Pod 이란?
Pod(파드)는 쿠버네티스에서 만들고 관리할 수 있는 가장작은 배포 단위입니다. 쿠버네티스 클러스터에서 마스터 노드는 워커노드로 pod을 할당하고 워커노드에서 pod을 띄우는 구조로 작동합니다. 워커노드에는 여러개의 pod이 있을 수 있고 pod은 한개 이상의 컨테이너로 구성됩니다.
쿠버네티스 클러스터에서 pod가 사용되는 방식은 두 가지입니다. 단일 컨테이너를 실행하는 pod이가장 일반적인 사용사례고, 다수의 컨테이너를 실행한 pod을 배포하는 것은 비교적 고급 사용 사례에 해당합니다.
pod안에 다수의 컨테이너를 배포하는 것이 가능한 데에는 pod안에 컨테이너와 더불어 네트워크 리소스와 디스크 볼륨이 할당되어 있기 때문입니다.
가령 A, B라는 컨테이너가 하나의 pod에 있다고 하면 두 컨테이너는 별도의 호출없이 localhost를 통해 통신이 가능합니다. A컨테이너(포트:7777)가 B컨테이너(포트:8888)를 호출하는 경우 localhost:8888로 호출하고 반대로 B컨테이너 A컨테이너를 호출하는 경우 localhost:7777로 호출합니다. pod 내부 컨테이너 간의 ip와 port를 공유하여 컨테이너간의 통신 용이성이 향상된 구조인 것이죠.
또한 pod안에 잇는 컨테이너 간에는 디스크 볼륨을 공유할 수 있습니다. 따라서 위와 같은 상황에서 두 컨테이너 A,B가 볼륨을 공유하기 때문에 동일한 디렉토리를 사용할 수 있습니다. 이 특성을 토대로 로그 수집기를 사이드카 패턴(Side-car Pattern)을 사용하여 추가 컨테이너로 붙여서 pod 내부에 있는 컨테이너들의 로그를 모두 수집하는 구성을 만들 수도 있습니다.
pod 간단 실습
pod을 만드는 데에는 두가지 방법이 존재합니다. 하나는 커맨드를 입력해서 만드는 명령적 방법이고 다른 하나는 설정파일을 작성하여 실행시키는 선언적 방법입니다.
명령적 방법
쿠버네티스의 모든 커멘드는 쿠버네티스의 CLI인 kubectl를 통해 이루어집니다. pod을 생성할 때는 docker run과 유사하게 kubectl run <pod name> --image=image
커맨드를 사용합니다. pod에 생성할 컨테이너 이미지를 지정해주는 것은 필수옵션이고 옵션으로 컨테이너의 포트, 환경설정 등을 지정할 수도 있습니다.
좀 더 상세하게 pod의 정보를 출력할 때는 kubectl describe pod <pod name>
이라는 커맨드를 입력합니다. 이때 kubectl get pods
커맨드로 원하는 pod의 이름을 확인하여 기입해주면 됩니다.
꽤나 긴 내용이 출력되지만 실질적으로 체크하는 부분은 Events 정도입니다. 현재 pod의 상태를 이벤트별로 확인할 수 있습니다.
선언적 방법
명령적 방법은 실전에서는 거의 사용되지 않는 기능입니다. 왜냐면 구성이 복잡해질 수록 커맨드가 점점더 길어지는데 이를 모두 kubectl로 표현하려면 너무 복잡하고 관리도 어렵습니다. 따라서 대부분의 경우 구성파일을 만들어 작성하는 선언적 방법을 사용합니다.
구성파일은 yaml 템플릿을 사용합니다. 앞서 생성했던 pod을 선언적 방식으로 작성하면 아래와 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# nginx-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx-app
spec:
containers:
- name: my-nginx
image: nginx
ports
- containerPort: 80
protocol: TCP
kubectl run
커맨드로 pod을 생성할 때와 비교하면, label
이라는 부분이 새로이 추가되어 있습니다. 쿠버네티스는 pod과 같은 리소스를 관리할 때 name
과 label
을 이용합니다. nginx-app이라는 app label을 통해 nginx라는 이름의 pod을 찾을 수 있다는 겁니다.
작성한 파일을 실행시킬 때는 kubectl apply -f <filename.yml>
커맨드를 사용합니다. 파일의 위치에 따라 경로를 지정해 줄 수도 있습니다.
앞서 작성한 구성파일의 필수요소들의 의미는 아래와 같습니다.
요소 | 설명 | 예시 |
---|---|---|
apiVersion | 쿠버네티스 api 버전 명세 | v1, app/v1, networking.k8s.io/v1 .. |
kind | 리소스 타입 | Pod, Deployment, Replicaset, Service … |
metadata | 메타데이터 | name, label, annotation |
spec | 상세명세 | 리소스마다 상이 |
한가지 주의해야할 부분은 apiVersion입니다. apiVersion은 Alpha(v1alpha1, ..)
→ Beta(v1beta1, ..)
→ Stable(v1)
의 과정을 거치는데 쿠버네티스 버전에 따라 지원하는 리소스 버전이 다릅니다. 그래서 쿠버네티스 이전 버전에서 최신 샘플을 배포하면 버전 오류가 발생할 수 있습니다.
pod 관련 기타 명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# pod 목록 조회
kubectl get pod
kubectl get pods
kubectl get po
# pod 로그 확인
kubectl logs <pod name> # 일시적
kubectl logs -f <pod name> # 지속적
# pod 내부 컨테이너 접속
kubectl exec -it nginx sh
ls
pwd
exit
# pod 삭제(pod, pods, po 다 가능)
kubectl delete pod/nginx
kubectl delete pod nginx
Pod 생성 단계
kubectl run 커맨드가 실행되고 pod이 생성되는 과정을 살펴보면,
Scheduler
는 API서버를 감시하면서 할당되지 않은(unassigned)Pod
이 있는지 체크Scheduler
는 할당되지 않은Pod
을 감지하고 적절한노드
에 할당 (minikube는 단일 노드)node
- 노드에 설치된
kubelet
은 자신의 노드에 할당된Pod
이 있는지 체크 kubelet
은Scheduler
에 의해 자신에게 할당된Pod
의 정보를 확인하고 컨테이너 생성kubelet
은 자신에게 할당된Pod
의 상태를API 서버
에 전달
현재 minikube는 단일노드로 작동하지만 노드가 수백개로 확장되더라도 scheduler만 정상적으로 작동한다면 문제없는 구조를 가지고 있습니다.
컨테이너 상태 모니터링
컨테이너 생성과 실제 서비스 준비는 약간의 차이가 존재합니다. 서버를 실행하면 바로 접속할 수 없고 다소간에 초기화시간이 소요되는데 실제로 접속이 가능할 때 비로소 서비스가 준비되었다고 할 수 있습니다. 쿠버네티스는 컨테이너가 생성되고 서비스가 준비되었다는 것을 체크하는 옵션을 별도로 제공하여 초기화하는 동안 서비스되는 것을 막을 수 있습니다.
livenessProbe
livenessProbe는 컨테이너가 정상적으로 동작하는지 체크하고 정상적으로 동작하지 않을 때 컨테이너를 재시작하여 문제를 해결합니다. 정상 상태를 체크하는 방식은 여러가지가 존재하는데 여기선 http get
요청을 보내 확인하는 방법을 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: echo-lp
labels:
app: echo
spec:
containers:
- name: app
image: ghcr.io/subicura/echo:v1
livenessProbe:
httpGet:
path: /not/exist
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 2 # Default 1
periodSeconds: 5 # Defaults 10
failureThreshold: 1 # Defaults 3
존재하지 않는 path와 포트를 지정하여 고의로 컨테이너 에러를 유발하면 아래와 같은 결과가 출력됩니다. 컨테이너가 정상적으로 응답하지 않아 Pod를 여러번 재시작하고 결국엔 CrashLoopBackOff
상태로 변경되었습니다.
kubectl describe
커맨드을 사용하면 더 상세한 내용을 확인할 수 있습니다. app이 실행이 되고나서 컨테이너에 http get
요청을 보냈지만 연결 실패가 발생했고, 이에따라 pod이 재시작했지만 여전히 정상연결이 되지않아 최종적으로 back-off
시켜버린 것을 확인할 수 있습니다.
readinessProbe
readinessProbe는 컨테이너가 준비되었는지를 체크합니다. 컨테이너가 실행될 준비가 되지않은 상태인 경우 Pod으로 들어오는 요청을 제외합니다. livenessProbe와 차이점은 문제가 있어도 Pod을 재시작하지 않고 요청만 제외한다는 점입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: echo-rp
labels:
app: echo
spec:
containers:
- name: app
image: ghcr.io/subicura/echo:v1
readinessProbe:
httpGet:
path: /not/exist
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 2 # Default 1
periodSeconds: 5 # Defaults 10
failureThreshold: 1 # Defaults 3
앞선 예제처럼 path와 포트를 통해 에러를 유발하면 아래와 같은 결과가 출력됩니다.
똑같이 상세한 내용을 확인해보면, 앞선 livenessProbe와 달리 pod을 재시작하는 과정없이 요청 제외만 한다는 것을 확인할 수 있습니다.
livenessProbe + readinessProbe
보통의 경우에는 livenessProbe와 readinessProbe를 둘다 사용합니다. 상세한 설정은 어플리케이션 환경에 따라 적절히 조정될수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: echo-health
labels:
app: echo
spec:
containers:
- name: app
image: ghcr.io/subicura/echo:v1
livenessProbe:
httpGet:
path: /
port: 3000
readinessProbe:
httpGet:
path: /
port: 3000
마무리
사실 pod을 단독으로 생성하여 사용하는 경우는 실전에서는 거의 없습니다. 다만 쿠버네티스에서는 pod이 컨테이너를 관리하는 것처럼 다른 리소스를 통해 pod을 관리하기 때문에 pod의 개념은 충분히 알고 있어야 합니다.