워크로드 관리하기

워크로드 관리하기 (Managing Workloads)

애플리케이션을 배포하고 Service로 노출했어요. 이제 뭘 할까요? 쿠버네티스는 스케일링과 업데이트를 포함해 애플리케이션 배포를 관리하는 데 도움을 주는 여러 도구를 제공해요.

출처: 문서

본문

리소스 구성 정리하기

많은 애플리케이션은 Deployment와 함께 Service 같은 여러 리소스 생성을 요구해요. 여러 리소스의 관리는 같은 파일에 그룹화함으로써 단순화할 수 있어요 (YAML에서 ---로 구분). 예를 들어:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  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

여러 리소스는 단일 리소스와 같은 방식으로 만들 수 있어요:

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

리소스는 매니페스트에 나타나는 순서대로 생성돼요. 따라서 Service를 먼저 지정하는 것이 가장 좋아요. 그러면 스케줄러가 Deployment 같은 컨트롤러가 Service와 연관된 파드를 만들 때 그 파드를 펼칠 수 있도록 보장해 주기 때문이에요.

kubectl apply는 여러 -f 인수도 받아들여요:

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

같은 마이크로서비스 또는 애플리케이션 티어와 관련된 리소스를 같은 파일에 두고, 애플리케이션과 연관된 모든 파일을 같은 디렉터리에 그룹화하는 것을 권장해요. 애플리케이션의 티어가 DNS로 서로 바인딩된다면 스택의 모든 컴포넌트를 함께 배포할 수 있어요.

URL도 구성 소스로 지정할 수 있는데, 소스 제어 시스템의 매니페스트에서 직접 배포할 때 편리해요:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

ConfigMap을 추가하는 것 같은 더 많은 매니페스트를 정의해야 한다면 그렇게 할 수도 있어요.

외부 도구

이 섹션은 쿠버네티스에서 워크로드를 관리하는 데 사용되는 가장 일반적인 도구만 나열해요. 더 큰 목록을 보려면 CNCF Landscape에서 애플리케이션 정의와 이미지 빌드를 보세요.

Helm

Helm은 미리 구성된 쿠버네티스 리소스의 패키지를 관리하는 도구예요. 이러한 패키지는 헬름 차트(Helm charts)라고 알려져 있어요.

Kustomize

Kustomize는 구성 옵션을 추가, 제거 또는 업데이트하기 위해 쿠버네티스 매니페스트를 순회해요. 독립 실행형 바이너리와 kubectl의 기본 기능으로 모두 사용할 수 있어요.

kubectl에서의 벌크 작업

리소스 생성이 kubectl이 벌크로 수행할 수 있는 유일한 작업은 아니에요. 구성 파일에서 리소스 이름을 추출해 다른 작업도 수행할 수 있는데, 특히 만든 것과 같은 리소스를 삭제할 수 있어요:

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

두 리소스의 경우 명령줄에서 resource/name 구문을 사용해 두 리소스를 모두 지정할 수 있어요:

kubectl delete deployments/my-nginx services/my-nginx-svc

더 많은 수의 리소스에 대해서는 -l 또는 --selector로 지정된 셀렉터(레이블 쿼리)를 사용해 레이블별로 리소스를 필터링하는 것이 더 쉽다는 것을 알게 될 거예요:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

연결과 필터링

kubectl은 받아들인 것과 같은 구문으로 리소스 이름을 출력하므로, $() 또는 xargs를 사용해 작업을 연결할 수 있어요:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service/)

kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service/ | xargs -i kubectl get '{}'

출력은 다음과 비슷할 수 있어요:

NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

위 명령들로 먼저 docs/concepts/cluster-administration/nginx/ 아래의 리소스를 만들고 -o name 출력 형식으로 생성된 리소스를 출력해요(각 리소스를 resource/name으로 출력). 그런 다음 Service만 grep하고 kubectl get으로 출력해요.

로컬 파일에 대한 재귀 작업

특정 디렉터리 내 여러 하위 디렉터리에 걸쳐 리소스를 구성한다면, --filename/-f 인수와 함께 --recursive 또는 -R을 지정해 하위 디렉터리에 대해서도 재귀적으로 작업을 수행할 수 있어요.

예를 들어, 개발 환경에 필요한 모든 매니페스트를 리소스 유형별로 정리해 보관하는 project/k8s/development 디렉터리가 있다고 가정해요:

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

기본적으로 project/k8s/development에 대한 벌크 작업을 수행하면 디렉터리의 첫 번째 수준에서 멈추고 어떤 하위 디렉터리도 처리하지 않아요. 다음 명령으로 이 디렉터리의 리소스를 만들려고 했다면 오류가 발생했을 거예요:

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

대신 --filename/-f 인수와 함께 --recursive 또는 -R 명령줄 인수를 지정해요:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

--recursive 인수는 --filename/-f 인수를 받아들이는 어떤 작업과도 작동해요. 예: kubectl create, kubectl get, kubectl delete, kubectl describe, 또는 심지어 kubectl rollout.

--recursive 인수는 여러 -f 인수가 제공될 때도 작동해요:

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

kubectl에 대해 더 배우고 싶다면 명령줄 도구(kubectl)를 읽어보세요.

중단 없이 애플리케이션 업데이트하기

어느 시점에 배포된 애플리케이션을 업데이트해야 할 텐데, 보통 새 이미지나 이미지 태그를 지정해요. kubectl은 각각 다른 시나리오에 적용되는 여러 업데이트 작업을 지원해요.

앱의 여러 복사본을 실행하고, 롤아웃을 사용해 새로운 정상 파드로 트래픽을 점진적으로 옮길 수 있어요. 결국 실행 중인 모든 파드가 새 소프트웨어를 갖게 될 거예요.

이 페이지의 이 섹션은 Deployment로 애플리케이션을 만들고 업데이트하는 방법을 안내해요.

nginx 1.14.2 버전을 실행하고 있었다고 가정해 봐요:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

1개의 레플리카가 있도록 확인해요:

kubectl scale --replicas 1 deployments/my-nginx --subresource='scale' --type='merge' -p '{"spec":{"replicas": 1}}'
deployment.apps/my-nginx scaled

그리고 100%의 서지 최대값을 설정해, 롤아웃 중에 쿠버네티스가 더 많은 임시 레플리카를 추가할 수 있게 해요:

kubectl patch --type='merge' -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge": "100%" }}}}'
deployment.apps/my-nginx patched

버전 1.16.1로 업데이트하려면, kubectl edit을 사용해 .spec.template.spec.containers[0].imagenginx:1.14.2에서 nginx:1.16.1로 변경해요:

kubectl edit deployment/my-nginx

# Change the manifest to use the newer container image, then save your changes

그것이 전부예요! Deployment는 선언적으로 배포된 nginx 애플리케이션을 백그라운드에서 점진적으로 업데이트할 거예요. 업데이트 중에 제한된 수의 기존 레플리카만 내려가고, 원하는 파드 수 위로 제한된 수의 새 레플리카만 생성되는 것을 보장해요. 이것이 어떻게 일어나는지에 대한 자세한 내용은 Deployment를 방문하세요.

DaemonSet, Deployment, 또는 StatefulSet에 롤아웃을 사용할 수 있어요.

롤아웃 관리

kubectl rollout을 사용해 기존 애플리케이션의 점진적 업데이트를 관리할 수 있어요.

예를 들어:

kubectl apply -f my-deployment.yaml

# wait for rollout to finish
kubectl rollout status deployment/my-deployment --timeout 10m # 10 minute timeout

또는

kubectl apply -f backing-stateful-component.yaml

# don't wait for rollout to finish, just check the status
kubectl rollout status statefulsets/backing-stateful-component --watch=false

롤아웃을 일시 중지, 재개 또는 취소할 수도 있어요. 더 배우려면 kubectl rollout을 방문하세요.

카나리아 배포

여러 레이블이 필요한 또 다른 시나리오는 같은 컴포넌트의 다른 릴리스나 구성을 구분하는 것이에요. 새 애플리케이션 릴리스의 카나리아를(파드 템플릿의 이미지 태그로 지정) 이전 릴리스 옆에 배포해, 완전히 롤아웃하기 전에 새 릴리스가 실시간 프로덕션 트래픽을 받을 수 있게 하는 것이 일반적인 관행이에요.

예를 들어 track 레이블을 사용해 다른 릴리스를 구분할 수 있어요.

주요 안정 릴리스(primary, stable release)는 값이 stabletrack 레이블을 가질 거예요:

name: frontend
replicas: 3
...
labels:
   app: guestbook
   tier: frontend
   track: stable
...
image: gb-frontend:v3

그런 다음 다른 값(즉 canary)을 가진 track 레이블을 담은 guestbook frontend의 새 릴리스를 만들어 두 파드 집합이 겹치지 않게 할 수 있어요:

name: frontend-canary
replicas: 1
...
labels:
   app: guestbook
   tier: frontend
   track: canary
...
image: gb-frontend:v4

frontend Service는 레이블의 공통 부분 집합을 선택해(즉 track 레이블 생략) 두 레플리카 집합을 모두 포함해, 트래픽이 두 애플리케이션으로 리디렉션되게 해요:

selector:
   app: guestbook
   tier: frontend

stable과 canary 릴리스의 레플리카 수를 조정해 각 릴리스가 받는 실시간 프로덕션 트래픽의 비율을 결정할 수 있어요 (이 경우 3:1). 확신이 생기면 stable 트랙을 새 애플리케이션 릴리스로 업데이트하고 canary를 제거할 수 있어요.

무엇이 필요한지 배우려면 카나리아 배포를 사용한 릴리스 배포 튜토리얼을 따르세요.

어노테이션 업데이트하기

때로는 리소스에 어노테이션을 붙이고 싶을 수 있어요. 어노테이션은 도구나 라이브러리 같은 API 클라이언트가 검색하기 위한 임의의 비식별 메타데이터예요. 이는 kubectl annotate로 할 수 있어요. 예를 들어:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'

kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

자세한 내용은 어노테이션과 kubectl annotate를 참조하세요.

애플리케이션 스케일링하기

애플리케이션의 부하가 늘거나 줄어들 때 kubectl을 사용해 애플리케이션을 스케일링해요. 예를 들어 nginx 레플리카 수를 3에서 1로 줄이려면:

kubectl scale deployment/my-nginx --replicas=1
deployment.apps/my-nginx scaled

이제 deployment가 관리하는 파드가 하나뿐이에요.

kubectl get pods -l app=my-nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

시스템이 필요에 따라 1에서 3까지 nginx 레플리카 수를 자동으로 선택하게 하려면:

# This requires an existing source of container and Pod metrics

kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

이제 nginx 레플리카가 필요에 따라 자동으로 늘었다 줄었다 할 거예요.

자세한 내용은 kubectl scale, kubectl autoscale과 수평 파드 오토스케일러 문서를 참조하세요.

리소스의 제자리 업데이트

때로는 만든 리소스에 좁고 비파괴적인 업데이트를 하는 것이 필요할 수 있어요.

kubectl apply

자원을 소스 제어에 구성 파일 집합으로 유지하는 것을 권장해요 (configuration as code 참조). 그렇게 하면 리소스가 구성하는 코드와 함께 유지 관리되고 버전 관리될 수 있어요. 그런 다음 kubectl apply를 사용해 구성 변경 사항을 클러스터에 푸시할 수 있어요.

이 명령은 푸시하는 구성 버전을 이전 버전과 비교해, 지정하지 않은 속성에 대한 자동화된 변경을 덮어쓰지 않고 변경 사항을 적용해요.

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

기본 메커니즘에 대해 더 배우려면 서버 사이드 적용(server-side apply)을 읽어보세요.

kubectl edit

대안으로 kubectl edit으로 리소스를 업데이트할 수도 있어요:

kubectl edit deployment/my-nginx

이것은 먼저 리소스를 가져와 텍스트 편집기에서 편집한 다음 업데이트된 버전으로 리소스를 적용하는 것과 동등해요:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml

vi /tmp/nginx.yaml

# do some edit, and then save the file

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

이렇게 하면 더 중요한 변경을 더 쉽게 할 수 있어요. 편집기를 EDITOR 또는 KUBE_EDITOR 환경 변수로 지정할 수 있다는 점을 기억하세요.

자세한 내용은 kubectl edit을 참조하세요.

kubectl patch

kubectl patch를 사용해 API 객체를 제자리에서 업데이트할 수 있어요. 이 하위 명령은 JSON patch, JSON merge patch, 그리고 전략적 병합 패치(strategic merge patch)를 지원해요.

자세한 내용은 kubectl patch로 API 객체 제자리 업데이트하기를 참조하세요.

파괴적인 업데이트

어떤 경우에는 초기화되면 업데이트할 수 없는 리소스 필드를 업데이트해야 할 수 있거나, Deployment가 만든 고장난 파드를 고치는 것처럼 즉시 재귀적인 변경을 하고 싶을 수 있어요. 그러한 필드를 변경하려면 replace --force를 사용해요. 이것은 리소스를 삭제하고 다시 만들어요. 이 경우 원래 구성 파일을 수정할 수 있어요:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

더 알아보기 (Learn more)

  • 애플리케이션 내부 검사(introspection)와 디버깅에 kubectl을 사용하는 방법 배우기.