반응형

01-1 자료구조와 알고리즘

"프로그램이란 데이터를 표현하고, 그렇게 표현된 데이터를 처리하는 것이다"

  • 자료구조 : 데이터의 표현 및 저장방법
  • 알고리즘 : 표현 및 저장된 데이터를 대상으로 하는 '문제의 해결 방법'

→ 자료구조에 따라서 알고리즘은 달라지고, 알고리즘은 자료구조에 의존적

본 책에서는 선형구조→비선형 구조까지 다룸 (파일구조x, 단순구조 x)

 

01-2 알고리즘의 성능분석 방법

시간 복잡도(Time complexity)와 공간 복잡도(Space complexity)

  • 잘 동작하는 것에 더하여 좋은 성능을 보장하기 위해 자료구조와 알고리즘을 분석하고 평가할 수 있어야 함 (만능은 존재하지 않음)
  • 시간복잡도 : 속도에 해당하는 알고리즘의 수행시간 분석 결과 (cpu에 얼마나 부하를 주는가?)
  • 공간복잡도 : 메모리 사용량에 대한 분석 결과

일반적으로 알고리즘을 평가할 때 메모리의 사용량보다 실행속도에 초점을 둠. 공간복잡도는 보조적인 역할

Q. 어떻게 속도를 평가하나?

  • 특정 상황에 대해서 속도를 재는 것은 무의미
  • $\because$ 처리해야 할 데이터 양의 변화에 따라 속도의 증가 및 감소의 정도가 달라지기 때문
  • 위의 이유로 알고리즘의 수행속도를 평가할 때 다음과 같은 방식을 취함
    1. 연산의 횟수를 셈
    2. 처리해야 할 데이터의 수 n에 대한 연산횟수의 함수 T(n)을 구성
  • 식을 구성하면 데이터 수의 증가에 따른 연산횟수의 변화 정도를 판단할 수 있고, 다른 알고리즘들의 시간복잡도를 비교할 수 있음

알고리즘은 상황에 맞게 종합적으로 사고하고 판단하여 선택해야 함

  • 데이터의 수가 많아짐에 따른 연산횟수의 증가 정도가 중요하므로 알고리즘 A가 B보다 훨씬 좋은 알고리즘. T(n)함수에 대한 패턴이 중요함
  • 그렇다고 알고리즘 B가 필요없는게 아님
    • B와 같은 성격의 알고리즘은 A보다 구현이 쉬워 데이터의 수가 많지 않고 성능에 덜 민감한 경우라면 구현의 편의를 위해 B를 선택하기도 함

순차 탐색(Linear Search) 알고리즘과 시간 복잡도 분석의 핵심요소

#include<iostream>
using namespace std;

int LSearch(int ar[], int len, int target) // 순차 탐색 알고리즘 적용된 함수
{
	int i;
	for (i = 0; i < len; i++)
	{
		if (ar[i] == target)
			return i; //찾은 대상의 인덱스 값 반환
	}
	return -1;
}

int main(void)
{
	int arr[] = { 3, 5, 2, 4, 9 };

	int idx;

	idx = LSearch(arr, sizeof(arr) / sizeof(int), 4);
	if (idx == -1)
		cout<<"탐색 실패"<<endl;
	else
		cout<<"타겟 저장 인덱스 : "<<idx<<endl;

	idx = LSearch(arr, sizeof(arr) / sizeof(int), 7);
	if (idx == 01)
		cout<<"탐색 실패"<<endl;
	else
		cout<<"타겟 저장 인덱스 : "<<idx<<endl;
	return 0;
}

타겟 저장 인덱스 : 3
타겟 저장 인덱스 : -1

  • 좋은 탐색 알고리즘에서는 비교하는 '==' 연산이 적게 수행되어야 함
    • '<'와 '++'는 '=='연산자가 true를 반환할 때까지 수행이 됨

최악의 경우와 최상의 경우

  • 탐색의 대상이 되는 요소의 수(ex) 배열길이)가 n인 경우,
    • 운이 좋아서 찾고자 하는 값이 배열의 맨 앞에 저장된 경우, 연산 횟수 1회 (best case)
    • 운이 없어서 찾고자 하는 값이 배열의 맨 뒤에 저장된 경우, 연산 횟수 n회 (worst case)
  • 알고리즘 평가에 있어 성능평가에서 중요한 것은 worst case

Q. 평균적인 경우를 따져야 하는 것 아닌가?

A. 계산하는 것이 쉽지 않음. 다양한 이론 및 가정이 포함되어야 함. 아래의 average case 참고

순차 탐색 알고리즘 시간복잡도 : worst case

데이터 수가 n개일 때, 최악의 경우에 해당하는 연산횟수는 n

$\therefore T(n)=n$

순차 탐색 알고리즘 시간복잡도 : average case

평균적인 경우의 연산 횟수 계산을 위한 두가지 가정

  • 가정 1. 탐색 대상이 배열에 존재하지 않을 확률이 50%
  • 가정 2. 배열의 첫 요소부터 마지막 요소까지, 탐색 대상이 존재할 확률은 동일

case1. 배열에 탐색대상이 존재하지 않는 경우의 연산횟수

  • 연산횟수 n

case2. 탐색 대상이 존재하는 경우의 연산횟수

  • 연산횟수 n/2
    • 길이가 7인 배열의 경우, 위의 가정 2에 따라 각 배열요소에 탐색 대상이 존재할 확률이 동일하므로 7번 탐색하면, 연산 횟수가 1+2+3+4+5+6+7 = 28, 평균 28/7 = 4회
    • n/2 = 3.5회이지만, 근사치 계산 (수식이 간결해지고 배열의 길이가 길어질수록 근사치 계산결과에 가까워짐)
  • 최종적으로는 가정 1에 따라, 1/2n + 1/2n/2 = 3/4*n
  • 최악의 경우에 비해서 상대적으로 average case에 대한 시간복잡도를 구하기 쉽지 않고 신뢰도도 높지 않음
    • 앞서 2가지 가정을 뒷받침할 근거가 부족
    • 프로그램, 데이터 성격에 따라 배열에 탐색 대상이 존재할 확률이 50%가 아닐 수 있는데 이런 부분이 고려되지 않음

→ 결론 : Worst case를 시간 복잡도의 기준으로 삼는다

이진 탐색(Binary Search) 알고리즘의 소개

  • 순차 탐색보다 높은 성능을 보이지만 아래의 조건을 만족해야 함
    • 배열에 저장된 데이터는 정렬되어 있어야 함

ex) 길이가 9인 배열

arr[] = {1, 2, 3, 7, 9, 12, 21, 23, 27};

이 배열을 대상으로 숫자 3이 저장되어 있는지 확인

첫번째 시도

  • 배열 인덱스의 시작과 끝은 각각 0과 8
  • 0과 8을 합하여 그 결과를 2로 나눔 → (0+8)/2 = 4
    • 2로 나눠서 얻은 결과 4를 인덱스 값으로 하여 arr[4]에 저장된 값이 3인지 확인 → NO

 

두번째 시도

  • arr[4] 에 저장된 값 9와 탐색 대상인 3의 대소를 비교
    • arr[4] = 9
    • 찾고자 하는 대상 3
    • 배열이 오름차순으로 정렬되었음을 전제하였기 때문에 탐색 범위를 수정 가능
  • arr[4] > 3이므로 탐색의 범위를 인덱스 기준 0~3으로 제한 (이전 : 0~8)
  • 0과 3을 더하여 그 결과를 2로 나눔. 나머지는 버림. (0+3)/2 = 1.5 ⇒ 1
  • 2로 나눠서 얻은 결과가 1이니, arr[1]에 저장된 값이 3인지 확인 → NO

→ 두번째 시도 만에 탐색의 대상을 반으로 줄임

세번째 시도

  • arr[1]에서 저장된 값 2와 탐색 대상인 3의 대소를 비교
  • 탐색 대상이 더 크므로 탐색의 범위를 인덱스 기준 2~3으로 제한(이전: 0~3)
  • 2와 3을 더하여 그 결과를 2로 나눔. 나머지는 버림. (2+3)/2 = 2.5 → 2
  • 2로 나눠서 얻은 결과가 2이니, arr[2]에 저장된 값이 3인지 확인 → YES

이진 탐색 알고리즘은 탐색의 대상을 반복하여 반씩 떨구어 내는 알고리즘

이진 탐색(Binary Search) 알고리즘의 구현

  • 탐색의 시작 인덱스(first)와 마지막 인덱스(last) 값을 포함하는 범위로, 탐색의 범위를 반으로 줄여나감.

Q. 언제까지 계속되어야 하나?

A. first 와 last가 같아질 때까지? NO. 같아진 경우 하나의 탐색 범위가 있으므로 이때도 탐색해야 함

#include <iostream>
using namespace std;

int BSearch(int ar[], int len, int targetValue)
{
	int first = 0;      // 탐색 대상의 시작 인덱스 값
	int last = len - 1; // 탐색 대상의 마지막 인덱스 값
	int mid;

	while (first <= last) **// 중요(등호 포함됨)** 
	{
		mid = (first + last) / 2;

		if (targetValue == ar[mid])
		{
			return mid;
		}
		else //타겟이 아니라면 탐색 대상을 반으로 줄인다. 
		{
			if (targetValue < ar[mid])
				last = mid - 1; //왜 -1을 하였을까?
			else
				first = mid + 1; //왜 +1을 하였을까? 
		}
	}
	return -1;
}

int main()
{
	int arr[] = { 1,2,3,7,9,12,21,23,27 };
	int idx;

	idx = BSearch(arr, sizeof(arr) / sizeof(int), 7);
	if (idx == -1)
		cout << "탐색 실패" << endl;
	else
		cout << "타겟 저장 인덱스 : " << idx << endl;

	idx = BSearch(arr, sizeof(arr) / sizeof(int), 4);
	if (idx== -1)
		cout << "탐색 실패" << endl;
	else
		cout << "타겟 저장 인덱스 : " << idx << endl;

	return 0;
}

타겟 저장 인덱스 : 3 탐색 실패

Q. 위의 코드 중 while 문 조건에서 등호 (=)를 뺀 후 결과는?

탐색 실패 탐색 실패

  • 값 7이 3번째 인덱스에 포함(arr[3] = 7)되어 있음에도 불구하고 탐색 실패의 결과를 얻음

→ "first가 last보다 큰 경우 탐색이 종료됨. 이렇게 종료되었다는 것은 탐색에 실패했음을 뜻함"

결론적으로 while문에서 '='를 빼면, worst case(탐색 범위가 하나만 남은 경우)에 index를 찾을 수 없게 됨

빅-오 표기법(Big-Oh Notation)

데이터의 수 n과 그에 따른 시간 복잡도 함수 T(n)을 정확히 그리고 오차 없이 구하는 것은 대부분의 경우 쉽지 않음

→ '빅-오'라는 것은 함수 T(n)에서 가장 영향력이 큰 부분이 어딘가를 따지는 것

ex) $T(n)=n^2+2n+1$ 은 $T(n)=n^2$ 로 간략화해서 진행할 수 있음

Q. 간략화 할 수 있는 이유는?

A. n의 값이 커질수록 T(n)에서 $n^2$이 차지하는 비율이 절대적으로 큼

표기는 $O(n^2)$로 하고, "빅-오 오브 n^2"로 읽음

단순하게 빅-오 구하기

"T(n)이 다항식으로 표현이 된 경우, 최고차항의 차수가 빅-오가 된다."

대표적인 빅-오

  • $O(1)$ : 상수형 빅-오. 데이터 수에 상관없이 연산횟수가 고정인 유형의 알고리즘
  • $O(logn)$ : 로그형 빅-오. '데이터 수의 증가율'에 비해서 '연산횟수의 증가율'이 훨씬 낮은 알고리즘
  • $O(n)$ : 선형 빅-오. 데이터수와 연산횟수가 비례
  • $O(nlogn)$ : 선형로그형 빅-오. 데이터의 수가 두배로 늘때, 연산횟수는 두배 조금 넘게 증가하는 알고리즘
  • $O(n^2)$ : 데이터 수의 제곱에 해당하는 연산횟수를 요구하는 알고리즘→데이터 양이 많은 경우 적용하기 부적절
  • $O(2^n)$ : 지수형 빅-오. 연산횟수가 지수적으로 증가

연산횟수 대소 정리

  • $O(1) < O(logn)<O(n)<O(nlogn)<O(n^2)<O(2^n)$

 

순차 탐색 알고리즘과 이진 탐색 알고리즘의 비교

#include <iostream>
using namespace std;

//Linear search vs Binary search 비교

int BSearch(int ar[], int length, int targetValue)
{
	int first = 0;
	int last = length - 1;
	int mid;
	int opCount = 0; //비교연산의 횟수를 기록

	while (first <= last)
	{
		mid = (first + last) / 2;

		if (targetValue == ar[mid])
		{
			return mid;
		}
		else
		{
			if (targetValue < ar[mid])
				last = mid - 1;
			else
				first = mid + 1;
		}
		opCount += 1;
	}
	cout << "데이터 수 "<<length<<"에 대한 "<<"비교연산횟수 : " << opCount << endl;
	return -1;
}

int main()
{
	int arr1[500] = { 0, };
	int arr2[5000] = { 0, };
	int arr3[50000] = { 0, };
	int idx;

	//배열 arr1을 대상으로, 저장되지 않는 정수 1을 찾으라고 명령 
	idx = BSearch(arr1, sizeof(arr1) / sizeof(int), 1);
	if (idx == -1)
		cout << "탐색 실패" << endl;
	else
		cout << "타겟 저장 인덱스 : " << idx << endl;

	//배열 arr2을 대상으로, 저장되지 않는 정수 1을 찾으라고 명령 
	idx = BSearch(arr2, sizeof(arr2) / sizeof(int), 1);
	if (idx == -1)
		cout << "탐색 실패" << endl;
	else
		cout << "타겟 저장 인덱스 : " << idx << endl;

	//배열 arr1을 대상으로, 저장되지 않는 정수 1을 찾으라고 명령 
	idx = BSearch(arr3, sizeof(arr3) / sizeof(int), 1);
	if (idx == -1)
		cout << "탐색 실패" << endl;
	else
		cout << "타겟 저장 인덱스 : " << idx << endl;

	return 0;
}

데이터 수 500에 대한 비교연산횟수 : 9
탐색 실패
데이터 수 5000에 대한 비교연산횟수 : 13
탐색 실패
데이터 수 50000에 대한 비교연산횟수 : 16
탐색 실패

순차 탐색 알고리즘의 Big Oh 가 O(n) 이므로, 비교해보면 $O(n)$ 과 $O(logn)$의 알고리즘 사이의 연산횟수에서 큰 차이를 보여줌

 
 
반응형
반응형
 

rate limit을 두는 이유?

  • 잠재적인 brute force attacks에 대비한 추가 보안 계층 역할
  • 트래픽 급증을 방지하여 안정적인 서버 확보
  • 사용자의 tier에 따라 service를 제한하는 역할 (ex) download )

 

Siege를 이용해 server에 load testing

$ apt-get install siege
# siege -v -r 2 -c 5 https:///thumb.png

c5 * r2 = 10 requests 
  • r: request test 수
  • c: concurrent connection

rate limit을 적용하지 않았고, 1.02초 안에 10번의 요청이 모두 성공

limit_req_zone $request_uri zone=MYZONE:10m rate=1r/s
  • limit_req_zone: rate limit을 적용할 zone을 정의
    • $server_name (서버에 들어오는 모든 요청)
    • $binary_remote_addr (user당)
    • $request_uri (URI 당)
  • zone: rate limit 을 적용할 zone의 이름과 memory에 저장할 zone의 size를 정의 (메모리는 뭘까)
  • rate: limit rate을 정의하고 초당 1개의 requests를 초과하지 않도록 제한

 

rate limit을 적용하면 처음 1개의 request만 성공, 나머지는 모두 503 (Service Unavailable)

 

burst

  • burst를 적용하면 rate limit을 초과하는 connect을 즉시 reject하지 않고 기다리게 만들 수 있음
limit_req_zone $request_uri zone=MYZONE:10m rate=10r/s burst=5
  • 1 + 5burst = 6 connection을 가질 수 있음. 하지만 5개는 바로 response하진 않음
  • 약간의 버퍼를 제공하고 요청 속도는 늦쳐짐
  • 내부적으로 간단한 queue를 사용 (https://www.nginx.com/blog/rate-limiting-nginx/)

  • 첫 요청은 바로 처리, 나머지는 4개는 순차적으로 1r/s에 맞춰 처리
  • 다음 5개의 연결이 전송되면 동일하게 처리됨, 하지만 9.33초 걸림

 

burst를 초과하도록 요청하면,

  • 한번에 15개를 요청하면, 1 + 5burst 이외에 9개는 즉시 503 error 발생
  • 나머지 5burst는 순차적으로 처리됨

nodelay : 허용된 버스트 요청을 가능한 빨리 처리

  • burst를 적용한 zone에 적용 가능
limit_req zone=MYZONE burst=5 nodelay;
  • burst 수 만큼의 connection은 rate limit 무시하고 즉시 처리
    • 즉시 response되었어도 다음 새로운 request에서는 여전히 동일한 rate limit 적용 받음

  • 첫 6개의 요청은 모두 성공
  • 2초 정도 머문 후 다음 요청을 했다고 가정하면, 2개의 요청만 처리가능한 시간이 지났으므로 나머지는 모두 503 error

 

Reference


반응형
반응형

02-1 이더넷

  • 물리 계층과 데이터 링크 계층은 이더넷이라는 공통된 기술이 사용되기 때문에 서로 밀접하게 관련되어 있음

이더넷

  • 유선 LAN 환경에서 가장 대중적으로 사용되는 기술로 케이블 등의 다양한 통신 매체의 규격들과 송수신되는 프레임의 형태, 프레임을 주고 받는 방법 등이 정의된 네트워크 기술

이더넷 표준

  • 유선 LAN 환경에서는 대부분 물리 계층에서는 이더넷 규격 케이블을 사용하고, 데이터 링크 계층에서 주고 받는 프레임은 이더넷 프레임의 형식을 따름
  • 이더넷은 국제적으로 표준화가 이루어져 IEEE 802.3이라는 이름으로 불림. 서로 다른 컴퓨터가 각기 다른 제조사의 네트워크 장비를 사용해도 이더넷 표준을 준수하면 서로 통일된 형태로 통신할 수 있음

통신 매체 표기 형태

  • 이더넷 표준에 따라 통신 매체의 종류과 전송 속도가 달라질 수 있는데 속도와 특성을 한눈에 파악하기 쉽도록 아래와 같은 형태로 표기

전송 속도 BASE-추가특성

1. 전송 속도(data rate)

  • 숫자만 표기되어 있으면 Mbps, 숫자 뒤에 G가 붙는 경우 Gbps 속도를 의미
    • 1000Base-T 케이블은 1000Mbps 속도를 지원하는 케이블
    • 10GBase-T 케이블은 10Gbps 속도를 지원하는 케이블

2. BASE

  • 베이스 밴드(BASEband)의 약자로, 변조 타입(modulation type)을 의미
    • 변조타입 : 비트 신호로 변환된 데이터를 통신 매체로 전송하는 방법으로 대부분 디지털 신호를 송수신하는 베이브밴드 방식을 사용

3. 추가특성

  • 다양한 추가적인 특성을 표현
  • 10BASE-2, 10BASE-5와 같이 전송 가능한 최대 거리가 명시
  • 데이터가 비트 신호로 변환되는 방식을 의미하는 물리 계층 인코딩 방식이 명시되기도 하고 (EX) 1000BASE-CX)
  • 비트 신호를 옮길 수 있는 전송로 수를 의미하는 레인 수가 명시되기도 함 (ex) 100GBASE-LR4)

통신 매체 종류

  • 가장 대중적인 통신 매체의 종류 예시로 추가 특성에 C, T, S, L이라는 글자가 있음
    • C: 동축 케이블
    • T: 트위스티드 페어 케이블
    • S: 단파장 광섬유 케이블
    • L: 장파장 광섬유 케이블

이더넷 프레임(Ethernet frame)

  • 이더넷 네트워크에서 주고받는 프레임 형식으로 상위 계층으로부터 받아들인 정보에 헤더와 트레일러를 추가하는 캡슐화 과정을 통해 만들어짐
  • 헤더는 기본적으로 프리앰블, 수신지 MAC 주소, 송신지 MAC 주소, 타입/길이로 구성되고, 페이로드는 데이터, 트레일러는 FCS로 구성

프리엠블 (preamble)

  • 서두를 뜻하고, 이더넷 프레임의 시작을 알리는 8바이트(64비트) 크기의 정보임
  • 첫 7바이트는 10101010 값을 가지고, 마지막 바이트는 10101011 값을 가짐. 수신지는 이 프리엠블을 통해 이더넷 프레임이 오고 있음을 알아차림

수신지 MAC 주소와 송신지 MAC 주소

  • MAC 주소란 Media Access Control address로 ‘물리적 주소’라고도 불림
  • 네트워크 인터페이스마다 부여되는 6바이트(48비트) 길이의 주소로 LAN 내의 수신지와 송신지를 특정할 수 있음
  • 보통 NIC(Network Interface Controller)라는 장치가 네트워크 인터페이스 역할을 하는데 한 컴퓨터에 NIC가 여러개 있다면 MAC주소도 여러개 있을 수 있음
  • 아래의 명령어로 컴퓨터의 MAC 주소를 직접 확인할 수 있음
    • Windows: ipconfig /all
    • 맥OS 나 리눅스 운영체제: ifconfig (결과 예시 bc:d0:74:61:fd:3c)
$ ifconfig 
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	options=6460<TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
	ether bc:d0:74:61:fd:3c
	inet6 fe80::1c94:978d:432:1cdf%en0 prefixlen 64 secured scopeid 0xf 
	inet 172.30.1.5 netmask 0xffffff00 broadcast 172.30.1.255
	nd6 options=201<PERFORMNUD,DAD>
	media: autoselect
	status: active

타입/길이

  • 필드에 명시된 크기가 1500(16진수 05DC) 이하인 경우 프레임의 크기(길이)를 나타냄
  • 크기가 1536(16진수 0600) 이상인 경우 타입을 나타냄
  • 타입이란 이더넷 프레임이 ‘어떤 정보를 캡슐화했는지’를 나타내는 정보로 이더타입(ethertype)이라고도 부름
  • 대표적으로 상위 계층에서 사용된 프로토콜의 이름이 명시됨
    • 0800: IPv4
    • 86DD: IPv6
    • 0806: ARP

데이터

  • 상위 계층에서 전달받거나 상위 계층으로 전달해야 할 내용으로 최대 크기는 1500 바이트
  • 항상 46 바이트 이상이어야 하며 그 이하의 데이터인 경우믄 크기를 맞추기 위해 패딩(padding)이라는 정보가 내부에 채워짐. 보통 0으로 채워짐

FCS

  • Frame Check Sequence로 수신한 이더넷 프레임에 오류가 있는지 체크하기 위한 필드
  • 이 필드에는 CRC(Cycle Redundancy Check) 라 불리는 오류 검출용 값이 들어가는데 송신지는 프리엠블을 제외한 나머지 필드 값들을 바탕으로 CRC 값을 계산 후 FCS에 명시
  • 수신지는 수신한 프레임에서 프리앰블과 FCS 필드를 제외한 나머지 필드 값을 바탕으로 CRC 값을 계산한 뒤, 이 값을 FCS와 비교하여 일치 하지 않으면 프레임을 폐기
 
 
 
반응형
반응형

pathlib 모듈은 파이썬 표준 라이브러리로, 파일 읽기/쓰기 작업이나 디렉토리에 있는 특정 유형 파일 나열, 특정 파일의 상위 디렉토리 찾기 등의 작업을 할 때 사용됨

The Problem With Representing Paths as Strings

  • python 3.4 버전부터 pathlib 모듈이 등장했는데 pathlib이 존재하기 전에는 전통적으로 string을 이용하여 파일 경로를 표현했음
  • 하지만 경로는 일반 문자열 이상이기 때문에 중요한 기능들이 os, glob, shutil과 같은 라이브러리를 포함한 표준 라이브러리 전체에 분산되어 있었음
  • 예를 들어 아래의 코드는 txt 파일을 하위의 archive 폴더로 이동시키는 내용
import glob
import os
import shutil

for file_name in glob.glob("*.txt"):
    new_path = os.path.join("archive", file_name)
    shutil.move(file_name, new_path)
  • glob, os, shutil 까지 3개의 import statement 필요
  • pathlib 모듈은 여러 운영체제에서 동일한 방식으로 작동하는 Path 클래스를 제공해 위 3가지 모듈은 임포트 하는 대신 pathlib 모듈만 사용하여 동일한 작업을 수행할 수 있음
from pathlib import Path

for file_path in Path.cwd().glob("*.txt"):
    new_path = Path("archive") / file_path.name
    file_path.replace(new_path)

Path Instantiation With Python’s pathlib

  • pathlib 모듈에 대해 한가지 강력한 동기는 string 대신 전용 객체로 파일 시스템을 표현하는 것
  • 객체 지향 접근 방식은 기존 os.path 방식과 대조할 때, pathlib 핵심이 Path 클래스 라는 점에 주목하면 더욱 분명함
>>> from pathlib import Path
>>> Path
<class 'pathlib.Path'>
  • Path 클래스로 작업하기 때문에 import pathlib; pathlib.Path 보다 from pathlib import Path로 작업하는게 더 효율적
  • Path 객체를 인스턴스화 하는 방법에는 몇가지가 있지만 이 글에서는 클래스 메소드, 문자열 전달, path 컴포넌트를 조인함으로써 path 객체를 생성하는 것을 살펴봄

Using Path Methods

  • Path를 import 한 후에 working directory나 home directory를 가져오기 위해 기존 메소드를 사용할 수 있음
>>> from pathlib import Path
>>> Path.cwd()
PosixPath('/Users/woo-seongchoi/Desktop/realpython')
  • pathlib.Path를 인스턴스로 만들면, OS에 따라 WindowsPath나 PosixPath 객체를 얻을 수 있음
  • 일반적으로 Path를 사용하면, 사용중인 플랫폼에 대한 구체적인 경로를 인스턴스화하는 동시에 코드가 플랫폼에 독립적으로 유지됨
>>> from pathlib import Path
>>> Path.home()
PosixPath('/Users/woo-seongchoi')
  • Path 객체의 cwd나 home 메소드를 통해 python script의 starting point를 쉽게 얻을 수 있음

Passing in a String

  • home directory나 current working directory 대신에 string을 Path 에 전달함으로써 directory나 file을 가리킬 수 있음
>>> from pathlib import Path
>>> Path("/Users/woo-seongchoi/Desktop/realpython/file.txt")
PosixPath('/Users/woo-seongchoi/Desktop/realpython/file.txt')
  • Path 객체를 생성하고 string을 다루는 대신 pathlib 모듈이 제공하는 유연성을 통해 작업 가능
  • POSIX는 Portable Operating System Interface 이고, path 표현 등을 포함하여 운영 체제간 호환성을 유지하기 위한 표준임

Joining Paths

  • 슬래시 (’/’) 를 이용하여 경로의 일부를 연결하도록 경로를 구성할 수 있음
from pathlib import Path

for file_path in Path.cwd().glob("*.txt"):
    new_path = Path("archive") / file_path.name
    file_path.rename(new_path)
  • 슬래시 연산자는 Path 객체를 포함하는 한 여러 경로 또는 경로와 문자열이 섞인 경우도 결합시킬 수 있음
  • 슬래시 연산자를 사용하지 않는다면 joinpath 메소드를 사용할 수 있음
>>> from pathlib import Path
>>> Path.home().joinpath("python", "scripts", "test.py")
PosixPath('/home/woo-seongchoi/python/scripts/test.py')

References

https://realpython.com/python-pathlib/

 

 

반응형
반응형

본 글은 인프런 강의 "대세는 쿠버네티스 [초급~중급]" 를 듣고 요약 및 실습 정리한 내용입니다.

 

Service

  • 기본적으로 자신의 cluster ip를 가짐

 

 

  • Service를 Pod에 연결하면 Service IP를 통해 Pod에 접근 가능
  • Pod는 재생성될 때 IP가 변경되기 때문에 신뢰성이 떨어짐

Service의 종류는 3가지가 있음

1) ClusterIP

  • 외부 (External)에서 접근 불가
  • Cluster 내에서 접근 가능
  • Service의 9000 포트로 요청이 들어오면 Pod의 8080 포트와 연결이 됨

 

  • 하나의 Service에 여러개의 Pod를 연결시킬 수 있고 서비스가 트래픽을 분산해서 Pod에 전달해줌
  • 활용: 인가된 사용자, 내부 대쉬보드, pod의 서비스상태 디버깅

Service yaml 파일

apiVersion: v1
kind: Service
metadata:
  name: svc-1
spec:
  selector:
    app: pod
    ports:
      - port: 9000 # Service port 
      - targetPort: 8080 # Service에 연결된 Pod port
		type: ClusterIP # default로 생략가능

 

Pod yaml 파일

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
labels:
  app: pod
spec:
  containers:
  - name: container
    image: tmkube/app
    ports:
    - containerPort: 8080
  • Service yaml파일의 targetPort와 pod yaml파일의 containerPort가 같아야 함

 

2) NodePort

  • 서비스에 clusterIP가 할당됨
  • 쿠버네티스에 할당된 모든 Node에 똑같은 Port가 할당됨
  • 외부에서 어떤 노드든 간에 접속이 되면 Service에 연결이 됨
  • 서비스는 자신과 연결된 Pod에 트래픽을 전달

 

주의할점

  • pod가 있는 node만 port가 할당되는게 아니라 모든 Node에 포트가 할당됨
  • 활용: 내부망 연결, 데모나 임시 연결용
apiVersion: v1
kind: Service
metadata:
  name: svc-2
spec:
  selector:
    app: pod
  ports:
  - port: 9000
    targetPort: 8080
    nodePort: 30000 # 30000 ~ 32767
  type: NodePort
  • externalTrafficPolicy: Local을 통해 각 Node에 속한 pod로 트래픽이 분산됨

3) Load Balancer (외부 시스템 노출용)

  • NodePort의 성격을 그대로 가지고 있고 추가적으로 Load Balancer가 생김 (트래픽 분산)

  • Load balancer에 접근하기 위한 외부 접속 ip주소는 별도로 할당하기 위한 플러그인 설치 필요
apiVersion: v1
kind: Service
metadata:
  name: svc-3
spec:
  selector:
    app: pod
  ports:
  - port: 9000
    targetPort: 8080
  type: LoadBalancer
반응형

'Kubernetes' 카테고리의 다른 글

Node schedule  (0) 2024.08.04
Pod - Container  (0) 2024.08.04
Pod - Label  (0) 2024.08.04

+ Recent posts