파드는 함께 배치된 컨테이너 그룹이며 쿠버네티스의 기본 빌딩 블록이다.
컨테이너를 개별적으로 배포하기보다는 컨테이너를 가진 파드를 배포하고 운영한다.
일반적으로 파드는 하나의 컨테이너만 포함되지만, 파드의 핵심 사항은 파드가 여러 컨테이너를 가지고 있을 경우에 모든 컨테이너는 항상 하나의 워커노드에서 실행되며
여러 워커 노드에 걸쳐 실행되지 않는다.
컨테이너 모음을 사용해 밀접하게 연관된 프로세스를 함께 실행하고 단일 컨테이너 안에서 모두 함께 실행되는 것처럼 동일한 환경을 제공할 수 있으면서도 이들을 격리된 상태로 유지할 수 있다.
컨테이너가 제공하는 모든 기능을 활용하는 동시에 프로세스가 함께 실행되는 것처럼 보이게 할 수 있다.
컨테이너를 파드로 묶어 그룹으로 만들 때는, 즉 두개의 컨테이너를 단일 파드로 넣을지 아니면 두 개의 별도 파드에 넣을지 결정하기 위해
다음과 같은 질문을 할 수 있다.
기본적으로 파드는 특정한 이유 때문에 컨테이너를 단일 파드로 구성하는 것을 요구하지 않는다면,
분리된 파드에서 컨테이너를 실행하는게 좋다.
대표적으로 프론트엔드파드와 백엔드파드를 분리한다고 생각하면 되겠다.
파드를 포함한 다른 쿠버네티스 리소스는 일반적으로 쿠버네티스 REST API 엔드포인트에 JSON 혹은 YAML 매니페스트를 전송해 생성한다.
kubectl run 명령처럼 다른 간단한 방법으로 리소스를 만들 수 있지만, YAML 파일에 모든 쿠버네티스 오브젝트를 정의하면 버전 관리 시스템에 넣는 것이 가능해져 그에 따른 모든 이점을 누릴 수 있다.
이전 챕터에 레플리케이션컨트롤러로 생성한 파드가 있으므로, 이 중 하나에 관한 YAML 정의가 어떻게 되는지 살펴보자
$ kubectl get po kubia-b5fxm -o yaml
apiVersion: v1 # 이 YAML 디스크립트에서 사용한 쿠버네티스 api 버전
kind: Pod # 쿠버네티스 오브젝트/리소스 유형
metadata: # 파드 메타데이터(이름, 레이블, 어노테이션등)
creationTimestamp: "2022-09-04T07:00:46Z"
generateName: kubia-
labels:
run: kubia
name: kubia-b5fxm
namespace: default
ownerReferences:
- apiVersion: v1
blockOwnerDeletion: true
controller: true
kind: ReplicationController
name: kubia
uid: 5b90478b-3d84-4c4e-b4a5-7ffae34f67b3
resourceVersion: "33994"
uid: 2681779d-2cbc-4483-9081-4d3d64b37cdb
spec: # 파드 정의/내용(파드 컨테이너 목록, 볼륭 등)
containers:
- image: luksa/kubia:latest
imagePullPolicy: Always
name: kubia
ports:
- containerPort: 8080
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-mh4bx
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: gke-kubia-default-pool-c2f41b01-wgt5
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-mh4bx
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status: # 파드와 그 안의 여러 컨테이너의 상세한 상태
conditions:
- lastProbeTime: null
lastTransitionTime: "2022-09-04T07:00:46Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2022-09-04T07:01:07Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2022-09-04T07:01:07Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2022-09-04T07:00:46Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://9bac49f5c88dbfabfe11953da2b24747528e38a1139504198d201bf8de4f7218
image: docker.io/luksa/kubia:latest
imageID: docker.io/luksa/kubia@sha256:3f28e304dc0f63dc30f273a4202096f0fa0d08510bd2ee7e1032ce600616de24
lastState: {}
name: kubia
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2022-09-04T07:01:06Z"
hostIP: 10.138.0.2
phase: Running
podIP: 10.32.0.7
podIPs:
- ip: 10.32.0.7
qosClass: BestEffort
startTime: "2022-09-04T07:00:46Z"
먼저 YAML에서 사용하는 쿠버네티스 api 버전과 yaml이 설명하는 리소스 유형이 있다.
이어서 거의 모든 쿠버네티스 리소스가 갖고 있는 세 가지 중요한 부분이 있다.
kubia-manual.yaml 파일을 만들어보자
kubia-manual.yaml
apiVersion: v1 # 디스크립터는 쿠버네티스 API 버전 v1을 준수함
kind: Pod # 오브젝트 종류가 파드임
metadata:
name: kubia-manual # 파드 이름
spec:
containers:
- image: luksa/kubia # 컨테이너를 만드는 컨테이너 이미지
name: kubia # 컨테이너 이름
ports:
- containerPort: 8080 # 애플리케이션이 수신하는 포트
protocol: TCP
kubectl create 명령을 이용하여 파드를 생성한다
$ kubectl create -f kubia-manual.yaml
-f 명령은 YAML 또는 JSON 파일로(파드뿐만아니라) 리소스를 만드는 데 사용한다.
JSON으로 보기 원한다면, YAML 대신에 JSON을 반환하도록 할 수 있다
$ kubectl get po kubia-manual -o json
ssh로 파드가 실행 중인 노드에 접속해 docker logs [container_id] 명령으로 로그를 가져올 수도 있지만, 쿠버네티스는 더 쉬운 방법을 사용한다
$ kubectl logs kubia-manual
Kubia server starting...
컨테이너 이름을 지정해 다중 컨테이너 파드에서 로그를 가져오는 방법은 -c [container_name] 옵션과 함께 명시적으로 포함하면 된다.
$ kubectl logs kubia-manual -c kubia
Kubia server starting...
이전 챕터에 expose 명령으로 외부에서 파드에 접속할 수 있는 서비스를 만들었다
다음 챕터에 서비스를 집중적으로 다룰 예정이라 여기서는 파드에 테스트와 디버깅 목적으로 연결할 수 있는 방법중 하나로 포트 포워딩을 해보자.
서비스를 거치지 않고 특정 파드와 대화하고 싶을 때 쿠버네티스는 해당 파드로 향하는 포트 포워딩을 구성해준다.
kubectl port-forward 명령으로 할 수 있다. 다음 명령은 머신의 로컬 포트 8888을 kubia-manual 파드의 8080포트로 향하게 한다.
$ kubectl port-forward kubia-manual 8888:8080
Forwarding from 127.0.0.1:8888 -> 8080
이제 다른 터미널에서 curl을 이용해 localhost:8888에서 실행되고 있는 kubectl port-forward 프록시를 통해 HTTP 요청을 해당 파드에 보낼 수 있다.
$ curl localhost:8888
모든 개발자와 시스템 관리자는 어떤 파드가 어떤 것인지 쉽게 알 수 있도록 임의의 기준에 따라 작은 그룹으로 조직하는 방법이 필요하다.
각 파드에 대해 개별적으로 작업을 수행하기보다 특정 그룹에 속한 모든 파드에 관해 한 번에 작업하기를 원할 것이다
레이블을 통해 파드와 기타 다른 쿠버네티스 오브젝트의 조직화가 이뤄진다.
레이블은 리소스에 첨부하는 키-값 쌍으로, 이 쌍은 레이블 셀렉터를 사용해 리소스를 선택할 때 활용된다.
레이블 키가 해당 리소스 내에서 고유하다면, 하나 이상 원하는 만큼 레이블을 가질 수 있다.
일반적으로 리소스를 생성할 때 레이블을 붙이지만, 나중에 레이블을 추가하거나 기존 레이블 값을 수정할 수도 있다.
레이블 두 개를 가진 파드를 생성해 실제로 레이블이 어떻게 동작하는지 살펴보자
kubia-manual-with-labels.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual-v2
labels: # 레이블 두 개를 파드에 붙였다.
creation_method: manual
env: prod
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
레이블 creation_method=manual과 env=pod를 metadata.labels 섹션에 포함했다.
이제 파드를 생성한다.
$ kubectl create -f kubia-manual-with-labels.yaml
--show-labels 스위치를 사용해 레이블을 볼 수 있다.
$ kubectl get po --show-labels
NAME READY STATUS RESTARTS AGE LABELS
kubia-b5fxm 1/1 Running 0 29h run=kubia
kubia-lzbwf 1/1 Running 0 30h run=kubia
kubia-manual 1/1 Running 0 13m <none>
kubia-manual-v2 1/1 Running 0 13s creation_method=manual,env=prod
kubia-p8vdx 1/1 Running 0 29h run=kubia
특정 레이블에만 관심이 있을 경우 -L 스위치를 지정해 각 레이블을 자체 열에 표시할 수 있다.
$ kubectl get po -L creation_method,env
NAME READY STATUS RESTARTS AGE CREATION_METHOD ENV
kubia-b5fxm 1/1 Running 0 29h
kubia-lzbwf 1/1 Running 0 30h
kubia-manual 1/1 Running 0 14m
kubia-manual-v2 1/1 Running 0 47s manual prod
kubia-p8vdx 1/1 Running 0 29h
이전에 kubia-manual 파드를 수정으로 생성했으니, creation_method=manual 레이블을 추가해보자
$ kubectl label po kubia-manual creation_method=manual
pod/kubia-manual labeled
기존의 가지고 있던 레이블을 어떻게 변경하는지 보기 위해 kubia-manual-v2 파드에 env=pod 레이블을 env=debug 레이블로 변경한다.
레이블 변경은 --overwrite 옵션이 필요하다
$ kubectl label po kubia-manual-v2 env=debug --overwrite
pod/kubia-manual-v2 labeled
갱신된 레이블을 다시 확인해보자
$ kubectl get po -L creation_method,env
NAME READY STATUS RESTARTS AGE CREATION_METHOD ENV
kubia-b5fxm 1/1 Running 0 29h
kubia-lzbwf 1/1 Running 0 30h
kubia-manual 1/1 Running 0 15m manual
kubia-manual-v2 1/1 Running 0 2m6s manual debug
kubia-p8vdx 1/1 Running 0 29h
레이블 셀렉터는 특정 레이블로 태그된 파드의 부분 집합을 선택해 원하는 작업을 수행한다. 특정 값과 레이블을 갖는지 여부에 따라 리소스를 필터링하는 기준이 된다.
만들어둔 파드에 레이블 셀렉터를 사용해보자. 수동으로 생성한 모든 파드를 보려면 다음 명령을 수행한다.
$ kubectl get po -l creation_method=manual
NAME READY STATUS RESTARTS AGE
kubia-manual 1/1 Running 0 16m
kubia-manual-v2 1/1 Running 0 2m39s
env 레이블을 가지고 있지만, 값은 무엇이든 상관없는 파드를 보려면 다음 명령을 수행한다.
$ kubectl get po -l env
NAME READY STATUS RESTARTS AGE
kubia-manual-v2 1/1 Running 0 2m53s
env 레이브를 가지고 있지 않는 파드는 다음과 같다.
$ kubectl get po -l '!env'
NAME READY STATUS RESTARTS AGE
kubia-b5fxm 1/1 Running 0 29h
kubia-lzbwf 1/1 Running 0 30h
kubia-manual 1/1 Running 0 16m
kubia-p8vdx 1/1 Running 0 29h
레이블 셀렉터는 파드 목록을 나열하는 것뿐만 아니라, 파드 부분 집합에 작업을 수행 할 때도 유용하다.
또한 셀렉터는 kubectl에서만 사용하는 것이 아니고 내부적으로도 사용된다.
파드를 스케줄링할 위치를 결정할 때 약간이라도 영향을 미치고 싶은 상황이있다.
예를 들어 하드웨어 인프라가 동일하지 않은 경우를 들 수 있다. 워커 노드 일부는 HDD를 가지고 있고 나머지는 SSD를 가지고 있는 경우
특정 파드를 한 그룹에 나머지 파드는 다른 그룹에 스케줄링되도록 할 수 있다.
또 다른 예는 GPU 가속을 제공하는 노드에만 GPU 계산이 필요한 파드를 스케줄링하는 것을 들 수 있다.
클러스터에 범용 GPU 컴퓨팅에 사용할 수 있는 GPU를 가지고 있는 노드가 있다고 가정하자, 이 기능을 가지고 있음을 보여주는 레이블 노드에 추가하려고 한다.
gpu=true 레이블을 노드 중 하나에 추가하자
$ kubectl label node gke-kubia-default-pool-c2f41b01-37p5 gpu=true
이제 파드를 나열할 때 처럼 노드를 나열할 때 레이블 셀렉터를 사용할 수 있다.
노드중에 gpu=true 레이블을 가진 노드를 나열하자
$ kubectl get nodes -l gpu=true
NAME STATUS ROLES AGE VERSION
gke-kubia-default-pool-c2f41b01-37p5 Ready <none> 31h v1.22.11-gke.400
이제 GPU를 필요로 하는 새로운 파드를 배포해야 한다고 가정해보자.
스케줄러가 GPU를 제공하는 노드를 선택하도록 요청하려면, 파드의 YAML 파일에 노드 셀렉터를 추가해야한다.
kubia-gpu.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
nodeSelector: # nodeSelector는 쿠버네티스에 gpu=true 레이블을 포함한 노드에 이 파드를 배포하도록 지시한다.
gpu: "true"
containers:
- image: luksa/kubia
name: kubia
nodeSelector 필드를 추가하면 파드를 생성할 때 스케쥴러는 gpu=true 레이블을 가지고 있는 노드 중에서 선택하게된다.
앞에서 본 예제는 레이블과 레이블 셀렉터가 동작하는 방식과 쿠버네티스 운영에 어떻게 영향을 주는지 볼 수 있는 간단한 데모였다.
레이블 셀렉터의 중요성과 유용함은 다음 챕터의 레플리케이션컨트롤러와 서비스를 이야기할 때 더욱 분명해질 것이다.
레이블을 이용해 파드와 다른 오브젝트를 그룹으로 묶는 것을 봤다.
각 오브젝트는 여러 레이블을 가질 수 있기 때문에 오브젝트 그룹은 서로 겹쳐질 수 있다.
또한 클러스터에서 작업을 수행할 때 레이블 셀렉터를 명시적으로 지정하지 않으면 항상 모든 오브젝트를 보게 된다.
오브젝트를 겹치지않는 그룹으로 분할하고자 할 때, 쿠버네티스는 오브젝트를 네임스페이스로 그룹화한다. 쿠버네티스 네임스페이스는 오브젝트 이름의 범위를 제공한다.
모든 리소스를 하나의 단일 네임스페이스에 두는 대신에 여러 네임스페이스로 분할할 수 있으며, 이렇게 분리된 네임스페이스는 같은 리소스 이름을 다른 네임스페이스에 걸쳐 여러 번 사용할 수 있게 해준다.
ns 명령으로 모든 네임스페이스를 나열한다.
$ kubectl get ns
NAME STATUS AGE
default Active 31h
kube-node-lease Active 31h
kube-public Active 31h
kube-system Active 31h
지금까지는 default 네임스페이스에서만 작업을 진행했다. 네임스페이스를 명시적으로 지정한 적이 없기 때문에 kubectl 명령어는 항상 기본적으로 default 네임스페이스에 속해 있는 오브젝트만 표시했다
그러나 목록을 보면 kube-public과 kube-system 네임스페이스도 존재하는 것을 알 수 있다.
kube-system 네임스페이스에 속한 파드를 한번 조회해 보자.
$ kubectl get po --namespace kube-system
NAME READY STATUS RESTARTS AGE
event-exporter-gke-5479fd58c8-8lckp 2/2 Running 0 31h
fluentbit-gke-hdbqp 2/2 Running 0 31h
fluentbit-gke-jlb9w 2/2 Running 0 31h
fluentbit-gke-r5r65 2/2 Running 0 31h
gke-metrics-agent-2zzwp 1/1 Running 0 31h
gke-metrics-agent-g5zhn 1/1 Running 0 31h
gke-metrics-agent-rt78t 1/1 Running 0 31h
konnectivity-agent-859b9dc845-k2sjc 1/1 Running 0 31h
konnectivity-agent-859b9dc845-px5sk 1/1 Running 0 31h
konnectivity-agent-859b9dc845-wgrm8 1/1 Running 0 31h
konnectivity-agent-autoscaler-555f599d94-4k7jj 1/1 Running 0 31h
kube-dns-85df8994db-7hcbm 4/4 Running 0 31h
kube-dns-85df8994db-nnf9d 4/4 Running 0 31h
kube-dns-autoscaler-f4d55555-6sbd4 1/1 Running 0 31h
kube-proxy-gke-kubia-default-pool-c2f41b01-37p5 1/1 Running 0 31h
kube-proxy-gke-kubia-default-pool-c2f41b01-3gd0 1/1 Running 0 31h
kube-proxy-gke-kubia-default-pool-c2f41b01-wgt5 1/1 Running 0 31h
l7-default-backend-69fb9fd9f9-68wcr 1/1 Running 0 31h
metrics-server-v0.4.5-fb4c49dd6-ctqbm 2/2 Running 0 31h
pdcsi-node-dvchv 2/2 Running 0 31h
pdcsi-node-qrx7q 2/2 Running 0 31h
pdcsi-node-xj92s 2/2 Running 0 31h
네임스페이스 이름으로 유추해보면 쿠버네티스 시스템 자체와 관련된 리소스임을 알 수있다.
여기서 나열된 파드는 추후에 알아보도록 하겠다.
다음 예제로 custom-namespace.yaml 파일을 생성한다.
custom-namespace.yaml
apiVersion: v1
kind: Namespace # 네임스페이스를 정의한다.
metadata:
name: custom-namespace # 네임스페이스의 이름
네임스페이스를 생성하기 위해 YAML 매니페스트를 만들었다
이렇게 YAML 매니페스트를 API 서버에 전송해 생성, 읽기, 갱신, 삭제등을 할 수 있지만 다른 방법으로 네임스페이스를 생성할 수 있다.
$ kubectl create namespace custom-namespace
namespace/custom-namespace created
생성한 네임스페이스 안에 리소스를 만들기 위해 metadata 섹션에 namespace: custom-namespace 항목을 넣거나 kubectl create 명령을 사용할 때 네임스페이스를 지정한다.
$ kubectl create -f kubia-manual.yaml -n custom-namespace
이제 동일한 이름을 가진 두 파드가 있다. 하나는 default 네임스페이스에 있고 나머지는 custom-namespace에 있다.
네임스페이스를 사용하면 오브젝트를 별도 그룹으로 분리해 특정한 네임스페이스 안에 속한 리소스를 대상으로 작업할 수 있게 해주지만, 실행 중인 오브젝트에 대한 격리는 제공하지 않는다.
예를 들어 다른 사용자들이 서로 다른 네임스페이스에 파드를 배포할 때 해당 파드가 서로 격리돼 통신할 수 없다고 생각할 수 있지만, 반드시 그런 것은 아니다.
네임스페이스에서 네트워크 격리를 제공하는지 쿠버네티스와 함께 배포하는 네트워킹 솔루션에 따라 다르다.
현재 default 네임스페이스에 네 개의 파드가 있고, custom-namespace 안에 하나의 파드가 있다.
더 이상 필요하지 않기 때문에 모두 중지해본다.
파드이름으로 kubia-gpu 삭제
$ kubectl delete po kubia-gpu
pod "kubia-gpu" deleted
레이블 셀렉터를 이용한 파드 삭제로
creation_method=manual 레이블을 사지고 있는 파드를 삭제 해보자
$ kubectl delete po -l creation_method=manual
pod "kubia-manual-v2" deleted
네임스페이스를 삭제한 파드 제거
custom-namespace안에 있는 파드는 더 이상 해당 네임스페이스 안에 있는 파드나 네임스페이스 자체가 필요하지 않다.
다음 명령을 사용해 네임스페이스 전체를 삭제 한다(파드는 네임스페이스와 함께 자동으로 삭제된다.)
$ kubectl delete ns custom-namespace
namespace "custom-namespace" deleted
특정 파드를 삭제하는 대신 --all 옵션을 이용해 쿠버네티스가 현재 네임스페이스에 있는 모든 파드를 삭제 할 수 있다.
$ kubectl delete po --all
pod "kubia-b5fxm" deleted
pod "kubia-lzbwf" deleted
pod "kubia-manual" deleted
pod "kubia-p8vdx" deleted
레플리케이션컨트롤러로 만든 파드 경우, 계속해서 즉시 새로운 파드를 생성한다. 파드를 삭제하기 위해서는 레플리케이션컨트롤러도 삭제해야 한다.
다음 명령으로 현재 네임스페이스에 있는 모든 리소스를 삭제할 수 있다(레플리케이션컨트롤러, 파드, 생성한 모든 서비스)
$ kubectl delete all --all
pod "kubia-4n9ft" deleted
pod "kubia-b5fxm" deleted
pod "kubia-j96lb" deleted
pod "kubia-lzbwf" deleted
pod "kubia-manual" deleted
pod "kubia-p8vdx" deleted
pod "kubia-z4r85" deleted
replicationcontroller "kubia" deleted
service "kubernetes" deleted
service "kubia-http" deleted