kubernetes Pod가 무엇인지 알아보고, 사용하는 옵션에 대해서 알아보겠습니다.

 

환경: 맥북 프로 m2, minikube 
실습에 사용된 모든 코드는 글 마지막에 적혀있는 Github에서 볼 수 있습니다.

1. Pod란?

쿠버네티스에서 관리하는 가장 작은 배포 단위입니다. 도커가 컨테이너를 만든다면, 쿠버네티스는 파드를 생성합니다. 이때 다른 점은 파드는 하나 이상의 컨테이너를 포함할 수 있습니다. 파드에 1개의 컨테이너가 동작할 수도 있고, 서로 긴밀히 동작하는 여러 개의 컨테이너가 있을 수 있습니다. 

 

2. Pod 생성하기

파드를 만들 수 있는 방법은 여러 가지가 있습니다. 하나씩 해보겠습니다. 

 

2-1. run 명령어

가장 간단하지만, 상세히 설정하기에 번거로운 명령어입니다. 

kubectl run {pod-name} --image={image-name}

많은 옵션을 주지 않고, 위와 같이 파드 이름과 이미지만 설정해서 파드를 생성할 수 있습니다. 파드가 생성됐는지 아래 명령어를 통해서 확인해 보겠습니다.

kubectl get pod -o wide // -o 옵션은 output 출력을 결정 wide는 더 상세히 출력해줌

워커 노드인 minikube-m02에 잘 배포된 것을 확인할 수 있습니다. 삭제하는 방법은 delete 명령어를 사용합니다.

kubectl delete pod {echo}

삭제 후에 다시 파드를 조회하면, 없는 것을 확인하실 수 있습니다. run 명령어는 거의 사용하지 않습니다. 대부분 yaml 파일을 이용해서 Pod, Deployment 등등 많은 오브잭트들을 생성합니다. 바로 알아보겠습니다.

2-2. yaml 사용

쿠버네티스를 사용하시는 분들이라면, 대부분 yaml 파일을 사용해서 오브잭트들을 생성합니다. (yaml 작성법은 따로 찾아보시는 것을 추천드립니다.)

아래의 명령어로 yaml 파일 작성 준비를 합니다. 

vim pod-default.yml

그리고 아래와 같이 작성해 줍니다. 해당 옵션에 대한 것들은 마지막에 다루겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx

아래 명령어로 yaml을 이용해 파드를 생성합니다. 앞으로는 해당 명령어로 yaml을 이용해서 오브젝트들을 생성할 예정입니다. 

kubectl create -f pod-default.yml

 

파드가 성공적으로 생성됐습니다. yaml 작성하는 것이 외워야 할 옵션들이 많지만, 매우 유용하게 작성되므로 밑에서 확인해 보겠습니다.

 

 

3. Pod 옵션

파드를 yaml 파일로 생성할 때 반드시 적어야 하는 것들이 있고, 옵션으로 줄 수 있는 것들이 있습니다. 

이름 설명
apiVersion 오브젝트의 버전 v1, app/v1, job ...
kind 오브젝트 종류 Pod, Deployment, Service, ConfigMap...
metadata 메타 데이터 name, label, namespace등 파드를 구분할 수 있는 명세
spec 오브젝트의 상세 명세 리소스마다 다름

위의 표는 오브젝트를 만들 때 반드시 적어줘야 하는 구성요소입니다. yaml 파일에 주석으로 보겠습니다. 

apiVersion: v1 // 오브젝트 버전으로 Pod가 사용하는 apiVersion(Deployment는 app/v1)
kind: Pod // 오브젝트 종류
metadata:
  labels:
    app: nginx // 파드에 부여하는 라벨
  name: nginx // 파드에 부여하는 이름
spec: // 파드의 상세한 명세
  containers:
  - name: nginx
    image: nginx

 

이처럼 가장 간단한 구성요소이고, spec에서 부여할 수 있는 옵션들을 살펴보겠습니다. 

 

3-1. activeDeadlineSeconds

명시한 시간이 지나면 파드를 종료시킵니다. yaml 파일을 보겠습니다. 15초 뒤에 파드를 종료합니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: deadline-pod
spec:
  containers:
  - name: nginx
    image: nginx
  activeDeadlineSeconds: 15

파드가 시작되고 15초가 지나자, 파드의 상태가 DeadlineExceeded로 변경되며 파드가 종료됩니다. 해당 옵션을 부여하는 것은 보통 일시적으로 실행하는 Job이나, CronJob에서 부여하는 옵션입니다. 해당 옵션이 위험한 이유는 파드의 컨테이너가 시작하고가 아닌 파드 자체가 시작하고 시간을 카운트하므로 컨테이너가 정상적인 가동을 하지 않은 채 종료될 수 있습니다.

3-2. containers

핵심이 되는 옵션입니다. 파드 내에서 기동 할 컨테이너를 명세하는 역할을 합니다. 위의 yaml에서 충분히 봤으므로 생략하겠습니다. 

 

3-3. nodeSelector, nodeName

nodeSelector와 nodeName 옵션의 경우 파드를 배포할 Node를 선택할 수 있습니다. 

현재 저는 minikube-m02, minikube-m03을 가지고 있습니다. 파드를 노드에 스케쥴링하는 스케쥴러는 어떠한 옵션도 없다면, 노드의 상태(리소스, 파드를 할당하고 있는 개수)를 보고, 적절하게 노드에 할당합니다. 하지만 nodeSelector, nodeName 등등 노드를 선택할 수 있는 옵션을 주면 해당 노드에 할당합니다. yaml 파일로 보겠습니다.

 

먼저 nodeSelector를 사용하기 위해 워커 노드들에 라벨을 부여하겠습니다. 

// 라벨 부여
kubectl label node minikube-m03 number=3
kubectl label node minikube-m02 number=2

// 부여된 라벨 확인
kubectl get node minikube-m03 --show-labels

잘 안 보이시겠지만, 가장 마지막에 number=3이라는 라벨이 부여됐습니다. 이제 yaml을 작성하고 생성해 보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    number: "3"

minikube-m03으로 할당된 것을 볼 수 있습니다. 마치 우연처럼 m03으로 할당된 것일 수 있으니, 파드의 이름을 다르게 해 yaml을 작성해서 다시 적용해 보겠습니다. 

파드의 이름을 다르게 해서 적용한 결과 똑같이 m03에 할당됐습니다.

 

nodeName은 예시만 보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx-node-name
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: minikube-m03

nodeSelector와 같은 역할로 m03에 할당했습니다. 

 

여기서 nodeSelector와 nodeName의 다른 점은 nodeName의 경우 하나의 노드에 무조건적으로 할당하지만, nodeSelector는 label을 기준으로 할당하므로, 동일한 label을 가진 node라면 스케쥴러에 의해 스케쥴링됩니다. 

 

3-4. restartPolicy 

파드에서 에러가 발생했을 때 파드를 종료하고 새로운 파드를 시작할 지에 대한 옵션입니다. 

Never: 재시작하지 않음

OnFailure: 실패했을 때 재시작

Always: 항상 재시작

 

예시를 보겠습니다. 일부러 이미지 이름을 잘못 적어 파드에 에러를 발생시키고, Always를 줘 계속해서 실행되게 하겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: restart-policy
spec:
  containers:
  - name: nginx
    image: nginxasd
  restartPolicy: Always

get에 -w 옵션을 줘 변화가 있을 때마다 찍히도록 했습니다. 이때 ErrImagePull 오류가 발생하고, 다시 가동했지만 똑같이 문제가 발생하여 무한 루프를 만들고 있습니다.

 

해당 yaml 파일을 이용해 파드를 삭제하고, Never 옵션으로 부여해서 살펴보겠습니다. 

오류로 판별되고 나서, 파드가 재실행되지 않습니다. 

 

3-5. affinity

affinity는 친밀도가 가장 유사한 해석이라고 생각합니다. 친밀도의 기준을 설정해서 친밀도가 높은 노드에 할당할 수 있습니다. 

3가지의 affinity가 있습니다.

 

nodeAffinity: node의 친밀도를 기준으로 할당합니다. 

podAffinity: pod의 친밀도를 기준으로 할당합니다.

podAntiAffinity: 명시한 친밀도를 가지지 않은 노드로 할당합니다.

 

Affinity에도 옵션이 있습니다. 

preferredDuringSchedulingIgnoredDuringExecution: 요구 사항에 만족하는 파드를 할당하고, 가중치를 설정해 가장 큰 노드를 할당할 수 있습니다. 

requiredDuringSchedulingIgnoredDuringExecution: 요구 사항에 만족하는 노드가 없을 시 파드를 할당하지 않습니다. 이미 할당한 파드에 대해선 신경 쓰지 않습니다.

 

nodeAffinity 예시로 보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: node-affinity
spec:
  containers:
  - name: nginx
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: 
        nodeSelectorTerms:
        - matchExpressions:
          - key: number // label
            operator: Exists // Exists, NotExists, In, NotIn, Lt, Gt 로 연산 가능

m02 노드에도 number 라벨이 있기에 할당됐습니다. 이렇게만 사용한다면, nodeSelector와 다를 바가 없습니다. 그래서 preferred를 섞어서 사용해, 가중치가 있는 노드로 보냅니다. 해당 과정은 생략하겠습니다. 

 

3-6. initContainer

초기화 컨테이너는 컨테이너가 시작되기 전에 먼저 실행되고, 해당 컨테이너가 실패하면 파드가 실패했다고 상태가 나옵니다. 초기화 컨테이너는 실행되고, 일정 시간을 거친 후 삭제됩니다. 초기화 컨테이너는 ls 명령어만 실행하고 종료됩니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: init-container
spec:
  containers:
  - name: nginx
    image: nginx
  initContainers:
  - name: init-container
    image: nginx
    command: ["/bin/bash","-c","ls"]

READY에서 볼 수 있듯이 실질적으로 사용할 컨테이너는 1개이고, STATUS에서 Init:0/1로 초기화 컨테이너가 먼저 실행되고, 끝나면 컨테이너가 Running 되는 것을 볼 수 있습니다. 

 

3-7. taint, tolerations

테인트와 톨러레이션은 자물쇠와 열쇠라고도 표현합니다. 테인트가 걸려있다면, 해당 톨러레이션이 없다면 지정해놓은 테인트 effect에 따라서 파드가 할당되거나, 되지 않을 수 있습니다. 

 

테인트 옵션은 3가지입니다. 

Noschedule:가장 기본적인 설정, 노드에 테인트가 설정되어 있는 경우 파드가 노드에 스케줄 되지 않음 이 경우 톨러레이션을 통해서만 배포 가능
PreferNoSchedule: NoSchedule과 유사하지만, 스케줄러에서 더 이상 할당할 수 있는 노드가 없는 경우 테인트 설정 무시 후 할당
NoExecute: NoSchedule에 현재 할당된 파드에도 바로 적용되도록 스케줄을 다시 조정하는 기능 추가 즉 톨러레이션이 없는 파드는 모두 노드에서 제거함

 

한 번 m03에 taint를 걸고, nodeSelector를 했을 때 어떻게 되는지 보겠습니다. 

테인트 부여 

kubectl taint node minikube-m03 m03Taint=true:NoSchedule

테인트 부여됐는지 확인

kubectl describe node minikube-m03 | grep -i taint -F4

테인트가 잘 부여됐습니다.

그렇다면, 이전에 생성한 nodeSelector가 할당되는지 확인해 보겠습니다. 

테인트가 걸려있지만, 톨러레이션이 없기에 할당되지 않고, Pending 상태로만 존재합니다. 

그럼 이제 톨러레이션을 부여해서 할당되는지 보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    number: "3"

  tolerations:
  - effect: NoSchedule
    key: "m03Taint"
    value: "true"
    operator: "Equal" // Exists도 있습니다.

 

톨러레이션을 부여하니, 정상적으로 할당되는 것을 볼 수 있습니다. 

 

3-8. 파드 정적 배포

Schedular, Control-Manager, api-server들도 마찬가지로 파드입니다. 그렇다면 우리는 배포한 적이 없는데, 어떻게 컨트롤 플레인에 속하는 파드들이 배포되는 것일까요? 

 

해당 파드들은 정적배포 대상이기 때문입니다. 저희가 만들 파드를 정적 배포하기 위해 마스터 노드 컨테이너에 접속하겠습니다. 

명령어로 이동합니다. 

cd /etc/kubernetes/manifests

해당 디렉터리에 있는 파일을 확인해보면 아래와 같습니다.

컨트롤 플레인에 속하는 파드들입니다. 그렇다면 해당 디렉터리에 파드 yaml 파일을 생성해 보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx-minikube
spec:
  containers:
  - name: nginx
    image: nginx

파드를 생성하는 명령어를 하지 않았음에도 불구하고 아래처럼 파드가 생성됩니다.

3-9. probe 상태 탐사

컨테이너가 잘 동작하는지 확인하는 health-check를 해주는 옵션입니다. 해당 옵션을 실행하는 주체는 각 노드에 있는 kubelet입니다.

 

probe 종류

 

startupProbe

- 가장 먼저 시작할 때 탐색 1회성 체크
- 충분한 시간을 가지고 컨테이너를 배포하지만, 정상적으로 올라오지 않으면, restartPolicy에 의해서 동작

livenessProbe

- 컨테이너가 의도한 대로 잘 동작하는지 확인
- 컨테이너를 죽이고, restartPolicy에 의해서 동작

readinessProbe

- 컨테이너의 애플리케이션이 요청을 처리할 수 있는 상태인지 체크 
- 잠깐 서비스를 못하도록 함 엔드포인트를 빼버림 -> 컨테이너는 살아있지만, 트래픽은 전달되지 않음

 

yaml로 예시를 보겠습니다. 

 

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: liveness-pod
  name: liveness-pod
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      httpGet: // Tcp, exec 명령어 수행도 있음
        path: /
        port: 80
      initialDelaySeconds: 3 // 시작후 3초동안은 체크하지 않음
      periodSeconds: 3 // 3초 간격으로 체크함

쿠버네티스의 핵심이라고 할 수 있는 컨테이너 상태관리를 담당하는 Probe인 만큼 중요한 옵션입니다. 

4. 정리

지금까지 파드를 생성할 때 필수 설정들과 옵션들을 알아봤습니다.

모든 코드는 아래의 GitHub에서 확인하실 수 있습니다. 

https://github.com/rlaehdals/kubernetes-study

 

GitHub - rlaehdals/kubernetes-study

Contribute to rlaehdals/kubernetes-study development by creating an account on GitHub.

github.com

 

'DevOps > Kubernetes' 카테고리의 다른 글

Kubernetes ReplicaSet, Deployment  (0) 2023.01.17
Kubernetes 구성 요소  (0) 2023.01.07
Kubernetes 란??  (0) 2023.01.07
Mac m1, m2 minikube 설치  (0) 2023.01.04