1 - 노드

쿠버네티스는 컨테이너를 파드내에 배치하고 노드 에서 실행함으로 워크로드를 구동한다. 노드는 클러스터에 따라 가상 또는 물리적 머신일 수 있다. 각 노드는 컨트롤 플레인에 의해 관리되며 파드를 실행하는 데 필요한 서비스를 포함한다.

일반적으로 클러스터에는 여러 개의 노드가 있으며, 학습 또는 리소스가 제한되는 환경에서는 하나만 있을 수도 있다.

노드의 컴포넌트에는 kubelet, 컨테이너 런타임 그리고 kube-proxy가 포함된다.

관리

API 서버에 노드를 추가하는 두가지 주요 방법이 있다.

  1. 노드의 kubelet으로 컨트롤 플레인에 자체 등록
  2. 사용자(또는 다른 사용자)가 노드 오브젝트를 수동으로 추가

노드 오브젝트 또는 노드의 kubelet으로 자체 등록한 후 컨트롤 플레인은 새 노드 오브젝트가 유효한지 확인한다. 예를 들어 다음 JSON 매니페스트에서 노드를 만들려는 경우이다.

{
  "kind": "Node",
  "apiVersion": "v1",
  "metadata": {
    "name": "10.240.79.157",
    "labels": {
      "name": "my-first-k8s-node"
    }
  }
}

쿠버네티스는 내부적으로 노드 오브젝트를 생성한다(표시한다). 쿠버네티스는 kubelet이 노드의 metadata.name 필드와 일치하는 API 서버에 등록이 되어 있는지 확인한다. 노드가 정상이면(예를 들어 필요한 모든 서비스가 실행중인 경우) 파드를 실행할 수 있게 된다. 그렇지 않으면, 해당 노드는 정상이 될 때까지 모든 클러스터 활동에 대해 무시된다.

노드 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

노드 이름 고유성

이름은 노드를 식별한다. 두 노드는 동시에 같은 이름을 가질 수 없다. 쿠버네티스는 또한 같은 이름의 리소스가 동일한 객체라고 가정한다. 노드의 경우, 동일한 이름을 사용하는 인스턴스가 동일한 상태(예: 네트워크 설정, 루트 디스크 내용)와 노드 레이블과 같은 동일한 속성(attribute)을 갖는다고 암시적으로 가정한다. 인스턴스가 이름을 변경하지 않고 수정된 경우 이로 인해 불일치가 발생할 수 있다. 노드를 대폭 교체하거나 업데이트해야 하는 경우, 기존 노드 오브젝트를 먼저 API 서버에서 제거하고 업데이트 후 다시 추가해야 한다.

노드에 대한 자체-등록(self-registration)

kubelet 플래그 --register-node가 참(기본값)일 경우, kubelet은 API 서버에 스스로 등록을 시도할 것이다. 이는 선호되는 패턴이며, 대부분의 배포판에서 사용된다.

자체-등록에 대해, kubelet은 다음 옵션과 함께 시작된다.

  • --kubeconfig - apiserver에 스스로 인증하기 위한 자격증명에 대한 경로.

  • --cloud-provider - 자신에 대한 메터데이터를 읽기 위해 어떻게 클라우드 제공자와 소통할지에 대한 방법.

  • --register-node - 자동으로 API 서버에 등록.

  • --register-with-taints - 주어진 테인트(taint) 리스트(콤마로 분리된 <key>=<value>:<effect>)를 가진 노드 등록.

    register-node가 거짓이면 동작 안 함.

  • --node-ip - 노드의 IP 주소.

  • --node-labels - 클러스터에 노드를 등록할 때 추가 할 레이블 (NodeRestriction admission plugin에 의해 적용되는 레이블 제한 사항 참고).

  • --node-status-update-frequency - 얼마나 자주 kubelet이 API 서버에 해당 노드 상태를 게시할 지 정의.

Node authorization modeNodeRestriction admission plugin이 활성화 되면, 각 kubelet은 자신이 속한 노드의 리소스에 대해서만 생성/수정할 권한을 가진다.

수동 노드 관리

kubectl을 사용해서 노드 오브젝트를 생성하고 수정할 수 있다.

노드 오브젝트를 수동으로 생성하려면 kubelet 플래그를 --register-node=false 로 설정한다.

--register-node 설정과 관계 없이 노드 오브젝트를 수정할 수 있다. 예를 들어 기존 노드에 레이블을 설정하거나, 스케줄 불가로 표시할 수 있다.

파드의 노드 셀렉터와 함께 노드의 레이블을 사용해서 스케줄링을 제어할 수 있다. 예를 들어, 사용 가능한 노드의 하위 집합에서만 실행되도록 파드를 제한할 수 있다.

노드를 스케줄 불가로 표시하면 스케줄러가 해당 노드에 새 파드를 배치할 수 없지만, 노드에 있는 기존 파드에는 영향을 미치지 않는다. 이는 노드 재부팅 또는 기타 유지보수 준비 단계에서 유용하다.

노드를 스케줄 불가로 표시하려면 다음을 실행한다.

kubectl cordon $NODENAME

보다 자세한 내용은 안전하게 노드를 드레인(drain)하기 를 참고한다.

노드 상태

노드의 상태는 다음의 정보를 포함한다.

kubectl 을 사용해서 노드 상태와 기타 세부 정보를 볼수 있다.

kubectl describe node <insert-node-name-here>

출력되는 각 섹션은 아래에 설명되어 있다.

주소

이 필드의 용법은 클라우드 제공사업자 또는 베어메탈 구성에 따라 다양하다.

  • HostName: 노드의 커널에 의해 알려진 호스트명이다. --hostname-override 파라미터를 통해 치환될 수 있다.
  • ExternalIP: 일반적으로 노드의 IP 주소는 외부로 라우트 가능 (클러스터 외부에서 이용 가능) 하다 .
  • InternalIP: 일반적으로 노드의 IP 주소는 클러스터 내에서만 라우트 가능하다.

컨디션

conditions 필드는 모든 Running 노드의 상태를 기술한다. 컨디션의 예로 다음을 포함한다.

노드 컨디션과 각 컨디션이 적용되는 시기에 대한 설명들이다.
노드 컨디션 설명
Ready 노드가 상태 양호하며 파드를 수용할 준비가 되어 있는 경우 True, 노드의 상태가 불량하여 파드를 수용하지 못할 경우 False, 그리고 노드 컨트롤러가 마지막 node-monitor-grace-period (기본값 40 기간 동안 노드로부터 응답을 받지 못한 경우) Unknown
DiskPressure 디스크 사이즈 상에 압박이 있는 경우, 즉 디스크 용량이 넉넉치 않은 경우 True, 반대의 경우 False
MemoryPressure 노드 메모리 상에 압박이 있는 경우, 즉 노드 메모리가 넉넉치 않은 경우 True, 반대의 경우 False
PIDPressure 프로세스 상에 압박이 있는 경우, 즉 노드 상에 많은 프로세스들이 존재하는 경우 True, 반대의 경우 False
NetworkUnavailable 노드에 대해 네트워크가 올바르게 구성되지 않은 경우 True, 반대의 경우 False

쿠버네티스 API에서, 노드의 컨디션은 노드 리소스의 .status 부분에 표현된다. 예를 들어, 다음의 JSON 구조는 상태가 양호한 노드를 나타낸다.

"conditions": [
  {
    "type": "Ready",
    "status": "True",
    "reason": "KubeletReady",
    "message": "kubelet is posting ready status",
    "lastHeartbeatTime": "2019-06-05T18:38:35Z",
    "lastTransitionTime": "2019-06-05T11:41:27Z"
  }
]

ready 컨디션의 statuspod-eviction-timeout (kube-controller-manager에 전달된 인수)보다 더 길게 Unknown 또는 False로 유지되는 경우, 노드 컨트롤러가 해당 노드에 할당된 전체 파드에 대해 API를 이용한 축출 을 트리거한다. 기본 축출 타임아웃 기간은 5분 이다. 노드에 접근이 불가할 때와 같은 경우, API 서버는 노드 상의 kubelet과 통신이 불가하다. API 서버와의 통신이 재개될 때까지 파드 삭제에 대한 결정은 kubelet에 전해질 수 없다. 그 사이, 삭제되도록 스케줄 되어진 파드는 분할된 노드 상에서 계속 동작할 수도 있다.

노드 컨트롤러가 클러스터 내 동작 중지된 것을 확신할 때까지는 파드를 강제로 삭제하지 않는다. 파드가 Terminating 또는 Unknown 상태로 있을 때 접근 불가한 노드 상에서 동작되고 있는 것을 보게 될 수도 있다. 노드가 영구적으로 클러스터에서 삭제되었는지에 대한 여부를 쿠버네티스가 기반 인프라로부터 유추할 수 없는 경우, 노드가 클러스터를 영구적으로 탈퇴하게 되면, 클러스터 관리자는 손수 노드 오브젝트를 삭제해야 할 수도 있다. 쿠버네티스에서 노드 오브젝트를 삭제하면 노드 상에서 동작 중인 모든 파드 오브젝트가 API 서버로부터 삭제되며 파드가 사용하던 이름을 다시 사용할 수 있게 된다.

노드에서 문제가 발생하면, 쿠버네티스 컨트롤 플레인은 자동으로 노드 상태에 영향을 주는 조건과 일치하는 테인트(taints)를 생성한다. 스케줄러는 파드를 노드에 할당할 때 노드의 테인트를 고려한다. 또한 파드는 노드에 특정 테인트가 있더라도 해당 노드에서 동작하도록 톨러레이션(toleration)을 가질 수 있다.

자세한 내용은 컨디션별 노드 테인트하기를 참조한다.

용량과 할당가능

노드 상에 사용 가능한 리소스를 나타낸다. 리소스에는 CPU, 메모리 그리고 노드 상으로 스케줄 되어질 수 있는 최대 파드 수가 있다.

용량 블록의 필드는 노드에 있는 리소스의 총량을 나타낸다. 할당가능 블록은 일반 파드에서 사용할 수 있는 노드의 리소스 양을 나타낸다.

노드에서 컴퓨팅 리소스 예약하는 방법을 배우는 동안 용량 및 할당가능 리소스에 대해 자세히 읽어보자.

정보

커널 버전, 쿠버네티스 버전 (kubelet과 kube-proxy 버전), 컨테이너 런타임 상세 정보 및 노드가 사용하는 운영 체제가 무엇인지와 같은 노드에 대한 일반적인 정보가 기술된다. 이 정보는 Kubelet이 노드에서 수집하여 쿠버네티스 API로 전송한다.

하트비트

쿠버네티스 노드가 보내는 하트비트는 클러스터가 개별 노드가 가용한지를 판단할 수 있도록 도움을 주고, 장애가 발견된 경우 조치를 할 수 있게한다.

노드에는 두 가지 형태의 하트비트가 있다.

  • 노드의 .status에 대한 업데이트
  • kube-node-lease 네임스페이스 내의 리스(Lease) 오브젝트. 각 노드는 연관된 리스 오브젝트를 갖는다.

노드의 .status에 비하면, 리스는 경량의 리소스이다. 큰 규모의 클러스터에서는 리스를 하트비트에 사용하여 업데이트로 인한 성능 영향을 줄일 수 있다.

kubelet은 노드의 .status 생성과 업데이트 및 관련된 리스의 업데이트를 담당한다.

  • kubelet은 상태가 변경되거나 설정된 인터벌보다 오래 업데이트가 없는 경우 노드의 .status를 업데이트한다. 노드의 .status 업데이트에 대한 기본 인터벌은 접근이 불가능한 노드에 대한 타임아웃인 40초 보다 훨씬 긴 5분이다.
  • kubelet은 리스 오브젝트를 (기본 업데이트 인터벌인) 매 10초마다 생성하고 업데이트한다. 리스 업데이트는 노드의 .status 업데이트와는 독립적이다. 만약 리스 업데이트가 실패하면, kubelet은 200밀리초에서 시작하고 7초의 상한을 갖는 지수적 백오프를 사용해서 재시도한다.

노드 컨트롤러

노드 컨트롤러는 노드의 다양한 측면을 관리하는 쿠버네티스 컨트롤 플레인 컴포넌트이다.

노드 컨트롤러는 노드가 생성되어 유지되는 동안 다양한 역할을 한다. 첫째는 등록 시점에 (CIDR 할당이 사용토록 설정된 경우) 노드에 CIDR 블럭을 할당하는 것이다.

두 번째는 노드 컨트롤러의 내부 노드 리스트를 클라우드 제공사업자의 사용 가능한 머신 리스트 정보를 근거로 최신상태로 유지하는 것이다. 클라우드 환경에서 동작 중일 경우, 노드상태가 불량할 때마다, 노드 컨트롤러는 해당 노드용 VM이 여전히 사용 가능한지에 대해 클라우드 제공사업자에게 묻는다. 사용 가능하지 않을 경우, 노드 컨트롤러는 노드 리스트로부터 그 노드를 삭제한다.

세 번째는 노드의 동작 상태를 모니터링하는 것이다. 노드 컨트롤러는 다음을 담당한다.

  • 노드가 접근 불가능(unreachable) 상태가 되는 경우, 노드의 .status 필드의 Ready 컨디션을 업데이트한다. 이 경우에는 노드 컨트롤러가 Ready 컨디션을 Unknown으로 설정한다.
  • 노드가 계속 접근 불가능 상태로 남아있는 경우, 해당 노드의 모든 파드에 대해서 API를 이용한 축출을 트리거한다. 기본적으로, 노드 컨트롤러는 노드를 Unknown으로 마킹한 뒤 5분을 기다렸다가 최초의 축출 요청을 시작한다.

기본적으로, 노드 컨트롤러는 5 초마다 각 노드의 상태를 체크한다. 체크 주기는 kube-controller-manager 구성 요소의 --node-monitor-period 플래그를 이용하여 설정할 수 있다.

축출 빈도 제한

대부분의 경우, 노드 컨트롤러는 초당 --node-eviction-rate(기본값 0.1)로 축출 속도를 제한한다. 이 말은 10초당 1개의 노드를 초과하여 파드 축출을 하지 않는다는 의미가 된다.

노드 축출 행위는 주어진 가용성 영역 내 하나의 노드가 상태가 불량할 경우 변화한다. 노드 컨트롤러는 영역 내 동시에 상태가 불량한 노드의 퍼센티지가 얼마나 되는지 체크한다(Ready 컨디션은 Unknown 또는 False 값을 가진다).

  • 상태가 불량한 노드의 비율이 최소 --unhealthy-zone-threshold (기본값 0.55)가 되면 축출 속도가 감소한다.
  • 클러스터가 작으면 (즉 --large-cluster-size-threshold 노드 이하면 - 기본값 50) 축출이 중지된다.
  • 이외의 경우, 축출 속도는 초당 --secondary-node-eviction-rate(기본값 0.01)로 감소된다.

이 정책들이 가용성 영역 단위로 실행되어지는 이유는 나머지가 연결되어 있는 동안 하나의 가용성 영역이 컨트롤 플레인으로부터 분할되어 질 수도 있기 때문이다. 만약 클러스터가 여러 클라우드 제공사업자의 가용성 영역에 걸쳐 있지 않는 이상, 축출 매커니즘은 영역 별 가용성을 고려하지 않는다.

노드가 가용성 영역들에 걸쳐 퍼져 있는 주된 이유는 하나의 전체 영역이 장애가 발생할 경우 워크로드가 상태 양호한 영역으로 이전되어질 수 있도록 하기 위해서이다. 그러므로, 하나의 영역 내 모든 노드들이 상태가 불량하면 노드 컨트롤러는 --node-eviction-rate 의 정상 속도로 축출한다. 코너 케이스란 모든 영역이 완전히 상태불량(클러스터 내 양호한 노드가 없는 경우)한 경우이다. 이러한 경우, 노드 컨트롤러는 컨트롤 플레인과 노드 간 연결에 문제가 있는 것으로 간주하고 축출을 실행하지 않는다. (중단 이후 일부 노드가 다시 보이는 경우 노드 컨트롤러는 상태가 양호하지 않거나 접근이 불가능한 나머지 노드에서 파드를 축출한다.)

또한, 노드 컨트롤러는 파드가 테인트를 허용하지 않을 때 NoExecute 테인트 상태의 노드에서 동작하는 파드에 대한 축출 책임을 가지고 있다. 추가로, 노드 컨틀로러는 연결할 수 없거나, 준비되지 않은 노드와 같은 노드 문제에 상응하는 테인트를 추가한다. 이는 스케줄러가 비정상적인 노드에 파드를 배치하지 않게 된다.

리소스 용량 추적

노드 오브젝트는 노드 리소스 용량에 대한 정보: 예를 들어, 사용 가능한 메모리의 양과 CPU의 수를 추적한다. 노드의 자체 등록은 등록하는 중에 용량을 보고한다. 수동으로 노드를 추가하는 경우 추가할 때 노드의 용량 정보를 설정해야 한다.

쿠버네티스 스케줄러는 노드 상에 모든 노드에 대해 충분한 리소스가 존재하도록 보장한다. 스케줄러는 노드 상에 컨테이너에 대한 요청의 합이 노드 용량보다 더 크지 않도록 체크한다. 요청의 합은 kubelet에서 관리하는 모든 컨테이너를 포함하지만, 컨테이너 런타임에 의해 직접적으로 시작된 컨 테이너는 제외되고 kubelet의 컨트롤 범위 밖에서 실행되는 모든 프로세스도 제외된다.

노드 토폴로지

기능 상태: Kubernetes v1.18 [beta]

TopologyManager 기능 게이트(feature gate)를 활성화 시켜두면, kubelet이 리소스 할당 결정을 할 때 토폴로지 힌트를 사용할 수 있다. 자세한 내용은 노드의 컨트롤 토폴로지 관리 정책을 본다.

그레이스풀(Graceful) 노드 셧다운(shutdown)

기능 상태: Kubernetes v1.21 [beta]

kubelet은 노드 시스템 셧다운을 감지하고 노드에서 실행 중인 파드를 종료하려고 시도한다.

Kubelet은 노드가 종료되는 동안 파드가 일반 파드 종료 프로세스를 따르도록 한다.

그레이스풀 노드 셧다운 기능은 systemd inhibitor locks를 사용하여 주어진 기간 동안 노드 종료를 지연시키므로 systemd에 의존한다.

그레이스풀 노드 셧다운은 1.21에서 기본적으로 활성화된 GracefulNodeShutdown 기능 게이트로 제어된다.

기본적으로, 아래 설명된 두 구성 옵션, shutdownGracePeriodshutdownGracePeriodCriticalPods 는 모두 0으로 설정되어 있으므로, 그레이스풀 노드 셧다운 기능이 활성화되지 않는다. 기능을 활성화하려면, 두 개의 kubelet 구성 설정을 적절하게 구성하고 0이 아닌 값으로 설정해야 한다.

그레이스풀 셧다운 중에 kubelet은 다음의 두 단계로 파드를 종료한다.

  1. 노드에서 실행 중인 일반 파드를 종료시킨다.
  2. 노드에서 실행 중인 중요(critical) 파드를 종료시킨다.

그레이스풀 노드 셧다운 기능은 두 개의 KubeletConfiguration 옵션으로 구성된다.

  • shutdownGracePeriod:
    • 노드가 종료를 지연해야 하는 총 기간을 지정한다. 이것은 모든 일반 및 중요 파드의 파드 종료에 필요한 총 유예 기간에 해당한다.
  • shutdownGracePeriodCriticalPods:
    • 노드 종료 중에 중요 파드를 종료하는 데 사용되는 기간을 지정한다. 이 값은 shutdownGracePeriod 보다 작아야 한다.

예를 들어, shutdownGracePeriod=30s, shutdownGracePeriodCriticalPods=10s 인 경우, kubelet은 노드 종료를 30초까지 지연시킨다. 종료하는 동안 처음 20(30-10)초는 일반 파드의 유예 종료에 할당되고, 마지막 10초는 중요 파드의 종료에 할당된다.

논 그레이스풀 노드 셧다운

기능 상태: Kubernetes v1.26 [beta]

전달한 명령이 kubelet에서 사용하는 금지 잠금 메커니즘(inhibitor locks mechanism)을 트리거하지 않거나, 또는 사용자 오류(예: ShutdownGracePeriod 및 ShutdownGracePeriodCriticalPods가 제대로 설정되지 않음)로 인해 kubelet의 노드 셧다운 관리자(Node Shutdown Mananger)가 노드 셧다운 액션을 감지하지 못할 수 있다. 자세한 내용은 위의 그레이스풀 노드 셧다운 섹션을 참조한다.

노드가 셧다운되었지만 kubelet의 노드 셧다운 관리자가 이를 감지하지 못하면, 스테이트풀셋에 속한 파드는 셧다운된 노드에 '종료 중(terminating)' 상태로 고착되어 다른 동작 중인 노드로 이전될 수 없다. 이는 셧다운된 노드의 kubelet이 파드를 지울 수 없어서 결국 스테이트풀셋이 동일한 이름으로 새 파드를 만들 수 없기 때문이다. 만약 파드가 사용하던 볼륨이 있다면, 볼륨어태치먼트(VolumeAttachment)도 기존의 셧다운된 노드에서 삭제되지 않아 결국 파드가 사용하던 볼륨이 다른 동작 중인 노드에 연결(attach)될 수 없다. 결과적으로, 스테이트풀셋에서 실행되는 애플리케이션이 제대로 작동하지 않는다. 기존의 셧다운된 노드가 정상으로 돌아오지 못하면, 이러한 파드는 셧다운된 노드에 '종료 중(terminating)' 상태로 영원히 고착될 것이다.

위와 같은 상황을 완화하기 위해, 사용자가 node.kubernetes.io/out-of-service 테인트를 NoExecute 또는 NoSchedule 값으로 추가하여 노드를 서비스 불가(out-of-service) 상태로 표시할 수 있다. kube-controller-managerNodeOutOfServiceVolumeDetach기능 게이트 가 활성화되어 있고, 노드가 이 테인트에 의해 서비스 불가 상태로 표시되어 있는 경우, 노드에 매치되는 톨러레이션이 없다면 노드 상의 파드는 강제로 삭제될 것이고, 노드 상에서 종료되는 파드에 대한 볼륨 해제(detach) 작업은 즉시 수행될 것이다. 이를 통해 서비스 불가 상태 노드의 파드가 빠르게 다른 노드에서 복구될 수 있다.

논 그레이스풀 셧다운 과정 동안, 파드는 다음의 두 단계로 종료된다.

  1. 매치되는 out-of-service 톨러레이션이 없는 파드를 강제로 삭제한다.
  2. 이러한 파드에 대한 볼륨 해제 작업을 즉시 수행한다.

파드 우선순위 기반 그레이스풀 노드 셧다운

기능 상태: Kubernetes v1.23 [alpha]

그레이스풀 노드 셧다운 시 파드 셧다운 순서에 더 많은 유연성을 제공할 수 있도록, 클러스터에 프라이어리티클래스(PriorityClass) 기능이 활성화되어 있으면 그레이스풀 노드 셧다운 과정에서 파드의 프라이어리티클래스가 고려된다. 이 기능으로 그레이스풀 노드 셧다운 시 파드가 종료되는 순서를 클러스터 관리자가 프라이어리티 클래스 기반으로 명시적으로 정할 수 있다.

위에서 기술된 것처럼, 그레이스풀 노드 셧다운 기능은 파드를 중요하지 않은(non-critical) 파드와 중요한(critical) 파드 2단계(phase)로 구분하여 종료시킨다. 셧다운 시 파드가 종료되는 순서를 명시적으로 더 상세하게 정해야 한다면, 파드 우선순위 기반 그레이스풀 노드 셧다운을 사용할 수 있다.

그레이스풀 노드 셧다운 과정에서 파드 우선순위가 고려되기 때문에, 그레이스풀 노드 셧다운이 여러 단계로 일어날 수 있으며, 각 단계에서 특정 프라이어리티 클래스의 파드를 종료시킨다. 정확한 단계와 단계별 셧다운 시간은 kubelet에 설정할 수 있다.

다음과 같이 클러스터에 커스텀 파드 프라이어리티 클래스가 있다고 가정하자.

파드 프라이어리티 클래스 이름 파드 프라이어리티 클래스 값
custom-class-a 100000
custom-class-b 10000
custom-class-c 1000
regular/unset 0

kubelet 환경 설정 안의 shutdownGracePeriodByPodPriority 설정은 다음과 같을 수 있다.

파드 프라이어리티 클래스 값 종료 대기 시간
100000 10 seconds
10000 180 seconds
1000 120 seconds
0 60 seconds

이를 나타내는 kubelet 환경 설정 YAML은 다음과 같다.

shutdownGracePeriodByPodPriority:
  - priority: 100000
    shutdownGracePeriodSeconds: 10
  - priority: 10000
    shutdownGracePeriodSeconds: 180
  - priority: 1000
    shutdownGracePeriodSeconds: 120
  - priority: 0
    shutdownGracePeriodSeconds: 60

위의 표에 의하면 priority 값이 100000 이상인 파드는 종료까지 10초만 주어지며, 10000 이상 ~ 100000 미만이면 180초, 1000 이상 ~ 10000 미만이면 120초가 주어진다. 마지막으로, 다른 모든 파드는 종료까지 60초가 주어질 것이다.

모든 클래스에 대해 값을 명시할 필요는 없다. 예를 들어, 대신 다음과 같은 구성을 사용할 수도 있다.

파드 프라이어리티 클래스 값 종료 대기 시간
100000 300 seconds
1000 120 seconds
0 60 seconds

위의 경우, custom-class-b에 속하는 파드와 custom-class-c에 속하는 파드는 동일한 종료 대기 시간을 갖게 될 것이다.

특정 범위에 해당되는 파드가 없으면, kubelet은 해당 범위에 해당되는 파드를 위해 기다려 주지 않는다. 대신, kubelet은 즉시 다음 프라이어리티 클래스 값 범위로 넘어간다.

기능이 활성화되어 있지만 환경 설정이 되어 있지 않으면, 순서 지정 동작이 수행되지 않을 것이다.

이 기능을 사용하려면 GracefulNodeShutdownBasedOnPodPriority 기능 게이트를 활성화해야 하고, kubelet configShutdownGracePeriodByPodPriority를 파드 프라이어리티 클래스 값과 각 값에 대한 종료 대기 시간을 명시하여 지정해야 한다.

graceful_shutdown_start_time_secondsgraceful_shutdown_end_time_seconds 메트릭은 노드 셧다운을 모니터링하기 위해 kubelet 서브시스템에서 방출된다.

스왑(swap) 메모리 관리

기능 상태: Kubernetes v1.22 [alpha]

쿠버네티스 1.22 이전에는 노드가 스왑 메모리를 지원하지 않았다. 그리고 kubelet은 노드에서 스왑을 발견하지 못한 경우 시작과 동시에 실패하도록 되어 있었다. 1.22부터는 스왑 메모리 지원을 노드 단위로 활성화할 수 있다.

노드에서 스왑을 활성화하려면, NodeSwap 기능 게이트가 kubelet에서 활성화되어야 하며, 명령줄 플래그 --fail-swap-on 또는 구성 설정에서 failSwapOn가 false로 지정되어야 한다.

사용자는 또한 선택적으로 memorySwap.swapBehavior를 구성할 수 있으며, 이를 통해 노드가 스왑 메모리를 사용하는 방식을 명시한다. 예를 들면,

memorySwap:
  swapBehavior: LimitedSwap

swapBehavior에 가용한 구성 옵션은 다음과 같다.

  • LimitedSwap: 쿠버네티스 워크로드는 스왑을 사용할 수 있는 만큼으로 제한된다. 쿠버네티스에 의해 관리되지 않는 노드의 워크로드는 여전히 스왑될 수 있다.
  • UnlimitedSwap: 쿠버네티스 워크로드는 요청한 만큼 스왑 메모리를 사용할 수 있으며, 시스템의 최대치까지 사용 가능하다.

만약 memorySwap 구성이 명시되지 않았고 기능 게이트가 활성화되어 있다면, kubelet은 LimitedSwap 설정과 같은 행동을 기본적으로 적용한다.

LimitedSwap 설정에 대한 행동은 노드가 ("cgroups"으로 알려진) 제어 그룹이 v1 또는 v2 중에서 무엇으로 동작하는가에 따라서 결정된다.

  • cgroupsv1: 쿠버네티스 워크로드는 메모리와 스왑의 조합을 사용할 수 있다. 파드의 메모리 제한이 설정되어 있다면 가용 상한이 된다.
  • cgroupsv2: 쿠버네티스 워크로드는 스왑 메모리를 사용할 수 없다.

테스트를 지원하고 피드벡을 제공하기 위한 정보는 KEP-2400디자인 제안에서 찾을 수 있다.

다음 내용

2 - 컨트롤 플레인-노드 간 통신

이 문서는 API 서버와 쿠버네티스 클러스터 사이에 대한 통신 경로의 목록을 작성한다. 이는 사용자가 신뢰할 수 없는 네트워크(또는 클라우드 공급자의 완전한 퍼블릭 IP)에서 클러스터를 실행할 수 있도록 네트워크 구성을 강화하기 위한 맞춤 설치를 할 수 있도록 한다.

노드에서 컨트롤 플레인으로의 통신

쿠버네티스에는 "허브 앤 스포크(hub-and-spoke)" API 패턴을 가지고 있다. 노드(또는 노드에서 실행되는 파드들)의 모든 API 사용은 API 서버에서 종료된다. 다른 컨트롤 플레인 컴포넌트 중 어느 것도 원격 서비스를 노출하도록 설계되지 않았다. API 서버는 하나 이상의 클라이언트 인증 형식이 활성화된 보안 HTTPS 포트(일반적으로 443)에서 원격 연결을 수신하도록 구성된다. 특히 익명의 요청 또는서비스 어카운트 토큰이 허용되는 경우, 하나 이상의 권한 부여 형식을 사용해야 한다.

노드는 유효한 클라이언트 자격 증명과 함께 API 서버에 안전하게 연결할 수 있도록 클러스터에 대한 공개 루트 인증서(root certificate)로 프로비전해야 한다. 클라이언트 인증서(client certificate) 형식으로 kubelet의 클라이언트 자격 증명을 사용하는 것은 좋은 방법이다. kubelet 클라이언트 인증서(client certificate)의 자동 프로비저닝은 kubelet TLS 부트스트랩을 참고한다.

API 서버에 연결하려는 파드는 서비스 어카운트를 활용하여 안전하게 쿠버네티스가 공개 루트 인증서(root certificate)와 유효한 베어러 토큰(bearer token)을 파드가 인스턴스화될 때 파드에 자동으로 주입할 수 있다. kubernetes 서비스(default 네임스페이스의)는 API 서버의 HTTPS 엔드포인트로 리디렉션되는 가상 IP 주소(kube-proxy를 통해)로 구성되어 있다.

컨트롤 플레인 컴포넌트는 보안 포트를 통해 클러스터 API 서버와도 통신한다.

결과적으로, 노드 및 노드에서 실행되는 파드에서 컨트롤 플레인으로 연결하기 위한 기본 작동 모드는 기본적으로 보호되며 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행될 수 있다.

컨트롤 플레인에서 노드로의 통신

컨트롤 플레인(API 서버)에서 노드로는 두 가지 기본 통신 경로가 있다. 첫 번째는 API 서버에서 클러스터의 각 노드에서 실행되는 kubelet 프로세스이다. 두 번째는 API 서버의 프록시 기능을 통해 API 서버에서 모든 노드, 파드 또는 서비스에 이르는 것이다.

API 서버에서 kubelet으로의 통신

API 서버에서 kubelet으로의 연결은 다음의 용도로 사용된다.

  • 파드에 대한 로그를 가져온다.
  • 실행 중인 파드에 (보통의 경우 kubectl을 통해) 연결한다.
  • kubelet의 포트-포워딩 기능을 제공한다.

위와 같은 연결은 kubelet의 HTTPS 엔드포인트에서 종료된다. 기본적으로, API 서버는 kubelet의 제공(serving) 인증서를 확인하지 않는다. 이는 연결이 중간자 공격(man-in-the-middle)에 시달리게 하며, 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행하기에 안전하지 않다 .

이 연결을 확인하려면, --kubelet-certificate-authority 플래그를 사용하여 API 서버에 kubelet의 제공(serving) 인증서를 확인하는데 사용할 루트 인증서 번들을 제공한다.

이것이 가능하지 않은 경우, 신뢰할 수 없는 네트워크 또는 공용 네트워크를 통한 연결을 피하기 위해 필요한 경우, API 서버와 kubelet 간 SSH 터널링을 사용한다.

마지막으로, kubelet API를 보호하려면 Kubelet 인증 및/또는 인가를 활성화해야 한다.

API 서버에서 노드, 파드 및 서비스로의 통신

API 서버에서 노드, 파드 또는 서비스로의 연결은 기본적으로 일반 HTTP 연결로 연결되므로 인증되거나 암호화되지 않는다. 이 연결에서 URL을 노드, 파드 또는 서비스 이름에 접두어 https: 을 붙여 보안 HTTPS 연결이 되도록 실행할 수 있지만, HTTPS 엔드포인트가 제공한 인증서의 유효성을 검증하지 않으며 클라이언트 자격 증명도 제공하지 않는다. 그래서 연결이 암호화되는 동안 그 어떤 무결성도 보장되지 않는다. 이러한 연결은 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행하기에 현재는 안전하지 않다 .

SSH 터널

쿠버네티스는 SSH 터널을 지원하여 컨트롤 플레인에서 노드로의 통신 경로를 보호한다. 이 구성에서, API 서버는 클러스터의 각 노드에 SSH 터널을 시작하고 (포트 22에서 수신 대기하는 ssh 서버에 연결) 터널을 통해 kubelet, 노드, 파드 또는 서비스로 향하는 모든 트래픽을 전달한다. 이 터널은 노드가 실행 중인 네트워크의 외부로 트래픽이 노출되지 않도록 한다.

Konnectivity 서비스

기능 상태: Kubernetes v1.18 [beta]

SSH 터널을 대체로, Konnectivity 서비스는 컨트롤 플레인과 클러스터 간 통신에 TCP 레벨 프록시를 제공한다. Konnectivity 서비스는 컨트롤 플레인 네트워크의 Konnectivity 서버와 노드 네트워크의 Konnectivity 에이전트, 두 부분으로 구성된다. Konnectivity 에이전트는 Konnectivity 서버에 대한 연결을 시작하고 네트워크 연결을 유지한다. Konnectivity 서비스를 활성화한 후, 모든 컨트롤 플레인에서 노드로의 트래픽은 이 연결을 통과한다.

Konnectivity 서비스 태스크에 따라 클러스터에서 Konnectivity 서비스를 설정한다.

3 - 리스(Lease)

분산 시스템에는 종종 공유 리소스를 잠그고 노드 간의 활동을 조정하는 메커니즘을 제공하는 "리스(Lease)"가 필요하다. 쿠버네티스에서 "리스" 개념은 coordination.k8s.io API 그룹에 있는 Lease 오브젝트로 표현되며, 노드 하트비트 및 컴포넌트 수준의 리더 선출과 같은 시스템 핵심 기능에서 사용된다.

노드 하트비트

쿠버네티스는 리스 API를 사용하여 kubelet 노드의 하트비트를 쿠버네티스 API 서버에 전달한다. 모든 노드에는 같은 이름을 가진 Lease 오브젝트가 kube-node-lease 네임스페이스에 존재한다. 내부적으로, 모든 kubelet 하트비트는 이 Lease 오브젝트에 대한 업데이트 요청이며, 이 업데이트 요청은 spec.renewTime 필드를 업데이트한다. 쿠버네티스 컨트롤 플레인은 이 필드의 타임스탬프를 사용하여 해당 노드의 가용성을 확인한다.

자세한 내용은 노드 리스 오브젝트를 참조한다.

리더 선출

리스는 쿠버네티스에서도 특정 시간 동안 컴포넌트의 인스턴스 하나만 실행되도록 보장하는 데에도 사용된다. 이는 구성 요소의 한 인스턴스만 활성 상태로 실행되고 다른 인스턴스는 대기 상태여야 하는 kube-controller-managerkube-scheduler와 같은 컨트롤 플레인 컴포넌트의 고가용성 설정에서 사용된다.

API 서버 신원

기능 상태: Kubernetes v1.26 [beta]

쿠버네티스 v1.26부터, 각 kube-apiserver는 리스 API를 사용하여 시스템의 나머지 부분에 자신의 신원을 게시한다. 그 자체로는 특별히 유용하지는 않지만, 이것은 클라이언트가 쿠버네티스 컨트롤 플레인을 운영 중인 kube-apiserver 인스턴스 수를 파악할 수 있는 메커니즘을 제공한다. kube-apiserver 리스의 존재는 향후 각 kube-apiserver 간의 조정이 필요할 때 기능을 제공해 줄 수 있다.

각 kube-apiserver가 소유한 리스는 kube-system 네임스페이스에서kube-apiserver-<sha256-hash>라는 이름의 리스 오브젝트를 확인하여 볼 수 있다. 또는 k8s.io/component=kube-apiserver 레이블 설렉터를 사용하여 볼 수도 있다.

$ kubectl -n kube-system get lease -l k8s.io/component=kube-apiserver
NAME                                        HOLDER                                                                           AGE
kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a   kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4   5m33s
kube-apiserver-dz2dqprdpsgnm756t5rnov7yka   kube-apiserver-dz2dqprdpsgnm756t5rnov7yka_84f2a85d-37c1-4b14-b6b9-603e62e4896f   4m23s
kube-apiserver-fyloo45sdenffw2ugwaz3likua   kube-apiserver-fyloo45sdenffw2ugwaz3likua_c5ffa286-8a9a-45d4-91e7-61118ed58d2e   4m43s

리스 이름에 사용된 SHA256 해시는 kube-apiserver가 보는 OS 호스트 이름을 기반으로 한다. 각 kube-apiserver는 클러스터 내에서 고유한 호스트 이름을 사용하도록 구성해야 한다. 동일한 호스트명을 사용하는 새로운 kube-apiserver 인스턴스는 새 리스 오브젝트를 인스턴스화하는 대신 새로운 소유자 ID를 사용하여 기존 리스를 차지할 수 있다. kube-apiserver가 사용하는 호스트네임은 kubernetes.io/hostname 레이블의 값을 확인하여 확인할 수 있다.

$ kubectl -n kube-system get lease kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2022-11-30T15:37:15Z"
  labels:
    k8s.io/component: kube-apiserver
    kubernetes.io/hostname: kind-control-plane
  name: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a
  namespace: kube-system
  resourceVersion: "18171"
  uid: d6c68901-4ec5-4385-b1ef-2d783738da6c
spec:
  holderIdentity: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4
  leaseDurationSeconds: 3600
  renewTime: "2022-11-30T18:04:27.912073Z"

더 이상 존재하지 않는 kube-apiserver의 만료된 임대는 1시간 후에 새로운 kube-apiserver에 의해 가비지 컬렉션된다.

4 - 컨트롤러

로보틱스와 자동화에서 컨트롤 루프 는 시스템 상태를 조절하는 종료되지 않는 루프이다.

컨트롤 루프의 예시: 실내 온도 조절기

사용자는 온도를 설정해서, 사용자가 의도한 상태 를 온도 조절기에 알려준다. 실제 실내 온도는 현재 상태 이다. 온도 조절기는 장비를 켜거나 꺼서 현재 상태를 의도한 상태에 가깝게 만든다.

쿠버네티스에서 컨트롤러는 클러스터 의 상태를 관찰 한 다음, 필요한 경우에 생성 또는 변경을 요청하는 컨트롤 루프이다. 각 컨트롤러는 현재 클러스터 상태를 의도한 상태에 가깝게 이동한다.

컨트롤러 패턴

컨트롤러는 적어도 하나 이상의 쿠버네티스 리소스 유형을 추적한다. 이 오브젝트 는 의도한 상태를 표현하는 사양 필드를 가지고 있다. 해당 리소스의 컨트롤러(들)은 현재 상태를 의도한 상태에 가깝게 만드는 역할을 한다.

컨트롤러는 스스로 작업을 수행할 수 있다. 보다 일반적으로, 쿠버네티스에서는 컨트롤러가 API 서버 로 유용한 부수적인 효과가 있는 메시지를 발송한다. 그 예시는 아래에서 볼 수 있다.

API 서버를 통한 제어

잡(Job) 컨트롤러는 쿠버네티스 내장 컨트롤러의 예시이다. 내장 컨트롤러는 클러스터 API 서버와 상호 작용하며 상태를 관리한다.

잡은 단일 파드 또는 여러 파드를 실행하고, 작업을 수행한 다음 중지하는 쿠버네티스 리소스 이다.

(일단 스케줄되면, 파드 오브젝트는 kubelet 의 의도한 상태 중 일부가 된다.)

잡 컨트롤러가 새로운 작업을 확인하면, 클러스터 어딘가에서 노드 집합의 kubelet이 작업을 수행하기에 적합한 수의 파드를 실행하게 한다. 잡 컨트롤러는 어떤 파드 또는 컨테이너를 스스로 실행하지 않는다. 대신, 잡 컨트롤러는 API 서버에 파드를 생성하거나 삭제하도록 지시한다. 컨트롤 플레인의 다른 컴포넌트는 신규 정보 (예약 및 실행해야 하는 새 파드가 있다는 정보)에 대응하여, 결국 해당 작업을 완료시킨다.

새 잡을 생성하고 나면, 의도한 상태는 해당 잡을 완료하는 것이 된다. 잡 컨트롤러는 현재 상태를 의도한 상태에 가깝게 만들며, 사용자가 원하는 잡을 수행하기 위해 파드를 생성해서 잡이 완료에 가까워 지도록 한다.

또한, 컨트롤러는 오브젝트의 설정을 업데이트 한다. 예시: 잡을 위한 작업이 종료된 경우, 잡 컨트롤러는 잡 오브젝트가 Finished 로 표시되도록 업데이트한다.

(이것은 지금 방 온도가 설정한 온도인 것을 표시하기 위해 실내 온도 조절기의 빛을 끄는 것과 약간 비슷하다).

직접 제어

잡과는 대조적으로, 일부 컨트롤러는 클러스터 외부의 것을 변경해야 할 필요가 있다.

예를 들어, 만약 컨트롤 루프를 사용해서 클러스터에 충분한 노드들이 있도록 만드는 경우, 해당 컨트롤러는 필요할 때 새 노드를 설정할 수 있도록 현재 클러스터 외부의 무언가를 필요로 한다.

외부 상태와 상호 작용하는 컨트롤러는 API 서버에서 의도한 상태를 찾은 다음, 외부 시스템과 직접 통신해서 현재 상태를 보다 가깝게 만든다.

(실제로 클러스터의 노드를 수평으로 확장하는 컨트롤러가 있다.)

여기서 중요한 점은 컨트롤러가 의도한 상태를 가져오기 위해 약간의 변화를 주고, 현재 상태를 클러스터의 API 서버에 다시 보고한다는 것이다. 다른 컨트롤 루프는 보고된 데이터를 관찰하고 자체 조치를 할 수 있다.

온도 조절기 예에서 방이 매우 추우면 다른 컨트롤러가 서리 방지 히터를 켤 수도 있다. 쿠버네티스 클러스터에서는 쿠버네티스 확장을 통해 IP 주소 관리 도구, 스토리지 서비스, 클라우드 제공자의 API 및 기타 서비스 등과 간접적으로 연동하여 이를 구현한다.

의도한 상태와 현재 상태

쿠버네티스는 클라우드-네이티브 관점에서 시스템을 관찰하며, 지속적인 변화에 대응할 수 있다.

작업이 발생함에 따라 어떤 시점에서든 클러스터가 변경 될 수 있으며 컨트롤 루프가 자동으로 실패를 바로잡는다. 이는 잠재적으로, 클러스터가 안정적인 상태에 도달하지 못하는 것을 의미한다.

클러스터의 컨트롤러가 실행 중이고 유용한 변경을 수행할 수 있는 한, 전체 상태가 안정적인지 아닌지는 중요하지 않다.

디자인

디자인 원리에 따라, 쿠버네티스는 클러스터 상태의 각 특정 측면을 관리하는 많은 컨트롤러를 사용한다. 가장 일반적으로, 특정 컨트롤 루프 (컨트롤러)는 의도한 상태로서 한 종류의 리소스를 사용하고, 의도한 상태로 만들기 위해 다른 종류의 리소스를 관리한다. 예를 들어, 잡 컨트롤러는 잡 오브젝트(새 작업을 발견하기 위해)와 파드 오브젝트(잡을 실행하고, 완료된 시기를 확인하기 위해)를 추적한다. 이 경우 파드는 잡 컨트롤러가 생성하는 반면, 잡은 다른 컨트롤러가 생성한다.

컨트롤 루프들로 연결 구성된 하나의 모놀리식(monolithic) 집합보다, 간단한 컨트롤러를 여러 개 사용하는 것이 유용하다. 컨트롤러는 실패할 수 있으므로, 쿠버네티스는 이를 허용하도록 디자인되었다.

컨트롤러를 실행하는 방법

쿠버네티스에는 kube-controller-manager 내부에서 실행되는 내장된 컨트롤러 집합이 있다. 이 내장 컨트롤러는 중요한 핵심 동작을 제공한다.

디플로이먼트 컨트롤러와 잡 컨트롤러는 쿠버네티스의 자체("내장" 컨트롤러)로 제공되는 컨트롤러 예시이다. 쿠버네티스를 사용하면 복원력이 뛰어난 컨트롤 플레인을 실행할 수 있으므로, 어떤 내장 컨트롤러가 실패하더라도 다른 컨트롤 플레인의 일부가 작업을 이어서 수행한다.

컨트롤 플레인의 외부에서 실행하는 컨트롤러를 찾아서 쿠버네티스를 확장할 수 있다. 또는, 원하는 경우 새 컨트롤러를 직접 작성할 수 있다. 소유하고 있는 컨트롤러를 파드 집합으로서 실행하거나, 또는 쿠버네티스 외부에서 실행할 수 있다. 가장 적합한 것은 특정 컨트롤러의 기능에 따라 달라진다.

다음 내용

5 - 클라우드 컨트롤러 매니저

기능 상태: Kubernetes v1.11 [beta]

클라우드 인프라스트럭처 기술을 통해 퍼블릭, 프라이빗 그리고 하이브리드 클라우드에서 쿠버네티스를 실행할 수 있다. 쿠버네티스는 컴포넌트간의 긴밀한 결합 없이 자동화된 API 기반의 인프라스트럭처를 신뢰한다.

클라우드 컨트롤러 매니저는 클라우드별 컨트롤 로직을 포함하는 쿠버네티스 컨트롤 플레인 컴포넌트이다. 클라우드 컨트롤러 매니저를 통해 클러스터를 클라우드 공급자의 API에 연결하고, 해당 클라우드 플랫폼과 상호 작용하는 컴포넌트와 클러스터와만 상호 작용하는 컴포넌트를 구분할 수 있게 해 준다.

쿠버네티스와 기본 클라우드 인프라스터럭처 간의 상호 운용성 로직을 분리함으로써, cloud-controller-manager 컴포넌트는 클라우드 공급자가 주요 쿠버네티스 프로젝트와 다른 속도로 기능들을 릴리스할 수 있도록 한다.

클라우드 컨트롤러 매니저는 다양한 클라우드 공급자가 자신의 플랫폼에 쿠버네티스를 통합할 수 있도록 하는 플러그인 메커니즘을 사용해서 구성된다.

디자인

쿠버네티스 컴포넌트

클라우드 컨트롤러 매니저는 컨트롤 플레인에서 복제된 프로세스의 집합으로 실행된다(일반적으로, 파드의 컨테이너). 각 클라우드 컨트롤러 매니저는 단일 프로세스에 여러 컨트롤러를 구현한다.

클라우드 컨트롤러 매니저의 기능

클라우드 컨틀롤러 매니저의 내부 컨트롤러에는 다음 컨트롤러들이 포함된다.

노드 컨트롤러

노드 컨트롤러는 클라우드 인프라스트럭처에 새 서버가 생성될 때 노드 오브젝트를 업데이트하는 역할을 한다. 노드 컨트롤러는 클라우드 공급자의 사용자 테넌시 내에서 실행되는 호스트에 대한 정보를 가져온다. 노드 컨트롤러는 다음 기능들을 수행한다.

  1. 클라우드 공급자 API를 통해 획득한 해당 서버의 고유 ID를 노드 오브젝트에 업데이트한다.
  2. 클라우드 관련 정보(예를 들어, 노드가 배포되는 지역과 사용 가능한 리소스(CPU, 메모리 등))를 사용해서 노드 오브젝트에 어노테이션과 레이블을 작성한다.
  3. 노드의 호스트 이름과 네트워크 주소를 가져온다.
  4. 노드의 상태를 확인한다. 노드가 응답하지 않는 경우, 이 컨트롤러는 사용자가 이용하는 클라우드 공급자의 API를 통해 서버가 비활성화됨 / 삭제됨 / 종료됨인지 확인한다. 노드가 클라우드에서 삭제된 경우, 컨트롤러는 사용자의 쿠버네티스 클러스터에서 노드 오브젝트를 삭제한다.

일부 클라우드 공급자의 구현에서는 이를 노드 컨트롤러와 별도의 노드 라이프사이클 컨트롤러로 분리한다.

라우트 컨트롤러

라우트 컨트롤러는 사용자의 쿠버네티스 클러스터의 다른 노드에 있는 각각의 컨테이너가 서로 통신할 수 있도록 클라우드에서 라우트를 적절히 구성해야 한다.

클라우드 공급자에 따라 라우트 컨트롤러는 파드 네트워크 IP 주소 블록을 할당할 수도 있다.

서비스 컨트롤러

서비스 는 관리형 로드 밸런서, IP 주소, 네트워크 패킷 필터링 그리고 대상 상태 확인과 같은 클라우드 인프라스트럭처 컴포넌트와 통합된다. 서비스 컨트롤러는 사용자의 클라우드 공급자 API와 상호 작용해서 필요한 서비스 리소스를 선언할 때 로드 밸런서와 기타 인프라스트럭처 컴포넌트를 설정한다.

인가

이 섹션에서는 클라우드 컨트롤러 매니저가 작업을 수행하기 위해 다양한 API 오브젝트에 필요한 접근 권한을 세분화한다.

노드 컨트롤러

노드 컨트롤러는 노드 오브젝트에서만 작동한다. 노드 오브젝트를 읽고, 수정하려면 전체 접근 권한이 필요하다.

v1/Node:

  • Get
  • List
  • Create
  • Update
  • Patch
  • Watch
  • Delete

라우트 컨트롤러

라우트 컨트롤러가 노드 오브젝트의 생성을 수신하고 적절하게 라우트를 구성한다. 노드 오브젝트에 대한 접근 권한이 필요하다.

v1/Node:

  • Get

서비스 컨트롤러

서비스 컨트롤러는 서비스 오브젝트 생성, 업데이트 그리고 삭제 이벤트를 수신한 다음 해당 서비스에 대한 엔드포인트를 적절하게 구성한다(엔드포인트슬라이스(EndpointSlice)의 경우, kube-controller-manager가 필요에 따라 이들을 관리한다).

서비스에 접근하려면, 목록과 감시 접근 권한이 필요하다. 서비스를 업데이트하려면, 패치와 업데이트 접근 권한이 필요하다.

서비스에 대한 엔드포인트 리소스를 설정하려면 생성, 목록, 가져오기, 감시 그리고 업데이트에 대한 접근 권한이 필요하다.

v1/Service:

  • List
  • Get
  • Watch
  • Patch
  • Update

그 외의 것들

클라우드 컨트롤러 매니저의 핵심 구현을 위해 이벤트 오브젝트를 생성하고, 안전한 작동을 보장하기 위해 서비스어카운트(ServiceAccounts)를 생성해야 한다.

v1/Event:

  • Create
  • Patch
  • Update

v1/ServiceAccount:

  • Create

클라우드 컨트롤러 매니저의 RBAC 클러스터롤(ClusterRole)은 다음과 같다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cloud-controller-manager
rules:
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - '*'
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - list
  - patch
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - endpoints
  verbs:
  - create
  - get
  - list
  - watch
  - update

다음 내용

클라우드 컨트롤러 매니저 관리에는 클라우드 컨트롤러 매니저의 실행과 관리에 대한 지침이 있다.

클라우드 컨트롤러 매니저를 사용하기 위해 HA 컨트롤 플레인을 업그레이드하려면, 클라우드 컨트롤러 매니저를 사용하기 위해 복제된 컨트롤 플레인 마이그레이션 하기를 참고한다.

자체 클라우드 컨트롤러 매니저를 구현하거나 기존 프로젝트를 확장하는 방법을 알고 싶은가?

클라우드 컨트롤러 매니저는 Go 인터페이스를 사용함으로써, 어떠한 클라우드에 대한 구현체(implementation)라도 플러그인 될 수 있도록 한다. 구체적으로는, kubernetes/cloud-providercloud.go에 정의된 CloudProvider 인터페이스를 사용한다.

이 문서(노드, 라우트와 서비스)에서 강조된 공유 컨트롤러의 구현과 공유 cloudprovider 인터페이스와 함께 일부 스캐폴딩(scaffolding)은 쿠버네티스 핵심의 일부이다. 클라우드 공급자 전용 구현은 쿠버네티스의 핵심 바깥에 있으며 CloudProvider 인터페이스를 구현한다.

플러그인 개발에 대한 자세한 내용은 클라우드 컨트롤러 매니저 개발하기를 참조한다.

6 - 컨테이너 런타임 인터페이스(CRI)

컨테이너 런타임 인터페이스(CRI)는 클러스터 컴포넌트를 다시 컴파일하지 않아도 Kubelet이 다양한 컨테이너 런타임을 사용할 수 있도록 하는 플러그인 인터페이스다.

클러스터의 모든 노드에 동작 중인 컨테이너 런타임이 존재해야, kubelet파드들과 컨테이너들을 구동할 수 있다.

컨테이너 런타임 인터페이스(CRI)는 kubelet과 컨테이너 런타임 사이의 통신을 위한 주요 프로토콜이다.

쿠버네티스 컨테이너 런타임 인터페이스(CRI)는 클러스터 컴포넌트 kubeletcontainer runtime 사이의 통신을 위한 주요 gRPC 프로토콜을 정의한다.

API

기능 상태: Kubernetes v1.23 [stable]

Kubelet은 gRPC를 통해 컨테이너 런타임과 연결할 때 클라이언트의 역할을 수행한다. 런타임과 이미지 서비스 엔드포인트는 컨테이너 런타임 내에서 사용 가능해야 하며, 이는 각각 Kubelet 내에서 --image-service-endpoint--container-runtime-endpoint 커맨드라인 플래그 를 통해 설정할 수 있다.

쿠버네티스 v1.32에서는, Kubelet은 CRI v1을 사용하는 것을 권장한다. 컨테이너 런타임이 CRI v1 버전을 지원하지 않는다면, Kubelet은 지원 가능한 이전 지원 버전으로 협상을 시도한다. 또한 v1.32 Kubelet은 CRI v1alpha2버전도 협상할 수 있지만, 해당 버전은 사용 중단(deprecated)으로 간주한다. Kubelet이 지원되는 CRI 버전을 협상할 수 없는 경우, Kubelet은 협상을 포기하고 노드로 등록하지 않는다.

업그레이드

쿠버네티스를 업그레이드할 때, Kubelet은 컴포넌트의 재시작 시점에서 최신 CRI 버전을 자동으로 선택하려고 시도한다. 이 과정이 실패하면 위에서 언급한 대로 이전 버전을 선택하는 과정을 거친다. 컨테이너 런타임이 업그레이드되어 gRPC 재다이얼이 필요하다면, 컨테이너 런타임도 처음에 선택된 버전을 지원해야 하며, 그렇지 못한 경우 재다이얼은 실패하게 될 것이다. 이 과정은 Kubelet의 재시작이 필요하다.

다음 내용

7 - 가비지(Garbage) 수집

쿠버네티스가 클러스터 자원을 정리하기 위해 사용하는 다양한 방법을 종합한 용어이다. 다음과 같은 리소스를 정리한다:

소유자(Owners)와 종속(dependents)

쿠버네티스의 많은 오브젝트는 owner references를 통해 서로 연결되어 있다.

소유자 참조(Owner references)는 컨트롤 플레인에게 어떤 오브젝트가 서로 종속적인지를 알려준다. 쿠버네티스는 소유자 참조를 사용하여 컨트롤 플레인과 다른 API 클라이언트에게 오브젝트를 삭제하기 전 관련 리소스를 정리하는 기회를 제공한다. 대부분의 경우, 쿠버네티스는 소유자 참조를 자동으로 관리한다.

소유권(Ownership)은 일부 리소스가 사용하는 레이블과 셀렉터 메커니즘과는 다르다. 예를 들어, EndpointSlice 오브젝트를 생성하는 서비스를 생각해보자. 서비스는 레이블을 사용해 컨트롤 플레인이 어떤 EndpointSlice 오브젝트가 해당 서비스에 의해 사용되는지 판단하는 데 도움을 준다. 레이블과 더불어, 서비스를 대신해 관리되는 각 EndpointSlice 오브젝트는 소유자 참조를 가진다. 소유자 참조는 쿠버네티스의 다른 부분이 제어하지 않는 오브젝트를 방해하는 것을 방지하는 데 도움을 준다.

캐스케이딩(Cascading) 삭제

쿠버네티스는 오브젝트를 삭제할 때 더 이상 소유자 참조가 없는지, 예를 들어 레플리카셋을 삭제할 때, 남겨진 파드가 없는지 확인하고 삭제한다. 오브젝트를 삭제할 때 쿠버네티스가 오브젝트의 종속 오브젝트들을 자동으로 삭제할 지 여부를 제어할 수 있다. 이 과정을 캐스케이딩 삭제라고 한다. 캐스케이딩 삭제에는 다음과 같은 두 가지 종류가 있다.

  • 포그라운드 캐스케이딩 삭제(Foreground cascading deletion)
  • 백그라운드 캐스케이딩 삭제(Background cascading deletion)

또한 쿠버네티스의 finalizers를 사용하여 가비지 수집이 소유자 참조가 있는 자원을 언제 어떻게 삭제할 것인지 제어할 수 있다.

포그라운드 캐스케이딩 삭제

포그라운드 캐스케이딩 삭제에서는 삭제하려는 소유자 오브젝트가 먼저 삭제 중 상태가 된다. 이 상태에서는 소유자 오브젝트에게 다음과 같은 일이 일어난다:

  • 쿠버네티스 API 서버가 오브젝트의 metadata.deletionTimestamp 필드를 오브젝트가 삭제 표시된 시간으로 설정한다.
  • 쿠버네티스 API 서버가 metadata.finalizers 필드를 foregroundDeletion로 설정한다.
  • 오브젝트는 삭제 과정이 완료되기 전까지 쿠버네티스 API를 통해 조회할 수 있다.

소유자 오브젝트가 삭제 중 상태가 된 이후, 컨트롤러는 종속 오브젝트들을 삭제한다. 모든 종속 오브젝트들이 삭제되고나면, 컨트롤러가 소유자 오브젝트를 삭제한다. 이 시점에서 오브젝트는 더 이상 쿠버네티스 API를 통해 조회할 수 없다.

포그라운드 캐스케이딩 삭제 중에 소유자 오브젝트의 삭제를 막는 종속 오브젝트는ownerReference.blockOwnerDeletion=true필드를 가진 오브젝트다. 더 자세한 내용은 Use foreground cascading deletion를 참고한다.

백그라운드 캐스케이딩 삭제

백그라운드 캐스케이딩 삭제에서는 쿠버네티스 API 서버가 소유자 오브젝트를 즉시 삭제하고 백그라운드에서 컨트롤러가 종속 오브젝트들을 삭제한다. 쿠버네티스는 수동으로 포그라운드 삭제를 사용하거나 종속 오브젝트를 분리하지 않는다면, 기본적으로 백그라운드 캐스케이딩 삭제를 사용한다.

더 자세한 내용은 Use background cascading deletion를 참고한다.

분리된 종속 (Orphaned dependents)

쿠버네티스가 소유자 오브젝트를 삭제할 때, 남은 종속 오브젝트는 분리된 오브젝트라고 부른다. 기본적으로 쿠버네티스는 종속 오브젝트를 삭제한다. 이 행동을 오버라이드하는 방법을 보려면, Delete owner objects and orphan dependents를 참고한다.

사용되지 않는 컨테이너와 이미지 가비지 수집

kubelet은 사용되지 않는 이미지에 대한 가비지 수집을 5분마다, 컨테이너에 대한 가비지 수집을 1분마다 수행한다. 외부 가비지 수집 도구는 kubelet 의 행동을 중단시키고 존재해야만 하는 컨테이너를 삭제할 수 있으므로 사용을 피해야 한다.

사용되지 않는 컨테이너와 이미지에 대한 가비지 수집 옵션을 구성하려면, configuration file 사용하여 kubelet 을 수정하거나 KubeletConfiguration 리소스 타입의 가비지 수집과 관련된 파라미터를 수정한다.

컨테이너 이미지 라이프사이클

쿠버네티스는 kubelet의 일부인 이미지 관리자cadvisor와 협동하여 모든 이미지의 라이프사이클을 관리한다. kubelet은 가비지 수집 결정을 내릴 때, 다음 디스크 사용량 제한을 고려한다.

  • HighThresholdPercent
  • LowThresholdPercent

HighThresholdPercent 값을 초과한 디스크 사용량은 마지막으로 사용된 시간을 기준으로 오래된 이미지순서대로 이미지를 삭제하는 가비지 수집을 트리거한다. kubelet은 디스크 사용량이 LowThresholdPercent 값에 도달할 때까지 이미지를 삭제한다.

컨테이너 이미지 가비지 수집

kubelet은 사용자가 정의할 수 있는 다음 변수들을 기반으로 사용되지 않는 컨테이너들을 삭제한다:

  • MinAge: kubelet이 가비지 수집할 수 있는 최소 나이. 0으로 세팅하여 비활성화할 수 있다.
  • MaxPerPodContainer: 각 파드 쌍이 가질 수 있는 죽은 컨테이너의 최대 개수. 0으로 세팅하여 비활성화할 수 있다.
  • MaxContainers: 클러스터가 가질 수 있는 죽은 컨테이너의 최대 개수 0으로 세팅하여 비활성화할 수 있다.

위 변수와 더불어, kubelet은 식별할 수 없고 삭제된 컨테이너들을 오래된 순서대로 가비지 수집한다.

MaxPerPodContainerMaxContainer는 파드의 최대 컨테이너 개수(MaxPerPodContainer)를 유지하는 것이 전체 죽은 컨테이너의 개수 제한(MaxContainers)을 초과하게 될 때, 서로 충돌이 발생할 수 있다. 이 상황에서 kubelet은 충돌을 해결하기 위해 MaxPodPerContainer를 조절한다. 최악의 시나리오에서는 MaxPerPodContainer1로 다운그레이드하고 가장 오래된 컨테이너들을 축출한다. 또한, 삭제된 파드가 소유한 컨테이너들은 MinAge보다 오래되었을 때 삭제된다.

가비지 수집 구성하기

자원을 관리하는 컨트롤러의 옵션을 구성하여 가비지 컬렉션을 수정할 수 있다. 다음 페이지에서 어떻게 가비지 수집을 구성할 수 있는지 확인할 수 있다.

다음 내용