티스토리 뷰

Pod 살펴보기

Pod 생성

쿠버네티스의 모든 리소스는 YAML 형태의 선언형 정의서 (declarative description)으로 표현될 수 있습니다. 먼저 다음과 같이 Pod template을 만들어 보겠습니다. --dry-run, -o yaml 옵션 조합으로 실제 Pod를 생성하지 않고 template을 생성할 수 있습니다.

kubectl run mynginx --image nginx --restart Never --dry-run -o yaml > mynginx.yaml

cat mynginx.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: mynginx
  name: mynginx
spec:
  restartPolicy: Always
  containers: 
  - name: mynginx
    image: nginx
  • apiVersion: 이름 conflict를 방지하기 위해 apiVersion을 명시함으로써, scope를 정의합니다. java의 package와 비슷한 역할을 합니다.
  • kind: 리소스의 타입을 정의합니다. java의 class 이름과 비슷한 역할을 합니다.
  • metadata: 리소스의 메타 정보를 나타냅니다.
    • label: 리소스의 라벨정보를 표기합니다.
    • name: 리소스의 이름을 표기합니다.
  • spec: 리소스를 정의합니다. (description) 여기서부터 리소스마다 조금씩 상이합니다.
    • restartPolicy: 재시작 정책을 지정합니다. (Always, OnFailure, Never가 있습니다.)
    • containers: 한개 이상의 컨테이너를 정의합니다. 예시에는 한개 컨테이너만 존재합니다.
      • name: 컨테이너의 이름을 표기합니다.
      • image: 컨테이너의 이미지를 표기합니다.

Restart Policy(재시작 정책)

  • Always: 항상 재시작합니다. (종료 시 + 에러 발생 시) 웹 서버와 같이 항상 떠있는 데몬 형태의 컨테이너에 적합합니다. (Pod 리소스 기본값)
  • OnFailure: 실패 시에만 재시작합니다. (에러 발생 시) 기계학습과 같이 배치 작업에 적합합니다. (뒤에서 살펴 볼 Job 리소스 기본값)
  • Never: 재시작하지 않습니다. 멱등성이 보장되지 않는 작업이 적합합니다. (멱등성이란, 동일한 연산을 반복하여 적용하여도 동일한 결과가 나오는 성질)

apply 명령

apply 명령은 Pod YAML 정의서를 이용하여 Pod를 생성하는 명령입니다. -f 옵션을 이용하여 파일을 전달하면 YAML 정의서에 맞게 Pod가 생성됩니다. docker-compose의 docker-compose.yaml 파일과 비슷한 방식이라고 생각하시면 됩니다.

# Pod 생성
kubectl apply -f mynginx.yaml
# pod/mynginx created

혹은 cat & here document 조합으로 즉석에서 Pod를 생성할 수도 있습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: mynginx
  name: mynginx-apply
spec:
  containers: 
  - name: mynginx
    image: nginx
EOF
kubectl get pod
# NAME            READY   STATUS    RESTARTS   AGE
# mynginx         1/1     Running   0          95s
# mynginx-apply   1/1     Running   0          67s

kubectl delete pod mynginx-apply
# pod "mynginx-apply" deleted

라벨링 시스템

앞서 metadata property에서 라벨 정보를 입력하는 것을 확인할 수 있었습니다. 이 라벨 정보를 어떻게 활용할 수 있는지 살펴 보겠습니다.

label 정보 추출

해당 Pod에 특정 라벨을 확인하기 위해서 -L옵션을 사용하고 전체 라벨을 확인하기 위해서는 --show-labels를 사용합니다.

# key가 app인 라벨의 값 표시
kubectl get pod mynginx -L run
# NAME      READY   STATUS    RESTARTS   AGE   RUN
# mynginx   1/1     Running   0          17s   mynginx

# 모든 라벨 정보 표시
kubectl get pod mynginx --show-labels
# NAME      READY   STATUS    RESTARTS   AGE   LABELS
# mynginx   1/1     Running   0          26s   run=mynginx

label을 이용한 필터

여러 라벨들 중에서 특정 라벨을 이용하여 필터하기 위해서 -l옵션을 사용합니다.

# 새로운 Pod yournginx 생성
kubectl run yournginx --image nginx --restart Never

# key가 run인 Pod들 출력
kubectl get pod -l run
# NAME        READY   STATUS    RESTARTS   AGE
# mynginx     1/1     Running   0          57s
# yournginx   1/1     Running   0          7s

# key가 run이고 value가 mynginx인 Pod 출력
kubectl get pod -l run=mynginx
# NAME        READY   STATUS    RESTARTS   AGE
# mynginx     1/1     Running   0          57s

# key가 run이고 value가 yournginx Pod 출력
kubectl get pod -l run=yournginx
# NAME        READY   STATUS    RESTARTS   AGE
# yournginx   1/1     Running   0          7s

nodeSelector를 이용한 노드 선택

쿠버네티스에서는 Pod를 하나 생성하면 기본적으로 마스터가 어떤 노드 위에서 실행될지를 판단하여 스케줄링합니다. 쿠버네티스는 클러스터링 시스템이기 때문에 사용자가 특정 서버를 일일이 선택하여 Pod를 실행시키기 않고 클러스터에서 관리해 줍니다. 하지만 간혹 특정 서버를 직접적으로 선택하거나 특정 노드 그룹 안에서 실행되게끔 할 수도 있습니다. 바로 nodeSelect라는 property를 이용하는 것입니다.

# 특정 노드에 특정 라벨 부여
kubectl label node master disktype=ssd
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-nodeselector
spec:
  containers: 
  - name: mynginx
    image: nginx
  # 특정 노드 라벨 선택
  nodeSelector:
    disktype: ssd
EOF

만약 두개 이상의 노드에 동일한 라벨이 부여되어 있는 경우, 같은 노드 그룹 안에서는 k8s 마스터가 적절히 스케줄링합니다.

Pod를 상세 조회해보시면 nodeSelector가 정의되어 있는 것을 확인할 수 있습니다.

kubectl get pod mynginx-nodeselector -oyaml
# apiVersion: v1
# kind: Pod
# metadata:
#   annotations:
#     kubectl.kubernetes.io/last-applied-configuration: |
#       {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"mynginx-nodeselector","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"mynginx"}],"nodeSelector":{"disktype":"ssd"}}}
#   ...
#   enableServiceLinks: true
#   nodeName: master
#   nodeSelector:
#     disktype: ssd
#   priority: 0
#   ...

환경변수 설정

docker-compose 와 유사하게 Pod에 환경변수를 입력해 보겠습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-env
spec:
  containers: 
  - name: mynginx
    image: nginx
    env:
    - name: MY_ENV
      value: hello
EOF

환경변수를 가지는 Pod를 생성합니다. 여기서 새로운 명령이 나오는데 바로 apply 명령입니다. 선언형 리소스 정의서 (YAML)를 생성할때는 apply라는 명령을 사용합니다.

# 환경변수 MY_ENV 확인
kubectl exec mynginx-env -- printenv | grep MY_ENV
# MY_ENV=hello

Volume 연결

도커에서와 마찬가지로 Pod에 Volume을 연결할 수 있습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: volume
spec:
  containers: 
  - name: mynginx
    image: nginx
    volumeMounts:
    - mountPath: /test-volume
      name: my-volume
  volumes:
  - name: my-volume
    hostPath:
      # directory location on host
      path: /home
      # this field is optional
      type: Directory
EOF

크게 volumeMounts 부분과 volumes 부분으로 나눌 수 있습니다. volumeMounts에서는 컨테이너 내부에서 사용할 디렉토리를, volumes에서는 Host 서버에서 연결될 디렉토리를 정의하고 my-volume 이라는 이름을 참조하여 서로 연결이 됩니다.

kubectl exec volume -- ls -al /test-volume
# total 12
# drwxr-xr-x 3 root root 4096 Jun 25 11:54 .
# drwxr-xr-x 1 root root 4096 Jun 25 15:39 ..
# drwxr-xr-x 7 1000 1000 4096 Jun 25 15:25 ubuntu

매개변수 전달

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-param
spec:
  containers: 
  - name: ubuntu
    image: ubuntu:18.04
    command: [ "echo" ]
    args: [ "abc", "def" ]
  restartPolicy: OnFailure
EOF
  • command: 명령어 (entrypoint)
  • args: 매개변수 (cmd)
  • restartPolicy: 기존에는 Never라는 옵션을 사용했는데 (생략시, Never) 이번에는 OnFailure라고 지정해 주었습니다. OnFailure는 말그대로 실패시에만 다시 Pod를 실행한다는 것을 의미합니다. echo "abc def" 명령은 웹 서버처럼 계속 실행되고 있는 명령이 아니라 일회성으로 실행되고 종료되는 명령이기에 OnFailure를 지정해 줍니다.

Pod에 파라미터를 전달할 수 있는 메커니즘이 있습니다. docker의 entrypoint와 cmd와 유사합니다.

kubectl logs pod-param
# abc def

리소스 관리

쿠버네티스는 컨테이너의 리소스를 제약할 수 있는 메커니즘을 제공합니다. resources라는 property를 작성함으로서 리소스를 조절합니다. resources property 아래에는 두가지 종류가 있는데 최소 spec을 보장해주는 requests, 최대 리소스 사용량을 제한하는 limits가 있습니다.

requests

쿠버네티스 클러스에게 Pod가 사용할 최소 리소스 사용량을 정의합니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: requests
spec:
  containers: 
  - name: mynginx
    image: nginx
    resources:
      requests:
        cpu: "250m"
        memory: "500Mi"
EOF

cpu의 1000m은 1 cpu를 core를 뜻합니다. 250m은 0.25 core를 의미합니다. memory의 Mi는 1MiB (2^20 bytes) 를 의미합니다. 예시에서는 쿠버네티스에게 최소 리소스 CPU 0.25와 메모리 500Mi를 요청합니다.

limits

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: limits
spec:
  containers: 
  - name: mynginx
    image: python:3.7
    command: [ "python" ]
    args: [ "-c", "arr = []\nwhile True: arr.append(range(1000))" ]
    resources:
      limits:
        cpu: "500m"
        memory: "1Gi"
EOF

반대로 limits는 최대 리소스 사용량을 제한합니다. 예시에서는 최대 0.5 cpu와 1Gi 메모리 사용을 제한합니다.

STATUS의 변화를 살펴봐 주세요.

watch kubectl get pod
# NAME     READY   STATUS    RESTARTS   AGE
# limits   1/1     Running   1          12s

두개 property를 조합하면 다음과 같습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-resource
spec:
  containers: 
  - name: mynginx
    image: nginx
    resources:
      requests:
        cpu: "250m"
        memory: "500Mi"
      limits:
        cpu: "500m"
        memory: "1Gi"
EOF

HealthChectk과 Probe

livenessProbe

쿠버네티스에는 컨테이너가 정상적으로 살아있는지 확인하는 메커니즘을 제공합니다. 바로 livenessProbe을 통해서 체크를 하는데요. livenessProbe에 health check을 하고 싶은 포트와 path를 지정하면 쿠버네티스가 장애 여부를 판단하여 상태를 표시하거나 추가적인 조치를 취합니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-live
spec:
  containers: 
  - name: mynginx
    image: nginx
    livenessProbe:
      httpGet:
        path: /
        port: 80
EOF

readinessProbe

livenessProbe는 현재 쿠버네티스가 살아있는지 확인하는 용도라면 readinessProbe은 Pod가 생성되었을때 해당 컨테이너로 트래픽을 보내도 되는지 확인하는 property입니다. Jenkins 서버와 같이 처음 구동하는데에 시간이 오래 걸리는 웹 프로그램인 경우, 웹 서버 구동이 완료된 이후에 트래픽을 받기 시작해야 됩니다. 이런 경우 readinessProbe을 통해서 해당 Pod의 initialization이 완료되었다는 것을 쿠버네티스에게 알리는 용도로 사용합니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-ready
spec:
  containers: 
  - name: mynginx
    image: nginx
    readinessProbe:
      httpGet:
        path: /ready
        port: 80
EOF

livenessProbe, readinessProbe 모두 http 통신뿐만 아니라 파일시스템 기반의 파일 존재 유무를 통해서도 살아있는지, 준비되었는지 확인을 할 수 있습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-ready-cmd
spec:
  containers: 
  - name: mynginx
    image: nginx
    readinessProbe:
      exec:
        command:
        - cat
        - /tmp/ready
EOF

readinessProbe의 상태를 아래 READY 0/1 표시를 통해 알 수 있습니다.

kubectl get pod
# NAME                READY   STATUS    RESTARTS   AGE
# mynginx-live        1/1     Running   0          44s
# mynginx-ready       0/1     Running   0          35s
# mynginx-ready-cmd   0/1     Running   0          12d

어떻게 하면 READY가 1/1로 바뀔까요?


Second container

앞서 쿠버네티스의 최소 가상환경 단위인 Pod는 한개 이상의 컨테이너의 집합이라고 설명을 드렸습니다. 이번 섹션에서는 한개 Pod 내에 두개의 컨테이너를 띄워 보도록 하겠습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: mynginx-second
spec:
  containers: 
  - name: mynginx
    image: nginx
  - name: curl
    image: curlimages/curl
    command: ["sh"]
    args: ["-c", "sleep 5 && curl localhost"]
EOF

위와 같은 Pod를 도커 컨테이너로 실행하시려면 다음과 같습니다.

docker run -p 5000:80 -d nginx
docker run --entrypoint sh curlimages/curl -c 'sleep 5 && curl $HOST:5000'

이제 로그를 확인해 봅시다.

# mynginx log 확인
kubectl logs mynginx-second -c mynginx

# curl log 확인
kubectl logs mynginx-second -c curl

curl을 실행하기 전에 sleep을 한 이유로, 쿠버네티스는 Pod 내부의 컨테이너끼리의 실행 순서를 보장하지 않습니다. 그렇기 때문에 nginx 컨테이너가 정상적으로 실행 된 이후에 curl을 호출하기 위해서 5초간 sleep 하였습니다.


Init container

앞서 컨테이너끼리는 실행순서를 보장하지 않다고 설명 드렸습니다. 명시적으로 메인 컨테이너가 실행되기 전에 미리 초기화 작업을 (initalize) 수행해야 하는 경우는 어떻게 할 수 있을까요? 바로 initContainers를 이용합니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: init-container
spec:
  containers: 
  - name: mynginx
    image: nginx
  initContainers:
  - name: git
    image: alpine/git
    command: ["sh"]
    args:
    - "-c"
    - "git clone https://github.com/moby/moby.git /tmp/moby"
EOF
  • initContainers: 메인 컨테이너 (containers) 실행에 앞서 초기화를 위해 먼저 실행되는 컨테이너를 정의합니다.

ConfigMap

mkdir conf
cd conf
wget https://raw.githubusercontent.com/hongkunyoo/docker-k8s-workshop/master/k8s/configmap/game-env.properties
wget https://raw.githubusercontent.com/hongkunyoo/docker-k8s-workshop/master/k8s/configmap/game.properties
wget https://raw.githubusercontent.com/hongkunyoo/docker-k8s-workshop/master/k8s/configmap/ui.properties

ConfigMap는 여타 리소스와는 조금 다르게 실제로 실행되는 주체가 아닌 메타 데이터(설정값)를 저장하는 리소스입니다. 지금까지는 설정파일이 특정 어플리케이션 안에 속해져 있는 경우가 많았는데 쿠버네티스를 이용하게 되면 설장파일 자체를 쿠버네티스 레벨로 빼낼 수 있게 됩니다.

kubectl create configmap <map-name> <data-source>
# conf 폴더 아래 다음과 같은 파일들이 있습니다.
ls .
# game.properties  ui.properties

# 간단한 설정값들이 있습니다.
cat game.properties
# enemies=aliens
# lives=3
# enemies.cheat=true
# enemies.cheat.level=noGoodRotten
# secret.code.passphrase=UUDDLRLRBABAS
# secret.code.allowed=true

# conf 디렉토리 아래의 파일들을 이용하여 game-config라는 configmap 생성합니다. (--from-file 옵션)
cd ..
kubectl create configmap game-config --from-file=conf/

kubectl get configmap
# NAME          DATA   AGE
# game-config   3      13s

# 혹은 각각 파일을 참조할 수도 있습니다.
kubectl create configmap game-config2 --from-file=conf/game.properties --from-file=conf/ui.properties

kubectl get configmap
# NAME           DATA   AGE
# game-config    3      39s
# game-config2   2      18s

# 생성된 ConfigMap을 확인하면 설정값들이 저장된 것을 확인할 수 있습니다.
kubectl get configmaps game-config -o yaml
# apiVersion: v1
# kind: ConfigMap
# metadata:
#   creationTimestamp: 2016-02-18T18:52:05Z
#   name: game-config
#   namespace: default
#   resourceVersion: "516"
#   uid: b4952dc3-d670-11e5-8cd0-68f728db1985
# data:
#   game.properties: |
#     enemies=aliens
#     lives=3
#     enemies.cheat=true
#     enemies.cheat.level=noGoodRotten
#     secret.code.passphrase=UUDDLRLRBABAS
#     secret.code.allowed=true
#     secret.code.lives=30
#   ui.properties: |
#     color.good=purple
#     color.bad=yellow
#     allow.textmode=true
#     how.nice.to.look=fairlyNice


# 이번에는 --from-literal 옵션을 사용하여 ConfigMap을 생성해 보겠습니다.
kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

# literal로 직접 key, value가 저장된 것을 확인할 수 있습니다.
kubectl get configmaps special-config -o yaml
# apiVersion: v1
# kind: ConfigMap
# metadata:
#   creationTimestamp: 2016-02-18T19:14:38Z
#   name: special-config
#   namespace: default
#   resourceVersion: "651"
#   uid: dadce046-d673-11e5-8cd0-68f728db1985
# data:
#   special.how: very
#   special.type: charm

env.valueFrom

생성된 ConfigMap을 사용하는 방법에 대해서 알아보겠습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: cat-env
spec:
  restartPolicy: Never
  containers:
  - name: cat-env
    image: k8s.gcr.io/busybox
    command: [ "printenv" ]
    args: [ "CONFIG_KEY" ]
    env:
    # 기존 환경변수 설정 방법
    - name: ORIGINAL_KEY
      value: "ORIGINAL_VALUE"
    # ConfigMap을 이용하여 환경설정하는 방법
    - name: CONFIG_KEY
      valueFrom:
        configMapKeyRef:
          name: special-config
          key: special.how
EOF
kubectl logs cat-env
# very

envFrom

envFrom이라는 property를 사용하여 ConfigMap 전체 설정값을 환경변수로 사용할 수도 있습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: cat-envfrom
spec:
  restartPolicy: Never
  containers:
  - name: cat-envfrom
    image: k8s.gcr.io/busybox
    command: [ "printenv" ]
    # env 대신에 envFrom 사용
    envFrom:
    - configMapRef:
        name: special-config
EOF
kubectl logs cat-envfrom
# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# HOSTNAME=cat-envfrom
# special.how=very
# special.type=charm
# KUBERNETES_SERVICE_PORT=443
# KUBERNETES_SERVICE_PORT_HTTPS=443
# KUBERNETES_PORT=tcp://10.43.0.1:443
# KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
# KUBERNETES_PORT_443_TCP_PROTO=tcp
# KUBERNETES_PORT_443_TCP_PORT=443
# KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
# KUBERNETES_SERVICE_HOST=10.43.0.1
# HOME=/root

Mount as Volume

ConfigMap을 환경변수 뿐만 아니라 Volume으로 마운트하여 파일처럼 사용할 수도 있습니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: ls-config
spec:
  restartPolicy: Never
  containers:
  - name: ls-config
    image: k8s.gcr.io/busybox
    command: [ "/bin/sh", "-c", "ls /etc/config/" ]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: special-config
EOF
kubectl logs ls-config
# special.how
# special.type

Clean up

kubectl delete pod --all

 

공부를 하며 도움이 된 글을 토대로 작성하였습니다. 이곳에서 참고하였습니다. 

 

hongkunyoo/docker-k8s-workshop

도커 쿠버네티스 워크샵. Contribute to hongkunyoo/docker-k8s-workshop development by creating an account on GitHub.

github.com

 

끝!

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30