디플로이먼트란

지난 포스트들을 통해 쿠버네티스 클러스터에서 파드(Pod)는 컨테이너로 이루어진 그룹이고, 레플리카셋(ReplicaSet)은 파드 집합의 실행을 안정적으로 유지하는데 목적을 갖춘 워크로드임을 알아보았습니다.

프로덕션 환경에서는 제품을 개발해 배포하고 방치해두고만 있지는 않습니다. 고객의 니즈에 따라, 버그와 같은 새로운 이슈가 뜰 때마다 제품을 업데이트하는데, 이를 재배포하기 위해 서버를 중단하는 것은 큰 반발을 가져옵니다. 디플로이먼트(Deployment)는 파드 또는 레플리카셋의 선언적 업데이트를 제공하는 워크로드입니다.

YAML 구성하기

디플로이먼트도 다른 쿠버네티스 오브젝트들과 마찬가지로 YAML 구성 파일로 관리할 수 있습니다. 디플로이먼트의 YAML 구성 예시는 다음과 같습니다.

# nginx-dp.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
tier: frontend
app: nginx
spec:
selector:
matchLabels:
app: myapp
replicas: 6
template:
metadata:
name: nginx-2
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx:1.24.0
  • apiVersion: 쿠버네티스의 common API인 apps/v1을 지정합니다.
  • kind: 오브젝트 종류를 명시하는 항목으로 Deployment로 지정합니다. 대소문자 표기에 주의합니다.
  • metadata: 디플로이먼트의 정보를 입력하는 항목입니다.
  • metadata.name: 디플로이먼트 조회 명령 입력시 보이는 이름입니다.
  • metadata.labels: 다른 워크로드가 이 디플로이먼트를 참조할 때 selector로 특정하기 위한 라벨입니다.
  • spec.replicas: 복제 파드의 개수를 지정합니다.
  • spec.selector: 디플로이먼트가 배포하는 레플리카셋이 복제할 파드를 참조하기 위한 selector입니다.
  • spec.template: 디플로이먼트가 배포하는 레플리카셋이 복제할 파드의 정보를 입력하는 필드입니다.

spec.template 이 변경되는 경우에만 디플로이먼트의 롤 아웃(roll out) 업데이트가 발동됩니다. 레플리카셋(replica set)이 새 복제 파드(pod)를 로드하고 이전 복제 파드를 삭제해 복구 불가능한 반면에, 디플로이먼트는 변경된 복제 파드 템플릿을 가진 레플리카셋을 새로 생성하고 이전 레플리카셋의 복제 파드 스케일을 0으로 줄이는 방법으로 배포를 업데이트합니다. 이 경우 스케일이 0인 레플리카셋이 노드에 남아있으므로 원하는 업데이트 시점으로 롤 백(roll back) 할 수 있습니다.

API 버전 apps/v1에서 디플로이먼트의 라벨 셀렉터(label selector, spec.selector)는 생성 이후에 변경할 수 없습니다. 따라서 처음 디플로이먼트 구성시 기존에 실행되고 있는 파드, 앞으로 로드할 파드의 라벨을 고려해 셀렉터 계획을 신중히 해야 합니다.

YAML 구성 파일은 kubectl 디플로이먼트 생성 명령을 모의 실행(dry run)해 실행 결과를 파일에 옮겨 담는 방식으로도 생성할 수 있습니다. 기본적인 템플릿이 제공되므로 파일 생성 후 세부 내용을 수정하는 것이 빈 파일에 새로 작성하는 것보다 편리합니다.

# nginx:1.24.0 컨테이너 이미지를 사용하는 복제 파드 6개를 갖는
# myapp-deployment 디플로이먼트를 구성하는 YAML 형식을 nginx-dp.yaml 파일에 옮겨 담기
kubectl create deployment --image=nginx:1.24.0 myapp-deployment --replicas=6 --dry-run=client -o yaml > nginx-dp.yaml
kubectl 디플로이먼트 생성 명령어 모의 실행으로 생성된 nginx-dp.yaml 파일.

디플로이먼트 생성하기

가장 간단한 생성 방법은 kubectl create deployment 명령어를 이용한 명령적 접근법입니다. 파드 템플릿 설정이나 라벨 셀렉터 설정 등 세부 설정을 하지 못한다는 단점이 있습니다.

# 간단하게 디플로이먼트 생성하기
kubectl create deployment --image=nginx:1.24.0 myapp-deployment --replicas=6

kubectl apply 명령어는 YAML 구성 파일을 이용해 생성하는 선언적 접근법입니다. 위에서 구성한 nginx-dp.yaml 파일을 이용해 디플로이먼트를 생성합니다.

# 미리 구성한 nginx-dp.yaml 구성 파일을 이용해 디플로이먼트 생성하기
kubectl apply -f nginx-dp.yaml

생성된 디플로이먼트는 kubectl get 명령어로 조회할 수 있습니다 kubectl은 오브젝트를 종류를 명시할 때 deployment와 축약형인 deploy를 모두 허용합니다.

# 클러스터 내 실행 중인 디플로이먼트 조회
kubectl get deployment
kubectl get deploy

# 특정 디플로이먼트 조회
kubectl get deployment myapp-deployment
kubectl get deploy myapp-deployment
kubectl get deployment/myapp-deployment
kubectl get deploy/myapp-deployment
축약형(pod→po, replicaset→rs, deployment→deploy)를 이용한 조회 명령.

새 배포 업데이트하기

롤 아웃(Roll Out)

디플로이먼트는 파드를 통해 배포를 실행 중일 때 배포를 업데이트하는데 이점을 가지고 있다고 했습니다. 디플로이먼트를 통해 파드가 실행 중인 상태에서 컨테이너 이미지의 버전을 변경해 배포를 업데이트하려고 합니다. kubectl set image 명령어를 통해 새 배포 버전을 선언적(declarative)으로 지정하는 방식으로 업데이트할 수 있습니다.

kubectl set image deployment/{디플로이먼트 이름} {컨테이너 이름}={새 배포 태그}

# Example
kubectl set image deployment/myapp-deployment nginx=nginx:1.25.1
배포 업데이트 전 상태. 레플리카셋과 복제 파드가 764c4cb9c8 태그를 달고 있음에 주목한다.
배포 업데이트 후 상태. 65cfb684c8 태그의 레플리카셋이 생성되고 파드 또한 해당 태그를 달고 있다.

디플로이먼트에서는 배포 업데이트 명령을 실행하면 이전 배포의 정보를 삭제하지 않는 대신, 새 배포 컨테이너를 담은 레플리카셋을 새로 생성해 이전 레플리카셋은 스케일 다운(scale down)하고 새 레플리카셋은 스케일 업(scale up)하는 방식으로 배포를 업데이트 합니다.

위의 이미지에서 기존 nginx:1.24.0 컨테이너 이미지를 사용하고 있는 65cfb684c8 태그의 레플리카셋을 그대로 두고 nginx:1.25.1 컨테이너 이미지를 사용하는 764c4cb9c8 태그의 레플리카셋이 생성됨을 확인할 수 있습니다. 이때 기존 65cfb684c8 태그의 복제 파드가 줄어들고 764c4cb9c8 태그의 복제 파드가 늘어나는 과정은 순차적으로 이뤄지지 않습니다. 업데이트 과정에서 앱에 접속해 있는 고객들이 접속이 끊기는 불편함을 겪지 않게 하기 위해 기존 배포의 스케일 다운과 새 배포의 스케일 업은 부분적으로 동시에 일어나며 이를 롤 아웃(roll out)이라고 합니다.

myapp-deployment 디플로이먼트의 상세 조회 화면. RollingUpdateStrategy에 배포 업데이트시 부분적으로 스케일 업/다운할 복제 파드의 규칙이 지정되어있다.

배포 업데이트시 기존 배포와 새 배포간의 스케일 업/다운 규칙은 디플로이먼트 상세 화면(kubectl describe deploy)의 RollingUpdateStrategy 필드에서 확인할 수 있습니다. 기본적으로 업데이트 과정에서 실행되는 파드의 개수는 지정한 파드 개수의 75%보다 적게 실행될 수 없고(25% max unavailable), 125%를 넘길 수 없습니다(25% max surge).

예를 들어 6개의 복제 파드로 구성된 myapp-deployment 디플로이먼트의 배포를 25% max unavailable, 25% max surge 규칙으로 업데이트 하는 과정은 다음과 같습니다.

6개의 복제 파드로 구성된 디플로이먼트가 25% max unavailable, 25% max surge 규칙으로 업데이트 되는 방법. PowerPoint에서 작성.

해당 범위는 kubectl edit 명령어를 통해 볼 수 있는 YAML 구성 편집 화면에서 spec.strategy 필드의 수치를 수정하는 방법으로 조정할 수 있습니다.

kubectl set image 명령어 외에도 kubectl edit 편집 화면에서 spec.template를 수정하는 방법으로 롤 아웃 업데이트를 발동할 수 있습니다. 예를 들어 spec.template.spec.containers[0].image 필드의 값을 nginx:1.25.1로 수정하면 위의 kubectl set image 명령어와 동일한 결과를 확인할 수 있습니다.

kubectl edit deployment/{디플로이먼트 이름}

# Example
kubectl edit deployment/myapp-deployment

롤 아웃 업데이트된 배포는 리비전 번호가 새로 부여됩니다. 위의 이미지; kubectl describe deploy 상세 화면에서 Annotation 필드에 배포의 리비전이 2임을 확인할 수 있습니다.

kubectl rollout 명령어로 현재 디플로이먼트의 롤 아웃 상태나 롤 아웃 이력을 확인할 수 있습니다.

# 롤 아웃 상태 확인하기
kubectl rollout status deployment myapp-deployment

# 롤 아웃 이력 확인하기
kubectl rollout history deployment myapp-deployment
kubectl rollout status 명령어와 kubectl rollout history 명령어 실행 모습

디플로이먼트를 생성, 업데이트하는 명령마다 --record 플래그를 붙이면 YAML 구성의 metadata.annotations.{kubernetes.io/change-cause} 필드에 명령어 사용 이력이 저장되면서 kubectl history 로 조회한 이력의 CHANGE-CAUSE 필드에 해당 내용을 볼 수 있습니다. 다음 이미지는 기존 디플로이먼트를 삭제하고 디플로이먼트 생성, 롤 아웃 업데이트 과정에 모두 --record 플래그를 붙여 실행했을 때 리비전 이력 조회 결과입니다.

디플로이먼트 생성, 롤 아웃 업데이트 과정에 ‘ — record’ 플래그를 사용한 모습. history의 change-cause에 이력이 남지만 플래그를 사용할 때 마다 ‘ — record’ 플래그는 곧 사장된다는 경고문이 거슬린다.

포스트를 작성하고 있는 2023년 6월 기준, --record 플래그는 사장될 것(deprecated)이라는 경고문이 눈에 띕니다. 독자에 따라 이 포스트를 보는 시점에 최신 kubectl 명령줄 도구에는 --record 플래그가 삭제되어있을 수도 있습니다. stack overflow에서도 --record 플래그의 대안으로 kubectl annotate 명령어를 이용해 kubernetes.io/change-cause 필드에 직접 주석을 작성하라고(…) 밖에 나와 있지 않는 것 같습니다. 추후에 대안이 제시된다면 포스트를 수정해 반영하겠습니다.

kubectl rollout history 명령어를 이용해 조회한 리비전 이력은 다시 --revision 플래그를 이용해 리비전별로 정보를 상세 조회할 수 있습니다. --revision=1 플래그를 이용해 myapp-deployment 디플로이먼트 리비전 1의 상세 정보를 조회한 결과 76c4cb9c8 태그를 가지고 nginx:1.24.0 컨테이너 이미지를 가지고 있다는 것을 확인할 수 있습니다.

# 리비전 1 상세 정보 조회
kubectl rollout history deployment/myapp-deployment --revision=1

# 리비전 2 상세 정보 조회
kubectl rollout history deployment/myapp-deployment --revision=2
myapp-deployment 디플로이먼트의 리비전 1과 리비전 2 개별 상세 조회 화면.

롤 백(Roll Back)

디플로이먼트가 컨테이너 템플릿(container template) 정보를 업데이트해도 과거 리비전(revision)의 정보를 보존하고 있는 것은 과거 리비전으로 복구 가능하다는 것을 의미합니다. 롤 아웃(roll out) 업데이트를 다시 복구한다는 점에서 롤 백(roll back)이라고 부릅니다.

이전 리비전으로 롤 백된 디플로이먼트는 새 리비전 번호를 부여 받음에 주의합니다. 예를 들어 리비전 2에서 리비전 1로 롤 백한 디플로이먼트는 리비전 번호로 1이 아닌 3을 부여받습니다.

# 바로 이전 리비전으로 롤 백하기
kubectl rollout undo deployment/myapp-deployment

# 이전의 특정 리비전 번호로 롤 백하기
kubectl rollout undo deployment/myapp-deployment --to-revision=1
myapp-deployment 디플로이먼트를 리비전 2에서 1로 롤 백하자 65cf 레플리카셋이 스케일 다운되고 764c 레플리카셋이 스케일 업된 모습. 롤 백된 디플로이먼트는 새로 리비전 3을 부여받는다.

롤 백의 과정은 롤 아웃과 다를 것이 없습니다. 롤 백 과정에서는 이전 리비전의 컨테이너 템플릿을 지니고 스케일이 0인 상태로 보존되고 있는 레플리카셋(replica set)을 이용합니다. 롤 백하려는 이전 리비전의 레플리카셋이 스케일 업되고 기존 레플리카셋은 스케일 다운되는 방식으로 업데이트됩니다.

롤 아웃과 마찬가지로 사용자가 spec.strategy에서 지정한 방식으로(기본 값으로는 25% max unavailable, 25% max surge) 부분 스케일 업/다운 되어 제품사용자 입장에서 업데이트로 인해 접속이 끊기는 상황을 대비합니다.

스케일링(Scaling)

디플로이먼트의 스케일링은 레플리카셋의 스케일링과 마찬가지로 kubectl scale 명령어를 이용해 적용할 수 있습니다.

스케일링은 spec.replicas의 수치를 조정합니다. 즉, spec.template 를 건들지 않으므로 롤 아웃의 트리거(trigger)가 되지 않습니다. 따라서 새 태그를 달은 레플리카셋이 생성되거나 리비전 번호가 새로 부여되지 않습니다.

kubectl scale deployment/{디플로이먼트 이름} --replicas={복제 파드 개수}

# Example
kubectl scale deployment/myapp-deployment --replicas=8
myapp-deployment 디플로이먼트의 복제 파드수를 기존 6개에서 8개로 조정한 모습. 컨테이너 템플릿이 변경된 것이 아니므로 리비전이 변경되지 않는다.

마무리

쿠버네티스 클러스터를 구성하는 대표적인 오브젝트인 디플로이먼트에 대해 알아보았습니다. 실제 프로덕션 과정에서는 파드와 레플리카셋 모두 단독으로 사용하지 않고 디플로이먼트에 감싸 배포해 버전 관리를 용이하게 합니다.

다음 포스트에서는 쿠버네티스 클러스터 외부에서 노드와 파드에 접근하기 위한 오브젝트인 서비스(Service)에 대해 알아보려고 합니다.

참고자료

--

--