이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.
워크로드 리소스
- 1: 디플로이먼트
- 2: 레플리카셋
- 3: 스테이트풀셋
- 4: 데몬셋
- 5: 잡
- 6: 완료된 잡 자동 정리
- 7: 크론잡
- 8: 레플리케이션 컨트롤러
1 - 디플로이먼트
디플로이먼트(Deployment) 는 파드와 레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공한다.
디플로이먼트에서 의도하는 상태 를 설명하고, 디플로이먼트 컨트롤러(Controller)는 현재 상태에서 의도하는 상태로 비율을 조정하며 변경한다. 새 레플리카셋을 생성하는 디플로이먼트를 정의하거나 기존 디플로이먼트를 제거하고, 모든 리소스를 새 디플로이먼트에 적용할 수 있다.
참고:
디플로이먼트가 소유하는 레플리카셋은 관리하지 말아야 한다. 사용자의 유스케이스가 다음에 포함되지 않는 경우 쿠버네티스 리포지터리에 이슈를 올릴 수 있다.유스케이스
다음은 디플로이먼트의 일반적인 유스케이스이다.
- 레플리카셋을 롤아웃 할 디플로이먼트 생성. 레플리카셋은 백그라운드에서 파드를 생성한다. 롤아웃 상태를 체크해서 성공 여부를 확인한다.
- 디플로이먼트의 PodTemplateSpec을 업데이트해서 파드의 새로운 상태를 선언한다. 새 레플리카셋이 생성되면, 디플로이먼트는 파드를 기존 레플리카셋에서 새로운 레플리카셋으로 속도를 제어하며 이동하는 것을 관리한다. 각각의 새로운 레플리카셋은 디플로이먼트의 수정 버전에 따라 업데이트한다.
- 만약 디플로이먼트의 현재 상태가 안정적이지 않은 경우 디플로이먼트의 이전 버전으로 롤백한다. 각 롤백은 디플로이먼트의 수정 버전에 따라 업데이트한다.
- 더 많은 로드를 위해 디플로이먼트의 스케일 업.
- 디플로이먼트 롤아웃 일시 중지로 PodTemplateSpec에 여러 수정 사항을 적용하고, 재개하여 새로운 롤아웃을 시작한다.
- 롤아웃이 막혀있는지를 나타내는 디플로이먼트 상태를 이용.
- 더 이상 필요 없는 이전 레플리카셋 정리.
디플로이먼트 생성
다음은 디플로이먼트의 예시이다. 예시는 3개의 nginx
파드를 불러오기 위한 레플리카셋을 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
이 예시에 대한 설명은 다음과 같다.
.metadata.name
필드에 따라,nginx-deployment
이름을 가진 디플로이먼트가 생성된다..spec.replicas
필드에 따라, 디플로이먼트는 3개의 레플리카 파드를 생성하는 레플리카셋을 생성한다..spec.selector
필드는, 생성된 레플리카셋이 관리할 파드를 찾아내는 방법을 정의한다. 이 사례에서는 파드 템플릿에 정의된 레이블(app: nginx
)을 선택한다. 그러나 파드 템플릿 자체의 규칙이 만족되는 한, 보다 정교한 선택 규칙의 적용이 가능하다.참고:
.spec.selector.matchLabels
필드는 {key,value}의 쌍으로 매핑되어 있다.matchLabels
에 매핑된 단일 {key,value}은matchExpressions
의 요소에 해당하며,key
필드는 "key"에 그리고operator
는 "In"에 대응되며value
배열은 "value"만 포함한다. 매칭을 위해서는matchLabels
와matchExpressions
의 모든 요건이 충족되어야 한다.template
필드에는 다음 하위 필드가 포함되어 있다.- 파드는
.metadata.labels
필드를 사용해서app: nginx
라는 레이블을 붙인다. - 파드 템플릿의 사양 또는
.template.spec
필드는 파드가 도커 허브의nginx
1.14.2 버전 이미지를 실행하는nginx
컨테이너 1개를 실행하는 것을 나타낸다. - 컨테이너 1개를 생성하고,
.spec.template.spec.containers[0].name
필드를 사용해서nginx
이름을 붙인다.
- 파드는
시작하기 전에, 쿠버네티스 클러스터가 시작되고 실행 중인지 확인한다. 위의 디플로이먼트를 생성하려면 다음 단계를 따른다.
- 다음 명령어를 실행해서 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
kubectl get deployments
을 실행해서 디플로이먼트가 생성되었는지 확인한다.
만약 디플로이먼트가 여전히 생성 중이면, 다음과 유사하게 출력된다.
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 0/3 0 0 1s
클러스터에서 디플로이먼트를 점검할 때, 다음 필드가 표시된다.
NAME
은 네임스페이스에 있는 디플로이먼트 이름의 목록이다.READY
는 사용자가 사용할 수 있는 애플리케이션의 레플리카의 수를 표시한다. ready/desired 패턴을 따른다.UP-TO-DATE
는 의도한 상태를 얻기 위해 업데이트된 레플리카의 수를 표시한다.AVAILABLE
은 사용자가 사용할 수 있는 애플리케이션 레플리카의 수를 표시한다.AGE
는 애플리케이션의 실행된 시간을 표시한다.
.spec.replicas
필드에 따라 의도한 레플리카의 수가 3개인지 알 수 있다.
디플로이먼트의 롤아웃 상태를 보려면,
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
몇 초 후
kubectl get deployments
를 다시 실행한다. 다음과 유사하게 출력된다.NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 18s
디플로이먼트에서 3개의 레플리카가 생성되었고, 모든 레플리카는 최신 상태(최신 파드 템플릿을 포함)이며 사용 가능한 것을 알 수 있다.
디플로이먼트로 생성된 레플리카셋(
rs
)을 보려면,kubectl get rs
를 실행한다. 다음과 유사하게 출력된다.NAME DESIRED CURRENT READY AGE nginx-deployment-75675f5897 3 3 3 18s
레플리카셋의 출력에는 다음 필드가 표시된다.
NAME
은 네임스페이스에 있는 레플리카셋 이름의 목록이다.DESIRED
는 디플로이먼트의 생성 시 정의된 의도한 애플리케이션 레플리카 의 수를 표시한다. 이것이 의도한 상태 이다.CURRENT
는 현재 실행 중인 레플리카의 수를 표시한다.READY
는 사용자가 사용할 수 있는 애플리케이션의 레플리카의 수를 표시한다.AGE
는 애플리케이션의 실행된 시간을 표시한다.
레플리카셋의 이름은 항상
[DEPLOYMENT-NAME]-[HASH]
형식으로 된 것을 알 수 있다.HASH
문자열은 레플리카셋의pod-template-hash
레이블과 같다.각 파드에 자동으로 생성된 레이블을 보려면,
kubectl get pods --show-labels
를 실행한다. 다음과 유사하게 출력된다.NAME READY STATUS RESTARTS AGE LABELS nginx-deployment-75675f5897-7ci7o 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453 nginx-deployment-75675f5897-kzszj 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453 nginx-deployment-75675f5897-qqcnn 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
만들어진 레플리카셋은 실행 중인 3개의
nginx
파드를 보장한다.
참고:
디플로이먼트에는 파드 템플릿 레이블과 적절한 셀렉터를 반드시 명시해야 한다
(이 예시에서는 app: nginx
).
레이블 또는 셀렉터는 다른 컨트롤러(다른 디플로이먼트와 스테이트풀셋(StatefulSet) 포함)와 겹치지 않아야 한다. 쿠버네티스는 겹치는 것을 막지 않으며, 만약 다중 컨트롤러가 겹치는 셀렉터를 가지는 경우 해당 컨트롤러의 충돌 또는 예기치 않은 동작을 야기할 수 있다.
Pod-template-hash 레이블
주의:
이 레이블은 변경하면 안 된다.pod-template-hash
레이블은 디플로이먼트 컨트롤러에 의해서 디플로이먼트가 생성 또는 채택한 모든 레플리카셋에 추가된다.
이 레이블은 디플로이먼트의 자식 레플리카셋이 겹치지 않도록 보장한다. 레플리카셋의 PodTemplate
을 해싱하고, 해시 결과를 레플리카셋 셀렉터,
파드 템플릿 레이블 및 레플리카셋 이 가질 수 있는 기존의 모든 파드에 레이블 값으로 추가해서 사용하도록 생성한다.
디플로이먼트 업데이트
참고:
디플로이먼트의 파드 템플릿(즉,.spec.template
)이 변경된 경우에만 디플로이먼트의 롤아웃이 트리거(trigger) 된다.
예를 들면 템플릿의 레이블이나 컨테이너 이미지가 업데이트된 경우이다. 디플로이먼트의 스케일링과 같은 다른 업데이트는 롤아웃을 트리거하지 말아야 한다.다음 단계에 따라 디플로이먼트를 업데이트한다.
nginx:1.14.2
이미지 대신nginx:1.16.1
이미지를 사용하도록 nginx 파드를 업데이트 한다.kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
또는 다음의 명령어를 사용한다.
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
다음과 유사하게 출력된다.
deployment.apps/nginx-deployment image updated
대안으로 디플로이먼트를
edit
해서.spec.template.spec.containers[0].image
를nginx:1.14.2
에서nginx:1.16.1
로 변경한다.kubectl edit deployment/nginx-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...
또는
deployment "nginx-deployment" successfully rolled out
업데이트된 디플로이먼트에 대해 자세한 정보 보기
롤아웃이 성공하면
kubectl get deployments
를 실행해서 디플로이먼트를 볼 수 있다. 이와 유사하게 출력된다.NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 36s
kubectl get rs
를 실행해서 디플로이먼트가 새 레플리카셋을 생성해서 파드를 업데이트 했는지 볼 수 있고, 새 레플리카셋을 최대 3개의 레플리카로 스케일 업, 이전 레플리카셋을 0개의 레플리카로 스케일 다운한다.kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-deployment-1564180365 3 3 3 6s nginx-deployment-2035384211 0 0 0 36s
get pods
를 실행하면 새 파드만 표시된다.kubectl get pods
이와 유사하게 출력된다.
NAME READY STATUS RESTARTS AGE nginx-deployment-1564180365-khku8 1/1 Running 0 14s nginx-deployment-1564180365-nacti 1/1 Running 0 14s nginx-deployment-1564180365-z9gth 1/1 Running 0 14s
다음에 이러한 파드를 업데이트 하려면 디플로이먼트의 파드 템플릿만 다시 업데이트 하면 된다.
디플로이먼트는 업데이트되는 동안 일정한 수의 파드만 중단되도록 보장한다. 기본적으로 적어도 의도한 파드 수의 75% 이상이 동작하도록 보장한다(최대 25% 불가).
또한 디플로이먼트는 의도한 파드 수 보다 더 많이 생성되는 파드의 수를 제한한다. 기본적으로, 의도한 파드의 수 기준 최대 125%까지만 추가 파드가 동작할 수 있도록 제한한다(최대 25% 까지).
예를 들어, 위 디플로이먼트를 자세히 살펴보면 먼저 새로운 파드를 생성한 다음, 이전 파드를 삭제하고, 또 다른 새로운 파드를 만든 것을 볼 수 있다. 충분한 수의 새로운 파드가 나올 때까지 이전 파드를 죽이지 않으며, 충분한 수의 이전 파드들이 죽기 전까지 새로운 파드를 만들지 않는다. 이것은 최소 3개의 파드를 사용할 수 있게 하고, 최대 4개의 파드를 사용할 수 있게 한다. 디플로이먼트의 레플리카 크기가 4인 경우, 파드 숫자는 3개에서 5개 사이이다.
디플로이먼트의 세부 정보 가져오기
kubectl describe deployments
이와 유사하게 출력된다.
Name: nginx-deployment Namespace: default CreationTimestamp: Thu, 30 Nov 2017 10:56:25 +0000 Labels: app=nginx Annotations: deployment.kubernetes.io/revision=2 Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set nginx-deployment-2035384211 to 3 Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 1 Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 2 Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 2 Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 1 Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 3 Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 0
처음 디플로이먼트를 생성했을 때, 디플로이먼트가 레플리카셋(nginx-deployment-2035384211)을 생성하고 3개의 레플리카로 직접 스케일 업한 것을 볼 수 있다. 디플로이먼트를 업데이트하자, 새 레플리카셋(nginx-deployment-1564180365)을 생성하고, 1개로 스케일 업한 다음 모두 실행될 때까지 대기하였다. 그 뒤 이전 레플리카셋을 2개로 스케일 다운하고 새 레플리카셋을 2개로 스케일 업하여 모든 시점에 대해 최소 3개 / 최대 3개의 파드가 존재하도록 하였다. 이후 지속해서 같은 롤링 업데이트 정책으로 새 레플리카셋은 스케일 업하고 이전 레플리카셋은 스케일 다운한다. 마지막으로 새로운 레플리카셋에 3개의 사용 가능한 레플리카가 구성되며, 이전 레플리카셋은 0개로 스케일 다운된다.
참고:
쿠버네티스가availableReplicas
수를 계산할 때 종료 중인(terminating) 파드는 포함하지 않으며,
이 수는 replicas - maxUnavailable
와 replicas + maxSurge
사이에 존재한다.
그 결과, 롤아웃 중에는 파드의 수가 예상보다 많을 수 있으며,
종료 중인 파드의 terminationGracePeriodSeconds
가 만료될 때까지는 디플로이먼트가 소비하는 총 리소스가 replicas + maxSurge
이상일 수 있다.롤오버(일명 인-플라이트 다중 업데이트)
디플로이먼트 컨트롤러는 각 시간마다 새로운 디플로이먼트에서 레플리카셋이
의도한 파드를 생성하고 띄우는 것을 주시한다. 만약 디플로이먼트가 업데이트되면, 기존 레플리카셋에서
.spec.selector
레이블과 일치하는 파드를 컨트롤 하지만, 템플릿과 .spec.template
이 불일치하면 스케일 다운이 된다.
결국 새로운 레플리카셋은 .spec.replicas
로 스케일되고, 모든 기존 레플리카셋은 0개로 스케일된다.
만약 기존 롤아웃이 진행되는 중에 디플로이먼트를 업데이트하는 경우 디플로이먼트가 업데이트에 따라 새 레플리카셋을 생성하고, 스케일 업하기 시작한다. 그리고 이전에 스케일 업 하던 레플리카셋에 롤오버 한다. --이것은 기존 레플리카셋 목록에 추가하고 스케일 다운을 할 것이다.
예를 들어 디플로이먼트로 nginx:1.14.2
레플리카를 5개 생성을 한다.
하지만 nginx:1.14.2
레플리카 3개가 생성되었을 때 디플로이먼트를 업데이트해서 nginx:1.16.1
레플리카 5개를 생성성하도록 업데이트를 한다고 가정한다. 이 경우 디플로이먼트는 즉시 생성된 3개의
nginx:1.14.2
파드 3개를 죽이기 시작하고 nginx:1.16.1
파드를 생성하기 시작한다.
이것은 과정이 변경되기 전 nginx:1.14.2
레플리카 5개가
생성되는 것을 기다리지 않는다.
레이블 셀렉터 업데이트
일반적으로 레이블 셀렉터를 업데이트 하는 것을 권장하지 않으며 셀렉터를 미리 계획하는 것을 권장한다. 어떤 경우든 레이블 셀렉터의 업데이트를 해야하는 경우 매우 주의하고, 모든 영향을 파악했는지 확인해야 한다.
참고:
API 버전apps/v1
에서 디플로이먼트의 레이블 셀렉터는 생성 이후에는 변경할 수 없다.- 셀렉터 추가 시 디플로이먼트의 사양에 있는 파드 템플릿 레이블도 새 레이블로 업데이트해야 한다. 그렇지 않으면 유효성 검사 오류가 반환된다. 이 변경은 겹치지 않는 변경으로 새 셀렉터가 이전 셀렉터로 만든 레플리카셋과 파드를 선택하지 않게 되고, 그 결과로 모든 기존 레플리카셋은 고아가 되며, 새로운 레플리카셋을 생성하게 된다.
- 셀렉터 업데이트는 기존 셀렉터 키 값을 변경하며, 결과적으로 추가와 동일한 동작을 한다.
- 셀렉터 삭제는 디플로이먼트 셀렉터의 기존 키를 삭제하며 파드 템플릿 레이블의 변경을 필요로 하지 않는다. 기존 레플리카셋은 고아가 아니고, 새 레플리카셋은 생성되지 않는다. 그러나 제거된 레이블은 기존 파드와 레플리카셋에 여전히 존재한다는 점을 참고해야 한다.
디플로이먼트 롤백
때때로 디플로이먼트의 롤백을 원할 수도 있다. 예를 들어 디플로이먼트가 지속적인 충돌로 안정적이지 않은 경우. 기본적으로 모든 디플로이먼트의 롤아웃 기록은 시스템에 남아있어 언제든지 원할 때 롤백이 가능하다 (이 사항은 수정 기록에 대한 상한 수정을 통해서 변경할 수 있다).
참고:
디플로이먼트의 수정 버전은 디플로이먼트 롤아웃시 생성된다. 이는 디플로이먼트 파드 템플릿 (.spec.template
)이 변경되는 경우에만 새로운 수정 버전이 생성된다는 것을 의미한다.
예를 들어 템플릿의 레이블 또는 컨테이너 이미지를 업데이트 하는 경우.
디플로이먼트의 스케일링과 같은 다른 업데이트시 디플로이먼트 수정 버전은 생성되지 않으며 수동-스케일링 또는 자동-스케일링을 동시에 수행할 수 있다.
이는 이전 수정 버전으로 롤백을 하는 경우에 디플로이먼트 파드 템플릿 부분만
롤백된다는 것을 의미한다.디플로이먼트를 업데이트하는 동안 이미지 이름을
nginx:1.16.1
이 아닌nginx:1.161
로 입력해서 오타를 냈다고 가정한다.kubectl set image deployment/nginx-deployment nginx=nginx:1.161
이와 유사하게 출력된다.
deployment.apps/nginx-deployment image updated
롤아웃이 고착 된다. 고착된 롤아웃 상태를 확인할 수 있다.
kubectl rollout status deployment/nginx-deployment
이와 유사하게 출력된다.
Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
Ctrl-C 를 눌러 위의 롤아웃 상태 보기를 중지한다. 고착된 롤아웃 상태에 대한 자세한 정보는 이 것을 더 읽어본다.
이전 레플리카는 2개(
nginx-deployment-1564180365
과nginx-deployment-2035384211
), 새 레플리카는 1개(nginx-deployment-3066724191)임을 알 수 있다.kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-deployment-1564180365 3 3 3 25s nginx-deployment-2035384211 0 0 0 36s nginx-deployment-3066724191 1 1 0 6s
생성된 파드를 보면, 새로운 레플리카셋에 생성된 1개의 파드가 이미지 풀 루프(pull loop)에서 고착된 것을 볼 수 있다.
kubectl get pods
이와 유사하게 출력된다.
NAME READY STATUS RESTARTS AGE nginx-deployment-1564180365-70iae 1/1 Running 0 25s nginx-deployment-1564180365-jbqqo 1/1 Running 0 25s nginx-deployment-1564180365-hysrc 1/1 Running 0 25s nginx-deployment-3066724191-08mng 0/1 ImagePullBackOff 0 6s
참고:
디플로이먼트 컨트롤러가 잘못된 롤아웃을 자동으로 중지하고, 새로운 레플리카셋의 스케일 업을 중지한다. 이는 지정한 롤링 업데이트의 파라미터(구체적으로 `maxUnavailable`)에 따라 달라진다. 쿠버네티스는 기본값으로 25%를 설정한다.
디플로이먼트에 대한 설명 보기
kubectl describe deployment
이와 유사하게 출력된다.
Name: nginx-deployment Namespace: default CreationTimestamp: Tue, 15 Mar 2016 14:48:04 -0700 Labels: app=nginx Selector: app=nginx Replicas: 3 desired | 1 updated | 4 total | 3 available | 1 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.161 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True ReplicaSetUpdated OldReplicaSets: nginx-deployment-1564180365 (3/3 replicas created) NewReplicaSet: nginx-deployment-3066724191 (1/1 replicas created) Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2035384211 to 3 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 1 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 2 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 2 21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 1 21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 3 13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 0 13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 1
이 문제를 해결하려면 디플로이먼트를 안정적인 이전 수정 버전으로 롤백해야 한다.
디플로이먼트의 롤아웃 기록 확인
다음 순서에 따라 롤아웃 기록을 확인한다.
먼저 이 디플로이먼트의 수정 사항을 확인한다.
kubectl rollout history deployment/nginx-deployment
이와 유사하게 출력된다.
deployments "nginx-deployment" REVISION CHANGE-CAUSE 1 kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml 2 kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 3 kubectl set image deployment/nginx-deployment nginx=nginx:1.161
CHANGE-CAUSE
는 수정 생성시 디플로이먼트 주석인kubernetes.io/change-cause
에서 복사한다. 다음에 대해CHANGE-CAUSE
메시지를 지정할 수 있다.- 디플로이먼트에
kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"
로 주석을 단다. - 수동으로 리소스 매니페스트 편집.
- 디플로이먼트에
각 수정 버전의 세부 정보를 보려면 다음을 실행한다.
kubectl rollout history deployment/nginx-deployment --revision=2
이와 유사하게 출력된다.
deployments "nginx-deployment" revision 2 Labels: app=nginx pod-template-hash=1159050644 Annotations: kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP QoS Tier: cpu: BestEffort memory: BestEffort Environment Variables: <none> No volumes.
이전 수정 버전으로 롤백
다음 단계에 따라 디플로이먼트를 현재 버전에서 이전 버전인 버전 2로 롤백한다.
이제 현재 롤아웃의 실행 취소 및 이전 수정 버전으로 롤백 하기로 결정했다.
kubectl rollout undo deployment/nginx-deployment
이와 유사하게 출력된다.
deployment.apps/nginx-deployment rolled back
또는 특정 수정 버전으로 롤백하려면
--to-revision
옵션에 해당 수정 버전을 명시한다.kubectl rollout undo deployment/nginx-deployment --to-revision=2
이와 유사하게 출력된다.
deployment.apps/nginx-deployment rolled back
롤아웃 관련 명령에 대한 자세한 내용은
kubectl rollout
을 참조한다.이제 디플로이먼트가 이전 안정 수정 버전으로 롤백 된다. 버전 2로 롤백하기 위해
DeploymentRollback
이벤트가 디플로이먼트 컨트롤러에서 생성되는 것을 볼 수 있다.만약 롤백에 성공하고, 디플로이먼트가 예상대로 실행되는지 확인하려면 다음을 실행한다.
kubectl get deployment nginx-deployment
이와 유사하게 출력된다.
NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 30m
디플로이먼트의 설명 가져오기.
kubectl describe deployment nginx-deployment
이와 유사하게 출력된다.
Name: nginx-deployment Namespace: default CreationTimestamp: Sun, 02 Sep 2018 18:17:55 -0500 Labels: app=nginx Annotations: deployment.kubernetes.io/revision=4 kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-c4747d96c (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deployment-75675f5897 to 3 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 1 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 2 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 2 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 1 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 3 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 0 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-595696685f to 1 Normal DeploymentRollback 15s deployment-controller Rolled back deployment "nginx-deployment" to revision 2 Normal ScalingReplicaSet 15s deployment-controller Scaled down replica set nginx-deployment-595696685f to 0
디플로이먼트 스케일링
다음 명령어를 사용해서 디플로이먼트의 스케일을 할 수 있다.
kubectl scale deployment/nginx-deployment --replicas=10
이와 유사하게 출력된다.
deployment.apps/nginx-deployment scaled
가령 클러스터에서 horizontal Pod autoscaling를 설정 한 경우 디플로이먼트에 대한 오토스케일러를 설정할 수 있다. 그리고 기존 파드의 CPU 사용률을 기준으로 실행할 최소 파드 및 최대 파드의 수를 선택할 수 있다.
kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80
이와 유사하게 출력된다.
deployment.apps/nginx-deployment scaled
비례적 스케일링(Proportional Scaling)
디플로이먼트 롤링업데이트는 여러 버전의 애플리케이션을 동시에 실행할 수 있도록 지원한다. 사용자 또는 오토스케일러가 롤아웃 중에 있는 디플로이먼트 롤링 업데이트를 스케일링 하는 경우(진행중 또는 일시 중지 중), 디플로이먼트 컨트롤러는 위험을 줄이기 위해 기존 활성화된 레플리카셋(파드와 레플리카셋)의 추가 레플리카의 균형을 조절 한다. 이것을 proportional scaling 라 부른다.
예를 들어, 10개의 레플리카를 디플로이먼트로 maxSurge=3, 그리고 maxUnavailable=2 로 실행 한다.
디플로이먼트에 있는 10개의 레플리카가 실행되는지 확인한다.
kubectl get deploy
이와 유사하게 출력된다.
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx-deployment 10 10 10 10 50s
클러스터 내부에서 확인할 수 없는 새 이미지로 업데이트 된다.
kubectl set image deployment/nginx-deployment nginx=nginx:sometag
이와 유사하게 출력된다.
deployment.apps/nginx-deployment image updated
이미지 업데이트는 레플리카셋 nginx-deployment-1989198191 으로 새로운 롤 아웃이 시작하지만, 위에서 언급한
maxUnavailable
의 요구 사항으로 인해 차단된다. 롤아웃 상태를 확인한다.kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-deployment-1989198191 5 5 0 9s nginx-deployment-618515232 8 8 8 1m
그 다음 디플로이먼트에 대한 새로운 스케일링 요청이 함께 따라온다. 오토스케일러는 디플로이먼트 레플리카를 15로 증가시킨다. 디플로이먼트 컨트롤러는 새로운 5개의 레플리카의 추가를 위한 위치를 결정해야 한다. 만약 비례적 스케일링을 사용하지 않으면 5개 모두 새 레플리카셋에 추가된다. 비례적 스케일링으로 추가 레플리카를 모든 레플리카셋에 걸쳐 분산할 수 있다. 비율이 높을수록 가장 많은 레플리카가 있는 레플리카셋으로 이동하고, 비율이 낮을 수록 적은 레플리카가 있는 레플리카셋으로 이동한다. 남은 것들은 대부분의 레플리카가 있는 레플리카셋에 추가된다. 0개의 레플리카가 있는 레플리카셋은 스케일 업 되지 않는다.
위의 예시에서 기존 레플리카셋에 3개의 레플리카가 추가되고, 2개의 레플리카는 새 레플리카에 추가된다. 결국 롤아웃 프로세스는 새 레플리카가 정상이라고 가정하면 모든 레플리카를 새 레플리카셋으로 이동시킨다. 이를 확인하려면 다음을 실행한다.
kubectl get deploy
이와 유사하게 출력된다.
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 15 18 7 8 7m
롤아웃 상태는 레플리카가 각 레플리카셋에 어떻게 추가되었는지 확인한다.
kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE
nginx-deployment-1989198191 7 7 0 7m
nginx-deployment-618515232 11 11 11 7m
디플로이먼트 롤아웃 일시 중지와 재개
디플로이먼트를 업데이트할 때 (또는 계획할 때), 하나 이상의 업데이트를 트리거하기 전에 해당 디플로이먼트에 대한 롤아웃을 일시 중지할 수 있다. 변경 사항을 적용할 준비가 되면, 디플로이먼트 롤아웃을 재개한다. 이러한 방법으로, 불필요한 롤아웃을 트리거하지 않고 롤아웃 일시 중지와 재개 사이에 여러 수정 사항을 적용할 수 있다.
예를 들어, 생성된 디플로이먼트의 경우
디플로이먼트 상세 정보를 가져온다.
kubectl get deploy
이와 유사하게 출력된다.
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 3 3 3 3 1m
롤아웃 상태를 가져온다.
kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-2142116321 3 3 3 1m
다음 명령을 사용해서 일시 중지한다.
kubectl rollout pause deployment/nginx-deployment
이와 유사하게 출력된다.
deployment.apps/nginx-deployment paused
그런 다음 디플로이먼트의 이미지를 업데이트 한다.
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
이와 유사하게 출력된다.
deployment.apps/nginx-deployment image updated
새로운 롤아웃이 시작되지 않는다.
kubectl rollout history deployment/nginx-deployment
이와 유사하게 출력된다.
deployments "nginx" REVISION CHANGE-CAUSE 1 <none>
기존 레플리카셋이 변경되지 않았는지 확인하기 위해 롤아웃 상태를 출력한다.
kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-2142116321 3 3 3 2m
예를 들어 사용할 리소스를 업데이트하는 것처럼 원하는 만큼 업데이트할 수 있다.
kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
이와 유사하게 출력된다.
deployment.apps/nginx-deployment resource requirements updated
디플로이먼트 롤아웃을 일시 중지하기 전 디플로이먼트의 초기 상태는 해당 기능을 지속한다. 그러나 디플로이먼트 롤아웃이 일시 중지한 상태에서는 디플로이먼트의 새 업데이트에 영향을 주지 않는다.
결국, 디플로이먼트 롤아웃을 재개하고 새로운 레플리카셋이 새로운 업데이트를 제공하는 것을 관찰한다.
kubectl rollout resume deployment/nginx-deployment
이와 유사하게 출력된다.
deployment.apps/nginx-deployment resumed
롤아웃이 완료될 때까지 상태를 관찰한다.
kubectl get rs -w
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-2142116321 2 2 2 2m nginx-3926361531 2 2 0 6s nginx-3926361531 2 2 1 18s nginx-2142116321 1 2 2 2m nginx-2142116321 1 2 2 2m nginx-3926361531 3 2 1 18s nginx-3926361531 3 2 1 18s nginx-2142116321 1 1 1 2m nginx-3926361531 3 3 1 18s nginx-3926361531 3 3 2 19s nginx-2142116321 0 1 1 2m nginx-2142116321 0 1 1 2m nginx-2142116321 0 0 0 2m nginx-3926361531 3 3 3 20s
롤아웃 최신 상태를 가져온다.
kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE nginx-2142116321 0 0 0 2m nginx-3926361531 3 3 3 28s
참고:
일시 중지된 디플로이먼트를 재개할 때까지 롤백할 수 없다.디플로이먼트 상태
디플로이먼트는 라이프사이클 동안 다양한 상태로 전환된다. 이는 새 레플리카셋을 롤아웃하는 동안 진행 중이 될 수 있고, 완료이거나 진행 실패일 수 있다.
디플로이먼트 진행 중
쿠버네티스는 다음 작업중 하나를 수행할 때 디플로이먼트를 진행 중 으로 표시한다.
- 디플로이먼트로 새 레플리카셋을 생성.
- 디플로이먼트로 새로운 레플리카셋을 스케일 업.
- 디플로이먼트로 기존 레플리카셋을 스케일 다운.
- 새 파드가 준비되거나 이용할 수 있음(최소 준비 시간(초) 동안 준비됨).
롤아웃이 "진행 중" 상태가 되면,
디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions
에 다음 속성을 포함하는 컨디션을 추가한다.
type: Progressing
status: "True"
reason: NewReplicaSetCreated
|reason: FoundNewReplicaSet
|reason: ReplicaSetUpdated
kubectl rollout status
를 사용해서 디플로이먼트의 진행사황을 모니터할 수 있다.
디플로이먼트 완료
쿠버네티스는 다음과 같은 특성을 가지게 되면 디플로이먼트를 완료 로 표시한다.
- 디플로이먼트과 관련된 모든 레플리카가 지정된 최신 버전으로 업데이트 되었을 때. 즉, 요청한 모든 업데이트가 완료되었을 때.
- 디플로이먼트와 관련한 모든 레플리카를 사용할 수 있을 때.
- 디플로이먼트에 대해 이전 복제본이 실행되고 있지 않을 때.
롤아웃이 "완료" 상태가 되면,
디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions
에 다음 속성을 포함하는 컨디션을 추가한다.
type: Progressing
status: "True"
reason: NewReplicaSetAvailable
이 Progressing
컨디션은 새로운 롤아웃이 시작되기 전까지는 "True"
상태값을 유지할 것이다.
레플리카의 가용성이 변경되는 경우에도(이 경우 Available
컨디션에 영향을 미침)
컨디션은 유지된다.
kubectl rollout status
를 사용해서 디플로이먼트가 완료되었는지 확인할 수 있다.
만약 롤아웃이 성공적으로 완료되면 kubectl rollout status
는 종료 코드로 0이 반환된다.
kubectl rollout status deployment/nginx-deployment
이와 유사하게 출력된다.
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
그리고 kubectl rollout
의 종료 상태는 0(success)이다.
echo $?
0
디플로이먼트 실패
디플로이먼트시 새 레플리카셋인 완료되지 않은 상태에서는 배포를 시도하면 고착될 수 있다. 이 문제는 다음 몇 가지 요인으로 인해 발생한다.
- 할당량 부족
- 준비성 프로브(readiness probe)의 실패
- 이미지 풀 에러
- 권한 부족
- 범위 제한
- 애플리케이션 런타임의 잘못된 구성
이 조건을 찾을 수 있는 한 가지 방법은 디플로이먼트 스펙에서 데드라인 파라미터를 지정하는 것이다
(.spec.progressDeadlineSeconds
). .spec.progressDeadlineSeconds
는
(디플로이먼트 상태에서) 디플로이먼트의 진행이 정지되었음을 나타내는 디플로이먼트 컨트롤러가
대기하는 시간(초)를 나타낸다.
다음 kubectl
명령어로 progressDeadlineSeconds
를 설정해서 컨트롤러가
10분 후 디플로이먼트 롤아웃에 대한 진행 상태의 부족에 대한 리포트를 수행하게 한다.
kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
이와 유사하게 출력된다.
deployment.apps/nginx-deployment patched
만약 데드라인을 넘어서면 디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions
속성에 다음의
디플로이먼트 컨디션(DeploymentCondition)을 추가한다.
type: Progressing
status: "False"
reason: ProgressDeadlineExceeded
이 컨디션은 일찍 실패할 수도 있으며 이러한 경우 ReplicaSetCreateError
를 이유로 상태값을 "False"
로 설정한다.
또한, 디플로이먼트 롤아웃이 완료되면 데드라인은 더 이상 고려되지 않는다.
컨디션 상태에 대한 자세한 내용은 쿠버네티스 API 규칙을 참고한다.
참고:
쿠버네티스는reason: ProgressDeadlineExceeded
과 같은 상태 조건을
보고하는 것 이외에 정지된 디플로이먼트에 대해 조치를 취하지 않는다. 더 높은 수준의 오케스트레이터는 이를 활용할 수 있으며,
예를 들어 디플로이먼트를 이전 버전으로 롤백할 수 있다.참고:
만약 디플로이먼트 롤아웃을 일시 중지하면 쿠버네티스는 지정된 데드라인과 비교하여 진행 상황을 확인하지 않는다. 롤아웃 중에 디플로이먼트 롤아웃을 안전하게 일시 중지하고, 데드라인을 넘기도록 하는 조건을 트리거하지 않고 재개할 수 있다.설정한 타임아웃이 낮거나 일시적으로 처리될 수 있는 다른 종료의 에러로 인해 디플로이먼트에 일시적인 에러가 발생할 수 있다. 예를 들어, 할당량이 부족하다고 가정해보자. 만약 디플로이먼트를 설명하려면 다음 섹션을 확인한다.
kubectl describe deployment nginx-deployment
이와 유사하게 출력된다.
<...>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True ReplicaSetUpdated
ReplicaFailure True FailedCreate
<...>
만약 kubectl get deployment nginx-deployment -o yaml
을 실행하면 디플로이먼트 상태는 다음과 유사하다.
status:
availableReplicas: 2
conditions:
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: Replica set "nginx-deployment-4262182780" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
- lastTransitionTime: 2016-10-04T12:25:42Z
lastUpdateTime: 2016-10-04T12:25:42Z
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
object-counts, requested: pods=1, used: pods=3, limited: pods=2'
reason: FailedCreate
status: "True"
type: ReplicaFailure
observedGeneration: 3
replicas: 2
unavailableReplicas: 2
결국, 디플로이먼트 진행 데드라인을 넘어서면, 쿠버네티스는 진행 컨디션의 상태와 이유를 업데이트한다.
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
ReplicaFailure True FailedCreate
디플로이먼트를 스케일 다운하거나, 실행 중인 다른 컨트롤러를 스케일 다운하거나,
네임스페이스에서 할당량을 늘려서 할당량이 부족한 문제를 해결할 수 있다.
만약 할당량 컨디션과 디플로이먼트 롤아웃이 완료되어 디플로이먼트 컨트롤러를 만족한다면
성공한 컨디션의 디플로이먼트 상태가 업데이트를 볼 수 있다(status: "True"
와 reason: NewReplicaSetAvailable
).
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
type: Available
과 status: "True"
는 디플로이먼트가 최소한의 가용성을 가지고 있는 것을 의미한다.
최소한의 가용성은 디플로이먼트 계획에 명시된 파라미터에 의해 결정된다. type: Progressing
과 status: "True"
는 디플로이먼트가
롤아웃 도중에 진행 중 이거나, 성공적으로 완료되었으며, 진행 중 최소한으로 필요한 새로운 레플리카를 이용 가능하다는 것이다.
(자세한 내용은 특정 조건의 이유를 참조한다.
이 경우 reason: NewReplicaSetAvailable
는 배포가 완료되었음을 의미한다.)
kubectl rollout status
를 사용해서 디플로이먼트의 진행이 실패되었는지 확인할 수 있다.
kubectl rollout status
는 디플로이먼트의 진행 데드라인을 초과하면 0이 아닌 종료 코드를 반환한다.
kubectl rollout status deployment/nginx-deployment
이와 유사하게 출력된다.
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
그리고 kubectl rollout
의 종료 상태는 1(error를 의미함)이다.
echo $?
1
실패한 디플로이먼트에서의 운영
완료된 디플로이먼트에 적용되는 모든 행동은 실패한 디플로이먼트에도 적용된다. 디플로이먼트 파드 템플릿에서 여러 개의 수정사항을 적용해야하는 경우 스케일 업/다운 하거나, 이전 수정 버전으로 롤백하거나, 일시 중지할 수 있다.
정책 초기화
디플로이먼트의 .spec.revisionHistoryLimit
필드를 설정해서
디플로이먼트에서 유지해야 하는 이전 레플리카셋의 수를 명시할 수 있다. 나머지는 백그라운드에서 가비지-수집이 진행된다.
기본적으로 10으로 되어 있다.
참고:
명시적으로 이 필드를 0으로 설정하면 그 결과로 디플로이먼트의 기록을 전부 초기화를 하고, 디플로이먼트는 롤백할 수 없게 된다.카나리 디플로이먼트
만약 디플로이먼트를 이용해서 일부 사용자 또는 서버에 릴리스를 롤아웃 하기 위해서는 리소스 관리에 설명된 카나리 패던에 따라 각 릴리스 마다 하나씩 여러 디플로이먼트를 생성할 수 있다.
디플로이먼트 사양 작성
다른 모든 쿠버네티스 설정과 마찬가지로 디플로이먼트에는 .apiVersion
, .kind
그리고 .metadata
필드가 필요하다.
설정 파일 작업에 대한 일반적인 내용은
애플리케이션 배포하기,
컨테이너 구성하기 그리고 kubectl을 사용해서 리소스 관리하기 문서를 참조한다.
디플로이먼트 오브젝트의 이름은 유효한
DNS 서브도메인 이름이어야 한다.
디플로이먼트에는 .spec
섹션도 필요하다.
파드 템플릿
.spec.template
과 .spec.selector
은 .spec
에서 유일한 필수 필드이다.
.spec.template
는 파드 템플릿이다. 이것은 파드와 정확하게 동일한 스키마를 가지고 있고, 중첩된 것을 제외하면 apiVersion
과 kind
를 가지고 있지 않는다.
파드에 필요한 필드 외에 디플로이먼트 파드 템플릿은 적절한 레이블과 적절한 재시작 정책을 명시해야 한다. 레이블의 경우 다른 컨트롤러와 겹치지 않도록 해야 한다. 자세한 것은 셀렉터를 참조한다.
.spec.template.spec.restartPolicy
에는 오직 Always
만 허용되고,
명시되지 않으면 기본값이 된다.
레플리카
.spec.replicas
은 필요한 파드의 수를 지정하는 선택적 필드이다. 이것의 기본값은 1이다.
예를 들어 kubectl scale deployment deployment --replicas=X
명령으로
디플로이먼트의 크기를 수동으로 조정한 뒤,
매니페스트를 이용하여 디플로이먼트를 업데이트하면(예: kubectl apply -f deployment.yaml
실행),
수동으로 설정했던 디플로이먼트의 크기가 오버라이드된다.
HorizontalPodAutoscaler(또는 수평 스케일링을 위한 유사 API)가
디플로이먼트 크기를 관리하고 있다면, .spec.replicas
를 설정해서는 안 된다.
대신, 쿠버네티스
컨트롤 플레인이
.spec.replicas
필드를 자동으로 관리한다.
셀렉터
.spec.selector
는 디플로이먼트의 대상이 되는 파드에 대해 레이블 셀렉터를
지정하는 필수 필드이다.
.spec.selector
는 .spec.template.metadata.labels
과 일치해야 하며, 그렇지 않으면 API에 의해 거부된다.
API 버전 apps/v1
에서는 .spec.selector
와 .metadata.labels
이 설정되지 않으면 .spec.template.metadata.labels
은 기본 설정되지 않는다. 그래서 이것들은 명시적으로 설정되어야 한다. 또한 apps/v1
에서는 디플로이먼트를 생성한 후에는 .spec.selector
이 변경되지 않는 점을 참고한다.
디플로이먼트는 템플릿의 .spec.template
와 다르거나 파드의 수가 .spec.replicas
를 초과할 경우
셀렉터와 일치하는 레이블을 가진 파드를 종료할 수 있다.
파드의 수가 의도한 수량보다 적을 경우 .spec.template
에 맞는 새 파드를 띄운다.
참고:
다른 디플로이먼트를 생성하거나, 레플리카셋 또는 레플리케이션컨트롤러와 같은 다른 컨트롤러를 사용해서 직접적으로 레이블과 셀렉터가 일치하는 다른 파드를 생성하지 말아야 한다. 만약 이렇게 하면 첫 번째 디플로이먼트는 다른 파드를 만들었다고 생각한다. 쿠버네티스는 이 일을 막지 않는다.만약 셀렉터가 겹치는 컨트롤러가 어러 개 있는 경우, 컨트롤러는 서로 싸우고 올바르게 작동하지 않는다.
전략
.spec.strategy
는 이전 파드를 새로운 파드로 대체하는 전략을 명시한다.
.spec.strategy.type
은 "재생성" 또는 "롤링업데이트"가 될 수 있다.
"롤링업데이트"가 기본값이다.
디플로이먼트 재생성
기존의 모든 파드는 .spec.strategy.type==Recreate
이면 새 파드가 생성되기 전에 죽는다.
참고:
이렇게 하면 업그레이드를 생성하기 전에 파드 종료를 보장할 수 있다. 디플로이먼트를 업그레이드하면, 이전 버전의 모든 파드가 즉시 종료된다. 신규 버전의 파드가 생성되기 전에 성공적으로 제거가 완료되기를 대기한다. 파드를 수동으로 삭제하면, 라이프사이클은 레플리카셋에 의해 제어되며(이전 파드가 여전히 종료 상태에 있는 경우에도) 교체용 파드가 즉시 생성된다. 파드에 대해 "최대" 보장이 필요한 경우 스테이트풀셋의 사용을 고려해야 한다.디플로이먼트 롤링 업데이트
디플로이먼트는 .spec.strategy.type==RollingUpdate
이면 파드를 롤링 업데이트
방식으로 업데이트 한다. maxUnavailable
와 maxSurge
를 명시해서
롤링 업데이트 프로세스를 제어할 수 있다.
최대 불가(Max Unavailable)
.spec.strategy.rollingUpdate.maxUnavailable
은 업데이트 프로세스 중에 사용할 수 없는 최대 파드의 수를 지정하는 선택적 필드이다.
이 값은 절대 숫자(예: 5) 또는 의도한 파드 비율(예: 10%)이 될 수 있다.
절대 값은 내림해서 백분율로 계산한다.
만약 .spec.strategy.rollingUpdate.maxSurge
가 0이면 값이 0이 될 수 없다. 기본 값은 25% 이다.
예를 들어 이 값을 30%로 설정하면 롤링업데이트 시작시 즉각 이전 레플리카셋의 크기를 의도한 파드 중 70%를 스케일 다운할 수 있다. 새 파드가 준비되면 기존 레플리카셋을 스케일 다운할 수 있으며, 업데이트 중에 항상 사용 가능한 전체 파드의 수는 의도한 파드의 수의 70% 이상이 되도록 새 레플리카셋을 스케일 업할 수 있다.
최대 서지(Max Surge)
.spec.strategy.rollingUpdate.maxSurge
는 의도한 파드의 수에 대해 생성할 수 있는 최대 파드의 수를 지정하는 선택적 필드이다.
이 값은 절대 숫자(예: 5) 또는 의도한 파드 비율(예: 10%)이 될 수 있다.
MaxUnavailable
값이 0이면 이 값은 0이 될 수 없다.
절대 값은 올림해서 백분율로 계산한다. 기본 값은 25% 이다.
예를 들어 이 값을 30%로 설정하면 롤링업데이트 시작시 새 레플리카셋의 크기를 즉시 조정해서 기존 및 새 파드의 전체 갯수를 의도한 파드의 130%를 넘지 않도록 한다. 기존 파드가 죽으면 새로운 래플리카셋은 스케일 업할 수 있으며, 업데이트하는 동안 항상 실행하는 총 파드의 수는 최대 의도한 파드의 수의 130%가 되도록 보장한다.
진행 기한 시간(초)
.spec.progressDeadlineSeconds
는 디플로어먼트가 표면적으로 type: Progressing
, status: "False"
의
상태 그리고 리소스가 reason: ProgressDeadlineExceeded
상태로 진행 실패를 보고하기 전에
디플로이먼트가 진행되는 것을 대기시키는 시간(초)를 명시하는 선택적 필드이다.
디플로이먼트 컨트롤러는 디플로이먼트를 계속 재시도 한다. 기본값은 600(초)이다.
미래에 자동화된 롤백이 구현된다면 디플로이먼트 컨트롤러는 상태를 관찰하고,
그 즉시 디플로이먼트를 롤백할 것이다.
만약 명시된다면 이 필드는 .spec.minReadySeconds
보다 커야 한다.
최소 대기 시간(초)
.spec.minReadySeconds
는 새롭게 생성된 파드의 컨테이너가 어떤 것과도 충돌하지 않고 사
용할 수 있도록 준비되어야 하는 최소 시간(초)을 지정하는 선택적 필드이다.
이 기본 값은 0이다(파드는 준비되는 즉시 사용할 수 있는 것으로 간주됨).
파드가 준비되었다고 간주되는 시기에 대한 자세한 내용은 컨테이너 프로브를 참조한다.
수정 버전 기록 제한
디플로이먼트의 수정 버전 기록은 자신이 컨트롤하는 레플리카셋에 저장된다.
.spec.revisionHistoryLimit
은 롤백을 허용하기 위해 보존할 이전 레플리카셋의 수를 지정하는 선택적 필드이다.
이 이전 레플리카셋은 etcd
의 리소스를 소비하고, kubectl get rs
의 결과를 가득차게 만든다. 각 디플로이먼트의 구성은 디플로이먼트의 레플리카셋에 저장된다. 이전 레플리카셋이 삭제되면 해당 디플로이먼트 수정 버전으로 롤백할 수 있는 기능이 사라진다. 기본적으로 10개의 기존 레플리카셋이 유지되지만 이상적인 값은 새로운 디플로이먼트의 빈도와 안정성에 따라 달라진다.
더욱 구체적으로 이 필드를 0으로 설정하면 레플리카가 0이 되며 이전 레플리카셋이 정리된다. 이 경우, 새로운 디플로이먼트 롤아웃을 취소할 수 없다. 새로운 디플로이먼트 롤아웃은 수정 버전 이력이 정리되기 때문이다.
일시 정지
.spec.paused
는 디플로이먼트를 일시 중지나 재개하기 위한 선택적 부울 필드이다.
일시 중지 된 디플로이먼트와 일시 중지 되지 않은 디플로이먼트 사이의 유일한 차이점은
일시 중지된 디플로이먼트는 PodTemplateSpec에 대한 변경 사항이 일시중지 된 경우 새 롤아웃을 트리거 하지 않는다.
디플로이먼트는 생성시 기본적으로 일시 중지되지 않는다.
다음 내용
- 파드에 대해 배운다.
- 디플로이먼트를 사용해서 상태를 유지하지 않는 애플리케이션을 구동한다.
Deployment
는 쿠버네티스 REST API에서 상위-수준 리소스이다. 디플로이먼트 API를 이해하기 위해서 Deployment 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
2 - 레플리카셋
레플리카셋의 목적은 레플리카 파드 집합의 실행을 항상 안정적으로 유지하는 것이다. 이처럼 레플리카셋은 보통 명시된 동일 파드 개수에 대한 가용성을 보증하는데 사용한다.
레플리카셋의 작동 방식
레플리카셋을 정의하는 필드는 획득 가능한 파드를 식별하는 방법이 명시된 셀렉터, 유지해야 하는 파드 개수를 명시하는 레플리카의 개수, 그리고 레플리카 수 유지를 위해 생성하는 신규 파드에 대한 데이터를 명시하는 파드 템플릿을 포함한다. 그러면 레플리카셋은 필드에 지정된 설정을 충족하기 위해 필요한 만큼 파드를 만들고 삭제한다. 레플리카셋이 새로운 파드를 생성해야 할 경우, 명시된 파드 템플릿을 사용한다.
레플리카셋은 파드의 metadata.ownerReferences 필드를 통해 파드에 연결되며, 이는 현재 오브젝트가 소유한 리소스를 명시한다. 레플리카셋이 가지고 있는 모든 파드의 ownerReferences 필드는 해당 파드를 소유한 레플리카셋을 식별하기 위한 소유자 정보를 가진다. 이 링크를 통해 레플리카셋은 자신이 유지하는 파드의 상태를 확인하고 이에 따라 관리 한다.
레플리카셋은 셀렉터를 이용해서 필요한 새 파드를 식별한다. 만약 파드에 OwnerReference가 없거나, OwnerReference가 컨트롤러(Controller) 가 아니고 레플리카셋의 셀렉터와 일치한다면 레플리카셋이 즉각 파드를 가지게 될 것이다.
레플리카셋을 사용하는 시기
레플리카셋은 지정된 수의 파드 레플리카가 항상 실행되도록 보장한다. 그러나 디플로이먼트는 레플리카셋을 관리하고 다른 유용한 기능과 함께 파드에 대한 선언적 업데이트를 제공하는 상위 개념이다. 따라서 별도의 사용자 정의(custom) 업데이트 오케스트레이션이 필요한 경우 또는 업데이트가 전혀 필요 없는 경우가 아니라면, 레플리카셋을 직접 사용하기보다는 디플로이먼트를 사용하는 것을 권장한다.
이는 레플리카셋 오브젝트를 직접 조작할 필요가 없다는 것을 의미한다. 대신 디플로이먼트를 이용하고 사양 부분에서 애플리케이션을 정의하면 된다.
예시
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 케이스에 따라 레플리카를 수정한다.
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
이 매니페스트를 frontend.yaml
에 저장하고 쿠버네티스 클러스터에 적용하면 정의되어 있는 레플리카셋이
생성되고 레플리카셋이 관리하는 파드가 생성된다.
kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml
현재 배포된 레플리카셋을 확인할 수 있다.
kubectl get rs
그리고 생성된 프런트엔드를 볼 수 있다.
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 6s
또한 레플리카셋의 상태를 확인할 수 있다.
kubectl describe rs/frontend
출력은 다음과 유사할 것이다.
Name: frontend
Namespace: default
Selector: tier=frontend
Labels: app=guestbook
tier=frontend
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: tier=frontend
Containers:
php-redis:
Image: gcr.io/google_samples/gb-frontend:v3
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 117s replicaset-controller Created pod: frontend-wtsmm
Normal SuccessfulCreate 116s replicaset-controller Created pod: frontend-b2zdv
Normal SuccessfulCreate 116s replicaset-controller Created pod: frontend-vcmts
마지막으로 파드가 올라왔는지 확인할 수 있다.
kubectl get pods
다음과 유사한 파드 정보를 볼 수 있다.
NAME READY STATUS RESTARTS AGE
frontend-b2zdv 1/1 Running 0 6m36s
frontend-vcmts 1/1 Running 0 6m36s
frontend-wtsmm 1/1 Running 0 6m36s
또한 파드들의 소유자 참조 정보가 해당 프런트엔드 레플리카셋으로 설정되어 있는지 확인할 수 있다. 확인을 위해서는 실행 중인 파드 중 하나의 yaml을 확인한다.
kubectl get pods frontend-b2zdv -o yaml
메타데이터의 ownerReferences 필드에 설정되어 있는 프런트엔드 레플리카셋의 정보가 다음과 유사하게 나오는 것을 볼 수 있다.
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-02-12T07:06:16Z"
generateName: frontend-
labels:
tier: frontend
name: frontend-b2zdv
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: frontend
uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...
템플릿을 사용하지 않는 파드의 획득
단독(bare) 파드를 생성하는 것에는 문제가 없지만, 단독 파드가 레플리카셋의 셀렉터와 일치하는 레이블을 가지지 않도록 하는 것을 강력하게 권장한다. 그 이유는 레플리카셋이 소유하는 파드가 템플릿에 명시된 파드에만 국한되지 않고, 이전 섹션에서 명시된 방식에 의해서도 다른 파드의 획득이 가능하기 때문이다.
이전 프런트엔드 레플리카셋 예제와 다음의 매니페스트에 명시된 파드를 가져와 참조한다.
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: gcr.io/google-samples/hello-app:2.0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: gcr.io/google-samples/hello-app:1.0
기본 파드는 소유자 관련 정보에 컨트롤러(또는 오브젝트)를 가지지 않기 때문에 프런트엔드 레플리카셋의 셀렉터와 일치하면 즉시 레플리카셋에 소유된다.
프런트엔드 레플리카셋이 배치되고 초기 파드 레플리카가 셋업된 이후에, 레플리카 수 요구 사항을 충족시키기 위해서 신규 파드를 생성한다고 가정해보자.
kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml
새로운 파드는 레플리카셋에 의해 인식되며 레플리카셋이 필요한 수량을 초과하면 즉시 종료된다.
파드를 가져온다.
kubectl get pods
결과에는 새로운 파드가 이미 종료되었거나 종료가 진행 중인 것을 보여준다.
NAME READY STATUS RESTARTS AGE
frontend-b2zdv 1/1 Running 0 10m
frontend-vcmts 1/1 Running 0 10m
frontend-wtsmm 1/1 Running 0 10m
pod1 0/1 Terminating 0 1s
pod2 0/1 Terminating 0 1s
파드를 먼저 생성한다.
kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml
그 다음 레플리카셋을 생성한다.
kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml
레플리카셋이 해당 파드를 소유한 것을 볼 수 있으며 새 파드 및 기존 파드의 수가 레플리카셋이 필요로 하는 수와 일치할 때까지 사양에 따라 신규 파드만 생성한다. 파드를 가져온다.
kubectl get pods
다음 출력에서 볼 수 있다.
NAME READY STATUS RESTARTS AGE
frontend-hmmj2 1/1 Running 0 9s
pod1 1/1 Running 0 36s
pod2 1/1 Running 0 36s
이러한 방식으로 레플리카셋은 템플릿을 사용하지 않는 파드를 소유하게 된다.
레플리카셋 매니페스트 작성하기
레플리카셋은 모든 쿠버네티스 API 오브젝트와 마찬가지로 apiVersion
, kind
, metadata
필드가 필요하다.
레플리카셋에 대한 kind
필드의 값은 항상 레플리카셋이다.
레플리카셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
레플리카셋도 .spec
섹션이 필요하다.
파드 템플릿
.spec.template
은 레이블을 붙이도록 되어 있는 파드 템플릿이다.
우리는 frontend.yaml
예제에서 tier: frontend
이라는 레이블을 하나 가지고 있다.
이 파드를 다른 컨트롤러가 취하지 않도록 다른 컨트롤러의 셀렉터와 겹치지 않도록 주의해야 한다.
템플릿의 재시작 정책 필드인
.spec.template.spec.restartPolicy
는 기본값인 Always
만 허용된다.
파드 셀렉터
.spec.selector
필드는 레이블 셀렉터이다.
앞서 논의한 것처럼 이 레이블은 소유될 가능성이 있는 파드를 식별하는데 사용된다.
우리 frontend.yaml
예제에서의 셀렉터는 다음과 같다.
matchLabels:
tier: frontend
레플리카셋에서 .spec.template.metadata.labels
는 spec.selector
과 일치해야 하며
그렇지 않으면 API에 의해 거부된다.
참고:
2개의 레플리카셋이 동일한.spec.selector
필드를 지정한 반면, 다른
.spec.template.metadata.labels
와 .spec.template.spec
필드를 명시한 경우, 각 레플리카셋은
다른 레플리카셋이 생성한 파드를 무시한다.레플리카
.spec.replicas
를 설정해서 동시에 동작하는 파드의 수를 지정할 수 있다.
레플리카셋은 파드의 수가 일치하도록 생성 및 삭제한다.
만약 .spec.replicas
를 지정하지 않으면 기본값은 1이다.
레플리카셋 작업
레플리카셋과 해당 파드 삭제
레플리카셋 및 모든 파드를 삭제하려면
kubectl delete
를 사용한다.
가비지 수집기는
기본적으로 종속되어 있는 모든 파드를 자동으로 삭제한다.
REST API 또는 client-go
라이브러리를 이용할 때는 -d 옵션으로 propagationPolicy
를
Background
또는 Foreground
로 설정해야 한다. 예시:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"
레플리카셋만 삭제하기
kubectl delete
에
--cascade=orphan
옵션을 사용하여
연관 파드에 영향을 주지 않고 레플리카셋을 삭제할 수 있다.
REST API 또는 client-go
라이브러리를 이용할 때는 propagationPolicy
에 Orphan
을 설정해야 한다.
예시:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"
원본이 삭제되면 새 레플리카셋을 생성해서 대체할 수 있다.
기존 .spec.selector
와 신규 .spec.selector
가 같으면 새 레플리카셋은 기존 파드를 선택한다.
하지만 신규 레플리카셋은 기존 파드를 신규 레플리카셋의 새롭고 다른 파드 템플릿에 일치시키는 작업을 수행하지는 않는다.
컨트롤 방식으로 파드를 새로운 사양으로 업데이트 하기 위해서는
디플로이먼트를 이용하면 된다.
이는 레플리카셋이 롤링 업데이트를 직접적으로 지원하지 않기 때문이다.
레플리카셋에서 파드 격리
레이블을 변경하면 레플리카셋에서 파드를 제거할 수 있다. 이 방식은 디버깅과 데이터 복구 등을 위해 서비스에서 파드를 제거하는 데 사용할 수 있다. 이 방식으로 제거된 파드는 자동으로 교체된다( 레플리카의 수가 변경되지 않는다고 가정한다).
레플리카셋의 스케일링
레플리카셋을 손쉽게 스케일 업 또는 다운하는 방법은 단순히 .spec.replicas
필드를 업데이트하면 된다.
레플리카셋 컨트롤러는 일치하는 레이블 셀렉터가 있는 파드가 의도한 수 만큼 가용하고 운영 가능하도록 보장한다.
스케일 다운할 때, 레플리카셋 컨트롤러는 스케일 다운할 파드의 우선순위를 정하기 위해 다음의 기준으로 가용 파드를 정렬하여 삭제할 파드를 결정한다.
- Pending 상태인 (+ 스케줄링할 수 없는) 파드가 먼저 스케일 다운된다.
controller.kubernetes.io/pod-deletion-cost
어노테이션이 설정되어 있는 파드에 대해서는, 낮은 값을 갖는 파드가 먼저 스케일 다운된다.- 더 많은 레플리카가 있는 노드의 파드가 더 적은 레플리카가 있는 노드의 파드보다 먼저 스케일 다운된다.
- 파드 생성 시간이 다르면, 더 최근에 생성된 파드가
이전에 생성된 파드보다 먼저 스케일 다운된다.
(
LogarithmicScaleDown
기능 게이트가 활성화되어 있으면 생성 시간이 정수 로그 스케일로 버킷화된다)
모든 기준에 대해 동등하다면, 스케일 다운할 파드가 임의로 선택된다.
파드 삭제 비용
Kubernetes v1.22 [beta]
controller.kubernetes.io/pod-deletion-cost
어노테이션을 이용하여,
레플리카셋을 스케일 다운할 때 어떤 파드부터 먼저 삭제할지에 대한 우선순위를 설정할 수 있다.
이 어노테이션은 파드에 설정되어야 하며, [-2147483647, 2147483647] 범위를 갖는다. 이 어노테이션은 하나의 레플리카셋에 있는 다른 파드와의 상대적 삭제 비용을 나타낸다. 삭제 비용이 낮은 파드는 삭제 비용이 높은 파드보다 삭제 우선순위가 높다.
파드에 대해 이 값을 명시하지 않으면 기본값은 0이다. 음수로도 설정할 수 있다. 유효하지 않은 값은 API 서버가 거부한다.
이 기능은 베타 상태이며 기본적으로 활성화되어 있다.
kube-apiserver와 kube-controller-manager에 대해 PodDeletionCost
기능 게이트를 이용하여 비활성화할 수 있다.
참고:
- 이 기능은 best-effort 방식으로 동작하므로, 파드 삭제 순서를 보장하지는 않는다.
- 이 값을 자주 바꾸는 것은 피해야 한다 (예: 메트릭 값에 따라 변경). apiserver에서 많은 양의 파드 업데이트를 동반하기 때문이다.
사용 예시
한 애플리케이션 내의 여러 파드는 각각 사용률이 다를 수 있다. 스케일 다운 시,
애플리케이션은 사용률이 낮은 파드를 먼저 삭제하고 싶을 수 있다. 파드를 자주
업데이트하는 것을 피하기 위해, 애플리케이션은 controller.kubernetes.io/pod-deletion-cost
값을
스케일 다운하기 전에 1회만 업데이트해야 한다 (파드 사용률에 비례하는 값으로 설정).
이 방식은 Spark 애플리케이션의 드라이버 파드처럼 애플리케이션이 스스로 다운스케일링을 수행하는 경우에 유효하다.
레플리카셋을 Horizontal Pod Autoscaler 대상으로 설정
레플리카셋은 Horizontal Pod Autoscalers (HPA)의 대상이 될 수 있다. 즉, 레플리카셋은 HPA에 의해 오토스케일될 수 있다. 다음은 이전에 만든 예시에서 만든 레플리카셋을 대상으로 하는 HPA 예시이다.
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
이 매니페스트를 hpa-rs.yaml
로 저장한 다음 쿠버네티스
클러스터에 적용하면 CPU 사용량에 따라 파드가 복제되는
오토스케일 레플리카셋 HPA가 생성된다.
kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml
또는 kubectl autoscale
커맨드을 사용해서 동일한 작업을 할 수 있다.
(그리고 더 쉽다!)
kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50
레플리카셋의 대안
디플로이먼트(권장)
디플로이먼트
는 레플리카셋을 소유하거나 업데이트를 하고,
파드의 선언적인 업데이트와 서버측 롤링 업데이트를 할 수 있는 오브젝트이다.
레플리카셋은 단독으로 사용할 수 있지만, 오늘날에는 주로 디플로이먼트로 파드의 생성과 삭제 그리고 업데이트를 오케스트레이션하는 메커니즘으로 사용한다.
디플로이먼트를 이용해서 배포할 때 생성되는 레플리카셋을 관리하는 것에 대해 걱정하지 않아도 된다.
디플로이먼트는 레플리카셋을 소유하거나 관리한다.
따라서 레플리카셋을 원한다면 디플로이먼트를 사용하는 것을 권장한다.
기본 파드
사용자가 직접 파드를 생성하는 경우와는 다르게, 레플리카셋은 노드 장애 또는 노드의 커널 업그레이드와 같은 관리 목적의 중단 등 어떤 이유로든 종료되거나 삭제된 파드를 교체한다. 이런 이유로 애플리케이션이 단일 파드가 필요하더라도 레플리카셋을 이용하는 것을 권장한다. 레플리카셋을 프로세스 관리자와 비교해서 생각해본다면, 레플리카셋은 단일 노드에서의 개별 프로세스들이 아닌 다수의 노드에 걸쳐있는 다수의 파드를 관리하는 것이다. 레플리카셋은 로컬 컨테이너의 재시작을 노드에 있는 Kubelet과 같은 에이전트에게 위임한다.
잡
스스로 종료되는 것이 예상되는 파드의 경우에는 레플리카셋 대신
잡
을 이용한다 (즉, 배치 잡).
데몬셋
머신 모니터링 또는 머신 로깅과 같은 머신-레벨의 기능을 제공하는 파드를 위해서는 레플리카셋 대신
데몬셋
을 사용한다.
이러한 파드의 수명은 머신의 수명과 연관되어 있고, 머신에서 다른 파드가 시작하기 전에 실행되어야 하며,
머신의 재부팅/종료가 준비되었을 때, 해당 파드를 종료하는 것이 안전하다.
레플리케이션 컨트롤러
레플리카셋은 레플리케이션 컨트롤러를 계승하였다. 이 두 개의 용도는 동일하고, 유사하게 동작하며, 레플리케이션 컨트롤러가 레이블 사용자 가이드에 설명된 설정-기반의 셀렉터의 요건을 지원하지 않는다는 점을 제외하면 유사하다. 따라서 레플리카셋이 레플리케이션 컨트롤러보다 선호된다.
다음 내용
- 파드에 대해 배운다.
- 디플로이먼트에 대해 배운다.
- 레플리카셋에 의존해서 동작하는 디플로이먼트로 스테이트리스 애플리케이션을 실행한다.
ReplicaSet
는 쿠버네티스 REST API의 상위-수준 리소스이다. 레플리카셋 API에 대해 이해하기 위해 ReplicaSet 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
3 - 스테이트풀셋
스테이트풀셋은 애플리케이션의 스테이트풀을 관리하는데 사용하는 워크로드 API 오브젝트이다.
파드 집합의 디플로이먼트와 스케일링을 관리하며, 파드들의 순서 및 고유성을 보장한다 .
디플로이먼트와 유사하게, 스테이트풀셋은 동일한 컨테이너 스펙을 기반으로 둔 파드들을 관리한다. 디플로이먼트와는 다르게, 스테이트풀셋은 각 파드의 독자성을 유지한다. 이 파드들은 동일한 스팩으로 생성되었지만, 서로 교체는 불가능하다. 다시 말해, 각각은 재스케줄링 간에도 지속적으로 유지되는 식별자를 가진다.
스토리지 볼륨을 사용해서 워크로드에 지속성을 제공하려는 경우, 솔루션의 일부로 스테이트풀셋을 사용할 수 있다. 스테이트풀셋의 개별 파드는 장애에 취약하지만, 퍼시스턴트 파드 식별자는 기존 볼륨을 실패한 볼륨을 대체하는 새 파드에 더 쉽게 일치시킬 수 있다.
스테이트풀셋 사용
스테이트풀셋은 다음 중 하나 또는 이상이 필요한 애플리케이션에 유용하다.
- 안정된, 고유한 네트워크 식별자.
- 안정된, 지속성을 갖는 스토리지.
- 순차적인, 정상 배포(graceful deployment)와 스케일링.
- 순차적인, 자동 롤링 업데이트.
위의 안정은 파드의 (재)스케줄링 전반에 걸친 지속성과 같은 의미이다. 만약 애플리케이션이 안정적인 식별자 또는 순차적인 배포, 삭제 또는 스케일링이 필요하지 않으면, 스테이트리스 레플리카셋(ReplicaSet)을 제공하는 워크로드 오브젝트를 사용해서 애플리케이션을 배포해야 한다. 디플로이먼트 또는 레플리카셋과 같은 컨트롤러가 스테이트리스 요구에 더 적합할 수 있다.
제한사항
- 파드에 지정된 스토리지는 관리자에 의해
퍼시스턴트 볼륨 프로비저너
를 기반으로 하는
storage class
를 요청해서 프로비전하거나 사전에 프로비전이 되어야 한다. - 스테이트풀셋을 삭제 또는 스케일 다운해도 스테이트풀셋과 연관된 볼륨이 삭제되지 않는다. 이는 일반적으로 스테이트풀셋과 연관된 모든 리소스를 자동으로 제거하는 것보다 더 중요한 데이터의 안전을 보장하기 위함이다.
- 스테이트풀셋은 현재 파드의 네트워크 신원을 책임지고 있는 헤드리스 서비스 가 필요하다. 사용자가 이 서비스를 생성할 책임이 있다.
- 스테이트풀셋은 스테이트풀셋의 삭제 시 파드의 종료에 대해 어떠한 보증을 제공하지 않는다. 스테이트풀셋에서는 파드가 순차적이고 정상적으로 종료(graceful termination)되도록 하려면, 삭제 전 스테이트풀셋의 스케일을 0으로 축소할 수 있다.
- 롤링 업데이트와 기본
파드 매니지먼트 폴리시 (
OrderedReady
)를 함께 사용시 복구를 위한 수동 개입이 필요한 파손 상태로 빠질 수 있다.
구성 요소
아래의 예시에서는 스테이트풀셋의 구성요소를 보여 준다.
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # .spec.template.metadata.labels 와 일치해야 한다
serviceName: "nginx"
replicas: 3 # 기본값은 1
minReadySeconds: 10 # 기본값은 0
template:
metadata:
labels:
app: nginx # .spec.selector.matchLabels 와 일치해야 한다
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
위의 예시에서:
- 이름이 nginx라는 헤드리스 서비스는 네트워크 도메인을 컨트롤하는데 사용 한다.
- 이름이 web인 스테이트풀셋은 3개의 nginx 컨테이너의 레플리카가 고유의 파드에서 구동될 것이라 지시하는 Spec을 갖는다.
- volumeClaimTemplates은 퍼시스턴트 볼륨 프로비저너에서 프로비전한 퍼시스턴트 볼륨을 사용해서 안정적인 스토리지를 제공한다.
스테이트풀셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
파드 셀렉터
스테이트풀셋의 .spec.selector
필드는
.spec.template.metadata.labels
레이블과 일치하도록 설정해야 한다. 해당되는 파드 셀렉터를 찾지 못하면
스테이트풀셋 생성 과정에서 검증 오류가 발생한다.
볼륨 클레임 템플릿
.spec.volumeClaimTemplates
를 설정하여, 퍼시스턴트볼륨 프로비저너에 의해 프로비전된
퍼시스턴트볼륨을 이용하는 안정적인 스토리지를
제공할 수 있다.
최소 준비 시간 초
Kubernetes v1.25 [stable]
.spec.minReadySeconds
는 파드가 '사용 가능(available)'이라고 간주될 수 있도록 파드의 모든 컨테이너가
문제 없이 실행되고 준비되는 최소 시간(초)을 나타내는 선택적인 필드이다.
롤링 업데이트 전략을 사용할 때 롤아웃 진행 상황을 확인하는 데 사용된다.
이 필드의 기본값은 0이다(이 경우, 파드가 Ready 상태가 되면 바로 사용 가능하다고 간주된다.)
파드가 언제 사용 가능하다고 간주되는지에 대한 자세한 정보는 컨테이너 프로브(probe)를 참고한다.
파드 신원
스테이트풀셋 파드는 순서, 안정적인 네트워크 신원 그리고 안정적인 스토리지로 구성되는 고유한 신원을 가진다. 신원은 파드가 어떤 노드에 있고, (재)스케줄과도 상관없이 파드에 붙어있다.
순서 색인
N개의 레플리카가 있는 스테이트풀셋은 스테이트풀셋에 있는 각 파드에 0에서 N-1 까지의 정수가 순서대로 할당되며 해당 스테이트풀셋 내에서 고유 하다. 기본적으로 파드는 0부터 N-1까지의 순서대로 할당된다.
시작 순서
Kubernetes v1.26 [alpha]
.spec.ordinals
은 각 파드에 할당할 순서에 대한
정수값을 설정할 수 있게 해주는 선택적인 필드로, 기본값은 nil이다.
이 필드를 사용하기 위해서는
StatefulSetStartOrdinal
기능 게이트를 활성화해야 한다.
활성화 시, 다음과 같은 옵션들을 설정할 수 있다.
.spec.ordinals.start
: 만약.spec.ordinals.start
필드가 세팅되지 않을 경우, 파드는.spec.ordinals.start
부터.spec.ordinals.start + .spec.replicas - 1
의 순서대로 할당된다.
안정적인 네트워크 신원
스테이트풀셋의 각 파드는 스테이트풀셋의 이름과 파드의 순번에서
호스트 이름을 얻는다. 호스트 이름을 구성하는 패턴은
$(statefulset name)-$(ordinal)
이다. 위의 예시에서 생성된 3개 파드의 이름은
web-0,web-1,web-2
이다.
스테이트풀셋은 스테이트풀셋에 있는 파드의 도메인을 제어하기위해
헤드리스 서비스를 사용할 수 있다.
이 서비스가 관리하는 도메인은 $(service name).$(namespace).svc.cluster.local
의 형식을 가지며,
여기서 "cluster.local"은 클러스터 도메인이다.
각 파드는 생성되면 $(podname).$(governing service domain)
형식을 가지고
일치되는 DNS 서브도메인을 가지며, 여기서 거버닝 서비스(governing service)는
스테이트풀셋의 serviceName
필드에 의해 정의된다.
클러스터에서 DNS가 구성된 방식에 따라, 새로 실행된 파드의 DNS 이름을 즉시 찾지 못할 수 있다. 이 동작은 클러스터의 다른 클라이언트가 파드가 생성되기 전에 파드의 호스트 이름에 대한 쿼리를 이미 보낸 경우에 발생할 수 있다. 네거티브 캐싱(DNS에서 일반적)은 이전에 실패한 조회 결과가 파드가 실행된 후에도 적어도 몇 초 동안 기억되고 재사용됨을 의미한다.
파드를 생성한 후 즉시 파드를 검색해야 하는 경우, 몇 가지 옵션이 있다.
- DNS 조회에 의존하지 않고 쿠버네티스 API를 직접(예를 들어 watch 사용) 쿼리한다.
- 쿠버네티스 DNS 공급자의 캐싱 시간(일반적으로 CoreDNS의 컨피그맵을 편집하는 것을 의미하며, 현재 30초 동안 캐시함)을 줄인다.
제한사항 섹션에서 언급한 것처럼 사용자는 파드의 네트워크 신원을 책임지는 헤드리스 서비스를 생성할 책임이 있다.
여기 클러스터 도메인, 서비스 이름, 스테이트풀셋 이름을 선택을 하고, 그 선택이 스테이트풀셋 파드의 DNS이름에 어떻게 영향을 주는지에 대한 약간의 예시가 있다.
클러스터 도메인 | 서비스 (ns/이름) | 스테이트풀셋 (ns/이름) | 스테이트풀셋 도메인 | 파드 DNS | 파드 호스트 이름 |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
안정된 스토리지
스테이트풀셋에 정의된 VolumeClaimTemplate 항목마다, 각 파드는 하나의
PersistentVolumeClaim을 받는다. 위의 nginx 예시에서 각 파드는 my-storage-class
라는 스토리지 클래스와
1 Gib의 프로비전된 스토리지를 가지는 단일 퍼시스턴트 볼륨을 받게 된다. 만약 스토리지 클래스가
명시되지 않은 경우, 기본 스토리지 클래스가 사용된다. 파드가 노드에서 스케줄 혹은 재스케줄이 되면
파드의 volumeMounts
는 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨이 마운트 된다.
참고로, 파드 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨은
파드 또는 스테이트풀셋이 삭제되더라도 삭제되지 않는다.
이것은 반드시 수동으로 해야 한다.
파드 이름 레이블
스테이트풀셋 컨트롤러
가 파드를 생성할 때 파드 이름으로 statefulset.kubernetes.io/pod-name
레이블이 추가된다. 이 레이블로 스테이트풀셋의 특정 파드에 서비스를
연결할 수 있다.
디플로이먼트와 스케일링 보증
- N개의 레플리카가 있는 스테이트풀셋이 파드를 배포할 때 연속해서 {0..N-1}의 순서로 생성한다.
- 파드가 삭제될 때는 {N-1..0}의 순서인 역순으로 종료된다.
- 파드에 스케일링 작업을 적용하기 전에 모든 선행 파드가 Running 및 Ready 상태여야 한다.
- 파드가 종료되기 전에 모든 후속 파드가 완전히 종료 되어야 한다.
스테이트풀셋은 pod.Spec.TerminationGracePeriodSeconds
을 0으로 명시해서는 안된다. 이 방법은
안전하지 않으며, 사용하지 않기를 강권한다. 자세한 설명은
스테이트풀셋 파드 강제 삭제를 참고한다.
위의 nginx 예시가 생성될 때 web-0, web-1, web-2 순서로 3개 파드가 배포된다. web-1은 web-0이 Running 및 Ready 상태가 되기 전에는 배포되지 않으며, web-2 도 web-1이 Running 및 Ready 상태가 되기 전에는 배포되지 않는다. 만약 web-1이 Running 및 Ready 상태가 된 이후, web-2가 시작되기 전에 web-0이 실패하게 된다면, web-2는 web-0이 성공적으로 재시작이되고, Running 및 Ready 상태가 되기 전까지 시작되지 않는다.
만약 사용자가 배포된 예제의 스테이트풀셋을 replicas=1
으로 패치해서
스케일한 경우 web-2가 먼저 종료된다. web-1은 web-2가 완전히 종료 및 삭제되기
전까지 정지되지 않는다. 만약 web-2의 종료 및 완전히 중지되고, web-1이 종료되기 전에
web-0이 실패할 경우 web-1은 web-0이 Running 및 Ready 상태가
되기 전까지 종료되지 않는다.
파드 관리 정책
스테이트풀셋의 .spec.podManagementPolicy
필드를 통해
고유성 및 신원 보증을 유지하면서 순차 보증을 완화한다.
OrderedReady 파드 관리
OrderedReady
파드 관리는 스테이트풀셋의 기본이다.
이것은 위에서 설명한 행위를 구현한다.
Parallel 파드 관리
Parallel
파드 관리는 스테이트풀셋 컨트롤러에게 모든 파드를
병렬로 실행 또는 종료하게 한다. 그리고 다른 파드의 실행이나
종료에 앞서 파드가 Running 및 Ready 상태가 되거나 완전히 종료되기를 기다리지 않는다.
이 옵션은 오직 스케일링 작업에 대한 동작에만 영향을 미친다. 업데이트는 영향을
받지 않는다.
업데이트 전략
스테이트풀셋의 .spec.updateStrategy
필드는 스테이트풀셋의
파드에 대한 컨테이너, 레이블, 리소스의 요청/제한 그리고 주석에 대한 자동화된 롤링 업데이트를
구성하거나 비활성화할 수 있다. 두 가지 가능한 전략이 있다.
OnDelete
(삭제시)- 스테이트풀셋의
.spec.updateStrategy.type
은OnDelete
를 설정하며, 스테이트풀셋 컨트롤러는 스테이트풀셋의 파드를 자동으로 업데이트하지 않는다. 사용자는 컨트롤러가 스테이트풀셋의.spec.template
를 반영하는 수정된 새로운 파드를 생성하도록 수동으로 파드를 삭제해야 한다. RollingUpdate
(롤링 업데이트)RollingUpdate
업데이트 전략은 스테이트풀셋의 파드에 대한 롤링 업데이트를 구현한다. 기본 업데이트 전략이다.
롤링 업데이트
스테이트풀셋에 롤링 업데이트
가 .spec.updateStrategy.type
에 설정되면
스테이트풀셋 컨트롤러는 스테이트풀셋의 각 파드를 삭제 및 재생성한다. 이 과정에서 똑같이
순차적으로 파드가 종료되고(가장 큰 순서 색인에서부터에서 작은 순서 색인쪽으로),
각 파드의 업데이트는 한 번에 하나씩 한다.
쿠버네티스 컨트롤 플레인은 이전 버전을 업데이트 하기 전에, 업데이트된 파드가 실행 및 준비될 때까지 기다린다.
.spec.minReadySeconds
(최소 준비 시간 초 참조)를
설정한 경우,
컨트롤 플레인은 파드가 준비 상태로 전환된 후 해당 시간을 추가로 기다린 후 이동한다.
파티션 롤링 업데이트
롤링 업데이트
의 업데이트 전략은 .spec.updateStrategy.rollingUpdate.partition
를 명시해서 파티션 할 수 있다. 만약 파티션을 명시하면 스테이트풀셋의 .spec.template
가
업데이트 될 때 부여된 수가 파티션보다 크거나 같은 모든 파드가 업데이트 된다.
파티션보다 작은 수를 가진 모든 파드는 업데이트 되지 않으며,
삭제 된 경우라도 이전 버전에서 재생성된다.
만약 스테이트풀셋의 .spec.updateStrategy.rollingUpdate.partition
이
.spec.replicas
보다 큰 경우 .spec.template
의 업데이트는 해당 파드에 전달하지 않는다.
대부분의 케이스는 파티션을 사용할 필요가 없지만 업데이트를 준비하거나,
카나리의 롤 아웃 또는 단계적인 롤 아웃을 행하려는 경우에는 유용하다.
최대 사용 불가능(unavailable) 파드 수
Kubernetes v1.24 [alpha]
.spec.updateStrategy.rollingUpdate.maxUnavailable
필드를 명시하여,
업데이트 과정에서 사용 불가능(unavailable) 파드를 최대 몇 개까지 허용할 것인지를 조절할 수 있다.
값은 절대값(예: 5
) 또는 목표 파드 퍼센티지(예: 10%
)로 명시할 수 있다.
절대값은 퍼센티지 값으로 계산한 뒤 올림하여 얻는다.
이 필드는 0일 수 없다. 기본값은 1이다.
이 필드는 0
에서 replicas - 1
사이 범위에 있는 모든 파드에 적용된다.
이 범위 내에 사용 불가능한 파드가 있으면,
maxUnavailable
로 집계된다.
강제 롤백
기본 파드 관리 정책 (OrderedReady
)과
함께 롤링 업데이트를 사용할 경우
직접 수동으로 복구를 해야하는 고장난 상태가 될 수 있다.
만약 파드 템플릿을 Running 및 Ready 상태가 되지 않는 구성으로 업데이트하는 경우(예시: 잘못된 바이너리 또는 애플리케이션-레벨 구성 오류로 인한) 스테이트풀셋은 롤아웃을 중지하고 기다린다.
이 상태에서는 파드 템플릿을 올바른 구성으로 되돌리는 것으로 충분하지 않다. 알려진 이슈로 인해 스테이트풀셋은 손상된 파드가 준비(절대 되지 않음)될 때까지 기다리며 작동하는 구성으로 되돌아가는 시도를 하기 전까지 기다린다.
템플릿을 되돌린 이후에는 스테이트풀셋이 이미 잘못된 구성으로 실행하려고 시도한 모든 파드를 삭제해야 한다. 그러면 스테이트풀셋은 되돌린 템플릿을 사용해서 파드를 다시 생성하기 시작한다.
퍼시스턴트볼륨클레임 유보
Kubernetes v1.23 [alpha]
선택적 필드인 .spec.persistentVolumeClaimRetentionPolicy
는
스테이트풀셋의 생애주기동안 PVC를 삭제할 것인지,
삭제한다면 어떻게 삭제하는지를 관리한다.
이 필드를 사용하려면 API 서버와 컨트롤러 매니저에 StatefulSetAutoDeletePVC
기능 게이트를 활성화해야 한다.
활성화 시, 각 스테이트풀셋에 대해 두 가지 정책을 설정할 수 있다.
whenDeleted
- 스테이트풀셋이 삭제될 때 적용될 볼륨 유보 동작을 설정한다.
whenScaled
- 스테이트풀셋의 레플리카 수가 줄어들 때, 예를 들면 스테이트풀셋을 스케일 다운할 때 적용될 볼륨 유보 동작을 설정한다.
설정 가능한 각 정책에 대해, 그 값을 Delete
또는 Retain
으로 설정할 수 있다.
Delete
volumeClaimTemplate
스테이트풀셋으로부터 생성된 PVC는 정책에 영향을 받는 각 파드에 대해 삭제된다.whenDeleted
가 이 값으로 설정되어 있으면volumeClaimTemplate
으로부터 생성된 모든 PVC는 파드가 삭제된 뒤에 삭제된다.whenScaled
가 이 값으로 설정되어 있으면 스케일 다운된 파드 레플리카가 삭제된 뒤, 삭제된 파드에 해당되는 PVC만 삭제된다.Retain
(기본값)- 파드가 삭제되어도
volumeClaimTemplate
으로부터 생성된 PVC는 영향을 받지 않는다. 이는 이 신기능이 도입되기 전의 기본 동작이다.
이러한 정책은 파드의 삭제가 스테이트풀셋 삭제 또는 스케일 다운으로 인한 것일 때에만 적용됨에 유의한다. 예를 들어, 스테이트풀셋의 파드가 노드 실패로 인해 실패했고, 컨트롤 플레인이 대체 파드를 생성했다면, 스테이트풀셋은 기존 PVC를 유지한다. 기존 볼륨은 영향을 받지 않으며, 새 파드가 실행될 노드에 클러스터가 볼륨을 연결(attach)한다.
정책의 기본값은 Retain
이며, 이는 이 신기능이 도입되기 전의 스테이트풀셋 기본 동작이다.
다음은 정책 예시이다.
apiVersion: apps/v1
kind: StatefulSet
...
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Delete
...
스테이트풀셋 컨트롤러는 자신이 소유한 PVC에
소유자 정보(reference)를
추가하며, 파드가 종료된 이후에는 가비지 콜렉터가 이 정보를 삭제한다.
이로 인해 PVC가 삭제되기 전에
(그리고 유보 정책에 따라, 매칭되는 기반 PV와 볼륨이 삭제되기 전에)
파드가 모든 볼륨을 깨끗하게 언마운트할 수 있다.
whenDeleted
정책을 Delete
로 설정하면,
해당 스테이트풀셋에 연결된 모든 PVC에 스테이트풀셋 인스턴스의 소유자 정보가 기록된다.
whenScaled
정책은 파드가 스케일 다운되었을 때에만 PVC를 삭제하며,
파드가 다른 원인으로 삭제되면 PVC를 삭제하지 않는다.
조정 상황 발생 시, 스테이트풀셋 컨트롤러는 목표 레플리카 수와 클러스터 상의 실제 파드 수를 비교한다.
레플리카 카운트보다 큰 ID를 갖는 스테이트풀셋 파드는 부적격 판정을 받으며 삭제 대상으로 표시된다.
whenScaled
정책이 Delete
이면, 부적격 파드는 삭제되기 전에,
연결된 스테이트풀셋 템플릿 PVC의 소유자로 지정된다.
이로 인해, 부적격 파드가 종료된 이후에만 PVC가 가비지 콜렉트된다.
이는 곧 만약 컨트롤러가 강제 종료되어 재시작되면, 파드의 소유자 정보가 정책에 적합하게 업데이트되기 전에는 어떤 파드도 삭제되지 않을 것임을 의미한다. 만약 컨트롤러가 다운된 동안 부적격 파드가 강제로 삭제되면, 컨트롤러가 강제 종료된 시점에 따라 소유자 정보가 설정되었을 수도 있고 설정되지 않았을 수도 있다. 소유자 정보가 업데이트되기까지 몇 번의 조정 절차가 필요할 수 있으며, 따라서 일부 부적격 파드는 소유자 정보 설정을 완료하고 나머지는 그러지 못했을 수 있다. 이러한 이유로, 컨트롤러가 다시 켜져서 파드를 종료하기 전에 소유자 정보를 검증할 때까지 기다리는 것을 추천한다. 이것이 불가능하다면, 관리자는 PVC의 소유자 정보를 확인하여 파드가 강제 삭제되었을 때 해당되는 오브젝트가 삭제되도록 해야 한다.
레플리카
.spec.replicas
은 필요한 파드의 수를 지정하는 선택적 필드이다. 기본값은 1이다.
예를 들어 kubectl scale deployment deployment --replicas=X
명령으로
디플로이먼트의 크기를 수동으로 조정한 뒤,
매니페스트를 이용하여 디플로이먼트를 업데이트하면(예: kubectl apply -f deployment.yaml
실행),
수동으로 설정했던 디플로이먼트의 크기가
오버라이드된다.
HorizontalPodAutoscaler(또는 수평 스케일링을 위한 유사 API)가
디플로이먼트 크기를 관리하고 있다면, .spec.replicas
를 설정해서는 안 된다.
대신, 쿠버네티스
컨트롤 플레인이
.spec.replicas
필드를 자동으로 관리한다.
다음 내용
- 파드에 대해 배운다.
- 스테이트풀셋을 사용하는 방법을 알아본다.
- 스테이트풀셋 애플리케이션 배포 예제를 따라한다.
- 스테이트풀셋으로 카산드라 배포 예제를 따라한다.
- 복제된 스테이트풀셋 애플리케이션 구동하기 예제를 따라한다.
- 스테이트풀셋 확장하기에 대해 배운다.
- 스테이트풀셋을 삭제하면 어떤 일이 수반되는지를 배운다.
- 스토리지의 볼륨을 사용하는 파드 구성을 하는 방법을 배운다.
- 스토리지로 퍼시스턴트볼륨(PersistentVolume)을 사용하도록 파드 설정하는 방법을 배운다.
StatefulSet
은 쿠버네티스 REST API의 상위-수준 리소스이다. 스테이트풀셋 API에 대해 이해하기 위해 StatefulSet 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
4 - 데몬셋
데몬셋 은 모든(또는 일부) 노드가 파드의 사본을 실행하도록 한다. 노드가 클러스터에 추가되면 파드도 추가된다. 노드가 클러스터에서 제거되면 해당 파드는 가비지(garbage)로 수집된다. 데몬셋을 삭제하면 데몬셋이 생성한 파드들이 정리된다.
데몬셋의 일부 대표적인 용도는 다음과 같다.
- 모든 노드에서 클러스터 스토리지 데몬 실행
- 모든 노드에서 로그 수집 데몬 실행
- 모든 노드에서 노드 모니터링 데몬 실행
단순한 케이스에서는, 각 데몬 유형의 처리를 위해서 모든 노드를 커버하는 하나의 데몬셋이 사용된다. 더 복잡한 구성에서는 단일 유형의 데몬에 여러 데몬셋을 사용할 수 있지만, 각기 다른 하드웨어 유형에 따라 서로 다른 플래그, 메모리, CPU 요구가 달라진다.
데몬셋 사양 작성
데몬셋 생성
YAML 파일에 데몬셋 명세를 작성할 수 있다. 예를 들어 아래 daemonset.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
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 이 톨러레이션(toleration)은 데몬셋이 컨트롤 플레인 노드에서 실행될 수 있도록 만든다.
# 컨트롤 플레인 노드가 이 파드를 실행해서는 안 되는 경우, 이 톨러레이션을 제거한다.
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- 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
volumeMounts:
- name: varlog
mountPath: /var/log
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
YAML 파일을 기반으로 데몬셋을 생성한다.
kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml
필수 필드
다른 모든 쿠버네티스 설정과 마찬가지로 데몬셋에는 apiVersion
, kind
그리고 metadata
필드가 필요하다.
일반적인 설정파일 작업에 대한 정보는
스테이트리스 애플리케이션 실행하기와
kubectl을 사용한 오브젝트 관리를 참고한다.
데몬셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
데몬셋에는
.spec
섹션도 필요하다.
파드 템플릿
.spec.template
는 .spec
의 필수 필드 중 하나이다.
.spec.template
는 파드 템플릿이다.
이것은 중첩되어 있다는 점과 apiVersion
또는 kind
를 가지지 않는 것을 제외하면
파드와 정확히 같은 스키마를 가진다.
데몬셋의 파드 템플릿에는 파드의 필수 필드 외에도 적절한 레이블이 명시되어야 한다(파드 셀렉터를 본다).
데몬셋의 파드 템플릿의 RestartPolicy
는 Always
를 가져야 하며,
명시되지 않은 경우 기본으로 Always
가 된다.
파드 셀렉터
.spec.selector
필드는 파드 셀렉터이다. 이것은
잡의 .spec.selector
와 같은 동작을 한다.
.spec.template
의 레이블과 매치되는
파드 셀렉터를 명시해야 한다.
또한, 한 번 데몬셋이 만들어지면
.spec.selector
는 바꿀 수 없다.
파드 셀렉터를 변형하면 의도치 않게 파드가 고아가 될 수 있으며, 이는 사용자에게 혼란을 주는 것으로 밝혀졌다.
.spec.selector
는 다음 2개의 필드로 구성된 오브젝트이다.
matchLabels
- 레플리케이션 컨트롤러의.spec.selector
와 동일하게 작동한다.matchExpressions
- 키, 값 목록 그리고 키 및 값에 관련된 연산자를 명시해서 보다 정교한 셀렉터를 만들 수 있다.
2개의 필드가 명시되면 두 필드를 모두 만족하는 것(ANDed)이 결과가 된다.
.spec.selector
는 .spec.template.metadata.labels
와 일치해야 한다.
이 둘이 서로 일치하지 않는 구성은 API에 의해 거부된다.
오직 일부 노드에서만 파드 실행
만약 .spec.template.spec.nodeSelector
를 명시하면 데몬셋 컨트롤러는
노드 셀렉터와
일치하는 노드에 파드를 생성한다.
마찬가지로 .spec.template.spec.affinity
를 명시하면
데몬셋 컨트롤러는 노드 어피니티와 일치하는 노드에 파드를 생성한다.
만약 둘 중 하나를 명시하지 않으면 데몬셋 컨트롤러는 모든 노드에서 파드를 생성한다.
데몬 파드가 스케줄 되는 방법
기본 스케줄러로 스케줄
Kubernetes 1.17 [stable]
데몬셋은 자격이 되는 모든 노드에서 파드 사본이 실행하도록 보장한다. 일반적으로 쿠버네티스 스케줄러에 의해 파드가 실행되는 노드가 선택된다. 그러나 데몬셋 파드는 데몬셋 컨트롤러에 의해 생성되고 스케줄된다. 이에 대한 이슈를 소개한다.
- 파드 동작의 불일치: 스케줄 되기 위해서 대기 중인 일반 파드는
Pending
상태로 생성된다. 그러나 데몬셋 파드는Pending
상태로 생성되지 않는다. 이것은 사용자에게 혼란을 준다. - 파드 선점은 기본 스케줄러에서 처리한다. 선점이 활성화되면 데몬셋 컨트롤러는 파드 우선순위와 선점을 고려하지 않고 스케줄 한다.
ScheduleDaemonSetPods
로 데몬셋 파드에 .spec.nodeName
용어 대신
NodeAffinity
용어를 추가해서 데몬셋 컨트롤러 대신 기본
스케줄러를 사용해서 데몬셋을 스케줄할 수 있다. 이후에 기본
스케줄러를 사용해서 대상 호스트에 파드를 바인딩한다. 만약 데몬셋 파드에
이미 노드 선호도가 존재한다면 교체한다(대상 호스트를 선택하기 전에
원래 노드의 어피니티가 고려된다). 데몬셋 컨트롤러는
데몬셋 파드를 만들거나 수정할 때만 이런 작업을 수행하며,
데몬셋의 spec.template
은 변경되지 않는다.
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name
또한, 데몬셋 파드에 node.kubernetes.io/unschedulable:NoSchedule
이 톨러레이션(toleration)으로
자동으로 추가된다. 기본 스케줄러는 데몬셋 파드를
스케줄링시 unschedulable
노드를 무시한다.
테인트(taints)와 톨러레이션(tolerations)
데몬 파드는 테인트와 톨러레이션을 존중하지만, 다음과 같이 관련 기능에 따라 자동적으로 데몬셋 파드에 톨러레이션을 추가한다.
톨러레이션 키 | 영향 | 버전 | 설명 |
---|---|---|---|
node.kubernetes.io/not-ready | NoExecute | 1.13+ | 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다. |
node.kubernetes.io/unreachable | NoExecute | 1.13+ | 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다. |
node.kubernetes.io/disk-pressure | NoSchedule | 1.8+ | 데몬셋 파드는 기본 스케줄러에서 디스크-압박(disk-pressure) 속성을 허용한다. |
node.kubernetes.io/memory-pressure | NoSchedule | 1.8+ | 데몬셋 파드는 기본 스케줄러에서 메모리-압박(memory-pressure) 속성을 허용한다. |
node.kubernetes.io/unschedulable | NoSchedule | 1.12+ | 데몬셋 파드는 기본 스케줄러의 스케줄할 수 없는(unschedulable) 속성을 극복한다. |
node.kubernetes.io/network-unavailable | NoSchedule | 1.12+ | 호스트 네트워크를 사용하는 데몬셋 파드는 기본 스케줄러에 의해 이용할 수 없는 네트워크(network-unavailable) 속성을 극복한다. |
데몬 파드와 통신
데몬셋의 파드와 통신할 수 있는 몇 가지 패턴은 다음과 같다.
- 푸시(Push): 데몬셋의 파드는 통계 데이터베이스와 같은 다른 서비스로 업데이트를 보내도록 구성되어 있다. 그들은 클라이언트들을 가지지 않는다.
- 노드IP와 알려진 포트: 데몬셋의 파드는
호스트 포트
를 사용할 수 있으며, 노드IP를 통해 파드에 접근할 수 있다. 클라이언트는 노드IP를 어떻게든지 알고 있으며, 관례에 따라 포트를 알고 있다. - DNS: 동일한 파드 셀렉터로 헤드리스 서비스를 만들고,
그 다음에
엔드포인트
리소스를 사용해서 데몬셋을 찾거나 DNS에서 여러 A레코드를 검색한다. - 서비스: 동일한 파드 셀렉터로 서비스를 생성하고, 서비스를 사용해서 임의의 노드의 데몬에 도달한다(특정 노드에 도달할 방법이 없다).
데몬셋 업데이트
만약 노드 레이블이 변경되면, 데몬셋은 새로 일치하는 노드에 즉시 파드를 추가하고, 새로 일치하지 않는 노드에서 파드를 삭제한다.
사용자는 데몬셋이 생성하는 파드를 수정할 수 있다. 그러나 파드는 모든 필드가 업데이트 되는 것을 허용하지 않는다. 또한 데몬셋 컨트롤러는 다음에 노드(동일한 이름으로)가 생성될 때 원본 템플릿을 사용한다.
사용자는 데몬셋을 삭제할 수 있다. 만약 kubectl
에서 --cascade=orphan
를 명시하면
파드는 노드에 남게 된다. 이후에 동일한 셀렉터로 새 데몬셋을 생성하면,
새 데몬셋은 기존 파드를 채택한다. 만약 파드를 교체해야 하는 경우 데몬셋은
updateStrategy
에 따라 파드를 교체한다.
사용자는 데몬셋에서 롤링 업데이트를 수행할 수 있다.
데몬셋의 대안
초기화 스크립트
데몬 프로세스를 직접 노드에서 시작해서 실행하는 것도 당연히 가능하다.
(예: init
, upstartd
또는 systemd
를 사용). 이 방법도 문제는 전혀 없다. 그러나 데몬셋을 통해 데몬
프로세스를 실행하면 몇 가지 이점 있다.
- 애플리케이션과 동일한 방법으로 데몬을 모니터링하고 로그 관리를 할 수 있다.
- 데몬 및 애플리케이션과 동일한 구성 언어와 도구(예: 파드 템플릿,
kubectl
). - 리소스 제한이 있는 컨테이너에서 데몬을 실행하면 앱 컨테이너에서 데몬간의 격리를 증가시킨다. 그러나 이것은 파드가 아닌 컨테이너에서 데몬을 실행해서 이루어진다.
베어(Bare) 파드
직접적으로 파드를 실행할 특정한 노드를 명시해서 파드를 생성할 수 있다. 그러나 데몬셋은 노드 장애 또는 커널 업그레이드와 같이 변경사항이 많은 노드 유지보수의 경우를 비롯하여 어떠한 이유로든 삭제되거나 종료된 파드를 교체한다. 따라서 개별 파드를 생성하는 것보다는 데몬 셋을 사용해야 한다.
스태틱(static) 파드
Kubelet이 감시하는 특정 디렉터리에 파일을 작성하는 파드를 생성할 수 있다. 이것을 스태틱 파드라고 부른다. 데몬셋과는 다르게 스태틱 파드는 kubectl 또는 다른 쿠버네티스 API 클라이언트로 관리할 수 없다. 스태틱 파드는 API 서버에 의존하지 않기 때문에 클러스터 부트스트랩(bootstraping)하는 경우에 유용하다. 또한 스태틱 파드는 향후에 사용 중단될 수 있다.
디플로이먼트
데몬셋은 파드를 생성한다는 점에서 디플로이먼트와 유사하고, 해당 파드에서는 프로세스가 종료되지 않을 것으로 예상한다(예: 웹 서버).
파드가 실행되는 호스트를 정확하게 제어하는 것보다 레플리카의 수를 스케일링 업 및 다운 하고, 업데이트 롤아웃이 더 중요한 프런트 엔드와 같은 것은 스테이트리스 서비스의 디플로이먼트를 사용한다. 데몬셋이 특정 노드에서 다른 파드가 올바르게 실행되도록 하는 노드 수준 기능을 제공한다면, 파드 사본이 항상 모든 호스트 또는 특정 호스트에서 실행되는 것이 중요한 경우에 데몬셋을 사용한다.
예를 들어, 네트워크 플러그인은 데몬셋으로 실행되는 컴포넌트를 포함할 수 있다. 데몬셋 컴포넌트는 작동 중인 노드가 정상적인 클러스터 네트워킹을 할 수 있도록 한다.
다음 내용
- 파드에 대해 배운다.
- 데몬셋을 어떻게 사용하는지 알아본다.
- 데몬셋 롤링 업데이트 수행하기
- 데몬셋 롤백하기 (예를 들어, 롤 아웃이 예상대로 동작하지 않은 경우).
- 쿠버네티스가 파드를 노드에 할당하는 방법을 이해한다.
- 데몬셋으로 구동되곤 하는, 디바이스 플러그인과 애드온에 대해 배운다.
DaemonSet
은 쿠버네티스 REST API에서 상위-수준 리소스이다. 데몬셋 API에 대해 이해하기 위해 DaemonSet 오브젝트 정의를 읽는다.
5 - 잡
잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료될 때까지 계속해서 파드의 실행을 재시도한다. 파드가 성공적으로 완료되면, 성공적으로 완료된 잡을 추적한다. 지정된 수의 성공 완료에 도달하면, 작업(즉, 잡)이 완료된다. 잡을 삭제하면 잡이 생성한 파드가 정리된다. 작업을 일시 중지하면 작업이 다시 재개될 때까지 활성 파드가 삭제된다.
간단한 사례는 잡 오브젝트를 하나 생성해서 파드 하나를 안정적으로 실행하고 완료하는 것이다. 첫 번째 파드가 실패 또는 삭제된 경우(예로는 노드 하드웨어의 실패 또는 노드 재부팅) 잡 오브젝트는 새로운 파드를 기동시킨다.
잡을 사용하면 여러 파드를 병렬로 실행할 수도 있다.
잡을 스케줄에 따라 구동하고 싶은 경우(단일 작업이든, 여러 작업의 병렬 수행이든), 크론잡(CronJob)을 참고한다.
예시 잡 실행하기
다음은 잡 설정 예시이다. 예시는 파이(π)의 2000 자리까지 계산해서 출력한다. 이를 완료하는 데 약 10초가 소요된다.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
이 명령으로 예시를 실행할 수 있다.
kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml
출력 결과는 다음과 같다.
job.batch/pi created
kubectl
을 사용해서 잡 상태를 확인한다.
Name: pi
Namespace: default
Selector: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
job-name=pi
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":...
Parallelism: 1
Completions: 1
Start Time: Mon, 02 Dec 2019 15:20:11 +0200
Completed At: Mon, 02 Dec 2019 15:21:16 +0200
Duration: 65s
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
job-name=pi
Containers:
pi:
Image: perl:5.34.0
Port: <none>
Host Port: <none>
Command:
perl
-Mbignum=bpi
-wle
print bpi(2000)
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 14m job-controller Created pod: pi-5rwd7
apiVersion: batch/v1
kind: Job
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":{"spec":{"containers":[{"command":["perl","-Mbignum=bpi","-wle","print bpi(2000)"],"image":"perl","name":"pi"}],"restartPolicy":"Never"}}}}
creationTimestamp: "2022-06-15T08:40:15Z"
generation: 1
labels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
job-name: pi
name: pi
namespace: default
resourceVersion: "987"
uid: 863452e6-270d-420e-9b94-53a54146c223
spec:
backoffLimit: 4
completionMode: NonIndexed
completions: 1
parallelism: 1
selector:
matchLabels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
suspend: false
template:
metadata:
creationTimestamp: null
labels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
job-name: pi
spec:
containers:
- command:
- perl
- -Mbignum=bpi
- -wle
- print bpi(2000)
image: perl:5.34.0
imagePullPolicy: Always
name: pi
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
active: 1
ready: 1
startTime: "2022-06-15T08:40:15Z"
kubectl get pods
를 사용해서 잡의 완료된 파드를 본다.
잡에 속하는 모든 파드를 기계적으로 읽을 수 있는 양식으로 나열하려면, 다음과 같은 명령을 사용할 수 있다.
pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods
출력 결과는 다음과 같다.
pi-5rwd7
여기서 셀렉터는 잡의 셀렉터와 동일하다. --output=jsonpath
옵션은 반환된
목록에 있는 각 파드의 이름으로 표현식을 지정한다.
파드 중 하나를 표준 출력으로 본다.
kubectl logs $pods
출력 결과는 다음과 같다.
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
잡 사양 작성하기
다른 쿠버네티스의 설정과 마찬가지로 잡에는 apiVersion
, kind
그리고 metadata
필드가 필요하다.
잡의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
잡에는 .spec
섹션도 필요하다.
파드 템플릿
.spec.template
은 .spec
의 유일한 필수 필드이다.
.spec.template
은 파드 템플릿이다. 이것은 apiVersion
또는 kind
가 없다는 것을 제외한다면 파드와 정확하게 같은 스키마를 가지고 있다.
추가로 파드의 필수 필드 외에도 잡의 파드 템플릿은 적절한 레이블(파드 셀렉터를 본다)과 적절한 재시작 정책을 명시해야 한다.
Never
또는 OnFailure
와 같은 RestartPolicy
만 허용된다.
파드 셀렉터
.spec.selector
필드는 선택 사항이다. 대부분의 케이스에서 지정해서는 안된다.
자신의 파드 셀렉터를 지정하기 섹션을 참고한다.
잡에 대한 병렬 실행
잡으로 실행하기에 적합한 작업 유형은 크게 세 가지가 있다.
- 비-병렬(Non-parallel) 잡:
- 일반적으로, 파드가 실패하지 않은 한, 하나의 파드만 시작된다.
- 파드가 성공적으로 종료하자마자 즉시 잡이 완료된다.
- 고정적(fixed)인 완료 횟수 를 가진 병렬 잡:
.spec.completions
에 0이 아닌 양수 값을 지정한다.- 잡은 전체 작업을 나타내며,
.spec.completions
성공한 파드가 있을 때 완료된다. .spec.completionMode="Indexed"
를 사용할 때, 각 파드는 0에서.spec.completions-1
범위 내의 서로 다른 인덱스를 가져온다.
- 작업 큐(queue) 가 있는 병렬 잡:
.spec.completions
를 지정하지 않고,.spec.parallelism
를 기본으로 한다.- 파드는 각자 또는 외부 서비스 간에 조정을 통해 각각의 작업을 결정해야 한다. 예를 들어 파드는 작업 큐에서 최대 N 개의 항목을 일괄로 가져올(fetch) 수 있다.
- 각 파드는 모든 피어들의 작업이 완료되었는지 여부를 독립적으로 판단할 수 있으며, 결과적으로 전체 잡이 완료되게 한다.
- 잡의 모든 파드가 성공적으로 종료되면, 새로운 파드는 생성되지 않는다.
- 하나 이상의 파드가 성공적으로 종료되고, 모든 파드가 종료되면 잡은 성공적으로 완료된다.
- 성공적으로 종료된 파드가 하나라도 생긴 경우, 다른 파드들은 해당 작업을 지속하지 않아야 하며 어떠한 출력도 작성하면 안 된다. 파드들은 모두 종료되는 과정에 있어야 한다.
비-병렬 잡은 .spec.completions
와 .spec.parallelism
모두를 설정하지 않은 채로 둘 수 있다. 이때 둘 다
설정하지 않은 경우 1이 기본으로 설정된다.
고정적인 완료 횟수 잡은 .spec.completions
을 필요한 완료 횟수로 설정해야 한다.
.spec.parallelism
을 설정할 수 있고, 설정하지 않으면 1이 기본으로 설정된다.
작업 큐 잡은 .spec.completions
를 설정하지 않은 상태로 두고, .spec.parallelism
을
음수가 아닌 정수로 설정해야 한다.
다른 유형의 잡을 사용하는 방법에 대한 더 자세한 정보는 잡 패턴 섹션을 본다.
병렬 처리 제어하기
요청된 병렬 처리(.spec.parallelism
)는 음수가 아닌 값으로 설정할 수 있다.
만약 지정되지 않은 경우에는 1이 기본이 된다.
만약 0으로 지정되면 병렬 처리가 증가할 때까지 사실상 일시 중지된다.
실제 병렬 처리(모든 인스턴스에서 실행되는 파드의 수)는 여러가지 이유로 요청된 병렬 처리보다 많거나 적을 수 있다.
- 고정적인 완료 횟수(fixed completion count) 잡의 경우, 병렬로 실행 중인 파드의 수는 남은 완료 수를
초과하지 않는다.
.spec.parallelism
의 더 큰 값은 사실상 무시된다. - 작업 큐 잡은 파드가 성공한 이후에 새로운 파드가 시작되지 않는다. 그러나 나머지 파드는 완료될 수 있다.
- 만약 잡 컨트롤러 가 반응할 시간이 없는 경우
- 만약 잡 컨트롤러가 어떤 이유(
ResourceQuota
의 부족, 권한 부족 등)로든 파드 생성에 실패한 경우, 요청한 것보다 적은 수의 파드가 있을 수 있다. - 잡 컨트롤러는 동일한 잡에서 과도하게 실패한 이전 파드들로 인해 새로운 파드의 생성을 조절할 수 있다.
- 파드가 정상적으로(gracefully) 종료되면, 중지하는데 시간이 소요된다.
완료 모드
Kubernetes v1.24 [stable]
완료 횟수가 고정적인 완료 횟수 즉, null이 아닌 .spec.completions
가 있는 잡은
.spec.completionMode
에 지정된 완료 모드를 가질 수 있다.
NonIndexed
(기본값):.spec.completions
가 성공적으로 완료된 파드가 있는 경우 작업이 완료된 것으로 간주된다. 즉, 각 파드 완료는 서로 상동하다(homologous). null.spec.completions
가 있는 잡은 암시적으로NonIndexed
이다.Indexed
: 잡의 파드는 연결된 완료 인덱스를 0에서.spec.completions-1
까지 가져온다. 이 인덱스는 다음의 세 가지 메카니즘으로 얻을 수 있다.- 파드 어노테이션
batch.kubernetes.io/job-completion-index
. - 파드 호스트네임 중 일부(
$(job-name)-$(index)
형태). 인덱스된(Indexed) 잡과 서비스를 결합하여 사용하고 있다면, 잡에 속한 파드는 DNS를 이용하여 서로를 디스커버 하기 위해 사전에 결정된 호스트네임을 사용할 수 있다. 이를 어떻게 설정하는지에 대해 궁금하다면, 파드 간 통신을 위한 잡를 확인한다. - 컨테이너화된 태스크의 경우, 환경 변수
JOB_COMPLETION_INDEX
에 있다.
각 인덱스에 대해 성공적으로 완료된 파드가 하나 있으면 작업이 완료된 것으로 간주된다. 이 모드를 사용하는 방법에 대한 자세한 내용은 정적 작업 할당을 사용한 병렬 처리를 위해 인덱싱된 잡을 참고한다. 참고로, 드물기는 하지만, 동일한 인덱스에 대해 둘 이상의 파드를 시작할 수 있지만, 그 중 하나만 완료 횟수에 포함된다.
- 파드 어노테이션
파드와 컨테이너 장애 처리하기
파드내 컨테이너의 프로세스가 0이 아닌 종료 코드로 종료되었거나 컨테이너 메모리 제한을
초과해서 죽는 등의 여러가지 이유로 실패할 수 있다. 만약 이런 일이
발생하고 .spec.template.spec.restartPolicy = "OnFailure"
라면 파드는
노드에 그대로 유지되지만, 컨테이너는 다시 실행된다. 따라서 프로그램은 로컬에서 재시작될 때의
케이스를 다루거나 .spec.template.spec.restartPolicy = "Never"
로 지정해야 한다.
더 자세한 정보는 파드 라이프사이클의 restartPolicy
를 본다.
파드가 노드에서 내보내지는 경우(노드 업그레이드, 재부팅, 삭제 등) 또는 파드의 컨테이너가 실패
되고 .spec.template.spec.restartPolicy = "Never"
로 설정됨과 같은 여러 이유로
전체 파드가 실패할 수 있다. 파드가 실패하면 잡 컨트롤러는
새 파드를 시작한다. 이 의미는 애플리케이션이 새 파드에서 재시작될 때 이 케이스를 처리해야
한다는 점이다. 특히, 이전 실행으로 인한 임시파일, 잠금, 불완전한 출력 그리고 이와 유사한
것들을 처리해야 한다.
기본적으로 파드의 실패는 .spec.backoffLimit
제한값으로 계산되며,
자세한 내용은 파드 백오프(backoff) 실패 정책을 확인한다. 그러나,
잡의 파드 실패 정책을 설정하여 파드의 실패 수준을 조절하여 사용할 수도 있다.
.spec.parallelism = 1
, .spec.completions = 1
그리고
.spec.template.spec.restartPolicy = "Never"
를 지정하더라도 같은 프로그램을
두 번 시작하는 경우가 있다는 점을 참고한다.
.spec.parallelism
그리고 .spec.completions
를 모두 1보다 크게 지정한다면 한번에
여러 개의 파드가 실행될 수 있다. 따라서 파드는 동시성에 대해서도 관대(tolerant)해야 한다.
만약 기능 게이트인
PodDisruptionConditions
와 JobPodFailurePolicy
가 모두 활성화되어 있고,
.spec.podFailurePolicy
필드가 설정되어 있다면, 잡 컨트롤러는 종료 중인
파드(.metadata.deletionTimestamp
필드가 설정된 파드)가 종료 상태(.status.phase
값이 Failed
혹은 Succeeded
)가 될 때 까지
해당 파드를 실패로 간주하지 않는다. 그러나, 잡 컨트롤러는
파드가 명백히 종료되었다고 판단하면 곧바로 대체 파드를 생성한다.
파드가 한 번 종료되면, 잡 컨트롤러는 방금 종료된 파드를 고려하여
관련 작업에 대해 .backoffLimit
과 .podFailurePolicy
를 평가한다.
이러한 조건들이 하나라도 충족되지 않을 경우, 잡 컨트롤러는
종료 중인 파드가 추후 phase: "Succeded"
로 종료된다고 할지라도,
해당 파드를 실패한 파드로 즉시 간주한다.
파드 백오프(backoff) 실패 정책
구성에 논리적 오류가 포함되어 있어서 몇 회의 재시도 이후에
잡이 실패되도록 만들어야 하는 경우가 있다.
이렇게 하려면 .spec.backoffLimit
의 값에
재시도(잡을 실패로 처리하기 이전까지) 횟수를 설정한다. 백오프 제한은 기본적으로 6으로 설정되어 있다.
잡에 연계된 실패 상태 파드는 6분 내에서 지수적으로 증가하는
백-오프 지연(10초, 20초, 40초 ...)을 적용하여, 잡 컨트롤러에 의해 재생성된다.
재시도 횟수는 다음 두 가지 방법으로 계산된다.
.status.phase = "Failed"
인 파드의 수.restartPolicy = "OnFailure"
를 사용하는 경우,.status.phase
가Pending
이거나Running
인 파드들이 가지고 있는 모든 컨테이너의 수.
계산 중 하나가 .spec.backoffLimit
에 도달하면, 잡이
실패한 것으로 간주한다.
참고:
만약 잡에restartPolicy = "OnFailure"
가 있는 경우 잡 백오프 한계에
도달하면 잡을 실행 중인 파드가 종료된다. 이로 인해 잡 실행 파일의 디버깅이
더 어려워질 수 있다. 디버깅하거나 로깅 시스템을 사용해서 실패한 작업의 결과를 실수로 손실되지 않도록
하려면 restartPolicy = "Never"
로 설정하는 것을 권장한다.잡의 종료와 정리
잡이 완료되면 파드가 더 이상 생성되지도 않지만, 일반적으로는 삭제되지도 않는다.
이를 유지하면
완료된 파드의 로그를 계속 보며 에러, 경고 또는 다른 기타 진단 출력을 확인할 수 있다.
잡 오브젝트는 완료된 후에도 상태를 볼 수 있도록 남아 있다. 상태를 확인한 후 이전 잡을 삭제하는 것은 사용자의 몫이다.
kubectl
로 잡을 삭제할 수 있다 (예: kubectl delete jobs/pi
또는 kubectl delete -f ./job.yaml
). kubectl
을 사용해서 잡을 삭제하면 생성된 모든 파드도 함께 삭제된다.
기본적으로 파드의 실패(restartPolicy=Never
) 또는 컨테이너가 오류(restartPolicy=OnFailure
)로 종료되지 않는 한, 잡은 중단되지 않고 실행되고
이때 위에서 설명했던 .spec.backoffLimit
까지 연기된다. .spec.backoffLimit
에 도달하면 잡은 실패로 표기되고 실행 중인 모든 파드는 종료된다.
잡을 종료하는 또 다른 방법은 유효 데드라인을 설정하는 것이다.
잡의 .spec.activeDeadlineSeconds
필드를 초 단위로 설정하면 된다.
activeDeadlineSeconds
는 생성된 파드의 수에 관계 없이 잡의 기간에 적용된다.
잡이 activeDeadlineSeconds
에 도달하면, 실행 중인 모든 파드가 종료되고 잡의 상태는 reason: DeadlineExceeded
와 함께 type: Failed
가 된다.
잡의 .spec.activeDeadlineSeconds
는 .spec.backoffLimit
보다 우선한다는 점을 참고한다. 따라서 하나 이상 실패한 파드를 재시도하는 잡은 backoffLimit
에 도달하지 않은 경우에도 activeDeadlineSeconds
에 지정된 시간 제한에 도달하면 추가 파드를 배포하지 않는다.
예시:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-timeout
spec:
backoffLimit: 5
activeDeadlineSeconds: 100
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
잡의 사양과 잡의 파드 템플릿 사양에는 모두 activeDeadlineSeconds
필드가 있다는 점을 참고한다. 이 필드를 적절한 레벨로 설정해야 한다.
restartPolicy
는 잡 자체에 적용되는 것이 아니라 파드에 적용된다는 점을 유념한다. 잡의 상태가 type: Failed
이 되면, 잡의 자동 재시작은 없다.
즉, .spec.activeDeadlineSeconds
와 .spec.backoffLimit
로 활성화된 잡의 종료 메커니즘은 영구적인 잡의 실패를 유발하며 이를 해결하기 위해 수동 개입이 필요하다.
완료된 잡을 자동으로 정리
완료된 잡은 일반적으로 시스템에서 더 이상 필요로 하지 않는다. 시스템 내에 이를 유지한다면 API 서버에 부담이 된다. 만약 크론잡과 같은 상위 레벨 컨트롤러가 잡을 직접 관리하는 경우, 지정된 용량 기반 정리 정책에 따라 크론잡이 잡을 정리할 수 있다.
완료된 잡을 위한 TTL 메커니즘
Kubernetes v1.23 [stable]
완료된 잡 (Complete
또는 Failed
)을 자동으로 정리하는 또 다른 방법은
잡의 .spec.ttlSecondsAfterFinished
필드를 지정해서 완료된 리소스에 대해
TTL 컨트롤러에서
제공하는 TTL 메커니즘을 사용하는
것이다.
TTL 컨트롤러는 잡을 정리하면 잡을 계단식으로 삭제한다. 즉, 잡과 함께 파드와 같은 종속 오브젝트를 삭제한다. 잡을 삭제하면 finalizer와 같은 라이프사이클 보증이 보장되는 것을 참고한다.
예시:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-ttl
spec:
ttlSecondsAfterFinished: 100
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
pi-with-ttl
잡은 완료 후 100
초 이후에
자동으로 삭제될 수 있다.
만약 필드를 0
으로 설정하면, 잡이 완료된 직후에 자동으로
삭제되도록 할 수 있다. 만약 필드를 설정하지 않으면, 이 잡이 완료된
후에 TTL 컨트롤러에 의해 정리되지 않는다.
참고:
ttlSecondsAfterFinished
필드를 설정하는 것을 권장하는데,
이는 관리되지 않는 잡(직접 생성한,
크론잡 등 다른 워크로드 API를 통해 간접적으로 생성하지 않은 잡)의
기본 삭제 정책이 orphanDependents
(관리되지 않는 잡이 완전히 삭제되어도
해당 잡에 의해 생성된 파드를 남겨둠)이기 때문이다.
삭제된 잡의 파드가 실패하거나 완료된 뒤
컨트롤 플레인이 언젠가
가비지 콜렉션을 한다고 해도,
이렇게 남아 있는 파드는 클러스터의 성능을 저하시키거나
최악의 경우에는 이 성능 저하로 인해 클러스터가 중단될 수도 있다.
리밋 레인지(Limit Range)와 리소스 쿼터를 사용하여 특정 네임스페이스가 사용할 수 있는 자원량을 제한할 수 있다.
잡 패턴
잡 오브젝트를 사용해서 신뢰할 수 있는 파드의 병렬 실행을 지원할 수 있다. 잡 오브젝트는 과학 컴퓨팅(scientific computing)에서 일반적으로 사용되는 밀접하게 통신하는 병렬 프로세스를 지원하도록 설계되지 않았다. 잡 오브젝트는 독립적이지만 관련된 작업 항목 집합의 병렬 처리를 지원한다. 여기에는 전송할 이메일들, 렌더링할 프레임, 코드 변환이 필요한 파일, NoSQL 데이터베이스에서의 키 범위 스캔 등이 있다.
복잡한 시스템에는 여러 개의 다른 작업 항목 집합이 있을 수 있다. 여기서는 사용자와 함께 관리하려는 하나의 작업 항목 집합 — 배치 잡 을 고려하고 있다.
병렬 계산에는 몇몇 다른 패턴이 있으며 각각의 장단점이 있다. 트레이드오프는 다음과 같다.
- 각 작업 항목에 대한 하나의 잡 오브젝트 vs 모든 작업 항목에 대한 단일 잡 오브젝트. 후자는 작업 항목 수가 많은 경우 더 적합하다. 전자는 사용자와 시스템이 많은 수의 잡 오브젝트를 관리해야 하는 약간의 오버헤드를 만든다.
- 작업 항목과 동일한 개수의 파드 생성 vs 각 파드에서 다수의 작업 항목을 처리. 전자는 일반적으로 기존 코드와 컨테이너를 거의 수정할 필요가 없다. 후자는 이전 글 머리표(-)와 비슷한 이유로 많은 수의 작업 항목에 적합하다.
- 여러 접근 방식이 작업 큐를 사용한다. 이를 위해서는 큐 서비스를 실행하고, 작업 큐를 사용하도록 기존 프로그램이나 컨테이너를 수정해야 한다. 다른 접근 방식들은 기존에 컨테이너화된 애플리케이션에 보다 쉽게 적용할 수 있다.
여기에 트레이드오프가 요약되어 있고, 2열에서 4열까지가 위의 트레이드오프에 해당한다. 패턴 이름은 예시와 더 자세한 설명을 위한 링크이다.
패턴 | 단일 잡 오브젝트 | 작업 항목보다 파드가 적은가? | 수정되지 않은 앱을 사용하는가? |
---|---|---|---|
작업 항목 당 파드가 있는 큐 | ✓ | 때때로 | |
가변 파드 수를 가진 큐 | ✓ | ✓ | |
정적 작업 할당을 사용한 인덱싱된 잡 | ✓ | ✓ | |
잡 템플릿 확장 | ✓ | ||
파드 간 통신을 위한 잡 | ✓ | 때때로 | 때때로 |
.spec.completions
로 완료를 지정할 때, 잡 컨트롤러에 의해 생성된 각 파드는
동일한 사양
을 갖는다. 이 의미는
작업의 모든 파드는 동일한 명령 줄과 동일한 이미지,
동일한 볼륨, (거의) 동일한 환경 변수를 가진다는 점이다. 이 패턴은
파드가 다른 작업을 수행하도록 배열하는 다른 방법이다.
이 표는 각 패턴에 필요한 .spec.parallelism
그리고 .spec.completions
설정을 보여준다.
여기서 W
는 작업 항목의 수이다.
패턴 | .spec.completions | .spec.parallelism |
---|---|---|
작업 항목 당 파드가 있는 큐 | W | any |
가변 파드 수를 가진 큐 | null | any |
정적 작업 할당을 사용한 인덱싱된 잡 | W | any |
잡 템플릿 확장 | 1 | 1이어야 함 |
파드 간 통신을 위한 잡 | W | W |
고급 사용법
잡 일시 중지
Kubernetes v1.24 [stable]
잡이 생성되면, 잡 컨트롤러는 잡의 요구 사항을 충족하기 위해 즉시 파드 생성을 시작하고 잡이 완료될 때까지 계속한다. 그러나, 잡의 실행을 일시적으로 중단하고 나중에 재개하거나, 잡을 중단 상태로 생성하고 언제 시작할지를 커스텀 컨트롤러가 나중에 결정하도록 하고 싶을 수도 있다.
잡을 일시 중지하려면, 잡의 .spec.suspend
필드를 true로
업데이트할 수 있다. 이후에, 다시 재개하려면, false로 업데이트한다.
.spec.suspend
로 설정된 잡을 생성하면 일시 중지된 상태로
생성된다.
잡이 일시 중지에서 재개되면, 해당 .status.startTime
필드가
현재 시간으로 재설정된다. 즉, 잡이 일시 중지 및 재개되면 .spec.activeDeadlineSeconds
타이머가 중지되고 재설정된다.
잡을 일시 중지하면, Completed
상태가 아닌 모든 실행중인 파드가 SIGTERM 시그널로 종료된다.
파드의 정상 종료 기간이 적용되며 사용자의 파드는 이 기간 동안에
이 시그널을 처리해야 한다. 나중에 진행 상황을 저장하거나
변경 사항을 취소하는 작업이 포함될 수 있다. 이 방법으로 종료된 파드는
잡의 completions
수에 포함되지 않는다.
일시 중지된 상태의 잡 정의 예시는 다음과 같다.
kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
suspend: true
parallelism: 1
completions: 5
template:
spec:
...
명령 줄에서 잡을 패치하여 잡 일시 중지를 전환할 수 있다.
활성화된 잡 일시 중지
kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'
일시 중지된 잡 재개
kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'
잡의 상태를 사용하여 잡이 일시 중지되었는지 또는 과거에 일시 중지되었는지 확인할 수 있다.
kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
conditions:
- lastProbeTime: "2021-02-05T13:14:33Z"
lastTransitionTime: "2021-02-05T13:14:33Z"
status: "True"
type: Suspended
startTime: "2021-02-05T13:13:48Z"
"True" 상태인 "Suspended" 유형의 잡의 컨디션은 잡이
일시 중지되었음을 의미한다. 이 lastTransitionTime
필드는 잡이 일시 중지된
기간을 결정하는 데 사용할 수 있다. 해당 컨디션의 상태가 "False"이면, 잡이
이전에 일시 중지되었다가 현재 실행 중이다. 이러한 컨디션이
잡의 상태에 없으면, 잡이 중지되지 않은 것이다.
잡이 일시 중지 및 재개될 때에도 이벤트가 생성된다.
kubectl describe jobs/myjob
Name: myjob
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 12m job-controller Created pod: myjob-hlrpl
Normal SuccessfulDelete 11m job-controller Deleted pod: myjob-hlrpl
Normal Suspended 11m job-controller Job suspended
Normal SuccessfulCreate 3s job-controller Created pod: myjob-jvb44
Normal Resumed 3s job-controller Job resumed
마지막 4개의 이벤트, 특히 "Suspended" 및 "Resumed" 이벤트는
.spec.suspend
필드를 전환한 결과이다. 이 두 이벤트 사이의 시간동안
파드가 생성되지 않았지만, 잡이 재개되자마자 파드 생성이 다시
시작되었음을 알 수 있다.
가변적 스케줄링 지시
Kubernetes v1.23 [beta]
병렬 잡에서 대부분의 경우 파드를 특정 제약 조건 하에서 실행하고 싶을 것이다. (예를 들면 동일 존에서 실행하거나, 또는 GPU 모델 x 또는 y를 사용하지만 둘을 혼합하지는 않는 등)
suspend 필드는 이러한 목적을 달성하기 위한 첫 번째 단계이다. 이 필드를 사용하면 커스텀 큐(queue) 컨트롤러가 잡이 언제 시작될지를 결정할 수 있다. 그러나, 잡이 재개된 이후에는, 커스텀 큐 컨트롤러는 잡의 파드가 실제로 어디에 할당되는지에 대해서는 영향을 주지 않는다.
이 기능을 이용하여 잡이 실행되기 전에 잡의 스케줄링 지시를 업데이트할 수 있으며, 이를 통해 커스텀 큐 컨트롤러가 파드 배치에 영향을 줌과 동시에 노드로의 파드 실제 할당 작업을 kube-scheduler로부터 경감시켜 줄 수 있도록 한다. 이는 이전에 재개된 적이 없는 중지된 잡에 대해서만 허용된다.
잡의 파드 템플릿 필드 중, 노드 어피니티(node affinity), 노드 셀렉터(node selector), 톨러레이션(toleration), 레이블(label), 어노테이션(annotation)은 업데이트가 가능하다.
자신의 파드 셀렉터를 지정하기
일반적으로 잡 오브젝트를 생성할 때 .spec.selector
를 지정하지 않는다.
시스템의 기본적인 로직은 잡이 생성될 때 이 필드를 추가한다.
이것은 다른 잡과 겹치지 않는 셀렉터 값을 선택한다.
그러나, 일부 케이스에서는 이 자동화된 설정 셀렉터를 재정의해야 할 수도 있다.
이를 위해 잡의 .spec.selector
를 설정할 수 있다.
이 것을 할 때는 매우 주의해야 한다. 만약 해당 잡의 파드에 고유하지
않고 연관이 없는 파드와 일치하는 레이블 셀렉터를 지정하면, 연관이 없는 잡의 파드가 삭제되거나,
해당 잡이 다른 파드가 완료한 것으로 수를 세거나, 하나 또는
양쪽 잡 모두 파드 생성이나 실행 완료를 거부할 수도 있다. 만약 고유하지 않은 셀렉터가
선택된 경우, 다른 컨트롤러(예: 레플리케이션 컨트롤러)와 해당 파드는
예측할 수 없는 방식으로 작동할 수 있다. 쿠버네티스는 당신이 .spec.selector
를 지정할 때
발생하는 실수를 막을 수 없을 것이다.
다음은 이 기능을 사용하려는 경우의 예시이다.
잡 old
가 이미 실행 중이다. 기존 파드가 계속
실행되기를 원하지만, 잡이 생성한 나머지 파드에는 다른
파드 템플릿을 사용하고 잡으로 하여금 새 이름을 부여하기를 원한다.
그러나 관련된 필드들은 업데이트가 불가능하기 때문에 잡을 업데이트할 수 없다.
따라서 kubectl delete jobs/old --cascade=orphan
명령을 사용해서
잡 old
를 삭제하지만, 파드를 실행 상태로 둔다.
삭제하기 전에 어떤 셀렉터를 사용하는지 기록한다.
kubectl get job old -o yaml
출력 결과는 다음과 같다.
kind: Job
metadata:
name: old
...
spec:
selector:
matchLabels:
controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
그런 이후에 이름이 new
인 새 잡을 생성하고, 동일한 셀렉터를 명시적으로 지정한다.
기존 파드에는 controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002
레이블이 있기에 잡 new
에 의해서도 제어된다.
시스템이 일반적으로 자동 생성하는 셀렉터를 사용하지 않도록 하기 위해
새 잡에서 manualSelector: true
를 지정해야 한다.
kind: Job
metadata:
name: new
...
spec:
manualSelector: true
selector:
matchLabels:
controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
새 잡 자체는 a8f3d00d-c6d2-11e5-9f87-42010af00002
와 다른 uid 를 가지게 될 것이다.
manualSelector: true
를 설정하면 시스템에게 사용자가 무엇을 하는지 알고 있으며
이런 불일치를 허용한다고 알릴 수 있다.
파드 실패 정책
Kubernetes v1.26 [beta]
참고:
잡(Job)에 대한 파드 실패 정책은JobPodFailurePolicy
기능 게이트가
클러스터에서 활성화됐을 경우에만 구성할 수 있다. 추가적으로,
파드 장애 정책의 파드 중단 조건 (참조:
파드 중단 조건)을
감지하고 처리할 수 있도록 PodDisruptionConditions
기능 게이트를 활성화하는 것을 권장한다. 두 기능 게이트 모두
쿠버네티스 1.31에서 사용할 수 있다..spec.podFailurePolicy
필드로 정의되는 파드 실패 정책은, 클러스터가
컨테이너 종료 코드와 파드 상태를 기반으로 파드의 실패를
처리하도록 활성화한다.
어떤 상황에서는, 파드의 실패를 처리할 때 잡(Job)의 .spec.backoffLimit
을 기반으로 하는
파드 백오프(backoff) 실패 정책에서
제공하는 제어보다 더 나은 제어를 원할 수 있다. 다음은 사용 사례의 몇 가지 예시다.
- 불필요한 파드 재시작을 방지하여 워크로드 실행 비용을 최적화하려면, 파드 중 하나가 소프트웨어 버그를 나타내는 종료 코드와 함께 실패하는 즉시 잡을 종료할 수 있다.
- 중단이 있더라도 잡이 완료되도록 하려면,
중단(예: 선점(preemption),
API를 이용한 축출(API-initiated Eviction)
또는 축출 기반 테인트(Taints))으로 인한
파드 실패를 무시하여
.spec.backoffLimit
재시도 한도에 포함되지 않도록 할 수 있다.
위의 사용 사례를 충족하기 위해
.spec.podFailurePolicy
필드에 파드 실패 정책을 구성할 수 있다.
이 정책은 컨테이너 종료 코드 및 파드 상태를 기반으로 파드 실패를 처리할 수 있다.
다음은 podFailurePolicy
를 정의하는 잡의 매니페스트이다.
apiVersion: batch/v1
kind: Job
metadata:
name: job-pod-failure-policy-example
spec:
completions: 12
parallelism: 3
template:
spec:
restartPolicy: Never
containers:
- name: main
image: docker.io/library/bash:5
command: ["bash"] # FailJob 액션을 트리거시키는 버그를 만들어내는 예제 명령어
args:
- -c
- echo "Hello world!" && sleep 5 && exit 42
backoffLimit: 6
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
containerName: main # 선택 사항
operator: In # [In / NotIn] 중 하나
values: [42]
- action: Ignore # [Ignore, FailJob, Count] 중 하나
onPodConditions:
- type: DisruptionTarget # 파드의 중단을 나타냄
위 예시에서, 파드 실패 정책의 첫 번째 규칙은 main
컨테이너가 42 종료코드와
함께 실패하면 잡도 실패로 표시되는 것으로
지정한다. 다음은 구체적으로 main
컨테이너에 대한 규칙이다.
- 종료 코드 0은 컨테이너가 성공했음을 의미한다.
- 종료 코드 42는 전체 잡이 실패했음을 의미한다.
- 다른 모든 종료 코드는 컨테이너 및 전체 파드가 실패했음을
나타낸다. 재시작 횟수인
backoffLimit
까지 파드가 다시 생성된다. 만약backoffLimit
에 도달하면 전체 잡이 실패한다.
참고:
파드 템플릿이restartPolicy: Never
로 지정되었기 때문에,
kubelet은 특정 파드에서 main
컨테이너를 재시작하지 않는다.DisruptionTarget
컨디션을 갖는 실패한 파드에 대해
Ignore
동작을 하도록 명시하고 있는 파드 실패 정책의 두 번째 규칙으로 인해,
.spec.backoffLimit
재시도 한도 계산 시 파드 중단(disruption)은 횟수에서 제외된다.
참고:
파드 실패 정책 또는 파드 백오프 실패 정책에 의해 잡이 실패하고, 잡이 여러 파드를 실행중이면, 쿠버네티스는 아직 보류(Pending) 또는 실행(Running) 중인 해당 잡의 모든 파드를 종료한다.다음은 API의 몇 가지 요구 사항 및 의미이다.
- 잡에
.spec.podFailurePolicy
필드를 사용하려면,.spec.restartPolicy
가Never
로 설정된 잡의 파드 템플릿 또한 정의해야 한다. spec.podFailurePolicy.rules
에 기재한 파드 실패 정책 규칙은 기재한 순서대로 평가된다. 파드 실패 정책 규칙이 파드 실패와 매치되면 나머지 규칙은 무시된다. 파드 실패와 매치되는 파드 실패 정책 규칙이 없으면 기본 처리 방식이 적용된다.spec.podFailurePolicy.rules[*].containerName
에 컨테이너 이름을 지정하여 파드 실패 규칙을 특정 컨테이너에게만 제한할 수 있다. 컨테이너 이름을 지정하지 않으면 파드 실패 규칙은 모든 컨테이너에 적용된다. 컨테이너 이름을 지정한 경우, 이는 파드 템플릿의 컨테이너 또는initContainer
이름 중 하나와 일치해야 한다.- 파드 실패 정책이
spec.podFailurePolicy.rules[*].action
과 일치할 때 취할 동작을 지정할 수 있다. 사용 가능한 값은 다음과 같다.FailJob
: 파드의 잡을Failed
로 표시하고 실행 중인 모든 파드를 종료해야 함을 나타낸다.Ignore
:.spec.backoffLimit
에 대한 카운터가 증가하지 않아야 하고 대체 파드가 생성되어야 함을 나타낸다.Count
: 파드가 기본 방식으로 처리되어야 함을 나타낸다..spec.backoffLimit
에 대한 카운터가 증가해야 한다.
종료자(finalizers)를 이용한 잡 추적
Kubernetes v1.26 [stable]
참고:
컨트롤 플레인을 1.26으로 업그레이드하더라도, 기능 게이트JobTrackingWithFinalizers
가 비활성화되어 있을 때 생성된 잡이라면,
컨트롤 플레인은 종료자를 사용하는 잡을 추적하지 않는다.컨트롤 플레인은 잡에 속한 파드들을 계속 추적하고,
API 서버로부터 제거된 파드가 있는지에 대해 알려준다. 이를 위해 잡 컨트롤러는
batch.kubernetes.io/job-tracking
종료자를 갖는 파드를 생성한다.
컨트롤러는 파드가 잡 상태로 처리된 이후에만 종료자를 제거하여,
다른 컨트롤러나 사용자가 파드를 제거할 수 있도록 한다.
쿠버네티스를 1.26으로 업그레이드하기 전이나, 기능 게이트
JobTrackingWithFinalizers
를 활성화시키기 전에 생성한 잡은 파드 종료자를 사용하지 않고
추적된다.
잡 컨트롤러는
클러스터에 존재하는 파드들에 대해서만 succeded
와 failed
파드들에 대한 상태 카운터를 갱신한다.
만약 파드가 클러스터에서 제거된다면,
컨트롤 플레인은 잡의 진행 상황을 제대로 추적하지 못할 수 있다.
잡이 batch.kubernetes.io/job-tracking
어노테이션을 가지고 있는지 확인함으로써
컨트롤 플레인이 파드 종료자를 사용하여 잡을 추적하고 있는지 알 수 있다.
따라서 잡으로부터 이 어노테이션을 수동으로 추가하거나 제거해서는 안 된다.
그 대신, 파드 종료자를 사용하여
추적이 가능한 잡을 재생성하면 된다.
대안
베어(Bare) 파드
파드가 실행 중인 노드가 재부팅되거나 실패하면 파드가 종료되고 재시작되지 않는다. 그러나 잡은 종료된 항목을 대체하기 위해 새 파드를 생성한다. 따라서, 애플리케이션에 단일 파드만 필요한 경우에도 베어 파드 대신 잡을 사용하는 것을 권장한다.
레플리케이션 컨트롤러
잡은 레플리케이션 컨트롤러를 보완한다. 레플리케이션 컨트롤러는 종료하지 않을 파드(예: 웹 서버)를 관리하고, 잡은 종료될 것으로 예상되는 파드(예: 배치 작업)를 관리한다.
파드 라이프사이클에서 설명한 것처럼, 잡
은 오직
OnFailure
또는 Never
와 같은 RestartPolicy
를 사용하는 파드에만 적절하다.
(참고: RestartPolicy
가 설정되지 않은 경우에는 기본값은 Always
이다.)
단일 잡으로 컨트롤러 파드 시작
또 다른 패턴은 단일 잡이 파드를 생성한 후 다른 파드들을 생성해서 해당 파드들에 일종의 사용자 정의 컨트롤러 역할을 하는 것이다. 이를 통해 최대한의 유연성을 얻을 수 있지만, 시작하기에는 다소 복잡할 수 있으며 쿠버네티스와의 통합성이 낮아진다.
이 패턴의 한 예시는 파드를 시작하는 잡이다. 파드는 스크립트를 실행해서 스파크(Spark) 마스터 컨트롤러 (스파크 예시를 본다)를 시작하고, 스파크 드라이버를 실행한 다음, 정리한다.
이 접근 방식의 장점은 전체 프로세스가 잡 오브젝트의 완료를 보장하면서도, 파드 생성과 작업 할당 방법을 완전히 제어하고 유지한다는 것이다.
다음 내용
- 파드에 대해 배운다.
- 다른 방식으로 잡을 구동하는 방법에 대해서 읽는다.
- 작업 대기열을 사용한 거친 병렬 처리
- 작업 대기열을 사용한 정밀 병렬 처리
- 병렬 처리를 위한 정적 작업 할당으로 인덱스된 잡 사용
- 템플릿 기반으로 복수의 잡 생성: 확장을 사용한 병렬 처리
- 완료된 잡을 자동으로 정리 섹션 내 링크를 따라서 클러스터가 완료되거나 실패된 태스크를 어떻게 정리하는지에 대해 더 배운다.
Job
은 쿠버네티스 REST API의 일부이다. 잡 API에 대해 이해하기 위해 Job 오브젝트 정의를 읽는다.- 스케줄을 기반으로 실행되는 일련의 잡을 정의하는데 사용할 수 있고, 유닉스 툴
cron
과 유사한CronJob
에 대해 읽는다. - 단계별로 구성된 예제를 통해,
podFailurePolicy
를 사용하여 재시도 가능 및 재시도 불가능 파드의 실패 처리를 하기위한 구성 방법을 연습한다.
6 - 완료된 잡 자동 정리
Kubernetes v1.23 [stable]
완료-이후-TTL(TTL-after-finished) 컨트롤러는 실행이 완료된 리소스 오브젝트의 수명을 제한하는 TTL (time to live) 메커니즘을 제공한다. TTL 컨트롤러는 잡만을 제어한다.
완료-이후-TTL 컨트롤러
완료-이후-TTL 컨트롤러는 잡만을 지원한다. 클러스터 운영자는
예시
와 같이 .spec.ttlSecondsAfterFinished
필드를 명시하여
완료된 잡(완료
또는 실패
)을 자동으로 정리하기 위해 이 기능을 사용할 수 있다.
잡의 작업이 완료된 TTL 초(sec) 후 (다른 말로는, TTL이 만료되었을 때),
완료-이후-TTL 컨트롤러는 해당 잡이 정리될 수 있다고 가정한다.
완료-이후-TTL 컨트롤러가 잡을 정리할때 잡을 연속적으로 삭제한다. 이는
의존하는 오브젝트도 해당 잡과 함께 삭제되는 것을 의미한다. 잡이 삭제되면 완료자(finalizers)와
같은 라이프 사이클 보증이 적용 된다.
TTL 초(sec)는 언제든지 설정이 가능하다. 여기에 잡 필드 중
.spec.ttlSecondsAfterFinished
를 설정하는 몇 가지 예시가 있다.
- 작업이 완료된 다음, 일정 시간 후에 자동으로 잡이 정리될 수 있도록 잡 메니페스트에 이 필드를 지정한다.
- 이미 완료된 기존 잡에 이 새 기능을 적용하기 위해서 이 필드를 설정한다.
- 어드미션 웹후크 변형 을 사용해서 잡 생성시 이 필드를 동적으로 설정 한다. 클러스터 관리자는 이것을 사용해서 완료된 잡에 대해 TTL 정책을 적용할 수 있다.
- 잡이 완료된 이후에 어드미션 웹후크 변형 을 사용해서 이 필드를 동적으로 설정하고, 잡의 상태, 레이블 등에 따라 다른 TTL 값을 선택한다.
경고
TTL 초(sec) 업데이트
TTL 기간은, 예를 들어 잡의 .spec.ttlSecondsAfterFinished
필드는
잡을 생성하거나 완료한 후에 수정할 수 있다. 그러나, 잡을
삭제할 수 있게 되면(TTL이 만료된 경우) 시스템은 TTL을 연장하기
위한 업데이트가 성공적인 API 응답을 리턴하더라도
작업이 유지되도록 보장하지 않는다.
시간 차이(Skew)
완료-이후-TTL 컨트롤러는 쿠버네티스 잡에 저장된 타임스탬프를 사용해서 TTL의 만료 여부를 결정하기 때문에, 이 기능은 클러스터 간의 시간 차이에 민감하며, 시간 차이에 의해서 완료-이후-TTL 컨트롤러가 잘못된 시간에 잡 오브젝트를 정리하게 될 수 있다.
시계가 항상 정확한 것은 아니지만, 그 차이는 아주 작아야 한다. 0이 아닌 TTL을 설정할때는 이 위험에 대해 유의해야 한다.
다음 내용
7 - 크론잡
Kubernetes v1.21 [stable]
크론잡은 반복 일정에 따라 잡을 만든다.
하나의 크론잡 오브젝트는 크론탭 (크론 테이블) 파일의 한 줄과 같다. 크론잡은 잡을 크론 형식으로 쓰여진 주어진 일정에 따라 주기적으로 동작시킨다.
주의:
모든 크론잡 일정:
시간은
kube-controller-manager의 시간대를 기준으로 한다.
컨트롤 플레인이 파드 또는 베어 컨테이너에서 kube-controller-manager를 실행하는 경우, kube-controller-manager 컨테이너에 설정된 시간대는 크론잡 컨트롤러가 사용하는 시간대로 결정한다.
주의:
v1 CronJob API은 위에서 설명한 타임존 설정을 공식적으로 지원하지는 않는다.
CRON_TZ
또는 TZ
와 같은 변수를 설정하는 것은 쿠버네티스 프로젝트에서 공식적으로 지원하지는 않는다.
CRON_TZ
또는 TZ
와 같은 변수를 설정하는 것은
크론탭을 파싱하고 다음 잡 생성 시간을 계산하는 내부 라이브러리의 구현 상세사항이다.
프로덕션 클러스터에서는 사용을 권장하지 않는다.
크론잡 리소스에 대한 매니페스트를 생성할 때에는 제공하는 이름이 유효한 DNS 서브도메인 이름이어야 한다. 이름은 52자 이하여야 한다. 이는 크론잡 컨트롤러는 제공된 잡 이름에 11자를 자동으로 추가하고, 작업 이름의 최대 길이는 63자라는 제약 조건이 있기 때문이다.
크론잡
크론잡은 백업, 리포트 생성 등의 정기적 작업을 수행하기 위해 사용된다. 각 작업은 무기한 반복되도록 구성해야 한다(예: 1일/1주/1달마다 1회). 작업을 시작해야 하는 해당 간격 내 특정 시점을 정의할 수 있다.
예시
이 크론잡 매니페스트 예제는 현재 시간과 hello 메시지를 1분마다 출력한다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
(크론잡으로 자동화된 작업 실행하기는 이 예시를 더 자세히 설명한다.)
크론 스케줄 문법
# ┌───────────── 분 (0 - 59)
# │ ┌───────────── 시 (0 - 23)
# │ │ ┌───────────── 일 (1 - 31)
# │ │ │ ┌───────────── 월 (1 - 12)
# │ │ │ │ ┌───────────── 요일 (0 - 6) (일요일부터 토요일까지;
# │ │ │ │ │ 특정 시스템에서는 7도 일요일)
# │ │ │ │ │ 또는 sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# * * * * *
항목 | 설명 | 상응 표현 |
---|---|---|
@yearly (or @annually) | 매년 1월 1일 자정에 실행 | 0 0 1 1 * |
@monthly | 매월 1일 자정에 실행 | 0 0 1 * * |
@weekly | 매주 일요일 자정에 실행 | 0 0 * * 0 |
@daily (or @midnight) | 매일 자정에 실행 | 0 0 * * * |
@hourly | 매시 0분에 시작 | 0 * * * * |
예를 들면, 다음은 해당 작업이 매주 금요일 자정에 시작되어야 하고, 매월 13일 자정에도 시작되어야 한다는 뜻이다.
0 0 13 * 5
크론잡 스케줄 표현을 생성하기 위해서 crontab.guru와 같은 웹 도구를 사용할 수도 있다.
타임 존
크론잡에 타임 존이 명시되어 있지 않으면, kube-controller-manager는 로컬 타임 존을 기준으로 스케줄을 해석한다.
Kubernetes v1.25 [beta]
CronJobTimeZone
기능 게이트를 활성화하면,
크론잡에 대해 타임 존을 명시할 수 있다(기능 게이트를 활성화하지 않거나,
타임 존에 대한 실험적 지원을 제공하지 않는 쿠버네티스 버전을 사용 중인 경우,
클러스터의 모든 크론잡은 타임 존이 명시되지 않은 것으로 동작한다).
이 기능을 활성화하면, spec.timeZone
을 유효한 타임 존으로 지정할 수 있다.
예를 들어, spec.timeZone: "Etc/UTC"
와 같이 설정하면 쿠버네티스는 협정 세계시를 기준으로 스케줄을 해석한다.
Go 표준 라이브러리의 타임 존 데이터베이스가 바이너리로 인클루드되며, 시스템에서 외부 데이터베이스를 사용할 수 없을 때 폴백(fallback)으로 사용된다.
크론잡의 한계
크론잡은 일정의 실행시간 마다 약 한 번의 잡 오브젝트를 생성한다. "약" 이라고 하는 이유는 특정 환경에서는 두 개의 잡이 만들어지거나, 잡이 생성되지 않기도 하기 때문이다. 보통 이렇게 하지 않도록 해야겠지만, 완벽히 그럴 수는 없다. 따라서 잡은 멱등원 이 된다.
만약 startingDeadlineSeconds
가 큰 값으로 설정되거나, 설정되지 않고(디폴트 값),
concurrencyPolicy
가 Allow
로 설정될 경우, 잡은 항상 적어도 한 번은
실행될 것이다.
주의:
startingDeadlineSeconds
가 10초 미만의 값으로 설정되면, 크론잡이 스케줄되지 않을 수 있다. 이는 크론잡 컨트롤러가 10초마다 항목을 확인하기 때문이다.모든 크론잡에 대해 크론잡 컨트롤러 는 마지막 일정부터 지금까지 얼마나 많은 일정이 누락되었는지 확인한다. 만약 100회 이상의 일정이 누락되었다면, 잡을 실행하지 않고 아래와 같은 에러 로그를 남긴다.
Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.
중요한 것은 만약 startingDeadlineSeconds
필드가 설정이 되면(nil
이 아닌 값으로), 컨트롤러는 마지막 일정부터 지금까지 대신 startingDeadlineSeconds
값에서 몇 개의 잡이 누락되었는지 카운팅한다. 예를 들면, startingDeadlineSeconds
가 200
이면, 컨트롤러는 최근 200초 내 몇 개의 잡이 누락되었는지 카운팅한다.
크론잡은 정해진 시간에 생성되는데 실패하면 누락(missed)된 것으로 집계된다. 예를 들어 concurrencyPolicy
가 Forbid
로 설정되어 있고 이전 크론잡이 여전히 실행 중인 상태라면, 크론잡은 일정에 따라 시도되었다가 생성을 실패하고 누락된 것으로 집계될 것이다.
즉, 크론잡이 08:30:00
에 시작하여 매 분마다 새로운 잡을 실행하도록 설정이 되었고,
startingDeadlineSeconds
값이 설정되어 있지 않는다고 가정해보자. 만약 크론잡 컨트롤러가
08:29:00
부터 10:21:00
까지 고장이 나면, 일정을 놓친 작업 수가 100개를 초과하여 잡이 실행되지 않을 것이다.
이 개념을 더 자세히 설명하자면, 크론잡이 08:30:00
부터 매 분 실행되는 일정으로 설정되고,
startingDeadlineSeconds
이 200이라고 가정한다. 크론잡 컨트롤러가
전의 예시와 같이 고장났다고 하면 (08:29:00
부터 10:21:00
까지), 잡은 10:22:00 부터 시작될 것이다. 이 경우, 컨트롤러가 마지막 일정부터 지금까지가 아니라, 최근 200초 안에 얼마나 놓쳤는지 체크하기 때문이다. (여기서는 3번 놓쳤다고 체크함)
크론잡은 오직 그 일정에 맞는 잡 생성에 책임이 있고, 잡은 그 잡이 대표하는 파드 관리에 책임이 있다.
컨트롤러 버전
쿠버네티스 v1.21부터 크론잡 컨트롤러의 두 번째 버전이
기본 구현이다. 기본 크론잡 컨트롤러를 비활성화하고
대신 원래 크론잡 컨트롤러를 사용하려면, CronJobControllerV2
기능 게이트
플래그를 kube-controller-manager에 전달하고,
이 플래그를 false
로 설정한다. 예를 들면, 다음과 같다.
--feature-gates="CronJobControllerV2=false"
다음 내용
- 크론잡이 의존하고 있는 파드와 잡 두 개념에 대해 배운다.
- 크론잡
.spec.schedule
필드의 형식에 대해서 읽는다. - 크론잡을 생성하고 다루기 위한 지침 및 크론잡 매니페스트의 예제로 크론잡으로 자동화된 작업 실행를 읽는다.
- 실패했거나 완료된 잡을 자동으로 정리하도록 하려면, 완료된 잡을 자동으로 정리를 확인한다.
CronJob
은 쿠버네티스 REST API의 일부이다. CronJob 오브젝트 정의를 읽고 쿠버네티스 크론잡 API에 대해 이해한다.
8 - 레플리케이션 컨트롤러
레플리케이션컨트롤러 는 언제든지 지정된 수의 파드 레플리카가 실행 중임을 보장한다. 다시 말하면, 레플리케이션 컨트롤러는 파드 또는 동일 종류의 파드의 셋이 항상 기동되고 사용 가능한지 확인한다.
레플리케이션 컨트롤러의 동작방식
파드가 너무 많으면 레플리케이션 컨트롤러가 추가적인 파드를 제거한다. 너무 적으면 레플리케이션 컨트롤러는 더 많은 파드를 시작한다. 수동으로 생성된 파드와 달리 레플리케이션 컨트롤러가 유지 관리하는 파드는 실패하거나 삭제되거나 종료되는 경우 자동으로 교체된다. 예를 들어, 커널 업그레이드와 같이 파괴적인 유지 보수 작업을 하고 난 이후의 노드에서 파드가 다시 생성된다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용해야 한다. 레플리케이션 컨트롤러는 프로세스 감시자(supervisor)와 유사하지만 단일 노드에서 개별 프로세스를 감시하는 대신 레플리케이션 컨트롤러는 여러 노드에서 여러 파드를 감시한다.
레플리케이션 컨트롤러는 디스커션에서 종종 "rc"로 축약되며 kubectl 명령에서 숏컷으로 사용된다.
간단한 경우는 하나의 레플리케이션 컨트롤러 오브젝트를 생성하여 한 개의 파드 인스턴스를 영구히 안정적으로 실행하는 것이다. 보다 복잡한 사용 사례는 웹 서버와 같이 복제된 서비스의 동일한 레플리카를 여러 개 실행하는 것이다.
레플리케이션 컨트롤러 예제 실행
레플리케이션 컨트롤러 예제의 config는 nginx 웹서버의 복사본 세 개를 실행한다.
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
예제 파일을 다운로드한 후 다음 명령을 실행하여 예제 작업을 실행하라.
kubectl apply -f https://k8s.io/examples/controllers/replication.yaml
출력 결과는 다음과 같다.
replicationcontroller/nginx created
다음 명령을 사용하여 레플리케이션 컨트롤러의 상태를 확인하자.
kubectl describe replicationcontrollers/nginx
출력 결과는 다음과 같다.
Name: nginx
Namespace: default
Selector: app=nginx
Labels: app=nginx
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- ---- ------ -------
20s 20s 1 {replication-controller } Normal SuccessfulCreate Created pod: nginx-qrm3m
20s 20s 1 {replication-controller } Normal SuccessfulCreate Created pod: nginx-3ntk0
20s 20s 1 {replication-controller } Normal SuccessfulCreate Created pod: nginx-4ok8v
이제 세 개의 파드가 생성되었으나 아직 이미지가 풀(pull)되지 않아서 어떤 파드도 시작되지 않았다. 조금 지난 후에 같은 명령이 다음과 같이 보일 것이다.
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
레플리케이션 컨트롤러에 속한 모든 파드를 머신이 읽을 수 있는 형식으로 나열하기 위해 다음과 같은 명령을 사용할 수 있다.
pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods
출력 결과는 다음과 같다.
nginx-3ntk0 nginx-4ok8v nginx-qrm3m
여기서 셀렉터는 레플리케이션컨트롤러(kubectl describe
의 출력에서 보인)의 셀렉터와 같고,
다른 형식의 파일인 replication.yaml
의 것과 동일하다. --output=jsonpath
은
반환된 목록의 각 파드의 이름을 출력하도록 하는 옵션이다.
레플리케이션 컨트롤러의 Spec 작성
다른 모든 쿠버네티스 컨피그와 마찬가지로 레플리케이션 컨트롤러는 apiVersion
, kind
, metadata
와 같은 필드가 필요하다.
레플리케이션 컨트롤러 오브젝트의 이름은 유효한
DNS 서브도메인 이름이어야 한다.
환경설정 파일의 동작에 관련된 일반적인 정보는 쿠버네티스 오브젝트 관리를 참고한다.
레플리케이션 컨트롤러는 또한 .spec
section도 필요하다.
파드 템플릿
.spec.template
는 오직 .spec
필드에서 요구되는 것이다.
.spec.template
는 파드 템플릿이다. 정확하게 파드 스키마와 동일하나, 중첩되어 있고 apiVersion
혹은 kind
를 갖지 않는다.
파드에 필요한 필드 외에도 레플리케이션 컨트롤러의 파드 템플릿은 적절한 레이블과 적절한 재시작 정책을 지정해야 한다. 레이블의 경우 다른 컨트롤러와 중첩되지 않도록 하라. 파드 셀렉터를 참조하라.
오직 Always
와 동일한 .spec.template.spec.restartPolicy
만 허용되며, 특별히 지정되지 않으면 기본값이다.
로컬 컨테이너의 재시작의 경우, 레플리케이션 컨트롤러는 노드의 에이전트에게 위임한다. 예를 들어 Kubelet이다.
레플리케이션 컨트롤러에서 레이블
레플리케이션 컨트롤러 자체는 레이블 (.metadata.labels
) 을 가질 수 있다. 일반적으로 이것을 .spec.template.metadata.labels
와 동일하게 설정할 것이다. .metadata.labels
가 지정되어 있지 않은 경우,
기본은 .spec.template.metadata.labels
이다. 하지만 레이블은
다른 것이 허용되며, .metadata.labels
라벨은 레플리케이션 컨트롤러의
동작에 영향을 미치지 않는다.
파드 셀렉터
.spec.selector
필드는 레이블 셀렉터이다. 레플리케이션 컨트롤러는 셀렉터와 일치하는 레이블이 있는 모든 파드를 관리한다.
직접 생성하거나 삭제된 파드와 다른 사람이나 프로세스가 생성하거나
삭제한 파드를 구분하지 않는다. 이렇게 하면 실행중인 파드에 영향을 주지 않고
레플리케이션 컨트롤러를 교체할 수 있다.
지정된 경우 .spec.template.metadata.labels
은
.spec.selector
와 동일해야 하며 그렇지 않으면 API에 의해 거부된다. .spec.selector
가 지정되지 않은 경우 기본값은
.spec.template.metadata.labels
이다.
또한 일반적으로 이 셀렉터와 레이블이 일치하는 파드를 직접 다른 레플리케이션 컨트롤러 또는 잡과 같은 다른 컨트롤러로 작성해서는 안된다. 그렇게 하면 레플리케이션 컨트롤러는 다른 파드를 생성했다고 생각한다. 쿠버네티스는 이런 작업을 중단해 주지 않는다.
중첩된 셀렉터들을 갖는 다수의 컨트롤러들을 종료하게 되면, 삭제된 것들은 스스로 관리를 해야 한다 (아래를 참조).
다수의 레플리카
.spec.replicas
를 동시에 실행하고 싶은 파드의 수로 설정함으로써
실행할 파드의 수를 지정할 수 있다. 레플리카가 증가 또는 감소한 경우 또는
파드가 정상적으로 종료되고 교체가 일찍 시작되는 경우라면
언제든지 실행중인 수가 더 높거나 낮을 수 있다.
.spec.replicas
를 지정하지 않으면 기본값은 1이다.
레플리케이션 컨트롤러 사용하기
레플리케이션 컨트롤러와 레플리케이션 컨트롤러의 파드 삭제
레플리케이션 컨트롤러와 레플리케이션의 모든 파드를 삭제하려면 kubectl delete
를 사용하라.
Kubectl은 레플리케이션 컨트롤러를 0으로 스케일하고 레플리케이션 컨트롤러 자체를
삭제하기 전에 각 파드를 삭제하기를 기다린다. 이 kubectl 명령이 인터럽트되면 다시 시작할 수 있다.
REST API나 클라이언트 라이브러리를 사용하는 경우 명시적으로 단계를 수행해야 한다(레플리카를 0으로 스케일하고 파드의 삭제를 기다린 이후, 레플리케이션 컨트롤러를 삭제).
레플리케이션 컨트롤러만 삭제
해당 파드에 영향을 주지 않고 레플리케이션 컨트롤러를 삭제할 수 있다.
kubectl을 사용하여, kubectl delete
에 옵션으로 --cascade=orphan
을 지정하라.
REST API나 클라이언트 라이브러리를 사용하는 경우 레플리케이션 컨트롤러 오브젝트를 삭제하라.
원본이 삭제되면 대체할 새로운 레플리케이션 컨트롤러를 생성하여 교체할 수 있다. 오래된 파드와 새로운 파드의 .spec.selector
가 동일하다면,
새로운 레플리케이션 컨트롤러는 오래된 파드를 채택할 것이다. 그러나 기존 파드를
새로운 파드 템플릿과 일치시키려는 노력은 하지 않을 것이다.
새로운 spec에 대한 파드를 제어된 방법으로 업데이트하려면 롤링 업데이트를 사용하라.
레플리케이션 컨트롤러에서 파드 격리
파드는 레이블을 변경하여 레플리케이션 컨트롤러의 대상 셋에서 제거될 수 있다. 이 기술은 디버깅과 데이터 복구를 위해 서비스에서 파드를 제거하는 데 사용될 수 있다. 이 방법으로 제거된 파드는 자동으로 교체된다 (레플리카 수가 변경되지 않는다고 가정).
일반적인 사용법 패턴
다시 스케줄하기
위에서 언급했듯이, 실행하려는 파드가 한 개 혹은 1000개이든 관계없이 레플리케이션 컨트롤러는 노드 실패 또는 파드 종료시 지정된 수의 파드가 존재하도록 보장한다 (예 : 다른 제어 에이전트에 의한 동작으로 인해).
스케일링
레플리케이션컨트롤러는 replicas
필드를 업데이트하여, 수동으로 또는 오토 스케일링 제어 에이전트를 통해, 레플리카의 수를 늘리거나 줄일 수 있다.
롤링 업데이트
레플리케이션 컨트롤러는 파드를 하나씩 교체함으로써 서비스에 대한 롤링 업데이트를 쉽게 하도록 설계되었다.
#1353에서 설명한 것처럼, 권장되는 접근법은 1 개의 레플리카를 가진 새로운 레플리케이션 컨트롤러를 생성하고 새로운 (+1) 컨트롤러 및 이전 (-1) 컨트롤러를 차례대로 스케일한 후 0개의 레플리카가 되면 이전 컨트롤러를 삭제하는 것이다. 예상치 못한 오류와 상관없이 파드 세트를 예측 가능하게 업데이트한다.
이상적으로 롤링 업데이트 컨트롤러는 애플리케이션 준비 상태를 고려하며 주어진 시간에 충분한 수의 파드가 생산적으로 제공되도록 보장할 것이다.
두 레플리케이션 컨트롤러는 일반적으로 롤링 업데이트를 동기화 하는 이미지 업데이트이기 때문에 파드의 기본 컨테이너 이미지 태그와 같이 적어도 하나의 차별화된 레이블로 파드를 생성해야 한다.
다수의 릴리스 트랙
롤링 업데이트가 진행되는 동안 다수의 애플리케이션 릴리스를 실행하는 것 외에도 다수의 릴리스 트랙을 사용하여 장기간에 걸쳐 또는 연속적으로 실행하는 것이 일반적이다. 트랙은 레이블 별로 구분된다.
예를 들어, 서비스는 tier in (frontend), environment in (prod)
이 있는 모든 파드를 대상으로 할 수 있다. 이제 이 계층을 구성하는 10 개의 복제된 파드가 있다고 가정해 보자. 하지만 이 구성 요소의 새로운 버전을 '카나리' 하기를 원한다. 대량의 레플리카에 대해 replicas
를 9로 설정하고 tier=frontend, environment=prod, track=stable
레이블을 설정한 레플리케이션 컨트롤러와, 카나리에 replicas
가 1로 설정된 다른 레플리케이션 컨트롤러에 tier=frontend, environment=prod, track=canary
라는 레이블을 설정할 수 있다. 이제 이 서비스는 카나리와 카나리 이외의 파드 모두를 포함한다. 그러나 레플리케이션 컨트롤러를 별도로 조작하여 테스트하고 결과를 모니터링하는 등의 작업이 혼란스러울 수 있다.
서비스와 레플리케이션컨트롤러 사용
하나의 서비스 뒤에 여러 개의 레플리케이션컨트롤러가 있을 수 있다. 예를 들어 일부 트래픽은 이전 버전으로 이동하고 일부는 새 버전으로 이동한다.
레플리케이션컨트롤러는 자체적으로 종료되지 않지만, 서비스만큼 오래 지속될 것으로 기대되지는 않는다. 서비스는 여러 레플리케이션컨트롤러에 의해 제어되는 파드로 구성될 수 있으며, 서비스 라이프사이클 동안(예를 들어, 서비스를 실행하는 파드 업데이트 수행을 위해) 많은 레플리케이션컨트롤러가 생성 및 제거될 것으로 예상된다. 서비스 자체와 클라이언트 모두 파드를 유지하는 레플리케이션컨트롤러를 의식하지 않는 상태로 남아 있어야 한다.
레플리케이션을 위한 프로그램 작성
레플리케이션 컨트롤러에 의해 생성된 파드는 해당 구성이 시간이 지남에 따라 이질적이 될 수 있지만 균일하고 의미상 동일하도록 설계되었다. 이는 레플리카된 상태 스테이트리스 서버에 적합하지만 레플리케이션 컨트롤러를 사용하여 마스터 선출, 샤드 및 워크-풀 애플리케이션의 가용성을 유지할 수도 있다. RabbitMQ work queues와 같은 애플리케이션은 안티패턴으로 간주되는 각 파드의 구성에 대한 정적/일회성 사용자 정의와 반대로 동적 작업 할당 메커니즘을 사용해야 한다. 리소스의 수직 자동 크기 조정 (예 : CPU 또는 메모리)과 같은 수행된 모든 파드 사용자 정의는 레플리케이션 컨트롤러 자체와 달리 다른 온라인 컨트롤러 프로세스에 의해 수행되어야 한다.
레플리케이션 컨트롤러의 책임
레플리케이션 컨트롤러는 의도한 수의 파드가 해당 레이블 셀렉터와 일치하고 동작하는지를 확인한다. 현재, 종료된 파드만 해당 파드의 수에서 제외된다. 향후 시스템에서 사용할 수 있는 readiness 및 기타 정보가 고려될 수 있으며 교체 정책에 대한 통제를 더 추가 할 수 있고 외부 클라이언트가 임의로 정교한 교체 또는 스케일 다운 정책을 구현하기 위해 사용할 수 있는 이벤트를 내보낼 계획이다.
레플리케이션 컨트롤러는 이 좁은 책임에 영원히 제약을 받는다. 그 자체로는 준비성 또는 활성 프로브를 실행하지 않을 것이다. 오토 스케일링을 수행하는 대신, 외부 오토 스케일러 (#492에서 논의된)가 레플리케이션 컨트롤러의 replicas
필드를 변경함으로써 제어되도록 의도되었다. 레플리케이션 컨트롤러에 스케줄링 정책 (예를 들어 spreading)을 추가하지 않을 것이다. 오토사이징 및 기타 자동화 된 프로세스를 방해할 수 있으므로 제어된 파드가 현재 지정된 템플릿과 일치하는지 확인해야 한다. 마찬가지로 기한 완료, 순서 종속성, 구성 확장 및 기타 기능은 다른 곳에 속한다. 대량의 파드 생성 메커니즘 (#170)까지도 고려해야 한다.
레플리케이션 컨트롤러는 조합 가능한 빌딩-블록 프리미티브가 되도록 고안되었다. 향후 사용자의 편의를 위해 더 상위 수준의 API 및/또는 도구와 그리고 다른 보완적인 기본 요소가 그 위에 구축 될 것으로 기대한다. 현재 kubectl이 지원하는 "매크로" 작업 (실행, 스케일)은 개념 증명의 예시이다. 예를 들어 Asgard와 같이 레플리케이션 컨트롤러, 오토 스케일러, 서비스, 정책 스케줄링, 카나리 등을 관리할 수 있다.
API 오브젝트
레플리케이션 컨트롤러는 쿠버네티스 REST API의 최상위 수준의 리소스이다. API 오브젝트에 대한 더 자세한 것은 ReplicationController API object 에서 찾을 수 있다.
레플리케이션 컨트롤러의 대안
레플리카셋
ReplicaSet
은 새로운 집합성 기준 레이블 셀렉터이다.
이것은 주로 디플로이먼트에 의해 파드의 생성, 삭제 및 업데이트를 오케스트레이션 하는 메커니즘으로 사용된다.
사용자 지정 업데이트 조정이 필요하거나 업데이트가 필요하지 않은 경우가 아니면 레플리카셋을 직접 사용하는 대신 디플로이먼트를 사용하는 것이 좋다.
디플로이먼트 (권장됨)
Deployment
는 기본 레플리카셋과 그 파드를 업데이트하는 상위 수준의 API 오브젝트이다. 선언적이며, 서버 사이드이고, 추가 기능이 있기 때문에 롤링 업데이트 기능을 원한다면 디플로이먼트를 권장한다.
베어 파드
사용자가 직접 파드를 만든 경우와 달리 레플리케이션 컨트롤러는 노드 오류 또는 커널 업그레이드와 같은 장애가 발생하는 노드 유지 관리의 경우와 같이 어떤 이유로든 삭제되거나 종료된 파드를 대체한다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용하는 것이 좋다. 프로세스 관리자와 비슷하게 생각하면, 단지 단일 노드의 개별 프로세스가 아닌 여러 노드에서 여러 파드를 감독하는 것이다. 레플리케이션 컨트롤러는 로컬 컨테이너가 kubelet과 같은 노드의 에이전트로 재시작하도록 위임한다.
잡
자체적으로 제거될 것으로 예상되는 파드 (즉, 배치 잡)의 경우
레플리케이션 컨트롤러 대신 Job
을 사용하라.
데몬셋
머신 모니터링이나 머신 로깅과 같은 머신 레벨 기능을 제공하는 파드에는 레플리케이션 컨트롤러 대신
DaemonSet
을 사용하라. 이런 파드들의 수명은 머신의 수명에 달려 있다.
다른 파드가 시작되기 전에 파드가 머신에서 실행되어야 하며,
머신이 재부팅/종료 준비가 되어 있을 때 안전하게 종료된다.
다음 내용
- 파드에 대해 배운다.
- 레플리케이션 컨트롤러를 대신하는 디플로이먼트에 대해 배운다.
ReplicationController
는 쿠버네티스 REST API의 일부이다. 레플리케이션 컨트롤러 API에 대해 이해하기 위해 ReplicationController 오브젝트 정의를 읽는다.