Study Note

쿠버네티스 Pod - [Kubernetes, k8s] 본문

Kubernetes/Kubernetes Pod

쿠버네티스 Pod - [Kubernetes, k8s]

moreLearn 2021. 7. 21. 21:21

Pod는 쿠버네티스의 최소 실행 단위입니다. 쿠버네티스는 Pod를 통해 기본 가상환경을 제공합니다.

 

가상환경 플롬폼 실행 단위 비교

  • 가상머신 : Instance
  • 도커 : Container
  • 쿠버네티스 : Pod

Pod 특징

  • 1개 이상의 컨테이너 실행
    • Pod는 1개 이상의 컨테이너를 가질 수 있습니다. 보통 1개의 컨테이너를 실행하며, 3개 이상 넘어가는 경우는 거의 없습니다.
  • 동일 노드에 할당
    • Pod 내의 실행되는 컨테이너들은 반드시 동일한 노드에 할당되며 동일한 생명주기를 갖습니다.
      [Pod 삭제 시 Pod 내의 모든 컨테이너 삭제]
  • 고유의 Pod IP
    • Pod 리소스는 노드 IP와 별개로 클러스터 내에서 접근 가능한 고유 IP를 할당 받습니다.
    • 다른 노드에 위치한 Pod(노드의 IP가 다름)라도 Pod 고유 IP를 이용하여 NAT(Network Address Translation) 통신 없이 접근이 가능합니다.
  • IP 공유
    • Pod 내에 있는 컨테이너들은 서로 IP를 공유하기 때문에 서로 localhost를 통해 접근이 가능합니다.
      [이때 포트를 이용하여 구분할 수 있습니다.]
  • volumn 공유
    • Pod 안의 컨테이너들은 동일한 볼륨과 연결이 가능하여 파일 시스템 기반으로 파일을 공유할 수 있습니다.

YAML을 통한 Pod 생성

다음 [--dry-run, -o yaml] 옵션을 통해 Pod를 실제로 생성하지 않고 템플릿 파일을 생성할 수 있습니다.

더보기
kubectl run mynginx --image nginx --dry-run=client -o yaml > mynginx.yaml

# mynginx.yaml
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: mynginx
  name: mynginx
spec:
  containers:
  - image: nginx
    name: mynginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
  • apiVersion : 모든 리소스에 정의되어 있으며, 리소스의 이름이 동일한 경우 이름 충돌을 피하기 위한 목적입니다. 프로그래밍 언어의 패키지와 유사한 역할을 합니다.
  • kind : 리소스의 타입을 정의합니다. [Pod, Service, Replicaset, Deployment]
  • metadata : 리소스의 메타 데이터입니다.
    • labels : 리소스의 라벨 정보입니다.
    • name : 리소스의 이름 정보입니다.
  • spec : 리소스의 스펙을 정의합니다. [리소스마다 조금씩 차이가 있습니다.]
    • containers : 1개 이상의 컨테이너를 정의합니다.
      • name : 컨테이너 이름 정보입니다.
      • image : 컨테이너 이미지 주소를 지정합니다.

 

Pod 생성 순서

다음 명령을 통해 위에서 생성한 YAML 정의서를 통해 Pod를 생성할 수 있습니다.

kubectl apply -f mynginx.yaml

// 실행 구조 이미지 필요

  1. Kubectl 명령을 통해 Pod 정의를 쿠버네티스 마스터에 전달
  2. 쿠버네티스 마스터가 YAML 정의에 대해서 유효청 체크 (validation check) 후에 특정 노드에 사용자 요청에 따라 컨테이너를 실행하도록 명령
  3. 노드(kubelet)는 명령에 따라 요청 사항에 맞게 컨테이너를 노트에서 실행

라벨링 시스템

라벨 정보 부여

다음과 같은 방법을 사용하여 라벨 정보를 부여할 수 있습니다.

  • label 명령을 이용하는 방법
더보기
kubectl label pod <NAME> <KEY>=<VALUE>

# ex)
kubectl label pod mynginx hello=world
  • 선연형 명령(YAML)을 이용하는 방법
더보기
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
	labels:
    	hello: world
        run: mynginx
    name: mynginx
spec:
	containers:
    - image: nginx
      name: nginx
EOF

라벨 정보 삭제

다음과 같이 명령하면 label의 정보가 비워집니다.

kubectl label pod <NAME> <KEY>-
kubectl run <NAME> 명령을 수생하면 자동으로 run=<NAME> 라벨이 등록됩니다.

라벨 정보 확인

다음과 같은 방법을 통해 Pod의 라벨을 조회할 수 있습니다.

  • 라벨 부분 조회 : kubectl get pod mynginx -L run
  • 라벨 전체 조회 : kubectl get pod mynginx --show-labels
  • 특정 라벨 필터링 조회 : kubectl get pod -l run=mynginx
라벨 부분 조회에서 [-L]은 꼭 대문자를 사용해야 합니다. [-l]은 라벨 조건 필터링에서 사용되는 키워드입니다.

nodeSelector를 이용한 노드 선택

기본적으로 Pod 생성시 쿠버네티스 마스터가 어떤 노드에 실행할지 판단하여 스케줄링 하지만 특정 노드나 노드그룹(ex. 디스크가 HDD인 노드)을 명시적으로 선택해서 실행시켜야 하는 경우 nodeSelector라는 property를 이용하여 노드를 선택할 수 있습니다.

 

다음은 nodeSelector를 사용하는 방법입니다.

더보기
# 마스터와 워커 노드에 다음과 같이 라벨을 등록합니다.
kubectl label node master disktype=ssd
kubectl label node worker disktype=hdd

# 노드의 라벨을 확인합니다.
kubectl get node --show-labels | grep disktype
# worker   Ready    <none>   23h   v1.18.6+k3s1   ...disktype=hdd...
# master   Ready    master   24h   v1.18.6+k3s1   ...disktype=ssd...

# nodeSelector property가 추가된 Pod YAML을 작성합니다.
apiVersion: v1
kind: Pod
metadata:
  name: node-selector
spec:
  containers:
  - image: nginx
    name: nginx
  nodeSelector: // 선택하고자 하는 노드의 라벨을 지정합니다.
    disktype: ssd
    
# 생성한 YAML을 통해 Pod를 생성합니다.
kubectl apply -f node-selector.yaml

# -o wide 옵션을 통해 Pod가 어느 노드에서 실행되고 있는지 확인합니다.
kubectl get pod node-selector -o wide
# node-selector   1/1     Running   0          16s   10.42.0.8   master   <none>           <none>

실행 명령 및 파라미터 지정

Pod 생성시 실행 명령(command), 파라미터(args)를 함께 지정할 수 있다.

echo 명령은 실행 후 종료되기 때문에 Always로 설정되어 있으면 계속 Pod가 재시작 됩니다.
더보기
apiVersion: v1
kind: Pod
metadata:
  name: cmd
spec:
  restartPolicy: OnFailure // Pod 재시작 정책
  containers:
  - image: nginx
    name: nginx
    commend: ["/bin/echo"] // 컨테이너의 시작 실행 명령 [Docker의 ENTRYPOINT]
    args: ["hello"] // 실행 명령에 넘겨줄 파라미터 [Docker의 CMD]

restartPolicy는 Pod의 재시작 정책이며 다음과 같은 옵션이 있습니다.

  • Always(Default) : Pod 종료 시 항상 재시작 시도
  • Never : 재시작 시도를 하지 않음
  • OnFailure : 실패 시에만 재시작 시도

환경변수 설정

env property를 사용하여 Pod의 환경변수를 전달할 수 있습니다. 또한 env property 뒤에 여러 환경변수를 한번에 설정할 수 있습니다.

더보기
apiVersion: v1
kind: Pod
metadata:
  name: env
spec:
  containers:
  - image: nginx
    name: nginx
    env: // 환경변수를 설정하는 property
    - name: hello // 환경변수 key 지정
      value: "world!" // 환경변수 value 지정

볼륨 연결

Pod 내부 스토리지는 Pod가 사라지면 저장된 데이터도 삭제가 되기 때문에 Pod 생명주기와 상관없이 데이터를 유지하고 싶으면 볼륨을 따로 연결해야 합니다.

 

  • host Volume: docker -v 옵션과 유사하게 호스트 서버의 디렉토리에 연결할 수 있는 설정 입니다.
더보기
apiVersion: v1
kind: Pod
metadata:
  name: volume
spec:
  containers:
  - image: nginx
    name: nginx
  volumeMounts: // 컨테이너 내부에 사용될 볼륨 선언
  - mountPath: /container-volume // 컨테이너 내부에 볼륨이 연결될 위치를 지정
    name: my-volume // volumeMounts와 volumes를 연결하는 식별자
  volumes: : // Pod에서 사용할 volume
  - name: my-volume // volumeMounts와 volumes를 연결하는 식별자
    hostPath: // 호스트 서버의 연결 위치
      path: /home
  • emptyDir volume : emptyDir volumne은 Pod 내에서 임시로 생성해서 사용하며 주로 컨테이너 끼리 파일 데이터를 주고 받을 때 자주 사용합니다.
더보기
apiVersion: v1
kind: Pod
metadata:
  name: volume
spec:
  containers:
  - image: nginx
    name: nginx
  volumeMounts:
  - mountPath:
    name: my-volume
  volumes: :
  - name: my-volume
    emptyDir: {} // 컨테이너가 디렉토리를 공유하는 공간으로, Pod의 생명주기를 따라가는 임시 volume

리소스 관리

쿠버네티스에서 컨테이너 실행에 필요한 리소스를 제약하는 메커니즘을 제공 recoursec property를 사용하여 제공합니다.

  • requests : Pod의 최소 리소스 사용량
더보기
apiVersion: v1
kind: Pod
metadata:
  name: requests
spec:
  containers:
  - image: nginx
    name: nginx
    resources: 
      requests: // 최소 리소스 사용량 정의
        cpu: "250m" // CPU 리소스 최소 사용량 정의
        memory: "500Mi" // memory 리소스 최소 사용량 정의
  • limits : Pod의 최대 리소스 사용량
더보기
apiVersion: v1
kind: Pod
metadata:
  name: limits
spec:
  restartPolicy: Never
  containers:
  - image: python:3.7
    name: limit
    command: ["python"]
    args: ["-c", "arr = []\nwhile True: arr.append(range(1000))"]
    resources:
      limits: // 최대 리소스 사용량 정의
        cpu: "250m" // CPU 리소스 최대 사용량 정의
        memory: "500Mi" // memory 리소스 최대 사용량 정의

상태 확인 (Health Check)

  • livenessProbe : livenessProbe property는 컨테이너가 정상적으로 살아있는지 확인하기 위해 사용합니다
더보기
apiVersion: v1
kind: Pod
metadata:
  name: lieveness
spec:
  containers:
  - image: nginx
    name: nginx
    livenessProbe: // Pod가 정상적으로 동작하고 있는지 확인하는 property
      httpGet: // HTTP GET method를 이용하셔 상태 확인 작업 수행
        path: /live // HTTP Path를 지정
        port: 80 // HTTP Port를 지정
        
# liveness.yaml 실행
kubectl apply -f liveness.yaml

# watch를 사용하여 liveness 상태 확인
watch kubectl get pod leveness

# /live 호출시 404Error를 반환 받기 때문에 Pod가 정상적이지 않다고 판단하여
# Pod를 강제로 재시작(자차치유) 합니다. [RESTARTS가 계속 증가하는 이유입니다.]
# NAME       READY   STATUS    RESTARTS   AGE
# leveness   1/1     Running   5          3m16s
  • readinessProbe : readinessPorbe property는 Pod 생성 직후, 트래픽을 받을 준비가 완료되었는지 확인하는 property입니다.
더보기
# readinessProbe에서 httpGet을 사용하는 방법
apiVersion: v1
kind: Pod
metadata:
  name: lieveness
spec:
  containers:
  - image: nginx
    name: nginx
    readinessProbe: // Pod의 준비 완료를 확인하는 property
      httpGet:
        path: /ready
        port: 80
# readinessProbe에서 exec을 사용하는 방법
apiVersion: v1
kind: Pod
metadata:
  name: lieveness
spec:
  containers:
  - image: nginx
    name: nginx
    readinessProbe:
      exec: // 명령 실행 옵션
        command: // 사용자가 실행할 명령을 지정
        - cat
        - /tmp/ready
  • startupProbe : 컨테이너 내의 애플리케이션이 시작되었는지 나타냅니다. startupProbe가 성공할 때까지 다른 probe는 비활성화 되며, 만약 startupProbe가 실패하면 쿠버네티스트는 컨테이너를 죽이고 컨테이너 재시작 정책을 처리합니다.

2개의 컨테이너를 실행하는 이유

컨테이너를 2개 이상 실행하는 경우는 여러 이유가 있지만 대표적으로 사이드카 패턴을 적용하기 위해서 입니다.

# main 컨테이너인 nginx는 본연의 임무를 수행하고 사이드 컨테이너인 curl은 loop를 돌며 5초간 대기후 localhost를 호출합니다.
apiVersion: v1
kind: Pod
metadata:
  name: second
spec:
  containers:
  - image: nginx
    name: nginx
  - image curlimages/curl
    name : curl
    command: ["/bin/sh"]
    args: ["-c", "while true; do sleep 5; curl localhost; done"]
    
    
# 2개 이상 컨테이너를 등록한 파드의 경우 -c 옵션을 통해 컨테이너를 지정해야 합니다.
kubectl logs second -c nginx
사이트가 패턴 : 오토바이의 사이드카를 비유한 것으로 메인 컨테이너가 주어진 본연의 임무를 수행하고 사이트카 컨테이너에서 메인 컨테이너를 보조하는 역할을 담달할 때 사용하는 패턴입니다.

초기화 컨테이너

기본적으로 컨테이너를 2개 이상 생성하는 Pod의 경우 실행순서를 보장하지 않습니다. 때문에 명시적으로 컨테이너가 실행되기 전에 초기화 작업(initalize)을 수행해야 하는경우 initContainers property를 사용해야합니다.

apiVersion: v1
kind: Pod
metadata:
  name: init-container
spec:
  restartPolicy: OnFailure
  containers:
  - image: busybox:1.28
    name: busybox
    command: ["ls"]
    args: ["/tmp/moby"]
    volumeMounts:
    - name: workdir
      mountPath: /tmp
  initContainers: // 메인 컨테이너 실행에 앞에 초기화를 위해 먼저 실행되는 컨테이너
  - name: git
    image: alpine/git
    command: ["sh"]
    args:
    - "-c"
    - "git clone https://github.com/moby/moby.git /tmp/moby"
    volumeMounts:
    - name: workdir
      mountPath: "/tmp"
  volumes:
  - name: workdir
    emptyDir: {}
    
 # Pod 생성
 kubectl apply -f init-container.yaml
 
 # init-container Pod 확인
 kubectl get pod
 # init-container   0/1     Completed   0          50s

initContainer property를 사용한 경우 다음과 같이 각 컨테이너의 log를 확인할 수 있습니다.

  • 메인 : kubectl logs init-container
  • initContainer : kubectl logs init-container -c git -f

Config 설정

쿠버네티스에서 설정값을 따로 모아 관리하는 ConfigMap을 사용해서 Pod 전달할 수 있습니다.

ConfigMap 리로스 생성

configMap은 다음과 같은 명령어를 통해서 생성할 수 있는 3가지 방법이 있습니다.

kubectl create configmap <KEY> <DATA-SOURCE>
  • --frome-file 옵션을 사용하여 File을 <DATA-COURSE>로 사용할 수 있습니다.
kubectl create configmap game-config --from-file=game.properties
# game.properties
# weapon=gun
# health=3
# damage=1
  • --from-literal 옵션을 사용해 사용자가 직접 설정이 가능합니다.
kubectl create configmap special-config \
--from-literal=special.power=10 \
--from-literal=special.strength=20
  • ConfigMap 리소스를 YAML로 작성하여 생성할 수도 있습니다.
kubectl apply -f monster-config.yaml

# monster-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: monster-config
  namespace: default
data:
  monsterType: fire
  monsterNum: "5"
  monsterLife: "3"

위와같이 생성한 configMap을 조회하면 다음과 같이 출력됩니다.

# 생성한 configMap 조회
kubectl get cm game-config -o yaml

# game-config 
apiVersion: v1
data: // 설정값
  game.properties: |
    weapon=gun
    health=3
    damage=1
kind: ConfigMap // ConfigMap 리소스
metadata:
  creationTimestamp: "2021-07-25T07:16:38Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:game.properties: {}
    manager: kubectl
    operation: Update
    time: "2021-07-25T07:16:38Z"
  name: game-config
  namespace: default
  resourceVersion: "12243"
  selfLink: /api/v1/namespaces/default/configmaps/game-config
  uid: 6dfc53cc-23b5-4222-bdca-4af7a3dafbcc

ConfigMap 활용

볼륨 연결

apiVersion: v1
kind: Pod
metadata:
  name: game-volume
spec:
  restartPolicy: OnFailure
  containers:
  - image: busybox:1.28
    name: game-volume
    command: ["/bin/sh", "-c", "cat /ect/config/game.properties"]
    volumeMounts:
    - name: game-volume
      mountPath: /etc/config
  volumes:
  - name: game-volume
    configMap: // configMap property를 사용하여 configMap을 사용
      name: game-config // 볼륨으로 사용할configMap 이름

환경변수 - valueFrom (1개의 설정만 지정)

apiVersion: v1
kind: Pod
metadata:
  name: special-env
spec:
  restartPolicy: OnFailure
  containers:
  - image: busybox:1.28
    name: special-env
    command: ["printevnt"]
    args: ["special_env"]
    env: // 컨테이너에 환경변수 사용을 선언
    - name: special_env // 환경변수 key 지정
      valueFrom: // valueFrom을 사용함으로써 다른 리소스의 정보를 참조하도록 선언
        configMapKeyRef: // configMap 키를 참조
          name: special-config // configMap 이름을 설정
          key: special.power // configMap내의 포함된 설정값 중 특정 설정값을 명시적으로 선택

환경변수 - envFrom

apiVersion: v1
kind: Pod
metadata:
  name: monster-env
spec:
  restartPolicy: OnFailure
  containers:
  - image: busybox:1.28
    name: monster-env
    command: ["printevnt"]
    envFrom: // env 대신 evnFrom을 사용하면 ConfigMap 설정값 전체를 환경변수로 선언
    - configMapRef: // configMap 전체를 사용하도록 설정
        name: monster-config //사용하려는 configMap 이름을 명시적으로 지정

민감한 데이터 관리 (Secret)

Secret은 ConfigMap과 유사하지만 민감한 데이터를 저장하는데 사용할 수 있는 리소스입니다. Secret 리소스는 각 노드에서 사용될 때 디스크에 저장되지 않고 tmpfs라는 메모리 기반 파일시스템을 사용합니다. 또한 Secret 리소스 조회시 평문보다 비교적 안전한 base64로 인코딩되어 포시됩니다.

Secret 리로스 생성

  • YAML을 통한 생성 (사용자가 데이터를 base64로 인코딩)
#echo -ne admin | base64
# YWRtaW4=
#echo -ne password123 | base64
# cGFzc3dvcmQxMjM=

apiVersion: v1
kind: Secret
metadata:
  name: user-info
type: Qpaque // Secret 리소스 타입 설정 [Qpaque, kubernetes.io/service-account-token, kubernetes.io/tls]
data: // 저장하려는 민감한 데이터
  username: YWRtaW4=
  password: cGFzc3dvcmQxMjM=
  • YAML을 통한 생성 (쿠버네티스가 데이터를 base64로 인코딩)
apiVersion: v1
kind: Secret
metadata:
  name: user-info
type: Qpaque
sringData: // 쿠버네티스에서 base64로 인코딩 작업을 대신 수행
  username: YWRtaW4=
  password: cGFzc3dvcmQxMjM=
  • 명령형 커맨드를 이용한 생성 [--form-env-file, --from-file, --form-literal]
kubectl create secret generic user-info-from-file \
--from-env-file=user-info.properties

# user-info.properties
# username=admin
# password=password123

 

--form-env-file, --from-file 차이 : --from-file은 파일 내용을 전부 설정하는 반면, --form-env-file은 내부의 key-value 값을 나누어서 저장합니다.

Secret 활용

볼륨 연결

apiVersion: v1
kind: Pod
metadata:
  name: secret-volume
spec:
  restartPolicy: OnFailure
  containers:
  - name: secret-volume
    image: busybox:1.28
    command: ["sh"]
    args: ["-c", "ls /secret; cat /secret/username"]
    volumeMounts:
    - name: secret
      mountPath: /secret
  volumes:
  - name: secret
    secret:
      secretName: user-info // secret 이름을 명시적으로 설정

환경변수 - env

apiVersion: v1
kind: Pod
metadata:
  name: secret-env
spec:
  restartPolicy: OnFailure
  containers:
  - name: secret-env
    image: busybox:1.28
    command: ["printenv"]
    env:
    - name: USERNAME
      valueFrom:
        secretKeyRef: // secret key 참조
          name: user-info
          key: username
    - name: PASSWORD
      valueFrom:
        secretKeyRef: // secret key 참조
          name: user-info
          key: password

환경변수 - envFrom

apiVersion: v1
kind: Pod
metadata:
  name: secret-envFrom
spec:
  restartPolicy: OnFailure
  containers:
  - name: secret-envFrom
    image: busybox:1.28
    command: ["printenv"]
    envFrom:
    - secretRef: // secret 전체를 사용하도록 설정
      name: user-info

메타데이터 절달

쿠버네티스에서 Pod의 메타데이터를 컨테이너에 절달할 수 있도록 Downward API를 제공합니다.

볼륨 연결

apiVersion: v1
kind: Pod
metadata:
  name: downward-volume
  labels:
    zone: ap-north-east
    cluster: cluster1
spec:
  restartPolicy: OnFailure
  containers:
  - name: downward
    image: busybox:1.28
    command: ["sh", "-c"]
    args: ["cat /etc/podinfo/labels"]
    volumeMounts:
    - name: podinfo
      mountPath: /etc/podinfo
  volumes:
  - name: podinfo
    downwardAPI: // DownwardAPI 볼륨 사용을 선언
      items: // 메타데이터로 사용할 아이템 리스트를 지정
      - path: "labels" // 볼륨과 연결될 컨테이너 내부 Path를 시정
        fieldRef: // 참조할 필드를 선언
          fieldPath: metadata.labels // Pod의 메타데이터 필드를 지정

환경병수 - env

apiVersion: v1
kind: Pod
metadata:
  name: downward-env
spec:
  restartPolicy: OnFailure
  containers:
  - name: downward
    image: busybox:1.28
    command: ["printenv"]
    env:
    - name: NODE_NAME
      valueFrom:
        fieldRef: // 참조할 필드를 선언
          fieldPath: spec.nodeName // Pod의 메타데이터 필드를 지정
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP