티스토리 뷰
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
공부를 하며 도움이 된 글을 토대로 작성하였습니다. 이곳에서 참고하였습니다.
끝!
'DevOps > Kubernetes' 카테고리의 다른 글
Kubernates Evicted Status Pod 삭제하기 (0) | 2021.04.30 |
---|---|
자주쓰는 Kubernates 명령어 정리 (with Docker 명령어) (0) | 2020.11.12 |
Kubernates 초간단 설치 (CentOS7, Windows10) (0) | 2020.10.16 |