LabHub
배우기 러닝패스 코스

CKAD — Kubernetes Application Developer

securityContext, Resources, Quotas and QoS

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

파드가 노드에서 무엇을 할 수 있는지(securityContext, ServiceAccount)와 얼마를 쓸 수 있는지(resources, LimitRange, ResourceQuota)를 매니페스트로 정하고, 그 결과 QoS 클래스가 어떻게 결정되는지 확인할 수 있게 된다.

왜 중요한가

컨테이너는 기본적으로 root 로 돈다. 이미지가 그렇게 만들어졌기 때문이다. 컨테이너 탈출 취약점이 하나라도 있으면 그 root 가 노드의 root 로 이어질 수 있다. runAsNonRoot: true 는 UID 0 으로 뜨려는 컨테이너를 아예 시작시키지 않고, capabilities.drop: ["ALL"] 은 커널이 주는 특권을 전부 회수한다. 웹 서버가 80 포트를 열어야 한다면 NET_BIND_SERVICE 하나만 다시 더한다. 이것이 최소 권한 원칙의 구체적인 모습이다.

리소스 쪽은 두 층으로 이해한다. requests 는 스케줄러의 언어다. 노드에 남은 할당 가능량이 requests 합계보다 커야 파드가 얹힌다. 실제 사용량과는 무관하다. limits 는 노드의 언어다. CPU 는 cgroup 쿼터로 스로틀링되고 메모리는 넘는 순간 OOMKilled 된다. 그래서 메모리 limits 를 실제 피크보다 낮게 잡으면 파드가 조용히 반복 재시작한다.

LimitRange 와 ResourceQuota 는 반드시 세트로 이해해야 한다. 쿼터가 걸린 네임스페이스에 requests/limits 없는 파드를 만들면 생성 자체가 거부된다. 팀 네임스페이스에 쿼터만 걸어 두면 아무것도 안 뜨는 사고가 나므로, LimitRange 로 기본값을 채워 주는 것이 사실상 필수다.

단계

  1. 네임스페이스 ckad-secure 를 만들고 그 안에 ServiceAccount app-sa 를 만든다.
  2. 파드 sa-pod 를 만든다. 이미지 nginx:1.27, serviceAccountName: app-sa, automountServiceAccountToken: false.
  3. 파드 nonroot 를 만든다. 이미지 nginx:1.27, 파드 수준 securityContextrunAsNonRoot: true, runAsUser: 1000, fsGroup: 2000.
  4. 파드 hardened 를 만든다. 이미지 nginx:1.27, 컨테이너 이름 app, 컨테이너 수준 securityContextallowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities.drop: ["ALL"], capabilities.add: ["NET_BIND_SERVICE"].
  5. Deployment api 를 만든다. 레플리카 2, 라벨 app=api, 이미지 nginx:1.27, 컨테이너 resources.requestscpu: 100m / memory: 128Mi, resources.limitscpu: 500m / memory: 512Mi.
  6. LimitRange defaults 를 만든다. type: Containerdefault(limits) cpu: 200m / memory: 256Mi, defaultRequest cpu: 100m / memory: 128Mi, max cpu: "1" / memory: 1Gi, min cpu: 50m / memory: 64Mi.
  7. ResourceQuota team-quota 를 만든다. requests.cpu: "2", requests.memory: 4Gi, limits.cpu: "4", limits.memory: 8Gi, pods: "10".
  8. 파드 두 개를 더 만든다. qos-guaranteed 는 requests 와 limits 가 모두 cpu: 250m / memory: 256Mi 로 같아야 하고, qos-burstable 은 requests 만 cpu: 100m / memory: 128Mi 로 준다. 둘 다 이미지 nginx:1.27.

참고

네임스페이스와 ServiceAccount

네임스페이스 ckad-secure 를 만들고 그 안에 ServiceAccount app-sa 를 만든다.

kubectl create serviceaccount 로 만든다. 네임스페이스를 먼저 만들고 그 안에 만든다.

파드에 ServiceAccount 지정하고 토큰 자동 마운트 끄기

파드 sa-pod 를 만든다. 이미지 nginx:1.27, serviceAccountName: app-sa, automountServiceAccountToken: false.

필드 이름은 spec.serviceAccountName 이다(serviceAccount 는 옛 별칭). 토큰 자동 마운트를 끄는 불리언 필드도 파드 스펙 최상위에 있다.

파드 수준 securityContext

파드 nonroot 를 만든다. 이미지 nginx:1.27, 파드 수준 securityContextrunAsNonRoot: true, runAsUser: 1000, fsGroup: 2000.

spec.securityContext 에 들어가는 것은 파드 전체에 적용되는 값들이다. 볼륨 소유 그룹을 지정하는 필드는 파드 수준에만 존재한다.

컨테이너 수준 securityContext 와 capabilities

파드 hardened 를 만든다. 이미지 nginx:1.27, 컨테이너 이름 app, 컨테이너 수준 securityContextallowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities.drop: ["ALL"], capabilities.add: ["NET_BIND_SERVICE"].

capabilities 는 컨테이너 수준에만 있다. 전부 떨어뜨린 뒤 필요한 것만 다시 더하는 방식이 최소 권한 원칙이다. dropadd 는 각각 문자열 배열이다.

Deployment 에 requests 와 limits 지정

Deployment api 를 만든다. 레플리카 2, 라벨 app=api, 이미지 nginx:1.27, 컨테이너 resources.requestscpu: 100m / memory: 128Mi, resources.limitscpu: 500m / memory: 512Mi.

resources 는 컨테이너 밑에 있다. requests 가 스케줄러가 보는 값, limits 가 런타임이 강제하는 값이다. CPU 단위 m 은 1/1000 코어다.

LimitRange 로 기본값과 상하한 정하기

LimitRange defaults 를 만든다. type: Containerdefault(limits) cpu: 200m / memory: 256Mi, defaultRequest cpu: 100m / memory: 128Mi, max cpu: "1" / memory: 1Gi, min cpu: 50m / memory: 64Mi.

spec.limits 는 배열이고 각 원소에 type 이 있다. 기본 limits 를 정하는 키와 기본 requests 를 정하는 키의 이름이 다르니 kubectl explain limitrange.spec.limits 로 확인한다.

ResourceQuota 로 네임스페이스 총량 제한

ResourceQuota team-quota 를 만든다. requests.cpu: "2", requests.memory: 4Gi, limits.cpu: "4", limits.memory: 8Gi, pods: "10".

spec.hard 는 맵이고 키 이름이 requests.cpu, limits.memory 처럼 점을 포함한다. 개수 제한은 pods 처럼 문자열 값으로 준다.

QoS 클래스를 의도한 대로 만들기 (종합)

파드 두 개를 더 만든다. qos-guaranteed 는 requests 와 limits 가 모두 cpu: 250m / memory: 256Mi 로 같아야 하고, qos-burstable 은 requests 만 cpu: 100m / memory: 128Mi 로 준다. 둘 다 이미지 nginx:1.27.

QoS 는 직접 지정하는 필드가 아니라 requests/limits 조합으로 결정되는 결과값이다. 모든 컨테이너에서 CPU·메모리 모두 requests 와 limits 가 같아야 최상위 등급이 된다. kubectl get pod -o jsonpath='{.status.qosClass}' 로 확인한다.