컨트롤러는 Desired State (사용자가 원하는 상태) 와 현재 상태가 일치하도록 Kubernetes 의 오브젝트를 생성, 수정, 삭제 및 실행 등의 제어를 하는, 말 그대로 컨트롤러의 역할을 하는 리소스이다.
ReplicaSet 은 Pod 가 항상 안정적으로 명시된 갯수 만큼 실행되는 것 (= 가용성) 을 보장하기 위해 사용된다.
ReplicaSet 을 생성하기 위해서는 대상 Pod 를 식별할 수 있는 selector, 유지해야 하는 Pod 의 갯수, 생성 될 Pod 의 정보를 담은 템플릿 등을 명시해야 한다.
상술하였듯 ReplicaSet 은 selector 를 통해 Pod 를 식별하기에, 해당 selector 에 선택될 수 있는 Pod 가 생성된다면 ReplicaSet 이 해당 Pod 를 관리하려 할 수 있어 주의가 필요하다.
ReplicaSet 은 자원의 업데이트를 선언적으로 설정 해 둘 수 없기 때문에 사용자 지정 오케스트레이션이 필요하거나 업데이트가 전혀 필요 없는 Pod 를 배포 할 때 등에 사용되고, 그 이외에는 주로 Deployment 가 사용된다.
## ReplicaSet 생성 예제
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 유지해야 하는 Pod 의 갯수
replicas: 3
# 대상 Pod 를 식별할 수 있는 selector
selector:
matchLabels:
tier: frontend
# 생성 될 Pod 의 정보를 담은 템플릿
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
Deployment 는 ReplicaSet 과 비슷하나 ReplicaSet 의 상위 개념으로 하위에 ReplicaSet 을 생성하는 방식으로 동작하며, 선언적 업데이트, 특히 Rolling-Update 를 지원하여 새 버전을 배포하기 용이하다. 또한 문제가 발생하면 이전 버전으로 Rollback 하기에도 좋아 배포시 가장 일반적으로 쓰이는 리소스이다.
## Deployment 생성 예제
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
# 유지해야 하는 Pod 의 갯수
replicas: 3
# 대상 Pod 를 식별할 수 있는 selector
selector:
matchLabels:
app: nginx
# 생성 될 Pod 의 정보를 담은 템플릿
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
## nginx 이미지의 버전이 1.14.2
image: nginx:1.14.2
ports:
- containerPort: 80
위 내용을 deployment.yaml 로 저장한 후 아래 명령어로 생성한다
kubectl apply -f deployment.yaml
# deployment.apps/nginx-deployment created
생성 상태는 아래 명령어로 확인 가능하다.
# Deployment 상태
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# nginx-deployment 2/3 3 2 16s
# Deployment Rollout 상태
kubectl rollout status deployment/nginx-deployment
# Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
# deployment "nginx-deployment" successfully rolled out
이 때, ReplicaSet 및 Pod 를 조회해보면 알아서 생성된 것을 확인 할 수 있다.
kubectl get rs
# NAME DESIRED CURRENT READY AGE
# nginx-deployment-66b6c48dd5 3 3 3 95s
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# nginx-deployment-66b6c48dd5-bwbwv 1/1 Running 0 4m15s
# nginx-deployment-66b6c48dd5-ksfng 1/1 Running 0 4m15s
# nginx-deployment-66b6c48dd5-wbqhh 1/1 Running 0 4m15s
위 예제에서 Image 를 nginx 의 1.14.2 버전으로 지정하였다. 만약 이미지의 버전을 업데이트 하고 싶다면 다음 명령어로 쉽게 변경할 수 있다. (Rolling Update)
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record
# deployment.apps/nginx-deployment image updated
또는 해당 Deployment 의 정의를 직접 수정하여 변경할 수도 있다.
kubectl edit deployment.v1.apps/nginx-deployment
# ~ Deployment 수정 화면이 표시 ~
# deployment.apps/nginx-deployment edited
진행 상태를 조회하려면 아래와 같이 확인한다.
kubectl rollout status deployment/nginx-deployment
# 진행중일 경우
# Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
# Waiting for rollout to finish: 1 old replicas are pending termination...
# 완료 되었을 경우
# deployment "nginx-deployment" successfully rolled out
이 때 ReplicaSet 을 조회해보면 업데이트 전에 쓰던 ReplicaSet 과 새로 생긴 ReplicaSet 이 남아있어, Deployment 가 알아서 Pod를 순차적으로 대체했음을 알 수 있으며, 이 덕분에 문제가 생길시 쉽게 Rollback 할 수 있다.
kubectl get rs
# NAME DESIRED CURRENT READY AGE
# nginx-deployment-559d658b74 3 3 3 46s
# nginx-deployment-66b6c48dd5 0 0 0 6m50s
Pod 를 조회해보면 새로 생성된 3개의 Pod 만 표시된다.
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# nginx-deployment-559d658b74-mgb6c 1/1 Running 0 2m8s
# nginx-deployment-559d658b74-n6wzn 1/1 Running 0 2m7s
# nginx-deployment-559d658b74-xf65n 1/1 Running 0 2m20s
이미지에 문제가 생겼거나 수정 중 오타로 정상적인 업데이트가 이루어지지 않는 등 문제가 발생할 경우 이미지를 롤백하고 싶을 수 있을 것이다. 이때 이미지를 롤백하려면 아래 명령어를 입력하면 된다.
# 바로 이전 버전으로 롤백하고 싶을 때
kubectl rollout undo deployment.v1.apps/nginx-deployment
# deployment.apps/nginx-deployment rolled back
# ---
# 특정 버전으로 롤백하고 싶을 때
# 먼저 Deployment 의 수정사항을 확인하여 롤백할 대상 버전을 찾는다
kubectl rollout history deployment.v1.apps/nginx-deployment
# deployments "nginx-deployment"
# REVISION CHANGE-CAUSE
# 1 kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml --record=true
# 2 kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 --record=true
# 3 kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.161 --record=true
# 수정사항에 대한 세부 정보를 확인한다
kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
# deployments "nginx-deployment" revision 2
# Labels: app=nginx
# pod-template-hash=1159050644
# 이하생략
# 특정 버전으로 롤백한다
kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
# deployment.apps/nginx-deployment rolled back
현재 운영중인 상태에서 별다른 수정 없이 Replica 수만 늘려 스케일링 하고 싶다면 아래 명령어를 입력한다
kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
# deployment.apps/nginx-deployment scaled
아래 명령어로 Deployment 의 동작 로그나 상세 정보를 확인할 수 있다
kubectl describe deployments/nginx-deployment
# Name: nginx-deployment
# Namespace: default
# CreationTimestamp: Mon, 26 Jul 2021 17:22:32 +0900
# Labels: app=nginx
# Annotations: deployment.kubernetes.io/revision: 2
# kubernetes.io/change-cause: kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record=true
# 이하생략
Deployment 는 수시로 재시작 되거나 Pod 내에 저장된 데이터가 삭제되어도 문제가 없는 Stateless 워크로드에 적합하다. 하지만 DB와 같이 디스크에 데이터가 유지되어야 하는 등의 Stateful 워크로드 에서는 사용하기 어렵다.
PersistantVolume 을 붙이는 방법도 있지만 한 볼륨을 공유해 쓸 수 없으며 매 Pod 마다 쓰기/읽기가 가능한 볼륨을 추가하는 것은 쉽지 않은 문제이다.
StatefulSet 은 이러한 불편함을 보완하기 위해 만들어졌다.
고유성 보장
랜덤한 이름이 아닌 {Pod 템플릿 이름}-{순번} 식의 고유한 이름을 갖게 되며 생성된 Pod의 순번은 StatefulSet 동작에서 고유한 ID 역할을 한다.
순서 보장
Pod가 생성, 시작될 때 동시에 모든 노드가 생성되지 않고 0, 1, 2... 순서대로 하나씩 처리된다. 마스터 노드 시작 후 슬레이브 노드가 시작 되어야 하는 경우 (DB Master - Slave Replication) 등에 유용하다.
이는 별도의 설정 (podManagementPolicy: Parallel) 으로 동시에 처리되도록 변경할 수 있다.
Pod 마다 고유한 볼륨 생성 가능
설정한 볼륨 정보 (PVC)에 따라 Pod 에 고유한 볼륨을 생성시킬 수 있으며, 문제가 생겨 Pod 가 재시작 되거나 줄어들 경우 (Scale-In) 디스크를 삭제하지 않으며, 다시 Pod가 생성되면 순번에 맞추어 존재하는 PV가 연결 된다.
아래는 디스크 정보를 넣은 StatefulSet 의 예제이다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
# 대상 Pod 를 식별할 수 있는 selector
selector:
matchLabels:
app: nginx
serviceName: "nginx"
# 유지해야 하는 Pod 의 갯수
replicas: 3
# 생성 될 Pod 의 정보를 담은 템플릿
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
# 볼륨을 디렉토리에 매핑
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
# PVC를 입력하여 필요한 디스크를 할당받음
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
만약 Pod 를 업데이트 한다면, 기본적으로 OnDelete 전략으로 동작하여 바로 업데이트 되지 않고 Pod를 수동으로 삭제하여야 업데이트가 적용된 Pod가 생성된다. 이외에도 Deployment 와 같은 Rolling-Update, Partition 등의 업데이트 전략을 사용할 수 있다.
선택된 Worker Node 에 Pod를 실행하도록 설정하는 리소스이다. 신규 Worker Node 가 추가되어도 조건에 만족한다면 자동으로 Pod 가 배포되며 Worker Node 가 클러스터에서 제거되면 해당 Pod 는 Garbage 처리 된다.
데몬셋의 일부 대표적인 용도는 다음과 같다.
아래 YAML 은 fluentd-elasticsearch 이미지를 배포하는 설정을 담고 있다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
# 셀렉터
selector:
matchLabels:
name: fluentd-elasticsearch
# Pod 템플릿
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 마스터 노드가 Pod 를 실행할 수 없도록 설정되어 있어도
# 이 DaemonSet 은 실행이 될 수 있도록
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
# 리소스 사용량 제한
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
# 시스템 로그를 수집하기 위해 시스템 디렉터리 볼륨를 Pod 에 마운트
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
# 시스템 로그를 수집하기 위해 시스템 디렉터리를 볼륨으로 추가
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
Toleration 을 DaemonSet 에 설정한 설명
Job 은 한번 정상적으로 실행되었다면 다시 실행 될 필요 없는 One-Shot 타입의 리소스 이다.
Job 은 설정한 개수 만큼의 Pod 가 성공적으로 완료될 때까지 계속해서 재시도하며 (설정한 개수 만큼의 Pod 가 성공적으로 완료됨을 보장), 성공적으로 완료된 Pod 가 설정된 개수를 넘으면 Job 이 완료된다.
아래 예시는 파이(π) 를 2000 자리까지 계산해서 출력하는 Job 의 예제이다
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
위에 보이듯 YAML 스펙은 Deployment 와 거의 동일하나, 일반적으로 Job을 생성할 때 .spec.selector 를 지정하지 않는다. Job이 알아서 Selector 를 생성하기 때문이며 직접 지정할 경우 세심한 주의가 필요하다. (완료 되지 않았는데 완료 된 것으로 인식 등)
컨테이너를 설정한 개수 만큼 병렬적으로 실행할 수도 있으나 (Parallel Jobs) 각 컨테이너는 독립적으로 동작하는 것을 전제하기 때문에 서로의 통신 등의 작업은 문제가 있을 수 있다.
Job 이 완료되면 Pod 가 신규 생성 되지는 않으나, 이미 생성 된 Pod 가 지워지지도 않는다. Pod 의 로그 등을 확인 할 수 있게 하려는 용도이며, Job 을 삭제하면 연관된 Pod 는 모두 삭제된다.
만약 주기적으로 삭제하려면 spec 에 ttlSecondsAfterFinished 필드를 추가하여 완료 후 삭제할 Timeout 을 지정할 수 있다. 다만, Kubernetes 1.21 에서 Beta 상태이기 때문에 적용은 검토가 필요하다.
쿠버네티스 컨트롤러 : 잡(Job) :: 아리수 (tistory.com)
Kubernetes 1.21 에서 CronJob 이 GA 되었는데, 이 CronJob 을 이용하면 주기적으로 Job을 스케줄링 할 수 있다.
다만, CronJob 은 Job 과는 약간의 구조 차이가 있다. (jobTemplate 밑에 Job 의 spec 필드를 정의 해야 하는 등)
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure