kubectl patch로 API 객체 제자리 업데이트하기

kubectl patch로 API 객체 제자리 업데이트하기 (Update API Objects in Place Using kubectl patch)

이 작업은 kubectl patch를 사용해 API 객체를 제자리에서(in place) 업데이트하는 방법을 보여 줘요. 이 작업의 연습은 전략적 병합 패치(strategic merge patch)와 JSON 병합 패치(JSON merge patch)를 시연해요.

출처: 문서

본문

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 명령줄 도구가 클러스터와 통신하도록 설정돼 있어야 해요. 이 튜토리얼은 제어 플레인 호스트 역할을 하지 않는 노드가 최소 두 개 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube를 사용하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용해 만들 수 있어요.

  • iximiuz Labs
  • Killercoda
  • KodeKloud

버전을 확인하려면 kubectl version을 입력하세요.

전략적 병합 패치로 Deployment 업데이트하기

레플리카가 두 개인 Deployment의 구성 파일은 다음과 같아요. 각 레플리카는 컨테이너가 하나인 파드예요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: patch-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: patch-demo-ctr
        image: nginx
      tolerations:
      - effect: NoSchedule
        key: dedicated
        value: test-team

Deployment를 만들어요.

kubectl apply -f https://k8s.io/examples/application/deployment-patch.yaml

Deployment와 연결된 파드를 확인해요.

kubectl get pods

출력은 Deployment에 파드가 두 개 있음을 보여 줘요. 1/1은 각 파드에 컨테이너가 하나 있음을 나타내요.

NAME                        READY     STATUS    RESTARTS   AGE
patch-demo-28633765-670qr   1/1       Running   0          23s
patch-demo-28633765-j5qs3   1/1       Running   0          23s

실행 중인 파드의 이름을 메모해 두세요. 나중에 이 파드들이 종료되고 새 것으로 교체되는 것을 보게 될 거예요.

이 시점에서 각 파드에는 nginx 이미지를 실행하는 컨테이너가 하나 있어요. 이제 각 파드에 nginx를 실행하는 컨테이너와 redis를 실행하는 컨테이너, 두 개의 컨테이너를 두고 싶다고 해 보죠.

이 내용이 담긴 patch-file.yaml 파일을 만드세요.

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-2
        image: redis

Deployment를 패치해요.

kubectl patch deployment patch-demo --patch-file patch-file.yaml

패치된 Deployment를 확인해요.

kubectl get deployment patch-demo --output yaml

출력은 Deployment의 PodSpec에 컨테이너가 두 개 있음을 보여 줘요.

containers:
- image: redis
  imagePullPolicy: Always
  name: patch-demo-ctr-2
  ...
- image: nginx
  imagePullPolicy: Always
  name: patch-demo-ctr
  ...

패치된 Deployment와 연결된 파드를 확인해요.

kubectl get pods

출력은 실행 중인 파드가 이전에 실행 중이던 파드와 다른 이름을 가짐을 보여 줘요. Deployment가 이전 파드를 종료하고 업데이트된 Deployment 스펙을 준수하는 새 파드 두 개를 만들었어요. 2/2는 각 파드에 컨테이너가 두 개 있음을 나타내요.

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1081991389-2wrn5   2/2       Running   0          1m
patch-demo-1081991389-jmg7b   2/2       Running   0          1m

patch-demo 파드 중 하나를 더 자세히 살펴보아요.

kubectl get pod <your-pod-name> --output yaml

출력은 파드에 nginx를 실행하는 컨테이너와 redis를 실행하는 컨테이너, 두 개의 컨테이너가 있음을 보여 줘요.

containers:
- image: redis
  ...
- image: nginx
  ...

전략적 병합 패치에 대한 참고 사항

앞선 연습에서 한 패치를 전략적 병합 패치(strategic merge patch)라고 해요. 패치가 containers 목록을 교체하지 않았다는 점을 주목하세요. 대신 목록에 새 Container를 추가했어요. 다시 말해, 패치의 목록이 기존 목록과 병합됐어요. 이것이 전략적 병합 패치를 목록에 사용할 때 항상 일어나는 일은 아니에요. 어떤 경우에는 목록이 병합되지 않고 교체돼요.

전략적 병합 패치에서 목록은 패치 전략(patch strategy)에 따라 교체되거나 병합돼요. 패치 전략은 쿠버네티스 소스 코드의 필드 태그에서 patchStrategy 키의 값으로 지정돼요. 예를 들어 PodSpec 구조체의 Containers 필드는 merge 패치 전략을 가져요.

type PodSpec struct {
  ...
  Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
  ...
}

OpenAPI 사양에서도 패치 전략을 볼 수 있어요.

"io.k8s.api.core.v1.PodSpec": {
    ...,
    "containers": {
        "description": "List of containers belonging to the pod.  ...."
    },
    "x-kubernetes-patch-merge-key": "name",
    "x-kubernetes-patch-strategy": "merge"
}

쿠버네티스 API 문서에서도 패치 전략을 볼 수 있어요.

이 내용이 담긴 patch-file-tolerations.yaml 파일을 만드세요.

spec:
  template:
    spec:
      tolerations:
      - effect: NoSchedule
        key: disktype
        value: ssd

Deployment를 패치해요.

kubectl patch deployment patch-demo --patch-file patch-file-tolerations.yaml

패치된 Deployment를 확인해요.

kubectl get deployment patch-demo --output yaml

출력은 Deployment의 PodSpec에 Toleration이 하나만 있음을 보여 줘요.

tolerations:
- effect: NoSchedule
  key: disktype
  value: ssd

PodSpec의 tolerations 목록이 교체됐고 병합되지 않았다는 점을 주목하세요. 이는 PodSpec의 Tolerations 필드가 필드 태그에 patchStrategy 키가 없기 때문이에요. 그래서 전략적 병합 패치는 기본 패치 전략인 replace를 사용해요.

type PodSpec struct {
  ...
  Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`
  ...
}

JSON 병합 패치로 Deployment 업데이트하기

전략적 병합 패치는 JSON 병합 패치(JSON merge patch)와 달라요. JSON 병합 패치에서는 목록을 업데이트하려면 전체 새 목록을 지정해야 해요. 그리고 새 목록이 기존 목록을 완전히 교체해요.

kubectl patch 명령에는 다음 값 중 하나로 설정할 수 있는 type 파라미터가 있어요.

파라미터 값 병합 유형
json JSON Patch, RFC 6902
merge JSON Merge Patch, RFC 7386
strategic 전략적 병합 패치

JSON patch와 JSON merge patch의 비교는 'JSON Patch and JSON Merge Patch'를 참고하세요.

type 파라미터의 기본값은 strategic이에요. 그래서 앞선 연습에서는 전략적 병합 패치를 한 거예요.

다음으로 같은 Deployment에 JSON 병합 패치를 해요. 이 내용이 담긴 patch-file-2.yaml 파일을 만드세요.

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-3
        image: gcr.io/google-samples/hello-app:2.0

패치 명령에서 typemerge로 설정해요.

kubectl patch deployment patch-demo --type merge --patch-file patch-file-2.yaml

패치된 Deployment를 확인해요.

kubectl get deployment patch-demo --output yaml

패치에서 지정한 containers 목록에는 컨테이너가 하나만 있어요. 출력은 컨테이너 하나짜리 목록이 기존 containers 목록을 교체했음을 보여 줘요.

spec:
  containers:
  - image: gcr.io/google-samples/hello-app:2.0
    ...
    name: patch-demo-ctr-3

실행 중인 파드를 나열해요.

kubectl get pods

출력에서 기존 파드가 종료되고 새 파드가 생성되었음을 볼 수 있어요. 1/1은 각 새 파드가 컨테이너를 하나만 실행 중임을 나타내요.

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1307768864-69308   1/1       Running   0          1m
patch-demo-1307768864-c86dc   1/1       Running   0          1m

retainKeys 전략으로 전략적 병합 패치 사용해 Deployment 업데이트하기

RollingUpdate 전략을 사용하는 Deployment의 구성 파일은 다음과 같아요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: retainkeys-demo
spec:
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 30%
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: retainkeys-demo-ctr
        image: nginx

Deployment를 만들어요.

kubectl apply -f https://k8s.io/examples/application/deployment-retainkeys.yaml

이 시점에서 deployment가 생성되었고 RollingUpdate 전략을 사용하고 있어요.

이 내용이 담긴 patch-file-no-retainkeys.yaml 파일을 만드세요.

spec:
  strategy:
    type: Recreate

Deployment를 패치해요.

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-no-retainkeys.yaml

출력에서 spec.strategy.rollingUpdate에 값이 정의되어 있을 때 typeRecreate로 설정할 수 없음을 볼 수 있어요.

The Deployment "retainkeys-demo" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'

type 값을 업데이트할 때 spec.strategy.rollingUpdate의 값을 제거하는 방법은 전략적 병합에 retainKeys 전략을 사용하는 것이에요.

이 내용이 담긴 patch-file-retainkeys.yaml 파일을 또 만들어요.

spec:
  strategy:
    $retainKeys:
    - type
    type: Recreate

이 패치에서 strategy 객체의 type 키만 유지하겠다고 나타내요. 따라서 패치 작업 중에 rollingUpdate가 제거될 거예요.

이 새 패치로 Deployment를 다시 패치해요.

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-retainkeys.yaml

Deployment의 내용을 검사해요.

kubectl get deployment retainkeys-demo --output yaml

출력은 Deployment의 strategy 객체에 rollingUpdate 키가 더 이상 없음을 보여 줘요.

spec:
  strategy:
    type: Recreate
  template:

retainKeys 전략의 전략적 병합 패치에 대한 참고 사항

앞선 연습에서 한 패치는 retainKeys 전략이 있는 전략적 병합 패치라고 해요. 이 방법은 다음 전략을 가진 새 지시자 $retainKeys를 도입해요.

  • 문자열 목록을 포함해요.
  • 보존해야 할 모든 필드가 $retainKeys 목록에 있어야 해요.
  • 존재하는 필드는 실시간 객체와 병합돼요.
  • 누락된 모든 필드는 패치할 때 지워져요.
  • $retainKeys 목록의 모든 필드는 패치에 존재하는 필드의 상위 집합이거나 같아야 해요.

retainKeys 전략은 모든 객체에 동작하지 않아요. 쿠버네티스 소스 코드의 필드 태그에서 patchStrategy 키의 값이 retainKeys를 포함할 때만 동작해요. 예를 들어 DeploymentSpec 구조체의 Strategy 필드는 retainKeys 패치 전략을 가져요.

type DeploymentSpec struct {
  ...
  // +patchStrategy=retainKeys
  Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" ...`
  ...
}

OpenAPI 사양에서도 retainKeys 전략을 볼 수 있어요.

"io.k8s.api.apps.v1.DeploymentSpec": {
    ...,
    "strategy": {
        "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy",
        "description": "The deployment strategy to use to replace existing pods with new ones.",
        "x-kubernetes-patch-strategy": "retainKeys"
    },
    ....
}

쿠버네티스 API 문서에서도 retainKeys 전략을 볼 수 있어요.

kubectl patch 명령의 대체 형식

kubectl patch 명령은 YAML 또는 JSON을 받아요. 패치를 파일로 또는 명령줄에서 직접 받을 수 있어요.

이 내용이 담긴 patch-file.json 파일을 만드세요.

{
   "spec": {
      "template": {
         "spec": {
            "containers": [
               {
                  "name": "patch-demo-ctr-2",
                  "image": "redis"
               }
            ]
         }
      }
   }
}

다음 명령들은 동일해요.

kubectl patch deployment patch-demo --patch-file patch-file.yaml

kubectl patch deployment patch-demo --patch 'spec:\n template:\n  spec:\n   containers:\n   - name: patch-demo-ctr-2\n     image: redis'

kubectl patch deployment patch-demo --patch-file patch-file.json

kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'

kubectl patch로 --subresource를 사용해 객체의 레플리카 수 업데이트하기

--subresource=[subresource-name] 플래그는 get, patch, edit, apply, replace 같은 kubectl 명령과 함께 사용되어, 지정한 리소스의 status, scale, resize 하위 리소스를 가져오거나 업데이트해요. status, scale, resize 하위 리소스가 있는 쿠버네티스 API 리소스(내장 및 CR) 어느 것이든 하위 리소스를 지정할 수 있어요.

예를 들어 Deployment에는 status 하위 리소스와 scale 하위 리소스가 있으므로, kubectl로 Deployment의 status 하위 리소스만 가져오거나 수정할 수 있어요.

레플리카가 두 개인 Deployment의 매니페스트는 다음과 같아요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # 템플릿과 일치하는 파드 2개를 실행하도록 deployment에 지시
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Deployment를 만들어요.

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

Deployment와 연결된 파드를 확인해요.

kubectl get pods -l app=nginx

출력에서 Deployment에 파드가 두 개 있음을 볼 수 있어요. 예를 들어:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          47s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          47s

이제 --subresource=[subresource-name] 플래그로 그 Deployment를 패치해요.

kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":3}}'

출력은 다음과 같아요.

scale.autoscaling/nginx-deployment patched

패치된 Deployment와 연결된 파드를 확인해요.

kubectl get pods -l app=nginx

출력에서 새 파드가 하나 생성되어 이제 3개의 실행 중인 파드가 있음을 볼 수 있어요.

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          107s
nginx-deployment-7fb96c846b-lxfr2   1/1     Running   0          14s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          107s

패치된 Deployment를 확인해요.

kubectl get deployment nginx-deployment -o yaml
...
spec:
  replicas: 3
  ...
status:
  ...
  availableReplicas: 3
  readyReplicas: 3
  replicas: 3

참고:

요약 (Summary)

이 연습에서 kubectl patch를 사용해 Deployment 객체의 실시간 구성을 변경했어요. Deployment 객체를 만들 때 원래 사용한 구성 파일은 변경하지 않았어요. API 객체를 업데이트하는 다른 명령으로는 kubectl annotate, kubectl edit, kubectl replace, kubectl scale, kubectl apply가 있어요.

참고:

더 알아보기 (Learn more)