Kubernetes Workloads에 대하여

Workloads

  • Deployment & ReplicaSet: 필요할 때마다 파드를 교체할 수 있는 무상태(stateless) 애플리케이션에 적합
  • StatefulSet: 데이터를 지속적으로 유지하는 등의 상태를 추적하는 하나 이상의 관련 파드를 실행하는 데 사용
  • DaemonSet: 각 노드에서 실행되어야 하는 파드를 보장하여 일반적으로 노드 수준의 작업에 사용
  • Job & CronJob: 완료되면 중단되는 작업을 정의. 잡은 한 번 실행되고, 크론잡은 정해진 일정에 따라 주기적으로 실행된다.

Pods

파드는 쿠버네티스에서 생성하고 관리할 수 있는 가장 작은 배포 가능한 컴퓨팅 단위이다. 파드는 공유 스토리지 및 네트워크 자원을 가진 하나 이상의 컨테이너 그룹으로, 컨테이너를 실행하는 방법에 대한 명세를 포함한다.
파드의 내용은 항상 공동 위치에 있으며 공동으로 스케줄링되고 공유 컨텍스트에서 실행된다. 파드는 애플리케이션 특정 “논리 호스트"를 모델링하며, 상대적으로 긴밀하게 연결된 하나 이상의 애플리케이션 컨테이너를 포함한다.

파드의 공유 컨텍스트는 linux namespace, cgroups 및 격리의 기능들로 구성되며, 이는 컨테이너를 격리하는 것과 동일한 것들이다. 파드의 컨텍스트 내에서 개별 애플리케이션은 추가적인 하위 격리를 적용받을 수 있다.

파드는 단일 컨테이너를 실행하는 경우와 여러 컨테이너가 함께 작동해야 하는 경우 두 가지 주요 방식으로 사용된다.

  • 한 파드 당 하나의 컨테이너” 모델은 가장 일반적인 쿠버네티스 사용 사례로, 이 경우 파드를 단일 컨테이너를 둘러싼 래퍼로 생각할 수 있으며, 쿠버네티스는 컨테이너가 아닌 파드를 관리한다.
  • 파드는 공유 자원을 필요로 하는 긴밀하게 연결된 여러 컨테이너로 구성된 애플리케이션을 캡슐화할 수 있다.

컨트롤러

파드는 일반적으로 직접 생성되지 않으며, 대신 Deployment나 Job과 같은 워크로드 리소스를 사용하여 생성된다. 컨트롤러는 복제 및 롤아웃을 관리하고 파드 실패 시 자동 복구를 담당한다.
예를 들어, 노드가 실패하면 컨트롤러는 해당 노드에서 작동을 멈춘 파드를 감지하고 대체 파드를 생성한다. 스케줄러는 대체 파드를 건강한 노드에 배치한다.

네트워크와 스토리지

파드는 이러한 컨테이너, 저장소 자원 및 일시적인 네트워크 신원을 단일 단위로 묶는다. 각 파드는 주어진 애플리케이션의 단일 인스턴스를 실행하도록 설계되었다. 애플리케이션을 수평으로 확장하려면 여러 Pod를 사용해야 한다.
쿠버네티스에서 이는 일반적으로 복제라고 한다. 복제된 파드는 일반적으로 워크로드 리소스와 해당 컨트롤러에 의해 그룹으로 생성 및 관리된다.

파드는 네트워크저장소라는 두 가지 유형의 공유 리소스를 기본적으로 제공한다. 파드를 직접 생성하는 것은 드물며, 싱글톤 파드조차도 대부분 워크로드 리소스를 사용하여 생성된다. 직접 또는 컨트롤러에 의해 Pod가 생성되면, 새로운 Pod는 클러스터의 노드 중 하나에서 실행되도록 예약된다.
파드는 실행이 완료될 때까지, 파드 객체가 삭제될 때까지, 리소스 부족으로 인해 파드가 추방될 때까지, 또는 노드가 실패할 때까지 해당 노드에 남아 있는다.

이름에 관한 주의사항

파드의 이름은 유효한 DNS 하위 도메인 값이어야 한다. 하지만 이는 Pod 호스트 이름에 대해 예상치 못한 결과를 초래할 수 있다. 최상의 호환성을 위해, 이름은 DNS 레이블에 대한 더 엄격한 규칙을 따르는 것이 좋다.

Pod Lifecycle

파드는 상태를 포함한 정의된 라이프사이클을 다음과 같이 가지고 있다.

  • Pending: 파드가 쿠버네티스 시스템에 의해 만들어졌으나, 하나 이상의 컨테이너가 설저오디고 실행 준비를 마치지 못하였다. 이 시간에는 스케줄링 전 시간과 네트워크를 통한 이미지 다운로드 시간을 포함한다.
  • Running: 파드가 노드에 바인딩되었고 모든 컨테이너가 생성되었다. 적어도 하나의 컨테이너가 여전히 실행 중이거나 시작 또는 재시작 과정에 있다.
  • Succeeded: 파드의 모든 컨테이너가 성공적으로 종료되었으며, 다시 시작되지 않는다.
  • Failed: 파드의 모든 컨테이너가 종료되었고 적어도 하나의 컨테이너가 실패로 종료되었다. 즉, 컨테이너가 0이 아닌 상태로 종료되었거나 시스템에 의해 종료되었다.
  • Unknown: 어떤 이유로 파드의 상태를 얻을 수 없다. 일반적으로 파드와 호스트의 통신 오류인 경우가 많다.

Pod Lifecycle Event

  • Scheduled: 파드가 노드에 할당됨
  • Pulling: 쿠버네티스가 컨테이너의 이미지를 가져오고 있음
  • Pulled: 이미지가 성공적으로 가져옴
  • Created: 쿠버네티스가 컨테이너를 생성함
  • Started: 쿠버네티스가 컨테이너를 시작함
  • Killing: 쿠버네티스가 컨테이너를 종료하고 있음
  • Killed: 컨테이너가 종료됨

Pod Condition

파드는 파드가 통과했거나 통과하지 못한 파드의 조건을 가진 PodStatus를 가지고 있다.

  • PodScheduled: 파드가 노드에 스케줄됨
  • ContainersReady: 파드의 모든 컨테이너가 준비됨
  • Initialized: 모든 초기화 컨테이너가 성공적으로 시작됨
  • Ready: 파드가 요청을 처리할 수 있으며 모든 일치하는 서비스의 로드 밸런싱 풀에 추가되어야 한다.

Container Probe

프로브란 쿠버네티스에 의해 주기적으로 컨테이너에서 수행되는 진단으로, 이를 수행하기 위해 쿠버네티스는 컨테이너에 구현된 3가지 핸들러를 호출한다.

  • ExecAction: 컨테이너 내부에서 지정된 명령을 실행한다. 명령이 상태 코드 0으로 종료되면 진단이 성공한 것으로 간주된다
  • TCPSocketAction: 지정된 포트에서 컨테이너의 IP 주소에 대해 TCP 검사를 수행한다. 포트가 열려 있으면 진단이 성공한 것으로 간주된다.
  • HTTPGetAction: 지정된 포트와 경로에서 컨테이너의 IP 주소에 대해 HTTP Get 요청을 수행한다. 응답이 상태 코드가 200 이상 400 미만이면 진단이 성공한 것으로 간주된다.

Init Container

파드는 애플리케이션 컨테이너가 시작하기 전에 실행되는 초기화 컨테이너를 가질 수 있다. 이러한 컨테이너는 앱 이미지에 없는 유틸리티나 설정 스크립트를 포함하기 위한 것이다.
초기화 컨테이너는 애플리케이션 컨테이너가 시작하기 전에 완료될 수 있으며, 파드가 정상적으로 작동하려면 초기화 컨테이너는 무조건 성공해야 한다.

  • 초기화 컨테이너는 애플리케이션 컨테이너와 동일한 이미지를 사용하거나, 애플리케이션 컨테이너와는 다른 이미지를 사용할 수 있다.
  • 각 초기화 컨테이너는 앞선 컨테이너가 성공적으로 완료될 때까지 순차적으로 실행된다.
  • 모든 초기화 컨테이너가 성공적으로 실행되면, 쿠버네티스는 팟의 애플리케이션 컨테이너를 순차적으로 시작한다.
  • 초기화 컨테이너가 실패할 경우, 쿠버네티스는 실패한 컨테이너를 재시도한다.
  • 초기화 컨테이너는 애플리케이션 컨테이너가 시작되기 전에 필요한 설정 작업이나 종속성을 준비하는 데 유용하다.
  • 초기화 컨테이너는 애플리케이션 컨테이너와 파일 시스템을 공유할 수 있으므로, 애플리케이션 컨테이너가 사용할 데이터를 준비하거나 구성할 수 있다.

특히, 애플리케이션 컨테이너와 라이프사이클 훅리소스 요청 및 제한은 공유하지 않는다.

Pod Lifetime

일반적으로 파드는 운영자나 컨트롤러가 파괴할 때까지 사라지지 않는다. 이 규칙의 유일한 예외는 Succeeded 또는 Failed 단계의 파드가 일정 기간(terminatedPodGCThreshold) 이상 지속되면 만료되어 자동으로 파괴된다.

Pod Presets

파드 프리셋은 생성 시 파드에 추가적인 런타임 요구 사항을 주입하는 데 사용할 수 있는 API 리소스이다. 주어진 파드 프리셋이 적용되는 파드를 지정하기 위해 레이블 선택자를 사용한다

Pod Overhead

쿠버네티스에서는 사이드카 컨테이너를 실행하는 데 관련된 오버헤드를 기반으로 파드 수준에서 파드의 오버헤드가 설정된다. 이 오버헤드는 컨테이너의 리소스 요청 및 제한에 추가하여 계산된다.

Disruptions

중단에 대해서 이야기하기 전에 중단의 종류에 대해서 알아야한다. 자발적 중단(Voluntary disruptions)과 비자발적 중단(Involuntary disruptions) 두 가지의 종류의 중단이 존재한다.
예시를 통해서 더 알아보도록 하자.

비자발적 중단 (Involuntary disruptions)

  • 노드를 백업하는 물리적 기계의 실패
  • 관리자가 실수로 VM 삭제
  • 클라우드 프로바이더나 하이퍼바이저 오류로 인해 VM 삭제
  • 커널 패닉
  • 클러스터 네트워크 파티션으로 인한 노드 삭제
  • 노드 자원 부족으로 인한 파드 추방

자발적 중단 (Voluntary disruptions)

  • 파드 관리하는 워크로드나 컨트롤러 삭제
  • 파드 템플릿의 업데이트로 인한 재시작
  • 의도적인 파드 삭제
  • 업데이트나 디버깅을 위한 노드 드레인
  • 클러스터 오토스케일링

이렇듯 자발적 중단이 자주 발생하면 고가용성 애플레케이션을 운영하는데에 지장이 생긴다. 이러한 상황에도 적어도 어느 정도의 서비스를 유지하고 싶다는 니즈를 충족시키기 위한 기능을 PodDisruptionBudget (PDB)라고 한다.
PDB는 자발적 중단으로 인해 동시에 다운되는 애플리케이션의 파드 수를 제한할 수 있다. 만약, 최소한의 파드 개수를 보장해야하는 경우 (쿼럼 기반, 클러스터 기반) PDB를 사용하면 최소한의 파드를 보장 받을 수 있다.

애플리케이션마다 PDB를 설정할 수 있으며, 자발적 중단 (node drain)과 같은 상황이 발생하였을 때 deployment의 .spec.replicas만큼 유지하다가 PDB의 minAvailable만큼만 제외하고 드레인이 진행된다.

Pod disruption conditions v1.26 [beta]

  • PreemptionByScheduler: 스케줄러에 의해 우선 순위가 더 높은 새로운 파드를 수용하기 위해 선점 될 예정
  • DeletionByTaintManager: 파드가 Taint Manager에 의해 삭제될 예정
  • EvictionByEvictionAPI: 쿠버네티스 Eviction API에 evict 예정
  • DeletionByPodGC: 더 이상 존재하지 않은 노드에 바인딩 된 파드가 가비지 컬렉터에 의해 삭제될 예정
  • TerminationByKubelet: 파드는 evict 혹은 노드의 graceful shutdown으로 kubelet에 의해 종료됨

Job이나 CronJob에서도 사용할 수 있으며, 파드 실패 정책으로 이용 될 수 있다.

Ephemeral Containers v1.25 [stable]

임시 컨테이너란 문제 해결을 하기 위해 기존 파드에서 일시적으로 실행되는 특별한 유형의 컨테이너이다. 대부분 애플리케이션 구축을 위한 용도보다는 서비스를 검사하거나, 디버깅 용도로 사용된다.
리소스나 실행에 대한 보장이 없고 자동으로 재시작되지 않고, 일반 컨테이너의 ContainerSpec에서 많은 필드들이 허용되지 않는다. 예시를 들면, 리소스 설정, 프로브와 포트 등이 있다.

임시 컨테이너는 pod.spec에 직접 추가하지 않고 ephemeralcontainers handler를 이용하여 생성된다. 따라서 kubectl edit을 사용하여 추가할 수 없다는 것이다. 또한, 파드에 추가된 경우 변경하거나 제거 할 수 없다.

컨테이너가 충돌하거나 컨테이너 이미지에 디버깅 도구가 포함되지 않은 경우나 kubectl exec로 충분하지 않을 때, 특히 distroless 이미지를 사용하는 경우에 유용하게 사용할 수 있다.

Pod QoS

파드에 지정된 컨테이너의 리소스 제약 조건에 따라 QoS 클래스를 할당하고 이 기준으로 노드에서 리소스가 부족할 시에 어떤 파드를 추방할지 정해지게 된다.

  • Guaranteed: 가장 추방될 가능성이 낮고 가장 엄격한 리소스 제한을 가지고 있다. 제한을 초과하지 않거나 더 낮은 우선순위의 파드가 노드에서 선점되지 않으면 죽지 않는다.
  • Burstable: 요청에 기반한 일부 리소스 보장을 가지고 있지만 특정한 제한은 요구하지 않는다. 리소스가 사용 가능할 때 추가 리소스를 사용할 수 있으며, BestEffort 파드가 모두 추방된 후에만 추방된다.
  • BestEffort: 다른 QoS 클래스에 특별히 할당되지 않은 리소스를 사용할 수 있다. 노드에 리소스 압박이 있을 때 가장 먼저 추방된다.

이러한 QoS 클래스는 위에서 말했듯이, 리소스 기반으로 분류된다. 예를 들어서 resource limit과 request가 동일한 경우 파드는 Guaranteed로 분류된다.

Memory QoS Kubernetes v1.22 [alpha]

cgroup v2의 메모리 컨트롤러를 사용하여 메모리 리소스를 보장한다. 파드 내 컨테이너의 메모리 limit과 request는 메모리 컨트롤러의 memory.min, memory.high를 설정하는데 사용된다.
memory.min이 memory request로 설정되면 메모리 리소스가 예약되고 커널에 의해 절대 회수되지 않는다. 이 부분이 Memory QoS가 쿠버네티스 파드에 메모리 가용성을 보장하는 방법이다.
이렇게 컨테이너 메모리 제한에 설정이되면 시스템은 컨테이너 메모리 사용을 제한해야한다는 것을 의미한다. 메모리 제한에 가까워지는 부분을 컨트롤하기 위해서 memory.high를 사용하여 시스템이 burst하지 못하도록 한다.

User Namespace Kubernetes v1.25 [alpha]

네임스페이스에 대하여

네임스페이스는 리눅스 시스템에서 프로세스 그룹에 대한 격리를 제공하는 기능이다다. 이를 통해 프로세스들은 시스템의 다른 부분과 격리되어 실행될 수 있다.
컨테이너 또는 쿠버네티스 파드가 호스트 시스템의 네임스페이스를 직접 사용할 때 쓰는 기능을 호스트 네임스페이스라고 칭한다. 호스트 네임스페이스는 여러 유형이 있으며, 각각은 시스템의 특정 부분을 격리한다.

  • PID 네임스페이스: 프로세스 ID를 격리한다. 이를 통해 컨테이너 내의 프로세스는 호스트 시스템의 다른 프로세스와 분리된 PID 네임스페이스를 가진다.
  • 네트워크 네임스페이스: 네트워크 인터페이스와 관련 설정을 격리한다. 컨테이너는 자체적인 네트워크 스택을 가질 수 있으며, 호스트의 네트워크와 분리된다.
  • 마운트 네임스페이스: 파일 시스템 마운트 포인트를 격리한다. 컨테이너는 자체 파일 시스템 뷰를 가지며, 호스트의 파일 시스템과 분리된다.
  • IPC 네임스페이스: 프로세스 간 통신(IPC) 리소스를 격리한다.
  • UTS 네임스페이스: 호스트 이름과 도메인 이름을 격리한다.
  • 사용자 네임스페이스: 사용자 ID와 그룹 ID를 격리한다.

사용자 네임스페이스는 컨테이너 내부의 사용자 ID(UID)와 그룹 ID(GID)를 호스트 시스템의 다른 UID/GID로 매핑하는 리눅스 기능으로. 컨테이너 내에서 루트(root)로 실행되는 프로세스가 호스트에서는 논 루트(non-root) 사용자로 작동하게 할 수 있다.
이 기능을 통해서 컨테이너가 손상되었을 때 호스트 시스템이나 다른 파드에 미치는 영향을 줄인다. 특정 보안 취약점의 경우 사용자 네임스페이스를 활성화하기만 하더라도 공격하기 어렵게 된다.

쿠버네티스에서 파드는 기본적으로 자체 네임스페이스를 가진다. 만약 파드가 호스트의 네임스페이스를 쓰기 원한다면, hostNetworktrue로 설정하면 사용할 수 있다.
그러면 파드는 호스트의 네트워크 네임스페이스를 사용하게 되며, 파드가 호스트의 네트워크 인터페이스와 IP 주소를 공유하게 된다.

장점은 성능이 향상되고, 시스템 리소스에 대한 더 깊게 통합이 가능해진다. 하지만, 보안 위험이 증가할 수 있다. 파드에서 호스트 시스템에 대한 접근이 가능해지면서 잠재적인 보안 취약점이 생길 수 있기 때문이다.

작동 방식

  • 사용자 ID 매핑: 컨테이너 내부에서 루트로 실행되는 프로세스는 호스트에서 다른 사용자 ID로 매핑된다. 이는 컨테이너 내부에서는 모든 권한을 가지지만, 호스트 시스템에서는 제한된 권한만을 가짐을 의미한다.
  • 파일 시스템 지원: /var/lib/kubelet/pods/ 또는 사용자가 설정한 디렉토리와 파드의 볼륨에 사용되는 모든 파일 시스템은 idmap 마운트를 지원해야한다.
  • 컨테이너 런타임 지원: CRI-O 버전 1.25 이상, containerd v1.7 등 특정 버전의 컨테이너 런타임만 지원한다.

설정

  • 파드 설정: 파드는 pod.spec.hostUsers 필드를 false로 설정하여 사용자 네임스페이스를 사용하도록 선택할 수 있다.
  • UID/GID 범위: 사용자 네임스페이스가 활성화되면 유효한 UID/GID 범위는 0-65535입니다. 이 범위를 벗어난 UID/GID를 사용하는 파일은 오버플로우 ID(보통 65534)로 간주된다.

제한 사항

  • 호스트 네임스페이스 사용 제한: 사용자 네임스페이스를 사용하는 파드는 다른 호스트 네임스페이스(예: hostNetwork, hostIPC, hostPID)를 사용할 수 없다.

사용자 네임스페이스의 중요성

  • 보안 강화: 사용자 네임스페이스는 컨테이너가 호스트 시스템에 영향을 미치는 것을 제한하여 보안을 강화한다. 컨테이너가 호스트로 탈출하더라도, 비루트 사용자로서의 제한된 권한으로 인해 호스트에 미치는 영향이 제한된다.
  • 다른 파드와의 격리: 각 파드는 호스트에서 서로 겹치지 않는 다른 사용자에게 매핑되므로, 한 파드가 다른 파드에 영향을 미치는 것도 제한된다.

Downward API

쿠버네티스의 Downward API란 파드와 컨테이너가 자신의 정보 (필드)를 알게하는 기능을 의미한다. 여기서 전달하는 방법이 두 가지가 있으며 다음과 같다.

  • 환경 변수
  • Downward API 볼륨 형태로 파일로 노출

사용 가능한 정보

  • 파드 수준 정보 (fieldRef를 통해 제공):
    • metadata.name: 파드의 이름
    • metadata.namespace: 파드가 속한 네임스페이스
    • metadata.uid: 파드의 고유 식별자
    • metadata.labels[’’]: 파드의 특정 라벨
    • metadata.annotations[’’]: 파드의 특정 주석
  • 컨테이너 수준 정보 (resourceFieldRef를 통해 제공):
    • limits.cpu, requests.cpu: CPU 리소스 제한 및 요청
    • limits.memory, requests.memory: 메모리 리소스 제한 및 요청
    • limits.ephemeral-storage, requests.ephemeral-storage: 임시 저장소 리소스 제한 및 요청

만약 컨테이너에 CPU와 메모리 제한이 명시되지 않은 경우, Downward API는 노드의 최대 할당 가능한 값에 기반하여 이 정보를 제공한다. 이러한 기능을 통해서 컨테이너가 자신의 리소스 사용을 더 잘 이해하고 관리할 수 있도록 돕는다.

사례

  • 환경 변수로 파드 정보 제공: 예를 들어, 어플리케이션이 특정 환경 변수에 고유 식별자를 기대하는 경우, 파드의 이름이나 UID를 해당 환경 변수에 주입할 수 있니다.
  • 파일로 파드 라벨 및 주석 제공: 파드의 라벨이나 주석을 파일 형태로 컨테이너 내부에 제공하여, 컨테이너가 이 정보를 읽고 사용할 수 있다.

댓글