정상 파드에만 트래픽 라우팅하기: 레디니스 프로브고장을 일으킨 파드 재시작하기 : 리브니스 프로브예시. TCP 통신 및 명령 실행 프로브헬름을 이용한 안전한 애플리케이션 업데이트헬름 테스트로 실행되는 쿠버네티스 잡업그레이드 실행 전, 업그레이드가 안전한지 확인하는 용도로 잡 활용계산 리소스를 관리하여 애플리케이션 및 노드 보호하기네임스페이스 단위로 총 리소스 사용량 지정CPU 사용량 제한자기수복형 애플리케이션의 한계점
정상 파드에만 트래픽 라우팅하기: 레디니스 프로브
쿠버네티스는 파드 컨테이너가 실행 중인지 파악할 수 있지만, 컨테이너 속에서 동작하는 애플리케이션 상태가 정상인지는 알 수 없다.
컨테이너 프로브
를 통해 애플리케이션의 정상 상태 여부를 판단가능함레디니스 프로브
: 파드 컨테이너의 상태가 비정상으로 판정되면 해당 파드는 준비 상태에서 제외되며, 서비스의 활성 파드 목록에서 제외됨- 일시적인 과부하 문제를 해결하는데 적합.
- 레디니스 프로브의 확인 요청에 정상이라면 서비스에 복귀
- 비정상이면 서비스의 엔드포인트에서 제외
apiVersion: apps/v1 kind: Deployment metadata: name: numbers-api labels: kiamol: ch12 spec: replicas: 2 selector: matchLabels: app: numbers-api template: metadata: labels: app: numbers-api version: v2 spec: restartPolicy: Always containers: - name: api image: kiamol/ch03-numbers-api ports: - containerPort: 80 name: api env: - name: FailAfterCallCount value: "1" readinessProbe: httpGet: path: /healthz port: 80 periodSeconds: 5
- 쿠버네티스에는 여러 유형의 컨테이너 프로브가 있는데, 위에서는 HTTP GET 요청으로 확인함
- 만약에 레디니스 프로브에만 의존하게 되면, 파드가 서비스에서 쭉 제외된 상태가 되고, 애플리케이션 처리 용량이 저하된 상태가 지속됨 →
리브니스 프로브
로 파드를 복구할 수 있음
- 레디니스 프로브는 애플리케이션 업데이트 중에도 유용. 새로 투입된 파드가 준비 상태가 되지 않으면 롤아웃이 진행되지 않음
고장을 일으킨 파드 재시작하기 : 리브니스 프로브
- 상태 체크 메커니즘은 레디니스 프로브와 동일함. 그러나 조치는 레디니스 프로브와 다름
- 리브니스 프로브의 조치는 파드 컨테이너의 재시작
apiVersion: apps/v1 kind: Deployment metadata: name: numbers-api labels: kiamol: ch12 spec: replicas: 2 selector: matchLabels: app: numbers-api template: metadata: labels: app: numbers-api version: v3 spec: restartPolicy: Always containers: - name: api image: kiamol/ch03-numbers-api ports: - containerPort: 80 name: api env: - name: FailAfterCallCount value: "1" readinessProbe: httpGet: path: /healthz port: 80 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: 80 periodSeconds: 10 initialDelaySeconds: 10 # 첫 번째 상태 체크 전 10초간 대기 failureThreshold: 2 # 상태 체크 실패 두 번까지는 용인
- 이렇게 설정 후 , 파드가 고장을 일으키면 레디니스 프로브가 서비스에서 파드를 제외하며 그 다음에는 리브니스 프로브가 파드를 재시작해서 파드가 서비스에 복귀함
- 재시작 대상은 파드 컨테이너이므로 IP 주소 등 파드 환경은 그대로 유지됨
- 애플리케이션이 정상 상태를 오래 유지하지 못하고 계속 고장을 일으킨다면 재시작 역시 완전한 해결책이 되지 못함. 쿠버네티스가 파드를 재시작하는 횟수에도 한계가 있기 때문
예시. TCP 통신 및 명령 실행 프로브
apiVersion: apps/v1 kind: Deployment metadata: name: todo-db labels: kiamol: ch12 spec: selector: matchLabels: app: todo-db template: metadata: labels: app: todo-db spec: containers: - name: db image: postgres:11.6-alpine env: - name: POSTGRES_PASSWORD_FILE value: /secrets/postgres_password volumeMounts: - name: secret mountPath: "/secrets" - name: data mountPath: /var/lib/postgresql/data readinessProbe: tcpSocket: port: 5432 periodSeconds: 5 livenessProbe: exec: command: ["pg_isready", "-h", "localhost"] periodSeconds: 10 initialDelaySeconds: 10 volumes: - name: secret secret: secretName: todo-db-secret defaultMode: 0400 items: - key: POSTGRES_PASSWORD path: postgres_password - name: data emptyDir: {}
- TCP 소켓 통신 액션은 지정된 포트가 열려 있고 이 포트로 들어오는 트래픽을 주시하는 지 확인한 후 컨테이너 안에서 지정된 명령을 실행하는 형태로 조치를 취함
- 리브니스 프로브가 충족되지 않으면 파드를 계속 재시작 하게 되는데 이 상황이 계속되면 파드 상태가 크래시루프백오프(
CrashLoopBackOff
) 상태에 빠지게 됨. - 이 상태는 정상적으로 실행될 수 없는 파드를 계속 재시작하느라 클러스터 자원이 낭비되는 것을 막아 주는 수단
- 어떤 파드가 이 상태에 빠졌다면 스스로 수복할 수 있는 상태가 아니라는 것
헬름을 이용한 안전한 애플리케이션 업데이트
- kubectl을 이용하면 업데이트 시, 실패가 계속되면 실패된 파드가 계속 남아 있는 상황이 되면서 롤아웃이 실패함
- 동일한 실패에 대해 helm 은 작업이 실패하면 자동으로 롤백을 도와줌
# 작업이 실패할 경우 자동으로 애플리케이션이 롤백됨. 프로브의 상태 체크 실패 역시 작업 실패로 간주됨 helm install --atomic todo-list todo-list/helm/v1/todo-list/
- 헬름은 롤아웃 상태를 파악하여 지정된 기간 안에 롤아웃을 성공하지 못하면 자동으로 롤백을 진행한다.
- 롤백과 함께 새로 생성된 파드들은 제거됨
- kubectl 을 사용했을 때 처럼 계속 파드를 재시작하거나 크래시루프백오프 상태에 빠지는 일이 발생하지 않음
helm upgrade --atomic --timeout 30s todo-list/helm/v2/todo-list/
헬름 테스트로 실행되는 쿠버네티스 잡
apiVersion: batch/v1 kind: Job metadata: name: {{ .Release.Name }}-db-test labels: kiamol: ch12 annotations: "helm.sh/hook": test spec: completions: 1 backoffLimit: 0 template: spec: restartPolicy: Never containers: - name: db image: postgres:11.8-alpine env: - name: PGHOST value: {{ .Release.Name }}-db - name: PGDATABASE value: todo - name: PGUSER value: postgres - name: PGPASSWORD valueFrom: secretKeyRef: key: POSTGRES_PASSWORD name: todo-db-secret command: ["psql", "-c", "SELECT COUNT(*) FROM \"public\".\"ToDos\""]
- 애너테이션을 통해 헬름 생애 주기 중 어떤 시점에 잡을 실행해야 할지 지정함. 위 잡은 업그레이드가 끝난 후 실행됨
helm upgrade --atomic --timeout 30s todo-list todo-list/helm/v3/todo-list/ helm test --help # The test command runs the tests for a release. # The argument this command takes is the name of a deployed release. # The tests to be run are defined in the chart that was installed. # 이 명령 실행시 test job 실행됨 helm test todo-list

업그레이드 실행 전, 업그레이드가 안전한지 확인하는 용도로 잡 활용
apiVersion: batch/v1 kind: Job metadata: name: {{ .Release.Name }}-db-check labels: kiamol: ch12 annotations: "helm.sh/hook": pre-upgrade "helm.sh/hook-weight": "10" spec: completions: 1 backoffLimit: 0 template: spec: restartPolicy: Never containers: - name: db image: postgres:11.8-alpine env: - name: PGHOST value: {{ .Release.Name }}-db - name: PGDATABASE value: todo - name: PGUSER value: postgres - name: PGPASSWORD valueFrom: secretKeyRef: key: POSTGRES_PASSWORD name: todo-db-secret command: ["/scripts/check-postgres-version.sh"] volumeMounts: - name: scripts mountPath: "/scripts" volumes: - name: scripts configMap: name: {{ .Release.Name }}-db-check-scripts defaultMode: 0555

- 위 애너테이션을 통해 헬름 생애 주기 중 어떤 시점에 잡을 실행해야 할지 지정. 이 잡은 업그레이드 할 때만 실행되며 신규로 설치할 때는 실행되지 않는다.
helm upgrade --atomic --timeout 30s todo-list todo-list/helm/v4/todo-list/ Error: UPGRADE FAILED: release todo-list failed, and has been rolled back due to atomic being set: pre-upgrade hooks failed: 1 error occurred: * job todo-list-db-check failed: BackoffLimitExceeded kubectl get jobs --show-labels NAME STATUS COMPLETIONS DURATION AGE LABELS todo-list-db-check Failed 0/1 55s 55s kiamol=ch12 todo-list-db-test Complete 1/1 98s 7m22s kiamol=ch12 kubectl logs -l job-name=todo-list-db-check ** ERROR - Postgres not at expected version - wanted: 11.6, got: PostgreSQL 11.8 - CANNOT UPGRADE **
계산 리소스를 관리하여 애플리케이션 및 노드 보호하기
초기 설정에서 메모리와 CPU 는 쿠버네티스의 관리 대상이 아님
파드 컨테이너는 자신이 동작 중인 노드의 CPU 나 메모리를 끌어다 사용하는 데 특별한 제한이 없음
발생하는 문제
- 메모리가 고갈되어 애플리케이션이 강제 종료될 위험
- 다른 애플리케이션을 실행할 노드의 리소스가 부족
리소스 사용량 제한은 애플리케이션 자체보다는 클러스터를 보호하려는 목적이 큼
그럼에도 모든 파드 정의에 리소스 사용량 제한을 지정하는 편이 좋음
클러스터를 네임스페이스로 분할해 두지 않았더라도 기본 네임스페이스에 CPU 및 메모리
사용량 제한을 지정하여 파드 정의에 리소스 사용량이 반드시 포함되도록 하자.
apiVersion: apps/v1 kind: Deployment metadata: name: memory-allocator labels: kiamol: ch12 spec: selector: matchLabels: app: memory-allocator template: metadata: labels: app: memory-allocator spec: containers: - name: api image: kiamol/ch12-memory-allocator resources: limits: memory: 50Mi
- 리소스 사용 제한이 적용된 파드 정의. 애플리케이션이 사용할 수 있는 메모리 상한 50MB
- 리소스 제한은 컨테이너 수준에서 지정됨. 만약에 해당 파드가 메모리 사용량을 limit을 넘어가면 파드가 OOM Killed 상태가 됨
- 계속 죽다보면 CrashLoopBackOff 상태가 됨
kubectl get pods -l app=memory-allocator --watch NAME READY STATUS RESTARTS AGE memory-allocator-689f86766-mztzf 1/1 Running 0 12s memory-allocator-689f86766-mztzf 0/1 OOMKilled 0 29s memory-allocator-689f86766-mztzf 1/1 Running 1 (3s ago) 32s
네임스페이스 단위로 총 리소스 사용량 지정
apiVersion: v1 kind: ResourceQuota metadata: name: memory-quota namespace: kiamol-ch12-memory spec: hard: limits.memory: 150Mi
- 리소스쿼터 객체가 있는 네임스페이스에서는 현재 리소스의 잔량과 파드를 생성하면서 사용하게될 리소스양을 비교할 수 있도록 모든 파드 정의에 resource 항목이 포함되어야 함
- 네임스페이스에 걸려있는 리소스 제한을 넘어 파드를 생성하려하면 생성 자체가 안됨
apiVersion: apps/v1 kind: Deployment metadata: name: memory-allocator namespace: kiamol-ch12-memory labels: kiamol: ch12 spec: selector: matchLabels: app: memory-allocator template: metadata: labels: app: memory-allocator spec: containers: - name: api image: kiamol/ch12-memory-allocator resources: limits: memory: 200Mi
CPU 사용량 제한
- CPU 사용량 역시 컨테이너별 사용량 제한이나 네임스페이스 별 총 사용량 제한을 둘 수 있음
- 하지만 메모리 사용량 제한과는 약간 적용되는 방식이 다름
- CPU 사용량 제한이 걸리더라도 컨테이너는 원하는 대로 CPU를 사용할 수 있음
- 설사 제한을 초과하더라도 파드가 재시작하거나 하는 일은 없음
apiVersion: apps/v1 kind: Deployment metadata: name: pi-web labels: kiamol: ch12 spec: selector: matchLabels: app: pi-web template: metadata: labels: app: pi-web spec: containers: - image: kiamol/ch05-pi command: ["dotnet", "Pi.Web.dll", "-m", "web"] name: web ports: - containerPort: 80 name: http resources: limits: cpu: 250m
- 1은 코어 하나를 의미. 250m 은 250 밀리코어, 즉 1/4 코어로 제한을 적용하는 것
kubectl apply -f pi/namespace-with-quota/ kubectl get replicaset -n kiamol-ch12-cpu NAME DESIRED CURRENT READY AGE pi-web-86778847fb 2 1 1 8s kubectl get endpoints pi-web -n kiamol-ch12-cpu NAME ENDPOINTS AGE pi-web 10.1.0.83:80 22s kubectl describe replicaset -n kiamol-ch12-cpu Warning FailedCreate 34s replicaset-controller Error creating: pods "pi-web-86778847fb-snr8c" is forbidden: exceeded quota: cpu-quota, requested: limits.cpu=300m, used: limits.cpu=300m, limited: limits.cpu=500m
- 네임스페이스에 걸려있는 CPU 제한은 500m 인데, pod 별로 300m 을 점유하여, 두번째 파드를 생성할 수 없음
자기수복형 애플리케이션의 한계점
지금까지 우리가 배운 모든 자기수복 수단은 파드를 재시작하는 형태로 동작했다. 대체되는 것은 컨테이너
- 우리가 만든 애플리케이션이 이 점에서 문제가 없는지 확인해야 함
- 멀티컨테이너가 적용되었다면 파드가 재시작 될 때는 초기화 컨테이너가 다시 실행되고 사이드카 컨테이너도 새로운 컨테이너로 교체됨
- 반복된 재시작으로 파드가 크래시루프백오프 상태에 빠지면 애플리케이션이 중단될 수 있음
- 쿠버네티스에서는 파드가 백오프 상태가 되는 조건이나 재시작 허용 횟수를 설정하는 수단이 따로 없고, 파드를 다른 노드로 재배치하여 새로 생성되는 기능도 없음
- 그래서 아직은, 모든 파드가 백오프 상태에 빠져 서비스의 엔드포인트 목록이 빌 수 있음을 감안해서 자기수복형 애플리케이션을 만들어야 함