Pod은 쿠버네티스에서 가장 기본이 되는 리소스다. 쿠버네티스에서는 각 리소스들이 빌딩 블록처럼 동작한다. 여러 개의 빌딩 블럭을 조합하여 하나의 커더란 빌딩 블럭을 만드는데, Pod은 가장 기초가 되는 빌딩 블럭이 된다.
Pod 소개
Pod는 쿠버네티스의 최소 실행 단위이다. 아무리 작고 단순한 프로세스 하나를 실행시키려 하더라도 Pod을 통해 수행된다. 쿠버네티스는 Pod을 통해 기본 가상환경을 제공한다.
가상환경 플랫폼 실행 단위
- 가상머신 : Instance
- 도커 : Container
- 쿠버네티스 : Pod
Pod의 특징
1개 이상의 컨테이너 실행
: Pod은 1개 이상의 컨테이너를 가질 수 있다. 보통은 1개 Pod에 1개의 컨테이너를 실행하지만, 상황에 따라 2개에서 3개까지 컨테이너를 실행한다.동일 노드에 할당
: Pod 내에 실행되는 컨테이너들은 반드시 동일한 노드에 할당되며 동일한 생명 주기를 갖는다. Pod을 삭제하면 Pod 내의 모든 컨테이너가 전부 같이 삭제된다.고유의 Pod IP
: Pod 리소스는 노드 IP와는 별개로 클러스터 내에서 접근 가능한 고유의 IP를 할당받는다. 따라서 고유의 IP를 통해 NAT 없이 직접 다른 노드에 위치한 Pod과 통신이 가능하다.IP 공유
: Pod 내에 있는 컨테이너들은 서로 IP를 공유한다. Pod 내부의 컨테이너끼리는localhost
를 통해 서로 네트워크 접근이 가능하며 포트를 이용하여 구분한다.volume 공유
: Pod 내의 컨테이너들은 동일한 볼륨과 연결이 가능하여 파일 시스템 기반으로 서로의 파일을 주고받을 수 있다.
Pod의 YAML 정의서
# mynginx.yaml 이라는 YAML 정의서 생성
# --dry-run과 -o yaml 옵션을 조합하여 Pod을 실제로 실행하지 않고 템플릿 파일을 생성
kubectl run mynginx --image nginx --dry-run=client -o yaml > mynginx.yaml
# 결과
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: mynginx
name: mynginx
spec:
containers:
- image: nginx
name: mynginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Pod을 구성하기 위한 최소 프로퍼티는 다음과 같다.
- apiVersion : 모든 리소스에는 apiVersion이 정의되어 있다. 리소스의 이름이 동일한 경우, 이름 충돌을 피하기 위한 목적으로 리소스의 scope를 정의한 것이다. 프로그래밍 언어의 패키지 이름과 비슷한 역할을 한다.
- kind : 리소스의 타입을 정의한다. 프로그래밍 언어에서 클래스 이름과 비슷한 역할을 한다.
- metadata : 리소스의 메타 정보를 나타낸다.
- labels : 리소스의 라벨 정보
- name : 리소스의 이름을 표시(mynginx)
- spec : 리소스의 스펙을 정의한다. spec은 리소스마다 조금씩 다르게 정의된다.
- name : 컨테이너의 이름
- image : 컨테이너의 이미지 주소
# YAML 정의서를 이용하여 Pod 생성
kubectl applfy -f mynginx.yaml
Pod을 생성하는 절차는 다음과 같다.
- 사용자가 kubectl 명령을 통해 Pod 정의를 쿠버네티스 마스터에 전달한다.(API Server로)
- 마스터는 YAML 정의의 유효성 체크 후 특정 노드에 사용자의 요청에 따라 컨테이너를 실행하도록 명령한다.
- 명령을 전달받은 kubelet은 요청 사항에 맞게 실제 컨테이너를 노드에 실행한다.
Pod에 새로운 기능을 추가할 때마다 Pod의 YAML 정의서의 spec 프로퍼티에 새로운 값들이 추가될 것이다.
라벨링 시스템
라벨링은 특정 리소스를 참조하거나, Pod에 트래픽을 전달할 때 사용하는 중요 메커니즘이다. 라벨 그 자체로는 단순한 key - value 형태의 문자열이다. Pod에 라벨을 부여한다는 의미는 이 key - value 형태의 문자열을 Pod의 metadata 프로퍼티에 추가한다는 것을 의미한다.(kubectl edit pod <NAME>
과 유사함) 단순한 작업이지만 이를 다양한 방법에 활용할 수 있어 라벨링은 중요 메커니즘이라 할 수 있다.
라벨 정보 부여
Pod에 라벨 정보를 부여하는 방법은 크게 두 가지가 있다.
첫 번째는 label
명령을 이용하는 방법이다.
kubectl label pod <NAME> <KEY>=<VALUE>
# mynginx라는 pod에 hello=world라는 라벨 추가
kubectl label pod mynginx hello=world
# 확인
kubectl get pod mynginx -o yaml
...
labels:
hello: world
run: mynginx
...
두 번째는 Pod의 YAML 정의서를 작성할 때 metadata 프로퍼티를 직접 추가하는 방법이다.
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
# hello=world label
labels:
hello: world
run: mynginx
name: mynginx
spec:
containers:
- image: nginx
name: mynginx
EOF
kubectl run <NAME>
명령을 수행하면 자동으로run=<NAME>
이라는 라벨이 추가된다. 그래서kubectl run mynginx --image nginx
를 실행하면 라벨에run=mynginx
프로퍼티가 추가된다.
라벨 정보 확인
Pod에 부여된 라벨을 확인하기 위해서 -L
옵션을 사용한다.
# 키 run에 대한 값을 확인
kubectl get pod mynginx -L run
NAME READY STATUS RESTARTS AGE RUN
mynginx 1/1 Running 0 88m mynginx
# 특정 라벨이 아닌 전체 라벨을 확인
kubectl get pod mynginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
mynginx 1/1 Running 0 88m hello=world,run=mynginx
라벨을 이용한 조건 필터링
특정 라벨을 가진 Pod만 필터링해서 보기 원한다면 -l
옵션을 사용하면 된다. 특정 key를 기준으로 필터링 할 수 있으며, key - value 전체를 이용해서 필터링 할 수도 있다.
# 테스트를 위해 yournginx 라는 pod 생성
kubectl run yournginx --image nginx
# 전체 pod 확인
kubectl get pod
NAME READY STATUS RESTARTS AGE
mynginx 1/1 Running 0 90m
yournginx 1/1 Running 0 6s
# key가 run인 Pod을 출력
kubectl get pod -l run
NAME READY STATUS RESTARTS AGE
mynginx 1/1 Running 0 91m
yournginx 1/1 Running 0 60s
# key가 run, value가 mynginx인 Pod을 출력
kubectl get pod -l run=mynginx
NAME READY STATUS RESTARTS AGE
mynginx 1/1 Running 0 92m
# key가 run, value가 yorunginx인 Pod을 출력
kubectl get pod -l run=yournginx
NAME READY STATUS RESTARTS AGE
yournginx 1/1 Running 0 2m9s
노드셀렉터(nodeSelector)를 이용한 노드 선택
라벨링 시스템을 이용하여 Pod이 특정 노드에 할당되도록 스케줄링이 가능하다. 별도의 설정 없이 Pod을 실행하면 쿠버네티스의 마스터 노드가 임의의 노드 위에서 Pod을 스케줄링한다.
쿠버네티스는 클러스터링 시스템이기 때문에 사용자가 매번 노드를 선택할 필요 없이 쿠버네티스가 Pod 스케줄링을 관리한다는 장점이 있다.
하지만 특정 Pod을 특정 노드에 할당할 필요가 있을 수도 있다. 예를 들어 디스크 설정이 A 노드는 SSD로, B 노드는 HDD로 되어 있다면 특정 Pod은 디스크 설정에 따라 A 혹은 B에 스케줄링 되어야 하기 때문이다.
# 전체 노드의 라벨 확인
kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
docker-desktop Ready control-plane 2d1h v1.24.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-desktop,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
도커 데스크톱으로 쿠버네티스를 실행하면 이런 결과를 얻을 수 있다. 노드에는 기본적으로 많은 라벨들이 설정되어 있는데, disktype
이라는 라벨을 추가하여 SSD, HDD를 구분하도록 할 것이다.
kubectl label node <NODE_NAME> <KEY>=<VALUE>
# docker-desktop 노드에 SSD 라벨 추가
kubectl label node docker-desktop disktype=SSD
# 노드의 라벨 확인
kubectl get node --show-labels
이제 새롭게 실행하려고 하는 Pod의 YAML 정의서에 노드셀렉터 프로퍼티를 추가해준다.
# node-selector.yaml
apiVersion: v1
kind: Pod
metadata:
name: node-selector
spec:
containers:
- name: nginx
image: nginx
# 노드 셀렉터
nodeSelector:
disktype: SSD
# or
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: node-selector
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
disktype: SSD
EOF
확인하는 방법은 다음과 같다.
kubectl get pod -o wide
만약 동일한 라벨을 가진 노드가 2개 이상이라면, 쿠버네티스는 노드의 상태(리소스 사용량 등)를 확인하여 그 가운데 최적의 노드 하나를 선택한다.
노드셀렉터 값을 변경하고 싶으면 edit을 사용하거나 YAML 정의서의 내용을 수정해서 다시 실행하면 된다.
실행 명령 및 파라미터 지정
Pod을 생성할 때, 실행 명령과 파라미터를 전달할 수 있다.
# cmd.yaml
apiVersion: v1
kind: Pod
metadata:
name: cmd
spec:
restartPolicy: OnFailure
containers:
- name: nginx
image: nginx
# 실행 명령
command: ["/bin/echo"]
# 파라미터
args: ["hello"]
- command : 컨테이너의 시작 실행 명령을 지정한다. 도커의 ENTRYPOINT에 대응하는 프로퍼티이다.
- args : 실행 명령에 넘겨줄 파라미터를 지정한다. 도커의 CMD에 대응되는 프로퍼티이다.
- restartPolicy : Pod의 재시작 정책을 설정한다.
- Always : Pod 종료시 항상 재시작을 시도(default)
- Never : 재시작 하지 않음
- OnFailure : 실패 시에만 재시작
# 로그 확인
kubectl logs -f cmd
환경변수 설정
env 프로퍼티를 사용하면 간단하게 환경변수를 설정할 수 있다.
# env.yaml
apiVersion: v1
kind: Pod
metadata:
name: env
spec:
containers:
- name: nginx
image: nginx
env:
- name: hello
value: "world!"
설정한 환경변수를 확인하는 방법은 exec 명령을 사용하면 된다.
kubectl exec env -- printenv | grep hello
볼륨 연결
Pod 내부 스토리지의 생명주기는 Pod과 동일하다. 따라서 Pod이 사라지면 스토리지에 저장된 데이터도 삭제된다. Pod의 생명주기와는 상관없이 데이터를 영구적으로 저장하고 싶다면 볼륨을 따로 연결해야 한다.
호스트 볼륨(host volume)은 도커의 -v
옵션과 유사하게 호스트 서버의 볼륨 공간에 Pod의 데이터를 저장하는 것을 의미한다.
# volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume
spec:
containers:
- name: nginx
image: nginx
# 컨테이너 내부의 연결 위치 지정
volumeMounts:
- mountPath: /container-volume
name: my-volume
# 호스트 서버의 연결 위치 지정
volumes:
- name: my-volume
hostPath:
# windows + docker desktop + wsl
path: /run/desktop/mnt/host/c/Users/김정민/Desktop/tools
- volumeMounts : 컨테이너 내부에 사용될 볼륨을 선언한다.
- mountPath : 컨테이너 내부에 볼륨이 연결될 위치를 지정한다.
- name : volumeMounts와 volumes를 연결하는 식별자로 사용된다.
- volumes : Pod에서 사용할 volume을 지정한다.
- name : volumeMounts와 volumes를 연결하는 식별자로 사용된다.
- hostPath : 호스트 서버의 연결 위치를 지정한다.
volume Pod을 생성해서 /container-volume
의 내부를 살펴보면 path
에 설정한 호스트 서버의 경로와 동일한 것을 확인할 수 있다.
Pod에는 임시 경로인 emptyDir 프로퍼티도 있는데, Pod 내부에 생성되어 데이터를 저장한다는 점에서 Pod 내부 스토리지를 이용한다는 것과 같으나, 여러 컨테이너가 서로 공유해서 사용할 수 있다는 특징을 가지고 있다.
# volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume
spec:
containers:
- name: nginx
image: nginx
# 컨테이너 내부의 연결 위치 지정
volumeMounts:
- mountPath: /container-volume
name: my-volume
# 호스트 서버의 연결 위치 지정
volumes:
- name: my-volume
emptyDir: {}
리소스(컴퓨팅 자원) 관리
쿠버네티스는 컨테이너 실행에 필요한 리소스를 리소스 프로퍼티를 이용하여 관리한다.
requests
Pod가 보장받을 수 있는 최소 리소스 사용량을 정의한다.
apiVersion: v1
kind: Pod
metadata:
name: requests
spec:
containers:
- name: nginx
image: nginx
resources:
cpu: "250m"
memory: "500Mi"
- cpu에서 1000m은 1core를 의미한다. 250m이면 0.25core가 된다
- memory의 Mi는 1MiB를 의미한다.
limits
Pod가 최대로 사용할 수 있는 최대 리소스 사용량을 정의한다.
apiVersion: v1
kind: Pod
metadata:
name: limits
spec:
restartPolicy: Never
containers:
- name: mynginx
image: python3.7
command: ["python"]
args: ["-c", "arr = []\nwhile True: arr.append(range(1000))"]
resources:
limits:
cpu: "500m"
memory: "1Gi"
컨테이너가 최대 리소스 사용량을 넘게 되면 CPU에서 스로틀링이 발생하고, 메모리는 OOM 에러가 발생하게 된다. 위의 파이썬 스크립트는 무한히 메모리 리소스를 소비한다. 위의 Pod을 실행하고 Pod의 상태를 확인해보면 OOMKiled
가 된 것을 확인할 수 있다.
kubectl apply -f limits.yaml
kubectl get pod
NAME READY STATUS RESTARTS AGE
limits 0/1 OOMKilled 0 25s
무한히 메모리를 소비하는 스크립트가 최대 메모리 리소스 사용량을 넘으면 강제로 프로세스가 중단된다. 쿠버네티스는 이를 통해 특정 프로세스의 리소스 사용률이 전체 서버에 영향을 주지 않고 해당 프로세스에만 영향을 미쳐 전체적으로 안정된 운영을 가능하게 한다.
requests - limits를 사용하여 각자 운영하려는 서비스에 적합한 최소 - 최대 리소스 사용량을 정하면 된다.
상태 확인
Pod이 정상적으로 작동하고 있는지 헬스 체크하는 기능들이 있다.
livenessProbe
컨테이너가 정상적으로 동작하는지 확인하기 위해 livenessProbe 프로퍼티를 이용한다. Pod가 정상적으로 동작하는지 확인하고, 자가치유를 위한 판단 기준으로 활용한다.
apiVersion: v1
kind: Pod
metadata:
name: liveness
spec:
containers:
- name: nginx
image: nginx
livenessProbe:
httpGet:
path: /live
port: 80
httpGet
메서드를 이용하여 해당 컨테이너의 헬스 체크 여부를 확인하게 된다.
위의 YAML 정의서에서는 Pod의 상태를 확인하는 방법으로 HTTP 프로토콜의 GET 메서드를 이용하여 /live
경로의 80번 포트를 지속적으로 호출하게 된다. 이때 HTTP 리턴 코드가 200 ~ 300 사이의 응답코드를 가지고 있으면 정상으로 판단하고, 그 이외 코드는 비정상으로 판단하여 컨테이너가 종료되고 재시작된다.
kubectl apply -f liveness.yaml
# 컨트롤 + C로 watch 종료하면 됨
watch kubectl get pod liveness
# RESTARTS의 값이 지속적으로 증가함(헬스 체크에 실패해서 재시작)
NAME READY STATUS RESTARTS AGE
liveness 1/1 Running 6 (11s ago) 4m2s
# 로그 확인
2022/06/27 12:04:10 [error] 31#31: *1 open() "/usr/share/nginx/html/live" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /live HTTP/1.1", host: "10.1.0.40:80"
10.1.0.1 - - [27/Jun/2022:12:04:10 +0000] "GET /live HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
2022/06/27 12:04:20 [error] 32#32: *2 open() "/usr/share/nginx/html/live" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /live HTTP/1.1", host: "10.1.0.40:80"
10.1.0.1 - - [27/Jun/2022:12:04:20 +0000] "GET /live HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
2022/06/27 12:04:30 [error] 33#33: *3 open() "/usr/share/nginx/html/live" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /live HTTP/1.1", host: "10.1.0.40:80"
10.1.0.1 - - [27/Jun/2022:12:04:30 +0000] "GET /live HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
watch
명령어로 liveness 컨테이너의 상태를 확인해보면 RESTARTS
의 값이 계속해서 증가하는 것을 확인할 수 있다. 쿠버네티스가 /live
경로에 대한 헬스 체크를 실시했을 때, 결과값으로 404
에러를 반환하기 때문에 해당 Pod이 비정상적이라고 판단하여 강제로 재시작 하기 때문이다.(자가치유 목적)
헬스 체크를 정상적으로 작동하게 만들기 위해 엔진엑스의 리소스 경로에 live
파일을 추가해준다.
# live 파일 생성
kubectl exec liveness -- touch /usr/share/nginx/html/live
# 로그 확인
10.1.0.1 - - [27/Jun/2022:12:09:20 +0000] "GET /live HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
2022/06/27 12:09:20 [error] 31#31: *1 open() "/usr/share/nginx/html/live" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /live HTTP/1.1", host: "10.1.0.40:80"
10.1.0.1 - - [27/Jun/2022:12:09:30 +0000] "GET /live HTTP/1.1" 200 0 "-" "kube-probe/1.24" "-"
10.1.0.1 - - [27/Jun/2022:12:09:40 +0000] "GET /live HTTP/1.1" 200 0 "-" "kube-probe/1.24" "-"
그리고 나서 다시 로그를 확인하면 정상적으로 200
의 응답 코드를 리턴하는 것을 확인할 수 있다. 헬스 체크에 성공했기 때문에 RESTARTS
의 값도 오르지 않게 된다.
readinessProbe
livenessProbe는 Pod이 살아있는지 확인하는 용도라면 readlinessProbe는 Pod가 생성된 직후, 트래픽을 받을 준비가 완료되었는지 확인하는 프로퍼티이다. 젠킨스 서버와 같이 처음 구동하는데에 오래 걸리는 웹 서비스라면 구동이 완료된 이후에 트래픽을 받아야 한다. 이런 경우 readinessProbe 프로퍼티를 해당 Pod의 초기화가 완료되었다는 것을 쿠버네티스에게 알리는 용도로 사용한다.
# readinessProbe
apiVersion: v1
kind: Pod
metadata:
name: readiness
spec:
containers:
- name: nginx
image: nginx
readinessProbe:
httpGet:
path: /ready
port: 80
httpGet
메서드를 이용하여 해당 컨테이너의 헬스 체크 여부를 확인하게 된다.
readinessProbe 상태는 READY의 값 0/1 표시를 통해 확인할 수 있다.
kubectl apply -f readiness.yaml
kubectl logs -f readiness
2022/06/27 12:14:21 [error] 39#39: *9 open() "/usr/share/nginx/html/ready" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /ready HTTP/1.1", host: "10.1.0.41:80"
10.1.0.1 - - [27/Jun/2022:12:14:21 +0000] "GET /ready HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
2022/06/27 12:14:31 [error] 40#40: *10 open() "/usr/share/nginx/html/ready" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /ready HTTP/1.1", host: "10.1.0.41:80"
10.1.0.1 - - [27/Jun/2022:12:14:31 +0000] "GET /ready HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
로그를 확인해보면 /ready
경로에서 404
에러 코드가 반환되어 정상적으로 준비가 되지 않았음을 확인할 수 있다. 이를 해결하기 위해선 readinessProbe에 대한 헬스 체크를 성공시켜주게 만들면 된다.
kubectl exec readiness -- touch /usr/share/nginx/html/ready
kubectl logs -f readiness
2022/06/27 12:16:01 [error] 50#50: *20 open() "/usr/share/nginx/html/ready" failed (2: No such file or directory), client: 10.1.0.1, server: localhost, request: "GET /ready HTTP/1.1", host: "10.1.0.41:80"
10.1.0.1 - - [27/Jun/2022:12:16:01 +0000] "GET /ready HTTP/1.1" 404 153 "-" "kube-probe/1.24" "-"
10.1.0.1 - - [27/Jun/2022:12:16:11 +0000] "GET /ready HTTP/1.1" 200 0 "-" "kube-probe/1.24" "-"
kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness 1/1 Running 0 3m11s
헬스 체크는 HTTP 프로토콜이 아니라 명령 실행으로도 가능하다.
# readinessProbe
apiVersion: v1
kind: Pod
metadata:
name: readiness
spec:
containers:
- name: nginx
image: nginx
readinessProbe:
exec:
command:
- cat
- /tmp/ready
리눅스의 명령이 정상적으로 종료되면 0을 리턴하고, 그렇지 않으면 다른 숫자를 리턴한다. 이를 이용해서 cat /tmp/ready
명령을 실행했을 때, /tmp/ready
가 존재하면 정상적으로 응답하여 0을 리턴하고, 그렇지 않은 경우 준비가 안됐다고 판단할 수 있게 되는 것이다.
2개 컨테이너 실행
지금까지는 Pod에서 하나의 컨테이너만 실행을 했다. 하지만 Pod은 2개 이상의 컨테이너를 가지고 있을 수 있다.
# second.yaml
apiVersion: v1
kind: Pod
metadata:
name: second
spec:
containers:
- name: nginx
image: nginx
- name: curl
image: curlimages/curl
command: ["/bin/sh"]
args: ["-c", "while true; do sleep 5; curl localhost; done"]
위의 YAML 정의서는 두 개의 컨테이너를 실행하도록 작성됐다. 첫 번째 컨테이너는 지금까지 사용한 간단한 nginx 웹 서버이고, 두 번째 컨테이너는 쉘 스크립트를 실행하는 컨테이너로, 루프를 돌며 5초간 대기 후 localhost를 호출한다.
containers 프로퍼티에는 이렇게 여러 컨테이너를 넣을 수 있다.
# 실행
kubectl apply -f second.yaml
# 로그 확인
kubectl logs -f second
Defaulted container "nginx" out of: nginx, curl
...
127.0.0.1 - - [28/Jun/2022:08:26:32 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.83.1-DEV" "-"
127.0.0.1 - - [28/Jun/2022:08:26:37 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.83.1-DEV" "-"
로그를 확인하는 경우에는 하나의 Pod 내부에 2개의 컨테니어가 존재하기 때문에 디폴트로 설정된 컨테이너의 로그가 출력되는 것을 확인할 수 있다.
명시적으로 특정 컨테이너의 로그를 확인하기 위해선 -c
옵션으로 컨테이너를 지정해주면 된다.
# nginx 컨테이너 지정
kubectl logs second -c nginx
kubectl logs second -c curl
두 번째 컨테이너는 curl 명령을 실행하기 전에 5초 간 슬립을 하고 있다. 쿠버네티스에서 Pod 내부의 컨테이너 실행 순서를 보장해주지 않기 때문이다. nginx 컨테이너가 정상적으로 실행된 뒤에 curl을 호출하기 위해 5초간 슬립 시킨 것이다.
초기화 컨테이너
기본적으로 쿠버네티스에서는 Pod 내의 컨테이너 간 실행순서를 보장해주지 않는다. 그럼에도 불구하고 여러 컨테이너를 사용하고, 특정 컨테이너의 리소스들이 미리 초기화 되어 있을 필요가 있을 수 있다. 이런 경우 initContainers 프로퍼티를 사용하면 먼저 초기화를 시킬 수 있게 된다.
# init-container.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-container
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: k8s.gcr.io/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: {}
초기화 컨테이너를 확인해보기 위해 두 개의 컨테이너를 실행하고자 한다. initContainers 프로퍼티를 사용한 경우, 일반 containers에 정의되어 있는 컨테이너보다 먼저 실행된다.
테스트 과정은 간단하다. 초기화 컨테이너에서 깃허브 레포지토리를 클론하고, 이를 컨테이너 간 공유 공간인 emptyDir에 전달하고 나면, 일반 컨테이너에서 이를 확인해보는 것이다.
# 실행
kubectl apply -f init-container.yaml
# 로그 확인해보기
kubectl logs init-container -c git
kubectl logs init-container -c busybox
Config 설정
쿠버네티스에서 사용하는 설정값들을 따로 모아두고 필요할 때 사용할 수 있다. ConfigMap을 사용하면 이것이 가능한데, ConfigMap에 저장되어 있는 설정값을 불러와 Pod에 전달하는 방법을 살펴볼 것이다.
ConfigMap 리소스 생성
ConfigMap 리소스는 메타데이터를 저장하는 리소스다. YAML 정의서에서 metadata:
라고 작성한 부분이 바로 메타데이터인데, 지금까지는 직접 정의서에 메타데이터를 작성했지만, ConfigMap에 메타데이터를 저장하고 Pod에서 필요할 때 불러와 사용할 수 있다.
# ConfigMap 리소스 생성
kubectl create configmap <KEY> <data-source>
ConfigMap의 데이터 소스를 지정하는 옵션 중 --from-file
옵션을 사용하면 파일로부터 리소스를 불러올 수 있다.
# game.properties
weapon=gun
health=3
potions=5
# ConfigMap 생성
kubectl create configmap game-config --from-file=game.properties
configmap/game-config created
# ConfigMap 조회
kubectl get cm game-config -o yaml
kubectl get configmap game-config -o yaml
조회를 해보면 다음과 같은 결과를 얻을 수 있다.
apiVersion: v1
data:
game.properties: |
# game.properties
weapon=gun
health=3
potions=5
kind: ConfigMap
metadata:
creationTimestamp: "2022-06-28T09:21:13Z"
name: game-config
namespace: default
resourceVersion: "31944"
uid: cb93635d-3d0e-49b2-b041-5ed095653780
game-config라는 ConfigMap에 파일에 작성한 내용물이 들어가 있는 것을 확인할 수 있다. --from-literal
이라는 옵션을 사용하면 명령을 입력하면서 ConfigMap에 저장할 설정값을 직접 입력한다.
kubectl create configmap special-config \
--from-literal=sepcial.power=10 \
--from-literal=special.strength=20
# 확인
kubectl get cm special-config -o yaml
apiVersion: v1
data:
sepcial.power: "10"
special.strength: "20"
kind: ConfigMap
metadata:
creationTimestamp: "2022-06-28T09:25:08Z"
name: special-config
namespace: default
resourceVersion: "32237"
uid: 1e2f1357-9b8c-441a-a580-9ec444102290
마지막으로 직접 YAML 정의서를 작성해서 ConfigMap을 만들 수도 있다.
# monster-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: monster-config
namespace: default
data:
monsterType: fire
monsterNum: "5"
monsterLife: "3"
# 실행
kubectl apply -f monster-config.yaml
# 확인
kubectl get cm monster-config -o yaml
apiVersion: v1
data:
monsterLife: "3"
monsterNum: "5"
monsterType: fire
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"monsterLife":"3","monsterNum":"5","monsterType":"fire"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"monster-config","namespace":"default"}}
creationTimestamp: "2022-06-28T09:31:58Z"
name: monster-config
namespace: default
resourceVersion: "32750"
uid: e7c2296d-f64a-4ee0-a6bd-fa89bfcc1e67
볼륨 연결하기
ConfigMap 리소스를 활용하는 방법으로는 ConfigMap을 볼륨으로 마운트하여 파일처럼 사용하는 방법이 있다.
# game-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: game-volume
spec:
restartPolicy: OnFailure
containers:
- name: gmae-volume
image: k8s.gcr.io/busybox
command: ["/bin/sh", "-c", "cat /etc/config/game.properties"]
volumeMounts:
- name: game-volume
mountPath: /etc/config
volumes:
- name: game-volume
configMap:
name: game-config
- volumes 프로퍼티에서
hostPath
,emptyDir
외에configMap
이라는 볼륨을 사용할 수 있다.
kubectl apply -f game-volume.yaml
kubectl logs game-volume
# game.properties
weapon=gun
health=3
potions=5
로그를 확인하면 game-config 파일에 기록된 내용과 동일한 결과물을 확인할 수 있다.
환경변수 valueFrom
ConfigMap은 Pod의 환경변수로도 사용할 수 있다.
# special-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: special-env
spec:
restartPolicy: OnFailure
containers:
- name: special-env
image: k8s.gcr.io/busybox
command: ["printenv"]
args: ["special_env"]
env:
- name: special_env
valueFrom:
configMapKeyRef:
name: special-config
key: sepcial.power
- configMapKeyRef 프로퍼티는 ConfigMap의 키를 참조하여 그 값을 가져오는 역할을 한다. special-config 라는 ConfigMap의 key인 special.power의 값을 환경변수 sepcial_env로 저장하게 된다.
kubectl apply -f special-env.yaml
kubectl logs special-env
10
환경변수 envFrom
환경변수를 ConfigMap의 일부 데이터가 아니라 모든 설정값을 환경변수로 사용하는 envFrom도 있다.
# monster-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: monster-env
spec:
restartPolicy: OnFailure
containers:
- name: monster-env
image: k8s.gcr.io/busybox
command: ["printenv"]
envFrom:
- configMapRef:
name: monster-config
- env 대신 envFrom을 써서 ConfigMap의 모든 설정값을 환경변수로 사용한다.
kubectl apply -f monseter-env.yaml
kubectl logs monster-env
...
HOSTNAME=monster-env
monsterType=fire
monsterLife=3
monsterNum=5
...
민감 데이터 관리
ConfigMap과 유사하지만 민감한 데이터를 저장하는데 사용하는 리소스가 바로 Secret이다. Secret 리소스는 각 노드에서 사용될 때 디스크에 저장되지 않고, tmpfs 라는 메모리 기반 파일시스템을 사용해서 보안에 더욱 강하다.
또한, Secret 리소스를 사용자가 조회할 때 base64로 한 번 인코딩되어 표시되는 특징을 가지고 있다.
Secret 리소스 생성
먼저 아이디와 패스워드를 base64로 인코딩한다
echo -ne admin | base64
YWRtaW4=
echo -ne password123 | base64
cGFzc3dvcmQxMjM=
이 값을 이용해서 Secret 리소스를 만든다.
# user-info.yaml
apiVersion: v1
kind: Secret
metadata:
name: user-info
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQxMjM=
- type 프로퍼티로 Secret 리소스의 타입을 설정한다. 기본적으로 Opaque를 사용하지만, 경우에 따라 타입을 사용한다.
- data 프로퍼티에는 Secret 리소스에 저장하고 싶은 민감 데이터를 입력한다.
kubectl apply -f user-info.yaml
kubectl get secret user-info -o yaml
apiVersion: v1
data:
password: cGFzc3dvcmQxMjM=
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"cGFzc3dvcmQxMjM=","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"name":"user-info","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2022-06-28T10:35:58Z"
name: user-info
namespace: default
resourceVersion: "37607"
uid: 7a02d6ad-6af6-41dc-8f48-2119123086a9
type: Opaque
data 프로퍼티의 값을 직접 base64로 인코딩하지 않고 쿠버네티스가 대신 처리해주길 원한다면 stringData 프로퍼티를 사용하면 된다.
# user-info-stringdata.yaml
apiVersion: v1
kind: Secret
metadata:
name: user-info-stringdata
type: Opaque
stringData:
username: admin
password: password123
stringData 프로퍼티를 사용하면 자동으로 base64 인코딩 처리를 진행하게 된다.
Secret 역시 명령을 이용하여 생성할 수 있다.
# user-info-properties
username=admin
password=password123
kubectl create secret generic user-info-from-file \
--from-env-file=user-info.properties
--from-env-file
, --from-file
, --from-literal
등의 옵션을 사용해서 Secret을 생성할 수 있다.
--from-env-file
은 해당 파일에서- 형식으로 이루어진 값만 data 프로퍼티에 추가하는 방식이다. --from-file
을 사용하면- 형식이 아닌 파일의 이름도 함께 data 프로퍼티에 추가된다.
Secret 활용
볼륨 연결
Secret은 ConfigMap과 매우 비슷하게 사용이 가능하다.
# secret-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-volume
spec:
restartPolicy: OnFailure
containers:
- name: secret-volume
image: k8s.gcr.io/busybox
command: ["sh"]
args: ["-c", "ls /secret; cat /secret/username"]
volumeMounts:
- name: secret
mountPath: "/secret"
volumes:
- name: secret
secret:
secretName: user-info
ConfigMap을 볼륨으로 사용한 것처럼 Secret도 동일하게 사용이 가능하다.
kubectl apply -f secret-volume.yaml
kubectl logs secret-volume
환경변수 - env
Secret 리소스를 환경변수로 사용할 수 있다. ConfigMap에서 사용한 방식과 매우 유사하다.
# secret-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-env
spec:
restartPolicy: OnFailure
containers:
- name: secret-env
image: k8s.gcr.io/busybox
command: ["printenv"]
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: user-info
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: user-info
key: password
ConfigMap에 저장되어 있는 key - value 데이터를 환경변수로 사용할 때는 configMapKeyRef
를, Secret에 저장되어 있는 key- value 데이터를 환경변수로 사용할 때는 secretKeyRef
를 사용하면 된다.
kubectl apply -f secret-env.yaml
kubectl logs secret-env
환경변수 - envFrom
Secret에 저장된 데이터 전부를 환경변수로 불러오는 방법이다. 이 역시 ConfigMap과 유사하다.
# secret-envfrom.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-envfrom
spec:
restartPolicy: OnFailure
containers:
- name: secret-envfrom
image: k8s.gcr.io/busybox
command: ["printenv"]
envFrom:
- secretRef:
name: user-info
kubectl apply -f secret-envfrom.yaml
kubectl logs secret-envfrom
메타데이터 전달
쿠버네티스는 Pod의 메타데이터를 컨테이너에게 전달할 수 있다. 이를 Downward API라 부른다. 실행되는 Pod의 정보를 컨테이너에 노출하고 싶을 때 사용한다. ConfigMap, Secret과 마찬가지로 환경변수로 사용하거나 볼륨 연결을 통해 컨테이너에 정보를 전달할 수 있다.
볼륨 연결
# downward-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: downward-volume
labels:
zone: ap-north-east
cluster: cluseter1
spec:
restartPolicy: OnFailure
containers:
- name: downward
image: k8s.gcr.io/busybox
command: ["sh", "-c"]
args: ["cat /etc/podinfo/labels"]
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- downwardAPI 프로퍼티를 통해 downwardAPI 볼륨 사용을 선언한다.
- items는 메타데이터로 사용할 아이템 리스트를 지정한다.
- path는 볼륨과 연결될 컨테이너의 내부 패스를 지정한다.
- fieldRef는 참조할 필드를 선언하는 의미다.
- fieldPath는 Pod의 메타데이터 필드를 지정한다.
kubectl apply -f downward-volume
kubectl logs downward-volume
downward로 볼륨을 만들면 컨테이너 내부에서 기본적으로 /etc/podinfo
라는 경로를 가지고 있는데, 해당 경로를 mountPath
의 값으로 사용하여 볼륨을 연결하고 있다.
/etc/podinfo
가 볼륨의 기본 경로가 되기 때문에 path
에 labels
라고 작성하게 되면 /etc/podinfo/labels
이라는 새로운 파일이 만들어지고, 해당 파일에 fieldPath
에서 지정한 YAML의 필드값이 기록되는 것이다. path
를 ./metadata/labels
라고 작성하면 /etc/podinfo/metadata/labels
에 YAML의 라벨값이 기록된다.
환경변수 - env
# downward-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: downward-env
spec:
restartPolicy: OnFailure
containers:
- name: downward
image: k8s.gcr.io/busybox
command: ["printenv"]
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
kubectl apply -f downward-env.yaml
kubectl logs downward-env
'쿠버네티스' 카테고리의 다른 글
쿠버네티스 네트워킹(Service 리소스) (0) | 2022.07.13 |
---|---|
쿠버네티스 기본 명령어 (0) | 2022.06.26 |
쿠버네티스 설치 (0) | 2022.06.24 |
쿠버네티스 소개 (0) | 2022.06.07 |
도커 기초 (0) | 2022.05.31 |