이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.

이 페이지의 일반 화면으로 돌아가기.

문서

1 - 쿠버네티스 문서

쿠버네티스는 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하기 위한 오픈소스 컨테이너 오케스트레이션 엔진이다. 오픈소스 프로젝트는 Cloud Native Computing Foundation에서 주관한다.

1.1 - 사용 가능한 문서의 버전

이 웹사이트에서는 쿠버네티스 최신 버전 및 이전 4개 버전에 대한 문서를 제공하고 있다.

2 - 시작하기

본 섹션에는 쿠버네티스를 설정하고 실행하는 다양한 방법이 나열되어 있다. 쿠버네티스를 설치할 때는 유지보수의 용이성, 보안, 제어, 사용 가능한 리소스, 그리고 클러스터를 운영하고 관리하기 위해 필요한 전문성을 기반으로 설치 유형을 선택한다.

쿠버네티스를 다운로드하여 로컬 머신에, 클라우드에, 데이터센터에 쿠버네티스 클러스터를 구축할 수 있다.

쿠버네티스 클러스터를 직접 관리하고 싶지 않다면, 인증된 플랫폼과 같은 매니지드 서비스를 선택할 수도 있다. 광범위한 클라우드 또는 베어 메탈 환경에 걸쳐 사용할 수 있는 표준화된/맞춤형 솔루션도 있다.

학습 환경

쿠버네티스를 배우고 있다면, 쿠버네티스 커뮤니티에서 지원하는 도구나, 로컬 머신에서 쿠버네티스를 설치하기 위한 생태계 내의 도구를 사용한다. 도구 설치를 살펴본다.

프로덕션 환경

프로덕션 환경을 위한 솔루션을 평가할 때에는, 쿠버네티스 클러스터(또는 추상화된 객체) 운영에 대한 어떤 측면을 스스로 관리하기를 원하는지, 또는 제공자에게 넘기기를 원하는지 고려한다.

클러스터를 직접 관리하는 경우, 공식적으로 지원되는 쿠버네티스 구축 도구는 kubeadm이다.

다음 내용

쿠버네티스의 컨트롤 플레인은 리눅스에서 실행되도록 설계되었다. 클러스터 내에서는 리눅스 또는 다른 운영 체제(예: 윈도우)에서 애플리케이션을 실행할 수 있다.

2.1 - 학습 환경

2.2 - 프로덕션 환경

프로덕션 수준의 쿠버네티스 클러스터 생성

프로덕션 수준의 쿠버네티스 클러스터에는 계획과 준비가 필요하다. 쿠버네티스 클러스터에 중요한 워크로드를 실행하려면 클러스터를 탄력적이도록 구성해야 한다. 이 페이지에서는 프로덕션용 클러스터를 설정하거나 기존 클러스터를 프로덕션용으로 업그레이드하기 위해 수행할 수 있는 단계를 설명한다. 이미 프로덕션 구성 내용에 익숙하여 단지 링크를 찾고 있다면, 다음 내용을 참고한다.

프로덕션 고려 사항

일반적으로 프로덕션 쿠버네티스 클러스터 환경에는 개인 학습용, 개발용 또는 테스트 환경용 클러스터보다 더 많은 요구 사항이 있다. 프로덕션 환경에는 많은 사용자의 보안 액세스, 일관된 가용성 및 변화하는 요구를 충족하기 위한 리소스가 필요할 수 있다.

프로덕션 쿠버네티스 환경이 상주할 위치(온 프레미스 또는 클라우드)와 직접 처리하거나 다른 사람에게 맡길 관리의 양을 결정할 때, 쿠버네티스 클러스터에 대한 요구 사항이 다음 이슈에 의해 어떻게 영향을 받는지 고려해야 한다.

  • 가용성: 단일 머신 쿠버네티스 학습 환경은 SPOF(Single Point of Failure, 단일 장애 지점) 이슈를 갖고 있다. 고가용성 클러스터를 만드는 것에는 다음과 같은 고려 사항이 있다.

    • 컨트롤 플레인과 워크 노드를 분리
    • 컨트롤 플레인 구성요소를 여러 노드에 복제
    • 클러스터의 API 서버로 가는 트래픽을 로드밸런싱
    • 워커 노드를 충분히 운영하거나, 워크로드 변경에 따라 빠르게 제공할 수 있도록 보장
  • 스케일링: 프로덕션 쿠버네티스 환경에 들어오는 요청의 양의 일정할 것으로 예상된다면, 필요한 만큼의 용량(capacity)을 증설하고 마무리할 수도 있다. 하지만, 요청의 양이 시간에 따라 점점 증가하거나 계절, 이벤트 등에 의해 극적으로 변동할 것으로 예상된다면, 컨트롤 플레인과 워커 노드로의 요청 증가로 인한 압박을 해소하기 위해 스케일 업 하거나 잉여 자원을 줄이기 위해 스케일 다운 하는 것에 대해 고려해야 한다.

  • 보안 및 접근 관리: 학습을 위한 쿠버네티스 클러스터에는 완전한 관리 권한을 가질 수 있다. 하지만 중요한 워크로드를 실행하며 두 명 이상의 사용자가 있는 공유 클러스터에는 누가, 그리고 무엇이 클러스터 자원에 접근할 수 있는지에 대해서 보다 정교한 접근 방식이 필요하다. 역할 기반 접근 제어(RBAC) 및 기타 보안 메커니즘을 사용하여, 사용자와 워크로드가 필요한 자원에 액세스할 수 있게 하면서도 워크로드와 클러스터를 안전하게 유지할 수 있다. 정책컨테이너 리소스를 관리하여, 사용자 및 워크로드가 접근할 수 있는 자원에 대한 제한을 설정할 수 있다.

쿠버네티스 프로덕션 환경을 직접 구축하기 전에, 이 작업의 일부 또는 전체를 턴키 클라우드 솔루션 제공 업체 또는 기타 쿠버네티스 파트너에게 넘기는 것을 고려할 수 있다. 다음과 같은 옵션이 있다.

  • 서버리스: 클러스터를 전혀 관리하지 않고 타사 장비에서 워크로드를 실행하기만 하면 된다. CPU 사용량, 메모리 및 디스크 요청과 같은 항목에 대한 요금이 부과된다.
  • 관리형 컨트롤 플레인: 쿠버네티스 서비스 공급자가 클러스터 컨트롤 플레인의 확장 및 가용성을 관리하고 패치 및 업그레이드를 처리하도록 한다.
  • 관리형 워커 노드: 필요에 맞는 노드 풀을 정의하면, 쿠버네티스 서비스 공급자는 해당 노드의 가용성 및 필요 시 업그레이드 제공을 보장한다.
  • 통합: 쿠버네티스를 스토리지, 컨테이너 레지스트리, 인증 방법 및 개발 도구와 같이 사용자가 필요로 하는 여러 서비스를 통합 제공하는 업체도 있다.

프로덕션 쿠버네티스 클러스터를 직접 구축하든 파트너와 협력하든, 요구 사항이 컨트롤 플레인, 워커 노드, 사용자 접근, 워크로드 자원과 관련되기 때문에, 다음 섹션들을 검토하는 것이 바람직하다.

프로덕션 클러스터 구성

프로덕션 수준 쿠버네티스 클러스터에서, 컨트롤 플레인은 다양한 방식으로 여러 컴퓨터에 분산될 수 있는 서비스들을 통해 클러스터를 관리한다. 반면, 각 워커 노드는 쿠버네티스 파드를 실행하도록 구성된 단일 엔티티를 나타낸다.

프로덕션 컨트롤 플레인

가장 간단한 쿠버네티스 클러스터는 모든 컨트롤 플레인 및 워커 노드 서비스가 하나의 머신에 실행되는 클러스터이다. 쿠버네티스 컴포넌트 그림에 명시된 대로, 워커 노드를 추가하여 해당 환경을 확장할 수 있다. 클러스터를 단기간만 사용하거나, 심각한 문제가 발생한 경우 폐기하는 것이 가능하다면, 이 방식을 선택할 수 있다.

그러나 더 영구적이고 가용성이 높은 클러스터가 필요한 경우 컨트롤 플레인 확장을 고려해야 한다. 설계 상, 단일 시스템에서 실행되는 단일 시스템 컨트롤 플레인 서비스는 가용성이 높지 않다. 클러스터를 계속 유지하면서 문제가 발생한 경우 복구할 수 있는지 여부가 중요한 경우, 다음 사항들을 고려한다.

  • 배포 도구 선택: kubeadm, kops, kubespray와 같은 도구를 이용해 컨트롤 플레인을 배포할 수 있다. 배포 도구로 쿠버네티스 설치하기에서 여러 배포 도구를 이용한 프로덕션 수준 배포에 대한 팁을 확인한다. 배포 시, 다양한 컨테이너 런타임을 사용할 수 있다.
  • 인증서 관리: 컨트롤 플레인 서비스 간의 보안 통신은 인증서를 사용하여 구현된다. 인증서는 배포 중에 자동으로 생성되거나, 또는 자체 인증 기관을 사용하여 생성할 수 있다. PKI 인증서 및 요구 조건에서 상세 사항을 확인한다.
  • apiserver를 위한 로드밸런서 구성: 여러 노드에서 실행되는 apiserver 서비스 인스턴스에 외부 API 호출을 분산할 수 있도록 로드밸런서를 구성한다. 외부 로드밸런서 생성하기에서 상세 사항을 확인한다.
  • etcd 서비스 분리 및 백업: etcd 서비스는 다른 컨트롤 플레인 서비스와 동일한 시스템에서 실행되거나, 또는 추가 보안 및 가용성을 위해 별도의 시스템에서 실행될 수 있다. etcd는 클러스터 구성 데이터를 저장하므로 필요한 경우 해당 데이터베이스를 복구할 수 있도록 etcd 데이터베이스를 정기적으로 백업해야 한다. etcd FAQ에서 etcd 구성 및 사용 상세를 확인한다. 쿠버네티스를 위한 etcd 클러스터 운영하기kubeadm을 이용하여 고가용성 etcd 생성하기에서 상세 사항을 확인한다.
  • 다중 컨트롤 플레인 시스템 구성: 고가용성을 위해, 컨트롤 플레인은 단일 머신으로 제한되지 않아야 한다. 컨트롤 플레인 서비스가 init 서비스(예: systemd)에 의해 실행되는 경우, 각 서비스는 최소 3대의 머신에서 실행되어야 한다. 그러나, 컨트롤 플레인 서비스를 쿠버네티스 상의 파드 형태로 실행하면 각 서비스 복제본 요청이 보장된다. 스케줄러는 내결함성이 있어야 하고, 고가용성은 필요하지 않다. 일부 배포 도구는 쿠버네티스 서비스의 리더 선출을 수행하기 위해 Raft 합의 알고리즘을 설정한다. 리더를 맡은 서비스가 사라지면 다른 서비스가 스스로 리더가 되어 인계를 받는다.
  • 다중 영역(zone)으로 확장: 클러스터를 항상 사용 가능한 상태로 유지하는 것이 중요하다면 여러 데이터 센터(클라우드 환경에서는 '영역'이라고 함)에서 실행되는 클러스터를 만드는 것이 좋다. 영역의 그룹을 지역(region)이라고 한다. 동일한 지역의 여러 영역에 클러스터를 분산하면 하나의 영역을 사용할 수 없게 된 경우에도 클러스터가 계속 작동할 가능성을 높일 수 있다. 여러 영역에서 실행에서 상세 사항을 확인한다.
  • 구동 중인 기능 관리: 클러스터를 계속 유지하려면, 상태 및 보안을 유지하기 위해 수행해야 하는 작업이 있다. 예를 들어 kubeadm으로 클러스터를 생성한 경우, 인증서 관리kubeadm 클러스터 업그레이드하기에 대해 도움이 되는 가이드가 있다. 클러스터 운영하기에서 더 많은 쿠버네티스 관리 작업을 볼 수 있다.

컨트롤 플레인 서비스를 실행할 때 사용 가능한 옵션에 대해 보려면, kube-apiserver, kube-controller-manager, kube-scheduler를 참조한다. 고가용성 컨트롤 플레인 예제는 고가용성 토폴로지를 위한 옵션, kubeadm을 이용하여 고가용성 클러스터 생성하기, 쿠버네티스를 위한 etcd 클러스터 운영하기를 참조한다. etcd 백업 계획을 세우려면 etcd 클러스터 백업하기를 참고한다.

프로덕션 워커 노드

프로덕션 수준 워크로드는 복원력이 있어야 하고, 이들이 의존하는 모든 것들(예: CoreDNS)도 복원력이 있어야 한다. 컨트롤 플레인을 자체적으로 관리하든 클라우드 공급자가 대신 수행하도록 하든 상관없이, 워커 노드(간단히 노드라고도 함)를 어떤 방법으로 관리할지 고려해야 한다.

  • 노드 구성하기: 노드는 물리적 또는 가상 머신일 수 있다. 직접 노드를 만들고 관리하려면 지원되는 운영 체제를 설치한 다음 적절한 노드 서비스를 추가하고 실행한다. 다음을 고려해야 한다.
    • 워크로드의 요구 사항 (노드가 적절한 메모리, CPU, 디스크 속도, 저장 용량을 갖도록 구성)
    • 일반적인 컴퓨터 시스템이면 되는지, 아니면 GPU, 윈도우 노드, 또는 VM 격리를 필요로 하는 워크로드가 있는지
  • 노드 검증하기: 노드 구성 검증하기에서 노드가 쿠버네티스 클러스터에 조인(join)에 필요한 요구 사항을 만족하는지 확인하는 방법을 알아본다.
  • 클러스터에 노드 추가하기: 클러스터를 자체적으로 관리하는 경우, 머신을 준비하고, 클러스터의 apiserver에 이를 수동으로 추가하거나 또는 머신이 스스로 등록하도록 하여 노드를 추가할 수 있다. 이러한 방식으로 노드를 추가하는 방법을 보려면 노드 섹션을 확인한다.
  • 클러스터에 윈도우 노드 추가하기: 윈도우 컨테이너로 구현된 워크로드를 실행할 수 있도록, 쿠버네티스는 윈도우 워커 노드를 지원한다. 쿠버네티스에서의 윈도우에서 상세 사항을 확인한다.
  • 노드 스케일링: 클러스터가 최종적으로 필요로 하게 될 용량만큼 확장하는 것에 대한 계획이 있어야 한다. 실행해야 하는 파드 및 컨테이너 수에 따라 필요한 노드 수를 판별하려면 대형 클러스터에 대한 고려 사항을 확인한다. 만약 노드를 직접 관리한다면, 직접 물리적 장비를 구입하고 설치해야 할 수도 있음을 의미한다.
  • 노드 자동 스케일링: 대부분의 클라우드 공급자는 비정상 노드를 교체하거나 수요에 따라 노드 수를 늘리거나 줄일 수 있도록 클러스터 오토스케일러를 지원한다. 자주 묻는 질문에서 오토스케일러가 어떻게 동작하는지, 배치 섹션에서 각 클라우드 공급자별로 어떻게 구현했는지를 확인한다. 온프레미스의 경우, 필요에 따라 새 노드를 가동하도록 스크립트를 구성할 수 있는 가상화 플랫폼이 있다.
  • 노드 헬스 체크 구성: 중요한 워크로드의 경우, 해당 노드에서 실행 중인 노드와 파드의 상태가 정상인지 확인하고 싶을 것이다. Node Problem Detector 데몬을 사용하면 노드가 정상인지 확인할 수 있다.

프로덕션 사용자 관리

프로덕션에서는, 클러스터를 한 명 또는 여러 명이 사용하던 모델에서 수십에서 수백 명이 사용하는 모델로 바꿔야 하는 경우가 발생할 수 있다. 학습 환경 또는 플랫폼 프로토타입에서는 모든 작업에 대한 단일 관리 계정으로도 충분할 수 있다. 프로덕션에서는 여러 네임스페이스에 대한, 액세스 수준이 각각 다른 더 많은 계정이 필요하다.

프로덕션 수준의 클러스터를 사용한다는 것은 다른 사용자의 액세스를 선택적으로 허용할 방법을 결정하는 것을 의미한다. 특히 클러스터에 액세스를 시도하는 사용자의 신원을 확인(인증, authentication)하고 요청한 작업을 수행할 권한이 있는지 결정(인가, authorization)하기 위한 다음과 같은 전략을 선택해야 한다.

  • 인증: apiserver는 클라이언트 인증서, 전달자 토큰, 인증 프록시 또는 HTTP 기본 인증을 사용하여 사용자를 인증할 수 있다. 사용자는 인증 방법을 선택하여 사용할 수 있다. apiserver는 또한 플러그인을 사용하여 LDAP 또는 Kerberos와 같은 조직의 기존 인증 방법을 활용할 수 있다. 쿠버네티스 사용자를 인증하는 다양한 방법에 대한 설명은 인증을 참조한다.
  • 인가: 일반 사용자 인가를 위해, RBAC 와 ABAC 중 하나를 선택하여 사용할 수 있다. 인가 개요에서 사용자 계정과 서비스 어카운트 인가를 위한 여러 가지 모드를 확인할 수 있다.
    • 역할 기반 접근 제어 (RBAC): 인증된 사용자에게 특정 권한 집합을 허용하여 클러스터에 대한 액세스를 할당할 수 있다. 특정 네임스페이스(Role) 또는 전체 클러스터(ClusterRole)에 권한을 할당할 수 있다. 그 뒤에 RoleBindings 및 ClusterRoleBindings를 사용하여 해당 권한을 특정 사용자에게 연결할 수 있다.
    • 속성 기반 접근 제어 (ABAC): 클러스터의 리소스 속성을 기반으로 정책을 생성하고 이러한 속성을 기반으로 액세스를 허용하거나 거부할 수 있다. 정책 파일의 각 줄은 버전 관리 속성(apiVersion 및 종류), 그리고 '대상(사용자 또는 그룹)', '리소스 속성', '비 리소스 속성(/version 또는 /apis)' 및 '읽기 전용'과 일치하는 사양 속성 맵을 식별한다. 자세한 내용은 예시를 참조한다.

프로덕션 쿠버네티스 클러스터에 인증과 인가를 설정할 때, 다음의 사항을 고려해야 한다.

  • 인가 모드 설정: 쿠버네티스 API 서버(kube-apiserver)를 실행할 때, --authorization-mode 플래그를 사용하여 인증 모드를 설정해야 한다. 예를 들어, (*/etc/kubernetes/manifests*에 있는) kube-adminserver.yaml 파일 안의 플래그를 Node,RBAC으로 설정할 수 있다. 이렇게 하여 인증된 요청이 Node 인가와 RBAC 인가를 사용할 수 있게 된다.
  • 사용자 인증서와 롤 바인딩 생성(RBAC을 사용하는 경우): RBAC 인증을 사용하는 경우, 사용자는 클러스터 CA가 서명한 CSR(CertificateSigningRequest)을 만들 수 있다. 그 뒤에 각 사용자에게 역할 및 ClusterRoles를 바인딩할 수 있다. 자세한 내용은 인증서 서명 요청을 참조한다.
  • 속성을 포함하는 정책 생성(ABAC을 사용하는 경우): ABAC 인증을 사용하는 경우, 속성의 집합으로 정책을 생성하여, 인증된 사용자 또는 그룹이 특정 리소스(예: 파드), 네임스페이스, 또는 apiGroup에 접근할 수 있도록 한다. 예시에서 더 많은 정보를 확인한다.
  • 어드미션 컨트롤러 도입 고려: 웹훅 토큰 인증은 API 서버를 통해 들어오는 요청의 인가에 사용할 수 있는 추가적인 방법이다. 웹훅 및 다른 인가 형식을 사용하려면 API 서버에 어드미션 컨트롤러를 추가해야 한다.

워크로드에 자원 제한 걸기

프로덕션 워크로드의 요구 사항이 쿠버네티스 컨트롤 플레인 안팎의 압박을 초래할 수 있다. 워크로드의 요구 사항을 충족하도록 클러스터를 구성할 때 다음 항목을 고려한다.

  • 네임스페이스 제한 설정: 메모리, CPU와 같은 자원의 네임스페이스 별 쿼터를 설정한다. 메모리, CPU 와 API 리소스 관리에서 상세 사항을 확인한다. 계층적 네임스페이스를 설정하여 제한을 상속할 수도 있다.
  • DNS 요청에 대한 대비: 워크로드가 대규모로 확장될 것으로 예상된다면, DNS 서비스도 확장할 준비가 되어 있어야 한다. 클러스터의 DNS 서비스 오토스케일링을 확인한다.
  • 추가적인 서비스 어카운트 생성: 사용자 계정은 클러스터에서 사용자가 무엇을 할 수 있는지 결정하는 반면에, 서비스 어카운트는 특정 네임스페이스 내의 파드 접근 권한을 결정한다. 기본적으로, 파드는 자신의 네임스페이스의 기본 서비스 어카운트을 이용한다. 서비스 어카운트 관리하기에서 새로운 서비스 어카운트을 생성하는 방법을 확인한다. 예를 들어, 다음의 작업을 할 수 있다.

다음 내용

2.2.1 - 컨테이너 런타임

파드가 노드에서 실행될 수 있도록 클러스터의 각 노드에 컨테이너 런타임을 설치해야 한다. 이 페이지에서는 관련된 항목을 설명하고 노드 설정 관련 작업을 설명한다.

이 페이지에는 리눅스 환경의 쿠버네티스에서 여러 공통 컨테이너 런타임을 사용하는 방법에 대한 세부 정보가 있다.

cgroup 드라이버

Control group은 프로세스에 할당된 리소스를 제한하는데 사용된다.

리눅스 배포판의 init 시스템이 systemd인 경우, init 프로세스는 root control group(cgroup)을 생성 및 사용하는 cgroup 관리자로 작동한다. Systemd는 cgroup과의 긴밀한 통합을 통해 프로세스당 cgroup을 할당한다. 컨테이너 런타임과 kubelet이 cgroupfs를 사용하도록 설정할 수 있다. systemd와 함께 cgroupfs를 사용하면 두 개의 서로 다른 cgroup 관리자가 존재하게 된다는 뜻이다.

단일 cgroup 관리자는 할당되는 리소스가 무엇인지를 단순화하고, 기본적으로 사용할 수 있는 리소스와 사용 중인 리소스를 일관성있게 볼 수 있다. 시스템에 두 개의 cgroup 관리자가 있으면, 이런 리소스도 두 개의 관점에서 보게 된다. 현장에서 사람들은 kubelet과 도커에 cgroupfs를 사용하고, 나머지 프로세스는 systemd를 사용하도록 노드가 설정된 경우, 리소스가 부족할 때 불안정해지는 사례를 보고했다.

컨테이너 런타임과 kubelet이 systemd를 cgroup 드라이버로 사용하도록 설정을 변경하면 시스템이 안정화된다. 도커에 대해 구성하려면, native.cgroupdriver=systemd를 설정한다.

cgroup v2

cgroup v2는 cgroup Linux API의 다음 버전이다. cgroup v1과는 다르게 각 컨트롤러마다 다른 계층 대신 단일 계층이 있다.

새 버전은 cgroup v1에 비해 몇 가지 향상된 기능을 제공하며, 개선 사항 중 일부는 다음과 같다.

  • API를 더 쉽고 깔끔하게 사용할 수 있음
  • 컨테이너로의 안전한 하위 트리 위임
  • 압력 중지 정보와 같은 새로운 기능

일부 컨트롤러는 cgroup v1에 의해 관리되고 다른 컨트롤러는 cgroup v2에 의해 관리되는 하이브리드 구성을 지원하더라도, 쿠버네티스는 모든 컨트롤러를 관리하기 위해 동일한 cgroup 버전만 지원한다.

systemd가 기본적으로 cgroup v2를 사용하지 않는 경우, 커널 명령줄에 systemd.unified_cgroup_hierarchy=1을 추가하여 cgroup v2를 사용하도록 시스템을 구성할 수 있다.

# dnf install -y grubby && \
  sudo grubby \
  --update-kernel=ALL \
  --args=”systemd.unified_cgroup_hierarchy=1"

구성을 적용하려면 노드를 재부팅해야 한다.

cgroup v2로 전환할 때 사용자가 노드 또는 컨테이너 내에서 cgroup 파일 시스템에 직접 접근하지 않는 한 사용자 경험에 현저한 차이가 없어야 한다.

cgroup v2를 사용하려면 CRI 런타임에서도 cgroup v2를 지원해야 한다.

kubeadm으로 생성한 클러스터의 드라이버를 systemd로 변경하기

kubeadm으로 생성한 클러스터의 cgroup 드라이버를 systemd로 변경하려면 변경 가이드를 참고한다.

컨테이너 런타임

containerd

이 섹션에는 containerd 를 CRI 런타임으로 사용하는 데 필요한 단계가 포함되어 있다.

필수 구성 요소를 설치 및 구성한다.

cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 필요한 sysctl 파라미터를 설정하면 재부팅 후에도 유지된다.
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

# 재부팅하지 않고 sysctl 파라미터 적용
sudo sysctl --system

containerd를 설치한다.

  1. 공식 도커 리포지터리에서 containerd.io 패키지를 설치한다. 각 리눅스 배포판에 대한 도커 리포지터리를 설정하고 containerd.io 패키지를 설치하는 방법은 도커 엔진 설치에서 찾을 수 있다.

  2. containerd 설정

    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    
  3. containerd 재시작

    sudo systemctl restart containerd
    

PowerShell 세션을 시작하고 $Version을 원하는 버전으로 설정(예: $Version:1.4.3)한 후 다음 명령을 실행한다.

  1. containerd 다운로드

    curl.exe -L https://github.com/containerd/containerd/releases/download/v$Version/containerd-$Version-windows-amd64.tar.gz -o containerd-windows-amd64.tar.
    

gz tar.exe xvf .\containerd-windows-amd64.tar.gz


2. 추출과 설정

```powershell
Copy-Item -Path ".\bin\" -Destination "$Env:ProgramFiles\containerd" -Recurse -Force
cd $Env:ProgramFiles\containerd\
.\containerd.exe config default | Out-File config.toml -Encoding ascii

# 설정을 검토한다. 설정에 따라 다음을 조정할 수 있다.
# - sandbox_image (쿠버네티스 일시중지 이미지)
# - cni bin 폴더와 conf 폴더 위치
Get-Content config.toml

# (선택사항 - 그러나 적극 권장함) Windows 디펜더 검사에서 containerd 제외
Add-MpPreference -ExclusionProcess "$Env:ProgramFiles\containerd\containerd.exe"
  1. containerd 실행

    .\containerd.exe --register-service
    Start-Service containerd
    

systemd cgroup 드라이버의 사용

/etc/containerd/config.tomlsystemd cgroup 드라이버를 runc 에서 사용하려면, 다음과 같이 설정한다.

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

이 변경 사항을 적용하는 경우 containerd를 재시작한다.

sudo systemctl restart containerd

kubeadm을 사용하는 경우, kubelet용 cgroup 드라이버를 수동으로 구성한다.

CRI-O

이 섹션은 CRI-O를 컨테이너 런타임으로 설치하는 필수적인 단계를 담고 있다.

시스템에 CRI-O를 설치하기 위해서 다음의 커맨드를 사용한다.

필수 구성 요소를 설치하고 구성한다.

# .conf 파일을 만들어 부팅 시 모듈을 로드한다
cat <<EOF | sudo tee /etc/modules-load.d/crio.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 요구되는 sysctl 파라미터 설정, 이 설정은 재부팅 간에도 유지된다.
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

sudo sysctl --system

다음의 운영 체제에서 CRI-O를 설치하려면, 환경 변수 OS 를 아래의 표에서 적절한 필드로 설정한다.

운영 체제 $OS
Debian Unstable Debian_Unstable
Debian Testing Debian_Testing

그런 다음, `$VERSION` 을 사용자의 쿠버네티스 버전과 일치하는 CRI-O 버전으로 설정한다. 예를 들어, CRI-O 1.20을 설치하려면, `VERSION=1.20` 로 설정한다. 사용자의 설치를 특정 릴리스에 고정할 수 있다. 버전 1.20.0을 설치하려면, `VERSION=1.20:1.20.0` 을 설정한다.

그런 다음, 아래를 실행한다.

cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /
EOF
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /
EOF

curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -

sudo apt-get update
sudo apt-get install cri-o cri-o-runc

다음의 운영 체제에서 CRI-O를 설치하려면, 환경 변수 OS 를 아래의 표에서 적절한 필드로 설정한다.

운영 체제 $OS
Ubuntu 20.04 xUbuntu_20.04
Ubuntu 19.10 xUbuntu_19.10
Ubuntu 19.04 xUbuntu_19.04
Ubuntu 18.04 xUbuntu_18.04

그런 다음, `$VERSION` 을 사용자의 쿠버네티스 버전과 일치하는 CRI-O 버전으로 설정한다. 예를 들어, CRI-O 1.20을 설치하려면, `VERSION=1.20` 로 설정한다. 사용자의 설치를 특정 릴리스에 고정할 수 있다. 버전 1.20.0을 설치하려면, `VERSION=1.20:1.20.0` 을 설정한다.

그런 다음, 아래를 실행한다.

cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /
EOF
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /
EOF

curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers-cri-o.gpg add -

apt-get update
apt-get install cri-o cri-o-runc

다음의 운영 체제에서 CRI-O를 설치하려면, 환경 변수 OS 를 아래의 표에서 적절한 필드로 설정한다.

운영 체제 $OS
Centos 8 CentOS_8
Centos 8 Stream CentOS_8_Stream
Centos 7 CentOS_7

그런 다음, `$VERSION` 을 사용자의 쿠버네티스 버전과 일치하는 CRI-O 버전으로 설정한다. 예를 들어, CRI-O 1.20을 설치하려면, `VERSION=1.20` 로 설정한다. 사용자의 설치를 특정 릴리스에 고정할 수 있다. 버전 1.20.0을 설치하려면, `VERSION=1.20:1.20.0` 을 설정한다.

그런 다음, 아래를 실행한다.

sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/devel:kubic:libcontainers:stable.repo
sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo
sudo yum install cri-o

sudo zypper install cri-o

$VERSION 을 사용자의 쿠버네티스 버전과 일치하는 CRI-O 버전으로 설정한다. 예를 들어, CRI-O 1.20을 설치하려면, VERSION=1.20 로 설정한다.

사용할 수 있는 버전을 찾으려면 다음을 실행한다.

sudo dnf module list cri-o

CRI-O는 Fedora에서 특정 릴리스를 고정하여 설치하는 방법은 지원하지 않는다.

그런 다음, 아래를 실행한다.

sudo dnf module enable cri-o:$VERSION
sudo dnf install cri-o

CRI-O를 시작한다.

sudo systemctl daemon-reload
sudo systemctl enable crio --now

자세한 사항은 CRI-O 설치 가이드를 참고한다.

cgroup 드라이버

CRI-O는 기본적으로 systemd cgroup 드라이버를 사용한다. cgroupfs cgroup 드라이버로 전환하려면, /etc/crio/crio.conf 를 수정하거나 /etc/crio/crio.conf.d/02-cgroup-manager.conf 에 드롭-인(drop-in) 구성을 배치한다. 예를 들면, 다음과 같다.

[crio.runtime]
conmon_cgroup = "pod"
cgroup_manager = "cgroupfs"

또한 cgroupfs 와 함께 CRI-O를 사용할 때 pod 값으로 설정해야 하는 변경된 conmon_cgroup 에 유의한다. 일반적으로 kubelet(일반적으로 kubeadm을 통해 수행됨)과 CRI-O의 cgroup 드라이버 구성을 동기화 상태로 유지해야 한다.

도커

  1. 각 노드에서 도커 엔진 설치에 따라 리눅스 배포판용 도커를 설치한다. 이 의존성 파일에서 검증된 최신 버전의 도커를 찾을 수 있다.

  2. 특히 컨테이너의 cgroup 관리에 systemd를 사용하도록 도커 데몬을 구성한다.

    sudo mkdir /etc/docker
    cat <<EOF | sudo tee /etc/docker/daemon.json
    {
      "exec-opts": ["native.cgroupdriver=systemd"],
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "100m"
      },
      "storage-driver": "overlay2"
    }
    EOF
    
  3. 도커 재시작과 부팅시 실행되게 설정

    sudo systemctl enable docker
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

2.2.2 - 배포 도구로 쿠버네티스 설치하기

2.2.2.1 - kubeadm으로 클러스터 구성하기

2.2.2.1.1 - kubeadm 설치하기

이 페이지에서는 kubeadm 툴박스 설치 방법을 보여준다. 이 설치 프로세스를 수행한 후 kubeadm으로 클러스터를 만드는 방법에 대한 자세한 내용은 kubeadm을 사용하여 클러스터 생성하기 페이지를 참고한다.

시작하기 전에

  • 호환되는 리눅스 머신. 쿠버네티스 프로젝트는 데비안 기반 배포판, 레드햇 기반 배포판, 그리고 패키지 매니저를 사용하지 않는 경우에 대한 일반적인 가이드를 제공한다.
  • 2 GB 이상의 램을 장착한 머신. (이 보다 작으면 사용자의 앱을 위한 공간이 거의 남지 않음)
  • 2 이상의 CPU.
  • 클러스터의 모든 머신에 걸친 전체 네트워크 연결. (공용 또는 사설 네트워크면 괜찮음)
  • 모든 노드에 대해 고유한 호스트 이름, MAC 주소 및 product_uuid. 자세한 내용은 여기를 참고한다.
  • 컴퓨터의 특정 포트들 개방. 자세한 내용은 여기를 참고한다.
  • 스왑의 비활성화. kubelet이 제대로 작동하게 하려면 반드시 스왑을 사용하지 않도록 설정한다.

MAC 주소 및 product_uuid가 모든 노드에 대해 고유한지 확인

  • 사용자는 ip link 또는 ifconfig -a 명령을 사용하여 네트워크 인터페이스의 MAC 주소를 확인할 수 있다.
  • product_uuid는 sudo cat /sys/class/dmi/id/product_uuid 명령을 사용하여 확인할 수 있다.

일부 가상 머신은 동일한 값을 가질 수 있지만 하드웨어 장치는 고유한 주소를 가질 가능성이 높다. 쿠버네티스는 이러한 값을 사용하여 클러스터의 노드를 고유하게 식별한다. 이러한 값이 각 노드에 고유하지 않으면 설치 프로세스가 실패할 수 있다.

네트워크 어댑터 확인

네트워크 어댑터가 두 개 이상이고, 쿠버네티스 컴포넌트가 디폴트 라우트(default route)에서 도달할 수 없는 경우, 쿠버네티스 클러스터 주소가 적절한 어댑터를 통해 이동하도록 IP 경로를 추가하는 것이 좋다.

iptables가 브리지된 트래픽을 보게 하기

br_netfilter 모듈이 로드되었는지 확인한다. lsmod | grep br_netfilter 를 실행하면 된다. 명시적으로 로드하려면 sudo modprobe br_netfilter 를 실행한다.

리눅스 노드의 iptables가 브리지된 트래픽을 올바르게 보기 위한 요구 사항으로, sysctl 구성에서 net.bridge.bridge-nf-call-iptables 가 1로 설정되어 있는지 확인해야 한다. 다음은 예시이다.

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system

자세한 내용은 네트워크 플러그인 요구 사항 페이지를 참고한다.

필수 포트 확인

필수 포트들은 쿠버네티스 컴포넌트들이 서로 통신하기 위해서 열려 있어야 한다. 다음과 같이 telnet 명령을 이용하여 포트가 열려 있는지 확인해 볼 수 있다.

telnet 127.0.0.1 6443

사용자가 사용하는 파드 네트워크 플러그인(아래 참조)은 특정 포트를 열어야 할 수도 있다. 이것은 각 파드 네트워크 플러그인마다 다르므로, 필요한 포트에 대한 플러그인 문서를 참고한다.

런타임 설치

파드에서 컨테이너를 실행하기 위해, 쿠버네티스는 컨테이너 런타임을 사용한다.

기본적으로, 쿠버네티스는 컨테이너 런타임 인터페이스(CRI)를 사용하여 사용자가 선택한 컨테이너 런타임과 인터페이스한다.

런타임을 지정하지 않으면, kubeadm은 잘 알려진 유닉스 도메인 소켓 목록을 검색하여 설치된 컨테이너 런타임을 자동으로 감지하려고 한다. 다음 표에는 컨테이너 런타임 및 관련 소켓 경로가 나열되어 있다.

컨테이너 런타임과 소켓 경로
런타임 유닉스 도메인 소켓 경로
도커 /var/run/dockershim.sock
containerd /run/containerd/containerd.sock
CRI-O /var/run/crio/crio.sock

도커와 containerd가 모두 감지되면 도커가 우선시된다. 이것이 필요한 이유는 도커 18.09에서 도커만 설치한 경우에도 containerd와 함께 제공되므로 둘 다 감지될 수 있기 때문이다. 다른 두 개 이상의 런타임이 감지되면, kubeadm은 오류와 함께 종료된다.

kubelet은 빌트인 dockershim CRI 구현을 통해 도커와 통합된다.

자세한 내용은 컨테이너 런타임을 참고한다.

기본적으로, kubeadm은 컨테이너 런타임으로 도커(Docker)를 사용한다. kubelet은 빌트인 dockershim CRI 구현을 통해 도커와 통합된다.

자세한 내용은 컨테이너 런타임을 참고한다.

kubeadm, kubelet 및 kubectl 설치

모든 머신에 다음 패키지들을 설치한다.

  • kubeadm: 클러스터를 부트스트랩하는 명령이다.

  • kubelet: 클러스터의 모든 머신에서 실행되는 파드와 컨테이너 시작과 같은 작업을 수행하는 컴포넌트이다.

  • kubectl: 클러스터와 통신하기 위한 커맨드 라인 유틸리티이다.

kubeadm은 kubelet 또는 kubectl 을 설치하거나 관리하지 않으므로, kubeadm이 설치하려는 쿠버네티스 컨트롤 플레인의 버전과 일치하는지 확인해야 한다. 그렇지 않으면, 예상치 못한 버그 동작으로 이어질 수 있는 버전 차이(skew)가 발생할 위험이 있다. 그러나, kubelet과 컨트롤 플레인 사이에 하나의 마이너 버전 차이가 지원되지만, kubelet 버전은 API 서버 버전 보다 높을 수 없다. 예를 들어, 1.7.0 버전의 kubelet은 1.8.0 API 서버와 완전히 호환되어야 하지만, 그 반대의 경우는 아니다.

kubectl 설치에 대한 정보는 kubectl 설치 및 설정을 참고한다.

버전 차이에 대한 자세한 내용은 다음을 참고한다.

  1. apt 패키지 색인을 업데이트하고, 쿠버네티스 apt 리포지터리를 사용하는 데 필요한 패키지를 설치한다.

    sudo apt-get update
    sudo apt-get install -y apt-transport-https ca-certificates curl
    
  2. 구글 클라우드의 공개 사이닝 키를 다운로드 한다.

    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    
  3. 쿠버네티스 apt 리포지터리를 추가한다.

    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  4. apt 패키지 색인을 업데이트하고, kubelet, kubeadm, kubectl을 설치하고 해당 버전을 고정한다.

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl
    

cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# permissive 모드로 SELinux 설정(효과적으로 비활성화)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

sudo systemctl enable --now kubelet

참고:

  • setenforce 0sed ... 를 실행하여 permissive 모드로 SELinux를 설정하면 효과적으로 비활성화된다. 컨테이너가 호스트 파일시스템(예를 들어, 파드 네트워크에 필요한)에 접근하도록 허용하는 데 필요하다. kubelet에서 SELinux 지원이 개선될 때까지 이 작업을 수행해야 한다.

  • 구성 방법을 알고 있는 경우 SELinux를 활성화된 상태로 둘 수 있지만 kubeadm에서 지원하지 않는 설정이 필요할 수 있다.

CNI 플러그인 설치(대부분의 파드 네트워크에 필요)

CNI_VERSION="v0.8.2"
ARCH="amd64"
sudo mkdir -p /opt/cni/bin
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz" | sudo tar -C /opt/cni/bin -xz

명령어 파일을 다운로드할 디렉터리 정의

DOWNLOAD_DIR=/usr/local/bin
sudo mkdir -p $DOWNLOAD_DIR

crictl 설치(kubeadm / Kubelet 컨테이너 런타임 인터페이스(CRI)에 필요)

CRICTL_VERSION="v1.17.0"
ARCH="amd64"
curl -L "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz" | sudo tar -C $DOWNLOAD_DIR -xz

kubeadm, kubelet, kubectl 설치 및 kubelet systemd 서비스 추가

RELEASE="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
ARCH="amd64"
cd $DOWNLOAD_DIR
sudo curl -L --remote-name-all https://storage.googleapis.com/kubernetes-release/release/${RELEASE}/bin/linux/${ARCH}/{kubeadm,kubelet,kubectl}
sudo chmod +x {kubeadm,kubelet,kubectl}

RELEASE_VERSION="v0.4.0"
curl -sSL "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubelet/lib/systemd/system/kubelet.service" | sed "s:/usr/bin:${DOWNLOAD_DIR}:g" | sudo tee /etc/systemd/system/kubelet.service
sudo mkdir -p /etc/systemd/system/kubelet.service.d
curl -sSL "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubeadm/10-kubeadm.conf" | sed "s:/usr/bin:${DOWNLOAD_DIR}:g" | sudo tee /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

kubelet 활성화 및 시작

systemctl enable --now kubelet

kubelet은 이제 kubeadm이 수행할 작업을 알려 줄 때까지 크래시루프(crashloop) 상태로 기다려야 하므로 몇 초마다 다시 시작된다.

cgroup 드라이버 구성

컨테이너 런타임과 kubelet은 "cgroup 드라이버"라는 속성을 갖고 있으며, cgroup 드라이버는 리눅스 머신의 cgroup 관리 측면에 있어서 중요하다.

문제 해결

kubeadm에 문제가 있는 경우, 문제 해결 문서를 참고한다.

다음 내용

2.2.2.1.2 - kubeadm API로 컴포넌트 사용자 정의하기

이 페이지는 kubeadm이 배포하는 컴포넌트(component)들을 사용자 정의하는 방법을 다룬다. 컨트롤 플레인 컴포넌트에 대해서는 Cluster Configuration 구조에서 플래그를 사용하거나 노드당 패치를 사용할 수 있다. kubelet과 kube-proxy의 경우, KubeletConfigurationKubeProxyConfiguration을 각각 사용할 수 있다.

이 모든 옵션이 kubeadm 구성 API를 통해 가용하다. 구성의 각 필드 상세 사항은 API 참조 페이지에서 찾아볼 수 있다.

FEATURE STATE: Kubernetes v1.12 [stable]

ClusterConfiguration의 플래그로 컨트롤 플레인 사용자 정의하기

kubeadm의 ClusterConfiguration 오브젝트는 API 서버, 컨트롤러매니저, 스케줄러, Etcd와 같은 컨트롤 플레인 컴포넌트에 전달되는 기본 플래그를 사용자가 덮어쓸 수 있도록 노출한다. 이 컴포넌트는 다음 구조체를 사용하여 정의된다.

  • apiServer
  • controllerManager
  • scheduler
  • etcd

이 구조체들은 공통 필드인 extraArgs를 포함하며, 이 필드는 키: 값 쌍으로 구성된다. 컨트롤 플레인 컴포넌트를 위한 플래그를 덮어쓰려면 다음을 수행한다.

  1. 사용자 구성에 적절한 extraArgs 필드를 추가한다.
  2. extraArgs 필드에 플래그를 추가한다.
  3. kubeadm init--config <CONFIG YAML 파일> 파라미터를 추가해서 실행한다.

APIServer 플래그

자세한 내용은 kube-apiserver 레퍼런스 문서를 확인한다.

사용 예시:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
apiServer:
  extraArgs:
    anonymous-auth: "false"
    enable-admission-plugins: AlwaysPullImages,DefaultStorageClass
    audit-log-path: /home/johndoe/audit.log

컨트롤러매니저 플래그

자세한 내용은 kube-controller-manager 레퍼런스 문서를 확인한다.

사용 예시:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
controllerManager:
  extraArgs:
    cluster-signing-key-file: /home/johndoe/keys/ca.key
    deployment-controller-sync-period: "50"

스케줄러 플래그

자세한 내용은 kube-scheduler 레퍼런스 문서를 확인한다.

사용 예시:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
scheduler:
  extraArgs:
    config: /etc/kubernetes/scheduler-config.yaml
  extraVolumes:
    - name: schedulerconfig
      hostPath: /home/johndoe/schedconfig.yaml
      mountPath: /etc/kubernetes/scheduler-config.yaml
      readOnly: true
      pathType: "File"

Etcd 플래그

자세한 사항은 etcd 서버 문서를 확인한다.

사용 예시:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
  local:
    extraArgs:
      election-timeout: 1000

패치를 통해 컨트롤 플레인 사용자 정의하기

FEATURE STATE: Kubernetes v1.22 [beta]

Kubeadm을 사용하면 패치 파일이 있는 디렉토리를 개별 노드에 대한 InitConfigurationJoinConfiguration에 전달할 수 있다. 이 패치는 컨트롤 플레인 컴포넌트 메니패스트가 디스크에 기록되기 전에 최종 사용자 정의 단계로 사용될 수 있다.

--config <YOUR CONFIG YAML>을 사용하여 이 파일을 kubeadm init에 전달할 수 있다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
  patches:
    directory: /home/user/somedir

--config <YOUR CONFIG YAML>을 사용하여 이 파일을 kubeadm join에 전달할 수 있다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
nodeRegistration:
  patches:
    directory: /home/user/somedir

디렉토리는 target[suffix][+patchtype].extension 형태의 파일을 포함해야 한다. 예를 들면, kube-apiserver0+merge.yaml 또는 단순히 etcd.json의 형태이다.

  • targetkube-apiserver, kube-controller-manager, kube-scheduler 그리고 etcd 중 하나가 될 수 있다.
  • patchtypestrategic, merge 그리고 json 중 하나가 될 수 있으며 kubectl에서 지원하는 패치 형식을 준수해야 한다. patchtype의 기본값은 strategic이다.
  • extensionjson 또는 yaml 중 하나여야 한다.
  • suffix는 어떤 패치가 먼저 적용되는지를 결정하는 데 사용할 수 있는 영숫자 형태의 선택적 문자열이다.

kubelet 사용자 정의하기

kubelet을 사용자 정의하려면, KubeletConfiguration을 동일한 구성 파일 내에서 ---로 구분된 ClusterConfiguration이나 InitConfiguration 다음에 추가하면 된다. 그런 다음 kubeadm init에 해당 파일을 전달한다.

자세한 사항은 kubeadm을 통해 클러스터의 각 kubelet 구성하기에서 살펴본다.

kube-proxy 사용자 정의하기

kube-proxy를 사용자 정의하려면, KubeProxyConfiguration---로 구분된 ClusterConfiguration이나 InitConfiguration 다음에 두고 kubeadm init에 전달하면 된다.

자세한 사항은 API 참조 페이지에서 살펴볼 수 있다.

2.2.2.1.3 - 고가용성 토폴로지 선택

이 페이지는 고가용성(HA) 쿠버네티스 클러스터의 토플로지를 구성하는 두 가지 선택 사항을 설명한다.

다음과 같이 HA 클러스터를 구성할 수 있다.

  • etcd 노드와 컨트롤 플레인 노드를 함께 위치시키는 중첩된(stacked) 컨트롤 플레인 노드 방식
  • etcd와 컨트롤 플레인이 분리된 노드에서 운영되는 외부 etcd 노드 방식

HA 클러스터를 구성하기 전에 각 토플로지의 장단점을 주의 깊게 고려해야 한다.

중첩된 etcd 토플로지

중첩된 HA 클러스터는 etcd에서 제공하는 분산 데이터 저장소 클러스터를, 컨트롤 플레인 구성 요소를 실행하는 kubeadm으로 관리되는 노드에 의해서 형성된 클러스터 상단에 중첩하는 토플로지이다.

각 컨트롤 플레인 노드는 kube-apiserver, kube-scheduler, kube-controller-manager 인스턴스를 운영한다. kube-apiserver는 로드 밸런서를 이용하여 워커 노드에 노출되어 있다.

각 컨트롤 플레인 노드는 지역 etcd 맴버를 생성하고 이 etcd 맴버는 오직 해당 노드의 kube-apiserver와 통신한다. 비슷한 방식이 지역의 kube-controller-managerkube-scheduler에도 적용된다.

이 토플로지는 컨트롤 플레인과 etcd 맴버가 같은 노드에 묶여 있다. 이는 외부 etcd 노드의 클러스터를 구성하는 것보다는 단순하며 복제 관리도 간단하다.

그러나 중첩된 클러스터는 커플링에 실패할 위험이 있다. 한 노드가 다운되면 etcd 맴버와 컨트롤 플레인을 모두 잃어버리고, 중복성도 손상된다. 더 많은 컨트롤 플레인 노드를 추가하여 이 위험을 완화할 수 있다.

그러므로 HA 클러스터를 위해 최소 3개인 중첩된 컨트롤 플레인 노드를 운영해야 한다.

이는 kubeadm의 기본 토플로지이다. 지역 etcd 맴버는 kubeadm initkubeadm join --control-plane 을 이용할 때에 컨트롤 플레인 노드에 자동으로 생성된다.

중첩된 etcd 토플로지

외부 etcd 토플로지

외부 etcd를 이용하는 HA 클러스터는 etcd로 제공한 분산된 데이터 스토리지 클러스터가 컨트롤 플레인 구성 요소를 운영하는 노드로 형성하는 클러스터의 외부에 있는 토플로지이다.

중첩된 etcd 토플로지와 유사하게, 외부 etcd 토플로지에 각 컨트롤 플레인 노드는 kube-apiserver, kube-scheduler, kube-controller-manager의 인스턴스를 운영한다. 그리고 kube-apiserver는 로드 밸런서를 이용하여 워커노드에 노출한다. 그러나 etcd 맴버는 분리된 호스트에서 운영되고, 각 etcd 호스트는 각 컨트롤 플레인 노드의 kube-apiserver와 통신한다.

이 토플로지는 컨트롤 플레인과 etcd 맴버를 분리한다. 이는 그러므로 컨트롤 플레인 인스턴스나 etcd 맴버를 잃는 충격이 덜하고, 클러스터 중복성에 있어 중첩된 HA 토플로지만큼 영향을 미치지 않는다.

그러나, 이 토플로지는 중첩된 토플로지에 비해 호스트 개수가 두배나 필요하다. 이 토플로지로 HA 클러스터를 구성하기 위해서는 최소한 3개의 컨트롤 플레인과 3개의 etcd 노드가 필요하다.

외부 etcd 토플로지

다음 내용

2.2.2.2 - Kops로 쿠버네티스 설치하기

이곳 빠른 시작에서는 사용자가 얼마나 쉽게 AWS에 쿠버네티스 클러스터를 설치할 수 있는지 보여준다. kops라는 이름의 툴을 이용할 것이다.

kops는 자동화된 프로비저닝 시스템인데,

  • 완전 자동화된 설치
  • DNS를 통해 클러스터들의 신원 확인
  • 자체 복구: 모든 자원이 Auto-Scaling Groups에서 실행
  • 다양한 OS 지원(Debian, Ubuntu 16.04 supported, CentOS & RHEL, Amazon Linux and CoreOS) - images.md 보기
  • 고가용성 지원 - high_availability.md 보기
  • 직접 프로비저닝 하거나 또는 할 수 있도록 terraform 매니페스트를 생성 - terraform.md 보기

시작하기 전에

  • kubectl을 반드시 설치해야 한다.

  • 반드시 64-bit (AMD64 그리고 Intel 64)디바이스 아키텍쳐 위에서 kops설치 한다.

  • AWS 계정이 있고 IAM 키를 생성하고 구성해야 한다. IAM 사용자는 적절한 권한이 필요하다.

클러스터 구축

(1/5) kops 설치

설치

releases page에서 kops를 다운로드한다(소스 코드로부터 빌드하는 것도 역시 편리하다).

최신 버전의 릴리스를 다운받는 명령어:

curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest
| grep tag_name | cut -d '"' -f 4)/kops-darwin-amd64

특정 버전을 다운로드 받는다면 명령의 다음 부분을 특정 kops 버전으로 변경한다.

$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)

예를 들어 kops 버전을 v1.20.0을 다운로드 하려면 다음을 입력한다.

curl -LO https://github.com/kubernetes/kops/releases/download/v1.20.0/kops-darwin-amd64

kops 바이너리를 실행 가능하게 만든다.

chmod +x kops-darwin-amd64

kops 바이너리를 사용자의 PATH로 이동한다.

sudo mv kops-darwin-amd64 /usr/local/bin/kops

사용자는 Homebrew를 이용해서 kops를 설치할 수 있다.

brew update && brew install kops

최신 릴리스를 다운로드 받는 명령어:

curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64

특정 버전의 kops를 다운로드하려면 명령의 다음 부분을 특정 kops 버전으로 변경한다.

$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)

예를 들어 kops 버전을 v1.20.0을 다운로드 하려면 다음을 입력한다.

curl -LO https://github.com/kubernetes/kops/releases/download/v1.20.0/kops-linux-amd64

kops 바이너리를 실행 가능하게 만든다.

chmod +x kops-linux-amd64

kops 바이너리를 사용자의 PATH로 이동한다.

sudo mv kops-linux-amd64 /usr/local/bin/kops

사용자는 Homebrew를 이용해서 kops를 설치할 수 있다.

brew update && brew install kops

(2/5) 클러스터에 사용할 route53 domain 생성

kops는 클러스터 내부와 외부 모두에서 검색을 위해 DNS을 사용하기에 클라이언트에서 쿠버네티스 API 서버에 연결할 수 있다.

이런 클러스터 이름에 kops는 명확한 견해을 가지는데: 반드시 유효한 DNS 이름이어야 한다. 이렇게 함으로써 사용자는 클러스터를 헷갈리지 않을것이고, 동료들과 혼선없이 공유할 수 있으며, IP를 기억할 필요없이 접근할 수 있다.

그렇게 하고 있겠지만, 클러스터를 구분하기 위해 서브도메인을 활용할 수 있다. 예를 들어 useast1.dev.example.com을 이용한다면, API 서버 엔드포인트는 api.useast1.dev.example.com가 될 것이다.

Route53 hosted zone은 서브도메인도 지원한다. 여러분의 hosted zone은 useast1.dev.example.com, dev.example.com 그리고 example.com 같은 것도 될 수 있다. kops는 이것들 모두와 잘 동작하며, 사용자는 보통 조직적인 부분을 고려해 결정한다(예를 들어, 사용자가 dev.example.com하위에 레코드를 생성하는것은 허용되지만, example.com하위에는 그렇지 않을 수 있다).

dev.example.com을 hosted zone으로 사용하고 있다고 가정해보자. 보통 사용자는 일반적인 방법 에 따라 생성하거나 aws route53 create-hosted-zone --name dev.example.com --caller-reference 1 와 같은 커맨드를 이용한다.

그 후 도메인 내 레코드들을 확인할 수 있도록 상위 도메인내에 NS 레코드를 생성해야 한다. 여기서는, dev NS 레코드를 example.com에 생성한다. 만약 이것이 루트 도메인 네임이라면 이 NS 레코드들은 도메인 등록기관을 통해서 생성해야 한다(예를 들어, example.comexample.com를 구매한 곳에서 설정 할 수 있다).

route53 도메인 설정을 확인한다(문제를 만드는 가장 큰 이유이다!). dig 툴을 실행해서 클러스터 설정이 정확한지 한번 더 확인한다.

dig NS dev.example.com

당신의 hosted zone용으로 할당된 3~4개의 NS 레코드를 Route53에서 확인할 수 있어야 한다.

(3/5) 클러스터 상태 저장용 S3 버킷 생성

kops는 설치 이후에도 클러스터를 관리할 수 있다. 이를 위해 사용자가 생성한 클러스터의 상태나 사용하는 키 정보들을 지속적으로 추적해야 한다. 이 정보가 S3에 저장된다. 이 버킷의 접근은 S3 권한으로 제어한다.

다수의 클러스터는 동일한 S3 버킷을 이용할 수 있고, 사용자는 이 S3 버킷을 같은 클러스트를 운영하는 동료에게 공유할 수 있다. 하지만 이 S3 버킷에 접근 가능한 사람은 사용자의 모든 클러스터에 관리자 접근이 가능하게 되니, 운영팀 이외로 공유되지 않도록 해야 한다.

그래서 보통 한 운영팀 당 하나의 S3 버킷을 가지도록 하기도 한다.(그리고 종종 운영팀 이름은 위에서 언급한 hosted zone과 동일하게 짓기도 한다!)

우리 예제에서는, dev.example.com를 hosted zone으로 했으니 clusters.dev.example.com를 S3 버킷 이름으로 정하자.

  • AWS_PROFILE를 선언한다. (AWS CLI 동작을 위해 다른 profile을 선택해야 할 경우)

  • aws s3 mb s3://clusters.dev.example.com를 이용해 S3 버킷을 생성한다.

  • export KOPS_STATE_STORE=s3://clusters.dev.example.com 하면, kops는 이 위치를 기본값으로 인식할 것이다. 이 부분을 bash profile등에 넣어두는것을 권장한다.

(4/5) 클러스터 설정 구성

클러스터 설정하려면, kops create cluster 를 실행한다:

kops create cluster --zones=us-east-1c useast1.dev.example.com

kops는 클러스터에 사용될 설정을 생성할것이다. 여기서 주의할 점은 실제 클러스트 리소스가 아닌 설정 만을 생성한다는 것에 주의하자 - 이 부분은 다음 단계에서 kops update cluster 으로 구성해볼 것이다. 그 때 만들어진 설정을 점검하거나 변경할 수 있다.

더 자세한 내용을 알아보기 위한 커맨드가 출력된다.

  • 클러스터 조회: kops get cluster
  • 클러스트 수정: kops edit cluster useast1.dev.example.com
  • 인스턴스 그룹 수정: kops edit ig --name=useast1.dev.example.com nodes
  • 마스터 인스턴스 그룹 수정: kops edit ig --name=useast1.dev.example.com master-us-east-1c

만약 kops사용이 처음이라면, 얼마 걸리지 않으니 이들을 시험해 본다. 인스턴스 그룹은 쿠버네티스 노드로 등록된 인스턴스의 집합을 말한다. AWS상에서는 auto-scaling-groups를 통해 만들어진다. 사용자는 여러 개의 인스턴스 그룹을 관리할 수 있는데, 예를 들어, spot과 on-demand 인스턴스 조합 또는 GPU 와 non-GPU 인스턴스의 조합으로 구성할 수 있다.

(5/5) AWS에 클러스터 생성

kops update cluster를 실행해 AWS에 클러스터를 생성한다.

kops update cluster useast1.dev.example.com --yes

실행은 수 초 만에 되지만, 실제로 클러스터가 준비되기 전까지 수 분이 걸릴 수 있다. 언제든 kops update cluster로 클러스터 설정을 변경할 수 있다. 사용자가 변경한 클러스터 설정을 그대로 반영해 줄 것이며, 필요다하면 AWS 나 쿠버네티스를 재설정 해 줄것이다.

예를 들면, kops edit ig nodes 뒤에 kops update cluster --yes를 실행해 설정을 반영한다. 그리고 kops rolling-update cluster로 설정을 즉시 원복시킬 수 있다.

--yes를 명시하지 않으면 kops update cluster 커맨드 후 어떤 설정이 변경될지가 표시된다. 운영계 클러스터 관리할 때 사용하기 좋다!

다른 애드온 탐험

애드온 리스트 에서 쿠버네티스 클러스터용 로깅, 모니터링, 네트워크 정책, 시각화 & 제어 등을 포함한 다른 애드온을 확인해본다.

정리하기

  • kops delete cluster useast1.dev.example.com --yes 로 클러스터를 삭제한다.

다음 내용

  • 쿠버네티스 개념kubectl에 대해 더 알아보기.
  • 튜토리얼, 모범사례 및 고급 구성 옵션에 대한 kops 고급 사용법에 대해 더 자세히 알아본다.
  • 슬랙(Slack)에서 kops 커뮤니티 토론을 할 수 있다: 커뮤니티 토론
  • 문제를 해결하거나 이슈를 제기하여 kops 에 기여한다. 깃헙 이슈

2.2.2.3 - Kubespray로 쿠버네티스 설치하기

이 가이드는 Kubespray를 이용하여 GCE, Azure, OpenStack, AWS, vSphere, Packet(베어메탈), Oracle Cloud infrastructure(실험적) 또는 베어메탈 등에서 운영되는 쿠버네티스 클러스터를 설치하는 과정을 보여준다.

Kubespray는 Ansible 플레이북, 인벤토리, 프로비저닝 도구와 일반적인 운영체제, 쿠버네티스 클러스터의 설정 관리 작업에 대한 도메인 지식의 결합으로 만들어졌다. Kubespray는 아래와 같은 기능을 제공한다.

  • 고가용성을 지닌 클러스터
  • 구성할 수 있는 속성들
  • 대부분의 인기있는 리눅스 배포판들에 대한 지원
    • Ubuntu 16.04, 18.04, 20.04
    • CentOS/RHEL/Oracle Linux 7, 8
    • Debian Buster, Jessie, Stretch, Wheezy
    • Fedora 31, 32
    • Fedora CoreOS
    • openSUSE Leap 15
    • Flatcar Container Linux by Kinvolk
  • 지속적인 통합 (CI) 테스트

클러스터를 설치해 줄 도구로 유스케이스와 가장 잘 맞는 것을 고르고 싶다면, kubespray를 kubeadm, kops비교한 글을 읽어보자.

클러스터 생성하기

(1/5) 아래의 요건 충족하기

언더레이(underlay) 요건을 만족하는 프로비전 한다.

  • Ansible의 명령어를 실행하기 위해 Ansible v 2.9와 Python netaddr 라이브러리가 머신에 설치되어 있어야 한다
  • Ansible 플레이북을 실행하기 위해 2.11 (혹은 그 이상) 버전의 Jinja가 필요하다
  • 타겟 서버들은 docker 이미지를 풀(pull) 하기 위해 반드시 인터넷에 접속할 수 있어야 한다. 아니라면, 추가적인 설정을 해야 한다 (오프라인 환경 확인하기)
  • 타겟 서버들의 IPv4 포워딩이 활성화되어야 한다
  • SSH 키가 인벤토리의 모든 서버들에 복사되어야 한다
  • 방화벽은 관리되지 않는다. 사용자가 예전 방식대로 고유한 규칙을 구현해야 한다. 디플로이먼트 과정에서의 문제를 방지하려면 방화벽을 비활성화해야 한다
  • 만약 kubespray가 루트가 아닌 사용자 계정에서 실행되었다면, 타겟 서버에서 알맞은 권한 확대 방법이 설정되어야 한다. 그 뒤 ansible_become 플래그나 커맨드 파라미터들, --become 또는 -b 가 명시되어야 한다

Kubespray는 환경에 맞는 프로비저닝을 돕기 위해 아래와 같은 서비스를 제공한다:

(2/5) 인벤토리 파일 구성하기

서버들을 프로비저닝 한 후, Ansible의 인벤토리 파일을 만들어야 한다. 수동으로 만들 수도 있고, 동적인 인벤토리 스크립트를 통해 만들 수도 있다. 더 많이 알고싶다면 " 나만의 인벤토리 만들기" 글을 확인하자.

(3/5) 클러스터 디플로이먼트 계획하기

Kubespray에서는 디플로이먼트의 많은 속성들을 사용자가 정의(customize)할 수 있다:

  • 디플로이먼트 모드의 선택: kubeadm 또는 그 외
  • CNI(네트워킹) 플러그인
  • DNS 설정
  • 컨트롤 플레인 선택: 네이티브/바이너리 또는 컨테이너화 된 것
  • 컴포넌트 버전
  • Calico 라우터 리플렉터
  • 컴포넌트 런타임 옵션
  • 인증서 생성 방법

Kubespray의 변수 파일들을 사용자가 정의할 수 있다. 만약 Kubespray를 처음 접하는 경우, kubespray의 기본 설정값을 이용해 클러스터를 배포하고 Kubernetes를 탐색하는 것이 좋다.

(4/5) 클러스터 배포하기

다음으로, 클러스터를 배포한다.

Ansible-플레이북을 이용한 클러스터 디플로이먼트

ansible-playbook -i your/inventory/inventory.ini cluster.yml -b -v \
  --private-key=~/.ssh/private_key

규모가 큰 디플로이먼트는 (100개 이상의 노드) 최적의 결과를 얻기 위해 특정한 조정을 필요로 할 수도 있다.

(5/5) 디플로이먼트 검증하기

Kubespray는 Netchecker를 사용하여 파드 사이의 연결성과 DNS 해석을 검증할 방법을 제공한다. Netchecker는 netchecker-agents 파드들이 DNS 요청을 해석하고 기본(default) 네임스페이스 내부에서 서로에게 ping을 보낼 수 있도록 보장한다. 그 파드들은 나머지 워크로드의 유사한 동작을 모방하고 클러스터의 상태 표시기 역할을 한다.

클러스터 동작

Kubespray는 클러스터를 관리하기 위한 추가적인 플레이북, scaleupgrade 를 제공한다.

클러스터 스케일링하기

scale 플레이북을 실행해 클러스터에 워커 노드를 추가할 수 있다. 더 자세히 알고 싶다면, "노드 추가하기" 문서를 확인하자. remove-node 플레이북을 실행하면 클러스터로부터 워커 노드를 제거할 수 있다. 더 알고 싶다면 "노드 제거하기" 문서를 확인하자.

클러스터 업그레이드 하기

upgrade-cluster 플레이북을 실행해 클러스터를 업그레이드 할 수 있다. 더 자세히 알고 싶다면 "업그레이드" 문서를 확인하자.

클린업

reset 플레이북을 이용하여 노드들을 리셋하고 Kubespray로 설치된 모든 구성요소를 삭제할 수 있다.

피드백

다음 내용

Kubespray의 로드맵에서 계획중인 작업을 확인해보자.

2.2.3 - 턴키 클라우드 솔루션

이 페이지는 인증된 쿠버네티스 솔루션 제공자 목록을 제공한다. 각 제공자 페이지를 통해서, 프로덕션에 준비된 클러스터를 설치 및 설정하는 방법을 학습할 수 있다.

2.2.4 - 쿠버네티스에서 윈도우

2.2.4.1 - 쿠버네티스에서 윈도우 컨테이너

윈도우 애플리케이션은 많은 조직에서 실행되는 서비스 및 애플리케이션의 상당 부분을 구성한다. 윈도우 컨테이너는 프로세스와 패키지 종속성을 캡슐화하는 현대적인 방법을 제공하여, 데브옵스(DevOps) 사례를 더욱 쉽게 사용하고 윈도우 애플리케이션의 클라우드 네이티브 패턴을 따르도록 한다. 쿠버네티스는 사실상의 표준 컨테이너 오케스트레이터가 되었으며, 쿠버네티스 1.14 릴리스에는 쿠버네티스 클러스터의 윈도우 노드에서 윈도우 컨테이너 스케줄링을 위한 프로덕션 지원이 포함되어 있어, 광범위한 윈도우 애플리케이션 생태계가 쿠버네티스의 강력한 기능을 활용할 수 있다. 윈도우 기반 애플리케이션과 리눅스 기반 애플리케이션에 투자한 조직은 워크로드를 관리하기 위해 별도의 오케스트레이터를 찾을 필요가 없으므로, 운영 체제와 관계없이 배포 전반에 걸쳐 운영 효율성이 향상된다.

쿠버네티스의 윈도우 컨테이너

쿠버네티스에서 윈도우 컨테이너 오케스트레이션을 활성화하려면, 기존 리눅스 클러스터에 윈도우 노드를 포함한다. 쿠버네티스의 파드에서 윈도우 컨테이너를 스케줄링하는 것은 리눅스 기반 컨테이너를 스케줄링하는 것과 유사하다.

윈도우 컨테이너를 실행하려면, 쿠버네티스 클러스터에 리눅스를 실행하는 컨트롤 플레인 노드와 사용자의 워크로드 요구에 따라 윈도우 또는 리눅스를 실행하는 워커가 있는 여러 운영 체제가 포함되어 있어야 한다. 윈도우 서버 2019는 윈도우에서 쿠버네티스 노드를 활성화하는 유일한 윈도우 운영 체제이다(kubelet, 컨테이너 런타임 및 kube-proxy 포함). 윈도우 배포 채널에 대한 자세한 설명은 Microsoft 문서를 참고한다.

마스터 컴포넌트를 포함한 쿠버네티스 컨트롤 플레인은 리눅스에서 계속 실행된다. 윈도우 전용 쿠버네티스 클러스터는 계획이 없다.

이 문서에서 윈도우 컨테이너에 대해 이야기할 때 프로세스 격리된 윈도우 컨테이너를 의미한다. Hyper-V 격리가 있는 윈도우 컨테이너는 향후 릴리스로 계획되어 있다.

지원되는 기능 및 제한

지원되는 기능

윈도우 OS 버전 지원

쿠버네티스의 윈도우 운영 체제 지원은 다음 표를 참조한다. 단일 이기종 쿠버네티스 클러스터에는 윈도우 및 리눅스 워커 노드가 모두 있을 수 있다. 윈도우 컨테이너는 윈도우 노드에서, 리눅스 컨테이너는 리눅스 노드에서 스케줄되어야 한다.

쿠버네티스 버전 윈도우 서버 LTSC 릴리스 윈도우 서버 SAC 릴리스
Kubernetes v1.20 Windows Server 2019 Windows Server ver 1909, Windows Server ver 2004
Kubernetes v1.21 Windows Server 2019 Windows Server ver 2004, Windows Server ver 20H2
Kubernetes v1.22 Windows Server 2019 Windows Server ver 2004, Windows Server ver 20H2

지원 모델을 포함한 다양한 윈도우 서버 서비스 채널에 대한 정보는 윈도우 서버 서비스 채널에서 확인할 수 있다.

모든 윈도우 고객이 앱의 운영 체제를 자주 업데이트하는 것은 아니다. 애플리케이션 업그레이드를 위해서는 클러스터에 새 노드를 업그레이드하거나 도입하는 것이 필요하다. 이 문서에서 쿠버네티스에서 실행되는 컨테이너의 운영 체제를 업그레이드하기로 선택한 고객을 위해 새 운영 체제 버전에 대한 지원을 추가할 때의 가이드와 단계별 지침을 제공한다. 이 가이드에는 클러스터 노드와 함께 사용자 애플리케이션을 업그레이드하기 위한 권장 업그레이드 절차가 포함된다. 윈도우 노드는 현재 리눅스 노드와 동일한 방식으로 쿠버네티스 버전-차이(skew) 정책(노드 대 컨트롤 플레인 버전 관리)을 준수한다.

윈도우 서버 호스트 운영 체제에는 윈도우 서버 라이선스가 적용된다. 윈도우 컨테이너 이미지에는 윈도우 컨테이너에 대한 추가 사용 조건이 적용된다.

프로세스 격리가 포함된 윈도우 컨테이너에는 엄격한 호환성 규칙이 있으며, 여기서 호스트 OS 버전은 컨테이너 베이스 이미지 OS 버전과 일치해야 한다. 일단 쿠버네티스에서 Hyper-V 격리가 포함된 윈도우 컨테이너를 지원하면, 제한 및 호환성 규칙이 변경될 것이다.

퍼즈(Pause) 이미지

쿠버네티스는 윈도우 지원을 포함하는 다중 아키텍처 이미지를 유지보수한다. 쿠버네티스 v1.22의 경우 권장 퍼즈 이미지는 k8s.gcr.io/pause:3.5이다. 소스 코드는 GitHub에서 찾을 수 있다.

Microsoft는 리눅스, 윈도우 amd64를 지원하는 다중 아키텍처 이미지를 mcr.microsoft.com/oss/kubernetes/pause:3.5에서 유지보수하고 있다. 이 이미지는 쿠버네티스가 유지 관리하는 이미지와 동일한 소스코드에서 생성되었지만, 모든 윈도우 바이너리는 Microsoft에 의해 서명된 인증 코드이다. 프로덕션 환경에서 서명된 바이너리가 필요한 경우, Microsoft가 유지 관리하는 이미지를 사용하는 것을 권장한다.

컴퓨트

API 및 kubectl의 관점에서, 윈도우 컨테이너는 리눅스 기반 컨테이너와 거의 같은 방식으로 작동한다. 그러나 제한 섹션에 요약된 주요 기능에는 몇 가지 눈에 띄는 차이점이 있다.

윈도우에서 주요 쿠버네티스 요소는 리눅스와 동일한 방식으로 작동한다. 이 섹션에서는, 주요 워크로드 인에이블러(enabler) 일부와 이들이 윈도우에 매핑되는 방법에 대해 설명한다.

  • 파드

    파드는 쿠버네티스의 기본 빌딩 블록이다 - 쿠버네티스 오브젝트 모델에서 생성하고 배포하는 가장 작고 간단한 단위. 동일한 파드에 윈도우 및 리눅스 컨테이너를 배포할 수 없다. 파드의 모든 컨테이너는 단일 노드로 스케줄되며 각 노드는 특정 플랫폼 및 아키텍처를 나타낸다. 다음과 같은 파드 기능, 속성 및 이벤트가 윈도우 컨테이너에서 지원된다.

    • 프로세스 분리 및 볼륨 공유 기능을 갖춘 파드 당 하나 또는 여러 개의 컨테이너
    • 파드 상태 필드
    • 준비성(readiness) 및 활성 프로브(liveness probe)
    • postStart 및 preStop 컨테이너 라이프사이클 이벤트
    • 컨피그맵(ConfigMap), 시크릿(Secrets): 환경 변수 또는 볼륨으로
    • EmptyDir
    • 명명된 파이프 호스트 마운트
    • 리소스 제한
  • 컨트롤러

    쿠버네티스 컨트롤러는 파드의 의도한 상태(desired state)를 처리한다. 윈도우 컨테이너에서 지원되는 워크로드 컨트롤러는 다음과 같다.

    • 레플리카셋(ReplicaSet)
    • 레플리케이션컨트롤러(ReplicationController)
    • 디플로이먼트(Deployment)
    • 스테이트풀셋(StatefulSet)
    • 데몬셋(DaemonSet)
    • 잡(Job)
    • 크론잡(CronJob)
  • 서비스

    쿠버네티스 서비스는 논리적인 파드 집합과 그것에(마이크로 서비스라고도 함) 접근하는 정책을 정의하는 추상화 개념이다. 상호-운영 체제 연결을 위해 서비스를 사용할 수 있다. 윈도우에서 서비스는 다음의 유형, 속성 및 기능을 활용할 수 있다.

    • 서비스 환경 변수
    • 노드포트(NodePort)
    • 클러스터IP(ClusterIP)
    • 로드밸런서(LoadBalancer)
    • ExternalName
    • 헤드리스 서비스(Headless services)

파드, 컨트롤러 및 서비스는 쿠버네티스에서 윈도우 워크로드를 관리하는데 중요한 요소이다. 그러나 그 자체로는 동적 클라우드 네이티브 환경에서 윈도우 워크로드의 적절한 수명 주기 관리를 수행하기에 충분하지 않다. 다음 기능에 대한 지원이 추가되었다.

  • 파드와 컨테이너 메트릭
  • Horizontal Pod Autoscaler 지원
  • kubectl Exec
  • 리소스쿼터(Resource Quotas)
  • 스케쥴러 선점(preemption)

컨테이너 런타임

Docker EE
FEATURE STATE: Kubernetes v1.14 [stable]

Docker EE-basic 19.03 이상은 모든 윈도우 서버 버전에 대해 권장되는 컨테이너 런타임이다. 이것은 kubelet에 포함된 dockershim 코드와 함께 작동한다.

CRI-ContainerD
FEATURE STATE: Kubernetes v1.20 [stable]

ContainerD 1.4.0+는 윈도우 쿠버네티스 노드의 컨테이너 런타임으로도 사용할 수 있다.

윈도우에 ContainerD 설치 방법을 확인한다.

퍼시스턴트 스토리지(Persistent Storage)

쿠버네티스 볼륨을 사용하면 데이터 지속성(persistence) 및 파드 볼륨 공유 요구 사항이 있는 복잡한 애플리케이션을 쿠버네티스에 배포할 수 있다. 특정 스토리지 백엔드 또는 프로토콜과 관련된 퍼시스턴트 볼륨 관리에는 볼륨 프로비저닝/디-프로비저닝/크기 조정, 쿠버네티스 노드에 볼륨 연결/분리, 데이터를 유지해야 하는 파드의 개별 컨테이너에 볼륨 마운트/분리와 같은 작업이 포함된다. 특정 스토리지 백엔드 또는 프로토콜에 대해 이러한 볼륨 관리 작업을 구현하는 코드는 쿠버네티스 볼륨 플러그인의 형태로 제공된다. 다음과 같은 광범위한 쿠버네티스 볼륨 플러그인 클래스가 윈도우에서 지원된다.

인-트리(In-tree) 볼륨 플러그인

인-트리 볼륨 플러그인과 관련된 코드는 핵심 쿠버네티스 코드 베이스의 일부로 제공된다. 인-트리 볼륨 플러그인 배포는 추가 스크립트를 설치하거나 별도의 컨테이너화된 플러그인 컴포넌트를 배포할 필요가 없다. 이러한 플러그인들은 볼륨 프로비저닝/디-프로비저닝, 스토리지 백엔드 볼륨 크기 조정, 쿠버네티스 노드에 볼륨 연결/분리, 파드의 개별 컨테이너에 볼륨 마운트/분리를 처리할 수 있다. 다음의 인-트리 플러그인은 윈도우 노드를 지원한다.

FlexVolume 플러그인

FlexVolume 플러그인과 관련된 코드는 아웃-오브-트리(out-of-tree) 스크립트 또는 호스트에 직접 배포해야 하는 바이너리로 제공된다. FlexVolume 플러그인은 쿠버네티스 노드에 볼륨 연결/분리 및 파드의 개별 컨테이너에 볼륨 마운트/분리를 처리한다. FlexVolume 플러그인과 관련된 퍼시스턴트 볼륨의 프로비저닝/디-프로비저닝은 일반적으로 FlexVolume 플러그인과는 별도의 외부 프로비저너를 통해 처리될 수 있다. 호스트에서 powershell 스크립트로 배포된 다음의 FlexVolume 플러그인은 윈도우 노드를 지원한다.

CSI 플러그인
FEATURE STATE: Kubernetes v1.22 [stable]

CSI 플러그인과 관련된 코드는 일반적으로 컨테이너 이미지로 배포되고 데몬셋(DaemonSets) 및 스테이트풀셋(StatefulSets)과 같은 표준 쿠버네티스 구성을 사용하여 배포되는 아웃-오브-트리 스크립트 및 바이너리로 제공된다. CSI 플러그인은 쿠버네티스에서 볼륨 프로비저닝/디-프로비저닝, 볼륨 크기 조정, 쿠버네티스 노드에 볼륨 연결/분리, 파드의 개별 컨테이너에 볼륨 마운트/분리, 스냅샷 및 복제를 사용하여 퍼시스턴트 데이터 백업/복원과 같은 다양한 볼륨 관리 작업을 처리한다.

CSI 노드 플러그인(특히 블록 디바이스 또는 공유 파일시스템으로 노출된 퍼시스턴트 볼륨과 관련된 플러그인)은 디스크 장치 스캔, 파일 시스템 마운트 등과 같은 다양한 특권이 필요한(privileged) 작업을 수행해야 한다. 이러한 작업은 호스트 운영 체제마다 다르다. 리눅스 워커 노드의 경우 컨테이너화된 CSI 노드 플러그인은 일반적으로 특권을 가진 컨테이너로 배포된다. 윈도우 워커 노드의 경우 컨테이너화된 CSI 노드 플러그인에 대한 특권이 필요한 작업은 커뮤니티에서 관리되고, 각 윈도우 노드에 사전 설치되어야 하는 독립형(stand-alone) 바이너리인 csi-proxy를 사용하여 지원된다. 자세한 내용은 배포하려는 CSI 플러그인의 배포 가이드를 참조한다.

윈도우 노드에서 CSI 노드 플러그인은 일반적으로 로컬 스토리지 작업을 처리하는 커뮤니티에서 관리하는 csi-proxy에 의해 노출된 API를 호출한다.

설치에 대한 자세한 내용은 윈도우 CSI 플러그인을 배포할 환경의 배포 가이드를 참고한다. 또한 다음 설치 단계를 참고할 수도 있다.

네트워킹

윈도우 컨테이너용 네트워킹은 CNI 플러그인을 통해 노출된다. 윈도우 컨테이너는 네트워킹과 관련하여 가상 머신과 유사하게 작동한다. 각 컨테이너에는 Hyper-V 가상 스위치(vSwitch)에 연결된 가상 네트워크 어댑터(vNIC)가 있다. 호스트 네트워킹 서비스(HNS)와 호스트 컴퓨팅 서비스(HCS)는 함께 작동하여 컨테이너를 만들고 컨테이너 vNIC을 네트워크에 연결한다. HCS는 컨테이너 관리를 담당하는 반면 HNS는 다음과 같은 네트워킹 리소스 관리를 담당한다.

  • 가상 네트워크(vSwitch 생성 포함)
  • 엔드포인트 / vNIC
  • 네임스페이스
  • 정책(패킷 캡슐화, 로드 밸런싱 규칙, ACL, NAT 규칙 등)

다음의 서비스 사양 유형이 지원된다.

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName
네트워크 모드

윈도우는 L2bridge, L2tunnel, Overlay, Transparent 및 NAT의 다섯 가지 네트워킹 드라이버/모드를 지원한다. 윈도우와 리눅스 워커 노드가 있는 이기종 클러스터에서는 윈도우와 리눅스 모두에서 호환되는 네트워킹 솔루션을 선택해야 한다. 윈도우에서 다음과 같은 out-of-tree 플러그인이 지원되며 각 CNI 사용 시 권장 사항이 있다.

네트워크 드라이버 설명 컨테이너 패킷 수정 네트워크 플러그인 네트워크 플러그인 특성
L2bridge 컨테이너는 외부 vSwitch에 연결된다. 컨테이너는 언더레이 네트워크에 연결된다. 하지만 인그레스/이그레스시에 재작성되기 때문에 물리적 네트워크가 컨테이너 MAC을 학습할 필요가 없다. MAC은 호스트 MAC에 다시 쓰여지고, IP는 HNS OutboundNAT 정책을 사용하여 호스트 IP에 다시 쓰여질 수 있다. win-bridge, Azure-CNI, Flannel 호스트 게이트웨이는 win-bridge를 사용한다. win-bridge는 L2bridge 네트워크 모드를 사용하고, 컨테이너를 호스트의 언더레이에 연결하여 최상의 성능을 제공한다. 노드 간 연결을 위해 사용자 정의 경로(user-defined routes, UDR)가 필요하다.
L2Tunnel 이것은 l2bridge의 특별한 케이스이지만 Azure에서만 사용된다. 모든 패킷은 SDN 정책이 적용되는 가상화 호스트로 전송된다. MAC 재작성되고, 언더레이 네트워크 상에서 IP가 보인다. Azure-CNI Azure-CNI를 사용하면 컨테이너를 Azure vNET과 통합할 수 있으며, Azure Virtual Network에서 제공하는 기능 집합을 활용할 수 있다. 예를 들어, Azure 서비스에 안전하게 연결하거나 Azure NSG를 사용한다. azure-cni 예제를 참고한다.
오버레이(쿠버네티스에서 윈도우용 오버레이 네트워킹은 알파 단계에 있음) 컨테이너에는 외부 vSwitch에 연결된 vNIC이 제공된다. 각 오버레이 네트워크는 사용자 지정 IP 접두사로 정의된 자체 IP 서브넷을 가져온다. 오버레이 네트워크 드라이버는 VXLAN 캡슐화를 사용한다. 외부 헤더로 캡슐화된다. Win-overlay, Flannel VXLAN (win-overlay 사용) win-overlay는 가상 컨테이너 네트워크를 호스트의 언더레이에서 격리하려는 경우(예: 보안 상의 이유로) 사용해야 한다. 데이터 센터의 IP에 제한이 있는 경우, (다른 VNID 태그가 있는) 다른 오버레이 네트워크에 IP를 재사용할 수 있다. 이 옵션을 사용하려면 윈도우 서버 2019에서 KB4489899가 필요하다.
Transparent(ovn-kubernetes의 특수한 유스케이스) 외부 vSwitch가 필요하다. 컨테이너는 논리적 네트워크(논리적 스위치 및 라우터)를 통해 파드 내 통신을 가능하게 하는 외부 vSwitch에 연결된다. 패킷은 GENEVE, STT 터널링을 통해 캡슐화되는데, 동일한 호스트에 있지 않은 파드에 도달하기 위한 터널링을 한다.
패킷은 ovn 네트워크 컨트롤러에서 제공하는 터널 메타데이터 정보를 통해 전달되거나 삭제된다.
NAT는 north-south 통신(데이터 센터와 클라이언트, 네트워크 상의 데이터 센터 외부와의 통신)을 위해 수행된다.
ovn-kubernetes Ansible을 통해 배포한다. 분산 ACL은 쿠버네티스 정책을 통해 적용할 수 있다. IPAM을 지원한다. kube-proxy 없이 로드 밸런싱을 수행할 수 있다. NAT를 수행할 때 iptables/netsh를 사용하지 않고 수행된다.
NAT (쿠버네티스에서 사용되지 않음) 컨테이너에는 내부 vSwitch에 연결된 vNIC이 제공된다. DNS/DHCP는 WinNAT라는 내부 컴포넌트를 사용하여 제공된다. MAC 및 IP는 호스트 MAC/IP에 다시 작성된다. nat 완전성을 위해 여기에 포함되었다.

위에서 설명한대로 플란넬(Flannel) CNI 메타 플러그인VXLAN 네트워크 백엔드 (alpha 지원, win-overlay에 위임) 및 host-gateway network backend (안정적인 지원, win-bridge에 위임)를 통해 윈도우에서도 지원된다. 이 플러그인은 자동 노드 서브넷 임대 할당과 HNS 네트워크 생성을 위해 윈도우 (Flanneld)에서 Flannel 데몬과 함께 작동하도록 참조 CNI 플러그인 (win-overlay, win-bridge) 중 하나에 대한 위임을 지원한다. 이 플러그인은 자체 구성 파일 (cni.conf)을 읽고, 이를 FlannelD 생성하는 subnet.env 파일의 환경 변수와 함께 집계한다. 이후 네트워크 연결을 위한 참조 CNI 플러그인 중 하나에 위임하고 노드 할당 서브넷을 포함하는 올바른 구성을 IPAM 플러그인 (예: 호스트-로컬)으로 보낸다.

노드, 파드, 서비스 오브젝트의 경우 TCP/UDP 트래픽에 대해 다음 네트워크 흐름이 지원된다.

  • 파드 -> 파드(IP)
  • 파드 -> 파드(Name)
  • 파드 -> 서비스(Cluster IP)
  • 파드 -> 서비스(PQDN, 단 "."이 없는 경우에만)
  • 파드 -> 서비스(FQDN)
  • 파드 -> External(IP)
  • 파드 -> External(DNS)
  • 노드 -> 파드
  • 파드 -> 노드
IP 주소 관리(IPAM)

윈도우에서는 다음 IPAM 옵션이 지원된다.

로드 밸런싱과 서비스

윈도우에서는 다음 설정을 사용하여 서비스 및 로드 밸런싱 동작을 구성할 수 있다.

윈도우 서비스 구성
기능 설명 지원되는 쿠버네티스 버전 지원되는 윈도우 OS 빌드 활성화하는 방법
세션 어피니티 특정 클라이언트의 연결이 매번 동일한 파드로 전달되도록 한다. v1.20 이상 윈도우 서버 vNext Insider Preview Build 19551 (또는 그 이상) service.spec.sessionAffinity를 "ClientIP"로 설정
직접 서버 반환 (DSR) IP 주소 수정 및 LBNAT가 컨테이너 vSwitch 포트에서 직접 발생하는 로드 밸런싱 모드. 서비스 트래픽은 소스 IP가 원래 파드 IP로 설정된 상태로 도착한다. v1.20 이상 윈도우 서버 2019 kube-proxy에서 다음 플래그를 설정한다. --feature-gates="WinDSR=true" --enable-dsr=true
대상 보존(Preserve-Destination) 서비스 트래픽의 DNAT를 스킵하여, 백엔드 파드에 도달하는 패킷에서 대상 서비스의 가상 IP를 보존한다. 또한 노드-노드 전달을 비활성화한다. v1.20 이상 윈도우 서버, 버전 1903 (또는 그 이상) 서비스 어노테이션에서 "preserve-destination": "true"를 설정하고 kube-proxy에서 DSR을 활성화한다.
IPv4/IPv6 이중 스택 네트워킹 클러스터 내/외부 기본 IPv4-to-IPv4 통신과 함께 IPv6-to-IPv6 통신 v1.19 이상 윈도우 서버, 버전 2004 (또는 그 이상) IPv4/IPv6 이중 스택을 참고한다.
클라이언트 IP 보존 인그레스 트래픽의 소스 IP가 유지되도록 한다. 또한 노드-노드 전달을 비활성화한다. v1.20 이상 윈도우 서버, 버전 2019 (또는 그 이상) service.spec.externalTrafficPolicy를 "Local"로 설정하고 kube-proxy에서 DSR을 활성화한다.

IPv4/IPv6 이중 스택

IPv6DualStack 기능 게이트를 사용하여 l2bridge 네트워크에 IPv4/IPv6 이중 스택 네트워킹을 활성화할 수 있다. 자세한 내용은 IPv4/IPv6 이중 스택 활성화를 참조한다.

윈도우에서 쿠버네티스와 함께 IPv6를 사용하려면 윈도우 서버 버전 2004 (커널 버전 10.0.19041.610) 이상이 필요하다.

윈도우의 오버레이(VXLAN) 네트워크는 현재 이중 스택 네트워킹을 지원하지 않는다.

제한

윈도우는 쿠버네티스 아키텍처 및 컴포넌트 매트릭스에서 워커 노드로만 지원된다. 즉, 쿠버네티스 클러스터에는 항상 리눅스 마스터 노드가 반드시 포함되어야 하고, 0개 이상의 리눅스 워커 노드 및 0개 이상의 윈도우 워커 노드가 포함된다.

자원 관리

리눅스 cgroup은 리눅스에서 리소스 제어를 위한 파드 경계로 사용된다. 컨테이너는 네트워크, 프로세스 및 파일시스템 격리를 위해 해당 경계 내에 생성된다. cgroups API는 cpu/io/memory 통계를 수집하는 데 사용할 수 있다. 반대로 윈도우는 시스템 네임스페이스 필터가 있는 컨테이너별로 잡(Job) 오브젝트를 사용하여 컨테이너의 모든 프로세스를 포함하고 호스트와의 논리적 격리를 제공한다. 네임스페이스 필터링 없이 윈도우 컨테이너를 실행할 수 있는 방법은 없다. 즉, 시스템 권한은 호스트 컨텍스트에서 삽입될(assert) 수 없으므로 권한이 있는(privileged) 컨테이너는 윈도우에서 사용할 수 없다. 보안 계정 매니져(Security Account Manager, SAM)가 분리되어 있으므로 컨테이너는 호스트의 ID를 가정할 수 없다.

자원 예약

메모리 예약

윈도우에는 리눅스에는 있는 메모리 부족 프로세스 킬러가 없다. 윈도우는 모든 사용자-모드 메모리 할당을 항상 가상 메모리처럼 처리하며, 페이지파일이 필수이다. 결과적으로 윈도우에서는 리눅스에서 발생할 수 있는 메모리 부족 상태에 도달하지 않으며, 프로세스는 메모리 부족(out of memory, OOM) 종료를 겪는 대신 디스크로 페이징한다. 메모리가 오버프로비저닝되고 모든 물리 메모리가 고갈되면 페이징으로 인해 성능이 저하될 수 있다.

kubelet 파라미터 --kubelet-reserve 를 사용하여 메모리 사용량을 합리적인 범위 내로 유지할 수 있으며, --system-reserve 를 사용하여 노드(컨테이너 외부)의 메모리 사용량을 예약할 수 있다. 이들을 사용하면 그만큼 노드 할당(NodeAllocatable)은 줄어든다.

워크로드를 배포할 때, 컨테이너에 리소스 제한을 걸어라(제한만 설정하거나, 제한이 요청과 같아야 함). 이 또한 NodeAllocatable에서 차감되며, 메모리가 꽉 찬 노드에 스케줄러가 파드를 할당하지 않도록 제한한다.

오버프로비저닝을 방지하는 가장 좋은 방법은 윈도우, 도커, 그리고 쿠버네티스 프로세스를 위해 최소 2GB 이상의 시스템 예약 메모리로 kubelet을 설정하는 것이다.

CPU 예약

윈도우, 도커, 그리고 다른 쿠버네티스 호스트 프로세스가 이벤트에 잘 응답할 수 있도록, CPU의 일정 비율을 예약하는 것이 좋다. 이 값은 윈도우 노드에 있는 CPU 코어 수에 따라 조정해야 한다. 이 비율을 결정하려면, 각 노드의 최대 파드 밀도(density)를 관찰하고, 시스템 서비스의 CPU 사용량을 모니터링하여 워크로드 요구사항을 충족하는 값을 선택해야 한다.

kubelet 파라미터 --kubelet-reserve 를 사용하여 CPU 사용량을 합리적인 범위 내로 유지할 수 있으며, --system-reserve 를 사용하여 노드 (컨테이너 외부) 의 CPU 사용량을 예약할 수 있다. 이들을 사용하면 그만큼 노드 할당(NodeAllocatable)은 줄어든다.

기능 제한

  • TerminationGracePeriod: 구현되지 않음
  • 단일 파일 매핑: CRI-ContainerD로 구현 예정
  • 종료 메시지: CRI-ContainerD로 구현 예정
  • 특권을 가진(Privileged) 컨테이너: 현재 윈도우 컨테이너에서 지원되지 않음
  • HugePages: 현재 윈도우 컨테이너에서 지원되지 않음
  • 기존 노드 문제 감지기는 리눅스 전용이며 특권을 가진 컨테이너가 필요하다. 윈도우에서 특권을 가진 컨테이너를 지원하지 않기 때문에 일반적으로 윈도우에서 이 기능이 사용될 것으로 예상하지 않는다.
  • 공유 네임스페이스의 모든 기능이 지원되는 것은 아니다. (자세한 내용은 API 섹션 참조).

각 플래그의 리눅스와의 차이점

윈도우 노드에서의 kubelet 플래그의 동작은 아래에 설명된 대로 다르게 동작한다.

  • --kubelet-reserve, --system-reserve, --eviction-hard 플래그는 Node Allocatable 업데이트

  • --enforce-node-allocable을 사용한 축출(Eviction)은 구현되지 않았다.

  • --eviction-hard--eviction-soft를 사용한 축출은 구현되지 않았다.

  • MemoryPressure 조건은 구현되지 않았다.

  • kubelet이 취한 OOM 축출 조치가 없다.

  • 윈도우 노드에서 실행되는 Kubelet에는 메모리 제한이 없다. --kubelet-reserve--system-reserve는 호스트에서 실행되는 kubelet 또는 프로세스에 제한을 설정하지 않는다. 이는 호스트의 kubelet 또는 프로세스가 node-allocatable 및 스케줄러 외부에서 메모리 리소스 부족을 유발할 수 있음을 의미한다.

  • kubelet 프로세스의 우선 순위를 설정하는 추가 플래그는 --windows-priorityclass라는 윈도우 노드에서 사용할 수 있다. 이 플래그를 사용하면 kubelet 프로세스가 윈도우 호스트에서 실행중인 다른 프로세스와 비교할 때 더 많은 CPU 시간 슬라이스을 얻을 수 있다. 허용되는 값과 그 의미에 대한 자세한 내용은 윈도우 우선순위 클래스에서 확인할 수 있다. kubelet이 항상 충분한 CPU주기를 갖도록 하려면 이 플래그를 ABOVE_NORMAL_PRIORITY_CLASS 이상으로 설정하는 것이 좋다.

스토리지

윈도우에는 컨테이너 계층을 마운트하고 NTFS를 기반으로 하는 복제 파일시스템을 만드는 레이어드(layered) 파일시스템 드라이버가 있다. 컨테이너의 모든 파일 경로는 해당 컨테이너의 컨텍스트 내에서만 확인된다.

  • 도커 볼륨 마운트는 개별 파일이 아닌 컨테이너의 디렉터리만 대상으로 할 수 있다. 이 제한은 CRI-containerD에는 존재하지 않는다.

  • 볼륨 마운트는 파일이나 디렉터리를 호스트 파일시스템으로 다시 투영할 수 없다.

  • 읽기 전용 파일시스템은 윈도우 레지스트리 및 SAM 데이터베이스에 항상 쓰기 접근이 필요하기 때문에 지원되지 않는다. 그러나 읽기 전용 볼륨은 지원된다.

  • 볼륨 사용자 마스크(user-masks) 및 권한은 사용할 수 없다. SAM은 호스트와 컨테이너 간에 공유되지 않기 때문에 이들 간에 매핑이 없다. 모든 권한은 컨테이너 컨텍스트 내에서 해결된다.

결과적으로, 다음 스토리지 기능은 윈도우 노드에서 지원되지 않는다.

  • 볼륨 하위 경로(subpath) 마운트. 전체 볼륨만 윈도우 컨테이너에 마운트할 수 있다.
  • 시크릿에 대한 하위 경로 볼륨 마운트
  • 호스트 마운트 프로젝션
  • DefaultMode(UID/GID 종속성에 기인함)
  • 읽기 전용 루트 파일시스템. 매핑된 볼륨은 여전히 ​​읽기 전용을 지원한다.
  • 블록 디바이스 매핑
  • 저장 매체로서의 메모리
  • uui/guid, 사용자별 리눅스 파일시스템 권한과 같은 파일시스템 기능
  • NFS 기반 스토리지/볼륨 지원
  • 마운트된 볼륨 확장(resizefs)

네트워킹

윈도우 컨테이너 네트워킹은 리눅스 네트워킹과 몇 가지 중요한 면에서 다르다. 윈도우 컨테이너 네트워킹에 대한 Microsoft 문서에는 추가 세부 정보와 배경이 포함되어 있다.

윈도우 호스트 네트워킹 서비스와 가상 스위치는 네임스페이스를 구현하고 파드 또는 컨테이너에 필요한 가상 NIC을 만들 수 있다. 그러나 DNS, 라우트, 메트릭과 같은 많은 구성은 리눅스에서와 같이 /etc/... 파일이 아닌 윈도우 레지스트리 데이터베이스에 저장된다. 컨테이너의 윈도우 레지스트리는 호스트 레지스트리와 별개이므로 호스트에서 컨테이너로 /etc/resolv.conf를 매핑하는 것과 같은 개념은 리눅스에서와 동일한 효과를 갖지 않는다. 해당 컨테이너의 컨텍스트에서 실행되는 윈도우 API를 사용하여 구성해야 한다. 따라서 CNI 구현에서는 파일 매핑에 의존하는 대신 HNS를 호출하여 네트워크 세부 정보를 파드 또는 컨테이너로 전달해야 한다.

다음 네트워킹 기능은 윈도우 노드에서 지원되지 않는다.

  • 윈도우 파드에서는 호스트 네트워킹 모드를 사용할 수 없다.

  • 노드 자체에서 로컬 NodePort 접근은 실패한다. (다른 노드 또는 외부 클라이언트에서는 가능)

  • 노드에서 서비스 VIP에 접근하는 것은 향후 윈도우 서버 릴리스에서 사용할 수 있다.

  • 한 서비스는 최대 64개의 백엔드 파드 또는 고유한 목적지 IP를 지원할 수 있다.

  • kube-proxy의 오버레이 네트워킹 지원은 베타 기능이다. 또한 윈도우 서버 2019에 KB4482887을 설치해야 한다.

  • 비-DSR 모드의 로컬 트래픽 정책

  • 오버레이 네트워크에 연결된 윈도우 컨테이너는 IPv6 스택을 통한 통신을 지원하지 않는다. 이 네트워크 드라이버가 IPv6 주소를 사용하고 kubelet, kube-proxy 및 CNI 플러그인에서 후속 쿠버네티스 작업을 사용할 수 있도록 하는데 필요한 뛰어난 윈도우 플랫폼 작업이 있다.

  • win-overlay, win-bridge, Azure-CNI 플러그인을 통해 ICMP 프로토콜을 사용하는 아웃바운드 통신. 특히, 윈도우 데이터 플레인 (VFP)은 ICMP 패킷 치환을 지원하지 않는다. 이것은 다음을 의미한다.

    • 동일한 네트워크(예: ping을 통한 파드 간 통신) 내의 목적지로 전달되는 ICMP 패킷은 예상대로 제한 없이 작동한다.

    • TCP/UDP 패킷은 예상대로 제한 없이 작동한다.

    • 원격 네트워크를 통과하도록 지정된 ICMP 패킷(예: ping을 통한 파드에서 외부 인터넷으로의 통신)은 치환될 수 없으므로 소스로 다시 라우팅되지 않는다.

    • TCP/UDP 패킷은 여전히 ​​치환될 수 있기 때문에 ping <destination>curl <destination>으로 대체하여 외부와의 연결을 디버깅할 수 있다.

해당 기능은 쿠버네티스 v1.15에 추가되었다.

  • kubectl port-forward
CNI 플러그인
  • 윈도우 참조 네트워크 플러그인 win-bridge와 win-overlay는 현재 "CHECK" 구현 누락으로 인해 CNI 사양 v0.4.0을 구현하지 않는다.

  • Flannel VXLAN CNI는 윈도우에서 다음과 같은 제한이 있다.

    1. 노드-파드 연결은 설계상 불가능하다. Flannel v0.12.0(또는 그 이상)이 있는 로컬 파드에서만 가능하다.

    2. VNI 4096와 UDP 4789 포트 사용은 제한된다. VNI 제한은 작업 중이며 향후 릴리스(오픈 소스 flannel 변경)에서 구현될 것이다. 이러한 파라미터에 대한 자세한 내용은 공식 Flannel VXLAN 백엔드 문서를 참고한다.

DNS
  • ClusterFirstWithHostNet은 DNS에서 지원되지 않는다. 윈도우는 '.'이 있는 모든 이름을 FQDN으로 처리하고 PQDN 확인을 건너뛴다.

  • 리눅스에서는 PQDN을 확인하려고 할 때 사용되는 DNS 접미사 목록이 있다. 윈도우에서는 해당 파드의 네임스페이스(예: mydns.svc.cluster.local)와 연결된 DNS 접미사인 DNS 접미사 1개만 있다. 윈도우는 FQDN과 서비스 또는 해당 접미사만으로 확인할 수 있는 이름을 확인할 수 있다. 예를 들어, 디폴트 네임스페이스에서 생성된 파드에는 DNS 접미사 default.svc.cluster.local이 있다. 윈도우 파드에서는 kubernetes.default.svc.cluster.localkubernetes를 모두 확인할 수 있지만 kubernetes.default 또는 kubernetes.default.svc와 같은 중간 항목은 확인할 수 없다.

  • 윈도우에서는 사용할 수 있는 여러 가지의 DNS 리졸버(resolver)가 있다. 이들은 약간 다른 동작을 제공하므로, 이름 쿼리 확인을 위해 Resolve-DNSName 유틸리티를 사용하는 것이 좋다.

IPv6

윈도우의 쿠버네티스는 단일 스택 "IPv6 전용" 네트워킹을 지원하지 않는다. 그러나 단일 제품군 서비스를 사용하는 파드와 노드에 대한 이중 스택 IPv4/IPv6 네트워킹이 지원된다. 자세한 내용은 IPv4/IPv6 이중 스택 네트워킹을 참고한다.

세션 어피니티(affinity)

service.spec.sessionAffinityConfig.clientIP.timeoutSeconds를 사용하는 윈도우 서비스의 최대 세션 고정(sticky) 시간 설정은 지원되지 않는다.

보안

시크릿(Secret)은 노드의 볼륨(리눅스의 tmpfs/in-memory와 비교)에 일반 텍스트로 작성된다. 이는 고객이 두 가지 작업을 수행해야 함을 의미한다.

  1. 파일 ACL을 사용하여 시크릿 파일 위치를 보호한다.
  2. BitLocker를 사용한 볼륨-레벨 암호화를 사용한다.

RunAsUsername은 컨테이너 프로세스를 노드 기본 사용자로 실행하기 위해 윈도우 파드 또는 컨테이너에 지정할 수 있다. 이것은 RunAsUser와 거의 동일하다.

SELinux, AppArmor, Seccomp, 기능(POSIX 기능)과 같은 리눅스 특유의 파드 시큐리티 컨텍스트 권한은 지원하지 않는다.

또한 이미 언급했듯이 특권을 가진 컨테이너는 윈도우에서 지원되지 않는다.

API

대부분의 Kubernetes API가 윈도우에서 작동하는 방식은 차이가 없다. 중요한 차이점은 OS와 컨테이너 런타임의 차이로 귀결된다. 특정 상황에서 파드 또는 컨테이너와 같은 워크로드 API의 일부 속성은 리눅스에서 구현되고 윈도우에서 실행되지 않는다는 가정 하에 설계되었다.

높은 수준에서 이러한 OS 개념은 다르다.

  • ID - 리눅스는 정수형으로 표시되는 userID(UID) 및 groupID(GID)를 사용한다. 사용자와 그룹 이름은 정식 이름이 아니다. UID+GID에 대한 /etc/groups 또는 /etc/passwd의 별칭일 뿐이다. 윈도우는 윈도우 보안 계정 관리자(Security Account Manager, SAM) 데이터베이스에 저장된 더 큰 이진 보안 식별자(SID)를 사용한다. 이 데이터베이스는 호스트와 컨테이너 간에 또는 컨테이너들 간에 공유되지 않는다.

  • 파일 퍼미션 - 윈도우는 권한 및 UUID+GID의 비트 마스크(bitmask) 대신 SID를 기반으로 하는 접근 제어 목록을 사용한다.

  • 파일 경로 - 윈도우의 규칙은 / 대신 \를 사용하는 것이다. Go IO 라이브러리는 두 가지 파일 경로 분리자를 모두 허용한다. 하지만, 컨테이너 내부에서 해석되는 경로 또는 커맨드 라인을 설정할 때 \가 필요할 수 있다.

  • 신호(Signals) - 윈도우 대화형(interactive) 앱은 종료를 다르게 처리하며, 다음 중 하나 이상을 구현할 수 있다.

    • UI 스레드는 WM_CLOSE를 포함하여 잘 정의된(well-defined) 메시지를 처리한다.

    • 콘솔 앱은 컨트롤 핸들러(Control Handler)를 사용하여 ctrl-c 또는 ctrl-break를 처리한다.

    • 서비스는 SERVICE_CONTROL_STOP 제어 코드를 수용할 수 있는 Service Control Handler 함수를 등록한다.

종료 코드는 0일 때 성공, 0이 아닌 경우 실패인 동일한 규칙을 따른다. 특정 오류 코드는 윈도우와 리눅스에서 다를 수 있다. 그러나 쿠버네티스 컴포넌트(kubelet, kube-proxy)에서 전달된 종료 코드는 변경되지 않는다.

V1.Container
  • V1.Container.ResourceRequirements.limits.cpu 및 V1.Container.ResourceRequirements.limits.memory - 윈도우는 CPU 할당에 하드 리밋(hard limit)을 사용하지 않는다. 대신 공유 시스템이 사용된다. 밀리코어를 기반으로 하는 기존 필드는 윈도우 스케줄러가 뒤따르는 상대적인 공유로 스케일된다. 참고: kuberuntime/helpers_windows.go, 참고: Microsoft 문서 내 리소스 제어

    • Huge page는 윈도우 컨테이너 런타임에서 구현되지 않으며, 사용할 수 없다. 컨테이너에 대해 구성할 수 없는 사용자 권한(privilege) 어설트가 필요하다.
  • V1.Container.ResourceRequirements.requests.cpu 및 V1.Container.ResourceRequirements.requests.memory - 노드의 사용 가능한 리소스에서 요청(requests)을 빼서, 노드에 대한 오버 프로비저닝을 방지하는데 사용할 수 있다. 그러나 오버 프로비저닝된 노드에서 리소스를 보장하는 데는 사용할 수 없다. 운영자가 오버 프로비저닝을 완전히 피하려는 경우 모범 사례로 모든 컨테이너에 적용해야 한다.

  • V1.Container.SecurityContext.allowPrivilegeEscalation - 윈도우에서는 불가능하며, 어떤 기능도 연결되지 않는다.

  • V1.Container.SecurityContext.Capabilities - POSIX 기능은 윈도우에서 구현되지 않는다.

  • V1.Container.SecurityContext.privileged - 윈도우는 특권을 가진 컨테이너를 지원하지 않는다.

  • V1.Container.SecurityContext.procMount - 윈도우에는 /proc 파일시스템이 없다.

  • V1.Container.SecurityContext.readOnlyRootFilesystem - 윈도우에서는 불가능하며, 레지스트리 및 시스템 프로세스가 컨테이너 내부에서 실행되려면 쓰기 권한이 필요하다.

  • V1.Container.SecurityContext.runAsGroup - 윈도우에서는 불가능하며, GID 지원이 없다.

  • V1.Container.SecurityContext.runAsNonRoot - 윈도우에는 root 사용자가 없다. 가장 가까운 항목은 노드에 존재하지 않는 아이덴티티(identity)인 ContainerAdministrator이다.

  • V1.Container.SecurityContext.runAsUser - 윈도우에서는 불가능하며, 정수값으로의 UID 지원이 없다.

  • V1.Container.SecurityContext.seLinuxOptions - 윈도우에서는 불가능하며, SELinux가 없다.

  • V1.Container.terminationMessagePath - 윈도우가 단일 파일 매핑을 지원하지 않는다는 점에서 몇 가지 제한이 있다. 기본값은 /dev/termination-log이며, 기본적으로 윈도우에 존재하지 않기 때문에 작동한다.

V1.Pod
  • V1.Pod.hostIPC, v1.pod.hostpid - 윈도우에서 호스트 네임스페이스 공유가 불가능하다.

  • V1.Pod.hostNetwork - 호스트 네트워크를 공유하기 위한 윈도우 OS 지원이 없다.

  • V1.Pod.dnsPolicy - ClusterFirstWithHostNet - 윈도우에서 호스트 네트워킹이 지원되지 않기 때문에 지원되지 않는다.

  • V1.Pod.podSecurityContext - 아래 V1.PodSecurityContext 내용을 참고한다.

  • V1.Pod.shareProcessNamespace - 이것은 베타 기능이며, 윈도우에서 구현되지 않은 리눅스 네임스페이스에 따라 다르다. 윈도우는 프로세스 네임스페이스 또는 컨테이너의 루트 파일시스템을 공유할 수 없다. 네트워크만 공유할 수 있다.

  • V1.Pod.terminationGracePeriodSeconds - 이것은 윈도우의 도커에서 완전히 구현되지 않았다. 참조의 내용을 참고한다. 현재 동작은 ENTRYPOINT 프로세스가 CTRL_SHUTDOWN_EVENT로 전송된 다음, 윈도우가 기본적으로 5초를 기다린 후, 마지막으로 정상적인 윈도우 종료 동작을 사용하여 모든 프로세스를 종료하는 것이다. 5초 기본값은 실제로 컨테이너 내부 윈도우 레지스트리에 있으므로 컨테이너를 빌드할 때 재정의 할 수 있다.

  • V1.Pod.volumeDevices - 이것은 베타 기능이며, 윈도우에서 구현되지 않는다. 윈도우는 원시 블록 장치(raw block device)를 파드에 연결할 수 없다.

  • V1.Pod.volumes - EmptyDir, 시크릿, 컨피그맵, HostPath - 모두 작동하며 TestGrid에 테스트가 있다.

    • V1.emptyDirVolumeSource - 노드 기본 매체는 윈도우의 디스크이다. 윈도우에는 내장 RAM 디스크가 없기 때문에 메모리는 지원되지 않는다.
  • V1.VolumeMount.mountPropagation - 마운트 전파(propagation)는 윈도우에서 지원되지 않는다.

V1.PodSecurityContext

PodSecurityContext 필드는 윈도우에서 작동하지 않는다. 참조를 위해 여기에 나열한다.

  • V1.PodSecurityContext.SELinuxOptions - SELinux는 윈도우에서 사용할 수 없다.

  • V1.PodSecurityContext.RunAsUser - 윈도우에서는 사용할 수 없는 UID를 제공한다.

  • V1.PodSecurityContext.RunAsGroup - 윈도우에서는 사용할 수 없는 GID를 제공한다.

  • V1.PodSecurityContext.RunAsNonRoot - 윈도우에는 root 사용자가 없다. 가장 가까운 항목은 노드에 존재하지 않는 아이덴티티인 ContainerAdministrator이다.

  • V1.PodSecurityContext.SupplementalGroups - 윈도우에서는 사용할 수 없는 GID를 제공한다.

  • V1.PodSecurityContext.Sysctls - 이것들은 리눅스 sysctl 인터페이스의 일부이다. 윈도우에는 이에 상응하는 것이 없다.

운영 체제 버전 제한

윈도우에는 호스트 OS 버전이 컨테이너 베이스 이미지 OS 버전과 일치해야 하는 엄격한 호환성 규칙이 있다. 윈도우 서버 2019의 컨테이너 운영 체제가 있는 윈도우 컨테이너만 지원된다. 윈도우 컨테이너 이미지 버전의 일부 이전 버전과의 호환성을 가능하게 하는 컨테이너의 Hyper-V 격리는 향후 릴리스로 계획되어 있다.

도움 받기 및 트러블슈팅

쿠버네티스 클러스터 트러블슈팅을 위한 기본 도움말은 이 섹션에서 먼저 찾아야 한다. 이 섹션에는 몇 가지 추가 윈도우 관련 트러블슈팅 도움말이 포함되어 있다. 로그는 쿠버네티스에서 트러블슈팅하는데 중요한 요소이다. 다른 기여자로부터 트러블슈팅 지원을 구할 때마다 이를 포함해야 한다. SIG-Windows 로그 수집에 대한 기여 가이드의 지침을 따른다.

  • start.ps1이 성공적으로 완료되었는지 어떻게 알 수 있는가?

    kubelet, kube-proxy 및 (Flannel을 네트워킹 솔루션으로 선택한 경우) 노드에서 실행 중인 flanneld 호스트 에이전트 프로세스를 확인할 수 있어야 하는데, 별도의 PowerShell 윈도우에서 실행 중인 로그가 표시된다. 또한 윈도우 노드는 쿠버네티스 클러스터에서 "Ready"로 조회되어야 한다.

  • 백그라운드에서 서비스로 실행되도록 쿠버네티스 노드 프로세스를 구성할 수 있는가?

    Kubelet 및 kube-proxy는 이미 기본 윈도우 서비스로 실행되도록 구성되어 있으며, 실패(예: 프로세스 충돌) 시 서비스를 자동으로 다시 시작하여 복원력(resiliency)을 제공한다. 이러한 노드 컴포넌트를 서비스로 구성하기 위한 두 가지 옵션이 있다.

    • 네이티브 윈도우 서비스

      Kubelet와 kube-proxy는 sc.exe를 사용하여 네이티브 윈도우 서비스로 실행될 수 있다.

      # 두 개의 개별 명령으로 kubelet 및 kube-proxy에 대한 서비스 생성
      sc.exe create <컴포넌트_명> binPath= "<바이너리_경로> --service <다른_인자>"
      
      # 인자에 공백이 포함된 경우 이스케이프 되어야 한다.
      sc.exe create kubelet binPath= "C:\kubelet.exe --service --hostname-override 'minion' <다른_인자>"
      
      # 서비스 시작
      Start-Service kubelet
      Start-Service kube-proxy
      
      # 서비스 중지
      Stop-Service kubelet (-Force)
      Stop-Service kube-proxy (-Force)
      
      # 서비스 상태 질의
      Get-Service kubelet
      Get-Service kube-proxy
      
    • nssm.exe 사용

      또한 언제든지 nssm.exe와 같은 대체 서비스 관리자를 사용하여 백그라운드에서 이러한 프로세스(flanneld, kubelet, kube-proxy)를 실행할 수 있다. 이 샘플 스크립트를 사용하여 백그라운드에서 윈도우 서비스로 실행하기 위해 nssm.exe를 활용하여 kubelet, kube-proxy, flanneld.exe를 등록할 수 있다.

      register-svc.ps1 -NetworkMode <네트워크 모드> -ManagementIP <윈도우 노드 IP> -ClusterCIDR <클러스터 서브넷> -KubeDnsServiceIP <Kube-dns 서비스 IP> -LogDir <로그 위치 디렉터리>
      

      파라미터 설명은 아래와 같다.

      • NetworkMode: 네트워크 모드 l2bridge(flannel host-gw, 기본값이기도 함) 또는 네트워크 솔루션으로 선택한 오버레이(flannel vxlan)
      • ManagementIP: 윈도우 노드에 할당된 IP 주소. ipconfig를 사용하여 찾을 수 있다.
      • ClusterCIDR: 클러스터 서브넷 범위. (기본값 10.244.0.0/16)
      • KubeDnsServiceIP: 쿠버네티스 DNS 서비스 IP (기본값 10.96.0.10)
      • LogDir: kubelet 및 kube-proxy 로그가 각각의 출력 파일로 리다이렉션되는 디렉터리(기본값 C:\k)

      위에 언급된 스크립트가 적합하지 않은 경우, 다음 예제를 사용하여 nssm.exe를 수동으로 구성할 수 있다.

      flanneld.exe를 등록한다.

      nssm install flanneld C:\flannel\flanneld.exe
      nssm set flanneld AppParameters --kubeconfig-file=c:\k\config --iface=<ManagementIP> --ip-masq=1 --kube-subnet-mgr=1
      nssm set flanneld AppEnvironmentExtra NODE_NAME=<hostname>
      nssm set flanneld AppDirectory C:\flannel
      nssm start flanneld
      

      kubelet.exe를 등록한다.

      nssm install kubelet C:\k\kubelet.exe
      nssm set kubelet AppParameters --hostname-override=<hostname> --v=6 --pod-infra-container-image=k8s.gcr.io/pause:3.5 --resolv-conf="" --allow-privileged=true --enable-debugging-handlers --cluster-dns=<DNS-service-IP> --cluster-domain=cluster.local --kubeconfig=c:\k\config --hairpin-mode=promiscuous-bridge --image-pull-progress-deadline=20m --cgroups-per-qos=false  --log-dir=<log directory> --logtostderr=false --enforce-node-allocatable="" --network-plugin=cni --cni-bin-dir=c:\k\cni --cni-conf-dir=c:\k\cni\config
      nssm set kubelet AppDirectory C:\k
      nssm start kubelet
      

      kube-proxy.exe를 등록한다(l2bridge / host-gw).

      nssm install kube-proxy C:\k\kube-proxy.exe
      nssm set kube-proxy AppDirectory c:\k
      nssm set kube-proxy AppParameters --v=4 --proxy-mode=kernelspace --hostname-override=<hostname>--kubeconfig=c:\k\config --enable-dsr=false --log-dir=<log directory> --logtostderr=false
      nssm.exe set kube-proxy AppEnvironmentExtra KUBE_NETWORK=cbr0
      nssm set kube-proxy DependOnService kubelet
      nssm start kube-proxy
      

      kube-proxy.exe를 등록한다(overlay / vxlan).

      nssm install kube-proxy C:\k\kube-proxy.exe
      nssm set kube-proxy AppDirectory c:\k
      nssm set kube-proxy AppParameters --v=4 --proxy-mode=kernelspace --feature-gates="WinOverlay=true" --hostname-override=<hostname> --kubeconfig=c:\k\config --network-name=vxlan0 --source-vip=<source-vip> --enable-dsr=false --log-dir=<log directory> --logtostderr=false
      nssm set kube-proxy DependOnService kubelet
      nssm start kube-proxy
      

      초기 트러블슈팅을 위해 nssm.exe에서 다음 플래그를 사용하여 stdout 및 stderr을 출력 파일로 리다이렉션할 수 있다.

      nssm set <Service Name> AppStdout C:\k\mysvc.log
      nssm set <Service Name> AppStderr C:\k\mysvc.log
      

      자세한 내용은 공식 nssm 사용 문서를 참고한다.

  • 내 윈도우 파드에 네트워크 연결이 없다.

    가상 머신을 사용하는 경우, 모든 VM 네트워크 어댑터에서 MAC 스푸핑이 활성화되어 있는지 확인한다.

  • 내 윈도우 파드가 외부 리소스를 ping 할 수 없다.

    윈도우 파드에는 현재 ICMP 프로토콜용으로 프로그래밍된 아웃바운드 규칙이 없다. 그러나 TCP/UDP는 지원된다. 클러스터 외부 리소스에 대한 연결을 시연하려는 경우, ping <IP>를 해당 curl <IP>명령으로 대체한다.

    여전히 문제가 발생하는 경우, cni.conf의 네트워크 구성에 특별히 추가 확인이 필요하다. 언제든지 이 정적 파일을 편집할 수 있다. 구성 업데이트는 새로 생성된 모든 쿠버네티스 리소스에 적용된다.

    쿠버네티스 네트워킹 요구 사항 중 하나(쿠버네티스 모델)는 클러스터 통신이 내부적으로 NAT 없이 발생하는 것이다. 이 요구 사항을 준수하기 위해 아웃바운드 NAT가 발생하지 않도록 하는 모든 통신에 대한 ExceptionList가 있다. 그러나 이것은 쿼리하려는 외부 IP를 ExceptionList에서 제외해야 함도 의미한다. 그래야만 윈도우 파드에서 발생하는 트래픽이 제대로 SNAT 되어 외부에서 응답을 받는다. 이와 관련하여 cni.conf의 ExceptionList는 다음과 같아야 한다.

    "ExceptionList": [
        "10.244.0.0/16",  # 클러스터 서브넷
        "10.96.0.0/12",   # 서비스 서브넷
        "10.127.130.0/24" # 관리(호스트) 서브넷
    ]
    
  • 내 윈도우 노드가 NodePort 서비스에 접근할 수 없다.

    노드 자체에서는 로컬 NodePort 접근이 실패한다. 이것은 알려진 제약사항이다. NodePort 접근은 다른 노드 또는 외부 클라이언트에서는 가능하다.

  • 컨테이너의 vNIC 및 HNS 엔드포인트가 삭제되었다.

    이 문제는 hostname-override 파라미터가 kube-proxy에 전달되지 않은 경우 발생할 수 있다. 이를 해결하려면 사용자는 다음과 같이 hostname을 kube-proxy에 전달해야 한다.

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  • 플란넬(flannel)을 사용하면 클러스터에 다시 조인(join)한 후 노드에 이슈가 발생한다.

    이전에 삭제된 노드가 클러스터에 다시 조인될 때마다, flannelD는 새 파드 서브넷을 노드에 할당하려고 한다. 사용자는 다음 경로에서 이전 파드 서브넷 구성 파일을 제거해야 한다.

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  • start.ps1을 시작한 후, flanneld가 "Waiting for the Network to be created"에서 멈춘다.

    이슈에 대한 수많은 보고가 있다. 플란넬 네트워크의 관리 IP가 설정될 때의 타이밍 이슈일 가능성이 높다. 해결 방법은 start.ps1을 다시 시작하거나 다음과 같이 수동으로 다시 시작하는 것이다.

    PS C:> [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows_Worker_Hostname>")
    PS C:> C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows_Worker_Node_IP> --ip-masq=1 --kube-subnet-mgr=1
    
  • /run/flannel/subnet.env 누락으로 인해 윈도우 파드를 시작할 수 없다.

    이것은 플란넬이 제대로 실행되지 않았음을 나타낸다. flanneld.exe를 다시 시작하거나 쿠버네티스 마스터의 /run/flannel/subnet.env에서 윈도우 워커 노드의 C:\run\flannel\subnet.env로 파일을 수동으로 복사할 수 있고, FLANNEL_SUBNET 행을 다른 숫자로 수정한다. 예를 들어, 다음은 노드 서브넷 10.244.4.1/24가 필요한 경우이다.

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    
  • 내 윈도우 노드가 서비스 IP를 사용하여 내 서비스에 접근할 수 없다.

    이는 윈도우에서 현재 네트워킹 스택의 알려진 제약 사항이다. 그러나 윈도우 파드는 서비스 IP에 접근할 수 있다.

  • kubelet을 시작할 때 네트워크 어댑터를 찾을 수 없다.

    윈도우 네트워킹 스택에는 쿠버네티스 네트워킹이 작동하기 위한 가상 어댑터가 필요하다. 다음 명령이 (어드민 셸에서) 결과를 반환하지 않으면, Kubelet이 작동하는데 필요한 필수 구성 요소인 가상 네트워크 생성이 실패한 것이다.

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    호스트 네트워크 어댑터가 "Ethernet"이 아닌 경우, 종종 start.ps1 스크립트의 InterfaceName 파라미터를 수정하는 것이 좋다. 그렇지 않으면 start-kubelet.ps1 스크립트의 출력을 참조하여 가상 네트워크 생성 중에 오류가 있는지 확인한다.

  • 내 파드가 "Container Creating"에서 멈췄거나 계속해서 다시 시작된다.

    퍼즈 이미지가 OS 버전과 호환되는지 확인한다. 지침에서는 OS와 컨테이너가 모두 버전 1803이라고 가정한다. 이후 버전의 윈도우가 있는 경우, Insider 빌드와 같이 그에 따라 이미지를 조정해야 한다. 이미지는 Microsoft의 도커 리포지터리를 참조한다. 그럼에도 불구하고, 퍼즈 이미지 Dockerfile과 샘플 서비스는 이미지가 :latest로 태그될 것으로 예상한다.

  • DNS 확인(resolution)이 제대로 작동하지 않는다.

    윈도우에 대한 DNS 제한을 확인한다.

  • kubectl port-forward가 "unable to do port forwarding: wincat not found"로 실패한다.

    이는 쿠버네티스 1.15 및 퍼즈 인프라 컨테이너 mcr.microsoft.com/oss/kubernetes/pause:1.4.1에서 구현되었다. 해당 버전 또는 최신 버전을 사용해야 한다. 자체 퍼즈 인프라 컨테이너를 빌드하려면 wincat을 포함해야 한다.

    윈도우에서 포트 포워딩을 지원하려면 퍼즈 인프라 컨테이너를 이용하기 위해서 wincat.exe가 필요하다. 윈도우 OS 버전과 호환되는 지원되는 이미지를 사용하고 있는지 확인해야 한다. 자신만의 퍼즈 인프라 컨테이너를 구축하려면 wincat을 포함해야 한다.

  • 내 윈도우 서버 노드가 프록시 뒤에 있기 때문에 내 쿠버네티스 설치가 실패한다.

    프록시 뒤에 있는 경우 다음 PowerShell 환경 변수를 정의해야 한다.

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    
  • 퍼즈(pause) 컨테이너란 무엇인가?

    쿠버네티스 파드에서는 컨테이너 엔드포인트를 호스팅하기 위해 먼저 인프라 또는 "퍼즈" 컨테이너가 생성된다. 인프라 및 워커 컨테이너를 포함하여 동일한 파드에 속하는 컨테이너는 공통 네트워크 네임스페이스 및 엔드포인트(동일한 IP 및 포트 공간)를 공유한다. 네트워크 구성을 잃지 않고 워커 컨테이너가 충돌하거나 다시 시작되도록 하려면 퍼즈 컨테이너가 필요하다.

    퍼즈 이미지 추천 버전을 찾기 위해서는 퍼즈 이미지를 참고한다.

추가 조사

이러한 단계로 문제가 해결되지 않으면, 다음을 통해 쿠버네티스의 윈도우 노드에서 윈도우 컨테이너를 실행하는데 도움을 받을 수 있다.

이슈 리포팅 및 기능 요청

버그처럼 보이는 부분이 있거나 기능 요청을 하고 싶다면, GitHub 이슈 트래킹 시스템을 활용한다. GitHub에서 이슈를 열고 SIG-Windows에 할당할 수 있다. 먼저 이전에 보고된 이슈 목록을 검색하고 이슈에 대한 경험을 언급하고 추가 로그를 첨부해야 한다. SIG-Windows 슬랙은 티켓을 만들기 전에 초기 지원 및 트러블슈팅 아이디어를 얻을 수 있는 좋은 방법이기도 하다.

버그를 제출하는 경우, 다음과 같이 문제를 재현하는 방법에 대한 자세한 정보를 포함한다.

  • 쿠버네티스 버전: kubectl version
  • 환경 세부사항: 클라우드 공급자, OS 배포판, 네트워킹 선택 및 구성, 도커 버전
  • 문제를 재현하기 위한 세부 단계
  • 관련 로그
  • SIG-Windows 회원의 주의를 끌 수 있도록 /sig windows로 이슈에 대해 어노테이션을 달아 이슈에 sig/windows 태그를 지정한다.

다음 내용

로드맵에는 많은 기능이 있다. 요약된 높은 수준의 목록이 아래에 포함되어 있지만, 로드맵 프로젝트를 보고 기여하여 윈도우 지원을 개선하는데 도움이 주는 것이 좋다.

Hyper-V 격리(isolation)

쿠버네티스에서 윈도우 컨테이너에 대해 다음 유스케이스를 사용하려면 Hyper-V 격리가 필요하다.

  • 추가 보안을 위해 파드 간 하이퍼바이저 기반 격리

  • 하위 호환성을 통해 컨테이너를 다시 빌드할 필요 없이 노드에서 최신 윈도우 서버 버전을 실행할 수 있다.

  • 파드에 대한 특정 CPU/NUMA 설정

  • 메모리 격리 및 예약

Hyper-V 격리 지원은 이후 릴리스에 추가되며 CRI-Containerd가 필요하다.

kubeadm 및 클러스터 API를 사용한 배포

Kubeadm은 사용자가 쿠버네티스 클러스터를 배포하기 위한 사실상의 표준이 되고 있다. kubeadm의 윈도우 노드 지원은 현재 작업 중이지만 여기에서 가이드를 사용할 수 있다. 또한 윈도우 노드가 적절하게 프로비저닝되도록 클러스터 API에 투자하고 있다.

2.2.4.2 - 쿠버네티스에서 윈도우 컨테이너 스케줄링을 위한 가이드

많은 조직에서 실행하는 서비스와 애플리케이션의 상당 부분이 윈도우 애플리케이션으로 구성된다. 이 가이드는 쿠버네티스에서 윈도우 컨테이너를 구성하고 배포하는 단계를 안내한다.

목표

  • 윈도우 노드에서 윈도우 컨테이너를 실행하는 예시 디플로이먼트를 구성한다.
  • (선택) 그룹 매니지드 서비스 어카운트(GMSA)를 이용한 사용자 파드를 위한 액티브 디렉터리 신원(Active Directory Identity)을 구성한다.

시작하기 전에

  • 컨트롤 플레인과 윈도우 서버로 운영되는 워커 노드를 포함하는 쿠버네티스 클러스터를 생성한다.
  • 쿠버네티스에서 서비스와 워크로드를 생성하고 배포하는 것은 리눅스나 윈도우 컨테이너 모두 비슷한 방식이라는 것이 중요하다. Kubectl 커맨드로 클러스터에 접속하는 것은 동일하다. 아래 단원의 예시를 통해 윈도우 컨테이너와 좀 더 빨리 친숙해질 수 있다.

시작하기: 윈도우 컨테이너 배포하기

쿠버네티스에서 윈도우 컨테이너를 배포하려면, 먼저 예시 애플리케이션을 생성해야 한다. 아래 예시 YAML 파일은 간단한 웹서버 애플리케이션을 생성한다. 아래 내용으로 채운 서비스 스펙을 win-webserver.yaml로 생성하자.

apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # 이 서비스에서 제공하는 포트
    - port: 80
      targetPort: 80
  selector:
    app: win-webserver
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 2
  selector:
    matchLabels:
      app: win-webserver
  template:
    metadata:
      labels:
        app: win-webserver
      name: win-webserver
    spec:
     containers:
      - name: windowswebserver
        image: mcr.microsoft.com/windows/servercore:ltsc2019
        command:
        - powershell.exe
        - -command
        - "<#code used from https://gist.github.com/19WAS85/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
     nodeSelector:
      kubernetes.io/os: windows
  1. 모든 노드가 건강한지 확인한다.

    kubectl get nodes
    
  2. 서비스를 배포하고 파드 갱신을 지켜보자.

    kubectl apply -f win-webserver.yaml
    kubectl get pods -o wide -w
    

    이 서비스가 정확히 배포되면 모든 파드는 Ready로 표기된다. 지켜보기를 중단하려면, Ctrl+C 를 누르자.

  3. 이 디플로이먼트가 성공적인지 확인한다. 다음을 검토하자.

    • 윈도우 노드에 파드당 두 컨테이너가 존재하는지 확인하려면, docker ps를 사용한다.
    • 리눅스 컨트롤 플레인 노드에서 나열된 두 파드가 존재하는지 확인하려면, kubectl get pods를 사용한다.
    • 네트워크를 통한 노드에서 파드로의 통신이 되는지 확인하려면, 리눅스 컨트롤 플레인 노드에서 curl을 파드 IP 주소의 80 포트로 실행하여 웹 서버 응답을 확인한다.
    • 파드 간 통신이 되는지 확인하려면, docker execkubectl exec를 이용해 파드 간에 핑(ping)한다(윈도우 노드가 2대 이상이라면, 서로 다른 노드에 있는 파드 간 통신도 확인할 수 있다).
    • 서비스에서 파드로의 통신이 되는지 확인하려면, 리눅스 컨트롤 플레인 노드와 독립 파드에서 curl을 가상 서비스 IP 주소(kubectl get services로 볼 수 있는)로 실행한다.
    • 서비스 검색(discovery)이 되는지 확인하려면, 쿠버네티스 기본 DNS 접미사와 서비스 이름으로 curl을 실행한다.
    • 인바운드 연결이 되는지 확인하려면, 클러스터 외부 장비나 리눅스 컨트롤 플레인 노드에서 NodePort로 curl을 실행한다.
    • 아웃바운드 연결이 되는지 확인하려면, kubectl exec를 이용해서 파드에서 외부 IP 주소로 curl을 실행한다.

가시성

워크로드에서 로그 캡쳐하기

로그는 가시성의 중요한 요소이다. 로그는 사용자가 워크로드의 운영측면을 파악할 수 있도록 하며 문제 해결의 핵심 요소이다. 윈도우 컨테이너, 그리고 윈도우 컨테이너 내의 워크로드는 리눅스 컨테이너와는 다르게 동작하기 때문에, 사용자가 로그를 수집하기 어려웠고 이로 인해 운영 가시성이 제한되어 왔다. 예를 들어 윈도우 워크로드는 일반적으로 ETW(Event Tracing for Windows)에 로그인하거나 애플리케이션 이벤트 로그에 항목을 푸시하도록 구성한다. Microsoft의 오픈 소스 도구인 LogMonitor는 윈도우 컨테이너 안에 구성된 로그 소스를 모니터링하는 권장하는 방법이다. LogMonitor는 이벤트 로그, ETW 공급자 그리고 사용자 정의 애플리케이션 로그 모니터링을 지원하고 kubectl logs <pod> 에 의한 사용을 위해 STDOUT으로 파이프한다.

LogMonitor GitHub 페이지의 지침에 따라 모든 컨테이너 바이너리와 설정 파일을 복사하고, LogMonitor가 로그를 STDOUT으로 푸시할 수 있도록 필요한 엔트리포인트를 추가한다.

설정 가능한 컨테이너 username 사용하기

쿠버네티스 v1.16 부터, 윈도우 컨테이너는 이미지 기본 값과는 다른 username으로 엔트리포인트와 프로세스를 실행하도록 설정할 수 있다. 이 방식은 리눅스 컨테이너에서 지원되는 방식과는 조금 차이가 있다. 여기에서 이에 대해 추가적으로 배울 수 있다.

그룹 매니지드 서비스 어카운트를 이용하여 워크로드 신원 관리하기

쿠버네티스 v1.14부터 윈도우 컨테이너 워크로드는 그룹 매니지드 서비스 어카운트(GMSA, Group Managed Service Account)를 이용하여 구성할 수 있다. 그룹 매니지드 서비스 어카운트는 액티브 디렉터리 어카운트의 특정한 종류로 자동 암호 관리 기능, 단순화된 서비스 주체 이름(SPN, simplified service principal name), 여러 서버의 다른 관리자에게 관리를 위임하는 기능을 제공한다. GMSA로 구성한 컨테이너는 GMSA로 구성된 신원을 들고 있는 동안 외부 액티브 디렉터리 도메인 리소스를 접근할 수 있다. 윈도우 컨테이너를 위한 GMSA를 이용하고 구성하는 방법은 여기에서 알아보자.

테인트(Taint)와 톨러레이션(Toleration)

오늘날 사용자는 리눅스와 윈도우 워크로드를 (동일한 OS를 실행하는) 적절한 노드에 할당되도록 하기 위해 테인트와 노드셀렉터(nodeSelector)의 조합을 이용해야 한다. 이것은 윈도우 사용자에게만 부담을 줄 것으로 보인다. 아래는 권장되는 방식의 개요인데, 이것의 주요 목표 중에 하나는 이 방식이 기존 리눅스 워크로드와 호환되어야 한다는 것이다.

특정 OS 워크로드를 적절한 컨테이너 호스트에서 처리하도록 보장하기

사용자는 테인트와 톨러레이션을 이용하여 윈도우 컨테이너가 적절한 호스트에서 스케줄링되기를 보장할 수 있다. 오늘날 모든 쿠버네티스 노드는 다음 기본 레이블을 가지고 있다.

  • kubernetes.io/os = [windows|linux]
  • kubernetes.io/arch = [amd64|arm64|...]

파드 사양에 노드 셀렉터를 "kubernetes.io/os": windows와 같이 지정하지 않았다면, 그 파드는 리눅스나 윈도우, 아무 호스트에나 스케줄링될 수 있다. 윈도우 컨테이너는 윈도우에서만 운영될 수 있고 리눅스 컨테이너는 리눅스에서만 운영될 수 있기 때문에 이는 문제를 일으킬 수 있다. 가장 좋은 방법은 노드 셀렉터를 사용하는 것이다.

그러나 많은 경우 사용자는 이미 존재하는 대량의 리눅스 컨테이너용 디플로이먼트를 가지고 있을 뿐만 아니라, 헬름(Helm) 차트 커뮤니티 같은 상용 구성의 에코시스템이나, 오퍼레이터(Operator) 같은 프로그래밍 방식의 파드 생성 사례가 있음을 알고 있다. 이런 상황에서는 노드 셀렉터를 추가하는 구성 변경을 망설일 수 있다. 이에 대한 대안은 테인트를 사용하는 것이다. Kubelet은 등록하는 동안 테인트를 설정할 수 있기 때문에, 윈도우에서만 운영할 때에 자동으로 테인트를 추가하기 쉽다.

예를 들면, --register-with-taints='os=windows:NoSchedule'

모든 윈도우 노드에 테인트를 추가하여 아무 것도 거기에 스케줄링하지 않게 될 것이다(존재하는 리눅스 파드를 포함하여). 윈도우 파드가 윈도우 노드에 스케줄링되도록 하려면, 윈도우 노드가 선택되도록 하기 위한 노드 셀렉터 및 적합하게 일치하는 톨러레이션이 모두 필요하다.

nodeSelector:
    kubernetes.io/os: windows
    node.kubernetes.io/windows-build: '10.0.17763'
tolerations:
    - key: "os"
      operator: "Equal"
      value: "windows"
      effect: "NoSchedule"

동일 클러스터에서 여러 윈도우 버전을 조작하는 방법

파드에서 사용하는 윈도우 서버 버전은 노드의 윈도우 서버 버전과 일치해야 한다. 만약 동일한 클러스터에서 여러 윈도우 서버 버전을 사용하려면, 추가로 노드 레이블과 nodeSelectors를 설정해야 한다.

쿠버네티스 1.17은 이것을 단순화하기 위해 새로운 레이블인 node.kubernetes.io/windows-build 를 자동으로 추가한다. 만약 이전 버전을 실행 중인 경우, 이 레이블을 윈도우 노드에 수동으로 추가하는 것을 권장한다.

이 레이블은 호환성을 위해 일치시켜야 하는 윈도우 메이저, 마이너 및 빌드 번호를 나타낸다. 각 윈도우 서버 버전에 대해 현재 사용하고 있는 빌드 번호는 다음과 같다.

제품 이름 빌드 번호
윈도우 서버 2019 10.0.17763
윈도우 서버 버전 1809 10.0.17763
윈도우 서버 버전 1903 10.0.18362

RuntimeClass로 단순화

런타임클래스(RuntimeClass)를 사용해서 테인트(taint)와 톨러레이션(toleration)을 사용하는 프로세스를 간소화 할 수 있다. 클러스터 관리자는 이 테인트와 톨러레이션을 캡슐화하는 데 사용되는 RuntimeClass 오브젝트를 생성할 수 있다.

  1. 이 파일을 runtimeClasses.yml 로 저장한다. 여기에는 윈도우 OS, 아키텍처 및 버전에 적합한 nodeSelector 가 포함되어 있다.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: windows-2019
handler: 'docker'
scheduling:
  nodeSelector:
    kubernetes.io/os: 'windows'
    kubernetes.io/arch: 'amd64'
    node.kubernetes.io/windows-build: '10.0.17763'
  tolerations:
  - effect: NoSchedule
    key: os
    operator: Equal
    value: "windows"
  1. 클러스터 관리자로 kubectl create -f runtimeClasses.yml 를 실행해서 사용한다.
  2. 파드 사양에 적합한 runtimeClassName: windows-2019 를 추가한다.

예시:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: iis-2019
  labels:
    app: iis-2019
spec:
  replicas: 1
  template:
    metadata:
      name: iis-2019
      labels:
        app: iis-2019
    spec:
      runtimeClassName: windows-2019
      containers:
      - name: iis
        image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        resources:
          limits:
            cpu: 1
            memory: 800Mi
          requests:
            cpu: .1
            memory: 300Mi
        ports:
          - containerPort: 80
 selector:
    matchLabels:
      app: iis-2019
---
apiVersion: v1
kind: Service
metadata:
  name: iis
spec:
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
  selector:
    app: iis-2019

2.3 - 모범 사례

2.3.1 - 대형 클러스터에 대한 고려 사항

클러스터는 컨트롤 플레인에서 관리하는 쿠버네티스 에이전트를 실행하는 노드(물리 또는 가상 머신)의 집합이다. 쿠버네티스 v1.23는 노드 5,000개까지의 클러스터를 지원한다. 보다 정확하게는, 쿠버네티스는 다음 기준을 모두 만족하는 설정을 수용하도록 설계되었다.

  • 노드 당 파드 110 개 이하
  • 노드 5,000개 이하
  • 전체 파드 150,000개 이하
  • 전체 컨테이너 300,000개 이하

노드를 추가하거나 제거하여 클러스터를 확장할 수 있다. 이를 수행하는 방법은 클러스터 배포 방법에 따라 다르다.

클라우드 프로바이더 리소스 쿼터

여러 노드를 가지는 클러스터를 만들 때, 클라우드 프로바이더 쿼터 이슈를 피하기 위해 고려할 점은 다음과 같다.

  • 다음과 같은 클라우드 리소스에 대한 쿼터 증가를 요청한다.
    • 컴퓨터 인스턴스
    • CPU
    • 스토리지 볼륨
    • 사용 중인 IP 주소
    • 패킷 필터링 규칙 세트
    • 로드밸런서 개수
    • 로그 스트림
  • 일부 클라우드 프로바이더는 새로운 인스턴스 생성 속도에 상한이 있어, 클러스터 확장 작업 간 새로운 노드를 일괄적으로 배치하고, 배치 간에 일시 중지한다.

컨트롤 플레인 컴포넌트

대규모 클러스터의 경우, 충분한 컴퓨트 및 기타 리소스가 있는 컨트롤 플레인이 필요하다.

일반적으로 장애 영역 당 하나 또는 두 개의 컨트롤 플레인 인스턴스를 실행하고, 해당 인스턴스를 수직으로(vertically) 먼저 확장한 다음 (수직) 규모로 하락하는 지점에 도달한 후 수평으로(horizontally) 확장한다.

내결함성을 제공하려면 장애 영역 당 하나 이상의 인스턴스를 실행해야 한다. 쿠버네티스 노드는 동일한 장애 영역에 있는 컨트롤 플레인 엔드포인트로 트래픽을 자동으로 조정하지 않는다. 그러나, 클라우드 프로바이더는 이를 수행하기 위한 자체 메커니즘을 가지고 있을 수 있다.

예를 들어, 관리형 로드 밸런서를 사용하여 장애 영역 A 의 kubelet 및 파드에서 시작되는 트래픽을 전송하도록 로드 밸런서를 구성하고, 해당 트래픽을 A 영역에 있는 컨트롤 플레인 호스트로만 전달한다. 단일 컨트롤 플레인 호스트 또는 엔드포인트 장애 영역 A 이 오프라인이 되면, 영역 A 의 노드에 대한 모든 컨트롤 플레인 트래픽이 이제 영역간에 전송되고 있음을 의미한다. 각 영역에서 여러 컨트롤 플레인 호스트를 실행하면 가용성이 낮아진다.

etcd 저장소

큰 클러스터의 성능 향상을 위해, 사용자는 이벤트 오브젝트를 각각의 전용 etcd 인스턴스에 저장한다.

클러스터 생성시의 부가 스트립트이다. 클러스터 생성 시에 (사용자 도구를 사용하여) 다음을 수행할 수 있다.

  • 추가 etcd 인스턴스 시작 및 설정
  • 이벤트를 저장하기 위한 API server 설정

쿠버네티스를 위한 etcd 클러스터 운영하기kubeadm을 이용하여 고가용성 etcd 생성하기에서 큰 클러스터를 위한 etcd를 설정하고 관리하는 방법에 대한 상세 사항을 확인한다.

애드온 리소스

쿠버네티스 리소스 제한은 파드와 컨테이너가 다른 컴포넌트에 영향을 줄 수 있는 메모리 누수 및 기타 방식의 영향을 최소화하는 데 도움이 된다. 이러한 리소스 제한은 애플리케이션 워크로드에 적용될 수 있는 것처럼 애드온 리소스에도 적용될 수 있다.

예를 들어, 로깅 컴포넌트에 대한 CPU 및 메모리 제한을 설정할 수 있다.

  ...
  containers:
  - name: fluentd-cloud-logging
    image: fluent/fluentd-kubernetes-daemonset:v1
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

애드온의 기본 제한은 일반적으로 중소형 쿠버네티스 클러스터에서 각 애드온을 실행한 경험에서 수집된 데이터를 기반으로 한다. 대규모 클러스터에서 실행할 때, 애드온은 종종 기본 제한보다 많은 리소스를 소비한다. 이러한 값을 조정하지 않고 대규모 클러스터를 배포하면, 애드온이 메모리 제한에 계속 도달하기 때문에 지속적으로 종료될 수 있다. 또는, 애드온이 실행될 수 있지만 CPU 시간 슬라이스 제한으로 인해 성능이 저하된다.

클러스터 애드온 리소스 문제가 발생하지 않도록, 노드가 많은 클러스터를 만들 때, 다음 사항을 고려한다.

  • 일부 애드온은 수직으로 확장된다. 클러스터 또는 전체 장애 영역을 제공하는 애드온 레플리카가 하나 있다. 이러한 애드온의 경우, 클러스터를 확장할 때 요청과 제한을 늘린다.
  • 많은 애드온은 수평으로 확장된다. 더 많은 파드를 실행하여 용량을 추가하지만, 매우 큰 클러스터에서는 CPU 또는 메모리 제한을 약간 높여야 할 수도 있다. VerticalPodAutoscaler는 recommender 모드에서 실행되어 요청 및 제한에 대한 제안 수치를 제공할 수 있다.
  • 일부 애드온은 데몬셋(DaemonSet)에 의해 제어되는 노드 당 하나의 복사본으로 실행된다(예: 노드 수준 로그 수집기). 수평 확장 애드온의 경우와 유사하게, CPU 또는 메모리 제한을 약간 높여야 할 수도 있다.

다음 내용

VerticalPodAutoscaler 는 리소스 요청 및 파드 제한을 관리하는 데 도움이 되도록 클러스터에 배포할 수 있는 사용자 정의 리소스이다. 클러스터에 중요한 애드온을 포함하여 클러스터 컴포넌트를 확장하는 방법에 대한 자세한 내용은 Vertical Pod Autoscaler를 방문하여 배워본다.

클러스터 오토스케일러는 여러 클라우드 프로바이더와 통합되어 클러스터의 리소스 요구 수준에 맞는 노드 수를 실행할 수 있도록 도와준다.

addon resizer는 클러스터 스케일이 변경될 때 자동으로 애드온 크기를 조정할 수 있도록 도와준다.

2.3.2 - 여러 영역에서 실행

이 페이지에서는 여러 영역에서 쿠버네티스를 실행하는 방법을 설명한다.

배경

쿠버네티스는 단일 쿠버네티스 클러스터가 여러 장애 영역에서 실행될 수 있도록 설계되었다. 일반적으로 이러한 영역은 지역(region) 이라는 논리적 그룹 내에 적합하다. 주요 클라우드 제공자는 지역을 일관된 기능 집합을 제공하는 장애 영역 집합(가용성 영역 이라고도 함)으로 정의한다. 지역 내에서 각 영역은 동일한 API 및 서비스를 제공한다.

일반적인 클라우드 아키텍처는 한 영역의 장애가 다른 영역의 서비스도 손상시킬 가능성을 최소화하는 것을 목표로 한다.

컨트롤 플레인 동작

모든 컨트롤 플레인 컴포넌트는 컴포넌트별로 복제되는 교환 가능한 리소스 풀로 실행을 지원한다.

클러스터 컨트롤 플레인을 배포할 때, 여러 장애 영역에 컨트롤 플레인 컴포넌트의 복제본을 배치한다. 가용성이 중요한 문제인 경우, 3개 이상의 장애 영역을 선택하고 각 개별 컨트롤 플레인 컴포넌트(API 서버, 스케줄러, etcd, 클러스터 컨트롤러 관리자)를 3개 이상의 장애 영역에 복제한다. 클라우드 컨트롤러 관리자를 실행 중인 경우 선택한 모든 장애 영역에 걸쳐 이를 복제해야 한다.

노드 동작

쿠버네티스는 클러스터의 여러 노드에 걸쳐 워크로드 리소스(예: 디플로이먼트(Deployment) 또는 스테이트풀셋(StatefulSet))에 대한 파드를 자동으로 분배한다. 이러한 분배는 실패에 대한 영향을 줄이는 데 도움이 된다.

노드가 시작되면, 각 노드의 kubelet이 쿠버네티스 API에서 특정 kubelet을 나타내는 노드 오브젝트에 레이블을 자동으로 추가한다. 이러한 레이블에는 영역 정보가 포함될 수 있다.

클러스터가 여러 영역 또는 지역에 걸쳐있는 경우, 파드 토폴로지 분배 제약 조건과 함께 노드 레이블을 사용하여 파드가 장애 도메인(지역, 영역, 특정 노드) 간 클러스터에 분산되는 방식을 제어할 수 있다. 이러한 힌트를 통해 스케줄러는 더 나은 예상 가용성을 위해 파드를 배치할 수 있으므로, 상관 관계가 있는 오류가 전체 워크로드에 영향을 미칠 위험을 줄일 수 있다.

예를 들어, 가능할 때마다 스테이트풀셋의 3개 복제본이 모두 서로 다른 영역에서 실행되도록 제약 조건을 설정할 수 있다. 각 워크로드에 사용 중인 가용 영역을 명시적으로 정의하지 않고 이를 선언적으로 정의할 수 있다.

여러 영역에 노드 분배

쿠버네티스의 코어는 사용자를 위해 노드를 생성하지 않는다. 사용자가 직접 수행하거나, 클러스터 API와 같은 도구를 사용하여 사용자 대신 노드를 관리해야 한다.

클러스터 API와 같은 도구를 사용하면 여러 장애 도메인에서 클러스터의 워커 노드로 실행할 머신 집합과 전체 영역 서비스 중단 시 클러스터를 자동으로 복구하는 규칙을 정의할 수 있다.

파드에 대한 수동 영역 할당

생성한 파드와 디플로이먼트, 스테이트풀셋, 잡(Job)과 같은 워크로드 리소스의 파드 템플릿에 노드 셀렉터 제약 조건을 적용할 수 있다.

영역에 대한 스토리지 접근

퍼시스턴트 볼륨이 생성되면, PersistentVolumeLabel 어드미션 컨트롤러는 특정 영역에 연결된 모든 퍼시스턴트볼륨(PersistentVolume)에 영역 레이블을 자동으로 추가한다. 그런 다음 스케줄러NoVolumeZoneConflict 프레디케이트(predicate)를 통해 주어진 퍼시스턴트볼륨을 요구하는 파드가 해당 볼륨과 동일한 영역에만 배치되도록 한다.

해당 클래스의 스토리지가 사용할 수 있는 장애 도메인(영역)을 지정하는 퍼시스턴트볼륨클레임(PersistentVolumeClaims)에 대한 스토리지클래스(StorageClass)를 지정할 수 있다. 장애 도메인 또는 영역을 인식하는 스토리지클래스 구성에 대한 자세한 내용은 허용된 토폴로지를 참고한다.

네트워킹

쿠버네티스가 스스로 영역-인지(zone-aware) 네트워킹을 포함하지는 않는다. 네트워크 플러그인을 사용하여 클러스터 네트워킹을 구성할 수 있으며, 해당 네트워크 솔루션에는 영역별 요소가 있을 수 있다. 예를 들어, 클라우드 제공자가 type=LoadBalancer 를 사용하여 서비스를 지원하는 경우, 로드 밸런서는 지정된 연결을 처리하는 로드 밸런서 요소와 동일한 영역에서 실행 중인 파드로만 트래픽을 보낼 수 있다. 자세한 내용은 클라우드 제공자의 문서를 확인한다.

사용자 정의 또는 온-프레미스 배포의 경우, 비슷한 고려 사항이 적용된다. 다른 장애 영역 처리를 포함한 서비스인그레스(Ingress) 동작은 클러스터가 설정된 방식에 명확히 의존한다.

장애 복구

클러스터를 설정할 때, 한 지역의 모든 장애 영역이 동시에 오프라인 상태가 되는 경우 설정에서 서비스를 복원할 수 있는지 여부와 방법을 고려해야 할 수도 있다. 예를 들어, 영역에서 파드를 실행할 수 있는 노드가 적어도 하나 이상 있어야 하는가? 클러스터에 중요한 복구 작업이 클러스터에 적어도 하나 이상의 정상 노드에 의존하지 않는지 확인한다. 예를 들어, 모든 노드가 비정상인 경우, 하나 이상의 노드를 서비스할 수 있을 만큼 복구를 완료할 수 있도록 특별한 톨러레이션(toleration)으로 복구 작업을 실행해야 할 수 있다.

쿠버네티스는 이 문제에 대한 답을 제공하지 않는다. 그러나, 고려해야 할 사항이다.

다음 내용

스케줄러가 구성된 제약 조건을 준수하면서, 클러스터에 파드를 배치하는 방법을 알아보려면, 스케줄링과 축출(eviction)을 참고한다.

2.3.3 - 노드 구성 검증하기

노드 적합성 테스트

노드 적합성 테스트 는 노드의 시스템 검증과 기능 테스트를 제공하기 위해 컨테이너화된 테스트 프레임워크이다. 테스트는 노드가 쿠버네티스를 위한 최소 요구조건을 만족하는지를 검증한다. 그리고 테스트를 통과한 노드는 쿠버네티스 클러스터에 참 여할 자격이 주어진다.

노드 필수 구성 요소

노드 적합성 테스트를 실행하기 위해서는, 해당 노드는 표준 쿠버네티스 노드로서 동일한 전제조건을 만족해야 한다. 노드는 최소한 아래 데몬들이 설치되어 있어야 한다.

  • 컨테이너 런타임 (Docker)
  • Kubelet

노드 적합성 테스트 실행

노드 적합성 테스트는 다음 순서로 진행된다.

  1. kubelet에 대한 --kubeconfig 옵션의 값을 계산한다. 예를 들면, 다음과 같다. --kubeconfig = / var / lib / kubelet / config.yaml. 테스트 프레임워크는 kubelet을 테스트하기 위해 로컬 컨트롤 플레인을 시작하기 때문에, http://localhost:8080 을 API 서버의 URL로 사용한다. 사용할 수 있는 kubelet 커맨드 라인 파라미터가 몇 개 있다.
  • --pod-cidr: kubenet을 사용 중이라면, 임의의 CIDR을 Kubelet에 지정해주어야 한다. 예) --pod-cidr=10.180.0.0/24.
  • --cloud-provider: --cloud-provider=gce를 사용 중이라면, 테스트 실행 시에는 제거해야 한다.
  1. 다음 커맨드로 노드 적합성 테스트를 실행한다.
# $CONFIG_DIR는 Kublet의 파드 매니페스트 경로이다.
# $LOG_DIR는 테스트 출력 경로이다.
sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  k8s.gcr.io/node-test:0.2

다른 아키텍처에서 노드 적합성 테스트 실행

쿠버네티스는 다른 아키텍쳐용 노드 적합성 테스트 Docker 이미지도 제공한다.

Arch Image
amd64 node-test-amd64
arm node-test-arm
arm64 node-test-arm64

선택된 테스트 실행

특정 테스트만 실행하기 위해서는 환경 변수 FOCUS에 테스트하고자 하는 테스트를 정규식으로 지정한다.

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e FOCUS=MirrorPod \ # MirrorPod 테스트만 실행
  k8s.gcr.io/node-test:0.2

특정 테스트를 건너뛰기 위해서는, 환경 변수 SKIP에 건너뛰고자 하는 테스트를 정규식으로 지정한다.

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e SKIP=MirrorPod \ # MirrorPod 테스트만 건너뛰고 모든 적합성 테스트를 실행한다
  k8s.gcr.io/node-test:0.2

노드 적합성 테스트는 노드 e2e 테스트를 컨테이너화한 버전이다. 기본적으로, 모든 적합성 테스트를 실행한다.

이론적으로, 컨테이너와 필요한 볼륨을 적절히 설정했다면 어떤 노드 e2e 테스트도 수행할 수 있다. 하지만, 적합성 테스트가 아닌 테스트들은 훨씬 복잡한 설정이 필요하기 때문에 적합성 테스트만 실행하기를 강하게 추천한다.

주의 사항

  • 테스트 후, 노드 적합성 테스트 이미지 및 기능 테스트에 사용된 이미지들을 포함하여 몇 개의 Docker 이미지들이 노드에 남는다.
  • 테스트 후, 노드에 죽은 컨테이너가 남는다. 기능 테스트 도중에 생성된 컨테이너들이다.

2.3.4 - PKI 인증서 및 요구 사항

쿠버네티스는 TLS를 통한 인증을 위해서 PKI 인증서가 필요하다. 만약 kubeadm으로 쿠버네티스를 설치한다면, 클러스터에 필요한 인증서는 자동으로 생성된다. 또한 더 안전하게 자신이 소유한 인증서를 생성할 수 있다. 이를 테면, 개인키를 API 서버에 저장하지 않으므로 더 안전하게 보관할 수 있다. 이 페이지는 클러스터가 필요로 하는 인증서에 대해서 설명한다.

클러스터에서 인증서가 이용되는 방식

쿠버네티스는 다음 작업에서 PKI를 필요로 한다.

  • kubelet에서 API 서버 인증서를 인증시 사용하는 클라이언트 인증서
  • API 서버 엔드포인트를 위한 서버 인증서
  • API 서버에 클러스터 관리자 인증을 위한 클라이언트 인증서
  • API 서버에서 kubelet과 통신을 위한 클라이언트 인증서
  • API 서버에서 etcd 간의 통신을 위한 클라이언트 인증서
  • 컨트롤러 매니저와 API 서버 간의 통신을 위한 클라이언트 인증서/kubeconfig
  • 스케줄러와 API 서버간 통신을 위한 클라이언트 인증서/kubeconfig
  • front-proxy를 위한 클라이언트와 서버 인증서

etcd 역시 클라이언트와 피어 간에 상호 TLS 인증을 구현한다.

인증서를 저장하는 위치

만약 쿠버네티스를 kubeadm으로 설치했다면, 대부분의 인증서는 /etc/kubernetes/pki에 저장된다. 이 문서에 언급된 모든 파일 경로는 그 디렉터리에 상대적이나, kubeadm이 /etc/kubernetes에 저장하는 사용자 어카운트 인증서는 예외이다.

인증서 수동 설정

필요한 인증서를 kubeadm으로 생성하기 싫다면, 단일 루트 CA를 이용하거나 모든 인증서를 제공하여 생성할 수 있다. 소유한 인증기관을 이용해서 생성하는 방법에 대해서는 인증서를 살펴본다. 인증서를 관리하는 방법에 대해서는 kubeadm을 사용한 인증서 관리를 살펴본다.

단일 루트 CA

관리자에 의해 제어되는 단일 루트 CA를 만들 수 있다. 이 루트 CA는 여러 중간 CA를 생성할 수 있고, 모든 추가 생성에 관해서도 쿠버네티스 자체에 위임할 수 있다.

필요 CA:

경로 기본 CN 설명
ca.crt,key kubernetes-ca 쿠버네티스 일반 CA
etcd/ca.crt,key etcd-ca 모든 etcd 관련 기능을 위해서
front-proxy-ca.crt,key kubernetes-front-proxy-ca front-end proxy 위해서

위의 CA외에도, 서비스 계정 관리를 위한 공개/개인 키 쌍인 sa.keysa.pub 을 얻는 것이 필요하다. 다음은 이전 표에 나온 CA 키와 인증서 파일을 보여준다.

/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-ca.key

모든 인증서

이런 개인키를 API 서버에 복사하기 원치 않는다면, 모든 인증서를 스스로 생성할 수 있다.

필요한 인증서:

기본 CN 부모 CA O (주체에서) 종류 호스트 (SAN)
kube-etcd etcd-ca server, client localhost, 127.0.0.1
kube-etcd-peer etcd-ca server, client <hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-healthcheck-client etcd-ca client
kube-apiserver-etcd-client etcd-ca system:masters client
kube-apiserver kubernetes-ca server <hostname>, <Host_IP>, <advertise_IP>, [1]
kube-apiserver-kubelet-client kubernetes-ca system:masters client
front-proxy-client kubernetes-front-proxy-ca client

[1]: 클러스터에 접속한 다른 IP 또는 DNS 이름(kubeadm이 사용하는 로드 밸런서 안정 IP 또는 DNS 이름, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster, kubernetes.default.svc.cluster.local)

kind는 하나 이상의 x509 키 사용 종류를 가진다.

종류 키 사용
server digital signature, key encipherment, server auth
client digital signature, key encipherment, client auth

인증서 파일 경로

인증서는 권고하는 파일 경로에 존재해야 한다(kubeadm에서 사용되는 것처럼). 경로는 위치에 관계없이 주어진 파라미터를 사용하여 지정해야 한다.

기본 CN 권고되는 키 파일 경로 권고하는 인증서 파일 경로 명령어 키 파라미터 인증서 파라미터
etcd-ca etcd/ca.key etcd/ca.crt kube-apiserver --etcd-cafile
kube-apiserver-etcd-client apiserver-etcd-client.key apiserver-etcd-client.crt kube-apiserver --etcd-keyfile --etcd-certfile
kubernetes-ca ca.key ca.crt kube-apiserver --client-ca-file
kubernetes-ca ca.key ca.crt kube-controller-manager --cluster-signing-key-file --client-ca-file, --root-ca-file, --cluster-signing-cert-file
kube-apiserver apiserver.key apiserver.crt kube-apiserver --tls-private-key-file --tls-cert-file
kube-apiserver-kubelet-client apiserver-kubelet-client.key apiserver-kubelet-client.crt kube-apiserver --kubelet-client-key --kubelet-client-certificate
front-proxy-ca front-proxy-ca.key front-proxy-ca.crt kube-apiserver --requestheader-client-ca-file
front-proxy-ca front-proxy-ca.key front-proxy-ca.crt kube-controller-manager --requestheader-client-ca-file
front-proxy-client front-proxy-client.key front-proxy-client.crt kube-apiserver --proxy-client-key-file --proxy-client-cert-file
etcd-ca etcd/ca.key etcd/ca.crt etcd --trusted-ca-file, --peer-trusted-ca-file
kube-etcd etcd/server.key etcd/server.crt etcd --key-file --cert-file
kube-etcd-peer etcd/peer.key etcd/peer.crt etcd --peer-key-file --peer-cert-file
etcd-ca etcd/ca.crt etcdctl --cacert
kube-etcd-healthcheck-client etcd/healthcheck-client.key etcd/healthcheck-client.crt etcdctl --key --cert

서비스 계정 키 쌍에도 동일한 고려 사항이 적용된다.

개인키 경로 공개 키 경로 명령어 파라미터
sa.key kube-controller-manager --service-account-private-key-file
sa.pub kube-apiserver --service-account-key-file

다음은 키와 인증서를 모두 생성할 때에 제공해야 하는 이전 표에 있는 파일의 경로를 보여준다.

/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/apiserver-etcd-client.key
/etc/kubernetes/pki/apiserver-etcd-client.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/apiserver.key
/etc/kubernetes/pki/apiserver.crt
/etc/kubernetes/pki/apiserver-kubelet-client.key
/etc/kubernetes/pki/apiserver-kubelet-client.crt
/etc/kubernetes/pki/front-proxy-ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-client.key
/etc/kubernetes/pki/front-proxy-client.crt
/etc/kubernetes/pki/etcd/server.key
/etc/kubernetes/pki/etcd/server.crt
/etc/kubernetes/pki/etcd/peer.key
/etc/kubernetes/pki/etcd/peer.crt
/etc/kubernetes/pki/etcd/healthcheck-client.key
/etc/kubernetes/pki/etcd/healthcheck-client.crt
/etc/kubernetes/pki/sa.key
/etc/kubernetes/pki/sa.pub

각 사용자 계정을 위한 인증서 설정하기

반드시 이런 관리자 계정과 서비스 계정을 설정해야 한다.

파일명 자격증명 이름 기본 CN O (주체에서)
admin.conf default-admin kubernetes-admin system:masters
kubelet.conf default-auth system:node:<nodeName> (note를 보자) system:nodes
controller-manager.conf default-controller-manager system:kube-controller-manager
scheduler.conf default-scheduler system:kube-scheduler
  1. 각 환경 설정에 대해 주어진 CN과 O를 이용하여 x509 인증서와 키쌍을 생성한다.

  2. 각 환경 설정에 대해 다음과 같이 kubectl를 실행한다.

KUBECONFIG=<filename> kubectl config set-cluster default-cluster --server=https://<host ip>:6443 --certificate-authority <path-to-kubernetes-ca> --embed-certs
KUBECONFIG=<filename> kubectl config set-credentials <credential-name> --client-key <path-to-key>.pem --client-certificate <path-to-cert>.pem --embed-certs
KUBECONFIG=<filename> kubectl config set-context default-system --cluster default-cluster --user <credential-name>
KUBECONFIG=<filename> kubectl config use-context default-system

이 파일들은 다음과 같이 사용된다.

파일명 명령어 설명
admin.conf kubectl 클러스터 관리자를 설정한다.
kubelet.conf kubelet 클러스터 각 노드를 위해 필요하다.
controller-manager.conf kube-controller-manager 반드시 매니페스트를 manifests/kube-controller-manager.yaml에 추가해야 한다.
scheduler.conf kube-scheduler 반드시 매니페스트를 manifests/kube-scheduler.yaml에 추가해야 한다.

다음의 파일은 이전 표에 나열된 파일의 전체 경로를 보여준다.

/etc/kubernetes/admin.conf
/etc/kubernetes/kubelet.conf
/etc/kubernetes/controller-manager.conf
/etc/kubernetes/scheduler.conf

3 - 개념

개념 섹션을 통해 쿠버네티스 시스템을 구성하는 요소와 클러스터를 표현하는데 사용되는 추상 개념에 대해 배우고 쿠버네티스가 작동하는 방식에 대해 보다 깊이 이해할 수 있다.

3.1 - 개요

쿠버네티스와 그 컴포넌트에 대한 하이-레벨(high-level) 개요를 제공한다.

3.1.1 - 쿠버네티스란 무엇인가?

쿠버네티스는 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식할 수 있고, 확장 가능한 오픈소스 플랫폼으로, 선언적 구성과 자동화를 모두 지원한다. 쿠버네티스는 크고 빠르게 성장하는 생태계를 가지고 있다. 쿠버네티스 서비스, 지원 그리고 도구들은 광범위하게 제공된다.

이 페이지에서는 쿠버네티스 개요를 설명한다.

쿠버네티스는 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식성이 있고, 확장가능한 오픈소스 플랫폼이다. 쿠버네티스는 선언적 구성과 자동화를 모두 용이하게 해준다. 쿠버네티스는 크고, 빠르게 성장하는 생태계를 가지고 있다. 쿠버네티스 서비스, 기술 지원 및 도구는 어디서나 쉽게 이용할 수 있다.

쿠버네티스란 명칭은 키잡이(helmsman)나 파일럿을 뜻하는 그리스어에서 유래했다. K8s라는 표기는 "K"와 "s"와 그 사이에 있는 8글자를 나타내는 약식 표기이다. 구글이 2014년에 쿠버네티스 프로젝트를 오픈소스화했다. 쿠버네티스는 프로덕션 워크로드를 대규모로 운영하는 15년 이상의 구글 경험과 커뮤니티의 최고의 아이디어와 적용 사례가 결합되어 있다.

여정 돌아보기

시간이 지나면서 쿠버네티스가 왜 유용하게 되었는지 살펴보자.

배포 혁명

전통적인 배포 시대: 초기 조직은 애플리케이션을 물리 서버에서 실행했었다. 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 정의할 방법이 없었기에, 리소스 할당의 문제가 발생했다. 예를 들어 물리 서버 하나에서 여러 애플리케이션을 실행하면, 리소스 전부를 차지하는 애플리케이션 인스턴스가 있을 수 있고, 결과적으로는 다른 애플리케이션의 성능이 저하될 수 있었다. 이에 대한 해결책은 서로 다른 여러 물리 서버에서 각 애플리케이션을 실행하는 것이 있다. 그러나 이는 리소스가 충분히 활용되지 않는다는 점에서 확장 가능하지 않았으므로, 물리 서버를 많이 유지하기 위해서 조직에게 많은 비용이 들었다.

가상화된 배포 시대: 그 해결책으로 가상화가 도입되었다. 이는 단일 물리 서버의 CPU에서 여러 가상 시스템 (VM)을 실행할 수 있게 한다. 가상화를 사용하면 VM간에 애플리케이션을 격리하고 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스 할 수 없으므로, 일정 수준의 보안성을 제공할 수 있다.

가상화를 사용하면 물리 서버에서 리소스를 보다 효율적으로 활용할 수 있으며, 쉽게 애플리케이션을 추가하거나 업데이트할 수 있고 하드웨어 비용을 절감할 수 있어 더 나은 확장성을 제공한다. 가상화를 통해 일련의 물리 리소스를 폐기 가능한(disposable) 가상 머신으로 구성된 클러스터로 만들 수 있다.

각 VM은 가상화된 하드웨어 상에서 자체 운영체제를 포함한 모든 구성 요소를 실행하는 하나의 완전한 머신이다.

컨테이너 개발 시대: 컨테이너는 VM과 유사하지만 격리 속성을 완화하여 애플리케이션 간에 운영체제(OS)를 공유한다. 그러므로 컨테이너는 가볍다고 여겨진다. VM과 마찬가지로 컨테이너에는 자체 파일 시스템, CPU 점유율, 메모리, 프로세스 공간 등이 있다. 기본 인프라와의 종속성을 끊었기 때문에, 클라우드나 OS 배포본에 모두 이식할 수 있다.

컨테이너는 다음과 같은 추가적인 혜택을 제공하기 때문에 인기가 있다.

  • 기민한 애플리케이션 생성과 배포: VM 이미지를 사용하는 것에 비해 컨테이너 이미지 생성이 보다 쉽고 효율적임.
  • 지속적인 개발, 통합 및 배포: 안정적이고 주기적으로 컨테이너 이미지를 빌드해서 배포할 수 있고 (이미지의 불변성 덕에) 빠르고 효율적으로 롤백할 수 있다.
  • 개발과 운영의 관심사 분리: 배포 시점이 아닌 빌드/릴리스 시점에 애플리케이션 컨테이너 이미지를 만들기 때문에, 애플리케이션이 인프라스트럭처에서 분리된다.
  • 가시성(observability): OS 수준의 정보와 메트릭에 머무르지 않고, 애플리케이션의 헬스와 그 밖의 시그널을 볼 수 있다.
  • 개발, 테스팅 및 운영 환경에 걸친 일관성: 랩탑에서도 클라우드에서와 동일하게 구동된다.
  • 클라우드 및 OS 배포판 간 이식성: Ubuntu, RHEL, CoreOS, 온-프레미스, 주요 퍼블릭 클라우드와 어디에서든 구동된다.
  • 애플리케이션 중심 관리: 가상 하드웨어 상에서 OS를 실행하는 수준에서 논리적인 리소스를 사용하는 OS 상에서 애플리케이션을 실행하는 수준으로 추상화 수준이 높아진다.
  • 느슨하게 커플되고, 분산되고, 유연하며, 자유로운 마이크로서비스: 애플리케이션은 단일 목적의 머신에서 모놀리식 스택으로 구동되지 않고 보다 작고 독립적인 단위로 쪼개져서 동적으로 배포되고 관리될 수 있다.
  • 리소스 격리: 애플리케이션 성능을 예측할 수 있다.
  • 자원 사용량: 리소스 사용량: 고효율 고집적.

쿠버네티스가 왜 필요하고 무엇을 할 수 있나

컨테이너는 애플리케이션을 포장하고 실행하는 좋은 방법이다. 프로덕션 환경에서는 애플리케이션을 실행하는 컨테이너를 관리하고 가동 중지 시간이 없는지 확인해야 한다. 예를 들어 컨테이너가 다운되면 다른 컨테이너를 다시 시작해야 한다. 이 문제를 시스템에 의해 처리한다면 더 쉽지 않을까?

그것이 쿠버네티스가 필요한 이유이다! 쿠버네티스는 분산 시스템을 탄력적으로 실행하기 위한 프레임 워크를 제공한다. 애플리케이션의 확장과 장애 조치를 처리하고, 배포 패턴 등을 제공한다. 예를 들어, 쿠버네티스는 시스템의 카나리아 배포를 쉽게 관리 할 수 있다.

쿠버네티스는 다음을 제공한다.

  • 서비스 디스커버리와 로드 밸런싱 쿠버네티스는 DNS 이름을 사용하거나 자체 IP 주소를 사용하여 컨테이너를 노출할 수 있다. 컨테이너에 대한 트래픽이 많으면, 쿠버네티스는 네트워크 트래픽을 로드밸런싱하고 배포하여 배포가 안정적으로 이루어질 수 있다.
  • 스토리지 오케스트레이션 쿠버네티스를 사용하면 로컬 저장소, 공용 클라우드 공급자 등과 같이 원하는 저장소 시스템을 자동으로 탑재 할 수 있다.
  • 자동화된 롤아웃과 롤백 쿠버네티스를 사용하여 배포된 컨테이너의 원하는 상태를 서술할 수 있으며 현재 상태를 원하는 상태로 설정한 속도에 따라 변경할 수 있다. 예를 들어 쿠버네티스를 자동화해서 배포용 새 컨테이너를 만들고, 기존 컨테이너를 제거하고, 모든 리소스를 새 컨테이너에 적용할 수 있다.
  • 자동화된 빈 패킹(bin packing) 컨테이너화된 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공한다. 각 컨테이너가 필요로 하는 CPU와 메모리(RAM)를 쿠버네티스에게 지시한다. 쿠버네티스는 컨테이너를 노드에 맞추어서 리소스를 가장 잘 사용할 수 있도록 해준다.
  • 자동화된 복구(self-healing) 쿠버네티스는 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며, '사용자 정의 상태 검사'에 응답하지 않는 컨테이너를 죽이고, 서비스 준비가 끝날 때까지 그러한 과정을 클라이언트에 보여주지 않는다.
  • 시크릿과 구성 관리 쿠버네티스를 사용하면 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리 할 수 있다. 컨테이너 이미지를 재구성하지 않고 스택 구성에 시크릿을 노출하지 않고도 시크릿 및 애플리케이션 구성을 배포 및 업데이트 할 수 있다.

쿠버네티스가 아닌 것

쿠버네티스는 전통적인, 모든 것이 포함된 Platform as a Service(PaaS)가 아니다. 쿠버네티스는 하드웨어 수준보다는 컨테이너 수준에서 운영되기 때문에, PaaS가 일반적으로 제공하는 배포, 스케일링, 로드 밸런싱과 같은 기능을 제공하며, 사용자가 로깅, 모니터링 및 알림 솔루션을 통합할 수 있다. 하지만, 쿠버네티스는 모놀리식(monolithic)이 아니어서, 이런 기본 솔루션이 선택적이며 추가나 제거가 용이하다. 쿠버네티스는 개발자 플랫폼을 만드는 구성 요소를 제공하지만, 필요한 경우 사용자의 선택권과 유연성을 지켜준다.

쿠버네티스는:

  • 지원하는 애플리케이션의 유형을 제약하지 않는다. 쿠버네티스는 상태 유지가 필요 없는(stateless) 워크로드, 상태 유지가 필요한(stateful) 워크로드, 데이터 처리를 위한 워크로드를 포함해서 극단적으로 다양한 워크로드를 지원하는 것을 목표로 한다. 애플리케이션이 컨테이너에서 구동될 수 있다면, 쿠버네티스에서도 잘 동작할 것이다.
  • 소스 코드를 배포하지 않으며 애플리케이션을 빌드하지 않는다. 지속적인 통합과 전달과 배포, 곧 CI/CD 워크플로우는 조직 문화와 취향에 따를 뿐만 아니라 기술적인 요구사항으로 결정된다.
  • 애플리케이션 레벨의 서비스를 제공하지 않는다. 애플리케이션 레벨의 서비스에는 미들웨어(예, 메시지 버스), 데이터 처리 프레임워크(예, Spark), 데이터베이스(예, MySQL), 캐시 또는 클러스터 스토리지 시스템(예, Ceph) 등이 있다. 이런 컴포넌트는 쿠버네티스 상에서 구동될 수 있고, 쿠버네티스 상에서 구동 중인 애플리케이션이 Open Service Broker 와 같은 이식 가능한 메커니즘을 통해 접근할 수도 있다.
  • 로깅, 모니터링 또는 경보 솔루션을 포함하지 않는다. 개념 증명을 위한 일부 통합이나, 메트릭을 수집하고 노출하는 메커니즘을 제공한다.
  • 기본 설정 언어/시스템(예, Jsonnet)을 제공하거나 요구하지 않는다. 선언적 명세의 임의적인 형식을 목적으로 하는 선언적 API를 제공한다.
  • 포괄적인 머신 설정, 유지보수, 관리, 자동 복구 시스템을 제공하거나 채택하지 않는다.
  • 추가로, 쿠버네티스는 단순한 오케스트레이션 시스템이 아니다. 사실, 쿠버네티스는 오케스트레이션의 필요성을 없애준다. 오케스트레이션의 기술적인 정의는 A를 먼저 한 다음, B를 하고, C를 하는 것과 같이 정의된 워크플로우를 수행하는 것이다. 반면에, 쿠버네티스는 독립적이고 조합 가능한 제어 프로세스들로 구성되어 있다. 이 프로세스는 지속적으로 현재 상태를 입력받은 의도한 상태로 나아가도록 한다. A에서 C로 어떻게 갔는지는 상관이 없다. 중앙화된 제어도 필요치 않다. 이로써 시스템이 보다 더 사용하기 쉬워지고, 강력해지며, 견고하고, 회복력을 갖추게 되며, 확장 가능해진다.

다음 내용

3.1.2 - 쿠버네티스 컴포넌트

쿠버네티스 클러스터는 컴퓨터 집합인 노드 컴포넌트와 컨트롤 플레인 컴포넌트로 구성된다.

쿠버네티스를 배포하면 클러스터를 얻는다.

쿠버네티스 클러스터는 컨테이너화된 애플리케이션을 실행하는 노드라고 하는 워커 머신의 집합. 모든 클러스터는 최소 한 개의 워커 노드를 가진다.

워커 노드는 애플리케이션의 구성요소인 파드를 호스트한다. 컨트롤 플레인은 워커 노드와 클러스터 내 파드를 관리한다. 프로덕션 환경에서는 일반적으로 컨트롤 플레인이 여러 컴퓨터에 걸쳐 실행되고, 클러스터는 일반적으로 여러 노드를 실행하므로 내결함성과 고가용성이 제공된다.

이 문서는 완전히 작동하는 쿠버네티스 클러스터를 갖기 위해 필요한 다양한 컴포넌트들에 대해 요약하고 정리한다.

쿠버네티스 구성 요소

쿠버네티스 클러스터 구성 요소

컨트롤 플레인 컴포넌트

컨트롤 플레인 컴포넌트는 클러스터에 관한 전반적인 결정(예를 들어, 스케줄링)을 수행하고 클러스터 이벤트(예를 들어, 디플로이먼트의 replicas 필드에 대한 요구 조건이 충족되지 않을 경우 새로운 파드를 구동시키는 것)를 감지하고 반응한다.

컨트롤 플레인 컴포넌트는 클러스터 내 어떠한 머신에서든지 동작할 수 있다. 그러나 간결성을 위하여, 구성 스크립트는 보통 동일 머신 상에 모든 컨트롤 플레인 컴포넌트를 구동시키고, 사용자 컨테이너는 해당 머신 상에 동작시키지 않는다. 여러 VM에서 실행되는 컨트롤 플레인 설정의 예제를 보려면 kubeadm을 사용하여 고가용성 클러스터 만들기를 확인해본다.

kube-apiserver

API 서버는 쿠버네티스 API를 노출하는 쿠버네티스 컨트롤 플레인 컴포넌트이다. API 서버는 쿠버네티스 컨트롤 플레인의 프론트 엔드이다.

쿠버네티스 API 서버의 주요 구현은 kube-apiserver 이다. kube-apiserver는 수평으로 확장되도록 디자인되었다. 즉, 더 많은 인스턴스를 배포해서 확장할 수 있다. 여러 kube-apiserver 인스턴스를 실행하고, 인스턴스간의 트래픽을 균형있게 조절할 수 있다.

etcd

모든 클러스터 데이터를 담는 쿠버네티스 뒷단의 저장소로 사용되는 일관성·고가용성 키-값 저장소.

쿠버네티스 클러스터에서 etcd를 뒷단의 저장소로 사용한다면, 이 데이터를 백업하는 계획은 필수이다.

etcd에 대한 자세한 정보는, 공식 문서를 참고한다.

kube-scheduler

노드가 배정되지 않은 새로 생성된 파드 를 감지하고, 실행할 노드를 선택하는 컨트롤 플레인 컴포넌트.

스케줄링 결정을 위해서 고려되는 요소는 리소스에 대한 개별 및 총체적 요구 사항, 하드웨어/소프트웨어/정책적 제약, 어피니티(affinity) 및 안티-어피니티(anti-affinity) 명세, 데이터 지역성, 워크로드-간 간섭, 데드라인을 포함한다.

kube-controller-manager

컨트롤러 프로세스를 실행하는 컨트롤 플레인 컴포넌트.

논리적으로, 각 컨트롤러는 분리된 프로세스이지만, 복잡성을 낮추기 위해 모두 단일 바이너리로 컴파일되고 단일 프로세스 내에서 실행된다.

이들 컨트롤러는 다음을 포함한다.

  • 노드 컨트롤러: 노드가 다운되었을 때 통지와 대응에 관한 책임을 가진다.
  • 레플리케이션 컨트롤러: 시스템의 모든 레플리케이션 컨트롤러 오브젝트에 대해 알맞은 수의 파드들을 유지시켜 주는 책임을 가진다.
  • 엔드포인트 컨트롤러: 엔드포인트 오브젝트를 채운다(즉, 서비스와 파드를 연결시킨다.)
  • 서비스 어카운트 & 토큰 컨트롤러: 새로운 네임스페이스에 대한 기본 계정과 API 접근 토큰을 생성한다.

cloud-controller-manager

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

cloud-controller-manager는 클라우드 제공자 전용 컨트롤러만 실행한다. 자신의 사내 또는 PC 내부의 학습 환경에서 쿠버네티스를 실행 중인 경우 클러스터에는 클라우드 컨트롤러 매니저가 없다.

kube-controller-manager와 마찬가지로 cloud-controller-manager는 논리적으로 독립적인 여러 컨트롤 루프를 단일 프로세스로 실행하는 단일 바이너리로 결합한다. 수평으로 확장(두 개 이상의 복제 실행)해서 성능을 향상시키거나 장애를 견딜 수 있다.

다음 컨트롤러들은 클라우드 제공 사업자의 의존성을 가질 수 있다.

  • 노드 컨트롤러: 노드가 응답을 멈춘 후 클라우드 상에서 삭제되었는지 판별하기 위해 클라우드 제공 사업자에게 확인하는 것
  • 라우트 컨트롤러: 기본 클라우드 인프라에 경로를 구성하는 것
  • 서비스 컨트롤러: 클라우드 제공 사업자 로드밸런서를 생성, 업데이트 그리고 삭제하는 것

노드 컴포넌트

노드 컴포넌트는 동작 중인 파드를 유지시키고 쿠버네티스 런타임 환경을 제공하며, 모든 노드 상에서 동작한다.

kubelet

클러스터의 각 노드에서 실행되는 에이전트. Kubelet은 파드에서 컨테이너가 확실하게 동작하도록 관리한다.

Kubelet은 다양한 메커니즘을 통해 제공된 파드 스펙(PodSpec)의 집합을 받아서 컨테이너가 해당 파드 스펙에 따라 건강하게 동작하는 것을 확실히 한다. Kubelet은 쿠버네티스를 통해 생성되지 않는 컨테이너는 관리하지 않는다.

kube-proxy

kube-proxy는 클러스터의 각 노드에서 실행되는 네트워크 프록시로, 쿠버네티스의 서비스 개념의 구현부이다.

kube-proxy는 노드의 네트워크 규칙을 유지 관리한다. 이 네트워크 규칙이 내부 네트워크 세션이나 클러스터 바깥에서 파드로 네트워크 통신을 할 수 있도록 해준다.

kube-proxy는 운영 체제에 가용한 패킷 필터링 계층이 있는 경우, 이를 사용한다. 그렇지 않으면, kube-proxy는 트래픽 자체를 포워드(forward)한다.

컨테이너 런타임

컨테이너 런타임은 컨테이너 실행을 담당하는 소프트웨어이다.

쿠버네티스는 여러 컨테이너 런타임을 지원한다. 도커(Docker), containerd, CRI-O 그리고 Kubernetes CRI (컨테이너 런타임 인터페이스)를 구현한 모든 소프트웨어.

애드온

애드온은 쿠버네티스 리소스(데몬셋, 디플로이먼트 등)를 이용하여 클러스터 기능을 구현한다. 이들은 클러스터 단위의 기능을 제공하기 때문에 애드온에 대한 네임스페이스 리소스는 kube-system 네임스페이스에 속한다.

선택된 일부 애드온은 아래에 설명하였고, 사용 가능한 전체 확장 애드온 리스트는 애드온을 참조한다.

DNS

여타 애드온들이 절대적으로 요구되지 않지만, 많은 예시에서 필요로 하기 때문에 모든 쿠버네티스 클러스터는 클러스터 DNS를 갖추어야만 한다.

클러스터 DNS는 구성환경 내 다른 DNS 서버와 더불어, 쿠버네티스 서비스를 위해 DNS 레코드를 제공해주는 DNS 서버다.

쿠버네티스에 의해 구동되는 컨테이너는 DNS 검색에서 이 DNS 서버를 자동으로 포함한다.

웹 UI (대시보드)

대시보드는 쿠버네티스 클러스터를 위한 범용의 웹 기반 UI다. 사용자가 클러스터 자체뿐만 아니라, 클러스터에서 동작하는 애플리케이션에 대한 관리와 문제 해결을 할 수 있도록 해준다.

컨테이너 리소스 모니터링

컨테이너 리소스 모니터링은 중앙 데이터베이스 내의 컨테이너들에 대한 포괄적인 시계열 매트릭스를 기록하고 그 데이터를 열람하기 위한 UI를 제공해 준다.

클러스터-레벨 로깅

클러스터-레벨 로깅 메커니즘은 검색/열람 인터페이스와 함께 중앙 로그 저장소에 컨테이너 로그를 저장하는 책임을 진다.

다음 내용

3.1.3 - 쿠버네티스 API

쿠버네티스 API를 사용하면 쿠버네티스 오브젝트들의 상태를 쿼리하고 조작할 수 있다. 쿠버네티스 컨트롤 플레인의 핵심은 API 서버와 그것이 노출하는 HTTP API이다. 사용자와 클러스터의 다른 부분 및 모든 외부 컴포넌트는 API 서버를 통해 서로 통신한다.

쿠버네티스 컨트롤 플레인의 핵심은 API 서버이다. API 서버는 최종 사용자, 클러스터의 다른 부분 그리고 외부 컴포넌트가 서로 통신할 수 있도록 HTTP API를 제공한다.

쿠버네티스 API를 사용하면 쿠버네티스의 API 오브젝트(예: 파드(Pod), 네임스페이스(Namespace), 컨피그맵(ConfigMap) 그리고 이벤트(Event))를 질의(query)하고 조작할 수 있다.

대부분의 작업은 kubectl 커맨드 라인 인터페이스 또는 API를 사용하는 kubeadm과 같은 다른 커맨드 라인 도구를 통해 수행할 수 있다. 그러나, REST 호출을 사용하여 API에 직접 접근할 수도 있다.

쿠버네티스 API를 사용하여 애플리케이션을 작성하는 경우 클라이언트 라이브러리 중 하나를 사용하는 것이 좋다.

OpenAPI 명세

완전한 API 상세 내용은 OpenAPI를 활용해서 문서화했다.

OpenAPI 규격은 /openapi/v2 엔드포인트에서만 제공된다. 다음과 같은 요청 헤더를 사용해서 응답 형식을 요청할 수 있다.

Valid request header values for OpenAPI v2 queries
Header Possible values Notes
Accept-Encoding gzip not supplying this header is also acceptable
Accept application/[email protected]+protobuf mainly for intra-cluster use
application/json default
* serves application/json

쿠버네티스는 주로 클러스터 내부 통신을 위해 대안적인 Protobuf에 기반한 직렬화 형식을 구현한다. 이 형식에 대한 자세한 내용은 쿠버네티스 Protobuf 직렬화 디자인 제안과 API 오브젝트를 정의하는 Go 패키지에 들어있는 각각의 스키마에 대한 IDL(인터페이스 정의 언어) 파일을 참고한다.

지속성

쿠버네티스는 오브젝트의 직렬화된 상태를 etcd에 기록하여 저장한다.

API 그룹과 버전 규칙

필드를 쉽게 제거하거나 리소스 표현을 재구성하기 위해, 쿠버네티스는 각각 /api/v1 또는 /apis/rbac.authorization.k8s.io/v1alpha1 과 같은 서로 다른 API 경로에서 여러 API 버전을 지원한다.

버전 규칙은 리소스나 필드 수준이 아닌 API 수준에서 수행되어 API가 시스템 리소스 및 동작에 대한 명확하고 일관된 보기를 제공하고 수명 종료 및/또는 실험적 API에 대한 접근을 제어할 수 있도록 한다.

보다 쉽게 발전하고 API를 확장하기 위해, 쿠버네티스는 활성화 또는 비활성화가 가능한 API 그룹을 구현한다.

API 리소스는 API 그룹, 리소스 유형, 네임스페이스 (네임스페이스 리소스용) 및 이름으로 구분된다. API 서버는 API 버전 간의 변환을 투명하게 처리한다. 서로 다른 모든 버전은 실제로 동일한 지속 데이터의 표현이다. API 서버는 여러 API 버전을 통해 동일한 기본 데이터를 제공할 수 있다.

예를 들어, 동일한 리소스에 대해 v1v1beta1 이라는 두 가지 API 버전이 있다고 가정한다. 원래 API의 v1beta1 버전을 사용하여 오브젝트를 만든 경우, 나중에 v1beta1 또는 v1 API 버전을 사용하여 해당 오브젝트를 읽거나, 업데이트하거나, 삭제할 수 있다.

API 변경 사항

성공적인 시스템은 새로운 유스케이스가 등장하거나 기존 사례가 변경됨에 따라 성장하고 변화해야 한다. 따라서, 쿠버네티스는 쿠버네티스 API가 지속적으로 변경되고 성장할 수 있도록 설계했다. 쿠버네티스 프로젝트는 기존 클라이언트와의 호환성을 깨지 않고 다른 프로젝트가 적응할 기회를 가질 수 있도록 장기간 해당 호환성을 유지하는 것을 목표로 한다.

일반적으로, 새 API 리소스와 새 리소스 필드는 자주 추가될 수 있다. 리소스 또는 필드를 제거하려면 API 지원 중단 정책을 따라야 한다.

쿠버네티스는 일반적으로 API 버전 v1 에서 안정 버전(GA)에 도달하면, 공식 쿠버네티스 API에 대한 호환성 유지를 강력하게 이행한다. 또한, 쿠버네티스는 가능한 경우 베타 API 버전에서도 호환성을 유지한다. 베타 API를 채택하면 기능이 안정된 후에도 해당 API를 사용하여 클러스터와 계속 상호 작용할 수 있다.

API 버전 수준 정의에 대한 자세한 내용은 API 버전 레퍼런스를 참조한다.

API 확장

쿠버네티스 API는 다음 두 가지 방법 중 하나로 확장할 수 있다.

  1. 커스텀 리소스를 사용하면 API 서버가 선택한 리소스 API를 제공하는 방법을 선언적으로 정의할 수 있다.
  2. 애그리게이션 레이어(aggregation layer)를 구현하여 쿠버네티스 API를 확장할 수도 있다.

다음 내용

3.1.4 - 쿠버네티스 오브젝트로 작업하기

쿠버네티스 오브젝트는 쿠버네티스 시스템의 영구 엔티티이다. 쿠버네티스는 이러한 엔티티들을 사용하여 클러스터의 상태를 나타낸다. 쿠버네티스 오브젝트 모델과 쿠버네티스 오브젝트를 사용하는 방법에 대해 학습한다.

3.1.4.1 - 쿠버네티스 오브젝트 이해하기

이 페이지에서는 쿠버네티스 오브젝트가 쿠버네티스 API에서 어떻게 표현되고, 그 오브젝트를 어떻게 .yaml 형식으로 표현할 수 있는지에 대해 설명한다.

쿠버네티스 오브젝트 이해하기

쿠버네티스 오브젝트 는 쿠버네티스 시스템에서 영속성을 가지는 오브젝트이다. 쿠버네티스는 클러스터의 상태를 나타내기 위해 이 오브젝트를 이용한다. 구체적으로 말하자면, 다음같이 기술할 수 있다.

  • 어떤 컨테이너화된 애플리케이션이 동작 중인지 (그리고 어느 노드에서 동작 중인지)
  • 그 애플리케이션이 이용할 수 있는 리소스
  • 그 애플리케이션이 어떻게 재구동 정책, 업그레이드, 그리고 내고장성과 같은 것에 동작해야 하는지에 대한 정책

쿠버네티스 오브젝트는 하나의 "의도를 담은 레코드"이다. 오브젝트를 생성하게 되면, 쿠버네티스 시스템은 그 오브젝트 생성을 보장하기 위해 지속적으로 작동할 것이다. 오브젝트를 생성함으로써, 여러분이 클러스터의 워크로드를 어떤 형태로 보이고자 하는지에 대해 효과적으로 쿠버네티스 시스템에 전한다. 이것이 바로 여러분의 클러스터에 대해 의도한 상태 가 된다.

생성이든, 수정이든, 또는 삭제든 쿠버네티스 오브젝트를 동작시키려면, 쿠버네티스 API를 이용해야 한다. 예를 들어, kubectl 커맨드-라인 인터페이스를 이용할 때, CLI는 여러분 대신 필요한 쿠버네티스 API를 호출해 준다. 또한, 여러분은 클라이언트 라이브러리 중 하나를 이용하여 여러분만의 프로그램에서 쿠버네티스 API를 직접 이용할 수도 있다.

오브젝트 명세(spec)와 상태(status)

거의 모든 쿠버네티스 오브젝트는 오브젝트의 구성을 결정해주는 두 개의 중첩된 오브젝트 필드를 포함하는데 오브젝트 spec 과 오브젝트 status 이다. spec을 가진 오브젝트는 오브젝트를 생성할 때 리소스에 원하는 특징(의도한 상태)에 대한 설명을 제공해서 설정한다.

status 는 쿠버네티스 시스템과 컴포넌트에 의해 제공되고 업데이트된 오브젝트의 현재 상태 를 설명한다. 쿠버네티스 컨트롤 플레인은 모든 오브젝트의 실제 상태를 사용자가 의도한 상태와 일치시키기 위해 끊임없이 그리고 능동적으로 관리한다.

예를 들어, 쿠버네티스 디플로이먼트는 클러스터에서 동작하는 애플리케이션을 표현해줄 수 있는 오브젝트이다. 디플로이먼트를 생성할 때, 디플로이먼트 spec에 3개의 애플리케이션 레플리카가 동작되도록 설정할 수 있다. 쿠버네티스 시스템은 그 디플로이먼트 spec을 읽어 spec에 일치되도록 상태를 업데이트하여 3개의 의도한 애플리케이션 인스턴스를 구동시킨다. 만약, 그 인스턴스들 중 어느 하나가 어떤 문제로 인해 멈춘다면(상태 변화 발생), 쿠버네티스 시스템은 보정(이 경우에는 대체 인스턴스를 시작하여)을 통해 spec과 status간의 차이에 대응한다.

오브젝트 명세, 상태, 그리고 메타데이터에 대한 추가 정보는, Kubernetes API Conventions 를 참조한다.

쿠버네티스 오브젝트 기술하기

쿠버네티스에서 오브젝트를 생성할 때, (이름과 같은)오브젝트에 대한 기본적인 정보와 더불어, 의도한 상태를 기술한 오브젝트 spec을 제시해 줘야만 한다. 오브젝트를 생성하기 위해(직접이든 또는 kubectl을 통해서든) 쿠버네티스 API를 이용할 때, API 요청은 요청 내용 안에 JSON 형식으로 정보를 포함시켜 줘야만 한다. 대부분의 경우 정보를 .yaml 파일로 kubectl에 제공한다. kubectl은 API 요청이 이루어질 때, JSON 형식으로 정보를 변환시켜 준다.

여기 쿠버네티스 디플로이먼트를 위한 필수 필드와 오브젝트 spec을 보여주는 .yaml 파일 예시가 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

위 예시와 같이 .yaml 파일을 이용하여 디플로이먼트를 생성하기 위한 하나의 방식으로는 kubectl 커맨드-라인 인터페이스에 인자값으로 .yaml 파일을 건네 kubectl apply 커맨드를 이용하는 것이다. 다음 예시와 같다.

kubectl apply -f https://k8s.io/examples/application/deployment.yaml --record

그 출력 내용은 다음과 유사하다.

deployment.apps/nginx-deployment created

요구되는 필드

생성하고자 하는 쿠버네티스 오브젝트에 대한 .yaml 파일 내, 다음 필드를 위한 값들을 설정해 줘야한다.

  • apiVersion - 이 오브젝트를 생성하기 위해 사용하고 있는 쿠버네티스 API 버전이 어떤 것인지
  • kind - 어떤 종류의 오브젝트를 생성하고자 하는지
  • metadata - 이름 문자열, UID, 그리고 선택적인 네임스페이스를 포함하여 오브젝트를 유일하게 구분지어 줄 데이터
  • spec - 오브젝트에 대해 어떤 상태를 의도하는지

오브젝트 spec에 대한 정확한 포맷은 모든 쿠버네티스 오브젝트마다 다르고, 그 오브젝트 특유의 중첩된 필드를 포함한다. 쿠버네티스 API 레퍼런스 는 쿠버네티스를 이용하여 생성할 수 있는 오브젝트에 대한 모든 spec 포맷을 살펴볼 수 있도록 해준다.

예를 들어, API 내 파드에 대한 상세 정보는 spec 필드에 대한 레퍼런스에서, 디플로이먼트에 대한 상세 정보는 spec 필드에 대한 레퍼런스에서 확인할 수 있다. 해당 API 레퍼런스 페이지에서 PodSpec과 DeploymentSpec에 대해 언급된 내용을 볼 수 있다. 이 이름들은 쿠버네티스가 API를 구현하는데 사용한 Go 언어 코드 구현의 세부 내용이다.

다음 내용

  • 파드와 같이, 가장 중요하고 기본적인 쿠버네티스 오브젝트에 대해 배운다.
  • 쿠버네티스의 컨트롤러에 대해 배운다.
  • API 개념의 더 많은 설명은 쿠버네티스 API 사용을 본다.

3.1.4.2 - 쿠버네티스 오브젝트 관리

kubectl 커맨드라인 툴은 쿠버네티스 오브젝트를 생성하고 관리하기 위한 몇 가지 상이한 방법을 지원한다. 이 문서는 여러가지 접근법에 대한 개요을 제공한다. Kubectl로 오브젝트 관리하기에 대한 자세한 설명은 Kubectl 서적에서 확인한다.

관리 기법

관리기법 대상 권장 환경 지원하는 작업자 수 학습 난이도
명령형 커맨드 활성 오브젝트 개발 환경 1+ 낮음
명령형 오브젝트 구성 개별 파일 프로덕션 환경 1 보통
선언형 오브젝트 구성 파일이 있는 디렉터리 프로덕션 환경 1+ 높음

명령형 커맨드

명령형 커맨드를 사용할 경우, 사용자는 클러스터 내 활성 오브젝트를 대상으로 직접 동작시킨다. 사용자는 실행할 작업을 인수 또는 플래그로 kubectl 커맨드에 지정한다.

이것은 클러스터에서 일회성 작업을 개시시키거나 동작시키기 위한 추천 방법이다. 이 기법은 활성 오브젝트를 대상으로 직접적인 영향을 미치기 때문에, 이전 구성에 대한 이력을 제공해 주지 않는다.

예시

디플로이먼트 오브젝트를 생성하여 nginx 컨테이너의 인스턴스를 구동시킨다.

kubectl create deployment nginx --image nginx

트레이드 오프

오브젝트 구성에 비해 장점은 다음과 같다.

  • 커맨드는 하나의 동작을 나타내는 단어로 표현된다.
  • 커맨드는 클러스터를 수정하기 위해 단 하나의 단계만을 필요로 한다.

오브젝트 구성에 비해 단점은 다음과 같다.

  • 커맨드는 변경 검토 프로세스와 통합되지 않는다.
  • 커맨드는 변경에 관한 감사 추적(audit trail)을 제공하지 않는다.
  • 커맨드는 활성 동작 중인 경우를 제외하고는 레코드의 소스를 제공하지 않는다.
  • 커맨드는 새로운 오브젝트 생성을 위한 템플릿을 제공하지 않는다.

명령형 오브젝트 구성

명령형 오브젝트 구성에서는 kubectl 커맨드로 작업(생성, 교체 등), 선택적 플래그, 그리고 최소 하나의 파일 이름을 지정한다. 그 파일은 YAML 또는 JSON 형식으로 오브젝트의 완전한 정의를 포함해야만 한다.

오브젝트 정의에 대한 더 자세한 내용은 API 참조를 참고한다.

예시

구성 파일에 정의된 오브젝트를 생성한다.

kubectl create -f nginx.yaml

두 개의 구성 파일에 정의된 오브젝트를 삭제한다.

kubectl delete -f nginx.yaml -f redis.yaml

활성 동작하는 구성을 덮어씀으로써 구성 파일에 정의된 오브젝트를 업데이트한다.

kubectl replace -f nginx.yaml

트레이드 오프

명령형 커맨드에 비해 장점은 다음과 같다.

  • 오브젝트 구성은 Git과 같은 소스 컨트롤 시스템에 보관할 수 있다.
  • 오브젝트 구성은 푸시와 감사 추적 전에 변경사항을 검토하는 것과 같은 프로세스들과 통합할 수 있다.
  • 오브젝트 구성은 새로운 오브젝트 생성을 위한 템플릿을 제공한다.

명령형 커맨드에 비해 단점은 다음과 같다.

  • 오브젝트 구성은 오브젝트 스키마에 대한 기본적인 이해를 필요로 한다.
  • 오브젝트 구성은 YAML 파일을 기록하는 추가적인 과정을 필요로 한다.

선언형 오브젝트 구성에 비해 장점은 다음과 같다.

  • 명령형 오브젝트 구성의 동작은 보다 간결하고 이해하기 쉽다.
  • 쿠버네티스 버전 1.5 부터는 더 성숙한 명령형 오브젝트 구성을 제공한다.

선언형 오브젝트 구성에 비해 단점은 다음과 같다.

  • 명령형 오브젝트 구성은 디렉터리가 아닌, 파일에 가장 적합하다.
  • 활성 오브젝트에 대한 업데이트는 구성 파일에 반영되어야 한다. 그렇지 않으면 다음 교체 중에 손실된다.

선언형 오브젝트 구성

선언형 오브젝트 구성을 사용할 경우, 사용자는 로컬에 보관된 오브젝트 구성 파일을 대상으로 작동시키지만, 사용자는 파일에서 수행 할 작업을 정의하지 않는다. 생성, 업데이트, 그리고 삭제 작업은 kubectl에 의해 오브젝트마다 자동으로 감지된다. 이를 통해 다른 오브젝트에 대해 다른 조작이 필요할 수 있는 디렉터리에서 작업할 수 있다.

예시

configs 디렉터리 내 모든 오브젝트 구성 파일을 처리하고 활성 오브젝트를 생성 또는 패치한다. 먼저 어떠한 변경이 이루어지게 될지 알아보기 위해 diff 하고 나서 적용할 수 있다.

kubectl diff -f configs/
kubectl apply -f configs/

재귀적으로 디렉터리를 처리한다.

kubectl diff -R -f configs/
kubectl apply -R -f configs/

트레이드 오프

명령형 오브젝트 구성에 비해 장점은 다음과 같다.

  • 활성 오브젝트에 직접 작성된 변경 사항은 구성 파일로 다시 병합되지 않더라도 유지된다.
  • 선언형 오브젝트 구성은 디렉터리에서의 작업 및 오브젝트 별 작업 유형(생성, 패치, 삭제)의 자동 감지에 더 나은 지원을 제공한다.

명령형 오브젝트 구성에 비해 단점은 다음과 같다.

  • 선언형 오브젝트 구성은 예상치 못한 결과를 디버깅하고 이해하기가 더 어렵다.
  • diff를 사용한 부분 업데이트는 복잡한 병합 및 패치 작업을 일으킨다.

다음 내용

3.1.4.3 - 오브젝트 이름과 ID

클러스터의 각 오브젝트는 해당 유형의 리소스에 대하여 고유한 이름 을 가지고 있다. 또한, 모든 쿠버네티스 오브젝트는 전체 클러스터에 걸쳐 고유한 UID 를 가지고 있다.

예를 들어, 이름이 myapp-1234인 파드는 동일한 네임스페이스 내에서 하나만 존재할 수 있지만, 이름이 myapp-1234인 파드와 디플로이먼트는 각각 존재할 수 있다.

유일하지 않은 사용자 제공 속성의 경우 쿠버네티스는 레이블어노테이션을 제공한다.

이름

/api/v1/pods/some-name과 같이, 리소스 URL에서 오브젝트를 가리키는 클라이언트 제공 문자열.

특정 시점에 같은 종류(kind) 내에서는 하나의 이름은 하나의 오브젝트에만 지정될 수 있다. 하지만, 오브젝트를 삭제한 경우, 삭제된 오브젝트와 같은 이름을 새로운 오브젝트에 지정 가능하다.

다음은 리소스에 일반적으로 사용되는 네 가지 유형의 이름 제한 조건이다.

DNS 서브도메인 이름

대부분의 리소스 유형에는 RFC 1123에 정의된 대로 DNS 서브도메인 이름으로 사용할 수 있는 이름이 필요하다. 이것은 이름이 다음을 충족해야 한다는 것을 의미한다.

  • 253자를 넘지 말아야 한다.
  • 소문자와 영숫자 - 또는 . 만 포함한다.
  • 영숫자로 시작한다.
  • 영숫자로 끝난다.

RFC 1123 레이블 이름

일부 리소스 유형은 RFC 1123에 정의된 대로 DNS 레이블 표준을 따라야 한다. 이것은 이름이 다음을 충족해야 한다는 것을 의미한다.

  • 최대 63자이다.
  • 소문자와 영숫자 또는 - 만 포함한다.
  • 영숫자로 시작한다.
  • 영숫자로 끝난다.

RFC 1035 레이블 이름

몇몇 리소스 타입은 자신의 이름을 RFC 1035에 정의된 DNS 레이블 표준을 따르도록 요구한다. 이것은 이름이 다음을 만족해야 한다는 의미이다.

  • 최대 63개 문자를 포함
  • 소문자 영숫자 또는 '-'만 포함
  • 알파벳 문자로 시작
  • 영숫자로 끝남

경로 세그먼트 이름

일부 리소스 유형에서는 이름을 경로 세그먼트로 안전하게 인코딩 할 수 있어야 한다. 즉 이름이 "." 또는 ".."이 아닐 수 있으며 이름에는 "/" 또는 "%"가 포함될 수 없다.

아래는 파드의 이름이 nginx-demo라는 매니페스트 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

UID

오브젝트를 중복 없이 식별하기 위해 쿠버네티스 시스템이 생성하는 문자열.

쿠버네티스 클러스터가 구동되는 전체 시간에 걸쳐 생성되는 모든 오브젝트는 서로 구분되는 UID를 갖는다. 이는 기록상 유사한 오브젝트의 출현을 서로 구분하기 위함이다.

쿠버네티스 UID는 보편적으로 고유한 식별자이다(또는 UUID라고 한다). UUID는 ISO/IEC 9834-8 과 ITU-T X.667 로 표준화 되어 있다.

다음 내용

3.1.4.4 - 네임스페이스

쿠버네티스에서, 네임스페이스 는 단일 클러스터 내에서의 리소스 그룹 격리 메커니즘을 제공한다. 리소스의 이름은 네임스페이스 내에서 유일해야 하며, 네임스페이스 간에서 유일할 필요는 없다. 네임스페이스 기반 스코핑은 네임스페이스 기반 오브젝트 (예: 디플로이먼트, 서비스 등) 에만 적용 가능하며 클러스터 범위의 오브젝트 (예: 스토리지클래스, 노드, 퍼시스턴트볼륨 등) 에는 적용 불가능하다.

여러 개의 네임스페이스를 사용하는 경우

네임스페이스는 여러 개의 팀이나, 프로젝트에 걸쳐서 많은 사용자가 있는 환경에서 사용하도록 만들어졌다. 사용자가 거의 없거나, 수 십명 정도가 되는 경우에는 네임스페이스를 전혀 고려할 필요가 없다. 네임스페이스가 제공하는 기능이 필요할 때 사용하도록 하자.

네임스페이스는 이름의 범위를 제공한다. 리소스의 이름은 네임스페이스 내에서 유일해야하지만, 네임스페이스를 통틀어서 유일할 필요는 없다. 네임스페이스는 서로 중첩될 수 없으며, 각 쿠버네티스 리소스는 하나의 네임스페이스에만 있을 수 있다.

네임스페이스는 클러스터 자원을 (리소스 쿼터를 통해) 여러 사용자 사이에서 나누는 방법이다.

동일한 소프트웨어의 다른 버전과 같이 약간 다른 리소스를 분리하기 위해 여러 네임스페이스를 사용할 필요는 없다. 동일한 네임스페이스 내에서 리소스를 구별하기 위해 레이블을 사용한다.

네임스페이스 다루기

네임스페이스의 생성과 삭제는 네임스페이스 관리자 가이드 문서에 기술되어 있다.

네임스페이스 조회

사용 중인 클러스터의 현재 네임스페이스를 나열할 수 있다.

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-public       Active   1d
kube-system       Active   1d

쿠버네티스는 처음에 네 개의 초기 네임스페이스를 갖는다.

  • default 다른 네임스페이스가 없는 오브젝트를 위한 기본 네임스페이스
  • kube-system 쿠버네티스 시스템에서 생성한 오브젝트를 위한 네임스페이스
  • kube-public 이 네임스페이스는 자동으로 생성되며 모든 사용자(인증되지 않은 사용자 포함)가 읽기 권한으로 접근할 수 있다. 이 네임스페이스는 주로 전체 클러스터 중에 공개적으로 드러나서 읽을 수 있는 리소스를 위해 예약되어 있다. 이 네임스페이스의 공개적인 성격은 단지 관례이지 요구 사항은 아니다.
  • kube-node-lease 클러스터가 스케일링될 때 노드 하트비트의 성능을 향상시키는 각 노드와 관련된 리스(lease) 오브젝트에 대한 네임스페이스
  • kube-node-lease 이 네임스페이스는 각 노드와 연관된 리스 오브젝트를 갖는다. 노드 리스는 kubelet이 하트비트를 보내서 컨트롤 플레인이 노드의 장애를 탐지할 수 있게 한다.

요청에 네임스페이스 설정하기

현재 요청에 대한 네임스페이스를 설정하기 위해서 --namespace 플래그를 사용한다.

예를 들면,

kubectl run nginx --image=nginx --namespace=<insert-namespace-name-here>
kubectl get pods --namespace=<insert-namespace-name-here>

선호하는 네임스페이스 설정하기

이후 모든 kubectl 명령에서 사용하는 네임스페이스를 컨텍스트에 영구적으로 저장할 수 있다.

kubectl config set-context --current --namespace=<insert-namespace-name-here>
# 확인하기
kubectl config view --minify | grep namespace:

네임스페이스와 DNS

서비스를 생성하면 해당 DNS 엔트리가 생성된다. 이 엔트리는 <서비스-이름>.<네임스페이스-이름>.svc.cluster.local의 형식을 갖는데, 이는 컨테이너가 <서비스-이름>만 사용하는 경우, 네임스페이스 내에 국한된 서비스로 연결된다. 개발, 스테이징, 운영과 같이 여러 네임스페이스 내에서 동일한 설정을 사용하는 경우에 유용하다. 네임스페이스를 넘어서 접근하기 위해서는, 전체 주소 도메인 이름(FQDN)을 사용해야 한다.

모든 오브젝트가 네임스페이스에 속하지는 않음

대부분의 쿠버네티스 리소스(예를 들어, 파드, 서비스, 레플리케이션 컨트롤러 외)는 네임스페이스에 속한다. 하지만 네임스페이스 리소스 자체는 네임스페이스에 속하지 않는다. 그리고 노드나 퍼시스턴트 볼륨과 같은 저수준 리소스는 어느 네임스페이스에도 속하지 않는다.

다음은 네임스페이스에 속하지 않는 쿠버네티스 리소스를 조회하는 방법이다.

# 네임스페이스에 속하는 리소스
kubectl api-resources --namespaced=true

# 네임스페이스에 속하지 않는 리소스
kubectl api-resources --namespaced=false

자동 레이블링

FEATURE STATE: Kubernetes 1.21 [beta]

쿠버네티스 컨트롤 플레인은 NamespaceDefaultLabelName 기능 게이트가 활성화된 경우 모든 네임스페이스에 변경할 수 없는(immutable) 레이블 kubernetes.io / metadata.name 을 설정한다. 레이블 값은 네임스페이스 이름이다.

다음 내용

3.1.4.5 - 레이블과 셀렉터

레이블 은 파드와 같은 오브젝트에 첨부된 키와 값의 쌍이다. 레이블은 오브젝트의 특성을 식별하는 데 사용되어 사용자에게 중요하지만, 코어 시스템에 직접적인 의미는 없다. 레이블로 오브젝트의 하위 집합을 선택하고, 구성하는데 사용할 수 있다. 레이블은 오브젝트를 생성할 때에 붙이거나 생성 이후에 붙이거나 언제든지 수정이 가능하다. 오브젝트마다 키와 값으로 레이블을 정의할 수 있다. 오브젝트의 키는 고유한 값이어야 한다.

"metadata": {
  "labels": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

레이블은 UI와 CLI에서 효율적인 쿼리를 사용하고 검색에 사용하기에 적합하다. 식별되지 않는 정보는 어노테이션으로 기록해야 한다.

사용 동기

레이블을 이용하면 사용자가 느슨하게 결합한 방식으로 조직 구조와 시스템 오브젝트를 매핑할 수 있으며, 클라이언트에 매핑 정보를 저장할 필요가 없다.

서비스 배포와 배치 프로세싱 파이프라인은 흔히 다차원의 엔티티들이다(예: 다중 파티션 또는 배포, 다중 릴리스 트랙, 다중 계층, 계층 속 여러 마이크로 서비스들). 관리에는 크로스-커팅 작업이 필요한 경우가 많은데 이 작업은 사용자보다는 인프라에 의해 결정된 엄격한 계층 표현인 캡슐화를 깨트린다.

레이블 예시:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

이 예시는 일반적으로 사용하는 레이블이며, 사용자는 자신만의 규칙(convention)에 따라 자유롭게 개발할 수 있다. 오브젝트에 붙여진 레이블 키는 고유해야 한다는 것을 기억해야 한다.

구문과 캐릭터 셋

레이블 은 키와 값의 쌍이다. 유효한 레이블 키에는 슬래시(/)로 구분되는 선택한 접두사와 이름이라는 2개의 세그먼트가 있다. 이름 세그먼트는 63자 미만으로 시작과 끝은 알파벳과 숫자([a-z0-9A-Z])이며, 대시(-), 밑줄(_), 점(.)과 함께 사용할 수 있다. 접두사는 선택이다. 만약 접두사를 지정한 경우 접두사는 DNS의 하위 도메인으로 해야 하며, 점(.)과 전체 253자 이하, 슬래시(/)로 구분되는 DNS 레이블이다.

접두사를 생략하면 키 레이블은 개인용으로 간주한다. 최종 사용자의 오브젝트에 자동화된 시스템 컴포넌트(예: kube-scheduler, kube-controller-manager, kube-apiserver, kubectl 또는 다른 타사의 자동화 구성 요소)의 접두사를 지정해야 한다.

kubernetes.io/k8s.io/ 접두사는 쿠버네티스의 핵심 컴포넌트로 예약되어 있다.

유효한 레이블 값은 다음과 같다.

  • 63 자 이하여야 하고 (공백일 수도 있음),
  • (공백이 아니라면) 시작과 끝은 알파벳과 숫자([a-z0-9A-Z])이며,
  • 알파벳과 숫자, 대시(-), 밑줄(_), 점(.)을 중간에 포함할 수 있다.

다음의 예시는 파드에 environment: productionapp: nginx 2개의 레이블이 있는 구성 파일이다.


apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    environment: production
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

레이블 셀렉터

이름과 UID와 다르게 레이블은 고유하지 않다. 일반적으로 우리는 많은 오브젝트에 같은 레이블을 가질 것으로 예상한다.

레이블 셀렉터를 통해 클라이언트와 사용자는 오브젝트를 식별할 수 있다. 레이블 셀렉터는 쿠버네티스 코어 그룹의 기본이다.

API는 현재 일치성 기준집합성 기준 이라는 두 종류의 셀렉터를 지원한다. 레이블 셀렉터는 쉼표로 구분된 다양한 요구사항 에 따라 만들 수 있다. 다양한 요구사항이 있는 경우 쉼표 기호가 AND(&&) 연산자로 구분되는 역할을 하도록 해야 한다.

비어있거나 지정되지 않은 셀렉터는 상황에 따라 달라진다. 셀렉터를 사용하는 API 유형은 유효성과 의미를 문서화해야 한다.

일치성 기준 요건

일치성 기준 또는 불일치 기준 의 요구사항으로 레이블의 키와 값의 필터링을 허용한다. 일치하는 오브젝트는 추가 레이블을 가질 수 있지만, 레이블의 명시된 제약 조건을 모두 만족해야 한다. =,==,!= 이 세 가지 연산자만 허용한다. 처음 두 개의 연산자의 일치성(그리고 동의어), 나머지는 불일치 를 의미한다. 예를 들면,

environment = production
tier != frontend

전자는 environment를 키로 가지는 것과 production을 값으로 가지는 모든 리소스를 선택한다. 후자는 tier를 키로 가지고, 값을 frontend를 가지는 리소스를 제외한 모든 리소스를 선택하고, tier를 키로 가지며, 값을 공백으로 가지는 모든 리소스를 선택한다. environment=production,tier!=frontend 처럼 쉼표를 통해 한 문장으로 frontend를 제외한 production을 필터링할 수 있다.

일치성 기준 레이블 요건에 대한 하나의 이용 시나리오는 파드가 노드를 선택하는 기준을 지정하는 것이다. 예를 들어, 아래 샘플 파드는 "accelerator=nvidia-tesla-p100" 레이블을 가진 노드를 선택한다.

apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "k8s.gcr.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100

집합성 기준 요건

집합성 기준 레이블 요건에 따라 값 집합을 키로 필터링할 수 있다. in,notinexists(키 식별자만 해당)의 3개의 연산자를 지원한다. 예를 들면,

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • 첫 번째 예시에서 키가 environment이고 값이 production 또는 qa인 모든 리소스를 선택한다.
  • 두 번째 예시에서 키가 tier이고 값이 frontendbackend를 가지는 리소스를 제외한 모든 리소스와 키로 tier를 가지고 값을 공백으로 가지는 모든 리소스를 선택한다.
  • 세 번째 예시에서 레이블의 값에 상관없이 키가 partition을 포함하는 모든 리소스를 선택한다.
  • 네 번째 예시에서 레이블의 값에 상관없이 키가 partition을 포함하지 않는 모든 리소스를 선택한다.

마찬가지로 쉼표는 AND 연산자로 작동한다. 따라서 partition,environment notin (qa)와 같이 사용하면 값과 상관없이 키가 partition인 것과 키가 environment이고 값이 qa와 다른 리소스를 필터링할 수 있다. 집합성 기준 레이블 셀렉터는 일반적으로 environment=productionenvironment in (production)을 같은 것으로 본다. 유사하게는 !=notin을 같은 것으로 본다.

집합성 기준 요건은 일치성 기준 요건과 조합해서 사용할 수 있다. 예를 들어 partition in (customerA, customerB),environment!=qa

API

LIST와 WATCH 필터링

LIST와 WATCH 작업은 쿼리 파라미터를 사용해서 반환되는 오브젝트 집합을 필터링하기 위해 레이블 셀렉터를 지정할 수 있다. 다음의 두 가지 요건 모두 허용된다(URL 쿼리 문자열을 그대로 표기함).

  • 일치성 기준 요건: ?labelSelector=environment%3Dproduction,tier%3Dfrontend
  • 집합성 기준 요건: ?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29

두 가지 레이블 셀렉터 스타일은 모두 REST 클라이언트를 통해 선택된 리소스를 확인하거나 목록을 볼 수 있다. 예를 들어, kubectlapiserver를 대상으로 일치성 기준 으로 하는 셀렉터를 다음과 같이 이용할 수 있다.

kubectl get pods -l environment=production,tier=frontend

또는 집합성 기준 요건을 사용하면

kubectl get pods -l 'environment in (production),tier in (frontend)'

앞서 안내한 것처럼 집합성 기준 요건은 더 보여준다. 예시에서 다음과 같이 OR 연산자를 구현할 수 있다.

kubectl get pods -l 'environment in (production, qa)'

또는 exists 연산자에 불일치한 것으로 제한할 수 있다.

kubectl get pods -l 'environment,environment notin (frontend)'

API 오브젝트에서 참조 설정

servicesreplicationcontrollers와 같은 일부 쿠버네티스 오브젝트는 레이블 셀렉터를 사용해서 파드와 같은 다른 리소스 집합을 선택한다.

서비스와 레플리케이션 컨트롤러

services에서 지정하는 파드 집합은 레이블 셀렉터로 정의한다. 마찬가지로 replicationcontrollers가 관리하는 파드의 오브젝트 그룹도 레이블 셀렉터로 정의한다.

서비스와 레플리케이션 컨트롤러의 레이블 셀렉터는 json 또는 yaml 파일에 매핑된 일치성 기준 요구사항의 셀렉터만 지원한다.

"selector": {
    "component" : "redis",
}

or

selector:
    component: redis

json 또는 yaml 서식에서 셀렉터는 component=redis 또는 component in (redis) 모두 같은 것이다.

세트-기반 요건을 지원하는 리소스

Job, Deployment, ReplicaSet 그리고 DaemonSet 같은 새로운 리소스들은 집합성 기준 의 요건도 지원한다.

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - {key: tier, operator: In, values: [cache]}
    - {key: environment, operator: NotIn, values: [dev]}

matchLabels{key,value}의 쌍과 매칭된다. matchLabels에 매칭된 단일 {key,value}matchExpressions의 요소와 같으며 key 필드는 "key"로, operator는 "In" 그리고 values에는 "value"만 나열되어 있다. matchExpressions는 파드 셀렉터의 요건 목록이다. 유효한 연산자에는 In, NotIn, Exists 및 DoNotExist가 포함된다. In 및 NotIn은 설정된 값이 있어야 한다. matchLabelsmatchExpressions 모두 AND로 되어 있어 일치하기 위해서는 모든 요건을 만족해야 한다.

노드 셋 선택

레이블을 통해 선택하는 사용 사례 중 하나는 파드를 스케줄 할 수 있는 노드 셋을 제한하는 것이다. 자세한 내용은 노드 선택 문서를 참조한다.

3.1.4.6 - 어노테이션

쿠버네티스 어노테이션을 사용하여 임의의 비-식별 메타데이터를 오브젝트에 첨부할 수 있다. 도구 및 라이브러리와 같은 클라이언트는 이 메타데이터를 검색할 수 있다.

오브젝트에 메타데이터 첨부

레이블이나 어노테이션을 사용하여 쿠버네티스 오브젝트에 메타데이터를 첨부할 수 있다. 레이블을 사용하여 오브젝트를 선택하고, 특정 조건을 만족하는 오브젝트 컬렉션을 찾을 수 있다. 반면에, 어노테이션은 오브젝트를 식별하고 선택하는데 사용되지 않는다. 어노테이션의 메타데이터는 작거나 크고, 구조적이거나 구조적이지 않을 수 있으며, 레이블에서 허용되지 않는 문자를 포함할 수 있다.

어노테이션은 레이블과 같이 키/값 맵이다.

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

다음은 어노테이션에 기록할 수 있는 정보의 예제이다.

  • 필드는 선언적 구성 계층에 의해 관리된다. 이러한 필드를 어노테이션으로 첨부하는 것은 클라이언트 또는 서버가 설정한 기본 값, 자동 생성된 필드, 그리고 오토사이징 또는 오토스케일링 시스템에 의해 설정된 필드와 구분된다.

  • 빌드, 릴리스, 또는 타임 스탬프, 릴리스 ID, git 브랜치, PR 번호, 이미지 해시 및 레지스트리 주소와 같은 이미지 정보.

  • 로깅, 모니터링, 분석 또는 감사 리포지터리에 대한 포인터.

  • 디버깅 목적으로 사용될 수 있는 클라이언트 라이브러리 또는 도구 정보: 예를 들면, 이름, 버전, 그리고 빌드 정보.

  • 다른 생태계 구성 요소의 관련 오브젝트 URL과 같은 사용자 또는 도구/시스템 출처 정보.

  • 경량 롤아웃 도구 메타데이터. 예: 구성 또는 체크포인트

  • 책임자의 전화번호 또는 호출기 번호, 또는 팀 웹 사이트 같은 해당 정보를 찾을 수 있는 디렉터리 진입점.

  • 행동을 수정하거나 비표준 기능을 수행하기 위한 최종 사용자의 지시 사항.

어노테이션을 사용하는 대신, 이 유형의 정보를 외부 데이터베이스 또는 디렉터리에 저장할 수 있지만, 이는 배포, 관리, 인트로스펙션(introspection) 등을 위한 공유 클라이언트 라이브러리와 도구 생성을 훨씬 더 어렵게 만들 수 있다.

문법과 캐릭터 셋

어노테이션 은 키/값 쌍이다. 유효한 어노테이션 키에는 두 개의 세그먼트가 있다. 두 개의 세그먼트는 선택적인 접두사와 이름(name)이며, 슬래시(/)로 구분된다. 이름 세그먼트는 필수이며, 영문 숫자([a-z0-9A-Z])로 시작하고 끝나는 63자 이하이어야 하고, 사이에 대시(-), 밑줄(_), 점(.)이 들어갈 수 있다. 접두사는 선택적이다. 지정된 경우, 접두사는 DNS 서브도메인이어야 한다. 점(.)으로 구분된 일련의 DNS 레이블은 총 253자를 넘지 않고, 뒤에 슬래시(/)가 붙는다.

접두사가 생략되면, 어노테이션 키는 사용자에게 비공개로 간주된다. 최종 사용자 오브젝트에 어노테이션을 추가하는 자동화된 시스템 구성 요소(예 :kube-scheduler, kube-controller-manager, kube-apiserver, kubectl, 또는 다른 써드파티 자동화)는 접두사를 지정해야 한다.

kubernetes.io/k8s.io/ 접두사는 쿠버네티스 핵심 구성 요소를 위해 예약되어 있다.

다음은 imageregistry: https://hub.docker.com/ 어노테이션이 있는 파드의 구성 파일 예시이다.


apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

다음 내용

레이블과 셀렉터에 대해 알아본다.

3.1.4.7 - 필드 셀렉터

필드 셀렉터 는 한 개 이상의 리소스 필드 값에 따라 쿠버네티스 리소스를 선택하기 위해 사용된다. 필드 셀렉터 쿼리의 예시는 다음과 같다.

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

다음의 kubectl 커맨드는 status.phase 필드의 값이 Running 인 모든 파드를 선택한다.

kubectl get pods --field-selector status.phase=Running

사용 가능한 필드

사용 가능한 필드는 쿠버네티스의 리소스 종류에 따라서 다르다. 모든 리소스 종류는 metadata.namemetadata.namespace 필드 셀렉터를 사용할 수 있다. 사용할 수 없는 필드 셀렉터를 사용하면 다음과 같이 에러를 출력한다.

kubectl get ingress --field-selector foo.bar=baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

사용 가능한 연산자

필드 셀렉터에서 =, ==, != 연산자를 사용할 수 있다 (===는 동일한 의미이다). 예를 들면, 다음의 kubectl 커맨드는 default 네임스페이스에 속해있지 않은 모든 쿠버네티스 서비스를 선택한다.

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

연계되는 셀렉터

레이블을 비롯한 다른 셀렉터처럼, 쉼표로 구분되는 목록을 통해 필드 셀렉터를 연계해서 사용할 수 있다. 다음의 kubectl 커맨드는 status.phase 필드가 Running 이 아니고, spec.restartPolicy 필드가 Always 인 모든 파드를 선택한다.

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

여러 개의 리소스 종류

필드 셀렉터를 여러 개의 리소스 종류에 걸쳐 사용할 수 있다. 다음의 kubectl 커맨드는 default 네임스페이스에 속해있지 않은 모든 스테이트풀셋(StatefulSet)과 서비스를 선택한다.

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

3.1.4.8 - 권장 레이블

kubectl과 대시보드와 같은 많은 도구들로 쿠버네티스 오브젝트를 시각화 하고 관리할 수 있다. 공통 레이블 셋은 모든 도구들이 이해할 수 있는 공통의 방식으로 오브젝트를 식별하고 도구들이 상호 운용적으로 작동할 수 있도록 한다.

권장 레이블은 지원 도구 외에도 쿼리하는 방식으로 애플리케이션을 식별하게 한다.

메타데이터는 애플리케이션 의 개념을 중심으로 정리된다. 쿠버네티스는 플랫폼 서비스(PaaS)가 아니며 애플리케이션에 대해 공식적인 개념이 없거나 강요하지 않는다. 대신 애플리케이션은 비공식적이며 메타데이터로 설명된다. 애플리케이션에 포함된 정의는 유연하다.

공유 레이블과 주석에는 공통 접두사인 app.kubernetes.io 가 있다. 접두사가 없는 레이블은 사용자가 개인적으로 사용할 수 있다. 공유 접두사는 공유 레이블이 사용자 정의 레이블을 방해하지 않도록 한다.

레이블

레이블을 최대한 활용하려면 모든 리소스 오브젝트에 적용해야 한다.

설명 예시 타입
app.kubernetes.io/name 애플리케이션 이름 mysql 문자열
app.kubernetes.io/instance 애플리케이션의 인스턴스를 식별하는 고유한 이름 mysql-abcxzy 문자열
app.kubernetes.io/version 애플리케이션의 현재 버전 (예: a semantic version, revision hash 등.) 5.7.21 문자열
app.kubernetes.io/component 아키텍처 내 구성요소 database 문자열
app.kubernetes.io/part-of 이 애플리케이션의 전체 이름 wordpress 문자열
app.kubernetes.io/managed-by 애플리케이션의 작동을 관리하는 데 사용되는 도구 helm 문자열
app.kubernetes.io/created-by 이 리소스를 만든 컨트롤러/사용자 controller-manager 문자열

위 레이블의 실제 예시는 다음 스테이트풀셋 오브젝트를 고려한다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/created-by: controller-manager

애플리케이션과 애플리케이션 인스턴스

애플리케이션은 동일한 쿠버네티스 클러스터에, 심지어는 동일한 네임스페이스에도 한번 또는 그 이상 설치될 수 있다. 예를 들어, 하나의 쿠버네티스 클러스터에 WordPress가 여러 번 설치되어 각각 서로 다른 웹사이트를 서비스할 수 있다.

애플리케이션의 이름과 애플리케이션 인스턴스 이름은 별도로 기록된다. 예를 들어 WordPress는 애플리케이션 이름으로 app.kubernetes.io/name 이라는 레이블에 wordpress 라는 값을 가지며, 애플리케이션 인스턴스 이름으로는 app.kubernetes.io/instance 라는 레이블에 wordpress-abcxzy 라는 값을 가진다. 이를 통해 애플리케이션과 애플리케이션 인스턴스를 식별할 수 있다. 모든 애플리케이션 인스턴스는 고유한 이름을 가져야 한다.

예시

위 레이블을 사용하는 다른 방식에 대한 예시는 다양한 복잡성이 있다.

단순한 스테이트리스 서비스

DeploymentService 오브젝트를 통해 배포된 단순한 스테이트리스 서비스의 경우를 보자. 다음 두 식별자는 레이블을 가장 간단한 형태로 사용하는 방법을 나타낸다.

Deployment 는 애플리케이션을 실행하는 파드를 감시하는 데 사용한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

Service는 애플리케이션을 노출하기 위해 사용한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

데이터베이스가 있는 웹 애플리케이션

Helm을 이용해서 데이터베이스(MySQL)을 이용하는 웹 애플리케이션(WordPress)을 설치한 것과 같이 좀 더 복잡한 애플리케이션을 고려할 수 있다. 다음 식별자는 이 애플리케이션을 배포하는 데 사용하는 오브젝트의 시작을 보여준다.

WordPress를 배포하는 데 다음과 같이 Deployment 로 시작한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

Service 는 애플리케이션을 노출하기 위해 사용한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

MySQL은 StatefulSet 에 MySQL의 소속과 상위 애플리케이션에 대한 메타데이터가 포함되어 노출된다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

Service 는 WordPress의 일부로 MySQL을 노출하는 데 이용한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

MySQL StatefulSetService 로 MySQL과 WordPress가 더 큰 범위의 애플리케이션에 포함되어 있는 것을 알게 된다.

3.2 - 클러스터 아키텍처

쿠버네티스 뒤편의 구조와 설계 개념들

3.2.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이 마스터에 노드 상태를 게시할 지 정의.

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 내에 있는 NodeReady 컨디션을 업데이트한다. 이 경우에는 노드 컨트롤러가 NodeReady 컨디션을 ConditionUnknown으로 설정한다.
  • 노드가 계속 접근 불가능 상태로 남아있는 경우, 해당 노드의 모든 파드에 대해서 API를 이용한 축출을 트리거한다. 기본적으로, 노드 컨트롤러는 노드를 ConditionUnknown으로 마킹한 뒤 5분을 기다렸다가 최초의 축출 요청을 시작한다.

노드 컨트롤러는 매 --node-monitor-period 초 마다 각 노드의 상태를 체크한다.

축출 빈도 한계

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

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

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

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

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

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

리소스 용량 추적

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

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

노드 토폴로지

FEATURE STATE: Kubernetes v1.16 [alpha]

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

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

FEATURE STATE: 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초는 중요 파드의 종료에 할당된다.

스왑(swap) 메모리 관리

FEATURE STATE: 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디자인 제안에서 찾을 수 있다.

다음 내용

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

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

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

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

특히 익명의 요청 또는 서비스 어카운트 토큰이 허용되는 경우, 하나 이상의 권한 부여 형식을 사용해야 한다.

노드는 유효한 클라이언트 자격 증명과 함께 API 서버에 안전하게 연결할 수 있도록 클러스터에 대한 공개 루트 인증서로 프로비전해야 한다. 예를 들어, 기본 GKE 배포에서, kubelet에 제공되는 클라이언트 자격 증명은 클라이언트 인증서 형식이다. kubelet 클라이언트 인증서의 자동 프로비저닝은 kubelet TLS 부트스트랩을 참고한다.

API 서버에 연결하려는 파드는 쿠버네티스가 공개 루트 인증서와 유효한 베어러 토큰(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의 서빙 인증서를 확인하는 데 사용할 루트 인증서 번들을 제공한다.

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

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

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

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

SSH 터널

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

SSH 터널은 현재 더 이상 사용되지 않으므로, 수행 중인 작업이 어떤 것인지 모른다면 사용하면 안된다. Konnectivity 서비스는 이 통신 채널을 대체한다.

Konnectivity 서비스

FEATURE STATE: Kubernetes v1.18 [beta]

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

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

3.2.3 - 컨트롤러

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

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

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

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

컨트롤러 패턴

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

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

API 서버를 통한 제어

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

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

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

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

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

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

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

직접 제어

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

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

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

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

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

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

의도한 상태와 현재 상태

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

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

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

디자인

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

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

컨트롤러를 실행하는 방법

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

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

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

다음 내용

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

FEATURE STATE: Kubernetes v1.11 [beta]

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

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

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

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

디자인

쿠버네티스 컴포넌트

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

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

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

노드 컨트롤러

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

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

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

라우트 컨트롤러

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

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

서비스 컨트롤러

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

인가

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

노드 컨트롤러

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

v1/Node:

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

라우트 컨트롤러

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

v1/Node:

  • Get

서비스 컨트롤러

서비스 컨트롤러는 서비스 오브젝트 생성, 업데이트 그리고 삭제 이벤트를 수신한 다음 해당 서비스에 대한 엔드포인트를 적절하게 구성한다.

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

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

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 인터페이스를 구현한다.

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

3.3 - 컨테이너

런타임 의존성과 함께 애플리케이션을 패키징하는 기술

실행하는 각 컨테이너는 반복 가능하다. 의존성이 포함된 표준화는 어디에서 실행하던지 동일한 동작을 얻는다는 것을 의미한다.

컨테이너는 기본 호스트 인프라에서 애플리케이션을 분리한다. 따라서 다양한 클라우드 또는 OS 환경에서 보다 쉽게 ​​배포할 수 있다.

컨테이너 이미지

컨테이너 이미지는 애플리케이션을 실행하는 데 필요한 모든 것이 포함된 실행할 준비가 되어 있는(ready-to-run) 소프트웨어 패키지이다. 여기에는 실행하는 데 필요한 코드와 모든 런타임, 애플리케이션 및 시스템 라이브러리, 그리고 모든 필수 설정에 대한 기본값이 포함된다.

설계 상, 컨테이너는 변경할 수 없다. 이미 실행 중인 컨테이너의 코드를 변경할 수 없다. 컨테이너화된 애플리케이션이 있고 변경하려는 경우, 변경 사항이 포함된 새 이미지를 빌드한 다음, 업데이트된 이미지에서 시작하도록 컨테이너를 다시 생성해야 한다.

컨테이너 런타임

컨테이너 런타임은 컨테이너 실행을 담당하는 소프트웨어이다.

쿠버네티스는 여러 컨테이너 런타임을 지원한다. 도커(Docker), containerd, CRI-O 그리고 Kubernetes CRI (컨테이너 런타임 인터페이스)를 구현한 모든 소프트웨어.

다음 내용

3.3.1 - 이미지

컨테이너 이미지는 애플리케이션과 모든 소프트웨어 의존성을 캡슐화하는 바이너리 데이터를 나타낸다. 컨테이너 이미지는 독립적으로 실행할 수 있고 런타임 환경에 대해 잘 정의된 가정을 만드는 실행 가능한 소프트웨어 번들이다.

일반적으로 파드에서 참조하기 전에 애플리케이션의 컨테이너 이미지를 생성해서 레지스트리로 푸시한다.

이 페이지는 컨테이너 이미지 개념의 개요를 제공한다.

이미지 이름

컨테이너 이미지는 일반적으로 pause, example/mycontainer 또는 kube-apiserver 와 같은 이름을 부여한다. 이미지는 또한 레지스트리 호스트 이름을 포함할 수 있다. 예를 들면, fictional.registry.example/imagename 과 같다. 그리고 포트 번호도 포함할 수 있다. 예를 들면, fictional.registry.example:10443/imagename 과 같다.

레지스트리 호스트 이름을 지정하지 않으면, 쿠버네티스는 도커 퍼블릭 레지스트리를 의미한다고 가정한다.

이미지 이름 부분 다음에 tag 를 추가할 수 있다(dockerpodman 등의 명령과 함께 사용). 태그를 사용하면 동일한 시리즈 이미지의 다른 버전을 식별할 수 있다.

이미지 태그는 소문자와 대문자, 숫자, 밑줄(_), 마침표(.) 및 대시(-)로 구성된다. 이미지 태그 안에서 구분 문자(_, - 그리고 .)를 배치할 수 있는 위치에 대한 추가 규칙이 있다. 태그를 지정하지 않으면, 쿠버네티스는 태그 latest 를 의미한다고 가정한다.

이미지 업데이트

디플로이먼트, 스테이트풀셋, 파드 또는 파드 템플릿은 포함하는 다른 오브젝트를 처음 만들 때 특별히 명시하지 않은 경우 기본적으로 해당 파드에 있는 모든 컨테이너의 풀(pull) 정책은 IfNotPresent로 설정된다. 이 정책은 kubelet이 이미 존재하는 이미지에 대한 풀을 생략하게 한다.

이미지 풀(pull) 정책

컨테이너에 대한 imagePullPolicy와 이미지의 태그는 kubelet이 특정 이미지를 풀(다운로드)하려고 할 때 영향을 준다.

다음은 imagePullPolicy에 설정할 수 있는 값의 목록과 효과이다.

IfNotPresent
이미지가 로컬에 없는 경우에만 내려받는다.
Always
kubelet이 컨테이너를 기동할 때마다, kubelet이 컨테이너 이미지 레지스트리에 이름과 이미지의 다이제스트가 있는지 질의한다. 일치하는 다이제스트를 가진 컨테이너 이미지가 로컬에 있는 경우, kubelet은 캐시된 이미지를 사용한다. 이외의 경우, kubelet은 검색된 다이제스트를 가진 이미지를 내려받아서 컨테이너를 기동할 때 사용한다.
Never
kubelet은 이미지를 가져오려고 시도하지 않는다. 이미지가 어쨌든 이미 로컬에 존재하는 경우, kubelet은 컨테이너 기동을 시도한다. 이외의 경우 기동은 실패한다. 보다 자세한 내용은 미리 내려받은 이미지를 참조한다.

이미지 제공자에 앞서 깔린 캐시의 의미 체계는 레지스트리에 안정적으로 접근할 수 있는 한, imagePullPolicy: Always인 경우 조차도 효율적이다. 컨테이너 런타임은 노드에 이미 존재하는 이미지 레이어를 알고 다시 내려받지 않는다.

파드가 항상 컨테이너 이미지의 같은 버전을 사용하는 것을 확실히 하려면, 이미지의 다이제스트를 명기할 수 있다. <image-name>:<tag><image-name>@<digest>로 교체한다. (예를 들어, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2).

이미지 태그를 사용하는 경우, 이미지 레지스트리에서 한 이미지를 나타내는 태그에 코드를 변경하게 되면, 기존 코드와 신규 코드를 구동하는 파드가 섞이게 되고 만다. 이미지 다이제스트를 통해 이미지의 특정 버전을 유일하게 식별할 수 있기 때문에, 쿠버네티스는 매번 해당 이미지 이름과 다이제스트가 명시된 컨테이너를 기동해서 같은 코드를 구동한다. 이미지를 명시하는 것은 구동할 코드를 고정시켜서 레지스트리에서의 변경으로 인해 버전이 섞이는 일이 발생하지 않도록 해준다.

파드(및 파드 템플릿)가 생성될 때 구동 중인 워크로드가 태그가 아닌 이미지 다이제스트를 통해 정의되도록 조작해주는 서드-파티 어드미션 컨트롤러가 있다. 이는 레지스트리에서 태그가 변경되는 일이 발생해도 구동 중인 워크로드가 모두 같은 코드를 사용하고 있다는 것을 보장하기를 원하는 경우 유용할 것이다.

기본 이미지 풀 정책

사용자(또는 컨트롤러)가 신규 파드를 API 서버에 요청할 때, 특정 조건에 부합하면 클러스터가 imagePullPolicy 필드를 설정한다.

  • imagePullPolicy 필드를 생략하고 컨테이너 이미지의 태그가 :latest인 경우, imagePullPolicy는 자동으로 Always로 설정된다.
  • imagePullPolicy 필드를 생략하고 컨테이너 이미지의 태그를 명기하지 않은 경우, imagePullPolicy는 자동으로 Always로 설정된다.
  • imagePullPolicy 필드를 생략하고, 명기한 컨테이너 이미지의 태그가 :latest가 아니면, imagePullPolicy는 자동으로 IfNotPresent로 설정된다.

이미지 풀 강제

이미지를 내려받도록 강제하려면, 다음 중 한가지 방법을 사용한다.

  • 컨테이너의 imagePullPolicyAlways로 설정한다.
  • imagePullPolicy를 생략하고 사용할 이미지 태그로 :latest를 사용한다. 그러면 사용자가 파드를 요청할 때 쿠버네티스가 정책을 Always로 설정한다.
  • imagePullPolicy와 사용할 이미지의 태그를 생략한다. 그러면 사용자가 파드를 요청할 때 쿠버네티스가 정책을 Always로 설정한다.
  • AlwaysPullImages 어드미션 컨트롤러를 활성화 한다.

이미지풀백오프(ImagePullBackOff)

kubelet이 컨테이너 런타임을 사용하여 파드의 컨테이너 생성을 시작할 때, ImagePullBackOff로 인해 컨테이너가 Waiting 상태에 있을 수 있다.

ImagePullBackOff라는 상태는 (이미지 이름이 잘못됨, 또는 imagePullSecret 없이 비공개 레지스트리에서 풀링 시도 등의 이유로) 쿠버네티스가 컨테이너 이미지를 가져올 수 없기 때문에 컨테이너를 실행할 수 없음을 의미한다. BackOff라는 단어는 쿠버네티스가 백오프 딜레이를 증가시키면서 이미지 풀링을 계속 시도할 것임을 나타낸다.

쿠버네티스는 시간 간격을 늘려가면서 시도를 계속하며, 시간 간격의 상한은 쿠버네티스 코드에 300초(5분)로 정해져 있다.

이미지 인덱스가 있는 다중 아키텍처 이미지

바이너리 이미지를 제공할 뿐만 아니라, 컨테이너 레지스트리는 컨테이너 이미지 인덱스를 제공할 수도 있다. 이미지 인덱스는 컨테이너의 아키텍처별 버전에 대한 여러 이미지 매니페스트를 가리킬 수 있다. 아이디어는 이미지의 이름(예를 들어, pause, example/mycontainer, kube-apiserver)을 가질 수 있다는 것이다. 그래서 다른 시스템들이 사용하고 있는 컴퓨터 아키텍처에 적합한 바이너리 이미지를 가져올 수 있다.

쿠버네티스 자체는 일반적으로 -$(ARCH) 접미사로 컨테이너 이미지의 이름을 지정한다. 이전 버전과의 호환성을 위해, 접미사가 있는 오래된 이미지를 생성한다. 아이디어는 모든 아키텍처에 대한 매니페스트가 있는 pause 이미지와 이전 구성 또는 이전에 접미사로 이미지를 하드 코딩한 YAML 파일과 호환되는 pause-amd64 라고 하는 이미지를 생성한다.

프라이빗 레지스트리 사용

프라이빗 레지스트리는 해당 레지스트리에서 이미지를 읽을 수 있는 키를 요구할 것이다. 자격 증명(credential)은 여러 가지 방법으로 제공될 수 있다.

  • 프라이빗 레지스트리에 대한 인증을 위한 노드 구성
    • 모든 파드는 구성된 프라이빗 레지스트리를 읽을 수 있음
    • 클러스터 관리자에 의한 노드 구성 필요
  • 미리 내려받은(pre-pulled) 이미지
    • 모든 파드는 노드에 캐시된 모든 이미지를 사용 가능
    • 셋업을 위해서는 모든 노드에 대해서 root 접근이 필요
  • 파드에 ImagePullSecrets을 명시
    • 자신의 키를 제공하는 파드만 프라이빗 레지스트리에 접근 가능
  • 공급 업체별 또는 로컬 확장
    • 사용자 정의 노드 구성을 사용하는 경우, 사용자(또는 클라우드 제공자)가 컨테이너 레지스트리에 대한 노드 인증 메커니즘을 구현할 수 있다.

이들 옵션은 아래에서 더 자세히 설명한다.

프라이빗 레지스트리에 인증하도록 노드 구성

노드에서 도커를 실행하는 경우, 프라이빗 컨테이너 레지스트리를 인증하도록 도커 컨테이너 런타임을 구성할 수 있다.

이 방법은 노드 구성을 제어할 수 있는 경우에 적합하다.

도커는 프라이빗 레지스트리를 위한 키를 $HOME/.dockercfg 또는 $HOME/.docker/config.json 파일에 저장한다. 만약 동일한 파일을 아래의 검색 경로 리스트에 넣으면, kubelet은 이미지를 풀 할 때 해당 파일을 자격 증명 공급자로 사용한다.

  • {--root-dir:-/var/lib/kubelet}/config.json
  • {cwd of kubelet}/config.json
  • ${HOME}/.docker/config.json
  • /.docker/config.json
  • {--root-dir:-/var/lib/kubelet}/.dockercfg
  • {cwd of kubelet}/.dockercfg
  • ${HOME}/.dockercfg
  • /.dockercfg

프라이빗 레지스트리를 사용도록 사용자의 노드를 구성하기 위해서 권장되는 단계는 다음과 같다. 이 예제의 경우, 사용자의 데스크탑/랩탑에서 아래 내용을 실행한다.

  1. 사용하고 싶은 각 자격 증명 세트에 대해서 docker login [서버]를 실행한다. 이것은 여러분 PC의 $HOME/.docker/config.json를 업데이트한다.
  2. 편집기에서 $HOME/.docker/config.json를 보고 사용하고 싶은 자격 증명만 포함하고 있는지 확인한다.
  3. 노드의 리스트를 구한다. 예를 들면 다음과 같다.
    • 이름을 원하는 경우: nodes=$( kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}' )
    • IP를 원하는 경우: nodes=$( kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}' )
  4. 로컬의 .docker/config.json를 위의 검색 경로 리스트 중 하나에 복사한다.
    • 이를 테스트하기 위한 예: for n in $nodes; do scp ~/.docker/config.json root@"$n":/var/lib/kubelet/config.json; done

프라이빗 이미지를 사용하는 파드를 생성하여 검증한다. 예를 들면 다음과 같다.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: private-image-test-1
spec:
  containers:
    - name: uses-private-image
      image: $PRIVATE_IMAGE_NAME
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]
EOF
pod/private-image-test-1 created

만약 모든 것이 잘 작동한다면, 잠시 후에, 다음을 실행할 수 있다.

kubectl logs private-image-test-1

그리고 커맨드 출력을 본다.

SUCCESS

명령이 실패한 것으로 의심되는 경우 다음을 실행할 수 있다.

kubectl describe pods/private-image-test-1 | grep 'Failed'

실패하는 케이스에는 출력이 다음과 유사하다.

  Fri, 26 Jun 2015 15:36:13 -0700    Fri, 26 Jun 2015 15:39:13 -0700    19    {kubelet node-i2hq}    spec.containers{uses-private-image}    failed        Failed to pull image "user/privaterepo:v1": Error: image user/privaterepo:v1 not found

클러스터의 모든 노드가 반드시 동일한 .docker/config.json를 가져야 한다. 그렇지 않으면, 파드가 일부 노드에서만 실행되고 다른 노드에서는 실패할 것이다. 예를 들어, 노드 오토스케일링을 사용한다면, 각 인스턴스 템플릿은 .docker/config.json을 포함하거나 그것을 포함한 드라이브를 마운트해야 한다.

프라이빗 레지스트리 키가 .docker/config.json에 추가되고 나면 모든 파드는 프라이빗 레지스트리의 이미지에 읽기 접근 권한을 가지게 될 것이다.

미리 내려받은 이미지

기본적으로, kubelet은 지정된 레지스트리에서 각 이미지를 풀 하려고 한다. 그러나, 컨테이너의 imagePullPolicy 속성이 IfNotPresent 또는 Never으로 설정되어 있다면, 로컬 이미지가 사용된다(우선적으로 또는 배타적으로).

레지스트리 인증의 대안으로 미리 풀 된 이미지에 의존하고 싶다면, 클러스터의 모든 노드가 동일한 미리 내려받은 이미지를 가지고 있는지 확인해야 한다.

이것은 특정 이미지를 속도를 위해 미리 로드하거나 프라이빗 레지스트리에 대한 인증의 대안으로 사용될 수 있다.

모든 파드는 미리 내려받은 이미지에 대해 읽기 접근 권한을 가질 것이다.

파드에 ImagePullSecrets 명시

쿠버네티스는 파드에 컨테이너 이미지 레지스트리 키를 명시하는 것을 지원한다.

도커 구성으로 시크릿 생성

대문자 값을 적절히 대체하여, 다음 커맨드를 실행한다.

kubectl create secret docker-registry <name> --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

만약 도커 자격 증명 파일이 이미 존재한다면, 위의 명령을 사용하지 않고, 자격 증명 파일을 쿠버네티스 시크릿으로 가져올 수 있다. 기존 도커 자격 증명으로 시크릿 생성에서 관련 방법을 설명하고 있다.

kubectl create secret docker-registry는 하나의 프라이빗 레지스트리에서만 작동하는 시크릿을 생성하기 때문에, 여러 프라이빗 컨테이너 레지스트리를 사용하는 경우 특히 유용하다.

파드의 imagePullSecrets 참조

이제, imagePullSecrets 섹션을 파드의 정의에 추가함으로써 해당 시크릿을 참조하는 파드를 생성할 수 있다.

예를 들면 다음과 같다.

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

이것은 프라이빗 레지스트리를 사용하는 각 파드에 대해서 수행될 필요가 있다.

그러나, 이 필드의 셋팅은 서비스 어카운트 리소스에 imagePullSecrets을 셋팅하여 자동화할 수 있다.

자세한 지침을 위해서는 서비스 어카운트에 ImagePullSecrets 추가를 확인한다.

이것은 노드 당 .docker/config.json와 함께 사용할 수 있다. 자격 증명은 병합될 것이다.

유스케이스

프라이빗 레지스트리를 구성하기 위한 많은 솔루션이 있다. 다음은 여러 가지 일반적인 유스케이스와 제안된 솔루션이다.

  1. 비소유 이미지(예를 들어, 오픈소스)만 실행하는 클러스터의 경우. 이미지를 숨길 필요가 없다.
    • 도커 허브의 퍼블릭 이미지를 사용한다.
      • 설정이 필요 없다.
      • 일부 클라우드 제공자는 퍼블릭 이미지를 자동으로 캐시하거나 미러링하므로, 가용성이 향상되고 이미지를 가져오는 시간이 줄어든다.
  2. 모든 클러스터 사용자에게는 보이지만, 회사 외부에는 숨겨야하는 일부 독점 이미지를 실행하는 클러스터의 경우.
    • 호스트 된 프라이빗 도커 레지스트리를 사용한다.
      • 그것은 도커 허브에 호스트 되어 있거나, 다른 곳에 되어 있을 것이다.
      • 위에 설명된 바와 같이 수동으로 .docker/config.json을 구성한다.
    • 또는, 방화벽 뒤에서 읽기 접근 권한을 가진 내부 프라이빗 레지스트리를 실행한다.
      • 쿠버네티스 구성은 필요 없다.
    • 이미지 접근을 제어하는 ​​호스팅된 컨테이너 이미지 레지스트리 서비스를 사용한다.
      • 그것은 수동 노드 구성에 비해서 클러스터 오토스케일링과 더 잘 동작할 것이다.
    • 또는, 노드의 구성 변경이 불편한 클러스터에서는, imagePullSecrets를 사용한다.
  3. 독점 이미지를 가진 클러스터로, 그 중 일부가 더 엄격한 접근 제어를 필요로 하는 경우.
    • AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
    • 민감한 데이터는 이미지 안에 포장하는 대신, "시크릿" 리소스로 이동한다.
  4. 멀티-테넌트 클러스터에서 각 테넌트가 자신의 프라이빗 레지스트리를 필요로 하는 경우.
    • AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
    • 인가가 요구되도록 프라이빗 레지스트리를 실행한다.
    • 각 테넌트에 대한 레지스트리 자격 증명을 생성하고, 시크릿에 넣고, 각 테넌트 네임스페이스에 시크릿을 채운다.
    • 테넌트는 해당 시크릿을 각 네임스페이스의 imagePullSecrets에 추가한다.

다중 레지스트리에 접근해야 하는 경우, 각 레지스트리에 대해 하나의 시크릿을 생성할 수 있다. Kubelet은 모든 imagePullSecrets 파일을 하나의 가상 .docker/config.json 파일로 병합한다.

다음 내용

3.3.2 - 런타임클래스(RuntimeClass)

FEATURE STATE: Kubernetes v1.20 [stable]

이 페이지는 런타임클래스 리소스와 런타임 선택 메커니즘에 대해서 설명한다.

런타임클래스는 컨테이너 런타임을 구성을 선택하는 기능이다. 컨테이너 런타임 구성은 파드의 컨테이너를 실행하는 데 사용된다.

동기

서로 다른 파드간에 런타임클래스를 설정하여 성능과 보안의 균형을 유지할 수 있다. 예를 들어, 일부 작업에서 높은 수준의 정보 보안 보증이 요구되는 경우, 하드웨어 가상화를 이용하는 컨테이너 런타임으로 파드를 실행하도록 예약하는 선택을 할 수 있다. 그러면 몇가지 추가적인 오버헤드는 있지만 대체 런타임을 추가 분리하는 유익이 있다.

또한 런타임클래스를 사용하여 컨테이너 런타임이 같으나 설정이 다른 여러 파드를 실행할 수 있다.

셋업

  1. CRI 구현(implementation)을 노드에 설정(런타임에 따라서).
  2. 상응하는 런타임클래스 리소스 생성.

1. CRI 구현을 노드에 설정

런타임클래스를 통한 가능한 구성은 컨테이너 런타임 인터페이스(CRI) 구현에 의존적이다. 사용자의 CRI 구현에 따른 설정 방법은 연관된 문서를 통해서 확인한다(아래).

해당 설정은 상응하는 handler 이름을 가지며, 이는 런타임클래스에 의해서 참조된다. 런타임 핸들러는 유효한 DNS 1123 서브도메인(알파-숫자 + -.문자)을 가져야 한다.

2. 상응하는 런타임클래스 리소스 생성

1단계에서 셋업 한 설정은 연관된 handler 이름을 가져야 하며, 이를 통해서 설정을 식별할 수 있다. 각 런타임 핸들러(그리고 선택적으로 비어있는 "" 핸들러)에 대해서, 상응하는 런타임클래스 오브젝트를 생성한다.

현재 런타임클래스 리소스는 런타임클래스 이름(metadata.name)과 런타임 핸들러 (handler)로 단 2개의 중요 필드만 가지고 있다. 오브젝트 정의는 다음과 같은 형태이다.

apiVersion: node.k8s.io/v1  # 런타임클래스는 node.k8s.io API 그룹에 정의되어 있음
kind: RuntimeClass
metadata:
  name: myclass  # 런타임클래스는 해당 이름을 통해서 참조됨
  # 런타임클래스는 네임스페이스가 없는 리소스임
handler: myconfiguration  # 상응하는 CRI 설정의 이름임

런타임클래스 오브젝트의 이름은 유효한 DNS 레이블 이름어이야 한다.

사용

클러스터를 위해서 런타임클래스를 설정하고 나면, 그것을 사용하는 것은 매우 간단하다. 파드 스펙에 runtimeClassName를 명시한다. 예를 들면 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

이것은 kubelet이 지명된 런타임클래스를 사용하여 해당 파드를 실행하도록 지시할 것이다. 만약 지명된 런타임클래스가 없거나, CRI가 상응하는 핸들러를 실행할 수 없는 경우, 파드는 Failed 터미널 단계로 들어간다. 에러 메시지에 상응하는 이벤트를 확인한다.

만약 명시된 runtimeClassName가 없다면, 기본 런타임 핸들러가 사용되며, 런타임클래스 기능이 비활성화되었을 때와 동일하게 동작한다.

CRI 구성

CRI 런타임 설치에 대한 자세한 내용은 CRI 설치를 확인한다.

dockershim

dockershim을 사용하는 경우 RuntimeClass는 런타임 핸들러를 docker로 고정한다. dockershim은 사용자 정의 런타임 핸들러를 지원하지 않는다.

containerd

런타임 핸들러는 containerd의 구성 파일인 /etc/containerd/config.toml 통해 설정한다. 유효한 핸들러는 runtimes 단락 아래에서 설정한다.

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

더 자세한 containerd의 구성 문서를 살펴본다. https://github.com/containerd/cri/blob/master/docs/config.md

CRI-O

런타임 핸들러는 CRI-O의 구성파일인 /etc/crio/crio.conf을 통해 설정한다. crio.runtime 테이블 아래에 유효한 핸들러를 설정한다.

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

더 자세한 것은 CRI-O의 설정 문서를 본다.

스케줄

FEATURE STATE: Kubernetes v1.16 [beta]

RuntimeClass에 scheduling 필드를 지정하면, 이 RuntimeClass로 실행되는 파드가 이를 지원하는 노드로 예약되도록 제약 조건을 설정할 수 있다. scheduling이 설정되지 않은 경우 이 RuntimeClass는 모든 노드에서 지원되는 것으로 간주된다.

파드가 지정된 런타임클래스를 지원하는 노드에 안착한다는 것을 보장하려면, 해당 노드들은 runtimeClass.scheduling.nodeSelector 필드에서 선택되는 공통 레이블을 가져야한다. 런타임 클래스의 nodeSelector는 파드의 nodeSelector와 어드미션 시 병합되어서, 실질적으로 각각에 의해 선택된 노드의 교집합을 취한다. 충돌이 있는 경우, 파드는 거부된다.

지원되는 노드가 테인트(taint)되어서 다른 런타임클래스 파드가 노드에서 구동되는 것을 막고 있다면, tolerations를 런타임클래스에 추가할 수 있다. nodeSelector를 사용하면, 어드미션 시 해당 톨러레이션(toleration)이 파드의 톨러레이션과 병합되어, 실질적으로 각각에 의해 선택된 노드의 합집합을 취한다.

노드 셀렉터와 톨러레이션 설정에 대해 더 배우려면 노드에 파드 할당을 참고한다.

파드 오버헤드

FEATURE STATE: Kubernetes v1.18 [beta]

파드 실행과 연관되는 오버헤드 리소스를 지정할 수 있다. 오버헤드를 선언하면 클러스터(스케줄러 포함)가 파드와 리소스에 대한 결정을 내릴 때 처리를 할 수 있다. PodOverhead를 사용하려면, PodOverhead 기능 게이트 를 활성화 시켜야 한다. (기본으로 활성화 되어 있다.)

파드 오버헤드는 런타임 클래스에서 overhead 필드를 통해 정의된다. 이 필드를 사용하면, 해당 런타임 클래스를 사용해서 구동 중인 파드의 오버헤드를 특정할 수 있고 이 오버헤드가 쿠버네티스 내에서 처리된다는 것을 보장할 수 있다.

다음 내용

3.3.3 - 컨테이너 환경 변수

이 페이지는 컨테이너 환경에서 컨테이너에 가용한 리소스에 대해 설명한다.

컨테이너 환경

쿠버네티스 컨테이너 환경은 컨테이너에 몇 가지 중요한 리소스를 제공한다.

  • 하나의 이미지와 하나 이상의 볼륨이 결합된 파일 시스템.
  • 컨테이너 자신에 대한 정보.
  • 클러스터 내의 다른 오브젝트에 대한 정보.

컨테이너 정보

컨테이너의 호스트네임 은 컨테이너가 동작 중인 파드의 이름과 같다. 그것은 hostname 커맨드 또는 libc의 gethostname 함수 호출을 통해서 구할 수 있다.

파드 이름과 네임스페이스는 다운워드(Downward) API를 통해 환경 변수로 구할 수 있다.

Docker 이미지에 정적으로 명시된 환경 변수와 마찬가지로, 파드 정의에서의 사용자 정의 환경 변수도 컨테이너가 사용할 수 있다.

클러스터 정보

컨테이너가 생성될 때 실행 중이던 모든 서비스의 목록은 환경 변수로 해당 컨테이너에서 사용할 수 있다. 이 목록은 새로운 컨테이너의 파드 및 쿠버네티스 컨트롤 플레인 서비스와 동일한 네임스페이스 내에 있는 서비스로 한정된다. 이러한 환경 변수는 Docker 링크 구문과 일치한다.

bar 라는 이름의 컨테이너에 매핑되는 foo 라는 이름의 서비스에 대해서는, 다음의 형태로 변수가 정의된다.

FOO_SERVICE_HOST=<서비스가 동작 중인 호스트>
FOO_SERVICE_PORT=<서비스가 동작 중인 포트>

서비스에 지정된 IP 주소가 있고 DNS 애드온이 활성화된 경우, DNS를 통해서 컨테이너가 서비스를 사용할 수 있다.

다음 내용

3.3.4 - 컨테이너 라이프사이클 훅(Hook)

이 페이지는 kubelet이 관리하는 컨테이너가 관리 라이프사이클 동안의 이벤트에 의해 발동되는 코드를 실행하기 위해서 컨테이너 라이프사이클 훅 프레임워크를 사용하는 방법에 대해서 설명한다.

개요

Angular와 같이, 컴포넌트 라이프사이클 훅을 가진 많은 프로그래밍 언어 프레임워크와 유사하게, 쿠버네티스도 컨테이너에 라이프사이클 훅을 제공한다. 훅은 컨테이너가 관리 라이프사이클의 이벤트를 인지하고 상응하는 라이프사이클 훅이 실행될 때 핸들러에 구현된 코드를 실행할 수 있게 한다.

컨테이너 훅

컨테이너에 노출되는 훅은 두 가지가 있다.

PostStart

이 훅은 컨테이너가 생성된 직후에 실행된다. 그러나, 훅이 컨테이너 엔트리포인트에 앞서서 실행된다는 보장은 없다. 파라미터는 핸들러에 전달되지 않는다.

PreStop

이 훅은 API 요청이나 활성 프로브(liveness probe) 실패, 선점, 자원 경합 등의 관리 이벤트로 인해 컨테이너가 종료되기 직전에 호출된다. 컨테이너가 이미 terminated 또는 completed 상태인 경우에는 PreStop 훅 요청이 실패하며, 훅은 컨테이너를 중지하기 위한 TERM 신호가 보내지기 이전에 완료되어야 한다. 파드의 그레이스 종료 기간(termination grace period)의 초읽기는 PreStop 훅이 실행되기 전에 시작되어, 핸들러의 결과에 상관없이 컨테이너가 파드의 그레이스 종료 기간 내에 결국 종료되도록 한다. 어떠한 파라미터도 핸들러에게 전달되지 않는다.

종료 동작에 더 자세한 대한 설명은 파드의 종료에서 찾을 수 있다.

훅 핸들러 구현

컨테이너는 훅의 핸들러를 구현하고 등록함으로써 해당 훅에 접근할 수 있다. 구현될 수 있는 컨테이너의 훅 핸들러에는 두 가지 유형이 있다.

  • Exec - 컨테이너의 cgroups와 네임스페이스 안에서, pre-stop.sh와 같은, 특정 커맨드를 실행. 커맨드에 의해 소비된 리소스는 해당 컨테이너에 대해 계산된다.
  • HTTP - 컨테이너의 특정 엔드포인트에 대해서 HTTP 요청을 실행.

훅 핸들러 실행

컨테이너 라이프사이클 관리 훅이 호출되면, 쿠버네티스 관리 시스템은 훅 동작에 따라 핸들러를 실행하고, httpGettcpSocket 은 kubelet 프로세스에 의해 실행되고, exec 은 컨테이너에서 실행된다.

훅 핸들러 호출은 해당 컨테이너를 포함하고 있는 파드의 컨텍스트와 동기적으로 동작한다. 이것은 PostStart 훅에 대해서, 훅이 컨테이너 엔트리포인트와는 비동기적으로 동작함을 의미한다. 그러나, 만약 해당 훅이 너무 오래 동작하거나 어딘가에 걸려 있다면, 컨테이너는 running 상태에 이르지 못한다.

PreStop 훅은 컨테이너 중지 신호에서 비동기적으로 실행되지 않는다. 훅은 TERM 신호를 보내기 전에 실행을 완료해야 한다. 실행 중에 PreStop 훅이 중단되면, 파드의 단계는 Terminating 이며 terminationGracePeriodSeconds 가 만료된 후 파드가 종료될 때까지 남아 있다. 이 유예 기간은 PreStop 훅이 실행되고 컨테이너가 정상적으로 중지되는 데 걸리는 총 시간에 적용된다. 예를 들어, terminationGracePeriodSeconds 가 60이고, 훅이 완료되는 데 55초가 걸리고, 컨테이너가 신호를 수신한 후 정상적으로 중지하는 데 10초가 걸리면, terminationGracePeriodSeconds 이후 컨테이너가 정상적으로 중지되기 전에 종료된다. 이 두 가지 일이 발생하는 데 걸리는 총 시간(55+10)보다 적다.

만약 PostStart 또는 PreStop 훅이 실패하면, 그것은 컨테이너를 종료시킨다.

사용자는 훅 핸들러를 가능한 한 가볍게 만들어야 한다. 그러나, 컨테이너가 멈추기 전 상태를 저장하는 것과 같이, 오래 동작하는 커맨드가 의미 있는 경우도 있다.

훅 전달 보장

훅 전달은 한 번 이상 으로 의도되어 있는데, 이는 PostStart 또는 PreStop와 같은 특정 이벤트에 대해서, 훅이 여러 번 호출될 수 있다는 것을 의미한다. 이것을 올바르게 처리하는 것은 훅의 구현에 달려 있다.

일반적으로, 전달은 단 한 번만 이루어진다. 예를 들어, HTTP 훅 수신기가 다운되어 트래픽을 받을 수 없는 경우에도, 재전송을 시도하지 않는다. 그러나, 드문 경우로, 이중 전달이 발생할 수 있다. 예를 들어, 훅을 전송하는 도중에 kubelet이 재시작된다면, Kubelet이 구동된 후에 해당 훅은 재전송될 것이다.

디버깅 훅 핸들러

훅 핸들러의 로그는 파드 이벤트로 노출되지 않는다. 만약 핸들러가 어떠한 이유로 실패하면, 핸들러는 이벤트를 방송한다. PostStart의 경우, 이것은 FailedPostStartHook 이벤트이며, PreStop의 경우, 이것은 FailedPreStopHook 이벤트이다. 이 이벤트는 kubectl describe pod <파드_이름>를 실행하면 볼 수 있다. 다음은 이 커맨드 실행을 통한 이벤트 출력의 몇 가지 예다.

Events:
  FirstSeen  LastSeen  Count  From                                                   SubObjectPath          Type      Reason               Message
  ---------  --------  -----  ----                                                   -------------          --------  ------               -------
  1m         1m        1      {default-scheduler }                                                          Normal    Scheduled            Successfully assigned test-1730497541-cq1d2 to gke-test-cluster-default-pool-a07e5d30-siqd
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Pulling              pulling image "test:1.0"
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Created              Created container with docker id 5c6a256a2567; Security:[seccomp=unconfined]
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Pulled               Successfully pulled image "test:1.0"
  1m         1m        1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Started              Started container with docker id 5c6a256a2567
  38s        38s       1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Killing              Killing container with docker id 5c6a256a2567: PostStart handler: Error executing in Docker Container: 1
  37s        37s       1      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Normal    Killing              Killing container with docker id 8df9fdfd7054: PostStart handler: Error executing in Docker Container: 1
  38s        37s       2      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}                         Warning   FailedSync           Error syncing pod, skipping: failed to "StartContainer" for "main" with RunContainerError: "PostStart handler: Error executing in Docker Container: 1"
  1m         22s       2      {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}  spec.containers{main}  Warning   FailedPostStartHook

다음 내용

3.4 - 워크로드

쿠버네티스에서 배포할 수 있는 가장 작은 컴퓨트 오브젝트인 파드와, 이를 실행하는 데 도움이 되는 하이-레벨(higher-level) 추상화

워크로드는 쿠버네티스에서 구동되는 애플리케이션이다. 워크로드가 단일 컴포넌트이거나 함께 작동하는 여러 컴포넌트이든 관계없이, 쿠버네티스에서는 워크로드를 일련의 파드 집합 내에서 실행한다. 쿠버네티스에서 Pod 는 클러스터에서 실행 중인 컨테이너 집합을 나타낸다.

쿠버네티스 파드에는 정의된 라이프사이클이 있다. 예를 들어, 일단 파드가 클러스터에서 실행되고 나서 해당 파드가 동작 중인 노드에 심각한 오류가 발생하면 해당 노드의 모든 파드가 실패한다. 쿠버네티스는 이 수준의 실패를 최종(final)으로 취급한다. 사용자는 향후 노드가 복구되는 것과 상관 없이 Pod 를 새로 생성해야 한다.

그러나, 작업이 훨씬 쉽도록, 각 Pod 를 직접 관리할 필요는 없도록 만들었다. 대신, 사용자를 대신하여 파드 집합을 관리하는 워크로드 리소스 를 사용할 수 있다. 이러한 리소스는 지정한 상태와 일치하도록 올바른 수의 올바른 파드 유형이 실행되고 있는지 확인하는 컨트롤러를 구성한다.

쿠버네티스는 다음과 같이 여러 가지 빌트인(built-in) 워크로드 리소스를 제공한다.

  • DeploymentReplicaSet (레거시 리소스 레플리케이션컨트롤러(ReplicationController)를 대체). DeploymentDeployment 의 모든 Pod 가 필요 시 교체 또는 상호 교체 가능한 경우, 클러스터의 스테이트리스 애플리케이션 워크로드를 관리하기에 적합하다.
  • StatefulSet는 어떻게든 스테이트(state)를 추적하는 하나 이상의 파드를 동작하게 해준다. 예를 들면, 워크로드가 데이터를 지속적으로 기록하는 경우, 사용자는 PodPersistentVolume을 연계하는 StatefulSet 을 실행할 수 있다. 전체적인 회복력 향상을 위해서, StatefulSetPods 에서 동작 중인 코드는 동일한 StatefulSet 의 다른 Pods 로 데이터를 복제할 수 있다.
  • DaemonSet은 노드-로컬 기능(node-local facilities)을 제공하는 Pods 를 정의한다. 이러한 기능들은 클러스터를 운용하는 데 기본적인 것일 것이다. 예를 들면, 네트워킹 지원 도구 또는 add-on 등이 있다. DaemonSet 의 명세에 맞는 노드를 클러스터에 추가할 때마다, 컨트롤 플레인은 해당 신규 노드에 DaemonSet 을 위한 Pod 를 스케줄한다.
  • JobCronJob은 실행 완료 후 중단되는 작업을 정의한다. CronJobs 이 스케줄에 따라 반복되는 반면, 잡은 단 한 번의 작업을 나타낸다.

더 넓은 쿠버네티스 에코시스템 내에서는 추가적인 동작을 제공하는 제 3자의 워크로드 리소스도 찾을 수 있다. 커스텀 리소스 데피니션을 사용하면, 쿠버네티스 코어에서 제공하지 않는 특별한 동작을 원하는 경우 제 3자의 워크로드 리소스를 추가할 수 있다. 예를 들어, 사용자 애플리케이션을 위한 Pods 의 그룹을 실행하되 모든 파드가 가용한 경우가 아닌 경우 멈추고 싶다면(아마도 높은 처리량의 분산 처리를 하는 상황 같은), 사용자는 해당 기능을 제공하는 확장을 구현하거나 설치할 수 있다.

다음 내용

각 리소스에 대해 읽을 수 있을 뿐만 아니라, 리소스와 관련된 특정 작업에 대해서도 알아볼 수 있다.

코드를 구성(configuration)에서 분리하는 쿠버네티스의 메커니즘을 배우기 위해서는, 구성을 참고하길 바란다.

다음은 쿠버네티스가 애플리케이션의 파드를 어떻게 관리하는지를 알 수 있게 해주는 두 가지 개념이다.

일단 애플리케이션이 실행되면, 인터넷에서 서비스로 사용하거나, 웹 애플리케이션의 경우에만 인그레스(Ingress)를 이용하여 사용할 수 있다.

3.4.1 - 파드

파드(Pod) 는 쿠버네티스에서 생성하고 관리할 수 있는 배포 가능한 가장 작은 컴퓨팅 단위이다.

파드 (고래 떼(pod of whales)나 콩꼬투리(pea pod)와 마찬가지로)는 하나 이상의 컨테이너의 그룹이다. 이 그룹은 스토리지 및 네트워크를 공유하고, 해당 컨테이너를 구동하는 방식에 대한 명세를 갖는다. 파드의 콘텐츠는 항상 함께 배치되고, 함께 스케줄되며, 공유 콘텍스트에서 실행된다. 파드는 애플리케이션 별 "논리 호스트"를 모델링한다. 여기에는 상대적으로 밀접하게 결합된 하나 이상의 애플리케이션 컨테이너가 포함된다. 클라우드가 아닌 콘텍스트에서, 동일한 물리 또는 가상 머신에서 실행되는 애플리케이션은 동일한 논리 호스트에서 실행되는 클라우드 애플리케이션과 비슷하다.

애플리케이션 컨테이너와 마찬가지로, 파드에는 파드 시작 중에 실행되는 초기화 컨테이너가 포함될 수 있다. 클러스터가 제공하는 경우, 디버깅을 위해 임시 컨테이너를 삽입할 수도 있다.

파드란 무엇인가?

파드의 공유 콘텍스트는 리눅스 네임스페이스, 컨트롤 그룹(cgroup) 및 도커 컨테이너를 격리하는 것과 같이 잠재적으로 다른 격리 요소들이다. 파드의 콘텍스트 내에서 개별 애플리케이션은 추가적으로 하위 격리가 적용된다.

도커 개념 측면에서, 파드는 공유 네임스페이스와 공유 파일시스템 볼륨이 있는 도커 컨테이너 그룹과 비슷하다.

파드의 사용

다음은 nginx:1.14.2 이미지를 실행하는 컨테이너로 구성되는 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

위에서 설명한 파드를 생성하려면, 다음 명령을 실행한다.

kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

일반적으로 파드는 직접 생성하지는 않으며, 대신 워크로드 리소스를 사용하여 생성한다. 파드 작업 섹션에서 파드와 워크로드 리소스의 관계에 대한 더 많은 정보를 확인한다.

Workload resources for managing pods

일반적으로 싱글톤(singleton) 파드를 포함하여 파드를 직접 만들 필요가 없다. 대신, 디플로이먼트(Deployment) 또는 잡(Job)과 같은 워크로드 리소스를 사용하여 생성한다. 파드가 상태를 추적해야 한다면, 스테이트풀셋(StatefulSet) 리소스를 고려한다.

쿠버네티스 클러스터의 파드는 두 가지 주요 방식으로 사용된다.

  • 단일 컨테이너를 실행하는 파드. "파드 당 하나의 컨테이너" 모델은 가장 일반적인 쿠버네티스 유스케이스이다. 이 경우, 파드를 단일 컨테이너를 둘러싼 래퍼(wrapper)로 생각할 수 있다. 쿠버네티스는 컨테이너를 직접 관리하는 대신 파드를 관리한다.

  • 함께 작동해야 하는 여러 컨테이너를 실행하는 파드. 파드는 밀접하게 결합되어 있고 리소스를 공유해야 하는 함께 배치된 여러 개의 컨테이너로 구성된 애플리케이션을 캡슐화할 수 있다. 이런 함께 배치된 컨테이너는 하나의 결합된 서비스 단위를 형성한다. 예를 들어, 하나의 컨테이너는 공유 볼륨에 저장된 데이터를 퍼블릭에 제공하는 반면, 별도의 사이드카 컨테이너는 해당 파일을 새로 고치거나 업데이트한다. 파드는 이러한 컨테이너, 스토리지 리소스, 임시 네트워크 ID를 단일 단위로 함께 래핑한다.

각 파드는 특정 애플리케이션의 단일 인스턴스를 실행하기 위한 것이다. 더 많은 인스턴스를 실행하여 더 많은 전체 리소스를 제공하기 위해 애플리케이션을 수평적으로 확장하려면, 각 인스턴스에 하나씩, 여러 파드를 사용해야 한다. 쿠버네티스에서는 이를 일반적으로 레플리케이션 이라고 한다. 복제된 파드는 일반적으로 워크로드 리소스와 해당 컨트롤러에 의해 그룹으로 생성되고 관리된다.

쿠버네티스가 워크로드 리소스와 해당 컨트롤러를 사용하여 애플리케이션 스케일링과 자동 복구를 구현하는 방법에 대한 자세한 내용은 파드와 컨트롤러를 참고한다.

파드가 여러 컨테이너를 관리하는 방법

파드는 응집력있는 서비스 단위를 형성하는 여러 협력 프로세스(컨테이너)를 지원하도록 설계되었다. 파드의 컨테이너는 클러스터의 동일한 물리 또는 가상 머신에서 자동으로 같은 위치에 배치되고 함께 스케줄된다. 컨테이너는 리소스와 의존성을 공유하고, 서로 통신하고, 종료 시기와 방법을 조정할 수 있다.

예를 들어, 다음 다이어그램에서와 같이 공유 볼륨의 파일에 대한 웹 서버 역할을 하는 컨테이너와, 원격 소스에서 해당 파일을 업데이트하는 별도의 "사이드카" 컨테이너가 있을 수 있다.

파드 생성 다이어그램

일부 파드에는 앱 컨테이너 뿐만 아니라 초기화 컨테이너를 갖고 있다. 초기화 컨테이너는 앱 컨테이너가 시작되기 전에 실행되고 완료된다.

파드는 기본적으로 파드에 속한 컨테이너에 네트워킹스토리지라는 두 가지 종류의 공유 ​​리소스를 제공한다.

파드 작업

사용자가 쿠버네티스에서 직접 개별 파드를 만드는 경우는 거의 없다. 싱글톤 파드도 마찬가지이다. 이는 파드가 상대적으로 일시적인, 일회용 엔티티로 설계되었기 때문이다. 파드가 생성될 때(사용자가 직접 또는 컨트롤러가 간접적으로), 새 파드는 클러스터의 노드에서 실행되도록 스케줄된다. 파드는 파드 실행이 완료되거나, 파드 오브젝트가 삭제되거나, 리소스 부족으로 인해 파드가 축출 되거나, 노드가 실패할 때까지 해당 노드에 남아있다.

파드 오브젝트에 대한 매니페스트를 만들 때, 지정된 이름이 유효한 DNS 서브도메인 이름인지 확인한다.

파드와 컨트롤러

워크로드 리소스를 사용하여 여러 파드를 만들고 관리할 수 있다. 리소스에 대한 컨트롤러는 파드 장애 시 복제 및 롤아웃과 자동 복구를 처리한다. 예를 들어, 노드가 실패하면, 컨트롤러는 해당 노드의 파드가 작동을 중지했음을 인식하고 대체 파드를 생성한다. 스케줄러는 대체 파드를 정상 노드에 배치한다.

다음은 하나 이상의 파드를 관리하는 워크로드 리소스의 몇 가지 예시이다.

파드 템플릿

워크로드 리소스에 대한 컨트롤러는 파드 템플릿 에서 파드를 생성하고 사용자 대신 해당 파드를 관리한다.

파드템플릿(PodTemplate)은 파드를 생성하기 위한 명세이며, 디플로이먼트, 데몬셋과 같은 워크로드 리소스에 포함된다.

워크로드 리소스의 각 컨트롤러는 워크로드 오브젝트 내부의 PodTemplate 을 사용하여 실제 파드를 생성한다. PodTemplate 은 앱을 실행하는 데 사용되는 워크로드 리소스가 무엇이든지 원하는 상태의 일부이다.

아래 샘플은 하나의 컨테이너를 시작하는 template 이 있는 간단한 잡의 매니페스트이다. 해당 파드의 컨테이너는 메시지를 출력한 다음 일시 중지한다.

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # 여기서부터 파드 템플릿이다
    spec:
      containers:
      - name: hello
        image: busybox
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # 여기까지 파드 템플릿이다

파드 템플릿을 수정하거나 새로운 파드 템플릿으로 바꿔도 이미 존재하는 파드에는 직접적인 영향을 주지 않는다. 워크로드 리소스의 파드 템플릿을 변경하는 경우, 해당 리소스는 수정된 템플릿을 사용하는 대체 파드를 생성해야 한다.

예를 들어, 스테이트풀셋 컨트롤러는 실행 중인 파드가 각 스테이트풀셋 오브젝트에 대한 현재 파드 템플릿과 일치하는지 확인한다. 스테이트풀셋을 수정하여 파드 템플릿을 변경하면, 스테이트풀셋이 업데이트된 템플릿을 기반으로 새로운 파드를 생성하기 시작한다. 결국, 모든 이전의 파드가 새로운 파드로 교체되고, 업데이트가 완료된다.

각 워크로드 리소스는 파드 템플릿의 변경 사항을 처리하기 위한 자체 규칙을 구현한다. 스테이트풀셋에 대해 자세히 알아 보려면, 스테이트풀셋 기본 튜토리얼에서 업데이트 전략을 읽어본다.

노드에서 kubelet은 파드 템플릿과 업데이트에 대한 상세 정보를 직접 관찰하거나 관리하지 않는다. 이러한 상세 내용은 추상화된다. 이러한 추상화와 관심사 분리(separation of concerns)는 시스템 시맨틱을 단순화하고, 기존 코드를 변경하지 않고도 클러스터의 동작을 확장할 수 있게 한다.

파드 갱신 및 교체

이전 섹션에서 언급한 바와 같이, 워크로드 리소스의 파드 템플릿이 바뀌면, 컨트롤러는 기존의 파드를 갱신하거나 패치하는 대신 갱신된 템플릿을 기반으로 신규 파드를 생성한다.

쿠버네티스는 사용자가 파드를 직접 관리하는 것을 막지는 않는다. 동작 중인 파드의 필드를 갱신하는 것도 가능하다. 그러나, patchreplace와 같은 파드 갱신 작업에는 다음과 같은 제약이 있다.

  • 파드에 대한 대부분의 메타데이터는 불변(immutable)이다. 예를 들면, 사용자는 namespace, name, uid, 또는 creationTimestamp 필드를 변경할 수 없다. 그리고 generation 필드는 고유하다. 이 필드는 필드의 현재 값을 증가시키는 갱신만 허용한다.

  • metadata.deletionTimestamp 가 설정된 경우, metadata.finalizers 리스트에 새로운 항목이 추가될 수 없다.

  • 파드 갱신은 spec.containers[*].image, spec.initContainers[*].image, spec.activeDeadlineSeconds, 또는 spec.tolerations 이외의 필드는 변경하지 않을 것이다. spec.tolerations 에 대해서만 새로운 항목을 추가할 수 있다.

  • spec.activeDeadlineSeconds 필드를 추가할 때는, 다음의 두 가지 형태의 갱신만 허용한다.

    1. 지정되지 않은 필드를 양수로 설정;
    2. 필드의 양수를 음수가 아닌 더 작은 숫자로 갱신.

리소스 공유와 통신

파드는 파드에 속한 컨테이너 간의 데이터 공유와 통신을 지원한다.

파드 스토리지

파드는 공유 스토리지 볼륨의 집합을 지정할 수 있다. 파드의 모든 컨테이너는 공유 볼륨에 접근할 수 있으므로, 해당 컨테이너가 데이터를 공유할 수 있다. 또한 볼륨은 내부 컨테이너 중 하나를 다시 시작해야 하는 경우 파드의 영구 데이터를 유지하도록 허용한다. 쿠버네티스가 공유 스토리지를 구현하고 파드에서 사용할 수 있도록 하는 방법에 대한 자세한 내용은 스토리지를 참고한다.

파드 네트워킹

각 파드에는 각 주소 패밀리에 대해 고유한 IP 주소가 할당된다. 파드의 모든 컨테이너는 IP 주소와 네트워크 포트를 포함하여 네트워크 네임스페이스를 공유한다. 파드 내부(그때 해당)에서, 파드에 속한 컨테이너는 localhost 를 사용하여 서로 통신할 수 있다. 파드의 컨테이너가 파드 외부의 엔티티와 통신할 때, 공유 네트워크 리소스(포트와 같은)를 사용하는 방법을 조정해야 한다. 파드 내에서 컨테이너는 IP 주소와 포트 공간을 공유하며, localhost 를 통해 서로를 찾을 수 있다. 파드의 컨테이너는 SystemV 세마포어 또는 POSIX 공유 메모리와 같은 표준 프로세스 간 통신을 사용하여 서로 통신할 수도 있다. 다른 파드의 컨테이너는 고유한 IP 주소를 가지며 특별한 구성 없이 IPC로 통신할 수 없다. 다른 파드에서 실행되는 컨테이너와 상호 작용하려는 컨테이너는 IP 네트워킹을 사용하여 통신할 수 있다.

파드 내의 컨테이너는 시스템 호스트명이 파드에 대해 구성된 name 과 동일한 것으로 간주한다. 네트워킹 섹션에 이에 대한 자세한 내용이 있다.

컨테이너에 대한 특권 모드

리눅스에서, 파드의 모든 컨테이너는 컨테이너 명세의 보안 컨텍스트에 있는 privileged (리눅스) 플래그를 사용하여 특권 모드를 활성화할 수 있다. 이는 네트워크 스택 조작이나 하드웨어 장치 접근과 같은 운영 체제 관리 기능을 사용하려는 컨테이너에 유용하다.

클러스터가 WindowsHostProcessContainers 기능을 활성화하였다면, 파드 스펙의 보안 컨텍스트의 windowsOptions.hostProcess 에 의해 윈도우 HostProcess 파드를 생성할 수 있다. 이러한 모든 컨테이너는 윈도우 HostProcess 컨테이너로 실행해야 한다. HostProcess 파드는 직접적으로 호스트에서 실행하는 것으로, 리눅스 특권있는 컨테이너에서 수행되는 관리 태스크 수행에도 사용할 수 있다. 파드의 모든 컨테이너는 윈도우 HostProcess 컨테이너로 반드시 실행해야 한다. HostProcess 파드는 호스트에서 직접 실행되며 리눅스 특권있는 컨테이너에서 수행되는 것과 같은 관리 작업을 수행하는데도 사용할 수 있다.

정적 파드

정적 파드API 서버가 관찰하는 대신 특정 노드의 kubelet 데몬에 의해 직접 관리된다. 대부분의 파드는 컨트롤 플레인(예를 들어, 디플로이먼트)에 의해 관리되고, 정적 파드의 경우, kubelet이 각 정적 파드를 직접 감독한다(실패하면 다시 시작한다).

정적 파드는 항상 특정 노드의 Kubelet 하나에 바인딩된다. 정적 파드의 주요 용도는 자체 호스팅 컨트롤 플레인을 실행하는 것이다. 즉, kubelet을 사용하여 개별 컨트롤 플레인 컴포넌트를 감독한다.

kubelet은 자동으로 각 정적 파드에 대한 쿠버네티스 API 서버에서 미러 파드를 생성하려고 한다. 즉, 노드에서 실행되는 파드는 API 서버에서 보이지만, 여기에서 제어할 수는 없다는 의미이다.

컨테이너 프로브

_프로브_는 컨테이너의 kubelet에 의해 주기적으로 실행되는 진단이다. 진단을 수행하기 위하여 kubelet은 다음과 같은 작업을 호출할 수 있다.

  • ExecAction (컨테이너 런타임의 도움을 받아 수행)
  • TCPSocketAction (kubelet에 의해 직접 검사)
  • HTTPGetAction (kubelet에 의해 직접 검사)

프로브에 대한 자세한 내용은 파드 라이프사이클 문서를 참고한다.

다음 내용

쿠버네티스가 다른 리소스(스테이트풀셋이나 디플로이먼트와 같은)에서 공통 파드 API를 래핑하는 이유에 대한 콘텍스트를 이해하기 위해서, 다음과 같은 선행 기술에 대해 읽어볼 수 있다.

3.4.1.1 - 파드 라이프사이클

이 페이지에서는 파드의 라이프사이클을 설명한다. 파드는 정의된 라이프사이클을 따른다. Pending 단계에서 시작해서, 기본 컨테이너 중 적어도 하나 이상이 OK로 시작하면 Running 단계를 통과하고, 그런 다음 파드의 컨테이너가 실패로 종료되었는지 여부에 따라 Succeeded 또는 Failed 단계로 이동한다.

파드가 실행되는 동안, kubelet은 일종의 오류를 처리하기 위해 컨테이너를 다시 시작할 수 있다. 파드 내에서, 쿠버네티스는 다양한 컨테이너 상태를 추적하고 파드를 다시 정상 상태로 만들기 위해 취할 조치를 결정한다.

쿠버네티스 API에서 파드는 명세와 실제 상태를 모두 가진다. 파드 오브젝트의 상태는 일련의 파드 조건으로 구성된다. 사용자의 애플리케이션에 유용한 경우, 파드의 조건 데이터에 사용자 정의 준비성 정보를 삽입할 수도 있다.

파드는 파드의 수명 중 한 번만 스케줄된다. 파드가 노드에 스케줄(할당)되면, 파드는 중지되거나 종료될 때까지 해당 노드에서 실행된다.

파드의 수명

개별 애플리케이션 컨테이너와 마찬가지로, 파드는 비교적 임시(계속 이어지는 것이 아닌) 엔티티로 간주된다. 파드가 생성되고, 고유 ID(UID)가 할당되고, 종료(재시작 정책에 따라) 또는 삭제될 때까지 남아있는 노드에 스케줄된다. 만약 노드가 종료되면, 해당 노드에 스케줄된 파드는 타임아웃 기간 후에 삭제되도록 스케줄된다.

파드는 자체적으로 자가 치유되지 않는다. 파드가 노드에 스케줄된 후에 해당 노드가 실패하면, 파드는 삭제된다. 마찬가지로, 파드는 리소스 부족 또는 노드 유지 관리 작업으로 인한 축출에서 살아남지 못한다. 쿠버네티스는 컨트롤러라 부르는 하이-레벨 추상화를 사용하여 상대적으로 일회용인 파드 인스턴스를 관리하는 작업을 처리한다.

UID로 정의된 특정 파드는 다른 노드로 절대 "다시 스케줄"되지 않는다. 대신, 해당 파드는 사용자가 원한다면 이름은 같지만, UID가 다른, 거의 동일한 새 파드로 대체될 수 있다.

볼륨과 같은 어떤 것이 파드와 동일한 수명을 갖는다는 것은, 특정 파드(정확한 UID 포함)가 존재하는 한 그것이 존재함을 의미한다. 어떤 이유로든 해당 파드가 삭제되고, 동일한 대체 파드가 생성되더라도, 관련된 그것(이 예에서는 볼륨)도 폐기되고 새로 생성된다.

Pod diagram

컨테이너 간의 공유 스토리지에 퍼시스턴트 볼륨을 사용하는 웹 서버와 파일 풀러(puller)가 포함된 다중 컨테이너 파드이다.

파드의 단계

파드의 status 필드는 phase 필드를 포함하는 PodStatus 오브젝트로 정의된다.

파드의 phase는 파드가 라이프사이클 중 어느 단계에 해당하는지 표현하는 간단한 고수준의 요약이다. Phase는 컨테이너나 파드의 관측 정보에 대한 포괄적인 롤업이나, 포괄적인 상태 머신을 표현하도록 의도되지는 않았다.

파드 phase 값에서 숫자와 의미는 엄격하게 지켜진다. 여기에 문서화된 내용 이외에는, 파드와 파드에 주어진 phase 값에 대해서 어떤 사항도 가정되어서는 안 된다.

phase에 가능한 값은 다음과 같다.

의미
Pending 파드가 쿠버네티스 클러스터에서 승인되었지만, 하나 이상의 컨테이너가 설정되지 않았고 실행할 준비가 되지 않았다. 여기에는 파드가 스케줄되기 이전까지의 시간 뿐만 아니라 네트워크를 통한 컨테이너 이미지 다운로드 시간도 포함된다.
Running 파드가 노드에 바인딩되었고, 모든 컨테이너가 생성되었다. 적어도 하나의 컨테이너가 아직 실행 중이거나, 시작 또는 재시작 중에 있다.
Succeeded 파드에 있는 모든 컨테이너들이 성공적으로 종료되었고, 재시작되지 않을 것이다.
Failed 파드에 있는 모든 컨테이너가 종료되었고, 적어도 하나 이상의 컨테이너가 실패로 종료되었다. 즉, 해당 컨테이너는 non-zero 상태로 빠져나왔거나(exited) 시스템에 의해서 종료(terminated)되었다.
Unknown 어떤 이유에 의해서 파드의 상태를 얻을 수 없다. 이 단계는 일반적으로 파드가 실행되어야 하는 노드와의 통신 오류로 인해 발생한다.

노드가 죽거나 클러스터의 나머지와의 연결이 끊어지면, 쿠버네티스는 손실된 노드의 모든 파드의 phase 를 Failed로 설정하는 정책을 적용한다.

컨테이너 상태

전체 파드의 단계뿐 아니라, 쿠버네티스는 파드 내부의 각 컨테이너 상태를 추적한다. 컨테이너 라이프사이클 훅(hook)을 사용하여 컨테이너 라이프사이클의 특정 지점에서 실행할 이벤트를 트리거할 수 있다.

일단 스케줄러가 노드에 파드를 할당하면, kubelet은 컨테이너 런타임을 사용하여 해당 파드에 대한 컨테이너 생성을 시작한다. 표시될 수 있는 세 가지 컨테이너 상태는 Waiting, Running 그리고 Terminated 이다.

파드의 컨테이너 상태를 확인하려면, kubectl describe pod <name-of-pod> 를 사용할 수 있다. 출력 결과는 해당 파드 내의 각 컨테이너 상태가 표시된다.

각 상태에는 특정한 의미가 있다.

Waiting

만약 컨테이너가 Running 또는 Terminated 상태가 아니면, Waiting 상태이다. Waiting 상태의 컨테이너는 시작을 완료하는 데 필요한 작업(예를 들어, 컨테이너 이미지 레지스트리에서 컨테이너 이미지 가져오거나, 시크릿(Secret) 데이터를 적용하는 작업)을 계속 실행하고 있는 중이다. kubectl 을 사용하여 컨테이너가 Waiting 인 파드를 쿼리하면, 컨테이너가 해당 상태에 있는 이유를 요약하는 Reason 필드도 표시된다.

Running

Running 상태는 컨테이너가 문제없이 실행되고 있음을 나타낸다. postStart 훅이 구성되어 있었다면, 이미 실행되고 완료되었다. kubectl 을 사용하여 컨테이너가 Running 인 파드를 쿼리하면, 컨테이너가 Running 상태에 진입한 시기에 대한 정보도 볼 수 있다.

Terminated

Terminated 상태의 컨테이너는 실행을 시작한 다음 완료될 때까지 실행되었거나 어떤 이유로 실패했다. kubectl 을 사용하여 컨테이너가 Terminated 인 파드를 쿼리하면, 이유와 종료 코드 그리고 해당 컨테이너의 실행 기간에 대한 시작과 종료 시간이 표시된다.

컨테이너에 구성된 preStop 훅이 있는 경우, 컨테이너가 Terminated 상태에 들어가기 전에 실행된다.

컨테이너 재시작 정책

파드의 spec 에는 restartPolicy 필드가 있다. 사용 가능한 값은 Always, OnFailure 그리고 Never이다. 기본값은 Always이다.

restartPolicy 는 파드의 모든 컨테이너에 적용된다. restartPolicy 는 동일한 노드에서 kubelet에 의한 컨테이너 재시작만을 의미한다. 파드의 컨테이너가 종료된 후, kubelet은 5분으로 제한되는 지수 백오프 지연(10초, 20초, 40초, …)으로 컨테이너를 재시작한다. 컨테이너가 10분 동안 아무런 문제없이 실행되면, kubelet은 해당 컨테이너의 재시작 백오프 타이머를 재설정한다.

파드의 조건

파드는 하나의 PodStatus를 가지며, 그것은 파드가 통과했거나 통과하지 못한 조건에 대한 PodConditions 배열을 가진다.

  • PodScheduled: 파드가 노드에 스케줄되었다.
  • ContainersReady: 파드의 모든 컨테이너가 준비되었다.
  • Initialized: 모든 초기화 컨테이너가 성공적으로 시작되었다.
  • Ready: 파드는 요청을 처리할 수 있으며 일치하는 모든 서비스의 로드 밸런싱 풀에 추가되어야 한다.
필드 이름 설명
type 이 파드 조건의 이름이다.
status 가능한 값이 "True", "False", 또는 "Unknown"으로, 해당 조건이 적용 가능한지 여부를 나타낸다.
lastProbeTime 파드 조건이 마지막으로 프로브된 시간의 타임스탬프이다.
lastTransitionTime 파드가 한 상태에서 다른 상태로 전환된 마지막 시간에 대한 타임스탬프이다.
reason 조건의 마지막 전환에 대한 이유를 나타내는 기계가 판독 가능한 UpperCamelCase 텍스트이다.
message 마지막 상태 전환에 대한 세부 정보를 나타내는 사람이 읽을 수 있는 메시지이다.

파드의 준비성(readiness)

FEATURE STATE: Kubernetes v1.14 [stable]

애플리케이션은 추가 피드백 또는 신호를 PodStatus: Pod readiness 와 같이 주입할 수 있다. 이를 사용하기 위해, kubelet이 파드의 준비성을 평가하기 위한 추가적인 조건들을 파드의 specreadinessGate 필드를 통해서 지정할 수 있다.

준비성 게이트는 파드에 대한 status.condition 필드의 현재 상태에 따라 결정된다. 만약 쿠버네티스가 status.conditions 필드에서 해당하는 조건을 찾지 못한다면, 그 조건의 상태는 기본 값인 "False"가 된다.

여기 예제가 있다.

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready                              # 내장된 PodCondition이다
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"        # 추가적인 PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

추가하는 파드 상태에는 쿠버네티스 레이블 키 포맷을 충족하는 이름이 있어야 한다.

파드 준비성 상태

kubectl patch 명령어는 아직 오브젝트 상태 패치(patching)를 지원하지 않는다. 이러한 status.conditions 을 파드에 설정하려면 애플리케이션과 오퍼레이터PATCH 액션을 필요로 한다. 쿠버네티스 클라이언트 라이브러리를 사용해서 파드 준비성에 대한 사용자 지정 파드 조건을 설정하는 코드를 작성할 수 있다.

사용자 지정 조건을 사용하는 파드의 경우, 다음 두 조건이 모두 적용되는 경우에 해당 파드가 준비된 것으로 평가된다.

  • 파드 내의 모든 컨테이너들이 준비 상태이다.
  • readinessGates에 지정된 모든 조건들이 True 이다.

파드의 컨테이너가 Ready 이나 적어도 한 개의 사용자 지정 조건이 빠졌거나 False 이면, kubelet은 파드의 조건ContainerReady 로 설정한다.

컨테이너 프로브(probe)

프로브는 컨테이너에서 kubelet에 의해 주기적으로 수행되는 진단(diagnostic)이다. 진단을 수행하기 위해서, kubelet은 컨테이너에 의해서 구현된 핸들러를 호출한다. 핸들러에는 다음과 같이 세 가지 타입이 있다.

  • ExecAction은 컨테이너 내에서 지정된 명령어를 실행한다. 명령어가 상태 코드 0으로 종료되면 진단이 성공한 것으로 간주한다.

  • TCPSocketAction은 지정된 포트에서 컨테이너의 IP주소에 대해 TCP 검사를 수행한다. 포트가 활성화되어 있다면 진단이 성공한 것으로 간주한다.

  • HTTPGetAction은 지정한 포트 및 경로에서 컨테이너의 IP주소에 대한 HTTP GET 요청을 수행한다. 응답의 상태 코드가 200 이상 400 미만이면 진단이 성공한 것으로 간주한다.

각 probe는 다음 세 가지 결과 중 하나를 가진다.

  • Success: 컨테이너가 진단을 통과함.
  • Failure: 컨테이너가 진단에 실패함.
  • Unknown: 진단 자체가 실패하였으므로 아무런 액션도 수행되면 안됨.

kubelet은 실행 중인 컨테이너들에 대해서 선택적으로 세 가지 종류의 프로브를 수행하고 그에 반응할 수 있다.

  • livenessProbe: 컨테이너가 동작 중인지 여부를 나타낸다. 만약 활성 프로브(liveness probe)에 실패한다면, kubelet은 컨테이너를 죽이고, 해당 컨테이너는 재시작 정책의 대상이 된다. 만약 컨테이너가 활성 프로브를 제공하지 않는 경우, 기본 상태는 Success이다.

  • readinessProbe: 컨테이너가 요청을 처리할 준비가 되었는지 여부를 나타낸다. 만약 준비성 프로브(readiness probe)가 실패한다면, 엔드포인트 컨트롤러는 파드에 연관된 모든 서비스들의 엔드포인트에서 파드의 IP주소를 제거한다. 준비성 프로브의 초기 지연 이전의 기본 상태는 Failure이다. 만약 컨테이너가 준비성 프로브를 지원하지 않는다면, 기본 상태는 Success이다.

  • startupProbe: 컨테이너 내의 애플리케이션이 시작되었는지를 나타낸다. 스타트업 프로브(startup probe)가 주어진 경우, 성공할 때까지 다른 나머지 프로브는 활성화되지 않는다. 만약 스타트업 프로브가 실패하면, kubelet이 컨테이너를 죽이고, 컨테이너는 재시작 정책에 따라 처리된다. 컨테이너에 스타트업 프로브가 없는 경우, 기본 상태는 Success이다.

활성, 준비성 및 스타트업 프로브를 설정하는 방법에 대한 추가적인 정보는, 활성, 준비성 및 스타트업 프로브 설정하기를 참조하면 된다.

언제 활성 프로브를 사용해야 하는가?

FEATURE STATE: Kubernetes v1.0 [stable]

만약 컨테이너 속 프로세스가 어떠한 이슈에 직면하거나 건강하지 못한 상태(unhealthy)가 되는 등 프로세스 자체의 문제로 중단될 수 있더라도, 활성 프로브가 반드시 필요한 것은 아니다. 그 경우에는 kubelet이 파드의 restartPolicy에 따라서 올바른 대처를 자동적으로 수행할 것이다.

프로브가 실패한 후 컨테이너가 종료되거나 재시작되길 원한다면, 활성 프로브를 지정하고, restartPolicy를 항상(Always) 또는 실패 시(OnFailure)로 지정한다.

언제 준비성 프로브를 사용해야 하는가?

FEATURE STATE: Kubernetes v1.0 [stable]

프로브가 성공한 경우에만 파드에 트래픽 전송을 시작하려고 한다면, 준비성 프로브를 지정하길 바란다. 이 경우에서는, 준비성 프로브가 활성 프로브와 유사해 보일 수도 있지만, 스팩에 준비성 프로브가 존재한다는 것은 파드가 트래픽을 받지 않는 상태에서 시작되고 프로브가 성공하기 시작한 이후에만 트래픽을 받는다는 뜻이다.

만약 컨테이너가 유지 관리를 위해서 자체 중단되게 하려면, 준비성 프로브를 지정하길 바란다. 준비성 프로브는 활성 프로브와는 다르게 준비성에 특정된 엔드포인트를 확인한다.

만약 애플리케이션이 백엔드 서비스에 엄격한 의존성이 있다면, 활성 프로브와 준비성 프로브 모두 활용할 수도 있다. 활성 프로브는 애플리케이션 스스로가 건강한 상태면 통과하지만, 준비성 프로브는 추가적으로 요구되는 각 백-엔드 서비스가 가용한지 확인한다. 이를 이용하여, 오류 메시지만 응답하는 파드로 트래픽이 가는 것을 막을 수 있다.

만약 컨테이너가 시동 시 대량 데이터의 로딩, 구성 파일, 또는 마이그레이션에 대한 작업을 수행해야 한다면, 스타트업 프로브를 사용하면 된다. 그러나, 만약 failed 애플리케이션과 시동 중에 아직 데이터를 처리하고 있는 애플리케이션을 구분하여 탐지하고 싶다면, 준비성 프로브를 사용하는 것이 더 적합할 것이다.

언제 스타트업 프로브를 사용해야 하는가?

FEATURE STATE: Kubernetes v1.20 [stable]

스타트업 프로브는 서비스를 시작하는 데 오랜 시간이 걸리는 컨테이너가 있는 파드에 유용하다. 긴 활성 간격을 설정하는 대신, 컨테이너가 시작될 때 프로브를 위한 별도의 구성을 설정하여, 활성 간격보다 긴 시간을 허용할 수 있다.

컨테이너가 보통 initialDelaySeconds + failureThreshold × periodSeconds 이후에 기동된다면, 스타트업 프로브가 활성화 프로브와 같은 엔드포인트를 확인하도록 지정해야 한다. periodSeconds의 기본값은 10s 이다. 이 때 컨테이너가 활성화 프로브의 기본값 변경 없이 기동되도록 하려면, failureThreshold 를 충분히 높게 설정해주어야 한다. 그래야 데드락(deadlocks)을 방지하는데 도움이 된다.

파드의 종료

파드는 클러스터의 노드에서 실행되는 프로세스를 나타내므로, 해당 프로세스가 더 이상 필요하지 않을 때 정상적으로 종료되도록 하는 것이 중요하다(KILL 시그널로 갑자기 중지되고 정리할 기회가 없는 것 보다).

디자인 목표는 삭제를 요청하고 프로세스가 종료되는 시기를 알 수 있을 뿐만 아니라, 삭제가 결국 완료되도록 하는 것이다. 사용자가 파드의 삭제를 요청하면, 클러스터는 파드가 강제로 종료되기 전에 의도한 유예 기간을 기록하고 추적한다. 강제 종료 추적이 적용되면, kubelet은 정상 종료를 시도한다.

일반적으로, 컨테이너 런타임은 각 컨테이너의 기본 프로세스에 TERM 신호를 전송한다. 많은 컨테이너 런타임은 컨테이너 이미지에 정의된 STOPSIGNAL 값을 존중하며 TERM 대신 이 값을 보낸다. 일단 유예 기간이 만료되면, KILL 시그널이 나머지 프로세스로 전송되고, 그런 다음 파드는 API 서버로부터 삭제된다. 프로세스가 종료될 때까지 기다리는 동안 kubelet 또는 컨테이너 런타임의 관리 서비스가 다시 시작되면, 클러스터는 전체 원래 유예 기간을 포함하여 처음부터 다시 시도한다.

플로우의 예는 다음과 같다.

  1. kubectl 도구를 사용하여 기본 유예 기간(30초)으로 특정 파드를 수동으로 삭제한다.
  2. API 서버의 파드는 유예 기간과 함께 파드가 "dead"로 간주되는 시간으로 업데이트된다. kubectl describe 를 사용하여 삭제하려는 파드를 확인하면, 해당 파드가 "Terminating"으로 표시된다. 파드가 실행 중인 노드에서, kubelet이 파드가 종료된 것(terminating)으로 표시되었음을 확인하는 즉시(정상적인 종료 기간이 설정됨), kubelet은 로컬 파드의 종료 프로세스를 시작한다.
    1. 파드의 컨테이너 중 하나가 preStop 을 정의한 경우, kubelet은 컨테이너 내부에서 해당 훅을 실행한다. 유예 기간이 만료된 후 preStop 훅이 계속 실행되면, kubelet은 2초의 작은 일회성 유예 기간 연장을 요청한다.
    2. kubelet은 컨테이너 런타임을 트리거하여 각 컨테이너 내부의 프로세스 1에 TERM 시그널을 보낸다.
  3. kubelet이 정상 종료를 시작하는 동시에, 컨트롤 플레인은 구성된 셀렉터가 있는 서비스를 나타내는 엔드포인트(Endpoint)(그리고, 활성화된 경우, 엔드포인트슬라이스(EndpointSlice)) 오브젝트에서 종료된 파드를 제거한다. 레플리카셋(ReplicaSet)과 기타 워크로드 리소스는 더 이상 종료된 파드를 유효한 서비스 내 복제본으로 취급하지 않는다. 로드 밸런서(서비스 프록시와 같은)가 종료 유예 기간이 시작되는 즉시 엔드포인트 목록에서 파드를 제거하므로 느리게 종료되는 파드는 트래픽을 계속 제공할 수 없다.
  4. 유예 기간이 만료되면, kubelet은 강제 종료를 트리거한다. 컨테이너 런타임은 SIGKILL 을 파드의 모든 컨테이너에서 여전히 실행 중인 모든 프로세스로 전송한다. kubelet은 해당 컨테이너 런타임이 하나를 사용하는 경우 숨겨진 pause 컨테이너도 정리한다.
  5. kubelet은 유예 기간을 0(즉시 삭제)으로 설정하여, API 서버에서 파드 오브젝트의 강제 삭제를 트리거한다.
  6. API 서버가 파드의 API 오브젝트를 삭제하면, 더 이상 클라이언트에서 볼 수 없다.

강제 파드 종료

기본적으로, 모든 삭제는 30초 이내에는 정상적으로 수행된다. kubectl delete 명령은 기본값을 재정의하고 사용자의 고유한 값을 지정할 수 있는 --grace-period=<seconds> 옵션을 지원한다.

유예 기간을 0 로 강제로 즉시 설정하면 API 서버에서 파드가 삭제된다. 파드가 노드에서 계속 실행 중인 경우, 강제 삭제는 kubelet을 트리거하여 즉시 정리를 시작한다.

강제 삭제가 수행되면, API 서버는 실행 중인 노드에서 파드가 종료되었다는 kubelet의 확인을 기다리지 않는다. API에서 즉시 파드를 제거하므로 동일한 이름으로 새로운 파드를 생성할 수 있다. 노드에서 즉시 종료되도록 설정된 파드는 강제 종료되기 전에 작은 유예 기간이 계속 제공된다.

스테이트풀셋(StatefulSet)의 일부인 파드를 강제 삭제해야 하는 경우, 스테이트풀셋에서 파드를 삭제하기에 대한 태스크 문서를 참고한다.

실패한 파드의 가비지 콜렉션

실패한 파드의 경우, API 오브젝트는 사람이나 컨트롤러 프로세스가 명시적으로 파드를 제거할 때까지 클러스터의 API에 남아 있다.

컨트롤 플레인은 파드 수가 구성된 임계값(kube-controller-manager에서 terminated-pod-gc-threshold 에 의해 결정됨)을 초과할 때 종료된 파드(Succeeded 또는 Failed 단계 포함)를 정리한다. 이렇게 하면 시간이 지남에 따라 파드가 생성되고 종료될 때 리소스 유출이 방지된다.

다음 내용

3.4.1.2 - 초기화 컨테이너

이 페이지는 초기화 컨테이너에 대한 개요를 제공한다. 초기화 컨테이너는 파드의 앱 컨테이너들이 실행되기 전에 실행되는 특수한 컨테이너이며, 앱 이미지에는 없는 유틸리티 또는 설정 스크립트 등을 포함할 수 있다.

초기화 컨테이너는 containers 배열(앱 컨테이너를 기술하는)과 나란히 파드 스펙에 명시할 수 있다.

초기화 컨테이너 이해하기

파드는 앱들을 실행하는 다수의 컨테이너를 포함할 수 있고, 또한 앱 컨테이너 실행 전에 동작되는 하나 이상의 초기화 컨테이너도 포함할 수 있다.

다음의 경우를 제외하면, 초기화 컨테이너는 일반적인 컨테이너와 매우 유사하다.

  • 초기화 컨테이너는 항상 완료를 목표로 실행된다.
  • 각 초기화 컨테이너는 다음 초기화 컨테이너가 시작되기 전에 성공적으로 완료되어야 한다.

만약 파드의 초기화 컨테이너가 실패하면, kubelet은 초기화 컨테이너가 성공할 때까지 반복적으로 재시작한다. 그러나, 만약 파드의 restartPolicy 를 절대 하지 않음(Never)으로 설정하고, 해당 파드를 시작하는 동안 초기화 컨테이너가 실패하면, 쿠버네티스는 전체 파드를 실패한 것으로 처리한다.

컨테이너를 초기화 컨테이너로 지정하기 위해서는, 파드 스펙initContainers 필드를 container 항목(앱 container 필드 및 내용과 유사한)들의 배열로서 추가한다. 컨테이너에 대한 더 상세한 사항은 API 레퍼런스를 참고한다.

초기화 컨테이너의 상태는 컨테이너 상태의 배열(.status.containerStatuses 필드와 유사)로 .status.initContainerStatuses 필드에 반환된다.

일반적인 컨테이너와의 차이점

초기화 컨테이너는 앱 컨테이너의 리소스 상한(limit), 볼륨, 보안 세팅을 포함한 모든 필드와 기능을 지원한다. 그러나, 초기화 컨테이너를 위한 리소스 요청량과 상한은 리소스에 문서화된 것처럼 다르게 처리된다.

또한, 초기화 컨테이너는 lifecycle, livenessProbe, readinessProbe 또는 startupProbe 를 지원하지 않는다. 왜냐하면 초기화 컨테이너는 파드가 준비 상태가 되기 전에 완료를 목표로 실행되어야 하기 때문이다.

만약 다수의 초기화 컨테이너가 파드에 지정되어 있다면, kubelet은 해당 초기화 컨테이너들을 한 번에 하나씩 실행한다. 각 초기화 컨테이너는 다음 컨테이너를 실행하기 전에 꼭 성공해야 한다. 모든 초기화 컨테이너들이 실행 완료되었을 때, kubelet은 파드의 애플리케이션 컨테이너들을 초기화하고 평소와 같이 실행한다.

초기화 컨테이너 사용하기

초기화 컨테이너는 앱 컨테이너와는 별도의 이미지를 가지고 있기 때문에, 시동(start-up)에 관련된 코드로서 몇 가지 이점을 가진다.

  • 앱 이미지에는 없는 셋업을 위한 유틸리티 또는 맞춤 코드를 포함할 수 있다. 예를 들어, 셋업 중에 단지 sed, awk, python, 또는 dig와 같은 도구를 사용하기 위해서 다른 이미지로부터(FROM) 새로운 이미지를 만들 필요가 없다.
  • 애플리케이션 이미지 빌더와 디플로이어 역할은 독립적으로 동작될 수 있어서 공동의 단일 앱 이미지 형태로 빌드될 필요가 없다.
  • 초기화 컨테이너는 앱 컨테이너와 다른 파일 시스템 뷰를 가지도록 리눅스 네임스페이스를 사용한다. 결과적으로, 초기화 컨테이너에는 앱 컨테이너가 가질 수 없는 시크릿에 접근 권한이 주어질 수 있다.
  • 앱 컨테이너들은 병렬로 실행되는 반면, 초기화 컨테이너들은 어떠한 앱 컨테이너라도 시작되기 전에 실행 완료되어야 하므로, 초기화 컨테이너는 사전 조건들이 충족될 때까지 앱 컨테이너가 시동되는 것을 막거나 지연시키는 간편한 방법을 제공한다.
  • 초기화 컨테이너는 앱 컨테이너 이미지의 보안성을 떨어뜨릴 수도 있는 유틸리티 혹은 커스텀 코드를 안전하게 실행할 수 있다. 불필요한 툴들을 분리한 채로 유지함으로써 앱 컨테이너 이미지의 공격에 대한 노출을 제한할 수 있다.

예제

초기화 컨테이너를 사용하는 방법에 대한 몇 가지 아이디어는 다음과 같다.

  • 다음과 같은 셸 커맨드로, 서비스가 생성될 때까지 기다리기.

    for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
    
  • 다음과 같은 커맨드로, 다운워드 API(Downward API)를 통한 원격 서버에 해당 파드를 등록하기.

    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • 다음과 같은 커맨드로 앱 컨테이너가 시작되기 전에 일정 시간 기다리기.

    sleep 60
    
  • Git 저장소를 볼륨 안에 클론하기.

  • 설정 파일에 값을 지정하고 메인 앱 컨테이너를 위한 설정 파일을 동적으로 생성하기 위한 템플릿 도구를 실행하기. 예를 들어, 설정에 POD_IP 값을 지정하고 메인 앱 설정 파일을 Jinja를 통해서 생성.

사용 중인 초기화 컨테이너

쿠버네티스 1.5에 대한 다음의 yaml 파일은 두 개의 초기화 컨테이너를 포함한 간단한 파드에 대한 개요를 보여준다. 첫 번째는 myservice 를 기다리고 두 번째는 mydb 를 기다린다. 두 컨테이너들이 완료되면, 파드가 시작될 것이다.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

다음 커맨드들을 이용하여 파드를 시작하거나 디버깅할 수 있다.

kubectl apply -f myapp.yaml

출력 결과는 다음과 같다.

pod/myapp-pod created

그리고 파드의 상태를 확인한다.

kubectl get -f myapp.yaml

출력 결과는 다음과 같다.

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

혹은 좀 더 자세히 살펴본다.

kubectl describe -f myapp.yaml

출력 결과는 다음과 같다.

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app=myapp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634

파드의 초기화 컨테이너의 상태를 보기 위해, 다음을 실행한다.

kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb      # Inspect the second init container

mydbmyservice 서비스를 시작하고 나면, 초기화 컨테이너가 완료되고 myapp-pod 가 생성된 것을 볼 수 있다.

여기에 이 서비스를 보이기 위해 사용할 수 있는 구성이 있다.

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

mydbmyservice 서비스 생성하기.

kubectl apply -f services.yaml

출력 결과는 다음과 같다.

service/myservice created
service/mydb created

초기화 컨테이너들이 완료되는 것과 myapp-pod 파드가 Running 상태로 변경되는 것을 볼 것이다.

kubectl get -f myapp.yaml

출력 결과는 다음과 같다.

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

이 간단한 예제는 사용자만의 초기화 컨테이너를 생성하는데 영감을 줄 것이다. 다음 순서에는 더 자세한 예제의 링크가 있다.

자세한 동작

파드 시작 시에 kubelet은 네트워크와 스토리지가 준비될 때까지 초기화 컨테이너의 실행을 지연시킨다. 그런 다음 kubelet은 파드 사양에 나와있는 순서대로 파드의 초기화 컨테이너를 실행한다.

각 초기화 컨테이너는 다음 컨테이너가 시작되기 전에 성공적으로 종료되어야 한다. 만약 런타임 문제나 실패 상태로 종료되는 문제로인하여 초기화 컨테이너의 시작이 실패된다면, 초기화 컨테이너는 파드의 restartPolicy 에 따라서 재시도 된다. 다만, 파드의 restartPolicy 가 항상(Always)으로 설정된 경우, 해당 초기화 컨테이너는 restartPolicy 를 실패 시(OnFailure)로 사용한다.

파드는 모든 초기화 컨테이너가 성공되기 전까지 Ready 될 수 없다. 초기화 컨테이너의 포트는 서비스 하에 합쳐지지 않는다. 초기화 중인 파드는 Pending 상태이지만 Initialized 가 거짓이 되는 조건을 가져야 한다.

만약 파드가 재시작되었다면, 모든 초기화 컨테이너는 반드시 다시 실행된다.

초기화 컨테이너 스펙 변경은 컨테이너 이미지 필드에서만 한정적으로 가능하다. 초기화 컨테이너 이미지 필드를 변경하는 것은 파드를 재시작하는 것과 같다.

초기화 컨테이너는 재시작되거나, 재시도, 또는 재실행 될 수 있기 때문에, 초기화 컨테이너 코드는 멱등성(idempotent)을 유지해야 한다. 특히, EmptyDirs 에 있는 파일에 쓰기를 수행하는 코드는 출력 파일이 이미 존재할 가능성에 대비해야 한다.

초기화 컨테이너는 앱 컨테이너의 필드를 모두 가지고 있다. 그러나, 쿠버네티스는 readinessProbe 가 사용되는 것을 금지한다. 초기화 컨테이너가 완료 상태와 준비성을 구분해서 정의할 수 없기 때문이다. 이것은 유효성 검사 중에 시행된다.

초기화 컨테이너들이 실패를 영원히 지속하는 상황을 방지하기 위해서 파드의 activeDeadlineSeconds를 사용한다. Active deadline은 초기화 컨테이너를 포함한다. 그러나 팀에서 애플리케이션을 잡(job)으로 배포한 경우에만 activeDeadlineSeconds를 사용하길 추천한다. 왜냐하면, activeDeadlineSeconds는 초기화 컨테이너가 완료된 이후에도 영향을 주기 때문이다. 이미 정상적으로 동작하고 있는 파드도 activeDeadlineSeconds를 설정한 경우 종료(killed)될 수 있다.

파드 내의 각 앱과 초기화 컨테이너의 이름은 유일해야 한다. 어떤 컨테이너가 다른 컨테이너와 같은 이름을 공유하는 경우 유효성 오류가 발생한다.

리소스

초기화 컨테이너에게 명령과 실행이 주어진 경우, 리소스 사용에 대한 다음의 규칙이 적용된다.

  • 모든 컨테이너에 정의된 특정 리소스 요청량 또는 상한 중 가장 높은 것은 유효 초기화 요청량/상한 이다. 리소스 제한이 지정되지 않은 리소스는 이 유효 초기화 요청량/상한을 가장 높은 요청량/상한으로 간주한다.
  • 리소스를 위한 파드의 유효한 초기화 요청량/상한 은 다음 보다 더 높다.
    • 모든 앱 컨테이너의 리소스에 대한 요청량/상한의 합계
    • 리소스에 대한 유효한 초기화 요청량/상한
  • 스케줄링은 유효한 요청/상한에 따라 이루어진다. 즉, 초기화 컨테이너는 파드의 삶에서는 사용되지 않는 초기화를 위한 리소스를 예약할 수 있다.
  • 파드의 유효한 QoS 계층 에서 QoS(서비스의 품질) 계층은 초기화 컨테이너들과 앱 컨테이너들의 QoS 계층과 같다.

쿼터 및 상한은 유효한 파드의 요청량 및 상한에 따라 적용된다.

파드 레벨 cgroup은 유효한 파드 요청량 및 상한을 기반으로 한다. 이는 스케줄러와 같다.

파드 재시작 이유

파드는 다음과 같은 사유로, 초기화 컨테이너들의 재-실행을 일으키는, 재시작을 수행할 수 있다.

  • 파드 인프라스트럭처 컨테이너가 재시작된 상황. 이는 일반적인 상황이 아니며 노드에 대해서 root 접근 권한을 가진 누군가에 의해서 수행됐을 것이다.
  • 초기화 컨테이너의 완료 기록이 가비지 수집 때문에 유실된 상태에서, restartPolicy가 Always로 설정된 파드의 모든 컨테이너가 종료되어 모든 컨테이너를 재시작해야 하는 상황

초기화 컨테이너 이미지가 변경되거나 초기화 컨테이너의 완료 기록이 가비지 수집 때문에 유실된 상태이면 파드는 재시작되지 않는다. 이는 쿠버네티스 버전 1.20 이상에 적용된다. 이전 버전의 쿠버네티스를 사용하는 경우 해당 쿠버네티스 버전의 문서를 참고한다.

다음 내용

3.4.1.3 - 파드 토폴로지 분배 제약 조건

FEATURE STATE: Kubernetes v1.19 [stable]

사용자는 토폴로지 분배 제약 조건 을 사용해서 지역, 영역, 노드 그리고 기타 사용자-정의 토폴로지 도메인과 같이 장애-도메인으로 설정된 클러스터에 걸쳐 파드가 분산되는 방식을 제어할 수 있다. 이를 통해 고가용성뿐만 아니라, 효율적인 리소스 활용의 목적을 이루는 데 도움이 된다.

필수 구성 요소

노드 레이블

토폴로지 분배 제약 조건은 노드 레이블을 의지해서 각 노드가 속한 토폴로지 도메인(들)을 인식한다. 예를 들어, 노드에 다음과 같은 레이블을 가지고 있을 수 있다. node=node1,zone=us-east-1a,region=us-east-1

다음 레이블이 있고, 4개 노드를 가지는 클러스터가 있다고 가정한다.

NAME    STATUS   ROLES    AGE     VERSION   LABELS
node1   Ready    <none>   4m26s   v1.16.0   node=node1,zone=zoneA
node2   Ready    <none>   3m58s   v1.16.0   node=node2,zone=zoneA
node3   Ready    <none>   3m17s   v1.16.0   node=node3,zone=zoneB
node4   Ready    <none>   2m43s   v1.16.0   node=node4,zone=zoneB

그러면 클러스터는 논리적으로 다음과 같이 보이게 된다.

graph TB subgraph "zoneB" n3(Node3) n4(Node4) end subgraph "zoneA" n1(Node1) n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4 k8s; class zoneA,zoneB cluster;

레이블을 수동으로 적용하는 대신에, 사용자는 대부분의 클러스터에서 자동으로 생성되고 채워지는 잘-알려진 레이블을 재사용할 수 있다.

파드의 분배 제약 조건

API

API 필드 pod.spec.topologySpreadConstraints 는 다음과 같이 정의된다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  topologySpreadConstraints:
    - maxSkew: <integer>
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>

사용자는 하나 또는 다중 topologySpreadConstraint 를 정의해서 kube-scheduler 에게 클러스터에 걸쳐 있는 기존 파드와 시작하는 각각의 파드와 연관하여 배치하는 방법을 명령할 수 있다. 필드는 다음과 같다.

  • maxSkew 는 파드가 균등하지 않게 분산될 수 있는 정도를 나타낸다. 이것은 0보다는 커야 한다. 그 의미는 whenUnsatisfiable 의 값에 따라 다르다.
    • whenUnsatisfiable 이 "DoNotSchedule"과 같을 때, maxSkew 는 대상 토폴로지에서 일치하는 파드 수와 전역 최솟값 (토폴로지 도메인에서 레이블 셀렉터와 일치하는 최소 파드 수. 예를 들어 3개의 영역에 각각 0, 2, 3개의 일치하는 파드가 있으면, 전역 최솟값은 0) 사이에 허용되는 최대 차이이다.
    • whenUnsatisfiable 이 "ScheduleAnyway"와 같으면, 스케줄러는 왜곡을 줄이는데 도움이 되는 토폴로지에 더 높은 우선 순위를 부여한다.
  • topologyKey 는 노드 레이블의 키다. 만약 두 노드가 이 키로 레이블이 지정되고, 레이블이 동일한 값을 가진다면 스케줄러는 두 노드를 같은 토폴로지에 있는것으로 여기게 된다. 스케줄러는 각 토폴로지 도메인에 균형잡힌 수의 파드를 배치하려고 시도한다.
  • whenUnsatisfiable 는 분산 제약 조건을 만족하지 않을 경우에 처리하는 방법을 나타낸다.
    • DoNotSchedule (기본값)은 스케줄러에 스케줄링을 하지 말라고 알려준다.
    • ScheduleAnyway 는 스케줄러에게 차이(skew)를 최소화하는 노드에 높은 우선 순위를 부여하면서, 스케줄링을 계속하도록 지시한다.
  • labelSelector 는 일치하는 파드를 찾는데 사용된다. 이 레이블 셀렉터와 일치하는 파드의 수를 계산하여 해당 토폴로지 도메인에 속할 파드의 수를 결정한다. 자세한 내용은 레이블 셀렉터를 참조한다.

파드에 2개 이상의 topologySpreadConstraint가 정의되어 있으면, 각 제약 조건은 AND로 연결된다 - kube-scheduler는 새로운 파드의 모든 제약 조건을 만족하는 노드를 찾는다.

사용자는 kubectl explain Pod.spec.topologySpreadConstraints 를 실행해서 이 필드에 대한 자세한 내용을 알 수 있다.

예시: 단수 토폴로지 분배 제약 조건

4개 노드를 가지는 클러스터에 foo:bar 가 레이블된 3개의 파드가 node1, node2 그리고 node3에 각각 위치한다고 가정한다.

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class zoneA,zoneB cluster;

신규 파드가 기존 파드와 함께 영역에 걸쳐서 균등하게 분배되도록 하려면, 스펙(spec)은 다음과 같이 주어질 수 있다.

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: k8s.gcr.io/pause:3.1

topologyKey: zone 는 "zone:<any value>" 레이블 쌍을 가지는 노드에 대해서만 균등한 분배를 적용하는 것을 의미한다. whenUnsatisfiable: DoNotSchedule 은 만약 들어오는 파드가 제약 조건을 만족시키지 못하면 스케줄러에게 pending 상태를 유지하도록 지시한다.

만약 스케줄러가 이 신규 파드를 "zoneA"에 배치하면 파드 분포는 [3, 1]이 되며, 따라서 실제 차이(skew)는 2 (3 - 1)가 되어 maxSkew: 1 를 위반하게 된다. 이 예시에서는 들어오는 파드는 오직 "zoneB"에만 배치할 수 있다.

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) p4(mypod) --> n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

OR

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) p4(mypod) --> n3 n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

사용자는 파드 스펙을 조정해서 다음과 같은 다양한 요구사항을 충족할 수 있다.

  • maxSkew 를 "2" 보다 큰 값으로 변경해서 들어오는 파드들이 "zoneA"에도 배치할 수 있도록 한다.
  • topologyKey 를 "node"로 변경해서 파드가 영역이 아닌, 노드에 걸쳐 고르게 분산할 수 있게 한다. 위의 예시에서 만약 maxSkew 가 "1"로 유지되면 들어오는 파드는 오직 "node4"에만 배치할 수 있다.
  • whenUnsatisfiable: DoNotSchedule 에서 whenUnsatisfiable: ScheduleAnyway 로 변경하면 들어오는 파드는 항상 다른 스케줄링 API를 충족한다는 가정하에 스케줄할 수 있도록 보장한다. 그러나 일치하는 파드가 적은 토폴로지 도메인에 배치되는 것이 좋다. (이 선호도는 리소스 사용 비율 등과 같은 다른 내부 스케줄링 우선순위와 공동으로 정규화 된다는 것을 알아두자.)

예시: 다중 토폴로지 분배 제약 조건

4개 노드를 가지는 클러스터에 foo:bar 가 레이블된 3개의 파드가 node1, node2 그리고 node3에 각각 위치한다고 가정한다.

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;

사용자는 2개의 TopologySpreadConstraints를 사용해서 영역과 노드에 파드를 분배하는 것을 제어할 수 있다.

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: k8s.gcr.io/pause:3.1

이 경우에는, 첫 번째 제약 조건에 부합시키려면, 신규 파드는 오직 "zoneB"에만 배치할 수 있다. 두 번째 제약 조건에서는 신규 파드는 오직 "node4"에만 배치할 수 있다. 그런 다음 두 가지 제약 조건의 결과는 AND 가 되므로, 실행 가능한 유일한 옵션은 "node4"에 배치하는 것이다.

다중 제약 조건은 충돌로 이어질 수 있다. 3개의 노드를 가지는 클러스터 하나가 2개의 영역에 걸쳐 있다고 가정한다.

graph BT subgraph "zoneB" p4(Pod) --> n3(Node3) p5(Pod) --> n3 end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n1 p3(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3,p4,p5 k8s; class zoneA,zoneB cluster;

만약 사용자가 "two-constraints.yaml" 을 이 클러스터에 적용하면, "mypod"가 Pending 상태로 유지되는 것을 알게 된다. 이러한 이유는, 첫 번째 제약 조건을 충족하기 위해 "mypod"는 오직 "zoneB"에만 놓을 수 있다. 두 번째 제약 조건에서는 "mypod"는 오직 "node2"에만 놓을 수 있다. 그러면 "zoneB"와 "node2"의 공동 결과는 아무것도 반환되지 않는다.

이 상황을 극복하기 위해서는 사용자가 maxSkew 의 증가 또는 whenUnsatisfiable: ScheduleAnyway 를 사용하도록 제약 조건 중 하나를 수정할 수 있다.

노드 어피니티(Affinity) 및 노드 셀렉터(Selector)와의 상호 작용

스케줄러는 신규 파드에 spec.nodeSelector 또는 spec.affinity.nodeAffinity가 정의되어 있는 경우, 부합하지 않는 노드들을 차이(skew) 계산에서 생략한다.

zoneA 에서 zoneC에 걸쳐있고, 5개의 노드를 가지는 클러스터가 있다고 가정한다.

graph BT subgraph "zoneB" p3(Pod) --> n3(Node3) n4(Node4) end subgraph "zoneA" p1(Pod) --> n1(Node1) p2(Pod) --> n2(Node2) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n1,n2,n3,n4,p1,p2,p3 k8s; class p4 plain; class zoneA,zoneB cluster;
graph BT subgraph "zoneC" n5(Node5) end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class n5 k8s; class zoneC cluster;

그리고 알다시피 "zoneC"는 제외해야 한다. 이 경우에, "mypod"가 "zoneC"가 아닌 "zoneB"에 배치되도록 yaml을 다음과 같이 구성할 수 있다. 마찬가지로 spec.nodeSelector 도 존중된다.

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: NotIn
            values:
            - zoneC
  containers:
  - name: pause
    image: k8s.gcr.io/pause:3.1

스케줄러는 클러스터에 있는 모든 영역(zone) 또는 다른 토폴로지 도메인에 대한 사전 지식이 없다. 스케줄링은 클러스터의 기존 노드에서 결정된다. 노드 풀(또는 노드 그룹)이 0개의 노드로 스케일(scale)되고 사용자는 노드가 확장될 것으로 예상하는 경우, 자동 스케일되는 클러스터에서 문제가 발생할 수 있다. 이러한 토폴로지 도메인은 스케줄링에서 해당 도메인에 노드가 하나 이상 있을 때까지 고려되지 않을 것이기 때문이다.

기타 눈에 띄는 의미(semantics)

여기에 주목할만한 몇 가지 암묵적인 규칙이 있다.

  • 신규 파드와 같은 네임스페이스를 갖는 파드만이 매칭의 후보가 된다.

  • topologySpreadConstraints[*].topologyKey 가 없는 노드는 무시된다. 이것은 다음을 의미한다.

    1. 이러한 노드에 위치한 파드는 "maxSkew" 계산에 영향을 미치지 않는다. - 위의 예시에서, "node1"은 "zone" 레이블을 가지고 있지 않다고 가정하면, 파드 2개는 무시될 것이고, 이런 이유로 신규 파드는 "zoneA"로 스케줄된다.
    2. 신규 파드는 이런 종류의 노드에 스케줄 될 기회가 없다. - 위의 예시에서, 레이블로 {zone-typo: zoneC} 를 가지는 "node5"가 클러스터에 편입한다고 가정하면, 레이블 키에 "zone"이 없기 때문에 무시하게 된다.
  • 들어오는 파드의 topologySpreadConstraints[*].labelSelector 와 자체 레이블과 일치하지 않을 경우 어떻게 되는지 알고 있어야 한다. 위의 예시에서, 만약 들어오는 파드의 레이블을 제거하더라도 여전히 제약 조건이 충족하기 때문에 "zoneB"에 배치할 수 있다. 그러나, 배치 이후에도 클러스터의 불균형 정도는 변경되지 않는다. - 여전히 zoneA는 {foo:bar} 레이블을 가지고 있는 2개의 파드를 가지고 있고, zoneB 도 {foo:bar}를 레이블로 가지는 파드 1개를 가지고 있다. 따라서 만약 예상과 다르면, 워크로드의 topologySpreadConstraints[*].labelSelector 가 자체 레이블과 일치하도록 하는 것을 권장한다.

클러스터 수준의 기본 제약 조건

클러스터에 대한 기본 토폴로지 분배 제약 조건을 설정할 수 있다. 기본 토폴로지 분배 제약 조건은 다음과 같은 경우에만 파드에 적용된다.

  • .spec.topologySpreadConstraints 에는 어떠한 제약도 정의되어 있지 않는 경우.
  • 서비스, 레플리케이션컨트롤러(ReplicationController), 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)에 속해있는 경우.

기본 제약 조건은 스케줄링 프로파일에서 PodTopologySpread 플러그인의 일부로 설정할 수 있다. 제약 조건은 labelSelector 가 비어 있어야 한다는 점을 제외하고, 위와 동일한 API로 제약 조건을 지정한다. 셀렉터는 파드가 속한 서비스, 레플리케이션 컨트롤러, 레플리카셋 또는 스테이트풀셋에서 계산한다.

예시 구성은 다음과 같다.

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration

profiles:
  - pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints:
            - maxSkew: 1
              topologyKey: topology.kubernetes.io/zone
              whenUnsatisfiable: ScheduleAnyway
          defaultingType: List

내부 기본 제약

FEATURE STATE: Kubernetes v1.20 [beta]

기본적으로 활성화된 DefaultPodTopologySpread 기능 게이트를 사용하면, 기존 SelectorSpread 플러그인이 비활성화된다. kube-scheduler는 PodTopologySpread 플러그인 구성에 다음과 같은 기본 토폴로지 제약 조건을 사용한다.

defaultConstraints:
  - maxSkew: 3
    topologyKey: "kubernetes.io/hostname"
    whenUnsatisfiable: ScheduleAnyway
  - maxSkew: 5
    topologyKey: "topology.kubernetes.io/zone"
    whenUnsatisfiable: ScheduleAnyway

또한, 같은 동작을 제공하는 레거시 SelectorSpread 플러그인이 비활성화된다.

클러스터에 기본 파드 분배 제약 조건을 사용하지 않으려면, PodTopologySpread 플러그인 구성에서 defaultingTypeList 로 설정하고 defaultConstraints 를 비워두어 기본값을 비활성화할 수 있다.

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration

profiles:
  - pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints: []
          defaultingType: List

파드어피니티(PodAffinity)/파드안티어피니티(PodAntiAffinity)와의 비교

쿠버네티스에서 "어피니티(Affinity)"와 관련된 지침은 파드가 더 많이 채워지거나 더 많이 분산되는 방식으로 스케줄 되는 방법을 제어한다.

  • PodAffinity 는, 사용자가 자격이 충족되는 토폴로지 도메인에 원하는 수의 파드를 얼마든지 채울 수 있다.
  • PodAntiAffinity 로는, 단일 토폴로지 도메인에 단 하나의 파드만 스케줄 될 수 있다.

더 세밀한 제어를 위해, 토폴로지 분배 제약 조건을 지정하여 다양한 토폴로지 도메인에 파드를 분배해서 고 가용성 또는 비용 절감을 달성할 수 있는 유연한 옵션을 제공한다. 또한 워크로드의 롤링 업데이트와 레플리카의 원활한 스케일링 아웃에 도움이 될 수 있다. 더 자세한 내용은 모티베이션(Motivation)를 참고한다.

알려진 제한사항

  • 파드가 제거된 이후에도 제약 조건이 계속 충족된다는 보장은 없다. 예를 들어 디플로이먼트를 스케일링 다운하면 그 결과로 파드의 분포가 불균형해질 수 있다. Descheduler를 사용하여 파드 분포를 다시 균형있게 만들 수 있다.
  • 파드와 일치하는 테인트(taint)가 된 노드가 존중된다. 이슈 80921을 본다.

다음 내용

3.4.1.4 - 중단(disruption)

이 가이드는 고가용성 애플리케이션을 구성하려는 소유자와 파드에서 발생하는 장애 유형을 이해하기 원하는 애플리케이션 소유자를 위한 것이다.

또한 클러스터의 업그레이드와 오토스케일링과 같은 클러스터의 자동화 작업을 하려는 관리자를 위한 것이다.

자발적 중단과 비자발적 중단

파드는 누군가(사람 또는 컨트롤러)가 파괴하거나 불가피한 하드웨어 오류 또는 시스템 소프트웨어 오류가 아니면 사라지지 않는다.

우리는 이런 불가피한 상황을 애플리케이션의 비자발적 중단 으로 부른다. 예시:

  • 노드를 지원하는 물리 머신의 하드웨어 오류
  • 클러스터 관리자의 실수로 VM(인스턴스) 삭제
  • 클라우드 공급자 또는 하이퍼바이저의 오류로 인한 VM 장애
  • 커널 패닉
  • 클러스터 네트워크 파티션의 발생으로 클러스터에서 노드가 사라짐
  • 노드의 리소스 부족으로 파드가 축출됨

리소스 부족을 제외한 나머지 조건은 대부분의 사용자가 익숙할 것이다. 왜냐하면 그 조건은 쿠버네티스에 국한되지 않기 때문이다.

우리는 다른 상황을 자발적인 중단 으로 부른다. 여기에는 애플리케이션 소유자의 작업과 클러스터 관리자의 작업이 모두 포함된다. 다음은 대표적인 애플리케이션 소유자의 작업이다.

  • 디플로이먼트 제거 또는 다른 파드를 관리하는 컨트롤러의 제거
  • 재시작을 유발하는 디플로이먼트의 파드 템플릿 업데이트
  • 파드를 직접 삭제(예: 우연히)

클러스터 관리자의 작업은 다음을 포함한다.

  • 복구 또는 업그레이드를 위한 노드 드레이닝.
  • 클러스터의 스케일 축소를 위한 노드 드레이닝(클러스터 오토스케일링에 대해 알아보기 ).
  • 노드에 다른 무언가를 추가하기 위해 파드를 제거.

위 작업은 클러스터 관리자가 직접 수행하거나 자동화를 통해 수행하며, 클러스터 호스팅 공급자에 의해서도 수행된다.

클러스터에 자발적인 중단을 일으킬 수 있는 어떤 원인이 있는지 클러스터 관리자에게 문의하거나 클라우드 공급자에게 문의하고, 배포 문서를 참조해서 확인해야 한다. 만약 자발적인 중단을 일으킬 수 있는 원인이 없다면 Pod Disruption Budget의 생성을 넘길 수 있다.

중단 다루기

비자발적인 중단으로 인한 영향을 경감하기 위한 몇 가지 방법은 다음과 같다.

자발적 중단의 빈도는 다양하다. 기본적인 쿠버네티스 클러스터에서는 자동화된 자발적 중단은 발생하지 않는다(사용자가 지시한 자발적 중단만 발생한다). 그러나 클러스터 관리자 또는 호스팅 공급자가 자발적 중단이 발생할 수 있는 일부 부가 서비스를 운영할 수 있다. 예를 들어 노드 소프트웨어의 업데이트를 출시하는 경우 자발적 중단이 발생할 수 있다. 또한 클러스터(노드) 오토스케일링의 일부 구현에서는 단편화를 제거하고 노드의 효율을 높이는 과정에서 자발적 중단을 야기할 수 있다. 클러스터 관리자 또는 호스팅 공급자는 예측 가능한 자발적 중단 수준에 대해 문서화해야 한다. 파드 스펙 안에 프라이어리티클래스 사용하기와 같은 특정 환경설정 옵션 또한 자발적(+ 비자발적) 중단을 유발할 수 있다.

파드 disruption budgets

FEATURE STATE: Kubernetes v1.21 [stable]

쿠버네티스는 자발적인 중단이 자주 발생하는 경우에도 고 가용성 애플리케이션을 실행하는 데 도움이 되는 기능을 제공한다.

애플리케이션 소유자로써, 사용자는 각 애플리케이션에 대해 PodDisruptionBudget(PDB)을 만들 수 있다. PDB는 자발적 중단으로 일시에 중지되는 복제된 애플리케이션 파드의 수를 제한한다. 예를 들어, 정족수 기반의 애플리케이션이 실행 중인 레플리카의 수가 정족수 이하로 떨어지지 않도록 한다. 웹 프런트 엔드는 부하를 처리하는 레플리카의 수가 일정 비율 이하로 떨어지지 않도록 보장할 수 있다.

클러스터 관리자와 호스팅 공급자는 직접적으로 파드나 디플로이먼트를 제거하는 대신 Eviction API로 불리는 PodDisruptionBudget을 준수하는 도구를 이용해야 한다.

예를 들어, kubectl drain 하위 명령을 사용하면 노드를 서비스 중단으로 표시할 수 있다. kubectl drain 을 실행하면, 도구는 사용자가 서비스를 중단하는 노드의 모든 파드를 축출하려고 한다. kubectl 이 사용자를 대신하여 수행하는 축출 요청은 일시적으로 거부될 수 있으며, 도구는 대상 노드의 모든 파드가 종료되거나 설정 가능한 타임아웃이 도래할 때까지 주기적으로 모든 실패된 요청을 다시 시도한다.

PDB는 애플리케이션이 필요로 하는 레플리카의 수에 상대적으로, 용인할 수 있는 레플리카의 수를 지정한다. 예를 들어 .spec.replicas: 5 의 값을 갖는 디플로이먼트는 어느 시점에든 5개의 파드를 가져야 한다. 만약 해당 디플로이먼트의 PDB가 특정 시점에 파드를 4개 허용한다면, Eviction API는 한 번에 1개(2개의 파드가 아닌)의 파드의 자발적인 중단을 허용한다.

파드 그룹은 레이블 셀렉터를 사용해서 지정한 애플리케이션으로 구성되며 애플리케이션 컨트롤러(디플로이먼트, 스테이트풀셋 등)를 사용한 것과 같다.

파드의 "의도"하는 수량은 해당 파드를 관리하는 워크로드 리소스의 .spec.replicas 를 기반으로 계산한다. 컨트롤 플레인은 파드의 .metadata.ownerReferences 를 검사하여 소유하는 워크로드 리소스를 발견한다.

PDB는 비자발적 중단이 발생하는 것을 막을 수는 없지만, 버짓이 차감된다.

애플리케이션의 롤링 업그레이드로 파드가 삭제되거나 사용할 수 없는 경우 중단 버짓에 영향을 준다. 그러나 워크로드 리소스(디플로이먼트, 스테이트풀셋과 같은)는 롤링 업데이트 시 PDB의 제한을 받지 않는다. 대신, 애플리케이션 업데이트 중 실패 처리는 특정 워크로드 리소스에 대한 명세에서 구성된다.

Eviction API를 사용하여 파드를 축출하면, PodSpecterminationGracePeriodSeconds 설정을 준수하여 정상적으로 종료됨 상태가 된다.

PodDisruptionBudget 예시

node-1 부터 node-3 까지 3개의 노드가 있는 클러스터가 있다고 하자. 클러스터에는 여러 애플리케이션을 실행하고 있다. 여러 애플리케이션 중 하나는 pod-a, pod-b, pod-c 로 부르는 3개의 레플리카가 있다. 여기에 pod-x 라고 부르는 PDB와 무관한 파드가 보인다. 초기에 파드는 다음과 같이 배치된다.

node-1 node-2 node-3
pod-a available pod-b available pod-c available
pod-x available

전체 3개 파드는 디플로이먼트의 일부분으로 전체적으로 항상 3개의 파드 중 최소 2개의 파드를 사용할 수 있도록 하는 PDB를 가지고 있다.

예를 들어, 클러스터 관리자가 커널 버그를 수정하기위해 새 커널 버전으로 재부팅하려는 경우를 가정해보자. 클러스터 관리자는 첫째로 node-1kubectl drain 명령어를 사용해서 비우려 한다. kubectlpod-apod-x 를 축출하려고 한다. 이는 즉시 성공한다. 두 파드는 동시에 terminating 상태로 진입한다. 이렇게 하면 클러스터는 다음의 상태가 된다.

node-1 draining node-2 node-3
pod-a terminating pod-b available pod-c available
pod-x terminating

디플로이먼트는 한 개의 파드가 중지되는 것을 알게되고, pod-d 라는 대체 파드를 생성한다. node-1 은 차단되어 있어 다른 노드에 위치한다. 무언가가 pod-x 의 대체 파드로 pod-y 도 생성했다.

(참고: 스테이트풀셋은 pod-0 처럼 불릴, pod-a 를 교체하기 전에 완전히 중지해야 하며, pod-0 로 불리지만, 다른 UID로 생성된다. 그렇지 않으면 이 예시는 스테이트풀셋에도 적용된다.)

이제 클러스터는 다음과 같은 상태이다.

node-1 draining node-2 node-3
pod-a terminating pod-b available pod-c available
pod-x terminating pod-d starting pod-y

어느 순간 파드가 종료되고, 클러스터는 다음과 같은 상태가 된다.

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d starting pod-y

이 시점에서 만약 성급한 클러스터 관리자가 node-2 또는 node-3 을 비우려고 하는 경우 디플로이먼트에 available 상태의 파드가 2개 뿐이고, PDB에 필요한 최소 파드는 2개이기 때문에 drain 명령이 차단된다. 약간의 시간이 지나면 pod-d 가 available 상태가 된다.

이제 클러스터는 다음과 같은 상태이다.

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d available pod-y

이제 클러스터 관리자는 node-2 를 비우려고 한다. drain 커멘드는 pod-b 에서 pod-d 와 같이 어떤 순서대로 두 파드를 축출하려 할 것이다. drain 커멘드는 pod-b 를 축출하는데 성공했다. 그러나 drain 커멘드가 pod-d 를 축출하려 하는 경우 디플로이먼트에 available 상태의 파드는 1개로 축출이 거부된다.

디플로이먼트는pod-b 를 대체할 pod-e 라는 파드를 생성한다. 클러스터에 pod-e 를 스케줄하기 위한 충분한 리소스가 없기 때문에 드레이닝 명령어는 차단된다. 클러스터는 다음 상태로 끝나게 된다.

node-1 drained node-2 node-3 no node
pod-b terminating pod-c available pod-e pending
pod-d available pod-y

이 시점에서 클러스터 관리자는 클러스터에 노드를 추가해서 업그레이드를 진행해야 한다.

쿠버네티스에 중단이 발생할 수 있는 비율을 어떻게 변화시키는지 다음의 사례를 통해 알 수 있다.

  • 애플리케이션에 필요한 레플리카의 수
  • 인스턴스를 정상적으로 종료하는데 소요되는 시간
  • 새 인스턴스를 시작하는데 소요되는 시간
  • 컨트롤러의 유형
  • 클러스터의 리소스 용량

클러스터 소유자와 애플리케이션 소유자의 역할 분리

보통 클러스터 매니저와 애플리케이션 소유자는 서로에 대한 지식이 부족한 별도의 역할로 생각하는 것이 유용하다. 이와 같은 책임의 분리는 다음의 시나리오에서 타당할 수 있다.

  • 쿠버네티스 클러스터를 공유하는 애플리케이션 팀이 많고, 자연스럽게 역할이 나누어진 경우
  • 타사 도구 또는 타사 서비스를 이용해서 클러스터 관리를 자동화 하는 경우

Pod Disruption Budget은 역할 분리에 따라 역할에 맞는 인터페이스를 제공한다.

만약 조직에 역할 분리에 따른 책임의 분리가 없다면 Pod Disruption Budget을 사용할 필요가 없다.

클러스터에서 중단이 발생할 수 있는 작업을 하는 방법

만약 클러스터 관리자라면, 그리고 클러스터 전체 노드에 노드 또는 시스템 소프트웨어 업그레이드와 같은 중단이 발생할 수 있는 작업을 수행하는 경우 다음과 같은 옵션을 선택한다.

  • 업그레이드 하는 동안 다운타임을 허용한다.
  • 다른 레플리카 클러스터로 장애조치를 한다.
    • 다운타임은 없지만, 노드 사본과 전환 작업을 조정하기 위한 인력 비용이 많이 발생할 수 있다.
  • PDB를 이용해서 애플리케이션의 중단에 견디도록 작성한다.
    • 다운타임 없음
    • 최소한의 리소스 중복
    • 클러스터 관리의 자동화 확대 적용
    • 내결함성이 있는 애플리케이션의 작성은 까다롭지만 자발적 중단를 허용하는 작업의 대부분은 오토스케일링과 비자발적 중단를 지원하는 작업과 겹친다.

다음 내용

3.4.1.5 - 임시(Ephemeral) 컨테이너

FEATURE STATE: Kubernetes v1.22 [alpha]

이 페이지는 임시 컨테이너에 대한 개요를 제공한다. 이 특별한 유형의 컨테이너는 트러블슈팅과 같은 사용자가 시작한 작업을 완료하기 위해 기존 파드에서 임시적으로 실행된다. 임시 컨테이너는 애플리케이션을 빌드하는 경우보다는 서비스 점검과 같은 경우에 더 적합하다.

임시 컨테이너 이해하기

파드 는 쿠버네티스 애플리케이션의 기본 구성 요소이다. 파드는 일회용이고, 교체 가능한 것으로 의도되었기 때문에, 사용자는 파드가 한번 생성되면, 컨테이너를 추가할 수 없다. 대신, 사용자는 보통 디플로이먼트 를 사용해서 제어하는 방식으로 파드를 삭제하고 교체한다.

그러나 때때로 재현하기 어려운 버그의 문제 해결을 위해 기존 파드의 상태를 검사해야 할 수 있다. 이 경우 사용자는 기존 파드에서 임시 컨테이너를 실행해서 상태를 검사하고, 임의의 명령을 실행할 수 있다.

임시 컨테이너는 무엇인가?

임시 컨테이너는 리소스 또는 실행에 대한 보증이 없다는 점에서 다른 컨테이너와 다르며, 결코 자동으로 재시작되지 않는다. 그래서 애플리케이션을 만드는데 적합하지 않다. 임시 컨테이너는 일반 컨테이너와 동일한 ContainerSpec 을 사용해서 명시하지만, 많은 필드가 호환되지 않으며 임시 컨테이너에는 허용되지 않는다.

  • 임시 컨테이너는 포트를 가지지 않을 수 있으므로, ports, livenessProbe, readinessProbe 와 같은 필드는 허용되지 않는다.
  • 파드에 할당된 리소스는 변경할 수 없으므로, resources 설정이 허용되지 않는다.
  • 허용되는 필드의 전체 목록은 임시컨테이너 참조 문서를 본다.

임시 컨테이너는 pod.spec 에 직접 추가하는 대신 API에서 특별한 ephemeralcontainers 핸들러를 사용해서 만들어지기 때문에 kubectl edit을 사용해서 임시 컨테이너를 추가할 수 없다.

일반 컨테이너와 마찬가지로, 사용자는 임시 컨테이너를 파드에 추가한 이후에 변경하거나 제거할 수 없다.

임시 컨테이너의 사용

임시 컨테이너는 컨테이너가 충돌 되거나 또는 컨테이너 이미지에 디버깅 도구가 포함되지 않은 이유로 kubectl exec 이 불충분할 때 대화형 문제 해결에 유용하다.

특히, distroless 이미지 를 사용하면 공격 표면(attack surface)과 버그 및 취약점의 노출을 줄이는 최소한의 컨테이너 이미지를 배포할 수 있다. distroless 이미지는 셸 또는 어떤 디버깅 도구를 포함하지 않기 때문에, kubectl exec 만으로는 distroless 이미지의 문제 해결이 어렵다.

임시 컨테이너 사용 시 프로세스 네임스페이스 공유를 활성화하면 다른 컨테이너 안의 프로세스를 보는 데 도움이 된다.

다음 내용

3.4.2 - 워크로드 리소스

3.4.2.1 - 디플로이먼트

디플로이먼트(Deployment)파드레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공한다.

디플로이먼트에서 의도하는 상태 를 설명하고, 디플로이먼트 컨트롤러(Controller)는 현재 상태에서 의도하는 상태로 비율을 조정하며 변경한다. 새 레플리카셋을 생성하는 디플로이먼트를 정의하거나 기존 디플로이먼트를 제거하고, 모든 리소스를 새 디플로이먼트에 적용할 수 있다.

유스케이스

다음은 디플로이먼트의 일반적인 유스케이스이다.

디플로이먼트 생성

다음은 디플로이먼트의 예시이다. 예시는 3개의 nginx 파드를 불러오기 위한 레플리카셋을 생성한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  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

이 예시에 대한 설명은 다음과 같다.

  • .metadata.name 필드에 따라 nginx-deployment 이름으로 디플로이먼트가 생성된다.

  • .spec.replicas 필드에 따라 디플로이먼트는 3개의 레플리카 파드를 생성한다.

  • .spec.selector 필드는 디플로이먼트가 관리할 파드를 찾는 방법을 정의한다. 이 사례에서는 파드 템플릿에 정의된 레이블(app: nginx)을 선택한다. 그러나 파드 템플릿 자체의 규칙이 만족되는 한, 보다 정교한 선택 규칙의 적용이 가능하다.

  • template 필드에는 다음 하위 필드가 포함되어 있다.

    • 파드는 .metadata.labels 필드를 사용해서 app: nginx 라는 레이블을 붙인다.
    • 파드 템플릿의 사양 또는 .template.spec 필드는 파드가 도커 허브nginx 1.14.2 버전 이미지를 실행하는 nginx 컨테이너 1개를 실행하는 것을 나타낸다.
    • 컨테이너 1개를 생성하고, .spec.template.spec.containers[0].name 필드를 사용해서 nginx 이름을 붙인다.

시작하기 전에, 쿠버네티스 클러스터가 시작되고 실행 중인지 확인한다. 위의 디플로이먼트를 생성하려면 다음 단계를 따른다.

  1. 다음 명령어를 실행해서 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
  1. kubectl get deployments 을 실행해서 디플로이먼트가 생성되었는지 확인한다.

만약 디플로이먼트가 여전히 생성 중이면, 다음과 유사하게 출력된다.

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s

클러스터에서 디플로이먼트를 점검할 때, 다음 필드가 표시된다.

  • NAME 은 네임스페이스에 있는 디플로이먼트 이름의 목록이다.
  • READY 는 사용자가 사용할 수 있는 애플리케이션의 레플리카의 수를 표시한다. ready/desired 패턴을 따른다.
  • UP-TO-DATE 는 의도한 상태를 얻기 위해 업데이트된 레플리카의 수를 표시한다.
  • AVAILABLE 은 사용자가 사용할 수 있는 애플리케이션 레플리카의 수를 표시한다.
  • AGE 는 애플리케이션의 실행된 시간을 표시한다.

.spec.replicas 필드에 따라 의도한 레플리카의 수가 3개인지 알 수 있다.

  1. 디플로이먼트의 롤아웃 상태를 보려면, kubectl rollout status deployment/nginx-deployment 를 실행한다.

    다음과 유사하게 출력된다.

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    
  2. 몇 초 후 kubectl get deployments 를 다시 실행한다. 다음과 유사하게 출력된다.

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18s
    

    디플로이먼트에서 3개의 레플리카가 생성되었고, 모든 레플리카는 최신 상태(최신 파드 템플릿을 포함)이며 사용 가능한 것을 알 수 있다.

  3. 디플로이먼트로 생성된 레플리카셋(rs)을 보려면, kubectl get rs 를 실행한다. 다음과 유사하게 출력된다.

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-75675f5897   3         3         3       18s
    

    레플리카셋의 출력에는 다음 필드가 표시된다.

    • NAME 은 네임스페이스에 있는 레플리카셋 이름의 목록이다.
    • DESIRED 는 디플로이먼트의 생성 시 정의된 의도한 애플리케이션 레플리카 의 수를 표시한다. 이것이 의도한 상태 이다.
    • CURRENT 는 현재 실행 중인 레플리카의 수를 표시한다.
    • READY 는 사용자가 사용할 수 있는 애플리케이션의 레플리카의 수를 표시한다.
    • AGE 는 애플리케이션의 실행된 시간을 표시한다.

    레플리카셋의 이름은 항상 [DEPLOYMENT-NAME]-[RANDOM-STRING] 형식으로 된 것을 알 수 있다. 무작위 문자열은 무작위로 생성되며, pod-template-hash 를 시드(seed)로 사용한다.

  4. 각 파드에 자동으로 생성된 레이블을 보려면, kubectl get pods --show-labels 를 실행한다. 다음과 유사하게 출력된다.

    NAME                                READY     STATUS    RESTARTS   AGE       LABELS
    nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=3123191453
    nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=3123191453
    nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=3123191453
    

    만들어진 레플리카셋은 실행 중인 3개의 nginx 파드를 보장한다.

Pod-template-hash 레이블

pod-template-hash 레이블은 디플로이먼트 컨트롤러에 의해서 디플로이먼트가 생성 또는 채택한 모든 레플리카셋에 추가된다.

이 레이블은 디플로이먼트의 자식 레플리카셋이 겹치지 않도록 보장한다. 레플리카셋의 PodTemplate 을 해싱하고, 해시 결과를 레플리카셋 셀렉터, 파드 템플릿 레이블 및 레플리카셋 이 가질 수 있는 기존의 모든 파드에 레이블 값으로 추가해서 사용하도록 생성한다.

디플로이먼트 업데이트

다음 단계에 따라 디플로이먼트를 업데이트한다.

  1. nginx:1.14.2 이미지 대신 nginx:1.16.1 이미지를 사용하도록 nginx 파드를 업데이트 한다.

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    또는 다음의 명령어를 사용한다.

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    다음과 유사하게 출력된다.

    deployment.apps/nginx-deployment image updated
    

    대안으로 디플로이먼트를 edit 해서 .spec.template.spec.containers[0].imagenginx:1.14.2 에서 nginx:1.16.1 로 변경한다.

    kubectl edit deployment/nginx-deployment
    

    다음과 유사하게 출력된다.

    deployment.apps/nginx-deployment edited
    
  2. 롤아웃 상태를 보려면 다음을 실행한다.

    kubectl rollout status deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    또는

    deployment "nginx-deployment" successfully rolled out
    

업데이트된 디플로이먼트에 대해 자세한 정보 보기

  • 롤아웃이 성공하면 kubectl get deployments 를 실행해서 디플로이먼트를 볼 수 있다. 이와 유사하게 출력된다.

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           36s
    
  • kubectl get rs 를 실행해서 디플로이먼트가 새 레플리카셋을 생성해서 파드를 업데이트 했는지 볼 수 있고, 새 레플리카셋을 최대 3개의 레플리카로 스케일 업, 이전 레플리카셋을 0개의 레플리카로 스케일 다운한다.

    kubectl get rs
    

    이와 유사하게 출력된다.

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       6s
    nginx-deployment-2035384211   0         0         0       36s
    
  • get pods 를 실행하면 새 파드만 표시된다.

    kubectl get pods
    

    이와 유사하게 출력된다.

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1564180365-khku8   1/1       Running   0          14s
    nginx-deployment-1564180365-nacti   1/1       Running   0          14s
    nginx-deployment-1564180365-z9gth   1/1       Running   0          14s
    

    다음에 이러한 파드를 업데이트 하려면 디플로이먼트의 파드 템플릿만 다시 업데이트 하면 된다.

    디플로이먼트는 업데이트되는 동안 일정한 수의 파드만 중단되도록 보장한다. 기본적으로 적어도 의도한 파드 수의 75% 이상이 동작하도록 보장한다(최대 25% 불가).

    또한 디플로이먼트는 의도한 파드 수 보다 더 많이 생성되는 파드의 수를 제한한다. 기본적으로, 의도한 파드의 수 기준 최대 125%까지만 추가 파드가 동작할 수 있도록 제한한다(최대 25% 까지).

    예를 들어, 위 디플로이먼트를 자세히 살펴보면 먼저 새로운 파드를 생성한 다음 이전 파드를 삭제하고, 새로운 파드를 만든 것을 볼 수 있다. 충분한 수의 새로운 파드가 나올 때까지 이전 파드를 죽이지 않으며, 충분한 수의 이전 파드들이 죽기 전까지 새로운 파드를 만들지 않는다. 이것은 최소 2개의 파드를 사용할 수 있게 하고, 최대 4개의 파드를 사용할 수 있게 한다.

  • 디플로이먼트의 세부 정보 가져오기

    kubectl describe deployments
    

    이와 유사하게 출력된다.

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Thu, 30 Nov 2017 10:56:25 +0000
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
       Containers:
        nginx:
          Image:        nginx:1.16.1
          Port:         80/TCP
          Environment:  <none>
          Mounts:       <none>
        Volumes:        <none>
      Conditions:
        Type           Status  Reason
        ----           ------  ------
        Available      True    MinimumReplicasAvailable
        Progressing    True    NewReplicaSetAvailable
      OldReplicaSets:  <none>
      NewReplicaSet:   nginx-deployment-1564180365 (3/3 replicas created)
      Events:
        Type    Reason             Age   From                   Message
        ----    ------             ----  ----                   -------
        Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
        Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
        Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0
    

    처음 디플로이먼트를 생성했을 때, 디플로이먼트가 레플리카셋(nginx-deployment-2035384211)을 생성해서 3개의 레플리카로 직접 스케일 업한 것을 볼 수 있다. 디플로이먼트를 업데이트할 때 새 레플리카셋(nginx-deployment-1564180365)을 생성하고, 1개로 스케일 업한 다음 이전 레플리카셋을 2개로 스케일 다운해서, 최소 2개의 파드를 사용할 수 있고 최대 4개의 파드가 항상 생성되어 있도록 하였다. 이후 지속해서 같은 롤링 업데이트 정책으로 새 레플리카셋은 스케일 업하고 이전 레플리카셋은 스케일 다운한다. 마지막으로 새로운 레플리카셋에 3개의 사용 가능한 레플리카가 구성되며, 이전 레플리카셋은 0개로 스케일 다운된다.

롤오버(일명 인-플라이트 다중 업데이트)

디플로이먼트 컨트롤러는 각 시간마다 새로운 디플로이먼트에서 레플리카셋이 의도한 파드를 생성하고 띄우는 것을 주시한다. 만약 디플로이먼트가 업데이트되면, 기존 레플리카셋에서 .spec.selector 레이블과 일치하는 파드를 컨트롤 하지만, 템플릿과 .spec.template 이 불일치하면 스케일 다운이 된다. 결국 새로운 레플리카셋은 .spec.replicas 로 스케일되고, 모든 기존 레플리카셋은 0개로 스케일된다.

만약 기존 롤아웃이 진행되는 중에 디플로이먼트를 업데이트하는 경우 디플로이먼트가 업데이트에 따라 새 레플리카셋을 생성하고, 스케일 업하기 시작한다. 그리고 이전에 스케일 업 하던 레플리카셋에 롤오버 한다. --이것은 기존 레플리카셋 목록에 추가하고 스케일 다운을 할 것이다.

예를 들어 디플로이먼트로 nginx:1.14.2 레플리카를 5개 생성을 한다. 하지만 nginx:1.14.2 레플리카 3개가 생성되었을 때 디플로이먼트를 업데이트해서 nginx:1.16.1 레플리카 5개를 생성성하도록 업데이트를 한다고 가정한다. 이 경우 디플로이먼트는 즉시 생성된 3개의 nginx:1.14.2 파드 3개를 죽이기 시작하고 nginx:1.16.1 파드를 생성하기 시작한다. 이것은 과정이 변경되기 전 nginx:1.14.2 레플리카 5개가 생성되는 것을 기다리지 않는다.

레이블 셀렉터 업데이트

일반적으로 레이블 셀렉터를 업데이트 하는 것을 권장하지 않으며 셀렉터를 미리 계획하는 것을 권장한다. 어떤 경우든 레이블 셀렉터의 업데이트를 해야하는 경우 매우 주의하고, 모든 영향을 파악했는지 확인해야 한다.

  • 셀렉터 추가 시 디플로이먼트의 사양에 있는 파드 템플릿 레이블도 새 레이블로 업데이트해야 한다. 그렇지 않으면 유효성 검사 오류가 반환된다. 이 변경은 겹치지 않는 변경으로 새 셀렉터가 이전 셀렉터로 만든 레플리카셋과 파드를 선택하지 않게 되고, 그 결과로 모든 기존 레플리카셋은 고아가 되며, 새로운 레플리카셋을 생성하게 된다.
  • 셀렉터 업데이트는 기존 셀렉터 키 값을 변경하며, 결과적으로 추가와 동일한 동작을 한다.
  • 셀렉터 삭제는 디플로이먼트 셀렉터의 기존 키를 삭제하며 파드 템플릿 레이블의 변경을 필요로 하지 않는다. 기존 레플리카셋은 고아가 아니고, 새 레플리카셋은 생성되지 않는다. 그러나 제거된 레이블은 기존 파드와 레플리카셋에 여전히 존재한다는 점을 참고해야 한다.

디플로이먼트 롤백

때때로 디플로이먼트의 롤백을 원할 수도 있다. 예를 들어 디플로이먼트가 지속적인 충돌로 안정적이지 않은 경우. 기본적으로 모든 디플로이먼트의 롤아웃 기록은 시스템에 남아있어 언제든지 원할 때 롤백이 가능하다 (이 사항은 수정 기록에 대한 상한 수정을 통해서 변경할 수 있다).

  • 디플로이먼트를 업데이트하는 동안 이미지 이름을 nginx:1.16.1 이 아닌 nginx:1.161 로 입력해서 오타를 냈다고 가정한다.

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161 
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment image updated
    
  • 롤아웃이 고착 된다. 고착된 롤아웃 상태를 확인할 수 있다.

    kubectl rollout status deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • Ctrl-C 를 눌러 위의 롤아웃 상태 보기를 중지한다. 고착된 롤아웃 상태에 대한 자세한 정보는 이 것을 더 읽어본다.

  • 이전 레플리카는 2개(nginx-deployment-1564180365nginx-deployment-2035384211), 새 레플리카는 1개(nginx-deployment-3066724191)임을 알 수 있다.

    kubectl get rs
    

    이와 유사하게 출력된다.

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       25s
    nginx-deployment-2035384211   0         0         0       36s
    nginx-deployment-3066724191   1         1         0       6s
    
  • 생성된 파드를 보면, 새로운 레플리카셋에 생성된 1개의 파드가 이미지 풀 루프(pull loop)에서 고착된 것을 볼 수 있다.

    kubectl get pods
    

    이와 유사하게 출력된다.

    NAME                                READY     STATUS             RESTARTS   AGE
    nginx-deployment-1564180365-70iae   1/1       Running            0          25s
    nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
    nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
    nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
    
  • 디플로이먼트에 대한 설명 보기

    kubectl describe deployment
    

    이와 유사하게 출력된다.

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    이 문제를 해결하려면 디플로이먼트를 안정적인 이전 수정 버전으로 롤백해야 한다.

디플로이먼트의 롤아웃 기록 확인

다음 순서에 따라 롤아웃 기록을 확인한다.

  1. 먼저 이 디플로이먼트의 수정 사항을 확인한다.

    kubectl rollout history deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml
    2           kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    3           kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    CHANGE-CAUSE 는 수정 생성시 디플로이먼트 주석인 kubernetes.io/change-cause 에서 복사한다. 다음에 대해 CHANGE-CAUSE 메시지를 지정할 수 있다.

    • 디플로이먼트에 kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1" 로 주석을 단다.
    • 수동으로 리소스 매니페스트 편집.
  2. 각 수정 버전의 세부 정보를 보려면 다음을 실행한다.

    kubectl rollout history deployment/nginx-deployment --revision=2
    

    이와 유사하게 출력된다.

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
      Containers:
       nginx:
        Image:      nginx:1.16.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

이전 수정 버전으로 롤백

다음 단계에 따라 디플로이먼트를 현재 버전에서 이전 버전인 버전 2로 롤백한다.

  1. 이제 현재 롤아웃의 실행 취소 및 이전 수정 버전으로 롤백 하기로 결정했다.

    kubectl rollout undo deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment rolled back
    

    Alternatively, you can rollback to a specific revision by specifying it with --to-revision:

    kubectl rollout undo deployment/nginx-deployment --to-revision=2
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment rolled back
    

    롤아웃 관련 명령에 대한 자세한 내용은 kubectl rollout을 참조한다.

    이제 디플로이먼트가 이전 안정 수정 버전으로 롤백 된다. 버전 2로 롤백하기 위해 DeploymentRollback 이벤트가 디플로이먼트 컨트롤러에서 생성되는 것을 볼 수 있다.

  2. 만약 롤백에 성공하고, 디플로이먼트가 예상대로 실행되는지 확인하려면 다음을 실행한다.

    kubectl get deployment nginx-deployment
    

    이와 유사하게 출력된다.

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  3. 디플로이먼트의 설명 가져오기.

    kubectl describe deployment nginx-deployment
    

    이와 유사하게 출력된다.

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
                            kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.16.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

디플로이먼트 스케일링

다음 명령어를 사용해서 디플로이먼트의 스케일을 할 수 있다.

kubectl scale deployment/nginx-deployment --replicas=10

이와 유사하게 출력된다.

deployment.apps/nginx-deployment scaled

가령 클러스터에서 horizontal Pod autoscaling를 설정 한 경우 디플로이먼트에 대한 오토스케일러를 설정할 수 있다. 그리고 기존 파드의 CPU 사용률을 기준으로 실행할 최소 파드 및 최대 파드의 수를 선택할 수 있다.

kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80

이와 유사하게 출력된다.

deployment.apps/nginx-deployment scaled

비례적 스케일링(Proportional Scaling)

디플로이먼트 롤링업데이트는 여러 버전의 애플리케이션을 동시에 실행할 수 있도록 지원한다. 사용자 또는 오토스케일러가 롤아웃 중에 있는 디플로이먼트 롤링 업데이트를 스케일링 하는 경우(진행중 또는 일시 중지 중), 디플로이먼트 컨트롤러는 위험을 줄이기 위해 기존 활성화된 레플리카셋(파드와 레플리카셋)의 추가 레플리카의 균형을 조절 한다. 이것을 proportional scaling 라 부른다.

예를 들어, 10개의 레플리카를 디플로이먼트로 maxSurge=3, 그리고 maxUnavailable=2 로 실행 한다.

  • 디플로이먼트에 있는 10개의 레플리카가 실행되는지 확인한다.

    kubectl get deploy
    

    이와 유사하게 출력된다.

    NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment     10        10        10           10          50s
    
  • 클러스터 내부에서 확인할 수 없는 새 이미지로 업데이트 된다.

    kubectl set image deployment/nginx-deployment nginx=nginx:sometag
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment image updated
    
  • 이미지 업데이트는 레플리카셋 nginx-deployment-1989198191 으로 새로운 롤 아웃이 시작하지만, 위에서 언급한 maxUnavailable 의 요구 사항으로 인해 차단된다. 롤아웃 상태를 확인한다.

    kubectl get rs
    
    이와 유사하게 출력된다.
    
    NAME                          DESIRED   CURRENT   READY     AGE
    nginx-deployment-1989198191   5         5         0         9s
    nginx-deployment-618515232    8         8         8         1m
    
  • 그 다음 디플로이먼트에 대한 새로운 스케일링 요청이 함께 따라온다. 오토스케일러는 디플로이먼트 레플리카를 15로 증가시킨다. 디플로이먼트 컨트롤러는 새로운 5개의 레플리카의 추가를 위한 위치를 결정해야 한다. 만약 비례적 스케일링을 사용하지 않으면 5개 모두 새 레플리카셋에 추가된다. 비례적 스케일링으로 추가 레플리카를 모든 레플리카셋에 걸쳐 분산할 수 있다. 비율이 높을수록 가장 많은 레플리카가 있는 레플리카셋으로 이동하고, 비율이 낮을 수록 적은 레플리카가 있는 레플리카셋으로 이동한다. 남은 것들은 대부분의 레플리카가 있는 레플리카셋에 추가된다. 0개의 레플리카가 있는 레플리카셋은 스케일 업 되지 않는다.

위의 예시에서 기존 레플리카셋에 3개의 레플리카가 추가되고, 2개의 레플리카는 새 레플리카에 추가된다. 결국 롤아웃 프로세스는 새 레플리카가 정상이라고 가정하면 모든 레플리카를 새 레플리카셋으로 이동시킨다. 이를 확인하려면 다음을 실행한다.

kubectl get deploy

이와 유사하게 출력된다.

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

롤아웃 상태는 레플리카가 각 레플리카셋에 어떻게 추가되었는지 확인한다.

kubectl get rs

이와 유사하게 출력된다.

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

디플로이먼트의 일시 중지와 재개

하나 이상의 업데이트를 트리거하기 전에 디플로이먼트를 일시 중지한 다음 다시 시작할 수 있다. 이렇게 하면 불필요한 롤아웃을 트리거하지 않고 일시 중지와 재개 사이에 여러 수정 사항을 적용할 수 있다.

  • 예를 들어, 생성된 디플로이먼트의 경우 디플로이먼트 상세 정보를 가져온다.

    kubectl get deploy
    

    이와 유사하게 출력된다.

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx     3         3         3            3           1m
    

    롤아웃 상태를 가져온다.

    kubectl get rs
    

    이와 유사하게 출력된다.

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         1m
    
  • 다음 명령을 사용해서 일시 중지한다.

    kubectl rollout pause deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment paused
    
  • 그런 다음 디플로이먼트의 이미지를 업데이트 한다.

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment image updated
    
  • 새로운 롤아웃이 시작되지 않는다.

    kubectl rollout history deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    deployments "nginx"
    REVISION  CHANGE-CAUSE
    1   <none>
    
  • 롤아웃 상태를 가져와서 디플로이먼트 업데이트가 성공적인지 확인한다.

    kubectl get rs
    

    이와 유사하게 출력된다.

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         2m
    
  • 예를 들어 사용할 리소스를 업데이트하는 것처럼 원하는 만큼 업데이트할 수 있다.

    kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment resource requirements updated
    

    디플로이먼트를 일시 중지하기 전의 초기 상태는 해당 기능을 지속한다. 그러나 디플로이먼트가 일시 중지한 상태에서는 디플로이먼트의 새 업데이트에 영향을 주지 않는다.

  • 결국, 디플로이먼트를 재개하고 새로운 레플리카셋이 새로운 업데이트를 제공하는 것을 관찰한다.

    kubectl rollout resume deployment/nginx-deployment
    

    이와 유사하게 출력된다.

    deployment.apps/nginx-deployment resumed
    
  • 롤아웃이 완료될 때까지 상태를 관찰한다.

    kubectl get rs -w
    

    이와 유사하게 출력된다.

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   2         2         2         2m
    nginx-3926361531   2         2         0         6s
    nginx-3926361531   2         2         1         18s
    nginx-2142116321   1         2         2         2m
    nginx-2142116321   1         2         2         2m
    nginx-3926361531   3         2         1         18s
    nginx-3926361531   3         2         1         18s
    nginx-2142116321   1         1         1         2m
    nginx-3926361531   3         3         1         18s
    nginx-3926361531   3         3         2         19s
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         20s
    
  • 롤아웃 최신 상태를 가져온다.

    kubectl get rs
    

    이와 유사하게 출력된다.

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         28s
    

디플로이먼트 상태

디플로이먼트는 라이프사이클 동안 다양한 상태로 전환된다. 이는 새 레플리카셋을 롤아웃하는 동안 진행 중이 될 수 있고, 완료이거나 진행 실패일 수 있다.

디플로이먼트 진행 중

쿠버네티스는 다음 작업중 하나를 수행할 때 디플로이먼트를 진행 중 으로 표시한다.

  • 디플로이먼트로 새 레플리카셋을 생성.
  • 디플로이먼트로 새로운 레플리카셋을 스케일 업.
  • 디플로이먼트로 기존 레플리카셋을 스케일 다운.
  • 새 파드가 준비되거나 이용할 수 있음(최소 준비 시간(초) 동안 준비됨).

kubectl rollout status 를 사용해서 디플로이먼트의 진행사황을 모니터할 수 있다.

디플로이먼트 완료

쿠버네티스는 다음과 같은 특성을 가지게 되면 디플로이먼트를 완료 로 표시한다.

  • 디플로이먼트과 관련된 모든 레플리카가 지정된 최신 버전으로 업데이트 되었을 때. 즉, 요청한 모든 업데이트가 완료되었을 때.
  • 디플로이먼트와 관련한 모든 레플리카를 사용할 수 있을 때.
  • 디플로이먼트에 대해 이전 복제본이 실행되고 있지 않을 때.

kubectl rollout status 를 사용해서 디플로이먼트가 완료되었는지 확인할 수 있다. 만약 롤아웃이 성공적으로 완료되면 kubectl rollout status 는 종료 코드로 0이 반환된다.

kubectl rollout status deployment/nginx-deployment

이와 유사하게 출력된다.

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

그리고 kubectl rollout 의 종료 상태는 0(success)이다.

echo $?
0

디플로이먼트 실패

디플로이먼트시 새 레플리카셋인 완료되지 않은 상태에서는 배포를 시도하면 고착될 수 있다. 이 문제는 다음 몇 가지 요인으로 인해 발생한다.

  • 할당량 부족
  • 준비성 프로브(readiness probe)의 실패
  • 이미지 풀 에러
  • 권한 부족
  • 범위 제한
  • 애플리케이션 런타임의 잘못된 구성

이 조건을 찾을 수 있는 한 가지 방법은 디플로이먼트 스펙에서 데드라인 파라미터를 지정하는 것이다 (.spec.progressDeadlineSeconds). .spec.progressDeadlineSeconds 는 (디플로이먼트 상태에서) 디플로이먼트의 진행이 정지되었음을 나타내는 디플로이먼트 컨트롤러가 대기하는 시간(초)를 나타낸다.

다음 kubectl 명령어로 progressDeadlineSeconds 를 설정해서 컨트롤러가 10분 후 디플로이먼트에 대한 진행 상태의 부족에 대한 리포트를 수행하게 한다.

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

이와 유사하게 출력된다.

deployment.apps/nginx-deployment patched

만약 데드라인을 넘어서면 디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions 속성에 다음의 디플로이먼트 컨디션(DeploymentCondition)을 추가한다.

  • Type=Progressing
  • Status=False
  • Reason=ProgressDeadlineExceeded

컨디션 상태에 대한 자세한 내용은 쿠버네티스 API 규칙을 참고한다.

설정한 타임아웃이 낮거나 일시적으로 처리될 수 있는 다른 종료의 에러로 인해 디플로이먼트에 일시적인 에러가 발생할 수 있다. 예를 들어, 할당량이 부족하다고 가정해보자. 만약 디플로이먼트를 설명하려면 다음 섹션을 확인한다.

kubectl describe deployment nginx-deployment

이와 유사하게 출력된다.

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

만약 kubectl get deployment nginx-deployment -o yaml 을 실행하면 디플로이먼트 상태는 다음과 유사하다.

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

결국, 디플로이먼트 진행 데드라인을 넘어서면, 쿠버네티스는 진행 컨디션의 상태와 이유를 업데이트한다.

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

디플로이먼트를 스케일 다운하거나, 실행 중인 다른 컨트롤러를 스케일 다운하거나, 네임스페이스에서 할당량을 늘려서 할당량이 부족한 문제를 해결할 수 있다. 만약 할당량 컨디션과 디플로이먼트 롤아웃이 완료되어 디플로이먼트 컨트롤러를 만족한다면 성공한 컨디션의 디플로이먼트 상태가 업데이트를 볼 수 있다(Status=TrueReason=NewReplicaSetAvailable).

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

Type=AvailableStatus=True 는 디플로이먼트가 최소한의 가용성을 가지고 있는 것을 의미한다. 최소한의 가용성은 디플로이먼트 계획에 명시된 파라미터에 의해 결정된다. Type=ProgressingStatus=True 는 디플로이먼트가 롤아웃 도중에 진행 중 이거나, 성공적으로 완료되었으며, 진행 중 최소한으로 필요한 새로운 레플리카를 이용 가능하다는 것이다. (자세한 내용은 특정 조건의 이유를 참조한다. 이 경우 Reason=NewReplicaSetAvailable 는 배포가 완료되었음을 의미한다.)

kubectl rollout status 를 사용해서 디플로이먼트의 진행이 실패되었는지 확인할 수 있다. kubectl rollout status 는 디플로이먼트의 진행 데드라인을 초과하면 0이 아닌 종료 코드를 반환한다.

kubectl rollout status deployment/nginx-deployment

이와 유사하게 출력된다.

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

그리고 kubectl rollout 의 종료 상태는 1(error를 의미함)이다.

echo $?
1

실패한 디플로이먼트에서의 운영

완료된 디플로이먼트에 적용되는 모든 행동은 실패한 디플로이먼트에도 적용된다. 디플로이먼트 파드 템플릿에서 여러 개의 수정사항을 적용해야하는 경우 스케일 업/다운 하거나, 이전 수정 버전으로 롤백하거나, 일시 중지할 수 있다.

정책 초기화

디플로이먼트의 .spec.revisionHistoryLimit 필드를 설정해서 디플로이먼트에서 유지해야 하는 이전 레플리카셋의 수를 명시할 수 있다. 나머지는 백그라운드에서 가비지-수집이 진행된다. 기본적으로 10으로 되어 있다.

카나리 디플로이먼트

만약 디플로이먼트를 이용해서 일부 사용자 또는 서버에 릴리스를 롤아웃 하기 위해서는 리소스 관리에 설명된 카나리 패던에 따라 각 릴리스 마다 하나씩 여러 디플로이먼트를 생성할 수 있다.

디플로이먼트 사양 작성

다른 모든 쿠버네티스 설정과 마찬가지로 디플로이먼트에는 .apiVersion, .kind 그리고 .metadata 필드가 필요하다. 설정 파일 작업에 대한 일반적인 내용은 애플리케이션 배포하기, 컨테이너 구성하기 그리고 kubectl을 사용해서 리소스 관리하기 문서를 참조한다. 디플로이먼트 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

디플로이먼트에는 .spec 섹션도 필요하다.

파드 템플릿

.spec.template.spec.selector.spec 에서 유일한 필수 필드이다.

.spec.template파드 템플릿이다. 이것은 파드와 정확하게 동일한 스키마를 가지고 있고, 중첩된 것을 제외하면 apiVersionkind 를 가지고 있지 않는다.

파드에 필요한 필드 외에 디플로이먼트 파드 템플릿은 적절한 레이블과 적절한 재시작 정책을 명시해야 한다. 레이블의 경우 다른 컨트롤러와 겹치지 않도록 해야 한다. 자세한 것은 셀렉터를 참조한다.

.spec.template.spec.restartPolicy 에는 오직 Always 만 허용되고, 명시되지 않으면 기본값이 된다.

레플리카

.spec.replicas 은 필요한 파드의 수를 지정하는 선택적 필드이다. 이것의 기본값은 1이다.

셀렉터

.spec.selector 는 디플로이먼트의 대상이 되는 파드에 대해 레이블 셀렉터를 지정하는 필수 필드이다.

.spec.selector.spec.template.metadata.labels 과 일치해야 하며, 그렇지 않으면 API에 의해 거부된다.

API 버전 apps/v1 에서는 .spec.selector.metadata.labels 이 설정되지 않으면 .spec.template.metadata.labels 은 기본 설정되지 않는다. 그래서 이것들은 명시적으로 설정되어야 한다. 또한 apps/v1 에서는 디플로이먼트를 생성한 후에는 .spec.selector 이 변경되지 않는 점을 참고한다.

디플로이먼트는 템플릿의 .spec.template 와 다르거나 파드의 수가 .spec.replicas 를 초과할 경우 셀렉터와 일치하는 레이블을 가진 파드를 종료할 수 있다. 파드의 수가 의도한 수량보다 적을 경우 .spec.template 에 맞는 새 파드를 띄운다.

만약 셀렉터가 겹치는 컨트롤러가 어러 개 있는 경우, 컨트롤러는 서로 싸우고 올바르게 작동하지 않는다.

전략

.spec.strategy 는 이전 파드를 새로운 파드로 대체하는 전략을 명시한다. .spec.strategy.type 은 "재생성" 또는 "롤링업데이트"가 될 수 있다. "롤링업데이트"가 기본값이다.

디플로이먼트 재생성

기존의 모든 파드는 .spec.strategy.type==Recreate 이면 새 파드가 생성되기 전에 죽는다.

디플로이먼트 롤링 업데이트

디플로이먼트는 .spec.strategy.type==RollingUpdate 이면 파드를 롤링 업데이트 방식으로 업데이트 한다. maxUnavailablemaxSurge 를 명시해서 롤링 업데이트 프로세스를 제어할 수 있다.

최대 불가(Max Unavailable)

.spec.strategy.rollingUpdate.maxUnavailable 은 업데이트 프로세스 중에 사용할 수 없는 최대 파드의 수를 지정하는 선택적 필드이다. 이 값은 절대 숫자(예: 5) 또는 의도한 파드 비율(예: 10%)이 될 수 있다. 절대 값은 반올림해서 백분율로 계산한다. 만약 .spec.strategy.rollingUpdate.maxSurge 가 0이면 값이 0이 될 수 없다. 기본 값은 25% 이다.

예를 들어 이 값을 30%로 설정하면 롤링업데이트 시작시 즉각 이전 레플리카셋의 크기를 의도한 파드 중 70%를 스케일 다운할 수 있다. 새 파드가 준비되면 기존 레플리카셋을 스케일 다운할 수 있으며, 업데이트 중에 항상 사용 가능한 전체 파드의 수는 의도한 파드의 수의 70% 이상이 되도록 새 레플리카셋을 스케일 업할 수 있다.

최대 서지(Max Surge)

.spec.strategy.rollingUpdate.maxSurge 는 의도한 파드의 수에 대해 생성할 수 있는 최대 파드의 수를 지정하는 선택적 필드이다. 이 값은 절대 숫자(예: 5) 또는 의도한 파드 비율(예: 10%)이 될 수 있다. MaxUnavailable 값이 0이면 이 값은 0이 될 수 없다. 절대 값은 반올림해서 백분율로 계산한다. 기본 값은 25% 이다.

예를 들어 이 값을 30%로 설정하면 롤링업데이트 시작시 새 레플리카셋의 크기를 즉시 조정해서 기존 및 새 파드의 전체 갯수를 의도한 파드의 130%를 넘지 않도록 한다. 기존 파드가 죽으면 새로운 래플리카셋은 스케일 업할 수 있으며, 업데이트하는 동안 항상 실행하는 총 파드의 수는 최대 의도한 파드의 수의 130%가 되도록 보장한다.

진행 기한 시간(초)

.spec.progressDeadlineSeconds 는 디플로어먼트가 표면적으로 Type=Progressing, Status=False의 상태 그리고 리소스가 Reason=ProgressDeadlineExceeded 상태로 진행 실패를 보고하기 전에 디플로이먼트가 진행되는 것을 대기시키는 시간(초)를 명시하는 선택적 필드이다. 디플로이먼트 컨트롤러는 디플로이먼트를 계속 재시도 한다. 기본값은 600(초)이다. 미래에 자동화된 롤백이 구현된다면 디플로이먼트 컨트롤러는 상태를 관찰하고, 그 즉시 디플로이먼트를 롤백할 것이다.

만약 명시된다면 이 필드는 .spec.minReadySeconds 보다 커야 한다.

최소 대기 시간(초)

.spec.minReadySeconds 는 새롭게 생성된 파드의 컨테이너가 어떤 것과도 충돌하지 않고 사 용할 수 있도록 준비되어야 하는 최소 시간(초)을 지정하는 선택적 필드이다. 이 기본 값은 0이다(파드는 준비되는 즉시 사용할 수 있는 것으로 간주됨). 파드가 준비되었다고 간주되는 시기에 대한 자세한 내용은 컨테이너 프로브를 참조한다.

수정 버전 기록 제한

디플로이먼트의 수정 버전 기록은 자신이 컨트롤하는 레플리카셋에 저장된다.

.spec.revisionHistoryLimit 은 롤백을 허용하기 위해 보존할 이전 레플리카셋의 수를 지정하는 선택적 필드이다. 이 이전 레플리카셋은 etcd 의 리소스를 소비하고, kubectl get rs 의 결과를 가득차게 만든다. 각 디플로이먼트의 구성은 디플로이먼트의 레플리카셋에 저장된다. 이전 레플리카셋이 삭제되면 해당 디플로이먼트 수정 버전으로 롤백할 수 있는 기능이 사라진다. 기본적으로 10개의 기존 레플리카셋이 유지되지만 이상적인 값은 새로운 디플로이먼트의 빈도와 안정성에 따라 달라진다.

더욱 구체적으로 이 필드를 0으로 설정하면 레플리카가 0이 되며 이전 레플리카셋이 정리된다. 이 경우, 새로운 디플로이먼트 롤아웃을 취소할 수 없다. 새로운 디플로이먼트 롤아웃은 수정 버전 이력이 정리되기 때문이다.

일시 정지

.spec.paused 는 디플로이먼트를 일시 중지나 재개하기 위한 선택적 부울 필드이다. 일시 중지 된 디플로이먼트와 일시 중지 되지 않은 디플로이먼트 사이의 유일한 차이점은 일시 중지된 디플로이먼트는 PodTemplateSpec에 대한 변경 사항이 일시중지 된 경우 새 롤아웃을 트리거 하지 않는다. 디플로이먼트는 생성시 기본적으로 일시 중지되지 않는다.

다음 내용

3.4.2.2 - 레플리카셋

레플리카셋의 목적은 레플리카 파드 집합의 실행을 항상 안정적으로 유지하는 것이다. 이처럼 레플리카셋은 보통 명시된 동일 파드 개수에 대한 가용성을 보증하는데 사용한다.

레플리카셋의 작동 방식

레플리카셋을 정의하는 필드는 획득 가능한 파드를 식별하는 방법이 명시된 셀렉터, 유지해야 하는 파드 개수를 명시하는 레플리카의 개수, 그리고 레플리카 수 유지를 위해 생성하는 신규 파드에 대한 데이터를 명시하는 파드 템플릿을 포함한다. 그러면 레플리카셋은 필드에 지정된 설정을 충족하기 위해 필요한 만큼 파드를 만들고 삭제한다. 레플리카셋이 새로운 파드를 생성해야 할 경우, 명시된 파드 템플릿을 사용한다.

레플리카셋은 파드의 metadata.ownerReferences 필드를 통해 파드에 연결되며, 이는 현재 오브젝트가 소유한 리소스를 명시한다. 레플리카셋이 가지고 있는 모든 파드의 ownerReferences 필드는 해당 파드를 소유한 레플리카셋을 식별하기 위한 소유자 정보를 가진다. 이 링크를 통해 레플리카셋은 자신이 유지하는 파드의 상태를 확인하고 이에 따라 관리 한다.

레플리카셋은 셀렉터를 이용해서 필요한 새 파드를 식별한다. 만약 파드에 OwnerReference이 없거나 OwnerReference가 컨트롤러(Controller) 가 아니고 레플리카셋의 셀렉터와 일치한다면 레플리카셋이 즉각 파드를 가지게 될 것이다.

레플리카셋을 사용하는 시기

레플리카셋은 지정된 수의 파드 레플리카가 항상 실행되도록 보장한다. 그러나 디플로이먼트는 레플리카셋을 관리하고 다른 유용한 기능과 함께 파드에 대한 선언적 업데이트를 제공하는 상위 개념이다. 따라서 우리는 사용자 지정 오케스트레이션이 필요하거나 업데이트가 전혀 필요하지 않은 경우라면 레플리카셋을 직접적으로 사용하기 보다는 디플로이먼트를 사용하는 것을 권장한다.

이는 레플리카셋 오브젝트를 직접 조작할 필요가 없다는 것을 의미한다. 대신 디플로이먼트를 이용하고 사양 부분에서 애플리케이션을 정의하면 된다.

예시

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # 케이스에 따라 레플리카를 수정한다.
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

이 매니페스트를 frontend.yaml에 저장하고 쿠버네티스 클러스터에 적용하면 정의되어 있는 레플리카셋이 생성되고 레플리카셋이 관리하는 파드가 생성된다.

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

현재 배포된 레플리카셋을 확인할 수 있다.

kubectl get rs

그리고 생성된 프런트엔드를 볼 수 있다.

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

또한 레플리카셋의 상태를 확인할 수 있다.

kubectl describe rs/frontend

출력은 다음과 유사할 것이다.

Name:         frontend
Namespace:    default
Selector:     tier=frontend
Labels:       app=guestbook
              tier=frontend
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        gcr.io/google_samples/gb-frontend:v3
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  117s  replicaset-controller  Created pod: frontend-wtsmm
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-b2zdv
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-vcmts

마지막으로 파드가 올라왔는지 확인할 수 있다.

kubectl get pods

다음과 유사한 파드 정보를 볼 수 있다.

NAME             READY   STATUS    RESTARTS   AGE
frontend-b2zdv   1/1     Running   0          6m36s
frontend-vcmts   1/1     Running   0          6m36s
frontend-wtsmm   1/1     Running   0          6m36s

또한 파드들의 소유자 참조 정보가 해당 프런트엔드 레플리카셋으로 설정되어 있는지 확인할 수 있다. 확인을 위해서는 실행 중인 파드 중 하나의 yaml을 확인한다.

kubectl get pods frontend-b2zdv -o yaml

메타데이터의 ownerReferences 필드에 설정되어 있는 프런트엔드 레플리카셋의 정보가 다음과 유사하게 나오는 것을 볼 수 있다.

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-02-12T07:06:16Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-b2zdv
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...

템플릿을 사용하지 않는 파드의 획득

단독(bare) 파드를 생성하는 것에는 문제가 없지만, 단독 파드가 레플리카셋의 셀렉터와 일치하는 레이블을 가지지 않도록 하는 것을 강력하게 권장한다. 그 이유는 레플리카셋이 소유하는 파드가 템플릿에 명시된 파드에만 국한되지 않고, 이전 섹션에서 명시된 방식에 의해서도 다른 파드의 획득이 가능하기 때문이다.

이전 프런트엔드 레플리카셋 예제와 다음의 매니페스트에 명시된 파드를 가져와 참조한다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

기본 파드는 소유자 관련 정보에 컨트롤러(또는 오브젝트)를 가지지 않기 때문에 프런트엔드 레플리카셋의 셀렉터와 일치하면 즉시 레플리카셋에 소유된다.

프런트엔드 레플리카셋이 배치되고 초기 파드 레플리카가 셋업된 이후에, 레플리카 수 요구 사항을 충족시키기 위해서 신규 파드를 생성한다고 가정해보자.

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

새로운 파드는 레플리카셋에 의해 인식되며 레플리카셋이 필요한 수량을 초과하면 즉시 종료된다.

파드를 가져온다.

kubectl get pods

결과에는 새로운 파드가 이미 종료되었거나 종료가 진행 중인 것을 보여준다.

NAME             READY   STATUS        RESTARTS   AGE
frontend-b2zdv   1/1     Running       0          10m
frontend-vcmts   1/1     Running       0          10m
frontend-wtsmm   1/1     Running       0          10m
pod1             0/1     Terminating   0          1s
pod2             0/1     Terminating   0          1s

파드를 먼저 생성한다.

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

그 다음 레플리카셋을 생성한다.

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

레플리카셋이 해당 파드를 소유한 것을 볼 수 있으며 새 파드 및 기존 파드의 수가 레플리카셋이 필요로 하는 수와 일치할 때까지 사양에 따라 신규 파드만 생성한다. 파드를 가져온다.

kubectl get pods

다음 출력에서 볼 수 있다.

NAME             READY   STATUS    RESTARTS   AGE
frontend-hmmj2   1/1     Running   0          9s
pod1             1/1     Running   0          36s
pod2             1/1     Running   0          36s

이러한 방식으로 레플리카셋은 템플릿을 사용하지 않는 파드를 소유하게 된다.

레플리카셋 매니페스트 작성하기

레플리카셋은 모든 쿠버네티스 API 오브젝트와 마찬가지로 apiVersion, kind, metadata 필드가 필요하다. 레플리카셋에 대한 kind 필드의 값은 항상 레플리카셋이다. 쿠버네티스 1.9에서의 레플리카셋의 kind에 있는 API 버전 apps/v1은 현재 버전이며, 기본으로 활성화 되어 있다. API 버전 apps/v1beta2은 사용 중단(deprecated)되었다. API 버전에 대해서는 frontend.yaml 예제의 첫 번째 줄을 참고한다.

레플리카셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

레플리카셋도 .spec 섹션이 필요하다.

파드 템플릿

.spec.template은 레이블을 붙이도록 되어 있는 파드 템플릿이다. 우리는 frontend.yaml 예제에서 tier: frontend이라는 레이블을 하나 가지고 있다. 이 파드를 다른 컨트롤러가 취하지 않도록 다른 컨트롤러의 셀렉터와 겹치지 않도록 주의해야 한다.

템플릿의 재시작 정책 필드인 .spec.template.spec.restartPolicy는 기본값인 Always만 허용된다.

파드 셀렉터

.spec.selector 필드는 레이블 셀렉터이다. 앞서 논의한 것처럼 이 레이블은 소유될 가능성이 있는 파드를 식별하는데 사용된다. 우리 frontend.yaml 예제에서의 셀렉터는 다음과 같다.

matchLabels:
  tier: frontend

레플리카셋에서 .spec.template.metadata.labelsspec.selector과 일치해야 하며 그렇지 않으면 API에 의해 거부된다.

레플리카

.spec.replicas를 설정해서 동시에 동작하는 파드의 수를 지정할 수 있다. 레플리카셋은 파드의 수가 일치하도록 생성 및 삭제한다.

만약 .spec.replicas를 지정하지 않으면 기본값은 1이다.

레플리카셋 작업

레플리카셋과 해당 파드 삭제

레플리카셋 및 모든 파드를 삭제하려면 kubectl delete를 사용한다. 가비지 수집기는 기본적으로 종속되어 있는 모든 파드를 자동으로 삭제한다.

REST API또는 client-go 라이브러리를 이용할 때는 -d 옵션으로 propagationPolicyBackground또는 Foreground로 설정해야 한다. 예시:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"

레플리카셋만 삭제하기

레플리카셋을 --cascade=orphan 옵션과 함께 kubectl delete를 사용하면 연관 파드에 영향을 주지 않고 삭제할 수 있다. REST API 또는 client-go 라이브러리를 이용할 때는 propagationPolicyOrphan을 설정해야 한다. 예시:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"

원본이 삭제되면 새 레플리카셋을 생성해서 대체할 수 있다. 기존 .spec.selector와 신규 .spec.selector가 같으면 새 레플리카셋은 기존 파드를 선택한다. 하지만 신규 레플리카셋은 기존 파드를 신규 레플리카셋의 새롭고 다른 파드 템플릿에 일치시키는 작업을 수행하지는 않는다. 컨트롤 방식으로 파드를 새로운 사양으로 업데이트 하기 위해서는 디플로이먼트를 이용하면 된다. 이는 레플리카셋이 롤링 업데이트를 직접적으로 지원하지 않기 때문이다.

레플리카셋에서 파드 격리

레이블을 변경하면 레플리카셋에서 파드를 제거할 수 있다. 이 방식은 디버깅과 데이터 복구 등을 위해 서비스에서 파드를 제거하는 데 사용할 수 있다. 이 방식으로 제거된 파드는 자동으로 교체된다( 레플리카의 수가 변경되지 않는다고 가정한다).

레플리카셋의 스케일링

레플리카셋을 손쉽게 스케일 업 또는 다운하는 방법은 단순히 .spec.replicas 필드를 업데이트하면 된다. 레플리카셋 컨트롤러는 일치하는 레이블 셀렉터가 있는 파드가 의도한 수 만큼 가용하고 운영 가능하도록 보장한다.

스케일 다운할 때, 레플리카셋 컨트롤러는 스케일 다운할 파드의 우선순위를 정하기 위해 다음의 기준으로 가용 파드를 정렬하여 삭제할 파드를 결정한다.

  1. Pending 상태인 (+ 스케줄링할 수 없는) 파드가 먼저 스케일 다운된다.
  2. controller.kubernetes.io/pod-deletion-cost 어노테이션이 설정되어 있는 파드에 대해서는, 낮은 값을 갖는 파드가 먼저 스케일 다운된다.
  3. 더 많은 레플리카가 있는 노드의 파드가 더 적은 레플리카가 있는 노드의 파드보다 먼저 스케일 다운된다.
  4. 파드 생성 시간이 다르면, 더 최근에 생성된 파드가 이전에 생성된 파드보다 먼저 스케일 다운된다. (LogarithmicScaleDown 기능 게이트가 활성화되어 있으면 생성 시간이 정수 로그 스케일로 버킷화된다)

모든 기준에 대해 동등하다면, 스케일 다운할 파드가 임의로 선택된다.

파드 삭제 비용

FEATURE STATE: Kubernetes v1.22 [beta]

controller.kubernetes.io/pod-deletion-cost 어노테이션을 이용하여, 레플리카셋을 스케일 다운할 때 어떤 파드부터 먼저 삭제할지에 대한 우선순위를 설정할 수 있다.

이 어노테이션은 파드에 설정되어야 하며, [-2147483647, 2147483647] 범위를 갖는다. 이 어노테이션은 하나의 레플리카셋에 있는 다른 파드와의 상대적 삭제 비용을 나타낸다. 삭제 비용이 낮은 파드는 삭제 비용이 높은 파드보다 삭제 우선순위가 높다.

파드에 대해 이 값을 명시하지 않으면 기본값은 0이다. 음수로도 설정할 수 있다. 유효하지 않은 값은 API 서버가 거부한다.

이 기능은 베타 상태이며 기본적으로 활성화되어 있다. kube-apiserver와 kube-controller-manager에 대해 PodDeletionCost 기능 게이트를 이용하여 비활성화할 수 있다.

사용 예시

한 애플리케이션 내의 여러 파드는 각각 사용률이 다를 수 있다. 스케일 다운 시, 애플리케이션은 사용률이 낮은 파드를 먼저 삭제하고 싶을 수 있다. 파드를 자주 업데이트하는 것을 피하기 위해, 애플리케이션은 controller.kubernetes.io/pod-deletion-cost 값을 스케일 다운하기 전에 1회만 업데이트해야 한다 (파드 사용률에 비례하는 값으로 설정). 이 방식은 Spark 애플리케이션의 드라이버 파드처럼 애플리케이션이 스스로 다운스케일링을 수행하는 경우에 유효하다.

레플리카셋을 Horizontal Pod Autoscaler 대상으로 설정

레플리카셋은 Horizontal Pod Autoscalers (HPA)의 대상이 될 수 있다. 즉, 레플리카셋은 HPA에 의해 오토스케일될 수 있다. 다음은 이전에 만든 예시에서 만든 레플리카셋을 대상으로 하는 HPA 예시이다.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

이 매니페스트를 hpa-rs.yaml로 저장한 다음 쿠버네티스 클러스터에 적용하면 CPU 사용량에 따라 파드가 복제되는 오토스케일 레플리카셋 HPA가 생성된다.

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

또는 kubectl autoscale 커맨드을 사용해서 동일한 작업을 할 수 있다. (그리고 더 쉽다!)

kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50

레플리카셋의 대안

디플로이먼트(권장)

디플로이먼트는 레플리카셋을 소유하거나 업데이트를 하고, 파드의 선언적인 업데이트와 서버측 롤링 업데이트를 할 수 있는 오브젝트이다. 레플리카셋은 단독으로 사용할 수 있지만, 오늘날에는 주로 디플로이먼트로 파드의 생성과 삭제 그리고 업데이트를 오케스트레이션하는 메커니즘으로 사용한다. 디플로이먼트를 이용해서 배포할 때 생성되는 레플리카셋을 관리하는 것에 대해 걱정하지 않아도 된다. 디플로이먼트는 레플리카셋을 소유하거나 관리한다. 따라서 레플리카셋을 원한다면 디플로이먼트를 사용하는 것을 권장한다.

기본 파드

사용자가 직접 파드를 생성하는 경우와는 다르게, 레플리카셋은 노드 장애 또는 노드의 커널 업그레이드와 같은 관리 목적의 중단 등 어떤 이유로든 종료되거나 삭제된 파드를 교체한다. 이런 이유로 애플리케이션이 단일 파드가 필요하더라도 레플리카셋을 이용하는 것을 권장한다. 레플리카셋을 프로세스 관리자와 비교해서 생각해본다면, 레플리카셋은 단일 노드에서의 개별 프로세스들이 아닌 다수의 노드에 걸쳐있는 다수의 파드를 관리하는 것이다. 레플리카셋은 로컬 컨테이너의 재시작을 노드에 있는 어떤 에이전트에게 위임한다(예를들어 Kubelet 또는 도커).

스스로 종료되는 것이 예상되는 파드의 경우에는 레플리카셋 대신 을 이용한다 (즉, 배치 잡).

데몬셋

머신 모니터링 또는 머신 로깅과 같은 머신-레벨의 기능을 제공하는 파드를 위해서는 레플리카셋 대신 데몬셋을 사용한다. 이러한 파드의 수명은 머신의 수명과 연관되어 있고, 머신에서 다른 파드가 시작하기 전에 실행되어야 하며, 머신의 재부팅/종료가 준비되었을 때, 해당 파드를 종료하는 것이 안전하다.

레플리케이션 컨트롤러

레플리카셋은 레플리케이션 컨트롤러를 계승하였다. 이 두 개의 용도는 동일하고, 유사하게 동작하며, 레플리케이션 컨트롤러가 레이블 사용자 가이드에 설명된 설정-기반의 셀렉터의 요건을 지원하지 않는다는 점을 제외하면 유사하다. 따라서 레플리카셋이 레플리케이션 컨트롤러보다 선호된다.

다음 내용

3.4.2.3 - 스테이트풀셋

스테이트풀셋은 애플리케이션의 스테이트풀을 관리하는데 사용하는 워크로드 API 오브젝트이다.

파드 집합의 디플로이먼트와 스케일링을 관리하며, 파드들의 순서 및 고유성을 보장한다 .

디플로이먼트와 유사하게, 스테이트풀셋은 동일한 컨테이너 스펙을 기반으로 둔 파드들을 관리한다. 디플로이먼트와는 다르게, 스테이트풀셋은 각 파드의 독자성을 유지한다. 이 파드들은 동일한 스팩으로 생성되었지만, 서로 교체는 불가능하다. 다시 말해, 각각은 재스케줄링 간에도 지속적으로 유지되는 식별자를 가진다.

스토리지 볼륨을 사용해서 워크로드에 지속성을 제공하려는 경우, 솔루션의 일부로 스테이트풀셋을 사용할 수 있다. 스테이트풀셋의 개별 파드는 장애에 취약하지만, 퍼시스턴트 파드 식별자는 기존 볼륨을 실패한 볼륨을 대체하는 새 파드에 더 쉽게 일치시킬 수 있다.

스테이트풀셋 사용

스테이트풀셋은 다음 중 하나 또는 이상이 필요한 애플리케이션에 유용하다.

  • 안정된, 고유한 네트워크 식별자.
  • 안정된, 지속성을 갖는 스토리지.
  • 순차적인, 정상 배포(graceful deployment)와 스케일링.
  • 순차적인, 자동 롤링 업데이트.

위의 안정은 파드의 (재)스케줄링 전반에 걸친 지속성과 같은 의미이다. 만약 애플리케이션이 안정적인 식별자 또는 순차적인 배포, 삭제 또는 스케일링이 필요하지 않으면, 스테이트리스 레플리카셋(ReplicaSet)을 제공하는 워크로드 오브젝트를 사용해서 애플리케이션을 배포해야 한다. 디플로이먼트 또는 레플리카셋과 같은 컨트롤러가 스테이트리스 요구에 더 적합할 수 있다.

제한사항

  • 파드에 지정된 스토리지는 관리자에 의해 퍼시스턴트 볼륨 프로비저너를 기반으로 하는 storage class 를 요청해서 프로비전하거나 사전에 프로비전이 되어야 한다.
  • 스테이트풀셋을 삭제 또는 스케일 다운해도 스테이트풀셋과 연관된 볼륨이 삭제되지 않는다. 이는 일반적으로 스테이트풀셋과 연관된 모든 리소스를 자동으로 제거하는 것보다 더 중요한 데이터의 안전을 보장하기 위함이다.
  • 스테이트풀셋은 현재 파드의 네트워크 신원을 책임지고 있는 헤드리스 서비스가 필요하다. 사용자가 이 서비스를 생성할 책임이 있다.
  • 스테이트풀셋은 스테이트풀셋의 삭제 시 파드의 종료에 대해 어떠한 보증을 제공하지 않는다. 스테이트풀셋에서는 파드가 순차적이고 정상적으로 종료(graceful termination)되도록 하려면, 삭제 전 스테이트풀셋의 스케일을 0으로 축소할 수 있다.
  • 롤링 업데이트와 기본 파드 매니지먼트 폴리시 (OrderedReady)를 함께 사용시 복구를 위한 수동 개입이 필요한 파손 상태로 빠질 수 있다.

구성 요소

아래의 예시에서는 스테이트풀셋의 구성요소를 보여 준다.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

위의 예시에서:

  • 이름이 nginx라는 헤드리스 서비스는 네트워크 도메인을 컨트롤하는데 사용 한다.
  • 이름이 web인 스테이트풀셋은 3개의 nginx 컨테이너의 레플리카가 고유의 파드에서 구동될 것이라 지시하는 Spec을 갖는다.
  • volumeClaimTemplates은 퍼시스턴트 볼륨 프로비저너에서 프로비전한 퍼시스턴트 볼륨을 사용해서 안정적인 스토리지를 제공한다.

스테이트풀셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

파드 셀렉터

스테이트풀셋의 .spec.selector 필드는 .spec.template.metadata.labels 레이블과 일치하도록 설정해야 한다. 쿠버네티스 1.8 이전에서는 생략시에 .spec.selector 필드가 기본 설정 되었다. 1.8 과 이후 버전에서는 파드 셀렉터를 명시하지 않으면 스테이트풀셋 생성시 유효성 검증 오류가 발생하는 결과가 나오게 된다.

파드 신원

스테이트풀셋 파드는 순서, 안정적인 네트워크 신원 그리고 안정적인 스토리지로 구성되는 고유한 신원을 가진다. 신원은 파드가 어떤 노드에 있고, (재)스케줄과도 상관없이 파드에 붙어있다.

순서 색인

N개의 레플리카가 있는 스테이트풀셋은 스테이트풀셋에 있는 각 파드에 0에서 N-1 까지의 정수가 순서대로 할당되며 해당 스테이트풀셋 내에서 고유 하다.

안정적인 네트워크 신원

스테이트풀셋의 각 파드는 스테이트풀셋의 이름과 파드의 순번에서 호스트 이름을 얻는다. 호스트 이름을 구성하는 패턴은 $(statefulset name)-$(ordinal) 이다. 위의 예시에서 생성된 3개 파드의 이름은 web-0,web-1,web-2 이다. 스테이트풀셋은 스테이트풀셋에 있는 파드의 도메인을 제어하기위해 헤드리스 서비스를 사용할 수 있다. 이 서비스가 관리하는 도메인은 $(service name).$(namespace).svc.cluster.local 의 형식을 가지며, 여기서 "cluster.local"은 클러스터 도메인이다. 각 파드는 생성되면 $(podname).$(governing service domain) 형식을 가지고 일치되는 DNS 서브도메인을 가지며, 여기서 거버닝 서비스(governing service)는 스테이트풀셋의 serviceName 필드에 의해 정의된다.

클러스터에서 DNS가 구성된 방식에 따라, 새로 실행된 파드의 DNS 이름을 즉시 찾지 못할 수 있다. 이 동작은 클러스터의 다른 클라이언트가 파드가 생성되기 전에 파드의 호스트 이름에 대한 쿼리를 이미 보낸 경우에 발생할 수 있다. 네거티브 캐싱(DNS에서 일반적)은 이전에 실패한 조회 결과가 파드가 실행된 후에도 적어도 몇 초 동안 기억되고 재사용됨을 의미한다.

파드를 생성한 후 즉시 파드를 검색해야 하는 경우, 몇 가지 옵션이 있다.

  • DNS 조회에 의존하지 않고 쿠버네티스 API를 직접(예를 들어 watch 사용) 쿼리한다.
  • 쿠버네티스 DNS 공급자의 캐싱 시간(일반적으로 CoreDNS의 컨피그맵을 편집하는 것을 의미하며, 현재 30초 동안 캐시함)을 줄인다.

제한사항 섹션에서 언급한 것처럼 사용자는 파드의 네트워크 신원을 책임지는 헤드리스 서비스를 생성할 책임이 있다.

여기 클러스터 도메인, 서비스 이름, 스테이트풀셋 이름을 선택을 하고, 그 선택이 스테이트풀셋 파드의 DNS이름에 어떻게 영향을 주는지에 대한 약간의 예시가 있다.

클러스터 도메인 서비스 (ns/이름) 스테이트풀셋 (ns/이름) 스테이트풀셋 도메인 파드 DNS 파드 호스트 이름
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

안정된 스토리지

쿠버네티스는 각 VolumeClaimTemplate마다 하나의 퍼시스턴트 볼륨을 생성한다. 위의 nginx 예시에서 각 파드는 my-storage-class 라는 스토리지 클래스와 1 Gib의 프로비전된 스토리지를 가지는 단일 퍼시스턴트 볼륨을 받게 된다. 만약 스토리지 클래스가 명시되지 않은 경우, 기본 스토리지 클래스가 사용된다. 파드가 노드에서 스케줄 혹은 재스케줄이 되면 파드의 volumeMounts 는 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨이 마운트 된다. 참고로, 파드 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨은 파드 또는 스테이트풀셋이 삭제되더라도 삭제되지 않는다. 이것은 반드시 수동으로 해야 한다.

파드 이름 레이블

스테이트풀셋 컨트롤러(Controller) 가 파드를 생성할 때 파드 이름으로 statefulset.kubernetes.io/pod-name 레이블이 추가된다. 이 레이블로 스테이트풀셋의 특정 파드에 서비스를 연결할 수 있다.

디플로이먼트와 스케일링 보증

  • N개의 레플리카가 있는 스테이트풀셋이 파드를 배포할 때 연속해서 {0..N-1}의 순서로 생성한다.
  • 파드가 삭제될 때는 {N-1..0}의 순서인 역순으로 종료된다.
  • 파드에 스케일링 작업을 적용하기 전에 모든 선행 파드가 Running 및 Ready 상태여야 한다.
  • 파드가 종료되기 전에 모든 후속 파드가 완전히 종료 되어야 한다.

스테이트풀셋은 pod.Spec.TerminationGracePeriodSeconds 을 0으로 명시해서는 안된다. 이 방법은 안전하지 않으며, 사용하지 않기를 강권한다. 자세한 설명은 스테이트풀셋 파드 강제 삭제를 참고한다.

위의 nginx 예시가 생성될 때 web-0, web-1, web-2 순서로 3개 파드가 배포된다. web-1은 web-0이 Running 및 Ready 상태가 되기 전에는 배포되지 않으며, web-2 도 web-1이 Running 및 Ready 상태가 되기 전에는 배포되지 않는다. 만약 web-1이 Running 및 Ready 상태가 된 이후, web-2가 시작되기 전에 web-0이 실패하게 된다면, web-2는 web-0이 성공적으로 재시작이되고, Running 및 Ready 상태가 되기 전까지 시작되지 않는다.

만약 사용자가 배포된 예제의 스테이트풀셋을 replicas=1 으로 패치해서 스케일한 경우 web-2가 먼저 종료된다. web-1은 web-2가 완전히 종료 및 삭제되기 전까지 정지되지 않는다. 만약 web-2의 종료 및 완전히 중지되고, web-1이 종료되기 전에 web-0이 실패할 경우 web-1은 web-0이 Running 및 Ready 상태가 되기 전까지 종료되지 않는다.

파드 관리 정책

쿠버네티스 1.7 및 이후에는 스테이트풀셋의 .spec.podManagementPolicy 필드를 통해 고유성 및 신원 보증을 유지하면서 순차 보증을 완화한다.

OrderedReady 파드 관리

OrderedReady 파드 관리는 스테이트풀셋의 기본이다. 이것은 위에서 설명한 행위를 구현한다.

Parallel 파드 관리

Parallel 파드 관리는 스테이트풀셋 컨트롤러에게 모든 파드를 병렬로 실행 또는 종료하게 한다. 그리고 다른 파드의 실행이나 종료에 앞서 파드가 Running 및 Ready 상태가 되거나 완전히 종료되기를 기다리지 않는다. 이 옵션은 오직 스케일링 작업에 대한 동작에만 영향을 미친다. 업데이트는 영향을 받지 않는다.

업데이트 전략

스테이트풀셋의 .spec.updateStrategy 필드는 스테이트풀셋의 파드에 대한 컨테이너, 레이블, 리소스의 요청/제한 그리고 주석에 대한 자동화된 롤링 업데이트를 구성하거나 비활성화할 수 있다. 두 가지 가능한 전략이 있다.

OnDelete(삭제시)
스테이트풀셋의 .spec.updateStrategy.typeOnDelete 를 설정하며, 스테이트풀셋 컨트롤러는 스테이트풀셋의 파드를 자동으로 업데이트하지 않는다. 사용자는 컨트롤러가 스테이트풀셋의 .spec.template를 반영하는 수정된 새로운 파드를 생성하도록 수동으로 파드를 삭제해야 한다.
RollingUpdate(롤링 업데이트)
롤링 업데이트 의 업데이트 전략은 스테이트풀셋의 파드에 대한 롤링 업데이트를 구현한다. 롤링 업데이트는 .spec.updateStrategy 가 지정되지 않으면 기본 전략이 된다.

롤링 업데이트

스테이트풀셋에 롤링 업데이트.spec.updateStrategy.type 에 설정되면 스테이트풀셋 컨트롤러는 스테이트풀셋의 각 파드를 삭제 및 재생성한다. 이 과정에서 똑같이 순차적으로 파드가 종료되고(가장 큰 순서 색인에서부터에서 작은 순서 색인쪽으로), 각 파드의 업데이트는 한 번에 하나씩 한다.

쿠버네티스 컨트롤 플레인은 이전 버전을 업데이트 하기 전에, 업데이트된 파드가 실행 및 준비될 때까지 기다린다. .spec.minReadySeconds(최소 준비 시간 초 참조)를 설정한 경우, 컨트롤 플레인은 파드가 준비 상태로 전환된 후 해당 시간을 추가로 기다린 후 이동한다.

파티션 롤링 업데이트

롤링 업데이트 의 업데이트 전략은 .spec.updateStrategy.rollingUpdate.partition 를 명시해서 파티션 할 수 있다. 만약 파티션을 명시하면 스테이트풀셋의 .spec.template 가 업데이트 될 때 부여된 수가 파티션보다 크거나 같은 모든 파드가 업데이트 된다. 파티션보다 작은 수를 가진 모든 파드는 업데이트 되지 않으며, 삭제 된 경우라도 이전 버전에서 재생성된다. 만약 스테이트풀셋의 .spec.updateStrategy.rollingUpdate.partition.spec.replicas 보다 큰 경우 .spec.template 의 업데이트는 해당 파드에 전달하지 않는다. 대부분의 케이스는 파티션을 사용할 필요가 없지만 업데이트를 준비하거나, 카나리의 롤 아웃 또는 단계적인 롤 아웃을 행하려는 경우에는 유용하다.

강제 롤백

기본 파드 관리 정책 (OrderedReady)과 함께 롤링 업데이트를 사용할 경우 직접 수동으로 복구를 해야하는 고장난 상태가 될 수 있다.

만약 파드 템플릿을 Running 및 Ready 상태가 되지 않는 구성으로 업데이트하는 경우(예시: 잘못된 바이너리 또는 애플리케이션-레벨 구성 오류로 인한) 스테이트풀셋은 롤아웃을 중지하고 기다린다.

이 상태에서는 파드 템플릿을 올바른 구성으로 되돌리는 것으로 충분하지 않다. 알려진 이슈로 인해 스테이트풀셋은 손상된 파드가 준비(절대 되지 않음)될 때까지 기다리며 작동하는 구성으로 되돌아가는 시도를 하기 전까지 기다린다.

템플릿을 되돌린 이후에는 스테이트풀셋이 이미 잘못된 구성으로 실행하려고 시도한 모든 파드를 삭제해야 한다. 그러면 스테이트풀셋은 되돌린 템플릿을 사용해서 파드를 다시 생성하기 시작한다.

최소 준비 시간 초

FEATURE STATE: Kubernetes v1.22 [alpha]

.spec.minReadySeconds는 새로 생성된 파드가 사용가능하다고 간주되도록 컨테이너가 충돌되지 않고 준비되는 최소 시간 초를 지정하는 선택적 필드이다. 기본값은 0이다(파드는 준비되는 대로 사용 가능한 것으로 간주된다). 파드가 준비가 되는 시기에 대해 더 자세히 알아보고 싶다면, 컨테이너 프로브를 참고한다.

이 필드는 StatefulSetMinReadySeconds 기능 게이트를 사용하도록 설정한 경우에만 작동한다.

다음 내용

3.4.2.4 - 데몬셋

데몬셋 은 모든(또는 일부) 노드가 파드의 사본을 실행하도록 한다. 노드가 클러스터에 추가되면 파드도 추가된다. 노드가 클러스터에서 제거되면 해당 파드는 가비지(garbage)로 수집된다. 데몬셋을 삭제하면 데몬셋이 생성한 파드들이 정리된다.

데몬셋의 일부 대표적인 용도는 다음과 같다.

  • 모든 노드에서 클러스터 스토리지 데몬 실행
  • 모든 노드에서 로그 수집 데몬 실행
  • 모든 노드에서 노드 모니터링 데몬 실행

단순한 케이스에서는, 각 데몬 유형의 처리를 위해서 모든 노드를 커버하는 하나의 데몬셋이 사용된다. 더 복잡한 구성에서는 단일 유형의 데몬에 여러 데몬셋을 사용할 수 있지만, 각기 다른 하드웨어 유형에 따라 서로 다른 플래그, 메모리, CPU 요구가 달라진다.

데몬셋 사양 작성

데몬셋 생성

YAML 파일에 데몬셋 명세를 작성할 수 있다. 예를 들어 아래 daemonset.yaml 파일은 fluentd-elasticsearch 도커 이미지를 실행하는 데몬셋을 설명한다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # this toleration is to have the daemonset runnable on master nodes
      # remove it if your masters can't run pods
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

YAML 파일을 기반으로 데몬셋을 생성한다.

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

필수 필드

다른 모든 쿠버네티스 설정과 마찬가지로 데몬셋에는 apiVersion, kind 그리고 metadata 필드가 필요하다. 일반적인 설정파일 작업에 대한 정보는 스테이트리스 애플리케이션 실행하기kubectl을 사용한 오브젝트 관리를 참고한다.

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

데몬셋에는 .spec 섹션도 필요하다.

파드 템플릿

.spec.template.spec 의 필수 필드 중 하나이다.

.spec.template파드 템플릿이다. 이것은 중첩되어 있다는 점과 apiVersion 또는 kind 를 가지지 않는 것을 제외하면 파드와 정확히 같은 스키마를 가진다.

데몬셋의 파드 템플릿에는 파드의 필수 필드 외에도 적절한 레이블이 명시되어야 한다(파드 셀렉터를 본다).

데몬셋의 파드 템플릿의 RestartPolicyAlways 를 가져야 하며, 명시되지 않은 경우 기본으로 Always가 된다.

파드 셀렉터

.spec.selector 필드는 파드 셀렉터이다. 이것은 .spec.selector 와 같은 동작을 한다.

쿠버네티스 1.8 부터는 레이블이 .spec.template 와 일치하는 파드 셀렉터를 명시해야 한다. 파드 셀렉터는 비워두면 더 이상 기본 값이 설정이 되지 않는다. 셀렉터의 기본 값은 kubectl apply 과 호환되지 않는다. 또한, 한 번 데몬셋이 만들어지면 .spec.selector 의 변형은 가능하지 않다. 파드 셀렉터를 변형하면 의도하지 않게 파드는 고아가 되거나 사용자에게 혼란을 주는 것으로 밝혀졌다.

.spec.selector 는 다음 2개의 필드로 구성된 오브젝트이다.

  • matchLabels - 레플리케이션 컨트롤러.spec.selector 와 동일하게 작동한다.
  • matchExpressions - 키, 값 목록 그리고 키 및 값에 관련된 연산자를 명시해서 보다 정교한 셀렉터를 만들 수 있다.

2개의 필드가 명시되면 두 필드를 모두 만족하는 것(ANDed)이 결과가 된다.

만약 .spec.selector 를 명시하면, 이것은 .spec.template.metadata.labels 와 일치해야 한다. 일치하지 않는 구성은 API에 의해 거부된다.

오직 일부 노드에서만 파드 실행

만약 .spec.template.spec.nodeSelector 를 명시하면 데몬셋 컨트롤러는 노드 셀렉터와 일치하는 노드에 파드를 생성한다. 마찬가지로 .spec.template.spec.affinity 를 명시하면 데몬셋 컨트롤러는 노드 어피니티와 일치하는 노드에 파드를 생성한다. 만약 둘 중 하나를 명시하지 않으면 데몬셋 컨트롤러는 모든 노드에서 파드를 생성한다.

데몬 파드가 스케줄 되는 방법

기본 스케줄러로 스케줄

FEATURE STATE: Kubernetes v1.23 [stable]

데몬셋은 자격이 되는 모든 노드에서 파드 사본이 실행하도록 보장한다. 일반적으로 쿠버네티스 스케줄러에 의해 파드가 실행되는 노드가 선택된다. 그러나 데몬셋 파드는 데몬셋 컨트롤러에 의해 생성되고 스케줄된다. 이에 대한 이슈를 소개한다.

  • 파드 동작의 불일치: 스케줄 되기 위해서 대기 중인 일반 파드는 Pending 상태로 생성된다. 그러나 데몬셋 파드는 Pending 상태로 생성되지 않는다. 이것은 사용자에게 혼란을 준다.
  • 파드 선점은 기본 스케줄러에서 처리한다. 선점이 활성화되면 데몬셋 컨트롤러는 파드 우선순위와 선점을 고려하지 않고 스케줄 한다.

ScheduleDaemonSetPods 로 데몬셋 파드에 .spec.nodeName 용어 대신 NodeAffinity 용어를 추가해서 데몬셋 컨트롤러 대신 기본 스케줄러를 사용해서 데몬셋을 스케줄할 수 있다. 이후에 기본 스케줄러를 사용해서 대상 호스트에 파드를 바인딩한다. 만약 데몬셋 파드에 이미 노드 선호도가 존재한다면 교체한다(대상 호스트를 선택하기 전에 원래 노드의 어피니티가 고려된다). 데몬셋 컨트롤러는 데몬셋 파드를 만들거나 수정할 때만 이런 작업을 수행하며, 데몬셋의 spec.template 은 변경되지 않는다.

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

또한, 데몬셋 파드에 node.kubernetes.io/unschedulable:NoSchedule 이 톨러레이션(toleration)으로 자동으로 추가된다. 기본 스케줄러는 데몬셋 파드를 스케줄링시 unschedulable 노드를 무시한다.

테인트(taints)와 톨러레이션(tolerations)

데몬 파드는 테인트와 톨러레이션을 존중하지만, 다음과 같이 관련 기능에 따라 자동적으로 데몬셋 파드에 톨러레이션을 추가한다.

톨러레이션 키 영향 버전 설명
node.kubernetes.io/not-ready NoExecute 1.13+ 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다.
node.kubernetes.io/unreachable NoExecute 1.13+ 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다.
node.kubernetes.io/disk-pressure NoSchedule 1.8+ 데몬셋 파드는 기본 스케줄러에서 디스크-압박(disk-pressure) 속성을 허용한다.
node.kubernetes.io/memory-pressure NoSchedule 1.8+ 데몬셋 파드는 기본 스케줄러에서 메모리-압박(memory-pressure) 속성을 허용한다.
node.kubernetes.io/unschedulable NoSchedule 1.12+ 데몬셋 파드는 기본 스케줄러의 스케줄할 수 없는(unschedulable) 속성을 극복한다.
node.kubernetes.io/network-unavailable NoSchedule 1.12+ 호스트 네트워크를 사용하는 데몬셋 파드는 기본 스케줄러에 의해 이용할 수 없는 네트워크(network-unavailable) 속성을 극복한다.

데몬 파드와 통신

데몬셋의 파드와 통신할 수 있는 몇 가지 패턴은 다음과 같다.

  • 푸시(Push): 데몬셋의 파드는 통계 데이터베이스와 같은 다른 서비스로 업데이트를 보내도록 구성되어 있다. 그들은 클라이언트들을 가지지 않는다.
  • 노드IP와 알려진 포트: 데몬셋의 파드는 호스트 포트를 사용할 수 있으며, 노드IP를 통해 파드에 접근할 수 있다. 클라이언트는 노드IP를 어떻게든지 알고 있으며, 관례에 따라 포트를 알고 있다.
  • DNS: 동일한 파드 셀렉터로 헤드리스 서비스를 만들고, 그 다음에 엔드포인트 리소스를 사용해서 데몬셋을 찾거나 DNS에서 여러 A레코드를 검색한다.
  • 서비스: 동일한 파드 셀렉터로 서비스를 생성하고, 서비스를 사용해서 임의의 노드의 데몬에 도달한다(특정 노드에 도달할 방법이 없다).

데몬셋 업데이트

만약 노드 레이블이 변경되면, 데몬셋은 새로 일치하는 노드에 즉시 파드를 추가하고, 새로 일치하지 않는 노드에서 파드를 삭제한다.

사용자는 데몬셋이 생성하는 파드를 수정할 수 있다. 그러나 파드는 모든 필드가 업데이트 되는 것을 허용하지 않는다. 또한 데몬셋 컨트롤러는 다음에 노드(동일한 이름으로)가 생성될 때 원본 템플릿을 사용한다.

사용자는 데몬셋을 삭제할 수 있다. 만약 kubectl 에서 --cascade=orphan 를 명시하면 파드는 노드에 남게 된다. 이후에 동일한 셀렉터로 새 데몬셋을 생성하면, 새 데몬셋은 기존 파드를 채택한다. 만약 파드를 교체해야 하는 경우 데몬셋은 updateStrategy 에 따라 파드를 교체한다.

사용자는 데몬셋에서 롤링 업데이트를 수행할 수 있다.

데몬셋의 대안

초기화 스크립트

데몬 프로세스를 직접 노드에서 시작해서 실행하는 것도 당연히 가능하다. (예: init, upstartd 또는 systemd 를 사용). 이 방법도 문제는 전혀 없다. 그러나 데몬셋을 통해 데몬 프로세스를 실행하면 몇 가지 이점 있다.

  • 애플리케이션과 동일한 방법으로 데몬을 모니터링하고 로그 관리를 할 수 있다.
  • 데몬 및 애플리케이션과 동일한 구성 언어와 도구(예: 파드 템플릿, kubectl).
  • 리소스 제한이 있는 컨테이너에서 데몬을 실행하면 앱 컨테이너에서 데몬간의 격리를 증가시킨다. 그러나 이것은 파드가 아닌 컨테이너에서 데몬을 실행해서 이루어진다 (예: 도커에서 직접적으로 시작).

베어(Bare) 파드

직접적으로 파드를 실행할 특정한 노드를 명시해서 파드를 생성할 수 있다. 그러나 데몬셋은 노드 장애 또는 커널 업그레이드와 같이 변경사항이 많은 노드 유지보수의 경우를 비롯하여 어떠한 이유로든 삭제되거나 종료된 파드를 교체한다. 따라서 개별 파드를 생성하는 것보다는 데몬 셋을 사용해야 한다.

스태틱(static) 파드

Kubelet이 감시하는 특정 디렉터리에 파일을 작성하는 파드를 생성할 수 있다. 이것을 스태틱 파드라고 부른다. 데몬셋과는 다르게 스태틱 파드는 kubectl 또는 다른 쿠버네티스 API 클라이언트로 관리할 수 없다. 스태틱 파드는 API 서버에 의존하지 않기 때문에 클러스터 부트스트랩(bootstraping)하는 경우에 유용하다. 또한 스태틱 파드는 향후에 사용 중단될 수 있다.

디플로이먼트

데몬셋은 파드를 생성한다는 점에서 디플로이먼트와 유사하고, 해당 파드에서는 프로세스가 종료되지 않을 것으로 예상한다(예: 웹 서버).

파드가 실행되는 호스트를 정확하게 제어하는 것보다 레플리카의 수를 스케일링 업 및 다운 하고, 업데이트 롤아웃이 더 중요한 프런트 엔드와 같은 것은 스테이트리스 서비스의 디플로이먼트를 사용한다. 데몬셋이 특정 노드에서 다른 파드가 올바르게 실행되도록 하는 노드 수준 기능을 제공한다면, 파드 사본이 항상 모든 호스트 또는 특정 호스트에서 실행되는 것이 중요한 경우에 데몬셋을 사용한다.

예를 들어, 네트워크 플러그인은 데몬셋으로 실행되는 컴포넌트를 포함할 수 있다. 데몬셋 컴포넌트는 작동 중인 노드가 정상적인 클러스터 네트워킹을 할 수 있도록 한다.

다음 내용

3.4.2.5 - 잡

잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료될 때까지 계속해서 파드의 실행을 재시도한다. 파드가 성공적으로 완료되면, 성공적으로 완료된 잡을 추적한다. 지정된 수의 성공 완료에 도달하면, 작업(즉, 잡)이 완료된다. 잡을 삭제하면 잡이 생성한 파드가 정리된다. 작업을 일시 중지하면 작업이 다시 재개될 때까지 활성 파드가 삭제된다.

간단한 사례는 잡 오브젝트를 하나 생성해서 파드 하나를 안정적으로 실행하고 완료하는 것이다. 첫 번째 파드가 실패 또는 삭제된 경우(예로는 노드 하드웨어의 실패 또는 노드 재부팅) 잡 오브젝트는 새로운 파드를 기동시킨다.

잡을 사용하면 여러 파드를 병렬로 실행할 수도 있다.

잡을 스케줄에 따라 구동하고 싶은 경우(단일 작업이든, 여러 작업의 병렬 수행이든), 크론잡(CronJob)을 참고한다.

예시 잡 실행하기

다음은 잡 설정 예시이다. 예시는 파이(π)의 2000 자리까지 계산해서 출력한다. 이를 완료하는 데 약 10초가 소요된다.

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

이 명령으로 예시를 실행할 수 있다.

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml

출력 결과는 다음과 같다.

job.batch/pi created

kubectl 을 사용해서 잡 상태를 확인한다.

kubectl describe jobs/pi

출력 결과는 다음과 같다.

Name:           pi
Namespace:      default
Selector:       controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                job-name=pi
Annotations:    kubectl.kubernetes.io/last-applied-configuration:
                  {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":...
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           job-name=pi
  Containers:
   pi:
    Image:      perl
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  14m   job-controller  Created pod: pi-5rwd7

kubectl get pods 를 사용해서 잡의 완료된 파드를 본다.

잡에 속하는 모든 파드를 기계적으로 읽을 수 있는 양식으로 나열하려면, 다음과 같은 명령을 사용할 수 있다.

pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

출력 결과는 다음과 같다.

pi-5rwd7

여기서 셀렉터는 잡의 셀렉터와 동일하다. --output=jsonpath 옵션은 반환된 목록에 있는 각 파드의 이름으로 표현식을 지정한다.

파드 중 하나를 표준 출력으로 본다.

kubectl logs $pods

출력 결과는 다음과 같다.

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

잡 사양 작성하기

다른 쿠버네티스의 설정과 마찬가지로 잡에는 apiVersion, kind 그리고 metadata 필드가 필요하다. 잡의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

잡에는 .spec 섹션도 필요하다.

파드 템플릿

.spec.template.spec 의 유일한 필수 필드이다.

.spec.template파드 템플릿이다. 이것은 apiVersion 또는 kind 가 없다는 것을 제외한다면 파드와 정확하게 같은 스키마를 가지고 있다.

추가로 파드의 필수 필드 외에도 잡의 파드 템플릿은 적절한 레이블(파드 셀렉터를 본다)과 적절한 재시작 정책을 명시해야 한다.

Never 또는 OnFailure 와 같은 RestartPolicy만 허용된다.

파드 셀렉터

.spec.selector 필드는 선택 사항이다. 대부분의 케이스에서 지정해서는 안된다. 자신의 파드 셀렉터를 지정하기 섹션을 참고한다.

잡에 대한 병렬 실행

잡으로 실행하기에 적합한 작업 유형은 크게 세 가지가 있다.

  1. 비-병렬(Non-parallel) 잡:
    • 일반적으로, 파드가 실패하지 않은 한, 하나의 파드만 시작된다.
    • 파드가 성공적으로 종료하자마자 즉시 잡이 완료된다.
  2. 고정적(fixed)인 완료 횟수 를 가진 병렬 잡:
    • .spec.completions 에 0이 아닌 양수 값을 지정한다.
    • 잡은 전체 작업을 나타내며, .spec.completions 성공한 파드가 있을 때 완료된다.
    • .spec.completionMode="Indexed" 를 사용할 때, 각 파드는 0에서 .spec.completions-1 범위 내의 서로 다른 인덱스를 가져온다.
  3. 작업 큐(queue) 가 있는 병렬 잡:
    • .spec.completions 를 지정하지 않고, .spec.parallelism 를 기본으로 한다.
    • 파드는 각자 또는 외부 서비스 간에 조정을 통해 각각의 작업을 결정해야 한다. 예를 들어 파드는 작업 큐에서 최대 N 개의 항목을 일괄로 가져올(fetch) 수 있다.
    • 각 파드는 모든 피어들의 작업이 완료되었는지 여부를 독립적으로 판단할 수 있으며, 결과적으로 전체 잡이 완료되게 한다.
    • 잡의 모든 파드가 성공적으로 종료되면, 새로운 파드는 생성되지 않는다.
    • 하나 이상의 파드가 성공적으로 종료되고, 모든 파드가 종료되면 잡은 성공적으로 완료된다.
    • 성공적으로 종료된 파드가 하나라도 생긴 경우, 다른 파드들은 해당 작업을 지속하지 않아야 하며 어떠한 출력도 작성하면 안 된다. 파드들은 모두 종료되는 과정에 있어야 한다.

비-병렬 잡은 .spec.completions.spec.parallelism 모두를 설정하지 않은 채로 둘 수 있다. 이때 둘 다 설정하지 않은 경우 1이 기본으로 설정된다.

고정적인 완료 횟수 잡은 .spec.completions 을 필요한 완료 횟수로 설정해야 한다. .spec.parallelism 을 설정할 수 있고, 설정하지 않으면 1이 기본으로 설정된다.

작업 큐 잡은 .spec.completions 를 설정하지 않은 상태로 두고, .spec.parallelism 을 음수가 아닌 정수로 설정해야 한다.

다른 유형의 잡을 사용하는 방법에 대한 더 자세한 정보는 잡 패턴 섹션을 본다.

병렬 처리 제어하기

요청된 병렬 처리(.spec.parallelism)는 음수가 아닌 값으로 설정할 수 있다. 만약 지정되지 않은 경우에는 1이 기본이 된다. 만약 0으로 지정되면 병렬 처리가 증가할 때까지 사실상 일시 중지된다.

실제 병렬 처리(모든 인스턴스에서 실행되는 파드의 수)는 여러가지 이유로 요청된 병렬 처리보다 많거나 적을 수 있다.

  • 고정적인 완료 횟수(fixed completion count) 잡의 경우, 병렬로 실행 중인 파드의 수는 남은 완료 수를 초과하지 않는다. .spec.parallelism 의 더 큰 값은 사실상 무시된다.
  • 작업 큐 잡은 파드가 성공한 이후에 새로운 파드가 시작되지 않는다. 그러나 나머지 파드는 완료될 수 있다.
  • 만약 잡 컨트롤러 가 반응할 시간이 없는 경우
  • 만약 잡 컨트롤러가 어떤 이유(ResourceQuota 의 부족, 권한 부족 등)로든 파드 생성에 실패한 경우, 요청한 것보다 적은 수의 파드가 있을 수 있다.
  • 잡 컨트롤러는 동일한 잡에서 과도하게 실패한 이전 파드들로 인해 새로운 파드의 생성을 조절할 수 있다.
  • 파드가 정상적으로(gracefully) 종료되면, 중지하는데 시간이 소요된다.

완료 모드

FEATURE STATE: Kubernetes v1.22 [beta]

완료 횟수가 고정적인 완료 횟수 즉, null이 아닌 .spec.completions 가 있는 잡은 .spec.completionMode 에 지정된 완료 모드를 가질 수 있다.

  • NonIndexed (기본값): .spec.completions 가 성공적으로 완료된 파드가 있는 경우 작업이 완료된 것으로 간주된다. 즉, 각 파드 완료는 서로 상동하다(homologous). null .spec.completions 가 있는 잡은 암시적으로 NonIndexed 이다.
  • Indexed: 잡의 파드는 연결된 완료 인덱스를 0에서 .spec.completions-1 까지 가져온다. 이 인덱스는 다음의 세 가지 메카니즘으로 얻을 수 있다.
    • 파드 어노테이션 batch.kubernetes.io/job-completion-index.
    • 파드 호스트네임 중 일부($(job-name)-$(index) 형태). 인덱스된(Indexed) 잡과 서비스를 결합하여 사용하고 있다면, 잡에 속한 파드는 DNS를 이용하여 서로를 디스커버 하기 위해 사전에 결정된 호스트네임을 사용할 수 있다.
    • 컨테이너화된 태스크의 경우, JOB_COMPLETION_INDEX 환경 변수. 각 인덱스에 대해 성공적으로 완료된 파드가 하나 있으면 작업이 완료된 것으로 간주된다. 이 모드를 사용하는 방법에 대한 자세한 내용은 정적 작업 할당을 사용한 병렬 처리를 위해 인덱싱된 잡을 참고한다. 참고로, 드물기는 하지만, 동일한 인덱스에 대해 둘 이상의 파드를 시작할 수 있지만, 그 중 하나만 완료 횟수에 포함된다.

파드와 컨테이너 장애 처리하기

파드내 컨테이너의 프로세스가 0이 아닌 종료 코드로 종료되었거나 컨테이너 메모리 제한을 초과해서 죽는 등의 여러가지 이유로 실패할 수 있다. 만약 이런 일이 발생하고 .spec.template.spec.restartPolicy = "OnFailure" 라면 파드는 노드에 그대로 유지되지만, 컨테이너는 다시 실행된다. 따라서 프로그램은 로컬에서 재시작될 때의 케이스를 다루거나 .spec.template.spec.restartPolicy = "Never" 로 지정해야 한다. 더 자세한 정보는 파드 라이프사이클restartPolicy 를 본다.

파드가 노드에서 내보내지는 경우(노드 업그레이드, 재부팅, 삭제 등) 또는 파드의 컨테이너가 실패 되고 .spec.template.spec.restartPolicy = "Never" 로 설정됨과 같은 여러 이유로 전체 파드가 실패할 수 있다. 파드가 실패하면 잡 컨트롤러는 새 파드를 시작한다. 이 의미는 애플리케이션이 새 파드에서 재시작될 때 이 케이스를 처리해야 한다는 점이다. 특히, 이전 실행으로 인한 임시파일, 잠금, 불완전한 출력 그리고 이와 유사한 것들을 처리해야 한다.

.spec.parallelism = 1, .spec.completions = 1 그리고 .spec.template.spec.restartPolicy = "Never" 를 지정하더라도 같은 프로그램을 두 번 시작하는 경우가 있다는 점을 참고한다.

.spec.parallelism 그리고 .spec.completions 를 모두 1보다 크게 지정한다면 한번에 여러 개의 파드가 실행될 수 있다. 따라서 파드는 동시성에 대해서도 관대(tolerant)해야 한다.

파드 백오프(backoff) 실패 정책

구성 등의 논리적 오류로 인해 약간의 재시도 이후에 잡을 실패하게 만들려는 경우가 있다. 이렇게 하려면 .spec.backoffLimit 에 잡을 실패로 간주하기 이전에 재시도할 횟수를 설정한다. 백오프 제한은 기본적으로 6으로 설정되어 있다. 잡과 관련한 실패한 파드는 최대 6분안에서 기하급수적으로 증가하는 백-오프 지연 (10초, 20초, 40초 ...) 한도가 되어 잡 컨트롤러에 의해 재생성된다. 잡의 파드가 삭제되거나 해당 시간 동안 잡에 대한 다른 파드가 실패 없이 성공했을 때 백 오프 카운트가 재설정된다.

잡의 종료와 정리

잡이 완료되면 파드가 더 이상 생성되지도 않지만, 일반적으로는 삭제되지도 않는다.
이를 유지하면 완료된 파드의 로그를 계속 보며 에러, 경고 또는 다른 기타 진단 출력을 확인할 수 있다. 잡 오브젝트는 완료된 후에도 상태를 볼 수 있도록 남아 있다. 상태를 확인한 후 이전 잡을 삭제하는 것은 사용자의 몫이다. kubectl 로 잡을 삭제할 수 있다 (예: kubectl delete jobs/pi 또는 kubectl delete -f ./job.yaml). kubectl 을 사용해서 잡을 삭제하면 생성된 모든 파드도 함께 삭제된다.

기본적으로 파드의 실패(restartPolicy=Never) 또는 컨테이너가 오류(restartPolicy=OnFailure)로 종료되지 않는 한, 잡은 중단되지 않고 실행되고 이때 위에서 설명했던 .spec.backoffLimit 까지 연기된다. .spec.backoffLimit 에 도달하면 잡은 실패로 표기되고 실행 중인 모든 파드는 종료된다.

잡을 종료하는 또 다른 방법은 유효 데드라인을 설정하는 것이다. 잡의 .spec.activeDeadlineSeconds 필드를 초 단위로 설정하면 된다. activeDeadlineSeconds 는 생성된 파드의 수에 관계 없이 잡의 기간에 적용된다. 잡이 activeDeadlineSeconds 에 도달하면, 실행 중인 모든 파드가 종료되고 잡의 상태는 reason: DeadlineExceeded 와 함께 type: Failed 가 된다.

잡의 .spec.activeDeadlineSeconds.spec.backoffLimit 보다 우선한다는 점을 참고한다. 따라서 하나 이상 실패한 파드를 재시도하는 잡은 backoffLimit 에 도달하지 않은 경우에도 activeDeadlineSeconds 에 지정된 시간 제한에 도달하면 추가 파드를 배포하지 않는다.

예시:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

잡의 사양과 잡의 파드 템플릿 사양에는 모두 activeDeadlineSeconds 필드가 있다는 점을 참고한다. 이 필드를 적절한 레벨로 설정해야 한다.

restartPolicy 는 잡 자체에 적용되는 것이 아니라 파드에 적용된다는 점을 유념한다. 잡의 상태가 type: Failed 이 되면, 잡의 자동 재시작은 없다. 즉, .spec.activeDeadlineSeconds.spec.backoffLimit 로 활성화된 잡의 종료 메커니즘은 영구적인 잡의 실패를 유발하며 이를 해결하기 위해 수동 개입이 필요하다.

완료된 잡을 자동으로 정리

완료된 잡은 일반적으로 시스템에서 더 이상 필요로 하지 않는다. 시스템 내에 이를 유지한다면 API 서버에 부담이 된다. 만약 크론잡과 같은 상위 레벨 컨트롤러가 잡을 직접 관리하는 경우, 지정된 용량 기반 정리 정책에 따라 크론잡이 잡을 정리할 수 있다.

완료된 잡을 위한 TTL 메커니즘

FEATURE STATE: Kubernetes v1.21 [beta]

완료된 잡 (Complete 또는 Failed)을 자동으로 정리하는 또 다른 방법은 잡의 .spec.ttlSecondsAfterFinished 필드를 지정해서 완료된 리소스에 대해 TTL 컨트롤러에서 제공하는 TTL 메커니즘을 사용하는 것이다.

TTL 컨트롤러는 잡을 정리하면 잡을 계단식으로 삭제한다. 즉, 잡과 함께 파드와 같은 종속 오브젝트를 삭제한다. 잡을 삭제하면 finalizer와 같은 라이프사이클 보증이 보장되는 것을 참고한다.

예시:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

pi-with-ttl 잡은 완료 후 100 초 이후에 자동으로 삭제될 수 있다.

만약 필드를 0 으로 설정하면, 잡이 완료된 직후에 자동으로 삭제되도록 할 수 있다. 만약 필드를 설정하지 않으면, 이 잡이 완료된 후에 TTL 컨트롤러에 의해 정리되지 않는다.

잡 패턴

잡 오브젝트를 사용해서 신뢰할 수 있는 파드의 병렬 실행을 지원할 수 있다. 잡 오브젝트는 과학 컴퓨팅(scientific computing)에서 일반적으로 사용되는 밀접하게 통신하는 병렬 프로세스를 지원하도록 설계되지 않았다. 잡 오브젝트는 독립적이지만 관련된 작업 항목 집합의 병렬 처리를 지원한다. 여기에는 전송할 이메일들, 렌더링할 프레임, 코드 변환이 필요한 파일, NoSQL 데이터베이스에서의 키 범위 스캔 등이 있다.

복잡한 시스템에는 여러 개의 다른 작업 항목 집합이 있을 수 있다. 여기서는 사용자와 함께 관리하려는 하나의 작업 항목 집합 — 배치 잡 을 고려하고 있다.

병렬 계산에는 몇몇 다른 패턴이 있으며 각각의 장단점이 있다. 트레이드오프는 다음과 같다.

  • 각 작업 항목에 대한 하나의 잡 오브젝트 vs 모든 작업 항목에 대한 단일 잡 오브젝트. 후자는 작업 항목 수가 많은 경우 더 적합하다. 전자는 사용자와 시스템이 많은 수의 잡 오브젝트를 관리해야 하는 약간의 오버헤드를 만든다.
  • 작업 항목과 동일한 개수의 파드 생성 vs 각 파드에서 다수의 작업 항목을 처리. 전자는 일반적으로 기존 코드와 컨테이너를 거의 수정할 필요가 없다. 후자는 이전 글 머리표(-)와 비슷한 이유로 많은 수의 작업 항목에 적합하다.
  • 여러 접근 방식이 작업 큐를 사용한다. 이를 위해서는 큐 서비스를 실행하고, 작업 큐를 사용하도록 기존 프로그램이나 컨테이너를 수정해야 한다. 다른 접근 방식들은 기존에 컨테이너화된 애플리케이션에 보다 쉽게 적용할 수 있다.

여기에 트레이드오프가 요약되어 있고, 2열에서 4열까지가 위의 트레이드오프에 해당한다. 패턴 이름은 예시와 더 자세한 설명을 위한 링크이다.

패턴 단일 잡 오브젝트 작업 항목보다 파드가 적은가? 수정되지 않은 앱을 사용하는가?
작업 항목 당 파드가 있는 큐 때때로
가변 파드 수를 가진 큐
정적 작업 할당을 사용한 인덱싱된 잡
잡 템플릿 확장

.spec.completions 로 완료를 지정할 때, 잡 컨트롤러에 의해 생성된 각 파드는 동일한 사양을 갖는다. 이 의미는 작업의 모든 파드는 동일한 명령 줄과 동일한 이미지, 동일한 볼륨, (거의) 동일한 환경 변수를 가진다는 점이다. 이 패턴은 파드가 다른 작업을 수행하도록 배열하는 다른 방법이다.

이 표는 각 패턴에 필요한 .spec.parallelism 그리고 .spec.completions 설정을 보여준다. 여기서 W 는 작업 항목의 수이다.

패턴 .spec.completions .spec.parallelism
작업 항목 당 파드가 있는 큐 W any
가변 파드 수를 가진 큐 null any
정적 작업 할당을 사용한 인덱싱된 잡 W any
잡 템플릿 확장 1 1이어야 함

고급 사용법

잡 일시 중지

FEATURE STATE: Kubernetes v1.22 [beta]

잡이 생성되면, 잡 컨트롤러는 잡의 요구 사항을 충족하기 위해 즉시 파드 생성을 시작하고 잡이 완료될 때까지 계속한다. 그러나, 잡의 실행을 일시적으로 중단하고 나중에 다시 시작할 수도 있다. 잡을 일시 중지하려면, 잡의 .spec.suspend 필드를 true로 업데이트할 수 있다. 나중에, 다시 재개하려면, false로 업데이트한다. .spec.suspend 로 설정된 잡을 생성하면 일시 중지된 상태로 생성된다.

잡이 일시 중지에서 재개되면, 해당 .status.startTime 필드가 현재 시간으로 재설정된다. 즉, 잡이 일시 중지 및 재개되면 .spec.activeDeadlineSeconds 타이머가 중지되고 재설정된다.

잡을 일시 중지하면 모든 활성 파드가 삭제된다. 잡이 일시 중지되면, SIGTERM 시그널로 파드가 종료된다. 파드의 정상 종료 기간이 적용되며 사용자의 파드는 이 기간 동안에 이 시그널을 처리해야 한다. 나중에 진행 상황을 저장하거나 변경 사항을 취소하는 작업이 포함될 수 있다. 이 방법으로 종료된 파드는 잡의 completions 수에 포함되지 않는다.

일시 중지된 상태의 잡 정의 예시는 다음과 같다.

kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      ...

잡의 상태를 사용하여 잡이 일시 중지되었는지 또는 과거에 일시 중지되었는지 확인할 수 있다.

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
  conditions:
  - lastProbeTime: "2021-02-05T13:14:33Z"
    lastTransitionTime: "2021-02-05T13:14:33Z"
    status: "True"
    type: Suspended
  startTime: "2021-02-05T13:13:48Z"

"True" 상태인 "Suspended" 유형의 잡의 컨디션은 잡이 일시 중지되었음을 의미한다. 이 lastTransitionTime 필드는 잡이 일시 중지된 기간을 결정하는 데 사용할 수 있다. 해당 컨디션의 상태가 "False"이면, 잡이 이전에 일시 중지되었다가 현재 실행 중이다. 이러한 컨디션이 잡의 상태에 없으면, 잡이 중지되지 않은 것이다.

잡이 일시 중지 및 재개될 때에도 이벤트가 생성된다.

kubectl describe jobs/myjob
Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed

마지막 4개의 이벤트, 특히 "Suspended" 및 "Resumed" 이벤트는 .spec.suspend 필드를 전환한 결과이다. 이 두 이벤트 사이의 시간동안 파드가 생성되지 않았지만, 잡이 재개되자마자 파드 생성이 다시 시작되었음을 알 수 있다.

자신의 파드 셀렉터를 지정하기

일반적으로 잡 오브젝트를 생성할 때 .spec.selector 를 지정하지 않는다. 시스템의 기본적인 로직은 잡이 생성될 때 이 필드를 추가한다. 이것은 다른 잡과 겹치지 않는 셀렉터 값을 선택한다.

그러나, 일부 케이스에서는 이 자동화된 설정 셀렉터를 재정의해야 할 수도 있다. 이를 위해 잡의 .spec.selector 를 설정할 수 있다.

이 것을 할 때는 매우 주의해야 한다. 만약 해당 잡의 파드에 고유하지 않고 연관이 없는 파드와 일치하는 레이블 셀렉터를 지정하면, 연관이 없는 잡의 파드가 삭제되거나, 해당 잡이 다른 파드가 완료한 것으로 수를 세거나, 하나 또는 양쪽 잡 모두 파드 생성이나 실행 완료를 거부할 수도 있다. 만약 고유하지 않은 셀렉터가 선택된 경우, 다른 컨트롤러(예: 레플리케이션 컨트롤러)와 해당 파드는 예측할 수 없는 방식으로 작동할 수 있다. 쿠버네티스는 당신이 .spec.selector 를 지정할 때 발생하는 실수를 막을 수 없을 것이다.

다음은 이 기능을 사용하려는 경우의 예시이다.

old 가 이미 실행 중이다. 기존 파드가 계속 실행되기를 원하지만, 잡이 생성한 나머지 파드에는 다른 파드 템플릿을 사용하고 잡으로 하여금 새 이름을 부여하기를 원한다. 그러나 관련된 필드들은 업데이트가 불가능하기 때문에 잡을 업데이트할 수 없다. 따라서 kubectl delete jobs/old --cascade=orphan 명령을 사용해서 잡 old 를 삭제하지만, 파드를 실행 상태로 둔다. 삭제하기 전에 어떤 셀렉터를 사용하는지 기록한다.

kubectl get job old -o yaml

출력 결과는 다음과 같다.

kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

그런 이후에 이름이 new 인 새 잡을 생성하고, 동일한 셀렉터를 명시적으로 지정한다. 기존 파드에는 controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002 레이블이 있기에 잡 new 에 의해서도 제어된다.

시스템이 일반적으로 자동 생성하는 셀렉터를 사용하지 않도록 하기 위해 새 잡에서 manualSelector: true 를 지정해야 한다.

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

새 잡 자체는 a8f3d00d-c6d2-11e5-9f87-42010af00002 와 다른 uid 를 가지게 될 것이다. manualSelector: true 를 설정하면 시스템에게 사용자가 무엇을 하는지 알고 있음을 알리고, 이런 불일치를 허용한다.

종료자(finalizers)를 이용한 잡 추적

FEATURE STATE: Kubernetes v1.22 [alpha]

이 기능이 활성화되지 않으면, 잡 컨트롤러succeededfailed 파드의 수를 세어 잡 상태를 추적한다. 그런데, 파드는 다음과 같은 이유로 제거될 수 있다.

  • 노드가 다운되었을 때 가비지 콜렉터가 버려진(orphan) 파드를 제거
  • 가비지 콜렉터가 (Succeeded 또는 Failed 단계에 있는) 완료된 파드를 일정 임계값 이후에 제거
  • 잡에 속한 파드를 사용자가 임의로 제거
  • (쿠버네티스에 속하지 않는) 외부 컨트롤러가 파드를 제거하거나 교체

클러스터에서 JobTrackingWithFinalizers 기능을 활성화하면, 컨트롤 플레인은 잡에 속하는 파드의 상태를 추적하고 API 서버에서 파드가 제거되면 이를 알아챈다. 이를 위해, 잡 컨트롤러는 batch.kubernetes.io/job-tracking 종료자를 갖는 파드를 생성한다. 컨트롤러는 파드의 상태 변화가 잡 상태에 반영된 후에만 종료자를 제거하므로, 이후 다른 컨트롤러나 사용자가 파드를 제거할 수 있다.

잡 컨트롤러는 새로운 잡에 대해서만 새로운 알고리즘을 적용한다. 이 기능이 활성화되기 전에 생성된 잡은 영향을 받지 않는다. 잡에 batch.kubernetes.io/job-tracking 어노테이션이 있는지 확인하여, 잡 컨트롤러가 파드 종료자를 이용하여 잡을 추적하고 있는지 여부를 확인할 수 있다. 이 어노테이션을 잡에 수동으로 추가하거나 제거해서는 안 된다.

대안

베어(Bare) 파드

파드가 실행 중인 노드가 재부팅되거나 실패하면 파드가 종료되고 다시 시작되지 않는다. 그러나 잡은 종료된 항목을 대체하기 위해 새 파드를 생성한다. 따라서, 애플리케이션에 단일 파드만 필요한 경우에도 베어 파드 대신 잡을 사용하는 것을 권장한다.

레플리케이션 컨트롤러

잡은 레플리케이션 컨트롤러를 보완한다. 레플리케이션 컨트롤러는 종료하지 않을 파드(예: 웹 서버)를 관리하고, 잡은 종료될 것으로 예상되는 파드(예: 배치 작업)를 관리한다.

파드 라이프사이클에서 설명한 것처럼, 오직 OnFailure 또는 Never 와 같은 RestartPolicy 를 사용하는 파드에만 적절하다. (참고: RestartPolicy 가 설정되지 않은 경우에는 기본값은 Always 이다.)

단일 잡으로 컨트롤러 파드 시작

또 다른 패턴은 단일 잡이 파드를 생성한 후 다른 파드들을 생성해서 해당 파드들에 일종의 사용자 정의 컨트롤러 역할을 하는 것이다. 이를 통해 최대한의 유연성을 얻을 수 있지만, 시작하기에는 다소 복잡할 수 있으며 쿠버네티스와의 통합성이 낮아진다.

이 패턴의 한 예시는 파드를 시작하는 잡이다. 파드는 스크립트를 실행해서 스파크(Spark) 마스터 컨트롤러 (스파크 예시를 본다)를 시작하고, 스파크 드라이버를 실행한 다음, 정리한다.

이 접근 방식의 장점은 전체 프로세스가 잡 오브젝트의 완료를 보장하면서도, 파드 생성과 작업 할당 방법을 완전히 제어하고 유지한다는 것이다.

다음 내용

3.4.2.6 - 가비지(Garbage) 수집

쿠버네티스의 가비지 수집기는 한때 소유자가 있었지만, 더 이상 소유자가 없는 오브젝트들을 삭제하는 역할을 한다.

소유자(owner)와 종속(dependent)

일부 쿠버네티스 오브젝트는 다른 오브젝트의 소유자이다. 예를 들어 레플리카셋은 파드 집합의 소유자이다. 소유자 오브젝트에게 소유된 오브젝트를 종속 이라고 한다. 모든 종속 오브젝트는 소유하는 오브젝트를 가르키는 metadata.ownerReferences 필드를 가지고 있다.

때때로, 쿠버네티스는 ownerReference 값을 자동적으로 설정한다. 예를 들어 레플리카셋을 만들 때 쿠버네티스는 레플리카셋에 있는 각 파드의 ownerReference 필드를 자동으로 설정한다. 1.8 에서는 쿠버네티스가 레플리케이션컨트롤러, 레플리카셋, 스테이트풀셋, 데몬셋, 디플로이먼트, 잡 그리고 크론잡에 의해서 생성되거나 차용된 오브젝트의 ownerReference 값을 자동으로 설정한다.

또한 ownerReference 필드를 수동으로 설정해서 소유자와 종속 항목 간의 관계를 지정할 수도 있다.

여기에 파드 3개가 있는 레플리카셋의 구성 파일이 있다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: my-repset
spec:
  replicas: 3
  selector:
    matchLabels:
      pod-is-for: garbage-collection-example
  template:
    metadata:
      labels:
        pod-is-for: garbage-collection-example
    spec:
      containers:
      - name: nginx
        image: nginx

레플리카셋을 생성하고 파드의 메타데이터를 본다면, OwnerReferences 필드를 찾을 수 있다.

kubectl apply -f https://k8s.io/examples/controllers/replicaset.yaml
kubectl get pods --output=yaml

출력 결과는 파드의 소유자가 my-repset 이라는 이름의 레플리카셋인 것을 보여준다.

apiVersion: v1
kind: Pod
metadata:
  ...
  ownerReferences:
  - apiVersion: apps/v1
    controller: true
    blockOwnerDeletion: true
    kind: ReplicaSet
    name: my-repset
    uid: d9607e19-f88f-11e6-a518-42010a800195
  ...

가비지 수집기의 종속 항목 삭제 방식 제어

오브젝트를 삭제할 때, 오브젝트의 종속 항목을 자동으로 삭제하는지의 여부를 지정할 수 있다. 종속 항목을 자동으로 삭제하는 것을 캐스케이딩(cascading) 삭제 라고 한다. 캐스케이딩 삭제 에는 백그라운드포어그라운드 2가지 모드가 있다.

만약 종속 항목을 자동으로 삭제하지 않고 오브젝트를 삭제한다면, 종속 항목은 분리됨(orphaned) 이라고 한다.

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

포어그라운드 캐스케이딩 삭제 에서는 루트 오브젝트가 먼저 "삭제 중(deletion in progress)" 상태가 된다. "삭제 중" 상태에서는 다음 사항이 적용된다.

  • 오브젝트는 REST API를 통해 여전히 볼 수 있음
  • 오브젝트에 deletionTimestamp 가 설정됨
  • 오브젝트의 "foregroundDeletion"에 metadata.finalizers 값이 포함됨.

"삭제 중" 상태가 설정되면, 가비지 수집기는 오브젝트의 종속 항목을 삭제한다. 가비지 수집기는 모든 "차단" 종속 항목(ownerReference.blockOwnerDeletion=true 가 있는 오브젝트)의 삭제가 완료되면, 소유자 오브젝트를 삭제한다.

"foregroundDeletion" 에서는 ownerReference.blockOwnerDeletion=true 로 설정된 종속 항목만 소유자 오브젝트의 삭제를 차단한다는 것을 참고한다. 쿠버네티스 버전 1.7에서는 소유자 오브젝트에 대한 삭제 권한에 따라 blockOwnerDeletion 를 true로 설정하기 위해 사용자 접근을 제어하는 어드미션 컨트롤러가 추가되었기에 권한이 없는 종속 항목은 소유자 오브젝트의 삭제를 지연시킬 수 없다.

만약 오브젝트의 ownerReferences 필드가 컨트롤러(디플로이먼트 또는 레플리카셋과 같은)에 의해 설정된 경우 blockOwnerDeletion이 자동으로 설정되므로 이 필드를 수동으로 수정할 필요가 없다.

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

백그라운드 캐스케이딩 삭제 에서 쿠버네티스는 소유자 오브젝트를 즉시 삭제하고, 가비지 수집기는 백그라운드에서 종속 항목을 삭제한다.

캐스케이딩 삭제 정책 설정하기

캐스케이딩 삭제 정책을 제어하려면, 오브젝트를 삭제할 때 deleteOptions 인수를 propagationPolicy 필드에 설정한다. 여기에 가능한 값으로는 "Orphan", "Foreground" 또는 "Background" 이다.

여기에 백그라운드에서 종속 항목을 삭제하는 예시가 있다.

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
  -H "Content-Type: application/json"

여기에 포어그라운드에서 종속 항목을 삭제하는 예시가 있다.

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
  -H "Content-Type: application/json"

여기에 종속 항목을 분리됨으로 하는 예시가 있다.

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
  -H "Content-Type: application/json"

kubectl도 캐스케이딩 삭제를 지원한다.

kubectl을 사용해서 포어그라운드의 종속 항목을 삭제하려면 --cascade=foreground 를 설정한다. 종속 항목을 분리하기 위해서는 --cascade=orphan 를 설정한다.

기본 동작은 백그라운드의 종속 항목을 삭제하는 것이며, 이는 --cascade 를 생략하거나 명시적으로 background 를 설정한 경우의 동작에 해당한다.

여기에 레플리카셋의 종속 항목을 분리로 만드는 예시가 있다.

kubectl delete replicaset my-repset --cascade=false

디플로이먼트에 대한 추가 참고

1.7 이전에서는 디플로이먼트와 캐스케이딩 삭제를 사용하면 반드시 propagationPolicy: Foreground 를 사용해서 생성된 레플리카셋뿐만 아니라 해당 파드도 삭제해야 한다. 만약 이 propagationPolicy 유형을 사용하지 않는다면, 레플리카셋만 삭제되고 파드는 분리된 상태로 남을 것이다. 더 많은 정보는 kubeadm/#149를 본다.

알려진 이슈들

#26120을 추적한다.

다음 내용

디자인 문서 1

디자인 문서 2

3.4.2.7 - 완료된 리소스를 위한 TTL 컨트롤러

FEATURE STATE: Kubernetes v1.21 [beta]

TTL 컨트롤러는 실행이 완료된 리소스 오브젝트의 수명을 제한하는 TTL (time to live) 메커니즘을 제공한다. TTL 컨트롤러는 현재 잡(Job)만 처리하며, 파드와 커스텀 리소스와 같이 실행을 완료할 다른 리소스를 처리하도록 확장될 수 있다.

이 기능은 현재 베타이고 기본적으로 활성화되어 있다. kube-apiserver와 kube-controller-manager에서 TTLAfterFinished 기능 게이트를 이용하여 비활성화할 수 있다.

TTL 컨트롤러

현재의 TTL 컨트롤러는 잡만 지원한다. 클러스터 운영자는 예시 와 같이 .spec.ttlSecondsAfterFinished 필드를 명시하여 완료된 잡(완료 또는 실패)을 자동으로 정리하기 위해 이 기능을 사용할 수 있다. 리소스의 작업이 완료된 TTL 초(sec) 후 (다른 말로는, TTL이 만료되었을 때), TTL 컨트롤러는 해당 리소스가 정리될 수 있다고 가정한다. TTL 컨트롤러가 리소스를 정리할때 리소스를 연속적으로 삭제한다. 이는 의존하는 오브젝트도 해당 리소스와 함께 삭제되는 것을 의미한다. 리소스가 삭제되면 완료자(finalizers)와 같은 라이프 사이클 보증이 적용 된다.

TTL 초(sec)는 언제든지 설정이 가능하다. 여기에 잡 필드 중 .spec.ttlSecondsAfterFinished 를 설정하는 몇 가지 예시가 있다.

  • 작업이 완료된 다음, 일정 시간 후에 자동으로 잡이 정리될 수 있도록 리소스 메니페스트에 이 필드를 지정한다.
  • 이미 완료된 기존 리소스에 이 새 기능을 적용하기 위해서 이 필드를 설정한다.
  • 어드미션 웹후크 변형 을 사용해서 리소스 생성시 이 필드를 동적으로 설정 한다. 클러스터 관리자는 이것을 사용해서 완료된 리소스에 대해 TTL 정책을 적용할 수 있다.
  • 리소스가 완료된 이후에 어드미션 웹후크 변형 을 사용해서 이 필드를 동적으로 설정하고, 리소스의 상태, 레이블 등에 따라 다른 TTL 값을 선택한다.

경고

TTL 초(sec) 업데이트

TTL 기간은, 예를 들어 잡의 .spec.ttlSecondsAfterFinished 필드는 리소스를 생성하거나 완료한 후에 수정할 수 있다. 그러나, 잡을 삭제할 수 있게 되면(TTL이 만료된 경우) 시스템은 TTL을 연장하기 위한 업데이트가 성공적인 API 응답을 리턴하더라도 작업이 유지되도록 보장하지 않는다.

시간 차이(Skew)

TTL 컨트롤러는 쿠버네티스 리소스에 저장된 타임스탬프를 사용해서 TTL의 만료 여부를 결정하기 때문에, 이 기능은 클러스터 간의 시간 차이에 민감하며, 시간 차이에 의해서 TTL 컨트롤러가 잘못된 시간에 리소스 오브젝트를 정리하게 될 수 있다.

쿠버네티스에서는 시간 차이를 피하기 위해 모든 노드 (#6159를 본다) 에서 NTP를 실행해야 한다. 시계가 항상 정확한 것은 아니지만, 그 차이는 아주 작아야 한다. 0이 아닌 TTL을 설정할때는 이 위험에 대해 유의해야 한다.

다음 내용

3.4.2.8 - 크론잡

FEATURE STATE: Kubernetes v1.21 [stable]

크론잡은 반복 일정에 따라 을 만든다.

하나의 크론잡 오브젝트는 크론탭 (크론 테이블) 파일의 한 줄과 같다. 크론잡은 잡을 크론 형식으로 쓰여진 주어진 일정에 따라 주기적으로 동작시킨다.

추가로, 크론잡 스케줄은 타임존(timezone) 처리를 지원해서, 크론잡 스케줄 시작 부분에 "CRON_TZ=

크론잡 리소스에 대한 매니페스트를 생성할 때에는 제공하는 이름이 유효한 DNS 서브도메인 이름이어야 한다. 이름은 52자 이하여야 한다. 이는 크론잡 컨트롤러는 제공된 잡 이름에 11자를 자동으로 추가하고, 작업 이름의 최대 길이는 63자라는 제약 조건이 있기 때문이다.

크론잡

크론잡은 백업, 리포트 생성 등의 정기적 작업을 수행하기 위해 사용된다. 각 작업은 무기한 반복되도록 구성해야 한다(예: 1일/1주/1달마다 1회). 작업을 시작해야 하는 해당 간격 내 특정 시점을 정의할 수 있다.

예시

이 크론잡 매니페스트 예제는 현재 시간과 hello 메시지를 1분마다 출력한다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

(크론잡으로 자동화된 작업 실행하기는 이 예시를 더 자세히 설명한다.)

크론 스케줄 문법

#      ┌────────────────── 타임존 (옵션)
#      |      ┌───────────── 분 (0 - 59)
#      |      │ ┌───────────── 시 (0 - 23)
#      |      │ │ ┌───────────── 일 (1 - 31)
#      |      │ │ │ ┌───────────── 월 (1 - 12)
#      |      │ │ │ │ ┌───────────── 요일 (0 - 6) (일요일부터 토요일까지;
#      |      │ │ │ │ │                           특정 시스템에서는 7도 일요일)
#      |      │ │ │ │ │
#      |      │ │ │ │ │
# CRON_TZ=UTC * * * * *
항목 설명 상응 표현
@yearly (or @annually) 매년 1월 1일 자정에 실행 0 0 1 1 *
@monthly 매월 1일 자정에 실행 0 0 1 * *
@weekly 매주 일요일 자정에 실행 0 0 * * 0
@daily (or @midnight) 매일 자정에 실행 0 0 * * *
@hourly 매시 0분에 시작 0 * * * *

예를 들면, 다음은 해당 작업이 매주 금요일 자정에 시작되어야 하고, 매월 13일 자정(UTC 기준)에도 시작되어야 한다는 뜻이다.

CRON_TZ=UTC 0 0 13 * 5

크론잡 스케줄 표현을 생성하기 위해서 crontab.guru와 같은 웹 도구를 사용할 수도 있다.

크론잡의 한계

크론잡은 일정의 실행시간 마다 한 번의 잡 오브젝트를 생성한다. "약" 이라고 하는 이유는 특정 환경에서는 두 개의 잡이 만들어지거나, 잡이 생성되지 않기도 하기 때문이다. 보통 이렇게 하지 않도록 해야겠지만, 완벽히 그럴 수는 없다. 따라서 잡은 멱등원 이 된다.

만약 startingDeadlineSeconds 가 큰 값으로 설정되거나, 설정되지 않고(디폴트 값), concurrencyPolicyAllow 로 설정될 경우, 잡은 항상 적어도 한 번은 실행될 것이다.

모든 크론잡에 대해 크론잡 컨트롤러 는 마지막 일정부터 지금까지 얼마나 많은 일정이 누락되었는지 확인한다. 만약 100회 이상의 일정이 누락되었다면, 잡을 실행하지 않고 아래와 같은 에러 로그를 남긴다.

Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.

중요한 것은 만약 startingDeadlineSeconds 필드가 설정이 되면(nil 이 아닌 값으로), 컨트롤러는 마지막 일정부터 지금까지 대신 startingDeadlineSeconds 값에서 몇 개의 잡이 누락되었는지 카운팅한다. 예를 들면, startingDeadlineSeconds200 이면, 컨트롤러는 최근 200초 내 몇 개의 잡이 누락되었는지 카운팅한다.

크론잡은 정해진 일정에 잡 실행을 실패하면 놓쳤다고 카운팅된다. 예를 들면, concurrencyPolicyForbid 로 설정되었고, 크론잡이 이전 일정이 스케줄되어 여전히 시도하고 있을 때, 그 때 누락되었다고 판단한다.

즉, 크론잡이 08:30:00 에 시작하여 매 분마다 새로운 잡을 실행하도록 설정이 되었고, startingDeadlineSeconds 값이 설정되어 있지 않는다고 가정해보자. 만약 크론잡 컨트롤러가 08:29:00 부터 10:21:00 까지 고장이 나면, 일정을 놓친 작업 수가 100개를 초과하여 잡이 실행되지 않을 것이다.

이 개념을 더 자세히 설명하자면, 크론잡이 08:30:00 부터 매 분 실행되는 일정으로 설정되고, startingDeadlineSeconds 이 200이라고 가정한다. 크론잡 컨트롤러가 전의 예시와 같이 고장났다고 하면 (08:29:00 부터 10:21:00 까지), 잡은 10:22:00 부터 시작될 것이다. 이 경우, 컨트롤러가 마지막 일정부터 지금까지가 아니라, 최근 200초 안에 얼마나 놓쳤는지 체크하기 때문이다. (여기서는 3번 놓쳤다고 체크함)

크론잡은 오직 그 일정에 맞는 잡 생성에 책임이 있고, 잡은 그 잡이 대표하는 파드 관리에 책임이 있다.

컨트롤러 버전

쿠버네티스 v1.21부터 크론잡 컨트롤러의 두 번째 버전이 기본 구현이다. 기본 크론잡 컨트롤러를 비활성화하고 대신 원래 크론잡 컨트롤러를 사용하려면, CronJobControllerV2 기능 게이트 플래그를 kube-controller-manager에 전달하고, 이 플래그를 false 로 설정한다. 예를 들면, 다음과 같다.

--feature-gates="CronJobControllerV2=false"

다음 내용

  • 크론잡이 의존하고 있는 파드 두 개념에 대해 배운다.
  • 크론잡 .spec.schedule 필드의 형식에 대해서 읽는다.
  • 크론잡을 생성하고 다루기 위한 지침 및 크론잡 매니페스트의 예제로 크론잡으로 자동화된 작업 실행를 읽는다.
  • CronJob은 쿠버네티스 REST API의 일부이다. 오브젝트 정의를 읽고 쿠버네티스 크론잡 API에 대해 이해한다.

3.4.2.9 - 레플리케이션 컨트롤러

레플리케이션컨트롤러 는 언제든지 지정된 수의 파드 레플리카가 실행 중임을 보장한다. 다시 말하면, 레플리케이션 컨트롤러는 파드 또는 동일 종류의 파드의 셋이 항상 기동되고 사용 가능한지 확인한다.

레플리케이션 컨트롤러의 동작방식

파드가 너무 많으면 레플리케이션 컨트롤러가 추가적인 파드를 제거한다. 너무 적으면 레플리케이션 컨트롤러는 더 많은 파드를 시작한다. 수동으로 생성된 파드와 달리 레플리케이션 컨트롤러가 유지 관리하는 파드는 실패하거나 삭제되거나 종료되는 경우 자동으로 교체된다. 예를 들어, 커널 업그레이드와 같이 파괴적인 유지 보수 작업을 하고 난 이후의 노드에서 파드가 다시 생성된다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용해야 한다. 레플리케이션 컨트롤러는 프로세스 감시자(supervisor)와 유사하지만 단일 노드에서 개별 프로세스를 감시하는 대신 레플리케이션 컨트롤러는 여러 노드에서 여러 파드를 감시한다.

레플리케이션 컨트롤러는 디스커션에서 종종 "rc"로 축약되며 kubectl 명령에서 숏컷으로 사용된다.

간단한 경우는 하나의 레플리케이션 컨트롤러 오브젝트를 생성하여 한 개의 파드 인스턴스를 영구히 안정적으로 실행하는 것이다. 보다 복잡한 사용 사례는 웹 서버와 같이 복제된 서비스의 동일한 레플리카를 여러 개 실행하는 것이다.

레플리케이션 컨트롤러 예제 실행

레플리케이션 컨트롤러 예제의 config는 nginx 웹서버의 복사본 세 개를 실행한다.

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

예제 파일을 다운로드한 후 다음 명령을 실행하여 예제 작업을 실행하라.

kubectl apply -f https://k8s.io/examples/controllers/replication.yaml

출력 결과는 다음과 같다.

replicationcontroller/nginx created

다음 명령을 사용하여 레플리케이션 컨트롤러의 상태를 확인하자.

kubectl describe replicationcontrollers/nginx

출력 결과는 다음과 같다.

Name:        nginx
Namespace:   default
Selector:    app=nginx
Labels:      app=nginx
Annotations:    <none>
Replicas:    3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       app=nginx
  Containers:
   nginx:
    Image:              nginx
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen       LastSeen     Count    From                        SubobjectPath    Type      Reason              Message
  ---------       --------     -----    ----                        -------------    ----      ------              -------
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-qrm3m
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-3ntk0
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-4ok8v

이제 세 개의 파드가 생성되었으나 아직 이미지가 풀(pull)되지 않아서 어떤 파드도 시작되지 않았다. 조금 지난 후에 같은 명령이 다음과 같이 보일 것이다.

Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

레플리케이션 컨트롤러에 속한 모든 파드를 머신이 읽을 수 있는 형식으로 나열하기 위해 다음과 같은 명령을 사용할 수 있다.

pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods

출력 결과는 다음과 같다.

nginx-3ntk0 nginx-4ok8v nginx-qrm3m

여기서 셀렉터는 레플리케이션컨트롤러(kubectl describe 의 출력에서 보인)의 셀렉터와 같고, 다른 형식의 파일인 replication.yaml 의 것과 동일하다. --output=jsonpath 은 반환된 목록의 각 파드의 이름을 출력하도록 하는 옵션이다.

레플리케이션 컨트롤러의 Spec 작성

다른 모든 쿠버네티스 컨피그와 마찬가지로 레플리케이션 컨트롤러는 apiVersion, kind, metadata 와 같은 필드가 필요하다. 레플리케이션 컨트롤러 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다. 환경설정 파일의 동작에 관련된 일반적인 정보는 쿠버네티스 오브젝트 관리를 참고한다.

레플리케이션 컨트롤러는 또한 .spec section도 필요하다.

파드 템플릿

.spec.template 는 오직 .spec 필드에서 요구되는 것이다.

.spec.template파드 템플릿이다. 정확하게 파드 스키마와 동일하나, 중첩되어 있고 apiVersion 혹은 kind를 갖지 않는다.

파드에 필요한 필드 외에도 레플리케이션 컨트롤러의 파드 템플릿은 적절한 레이블과 적절한 재시작 정책을 지정해야 한다. 레이블의 경우 다른 컨트롤러와 중첩되지 않도록 하라. 파드 셀렉터를 참조하라.

오직 Always 와 동일한 .spec.template.spec.restartPolicy만 허용되며, 특별히 지정되지 않으면 기본값이다.

로컬 컨테이너의 재시작의 경우, 레플리케이션 컨트롤러는 노드의 에이전트에게 위임한다. 예를 들어 Kubelet 혹은 도커이다.

레플리케이션 컨트롤러에서 레이블

레플리케이션 컨트롤러 자체는 레이블 (.metadata.labels) 을 가질 수 있다. 일반적으로 이것을 .spec.template.metadata.labels 와 동일하게 설정할 것이다. .metadata.labels 가 지정되어 있지 않은 경우, 기본은 .spec.template.metadata.labels 이다. 하지만 레이블은 다른 것이 허용되며, .metadata.labels 라벨은 레플리케이션 컨트롤러의 동작에 영향을 미치지 않는다.

파드 셀렉터

.spec.selector 필드는 레이블 셀렉터이다. 레플리케이션 컨트롤러는 셀렉터와 일치하는 레이블이 있는 모든 파드를 관리한다. 직접 생성하거나 삭제된 파드와 다른 사람이나 프로세스가 생성하거나 삭제한 파드를 구분하지 않는다. 이렇게 하면 실행중인 파드에 영향을 주지 않고 레플리케이션 컨트롤러를 교체할 수 있다.

지정된 경우 .spec.template.metadata.labels.spec.selector 와 동일해야 하며 그렇지 않으면 API에 의해 거부된다. .spec.selector 가 지정되지 않은 경우 기본값은 .spec.template.metadata.labels 이다.

또한 일반적으로 이 셀렉터와 레이블이 일치하는 파드를 직접 다른 레플리케이션 컨트롤러 또는 잡과 같은 다른 컨트롤러로 작성해서는 안된다. 그렇게 하면 레플리케이션 컨트롤러는 다른 파드를 생성했다고 생각한다. 쿠버네티스는 이런 작업을 중단해 주지 않는다.

중첩된 셀렉터들을 갖는 다수의 컨트롤러들을 종료하게 되면, 삭제된 것들은 스스로 관리를 해야 한다 (아래를 참조).

다수의 레플리카

.spec.replicas 를 동시에 실행하고 싶은 파드의 수로 설정함으로써 실행할 파드의 수를 지정할 수 있다. 레플리카가 증가 또는 감소한 경우 또는 파드가 정상적으로 종료되고 교체가 일찍 시작되는 경우라면 언제든지 실행중인 수가 더 높거나 낮을 수 있다.

.spec.replicas 를 지정하지 않으면 기본값은 1이다.

레플리케이션 컨트롤러 사용하기

레플리케이션 컨트롤러와 레플리케이션 컨트롤러의 파드 삭제

레플리케이션 컨트롤러와 레플리케이션의 모든 파드를 삭제하려면 kubectl delete 를 사용하라. Kubectl은 레플리케이션 컨트롤러를 0으로 스케일하고 레플리케이션 컨트롤러 자체를 삭제하기 전에 각 파드를 삭제하기를 기다린다. 이 kubectl 명령이 인터럽트되면 다시 시작할 수 있다.

REST API나 Go 클라이언트 라이브러리를 사용하는 경우 명시적으로 단계를 수행해야 한다(레플리카를 0으로 스케일하고 파드의 삭제를 기다린 이후, 레플리케이션 컨트롤러를 삭제).

레플리케이션 컨트롤러만 삭제

해당 파드에 영향을 주지 않고 레플리케이션 컨트롤러를 삭제할 수 있다.

kubectl을 사용하여, kubectl delete에 옵션으로 --cascade=orphan을 지정하라.

REST API나 Go 클라이언트 라이브러리를 사용하는 경우 레플리케이션 컨트롤러 오브젝트를 삭제하라.

원본이 삭제되면 대체할 새로운 레플리케이션 컨트롤러를 생성하여 교체할 수 있다. 오래된 파드와 새로운 파드의 .spec.selector 가 동일하다면, 새로운 레플리케이션 컨트롤러는 오래된 파드를 채택할 것이다. 그러나 기존 파드를 새로운 파드 템플릿과 일치시키려는 노력은 하지 않을 것이다. 새로운 spec에 대한 파드를 제어된 방법으로 업데이트하려면 롤링 업데이트를 사용하라.

레플리케이션 컨트롤러에서 파드 격리

파드는 레이블을 변경하여 레플리케이션 컨트롤러의 대상 셋에서 제거될 수 있다. 이 기술은 디버깅과 데이터 복구를 위해 서비스에서 파드를 제거하는 데 사용될 수 있다. 이 방법으로 제거된 파드는 자동으로 교체된다 (레플리카 수가 변경되지 않는다고 가정).

일반적인 사용법 패턴

다시 스케줄하기

위에서 언급했듯이, 실행하려는 파드가 한 개 혹은 1000개이든 관계없이 레플리케이션 컨트롤러는 노드 실패 또는 파드 종료시 지정된 수의 파드가 존재하도록 보장한다 (예 : 다른 제어 에이전트에 의한 동작으로 인해).

스케일링

레플리케이션컨트롤러는 replicas 필드를 업데이트하여, 수동으로 또는 오토 스케일링 제어 에이전트를 통해, 레플리카의 수를 늘리거나 줄일 수 있다.

롤링 업데이트

레플리케이션 컨트롤러는 파드를 하나씩 교체함으로써 서비스에 대한 롤링 업데이트를 쉽게 하도록 설계되었다.

#1353에서 설명한 것처럼, 권장되는 접근법은 1 개의 레플리카를 가진 새로운 레플리케이션 컨트롤러를 생성하고 새로운 (+1) 컨트롤러 및 이전 (-1) 컨트롤러를 차례대로 스케일한 후 0개의 레플리카가 되면 이전 컨트롤러를 삭제하는 것이다. 예상치 못한 오류와 상관없이 파드 세트를 예측 가능하게 업데이트한다.

이상적으로 롤링 업데이트 컨트롤러는 애플리케이션 준비 상태를 고려하며 주어진 시간에 충분한 수의 파드가 생산적으로 제공되도록 보장할 것이다.

두 레플리케이션 컨트롤러는 일반적으로 롤링 업데이트를 동기화 하는 이미지 업데이트이기 때문에 파드의 기본 컨테이너 이미지 태그와 같이 적어도 하나의 차별화된 레이블로 파드를 생성해야 한다.

다수의 릴리스 트랙

롤링 업데이트가 진행되는 동안 다수의 애플리케이션 릴리스를 실행하는 것 외에도 다수의 릴리스 트랙을 사용하여 장기간에 걸쳐 또는 연속적으로 실행하는 것이 일반적이다. 트랙은 레이블 별로 구분된다.

예를 들어, 서비스는 tier in (frontend), environment in (prod) 이 있는 모든 파드를 대상으로 할 수 있다. 이제 이 계층을 구성하는 10 개의 복제된 파드가 있다고 가정해 보자. 하지만 이 구성 요소의 새로운 버전을 '카나리' 하기를 원한다. 대량의 레플리카에 대해 replicas 를 9로 설정하고 tier=frontend, environment=prod, track=stable 레이블을 설정한 레플리케이션 컨트롤러와, 카나리에 replicas 가 1로 설정된 다른 레플리케이션 컨트롤러에 tier=frontend, environment=prod, track=canary 라는 레이블을 설정할 수 있다. 이제 이 서비스는 카나리와 카나리 이외의 파드 모두를 포함한다. 그러나 레플리케이션 컨트롤러를 별도로 조작하여 테스트하고 결과를 모니터링하는 등의 작업이 혼란스러울 수 있다.

서비스와 레플리케이션컨트롤러 사용

하나의 서비스 뒤에 여러 개의 레플리케이션컨트롤러가 있을 수 있다. 예를 들어 일부 트래픽은 이전 버전으로 이동하고 일부는 새 버전으로 이동한다.

레플리케이션컨트롤러는 자체적으로 종료되지 않지만, 서비스만큼 오래 지속될 것으로 기대되지는 않는다. 서비스는 여러 레플리케이션컨트롤러에 의해 제어되는 파드로 구성될 수 있으며, 서비스 라이프사이클 동안(예를 들어, 서비스를 실행하는 파드 업데이트 수행을 위해) 많은 레플리케이션컨트롤러가 생성 및 제거될 것으로 예상된다. 서비스 자체와 클라이언트 모두 파드를 유지하는 레플리케이션컨트롤러를 의식하지 않는 상태로 남아 있어야 한다.

레플리케이션을 위한 프로그램 작성

레플리케이션 컨트롤러에 의해 생성된 파드는 해당 구성이 시간이 지남에 따라 이질적이 될 수 있지만 균일하고 의미상 동일하도록 설계되었다. 이는 레플리카된 상태 스테이트리스 서버에 적합하지만 레플리케이션 컨트롤러를 사용하여 마스터 선출, 샤드 및 워크-풀 애플리케이션의 가용성을 유지할 수도 있다. RabbitMQ work queues와 같은 애플리케이션은 안티패턴으로 간주되는 각 파드의 구성에 대한 정적/일회성 사용자 정의와 반대로 동적 작업 할당 메커니즘을 사용해야 한다. 리소스의 수직 자동 크기 조정 (예 : CPU 또는 메모리)과 같은 수행된 모든 파드 사용자 정의는 레플리케이션 컨트롤러 자체와 달리 다른 온라인 컨트롤러 프로세스에 의해 수행되어야 한다.

레플리케이션 컨트롤러의 책임

레플리케이션 컨트롤러는 의도한 수의 파드가 해당 레이블 셀렉터와 일치하고 동작하는지를 확인한다. 현재, 종료된 파드만 해당 파드의 수에서 제외된다. 향후 시스템에서 사용할 수 있는 readiness 및 기타 정보가 고려될 수 있으며 교체 정책에 대한 통제를 더 추가 할 수 있고 외부 클라이언트가 임의로 정교한 교체 또는 스케일 다운 정책을 구현하기 위해 사용할 수 있는 이벤트를 내보낼 계획이다.

레플리케이션 컨트롤러는 이 좁은 책임에 영원히 제약을 받는다. 그 자체로는 준비성 또는 활성 프로브를 실행하지 않을 것이다. 오토 스케일링을 수행하는 대신, 외부 오토 스케일러 (#492에서 논의된)가 레플리케이션 컨트롤러의 replicas 필드를 변경함으로써 제어되도록 의도되었다. 레플리케이션 컨트롤러에 스케줄링 정책 (예를 들어 spreading)을 추가하지 않을 것이다. 오토사이징 및 기타 자동화 된 프로세스를 방해할 수 있으므로 제어된 파드가 현재 지정된 템플릿과 일치하는지 확인해야 한다. 마찬가지로 기한 완료, 순서 종속성, 구성 확장 및 기타 기능은 다른 곳에 속한다. 대량의 파드 생성 메커니즘 (#170)까지도 고려해야 한다.

레플리케이션 컨트롤러는 조합 가능한 빌딩-블록 프리미티브가 되도록 고안되었다. 향후 사용자의 편의를 위해 더 상위 수준의 API 및/또는 도구와 그리고 다른 보완적인 기본 요소가 그 위에 구축 될 것으로 기대한다. 현재 kubectl이 지원하는 "매크로" 작업 (실행, 스케일)은 개념 증명의 예시이다. 예를 들어 Asgard와 같이 레플리케이션 컨트롤러, 오토 스케일러, 서비스, 정책 스케줄링, 카나리 등을 관리할 수 있다.

API 오브젝트

레플리케이션 컨트롤러는 쿠버네티스 REST API의 최상위 수준의 리소스이다. API 오브젝트에 대한 더 자세한 것은 ReplicationController API object 에서 찾을 수 있다.

레플리케이션 컨트롤러의 대안

레플리카셋

ReplicaSet은 새로운 집합성 기준 레이블 셀렉터이다. 이것은 주로 디플로이먼트에 의해 파드의 생성, 삭제 및 업데이트를 오케스트레이션 하는 메커니즘으로 사용된다. 사용자 지정 업데이트 조정이 필요하거나 업데이트가 필요하지 않은 경우가 아니면 레플리카셋을 직접 사용하는 대신 디플로이먼트를 사용하는 것이 좋다.

디플로이먼트 (권장됨)

Deployment는 기본 레플리카셋과 그 파드를 업데이트하는 상위 수준의 API 오브젝트이다. 선언적이며, 서버 사이드이고, 추가 기능이 있기 때문에 롤링 업데이트 기능을 원한다면 디플로이먼트를 권장한다.

베어 파드

사용자가 직접 파드를 만든 경우와 달리 레플리케이션 컨트롤러는 노드 오류 또는 커널 업그레이드와 같은 장애가 발생하는 노드 유지 관리의 경우와 같이 어떤 이유로든 삭제되거나 종료된 파드를 대체한다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용하는 것이 좋다. 프로세스 관리자와 비슷하게 생각하면, 단지 단일 노드의 개별 프로세스가 아닌 여러 노드에서 여러 파드를 감독하는 것이다. 레플리케이션 컨트롤러는 로컬 컨테이너가 노드의 에이전트로 (예를 들어 Kubelet 또는 도커 ) 재시작하도록 위임한다.

자체적으로 제거될 것으로 예상되는 파드 (즉, 배치 잡)의 경우 레플리케이션 컨트롤러 대신 Job을 사용하라.

데몬셋

머신 모니터링이나 머신 로깅과 같은 머신 레벨 기능을 제공하는 파드에는 레플리케이션 컨트롤러 대신 DaemonSet을 사용하라. 이런 파드들의 수명은 머신의 수명에 달려 있다. 다른 파드가 시작되기 전에 파드가 머신에서 실행되어야 하며, 머신이 재부팅/종료 준비가 되어 있을 때 안전하게 종료된다.

다음 내용

  • 파드에 대해 배운다.
  • 레플리케이션 컨트롤러를 대신하는 디플로이먼트에 대해 배운다.
  • ReplicationController는 쿠버네티스 REST API의 일부이다. 레플리케이션 컨트롤러 API에 대해 이해하기 위해 오브젝트 정의를 읽는다.

3.5 - 서비스, 로드밸런싱, 네트워킹

쿠버네티스의 네트워킹에 대한 개념과 리소스에 대해 설명한다.

쿠버네티스 네트워킹은 다음의 네 가지 문제를 해결한다.

  • 파드 내의 컨테이너는 루프백(loopback)을 통한 네트워킹을 사용하여 통신한다.
  • 클러스터 네트워킹은 서로 다른 파드 간의 통신을 제공한다.
  • 서비스 리소스를 사용하면 파드에서 실행 중인 애플리케이션을 클러스터 외부에서 접근할 수 있다.
  • 또한 서비스를 사용하여 클러스터 내부에서 사용할 수 있는 서비스만 게시할 수 있다.

3.5.1 - 서비스

파드 집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법

쿠버네티스를 사용하면 익숙하지 않은 서비스 디스커버리 메커니즘을 사용하기 위해 애플리케이션을 수정할 필요가 없다. 쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.

동기

쿠버네티스 파드는 클러스터 상태와 일치하도록 생성되고 삭제된다. 파드는 비영구적 리소스이다. 만약 앱을 실행하기 위해 디플로이먼트를 사용한다면, 동적으로 파드를 생성하고 제거할 수 있다.

각 파드는 고유한 IP 주소를 갖지만, 디플로이먼트에서는 한 시점에 실행되는 파드 집합이 잠시 후 실행되는 해당 파드 집합과 다를 수 있다.

이는 다음과 같은 문제를 야기한다. ("백엔드"라 불리는) 일부 파드 집합이 클러스터의 ("프론트엔드"라 불리는) 다른 파드에 기능을 제공하는 경우, 프론트엔드가 워크로드의 백엔드를 사용하기 위해, 프론트엔드가 어떻게 연결할 IP 주소를 찾아서 추적할 수 있는가?

서비스 로 들어가보자.

서비스 리소스

쿠버네티스에서 서비스는 파드의 논리적 집합과 그것들에 접근할 수 있는 정책을 정의하는 추상적 개념이다. (때로는 이 패턴을 마이크로-서비스라고 한다.) 서비스가 대상으로 하는 파드 집합은 일반적으로 셀렉터가 결정한다. 서비스 엔드포인트를 정의하는 다른 방법에 대한 자세한 내용은 셀렉터가 없는 서비스를 참고한다.

예를 들어, 3개의 레플리카로 실행되는 스테이트리스 이미지-처리 백엔드를 생각해보자. 이러한 레플리카는 대체 가능하다. 즉, 프론트엔드는 그것들이 사용하는 백엔드를 신경쓰지 않는다. 백엔드 세트를 구성하는 실제 파드는 변경될 수 있지만, 프론트엔드 클라이언트는 이를 인식할 필요가 없으며, 백엔드 세트 자체를 추적해야 할 필요도 없다.

서비스 추상화는 이러한 디커플링을 가능하게 한다.

클라우드-네이티브 서비스 디스커버리

애플리케이션에서 서비스 디스커버리를 위해 쿠버네티스 API를 사용할 수 있는 경우, 서비스 내 파드 세트가 변경될 때마다 업데이트되는 엔드포인트를 API 서버에 질의할 수 ​​있다.

네이티브 애플리케이션이 아닌 (non-native applications) 경우, 쿠버네티스는 애플리케이션과 백엔드 파드 사이에 네트워크 포트 또는 로드 밸런서를 배치할 수 있는 방법을 제공한다.

서비스 정의

쿠버네티스의 서비스는 파드와 비슷한 REST 오브젝트이다. 모든 REST 오브젝트와 마찬가지로, 서비스 정의를 API 서버에 POST하여 새 인스턴스를 생성할 수 있다. 서비스 오브젝트의 이름은 유효한 RFC 1035 레이블 이름이어야 한다.

예를 들어, 각각 TCP 포트 9376에서 수신하고 app=MyApp 레이블을 가지고 있는 파드 세트가 있다고 가정해 보자.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

이 명세는 "my-service"라는 새로운 서비스 오브젝트를 생성하고, app=MyApp 레이블을 가진 파드의 TCP 9376 포트를 대상으로 한다.

쿠버네티스는 이 서비스에 서비스 프록시가 사용하는 IP 주소 ("cluster IP"라고도 함) 를 할당한다. (이하 가상 IP와 서비스 프록시 참고)

서비스 셀렉터의 컨트롤러는 셀렉터와 일치하는 파드를 지속적으로 검색하고, "my-service"라는 엔드포인트 오브젝트에 대한 모든 업데이트를 POST한다.

파드의 포트 정의에는 이름이 있고, 서비스의 targetPort 속성에서 이 이름을 참조할 수 있다. 이것은 다른 포트 번호를 통한 가용한 동일 네트워크 프로토콜이 있고, 단일 구성 이름을 사용하는 서비스 내에 혼합된 파드가 존재해도 가능하다. 이를 통해 서비스를 배포하고 진전시키는데 많은 유연성을 제공한다. 예를 들어, 클라이언트를 망가뜨리지 않고, 백엔드 소프트웨어의 다음 버전에서 파드가 노출시키는 포트 번호를 변경할 수 있다.

서비스의 기본 프로토콜은 TCP이다. 다른 지원되는 프로토콜을 사용할 수도 있다.

많은 서비스가 하나 이상의 포트를 노출해야 하기 때문에, 쿠버네티스는 서비스 오브젝트에서 다중 포트 정의를 지원한다. 각 포트는 동일한 프로토콜 또는 다른 프로토콜로 정의될 수 있다.

셀렉터가 없는 서비스

서비스는 일반적으로 쿠버네티스 파드에 대한 접근을 추상화하지만, 다른 종류의 백엔드를 추상화할 수도 있다. 예를 들면

  • 프로덕션 환경에서는 외부 데이터베이스 클러스터를 사용하려고 하지만, 테스트 환경에서는 자체 데이터베이스를 사용한다.
  • 한 서비스에서 다른 네임스페이스 또는 다른 클러스터의 서비스를 지정하려고 한다.
  • 워크로드를 쿠버네티스로 마이그레이션하고 있다. 해당 방식을 평가하는 동안, 쿠버네티스에서는 백엔드의 일부만 실행한다.

이러한 시나리오 중에서 파드 셀렉터 없이 서비스를 정의 할 수 있다. 예를 들면

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

이 서비스에는 셀렉터가 없으므로, 해당 엔드포인트 오브젝트가 자동으로 생성되지 않는다. 엔드포인트 오브젝트를 수동으로 추가하여, 서비스를 실행 중인 네트워크 주소 및 포트에 서비스를 수동으로 매핑할 수 있다.

apiVersion: v1
kind: Endpoints
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 192.0.2.42
    ports:
      - port: 9376

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

셀렉터가 없는 서비스에 접근하면 셀렉터가 있는 것처럼 동일하게 작동한다. 위의 예에서, 트래픽은 YAML에 정의된 단일 엔드 포인트로 라우팅된다. 192.0.2.42:9376 (TCP)

ExternalName 서비스는 셀렉터가 없고 DNS명을 대신 사용하는 특수한 상황의 서비스이다. 자세한 내용은 이 문서 뒷부분의 ExternalName 섹션을 참조한다.

초과 용량 엔드포인트

엔드포인트 리소스에 1,000개가 넘는 엔드포인트가 있는 경우 쿠버네티스 v1.22(또는 그 이상) 클러스터는 해당 엔드포인트에 endpoints.kubernetes.io/over-capacity: truncated 어노테이션을 추가한다. 이 어노테이션은 영향을 받는 엔드포인트 오브젝트가 용량을 초과했으며 엔드포인트 컨트롤러가 엔드포인트의 수를 1000으로 줄였음을 나타낸다.

엔드포인트슬라이스

FEATURE STATE: Kubernetes v1.21 [stable]

엔드포인트슬라이스는 엔드포인트에 보다 확장 가능한 대안을 제공할 수 있는 API 리소스이다. 개념적으로 엔드포인트와 매우 유사하지만, 엔드포인트슬라이스를 사용하면 여러 리소스에 네트워크 엔드포인트를 분산시킬 수 있다. 기본적으로, 엔드포인트슬라이스는 100개의 엔드포인트에 도달하면 "가득찬 것"로 간주되며, 추가 엔드포인트를 저장하기 위해서는 추가 엔드포인트슬라이스가 생성된다.

엔드포인트슬라이스는 엔드포인트슬라이스에서 자세하게 설명된 추가적인 속성 및 기능을 제공한다.

애플리케이션 프로토콜

FEATURE STATE: Kubernetes v1.20 [stable]

appProtocol 필드는 각 서비스 포트에 대한 애플리케이션 프로토콜을 지정하는 방법을 제공한다. 이 필드의 값은 해당 엔드포인트와 엔드포인트슬라이스 오브젝트에 의해 미러링된다.

이 필드는 표준 쿠버네티스 레이블 구문을 따른다. 값은 IANA 표준 서비스 이름 또는 mycompany.com/my-custom-protocol과 같은 도메인 접두사 이름 중 하나여야 한다.

가상 IP와 서비스 프록시

쿠버네티스 클러스터의 모든 노드는 kube-proxy를 실행한다. kube-proxyExternalName 이외의 유형의 서비스에 대한 가상 IP 형식을 구현한다.

라운드-로빈 DNS를 사용하지 않는 이유

항상 발생하는 질문은 왜 쿠버네티스가 인바운드 트래픽을 백엔드로 전달하기 위해 프록시에 의존하는가 하는 점이다. 다른 접근법이 있는가? 예를 들어, 여러 A 값 (또는 IPv6의 경우 AAAA)을 가진 DNS 레코드를 구성하고, 라운드-로빈 이름 확인 방식을 취할 수 있는가?

서비스에 프록시를 사용하는 데는 몇 가지 이유가 있다.

  • 레코드 TTL을 고려하지 않고, 만료된 이름 검색 결과를 캐싱하는 DNS 구현에 대한 오래된 역사가 있다.
  • 일부 앱은 DNS 검색을 한 번만 수행하고 결과를 무기한으로 캐시한다.
  • 앱과 라이브러리가 적절히 재-확인을 했다고 하더라도, DNS 레코드의 TTL이 낮거나 0이면 DNS에 부하가 높아 관리하기가 어려워 질 수 있다.

본 페이지의 뒷 부분에서 다양한 kube-proxy 구현의 동작에 대해 읽을 수 있다. 우선 알아두어야 할 것은, kube-proxy를 구동할 때, 커널 수준의 규칙이 수정(예를 들어, iptables 규칙이 생성될 수 있음)될 수 있고, 이는 때로는 리부트 전까지 정리되지 않을 수도 있다. 그래서, kube-proxy는 컴퓨터에서 저수준의, 특권을 가진(privileged) 네트워킹 프록시 서비스가 구동됨으로써 발생하는 결과를 이해하고 있는 관리자에 의해서만 구동되어야 한다. 비록 kube-proxy 실행 파일이 cleanup 기능을 지원하기는 하지만, 이 기능은 공식적인 기능이 아니기 때문에 구현된 그대로만 사용할 수 있다.

구성

kube-proxy는 구성에 따라 결정되는 여러 모드에서 기동될 수 있다.

  • kube-proxy의 구성은 컨피그맵(ConfigMap)을 통해 이루어진다. 그리고 해당 kube-proxy를 위한 컨피그맵은 실효성있게 거의 대부분의 kube-proxy의 플래그의 행위를 더 이상 사용하지 않도록 한다.
  • kube-proxy를 위한 해당 컨피그맵은 기동 중 구성의 재적용(live reloading)은 지원하지 않는다.
  • kube-proxy를 위한 컨피그맵 파라미터는 기동 시에 검증이나 확인을 하지 않는다. 예를 들어, 운영 체계가 iptables 명령을 허용하지 않을 경우, 표준 커널 kube-proxy 구현체는 작동하지 않을 것이다. 마찬가지로, netsh을 지원하지 않는 운영 체계에서는, 윈도우 유저스페이스 모드로는 기동하지 않을 것이다.

유저 스페이스(User space) 프록시 모드

이 모드에서는, kube-proxy는 쿠버네티스 컨트롤 플레인의 서비스 및 엔드포인트 오브젝트의 추가와 제거를 감시한다. 각 서비스는 로컬 노드에서 포트(임의로 선택됨)를 연다. 이 "프록시 포트"에 대한 모든 연결은 (엔드포인트를 통해 보고된 대로) 서비스의 백엔드 파드 중 하나로 프록시된다. kube-proxy는 사용할 백엔드 파드를 결정할 때 서비스의 SessionAffinity 설정을 고려한다.

마지막으로, 유저-스페이스 프록시는 서비스의 clusterIP (가상)와 port 에 대한 트래픽을 캡처하는 iptables 규칙을 설치한다. 이 규칙은 트래픽을 백엔드 파드를 프록시하는 프록시 포트로 리다이렉션한다.

기본적으로, 유저스페이스 모드의 kube-proxy는 라운드-로빈 알고리즘으로 백엔드를 선택한다.

유저스페이스 프록시에 대한 서비스 개요 다이어그램

iptables 프록시 모드

이 모드에서는, kube-proxy는 쿠버네티스 컨트롤 플레인의 서비스, 엔드포인트 오브젝트의 추가와 제거를 감시한다. 각 서비스에 대해, 서비스의 clusterIPport에 대한 트래픽을 캡처하고 해당 트래픽을 서비스의 백엔드 세트 중 하나로 리다이렉트(redirect)하는 iptables 규칙을 설치한다. 각 엔드포인트 오브젝트에 대해, 백엔드 파드를 선택하는 iptables 규칙을 설치한다.

기본적으로, iptables 모드의 kube-proxy는 임의의 백엔드를 선택한다.

트래픽을 처리하기 위해 iptables를 사용하면 시스템 오버헤드가 줄어드는데, 유저스페이스와 커널 스페이스 사이를 전환할 필요없이 리눅스 넷필터(netfilter)가 트래픽을 처리하기 때문이다. 이 접근 방식은 더 신뢰할 수 있기도 하다.

kube-proxy가 iptables 모드에서 실행 중이고 선택된 첫 번째 파드가 응답하지 않으면, 연결이 실패한다. 이는 userspace 모드와 다르다. 해당 시나리오에서는, kube-proxy는 첫 번째 파드에 대한 연결이 실패했음을 감지하고 다른 백엔드 파드로 자동으로 재시도한다.

파드 준비성 프로브(readiness probe)를 사용하여 백엔드 파드가 제대로 작동하는지 확인할 수 있으므로, iptables 모드의 kube-proxy는 정상으로 테스트된 백엔드만 볼 수 있다. 이렇게 하면 트래픽이 kube-proxy를 통해 실패한 것으로 알려진 파드로 전송되는 것을 막을 수 있다.

iptables 프록시에 대한 서비스 개요 다이어그램

IPVS 프록시 모드

FEATURE STATE: Kubernetes v1.11 [stable]

ipvs 모드에서는, kube-proxy는 쿠버네티스 서비스와 엔드포인트를 감시하고, netlink 인터페이스를 호출하여 그에 따라 IPVS 규칙을 생성하고 IPVS 규칙을 쿠버네티스 서비스와 엔드포인트와 주기적으로 동기화한다. 이 제어 루프는 IPVS 상태가 원하는 상태와 일치하도록 보장한다. 서비스에 접근하면, IPVS는 트래픽을 백엔드 파드 중 하나로 보낸다.

IPVS 프록시 모드는 iptables 모드와 유사한 넷필터 후크 기능을 기반으로 하지만, 해시 테이블을 기본 데이터 구조로 사용하고 커널 스페이스에서 동작한다. 이는 IPVS 모드의 kube-proxy는 iptables 모드의 kube-proxy보다 지연 시간이 짧은 트래픽을 리다이렉션하고, 프록시 규칙을 동기화할 때 성능이 훨씬 향상됨을 의미한다. 다른 프록시 모드와 비교했을 때, IPVS 모드는 높은 네트워크 트래픽 처리량도 지원한다.

IPVS는 트래픽을 백엔드 파드로 밸런싱하기 위한 추가 옵션을 제공한다. 다음과 같다.

  • rr: 라운드-로빈
  • lc: 최소 연결 (가장 적은 수의 열려있는 연결)
  • dh: 목적지 해싱
  • sh: 소스 해싱
  • sed: 최단 예상 지연 (shortest expected delay)
  • nq: 큐 미사용 (never queue)

IPVS 프록시에 대한 서비스 개요 다이어그램

이 프록시 모델에서 클라이언트가 쿠버네티스 또는 서비스 또는 파드에 대해 알지 못하는 경우 서비스의 IP:포트로 향하는 트래픽은 적절한 백엔드로 프록시된다.

특정 클라이언트의 연결이 매번 동일한 파드로 전달되도록 하려면, service.spec.sessionAffinity를 "ClientIP"로 설정하여 클라이언트의 IP 주소를 기반으로 세션 어피니티(Affinity)를 선택할 수 있다. (기본값은 "None") service.spec.sessionAffinityConfig.clientIP.timeoutSeconds를 적절히 설정하여 최대 세션 고정 시간을 설정할 수도 있다. (기본값은 10800으로, 3시간)

멀티-포트 서비스

일부 서비스의 경우, 둘 이상의 포트를 노출해야 한다. 쿠버네티스는 서비스 오브젝트에서 멀티 포트 정의를 구성할 수 있도록 지원한다. 서비스에 멀티 포트를 사용하는 경우, 모든 포트 이름을 명확하게 지정해야 한다. 예를 들면

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

자신의 IP 주소 선택

서비스 생성 요청시 고유한 클러스터 IP 주소를 지정할 수 있다. 이를 위해, .spec.clusterIP 필드를 설정한다. 예를 들어, 재사용하려는 기존 DNS 항목이 있거나, 특정 IP 주소로 구성되어 재구성이 어려운 레거시 시스템인 경우이다.

선택한 IP 주소는 API 서버에 대해 구성된 service-cluster-ip-range CIDR 범위 내의 유효한 IPv4 또는 IPv6 주소여야 한다. 유효하지 않은 clusterIP 주소 값으로 서비스를 생성하려고 하면, API 서버는 422 HTTP 상태 코드를 리턴하여 문제점이 있음을 알린다.

트래픽 정책

외부 트래픽 정책

spec.externalTrafficPolicy 필드를 설정하여 외부 소스에서 오는 트래픽이 어떻게 라우트될지를 제어할 수 있다. 이 필드는 Cluster 또는 Local로 설정할 수 있다. 필드를 Cluster로 설정하면 외부 트래픽을 준비 상태의 모든 엔드포인트로 라우트하며, Local로 설정하면 준비 상태의 노드-로컬 엔드포인트로만 라우트한다. 만약 트래픽 정책이 Local로 설정되어 있는데 노드-로컬 엔드포인트가 하나도 없는 경우, kube-proxy는 연관된 서비스로의 트래픽을 포워드하지 않는다.

내부 트래픽 정책

FEATURE STATE: Kubernetes v1.22 [beta]

spec.internalTrafficPolicy 필드를 설정하여 내부 소스에서 오는 트래픽이 어떻게 라우트될지를 제어할 수 있다. 이 필드는 Cluster 또는 Local로 설정할 수 있다. 필드를 Cluster로 설정하면 내부 트래픽을 준비 상태의 모든 엔드포인트로 라우트하며, Local로 설정하면 준비 상태의 노드-로컬 엔드포인트로만 라우트한다. 만약 트래픽 정책이 Local로 설정되어 있는데 노드-로컬 엔드포인트가 하나도 없는 경우, kube-proxy는 트래픽을 포워드하지 않는다.

서비스 디스커버리하기

쿠버네티스는 서비스를 찾는 두 가지 기본 모드를 지원한다. - 환경 변수와 DNS

환경 변수

파드가 노드에서 실행될 때, kubelet은 각 활성화된 서비스에 대해 환경 변수 세트를 추가한다. 도커 링크 호환 변수 (makeLinkVariables 참조)와 보다 간단한 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT 변수를 지원하고, 이때 서비스 이름은 대문자이고 대시는 밑줄로 변환된다.

예를 들어, TCP 포트 6379를 개방하고 클러스터 IP 주소 10.0.0.11이 할당된 서비스 redis-master는, 다음 환경 변수를 생성한다.

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

DNS

애드-온을 사용하여 쿠버네티스 클러스터의 DNS 서비스를 설정할 수(대개는 필수적임) 있다.

CoreDNS와 같은, 클러스터-인식 DNS 서버는 새로운 서비스를 위해 쿠버네티스 API를 감시하고 각각에 대한 DNS 레코드 세트를 생성한다. 클러스터 전체에서 DNS가 활성화된 경우 모든 파드는 DNS 이름으로 서비스를 자동으로 확인할 수 있어야 한다.

예를 들면, 쿠버네티스 네임스페이스 my-nsmy-service라는 서비스가 있는 경우, 컨트롤 플레인과 DNS 서비스가 함께 작동하여 my-service.my-ns에 대한 DNS 레코드를 만든다. my-ns 네임 스페이스의 파드들은 my-service(my-service.my-ns 역시 동작함)에 대한 이름 조회를 수행하여 서비스를 찾을 수 있어야 한다.

다른 네임스페이스의 파드들은 이름을 my-service.my-ns으로 사용해야 한다. 이 이름은 서비스에 할당된 클러스터 IP로 변환된다.

쿠버네티스는 또한 알려진 포트에 대한 DNS SRV (서비스) 레코드를 지원한다. my-service.my-ns 서비스에 프로토콜이 TCP로 설정된 http라는 포트가 있는 경우, IP 주소와 http에 대한 포트 번호를 검색하기 위해 _http._tcp.my-service.my-ns 에 대한 DNS SRV 쿼리를 수행할 수 있다.

쿠버네티스 DNS 서버는 ExternalName 서비스에 접근할 수 있는 유일한 방법이다. DNS 파드와 서비스에서 ExternalName 검색에 대한 자세한 정보를 찾을 수 있다.

헤드리스(Headless) 서비스

때때로 로드-밸런싱과 단일 서비스 IP는 필요치 않다. 이 경우, "헤드리스" 서비스라는 것을 만들 수 있는데, 명시적으로 클러스터 IP (.spec.clusterIP)에 "None"을 지정한다.

쿠버네티스의 구현에 묶이지 않고, 헤드리스 서비스를 사용하여 다른 서비스 디스커버리 메커니즘과 인터페이스할 수 있다.

헤드리스 서비스의 경우, 클러스터 IP가 할당되지 않고, kube-proxy가 이러한 서비스를 처리하지 않으며, 플랫폼에 의해 로드 밸런싱 또는 프록시를 하지 않는다. DNS가 자동으로 구성되는 방법은 서비스에 셀렉터가 정의되어 있는지 여부에 달려있다.

셀렉터가 있는 경우

셀렉터를 정의하는 헤드리스 서비스의 경우, 엔드포인트 컨트롤러는 API에서 엔드포인트 레코드를 생성하고, DNS 구성을 수정하여 서비스 를 지원하는 파드 를 직접 가리키는 A 레코드(IP 주소)를 반환한다.

셀렉터가 없는 경우

셀렉터를 정의하지 않는 헤드리스 서비스의 경우, 엔드포인트 컨트롤러는 엔드포인트 레코드를 생성하지 않는다. 그러나 DNS 시스템은 다음 중 하나를 찾고 구성한다.

  • ExternalName-유형 서비스에 대한 CNAME 레코드
  • 다른 모든 유형에 대해, 서비스의 이름을 공유하는 모든 엔드포인트에 대한 레코드

서비스 퍼블리싱 (ServiceTypes)

애플리케이션 중 일부(예: 프론트엔드)는 서비스를 클러스터 밖에 위치한 외부 IP 주소에 노출하고 싶은 경우가 있을 것이다.

쿠버네티스 ServiceTypes는 원하는 서비스 종류를 지정할 수 있도록 해준다. 기본 값은 ClusterIP이다.

Type 값과 그 동작은 다음과 같다.

  • ClusterIP: 서비스를 클러스터-내부 IP에 노출시킨다. 이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다. 이것은 ServiceTypes의 기본 값이다.
  • NodePort: 고정 포트 (NodePort)로 각 노드의 IP에 서비스를 노출시킨다. NodePort 서비스가 라우팅되는 ClusterIP 서비스가 자동으로 생성된다. <NodeIP>:<NodePort>를 요청하여, 클러스터 외부에서 NodePort 서비스에 접속할 수 있다.
  • LoadBalancer: 클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 외부 로드 밸런서가 라우팅되는 NodePortClusterIP 서비스가 자동으로 생성된다.
  • ExternalName: 값과 함께 CNAME 레코드를 리턴하여, 서비스를 externalName 필드의 콘텐츠 (예:foo.bar.example.com)에 매핑한다. 어떤 종류의 프록시도 설정되어 있지 않다.

인그레스를 사용하여 서비스를 노출시킬 수도 있다. 인그레스는 서비스 유형이 아니지만, 클러스터의 진입점 역할을 한다. 동일한 IP 주소로 여러 서비스를 노출시킬 수 있기 때문에 라우팅 규칙을 단일 리소스로 통합할 수 있다.

NodePort 유형

type 필드를 NodePort로 설정하면, 쿠버네티스 컨트롤 플레인은 --service-node-port-range 플래그로 지정된 범위에서 포트를 할당한다 (기본값 : 30000-32767). 각 노드는 해당 포트 (모든 노드에서 동일한 포트 번호)를 서비스로 프록시한다. 서비스는 할당된 포트를 .spec.ports[*].nodePort 필드에 나타낸다.

포트를 프록시하기 위해 특정 IP를 지정하려면, kube-proxy에 대한 --nodeport-addresses 플래그 또는 kube-proxy 구성 파일의 동등한 nodePortAddresses 필드를 특정 IP 블록으로 설정할 수 있다.

이 플래그는 쉼표로 구분된 IP 블록 목록(예: 10.0.0.0/8, 192.0.2.0/25)을 사용하여 kube-proxy가 로컬 노드로 고려해야 하는 IP 주소 범위를 지정한다.

예를 들어, --nodeport-addresses=127.0.0.0/8 플래그로 kube-proxy를 시작하면, kube-proxy는 NodePort 서비스에 대하여 루프백(loopback) 인터페이스만 선택한다. --nodeport-addresses의 기본 값은 비어있는 목록이다. 이것은 kube-proxy가 NodePort에 대해 사용 가능한 모든 네트워크 인터페이스를 고려해야 한다는 것을 의미한다. (이는 이전 쿠버네티스 릴리스와도 호환된다).

특정 포트 번호를 원한다면, nodePort 필드에 값을 지정할 수 있다. 컨트롤 플레인은 해당 포트를 할당하거나 API 트랜잭션이 실패했다고 보고한다. 이는 사용자 스스로 포트 충돌의 가능성을 고려해야 한다는 의미이다. 또한 NodePort 사용을 위해 구성된 범위 내에 있는, 유효한 포트 번호를 사용해야 한다.

NodePort를 사용하면 자유롭게 자체 로드 밸런싱 솔루션을 설정하거나, 쿠버네티스가 완벽하게 지원하지 않는 환경을 구성하거나, 하나 이상의 노드 IP를 직접 노출시킬 수 있다.

이 서비스는 <NodeIP>:spec.ports[*].nodePort.spec.clusterIP:spec.ports[*].port로 표기된다. kube-proxy에 대한 --nodeport-addresses 플래그 또는 kube-proxy 구성 파일의 동등한 필드가 설정된 경우, <NodeIP> 는 노드 IP를 필터링한다.

예를 들면

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
      # 기본적으로 그리고 편의상 `targetPort` 는 `port` 필드와 동일한 값으로 설정된다.
    - port: 80
      targetPort: 80
      # 선택적 필드
      # 기본적으로 그리고 편의상 쿠버네티스 컨트롤 플레인은 포트 범위에서 할당한다(기본값: 30000-32767)
      nodePort: 30007

로드밸런서 유형

외부 로드 밸런서를 지원하는 클라우드 공급자 상에서, type 필드를 LoadBalancer로 설정하면 서비스에 대한 로드 밸런서를 프로비저닝한다. 로드 밸런서의 실제 생성은 비동기적으로 수행되고, 프로비저닝된 밸런서에 대한 정보는 서비스의 .status.loadBalancer 필드에 발행된다. 예를 들면

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

외부 로드 밸런서의 트래픽은 백엔드 파드로 전달된다. 클라우드 공급자는 로드 밸런싱 방식을 결정한다.

일부 클라우드 공급자는 loadBalancerIP를 지정할 수 있도록 허용한다. 이 경우, 로드 밸런서는 사용자 지정 loadBalancerIP로 생성된다. loadBalancerIP 필드가 지정되지 않으면, 임시 IP 주소로 loadBalancer가 설정된다. loadBalancerIP를 지정했지만 클라우드 공급자가 이 기능을 지원하지 않는 경우, 설정한 loadbalancerIP 필드는 무시된다.

프로토콜 유형이 혼합된 로드밸런서

FEATURE STATE: Kubernetes v1.20 [alpha]

기본적으로 로드밸런서 서비스 유형의 경우 둘 이상의 포트가 정의되어 있을 때 모든 포트는 동일한 프로토콜을 가져야 하며 프로토콜은 클라우드 공급자가 지원하는 프로토콜이어야 한다.

kube-apiserver에 대해 기능 게이트 MixedProtocolLBService가 활성화된 경우 둘 이상의 포트가 정의되어 있을 때 다른 프로토콜을 사용할 수 있다.

로드밸런서 NodePort 할당 비활성화

FEATURE STATE: Kubernetes v1.20 [alpha]

v1.20부터는 spec.allocateLoadBalancerNodePorts 필드를 false로 설정하여 서비스 Type=LoadBalancer에 대한 노드 포트 할당을 선택적으로 비활성화 할 수 있다. 노드 포트를 사용하는 대신 트래픽을 파드로 직접 라우팅하는 로드 밸런서 구현에만 사용해야 한다. 기본적으로 spec.allocateLoadBalancerNodePortstrue이며 로드밸런서 서비스 유형은 계속해서 노드 포트를 할당한다. 노드 포트가 할당된 기존 서비스에서 spec.allocateLoadBalancerNodePortsfalse로 설정된 경우 해당 노드 포트는 자동으로 할당 해제되지 않는다. 이러한 노드 포트를 할당 해제하려면 모든 서비스 포트에서 nodePorts 항목을 명시적으로 제거해야 한다. 이 필드를 사용하려면 ServiceLBNodePortControl 기능 게이트를 활성화해야 한다.

로드 밸런서 구현 클래스 지정

FEATURE STATE: Kubernetes v1.22 [beta]

spec.loadBalancerClass 필드를 설정하여 클라우드 제공자가 설정한 기본값 이외의 로드 밸런서 구현을 사용할 수 있다. 이 기능은 v1.21부터 사용할 수 있으며, v1.21에서는 이 필드를 사용하기 위해 ServiceLoadBalancerClass 기능 게이트를 활성화해야 하지만, v1.22부터는 해당 기능 게이트가 기본적으로 활성화되어 있다. 기본적으로, spec.loadBalancerClassnil 이고, 클러스터가 클라우드 제공자의 로드밸런서를 이용하도록 --cloud-provider 컴포넌트 플래그를 이용하여 설정되어 있으면 LoadBalancer 유형의 서비스는 클라우드 공급자의 기본 로드 밸런서 구현을 사용한다. spec.loadBalancerClass 가 지정되면, 지정된 클래스와 일치하는 로드 밸런서 구현이 서비스를 감시하고 있다고 가정한다. 모든 기본 로드 밸런서 구현(예: 클라우드 공급자가 제공하는 로드 밸런서 구현)은 이 필드가 설정된 서비스를 무시한다. spec.loadBalancerClassLoadBalancer 유형의 서비스에서만 설정할 수 있다. 한 번 설정하면 변경할 수 없다. spec.loadBalancerClass 의 값은 "internal-vip" 또는 "example.com/internal-vip" 와 같은 선택적 접두사가 있는 레이블 스타일 식별자여야 한다. 접두사가 없는 이름은 최종 사용자를 위해 예약되어 있다.

내부 로드 밸런서

혼재된 환경에서는 서비스의 트래픽을 동일한 (가상) 네트워크 주소 블록 내로 라우팅해야 하는 경우가 있다.

수평 분할 DNS 환경에서는 외부와 내부 트래픽을 엔드포인트로 라우팅 할 수 있는 두 개의 서비스가 필요하다.

내부 로드 밸런서를 설정하려면, 사용 중인 클라우드 서비스 공급자에 따라 다음의 어노테이션 중 하나를 서비스에 추가한다.

탭 중 하나를 선택

[...]
metadata:
    name: my-service
    annotations:
        cloud.google.com/load-balancer-type: "Internal"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-internal: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
[...]

[...]
metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
[...]

[...]
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
[...]

AWS에서 TLS 지원

AWS에서 실행되는 클러스터에서 부분적으로 TLS / SSL을 지원하기 위해, LoadBalancer 서비스에 세 가지 어노테이션을 추가할 수 있다.

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012

첫 번째는 사용할 인증서의 ARN을 지정한다. IAM에 업로드된 써드파티 발급자의 인증서이거나 AWS Certificate Manager에서 생성된 인증서일 수 있다.

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)

두 번째 어노테이션은 파드가 알려주는 프로토콜을 지정한다. HTTPS와 SSL의 경우, ELB는 인증서를 사용하여 암호화된 연결을 통해 파드가 스스로를 인증할 것으로 예상한다.

HTTP와 HTTPS는 7 계층 프록시를 선택한다. ELB는 요청을 전달할 때 사용자와의 연결을 종료하고, 헤더를 파싱하고 사용자의 IP 주소로 X-Forwarded-For 헤더를 삽입한다. (파드는 해당 연결의 다른 종단에서의 ELB의 IP 주소만 참조)

TCP 및 SSL은 4 계층 프록시를 선택한다. ELB는 헤더를 수정하지 않고 트래픽을 전달한다.

일부 포트는 보안성을 갖추고 다른 포트는 암호화되지 않은 혼재된 사용 환경에서는 다음 어노테이션을 사용할 수 있다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
        service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,8443"

위의 예에서, 서비스에 80, 443, 8443의 3개 포트가 포함된 경우, 443, 8443은 SSL 인증서를 사용하지만, 80은 프록시하는 HTTP이다.

쿠버네티스 v1.9부터는 서비스에 대한 HTTPS 또는 SSL 리스너와 함께 사전에 정의된 AWS SSL 정책을 사용할 수 있다. 사용 가능한 정책을 확인하려면, aws 커맨드라인 툴을 사용한다.

aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'

그리고 "service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy" 어노테이션을 사용하여 이러한 정책 중 하나를 지정할 수 있다. 예를 들면

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS-1-2-2017-01"

AWS에서 지원하는 프록시 프로토콜

AWS에서 실행되는 클러스터에 대한 프록시 프로토콜 지원을 활성화하려면, 다음의 서비스 어노테이션을 사용할 수 있다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"

버전 1.3.0 부터, 이 어노테이션의 사용은 ELB에 의해 프록시되는 모든 포트에 적용되며 다르게 구성할 수 없다.

AWS의 ELB 접근 로그

AWS ELB 서비스의 접근 로그를 관리하기 위한 몇 가지 어노테이션이 있다.

service.beta.kubernetes.io/aws-load-balancer-access-log-enabled 어노테이션은 접근 로그의 활성화 여부를 제어한다.

service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval 어노테이션은 접근 로그를 게시하는 간격을 분 단위로 제어한다. 5분 또는 60분의 간격으로 지정할 수 있다.

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name 어노테이션은 로드 밸런서 접근 로그가 저장되는 Amazon S3 버킷의 이름을 제어한다.

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix 어노테이션은 Amazon S3 버킷을 생성한 논리적 계층을 지정한다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
        # 로드 밸런서의 접근 로그 활성화 여부를 명시.
        service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
        # 접근 로그를 게시하는 간격을 분 단위로 제어. 5분 또는 60분의 간격을 지정.
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
        # 로드 밸런서 접근 로그가 저장되는 Amazon S3 버킷의 이름 명시.
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-bucket-prefix/prod"
        # Amazon S3 버킷을 생성한 논리적 계층을 지정. 예: `my-bucket-prefix/prod`

AWS의 연결 드레이닝(Draining)

Classic ELB의 연결 드레이닝은 service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled 어노테이션을 "true"값으로 설정하여 관리할 수 ​​있다. service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout 어노테이션을 사용하여 인스턴스를 해제하기 전에, 기존 연결을 열어 두는 목적으로 최대 시간을 초 단위로 설정할 수도 있다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"

다른 ELB 어노테이션

이하는 클래식 엘라스틱 로드 밸런서(Classic Elastic Load Balancers)를 관리하기 위한 다른 어노테이션이다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
        # 로드 밸런서가 연결을 닫기 전에, 유휴 상태(연결을 통해 전송 된 데이터가 없음)의 연결을 허용하는 초단위 시간

        service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
        # 로드 밸런서에 교차-영역(cross-zone) 로드 밸런싱을 사용할 지 여부를 지정

        service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
        # 쉼표로 구분된 key-value 목록은 ELB에
        # 추가 태그로 기록됨

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
        # 백엔드가 정상인 것으로 간주되는데 필요한 연속적인
        # 헬스 체크 성공 횟수이다. 기본값은 2이며, 2와 10 사이여야 한다.

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
        # 백엔드가 비정상인 것으로 간주되는데 필요한
        # 헬스 체크 실패 횟수이다. 기본값은 6이며, 2와 10 사이여야 한다.

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
        # 개별 인스턴스의 상태 점검 사이의
        # 대략적인 간격 (초 단위). 기본값은 10이며, 5와 300 사이여야 한다.

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
        # 헬스 체크 실패를 의미하는 무 응답의 총 시간 (초 단위)
        # 이 값은 service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
        # 값 보다 작아야한다. 기본값은 5이며, 2와 60 사이여야 한다.

        service.beta.kubernetes.io/aws-load-balancer-security-groups: "sg-53fae93f"
        # 생성된 ELB에 설정할 기존 보안 그룹(security group) 목록.
        # service.beta.kubernetes.io/aws-load-balancer-extra-security-groups 어노테이션과 달리, 이는 이전에 ELB에 할당된 다른 모든 보안 그룹을 대체하며,
        # '해당 ELB를 위한 고유 보안 그룹 생성'을 오버라이드한다.
        # 목록의 첫 번째 보안 그룹 ID는 인바운드 트래픽(서비스 트래픽과 헬스 체크)이 워커 노드로 향하도록 하는 규칙으로 사용된다.
        # 여러 ELB가 하나의 보안 그룹 ID와 연결되면, 1줄의 허가 규칙만이 워커 노드 보안 그룹에 추가된다.
        # 즉, 만약 여러 ELB 중 하나를 지우면, 1줄의 허가 규칙이 삭제되어, 같은 보안 그룹 ID와 연결된 모든 ELB에 대한 접속이 막힌다.
        # 적절하게 사용되지 않으면 이는 다수의 서비스가 중단되는 상황을 유발할 수 있다.

        service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
        # 생성된 ELB에 추가할 추가 보안 그룹 목록
        # 이 방법을 사용하면 이전에 생성된 고유 보안 그룹이 그대로 유지되므로, 각 ELB가 고유 보안 그룹 ID와 그에 매칭되는 허가 규칙 라인을 소유하여
        # 트래픽(서비스 트래픽과 헬스 체크)이 워커 노드로 향할 수 있도록 한다. 여기에 기재되는 보안 그룹은 여러 서비스 간 공유될 수 있다.

        service.beta.kubernetes.io/aws-load-balancer-target-node-labels: "ingress-gw,gw-name=public-api"
        # 로드 밸런서의 대상 노드를 선택하는 데
        # 사용되는 키-값 쌍의 쉼표로 구분된 목록

AWS의 네트워크 로드 밸런서 지원

FEATURE STATE: Kubernetes v1.15 [beta]

AWS에서 네트워크 로드 밸런서를 사용하려면, nlb 값이 설정된 service.beta.kubernetes.io/aws-load-balancer-type 어노테이션을 사용한다.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

클래식 엘라스틱 로드 밸런서와 달리, 네트워크 로드 밸런서 (NLB)는 클라이언트의 IP 주소를 노드로 전달한다. 서비스의 .spec.externalTrafficPolicyCluster로 설정되어 있으면, 클라이언트의 IP 주소가 종단 파드로 전파되지 않는다.

.spec.externalTrafficPolicyLocal로 설정하면, 클라이언트 IP 주소가 종단 파드로 전파되지만, 트래픽이 고르지 않게 분배될 수 있다. 특정 로드밸런서 서비스를 위한 파드가 없는 노드는 자동 할당된 .spec.healthCheckNodePort에 의해서 NLB 대상 그룹의 헬스 체크에 실패하고 트래픽을 수신하지 못하게 된다.

트래픽을 균일하게 하려면, DaemonSet을 사용하거나, 파드 안티어피니티(pod anti-affinity) 를 지정하여 동일한 노드에 위치하지 않도록 한다.

내부 로드 밸런서 어노테이션과 함께 NLB 서비스를 사용할 수도 있다.

클라이언트 트래픽이 NLB 뒤의 인스턴스에 도달하기 위해, 노드 보안 그룹은 다음 IP 규칙으로 수정된다.

규칙 프로토콜 포트 IP 범위 IP 범위 설명
헬스 체크 TCP NodePort(s) (.spec.healthCheckNodePort for .spec.externalTrafficPolicy = Local) Subnet CIDR kubernetes.io/rule/nlb/health=<loadBalancerName>
클라이언트 트래픽 TCP NodePort(s) .spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0) kubernetes.io/rule/nlb/client=<loadBalancerName>
MTU 탐색 ICMP 3,4 .spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0) kubernetes.io/rule/nlb/mtu=<loadBalancerName>

네트워크 로드 밸런서에 접근할 수 있는 클라이언트 IP를 제한하려면, loadBalancerSourceRanges를 지정한다.

spec:
  loadBalancerSourceRanges:
    - "143.231.0.0/16"

엘라스틱 IP에 대한 설명 문서와 기타 일반적 사용 사례를 AWS 로드 밸런서 컨트롤러 문서에서 볼 수 있다.

Tencent Kubernetes Engine (TKE)의 다른 CLB 어노테이션

아래 표시된 것처럼 TKE에서 클라우드 로드 밸런서를 관리하기 위한 다른 어노테이션이 있다.

    metadata:
      name: my-service
      annotations:
        # 지정된 노드로 로드 밸런서 바인드
        service.kubernetes.io/qcloud-loadbalancer-backends-label: key in (value1, value2)

        # 기존 로드 밸런서의 ID
        service.kubernetes.io/tke-existed-lbid:lb-6swtxxxx

        # 로드 밸런서 (LB)에 대한 사용자 지정 매개 변수는 아직 LB 유형 수정을 지원하지 않음
        service.kubernetes.io/service.extensiveParameters: ""

        # LB 리스너의 사용자 정의 매개 변수
        service.kubernetes.io/service.listenerParameters: ""

        # 로드 밸런서 유형 지정
        # 유효 값 : 클래식 (클래식 클라우드 로드 밸런서) 또는 애플리케이션 (애플리케이션 클라우드 로드 밸런서)
        service.kubernetes.io/loadbalance-type: xxxxx

        # 퍼블릭 네트워크 대역폭 청구 방법 지정
        # 유효 값: TRAFFIC_POSTPAID_BY_HOUR (트래픽 별) 및 BANDWIDTH_POSTPAID_BY_HOUR (대역폭 별)
        service.kubernetes.io/qcloud-loadbalancer-internet-charge-type: xxxxxx

        # 대역폭 값 지정 (값 범위 : [1,2000] Mbps).
        service.kubernetes.io/qcloud-loadbalancer-internet-max-bandwidth-out: "10"

        # 이 어느테이션이 설정되면, 로드 밸런서는 파드가
        # 실행중인 노드만 등록하고, 그렇지 않으면 모든 노드가 등록됨
        service.kubernetes.io/local-svc-only-bind-node-with-pod: true

ExternalName 유형

ExternalName 유형의 서비스는 my-service 또는 cassandra와 같은 일반적인 셀렉터에 대한 서비스가 아닌, DNS 이름에 대한 서비스에 매핑한다. spec.externalName 파라미터를 사용하여 이러한 서비스를 지정한다.

예를 들면, 이 서비스 정의는 prod 네임 스페이스의 my-service 서비스를 my.database.example.com에 매핑한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

my-service.prod.svc.cluster.local 호스트를 검색하면, 클러스터 DNS 서비스는 my.database.example.com 값의 CNAME 레코드를 반환한다. my-service에 접근하는 것은 다른 서비스와 같은 방식으로 작동하지만, 리다이렉션은 프록시 또는 포워딩을 통하지 않고 DNS 수준에서 발생한다는 중요한 차이점이 있다. 나중에 데이터베이스를 클러스터로 이동하기로 결정한 경우, 해당 파드를 시작하고 적절한 셀렉터 또는 엔드포인트를 추가하고, 서비스의 유형(type)을 변경할 수 있다.

외부 IP

하나 이상의 클러스터 노드로 라우팅되는 외부 IP가 있는 경우, 쿠버네티스 서비스는 이러한 externalIPs에 노출될 수 있다. 서비스 포트에서 외부 IP (목적지 IP)를 사용하여 클러스터로 들어오는 트래픽은 서비스 엔드포인트 중 하나로 라우팅된다. externalIPs는 쿠버네티스에 의해 관리되지 않으며 클러스터 관리자에게 책임이 있다.

서비스 명세에서, externalIPs는 모든 ServiceTypes와 함께 지정할 수 있다. 아래 예에서, 클라이언트는 "80.11.12.10:80"(외부 IP:포트)로 "my-service"에 접근할 수 있다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
  externalIPs:
    - 80.11.12.10

단점

VIP용 유저스페이스 프록시를 사용하면 중소 규모의 스케일에서는 동작하지만, 수천 개의 서비스가 포함된 대규모 클러스터로는 확장되지 않는다. 포털에 대한 독창적인 설계 제안에 이에 대한 자세한 내용이 있다.

유저스페이스 프록시를 사용하면 서비스에 접근하는 패킷의 소스 IP 주소가 가려진다. 이것은 일종의 네트워크 필터링 (방화벽)을 불가능하게 만든다. iptables 프록시 모드는 클러스터 내 소스 IP를 가리지 않지만, 여전히 로드 밸런서 또는 노드-포트를 통해 오는 클라이언트에 영향을 미친다.

Type 필드는 중첩된 기능으로 설계되었다. - 각 레벨은 이전 레벨에 추가된다. 이는 모든 클라우드 공급자에 반드시 필요한 것은 아니지만, (예: Google Compute Engine는 LoadBalancer를 작동시키기 위해 NodePort를 할당할 필요는 없지만, AWS는 필요하다) 현재 API에는 필요하다.

가상 IP 구현

서비스를 사용하려는 많은 사람들에게 이전 정보가 충분해야 한다. 그러나, 이해가 필요한 부분 뒤에는 많은 일이 있다.

충돌 방지

쿠버네티스의 주요 철학 중 하나는 잘못한 것이 없는 경우 실패할 수 있는 상황에 노출되어서는 안된다는 것이다. 서비스 리소스 설계 시, 다른 사람의 포트 선택과 충돌할 경우에 대비해 자신의 포트 번호를 선택하지 않아도 된다. 그것은 격리 실패이다.

서비스에 대한 포트 번호를 선택할 수 있도록 하기 위해, 두 개의 서비스가 충돌하지 않도록 해야 한다. 쿠버네티스는 각 서비스에 고유한 IP 주소를 할당하여 이를 수행한다.

각 서비스가 고유한 IP를 받도록 하기 위해, 내부 할당기는 각 서비스를 만들기 전에 etcd에서 글로벌 할당 맵을 원자적으로(atomically) 업데이트한다. 서비스가 IP 주소 할당을 가져오려면 레지스트리에 맵 오브젝트가 있어야 하는데, 그렇지 않으면 IP 주소를 할당할 수 없다는 메시지와 함께 생성에 실패한다.

컨트롤 플레인에서, 백그라운드 컨트롤러는 해당 맵을 생성해야 한다. (인-메모리 잠금을 사용하는 이전 버전의 쿠버네티스에서 마이그레이션 지원 필요함) 쿠버네티스는 또한 컨트롤러를 사용하여 유효하지 않은 할당 (예: 관리자 개입으로)을 체크하고 더 이상 서비스에서 사용하지 않는 할당된 IP 주소를 정리한다.

서비스 IP 주소

실제로 고정된 목적지로 라우팅되는 파드 IP 주소와 달리, 서비스 IP는 실제로 단일 호스트에서 응답하지 않는다. 대신에, kube-proxy는 iptables (리눅스의 패킷 처리 로직)를 필요에 따라 명백하게 리다이렉션되는 가상 IP 주소를 정의하기 위해 사용한다. 클라이언트가 VIP에 연결하면, 트래픽이 자동으로 적절한 엔드포인트로 전송된다. 환경 변수와 서비스 용 DNS는 실제로 서비스의 가상 IP 주소 (및 포트)로 채워진다.

kube-proxy는 조금씩 다르게 작동하는 세 가지 프록시 모드—유저스페이스, iptables and IPVS—를 지원한다.

유저스페이스 (Userspace)

예를 들어, 위에서 설명한 이미지 처리 애플리케이션을 고려한다. 백엔드 서비스가 생성되면, 쿠버네티스 마스터는 가상 IP 주소(예 : 10.0.0.1)를 할당한다. 서비스 포트를 1234라고 가정하면, 서비스는 클러스터의 모든 kube-proxy 인스턴스에서 관찰된다. 프록시가 새 서비스를 발견하면, 새로운 임의의 포트를 열고, 가상 IP 주소에서 이 새로운 포트로 iptables 리다이렉션을 설정한 후, 연결을 수락하기 시작한다.

클라이언트가 서비스의 가상 IP 주소에 연결하면, iptables 규칙이 시작되고, 패킷을 프록시의 자체 포트로 리다이렉션한다. "서비스 프록시"는 백엔드를 선택하고, 클라이언트에서 백엔드로의 트래픽을 프록시하기 시작한다.

이는 서비스 소유자가 충돌 위험 없이 원하는 어떤 포트든 선택할 수 있음을 의미한다. 클라이언트는 실제로 접근하는 파드를 몰라도, IP와 포트에 연결할 수 있다.

iptables

다시 한번, 위에서 설명한 이미지 처리 애플리케이션을 고려한다. 백엔드 서비스가 생성되면, 쿠버네티스 컨트롤 플레인은 가상 IP 주소(예 : 10.0.0.1)를 할당한다. 서비스 포트를 1234라고 가정하면, 서비스는 클러스터의 모든 kube-proxy 인스턴스에서 관찰된다. 프록시가 새로운 서비스를 발견하면, 가상 IP 주소에서 서비스-별 규칙으로 리다이렉션되는 일련의 iptables 규칙을 설치한다. 서비스-별 규칙은 트래픽을 (목적지 NAT를 사용하여) 백엔드로 리다이렉션하는 엔드포인트-별 규칙에 연결한다.

클라이언트가 서비스의 가상 IP 주소에 연결하면 iptables 규칙이 시작한다. (세션 어피니티(Affinity)에 따라 또는 무작위로) 백엔드가 선택되고 패킷이 백엔드로 리다이렉션된다. 유저스페이스 프록시와 달리, 패킷은 유저스페이스로 복사되지 않으며, 가상 IP 주소가 작동하기 위해 kube-proxy가 실행 중일 필요는 없으며, 노드는 변경되지 않은 클라이언트 IP 주소에서 오는 트래픽을 본다.

트래픽이 노드-포트 또는 로드 밸런서를 통해 들어오는 경우에도, 이와 동일한 기본 흐름이 실행되지만, 클라이언트 IP는 변경된다.

IPVS

iptables 작업은 대규모 클러스터 (예: 10,000 서비스)에서 크게 느려진다. IPVS는 로드 밸런싱을 위해 설계되었고 커널-내부 해시 테이블을 기반으로 한다. 따라서 IPVS 기반 kube-proxy로부터 많은 개수의 서비스에서 일관성 있는 성능을 가질 수 있다. 한편, IPVS 기반 kube-proxy는 보다 정교한 로드 밸런싱 알고리즘 (least conns, locality, weighted, persistence)을 가진다.

API 오브젝트

서비스는 쿠버네티스 REST API의 최상위 리소스이다. API 오브젝트에 대한 자세한 내용은 다음을 참고한다. 서비스 API 오브젝트

지원되는 프로토콜

TCP

모든 종류의 서비스에 TCP를 사용할 수 있으며, 이는 기본 네트워크 프로토콜이다.

UDP

대부분의 서비스에 UDP를 사용할 수 있다. type=LoadBalancer 서비스의 경우, UDP 지원은 이 기능을 제공하는 클라우드 공급자에 따라 다르다.

SCTP

FEATURE STATE: Kubernetes v1.20 [stable]

SCTP 트래픽을 지원하는 네트워크 플러그인을 사용하는 경우 대부분의 서비스에 SCTP를 사용할 수 있다. type=LoadBalancer 서비스의 경우 SCTP 지원은 이 기능을 제공하는 클라우드 공급자에 따라 다르다. (대부분 그렇지 않음)

경고

멀티홈드(multihomed) SCTP 연결을 위한 지원
윈도우
유저스페이스 kube-proxy

HTTP

클라우드 공급자가 이를 지원하는 경우, LoadBalancer 모드의 서비스를 사용하여 서비스의 엔드포인트로 전달하는 외부 HTTP / HTTPS 리버스 프록시를 설정할 수 있다.

PROXY 프로토콜

클라우드 공급자가 지원하는 경우에, LoadBalancer 모드의 서비스를 사용하여 쿠버네티스 자체 외부에 로드 밸런서를 구성할 수 있으며, 이때 접두사가 PROXY 프로토콜 인 연결을 전달하게 된다.

로드 밸런서는 들어오는 연결을 설명하는 초기 일련의 옥텟(octets)을 전송하며, 이 예와 유사하게

PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n

클라이언트 데이터가 뒤따라온다.

다음 내용

3.5.2 - 토폴로지 키를 사용하여 토폴로지-인지 트래픽 라우팅

FEATURE STATE: Kubernetes v1.21 [deprecated]

서비스 토폴로지 를 활성화 하면 서비스는 클러스터의 노드 토폴로지를 기반으로 트래픽을 라우팅한다. 예를 들어, 서비스는 트래픽을 클라이언트와 동일한 노드이거나 동일한 가용성 영역에 있는 엔드포인트로 우선적으로 라우팅되도록 지정할 수 있다.

소개

기본적으로 ClusterIP 또는 NodePort 서비스로 전송된 트래픽은 서비스의 모든 백엔드 주소로 라우팅될 수 있다. 쿠버네티스 1.7을 사용하면 트래픽을 수신한 동일한 노드에서 실행 중인 파드로 "외부(external)" 트래픽을 라우팅할 수 있다. ClusterIP 서비스의 경우, 라우팅에 대한 동일한 노드 기본 설정이 불가능했다. 또한 동일한 영역 내의 엔드 포인트에 대한 라우팅을 선호하도록 클러스터를 구성할 수도 없다. 서비스에 topologyKeys 를 설정하면, 출발 및 대상 노드에 대한 노드 레이블을 기반으로 트래픽을 라우팅하는 정책을 정의할 수 있다.

소스와 목적지 사이의 레이블 일치를 통해 클러스터 운영자는 서로 "근접(closer)"하거나 "먼(father)" 노드 그룹을 지정할 수 있다. 자신의 요구 사항에 맞는 메트릭을 나타내는 레이블을 정의할 수 있다. 예를 들어, 퍼블릭 클라우드에서는 지역 간의 트래픽에는 관련 비용이 발생(지역 내 트래픽은 일반적으로 그렇지 않다)하기 때문에, 네트워크 트래픽을 동일한 지역 내에 유지하는 것을 선호할 수 있다. 다른 일반적인 필요성으로는 데몬셋(DaemonSet)이 관리하는 로컬 파드로 트래픽을 라우팅하거나, 대기 시간을 최소화하기 위해 동일한 랙 상단(top-of-rack) 스위치에 연결된 노드로 트래픽을 유지하는 것이 있다.

서비스 토폴로지 사용하기

만약 클러스터에서 ServiceTopology 기능 게이트가 활성화된 경우, 서비스 사양에서 topologyKeys 필드를 지정해서 서비스 트래픽 라우팅을 제어할 수 있다. 이 필드는 이 서비스에 접근할 때 엔드포인트를 정렬하는데 사용되는 노드 레이블의 우선 순위 목록이다. 트래픽은 첫 번째 레이블 값이 해당 레이블의 발신 노드 값과 일치하는 노드로 보내진다. 만약 노드에 서비스와 일치하는 백엔드가 없는 경우, 두 번째 레이블을 그리고 더 이상의 레이블이 남지 않을 때까지 고려한다.

만약 일치하는 것을 못찾는 경우에는, 서비스에 대한 백엔드가 없었던 것처럼 트래픽이 거부될 것이다. 즉, 엔드포인트는 사용 가능한 백엔드가 있는 첫 번째 토폴로지 키를 기반으로 선택된다. 만약 이 필드가 지정되고 모든 항목에 클라이언트의 토폴로지와 일치하는 백엔드가 없는 경우, 서비스에는 해당 클라이언트에 대한 백엔드가 없기에 연결에 실패해야 한다. 특수한 값인 "*" 은 "모든 토폴로지"를 의미하는데 사용될 수 있다. 이 캐치 올(catch-all) 값을 사용하는 경우 목록의 마지막 값으로만 타당하다.

만약 topologyKeys 가 지정되지 않거나 비어있는 경우 토폴로지 제약 조건이 적용되지 않는다.

호스트 이름, 영역 이름 그리고 지역 이름으로 레이블이 지정된 노드가 있는 클러스터가 있다고 생각해 보자. 그러고 나면, 서비스의 topologyKeys 값을 설정해서 다음과 같이 트래픽을 전달할 수 있다.

  • 동일한 노드의 엔드포인트에만 해당하고, 엔드포인트가 노드에 없으면 실패한다: ["kubernetes.io/hostname"].
  • 동일한 노드의 엔드포인트를 선호하지만, 동일한 영역의 엔드포인트로 대체 한 후 동일한 지역으로 대체되고, 그렇지 않으면 실패한다: ["kubernetes.io/hostname", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"]. 예를 들어 데이터 위치가 중요한 경우에 유용할 수 있다.
  • 동일한 영역이 선호되지만, 이 영역 내에 사용할 수 있는 항목이 없는 경우에는 사용가능한 엔드포인트로 대체된다: ["topology.kubernetes.io/zone", "*"].

제약들

  • 서비스 토폴로지는 externalTrafficPolicy=Local 와 호환되지 않으므로 서비스는 이 두 가지 기능을 함께 사용할 수 없다. 동일한 서비스가 아닌 같은 클러스터의 다른 서비스라면 이 기능을 함께 사용할 수 있다.

  • 유효한 토폴로지 키는 현재 kubernetes.io/hostname, topology.kubernetes.io/zone 그리고 topology.kubernetes.io/region 로 제한되어 있지만, 앞으로 다른 노드 레이블로 일반화 될 것이다.

  • 토폴로지 키는 유효한 레이블 키이어야 하며 최대 16개의 키를 지정할 수 있다.

  • 만약 캐치 올(catch-all) 값인 "*" 를 사용한다면 토폴로지 키들의 마지막 값이어야 한다.

예시들

다음은 서비스 토폴로지 기능을 사용하는 일반적인 예시이다.

노드 로컬 엔드포인트만

노드 로컬 엔드포인트로만 라우팅하는 서비스이다. 만약 노드에 엔드포인트가 없으면 트레픽이 드롭된다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"

노드 로컬 엔드포인트 선호

노드 로컬 엔드포인트를 선호하지만, 노드 로컬 엔드포인트가 없는 경우 클러스터 전체 엔드포인트로 폴백 하는 서비스이다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "*"

영역 또는 지리적 엔드포인트만

영역보다는 지리적 엔드포인트를 선호하는 서비스이다. 만약 엔드포인트가 없다면, 트래픽은 드롭된다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"

노드 로컬, 영역 및 지역 엔드포인트 선호

노드 로컬, 영역 및 지역 엔드포인트를 선호하지만, 클러스터 전체 엔드포인트로 폴백하는 서비스이다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"
    - "*"

다음 내용

3.5.3 - 서비스 및 파드용 DNS

쿠버네티스는 파드와 서비스를 위한 DNS 레코드를 생성한다. 사용자는 IP 주소 대신에 일관된 DNS 네임을 통해서 서비스에 접속할 수 있다.

소개

쿠버네티스 DNS는 클러스터의 서비스와 DNS 파드를 관리하며, 개별 컨테이너들이 DNS 네임을 해석할 때 DNS 서비스의 IP를 사용하도록 kubelets를 구성한다.

클러스터 내의 모든 서비스(DNS 서버 자신도 포함하여)에는 DNS 네임이 할당된다. 기본적으로 클라이언트 파드의 DNS 검색 리스트는 파드 자체의 네임스페이스와 클러스터의 기본 도메인을 포함한다.

서비스의 네임스페이스

DNS 쿼리는 그것을 생성하는 파드의 네임스페이스에 따라 다른 결과를 반환할 수 있다. 네임스페이스를 지정하지 않은 DNS 쿼리는 파드의 네임스페이스에 국한된다. DNS 쿼리에 네임스페이스를 명시하여 다른 네임스페이스에 있는 서비스에 접속한다.

예를 들어, test 네임스페이스에 있는 파드를 생각해보자. data 서비스는 prod 네임스페이스에 있다.

이 경우, data 에 대한 쿼리는 파드의 test 네임스페이스를 사용하기 때문에 결과를 반환하지 않을 것이다.

data.prod 로 쿼리하면 의도한 결과를 반환할 것이다. 왜냐하면 네임스페이스가 명시되어 있기 때문이다.

DNS 쿼리는 파드의 /etc/resolv.conf 를 사용하여 확장될 수 있을 것이다. Kubelet은 각 파드에 대해서 파일을 설정한다. 예를 들어, data 만을 위한 쿼리는 data.test.cluster.local 로 확장된다. search 옵션의 값은 쿼리를 확장하기 위해서 사용된다. DNS 쿼리에 대해 더 자세히 알고 싶은 경우, resolv.conf 설명 페이지.를 참고한다.

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

요약하면, test 네임스페이스에 있는 파드는 data.prod 또는 data.prod.svc.cluster.local 중 하나를 통해 성공적으로 해석될 수 있다.

DNS 레코드

어떤 오브젝트가 DNS 레코드를 가지는가?

  1. 서비스
  2. 파드

다음 섹션은 지원되는 DNS 레코드의 종류 및 레이아웃에 대한 상세 내용이다. 혹시 동작시킬 필요가 있는 다른 레이아웃, 네임, 또는 쿼리는 구현 세부 사항으로 간주되며 경고 없이 변경될 수 있다. 최신 명세 확인을 위해서는, 쿠버네티스 DNS-기반 서비스 디스커버리를 본다.

서비스

A/AAAA 레코드

"노멀"(헤드리스가 아닌) 서비스는 서비스 IP 계열에 따라 my-svc.my-namespace.svc.cluster-domain.example 형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 이는 서비스의 클러스터 IP로 해석된다.

"헤드리스"(클러스터 IP가 없는) 서비스 또한 서비스 IP 계열에 따라 my-svc.my-namespace.svc.cluster-domain.example 형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 노멀 서비스와는 다르게 이는 서비스에 의해 선택된 파드들의 IP 집합으로 해석된다. 클라이언트는 해석된 IP 집합에서 IP를 직접 선택하거나 표준 라운드로빈을 통해 선택할 수 있다.

SRV 레코드

SRV 레코드는 노멀 서비스 또는 헤드리스 서비스에 속하는 네임드 포트를 위해 만들어졌다. 각각의 네임드 포트에 대해서 SRV 레코드는 다음과 같은 형식을 가질 수 있다. _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example. 정규 서비스의 경우, 이는 포트 번호와 도메인 네임으로 해석된다. my-svc.my-namespace.svc.cluster-domain.example. 헤드리스 서비스의 경우, 서비스를 지원하는 각 파드에 대해 하나씩 복수 응답으로 해석되며 이 응답은 파드의 포트 번호와 도메인 이름을 포함한다. auto-generated-name.my-svc.my-namespace.svc.cluster-domain.example.

파드

A/AAAA 레코드

일반적으로 파드에는 다음과 같은 DNS 주소를 갖는다.

pod-ip-address.my-namespace.pod.cluster-domain.example.

예를 들어, default 네임스페이스의 파드에 IP 주소 172.17.0.3이 있고, 클러스터의 도메인 이름이 cluster.local 이면, 파드는 다음과 같은 DNS 주소를 갖는다.

172-17-0-3.default.pod.cluster.local.

서비스에 의해 노출된 디플로이먼트(Deployment)나 데몬셋(DaemonSet)에 의해 생성된 모든 파드는 다음과 같은 DNS 주소를 갖는다.

pod-ip-address.deployment-name.my-namespace.svc.cluster-domain.example.

파드의 hostname 및 subdomain 필드

파드가 생성되면 hostname은 해당 파드의 metadata.name 값이 된다.

파드 스펙(Pod spec)에는 선택적 필드인 hostname이 있다. 이 필드는 파드의 호스트네임을 지정할 수 있다. hostname 필드가 지정되면, 파드의 이름보다 파드의 호스트네임이 우선시된다. 예를 들어 hostname 필드가 "my-host"로 설정된 파드는 호스트네임이 "my-host"로 설정된다.

또한, 파드 스펙에는 선택적 필드인 subdomain이 있다. 이 필드는 서브도메인을 지정할 수 있다. 예를 들어 "my-namespace" 네임스페이스에서, hostname 필드가 "foo"로 설정되고, subdomain 필드가 "bar"로 설정된 파드는 전체 주소 도메인 네임(FQDN)을 가지게 된다. "foo.bar.my-namespace.svc.cluster-domain.example".

예시:

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # 사실 포트는 필요하지 않다.
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

파드와 동일한 네임스페이스 내에 같은 서브도메인 이름을 가진 헤드리스 서비스가 있다면, 클러스터의 DNS 서버는 파드의 전체 주소 호스트네임(fully qualified hostname)인 A 또는 AAAA 레코드를 반환한다. 예를 들어 호스트네임이 "busybox-1"이고, 서브도메인이 "default-subdomain"이고, 같은 네임스페이스 내 헤드리스 서비스의 이름이 "default-subdomain"이면, 파드는 다음과 같이 자기 자신의 FQDN을 얻게 된다. "busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example". DNS는 위 FQDN에 대해 파드의 IP를 가리키는 A 또는 AAAA 레코드를 제공한다. "busybox1"와 "busybox2" 파드 모두 각 파드를 구분 가능한 A 또는 AAAA 레코드를 가지고 있다.

엔드포인트 오브젝트는 hostname 필드를 임의의 엔드포인트 IP 주소로 지정할 수 있다.

파드의 setHostnameAsFQDN 필드

FEATURE STATE: Kubernetes v1.22 [stable]

파드가 전체 주소 도메인 이름(FQDN)을 갖도록 구성된 경우, 해당 호스트네임은 짧은 호스트네임이다. 예를 들어, 전체 주소 도메인 이름이 busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example 인 파드가 있는 경우, 기본적으로 해당 파드 내부의 hostname 명령어는 busybox-1 을 반환하고 hostname --fqdn 명령은 FQDN을 반환한다.

파드 명세에서 setHostnameAsFQDN: true 를 설정하면, kubelet은 파드의 FQDN을 해당 파드 네임스페이스의 호스트네임에 기록한다. 이 경우, hostnamehostname --fqdn 은 모두 파드의 FQDN을 반환한다.

파드의 DNS 정책

DNS 정책은 파드별로 설정할 수 있다. 현재 쿠버네티스는 다음과 같은 파드별 DNS 정책을 지원한다. 이 정책들은 파드 스펙의 dnsPolicy 필드에서 지정할 수 있다.

  • "Default": 파드는 파드가 실행되고 있는 노드로부터 네임 해석 설정(the name resolution configuration)을 상속받는다. 자세한 내용은 관련 논의에서 확인할 수 있다.
  • "ClusterFirst": "www.kubernetes.io"와 같이 클러스터 도메인 suffix 구성과 일치하지 않는 DNS 쿼리는 노드에서 상속된 업스트림 네임서버로 전달된다. 클러스터 관리자는 추가 스텁-도메인(stub-domain)과 업스트림 DNS 서버를 구축할 수 있다. 그러한 경우 DNS 쿼리를 어떻게 처리하는지에 대한 자세한 내용은 관련 논의에서 확인할 수 있다.
  • "ClusterFirstWithHostNet": hostNetwork에서 running 상태인 파드의 경우 DNS 정책인 "ClusterFirstWithHostNet"을 명시적으로 설정해야 한다.
  • "None": 이 정책은 파드가 쿠버네티스 환경의 DNS 설정을 무시하도록 한다. 모든 DNS 설정은 파드 스펙 내에 dnsConfig필드를 사용하여 제공해야 한다. 아래 절인 파드의 DNS 설정에서 자세한 내용을 확인할 수 있다.

아래 예시는 hostNetwork필드가 true로 설정되어 있어서 DNS 정책이 "ClusterFirstWithHostNet"으로 설정된 파드를 보여준다.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

파드의 DNS 설정

FEATURE STATE: Kubernetes v1.14 [stable]

사용자들은 파드의 DNS 설정을 통해서 직접 파드의 DNS를 세팅할 수 있다.

dnsConfig 필드는 선택적이고, dnsPolicy 세팅과 함께 동작한다. 이때, 파드의 dnsPolicy의 값이 "None"으로 설정되어 있어야 dnsConfig 필드를 지정할 수 있다.

사용자는 dnsConfig 필드에서 다음과 같은 속성들을 지정할 수 있다.

  • nameservers: 파드의 DNS 서버가 사용할 IP 주소들의 목록이다. 파드의 dnsPolicy가 "None" 으로 설정된 경우에는 적어도 하나의 IP 주소가 포함되어야 하며, 그렇지 않으면 이 속성은 생략할 수 있다. nameservers에 나열된 서버는 지정된 DNS 정책을 통해 생성된 기본 네임 서버와 합쳐지며 중복되는 주소는 제거된다.
  • searches: 파드의 호스트네임을 찾기 위한 DNS 검색 도메인의 목록이다. 이 속성은 생략이 가능하며, 값을 지정한 경우 나열된 검색 도메인은 지정된 DNS 정책을 통해 생성된 기본 검색 도메인에 합쳐진다. 병합 시 중복되는 도메인은 제거되며, 쿠버네티스는 최대 6개의 검색 도메인을 허용하고 있다.
  • options: name 속성(필수)과 value 속성(선택)을 가질 수 있는 오브젝트들의 선택적 목록이다. 이 속성의 내용은 지정된 DNS 정책에서 생성된 옵션으로 병합된다. 이 속성의 내용은 지정된 DNS 정책을 통해 생성된 옵션으로 합쳐지며, 병합 시 중복되는 항목은 제거된다.

다음은 커스텀 DNS 세팅을 한 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

위에서 파드가 생성되면, 컨테이너 test/etc/resolv.conf 파일에는 다음과 같은 내용이 추가된다.

nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

IPv6 셋업을 위해서 검색 경로와 네임 서버 셋업은 다음과 같아야 한다:

kubectl exec -it dns-example -- cat /etc/resolv.conf

출력은 다음과 같은 형식일 것이다.

nameserver fd00:79:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

확장된 DNS 환경 설정

FEATURE STATE: Kubernetes 1.22 [alpha]

쿠버네티스는 파드의 DNS 환경 설정을 위해 기본적으로 최대 6개의 탐색 도메인과 최대 256자의 탐색 도메인 목록을 허용한다.

kube-apiserver와 kubelet에 ExpandedDNSConfig 기능 게이트가 활성화되어 있으면, 쿠버네티스는 최대 32개의 탐색 도메인과 최대 2048자의 탐색 도메인 목록을 허용한다.

기능 가용성

파드 DNS 환경 설정 기능과 DNS 정책 "None" 기능의 쿠버네티스 버전별 가용성은 다음과 같다.

쿠버네티스 버전 기능 지원
1.14 안정
1.10 베타 (기본값으로 켜져 있음)
1.9 알파

다음 내용

DNS 구성 관리에 대한 지침은 DNS 서비스 구성에서 확인할 수 있다.

3.5.4 - 서비스와 애플리케이션 연결하기

컨테이너 연결을 위한 쿠버네티스 모델

지속적으로 실행중이고, 복제된 애플리케이션을 가지고 있다면 네트워크에 노출할 수 있다. 쿠버네티스의 네트워킹 접근 방식을 논의하기 전에, 도커와 함께 동작하는 "일반적인" 네트워킹 방법과 대조해볼 가치가 있다.

기본적으로 도커는 호스트-프라이빗 네트워킹을 사용하기에 컨테이너는 동일한 머신에 있는 경우에만 다른 컨테이너와 통신 할 수 있다. 도커 컨테이너가 노드를 통해 통신하려면 머신 포트에 IP 주소가 할당되어야 컨테이너에 전달되거나 프록시된다. 이것은 컨테이너가 사용하는 포트를 매우 신중하게 조정하거나 포트를 동적으로 할당해야 한다는 의미이다.

컨테이너를 제공하는 여러 개발자 또는 팀에서 포트를 조정하는 것은 규모면에서 매우 어려우며, 사용자가 제어할 수 없는 클러스터 수준의 문제에 노출된다. 쿠버네티스는 파드가 배치된 호스트와는 무관하게 다른 파드와 통신할 수 있다고 가정한다. 쿠버네티스는 모든 파드에게 자체 클러스터-프라이빗 IP 주소를 제공하기 때문에 파드간에 명시적으로 링크를 만들거나 컨테이너 포트를 호스트 포트에 매핑 할 필요가 없다. 이것은 파드 내의 컨테이너는 모두 로컬호스트에서 서로의 포트에 도달할 수 있으며 클러스터의 모든 파드는 NAT 없이 서로를 볼 수 있다는 의미이다. 이 문서의 나머지 부분에서는 이러한 네트워킹 모델에서 신뢰할 수 있는 서비스를 실행하는 방법에 대해 자세히 설명할 것이다.

이 가이드는 간단한 nginx 서버를 사용해서 개념증명을 보여준다.

파드를 클러스터에 노출하기

이 작업은 이전 예시에서 수행해 보았지만, 네트워킹 관점을 중점에 두고 다시 한번 수행해 보자. nginx 파드를 생성하고, 해당 파드에 컨테이너 포트 사양이 있는 것을 참고한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

이렇게 하면 클러스터의 모든 노드에서 접근할 수 있다. 파드를 실행 중인 노드를 확인한다.

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

파드의 IP를 확인한다.

kubectl get pods -l run=my-nginx -o yaml | grep podIP
    podIP: 10.244.3.4
    podIP: 10.244.2.5

클러스터의 모든 노드로 ssh 접속하고 두 IP로 curl을 할수 있어야 한다. 컨테이너는 노드의 포트 80을 사용하지 않으며 , 트래픽을 파드로 라우팅하는 특별한 NAT 규칙도 없다는 것을 참고한다. 이것은 동일한 containerPort를 사용해서 동일한 노드에서 여러 nginx 파드를 실행하고 IP를 사용해서 클러스터의 다른 파드나 노드에서 접근할 수 있다는 의미이다. 도커와 마찬가지로 포트는 여전히 호스트 노드의 인터페이스에 게시될 수 있지만, 네트워킹 모델로 인해 포트의 필요성이 크게 줄어든다.

만약 궁금하다면 우리가 이것을 달성하는 방법을 자세히 읽어본다.

서비스 생성하기

평평하고 넓은 클러스터 전체의 주소 공간에서 nginx를 실행하는 파드가 있다고 가정하자. 이론적으로는 이러한 파드와 직접 대화할 수 있지만, 노드가 죽으면 어떻게 되는가? 파드가 함께 죽으면 디플로이먼트에서 다른 IP를 가진 새로운 파드를 생성한다. 이 문제를 서비스가 해결한다.

쿠버네티스 서비스는 클러스터 어딘가에서 실행되는 논리적인 파드 집합을 정의하고 추상화함으로써 모두 동일한 기능을 제공한다. 생성시 각 서비스에는 고유한 IP 주소(clusterIP라고도 한다)가 할당된다. 이 주소는 서비스의 수명과 연관되어 있으며, 서비스가 활성화 되어 있는 동안에는 변경되지 않는다. 파드는 서비스와 통신하도록 구성할 수 있으며, 서비스와의 통신은 서비스의 맴버 중 일부 파드에 자동적으로 로드-밸런싱 된다.

kubectl expose 를 사용해서 2개의 nginx 레플리카에 대한 서비스를 생성할 수 있다.

kubectl expose deployment/my-nginx
service/my-nginx exposed

이것은 다음 yaml 파일을 kubectl apply -f 로 실행한 것과 동일하다.

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

이 사양은 run: my-nginx 레이블이 부착된 모든 파드에 TCP 포트 80을 대상으로 하는 서비스를 만들고 추상화된 서비스 포트에 노출시킨다 (targetPort 는 컨테이너가 트래픽을 수신하는 포트, port 는 추상화된 서비스 포트로 다른 파드들이 서비스에 접속하기위해 사용하는 모든 포트일 수 있다). 서비스의 API 오브젝트를 보고 서비스 정의에서 지원되는 필드 목록을 확인한다. 서비스를 확인한다.

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

앞에서 언급한 바와 같이, 서비스는 파드 그룹에 의해 지원된다. 이 파드들은 endpoints 를 통해 노출된다. 서비스 셀렉터는 지속적으로 평가되고 결과는 my-nginx 이름의 엔드포인트 오브젝트에 POST된다. 파드가 죽으면 자동적으로 엔드포인트에서 제거되며 서비스 셀렉터와 일치하는 새 파드는 자동적으로 엔드포인트에 추가된다. 엔드포인트를 확인하고 IP가 첫 번째 단계에서 생성된 파드와 동일하다는 점을 참고한다.

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP:                  10.0.162.149
Port:                <unset> 80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get ep my-nginx
NAME       ENDPOINTS                     AGE
my-nginx   10.244.2.5:80,10.244.3.4:80   1m

이제 클러스터의 모든 노드에서 <CLUSTER-IP>:<PORT> 로 nginx 서비스를 curl을 할 수 있을 것이다. 서비스 IP는 완전히 가상이므로 외부에서는 절대로 연결되지 않음에 참고한다. 만약 이것이 어떻게 작동하는지 궁금하다면 서비스 프록시에 대해 더 읽어본다.

서비스에 접근하기

쿠버네티스는 서비스를 찾는 두 가지 기본 모드인 환경 변수와 DNS를 지원한다. 전자는 기본적으로 작동하지만 후자는 CoreDNS 클러스터 애드온이 필요하다.

환경 변수들

파드가 노드에서 실행될 때 kubelet은 각기 활성화된 서비스에 대해 일련의 환경 변수 집합을 추가한다. 이것은 순서 문제를 야기한다. 이유를 확인하려면 실행 중인 nginx 파드 환경을 점검해야 한다(실제 사용자의 파드 이름은 다를 것이다).

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

서비스에 대한 언급이 없다는 것에 참고해야 한다. 이것은 서비스 이전에 레플리카를 생성했기 때문이다. 이 작업을 수행할 때 또 다른 단점은 스케줄러가 두 파드를 모두 동일한 머신에 배치할 수도 있다는 것이며, 이로 인해 전체 서비스가 중단될 수 있다. 두개의 파드를 죽이고 디플로이먼트가 파드를 재생성하기를 기다리는 것으로 이를 정상화 할 수 있다. 이번에는 서비스가 레플리카들 에 존재한다. 이렇게 하면 올바른 환경 변수뿐만 아니라 파드의 스케줄러-수준의 서비스 분배(모든 노드에 동일한 용량이 제공되는 경우)가 된다.

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;

kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

파드가 죽고 재생성되었기 때문에 다른 이름을 가지는 것을 알 수 있다.

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

쿠버네티스는 DNS 클러스터 애드온 서비스를 제공하며 dns 이름을 다른 서비스에 자동으로 할당한다. 다음 명령어로 이것이 클러스터에서 실행 중인지 확인할 수 있다.

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

이 섹션의 나머지 부분에서는 수명이 긴 IP의 서비스(my-nginx)와 이 IP 에 이름을 할당한 DNS 서버가 있다고 가정한다. 여기서는 CoreDNS 클러스터 애드온(애플리케이션 이름 kube-dns)을 사용하므로, 표준 방법(예: gethostbyname())을 사용해서 클러스터의 모든 파드에서 서비스와 통신할 수 있다. 만약 CoreDNS가 실행 중이 아니라면 CoreDNS README 또는 CoreDNS 설치를 참조해서 활성화 할 수 있다. 이것을 테스트하기 위해 다른 curl 애플리케이션을 실행한다.

kubectl run curl --image=radial/busyboxplus:curl -i --tty
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

이제, nslookup my-nginx 를 입력하고 실행한다:

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

서비스 보안

지금까지는 클러스터 내부에서만 ngnix 서버에 엑세스 해왔다. 서비스를 인터넷에 공개하기 전에 통신 채널이 안전한지 확인해야 한다. 이를 위해선 다음이 필요하다.

  • https에 대한 자체 서명한 인증서 (신원 인증서를 가지고 있지 않은 경우)
  • 인증서를 사용하도록 구성된 nginx 서버
  • 파드에 접근할 수 있는 인증서를 만드는 시크릿

nginx https 예제에서 이 모든 것을 얻을 수 있다. 이를 위해서는 도구를 설치해야 한다. 만약 설치하지 않으려면 나중에 수동으로 단계를 수행한다. 한마디로:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

그리고 또한 컨피그맵:

kubectl create configmap nginxconfigmap --from-file=default.conf
configmap/nginxconfigmap created
kubectl get configmaps
NAME             DATA   AGE
nginxconfigmap   1      114s

다음은 make를 실행하는데 문제가 있는 경우에 수행해야 하는 수동 단계이다(예시로 windows).

# Create a public private key pair
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
# Convert the keys to base64 encoding
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

이전 명령의 출력을 사용해서 다음과 같이 yaml 파일을 생성한다. base64로 인코딩된 값은 모두 한 줄에 있어야 한다.

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
type: kubernetes.io/tls
data:
  tls.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
  tls.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

이제 파일을 사용해서 시크릿을 생성한다.

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

이제 nginx 레플리카를 수정하여 암호화된 인증서를 사용한 https 서버와 서비스를 실행하고, 두 포트(80과 443)를 노출한다.

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      - name: configmap-volume
        configMap:
          name: nginxconfigmap
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
        - mountPath: /etc/nginx/conf.d
          name: configmap-volume

nginx-secure-app의 매니페스트에 대한 주목할만한 점:

  • 이것은 동일한 파일에 디플로이먼트와 서비스의 사양을 모두 포함하고 있다.
  • nginx 서버 는 포트 80에서 HTTP 트래픽을 443에서 HTTPS 트래픽 서비스를 제공하고, nginx 서비스는 두 포트를 모두 노출한다.
  • 각 컨테이너는 /etc/nginx/ssl 에 마운트된 볼륨을 통해 키에 접근할 수 있다. 이것은 nginx 서버가 시작되기 전에 설정된 것이다.
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

이 시점에서 모든 노드에서 nginx 서버에 연결할 수 있다.

kubectl get pods -o yaml | grep -i podip
    podIP: 10.244.3.5
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

마지막 단계에서 curl에 -k 파라미터를 제공한 방법에 참고한다. 이는 인증서 생성시 nginx를 실행하는 파드에 대해 아무것도 모르기 때문에 curl에 CName 불일치를 무시하도록 지시해야하기 때문이다. 서비스를 생성해서 인증서에 사용된 CName을 서비스 조회시 파드에서 사용된 실제 DNS 이름과 연결했다. 파드에서 이것을 테스트 해보자(단순히 동일한 시크릿이 재사용되고 있으며, 파드는 서비스에 접근하기위해 nginx.crt만 필요하다).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...

서비스 노출하기

애플리케이션의 일부인 경우 원한다면 외부 IP 주소에 서비스를 노출할 수 있다. 쿠버네티스는 이를 수행하는 2가지 방법인 NodePorts와 LoadBalancers를지원한다. 마지막 섹션에서 생성된 서비스는 이미 NodePort 를 사용했기에 노드에 공용 IP가 있는경우 nginx HTTPS 레플리카가 인터넷 트래픽을 처리할 준비가 되어 있다.

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

이제 클라우드 로드 밸런서를 사용하도록 서비스를 재생성한다. my-nginx 서비스의 TypeNodePort 에서 LoadBalancer 로 변경한다.

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149   xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

EXTERNAL-IP 의 IP 주소는 공용 인터넷에서 이용할 수 있는 주소이다. CLUSTER-IP 는 클러스터/프라이빗 클라우드 네트워크 내에서만 사용할 수 있다.

AWS에서는 LoadBalancer 유형은 IP가 아닌 (긴)호스트네임을 사용하는 ELB를 생성한다는 점을 참고한다. 이것은 일반적인 kubectl get svc 의 출력에 맞추기에는 매우 길기 때문에 실제로 이를 보려면 kubectl describe service my-nginx 를 수행해야 한다. 다음과 같은 것을 보게 된다.

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

다음 내용

3.5.5 - 인그레스 컨트롤러

인그레스 리소스가 작동하려면, 클러스터는 실행 중인 인그레스 컨트롤러가 반드시 필요하다.

kube-controller-manager 바이너리의 일부로 실행되는 컨트롤러의 다른 타입과 달리 인그레스 컨트롤러는 클러스터와 함께 자동으로 실행되지 않는다. 클러스터에 가장 적합한 인그레스 컨트롤러 구현을 선택하는데 이 페이지를 사용한다.

프로젝트로서 쿠버네티스는 AWS, GCEnginx 인그레스 컨트롤러를 지원하고 유지한다.

추가 컨트롤러

여러 인그레스 컨트롤러 사용

하나의 클러스터 내에 여러 개의 인그레스 컨트롤러를 배포할 수 있다. 인그레스를 생성할 때, 클러스터 내에 둘 이상의 인그레스 컨트롤러가 존재하는 경우 어떤 인그레스 컨트롤러를 사용해야 하는지 표시해주는 적절한 ingress.class 어노테이션을 각각의 인그레스에 달아야 한다.

만약 클래스를 정의하지 않으면, 클라우드 제공자는 기본 인그레스 컨트롤러를 사용할 수 있다.

이상적으로는 모든 인그레스 컨트롤러가 이 사양을 충족해야 하지만, 다양한 인그레스 컨트롤러는 약간 다르게 작동한다.

다음 내용

3.5.6 - 인그레스(Ingress)

FEATURE STATE: Kubernetes v1.19 [stable]

클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리함.

인그레스는 부하 분산, SSL 종료, 명칭 기반의 가상 호스팅을 제공할 수 있다.

용어

이 가이드는 용어의 명확성을 위해 다음과 같이 정의한다.

  • 노드(Node): 클러스터의 일부이며, 쿠버네티스에 속한 워커 머신.
  • 클러스터(Cluster): 쿠버네티스에서 관리되는 컨테이너화 된 애플리케이션을 실행하는 노드 집합. 이 예시와 대부분의 일반적인 쿠버네티스 배포에서 클러스터에 속한 노드는 퍼블릭 인터넷의 일부가 아니다.
  • 에지 라우터(Edge router): 클러스터에 방화벽 정책을 적용하는 라우터. 이것은 클라우드 공급자 또는 물리적 하드웨어의 일부에서 관리하는 게이트웨이일 수 있다.
  • 클러스터 네트워크(Cluster network): 쿠버네티스 네트워킹 모델에 따라 클러스터 내부에서 통신을 용이하게 하는 논리적 또는 물리적 링크 집합.
  • 서비스: 레이블 셀렉터를 사용해서 파드 집합을 식별하는 쿠버네티스 서비스. 달리 언급하지 않으면 서비스는 클러스터 네트워크 내에서만 라우팅 가능한 가상 IP를 가지고 있다고 가정한다.

인그레스란?

인그레스는 클러스터 외부에서 클러스터 내부 서비스로 HTTP와 HTTPS 경로를 노출한다. 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 의해 컨트롤된다.

다음은 인그레스가 모든 트래픽을 하나의 서비스로 보내는 간단한 예시이다.

graph LR; client([클라이언트])-. 인그레스-매니지드
로드 밸런서 .->ingress[인그레스]; ingress-->|라우팅 규칙|service[서비스]; subgraph 클러스터 ingress; service-->pod1[파드]; service-->pod2[파드]; end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service,pod1,pod2 k8s; class client plain; class cluster cluster;

인그레스는 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL / TLS 종료 그리고 이름-기반의 가상 호스팅을 제공하도록 구성할 수 있다. 인그레스 컨트롤러는 일반적으로 로드 밸런서를 사용해서 인그레스를 수행할 책임이 있으며, 트래픽을 처리하는데 도움이 되도록 에지 라우터 또는 추가 프런트 엔드를 구성할 수도 있다.

인그레스는 임의의 포트 또는 프로토콜을 노출시키지 않는다. HTTP와 HTTPS 이외의 서비스를 인터넷에 노출하려면 보통 Service.Type=NodePort 또는 Service.Type=LoadBalancer 유형의 서비스를 사용한다.

전제 조건들

인그레스 컨트롤러가 있어야 인그레스를 충족할 수 있다. 인그레스 리소스만 생성한다면 효과가 없다.

ingress-nginx와 같은 인그레스 컨트롤러를 배포해야 할 수도 있다. 여러 인그레스 컨트롤러 중에서 선택할 수도 있다.

이상적으로, 모든 인그레스 컨트롤러는 참조 사양이 맞아야 한다. 실제로, 다양한 인그레스 컨트롤러는 조금 다르게 작동한다.

인그레스 리소스

최소한의 인그레스 리소스 예제:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

다른 모든 쿠버네티스 리소스와 마찬가지로 인그레스에는 apiVersion, kind, 그리고 metadata 필드가 필요하다. 인그레스 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다. 설정 파일의 작성에 대한 일반적인 내용은 애플리케이션 배포하기, 컨테이너 구성하기, 리소스 관리하기를 참조한다. 인그레스는 종종 어노테이션을 이용해서 인그레스 컨트롤러에 따라 몇 가지 옵션을 구성하는데, 그 예시는 재작성-타겟 어노테이션이다. 다른 인그레스 컨트롤러는 다른 어노테이션을 지원한다. 지원되는 어노테이션을 확인하려면 선택한 인그레스 컨트롤러의 설명서를 검토한다.

인그레스 사양 에는 로드 밸런서 또는 프록시 서버를 구성하는데 필요한 모든 정보가 있다. 가장 중요한 것은, 들어오는 요청과 일치하는 규칙 목록을 포함하는 것이다. 인그레스 리소스는 HTTP(S) 트래픽을 지시하는 규칙만 지원한다.

인그레스 규칙

각 HTTP 규칙에는 다음의 정보가 포함된다.

  • 선택적 호스트. 이 예시에서는, 호스트가 지정되지 않기에 지정된 IP 주소를 통해 모든 인바운드 HTTP 트래픽에 규칙이 적용 된다. 만약 호스트가 제공되면(예, foo.bar.com), 규칙이 해당 호스트에 적용된다.
  • 경로 목록 (예, /testpath)에는 각각 service.nameservice.port.name 또는 service.port.number 가 정의되어 있는 관련 백엔드를 가지고 있다. 로드 밸런서가 트래픽을 참조된 서비스로 보내기 전에 호스트와 경로가 모두 수신 요청의 내용과 일치해야 한다.
  • 백엔드는 서비스 문서 또는 사용자 정의 리소스 백엔드에 설명된 바와 같이 서비스와 포트 이름의 조합이다. 호스트와 규칙 경로가 일치하는 인그레스에 대한 HTTP(와 HTTPS) 요청은 백엔드 목록으로 전송된다.

defaultBackend 는 종종 사양의 경로와 일치하지 않는 서비스에 대한 모든 요청을 처리하도록 인그레스 컨트롤러에 구성되는 경우가 많다.

DefaultBackend

규칙이 없는 인그레스는 모든 트래픽을 단일 기본 백엔드로 전송한다. defaultBackend 는 일반적으로 인그레스 컨트롤러의 구성 옵션이며, 인그레스 리소스에 지정되어 있지 않다.

만약 인그레스 오브젝트의 HTTP 요청과 일치하는 호스트 또는 경로가 없으면, 트래픽은 기본 백엔드로 라우팅 된다.

리소스 백엔드

Resource 백엔드는 인그레스 오브젝트와 동일한 네임스페이스 내에 있는 다른 쿠버네티스 리소스에 대한 ObjectRef이다. Resource 는 서비스와 상호 배타적인 설정이며, 둘 다 지정하면 유효성 검사에 실패한다. Resource 백엔드의 일반적인 용도는 정적 자산이 있는 오브젝트 스토리지 백엔드로 데이터를 수신하는 것이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

위의 인그레스를 생성한 후, 다음의 명령으로 확인할 수 있다.

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

경로 유형

인그레스의 각 경로에는 해당 경로 유형이 있어야 한다. 명시적 pathType 을 포함하지 않는 경로는 유효성 검사에 실패한다. 지원되는 경로 유형은 세 가지이다.

  • ImplementationSpecific: 이 경로 유형의 일치 여부는 IngressClass에 따라 달라진다. 이를 구현할 때 별도 pathType 으로 처리하거나, Prefix 또는 Exact 경로 유형과 같이 동일하게 처리할 수 있다.

  • Exact: URL 경로의 대소문자를 엄격하게 일치시킨다.

  • Prefix: URL 경로의 접두사를 / 를 기준으로 분리한 값과 일치시킨다. 일치는 대소문자를 구분하고, 요소별로 경로 요소에 대해 수행한다. 모든 p 가 요청 경로의 요소별 접두사가 p 인 경우 요청은 p 경로에 일치한다.

예제

종류 경로 요청 경로 일치 여부
Prefix / (모든 경로)
Exact /foo /foo
Exact /foo /bar 아니오
Exact /foo /foo/ 아니오
Exact /foo/ /foo 아니오
Prefix /foo /foo, /foo/
Prefix /foo/ /foo, /foo/
Prefix /aaa/bb /aaa/bbb 아니오
Prefix /aaa/bbb /aaa/bbb
Prefix /aaa/bbb/ /aaa/bbb 예, 마지막 슬래시 무시함
Prefix /aaa/bbb /aaa/bbb/ 예, 마지막 슬래시 일치함
Prefix /aaa/bbb /aaa/bbb/ccc 예, 하위 경로 일치함
Prefix /aaa/bbb /aaa/bbbxyz 아니오, 문자열 접두사 일치하지 않음
Prefix /, /aaa /aaa/ccc 예, /aaa 접두사 일치함
Prefix /, /aaa, /aaa/bbb /aaa/bbb 예, /aaa/bbb 접두사 일치함
Prefix /, /aaa, /aaa/bbb /ccc 예, / 접두사 일치함
Prefix /aaa /ccc 아니오, 기본 백엔드 사용함
Mixed /foo (Prefix), /foo (Exact) /foo 예, Exact 선호함

다중 일치

경우에 따라 인그레스의 여러 경로가 요청과 일치할 수 있다. 이 경우 가장 긴 일치하는 경로가 우선하게 된다. 두 개의 경로가 여전히 동일하게 일치하는 경우 접두사(prefix) 경로 유형보다 정확한(exact) 경로 유형을 가진 경로가 사용 된다.

호스트네임 와일드카드

호스트는 정확한 일치(예: "foo.bar.com") 또는 와일드카드(예: "* .foo.com")일 수 있다. 정확한 일치를 위해서는 HTTP host 헤더가 host 필드와 일치해야 한다. 와일드카드 일치를 위해서는 HTTP host 헤더가 와일드카드 규칙의 접미사와 동일해야 한다.

호스트 호스트 헤더 일치 여부
*.foo.com bar.foo.com 공유 접미사를 기반으로 일치함
*.foo.com baz.bar.foo.com 일치하지 않음, 와일드카드는 단일 DNS 레이블만 포함함
*.foo.com foo.com 일치하지 않음, 와일드카드는 단일 DNS 레이블만 포함함
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

인그레스 클래스

인그레스는 서로 다른 컨트롤러에 의해 구현될 수 있으며, 종종 다른 구성으로 구현될 수 있다. 각 인그레스에서는 클래스를 구현해야하는 컨트롤러 이름을 포함하여 추가 구성이 포함된 IngressClass 리소스에 대한 참조 클래스를 지정해야 한다.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

IngressClass 리소스에는 선택적인 파라미터 필드가 있다. 이 클래스에 대한 추가 구현 별 구성을 참조하는데 사용할 수 있다.

네임스페이스 범위의 파라미터

FEATURE STATE: Kubernetes v1.22 [beta]

Parameters 필드에는 인그레스 클래스 구성을 위해 네임스페이스 별 리소스를 참조하는 데 사용할 수 있는 scopenamespace 필드가 있다. Scope 필드의 기본값은 Cluster 이다. 즉, 기본값은 클러스터 범위의 리소스이다. ScopeNamespace 로 설정하고 Namespace 필드를 설정하면 특정 네임스페이스의 파라미터 리소스를 참조한다.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb
    namespace: external-configuration
    scope: Namespace

사용중단(Deprecated) 어노테이션

쿠버네티스 1.18에 IngressClass 리소스 및 ingressClassName 필드가 추가되기 전에 인그레스 클래스는 인그레스에서 kubernetes.io/ingress.class 어노테이션으로 지정되었다. 이 어노테이션은 공식적으로 정의된 것은 아니지만, 인그레스 컨트롤러에서 널리 지원되었었다.

인그레스의 최신 ingressClassName 필드는 해당 어노테이션을 대체하지만, 직접적으로 해당하는 것은 아니다. 어노테이션은 일반적으로 인그레스를 구현해야 하는 인그레스 컨트롤러의 이름을 참조하는 데 사용되었지만, 이 필드는 인그레스 컨트롤러의 이름을 포함하는 추가 인그레스 구성이 포함된 인그레스 클래스 리소스에 대한 참조이다.

기본 IngressClass

특정 IngressClass를 클러스터의 기본 값으로 표시할 수 있다. IngressClass 리소스에서 ingressclass.kubernetes.io/is-default-classtrue 로 설정하면 ingressClassName 필드가 지정되지 않은 새 인그레스에게 기본 IngressClass가 할당된다.

인그레스 유형들

단일 서비스로 지원되는 인그레스

단일 서비스를 노출할 수 있는 기존 쿠버네티스 개념이 있다 (대안을 본다). 인그레스에 규칙 없이 기본 백엔드 를 지정해서 이를 수행할 수 있다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

만약 kubectl apply -f 를 사용해서 생성한다면 추가한 인그레스의 상태를 볼 수 있어야 한다.

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

여기서 203.0.113.123 는 인그레스 컨트롤러가 인그레스를 충족시키기 위해 할당한 IP 이다.

간단한 팬아웃(fanout)

팬아웃 구성은 HTTP URI에서 요청된 것을 기반으로 단일 IP 주소에서 1개 이상의 서비스로 트래픽을 라우팅 한다. 인그레스를 사용하면 로드 밸런서의 수를 최소로 유지할 수 있다. 예를 들어 다음과 같은 설정을 한다.

graph LR; client([클라이언트])-. 인그레스-매니지드
로드 밸런서 .->ingress[인그레스, 178.91.123.132]; ingress-->|/foo|service1[서비스 service1:4200]; ingress-->|/bar|service2[서비스 service2:8080]; subgraph 클러스터 ingress; service1-->pod1[파드]; service1-->pod2[파드]; service2-->pod3[파드]; service2-->pod4[파드]; end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service1,service2,pod1,pod2,pod3,pod4 k8s; class client plain; class cluster cluster;

다음과 같은 인그레스가 필요하다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

kubectl apply -f 를 사용해서 인그레스를 생성 할 때 다음과 같다.

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

인그레스 컨트롤러는 서비스(service1, service2)가 존재하는 한, 인그레스를 만족시키는 특정한 로드 밸런서를 프로비저닝한다. 이렇게 하면, 주소 필드에서 로드 밸런서의 주소를 볼 수 있다.

이름 기반의 가상 호스팅

이름 기반의 가상 호스트는 동일한 IP 주소에서 여러 호스트 이름으로 HTTP 트래픽을 라우팅하는 것을 지원한다.

graph LR; client([클라이언트])-. 인그레스-매니지드
로드 밸런서 .->ingress[인그레스, 178.91.123.132]; ingress-->|호스트: foo.bar.com|service1[서비스 service1:80]; ingress-->|호스트: bar.foo.com|service2[서비스 service2:80]; subgraph 클러스터 ingress; service1-->pod1[파드]; service1-->pod2[파드]; service2-->pod3[파드]; service2-->pod4[파드]; end classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service1,service2,pod1,pod2,pod3,pod4 k8s; class client plain; class cluster cluster;

다음 인그레스는 호스트 헤더에 기반한 요청을 라우팅 하기 위해 뒷단의 로드 밸런서를 알려준다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

만약 규칙에 정의된 호스트 없이 인그레스 리소스를 생성하는 경우, 이름 기반 가상 호스트가 없어도 인그레스 컨트롤러의 IP 주소에 대한 웹 트래픽을 일치 시킬 수 있다.

예를 들어, 다음 인그레스는 first.bar.com에 요청된 트래픽을 service1로, second.bar.comservice2로, 호스트 이름이 정의되지 않은(즉, 요청 헤더가 표시 되지 않는) IP 주소로의 모든 트래픽은 service3로 라우팅 한다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

TLS 개인 키 및 인증서가 포함된 시크릿(Secret)을 지정해서 인그레스를 보호할 수 있다. 인그레스 리소스는 단일 TLS 포트인 443만 지원하고 인그레스 지점에서 TLS 종료를 가정한다(서비스 및 해당 파드에 대한 트래픽은 일반 텍스트임). 인그레스의 TLS 구성 섹션에서 다른 호스트를 지정하면, SNI TLS 확장을 통해 지정된 호스트이름에 따라 동일한 포트에서 멀티플렉싱 된다(인그레스 컨트롤러가 SNI를 지원하는 경우). TLS secret에는 tls.crttls.key 라는 이름의 키가 있어야 하고, 여기에는 TLS에 사용할 인증서와 개인 키가 있다. 예를 들어 다음과 같다.

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

인그레스에서 시크릿을 참조하면 인그레스 컨트롤러가 TLS를 사용하여 클라이언트에서 로드 밸런서로 채널을 보호하도록 지시한다. 생성한 TLS 시크릿이 https-example.foo.com 의 정규화 된 도메인 이름(FQDN)이라고 하는 일반 이름(CN)을 포함하는 인증서에서 온 것인지 확인해야 한다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

로드 밸런싱

인그레스 컨트롤러는 로드 밸런싱 알고리즘, 백엔드 가중치 구성표 등 모든 인그레스에 적용되는 일부 로드 밸런싱 정책 설정으로 부트스트랩된다. 보다 진보된 로드 밸런싱 개념 (예: 지속적인 세션, 동적 가중치)은 아직 인그레스를 통해 노출되지 않는다. 대신 서비스에 사용되는 로드 밸런서를 통해 이러한 기능을 얻을 수 있다.

또한, 헬스 체크를 인그레스를 통해 직접 노출되지 않더라도, 쿠버네티스에는 준비 상태 프로브와 같은 동일한 최종 결과를 얻을 수 있는 병렬 개념이 있다는 점도 주목할 가치가 있다. 컨트롤러 별 설명서를 검토하여 헬스 체크를 처리하는 방법을 확인한다(예: nginx, 또는 GCE).

인그레스 업데이트

기존 인그레스를 업데이트해서 새 호스트를 추가하려면, 리소스를 편집해서 호스트를 업데이트 할 수 있다.

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

YAML 형식의 기존 구성이 있는 편집기가 나타난다. 새 호스트를 포함하도록 수정한다.

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

변경사항을 저장한 후, kubectl은 API 서버의 리소스를 업데이트하며, 인그레스 컨트롤러에게도 로드 밸런서를 재구성하도록 지시한다.

이것을 확인한다.

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

수정된 인그레스 YAML 파일을 kubectl replace -f 를 호출해서 동일한 결과를 얻을 수 있다.

가용성 영역에 전체에서의 실패

장애 도메인에 트래픽을 분산시키는 기술은 클라우드 공급자마다 다르다. 자세한 내용은 인그레스 컨트롤러 설명서를 확인한다.

대안

사용자는 인그레스 리소스를 직접적으로 포함하지 않는 여러가지 방법으로 서비스를 노출할 수 있다.

다음 내용

3.5.7 - 서비스 내부 트래픽 정책

FEATURE STATE: Kubernetes v1.21 [alpha]

서비스 내부 트래픽 정책 을 사용하면 내부 트래픽 제한이 트래픽이 시작된 노드 내의 엔드포인트로만 내부 트래픽을 라우팅하도록 한다. 여기서 "내부" 트래픽은 현재 클러스터의 파드로부터 시작된 트래픽을 지칭한다. 이를 통해 비용을 절감하고 성능을 개선할 수 있다.

서비스 내부 트래픽 정책 사용

기능 게이트에서 ServiceInternalTrafficPolicy를 활성화한 후에 서비스.spec.internalTrafficPolicyLocal로 설정하여 내부 전용 트래픽 정책을 활성화 할 수 있다. 이것은 kube-proxy가 클러스터 내부 트래픽을 위해 노드 내부 엔드포인트로만 사용하도록 한다.

다음 예제는 서비스의 .spec.internalTrafficPolicyLocal로 설정하는 것을 보여 준다:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

작동 방식

kube-proxy는 spec.internalTrafficPolicy 의 설정에 따라서 라우팅되는 엔드포인트를 필터링한다. 이것을 Local로 설정하면, 노드 내부 엔드포인트만 고려한다. 이 설정이 Cluster이거나 누락되었다면 모든 엔드포인트를 고려한다. 기능 게이트ServiceInternalTrafficPolicy를 활성화한다면, spec.internalTrafficPolicy는 기본값 "Cluster"로 설정된다.

제약조건

  • 같은 서비스에서 externalTrafficPolicyLocal로 설정된 경우 서비스 내부 트래픽 정책이 사용되지 않는다. 클러스터에서 동일하지 않은 다른 서비스에서 이 두 가지 기능은 동시에 사용할 수 있다.

다음 내용

3.5.8 - 엔드포인트슬라이스

FEATURE STATE: Kubernetes v1.21 [stable]

엔드포인트슬라이스 는 쿠버네티스 클러스터 내의 네트워크 엔드포인트를 추적하는 간단한 방법을 제공한다. 이것은 엔드포인트를 더 확장하고, 확장 가능한 대안을 제안한다.

사용동기

엔드포인트 API는 쿠버네티스에서 네트워크 엔드포인트를 추적하는 간단하고 직접적인 방법을 제공한다. 불행하게도 쿠버네티스 클러스터와 서비스가 더 많은 수의 백엔드 파드로 더 많은 트래픽을 처리하고 전송하는 방향으로 성장함에 따라, 이 API의 한계가 더욱 눈에 띄게 되었다. 특히나, 많은 수의 네트워크 엔드포인트로 확장하는 것에 어려움이 있었다.

이후로 서비스에 대한 모든 네트워크 엔드포인트가 단일 엔드포인트 리소스에 저장되기 때문에 엔드포인트 리소스가 상당히 커질 수 있다. 이것은 쿠버네티스 구성요소 (특히 마스터 컨트롤 플레인)의 성능에 영향을 미쳤고 엔드포인트가 변경될 때 상당한 양의 네트워크 트래픽과 처리를 초래했다. 엔드포인트슬라이스는 이러한 문제를 완화하고 토폴로지 라우팅과 같은 추가 기능을 위한 확장 가능한 플랫폼을 제공한다.

엔드포인트슬라이스 리소스

쿠버네티스에서 엔드포인트슬라이스는 일련의 네트워크 엔드포인트에 대한 참조를 포함한다. 쿠버네티스 서비스에 셀렉터가 지정되면 컨트롤 플레인은 자동으로 엔드포인트슬라이스를 생성한다. 이 엔드포인트슬라이스는 서비스 셀렉터와 매치되는 모든 파드들을 포함하고 참조한다. 엔드포인트슬라이스는 프로토콜, 포트 번호 및 서비스 이름의 고유한 조합을 통해 네트워크 엔드포인트를 그룹화한다. 엔드포인트슬라이스 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

예를 들어, 여기에 example 쿠버네티스 서비스를 위한 엔드포인트슬라이스 리소스 샘플이 있다.

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    nodeName: node-1
    zone: us-west2-a

기본적으로, 컨트롤 플레인은 각각 100개 이하의 엔드포인트를 갖도록 엔드포인트슬라이스를 생성하고 관리한다. --max-endpoints-per-slice kube-controller-manager 플래그를 사용하여, 최대 1000개까지 구성할 수 있다.

엔드포인트슬라이스는 내부 트래픽을 라우트하는 방법에 대해 kube-proxy에 신뢰할 수 있는 소스로 역할을 할 수 있다. 이를 활성화 하면, 많은 수의 엔드포인트를 가지는 서비스에 대해 성능 향상을 제공해야 한다.

주소 유형

엔드포인트슬라이스는 다음 주소 유형을 지원한다.

  • IPv4
  • IPv6
  • FQDN (전체 주소 도메인 이름)

조건

엔드포인트슬라이스 API는 컨슈머에게 유용한 엔드포인트에 대한 조건을 저장한다. 조건은 준비, 제공종료 세 가지가 있다.

준비

ready는 파드의 Ready 조건에 매핑되는 조건이다. Ready 조건이 True로 설정된 실행 중인 파드는 이 엔드포인트슬라이스 조건도 true로 설정되어야 한다. 호환성의 이유로, 파드가 종료될 때 ready는 절대 true가 되면 안 된다. 컨슈머는 serving 조건을 참조하여 파드 종료 준비 상태(readiness)를 검사해야 한다. 이 규칙의 유일한 예외는 spec.publishNotReadyAddressestrue로 설정된 서비스이다. 이러한 서비스의 엔드 포인트는 항상 ready조건이 true로 설정된다.

제공(Serving)

FEATURE STATE: Kubernetes v1.20 [alpha]

serving은 종료 상태를 고려하지 않는다는 점을 제외하면 ready 조건과 동일하다. 엔드포인트슬라이스 API 컨슈머는 파드가 종료되는 동안 파드 준비 상태에 관심이 있다면 이 조건을 확인해야 한다.

종료(Terminating)

FEATURE STATE: Kubernetes v1.20 [alpha]

종료(Terminating)는 엔드포인트가 종료되는지 여부를 나타내는 조건이다. 파드의 경우 삭제 타임 스탬프가 설정된 모든 파드이다.

토폴로지 정보

엔드포인트슬라이스 내의 각 엔드 포인트는 관련 토폴로지 정보를 포함할 수 있다. 토폴로지 정보에는 엔드 포인트의 위치와 해당 노드 및 영역에 대한 정보가 포함된다. 엔드포인트슬라이스의 다음의 엔드 포인트별 필드에서 사용할 수 있다.

*nodeName - 이 엔드 포인트가 있는 노드의 이름이다. *zone - 이 엔드 포인트가 있는 영역이다.

관리

대부분의 경우, 컨트롤 플레인(특히, 엔드포인트슬라이스 컨트롤러)는 엔드포인트슬라이스 오브젝트를 생성하고 관리한다. 다른 엔티티나 컨트롤러가 추가 엔드포인트슬라이스 집합을 관리하게 할 수 있는 서비스 메시 구현과 같이 엔드포인트슬라이스에 대한 다양한 다른 유스케이스가 있다.

여러 엔티티가 서로 간섭하지 않고 엔드포인트슬라이스를 관리할 수 있도록 쿠버네티스는 엔드포인트슬라이스를 관리하는 엔티티를 나타내는 endpointslice.kubernetes.io/managed-by 레이블을 정의한다. 엔드포인트슬라이스 컨트롤러는 관리하는 모든 엔드포인트슬라이스에 레이블의 값으로 endpointslice-controller.k8s.io 를 설정한다. 엔드포인트슬라이스를 관리하는 다른 엔티티도 이 레이블에 고유한 값을 설정해야 한다.

소유권

대부분의 유스케이스에서, 엔드포인트슬라이스 오브젝트가 엔드포인트를 추적하는 서비스가 엔드포인트슬라이스를 소유한다. 이 소유권은 각 엔드포인트슬라이스의 소유자 참조와 서비스에 속한 모든 엔드포인트슬라이스의 간단한 조회를 가능하게 하는 kubernetes.io/service-name 레이블로 표시된다.

엔드포인트슬라이스 미러링

경우에 따라, 애플리케이션이 사용자 지정 엔드포인트 리소스를 생성한다. 이러한 애플리케이션이 엔드포인트와 엔드포인트슬라이스 리소스에 동시에 쓸 필요가 없도록 클러스터의 컨트롤 플레인은 대부분의 엔드포인트 리소스를 해당 엔드포인트슬라이스에 미러링한다.

컨트롤 플레인은 다음을 제외하고 엔드포인트 리소스를 미러링한다.

  • 엔드포인트 리소스에는 endpointslice.kubernetes.io/skip-mirror 레이블이 true 로 설정되어 있다.
  • 엔드포인트 리소스에는 control-plane.alpha.kubernetes.io/leader 어노테이션이 있다.
  • 해당 서비스 리소스가 존재하지 않는다.
  • 해당 서비스 리소스에 nil이 아닌 셀렉터가 있다.

개별 엔드포인트 리소스는 여러 엔드포인트슬라이스로 변환될 수 있다. 엔드포인트 리소스에 여러 하위 집합이 있거나 여러 IP 제품군(IPv4 및 IPv6)이 있는 엔드포인트가 포함된 경우 변환이 일어난다. 하위 집합 당 최대 1000개의 주소가 엔드포인트슬라이스에 미러링된다.

엔드포인트슬라이스의 배포

각 엔드포인트슬라이스에는 리소스 내에 모든 엔드포인트가 적용되는 포트 집합이 있다. 서비스에 알려진 포트를 사용하는 경우 파드는 동일하게 알려진 포트에 대해 다른 대상 포트 번호로 끝날 수 있으며 다른 엔드포인트슬라이스가 필요하다. 이는 하위 집합이 엔드포인트와 그룹화하는 방식의 논리와 유사하다.

컨트롤 플레인은 엔드포인트슬라이스를 최대한 채우려고 노력하지만, 적극적으로 재조정하지는 않는다. 로직은 매우 직관적이다.

  1. 기존 엔드포인트슬라이스에 대해 반복적으로, 더 이상 필요하지 않는 엔드포인트를 제거하고 변경에 의해 일치하는 엔드포인트를 업데이트 한다.
  2. 첫 번째 단계에서 수정된 엔드포인트슬라이스를 반복해서 필요한 새 엔드포인트로 채운다.
  3. 추가할 새 엔드포인트가 여전히 남아있으면, 이전에 변경되지 않은 슬라이스에 엔드포인트를 맞추거나 새로운 것을 생성한다.

중요한 것은, 세 번째 단계는 엔드포인트슬라이스를 완벽하게 전부 배포하는 것보다 엔드포인트슬라이스 업데이트 제한을 우선시한다. 예를 들어, 추가할 새 엔드포인트가 10개이고 각각 5개의 공간을 사용할 수 있는 엔드포인트 공간이 있는 2개의 엔드포인트슬라이스가 있는 경우, 이 방법은 기존 엔드포인트슬라이스 2개를 채우는 대신에 새 엔드포인트슬라이스를 생성한다. 다른 말로, 단일 엔드포인트슬라이스를 생성하는 것이 여러 엔드포인트슬라이스를 업데이트하는 것 보다 더 선호된다.

각 노드에서 kube-proxy를 실행하고 엔드포인트슬라이스를 관찰하면, 엔드포인트슬라이스에 대한 모든 변경 사항이 클러스터의 모든 노드로 전송되기 때문에 상대적으로 비용이 많이 소요된다. 이 방법은 여러 엔드포인트슬라이스가 가득 차지 않은 결과가 발생할지라도, 모든 노드에 전송해야 하는 변경 횟수를 의도적으로 제한하기 위한 것이다.

실제로는, 이러한 이상적이지 않은 분배는 드물 것이다. 엔드포인트슬라이스 컨트롤러에서 처리하는 대부분의 변경 내용은 기존 엔드포인트슬라이스에 적합할 정도로 적고, 그렇지 않은 경우 새 엔드포인트슬라이스가 필요할 수 있다. 디플로이먼트의 롤링 업데이트도 모든 파드와 해당 교체되는 엔드포인트에 대해서 엔드포인트슬라이스를 자연스럽게 재포장한다.

중복 엔드포인트

엔드포인트슬라이스 변경의 특성으로 인해, 엔드포인트는 동시에 둘 이상의 엔드포인트슬라이스에 표시될 수 있다. 이는 다른 엔드포인트슬라이스 오브젝트에 대한 변경 사항이 다른 시간에서의 쿠버네티스 클라이언트 워치(watch)/캐시에 도착할 수 있기 때문에 자연스럽게 발생한다. 엔드포인트슬라이스를 사용하는 구현은 엔드포인트가 둘 이상의 슬라이스에 표시되도록 할 수 있어야 한다. 엔드포인트 중복 제거를 수행하는 방법에 대한 레퍼런스 구현은 kube-proxyEndpointSliceCache 구현에서 찾을 수 있다.

다음 내용

3.5.9 - 네트워크 정책

IP 주소 또는 포트 수준(OSI 계층 3 또는 4)에서 트래픽 흐름을 제어하려는 경우, 클러스터의 특정 애플리케이션에 대해 쿠버네티스 네트워크폴리시(NetworkPolicy) 사용을 고려할 수 있다. 네트워크폴리시는 파드가 네트워크 상의 다양한 네트워크 "엔티티"(여기서는 "엔티티"를 사용하여 쿠버네티스에서 특별한 의미로 사용되는 "엔드포인트" 및 "서비스"와 같은 일반적인 용어가 중의적으로 표현되는 것을 방지함)와 통신할 수 있도록 허용하는 방법을 지정할 수 있는 애플리케이션 중심 구조이다.

파드가 통신할 수 있는 엔티티는 다음 3개의 식별자 조합을 통해 식별된다.

  1. 허용되는 다른 파드(예외: 파드는 자신에 대한 접근을 차단할 수 없음)
  2. 허용되는 네임스페이스
  3. IP 블록(예외: 파드 또는 노드의 IP 주소와 관계없이 파드가 실행 중인 노드와의 트래픽은 항상 허용됨)

pod- 또는 namespace- 기반의 네트워크폴리시를 정의할 때, 셀렉터를 사용하여 셀렉터와 일치하는 파드와 주고받는 트래픽을 지정한다.

한편, IP 기반의 네트워크폴리시가 생성되면, IP 블록(CIDR 범위)을 기반으로 정책을 정의한다.

전제 조건

네트워크 정책은 네트워크 플러그인으로 구현된다. 네트워크 정책을 사용하려면 네트워크폴리시를 지원하는 네트워킹 솔루션을 사용해야만 한다. 이를 구현하는 컨트롤러 없이 네트워크폴리시 리소스를 생성해도 아무런 효과가 없기 때문이다.

격리 및 격리되지 않은 파드

기본적으로, 파드는 격리되지 않는다. 이들은 모든 소스에서 오는 트래픽을 받아들인다.

파드는 파드를 선택한 네트워크폴리시에 의해서 격리된다. 네임스페이스에 특정 파드를 선택하는 네트워크폴리시가 있으면 해당 파드는 네트워크폴리시에서 허용하지 않는 모든 연결을 거부한다. (네임스페이스 내에서 어떠한 네트워크폴리시에도 선택 받지 않은 다른 파드들은 계속해서 모든 트래픽을 받아들인다.)

네트워크 정책은 충돌하지 않으며, 추가된다. 만약 어떤 정책 또는 정책들이 파드를 선택하면, 해당 정책의 인그레스(수신)/이그레스(송신) 규칙을 통합하여 허용되는 범위로 파드가 제한된다. 따라서 평가 순서는 정책 결과에 영향을 미치지 않는다.

두 파드간 네트워크 흐름을 허용하기 위해서는, 소스 파드의 이그레스 정책과 목적지 파드의 인그레스 정책 모두가 해당 트래픽을 허용해야 한다. 만약 소스의 이그레스 정책이나 목적지의 인그레스 정책 중 한쪽이라도 트래픽을 거절하게 되어 있다면, 해당 트래픽은 거절될 것이다.

네트워크폴리시 리소스

리소스에 대한 전체 정의에 대한 참조는 네트워크폴리시 를 본다.

네트워크폴리시 의 예시는 다음과 같다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

필수 필드들: 다른 모든 쿠버네티스 설정과 마찬가지로 네트워크폴리시 에는 apiVersion, kind, 그리고 metadata 필드가 필요하다. 구성 파일 작업에 대한 일반적인 정보는 컨피그 맵을 사용해서 컨테이너 구성하기, 그리고 오브젝트 관리 를 본다.

spec: 네트워크폴리시 사양에는 지정된 네임스페이스에서 특정 네트워크 정책을 정의하는데 필요한 모든 정보가 있다.

podSelector: 각 네트워크폴리시에는 정책이 적용되는 파드 그룹을 선택하는 podSelector 가 포함된다. 예시 정책은 "role=db" 레이블이 있는 파드를 선택한다. 비어있는 podSelector 는 네임스페이스의 모든 파드를 선택한다.

policyTypes: 각 네트워크폴리시에는 Ingress, Egress 또는 두 가지 모두를 포함할 수 있는 policyTypes 목록이 포함된다. policyTypes 필드는 선택한 파드에 대한 인그레스 트래픽 정책, 선택한 파드에 대한 이그레스 트래픽 정책 또는 두 가지 모두에 지정된 정책의 적용 여부를 나타낸다. 만약 네트워크폴리시에 policyTypes 가 지정되어 있지 않으면 기본적으로 Ingress 가 항상 설정되고, 네트워크폴리시에 Egress 가 있으면 이그레스 규칙이 설정된다.

ingress: 각 네트워크폴리시에는 화이트리스트 ingress 규칙 목록이 포함될 수 있다. 각 규칙은 fromports 부분과 모두 일치하는 트래픽을 허용한다. 예시 정책에는 단일 규칙이 포함되어 있는데 첫 번째 포트는 ipBlock 을 통해 지정되고, 두 번째는 namespaceSelector 를 통해 그리고 세 번째는 podSelector 를 통해 세 가지 소스 중 하나의 단일 포트에서 발생하는 트래픽과 일치 시킨다.

egress: 각 네트워크폴리시에는 화이트리스트 egress 규칙이 포함될 수 있다. 각 규칙은 toports 부분과 모두 일치하는 트래픽을 허용한다. 예시 정책에는 단일 포트의 트래픽을 10.0.0.0/24 의 모든 대상과 일치시키는 단일 규칙을 포함하고 있다.

따라서 예시의 네트워크폴리시는 다음과 같이 동작한다.

  1. 인그레스 및 이그레스 트래픽에 대해 "default" 네임스페이스에서 "role=db"인 파드를 격리한다(아직 격리되지 않은 경우).

  2. (인그레스 규칙)은 "role=db" 레이블을 사용하는 "default" 네임스페이스의 모든 파드에 대해서 TCP 포트 6397로의 연결을 허용한다. 인그레스을 허용 할 대상은 다음과 같다.

    • "role=frontend" 레이블이 있는 "default" 네임스페이스의 모든 파드
    • 네임스페이스와 "project=myproject" 를 레이블로 가지는 모든 파드
    • 172.17.0.0–172.17.0.255 와 172.17.2.0–172.17.255.255 의 범위를 가지는 IP 주소(예: 172.17.0.0/16 전체에서 172.17.1.0/24 를 제외)
  3. (이그레스 규칙)은 "role=db" 레이블이 있는 "default" 네임스페이스의 모든 파드에서 TCP 포트 5978의 CIDR 10.0.0.0/24 로의 연결을 허용한다.

자세한 설명과 추가 예시는 네트워크 정책 선언을 본다.

tofrom 셀럭터의 동작

ingress from 부분 또는 egress to 부분에 지정할 수 있는 네 종류의 셀렉터가 있다.

podSelector: 네트워크폴리시를 통해서, 인그레스 소스 또는 이그레스 목적지로 허용되야 하는 동일한 네임스페이스에 있는 특정 파드들을 선택한다.

namespaceSelector: 모든 파드가 인그레스 소스 또는 이그레스를 대상으로 허용되어야 하는 특정 네임스페이스를 선택한다.

namespaceSelector podSelector: namespaceSelectorpodSelector 를 모두 지정하는 단일 to/from 항목은 특정 네임스페이스 내에서 특정 파드를 선택한다. 올바른 YAML 구문을 사용하도록 주의해야 한다. 이 정책:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

네임스페이스에서 레이블이 role=client 인 것과 레이블이 user=alice 인 파드의 연결을 허용하는 단일 from 요소가 포함되어 있다. 그러나 정책:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

from 배열에 두 개의 요소가 포함되어 있으며, 로컬 네임스페이스에 레이블을 role=client 로 가지는 파드 또는 네임스페이스에 레이블을 user=alice 로 가지는 파드의 연결을 허용한다.

의심스러운 경우, kubectl describe 를 사용해서 쿠버네티스가 정책을 어떻게 해석하는지 확인해본다.

ipBlock: 인그레스 소스 또는 이그레스 대상으로 허용할 IP CIDR 범위를 선택한다. 파드 IP는 임시적이고 예측할 수 없기에 클러스터 외부 IP이어야 한다.

클러스터 인그레스 및 이그레스 매커니즘은 종종 패킷의 소스 또는 대상 IP의 재작성을 필요로 한다. 이러한 상황이 발생하는 경우, 네트워크폴리시의 처리 전 또는 후에 발생한 것인지 정의되지 않으며, 네트워크 플러그인, 클라우드 공급자, 서비스 구현 등의 조합에 따라 동작이 다를 수 있다.

인그레스 사례에서의 의미는 실제 원본 소스 IP를 기준으로 들어오는 패킷을 필터링할 수 있는 반면에 다른 경우에는 네트워크폴리시가 작동하는 "소스 IP"는 LoadBalancer 또는 파드가 속한 노드 등의 IP일 수 있다.

이그레스의 경우 파드에서 클러스터 외부 IP로 다시 작성된 서비스 IP로의 연결은 ipBlock 기반의 정책의 적용을 받거나 받지 않을 수 있다는 것을 의미한다.

기본 정책

기본적으로 네임스페이스 정책이 없으면 해당 네임스페이스의 파드에 대한 모든 인그레스와 이그레스 트래픽이 허용된다. 다음 예시에서는 해당 네임스페이스의 기본 동작을 변경할 수 있다.

기본적으로 모든 인그레스 트래픽 거부

모든 파드를 선택하지만 해당 파드에 대한 인그레스 트래픽은 허용하지 않는 네트워크폴리시를 생성해서 네임스페이스에 대한 "기본" 격리 정책을 생성할 수 있다.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

이렇게 하면 다른 네트워크폴리시에서 선택하지 않은 파드도 여전히 격리된다. 이 정책은 기본 이그레스 격리 동작을 변경하지 않는다.

기본적으로 모든 인그레스 트래픽 허용

만약 네임스페이스의 모든 파드에 대한 모든 트래픽을 허용하려는 경우(일부 파드가 "격리 된" 것으로 처리되는 정책이 추가 된 경우에도) 해당 네임스페이스의 모든 트래픽을 명시적으로 허용하는 정책을 만들 수 있다.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

기본적으로 모든 이그레스 트래픽 거부

모든 파드를 선택하지만, 해당 파드의 이그레스 트래픽을 허용하지 않는 네트워크폴리시를 생성해서 네임스페이스에 대한 "기본" 이그레스 격리 정책을 생성할 수 있다.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

이렇게 하면 다른 네트워크폴리시에서 선택하지 않은 파드조차도 이그레스 트래픽을 허용하지 않는다. 이 정책은 기본 인그레스 격리 정책을 변경하지 않는다.

기본적으로 모든 이그레스 트래픽 허용

만약 네임스페이스의 모든 파드의 모든 트래픽을 허용하려면 (일부 파드가 "격리 된"으로 처리되는 정책이 추가 된 경우에도) 해당 네임스페이스에서 모든 이그레스 트래픽을 명시적으로 허용하는 정책을 생성할 수 있다.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

기본적으로 모든 인그레스와 모든 이그레스 트래픽 거부

해당 네임스페이스에 아래의 네트워크폴리시를 만들어 모든 인그레스와 이그레스 트래픽을 방지하는 네임스페이스에 대한 "기본" 정책을 만들 수 있다.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

이렇게 하면 다른 네트워크폴리시에서 선택하지 않은 파드도 인그레스 또는 이그레스 트래픽을 허용하지 않는다.

SCTP 지원

FEATURE STATE: Kubernetes v1.20 [stable]

안정된 기능으로, 기본 활성화되어 있다. 클러스터 수준에서 SCTP를 비활성화하려면, 사용자(또는 클러스터 관리자)가 API 서버에 --feature-gates=SCTPSupport=false,… 를 사용해서 SCTPSupport 기능 게이트를 비활성화해야 한다. 해당 기능 게이트가 활성화되어 있는 경우, 네트워크폴리시의 protocol 필드를 SCTP로 지정할 수 있다.

포트 범위 지정

FEATURE STATE: Kubernetes v1.22 [beta]

네트워크폴리시를 작성할 때, 단일 포트 대신 포트 범위를 대상으로 지정할 수 있다.

다음 예와 같이 endPort 필드를 사용하면, 이 작업을 수행할 수 있다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 32000
      endPort: 32768

위 규칙은 대상 포트가 32000에서 32768 사이에 있는 경우, 네임스페이스 default 에 레이블이 role=db 인 모든 파드가 TCP를 통해 10.0.0.0/24 범위 내의 모든 IP와 통신하도록 허용한다.

이 필드를 사용할 때 다음의 제한 사항이 적용된다.

  • 베타 기능으로, 기본적으로 활성화되어 있다. 클러스터 수준에서 endPort 필드를 비활성화하려면, 사용자(또는 클러스터 관리자)가 API 서버에 대해 --feature-gates=NetworkPolicyEndPort=false,… 명령을 이용하여 NetworkPolicyEndPort 기능 게이트를 비활성화해야 한다.
  • endPort 필드는 port 필드보다 크거나 같아야 한다.
  • endPortport 도 정의된 경우에만 정의할 수 있다.
  • 두 포트 모두 숫자여야 한다.

이름으로 네임스페이스 지정

FEATURE STATE: Kubernetes 1.21 [beta]

쿠버네티스 컨트롤 플레인은 NamespaceDefaultLabelName 기능 게이트가 활성화된 경우 모든 네임스페이스에 변경할 수 없는(immutable) 레이블 kubernetes.io/metadata.name 을 설정한다. 레이블의 값은 네임스페이스 이름이다.

네트워크폴리시는 일부 오브젝트 필드가 있는 이름으로 네임스페이스를 대상으로 지정할 수 없지만, 표준화된 레이블을 사용하여 특정 네임스페이스를 대상으로 지정할 수 있다.

네트워크 정책으로 할 수 없는 것(적어도 아직은 할 수 없는)

쿠버네티스 1.23부터 다음의 기능은 네트워크폴리시 API에 존재하지 않지만, 운영 체제 컴포넌트(예: SELinux, OpenVSwitch, IPTables 등) 또는 Layer 7 기술(인그레스 컨트롤러, 서비스 메시 구현) 또는 어드미션 컨트롤러를 사용하여 제2의 해결책을 구현할 수 있다. 쿠버네티스의 네트워크 보안을 처음 사용하는 경우, 네트워크폴리시 API를 사용하여 다음의 사용자 스토리를 (아직) 구현할 수 없다는 점에 유의할 필요가 있다.

  • 내부 클러스터 트래픽이 공통 게이트웨이를 통과하도록 강제한다(서비스 메시나 기타 프록시와 함께 제공하는 것이 가장 좋을 수 있음).
  • TLS와 관련된 모든 것(이를 위해 서비스 메시나 인그레스 컨트롤러 사용).
  • 노드별 정책(이에 대해 CIDR 표기법을 사용할 수 있지만, 특히 쿠버네티스 ID로 노드를 대상으로 지정할 수 없음).
  • 이름으로 서비스를 타겟팅한다(그러나, 레이블로 파드나 네임스페이스를 타겟팅할 수 있으며, 이는 종종 실행할 수 있는 해결 방법임).
  • 타사 공급사가 이행한 "정책 요청"의 생성 또는 관리.
  • 모든 네임스페이스나 파드에 적용되는 기본 정책(이를 수행할 수 있는 타사 공급사의 쿠버네티스 배포본 및 프로젝트가 있음).
  • 고급 정책 쿼리 및 도달 가능성 도구.
  • 네트워크 보안 이벤트를 기록하는 기능(예: 차단되거나 수락된 연결).
  • 명시적으로 정책을 거부하는 기능(현재 네트워크폴리시 모델은 기본적으로 거부하며, 허용 규칙을 추가하는 기능만 있음).
  • 루프백 또는 들어오는 호스트 트래픽을 방지하는 기능(파드는 현재 로컬 호스트 접근을 차단할 수 없으며, 상주 노드의 접근을 차단할 수 있는 기능도 없음).

다음 내용

  • 자세한 설명과 추가 예시는 네트워크 정책 선언을 본다.
  • 네트워크폴리시 리소스에서 사용되는 일반적인 시나리오는 레시피를 본다.

3.5.10 - IPv4/IPv6 이중 스택

FEATURE STATE: Kubernetes v1.21 [beta]

IPv4/IPv6 이중 스택 네트워킹을 사용하면 파드서비스에 IPv4와 IPv6 주소를 모두 할당할 수 있다.

IPv4/IPv6 이중 스택 네트워킹은 1.21부터 쿠버네티스 클러스터에 기본적으로 활성화되어 있고, IPv4 및 IPv6 주소를 동시에 할당할 수 있다.

지원되는 기능

쿠버네티스 클러스터의 IPv4/IPv6 이중 스택은 다음의 기능을 제공한다.

  • 이중 스택 파드 네트워킹(파드 당 단일 IPv4와 IPv6 주소 할당)
  • IPv4와 IPv6 지원 서비스
  • IPv4와 IPv6 인터페이스를 통한 파드 오프(off) 클러스터 이그레스 라우팅(예: 인터넷)

필수 구성 요소

IPv4/IPv6 이중 스택 쿠버네티스 클러스터를 활용하려면 다음의 필수 구성 요소가 필요하다.

  • 쿠버네티스 1.20 이상 이전 버전과 함께 이중 스택 서비스를 사용하는 방법에 대한 정보 쿠버네티스 버전, 쿠버네티스 해당 버전에 대한 문서 참조
  • 이중 스택 네트워킹을 위한 공급자의 지원(클라우드 공급자 또는 다른 방식으로 쿠버네티스 노드에 라우팅 가능한 IPv4/IPv6 네트워크 인터페이스를 제공할 수 있어야 한다.)
  • 이중 스택(예: Kubenet 또는 Calico)을 지원하는 네트워크 플러그인

IPv4/IPv6 이중 스택 구성

IPv4/IPv6 이중 스택을 사용하려면, 클러스터의 관련 구성 요소에 대해 IPv6DualStack 기능 게이트를 활성화한다. (1.21부터 IPv4/IPv6 이중 스택이 기본적으로 활성화된다.)

IPv4/IPv6 이중 스택을 구성하려면, 이중 스택 클러스터 네트워크 할당을 설정한다.

  • kube-apiserver:
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
  • kube-controller-manager:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 IPv4의 기본값은 /24 이고 IPv6의 기본값은 /64 이다.
  • kube-proxy:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>

서비스

IPv4, IPv6 또는 둘 다를 사용할 수 있는 서비스를 생성할 수 있다.

서비스의 주소 계열은 기본적으로 첫 번째 서비스 클러스터 IP 범위의 주소 계열로 설정된다. (--service-cluster-ip-range 플래그를 통해 kube-apiserver에 구성)

서비스를 정의할 때 선택적으로 이중 스택으로 구성할 수 있다. 원하는 동작을 지정하려면 .spec.ipFamilyPolicy 필드를 다음 값 중 하나로 설정한다.

  • SingleStack: 단일 스택 서비스. 컨트롤 플레인은 첫 번째로 구성된 서비스 클러스터 IP 범위를 사용하여 서비스에 대한 클러스터 IP를 할당한다.
  • PreferDualStack:
    • 서비스에 IPv4 및 IPv6 클러스터 IP를 할당한다. (클러스터에 --feature-gates="IPv6DualStack=false" 가 있는 경우, 이 설정은 SingleStack 과 동일한 동작을 따른다.)
  • RequireDualStack: IPv4 및 IPv6 주소 범위 모두에서 서비스 .spec.ClusterIPs를 할당한다.
    • .spec.ipFamilies 배열의 첫 번째 요소의 주소 계열을 기반으로 .spec.ClusterIPs 목록에서 .spec.ClusterIP를 선택한다.

단일 스택에 사용할 IP 계열을 정의하거나 이중 스택에 대한 IP 군의 순서를 정의하려는 경우, 서비스에서 옵션 필드 .spec.ipFamilies를 설정하여 주소 군을 선택할 수 있다.

.spec.ipFamilies를 다음 배열 값 중 하나로 설정할 수 있다.

  • ["IPv4"]
  • ["IPv6"]
  • ["IPv4","IPv6"] (이중 스택)
  • ["IPv6","IPv4"] (이중 스택)

나열한 첫 번째 군은 레거시.spec.ClusterIP 필드에 사용된다.

이중 스택 서비스 구성 시나리오

이 예제는 다양한 이중 스택 서비스 구성 시나리오의 동작을 보여준다.

새로운 서비스에 대한 이중 스택 옵션

  1. 이 서비스 사양은 .spec.ipFamilyPolicy를 명시적으로 정의하지 않는다. 이 서비스를 만들 때 쿠버네티스는 처음 구성된 service-cluster-ip-range에서 서비스에 대한 클러스터 IP를 할당하고 .spec.ipFamilyPolicySingleStack으로 설정한다. (셀렉터가 없는 서비스헤드리스 서비스와 같은 방식으로 동작한다.)
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
  1. 이 서비스 사양은 .spec.ipFamilyPolicyPreferDualStack을 명시적으로 정의한다. 이중 스택 클러스터에서 이 서비스를 생성하면 쿠버네티스는 서비스에 대해 IPv4 및 IPv6 주소를 모두 할당한다. 컨트롤 플레인은 서비스의 .spec을 업데이트하여 IP 주소 할당을 기록한다. 필드 .spec.ClusterIPs는 기본 필드이며 할당된 IP 주소를 모두 포함한다. .spec.ClusterIP는 값이 .spec.ClusterIPs에서 계산된 보조 필드이다.

    • .spec.ClusterIP 필드의 경우 컨트롤 플레인은 첫 번째 서비스 클러스터 IP 범위와 동일한 주소 계열의 IP 주소를 기록한다.
    • 단일 스택 클러스터에서 .spec.ClusterIPs.spec.ClusterIP 필드는 모두 하나의 주소만 나열한다.
    • 이중 스택이 활성화된 클러스터에서 .spec.ipFamilyPolicyRequireDualStack을 지정하면 PreferDualStack과 동일하게 작동한다.
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
  1. 이 서비스 사양은 .spec.ipFamilies IPv6IPv4를 명시적으로 정의하고 .spec.ipFamilyPolicyPreferDualStack을 정의한다. 쿠버네티스가 .spec.ClusterIPs에 IPv6 및 IPv4 주소를 할당할 때 .spec.ClusterIP.spec.ClusterIPs 배열의 첫 번째 요소이므로 IPv6 주소로 설정되어 기본값을 재정의한다.
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
  - IPv6
  - IPv4
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

기존 서비스의 이중 스택 기본값

이 예제는 서비스가 이미 있는 클러스터에서 이중 스택이 새로 활성화된 경우의 기본 동작을 보여준다. (--feature-gates="IPv6DualStack=false" 가 설정되지 않은 경우 기존 클러스터를 1.21로 업그레이드하면 이중 스택이 활성화된다.)

  1. 클러스터에서 이중 스택이 활성화된 경우 기존 서비스 (IPv4 또는 IPv6)는 컨트롤 플레인이 .spec.ipFamilyPolicySingleStack으로 지정하고 .spec.ipFamilies를 기존 서비스의 주소 계열로 설정한다. 기존 서비스 클러스터 IP는 .spec.ClusterIPs에 저장한다.
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl을 사용하여 기존 서비스를 검사하여 이 동작을 검증할 수 있다.

kubectl get svc my-service -o yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: MyApp
  name: my-service
spec:
  clusterIP: 10.0.197.123
  clusterIPs:
  - 10.0.197.123
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: MyApp
  type: ClusterIP
status:
  loadBalancer: {}
  1. 클러스터에서 이중 스택이 활성화된 경우, 셀렉터가 있는 기존 헤드리스 서비스.spec.ClusterIPNone이라도 컨트롤 플레인이 .spec.ipFamilyPolicySingleStack으로 지정하고 .spec.ipFamilies는 첫 번째 서비스 클러스터 IP 범위(kube-apiserver에 대한 --service-cluster-ip-range 플래그를 통해 구성)의 주소 계열으로 지정한다.
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl을 사용하여 셀렉터로 기존 헤드리스 서비스를 검사하여 이 동작의 유효성을 검사 할 수 있다.

kubectl get svc my-service -o yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: MyApp
  name: my-service
spec:
  clusterIP: None
  clusterIPs:
  - None
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: MyApp

단일 스택과 이중 스택 간 서비스 전환

서비스는 단일 스택에서 이중 스택으로, 이중 스택에서 단일 스택으로 변경할 수 있다.

  1. 서비스를 단일 스택에서 이중 스택으로 변경하려면 원하는 대로 .spec.ipFamilyPolicySingleStack에서 PreferDualStack 또는 RequireDualStack으로 변경한다. 이 서비스를 단일 스택에서 이중 스택으로 변경하면 쿠버네티스는 누락된 주소 계열의 것을 배정하므로 해당 서비스는 이제 IPv4와 IPv6 주소를 갖게된다.

    .spec.ipFamilyPolicySingleStack에서 PreferDualStack으로 업데이트하는 서비스 사양을 편집한다.

이전:

spec:
  ipFamilyPolicy: SingleStack

이후:

spec:
  ipFamilyPolicy: PreferDualStack
  1. 서비스를 이중 스택에서 단일 스택으로 변경하려면 .spec.ipFamilyPolicyPreferDualStack에서 또는 RequireDualStackSingleStack으로 변경한다. 이 서비스를 이중 스택에서 단일 스택으로 변경하면 쿠버네티스는 .spec.ClusterIPs 배열의 첫 번째 요소 만 유지하고 .spec.ClusterIP를 해당 IP 주소로 설정하고 .spec.ipFamilies.spec.ClusterIPs의 주소 계열로 설정한다.

셀렉터가 없는 헤드리스 서비스

셀렉터가 없는 서비스.spec.ipFamilyPolicy가 명시적으로 설정되지 않은 경우 .spec.ipFamilyPolicy 필드의 기본값은 RequireDualStack 이다.

로드밸런서 서비스 유형

서비스에 이중 스택 로드밸런서를 프로비저닝하려면

  • .spec.type 필드를 LoadBalancer로 설정
  • .spec.ipFamilyPolicy 필드를 PreferDualStack 또는 RequireDualStack으로 설정

이그레스(Egress) 트래픽

비공개로 라우팅할 수 있는 IPv6 주소를 사용하는 파드에서 클러스터 외부 대상 (예: 공용 인터넷)에 도달하기 위해 이그레스 트래픽을 활성화하려면 투명 프록시 또는 IP 위장과 같은 메커니즘을 통해 공개적으로 라우팅한 IPv6 주소를 사용하도록 파드를 활성화해야 한다. ip-masq-agent 프로젝트는 이중 스택 클러스터에서 IP 위장을 지원한다.

다음 내용

3.6 - 스토리지

클러스터의 파드에 장기(long-term) 및 임시 스토리지를 모두 제공하는 방법

3.6.1 - 볼륨

컨테이너 내의 디스크에 있는 파일은 임시적이며, 컨테이너에서 실행될 때 애플리케이션에 적지 않은 몇 가지 문제가 발생한다. 한 가지 문제는 컨테이너가 크래시될 때 파일이 손실된다는 것이다. kubelet은 컨테이너를 다시 시작하지만 초기화된 상태이다. 두 번째 문제는 Pod에서 같이 실행되는 컨테이너간에 파일을 공유할 때 발생한다. 쿠버네티스 볼륨 추상화는 이러한 문제를 모두 해결한다. 파드에 대해 익숙해지는 것을 추천한다.

배경

도커는 다소 느슨하고, 덜 관리되지만 볼륨이라는 개념을 가지고 있다. 도커 볼륨은 디스크에 있는 디렉터리이거나 다른 컨테이너에 있다. 도커는 볼륨 드라이버를 제공하지만, 기능이 다소 제한된다.

쿠버네티스는 다양한 유형의 볼륨을 지원한다. 파드는 여러 볼륨 유형을 동시에 사용할 수 있다. 임시 볼륨 유형은 파드의 수명을 갖지만, 퍼시스턴트 볼륨은 파드의 수명을 넘어 존재한다. 파드가 더 이상 존재하지 않으면, 쿠버네티스는 임시(ephemeral) 볼륨을 삭제하지만, 퍼시스턴트(persistent) 볼륨은 삭제하지 않는다. 볼륨의 종류와 상관없이, 파드 내의 컨테이너가 재시작되어도 데이터는 보존된다.

기본적으로 볼륨은 디렉터리이며, 일부 데이터가 있을 수 있으며, 파드 내 컨테이너에서 접근할 수 있다. 디렉터리의 생성 방식, 이를 지원하는 매체와 내용은 사용된 특정 볼륨의 유형에 따라 결정된다.

볼륨을 사용하려면, .spec.volumes 에서 파드에 제공할 볼륨을 지정하고 .spec.containers[*].volumeMounts 의 컨테이너에 해당 볼륨을 마운트할 위치를 선언한다. 컨테이너의 프로세스는 도커 이미지와 볼륨으로 구성된 파일시스템 뷰를 본다. 도커 이미지는 파일시스템 계층의 루트에 있다. 볼륨은 이미지 내에 지정된 경로에 마운트된다. 볼륨은 다른 볼륨에 마운트할 수 없거나 다른 볼륨에 대한 하드 링크를 가질 수 없다. 파드 구성의 각 컨테이너는 각 볼륨을 마운트할 위치를 독립적으로 지정해야 한다.

볼륨 유형들

쿠버네티스는 여러 유형의 볼륨을 지원한다.

awsElasticBlockStore

awsElasticBlockStore 볼륨은 아마존 웹 서비스 (AWS) EBS 볼륨을 파드에 마운트 한다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 EBS 볼륨의 내용은 유지되고, 볼륨은 마운트 해제만 된다. 이 의미는 EBS 볼륨에 데이터를 미리 채울 수 있으며, 파드 간에 데이터를 "전달(handed off)"할 수 있다.

awsElasticBlockStore 볼륨을 사용할 때 몇 가지 제한이 있다.

  • 파드가 실행 중인 노드는 AWS EC2 인스턴스여야 함
  • 이러한 인스턴스는 EBS 볼륨과 동일한 지역과 가용성 영역에 있어야 함
  • EBS는 볼륨을 마운트하는 단일 EC2 인스턴스만 지원함

AWS EBS 볼륨 생성하기

파드와 함께 EBS 볼륨을 사용하려면, 먼저 EBS 볼륨을 생성해야 한다.

aws ec2 create-volume --availability-zone=eu-west-1a --size=10 --volume-type=gp2

클러스터를 띄운 영역과 생성하는 영역이 일치하는지 확인한다. 크기와 EBS 볼륨 유형이 사용에 적합한지 확인한다.

AWS EBS 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-ebs
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: test-volume
  volumes:
  - name: test-volume
    # 이 AWS EBS 볼륨은 이미 존재해야 한다.
    awsElasticBlockStore:
      volumeID: "<volume-id>"
      fsType: ext4

EBS 볼륨이 파티션된 경우, 선택적 필드인 partition: "<partition number>" 를 제공하여 마운트할 파티션을 지정할 수 있다.

AWS EBS CSI 마이그레이션

FEATURE STATE: Kubernetes v1.17 [beta]

awsElasticBlockStoreCSIMigration 기능이 활성화된 경우, 기존 인-트리 플러그인의 모든 플러그인 작업을 ebs.csi.aws.com 컨테이너 스토리지 인터페이스(CSI) 드라이버로 리디렉션한다. 이 기능을 사용하려면, 클러스터에 AWS EBS CSI 드라이버를 설치하고 CSIMigrationCSIMigrationAWS 베타 기능을 활성화해야 한다.

AWS EBS CSI 마이그레이션 완료

FEATURE STATE: Kubernetes v1.17 [alpha]

컨트롤러 관리자와 kubelet에 의해 로드되지 않도록 awsElasticBlockStore 스토리지 플러그인을 끄려면, InTreePluginAWSUnregister 플래그를 true 로 설정한다.

azureDisk

azureDisk 볼륨 유형은 Microsoft Azure 데이터 디스크를 파드에 마운트한다.

더 자세한 내용은 azureDisk 볼륨 플러그인을 참고한다.

azureDisk CSI 마이그레이션

FEATURE STATE: Kubernetes v1.19 [beta]

azureDiskCSIMigration 기능이 활성화된 경우, 기존 트리 내 플러그인에서 disk.csi.azure.com 컨테이너 스토리지 인터페이스(CSI) 드라이버로 모든 플러그인 작업을 수행한다. 이 기능을 사용하려면, 클러스터에 Azure 디스크 CSI 드라이버 를 설치하고 CSIMigrationCSIMigrationAzureDisk 기능을 활성화해야 한다.

azureFile

azureFile 볼륨 유형은 Microsoft Azure 파일 볼륨(SMB 2.1과 3.0)을 파드에 마운트한다.

더 자세한 내용은 azureFile 볼륨 플러그인을 참고한다.

azureFile CSI 마이그레이션

FEATURE STATE: Kubernetes v1.21 [beta]

azureFileCSIMigration 기능이 활성화된 경우, 기존 트리 내 플러그인에서 file.csi.azure.com 컨테이너 스토리지 인터페이스(CSI) 드라이버로 모든 플러그인 작업을 수행한다. 이 기능을 사용하려면, 클러스터에 Azure 파일 CSI 드라이버 를 설치하고 CSIMigrationCSIMigrationAzureFile 기능 게이트를 활성화해야 한다.

Azure File CSI 드라이버는 동일한 볼륨을 다른 fsgroup에서 사용하는 것을 지원하지 않는다. Azurefile CSI 마이그레이션이 활성화된 경우, 다른 fsgroup에서 동일한 볼륨을 사용하는 것은 전혀 지원되지 않는다.

cephfs

cephfs 볼륨은 기존 CephFS 볼륨을 파드에 마운트 할 수 있다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 cephfs 볼륨의 내용은 유지되고, 볼륨은 그저 마운트 해제만 된다. 이 의미는 cephfs 볼륨에 데이터를 미리 채울 수 있으며, 해당 데이터는 파드 간에 공유될 수 있다. cephfs 볼륨은 여러 작성자가 동시에 마운트할 수 있다.

더 자세한 내용은 CephFS 예시를 참조한다.

cinder

cinder 볼륨 유형은 오픈스택 Cinder 볼륨을 파드에 마운트하는데 사용된다.

Cinder 볼륨 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-cinder
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-cinder-container
    volumeMounts:
    - mountPath: /test-cinder
      name: test-volume
  volumes:
  - name: test-volume
    # 이 오픈스택 볼륨은 이미 존재해야 한다.
    cinder:
      volumeID: "<volume id>"
      fsType: ext4

오픈스택 CSI 마이그레이션

FEATURE STATE: Kubernetes v1.21 [beta]

Cinder의CSIMigration 기능은 Kubernetes 1.21에서 기본적으로 활성화됩니다. 기존 트리 내 플러그인에서 cinder.csi.openstack.org 컨테이너 스토리지 인터페이스(CSI) 드라이버로 모든 플러그인 작업을 수행한다. 오픈스택 Cinder CSI 드라이버가 클러스터에 설치되어 있어야 한다. CSIMigrationOpenStack 기능 게이트false 로 설정하여 클러스터에 대한 Cinder CSI 마이그레이션을 비활성화할 수 있다. CSIMigrationOpenStack 기능을 비활성화하면, 트리 내 Cinder 볼륨 플러그인이 Cinder 볼륨 스토리지 관리의 모든 측면을 담당한다.

컨피그맵(configMap)

컨피그맵은 구성 데이터를 파드에 주입하는 방법을 제공한다. 컨피그맵에 저장된 데이터는 configMap 유형의 볼륨에서 참조되고 그런 다음에 파드에서 실행되는 컨테이너화된 애플리케이션이 소비한다.

컨피그맵을 참조할 때, 볼륨에 컨피그맵의 이름을 제공한다. 컨피그맵의 특정 항목에 사용할 경로를 사용자 정의할 수 있다. 다음 구성은 log-config 컨피그맵을 configmap-pod 라 부르는 파드에 마운트하는 방법을 보여준다.

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log_level

log-config 컨피그맵은 볼륨으로 마운트되며, log_level 항목에 저장된 모든 컨텐츠는 파드의 /etc/config/log_level 경로에 마운트된다. 이 경로는 볼륨의 mountPathlog_level 로 키가 지정된 path 에서 파생된다.

downwardAPI

downwardAPI 볼륨은 애플리케이션에서 다운워드(downward) API 데이터를 사용할 수 있도록 한다. 이것은 디렉터리를 마운트하고 요청된 데이터를 일반 텍스트 파일로 작성한다.

더 자세한 내용은 다운워드 API 예시를 참고한다.

emptyDir

emptyDir 볼륨은 파드가 노드에 할당될 때 처음 생성되며, 해당 노드에서 파드가 실행되는 동안에만 존재한다. 이름에서 알 수 있듯이 emptyDir 볼륨은 처음에는 비어있다. 파드 내 모든 컨테이너는 emptyDir 볼륨에서 동일한 파일을 읽고 쓸 수 있지만, 해당 볼륨은 각각의 컨테이너에서 동일하거나 다른 경로에 마운트될 수 있다. 어떤 이유로든 노드에서 파드가 제거되면 emptyDir 의 데이터가 영구적으로 삭제된다.

emptyDir 의 일부 용도는 다음과 같다.

  • 디스크 기반의 병합 종류와 같은 스크레치 공간
  • 충돌로부터 복구하기위해 긴 계산을 검사점으로 지정
  • 웹 서버 컨테이너가 데이터를 처리하는 동안 컨텐츠 매니저 컨테이너가 가져오는 파일을 보관

환경에 따라, emptyDir 볼륨은 디스크, SSD 또는 네트워크 스토리지와 같이 노드를 지원하는 모든 매체에 저장된다. 그러나, emptyDir.medium 필드를 "Memory"로 설정하면, 쿠버네티스에 tmpfs(RAM 기반 파일시스템)를 마운트하도록 할 수 있다. tmpfs는 매우 빠르지만, 디스크와 다르게 노드 재부팅시 tmpfs가 지워지고, 작성하는 모든 파일이 컨테이너 메모리 제한에 포함된다.

emptyDir 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

fc (파이버 채널)

fc 볼륨 유형은 기존 파이버 채널 블록 스토리지 볼륨을 파드에 마운트할 수 있게 한다. 볼륨 구성에서 targetWWNs 파라미터를 사용하여 단일 또는 다중 대상 월드 와이드 이름(WWN)을 지정할 수 있다. 만약 여러 WWN이 지정된 경우, targetWWN은 해당 WWN이 다중 경로 연결에서 온 것으로 예상한다.

더 자세한 내용은 파이버 채널 예시를 참고한다.

flocker (사용 중단됨(deprecated))

Flocker는 오픈소스이고, 클러스터 컨테이너 데이터 볼륨 매니저이다. Flocker는 다양한 스토리지 백엔드가 지원하는 데이터 볼륨 관리와 오케스트레이션을 제공한다.

flocker 볼륨은 Flocker 데이터셋을 파드에 마운트할 수 있게 한다. 만약 Flocker내에 데이터셋이 없는 경우, 먼저 Flocker CLI 또는 Flocker API를 사용해서 생성해야 한다. 만약 데이터셋이 이미 있다면 Flocker는 파드가 스케줄 되어 있는 노드에 다시 연결한다. 이는 필요에 따라 파드 간에 데이터를 공유할 수 있다는 의미이다.

더 자세한 내용은 Flocker 예시를 참조한다.

gcePersistentDisk

gcePersistentDisk 볼륨은 구글 컴퓨트 엔진(GCE) 영구 디스크(PD)를 파드에 마운트한다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게, PD의 내용은 유지되고, 볼륨은 마운트 해제만 된다. 이는 PD에 데이터를 미리 채울 수 있으며, 파드 간에 데이터를 공유할 수 있다는 것을 의미한다.

gcePersistentDisk 를 사용할 때 몇 가지 제한이 있다.

  • 파드가 실행 중인 노드는 GCE VM이어야 함
  • 이러한 VM은 영구 디스크와 동일한 GCE 프로젝트와 영역에 있어야 함

GCE 영구 디스크의 한 가지 기능은 영구 디스크에 대한 동시 읽기 전용 접근이다. gcePersistentDisk 볼륨을 사용하면 여러 사용자가 영구 디스크를 읽기 전용으로 동시에 마운트할 수 있다. 즉, PD를 데이터 세트로 미리 채운 다음 필요한 만큼 많은 파드에서 병렬로 제공할 수 있다. 불행히도, PD는 읽기-쓰기 모드에서 단일 사용자만 마운트할 수 있다. 동시 쓰기는 허용되지 않는다.

PD가 읽기 전용이거나 레플리카의 수가 0 또는 1이 아니라면 레플리카셋(ReplicaSet)으로 제어되는 파드가 있는 GCE 영구 디스크를 사용할 수 없다.

GCE 영구 디스크 생성하기

GCE 영구 디스크를 파드와 함께 사용하려면, 디스크를 먼저 생성해야 한다.

gcloud compute disks create --size=500GB --zone=us-central1-a my-data-disk

GCE 영구 디스크 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    # 이 GCE PD는 이미 존재해야 한다.
    gcePersistentDisk:
      pdName: my-data-disk
      fsType: ext4

리전 영구 디스크

리전 영구 디스크 기능을 사용하면 동일한 영역 내의 두 영역에서 사용할 수 있는 영구 디스크를 생성할 수 있다. 이 기능을 사용하려면 볼륨을 퍼시스턴트볼륨(PersistentVolume)으로 프로비저닝해야 한다. 파드에서 직접 볼륨을 참조하는 것은 지원되지 않는다.

리전 PD 퍼시스턴트볼륨을 수동으로 프로비저닝하기

GCE PD용 스토리지클래스를 사용해서 동적 프로비저닝이 가능하다. 퍼시스턴트볼륨을 생성하기 전에 영구 디스크를 생성해야만 한다.

gcloud compute disks create --size=500GB my-data-disk
  --region us-central1
  --replica-zones us-central1-a,us-central1-b

리전 영구 디스크 구성 예시

apiVersion: v1
kind: PersistentVolume
metadata:
  name: test-volume
spec:
  capacity:
    storage: 400Gi
  accessModes:
  - ReadWriteOnce
  gcePersistentDisk:
    pdName: my-data-disk
    fsType: ext4
      nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        # 1.21 이전 버전에서는 failure-domain.beta.kubernetes.io/zone 키를 사용해야 한다.
        - key: topology.kubernetes.io/zone
          operator: In
          values:
          - us-central1-a
          - us-central1-b

GCE CSI 마이그레이션

FEATURE STATE: Kubernetes v1.17 [beta]

GCE PD의 CSIMigration 기능이 활성화된 경우 기존 인-트리 플러그인에서 pd.csi.storage.gke.io 컨테이너 스토리지 인터페이스(CSI) 드라이버로 모든 플러그인 작업을 리디렉션한다. 이 기능을 사용하려면, 클러스터에 GCE PD CSI 드라이버 를 설치하고 CSIMigrationCSIMigrationGCE 베타 기능을 활성화해야 한다.

GCE CSI 마이그레이션 완료

FEATURE STATE: Kubernetes v1.21 [alpha]

컨트롤러 매니저와 kubelet이 gcePersistentDisk 스토리지 플러그인을 로드하는 것을 방지하려면, InTreePluginGCEUnregister 플래그를 true로 설정한다.

gitRepo (사용 중단됨)

gitRepo 볼륨은 볼륨 플러그인의 예시이다. 이 플러그인은 빈 디렉터리를 마운트하고 파드가 사용할 수 있도록 이 디렉터리에 git 리포지터리를 복제한다.

여기 gitRepo 볼륨의 예시가 있다.

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

glusterfs

glusterfs 볼륨을 사용하면 Glusterfs (오픈 소스 네트워크 파일시스템) 볼륨을 파드에 마운트할 수 있다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 glusterfs 볼륨의 내용은 유지되고, 볼륨은 마운트 해제만 된다. 이 의미는 glusterfs 볼륨에 데이터를 미리 채울 수 있으며, 파드 간에 데이터를 공유할 수 있다. GlusterFS는 여러 작성자가 동시에 마운트할 수 있다.

더 자세한 내용은 GlusterFS 예시를 본다.

hostPath

hostPath 볼륨은 호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 마운트 한다. 이것은 대부분의 파드들이 필요한 것은 아니지만, 일부 애플리케이션에 강력한 탈출구를 제공한다.

예를 들어, hostPath 의 일부 용도는 다음과 같다.

  • 도커 내부에 접근할 필요가 있는 실행중인 컨테이너. /var/lib/dockerhostPath 로 이용함
  • 컨테이너에서 cAdvisor의 실행. /syshostPath 로 이용함
  • 파드는 주어진 hostPath 를 파드가 실행되기 이전에 있어야 하거나, 생성해야 하는지 그리고 존재해야 하는 대상을 지정할 수 있도록 허용함

필요한 path 속성 외에도, hostPath 볼륨에 대한 type 을 마음대로 지정할 수 있다.

필드가 type 에 지원되는 값은 다음과 같다.

행동
빈 문자열 (기본값)은 이전 버전과의 호환성을 위한 것으로, hostPath 볼륨은 마운트 하기 전에 아무런 검사도 수행되지 않는다.
DirectoryOrCreate 만약 주어진 경로에 아무것도 없다면, 필요에 따라 Kubelet이 가지고 있는 동일한 그룹과 소유권, 권한을 0755로 설정한 빈 디렉터리를 생성한다.
Directory 주어진 경로에 디렉터리가 있어야 함
FileOrCreate 만약 주어진 경로에 아무것도 없다면, 필요에 따라 Kubelet이 가지고 있는 동일한 그룹과 소유권, 권한을 0644로 설정한 빈 파일을 생성한다.
File 주어진 경로에 파일이 있어야 함
Socket 주어진 경로에 UNIX 소캣이 있어야 함
CharDevice 주어진 경로에 문자 디바이스가 있어야 함
BlockDevice 주어진 경로에 블록 디바이스가 있어야 함

다음과 같은 이유로 이 유형의 볼륨 사용시 주의해야 한다.

  • HostPath는 권한있는 시스템 자격 증명 (예 : Kubelet 용) 또는 권한있는 API (예 : 컨테이너 런타임 소켓)를 노출 할 수 있으며, 이는 컨테이너 이스케이프 또는 클러스터의 다른 부분을 공격하는 데 사용될 수 있다.
  • 동일한 구성(파드템플릿으로 생성한 것과 같은)을 가진 파드는 노드에 있는 파일이 다르기 때문에 노드마다 다르게 동작할 수 있다.
  • 기본 호스트에 생성된 파일 또는 디렉터리는 root만 쓸 수 있다. 프로세스를 특권을 가진(privileged) 컨테이너에서 루트로 실행하거나 hostPath 볼륨에 쓸 수 있도록 호스트의 파일 권한을 수정해야 한다.

hostPath 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 호스트의 디렉터리 위치
      path: /data
      # 이 필드는 선택 사항이다
      type: Directory

hostPath FileOrCreate 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-webserver
spec:
  containers:
  - name: test-webserver
    image: k8s.gcr.io/test-webserver:latest
    volumeMounts:
    - mountPath: /var/local/aaa
      name: mydir
    - mountPath: /var/local/aaa/1.txt
      name: myfile
  volumes:
  - name: mydir
    hostPath:
      # 파일 디렉터리가 생성되었는지 확인한다.
      path: /var/local/aaa
      type: DirectoryOrCreate
  - name: myfile
    hostPath:
      path: /var/local/aaa/1.txt
      type: FileOrCreate

iscsi

iscsi 볼륨을 사용하면 기존 iSCSI (SCSI over IP) 볼륨을 파드에 마운트 할수 있다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 iscsi 볼륨의 내용은 유지되고, 볼륨은 그저 마운트 해제만 된다. 이 의미는 iscsi 볼륨에 데이터를 미리 채울 수 있으며, 파드간에 데이터를 공유할 수 있다는 것이다.

iSCSI 특징은 여러 고객이 읽기 전용으로 마운트할 수 있다는 것이다. 즉, 데이터셋으로 사전에 볼륨을 채운다음, 필요한 만큼 많은 파드에서 병렬로 제공할 수 있다. 불행하게도, iSCSI 볼륨은 읽기-쓰기 모드에서는 단일 고객만 마운트할 수 있다. 동시 쓰기는 허용되지 않는다.

더 자세한 내용은 iSCSI 예시를 본다.

local

local 볼륨은 디스크, 파티션 또는 디렉터리 같은 마운트된 로컬 스토리지 장치를 나타낸다.

로컬 볼륨은 정적으로 생성된 퍼시스턴트볼륨으로만 사용할 수 있다. 동적으로 프로비저닝된 것은 지원되지 않는다.

hostPath 볼륨에 비해 local 볼륨은 수동으로 파드를 노드에 예약하지 않고도 내구성과 휴대성을 갖춘 방식으로 사용된다. 시스템은 퍼시스턴트볼륨의 노드 어피니티를 확인하여 볼륨의 노드 제약 조건을 인식한다.

그러나 local 볼륨은 여전히 기본 노드의 가용성을 따르며 모든 애플리케이션에 적합하지는 않는다. 만약 노드가 비정상 상태가 되면 local 볼륨도 접근할 수 없게 되고, 파드를 실행할 수 없게 된다. local 볼륨을 사용하는 애플리케이션은 기본 디스크의 내구 특성에 따라 이러한 감소되는 가용성과 데이터 손실 가능성도 허용할 수 있어야 한다.

다음의 예시는 local 볼륨과 nodeAffinity 를 사용하는 퍼시스턴트볼륨을 보여준다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

local 볼륨을 사용하는 경우 퍼시스턴트볼륨 nodeAffinity 를 설정해야 합니다. 쿠버네티스 스케줄러는 퍼시스턴트볼륨 nodeAffinity 를 사용하여 파드를 올바른 노드로 스케줄한다.

퍼시스턴트볼륨의 volumeMode 을 "Block" (기본값인 "Filesystem"을 대신해서)으로 설정하면 로컬 볼륨을 원시 블록 장치로 노출할 수 있다.

로컬 볼륨을 사용할 때는 volumeBindingModeWaitForFirstConsumer 로 설정된 스토리지클래스(StorageClass)를 생성하는 것을 권장한다. 자세한 내용은 local 스토리지클래스(StorageClas) 예제를 참고한다. 볼륨 바인딩을 지연시키는 것은 퍼시스턴트볼륨클래임 바인딩 결정도 노드 리소스 요구사항, 노드 셀렉터, 파드 어피니티 그리고 파드 안티 어피니티와 같이 파드가 가질 수 있는 다른 노드 제약 조건으로 평가되도록 만든다.

로컬 볼륨 라이프사이클의 향상된 관리를 위해 외부 정적 프로비저너를 별도로 실행할 수 있다. 이 프로비저너는 아직 동적 프로비저닝을 지원하지 않는 것을 참고한다. 외부 로컬 프로비저너를 실행하는 방법에 대한 예시는 로컬 볼륨 프로비저너 사용자 가이드를 본다.

nfs

nfs 볼륨을 사용하면 기존 NFS (네트워크 파일 시스템) 볼륨을 파드에 마운트 할수 있다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 nfs 볼륨의 내용은 유지되고, 볼륨은 그저 마운트 해제만 된다. 이 의미는 NFS 볼륨에 데이터를 미리 채울 수 있으며, 파드 간에 데이터를 공유할 수 있다는 뜻이다. NFS는 여러 작성자가 동시에 마운트할 수 있다.

더 자세한 내용은 NFS 예시를 본다.

persistentVolumeClaim

persistentVolumeClaim 볼륨은 퍼시스턴트볼륨을 파드에 마운트하는데 사용한다. 퍼시스턴트볼륨클레임은 사용자가 특정 클라우드 환경의 세부 내용을 몰라도 내구성이있는 스토리지 (GCE 퍼시스턴트디스크 또는 iSCSI 볼륨와 같은)를 "클레임" 할 수 있는 방법이다.

더 자세한 내용은 퍼시스턴트볼륨 예시를 본다.

portworxVolume

portworxVolume 은 쿠버네티스와 하이퍼컨버지드(hyperconverged)를 실행하는 탄력적인 블록 스토리지 계층이다. Portworx는 서버의 스토리지를 핑거프린팅하고(fingerprints), 기능에 기반하여 계층화하고, 그리고 여러 서버에 걸쳐 용량을 집계한다. Portworx는 가상 머신 내 게스트 또는 베어 메탈 리눅스 노드 위에서 실행된다.

portworxVolume 은 쿠버네티스를 통해 동적으로 생성되거나 사전에 프로비전할 수 있으며 쿠버네티스 파드 내에서 참조할 수 있다. 다음은 사전에 프로비저닝된 Portworx 볼륨을 참조하는 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # 이 Portworx 볼륨은 이미 존재해야 한다.
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

자세한 내용은 Portworx 볼륨 예제를 참고한다.

projected

Projected 볼륨은 여러 기존 볼륨 소스를 동일한 디렉터리에 매핑한다.

현재, 다음 유형의 볼륨 소스를 프로젝티드한다.

모든 소스는 파드와 동일한 네임스페이스에 있어야 한다. 더 자세한 내용은 올인원 볼륨 디자인 문서를 본다.

시크릿, 다운워드 API 그리고 컨피그맵이 있는 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config

구성 예시: 기본값이 아닌 소유권 모드 설정의 시크릿

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - secret:
          name: mysecret2
          items:
            - key: password
              path: my-group/my-password
              mode: 511

각각의 projected 볼륨 소스는 source 아래 사양 목록에 있다. 파라미터는 두 가지 예외를 제외하고 거의 동일하다.

  • 시크릿의 경우 secretName 필드는 컨피그맵 이름과 일치하도록 name 으로 변경되었다.
  • defaultMode 는 각각의 볼륨 소스에 대해 projected 수준에서만 지정할 수 있다. 그러나 위에서 설명한 것처럼 각각의 개별 projection 에 대해 mode 를 명시적으로 설정할 수 있다.

TokenRequestProjection 기능이 활성화 되면, 현재 서비스 어카운트에 대한 토큰을 파드의 지정된 경로에 주입할 수 있다. 예를 들면 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: sa-token-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: token-vol
      mountPath: "/service-account"
      readOnly: true
  volumes:
  - name: token-vol
    projected:
      sources:
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

예시 파드에 주입된 서비스 어카운트 토큰이 포함된 projected 볼륨이 있다. 이 토큰은 파드의 컨테이너에서 쿠버네티스 API 서버에 접근하는데 사용할 수 있다. audience 필드는 토큰에 의도하는 대상을 포함한다. 토큰 수령은 토큰 대상에 지정된 식별자로 자신을 식별해야 하며, 그렇지 않으면 토큰을 거부해야 한다. 이 필드는 선택 사항이며 기본값은 API 서버의 식별자이다.

expirationSeconds 는 서비스 어카운트 토큰의 예상 유효 기간이다. 기본값은 1시간이며 최소 10분(600초)이어야 한다. 관리자는 API 서버에 대해 --service-account-max-token-expiration 옵션을 지정해서 최대 값을 제한할 수도 있다. path 필드는 projected 볼륨의 마운트 위치에 대한 상대 경로를 지정한다.

quobyte (사용 중단됨)

quobyte 볼륨을 사용하면 기존 Quobyte 볼륨을 파드에 마운트할 수 있다.

Quobyte는 컨테이너 스토리지 인터페이스를 지원한다. CSI 는 쿠버네티스 내에서 Quobyte 볼륨을 사용하기 위해 권장하는 플러그인이다. Quobyte의 깃헙 프로젝트에는 예시와 함께 CSI를 사용해서 Quobyte를 배포하기 위한 사용 설명서가 있다.

rbd

rbd 볼륨을 사용하면 Rados Block Device(RBD) 볼륨을 파드에 마운트할 수 있다. 파드를 제거할 때 지워지는 emptyDir 와는 다르게 rbd 볼륨의 내용은 유지되고, 볼륨은 마운트 해제만 된다. 이 의미는 RBD 볼륨에 데이터를 미리 채울 수 있으며, 데이터를 공유할 수 있다는 것이다.

RBD의 특징은 여러 고객이 동시에 읽기 전용으로 마운트할 수 있다는 것이다. 즉, 데이터셋으로 볼륨을 미리 채운 다음, 필요한 만큼 많은 파드에서 병렬로 제공할수 있다. 불행하게도, RBD는 읽기-쓰기 모드에서 단일 고객만 마운트할 수 있다. 동시 쓰기는 허용되지 않는다.

더 자세한 내용은 RBD 예시를 참고한다.

secret

secret 볼륨은 암호와 같은 민감한 정보를 파드에 전달하는데 사용된다. 쿠버네티스 API에 시크릿을 저장하고 쿠버네티스에 직접적으로 연결하지 않고도 파드에서 사용할 수 있도록 파일로 마운트 할 수 있다. secret 볼륨은 tmpfs(RAM 기반 파일시스템)로 지원되기 때문에 비 휘발성 스토리지에 절대 기록되지 않는다.

더 자세한 내용은 시크릿 구성하기를 참고한다.

storageOS (사용 중단됨)

storageos 볼륨을 사용하면 기존 StorageOS 볼륨을 파드에 마운트할 수 있다.

StorageOS 는 쿠버네티스 환경에서 컨테이너로 실행되므로 쿠버네티스 클러스터의 모든 노드의 로컬 또는 연결된 스토리지에 접근할 수 있다. 노드 장애로부터 보호하기 위해 데이터를 복제할 수 있다. 씬(Thin) 프로비저닝과 압축은 활용률을 높이고 비용을 절감할 수 있게 한다.

StorageOS의 핵심은 컨테이너에 파일시스템을 통해 접근할 수 있는 블록 스토리지를 제공하는 것이다.

StorageOS 컨테이너는 64 비트 리눅스가 필요하고 추가적인 종속성이 없다. 무료 개발자 라이선스를 사용할 수 있다.

다음의 예시는 StorageOS를 사용한 파드 구성이다.

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: redis
    role: master
  name: test-storageos-redis
spec:
  containers:
    - name: master
      image: kubernetes/redis:v1
      env:
        - name: MASTER
          value: "true"
      ports:
        - containerPort: 6379
      volumeMounts:
        - mountPath: /redis-master-data
          name: redis-data
  volumes:
    - name: redis-data
      storageos:
        # `redis-vol01` 볼륨은 StorageOS에 `default` 네임스페이스로 있어야 한다.
        volumeName: redis-vol01
        fsType: ext4

StorageOS, 동적 프로비저닝과 퍼시스턴트 볼륨 클래임에 대한 더 자세한 정보는 StorageOS 예제를 참고한다.

vsphereVolume

vsphereVolume 은 vSphere VMDK 볼륨을 파드에 마운트하는데 사용된다. 볼륨을 마운트 해제해도 볼륨의 내용이 유지된다. VMFS와 VSAM 데이터스토어를 모두 지원한다.

VMDK 볼륨 생성하기

다음 중 하나를 선택해서 VMDK를 생성한다.

먼저 ESX에 ssh로 들어간 다음, 다음 명령을 사용해서 VMDK를 생성한다.

vmkfstools -c 2G /vmfs/volumes/DatastoreName/volumes/myDisk.vmdk

다음 명령을 사용해서 VMDK를 생성한다.

vmware-vdiskmanager -c -t 0 -s 40GB -a lsilogic myDisk.vmdk

vSphere VMDK 구성 예시

apiVersion: v1
kind: Pod
metadata:
  name: test-vmdk
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-vmdk
      name: test-volume
  volumes:
  - name: test-volume
    # 이 VMDK 볼륨은 이미 있어야 한다.
    vsphereVolume:
      volumePath: "[DatastoreName] volumes/myDisk"
      fsType: ext4

더 자세한 내용은 vSphere 볼륨 예제를 참고한다.

vSphere CSI 마이그레이션

FEATURE STATE: Kubernetes v1.19 [beta]

vsphereVolumeCSIMigration 기능이 활성화되면, 기존 인-트리 플러그인에서 csi.vsphere.vmware.com CSI 드라이버로 모든 플러그인 작업을 리디렉션한다. 이 기능을 사용하려면, vSphere CSI 드라이버가 클러스터에 설치되어야 하며 CSIMigrationCSIMigrationvSphere 기능 게이트가 활성화되어 있어야 한다.

또한 최소 vSphere vCenter/ESXi 버전은 7.0u1이고 최소 HW 버전은 VM 버전 15여야 한다.

vSphere CSI 마이그레이션 완료

FEATURE STATE: Kubernetes v1.19 [beta]

vsphereVolume 플러그인이 컨트롤러 관리자와 kubelet에 의해 로드되지 않도록 기능을 비활성화하려면, InTreePluginvSphereUnregister 기능 플래그를 true 로 설정해야 한다. 이를 위해서는 모든 워커 노드에 csi.vsphere.vmware.com CSI 드라이버를 설치해야 한다.

subPath 사용하기

때로는 단일 파드에서 여러 용도의 한 볼륨을 공유하는 것이 유용하다. volumeMounts.subPath 속성을 사용해서 root 대신 참조하는 볼륨 내의 하위 경로를 지정할 수 있다.

다음의 예시는 단일 공유 볼륨을 사용하여 LAMP 스택(리눅스 Apache MySQL PHP)이 있는 파드를 구성하는 방법을 보여준다. 이 샘플 subPath 구성은 프로덕션 용도로 권장되지 않는다.

PHP 애플리케이션의 코드와 자산은 볼륨의 html 폴더에 매핑되고 MySQL 데이터베이스는 볼륨의 mysql 폴더에 저장된다. 예를 들면 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

subPath를 확장된 환경 변수와 함께 사용하기

FEATURE STATE: Kubernetes v1.17 [stable]

subPathExpr 필드를 사용해서 다운워드 API 환경 변수로부터 subPath 디렉터리 이름을 구성한다. subPathsubPathExpr 속성은 상호 배타적이다.

이 예제는 PodsubPathExpr 을 사용해서 hostPath 볼륨 /var/log/pods 내에 pod1 디렉터리를 만든다. hostPath 볼륨은 downwardAPI 에서 Pod 이름을 사용한다. 호스트 디렉토리 /var/log/pods/pod1 은 컨테이너의 /logs 에 마운트된다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      subPathExpr: $(POD_NAME)
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods

리소스

emptyDir 볼륨의 스토리지 매체(디스크나 SSD와 같은)는 kubelet root 디렉터리(보통 /var/lib/kubelet)를 보유한 파일시스템의 매체에 의해 결정 된다. emptyDir 또는 hostPath 볼륨이 사용할 수 있는 공간의 크기는 제한이 없으며, 컨테이너 간 또는 파드 간 격리는 없다.

리소스 사양을 사용한 공간 요청에 대한 자세한 내용은 리소스 관리 방법을 참고한다.

아웃-오브-트리(out-of-tree) 볼륨 플러그인

아웃-오브-트리 볼륨 플러그인에는 컨테이너 스토리지 인터페이스(CSI) 그리고 FlexVolume이 포함된다. 이러한 플러그인을 사용하면 스토리지 벤더들은 플러그인 소스 코드를 쿠버네티스 리포지터리에 추가하지 않고도 사용자 정의 스토리지 플러그인을 만들 수 있다.

이전에는 모든 볼륨 플러그인이 "인-트리(in-tree)"에 있었다. "인-트리" 플러그인은 쿠버네티스 핵심 바이너리와 함께 빌드, 링크, 컴파일 및 배포되었다. 즉, 쿠버네티스(볼륨 플러그인)에 새로운 스토리지 시스템을 추가하려면 쿠버네티스 핵심 코드 리포지토리의 코드 확인이 필요했음을 의미한다.

CSI와 FlexVolume을 통해 쿠버네티스 코드 베이스와는 독립적으로 볼륨 플러그인을 개발하고, 쿠버네티스 클러스터의 확장으로 배포(설치) 할 수 있다.

아웃 오브 트리(out-of-tree) 볼륨 플러그인을 생성하려는 스토리지 벤더는 볼륨 플러그인 FAQ를 참조한다.

csi

컨테이너 스토리지 인터페이스(CSI)는 컨테이너 오케스트레이션 시스템(쿠버네티스와 같은)을 위한 표준 인터페이스를 정의하여 임의의 스토리지 시스템을 컨테이너 워크로드에 노출시킨다.

더 자세한 정보는 CSI 디자인 제안을 읽어본다.

CSI 호환 볼륨 드라이버가 쿠버네티스 클러스터에 배포되면 사용자는 csi 볼륨 유형을 사용해서 CSI 드라이버에 의해 노출된 볼륨에 연결하거나 마운트할 수 있다.

csi 볼륨은 세 가지 방법으로 파드에서 사용할 수 있다.

스토리지 관리자가 다음 필드를 사용해서 CSI 퍼시스턴트 볼륨을 구성할 수 있다.

  • driver: 사용할 볼륨 드라이버의 이름을 지정하는 문자열 값. 이 값은 CSI 사양에 정의된 CSI 드라이버가 GetPluginInfoResponse 에 반환하는 값과 일치해야 한다. 쿠버네티스에서 호출할 CSI 드라이버를 식별하고, CSI 드라이버 컴포넌트에서 CSI 드라이버에 속하는 PV 오브젝트를 식별하는데 사용한다.
  • volumeHandle: 볼륨을 식별하게 하는 고유한 문자열 값. 이 값은 CSI 사양에 정의된 CSI 드라이버가 CreateVolumeResponsevolume.id 필드에 반환하는 값과 일치해야 한다. 이 값은 볼륨을 참조할 때 CSI 볼륨 드라이버에 대한 모든 호출에 volume_id 값을 전달한다.
  • readOnly: 볼륨을 읽기 전용으로 "ControllerPublished" (연결)할지 여부를 나타내는 선택적인 불리언(boolean) 값. 기본적으로 false 이다. 이 값은 ControllerPublishVolumeRequestreadonly 필드를 통해 CSI 드라이버로 전달된다.
  • fsType: 만약 PV의 VolumeModeFilesystem 인 경우에 이 필드는 볼륨을 마운트하는 데 사용해야 하는 파일시스템을 지정하는 데 사용될 수 있다. 만약 볼륨이 포맷되지 않았고 포맷이 지원되는 경우, 이 값은 볼륨을 포맷하는데 사용된다. 이 값은 ControllerPublishVolumeRequest, NodeStageVolumeRequest 그리고 NodePublishVolumeRequestVolumeCapability 필드를 통해 CSI 드라이버로 전달된다.
  • volumeAttributes: 볼륨의 정적 속성을 지정하는 문자열과 문자열을 매핑한다. 이 매핑은 CSI 사양에 정의된 대로 CSI 드라이버의 CreateVolumeResponsevolume.attributes 필드에서 반환되는 매핑과 일치해야 한다. 이 매핑은 ControllerPublishVolumeRequest, NodeStageVolumeRequest, 그리고 NodePublishVolumeRequestvolume_context 필드를 통해 CSI 드라이버로 전달된다.
  • controllerPublishSecretRef: CSI의 ControllerPublishVolume 그리고 ControllerUnpublishVolume 호출을 완료하기 위해 CSI 드라이버에 전달하려는 민감한 정보가 포함된 시크릿 오브젝트에 대한 참조이다. 이 필드는 선택 사항이며, 시크릿이 필요하지 않은 경우 비어있을 수 있다. 만약 시크릿에 둘 이상의 시크릿이 포함된 경우에도 모든 시크릿이 전달된다.
  • nodeStageSecretRef: CSI의 NodeStageVolume 호출을 완료하기위해 CSI 드라이버에 전달하려는 민감한 정보가 포함 된 시크릿 오브젝트에 대한 참조이다. 이 필드는 선택 사항이며, 시크릿이 필요하지 않은 경우 비어있을 수 있다. 만약 시크릿에 둘 이상의 시크릿이 포함된 경우에도 모든 시크릿이 전달된다.
  • nodePublishSecretRef: CSI의 NodePublishVolume 호출을 완료하기위해 CSI 드라이버에 전달하려는 민감한 정보가 포함 된 시크릿 오브젝트에 대한 참조이다. 이 필드는 선택 사항이며, 시크릿이 필요하지 않은 경우 비어있을 수 있다. 만약 시크릿 오브젝트에 둘 이상의 시크릿이 포함된 경우에도 모든 시크릿이 전달된다.

CSI 원시(raw) 블록 볼륨 지원

FEATURE STATE: Kubernetes v1.18 [stable]

외부 CSI 드라이버가 있는 벤더들은 쿠버네티스 워크로드에서 원시(raw) 블록 볼륨 지원을 구현할 수 있다.

CSI 설정 변경 없이 평소와 같이 원시 블록 볼륨 지원으로 퍼시스턴트볼륨/퍼시스턴트볼륨클레임 설정을 할 수 있다.

CSI 임시(ephemeral) 볼륨

FEATURE STATE: Kubernetes v1.16 [beta]

파드 명세 내에서 CSI 볼륨을 직접 구성할 수 있다. 이 방식으로 지정된 볼륨은 임시 볼륨이며 파드가 다시 시작할 때 지속되지 않는다. 자세한 내용은 임시 볼륨을 참고한다.

CSI 드라이버의 개발 방법에 대한 더 자세한 정보는 쿠버네티스-csi 문서를 참조한다.

인-트리 플러그인으로부터 CSI 드라이버로 마이그레이션하기

FEATURE STATE: Kubernetes v1.17 [beta]

CSIMigration 기능이 활성화 되면 기존의 인-트리 플러그인에 대한 작업을 해당 CSI 플러그인(설치와 구성이 될 것으로 예상한)으로 유도한다. 결과적으로, 운영자는 인-트리 플러그인을 대체하는 CSI 드라이버로 전환할 때 기존 스토리지 클래스, 퍼시스턴트볼륨 또는 퍼시스턴트볼륨클레임(인-트리 플러그인 참조)에 대한 구성 변경을 수행할 필요가 없다.

지원되는 작업 및 기능은 프로비저닝/삭제, 연결/분리, 마운트/마운트 해제 그리고 볼륨 크기 재조정이 포함된다.

CSIMigration 을 지원하고 해당 CSI 드라이버가 구현된 인-트리 플러그인은 볼륨 유형들에 나열되어 있다.

flexVolume

FlexVolume은 버전 1.2(CSI 이전) 이후 쿠버네티스에 존재하는 아웃-오브-트리 플러그인 인터페이스이다. 이것은 exec 기반 모델을 사용해서 드라이버에 접속한다. FlexVolume 드라이버 바이너리 파일은 각각의 노드와 일부 경우에 컨트롤 플레인 노드의 미리 정의된 볼륨 플러그인 경로에 설치해야 한다.

파드는 flexvolume 인-트리 볼륨 플러그인을 통해 FlexVolume 드라이버와 상호 작용한다. 더 자세한 내용은 FlexVolume 예제를 참고한다.

마운트 전파(propagation)

마운트 전파를 통해 컨테이너가 마운트한 볼륨을 동일한 파드의 다른 컨테이너 또는 동일한 노드의 다른 파드로 공유할 수 있다.

볼륨 마운트 전파는 Container.volumeMountsmountPropagation 필드에 의해 제어된다. 그 값은 다음과 같다.

  • None - 이 볼륨 마운트는 호스트의 볼륨 또는 해당 서브디렉터리에 마운트된 것을 마운트 이후에 수신하지 않는다. 비슷한 방식으로, 컨테이너가 생성한 마운트는 호스트에서 볼 수 없다. 이것이 기본 모드이다.

    이 모드는 리눅스 커널 문서에 설명된 rshared 마운트 전파와 같다.

  • HostToContainer - 이 볼륨 마운트는 볼륨 또는 해당 서브디렉터리를 마운트한 정보를 수신한다.

    다시 말하면, 만약 호스트가 볼륨 마운트 내부에 다른 것을 마운트 하더라도 컨테이너가 마운트된 것을 볼 수 있다.

    마찬가지로 Bidirectional 마운트 전파가 있는 파드가 동일한 마운트가 된 경우에 파드에 HostToContainer 마운트 전파가 있는 컨테이너가 이를 볼 수 있다.

    이 모드는 리눅스 커널 문서에 설명된 rshared 마운트 전파와 같다.

  • Bidirectional - 이 볼륨 마운트는 HostToContainer 마운트와 동일하게 작동한다. 추가로 컨테이너에서 생성된 모든 볼륨 마운트는 동일한 볼륨을 사용하는 모든 파드의 모든 컨테이너와 호스트로 다시 전파된다.

    이 모드의 일반적인 유스 케이스로는 FlexVolume 또는 CSI 드라이버를 사용하는 파드 또는 hostPath 볼륨을 사용하는 호스트에 무언가를 마운트해야 하는 파드이다.

    이 모드는 리눅스 커널 문서에 설명된 rshared 마운트 전파와 같다.

구성

일부 배포판(CoreOS, RedHat/Centos, Ubuntu)에서 마운트 전파가 제대로 작동하려면 아래와 같이 도커에서의 마운트 공유를 올바르게 구성해야 한다.

도커의 systemd 서비스 파일을 편집한다. MountFlags 를 다음과 같이 설정한다.

MountFlags=shared

또는 MountFlags=slave 가 있으면 제거한다. 이후 도커 데몬을 재시작 한다.

sudo systemctl daemon-reload
sudo systemctl restart docker

다음 내용

퍼시스턴트 볼륨과 함께 워드프레스와 MySQL 배포하기의 예시를 따른다.

3.6.2 - 볼륨 스냅샷

쿠버네티스에서 스토리지 시스템 볼륨 스냅샷은 VolumeSnapshot 을 나타낸다. 이 문서는 이미 쿠버네티스 퍼시스턴트 볼륨에 대해 잘 알고 있다고 가정한다.

소개

API 리소스 PersistentVolumePersistentVolumeClaim 가 사용자 및 관리자가 볼륨을 프로비전할 때의 방법과 유사하게, VolumeSnapshotContentVolumeSnapshot API 리소스는 볼륨 스냅샷을 생성하기 위해 제공된다.

VolumeSnapshotContent 는 관리자가 프로버져닝한 클러스터 볼륨에서의 스냅샷이다. 퍼시스턴트볼륨이 클러스터 리소스인 것처럼 이것 또한 클러스터 리소스이다.

VolumeSnapshot 은 사용자가 볼륨의 스냅샷을 요청할 수 있는 방법이다. 이는 퍼시스턴트볼륨클레임과 유사하다.

VolumeSnapshotClass 을 사용하면 VolumeSnapshot 에 속한 다른 속성을 지정할 수 있다. 이러한 속성은 스토리지 시스템에의 동일한 볼륨에서 가져온 스냅샷마다 다를 수 있으므로 PersistentVolumeClaimStorageClass 를 사용하여 표현할 수는 없다.

볼륨 스냅샷은 쿠버네티스 사용자에게 완전히 새로운 볼륨을 생성하지 않고도 특정 시점에 볼륨의 콘텐츠를 복사하는 표준화된 방법을 제공한다. 예를 들어, 데이터베이스 관리자는 이 기능을 사용하여 수정 사항을 편집 또는 삭제하기 전에 데이터베이스를 백업할 수 있다.

사용자는 이 기능을 사용할 때 다음 사항을 알고 있어야 한다.

  • API 오브젝트인 VolumeSnapshot, VolumeSnapshotContent, VolumeSnapshotClass 는 핵심 API가 아닌, CRDs이다.
  • VolumeSnapshot 은 CSI 드라이버에서만 사용할 수 있다.
  • 쿠버네티스 팀은 VolumeSnapshot 의 배포 프로세스 일부로써, 컨트롤 플레인에 배포할 스냅샷 컨트롤러와 CSI 드라이버와 함께 배포할 csi-snapshotter라는 사이드카 헬퍼(helper) 컨테이너를 제공한다. 스냅샷 컨트롤러는 VolumeSnapshotVolumeSnapshotContent 오브젝트를 관찰하고 동적 프로비저닝에서 VolumeSnapshotContent 오브젝트의 생성 및 삭제를 할 수 있다.사이드카 csi-snapshotter는 VolumeSnapshotContent 오브젝트를 관찰하고 CSI 엔드포인트에 대해 CreateSnapshotDeleteSnapshot 을 트리거(trigger)한다.
  • 스냅샷 오브젝트에 대한 강화된 검증을 제공하는 검증 웹훅 서버도 있다. 이는 CSI 드라이버가 아닌 스냅샷 컨트롤러 및 CRD와 함께 쿠버네티스 배포판에 의해 설치되어야 한다. 스냅샷 기능이 활성화된 모든 쿠버네티스 클러스터에 설치해야 한다.
  • CSI 드라이버에서의 볼륨 스냅샷 기능 유무는 확실하지 않다. 볼륨 스냅샷 서포트를 제공하는 CSI 드라이버는 csi-snapshotter를 사용할 가능성이 높다. 자세한 사항은 CSI 드라이버 문서를 확인하면 된다.
  • CRDs 및 스냅샷 컨트롤러는 쿠버네티스 배포 시 설치된다.

볼륨 스냅샷 및 볼륨 스냅샷 컨텐츠의 라이프사이클

VolumeSnapshotContents 은 클러스터 리소스이다. VolumeSnapshots 은 이러한 리소스의 요청이다. VolumeSnapshotContentsVolumeSnapshots의 상호 작용은 다음과 같은 라이프사이클을 따른다.

프로비저닝 볼륨 스냅샷

스냅샷을 프로비저닝할 수 있는 방법에는 사전 프로비저닝 혹은 동적 프로비저닝의 두 가지가 있다: .

사전 프로비전

클러스터 관리자는 많은 VolumeSnapshotContents 을 생성한다. 그들은 클러스터 사용자들이 사용 가능한 스토리지 시스템의 실제 볼륨 스냅샷 세부 정보를 제공한다. 이것은 쿠버네티스 API에 있고 사용 가능하다.

동적

사전 프로비저닝을 사용하는 대신 퍼시스턴트볼륨클레임에서 스냅샷을 동적으로 가져오도록 요청할 수 있다. 볼륨스냅샷클래스는 스냅샷 사용 시 스토리지 제공자의 특정 파라미터를 명세한다.

바인딩

스냅샷 컨트롤러는 사전 프로비저닝과 동적 프로비저닝된 시나리오에서 VolumeSnapshot 오브젝트와 적절한 VolumeSnapshotContent 오브젝트와의 바인딩을 처리한다. 바인딩은 1:1 매핑이다.

사전 프로비저닝된 경우, 볼륨스냅샷은 볼륨스냅샷컨텐츠 오브젝트 생성이 요청될 때까지 바인드되지 않은 상태로 유지된다.

스냅샷 소스 보호로서의 퍼시스턴트 볼륨 클레임

이 보호의 목적은 스냅샷이 생성되는 동안 사용 중인 퍼시스턴트볼륨클레임 API 오브젝트가 시스템에서 지워지지 않게 하는 것이다(데이터 손실이 발생할 수 있기 때문에).

퍼시스턴트볼륨클레임이 스냅샷을 생성할 동안에는 해당 퍼시스턴트볼륨클레임은 사용 중인 상태이다. 스냅샷 소스로 사용 중인 퍼시스턴트볼륨클레임 API 오브젝트를 삭제한다면, 퍼시스턴트볼륨클레임 오브젝트는 즉시 삭제되지 않는다. 대신, 퍼시스턴트볼륨클레임 오브젝트 삭제는 스냅샷이 준비(readyToUse) 혹은 중단(aborted) 상태가 될 때까지 연기된다.

삭제

삭제는 VolumeSnapshot 를 삭제 시 트리거로 DeletionPolicy 가 실행된다. DeletionPolicyDelete 라면, 기본 스토리지 스냅샷이 VolumeSnapshotContent 오브젝트와 함께 삭제될 것이다. DeletionPolicyRetain 이라면, 기본 스트리지 스냅샷과 VolumeSnapshotContent 둘 다 유지된다.

볼륨 스냅샷

각각의 볼륨 스냅샷은 스펙과 상태를 포함한다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-test
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass
  source:
    persistentVolumeClaimName: pvc-test

persistentVolumeClaimName 은 스냅샷을 위한 퍼시스턴트볼륨클레임 데이터 소스의 이름이다. 이 필드는 동적 프로비저닝 스냅샷이 필요하다.

볼륨 스냅샷은 volumeSnapshotClassName 속성을 사용하여 볼륨스냅샷클래스의 이름을 지정하여 특정 클래스를 요청할 수 있다. 아무것도 설정하지 않으면, 사용 가능한 경우 기본 클래스가 사용될 것이다.

사전 프로비저닝된 스냅샷의 경우, 다음 예와 같이 volumeSnapshotContentName을 스냅샷 소스로 지정해야 한다. 사전 프로비저닝된 스냅샷에는 volumeSnapshotContentName 소스 필드가 필요하다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: test-snapshot
spec:
  source:
    volumeSnapshotContentName: test-content

볼륨 스냅샷 컨텐츠

각각의 볼륨스냅샷컨텐츠는 스펙과 상태를 포함한다. 동적 프로비저닝에서는, 스냅샷 공통 컨트롤러는 VolumeSnapshotContent 오브젝트를 생성한다. 예시는 다음과 같다.

apiVersion: snapshot.storage.k8s.io/v1beta1
kind: VolumeSnapshotContent
metadata:
  name: snapcontent-72d9a349-aacd-42d2-a240-d775650d2455
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    volumeHandle: ee0cfb94-f8d4-11e9-b2d8-0242ac110002
  volumeSnapshotClassName: csi-hostpath-snapclass
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default
    uid: 72d9a349-aacd-42d2-a240-d775650d2455

volumeHandle 은 스토리지 백엔드에서 생성되고 볼륨 생성 중에 CSI 드라이버가 반환하는 볼륨의 고유 식별자이다. 이 필드는 스냅샷을 동적 프로비저닝하는 데 필요하다. 이것은 스냅샷의 볼륨 소스를 지정한다.

사전 프로비저닝된 스냅샷의 경우, (클러스터 관리자로서) 다음과 같이 VolumeSnapshotContent 오브젝트를 작성해야 한다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default

snapshotHandle 은 스토리지 백엔드에서 생성된 볼륨 스냅샷의 고유 식별자이다. 이 필드는 사전 프로비저닝된 스냅샷에 필요하다. VolumeSnapshotContent 가 나타내는 스토리지 시스템의 CSI 스냅샷 id를 지정한다.

스냅샷을 위한 프로비저닝 볼륨

PersistentVolumeClaim 오브젝트의 dataSource 필드를 사용하여 스냅샷 데이터로 미리 채워진 새 볼륨을 프로비저닝할 수 있다.

보다 자세한 사항은 볼륨 스냅샷 및 스냅샷에서 볼륨 복원에서 확인할 수 있다.

3.6.3 - 퍼시스턴트 볼륨

이 페이지는 쿠버네티스의 퍼시스턴트 볼륨 의 현재 상태를 설명한다. 볼륨에 대해 익숙해지는 것을 추천한다.

소개

스토리지 관리는 컴퓨트 인스턴스 관리와는 별개의 문제다. 퍼시스턴트볼륨 서브시스템은 사용자 및 관리자에게 스토리지 사용 방법에서부터 스토리지가 제공되는 방법에 대한 세부 사항을 추상화하는 API를 제공한다. 이를 위해 퍼시스턴트볼륨 및 퍼시스턴트볼륨클레임이라는 두 가지 새로운 API 리소스를 소개한다.

퍼시스턴트볼륨 (PV)은 관리자가 프로비저닝하거나 스토리지 클래스를 사용하여 동적으로 프로비저닝한 클러스터의 스토리지이다. 노드가 클러스터 리소스인 것처럼 PV는 클러스터 리소스이다. PV는 Volumes와 같은 볼륨 플러그인이지만, PV를 사용하는 개별 파드와는 별개의 라이프사이클을 가진다. 이 API 오브젝트는 NFS, iSCSI 또는 클라우드 공급자별 스토리지 시스템 등 스토리지 구현에 대한 세부 정보를 담아낸다.

퍼시스턴트볼륨클레임 (PVC)은 사용자의 스토리지에 대한 요청이다. 파드와 비슷하다. 파드는 노드 리소스를 사용하고 PVC는 PV 리소스를 사용한다. 파드는 특정 수준의 리소스(CPU 및 메모리)를 요청할 수 있다. 클레임은 특정 크기 및 접근 모드를 요청할 수 있다(예: ReadWriteOnce, ReadOnlyMany 또는 ReadWriteMany로 마운트 할 수 있음. AccessModes 참고).

퍼시스턴트볼륨클레임을 사용하면 사용자가 추상화된 스토리지 리소스를 사용할 수 있지만, 다른 문제들 때문에 성능과 같은 다양한 속성을 가진 퍼시스턴트볼륨이 필요한 경우가 일반적이다. 클러스터 관리자는 사용자에게 해당 볼륨의 구현 방법에 대한 세부 정보를 제공하지 않고 크기와 접근 모드와는 다른 방식으로 다양한 퍼시스턴트볼륨을 제공할 수 있어야 한다. 이러한 요구에는 스토리지클래스 리소스가 있다.

실습 예제와 함께 상세한 내용을 참고하길 바란다.

볼륨과 클레임 라이프사이클

PV는 클러스터 리소스이다. PVC는 해당 리소스에 대한 요청이며 리소스에 대한 클레임 검사 역할을 한다. PV와 PVC 간의 상호 작용은 다음 라이프사이클을 따른다.

프로비저닝

PV를 프로비저닝 할 수 있는 두 가지 방법이 있다: 정적(static) 프로비저닝과 동적(dynamic) 프로비저닝

정적 프로비저닝

클러스터 관리자는 여러 PV를 만든다. 클러스터 사용자가 사용할 수 있는 실제 스토리지의 세부 사항을 제공한다. 이 PV들은 쿠버네티스 API에 존재하며 사용할 수 있다.

동적 프로비저닝

관리자가 생성한 정적 PV가 사용자의 퍼시스턴트볼륨클레임과 일치하지 않으면 클러스터는 PVC를 위해 특별히 볼륨을 동적으로 프로비저닝 하려고 시도할 수 있다. 이 프로비저닝은 스토리지클래스를 기반으로 한다. PVC는 스토리지 클래스를 요청해야 하며 관리자는 동적 프로비저닝이 발생하도록 해당 클래스를 생성하고 구성해야 한다. "" 클래스를 요청하는 클레임은 동적 프로비저닝을 효과적으로 비활성화한다.

스토리지 클래스를 기반으로 동적 스토리지 프로비저닝을 사용하려면 클러스터 관리자가 API 서버에서 DefaultStorageClass 어드미션 컨트롤러를 사용하도록 설정해야 한다. 예를 들어 API 서버 컴포넌트의 --enable-admission-plugins 플래그에 대한 쉼표로 구분되어 정렬된 값들의 목록 중에 DefaultStorageClass가 포함되어 있는지 확인하여 설정할 수 있다. API 서버 커맨드라인 플래그에 대한 자세한 정보는 kube-apiserver 문서를 확인하면 된다.

바인딩

사용자는 원하는 특정 용량의 스토리지와 특정 접근 모드로 퍼시스턴트볼륨클레임을 생성하거나 동적 프로비저닝의 경우 이미 생성한 상태다. 마스터의 컨트롤 루프는 새로운 PVC를 감시하고 일치하는 PV(가능한 경우)를 찾아 서로 바인딩한다. PV가 새 PVC에 대해 동적으로 프로비저닝된 경우 루프는 항상 해당 PV를 PVC에 바인딩한다. 그렇지 않으면 사용자는 항상 최소한 그들이 요청한 것을 얻지만 볼륨은 요청된 것을 초과할 수 있다. 일단 바인딩되면 퍼시스턴트볼륨클레임은 어떻게 바인딩되었는지 상관없이 배타적으로 바인딩된다. PVC 대 PV 바인딩은 일대일 매핑으로, 퍼시스턴트볼륨과 퍼시스턴트볼륨클레임 사이의 양방향 바인딩인 ClaimRef를 사용한다.

일치하는 볼륨이 없는 경우 클레임은 무한정 바인딩되지 않은 상태로 남아 있다. 일치하는 볼륨이 제공되면 클레임이 바인딩된다. 예를 들어 많은 수의 50Gi PV로 프로비저닝된 클러스터는 100Gi를 요청하는 PVC와 일치하지 않는다. 100Gi PV가 클러스터에 추가되면 PVC를 바인딩할 수 있다.

사용 중

파드는 클레임을 볼륨으로 사용한다. 클러스터는 클레임을 검사하여 바인딩된 볼륨을 찾고 해당 볼륨을 파드에 마운트한다. 여러 접근 모드를 지원하는 볼륨의 경우 사용자는 자신의 클레임을 파드에서 볼륨으로 사용할 때 원하는 접근 모드를 지정한다.

일단 사용자에게 클레임이 있고 그 클레임이 바인딩되면, 바인딩된 PV는 사용자가 필요로 하는 한 사용자에게 속한다. 사용자는 파드의 volumes 블록에 persistentVolumeClaim을 포함하여 파드를 스케줄링하고 클레임한 PV에 접근한다. 이에 대한 자세한 내용은 볼륨으로 클레임하기를 참고하길 바란다.

사용 중인 스토리지 오브젝트 ​​보호

사용 중인 스토리지 오브젝트 ​​보호 기능의 목적은 PVC에 바인딩된 파드와 퍼시스턴트볼륨(PV)이 사용 중인 퍼시스턴트볼륨클레임(PVC)을 시스템에서 삭제되지 않도록 하는 것이다. 삭제되면 이로 인해 데이터의 손실이 발생할 수 있기 때문이다.

사용자가 파드에서 활발하게 사용 중인 PVC를 삭제하면 PVC는 즉시 삭제되지 않는다. PVC가 더 이상 파드에서 적극적으로 사용되지 않을 때까지 PVC 삭제가 연기된다. 또한 관리자가 PVC에 바인딩된 PV를 삭제하면 PV는 즉시 삭제되지 않는다. PV가 더 이상 PVC에 바인딩되지 않을 때까지 PV 삭제가 연기된다.

PVC의 상태가 Terminating이고 Finalizers 목록에 kubernetes.io/pvc-protection이 포함되어 있으면 PVC가 보호된 것으로 볼 수 있다.

kubectl describe pvc hostpath
Name:          hostpath
Namespace:     default
StorageClass:  example-hostpath
Status:        Terminating
Volume:
Labels:        <none>
Annotations:   volume.beta.kubernetes.io/storage-class=example-hostpath
               volume.beta.kubernetes.io/storage-provisioner=example.com/hostpath
Finalizers:    [kubernetes.io/pvc-protection]
...

마찬가지로 PV 상태가 Terminating이고 Finalizers 목록에 kubernetes.io/pv-protection이 포함되어 있으면 PV가 보호된 것으로 볼 수 있다.

kubectl describe pv task-pv-volume
Name:            task-pv-volume
Labels:          type=local
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    standard
Status:          Terminating
Claim:
Reclaim Policy:  Delete
Access Modes:    RWO
Capacity:        1Gi
Message:
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /tmp/data
    HostPathType:
Events:            <none>

반환(Reclaiming)

사용자가 볼륨을 다 사용하고나면 리소스를 반환할 수 있는 API를 사용하여 PVC 오브젝트를 삭제할 수 있다. 퍼시스턴트볼륨의 반환 정책은 볼륨에서 클레임을 해제한 후 볼륨에 수행할 작업을 클러스터에 알려준다. 현재 볼륨에 대한 반환 정책은 Retain, Recycle, 그리고 Delete가 있다.

Retain(보존)

Retain 반환 정책은 리소스를 수동으로 반환할 수 있게 한다. 퍼시스턴트볼륨클레임이 삭제되면 퍼시스턴트볼륨은 여전히 존재하며 볼륨은 "릴리스 된" 것으로 간주된다. 그러나 이전 요청자의 데이터가 여전히 볼륨에 남아 있기 때문에 다른 요청에 대해서는 아직 사용할 수 없다. 관리자는 다음 단계에 따라 볼륨을 수동으로 반환할 수 있다.

  1. 퍼시스턴트볼륨을 삭제한다. PV가 삭제된 후에도 외부 인프라(예: AWS EBS, GCE PD, Azure Disk 또는 Cinder 볼륨)의 관련 스토리지 자산이 존재한다.
  2. 관련 스토리지 자산의 데이터를 수동으로 삭제한다.
  3. 연결된 스토리지 자산을 수동으로 삭제한다.

동일한 스토리지 자산을 재사용하려는 경우, 동일한 스토리지 자산 정의로 새 퍼시스턴트볼륨을 생성한다.

Delete(삭제)

Delete 반환 정책을 지원하는 볼륨 플러그인의 경우, 삭제는 쿠버네티스에서 퍼시스턴트볼륨 오브젝트와 외부 인프라(예: AWS EBS, GCE PD, Azure Disk 또는 Cinder 볼륨)의 관련 스토리지 자산을 모두 삭제한다. 동적으로 프로비저닝된 볼륨은 스토리지클래스의 반환 정책을 상속하며 기본값은 Delete이다. 관리자는 사용자의 기대에 따라 스토리지클래스를 구성해야 한다. 그렇지 않으면 PV를 생성한 후 PV를 수정하거나 패치해야 한다. 퍼시스턴트볼륨의 반환 정책 변경을 참고하길 바란다.

Recycle(재활용)

기본 볼륨 플러그인에서 지원하는 경우 Recycle 반환 정책은 볼륨에서 기본 스크럽(rm -rf /thevolume/*)을 수행하고 새 클레임에 다시 사용할 수 있도록 한다.

그러나 관리자는 레퍼런스에 설명된 대로 쿠버네티스 컨트롤러 관리자 커맨드라인 인자(command line arguments)를 사용하여 사용자 정의 재활용 파드 템플릿을 구성할 수 있다. 사용자 정의 재활용 파드 템플릿에는 아래 예와 같이 volumes 명세가 포함되어야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "k8s.gcr.io/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

그러나 volumes 부분의 사용자 정의 재활용 파드 템플릿에 지정된 특정 경로는 재활용되는 볼륨의 특정 경로로 바뀐다.

퍼시스턴트볼륨 예약

컨트롤 플레인은 클러스터에서 퍼시스턴트볼륨클레임을 일치하는 퍼시스턴트볼륨에 바인딩할 수 있다. 그러나, PVC를 특정 PV에 바인딩하려면, 미리 바인딩해야 한다.

퍼시스턴트볼륨클레임에서 퍼시스턴트볼륨을 지정하여, 특정 PV와 PVC 간의 바인딩을 선언한다. 퍼시스턴트볼륨이 존재하고 claimRef 필드를 통해 퍼시스턴트볼륨클레임을 예약하지 않은 경우, 퍼시스턴트볼륨 및 퍼시스턴트볼륨클레임이 바인딩된다.

바인딩은 노드 선호도(affinity)를 포함하여 일부 볼륨 일치(matching) 기준과 관계없이 발생한다. 컨트롤 플레인은 여전히 스토리지 클래스, 접근 모드 및 요청된 스토리지 크기가 유효한지 확인한다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: foo
spec:
  storageClassName: "" # 빈 문자열은 명시적으로 설정해야 하며 그렇지 않으면 기본 스토리지클래스가 설정됨
  volumeName: foo-pv
  ...

이 메서드는 퍼시스턴트볼륨에 대한 바인딩 권한을 보장하지 않는다. 다른 퍼시스턴트볼륨클레임에서 지정한 PV를 사용할 수 있는 경우, 먼저 해당 스토리지 볼륨을 예약해야 한다. PV의 claimRef 필드에 관련 퍼시스턴트볼륨클레임을 지정하여 다른 PVC가 바인딩할 수 없도록 한다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
spec:
  storageClassName: ""
  claimRef:
    name: foo-pvc
    namespace: foo
  ...

이는 기존 PV를 재사용하는 경우를 포함하여 claimPolicyRetain 으로 설정된 퍼시스턴트볼륨을 사용하려는 경우에 유용하다.

퍼시스턴트 볼륨 클레임 확장

FEATURE STATE: Kubernetes v1.11 [beta]

이제 퍼시스턴트볼륨클레임(PVC) 확장 지원이 기본적으로 활성화되어 있다. 다음 유형의 볼륨을 확장할 수 있다.

  • gcePersistentDisk
  • awsElasticBlockStore
  • Cinder
  • glusterfs
  • rbd
  • Azure File
  • Azure Disk
  • Portworx
  • FlexVolumes
  • CSI

스토리지 클래스의 allowVolumeExpansion 필드가 true로 설정된 경우에만 PVC를 확장할 수 있다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gluster-vol-default
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://192.168.10.100:8080"
  restuser: ""
  secretNamespace: ""
  secretName: ""
allowVolumeExpansion: true

PVC에 대해 더 큰 볼륨을 요청하려면 PVC 오브젝트를 수정하여 더 큰 용량을 지정한다. 이는 기본 퍼시스턴트볼륨을 지원하는 볼륨의 확장을 트리거한다. 클레임을 만족시키기 위해 새로운 퍼시스턴트볼륨이 생성되지 않고 기존 볼륨의 크기가 조정된다.

CSI 볼륨 확장

FEATURE STATE: Kubernetes v1.16 [beta]

CSI 볼륨 확장 지원은 기본적으로 활성화되어 있지만 볼륨 확장을 지원하려면 특정 CSI 드라이버도 필요하다. 자세한 내용은 특정 CSI 드라이버 문서를 참고한다.

파일시스템을 포함하는 볼륨 크기 조정

파일시스템이 XFS, Ext3 또는 Ext4 인 경우에만 파일시스템을 포함하는 볼륨의 크기를 조정할 수 있다.

볼륨에 파일시스템이 포함된 경우 새 파드가 ReadWrite 모드에서 퍼시스턴트볼륨클레임을 사용하는 경우에만 파일시스템의 크기가 조정된다. 파일시스템 확장은 파드가 시작되거나 파드가 실행 중이고 기본 파일시스템이 온라인 확장을 지원할 때 수행된다.

FlexVolumes는 RequiresFSResize 기능으로 드라이버가 true로 설정된 경우 크기 조정을 허용한다. FlexVolume은 파드 재시작 시 크기를 조정할 수 있다.

사용 중인 퍼시스턴트볼륨클레임 크기 조정

FEATURE STATE: Kubernetes v1.15 [beta]

이 경우 기존 PVC를 사용하는 파드 또는 디플로이먼트를 삭제하고 다시 만들 필요가 없다. 파일시스템이 확장되자마자 사용 중인 PVC가 파드에서 자동으로 사용 가능하다. 이 기능은 파드나 디플로이먼트에서 사용하지 않는 PVC에는 영향을 미치지 않는다. 확장을 완료하기 전에 PVC를 사용하는 파드를 만들어야 한다.

다른 볼륨 유형과 비슷하게 FlexVolume 볼륨도 파드에서 사용 중인 경우 확장할 수 있다.

볼륨 확장 시 오류 복구

기본 스토리지 확장에 실패하면, 클러스터 관리자가 수동으로 퍼시스턴트 볼륨 클레임(PVC) 상태를 복구하고 크기 조정 요청을 취소할 수 있다. 그렇지 않으면, 컨트롤러가 관리자 개입 없이 크기 조정 요청을 계속해서 재시도한다.

  1. 퍼시스턴트볼륨클레임(PVC)에 바인딩된 퍼시스턴트볼륨(PV)을 Retain 반환 정책으로 표시한다.
  2. PVC를 삭제한다. PV에는 Retain 반환 정책이 있으므로 PVC를 재생성할 때 데이터가 손실되지 않는다.
  3. 새 PVC를 바인딩할 수 있도록 PV 명세에서 claimRef 항목을 삭제한다. 그러면 PV가 Available 상태가 된다.
  4. PV 보다 작은 크기로 PVC를 다시 만들고 PVC의 volumeName 필드를 PV 이름으로 설정한다. 이것은 새 PVC를 기존 PV에 바인딩해야 한다.
  5. PV의 반환 정책을 복원하는 것을 잊지 않는다.

퍼시스턴트 볼륨의 유형

퍼시스턴트볼륨 유형은 플러그인으로 구현된다. 쿠버네티스는 현재 다음의 플러그인을 지원한다.

  • awsElasticBlockStore - AWS Elastic Block Store (EBS)
  • azureDisk - Azure Disk
  • azureFile - Azure File
  • cephfs - CephFS 볼륨
  • csi - 컨테이너 스토리지 인터페이스 (CSI)
  • fc - Fibre Channel (FC) 스토리지
  • flexVolume - FlexVolume
  • gcePersistentDisk - GCE Persistent Disk
  • glusterfs - Glusterfs 볼륨
  • hostPath - HostPath 볼륨 (단일 노드 테스트 전용. 다중-노드 클러스터에서 작동하지 않음. 대신 로컬 볼륨 사용 고려)
  • iscsi - iSCSI (SCSI over IP) 스토리지
  • local - 노드에 마운트된 로컬 스토리지 디바이스
  • nfs - 네트워크 파일 시스템 (NFS) 스토리지
  • portworxVolume - Portworx 볼륨
  • rbd - Rados Block Device (RBD) 볼륨
  • vsphereVolume - vSphere VMDK 볼륨

아래의 PersistentVolume 타입은 사용 중단되었다. 이 말인 즉슨, 지원은 여전히 제공되지만 추후 쿠버네티스 릴리스에서는 삭제될 예정이라는 것이다.

  • cinder - Cinder (오픈스택 블록 스토리지) (v1.18에서 사용 중단)
  • flocker - Flocker 스토리지 (v1.22에서 사용 중단)
  • quobyte - Quobyte 볼륨 (v1.22에서 사용 중단)
  • storageos - StorageOS 볼륨 (v1.22에서 사용 중단)

이전 쿠버네티스 버전은 아래의 인-트리 PersistentVolume 타입도 지원했었다.

  • photonPersistentDisk - Photon 컨트롤러 퍼시스턴트 디스크. (v1.15 이후 사용 불가)
  • scaleIO - ScaleIO 볼륨 (v1.21 이후 사용 불가)

퍼시스턴트 볼륨

각 PV에는 스펙과 상태(볼륨의 명세와 상태)가 포함된다. 퍼시스턴트볼륨 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

용량

일반적으로 PV는 특정 저장 용량을 가진다. 이것은 PV의 capacity 속성을 사용하여 설정된다. capacity가 사용하는 단위를 이해하려면 쿠버네티스 리소스 모델을 참고한다.

현재 스토리지 용량 크기는 설정하거나 요청할 수 있는 유일한 리소스이다. 향후 속성에 IOPS, 처리량 등이 포함될 수 있다.

볼륨 모드

FEATURE STATE: Kubernetes v1.18 [stable]

쿠버네티스는 퍼시스턴트볼륨의 두 가지 volumeModesFilesystemBlock을 지원한다.

volumeMode는 선택적 API 파라미터이다. FilesystemvolumeMode 파라미터가 생략될 때 사용되는 기본 모드이다.

volumeMode: Filesystem이 있는 볼륨은 파드의 디렉터리에 마운트 된다. 볼륨이 장치에 의해 지원되고 그 장치가 비어 있으면 쿠버네티스는 장치를 처음 마운트하기 전에 장치에 파일시스템을 만든다.

볼륨을 원시 블록 장치로 사용하려면 volumeMode의 값을 Block으로 설정할 수 있다. 이러한 볼륨은 파일시스템이 없는 블록 장치로 파드에 제공된다. 이 모드는 파드와 볼륨 사이에 파일시스템 계층 없이도 볼륨에 액세스하는 가장 빠른 방법을 파드에 제공하는 데 유용하다. 반면에 파드에서 실행되는 애플리케이션은 원시 블록 장치를 처리하는 방법을 알아야 한다. 파드에서 volumeMode: Block으로 볼륨을 사용하는 방법에 대한 예는 원시 블록 볼륨 지원를 참조하십시오.

접근 모드

리소스 제공자가 지원하는 방식으로 호스트에 퍼시스턴트볼륨을 마운트할 수 있다. 아래 표에서 볼 수 있듯이 제공자들은 서로 다른 기능을 가지며 각 PV의 접근 모드는 해당 볼륨에서 지원하는 특정 모드로 설정된다. 예를 들어 NFS는 다중 읽기/쓰기 클라이언트를 지원할 수 있지만 특정 NFS PV는 서버에서 읽기 전용으로 export할 수 있다. 각 PV는 특정 PV의 기능을 설명하는 자체 접근 모드 셋을 갖는다.

접근 모드는 다음과 같다.

ReadWriteOnce
하나의 노드에서 해당 볼륨이 읽기-쓰기로 마운트 될 수 있다. ReadWriteOnce 접근 모드에서도 파트가 동일 노드에서 구동되는 경우에는 복수의 파드에서 볼륨에 접근할 수 있다.
ReadWriteMany
볼륨이 다수의 노드에서 읽기 전용으로 마운트 될 수 있다.
ReadWriteOncePod
볼륨이 단일 파드에서 읽기-쓰기로 마운트될 수 있다. 전체 클러스터에서 단 하나의 파드만 해당 PVC를 읽거나 쓸 수 있어야하는 경우 ReadWriteOncePod 접근 모드를 사용한다. 이 기능은 CSI 볼륨과 쿠버네티스 버전 1.22+ 에서만 지원된다.

퍼시스턴트 볼륨에 대한 단일 파드 접근 모드 소개 블로그 기사에서 이에 대해 보다 자세한 내용을 다룬다.

CLI에서 접근 모드는 다음과 같이 약어로 표시된다.

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany
  • RWOP - ReadWriteOncePod

중요! 볼륨이 여러 접근 모드를 지원하더라도 한 번에 하나의 접근 모드를 사용하여 마운트할 수 있다. 예를 들어 GCEPersistentDisk는 하나의 노드가 ReadWriteOnce로 마운트하거나 여러 노드가 ReadOnlyMany로 마운트할 수 있지만 동시에는 불가능하다.

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany ReadWriteOncePod
AWSElasticBlockStore - - -
AzureFile -
AzureDisk - - -
CephFS -
Cinder - - -
CSI depends on the driver depends on the driver depends on the driver depends on the driver
FC - -
FlexVolume depends on the driver -
Flocker - - -
GCEPersistentDisk - -
Glusterfs -
HostPath - - -
iSCSI - -
Quobyte -
NFS -
RBD - -
VsphereVolume - - (works when Pods are collocated) -
PortworxVolume - -
StorageOS - - -

클래스

PV는 storageClassName 속성을 스토리지클래스의 이름으로 설정하여 지정하는 클래스를 가질 수 있다. 특정 클래스의 PV는 해당 클래스를 요청하는 PVC에만 바인딩될 수 있다. storageClassName이 없는 PV에는 클래스가 없으며 특정 클래스를 요청하지 않는 PVC에만 바인딩할 수 있다.

이전에는 volume.beta.kubernetes.io/storage-class 어노테이션이 storageClassName 속성 대신 사용되었다. 이 어노테이션은 아직까지는 사용할 수 있지만, 향후 쿠버네티스 릴리스에서 완전히 사용 중단(deprecated)이 될 예정이다.

반환 정책

현재 반환 정책은 다음과 같다.

  • Retain(보존) -- 수동 반환
  • Recycle(재활용) -- 기본 스크럽 (rm -rf /thevolume/*)
  • Delete(삭제) -- AWS EBS, GCE PD, Azure Disk 또는 OpenStack Cinder 볼륨과 같은 관련 스토리지 자산이 삭제됨

현재 NFS 및 HostPath만 재활용을 지원한다. AWS EBS, GCE PD, Azure Disk 및 Cinder 볼륨은 삭제를 지원한다.

마운트 옵션

쿠버네티스 관리자는 퍼시스턴트 볼륨이 노드에 마운트될 때 추가 마운트 옵션을 지정할 수 있다.

다음 볼륨 유형은 마운트 옵션을 지원한다.

  • AWSElasticBlockStore
  • AzureDisk
  • AzureFile
  • CephFS
  • Cinder (OpenStack 블록 스토리지)
  • GCEPersistentDisk
  • Glusterfs
  • NFS
  • Quobyte Volumes
  • RBD (Ceph Block Device)
  • StorageOS
  • VsphereVolume
  • iSCSI

마운트 옵션의 유효성이 검사되지 않는다. 마운트 옵션이 유효하지 않으면, 마운트가 실패한다.

이전에는 mountOptions 속성 대신 volume.beta.kubernetes.io/mount-options 어노테이션이 사용되었다. 이 어노테이션은 아직까지는 사용할 수 있지만, 향후 쿠버네티스 릴리스에서 완전히 사용 중단(deprecated)이 될 예정이다.

노드 어피니티(affinity)

PV는 노드 어피니티를 지정하여 이 볼륨에 접근할 수 있는 노드를 제한하는 제약 조건을 정의할 수 있다. PV를 사용하는 파드는 노드 어피니티에 의해 선택된 노드로만 스케줄링된다. 노드 어피니티를 명기하기 위해서는, PV의 .specnodeAffinity를 설정한다. 퍼시스턴트볼륨 API 레퍼런스에 해당 필드에 대해 보다 자세한 내용이 있다.

단계(Phase)

볼륨은 다음 단계 중 하나이다.

  • Available(사용 가능) -– 아직 클레임에 바인딩되지 않은 사용할 수 있는 리소스
  • Bound(바인딩) –- 볼륨이 클레임에 바인딩됨
  • Released(릴리스) –- 클레임이 삭제되었지만 클러스터에서 아직 리소스를 반환하지 않음
  • Failed(실패) –- 볼륨이 자동 반환에 실패함

CLI는 PV에 바인딩된 PVC의 이름을 표시한다.

퍼시스턴트볼륨클레임

각 PVC에는 스펙과 상태(클레임의 명세와 상태)가 포함된다. 퍼시스턴트볼륨클레임 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

접근 모드

클레임은 특정 접근 모드로 저장소를 요청할 때 볼륨과 동일한 규칙을 사용한다.

볼륨 모드

클레임은 볼륨과 동일한 규칙을 사용하여 파일시스템 또는 블록 장치로 볼륨을 사용함을 나타낸다.

리소스

파드처럼 클레임은 특정 수량의 리소스를 요청할 수 있다. 이 경우는 스토리지에 대한 요청이다. 동일한 리소스 모델이 볼륨과 클레임 모두에 적용된다.

셀렉터

클레임은 볼륨 셋을 추가로 필터링하기 위해 레이블 셀렉터를 지정할 수 있다. 레이블이 셀렉터와 일치하는 볼륨만 클레임에 바인딩할 수 있다. 셀렉터는 두 개의 필드로 구성될 수 있다.

  • matchLabels - 볼륨에 이 값의 레이블이 있어야함
  • matchExpressions - 키, 값의 목록, 그리고 키와 값에 관련된 연산자를 지정하여 만든 요구 사항 목록. 유효한 연산자에는 In, NotIn, Exists 및 DoesNotExist가 있다.

matchLabelsmatchExpressions의 모든 요구 사항이 AND 조건이다. 일치하려면 모두 충족해야 한다.

클래스

클레임은 storageClassName 속성을 사용하여 스토리지클래스의 이름을 지정하여 특정 클래스를 요청할 수 있다. 요청된 클래스의 PV(PVC와 동일한 storageClassName을 갖는 PV)만 PVC에 바인딩될 수 있다.

PVC는 반드시 클래스를 요청할 필요는 없다. storageClassName""로 설정된 PVC는 항상 클래스가 없는 PV를 요청하는 것으로 해석되므로 클래스가 없는 PV(어노테이션이 없거나 ""와 같은 하나의 셋)에만 바인딩될 수 있다. storageClassName이 없는 PVC는 DefaultStorageClass 어드미션 플러그인이 켜져 있는지 여부에 따라 동일하지 않으며 클러스터에 따라 다르게 처리된다.

  • 어드미션 플러그인이 켜져 있으면 관리자가 기본 스토리지클래스를 지정할 수 있다. storageClassName이 없는 모든 PVC는 해당 기본값의 PV에만 바인딩할 수 있다. 기본 스토리지클래스 지정은 스토리지클래스 오브젝트에서 어노테이션 storageclass.kubernetes.io/is-default-classtrue로 설정하여 수행된다. 관리자가 기본값을 지정하지 않으면 어드미션 플러그인이 꺼져 있는 것처럼 클러스터가 PVC 생성에 응답한다. 둘 이상의 기본값이 지정된 경우 어드미션 플러그인은 모든 PVC 생성을 금지한다.
  • 어드미션 플러그인이 꺼져 있으면 기본 스토리지클래스에 대한 기본값 자체가 없다. storageClassName이 없는 모든 PVC는 클래스가 없는 PV에만 바인딩할 수 있다. 이 경우 storageClassName이 없는 PVC는 storageClassName""로 설정된 PVC와 같은 방식으로 처리된다.

설치 방법에 따라 설치 중에 애드온 관리자가 기본 스토리지클래스를 쿠버네티스 클러스터에 배포할 수 있다.

PVC가 스토리지클래스를 요청하는 것 외에도 selector를 지정하면 요구 사항들이 AND 조건으로 동작한다. 요청된 클래스와 요청된 레이블이 있는 PV만 PVC에 바인딩될 수 있다.

이전에는 volume.beta.kubernetes.io/storage-class 어노테이션이 storageClassName 속성 대신 사용되었다. 이 어노테이션은 아직까지는 사용할 수 있지만, 향후 쿠버네티스 릴리스에서는 지원되지 않는다.

볼륨으로 클레임하기

클레임을 볼륨으로 사용해서 파드가 스토리지에 접근한다. 클레임은 클레임을 사용하는 파드와 동일한 네임스페이스에 있어야 한다. 클러스터는 파드의 네임스페이스에서 클레임을 찾고 이를 사용하여 클레임과 관련된 퍼시스턴트볼륨을 얻는다. 그런 다음 볼륨이 호스트와 파드에 마운트된다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

네임스페이스에 대한 참고 사항

퍼시스턴트볼륨 바인딩은 배타적이며, 퍼시스턴트볼륨클레임은 네임스페이스 오브젝트이므로 "다중" 모드(ROX, RWX)를 사용한 클레임은 하나의 네임스페이스 내에서만 가능하다.

hostPath 유형의 퍼시스턴트볼륨

hostPath 퍼시스턴트볼륨은 노드의 파일이나 디렉터리를 사용하여 네트워크 연결 스토리지를 에뮬레이션한다. hostPath 유형 볼륨의 예를 참고한다.

원시 블록 볼륨 지원

FEATURE STATE: Kubernetes v1.18 [stable]

다음 볼륨 플러그인에 해당되는 경우 동적 프로비저닝을 포함하여 원시 블록 볼륨을 지원한다.

  • AWSElasticBlockStore
  • AzureDisk
  • CSI
  • FC (파이버 채널)
  • GCEPersistentDisk
  • iSCSI
  • Local volume
  • OpenStack Cinder
  • RBD (Ceph Block Device)
  • VsphereVolume

원시 블록 볼륨을 사용하는 퍼시스턴트볼륨

apiVersion: v1
kind: PersistentVolume
metadata:
  name: block-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  persistentVolumeReclaimPolicy: Retain
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

원시 블록 볼륨을 요청하는 퍼시스턴트볼륨클레임

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi

컨테이너에 원시 블록 장치 경로를 추가하는 파드 명세

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: [ "tail -f /dev/null" ]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: block-pvc

블록 볼륨 바인딩

사용자가 퍼시스턴트볼륨클레임 스펙에서 volumeMode 필드를 사용하여 이를 나타내는 원시 블록 볼륨을 요청하는 경우 바인딩 규칙은 스펙의 일부분으로 이 모드를 고려하지 않은 이전 릴리스에 비해 약간 다르다. 사용자와 관리자가 원시 블록 장치를 요청하기 위해 지정할 수 있는 가능한 조합의 표가 아래 나열되어 있다. 이 테이블은 볼륨이 바인딩되는지 여부를 나타낸다. 정적 프로비저닝된 볼륨에 대한 볼륨 바인딩 매트릭스이다.

PV volumeMode PVC volumeMode Result
지정되지 않음 지정되지 않음 BIND
지정되지 않음 Block NO BIND
지정되지 않음 Filesystem BIND
Block 지정되지 않음 NO BIND
Block Block BIND
Block Filesystem NO BIND
Filesystem Filesystem BIND
Filesystem Block NO BIND
Filesystem 지정되지 않음 BIND

볼륨 스냅샷 및 스냅샷 지원에서 볼륨 복원

FEATURE STATE: Kubernetes v1.20 [stable]

볼륨 스냅 샷은 아웃-오브-트리 CSI 볼륨 플러그인만 지원한다. 자세한 내용은 볼륨 스냅샷을 참조한다. 인-트리 볼륨 플러그인은 사용 중단 되었다. 볼륨 플러그인 FAQ에서 사용 중단된 볼륨 플러그인에 대해 확인할 수 있다.

볼륨 스냅샷에서 퍼시스턴트볼륨클레임 생성

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: restore-pvc
spec:
  storageClassName: csi-hostpath-sc
  dataSource:
    name: new-snapshot-test
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

볼륨 복제

볼륨 복제는 CSI 볼륨 플러그인만 사용할 수 있다.

기존 pvc에서 퍼시스턴트볼륨클레임 생성

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cloned-pvc
spec:
  storageClassName: my-csi-plugin
  dataSource:
    name: existing-src-pvc-name
    kind: PersistentVolumeClaim
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

볼륨 파퓰레이터(Volume populator)와 데이터 소스

FEATURE STATE: Kubernetes v1.22 [alpha]

볼륨 파퓰레이터는 dataSourceRef라는 PVC 스펙 필드를 활용한다. 다른 PersistentVolumeClaim 또는 VolumeSnapshot을 가리키는 참조만 명시할 수 있는 dataSource 필드와는 다르게, dataSourceRef 필드는 동일 네임스페이스에 있는 어떠한 오브젝트에 대한 참조도 명시할 수 있다(단, PVC 외의 다른 코어 오브젝트는 제외). 기능 게이트가 활성화된 클러스터에서는 dataSource보다 dataSourceRef를 사용하는 것을 권장한다.

데이터 소스 참조

dataSourceRef 필드는 dataSource 필드와 거의 동일하게 동작한다. 둘 중 하나만 명시되어 있으면, API 서버는 두 필드에 같은 값을 할당할 것이다. 두 필드 모두 생성 이후에는 변경될 수 없으며, 두 필드에 다른 값을 넣으려고 시도하면 검증 에러가 발생할 것이다. 따라서 두 필드는 항상 같은 값을 갖게 된다.

dataSourceRef 필드와 dataSource 필드 사이에는 사용자가 알고 있어야 할 두 가지 차이점이 있다.

  • dataSource 필드는 유효하지 않은 값(예를 들면, 빈 값)을 무시하지만, dataSourceRef 필드는 어떠한 값도 무시하지 않으며 유효하지 않은 값이 들어오면 에러를 발생할 것이다. 유효하지 않은 값은 PVC를 제외한 모든 코어 오브젝트(apiGroup이 없는 오브젝트)이다.
  • dataSourceRef 필드는 여러 타입의 오브젝트를 포함할 수 있지만, dataSource 필드는 PVC와 VolumeSnapshot만 포함할 수 있다.

기능 게이트가 활성화된 클러스터에서는 dataSourceRef를 사용해야 하고, 그렇지 않은 클러스터에서는 dataSource를 사용해야 한다. 어떤 경우에서든 두 필드 모두를 확인해야 할 필요는 없다. 이렇게 약간의 차이만 있는 중복된 값은 이전 버전 호환성을 위해서만 존재하는 것이다. 상세히 설명하면, 이전 버전과 새로운 버전의 컨트롤러가 함께 동작할 수 있는데, 이는 두 필드가 동일하기 때문이다.

볼륨 파퓰레이터 사용하기

볼륨 파퓰레이터는 비어 있지 않은 볼륨(non-empty volume)을 생성할 수 있는 컨트롤러이며, 이 볼륨의 내용물은 커스텀 리소스(Custom Resource)에 의해 결정된다. 파퓰레이티드 볼륨(populated volume)을 생성하려면 dataSourceRef 필드에 커스텀 리소스를 기재한다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: populated-pvc
spec:
  dataSourceRef:
    name: example-name
    kind: ExampleDataSource
    apiGroup: example.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

볼륨 파퓰레이터는 외부 컴포넌트이기 때문에, 만약 적합한 컴포넌트가 설치되어 있지 않다면 볼륨 파퓰레이터를 사용하는 PVC에 대한 생성 요청이 실패할 수 있다. 외부 컨트롤러는 '컴포넌트가 없어서 PVC를 생성할 수 없음' 경고와 같은 PVC 생성 상태에 대한 피드백을 제공하기 위해, PVC에 대한 이벤트를 생성해야 한다.

알파 버전의 볼륨 데이터 소스 검증기를 클러스터에 설치할 수 있다. 해당 데이터 소스를 다루는 파퓰레이터가 등록되어 있지 않다면 이 컨트롤러가 PVC에 경고 이벤트를 생성한다. PVC를 위한 적절한 파퓰레이터가 설치되어 있다면, 볼륨 생성과 그 과정에서 발생하는 이슈에 대한 이벤트를 생성하는 것은 파퓰레이터 컨트롤러의 몫이다.

포터블 구성 작성

광범위한 클러스터에서 실행되고 퍼시스턴트 스토리지가 필요한 구성 템플릿 또는 예제를 작성하는 경우 다음 패턴을 사용하는 것이 좋다.

  • 구성 번들(디플로이먼트, 컨피그맵 등)에 퍼시스턴트볼륨클레임 오브젝트를 포함시킨다.
  • 구성을 인스턴스화 하는 사용자에게 퍼시스턴트볼륨을 생성할 권한이 없을 수 있으므로 퍼시스턴트볼륨 오브젝트를 구성에 포함하지 않는다.
  • 템플릿을 인스턴스화 할 때 스토리지 클래스 이름을 제공하는 옵션을 사용자에게 제공한다.
    • 사용자가 스토리지 클래스 이름을 제공하는 경우 해당 값을 permanentVolumeClaim.storageClassName 필드에 입력한다. 클러스터에서 관리자가 스토리지클래스를 활성화한 경우 PVC가 올바른 스토리지 클래스와 일치하게 된다.
    • 사용자가 스토리지 클래스 이름을 제공하지 않으면 permanentVolumeClaim.storageClassName 필드를 nil로 남겨둔다. 그러면 클러스터에 기본 스토리지클래스가 있는 사용자에 대해 PV가 자동으로 프로비저닝된다. 많은 클러스터 환경에 기본 스토리지클래스가 설치되어 있거나 관리자가 고유한 기본 스토리지클래스를 생성할 수 있다.
  • 도구(tooling)에서 일정 시간이 지나도 바인딩되지 않는 PVC를 관찰하여 사용자에게 노출시킨다. 이는 클러스터가 동적 스토리지를 지원하지 않거나(이 경우 사용자가 일치하는 PV를 생성해야 함), 클러스터에 스토리지 시스템이 없음을 나타낸다(이 경우 사용자는 PVC가 필요한 구성을 배포할 수 없음).

다음 내용

API 레퍼런스

본 페이지에 기술된 API에 대해서 다음을 읽어본다.

3.6.4 - CSI 볼륨 복제하기

이 문서에서는 쿠버네티스의 기존 CSI 볼륨 복제의 개념을 설명한다. 볼륨을 숙지하는 것을 추천한다.

소개

CSI 볼륨 복제 기능은 dataSource 필드에 기존 PVC를 지정하는 지원을 추가해서 사용자가 볼륨(Volume)을 복제하려는 것을 나타낸다.

복제는 표준 볼륨처럼 소비할 수 있는 쿠버네티스 볼륨의 복제본으로 정의된다. 유일한 차이점은 프로비저닝할 때 "새" 빈 볼륨을 생성하는 대신에 백엔드 장치가 지정된 볼륨의 정확한 복제본을 생성한다는 것이다.

쿠버네티스 API의 관점에서 복제를 구현하면 새로운 PVC 생성 중에 기존 PVC를 데이터 소스로 지정할 수 있는 기능이 추가된다. 소스 PVC는 바인딩되어 있고, 사용 가능해야 한다(사용 중이 아니어야 함).

사용자는 이 기능을 사용할 때 다음 사항을 알고 있어야 한다.

  • 복제 지원(VolumePVCDataSource)은 CSI 드라이버에서만 사용할 수 있다.
  • 복제 지원은 동적 프로비저너만 사용할 수 있다.
  • CSI 드라이버는 볼륨 복제 기능을 구현했거나 구현하지 않았을 수 있다.
  • PVC는 대상 PVC와 동일한 네임스페이스에 있는 경우에만 복제할 수 있다(소스와 대상은 동일한 네임스페이스에 있어야 함).
  • 복제는 동일한 스토리지 클래스 내에서만 지원된다.
    • 대상 볼륨은 소스와 동일한 스토리지 클래스여야 한다.
    • 기본 스토리지 클래스를 사용할 수 있으며, 사양에 storageClassName을 생략할 수 있다.
  • 동일한 VolumeMode 설정을 사용하는 두 볼륨에만 복제를 수행할 수 있다(블록 모드 볼륨을 요청하는 경우에는 반드시 소스도 블록 모드여야 한다).

프로비저닝

동일한 네임스페이스에서 기존 PVC를 참조하는 dataSource를 추가하는 것을 제외하고는 다른 PVC와 마찬가지로 복제가 프로비전된다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: clone-of-pvc-1
    namespace: myns
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: cloning
  resources:
    requests:
      storage: 5Gi
  dataSource:
    kind: PersistentVolumeClaim
    name: pvc-1

그 결과로 지정된 소스 pvc-1 과 동일한 내용을 가진 clone-of-pvc-1 이라는 이름을 가지는 새로운 PVC가 생겨난다.

사용

새 PVC를 사용할 수 있게 되면, 복제된 PVC는 다른 PVC와 동일하게 소비된다. 또한, 이 시점에서 새롭게 생성된 PVC는 독립된 오브젝트이다. 원본 dataSource PVC와는 무관하게 독립적으로 소비하고, 복제하고, 스냅샷의 생성 또는 삭제를 할 수 있다. 이는 소스가 새롭게 생성된 복제본에 어떤 방식으로든 연결되어 있지 않으며, 새롭게 생성된 복제본에 영향 없이 수정하거나, 삭제할 수도 있는 것을 의미한다.

3.6.5 - 볼륨 스냅샷 클래스

이 문서는 쿠버네티스의 볼륨스냅샷클래스(VolumeSnapshotClass) 개요를 설명한다. 볼륨 스냅샷스토리지 클래스의 숙지를 추천한다.

소개

스토리지클래스(StorageClass)는 관리자가 볼륨을 프로비저닝할 때 제공하는 스토리지의 "클래스"를 설명하는 방법을 제공하는 것처럼, 볼륨스냅샷클래스는 볼륨 스냅샷을 프로비저닝할 때 스토리지의 "클래스"를 설명하는 방법을 제공한다.

VolumeSnapshotClass 리소스

각 볼륨스냅샷클래스에는 클래스에 속하는 볼륨스냅샷을 동적으로 프로비전 할 때 사용되는 driver, deletionPolicy 그리고 parameters 필드를 포함한다.

볼륨스냅샷클래스 오브젝트의 이름은 중요하며, 사용자가 특정 클래스를 요청할 수 있는 방법이다. 관리자는 볼륨스냅샷클래스 오브젝트를 처음 생성할 때 클래스의 이름과 기타 파라미터를 설정하고, 오브젝트가 생성된 이후에는 업데이트할 수 없다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

관리자는snapshot.storage.kubernetes.io/is-default-class: "true" 어노테이션을 추가하여 바인딩할 특정 클래스를 요청하지 않는 볼륨스냅샷에 대한 기본 볼륨스냅샷클래스를 지정할 수 있다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

드라이버

볼륨 스냅샷 클래스에는 볼륨스냅샷의 프로비저닝에 사용되는 CSI 볼륨 플러그인을 결정하는 드라이버를 가지고 있다. 이 필드는 반드시 지정해야 한다.

삭제정책(DeletionPolicy)

볼륨 스냅샷 클래스는 삭제정책을 가지고 있다. 바인딩된 볼륨스냅샷 오브젝트를 삭제할 때 VolumeSnapshotContent의 상황을 구성할 수 있다. 볼륨 스냅샷 클래스의 삭제정책은 Retain 또는 Delete 일 수 있다. 이 필드는 반드시 지정해야 한다.

삭제정책이 Delete 인 경우 기본 스토리지 스냅샷이 VolumeSnapshotContent 오브젝트와 함께 삭제된다. 삭제정책이 Retain 인 경우 기본 스냅샷과 VolumeSnapshotContent 모두 유지된다.

파라미터

볼륨 스냅샷 클래스에는 볼륨 스냅샷 클래스에 속하는 볼륨 스냅샷을 설명하는 파라미터를 가지고 있다. driver 에 따라 다른 파라미터를 사용할 수 있다.

3.6.6 - 스토리지 클래스

이 문서는 쿠버네티스의 스토리지클래스의 개념을 설명한다. 볼륨퍼시스턴트 볼륨에 익숙해지는 것을 권장한다.

소개

스토리지클래스는 관리자가 제공하는 스토리지의 "classes"를 설명할 수 있는 방법을 제공한다. 다른 클래스는 서비스의 품질 수준 또는 백업 정책, 클러스터 관리자가 정한 임의의 정책에 매핑될 수 있다. 쿠버네티스 자체는 클래스가 무엇을 나타내는지에 대해 상관하지 않는다. 다른 스토리지 시스템에서는 이 개념을 "프로파일"이라고도 한다.

스토리지클래스 리소스

각 스토리지클래스에는 해당 스토리지클래스에 속하는 퍼시스턴트볼륨을 동적으로 프로비저닝 할 때 사용되는 provisioner, parametersreclaimPolicy 필드가 포함된다.

스토리지클래스 오브젝트의 이름은 중요하며, 사용자가 특정 클래스를 요청할 수 있는 방법이다. 관리자는 스토리지클래스 오브젝트를 처음 생성할 때 클래스의 이름과 기타 파라미터를 설정하며, 일단 생성된 오브젝트는 업데이트할 수 없다.

관리자는 특정 클래스에 바인딩을 요청하지 않는 PVC에 대해서만 기본 스토리지클래스를 지정할 수 있다. 자세한 내용은 퍼시스턴트볼륨클레임 섹션을 본다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate

프로비저너

각 스토리지클래스에는 PV 프로비저닝에 사용되는 볼륨 플러그인을 결정하는 프로비저너가 있다. 이 필드는 반드시 지정해야 한다.

볼륨 플러그인 내부 프로비저너 설정 예시
AWSElasticBlockStore AWS EBS
AzureFile Azure 파일
AzureDisk Azure 디스크
CephFS - -
Cinder OpenStack Cinder
FC - -
FlexVolume - -
Flocker -
GCEPersistentDisk GCE PD
Glusterfs Glusterfs
iSCSI - -
Quobyte Quobyte
NFS - NFS
RBD Ceph RBD
VsphereVolume vSphere
PortworxVolume Portworx 볼륨
ScaleIO ScaleIO
StorageOS StorageOS
Local - Local

여기 목록에서 "내부" 프로비저너를 지정할 수 있다(이 이름은 "kubernetes.io" 가 접두사로 시작하고, 쿠버네티스와 함께 제공된다). 또한, 쿠버네티스에서 정의한 사양을 따르는 독립적인 프로그램인 외부 프로비저너를 실행하고 지정할 수 있다. 외부 프로비저너의 작성자는 코드의 수명, 프로비저너의 배송 방법, 실행 방법, (Flex를 포함한)볼륨 플러그인 등에 대한 완전한 재량권을 가진다. kubernetes-sigs/sig-storage-lib-external-provisioner 리포지터리에는 대량의 사양을 구현하는 외부 프로비저너를 작성하기 위한 라이브러리가 있다. 일부 외부 프로비저너의 목록은 kubernetes-sigs/sig-storage-lib-external-provisioner 리포지터리에 있다.

예를 들어, NFS는 내부 프로비저너를 제공하지 않지만, 외부 프로비저너를 사용할 수 있다. 타사 스토리지 업체가 자체 외부 프로비저너를 제공하는 경우도 있다.

리클레임 정책

스토리지클래스에 의해 동적으로 생성된 퍼시스턴트볼륨은 클래스의 reclaimPolicy 필드에 지정된 리클레임 정책을 가지는데, 이는 Delete 또는 Retain 이 될 수 있다. 스토리지클래스 오브젝트가 생성될 때 reclaimPolicy 가 지정되지 않으면 기본값은 Delete 이다.

수동으로 생성되고 스토리지클래스를 통해 관리되는 퍼시스턴트볼륨에는 생성 시 할당된 리클레임 정책이 있다.

볼륨 확장 허용

FEATURE STATE: Kubernetes v1.11 [beta]

퍼시스턴트볼륨은 확장이 가능하도록 구성할 수 있다. 이 기능을 true 로 설정하면 해당 PVC 오브젝트를 편집하여 볼륨 크기를 조정할 수 있다.

다음 볼륨 유형은 기본 스토리지클래스에서 allowVolumeExpansion 필드가 true로 설정된 경우 볼륨 확장을 지원한다.

Table of Volume types and the version of Kubernetes they require
볼륨 유형 요구되는 쿠버네티스 버전
gcePersistentDisk 1.11
awsElasticBlockStore 1.11
Cinder 1.11
glusterfs 1.11
rbd 1.11
Azure File 1.11
Azure Disk 1.11
Portworx 1.11
FlexVolume 1.13
CSI 1.14 (alpha), 1.16 (beta)

마운트 옵션

스토리지클래스에 의해 동적으로 생성된 퍼시스턴트볼륨은 클래스의 mountOptions 필드에 지정된 마운트 옵션을 가진다.

만약 볼륨 플러그인이 마운트 옵션을 지원하지 않는데, 마운트 옵션을 지정하면 프로비저닝은 실패한다. 마운트 옵션은 클래스 또는 PV에서 검증되지 않는다. PV 마운트가 유효하지 않으면, 마운트가 실패하게 된다.

볼륨 바인딩 모드

volumeBindingMode 필드는 볼륨 바인딩과 동적 프로비저닝의 시작 시기를 제어한다. 설정되어 있지 않으면, Immediate 모드가 기본으로 사용된다.

Immediate 모드는 퍼시스턴트볼륨클레임이 생성되면 볼륨 바인딩과 동적 프로비저닝이 즉시 발생하는 것을 나타낸다. 토폴로지 제약이 있고 클러스터의 모든 노드에서 전역적으로 접근할 수 없는 스토리지 백엔드의 경우, 파드의 스케줄링 요구 사항에 대한 지식 없이 퍼시스턴트볼륨이 바인딩되거나 프로비저닝된다. 이로 인해 스케줄되지 않은 파드가 발생할 수 있다.

클러스터 관리자는 WaitForFirstConsumer 모드를 지정해서 이 문제를 해결할 수 있는데 이 모드는 퍼시스턴트볼륨클레임을 사용하는 파드가 생성될 때까지 퍼시스턴트볼륨의 바인딩과 프로비저닝을 지연시킨다. 퍼시스턴트볼륨은 파드의 스케줄링 제약 조건에 의해 지정된 토폴로지에 따라 선택되거나 프로비전된다. 여기에는 리소스 요구 사항, 노드 셀렉터, 파드 어피니티(affinity)와 안티-어피니티(anti-affinity) 그리고 테인트(taint)와 톨러레이션(toleration)이 포함된다.

다음 플러그인은 동적 프로비저닝과 WaitForFirstConsumer 를 지원한다.

다음 플러그인은 사전에 생성된 퍼시스턴트볼륨 바인딩으로 WaitForFirstConsumer 를 지원한다.

  • 위에서 언급한 모든 플러그인
  • Local

FEATURE STATE: Kubernetes v1.17 [stable]
CSI 볼륨은 동적 프로비저닝과 사전에 생성된 PV에서도 지원되지만, 지원되는 토폴로지 키와 예시를 보려면 해당 CSI 드라이버에 대한 문서를 본다.

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: kube-01
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

허용된 토폴로지

클러스터 운영자가 WaitForFirstConsumer 볼륨 바인딩 모드를 지정하면, 대부분의 상황에서 더 이상 특정 토폴로지로 프로비저닝을 제한할 필요가 없다. 그러나 여전히 필요한 경우에는 allowedTopologies 를 지정할 수 있다.

이 예시는 프로비전된 볼륨의 토폴로지를 특정 영역으로 제한하는 방법을 보여 주며 지원되는 플러그인의 zonezones 파라미터를 대체하는 데 사용해야 한다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
  - key: failure-domain.beta.kubernetes.io/zone
    values:
    - us-central1-a
    - us-central1-b

파라미터

스토리지 클래스에는 스토리지 클래스에 속하는 볼륨을 설명하는 파라미터가 있다. provisioner 에 따라 다른 파라미터를 사용할 수 있다. 예를 들어, 파라미터 type 에 대한 값 io1 과 파라미터 iopsPerGB 는 EBS에만 사용할 수 있다. 파라미터 생략 시 일부 기본값이 사용된다.

스토리지클래스에 대해 최대 512개의 파라미터를 정의할 수 있다. 키와 값을 포함하여 파라미터 오브젝터의 총 길이는 256 KiB를 초과할 수 없다.

AWS EBS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4
  • type: io1, gp2, sc1, st1. 자세한 내용은 AWS 문서를 본다. 기본값: gp2.
  • zone (사용 중단(deprecated)): AWS 영역. zonezones 를 지정하지 않으면, 일반적으로 쿠버네티스 클러스터의 노드가 있는 모든 활성화된 영역에 걸쳐 볼륨이 라운드 로빈으로 조정된다. zonezones 파라미터를 동시에 사용해서는 안된다.
  • zones (사용 중단): 쉼표로 구분된 AWS 영역의 목록. zonezones 를 지정하지 않으면, 일반적으로 쿠버네티스 클러스터의 노드가 있는 모든 활성화된 영역에 걸쳐 볼륨이 라운드 로빈으로 조정된다. zonezones 파라미터를 동시에 사용해서는 안된다.
  • iopsPerGB: io1 볼륨 전용이다. 1초당 GiB에 대한 I/O 작업 수이다. AWS 볼륨 플러그인은 요청된 볼륨 크기에 곱셈하여 볼륨의 IOPS를 계산하고 이를 20,000 IOPS로 제한한다(AWS에서 지원하는 최대값으로, AWS 문서를 본다). 여기에는 문자열, 즉 10 이 아닌, "10" 이 필요하다.
  • fsType: fsType은 쿠버네티스에서 지원된다. 기본값: "ext4".
  • encrypted: EBS 볼륨의 암호화 여부를 나타낸다. 유효한 값은 "ture" 또는 "false" 이다. 여기에는 문자열, 즉 true 가 아닌, "true" 가 필요하다.
  • kmsKeyId: 선택 사항. 볼륨을 암호화할 때 사용할 키의 전체 Amazon 리소스 이름이다. 아무것도 제공되지 않지만, encrypted 가 true라면 AWS에 의해 키가 생성된다. 유효한 ARN 값은 AWS 문서를 본다.

GCE PD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  fstype: ext4
  replication-type: none
  • type: pd-standard 또는 pd-ssd. 기본값: pd-standard

  • zone (사용 중단): GCE 영역. zonezones 를 모두 지정하지 않으면, 쿠버네티스 클러스터의 노드가 있는 모든 활성화된 영역에 걸쳐 볼륨이 라운드 로빈으로 조정된다. zonezones 파라미터를 동시에 사용해서는 안된다.

  • zones (사용 중단): 쉼표로 구분되는 GCE 영역의 목록. zonezones 를 모두 지정하지 않으면, 쿠버네티스 클러스터의 노드가 있는 모든 활성화된 영역에 걸쳐 볼륨이 라운드 로빈으로 조정된다. zonezones 파라미터를 동시에 사용해서는 안된다.

  • fstype: ext4 또는 xfs. 기본값: ext4. 정의된 파일시스템 유형은 호스트 운영체제에서 지원되어야 한다.

  • replication-type: none 또는 regional-pd. 기본값: none.

replication-typenone 으로 설정하면 (영역) PD가 프로비전된다.

replication-typeregional-pd 로 설정되면, 지역 퍼시스턴트 디스크 가 프로비전된다. 이는 퍼시스턴트볼륨클레임과 스토리지클래스를 소모하는 파드를 생성할 때 지역 퍼시스턴트 디스크는 두개의 영역으로 프로비전되기에 volumeBindingMode: WaitForFirstConsumer 를 설정하는 것을 강력히 권장한다. 하나의 영역은 파드가 스케줄된 영역과 동일하다. 다른 영역은 클러스터에서 사용할 수 있는 영역에서 임의로 선택된다. 디스크 영역은 allowedTopologies 를 사용하면 더 제한할 수 있다.

Glusterfs

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://127.0.0.1:8081"
  clusterid: "630372ccdc720a92c681fb928f27b53f"
  restauthenabled: "true"
  restuser: "admin"
  secretNamespace: "default"
  secretName: "heketi-secret"
  gidMin: "40000"
  gidMax: "50000"
  volumetype: "replicate:3"
  • resturl: 필요에 따라 gluster 볼륨을 프로비전하는 Gluster REST 서비스/Heketi 서비스 url 이다. 일반적인 형식은 IPaddress:Port 이어야 하며 이는 GlusterFS 동적 프로비저너의 필수 파라미터이다. Heketi 서비스가 openshift/kubernetes 설정에서 라우팅이 가능한 서비스로 노출이 되는 경우 이것은 fqdn이 해석할 수 있는 Heketi 서비스 url인 http://heketi-storage-project.cloudapps.mystorage.com 과 유사한 형식을 가질 수 있다.

  • restauthenabled : REST 서버에 대한 인증을 가능하게 하는 Gluster REST 서비스 인증 부울이다. 이 값이 "true" 이면, restuserrestuserkey 또는 secretNamespace + secretName 을 채워야 한다. 이 옵션은 사용 중단이며, restuser, restuserkey, secretName 또는 secretNamespace 중 하나를 지정하면 인증이 활성화된다.

  • restuser : Gluster REST 서비스/Heketi 사용자로 Gluster Trusted Pool에서 볼륨을 생성할 수 있다.

  • restuserkey : REST 서버에 대한 인증에 사용될 Gluster REST 서비스/Heketi 사용자의 암호이다. 이 파라미터는 secretNamespace + secretName 을 위해 사용 중단 되었다.

  • secretNamespace, secretName : Gluster REST 서비스와 통신할 때 사용할 사용자 암호가 포함된 시크릿 인스턴스를 식별한다. 이 파라미터는 선택 사항으로 secretNamespacesecretName 을 모두 생략하면 빈 암호가 사용된다. 제공된 시크릿은 "kubernetes.io/glusterfs" 유형이어야 하며, 예를 들어 다음과 같이 생성한다.

    kubectl create secret generic heketi-secret \
      --type="kubernetes.io/glusterfs" --from-literal=key='opensesame' \
      --namespace=default
    

    시크릿의 예시는 glusterfs-provisioning-secret.yaml에서 찾을 수 있다.

  • clusterid: 630372ccdc720a92c681fb928f27b53f 는 볼륨을 프로비저닝할 때 Heketi가 사용할 클러스터의 ID이다. 또한, 예시와 같이 클러스터 ID 목록이 될 수 있다. 예: "8452344e2becec931ece4e33c4674e4e,42982310de6c63381718ccfa6d8cf397". 이것은 선택적 파라미터이다.

  • gidMin, gidMax : 스토리지클래스에 대한 GID 범위의 최소값과 최대값이다. 이 범위( gidMin-gidMax )의 고유한 값(GID)은 동적으로 프로비전된 볼륨에 사용된다. 이것은 선택적인 값이다. 지정하지 않으면, 볼륨은 각각 gidMin과 gidMax의 기본값인 2000-2147483647 사이의 값으로 프로비전된다.

  • volumetype : 볼륨 유형과 파라미터는 이 선택적 값으로 구성할 수 있다. 볼륨 유형을 언급하지 않는 경우, 볼륨 유형을 결정하는 것은 프로비저너의 책임이다.

    예를 들어:

    • 레플리카 볼륨: volumetype: replicate:3 여기서 '3'은 레플리카의 수이다.
    • Disperse/EC 볼륨: volumetype: disperse:4:2 여기서 '4'는 데이터이고 '2'는 중복 횟수이다.
    • Distribute 볼륨: volumetype: none

    사용 가능한 볼륨 유형과 관리 옵션에 대해서는 관리 가이드를 참조한다.

    자세한 정보는 Heketi 구성 방법을 참조한다.

    퍼시스턴트 볼륨이 동적으로 프로비전되면 Gluster 플러그인은 gluster-dynamic-<claimname> 이라는 이름으로 엔드포인트와 헤드리스 서비스를 자동으로 생성한다. 퍼시스턴트 볼륨 클레임을 삭제하면 동적 엔드포인트와 서비스가 자동으로 삭제된다.

NFS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-nfs
provisioner: example.com/external-nfs
parameters:
  server: nfs-server.example.com
  path: /share
  readOnly: false
  • server: NFS 서버의 호스트네임 또는 IP 주소.
  • path: NFS 서버가 익스포트(export)한 경로.
  • readOnly: 스토리지를 읽기 전용으로 마운트할지 나타내는 플래그(기본값: false).

쿠버네티스에는 내장 NFS 프로비저너가 없다. NFS를 위한 스토리지클래스를 생성하려면 외부 프로비저너를 사용해야 한다. 예시는 다음과 같다.

OpenStack Cinder

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gold
provisioner: kubernetes.io/cinder
parameters:
  availability: nova
  • availability: 가용성 영역. 지정하지 않으면, 일반적으로 쿠버네티스 클러스터의 노드가 있는 모든 활성화된 영역에 걸쳐 볼륨이 라운드 로빈으로 조정된다.

vSphere

vSphere 스토리지 클래스에는 두 가지 유형의 프로비저닝 도구가 있다.

인-트리 프로비저닝 도구는 사용 중단되었다. CSI 프로비저닝 도구에 대한 자세한 내용은 쿠버네티스 vSphere CSI 드라이버vSphereVolume CSI 마이그레이션을 참고한다.

CSI 프로비저닝 도구

vSphere CSI 스토리지클래스 프로비저닝 도구는 Tanzu 쿠버네티스 클러스터에서 작동한다. 예시는 vSphere CSI 리포지터리를 참조한다.

vCP 프로비저닝 도구

다음 예시에서는 VMware 클라우드 공급자(vCP) 스토리지클래스 프로비저닝 도구를 사용한다.

  1. 사용자 지정 디스크 형식으로 스토리지클래스를 생성한다.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
    

    diskformat: thin, zeroedthickeagerzeroedthick. 기본값: "thin".

  2. 사용자 지정 데이터스토어에 디스크 형식으로 스토리지클래스를 생성한다.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
        diskformat: zeroedthick
        datastore: VSANDatastore
    

    datastore: 또한, 사용자는 스토리지클래스에서 데이터스토어를 지정할 수 있다. 볼륨은 스토리지클래스에 지정된 데이터스토어에 생성되며, 이 경우 VSANDatastore 이다. 이 필드는 선택 사항이다. 데이터스토어를 지정하지 않으면, vSphere 클라우드 공급자를 초기화하는데 사용되는 vSphere 설정 파일에 지정된 데이터스토어에 볼륨이 생성된다.

  3. 쿠버네티스 내부 스토리지 정책을 관리한다.

    • 기존 vCenter SPBM 정책을 사용한다.

      vSphere 스토리지 관리의 가장 중요한 기능 중 하나는 정책 기반 관리이다. 스토리지 정책 기반 관리(Storage Policy Based Management (SPBM))는 광범위한 데이터 서비스와 스토리지 솔루션에서 단일 통합 컨트롤 플레인을 제공하는 스토리지 정책 프레임워크이다. SPBM을 통해 vSphere 관리자는 용량 계획, 차별화된 서비스 수준과 용량의 헤드룸(headroom) 관리와 같은 선행 스토리지 프로비저닝 문제를 극복할 수 있다.

      SPBM 정책은 storagePolicyName 파라미터를 사용하면 스토리지클래스에서 지정할 수 있다.

    • 쿠버네티스 내부의 가상 SAN 정책 지원

      Vsphere 인프라스트럭쳐(Vsphere Infrastructure (VI)) 관리자는 동적 볼륨 프로비저닝 중에 사용자 정의 가상 SAN 스토리지 기능을 지정할 수 있다. 이제 동적 볼륨 프로비저닝 중에 스토리지 기능의 형태로 성능 및 가용성과 같은 스토리지 요구 사항을 정의할 수 있다. 스토리지 기능 요구 사항은 가상 SAN 정책으로 변환된 퍼시스턴트 볼륨(가상 디스크)을 생성할 때 가상 SAN 계층으로 푸시된다. 가상 디스크는 가상 SAN 데이터 스토어에 분산되어 요구 사항을 충족시키게 된다.

      퍼시스턴트 볼륨 관리에 스토리지 정책을 사용하는 방법에 대한 자세한 내용은 볼륨의 동적 프로비저닝을 위한 스토리지 정책 기반 관리(SPBM)를 참조한다.

vSphere용 쿠버네티스 내에서 퍼시스턴트 볼륨 관리를 시도하는 vSphere 예시는 거의 없다.

Ceph RBD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/rbd
parameters:
  monitors: 10.16.153.105:6789
  adminId: kube
  adminSecretName: ceph-secret
  adminSecretNamespace: kube-system
  pool: kube
  userId: kube
  userSecretName: ceph-secret-user
  userSecretNamespace: default
  fsType: ext4
  imageFormat: "2"
  imageFeatures: "layering"
  • monitors: 쉼표로 구분된 Ceph 모니터. 이 파라미터는 필수이다.

  • adminId: 풀에 이미지를 생성할 수 있는 Ceph 클라이언트 ID. 기본값은 "admin".

  • adminSecretName: adminId 의 시크릿 이름. 이 파라미터는 필수이다. 제공된 시크릿은 "kubernetes.io/rbd" 유형이어야 한다.

  • adminSecretNamespace: adminSecretName 의 네임스페이스. 기본값은 "default".

  • pool: Ceph RBD 풀. 기본값은 "rbd".

  • userId: RBD 이미지를 매핑하는 데 사용하는 Ceph 클라이언트 ID. 기본값은 adminId 와 동일하다.

  • userSecretName: RDB 이미지를 매핑하기 위한 userId 에 대한 Ceph 시크릿 이름. PVC와 동일한 네임스페이스에 존재해야 한다. 이 파라미터는 필수이다. 제공된 시크릿은 "kubernetes.io/rbd" 유형이어야 하며, 다음의 예시와 같이 생성되어야 한다.

    kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" \
      --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' \
      --namespace=kube-system
    
  • userSecretNamespace: userSecretName 의 네임스페이스.

  • fsType: 쿠버네티스가 지원하는 fsType. 기본값: "ext4".

  • imageFormat: Ceph RBD 이미지 형식, "1" 또는 "2". 기본값은 "2".

  • imageFeatures: 이 파라미터는 선택 사항이며, imageFormat 을 "2"로 설정한 경우에만 사용해야 한다. 현재 layering 에서만 기능이 지원된다. 기본값은 ""이며, 기능이 설정되어 있지 않다.

Quobyte

FEATURE STATE: Kubernetes v1.22 [deprecated]

Quobyte 인-트리 스토리지 플러그인은 사용 중단되었으며, 아웃-오브-트리 Quobyte 플러그인에 대한 예제 StorageClass는 Quobyte CSI 저장소에서 찾을 수 있다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: slow
provisioner: kubernetes.io/quobyte
parameters:
    quobyteAPIServer: "http://138.68.74.142:7860"
    registry: "138.68.74.142:7861"
    adminSecretName: "quobyte-admin-secret"
    adminSecretNamespace: "kube-system"
    user: "root"
    group: "root"
    quobyteConfig: "BASE"
    quobyteTenant: "DEFAULT"
  • quobyteAPIServer: "http(s)://api-server:7860" 형식의 Quobyte의 API 서버이다.

  • registry: 볼륨을 마운트하는 데 사용할 Quobyte 레지스트리이다. 레지스트리를 <host>:<port> 의 쌍으로 지정하거나 여러 레지스트리를 지정하려면 쉼표만 있으면 된다. 예: <host1>:<port>,<host2>:<port>,<host3>:<port> 호스트는 IP 주소이거나 DNS가 작동 중인 경우 DNS 이름을 제공할 수도 있다.

  • adminSecretNamespace: adminSecretName 의 네임스페이스. 기본값은 "default".

  • adminSecretName: 시크릿은 API 서버에 대해 인증하기 위한 Quobyte 사용자와 암호에 대한 정보를 담고 있다. 제공된 시크릿은 "kubernetes.io/quobyte" 유형과 userpassword 키를 가져야 하며, 예를 들면 다음과 같다.

    kubectl create secret generic quobyte-admin-secret \
      --type="kubernetes.io/quobyte" --from-literal=user='admin' --from-literal=password='opensesame' \
      --namespace=kube-system
    
  • user: 이 사용자에 대한 모든 접근을 매핑한다. 기본값은 "root".

  • group: 이 그룹에 대한 모든 접근을 매핑한다. 기본값은 "nfsnobody".

  • quobyteConfig: 지정된 구성을 사용해서 볼륨을 생성한다. 웹 콘솔 또는 quobyte CLI를 사용해서 새 구성을 작성하거나 기존 구성을 수정할 수 있다. 기본값은 "BASE".

  • quobyteTenant: 지정된 테넌트 ID를 사용해서 볼륨을 생성/삭제한다. 이 Quobyte 테넌트는 이미 Quobyte에 있어야 한다. 기본값은 "DEFAULT".

Azure 디스크

Azure 비관리 디스크 스토리지 클래스

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName: Azure 스토리지 계정 Sku 계층. 기본값은 없음.
  • location: Azure 스토리지 계정 지역. 기본값은 없음.
  • storageAccount: Azure 스토리지 계정 이름. 스토리지 계정이 제공되면, 클러스터와 동일한 리소스 그룹에 있어야 하며, location 은 무시된다. 스토리지 계정이 제공되지 않으면, 클러스터와 동일한 리소스 그룹에 새 스토리지 계정이 생성된다.

Azure 디스크 스토리지 클래스(v1.7.2부터 제공)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: managed
  • storageaccounttype: Azure 스토리지 계정 Sku 계층. 기본값은 없음.
  • kind: 가능한 값은 shared, dedicated, 그리고 managed (기본값) 이다. kindshared 인 경우, 모든 비관리 디스크는 클러스터와 동일한 리소스 그룹에 있는 몇 개의 공유 스토리지 계정에 생성된다. kinddedicated 인 경우, 클러스터와 동일한 리소스 그룹에서 새로운 비관리 디스크에 대해 새로운 전용 스토리지 계정이 생성된다. kindmanaged 인 경우, 모든 관리 디스크는 클러스터와 동일한 리소스 그룹에 생성된다.
  • resourceGroup: Azure 디스크를 만들 리소스 그룹을 지정한다. 기존에 있는 리소스 그룹 이름이어야 한다. 지정되지 않는 경우, 디스크는 현재 쿠버네티스 클러스터와 동일한 리소스 그룹에 배치된다.
  • 프리미엄 VM은 표준 LRS(Standard_LRS)와 프리미엄 LRS(Premium_LRS) 디스크를 모두 연결할 수 있는 반면에, 표준 VM은 표준 LRS(Standard_LRS) 디스크만 연결할 수 있다.
  • 관리되는 VM은 관리되는 디스크만 연결할 수 있고, 비관리 VM은 비관리 디스크만 연결할 수 있다.

Azure 파일

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName: Azure 스토리지 계정 Sku 계층. 기본값은 없음.
  • location: Azure 스토리지 계정 지역. 기본값은 없음.
  • storageAccount: Azure 스토리지 계정 이름. 기본값은 없음. 스토리지 계정이 제공되지 않으면, 리소스 그룹과 관련된 모든 스토리지 계정이 검색되어 skuNamelocation 이 일치하는 것을 찾는다. 스토리지 계정이 제공되면, 클러스터와 동일한 리소스 그룹에 있어야 하며 skuNamelocation 은 무시된다.
  • secretNamespace: Azure 스토리지 계정 이름과 키가 포함된 시크릿 네임스페이스. 기본값은 파드와 동일하다.
  • secretName: Azure 스토리지 계정 이름과 키가 포함된 시크릿 이름. 기본값은 azure-storage-account-<accountName>-secret
  • readOnly: 스토리지가 읽기 전용으로 마운트되어야 하는지 여부를 나타내는 플래그. 읽기/쓰기 마운트를 의미하는 기본값은 false. 이 설정은 볼륨마운트(VolumeMounts)의 ReadOnly 설정에도 영향을 준다.

스토리지 프로비저닝 중에 마운트 자격증명에 대해 secretName 이라는 시크릿이 생성된다. 클러스터에 RBAC컨트롤러의 롤(role)들을 모두 활성화한 경우, clusterrole system:controller:persistent-volume-binder 에 대한 secret 리소스에 create 권한을 추가한다.

다중 테넌시 컨텍스트에서 secretNamespace 의 값을 명시적으로 설정하는 것을 권장하며, 그렇지 않으면 다른 사용자가 스토리지 계정 자격증명을 읽을 수 있기 때문이다.

Portworx 볼륨

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
  repl: "1"
  snap_interval:   "70"
  priority_io:  "high"

  • fs: 배치할 파일 시스템: none/xfs/ext4 (기본값: ext4)
  • block_size: Kbytes 단위의 블록 크기(기본값: 32).
  • repl: 레플리케이션 팩터 1..3 (기본값: 1)의 형태로 제공될 동기 레플리카의 수. 여기에는 문자열, 즉 0 이 아닌, "0" 이 필요하다.
  • priority_io: 볼륨이 고성능 또는 우선 순위가 낮은 스토리지에서 생성될 것인지를 결정한다 high/medium/low (기본값: low).
  • snap_interval: 스냅샷을 트리거할 때의 시각/시간 간격(분). 스냅샷은 이전 스냅샷과의 차이에 따라 증분되며, 0은 스냅을 비활성화 한다(기본값: 0). 여기에는 문자열, 즉 70 이 아닌, "70" 이 필요하다.
  • aggregation_level: 볼륨이 분배될 청크 수를 지정하며, 0은 집계되지 않은 볼륨을 나타낸다(기본값: 0). 여기에는 문자열, 즉 0 이 아닌, "0" 이 필요하다.
  • ephemeral: 마운트 해제 후 볼륨을 정리해야 하는지 혹은 지속적이어야 하는지를 지정한다. emptyDir 에 대한 유스케이스는 이 값을 true로 설정할 수 있으며, persistent volumes 에 대한 유스케이스인 카산드라와 같은 데이터베이스는 false로 설정해야 한다. true/false (기본값 false) 여기에는 문자열, 즉 true 가 아닌, "true" 가 필요하다.

ScaleIO

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/scaleio
parameters:
  gateway: https://192.168.99.200:443/api
  system: scaleio
  protectionDomain: pd0
  storagePool: sp1
  storageMode: ThinProvisioned
  secretRef: sio-secret
  readOnly: false
  fsType: xfs
  • provisioner: 속성이 kubernetes.io/scaleio 로 설정되어 있다.
  • gateway: ScaleIO API 게이트웨이 주소(필수)
  • system: ScaleIO 시스템의 이름(필수)
  • protectionDomain: ScaleIO 보호 도메인의 이름(필수)
  • storagePool: 볼륨 스토리지 풀의 이름(필수)
  • storageMode: 스토리지 프로비전 모드: ThinProvisioned (기본값) 또는 ThickProvisioned
  • secretRef: 구성된 시크릿 오브젝트에 대한 참조(필수)
  • readOnly: 마운트된 볼륨에 대한 접근 모드의 지정(기본값: false)
  • fsType: 볼륨에 사용할 파일 시스템 유형(기본값: ext4)

ScaleIO 쿠버네티스 볼륨 플러그인에는 구성된 시크릿 오브젝트가 필요하다. 시크릿은 다음 명령에 표시된 것처럼 kubernetes.io/scaleio 유형으로 작성해야 하며, PVC와 동일한 네임스페이스 값을 사용해야 한다.

kubectl create secret generic sio-secret --type="kubernetes.io/scaleio" \
--from-literal=username=sioadmin --from-literal=password=d2NABDNjMA== \
--namespace=default

StorageOS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/storageos
parameters:
  pool: default
  description: Kubernetes volume
  fsType: ext4
  adminSecretNamespace: default
  adminSecretName: storageos-secret
  • pool: 볼륨을 프로비전할 StorageOS 분산 용량 풀의 이름. 지정되지 않은 경우 일반적으로 존재하는 default 풀을 사용한다.
  • description: 동적으로 생성된 볼륨에 할당할 설명. 모든 볼륨 설명은 스토리지 클래스에 대해 동일하지만, 서로 다른 유스케이스에 대한 설명을 허용하기 위해 다른 스토리지 클래스를 사용할 수 있다. 기본값은 Kubernetes volume.
  • fsType: 요청할 기본 파일 시스템 유형. StorageOS 내의 사용자 정의 규칙이 이 값을 무시할 수 있다. 기본 값은 ext4.
  • adminSecretNamespace: API 구성 시크릿이 있는 네임스페이스. adminSecretName 이 설정된 경우 필수이다.
  • adminSecretName: StorageOS API 자격증명을 얻는 데 사용할 시크릿의 이름. 지정하지 않으면 기본값이 시도된다.

StorageOS 쿠버네티스 볼륨 플러그인은 시크릿 오브젝트를 사용해서 StorageOS API에 접근하기 위한 엔드포인트와 자격증명을 지정할 수 있다. 이것은 기본값이 변경된 경우에만 필요하다. 시크릿은 다음의 명령과 같이 kubernetes.io/storageos 유형으로 만들어야 한다.

kubectl create secret generic storageos-secret \
--type="kubernetes.io/storageos" \
--from-literal=apiAddress=tcp://localhost:5705 \
--from-literal=apiUsername=storageos \
--from-literal=apiPassword=storageos \
--namespace=default

동적으로 프로비전된 볼륨에 사용되는 시크릿은 모든 네임스페이스에서 생성할 수 있으며 adminSecretNamespace 파라미터로 참조될 수 있다. 사전에 프로비전된 볼륨에서 사용하는 시크릿은 이를 참조하는 PVC와 동일한 네임스페이스에서 작성해야 한다.

Local

FEATURE STATE: Kubernetes v1.14 [stable]
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

로컬 볼륨은 현재 동적 프로비저닝을 지원하지 않지만, 파드 스케줄링까지 볼륨 바인딩을 지연시키기 위해서는 스토리지클래스가 여전히 생성되어야 한다. 이것은 WaitForFirstConsumer 볼륨 바인딩 모드에 의해 지정된다.

볼륨 바인딩을 지연시키면 스케줄러가 퍼시스턴트볼륨클레임에 적절한 퍼시스턴트볼륨을 선택할 때 파드의 모든 스케줄링 제약 조건을 고려할 수 있다.

3.6.7 - 동적 볼륨 프로비저닝

동적 볼륨 프로비저닝을 통해 온-디맨드 방식으로 스토리지 볼륨을 생성할 수 있다. 동적 프로비저닝이 없으면 클러스터 관리자는 클라우드 또는 스토리지 공급자에게 수동으로 요청해서 새 스토리지 볼륨을 생성한 다음, 쿠버네티스에 표시하기 위해 PersistentVolume 오브젝트를 생성해야 한다. 동적 프로비저닝 기능을 사용하면 클러스터 관리자가 스토리지를 사전 프로비저닝 할 필요가 없다. 대신 사용자가 스토리지를 요청하면 자동으로 프로비저닝 한다.

배경

동적 볼륨 프로비저닝의 구현은 storage.k8s.io API 그룹의 StorageClass API 오브젝트를 기반으로 한다. 클러스터 관리자는 볼륨을 프로비전하는 볼륨 플러그인 (프로비저너라고도 알려짐)과 프로비저닝시에 프로비저너에게 전달할 파라미터 집합을 지정하는 StorageClass 오브젝트를 필요한 만큼 정의할 수 있다. 클러스터 관리자는 클러스터 내에서 사용자 정의 파라미터 집합을 사용해서 여러 가지 유형의 스토리지 (같거나 다른 스토리지 시스템들)를 정의하고 노출시킬 수 있다. 또한 이 디자인을 통해 최종 사용자는 스토리지 프로비전 방식의 복잡성과 뉘앙스에 대해 걱정할 필요가 없다. 하지만, 여전히 여러 스토리지 옵션들을 선택할 수 있다.

스토리지 클래스에 대한 자세한 정보는 여기에서 찾을 수 있다.

동적 프로비저닝 활성화하기

동적 프로비저닝을 활성화하려면 클러스터 관리자가 사용자를 위해 하나 이상의 스토리지클래스(StorageClass) 오브젝트를 사전 생성해야 한다. 스토리지클래스 오브젝트는 동적 프로비저닝이 호출될 때 사용할 프로비저너와 해당 프로비저너에게 전달할 파라미터를 정의한다. 스토리지클래스 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

다음 매니페스트는 표준 디스크와 같은 퍼시스턴트 디스크를 프로비전하는 스토리지 클래스 "slow"를 만든다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard

다음 매니페스트는 SSD와 같은 퍼시스턴트 디스크를 프로비전하는 스토리지 클래스 "fast"를 만든다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

동적 프로비저닝 사용하기

사용자는 PersistentVolumeClaim 에 스토리지 클래스를 포함시켜 동적으로 프로비전된 스토리지를 요청한다. 쿠버네티스 v1.6 이전에는 volume.beta.kubernetes.io/storage-class 어노테이션을 통해 수행되었다. 그러나 이 어노테이션은 v1.9부터는 더 이상 사용하지 않는다. 사용자는 이제 PersistentVolumeClaim 오브젝트의 storageClassName 필드를 사용해야 한다. 이 필드의 값은 관리자가 구성한 StorageClass 의 이름과 일치해야 한다. (아래를 참고)

예를 들어 "fast" 스토리지 클래스를 선택하려면 다음과 같은 PersistentVolumeClaim 을 생성한다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast
  resources:
    requests:
      storage: 30Gi

이 클레임의 결과로 SSD와 같은 퍼시스턴트 디스크가 자동으로 프로비전 된다. 클레임이 삭제되면 볼륨이 삭제된다.

기본 동작

스토리지 클래스가 지정되지 않은 경우 모든 클레임이 동적으로 프로비전이 되도록 클러스터에서 동적 프로비저닝을 활성화 할 수 있다. 클러스터 관리자는 이 방법으로 활성화 할 수 있다.

관리자는 storageclass.kubernetes.io/is-default-class 어노테이션을 추가해서 특정 StorageClass 를 기본으로 표시할 수 있다. 기본 StorageClass 가 클러스터에 존재하고 사용자가 storageClassName 를 지정하지 않은 PersistentVolumeClaim 을 작성하면, DefaultStorageClass 어드미션 컨트롤러가 디폴트 스토리지 클래스를 가리키는 storageClassName 필드를 자동으로 추가한다.

클러스터에는 최대 하나의 default 스토리지 클래스가 있을 수 있다. 그렇지 않은 경우 storageClassName 을 명시적으로 지정하지 않은 PersistentVolumeClaim 을 생성할 수 없다.

토폴로지 인식

다중 영역 클러스터에서 파드는 한 지역 내 여러 영역에 걸쳐 분산될 수 있다. 파드가 예약된 영역에서 단일 영역 스토리지 백엔드를 프로비전해야 한다. 볼륨 바인딩 모드를 설정해서 수행할 수 있다.

3.6.8 - 스토리지 용량

스토리지 용량은 제한이 있으며, 파드가 실행되는 노드의 상황에 따라 달라질 수 있다. 예를 들어, 일부 노드에서 NAS(Network Attached Storage)에 접근할 수 없는 경우가 있을 수 있으며, 또는 각 노드에 종속적인 로컬 스토리지를 사용하는 경우일 수도 있다.

FEATURE STATE: Kubernetes v1.19 [alpha]
FEATURE STATE: Kubernetes v1.21 [beta]

이 페이지에서는 쿠버네티스가 어떻게 스토리지 용량을 추적하고 스케줄러가 남아 있는 볼륨을 제공하기 위해 스토리지 용량이 충분한 노드에 파드를 스케줄링하기 위해 이 정보를 어떻게 사용하는지 설명한다. 스토리지 용량을 추적하지 않으면, 스케줄러는 볼륨을 제공할 충분한 용량이 없는 노드를 선정할 수 있으며, 스케줄링을 여러 번 다시 시도해야 한다.

스토리지 용량 추적은 컨테이너 스토리지 인터페이스(CSI) 드라이버에서 지원하며, CSI 드라이버를 설치할 때 사용하도록 설정해야 한다.

API

이 기능에는 다음 두 가지 API 확장이 있다.

  • CSIStorageCapacity 오브젝트: CSI 드라이버가 설치된 네임스페이스에 CSI 드라이버가 이 오브젝트를 생성한다. 각 오브젝트는 하나의 스토리지 클래스에 대한 용량 정보를 담고 있으며, 어떤 노드가 해당 스토리지에 접근할 수 있는지를 정의한다.
  • CSIDriverSpec.StorageCapacity 필드: true로 설정하면, 쿠버네티스 스케줄러가 CSI 드라이버를 사용하는 볼륨의 스토리지 용량을 고려하게 된다.

스케줄링

다음과 같은 경우 쿠버네티스 스케줄러에서 스토리지 용량 정보를 사용한다.

  • CSIStorageCapacity 기능 게이트(feature gate)가 true이고,
  • 파드가 아직 생성되지 않은 볼륨을 사용하고,
  • 해당 볼륨은 CSI 드라이버를 참조하고 WaitForFirstConsumer 볼륨 바인딩 모드를 사용하는 스토리지클래스(StorageClass)를 사용하고,
  • 드라이버의 CSIDriver 오브젝트에 StorageCapacity 속성이 true로 설정되어 있다.

이 경우 스케줄러는 파드에 제공할 충분한 스토리지가 있는 노드만 고려한다. 이 검사는 아주 간단한데, 볼륨의 크기를 노드를 포함하는 토폴로지를 가진 CSIStorageCapacity 오브젝트에 나열된 용량과 비교한다.

볼륨 바인딩 모드가 Immediate 인 볼륨의 경우에는 스토리지 드라이버는 볼륨을 사용하는 파드와 관계없이 볼륨을 생성할 위치를 정한다. 볼륨을 생성한 후에, 스케줄러는 볼륨을 사용할 수 있는 노드에 파드를 스케줄링한다.

CSI 임시 볼륨의 경우에는 볼륨 유형이 로컬 볼륨이고 큰 자원이 필요하지 않은 특정 CSI 드라이버에서만 사용된다는 가정하에, 항상 스토리지 용량을 고려하지 않고 스케줄링한다.

리스케줄링

WaitForFirstConsumer 볼륨을 가진 파드에 대해 노드가 선정되었더라도 아직은 잠정적인 결정이다. 다음 단계에서 선정한 노드에서 볼륨을 사용할 수 있어야 한다는 힌트를 주고 CSI 스토리지 드라이버에 볼륨 생성을 요청한다

쿠버네티스는 시간이 지난 스토리지 용량 정보를 기반으로 노드를 선정할 수도 있으므로, 볼륨을 실제로 생성하지 않을 수도 있다. 그런 다음 노드 선정이 재설정되고 쿠버네티스 스케줄러가 파드를 위한 노드를 찾는 것을 재시도한다.

제한사항

스토리지 용량 추적은 첫 시도에 스케줄링이 성공할 가능성을 높이지만, 스케줄러가 시간이 지난 정보를 기반으로 결정해야 할 수도 있기 때문에 이를 보장하지는 않는다. 일반적으로 스토리지 용량 정보가 없는 스케줄링과 동일한 재시도 메커니즘으로 스케줄링 실패를 처리한다.

스케줄링이 영구적으로 실패할 수 있는 한 가지 상황은 파드가 여러 볼륨을 사용하는 경우이다. 토폴로지 세그먼트에 하나의 볼륨이 이미 생성되어 다른 볼륨에 충분한 용량이 남아 있지 않을 수 있다. 이러한 상황을 복구하려면 용량을 늘리거나 이미 생성된 볼륨을 삭제하는 등의 수작업이 필요하며, 자동으로 처리하려면 추가 작업이 필요하다.

스토리지 용량 추적 활성화

스토리지 용량 추적은 베타 기능이며, 쿠버네티스 1.21 이후 버전부터 쿠버네티스 클러스터에 기본적으로 활성화되어 있다. 클러스터에서 스토리지 용량 추적 기능을 활성화하는 것뿐만 아니라, CSI 드라이버에서도 이 기능을 지원해야 한다. 자세한 내용은 드라이버 문서를 참조한다.

다음 내용

3.6.9 - 노드 별 볼륨 한도

이 페이지는 다양한 클라우드 공급자들이 제공하는 노드에 연결할 수 있는 최대 볼륨 수를 설명한다.

Google, Amazon 그리고 Microsoft와 같은 클라우드 공급자는 일반적으로 노드에 연결할 수 있는 볼륨 수에 제한이 있다. 쿠버네티스가 이러한 제한을 준수하는 것은 중요하다. 그렇지 않으면, 노드에서 예약된 파드가 볼륨이 연결될 때까지 멈추고 기다릴 수 있다.

쿠버네티스 기본 한도

쿠버네티스 스케줄러에는 노드에 연결될 수 있는 볼륨 수에 대한 기본 한도가 있다.

클라우드 서비스노드 당 최대 볼륨
Amazon Elastic Block Store (EBS)39
Google Persistent Disk16
Microsoft Azure Disk Storage16

사용자 정의 한도

KUBE_MAX_PD_VOLS 환경 변수의 값을 설정한 후, 스케줄러를 시작하여 이러한 한도를 변경할 수 있다. CSI 드라이버는 절차가 다를 수 있으므로, 한도를 사용자 정의하는 방법에 대한 문서를 참고한다.

기본 한도보다 높은 한도를 설정한 경우 주의한다. 클라우드 공급자의 문서를 참조하여 노드가 실제로 사용자가 설정한 한도를 지원할 수 있는지 확인한다.

한도는 전체 클러스터에 적용되므로, 모든 노드에 영향을 준다.

동적 볼륨 한도

FEATURE STATE: Kubernetes v1.17 [stable]

다음 볼륨 유형에 대해 동적 볼륨 한도가 지원된다.

  • Amazon EBS
  • Google Persistent Disk
  • Azure Disk
  • CSI

인-트리(in-tree) 볼륨 플러그인으로 관리되는 볼륨의 경우, 쿠버네티스는 자동으로 노드 유형을 결정하고 노드에 적절한 최대 볼륨 수를 적용한다. 예를 들면, 다음과 같다.

  • Google Compute Engine에서는, 노드 유형에 따라 최대 127개의 볼륨까지 노드에 연결할 수 있다.

  • M5, C5, R5, T3와 Z1D 인스턴스 유형의 Amazon EBS 디스크의 경우, 쿠버네티스는 25개의 볼륨만 노드에 연결할 수 있도록 허용한다. Amazon Elastic Compute Cloud (EC2)의 다른 인스턴스 유형의 경우, 쿠버네티스는 노드에 39개의 볼륨을 연결할 수 있도록 허용한다.

  • Azure에서는, 노드 유형에 따라 최대 64개의 디스크를 노드에 연결할 수 있다. 더 자세한 내용은 Azure의 가상 머신 크기를 참고한다.

  • CSI 스토리지 드라이버가 NodeGetInfo 를 사용해서 노드에 대한 최대 볼륨 수를 알린다면, kube-scheduler는 그 한도를 따른다.

자세한 내용은 CSI 명세를 참고한다.

  • CSI 드라이버로 마이그레이션된 인-트리 플러그인으로 관리되는 볼륨의 경우, 최대 볼륨 수는 CSI 드라이버가 보고한 개수이다.

3.6.10 - 볼륨 헬스 모니터링

FEATURE STATE: Kubernetes v1.21 [alpha]

CSI 볼륨 헬스 모니터링을 통해 CSI 드라이버는 기본 스토리지 시스템에서 비정상적인 볼륨 상태를 감지하고 이를 PVC 또는 파드의 이벤트로 보고한다.

볼륨 헬스 모니터링

쿠버네티스 볼륨 헬스 모니터링 은 쿠버네티스가 CSI(Container Storage Interface)를 구현하는 방법의 일부다. 볼륨 헬스 모니터링 기능은 외부 헬스 모니터 컨트롤러와 kubelet, 2가지 컴포넌트로 구현된다.

CSI 드라이버가 컨트롤러 측의 볼륨 헬스 모니터링 기능을 지원하는 경우, CSI 볼륨에서 비정상적인 볼륨 상태가 감지될 때 관련 퍼시스턴트볼륨클레임(PersistentVolumeClaim, PVC) 이벤트가 보고된다.

외부 헬스 모니터 컨트롤러는 노드 장애 이벤트도 감시한다. enable-node-watcher 플래그를 true로 설정하여 노드 장애 모니터링을 활성화할 수 있다. 외부 헬스 모니터가 노드 장애 이벤트를 감지하면, 컨트롤러는 이 PVC를 사용하는 파드가 장애 상태인 노드에 있음을 나타내는 이벤트가 PVC에 보고된다고 알린다.

CSI 드라이버가 노드 측에서 볼륨 헬스 모니터링 기능을 지원하는 경우, CSI 볼륨에서 비정상적인 볼륨 상태가 감지되면 PVC를 사용하는 모든 파드에서 이벤트가 보고된다.

다음 내용

이 기능을 구현한 CSI 드라이버를 확인하려면 CSI 드라이버 문서를 참고한다.

3.7 - 구성

쿠버네티스가 파드 구성을 위해 제공하는 리소스

3.7.1 - 구성 모범 사례

이 문서는 사용자 가이드, 시작하기 문서 및 예제들에 걸쳐 소개된 구성 모범 사례를 강조하고 통합한다.

이 문서는 지속적으로 변경 가능하다. 이 목록에 없지만 다른 사람들에게 유용할 것 같은 무엇인가를 생각하고 있다면, 새로운 이슈를 생성하거나 풀 리퀘스트를 제출하는 것을 망설이지 말기를 바란다.

일반적인 구성 팁

  • 구성을 정의할 때, 안정된 최신 API 버전을 명시한다.

  • 구성 파일들은 클러스터에 적용되기 전에 버전 컨트롤에 저장되어 있어야 한다. 이는 만약 필요하다면 구성의 변경 사항을 빠르게 되돌릴 수 있도록 해준다. 이는 또한 클러스터의 재-생성과 복원을 도와준다.

  • JSON보다는 YAML을 사용해 구성 파일을 작성한다. 비록 이러한 포맷들은 대부분의 모든 상황에서 통용되어 사용될 수 있지만, YAML이 좀 더 사용자 친화적인 성향을 가진다.

  • 의미상 맞다면 가능한 연관된 오브젝트들을 하나의 파일에 모아 놓는다. 때로는 여러 개의 파일보다 하나의 파일이 더 관리하기 쉽다. 이 문법의 예시로서 guestbook-all-in-one.yaml 파일을 참고한다.

  • 많은 kubectl 커맨드들은 디렉터리에 대해 호출될 수 있다. 예를 들어, 구성 파일들의 디렉터리에 대해 kubectl apply를 호출할 수 있다.

  • 불필요하게 기본 값을 명시하지 않는다. 간단하고 최소한의 설정은 에러를 덜 발생시킨다.

  • 더 나은 인트로스펙션(introspection)을 위해서, 어노테이션에 오브젝트의 설명을 넣는다.

"단독(Naked)" 파드 vs 레플리카셋(ReplicaSet), 디플로이먼트(Deployment), 그리고 잡(Job)

  • 가능하다면 단독 파드(즉, 레플리카셋이나 디플로이먼트에 연결되지 않은 파드)를 사용하지 않는다. 단독 파드는 노드 장애 이벤트가 발생해도 다시 스케줄링되지 않는다.

    명백하게 restartPolicy: Never를 사용하는 상황을 제외한다면, 의도한 파드의 수가 항상 사용 가능한 상태를 유지하는 레플리카셋을 생성하고, 파드를 교체하는 전략(롤링 업데이트와 같은)을 명시하는 디플로이먼트는 파드를 직접 생성하기 위해 항상 선호되는 방법이다. 또한 적절할 수 있다.

서비스

  • 서비스에 대응하는 백엔드 워크로드(디플로이먼트 또는 레플리카셋) 또는 서비스 접근이 필요한 어떠한 워크로드를 생성하기 전에 서비스를 미리 생성한다. 쿠버네티스가 컨테이너를 시작할 때, 쿠버네티스는 컨테이너 시작 당시에 생성되어 있는 모든 서비스를 가리키는 환경 변수를 컨테이너에 제공한다. 예를 들어, foo 라는 이름의 서비스가 존재한다면, 모든 컨테이너들은 초기 환경에서 다음의 변수들을 얻을 것이다.

    FOO_SERVICE_HOST=<서비스가 동작 중인 호스트>
    FOO_SERVICE_PORT=<서비스가 동작 중인 포트>
    

    이는 순서를 정하는 일이 요구됨을 암시한다 - 파드가 접근하기를 원하는 어떠한 서비스파드 스스로가 생성되기 전에 미리 생성되어 있어야 하며, 그렇지 않으면 환경 변수가 설정되지 않을 것이다. DNS는 이러한 제한을 가지고 있지 않다.

  • 선택적인(그렇지만 매우 권장되는) 클러스터 애드온은 DNS 서버이다. DNS 서버는 새로운 서비스를 위한 쿠버네티스 API를 Watch하며, 각 서비스를 위한 DNS 레코드 셋을 생성한다. 만약 DNS가 클러스터에 걸쳐 활성화되어 있다면, 모든 파드서비스의 이름을 자동으로 해석할 수 있어야 한다.

  • 반드시 필요한 것이 아니라면 파드에 hostPort 를 명시하지 않는다. <hostIP, hostPort, protocol> 조합은 유일해야 하기 때문에, hostPort로 바인드하는 것은 파드가 스케줄링될 수 있는 위치의 개수를 제한한다. 만약 hostIPprotocol을 뚜렷히 명시하지 않으면, 쿠버네티스는 hostIP의 기본 값으로 0.0.0.0를, protocol의 기본 값으로 TCP를 사용한다.

    만약 오직 디버깅의 목적으로 포트에 접근해야 한다면, apiserver proxy 또는 kubectl port-forward를 사용할 수 있다.

    만약 파드의 포트를 노드에서 명시적으로 노출해야 한다면, hostPort에 의존하기 전에 NodePort 서비스를 사용하는 것을 고려할 수 있다.

  • hostPort와 같은 이유로, hostNetwork를 사용하는 것을 피한다.

  • kube-proxy 로드 밸런싱이 필요하지 않을 때, 서비스 발견을 위해 헤드리스 서비스(ClusterIP의 값을 None으로 가지는)를 사용한다.

레이블 사용하기

  • { app: myapp, tier: frontend, phase: test, deployment: v3 }처럼 애플리케이션이나 디플로이먼트의 속성에 대한 의미 를 식별하는 레이블을 정의해 사용한다. 다른 리소스를 위해 적절한 파드를 선택하는 용도로 이러한 레이블을 이용할 수 있다. 예를 들어, 모든 tier: frontend 파드를 선택하거나, app: myapp의 모든 phase: test 컴포넌트를 선택하는 서비스를 생각해 볼 수 있다. 이 접근 방법의 예시는 방명록 앱을 참고한다.

릴리스에 특정되는 레이블을 서비스의 셀렉터에서 생략함으로써 여러 개의 디플로이먼트에 걸치는 서비스를 생성할 수 있다. 동작 중인 서비스를 다운타임 없이 갱신하려면, 디플로이먼트를 사용한다.

오브젝트의 의도한 상태는 디플로이먼트에 의해 기술되며, 만약 그 스펙에 대한 변화가 적용될 경우, 디플로이먼트 컨트롤러는 일정한 비율로 실제 상태를 의도한 상태로 변화시킨다.

  • 일반적인 활용 사례인 경우 쿠버네티스 공통 레이블을 사용한다. 이 표준화된 레이블은 kubectl대시보드와 같은 도구들이 상호 운용이 가능한 방식으로 동작할 수 있도록 메타데이터를 향상시킨다.

  • 디버깅을 위해 레이블을 조작할 수 있다. (레플리카셋과 같은) 쿠버네티스 컨트롤러와 서비스는 셀렉터 레이블을 사용해 파드를 선택하기 때문에, 관련된 레이블을 파드에서 삭제하는 것은 컨트롤러로부터 관리되거나 서비스로부터 트래픽을 전달받는 것을 중단시킨다. 만약 이미 존재하는 파드의 레이블을 삭제한다면, 파드의 컨트롤러는 그 자리를 대신할 새로운 파드를 생성한다. 이것은 이전에 "살아 있는" 파드를 "격리된" 환경에서 디버그할 수 있는 유용한 방법이다. 레이블을 상호적으로 추가하고 삭제하기 위해서, kubectl label를 사용할 수 있다.

kubectl 사용하기

3.7.2 - 컨피그맵(ConfigMap)

컨피그맵은 키-값 쌍으로 기밀이 아닌 데이터를 저장하는 데 사용하는 API 오브젝트이다. 파드볼륨에서 환경 변수, 커맨드-라인 인수 또는 구성 파일로 컨피그맵을 사용할 수 있다.

컨피그맵을 사용하면 컨테이너 이미지에서 환경별 구성을 분리하여, 애플리케이션을 쉽게 이식할 수 있다.

사용 동기

애플리케이션 코드와 별도로 구성 데이터를 설정하려면 컨피그맵을 사용하자.

예를 들어, 자신의 컴퓨터(개발용)와 클라우드(실제 트래픽 처리)에서 실행할 수 있는 애플리케이션을 개발한다고 가정해보자. DATABASE_HOST 라는 환경 변수를 찾기 위해 코드를 작성한다. 로컬에서는 해당 변수를 localhost 로 설정한다. 클라우드에서는, 데이터베이스 컴포넌트를 클러스터에 노출하는 쿠버네티스 서비스를 참조하도록 설정한다. 이를 통해 클라우드에서 실행 중인 컨테이너 이미지를 가져와 필요한 경우 정확히 동일한 코드를 로컬에서 디버깅할 수 있다.

컨피그맵은 많은 양의 데이터를 보유하도록 설계되지 않았다. 컨피그맵에 저장된 데이터는 1MiB를 초과할 수 없다. 이 제한보다 큰 설정을 저장해야 하는 경우, 볼륨을 마운트하는 것을 고려하거나 별도의 데이터베이스 또는 파일 서비스를 사용할 수 있다.

컨피그맵 오브젝트

컨피그맵은 다른 오브젝트가 사용할 구성을 저장할 수 있는 API 오브젝트이다. spec 이 있는 대부분의 쿠버네티스 오브젝트와 달리, 컨피그맵에는 databinaryData 필드가 있다. 이러한 필드는 키-값 쌍을 값으로 허용한다. data 필드와 binaryData 는 모두 선택 사항이다. data 필드는 UTF-8 바이트 시퀀스를 포함하도록 설계되었으며 binaryData 필드는 바이너리 데이터를 base64로 인코딩된 문자열로 포함하도록 설계되었다.

컨피그맵의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

data 또는 binaryData 필드 아래의 각 키는 영숫자 문자, -, _ 또는 . 으로 구성되어야 한다. data 에 저장된 키는 binaryData 필드의 키와 겹치지 않아야 한다.

v1.19부터 컨피그맵 정의에 immutable 필드를 추가하여 변경할 수 없는 컨피그맵을 만들 수 있다.

컨피그맵과 파드

컨피그맵을 참조하는 파드 spec 을 작성하고 컨피그맵의 데이터를 기반으로 해당 파드의 컨테이너를 구성할 수 있다. 파드와 컨피그맵은 동일한 네임스페이스에 있어야 한다.

다음은 단일 값을 가진 키와, 값이 구성 형식의 일부처럼 보이는 키를 가진 컨피그맵의 예시이다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 속성과 비슷한 키; 각 키는 간단한 값으로 매핑됨
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # 파일과 비슷한 키
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true    

컨피그맵을 사용하여 파드 내부에 컨테이너를 구성할 수 있는 네 가지 방법이 있다.

  1. 컨테이너 커맨드와 인수 내에서
  2. 컨테이너에 대한 환경 변수
  3. 애플리케이션이 읽을 수 있도록 읽기 전용 볼륨에 파일 추가
  4. 쿠버네티스 API를 사용하여 컨피그맵을 읽는 파드 내에서 실행할 코드 작성

이러한 방법들은 소비되는 데이터를 모델링하는 방식에 따라 다르게 쓰인다. 처음 세 가지 방법의 경우, kubelet은 파드의 컨테이너를 시작할 때 컨피그맵의 데이터를 사용한다.

네 번째 방법은 컨피그맵과 데이터를 읽기 위해 코드를 작성해야 한다는 것을 의미한다. 그러나, 쿠버네티스 API를 직접 사용하기 때문에, 애플리케이션은 컨피그맵이 변경될 때마다 업데이트를 받기 위해 구독할 수 있고, 업데이트가 있으면 반응한다. 쿠버네티스 API에 직접 접근하면, 이 기술을 사용하여 다른 네임스페이스의 컨피그맵에 접근할 수도 있다.

다음은 game-demo 의 값을 사용하여 파드를 구성하는 파드 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # 환경 변수 정의
        - name: PLAYER_INITIAL_LIVES # 참고로 여기서는 컨피그맵의 키 이름과
                                     # 대소문자가 다르다.
          valueFrom:
            configMapKeyRef:
              name: game-demo           # 이 값의 컨피그맵.
              key: player_initial_lives # 가져올 키.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    # 파드 레벨에서 볼륨을 설정한 다음, 해당 파드 내의 컨테이너에 마운트한다.
    - name: config
      configMap:
        # 마운트하려는 컨피그맵의 이름을 제공한다.
        name: game-demo
        # 컨피그맵에서 파일로 생성할 키 배열
        items:
        - key: "game.properties"
          path: "game.properties"
        - key: "user-interface.properties"
          path: "user-interface.properties"

컨피그맵은 단일 라인 속성(single line property) 값과 멀티 라인의 파일과 비슷한(multi-line file-like) 값을 구분하지 않는다. 더 중요한 것은 파드와 다른 오브젝트가 이러한 값을 소비하는 방식이다.

이 예제에서, 볼륨을 정의하고 demo 컨테이너에 /config 로 마운트하면 컨피그맵에 4개의 키가 있더라도 /config/game.properties/config/user-interface.properties 2개의 파일이 생성된다. 이것은 파드 정의가 volume 섹션에서 items 배열을 지정하기 때문이다. items 배열을 완전히 생략하면, 컨피그맵의 모든 키가 키와 이름이 같은 파일이 되고, 4개의 파일을 얻게 된다.

컨피그맵 사용하기

컨피그맵은 데이터 볼륨으로 마운트할 수 있다. 컨피그맵은 파드에 직접적으로 노출되지 않고, 시스템의 다른 부분에서도 사용할 수 있다. 예를 들어, 컨피그맵은 시스템의 다른 부분이 구성을 위해 사용해야 하는 데이터를 보유할 수 있다.

컨피그맵을 사용하는 가장 일반적인 방법은 동일한 네임스페이스의 파드에서 실행되는 컨테이너에 대한 설정을 구성하는 것이다. 컨피그맵을 별도로 사용할 수도 있다.

예를 들어, 컨피그맵에 기반한 동작을 조정하는 애드온이나 오퍼레이터를 사용할 수도 있다.

파드에서 컨피그맵을 파일로 사용하기

파드의 볼륨에서 컨피그맵을 사용하려면 다음을 수행한다.

  1. 컨피그맵을 생성하거나 기존 컨피그맵을 사용한다. 여러 파드가 동일한 컨피그맵을 참조할 수 있다.
  2. 파드 정의를 수정해서 .spec.volumes[] 아래에 볼륨을 추가한다. 볼륨 이름은 원하는 대로 정하고, 컨피그맵 오브젝트를 참조하도록 .spec.volumes[].configMap.name 필드를 설정한다.
  3. 컨피그맵이 필요한 각 컨테이너에 .spec.containers[].volumeMounts[] 를 추가한다. .spec.containers[].volumeMounts[].readOnly = true 를 설정하고 컨피그맵이 연결되기를 원하는 곳에 사용하지 않은 디렉터리 이름으로 .spec.containers[].volumeMounts[].mountPath 를 지정한다.
  4. 프로그램이 해당 디렉터리에서 파일을 찾도록 이미지 또는 커맨드 라인을 수정한다. 컨피그맵의 data 맵 각 키는 mountPath 아래의 파일 이름이 된다.

다음은 볼륨에 컨피그맵을 마운트하는 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configmap:
      name: myconfigmap

사용하려는 각 컨피그맵은 .spec.volumes 에서 참조해야 한다.

파드에 여러 컨테이너가 있는 경우 각 컨테이너에는 자체 volumeMounts 블록이 필요하지만, 컨피그맵은 각 컨피그맵 당 하나의 .spec.volumes 만 필요하다.

마운트된 컨피그맵이 자동으로 업데이트

현재 볼륨에서 사용된 컨피그맵이 업데이트되면, 프로젝션된 키도 마찬가지로 업데이트된다. kubelet은 모든 주기적인 동기화에서 마운트된 컨피그맵이 최신 상태인지 확인한다. 그러나, kubelet은 로컬 캐시를 사용해서 컨피그맵의 현재 값을 가져온다. 캐시 유형은 KubeletConfiguration 구조체ConfigMapAndSecretChangeDetectionStrategy 필드를 사용해서 구성할 수 있다. 컨피그맵은 watch(기본값), ttl 기반 또는 API 서버로 직접 모든 요청을 리디렉션할 수 있다. 따라서 컨피그맵이 업데이트되는 순간부터 새 키가 파드에 업데이트되는 순간까지의 총 지연시간은 kubelet 동기화 기간 + 캐시 전파 지연만큼 길 수 있다. 여기서 캐시 전파 지연은 선택한 캐시 유형에 따라 달라질 수 있다(전파 지연을 지켜보거나, 캐시의 ttl 또는 0에 상응함).

환경 변수로 사용되는 컨피그맵은 자동으로 업데이트되지 않으며 파드를 다시 시작해야 한다.

변경할 수 없는(immutable) 컨피그맵

FEATURE STATE: Kubernetes v1.21 [stable]

쿠버네티스 기능인 변경할 수 없는 시크릿과 컨피그맵 은 개별 시크릿과 컨피그맵을 변경할 수 없는 것으로 설정하는 옵션을 제공한다. 컨피그맵을 광범위하게 사용하는 클러스터(최소 수만 개의 고유한 컨피그맵이 파드에 마운트)의 경우 데이터 변경을 방지하면 다음과 같은 이점이 있다.

  • 애플리케이션 중단을 일으킬 수 있는 우발적(또는 원하지 않는) 업데이트로부터 보호
  • immutable로 표시된 컨피그맵에 대한 감시를 중단하여, kube-apiserver의 부하를 크게 줄임으로써 클러스터의 성능을 향상시킴

이 기능은 ImmutableEphemeralVolumes 기능 게이트에 의해 제어된다. immutable 필드를 true 로 설정하여 변경할 수 없는 컨피그맵을 생성할 수 있다. 다음은 예시이다.

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

컨피그맵을 immutable로 표시하면, 이 변경 사항을 되돌리거나 data 또는 binaryData 필드 내용을 변경할 수 없다. 컨피그맵만 삭제하고 다시 작성할 수 있다. 기존 파드는 삭제된 컨피그맵에 대한 마운트 지점을 유지하므로, 이러한 파드를 다시 작성하는 것을 권장한다.

다음 내용

3.7.3 - 시크릿(Secret)

시크릿은 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트이다. 이를 사용하지 않으면 중요한 정보가 파드 명세나 컨테이너 이미지에 포함될 수 있다. 시크릿을 사용한다는 것은 사용자의 기밀 데이터를 애플리케이션 코드에 넣을 필요가 없음을 뜻한다.

시크릿은 시크릿을 사용하는 파드와 독립적으로 생성될 수 있기 때문에, 파드를 생성하고, 확인하고, 수정하는 워크플로우 동안 시크릿(그리고 데이터)이 노출되는 것에 대한 위험을 경감시킬 수 있다. 쿠버네티스 및 클러스터에서 실행되는 애플리케이션은 기밀 데이터를 비휘발성 저장소에 쓰는 것을 피하는 것과 같이, 시크릿에 대해 추가 예방 조치를 취할 수도 있다.

시크릿은 컨피그맵과 유사하지만 특별히 기밀 데이터를 보관하기 위한 것이다.

시크릿 개요

시크릿을 사용하려면, 파드가 시크릿을 참조해야 한다. 시크릿은 세 가지 방법으로 파드와 함께 사용할 수 있다.

쿠버네티스 컨트롤 플레인 또한 시크릿을 사용한다. 예를 들어, 부트스트랩 토큰 시크릿은 노드 등록을 자동화하는 데 도움을 주는 메커니즘이다.

시크릿 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다. 사용자는 시크릿을 위한 파일을 구성할 때 data 및 (또는) stringData 필드를 명시할 수 있다. 해당 datastringData 필드는 선택적으로 명시할 수 있다. data 필드의 모든 키(key)에 해당하는 값(value)은 base64로 인코딩된 문자열이어야 한다. 만약 사용자에게 base64로의 문자열 변환이 적합하지 않다면, 임의의 문자열을 값으로 받는 stringData 필드를 대신 사용할 수 있다.

datastringData의 키는 영숫자 문자, -, _, 또는 . 으로 구성되어야 한다. stringData 필드의 모든 키-값 쌍은 의도적으로 data 필드로 합쳐진다. 만약 키가 datastringData 필드 모두에 정의되어 있으면, stringData 필드에 지정된 값이 우선적으로 사용된다.

시크릿 타입

시크릿을 생성할 때, Secret 리소스의 type 필드를 사용하거나, (활용 가능하다면) kubectl 의 유사한 특정 커맨드라인 플래그를 사용하여 시크릿의 타입을 명시할 수 있다. 시크릿 타입은 여러 종류의 기밀 데이터를 프로그래밍 방식으로 용이하게 처리하기 위해 사용된다.

쿠버네티스는 일반적인 사용 시나리오를 위해 몇 가지 빌트인 타입을 제공한다. 이 타입은 쿠버네티스가 부과하여 수행되는 검증 및 제약에 따라 달라진다.

빌트인 타입 사용처
Opaque 임의의 사용자 정의 데이터
kubernetes.io/service-account-token 서비스 어카운트 토큰
kubernetes.io/dockercfg 직렬화 된(serialized) ~/.dockercfg 파일
kubernetes.io/dockerconfigjson 직렬화 된 ~/.docker/config.json 파일
kubernetes.io/basic-auth 기본 인증을 위한 자격 증명(credential)
kubernetes.io/ssh-auth SSH를 위한 자격 증명
kubernetes.io/tls TLS 클라이언트나 서버를 위한 데이터
bootstrap.kubernetes.io/token 부트스트랩 토큰 데이터

사용자는 시크릿 오브젝트의 type 값에 비어 있지 않은 문자열을 할당하여 자신만의 시크릿 타입을 정의하고 사용할 수 있다. 비어 있는 문자열은 Opaque 타입으로 인식된다. 쿠버네티스는 타입 명칭에 제약을 부과하지는 않는다. 그러나 만약 빌트인 타입 중 하나를 사용한다면, 해당 타입에 정의된 모든 요구 사항을 만족시켜야 한다.

불투명(Opaque) 시크릿

Opaque 은 시크릿 구성 파일에서 누락된 경우의 기본 시크릿 타입이다. kubectl 을 사용하여 시크릿을 생성할 때 Opaque 시크릿 타입을 나타내기 위해서는 generic 하위 커맨드를 사용할 것이다. 예를 들어, 다음 커맨드는 타입 Opaque 의 비어 있는 시크릿을 생성한다.

kubectl create secret generic empty-secret
kubectl get secret empty-secret

출력은 다음과 같다.

NAME           TYPE     DATA   AGE
empty-secret   Opaque   0      2m6s

해당 DATA 열은 시크릿에 저장된 데이터 아이템의 수를 보여준다. 이 경우, 0 은 비어 있는 시크릿을 하나 생성하였다는 것을 의미한다.

서비스 어카운트 토큰 시크릿

kubernetes.io/service-account-token 시크릿 타입은 서비스 어카운트를 확인하는 토큰을 저장하기 위해서 사용한다. 이 시크릿 타입을 사용할 때는, kubernetes.io/service-account.name 어노테이션이 존재하는 서비스 어카운트 이름으로 설정되도록 해야 한다. 쿠버네티스 컨트롤러는 kubernetes.io/service-account.uid 및 실제 토큰 콘텐츠로 설정된 data 필드의 token 키와 같은, 몇 가지 다른 필드들을 채운다.

다음은 서비스 어카운트 토큰 시크릿의 구성 예시이다.

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  # 사용자는 불투명 시크릿을 사용하므로 추가적인 키 값 쌍을 포함할 수 있다.
  extra: YmFyCg==

Pod 를 생성할 때, 쿠버네티스는 자동으로 서비스 어카운트 시크릿을 생성하고 자동으로 파드가 해당 시크릿을 사용하도록 수정한다. 해당 서비스 어카운트 토큰 시크릿은 API 접속을 위한 자격 증명을 포함한다.

이러한 API 자격 증명의 자동 생성과 사용은 원하는 경우 해제하거나 기각할 수 있다. 그러나 만약 사용자가 API 서버에 안전하게 접근하는 것만 필요하다면, 이것이 권장되는 워크플로우이다.

서비스 어카운트 문서를 보면 서비스 어카운트가 동작하는 방법에 대한 더 자세한 정보를 얻을 수 있다. 또한 파드에서 서비스 어카운트를 참조하는 방법을 PodautomountServiceAccountToken 필드와 serviceAccountName 필드를 통해 확인할 수 있다.

도커 컨피그 시크릿

이미지에 대한 도커 레지스트리 접속 자격 증명을 저장하기 위한 시크릿을 생성하기 위해서 다음의 type 값 중 하나를 사용할 수 있다.

  • kubernetes.io/dockercfg
  • kubernetes.io/dockerconfigjson

kubernetes.io/dockercfg 는 직렬화 된 도커 커맨드라인 구성을 위한 기존(legacy) 포맷 ~/.dockercfg 를 저장하기 위해 할당된 타입이다. 시크릿 타입을 사용할 때는, data 필드가 base64 포맷으로 인코딩된 ~/.dockercfg 파일의 콘텐츠를 값으로 가지는 .dockercfg 키를 포함하고 있는지 확실히 확인해야 한다.

kubernetes.io/dockerconfigjson 타입은 ~/.dockercfg 의 새로운 포맷인 ~/.docker/config.json 파일과 동일한 포맷 법칙을 따르는 직렬화 된 JSON의 저장을 위해 디자인되었다. 이 시크릿 타입을 사용할 때는, 시크릿 오브젝트의 data 필드가 .dockerconfigjson 키를 꼭 포함해야 한다. ~/.docker/config.json 파일을 위한 콘텐츠는 base64로 인코딩된 문자열으로 제공되어야 한다.

아래는 시크릿의 kubernetes.io/dockercfg 타입 예시이다.

apiVersion: v1
kind: Secret
metadata:
  name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
  .dockercfg: |
        "<base64 encoded ~/.dockercfg file>"

이러한 타입들을 매니페스트를 사용하여 생성하는 경우, API 서버는 해당 data 필드에 기대하는 키가 존재하는지 확인하고, 제공된 값이 유효한 JSON으로 파싱될 수 있는지 검증한다. API 서버가 해당 JSON이 실제 도커 컨피그 파일인지를 검증하지는 않는다.

도커 컨피그 파일을 가지고 있지 않거나 도커 레지스트리 시크릿을 생성하기 위해 kubectl 을 사용하고 싶은 경우, 다음과 같이 처리할 수 있다.

kubectl create secret docker-registry secret-tiger-docker \
  --docker-username=tiger \
  --docker-password=pass113 \
  --docker-email=[email protected] \
  --docker-server=my-registry.example:5000

이 커맨드는 kubernetes.io/dockerconfigjson 타입의 시크릿을 생성한다. 만약 data 필드로부터 .dockerconfigjson 콘텐츠를 복사(dump)해오면, 다음과 같이 유효한 도커 JSON 콘텐츠를 즉석에서 얻게 될 것이다.

{
    "apiVersion": "v1",
    "data": {
        ".dockerconfigjson": "eyJhdXRocyI6eyJteS1yZWdpc3RyeTo1MDAwIjp7InVzZXJuYW1lIjoidGlnZXIiLCJwYXNzd29yZCI6InBhc3MxMTMiLCJlbWFpbCI6InRpZ2VyQGFjbWUuY29tIiwiYXV0aCI6ImRHbG5aWEk2Y0dGemN6RXhNdz09In19fQ=="
    },
    "kind": "Secret",
    "metadata": {
        "creationTimestamp": "2021-07-01T07:30:59Z",
        "name": "secret-tiger-docker",
        "namespace": "default",
        "resourceVersion": "566718",
        "uid": "e15c1d7b-9071-4100-8681-f3a7a2ce89ca"
    },
    "type": "kubernetes.io/dockerconfigjson"
}

기본 인증 시크릿

kubernetes.io/basic-auth 타입은 기본 인증을 위한 자격 증명을 저장하기 위해 제공된다. 이 시크릿 타입을 사용할 때는 시크릿의 data 필드가 다음의 두 키를 포함해야 한다.

  • username: 인증을 위한 사용자 이름
  • password: 인증을 위한 암호나 토큰

위의 두 키에 대한 두 값은 모두 base64로 인코딩된 문자열이다. 물론, 시크릿 생성 시 stringData 를 사용하여 평문 텍스트 콘텐츠(clear text content)를 제공할 수도 있다.

다음의 YAML은 기본 인증 시크릿을 위한 구성 예시이다.

apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: t0p-Secret

이 기본 인증 시크릿 타입은 사용자 편의만을 위해서 제공된다. 사용자는 기본 인증에서 사용되는 자격 증명을 위한 Opaque 를 생성할 수도 있다. 그러나, 빌트인 시크릿 타입을 사용하는 것은 사용자의 자격 증명들의 포맷을 통합하는 데 도움이 되고, API 서버는 요구되는 키가 시크릿 구성에서 제공되고 있는지 검증도 한다.

SSH 인증 시크릿

이 빌트인 타입 kubernetes.io/ssh-auth 는 SSH 인증에 사용되는 데이터를 저장하기 위해서 제공된다. 이 시크릿 타입을 사용할 때는 ssh-privatekey 키-값 쌍을 사용할 SSH 자격 증명으로 data (또는 stringData) 필드에 명시해야 할 것이다.

다음 YAML은 SSH 인증 시크릿의 구성 예시이다.

apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  # 본 예시를 위해 축약된 데이터임
  ssh-privatekey: |
          MIIEpQIBAAKCAQEAulqb/Y ...

SSH 인증 시크릿 타입은 사용자 편의만을 위해서 제공된다. 사용자는 SSH 인증에서 사용되는 자격 증명을 위한 Opaque 를 생성할 수도 있다. 그러나, 빌트인 시크릿 타입을 사용하는 것은 사용자의 자격 증명들의 포맷을 통합하는 데 도움이 되고, API 서버는 요구되는 키가 시크릿 구성에서 제공되고 있는지 검증도 한다.

TLS 시크릿

쿠버네티스는 보통 TLS를 위해 사용되는 인증서와 관련된 키를 저장하기 위해서 빌트인 시크릿 타입 kubernetes.io/tls 를 제공한다. 이 데이터는 인그레스 리소스의 TLS 종료에 주로 사용되지만, 다른 리소스나 워크로드에 의해 직접적으로 사용될 수도 있다. 이 타입의 시크릿을 사용할 때는 tls.keytls.crt 키가 시크릿 구성의 data (또는 stringData) 필드에서 제공되어야 한다. 그러나, API 서버가 각 키에 대한 값이 유효한지 실제로 검증하지는 않는다.

다음 YAML은 TLS 시크릿을 위한 구성 예시를 포함한다.

apiVersion: v1
kind: Secret
metadata:
  name: secret-tls
type: kubernetes.io/tls
data:
  # 본 예시를 위해 축약된 데이터임
  tls.crt: |
        MIIC2DCCAcCgAwIBAgIBATANBgkqh ...
  tls.key: |
        MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...

TLS 시크릿 타입은 사용자 편의만을 위해서 제공된다. 사용자는 TLS 서버 및/또는 클라이언트를 위해 사용되는 자격 증명을 위한 Opaque 를 생성할 수도 있다. 그러나, 빌트인 시크릿 타입을 사용하는 것은 사용자의 자격 증명들의 포맷을 통합하는 데 도움이 되고, API 서버는 요구되는 키가 시크릿 구성에서 제공되고 있는지 검증도 한다.

kubectl 를 사용하여 TLS 시크릿을 생성할 때, tls 하위 커맨드를 다음 예시와 같이 사용할 수 있다.

kubectl create secret tls my-tls-secret \
  --cert=path/to/cert/file \
  --key=path/to/key/file

공개/개인 키 쌍은 사전에 존재해야 한다. --cert 를 위한 공개 키 인증서는 .PEM 으로 인코딩(Base64로 인코딩된 DER 포맷)되어야 하며, --key 를 위해 주어진 개인 키에 맞아야 한다. 개인 키는 일반적으로 PEM 개인 키 포맷이라고 하는, 암호화되지 않은 형태(unencrypted)이어야 한다. 두 가지 방식 모두에 대해서, PEM의 시작과 끝 라인(예를 들면, 인증서의 --------BEGIN CERTIFICATE------------END CERTIFICATE----) 은 포함되면 된다.

부트스트랩 토큰 시크릿

부트스트랩 토큰 시크릿은 시크릿 typebootstrap.kubernetes.io/token 으로 명확하게 지정하면 생성할 수 있다. 이 타입의 시크릿은 노드 부트스트랩 과정 중에 사용되는 토큰을 위해 디자인되었다. 이것은 잘 알려진 컨피그맵에 서명하는 데 사용되는 토큰을 저장한다.

부트스트랩 토큰 시크릿은 보통 kube-system 네임스페이스에 생성되며 <token-id> 가 해당 토큰 ID의 6개 문자의 문자열으로 구성된 bootstrap-token-<token-id> 형태로 이름이 지정된다.

쿠버네티스 매니페스트로서, 부트스트렙 토큰 시크릿은 다음과 유사할 것이다.

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-5emitj
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
  expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
  token-id: NWVtaXRq
  token-secret: a3E0Z2lodnN6emduMXAwcg==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==

부트스트랩 타입 시크릿은 data 아래 명시된 다음의 키들을 가진다.

  • token-id: 토큰 식별자로 임의의 6개 문자의 문자열. 필수 사항.
  • token-secret: 실제 토큰 시크릿으로 임의의 16개 문자의 문자열. 필수 사항.
  • description: 토큰의 사용처를 설명하는 사람이 읽을 수 있는 문자열. 선택 사항.
  • expiration: 토큰이 만료되어야 하는 시기를 명시한 RFC3339를 사용하는 절대 UTC 시간. 선택 사항.
  • usage-bootstrap-<usage>: 부트스트랩 토큰의 추가적인 사용처를 나타내는 불리언(boolean) 플래그.
  • auth-extra-groups: system:bootstrappers 그룹에 추가로 인증될 쉼표로 구분된 그룹 이름 목록.

위의 YAML은 모두 base64로 인코딩된 문자열 값이므로 혼란스러워 보일 수 있다. 사실은 다음 YAML을 사용하여 동일한 시크릿을 생성할 수 있다.

apiVersion: v1
kind: Secret
metadata:
  # 시크릿 이름이 어떻게 지정되었는지 확인
  name: bootstrap-token-5emitj
  # 부트스트랩 토큰 시크릿은 일반적으로 kube-system 네임스페이스에 포함
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
  expiration: "2020-09-13T04:39:10Z"
  # 이 토큰 ID는 이름에 사용됨
  token-id: "5emitj"
  token-secret: "kq4gihvszzgn1p0r"
  # 이 토큰은 인증을 위해서 사용될 수 있음
  usage-bootstrap-authentication: "true"
  # 또한 서명(signing)에도 사용될 수 있음
  usage-bootstrap-signing: "true"

시크릿 생성하기

시크릿을 생성하기 위한 몇 가지 옵션이 있다.

시크릿 편집하기

기존 시크릿은 다음 명령을 사용하여 편집할 수 있다.

kubectl edit secrets mysecret

이렇게 하면 기본으로 설정된 에디터가 열리고 data 필드에 base64로 인코딩된 시크릿 값을 업데이트할 수 있다.

# 아래 오브젝트를 수정한다. '#'로 시작하는 줄은 무시되고,
# 빈 파일은 편집이 취소될 것이다. 이 파일을 저장하는 도중에 오류가 발생하면
# 관련 오류와 함께 다시 열린다.
#
apiVersion: v1
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: { ... }
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque

시크릿 사용하기

시크릿은 데이터 볼륨으로 마운트되거나 파드의 컨테이너에서 사용할 환경 변수로 노출될 수 있다. 또한, 시크릿은 파드에 직접 노출되지 않고, 시스템의 다른 부분에서도 사용할 수 있다. 예를 들어, 시크릿은 시스템의 다른 부분이 사용자를 대신해서 외부 시스템과 상호 작용하는 데 사용해야 하는 자격 증명을 보유할 수 있다.

시크릿을 파드의 파일로 사용하기

파드의 볼륨에서 시크릿을 사용하려면 다음과 같이 한다.

  1. 시크릿을 생성하거나 기존 시크릿을 사용한다. 여러 파드가 동일한 시크릿을 참조할 수 있다.
  2. .spec.volumes[]. 아래에 볼륨을 추가하려면 파드 정의를 수정한다. 볼륨의 이름을 뭐든지 지정하고, 시크릿 오브젝트의 이름과 동일한 .spec.volumes[].secret.secretName 필드를 생성한다.
  3. 시크릿이 필요한 각 컨테이너에 .spec.containers[].volumeMounts[] 를 추가한다. 시크릿을 표시하려는 사용되지 않은 디렉터리 이름에 .spec.containers[].volumeMounts[].readOnly = true.spec.containers[].volumeMounts[].mountPath 를 지정한다.
  4. 프로그램이 해당 디렉터리에서 파일을 찾도록 이미지 또는 커맨드 라인을 수정한다. 시크릿 data 맵의 각 키는 mountPath 아래의 파일명이 된다.

다음은 볼륨에 시크릿을 마운트하는 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

사용하려는 각 시크릿은 .spec.volumes 에서 참조해야 한다.

파드에 여러 컨테이너가 있는 경우, 모든 컨테이너는 자체 volumeMounts 블록이 필요하지만, 시크릿에 대해서는 시크릿당 하나의 .spec.volumes 만 필요하다.

많은 파일을 하나의 시크릿으로 패키징하거나, 여러 시크릿을 사용할 수 있으며, 어느 쪽이든 편리한 방법을 사용하면 된다.

특정 경로에 대한 시크릿 키 투영하기

시크릿 키가 투영되는 볼륨 내 경로를 제어할 수도 있다. .spec.volumes[].secret.items 필드를 사용하여 각 키의 대상 경로를 변경할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

다음과 같은 일들이 일어날 것이다.

  • username 시크릿은 /etc/foo/username 대신 /etc/foo/my-group/my-username 아래의 파일에 저장된다.
  • password 시크릿은 투영되지 않는다.

.spec.volumes[].secret.items 를 사용하면, items 에 지정된 키만 투영된다. 시크릿의 모든 키를 사용하려면, 모든 키가 items 필드에 나열되어야 한다. 나열된 모든 키는 해당 시크릿에 존재해야 한다. 그렇지 않으면, 볼륨이 생성되지 않는다.

시크릿 파일 퍼미션

단일 시크릿 키에 대한 파일 접근 퍼미션 비트를 설정할 수 있다. 만약 사용자가 퍼미션을 지정하지 않는다면, 기본적으로 0644 가 사용된다. 전체 시크릿 볼륨에 대한 기본 모드를 설정하고 필요한 경우 키별로 오버라이드할 수도 있다.

예를 들어, 다음과 같은 기본 모드를 지정할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0400

그러고 나면, 시크릿이 /etc/foo 에 마운트되고 시크릿 볼륨 마운트로 생성된 모든 파일의 퍼미션은 0400 이 될 것이다.

참고로 JSON 스펙은 8진수 표기법을 지원하지 않으므로, 0400 퍼미션에 대해서 값 256을 사용한다. 파드에 대해 JSON 대신 YAML을 사용하는 경우, 8진수 표기법을 사용하여 보다 자연스러운 방식으로 퍼미션을 지정할 수 있다.

참고로 파드에 kubectl exec 을 사용하는 경우, 예상되는 파일 모드를 찾기 위해 심볼릭 링크를 따라가야 한다. 예를 들면, 다음과 같다.

파드에서 시크릿 파일 모드를 확인한다.

kubectl exec mypod -it sh

cd /etc/foo
ls -l

출력 결과는 다음과 비슷하다.

total 0
lrwxrwxrwx 1 root root 15 May 18 00:18 password -> ..data/password
lrwxrwxrwx 1 root root 15 May 18 00:18 username -> ..data/username

올바른 파일 모드를 찾으려면 심볼릭 링크를 따라간다.

cd /etc/foo/..data
ls -l

출력 결과는 다음과 비슷하다.

total 8
-r-------- 1 root root 12 May 18 00:18 password
-r-------- 1 root root  5 May 18 00:18 username

이전 예제에서와 같이 매핑을 사용하여, 다음과 같이 다른 파일에 대해 다른 퍼미션을 지정할 수도 있다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 0777

이 경우, /etc/foo/my-group/my-username 에 있는 파일은 결과적으로 0777 퍼미션 값을 갖게 된다. JSON을 사용하는 경우, JSON 제한으로 인해 10진수 표기법(511)으로 모드를 지정해야 한다.

참고로 이 퍼미션 값은 나중에 읽을 때 10진수 표기법으로 표시될 수 있다.

볼륨에서 시크릿 값 사용하기

시크릿 볼륨을 마운트하는 컨테이너 내부에서, 시크릿 키는 파일로 나타나고 시크릿 값은 base64로 디코딩되어 이런 파일 내에 저장된다. 다음은 위의 예에서 컨테이너 내부에서 실행된 명령의 결과이다.

ls /etc/foo/

출력 결과는 다음과 비슷하다.

username
password
cat /etc/foo/username

출력 결과는 다음과 비슷하다.

admin
cat /etc/foo/password

출력 결과는 다음과 비슷하다.

1f2d1e2e67df

컨테이너의 프로그램은 파일에서 시크릿을 읽는 역할을 한다.

마운트된 시크릿은 자동으로 업데이트됨

볼륨에서 현재 사용되는 시크릿이 업데이트되면, 투영된 키도 결국 업데이트된다. kubelet은 마운트된 시크릿이 모든 주기적인 동기화에서 최신 상태인지 여부를 확인한다. 그러나, kubelet은 시크릿의 현재 값을 가져 오기 위해 로컬 캐시를 사용한다. 캐시의 유형은 KubeletConfiguration 구조체ConfigMapAndSecretChangeDetectionStrategy 필드를 사용하여 구성할 수 있다. 시크릿은 watch(기본값), ttl 기반 또는 API 서버로 모든 요청을 직접 리디렉션하여 전파할 수 있다. 결과적으로, 시크릿이 업데이트된 순간부터 새로운 키가 파드에 투영되는 순간까지의 총 지연 시간은 kubelet 동기화 시간 + 캐시 전파 지연만큼 길 수 있다. 여기서 캐시 전파 지연은 선택한 캐시 유형에 따라 달라질 수 있다(캐시 전파 지연은 각 캐시 유형에 따라 watch 전파 지연, 캐시의 ttl, 또는 0 에 상응함).

시크릿을 환경 변수로 사용하기

파드에서 환경 변수에 시크릿을 사용하려면 다음과 같이 한다.

  1. 시크릿을 생성하거나 기존 시크릿을 사용한다. 여러 파드가 동일한 시크릿을 참조할 수 있다.
  2. 사용하려는 각 시크릿 키에 대한 환경 변수를 추가하려면 시크릿 키 값을 사용하려는 각 컨테이너에서 파드 정의를 수정한다. 시크릿 키를 사용하는 환경 변수는 시크릿의 이름과 키를 env[].valueFrom.secretKeyRef 에 채워야 한다.
  3. 프로그램이 지정된 환경 변수에서 값을 찾도록 이미지 및/또는 커맨드 라인을 수정한다.

다음은 환경 변수의 시크릿을 사용하는 파드의 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

환경 변수에서 시크릿 값 사용하기

환경 변수에서 시크릿을 사용하는 컨테이너 내부에서, 시크릿 키는 시크릿 데이터의 base64 디코딩된 값을 포함하는 일반 환경 변수로 나타난다. 다음은 위의 예에서 컨테이너 내부에서 실행된 명령의 결과이다.

echo $SECRET_USERNAME

출력 결과는 다음과 비슷하다.

admin
echo $SECRET_PASSWORD

출력 결과는 다음과 비슷하다.

1f2d1e2e67df

시크릿 업데이트 후 환경 변수가 업데이트되지 않음

컨테이너가 환경 변수에서 이미 시크릿을 사용하는 경우, 다시 시작하지 않는 한 컨테이너에서 시크릿 업데이트를 볼 수 없다. 시크릿이 변경될 때 재시작을 트리거하는 써드파티 솔루션이 있다.

변경할 수 없는(immutable) 시크릿

FEATURE STATE: Kubernetes v1.21 [stable]

쿠버네티스 기능인 변경할 수 없는 시크릿과 컨피그맵 은 개별 시크릿과 컨피그맵을 변경할 수 없는 것으로 설정하는 옵션을 제공한다. 시크릿을 광범위하게 사용하는 클러스터(최소 수만 개의 고유한 시크릿이 파드에 마운트)의 경우, 데이터 변경을 방지하면 다음과 같은 이점이 있다.

  • 애플리케이션 중단을 유발할 수 있는 우발적(또는 원하지 않는) 업데이트로부터 보호
  • immutable로 표시된 시크릿에 대한 감시를 중단하여, kube-apiserver의 부하를 크게 줄임으로써 클러스터의 성능을 향상시킴

이 기능은 v1.19부터 기본적으로 활성화된 ImmutableEphemeralVolumes 기능 게이트에 의해 제어된다. immutable 필드를 true 로 설정하여 변경할 수 없는 시크릿을 생성할 수 있다. 다음은 예시이다.

apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
immutable: true

imagePullSecrets 사용하기

imagePullSecrets 필드는 동일한 네임스페이스의 시크릿에 대한 참조 목록이다. imagePullSecretsDocker 를 사용하여 도커(또는 다른 컨테이너) 이미지 레지스트리 비밀번호가 포함된 시크릿을 kubelet에 전달할 수 있다. kubelet은 이 정보를 사용해서 파드를 대신하여 프라이빗 이미지를 가져온다. imagePullSecrets 필드에 대한 자세한 정보는 PodSpec API를 참고한다.

imagePullSecret 수동으로 지정하기

컨테이너 이미지 문서에서 ImagePullSecrets 지정하는 방법을 배울 수 있다.

imagePullSecrets가 자동으로 연결되도록 정렬하기

수동으로 imagePullSecrets 를 생성하고, 서비스어카운트(ServiceAccount)에서 참조할 수 있다. 해당 서비스어카운트로 생성되거나 기본적인 서비스어카운트로 생성된 모든 파드는 파드의 imagePullSecrets 필드를 가져오고 서비스 어카운트의 필드로 설정한다. 해당 프로세스에 대한 자세한 설명은 서비스 어카운트에 ImagePullSecrets 추가하기를 참고한다.

상세 내용

제약 사항

시크릿 볼륨 소스는 지정된 오브젝트 참조가 실제로 시크릿 유형의 오브젝트를 가리키는지 확인하기 위해 유효성을 검사한다. 따라서, 시크릿에 의존하는 모든 파드보다 먼저 시크릿을 만들어야 한다.

시크릿 리소스는 네임스페이스에 존재한다. 시크릿은 동일한 네임스페이스에 있는 파드에서만 참조할 수 있다.

개별 시크릿의 크기는 1MiB로 제한된다. 이는 API 서버와 kubelet 메모리를 소진시키는 매우 큰 시크릿 생성을 막기 위한 것이다. 그러나, 많은 작은 시크릿을 만들어도 메모리가 고갈될 수 있다. 시크릿으로 인한 메모리 사용에 대한 보다 포괄적인 제한은 향후 버전에 계획된 기능이다.

kubelet은 API 서버에서 시크릿을 가져오는 파드에 대한 시크릿 사용만 지원한다. 여기에는 kubectl 을 사용하거나, 레플리케이션 컨트롤러를 통해 간접적으로 생성된 모든 파드가 포함된다. kubelet의 --manifest-url 플래그, --config 플래그 또는 kubectl의 REST API(이 방법들은 파드를 생성하는 일반적인 방법이 아님)로 생성된 파드는 포함하지 않는다. 스태틱(static) 파드spec은 컨피그맵 또는 다른 API 오브젝트를 참조할 수 없다.

시크릿은 optional(선택 사항)로 표시되지 않는 한 파드에서 환경 변수로 사용되기 전에 생성되어야 한다. 존재하지 않는 시크릿을 참조하면 파드가 시작되지 않는다.

명명된 시크릿에 존재하지 않는 키에 대한 참조(secretKeyRef 필드)는 파드가 시작되지 않도록 한다.

잘못된 환경 변수 이름으로 간주되는 키가 있는 envFrom 필드로 환경 변수를 채우는 데 사용되는 시크릿은 해당 키를 건너뛴다. 이러면 해당 파드가 시작될 수 있다. 원인이 InvalidVariableNames 인 이벤트가 발생하며 건너뛴 유효하지 않은 키 목록이 포함된 메시지가 생성된다. 다음의 예는 2개의 유효하지 않은 키(1badkey2alsobad)가 포함된 default/mysecret을 참조하는 파드를 보여준다.

kubectl get events

출력 결과는 다음과 비슷하다.

LASTSEEN   FIRSTSEEN   COUNT     NAME            KIND      SUBOBJECT                         TYPE      REASON
0s         0s          1         dapi-test-pod   Pod                                         Warning   InvalidEnvironmentVariableNames   kubelet, 127.0.0.1      Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.

시크릿 및 파드 수명 상호 작용

쿠버네티스 API를 호출하여 파드가 생성될 때, 참조된 시크릿이 있는지 확인하지 않는다. 일단 파드가 스케줄되면, kubelet은 시크릿 값 가져오기를 시도한다. 시크릿이 존재하지 않거나 API 서버에 대한 일시적인 연결 부족으로 인해 시크릿을 가져올 수 없는 경우, kubelet은 주기적으로 재시도한다. kubelet은 아직 시작되지 않은 이유를 설명하는 파드에 대한 이벤트를 보고한다. 시크릿을 가져오면, kubelet은 이를 포함하는 볼륨을 생성하고 마운트한다. 모든 파드의 볼륨이 마운트될 때까지 파드의 컨테이너는 시작되지 않는다.

사용 사레

사용 사례: 컨테이너 환경 변수로 사용하기

시크릿 정의를 작성한다.

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: MWYyZDFlMmU2N2Rm

시크릿을 생성한다.

kubectl apply -f mysecret.yaml

모든 시크릿 데이터를 컨테이너 환경 변수로 정의하는 데 envFrom 을 사용한다. 시크릿의 키는 파드의 환경 변수 이름이 된다.

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - secretRef:
          name: mysecret
  restartPolicy: Never

사용 사례: ssh 키가 있는 파드

몇 가지 ssh 키를 포함하는 시크릿을 생성한다.

kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

출력 결과는 다음과 비슷하다.

secret "ssh-key-secret" created

ssh 키를 포함하는 secretGenerator 필드가 있는 kustomization.yaml 를 만들 수도 있다.

이제 ssh 키를 가진 시크릿을 참조하고 볼륨에서 시크릿을 사용하는 파드를 만들 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

컨테이너의 명령이 실행될 때, 다음 위치에서 키 부분을 사용할 수 있다.

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

그러면 컨테이너는 ssh 연결을 맺기 위해 시크릿 데이터를 자유롭게 사용할 수 있다.

사용 사례: 운영 / 테스트 자격 증명이 있는 파드

이 예제에서는 운영 환경의 자격 증명이 포함된 시크릿을 사용하는 파드와 테스트 환경의 자격 증명이 있는 시크릿을 사용하는 다른 파드를 보여준다.

사용자는 secretGenerator 필드가 있는 kustomization.yaml 을 만들거나 kubectl create secret 을 실행할 수 있다.

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11

출력 결과는 다음과 비슷하다.

secret "prod-db-secret" created

테스트 환경의 자격 증명에 대한 시크릿을 만들 수도 있다.

kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests

출력 결과는 다음과 비슷하다.

secret "test-db-secret" created

이제 파드를 생성한다.

cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
EOF

동일한 kustomization.yaml에 파드를 추가한다.

cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF

다음을 실행하여 API 서버에 이러한 모든 오브젝트를 적용한다.

kubectl apply -k .

두 컨테이너 모두 각 컨테이너의 환경에 대한 값을 가진 파일시스템에 다음의 파일이 존재한다.

/etc/secret-volume/username
/etc/secret-volume/password

두 파드의 사양이 한 필드에서만 어떻게 다른지 확인한다. 이를 통해 공통 파드 템플릿에서 다양한 기능을 가진 파드를 생성할 수 있다.

두 개의 서비스 어카운트를 사용하여 기본 파드 명세를 더욱 단순화할 수 있다.

  1. prod-db-secret 을 가진 prod-user
  2. test-db-secret 을 가진 test-user

파드 명세는 다음과 같이 단축된다.

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

사용 사례: 시크릿 볼륨의 도트 파일(dotfile)

점으로 시작하는 키를 정의하여 데이터를 "숨김"으로 만들 수 있다. 이 키는 도트 파일 또는 "숨겨진" 파일을 나타낸다. 예를 들어, 다음 시크릿이 secret-volume 볼륨에 마운트되면 아래와 같다.

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: k8s.gcr.io/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

볼륨은 .secret-file 이라는 하나의 파일을 포함하고, dotfile-test-container/etc/secret-volume/.secret-file 경로에 이 파일을 가지게 된다.

사용 사례: 파드의 한 컨테이너에 표시되는 시크릿

HTTP 요청을 처리하고, 복잡한 비즈니스 로직을 수행한 다음, HMAC이 있는 일부 메시지에 서명해야 하는 프로그램을 고려한다. 애플리케이션 로직이 복잡하기 때문에, 서버에서 눈에 띄지 않는 원격 파일 읽기 공격이 있을 수 있으며, 이로 인해 개인 키가 공격자에게 노출될 수 있다.

이는 두 개의 컨테이너의 두 개 프로세스로 나눌 수 있다. 사용자 상호 작용과 비즈니스 로직을 처리하지만, 개인 키를 볼 수 없는 프론트엔드 컨테이너와 개인 키를 볼 수 있고, 프론트엔드의 간단한 서명 요청(예를 들어, localhost 네트워킹을 통해)에 응답하는 서명자 컨테이너로 나눌 수 있다.

이 분할된 접근 방식을 사용하면, 공격자는 이제 애플리케이션 서버를 속여서 파일을 읽는 것보다 다소 어려운 임의적인 어떤 작업을 수행해야 한다.

모범 사례

시크릿 API를 사용하는 클라이언트

시크릿 API와 상호 작용하는 애플리케이션을 배포할 때, RBAC과 같은 인가 정책을 사용하여 접근을 제한해야 한다.

시크릿은 종종 다양한 중요도에 걸친 값을 보유하며, 이 중 많은 부분이 쿠버네티스(예: 서비스 어카운트 토큰)와 외부 시스템으로 단계적으로 확대될 수 있다. 개별 앱이 상호 작용할 것으로 예상되는 시크릿의 힘에 대해 추론할 수 있더라도 동일한 네임스페이스 내의 다른 앱이 이러한 가정을 무효화할 수 있다.

이러한 이유로 네임스페이스 내 시크릿에 대한 watchlist 요청은 매우 강력한 기능이며, 시크릿을 나열하면 클라이언트가 해당 네임스페이스에 있는 모든 시크릿의 값을 검사할 수 있기 때문에 피해야 한다. 클러스터의 모든 시크릿을 감시(watch)하고 나열(list)하는 기능은 가장 특권이 있는 시스템 레벨의 컴포넌트에 대해서만 예약되어야 한다.

시크릿 API에 접근해야 하는 애플리케이션은 필요한 시크릿에 대한 get 요청을 수행해야 한다. 이를 통해 관리자는 앱에 필요한 개별 인스턴스에 대한 접근을 허용 목록에 추가하면서 모든 시크릿에 대한 접근을 제한할 수 있다.

get 반복을 통한 성능 향상을 위해, 클라이언트는 시크릿을 참조한 다음 리소스를 감시(watch)하고, 참조가 변경되면 시크릿을 다시 요청하는 리소스를 설계할 수 있다. 덧붙여, 클라이언트에게 개별 리소스를 감시(watch)하도록 하는 "대량 감시" API도 제안되었으며, 쿠버네티스의 후속 릴리스에서 사용할 수 있을 것이다.

보안 속성

보호

시크릿은 시크릿을 사용하는 파드와 독립적으로 생성될 수 있으므로, 파드 생성, 보기, 편집 워크플로 중에 시크릿이 노출될 위험이 적다. 또한 시스템은 가능한 경우 디스크에 기록하지 않는 등 시크릿에 대한 추가 예방 조치를 취할 수 있다.

해당 노드의 파드에 필요한 경우에만 시크릿이 노드로 전송된다. kubelet은 시크릿이 디스크 저장소에 기록되지 않도록 시크릿을 tmpfs 에 저장한다. 일단 시크릿에 의존하는 파드가 삭제되면, kubelet은 시크릿 데이터의 로컬 복제본도 삭제한다.

동일한 노드의 여러 파드에 대한 시크릿이 있을 수 있다. 그러나 파드가 요청하는 시크릿만 해당 컨테이너 내에서 잠재적으로 볼 수 있다. 따라서, 하나의 파드는 다른 파드의 시크릿에 접근할 수 없다.

파드에는 여러 개의 컨테이너가 있을 수 있다. 그러나, 파드의 각 컨테이너는 컨테이너 내에서 볼 수 있도록 파드의 volumeMounts 에 있는 시크릿 볼륨을 요청해야 한다. 이것은 유용한 파드 레벨에서의 보안 파티션을 구성하는 데 사용할 수 있다.

대부분의 쿠버네티스 배포판에서, 사용자와 API 서버 간, API 서버에서 kubelet으로의 통신은 SSL/TLS로 보호된다. 이러한 채널을 통해 전송될 때 시크릿이 보호된다.

FEATURE STATE: Kubernetes v1.13 [beta]

시크릿 데이터에 대해 저장 시 암호화(encryption at rest)를 활성화할 수 있으며, 이를 통해 보안성에 대한 보장 없이는 시크릿이 etcd에 저장되지 않도록 한다 .

위험

  • API 서버에서 시크릿 데이터는 etcd에 저장된다. 따라서,
    • 관리자는 클러스터 데이터에 대해 저장 시 암호화를 활성화해야 한다. (v1.13 이상 필요)
    • 관리자는 etcd에 대한 접근을 admin 사용자로 제한해야 한다.
    • 관리자는 더 이상 사용하지 않을 때 etcd에서 사용하는 디스크를 지우거나 폐기할 수 있다.
    • 클러스터에서 etcd를 실행하는 경우, 관리자는 etcd peer-to-peer 통신에 대해 SSL/TLS를 사용해야 한다.
  • base64로 인코딩된 시크릿 데이터가 있는 매니페스트(JSON 또는 YAML) 파일을 통해 시크릿을 구성하는 경우, 이 파일을 공유하거나 소스 리포지터리에 체크인하면 시크릿이 손상된다. Base64 인코딩은 암호화 방법이 아니며 일반 텍스트와 동일한 것으로 간주된다.
  • 실수로 기록하거나 신뢰할 수 없는 상대방에게 전송하지 않는 것과 같이, 애플리케이션은 볼륨에서 읽은 후에 시크릿 값을 보호해야 한다.
  • 시크릿을 사용하는 파드를 생성할 수 있는 사용자는 해당 시크릿의 값도 볼 수 있다. API 서버 정책이 해당 사용자가 시크릿을 읽을 수 있도록 허용하지 않더라도, 사용자는 시크릿을 노출하는 파드를 실행할 수 있다.

다음 내용

3.7.4 - 컨테이너 리소스 관리

파드를 지정할 때, 컨테이너에 필요한 각 리소스의 양을 선택적으로 지정할 수 있다. 지정할 가장 일반적인 리소스는 CPU와 메모리(RAM) 그리고 다른 것들이 있다.

파드에서 컨테이너에 대한 리소스 요청(request) 을 지정하면, 스케줄러는 이 정보를 사용하여 파드가 배치될 노드를 결정한다. 컨테이너에 대한 리소스 제한(limit) 을 지정하면, kubelet은 실행 중인 컨테이너가 설정한 제한보다 많은 리소스를 사용할 수 없도록 해당 제한을 적용한다. 또한 kubelet은 컨테이너가 사용할 수 있도록 해당 시스템 리소스의 최소 요청 량을 예약한다.

요청 및 제한

파드가 실행 중인 노드에 사용 가능한 리소스가 충분하면, 컨테이너가 해당 리소스에 지정한 request 보다 더 많은 리소스를 사용할 수 있도록 허용된다. 그러나, 컨테이너는 리소스 limit 보다 더 많은 리소스를 사용할 수는 없다.

예를 들어, 컨테이너에 대해 256MiB의 memory 요청을 설정하고, 해당 컨테이너가 8GiB의 메모리를 가진 노드로 스케줄된 파드에 있고 다른 파드는 없는 경우, 컨테이너는 더 많은 RAM을 사용할 수 있다.

해당 컨테이너에 대해 4GiB의 memory 제한을 설정하면, kubelet(그리고 컨테이너 런타임)이 제한을 적용한다. 런타임은 컨테이너가 구성된 리소스 제한을 초과하여 사용하지 못하게 한다. 예를 들어, 컨테이너의 프로세스가 허용된 양보다 많은 메모리를 사용하려고 하면, 시스템 커널은 메모리 부족(out of memory, OOM) 오류와 함께 할당을 시도한 프로세스를 종료한다.

제한은 반응적(시스템이 위반을 감지한 후에 개입)으로 또는 강제적(시스템이 컨테이너가 제한을 초과하지 않도록 방지)으로 구현할 수 있다. 런타임마다 다른 방식으로 동일한 제약을 구현할 수 있다.

리소스 타입

CPU메모리 는 각각 리소스 타입 이다. 리소스 타입에는 기본 단위가 있다. CPU는 컴퓨팅 처리를 나타내며 쿠버네티스 CPU 단위로 지정된다. 메모리는 바이트 단위로 지정된다. 쿠버네티스 v1.14 이상을 사용하는 경우, huge page 리소스를 지정할 수 있다. Huge page는 노드 커널이 기본 페이지 크기보다 훨씬 큰 메모리 블록을 할당하는 리눅스 관련 기능이다.

예를 들어, 기본 페이지 크기가 4KiB인 시스템에서, hugepages-2Mi: 80Mi 제한을 지정할 수 있다. 컨테이너가 40개 이상의 2MiB huge page(총 80MiB)를 할당하려고 하면 해당 할당이 실패한다.

CPU와 메모리를 통칭하여 컴퓨트 리소스 또는 리소스 라고 한다. 컴퓨트 리소스는 요청, 할당 및 소비될 수 있는 측정 가능한 수량이다. 이것은 API 리소스와는 다르다. 파드 및 서비스와 같은 API 리소스는 쿠버네티스 API 서버를 통해 읽고 수정할 수 있는 오브젝트이다.

파드와 컨테이너의 리소스 요청 및 제한

파드의 각 컨테이너는 다음 중 하나 이상을 지정할 수 있다.

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.limits.hugepages-<size>
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory
  • spec.containers[].resources.requests.hugepages-<size>

요청과 제한은 개별 컨테이너에서만 지정할 수 있지만, 파드 리소스 요청 및 제한에 대해 이야기하는 것이 편리하다. 특정 리소스 타입에 대한 파드 리소스 요청/제한 은 파드의 각 컨테이너에 대한 해당 타입의 리소스 요청/제한의 합이다.

쿠버네티스의 리소스 단위

CPU의 의미

CPU 리소스에 대한 제한 및 요청은 cpu 단위로 측정된다. 쿠버네티스의 CPU 1개는 클라우드 공급자용 vCPU/Core 1개 와 베어메탈 인텔 프로세서에서의 1개 하이퍼스레드 에 해당한다.

분수의 요청이 허용된다. 0.5spec.containers[].resources.requests.cpu 요청을 가진 컨테이너는 CPU 1개를 요구하는 컨테이너의 절반만큼 CPU를 보장한다. 0.1 이라는 표현은 "백 밀리cpu"로 읽을 수 있는 100m 표현과 동일하다. 어떤 사람들은 "백 밀리코어"라고 말하는데, 같은 것을 의미하는 것으로 이해된다. 0.1 과 같이 소수점이 있는 요청은 API에 의해 100m 으로 변환되며, 1m 보다 더 정밀한 단위는 허용되지 않는다. 이러한 이유로, 100m 과 같은 형식이 선호될 수 있다.

CPU는 항상 절대 수량으로 요청되며, 상대적 수량은 아니다. 0.1은 단일 코어, 이중 코어 또는 48코어 시스템에서 동일한 양의 CPU이다.

메모리의 의미

memory 에 대한 제한 및 요청은 바이트 단위로 측정된다. E, P, T, G, M, K와 같은 접미사 중 하나를 사용하여 메모리를 일반 정수 또는 고정 소수점 숫자로 표현할 수 있다. Ei, Pi, Ti, Gi, Mi, Ki와 같은 2의 거듭제곱을 사용할 수도 있다. 예를 들어, 다음은 대략 동일한 값을 나타낸다.

128974848, 129e6, 129M, 123Mi

다음은 예제이다. 다음 파드에는 두 개의 컨테이너가 있다. 각 컨테이너에는 0.25 cpu와 64MiB(226 바이트)의 메모리 요청이 있다. 각 컨테이너는 0.5 cpu와 128MiB 메모리로 제한된다. 파드에 0.5 cpu와 128 MiB 메모리, 1 cpu와 256MiB 메모리 제한이 있다고 말할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

리소스 요청이 포함된 파드를 스케줄링하는 방법

파드를 생성할 때, 쿠버네티스 스케줄러는 파드를 실행할 노드를 선택한다. 각 노드는 파드에 제공할 수 있는 CPU와 메모리 양과 같은 각 리소스 타입에 대해 최대 용량을 갖는다. 스케줄러는 각 리소스 타입마다 스케줄된 컨테이너의 리소스 요청 합계가 노드 용량보다 작도록 한다. 참고로 노드의 실제 메모리나 CPU 리소스 사용량은 매우 적지만, 용량 확인에 실패한 경우 스케줄러는 여전히 노드에 파드를 배치하지 않는다. 이는 리소스 사용량이 나중에 증가할 때, 예를 들어, 일일 요청 비율이 최대일 때 노드의 리소스 부족을 방지한다.

리소스 제한이 있는 파드가 실행되는 방법

kubelet은 파드의 컨테이너를 시작할 때, CPU와 메모리 제한을 컨테이너 런타임으로 전달한다.

도커를 사용하는 경우에는 다음과 같다.

  • spec.containers[].resources.requests.cpu 는 잠재적인 분수이며, 1024를 곱한 값인 코어 값으로 변환된다. 이 숫자 또는 2보다 큰 값은 docker run 명령에서 --cpu-shares 플래그의 값으로 사용된다.

  • spec.containers[].resources.limits.cpu 값은 밀리코어 값으로 변환되고 100을 곱한 값이다. 그 결과 값은 컨테이너가 100ms마다 사용할 수 있는 마이크로초 단위의 총 CPU 시간이다. 이 간격 동안 컨테이너는 CPU 시간을 초과하여 사용할 수 없다.

  • spec.containers[].resources.limits.memory 는 정수로 변환되어, docker run 명령에서 --memory 플래그의 값으로 사용된다.

컨테이너가 메모리 제한을 초과하면, 컨테이너는 종료될 수 있다. 다시 시작할 수 있으면, 다른 타입의 런타임 오류와 마찬가지로, kubelet이 다시 시작한다.

컨테이너가 메모리 요청을 초과하면, 노드에 메모리가 부족할 때마다 파드가 축출될 수 있다.

컨테이너가 오랫동안 CPU 제한을 초과하는 것은 허용되거나 허용되지 않을 수 있다. 그러나, 과도한 CPU 사용으로 인해 종료되지는 않는다.

리소스 제한으로 인해 컨테이너를 스케줄할 수 없는지 또는 종료 중인지 확인하려면, 문제 해결 섹션을 참조한다.

컴퓨트 및 메모리 리소스 사용량 모니터링

파드의 리소스 사용량은 파드 상태의 일부로 보고된다.

클러스터에서 선택적인 모니터링 도구를 사용할 수 있다면, 메트릭 API에서 직접 또는 모니터링 도구에서 파드 리소스 사용량을 검색할 수 있다.

로컬 임시(ephemeral) 스토리지

FEATURE STATE: Kubernetes v1.10 [beta]

노드에는 로컬에 연결된 쓰기 가능 장치 또는, 때로는 RAM에 의해 지원되는 로컬 임시 스토리지가 있다. "임시"는 내구성에 대한 장기간의 보증이 없음을 의미한다.

파드는 스크래치 공간, 캐싱 및 로그에 대해 임시 로컬 스토리지를 사용한다. kubelet은 로컬 임시 스토리지를 사용하여 컨테이너에 emptyDir 볼륨을 마운트하기 위해 파드에 스크래치 공간을 제공할 수 있다.

kubelet은 이러한 종류의 스토리지를 사용하여 노드-레벨 컨테이너 로그, 컨테이너 이미지 및 실행 중인 컨테이너의 쓰기 가능 계층을 보유한다.

베타 기능에서, 쿠버네티스는 파드가 사용할 수 있는 임시 로컬 스토리지의 양을 추적, 예약 및 제한할 수 있도록 해준다.

로컬 임시 스토리지 구성

쿠버네티스는 노드에서 로컬 임시 스토리지를 구성하는 두 가지 방법을 지원한다.

이 구성에서, 모든 종류의 임시 로컬 데이터(emptyDir 볼륨, 쓰기 가능 계층, 컨테이너 이미지, 로그)를 하나의 파일시스템에 배치한다. kubelet을 구성하는 가장 효과적인 방법은 이 파일시스템을 쿠버네티스(kubelet) 데이터 전용으로 하는 것이다.

kubelet은 또한 노드-레벨 컨테이너 로그를 작성하고 임시 로컬 스토리지와 유사하게 처리한다.

kubelet은 구성된 로그 디렉터리 내의 파일에 로그를 기록한다(기본적으로 /var/log). 그리고 로컬에 저장된 다른 데이터에 대한 기본 디렉터리가 있다(기본적으로 /var/lib/kubelet).

일반적으로, /var/lib/kubelet/var/log 모두 시스템 루트 파일시스템에 위치하고, 그리고 kubelet은 이런 레이아웃을 염두에 두고 설계되었다.

노드는 쿠버네티스에서 사용하지 않는 다른 많은 파일시스템을 가질 수 있다.

사용하고 있는 노드에 실행 중인 파드에서 발생하는 임시 데이터를 위한 파일시스템을 가진다(로그와 emptyDir 볼륨). 이 파일시스템을 다른 데이터(예를 들어, 쿠버네티스와 관련없는 시스템 로그)를 위해 사용할 수 있다. 이 파일시스템은 루트 파일시스템일 수도 있다.

kubelet은 또한 노드-레벨 컨테이너 로그를 첫 번째 파일시스템에 기록하고, 임시 로컬 스토리지와 유사하게 처리한다.

또한 다른 논리 스토리지 장치가 지원하는 별도의 파일시스템을 사용한다. 이 구성에서, 컨테이너 이미지 계층과 쓰기 가능한 계층을 배치하도록 kubelet에 지시하는 디렉터리는 이 두 번째 파일시스템에 있다.

첫 번째 파일시스템에는 이미지 계층이나 쓰기 가능한 계층이 없다.

노드는 쿠버네티스에서 사용하지 않는 다른 많은 파일시스템을 가질 수 있다.

kubelet은 사용 중인 로컬 스토리지 양을 측정할 수 있다. 이것은 다음을 제공한다.

  • LocalStorageCapacityIsolation 기능 게이트(이 기능이 기본적으로 설정되어 있음)를 활성화하고,
  • 로컬 임시 스토리지에 대한 지원되는 구성 중 하나를 사용하여 노드를 설정한다.

다른 구성을 사용하는 경우, kubelet은 임시 로컬 스토리지에 대한 리소스 제한을 적용하지 않는다.

로컬 임시 스토리지에 대한 요청 및 제한 설정

임시-스토리지 를 사용하여 로컬 임시 저장소를 관리할 수 있다. 파드의 각 컨테이너는 다음 중 하나 이상을 지정할 수 있다.

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storage 에 대한 제한 및 요청은 바이트 단위로 측정된다. E, P, T, G, M, K와 같은 접미사 중 하나를 사용하여 스토리지를 일반 정수 또는 고정 소수점 숫자로 표현할 수 있다. Ei, Pi, Ti, Gi, Mi, Ki와 같은 2의 거듭제곱을 사용할 수도 있다. 예를 들어, 다음은 대략 동일한 값을 나타낸다.

128974848, 129e6, 129M, 123Mi

다음 예에서, 파드에 두 개의 컨테이너가 있다. 각 컨테이너에는 2GiB의 로컬 임시 스토리지 요청이 있다. 각 컨테이너에는 4GiB의 로컬 임시 스토리지 제한이 있다. 따라서, 파드는 4GiB의 로컬 임시 스토리지 요청과 8GiB 로컬 임시 스토리지 제한을 가진다.

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  volumes:
    - name: ephemeral
      emptyDir: {}

임시-스토리지 요청이 있는 파드의 스케줄링 방법

파드를 생성할 때, 쿠버네티스 스케줄러는 파드를 실행할 노드를 선택한다. 각 노드에는 파드에 제공할 수 있는 최대 임시 스토리지 공간이 있다. 자세한 정보는, 노드 할당 가능을 참조한다.

스케줄러는 스케줄된 컨테이너의 리소스 요청 합계가 노드 용량보다 작도록 한다.

임시 스토리지 소비 관리

kubelet이 로컬 임시 스토리지를 리소스로 관리하는 경우, kubelet은 다음에서 스토리지 사용을 측정한다.

  • tmpfs emptyDir 볼륨을 제외한 emptyDir 볼륨
  • 노드-레벨 로그가 있는 디렉터리
  • 쓰기 가능한 컨테이너 계층

허용하는 것보다 더 많은 임시 스토리지를 파드가 사용하는 경우, kubelet은 파드 축출을 트리거하는 축출 신호를 설정한다.

컨테이너-레벨 격리의 경우, 컨테이너의 쓰기 가능한 계층과 로그 사용량이 스토리지 제한을 초과하면, kubelet은 파드를 축출하도록 표시한다.

파드-레벨 격리에 대해 kubelet은 해당 파드의 컨테이너에 대한 제한을 합하여 전체 파드 스토리지 제한을 해결한다. 이 경우, 모든 컨테이너와 파드의 emptyDir 볼륨의 로컬 임시 스토리지 사용량 합계가 전체 파드 스토리지 제한을 초과하면, kubelet은 파드를 축출 대상으로 표시한다.

kubelet은 파드 스토리지 사용을 측정하는 다양한 방법을 지원한다.

kubelet은 각 emptyDir 볼륨, 컨테이너 로그 디렉터리 및 쓰기 가능한 컨테이너 계층을 스캔하는 정기적인 스케줄 검사를 수행한다.

스캔은 사용된 공간의 양을 측정한다.

FEATURE STATE: Kubernetes v1.15 [alpha]

프로젝트 쿼터는 파일시스템에서 스토리지 사용을 관리하기 위한 운영체제 레벨의 기능이다. 쿠버네티스를 사용하면, 스토리지 사용을 모니터링하기 위해 프로젝트 쿼터를 사용할 수 있다. 노드에서 'emptyDir' 볼륨을 지원하는 파일시스템이 프로젝트 쿼터 지원을 제공하는지 확인한다. 예를 들어, XFS와 ext4fs는 프로젝트 쿼터를 지원한다.

쿠버네티스는 1048576 부터 프로젝트 ID를 사용한다. 사용 중인 ID는 /etc/projects/etc/projid 에 등록되어 있다. 이 범위의 프로젝트 ID가 시스템에서 다른 목적으로 사용되는 경우, 쿠버네티스가 이를 사용하지 않도록 해당 프로젝트 ID를 /etc/projects/etc/projid 에 등록해야 한다.

쿼터는 디렉터리 검색보다 빠르고 정확하다. 디렉터리가 프로젝트에 할당되면, 디렉터리 아래에 생성된 모든 파일이 해당 프로젝트에 생성되며, 커널은 해당 프로젝트의 파일에서 사용 중인 블록 수를 추적하기만 하면 된다. 파일이 생성되고 삭제되었지만, 열린 파일 디스크립터가 있으면, 계속 공간을 소비한다. 쿼터 추적은 공간을 정확하게 기록하는 반면 디렉터리 스캔은 삭제된 파일이 사용한 스토리지를 간과한다.

프로젝트 쿼터를 사용하려면, 다음을 수행해야 한다.

  • kubelet 구성featureGates 필드 또는 --feature-gates 커맨드 라인 플래그를 사용하여 LocalStorageCapacityIsolationFSQuotaMonitoring=true 기능 게이트를 활성화한다.

  • 루트 파일시스템(또는 선택적인 런타임 파일시스템)에 프로젝트 쿼터가 활성화되어 있는지 확인한다. 모든 XFS 파일시스템은 프로젝트 쿼터를 지원한다. ext4 파일시스템의 경우, 파일시스템이 마운트되지 않은 상태에서 프로젝트 쿼터 추적 기능을 활성화해야 한다.

    # ext4인 /dev/block-device가 마운트되지 않은 경우
    sudo tune2fs -O project -Q prjquota /dev/block-device
    
  • 루트 파일시스템(또는 선택적인 런타임 파일시스템)은 프로젝트 쿼터를 활성화한 상태에서 마운트해야 힌다. XFS와 ext4fs 모두에서, 마운트 옵션의 이름은 prjquota 이다.

확장된 리소스

확장된 리소스는 kubernetes.io 도메인 외부의 전체 주소(fully-qualified) 리소스 이름이다. 쿠버네티스에 내장되지 않은 리소스를 클러스터 운영자가 알리고 사용자는 사용할 수 있다.

확장된 리소스를 사용하려면 두 단계가 필요한다. 먼저, 클러스터 운영자는 확장된 리소스를 알려야 한다. 둘째, 사용자는 파드의 확장된 리소스를 요청해야 한다.

확장된 리소스 관리

노드-레벨의 확장된 리소스

노드-레벨의 확장된 리소스는 노드에 연결된다.

장치 플러그인 관리 리소스

각 노드에서 장치 플러그인 관리 리소스를 알리는 방법은 장치 플러그인을 참조한다.

기타 리소스

새로운 노드-레벨의 확장된 리소스를 알리기 위해, 클러스터 운영자는 API 서버에 PATCH HTTP 요청을 제출하여 클러스터의 노드에 대해 status.capacity 에서 사용할 수 있는 수량을 지정할 수 있다. 이 작업 후에는, 노드의 status.capacity 에 새로운 리소스가 포함된다. 이 status.allocatable 필드는 kubelet에 의해 비동기적으로 새로운 리소스로 자동 업데이트된다. 참고로 스케줄러가 파드 적합성을 평가할 때 노드 status.allocatable 값을 사용하므로, 노드 용량을 새 리소스로 패치하는 것과 해당 노드에서 리소스를 스케줄하도록 요청하는 첫 번째 파드 사이에 약간의 지연이 있을 수 있다.

예제:

다음은 curl 을 사용하여 마스터가 k8s-master 인 노드 k8s-node-1 에 5개의 "example.com/foo" 리소스를 알리는 HTTP 요청을 구성하는 방법을 보여주는 예이다.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status

클러스터-레벨의 확장된 리소스

클러스터-레벨의 확장된 리소스는 노드에 연결되지 않는다. 이들은 일반적으로 리소스 소비와 리소스 쿼터를 처리하는 스케줄러 익스텐더(extender)에 의해 관리된다.

스케줄러 정책 구성에서 스케줄러 익스텐더가 처리하는 확장된 리소스를 지정할 수 있다.

예제:

스케줄러 정책에 대한 다음의 구성은 클러스터-레벨의 확장된 리소스 "example.com/foo"가 스케줄러 익스텐더에 의해 처리됨을 나타낸다.

  • 파드가 "example.com/foo"를 요청하는 경우에만 스케줄러가 파드를 스케줄러 익스텐더로 보낸다.
  • ignoredByScheduler 필드는 스케줄러가 PodFitsResources 속성에서 "example.com/foo" 리소스를 확인하지 않도록 지정한다.
{
  "kind": "Policy",
  "apiVersion": "v1",
  "extenders": [
    {
      "urlPrefix":"<extender-endpoint>",
      "bindVerb": "bind",
      "managedResources": [
        {
          "name": "example.com/foo",
          "ignoredByScheduler": true
        }
      ]
    }
  ]
}

확장된 리소스 소비

사용자는 CPU와 메모리 같은 파드 스펙의 확장된 리소스를 사용할 수 있다. 스케줄러는 리소스 어카운팅(resource accounting)을 관리하여 사용 가능한 양보다 많은 양의 리소스가 여러 파드에 동시에 할당되지 않도록 한다.

API 서버는 확장된 리소스의 수량을 정수로 제한한다. 유효한 수량의 예로는 3, 3000m 그리고 3Ki 를 들 수 있다. 유효하지 않은 수량의 예는 0.51500m 이다.

파드에서 확장된 리소스를 사용하려면, 컨테이너 사양에서 spec.containers[].resources.limits 맵에 리소스 이름을 키로 포함한다.

파드는 CPU, 메모리 및 확장된 리소스를 포함하여 모든 리소스 요청이 충족되는 경우에만 예약된다. 리소스 요청을 충족할 수 없다면 파드는 PENDING 상태를 유지한다.

예제:

아래의 파드는 2개의 CPU와 1개의 "example.com/foo"(확장된 리소스)를 요청한다.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: myimage
    resources:
      requests:
        cpu: 2
        example.com/foo: 1
      limits:
        example.com/foo: 1

PID 제한

프로세스 ID(PID) 제한은 kubelet의 구성에 대해 주어진 파드가 사용할 수 있는 PID 수를 제한할 수 있도록 허용한다. 자세한 내용은 Pid 제한을 참고한다.

문제 해결

내 파드가 failedScheduling 이벤트 메시지로 보류 중이다

파드가 배치될 수 있는 노드를 스케줄러가 찾을 수 없으면, 노드를 찾을 수 있을 때까지 파드는 스케줄되지 않은 상태로 유지한다. 스케줄러가 다음과 같이 파드의 위치를 ​​찾지 못하면 이벤트가 생성된다.

kubectl describe pod frontend | grep -A 3 Events
Events:
  FirstSeen LastSeen   Count  From          Subobject   PathReason      Message
  36s   5s     6      {scheduler }              FailedScheduling  Failed for reason PodExceedsFreeCPU and possibly others

위의 예에서, 노드의 CPU 리소스가 충분하지 않아 이름이 "frontend"인 파드를 스케줄하지 못했다. 비슷한 메시지로 메모리 부족(PodExceedsFreeMemory)으로 인한 장애도 알릴 수 있다. 일반적으로, 파드가 이 타입의 메시지로 보류 중인 경우, 몇 가지 시도해 볼 것들이 있다.

  • 클러스터에 더 많은 노드를 추가한다.
  • 불필요한 파드를 종료하여 보류 중인 파드를 위한 공간을 확보한다.
  • 파드가 모든 노드보다 크지 않은지 확인한다. 예를 들어, 모든 노드의 용량이 cpu: 1 인 경우, cpu: 1.1 요청이 있는 파드는 절대 스케줄되지 않는다.

kubectl describe nodes 명령으로 노드 용량과 할당된 양을 확인할 수 있다. 예를 들면, 다음과 같다.

kubectl describe nodes e2e-test-node-pool-4lw4
Name:            e2e-test-node-pool-4lw4
[ ... 명확하게 하기 위해 라인들을 제거함 ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... 명확하게 하기 위해 라인들을 제거함 ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (11%)        1070Mi (13%)

위의 출력에서, ​파드가 1120m 이상의 CPU 또는 6.23Gi의 메모리를 요청하는 것은 노드에 맞지 않음을 알 수 있다.

Pods 섹션을 살펴보면, 파드가 노드에서 공간을 차지하는 것을 볼 수 있다.

시스템 데몬이 사용 가능한 리소스의 일부를 사용하기 때문에, 파드에 사용 가능한 리소스의 양이 노드 용량보다 적다. allocatable 필드 NodeStatus는 파드가 사용할 수 있는 리소스의 양을 제공한다. 자세한 정보는 노드 할당 가능 리소스를 참조한다.

리소스 쿼터 기능은 소비될 수 있는 리소스의 총량을 제한하도록 구성할 수 있다. 네임스페이스와 함께 사용하면, 한 팀이 모든 리소스를 사용하는 경우를 방지할 수 있다.

내 컨테이너가 종료되었다

리소스가 부족하여 컨테이너가 종료될 수 있다. 리소스 제한에 도달하여 컨테이너가 종료되고 있는지 확인하려면, 관심있는 파드에 대해 kubectl describe pod 를 호출한다.

kubectl describe pod simmemleak-hra99
Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Replication Controllers:        simmemleak (1/1 replicas created)
Containers:
  simmemleak:
    Image:  saadali/simmemleak
    Limits:
      cpu:                      100m
      memory:                   50Mi
    State:                      Running
      Started:                  Tue, 07 Jul 2015 12:54:41 -0700
    Last Termination State:     Terminated
      Exit Code:                1
      Started:                  Fri, 07 Jul 2015 12:54:30 -0700
      Finished:                 Fri, 07 Jul 2015 12:54:33 -0700
    Ready:                      False
    Restart Count:              5
Conditions:
  Type      Status
  Ready     False
Events:
  FirstSeen                         LastSeen                         Count  From                              SubobjectPath                       Reason      Message
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {scheduler }                                                          scheduled   Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   pulled      Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   created     Created with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   started     Started with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    spec.containers{simmemleak}         created     Created with docker id 87348f12526a

앞의 예제에서, Restart Count: 5 표시는 파드의 simmemleak 컨테이너가 종료되고 5번 다시 시작되었음을 나타낸다.

이전에 종료된 컨테이너의 상태를 가져오기 위해 -o go-template=... 옵션을 사용해서 kubectl get pod 를 호출할 수 있다.

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]

컨테이너가 reason:OOM Killed(OOM 은 메모리 부족(Out Of Memory)의 약자) 때문에 종료된 것을 알 수 있다.

다음 내용

3.7.5 - kubeconfig 파일을 사용하여 클러스터 접근 구성하기

kubeconfig 파일들을 사용하여 클러스터, 사용자, 네임스페이스 및 인증 메커니즘에 대한 정보를 관리하자. kubectl 커맨드라인 툴은 kubeconfig 파일을 사용하여 클러스터의 선택과 클러스터의 API 서버와의 통신에 필요한 정보를 찾는다.

기본적으로 kubectl$HOME/.kube 디렉터리에서 config라는 이름의 파일을 찾는다. KUBECONFIG 환경 변수를 설정하거나 --kubeconfig 플래그를 지정해서 다른 kubeconfig 파일을 사용할 수 있다.

kubeconfig 파일을 생성하고 지정하는 단계별 지시사항은 다중 클러스터로 접근 구성하기를 참조한다.

다중 클러스터, 사용자와 인증 메커니즘 지원

여러 클러스터가 있고, 사용자와 구성 요소가 다양한 방식으로 인증한다고 가정하자. 예를 들면 다음과 같다.

  • 실행 중인 kubelet은 인증서를 이용하여 인증할 수 있다.
  • 사용자는 토큰으로 인증할 수 있다.
  • 관리자는 개별 사용자에게 제공하는 인증서 집합을 가지고 있다.

kubeconfig 파일을 사용하면 클러스터와 사용자와 네임스페이스를 구성할 수 있다. 또한 컨텍스트를 정의하여 빠르고 쉽게 클러스터와 네임스페이스 간에 전환할 수 있다.

컨텍스트

kubeconfig에서 컨텍스트 요소는 편리한 이름으로 접속 매개 변수를 묶는데 사용한다. 각 컨텍스트는 클러스터, 네임스페이스와 사용자라는 세 가지 매개 변수를 가진다. 기본적으로 kubectl 커맨드라인 툴은 현재 컨텍스트 의 매개 변수를 사용하여 클러스터와 통신한다.

현재 컨택스트를 선택하려면 다음을 실행한다.

kubectl config use-context

KUBECONFIG 환경 변수

KUBECONFIG 환경 변수는 kubeconfig 파일 목록을 보유한다. 리눅스 및 Mac의 경우 이는 콜론(:)으로 구분된 목록이다. 윈도우는 세미콜론(;)으로 구분한다. KUBECONFIG 환경 변수가 필수는 아니다. KUBECONFIG 환경 변수가 없으면, kubectl은 기본 kubeconfig 파일인 $HOME/.kube/config를 사용한다.

KUBECONFIG 환경 변수가 존재하면, kubectlKUBECONFIG 환경 변수에 나열된 파일을 병합한 결과 형태의 효과적 구성을 이용한다.

kubeconfig 파일 병합

구성을 보려면, 다음 커맨드를 입력한다.

kubectl config view

앞서 설명한 것처럼, 이 출력 내용은 단일 kubeconfig 파일이나 여러 kubeconfig 파일을 병합한 결과 일 수 있다.

다음은 kubeconfig 파일을 병합할 때에 kubectl에서 사용하는 규칙이다.

  1. --kubeconfig 플래그를 설정했으면, 지정한 파일만 사용한다. 병합하지 않는다. 이 플래그는 오직 한 개 인스턴스만 허용한다.

    그렇지 않고, KUBECONFIG 환경 변수를 설정하였다면 병합해야 하는 파일의 목록으로 사용한다. KUBECONFIG 환경 변수의 나열된 파일은 다음 규칙에 따라 병합한다.

    • 빈 파일명은 무시한다.
    • 역 직렬화 불가한 파일 내용에 대해서 오류를 일으킨다.
    • 특정 값이나 맵 키를 설정한 첫 번째 파일을 우선한다.
    • 값이나 맵 키를 변경하지 않는다. 예: 현재 컨텍스트를 설정할 첫 번째 파일의 컨택스트를 유지한다. 예: 두 파일이 red-user를 지정했다면, 첫 번째 파일의 red-user 값만을 사용한다. 두 번째 파일의 red-user 하위에 충돌하지 않는 항목이 있어도 버린다.

    KUBECONFIG 환경 변수 설정의 예로, KUBECONFIG 환경 변수 설정를 참조한다.

    그렇지 않다면, 병합하지 않고 기본 kubeconfig 파일인 $HOME/.kube/config를 사용한다.

  2. 이 체인에서 첫 번째를 기반으로 사용할 컨텍스트를 결정한다.

    1. 커맨드라인 플래그의 --context를 사용한다.
    2. 병합된 kubeconfig 파일에서 current-context를 사용한다.

    이 시점에서는 빈 컨텍스트도 허용한다.

  3. 클러스터와 사용자를 결정한다. 이 시점에서는 컨텍스트가 있을 수도 있고 없을 수도 있다. 사용자에 대해 한 번, 클러스터에 대해 한 번 총 두 번에 걸친 이 체인에서 첫 번째 것을 기반으로 클러스터와 사용자를 결정한다.

    1. 커맨드라인 플래그가 존재하면, --user 또는 --cluster를 사용한다.
    2. 컨텍스트가 비어있지 않다면, 컨텍스트에서 사용자 또는 클러스터를 가져온다.

    이 시점에서는 사용자와 클러스터는 비워둘 수 있다.

  4. 사용할 실제 클러스터 정보를 결정한다. 이 시점에서 클러스터 정보가 있을 수 있고 없을 수도 있다. 이 체인을 기반으로 클러스터 정보를 구축한다. 첫 번째 것을 사용한다.

    1. 커맨드라인 플래그가 존재하면, --server, --certificate-authority, --insecure-skip-tls-verify를 사용한다.
    2. 병합된 kubeconfig 파일에서 클러스터 정보 속성이 있다면 사용한다.
    3. 서버 위치가 없다면 실패한다.
  5. 사용할 실제 사용자 정보를 결정한다. 사용자 당 하나의 인증 기법만 허용하는 것을 제외하고는 클러스터 정보와 동일한 규칙을 사용하여 사용자 정보를 작성한다.

    1. 커맨드라인 플래그가 존재하면, --client-certificate, --client-key, --username, --password, --token을 사용한다.
    2. 병합된 kubeconfig 파일에서 user 필드를 사용한다.
    3. 충돌하는 두 가지 기법이 있다면 실패한다.
  6. 여전히 누락된 정보는 기본 값을 사용하고 인증 정보를 묻는 메시지가 표시될 수 있다.

파일 참조

kubeconfig 파일에서 파일과 경로 참조는 kubeconfig 파일의 위치와 관련 있다. 커맨드라인 상에 파일 참조는 현재 디렉터리를 기준으로 한다. $HOME/.kube/config에서 상대 경로는 상대적으로, 절대 경로는 절대적으로 저장한다.

다음 내용

3.8 - 보안

클라우드 네이티브 워크로드를 안전하게 유지하기 위한 개념

3.8.1 - 클라우드 네이티브 보안 개요

클라우드 네이티브 보안 관점에서 쿠버네티스 보안을 생각해보기 위한 모델

이 개요는 클라우드 네이티브 보안의 맥락에서 쿠버네티스 보안에 대한 생각의 모델을 정의한다.

클라우드 네이티브 보안의 4C

보안은 계층으로 생각할 수 있다. 클라우드 네이티브 보안의 4C는 클라우드(Cloud), 클러스터(Cluster), 컨테이너(Container)와 코드(Code)이다.

클라우드 네이티브 보안의 4C

클라우드 네이티브 보안 모델의 각 계층은 다음의 가장 바깥쪽 계층을 기반으로 한다. 코드 계층은 강력한 기본(클라우드, 클러스터, 컨테이너) 보안 계층의 이점을 제공한다. 코드 수준에서 보안을 처리하여 기본 계층의 열악한 보안 표준을 보호할 수 없다.

클라우드

여러 면에서 클라우드(또는 공동 위치 서버, 또는 기업의 데이터 센터)는 쿠버네티스 클러스터 구성을 위한 신뢰 컴퓨팅 기반(trusted computing base) 이다. 클라우드 계층이 취약하거나 취약한 방식으로 구성된 경우 이 기반 위에서 구축된 구성 요소가 안전하다는 보장은 없다. 각 클라우드 공급자는 해당 환경에서 워크로드를 안전하게 실행하기 위한 보안 권장 사항을 제시한다.

클라우드 공급자 보안

자신의 하드웨어 또는 다른 클라우드 공급자에서 쿠버네티스 클러스터를 실행 중인 경우, 보안 모범 사례는 설명서를 참고한다. 다음은 인기있는 클라우드 공급자의 보안 문서 중 일부에 대한 링크이다.

클라우드 공급자 보안
IaaS 공급자 링크
Alibaba Cloud https://www.alibabacloud.com/trust-center
Amazon Web Services https://aws.amazon.com/security/
Google Cloud Platform https://cloud.google.com/security/
IBM Cloud https://www.ibm.com/cloud/security
Microsoft Azure https://docs.microsoft.com/en-us/azure/security/azure-security
Oracle Cloud Infrastructure https://www.oracle.com/security/
VMWare VSphere https://www.vmware.com/security/hardening-guides.html

인프라스트럭처 보안

쿠버네티스 클러스터에서 인프라 보안을 위한 제안은 다음과 같다.

인프라스트럭처 보안
쿠버네티스 인프라에서 고려할 영역 추천
API 서버에 대한 네트워크 접근(컨트롤 플레인) 쿠버네티스 컨트롤 플레인에 대한 모든 접근은 인터넷에서 공개적으로 허용되지 않으며 클러스터 관리에 필요한 IP 주소 집합으로 제한된 네트워크 접근 제어 목록에 의해 제어된다.
노드에 대한 네트워크 접근(노드) 지정된 포트의 컨트롤 플레인에서 (네트워크 접근 제어 목록을 통한) 연결을 허용하고 NodePort와 LoadBalancer 유형의 쿠버네티스 서비스에 대한 연결을 허용하도록 노드를 구성해야 한다. 가능하면 이러한 노드가 공용 인터넷에 완전히 노출되어서는 안된다.
클라우드 공급자 API에 대한 쿠버네티스 접근 각 클라우드 공급자는 쿠버네티스 컨트롤 플레인 및 노드에 서로 다른 권한 집합을 부여해야 한다. 관리해야하는 리소스에 대해 최소 권한의 원칙을 따르는 클라우드 공급자의 접근 권한을 클러스터에 구성하는 것이 가장 좋다. Kops 설명서는 IAM 정책 및 역할에 대한 정보를 제공한다.
etcd에 대한 접근 etcd(쿠버네티스의 데이터 저장소)에 대한 접근은 컨트롤 플레인으로만 제한되어야 한다. 구성에 따라 TLS를 통해 etcd를 사용해야 한다. 자세한 내용은 etcd 문서에서 확인할 수 있다.
etcd 암호화 가능한 한 모든 스토리지를 암호화하는 것이 좋은 방법이며, etcd는 전체 클러스터(시크릿 포함)의 상태를 유지하고 있기에 특히 디스크는 암호화되어 있어야 한다.

클러스터

쿠버네티스 보안에는 다음의 두 가지 영역이 있다.

  • 설정 가능한 클러스터 컴포넌트의 보안
  • 클러스터에서 실행되는 애플리케이션의 보안

클러스터의 컴포넌트

우발적이거나 악의적인 접근으로부터 클러스터를 보호하고, 모범 사례에 대한 정보를 채택하기 위해서는 클러스터 보안에 대한 조언을 읽고 따른다.

클러스터 내 컴포넌트(애플리케이션)

애플리케이션의 공격 영역에 따라, 보안의 특정 측면에 중점을 둘 수 있다. 예를 들어, 다른 리소스 체인에 중요한 서비스(서비스 A)와 리소스 소진 공격에 취약한 별도의 작업 부하(서비스 B)를 실행하는 경우, 서비스 B의 리소스를 제한하지 않으면 서비스 A가 손상될 위험이 높다. 다음은 쿠버네티스에서 실행되는 워크로드를 보호하기 위한 보안 문제 및 권장 사항이 나와 있는 표이다.

워크로드 보안에서 고려할 영역 추천
RBAC 인증(쿠버네티스 API에 대한 접근) https://kubernetes.io/docs/reference/access-authn-authz/rbac/
인증 https://kubernetes.io/ko/docs/concepts/security/controlling-access/
애플리케이션 시크릿 관리(및 유휴 상태에서의 etcd 암호화 등) https://kubernetes.io/ko/docs/concepts/configuration/secret/
https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
파드가 파드 시큐리티 폴리시를 만족하는지 확인하기 https://kubernetes.io/docs/concepts/security/pod-security-standards/#policy-instantiation
서비스 품질(및 클러스터 리소스 관리) https://kubernetes.io/ko/docs/tasks/configure-pod-container/quality-service-pod/
네트워크 정책 https://kubernetes.io/ko/docs/concepts/services-networking/network-policies/
쿠버네티스 인그레스를 위한 TLS https://kubernetes.io/ko/docs/concepts/services-networking/ingress/#tls

컨테이너

컨테이너 보안은 이 가이드의 범위를 벗어난다. 다음은 일반적인 권장사항과 이 주제에 대한 링크이다.

컨테이너에서 고려할 영역 추천
컨테이너 취약점 스캔 및 OS에 종속적인 보안 이미지 빌드 단계의 일부로 컨테이너에 알려진 취약점이 있는지 검사해야 한다.
이미지 서명 및 시행 컨테이너 이미지에 서명하여 컨테이너의 내용에 대한 신뢰 시스템을 유지한다.
권한있는 사용자의 비허용 컨테이너를 구성할 때 컨테이너의 목적을 수행하는데 필요한 최소 권한을 가진 사용자를 컨테이너 내에 만드는 방법에 대해서는 설명서를 참조한다.
더 강력한 격리로 컨테이너 런타임 사용 더 강력한 격리를 제공하는 컨테이너 런타임 클래스를 선택한다.

코드

애플리케이션 코드는 가장 많은 제어를 할 수 있는 주요 공격 영역 중 하나이다. 애플리케이션 코드 보안은 쿠버네티스 보안 주제를 벗어나지만, 애플리케이션 코드를 보호하기 위한 권장 사항은 다음과 같다.

코드 보안

코드 보안
코드에서 고려할 영역 추천
TLS를 통한 접근 코드가 TCP를 통해 통신해야 한다면, 미리 클라이언트와 TLS 핸드 셰이크를 수행한다. 몇 가지 경우를 제외하고, 전송 중인 모든 것을 암호화한다. 한 걸음 더 나아가, 서비스 간 네트워크 트래픽을 암호화하는 것이 좋다. 이것은 인증서를 가지고 있는 두 서비스의 양방향 검증을 실행하는 mTLS(상호 TLS 인증)를 통해 수행할 수 있다.
통신 포트 범위 제한 이 권장사항은 당연할 수도 있지만, 가능하면 통신이나 메트릭 수집에 꼭 필요한 서비스의 포트만 노출시켜야 한다.
타사 종속성 보안 애플리케이션의 타사 라이브러리를 정기적으로 스캔하여 현재 알려진 취약점이 없는지 확인하는 것이 좋다. 각 언어에는 이런 검사를 자동으로 수행하는 도구를 가지고 있다.
정적 코드 분석 대부분 언어에는 잠재적으로 안전하지 않은 코딩 방법에 대해 코드 스니펫을 분석할 수 있는 방법을 제공한다. 가능한 언제든지 일반적인 보안 오류에 대해 코드베이스를 스캔할 수 있는 자동화된 도구를 사용하여 검사를 한다. 도구는 다음에서 찾을 수 있다. https://owasp.org/www-community/Source_Code_Analysis_Tools
동적 탐지 공격 잘 알려진 공격 중 일부를 서비스에 테스트할 수 있는 자동화된 몇 가지 도구가 있다. 여기에는 SQL 인젝션, CSRF 및 XSS가 포함된다. 가장 널리 사용되는 동적 분석 도구는 OWASP Zed Attack 프록시이다.

다음 내용

쿠버네티스 보안 주제에 관련한 내용들을 배워보자.

3.8.2 - 쿠버네티스 API 접근 제어하기

이 페이지는 쿠버네티스 API에 대한 접근 제어의 개요를 제공한다.

사용자는 kubectl, 클라이언트 라이브러리 또는 REST 요청을 통해 API에 접근한다. 사용자와 쿠버네티스 서비스 어카운트 모두 API에 접근할 수 있다. 요청이 API에 도달하면, 다음 다이어그램에 설명된 몇 가지 단계를 거친다.

Diagram of request handling steps for Kubernetes API request

전송 보안

일반적인 쿠버네티스 클러스터에서 API는 443번 포트에서 서비스한다. API 서버는 인증서를 제시한다. 이 인증서는 종종 자체 서명되기 때문에 일반적으로 사용자 머신의 $USER/.kube/config는 API 서버의 인증서에 대한 루트 인증서를 포함하며, 시스템 기본 루트 인증서 대신 사용된다. kube-up.sh을 사용하여 클러스터를 직접 생성할 때 이 인증서는 일반적으로 $USER/.kube/config에 자동으로 기록된다. 클러스터에 여러 명의 사용자가 있는 경우, 작성자는 인증서를 다른 사용자와 공유해야 한다.

인증

TLS가 설정되면 HTTP 요청이 인증 단계로 넘어간다. 이는 다이어그램에 1단계로 표시되어 있다. 클러스터 생성 스크립트 또는 클러스터 관리자는 API 서버가 하나 이상의 인증기 모듈을 실행하도록 구성한다. 인증기는 여기에서 더 자세히 서술한다.

인증 단계로 들어가는 것은 온전한 HTTP 요청이지만 일반적으로 헤더 그리고/또는 클라이언트 인증서를 검사한다.

인증 모듈은 클라이언트 인증서, 암호 및 일반 토큰, 부트스트랩 토큰, JWT 토큰(서비스 어카운트에 사용됨)을 포함한다.

여러 개의 인증 모듈을 지정할 수 있으며, 이 경우 하나의 인증 모듈이 성공할 때까지 각 모듈을 순차적으로 시도한다.

GCE에서는 클라이언트 인증서, 암호, 일반 토큰 및 JWT 토큰이 모두 사용 가능하다.

요청을 인증할 수 없는 경우 HTTP 상태 코드 401과 함께 거부된다. 이 외에는 사용자가 특정 username으로 인증되며, 이 username은 다음 단계에서 사용자의 결정에 사용할 수 있다. 일부 인증기는 사용자 그룹 관리 기능을 제공하는 반면, 이외의 인증기는 그렇지 않다.

쿠버네티스는 접근 제어 결정과 요청 기록 시 usernames를 사용하지만, user 오브젝트를 가지고 있지 않고 usernames 나 기타 사용자 정보를 오브젝트 저장소에 저장하지도 않는다.

인가

특정 사용자로부터 온 요청이 인증된 후에는 인가되어야 한다. 이는 다이어그램에 2단계로 표시되어 있다.

요청은 요청자의 username, 요청된 작업 및 해당 작업이 영향을 주는 오브젝트를 포함해야 한다. 기존 정책이 요청된 작업을 완료할 수 있는 권한이 해당 사용자에게 있다고 선언하는 경우 요청이 인가된다.

예를 들어 Bob이 아래와 같은 정책을 가지고 있다면 projectCaribou 네임스페이스에서만 파드를 읽을 수 있다.

{
    "apiVersion": "abac.authorization.kubernetes.io/v1beta1",
    "kind": "Policy",
    "spec": {
        "user": "bob",
        "namespace": "projectCaribou",
        "resource": "pods",
        "readonly": true
    }
}

Bob이 다음과 같은 요청을 하면 'projectCaribou' 네임스페이스의 오브젝트를 읽을 수 있기 때문에 요청이 인가된다.

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "projectCaribou",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    }
  }
}

Bob이 projectCaribou 네임스페이스에 있는 오브젝트에 쓰기(create 또는 update) 요청을 하면 그의 인가는 거부된다. 만약 Bob이 projectFish처럼 다른 네임스페이스의 오브젝트 읽기(get) 요청을 하면 그의 인가는 거부된다.

쿠버네티스 인가는 공통 REST 속성을 사용하여 기존 조직 전체 또는 클라우드 제공자 전체의 접근 제어 시스템과 상호 작용할 것을 요구한다. 이러한 제어 시스템은 쿠버네티스 API 이외의 다른 API와 상호작용할 수 있으므로 REST 형식을 사용하는 것이 중요하다.

쿠버네티스는 ABAC 모드, RBAC 모드, 웹훅 모드와 같은 여러 개의 인가 모듈을 지원한다. 관리자가 클러스터를 생성할 때 API 서버에서 사용해야 하는 인가 모듈을 구성했다. 인가 모듈이 2개 이상 구성되면 쿠버네티스가 각 모듈을 확인하고, 어느 모듈이 요청을 승인하면 요청을 진행할 수 있다. 모든 모듈이 요청을 거부하면 요청이 거부된다(HTTP 상태 코드 403).

인가 모듈을 사용한 정책 생성을 포함해 쿠버네티스 인가에 대해 더 배우려면 인가 개요를 참조한다.

어드미션 제어

어드미션 제어 모듈은 요청을 수정하거나 거부할 수 있는 소프트웨어 모듈이다. 인가 모듈에서 사용할 수 있는 속성 외에도 어드미션 제어 모듈은 생성되거나 수정된 오브젝트 내용에 접근할 수 있다.

어드미션 컨트롤러는 오브젝트를 생성, 수정, 삭제 또는 (프록시에) 연결하는 요청에 따라 작동한다. 어드미션 컨트롤러는 단순히 오브젝트를 읽는 요청에는 작동하지 않는다. 여러 개의 어드미션 컨트롤러가 구성되면 순서대로 호출된다.

이는 다이어그램에 3단계로 표시되어 있다.

인증 및 인가 모듈과 달리, 어드미션 제어 모듈이 거부되면 요청은 즉시 거부된다.

어드미션 제어 모듈은 오브젝트를 거부하는 것 외에도 필드의 복잡한 기본값을 설정할 수 있다.

사용 가능한 어드미션 제어 모듈은 여기에 서술되어 있다.

요청이 모든 어드미션 제어 모듈을 통과하면 유효성 검사 루틴을 사용하여 해당 API 오브젝트를 검증한 후 오브젝트 저장소에 기록(4단계)된다.

API 서버 포트와 IP

이전의 논의는 (일반적인 경우) API 서버의 보안 포트로 전송되는 요청에 적용된다. API 서버는 실제로 다음과 같이 2개의 포트에서 서비스할 수 있다.

기본적으로, 쿠버네티스 API 서버는 2개의 포트에서 HTTP 서비스를 한다.

  1. 로컬호스트 포트:

    • 테스트 및 부트스트랩을 하기 위한 것이며 마스터 노드의 다른 구성요소 (스케줄러, 컨트롤러 매니저)가 API와 통신하기 위한 것이다.
    • TLS가 없다.
    • 기본 포트는 8080이다.
    • 기본 IP는 로컬호스트(localhost)이며, --insecure-bind-address 플래그를 사용하여 변경한다.
    • 요청이 인증 및 인가 모듈을 우회한다.
    • 요청이 어드미션 제어 모듈(들)에 의해 처리된다.
    • 호스트 접근 요구로부터 보호를 받는다.
  2. 보안 포트:

    • 가능한 항상 사용하는 것이 좋다.
    • TLS를 사용한다. --tls-cert-file 플래그로 인증서를 지정하고 --tls-private-key-file 플래그로 키를 지정한다.
    • 기본 포트는 6443이며, --secure-port 플래그를 사용하여 변경한다.
    • 기본 IP는 로컬호스트가 아닌 첫 번째 네트워크 인터페이스이며, --bind-address 플래그를 사용하여 변경한다.
    • 요청이 인증 및 인가 모듈에 의해 처리된다.
    • 요청이 어드미션 제어 모듈(들)에 의해 처리된다.
    • 인증 및 인가 모듈을 실행한다.

GCE(구글 컴퓨트 엔진) 및 다른 클라우드 제공자에서 kube-up.sh로 클러스터를 생성하면 API 서버는 포트 443에서 서비스한다. GCE에서는 외부 HTTPS가 API에 접근할 수 있도록 프로젝트에서 방화벽 규칙이 구성된다. 이외에 클러스터 설정 방법은 다양하다.

3.9 - 스케줄링, 선점(Preemption), 축출(Eviction)

쿠버네티스에서, 스케줄링은 kubelet이 파드를 실행할 수 있도록 파드를 노드에 할당하는 것을 말한다. 선점은 우선순위가 높은 파드가 노드에 스케줄될 수 있도록 우선순위가 낮은 파드를 종료시키는 과정을 말한다. 축출은 리소스가 부족한 노드에서 하나 이상의 파드를 사전에 종료시키는 프로세스이다.

쿠버네티스에서, 스케줄링은 kubelet이 파드를 실행할 수 있도록 파드노드에 할당하는 것을 말한다. 선점은 우선순위가 높은 파드가 노드에 스케줄될 수 있도록 우선순위가 낮은 파드를 종료시키는 과정을 말한다. 축출은 리소스가 부족한 노드에서 하나 이상의 파드를 사전에 종료시키는 프로세스이다.

스케줄링

파드 중단(disruption)

파드 중단은 노드에 있는 파드가 자발적 또는 비자발적으로 종료되는 절차이다.

자발적 중단은 애플리케이션 소유자 또는 클러스터 관리자가 의도적으로 시작한다. 비자발적 중단은 의도하지 않은 것이며, 노드의 리소스 부족과 같은 피할 수 없는 문제 또는 우발적인 삭제로 인해 트리거될 수 있다.

3.9.1 - 쿠버네티스 스케줄러

쿠버네티스에서 스케줄링Kubelet이 파드를 실행할 수 있도록 파드노드에 적합한지 확인하는 것을 말한다.

스케줄링 개요

스케줄러는 노드가 할당되지 않은 새로 생성된 파드를 감시한다. 스케줄러가 발견한 모든 파드에 대해 스케줄러는 해당 파드가 실행될 최상의 노드를 찾는 책임을 진다. 스케줄러는 아래 설명된 스케줄링 원칙을 고려하여 이 배치 결정을 하게 된다.

파드가 특정 노드에 배치되는 이유를 이해하려고 하거나 사용자 정의된 스케줄러를 직접 구현하려는 경우 이 페이지를 통해서 스케줄링에 대해 배울 수 있을 것이다.

kube-scheduler

kube-scheduler는 쿠버네티스의 기본 스케줄러이며 컨트롤 플레인의 일부로 실행된다. kube-scheduler는 원하거나 필요에 따라 자체 스케줄링 컴포넌트를 만들고 대신 사용할 수 있도록 설계되었다.

새로 생성된 모든 파드 또는 예약되지 않은 다른 파드에 대해 kube-scheduler는 실행할 최적의 노드를 선택한다. 그러나 파드의 모든 컨테이너에는 리소스에 대한 요구사항이 다르며 모든 파드에도 요구사항이 다르다. 따라서 기존 노드들은 특정 스케줄링 요구사항에 따라 필터링 되어야 한다.

클러스터에서 파드에 대한 스케줄링 요구사항을 충족하는 노드를 실행 가능한(feasible) 노드라고 한다. 적합한 노드가 없으면 스케줄러가 배치할 수 있을 때까지 파드가 스케줄 되지 않은 상태로 유지된다.

스케줄러는 파드가 실행 가능한 노드를 찾은 다음 실행 가능한 노드의 점수를 측정하는 기능 셋을 수행하고 실행 가능한 노드 중에서 가장 높은 점수를 가진 노드를 선택하여 파드를 실행한다. 그런 다음 스케줄러는 바인딩 이라는 프로세스에서 이 결정에 대해 API 서버에 알린다.

스케줄링 결정을 위해 고려해야 할 요소에는 개별 및 집단 리소스 요구사항, 하드웨어 / 소프트웨어 / 정책 제한조건, 어피니티 및 안티-어피니티 명세, 데이터 지역성(data locality), 워크로드 간 간섭 등이 포함된다.

kube-scheduler에서 노드 선택

kube-scheduler는 2단계 작업에서 파드에 대한 노드를 선택한다.

  1. 필터링
  2. 스코어링(scoring)

필터링 단계는 파드를 스케줄링 할 수 있는 노드 셋을 찾는다. 예를 들어 PodFitsResources 필터는 후보 노드가 파드의 특정 리소스 요청을 충족시키기에 충분한 가용 리소스가 있는지 확인한다. 이 단계 다음에 노드 목록에는 적합한 노드들이 포함된다. 하나 이상의 노드가 포함된 경우가 종종 있을 것이다. 목록이 비어 있으면 해당 파드는 (아직) 스케줄링 될 수 없다.

스코어링 단계에서 스케줄러는 목록에 남아있는 노드의 순위를 지정하여 가장 적합한 파드 배치를 선택한다. 스케줄러는 사용 중인 스코어링 규칙에 따라 이 점수를 기준으로 필터링에서 통과된 각 노드에 대해 점수를 지정한다.

마지막으로 kube-scheduler는 파드를 순위가 가장 높은 노드에 할당한다. 점수가 같은 노드가 두 개 이상인 경우 kube-scheduler는 이들 중 하나를 임의로 선택한다.

스케줄러의 필터링 및 스코어링 동작을 구성하는 데 지원되는 두 가지 방법이 있다.

  1. 스케줄링 정책을 사용하면 필터링을 위한 단정(Predicates) 및 스코어링을 위한 우선순위(Priorities) 를 구성할 수 있다.
  2. 스케줄링 프로파일을 사용하면 QueueSort, Filter, Score, Bind, Reserve, Permit 등의 다른 스케줄링 단계를 구현하는 플러그인을 구성할 수 있다. 다른 프로파일을 실행하도록 kube-scheduler를 구성할 수도 있다.

다음 내용

3.9.2 - 노드에 파드 할당하기

특정한 노드(들) 집합에서만 동작하도록 파드를 제한할 수 있다. 이를 수행하는 방법에는 여러 가지가 있으며 권장되는 접근 방식은 모두 레이블 셀렉터를 사용하여 선택을 용이하게 한다. 보통 스케줄러가 자동으로 합리적인 배치(예: 자원이 부족한 노드에 파드를 배치하지 않도록 노드 간에 파드를 분배하는 등)를 수행하기에 이러한 제약 조건은 필요하지 않지만 간혹 파드가 배포할 노드를 제어해야 하는 경우가 있다. 예를 들어 SSD가 장착된 머신에 파드가 연결되도록 하거나 또는 동일한 가용성 영역(availability zone)에서 많은 것을 통신하는 두 개의 서로 다른 서비스의 파드를 같이 배치할 수 있다.

노드 셀렉터(nodeSelector)

nodeSelector 는 가장 간단하고 권장되는 노드 선택 제약 조건의 형태이다. nodeSelector 는 PodSpec의 필드이다. 이는 키-값 쌍의 매핑으로 지정한다. 파드가 노드에서 동작할 수 있으려면, 노드는 키-값의 쌍으로 표시되는 레이블을 각자 가지고 있어야 한다(이는 추가 레이블을 가지고 있을 수 있다). 일반적으로 하나의 키-값 쌍이 사용된다.

nodeSelector 를 어떻게 사용하는지 예시를 통해 알아보도록 하자.

0 단계: 사전 준비

이 예시는 쿠버네티스 파드에 대한 기본적인 이해를 하고 있고 쿠버네티스 클러스터가 설정되어 있다고 가정한다.

1 단계: 노드에 레이블 붙이기

kubectl get nodes 를 실행해서 클러스터 노드 이름을 가져온다. 이 중에 레이블을 추가하기 원하는 것 하나를 선택한 다음에 kubectl label nodes <노드 이름> <레이블 키>=<레이블 값> 을 실행해서 선택한 노드에 레이블을 추가한다. 예를 들어 노드의 이름이 'kubernetes-foo-node-1.c.a-robinson.internal' 이고, 원하는 레이블이 'disktype=ssd' 라면, kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd 를 실행한다.

kubectl get nodes --show-labels 를 다시 실행해서 노드가 현재 가진 레이블을 확인하여, 이 작업을 검증할 수 있다. 또한 kubectl describe node "노드 이름" 을 사용해서 노드에 주어진 레이블의 전체 목록을 확인할 수 있다.

2 단계: 파드 설정에 nodeSelector 필드 추가하기

실행하고자 하는 파드의 설정 파일을 가져오고, 이처럼 nodeSelector 섹션을 추가한다. 예를 들어 이것이 파드 설정이라면,

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx

이 다음에 nodeSelector 를 다음과 같이 추가한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

그런 다음에 kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml 을 실행하면, 레이블이 붙여진 노드에 파드가 스케줄된다. kubectl get pods -o wide 를 실행해서 파드가 할당된 "NODE" 를 보면 작동하는지 검증할 수 있다.

넘어가기 전에: 내장 노드 레이블들

붙인 레이블뿐만 아니라, 노드에는 표준 레이블 셋이 미리 채워져 있다. 이들 목록은 잘 알려진 레이블, 어노테이션 및 테인트를 참고한다.

노드 격리(isolation)/제한(restriction)

노드 오브젝트에 레이블을 추가하면 파드가 특정 노드 또는 노드 그룹을 목표 대상으로 할 수 있게 된다. 이는 특정 파드가 어떤 격리, 보안, 또는 규제 속성이 있는 노드에서만 실행되도록 사용할 수 있다. 이 목적으로 레이블을 사용하는 경우, 노드에서 kubelet 프로세스로 수정할 수 없는 레이블 키를 선택하는 것을 권장한다. 이렇게 하면 손상된 노드가 해당 kubelet 자격 증명을 사용해서 해당 레이블을 자체 노드 오브젝트에 설정하고, 스케줄러가 손상된 노드로 워크로드를 스케줄 하는 것을 방지할 수 있다.

NodeRestriction 어드미션 플러그인은 kubelet이 node-restriction.kubernetes.io/ 접두사로 레이블을 설정 또는 수정하지 못하게 한다. 노드 격리에 해당 레이블 접두사를 사용하려면 다음과 같이 한다.

  1. 노드 권한부여자를 사용하고 있고, NodeRestriction 어드미션 플러그인활성화 해야 한다.
  2. 노드 오브젝트의 node-restriction.kubernetes.io/ 접두사 아래에 레이블을 추가하고, 해당 레이블을 노드 셀렉터에서 사용한다. 예를 들어, example.com.node-restriction.kubernetes.io/fips=true 또는 example.com.node-restriction.kubernetes.io/pci-dss=true 이다.

어피니티(affinity)와 안티-어피니티(anti-affinity)

nodeSelector 는 파드를 특정 레이블이 있는 노드로 제한하는 매우 간단한 방법을 제공한다. 어피니티/안티-어피니티 기능은 표현할 수 있는 제약 종류를 크게 확장한다. 주요 개선 사항은 다음과 같다.

  1. 어피니티/안티-어피니티 언어가 더 표현적이다. 언어는 논리 연산자인 AND 연산으로 작성된 정확한 매칭 항목 이외에 더 많은 매칭 규칙을 제공한다.
  2. 규칙이 엄격한 요구 사항이 아니라 "유연한(soft)"/"선호(preference)" 규칙을 나타낼 수 있기에 스케줄러가 규칙을 만족할 수 없더라도, 파드가 계속 스케줄되도록 한다.
  3. 노드 자체에 레이블을 붙이기보다는 노드(또는 다른 토폴로지 도메인)에서 실행 중인 다른 파드의 레이블을 제한할 수 있다. 이를 통해 어떤 파드가 함께 위치할 수 있는지와 없는지에 대한 규칙을 적용할 수 있다.

어피니티 기능은 "노드 어피니티" 와 "파드 간 어피니티/안티-어피니티" 두 종류의 어피니티로 구성된다. 노드 어피니티는 기존 nodeSelector 와 비슷하지만(그러나 위에서 나열된 첫째와 두 번째 이점이 있다.), 파드 간 어피니티/안티-어피니티는 위에서 나열된 세번째 항목에 설명된 대로 노드 레이블이 아닌 파드 레이블에 대해 제한되고 위에서 나열된 첫 번째와 두 번째 속성을 가진다.

노드 어피니티

노드 어피니티는 개념적으로 nodeSelector 와 비슷하다 -- 이는 노드의 레이블을 기반으로 파드를 스케줄할 수 있는 노드를 제한할 수 있다.

여기에 현재 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 로 부르는 두 가지 종류의 노드 어피니티가 있다. 전자는 파드가 노드에 스케줄되도록 반드시 규칙을 만족해야 하는 것(nodeSelector 와 비슷하나 보다 표현적인 구문을 사용해서)을 지정하고, 후자는 스케줄러가 시도하려고는 하지만, 보증하지 않는 선호(preferences) 를 지정한다는 점에서 이를 각각 "엄격함(hard)" 과 "유연함(soft)" 으로 생각할 수 있다. 이름의 "IgnoredDuringExecution" 부분은 nodeSelector 작동 방식과 유사하게 노드의 레이블이 런타임 중에 변경되어 파드의 어피니티 규칙이 더 이상 충족되지 않으면 파드가 그 노드에서 동작한다는 의미이다. 향후에는 파드의 노드 어피니티 요구 사항을 충족하지 않는 노드에서 파드를 제거한다는 점을 제외하고는 requiredDuringSchedulingIgnoredDuringExecution 와 동일한 requiredDuringSchedulingRequiredDuringExecution 를 제공할 계획이다.

따라서 requiredDuringSchedulingIgnoredDuringExecution 의 예로는 "인텔 CPU가 있는 노드에서만 파드 실행"이 될 수 있고, preferredDuringSchedulingIgnoredDuringExecution 의 예로는 "장애 조치 영역 XYZ에 파드 집합을 실행하려고 하지만, 불가능하다면 다른 곳에서 일부를 실행하도록 허용"이 있을 것이다.

노드 어피니티는 PodSpec의 affinity 필드의 nodeAffinity 필드에서 지정된다.

여기에 노드 어피니티를 사용하는 파드 예시가 있다.

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

이 노드 어피니티 규칙은 키가 kubernetes.io/e2e-az-name 이고 값이 e2e-az1 또는 e2e-az2 인 레이블이 있는 노드에만 파드를 배치할 수 있다고 말한다. 또한, 이 기준을 충족하는 노드들 중에서 키가 another-node-label-key 이고 값이 another-node-label-value 인 레이블이 있는 노드를 선호하도록 한다.

예시에서 연산자 In 이 사용되고 있는 것을 볼 수 있다. 새로운 노드 어피니티 구문은 다음의 연산자들을 지원한다. In, NotIn, Exists, DoesNotExist, Gt, Lt. NotInDoesNotExist 를 사용해서 안티-어피니티를 수행하거나, 특정 노드에서 파드를 쫓아내는 노드 테인트(taint)를 설정할 수 있다.

nodeSelectornodeAffinity 를 모두 지정한다면 파드가 후보 노드에 스케줄되기 위해서는 둘 다 반드시 만족해야 한다.

nodeAffinity 유형과 연관된 nodeSelectorTerms 를 지정하면, nodeSelectorTerms하나라도 만족시키는 노드에 파드가 스케줄된다.

nodeSelectorTerms 와 연관된 여러 matchExpressions 를 지정하면, 파드는 matchExpressions모두 만족하는 노드에만 스케줄된다.

파드가 스케줄된 노드의 레이블을 지우거나 변경해도 파드는 제거되지 않는다. 다시 말해서 어피니티 선택은 파드를 스케줄링 하는 시점에만 작동한다.

preferredDuringSchedulingIgnoredDuringExecutionweight 필드의 범위는 1-100이다. 모든 스케줄링 요구 사항 (리소스 요청, RequiredDuringScheduling 어피니티 표현식 등)을 만족하는 각 노드들에 대해 스케줄러는 이 필드의 요소들을 반복해서 합계를 계산하고 노드가 MatchExpressions 에 일치하는 경우 합계에 "가중치(weight)"를 추가한다. 이후에 이 점수는 노드에 대한 다른 우선순위 함수의 점수와 합쳐진다. 전체 점수가 가장 높은 노드를 가장 선호한다.

스케줄링 프로파일당 노드 어피니티

FEATURE STATE: Kubernetes v1.20 [beta]

여러 스케줄링 프로파일을 구성할 때 노드 어피니티가 있는 프로파일을 연결할 수 있는데, 이는 프로파일이 특정 노드 집합에만 적용되는 경우 유용하다. 이렇게 하려면 스케줄러 구성에 있는 NodeAffinity 플러그인의 인수에 addedAffinity를 추가한다. 예를 들면

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinity.spec.schedulerNamefoo-scheduler로 설정하는 모든 파드에 적용되며 PodSpec에 지정된 NodeAffinity도 적용된다. 즉, 파드를 매칭시키려면, 노드가 addedAffinity와 파드의 .spec.NodeAffinity를 충족해야 한다.

addedAffinity는 엔드 유저에게 표시되지 않으므로, 예상치 못한 동작이 일어날 수 있다. 프로파일의 스케줄러 이름과 명확한 상관 관계가 있는 노드 레이블을 사용하는 것이 좋다.

파드간 어피니티와 안티-어피니티

파드간 어피니티와 안티-어피니티를 사용하면 노드의 레이블을 기반으로 하지 않고, 노드에서 이미 실행 중인 파드 레이블을 기반으로 파드가 스케줄될 수 있는 노드를 제한할 수 있다. 규칙은 "X가 규칙 Y를 충족하는 하나 이상의 파드를 이미 실행중인 경우 이 파드는 X에서 실행해야 한다(또는 안티-어피니티가 없는 경우에는 동작하면 안된다)"는 형태이다. Y는 선택적으로 연관된 네임스페이스 목록을 가진 LabelSelector로 표현된다. 노드와는 다르게 파드는 네임스페이스이기에 (그리고 따라서 파드의 레이블은 암암리에 네임스페이스이다) 파드 레이블위의 레이블 셀렉터는 반드시 셀렉터가 적용될 네임스페이스를 지정해야만 한다. 개념적으로 X는 노드, 랙, 클라우드 공급자 영역, 클라우드 공급자 지역 등과 같은 토폴로지 도메인이다. 시스템이 이런 토폴로지 도메인을 나타내는 데 사용하는 노드 레이블 키인 topologyKey 를 사용하여 이를 표현한다. 예: 넘어가기 전에: 빌트인 노드 레이블 섹션 위에 나열된 레이블 키를 본다.

노드 어피니티와 마찬가지로 현재 파드 어피니티와 안티-어피니티로 부르는 "엄격함" 대 "유연함"의 요구사항을 나타내는 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 두 가지 종류가 있다. 앞의 노드 어피니티 섹션의 설명을 본다. requiredDuringSchedulingIgnoredDuringExecution 어피니티의 예시는 "서로 많은 통신을 하기 때문에 서비스 A와 서비스 B를 같은 영역에 함께 위치시키는 것"이고, preferredDuringSchedulingIgnoredDuringExecution 안티-어피니티의 예시는 "서비스를 여러 영역에 걸쳐서 분배하는 것"이다 (엄격한 요구사항은 영역보다 파드가 더 많을 수 있기 때문에 엄격한 요구사항은 의미가 없다).

파드간 어피니티는 PodSpec에서 affinity 필드 중 podAffinity 필드로 지정한다. 그리고 파드간 안티-어피니티는 PodSpec에서 affinity 필드 중 podAntiAffinity 필드로 지정한다.

파드 어피니티를 사용하는 파드의 예시

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

이 파드의 어피니티는 하나의 파드 어피니티 규칙과 하나의 파드 안티-어피니티 규칙을 정의한다. 이 예시에서 podAffinityrequiredDuringSchedulingIgnoredDuringExecution 이고 podAntiAffinitypreferredDuringSchedulingIgnoredDuringExecution 이다. 파드 어피니티 규칙에 의하면 키 "security" 와 값 "S1"인 레이블이 있는 하나 이상의 이미 실행 중인 파드와 동일한 영역에 있는 경우에만 파드를 노드에 스케줄할 수 있다. (보다 정확하게는, 클러스터에 키 "security"와 값 "S1"인 레이블을 가지고 있는 실행 중인 파드가 있는 키 topology.kubernetes.io/zone 와 값 V인 노드가 최소 하나 이상 있고, 노드 N이 키 topology.kubernetes.io/zone 와 일부 값이 V인 레이블을 가진다면 파드는 노드 N에서 실행할 수 있다.) 파드 안티-어피니티 규칙에 의하면 파드는 키 "security"와 값 "S2"인 레이블을 가진 파드와 동일한 영역의 노드에 스케줄되지 않는다. 디자인 문서를 통해 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 의 파드 어피니티와 안티-어피니티에 대한 많은 예시를 맛볼 수 있다.

파드 어피니티와 안티-어피니티의 적합한 연산자는 In, NotIn, Exists, DoesNotExist 이다.

원칙적으로, topologyKey 는 적법한 어느 레이블-키도 될 수 있다. 하지만, 성능과 보안상의 이유로 topologyKey에는 몇 가지 제약조건이 있다.

  1. 파드 어피니티에서 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecutiontopologyKey 의 빈 값을 허용하지 않는다.
  2. 파드 안티-어피니티에서도 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecutiontopologyKey 의 빈 값을 허용하지 않는다.
  3. requiredDuringSchedulingIgnoredDuringExecution 파드 안티-어피니티에서 topologyKeykubernetes.io/hostname 로 제한하기 위해 어드미션 컨트롤러 LimitPodHardAntiAffinityTopology 가 도입되었다. 사용자 지정 토폴로지를 사용할 수 있도록 하려면, 어드미션 컨트롤러를 수정하거나 아니면 이를 비활성화해야 한다.
  4. 위의 경우를 제외하고, topologyKey 는 적법한 어느 레이블-키도 가능하다.

labelSelectortopologyKey 외에도 labelSelector 와 일치해야 하는 네임스페이스 목록 namespaces 를 선택적으로 지정할 수 있다(이것은 labelSelectortopologyKey 와 같은 수준의 정의이다). 생략되어 있거나 비어있을 경우 어피니티/안티-어피니티 정의가 있는 파드의 네임스페이스가 기본 값이다.

파드를 노드에 스케줄하려면 requiredDuringSchedulingIgnoredDuringExecution 어피니티와 안티-어피니티와 연관된 matchExpressions 가 모두 충족되어야 한다.

네임스페이스 셀렉터

FEATURE STATE: Kubernetes v1.22 [beta]

사용자는 네임스페이스 집합에 대한 레이블 쿼리인 namespaceSelector 를 사용하여 일치하는 네임스페이스를 선택할 수도 있다. 어피니티 용어는 namespaceSelector 에서 선택한 네임스페이스와 namespaces 필드에 나열된 네임스페이스의 결합에 적용된다. 빈 namespaceSelector ({})는 모든 네임스페이스와 일치하는 반면, null 또는 빈 namespaces 목록과 null namespaceSelector 는 "이 파드의 네임스페이스"를 의미한다.

이 기능은 베타이며 기본으로 활성화되어 있다. kube-apiserver 및 kube-scheduler 모두에서 기능 게이트 PodAffinityNamespaceSelector 를 사용하여 비활성화할 수 있다.

더 실용적인 유스케이스

파드간 어피니티와 안티-어피니티는 레플리카셋, 스테이트풀셋, 디플로이먼트 등과 같은 상위 레벨 모음과 함께 사용할 때 더욱 유용할 수 있다. 워크로드 집합이 동일한 노드와 같이 동일하게 정의된 토폴로지와 같은 위치에 배치되도록 쉽게 구성할 수 있다.

항상 같은 노드에 위치시키기

세 개의 노드가 있는 클러스터에서 웹 애플리케이션에는 redis와 같은 인-메모리 캐시가 있다. 웹 서버가 가능한 캐시와 함께 위치하기를 원한다.

다음은 세 개의 레플리카와 셀렉터 레이블이 app=store 가 있는 간단한 redis 디플로이먼트의 yaml 스니펫이다. 디플로이먼트에는 스케줄러가 단일 노드에서 레플리카를 함께 배치하지 않도록 PodAntiAffinity 가 구성되어 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

아래 yaml 스니펫의 웹서버 디플로이먼트는 podAntiAffinitypodAffinity 설정을 가지고 있다. 이렇게 하면 스케줄러에 모든 레플리카는 셀렉터 레이블이 app=store 인 파드와 함께 위치해야 한다. 또한 각 웹 서버 레플리카가 단일 노드의 같은 위치에 있지 않도록 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

만약 위의 두 디플로이먼트를 생성하면 세 개의 노드가 있는 클러스터는 다음과 같아야 한다.

node-1 node-2 node-3
webserver-1 webserver-2 webserver-3
cache-1 cache-2 cache-3

여기서 볼 수 있듯이 web-server 의 세 레플리카들이 기대했던 것처럼 자동으로 캐시와 함께 위치하게 된다.

kubectl get pods -o wide

출력은 다음과 유사할 것이다.

NAME                           READY     STATUS    RESTARTS   AGE       IP           NODE
redis-cache-1450370735-6dzlj   1/1       Running   0          8m        10.192.4.2   kube-node-3
redis-cache-1450370735-j2j96   1/1       Running   0          8m        10.192.2.2   kube-node-1
redis-cache-1450370735-z73mh   1/1       Running   0          8m        10.192.3.1   kube-node-2
web-server-1287567482-5d4dz    1/1       Running   0          7m        10.192.2.3   kube-node-1
web-server-1287567482-6f7v5    1/1       Running   0          7m        10.192.4.3   kube-node-3
web-server-1287567482-s330j    1/1       Running   0          7m        10.192.3.2   kube-node-2
절대 동일한 노드에 위치시키지 않게 하기

위의 예시에서 topologyKey:"kubernetes.io/hostname" 과 함께 PodAntiAffinity 규칙을 사용해서 두 개의 인스터스가 동일한 호스트에 있지 않도록 redis 클러스터를 배포한다. 같은 기술을 사용해서 고 가용성을 위해 안티-어피니티로 구성된 스테이트풀셋의 예시는 ZooKeeper 튜토리얼을 본다.

nodeName

nodeName 은 가장 간단한 형태의 노트 선택 제약 조건이지만, 한계로 인해 일반적으로는 사용하지 않는다. nodeName 은 PodSpec의 필드이다. 만약 비어있지 않으면, 스케줄러는 파드를 무시하고 명명된 노드에서 실행 중인 kubelet이 파드를 실행하려고 한다. 따라서 만약 PodSpec에 nodeName 가 제공된 경우, 노드 선택을 위해 위의 방법보다 우선한다.

nodeName 을 사용해서 노드를 선택할 때의 몇 가지 제한은 다음과 같다.

  • 만약 명명된 노드가 없으면, 파드가 실행되지 않고 따라서 자동으로 삭제될 수 있다.
  • 만약 명명된 노드에 파드를 수용할 수 있는 리소스가 없는 경우 파드가 실패하고, 그 이유는 다음과 같이 표시된다. 예: OutOfmemory 또는 OutOfcpu.
  • 클라우드 환경의 노드 이름은 항상 예측 가능하거나 안정적인 것은 아니다.

여기에 nodeName 필드를 사용하는 파드 설정 파일 예시가 있다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

위 파드는 kube-01 노드에서 실행될 것이다.

다음 내용

테인트는 노드가 특정 파드들을 쫓아낼 수 있다.

노드 어피니티파드간 어피니티/안티-어피니티에 대한 디자인 문서에는 이러한 기능에 대한 추가 배경 정보가 있다.

파드가 노드에 할당되면 kubelet은 파드를 실행하고 노드의 로컬 리소스를 할당한다. 토폴로지 매니저는 노드 수준의 리소스 할당 결정에 참여할 수 있다.

3.9.3 - 파드 오버헤드

FEATURE STATE: Kubernetes v1.18 [beta]

노드 위에서 파드를 구동할 때, 파드는 그 자체적으로 많은 시스템 리소스를 사용한다. 이러한 리소스는 파드 내의 컨테이너들을 구동하기 위한 리소스 이외에 추가적으로 필요한 것이다. 파드 오버헤드 는 컨테이너 리소스 요청과 상한 위에서 파드의 인프라에 의해 소비되는 리소스를 계산하는 기능이다.

쿠버네티스에서 파드의 오버헤드는 파드의 런타임클래스 와 관련된 오버헤드에 따라 어드미션 이 수행될 때 지정된다.

파드 오버헤드가 활성화 되면, 파드를 노드에 스케줄링 할 때 컨테이너 리소스 요청의 합에 파드의 오버헤드를 추가해서 스케줄링을 고려한다. 마찬가지로, kubelet은 파드의 cgroups 크기를 변경하거나 파드의 축출 등급을 부여할 때에도 파드의 오버헤드를 포함하여 고려한다.

파드 오버헤드 활성화하기

기능 활성화를 위해 클러스터에서 PodOverhead 기능 게이트가 활성화되어 있고(1.18 버전에서는 기본적으로 활성화), overhead 필드를 정의하는 RuntimeClass 가 사용되고 있는지 확인해야 한다.

사용 예제

파드 오버헤드 기능을 사용하기 위하여, overhead 필드를 정의하는 런타임클래스가 필요하다. 예를 들어, 가상 머신 및 게스트 OS에 대하여 파드 당 120 MiB를 사용하는 가상화 컨테이너 런타임의 런타임클래스의 경우 다음과 같이 정의 할 수 있다.

---
kind: RuntimeClass
apiVersion: node.k8s.io/v1
metadata:
    name: kata-fc
handler: kata-fc
overhead:
    podFixed:
        memory: "120Mi"
        cpu: "250m"

kata-fc 런타임클래스 핸들러를 지정하는 워크로드는 리소스 쿼터 계산, 노드 스케줄링 및 파드 cgroup 크기 조정을 위하여 메모리와 CPU 오버헤드를 고려한다.

주어진 예제 워크로드 test-pod의 구동을 고려해보자.

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  runtimeClassName: kata-fc
  containers:
  - name: busybox-ctr
    image: busybox
    stdin: true
    tty: true
    resources:
      limits:
        cpu: 500m
        memory: 100Mi
  - name: nginx-ctr
    image: nginx
    resources:
      limits:
        cpu: 1500m
        memory: 100Mi

어드미션 수행 시에, 어드미션 컨트롤러는 런타임클래스에 기술된 overhead 를 포함하기 위하여 워크로드의 PodSpec 항목을 갱신한다. 만약 PodSpec이 이미 해당 필드에 정의되어 있으면, 파드는 거부된다. 주어진 예제에서, 오직 런타임클래스의 이름만이 정의되어 있기 때문에, 어드미션 컨트롤러는 파드가 overhead 를 포함하도록 변경한다.

런타임클래스의 어드미션 수행 후에, 파드의 스펙이 갱신된 것을 확인할 수 있다.

kubectl get pod test-pod -o jsonpath='{.spec.overhead}'

명령 실행 결과는 다음과 같다.

map[cpu:250m memory:120Mi]

만약 리소스쿼터 항목이 정의되어 있다면, 컨테이너의 리소스 요청의 합에는 overhead 필드도 추가된다.

kube-scheduler 는 어떤 노드에 파드가 기동 되어야 할지를 정할 때, 파드의 overhead 와 해당 파드에 대한 컨테이너의 리소스 요청의 합을 고려한다. 이 예제에서, 스케줄러는 리소스 요청과 파드의 오버헤드를 더하고, 2.25 CPU와 320 MiB 메모리가 사용 가능한 노드를 찾는다.

일단 파드가 특정 노드에 스케줄링 되면, 해당 노드에 있는 kubelet 은 파드에 대한 새로운 cgroup을 생성한다. 기본 컨테이너 런타임이 만들어내는 컨테이너들은 이 파드 안에 존재한다.

만약 각 컨테이너에 대하여 QoS가 보장되었거나 향상이 가능하도록 QoS 의 리소스 상한 제한이 걸려있으면, kubelet 은 해당 리소스(CPU의 경우 cpu.cfs_quota_us, 메모리의 경우 memory.limit_in_bytes)와 연관된 파드의 cgroup 의 상한선을 설정한다. 이 상한선은 컨테이너 리소스 상한과 PodSpec에 정의된 overhead 의 합에 기반한다.

CPU의 경우, 만약 파드가 보장형 또는 버스트형 QoS로 설정되었으면, kubelet은 PodSpec에 정의된 overhead 에 컨테이너의 리소스 요청의 합을 더한 값을 cpu.shares 로 설정한다.

다음의 예제를 참고하여, 워크로드에 대하여 컨테이너의 리소스 요청을 확인하자.

kubectl get pod test-pod -o jsonpath='{.spec.containers[*].resources.limits}'

컨테이너 리소스 요청의 합은 각각 CPU 2000m 와 메모리 200MiB 이다.

map[cpu: 500m memory:100Mi] map[cpu:1500m memory:100Mi]

노드에서 측정된 내용과 비교하여 확인해보자.

kubectl describe node | grep test-pod -B2

CPU 2250m와 메모리 320MiB 가 리소스로 요청되었으며, 이 결과는 파드의 오버헤드를 포함한다.

  Namespace                   Name                CPU Requests  CPU Limits   Memory Requests  Memory Limits  AGE
  ---------                   ----                ------------  ----------   ---------------  -------------  ---
  default                     test-pod            2250m (56%)   2250m (56%)  320Mi (1%)       320Mi (1%)     36m

파드 cgroup 상한 확인하기

워크로드가 실행 중인 노드에서 파드의 메모리 cgroup들을 확인 해보자. 다음의 예제에서, crictl은 노드에서 사용되며, CRI-호환 컨테이너 런타임을 위해서 노드에서 사용할 수 있는 CLI 를 제공한다. 파드의 오버헤드 동작을 보여주는 좋은 예이며, 사용자가 노드에서 직접 cgroup들을 확인하지 않아도 된다.

먼저 특정 노드에서 파드의 식별자를 확인해 보자.

# 파드가 스케줄 된 노드에서 이것을 실행
POD_ID="$(sudo crictl pods --name test-pod -q)"

여기에서, 파드의 cgroup 경로를 확인할 수 있다.

# 파드가 스케줄 된 노드에서 이것을 실행
sudo crictl inspectp -o=json $POD_ID | grep cgroupsPath

명령의 결과로 나온 cgroup 경로는 파드의 pause 컨테이너를 포함한다. 파드 레벨의 cgroup은 하나의 디렉터리이다.

        "cgroupsPath": "/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/7ccf55aee35dd16aca4189c952d83487297f3cd760f1bbf09620e206e7d0c27a"

아래의 특정한 경우에, 파드 cgroup 경로는 kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2 이다. 메모리의 파드 레벨 cgroup 설정을 확인하자.

# 파드가 스케줄 된 노드에서 이것을 실행.
# 또한 사용자의 파드에 할당된 cgroup 이름에 맞춰 해당 이름을 수정.
 cat /sys/fs/cgroup/memory/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/memory.limit_in_bytes

예상대로 320 MiB 이다.

335544320

관찰성

kube_pod_overhead 항목은 kube-state-metrics 에서 사용할 수 있어, 파드 오버헤드가 사용되는 시기를 식별하고, 정의된 오버헤드로 실행되는 워크로드의 안정성을 관찰할 수 있다. 이 기능은 kube-state-metrics 의 1.9 릴리스에서는 사용할 수 없지만, 다음 릴리스에서는 가능할 예정이다. 그 전까지는 소스로부터 kube-state-metric 을 빌드해야 한다.

다음 내용

3.9.4 - 테인트(Taints)와 톨러레이션(Tolerations)

노드 어피니티노드 셋을 (기본 설정 또는 어려운 요구 사항으로) 끌어들이는 파드의 속성이다. 테인트 는 그 반대로, 노드가 파드 셋을 제외할 수 있다.

톨러레이션 은 파드에 적용되며, 파드를 일치하는 테인트가 있는 노드에 스케줄되게 하지만 필수는 아니다.

테인트와 톨러레이션은 함께 작동하여 파드가 부적절한 노드에 스케줄되지 않게 한다. 하나 이상의 테인트가 노드에 적용된다. 이것은 노드가 테인트를 용인하지 않는 파드를 수용해서는 안 되는 것을 나타낸다.

개요

kubectl taint를 사용하여 노드에 테인트을 추가한다. 예를 들면 다음과 같다.

kubectl taint nodes node1 key1=value1:NoSchedule

node1 노드에 테인트을 배치한다. 테인트에는 키 key1, 값 value1 및 테인트 이펙트(effect) NoSchedule 이 있다. 이는 일치하는 톨러레이션이 없으면 파드를 node1 에 스케줄할 수 없음을 의미한다.

위의 명령으로 추가한 테인트를 제거하려면, 다음을 실행한다.

kubectl taint nodes node1 key1=value1:NoSchedule-

PodSpec에서 파드에 대한 톨러레이션를 지정한다. 다음의 톨러레이션은 위의 kubectl taint 라인에 의해 생성된 테인트와 "일치"하므로, 어느 쪽 톨러레이션을 가진 파드이던 node1 에 스케줄 될 수 있다.

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

톨러레이션을 사용하는 파드의 예는 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

지정하지 않으면 operator 의 기본값은 Equal 이다.

톨러레이션은 키가 동일하고 이펙트가 동일한 경우, 테인트와 "일치"한다. 그리고 다음의 경우에도 마찬가지다.

  • operatorExists 인 경우(이 경우 value 를 지정하지 않아야 함), 또는
  • operatorEqual 이고 valuevalue 로 같다.

위의 예는 NoScheduleeffect 를 사용했다. 또는, PreferNoScheduleeffect 를 사용할 수 있다. 이것은 NoSchedule 의 "기본 설정(preference)" 또는 "소프트(soft)" 버전이다. 시스템은 노드의 테인트를 허용하지 않는 파드를 배치하지 않으려고 시도 하지만, 필요하지는 않다. 세 번째 종류의 effect 는 나중에 설명할 NoExecute 이다.

동일한 노드에 여러 테인트를, 동일한 파드에 여러 톨러레이션을 둘 수 있다. 쿠버네티스가 여러 테인트 및 톨러레이션을 처리하는 방식은 필터와 같다. 모든 노드의 테인트로 시작한 다음, 파드에 일치하는 톨러레이션이 있는 것을 무시한다. 무시되지 않은 나머지 테인트는 파드에 표시된 이펙트를 가진다. 특히,

  • NoSchedule 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 해당 노드에 파드를 스케줄하지 않는다.
  • NoSchedule 이펙트가 있는 무시되지 않은 테인트가 없지만 PreferNoSchedule 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 파드를 노드에 스케쥴하지 않으려고 시도 한다
  • NoExecute 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 파드가 노드에서 축출되고(노드에서 이미 실행 중인 경우), 노드에서 스케줄되지 않는다(아직 실행되지 않은 경우).

예를 들어, 이와 같은 노드를 테인트하는 경우는 다음과 같다.

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

그리고 파드에는 두 가지 톨러레이션이 있다.

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

이 경우, 세 번째 테인트와 일치하는 톨러레이션이 없기 때문에, 파드는 노드에 스케줄 될 수 없다. 그러나 세 번째 테인트가 파드에서 용인되지 않는 세 가지 중 하나만 있기 때문에, 테인트가 추가될 때 노드에서 이미 실행 중인 경우, 파드는 계속 실행할 수 있다.

일반적으로, NoExecute 이펙트가 있는 테인트가 노드에 추가되면, 테인트를 용인하지 않는 파드는 즉시 축출되고, 테인트를 용인하는 파드는 축출되지 않는다. 그러나 NoExecute 이펙트가 있는 톨러레이션은 테인트가 추가된 후 파드가 노드에 바인딩된 시간을 지정하는 선택적 tolerationSeconds 필드를 지정할 수 있다. 예를 들어,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

이것은 이 파드가 실행 중이고 일치하는 테인트가 노드에 추가되면, 파드는 3600초 동안 노드에 바인딩된 후, 축출된다는 것을 의미한다. 그 전에 테인트를 제거하면, 파드가 축출되지 않는다.

유스케이스 예시

테인트 및 톨러레이션은 파드를 노드에서 멀어지게 하거나 실행되지 않아야 하는 파드를 축출할 수 있는 유연한 방법이다. 유스케이스 중 일부는 다음과 같다.

  • 전용 노드: 특정 사용자들이 독점적으로 사용하도록 노드 셋을 전용하려면, 해당 노드에 테인트를 추가(예: kubectl taint nodes nodename dedicated=groupName:NoSchedule)한 다음 해당 톨러레이션을 그들의 파드에 추가할 수 있다(사용자 정의 [어드미션 컨트롤러] (/docs/reference/access-authn-authz/admission-controllers/)를 작성하면 가장 쉽게 수행할 수 있음). 그런 다음 톨러레이션이 있는 파드는 테인트된(전용) 노드와 클러스터의 다른 노드를 사용할 수 있다. 노드를 특정 사용자들에게 전용으로 지정하고 그리고 그 사용자들이 전용 노드 사용하려면, 동일한 노드 셋에 테인트와 유사한 레이블을 추가해야 하고(예: dedicated=groupName), 어드미션 컨트롤러는 추가로 파드가 dedicated=groupName 으로 레이블이 지정된 노드에만 스케줄될 수 있도록 노드 어피니티를 추가해야 한다.

  • 특별한 하드웨어가 있는 노드: 작은 서브셋의 노드에 특별한 하드웨어(예: GPU)가 있는 클러스터에서는, 특별한 하드웨어가 필요하지 않는 파드를 해당 노드에서 분리하여, 나중에 도착하는 특별한 하드웨어가 필요한 파드를 위한 공간을 남겨두는 것이 바람직하다. 이는 특별한 하드웨어가 있는 노드(예: kubectl taint nodes nodename special=true:NoSchedule 또는 kubectl taint nodes nodename special=true:PreferNoSchedule)에 테인트를 추가하고 특별한 하드웨어를 사용하는 파드에 해당 톨러레이션을 추가하여 수행할 수 있다. 전용 노드 유스케이스에서와 같이, 사용자 정의 어드미션 컨트롤러를 사용하여 톨러레이션를 적용하는 것이 가장 쉬운 방법이다. 예를 들어, 확장된 리소스를 사용하여 특별한 하드웨어를 나타내고, 확장된 리소스 이름으로 특별한 하드웨어 노드를 테인트시키고 ExtendedResourceToleration 어드미션 컨트롤러를 실행하는 것을 권장한다. 이제, 노드가 테인트되었으므로, 톨러레이션이 없는 파드는 스케줄되지 않는다. 그러나 확장된 리소스를 요청하는 파드를 제출하면, ExtendedResourceToleration 어드미션 컨트롤러가 파드에 올바른 톨러레이션을 자동으로 추가하고 해당 파드는 특별한 하드웨어 노드에서 스케줄된다. 이렇게 하면 이러한 특별한 하드웨어 노드가 해당 하드웨어를 요청하는 파드가 전용으로 사용하며 파드에 톨러레이션을 수동으로 추가할 필요가 없다.

  • 테인트 기반 축출: 노드 문제가 있을 때 파드별로 구성 가능한 축출 동작은 다음 섹션에서 설명한다.

테인트 기반 축출

FEATURE STATE: Kubernetes v1.18 [stable]

앞에서 우리는 노드에서 이미 실행 중인 파드에 영향을 주는 NoExecute 테인트 이펙트를 다음과 같이 언급했다.

  • 테인트를 용인하지 않는 파드는 즉시 축출된다.
  • 톨러레이션 명세에 tolerationSeconds 를 지정하지 않고 테인트를 용인하는 파드는 계속 바인딩된다.
  • tolerationSeconds 가 지정된 테인트를 용인하는 파드는 지정된 시간 동안 바인딩된 상태로 유지된다.

노드 컨트롤러는 특정 조건이 참일 때 자동으로 노드를 테인트시킨다. 다음은 빌트인 테인트이다.

  • node.kubernetes.io/not-ready: 노드가 준비되지 않았다. 이는 NodeCondition Ready 가 "False"로 됨에 해당한다.
  • node.kubernetes.io/unreachable: 노드가 노드 컨트롤러에서 도달할 수 없다. 이는 NodeCondition Ready 가 "Unknown"로 됨에 해당한다.
  • node.kubernetes.io/memory-pressure: 노드에 메모리 할당 압박이 있다.
  • node.kubernetes.io/disk-pressure: 노드에 디스크 할당 압박이 있다.
  • node.kubernetes.io/pid-pressure: 노드에 PID 할당 압박이 있다.
  • node.kubernetes.io/network-unavailable: 노드의 네트워크를 사용할 수 없다.
  • node.kubernetes.io/unschedulable: 노드를 스케줄할 수 없다.
  • node.cloudprovider.kubernetes.io/uninitialized: "외부" 클라우드 공급자로 kubelet을 시작하면, 이 테인트가 노드에서 사용 불가능으로 표시되도록 설정된다. 클라우드-컨트롤러-관리자의 컨트롤러가 이 노드를 초기화하면, kubelet이 이 테인트를 제거한다.

노드가 축출될 경우, 노드 컨트롤러 또는 kubelet은 NoExecute 이펙트로 관련 테인트를 추가한다. 장애 상태가 정상으로 돌아오면 kubelet 또는 노드 컨트롤러가 관련 테인트를 제거할 수 있다.

이 기능을 tolerationSeconds 와 함께 사용하면, 파드에서 이러한 문제 중 하나 또는 둘 다가 있는 노드에 바인딩된 기간을 지정할 수 있다.

예를 들어, 로컬 상태가 많은 애플리케이션은 네트워크 분할의 장애에서 네트워크가 복구된 후에 파드가 축출되는 것을 피하기 위해 오랫동안 노드에 바인딩된 상태를 유지하려고 할 수 있다. 이 경우 파드가 사용하는 톨러레이션은 다음과 같다.

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

데몬셋 파드는 tolerationSeconds 가 없는 다음 테인트에 대해 NoExecute 톨러레이션를 가지고 생성된다.

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

이렇게 하면 이러한 문제로 인해 데몬셋 파드가 축출되지 않는다.

조건(condition)을 기준으로 노드 테인트하기

컨트롤 플레인은 노드 컨트롤러를 이용하여 노드 조건에 대한 NoSchedule 효과를 사용하여 자동으로 테인트를 생성한다.

스케줄러는 스케줄링 결정을 내릴 때 노드 조건을 확인하는 것이 아니라 테인트를 확인한다. 이렇게 하면 노드 조건이 스케줄링에 직접적인 영향을 주지 않는다. 예를 들어 DiskPressure 노드 조건이 활성화된 경우 컨트롤 플레인은 node.kubernetes.io/disk-pressure 테인트를 추가하고 영향을 받는 노드에 새 파드를 할당하지 않는다. MemoryPressure 노드 조건이 활성화되면 컨트롤 플레인이 node.kubernetes.io/memory-pressure 테인트를 추가한다.

새로 생성된 파드에 파드 톨러레이션을 추가하여 노드 조건을 무시하도록 할 수 있다. 또한 컨트롤 플레인은 BestEffort 이외의 QoS 클래스를 가지는 파드에 node.kubernetes.io/memory-pressure 톨러레이션을 추가한다. 이는 쿠버네티스가 Guaranteed 또는 Burstable QoS 클래스를 갖는 파드(메모리 요청이 설정되지 않은 파드 포함)를 마치 그 파드들이 메모리 압박에 대처 가능한 것처럼 다루는 반면, 새로운 BestEffort 파드는 영향을 받는 노드에 할당하지 않기 때문이다.

데몬셋 컨트롤러는 다음의 NoSchedule 톨러레이션을 모든 데몬에 자동으로 추가하여, 데몬셋이 중단되는 것을 방지한다.

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/pid-pressure (1.14 이상)
  • node.kubernetes.io/unschedulable (1.10 이상)
  • node.kubernetes.io/network-unavailable (호스트 네트워크만 해당)

이러한 톨러레이션을 추가하면 이전 버전과의 호환성이 보장된다. 데몬셋에 임의의 톨러레이션을 추가할 수도 있다.

다음 내용

3.9.5 - 노드-압박 축출

노드-압박 축출은 kubelet이 노드의 자원을 회수하기 위해 파드를 능동적으로 중단시키는 절차이다.

kubelet은 클러스터 노드의 CPU, 메모리, 디스크 공간, 파일시스템 inode와 같은 자원을 모니터링한다. 이러한 자원 중 하나 이상이 특정 소모 수준에 도달하면, kubelet은 하나 이상의 파드를 능동적으로 중단시켜 자원을 회수하고 고갈 상황을 방지할 수 있다.

노드-압박 축출 과정에서, kubelet은 축출할 파드의 PodPhaseFailed로 설정한다. 이로써 파드가 종료된다.

노드-압박 축출은 API를 이용한 축출과는 차이가 있다.

kubelet은 이전에 설정된 PodDisruptionBudget 값이나 파드의 terminationGracePeriodSeconds 값을 따르지 않는다. 소프트 축출 임계값을 사용하는 경우, kubelet은 이전에 설정된 eviction-max-pod-grace-period 값을 따른다. 하드 축출 임계값을 사용하는 경우, 파드 종료 시 0s 만큼 기다린 후 종료한다(즉, 기다리지 않고 바로 종료한다).

실패한 파드를 새로운 파드로 교체하는 워크로드 리소스(예: 스테이트풀셋(StatefulSet) 또는 디플로이먼트(Deployment))가 파드를 관리하는 경우, 컨트롤 플레인이나 kube-controller-manager가 축출된 파드를 대신할 새 파드를 생성한다.

kubelet은 축출 결정을 내리기 위해 다음과 같은 다양한 파라미터를 사용한다.

  • 축출 신호
  • 축출 임계값
  • 모니터링 간격

축출 신호

축출 신호는 특정 시점에서 특정 자원의 현재 상태이다. Kubelet은 노드에서 사용할 수 있는 리소스의 최소량인 축출 임계값과 축출 신호를 비교하여 축출 결정을 내린다.

Kubelet은 다음과 같은 축출 신호를 사용한다.

축출 신호 설명
memory.available memory.available := node.status.capacity[memory] - node.stats.memory.workingSet
nodefs.available nodefs.available := node.stats.fs.available
nodefs.inodesFree nodefs.inodesFree := node.stats.fs.inodesFree
imagefs.available imagefs.available := node.stats.runtime.imagefs.available
imagefs.inodesFree imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree
pid.available pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

이 표에서, 설명 열은 kubelet이 축출 신호 값을 계산하는 방법을 나타낸다. 각 축출 신호는 백분율 또는 숫자값을 지원한다. Kubelet은 총 용량 대비 축출 신호의 백분율 값을 계산한다.

memory.available 값은 free -m과 같은 도구가 아니라 cgroupfs로부터 도출된다. 이는 free -m이 컨테이너 안에서는 동작하지 않고, 또한 사용자가 node allocatable 기능을 사용하는 경우 자원 부족에 대한 결정은 루트 노드뿐만 아니라 cgroup 계층 구조의 최종 사용자 파드 부분에서도 지역적으로 이루어지기 때문에 중요하다. 이 스크립트는 kubelet이 memory.available을 계산하기 위해 수행하는 동일한 단계들을 재현한다. kubelet은 메모리 압박 상황에서 메모리가 회수 가능하다고 가정하므로, inactive_file(즉, 비활성 LRU 목록의 파일 기반 메모리 바이트 수)을 계산에서 제외한다.

kubelet은 다음과 같은 파일시스템 파티션을 지원한다.

  1. nodefs: 노드의 메인 파일시스템이며, 로컬 디스크 볼륨, emptyDir, 로그 스토리지 등에 사용된다. 예를 들어 nodefs/var/lib/kubelet/을 포함한다.
  2. imagefs: 컨테이너 런타임이 컨테이너 이미지 및 컨테이너 쓰기 가능 레이어를 저장하는 데 사용하는 선택적 파일시스템이다.

Kubelet은 이러한 파일시스템을 자동으로 검색하고 다른 파일시스템은 무시한다. Kubelet은 다른 구성은 지원하지 않는다.

축출 임계값

kubelet이 축출 결정을 내릴 때 사용하는 축출 임계값을 사용자가 임의로 설정할 수 있다.

축출 임계값은 [eviction-signal][operator][quantity] 형태를 갖는다.

  • eviction-signal에는 사용할 축출 신호를 적는다.
  • operator에는 관계연산자를 적는다(예: < - 미만)
  • quantity에는 1Gi와 같이 축출 임계값 수치를 적는다. quantity에 들어가는 값은 쿠버네티스가 사용하는 수치 표현 방식과 맞아야 한다. 숫자값 또는 백분율(%)을 사용할 수 있다.

예를 들어, 노드에 총 10Gi의 메모리가 있고 1Gi 아래로 내려갔을 때 축출이 시작되도록 만들고 싶으면, 축출 임계값을 memory.available<10% 또는 memory.available<1Gi 형태로 정할 수 있다. 둘을 동시에 사용할 수는 없다.

소프트 축출 임계값과 하드 축출 임계값을 설정할 수 있다.

소프트 축출 임계값

소프트 축출 임계값은 관리자가 설정하는 유예 시간(필수)과 함께 정의된다. kubelet은 유예 시간이 초과될 때까지 파드를 제거하지 않는다. 유예 시간이 지정되지 않으면 kubelet 시작 시 오류가 반환된다.

kubelet이 축출 과정에서 사용할 수 있도록, '소프트 축출 임계값'과 '최대 허용 파드 종료 유예 시간' 둘 다를 설정할 수 있다. '최대 허용 파드 종료 유예 시간'이 설정되어 있는 상태에서 '소프트 축출 임계값'에 도달하면, kubelet은 두 유예 시간 중 작은 쪽을 적용한다. '최대 허용 파드 종료 유예 시간'을 설정하지 않으면, kubelet은 축출된 파드를 유예 시간 없이 즉시 종료한다.

소프트 축출 임계값을 설정할 때 다음과 같은 플래그를 사용할 수 있다.

  • eviction-soft: 축출 임계값(예: memory.available<1.5Gi)의 집합이며, 지정된 유예 시간동안 이 축출 임계값 조건이 충족되면 파드 축출이 트리거된다.
  • eviction-soft-grace-period: 축출 유예 시간의 집합이며, 소프트 축출 임계값 조건이 이 유예 시간동안 충족되면 파드 축출이 트리거된다.
  • eviction-max-pod-grace-period: '최대 허용 파드 종료 유예 시간(단위: 초)'이며, 소프트 축출 임계값 조건이 충족되어 파드를 종료할 때 사용한다.

하드 축출 임계값

하드 축출 임계값에는 유예 시간이 없다. 하드 축출 임계값 조건이 충족되면, kubelet은 고갈된 자원을 회수하기 위해 파드를 유예 시간 없이 즉시 종료한다.

eviction-hard 플래그를 사용하여 하드 축출 임계값(예: memory.available<1Gi)을 설정할 수 있다.

kubelet은 다음과 같은 하드 축출 임계값을 기본적으로 설정하고 있다.

  • memory.available<100Mi
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5% (리눅스 노드)

축출 모니터링 시간 간격

kubelet은 housekeeping-interval에 설정된 시간 간격(기본값: 10s)마다 축출 임계값을 확인한다.

노드 컨디션

kubelet은 하드/소프트 축출 임계값 조건이 충족되어 노드 압박이 발생했다는 것을 알리기 위해, 설정된 유예 시간과는 관계없이 노드 컨디션을 보고한다.

kubelet은 다음과 같이 노드 컨디션과 축출 신호를 매핑한다.

노드 컨디션 축출 신호 설명
MemoryPressure memory.available 노드의 가용 메모리 양이 축출 임계값에 도달함
DiskPressure nodefs.available, nodefs.inodesFree, imagefs.available, 또는 imagefs.inodesFree 노드의 루트 파일시스템 또는 이미지 파일시스템의 가용 디스크 공간 또는 inode의 수가 축출 임계값에 도달함
PIDPressure pid.available (리눅스) 노드의 가용 프로세스 ID(PID)가 축출 임계값 이하로 내려옴

kubelet은 --node-status-update-frequency에 설정된 시간 간격(기본값: 10s)마다 노드 컨디션을 업데이트한다.

노드 컨디션 진동(oscillation)

경우에 따라, 노드의 축출 신호값이 사전에 설정된 유예 시간 동안 유지되지 않고 소프트 축출 임계값을 중심으로 진동할 수 있다. 이로 인해 노드 컨디션이 계속 truefalse로 바뀌며, 잘못된 축출 결정을 야기할 수 있다.

이러한 진동을 방지하기 위해, eviction-pressure-transition-period 플래그를 사용하여 kubelet이 노드 컨디션을 다른 상태로 바꾸기 위해 기다려야 하는 시간을 설정할 수 있다. 기본값은 5m이다.

노드-수준 자원 회수하기

kubelet은 최종 사용자 파드를 축출하기 전에 노드-수준 자원 회수를 시도한다.

DiskPressure 노드 컨디션이 보고되면, kubelet은 노드의 파일시스템을 기반으로 노드-수준 자원을 회수한다.

imagefs가 있는 경우

컨테이너 런타임이 사용할 전용 imagefs 파일시스템이 노드에 있으면, kubelet은 다음 작업을 수행한다.

  • nodefs 파일시스템이 축출 임계값 조건을 충족하면, kubelet은 종료된 파드와 컨테이너에 대해 가비지 수집을 수행한다.
  • imagefs 파일시스템이 축출 임계값 조건을 충족하면, kubelet은 모든 사용중이지 않은 이미지를 삭제한다.

imagefs가 없는 경우

노드에 nodefs 파일시스템만 있고 이것이 축출 임계값 조건을 충족한 경우, kubelet은 다음 순서로 디스크 공간을 확보한다.

  1. 종료된 파드와 컨테이너에 대해 가비지 수집을 수행한다.
  2. 사용중이지 않은 이미지를 삭제한다.

kubelet 축출을 위한 파드 선택

kubelet이 노드-수준 자원을 회수했음에도 축출 신호가 임계값 아래로 내려가지 않으면, kubelet은 최종 사용자 파드 축출을 시작한다.

kubelet은 파드 축출 순서를 결정하기 위해 다음의 파라미터를 활용한다.

  1. 파드의 자원 사용량이 요청량을 초과했는지 여부
  2. 파드 우선순위
  3. 파드의 자원 요청량 대비 자원 사용량

결과적으로, kubelet은 다음과 같은 순서로 파드의 축출 순서를 정하고 축출을 수행한다.

  1. BestEffort 또는 Burstable 파드 중 자원 사용량이 요청량을 초과한 파드. 이 파드들은 파드들의 우선순위, 그리고 자원 사용량이 요청량을 얼마나 초과했는지에 따라 축출된다.
  2. Guaranteed, Burstable 파드 중 자원 사용량이 요청량보다 낮은 파드는 우선순위에 따라 후순위로 축출된다.

Guaranteed 파드는 모든 컨테이너에 대해 자원 요청량과 제한이 명시되고 그 둘이 동일할 때에만 보장(guaranteed)된다. 다른 파드의 자원 사용으로 인해 Guaranteed 파드가 축출되는 일은 발생하지 않는다. 만약 시스템 데몬(예: kubelet, docker, journald)이 system-reserved 또는 kube-reserved 할당을 통해 예약된 것보다 더 많은 자원을 소비하고, 노드에는 요청량보다 적은 양의 자원을 사용하고 있는 Guaranteed / Burstable 파드만 존재한다면, kubelet은 노드 안정성을 유지하고 자원 고갈이 다른 파드에 미칠 영향을 통제하기 위해 이러한 파드 중 하나를 골라 축출해야 한다. 이 경우, 가장 낮은 Priority를 갖는 파드가 선택된다.

inodesPIDs에 대한 요청량은 정의하고 있지 않기 때문에, kubelet이 inode 또는 PID 고갈 때문에 파드를 축출할 때에는 파드의 Priority를 이용하여 축출 순위를 정한다.

노드에 전용 imagefs 파일시스템이 있는지 여부에 따라 kubelet이 파드 축출 순서를 정하는 방식에 차이가 있다.

imagefs가 있는 경우

nodefs로 인한 축출의 경우, kubelet은 nodefs 사용량(모든 컨테이너의 로컬 볼륨 + 로그)을 기준으로 축출 순서를 정한다.

imagefs로 인한 축출의 경우, kubelet은 모든 컨테이너의 쓰기 가능한 레이어(writable layer) 사용량을 기준으로 축출 순서를 정한다.

imagefs가 없는 경우

nodefs로 인한 축출의 경우, kubelet은 각 파드의 총 디스크 사용량(모든 컨테이너의 로컬 볼륨 + 로그 + 쓰기 가능한 레이어)을 기준으로 축출 순서를 정한다.

최소 축출 회수량

경우에 따라, 파드를 축출했음에도 적은 양의 자원만이 회수될 수 있다. 이로 인해 kubelet이 반복적으로 축출 임계값 도달을 감지하고 여러 번의 축출을 수행할 수 있다.

--eviction-minimum-reclaim 플래그 또는 kubelet 설정 파일을 이용하여 각 자원에 대한 최소 회수량을 설정할 수 있다. kubelet이 자원 부족 상황을 감지하면, 앞서 설정한 최소 회수량에 도달할때까지 회수를 계속 진행한다.

예를 들어, 다음 YAML은 최소 회수량을 정의하고 있다.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "1Gi"
  imagefs.available: "100Gi"
evictionMinimumReclaim:
  memory.available: "0Mi"
  nodefs.available: "500Mi"
  imagefs.available: "2Gi"

이 예제에서, 만약 nodefs.available 축출 신호가 축출 임계값 조건에 도달하면, kubelet은 축출 신호가 임계값인 1Gi에 도달할 때까지 자원을 회수하며, 이어서 축출 신호가 1.5Gi에 도달할 때까지 최소 500Mi 이상의 자원을 회수한다.

유사한 방식으로, kubelet은 imagefs.available 축출 신호가 102Gi에 도달할 때까지 imagefs 자원을 회수한다.

모든 자원에 대해 eviction-minimum-reclaim의 기본값은 0이다.

노드 메모리 부족 시의 동작

kubelet의 메모리 회수가 가능하기 이전에 노드에 메모리 부족(out of memory, 이하 OOM) 이벤트가 발생하면, 노드는 oom_killer에 의존한다.

kubelet은 각 파드에 설정된 QoS를 기반으로 각 컨테이너에 oom_score_adj 값을 설정한다.

서비스 품질(Quality of Service) oom_score_adj
Guaranteed -997
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

노드가 OOM을 겪기 전에 kubelet이 메모리를 회수하지 못하면, oom_killer가 노드의 메모리 사용률 백분율을 이용하여 oom_score를 계산하고, 각 컨테이너의 실질 oom_score를 구하기 위해 oom_score_adj를 더한다. 그 뒤 oom_score가 가장 높은 컨테이너부터 종료시킨다.

이는 곧, 스케줄링 요청에 비해 많은 양의 메모리를 사용하면서 QoS가 낮은 파드에 속한 컨테이너가 먼저 종료됨을 의미한다.

파드 축출과 달리, 컨테이너가 OOM으로 인해 종료되면, kubelet이 컨테이너의 RestartPolicy를 기반으로 컨테이너를 다시 실행할 수 있다.

추천 예시

아래 섹션에서 축출 설정에 대한 추천 예시를 소개한다.

스케줄 가능한 자원과 축출 정책

kubelet에 축출 정책을 설정할 때, 만약 어떤 파드 배치가 즉시 메모리 압박을 야기하기 때문에 축출을 유발한다면 스케줄러가 그 파드 배치를 수행하지 않도록 설정해야 한다.

다음 시나리오를 가정한다.

  • 노드 메모리 용량: 10Gi
  • 운영자는 시스템 데몬(커널, kubelet 등)을 위해 메모리 용량의 10%를 확보해 놓고 싶어 한다.
  • 운영자는 시스템 OOM 발생을 줄이기 위해 메모리 사용률이 95%인 상황에서 파드를 축출하고 싶어한다.

이것이 실현되도록, kubelet이 다음과 같이 실행된다.

--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi

이 환경 설정에서, --system-reserved 플래그는 시스템 용으로 1.5Gi 메모리를 확보하는데, 이는 총 메모리의 10% + 축출 임계값에 해당된다.

파드가 요청량보다 많은 메모리를 사용하거나 시스템이 1Gi 이상의 메모리를 사용하여, memory.available 축출 신호가 500Mi 아래로 내려가면 노드가 축출 임계값에 도달할 수 있다.

데몬셋(DaemonSet)

파드 우선 순위(Priority)는 파드 축출 결정을 내릴 때의 주요 요소이다. kubelet이 DaemonSet에 속하는 파드를 축출하지 않도록 하려면 해당 파드의 파드 스펙에 충분히 높은 priorityClass를 지정한다. 또는 낮은 priorityClass나 기본값을 사용하여 리소스가 충분할 때만 DaemonSet 파드가 실행되도록 허용할 수도 있다.

알려진 이슈

다음 섹션에서는 리소스 부족 처리와 관련된 알려진 이슈에 대해 다룬다.

kubelet이 메모리 압박을 즉시 감지하지 못할 수 있음

기본적으로 kubelet은 cAdvisor를 폴링하여 일정한 간격으로 메모리 사용량 통계를 수집한다. 해당 타임 윈도우 내에서 메모리 사용량이 빠르게 증가하면 kubelet이 MemoryPressure를 충분히 빠르게 감지하지 못해 OOMKiller가 계속 호출될 수 있다.

--kernel-memcg-notification 플래그를 사용하여 kubelet의 memcg 알림 API가 임계값을 초과할 때 즉시 알림을 받도록 할 수 있다.

사용률(utilization)을 극단적으로 높이려는 것이 아니라 오버커밋(overcommit)에 대한 합리적인 조치만 원하는 경우, 이 문제에 대한 현실적인 해결 방법은 --kube-reserved--system-reserved 플래그를 사용하여 시스템에 메모리를 할당하는 것이다.

active_file 메모리가 사용 가능한 메모리로 간주되지 않음

리눅스에서, 커널은 활성 LRU 목록의 파일 지원 메모리 바이트 수를 active_file 통계로 추적한다. kubelet은 active_file 메모리 영역을 회수할 수 없는 것으로 취급한다. 임시 로컬 스토리지를 포함하여 블록 지원 로컬 스토리지를 집중적으로 사용하는 워크로드의 경우 파일 및 블록 데이터의 커널 수준 캐시는 최근에 액세스한 많은 캐시 페이지가 active_file로 계산될 가능성이 있음을 의미한다. 활성 LRU 목록에 이러한 커널 블록 버퍼가 충분히 많으면, kubelet은 이를 높은 자원 사용 상태로 간주하고 노드가 메모리 압박을 겪고 있다고 테인트를 표시할 수 있으며, 이는 파드 축출을 유발한다.

더 자세한 사항은 https://github.com/kubernetes/kubernetes/issues/43916를 참고한다.

집중적인 I/O 작업을 수행할 가능성이 있는 컨테이너에 대해 메모리 제한량 및 메모리 요청량을 동일하게 설정하여 이 문제를 해결할 수 있다. 해당 컨테이너에 대한 최적의 메모리 제한량을 추정하거나 측정해야 한다.

다음 내용

3.9.6 - API를 이용한 축출(Eviction)

API를 이용한 축출은 축출 API를 사용하여 생성된 Eviction 오브젝트로 파드를 정상 종료한다.

kubectl drain 명령과 같은 kube-apiserver의 클라이언트를 사용하여, 축출 API를 직접 호출해 축출 요청을 할 수 있다. 그러면 API 서버가 파드를 종료하는 Eviction 오브젝트가 생성된다.

API를 이용한 축출은 구성된 PodDisruptionBudgetsterminationGracePeriodSeconds를 준수한다.

다음 내용

3.9.7 - 파드 우선순위(priority)와 선점(preemption)

FEATURE STATE: Kubernetes v1.14 [stable]

파드우선순위 를 가질 수 있다. 우선순위는 다른 파드에 대한 상대적인 파드의 중요성을 나타낸다. 파드를 스케줄링할 수 없는 경우, 스케줄러는 우선순위가 낮은 파드를 선점(축출)하여 보류 중인 파드를 스케줄링할 수 있게 한다.

우선순위와 선점을 사용하는 방법

우선순위와 선점을 사용하려면 다음을 참고한다.

  1. 하나 이상의 프라이어리티클래스를 추가한다.

  2. 추가된 프라이어리티클래스 중 하나에 priorityClassName이 설정된 파드를 생성한다. 물론 파드를 직접 생성할 필요는 없다. 일반적으로 디플로이먼트와 같은 컬렉션 오브젝트의 파드 템플릿에 priorityClassName 을 추가한다.

이 단계에 대한 자세한 내용은 계속 읽어보자.

프라이어리티클래스

프라이어리티클래스는 프라이어리티클래스 이름에서 우선순위의 정수 값으로의 매핑을 정의하는 네임스페이스가 아닌(non-namespaced) 오브젝트이다. 이름은 프라이어리티클래스 오브젝트의 메타데이터의 name 필드에 지정된다. 값은 필수 value 필드에 지정되어 있다. 값이 클수록, 우선순위가 높다. 프라이어리티클래스 오브젝트의 이름은 유효한 DNS 서브 도메인 이름이어야 하며, system- 접두사를 붙일 수 없다.

프라이어리티클래스 오브젝트는 10억 이하의 32비트 정수 값을 가질 수 있다. 일반적으로 선점하거나 축출해서는 안되는 중요한 시스템 파드에는 더 큰 숫자가 예약되어 있다. 클러스터 관리자는 원하는 각 매핑에 대해 프라이어리티클래스 오브젝트를 하나씩 생성해야 한다.

프라이어리티클래스에는 globalDefaultdescription 두 개의 선택적인 필드도 있다. globalDefault 필드는 이 프라이어리티클래스의 값을 priorityClassName 이 없는 파드에 사용해야 함을 나타낸다. 시스템에서 globalDefaulttrue 로 설정된 프라이어리티클래스는 하나만 존재할 수 있다. globalDefault 가 설정된 프라이어리티클래스가 없을 경우, priorityClassName 이 없는 파드의 우선순위는 0이다.

description 필드는 임의의 문자열이다. 이 필드는 이 프라이어리티클래스를 언제 사용해야 하는지를 클러스터 사용자에게 알려주기 위한 것이다.

PodPriority와 기존 클러스터에 대한 참고 사항

  • 이 기능없이 기존 클러스터를 업그레이드 하는 경우, 기존 파드의 우선순위는 사실상 0이다.

  • globalDefaulttrue 로 설정된 프라이어리티클래스를 추가해도 기존 파드의 우선순위는 변경되지 않는다. 이러한 프라이어리티클래스의 값은 프라이어리티클래스를 추가한 후 생성된 파드에만 사용된다.

  • 프라이어리티클래스를 삭제하면, 삭제된 프라이어리티클래스의 이름을 사용하는 기존 파드는 변경되지 않고 남아있지만, 삭제된 프라이어리티클래스의 이름을 사용하는 파드는 더 이상 생성할 수 없다.

프라이어리티클래스 예제

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "이 프라이어리티클래스는 XYZ 서비스 파드에만 사용해야 한다."

비-선점 프라이어리티클래스

FEATURE STATE: Kubernetes v1.19 [beta]

PreemptionPolicy: Never 를 가진 파드는 낮은 우선순위 파드의 스케줄링 대기열의 앞쪽에 배치되지만, 그 파드는 다른 파드를 축출할 수 없다. 스케줄링 대기 중인 비-선점 파드는 충분한 리소스가 확보되고 스케줄링될 수 있을 때까지 스케줄링 대기열에 대기한다. 다른 파드와 마찬가지로, 비-선점 파드는 스케줄러 백오프(back-off)에 종속된다. 이는 스케줄러가 이러한 파드를 스케줄링하려고 시도하고 스케줄링할 수 없는 경우, 더 적은 횟수로 재시도하여, 우선순위가 낮은 다른 파드를 미리 스케줄링할 수 있음을 의미한다.

비-선점 파드는 다른 우선순위가 높은 파드에 의해 축출될 수 있다.

PreemptionPolicy 는 기본값으로 PreemptLowerPriority 로 설정되어, 해당 프라이어리티클래스의 파드가 우선순위가 낮은 파드를 축출할 수 있다(기존의 기본 동작과 동일). PreemptionPolicyNever 로 설정된 경우, 해당 프라이어리티클래스의 파드는 비-선점될 것이다.

예제 유스케이스는 데이터 과학 관련 워크로드이다. 사용자는 다른 워크로드보다 우선순위가 높은 잡(job)을 제출할 수 있지만, 실행 중인 파드를 축출하여 기존의 작업을 삭제하지는 않을 것이다. 클러스터 리소스가 "자연스럽게" 충분히 사용할 수 있게 되면, PreemptionPolicy: Never 의 우선순위가 높은 잡이 다른 대기 중인 파드보다 먼저 스케줄링된다.

비-선점 프라이어리티클래스 예제

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "이 프라이어리티클래스는 다른 파드를 축출하지 않는다."

파드 우선순위

프라이어리티클래스가 하나 이상 있으면, 그것의 명세에서 이들 프라이어리티클래스 이름 중 하나를 지정하는 파드를 생성할 수 있다. 우선순위 어드미션 컨트롤러는 priorityClassName 필드를 사용하고 우선순위의 정수 값을 채운다. 프라이어리티클래스를 찾을 수 없으면, 파드가 거부된다.

다음의 YAML은 이전 예제에서 생성된 프라이어리티클래스를 사용하는 파드 구성의 예이다. 우선순위 어드미션 컨트롤러는 명세를 확인하고 파드의 우선순위를 1000000으로 해석한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

스케줄링 순서에 대한 파드 우선순위의 영향

파드 우선순위가 활성화되면, 스케줄러가 우선순위에 따라 보류 중인 파드를 주문하고 보류 중인 파드는 스케줄링 대기열에서 우선순위가 낮은 다른 보류 중인 파드보다 우선한다. 결과적으로, 스케줄링 요구 사항이 충족되는 경우 우선순위가 더 낮은 파드보다 우선순위가 높은 파드가 더 빨리 스케줄링될 수 있다. 이러한 파드를 스케줄링할 수 없는 경우, 스케줄러는 계속 진행하고 우선순위가 낮은 다른 파드를 스케줄링하려고 한다.

선점

파드가 생성되면, 대기열로 이동하여 스케줄링을 기다린다. 스케줄러가 대기열에서 파드를 선택하여 노드에 스케줄링하려고 한다. 파드의 지정된 모든 요구 사항을 충족하는 노드가 없으면, 보류 중인 파드에 대해 선점 로직이 트리거된다. 보류 중인 파드를 P라 하자. 선점 로직은 P보다 우선순위가 낮은 하나 이상의 파드를 제거하면 해당 노드에서 P를 스케줄링할 수 있는 노드를 찾는다. 이러한 노드가 발견되면, 하나 이상의 우선순위가 낮은 파드가 노드에서 축출된다. 파드가 축출된 후, P는 노드에 스케줄링될 수 있다.

사용자 노출 정보

파드 P가 노드 N에서 하나 이상의 파드를 축출할 경우, 파드 P의 상태 nominatedNodeName 필드는 노드 N의 이름으로 설정된다. 이 필드는 스케줄러가 파드 P에 예약된 리소스를 추적하는 데 도움이 되고 사용자에게 클러스터의 선점에 대한 정보를 제공한다.

파드 P는 반드시 "지정된 노드"로 스케줄링되지는 않는다. 피해자 파드가 축출된 후, 그것은 정상적(graceful)으로 종료되는 기간을 갖는다. 스케줄러가 종료될 피해자 파드를 기다리는 동안 다른 노드를 사용할 수 있게 되면, 스케줄러는 파드 P를 스케줄링하기 위해 다른 노드를 사용한다. 그 결과, 파드 스펙의 nominatedNodeNamenodeName 은 항상 동일하지 않다. 또한, 스케줄러가 노드 N에서 파드를 축출했지만, 파드 P보다 우선순위가 높은 파드가 도착하면, 스케줄러가 노드 N에 새로운 우선순위가 높은 파드를 제공할 수 있다. 이러한 경우, 스케줄러는 파드 P의 nominatedNodeName 을 지운다. 이렇게하면, 스케줄러는 파드 P가 다른 노드에서 파드를 축출할 수 있도록 한다.

선점의 한계

선점 피해자의 정상적인 종료

파드가 축출되면, 축출된 피해자 파드는 정상적인 종료 기간을 가진다. 피해자 파드는 작업을 종료하고 빠져나가는 데(exit) 많은 시간을 가진다. 그렇지 않으면, 파드는 강제종료(kill) 된다. 이 정상적인 종료 기간은 스케줄러가 파드를 축출하는 지점과 보류 중인 파드(P)를 노드(N)에서 스케줄링할 수 있는 시점 사이의 시간 간격을 만든다. 그 동안, 스케줄러는 보류 중인 다른 파드를 계속 스케줄링한다. 피해자 파드가 빠져나가거나 종료되면, 스케줄러는 보류 대기열에서 파드를 스케줄하려고 한다. 따라서, 일반적으로 스케줄러가 피해자 파드를 축출하는 시점과 파드 P가 스케줄링된 시점 사이에 시간 간격이 있다. 이러한 차이를 최소화하기 위해, 우선순위가 낮은 파드의 정상적인 종료 기간을 0 또는 작은 수로 설정할 수 있다.

PodDisruptionBudget을 지원하지만, 보장하지 않음

Pod Disruption Budget(PDB)은 애플리케이션 소유자가 자발적 중단에서 동시에 다운된 복제된 애플리케이션의 파드 수를 제한할 수 있다. 쿠버네티스는 파드를 선점할 때 PDB를 지원하지만, PDB를 따르는 것이 최선의 노력이다. 스케줄러는 선점에 의해 PDB를 위반하지 않은 피해자 파드를 찾으려고 하지만, 그러한 피해자 파드가 발견되지 않으면, 선점은 여전히 발생하며, PDB를 위반하더라도 우선순위가 낮은 파드는 제거된다.

우선순위가 낮은 파드에 대한 파드-간 어피니티

이 질문에 대한 답변이 '예'인 경우에만 노드가 선점 대상으로 간주된다. "대기 중인 파드보다 우선순위가 낮은 모든 파드가 노드에서 제거되면, 보류 중인 파드를 노드에 스케줄링할 수 있습니까?"

보류 중인 파드가 노드에 있는 하나 이상의 우선순위가 낮은 파드에 대한 파드-간 어피니티를 가진 경우에, 우선순위가 낮은 파드가 없을 때 파드-간 어피니티 규칙을 충족할 수 없다. 이 경우, 스케줄러는 노드의 파드를 축출하지 않는다. 대신, 다른 노드를 찾는다. 스케줄러가 적합한 노드를 찾거나 찾지 못할 수 있다. 보류 중인 파드를 스케줄링할 수 있다는 보장은 없다.

이 문제에 대한 권장 솔루션은 우선순위가 같거나 높은 파드에 대해서만 파드-간 어피니티를 생성하는 것이다.

교차 노드 선점

보류 중인 파드 P가 노드 N에 스케줄링될 수 있도록 노드 N이 선점 대상으로 고려되고 있다고 가정한다. 다른 노드의 파드가 축출된 경우에만 P가 N에서 실행 가능해질 수 있다. 예를 들면 다음과 같다.

  • 파드 P는 노드 N에 대해 고려된다.
  • 파드 Q는 노드 N과 동일한 영역의 다른 노드에서 실행 중이다.
  • 파드 P는 파드 Q(topologyKey: topology.kubernetes.io/zone)와 영역(zone) 전체의 안티-어피니티를 갖는다.
  • 영역에서 파드 P와 다른 파드 간의 안티-어피니티에 대한 다른 경우는 없다.
  • 노드 N에서 파드 P를 스케줄링하기 위해, 파드 Q를 축출할 수 있지만, 스케줄러는 교차-노드 선점을 수행하지 않는다. 따라서, 파드 P가 노드 N에서 스케줄링할 수 없는 것으로 간주된다.

파드 Q가 노드에서 제거되면, 파드 안티-어피니티 위반이 사라지고, 파드 P는 노드 N에서 스케줄링될 수 있다.

수요가 충분하고 합리적인 성능의 알고리즘을 찾을 경우 향후 버전에서 교차 노드 선점의 추가를 고려할 수 있다.

문제 해결

파드 우선순위와 선점은 원하지 않는 부작용을 가질 수 있다. 다음은 잠재적 문제의 예시와 이를 해결하는 방법이다.

파드가 불필요하게 선점(축출)됨

선점은 우선순위가 높은 보류 중인 파드를 위한 공간을 만들기 위해 리소스 압박을 받고 있는 클러스터에서 기존 파드를 제거한다. 실수로 특정 파드에 높은 우선순위를 부여하면, 의도하지 않은 높은 우선순위 파드가 클러스터에서 선점을 유발할 수 있다. 파드 우선순위는 파드 명세에서 priorityClassName 필드를 설정하여 지정한다. 그런 다음 우선순위의 정수 값이 분석되어 podSpecpriority 필드에 채워진다.

문제를 해결하기 위해, 해당 파드가 우선순위가 낮은 클래스를 사용하도록 priorityClassName 을 변경하거나, 해당 필드를 비워둘 수 있다. 빈 priorityClassName 은 기본값이 0으로 해석된다.

파드가 축출되면, 축출된 파드에 대한 이벤트가 기록된다. 선점은 클러스터가 파드에 대한 리소스를 충분히 가지지 못한 경우에만 발생한다. 이러한 경우, 선점은 보류 중인 파드(선점자)의 우선순위가 피해자 파드보다 높은 경우에만 발생한다. 보류 중인 파드가 없거나, 보류 중인 파드의 우선순위가 피해자 파드와 같거나 낮은 경우 선점이 발생하지 않아야 한다. 그러한 시나리오에서 선점이 발생하면, 이슈를 올리기 바란다.

파드가 축출되었지만, 선점자는 스케줄링되지 않음

파드가 축출되면, 요청된 정상적인 종료 기간(기본적으로 30초)이 주어진다. 이 기간 내에 대상 파드가 종료되지 않으면, 강제 종료된다. 모든 피해자 파드가 사라지면, 선점자 파드를 스케줄링할 수 있다.

선점자 파드가 피해자 파드가 없어지기를 기다리는 동안, 동일한 노드에 적합한 우선순위가 높은 파드가 생성될 수 있다. 이 경우, 스케줄러는 선점자 대신 우선순위가 높은 파드를 스케줄링한다.

이것은 예상된 동작이다. 우선순위가 높은 파드는 우선순위가 낮은 파드를 대체해야 한다.

우선순위가 높은 파드는 우선순위가 낮은 파드보다 우선함

스케줄러가 보류 중인 파드를 실행할 수 있는 노드를 찾으려고 한다. 노드를 찾지 못하면, 스케줄러는 임의의 노드에서 우선순위가 낮은 파드를 제거하여 보류 중인 파드를 위한 공간을 만든다. 우선순위가 낮은 파드가 있는 노드가 보류 중인 파드를 실행할 수 없는 경우, 스케줄러는 선점을 위해 우선순위가 높은 다른 노드(다른 노드의 파드와 비교)를 선택할 수 있다. 피해자 파드는 여전히 선점자 파드보다 우선순위가 낮아야 한다.

선점할 수 있는 여러 노드가 있는 경우, 스케줄러는 우선순위가 가장 낮은 파드 세트를 가진 노드를 선택하려고 한다. 그러나, 이러한 파드가 위반될 PodDisruptionBudget을 가지고 있고 축출된 경우 스케줄러는 우선순위가 높은 파드를 가진 다른 노드를 선택할 수 있다.

선점을 위해 여러 개의 노드가 존재하고 위의 시나리오 중 어느 것도 적용되지 않는 경우, 스케줄러는 우선순위가 가장 낮은 노드를 선택한다.

파드 우선순위와 서비스 품질 간의 상호 작용

파드 우선순위와 QoS 클래스는 상호 작용이 거의 없고 QoS 클래스를 기반으로 파드 우선순위를 설정하는 데 대한 기본 제한이 없는 두 개의 직교(orthogonal) 기능이다. 스케줄러의 선점 로직은 선점 대상을 선택할 때 QoS를 고려하지 않는다. 선점은 파드 우선순위를 고려하고 우선순위가 가장 낮은 대상 세트를 선택하려고 한다. 우선순위가 가장 높은 파드는 스케줄러가 선점자 파드를 스케줄링할 수 없거나 우선순위가 가장 낮은 파드가 PodDisruptionBudget 으로 보호되는 경우에만, 우선순위가 가장 낮은 파드를 축출 대상으로 고려한다.

kubelet은 우선순위를 사용하여 파드의 노드-압박(node-pressure) 축출 순서를 결정한다. 사용자는 QoS 클래스를 사용하여 어떤 파드가 축출될 것인지 예상할 수 있다. kubelet은 다음의 요소들을 통해서 파드의 축출 순위를 매긴다.

  1. 기아(starved) 리소스 사용량이 요청을 초과하는지 여부
  2. 파드 우선순위
  3. 요청 대비 리소스 사용량

더 자세한 내용은 kubelet 축출을 위한 파드 선택을 참조한다.

kubelet 노드-압박 축출은 사용량이 요청을 초과하지 않는 경우 파드를 축출하지 않는다. 우선순위가 낮은 파드가 요청을 초과하지 않으면, 축출되지 않는다. 요청을 초과하는 우선순위가 더 높은 다른 파드가 축출될 수 있다.

다음 내용

3.9.8 - 확장된 리소스를 위한 리소스 빈 패킹(bin packing)

FEATURE STATE: Kubernetes v1.16 [alpha]

kube-scheduler는 RequestedToCapacityRatioResourceAllocation 우선 순위 기능을 사용해서 확장된 리소스와 함께 리소스의 빈 패킹이 가능하도록 구성할 수 있다. 우선 순위 기능을 사용해서 맞춤 요구에 따라 kube-scheduler를 미세 조정할 수 있다.

RequestedToCapacityRatioResourceAllocation을 사용해서 빈 패킹 활성화하기

쿠버네티스를 사용하면 사용자가 각 리소스에 대한 가중치와 함께 리소스를 지정하여 용량 대비 요청 비율을 기반으로 노드의 점수를 매기는 것을 허용한다. 이를 통해 사용자는 적절한 파라미터를 사용해서 확장된 리소스를 빈 팩으로 만들 수 있어 대규모의 클러스터에서 부족한 리소스의 활용도가 향상된다. RequestedToCapacityRatioResourceAllocation 우선 순위 기능의 동작은 RequestedToCapacityRatioArgs라는 구성 옵션으로 제어할 수 있다. 이 인수는 shaperesources 두 개의 파라미터로 구성된다. shape 파라미터는 사용자가 utilizationscore 값을 기반으로 최소 요청 또는 최대 요청된 대로 기능을 조정할 수 있게 한다. resources 파라미터는 점수를 매길 때 고려할 리소스의 name 과 각 리소스의 가중치를 지정하는 weight 로 구성된다.

다음은 확장된 리소스 intel.com/foointel.com/bar 에 대한 requestedToCapacityRatioArguments 를 빈 패킹 동작으로 설정하는 구성의 예시이다.

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
profiles:
# ...
  pluginConfig:
  - name: RequestedToCapacityRatio
    args: 
      shape:
      - utilization: 0
        score: 10
      - utilization: 100
        score: 0
      resources:
      - name: intel.com/foo
        weight: 3
      - name: intel.com/bar
        weight: 5

kube-scheduler 플래그 --config=/path/to/config/file 을 사용하여 KubeSchedulerConfiguration 파일을 참조하면 구성이 스케줄러에 전달된다.

이 기능은 기본적으로 비활성화되어 있다.

우선 순위 기능 튜닝하기

shapeRequestedToCapacityRatioPriority 기능의 동작을 지정하는 데 사용된다.

shape:
 - utilization: 0
   score: 0
 - utilization: 100
   score: 10

위의 인수는 utilization 이 0%인 경우 score 는 0, utilization 이 100%인 경우 10으로 하여, 빈 패킹 동작을 활성화한다. 최소 요청을 활성화하려면 점수 값을 다음과 같이 변경해야 한다.

shape:
  - utilization: 0
    score: 10
  - utilization: 100
    score: 0

resources 는 기본적으로 다음과 같이 설정되는 선택적인 파라미터이다.

resources:
  - name: cpu
    weight: 1
  - name: memory
    weight: 1

다음과 같이 확장된 리소스를 추가하는 데 사용할 수 있다.

resources:
  - name: intel.com/foo
    weight: 5
  - name: cpu
    weight: 3
  - name: memory
    weight: 1

weight 파라미터는 선택 사항이며 지정되지 않은 경우 1로 설정 된다. 또한, weight 는 음수로 설정할 수 없다.

용량 할당을 위해 노드에 점수 매기기

이 섹션은 이 기능 내부의 세부적인 사항을 이해하려는 사람들을 위한 것이다. 아래는 주어진 값의 집합에 대해 노드 점수가 계산되는 방법의 예시이다.

요청된 리소스는 다음과 같다.

intel.com/foo : 2
memory: 256MB
cpu: 2

리소스의 가중치는 다음과 같다.

intel.com/foo : 5
memory: 1
cpu: 3

FunctionShapePoint {{0, 0}, {100, 10}}

노드 1의 사양은 다음과 같다.

Available:
  intel.com/foo: 4
  memory: 1 GB
  cpu: 8

Used:
  intel.com/foo: 1
  memory: 256MB
  cpu: 1

노드 점수는 다음과 같다.

intel.com/foo  = resourceScoringFunction((2+1),4)
               = (100 - ((4-3)*100/4)
               = (100 - 25)
               = 75                       # requested + used = 75% * available
               = rawScoringFunction(75) 
               = 7                        # floor(75/10) 

memory         = resourceScoringFunction((256+256),1024)
               = (100 -((1024-512)*100/1024))
               = 50                       # requested + used = 50% * available
               = rawScoringFunction(50)
               = 5                        # floor(50/10)

cpu            = resourceScoringFunction((2+1),8)
               = (100 -((8-3)*100/8))
               = 37.5                     # requested + used = 37.5% * available
               = rawScoringFunction(37.5)
               = 3                        # floor(37.5/10)

NodeScore   =  (7 * 5) + (5 * 1) + (3 * 3) / (5 + 1 + 3)
            =  5

노드 2의 사양은 다음과 같다.

Available:
  intel.com/foo: 8
  memory: 1GB
  cpu: 8
Used:
  intel.com/foo: 2
  memory: 512MB
  cpu: 6

노드 점수는 다음과 같다.

intel.com/foo  = resourceScoringFunction((2+2),8)
               =  (100 - ((8-4)*100/8)
               =  (100 - 50)
               =  50
               =  rawScoringFunction(50)
               = 5

Memory         = resourceScoringFunction((256+512),1024)
               = (100 -((1024-768)*100/1024))
               = 75
               = rawScoringFunction(75)
               = 7

cpu            = resourceScoringFunction((2+6),8)
               = (100 -((8-8)*100/8))
               = 100
               = rawScoringFunction(100)
               = 10

NodeScore   =  (5 * 5) + (7 * 1) + (10 * 3) / (5 + 1 + 3)
            =  7

다음 내용

3.9.9 - 스케줄러 성능 튜닝

FEATURE STATE: Kubernetes 1.14 [beta]

kube-scheduler는 쿠버네티스의 기본 스케줄러이다. 그것은 클러스터의 노드에 파드를 배치하는 역할을 한다.

파드의 스케줄링 요건을 충족하는 클러스터의 노드를 파드에 적합한(feasible) 노드라고 한다. 스케줄러는 파드에 대해 적합한 노드를 찾고 기능 셋을 실행하여 해당 노드의 점수를 측정한다. 그리고 스케줄러는 파드를 실행하는데 적합한 모든 노드 중 가장 높은 점수를 가진 노드를 선택한다. 이후 스케줄러는 바인딩 이라는 프로세스로 API 서버에 해당 결정을 통지한다.

본 페이지에서는 상대적으로 큰 규모의 쿠버네티스 클러스터에 대한 성능 튜닝 최적화에 대해 설명한다.

큰 규모의 클러스터에서는 스케줄러의 동작을 튜닝하여 응답 시간 (새 파드가 빠르게 배치됨)과 정확도(스케줄러가 배치 결정을 잘 못하는 경우가 드물게 됨) 사이에서의 스케줄링 결과를 균형 잡을 수 있다.

kube-scheduler 의 percentageOfNodesToScore 설정을 통해 이 튜닝을 구성 한다. 이 KubeSchedulerConfiguration 설정에 따라 클러스터의 노드를 스케줄링할 수 있는 임계값이 결정된다.

임계값 설정하기

percentageOfNodesToScore 옵션은 0과 100 사이의 값을 허용한다. 값 0은 kube-scheduler가 컴파일 된 기본값을 사용한다는 것을 나타내는 특별한 숫자이다. percentageOfNodesToScore 를 100 보다 높게 설정해도 kube-scheduler는 마치 100을 설정한 것처럼 작동한다.

값을 변경하려면, kube-scheduler 구성 파일을 편집한 다음 스케줄러를 재시작한다. 대부분의 경우, 구성 파일은 /etc/kubernetes/config/kube-scheduler.yaml 에서 찾을 수 있다.

이를 변경한 후에 다음을 실행해서

kubectl get pods -n kube-system | grep kube-scheduler

kube-scheduler 컴포넌트가 정상인지 확인할 수 있다.

노드 스코어링(scoring) 임계값

스케줄링 성능을 향상시키기 위해 kube-scheduler는 실행 가능한 노드가 충분히 발견되면 이를 찾는 것을 중단할 수 있다. 큰 규모의 클러스터에서는 모든 노드를 고려하는 고지식한 접근 방식에 비해 시간이 절약된다.

클러스터에 있는 모든 노드의 정수 백분율로 충분한 노두의 수에 대한 임계값을 지정한다. kube-scheduler는 이 값을 노드의 정수 값(숫자)로 변환 한다. 스케줄링 중에 kube-scheduler가 구성된 비율을 초과 할만큼 충분히 실행 가능한 노드를 식별한 경우, kube-scheduler는 더 실행 가능한 노드를 찾는 검색을 중지하고 스코어링 단계를 진행한다.

스케줄러가 노드 탐색을 반복(iterate)하는 방법 은 이 프로세스를 자세히 설명한다.

기본 임계값

임계값을 지정하지 않으면 쿠버네티스는 100 노드 클러스터인 경우 50%, 5000 노드 클러스터인 경우 10%를 산출하는 선형 공식을 사용하여 수치를 계산한다. 자동 값의 하한선은 5% 이다.

즉, percentageOfNodesToScore 를 명시적으로 5보다 작게 설정하지 않은 경우 클러스터가 아무리 크더라도 kube-scheduler는 항상 클러스터의 최소 5%를 스코어링을 한다.

스케줄러가 클러스터의 모든 노드에 스코어링을 하려면 percentageOfNodesToScore 를 100으로 설정 한다.

예시

아래는 percentageOfNodesToScore를 50%로 설정하는 구성 예시이다.

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

percentageOfNodesToScore: 50

percentageOfNodesToScore 튜닝

percentageOfNodesToScore는 1과 100 사이의 값이어야 하며 기본값은 클러스터 크기에 따라 계산된다. 또한 50 노드로 하드 코딩된 최솟값도 있다.

이 값을 세팅할 때 중요하고 자세한 사항은, 클러스터에서 적은 수의 노드에 대해서만 적합성을 확인하면, 주어진 파드에 대해서 일부 노드의 점수는 측정이되지 않는다는 것이다. 결과적으로, 주어진 파드를 실행하는데 가장 높은 점수를 가질 가능성이 있는 노드가 점수 측정 단계로 조차 넘어가지 않을 수 있다. 이것은 파드의 이상적인 배치보다 낮은 결과를 초래할 것이다.

percentageOfNodesToScore 를 매우 낮게 설정해서 kube-scheduler가 파드 배치 결정을 잘못 내리지 않도록 해야 한다. 스케줄러의 처리량에 대해 애플리케이션이 중요하고 노드 점수가 중요하지 않은 경우가 아니라면 백분율을 10% 미만으로 설정하지 말아야 한다. 즉, 가능한 한 모든 노드에서 파드를 실행하는 것이 좋다.

스케줄러가 노드 탐색을 반복(iterate)하는 방법

이 섹션은 이 특징의 상세한 내부 방식을 이해하고 싶은 사람들을 위해 작성되었다.

클러스터의 모든 노드가 파드 실행 대상으로 고려되어 공정한 기회를 가지도록, 스케줄러는 라운드 로빈(round robin) 방식으로 모든 노드에 대해서 탐색을 반복한다. 모든 노드가 배열에 나열되어 있다고 생각해보자. 스케줄러는 배열의 시작부터 시작하여 percentageOfNodesToScore에 명시된 충분한 수의 노드를 찾을 때까지 적합성을 확인한다. 그 다음 파드에 대해서는, 스케줄러가 이전 파드를 위한 노드 적합성 확인이 마무리된 지점인 노드 배열의 마지막 포인트부터 확인을 재개한다.

만약 노드들이 다중의 영역(zone)에 있다면, 다른 영역에 있는 노드들이 적합성 확인의 대상이 되도록 스케줄러는 다양한 영역에 있는 노드에 대해서 탐색을 반복한다. 예제로, 2개의 영역에 있는 6개의 노드를 생각해보자.

영역 1: 노드 1, 노드 2, 노드 3, 노드 4
영역 2: 노드 5, 노드 6

스케줄러는 노드의 적합성 평가를 다음의 순서로 실행한다.

노드 1, 노드 5, 노드 2, 노드 6, 노드 3, 노드 4

모든 노드를 검토한 후, 노드 1로 돌아간다.

다음 내용

3.10 - 정책

리소스의 그룹에 적용되도록 구성할 수 있는 정책

3.10.1 - 리밋 레인지(Limit Range)

기본적으로 컨테이너는 쿠버네티스 클러스터에서 무제한 컴퓨팅 리소스로 실행된다. 리소스 쿼터을 사용하면 클러스터 관리자는 네임스페이스별로 리소스 사용과 생성을 제한할 수 있다. 네임스페이스 내에서 파드나 컨테이너는 네임스페이스의 리소스 쿼터에 정의된 만큼의 CPU와 메모리를 사용할 수 있다. 하나의 파드 또는 컨테이너가 사용 가능한 모든 리소스를 독점할 수 있다는 우려가 있다. 리밋레인지는 네임스페이스에서 리소스 할당(파드 또는 컨테이너)을 제한하는 정책이다.

리밋레인지 는 다음과 같은 제약 조건을 제공한다.

  • 네임스페이스에서 파드 또는 컨테이너별 최소 및 최대 컴퓨팅 리소스 사용량을 지정한다.
  • 네임스페이스에서 스토리지클래스별 최소 및 최대 스토리지 요청을 지정한다.
  • 네임스페이스에서 리소스에 대한 요청과 제한 사이의 비율을 지정한다.
  • 네임스페이스에서 컴퓨팅 리소스에 대한 기본 요청/제한을 설정하고 런타임에 있는 컨테이너에 자동으로 설정한다.

리밋레인지 활성화

쿠버네티스 1.10 버전부터 리밋레인지 지원이 기본적으로 활성화되었다.

해당 네임스페이스에 리밋레인지 오브젝트가 있는 경우 특정 네임스페이스에 리밋레인지가 지정된다.

리밋레인지 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

리밋 레인지 개요

  • 관리자는 하나의 네임스페이스에 하나의 리밋레인지를 만든다.
  • 사용자는 네임스페이스에서 파드, 컨테이너 및 퍼시스턴트볼륨클레임과 같은 리소스를 생성한다.
  • LimitRanger 어드미션 컨트롤러는 컴퓨팅 리소스 요청 사항을 설정하지 않은 모든 파드와 컨테이너에 대한 기본값과 제한을 지정하고 네임스페이스의 리밋레인지에 정의된 리소스의 최소, 최대 및 비율을 초과하지 않도록 사용량을 추적한다.
  • 리밋레인지 제약 조건을 위반하는 리소스(파드, 컨테이너, 퍼시스턴트볼륨클레임)를 생성하거나 업데이트하는 경우 HTTP 상태 코드 403 FORBIDDEN 및 위반된 제약 조건을 설명하는 메시지와 함께 API 서버에 대한 요청이 실패한다.
  • cpu, memory와 같은 컴퓨팅 리소스의 네임스페이스에서 리밋레인지가 활성화된 경우 사용자는 해당 값에 대한 요청 또는 제한을 지정해야 한다. 그렇지 않으면 시스템에서 파드 생성이 거부될 수 있다.
  • 리밋레인지 유효성 검사는 파드 실행 단계가 아닌 파드 어드미션 단계에서만 발생한다.

리밋 레인지를 사용하여 생성할 수 있는 정책의 예는 다음과 같다.

  • 용량이 8GiB RAM과 16 코어인 2 노드 클러스터에서 네임스페이스의 파드를 제한하여 CPU의 최대 제한이 500m인 CPU 100m를 요청하고 메모리의 최대 제한이 600M인 메모리 200Mi를 요청하라.
  • 스펙에 CPU 및 메모리 요청없이 시작된 컨테이너에 대해 기본 CPU 제한 및 요청을 150m로, 메모리 기본 요청을 300Mi로 정의하라.

네임스페이스의 총 제한이 파드/컨테이너의 제한 합보다 작은 경우 리소스에 대한 경합이 있을 수 있다. 이 경우 컨테이너 또는 파드가 생성되지 않는다.

경합이나 리밋레인지 변경은 이미 생성된 리소스에 영향을 미치지 않는다.

다음 내용

자세한 내용은 LimitRanger 디자인 문서를 참조한다.

제한의 사용에 대한 예시는 다음을 참조한다.

3.10.2 - 리소스 쿼터

여러 사용자나 팀이 정해진 수의 노드로 클러스터를 공유할 때 한 팀이 공정하게 분배된 리소스보다 많은 리소스를 사용할 수 있다는 우려가 있다.

리소스 쿼터는 관리자가 이 문제를 해결하기 위한 도구이다.

ResourceQuota 오브젝트로 정의된 리소스 쿼터는 네임스페이스별 총 리소스 사용을 제한하는 제약 조건을 제공한다. 유형별로 네임스페이스에서 만들 수 있는 오브젝트 수와 해당 네임스페이스의 리소스가 사용할 수 있는 총 컴퓨트 리소스의 양을 제한할 수 있다.

리소스 쿼터는 다음과 같이 작동한다.

  • 다른 팀은 다른 네임스페이스에서 작동한다. 현재 이것은 자발적이지만 ACL을 통해 이 필수 사항을 적용하기 위한 지원이 계획되어 있다.

  • 관리자는 각 네임스페이스에 대해 하나의 리소스쿼터를 생성한다.

  • 사용자는 네임스페이스에서 리소스(파드, 서비스 등)를 생성하고 쿼터 시스템은 사용량을 추적하여 리소스쿼터에 정의된 하드(hard) 리소스 제한을 초과하지 않도록 한다.

  • 리소스를 생성하거나 업데이트할 때 쿼터 제약 조건을 위반하면 위반된 제약 조건을 설명하는 메시지와 함께 HTTP 상태 코드 403 FORBIDDEN으로 요청이 실패한다.

  • cpu, memory와 같은 컴퓨트 리소스에 대해 네임스페이스에서 쿼터가 활성화된 경우 사용자는 해당값에 대한 요청 또는 제한을 지정해야 한다. 그렇지 않으면 쿼터 시스템이 파드 생성을 거부할 수 있다. 힌트: 컴퓨트 리소스 요구 사항이 없는 파드를 기본값으로 설정하려면 LimitRanger 어드미션 컨트롤러를 사용하자.

    이 문제를 회피하는 방법에 대한 예제는 연습을 참고하길 바란다.

리소스쿼터 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

네임스페이스와 쿼터를 사용하여 만들 수 있는 정책의 예는 다음과 같다.

  • 용량이 32GiB RAM, 16 코어인 클러스터에서 A 팀이 20GiB 및 10 코어를 사용하고 B 팀은 10GiB 및 4 코어를 사용하게 하고 2GiB 및 2 코어를 향후 할당을 위해 보유하도록 한다.
  • "testing" 네임스페이스를 1 코어 및 1GiB RAM을 사용하도록 제한한다. "production" 네임스페이스에는 원하는 양을 사용하도록 한다.

클러스터의 총 용량이 네임스페이스의 쿼터 합보다 작은 경우 리소스에 대한 경합이 있을 수 있다. 이것은 선착순으로 처리된다.

경합이나 쿼터 변경은 이미 생성된 리소스에 영향을 미치지 않는다.

리소스 쿼터 활성화

많은 쿠버네티스 배포판에 기본적으로 리소스 쿼터 지원이 활성화되어 있다. API 서버 --enable-admission-plugins= 플래그의 인수 중 하나로 ResourceQuota가 있는 경우 활성화된다.

해당 네임스페이스에 리소스쿼터가 있는 경우 특정 네임스페이스에 리소스 쿼터가 적용된다.

컴퓨트 리소스 쿼터

지정된 네임스페이스에서 요청할 수 있는 총 컴퓨트 리소스 합을 제한할 수 있다.

다음과 같은 리소스 유형이 지원된다.

리소스 이름 설명
limits.cpu 터미널이 아닌 상태의 모든 파드에서 CPU 제한의 합은 이 값을 초과할 수 없음.
limits.memory 터미널이 아닌 상태의 모든 파드에서 메모리 제한의 합은 이 값을 초과할 수 없음.
requests.cpu 터미널이 아닌 상태의 모든 파드에서 CPU 요청의 합은 이 값을 초과할 수 없음.
requests.memory 터미널이 아닌 상태의 모든 파드에서 메모리 요청의 합은 이 값을 초과할 수 없음.
hugepages-<size> 터미널 상태가 아닌 모든 파드에 걸쳐서, 지정된 사이즈의 휴즈 페이지 요청은 이 값을 초과하지 못함.
cpu requests.cpu 와 같음.
memory requests.memory 와 같음.

확장된 리소스에 대한 리소스 쿼터

위에서 언급한 리소스 외에도 릴리스 1.10에서는 확장된 리소스에 대한 쿼터 지원이 추가되었다.

확장된 리소스에는 오버커밋(overcommit)이 허용되지 않으므로 하나의 쿼터에서 동일한 확장된 리소스에 대한 requestslimits을 모두 지정하는 것은 의미가 없다. 따라서 확장된 리소스의 경우 지금은 접두사 requests.이 있는 쿼터 항목만 허용된다.

예를 들어, 리소스 이름이 nvidia.com/gpu이고 네임스페이스에서 요청된 총 GPU 수를 4개로 제한하려는 경우, GPU 리소스를 다음과 같이 쿼터를 정의할 수 있다.

  • requests.nvidia.com/gpu: 4

자세한 내용은 쿼터 보기 및 설정을 참고하길 바란다.

스토리지 리소스 쿼터

지정된 네임스페이스에서 요청할 수 있는 총 스토리지 리소스 합을 제한할 수 있다.

또한 연관된 ​​스토리지 클래스를 기반으로 스토리지 리소스 사용을 제한할 수 있다.

리소스 이름 설명
requests.storage 모든 퍼시스턴트 볼륨 클레임에서 스토리지 요청의 합은 이 값을 초과할 수 없음
persistentvolumeclaims 네임스페이스에 존재할 수 있는 총 퍼시스턴트 볼륨 클레임
<storage-class-name>.storageclass.storage.k8s.io/requests.storage storage-class-name과 관련된 모든 퍼시스턴트 볼륨 클레임에서 스토리지 요청의 합은 이 값을 초과할 수 없음
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims <storage-class-name> 과 관련된 모든 퍼시스턴트 볼륨 클레임에서 네임스페이스에 존재할 수 있는 총 퍼시스턴트 볼륨 클레임

예를 들어, 운영자가 bronze 스토리지 클래스와 별도로 gold 스토리지 클래스를 사용하여 스토리지에 쿼터를 지정하려는 경우 운영자는 다음과 같이 쿼터를 정의할 수 있다.

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

릴리스 1.8에서는 로컬 임시 스토리지에 대한 쿼터 지원이 알파 기능으로 추가되었다.

리소스 이름 설명
requests.ephemeral-storage 네임스페이스의 모든 파드에서 로컬 임시 스토리지 요청의 합은 이 값을 초과할 수 없음.
limits.ephemeral-storage 네임스페이스의 모든 파드에서 로컬 임시 스토리지 제한의 합은 이 값을 초과할 수 없음.
ephemeral-storage requests.ephemeral-storage 와 같음.

오브젝트 수 쿼터

다음 구문을 사용하여 모든 표준 네임스페이스 처리된(namespaced) 리소스 유형에 대한 특정 리소스 전체 수에 대하여 쿼터를 지정할 수 있다.

  • 코어 그룹이 아닌(non-core) 리소스를 위한 count/<resource>.<group>
  • 코어 그룹의 리소스를 위한 count/<resource>

다음은 사용자가 오브젝트 수 쿼터 아래에 배치하려는 리소스 셋의 예이다.

  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/replicationcontrollers
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

사용자 정의 리소스를 위해 동일한 구문을 사용할 수 있다. 예를 들어 example.com API 그룹에서 widgets 사용자 정의 리소스에 대한 쿼터를 생성하려면 count/widgets.example.com을 사용한다.

count/* 리소스 쿼터를 사용할 때 서버 스토리지 영역에 있다면 오브젝트는 쿼터에 대해 과금된다. 이러한 유형의 쿼터는 스토리지 리소스 고갈을 방지하는 데 유용하다. 예를 들어, 크기가 큰 서버에서 시크릿 수에 쿼터를 지정할 수 있다. 클러스터에 시크릿이 너무 많으면 실제로 서버와 컨트롤러가 시작되지 않을 수 있다. 잘못 구성된 크론 잡으로부터의 보호를 위해 잡의 쿼터를 설정할 수 있다. 네임스페이스 내에서 너무 많은 잡을 생성하는 크론 잡은 서비스 거부를 유발할 수 있다.

또한 제한된 리소스 셋에 대해서 일반 오브젝트 수(generic object count) 쿼터를 적용하는 것도 가능하다. 다음 유형이 지원된다.

리소스 이름 설명
configmaps 네임스페이스에 존재할 수 있는 총 컨피그맵 수
persistentvolumeclaims 네임스페이스에 존재할 수 있는 총 퍼시스턴트 볼륨 클레임
pods 네임스페이스에 존재할 수 있는 터미널이 아닌 상태의 파드의 총 수. .status.phase in (Failed, Succeeded)가 true인 경우 파드는 터미널 상태임
replicationcontrollers 네임스페이스에 존재할 수 있는 총 레플리케이션컨트롤러 수
resourcequotas 네임스페이스에 존재할 수 있는 총 리소스쿼터 수
services 네임스페이스에 존재할 수 있는 총 서비스 수
services.loadbalancers 네임스페이스에 존재할 수 있는 LoadBalancer 유형의 총 서비스 수
services.nodeports 네임스페이스에 존재할 수 있는 NodePort 유형의 총 서비스 수
secrets 네임스페이스에 존재할 수 있는 총 시크릿 수

예를 들어, pods 쿼터는 터미널이 아닌 단일 네임스페이스에서 생성된 pods 수를 계산하고 최댓값을 적용한다. 사용자가 작은 파드를 많이 생성하여 클러스터의 파드 IP 공급이 고갈되는 경우를 피하기 위해 네임스페이스에 pods 쿼터를 설정할 수 있다.

쿼터 범위

각 쿼터에는 연결된 scopes 셋이 있을 수 있다. 쿼터는 열거된 범위의 교차 부분과 일치하는 경우에만 리소스 사용량을 측정한다.

범위가 쿼터에 추가되면 해당 범위와 관련된 리소스를 지원하는 리소스 수가 제한된다. 허용된 셋 이외의 쿼터에 지정된 리소스는 유효성 검사 오류가 발생한다.

범위 설명
Terminating .spec.activeDeadlineSeconds >= 0에 일치하는 파드
NotTerminating .spec.activeDeadlineSeconds is nil에 일치하는 파드
BestEffort 최상의 서비스 품질을 제공하는 파드
NotBestEffort 서비스 품질이 나쁜 파드
PriorityClass 지정된 프라이어리티클래스를 참조하여 일치하는 파드.
CrossNamespacePodAffinity 크로스-네임스페이스 파드 [(안티)어피니티 용어]가 있는 파드

BestEffort 범위는 다음의 리소스를 추적하도록 쿼터를 제한한다.

  • pods

Terminating, NotTerminating, NotBestEffortPriorityClass 범위는 쿼터를 제한하여 다음의 리소스를 추적한다.

  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

TerminatingNotTerminating 범위를 동일한 쿼터 내에 모두 명시하지는 못하며, 마찬가지로 BestEffortNotBestEffort 범위도 동일한 쿼터 내에서 모두 명시하지는 못한다.

scopeSelectoroperator 필드에 다음의 값을 지원한다.

  • In
  • NotIn
  • Exists
  • DoesNotExist

scopeSelector 를 정의할 때, scopeName 으로 다음의 값 중 하나를 사용하는 경우, operatorExists 이어야 한다.

  • Terminating
  • NotTerminating
  • BestEffort
  • NotBestEffort

만약 operatorIn 또는 NotIn 인 경우, values 필드는 적어도 하나의 값은 가져야 한다. 예를 들면 다음과 같다.

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

만약 operatorExists 또는 DoesNotExist 이라면, values 필드는 명시되면 안된다.

PriorityClass별 리소스 쿼터

FEATURE STATE: Kubernetes v1.17 [stable]

특정 우선 순위로 파드를 생성할 수 있다. 쿼터 스펙의 scopeSelector 필드를 사용하여 파드의 우선 순위에 따라 파드의 시스템 리소스 사용을 제어할 수 있다.

쿼터 스펙의 scopeSelector가 파드를 선택한 경우에만 쿼터가 일치하고 사용된다.

scopeSelector 필드를 사용하여 우선 순위 클래스의 쿼터 범위를 지정하면, 쿼터 오브젝트는 다음의 리소스만 추적하도록 제한된다.

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

이 예에서는 쿼터 오브젝트를 생성하여 특정 우선 순위의 파드와 일치시킨다. 예제는 다음과 같이 작동한다.

  • 클러스터의 파드는 "low(낮음)", "medium(중간)", "high(높음)"의 세 가지 우선 순위 클래스 중 하나를 가진다.
  • 각 우선 순위마다 하나의 쿼터 오브젝트가 생성된다.

다음 YAML을 quota.yml 파일에 저장한다.

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["low"]

kubectl create를 사용하여 YAML을 적용한다.

kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

kubectl describe quota를 사용하여 Used 쿼터가 0인지 확인하자.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

우선 순위가 "high"인 파드를 생성한다. 다음 YAML을 high-priority-pod.yml 파일에 저장한다.

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

kubectl create로 적용하자.

kubectl create -f ./high-priority-pod.yml

"high" 우선 순위 쿼터가 적용된 pods-high에 대한 "Used" 통계가 변경되었고 다른 두 쿼터는 변경되지 않았는지 확인한다.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

네임스페이스 간 파드 어피니티 쿼터

FEATURE STATE: Kubernetes v1.22 [beta]

오퍼레이터는 네임스페이스를 교차하는 어피니티가 있는 파드를 가질 수 있는 네임스페이스를 제한하기 위해 CrossNamespacePodAffinity 쿼터 범위를 사용할 수 있다. 특히, 파드 어피니티 용어의 namespaces 또는 namespaceSelector 필드를 설정할 수 있는 파드를 제어한다.

안티-어피니티 제약 조건이 있는 파드는 장애 도메인에서 다른 모든 네임스페이스의 파드가 예약되지 않도록 차단할 수 있으므로 사용자가 네임스페이스 간 어피니티 용어를 사용하지 못하도록 하는 것이 바람직할 수 있다.

이 범위 오퍼레이터를 사용하면 CrossNamespaceAffinity 범위와 하드(hard) 제한이 0인 네임스페이스에 리소스 쿼터 오브젝트를 생성하여 특정 네임스페이스(아래 예에서 foo-ns)가 네임스페이스 간 파드 어피니티를 사용하는 파드를 사용하지 못하도록 방지할 수 있다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespaceAffinity

오퍼레이터가 기본적으로 namespacesnamespaceSelector 사용을 허용하지 않고, 특정 네임스페이스에만 허용하려는 경우, kube-apiserver 플래그 --admission-control-config-file를 다음의 구성 파일의 경로로 설정하여 CrossNamespaceAffinity 를 제한된 리소스로 구성할 수 있다.

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: CrossNamespaceAffinity

위의 구성을 사용하면, 파드는 생성된 네임스페이스에 CrossNamespaceAffinity 범위가 있는 리소스 쿼터 오브젝트가 있고, 해당 필드를 사용하는 파드 수보다 크거나 같은 하드 제한이 있는 경우에만 파드 어피니티에서 namespacesnamespaceSelector 를 사용할 수 있다.

이 기능은 베타이며 기본으로 활성화되어 있다. kube-apiserver 및 kube-scheduler 모두에서 기능 게이트 PodAffinityNamespaceSelector 를 사용하여 비활성화할 수 있다.

요청과 제한의 비교

컴퓨트 리소스를 할당할 때 각 컨테이너는 CPU 또는 메모리에 대한 요청과 제한값을 지정할 수 있다. 쿼터는 값에 대한 쿼터를 지정하도록 구성할 수 있다.

쿼터에 requests.cpurequests.memory에 지정된 값이 있으면 들어오는 모든 컨테이너가 해당 리소스에 대한 명시적인 요청을 지정해야 한다. 쿼터에 limits.cpulimits.memory에 지정된 값이 있으면 들어오는 모든 컨테이너가 해당 리소스에 대한 명시적인 제한을 지정해야 한다.

쿼터 보기 및 설정

Kubectl은 쿼터 생성, 업데이트 및 보기를 지원한다.

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

Kubectl은 count/<resource>.<group> 구문을 사용하여 모든 표준 네임스페이스 리소스에 대한 오브젝트 수 쿼터를 지원한다.

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

쿼터 및 클러스터 용량

리소스쿼터는 클러스터 용량과 무관하다. 그것들은 절대 단위로 표현된다. 따라서 클러스터에 노드를 추가해도 각 네임스페이스에 더 많은 리소스를 사용할 수 있는 기능이 자동으로 부여되지는 않는다.

가끔 다음과 같은 보다 복잡한 정책이 필요할 수 있다.

  • 여러 팀으로 전체 클러스터 리소스를 비례적으로 나눈다.
  • 각 테넌트가 필요에 따라 리소스 사용량을 늘릴 수 있지만, 실수로 리소스가 고갈되는 것을 막기 위한 충분한 제한이 있다.
  • 하나의 네임스페이스에서 요구를 감지하고 노드를 추가하며 쿼터를 늘린다.

이러한 정책은 쿼터 사용을 감시하고 다른 신호에 따라 각 네임스페이스의 쿼터 하드 제한을 조정하는 "컨트롤러"를 작성하여 ResourceQuotas를 구성 요소로 사용하여 구현할 수 있다.

리소스 쿼터는 통합된 클러스터 리소스를 분할하지만 노드에 대한 제한은 없다. 여러 네임스페이스의 파드가 동일한 노드에서 실행될 수 있다.

기본적으로 우선 순위 클래스 소비 제한

파드가 특정 우선 순위, 예를 들어 일치하는 쿼터 오브젝트가 존재하는 경우에만 "cluster-services"가 네임스페이스에 허용되어야 한다.

이 메커니즘을 통해 운영자는 특정 우선 순위가 높은 클래스의 사용을 제한된 수의 네임스페이스로 제한할 수 있으며 모든 네임스페이스가 기본적으로 이러한 우선 순위 클래스를 사용할 수 있는 것은 아니다.

이를 적용하려면 kube-apiserver 플래그 --admission-control-config-file 을 사용하여 다음 구성 파일의 경로를 전달해야 한다.

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

그리고, kube-system 네임스페이스에 리소스 쿼터 오브젝트를 생성한다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

이 경우, 파드 생성은 다음의 조건을 만족해야 허용될 것이다.

  1. 파드의 priorityClassName 가 명시되지 않음.
  2. 파드의 priorityClassNamecluster-services 이외의 다른 값으로 명시됨.
  3. 파드의 priorityClassNamecluster-services 로 설정되고, 파드가 kube-system 네임스페이스에 생성되었으며 리소스 쿼터 검증을 통과함.

파드 생성 요청은 priorityClassNamecluster-services 로 명시되고 kube-system 이외의 다른 네임스페이스에 생성되는 경우, 거절된다.

다음 내용

3.10.3 - 파드 시큐리티 폴리시

FEATURE STATE: Kubernetes v1.21 [deprecated]

파드시큐리티폴리시(PodSecurityPolicy)는 쿠버네티스 v1.21부터 더이상 사용되지 않으며, v1.25에서 제거된다. 사용 중단에 대한 상세 사항은 파드시큐리티폴리시 사용 중단: 과거, 현재, 그리고 미래를 참조한다.

파드 시큐리티 폴리시를 사용하면 파드 생성 및 업데이트에 대한 세분화된 권한을 부여할 수 있다.

파드 시큐리티 폴리시란?

Pod Security Policy 는 파드 명세의 보안 관련 측면을 제어하는 ​​클러스터-레벨의 리소스이다. 파드시큐리티폴리시 오브젝트는 관련 필드에 대한 기본값뿐만 아니라 시스템에 적용하기 위해 파드가 실행해야만 하는 조건 셋을 정의한다. 관리자는 다음을 제어할 수 있다.

제어 측면 필드 이름
특권을 가진(privileged) 컨테이너의 실행 privileged
호스트 네임스페이스의 사용 hostPID, hostIPC
호스트 네트워킹과 포트의 사용 hostNetwork, hostPorts
볼륨 유형의 사용 volumes
호스트 파일시스템의 사용 allowedHostPaths
특정 FlexVolume 드라이버의 허용 allowedFlexVolumes
파드 볼륨을 소유한 FSGroup 할당 fsGroup
읽기 전용 루트 파일시스템 사용 필요 readOnlyRootFilesystem
컨테이너의 사용자 및 그룹 ID runAsUser, runAsGroup, supplementalGroups
루트 특권으로의 에스컬레이션 제한 allowPrivilegeEscalation, defaultAllowPrivilegeEscalation
리눅스 기능 defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities
컨테이너의 SELinux 컨텍스트 seLinux
컨테이너에 허용된 Proc 마운트 유형 allowedProcMountTypes
컨테이너가 사용하는 AppArmor 프로파일 어노테이션
컨테이너가 사용하는 seccomp 프로파일 어노테이션
컨테이너가 사용하는 sysctl 프로파일 forbiddenSysctls,allowedUnsafeSysctls

파드 시큐리티 폴리시 활성화

파드 시큐리티 폴리시 제어는 선택 사항인 어드미션 컨트롤러로 구현된다. 어드미션 컨트롤러를 활성화하면 파드시큐리티폴리시가 적용되지만, 정책을 승인하지 않고 활성화하면 클러스터에 파드가 생성되지 않는다.

파드 시큐리티 폴리시 API(policy/v1beta1/podsecuritypolicy)는 어드미션 컨트롤러와 독립적으로 활성화되므로 기존 클러스터의 경우 어드미션 컨트롤러를 활성화하기 전에 정책을 추가하고 권한을 부여하는 것이 좋다.

정책 승인

파드시큐리티폴리시 리소스가 생성되면 아무 것도 수행하지 않는다. 이를 사용하려면 요청 사용자 또는 대상 파드의 서비스 어카운트는 정책에서 use 동사를 허용하여 정책을 사용할 권한이 있어야 한다.

대부분의 쿠버네티스 파드는 사용자가 직접 만들지 않는다. 대신 일반적으로 컨트롤러 관리자를 통해 디플로이먼트, 레플리카셋, 또는 기타 템플릿 컨트롤러의 일부로 간접적으로 생성된다. 컨트롤러에 정책에 대한 접근 권한을 부여하면 해당 컨트롤러에 의해 생성된 모든 파드에 대한 접근 권한이 부여되므로 정책을 승인하는 기본 방법은 파드의 서비스 어카운트에 대한 접근 권한을 부여하는 것이다( 참고).

RBAC을 통한 방법

RBAC은 표준 쿠버네티스 권한 부여 모드이며, 정책 사용 권한을 부여하는 데 쉽게 사용할 수 있다.

먼저, Role 또는 ClusterRole은 원하는 정책을 use 하려면 접근 권한을 부여해야 한다. 접근 권한을 부여하는 규칙은 다음과 같다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: <role name>
rules:
- apiGroups: ['policy']
  resources: ['podsecuritypolicies']
  verbs:     ['use']
  resourceNames:
  - <list of policies to authorize>

그런 다음 (Cluster)Role이 승인된 사용자에게 바인딩된다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: <binding name>
roleRef:
  kind: ClusterRole
  name: <role name>
  apiGroup: rbac.authorization.k8s.io
subjects:
# 네임스페이스의 모든 서비스 어카운트 승인(권장):
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:serviceaccounts:<authorized namespace>
# 특정 서비스 어카운트 승인(권장하지 않음):
- kind: ServiceAccount
  name: <authorized service account name>
  namespace: <authorized pod namespace>
# 특정 사용자 승인(권장하지 않음):
- kind: User
  apiGroup: rbac.authorization.k8s.io
  name: <authorized user name>

RoleBinding(ClusterRoleBinding 아님)을 사용하는 경우, 바인딩과 동일한 네임스페이스에서 실행되는 파드에 대해서만 사용 권한을 부여한다. 네임스페이스에서 실행되는 모든 파드에 접근 권한을 부여하기 위해 시스템 그룹과 쌍을 이룰 수 있다.

# 네임스페이스의 모든 서비스 어카운트 승인:
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:serviceaccounts
# 또는 동일하게, 네임스페이스의 모든 승인된 사용자에게 사용 권한 부여
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:authenticated

RBAC 바인딩에 대한 자세한 예는, 역할 바인딩 예제를 참고한다. 파드시큐리티폴리시 인증에 대한 전체 예제는 아래를 참고한다.

추천 예제

파드시큐리티폴리시는 새롭고 간결해진 PodSecurity 어드미션 컨트롤러로 대체되고 있다. 이 변경에 대한 상세사항은 파드시큐리티폴리시 사용 중단: 과거, 현재, 그리고 미래를 참조한다. 다음 가이드라인을 참조하여 파드시큐리티폴리시를 새로운 어드미션 컨트롤러로 쉽게 전환할 수 있다.

  1. 파드시큐리티폴리시를 파드 보안 표준에 의해 정의된 폴리시로 한정한다.

  2. system:serviceaccounts:<namespace> (여기서 <namespace>는 타겟 네임스페이스) 그룹을 사용하여 파드시큐리티폴리시를 전체 네임스페이스에만 바인드한다. 예시는 다음과 같다.

    apiVersion: rbac.authorization.k8s.io/v1
    # 이 클러스터롤바인딩(ClusterRoleBinding)을 통해 "development" 네임스페이스의 모든 파드가 기준 파드시큐리티폴리시(PSP)를 사용할 수 있다.
    kind: ClusterRoleBinding
    metadata:
      name: psp-baseline-namespaces
    roleRef:
      kind: ClusterRole
      name: psp-baseline
      apiGroup: rbac.authorization.k8s.io
    subjects:
    - kind: Group
      name: system:serviceaccounts:development
      apiGroup: rbac.authorization.k8s.io
    - kind: Group
      name: system:serviceaccounts:canary
      apiGroup: rbac.authorization.k8s.io
    

문제 해결

  • 컨트롤러 관리자는 보안 API 포트에 대해 실행되어야 하며 수퍼유저 권한이 없어야 한다. API 서버 접근 제어에 대한 자세한 내용은 쿠버네티스 API에 대한 접근 제어를 참고하길 바란다. 컨트롤러 관리자가 신뢰할 수 있는 API 포트(localhost 리스너라고도 함)를 통해 연결된 경우, 요청이 인증 및 권한 부여 모듈을 우회하고, 모든 파드시큐리티폴리시 오브젝트가 허용되며 사용자는 특권을 가진 컨테이너를 만들 수 있는 권한을 부여할 수 있다.

컨트롤러 관리자 권한 구성에 대한 자세한 내용은 컨트롤러 역할을 참고하기 바란다.

정책 순서

파드 생성 및 업데이트를 제한할 뿐만 아니라 파드 시큐리티 폴리시를 사용하여 제어하는 ​​많은 필드에 기본값을 제공할 수도 있다. 여러 정책을 사용할 수 있는 경우 파드 시큐리티 폴리시 컨트롤러는 다음 기준에 따라 정책을 선택한다.

  1. 기본 설정을 변경하거나 파드를 변경하지 않고 파드를 있는 그대로 허용하는 파드시큐리티폴리시가 선호된다. 이러한 비-변이(non-mutating) 파드시큐리티폴리시의 순서는 중요하지 않다.
  2. 파드를 기본값으로 설정하거나 변경해야 하는 경우, 파드를 허용할 첫 번째 파드시큐리티폴리시 (이름순)가 선택된다.

예제

이 예에서는 파드시큐리티폴리시 어드미션 컨트롤러가 활성화된 클러스터가 실행 중이고 클러스터 관리자 권한이 있다고 가정한다.

설정

이 예제와 같이 네임스페이스와 서비스 어카운트를 설정한다. 이 서비스 어카운트를 사용하여 관리자가 아닌 사용자를 조정한다.

kubectl create namespace psp-example
kubectl create serviceaccount -n psp-example fake-user
kubectl create rolebinding -n psp-example fake-editor --clusterrole=edit --serviceaccount=psp-example:fake-user

어떤 사용자로 활동하고 있는지 명확하게 하고 입력 내용을 저장하려면 2개의 별칭(alias)을 만든다.

alias kubectl-admin='kubectl -n psp-example'
alias kubectl-user='kubectl --as=system:serviceaccount:psp-example:fake-user -n psp-example'

정책과 파드 생성

파일에서 예제 파드시큐리티폴리시 오브젝트를 정의한다. 이는 특권있는 파드를 만들지 못하게 하는 정책이다. 파드시큐리티폴리시 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: example
spec:
  privileged: false  # 특권을 가진 파드는 허용금지!
  # 나머지는 일부 필수 필드를 채운다.
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

그리고 kubectl로 생성한다.

kubectl-admin create -f example-psp.yaml

이제 권한이 없는 사용자로서 간단한 파드를 생성해보자.

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: pause
spec:
  containers:
    - name: pause
      image: k8s.gcr.io/pause
EOF

이것의 출력은 다음과 같을 것이다.

Error from server (Forbidden): error when creating "STDIN": pods "pause" is forbidden: unable to validate against any pod security policy: []

무슨 일이 일어났나? 파드시큐리티폴리시가 생성되었지만, 파드의 서비스 어카운트나 fake-user는 새 정책을 사용할 권한이 없다.

kubectl-user auth can-i use podsecuritypolicy/example
no

예제 정책에서 fake-user에게 use 동사를 부여하는 rolebinding을 생성한다.

kubectl-admin create role psp:unprivileged \
    --verb=use \
    --resource=podsecuritypolicy \
    --resource-name=example
role "psp:unprivileged" created

kubectl-admin create rolebinding fake-user:psp:unprivileged \
    --role=psp:unprivileged \
    --serviceaccount=psp-example:fake-user
rolebinding "fake-user:psp:unprivileged" created

kubectl-user auth can-i use podsecuritypolicy/example
yes

이제 파드 생성을 다시 시도하자.

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: pause
spec:
  containers:
    - name: pause
      image: k8s.gcr.io/pause
EOF

이것의 출력은 다음과 같을 것이다.

pod "pause" created

예상대로 작동한다! 그러나 특권있는 파드를 만들려는 시도는 여전히 거부되어야 한다.

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged
spec:
  containers:
    - name: pause
      image: k8s.gcr.io/pause
      securityContext:
        privileged: true
EOF

이것의 출력은 다음과 같을 것이다.

Error from server (Forbidden): error when creating "STDIN": pods "privileged" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]

계속 진행하기 전에 파드를 삭제하자.

kubectl-user delete pod pause

다른 파드를 실행

약간 다르게 다시 시도해보자.

kubectl-user create deployment pause --image=k8s.gcr.io/pause
deployment "pause" created

kubectl-user get pods
No resources found.

kubectl-user get events | head -n 2
LASTSEEN   FIRSTSEEN   COUNT     NAME              KIND         SUBOBJECT                TYPE      REASON                  SOURCE                                  MESSAGE
1m         2m          15        pause-7774d79b5   ReplicaSet                            Warning   FailedCreate            replicaset-controller                   Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request

무슨 일이 일어났나? 우리는 이미 fake-user에 대해 psp:unprivileged 역할을 바인딩했는데, Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request 오류가 발생하는 이유는 무엇인가? 그 답은 소스인 replicaset-controller에 있다. Fake-user가 디플로이먼트를 성공적으로 생성했지만(레플리카셋을 성공적으로 생성했음), 레플리카셋이 파드를 생성했을 때 podsecuritypolicy 예제를 사용할 권한이 없었다.

이 문제를 해결하려면 psp:unprivileged 역할을 파드의 서비스 어카운트에 대신 바인딩한다. 이 경우(지정하지 않았으므로) 서비스 어카운트는 default이다.

kubectl-admin create rolebinding default:psp:unprivileged \
    --role=psp:unprivileged \
    --serviceaccount=psp-example:default
rolebinding "default:psp:unprivileged" created

이제 다시 한번 해본다면 replicaset-controller가 파드를 성공적으로 생성할 것이다.

kubectl-user get pods --watch
NAME                    READY     STATUS    RESTARTS   AGE
pause-7774d79b5-qrgcb   0/1       Pending   0         1s
pause-7774d79b5-qrgcb   0/1       Pending   0         1s
pause-7774d79b5-qrgcb   0/1       ContainerCreating   0         1s
pause-7774d79b5-qrgcb   1/1       Running   0         2s

정리

네임스페이스를 삭제하여 대부분의 예제 리소스를 정리한다.

kubectl-admin delete ns psp-example
namespace "psp-example" deleted

PodSecurityPolicy 리소스는 네임스페이스에 포함되지 않으므로 별도로 정리해야 한다.

kubectl-admin delete psp example
podsecuritypolicy "example" deleted

정책 예제

다음은 파드 시큐리티 폴리시 어드미션 컨트롤러를 사용하지 않는 것과 동일하게 만들 수 있는 최소한의 제한 정책이다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

다음은 권한이 없는 사용자로서의 실행을 필요로 하고, 루트로의 에스컬레이션(escalation) 가능성을 차단하고, 여러 보안 메커니즘을 사용을 필요로 하는 제한적 정책의 예제이다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # 루트로의 에스컬레이션을 방지하는 데 필요하다.
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  # 기본 볼륨 유형을 허용한다.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # 클러스터 관리자에 의해 구성된 휘발성 CSI 드라이버와 퍼시스턴트볼륨(PersistentVolume)의 사용은 안전하다고 가정한다.
    - 'csi'
    - 'persistentVolumeClaim'
    - 'ephemeral'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # 루트 권한없이 컨테이너를 실행해야 한다.
    rule: 'MustRunAsNonRoot'
  seLinux:
    # 이 정책은 노드가 SELinux가 아닌 AppArmor를 사용한다고 가정한다.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # 루트 그룹을 추가하지 않는다.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # 루트 그룹을 추가하지 않는다.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

더 많은 예제는 파드 보안 표준을 본다.

정책 레퍼런스

특권을 가진

Privileged - 파드의 컨테이너가 특권 모드를 사용할 수 있는지 여부를 결정한다. 기본적으로 컨테이너는 호스트의 모든 장치에 접근할 수 없지만 "특권을 가진" 컨테이너는 호스트의 모든 장치에 접근할 수 있다. 이것은 컨테이너가 호스트에서 실행되는 프로세스와 거의 동일한 접근을 허용한다. 이것은 네트워크 스택 조작 및 장치 접근과 같은 리눅스 기능을 사용하려는 컨테이너에 유용하다.

호스트 네임스페이스

HostPID - 파드 컨테이너가 호스트 프로세스 ID 네임스페이스를 공유할 수 있는지 여부를 제어한다. ptrace와 함께 사용하면 컨테이너 외부로 권한을 에스컬레이션하는 데 사용할 수 있다(ptrace는 기본적으로 금지되어 있음).

HostIPC - 파드 컨테이너가 호스트 IPC 네임스페이스를 공유할 수 있는지 여부를 제어한다.

HostNetwork - 파드가 노드 네트워크 네임스페이스를 사용할 수 있는지 여부를 제어한다. 이렇게 하면 파드에 루프백 장치에 접근 권한을 주고, 서비스는 로컬호스트(localhost)를 리스닝할 수 있으며, 동일한 노드에 있는 다른 파드의 네트워크 활동을 스누핑(snoop)하는 데 사용할 수 있다.

HostPorts - 호스트 네트워크 네임스페이스에 허용되는 포트 범위의 목록을 제공한다. minmax를 포함하여 HostPortRange의 목록으로 정의된다. 기본값은 허용하는 호스트 포트 없음(no allowed host ports)이다.

볼륨 및 파일시스템

Volumes - 허용되는 볼륨 유형의 목록을 제공한다. 허용 가능한 값은 볼륨을 생성할 때 정의된 볼륨 소스에 따른다. 볼륨 유형의 전체 목록은 볼륨 유형들에서 참고한다. 또한 *를 사용하여 모든 볼륨 유형을 허용할 수 있다.

새 PSP에 허용되는 볼륨의 최소 권장 셋 은 다음과 같다.

  • 컨피그맵
  • 다운워드API
  • emptyDir
  • 퍼시스턴트볼륨클레임
  • 시크릿
  • 프로젝티드(projected)

FSGroup - 일부 볼륨에 적용되는 보충 그룹(supplemental group)을 제어한다.

  • MustRunAs - 하나 이상의 range를 지정해야 한다. 첫 번째 범위의 최솟값을 기본값으로 사용한다. 모든 범위에 대해 검증한다.
  • MayRunAs - 하나 이상의 range를 지정해야 한다. 기본값을 제공하지 않고 FSGroups을 설정하지 않은 상태로 둘 수 있다. FSGroups이 설정된 경우 모든 범위에 대해 유효성을 검사한다.
  • RunAsAny - 기본값은 제공되지 않는다. 어떠한 fsGroup ID의 지정도 허용한다.

AllowedHostPaths - hostPath 볼륨에서 사용할 수 있는 호스트 경로의 목록을 지정한다. 빈 목록은 사용되는 호스트 경로에 제한이 없음을 의미한다. 이는 단일 pathPrefix 필드가 있는 오브젝트 목록으로 정의되며, hostPath 볼륨은 허용된 접두사로 시작하는 경로를 마운트할 수 있으며 readOnly 필드는 읽기-전용으로 마운트 되어야 함을 나타낸다. 예를 들면 다음과 같습니다.

 allowedHostPaths:
   # 이 정책은 "/foo", "/foo/", "/foo/bar" 등을 허용하지만,
   # "/fool", "/etc/foo" 등은 허용하지 않는다.
   # "/foo/../" 는 절대 유효하지 않다.
   - pathPrefix: "/foo"
     readOnly: true # 읽기 전용 마운트만 허용

ReadOnlyRootFilesystem - 컨테이너는 읽기-전용 루트 파일시스템(즉, 쓰기 가능한 레이어 없음)으로 실행해야 한다.

FlexVolume 드라이버

flexvolume에서 사용할 수 있는 FlexVolume 드라이버의 목록을 지정한다. 빈 목록 또는 nil은 드라이버에 제한이 없음을 의미한다. volumes 필드에 flexVolume 볼륨 유형이 포함되어 있는지 확인한다. 그렇지 않으면 FlexVolume 드라이버가 허용되지 않는다.

예를 들면 다음과 같다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: allow-flex-volumes
spec:
  # ... 다른 스펙 필드
  volumes:
    - flexVolume
  allowedFlexVolumes:
    - driver: example/lvm
    - driver: example/cifs

사용자 및 그룹

RunAsUser - 컨테이너를 실행할 사용자 ID를 제어힌다.

  • MustRunAs - 하나 이상의 range를 지정해야 한다. 첫 번째 범위의 최솟값을 기본값으로 사용한다. 모든 범위에 대해 검증한다.
  • MustRunAsNonRoot - 파드가 0이 아닌 runAsUser로 제출되거나 이미지에 USER 지시문이 정의되어 있어야 한다(숫자 UID 사용). runAsNonRoot 또는 runAsUser 설정을 지정하지 않은 파드는 runAsNonRoot=true를 설정하도록 변경되므로 컨테이너에 0이 아닌 숫자가 정의된 USER 지시문이 필요하다. 기본값은 제공되지 않는다. 이 전략에서는 allowPrivilegeEscalation=false를 설정하는 것이 좋다.
  • RunAsAny - 기본값은 제공되지 않는다. 어떠한 runAsUser의 지정도 허용한다.

RunAsGroup - 컨테이너가 실행될 기본 그룹 ID를 제어한다.

  • MustRunAs - 하나 이상의 range를 지정해야 한다. 첫 번째 범위의 최솟값을 기본값으로 사용한다. 모든 범위에 대해 검증한다.
  • MayRunAs - RunAsGroup을 지정할 필요가 없다. 그러나 RunAsGroup을 지정하면 정의된 범위에 속해야 한다.
  • RunAsAny - 기본값은 제공되지 않는다. 어떠한 runAsGroup의 지정도 허용한다.

SupplementalGroups - 컨테이너가 추가할 그룹 ID를 제어한다.

  • MustRunAs - 하나 이상의 range를 지정해야 한다. 첫 번째 범위의 최솟값을 기본값으로 사용한다. 모든 범위에 대해 검증한다.
  • MayRunAs - 하나 이상의 range를 지정해야 한다. supplementalGroups에 기본값을 제공하지 않고 설정하지 않은 상태로 둘 수 있다. supplementalGroups가 설정된 경우 모든 범위에 대해 유효성을 검증한다.
  • RunAsAny - 기본값은 제공되지 않는다. 어떠한 supplementalGroups의 지정도 허용한다.

권한 에스컬레이션

이 옵션은 allowPrivilegeEscalation 컨테이너 옵션을 제어한다. 이 bool은 컨테이너 프로세스에서 no_new_privs 플래그가 설정되는지 여부를 직접 제어한다. 이 플래그는 setuid 바이너리가 유효 사용자 ID를 변경하지 못하게 하고 파일에 추가 기능을 활성화하지 못하게 한다(예: ping 도구 사용을 못하게 함). MustRunAsNonRoot를 효과적으로 강제하려면 이 동작이 필요하다.

AllowPrivilegeEscalation - 사용자가 컨테이너의 보안 컨텍스트를 allowPrivilegeEscalation=true로 설정할 수 있는지 여부를 게이트한다. 이 기본값은 setuid 바이너리를 중단하지 않도록 허용한다. 이를 false로 설정하면 컨테이너의 하위 프로세스가 상위 프로세스보다 더 많은 권한을 얻을 수 없다.

DefaultAllowPrivilegeEscalation - allowPrivilegeEscalation 옵션의 기본값을 설정한다. 이것이 없는 기본 동작은 setuid 바이너리를 중단하지 않도록 권한 에스컬레이션을 허용하는 것이다. 해당 동작이 필요하지 않은 경우 이 필드를 사용하여 기본적으로 허용하지 않도록 설정할 수 있지만 파드는 여전히 allowPrivilegeEscalation을 명시적으로 요청할 수 있다.

기능

리눅스 기능은 전통적으로 슈퍼유저와 관련된 권한을 보다 세밀하게 분류한다. 이러한 기능 중 일부는 권한 에스컬레이션 또는 컨테이너 분류에 사용될 수 있으며 파드시큐리티폴리시에 의해 제한될 수 있다. 리눅스 기능에 대한 자세한 내용은 기능(7)을 참고하길 바란다.

다음 필드는 대문자로 표기된 기능 이름 목록을 CAP_ 접두사 없이 가져온다.

AllowedCapabilities - 컨테이너에 추가될 수 있는 기능의 목록을 제공한다. 기본적인 기능 셋은 암시적으로 허용된다. 비어있는 셋은 기본 셋을 넘어서는 추가 기능이 추가되지 않는 것을 의미한다. *는 모든 기능을 허용하는 데 사용할 수 있다.

RequiredDropCapabilities - 컨테이너에서 삭제해야 하는 기능이다. 이러한 기능은 기본 셋에서 제거되며 추가해서는 안된다. RequiredDropCapabilities에 나열된 기능은 AllowedCapabilities 또는 DefaultAddCapabilities에 포함되지 않아야 한다.

DefaultAddCapabilities - 런타임 기본값 외에 기본적으로 컨테이너에 추가되는 기능이다. 도커 런타임을 사용할 때 기본 기능 목록은 도커 문서를 참고하길 바란다.

SELinux

  • MustRunAs - seLinuxOptions을 구성해야 한다. seLinuxOptions을 기본값으로 사용한다. seLinuxOptions에 대해 유효성을 검사한다.
  • RunAsAny - 기본값은 제공되지 않는다. 어떠한 seLinuxOptions의 지정도 허용한다.

AllowedProcMountTypes

allowedProcMountTypes는 허용된 ProcMountTypes의 목록이다. 비어 있거나 nil은 DefaultProcMountType만 사용할 수 있음을 나타낸다.

DefaultProcMount는 /proc의 읽기 전용 및 마스킹(masking)된 경로에 컨테이너 런타임 기본값을 사용한다. 대부분의 컨테이너 런타임은 특수 장치나 정보가 실수로 보안에 노출되지 않도록 /proc의 특정 경로를 마스킹한다. 이것은 문자열 Default로 표시된다.

유일하게 다른 ProcMountType은 UnmaskedProcMount로, 컨테이너 런타임의 기본 마스킹 동작을 무시하고 새로 작성된 /proc 컨테이너가 수정없이 그대로 유지되도록 한다. 이 문자열은 Unmasked로 표시된다.

AppArmor

파드시큐리티폴리시의 어노테이션을 통해 제어된다. AppArmor 문서를 참고하길 바란다.

Seccomp

쿠버네티스 v1.19부터 파드나 컨테이너의 securityContext 에서 seccompProfile 필드를 사용하여 seccomp 프로파일 사용을 제어할 수 있다. 이전 버전에서는, 파드에 어노테이션을 추가하여 seccomp를 제어했다. 두 버전에서 동일한 파드시큐리티폴리시를 사용하여 이러한 필드나 어노테이션이 적용되는 방식을 적용할 수 있다.

seccomp.security.alpha.kubernetes.io/defaultProfileName - 컨테이너에 적용할 기본 seccomp 프로파일을 지정하는 어노테이션이다. 가능한 값은 다음과 같다.

  • unconfined - 대안이 제공되지 않으면 Seccomp가 컨테이너 프로세스에 적용되지 않는다(쿠버네티스의 기본값임).
  • runtime/default - 기본 컨테이너 런타임 프로파일이 사용된다.
  • docker/default - 도커 기본 seccomp 프로파일이 사용된다. 쿠버네티스 1.11 부터 사용 중단(deprecated) 되었다. 대신 runtime/default 사용을 권장한다.
  • localhost/<path> - <seccomp_root>/<path>에 있는 노드에서 파일을 프로파일로 지정한다. 여기서 <seccomp_root>는 Kubelet의 --seccomp-profile-root 플래그를 통해 정의된다. --seccomp-profile-root 플래그가 정의되어 있지 않으면, <root-dir>--root-dir 플래그로 지정된 <root-dir>/seccomp 기본 경로가 사용된다.

seccomp.security.alpha.kubernetes.io/allowedProfileNames - 파드 seccomp 어노테이션에 허용되는 값을 지정하는 어노테이션. 쉼표로 구분된 허용된 값의 목록으로 지정된다. 가능한 값은 위에 나열된 값과 모든 프로파일을 허용하는 * 이다. 이 주석이 없으면 기본값을 변경할 수 없다.

Sysctl

기본적으로 모든 안전한 sysctls가 허용된다.

  • forbiddenSysctls - 특정 sysctls를 제외한다. 목록에서 안전한 것과 안전하지 않은 sysctls의 조합을 금지할 수 있다. 모든 sysctls 설정을 금지하려면 자체적으로 *를 사용한다.
  • allowedUnsafeSysctls - forbiddenSysctls에 나열되지 않는 한 기본 목록에서 허용하지 않은 특정 sysctls를 허용한다.

Sysctl 문서를 참고하길 바란다.

다음 내용

3.10.4 - 노드 리소스 매니저

쿠버네티스는 지연 시간에 민감하고 처리량이 많은 워크로드를 지원하기 위해 리소스 매니저 세트를 제공한다. 매니저는 CPU, 장치 및 메모리 (hugepages) 리소스와 같은 특정한 요구 사항으로 구성된 파드를 위해 노드의 리소스 할당을 조정하고 최적화하는 것을 목표로 한다.

주 매니저인 토폴로지 매니저는 정책을 통해 전체 리소스 관리 프로세스를 조정하는 Kubelet 컴포넌트이다.

개별 매니저의 구성은 다음의 문서에 자세히 기술되어 있다.

3.11 - 클러스터 관리

쿠버네티스 클러스터 생성 또는 관리에 관련된 로우-레벨(lower-level)의 세부 정보를 설명한다.

클러스터 관리 개요는 쿠버네티스 클러스터를 생성하거나 관리하는 모든 사람들을 위한 것이다. 핵심 쿠버네티스 개념에 어느 정도 익숙하다고 가정한다.

클러스터 계획

쿠버네티스 클러스터를 계획, 설정 및 구성하는 방법에 대한 예는 시작하기에 있는 가이드를 참고한다. 이 문서에 나열된 솔루션을 배포판 이라고 한다.

가이드를 선택하기 전에 고려해야 할 사항은 다음과 같다.

  • 컴퓨터에서 쿠버네티스를 한번 사용해보고 싶은가? 아니면, 고가용 멀티 노드 클러스터를 만들고 싶은가? 사용자의 필요에 따라 가장 적합한 배포판을 선택한다.
  • 구글 쿠버네티스 엔진(Google Kubernetes Engine)과 같은 클라우드 제공자의 쿠버네티스 클러스터 호스팅 을 사용할 것인가? 아니면, 자체 클러스터를 호스팅 할 것인가?
  • 클러스터가 온-프레미스 환경 에 있나? 아니면, 클라우드(IaaS) 에 있나? 쿠버네티스는 하이브리드 클러스터를 직접 지원하지는 않는다. 대신 여러 클러스터를 설정할 수 있다.
  • 온-프레미스 환경에 쿠버네티스 를 구성하는 경우, 어떤 네트워킹 모델이 가장 적합한 지 고려한다.
  • 쿠버네티스를 "베어 메탈" 하드웨어 에서 실행할 것인가? 아니면, 가상 머신(VM) 에서 실행할 것인가?
  • 클러스터만 실행할 것인가? 아니면, 쿠버네티스 프로젝트 코드를 적극적으로 개발 하는 것을 기대하는가? 만약 후자라면, 활발하게 개발이 진행되고 있는 배포판을 선택한다. 일부 배포판은 바이너리 릴리스만 사용하지만, 더 다양한 선택을 제공한다.
  • 클러스터를 실행하는 데 필요한 컴포넌트에 익숙해지자.

클러스터 관리

클러스터 보안

kubelet 보안

선택적 클러스터 서비스

3.11.1 - 인증서

클러스터를 위한 인증서를 생성하기 위해서는, 인증서를 참고한다.

3.11.2 - 리소스 관리

애플리케이션을 배포하고 서비스를 통해 노출했다. 이제 무엇을 해야 할까? 쿠버네티스는 확장과 업데이트를 포함하여, 애플리케이션 배포를 관리하는 데 도움이 되는 여러 도구를 제공한다. 더 자세히 설명할 기능 중에는 구성 파일레이블이 있다.

리소스 구성 구성하기

많은 애플리케이션들은 디플로이먼트 및 서비스와 같은 여러 리소스를 필요로 한다. 여러 리소스의 관리는 동일한 파일에 그룹화하여 단순화할 수 있다(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

리소스는 파일에 표시된 순서대로 생성된다. 따라서, 스케줄러가 디플로이먼트와 같은 컨트롤러에서 생성한 서비스와 관련된 파드를 분산시킬 수 있으므로, 서비스를 먼저 지정하는 것이 가장 좋다.

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

그리고 개별 파일 대신 또는 추가로 디렉터리를 지정할 수 있다.

kubectl apply -f https://k8s.io/examples/application/nginx/

kubectl 은 접미사가 .yaml, .yml 또는 .json 인 파일을 읽는다.

동일한 마이크로서비스 또는 애플리케이션 티어(tier)와 관련된 리소스를 동일한 파일에 배치하고, 애플리케이션과 연관된 모든 파일을 동일한 디렉터리에 그룹화하는 것이 좋다. 애플리케이션의 티어가 DNS를 사용하여 서로 바인딩되면, 스택의 모든 컴포넌트를 함께 배포할 수 있다.

URL을 구성 소스로 지정할 수도 있다. 이는 GitHub에 체크인된 구성 파일에서 직접 배포하는 데 편리하다.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

kubectl에서의 대량 작업

kubectl 이 대량으로 수행할 수 있는 작업은 리소스 생성만이 아니다. 또한 다른 작업을 수행하기 위해, 특히 작성한 동일한 리소스를 삭제하기 위해 구성 파일에서 리소스 이름을 추출할 수도 있다.

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

두 개의 리소스가 있는 경우, 리소스/이름 구문을 사용하여 커맨드 라인에서 둘다 모두 지정할 수도 있다.

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

위의 명령을 사용하여, 먼저 examples/application/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 플래그는 kubectl {create,get,delete,describe,rollout} 등과 같이 --filename,-f 플래그를 허용하는 모든 작업에서 작동한다.

--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 개요를 참조한다.

효과적인 레이블 사용

지금까지 사용한 예는 모든 리소스에 최대 한 개의 레이블만 적용하는 것이었다. 세트를 서로 구별하기 위해 여러 레이블을 사용해야 하는 많은 시나리오가 있다.

예를 들어, 애플리케이션마다 app 레이블에 다른 값을 사용하지만, 방명록 예제와 같은 멀티-티어 애플리케이션은 각 티어를 추가로 구별해야 한다. 프론트엔드는 다음의 레이블을 가질 수 있다.

     labels:
        app: guestbook
        tier: frontend

Redis 마스터와 슬레이브는 프론트엔드와 다른 tier 레이블을 가지지만, 아마도 추가로 role 레이블을 가질 것이다.

     labels:
        app: guestbook
        tier: backend
        role: master

그리고

     labels:
        app: guestbook
        tier: backend
        role: slave

레이블은 레이블로 지정된 차원에 따라 리소스를 분할하고 사용할 수 있게 한다.

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY     STATUS    RESTARTS   AGE       APP         TIER       ROLE
guestbook-fe-4nlpb             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-ght6d             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-jpy62             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1       Running   0          1m        guestbook   backend    master
guestbook-redis-slave-2q2yf    1/1       Running   0          1m        guestbook   backend    slave
guestbook-redis-slave-qgazl    1/1       Running   0          1m        guestbook   backend    slave
my-nginx-divi2                 1/1       Running   0          29m       nginx       <none>     <none>
my-nginx-o0ef1                 1/1       Running   0          29m       nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=slave
NAME                          READY     STATUS    RESTARTS   AGE
guestbook-redis-slave-2q2yf   1/1       Running   0          3m
guestbook-redis-slave-qgazl   1/1       Running   0          3m

카나리(canary) 디플로이먼트

여러 레이블이 필요한 또 다른 시나리오는 동일한 컴포넌트의 다른 릴리스 또는 구성의 디플로이먼트를 구별하는 것이다. 새 릴리스가 완전히 롤아웃되기 전에 실제 운영 트래픽을 수신할 수 있도록 새로운 애플리케이션 릴리스(파드 템플리트의 이미지 태그를 통해 지정됨)의 카나리 를 이전 릴리스와 나란히 배포하는 것이 일반적이다.

예를 들어, track 레이블을 사용하여 다른 릴리스를 구별할 수 있다.

기본(primary), 안정(stable) 릴리스에는 값이 stabletrack 레이블이 있다.

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

그런 다음 서로 다른 값(예: canary)으로 track 레이블을 전달하는 방명록 프론트엔드의 새 릴리스를 생성하여, 두 세트의 파드가 겹치지 않도록 할 수 있다.

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

프론트엔드 서비스는 레이블의 공통 서브셋을 선택하여(즉, track 레이블 생략) 두 레플리카 세트에 걸쳐 있으므로, 트래픽이 두 애플리케이션으로 리디렉션된다.

  selector:
     app: guestbook
     tier: frontend

안정 및 카나리 릴리스의 레플리카 수를 조정하여 실제 운영 트래픽을 수신할 각 릴리스의 비율을 결정한다(이 경우, 3:1). 확신이 들면, 안정 릴리스의 track을 새로운 애플리케이션 릴리스로 업데이트하고 카나리를 제거할 수 있다.

보다 구체적인 예시는, Ghost 배포에 대한 튜토리얼을 확인한다.

레이블 업데이트

새로운 리소스를 만들기 전에 기존 파드 및 기타 리소스의 레이블을 다시 지정해야 하는 경우가 있다. 이것은 kubectl label 로 수행할 수 있다. 예를 들어, 모든 nginx 파드에 프론트엔드 티어로 레이블을 지정하려면, 다음과 같이 실행한다.

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

먼저 "app=nginx" 레이블이 있는 모든 파드를 필터링한 다음, "tier=fe" 레이블을 지정한다. 레이블을 지정한 파드를 보려면, 다음을 실행한다.

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

그러면 파드 티어의 추가 레이블 열(-L 또는 --label-columns 로 지정)과 함께, 모든 "app=nginx" 파드가 출력된다.

더 자세한 내용은, 레이블kubectl label을 참고하길 바란다.

어노테이션 업데이트

때로는 어노테이션을 리소스에 첨부하려고 할 수도 있다. 어노테이션은 도구, 라이브러리 등과 같은 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

이제 디플로이먼트가 관리하는 파드가 하나만 있다.

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

시스템이 필요에 따라 1에서 3까지의 범위에서 nginx 레플리카 수를 자동으로 선택하게 하려면, 다음을 수행한다.

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

이제 nginx 레플리카가 필요에 따라 자동으로 확장되거나 축소된다.

더 자세한 내용은, kubectl scale, kubectl autoscalehorizontal pod autoscaler 문서를 참고하길 바란다.

리소스 인플레이스(in-place) 업데이트

때로는 자신이 만든 리소스를 필요한 부분만, 중단없이 업데이트해야 할 때가 있다.

kubectl apply

구성 파일 셋을 소스 제어에서 유지하는 것이 좋으며 (코드로서의 구성 참조), 그렇게 하면 구성하는 리소스에 대한 코드와 함께 버전을 지정하고 유지할 수 있다. 그런 다음, kubectl apply를 사용하여 구성 변경 사항을 클러스터로 푸시할 수 있다.

이 명령은 푸시하려는 구성의 버전을 이전 버전과 비교하고 지정하지 않은 속성에 대한 자동 변경 사항을 덮어쓰지 않은 채 수정한 변경 사항을 적용한다.

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

참고로 kubectl apply 는 이전의 호출 이후 구성의 변경 사항을 판별하기 위해 리소스에 어노테이션을 첨부한다. 호출되면, kubectl apply 는 리소스를 수정하는 방법을 결정하기 위해, 이전 구성과 제공된 입력 및 리소스의 현재 구성 간에 3-way diff를 수행한다.

현재, 이 어노테이션 없이 리소스가 생성되므로, kubectl apply 의 첫 번째 호출은 제공된 입력과 리소스의 현재 구성 사이의 2-way diff로 대체된다. 이 첫 번째 호출 중에는, 리소스를 생성할 때 설정된 특성의 삭제를 감지할 수 없다. 이러한 이유로, 그 특성들을 삭제하지 않는다.

kubectl apply 에 대한 모든 후속 호출, 그리고 kubectl replacekubectl edit 와 같이 구성을 수정하는 다른 명령은, 어노테이션을 업데이트하여, kubectl apply 에 대한 후속 호출이 3-way diff를 사용하여 삭제를 감지하고 수행할 수 있도록 한다.

kubectl edit

또는, kubectl edit로 리소스를 업데이트할 수도 있다.

kubectl edit deployment/my-nginx

이것은 먼저 리소스를 get 하여, 텍스트 편집기에서 편집한 다음, 업데이트된 버전으로 리소스를 apply 하는 것과 같다.

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# 편집한 다음, 파일을 저장한다.

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 패치, JSON 병합 패치 그리고 전략적 병합 패치를 지원한다. kubectl patch를 사용한 인플레이스 API 오브젝트 업데이트kubectl patch를 참조한다.

파괴적(disruptive) 업데이트

경우에 따라, 한 번 초기화하면 업데이트할 수 없는 리소스 필드를 업데이트해야 하거나, 디플로이먼트에서 생성된 손상된 파드를 고치는 등의 재귀적 변경을 즉시 원할 수도 있다. 이러한 필드를 변경하려면, 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

서비스 중단없이 애플리케이션 업데이트

언젠가는, 위의 카나리 디플로이먼트 시나리오에서와 같이, 일반적으로 새 이미지 또는 이미지 태그를 지정하여, 배포된 애플리케이션을 업데이트해야 한다. kubectl 은 여러 가지 업데이트 작업을 지원하며, 각 업데이트 작업은 서로 다른 시나리오에 적용할 수 있다.

디플로이먼트를 사용하여 애플리케이션을 생성하고 업데이트하는 방법을 안내한다.

nginx 1.14.2 버전을 실행한다고 가정해 보겠다.

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

3개의 레플리카를 포함한다(이전과 새 개정판이 공존할 수 있음).

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

1.16.1 버전으로 업데이트하려면, 위에서 배운 kubectl 명령을 사용하여 .spec.template.spec.containers[0].imagenginx:1.14.2 에서 nginx:1.16.1 로 변경한다.

kubectl edit deployment/my-nginx

이것으로 끝이다! 디플로이먼트는 배포된 nginx 애플리케이션을 배후에서 점차적으로 업데이트한다. 업데이트되는 동안 특정 수의 이전 레플리카만 중단될 수 있으며, 원하는 수의 파드 위에 특정 수의 새 레플리카만 생성될 수 있다. 이에 대한 더 자세한 내용을 보려면, 디플로이먼트 페이지를 방문한다.

다음 내용

3.11.3 - 클러스터 네트워킹

네트워킹은 쿠버네티스의 중심적인 부분이지만, 어떻게 작동하는지 정확하게 이해하기가 어려울 수 있다. 쿠버네티스에는 4가지 대응해야 할 네트워킹 문제가 있다.

  1. 고도로 결합된 컨테이너 간의 통신: 이 문제는 파드localhost 통신으로 해결된다.
  2. 파드 간 통신: 이 문제가 이 문서의 주요 초점이다.
  3. 파드와 서비스 간 통신: 이 문제는 서비스에서 다룬다.
  4. 외부와 서비스 간 통신: 이 문제는 서비스에서 다룬다.

쿠버네티스는 애플리케이션 간에 머신을 공유하는 것이다. 일반적으로, 머신을 공유하려면 두 애플리케이션이 동일한 포트를 사용하지 않도록 해야 한다. 여러 개발자 간에 포트를 조정하는 것은 대규모로 실시하기가 매우 어렵고, 사용자가 통제할 수 없는 클러스터 수준의 문제에 노출된다.

동적 포트 할당은 시스템에 많은 복잡성을 야기한다. 모든 애플리케이션은 포트를 플래그로 가져와야 하며, API 서버는 동적 포트 번호를 구성 블록에 삽입하는 방법을 알아야 하고, 서비스는 서로를 찾는 방법 등을 알아야 한다. 쿠버네티스는 이런 것들을 다루는 대신 다른 접근법을 취한다.

쿠버네티스 네트워크 모델

모든 Pod 에는 고유의 IP 주소가 있다. 즉, Pod 간에 링크를 명시적으로 생성할 필요가 없으며 컨테이너 포트를 호스트 포트에 매핑할 필요가 거의 없다. 이렇게 하면 포트 할당, 이름 지정, 서비스 검색, 로드 밸런싱, 애플리케이션 구성 및 마이그레이션 관점에서 Pod 를 VM 또는 물리적 호스트처럼 취급할 수 있는 깔끔하고, 하위 호환성 있는 모델이 생성된다.

쿠버네티스는 모든 네트워크 구현에 다음과 같은 기본 요구 사항을 적용한다(의도적 네트워크 세분화 정책 제외).

  • 노드의 파드는 NAT 없이 모든 노드의 모든 파드와 통신할 수 있다.
  • 노드의 에이전트(예: 시스템 데몬, kubelet)는 해당 노드의 모든 파드와 통신할 수 있다.

참고: 호스트 네트워크에서 실행되는 Pod 를 지원하는 리눅스와 같은 플랫폼의 경우, 다음의 요구 사항을 적용한다.

  • 노드의 호스트 네트워크에 있는 파드는 NAT 없이 모든 노드에 있는 모든 파드와 통신할 수 있다.

이 모델은 전체적으로 덜 복잡할 뿐만 아니라, 쿠버네티스를 위해 VM에서 컨테이너로 애플리케이션을 포팅할 때 충돌이 적게 구현하려는 요구와 주로 호환된다. 잡이 이전에 VM에서 실행된 경우, VM에 IP가 있고 프로젝트의 다른 VM과 통신할 수 있다. 이것은 동일한 기본 모델이다.

쿠버네티스의 IP 주소는 그것의 IP 주소와 MAC 주소를 포함하여 Pod 범위에 존재한다(Pod 내 컨테이너는 네트워크 네임스페이스를 공유함). 이것은 Pod 내 컨테이너가 모두 localhost 에서 서로의 포트에 도달할 수 있다는 것을 의미한다. 또한 Pod 내부의 컨테이너 포트의 사용을 조정해야하는 것을 의미하지만, 이것도 VM 내의 프로세스와 동일하다. 이것을 "IP-per-pod(파드별 IP)" 모델이라고 한다.

이것이 어떻게 구현되는 지는 사용 중인 특정 컨테이너 런타임의 세부 사항이다.

Pod 로 전달하는 Node 자체의 포트(호스트 포트라고 함)를 요청할 수 있지만, 이는 매우 틈새 작업이다. 전달이 구현되는 방법은 컨테이너 런타임의 세부 사항이기도 하다. Pod 자체는 호스트 포트의 존재 여부에 대해 인식하지 못한다.

쿠버네티스 네트워크 모델의 구현 방법

이 네트워크 모델을 구현할 수 있는 방법에는 여러 가지가 있다. 이 문서는 다양한 방법에 대한 철저한 연구는 아니지만, 다양한 기술에 대한 소개로 활용되며 도약하는 포인트로 사용되기를 바란다.

이 목록은 알파벳 순으로 정렬되어 있으며, 정렬된 순서가 우선 상태를 의미하는 것은 아니다.

ACI

Cisco 애플리케이션 센트릭 인프라스트럭처(Application Centric Infrastructure)는 컨테이너, 가상 머신 및 베어메탈 서버를 지원하는 통합 오버레이 및 언더레이 SDN 솔루션을 제공한다. ACI는 ACI를 위한 컨테이너 네트워킹 통합을 제공한다. 통합의 개요는 여기에서 제공된다.

Antrea

프로젝트 Antrea는 쿠버네티스 고유의 오픈소스 쿠버네티스 네트워킹 솔루션이다. 네트워킹 데이터 플레인으로 Open vSwitch를 활용한다. Open vSwitch는 리눅스와 윈도우를 모두 지원하는 고성능의 프로그래밍이 가능한 가상 스위치이다. Antrea는 Open vSwitch를 통해 쿠버네티스 네트워크 정책을 고성능의 효율적인 방식으로 구현할 수 있다. Antrea는 Open vSwitch의 "프로그래밍이 가능한" 특성으로 인해 Open vSwitch 위에 광범위한 네트워킹 및 보안 기능과 서비스를 구현할 수 있다.

쿠버네티스용 AWS VPC CNI

AWS VPC CNI는 쿠버네티스 클러스터를 위한 통합된 AWS 버추얼 프라이빗 클라우드(Virtual Private Cloud, VPC) 네트워킹을 제공한다. 이 CNI 플러그인은 높은 처리량과 가용성, 낮은 레이턴시(latency) 그리고 최소 네트워크 지터(jitter)를 제공한다. 또한, 사용자는 쿠버네티스 클러스터를 구축하기 위한 기존의 AWS VPC 네트워킹 및 보안 모범 사례를 적용할 수 있다. 여기에는 VPC 플로우 로그, VPC 라우팅 정책과 네트워크 트래픽 격리를 위한 보안 그룹을 사용하는 기능이 포함되어 있다.

이 CNI 플러그인을 사용하면 쿠버네티스 파드는 VPC 네트워크와 동일한 IP 주소를 파드 내부에 가질 수 있다. CNI는 각 쿠버네티스 노드에 AWS 엘라스틱 네트워킹 인터페이스(Elastic Networking Interfaces, ENI)를 할당하고 노드의 파드에 대해 각 ENI의 보조 IP 범위를 사용한다. CNI에는 파드를 빠르게 시작하기 위해 ENI와 IP 주소의 사전 할당 제어 기능이 포함되어 있으며 최대 2,000개의 노드로 구성된 대규모 클러스터가 가능하다.

또한, CNI는 네트워크 폴리시 적용을 위해 캘리코(Calico)와 함께 실행할 수 있다. AWS VPC CNI 프로젝트는 GitHub의 문서와 함께 오픈소스로 공개되어 있다.

쿠버네티스용 Azure CNI

Azure CNI는 VM과 동등한 네트워크 성능을 제공하는 Azure 버추얼 네트워크(VNet이라고도 알려진)와 쿠버네티스 파드를 통합하는 오픈소스 플러그인이다. 파드는 피어링된 VNet과 Express Route 또는 사이트 간 VPN을 통해 온-프레미스에 연결할 수 있으며 이러한 네트워크에서 직접 연결할 수도 있다. 파드는 서비스 엔드포인트 또는 프라이빗 링크로 보호되는 스토리지와 SQL과 같은 Azure 서비스에 접근할 수 있다. VNet 보안 정책과 라우팅을 사용하여 파드 트래픽을 필터링할 수 있다. 플러그인은 쿠버네티스 노드의 네트워크 인터페이스에 사전 구성된 보조 IP 풀을 활용하여 VNet IP를 파드에 할당한다.

Azure CNI는 Azure 쿠버네티스 서비스(Azure Kubernetes Service, AKS)에서 기본적으로 사용할 수 있다.

캘리코

캘리코는 컨테이너, 가상 시스템 및 기본 호스트 기반 워크로드를 위한 오픈소스 네트워킹 및 네트워크 보안 솔루션이다. 캘리코는 순수 리눅스 eBPF 데이터플레인, 표준 리눅스 네트워킹 데이터플레인, 윈도우 HNS 데이터플레인을 포함한 여러 데이터플레인을 지원한다. 캘리코는 완전한 네트워킹 스택을 제공하지만, 클라우드 제공자 CNI와 함께 사용하여 네트워크 정책 시행을 제공할 수도 있다.

실리움(Cilium)

실리움은 애플리케이션 컨테이너 간에 네트워크 연결을 제공하고 투명하게 보호하기 위한 오픈소스 소프트웨어이다. 실리움은 L7/HTTP를 인식하며 네트워크 주소 지정에서 분리된 ID 기반 보안 모델을 사용하여 L3-L7에서 네트워크 정책을 적용할 수 있으며, 다른 CNI 플러그인과 함께 사용할 수 있다.

화웨이의 CNI-Genie

CNI-Genie는 쿠버네티스가 런타임 시 쿠버네티스 네트워크 모델서로 다른 구현에 동시에 접근할 수 있는 CNI 플러그인이다. 여기에는 플라넬(Flannel), 캘리코, 로마나(Romana), 위브넷(Weave-net)과 같은 CNI 플러그인으로 실행되는 모든 구현이 포함된다.

CNI-Genie는 각각 다른 CNI 플러그인에서 하나의 파드에 여러 IP 주소를 할당하는 것도 지원한다.

cni-ipvlan-vpc-k8s

cni-ipvlan-vpc-k8s는 L2 모드에서 리눅스 커널의 IPvlan 드라이버를 사용하여 Amazon 엘라스틱 네트워크 인터페이스(ENI)를 사용하고 AWS 매니지드 IP를 파드에 바인딩하는 Amazon 버추얼 프라이빗 클라우드(VPC) 환경 내에서 쿠버네티스를 위한 간단하고, 호스트 로컬, 낮은 레이턴시, 높은 처리량 및 호환 네트워킹 스택을 제공하는 CNI와 IPAM 플러그인 셋을 포함한다.

플러그인은 VPC 내에서 구성하고 배포할 수 있도록 간단하게 설계되었다. Kubelets는 오버레이 네트워크 관리, BGP 관리, 소스/대상 확인 비활성화 또는 VPC 라우팅 테이블을 조정하여 각 호스트에 인스턴스별 서브넷을 제공(VPC별 50-100개 항목으로 제한)하는 등의 자주 권장되는 복잡성을 요구하지 않고 부팅한 다음 필요에 따라 IP 사용량을 자체 구성하고 확장할 수 있다. 즉, cni-ipvlan-vpc-k8s는 AWS 내에서 쿠버네티스를 대규모로 배포하는 데 필요한 네트워크 복잡성을 크게 줄인다.

Coil

Coil은 통합이 용이하도록 설계된 CNI 플러그인으로 유연한 이그레스(egress) 네트워킹을 제공한다. Coil은 베어메탈에 비해 낮은 오버헤드로 작동하며, 외부 네트워크에 대해 임의의 이그레스 NAT 게이트웨이를 정의할 수 있다.

콘티브(Contiv)

콘티브는 다양한 적용 사례에서 구성 가능한 네트워킹(BGP를 사용하는 네이티브 L3, vxlan을 사용하는 오버레이, 클래식 L2 또는 Cisco-SDN/ACI)을 제공한다.

콘트레일(Contrail) / 텅스텐 패브릭(Tungsten Fabric)

텅스텐 패브릭을 기반으로 하는 콘트레일은 진정한 개방형 멀티 클라우드 네트워크 가상화 및 정책 관리 플랫폼이다. 콘트레일 및 텅스텐 패브릭은 쿠버네티스, OpenShift, OpenStack 및 Mesos와 같은 다양한 오케스트레이션 시스템과 통합되어 있으며, 가상 머신, 컨테이너/파드 및 베어메탈 워크로드에 대해 서로 다른 격리 모드를 제공한다.

DANM

DANM은 쿠버네티스 클러스터에서 실행되는 통신사 워크로드를 위한 네트워킹 솔루션이다. 다음의 컴포넌트로 구성된다.

  • 고급 기능들로 IPVLAN 인터페이스를 프로비저닝할 수 있는 CNI 플러그인
  • 여러 클러스터 전체의 불연속 L3 네트워크를 관리하고 요청 시 동적, 정적 또는 IP를 할당하지 않는 방식을 제공하는 내장 IPAM 모듈
  • 자체 CNI를 통해서, 또는 SRI-OV나 플라넬과 같은 널리 사용되는 CNI 솔루션에 잡을 동시에 위임하여 여러 네트워크 인터페이스를 컨테이너에 연결할 수 있는 CNI 메타플러그인
  • 모든 쿠버네티스 호스트의 VxLAN 및 VLAN 인터페이스를 중앙에서 관리할 수 있는 쿠버네티스 컨트롤러
  • 쿠버네티스의 서비스 기반의 서비스 검색 개념을 확장하여 파드의 모든 네트워크 인터페이스에서 작동하는 다른 쿠버네티스 컨트롤러

이 도구 셋을 통해 DANM은 여러 개의 분리된 네트워크 인터페이스를 제공할 수 있으며, 파드에 다른 네트워킹 백엔드 및 고급 IPAM 기능을 사용할 수 있다.

플라넬

플라넬은 쿠버네티스 요구 사항을 충족하는 매우 간단한 오버레이 네트워크이다. 많은 경우에 쿠버네티스와 플라넬은 성공적으로 적용이 가능하다.

Google 컴퓨트 엔진(GCE)

Google 컴퓨트 엔진 클러스터 구성 스크립트의 경우, 고급 라우팅을 사용하여 각 VM에 서브넷을 할당한다(기본값은 /24 - 254개 IP). 해당 서브넷에 바인딩된 모든 트래픽은 GCE 네트워크 패브릭에 의해 VM으로 직접 라우팅된다. 이는 아웃 바운드 인터넷 접근을 위해 NAT로 구성된 VM에 할당된 "기본" IP 주소에 추가된다. 리눅스 브릿지(cbr0)는 해당 서브넷에 존재하도록 구성되며, 도커의 --bridge 플래그로 전달된다.

도커는 다음의 설정으로 시작한다.

DOCKER_OPTS="--bridge=cbr0 --iptables=false --ip-masq=false"

이 브릿지는 노드의 .spec.podCIDR에 따라 Kubelet(--network-plugin=kubenet 플래그로 제어되는)에 의해 생성된다.

도커는 이제 cbr-cidr 블록에서 IP를 할당한다. 컨테이너는 cbr0 브릿지를 통해 서로 Node 에 도달할 수 있다. 이러한 IP는 모두 GCE 프로젝트 네트워크 내에서 라우팅할 수 있다.

그러나, GCE 자체는 이러한 IP에 대해 전혀 알지 못하므로, 아웃 바운드 인터넷 트래픽을 위해 IP를 NAT하지 않는다. 그것을 달성하기 위해 iptables 규칙을 사용하여 GCE 프로젝트 네트워크(10.0.0.0/8) 외부의 IP에 바인딩된 트래픽을 마스커레이드(일명 SNAT - 마치 패킷이 Node 자체에서 온 것처럼 보이게 함)한다.

iptables -t nat -A POSTROUTING ! -d 10.0.0.0/8 -o eth0 -j MASQUERADE

마지막으로 커널에서 IP 포워딩이 활성화되어 있으므로, 커널은 브릿지된 컨테이너에 대한 패킷을 처리한다.

sysctl net.ipv4.ip_forward=1

이 모든 것의 결과는 모든 Pod 가 서로에게 도달할 수 있고 인터넷으로 트래픽을 송신할 수 있다는 것이다.

재규어(Jaguar)

재규어는 OpenDaylight 기반의 쿠버네티스 네트워크를 위한 오픈소스 솔루션이다. 재규어는 vxlan을 사용하여 오버레이 네트워크를 제공하고 재규어 CNI 플러그인은 파드별로 하나의 IP 주소를 제공한다.

k-vswitch

k-vswitchOpen vSwitch 기반의 간단한 쿠버네티스 네트워킹 플러그인이다. Open vSwitch의 기존 기능을 활용하여 운영하기 쉽고, 성능이 뛰어나고 안전한 강력한 네트워킹 플러그인을 제공한다.

Knitter

Knitter는 쿠버네티스에서 여러 네트워킹을 지원하는 네트워크 솔루션이다. 테넌트 관리 및 네트워크 관리 기능을 제공한다. Knitter에는 애플리케이션의 IP 주소 유지, IP 주소 마이그레이션 등과 같은 여러 네트워크 플레인 외에 엔드-투-엔드 NFV 컨테이너 네트워킹 솔루션 셋이 포함되어 있다.

Kube-OVN

Kube-OVN은 기업을 위한 OVN 기반 쿠버네티스 네트워크 패브릭이다. OVN/OVS의 도움으로, 서브넷, QoS, 고정 IP 할당, 트래픽 미러링, 게이트웨이, 오픈플로우 기반 네트워크 정책 및 서비스 프록시와 같은 고급 오버레이 네트워크 기능을 제공한다.

Kube-router

kube-router는 쿠버네티스를 위한 특수 목적의 네트워킹 솔루션으로 고성능 및 운영 단순성을 제공한다. 큐브 라우터는 리눅스 LVS/IPVS 기반 서비스 프록시, 오버레이가 없는 리눅스 커널 포워딩 기반의 파드 간 네트워킹 솔루션 그리고 iptables/ipset 기반 네트워크 정책 집행도구를 제공한다.

L2 네트워크 및 리눅스 브릿지

"베어메탈" 환경의 간단한 스위치와 같은 "더미(dumb)" L2 네트워크가 있는 경우, 위의 GCE 설정과 비슷한 작업을 수행할 수 있어야 한다. 이 방법은 매우 우연히 시도되었고 작동하는 것으로 보이지만 철저히 테스트되지 않았다. 이 기술을 사용하여 프로세스를 완료한 경우, 알려주길 바란다.

Lars Kellogg-Stedman이 제공하는 이 훌륭한 튜토리얼의 "With Linux Bridge devices" 섹션을 참고한다.

Multus(멀티 네트워크 플러그인)

Multus는 쿠버네티스의 CRD 기반 네트워크 오브젝트를 사용하여 쿠버네티스에서 멀티 네트워킹 기능을 지원하는 멀티 CNI 플러그인이다.

Multus는 CNI 명세를 구현하는 모든 레퍼런스 플러그인(예: 플라넬, DHCP, Macvlan) 및 써드파티 플러그인(예: 캘리코, 위브(Weave), 실리움, 콘티브)을 지원한다. 또한, Multus는 쿠버네티스의 클라우드 네이티브 애플리케이션과 NFV 기반 애플리케이션을 통해 쿠버네티스의 SRIOV, DPDK, OVS-DPDK 및 VPP 워크로드를 지원한다.

OVN4NFV-K8s-Plugin (OVN 기반의 CNI 컨트롤러 & 플러그인)

OVN4NFV-K8S-Plugin은 OVN 기반의 CNI 컨트롤러 플러그인으로 클라우드 네이티브 기반 서비스 기능 체인(Service function chaining(SFC)), 다중 OVN 오버레이 네트워킹, 동적 서브넷 생성, 동적 가상 네트워크 생성, VLAN 공급자 네트워크, 직접 공급자 네트워크와 멀티 클러스터 네트워킹의 엣지 기반 클라우드 등 네이티브 워크로드에 이상적인 멀티 네티워크 플러그인이다.

NSX-T

VMware NSX-T는 네트워크 가상화 및 보안 플랫폼이다. NSX-T는 멀티 클라우드 및 멀티 하이퍼바이저 환경에 네트워크 가상화를 제공할 수 있으며 이기종 엔드포인트와 기술 스택이 있는 새로운 애플리케이션 프레임워크 및 아키텍처에 중점을 둔다. vSphere 하이퍼바이저 외에도, 이러한 환경에는 KVM, 컨테이너 및 베어메탈과 같은 다른 하이퍼바이저가 포함된다.

NSX-T 컨테이너 플러그인(NCP)은 NSX-T와 쿠버네티스와 같은 컨테이너 오케스트레이터 사이의 통합은 물론, NSX-T와 Pivotal 컨테이너 서비스(PKS) 및 OpenShift와 같은 컨테이너 기반 CaaS/PaaS 플랫폼 간의 통합을 제공한다.

OpenVSwitch

OpenVSwitch는 다소 성숙하지만 오버레이 네트워크를 구축하는 복잡한 방법이다. 이것은 네트워킹 분야의 몇몇 "대형 벤더"에 의해 승인되었다.

OVN(오픈 버추얼 네트워킹)

OVN은 Open vSwitch 커뮤니티에서 개발한 오픈소스 네트워크 가상화 솔루션이다. 논리적 스위치, 논리적 라우터, 스테이트풀 ACL, 로드 밸런서 등을 생성하여 서로 다른 가상 네트워킹 토폴로지를 구축할 수 있다. 이 프로젝트에는 ovn-kubernetes에 특정 쿠버네티스 플러그인 및 문서가 있다.

로마나

로마나는 오버레이 네트워크 없이 쿠버네티스를 배포할 수 있는 오픈소스 네트워크 및 보안 자동화 솔루션이다. 로마나는 쿠버네티스 네트워크 폴리시를 지원하여 네트워크 네임스페이스에서 격리를 제공한다.

Weaveworks의 위브넷

위브넷은 쿠버네티스 및 호스팅된 애플리케이션을 위한 탄력적이고 사용하기 쉬운 네트워크이다. 위브넷은 CNI 플러그인 또는 독립형으로 실행된다. 두 버전에서, 실행하기 위해 구성이나 추가 코드가 필요하지 않으며, 두 경우 모두, 쿠버네티스의 표준과 같이 네트워크에서 파드별로 하나의 IP 주소를 제공한다.

다음 내용

네트워크 모델의 초기 설계와 그 근거 및 미래의 계획은 네트워킹 디자인 문서에 자세히 설명되어 있다.

3.11.4 - 로깅 아키텍처

애플리케이션 로그는 애플리케이션 내부에서 발생하는 상황을 이해하는 데 도움이 된다. 로그는 문제를 디버깅하고 클러스터 활동을 모니터링하는 데 특히 유용하다. 대부분의 최신 애플리케이션에는 일종의 로깅 메커니즘이 있다. 마찬가지로, 컨테이너 엔진들도 로깅을 지원하도록 설계되었다. 컨테이너화된 애플리케이션에 가장 쉽고 가장 널리 사용되는 로깅 방법은 표준 출력과 표준 에러 스트림에 작성하는 것이다.

그러나, 일반적으로 컨테이너 엔진이나 런타임에서 제공하는 기본 기능은 완전한 로깅 솔루션으로 충분하지 않다. 예를 들어, 컨테이너가 크래시되거나, 파드가 축출되거나, 노드가 종료된 경우에도 애플리케이션의 로그에 접근하고 싶을 것이다. 클러스터에서 로그는 노드, 파드 또는 컨테이너와는 독립적으로 별도의 스토리지와 라이프사이클을 가져야 한다. 이 개념을 클러스터-레벨-로깅 이라고 한다.

클러스터-레벨 로깅은 로그를 저장, 분석, 쿼리하기 위해서는 별도의 백엔드가 필요하다. 쿠버네티스가 로그 데이터를 위한 네이티브 스토리지 솔루션을 제공하지는 않지만, 쿠버네티스에 통합될 수 있는 기존의 로깅 솔루션이 많이 있다.

쿠버네티스의 기본 로깅

이 예시는 텍스트를 초당 한 번씩 표준 출력에 쓰는 컨테이너에 대한 Pod 명세를 사용한다.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

이 파드를 실행하려면, 다음의 명령을 사용한다.

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

출력은 다음과 같다.

pod/counter created

로그를 가져오려면, 다음과 같이 kubectl logs 명령을 사용한다.

kubectl logs counter

출력은 다음과 같다.

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...

kubectl logs --previous 를 사용해서 컨테이너의 이전 인스턴스에 대한 로그를 검색할 수 있다. 파드에 여러 컨테이너가 있는 경우, 명령에 컨테이너 이름을 추가하여 접근하려는 컨테이너 로그를 지정해야 한다. 자세한 내용은 kubectl logs 문서를 참조한다.

노드 레벨에서의 로깅

노드 레벨 로깅

컨테이너화된 애플리케이션의 stdout(표준 출력)stderr(표준 에러) 스트림에 의해 생성된 모든 출력은 컨테이너 엔진이 처리 및 리디렉션 한다. 예를 들어, 도커 컨테이너 엔진은 이 두 스트림을 로깅 드라이버로 리디렉션 한다. 이 드라이버는 쿠버네티스에서 JSON 형식의 파일에 작성하도록 구성된다.

기본적으로, 컨테이너가 다시 시작되면, kubelet은 종료된 컨테이너 하나를 로그와 함께 유지한다. 파드가 노드에서 축출되면, 해당하는 모든 컨테이너도 로그와 함께 축출된다.

노드-레벨 로깅에서 중요한 고려 사항은 로그 로테이션을 구현하여, 로그가 노드에서 사용 가능한 모든 스토리지를 사용하지 않도록 하는 것이다. 쿠버네티스는 로그 로테이션에 대한 의무는 없지만, 디플로이먼트 도구로 이를 해결하기 위한 솔루션을 설정해야 한다. 예를 들어, kube-up.sh 스크립트에 의해 배포된 쿠버네티스 클러스터에는, 매시간 실행되도록 구성된 logrotate 도구가 있다. 애플리케이션의 로그를 자동으로 로테이션하도록 컨테이너 런타임을 설정할 수도 있다.

예를 들어, kube-up.sh 가 GCP의 COS 이미지 로깅을 설정하는 방법은 configure-helper 스크립트를 통해 자세히 알 수 있다.

CRI 컨테이너 런타임 을 사용할 때, kubelet은 로그를 로테이션하고 로깅 디렉터리 구조를 관리한다. kubelet은 이 정보를 CRI 컨테이너 런타임에 전송하고 런타임은 컨테이너 로그를 지정된 위치에 기록한다. kubelet config file에 있는 두 개의 kubelet 파라미터 containerLogMaxSizecontainerLogMaxFiles를 사용하여 각 로그 파일의 최대 크기와 각 컨테이너에 허용되는 최대 파일 수를 각각 구성할 수 있다.

기본 로깅 예제에서와 같이 kubectl logs를 실행하면, 노드의 kubelet이 요청을 처리하고 로그 파일에서 직접 읽는다. kubelet은 로그 파일의 내용을 반환한다.

시스템 컴포넌트 로그

시스템 컴포넌트에는 컨테이너에서 실행되는 것과 컨테이너에서 실행되지 않는 두 가지 유형이 있다. 예를 들면 다음과 같다.

  • 쿠버네티스 스케줄러와 kube-proxy는 컨테이너에서 실행된다.
  • Kubelet과 컨테이너 런타임은 컨테이너에서 실행되지 않는다.

systemd를 사용하는 시스템에서는, kubelet과 컨테이너 런타임은 journald에 작성한다. systemd를 사용하지 않으면, kubelet과 컨테이너 런타임은 /var/log 디렉터리의 .log 파일에 작성한다. 컨테이너 내부의 시스템 컴포넌트는 기본 로깅 메커니즘을 무시하고, 항상 /var/log 디렉터리에 기록한다. 시스템 컴포넌트는 klog 로깅 라이브러리를 사용한다. 로깅에 대한 개발 문서에서 해당 컴포넌트의 로깅 심각도(severity)에 대한 규칙을 찾을 수 있다.

컨테이너 로그와 마찬가지로, /var/log 디렉터리의 시스템 컴포넌트 로그를 로테이트해야 한다. kube-up.sh 스크립트로 구축한 쿠버네티스 클러스터에서 로그는 매일 또는 크기가 100MB를 초과하면 logrotate 도구에 의해 로테이트가 되도록 구성된다.

클러스터 레벨 로깅 아키텍처

쿠버네티스는 클러스터-레벨 로깅을 위한 네이티브 솔루션을 제공하지 않지만, 고려해야 할 몇 가지 일반적인 접근 방법을 고려할 수 있다. 여기 몇 가지 옵션이 있다.

  • 모든 노드에서 실행되는 노드-레벨 로깅 에이전트를 사용한다.
  • 애플리케이션 파드에 로깅을 위한 전용 사이드카 컨테이너를 포함한다.
  • 애플리케이션 내에서 로그를 백엔드로 직접 푸시한다.

노드 로깅 에이전트 사용

노드 레벨 로깅 에이전트 사용

각 노드에 노드-레벨 로깅 에이전트 를 포함시켜 클러스터-레벨 로깅을 구현할 수 있다. 로깅 에이전트는 로그를 노출하거나 로그를 백엔드로 푸시하는 전용 도구이다. 일반적으로, 로깅 에이전트는 해당 노드의 모든 애플리케이션 컨테이너에서 로그 파일이 있는 디렉터리에 접근할 수 있는 컨테이너이다.

로깅 에이전트는 모든 노드에서 실행되어야 하므로, 에이전트를 DaemonSet 으로 동작시키는 것을 추천한다.

노드-레벨 로깅은 노드별 하나의 에이전트만 생성하며, 노드에서 실행되는 애플리케이션에 대한 변경은 필요로 하지 않는다.

컨테이너는 stdout과 stderr를 동의되지 않은 포맷으로 작성한다. 노드-레벨 에이전트는 이러한 로그를 수집하고 취합을 위해 전달한다.

로깅 에이전트와 함께 사이드카 컨테이너 사용

다음 중 한 가지 방법으로 사이드카 컨테이너를 사용할 수 있다.

  • 사이드카 컨테이너는 애플리케이션 로그를 자체 stdout 으로 스트리밍한다.
  • 사이드카 컨테이너는 로깅 에이전트를 실행하며, 애플리케이션 컨테이너에서 로그를 가져오도록 구성된다.

사이드카 컨테이너 스트리밍

스트리밍 컨테이너가 있는 사이드카 컨테이너

사이드카 컨테이너가 자체 stdoutstderr 스트림으로 쓰도록 하면, 각 노드에서 이미 실행 중인 kubelet과 로깅 에이전트를 활용할 수 있다. 사이드카 컨테이너는 파일, 소켓 또는 journald에서 로그를 읽는다. 각 사이드카 컨테이너는 자체 stdout 또는 stderr 스트림에 로그를 출력한다.

이 방법을 사용하면 애플리케이션의 다른 부분에서 여러 로그 스트림을 분리할 수 ​​있고, 이 중 일부는 stdout 또는 stderr 에 작성하기 위한 지원이 부족할 수 있다. 로그를 리디렉션하는 로직은 최소화되어 있기 때문에, 심각한 오버헤드가 아니다. 또한, stdoutstderr 가 kubelet에서 처리되므로, kubectl logs 와 같은 빌트인 도구를 사용할 수 있다.

예를 들어, 파드는 단일 컨테이너를 실행하고, 컨테이너는 서로 다른 두 가지 형식을 사용하여 서로 다른 두 개의 로그 파일에 기록한다. 파드에 대한 구성 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

두 컴포넌트를 컨테이너의 stdout 스트림으로 리디렉션한 경우에도, 동일한 로그 스트림에 서로 다른 형식의 로그 항목을 작성하는 것은 추천하지 않는다. 대신, 두 개의 사이드카 컨테이너를 생성할 수 있다. 각 사이드카 컨테이너는 공유 볼륨에서 특정 로그 파일을 테일(tail)한 다음 로그를 자체 stdout 스트림으로 리디렉션할 수 있다.

다음은 사이드카 컨테이너가 두 개인 파드에 대한 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

이제 이 파드를 실행하면, 다음의 명령을 실행하여 각 로그 스트림에 개별적으로 접근할 수 있다.

kubectl logs counter count-log-1

출력은 다음과 같다.

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...
kubectl logs counter count-log-2

출력은 다음과 같다.

Mon Jan  1 00:00:00 UTC 2001 INFO 0
Mon Jan  1 00:00:01 UTC 2001 INFO 1
Mon Jan  1 00:00:02 UTC 2001 INFO 2
...

클러스터에 설치된 노드-레벨 에이전트는 추가 구성없이 자동으로 해당 로그 스트림을 선택한다. 원한다면, 소스 컨테이너에 따라 로그 라인을 파싱(parse)하도록 에이전트를 구성할 수 있다.

참고로, CPU 및 메모리 사용량이 낮음에도 불구하고(cpu에 대한 몇 밀리코어의 요구와 메모리에 대한 몇 메가바이트의 요구), 로그를 파일에 기록한 다음 stdout 으로 스트리밍하면 디스크 사용량은 두 배가 될 수 있다. 단일 파일에 쓰는 애플리케이션이 있는 경우, 일반적으로 스트리밍 사이드카 컨테이너 방식을 구현하는 대신 /dev/stdout 을 대상으로 설정하는 것을 추천한다.

사이드카 컨테이너를 사용하여 애플리케이션 자체에서 로테이션할 수 없는 로그 파일을 로테이션할 수도 있다. 이 방법의 예시는 정기적으로 logrotate 를 실행하는 작은 컨테이너를 두는 것이다. 그러나, stdoutstderr 을 직접 사용하고 로테이션과 유지 정책을 kubelet에 두는 것이 권장된다.

로깅 에이전트가 있는 사이드카 컨테이너

로깅 에이전트가 있는 사이드카 컨테이너

노드-레벨 로깅 에이전트가 상황에 맞게 충분히 유연하지 않은 경우, 애플리케이션과 함께 실행하도록 특별히 구성된 별도의 로깅 에이전트를 사용하여 사이드카 컨테이너를 생성할 수 있다.

여기에 로깅 에이전트가 포함된 사이드카 컨테이너를 구현하는 데 사용할 수 있는 두 가지 구성 파일이 있다. 첫 번째 파일에는 fluentd를 구성하기 위한 ConfigMap이 포함되어 있다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

두 번째 파일은 fluentd가 실행되는 사이드카 컨테이너가 있는 파드를 설명한다. 파드는 fluentd가 구성 데이터를 가져올 수 있는 볼륨을 마운트한다.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: k8s.gcr.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

이 예시 구성에서, 사용자는 애플리케이션 컨테이너 내의 모든 소스을 읽는 fluentd를 다른 로깅 에이전트로 대체할 수 있다.

애플리케이션에서 직접 로그 노출

애플리케이션에서 직접 로그 노출

애플리케이션에서 직접 로그를 노출하거나 푸시하는 클러스터-로깅은 쿠버네티스의 범위를 벗어난다.

3.11.5 - 시스템 로그

시스템 컴포넌트 로그는 클러스터에서 발생하는 이벤트를 기록하며, 이는 디버깅에 아주 유용하다. 더 많거나 적은 세부 정보를 표시하도록 다양하게 로그를 설정할 수 있다. 로그는 컴포넌트 내에서 오류를 표시하는 것 처럼 간단하거나, 이벤트의 단계적 추적(예: HTTP 엑세스 로그, 파드의 상태 변경, 컨트롤러 작업 또는 스케줄러의 결정)을 표시하는 것처럼 세밀할 수 있다.

Klog

klog는 쿠버네티스의 로깅 라이브러리다. klog 는 쿠버네티스 시스템 컴포넌트의 로그 메시지를 생성한다.

klog 설정에 대한 더 많은 정보는, 커맨드라인 툴을 참고한다.

klog 네이티브 형식 예 :

I1025 00:15:15.525108       1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]

구조화된 로깅

FEATURE STATE: Kubernetes v1.19 [alpha]

구조화된 로깅은 로그 메시지에 통일된 구조를 적용하여 정보를 쉽게 추출하고, 로그를 보다 쉽고 저렴하게 저장하고 처리하는 작업이다. 새로운 메시지 형식은 이전 버전과 호환되며 기본적으로 활성화 된다.

구조화된 로그 형식:

<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...

예시:

I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"

JSON 로그 형식

FEATURE STATE: Kubernetes v1.19 [alpha]

--logging-format=json 플래그는 로그 형식을 klog 기본 형식에서 JSON 형식으로 변경한다. JSON 로그 형식 예시(보기좋게 출력된 형태):

{
   "ts": 1580306777.04728,
   "v": 4,
   "msg": "Pod status updated",
   "pod":{
      "name": "nginx-1",
      "namespace": "default"
   },
   "status": "ready"
}

특별한 의미가 있는 키:

  • ts - Unix 시간의 타임스탬프 (필수, 부동 소수점)
  • v - 자세한 정도 (필수, 정수, 기본 값 0)
  • err - 오류 문자열 (선택 사항, 문자열)
  • msg - 메시지 (필수, 문자열)

현재 JSON 형식을 지원하는 컴포넌트 목록:

로그 정리(sanitization)

FEATURE STATE: Kubernetes v1.20 [alpha]

--experimental-logging-sanitization 플래그는 klog 정리(sanitization) 필터를 활성화 한다. 활성화된 경우 모든 로그 인자에서 민감한 데이터(예: 비밀번호, 키, 토큰)가 표시된 필드를 검사하고 이러한 필드의 로깅이 방지된다.

현재 로그 정리(sanitization)를 지원하는 컴포넌트 목록:

  • kube-controller-manager
  • kube-apiserver
  • kube-scheduler
  • kubelet

로그 상세 레벨(verbosity)

-v 플래그로 로그 상세 레벨(verbosity)을 제어한다. 값을 늘리면 기록된 이벤트 수가 증가한다. 값을 줄이면 기록된 이벤트 수가 줄어든다. 로그 상세 레벨(verbosity)를 높이면 점점 덜 심각한 이벤트가 기록된다. 로그 상세 레벨(verbosity)을 0으로 설정하면 중요한 이벤트만 기록된다.

로그 위치

시스템 컴포넌트에는 컨테이너에서 실행되는 것과 컨테이너에서 실행되지 않는 두 가지 유형이 있다. 예를 들면 다음과 같다.

  • 쿠버네티스 스케줄러와 kube-proxy는 컨테이너에서 실행된다.
  • kubelet과 컨테이너 런타임(예: 도커)은 컨테이너에서 실행되지 않는다.

systemd를 사용하는 시스템에서는, kubelet과 컨테이너 런타임은 jounald에 기록한다. 그 외 시스템에서는, /var/log 디렉터리의 .log 파일에 기록한다. 컨테이너 내부의 시스템 컴포넌트들은 기본 로깅 메커니즘을 무시하고, 항상 /var/log 디렉터리의 .log 파일에 기록한다. 컨테이너 로그와 마찬가지로, /var/log 디렉터리의 시스템 컴포넌트 로그들은 로테이트해야 한다. kube-up.sh 스크립트로 생성된 쿠버네티스 클러스터에서는, logrotate 도구로 로그가 로테이트되도록 설정된다. logrotate 도구는 로그가 매일 또는 크기가 100MB 보다 클 때 로테이트된다.

다음 내용

3.11.6 - 쿠버네티스 시스템 컴포넌트에 대한 메트릭

시스템 컴포넌트 메트릭으로 내부에서 발생하는 상황을 더 잘 파악할 수 있다. 메트릭은 대시보드와 경고를 만드는 데 특히 유용하다.

쿠버네티스 컴포넌트의 메트릭은 프로메테우스 형식으로 출력된다. 이 형식은 구조화된 평문으로 디자인되어 있으므로 사람과 기계 모두가 쉽게 읽을 수 있다.

쿠버네티스의 메트릭

대부분의 경우 메트릭은 HTTP 서버의 /metrics 엔드포인트에서 사용할 수 있다. 기본적으로 엔드포인트를 노출하지 않는 컴포넌트의 경우 --bind-address 플래그를 사용하여 활성화할 수 있다.

해당 컴포넌트의 예는 다음과 같다.

프로덕션 환경에서는 이러한 메트릭을 주기적으로 수집하고 시계열 데이터베이스에서 사용할 수 있도록 프로메테우스 서버 또는 다른 메트릭 수집기(scraper)를 구성할 수 있다.

참고로 kubelet/metrics/cadvisor, /metrics/resource 그리고 /metrics/probes 엔드포인트에서 메트릭을 노출한다. 이러한 메트릭은 동일한 라이프사이클을 가지지 않는다.

클러스터가 RBAC을 사용하는 경우, 메트릭을 읽으려면 /metrics 에 접근을 허용하는 클러스터롤(ClusterRole)을 가지는 사용자, 그룹 또는 서비스어카운트(ServiceAccount)를 통한 권한이 필요하다. 예를 들면, 다음과 같다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
  - nonResourceURLs:
      - "/metrics"
    verbs:
      - get

메트릭 라이프사이클

알파(Alpha) 메트릭 → 안정적인(Stable) 메트릭 → 사용 중단된(Deprecated) 메트릭 → 히든(Hidden) 메트릭 → 삭제된(Deleted) 메트릭

알파 메트릭은 안정성을 보장하지 않는다. 따라서 언제든지 수정되거나 삭제될 수 있다.

안정적인 메트릭은 변경되지 않는다는 것을 보장한다. 이것은 다음을 의미한다.

  • 사용 중단 표기가 없는 안정적인 메트릭은, 이름이 변경되거나 삭제되지 않는다.
  • 안정적인 메트릭의 유형(type)은 수정되지 않는다.

사용 중단된 메트릭은 해당 메트릭이 결국 삭제된다는 것을 나타내지만, 아직은 사용 가능하다는 뜻이다. 이 메트릭은 어느 버전에서부터 사용 중단된 것인지를 표시하는 어노테이션을 포함한다.

예를 들면,

  • 사용 중단 이전에는 다음과 같다.
# HELP some_counter this counts things
# TYPE some_counter counter
some_counter 0
  • 사용 중단 이후에는 다음과 같다.
# HELP some_counter (Deprecated since 1.15.0) this counts things
# TYPE some_counter counter
some_counter 0

히든 메트릭은 깔끔함(scraping)을 위해 더 이상 게시되지는 않지만, 여전히 사용은 가능하다. 히든 메트릭을 사용하려면, 히든 메트릭 표시 섹션을 참고한다.

삭제된 메트릭은 더 이상 게시되거나 사용할 수 없다.

히든 메트릭 표시

위에서 설명한 것처럼, 관리자는 특정 바이너리의 커맨드 라인 플래그를 통해 히든 메트릭을 활성화할 수 있다. 관리자가 지난 릴리스에서 사용 중단된 메트릭의 마이그레이션을 놓친 경우 관리자를 위한 임시방편으로 사용된다.

show-hidden-metrics-for-version 플래그는 해당 릴리스에서 사용 중단된 메트릭을 보여주려는 버전을 사용한다. 버전은 xy로 표시되며, 여기서 x는 메이저(major) 버전이고, y는 마이너(minor) 버전이다. 패치 릴리스에서 메트릭이 사용 중단될 수 있지만, 패치 버전은 필요하지 않다. 그 이유는 메트릭 사용 중단 정책이 마이너 릴리스에 대해 실행되기 때문이다.

플래그는 그 값으로 이전의 마이너 버전만 사용할 수 있다. 관리자가 이전 버전을 show-hidden-metrics-for-version 에 설정하면 이전 버전의 모든 히든 메트릭이 생성된다. 사용 중단 메트릭 정책을 위반하기 때문에 너무 오래된 버전은 허용되지 않는다.

1.n 버전에서 사용 중단되었다고 가정한 메트릭 A 를 예로 들어보겠다. 메트릭 사용 중단 정책에 따르면, 다음과 같은 결론에 도달할 수 있다.

  • 1.n 릴리스에서는 메트릭이 사용 중단되었으며, 기본적으로 생성될 수 있다.
  • 1.n+1 릴리스에서는 기본적으로 메트릭이 숨겨져 있으며, show-hidden-metrics-for-version=1.n 커맨드 라인에 의해서 생성될 수 있다.
  • 1.n+2 릴리스에서는 코드베이스에서 메트릭이 제거되어야 한다. 더이상 임시방편은 존재하지 않는다.

릴리스 1.12 에서 1.13 으로 업그레이드 중이지만, 1.12 에서 사용 중단된 메트릭 A 를 사용하고 있다면, 커맨드 라인에서 --show-hidden-metrics=1.12 플래그로 히든 메트릭을 설정해야 하고, 1.14 로 업그레이드하기 전에 이 메트릭을 사용하지 않도록 의존성을 제거하는 것을 기억해야 한다.

액셀러레이터 메트릭 비활성화

kubelet은 cAdvisor를 통해 액셀러레이터 메트릭을 수집한다. NVIDIA GPU와 같은 액셀러레이터의 경우, 이러한 메트릭을 수집하기 위해 kubelet은 드라이버에 열린 핸들을 가진다. 이는 인프라 변경(예: 드라이버 업데이트)을 수행하기 위해 클러스터 관리자가 kubelet 에이전트를 중지해야 함을 의미한다.

액셀러레이터 메트릭을 수집하는 책임은 이제 kubelet이 아닌 공급 업체에 있다. 공급 업체는 메트릭을 수집하여 메트릭 서비스(예: 프로메테우스)에 노출할 컨테이너를 제공해야 한다.

[DisableAcceleratorUsageMetrics 기능 게이트](/ko/docs/reference/command-line-tools-reference/feature-gates/#알파-또는-베타-기능을-위한-기능-게이트:~:text= DisableAcceleratorUsageMetrics,-false)는 이 기능을 기본적으로 사용하도록 설정하는 타임라인를 사용하여 kubelet에서 수집한 메트릭을 비활성화한다.

컴포넌트 메트릭

kube-controller-manager 메트릭

컨트롤러 관리자 메트릭은 컨트롤러 관리자의 성능과 상태에 대한 중요한 인사이트를 제공한다. 이러한 메트릭에는 go_routine 수와 같은 일반적인 Go 언어 런타임 메트릭과 etcd 요청 대기 시간 또는 Cloudprovider(AWS, GCE, OpenStack) API 대기 시간과 같은 컨트롤러 특정 메트릭이 포함되어 클러스터의 상태를 측정하는 데 사용할 수 있다.

쿠버네티스 1.7부터 GCE, AWS, Vsphere 및 OpenStack의 스토리지 운영에 대한 상세한 Cloudprovider 메트릭을 사용할 수 있다. 이 메트릭은 퍼시스턴트 볼륨 동작의 상태를 모니터링하는 데 사용할 수 있다.

예를 들어, GCE의 경우 이러한 메트릭을 다음과 같이 호출한다.

cloudprovider_gce_api_request_duration_seconds { request = "instance_list"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_insert"}
cloudprovider_gce_api_request_duration_seconds { request = "disk_delete"}
cloudprovider_gce_api_request_duration_seconds { request = "attach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "detach_disk"}
cloudprovider_gce_api_request_duration_seconds { request = "list_disk"}

kube-scheduler 메트릭

FEATURE STATE: Kubernetes v1.21 [beta]

스케줄러는 실행 중인 모든 파드의 요청(request)된 리소스와 요구되는 제한(limit)을 보고하는 선택적 메트릭을 노출한다. 이러한 메트릭은 용량 계획(capacity planning) 대시보드를 구축하고, 현재 또는 과거 스케줄링 제한을 평가하고, 리소스 부족으로 스케줄할 수 없는 워크로드를 빠르게 식별하고, 실제 사용량을 파드의 요청과 비교하는 데 사용할 수 있다.

kube-scheduler는 각 파드에 대해 구성된 리소스 요청과 제한을 식별한다. 요청 또는 제한이 0이 아닌 경우 kube-scheduler는 메트릭 시계열을 보고한다. 시계열에는 다음과 같은 레이블이 지정된다.

  • 네임스페이스
  • 파드 이름
  • 파드가 스케줄된 노드 또는 아직 스케줄되지 않은 경우 빈 문자열
  • 우선순위
  • 해당 파드에 할당된 스케줄러
  • 리소스 이름 (예: cpu)
  • 알려진 경우 리소스 단위 (예: cores)

파드가 완료되면 (Never 또는 OnFailurerestartPolicy가 있고 Succeeded 또는 Failed 파드 단계에 있거나, 삭제되고 모든 컨테이너가 종료된 상태에 있음) 스케줄러가 이제 다른 파드를 실행하도록 스케줄할 수 있으므로 시리즈가 더 이상 보고되지 않는다. 두 메트릭을 kube_pod_resource_requestkube_pod_resource_limit 라고 한다.

메트릭은 HTTP 엔드포인트 /metrics/resources에 노출되며 스케줄러의 /metrics 엔드포인트 와 동일한 인증이 필요하다. 이러한 알파 수준의 메트릭을 노출시키려면 --show-hidden-metrics-for-version=1.20 플래그를 사용해야 한다.

메트릭 비활성화

커맨드 라인 플래그 --disabled-metrics 를 통해 메트릭을 명시적으로 끌 수 있다. 이 방법이 필요한 이유는 메트릭이 성능 문제를 일으키는 경우을 예로 들 수 있다. 입력값은 비활성화되는 메트릭 목록이다(예: --disabled-metrics=metric1,metric2).

메트릭 카디널리티(cardinality) 적용

제한되지 않은 차원의 메트릭은 계측하는 컴포넌트에서 메모리 문제를 일으킬 수 있다. 리소스 사용을 제한하려면, --allow-label-value 커맨드 라인 옵션을 사용하여 메트릭 항목에 대한 레이블 값의 허용 목록(allow-list)을 동적으로 구성한다.

알파 단계에서, 플래그는 메트릭 레이블 허용 목록으로 일련의 매핑만 가져올 수 있다. 각 매핑은 <metric_name>,<label_name>=<allowed_labels> 형식이다. 여기서 <allowed_labels> 는 허용되는 레이블 이름의 쉼표로 구분된 목록이다.

전체 형식은 다음과 같다. --allow-label-value <metric_name>,<label_name>='<allow_value1>, <allow_value2>...', <metric_name2>,<label_name>='<allow_value1>, <allow_value2>...', ....

예시는 다음과 같다. --allow-label-value number_count_metric,odd_number='1,3,5', number_count_metric,even_number='2,4,6', date_gauge_metric,weekend='Saturday,Sunday'

다음 내용

3.11.7 - kubelet 가비지(Garbage) 수집 설정하기

가비지 수집은 사용되지 않는 이미지들과 컨테이너들을 정리하는 kubelet의 유용한 기능이다. Kubelet은 1분마다 컨테이너들에 대하여 가비지 수집을 수행하며, 5분마다 이미지들에 대하여 가비지 수집을 수행한다.

별도의 가비지 수집 도구들을 사용하는 것은, 이러한 도구들이 존재할 수도 있는 컨테이너들을 제거함으로써 kubelet을 중단시킬 수도 있으므로 권장하지 않는다.

이미지 수집

쿠버네티스는 cadvisor와 imageManager를 통하여 모든 이미지들의 라이프사이클을 관리한다.

이미지들에 대한 가비지 수집 정책은 다음의 2가지 요소를 고려한다. HighThresholdPercentLowThresholdPercent. 임계값을 초과하는 디스크 사용량은 가비지 수집을 트리거 한다. 가비지 수집은 낮은 입계값에 도달 할 때까지 최근에 가장 적게 사용한 이미지들을 삭제한다.

컨테이너 수집

컨테이너에 대한 가비지 수집 정책은 세 가지 사용자 정의 변수들을 고려한다. MinAge 는 컨테이너를 가비지 수집할 수 있는 최소 연령이다. MaxPerPodContainer 는 모든 단일 파드(UID, 컨테이너 이름) 쌍이 가질 수 있는 최대 비활성 컨테이너의 수량이다. MaxContainers 는 죽은 컨테이너의 최대 수량이다. 이러한 변수는 MinAge 를 0으로 설정하고, MaxPerPodContainerMaxContainers 를 각각 0 보다 작게 설정해서 비활성화할 수 있다.

Kubelet은 미확인, 삭제 또는 앞에서 언급한 플래그가 설정한 경계를 벗어나거나, 확인되지 않은 컨테이너에 대해 조치를 취한다. 일반적으로 가장 오래된 컨테이너가 먼저 제거된다. MaxPerPodContainerMaxContainer 는 파드 당 최대 컨테이너 수(MaxPerPodContainer)가 허용 가능한 범위의 전체 죽은 컨테이너의 수(MaxContainers)를 벗어나는 상황에서 잠재적으로 서로 충돌할 수 있다. 다음의 상황에서 MaxPerPodContainer 가 조정된다. 최악의 시나리오는 MaxPerPodContainer 를 1로 다운그레이드하고 가장 오래된 컨테이너를 제거하는 것이다. 추가로, 삭제된 파드가 소유한 컨테이너는 MinAge 보다 오래되면 제거된다.

kubelet이 관리하지 않는 컨테이너는 컨테이너 가비지 수집 대상이 아니다.

사용자 설정

여러분은 후술될 kubelet 플래그들을 통하여 이미지 가비지 수집을 조정하기 위하여 다음의 임계값을 조정할 수 있다.

  1. image-gc-high-threshold, 이미지 가비지 수집을 발생시키는 디스크 사용량의 비율로 기본값은 85% 이다.
  2. image-gc-low-threshold, 이미지 가비지 수집을 더 이상 시도하지 않는 디스크 사용량의 비율로 기본값은 80% 이다.

다음의 kubelet 플래그를 통해 가비지 수집 정책을 사용자 정의할 수 있다.

  1. minimum-container-ttl-duration, 종료된 컨테이너가 가비지 수집 되기 전의 최소 시간. 기본 값은 0 분이며, 이 경우 모든 종료된 컨테이너는 바로 가비지 수집의 대상이 된다.
  2. maximum-dead-containers-per-container, 컨테이너가 보유할 수 있는 오래된 인스턴스의 최대 수. 기본 값은 1 이다.
  3. maximum-dead-containers, 글로벌하게 보유 할 컨테이너의 최대 오래된 인스턴스의 최대 수. 기본 값은 -1이며, 이 경우 인스턴스 수의 제한은 없다.

컨테이너들은 유용성이 만료되기 이전에도 가비지 수집이 될 수 있다. 이러한 컨테이너들은 문제 해결에 도움이 될 수 있는 로그나 다른 데이터를 포함하고 있을 수 있다. 컨테이너 당 적어도 1개의 죽은 컨테이너가 허용될 수 있도록 maximum-dead-containers-per-container 값을 충분히 큰 값으로 지정하는 것을 권장한다. 동일한 이유로 maximum-dead-containers 의 값도 상대적으로 더 큰 값을 권장한다. 자세한 내용은 해당 이슈를 참고한다.

사용 중단(Deprecation)

문서에 있는 몇 가지 kubelet의 가비지 수집 특징은 향후에 kubelet 축출(eviction) 기능으로 대체될 예정이다.

포함:

기존 Flag 신규 Flag 근거
--image-gc-high-threshold --eviction-hard or --eviction-soft 기존의 축출 신호로 인하여 이미지 가비지 수집이 트리거 될 수 있음
--image-gc-low-threshold --eviction-minimum-reclaim 축출 리클레임 기능이 동일한 행동을 수행
--maximum-dead-containers 컨테이너의 외부 영역에 오래된 로그가 저장되어 사용중단(deprecated)됨
--maximum-dead-containers-per-container 컨테이너의 외부 영역에 오래된 로그가 저장되어 사용중단(deprecated)됨
--minimum-container-ttl-duration 컨테이너의 외부 영역에 오래된 로그가 저장되어 사용중단(deprecated)됨
--low-diskspace-threshold-mb --eviction-hard or eviction-soft 축출이 다른 리소스에 대한 디스크 임계값을 일반화 함
--outofdisk-transition-frequency --eviction-pressure-transition-period 축출이 다른 리소스로의 디스크 압력전환을 일반화 함

다음 내용

자세한 내용은 리소스 부족 처리 구성를 본다.

3.11.8 - 쿠버네티스에서 프락시(Proxy)

이 페이지는 쿠버네티스에서 함께 사용되는 프락시(Proxy)를 설명한다.

프락시

쿠버네티스를 이용할 때에 사용할 수 있는 여러 프락시가 있다.

  1. kubectl proxy:

    • 사용자의 데스크탑이나 파드 안에서 실행한다.
    • 로컬 호스트 주소에서 쿠버네티스의 API 서버로 프락시한다.
    • 클라이언트로 프락시는 HTTP를 사용한다.
    • API 서버로 프락시는 HTTPS를 사용한다.
    • API 서버를 찾는다.
    • 인증 헤더를 추가한다.
  2. apiserver proxy:

    • API 서버에 내장된 요새(bastion)이다.
    • 클러스터 외부의 사용자가 도달할 수 없는 클러스터 IP 주소로 연결한다.
    • API 서버 프로세스에서 실행한다.
    • 클라이언트로 프락시는 HTTPS(또는 API서버에서 HTTP로 구성된 경우는 HTTP)를 사용한다.
    • 사용 가능한 정보를 이용하여 프락시에 의해 선택한 HTTP나 HTTPS를 사용할 수 있는 대상이다.
    • 노드, 파드, 서비스에 도달하는데 사용할 수 있다.
    • 서비스에 도달할 때에는 로드 밸런싱을 수행한다.
  3. kube proxy:

    • 각 노드에서 실행한다.
    • UDP, TCP, SCTP를 이용하여 프락시 한다.
    • HTTP는 이해하지 못한다.
    • 로드 밸런싱을 제공한다.
    • 서비스에 도달하는데만 사용한다.
  4. API 서버 앞단의 프락시/로드밸런서

    • 존재 및 구현은 클러스터 마다 다르다. (예: nginx)
    • 모든 클라이언트와 하나 이상의 API 서버에 위치한다.
    • 여러 API 서버가 있는 경우 로드 밸런서로서 작동한다.
  5. 외부 서비스의 클라우드 로드 밸런서

    • 일부 클라우드 제공자는 제공한다. (예: AWS ELB, 구글 클라우드 로드 밸런서)
    • 쿠버네티스 서비스로 LoadBalancer 유형이 있으면 자동으로 생성된다.
    • 일반적으로 UDP/TCP만 지원한다.
    • SCTP 지원은 클라우드 제공자의 구현에 달려 있다.
    • 구현은 클라우드 제공자에 따라 다양하다.

쿠버네티스 사용자는 보통 처음 두 가지 유형 외의 것은 걱정할 필요없다. 클러스터 관리자는 일반적으로 후자의 유형이 올바르게 구성되었는지 확인한다.

요청을 리다이렉트하기

프락시는 리다이렉트 기능을 대체했다. 리다이렉트는 더 이상 사용하지 않는다.

3.11.9 - 애드온 설치

애드온은 쿠버네티스의 기능을 확장한다.

이 페이지는 사용 가능한 일부 애드온과 관련 설치 지침 링크를 나열한다.

네트워킹과 네트워크 폴리시

  • ACI는 Cisco ACI로 통합 컨테이너 네트워킹 및 네트워크 보안을 제공한다.
  • Antrea는 레이어 3/4에서 작동하여 쿠버네티스를 위한 네트워킹 및 보안 서비스를 제공하며, Open vSwitch를 네트워킹 데이터 플레인으로 활용한다.
  • Calico는 네트워킹 및 네트워크 폴리시 제공자이다. Calico는 유연한 네트워킹 옵션을 지원하므로 BGP 유무에 관계없이 비-오버레이 및 오버레이 네트워크를 포함하여 가장 상황에 맞는 옵션을 선택할 수 있다. Calico는 동일한 엔진을 사용하여 서비스 메시 계층(service mesh layer)에서 호스트, 파드 및 (이스티오(istio)와 Envoy를 사용하는 경우) 애플리케이션에 대한 네트워크 폴리시를 적용한다.
  • Canal은 Flannel과 Calico를 통합하여 네트워킹 및 네트워크 폴리시를 제공한다.
  • Cilium은 L3 네트워크 및 네트워크 폴리시 플러그인으로 HTTP/API/L7 폴리시를 투명하게 시행할 수 있다. 라우팅 및 오버레이/캡슐화 모드를 모두 지원하며, 다른 CNI 플러그인 위에서 작동할 수 있다.
  • CNI-Genie를 사용하면 쿠버네티스는 Calico, Canal, Flannel, Romana 또는 Weave와 같은 CNI 플러그인을 완벽하게 연결할 수 있다.
  • Contiv는 다양한 유스케이스와 풍부한 폴리시 프레임워크를 위해 구성 가능한 네트워킹(BGP를 사용하는 네이티브 L3, vxlan을 사용하는 오버레이, 클래식 L2 그리고 Cisco-SDN/ACI)을 제공한다. Contiv 프로젝트는 완전히 오픈소스이다. 인스톨러는 kubeadm을 이용하거나, 그렇지 않은 경우에 대해서도 설치 옵션을 모두 제공한다.
  • ContrailTungsten Fabric을 기반으로 하며, 오픈소스이고, 멀티 클라우드 네트워크 가상화 및 폴리시 관리 플랫폼이다. Contrail과 Tungsten Fabric은 쿠버네티스, OpenShift, OpenStack 및 Mesos와 같은 오케스트레이션 시스템과 통합되어 있으며, 가상 머신, 컨테이너/파드 및 베어 메탈 워크로드에 대한 격리 모드를 제공한다.
  • Flannel은 쿠버네티스와 함께 사용할 수 있는 오버레이 네트워크 제공자이다.
  • Knitter는 쿠버네티스 파드에서 여러 네트워크 인터페이스를 지원하는 플러그인이다.
  • Multus는 쿠버네티스에서 SRIOV, DPDK, OVS-DPDK 및 VPP 기반 워크로드 외에 모든 CNI 플러그인(예: Calico, Cilium, Contiv, Flannel)을 지원하기 위해 쿠버네티스에서 다중 네트워크 지원을 위한 멀티 플러그인이다.
  • OVN-Kubernetes는 Open vSwitch(OVS) 프로젝트에서 나온 가상 네트워킹 구현인 OVN(Open Virtual Network)을 기반으로 하는 쿠버네티스용 네트워킹 제공자이다. OVN-Kubernetes는 OVS 기반 로드 밸런싱과 네트워크 폴리시 구현을 포함하여 쿠버네티스용 오버레이 기반 네트워킹 구현을 제공한다.
  • OVN4NFV-K8S-Plugin은 OVN 기반의 CNI 컨트롤러 플러그인으로 클라우드 네이티브 기반 서비스 기능 체인(Service function chaining(SFC)), 다중 OVN 오버레이 네트워킹, 동적 서브넷 생성, 동적 가상 네트워크 생성, VLAN 공급자 네트워크, 직접 공급자 네트워크와 멀티 클러스터 네트워킹의 엣지 기반 클라우드 등 네이티브 워크로드에 이상적인 멀티 네티워크 플러그인이다.
  • NSX-T 컨테이너 플러그인(NCP)은 VMware NSX-T와 쿠버네티스와 같은 컨테이너 오케스트레이터 간의 통합은 물론 NSX-T와 PKS(Pivotal 컨테이너 서비스) 및 OpenShift와 같은 컨테이너 기반 CaaS/PaaS 플랫폼 간의 통합을 제공한다.
  • Nuage는 가시성과 보안 모니터링 기능을 통해 쿠버네티스 파드와 비-쿠버네티스 환경 간에 폴리시 기반 네트워킹을 제공하는 SDN 플랫폼이다.
  • Romana네트워크폴리시 API도 지원하는 파드 네트워크용 Layer 3 네트워킹 솔루션이다. Kubeadm 애드온 설치에 대한 세부 정보는 여기에 있다.
  • Weave Net은 네트워킹 및 네트워크 폴리시를 제공하고, 네트워크 파티션의 양면에서 작업을 수행하며, 외부 데이터베이스는 필요하지 않다.

서비스 검색

  • CoreDNS는 유연하고 확장 가능한 DNS 서버로, 파드를 위한 클러스터 내 DNS로 설치할 수 있다.

시각화 & 제어

  • 대시보드는 쿠버네티스를 위한 대시보드 웹 인터페이스이다.
  • Weave Scope는 컨테이너, 파드, 서비스 등을 그래픽으로 시각화하는 도구이다. Weave Cloud 어카운트와 함께 사용하거나 UI를 직접 호스팅한다.

인프라스트럭처

  • KubeVirt는 쿠버네티스에서 가상 머신을 실행하기 위한 애드온이다. 일반적으로 베어 메탈 클러스터에서 실행한다.

레거시 애드온

더 이상 사용되지 않는 cluster/addons 디렉터리에 다른 여러 애드온이 문서화되어 있다.

잘 관리된 것들이 여기에 연결되어 있어야 한다. PR을 환영한다!

3.12 - 쿠버네티스 확장

쿠버네티스 클러스터의 동작을 변경하는 다양한 방법

쿠버네티스는 매우 유연하게 구성할 수 있고 확장 가능하다. 결과적으로 쿠버네티스 프로젝트를 포크하거나 코드에 패치를 제출할 필요가 거의 없다.

이 가이드는 쿠버네티스 클러스터를 사용자 정의하기 위한 옵션을 설명한다. 쿠버네티스 클러스터를 업무 환경의 요구에 맞게 조정하는 방법을 이해하려는 클러스터 운영자를 대상으로 한다. 잠재적인 플랫폼 개발자 또는 쿠버네티스 프로젝트 컨트리뷰터인 개발자에게도 어떤 익스텐션(extension) 포인트와 패턴이 있는지, 그리고 그것의 트레이드오프와 제약을 이해하는 데 도움이 될 것이다.

개요

사용자 정의 방식은 크게 플래그, 로컬 구성 파일 또는 API 리소스 변경만 포함하는 구성 과 추가 프로그램이나 서비스 실행과 관련된 익스텐션 으로 나눌 수 있다. 이 문서는 주로 익스텐션에 관한 것이다.

구성

구성 파일플래그 는 온라인 문서의 레퍼런스 섹션에 각 바이너리 별로 문서화되어 있다.

호스팅된 쿠버네티스 서비스 또는 매니지드 설치 환경의 배포판에서 플래그 및 구성 파일을 항상 변경할 수 있는 것은 아니다. 변경 가능한 경우 일반적으로 클러스터 관리자만 변경할 수 있다. 또한 향후 쿠버네티스 버전에서 변경될 수 있으며, 이를 설정하려면 프로세스를 다시 시작해야 할 수도 있다. 이러한 이유로 다른 옵션이 없는 경우에만 사용해야 한다.

리소스쿼터, 파드시큐리티폴리시(PodSecurityPolicy), 네트워크폴리시 및 역할 기반 접근 제어(RBAC)와 같은 빌트인 정책 API(built-in Policy API) 는 기본적으로 제공되는 쿠버네티스 API이다. API는 일반적으로 호스팅된 쿠버네티스 서비스 및 매니지드 쿠버네티스 설치 환경과 함께 사용된다. 그것들은 선언적이며 파드와 같은 다른 쿠버네티스 리소스와 동일한 규칙을 사용하므로, 새로운 클러스터 구성을 반복할 수 있고 애플리케이션과 동일한 방식으로 관리할 수 있다. 또한, 이들 API가 안정적인 경우, 다른 쿠버네티스 API와 같이 정의된 지원 정책을 사용할 수 있다. 이러한 이유로 인해 구성 파일플래그 보다 선호된다.

익스텐션

익스텐션은 쿠버네티스를 확장하고 쿠버네티스와 긴밀하게 통합되는 소프트웨어 컴포넌트이다. 이들 컴포넌트는 쿠버네티스가 새로운 유형과 새로운 종류의 하드웨어를 지원할 수 있게 해준다.

많은 클러스터 관리자가 호스팅 또는 배포판 쿠버네티스 인스턴스를 사용한다. 이러한 클러스터들은 미리 설치된 익스텐션을 포함한다. 결과적으로 대부분의 쿠버네티스 사용자는 익스텐션을 설치할 필요가 없고, 새로운 익스텐션을 만들 필요가 있는 사용자는 더 적다.

익스텐션 패턴

쿠버네티스는 클라이언트 프로그램을 작성하여 자동화 되도록 설계되었다. 쿠버네티스 API를 읽고 쓰는 프로그램은 유용한 자동화를 제공할 수 있다. 자동화 는 클러스터 상에서 또는 클러스터 밖에서 실행할 수 있다. 이 문서의 지침에 따라 고가용성과 강력한 자동화를 작성할 수 있다. 자동화는 일반적으로 호스트 클러스터 및 매니지드 설치 환경을 포함한 모든 쿠버네티스 클러스터에서 작동한다.

쿠버네티스와 잘 작동하는 클라이언트 프로그램을 작성하기 위한 특정 패턴은 컨트롤러 패턴이라고 한다. 컨트롤러는 일반적으로 오브젝트의 .spec을 읽고, 가능한 경우 수행한 다음 오브젝트의 .status를 업데이트 한다.

컨트롤러는 쿠버네티스의 클라이언트이다. 쿠버네티스가 클라이언트이고 원격 서비스를 호출할 때 이를 웹훅(Webhook) 이라고 한다. 원격 서비스를 웹훅 백엔드 라고 한다. 컨트롤러와 마찬가지로 웹훅은 장애 지점을 추가한다.

웹훅 모델에서 쿠버네티스는 원격 서비스에 네트워크 요청을 한다. 바이너리 플러그인 모델에서 쿠버네티스는 바이너리(프로그램)를 실행한다. 바이너리 플러그인은 kubelet(예: Flex Volume 플러그인네트워크 플러그인)과 kubectl에서 사용한다.

아래는 익스텐션 포인트가 쿠버네티스 컨트롤 플레인과 상호 작용하는 방법을 보여주는 다이어그램이다.

익스텐션 포인트와 컨트롤 플레인

익스텐션 포인트

이 다이어그램은 쿠버네티스 시스템의 익스텐션 포인트를 보여준다.

익스텐션 포인트

  1. 사용자는 종종 kubectl을 사용하여 쿠버네티스 API와 상호 작용한다. Kubectl 플러그인은 kubectl 바이너리를 확장한다. 개별 사용자의 로컬 환경에만 영향을 미치므로 사이트 전체 정책을 적용할 수는 없다.
  2. apiserver는 모든 요청을 처리한다. apiserver의 여러 유형의 익스텐션 포인트는 요청을 인증하거나, 콘텐츠를 기반으로 요청을 차단하거나, 콘텐츠를 편집하고, 삭제 처리를 허용한다. 이 내용은 API 접근 익스텐션 섹션에 설명되어 있다.
  3. apiserver는 다양한 종류의 리소스 를 제공한다. pods와 같은 빌트인 리소스 종류 는 쿠버네티스 프로젝트에 의해 정의되며 변경할 수 없다. 직접 정의한 리소스를 추가할 수도 있고, 커스텀 리소스 섹션에 설명된 대로 커스텀 리소스 라고 부르는 다른 프로젝트에서 정의한 리소스를 추가할 수도 있다. 커스텀 리소스는 종종 API 접근 익스텐션과 함께 사용된다.
  4. 쿠버네티스 스케줄러는 파드를 배치할 노드를 결정한다. 스케줄링을 확장하는 몇 가지 방법이 있다. 이들은 스케줄러 익스텐션 섹션에 설명되어 있다.
  5. 쿠버네티스의 많은 동작은 API-Server의 클라이언트인 컨트롤러(Controller)라는 프로그램으로 구현된다. 컨트롤러는 종종 커스텀 리소스와 함께 사용된다.
  6. kubelet은 서버에서 실행되며 파드가 클러스터 네트워크에서 자체 IP를 가진 가상 서버처럼 보이도록 한다. 네트워크 플러그인을 사용하면 다양한 파드 네트워킹 구현이 가능하다.
  7. kubelet은 컨테이너의 볼륨을 마운트 및 마운트 해제한다. 새로운 유형의 스토리지는 스토리지 플러그인을 통해 지원될 수 있다.

어디서부터 시작해야 할지 모르겠다면, 이 플로우 차트가 도움이 될 수 있다. 일부 솔루션에는 여러 유형의 익스텐션이 포함될 수 있다.

익스텐션 플로우차트

API 익스텐션

사용자 정의 유형

새 컨트롤러, 애플리케이션 구성 오브젝트 또는 기타 선언적 API를 정의하고 kubectl 과 같은 쿠버네티스 도구를 사용하여 관리하려면 쿠버네티스에 커스텀 리소스를 추가하자.

애플리케이션, 사용자 또는 모니터링 데이터의 데이터 저장소로 커스텀 리소스를 사용하지 않는다.

커스텀 리소스에 대한 자세한 내용은 커스텀 리소스 개념 가이드를 참고하길 바란다.

새로운 API와 자동화의 결합

사용자 정의 리소스 API와 컨트롤 루프의 조합을 오퍼레이터(operator) 패턴이라고 한다. 오퍼레이터 패턴은 특정 애플리케이션, 일반적으로 스테이트풀(stateful) 애플리케이션을 관리하는 데 사용된다. 이러한 사용자 정의 API 및 컨트롤 루프를 사용하여 스토리지나 정책과 같은 다른 리소스를 제어할 수도 있다.

빌트인 리소스 변경

사용자 정의 리소스를 추가하여 쿠버네티스 API를 확장하면 추가된 리소스는 항상 새로운 API 그룹에 속한다. 기존 API 그룹을 바꾸거나 변경할 수 없다. API를 추가해도 기존 API(예: 파드)의 동작에 직접 영향을 미치지는 않지만 API 접근 익스텐션은 영향을 준다.

API 접근 익스텐션

요청이 쿠버네티스 API 서버에 도달하면 먼저 인증이 되고, 그런 다음 승인된 후 다양한 유형의 어드미션 컨트롤이 적용된다. 이 흐름에 대한 자세한 내용은 쿠버네티스 API에 대한 접근 제어를 참고하길 바란다.

이러한 각 단계는 익스텐션 포인트를 제공한다.

쿠버네티스에는 이를 지원하는 몇 가지 빌트인 인증 방법이 있다. 또한 인증 프록시 뒤에 있을 수 있으며 인증 헤더에서 원격 서비스로 토큰을 전송하여 확인할 수 있다(웹훅). 이러한 방법은 모두 인증 설명서에 설명되어 있다.

인증

인증은 모든 요청의 헤더 또는 인증서를 요청하는 클라이언트의 사용자 이름에 매핑한다.

쿠버네티스는 몇 가지 빌트인 인증 방법과 필요에 맞지 않는 경우 인증 웹훅 방법을 제공한다.

인가

인가는 특정 사용자가 API 리소스에서 읽고, 쓰고, 다른 작업을 수행할 수 있는지를 결정한다. 전체 리소스 레벨에서 작동하며 임의의 오브젝트 필드를 기준으로 구별하지 않는다. 빌트인 인증 옵션이 사용자의 요구를 충족시키지 못하면 인가 웹훅을 통해 사용자가 제공한 코드를 호출하여 인증 결정을 내릴 수 있다.

동적 어드미션 컨트롤

요청이 승인된 후, 쓰기 작업인 경우 어드미션 컨트롤 단계도 수행된다. 빌트인 단계 외에도 몇 가지 익스텐션이 있다.

  • 이미지 정책 웹훅은 컨테이너에서 실행할 수 있는 이미지를 제한한다.
  • 임의의 어드미션 컨트롤 결정을 내리기 위해 일반적인 어드미션 웹훅을 사용할 수 있다. 어드미션 웹훅은 생성 또는 업데이트를 거부할 수 있다.

인프라스트럭처 익스텐션

스토리지 플러그인

Flex Volumes을 사용하면 Kubelet이 바이너리 플러그인을 호출하여 볼륨을 마운트하도록 함으로써 빌트인 지원 없이 볼륨 유형을 마운트 할 수 있다.

장치 플러그인

장치 플러그인은 노드가 장치 플러그인을 통해 새로운 노드 리소스(CPU 및 메모리와 같은 빌트인 자원 외에)를 발견할 수 있게 해준다.

네트워크 플러그인

노드-레벨의 네트워크 플러그인 을 통해 다양한 네트워킹 패브릭을 지원할 수 있다.

스케줄러 익스텐션

스케줄러는 파드를 감시하고 파드를 노드에 할당하는 특수한 유형의 컨트롤러이다. 다른 쿠버네티스 컴포넌트를 계속 사용하면서 기본 스케줄러를 완전히 교체하거나, 여러 스케줄러를 동시에 실행할 수 있다.

이것은 중요한 부분이며, 거의 모든 쿠버네티스 사용자는 스케줄러를 수정할 필요가 없다는 것을 알게 된다.

스케줄러는 또한 웹훅 백엔드(스케줄러 익스텐션)가 파드에 대해 선택된 노드를 필터링하고 우선 순위를 지정할 수 있도록 하는 웹훅을 지원한다.

다음 내용

3.12.1 - 쿠버네티스 API 확장하기

3.12.1.1 - 커스텀 리소스

커스텀 리소스 는 쿠버네티스 API의 익스텐션이다. 이 페이지에서는 쿠버네티스 클러스터에 커스텀 리소스를 추가할 시기와 독립형 서비스를 사용하는 시기에 대해 설명한다. 커스텀 리소스를 추가하는 두 가지 방법과 이들 중에서 선택하는 방법에 대해 설명한다.

커스텀 리소스

리소스쿠버네티스 API에서 특정 종류의 API 오브젝트 모음을 저장하는 엔드포인트이다. 예를 들어 빌트인 파드 리소스에는 파드 오브젝트 모음이 포함되어 있다.

커스텀 리소스 는 쿠버네티스 API의 익스텐션으로, 기본 쿠버네티스 설치에서 반드시 사용할 수 있는 것은 아니다. 이는 특정 쿠버네티스 설치에 수정이 가해졌음을 나타낸다. 그러나 많은 핵심 쿠버네티스 기능은 이제 커스텀 리소스를 사용하여 구축되어, 쿠버네티스를 더욱 모듈화한다.

동적 등록을 통해 실행 중인 클러스터에서 커스텀 리소스가 나타나거나 사라질 수 있으며 클러스터 관리자는 클러스터 자체와 독립적으로 커스텀 리소스를 업데이트 할 수 있다. 커스텀 리소스가 설치되면 사용자는 파드 와 같은 빌트인 리소스와 마찬가지로 kubectl을 사용하여 해당 오브젝트를 생성하고 접근할 수 있다.

커스텀 컨트롤러

자체적으로 커스텀 리소스를 사용하면 구조화된 데이터를 저장하고 검색할 수 있다. 커스텀 리소스를 커스텀 컨트롤러 와 결합하면, 커스텀 리소스가 진정한 선언적(declarative) API 를 제공하게 된다.

쿠버네티스 선언적 API는 책임의 분리를 강제한다. 사용자는 리소스의 의도한 상태를 선언한다. 쿠버네티스 컨트롤러는 쿠버네티스 오브젝트의 현재 상태가 선언한 의도한 상태에 동기화 되도록 한다. 이는 서버에 무엇을 해야할지 지시하는 명령적인 API와는 대조된다.

클러스터 라이프사이클과 관계없이 실행 중인 클러스터에 커스텀 컨트롤러를 배포하고 업데이트할 수 있다. 커스텀 컨트롤러는 모든 종류의 리소스와 함께 작동할 수 있지만 커스텀 리소스와 결합할 때 특히 효과적이다. 오퍼레이터 패턴은 사용자 정의 리소스와 커스텀 컨트롤러를 결합한다. 커스텀 컨트롤러를 사용하여 특정 애플리케이션에 대한 도메인 지식을 쿠버네티스 API의 익스텐션으로 인코딩할 수 있다.

쿠버네티스 클러스터에 커스텀 리소스를 추가해야 하나?

새로운 API를 생성할 때 쿠버네티스 클러스터 API와 생성한 API를 애그리게이트할 것인지 아니면 생성한 API를 독립적으로 유지할 것인지 고려하자.

API 애그리게이트를 고려할 경우 독립 API를 고려할 경우
API가 선언적이다. API가 선언적 모델에 맞지 않다.
kubectl을 사용하여 새로운 타입을 읽고 쓸 수 있기를 원한다. kubectl 지원이 필요하지 않다.
쿠버네티스 UI(예: 대시보드)에서 빌트인 타입과 함께 새로운 타입을 보길 원한다. 쿠버네티스 UI 지원이 필요하지 않다.
새로운 API를 개발 중이다. 생성한 API를 제공하는 프로그램이 이미 있고 잘 작동하고 있다.
API 그룹 및 네임스페이스와 같은 REST 리소스 경로에 적용하는 쿠버네티스의 형식 제한을 기꺼이 수용한다. (API 개요를 참고한다.) 이미 정의된 REST API와 호환되도록 특정 REST 경로가 있어야 한다.
자체 리소스는 자연스럽게 클러스터 또는 클러스터의 네임스페이스로 범위가 지정된다. 클러스터 또는 네임스페이스 범위의 리소스는 적합하지 않다. 특정 리소스 경로를 제어해야 한다.
쿠버네티스 API 지원 기능을 재사용하려고 한다. 이러한 기능이 필요하지 않다.

선언적 API

선언적 API에서는 다음의 특성이 있다.

  • API는 상대적으로 적은 수의 상대적으로 작은 오브젝트(리소스)로 구성된다.
  • 오브젝트는 애플리케이션 또는 인프라의 구성을 정의한다.
  • 오브젝트는 비교적 드물게 업데이트 된다.
  • 사람이 종종 오브젝트를 읽고 쓸 필요가 있다.
  • 오브젝트의 주요 작업은 CRUD-y(생성, 읽기, 업데이트 및 삭제)이다.
  • 오브젝트 간 트랜잭션은 필요하지 않다. API는 정확한(exact) 상태가 아니라 의도한 상태를 나타낸다.

명령형 API는 선언적이지 않다. 자신의 API가 선언적이지 않을 수 있다는 징후는 다음과 같다.

  • 클라이언트는 "이 작업을 수행한다"라고 말하고 완료되면 동기(synchronous) 응답을 받는다.
  • 클라이언트는 "이 작업을 수행한다"라고 말한 다음 작업 ID를 다시 가져오고 별도의 오퍼레이션(operation) 오브젝트를 확인하여 요청의 완료 여부를 결정해야 한다.
  • RPC(원격 프로시저 호출)에 대해 얘기한다.
  • 대량의 데이터를 직접 저장한다. 예: > 오브젝트별 몇 kB 또는 > 1000개의 오브젝트.
  • 높은 대역폭 접근(초당 10개의 지속적인 요청)이 필요하다.
  • 최종 사용자 데이터(예: 이미지, PII 등) 또는 애플리케이션에서 처리한 기타 대규모 데이터를 저장한다.
  • 오브젝트에 대한 자연스러운 조작은 CRUD-y가 아니다.
  • API는 오브젝트로 쉽게 모델링되지 않는다.
  • 작업 ID 또는 작업 오브젝트로 보류 중인 작업을 나타내도록 선택했다.

컨피그맵을 사용해야 하나, 커스텀 리소스를 사용해야 하나?

다음 중 하나에 해당하면 컨피그맵을 사용하자.

  • mysql.cnf 또는 pom.xml과 같이 잘 문서화된 기존 구성 파일 형식이 있다.
  • 전체 구성 파일을 컨피그맵의 하나의 키에 넣고 싶다.
  • 구성 파일의 주요 용도는 클러스터의 파드에서 실행 중인 프로그램이 파일을 사용하여 자체 구성하는 것이다.
  • 파일 사용자는 쿠버네티스 API가 아닌 파드의 환경 변수 또는 파드의 파일을 통해 사용하는 것을 선호한다.
  • 파일이 업데이트될 때 디플로이먼트 등을 통해 롤링 업데이트를 수행하려고 한다.

다음 중 대부분이 적용되는 경우 커스텀 리소스(CRD 또는 애그리게이트 API(aggregated API))를 사용하자.

  • 쿠버네티스 클라이언트 라이브러리 및 CLI를 사용하여 새 리소스를 만들고 업데이트하려고 한다.
  • kubectl 의 최상위 지원을 원한다. 예: kubectl get my-object object-name.
  • 새 오브젝트에 대한 업데이트를 감시한 다음 다른 오브젝트를 CRUD하거나 그 반대로 하는 새로운 자동화를 구축하려고 한다.
  • 오브젝트의 업데이트를 처리하는 자동화를 작성하려고 한다.
  • .spec, .status.metadata와 같은 쿠버네티스 API 규칙을 사용하려고 한다.
  • 제어된 리소스의 콜렉션 또는 다른 리소스의 요약에 대한 오브젝트가 되기를 원한다.

커스텀 리소스 추가

쿠버네티스는 클러스터에 커스텀 리소스를 추가하는 두 가지 방법을 제공한다.

  • CRD는 간단하며 프로그래밍 없이 만들 수 있다.
  • API 애그리게이션에는 프로그래밍이 필요하지만, 데이터 저장 방법 및 API 버전 간 변환과 같은 API 동작을 보다 강력하게 제어할 수 있다.

쿠버네티스는 다양한 사용자의 요구를 충족시키기 위해 이 두 가지 옵션을 제공하므로 사용의 용이성이나 유연성이 저하되지 않는다.

애그리게이트 API는 기본 API 서버 뒤에 있는 하위 API 서버이며 프록시 역할을 한다. 이 배치를 API 애그리게이션(AA)이라고 한다. 사용자에게는 쿠버네티스 API가 확장된 것으로 나타난다.

CRD를 사용하면 다른 API 서버를 추가하지 않고도 새로운 타입의 리소스를 생성할 수 있다. CRD를 사용하기 위해 API 애그리게이션을 이해할 필요는 없다.

설치 방법에 관계없이 새 리소스는 커스텀 리소스라고 하며 빌트인 쿠버네티스 리소스(파드 등)와 구별된다.

커스텀리소스데피니션

커스텀리소스데피니션 API 리소스를 사용하면 커스텀 리소스를 정의할 수 있다. CRD 오브젝트를 정의하면 지정한 이름과 스키마를 사용하여 새 커스텀 리소스가 만들어진다. 쿠버네티스 API는 커스텀 리소스의 스토리지를 제공하고 처리한다. CRD 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

따라서 커스텀 리소스를 처리하기 위해 자신의 API 서버를 작성할 수 없지만 구현의 일반적인 특성으로 인해 API 서버 애그리게이션보다 유연성이 떨어진다.

새 커스텀 리소스를 등록하고 새 리소스 타입의 인스턴스에 대해 작업하고 컨트롤러를 사용하여 이벤트를 처리하는 방법에 대한 예제는 커스텀 컨트롤러 예제를 참고한다.

API 서버 애그리게이션

일반적으로 쿠버네티스 API의 각 리소스에는 REST 요청을 처리하고 오브젝트의 퍼시스턴트 스토리지를 관리하는 코드가 필요하다. 주요 쿠버네티스 API 서버는 파드서비스 와 같은 빌트인 리소스를 처리하고, 일반적으로 CRD를 통해 커스텀 리소스를 처리할 수 ​​있다.

애그리게이션 레이어를 사용하면 자체 독립형 API 서버를 작성하고 배포하여 커스텀 리소스에 대한 특수한 구현을 제공할 수 있다. 기본 API 서버는 처리하는 커스텀 리소스에 대한 요청을 사용자에게 위임하여 모든 클라이언트가 사용할 수 있게 한다.

커스텀 리소스를 추가할 방법 선택

CRD는 사용하기가 더 쉽다. 애그리게이트 API가 더 유연하다. 자신의 요구에 가장 잘 맞는 방법을 선택하자.

일반적으로 CRD는 다음과 같은 경우에 적합하다.

  • 필드가 몇 개 되지 않는다
  • 회사 내에서 또는 소규모 오픈소스 프로젝트의 일부인(상용 제품이 아닌) 리소스를 사용하고 있다.

사용 편의성 비교

CRD는 애그리게이트 API보다 생성하기가 쉽다.

CRD 애그리게이트 API
프로그래밍이 필요하지 않다. 사용자는 CRD 컨트롤러에 대한 모든 언어를 선택할 수 있다. 프로그래밍하고 바이너리와 이미지를 빌드해야 한다.
실행할 추가 서비스가 없다. CR은 API 서버에서 처리한다. 추가 서비스를 생성하면 실패할 수 있다.
CRD가 생성된 후에는 지속적인 지원이 없다. 모든 버그 픽스는 일반적인 쿠버네티스 마스터 업그레이드의 일부로 선택된다. 업스트림에서 버그 픽스를 주기적으로 선택하고 애그리게이트 API 서버를 다시 빌드하고 업데이트해야 할 수 있다.
여러 버전의 API를 처리할 필요가 없다. 예를 들어, 이 리소스에 대한 클라이언트를 제어할 때 API와 동기화하여 업그레이드할 수 있다. 인터넷에 공유할 익스텐션을 개발할 때와 같이 여러 버전의 API를 처리해야 한다.

고급 기능 및 유연성

애그리게이트 API는 보다 고급 API 기능과 스토리지 레이어와 같은 다른 기능의 사용자 정의를 제공한다.

기능 설명 CRD 애그리게이트 API
유효성 검사 사용자가 오류를 방지하고 클라이언트와 독립적으로 API를 발전시킬 수 있도록 도와준다. 이러한 기능은 동시에 많은 클라이언트를 모두 업데이트할 수 없는 경우에 아주 유용하다. 예. OpenAPI v3.0 유효성 검사를 사용하여 CRD에서 대부분의 유효성 검사를 지정할 수 있다. 웹훅 유효성 검사를 추가해서 다른 모든 유효성 검사를 지원한다. 예, 임의의 유효성 검사를 지원한다.
기본 설정 위를 참고하자. 예, OpenAPI v3.0 유효성 검사default 키워드(1.17에서 GA) 또는 웹훅 변형(mutating)(이전 오브젝트의 etcd에서 읽을 때는 실행되지 않음)을 통해 지원한다.
다중 버전 관리 두 가지 API 버전을 통해 동일한 오브젝트를 제공할 수 있다. 필드 이름 바꾸기와 같은 API 변경을 쉽게 할 수 있다. 클라이언트 버전을 제어하는 ​​경우는 덜 중요하다.
사용자 정의 스토리지 다른 성능 모드(예를 들어, 키-값 저장소 대신 시계열 데이터베이스)나 보안에 대한 격리(예를 들어, 암호화된 시크릿이나 다른 암호화) 기능을 가진 스토리지가 필요한 경우 아니오
사용자 정의 비즈니스 로직 오브젝트를 생성, 읽기, 업데이트 또는 삭제를 할 때 임의의 점검 또는 조치를 수행한다. 예, 웹훅을 사용한다.
서브리소스 크기 조정 HorizontalPodAutoscaler 및 PodDisruptionBudget과 같은 시스템이 새로운 리소스와 상호 작용할 수 있다.
서브리소스 상태 사용자가 스펙 섹션을 작성하고 컨트롤러가 상태 섹션을 작성하는 세분화된 접근 제어를 허용한다. 커스텀 리소스 데이터 변형 시 오브젝트 생성을 증가시킨다(리소스에서 별도의 스펙과 상태 섹션 필요).
기타 서브리소스 "logs" 또는 "exec"과 같은 CRUD 이외의 작업을 추가한다. 아니오
strategic-merge-patch 새로운 엔드포인트는 Content-Type: application/strategic-merge-patch+json 형식의 PATCH를 지원한다. 로컬 및 서버 양쪽에서 수정할 수도 있는 오브젝트를 업데이트하는 데 유용하다. 자세한 내용은 "kubectl 패치를 사용한 API 오브젝트 업데이트"를 참고한다. 아니오
프로토콜 버퍼 새로운 리소스는 프로토콜 버퍼를 사용하려는 클라이언트를 지원한다. 아니오
OpenAPI 스키마 서버에서 동적으로 가져올 수 있는 타입에 대한 OpenAPI(스웨거(swagger)) 스키마가 있는가? 허용된 필드만 설정하여 맞춤법이 틀린 필드 이름으로부터 사용자를 보호하는가? 타입이 적용되는가(즉, string 필드에 int를 넣지 않는가?) 예, OpenAPI v3.0 유효성 검사를 기반으로 하는 스키마(1.16에서 GA)

일반적인 기능

CRD 또는 AA를 통해 커스텀 리소스를 생성하면 쿠버네티스 플랫폼 외부에서 구현하는 것과 비교하여 API에 대한 많은 기능이 제공된다.

기능 설명
CRUD 새로운 엔드포인트는 HTTP 및 kubectl을 통해 CRUD 기본 작업을 지원한다.
감시 새로운 엔드포인트는 HTTP를 통해 쿠버네티스 감시 작업을 지원한다.
디스커버리 kubectl 및 대시보드와 같은 클라이언트는 리소스에 대해 목록, 표시 및 필드 수정 작업을 자동으로 제공한다.
json-patch 새로운 엔드포인트는 Content-Type: application/json-patch+json 형식의 PATCH를 지원한다.
merge-patch 새로운 엔드포인트는 Content-Type: application/merge-patch+json 형식의 PATCH를 지원한다.
HTTPS 새로운 엔드포인트는 HTTPS를 사용한다.
빌트인 인증 익스텐션에 대한 접근은 인증을 위해 기본 API 서버(애그리게이션 레이어)를 사용한다.
빌트인 권한 부여 익스텐션에 접근하면 기본 API 서버(예: RBAC)에서 사용하는 권한을 재사용할 수 있다.
Finalizer 외부 정리가 발생할 때까지 익스텐션 리소스의 삭제를 차단한다.
어드미션 웹훅 생성/업데이트/삭제 작업 중에 기본값을 설정하고 익스텐션 리소스의 유효성 검사를 한다.
UI/CLI 디스플레이 Kubectl, 대시보드는 익스텐션 리소스를 표시할 수 있다.
설정하지 않음(unset)과 비어있음(empty) 클라이언트는 값이 없는 필드 중에서 설정되지 않은 필드를 구별할 수 있다.
클라이언트 라이브러리 생성 쿠버네티스는 일반 클라이언트 라이브러리와 타입별 클라이언트 라이브러리를 생성하는 도구를 제공한다.
레이블 및 어노테이션 공통 메타데이터는 핵심 및 커스텀 리소스를 수정하는 방법을 알고 있는 도구이다.

커스텀 리소스 설치 준비

클러스터에 커스텀 리소스를 추가하기 전에 알아야 할 몇 가지 사항이 있다.

써드파티 코드 및 새로운 장애 포인트

CRD를 생성해도 새로운 장애 포인트(예를 들어, API 서버에서 장애를 유발하는 써드파티 코드가 실행됨)가 자동으로 추가되지는 않지만, 패키지(예: 차트(Charts)) 또는 기타 설치 번들에는 CRD 및 새로운 커스텀 리소스에 대한 비즈니스 로직을 구현하는 써드파티 코드의 디플로이먼트가 포함되는 경우가 종종 있다.

애그리게이트 API 서버를 설치하려면 항상 새 디플로이먼트를 실행해야 한다.

스토리지

커스텀 리소스는 컨피그맵과 동일한 방식으로 스토리지 공간을 사용한다. 너무 많은 커스텀 리소스를 생성하면 API 서버의 스토리지 공간이 과부하될 수 있다.

애그리게이트 API 서버는 기본 API 서버와 동일한 스토리지를 사용할 수 있으며 이 경우 동일한 경고가 적용된다.

인증, 권한 부여 및 감사

CRD는 항상 API 서버의 빌트인 리소스와 동일한 인증, 권한 부여 및 감사 로깅을 사용한다.

권한 부여에 RBAC를 사용하는 경우 대부분의 RBAC 역할은 새로운 리소스에 대한 접근 권한을 부여하지 않는다(클러스터 관리자 역할 또는 와일드 카드 규칙으로 생성된 역할 제외). 새로운 리소스에 대한 접근 권한을 명시적으로 부여해야 한다. CRD 및 애그리게이트 API는 종종 추가하는 타입에 대한 새로운 역할 정의와 함께 제공된다.

애그리게이트 API 서버는 기본 API 서버와 동일한 인증, 권한 부여 및 감사를 사용하거나 사용하지 않을 수 있다.

커스텀 리소스에 접근

쿠버네티스 클라이언트 라이브러리를 사용하여 커스텀 리소스에 접근할 수 있다. 모든 클라이언트 라이브러리가 커스텀 리소스를 지원하는 것은 아니다. Gopython 클라이언트 라이브러리가 지원한다.

커스텀 리소스를 추가하면 다음을 사용하여 접근할 수 있다.

  • kubectl
  • 쿠버네티스 동적 클라이언트
  • 작성한 REST 클라이언트
  • 쿠버네티스 클라이언트 생성 도구를 사용하여 생성된 클라이언트(하나를 생성하는 것은 고급 기능이지만, 일부 프로젝트는 CRD 또는 AA와 함께 클라이언트를 제공할 수 있다).

다음 내용

3.12.1.2 - 쿠버네티스 API 애그리게이션 레이어(aggregation layer)

애그리게이션 레이어는 코어 쿠버네티스 API가 제공하는 기능 이외에 더 많은 기능을 제공할 수 있도록 추가 API를 더해 쿠버네티스를 확장할 수 있게 해준다. 추가 API는 메트릭 서버와 같이 미리 만들어진 솔루션이거나 사용자가 직접 개발한 API일 수 있다.

애그리게이션 레이어는 사용자 정의 리소스와는 다르며, 애그리게이션 레이어는 kube-apiserver 가 새로운 종류의 오브젝트를 인식하도록 하는 방법이다.

애그리게이션 레이어

애그리게이션 레이어는 kube-apiserver 프로세스 안에서 구동된다. 확장 리소스가 등록되기 전까지, 애그리게이션 레이어는 아무 일도 하지 않는다. API를 등록하기 위해서, 사용자는 쿠버네티스 API 내에서 URL 경로를 "요구하는(claim)" APIService 오브젝트를 추가해야 한다. 이때, 애그리게이션 레이어는 해당 API 경로(예: /apis/myextensions.mycompany.io/v1/...)로 전송되는 모든 것을 등록된 APIService로 프록시하게 된다.

APIService를 구현하는 가장 일반적인 방법은 클러스터 내에 실행되고 있는 파드에서 extension API server 를 실행하는 것이다. extension API server를 사용해서 클러스터의 리소스를 관리하는 경우 extension API server("extension-apiserver" 라고도 한다)는 일반적으로 하나 이상의 컨트롤러와 쌍을 이룬다. apiserver-builder 라이브러리는 extension API server와 연관된 컨틀로러에 대한 스켈레톤을 제공한다.

응답 레이턴시

Extension-apiserver는 kube-apiserver로 오가는 연결의 레이턴시가 낮아야 한다. kube-apiserver로 부터의 디스커버리 요청은 왕복 레이턴시가 5초 이내여야 한다.

extention API server가 레이턴시 요구 사항을 달성할 수 없는 경우 이를 충족할 수 있도록 변경하는 것을 고려한다.

다음 내용

대안으로, 어떻게 쿠버네티스 API를 커스텀 리소스 데피니션으로 확장하는지를 배워본다.

3.12.2 - 오퍼레이터(operator) 패턴

오퍼레이터(Operator)는 사용자 정의 리소스를 사용하여 애플리케이션 및 해당 컴포넌트를 관리하는 쿠버네티스의 소프트웨어 익스텐션이다. 오퍼레이터는 쿠버네티스 원칙, 특히 컨트롤 루프를 따른다.

동기 부여

오퍼레이터 패턴은 서비스 또는 서비스 셋을 관리하는 운영자의 주요 목표를 포착하는 것을 목표로 한다. 특정 애플리케이션 및 서비스를 돌보는 운영자는 시스템의 작동 방식, 배포 방법 및 문제가 있는 경우 대처 방법에 대해 깊이 알고 있다.

쿠버네티스에서 워크로드를 실행하는 사람들은 종종 반복 가능한 작업을 처리하기 위해 자동화를 사용하는 것을 좋아한다. 오퍼레이터 패턴은 쿠버네티스 자체가 제공하는 것 이상의 작업을 자동화하기 위해 코드를 작성하는 방법을 포착한다.

쿠버네티스의 오퍼레이터

쿠버네티스는 자동화를 위해 설계되었다. 기본적으로 쿠버네티스의 중추를 통해 많은 빌트인 자동화 기능을 사용할 수 있다. 쿠버네티스를 사용하여 워크로드 배포 및 실행을 자동화할 수 있고, 또한 쿠버네티스가 수행하는 방식을 자동화할 수 있다.

쿠버네티스의 컨트롤러 개념을 통해 쿠버네티스 코드 자체를 수정하지 않고도 클러스터의 동작을 확장할 수 있다. 오퍼레이터는 사용자 정의 리소스의 컨트롤러 역할을 하는 쿠버네티스 API의 클라이언트이다.

오퍼레이터 예시

오퍼레이터를 사용하여 자동화할 수 있는 몇 가지 사항은 다음과 같다.

  • 주문형 애플리케이션 배포
  • 해당 애플리케이션의 상태를 백업하고 복원
  • 데이터베이스 스키마 또는 추가 구성 설정과 같은 관련 변경 사항에 따른 애플리케이션 코드 업그레이드 처리
  • 쿠버네티스 API를 지원하지 않는 애플리케이션에 서비스를 게시하여 검색을 지원
  • 클러스터의 전체 또는 일부에서 장애를 시뮬레이션하여 가용성 테스트
  • 내부 멤버 선출 절차없이 분산 애플리케이션의 리더를 선택

오퍼레이터의 모습을 더 자세하게 볼 수 있는 방법은 무엇인가? 예시는 다음과 같다.

  1. 클러스터에 구성할 수 있는 SampleDB라는 사용자 정의 리소스.
  2. 오퍼레이터의 컨트롤러 부분이 포함된 파드의 실행을 보장하는 디플로이먼트.
  3. 오퍼레이터 코드의 컨테이너 이미지.
  4. 컨트롤 플레인을 쿼리하여 어떤 SampleDB 리소스가 구성되어 있는지 알아내는 컨트롤러 코드.
  5. 오퍼레이터의 핵심은 API 서버에 구성된 리소스와 현재 상태를 일치시키는 방법을 알려주는 코드이다.
    • 새 SampleDB를 추가하면 오퍼레이터는 퍼시스턴트볼륨클레임을 설정하여 내구성있는 데이터베이스 스토리지, SampleDB를 실행하는 스테이트풀셋 및 초기 구성을 처리하는 잡을 제공한다.
    • SampleDB를 삭제하면 오퍼레이터는 스냅샷을 생성한 다음 스테이트풀셋과 볼륨도 제거되었는지 확인한다.
  6. 오퍼레이터는 정기적인 데이터베이스 백업도 관리한다. 오퍼레이터는 각 SampleDB 리소스에 대해 데이터베이스에 연결하고 백업을 수행할 수 있는 파드를 생성하는 시기를 결정한다. 이 파드는 데이터베이스 연결 세부 정보 및 자격 증명이 있는 컨피그맵 및 / 또는 시크릿에 의존한다.
  7. 오퍼레이터는 관리하는 리소스에 견고한 자동화를 제공하는 것을 목표로 하기 때문에 추가 지원 코드가 있다. 이 예제에서 코드는 데이터베이스가 이전 버전을 실행 중인지 확인하고, 업그레이드된 경우 이를 업그레이드하는 잡 오브젝트를 생성한다.

오퍼레이터 배포

오퍼레이터를 배포하는 가장 일반적인 방법은 커스텀 리소스 데피니션의 정의 및 연관된 컨트롤러를 클러스터에 추가하는 것이다. 컨테이너화된 애플리케이션을 실행하는 것처럼 컨트롤러는 일반적으로 컨트롤 플레인 외부에서 실행된다. 예를 들어 클러스터에서 컨트롤러를 디플로이먼트로 실행할 수 있다.

오퍼레이터 사용

오퍼레이터가 배포되면 오퍼레이터가 사용하는 리소스의 종류를 추가, 수정 또는 삭제하여 사용한다. 위의 예에 따라 오퍼레이터 자체에 대한 디플로이먼트를 설정한 후 다음을 수행한다.

kubectl get SampleDB                   # 구성된 데이터베이스 찾기

kubectl edit SampleDB/example-database # 일부 설정을 수동으로 변경하기

…이것으로 끝이다! 오퍼레이터는 변경 사항을 적용하고 기존 서비스를 양호한 상태로 유지한다.

자신만의 오퍼레이터 작성

에코시스템에 원하는 동작을 구현하는 오퍼레이터가 없다면 직접 코딩할 수 있다.

또한 쿠버네티스 API의 클라이언트 역할을 할 수 있는 모든 언어 / 런타임을 사용하여 오퍼레이터(즉, 컨트롤러)를 구현한다.

다음은 클라우드 네이티브 오퍼레이터를 작성하는 데 사용할 수 있는 몇 가지 라이브러리와 도구들이다.

다음 내용

  • CNCF 오퍼레이터 백서 읽어보기
  • 사용자 정의 리소스에 대해 더 알아보기
  • OperatorHub.io에서 유스케이스에 맞는 이미 만들어진 오퍼레이터 찾기
  • 다른 사람들이 사용할 수 있도록 자신의 오퍼레이터를 게시하기
  • 오퍼레이터 패턴을 소개한 CoreOS 원본 글 읽기 (이 링크는 원본 글에 대한 보관 버전임)
  • 오퍼레이터 구축을 위한 모범 사례에 대한 구글 클라우드(Google Cloud)의 기사 읽기

3.12.3 - 컴퓨트, 스토리지 및 네트워킹 익스텐션

3.12.3.1 - 네트워크 플러그인

쿠버네티스의 네트워크 플러그인은 몇 가지 종류가 있다.

  • CNI 플러그인: 상호 운용성을 위해 설계된 컨테이너 네트워크 인터페이스(CNI) 명세를 준수한다.
  • 쿠버네티스는 CNI 명세의 v0.4.0 릴리스를 따른다.
  • Kubenet 플러그인: bridgehost-local CNI 플러그인을 사용하여 기본 cbr0 구현한다.

설치

kubelet에는 단일 기본 네트워크 플러그인과 전체 클러스터에 공통된 기본 네트워크가 있다. 플러그인은 시작할 때 플러그인을 검색하고, 찾은 것을 기억하며, 파드 라이프사이클에서 적절한 시간에 선택한 플러그인을 실행한다(CRI는 자체 CNI 플러그인을 관리하므로 도커에만 해당됨). 플러그인 사용 시 명심해야 할 두 가지 Kubelet 커맨드라인 파라미터가 있다.

  • cni-bin-dir: Kubelet은 시작할 때 플러그인에 대해 이 디렉터리를 검사한다.
  • network-plugin: cni-bin-dir 에서 사용할 네트워크 플러그인. 플러그인 디렉터리에서 검색한 플러그인이 보고된 이름과 일치해야 한다. CNI 플러그인의 경우, 이는 "cni"이다.

네트워크 플러그인 요구 사항

파드 네트워킹을 구성하고 정리하기 위해 NetworkPlugin 인터페이스를 제공하는 것 외에도, 플러그인은 kube-proxy에 대한 특정 지원이 필요할 수 있다. iptables 프록시는 분명히 iptables에 의존하며, 플러그인은 컨테이너 트래픽이 iptables에 사용 가능하도록 해야 한다. 예를 들어, 플러그인이 컨테이너를 리눅스 브릿지에 연결하는 경우, 플러그인은 net/bridge/bridge-nf-call-iptables sysctl을 1 로 설정하여 iptables 프록시가 올바르게 작동하는지 확인해야 한다. 플러그인이 리눅스 브리지를 사용하지 않는 경우(그러나 Open vSwitch나 다른 메커니즘과 같은 기능을 사용함) 컨테이너 트래픽이 프록시에 대해 적절하게 라우팅되도록 해야 한다.

kubelet 네트워크 플러그인이 지정되지 않은 경우, 기본적으로 noop 플러그인이 사용되며, net/bridge/bridge-nf-call-iptables=1 을 설정하여 간단한 구성(브릿지가 있는 도커 등)이 iptables 프록시에서 올바르게 작동하도록 한다.

CNI

CNI 플러그인은 Kubelet에 --network-plugin=cni 커맨드라인 옵션을 전달하여 선택된다. Kubelet은 --cni-conf-dir(기본값은 /etc/cni/net.d)에서 파일을 읽고 해당 파일의 CNI 구성을 사용하여 각 파드의 네트워크를 설정한다. CNI 구성 파일은 CNI 명세와 일치해야 하며, 구성에서 참조하는 필수 CNI 플러그인은 --cni-bin-dir(기본값은 /opt/cni/bin)에 있어야 한다.

디렉터리에 여러 CNI 구성 파일이 있는 경우, kubelet은 이름별 알파벳 순으로 구성 파일을 사용한다.

구성 파일에 지정된 CNI 플러그인 외에도, 쿠버네티스는 최소 0.2.0 버전의 표준 CNI lo 플러그인이 필요하다.

hostPort 지원

CNI 네트워킹 플러그인은 hostPort 를 지원한다. CNI 플러그인 팀이 제공하는 공식 포트맵(portmap) 플러그인을 사용하거나 portMapping 기능이 있는 자체 플러그인을 사용할 수 있다.

hostPort 지원을 사용하려면, cni-conf-dirportMappings capability 를 지정해야 한다. 예를 들면 다음과 같다.

{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

트래픽 셰이핑 지원

실험적인 기능입니다

CNI 네트워킹 플러그인은 파드 수신 및 송신 트래픽 셰이핑도 지원한다. CNI 플러그인 팀에서 제공하는 공식 대역폭(bandwidth) 플러그인을 사용하거나 대역폭 제어 기능이 있는 자체 플러그인을 사용할 수 있다.

트래픽 셰이핑 지원을 활성화하려면, CNI 구성 파일 (기본값 /etc/cni/net.d)에 bandwidth 플러그인을 추가하고, 바이너리가 CNI 실행 파일 디렉터리(기본값: /opt/cni/bin)에 포함되어 있는지 확인한다.

{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

이제 파드에 kubernetes.io/ingress-bandwidthkubernetes.io/egress-bandwidth 어노테이션을 추가할 수 있다. 예를 들면 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

kubenet

Kubenet은 리눅스에서만 사용할 수 있는 매우 기본적이고, 간단한 네트워크 플러그인이다. 그 자체로는, 크로스-노드 네트워킹 또는 네트워크 정책과 같은 고급 기능을 구현하지 않는다. 일반적으로 노드 간, 또는 단일 노드 환경에서 통신을 위한 라우팅 규칙을 설정하는 클라우드 제공자와 함께 사용된다.

Kubenet은 cbr0 라는 리눅스 브리지를 만들고 각 쌍의 호스트 끝이 cbr0 에 연결된 각 파드에 대한 veth 쌍을 만든다. 쌍의 파드 끝에는 구성 또는 컨트롤러 관리자를 통해 노드에 할당된 범위 내에서 할당된 IP 주소가 지정된다. cbr0 에는 호스트에서 활성화된 일반 인터페이스의 가장 작은 MTU와 일치하는 MTU가 지정된다.

플러그인에는 몇 가지 사항이 필요하다.

  • 표준 CNI bridge, lohost-local 플러그인은 최소 0.2.0 버전이 필요하다. Kubenet은 먼저 /opt/cni/bin 에서 검색한다. 추가 검색 경로를 제공하려면 cni-bin-dir 을 지정한다. 처음 검색된 디렉터리가 적용된다.
  • 플러그인을 활성화하려면 Kubelet을 --network-plugin=kubenet 인수와 함께 실행해야 한다.
  • Kubelet은 --non-masquerade-cidr=<clusterCidr> 인수와 함께 실행하여 이 범위 밖 IP로의 트래픽이 IP 마스커레이드(masquerade)를 사용하도록 해야 한다.
  • --pod-cidr kubelet 커맨드라인 옵션 또는 --allocate-node-cidrs=true --cluster-cidr=<cidr> 컨트롤러 관리자 커맨드라인 옵션을 통해 노드에 IP 서브넷을 할당해야 한다.

MTU 사용자 정의 (kubenet 사용)

최상의 네트워킹 성능을 얻으려면 MTU를 항상 올바르게 구성해야 한다. 네트워크 플러그인은 일반적으로 합리적인 MTU를 유추하려고 시도하지만, 때로는 로직에 따라 최적의 MTU가 지정되지 않는다. 예를 들어, 도커 브리지나 다른 인터페이스에 작은 MTU가 지정되어 있으면, kubenet은 현재 해당 MTU를 선택한다. 또는 IPSEC 캡슐화를 사용하는 경우, MTU를 줄여야 하며, 이 계산은 대부분의 네트워크 플러그인에서 범위를 벗어난다.

필요한 경우, network-plugin-mtu kubelet 옵션을 사용하여 MTU를 명시 적으로 지정할 수 있다. 예를 들어, AWS에서 eth0 MTU는 일반적으로 9001이므로, --network-plugin-mtu=9001 을 지정할 수 있다. IPSEC를 사용하는 경우 캡슐화 오버헤드를 허용하도록 --network-plugin-mtu=8873 과 같이 IPSEC을 줄일 수 있다.

이 옵션은 네트워크 플러그인에 제공된다. 현재 kubenet만 network-plugin-mtu 를 지원한다.

용법 요약

  • --network-plugin=cni--cni-bin-dir(기본값 /opt/cni/bin)에 있는 실제 CNI 플러그인 바이너리와 --cni-conf-dir(기본값 /etc/cni/net.d)에 있는 CNI 플러그인 구성과 함께 cni 네트워크 플러그인을 사용하도록 지정한다.
  • --network-plugin=kubenet/opt/cni/bin 또는 cni-bin-dir 에 있는 CNI bridge, lohost-local 플러그인과 함께 kubenet 네트워크 플러그인을 사용하도록 지정한다.
  • 현재 kubenet 네트워크 플러그인에서만 사용하는 --network-plugin-mtu=9001 은 사용할 MTU를 지정한다.

3.12.3.2 - 장치 플러그인

GPU, NIC, FPGA, InfiniBand 및 공급 업체별 설정이 필요한 유사한 리소스를 위한 플러그인을 구현하는데 쿠버네티스 장치 플러그인 프레임워크를 사용한다.
FEATURE STATE: Kubernetes v1.10 [beta]

쿠버네티스는 시스템 하드웨어 리소스를 Kubelet에 알리는 데 사용할 수 있는 장치 플러그인 프레임워크를 제공한다.

공급 업체는 쿠버네티스 자체의 코드를 커스터마이징하는 대신, 수동 또는 데몬셋으로 배포하는 장치 플러그인을 구현할 수 있다. 대상이 되는 장치에는 GPU, 고성능 NIC, FPGA, InfiniBand 어댑터 및 공급 업체별 초기화 및 설정이 필요할 수 있는 기타 유사한 컴퓨팅 리소스가 포함된다.

장치 플러그인 등록

kubelet은 Registration gRPC 서비스를 노출시킨다.

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

장치 플러그인은 이 gRPC 서비스를 통해 kubelet에 자체 등록할 수 있다. 등록하는 동안, 장치 플러그인은 다음을 보내야 한다.

  • 유닉스 소켓의 이름.
  • 빌드된 장치 플러그인 API 버전.
  • 알리려는 ResourceName. 여기서 ResourceName확장된 리소스 네이밍 체계vendor-domain/resourcetype 의 형식으로 따라야 한다. (예를 들어, NVIDIA GPU는 nvidia.com/gpu 로 알려진다.)

성공적으로 등록하고 나면, 장치 플러그인은 kubelet이 관리하는 장치 목록을 전송한 다음, kubelet은 kubelet 노드 상태 업데이트의 일부로 해당 자원을 API 서버에 알리는 역할을 한다. 예를 들어, 장치 플러그인이 kubelet에 hardware-vendor.example/foo 를 등록하고 노드에 두 개의 정상 장치를 보고하고 나면, 노드 상태가 업데이트되어 노드에 2개의 "Foo" 장치가 설치되어 사용 가능함을 알릴 수 있다.

그러고 나면, 사용자가 컨테이너 명세에 있는 장치를 요청할 수 있다. 다만, 다른 종류의 리소스를 요청하는 것이므로 다음과 같은 제한이 있다.

  • 확장된 리소스는 정수(integer) 형태만 지원되며 오버커밋(overcommit) 될 수 없다.
  • 컨테이너간에 장치를 공유할 수 없다.

쿠버네티스 클러스터가 특정 노드에서 hardware-vendor.example/foo 리소스를 알리는 장치 플러그인을 실행한다고 가정해 보자. 다음은 데모 워크로드를 실행하기 위해 이 리소스를 요청하는 파드의 예이다.

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: k8s.gcr.io/pause:2.0
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# 이 파드는 2개의 hardware-vendor.example/foo 장치가 필요하며
# 해당 요구를 충족할 수 있는 노드에만
# 예약될 수 있다.
#
# 노드에 2개 이상의 사용 가능한 장치가 있는 경우
# 나머지는 다른 파드에서 사용할 수 있다.

장치 플러그인 구현

장치 플러그인의 일반적인 워크플로우에는 다음 단계가 포함된다.

  • 초기화. 이 단계에서, 장치 플러그인은 공급 업체별 초기화 및 설정을 수행하여 장치가 준비 상태에 있는지 확인한다.

  • 플러그인은 다음의 인터페이스를 구현하는 호스트 경로 /var/lib/kubelet/device-plugins/ 아래에 유닉스 소켓과 함께 gRPC 서비스를 시작한다.

    service DevicePlugin {
      	    // GetDevicePluginOptions는 장치 관리자와 통신할 옵션을 반환한다.
          rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
      		// ListAndWatch는 장치 목록 스트림을 반환한다.
      	    // 장치 상태가 변경되거나 장치가 사라질 때마다, ListAndWatch는
      	    // 새 목록을 반환한다.
          rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
      			// 컨테이너를 생성하는 동안 Allocate가 호출되어 장치
      			// 플러그인이 장치별 작업을 실행하고 Kubelet에 장치를
      			// 컨테이너에서 사용할 수 있도록 하는 단계를 지시할 수 있다.
          rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
          // GetPreferredAllocation은 사용 가능한 장치 목록에서 할당할
      			// 기본 장치 집합을 반환한다. 그 결과로 반환된 선호하는 할당은
      			// devicemanager가 궁극적으로 수행하는 할당이 되는 것을 보장하지
      			// 않는다. 가능한 경우 devicemanager가 정보에 입각한 할당 결정을
      			// 내릴 수 있도록 설계되었다.
          rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
          // PreStartContainer는 등록 단계에서 장치 플러그인에 의해 표시되면 각 컨테이너가
      			// 시작되기 전에 호출된다. 장치 플러그인은 장치를 컨테이너에서 사용할 수 있도록 하기 전에
      			// 장치 재설정과 같은 장치별 작업을 실행할 수 있다.
          rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  • 플러그인은 호스트 경로 /var/lib/kubelet/device-plugins/kubelet.sock 에서 유닉스 소켓을 통해 kubelet에 직접 등록한다.

  • 성공적으로 등록하고 나면, 장치 플러그인은 서빙(serving) 모드에서 실행되며, 그 동안 플러그인은 장치 상태를 모니터링하고 장치 상태 변경 시 kubelet에 다시 보고한다. 또한 gRPC 요청 Allocate 를 담당한다. Allocate 하는 동안, 장치 플러그인은 GPU 정리 또는 QRNG 초기화와 같은 장치별 준비를 수행할 수 있다. 작업이 성공하면, 장치 플러그인은 할당된 장치에 접근하기 위한 컨테이너 런타임 구성이 포함된 AllocateResponse 를 반환한다. kubelet은 이 정보를 컨테이너 런타임에 전달한다.

kubelet 재시작 처리

장치 플러그인은 일반적으로 kubelet의 재시작을 감지하고 새로운 kubelet 인스턴스에 자신을 다시 등록할 것으로 기대된다. 현재의 구현에서, 새 kubelet 인스턴스는 시작될 때 /var/lib/kubelet/device-plugins 아래에 있는 모든 기존의 유닉스 소켓을 삭제한다. 장치 플러그인은 유닉스 소켓의 삭제를 모니터링하고 이러한 이벤트가 발생하면 다시 자신을 등록할 수 있다.

장치 플러그인 배포

장치 플러그인을 데몬셋, 노드 운영 체제의 패키지 또는 수동으로 배포할 수 있다.

표준 디렉터리 /var/lib/kubelet/device-plugins 에는 특권을 가진 접근이 필요하므로, 장치 플러그인은 특권을 가진 보안 컨텍스트에서 실행해야 한다. 장치 플러그인을 데몬셋으로 배포하는 경우, 플러그인의 PodSpec에서 /var/lib/kubelet/device-plugins볼륨으로 마운트해야 한다.

데몬셋 접근 방식을 선택하면 쿠버네티스를 사용하여 장치 플러그인의 파드를 노드에 배치하고, 장애 후 데몬 파드를 다시 시작하고, 업그레이드를 자동화할 수 있다.

API 호환성

쿠버네티스 장치 플러그인 지원은 베타 버전이다. 호환되지 않는 방식으로 안정화 전에 API가 변경될 수 있다. 프로젝트로서, 쿠버네티스는 장치 플러그인 개발자에게 다음 사항을 권장한다.

  • 향후 릴리스에서 변경 사항을 확인하자.
  • 이전/이후 버전과의 호환성을 위해 여러 버전의 장치 플러그인 API를 지원한다.

최신 장치 플러그인 API 버전의 쿠버네티스 릴리스로 업그레이드해야 하는 노드에서 DevicePlugins 기능을 활성화하고 장치 플러그인을 실행하는 경우, 이 노드를 업그레이드하기 전에 두 버전을 모두 지원하도록 장치 플러그인을 업그레이드한다. 이 방법을 사용하면 업그레이드 중에 장치 할당이 지속적으로 작동한다.

장치 플러그인 리소스 모니터링

FEATURE STATE: Kubernetes v1.15 [beta]

장치 플러그인에서 제공하는 리소스를 모니터링하려면, 모니터링 에이전트가 노드에서 사용 중인 장치 셋을 검색하고 메트릭과 연관될 컨테이너를 설명하는 메타데이터를 얻을 수 있어야 한다. 장치 모니터링 에이전트에 의해 노출된 프로메테우스 지표는 쿠버네티스 Instrumentation 가이드라인을 따라 pod, namespacecontainer 프로메테우스 레이블을 사용하여 컨테이너를 식별해야 한다.

kubelet은 gRPC 서비스를 제공하여 사용 중인 장치를 검색하고, 이러한 장치에 대한 메타데이터를 제공한다.

// PodResourcesLister는 kubelet에서 제공하는 서비스로, 노드의 포드 및 컨테이너가
// 사용한 노드 리소스에 대한 정보를 제공한다.
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
}

List 엔드포인트는 실행 중인 파드의 리소스에 대한 정보를 제공하며, 독점적으로 할당된 CPU의 ID, 장치 플러그인에 의해 보고된 장치 ID, 이러한 장치가 할당된 NUMA 노드의 ID와 같은 세부 정보를 함께 제공한다. 또한, NUMA 기반 머신의 경우, 컨테이너를 위해 예약된 메모리와 hugepage에 대한 정보를 포함한다.

// ListPodResourcesResponse는 List 함수가 반환하는 응답이다.
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources에는 파드에 할당된 노드 리소스에 대한 정보가 포함된다.
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources는 컨테이너에 할당된 리소스에 대한 정보를 포함한다.
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
}

// ContainerMemory는 컨테이너에 할당된 메모리와 hugepage에 대한 정보를 포함한다.
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// 토폴로지는 리소스의 하드웨어 토폴로지를 설명한다.
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA 노드의 NUMA 표현
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices는 컨테이너에 할당된 장치에 대한 정보를 포함한다.
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

GetAllocatableResources는 워커 노드에서 처음 사용할 수 있는 리소스에 대한 정보를 제공한다. kubelet이 APIServer로 내보내는 것보다 더 많은 정보를 제공한다.

// AllocatableResourcesResponses에는 kubelet이 알고 있는 모든 장치에 대한 정보가 포함된다.
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices 는 장치가 어떤 NUMA 셀과 연관되는지를 선언하는 토폴로지 정보를 노출한다. NUMA 셀은 불분명한(opaque) 정수 ID를 사용하여 식별되며, 이 값은 kubelet에 등록할 때 장치 플러그인이 보고하는 것과 일치한다.

gRPC 서비스는 /var/lib/kubelet/pod-resources/kubelet.sock 의 유닉스 소켓을 통해 제공된다. 장치 플러그인 리소스에 대한 모니터링 에이전트는 데몬 또는 데몬셋으로 배포할 수 있다. 표준 디렉터리 /var/lib/kubelet/pod-resources 에는 특권을 가진 접근이 필요하므로, 모니터링 에이전트는 특권을 가진 ​​보안 컨텍스트에서 실행해야 한다. 장치 모니터링 에이전트가 데몬셋으로 실행 중인 경우, 해당 장치 모니터링 에이전트의 PodSpec에서 /var/lib/kubelet/pod-resources볼륨으로 마운트해야 한다.

PodResourcesLister service 를 지원하려면 KubeletPodResources 기능 게이트를 활성화해야 한다. 이것은 쿠버네티스 1.15부터 기본으로 활성화되어 있으며, 쿠버네티스 1.20부터는 v1 상태이다.

토폴로지 관리자로 장치 플러그인 통합

FEATURE STATE: Kubernetes v1.18 [beta]

토폴로지 관리자는 Kubelet 컴포넌트로, 리소스를 토폴로지 정렬 방식으로 조정할 수 있다. 이를 위해, 장치 플러그인 API가 TopologyInfo 구조체를 포함하도록 확장되었다.

message TopologyInfo {
	repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

토폴로지 관리자를 활용하려는 장치 플러그인은 장치 ID 및 장치의 정상 상태와 함께 장치 등록의 일부로 채워진 TopologyInfo 구조체를 다시 보낼 수 있다. 그런 다음 장치 관리자는 이 정보를 사용하여 토폴로지 관리자와 상의하고 리소스 할당 결정을 내린다.

TopologyInfonil(기본값) 또는 NUMA 노드 목록인 nodes 필드를 지원한다. 이를 통해 NUMA 노드에 걸쳐있을 수 있는 장치 플러그인을 게시할 수 있다.

장치 플러그인으로 장치에 대해 채워진 TopologyInfo 구조체의 예는 다음과 같다.

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

장치 플러그인 예시

다음은 장치 플러그인 구현의 예이다.

다음 내용

3.12.4 - 서비스 카탈로그

서비스 카탈로그는 쿠버네티스 클러스터 내에서 실행되는 응용 프로그램이 클라우드 공급자가 제공하는 데이터 저장소 서비스와 같은 외부 관리 소프트웨어 제품을 쉽게 사용할 수 있도록하는 확장 API이다.

서비스 생성 또는 관리에 대한 자세한 지식 없이도 서비스 브로커를 통해 외부의 매니지드 서비스의 목록과 프로비전, 바인딩하는 방법을 제공한다.

오픈 서비스 브로커 API 명세에 정의된 서비스 브로커는 AWS, GCP 또는 Azure와 같은 타사 클라우드 공급자에 의해 제공되고 관리되는 매니지드 서비스의 세트에 대한 엔드포인트다. 매니지드 서비스의 예로 Microsoft Azure Cloud Queue, Amazon Simple Quere Service, Google Cloud Pub/Sub이 있으나 애플리케이션에서 사용할 수 있는 모든 소프트웨어 제품일 수 있다.

클러스터 오퍼레이터는 서비스 카탈로그를 사용하여 서비스 브로커가 제공하는 매니지드 서비스 목록을 탐색하거나 매니지드 서비스 인스턴스를 프로비저닝하고, 쿠버네티스 클러스터 내의 애플리케이션에서 사용할 수 있도록 바인딩할 수 있다.

유스케이스 예제

애플리케이션 개발자가 쿠버네티스 클러스터 내에서 실행되는 애플리케이션 중 일부로 메시지 큐를 사용하기를 원한다. 그러나 그러한 서비스에 대한 설정과 관리에는 부담이 따른다. 다행히 서비스 브로커를 통해 메시지 큐를 매니지드 서비스로 제공하는 클라우드 공급자가 있다.

클러스터 운영자는 서비스 카탈로그를 설정하고 이를 이용하여 클라우드 공급자의 서비스 브로커와 통신하여 메시지 큐 서비스의 인스턴스를 프로비저닝하고 쿠버네티스 클러스터 내의 애플리케이션에서 사용할 수 있게 한다. 따라서 애플리케이션 개발자는 메시지 큐의 세부 구현 또는 관리에 신경 쓸 필요가 없다. 애플리케이션은 메시지 큐에 서비스로 접속할 수 있다.

아키텍처

서비스 카탈로그는 오픈 서비스 브로커 API를 사용하여 쿠버네티스 API 서버가 초기 프로비저닝을 협상하고 애플리케이션이 매니지드 서비스를 사용하는데 필요한 자격 증명을 검색하는 중개자 역할을 하는 서비스 브로커와 통신한다.

이는 CRD 기반 아키텍처를 사용해서 구현되었다.


Service Catalog Architecture

API 리소스

서비스 카탈로그는 servicecatalog.k8s.io API를 설치하고 다음 쿠버네티스 리소스를 제공한다.

  • ClusterServiceBroker: 서버 연결 세부 사항을 캡슐화한, 서비스 브로커의 클러스터 내부 대표. 이들은 클러스터 내에서 새로운 유형의 매니지드 서비스를 사용할 수 있도록 하려는 클러스터 운영자가 만들고 관리한다.
  • ClusterServiceClass: 특정 서비스 브로커가 제공하는 매니지드 서비스. 새로운 ClusterServiceBroker 리소스가 클러스터에 추가되면 서비스 카탈로그 컨트롤러는 서비스 브로커에 연결해서 사용 가능한 매니지드 서비스 목록을 얻는다. 그 다음 각 매니지드 서비스에 해당하는 새로운 ClusterServiceClass 리소스를 만든다.
  • ClusterServicePlan: 매니지드 서비스의 특별 요청. 예를 들어, 매니지드 서비스는 무료 혹은 유료 티어와 같이 사용 가능한 서로 다른 상품이 있거나, SSD 스토리지를 사용하거나 더 많은 리소스를 갖는 등 다른 구성 옵션을 가질 수 있다. ClusterServiceClass와 유사하게, 새로운 ClusterServiceBroker가 클러스터에 추가되면, 서비스 카탈로그는 각 매니지드 서비스에 사용 가능한 서비스 플랜에 해당하는 새로운 ClusterServicePlan 리소스를 작성한다.
  • ServiceInstance: ClusterServiceClass의 프로비저닝된 인스턴스. 클러스터 운영자가 하나 이상의 클러스터 애플리케이션에서 사용할 수 있도록 매니지드 서비스의 특정 인스턴스를 사용하기 위해 생성한다. 새로운 ServiceInstance리소스가 생성되면, 서비스 카탈로그 컨트롤러는 해당 서비스 브로커에 연결하여 서비스 인스턴스를 프로비저닝하도록 지시한다.
  • ServiceBinding: ServiceInstance에 대한 자격 증명에 액세스한다. 자신의 애플리케이션이 ServiceInstance를 사용하기를 원하는 클러스터 운영자가 이들을 생성한다. 서비스 카탈로그 컨트롤러는 생성 시 파드에 마운트될 수 있는 서비스 인스턴스에 대한 연결 세부 정보와 자격 증명이 포함된 쿠버네티스 '시크릿(secret)'을 생성한다.

인증

서비스 카탈로그는 다음의 인증 방법을 지원한다.

사용법

클러스터 운영자는 서비스 카탈로그 API 리소스를 사용하여 매니지드 서비스를 프로비저닝하여 쿠버네티스 클러스터 내에서 사용할 수 있게 한다. 관련 단계는 다음과 같다.

  1. 서비스 브로커에서 사용 가능한 매니지드 서비스와 서비스 플랜을 나열.
  2. 매니지드 서비스의 새 인스턴스 프로비저닝.
  3. 연결 자격 증명을 반환하는 매니지드 서비스에 바인딩.
  4. 연결 자격 증명을 애플리케이션에 매핑.

매니지드 서비스와 서비스 플랜 나열

먼저, 클러스터 운영자는 servicecatalog.k8s.io 그룹 내에 ClusterServiceBroker 리소스를 생성해야 한다. 이 리소스는 서비스 브로커 엔드포인트에 접근하는데 필요한 URL과 연결 세부 사항을 포함한다.

다음은 ClusterServiceBroker 리소스 예시이다.

apiVersion: servicecatalog.k8s.io/v1beta1
kind: ClusterServiceBroker
metadata:
  name: cloud-broker
spec:
  # 서비스 브로커의 엔드포인트를 가리킨다. (이 예시는 동작하지 않는 URL이다.)
  url:  https://servicebroker.somecloudprovider.com/v1alpha1/projects/service-catalog/brokers/default
  #####
  # bearer 토큰 정보 혹은 TLS용 caBundle과 같은
  # 서비스 브로커와 통신하는데 사용될 수 있는 값을 여기에 추가할 수 있다.
  #####

다음은 서비스 브로커에서 사용 가능한 매니지드 서비스와 플랜을 나열하는 단계를 설명하는 시퀀스 다이어그램이다.

List Services

  1. ClusterServiceBroker 리소스가 서비스 카탈로그에 추가되면, 사용 가능한 서비스 목록에 대한 외부 서비스 브로커에 대한 호출을 발생시킨다.

  2. 서비스 브로커는 사용 가능한 매니지드 서비스 목록과 서비스 플랜 목록을 반환한다. 이 목록은 각각 로컬 ClusterServiceClassClusterServicePlan 리소스로 캐시된다.

  3. 그런 다음 클러스터 운영자는 다음의 명령어를 사용하여 가용한 관리 서비스 목록을 얻을 수 있다.

     kubectl get clusterserviceclasses -o=custom-columns=SERVICE\ NAME:.metadata.name,EXTERNAL\ NAME:.spec.externalName
    

    아래와 같은 형태의 서비스 이름 목록이 출력된다.

     SERVICE NAME                           EXTERNAL NAME
     4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468   cloud-provider-service
     ...                                    ...
    

    또한 다음의 명령어를 사용하여 가용한 서비스 플랜을 볼 수 있다.

     kubectl get clusterserviceplans -o=custom-columns=PLAN\ NAME:.metadata.name,EXTERNAL\ NAME:.spec.externalName
    

    아래와 같은 형태의 플랜 이름 목록이 출력된다.

     PLAN NAME                              EXTERNAL NAME
     86064792-7ea2-467b-af93-ac9694d96d52   service-plan-name
     ...                                    ...
    

새 인스턴스 프로비저닝

클러스터 운영자는 ServiceInstance 리소스를 생성하여 새 인스턴스 프로비저닝을 시작할 수 있다.

다음은 ServiceInstance 리소스의 예시이다.

apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
  name: cloud-queue-instance
  namespace: cloud-apps
spec:
  # 이전에 반환된 서비스 중 하나를 참조
  clusterServiceClassExternalName: cloud-provider-service
  clusterServicePlanExternalName: service-plan-name
  #####
  # 이곳에 서비스 브로커가 사용할 수 있는
  # 파라미터를 추가할 수 있다.
  #####

다음의 시퀀스 다이어그램은 매니지드 서비스의 새 인스턴스 프로비저닝과 관련된 일련의 단계를 보여준다.

Provision a Service

  1. ServiceInstance 리소스가 생성되면, 서비스 카탈로그는 서비스 인스턴스를 프로비저닝하기 위해 외부의 서비스 브로커 호출을 초기화한다.
  2. 서비스 브로커는 새로운 매니지드 서비스 인스턴스를 생성하고 HTTP 응답을 리턴한다.
  3. 그 후 클러스터 운영자는 인스턴스 상태가 준비되었는지 점검할 수 있다.

매니지드 서비스에 바인딩

새 인스턴스가 프로비저닝된 후, 클러스터 운영자는 애플리케이션이 서비스를 사용하는데 필요한 자격 증명을 얻기 위해 매니지드 서비스에 바인드해야 한다. 이것은 ServiceBinding 리소스를 생성하는 것으로 이루어진다.

다음은 ServiceBinding 리소스의 예시다.

apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
  name: cloud-queue-binding
  namespace: cloud-apps
spec:
  instanceRef:
    name: cloud-queue-instance
  #####
  # 서비스 브로커가 사용할 수 있는 secretName, 서비스 어카운트 파라미터 등의
  # 추가 정보를 여기에 추가할 수 있다.
  #####

다음의 시퀀스 다이어그램은 매니지드 서비스 인스턴스에 바인딩하는 단계를 보여준다.

Bind to a managed service

  1. ServiceBinding이 생성된 이후, 서비스 카탈로그는 서비스 인스턴스와 바인딩하는데 필요한 정보를 요청하는 외부 서비스 브로커를 호출한다.
  2. 서비스 브로커는 적절한 서비스 어카운트에 대한 애플리케이션 권한/역할을 활성화한다.
  3. 서비스 브로커는 매니지드 서비스 인스턴스에 연결하고 액세스하는데 필요한 정보를 리턴한다. 이는 제공자와 서비스에 특화되어 있으므로 서비스 프로바이더와 매니지드 서비스에 따라 다를 수 있다.

연결 자격 증명 매핑

바인딩 후 마지막 단계는 연결 자격 증명과 서비스 특화 정보를 애플리케이션에 매핑하는 것이다. 이런 정보는 클러스터의 애플리케이션이 액세스하여 매니지드 서비스와 직접 연결하는데 사용할 수 있는 시크릿으로 저장된다.


Map connection credentials

파드 구성 파일

이 매핑을 수행하는 한 가지 방법은 선언적 파드 구성을 사용하는 것이다.

다음 예시는 서비스 자격 증명을 애플리케이션에 매핑하는 방법을 설명한다. sa-key라는 키는 provider-cloud-key라는 볼륨에 저장되며, 애플리케이션은 이 볼륨을 /var/secrets/provider/key.json에 마운트한다. 환경 변수 PROVIDER_APPLICATION_CREDENTIALS는 마운트된 파일의 값에서 매핑된다.

...
    spec:
      volumes:
        - name: provider-cloud-key
          secret:
            secretName: sa-key
      containers:
...
          volumeMounts:
          - name: provider-cloud-key
            mountPath: /var/secrets/provider
          env:
          - name: PROVIDER_APPLICATION_CREDENTIALS
            value: "/var/secrets/provider/key.json"

다음 예시는 시크릿 값을 애플리케이션 환경 변수에 매핑하는 방법을 설명한다. 이 예시에서 메시지 큐 토픽 이름은 topic 라는 키의 provider-queue-credentials 시크릿에서 환경 변수 TOPIC에 매핑된다.

...
          env:
          - name: "TOPIC"
            valueFrom:
                secretKeyRef:
                   name: provider-queue-credentials
                   key: topic

다음 내용

4 - 태스크

쿠버네티스 문서에서 이 섹션은 개별의 태스크를 수행하는 방법을 보여준다. 한 태스크 페이지는 일반적으로 여러 단계로 이루어진 짧은 시퀀스를 제공함으로써, 하나의 일을 수행하는 방법을 보여준다.

만약 태스크 페이지를 작성하고 싶다면, 문서 풀 리퀘스트(Pull Request) 생성하기를 참조한다.

4.1 - 도구 설치

컴퓨터에서 쿠버네티스 도구를 설정한다.

kubectl

쿠버네티스 커맨드 라인 도구인 kubectl을 사용하면 쿠버네티스 클러스터에 대해 명령을 실행할 수 있다. kubectl 을 사용하여 애플리케이션을 배포하고, 클러스터 리소스를 검사 및 관리하고, 로그를 볼 수 있다. kubectl 전체 명령어를 포함한 추가 정보는 kubectl 레퍼런스 문서에서 확인할 수 있다.

kubectl 은 다양한 리눅스 플랫폼, macOS, 그리고 윈도우에 설치할 수 있다. 각각에 대한 설치 가이드는 다음과 같다.

kind

kind를 사용하면 로컬 컴퓨터에서 쿠버네티스를 실행할 수 있다. 이 도구를 사용하려면 도커를 설치하고 구성해야 한다.

kind 퀵 스타트 페이지는 kind를 시작하고 실행하기 위해 수행해야 하는 작업을 보여준다.

kind 시작하기 가이드 보기

minikube

kind 와 마찬가지로, minikube는 쿠버네티스를 로컬에서 실행할 수 있는 도구이다. minikube 는 개인용 컴퓨터(윈도우, macOS 및 리눅스 PC 포함)에서 단일 노드 쿠버네티스 클러스터를 실행하여 쿠버네티스를 사용해보거나 일상적인 개발 작업을 수행할 수 있다.

도구 설치에 중점을 두고 있다면 공식 사이트에서의 시작하기! 가이드를 따라 해볼 수 있다.

minikube 시작하기! 가이드 보기

minikube 가 작동하면, 이를 사용하여 샘플 애플리케이션을 실행해볼 수 있다.

kubeadm

kubeadm 도구를 사용하여 쿠버네티스 클러스터를 만들고 관리할 수 있다. 사용자 친화적인 방식으로 최소한의 실행 가능하고 안전한 클러스터를 설정하고 실행하는 데 필요한 작업을 수행한다.

kubeadm 설치 페이지는 kubeadm 설치하는 방법을 보여준다. 설치가 끝나면, 클러스터 생성이 가능하다.

kubeadm 설치 가이드 보기

4.1.1 - macOS에 kubectl 설치 및 설정

시작하기 전에

클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.23 클라이언트는 v1.22, v1.23, v1.24의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.

macOS에 kubectl 설치

다음과 같은 방법으로 macOS에 kubectl을 설치할 수 있다.

macOS에서 curl을 사용하여 kubectl 바이너리 설치

  1. 최신 릴리스를 다운로드한다.

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
       
  2. 바이너리를 검증한다. (선택 사항)

    kubectl 체크섬 파일을 다운로드한다.

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
       

    kubectl 바이너리를 체크섬 파일을 통해 검증한다.

    echo "$(<kubectl.sha256)  kubectl" | shasum -a 256 --check
    

    검증이 성공한다면, 출력은 다음과 같다.

    kubectl: OK
    

    검증이 실패한다면, shasum이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.

    kubectl: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. kubectl 바이너리를 실행 가능하게 한다.

    chmod +x ./kubectl
    
  4. kubectl 바이너리를 시스템 PATH 의 파일 위치로 옮긴다.

    sudo mv ./kubectl /usr/local/bin/kubectl
    sudo chown root: /usr/local/bin/kubectl
    
  5. 설치한 버전이 최신 버전인지 확인한다.

    kubectl version --client
    

macOS에서 Homebrew를 사용하여 설치

macOS에서 Homebrew 패키지 관리자를 사용하는 경우, Homebrew로 kubectl을 설치할 수 있다.

  1. 설치 명령을 실행한다.

    brew install kubectl
    

    또는

    brew install kubernetes-cli
    
  2. 설치한 버전이 최신 버전인지 확인한다.

    kubectl version --client
    

macOS에서 Macports를 사용하여 설치

macOS에서 Macports 패키지 관리자를 사용하는 경우, Macports로 kubectl을 설치할 수 있다.

  1. 설치 명령을 실행한다.

    sudo port selfupdate
    sudo port install kubectl
    
  2. 설치한 버전이 최신 버전인지 확인한다.

    kubectl version --client
    

kubectl 구성 확인

kubectl이 쿠버네티스 클러스터를 찾아 접근하려면, kube-up.sh를 사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는 kubeconfig 파일이 필요하다. 기본적으로, kubectl 구성은 ~/.kube/config 에 있다.

클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.

kubectl cluster-info

URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.

다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.

kubectl cluster-info가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.

kubectl cluster-info dump

선택적 kubectl 구성 및 플러그인

셸 자동 완성 활성화

kubectl은 Bash 및 Zsh에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.

다음은 Bash 및 Zsh에 대한 자동 완성을 설정하는 절차이다.

소개

Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash 로 생성할 수 있다. 이 스크립트를 셸에 소싱하면 kubectl 자동 완성이 가능하다.

그러나 kubectl 자동 완성 스크립트는 미리 bash-completion을 설치해야 동작한다.

Bash 업그레이드

여기의 지침에서는 Bash 4.1 이상을 사용한다고 가정한다. 다음을 실행하여 Bash 버전을 확인할 수 있다.

echo $BASH_VERSION

너무 오래된 버전인 경우, Homebrew를 사용하여 설치/업그레이드할 수 있다.

brew install bash

셸을 다시 로드하고 원하는 버전을 사용 중인지 확인한다.

echo $BASH_VERSION $SHELL

Homebrew는 보통 /usr/local/bin/bash 에 설치한다.

bash-completion 설치

bash-completion v2가 이미 설치되어 있는지 type_init_completion 으로 확인할 수 있다. 그렇지 않은 경우, Homebrew로 설치할 수 있다.

brew install bash-completion@2

이 명령의 출력에 명시된 바와 같이, ~/.bash_profile 파일에 다음을 추가한다.

export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d"
[[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"

셸을 다시 로드하고 bash-completion v2가 올바르게 설치되었는지 type _init_completion 으로 확인한다.

kubectl 자동 완성 활성화

이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행하는 방법에는 여러 가지가 있다.

  • 자동 완성 스크립트를 ~/.bash_profile 파일에서 소싱한다.

    echo 'source <(kubectl completion bash)' >>~/.bash_profile
    
  • 자동 완성 스크립트를 /usr/local/etc/bash_completion.d 디렉터리에 추가한다.

    kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
    
  • kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하기 위해 셸 자동 완성을 확장할 수 있다.

    echo 'alias k=kubectl' >>~/.bash_profile
    echo 'complete -F __start_kubectl k' >>~/.bash_profile
    
  • Homebrew로 kubectl을 설치한 경우(여기의 설명을 참고), kubectl 자동 완성 스크립트가 이미 /usr/local/etc/bash_completion.d/kubectl 에 있을 것이다. 이 경우, 아무 것도 할 필요가 없다.

어떤 경우든, 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.

모든 셸 세션에서 사용하려면, ~/.zshrc 파일에 다음을 추가한다.

source <(kubectl completion zsh)

kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.zshrc
echo 'compdef __start_kubectl k' >>~/.zshrc

셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

complete:13: command not found: compdef 와 같은 오류가 발생하면, ~/.zshrc 파일의 시작 부분에 다음을 추가한다.

autoload -Uz compinit
compinit

kubectl convert 플러그인 설치

이것은 쿠버네티스 커맨드 라인 도구인 kubectl의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로 변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다. 더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.

  1. 다음 명령으로 최신 릴리스를 다운로드한다.

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert"
       
  2. 바이너리를 검증한다. (선택 사항)

    kubectl-convert 체크섬(checksum) 파일을 다운로드한다.

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert.sha256"
       

    kubectl-convert 바이너리를 체크섬 파일을 통해 검증한다.

    echo "$(<kubectl-convert.sha256)  kubectl-convert" | shasum -a 256 --check
    

    검증이 성공한다면, 출력은 다음과 같다.

    kubectl-convert: OK
    

    검증이 실패한다면, shasum이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.

    kubectl-convert: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. kubectl-convert 바이너리를 실행 가능하게 한다.

    chmod +x ./kubectl-convert
    
  4. kubectl-convert 바이너리를 시스템 PATH 의 파일 위치로 옮긴다.

    sudo mv ./kubectl-convert /usr/local/bin/kubectl-convert
    sudo chown root: /usr/local/bin/kubectl-convert
    
  5. 플러그인이 정상적으로 설치되었는지 확인한다.

    kubectl convert --help
    

    에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.

다음 내용

4.1.2 - 리눅스에 kubectl 설치 및 설정

시작하기 전에

클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.23 클라이언트는 v1.22, v1.23, v1.24의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.

리눅스에 kubectl 설치

다음과 같은 방법으로 리눅스에 kubectl을 설치할 수 있다.

리눅스에서 curl을 사용하여 kubectl 바이너리 설치

  1. 다음 명령으로 최신 릴리스를 다운로드한다.

    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    
  2. 바이너리를 검증한다. (선택 사항)

    kubectl 체크섬(checksum) 파일을 다운로드한다.

    curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
    

    kubectl 바이너리를 체크섬 파일을 통해 검증한다.

    echo "$(<kubectl.sha256) kubectl" | sha256sum --check
    

    검증이 성공한다면, 출력은 다음과 같다.

    kubectl: OK
    

    검증이 실패한다면, shasum이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.

    kubectl: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. kubectl 설치

    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    
  4. 설치한 버전이 최신인지 확인한다.

    kubectl version --client
    

기본 패키지 관리 도구를 사용하여 설치

  1. apt 패키지 색인을 업데이트하고 쿠버네티스 apt 리포지터리를 사용하는 데 필요한 패키지들을 설치한다.

    sudo apt-get update
    sudo apt-get install -y apt-transport-https ca-certificates curl
    
  2. 구글 클라우드 공개 사이닝 키를 다운로드한다.

    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    
  3. 쿠버네티스 apt 리포지터리를 추가한다.

    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  4. 새 리포지터리의 apt 패키지 색인을 업데이트하고 kubectl을 설치한다.

    sudo apt-get update
    sudo apt-get install -y kubectl
    


cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
sudo yum install -y kubectl

다른 패키지 관리 도구를 사용하여 설치

snap 패키지 관리자를 지원하는 Ubuntu 또는 다른 리눅스 배포판을 사용하는 경우, kubectl을 snap 애플리케이션으로 설치할 수 있다.

snap install kubectl --classic
kubectl version --client

리눅스 상에서 Homebrew 패키지 관리자를 사용한다면, 설치를 통해 kubectl을 사용할 수 있다.

brew install kubectl
kubectl version --client

kubectl 구성 확인

kubectl이 쿠버네티스 클러스터를 찾아 접근하려면, kube-up.sh를 사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는 kubeconfig 파일이 필요하다. 기본적으로, kubectl 구성은 ~/.kube/config 에 있다.

클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.

kubectl cluster-info

URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.

다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.

kubectl cluster-info가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.

kubectl cluster-info dump

선택적 kubectl 구성 및 플러그인

셸 자동 완성 활성화

kubectl은 Bash 및 Zsh에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.

다음은 Bash 및 Zsh에 대한 자동 완성을 설정하는 절차이다.

소개

Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱(sourcing)하면 kubectl 자동 완성 기능이 활성화된다.

그러나, 자동 완성 스크립트는 bash-completion에 의존하고 있으며, 이 소프트웨어를 먼저 설치해야 한다(type _init_completion 을 실행하여 bash-completion이 이미 설치되어 있는지 확인할 수 있음).

bash-completion 설치

bash-completion은 많은 패키지 관리자에 의해 제공된다(여기 참고). apt-get install bash-completion 또는 yum install bash-completion 등으로 설치할 수 있다.

위의 명령은 bash-completion의 기본 스크립트인 /usr/share/bash-completion/bash_completion 을 생성한다. 패키지 관리자에 따라, ~/.bashrc 파일에서 이 파일을 수동으로 소스(source)해야 한다.

확인하려면, 셸을 다시 로드하고 type _init_completion 을 실행한다. 명령이 성공하면, 이미 설정된 상태이고, 그렇지 않으면 ~/.bashrc 파일에 다음을 추가한다.

source /usr/share/bash-completion/bash_completion

셸을 다시 로드하고 type _init_completion 을 입력하여 bash-completion이 올바르게 설치되었는지 확인한다.

kubectl 자동 완성 활성화

이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행할 수 있는 두 가지 방법이 있다.

  • ~/.bashrc 파일에서 자동 완성 스크립트를 소싱한다.

    echo 'source <(kubectl completion bash)' >>~/.bashrc
    
  • 자동 완성 스크립트를 /etc/bash_completion.d 디렉터리에 추가한다.

    kubectl completion bash >/etc/bash_completion.d/kubectl
    

kubectl에 대한 앨리어스(alias)가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -F __start_kubectl k' >>~/.bashrc

두 방법 모두 동일하다. 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.

모든 셸 세션에서 사용하려면, ~/.zshrc 파일에 다음을 추가한다.

source <(kubectl completion zsh)

kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.zshrc
echo 'compdef __start_kubectl k' >>~/.zshrc

셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

complete:13: command not found: compdef 와 같은 오류가 발생하면, ~/.zshrc 파일의 시작 부분에 다음을 추가한다.

autoload -Uz compinit
compinit

kubectl convert 플러그인 설치

이것은 쿠버네티스 커맨드 라인 도구인 kubectl의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로 변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다. 더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.

  1. 다음 명령으로 최신 릴리스를 다운로드한다.

    curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert
    
  2. 바이너리를 검증한다. (선택 사항)

    kubectl-convert 체크섬(checksum) 파일을 다운로드한다.

    curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256"
    

    kubectl-convert 바이너리를 체크섬 파일을 통해 검증한다.

    echo "$(<kubectl-convert.sha256) kubectl-convert" | sha256sum --check
    

    검증이 성공한다면, 출력은 다음과 같다.

    kubectl-convert: OK
    

    검증이 실패한다면, sha256이 0이 아닌 상태로 종료되며 다음과 유사한 결과를 출력한다.

    kubectl-convert: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. kubectl-convert 설치

    sudo install -o root -g root -m 0755 kubectl-convert /usr/local/bin/kubectl-convert
    
  4. 플러그인이 정상적으로 설치되었는지 확인한다.

    kubectl convert --help
    

    에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.

다음 내용

4.1.3 - 윈도우에 kubectl 설치 및 설정

시작하기 전에

클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.23 클라이언트는 v1.22, v1.23, v1.24의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전의 kubectl을 사용하면 예기치 않은 문제를 피할 수 있다.

윈도우에 kubectl 설치

다음과 같은 방법으로 윈도우에 kubectl을 설치할 수 있다.

윈도우에서 curl을 사용하여 kubectl 바이너리 설치

  1. 최신 릴리스 v1.23.0를 다운로드한다.

    또는 curl 을 설치한 경우, 다음 명령을 사용한다.

    curl -LO "https://dl.k8s.io/release/v1.23.0/bin/windows/amd64/kubectl.exe"
    
  2. 바이너리를 검증한다. (선택 사항)

    kubectl 체크섬 파일을 다운로드한다.

    curl -LO "https://dl.k8s.io/v1.23.0/bin/windows/amd64/kubectl.exe.sha256"
    

    kubectl 바이너리를 체크섬 파일을 통해 검증한다.

    • 커맨드 프롬프트를 사용하는 경우, CertUtil 의 출력과 다운로드한 체크섬 파일을 수동으로 비교한다.

      CertUtil -hashfile kubectl.exe SHA256
      type kubectl.exe.sha256
      
    • PowerShell을 사용하는 경우, -eq 연산자를 통해 True 또는 False 결과가 출력되는 자동 검증을 수행한다.

      $($(CertUtil -hashfile .\kubectl.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl.exe.sha256)
      
  3. PATH로 설정된 디렉터리 중 하나에 kubectl 바이너리를 추가한다.

  4. kubectl 의 버전이 다운로드한 버전과 같은지 확인한다.

    kubectl version --client
    

Chocolatey 또는 Scoop을 사용하여 윈도우에 설치

  1. 윈도우에 kubectl을 설치하기 위해서 Chocolatey 패키지 관리자나 Scoop 커맨드 라인 설치 프로그램을 사용할 수 있다.

     choco install kubernetes-cli
    

     scoop install kubectl
    
  2. 설치한 버전이 최신 버전인지 확인한다.

    kubectl version --client
    
  3. 홈 디렉터리로 이동한다.

    # cmd.exe를 사용한다면, 다음을 실행한다. cd %USERPROFILE%
    cd ~
    
  4. .kube 디렉터리를 생성한다.

    mkdir .kube
    
  5. 금방 생성한 .kube 디렉터리로 이동한다.

    cd .kube
    
  6. 원격 쿠버네티스 클러스터를 사용하도록 kubectl을 구성한다.

    New-Item config -type file
    

kubectl 구성 확인

kubectl이 쿠버네티스 클러스터를 찾아 접근하려면, kube-up.sh를 사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는 kubeconfig 파일이 필요하다. 기본적으로, kubectl 구성은 ~/.kube/config 에 있다.

클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.

kubectl cluster-info

URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.

다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.

kubectl cluster-info가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.

kubectl cluster-info dump

선택적 kubectl 구성 및 플러그인

셸 자동 완성 활성화

kubectl은 Bash 및 Zsh에 대한 자동 완성 지원을 제공하므로 입력을 위한 타이핑을 많이 절약할 수 있다.

다음은 Zsh에 대한 자동 완성을 설정하는 절차이다.

Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.

모든 셸 세션에서 사용하려면, ~/.zshrc 파일에 다음을 추가한다.

source <(kubectl completion zsh)

kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.zshrc
echo 'compdef __start_kubectl k' >>~/.zshrc

셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

complete:13: command not found: compdef 와 같은 오류가 발생하면, ~/.zshrc 파일의 시작 부분에 다음을 추가한다.

autoload -Uz compinit
compinit

kubectl convert 플러그인 설치

이것은 쿠버네티스 커맨드 라인 도구인 kubectl의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로 변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다. 더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.

  1. 다음 명령으로 최신 릴리스를 다운로드한다.

    curl -LO "https://dl.k8s.io/release/v1.23.0/bin/windows/amd64/kubectl-convert.exe"
    
  2. 바이너리를 검증한다. (선택 사항)

    kubectl-convert 체크섬(checksum) 파일을 다운로드한다.

    curl -LO "https://dl.k8s.io/v1.23.0/bin/windows/amd64/kubectl-convert.exe.sha256"
    

    kubectl-convert 바이너리를 체크섬 파일을 통해 검증한다.

    • 커맨드 프롬프트를 사용하는 경우, CertUtil 의 출력과 다운로드한 체크섬 파일을 수동으로 비교한다.

      CertUtil -hashfile kubectl-convert.exe SHA256
      type kubectl-convert.exe.sha256
      
    • PowerShell을 사용하는 경우, -eq 연산자를 통해 True 또는 False 결과가 출력되는 자동 검증을 수행한다.

      $($(CertUtil -hashfile .\kubectl-convert.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl-convert.exe.sha256)
      
  3. PATH로 설정된 디렉터리 중 하나에 kubectl-convert 바이너리를 추가한다.

  4. 플러그인이 정상적으로 설치되었는지 확인한다.

    kubectl convert --help
    

    에러가 출력되지 않는다면, 플러그인이 정상적으로 설치된 것이다.

다음 내용

4.1.4 - 포함된 도구들

메인 kubectl-installs-*.md 페이지에 포함될 스니펫.

4.1.4.1 - kubectl 설치 검증하기

kubectl을 검증하는 방법에 대해 설명한다.

kubectl이 쿠버네티스 클러스터를 찾아 접근하려면, kube-up.sh를 사용하여 클러스터를 생성하거나 Minikube 클러스터를 성공적으로 배포할 때 자동으로 생성되는 kubeconfig 파일이 필요하다. 기본적으로, kubectl 구성은 ~/.kube/config 에 있다.

클러스터 상태를 가져와서 kubectl이 올바르게 구성되어 있는지 확인한다.

kubectl cluster-info

URL 응답이 표시되면, kubectl이 클러스터에 접근하도록 올바르게 구성된 것이다.

다음과 비슷한 메시지가 표시되면, kubectl이 올바르게 구성되지 않았거나 쿠버네티스 클러스터에 연결할 수 없다.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

예를 들어, 랩톱에서 로컬로 쿠버네티스 클러스터를 실행하려면, Minikube와 같은 도구를 먼저 설치한 다음 위에서 언급한 명령을 다시 실행해야 한다.

kubectl cluster-info가 URL 응답을 반환하지만 클러스터에 접근할 수 없는 경우, 올바르게 구성되었는지 확인하려면 다음을 사용한다.

kubectl cluster-info dump

4.1.4.2 - kubectl-convert 개요

특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로 변환하는 kubectl 플러그인.

이것은 쿠버네티스 커맨드 라인 도구인 kubectl의 플러그인으로서, 특정 버전의 쿠버네티스 API로 작성된 매니페스트를 다른 버전으로 변환할 수 있도록 한다. 이것은 매니페스트를 최신 쿠버네티스 릴리스의 사용 중단되지 않은 API로 마이그레이션하는 데 특히 유용하다. 더 많은 정보는 다음의 사용 중단되지 않은 API로 마이그레이션을 참고한다.

4.1.4.3 - macOS에서 bash 자동 완성 사용하기

macOS에서 bash 자동 완성을 위한 몇 가지 선택적 구성에 대해 설명한다.

소개

Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash 로 생성할 수 있다. 이 스크립트를 셸에 소싱하면 kubectl 자동 완성이 가능하다.

그러나 kubectl 자동 완성 스크립트는 미리 bash-completion을 설치해야 동작한다.

Bash 업그레이드

여기의 지침에서는 Bash 4.1 이상을 사용한다고 가정한다. 다음을 실행하여 Bash 버전을 확인할 수 있다.

echo $BASH_VERSION

너무 오래된 버전인 경우, Homebrew를 사용하여 설치/업그레이드할 수 있다.

brew install bash

셸을 다시 로드하고 원하는 버전을 사용 중인지 확인한다.

echo $BASH_VERSION $SHELL

Homebrew는 보통 /usr/local/bin/bash 에 설치한다.

bash-completion 설치

bash-completion v2가 이미 설치되어 있는지 type_init_completion 으로 확인할 수 있다. 그렇지 않은 경우, Homebrew로 설치할 수 있다.

brew install bash-completion@2

이 명령의 출력에 명시된 바와 같이, ~/.bash_profile 파일에 다음을 추가한다.

export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d"
[[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"

셸을 다시 로드하고 bash-completion v2가 올바르게 설치되었는지 type _init_completion 으로 확인한다.

kubectl 자동 완성 활성화

이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행하는 방법에는 여러 가지가 있다.

  • 자동 완성 스크립트를 ~/.bash_profile 파일에서 소싱한다.

    echo 'source <(kubectl completion bash)' >>~/.bash_profile
    
  • 자동 완성 스크립트를 /usr/local/etc/bash_completion.d 디렉터리에 추가한다.

    kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
    
  • kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하기 위해 셸 자동 완성을 확장할 수 있다.

    echo 'alias k=kubectl' >>~/.bash_profile
    echo 'complete -F __start_kubectl k' >>~/.bash_profile
    
  • Homebrew로 kubectl을 설치한 경우(여기의 설명을 참고), kubectl 자동 완성 스크립트가 이미 /usr/local/etc/bash_completion.d/kubectl 에 있을 것이다. 이 경우, 아무 것도 할 필요가 없다.

어떤 경우든, 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

4.1.4.4 - zsh 자동 완성

zsh 자동 완성을 위한 몇 가지 선택적 구성에 대해 설명한다.

Zsh용 kubectl 자동 완성 스크립트는 kubectl completion zsh 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱하면 kubectl 자동 완성 기능이 활성화된다.

모든 셸 세션에서 사용하려면, ~/.zshrc 파일에 다음을 추가한다.

source <(kubectl completion zsh)

kubectl에 대한 앨리어스가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.zshrc
echo 'compdef __start_kubectl k' >>~/.zshrc

셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

complete:13: command not found: compdef 와 같은 오류가 발생하면, ~/.zshrc 파일의 시작 부분에 다음을 추가한다.

autoload -Uz compinit
compinit

4.1.4.5 - 다음 단계는 무엇인가?

kubectl을 설치한 다음 해야 하는 것에 대해 설명한다.

4.1.4.6 - 리눅스에서 bash 자동 완성 사용하기

리눅스에서 bash 자동 완성을 위한 몇 가지 선택적 구성에 대해 설명한다.

소개

Bash의 kubectl 자동 완성 스크립트는 kubectl completion bash 명령으로 생성할 수 있다. 셸에서 자동 완성 스크립트를 소싱(sourcing)하면 kubectl 자동 완성 기능이 활성화된다.

그러나, 자동 완성 스크립트는 bash-completion에 의존하고 있으며, 이 소프트웨어를 먼저 설치해야 한다(type _init_completion 을 실행하여 bash-completion이 이미 설치되어 있는지 확인할 수 있음).

bash-completion 설치

bash-completion은 많은 패키지 관리자에 의해 제공된다(여기 참고). apt-get install bash-completion 또는 yum install bash-completion 등으로 설치할 수 있다.

위의 명령은 bash-completion의 기본 스크립트인 /usr/share/bash-completion/bash_completion 을 생성한다. 패키지 관리자에 따라, ~/.bashrc 파일에서 이 파일을 수동으로 소스(source)해야 한다.

확인하려면, 셸을 다시 로드하고 type _init_completion 을 실행한다. 명령이 성공하면, 이미 설정된 상태이고, 그렇지 않으면 ~/.bashrc 파일에 다음을 추가한다.

source /usr/share/bash-completion/bash_completion

셸을 다시 로드하고 type _init_completion 을 입력하여 bash-completion이 올바르게 설치되었는지 확인한다.

kubectl 자동 완성 활성화

이제 kubectl 자동 완성 스크립트가 모든 셸 세션에서 제공되도록 해야 한다. 이를 수행할 수 있는 두 가지 방법이 있다.

  • ~/.bashrc 파일에서 자동 완성 스크립트를 소싱한다.

    echo 'source <(kubectl completion bash)' >>~/.bashrc
    
  • 자동 완성 스크립트를 /etc/bash_completion.d 디렉터리에 추가한다.

    kubectl completion bash >/etc/bash_completion.d/kubectl
    

kubectl에 대한 앨리어스(alias)가 있는 경우, 해당 앨리어스로 작업하도록 셸 자동 완성을 확장할 수 있다.

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -F __start_kubectl k' >>~/.bashrc

두 방법 모두 동일하다. 셸을 다시 로드하면, kubectl 자동 완성 기능이 작동할 것이다.

4.2 - 클러스터 운영

클러스터를 운영하기 위한 공통 태스크를 배운다.

4.2.1 - kubeadm으로 관리하기

4.2.1.1 - kubeadm을 사용한 인증서 관리

FEATURE STATE: Kubernetes v1.15 [stable]

kubeadm으로 생성된 클라이언트 인증서는 1년 후에 만료된다. 이 페이지는 kubeadm으로 인증서 갱신을 관리하는 방법을 설명한다.

시작하기 전에

쿠버네티스의 PKI 인증서와 요구 조건에 익숙해야 한다.

사용자 정의 인증서 사용

기본적으로, kubeadm은 클러스터를 실행하는 데 필요한 모든 인증서를 생성한다. 사용자는 자체 인증서를 제공하여 이 동작을 무시할 수 있다.

이렇게 하려면, --cert-dir 플래그 또는 kubeadm ClusterConfigurationcertificatesDir 필드에 지정된 디렉터리에 배치해야 한다. 기본적으로 /etc/kubernetes/pki 이다.

kubeadm init 을 실행하기 전에 지정된 인증서와 개인 키(private key) 쌍이 존재하면, kubeadm은 이를 덮어 쓰지 않는다. 이는 예를 들어, 기존 CA를 /etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.key 에 복사할 수 있고, kubeadm은 이 CA를 사용하여 나머지 인증서에 서명한다는 걸 의미한다.

외부 CA 모드

ca.key 파일이 아닌 ca.crt 파일만 제공할 수도 있다(이는 다른 인증서 쌍이 아닌 루트 CA 파일에만 사용 가능함). 다른 모든 인증서와 kubeconfig 파일이 있으면, kubeadm은 이 조건을 인식하고 "외부 CA" 모드를 활성화한다. kubeadm은 디스크에 CA 키없이 진행한다.

대신, --controllers=csrsigner 사용하여 controller-manager를 독립적으로 실행하고 CA 인증서와 키를 가리킨다.

PKI 인증서와 요구 조건은 외부 CA를 사용하도록 클러스터 설정에 대한 지침을 포함한다.

인증서 만료 확인

check-expiration 하위 명령을 사용하여 인증서가 만료되는 시기를 확인할 수 있다.

kubeadm certs check-expiration

출력 결과는 다음과 비슷하다.

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Dec 30, 2020 23:36 UTC   364d                                    no
apiserver                  Dec 30, 2020 23:36 UTC   364d            ca                      no
apiserver-etcd-client      Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
apiserver-kubelet-client   Dec 30, 2020 23:36 UTC   364d            ca                      no
controller-manager.conf    Dec 30, 2020 23:36 UTC   364d                                    no
etcd-healthcheck-client    Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-peer                  Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-server                Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
front-proxy-client         Dec 30, 2020 23:36 UTC   364d            front-proxy-ca          no
scheduler.conf             Dec 30, 2020 23:36 UTC   364d                                    no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 28, 2029 23:36 UTC   9y              no
etcd-ca                 Dec 28, 2029 23:36 UTC   9y              no
front-proxy-ca          Dec 28, 2029 23:36 UTC   9y              no

이 명령은 /etc/kubernetes/pki 폴더의 클라이언트 인증서와 kubeadm이 사용하는 KUBECONFIG 파일(admin.conf, controller-manager.confscheduler.conf)에 포함된 클라이언트 인증서의 만료/잔여 기간을 표시한다.

또한, kubeadm은 인증서가 외부에서 관리되는지를 사용자에게 알린다. 이 경우 사용자는 수동으로 또는 다른 도구를 사용해서 인증서 갱신 관리를 해야 한다.

자동 인증서 갱신

kubeadm은 컨트롤 플레인 업그레이드 동안 모든 인증서를 갱신한다.

이 기능은 가장 간단한 유스케이스를 해결하기 위해 설계되었다. 인증서 갱신에 대해 특별한 요구 사항이 없고 쿠버네티스 버전 업그레이드를 정기적으로(매 1년 이내 업그레이드 수행) 수행하는 경우, kubeadm은 클러스터를 최신 상태로 유지하고 합리적으로 보안을 유지한다.

인증서 갱신에 대해 보다 복잡한 요구 사항이 있는 경우, --certificate-renewal=falsekubeadm upgrade apply 또는 kubeadm upgrade node 와 함께 사용하여 기본 동작이 수행되지 않도록 할 수 있다.

수동 인증서 갱신

kubeadm certs renew 명령을 사용하여 언제든지 인증서를 수동으로 갱신할 수 있다.

이 명령은 /etc/kubernetes/pki 에 저장된 CA(또는 프론트 프록시 CA) 인증서와 키를 사용하여 갱신을 수행한다.

명령을 실행한 후에는 컨트롤 플레인 파드를 재시작해야 한다. 이는 현재 일부 구성 요소 및 인증서에 대해 인증서를 동적으로 다시 로드하는 것이 지원되지 않기 때문이다. 스태틱(static) 파드는 API 서버가 아닌 로컬 kubelet에서 관리되므로 kubectl을 사용하여 삭제 및 재시작할 수 없다. 스태틱 파드를 다시 시작하려면 /etc/kubernetes/manifests/에서 매니페스트 파일을 일시적으로 제거하고 20초를 기다리면 된다 (KubeletConfiguration structfileCheckFrequency 값을 참고한다). 파드가 매니페스트 디렉터리에 더 이상 없는 경우 kubelet은 파드를 종료한다. 그런 다음 파일을 다시 이동할 수 있으며 또 다른 fileCheckFrequency 기간이 지나면, kubelet은 파드를 생성하고 구성 요소에 대한 인증서 갱신을 완료할 수 있다.

kubeadm certs renew 는 다음의 옵션을 제공한다.

쿠버네티스 인증서는 일반적으로 1년 후 만료일에 도달한다.

  • --csr-only 는 실제로 인증서를 갱신하지 않고 인증서 서명 요청을 생성하여 외부 CA로 인증서를 갱신하는 데 사용할 수 있다. 자세한 내용은 다음 단락을 참고한다.

  • 모든 인증서 대신 단일 인증서를 갱신할 수도 있다.

쿠버네티스 인증서 API를 사용하여 인증서 갱신

이 섹션에서는 쿠버네티스 인증서 API를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.

서명자 설정

쿠버네티스 인증 기관(Certificate Authority)은 기본적으로 작동하지 않는다. cert-manager와 같은 외부 서명자를 설정하거나, 빌트인 서명자를 사용할 수 있다.

빌트인 서명자는 kube-controller-manager의 일부이다.

빌트인 서명자를 활성화하려면, --cluster-signing-cert-file--cluster-signing-key-file 플래그를 전달해야 한다.

새 클러스터를 생성하는 경우, kubeadm 구성 파일을 사용할 수 있다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt
    cluster-signing-key-file: /etc/kubernetes/pki/ca.key

인증서 서명 요청(CSR) 생성

쿠버네티스 API로 CSR을 작성하려면 CertificateSigningRequest 생성을 본다.

외부 CA로 인증서 갱신

이 섹션에서는 외부 CA를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.

외부 CA와 보다 효과적으로 통합하기 위해 kubeadm은 인증서 서명 요청(CSR)을 생성할 수도 있다. CSR은 클라이언트의 서명된 인증서에 대한 CA 요청을 나타낸다. kubeadm 관점에서, 일반적으로 온-디스크(on-disk) CA에 의해 서명되는 모든 인증서는 CSR로 생성될 수 있다. 그러나 CA는 CSR로 생성될 수 없다.

인증서 서명 요청(CSR) 생성

kubeadm certs renew --csr-only 로 인증서 서명 요청을 만들 수 있다.

CSR과 함께 제공되는 개인 키가 모두 출력된다. --csr-dir 로 사용할 디텍터리를 전달하여 지정된 위치로 CSR을 출력할 수 있다. --csr-dir 을 지정하지 않으면, 기본 인증서 디렉터리(/etc/kubernetes/pki)가 사용된다.

kubeadm certs renew --csr-only 로 인증서를 갱신할 수 있다. kubeadm init 과 마찬가지로 출력 디렉터리를 --csr-dir 플래그로 지정할 수 있다.

CSR에는 인증서 이름, 도메인 및 IP가 포함되지만, 용도를 지정하지는 않는다. 인증서를 발행할 때 올바른 인증서 용도를 지정하는 것은 CA의 책임이다.

선호하는 방법으로 인증서에 서명한 후, 인증서와 개인 키를 PKI 디렉터리(기본적으로 /etc/kubernetes/pki)에 복사해야 한다.

인증 기관(CA) 순환(rotation)

Kubeadm은 CA 인증서의 순환이나 교체 기능을 기본적으로 지원하지 않는다.

CA의 수동 순환이나 교체에 대한 보다 상세한 정보는 CA 인증서 수동 순환 문서를 참조한다.

서명된 kubelet 인증서 활성화하기

기본적으로 kubeadm에 의해서 배포된 kubelet 인증서는 자가 서명된(self-signed) 것이다. 이것은 metrics-server와 같은 외부 서비스의 kubelet에 대한 연결은 TLS로 보안되지 않음을 의미한다.

제대로 서명된 인증서를 얻기 위해서 신규 kubeadm 클러스터의 kubelet을 구성하려면 다음의 최소 구성을 kubeadm init 에 전달해야 한다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

만약 이미 클러스터를 생성했다면 다음을 따라 이를 조정해야 한다.

  • kube-system 네임스페이스에서 kubelet-config-1.23 컨피그맵을 찾아서 수정한다. 해당 컨피그맵에는 kubelet 키가 KubeletConfiguration 문서를 값으로 가진다. serverTLSBootstrap: true 가 되도록 KubeletConfiguration 문서를 수정한다.
  • 각 노드에서, serverTLSBootstrap: true 필드를 /var/lib/kubelet/config.yaml 에 추가한다. 그리고 systemctl restart kubelet 로 kubelet을 재시작한다.

serverTLSBootstrap: true 필드는 kubelet 인증서를 이용한 부트스트랩을 certificates.k8s.io API에 요청함으로써 활성화할 것이다. 한 가지 알려진 제약은 이 인증서들에 대한 CSR(인증서 서명 요청)들이 kube-controller-manager - kubernetes.io/kubelet-serving의 기본 서명자(default signer)에 의해서 자동으로 승인될 수 없다는 점이다. 이것은 사용자나 제 3의 컨트롤러의 액션을 필요로 할 것이다.

이 CSR들은 다음을 통해 볼 수 있다.

kubectl get csr
NAME        AGE     SIGNERNAME                        REQUESTOR                      CONDITION
csr-9wvgt   112s    kubernetes.io/kubelet-serving     system:node:worker-1           Pending
csr-lz97v   1m58s   kubernetes.io/kubelet-serving     system:node:control-plane-1    Pending

이를 승인하기 위해서는 다음을 수행한다.

kubectl certificate approve <CSR-name>

기본적으로, 이 인증서는 1년 후에 만기될 것이다. Kubeadm은 KubeletConfiguration 필드의 rotateCertificatestrue 로 설정한다. 이것은 만기가 다가오면 인증서를 위한 신규 CSR 세트가 생성되는 것을 의미하며, 해당 순환(rotation)을 완료하기 위해서는 승인이 되어야 한다는 것을 의미한다. 더 상세한 이해를 위해서는 인증서 순환를 확인한다.

만약 이 CSR들의 자동 승인을 위한 솔루션을 찾고 있다면 클라우드 제공자와 연락하여 대역 외 메커니즘(out of band mechanism)을 통해 노드의 신분을 검증할 수 있는 CSR 서명자를 가지고 있는지 문의하는 것을 추천한다.

제 3 자 커스텀 컨트롤러도 사용될 수 있다.

이러한 컨트롤러는 CSR의 CommonName과 요청된 IPs 및 도메인 네임을 모두 검증하지 않는 한, 보안이 되는 메커니즘이 아니다. 이것을 통해 악의적 행위자가 kubelet 인증서(클라이언트 인증)를 사용하여 아무 IP나 도메인 네임에 대해 인증서를 요청하는 CSR의 생성을 방지할 수 있을 것이다.

4.2.1.2 - kubeadm 클러스터 업그레이드

이 페이지는 kubeadm으로 생성된 쿠버네티스 클러스터를 1.22.x 버전에서 1.23.x 버전으로, 1.23.x 버전에서 1.23.y(여기서 y > x) 버전으로 업그레이드하는 방법을 설명한다. 업그레이드가 지원되지 않는 경우 마이너 버전을 건너뛴다.

이전 버전의 kubeadm을 사용하여 생성된 클러스터 업그레이드에 대한 정보를 보려면, 이 페이지 대신 다음의 페이지들을 참고한다.

추상적인 업그레이드 작업 절차는 다음과 같다.

  1. 기본 컨트롤 플레인 노드를 업그레이드한다.
  2. 추가 컨트롤 플레인 노드를 업그레이드한다.
  3. 워커(worker) 노드를 업그레이드한다.

시작하기 전에

  • 릴리스 노트를 주의 깊게 읽어야 한다.
  • 클러스터는 정적 컨트롤 플레인 및 etcd 파드 또는 외부 etcd를 사용해야 한다.
  • 데이터베이스에 저장된 앱-레벨 상태와 같은 중요한 컴포넌트를 반드시 백업한다. kubeadm upgrade 는 워크로드에 영향을 미치지 않고, 쿠버네티스 내부의 컴포넌트만 다루지만, 백업은 항상 모범 사례일 정도로 중요하다.
  • 스왑을 비활성화해야 한다.

추가 정보

  • kubelet 마이너 버전을 업그레이드하기 전에 노드 드레이닝(draining)이 필요하다. 컨트롤 플레인 노드의 경우 CoreDNS 파드 또는 기타 중요한 워크로드를 실행할 수 있다.
  • 컨테이너 사양 해시 값이 변경되므로, 업그레이드 후 모든 컨테이너가 다시 시작된다.

업그레이드할 버전 결정

OS 패키지 관리자를 사용하여 쿠버네티스의 최신 패치 릴리스 버전(1.23)을 찾는다.

apt update
apt-cache madison kubeadm
# 목록에서 최신 버전(1.23)을 찾는다
# 1.23.x-00과 같아야 한다. 여기서 x는 최신 패치이다.

yum list --showduplicates kubeadm --disableexcludes=kubernetes
# 목록에서 최신 버전(1.23)을 찾는다
# 1.23.x-0과 같아야 한다. 여기서 x는 최신 패치이다.

컨트롤 플레인 노드 업그레이드

컨트롤 플레인 노드의 업그레이드 절차는 한 번에 한 노드씩 실행해야 한다. 먼저 업그레이드할 컨트롤 플레인 노드를 선택한다. /etc/kubernetes/admin.conf 파일이 있어야 한다.

"kubeadm upgrade" 호출

첫 번째 컨트롤 플레인 노드의 경우

  • kubeadm 업그레이드

# 1.23.x-00에서 x를 최신 패치 버전으로 바꾼다.
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.23.x-00 && \
apt-mark hold kubeadm
-
# apt-get 버전 1.1부터 다음 방법을 사용할 수도 있다
apt-get update && \
apt-get install -y --allow-change-held-packages kubeadm=1.23.x-00

# 1.23.x-0에서 x를 최신 패치 버전으로 바꾼다.
yum install -y kubeadm-1.23.x-0 --disableexcludes=kubernetes

  • 다운로드하려는 버전이 잘 받아졌는지 확인한다.

    kubeadm version
    
  • 업그레이드 계획을 확인한다.

    kubeadm upgrade plan
    

    이 명령은 클러스터를 업그레이드할 수 있는지를 확인하고, 업그레이드할 수 있는 버전을 가져온다. 또한 컴포넌트 구성 버전 상태가 있는 표를 보여준다.

  • 업그레이드할 버전을 선택하고, 적절한 명령을 실행한다. 예를 들면 다음과 같다.

    # 이 업그레이드를 위해 선택한 패치 버전으로 x를 바꾼다.
    sudo kubeadm upgrade apply v1.23.x
    

    명령이 완료되면 다음을 확인해야 한다.

    [upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.23.x". Enjoy!
    
    [upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
    
  • CNI 제공자 플러그인을 수동으로 업그레이드한다.

    CNI(컨테이너 네트워크 인터페이스) 제공자는 자체 업그레이드 지침을 따를 수 있다. 애드온 페이지에서 사용하는 CNI 제공자를 찾고 추가 업그레이드 단계가 필요한지 여부를 확인한다.

    CNI 제공자가 데몬셋(DaemonSet)으로 실행되는 경우 추가 컨트롤 플레인 노드에는 이 단계가 필요하지 않다.

다른 컨트롤 플레인 노드의 경우

첫 번째 컨트롤 플레인 노드와 동일하지만 다음을 사용한다.

sudo kubeadm upgrade node

아래 명령 대신 위의 명령을 사용한다.

sudo kubeadm upgrade apply

kubeadm upgrade plan 을 호출하고 CNI 공급자 플러그인을 업그레이드할 필요가 없다.

노드 드레인

  • Prepare the node for maintenance by marking it unschedulable and evicting the workloads:

    # <node-to-drain>을 드레인하는 노드의 이름으로 바꾼다.
    kubectl drain <node-to-drain> --ignore-daemonsets
    

kubelet과 kubectl 업그레이드

  • 모든 컨트롤 플레인 노드에서 kubelet 및 kubectl을 업그레이드한다.

# replace x in 1.23.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.23.x-00 kubectl=1.23.x-00 && \
apt-mark hold kubelet kubectl
-
# apt-get 버전 1.1부터 다음 방법을 사용할 수도 있다
apt-get update && \
apt-get install -y --allow-change-held-packages kubelet=1.23.x-00 kubectl=1.23.x-00

# 1.23.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubelet-1.23.x-0 kubectl-1.23.x-0 --disableexcludes=kubernetes

  • kubelet을 다시 시작한다.
sudo systemctl daemon-reload
sudo systemctl restart kubelet

노드 uncordon

  • 노드를 스케줄 가능으로 표시하여 노드를 다시 온라인 상태로 전환한다.

    # <node-to-drain>을 드레인하는 노드의 이름으로 바꾼다.
    kubectl uncordon <node-to-drain>
    

워커 노드 업그레이드

워커 노드의 업그레이드 절차는 워크로드를 실행하는 데 필요한 최소 용량을 보장하면서, 한 번에 하나의 노드 또는 한 번에 몇 개의 노드로 실행해야 한다.

kubeadm 업그레이드

  • 모든 워커 노드에서 kubeadm을 업그레이드한다.

# 1.23.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.23.x-00 && \
apt-mark hold kubeadm
-
# apt-get 버전 1.1부터 다음 방법을 사용할 수도 있다
apt-get update && \
apt-get install -y --allow-change-held-packages kubeadm=1.23.x-00

# 1.23.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubeadm-1.23.x-0 --disableexcludes=kubernetes

"kubeadm upgrade" 호출

  • 워커 노드의 경우 로컬 kubelet 구성을 업그레이드한다.

    sudo kubeadm upgrade node
    

노드 드레인

  • 스케줄 불가능(unschedulable)으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain>을 드레이닝하려는 노드 이름으로 바꾼다.
    kubectl drain <node-to-drain> --ignore-daemonsets
    

kubelet과 kubectl 업그레이드

  • kubelet 및 kubectl을 업그레이드한다.

# 1.23.x-00의 x를 최신 패치 버전으로 바꾼다
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.23.x-00 kubectl=1.23.x-00 && \
apt-mark hold kubelet kubectl
-
# apt-get 버전 1.1부터 다음 방법을 사용할 수도 있다
apt-get update && \
apt-get install -y --allow-change-held-packages kubelet=1.23.x-00 kubectl=1.23.x-00

# 1.23.x-0에서 x를 최신 패치 버전으로 바꾼다
yum install -y kubelet-1.23.x-0 kubectl-1.23.x-0 --disableexcludes=kubernetes

  • kubelet을 다시 시작한다.

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

노드에 적용된 cordon 해제

  • 스케줄 가능(schedulable)으로 표시하여 노드를 다시 온라인 상태로 만든다.

    # <node-to-drain>을 노드의 이름으로 바꾼다.
    kubectl uncordon <node-to-drain>
    

클러스터 상태 확인

모든 노드에서 kubelet을 업그레이드한 후 kubectl이 클러스터에 접근할 수 있는 곳에서 다음의 명령을 실행하여 모든 노드를 다시 사용할 수 있는지 확인한다.

kubectl get nodes

모든 노드에 대해 STATUS 열에 Ready 가 표시되어야 하고, 버전 번호가 업데이트되어 있어야 한다.

장애 상태에서의 복구

예를 들어 kubeadm upgrade 를 실행하는 중에 예기치 못한 종료로 인해 업그레이드가 실패하고 롤백하지 않는다면, kubeadm upgrade 를 다시 실행할 수 있다. 이 명령은 멱등성을 보장하며 결국 실제 상태가 선언한 의도한 상태인지 확인한다.

잘못된 상태에서 복구하기 위해, 클러스터가 실행 중인 버전을 변경하지 않고 kubeadm upgrade apply --force 를 실행할 수도 있다.

업그레이드하는 동안 kubeadm은 /etc/kubernetes/tmp 아래에 다음과 같은 백업 폴더를 작성한다.

  • kubeadm-backup-etcd-<date>-<time>
  • kubeadm-backup-manifests-<date>-<time>

kubeadm-backup-etcd 는 컨트롤 플레인 노드에 대한 로컬 etcd 멤버 데이터의 백업을 포함한다. etcd 업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을 /var/lib/etcd 에서 수동으로 복원할 수 있다. 외부 etcd를 사용하는 경우 이 백업 폴더는 비어있다.

kubeadm-backup-manifests 는 컨트롤 플레인 노드에 대한 정적 파드 매니페스트 파일의 백업을 포함한다. 업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을 /etc/kubernetes/manifests 에서 수동으로 복원할 수 있다. 어떤 이유로 특정 컴포넌트의 업그레이드 전 매니페스트 파일과 업그레이드 후 매니페스트 파일 간에 차이가 없는 경우, 백업 파일은 기록되지 않는다.

작동 원리

kubeadm upgrade apply 는 다음을 수행한다.

  • 클러스터가 업그레이드 가능한 상태인지 확인한다.
    • API 서버에 접근할 수 있다
    • 모든 노드가 Ready 상태에 있다
    • 컨트롤 플레인이 정상적으로 동작한다
  • 버전 차이(skew) 정책을 적용한다.
  • 컨트롤 플레인 이미지가 사용 가능한지 또는 머신으로 가져올 수 있는지 확인한다.
  • 컴포넌트 구성에 버전 업그레이드가 필요한 경우 대체 구성을 생성하거나 사용자가 제공한 것으로 덮어 쓰기한다.
  • 컨트롤 플레인 컴포넌트 또는 롤백 중 하나라도 나타나지 않으면 업그레이드한다.
  • 새로운 CoreDNSkube-proxy 매니페스트를 적용하고 필요한 모든 RBAC 규칙이 생성되도록 한다.
  • API 서버의 새 인증서와 키 파일을 작성하고 180일 후에 만료될 경우 이전 파일을 백업한다.

kubeadm upgrade node 는 추가 컨트롤 플레인 노드에서 다음을 수행한다.

  • 클러스터에서 kubeadm ClusterConfiguration 을 가져온다.
  • 선택적으로 kube-apiserver 인증서를 백업한다.
  • 컨트롤 플레인 컴포넌트에 대한 정적 파드 매니페스트를 업그레이드한다.
  • 이 노드의 kubelet 구성을 업그레이드한다.

kubeadm upgrade node 는 워커 노드에서 다음을 수행한다.

  • 클러스터에서 kubeadm ClusterConfiguration 을 가져온다.
  • 이 노드의 kubelet 구성을 업그레이드한다.

4.2.1.3 - 윈도우 노드 추가

FEATURE STATE: Kubernetes v1.18 [beta]

쿠버네티스를 사용하여 리눅스와 윈도우 노드를 혼합하여 실행할 수 있으므로, 리눅스에서 실행되는 파드와 윈도우에서 실행되는 파드를 혼합할 수 있다. 이 페이지는 윈도우 노드를 클러스터에 등록하는 방법을 보여준다.

시작하기 전에

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.17. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

목적

  • 클러스터에 윈도우 노드 등록
  • 리눅스 및 윈도우의 파드와 서비스가 서로 통신할 수 있도록 네트워킹 구성

시작하기: 클러스터에 윈도우 노드 추가

네트워킹 구성

리눅스 기반 쿠버네티스 컨트롤 플레인 노드가 있으면 네트워킹 솔루션을 선택할 수 있다. 이 가이드는 VXLAN 모드의 플란넬(Flannel)을 사용하는 방법을 짧막하게 보여준다.

플란넬 구성

  1. 플란넬을 위한 쿠버네티스 컨트롤 플레인 준비

    클러스터의 쿠버네티스 컨트롤 플레인에서 약간의 준비가 필요하다. 플란넬을 사용할 때 iptables 체인에 브릿지된 IPv4 트래픽을 활성화하는 것을 권장한다. 아래 명령을 모든 리눅스 노드에서 실행해야만 한다.

    sudo sysctl net.bridge.bridge-nf-call-iptables=1
    
  2. 리눅스용 플란넬 다운로드 및 구성

    가장 최근의 플란넬 매니페스트를 다운로드한다.

    wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    

    VNI를 4096으로 설정하고 포트를 4789로 설정하려면 플란넬 매니페스트의 net-conf.json 섹션을 수정한다. 다음과 같을 것이다.

    net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan",
            "VNI": 4096,
            "Port": 4789
          }
        }
    
  3. 플란넬 매니페스트 적용 및 유효성 검사

    플란넬 구성을 적용해보자.

    kubectl apply -f kube-flannel.yml
    

    몇 분 후에, 플란넬 파드 네트워크가 배포되었다면 모든 파드가 실행 중인 것으로 표시된다.

    kubectl get pods -n kube-system
    

    출력 결과에 리눅스 flannel 데몬셋(DaemonSet)이 실행 중인 것으로 나와야 한다.

    NAMESPACE     NAME                                      READY        STATUS    RESTARTS   AGE
    ...
    kube-system   kube-flannel-ds-54954                     1/1          Running   0          1m
    
  4. 윈도우 플란넬 및 kube-proxy 데몬셋 추가

    이제 윈도우 호환 버전의 플란넬과 kube-proxy를 추가할 수 있다. 호환 가능한 kube-proxy 버전을 얻으려면, 이미지의 태그를 대체해야 한다. 다음의 예시는 쿠버네티스 v1.23.0의 사용법을 보여주지만, 사용자의 배포에 맞게 버전을 조정해야 한다.

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.23.0/g' | kubectl apply -f -
    kubectl apply -f https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/flannel-overlay.yml
    

윈도우 워커 노드 조인(joining)

Docker EE 설치

컨테이너 기능 설치

Install-WindowsFeature -Name containers

도커 설치 자세한 내용은 도커 엔진 설치 - 윈도우 서버 엔터프라이즈에서 확인할 수 있다.

wins, kubelet 및 kubeadm 설치

curl.exe -LO https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/kubeadm/scripts/PrepareNode.ps1
.\PrepareNode.ps1 -KubernetesVersion v1.23.0

kubeadm 실행하여 노드에 조인

컨트롤 플레인 호스트에서 kubeadm init 실행할 때 제공된 명령을 사용한다. 이 명령이 더 이상 없거나, 토큰이 만료된 경우, kubeadm token create --print-join-command (컨트롤 플레인 호스트에서)를 실행하여 새 토큰 및 조인 명령을 생성할 수 있다.

containerD 설치

curl.exe -LO https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/Install-Containerd.ps1
.\Install-Containerd.ps1

wins, kubelet 및 kubeadm 설치

curl.exe -LO https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/kubeadm/scripts/PrepareNode.ps1
.\PrepareNode.ps1 -KubernetesVersion v1.23.0 -ContainerRuntime containerD

kubeadm 실행하여 노드에 조인

컨트롤 플레인 호스트에서 `kubeadm init` 실행할 때 제공된 명령을 사용한다.
이 명령이 더 이상 없거나, 토큰이 만료된 경우, `kubeadm token create --print-join-command`
(컨트롤 플레인 호스트에서)를 실행하여 새 토큰 및 조인 명령을 생성할 수 있다.

설치 확인

이제 다음을 실행하여 클러스터에서 윈도우 노드를 볼 수 있다.

kubectl get nodes -o wide

새 노드가 NotReady 상태인 경우 플란넬 이미지가 여전히 다운로드 중일 수 있다. kube-system 네임스페이스에서 flannel 파드를 확인하여 이전과 같이 진행 상황을 확인할 수 있다.

kubectl -n kube-system get pods -l app=flannel

flannel 파드가 실행되면, 노드는 Ready 상태가 되고 워크로드를 처리할 수 있어야 한다.

다음 내용

4.2.1.4 - 윈도우 노드 업그레이드

FEATURE STATE: Kubernetes v1.18 [beta]

이 페이지는 kubeadm으로 생성된 윈도우 노드를 업그레이드하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.17. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

워커 노드 업그레이드

kubeadm 업그레이드

  1. 윈도우 노드에서, kubeadm을 업그레이드한다.

    # replace v1.23.0 with your desired version
    curl.exe -Lo C:\k\kubeadm.exe https://dl.k8s.io//bin/windows/amd64/kubeadm.exe
    

노드 드레인

  1. 쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 불가능한 것으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain>을 드레이닝하려는 노드 이름으로 바꾼다
    kubectl drain <node-to-drain> --ignore-daemonsets
    

    다음과 비슷한 출력이 표시되어야 한다.

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

kubelet 구성 업그레이드

  1. 윈도우 노드에서, 다음의 명령을 호출하여 새 kubelet 구성을 동기화한다.

    kubeadm upgrade node
    

kubelet 업그레이드

  1. 윈도우 노드에서, kubelet을 업그레이드하고 다시 시작한다.

    stop-service kubelet
    curl.exe -Lo C:\k\kubelet.exe https://dl.k8s.io//bin/windows/amd64/kubelet.exe
    restart-service kubelet
    

노드에 적용된 cordon 해제

  1. 쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 가능으로 표시하여 노드를 다시 온라인으로 가져온다.

    # <node-to-drain>을 노드의 이름으로 바꾼다
    kubectl uncordon <node-to-drain>
    

kube-proxy 업그레이드

  1. 쿠버네티스 API에 접근할 수 있는 머신에서, 다음을 실행하여, v1.23.0을 원하는 버전으로 다시 바꾼다.

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.23.0/g' | kubectl apply -f -
    

4.2.2 - 메모리, CPU 와 API 리소스 관리

4.2.2.1 - 네임스페이스에 대한 기본 메모리 요청량과 상한 구성

이 페이지는 네임스페이스에 대한 기본 메모리 요청량(request)과 상한(limit)을 구성하는 방법을 보여준다. 기본 메모리 상한이 있는 네임스페이스에서 컨테이너가 생성되고, 컨테이너가 자체 메모리 상한을 지정하지 않으면, 컨테이너에 기본 메모리 상한이 할당된다. 쿠버네티스는 이 문서의 뒷부분에서 설명하는 특정 조건에서 기본 메모리 요청량을 할당한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터의 각 노드에는 최소 2GiB의 메모리가 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 다른 리소스와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace default-mem-example

리밋레인지(LimitRange)와 파드 생성

다음은 리밋레인지 오브젝트의 구성 파일이다. 구성은 메모리 요청량 기본값(default)과 메모리 상한 기본값을 지정한다.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

default-mem-example 네임스페이스에 리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example

이제 컨테이너가 default-mem-example 네임스페이스에 생성되고, 컨테이너가 메모리 요청량 및 메모리 상한에 대해 고유한 값을 지정하지 않으면, 컨테이너에 메모리 요청량 기본값 256MiB와 메모리 상한 기본값 512MiB가 지정된다.

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 메모리 요청량 및 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example

출력 결과는 파드의 컨테이너에 256MiB의 메모리 요청량과 512MiB의 메모리 상한이 있음을 나타낸다. 이것은 리밋레인지에 의해 지정된 기본값이다.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

파드를 삭제한다.

kubectl delete pod default-mem-demo --namespace=default-mem-example

컨테이너 상한은 지정하고, 요청량을 지정하지 않으면 어떻게 되나?

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 메모리 상한을 지정하지만, 요청량은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: default-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example

출력 결과는 컨테이너의 메모리 요청량이 메모리 상한과 일치하도록 설정되었음을 보여준다. 참고로 컨테이너에는 기본 메모리 요청량의 값인 256Mi가 할당되지 않았다.

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

컨테이너의 요청량은 지정하고, 상한을 지정하지 않으면 어떻게 되나?

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 메모리 요청량을 지정하지만, 상한은 지정하지 않았다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example

파드 사양을 확인한다.

kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example

출력 결과는 컨테이너의 메모리 요청량이 컨테이너의 구성 파일에 지정된 값으로 설정되었음을 보여준다. 컨테이너의 메모리 상한은 네임스페이스의 기본 메모리 상한인 512Mi로 설정되어 있다.

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

기본 메모리 상한 및 요청량에 대한 동기

네임스페이스에 리소스 쿼터가 있는 경우, 메모리 상한에 기본값을 설정하는 것이 좋다. 다음은 리소스 쿼터가 네임스페이스에 적용하는 두 가지 제한 사항이다.

  • 네임스페이스에서 실행되는 모든 컨테이너에는 자체 메모리 상한이 있어야 한다.
  • 네임스페이스의 모든 컨테이너가 사용하는 총 메모리 양은 지정된 상한을 초과하지 않아야 한다.

컨테이너가 자체 메모리 상한을 지정하지 않으면, 기본 상한이 부여되고, 쿼터에 의해 제한되는 네임스페이스에서 실행될 수 있다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace default-mem-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.2.2 - 네임스페이스에 대한 기본 CPU 요청량과 상한 구성

이 페이지는 네임스페이스에 대한 기본 CPU 요청량(request) 및 상한(limit)을 구성하는 방법을 보여준다. 쿠버네티스 클러스터는 네임스페이스로 나눌 수 있다. 기본 CPU 상한이 있는 네임스페이스에서 컨테이너가 생성되고, 컨테이너가 자체 CPU 상한을 지정하지 않으면, 컨테이너에 기본 CPU 상한이 할당된다. 쿠버네티스는 이 문서의 뒷부분에서 설명하는 특정 조건에서 기본 CPU 요청량을 할당한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace default-cpu-example

리밋레인지(LimitRange)와 파드 생성

다음은 리밋레인지 오브젝트의 구성 파일이다. 구성은 기본 CPU 요청량 및 기본 CPU 상한을 지정한다.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

default-cpu-example 네임스페이스에 리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults.yaml --namespace=default-cpu-example

이제 컨테이너가 default-cpu-example 네임스페이스에 생성되고, 컨테이너가 CPU 요청량 및 CPU 상한에 대해 고유한 값을 지정하지 않으면, 컨테이너에 CPU 요청량의 기본값 0.5와 CPU 상한 기본값 1이 부여된다.

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 CPU 요청량과 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod.yaml --namespace=default-cpu-example

파드의 사양을 확인한다.

kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example

출력 결과는 파드의 컨테이너에 500 milicpu의 CPU 요청량과 1 cpu의 CPU 상한이 있음을 나타낸다. 이것은 리밋레인지에 의해 지정된 기본값이다.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

컨테이너 상한은 지정하고, 요청량을 지정하지 않으면 어떻게 되나?

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 CPU 상한을 지정하지만, 요청량은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-2
spec:
  containers:
  - name: default-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-2.yaml --namespace=default-cpu-example

파드 사양을 확인한다.

kubectl get pod default-cpu-demo-2 --output=yaml --namespace=default-cpu-example

출력 결과는 컨테이너의 CPU 요청량이 CPU 상한과 일치하도록 설정되었음을 보여준다. 참고로 컨테이너에는 CPU 요청량의 기본값인 0.5 cpu가 할당되지 않았다.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "1"

컨테이너의 요청량은 지정하고, 상한을 지정하지 않으면 어떻게 되나?

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 CPU 요청량을 지정하지만, 상한은 지정하지 않았다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-3
spec:
  containers:
  - name: default-cpu-demo-3-ctr
    image: nginx
    resources:
      requests:
        cpu: "0.75"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-3.yaml --namespace=default-cpu-example

파드 사양을 확인한다.

kubectl get pod default-cpu-demo-3 --output=yaml --namespace=default-cpu-example

출력 결과는 컨테이너의 CPU 요청량이 컨테이너의 구성 파일에 지정된 값으로 설정되었음을 보여준다. 컨테이너의 CPU 상한은 1 cpu로 설정되며, 이는 네임스페이스의 CPU 상한 기본값이다.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 750m

CPU 상한 및 요청량의 기본값에 대한 동기

네임스페이스에 리소스 쿼터가 있는 경우, CPU 상한에 대해 기본값을 설정하는 것이 좋다. 다음은 리소스 쿼터가 네임스페이스에 적용하는 두 가지 제한 사항이다.

  • 네임스페이스에서 실행되는 모든 컨테이너에는 자체 CPU 상한이 있어야 한다.
  • 네임스페이스의 모든 컨테이너가 사용하는 총 CPU 양은 지정된 상한을 초과하지 않아야 한다.

컨테이너가 자체 CPU 상한을 지정하지 않으면, 상한 기본값이 부여되고, 쿼터에 의해 제한되는 네임스페이스에서 실행될 수 있다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace default-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.2.3 - 네임스페이스에 대한 메모리의 최소 및 최대 제약 조건 구성

이 페이지는 네임스페이스에서 실행되는 컨테이너가 사용하는 메모리의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 최소 및 최대 메모리 값을 지정한다. 파드가 리밋레인지에 의해 부과된 제약 조건을 충족하지 않으면, 네임스페이스에서 생성될 수 없다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터의 각 노드에는 최소 1GiB의 메모리가 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace constraints-mem-example

리밋레인지와 파드 생성

다음은 리밋레인지의 구성 파일이다.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

리밋레인지에 대한 자세한 정보를 본다.

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

출력 결과는 예상대로 메모리의 최소 및 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지의 구성 파일에 기본값(default)을 지정하지 않아도 자동으로 생성된다.

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

이제 constraints-mem-example 네임스페이스에 컨테이너가 생성될 때마다, 쿠버네티스는 다음 단계를 수행한다.

  • 컨테이너가 자체 메모리 요청량(request)과 상한(limit)을 지정하지 않으면, 기본 메모리 요청량과 상한을 컨테이너에 지정한다.

  • 컨테이너에 500MiB 이상의 메모리 요청량이 있는지 확인한다.

  • 컨테이너의 메모리 상한이 1GiB 이하인지 확인한다.

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너 매니페스트는 600MiB의 메모리 요청량과 800MiB의 메모리 상한을 지정한다. 이들은 리밋레인지에 의해 부과된 메모리의 최소 및 최대 제약 조건을 충족한다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

출력 결과는 컨테이너의 메모리 요청량이 600MiB이고 메모리 상한이 800MiB임을 나타낸다. 이는 리밋레인지에 의해 부과된 제약 조건을 충족한다.

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

파드를 삭제한다.

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

최대 메모리 제약 조건을 초과하는 파드 생성 시도

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 800MiB의 메모리 요청량과 1.5GiB의 메모리 상한을 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

컨테이너가 너무 큰 메모리 상한을 지정하므로, 출력 결과에 파드가 생성되지 않은 것으로 표시된다.

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

최소 메모리 요청량을 충족하지 않는 파드 생성 시도

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 100MiB의 메모리 요청량과 800MiB의 메모리 상한을 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

컨테이너가 너무 작은 메모리 요청량을 지정하므로, 출력 결과에 파드가 생성되지 않은 것으로 표시된다.

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

메모리 요청량 또는 상한을 지정하지 않은 파드 생성

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 메모리 요청량을 지정하지 않으며, 메모리 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

출력 결과는 파드의 컨테이너에 1GiB의 메모리 요청량과 1GiB의 메모리 상한이 있음을 보여준다. 컨테이너는 이러한 값을 어떻게 얻었을까?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

컨테이너가 자체 메모리 요청량과 상한을 지정하지 않았으므로, 리밋레인지의 메모리의 요청량과 상한 기본값이 제공되었다.

이 시점에서, 컨테이너가 실행 중이거나 실행 중이 아닐 수 있다. 이 태스크의 전제 조건은 노드에 최소 1GiB의 메모리가 있어야 한다는 것이다. 각 노드에 1GiB의 메모리만 있는 경우, 노드에 할당할 수 있는 메모리가 1GiB의 메모리 요청량을 수용하기에 충분하지 않을 수 있다. 메모리가 2GiB인 노드를 사용하는 경우에는, 메모리가 1GiB 요청량을 수용하기에 충분할 것이다.

파드를 삭제한다.

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

메모리의 최소 및 최대 제약 조건 적용

리밋레인지에 의해 네임스페이스에 부과된 메모리의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.

메모리의 최소 및 최대 제약 조건에 대한 동기

클러스터 관리자는 파드가 사용할 수 있는 메모리 양에 제한을 둘 수 있다. 예를 들면 다음과 같다.

  • 클러스터의 각 노드에는 2GB의 메모리가 있다. 클러스터의 어떤 노드도 2GB 이상의 요청량을 지원할 수 없으므로, 2GB 이상의 메모리를 요청하는 파드를 수락하지 않으려고 한다.

  • 클러스터는 운영 부서와 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 8GB의 메모리를 소비하도록 하려면, 개발 워크로드를 512MB로 제한해야 한다. 프로덕션 및 개발을 위해 별도의 네임스페이스를 만들고, 각 네임스페이스에 메모리 제약 조건을 적용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace constraints-mem-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.2.4 - 네임스페이스에 대한 CPU의 최소 및 최대 제약 조건 구성

이 페이지는 네임스페이스에서 컨테이너와 파드가 사용하는 CPU 리소스의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 CPU의 최솟값과 최댓값을 지정한다. 리밋레인지에 의해 부과된 제약 조건을 파드가 충족하지 않으면, 네임스페이스에서 생성될 수 없다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

태스크 예제를 실행하려면 클러스터에 적어도 1 CPU 이상이 사용 가능해야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace constraints-cpu-example

리밋레인지와 파드 생성

다음은 리밋레인지에 대한 구성 파일이다.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints.yaml --namespace=constraints-cpu-example

리밋레인지에 대한 자세한 정보를 본다.

kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example

출력 결과는 예상대로 CPU의 최소와 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지에 대한 구성 파일에 기본값을 지정하지 않아도 자동으로 생성된다.

limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container

이제 constraints-cpu-example 네임스페이스에 컨테이너가 생성될 때마다, 쿠버네티스는 다음 단계를 수행한다.

  • 컨테이너가 자체 CPU 요청량(request)과 상한(limit)을 지정하지 않으면, 컨테이너에 CPU 요청량과 상한의 기본값(default)을 지정한다.

  • 컨테이너가 200 millicpu 이상의 CPU 요청량을 지정하는지 확인한다.

  • 컨테이너가 800 millicpu 이하의 CPU 상한을 지정하는지 확인한다.

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너 매니페스트는 500 millicpu의 CPU 요청량 및 800 millicpu의 CPU 상한을 지정한다. 이는 리밋레인지에 의해 부과된 CPU의 최소와 최대 제약 조건을 충족시킨다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod.yaml --namespace=constraints-cpu-example

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

출력 결과는 컨테이너의 CPU 요청량이 500 millicpu이고, CPU 상한이 800 millicpu임을 나타낸다. 이는 리밋레인지에 의해 부과된 제약 조건을 만족시킨다.

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 500m

파드 삭제

kubectl delete pod constraints-cpu-demo --namespace=constraints-cpu-example

CPU 최대 제약 조건을 초과하는 파드 생성 시도

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 500 millicpu의 CPU 요청량과 1.5 cpu의 CPU 상한을 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

컨테이너가 너무 큰 CPU 상한을 지정하므로, 출력 결과에 파드가 생성되지 않은 것으로 표시된다.

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

최소 CPU 요청량을 충족하지 않는 파드 생성 시도

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 100 millicpu의 CPU 요청량과 800 millicpu의 CPU 상한을 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

컨테이너가 너무 작은 CPU 요청량을 지정하므로, 출력 결과에 파드가 생성되지 않은 것으로 표시된다.

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-3" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.

CPU 요청량 또는 상한을 지정하지 않은 파드 생성

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 CPU 요청량을 지정하지 않으며, CPU 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

출력 결과는 파드의 컨테이너에 대한 CPU 요청량이 800 millicpu이고, CPU 상한이 800 millicpu임을 나타낸다. 컨테이너는 어떻게 이런 값을 얻었을까?

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 800m

컨테이너가 자체 CPU 요청량과 상한을 지정하지 않았으므로, 리밋레인지로부터 CPU 요청량과 상한의 기본값이 주어졌다.

이 시점에서, 컨테이너는 실행 중이거나 실행 중이 아닐 수 있다. 이 태스크의 전제 조건은 클러스터에 1 CPU 이상 사용 가능해야 한다는 것이다. 각 노드에 1 CPU만 있는 경우, 노드에 할당할 수 있는 CPU가 800 millicpu의 요청량을 수용하기에 충분하지 않을 수 있다. 2 CPU인 노드를 사용하는 경우에는, CPU가 800 millicpu 요청량을 수용하기에 충분할 것이다.

파드를 삭제한다.

kubectl delete pod constraints-cpu-demo-4 --namespace=constraints-cpu-example

CPU의 최소 및 최대 제약 조건의 적용

리밋레인지에 의해 네임스페이스에 부과된 CPU의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.

CPU의 최소 및 최대 제약 조건에 대한 동기

클러스터 관리자는 파드가 사용할 수 있는 CPU 리소스에 제한을 둘 수 있다. 예를 들면 다음과 같다.

  • 클러스터의 각 노드에는 2 CPU가 있다. 클러스터의 어떤 노드도 요청량을 지원할 수 없기 때문에, 2 CPU 이상을 요청하는 파드를 수락하지 않으려고 한다.

  • 클러스터는 프로덕션과 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 3 CPU를 소비하도록 하고 싶지만, 개발 워크로드는 1 CPU로 제한하려고 한다. 프로덕션과 개발을 위해 별도의 네임스페이스를 생성하고, 각 네임스페이스에 CPU 제약 조건을 적용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace constraints-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.2.5 - 네임스페이스에 대한 메모리 및 CPU 쿼터 구성

이 페이지는 네임스페이스에서 실행 중인 모든 컨테이너가 사용할 수 있는 총 메모리 및 CPU 양에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터의 각 노드에는 최소 1GiB의 메모리가 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace quota-mem-cpu-example

리소스쿼터 생성

다음은 리소스쿼터 오브젝트의 구성 파일이다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

리소스쿼터를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

리소스쿼터는 이러한 요구 사항을 quota-mem-cpu-example 네임스페이스에 배치한다.

  • 모든 컨테이너에는 메모리 요청량(request), 메모리 상한(limit), CPU 요청량 및 CPU 상한이 있어야 한다.
  • 모든 컨테이너에 대한 총 메모리 요청량은 1GiB를 초과하지 않아야 한다.
  • 모든 컨테이너에 대한 총 메모리 상한은 2GiB를 초과하지 않아야 한다.
  • 모든 컨테이너에 대한 총 CPU 요청량은 1 cpu를 초과해서는 안된다.
  • 모든 컨테이너에 대한 총 CPU 상한은 2 cpu를 초과해서는 안된다.

파드 생성

파드의 구성 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

다시 한 번, 리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

출력 결과는 쿼터와 사용된 쿼터를 함께 보여준다. 파드의 메모리와 CPU 요청량 및 상한이 쿼터를 초과하지 않은 것을 볼 수 있다.

status:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
  used:
    limits.cpu: 800m
    limits.memory: 800Mi
    requests.cpu: 400m
    requests.memory: 600Mi

두 번째 파드 생성 시도

다음은 두 번째 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"

구성 파일에서, 파드의 메모리 요청량이 700MiB임을 알 수 있다. 사용된 메모리 요청량과 이 새 메모리 요청량의 합계가 메모리 요청량 쿼터를 초과한다. 600MiB + 700MiB > 1GiB

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

두 번째 파드는 생성되지 않는다. 출력 결과는 두 번째 파드를 생성하면 메모리 요청량의 총 합계가 메모리 요청량 쿼터를 초과함을 보여준다.

Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi

토론

이 연습에서 보았듯이, 리소스쿼터를 사용하여 네임스페이스에서 실행 중인 모든 컨테이너에 대한 메모리 요청량의 총 합계를 제한할 수 있다. 메모리 상한, CPU 요청량 및 CPU 상한의 총 합계를 제한할 수도 있다.

모든 컨테이너에 대한 합계 대신 개별 컨테이너를 제한하려면, 리밋레인지(LimitRange)를 사용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace quota-mem-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.2.6 - 네임스페이스에 대한 파드 쿼터 구성

이 페이지는 네임스페이스에서 실행할 수 있는 총 파드 수에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

네임스페이스 생성

이 실습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace quota-pod-example

리소스쿼터 생성

다음은 리소스쿼터 오브젝트의 구성 파일이다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"

리소스쿼터를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod.yaml --namespace=quota-pod-example

리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

출력 결과는 네임스페이스에 두 개의 파드 쿼터가 있고, 현재 파드가 없음을 보여준다. 즉, 쿼터 중 어느 것도 사용되지 않았다.

spec:
  hard:
    pods: "2"
status:
  hard:
    pods: "2"
  used:
    pods: "0"

다음은 디플로이먼트(Deployment) 구성 파일이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

구성 파일에서, replicas: 3 은 쿠버네티스가 모두 동일한 애플리케이션을 실행하는 세 개의 파드를 만들도록 지시한다.

디플로이먼트를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod-deployment.yaml --namespace=quota-pod-example

디플로이먼트에 대한 자세한 정보를 본다.

kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml

출력 결과는 디플로이먼트에서 3개의 레플리카를 지정하더라도, 쿼터로 인해 2개의 파드만 생성되었음을 보여준다.

spec:
  ...
  replicas: 3
...
status:
  availableReplicas: 2
...
lastUpdateTime: 2017-07-07T20:57:05Z
    message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
      exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'

정리

네임스페이스를 삭제한다.

kubectl delete namespace quota-pod-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4.2.3 - 인증서

클라이언트 인증서로 인증을 사용하는 경우 easyrsa, openssl 또는 cfssl 을 통해 인증서를 수동으로 생성할 수 있다.

easyrsa

easyrsa 는 클러스터 인증서를 수동으로 생성할 수 있다.

  1. easyrsa3의 패치 버전을 다운로드하여 압축을 풀고, 초기화한다.

    curl -LO https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  2. 새로운 인증 기관(CA)을 생성한다. --batch 는 자동 모드를 설정한다. --req-cn 는 CA의 새 루트 인증서에 대한 일반 이름(Common Name (CN))을 지정한다.

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  3. 서버 인증서와 키를 생성한다. --subject-alt-name 인수는 API 서버에 접근이 가능한 IP와 DNS 이름을 설정한다. MASTER_CLUSTER_IP 는 일반적으로 API 서버와 컨트롤러 관리자 컴포넌트에 대해 --service-cluster-ip-range 인수로 지정된 서비스 CIDR의 첫 번째 IP이다. --days 인수는 인증서가 만료되는 일 수를 설정하는데 사용된다. 또한, 아래 샘플은 기본 DNS 이름으로 cluster.local 을 사용한다고 가정한다.

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  4. pki/ca.crt, pki/issued/server.crt 그리고 pki/private/server.key 를 디렉터리에 복사한다.

  5. API 서버 시작 파라미터에 다음 파라미터를 채우고 추가한다.

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

openssl 은 클러스터 인증서를 수동으로 생성할 수 있다.

  1. ca.key를 2048bit로 생성한다.

    openssl genrsa -out ca.key 2048
    
  2. ca.key에 따라 ca.crt를 생성한다(인증서 유효 기간을 사용하려면 -days를 사용한다).

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  3. server.key를 2048bit로 생성한다.

    openssl genrsa -out server.key 2048
    
  4. 인증서 서명 요청(Certificate Signing Request (CSR))을 생성하기 위한 설정 파일을 생성한다. 파일에 저장하기 전에 꺾쇠 괄호(예: <MASTER_IP>)로 표시된 값을 실제 값으로 대체한다(예: csr.conf). MASTER_CLUSTER_IP 의 값은 이전 하위 섹션에서 설명한 대로 API 서버의 서비스 클러스터 IP이다. 또한, 아래 샘플에서는 cluster.local 을 기본 DNS 도메인 이름으로 사용하고 있다고 가정한다.

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <국가(country)>
    ST = <도(state)>
    L = <시(city)>
    O = <조직(organization)>
    OU = <조직 단위(organization unit)>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  5. 설정 파일을 기반으로 인증서 서명 요청을 생성한다.

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  6. ca.key, ca.crt 그리고 server.csr을 사용해서 서버 인증서를 생성한다.

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out server.crt -days 10000 \
    -extensions v3_ext -extfile csr.conf
    
  7. 인증서 서명 요청을 확인한다.

    openssl req  -noout -text -in ./server.csr
    
  8. 인증서를 확인한다.

    openssl x509  -noout -text -in ./server.crt
    

마지막으로, API 서버 시작 파라미터에 동일한 파라미터를 추가한다.

cfssl

cfssl 은 인증서 생성을 위한 또 다른 도구이다.

  1. 아래에 표시된 대로 커맨드 라인 도구를 다운로드하여 압축을 풀고 준비한다. 사용 중인 하드웨어 아키텍처 및 cfssl 버전에 따라 샘플 명령을 조정해야 할 수도 있다.

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  2. 아티팩트(artifact)를 보유할 디렉터리를 생성하고 cfssl을 초기화한다.

    mkdir cert
    cd cert
    ../cfssl print-defaults config > config.json
    ../cfssl print-defaults csr > csr.json
    
  3. CA 파일을 생성하기 위한 JSON 설정 파일을 ca-config.json 예시와 같이 생성한다.

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  4. CA 인증서 서명 요청(CSR)을 위한 JSON 설정 파일을 ca-csr.json 예시와 같이 생성한다. 꺾쇠 괄호로 표시된 값을 사용하려는 실제 값으로 변경한다.

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "<국가(country)>",
        "ST": "<도(state)>",
        "L": "<시(city)>",
        "O": "<조직(organization)>",
        "OU": "<조직 단위(organization unit)>"
      }]
    }
    
  5. CA 키(ca-key.pem)와 인증서(ca.pem)을 생성한다.

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  6. API 서버의 키와 인증서를 생성하기 위한 JSON 구성파일을 server-csr.json 예시와 같이 생성한다. 꺾쇠 괄호 안의 값을 사용하려는 실제 값으로 변경한다. MASTER_CLUSTER_IP 는 이전 하위 섹션에서 설명한 API 서버의 클러스터 IP이다. 아래 샘플은 기본 DNS 도메인 이름으로 cluster.local 을 사용한다고 가정한다.

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
        "C": "<국가(country)>",
        "ST": "<도(state)>",
        "L": "<시(city)>",
        "O": "<조직(organization)>",
        "OU": "<조직 단위(organization unit)>"
      }]
    }
    
  7. API 서버 키와 인증서를 생성하면, 기본적으로 server-key.pemserver.pem 파일에 각각 저장된다.

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
    --config=ca-config.json -profile=kubernetes \
    server-csr.json | ../cfssljson -bare server
    

자체 서명된 CA 인증서의 배포

클라이언트 노드는 자체 서명된 CA 인증서를 유효한 것으로 인식하지 않을 수 있다. 비-프로덕션 디플로이먼트 또는 회사 방화벽 뒤에서 실행되는 디플로이먼트의 경우, 자체 서명된 CA 인증서를 모든 클라이언트에 배포하고 유효한 인증서의 로컬 목록을 새로 고칠 수 있다.

각 클라이언트에서, 다음 작업을 수행한다.

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

인증서 API

certificates.k8s.io API를 사용해서 여기에 설명된 대로 인증에 사용할 x509 인증서를 프로비전 할 수 있다.

4.2.4 - 네트워크 폴리시 제공자(Network Policy Provider) 설치

4.2.4.1 - 네트워크 폴리시로 캘리코(Calico) 사용하기

이 페이지는 쿠버네티스에서 캘리코(Calico) 클러스터를 생성하는 몇 가지 빠른 방법을 살펴본다.

시작하기 전에

클라우드지역 클러스터 중에 어디에 배포할지 결정한다.

구글 쿠버네티스 엔진(GKE)에 캘리코 클러스터 생성하기

사전요구사항: gcloud.

  1. 캘리코로 GKE 클러스터를 시작하려면, --enable-network-policy 플래그를 추가한다.

    문법

    gcloud container clusters create [클러스터_이름] --enable-network-policy
    

    예시

    gcloud container clusters create my-calico-cluster --enable-network-policy
    
  2. 배포를 확인하기 위해, 다음 커맨드를 이용하자.

    kubectl get pods --namespace=kube-system
    

    캘리코 파드는 calico로 시작한다. 각각의 상태가 Running임을 확인하자.

kubeadm으로 지역 캘리코 클러스터 생성하기

Kubeadm을 이용해서 15분 이내에 지역 단일 호스트 캘리코 클러스터를 생성하려면, 캘리코 빠른 시작을 참고한다.

다음 내용

클러스터가 동작하면, 쿠버네티스 네트워크 폴리시(NetworkPolicy)를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.2.4.2 - 네트워크 폴리시로 실리움(Cilium) 사용하기

이 페이지는 어떻게 네트워크 폴리시(NetworkPolicy)로 실리움(Cilium)를 사용하는지 살펴본다.

실리움의 배경에 대해서는 실리움 소개를 읽어보자.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

기본 시험을 위해 실리움을 Minikube에 배포하기

실리움에 쉽게 친숙해지기 위해 Minikube에 실리움을 기본적인 데몬셋으로 설치를 수행하는 실리움 쿠버네티스 시작하기 안내를 따라 해볼 수 있다.

Minikube를 시작하려면 최소 버전으로 >= v1.5.2 이 필요하고, 다음의 실행 파라미터로 실행한다.

minikube version
minikube version: v1.5.2
minikube start --network-plugin=cni

minikube의 경우 CLI 도구를 사용하여 실리움을 설치할 수 있다. 실리움은 클러스터 구성을 자동으로 감지하고 성공적인 설치를 위해 적절한 구성 요소를 설치한다.

curl -LO https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz
cilium install
🔮 Auto-detected Kubernetes kind: minikube
✨ Running "minikube" validation checks
✅ Detected minikube version "1.20.0"
ℹ️  Cilium version not set, using default version "v1.10.0"
🔮 Auto-detected cluster name: minikube
🔮 Auto-detected IPAM mode: cluster-pool
🔮 Auto-detected datapath mode: tunnel
🔑 Generating CA...
2021/05/27 02:54:44 [INFO] generate received request
2021/05/27 02:54:44 [INFO] received CSR
2021/05/27 02:54:44 [INFO] generating key: ecdsa-256
2021/05/27 02:54:44 [INFO] encoded CSR
2021/05/27 02:54:44 [INFO] signed certificate with serial number 48713764918856674401136471229482703021230538642
🔑 Generating certificates for Hubble...
2021/05/27 02:54:44 [INFO] generate received request
2021/05/27 02:54:44 [INFO] received CSR
2021/05/27 02:54:44 [INFO] generating key: ecdsa-256
2021/05/27 02:54:44 [INFO] encoded CSR
2021/05/27 02:54:44 [INFO] signed certificate with serial number 3514109734025784310086389188421560613333279574
🚀 Creating Service accounts...
🚀 Creating Cluster roles...
🚀 Creating ConfigMap...
🚀 Creating Agent DaemonSet...
🚀 Creating Operator Deployment...
⌛ Waiting for Cilium to be installed...

시작하기 안내서의 나머지 부분은 예제 애플리케이션을 이용하여 L3/L4(예, IP 주소 + 포트) 모두의 보안 정책뿐만 아니라 L7(예, HTTP)의 보안 정책을 적용하는 방법을 설명한다.

실리움을 실 서비스 용도로 배포하기

실리움을 실 서비스 용도의 배포에 관련한 자세한 방법은 실리움 쿠버네티스 설치 안내를 살펴본다. 이 문서는 자세한 요구사항, 방법과 실제 데몬셋 예시를 포함한다.

실리움 구성요소 이해하기

실리움으로 클러스터를 배포하면 파드가 kube-system 네임스페이스에 추가된다. 파드의 목록을 보려면 다음을 실행한다.

kubectl get pods --namespace=kube-system -l k8s-app=cilium

다음과 유사한 파드의 목록을 볼 것이다.

NAME           READY   STATUS    RESTARTS   AGE
cilium-kkdhz   1/1     Running   0          3m23s
...

cilium 파드는 클러스터 각 노드에서 실행되며, 리눅스 BPF를 사용해서 해당 노드의 파드에 대한 트래픽 네트워크 폴리시를 적용한다.

다음 내용

클러스터가 동작하면, 실리움으로 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 재미있게 즐기고, 질문이 있다면 실리움 슬랙 채널을 이용하여 연락한다.

4.2.4.3 - 네트워크 폴리시로 큐브 라우터(Kube-router) 사용하기

이 페이지는 네트워크 폴리시(NetworkPolicy)로 큐브 라우터(Kube-router)를 사용하는 방법을 살펴본다.

시작하기 전에

운영 중인 쿠버네티스 클러스터가 필요하다. 클러스터가 없다면, Kops, Bootkube, Kubeadm 등을 이용해서 클러스터를 생성할 수 있다.

큐브 라우터 애드온 설치하기

큐브 라우터 애드온은 갱신된 모든 네트워크 폴리시 및 파드에 대해 쿠버네티스 API 서버를 감시하고, 정책에 따라 트래픽을 허용하거나 차단하도록 iptables 규칙와 ipset을 구성하는 네트워크 폴리시 컨트롤러와 함께 제공된다. 큐브 라우터 애드온을 설치하는 큐브 라우터를 클러스터 인스톨러와 함께 사용하기 안내서를 따라해 봅니다.

다음 내용

큐브 라우터 애드온을 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.2.4.4 - 네트워크 폴리시로 로마나(Romana)

이 페이지는 네트워크 폴리시(NetworkPolicy)로 로마나(Romana)를 사용하는 방법을 살펴본다.

시작하기 전에

kubeadm 시작하기의 1, 2, 3 단계를 완료하자.

kubeadm으로 로마나 설치하기

Kubeadm을 위한 컨테이너화된 설치 안내서를 따른다.

네트워크 폴리시 적용하기

네트워크 폴리시를 적용하기 위해 다음 중에 하나를 사용하자.

다음 내용

로마나를 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.2.4.5 - 네트워크 폴리시로 위브넷(Weave Net) 사용하기

이 페이지는 네트워크 폴리시(NetworkPolicy)로 위브넷(Weave Net)를 사용하는 방법을 살펴본다.

시작하기 전에

쿠버네티스 클러스터가 필요하다. 맨 땅에서부터 시작하기를 위해서 kubeadm 시작하기 안내서를 따른다.

Weave Net 애드온을 설치한다

애드온을 통한 쿠버네티스 통합하기 가이드를 따른다.

쿠버네티스의 위브넷 애드온은 쿠버네티스의 모든 네임스페이스의 네크워크 정책 어노테이션을 자동으로 모니터링하며, 정책에 따라 트래픽을 허용하고 차단하는 iptables 규칙을 구성하는 네트워크 폴리시 컨트롤러와 함께 제공된다.

설치 시험

위브넷이 동작하는지 확인한다.

다음 커맨드를 입력한다.

kubectl get pods -n kube-system -o wide

출력은 다음과 유사하다.

NAME                                    READY     STATUS    RESTARTS   AGE       IP              NODE
weave-net-1t1qg                         2/2       Running   0          9d        192.168.2.10    worknode3
weave-net-231d7                         2/2       Running   1          7d        10.2.0.17       worknodegpu
weave-net-7nmwt                         2/2       Running   3          9d        192.168.2.131   masternode
weave-net-pmw8w                         2/2       Running   0          9d        192.168.2.216   worknode2

위브넷 파드를 가진 각 노드와 모든 파드는 Running이고 2/2 READY이다(2/2는 각 파드가 weaveweave-npc를 가지고 있음을 뜻한다).

다음 내용

위브넷 애드온을 설치하고 나서, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 질문이 있으면 슬랙 #weave-community 이나 Weave 유저그룹에 연락한다.

4.2.5 - DNS 서비스 사용자 정의하기

이 페이지는 클러스터 안에서 사용자의 DNS 파드(Pod) 를 설정하고 DNS 변환(DNS resolution) 절차를 사용자 정의하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

클러스터는 CoreDNS 애드온을 구동하고 있어야 한다. CoreDNS로 이관하기kubeadm 을 이용하여 kube-dns 로부터 이관하는 방법을 설명한다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.12. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

소개

DNS는 애드온 관리자클러스터 애드온을 사용하여 자동으로 시작되는 쿠버네티스 내장 서비스이다.

쿠버네티스 v1.12 부터, CoreDNS는 kube-dns를 대체하여 권장되는 DNS 서버이다. 만약 사용자의 클러스터가 원래 kube-dns를 사용하였을 경우, CoreDNS 대신 kube-dns 를 계속 사용할 수도 있다.

CoreDNS를 디플로이먼트(Deployment)로 실행하고 있을 경우, 일반적으로 고정 IP 주소를 갖는 쿠버네티스 서비스로 노출된다. Kubelet 은 --cluster-dns=<dns-service-ip> 플래그를 사용하여 DNS 확인자 정보를 각 컨테이너에 전달한다.

DNS 이름에도 도메인이 필요하다. 사용자는 kubelet 에 있는 --cluster-domain=<default-local-domain> 플래그를 통하여 로컬 도메인을 설정할 수 있다.

DNS 서버는 정방향 조회(A 및 AAAA 레코드), 포트 조회(SRV 레코드), 역방향 IP 주소 조회(PTR 레코드) 등을 지원한다. 더 자세한 내용은 서비스 및 파드용 DNS를 참고한다.

만약 파드의 dnsPolicydefault 로 지정되어 있는 경우, 파드는 자신이 실행되는 노드의 이름 변환(name resolution) 구성을 상속한다. 파드의 DNS 변환도 노드와 동일하게 작동해야 한다. 그 외에는 알려진 이슈를 참고한다.

만약 위와 같은 방식을 원하지 않거나, 파드를 위해 다른 DNS 설정이 필요한 경우, 사용자는 kubelet 의 --resolv-conf 플래그를 사용할 수 있다. 파드가 DNS를 상속받지 못하도록 하기 위해 이 플래그를 ""로 설정한다. DNS 상속을 위해 /etc/resolv.conf 이외의 파일을 지정할 경우 유효한 파일 경로를 설정한다.

CoreDNS

CoreDNS는 dns 명세를 준수하며 클러스터 DNS 역할을 할 수 있는, 범용적인 권한을 갖는 DNS 서버이다.

CoreDNS 컨피그맵(ConfigMap) 옵션

CoreDNS는 모듈형이자 플러그인이 가능한 DNS 서버이며, 각 플러그인들은 CoreDNS에 새로운 기능을 부가한다. 이는 CoreDNS 구성 파일인 Corefile을 관리하여 구성할 수 있다. 클러스터 관리자는 CoreDNS Corefile에 대한 컨피그맵을 수정하여 해당 클러스터에 대한 DNS 서비스 검색 동작을 변경할 수 있다.

쿠버네티스에서 CoreDNS는 아래의 기본 Corefile 구성으로 설치된다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

Corefile의 구성은 CoreDNS의 아래 플러그인을 포함한다.

  • errors: 오류가 표준 출력(stdout)에 기록된다.
  • health: CoreDNS의 상태(healthy)가 http://localhost:8080/health 에 기록된다. 이 확장 구문에서 lameduck 은 프로세스를 비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.
  • ready: 8181 포트의 HTTP 엔드포인트가, 모든 플러그인이 준비되었다는 신호를 보내면 200 OK 를 반환한다.
  • kubernetes: CoreDNS가 쿠버네티스의 서비스 및 파드의 IP를 기반으로 DNS 쿼리에 대해 응답한다. 해당 플러그인에 대한 세부 사항은 CoreDNS 웹사이트에서 확인할 수 있다. ttl 을 사용하면 응답에 대한 사용자 정의 TTL 을 지정할 수 있으며, 기본값은 5초이다. 허용되는 최소 TTL은 0초이며, 최대값은 3600초이다. 레코드가 캐싱되지 않도록 할 경우, TTL을 0으로 설정한다. pods insecure 옵션은 kube-dns 와의 하위 호환성을 위해 제공된다. pods verified 옵션을 사용하여, 일치하는 IP의 동일 네임스페이스(Namespace)에 파드가 존재하는 경우에만 A 레코드를 반환하게 할 수 있다. pods disabled 옵션은 파드 레코드를 사용하지 않을 경우 사용된다.
  • prometheus: CoreDNS의 메트릭은 프로메테우스 형식(OpenMetrics 라고도 알려진)의 http://localhost:9153/metrics 에서 사용 가능하다.
  • forward: 쿠버네티스 클러스터 도메인에 없는 쿼리들은 모두 사전에 정의된 리졸버(/etc/resolv.conf)로 전달된다.
  • cache: 프론트 엔드 캐시를 활성화한다.
  • loop: 간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt)한다.
  • reload: 변경된 Corefile을 자동으로 다시 로드하도록 한다. 컨피그맵 설정을 변경한 후에 변경 사항이 적용되기 위하여 약 2분정도 소요된다.
  • loadbalance: 응답에 대하여 A, AAAA, MX 레코드의 순서를 무작위로 선정하는 라운드-로빈 DNS 로드밸런서이다.

사용자는 컨피그맵을 변경하여 기본 CoreDNS 동작을 변경할 수 있다.

CoreDNS를 사용하는 스텁 도메인(Stub-domain)과 업스트림 네임서버(nameserver)의 설정

CoreDNS는 포워드 플러그인을 사용하여 스텁 도메인 및 업스트림 네임서버를 구성할 수 있다.

예시

만약 클러스터 운영자가 10.150.0.1 에 위치한 Consul 도메인 서버를 가지고 있고, 모든 Consul 이름의 접미사가 .consul.local 인 경우, CoreDNS에서 이를 구성하기 위해 클러스터 관리자는 CoreDNS 컨피그맵에서 다음 구문을 생성한다.

consul.local:53 {
        errors
        cache 30
        forward . 10.150.0.1
    }

모든 비 클러스터의 DNS 조회가 172.16.0.1 의 특정 네임서버를 통과하도록 할 경우, /etc/resolv.conf 대신 forward 를 네임서버로 지정한다.

forward .  172.16.0.1

기본 Corefile 구성에 따른 최종 컨피그맵은 다음과 같다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . 172.16.0.1
        cache 30
        loop
        reload
        loadbalance
    }
    consul.local:53 {
        errors
        cache 30
        forward . 10.150.0.1
    }    

Kubeadm 툴은 kube-dns 컨피그맵에서 동일한 설정의 CoreDNS 컨피그맵으로의 자동 변환을 지원한다.

kube-dns에 대응되는 CoreDNS 설정

CoreDNS는 kube-dns 이상의 기능을 지원한다. StubDomainsupstreamNameservers 를 지원하도록 생성된 kube-dns의 컨피그맵은 CoreDNS의 forward 플러그인으로 변환된다.

예시

kube-dns에 대한 이 컨피그맵 예제는 stubDomains 및 upstreamNameservers를 지정한다.

apiVersion: v1
data:
  stubDomains: |
        {"abc.com" : ["1.2.3.4"], "my.cluster.local" : ["2.3.4.5"]}
  upstreamNameservers: |
        ["8.8.8.8", "8.8.4.4"]
kind: ConfigMap

CoreDNS에서는 동등한 설정으로 Corefile을 생성한다.

  • stubDomains 에 대응하는 설정:
abc.com:53 {
    errors
    cache 30
    forward . 1.2.3.4
}
my.cluster.local:53 {
    errors
    cache 30
    forward . 2.3.4.5
}

기본 플러그인으로 구성된 완전한 Corefile.

.:53 {
    errors
    health
    kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        fallthrough in-addr.arpa ip6.arpa
    }
    federation cluster.local {
        foo foo.feddomain.com
    }
    prometheus :9153
    forward . 8.8.8.8 8.8.4.4
    cache 30
}
abc.com:53 {
    errors
    cache 30
    forward . 1.2.3.4
}
my.cluster.local:53 {
    errors
    cache 30
    forward . 2.3.4.5
}

CoreDNS로의 이관

kube-dns에서 CoreDNS로 이관하기 위하여, kube-dns를 CoreDNS로 교체하여 적용하는 방법에 대한 상세 정보는 블로그 기사를 참고한다.

또한 공식적인 CoreDNS 배포 스크립트를 사용하여 이관할 수도 있다.

다음 내용

4.2.6 - 고가용성 쿠버네티스 클러스터 컨트롤 플레인 설정하기

FEATURE STATE: Kubernetes v1.5 [alpha]

구글 컴퓨트 엔진(Google Compute Engine, 이하 GCE)의 kube-up이나 kube-down 스크립트에 쿠버네티스 컨트롤 플레인 노드를 복제할 수 있다. 하지만 이러한 스크립트들은 프로덕션 용도로 사용하기에 적합하지 않으며, 프로젝트의 CI에서만 주로 사용된다. 이 문서는 kube-up/down 스크립트를 사용하여 고가용(HA) 컨트롤 플레인을 관리하는 방법과 GCE와 함께 사용하기 위해 HA 컨트롤 플레인을 구현하는 방법에 관해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

HA 호환 클러스터 시작

새 HA 호환 클러스터를 생성하려면, kube-up 스크립트에 다음 플래그를 설정해야 한다.

  • MULTIZONE=true - 서버의 기본 영역(zone)과 다른 영역에서 컨트롤 플레인 kubelet이 제거되지 않도록 한다. 여러 영역에서 컨트롤 플레인 노드를 실행(권장됨)하려는 경우에 필요하다.

  • ENABLE_ETCD_QUORUM_READ=true - 모든 API 서버에서 읽은 내용이 최신 데이터를 반환하도록 하기 위한 것이다. true인 경우, Etcd의 리더 복제본에서 읽는다. 이 값을 true로 설정하는 것은 선택 사항이다. 읽기는 더 안정적이지만 느리게 된다.

선택적으로, 첫 번째 컨트롤 플레인 노드가 생성될 GCE 영역을 지정할 수 있다. 다음 플래그를 설정한다.

  • KUBE_GCE_ZONE=zone - 첫 번째 컨트롤 플레인 노드가 실행될 영역.

다음 샘플 커맨드는 europe-west1-b GCE 영역에 HA 호환 클러스터를 구성한다.

MULTIZONE=true KUBE_GCE_ZONE=europe-west1-b  ENABLE_ETCD_QUORUM_READS=true ./cluster/kube-up.sh

위의 커맨드는 하나의 컨트롤 플레인 노드를 포함하는 클러스터를 생성한다. 그러나 후속 커맨드로 새 컨트롤 플레인 노드를 추가할 수 있다.

새 컨트롤 플레인 노드 추가

HA 호환 클러스터를 생성했다면, 여기에 컨트롤 플레인 노드를 추가할 수 있다. kube-up 스크립트에 다음 플래그를 사용하여 컨트롤 플레인 노드를 추가한다.

  • KUBE_REPLICATE_EXISTING_MASTER=true - 기존 컨트롤 플레인 노드의 복제본을 만든다.

  • KUBE_GCE_ZONE=zone - 컨트롤 플레인 노드가 실행될 영역. 반드시 다른 컨트롤 플레인 노드가 존재하는 영역과 동일한 지역(region)에 있어야 한다.

HA 호환 클러스터를 시작할 때, 상속되는 MULTIZONE이나 ENABLE_ETCD_QUORUM_READS 플래그를 따로 설정할 필요는 없다.

다음 샘플 커맨드는 기존 HA 호환 클러스터에서 컨트롤 플레인 노드를 복제한다.

KUBE_GCE_ZONE=europe-west1-c KUBE_REPLICATE_EXISTING_MASTER=true ./cluster/kube-up.sh

컨트롤 플레인 노드 제거

다음 플래그가 있는 kube-down 스크립트를 사용하여 HA 클러스터에서 컨트롤 플레인 노드를 제거할 수 있다.

  • KUBE_DELETE_NODES=false - kubelet을 삭제하지 않기 위한 것이다.

  • KUBE_GCE_ZONE=zone - 컨트롤 플레인 노드가 제거될 영역.

  • KUBE_REPLICA_NAME=replica_name - (선택) 제거할 컨트롤 플레인 노드의 이름. 명시하지 않으면, 해당 영역의 모든 복제본이 제거된다.

다음 샘플 커맨드는 기존 HA 클러스터에서 컨트롤 플레인 노드를 제거한다.

KUBE_DELETE_NODES=false KUBE_GCE_ZONE=europe-west1-c ./cluster/kube-down.sh

동작에 실패한 컨트롤 플레인 노드 처리

HA 클러스터의 컨트롤 플레인 노드 중 하나가 동작에 실패하면, 클러스터에서 해당 노드를 제거하고 동일한 영역에 새 컨트롤 플레인 노드를 추가하는 것이 가장 좋다. 다음 샘플 커맨드로 이 과정을 시연한다.

  1. 손상된 복제본을 제거한다.
KUBE_DELETE_NODES=false KUBE_GCE_ZONE=replica_zone KUBE_REPLICA_NAME=replica_name ./cluster/kube-down.sh
  1. 기존 복제본을 대신할 새 노드를 추가한다.
KUBE_GCE_ZONE=replica-zone KUBE_REPLICATE_EXISTING_MASTER=true ./cluster/kube-up.sh

HA 클러스터에서 컨트롤 플레인 노드 복제에 관한 모범 사례

  • 다른 영역에 컨트롤 플레인 노드를 배치하도록 한다. 한 영역이 동작에 실패하는 동안, 해당 영역에 있는 컨트롤 플레인 노드도 모두 동작에 실패할 것이다. 영역 장애를 극복하기 위해 노드를 여러 영역에 배치한다 (더 자세한 내용은 멀티 영역를 참조한다).

  • 두 개의 노드로 구성된 컨트롤 플레인은 사용하지 않는다. 두 개의 노드로 구성된 컨트롤 플레인에서의 합의를 위해서는 지속적 상태(persistent state) 변경 시 두 컨트롤 플레인 노드가 모두 정상적으로 동작 중이어야 한다. 결과적으로 두 컨트롤 플레인 노드 모두 필요하고, 둘 중 한 컨트롤 플레인 노드에만 장애가 발생해도 클러스터의 심각한 장애 상태를 초래한다. 따라서 HA 관점에서는 두 개의 노드로 구성된 컨트롤 플레인은 단일 노드로 구성된 컨트롤 플레인보다도 못하다.

  • 컨트롤 플레인 노드를 추가하면, 클러스터의 상태(Etcd)도 새 인스턴스로 복사된다. 클러스터가 크면, 이 상태를 복제하는 시간이 오래 걸릴 수 있다. 이 작업은 etcd 관리 가이드에 기술한 대로 Etcd 데이터 디렉터리를 마이그레이션하여 속도를 높일 수 있다. (향후에 Etcd 데이터 디렉터리 마이그레이션 지원 추가를 고려 중이다)

구현 지침

ha-control-plane

개요

위의 그림은 3개의 컨트롤 플레인 노드와 컴포넌트를 고가용 클러스터로 구성한 형상을 보여준다. 해당 컨트롤 플레인 노드의 컴포넌트들은 다음의 방법을 차용하고 있다.

  • etcd: 인스턴스는 합의(consensus)를 통해서 클러스터링되어 있다.

  • 컨트롤러들, 스케줄러, 클러스터 오토-스케일러: 리스(lease) 메커니즘을 통해 클러스터에서 각각 단 하나의 인스턴스만 활성화될 것이다.

  • 애드-온(add-on) 메니저: 애드-온들이 동기화를 유지하도록 각각 독립적으로 동작한다.

추가적으로, API 서버들 앞에서 동작하는 로드 밸런서는 내부와 외부 트래픽을 컨트롤 플레인 노드들로 연결(route)한다. 각 컨트롤 플레인 노드는 다음 모드에서 다음 구성 요소를 실행한다.

  • Etcd 인스턴스: 모든 인스턴스는 합의를 사용하여 함께 클러스터화 한다.

  • API 서버: 각 서버는 내부 Etcd와 통신한다. 클러스터의 모든 API 서버가 가용하게 된다.

  • 컨트롤러, 스케줄러, 클러스터 오토스케일러: 임대 방식을 이용한다. 각 인스턴스 중 하나만이 클러스터에서 활성화된다.

  • 애드온 매니저: 각 매니저는 애드온의 동기화를 유지하려고 독립적으로 작업한다.

또한 API 서버 앞단에 외부/내부 트래픽을 라우팅하는 로드 밸런서가 있을 것이다.

로드 밸런싱

두 번째 컨트롤 플레인 노드를 배치할 때, 두 개의 복제본에 대한 로드 밸런서가 생성될 것이고, 첫 번째 복제본의 IP 주소가 로드 밸런서의 IP 주소로 승격된다. 비슷하게 끝에서 두 번째의 컨트롤 플레인 노드를 제거한 후에는 로드 밸런서가 제거되고 해당 IP 주소는 마지막으로 남은 복제본에 할당된다. 로드 밸런서 생성 및 제거는 복잡한 작업이며, 이를 전파하는 데 시간(~20분)이 걸릴 수 있다.

컨트롤 플레인 서비스와 Kubelet

쿠버네티스 서비스에서 최신의 쿠버네티스 API 서버 목록을 유지하는 대신, 시스템은 모든 트래픽을 외부 IP 주소로 보낸다.

  • 단일 노드 컨트롤 플레인의 경우, IP 주소는 단일 컨트롤 플레인 노드를 가리킨다.

  • 고가용성 컨트롤 플레인의 경우, IP 주소는 컨트롤 플레인 노드 앞의 로드밸런서를 가리킨다.

마찬가지로 Kubelet은 외부 IP 주소를 사용하여 컨트롤 플레인과 통신한다.

컨트롤 플레인 노드 인증서

쿠버네티스는 각 컨트롤 플레인 노드의 외부 퍼블릭 IP 주소와 내부 IP 주소를 대상으로 TLS 인증서를 발급한다. 컨트롤 플레인 노드의 임시 퍼블릭 IP 주소에 대한 인증서는 없다. 임시 퍼블릭 IP 주소를 통해 컨트롤 플레인 노드에 접근하려면, TLS 검증을 건너뛰어야 한다.

etcd 클러스터화

etcd를 클러스터로 구축하려면, etcd 인스턴스간 통신에 필요한 포트를 열어야 한다(클러스터 내부 통신용). 이러한 배포를 안전하게 하기 위해, etcd 인스턴스간의 통신은 SSL을 이용하여 승인한다.

API 서버 신원

FEATURE STATE: Kubernetes v1.20 [alpha]

API 서버 식별 기능은 기능 게이트에 의해 제어되며 기본적으로 활성화되지 않는다. API 서버 시작 시 APIServerIdentity 라는 기능 게이트를 활성화하여 API 서버 신원을 활성화할 수 있다.

kube-apiserver \
--feature-gates=APIServerIdentity=true \
 # …다른 플래그는 평소와 같다.

부트스트랩 중에 각 kube-apiserver는 고유한 ID를 자신에게 할당한다. ID는 kube-apiserver-{UUID} 형식이다. 각 kube-apiserver는 kube-system 네임스페이스임대를 생성한다. 임대 이름은 kube-apiserver의 고유 ID이다. 임대에는 k8s.io/component=kube-apiserver 라는 레이블이 있다. 각 kube-apiserver는 IdentityLeaseRenewIntervalSeconds (기본값은 10초)마다 임대를 새로 갱신한다. 각 kube-apiserver는 IdentityLeaseDurationSeconds (기본값은 3600초)마다 모든 kube-apiserver 식별 ID 임대를 확인하고, IdentityLeaseDurationSeconds 이상 갱신되지 않은 임대를 삭제한다. IdentityLeaseRenewIntervalSecondsIdentityLeaseDurationSeconds는 kube-apiserver 플래그 identity-lease-renew-interval-secondsidentity-lease-duration-seconds로 구성된다.

이 기능을 활성화하는 것은 HA API 서버 조정과 관련된 기능을 사용하기 위한 전제조건이다(예: StorageVersionAPI 기능 게이트).

추가 자료

자동화된 HA 마스터 배포 - 제안 문서

4.2.7 - 기본 스토리지클래스(StorageClass) 변경하기

이 페이지는 특별한 요구사항이 없는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 볼륨을 프로비저닝 하는데 사용되는 기본 스토리지 클래스를 변경하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

왜 기본 스토리지 클래스를 변경하는가?

설치 방법에 따라, 사용자의 쿠버네티스 클러스터는 기본으로 표시된 기존 스토리지클래스와 함께 배포될 수 있다. 이 기본 스토리지클래스는 특정 스토리지 클래스가 필요하지 않은 퍼시스턴트볼륨클레임에 대해 스토리지를 동적으로 프로비저닝 하기 위해 사용된다. 더 자세한 내용은 퍼시스턴트볼륨클레임 문서를 보자.

미리 설치된 기본 스토리지클래스가 사용자의 예상되는 워크로드에 적합하지 않을수도 있다. 예를 들어, 너무 가격이 높은 스토리지를 프로비저닝 해야할 수도 있다. 이런 경우에, 기본 스토리지 클래스를 변경하거나 완전히 비활성화 하여 스토리지의 동적 프로비저닝을 방지할 수 있다.

기본 스토리지클래스를 삭제하는 경우, 사용자의 클러스터에서 구동 중인 애드온 매니저에 의해 자동으로 다시 생성될 수 있으므로 정상적으로 삭제가 되지 않을 수도 있다. 애드온 관리자 및 개별 애드온을 비활성화 하는 방법에 대한 자세한 내용은 설치 문서를 참조하자.

기본 스토리지클래스 변경하기

  1. 사용자의 클러스터에 있는 스토리지클래스 목록을 조회한다.

    kubectl get storageclass
    

    결과는 아래와 유사하다.

    NAME                 PROVISIONER               AGE
    standard (default)   kubernetes.io/gce-pd      1d
    gold                 kubernetes.io/gce-pd      1d
    

    기본 스토리지클래스는 (default) 로 표시되어 있다.

  2. 기본 스토리지클래스를 기본값이 아닌 것으로 표시한다.

    기본 스토리지클래스에는 storageclass.kubernetes.io/is-default-class 의 값이 true 로 설정되어 있다. 다른 값이거나 어노테이션이 없을 경우 false 로 처리된다.

    스토리지클래스를 기본값이 아닌 것으로 표시하려면, 그 값을 false 로 변경해야 한다.

    kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
    

    여기서 standard 는 사용자가 선택한 스토리지클래스의 이름이다.

  3. 스토리지클래스를 기본값으로 표시한다.

    이전 과정과 유사하게, 어노테이션을 추가/설정해야 한다. storageclass.kubernetes.io/is-default-class=true.

    kubectl patch storageclass gold -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
    

    최대 1개의 스토리지클래스를 기본값으로 표시할 수 있다는 것을 알아두자. 만약 2개 이상이 기본값으로 표시되면, 명시적으로 storageClassName 가 지정되지 않은 PersistentVolumeClaim 은 생성될 수 없다.

  4. 사용자가 선택한 스토리지클래스가 기본값으로 되어 있는지 확인한다.

    kubectl get storageclass
    

    결과는 아래와 유사하다.

    NAME             PROVISIONER               AGE
    standard         kubernetes.io/gce-pd      1d
    gold (default)   kubernetes.io/gce-pd      1d
    

다음 내용

4.2.8 - 네트워크 폴리시(Network Policy) 선언하기

이 문서는 사용자가 쿠버네티스 네트워크폴리시 API를 사용하여 파드(Pod)가 서로 통신하는 방법을 제어하는 네트워크 폴리시를 선언하는데 도움을 준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.8. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

네트워크 폴리시를 지원하는 네트워크 제공자를 구성하였는지 확인해야 한다. 다음과 같이 네트워크폴리시를 지원하는 많은 네트워크 제공자들이 있다.

nginx 디플로이먼트(Deployment)를 생성하고 서비스(Service)를 통해 노출하기

쿠버네티스 네트워크 폴리시가 어떻게 동작하는지 확인하기 위해서, nginx 디플로이먼트를 생성한다.

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

nginx 라는 이름의 서비스를 통해 디플로이먼트를 노출한다.

kubectl expose deployment nginx --port=80
service/nginx exposed

위 명령어들은 nginx 파드에 대한 디플로이먼트를 생성하고, nginx 라는 이름의 서비스를 통해 디플로이먼트를 노출한다. nginx 파드와 디플로이먼트는 default 네임스페이스(namespace)에 존재한다.

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

다른 파드에서 접근하여 서비스 테스트하기

사용자는 다른 파드에서 새 nginx 서비스에 접근할 수 있어야 한다. default 네임스페이스에 있는 다른 파드에서 nginx 서비스에 접근하기 위하여, busybox 컨테이너를 생성한다.

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

nginx 서비스에 대해 접근 제한하기

access: true 레이블을 가지고 있는 파드만 nginx 서비스에 접근할 수 있도록 하기 위하여, 다음과 같은 네트워크폴리시 오브젝트를 생성한다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

네트워크폴리시 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

서비스에 정책 할당하기

kubectl을 사용하여 위 nginx-policy.yaml 파일로부터 네트워크폴리시를 생성한다.

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

access 레이블이 정의되지 않은 서비스에 접근 테스트

올바른 레이블이 없는 파드에서 nginx 서비스에 접근하려 할 경우, 요청 타임 아웃이 발생한다.

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

접근 레이블을 정의하고 다시 테스트

사용자는 요청이 허용되도록 하기 위하여 올바른 레이블을 갖는 파드를 생성한다.

kubectl run busybox --rm -ti --labels="access=true" --image=busybox -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

4.2.9 - 노드에 대한 확장 리소스 알리기

이 페이지는 노드의 확장 리소스를 지정하는 방법을 보여준다. 확장 리소스를 통해 클러스터 관리자는 쿠버네티스에게 알려지지 않은 노드-레벨 리소스를 알릴 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

노드의 이름을 확인한다

kubectl get nodes

이 연습에 사용할 노드 중 하나를 선택한다.

노드 중 하나에 새로운 확장 리소스를 알린다

노드에서 새로운 확장 리소스를 알리려면, 쿠버네티스 API 서버에 HTTP PATCH 요청을 보낸다. 예를 들어, 노드 중 하나에 4개의 동글(dongle)이 있다고 가정한다. 다음은 노드에 4개의 동글 리소스를 알리는 PATCH 요청의 예이다.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

참고로 쿠버네티스는 동글이 무엇인지 또는 동글이 무엇을 위한 것인지 알 필요가 없다. 위의 PATCH 요청은 노드에 동글이라고 하는 네 가지 항목이 있음을 쿠버네티스에 알려준다.

쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.

kubectl proxy

다른 명령 창에서 HTTP PATCH 요청을 보낸다. <your-node-name> 을 노드의 이름으로 바꾼다.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status

출력은 노드가 4개의 동글 용량을 가졌음을 나타낸다.

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

노드의 정보를 확인한다.

kubectl describe node <your-node-name>

다시 한 번, 출력에 동글 리소스가 표시된다.

Capacity:
 cpu:  2
 memory:  2049008Ki
 example.com/dongle:  4

이제, 애플리케이션 개발자는 특정 개수의 동글을 요청하는 파드를 만들 수 있다. 컨테이너에 확장 리소스 할당하기를 참고한다.

토론

확장 리소스는 메모리 및 CPU 리소스와 비슷하다. 예를 들어, 노드에서 실행 중인 모든 컴포넌트가 공유할 특정 양의 메모리와 CPU가 노드에 있는 것처럼, 노드에서 실행 중인 모든 컴포넌트가 특정 동글을 공유할 수 있다. 또한 애플리케이션 개발자가 특정 양의 메모리와 CPU를 요청하는 파드를 생성할 수 있는 것처럼, 특정 동글을 요청하는 파드를 생성할 수 있다.

확장 리소스는 쿠버네티스에게 불투명하다. 쿠버네티스는 그것들이 무엇인지 전혀 모른다. 쿠버네티스는 노드에 특정 개수의 노드만 있다는 것을 알고 있다. 확장 리소스는 정수로 알려야 한다. 예를 들어, 노드는 4.5개의 동글이 아닌, 4개의 동글을 알릴 수 있다.

스토리지 예제

노드에 800GiB의 특별한 종류의 디스크 스토리지가 있다고 가정한다. example.com/special-storage와 같은 특별한 스토리지의 이름을 생성할 수 있다. 그런 다음 특정 크기, 100GiB의 청크로 알릴 수 있다. 이 경우, 노드에는 example.com/special-storage 유형의 8가지 리소스가 있다고 알린다.

Capacity:
 ...
 example.com/special-storage: 8

이 특별한 스토리지에 대한 임의 요청을 허용하려면, 1바이트 크기의 청크로 특별한 스토리지를 알릴 수 있다. 이 경우, example.com/special-storage 유형의 800Gi 리소스를 알린다.

Capacity:
 ...
 example.com/special-storage:  800Gi

그런 다음 컨테이너는 최대 800Gi의 임의 바이트 수의 특별한 스토리지를 요청할 수 있다.

정리

다음은 노드에서 동글 알림을 제거하는 PATCH 요청이다.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.

kubectl proxy

다른 명령 창에서 HTTP PATCH 요청을 보낸다. <your-node-name>을 노드의 이름으로 바꾼다.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status

동글 알림이 제거되었는지 확인한다.

kubectl describe node <your-node-name> | grep dongle

(출력이 보이지 않아야 함)

다음 내용

애플리케이션 개발자를 위한 문서

클러스터 관리자를 위한 문서

4.2.10 - 서비스 디스커버리를 위해 CoreDNS 사용하기

이 페이지는 CoreDNS 업그레이드 프로세스와 kube-dns 대신 CoreDNS를 설치하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

CoreDNS 소개

CoreDNS는 쿠버네티스 클러스터의 DNS 역할을 수행할 수 있는, 유연하고 확장 가능한 DNS 서버이다. 쿠버네티스와 동일하게, CoreDNS 프로젝트도 CNCF가 관리한다.

사용자는 기존 디플로이먼트인 kube-dns를 교체하거나, 클러스터를 배포하고 업그레이드하는 kubeadm과 같은 툴을 사용하여 클러스터 안의 kube-dns 대신 CoreDNS를 사용할 수 있다.

CoreDNS 설치

Kube-dns의 배포나 교체에 관한 매뉴얼은 CoreDNS GitHub 프로젝트에 있는 문서를 확인하자.

CoreDNS로 이관하기

Kubeadm을 사용해 기존 클러스터 업그레이드하기

쿠버네티스 버전 1.10 이상에서, kube-dns 를 사용하는 클러스터를 업그레이드하기 위하여 kubeadm 을 사용할 때 CoreDNS로 전환할 수도 있다. 이 경우, kubeadmkube-dns 컨피그맵(ConfigMap)을 기반으로 스텁 도메인(stub domain), 업스트림 네임 서버의 설정을 유지하며 CoreDNS 설정("Corefile")을 생성한다.

만약 kube-dns에서 CoreDNS로 이동하는 경우, 업그레이드 과정에서 기능 게이트의 CoreDNS 값을 true 로 설정해야 한다. 예를 들어, v1.11.0 로 업그레이드 하는 경우는 다음과 같다.

kubeadm upgrade apply v1.11.0 --feature-gates=CoreDNS=true

쿠버네티스 1.13 이상에서 기능 게이트의 CoreDNS 항목은 제거되었으며, CoreDNS가 기본적으로 사용된다.

1.11 미만 버전일 경우 업그레이드 과정에서 만들어진 파일이 Corefile을 덮어쓴다. 만약 컨피그맵을 사용자 정의한 경우, 기존의 컨피그맵을 저장해야 한다. 새 컨피그맵이 시작된 후에 변경 사항을 다시 적용해야 할 수도 있다.

만약 쿠버네티스 1.11 이상 버전에서 CoreDNS를 사용하는 경우, 업그레이드 과정에서, 기존의 Corefile이 유지된다.

쿠버네티스 버전 1.21에서, kubeadm 의 kube-dns 지원 기능이 삭제되었다.

CoreDNS 업그레이드하기

CoreDNS는 쿠버네티스 1.9 버전부터 사용할 수 있다. 쿠버네티스와 함께 제공되는 CoreDNS의 버전과 CoreDNS의 변경 사항은 여기에서 확인할 수 있다.

CoreDNS는 사용자 정의 이미지를 사용하거나 CoreDNS만 업그레이드 하려는 경우에 수동으로 업그레이드할 수 있다. 업그레이드를 원활하게 수행하는 데 유용한 가이드라인 및 연습을 참고하자.

CoreDNS 튜닝하기

리소스 활용이 중요한 경우, CoreDNS 구성을 조정하는 것이 유용할 수 있다. 더 자세한 내용은 CoreDNS 스케일링에 대한 설명서를 확인하자.

다음 내용

Corefile 을 수정하여 kube-dns 보다 더 많은 유스케이스를 지원하도록 CoreDNS를 구성할 수 있다. 더 자세한 내용은 CoreDNS 웹사이트을 확인하자.

4.2.11 - 중요한 애드온 파드 스케줄링 보장하기

API 서버, 스케줄러 및 컨트롤러 매니저와 같은 쿠버네티스 주요 컴포넌트들은 컨트롤 플레인 노드에서 동작한다. 반면, 애드온들은 일반 클러스터 노드에서 동작한다. 이러한 애드온들 중 일부(예: 메트릭 서버, DNS, UI)는 클러스터 전부가 정상적으로 동작하는 데 필수적일 수 있다. 만약, 필수 애드온이 축출되고(수동 축출, 혹은 업그레이드와 같은 동작으로 인한 의도하지 않은 축출) pending 상태가 된다면, 클러스터가 더 이상 제대로 동작하지 않을 수 있다. (사용률이 매우 높은 클러스터에서 해당 애드온이 축출되자마자 다른 대기중인 파드가 스케줄링되거나 다른 이유로 노드에서 사용할 수 있는 자원량이 줄어들어 pending 상태가 발생할 수 있다)

유의할 점은, 파드를 중요(critical)로 표시하는 것은 축출을 완전히 방지하기 위함이 아니다. 이것은 단지 파드가 영구적으로 사용할 수 없게 되는 것만을 방지하기 위함이다. 중요로 표시한 스태틱(static) 파드는 축출될 수 없다. 반면, 중요로 표시한 일반적인(non-static) 파드의 경우 항상 다시 스케줄링된다.

파드를 중요(critical)로 표시하기

파드를 중요로 표시하기 위해서는, 해당 파드에 대해 priorityClassName을 system-cluster-critical이나 system-node-critical로 설정한다. system-node-critical은 가장 높은 우선 순위를 가지며, 심지어 system-cluster-critical보다도 우선 순위가 높다.

4.2.12 - 쿠버네티스 API 활성화 혹은 비활성화하기

이 페이지는 클러스터 컨트롤 플레인의 특정한 API 버전을 활성화하거나 비활성화하는 방법에 대해 설명한다.

API 서버에 --runtime-config=api/<version> 커맨드 라인 인자를 사용함으로서 특정한 API 버전을 활성화하거나 비활성화할 수 있다. 이 인자에 대한 값으로는 콤마로 구분된 API 버전의 목록을 사용한다. 뒤쪽에 위치한 값은 앞쪽의 값보다 우선적으로 사용된다.

runtime-config 커맨드 라인 인자에는 다음의 두 개의 특수 키를 사용할 수도 있다.

  • api/all: 사용할 수 있는 모든 API를 선택한다.
  • api/legacy: 레거시 API만을 선택한다. 여기서 레거시 API란 명시적으로 사용이 중단된 모든 API를 가리킨다.

예를 들어서, v1을 제외한 모든 API 버전을 비활성화하기 위해서는 kube-apiserver--runtime-config=api/all=false,api/v1=true 인자를 사용한다.

다음 내용

kube-apiserver 컴포넌트에 대한 더 자세한 내용은 다음의 문서 를 참고한다.

4.2.13 - 쿠버네티스 API를 사용하여 클러스터에 접근하기

이 페이지는 쿠버네티스 API를 사용하여 클러스터에 접근하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

쿠버네티스 API에 접근

kubectl을 사용하여 처음으로 접근

쿠버네티스 API에 처음 접근하는 경우, 쿠버네티스 커맨드 라인 도구인 kubectl 을 사용한다.

클러스터에 접근하려면, 클러스터 위치를 알고 접근할 수 있는 자격 증명이 있어야 한다. 일반적으로, 시작하기 가이드를 통해 작업하거나, 다른 사람이 클러스터를 설정하고 자격 증명과 위치를 제공할 때 자동으로 설정된다.

다음의 명령으로 kubectl이 알고 있는 위치와 자격 증명을 확인한다.

kubectl config view

많은 예제는 kubectl 사용에 대한 소개를 제공한다. 전체 문서는 kubectl 매뉴얼에 있다.

REST API에 직접 접근

kubectl은 API 서버 찾기와 인증을 처리한다. curl 이나 wget 과 같은 http 클라이언트 또는 브라우저를 사용하여 REST API에 직접 접근하려는 경우, API 서버를 찾고 인증할 수 있는 여러 가지 방법이 있다.

  1. 프록시 모드에서 kubectl을 실행한다(권장). 이 방법은 저장된 API 서버 위치를 사용하고 자체 서명된 인증서를 사용하여 API 서버의 ID를 확인하므로 권장한다. 이 방법을 사용하면 중간자(man-in-the-middle, MITM) 공격이 불가능하다.
  2. 또는, 위치와 자격 증명을 http 클라이언트에 직접 제공할 수 있다. 이 방법은 프록시를 혼란스럽게 하는 클라이언트 코드와 동작한다. 중간자 공격으로부터 보호하려면, 브라우저로 루트 인증서를 가져와야 한다.

Go 또는 Python 클라이언트 라이브러리를 사용하면 프록시 모드에서 kubectl에 접근할 수 있다.

kubectl 프록시 사용

다음 명령은 kubectl을 리버스 프록시로 작동하는 모드에서 실행한다. API 서버 찾기와 인증을 처리한다.

다음과 같이 실행한다.

kubectl proxy --port=8080 &

자세한 내용은 kubectl 프록시를 참고한다.

그런 다음 curl, wget 또는 브라우저를 사용하여 API를 탐색할 수 있다.

curl http://localhost:8080/api/

출력은 다음과 비슷하다.

{
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

kubectl 프록시 없이 접근

다음과 같이 인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다.

grep/cut 방식을 사용한다.

# .KUBECONFIG에 여러 콘텍스트가 있을 수 있으므로, 가능한 모든 클러스터를 확인한다.
kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

# 위의 출력에서 상호 작용하려는 클러스터의 이름을 선택한다.
export CLUSTER_NAME="some_server_name"

# 클러스터 이름을 참조하는 API 서버를 가리킨다.
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")

# 토큰 값을 얻는다
TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 --decode)

# TOKEN으로 API 탐색
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

출력은 다음과 비슷하다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

jsonpath 방식을 사용한다.

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret $(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode )
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

위의 예는 --insecure 플래그를 사용한다. 이로 인해 MITM 공격이 발생할 수 있다. kubectl이 클러스터에 접근하면 저장된 루트 인증서와 클라이언트 인증서를 사용하여 서버에 접근한다. (~/.kube 디렉터리에 설치된다.) 클러스터 인증서는 일반적으로 자체 서명되므로, http 클라이언트가 루트 인증서를 사용하도록 하려면 특별한 구성이 필요할 수 있다.

일부 클러스터에서, API 서버는 인증이 필요하지 않다. 로컬 호스트에서 제공되거나, 방화벽으로 보호될 수 있다. 이에 대한 표준은 없다. 쿠버네티스 API에 대한 접근 제어는 클러스터 관리자로서 이를 구성하는 방법에 대해 설명한다. 이러한 접근 방식은 향후 고 가용성 지원과 충돌할 수 있다.

API에 프로그래밍 방식으로 접근

쿠버네티스는 공식적으로 Go, Python, Java, dotnet, JavascriptHaskell 용 클라이언트 라이브러리를 지원한다. 쿠버네티스 팀이 아닌 작성자가 제공하고 유지 관리하는 다른 클라이언트 라이브러리가 있다. 다른 언어에서 API에 접근하고 인증하는 방법에 대해서는 클라이언트 라이브러리를 참고한다.

Go 클라이언트

  • 라이브러리를 얻으려면, 다음 명령을 실행한다. go get k8s.io/client-go@kubernetes-<kubernetes-version-number> 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes/client-go/releases를 참고한다.
  • client-go 클라이언트 위에 애플리케이션을 작성한다.

Go 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

package main

import (
  "context"
  "fmt"
  "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
)

func main() {
  // kubeconfig에서 현재 콘텍스트를 사용한다
  // path-to-kubeconfig -- 예를 들어, /root/.kube/config
  config, _ := clientcmd.BuildConfigFromFlags("", "<path-to-kubeconfig>")
  // clientset을 생성한다
  clientset, _ := kubernetes.NewForConfig(config)
  // 파드를 나열하기 위해 API에 접근한다
  pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), v1.ListOptions{})
  fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
}

애플리케이션이 클러스터 내의 파드로 배치된 경우, 파드 내에서 API 접근을 참고한다.

Python 클라이언트

Python 클라이언트를 사용하려면, 다음 명령을 실행한다. pip install kubernetes 추가 설치 옵션은 Python Client Library 페이지를 참고한다.

Python 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

from kubernetes import client, config

config.load_kube_config()

v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
    print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))

Java 클라이언트

Java 클라이언트를 설치하려면, 다음을 실행한다.

# java 라이브러리를 클론한다
git clone --recursive https://github.com/kubernetes-client/java

# 프로젝트 아티팩트, POM 등을 설치한다
cd java
mvn install

어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/java/releases를 참고한다.

Java 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

package io.kubernetes.client.examples;

import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.apis.CoreV1Api;
import io.kubernetes.client.models.V1Pod;
import io.kubernetes.client.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;

/**
 * 쿠버네티스 클러스터 외부의 애플리케이션에서 Java API를 사용하는 방법에 대한 간단한 예
 *
 * <p>이것을 실행하는 가장 쉬운 방법: mvn exec:java
 * -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
 *
 */
public class KubeConfigFileClientExample {
  public static void main(String[] args) throws IOException, ApiException {

    // KubeConfig의 파일 경로
    String kubeConfigPath = "~/.kube/config";

    // 파일시스템에서 클러스터 외부 구성인 kubeconfig 로드
    ApiClient client =
        ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();

    // 전역 디폴트 api-client를 위에서 정의한 클러스터 내 클라이언트로 설정
    Configuration.setDefaultApiClient(client);

    // CoreV1Api는 전역 구성에서 디폴트 api-client를 로드
    CoreV1Api api = new CoreV1Api();

    // CoreV1Api 클라이언트를 호출한다
    V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
    System.out.println("Listing all pods: ");
    for (V1Pod item : list.getItems()) {
      System.out.println(item.getMetadata().getName());
    }
  }
}

dotnet 클라이언트

dotnet 클라이언트를 사용하려면, 다음 명령을 실행한다. dotnet add package KubernetesClient --version 1.6.1 추가 설치 옵션은 dotnet Client Library 페이지를 참고한다. 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/csharp/releases를 참고한다.

dotnet 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

using System;
using k8s;

namespace simple
{
    internal class PodList
    {
        private static void Main(string[] args)
        {
            var config = KubernetesClientConfiguration.BuildDefaultConfig();
            IKubernetes client = new Kubernetes(config);
            Console.WriteLine("Starting Request!");

            var list = client.ListNamespacedPod("default");
            foreach (var item in list.Items)
            {
                Console.WriteLine(item.Metadata.Name);
            }
            if (list.Items.Count == 0)
            {
                Console.WriteLine("Empty!");
            }
        }
    }
}

JavaScript 클라이언트

JavaScript 클라이언트를 설치하려면, 다음 명령을 실행한다. npm install @kubernetes/client-node 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/javascript/releases를 참고한다.

JavaScript 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

const k8s = require('@kubernetes/client-node');

const kc = new k8s.KubeConfig();
kc.loadFromDefault();

const k8sApi = kc.makeApiClient(k8s.CoreV1Api);

k8sApi.listNamespacedPod('default').then((res) => {
    console.log(res.body);
});

Haskell 클라이언트

어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/haskell/releases를 참고한다.

Haskell 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

exampleWithKubeConfig :: IO ()
exampleWithKubeConfig = do
    oidcCache <- atomically $ newTVar $ Map.fromList []
    (mgr, kcfg) <- mkKubeClientConfig oidcCache $ KubeConfigFile "/path/to/kubeconfig"
    dispatchMime
            mgr
            kcfg
            (CoreV1.listPodForAllNamespaces (Accept MimeJSON))
        >>= print

다음 내용

4.2.14 - 쿠버네티스 클러스터에서 sysctl 사용하기

FEATURE STATE: Kubernetes v1.21 [stable]

이 문서는 쿠버네티스 클러스터에서 sysctl 인터페이스를 사용하여 커널 파라미터를 어떻게 구성하고, 사용하는지를 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

일부 단계에서는 실행 중인 클러스터의 kubelet에서 커맨드 라인 옵션을 재구성할 필요가 있다.

모든 sysctl 파라미터 나열

리눅스에서 sysctl 인터페이스는 관리자들이 런타임에 커널 파라미터를 수정할 수 있도록 허용한다. 파라미터는 /proc/sys 가상 파일 시스템을 통해 이용할 수 있다. 파라미터는 다음과 같은 다양한 서브 시스템을 포함한다.

  • 커널 (공통 접두사: kernel.)
  • 네트워크 (공통 접두사: net.)
  • 가상 메모리 (공통 접두사: vm.)
  • MDADM (공통 접두사: dev.)
  • 더 많은 서브 시스템은 커널 문서에 설명되어 있다.

모든 파라미터 리스트를 가져오려면 다음 명령을 실행한다.

sudo sysctl -a

unsafe sysctl 활성화하기

sysctl은 safe sysctl과 unsafe sysctl로 구성되어 있다. safe sysctl은 적절한 네임스페이스 뿐만 아니라 동일한 노드의 파드 사이에 고립 되어야 한다. 즉, 하나의 파드에 safe sysctl을 설정한다는 것은 다음을 의미한다.

  • 노드의 다른 파드에 영향을 미치지 않아야 한다
  • 노드의 상태를 손상시키지 않아야 한다
  • CPU 또는 메모리 리소스가 파드의 리소스 제한에 벗어나는 것을 허용하지 않아야 한다

아직까지 대부분 네임스페이스된 sysctl은 safe sysctl로 고려되지 않았다.

다음 sysctl은 safe 명령을 지원한다.

  • kernel.shm_rmid_forced,
  • net.ipv4.ip_local_port_range,
  • net.ipv4.tcp_syncookies,
  • net.ipv4.ping_group_range (Kubernetes 1.18 이후).

kubelet이 더 고립된 방법을 지원하면 추후 쿠버네티스 버전에서 확장될 것이다.

모든 safe sysctl은 기본적으로 활성화된다.

모든 unsafe sysctl은 기본적으로 비활성화되고, 노드별 기본 클러스터 관리자에 의해 수동으로 메뉴얼로 허용되어야 한다. unsafe sysctl이 비활성화된 파드는 스케줄링되지만, 시작에 실패한다.

위의 경고를 염두에 두고 클러스터 관리자는 고성능 또는 실시간 애플리케이션 조정과 같은 매우 특수한 상황에 대해 특정 unsafe sysctl을 허용할 수 있다. unsafe sysctl은 kubelet 플래그를 사용하여 노드별로 활성화된다. 예를 들면, 다음과 같다.

kubelet --allowed-unsafe-sysctls \
  'kernel.msg*,net.core.somaxconn' ...

Minikube의 경우, extra-config 플래그를 통해 이 작업을 수행할 수 있다.

minikube start --extra-config="kubelet.allowed-unsafe-sysctls=kernel.msg*,net.core.somaxconn"...

네임스페이스 sysctl만 이 방법을 사용할 수 있다.

파드에 대한 sysctl 설정

수많은 sysctl은 최근 리눅스 커널에서 네임스페이스 되어 있다. 이는 노드의 각 파드에 대해 개별적으로 설정할 수 있다는 것이다. 쿠버네티스의 파드 securityContext를 통해 네임스페이스 sysctl만 구성할 수 있다.

다음 sysctls는 네임스페이스로 알려져 있다. 이 목록은 이후 버전의 Linux 커널에서 변경될 수 있다.

  • kernel.shm*,
  • kernel.msg*,
  • kernel.sem,
  • fs.mqueue.*,
  • net.* 아래의 파라미터는 컨테이너 네트워킹 네임스페이스에서 설정할 수 있다. 그러나 예외가 존재한다. (예, net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_expect_max는 컨테이너 네트워킹 네임스페이스에서 설정되지만, 네임스페이스가 없다.)

네임스페이스가 없는 sysctl은 node-level sysctl이라고 부른다. 이를 설정해야 한다면, 각 노드의 OS에서 수동으로 구성하거나 특권있는 컨테이너의 데몬셋을 사용하여야 한다.

네임스페이스 sysctl을 구성하기 위해서 파드 securityContext를 사용한다. securityContext는 동일한 파드의 모든 컨테이너에 적용된다.

이 예시는 safe sysctl kernel.shm_rmid_forced와 두 개의 unsafe sysctl인 net.core.somaxconnkernel.msgmax 를 설정하기 위해 파드 securityContext를 사용한다.

apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

특별한 sysctl 설정이 있는 노드를 클러스터 내에서 _tainted_로 간주하고 sysctl 설정이 필요한 노드에만 파드를 예약하는 것이 좋다. 이를 구현하려면 쿠버네티스 테인트(taint)와 톨러레이션(toleration) 기능 을 사용하는 것이 좋다.

unsafe sysctl을 명시적으로 활성화하지 않은 노드에서 unsafe sysctl을 사용하는 파드가 시작되지 않는다. node-level sysctl과 마찬가지로 테인트와 톨러레이션 특징 또는 노드 테인트를 사용하여 해당 파드를 오른쪽 노드에 스케줄하는 것을 추천한다.

파드시큐리티폴리시(PodSecurityPolicy)

FEATURE STATE: Kubernetes v1.21 [deprecated]

또한 파드시큐리티폴리시의 forbiddenSysctls 및/또는 allowedUnsafeSysctls 필드에 sysctl 또는 sysctl 패턴 목록을 지정하여 파드에서 설정할 수 있는 sysctl를 제어할 수 있다. sysctl 패턴은 kernel.*과 같은 * 문자로 끝난다. * 문자 자체는 모든 sysctl와 일치한다.

기본적으로 모든 safe sysctl은 허용된다.

forbiddenSysctlsallowedUnsafeSysctls는 모두 단순한 sysctl 이름 또는 sysctl 패턴 목록이다(*로 끝남). * 문자는 모든 sysctl과 일치한다.

forbiddenSysctls 필드에는 특정 sysctl이 제외된다. 목록에서 safe sysctl과 unsafe sysctl의 조합을 금지할 수 있다. sysctl 설정을 금지하기 위해서는 *를 사용한다.

allowedUnsafeSysctls 필드에 unsafe sysctl을 지정하고 forbiddenSysctls 필드가 존재하지 않는 경우, 파드시큐리티폴리시를 사용하여 sysctl을 파드에서 사용할 수 있다. 파드시큐리티폴리시의 모든 unsafe sysctl을 설정하려면 *를 사용한다.

이 두 필드를 겹치도록 구성하지 않는다. 이는 지정된 sysctl이 허용 및 금지됨을 의미한다.

이 예에서는 kernel.msg 접두사가 붙은 unsafe sysctl을 설정할 수 있으며, kernel.shm_rmid_forced sysctl의 설정을 허용하지 않는다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: sysctl-psp
spec:
  allowedUnsafeSysctls:
  - kernel.msg*
  forbiddenSysctls:
  - kernel.shm_rmid_forced
 ...

4.2.15 - 클러스터에서 실행되는 서비스에 접근

이 페이지는 쿠버네티스 클러스터에서 실행되는 서비스에 연결하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터에서 실행되는 서비스에 접근

쿠버네티스에서, 노드, 파드서비스는 모두 고유한 IP를 가진다. 당신의 데스크탑 PC와 같은 클러스터 외부 장비에서는 클러스터 상의 노드 IP, 파드 IP, 서비스 IP로 라우팅되지 않아서 접근할 수 없을 것이다.

연결하는 방법

클러스터 외부에서 노드, 파드 및 서비스에 접속하기 위한 몇 가지 옵션이 있다.

  • 퍼블릭 IP를 통해 서비스에 접근한다.
    • 클러스터 외부에서 접근할 수 있도록 NodePort 또는 LoadBalancer 타입의 서비스를 사용한다. 서비스kubectl expose 문서를 참고한다.
    • 클러스터 환경에 따라, 서비스는 회사 네트워크에만 노출되기도 하며, 인터넷에 노출되는 경우도 있다. 이 경우 노출되는 서비스의 보안 여부를 고려해야 한다. 해당 서비스는 자체적으로 인증을 수행하는가?
    • 파드는 서비스 뒤에 위치시킨다. 디버깅과 같은 목적으로 레플리카 집합에서 특정 파드에 접근하려면, 파드에 고유한 레이블을 배치하고 이 레이블을 선택하는 새 서비스를 생성한다.
    • 대부분의 경우, 애플리케이션 개발자가 nodeIP를 통해 노드에 직접 접근할 필요는 없다.
  • 프록시 작업(Proxy Verb)을 사용하여 서비스, 노드 또는 파드에 접근한다.
    • 원격 서비스에 접근하기 전에 apiserver 인증과 권한 부여를 수행한다. 서비스가 인터넷에 노출되거나, 노드 IP의 포트에 접근하거나, 디버깅하기에 충분히 안전하지 않은 경우 사용한다.
    • 프록시는 일부 웹 애플리케이션에 문제를 일으킬 수 있다.
    • HTTP/HTTPS에서만 작동한다.
    • 여기에 설명되어 있다.
  • 클러스터의 노드 또는 파드에서 접근한다.
    • 파드를 실행한 다음, kubectl exec를 사용하여 셸에 연결한다. 해당 셸에서 다른 노드, 파드 및 서비스에 연결한다.
    • 일부 클러스터는 클러스터의 노드로 ssh를 통해 접근하는 것을 허용한다. 거기에서 클러스터 서비스에 접근할 수 있다. 이것은 비표준 방법이며, 일부 클러스터에서는 작동하지만 다른 클러스터에서는 작동하지 않는다. 브라우저 및 기타 도구가 설치되거나 설치되지 않을 수 있다. 클러스터 DNS가 작동하지 않을 수도 있다.

빌트인 서비스 검색

일반적으로 kube-system에 의해 클러스터에 실행되는 몇 가지 서비스가 있다. kubectl cluster-info 커맨드로 이 서비스의 리스트를 볼 수 있다.

kubectl cluster-info

출력은 다음과 비슷하다.

Kubernetes master is running at https://104.197.5.247
elasticsearch-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy

각 서비스에 접근하기 위한 프록시-작업 URL이 표시된다. 예를 들어, 이 클러스터에는 https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/ 로 접근할 수 있는 (Elasticsearch를 사용한) 클러스터 수준 로깅이 활성화되어 있다. 적합한 자격 증명이 전달되는 경우나 kubectl proxy를 통해 도달할 수 있다. 예를 들어 다음의 URL에서 확인할 수 있다. http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/.

apiserver 프록시 URL 수동 구성

위에서 언급한 것처럼, kubectl cluster-info 명령을 사용하여 서비스의 프록시 URL을 검색한다. 서비스 엔드포인트, 접미사 및 매개 변수를 포함하는 프록시 URL을 작성하려면, 서비스의 프록시 URL에 추가하면 된다. http://kubernetes_master_address/api/v1/namespaces/namespace_name/services/[https:]service_name[:port_name]/proxy

포트에 대한 이름을 지정하지 않은 경우, URL에 port_name 을 지정할 필요가 없다.

예제
  • Elasticsearch 서비스 엔드포인트 _search?q=user:kimchy 에 접근하려면, 다음을 사용한다.

    http://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy
    
  • Elasticsearch 클러스터 상태 정보 _cluster/health?pretty=true 에 접근하려면, 다음을 사용한다.

    https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true
    

    상태 정보는 다음과 비슷하다.

    {
      "cluster_name" : "kubernetes_logging",
      "status" : "yellow",
      "timed_out" : false,
      "number_of_nodes" : 1,
      "number_of_data_nodes" : 1,
      "active_primary_shards" : 5,
      "active_shards" : 5,
      "relocating_shards" : 0,
      "initializing_shards" : 0,
      "unassigned_shards" : 5
    }
    
  • https Elasticsearch 서비스 상태 정보 _cluster/health?pretty=true 에 접근하려면, 다음을 사용한다.

    https://104.197.5.247/api/v1/namespaces/kube-system/services/https:elasticsearch-logging/proxy/_cluster/health?pretty=true
    

웹 브라우저를 사용하여 클러스터에서 실행되는 서비스에 접근

브라우저의 주소 표시줄에 apiserver 프록시 URL을 넣을 수 있다. 그러나,

  • 웹 브라우저는 일반적으로 토큰을 전달할 수 없으므로, 기본 (비밀번호) 인증을 사용해야 할 수도 있다. Apiserver는 기본 인증을 수락하도록 구성할 수 있지만, 클러스터는 기본 인증을 수락하도록 구성되지 않을 수 있다.
  • 일부 웹 앱, 특히 프록시 경로 접두사를 인식하지 못하는 방식으로 URL을 구성하는 클라이언트 측 자바스크립트가 있는 웹 앱이 작동하지 않을 수 있다.

4.2.16 - 토폴로지 인지 힌트 활성화하기

FEATURE STATE: Kubernetes v1.21 [alpha]

토폴로지 인지 힌트엔드포인트슬라이스(EndpointSlices)에 포함되어 있는 토폴로지 정보를 이용해 토폴로지 인지 라우팅을 가능하게 한다. 이 방법은 트래픽을 해당 트래픽이 시작된 곳과 최대한 근접하도록 라우팅하는데, 이를 통해 비용을 줄이거나 네트워크 성능을 향상시킬 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.21. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

토폴로지 인지 힌트를 활성화하기 위해서는 다음의 필수 구성 요소가 필요하다.

  • kube-proxy가 iptables 모드 혹은 IPVS 모드로 동작하도록 설정
  • 엔드포인트슬라이스가 비활성화되지 않았는지 확인

토폴로지 인지 힌트 활성화하기

서비스 토폴로지 힌트를 활성화하기 위해서는 kube-apiserver, kube-controller-manager, kube-proxy에 대해 TopologyAwareHints 기능 게이트를 활성화한다.

--feature-gates="TopologyAwareHints=true"

다음 내용

4.2.17 - 퍼시스턴트볼륨 반환 정책 변경하기

이 페이지는 쿠버네티스 퍼시트턴트볼륨(PersistentVolume)의 반환 정책을 변경하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

왜 퍼시스턴트볼륨 반환 정책을 변경하는가?

퍼시스턴트볼륨은 "Retain(보존)", "Recycle(재활용)", "Delete(삭제)" 를 포함한 다양한 반환 정책을 갖는다. 동적으로 프로비저닝 된 퍼시스턴트볼륨의 경우 기본 반환 정책은 "Delete" 이다. 이는 사용자가 해당 PersistentVolumeClaim 을 삭제하면, 동적으로 프로비저닝 된 볼륨이 자동적으로 삭제됨을 의미한다. 볼륨에 중요한 데이터가 포함된 경우, 이러한 자동 삭제는 부적절 할 수 있다. 이 경우에는, "Retain" 정책을 사용하는 것이 더 적합하다. "Retain" 정책에서, 사용자가 퍼시스턴트볼륨클레임을 삭제할 경우 해당하는 퍼시스턴트볼륨은 삭제되지 않는다. 대신, Released 단계로 이동되어, 모든 데이터를 수동으로 복구할 수 있다.

퍼시스턴트볼륨 반환 정책 변경하기

  1. 사용자의 클러스터에서 퍼시스턴트볼륨을 조회한다.

    kubectl get pv
    

    결과는 아래와 같다.

     NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
     pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     10s
     pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     6s
     pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim3    manual                     3s
    

    이 목록은 동적으로 프로비저닝 된 볼륨을 쉽게 식별할 수 있도록 각 볼륨에 바인딩 되어 있는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 이름도 포함한다.

  2. 사용자의 퍼시스턴트볼륨 중 하나를 선택한 후에 반환 정책을 변경한다.

    kubectl patch pv <your-pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
    

    <your-pv-name> 는 사용자가 선택한 퍼시스턴트볼륨의 이름이다.

  3. 선택한 PersistentVolume이 올바른 정책을 갖는지 확인한다.

    kubectl get pv
    

    결과는 아래와 같다.

     NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
     pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     40s
     pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     36s
     pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Retain          Bound     default/claim3    manual                     33s
    

    위 결과에서, default/claim3 클레임과 바인딩 되어 있는 볼륨이 Retain 반환 정책을 갖는 것을 볼 수 있다. 사용자가 default/claim3 클레임을 삭제할 경우, 볼륨은 자동으로 삭제 되지 않는다.

다음 내용

레퍼런스

    • Pay attention to the 퍼시스턴트볼륨의 .spec.persistentVolumeReclaimPolicy 필드에 주의한다.

4.3 - 파드와 컨테이너 설정

파드와 컨테이너에 대한 공통 구성 태스크들을 수행한다.

4.3.1 - 컨테이너 및 파드 메모리 리소스 할당

이 페이지는 메모리 요청량 과 메모리 상한 을 컨테이너에 어떻게 지정하는지 보여준다. 컨테이너는 요청량 만큼의 메모리 확보가 보장되나 상한보다 더 많은 메모리는 사용할 수 없다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터의 각 노드에 최소 300 MiB 메모리가 있어야 한다.

이 페이지의 몇 가지 단계를 수행하기 위해서는 클러스터 내 metrics-server 서비스 실행이 필요하다. 이미 실행 중인 metrics-server가 있다면 다음 단계를 건너뛸 수 있다.

Minikube를 사용 중이라면, 다음 명령어를 실행해 metric-server를 활성화할 수 있다.

minikube addons enable metrics-server

metric-server가 실행 중인지 확인하거나 다른 제공자의 리소스 메트릭 API (metrics.k8s.io)를 확인하기 위해 다음의 명령어를 실행한다.

kubectl get apiservices

리소스 메트릭 API를 사용할 수 있다면 출력에 metrics.k8s.io에 대한 참조가 포함되어 있다.

NAME      
v1beta1.metrics.k8s.io

네임스페이스 생성

이 예제에서 생성할 자원과 클러스터 내 나머지를 분리하기 위해 네임스페이스를 생성한다.

kubectl create namespace mem-example

메모리 요청량 및 상한을 지정

컨테이너에 메모리 요청량을 지정하기 위해서는 컨테이너의 리소스 매니페스트에 resources:requests 필드를 포함한다. 리소스 상한을 지정하기 위해서는 resources:limits 필드를 포함한다.

이 예제에서 하나의 컨테이너를 가진 파드를 생성한다. 생성된 컨테이너는 100 MiB 메모리 요청량과 200 MiB 메모리 상한을 갖는다. 이 것이 파드 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

구성 파일 내 args 섹션은 컨테이너가 시작될 때 아규먼트를 제공한다. "--vm-bytes", "150M" 아규먼트는 컨테이너가 150 MiB 할당을 시도 하도록 한다.

파드 생성:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

파드 컨테이너가 실행 중인지 확인:

kubectl get pod memory-demo --namespace=mem-example

파드에 대한 자세한 정보 보기:

kubectl get pod memory-demo --output=yaml --namespace=mem-example

출력은 파드 내 하나의 컨테이너에 100MiB 메모리 요청량과 200 MiB 메모리 상한이 있는 것을 보여준다.

...
resources:
  limits:
    memory: 200Mi
  requests:
    memory: 100Mi
...

kubectl top을 실행하여 파드 메트릭 가져오기:

kubectl top pod memory-demo --namespace=mem-example

출력은 파드가 약 150 MiB 해당하는 약 162,900,000 바이트 메모리를 사용하는 것을 보여준다. 이는 파드의 100 MiB 요청 보다 많으나 파드의 200 MiB 상한보다는 적다.

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

파드 삭제:

kubectl delete pod memory-demo --namespace=mem-example

컨테이너의 메모리 상한을 초과

노드 내 메모리가 충분하다면 컨테이너는 지정한 요청량보다 많은 메모리를 사용 할 수 있다. 그러나 컨테이너는 지정한 메모리 상한보다 많은 메모리를 사용할 수 없다. 만약 컨테이너가 지정한 메모리 상한보다 많은 메모리를 할당하면 해당 컨테이너는 종료 대상 후보가 된다. 만약 컨테이너가 지속적으로 지정된 상한보다 많은 메모리를 사용한다면, 해당 컨테이너는 종료된다. 만약 종료된 컨테이너가 재실행 가능하다면 다른 런타임 실패와 마찬가지로 kubelet에 의해 재실행된다.

이 예제에서는 상한보다 많은 메모리를 할당하려는 파드를 생성한다. 이 것은 50 MiB 메모리 요청량과 100 MiB 메모리 상한을 갖는 하나의 컨테이너를 갖는 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

구성 파일의 args 섹션에서 컨테이너가 100 MiB 상한을 훨씬 초과하는 250 MiB의 메모리를 할당하려는 것을 볼 수 있다.

파드 생성:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

파드에 대한 자세한 정보 보기:

kubectl get pod memory-demo-2 --namespace=mem-example

이 시점에 컨테이너가 실행되거나 종료되었을 수 있다. 컨테이너가 종료될 때까지 이전의 명령을 반복한다.

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

컨테이너 상태의 상세 상태 보기:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

컨테이너가 메모리 부족 (OOM) 으로 종료되었음이 출력된다.

lastState:
   terminated:
     containerID: docker://65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

이 예제에서 컨테이너는 재실행 가능하여 kubelet에 의해 재실행된다. 컨테이너가 종료되었다 재실행되는 것을 보기 위해 다음 명령을 몇 번 반복한다.

kubectl get pod memory-demo-2 --namespace=mem-example

출력은 컨테이너의 종료, 재실행, 재종료, 재실행 등을 보여준다.

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

파드 내역에 대한 상세 정보 보기:

kubectl describe pod memory-demo-2 --namespace=mem-example

컨테이너가 반복적으로 시작하고 실패 하는 출력을 보여준다.

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

클러스터 노드에 대한 자세한 정보 보기:

kubectl describe nodes

출력에는 컨테이너가 메모리 부족으로 종료된 기록이 포함된다.

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

파드 삭제:

kubectl delete pod memory-demo-2 --namespace=mem-example

노드에 비해 너무 큰 메모리 요청량의 지정

메모리 요청량과 상한은 컨테이너와 관련있지만, 파드가 가지는 메모리 요청량과 상한으로 이해하면 유용하다. 파드의 메모리 요청량은 파드 내 모든 컨테이너의 메모리 요청량의 합이다. 마찬가지로 파드의 메모리 상한은 파드 내 모든 컨테이너의 메모리 상한의 합이다.

파드는 요청량을 기반하여 스케줄링된다. 노드에 파드의 메모리 요청량을 충족하기에 충분한 메모리가 있는 경우에만 파드가 노드에서 스케줄링된다.

이 예제에서는 메모리 요청량이 너무 커 클러스터 내 모든 노드의 용량을 초과하는 파드를 생성한다. 다음은 클러스터 내 모든 노드의 용량을 초과할 수 있는 1000 GiB 메모리 요청을 포함하는 컨테이너를 갖는 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "1000Gi"
      requests:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

파드 생성:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

파드 상태 보기:

kubectl get pod memory-demo-3 --namespace=mem-example

파드 상태가 PENDING 상태임이 출력된다. 즉 파드는 어떤 노드에서도 실행되도록 스케줄 되지 않고 PENDING가 계속 지속된다.

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

이벤트를 포함한 파드 상세 정보 보기:

kubectl describe pod memory-demo-3 --namespace=mem-example

출력은 노드 내 메모리가 부족하여 파드가 스케줄링될 수 없음을 보여준다.

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

메모리 단위

메모리 리소스는 byte 단위로 측정된다. 다음 접미사 중 하나로 정수 또는 고정 소수점으로 메모리를 표시할 수 있다. E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki. 예를 들어 다음은 거의 유사한 값을 나타낸다.

128974848, 129e6, 129M , 123Mi

파드 삭제:

kubectl delete pod memory-demo-3 --namespace=mem-example

메모리 상한을 지정하지 않으면

컨테이너에 메모리 상한을 지정하지 않으면 다음 중 하나가 적용된다.

  • 컨테이너가 사용할 수 있는 메모리 상한은 없다. 컨테이너가 실행 중인 노드에서 사용 가능한 모든 메모리를 사용하여 OOM Killer가 실행될 수 있다. 또한 메모리 부족으로 인한 종료 시 메모리 상한이 없는 컨테이너가 종료될 가능성이 크다.

  • 기본 메모리 상한을 갖는 네임스페이스 내에서 실행중인 컨테이너는 자동으로 기본 메모리 상한이 할당된다. 클러스터 관리자들은 LimitRange를 사용해 메모리 상한의 기본 값을 지정 가능하다.

메모리 요청량과 상한 동기부여

클러스터에서 실행되는 컨테이너에 메모리 요청량과 상한을 구성하여 클러스터 내 노드들의 메모리 리소스를 효율적으로 사용할 수 있게 할 수 있다. 파드의 메모리 요청량을 적게 유지하여 파드가 높은 확률로 스케줄링 될 수 있도록 한다. 메모리 상한이 메모리 요청량보다 크면 다음 두 가지가 수행된다.

  • 가용한 메모리가 있는 경우 파드가 이를 사용할 수 있는 버스트(burst) 활동을 할 수 있다.
  • 파드가 버스트 중 사용 가능한 메모리 양이 적절히 제한된다.

정리

네임스페이스를 지운다. 이 작업을 통해 네임스페이스 내 생성했던 모든 파드들은 삭제된다.

kubectl delete namespace mem-example

다음 내용

앱 개발자들을 위한

클러스터 관리자들을 위한

4.3.2 - 윈도우 파드 및 컨테이너에서 RunAsUserName 구성

FEATURE STATE: Kubernetes v1.18 [stable]

이 페이지에서는 윈도우 노드에서 실행될 파드 및 컨테이너에 runAsUserName 설정을 사용하는 방법을 소개한다. 이는 리눅스 관련 runAsUser 설정과 거의 동일하여, 컨테이너의 기본값과 다른 username으로 애플리케이션을 실행할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 있어야 하며 클러스터와 통신하도록 kubectl 명령줄 도구를 구성해야 한다. 클러스터에는 윈도우 워커 노드가 있어야 하고, 해당 노드에서 윈도우 워크로드를 실행하는 컨테이너의 파드가 스케쥴 된다.

파드의 username 설정

파드의 컨테이너 프로세스를 실행할 username을 지정하려면 파드 명세에 securityContext 필드 (PodSecurityContext) 를 포함시키고, 그 안에 runAsUserName 필드를 포함하는 windowsOptions (WindowsSecurityContextOptions) 필드를 추가한다.

파드에 지정하는 윈도우 보안 컨텍스트 옵션은 파드의 모든 컨테이너 및 초기화 컨테이너에 적용된다.

다음은 runAsUserName 필드가 설정된 윈도우 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-pod-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
  nodeSelector:
    kubernetes.io/os: windows

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/windows/run-as-username-pod.yaml

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pod run-as-username-pod-demo

실행 중인 컨테이너의 셸에 접근한다.

kubectl exec -it run-as-username-pod-demo -- powershell

셸이 올바른 username인 사용자로 실행 중인지 확인한다.

echo $env:USERNAME

결과는 다음과 같다.

ContainerUser

컨테이너의 username 설정

컨테이너의 프로세스를 실행할 username을 지정하려면, 컨테이너 매니페스트에 securityContext 필드 (SecurityContext) 를 포함시키고 그 안에 runAsUserName 필드를 포함하는 windowsOptions (WindowsSecurityContextOptions) 필드를 추가한다.

컨테이너에 지정하는 윈도우 보안 컨텍스트 옵션은 해당 개별 컨테이너에만 적용되며 파드 수준에서 지정한 설정을 재정의한다.

다음은 한 개의 컨테이너에 runAsUserName 필드가 파드 수준 및 컨테이너 수준에서 설정되는 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-container-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
    securityContext:
        windowsOptions:
            runAsUserName: "ContainerAdministrator"
  nodeSelector:
    kubernetes.io/os: windows

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/windows/run-as-username-container.yaml

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pod run-as-username-container-demo

실행 중인 컨테이너의 셸에 접근한다.

kubectl exec -it run-as-username-container-demo -- powershell

셸이 사용자에게 올바른 username(컨테이너 수준에서 설정된 사용자)을 실행 중인지 확인한다.

echo $env:USERNAME

결과는 다음과 같다.

ContainerAdministrator

윈도우 username 제약사항

이 기능을 사용하려면 runAsUserName 필드에 설정된 값이 유효한 username이어야 한다. 형식은 DOMAIN\USER 여야하고, 여기서 DOMAIN\은 선택 사항이다. 윈도우 username은 대소문자를 구분하지 않는다. 또한 DOMAINUSER 와 관련된 몇 가지 제약사항이 있다.

  • runAsUserName 필드는 비워 둘 수 없으며 제어 문자를 포함할 수 없다. (ASCII 값: 0x00-0x1F, 0x7F)
  • DOMAIN은 NetBios 이름 또는 DNS 이름이어야 하며 각각 고유한 제한이 있다.
    • NetBios 이름: 최대 15 자, .(마침표)으로 시작할 수 없으며 다음 문자를 포함할 수 없다. \ / : * ? " < > |
    • DNS 이름: 최대 255 자로 영숫자, 마침표(.), 대시(-)로만 구성되며, 마침표 또는 대시로 시작하거나 끝날 수 없다.
  • USER는 최대 20자이며, 오직 마침표나 공백들로는 구성할 수 없고, 다음 문자는 포함할 수 없다. " / \ [ ] : ; | = , + * ? < > @.

runAsUserName 필드에 허용되는 값의 예 : ContainerAdministrator,ContainerUser, NT AUTHORITY\NETWORK SERVICE, NT AUTHORITY\LOCAL SERVICE.

이러한 제약사항에 대한 자세한 내용은 여기여기를 확인한다.

다음 내용

4.3.3 - 윈도우 파드와 컨테이너용 GMSA 구성

FEATURE STATE: Kubernetes v1.18 [stable]

이 페이지는 윈도우 노드에서 실행되는 파드와 컨테이너용으로 그룹 관리 서비스 어카운트(Group Managed Service Accounts, GMSA)를 구성하는 방법을 소개한다. 그룹 관리 서비스 어카운트는 자동 암호 관리, 단순화된 서비스 사용자 이름(service principal name, SPN) 관리, 여러 서버에 걸쳐 다른 관리자에게 관리를 위임하는 기능을 제공하는 특정한 유형의 액티브 디렉터리(Active Directory) 계정이다.

쿠버네티스에서 GMSA 자격 증명 사양은 쿠버네티스 클러스터 전체 범위에서 사용자 정의 리소스(Custom Resources)로 구성된다. 윈도우 파드 및 파드 내의 개별 컨테이너들은 다른 윈도우 서비스와 상호 작용할 때 도메인 기반 기능(예: Kerberos 인증)에 GMSA를 사용하도록 구성할 수 있다. v1.16부터 도커 런타임은 윈도우 워크로드용 GMSA를 지원한다.

시작하기 전에

쿠버네티스 클러스터가 있어야 하며 클러스터와 통신하도록 kubectl 커맨드라인 툴을 구성해야 한다. 클러스터에는 윈도우 워커 노드가 있어야 한다. 이 섹션에서는 각 클러스터에 대해 한 번씩 필요한 일련의 초기 단계를 다룬다.

GMSACredentialSpec CRD 설치

GMSA 자격 증명 사양 리소스에 대한 커스텀리소스데피니션(CustomResourceDefinition, CRD)을 클러스터에서 구성하여 사용자 정의 리소스 유형 GMSACredentialSpec을 정의해야 한다. GMSA CRD YAML을 다운로드하고 gmsa-crd.yaml로 저장한다. 다음, kubectl apply -f gmsa-crd.yaml 로 CRD를 설치한다.

GMSA 사용자를 검증하기 위해 웹훅 설치

쿠버네티스 클러스터에서 두 개의 웹훅을 구성하여 파드 또는 컨테이너 수준에서 GMSA 자격 증명 사양 참조를 채우고 검증한다.

  1. 변형(mutating) 웹훅은 (파드 사양의 이름별로) GMSA에 대한 참조를 파드 사양 내 JSON 형식의 전체 자격 증명 사양으로 확장한다.

  2. 검증(validating) 웹훅은 GMSA에 대한 모든 참조가 파드 서비스 어카운트에서 사용하도록 승인되었는지 확인한다.

위의 웹훅 및 관련 오브젝트를 설치하려면 다음 단계가 필요하다.

  1. 인증서 키 쌍 생성 (웹훅 컨테이너가 클러스터와 통신할 수 있도록 하는데 사용됨)

  2. 위의 인증서로 시크릿을 설치

  3. 핵심 웹훅 로직에 대한 디플로이먼트(deployment)를 생성

  4. 디플로이먼트를 참조하여 검증 및 변경 웹훅 구성을 생성

스크립트를 사용하여 GMSA 웹훅과 위에서 언급한 관련 오브젝트를 배포 및 구성할 수 있다. 스크립트는 --dry-run=server 옵션으로 실행되어 클러스터에 대한 변경 사항을 검토할 수 있다.

스크립트에서 사용하는 YAML 템플릿을 사용하여 웹훅 및 (파라미터를 적절히 대체하여) 관련 오브젝트를 수동으로 배포할 수도 있다.

액티브 디렉터리에서 GMSA 및 윈도우 노드 구성

쿠버네티스의 파드가 GMSA를 사용하도록 구성되기 전에 윈도우 GMSA 문서에 설명된 대로 액티브 디렉터리에서 원하는 GMSA를 프로비저닝해야 한다. 윈도우 GMSA 문서에 설명된 대로 원하는 GMSA와 연결된 시크릿 자격 증명에 접근하려면 (쿠버네티스 클러스터의 일부인) 윈도우 워커 노드를 액티브 디렉터리에서 구성해야 한다.

GMSA 자격 증명 사양 리소스 생성

(앞에서 설명한 대로) GMSACredentialSpec CRD를 설치하면 GMSA 자격 증명 사양이 포함된 사용자 정의 리소스를 구성할 수 있다. GMSA 자격 증명 사양에는 시크릿 또는 민감한 데이터가 포함되어 있지 않다. 이것은 컨테이너 런타임이 원하는 윈도우 컨테이너 GMSA를 설명하는 데 사용할 수 있는 정보이다. GMSA 자격 증명 사양은 PowerShell 스크립트 유틸리티를 사용하여 YAML 형식으로 생성할 수 있다.

다음은 JSON 형식으로 GMSA 자격 증명 사양 YAML을 수동으로 생성한 다음 변환하는 단계이다.

  1. CredentialSpec 모듈 가져오기(import): ipmo CredentialSpec.psm1

  2. New-CredentialSpec을 사용하여 JSON 형식의 자격 증명 사양을 만든다. WebApp1이라는 GMSA 자격 증명 사양을 만들려면 New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)를 호출한다.

  3. Get-CredentialSpec을 사용하여 JSON 파일의 경로를 표시한다.

  4. credspec 파일을 JSON에서 YAML 형식으로 변환하고 필요한 헤더 필드 apiVersion, kind, metadata, credspec을 적용하여 쿠버네티스에서 구성할 수 있는 GMSACredentialSpec 사용자 정의 리소스로 만든다.

다음 YAML 구성은 gmsa-WebApp1이라는 GMSA 자격 증명 사양을 설명한다.

apiVersion: windows.k8s.io/v1alpha1
kind: GMSACredentialSpec
metadata:
  name: gmsa-WebApp1  #임의의 이름이지만 참조로 사용된다.
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: WebApp1   #GMSA 계정의 사용자 이름
      Scope: CONTOSO  #NETBIOS 도메인 명
    - Name: WebApp1   #GMSA 계정의 사용자 이름
      Scope: contoso.com #DNS 도메인 명
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: contoso.com  #DNS 도메인 명
    DnsTreeName: contoso.com #DNS 도메인 명 루트
    Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a  #GUID
    MachineAccountName: WebApp1 #GMSA 계정의 사용자 이름
    NetBiosName: CONTOSO  #NETBIOS 도메인 명
    Sid: S-1-5-21-2126449477-2524075714-3094792973 #SID of GMSA

위의 자격 증명 사양 리소스는 gmsa-Webapp1-credspec.yaml로 저장되고 kubectl apply -f gmsa-Webapp1-credspec.yml을 사용하여 클러스터에 적용될 수 있다.

특정 GMSA 자격 증명 사양에서 RBAC를 활성화하도록 cluster role 구성

각 GMSA 자격 증명 사양 리소스에 대해 cluster role을 정의해야 한다. 이것은 일반적으로 서비스 어카운트인 주체에 의해 특정 GMSA 리소스에 대한 use 동사를 승인한다. 다음 예는 위에서 gmsa-WebApp1 자격 증명 사양의 사용을 승인하는 클러스터 롤(cluster role)을 보여준다. 파일을 gmsa-webapp1-role.yaml로 저장하고 kubectl apply -f gmsa-webapp1-role.yaml을 사용하여 적용한다.

#credspec을 읽을 Role 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
  resources: ["gmsacredentialspecs"]
  verbs: ["use"]
  resourceNames: ["gmsa-WebApp1"]

특정 GMSA credspecs를 사용하도록 서비스 어카운트에 롤 할당

(파드가 사용하게 되는) 서비스 어카운트는 위에서 생성한 클러스터 롤에 바인딩되어야 한다. 이렇게 하면 서비스 어카운트가 원하는 GMSA 자격 증명 사양 리소스를 사용할 수 있다. 다음은 위에서 생성한 gmsa-WebApp1 자격 증명 사양 리소스를 사용하기 위해 webapp1-role 클러스터 롤에 바인딩되는 기본(default) 서비스 어카운트이다.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-default-svc-account-read-on-gmsa-WebApp1
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: webapp1-role
  apiGroup: rbac.authorization.k8s.io

파드 사양에서 GMSA 자격 증명 사양 참조 구성

파드 사양 필드 securityContext.windowsOptions.gmsaCredentialSpecName은 파드 사양에서 원하는 GMSA 자격 증명 사양 사용자 정의 리소스에 대한 참조를 지정하는 데 사용된다. 이렇게 하면 지정된 GMSA를 사용하도록 파드 사양의 모든 컨테이너가 구성된다. 다음은 gmsa-WebApp1을 참조하도록 채워진 어노테이션이 있는 샘플 파드 사양이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: gmsa-webapp1
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
      nodeSelector:
        kubernetes.io/os: windows

파드 사양의 개별 컨테이너는 컨테이너별 securityContext.windowsOptions.gmsaCredentialSpecName 필드를 사용하여 원하는 GMSA credspec을 지정할 수도 있다. 다음은 예이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
        securityContext:
          windowsOptions:
            gmsaCredentialSpecName: gmsa-Webapp1
      nodeSelector:
        kubernetes.io/os: windows

(위에서 설명한 대로) GMSA 필드가 채워진 파드 사양이 클러스터에 적용되면 다음과 같은 일련의 이벤트가 발생한다.

  1. 변형 웹훅은 GMSA 자격 증명 사양 리소스에 대한 모든 참조를 확인하고 GMSA 자격 증명 사양의 내용으로 확장한다.

  2. 검증 웹훅은 파드와 연결된 서비스 어카운트가 지정된 GMSA 자격 증명 사양의 use 동사에 대해 승인되었는지 확인한다.

  3. 컨테이너 런타임은 컨테이너가 액티브 디렉터리에서 GMSA의 ID를 가정하고 해당 ID를 사용하여 도메인의 서비스에 접근할 수 있도록 지정된 GMSA 자격 증명 사양으로 각 윈도우 컨테이너를 구성한다.

Containerd

윈도우 서버 2019에서 containerd와 함께 GMSA를 사용하려면 패치 KB5000822된 OS Build 17763.1817 (또는 이후 버전)을 실행해야 한다.

또한 파드에서 SMB 공유에 연결하려고 할 때 발생하는 containerd와 관련된 알려진 문제가 있다. GMSA를 구성하면 파드가 hostname 또는 FQDN을 사용하여 공유에 연결할 수 없지만, IP 주소를 사용하여 공유에 연결하면 예상대로 작동한다.

ping adserver.ad.local

hostname을 IPv4 주소로 올바르게 변환한다. 출력은 다음과 유사하다.

Pinging adserver.ad.local [192.168.111.18] with 32 bytes of data:
Reply from 192.168.111.18: bytes=32 time=6ms TTL=124
Reply from 192.168.111.18: bytes=32 time=5ms TTL=124
Reply from 192.168.111.18: bytes=32 time=5ms TTL=124
Reply from 192.168.111.18: bytes=32 time=5ms TTL=124

그러나 hostname을 사용하여 디렉터리를 탐색하려고 할 때

cd \\adserver.ad.local\test

대상 공유가 존재하지 않음을 암시하는 오류가 표시된다.

cd : Cannot find path '\\adserver.ad.local\test' because it does not exist.
At line:1 char:1
+ cd \\adserver.ad.local\test
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\\adserver.ad.local\test:String) [Set-Location], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand

그러나 IPv4 주소를 대신 사용하여 공유를 탐색하면 오류가 사라진다. 다음은 예시이다.

cd \\192.168.111.18\test

공유 내의 디렉터리로 변경하면 다음과 유사한 프롬프트가 표시된다.

Microsoft.PowerShell.Core\FileSystem::\\192.168.111.18\test>

동작을 수정하려면 노드에서 reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1을 실행하여 필요한 레지스트리 키를 추가해야 한다. 이 노드 변경 사항은 새로 생성된 파드에만 적용되는데, SMB 공유에 액세스해야 하는 실행 중인 파드를 다시 생성해야 하는 것을 의미한다.

문제 해결

GMSA가 사용자 환경에서 작동하도록 하는 데 어려움이 있는 경우 취할 수 있는 몇 가지 문제 해결 단계가 있다.

먼저 credspec이 파드에 전달되었는지 확인한다. 이렇게 하려면 파드 중 하나에서 exec를 실행하고 nltest.exe /parentdomain 명령의 출력을 확인해야 한다.

아래 예에서 파드는 credspec을 올바르게 가져오지 못했다.

kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

nltest.exe /parentdomain 는 다음과 같은 오류를 발생시킨다.

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

파드가 credspec을 올바르게 가져오면 다음으로 도메인과의 통신을 확인한다. 먼저 파드 내부에서 nslookup을 빠르게 수행하여 도메인의 루트를 찾는다.

이것은 다음의 세 가지를 의미한다.

  1. 파드는 DC에 도달할 수 있다.
  2. DC는 파드에 도달할 수 있다.
  3. DNS가 올바르게 작동하고 있다.

DNS 및 통신 테스트를 통과하면 다음으로 파드가 도메인과 보안 채널 통신을 설정했는지 확인해야 한다. 이렇게 하려면 파드에서 다시 exec를 실행하고 nltest.exe /query 명령을 실행한다.

nltest.exe /query

결과는 다음과 같다.

I_NetLogonControl failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

이것은 어떤 이유로 파드가 credspec에 지정된 계정을 사용하여 도메인에 로그온할 수 없음을 알려준다. 다음을 실행하여 보안 채널 복구를 시도할 수 있다.

nltest /sc_reset:domain.example

명령이 성공하면 다음과 유사한 출력이 표시된다.

Flags: 30 HAS_IP  HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

위의 방법으로 오류가 수정되면 다음 수명 주기 훅(hook)을 파드 사양에 추가하여 단계를 자동화할 수 있다. 오류가 수정되지 않은 경우 credspec을 다시 검사하여 정확하고 완전한지 확인해야 한다.

        image: registry.domain.example/iis-auth:1809v1
        lifecycle:
          postStart:
            exec:
              command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
        imagePullPolicy: IfNotPresent

위의 lifecycle 섹션을 파드 사양에 추가하면, 파드는 nltest.exe /query 명령이 오류 없이 종료될 때까지 나열된 명령을 실행하여 netlogon 서비스를 다시 시작한다.

4.3.4 - 파드에 대한 서비스 품질(QoS) 구성

이 페이지는 특정 서비스 품질(QoS) 클래스를 할당하기 위해 어떻게 파드를 구성해야 하는지 보여준다. 쿠버네티스는 QoS 클래스를 사용하여 파드 스케줄링과 축출을 결정한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

QoS 클래스

쿠버네티스가 파드를 생성할 때, 파드에 다음의 QoS 클래스 중 하나를 할당한다.

  • Guaranteed
  • Burstable
  • BestEffort

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace qos-example

Guaranteed QoS 클래스가 할당되는 파드 생성

파드에 Guaranteed QoS 클래스 할당을 위한 전제 조건은 다음과 같다.

  • 파드 내 모든 컨테이너는 메모리 상한과 메모리 요청량을 가지고 있어야 한다.
  • 파드 내 모든 컨테이너의 메모리 상한이 메모리 요청량과 일치해야 한다.
  • 파드 내 모든 컨테이너는 CPU 상한과 CPU 요청량을 가지고 있어야 한다.
  • 파드 내 모든 컨테이너의 CPU 상한이 CPU 요청량과 일치해야 한다.

이러한 제약은 초기화 컨테이너와 앱 컨테이너 모두에 동일하게 적용된다.

다음은 하나의 컨테이너를 갖는 파드의 구성 파일이다. 해당 컨테이너는 메모리 상한과 메모리 요청량을 갖고 있고, 200MiB로 동일하다. 해당 컨테이너는 CPU 상한과 CPU 요청량을 가지며, 700 milliCPU로 동일하다.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod.yaml --namespace=qos-example

파드의 상세 정보를 본다.

kubectl get pod qos-demo --namespace=qos-example --output=yaml

출력 결과는 쿠버네티스가 파드에 Guaranteed QoS 클래스를 부여했음을 보여준다. 또한 파드의 컨테이너가 메모리 요청량과 일치하는 메모리 상한을 가지며, CPU 요청량과 일치하는 CPU 상한을 갖고 있음을 확인할 수 있다.

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
    ...
status:
  qosClass: Guaranteed

파드를 삭제한다.

kubectl delete pod qos-demo --namespace=qos-example

Burstable QoS 클래스가 할당되는 파드 생성

다음의 경우 파드에 Burstable QoS 클래스가 부여된다.

  • 파드가 Guaranteed QoS 클래스 기준을 만족하지 않는다.
  • 파드 내에서 최소한 하나의 컨테이너가 메모리 또는 CPU 요청량을 가진다.

컨테이너가 하나인 파드의 구성 파일은 다음과 같다. 컨테이너는 200MiB의 메모리 상한과 100MiB의 메모리 요청량을 가진다.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-2.yaml --namespace=qos-example

파드의 상세 정보를 본다.

kubectl get pod qos-demo-2 --namespace=qos-example --output=yaml

출력 결과는 쿠버네티스가 파드에 Burstable QoS 클래스를 부여했음을 보여준다.

spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: qos-demo-2-ctr
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi
  ...
status:
  qosClass: Burstable

파드를 삭제한다.

kubectl delete pod qos-demo-2 --namespace=qos-example

BestEffort QoS 클래스가 할당되는 파드 생성

파드에 QoS 클래스 BestEffort를 제공하려면, 파드의 컨테이너에 메모리 또는 CPU의 상한이나 요청량이 없어야 한다.

컨테이너가 하나인 파드의 구성 파일이다. 해당 컨테이너는 메모리 또는 CPU의 상한이나 요청량을 갖지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-3.yaml --namespace=qos-example

파드의 상세 정보를 본다.

kubectl get pod qos-demo-3 --namespace=qos-example --output=yaml

출력 결과는 쿠버네티스가 파드에 BestEffort QoS 클래스를 부여했음을 보여준다.

spec:
  containers:
    ...
    resources: {}
  ...
status:
  qosClass: BestEffort

파드를 삭제한다.

kubectl delete pod qos-demo-3 --namespace=qos-example

컨테이너가 두 개인 파드 생성

컨테이너가 두 개인 파드의 구성 파일이다. 한 컨테이너는 200MiB의 메모리 요청량을 지정한다. 다른 컨테이너는 어떤 요청량이나 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-4
  namespace: qos-example
spec:
  containers:

  - name: qos-demo-4-ctr-1
    image: nginx
    resources:
      requests:
        memory: "200Mi"

  - name: qos-demo-4-ctr-2
    image: redis

참고로 이 파드는 Burstable QoS 클래스의 기준을 충족한다. 즉, Guaranteed QoS 클래스에 대한 기준을 충족하지 않으며, 해당 컨테이너 중 하나가 메모리 요청량을 갖는다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-4.yaml --namespace=qos-example

파드의 상세 정보를 본다.

kubectl get pod qos-demo-4 --namespace=qos-example --output=yaml

출력 결과는 쿠버네티스가 파드에 Burstable QoS 클래스를 부여했음을 보여준다.

spec:
  containers:
    ...
    name: qos-demo-4-ctr-1
    resources:
      requests:
        memory: 200Mi
    ...
    name: qos-demo-4-ctr-2
    resources: {}
    ...
status:
  qosClass: Burstable

파드를 삭제한다.

kubectl delete pod qos-demo-4 --namespace=qos-example

정리

네임스페이스를 삭제한다.

kubectl delete namespace qos-example

다음 내용

앱 개발자를 위한 문서

클러스터 관리자를 위한 문서

4.3.5 - 스토리지의 볼륨을 사용하는 파드 구성

이 페이지는 스토리지의 볼륨을 사용하는 파드를 구성하는 방법을 설명한다.

컨테이너 파일 시스템은 컨테이너가 살아있는 동안만 존재한다. 따라서 컨테이너가 종료되고 재시작할 때, 파일 시스템 변경사항이 손실된다. 컨테이너와 독립적이며 보다 일관된 스토리지를 위해 사용자는 볼륨을 사용할 수 있다. 이것은 레디스(Redis)와 같은 키-값 저장소나 데이터베이스와 같은 스테이트풀 애플리케이션에 매우 중요하다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

파드에 볼륨 구성

이 연습에서는 하나의 컨테이너를 실행하는 파드를 생성한다. 이 파드는 컨테이너가 종료되고, 재시작 하더라도 파드의 수명동안 지속되는 emptyDir 유형의 볼륨이 있다. 파드의 구성 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
  1. 파드 생성

    kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
    
  2. 파드의 컨테이너가 Running 중인지 확인하고, 파드의 변경사항을 지켜본다.

    kubectl get pod redis --watch
    

    출력은 이와 유사하다.

    NAME      READY     STATUS    RESTARTS   AGE
    redis     1/1       Running   0          13s
    
  3. 다른 터미널에서 실행 중인 컨테이너의 셸을 획득한다.

    kubectl exec -it redis -- /bin/bash
    
  4. 셸에서 /data/redis 로 이동하고, 파일을 생성한다.

    root@redis:/data# cd /data/redis/
    root@redis:/data/redis# echo Hello > test-file
    
  5. 셸에서 실행 중인 프로세스 목록을 확인한다.

    root@redis:/data/redis# apt-get update
    root@redis:/data/redis# apt-get install procps
    root@redis:/data/redis# ps aux
    

    출력은 이와 유사하다.

    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    redis        1  0.1  0.1  33308  3828 ?        Ssl  00:46   0:00 redis-server *:6379
    root        12  0.0  0.0  20228  3020 ?        Ss   00:47   0:00 /bin/bash
    root        15  0.0  0.0  17500  2072 ?        R+   00:48   0:00 ps aux
    
  6. 셸에서 Redis 프로세스를 강제종료(kill)한다.

    root@redis:/data/redis# kill <pid>
    

    여기서 <pid>는 Redis 프로세스 ID(PID) 이다.

  7. 원래 터미널에서, Redis 파드의 변경을 지켜본다. 결국, 다음과 유사한 것을 보게 될 것이다.

    NAME      READY     STATUS     RESTARTS   AGE
    redis     1/1       Running    0          13s
    redis     0/1       Completed  0         6m
    redis     1/1       Running    1         6m
    

이때, 컨테이너는 종료되고 재시작된다. 이는 Redis 파드의 restartPolicyAlways 이기 때문이다.

  1. 재시작된 컨테이너의 셸을 획득한다.

    kubectl exec -it redis -- /bin/bash
    
  2. 셸에서 /data/redis 로 이동하고, test-file 이 여전히 존재하는지 확인한다.

    root@redis:/data/redis# cd /data/redis/
    root@redis:/data/redis# ls
    test-file
    
  3. 이 연습을 위해 생성한 파드를 삭제한다.

    kubectl delete pod redis
    

다음 내용

  • 볼륨을 참고한다.

  • 파드을 참고한다.

  • 쿠버네티스는 emptyDir 이 제공하는 로컬 디스크 스토리지뿐만 아니라, 중요한 데이터에 선호하는 GCE의 PD, EC2의 EBS를 포함해서 네트워크 연결 스토리지(NAS) 솔루션을 지원하며, 노드의 디바이스 마운트, 언마운트와 같은 세부사항을 처리한다. 자세한 내용은 볼륨을 참고한다.

4.3.6 - 스토리지로 퍼시스턴트볼륨(PersistentVolume)을 사용하도록 파드 설정하기

이 페이지는 스토리지에 대해 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 사용하도록 파드를 설정하는 방법을 보여준다. 과정의 요약은 다음과 같다.

  1. 클러스터 관리자로서, 물리적 스토리지와 연결되는 퍼시스턴트볼륨을 생성한다. 볼륨을 특정 파드와 연결하지 않는다.

  2. 그 다음 개발자 / 클러스터 사용자의 역할로서, 적합한 퍼시스턴트볼륨에 자동으로 바인딩되는 퍼시스턴트볼륨클레임을 생성한다.

  3. 스토리지에 대해 위의 퍼시스턴트볼륨클레임을 사용하는 파드를 생성한다.

시작하기 전에

  • 사용자는 노드가 단 하나만 있는 쿠버네티스 클러스터가 필요하고, kubectl 커맨드라인 툴이 사용자의 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 만약 사용자가 아직 단일 노드 클러스터를 가지고 있지 않다면, Minikube를 사용하여 클러스터 하나를 생성할 수 있다.

  • 퍼시스턴트 볼륨의 관련 자료에 익숙해지도록 한다.

사용자 노드에 index.html 파일 생성하기

사용자 클러스터의 단일 노드에 연결되는 셸을 연다. 셸을 여는 방법은 클러스터 설정에 따라 달라진다. 예를 들어 Minikube를 사용하는 경우, minikube ssh 명령어를 입력하여 노드로 연결되는 셸을 열 수 있다.

해당 노드의 셸에서 /mnt/data 디렉터리를 생성한다.

# 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo mkdir /mnt/data

/mnt/data 디렉터리에서 index.html 파일을 생성한다.

# 이번에도 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"

index.html 파일이 존재하는지 테스트한다.

cat /mnt/data/index.html

결과는 다음과 같다.

Hello from Kubernetes storage

이제 사용자 노드에서 셸을 종료해도 된다.

퍼시스턴트볼륨 생성하기

이 예제에서, 사용자는 hostPath 퍼시스턴트볼륨을 생성한다. 쿠버네티스는 단일 노드에서의 개발과 테스트를 위해 hostPath를 지원한다. hostPath 퍼시스턴트볼륨은 네트워크로 연결된 스토리지를 모방하기 위해, 노드의 파일이나 디렉터리를 사용한다.

운영 클러스터에서, 사용자가 hostPath를 사용하지는 않는다. 대신, 클러스터 관리자는 Google Compute Engine 영구 디스크, NFS 공유 또는 Amazone Elastic Block Store 볼륨과 같은 네트워크 자원을 프로비저닝한다. 클러스터 관리자는 스토리지클래스(StorageClasses)를 사용하여 동적 프로비저닝을 설정할 수도 있다.

hostPath 퍼시스턴트볼륨의 설정 파일은 아래와 같다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

설정 파일에 클러스터 노드의 /mnt/data 에 볼륨이 있다고 지정한다. 또한 설정에서 볼륨 크기를 10 기가바이트로 지정하고 단일 노드가 읽기-쓰기 모드로 볼륨을 마운트할 수 있는 ReadWriteOnce 접근 모드를 지정한다. 여기서는 퍼시스턴트볼륨클레임의 스토리지클래스 이름manual 로 정의하며, 퍼시스턴트볼륨클레임의 요청을 이 퍼시스턴트볼륨에 바인딩하는데 사용한다.

퍼시스턴트볼륨을 생성한다.

kubectl apply -f https://k8s.io/examples/pods/storage/pv-volume.yaml

퍼시스턴트볼륨에 대한 정보를 조회한다.

kubectl get pv task-pv-volume

결과는 퍼시스턴트볼륨의 STATUSAvailable 임을 보여준다. 이는 아직 퍼시스턴트볼륨클레임이 바인딩되지 않았다는 것을 의미한다.

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Available             manual                   4s

퍼시스턴트볼륨클레임 생성하기

다음 단계는 퍼시스턴트볼륨클레임을 생성하는 단계이다. 파드는 퍼시스턴트볼륨클레임을 사용하여 물리적인 스토리지를 요청한다. 이 예제에서, 사용자는 적어도 하나 이상의 노드에 대해 읽기-쓰기 접근을 지원하며 최소 3 기가바이트의 볼륨을 요청하는 퍼시스턴트볼륨클레임을 생성한다.

퍼시스턴트볼륨클레임에 대한 설정 파일은 다음과 같다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

퍼시스턴트볼륨클레임을 생성한다.

kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml

사용자가 퍼시스턴트볼륨클레임을 생성한 후에, 쿠버네티스 컨트롤 플레인은 클레임의 요구사항을 만족하는 퍼시스턴트볼륨을 찾는다. 컨트롤 플레인이 동일한 스토리지클래스를 갖는 적절한 퍼시스턴트볼륨을 찾으면, 볼륨에 클레임을 바인딩한다.

퍼시스턴트볼륨을 다시 확인한다.

kubectl get pv task-pv-volume

이제 결과는 STATUSBound 임을 보여준다.

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                   STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Bound     default/task-pv-claim   manual                   2m

퍼시스턴트볼륨클레임을 확인한다.

kubectl get pvc task-pv-claim

결과는 퍼시스턴트볼륨클레임이 사용자의 퍼시스턴트볼륨인 task-pv-volume 에 바인딩되어 있음을 보여준다.

NAME            STATUS    VOLUME           CAPACITY   ACCESSMODES   STORAGECLASS   AGE
task-pv-claim   Bound     task-pv-volume   10Gi       RWO           manual         30s

파드 생성하기

다음 단계는 볼륨으로 퍼시스턴트볼륨클레임을 사용하는 파드를 만드는 단계이다.

파드에 대한 설정 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage


파드의 설정 파일은 퍼시스턴트볼륨클레임을 지정하지만, 퍼시스턴트볼륨을 지정하지는 않는다는 것을 유념하자. 파드의 관점에서 볼때, 클레임은 볼륨이다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/storage/pv-pod.yaml

파드의 컨테이너가 실행 중임을 확인한다.

kubectl get pod task-pv-pod

사용자 파드에서 구동되고 있는 컨테이너에 셸로 접근한다.

kubectl exec -it task-pv-pod -- /bin/bash

사용자의 셸에서, nginx가 hostPath 볼륨으로부터 index.html 파일을 제공하는지 확인한다.

# 이전 단계에서 "kubectl exec" 명령을 실행한 root 셸 안에서 
# 다음의 3개 명령을 실행해야 한다.
apt update
apt install curl
curl http://localhost/

결과는 hostPath 볼륨에 있는 index.html 파일에 사용자가 작성한 텍스트를 보여준다.

Hello from Kubernetes storage

만약 사용자가 위와 같은 메시지를 확인하면, 파드가 퍼시스턴트볼륨클레임의 스토리지를 사용하도록 성공적으로 설정한 것이다.

정리하기

파드, 퍼시스턴트볼륨클레임, 퍼시스턴트볼륨을 삭제한다.

kubectl delete pod task-pv-pod
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume

만약 클러스터의 노드에 대한 셸이 열려져 있지 않은 경우, 이전과 동일한 방식으로 새로운 셸을 연다.

사용자 노드의 셸에서, 생성한 파일과 디렉터리를 제거한다.

# 사용자 노드에서 슈퍼유저로 명령을 수행하기 위하여
# "sudo"를 사용한다고 가정한다
sudo rm /mnt/data/index.html
sudo rmdir /mnt/data

이제 사용자 노드에서 셸을 종료해도 된다.

접근 제어

그룹 ID(GID)로 설정된 스토리지는 동일한 GID를 사용하는 파드에서만 쓰기 작업을 허용한다. GID가 일치하지 않거나 누락되었을 경우 권한 거부 오류가 발생한다. 사용자와의 조정 필요성을 줄이기 위하여 관리자는 퍼시스턴트 볼륨에 GID로 어노테이션을 달 수 있다. 그 뒤에, 퍼시스턴트볼륨을 사용하는 모든 파드에 대하여 GID가 자동으로 추가된다.

다음과 같이 pv.beta.kubernetes.io/gid 어노테이션을 사용한다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
  annotations:
    pv.beta.kubernetes.io/gid: "1234"

파드가 GID 어노테이션이 있는 퍼시스턴트볼륨을 사용하면, 어노테이션으로 달린 GID가 파드의 보안 컨텍스트에 지정된 GID와 동일한 방식으로 파드의 모든 컨테이너에 적용된다. 파드의 명세 혹은 퍼시스턴트볼륨의 어노테이션으로부터 생성된 모든 GID는, 각 컨테이너에서 실행되는 첫 번째 프로세스에 적용된다.

다음 내용

Reference

4.3.7 - 프라이빗 레지스트리에서 이미지 받아오기

이 페이지는 프라이빗 도커 레지스트리나 리포지터리로부터 이미지를 받아오기 위해 시크릿(Secret)을 사용하는 파드를 생성하는 방법을 보여준다.

시작하기 전에

  • 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

    버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

  • 이 실습을 수행하기 위해, 도커 ID와 비밀번호가 필요하다.

도커 로그인

노트북에 프라이빗 이미지를 받아오기 위하여 레지스트리 인증을 필수로 수행해야 한다.

docker login

프롬프트가 나타나면, 도커 사용자 이름(username)과 비밀번호(password)를 입력하자.

로그인 프로세스는 권한 토큰 정보를 가지고 있는 config.json 파일을 생성하거나 업데이트 한다.

config.json 파일을 확인하자.

cat ~/.docker/config.json

하단과 유사한 결과를 확인할 수 있다.

{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "c3R...zE2"
        }
    }
}

기존의 도커 자격 증명을 기반으로 시크릿 생성하기

쿠버네티스 클러스터는 프라이빗 이미지를 받아올 때, 컨테이너 레지스트리에 인증하기 위하여 kubernetes.io/dockerconfigjson 타입의 시크릿을 사용한다.

만약 이미 docker login 을 수행하였다면, 이 때 생성된 자격 증명을 쿠버네티스 클러스터로 복사할 수 있다.

kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson

오브젝트에 대한 더 세밀한 제어(새로운 시크릿에 대한 네임스페이스나 레이블을 지정하는 등)가 필요할 경우, 시크릿을 사용자 정의한 후에 저장할 수도 있다. 다음을 확인하자.

  • 데이터 항목의 이름을 .dockerconfigjson 으로 설정한다
  • 도커 파일을 base64로 인코딩하고 그 문자열을 data[".dockerconfigjson"] 필드에 자르지 않고 한 줄로 이어서 붙여넣는다
  • typekubernetes.io/dockerconfigjson 으로 설정한다

예:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: awesomeapps
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson

만약 error: no objects passed to create 메세지가 출력될 경우, base64로 인코딩된 문자열이 유효하지 않음을 의미한다. 또한 Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ... 메세지가 출력될 경우, base64로 인코딩된 문자열이 정상적으로 디코딩되었으나, .docker/config.json 파일로 파싱되지 못한 것을 의미한다.

커맨드 라인에서 자격 증명을 통하여 시크릿 생성하기

regcred 라는 이름의 시크릿을 생성하자.

kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

아래의 각 항목에 대한 설명을 참고한다.

  • <your-registry-server> 은 프라이빗 도커 저장소의 FQDN 주소이다. 도커허브(DockerHub)는 https://index.docker.io/v1/ 를 사용한다.
  • <your-name> 은 도커 사용자의 계정이다.
  • <your-pword> 은 도커 사용자의 비밀번호이다.
  • <your-email> 은 도커 사용자의 이메일 주소이다.

이를 통해 regcred 라는 시크릿으로 클러스터 내에서 도커 자격 증명을 생성했다.

시크릿 regcred 검증하기

방금 생성한 regcred 시크릿의 내용을 확인하기 위하여, YAML 형식으로 시크릿을 확인하자.

kubectl get secret regcred --output=yaml

결과는 다음과 같다.

apiVersion: v1
kind: Secret
metadata:
  ...
  name: regcred
  ...
data:
  .dockerconfigjson: eyJodHRwczovL2luZGV4L ... J0QUl6RTIifX0=
type: kubernetes.io/dockerconfigjson

.dockerconfigjson 필드의 값은 도커 자격 증명의 base64 인코딩 결과이다.

.dockerconfigjson 필드의 값을 확인하기 위하여, 시크릿 데이터를 읽을 수 있는 형식으로 변경한다.

kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode

결과는 다음과 같다.

{"auths":{"your.private.registry.example.com":{"username":"janedoe","password":"xxxxxxxxxxx","email":"[email protected]","auth":"c3R...zE2"}}}

auth 필드의 값을 확인하기 위하여, base64로 인코딩된 데이터를 읽을 수 있는 형식으로 변경한다.

echo "c3R...zE2" | base64 --decode

결과로, 사용자 이름과 비밀번호가 : 로 연결되어 아래와 같이 표현된다.

janedoe:xxxxxxxxxxx

참고로 시크릿 데이터에는 사용자의 로컬에 있는 ~/.docker/config.json 파일과 유사한 인증 토큰이 포함되어 있다.

이를 통해 regcred 라는 시크릿으로 클러스터 내에서 도커 자격 증명을 생성했다.

시크릿을 사용하는 파드 생성하기

다음은 regcred 에 있는 도커 자격 증명에 접근해야 하는 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

아래의 파일을 다운로드받는다.

wget -O my-private-reg-pod.yaml https://k8s.io/examples/pods/private-reg-pod.yaml

my-private-reg-pod.yaml 파일 안에서, <your-private-image> 값을 다음과 같은 프라이빗 저장소 안의 이미지 경로로 변경한다.

your.private.registry.example.com/janedoe/jdoe-private:v1

프라이빗 저장소에서 이미지를 받아오기 위하여, 쿠버네티스에서 자격 증명이 필요하다. 구성 파일의 imagePullSecrets 필드를 통해 쿠버네티스가 regcred 라는 시크릿으로부터 자격 증명을 가져올 수 있다.

시크릿을 사용해서 파드를 생성하고, 파드가 실행되는지 확인하자.

kubectl apply -f my-private-reg-pod.yaml
kubectl get pod private-reg

다음 내용

4.3.8 - 노드 어피니티를 사용해 노드에 파드 할당

이 문서는 쿠버네티스 클러스터의 특정 노드에 노드 어피니티를 사용해 쿠버네티스 파드를 할당하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.10. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

노드에 레이블 추가

  1. 클러스터의 노드를 레이블과 함께 나열하자.

    kubectl get nodes --show-labels
    

    결과는 아래와 같다.

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. 노드 한 개를 선택하고, 레이블을 추가하자.

    kubectl label nodes <your-node-name> disktype=ssd
    

    <your-node-name> 는 선택한 노드의 이름이다.

  3. 선택한 노드가 disktype=ssd 레이블을 갖고 있는지 확인하자.

    kubectl get nodes --show-labels
    

    결과는 아래와 같다.

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    위의 결과에서, worker0 노드에 disktype=ssd 레이블이 있는 것을 확인할 수 있다.

필수적인 노드 어피니티를 사용해 파드 스케줄하기

이 매니페스트는 disktype: ssd 라는 requiredDuringSchedulingIgnoredDuringExecution 노드 어피니티를 가진 파드를 설명한다. 파드가 disktype=ssd 레이블이 있는 노드에만 스케줄될 것이라는 것을 의미한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd            
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. 매니페스트를 적용하여 선택한 노드에 스케줄된 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-required-affinity.yaml
    
  2. 파드가 선택한 노드에서 실행 중인지 확인하자.

    kubectl get pods --output=wide
    

    결과는 아래와 같다.

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

선호하는 노드 어피니티를 사용해 파드 스케줄하기

이 매니페스트는 disktype: ssd 라는 preferredDuringSchedulingIgnoredDuringExecution 노드 어피니티를 가진 파드를 설명한다. 파드가 disktype=ssd 레이블이 있는 노드를 선호한다는 것을 의미한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd          
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. 매니페스트를 적용하여 선택한 노드에 스케줄된 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-preferred-affinity.yaml
    
  2. 파드가 선택한 노드에서 실행 중인지 확인하자.

    kubectl get pods --output=wide
    

    결과는 아래와 같다.

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

다음 내용

노드 어피니티에 대해 더 알아보기.

4.3.9 - 노드에 파드 할당

이 문서는 쿠버네티스 클러스터의 특정 노드에 쿠버네티스 파드를 할당하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

노드에 레이블 추가

  1. 클러스터의 노드를 레이블과 함께 나열하자.

    kubectl get nodes --show-labels
    

    결과는 아래와 같다.

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. 노드 한 개를 선택하고, 레이블을 추가하자.

    kubectl label nodes <your-node-name> disktype=ssd
    

    <your-node-name>는 선택한 노드의 이름이다.

  3. 선택한 노드가 disktype=ssd 레이블을 갖고 있는지 확인하자.

    kubectl get nodes --show-labels
    

    결과는 아래와 같다.

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    위의 결과에서, worker0 노드에 disktype=ssd 레이블이 있는 것을 확인할 수 있다.

선택한 노드에 스케줄되도록 파드 생성하기

이 파드 구성 파일은 disktype: ssd라는 선택하는 노드 셀렉터를 가진 파드를 설명한다. 즉, disktype=ssd 레이블이 있는 노드에 파드가 스케줄될 것이라는 것을 의미한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
  1. 구성 파일을 사용해서 선택한 노드로 스케줄되도록 파드를 생성하자.

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml
    
  2. 파드가 선택한 노드에서 실행 중인지 확인하자.

    kubectl get pods --output=wide
    

    결과는 아래와 같다.

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

특정 노드에 스케줄되도록 파드 생성하기

nodeName 설정을 통해 특정 노드로 파드를 배포할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: foo-node # 특정 노드에 파드 스케줄
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

설정 파일을 사용해 foo-node 노드에 파드를 스케줄되도록 만들어 보자.

다음 내용

4.3.10 - 초기화 컨테이너에 대한 구성

이 페이지는 애플리케이션 실행 전에 파드를 초기화하기 위해 어떻게 초기화 컨테이너를 구성해야 하는지 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

초기화 컨테이너를 갖는 파드 생성

이 연습에서 하나의 애플리케이션 컨테이너와 하나의 초기화 컨테이너를 갖는 파드를 생성한다. 초기화 컨테이너는 애플리케이션 시작 전에 실행을 종료한다.

아래는 해당 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  # 이 컨테이너들은 파드 초기화 중에 실행된다.
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://info.cern.ch
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

이 구성 파일에서, 파드가 가진 볼륨을 초기화 컨테이너와 애플리케이션 컨테이너가 공유하는 것을 볼 수 있다.

초기화 컨테이너는 공유된 볼륨을 /work-dir 에 마운트하고, 애플리케이션 컨테이너는 공유된 볼륨을 /usr/share/nginx/html 에 마운트한다. 초기화 컨테이너는 다음 명령을 실행 후 종료한다.

wget -O /work-dir/index.html http://info.cern.ch

초기화 컨테이너는 nginx 서버의 루트 디렉터리 내 index.html 파일을 저장한다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/init-containers.yaml

nginx 컨테이너가 실행 중인지 확인한다.

kubectl get pod init-demo

출력 결과는 nginx 컨테이너가 실행 중임을 보여준다.

NAME        READY     STATUS    RESTARTS   AGE
init-demo   1/1       Running   0          1m

init-demo 파드 내 실행 중인 nginx 컨테이너의 셸을 실행한다.

kubectl exec -it init-demo -- /bin/bash

셸에서 GET 요청을 nginx 서버로 전송한다.

root@nginx:~# apt-get update
root@nginx:~# apt-get install curl
root@nginx:~# curl localhost

출력 결과는 nginx가 초기화 컨테이너에 의해 저장된 웹 페이지를 제공하고 있음을 보여준다.

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
  ...
  <li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
  ...

다음 내용

4.3.11 - 스태틱(static) 파드 생성하기

스태틱 파드API 서버 없이 특정 노드에 있는 kubelet 데몬에 의해 직접 관리된다. 컨트롤 플레인에 의해 관리되는 파드(예를 들어 디플로이먼트(Deployment))와는 달리, kubelet 이 각각의 스태틱 파드를 감시한다. (만약 실패할 경우 다시 구동한다.)

스태틱 파드는 항상 특정 노드에 있는 하나의 Kubelet에 매여 있다.

Kubelet 은 각각의 스태틱 파드에 대하여 쿠버네티스 API 서버에서 미러 파드(mirror pod)를 생성하려고 자동으로 시도한다. 즉, 노드에서 구동되는 파드는 API 서버에 의해서 볼 수 있지만, API 서버에서 제어될 수는 없다. 파드 이름에는 노드 호스트 이름 앞에 하이픈을 붙여 접미사로 추가된다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

이 페이지는 파드를 실행하기 위해 도커(Docker)를 사용하며, 노드에서 Fedora 운영 체제를 구동하고 있다고 가정한다. 다른 배포판이나 쿠버네티스 설치 지침과는 다소 상이할 수 있다.

스태틱 파드 생성하기

파일 시스템이 호스팅하는 구성 파일이나 웹이 호스팅하는 구성 파일을 사용하여 스태틱 파드를 구성할 수 있다.

파일시스템이 호스팅 하는 스태틱 파드 매니페스트

매니페스트는 특정 디렉터리에 있는 JSON 이나 YAML 형식의 표준 파드 정의이다. kubelet 구성 파일staticPodPath: <the directory> 필드를 사용하자. 명시한 디렉터리를 정기적으로 스캔하여, 디렉터리 안의 YAML/JSON 파일이 생성되거나 삭제되었을 때 스태틱 파드를 생성하거나 삭제한다. Kubelet 이 특정 디렉터리를 스캔할 때 점(.)으로 시작하는 단어를 무시한다는 점을 유의하자.

예를 들어, 다음은 스태틱 파드로 간단한 웹 서버를 구동하는 방법을 보여준다.

  1. 스태틱 파드를 실행할 노드를 선택한다. 이 예제에서는 my-model 이다.

    ssh my-node1
    
  2. /etc/kubelet.d 와 같은 디렉터리를 선택하고 웹 서버 파드의 정의를 해당 위치에, 예를 들어 /etc/kubelet.d/static-web.yaml 에 배치한다.

      # kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
    mkdir /etc/kubelet.d/
    cat <<EOF >/etc/kubelet.d/static-web.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    EOF
    
  3. 노드에서 kubelet 실행 시에 --pod-manifest-path=/etc/kubelet.d/ 와 같이 인자를 제공하여 해당 디렉터리를 사용하도록 구성한다. Fedora 의 경우 이 줄을 포함하기 위하여 /etc/kubernetes/kubelet 파일을 다음과 같이 수정한다.

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --pod-manifest-path=/etc/kubelet.d/"
    

    혹은 kubelet 구성 파일staticPodPath: <the directory> 필드를 추가한다.

  4. kubelet을 재시작한다. Fedora의 경우 아래와 같이 수행한다.

    # kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
    systemctl restart kubelet
    

웹이 호스팅 하는 스태틱 파드 매니페스트

Kubelet은 --manifest-url=<URL> 의 인수로 지정된 파일을 주기적으로 다운로드하여 해당 파일을 파드의 정의가 포함된 JSON/YAML 파일로 해석한다. 파일시스템이 호스팅 하는 매니페스트 의 작동 방식과 유사하게 kubelet은 스케줄에 맞춰 매니페스트 파일을 다시 가져온다. 스태틱 파드의 목록에 변경된 부분이 있을 경우, kubelet 은 이를 적용한다.

이 방법을 사용하기 위하여 다음을 수행한다.

  1. kubelet 에게 파일의 URL을 전달하기 위하여 YAML 파일을 생성하고 이를 웹 서버에 저장한다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    
  2. 선택한 노드에서 --manifest-url=<manifest-url> 을 실행하여 웹 메니페스트를 사용하도록 kubelet을 구성한다. Fedora 의 경우 이 줄을 포함하기 위하여 /etc/kubernetes/kubelet 파일을 수정한다.

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --manifest-url=<manifest-url>"
    
  3. Kubelet을 재시작한다. Fedora의 경우 아래와 같이 수행한다.

    # kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
    systemctl restart kubelet
    

스태틱 파드 행동 관찰하기

Kubelet 을 시작하면, 정의된 모든 스태틱 파드가 자동으로 시작된다. 스태틱 파드를 정의하고, kubelet을 재시작했으므로, 새로운 스태틱 파드가 이미 실행 중이어야 한다.

(노드에서) 구동되고 있는 (스태틱 파드를 포함한) 컨테이너들을 볼 수 있다.

# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
docker ps

결과는 다음과 유사하다.

CONTAINER ID IMAGE         COMMAND  CREATED        STATUS         PORTS     NAMES
f6d05272b57e nginx:latest  "nginx"  8 minutes ago  Up 8 minutes             k8s_web.6f802af4_static-web-fk-node1_default_67e24ed9466ba55986d120c867395f3c_378e5f3c

API 서버에서 미러 파드를 볼 수 있다.

kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node1        1/1       Running   0          2m

스태틱 파드에 있는 레이블 은 미러 파드로 전파된다. 셀렉터 등을 통하여 이러한 레이블을 사용할 수 있다.

만약 API 서버로부터 미러 파드를 지우기 위하여 kubectl 을 사용하려 해도, kubelet 은 스태틱 파드를 지우지 않는다.

kubectl delete pod static-web-my-node1
pod "static-web-my-node1" deleted

파드가 여전히 구동 중인 것을 볼 수 있다.

kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node1        1/1       Running   0          12s

kubelet 이 구동 중인 노드로 돌아가서 도커 컨테이너를 수동으로 중지할 수 있다. 일정 시간이 지나면, kubelet이 파드를 자동으로 인식하고 다시 시작하는 것을 볼 수 있다.

# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
docker stop f6d05272b57e # 예제를 수행하는 사용자의 컨테이너 ID로 변경한다.
sleep 20
docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED       ...
5b920cbaf8b1        nginx:latest  "nginx -g 'daemon of   2 seconds ago ...

스태틱 파드의 동적 추가 및 제거

실행 중인 kubelet 은 주기적으로, 설정된 디렉터리(예제에서는 /etc/kubelet.d)에서 변경 사항을 스캔하고, 이 디렉터리에 새로운 파일이 생성되거나 삭제될 경우, 파드를 생성/삭제 한다.

# 예제를 수행하는 사용자가 파일시스템이 호스팅하는 스태틱 파드 설정을 사용한다고 가정한다.
# kubelet 이 동작하고 있는 노드에서 이 명령을 수행한다.
#
mv /etc/kubelet.d/static-web.yaml /tmp
sleep 20
docker ps
# 구동 중인 nginx 컨테이너가 없는 것을 확인한다.
mv /tmp/static-web.yaml  /etc/kubelet.d/
sleep 20
docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED           ...
e7a62e3427f1        nginx:latest  "nginx -g 'daemon of   27 seconds ago

4.4 - 쿠버네티스 오브젝트 관리

쿠버네티스 API와 상호 작용하기 위한 선언적이고 명령적인 패러다임

4.4.1 - 구성 파일을 이용한 쿠버네티스 오브젝트의 선언형 관리

쿠버네티스 오브젝트는 여러 개의 오브젝트 구성 파일을 디렉터리에 저장하고 필요에 따라 kubectl apply를 사용하여 재귀적으로 오브젝트를 생성하고 업데이트함으로써 생성, 업데이트 및 삭제할 수 있다. 이 방식은 변경사항을 되돌려 오브젝트 구성 파일에 병합하지 않고 활성 오브젝트에 가해진 기록을 유지한다. kubectl diff는 또한 apply가 어떠한 변경사항을 이루어질지에 대한 프리뷰를 제공한다.

시작하기 전에

kubectl를 설치한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

트레이드 오프

kubectl 툴은 세 가지 방식의 오브젝트 관리를 지원한다.

  • 명령형 커맨드
  • 명령형 오브젝트 구성
  • 선언형 오브젝트 구성

오브젝트 관리 방식의 종류별 장단점에 대한 논의는 쿠버네티스 오브젝트 관리를 참고한다.

개요

선언형 오브젝트 구성은 쿠버네티스 오브젝트 정의와 구성에 대한 확실한 이해가 필요하다. 아직 그렇지 못하다면, 먼저 다음 문서를 읽고 이해한다.

다음은 이 문서에서 사용되는 용어에 대한 정의이다.

  • 오브젝트 구성 파일 / 구성 파일: 쿠버네티스 오브젝트에 대한 구성을 정의하는 하나의 파일. 이 주제는 어떻게 kubectl apply에 구성 파일을 전달하는지에 대해 보여준다. 구성 파일은 일반적으로 Git과 같은, 소스 컨트롤에 저장된다.
  • 활성 오브젝트 구성 / 활성 구성: 쿠버네티스 클러스터에 의해 관측된 오브젝트에 대한 활성 구성 값. 이것들은 쿠버네티스 클러스터 저장소에 유지된다. 일반적으로 etcd가 사용된다.
  • 선언형 구성 작성자 / 선언형 작성자: 활성 오브젝트를 업데이트해 주는 사람이나 소프트웨어. 이 주제에서 언급하는 활성 작성자는 오브젝트 구성 파일에 변경을 가하고 kubectl apply를 실행하여 변경사항을 기록한다.

오브젝트 생성 방법

기존에 존재하는 것을 제외한, 지정한 디렉터리 내 구성 파일에 의해 정의된 모든 오브젝트를 생성하기 위해 kubectl apply를 사용한다.

kubectl apply -f <디렉터리>/

이것은 각 오브젝트에 대해 kubectl.kubernetes.io/last-applied-configuration: '{...}' 어노테이션을 설정한다. 해당 어노테이션은 오브젝트를 생성하기 위해 사용했던 오브젝트 구성 파일의 내용을 포함한다. 

다음은 오브젝트 구성 파일에 대한 예시이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

생성될 오브젝트를 출력하려면 kubectl diff를 실행한다. 

kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml

kubectl apply를 사용하여 오브젝트를 생성한다.

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

kubectl get을 사용하여 활성 구성을 출력한다.

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

출력은 kubectl.kubernetes.io/last-applied-configuration 어노테이션이 활성 구성에 기록된 것을 보여주며, 그것은 구성 파일과 일치한다.

kind: Deployment
metadata:
  annotations:
    # ...
    # This is the json representation of simple_deployment.yaml
    # It was written by kubectl apply when the object was created
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

오브젝트 업데이트 방법

또한 오브젝트가 기존에 존재하더라도 디렉터리 내 정의된 모든 오브젝트를 업데이트하기 위해 kubectl apply를 사용할 수 있다. 이러한 접근방식은 다음을 수행할 수 있게 해준다.

  1. 활성 구성 내 구성 파일에 나타나는 필드 설정
  2. 활성 구성 내 구성 파일로부터 제거된 필드 정리
kubectl diff -f <디렉터리>/
kubectl apply -f <디렉터리>/

다음은 구성 파일의 예시이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

kubectl apply를 사용하여 오브젝트를 생성한다.

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

kubectl get을 사용하여 활성 구성을 출력한다.

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

출력은 kubectl.kubernetes.io/last-applied-configuration 어노테이션이 활성 구성에 기록된 것을 보여주며, 그것은 구성 파일과 일치한다.

kind: Deployment
metadata:
  annotations:
    # ...
    # This is the json representation of simple_deployment.yaml
    # It was written by kubectl apply when the object was created
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

kubectl scale을 사용하여 활성 구성 내 replicas 필드를 직접 업데이트한다. 이는 kubectl apply를 사용하지 않는다.

kubectl scale deployment/nginx-deployment --replicas=2

kubectl get을 사용하여 활성 구성을 출력한다.

kubectl get deployment nginx-deployment -o yaml

출력은 replicas 필드가 2로 설정된 것을 보여주며, last-applied-configuration 어노테이션은 replicas 필드를 포함하지 않는다.

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # note that the annotation does not contain replicas
    # because it was not updated through apply
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # written by scale
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

nginx:1.14.2에서 nginx:1.16.1로 이미지를 변경하기 위해 simple_deployment.yaml 구성 파일을 업데이트 하고, minReadySeconds 필드를 삭제한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # update the image
        ports:
        - containerPort: 80

구성 파일에 이루어진 변경사항을 적용한다.

kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml

kubectl get을 사용하여 활성 구성을 출력한다.

kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml

출력은 활성 구성에 다음의 변경사항을 보여준다.

  • replicas 필드는 kubectl scale에 의해 설정된 값 2를 유지한다.
    이는 구성 파일에서 생략되었기 때문에 가능하다.
  • image 필드는 nginx:1.14.2에서 nginx:1.16.1로 업데이트되었다.
  • last-applied-configuration 어노테이션은 새로운 이미지로 업데이트되었다.
  • minReadySeconds 필드는 지워졌다.
  • last-applied-configuration 어노테이션은 더 이상 minReadySeconds 필드를 포함하지 않는다.
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.11.9,
    # but does not contain the updated replicas to 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  replicas: 2 # Set by `kubectl scale`.  Ignored by `kubectl apply`.
  # minReadySeconds cleared by `kubectl apply`
  # ...
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

오브젝트 삭제 방법

kubectl apply에 의해 관리되는 오브젝트를 삭제하는데 2가지 접근 방법이 있다.

권장 방법: kubectl delete -f <파일명>

명령형 커맨드를 사용하여 오브젝트를 수동으로 삭제하는 것이 권장되는 방식인데, 무엇이 삭제되는지에 대해 더 명확하게 나타내므로 사용자가 의도하지 않게 무언가를 삭제할 가능성이 작아지기 때문이다.

kubectl delete -f <파일명>

대안: kubectl apply -f <디렉터리/> --prune -l your=레이블

무엇을 하는지 파악하는 경우에만 이를 사용한다.

kubectl delete에 대한 대안으로, 디렉터리로부터 구성 파일이 삭제된 후에 삭제될 오브젝트를 식별하기 위해 kubectl apply를 사용할 수 있다. --prune을 사용하여 적용하면 일련의 레이블의 집합과 일치하는 모든 오브젝트에 대해API 서버에 쿼리하고, 반환된 활성 오브젝트 구성을 오브젝트 구성 파일에 일치시키려고 시도한다. 오브젝트가 쿼리에 일치하고, 해당 디렉터리 내 구성 파일이 없고 last-applied-configuration어노테이션이 있는 경우, 삭제된다.

kubectl apply -f <디렉터리/> --prune -l <레이블>

오브젝트 확인 방법

활성 오브젝트의 구성을 확인하기 위해 -o yaml과 함께 kubectl get을 사용할 수 있다.

kubectl get -f <파일명|url> -o yaml

어떻게 apply가 차이를 계산하고 변경을 병합하는가

kubectl apply가 하나의 오브젝트에 대한 활성 구성을 업데이트할 때, API 서버에 패치 요청을 보냄으로써 그것을 수행한다. 그 패치는 활성 오브젝트 구성의 특정 필드에 대한 범위의 업데이트로 한정한다. kubectl apply 커맨드는 구성 파일, 활성 구성, 그리고 활성 구성에 저장된 last-applied-configuration어노테이션을 사용하여 이 패치 요청을 계산한다.

패치 계산 병합

kubectl apply 명령은 kubectl.kubernetes.io/last-applied-configuration 어노테이션에 구성 파일의 내용을 기록한다. 이것은 구성 파일로부터 제거되었고 활성 구성으로부터 지워질 필요가 있는 필드를 확인하는 데 사용된다. 다음은 어떤 필드가 삭제 또는 설정돼야 하는지 계산하기 위해 사용되는 단계이다.

  1. 삭제할 필드를 계산한다. 이것은 last-applied-configuration 내 존재하고 구성 파일로부터 유실된 필드이다.
  2. 추가 또는 설정되어야 할 필드를 계산한다. 이것은 활성 구성과 불일치하는 값을 가지는 구성 파일 내 존재하는 필드이다.

다음은 예시이다. 디플로이먼트 오브젝트에 대한 구성 파일이라고 가정한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # update the image
        ports:
        - containerPort: 80

또한, 이것은 동일한 디플로이먼트 오브젝트에 대한 활성 구성이라고 가정한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # note that the annotation does not contain replicas
    # because it was not updated through apply
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # written by scale
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

다음은 kubectl apply에 의해 수행될 병합 계산이다.

  1. last-applied-configuration으로부터 값을 읽어 구성 파일의 값과 비교하여 삭제할 필드를 계산한다. last-applied-configuration에 보이는 것과는 무관하게 로컬의 오브젝트 구성 파일 내 null이라고 명시적으로 설정된 필드를 지운다. 이 예시에서, minReadySecondslast-applied-configuration 어노테이션 내 나타나지만, 구성 파일 내에는 보여지지 않는다. 조치: 활성 구성으로부터 minReadySeconds을 지운다.
  2. 구성 파일로부터 값을 읽어 활성 구성 내 값과 비교하여 설정할 필드를 계산한다. 이 예시에서, 구성 파일 내 image 값은 활성 구성 내 값과 불일치한다. 조치: 활성 구성 내 image 값을 설정한다.
  3. 구성 파일의 값과 일치시키기 위해 last-applied-configuration 어노테이션을 설정한다.
  4. 1, 2, 3으로부터의 결과를 API 서버에 단일 패치 요청으로 병합한다.

다음은 병합의 결과인 활성 구성이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.11.9,
    # but does not contain the updated replicas to 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  selector:
    matchLabels:
      # ...
      app: nginx
  replicas: 2 # Set by `kubectl scale`.  Ignored by `kubectl apply`.
  # minReadySeconds cleared by `kubectl apply`
  # ...
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

어떻게 상이한 필드 타입이 병합되는가

구성 파일 내 특정 필드가 필드의 타입에 따라 어떻게 활성 구성과 함께 병합되는가. 여러 가지 필드 타입이 있다.

  • 기본(primitives): 문자열, 숫자 또는 불리언 타입의 필드. 예를 들어, imagereplicas는 기본 필드다. 조치: 교체.

  • , 또한 오브젝트 라 칭함: 맵 타입 또는 서브필드를 포함하는 복합 타입의 필드. 예를 들어, 레이블, 어노테이션,스펙메타데이터는 모두 맵이다. 조치: 구성요소 또는 서브필드 병합.

  • 리스트: 기본타입 또는 맵이 될 수 있는 아이템의 리스트를 포함하는 필드. 예를 들어, 컨테이너, 포트, 그리고 args는 리스트다. 조치: 다양함.

kubectl apply가 맵 또는 리스트 필드를 업데이트하는 경우, 일반적으로 전체 필드를 교체하는 대신, 개별 부 구성요소를 업데이트한다, 예를 들어, 디플로이먼트에 대한 spec을 병합할 경우, 전체 spec이 교체되지 않는다. 대신 replicas와 같은 spec의 서브필드가 비교되고 병합된다.

기본 필드에 대한 변경사항 병합하기

기본 필드는 교체되거나 지워진다.

Field in object configuration file Field in live object configuration Field in last-applied-configuration Action
Yes Yes - 구성 파일 값 활성으로 설정.
Yes No - 활성을 로컬 구성으로 설정.
No - Yes 활성 구성으로부터 지움.
No - No 아무것도 안함. 활성값 유지.

맵 필드에 변경사항 병합하기

맵을 요청하는 필드는 서브필드의 각각 또는 맵의 구성요소를 비교함으로써 병합된다.

Key in object configuration file Key in live object configuration Field in last-applied-configuration Action
Yes Yes - 서브필드 값 비교.
Yes No - 활성을 로컬 구성으로 설정.
No - Yes 활성 구성으로부터 삭제.
No - No 아무것도 안함. 활성값 유지.

타입 리스트의 필드에 대한 변경사항 병합하기

리스트에 대한 변경사항을 병합하는 것은 세 가지 전략 중 하나를 사용한다.

  • 구성요소가 모두 기본형인 경우 리스트를 교체한다.
  • 복합 구성요소의 리스트에서 개별 구성요소를 병합한다.
  • 기초 구성요소의 리스트를 병합한다.

전략에 대한 선택은 필드별로 이루어진다.

구성요소가 모두 기본형인 경우 리스트 교체

기초 필드와 동일한 리스트로 취급한다. 전체 리스트를 교체 또는 삭제한다. 이것은 순서를 유지한다.

예시: 파드 내 컨테이너의 args 필드를 업데이트하기 위해 kubectl apply를 사용한다. 이것은 활성 구성 내 args의 값을 구성 파일 내 값으로 설정한다. 활성 구성에 추가했던 이전의 모든 args구성요소들은 유실된다. 구성 파일 내 정의한 args 구성요소의 순서는 활성 구성 내 유지된다.

# last-applied-configuration value
    args: ["a", "b"]

# configuration file value
    args: ["a", "c"]

# live configuration
    args: ["a", "b", "d"]

# result after merge
    args: ["a", "c"]

설명: 병합은 새로운 리스트 값으로 구성 파일 값을 사용했다.

복합 구성요소 리스트에 대한 개별 구성요소 병합

리스트를 맵으로 취급하고 각 구성요소의 특정 필드를 키로 취급한다. 개별 구성요소를 추가, 삭제, 또는 업데이트 한다. 이것은 순서를 보존하지 않는다.

이 병합 전략은 각 필드에 patchMergeKey라 칭하는 특별한 태그를 사용한다. patchMergeKey는 쿠버네티스 소스 코드: types.go 의 각 필드에 대해 정의한다. 맵 리스트를 병합할 때, 주어진 구성요소에 대한 patchMergeKey로 지정한 필드는 해당 구성요소에 대한 맵키와 같이 사용된다.

예시: kubectl apply를 사용하여 PodSpec에 대한 containers필드를 업데이트한다.
이렇게 하면 각 구성요소가 name별로 키로 되어 있는 맵인 것처럼 리스트를 병합한다.

# last-applied-configuration value
    containers:
    - name: nginx
      image: nginx:1.10
    - name: nginx-helper-a # key: nginx-helper-a; will be deleted in result
      image: helper:1.3
    - name: nginx-helper-b # key: nginx-helper-b; will be retained
      image: helper:1.3

# configuration file value
    containers:
    - name: nginx
      image: nginx:1.10
    - name: nginx-helper-b
      image: helper:1.3
    - name: nginx-helper-c # key: nginx-helper-c; will be added in result
      image: helper:1.3

# live configuration
    containers:
    - name: nginx
      image: nginx:1.10
    - name: nginx-helper-a
      image: helper:1.3
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"] # Field will be retained
    - name: nginx-helper-d # key: nginx-helper-d; will be retained
      image: helper:1.3

# result after merge
    containers:
    - name: nginx
      image: nginx:1.10
      # Element nginx-helper-a was deleted
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"] # Field was retained
    - name: nginx-helper-c # Element was added
      image: helper:1.3
    - name: nginx-helper-d # Element was ignored
      image: helper:1.3

설명:

  • 구성 파일에 "nginx-helper-a"라는 이름을 가진 컨테이너가 나타나지 않았기 때문에 "nginx-helper-a"라는 컨테이너는 삭제되었다.
  • "nginx-helper-b"라는 컨테이너는 활성 구성에 args
    대한 변경사항을 유지했다. kubectl apply
    필드 값이 다름에도 불구하고(구성 파일에 args가 없음) 활성 구성에
    "nginx-helper-b"가 구성 파일과 동일한 "nginx-helper-b"임을 식별할 수 있었다. 이것은 patchMergeKey 필드 값(이름)이 둘 다 같았기 때문이다..
  • "nginx-helper-c"라는 이름의 컨테이너가 활성 구성에 나타나지 않았지만, 구성 파일에 그 이름을 가진 컨테이너가 나타났기 때문에 추가되었다.
  • last-applied-configuration에 그 이름을 가진 구성요소가 없었기 때문에 "nginx-helper-d"라는 이름의 컨테이너는 유지되었다.

기초 구성요소 리스트 병합

쿠버네티스 1.5로부터 기초 구성요소 병합하기는 지원되지 않는다.

기본 필드값

오브젝트가 생성될 때 값이 지정되지 않는 경우, API 서버는 활성 구성 내 특정 필드를 기본값으로 설정한다.

다음은 디플로이먼트에 대한 구성 파일이다. 파일에는 strategy가 지정되지 않았다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

kubectl apply를 사용하여 오브젝트를 생성한다.

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

kubectl get을 사용하여 활성 구성을 출력한다.

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

출력은 API 서버가 활성 구성 내 여러 필드를 기본값으로 설정한 것을 보여준다. 이 필드들은 구성 파일에 지정되지 않았다.

apiVersion: apps/v1
kind: Deployment
# ...
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  replicas: 1 # defaulted by apiserver
  strategy:
    rollingUpdate: # defaulted by apiserver - derived from strategy.type
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate # defaulted by apiserver
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        imagePullPolicy: IfNotPresent # defaulted by apiserver
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP # defaulted by apiserver
        resources: {} # defaulted by apiserver
        terminationMessagePath: /dev/termination-log # defaulted by apiserver
      dnsPolicy: ClusterFirst # defaulted by apiserver
      restartPolicy: Always # defaulted by apiserver
      securityContext: {} # defaulted by apiserver
      terminationGracePeriodSeconds: 30 # defaulted by apiserver
# ...

패치 요청에서, 패치 요청의 부분으로서 명시적으로 지워지지 않은 경우 기본 처리된 필드는 다시 기본으로 설정되지 않는다. 이것은 다른 필드에 대한 값에 따라 기본 처리된 필드에 대해 예상하지 못한 동작을 유발할 수 있다. 다른 필드가 나중에 변경되면, 그로부터 기본 처리된 것이 명시적으로 지워지지 않은 한 업데이트되지 않을 것이다.

이러한 사유로, 의도한 값이 서버의 기본값과 일치하더라도, 서버에 의해 기본 처리된 특정 필드는 구성 파일 내 명시적으로 정의할 것을 권고한다. 이렇게 하면 서버에 의해 다시 기본 처리되지 않게 될 충돌하는 값을 보다 쉽게 인식할 수 있도록 해준다.

Example:

# last-applied-configuration
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# configuration file
spec:
  strategy:
    type: Recreate # updated value
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# live configuration
spec:
  strategy:
    type: RollingUpdate # defaulted value
    rollingUpdate: # defaulted value derived from type
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# result after merge - ERROR!
spec:
  strategy:
    type: Recreate # updated value: incompatible with rollingUpdate
    rollingUpdate: # defaulted value: incompatible with "type: Recreate"
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

설명:

  1. 사용자가 strategy.type을 정의하지 않고 디플로이먼트를 생성한다.
  2. 서버는 strategy.typeRollingUpdate로 기본 설정하고 strategy.rollingUpdate값을 기본 값으로 처리한다.
  3. 사용자가 strategy.typeRecreate로 변경한다. 서버에서 해당 값이 삭제될 거라 예상하지만 strategy.rollingUpdate값은 기본값으로 남아 있다.
    strategy.rollingUpdate값이 처음에 구성 파일에서 지정되었다면, 이것을 삭제해야 한다는 것이 더 분명했을 것이다.
  4. strategy.rollingUpdate가 지워지지 않았기 때문에 적용은 실패한다. strategy.rollingupdate 필드는 Recreatestrategy.type으로 정의될 수 없다.

권고: 이들 필드는 오브젝트 구성 파일 내 명시적으로 정의돼야 한다.

  • 디플로이먼트, 스테이트풀셋, 잡, 데몬셋, 레플리카셋 및 레플리케이션컨트롤러와 같은 워크로드에 대한 셀렉터와 파드템플릿 레이블
  • 디플로이먼트 롤아웃 전략

서버 기본 필드 또는 다른 작성자에 의해 설정된 필드 지우는 방법

구성 파일 내 나타나지 않는 필드는 그 값을 null로 설정하고 나서 구성 파일을 적용함으로써 지워질 수 있다. 서버가 기본 값을 할당했던 필드에 대해서, 이는 다시 기본 값을 할당하도록 한다.

구성 파일과 직접 명령형 작성자 간의 필드 소유권을 변경시키는 방법

개별 오브젝트 필드를 변경시키는 데 사용해야 하는 유일한 방법은 다음과 같다.

  • kubectl apply를 사용한다.
  • 구성 파일을 수정하지 않고 활성 구성을 직접 작성한다. 예를 들어, kubectl scale을 사용한다.

직접 명령형 작성자에서 구성 파일로 소유자 변경하기

구성 파일에 필드를 추가한다. 해당 필드의 경우 kubectl apply를 거치지 않는 활성 구성에 대해 직접 업데이트를 적용하지 않는다.

구성 파일에서 직접 명령형 작성자로 소유자 변경하기

쿠버네티스 1.5로부터 구성 파일에서 명령형 작성자로 소유권을 변경하는데 수동 단계 필요하다.

  • 구성 파일에서 필드를 제거한다.
  • 활성 오브젝트 상의 kubectl.kubernetes.io/last-applied-configuration 어노테이션에서 필드를 제거한다.

관리 방법 변경하기

쿠버네티스 오브젝트는 한 번에 오직 하나의 방법을 사용하여 관리돼야 한다. 하나의 방법에서 다른 방법으로 전환하는 것은 가능하나, 수동 프로세스이다.

명령형 커맨드 관리에서 오브젝트 구성으로 이전하기

명령형 커맨드 관리에서 오브젝트 구성으로 이전하는 것은 여러 수동 단계를 포함한다.

  1. 활성 오브젝트를 로컬 구성 파일로 내보낸다.

    kubectl get <종류>/<이름> -o yaml > <종류>_<이름>.yaml
    
  2. 구성 파일에서 수동으로 status 필드를 제거한다.

  3. 오브젝트의 kubectl.kubernetes.io/last-applied-configuration 어노테이션을 설정한다.

    kubectl replace --save-config -f <종류>_<이름>.yaml
    
  4. 오직 오브젝트를 관리하기 위해 kubectl apply를 사용하도록 프로세스를 변경한다.

명령형 오브젝트 구성에서 선언형 오브젝트 구성으로 이전하기

  1. 오브젝트의 kubectl.kubernetes.io/last-applied-configuration 어노테이션을 설정한다.

    kubectl replace --save-config -f <종류>_<이름>.yaml
    
  2. 오직 오브젝트를 관리하기 위해 kubectl apply를 사용하도록 프로세스를 변경한다.

컨트롤러 셀렉터와 파드템플릿 레이블 정의하기

권고되는 접근 방법은 다른 의미론적 의미를 가지지 않고 컨트롤러에 의해서만 사용되는 단일, 불변의 파드템플릿 레이블을 정의하는 것이다.

예시:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

다음 내용

4.4.2 - Kustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리

Kustomizekustomization 파일을 통해 쿠버네티스 오브젝트를 사용자가 원하는 대로 변경하는(customize) 독립형 도구이다.

1.14 이후로, kubectl도 kustomization 파일을 사용한 쿠버네티스 오브젝트의 관리를 지원한다. kustomization 파일을 포함하는 디렉터리 내의 리소스를 보려면 다음 명령어를 실행한다.

kubectl kustomize <kustomization_directory>

이 리소스를 적용하려면 kubectl apply--kustomize 또는 -k 플래그와 함께 실행한다.

kubectl apply -k <kustomization_directory>

시작하기 전에

kubectl을 설치한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

Kustomize 개요

Kustomize는 쿠버네티스 구성을 사용자 정의화하는 도구이다. 이는 애플리케이션 구성 파일을 관리하기 위해 다음 기능들을 가진다.

  • 다른 소스에서 리소스 생성
  • 리소스에 대한 교차 편집 필드 설정
  • 리소스 집합을 구성하고 사용자 정의

리소스 생성

컨피그맵과 시크릿은 파드와 같은 다른 쿠버네티스 오브젝트에서 사용되는 설정이나 민감한 데이터를 가지고 있다. 컨피그맵이나 시크릿의 실질적인 소스는 일반적으로 .properties 파일이나 ssh key 파일과 같은 것들은 클러스터 외부에 있다. Kustomize는 시크릿과 컨피그맵을 파일이나 문자열에서 생성하는 secretGeneratorconfigMapGenerator를 가지고 있다.

configMapGenerator

파일에서 컨피그맵을 생성하려면 configMapGenerator 내의 files 리스트에 항목을 추가한다. 다음은 하나의 .properties 파일에서 데이터 항목으로 컨피그맵을 생성하는 예제이다.

# application.properties 파일을 생성
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

생성된 컨피그맵은 다음 명령어로 검사할 수 있다.

kubectl kustomize ./

생성된 컨피그맵은 다음과 같다.

apiVersion: v1
data:
  application.properties: |
        FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g

env 파일에서 컨피그맵을 생성하려면, configMapGeneratorenvs 리스트에 항목을 추가한다. 다음은 .env 파일의 데이터 항목으로 컨피그맵을 생성하는 예시를 보여준다.

# .env 파일 생성
cat <<EOF >.env
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  envs:
  - .env
EOF

생성된 컨피그맵은 다음 명령어로 검사할 수 있다.

kubectl kustomize ./

생성된 컨피그맵은 다음과 같다.

apiVersion: v1
data:
    FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g

컨피그맵은 문자로된 키-값 쌍들로도 생성할 수 있다. 문자로된 키-값 쌍에서 컨피그맵을 생성하려면, configMapGenerator 내의 literals 리스트에 항목을 추가한다. 다음은 키-값 쌍을 데이터 항목으로 받는 컨피그맵을 생성하는 예제이다.

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
  literals:
  - FOO=Bar
EOF

생성된 컨피그맵은 다음 명령어로 확인할 수 있다.

kubectl kustomize ./

생성된 컨피그맵은 다음과 같다.

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-2-g2hdhfc6tk

디플로이먼트에서 생성된 컨피그맵을 사용하기 위해서는, configMapGenerator의 이름을 참조한다. Kustomize는 자동으로 해당 이름을 생성된 이름으로 교체할 것이다.

다음은 생성된 컨피그맵을 사용하는 디플로이먼트의 예시다.

# application.properties 파일을 생성한다.
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: example-configmap-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

컨피그맵과 디플로이먼트를 생성한다.

kubectl kustomize ./

생성된 디플로이먼트는 이름을 통해서 생성된 컨피그맵을 참조한다.

apiVersion: v1
data:
  application.properties: |
        FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-app
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: my-app
        name: app
        volumeMounts:
        - mountPath: /config
          name: config
      volumes:
      - configMap:
          name: example-configmap-1-g4hk9g2ff8
        name: config

secretGenerator

파일 또는 문자로된 키-값 쌍들로 시크릿을 생성할 수 있다. 파일에서 시크릿을 생성하려면 secretGenerator 내의 files 리스트에 항목을 추가한다. 다음은 파일을 데이터 항목으로 받는 시크릿을 생성하는 예제이다.

# password.txt 파일을 생성
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

생성된 시크릿은 다음과 같다.

apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-t2kt65hgtb
type: Opaque

문자로된 키-값 쌍으로 시크릿을 생성하려면, secretGenerator 내의 literals 리스트에 항목을 추가한다. 다음은 키-값 쌍을 데이터 항목으로 받는 시크릿을 생성하는 예제이다.

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
  literals:
  - username=admin
  - password=secret
EOF

생성된 시크릿은 다음과 같다.

apiVersion: v1
data:
  password: c2VjcmV0
  username: YWRtaW4=
kind: Secret
metadata:
  name: example-secret-2-t52t6g96d8
type: Opaque

컨피그맵과 유사하게, 생성된 시크릿도 secretGenerator의 이름을 참조함으로써 디플로이먼트에서 사용될 수 있다.

# password.txt 파일을 생성한다.
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: password
          mountPath: /secrets
      volumes:
      - name: password
        secret:
          secretName: example-secret-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

generatorOptions

생성된 컨피그맵과 시크릿은 콘텐츠 해시 접미사가 추가된다. 이는 콘텐츠가 변경될 때 새로운 컨피그맵 이나 시크릿이 생성되는 것을 보장한다. 접미사를 추가하는 동작을 비활성화하는 방법으로 generatorOptions를 사용할 수 있다. 그밖에, 생성된 컨피그맵과 시크릿에 교차 편집 옵션들을 지정해주는 것도 가능하다.

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
  literals:
  - FOO=Bar
generatorOptions:
  disableNameSuffixHash: true
  labels:
    type: generated
  annotations:
    note: generated
EOF

생성된 컨피그맵을 보려면 kubectl kustomize ./를 실행한다.

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  annotations:
    note: generated
  labels:
    type: generated
  name: example-configmap-3

교차 편집 필드 설정

프로젝트 내 모든 쿠버네티스 리소스에 교차 편집 필드를 설정하는 것은 꽤나 일반적이다. 교차 편집 필드를 설정하는 몇 가지 사용 사례는 다음과 같다.

  • 모든 리소스에 동일한 네임스페이스를 설정
  • 동일한 네임 접두사 또는 접미사를 추가
  • 동일한 레이블들을 추가
  • 동일한 어노테이션들을 추가

다음은 예제이다.

# deployment.yaml을 생성
cat <<EOF >./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
EOF

cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
  app: bingo
commonAnnotations:
  oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF

이 필드들이 디플로이먼트 리소스에 모두 설정되었는지 보려면 kubectl kustomize ./를 실행한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    oncallPager: 800-555-1212
  labels:
    app: bingo
  name: dev-nginx-deployment-001
  namespace: my-namespace
spec:
  selector:
    matchLabels:
      app: bingo
  template:
    metadata:
      annotations:
        oncallPager: 800-555-1212
      labels:
        app: bingo
    spec:
      containers:
      - image: nginx
        name: nginx

리소스 구성과 사용자 정의

프로젝트 내 리소스의 집합을 구성하여 이들을 동일한 파일이나 디렉터리 내에서 관리하는 것은 일반적이다. Kustomize는 서로 다른 파일들로 리소스를 구성하고 패치나 다른 사용자 정의를 이들에 적용하는 것을 제공한다.

구성

Kustomize는 서로 다른 리소스들의 구성을 지원한다. kustomization.yaml 파일 내 resources 필드는 구성 내에 포함하려는 리소스들의 리스트를 정의한다. resources 리스트 내에 리소스의 구성 파일의 경로를 설정한다. 다음 예제는 디플로이먼트와 서비스로 구성된 NGINX 애플리케이션이다.

# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# service.yaml 파일 생성
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

# 이들을 구성하는 kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

kubectl kustomize ./의 리소스에는 디플로이먼트와 서비스 오브젝트가 모두 포함되어 있다.

사용자 정의

패치는 리소스에 다른 사용자 정의를 적용하는 데 사용할 수 있다. Kustomize는 patchesStrategicMergepatchesJson6902를 통해 서로 다른 패치 메커니즘을 지원한다. patchesStrategicMerge는 파일 경로들의 리스트이다. 각각의 파일은 전략적 병합 패치로 분석될 수 있어야 한다. 패치 내부의 네임은 반드시 이미 읽혀진 리소스 네임과 일치해야 한다. 한 가지 일을 하는 작은 패치가 권장된다. 예를 들기 위해 디플로이먼트 레플리카 숫자를 증가시키는 하나의 패치와 메모리 상한을 설정하는 다른 패치를 생성한다.

# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# increase_replicas.yaml 패치 생성
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
EOF

# 다른 패치로 set_memory.yaml 생성
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-nginx
        resources:
          limits:
            memory: 512Mi
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF

디플로이먼트를 보려면 kubectl kustomize ./를 실행한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80
        resources:
          limits:
            memory: 512Mi

모든 리소스 또는 필드가 전략적 병합 패치를 지원하는 것은 아니다. 임의의 리소스 내 임의의 필드의 수정을 지원하기 위해, Kustomize는 patchesJson6902를 통한 JSON 패치 적용을 제공한다. Json 패치의 정확한 리소스를 찾기 위해, 해당 리소스의 group, version, kind, name이 kustomization.yaml 내에 명시될 필요가 있다. 예를 들면, patchesJson6902를 통해 디플로이먼트 오브젝트의 레플리카 개수를 증가시킬 수 있다.

# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# json 패치 생성
cat <<EOF > patch.yaml
- op: replace
  path: /spec/replicas
  value: 3
EOF

# kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml

patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-nginx
  path: patch.yaml
EOF

kubectl kustomize ./를 실행하여 replicas 필드가 갱신되었는지 확인한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80

패치 기능에 추가로 Kustomize는 패치를 생성하지 않고 컨테이너 이미지를 사용자 정의하거나 다른 오브젝트의 필드 값을 컨테이너에 주입하는 기능도 제공한다. 예를 들어 kustomization.yamlimages 필드에 신규 이미지를 지정하여 컨테이너에서 사용되는 이미지를 변경할 수 있다.

cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
  newName: my.image.registry/nginx
  newTag: 1.4.0
EOF

사용된 이미지가 갱신되었는지 확인하려면 kubectl kustomize ./를 실행한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: my.image.registry/nginx:1.4.0
        name: my-nginx
        ports:
        - containerPort: 80

가끔, 파드 내에서 실행되는 애플리케이션이 다른 오브젝트의 설정 값을 사용해야 할 수도 있다. 예를 들어, 디플로이먼트 오브젝트의 파드는 Env 또는 커맨드 인수로 해당 서비스 네임을 읽어야 한다고 하자. kustomization.yaml 파일에 namePrefix 또는 nameSuffix가 추가되면 서비스 네임이 변경될 수 있다. 커맨드 인수 내에 서비스 네임을 하드 코딩하는 것을 권장하지 않는다. 이 용도에서 Kustomize는 vars를 통해 containers에 서비스 네임을 삽입할 수 있다.

# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        command: ["start", "--host", "$(MY_SERVICE_NAME)"]
EOF

# service.yaml 파일 생성
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"

resources:
- deployment.yaml
- service.yaml

vars:
- name: MY_SERVICE_NAME
  objref:
    kind: Service
    name: my-nginx
    apiVersion: v1
EOF

kubectl kustomize ./를 실행하면 dev-my-nginx-001로 컨테이너에 삽입된 서비스 네임을 볼 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-my-nginx-001
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - command:
        - start
        - --host
        - dev-my-nginx-001
        image: nginx
        name: my-nginx

Base와 Overlay

Kustomize는 baseoverlay 의 개념을 가지고 있다. basekustomization.yaml 과 함께 사용되는 디렉터리다. 이는 사용자 정의와 관련된 리소스들의 집합을 포함한다. kustomization.yaml의 내부에 표시되는 base는 로컬 디렉터리이거나 원격 리포지터리의 디렉터리가 될 수 있다. overlaykustomization.yaml이 있는 디렉터리로 다른 kustomization 디렉터리들을 bases로 참조한다. base 는 overlay에 대해서 알지 못하며 여러 overlay들에서 사용될 수 있다. 한 overlay는 다수의 base들을 가질 수 있고, base들에서 모든 리소스를 구성할 수 있으며, 이들의 위에 사용자 정의도 가질 수 있다.

다음은 base에 대한 예이다.

# base를 가지는 디렉터리 생성
mkdir base
# base/deployment.yaml 생성
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
EOF

# base/service.yaml 파일 생성
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF
# base/kustomization.yaml 생성
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

이 base는 다수의 overlay에서 사용될 수 있다. 다른 namePrefix 또는 다른 교차 편집 필드들을 서로 다른 overlay에 추가할 수 있다. 다음 예제는 동일한 base를 사용하는 두 overlay들이다.

mkdir dev
cat <<EOF > dev/kustomization.yaml
bases:
- ../base
namePrefix: dev-
EOF

mkdir prod
cat <<EOF > prod/kustomization.yaml
bases:
- ../base
namePrefix: prod-
EOF

Kustomize를 이용하여 오브젝트를 적용/확인/삭제하는 방법

kustomization.yaml에서 관리되는 리소스를 인식하려면 kubectl 명령어에 --kustomize-k를 사용한다. -k는 다음과 같이 kustomization 디렉터리를 가리키고 있어야 한다는 것을 주의한다.

kubectl apply -k <kustomization directory>/

다음 kustomization.yaml이 주어지고,

# deployment.yaml 파일 생성
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# kustomization.yaml 생성
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
  app: my-nginx
resources:
- deployment.yaml
EOF

디플로이먼트 오브젝트 dev-my-nginx를 적용하려면 다음 명령어를 실행한다.

> kubectl apply -k ./
deployment.apps/dev-my-nginx created

디플로이먼트 오브젝트 dev-my-nginx를 보려면 다음 명령어들 중에 하나를 실행한다.

kubectl get -k ./
kubectl describe -k ./

다음 명령을 실행해서 디플로이먼트 오브젝트 dev-my-nginx 를 매니페스트가 적용된 경우의 클러스터 상태와 비교한다.

kubectl diff -k ./

디플로이먼트 오브젝트 dev-my-nginx를 삭제하려면 다음 명령어를 실행한다.

> kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted

Kustomize 기능 리스트

필드 유형 설명
namespace string 모든 리소스에 네임스페이스 추가
namePrefix string 모든 리소스 네임에 이 필드의 값이 접두사로 추가된다
nameSuffix string 모든 리소스 네임에 이 필드의 값이 접미사로 추가된다
commonLabels map[string]string 모든 리소스와 셀렉터에 추가될 레이블
commonAnnotations map[string]string 모든 리소스에 추가될 어노테이션
resources []string 이 리스트 내 각각의 항목은 반드시 존재하는 리소스 구성 파일로 해석되어야 한다.
configMapGenerator []ConfigMapArgs 이 리스트의 각 항목은 컨피그맵을 생성한다.
secretGenerator []SecretArgs 이 리스트의 각 항목은 시크릿을 생성한다.
generatorOptions GeneratorOptions 모든 컨피그맵 및 시크릿 생성자(generator)의 동작을 수정한다.
bases []string 이 리스트 내 각각의 항목은 kustomization.yaml 파일을 가지는 디렉터리로 해석되어야 한다.
patchesStrategicMerge []string 이 리스트 내 각각의 항목은 쿠버네티스 오브젝트의 전략적 병합 패치로 해석되어야 한다.
patchesJson6902 []Patch 이 리스트 내 각각의 항목은 쿠버네티스 오브젝트와 Json 패치로 해석되어야 한다.
vars []Var 각각의 항목은 한 리소스의 필드에서 텍스트를 캡쳐한다.
images []Image 각각의 항목은 패치를 생성하지 않고 하나의 이미지에 대한 name, tags 그리고/또는 digest를 수정한다.
configurations []string 이 리스트 내 각각의 항목은 Kustomize 변환 설정을 포함하는 파일로 해석되어야 한다.
crds []string 이 리스트 내 각각의 항목은 쿠버네티스 타입에 대한 OpenAPI 정의 파일로 해석되어야 한다.

다음 내용

4.4.3 - 명령형 커맨드를 이용한 쿠버네티스 오브젝트 관리하기

쿠버네티스 오브젝트는 kubectl 커맨드 라인 툴 속에 내장된 명령형 커맨드를 이용함으로써 바로 신속하게 생성, 업데이트 및 삭제할 수 있다. 이 문서는 어떻게 커맨드가 구성되어 있으며, 이를 사용하여 활성 오브젝트를 어떻게 관리하는 지에 대해 설명한다.

시작하기 전에

kubectl을 설치한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

트레이드 오프

kubectl툴은 3가지 종류의 오브젝트 관리를 지원한다.

  • 명령형 커맨드
  • 명령형 오브젝트 구성
  • 선언형 오브젝트 구성

각 종류별 오브젝트 관리의 장점과 단점에 대한 논의는 쿠버네티스 오브젝트 관리 를 참고한다.

오브젝트 생성 방법

kubectl 툴은 가장 일반적인 오브젝트 타입을 생성하는데 동사 형태 기반의 커맨드를 지원한다. 쿠버네티스 오브젝트 타입에 익숙하지 않은 사용자가 인지할 수 있도록 커맨드 이름이 지어졌다.

  • run: 컨테이너를 실행할 새로운 파드를 생성한다.
  • expose: 파드에 걸쳐 트래픽을 로드 밸런스하도록 새로운 서비스 오브젝트를 생성한다.
  • autoscale: 디플로이먼트와 같이, 하나의 컨트롤러에 대해 자동으로 수평적 스케일이 이루어 지도록 새로운 Autoscaler 오브젝트를 생성한다.

또한 kubectl 툴은 오브젝트 타입에 의해 구동되는 생성 커맨드를 지원한다. 이러한 커맨드는 더 많은 오브젝트 타입을 지원해주며 그 의도하는 바에 대해 보다 명확하게 해주지만, 사용자가 생성하고자 하는 오브젝트 타입에 대해 알 수 있도록 해야 한다.

  • create <오브젝트 타입> [<서브 타입>] <인스턴스명>

일부 오브젝트 타입은 create 커맨드 내 정의할 수 있는 서브 타입을 가진다. 예를 들어, 서비스 오브젝트는 ClusterIP, LoadBalancer 및 NodePort 등을 포함하는 여러 서브 타입을 가진다, 다음은 NodePort 서브 타입을 통해 서비스를 생성하는 예제이다.

kubectl create service nodeport <사용자 서비스 명칭>

이전 예제에서, create service nodeport 커맨드는 create service 커맨드의 서브 커맨드라고 칭한다.

-h 플래그를 사용하여 서브 커맨드에 의해 지원되는 인수 및 플래그를 찾아 볼 수 있다.

kubectl create service nodeport -h

오브젝트 업데이트 방법

kubectl 커맨드는 일반적인 몇몇의 업데이트 작업을 위해 동사 형태 기반의 커맨드를 지원한다. 이 커맨드는 쿠버네티스 오브젝트에 익숙하지 않은 사용자가 설정되어야 하는 특정 필드를 모르는 상태에서도 업데이트를 수행할 수 있도록 이름 지어졌다.

  • scale: 컨트롤러의 레플리카 수를 업데이트 함으로써 파드를 추가 또는 제거하는 컨트롤러를 수평적으로 스케일한다.
  • annotate: 오브젝트로부터 어노테이션을 추가 또는 제거한다.
  • label: 오브젝트에서 레이블을 추가 또는 제거한다.

kubectl 커맨드는 또한 오브젝트 측면에서 구동되는 업데이트 커맨드를 지원한다. 이 측면의 설정은 다른 오브젝트 타입에 대한 다른 필드를 설정 할 수도 있다.

  • set <field>: 오브젝트의 측면을 설정한다.

kubectl 툴은 활성 오브젝트를 직접 업데이트하기 위해 추가적인 방법을 지원하지만, 쿠버네티스 오브젝트 스키마에 대한 추가적인 이해를 요구한다.

  • edit: 편집기에서 구성을 열어 활성 오브젝트에 대한 원래 그대로의 구성을 바로 편집한다.
  • patch: 패치 문자열를 사용하여 활성 오브젝트를 바로 편집한다. 패치 문자열에 대한 보다 자세한 정보를 보려면 API 규정에서 패치 섹션을 참고한다.

오브젝트 삭제 방법

클러스터에서 오브젝트를 삭제하기 위해 delete 커맨드을 사용할 수 있다.

  • delete <타입>/<이름>
kubectl delete deployment/nginx

오브젝트 확인 방법

오브젝트에 대한 정보를 출력하는 몇 가지 커맨드가 있다.

  • get: 일치하는 오브젝트에 대한 기본 정보를 출력한다. 옵션 리스트를 확인하기 위해 get -h를 사용한다.
  • describe: 일치하는 오브젝트에 대해 수집한 상세한 정보를 출력한다.
  • logs: 파드에서 실행 중인 컨테이너에 대한 stdout과 stderr를 출력한다.

생성 전 오브젝트 수정을 위해 set 커맨드 사용하기

create 커맨드에 사용할 수 있는 플래그가 없는 몇 가지 오브젝트 필드가 있다. 이러한 경우, 오브젝트 생성 전에 필드에 대한 값을 정의하기 위해 setcreate을 조합해서 사용할 수 있다. 이는 set 커맨드에 create 커맨드의 출력을 파이프 함으로써 수행할 수 있다. 다음은 관련 예제이다.

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
  1. kubectl create service -o yaml --dry-run=client 커맨드는 서비스에 대한 구성을 생성하지만, 이를 쿠버네티스 API 서버에 전송하는 대신 YAML 형식으로 stdout에 출력한다.
  2. kubectl set selector --local -f - -o yaml 커맨드는 stdin으로부터 구성을 읽어, YAML 형식으로 stdout에 업데이트된 구성을 기록한다.
  3. kubectl create -f - 커맨드는 stdin을 통해 제공된 구성을 사용하여 오브젝트를 생성한다.

생성 전 오브젝트 수정을 위해 --edit 사용하기

생성 전에 오브젝트에 임의의 변경을 가하기 위해 kubectl create --edit 을 사용할 수 있다. 다음은 관련 예제이다.

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
  1. kubectl create service 커맨드는 서비스에 대한 구성을 생성하고 이를 /tmp/srv.yaml에 저장한다.
  2. kubectl create --edit 커맨드는 오브젝트를 생성하기 전에 편집을 위해 구성파일을 열어준다.

다음 내용

4.4.4 - 구성파일을 이용한 명령형 쿠버네티스 오브젝트 관리

쿠버네티스 오브젝트는 YAML 또는 JSON으로 작성된 오프젝트 구성파일과 함께 kubectl 커맨드 라인 툴을 이용하여 생성, 업데이트 및 삭제할 수 있다. 이 문서는 구성파일을 이용하여 어떻게 오브젝트를 정의하고 관리할 수 있는지에 대해 설명한다.

시작하기 전에

kubectl을 설치한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

트레이드 오프

kubectl 툴은 3가지 종류의 오브젝트 관리를 지원한다.

  • 명령형 커맨드
  • 명령형 오브젝트 구성
  • 선언형 오브젝트 구성

각 종류별 오브젝트 관리의 장점과 단점에 대한 논의는 쿠버네티스 오브젝트 관리를 참고한다.

오브젝트 생성 방법

구성파일로부터 오브젝트를 생성하기 위해 kubectl create -f를 사용할 수 있다. 보다 상세한 정보는 쿠버네티스 API 참조를 참조한다.

  • kubectl create -f <파일명|url>

오브젝트 업데이트 방법

구성파일에 따라 활성 오브젝트를 업데이트하기 위해 kubectl replace -f 를 사용할 수 있다.

  • kubectl replace -f <파일명|url>

오브젝트 삭제 방법

구성파일에 정의한 오브젝트를 삭제하기 위해 kubectl delete -f를 사용할 수 있다.

  • kubectl delete -f <파일명|url>

오브젝트 확인 방법

구성파일에 정의한 오브젝트에 관한 정보 확인을 위해 kubectl get -f 명령을 사용할 수 있다.

  • kubectl get -f <파일명|url> -o yaml

-o yaml 플래그는 전체 오브젝트 구성이 출력되도록 정의한다. 옵션의 리스트를 확인하기 위해서는 kubectl get -h를 사용한다.

제약사항

create, replace, 그리고 delete 명령은 각 오브젝트의 구성이 그 구성파일 내에 완전하게 정의되고 기록되어질 경우 잘 동작한다. 그러나 활성 오브젝트가 업데이트 되고, 구성파일 안에 병합되지 않으면, 업데이트 내용은 다음번 replace가 실행될 때 삭제될 것이다. 이는 HorizontalPodAutoscaler와 같은 컨트롤러가 활성 오브젝트를 직접적으로 업데이트하도록 할 경우 발생한다. 여기 예시가 있다.

  1. 구성파일로부터 오브젝트를 생성할 경우
  2. 또 다른 소스가 일부 필드를 변경함으로써 오브젝트가 업데이트 되는 경우
  3. 구성파일로부터 오브젝트를 대체할 경우. 스텝 2에서의 다른 소스에 의해 이루어진 변경은 유실된다.

동일 오브젝트에 대해 여러 명의 작성자들로부터의 지원이 필요한 경우, 오브젝트를 관리하기 위해 kubectl apply를 사용할 수 있다.

구성 저장 없이 URL로부터 오브젝트 생성과 편집하기

구성파일에 대한 URL을 가진다고 가정해보자. kubectl create --edit을 사용하여 오브젝트가 생성되기 전에 구성을 변경할 수 있다. 이는 독자가 수정할 수 있는 구성파일을 가르키는 튜토리얼과 작업에 특히 유용하다.

kubectl create -f <url> --edit

명령형 커맨드에서 명령형 오브젝트 구성으로 전환하기

령형 커맨드에서 명령형 오브젝트 구성으로 전환하기 위해 몇 가지 수동 단계를 포함한다.

  1. 다음과 같이 활성 오브젝트를 로컬 오브젝트 구성파일로 내보낸다.

    kubectl get <종류>/<이름> -o yaml > <종류>_<이름>.yaml
    
  2. 수동으로 오브젝트 구성파일에서 상태 필드를 제거한다.

  3. 이후 오브젝트 관리를 위해, replace만 사용한다.

    kubectl replace -f <종류>_<이름>.yaml
    

컨트롤러 셀렉터와 PodTemplate 레이블 삭제하기

권고되는 접근방법은 다른 의미론적 의미가 없는 컨트롤러 셀렉터의 의해서만 사용되는 단일, 불변의 PodTemplate 레이블로 정의하는 것이다.

레이블 예시:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

다음 내용

4.5 - 시크릿(Secret) 관리

시크릿을 사용하여 기밀 설정 데이터 관리.

4.5.1 - kubectl을 사용한 시크릿 관리

kubectl 커맨드를 사용하여 시크릿 오브젝트를 생성.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

시크릿 생성

시크릿에는 파드가 데이터베이스에 접근하는 데 필요한 사용자 자격 증명이 포함될 수 있다. 예를 들어 데이터베이스 연결 문자열은 사용자 이름과 암호로 구성된다. 사용자 이름은 로컬 컴퓨터의 ./username.txt 파일에, 비밀번호는 ./password.txt 파일에 저장할 수 있다.

echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

이 명령에서 -n 플래그는 생성된 파일의 텍스트 끝에 추가 개행 문자가 포함되지 않도록 해 준다. 이는 kubectl이 파일을 읽고 내용을 base64 문자열로 인코딩할 때 개행 문자도 함께 인코딩될 수 있기 때문에 중요하다.

kubectl create secret 명령은 이러한 파일들을 시크릿으로 패키징하고 API 서버에 오브젝트를 생성한다.

kubectl create secret generic db-user-pass \
  --from-file=./username.txt \
  --from-file=./password.txt

출력은 다음과 유사하다.

secret/db-user-pass created

기본 키 이름은 파일 이름이다. 선택적으로 --from-file=[key=]source를 사용하여 키 이름을 설정할 수 있다. 예제:

kubectl create secret generic db-user-pass \
  --from-file=username=./username.txt \
  --from-file=password=./password.txt

파일에 포함하는 암호 문자열에서 특수 문자를 이스케이프하지 않아도 된다.

--from-literal=<key>=<value> 태그를 사용하여 시크릿 데이터를 제공할 수도 있다. 이 태그는 여러 키-값 쌍을 제공하기 위해 두 번 이상 지정할 수 있다. $, \, *, =!와 같은 특수 문자는 shell에 해석하고 처리하기 때문에 이스케이프할 필요가 있다.

대부분의 셸에서 암호를 이스케이프하는 가장 쉬운 방법은 암호를 작은따옴표(')로 둘러싸는 것이다. 예를 들어, 비밀번호가 S!B\*d$zDsb=인 경우, 다음 커맨드를 실행한다.

kubectl create secret generic db-user-pass \
  --from-literal=username=devuser \
  --from-literal=password='S!B\*d$zDsb='

시크릿 확인

시크릿이 생성되었는지 확인한다.

kubectl get secrets

출력은 다음과 유사하다.

NAME                  TYPE                                  DATA      AGE
db-user-pass          Opaque                                2         51s

다음 명령을 실행하여 시크릿에 대한 상세 사항을 볼 수 있다.

kubectl describe secrets/db-user-pass

출력은 다음과 유사하다.

Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password:    12 bytes
username:    5 bytes

kubectl getkubectl describe 명령은 기본적으로 시크릿의 내용을 표시하지 않는다. 이는 시크릿이 실수로 노출되거나 터미널 로그에 저장되는 것을 방지하기 위한 것이다.

시크릿 디코딩

생성한 시크릿을 보려면 다음 명령을 실행한다.

kubectl get secret db-user-pass -o jsonpath='{.data}'

출력은 다음과 유사하다.

{"password":"MWYyZDFlMmU2N2Rm","username":"YWRtaW4="}

이제 password 데이터를 디코딩할 수 있다.

echo 'MWYyZDFlMmU2N2Rm' | base64 --decode

출력은 다음과 유사하다.

1f2d1e2e67df

삭제

생성한 시크릿을 삭제하려면 다음 명령을 실행한다.

kubectl delete secret db-user-pass

다음 내용

4.5.2 - 환경 설정 파일을 사용하여 시크릿을 관리

환경 설정 파일을 사용하여 시크릿 오브젝트를 생성.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

환경 설정 파일 생성

먼저 새 파일에 JSON 이나 YAML 형식으로 시크릿(Secret)에 대한 상세 사항을 기록하고, 이 파일을 이용하여 해당 시크릿 오브젝트를 생성할 수 있다. 이 시크릿 리소스에는 datastringData 의 두 가지 맵이 포함되어 있다. data 필드는 base64로 인코딩된 임의의 데이터를 기입하는 데 사용된다. stringData 필드는 편의를 위해 제공되며, 이를 사용해 시크릿 데이터를 인코딩되지 않은 문자열로 기입할 수 있다. datastringData은 영숫자, -, _ 그리고 .로 구성되어야 한다.

예를 들어 시크릿에 data 필드를 사용하여 두 개의 문자열을 저장하려면 다음과 같이 문자열을 base64로 변환한다.

echo -n 'admin' | base64

출력은 다음과 유사하다.

YWRtaW4=
echo -n '1f2d1e2e67df' | base64

출력은 다음과 유사하다.

MWYyZDFlMmU2N2Rm

다음과 같이 시크릿 구성 파일을 작성한다.

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

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

특정 시나리오의 경우 stringData 필드를 대신 사용할 수 있다. 이 필드를 사용하면 base64로 인코딩되지 않은 문자열을 시크릿에 직접 넣을 수 있으며, 시크릿이 생성되거나 업데이트될 때 문자열이 인코딩된다.

이에 대한 실제적인 예로, 시크릿을 사용하여 구성 파일을 저장하는 애플리케이션을 배포하면서, 배포 프로세스 중에 해당 구성 파일의 일부를 채우려는 경우를 들 수 있다.

예를 들어 애플리케이션에서 다음 구성 파일을 사용하는 경우:

apiUrl: "https://my.api.com/api/v1"
username: "<user>"
password: "<password>"

다음 정의를 사용하여 이를 시크릿에 저장할 수 있다.

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: <user>
    password: <password>    

시크릿 오브젝트 생성

kubectl apply를 이용하여 시크릿 오브젝트를 생성한다.

kubectl apply -f ./secret.yaml

출력은 다음과 유사하다.

secret/mysecret created

시크릿 확인

stringData 필드는 쓰기 전용 편의 필드이다. 시크릿을 조회할 때 절대 출력되지 않는다. 예를 들어 다음 명령을 실행하는 경우:

kubectl get secret mysecret -o yaml

출력은 다음과 유사하다.

apiVersion: v1
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque

kubectl getkubectl describe 명령은 기본적으로 시크릿의 내용을 표시하지 않는다. 이는 시크릿이 실수로 구경꾼에게 노출되거나 터미널 로그에 저장되는 것을 방지하기 위한 것이다. 인코딩된 데이터의 실제 내용을 확인하려면 다음을 참조한다. 시크릿 디코딩.

하나의 필드(예: username)가 datastringData에 모두 명시되면, stringData에 명시된 값이 사용된다. 예를 들어 다음과 같은 시크릿인 경우:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

결과는 다음과 같은 시크릿이다.

apiVersion: v1
data:
  username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque

여기서 YWRtaW5pc3RyYXRvcg==administrator으로 디코딩된다.

삭제

생성한 시크릿을 삭제하려면 다음 명령을 실행한다.

kubectl delete secret mysecret

다음 내용

4.5.3 - kustomize를 사용하여 시크릿 관리

kustomization.yaml 파일을 사용하여 시크릿 오브젝트 생성.

쿠버네티스 v1.14부터 kubectlKustomize를 이용한 쿠버네티스 오브젝트의 선언형 관리를 지원한다. Kustomize는 시크릿 및 컨피그맵을 생성하기 위한 리소스 생성기를 제공한다. Kustomize 생성기는 디렉토리 내의 kustomization.yaml 파일에 지정되어야 한다. 시크릿 생성 후 kubectl apply를 통해 API 서버에 시크릿을 생성할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

Kustomization 파일 생성

kustomization.yaml 파일에 다른 기존 파일을 참조하는 secretGenerator를 정의하여 시크릿을 생성할 수 있다. 예를 들어 다음 kustomization 파일은 ./username.txt./password.txt 파일을 참조한다.

secretGenerator:
- name: db-user-pass
  files:
  - username.txt
  - password.txt

kustomization.yaml 파일에 리터럴을 명시하여 secretGenerator를 정의할 수도 있다. 예를 들어 다음 kustomization.yaml 파일에는 각각 usernamepassword에 대한 두 개의 리터럴이 포함되어 있다.

secretGenerator:
- name: db-user-pass
  literals:
  - username=admin
  - password=1f2d1e2e67df

kustomization.yaml 파일에 .env 파일을 명시하여 secretGenerator를 정의할 수도 있다. 예를 들어 다음 kustomization.yaml 파일은 .env.secret 파일에서 데이터를 가져온다.

secretGenerator:
- name: db-user-pass
  envs:
  - .env.secret

모든 경우에 대해, 값을 base64로 인코딩하지 않아도 된다.

시크릿 생성

다음 명령을 실행하여 시크릿을 생성한다.

kubectl apply -k .

출력은 다음과 유사하다.

secret/db-user-pass-96mffmfh4k created

시크릿이 생성되면 시크릿 데이터를 해싱하고 이름에 해시 값을 추가하여 시크릿 이름이 생성된다. 이렇게 함으로써 데이터가 수정될 때마다 시크릿이 새롭게 생성된다.

생성된 시크릿 확인

시크릿이 생성된 것을 확인할 수 있다.

kubectl get secrets

출력은 다음과 유사하다.

NAME                             TYPE                                  DATA      AGE
db-user-pass-96mffmfh4k          Opaque                                2         51s

다음 명령을 실행하여 시크릿에 대한 상세 사항을 볼 수 있다.

kubectl describe secrets/db-user-pass-96mffmfh4k

출력은 다음과 유사하다.

Name:            db-user-pass-96mffmfh4k
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes

kubectl getkubectl describe 명령은 기본적으로 시크릿의 내용을 표시하지 않는다. 이는 시크릿이 실수로 구경꾼에게 노출되는 것을 방지하기 위한 것으로, 또는 터미널 로그에 저장되지 않는다. 인코딩된 데이터의 실제 내용을 확인하려면 다음을 참조한다. 시크릿 디코딩.

삭제

생성한 시크릿을 삭제하려면 다음 명령을 실행한다.

kubectl delete secret db-user-pass-96mffmfh4k

다음 내용

4.6 - 애플리케이션에 데이터 주입하기

워크로드를 실행하는 파드에 대한 구성과 기타 데이터를 지정한다.

4.6.1 - 컨테이너를 위한 커맨드와 인자 정의하기

본 페이지는 파드 안에서 컨테이너를 실행할 때 커맨드와 인자를 정의하는 방법에 대해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

파드를 생성할 때 커맨드와 인자를 정의하기

파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 커맨드와 인자를 정의할 수 있다. 커맨드를 정의하기 위해서는, 파드 안에서 실행되는 컨테이너에 command 필드를 포함시킨다. 커맨드에 대한 인자를 정의하기 위해서는, 구성 파일에 args 필드를 포함시킨다. 정의한 커맨드와 인자들은 파드가 생성되고 난 이후에는 변경될 수 없다.

구성 파일 안에서 정의하는 커맨드와 인자들은 컨테이너 이미지가 제공하는 기본 커맨드와 인자들보다 우선시 된다. 만약 인자들을 정의하고 커맨드를 정의하지 않는다면, 기본 커맨드가 새로운 인자와 함께 사용된다.

이 예제에서는 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성 파일에서 커맨드와 두 개의 인자를 정의한다.

apiVersion: v1
kind: Pod
metadata:
  name: command-demo
  labels:
    purpose: demonstrate-command
spec:
  containers:
  - name: command-demo-container
    image: debian
    command: ["printenv"]
    args: ["HOSTNAME", "KUBERNETES_PORT"]
  restartPolicy: OnFailure
  1. YAML 구성 파일을 활용해 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/commands.yaml
    
  2. 실행 중인 파드들의 목록을 조회한다.

    kubectl get pods
    

    출력은 command-demo라는 파드 안에서 실행된 컨테이너가 완료되었다고 표시할 것이다.

  3. 컨테이너 안에서 실행된 커맨드의 출력을 보기 위해, 파드의 로그를 확인한다.

    kubectl logs command-demo
    

    출력은 HOSTNAME과 KUBERNETES_PORT 환경 변수들의 값들을 표시할 것이다.

    command-demo
    tcp://10.3.240.1:443
    

인자를 정의하기 위해 환경 변수를 사용하기

이전 예제에서는, 문자열을 제공하면서 직접 인자를 정의해보았다. 문자열을 직접 제공하는 것에 대한 대안으로, 환경 변수들을 사용하여 인자들을 정의할 수 있다.

env:
- name: MESSAGE
  value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

이것은 컨피그 맵시크릿을 포함해, 환경 변수를 정의하는데 활용할 수 있는 모든 방법들을 활용해서 파드를 위한 인자를 정의할 수 있다는 것을 의미한다.

셸 안에서 커맨드 실행하기

일부 경우들에서는 커맨드를 셸 안에서 실행해야할 필요가 있다. 예를 들어, 실행할 커맨드가 서로 연결되어 있는 여러 개의 커맨드들로 구성되어 있거나, 셸 스크립트일 수도 있다. 셸 안에서 커맨드를 실행하려고 한다면, 이런 방식으로 감싸주면 된다.

command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]

참고사항

이 테이블은 도커와 쿠버네티스에서 사용되는 필드 이름들을 정리한 것이다.

설명 도커 필드 이름 쿠버네티스 필드 이름
컨테이너에서 실행되는 커맨드 Entrypoint command
커맨드에 전달되는 인자들 Cmd arg

기본 Entrypoint와 Cmd 값을 덮어쓰려고 한다면, 아래의 규칙들이 적용된다.

  • 만약 컨테이너를 위한 command 값이나 args 값을 제공하지 않는다면, 도커 이미지 안에 제공되는 기본 값들이 사용된다.

  • 만약 컨테이너를 위한 command 값을 제공하고, args 값을 제공하지 않는다면, 제공된 command 값만이 사용된다. 도커 이미지 안에 정의된 기본 EntryPoint 값과 기본 Cmd 값은 덮어쓰여진다.

  • 만약 컨테이너를 위한 args 값만 제공한다면, 도커 이미지 안에 정의된 기본 EntryPoint 값이 정의한 args 값들과 함께 실행된다.

  • command 값과 args 값을 동시에 정의한다면, 도커 이미지 안에 정의된 기본 EntryPoint 값과 기본 Cmd 값이 덮어쓰여진다. commandargs 값과 함께 실행된다.

여기 몇 가지 예시들이 있다.

이미지 Entrypoint 이미지 Cmd 컨테이너 command 컨테이너 args 실행되는 커맨드
[/ep-1] [foo bar] <설정되지 않음> <설정되지 않음> [ep-1 foo bar]
[/ep-1] [foo bar] [/ep-2] <설정되지 않음> [ep-2]
[/ep-1] [foo bar] <설정되지 않음> [zoo boo] [ep-1 zoo boo]
[/ep-1] [foo bar] [/ep-2] [zoo boo] [ep-2 zoo boo]

다음 내용

4.6.2 - 종속 환경 변수 정의하기

본 페이지는 쿠버네티스 파드의 컨테이너를 위한 종속 환경 변수를 정의하는 방법에 대해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

컨테이너를 위한 종속 환경 변수 정의하기

파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 종속 환경 변수를 설정할 수 있다. 종속 환경 변수를 설정하려면, 구성 파일에서 envvalue에 $(VAR_NAME)을 사용한다.

이 예제에서, 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성 파일은 일반적인 방식으로 정의된 종속 환경 변수를 정의한다. 다음은 파드를 위한 구성 매니페스트 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: dependent-envars-demo
spec:
  containers:
    - name: dependent-envars-demo
      args:
        - while true; do echo -en '\n'; printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n'; sleep 30; done;
      command:
        - sh
        - -c
      image: busybox
      env:
        - name: SERVICE_PORT
          value: "80"
        - name: SERVICE_IP
          value: "172.17.0.1"
        - name: UNCHANGED_REFERENCE
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: PROTOCOL
          value: "https"
        - name: SERVICE_ADDRESS
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: ESCAPED_REFERENCE
          value: "$$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
  1. YAML 구성 파일을 활용해 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/inject/dependent-envars.yaml
    
    pod/dependent-envars-demo created
    
  2. 실행 중인 파드의 목록을 조회한다.

    kubectl get pods dependent-envars-demo
    
    NAME                      READY     STATUS    RESTARTS   AGE
    dependent-envars-demo     1/1       Running   0          9s
    
  3. 파드 안에서 동작 중인 컨테이너의 로그를 확인한다.

    kubectl logs pod/dependent-envars-demo
    
    
    UNCHANGED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    SERVICE_ADDRESS=https://172.17.0.1:80
    ESCAPED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    

위에서 보듯이, SERVICE_ADDRESS는 올바른 종속성 참조, UNCHANGED_REFERENCE는 잘못된 종속성 참조를 정의했으며 ESCAPED_REFERENCE는 종속성 참조를 건너뛴다.

환경 변수가 참조될 때 해당 환경 변수가 미리 정의되어 있으면 SERVICE_ADDRESS의 경우와 같이 참조를 올바르게 해석할 수 있다.

환경 변수가 정의되지 않았거나 일부 변수만 포함된 경우, 정의되지 않은 환경 변수는 UNCHANGED_REFERENCE의 경우와 같이 일반 문자열로 처리된다. 일반적으로 환경 변수 해석에 실패하더라도 컨테이너의 시작을 막지는 않는다.

$(VAR_NAME) 구문은 이중 $로 이스케이프될 수 있다. (예: $$(VAR_NAME)) 이스케이프된 참조는 참조된 변수가 정의되었는지 여부에 관계없이 해석을 수행하지 않는다. 이는 위의 ESCAPED_REFERENCE를 통해 확인할 수 있다.

다음 내용

4.6.3 - 컨테이너를 위한 환경 변수 정의하기

본 페이지는 쿠버네티스 파드의 컨테이너를 위한 환경 변수를 정의하는 방법에 대해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

컨테이너를 위한 환경 변수 정의하기

파드를 생성할 때, 파드 안에서 동작하는 컨테이너를 위한 환경 변수를 설정할 수 있다. 환경 변수를 설정하려면, 구성 파일에 envenvFrom 필드를 포함시켜야 한다.

이 예제에서, 한 개의 컨테이너를 실행하는 파드를 생성한다. 파드를 위한 구성 파일은 DEMO_GREETING 이라는 이름과 "Hello from the environment"이라는 값을 가지는 환경 변수를 정의한다. 다음은 파드를 위한 구성 매니페스트 예시이다.

apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/node-hello:1.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"
    - name: DEMO_FAREWELL
      value: "Such a sweet sorrow"
  1. YAML 구성 파일을 활용해 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
    
  2. 실행 중인 파드들의 목록을 조회한다.

    kubectl get pods -l purpose=demonstrate-envars
    

    출력은 아래와 비슷할 것이다.

    NAME            READY     STATUS    RESTARTS   AGE
    envar-demo      1/1       Running   0          9s
    
  3. 파드의 컨테이너 환경 변수를 나열한다.

    kubectl exec envar-demo -- printenv
    

    출력은 아래와 비슷할 것이다.

    NODE_VERSION=4.4.2
    EXAMPLE_SERVICE_PORT_8080_TCP_ADDR=10.3.245.237
    HOSTNAME=envar-demo
    ...
    DEMO_GREETING=Hello from the environment
    DEMO_FAREWELL=Such a sweet sorrow
    

설정 안에서 환경 변수 사용하기

파드의 구성 파일 안에서 정의한 환경 변수는 파드의 컨테이너를 위해 설정하는 커맨드와 인자들과 같이, 구성 파일 안의 다른 곳에서 사용할 수 있다. 아래의 구성 파일 예시에서, GREETING, HONORIFIC, 그리고 NAME 환경 변수들이 각각 Warm greetings to, The Most honorable, 그리고 Kubernetes로 설정되어 있다. 이 환경 변수들은 이후 env-print-demo 컨테이너에 전달되어 CLI 인자에서 사용된다.

apiVersion: v1
kind: Pod
metadata:
  name: print-greeting
spec:
  containers:
  - name: env-print-demo
    image: bash
    env:
    - name: GREETING
      value: "Warm greetings to"
    - name: HONORIFIC
      value: "The Most Honorable"
    - name: NAME
      value: "Kubernetes"
    command: ["echo"]
    args: ["$(GREETING) $(HONORIFIC) $(NAME)"]

컨테이너가 생성되면, echo Warm greetings to The Most Honorable Kubernetes 커맨드가 컨테이너에서 실행된다.

다음 내용

4.6.4 - 환경 변수로 컨테이너에 파드 정보 노출하기

본 페이지는 파드에서 실행 중인 컨테이너에게 파드가 환경 변수를 사용해서 자신의 정보를 노출하는 방법에 대해 설명한다. 환경 변수는 파드 필드와 컨테이너 필드를 노출할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

다운워드(Downward) API

파드 및 컨테이너 필드를 실행 중인 컨테이너에 노출하는 두 가지 방법이 있다.

파드 및 컨테이너 필드를 노출하는 이 두 가지 방법을 다운워드 API라고 한다.

파드 필드를 환경 변수의 값으로 사용하자

이 연습에서는 하나의 컨테이너를 가진 파드를 생성한다. 다음은 파드에 대한 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never

구성 파일에서 5개의 환경 변수를 확인할 수 있다. env 필드는 EnvVars의 배열이다. 배열의 첫 번째 요소는 MY_NODE_NAME 환경 변수가 파드의 spec.nodeName 필드에서 값을 가져오도록 지정한다. 마찬가지로 다른 환경 변수도 파드 필드에서 이름을 가져온다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-pod.yaml

파드의 컨테이너가 실행중인지 확인한다.

kubectl get pods

컨테이너의 로그를 본다.

kubectl logs dapi-envars-fieldref

출력은 선택된 환경 변수의 값을 보여준다.

minikube
dapi-envars-fieldref
default
172.17.0.4
default

이러한 값이 로그에 출력된 이유를 보려면 구성 파일의 commandargs 필드를 확인하자. 컨테이너가 시작되면 5개의 환경 변수 값을 stdout에 쓰며 10초마다 이를 반복한다.

다음으로 파드에서 실행 중인 컨테이너의 셸을 가져오자.

kubectl exec -it dapi-envars-fieldref -- sh

셸에서 환경 변수를 보자.

/# printenv

출력은 특정 환경 변수에 파드 필드 값이 할당되었음을 보여준다.

MY_POD_SERVICE_ACCOUNT=default
...
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.4
...
MY_NODE_NAME=minikube
...
MY_POD_NAME=dapi-envars-fieldref

컨테이너 필드를 환경 변수의 값으로 사용하기

이전 연습에서는 파드 필드를 환경 변수의 값으로 사용했다. 이 다음 연습에서는 컨테이너 필드를 환경 변수의 값으로 사용한다. 다음은 하나의 컨테이너가 있는 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-resourcefieldref
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox:1.24
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 10;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
  restartPolicy: Never

구성 파일에서 4개의 환경 변수를 확인할 수 있다. env 필드는 EnvVars의 배열이다. 배열의 첫 번째 요소는 MY_CPU_REQUEST 환경 변수가 test-container라는 컨테이너의 requests.cpu 필드에서 값을 가져오도록 지정한다. 마찬가지로 다른 환경 변수도 컨테이너 필드에서 값을 가져온다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-container.yaml

파드의 컨테이너가 실행중인지 확인한다.

kubectl get pods

컨테이너의 로그를 본다.

kubectl logs dapi-envars-resourcefieldref

출력은 선택된 환경 변수의 값을 보여준다.

1
1
33554432
67108864

다음 내용

4.6.5 - 파일로 컨테이너에 파드 정보 노출하기

본 페이지는 파드가 DownwardAPIVolumeFile을 사용하여 파드에서 실행되는 컨테이너에 자신에 대한 정보를 노출하는 방법에 대해 설명한다. DownwardAPIVolumeFile은 파드 필드와 컨테이너 필드를 노출할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

다운워드(Downward) API

실행 중인 컨테이너에 파드 및 컨테이너 필드를 노출하는 방법에는 두 가지가 있다.

파드 및 컨테이너 필드를 노출하는 이 두 가지 방법을 다운워드 API라고 한다.

파드 필드 저장

이 연습에서는 하나의 컨테이너를 가진 파드를 생성한다. 다음은 파드에 대한 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          if [[ -e /etc/podinfo/annotations ]]; then
            echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

구성 파일에서 파드에 downwardAPI 볼륨이 있고 컨테이너가 /etc/podinfo에 볼륨을 마운트하는 것을 확인할 수 있다.

downwardAPI 아래의 배열을 살펴보자. 배열의 각 요소는 DownwardAPIVolumeFile이다. 첫 번째 요소는 파드의 metadata.labels 필드 값이 labels라는 파일에 저장되어야 함을 지정한다. 두 번째 요소는 파드의 annotations 필드 값이 annotations라는 파일에 저장되어야 함을 지정한다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume.yaml

파드의 컨테이너가 실행 중인지 확인한다.

kubectl get pods

컨테이너의 로그를 본다.

kubectl logs kubernetes-downwardapi-volume-example

출력은 labels 파일과 annotations 파일의 내용을 보여준다.

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"

파드에서 실행 중인 컨테이너의 셸을 가져오자.

kubectl exec -it kubernetes-downwardapi-volume-example -- sh

셸에서 labels 파일을 보자.

/# cat /etc/podinfo/labels

출력을 통해 모든 파드의 레이블이 labels 파일에 기록되었음을 확인할 수 있다.

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

마찬가지로 annotations 파일을 확인하자.

/# cat /etc/podinfo/annotations

etc/podinfo 디렉터리에 파일을 확인하자.

/# ls -laR /etc/podinfo

출력에서 labelsannotations 파일이 임시 하위 디렉터리에 있음을 알 수 있다. 이 예제에서는 ..2982_06_02_21_47_53.299460680이다. /etc/podinfo 디렉터리에서 ..data는 임시 하위 디렉토리에 대한 심볼릭 링크이다. /etc/podinfo 디렉토리에서 labelsannotations 또한 심볼릭 링크이다.

drwxr-xr-x  ... Feb 6 21:47 ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 ..data -> ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 annotations -> ..data/annotations
lrwxrwxrwx  ... Feb 6 21:47 labels -> ..data/labels

/etc/..2982_06_02_21_47_53.299460680:
total 8
-rw-r--r--  ... Feb  6 21:47 annotations
-rw-r--r--  ... Feb  6 21:47 labels

심볼릭 링크를 사용하면 메타데이터의 동적(dynamic) 원자적(atomic) 갱신이 가능하다. 업데이트는 새 임시 디렉터리에 기록되고, ..data 심볼릭 링크는 rename(2)을 사용하여 원자적(atomic)으로 갱신한다.

셸을 종료한다.

/# exit

컨테이너 필드 저장

이전 연습에서는 파드 필드를 DownwardAPIVolumeFile에 저장하였다. 이 다음 연습에서는 컨테이너 필드를 저장한다. 다음은 하나의 컨테이너를 가진 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example-2
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox:1.24
      command: ["sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          if [[ -e /etc/podinfo/cpu_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_limit; fi;
          if [[ -e /etc/podinfo/cpu_request ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_request; fi;
          if [[ -e /etc/podinfo/mem_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_limit; fi;
          if [[ -e /etc/podinfo/mem_request ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_request; fi;
          sleep 5;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "cpu_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.cpu
              divisor: 1m
          - path: "cpu_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.cpu
              divisor: 1m
          - path: "mem_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.memory
              divisor: 1Mi
          - path: "mem_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.memory
              divisor: 1Mi

구성 파일에서 파드에 downwardAPI 볼륨이 있고 컨테이너는 /etc/podinfo에 볼륨을 마운트하는 것을 확인할 수 있다.

downwardAPI 아래의 items 배열을 살펴보자. 배열의 각 요소는 DownwardAPIVolumeFile이다.

첫 번째 요소는 client-container라는 컨테이너에서 1m으로 지정된 형식의 limits.cpu 필드 값이 cpu_limit이라는 파일에 저장되어야 함을 지정한다. divisor 필드는 선택 사항이며 기본값인 1은 CPU에 대한 코어 및 메모리에 대한 바이트를 의미한다.

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume-resources.yaml

파드에서 실행 중인 컨테이너의 셸을 가져온다.

kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh

셸에서 cpu_limit 파일을 확인한다.

/# cat /etc/podinfo/cpu_limit

비슷한 명령을 통해 cpu_request, mem_limitmem_request 파일을 확인할 수 있다.

다운워드 API의 기능

다음 정보는 환경 변수 및 downwardAPI 볼륨을 통해 컨테이너에서 사용할 수 있다.

  • fieldRef를 통해 다음 정보를 사용할 수 있다.
    • metadata.name - 파드의 이름
    • metadata.namespace - 파드의 네임스페이스(Namespace)
    • metadata.uid - 파드의 UID
    • metadata.labels['<KEY>'] - 파드의 레이블 <KEY> 값 (예를 들어, metadata.labels['mylabel'])
    • metadata.annotations['<KEY>'] - 파드의 어노테이션 <KEY> 값 (예를 들어, metadata.annotations['myannotation'])
  • resourceFieldRef를 통해 다음 정보를 사용할 수 있다.
    • 컨테이너의 CPU 한도(limit)
    • 컨테이너의 CPU 요청(request)
    • 컨테이너의 메모리 한도(limit)
    • 컨테이너의 메모리 요청(request)
    • 컨테이너의 hugepages 한도(limit) (DownwardAPIHugePages 기능 게이트(feature gate)가 활성화된 경우)
    • 컨테이너의 hugepages 요청(request) (DownwardAPIHugePages 기능 게이트(feature gate)가 활성화된 경우)
    • 컨테이너의 임시-스토리지 한도(limit)
    • 컨테이너의 임시-스토리지 요청(request)

downwardAPI 볼륨 fieldRef를 통해 다음 정보를 사용할 수 있다.

  • metadata.labels - 한 줄에 하나의 레이블이 있는 label-key="escaped-label-value" 형식의 모든 파드 레이블
  • metadata.annotations - 한 줄에 하나의 어노테이션이 있는 annotation-key="escaped-annotation-value" 형식의 모든 파드 어노테이션

환경 변수를 통해 다음 정보를 사용할 수 있다.

  • status.podIP - 파드의 IP 주소
  • spec.serviceAccountName - 파드의 서비스 계정 이름, v1.4.0-alpha.3부터 사용 가능
  • spec.nodeName - 노드의 이름, v1.4.0-alpha.3부터 사용 가능
  • status.hostIP - 노드의 IP, v1.7.0-alpha.1 이후 사용 가능

특정 경로 및 파일 권한에 대한 프로젝트 키

키(key)를 파드 안의 특정 경로에, 특정 권한으로, 파일 단위로 투영(project)할 수 있다. 자세한 내용은 시크릿(Secrets)을 참조한다.

다운워드 API에 대한 동기

컨테이너가 쿠버네티스에 과도하게 결합되지 않고 자체에 대한 정보를 갖는 것이 때때로 유용하다. 다운워드 API를 사용하면 컨테이너가 쿠버네티스 클라이언트 또는 API 서버를 사용하지 않고 자체 또는 클러스터에 대한 정보를 사용할 수 있다.

예를 들어 잘 알려진 특정 환경 변수에 고유 식별자가 있다고 가정하는 기존 애플리케이션이 있다. 한 가지 가능성은 애플리케이션을 래핑하는 것이지만 이는 지루하고 오류가 발생하기 쉬우며 낮은 결합 목표를 위반한다. 더 나은 옵션은 파드의 이름을 식별자로 사용하고 파드의 이름을 잘 알려진 환경 변수에 삽입하는 것이다.

다음 내용

4.6.6 - 시크릿(Secret)을 사용하여 안전하게 자격증명 배포하기

본 페이지는 암호 및 암호화 키와 같은 민감한 데이터를 파드에 안전하게 주입하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

시크릿 데이터를 base-64 표현으로 변환하기

사용자 이름 my-app과 비밀번호 39528$vdg7Jb의 두 가지 시크릿 데이터가 필요하다고 가정한다. 먼저 base64 인코딩 도구를 사용하여 사용자 이름과 암호를 base64 표현으로 변환한다. 다음은 일반적으로 사용 가능한 base64 프로그램을 사용하는 예제이다.

echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64

사용자 이름의 base-64 표현이 bXktYXBw이고 암호의 base-64 표현이 Mzk1MjgkdmRnN0pi임을 출력을 통해 확인할 수 있다.

시크릿 생성하기

다음은 사용자 이름과 암호가 들어 있는 시크릿을 생성하는 데 사용할 수 있는 구성 파일이다.

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
  1. 시크릿을 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
    
  2. 시크릿에 대한 정보를 확인한다.

    kubectl get secret test-secret
    

    Output:

    NAME          TYPE      DATA      AGE
    test-secret   Opaque    2         1m
    
  3. 시크릿에 대한 자세한 정보를 확인한다.

    kubectl describe secret test-secret
    

    Output:

    Name:       test-secret
    Namespace:  default
    Labels:     <none>
    Annotations:    <none>
    
    Type:   Opaque
    
    Data
    ====
    password:   13 bytes
    username:   7 bytes
    

kubectl로 직접 시크릿 생성하기

Base64 인코딩 단계를 건너뛰려면 kubectl create secret 명령을 사용하여 동일한 Secret을 생성할 수 있다. 다음은 예시이다.

kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'

이와 같이 더 편리하게 사용할 수 있다. 앞에서 설명한 자세한 접근 방식은 각 단계를 명시적으로 실행하여 현재 상황을 확인할 수 있다.

볼륨을 통해 시크릿 데이터에 접근할 수 있는 파드 생성하기

다음은 파드를 생성하는 데 사용할 수 있는 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        # name must match the volume name below
        - name: secret-volume
          mountPath: /etc/secret-volume
  # The secret data is exposed to Containers in the Pod through a Volume.
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret
  1. 파드를 생성한다.

    kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
    
  2. 파드가 실행중인지 확인한다.

    kubectl get pod secret-test-pod
    

    Output:

    NAME              READY     STATUS    RESTARTS   AGE
    secret-test-pod   1/1       Running   0          42m
    
  3. 파드에서 실행 중인 컨테이너의 셸을 가져오자.

    kubectl exec -i -t secret-test-pod -- /bin/bash
    
  4. 시크릿 데이터는 /etc/secret-volume에 마운트된 볼륨을 통해 컨테이너에 노출된다.

    셸에서 /etc/secret-volume 디렉터리의 파일을 나열한다.

    # 컨테이너 내부의 셸에서 실행하자
    ls /etc/secret-volume
    

    두 개의 파일과 각 파일의 시크릿 데이터 조각을 확인할 수 있다.

    password username
    
  5. 셸에서 usernamepassword 파일의 내용을 출력한다.

    # 컨테이너 내부의 셸에서 실행하자
    echo "$( cat /etc/secret-volume/username )"
    echo "$( cat /etc/secret-volume/password )"
    

    사용자 이름과 비밀번호가 출력된다.

    my-app
    39528$vdg7Jb
    

시크릿 데이터를 사용하여 컨테이너 환경 변수 정의하기

단일 시크릿 데이터로 컨테이너 환경 변수 정의하기

  • 환경 변수를 시크릿의 키-값 쌍으로 정의한다.

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    
  • 시크릿에 정의된 backend-username 값을 파드 명세의 SECRET_USERNAME 환경 변수에 할당한다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-single-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
    
  • 파드를 생성한다.

    kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
    
  • 셸에서 SECRET_USERNAME 컨테이너 환경 변수의 내용을 출력한다.

    kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
    

    출력은 다음과 같다.

    backend-admin
    

여러 시크릿 데이터로 컨테이너 환경 변수 정의하기

  • 이전 예제와 마찬가지로 시크릿을 먼저 생성한다.

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    kubectl create secret generic db-user --from-literal=db-username='db-admin'
    
  • 파드 명세에 환경 변수를 정의한다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: envvars-multiple-secrets
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: BACKEND_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-user
              key: db-username
    
  • 파드를 생성한다.

    kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
    
  • 셸에서 컨테이너 환경 변수를 출력한다.

    kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
    

    출력은 다음과 같다.

    DB_USERNAME=db-admin
    BACKEND_USERNAME=backend-admin
    

시크릿의 모든 키-값 쌍을 컨테이너 환경 변수로 구성하기

  • 여러 키-값 쌍을 포함하는 시크릿을 생성한다.

    kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
    
  • envFrom을 사용하여 시크릿의 모든 데이터를 컨테이너 환경 변수로 정의한다. 시크릿의 키는 파드에서 환경 변수의 이름이 된다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: envfrom-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        envFrom:
        - secretRef:
            name: test-secret
    
  • 파드를 생성한다.

    kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
    
  • usernamepassword 컨테이너 환경 변수를 셸에서 출력한다.

kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'

출력은 다음과 같다.

username: my-app
password: 39528$vdg7Jb

참고

다음 내용

  • 시크릿에 대해 더 배워 보기.
  • 볼륨에 대해 더 배워 보기.

4.7 - 애플리케이션 실행

스테이트리스와 스테이트풀 애플리케이션 모두를 실행하고 관리한다.

4.7.1 - 디플로이먼트(Deployment)로 스테이트리스 애플리케이션 실행하기

이 페이지에서는 쿠버네티스 디플로이먼트 오브젝트를 사용하여 애플리케이션을 실행하는 방법을 설명한다.

목적

  • nginx 디플로이먼트 생성하기
  • kubectl을 사용하여 디플로이먼트 정보 나열하기
  • 디플로이먼트 업데이트하기

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

nginx 디플로이먼트 생성하고 탐색하기

쿠버네티스 디플로이먼트 오브젝트를 생성하여 애플리케이션을 실행할 수 있으며, 디플로이먼트에 대한 명세를 YAML 파일에 기술할 수 있다. 예를 들어 이 YAML 파일은 nginx:1.14.2 도커 이미지를 실행하는 디플로이먼트에 대한 명세를 담고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. YAML 파일을 기반으로 디플로이먼트를 생성한다.

     kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  2. 디플로이먼트에 대한 정보를 살펴본다.

     kubectl describe deployment nginx-deployment
    

    출력은 다음과 유사하다.

     Name:     nginx-deployment
     Namespace:    default
     CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
     Labels:     app=nginx
     Annotations:    deployment.kubernetes.io/revision=1
     Selector:   app=nginx
     Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
     StrategyType:   RollingUpdate
     MinReadySeconds:  0
     RollingUpdateStrategy:  1 max unavailable, 1 max surge
     Pod Template:
       Labels:       app=nginx
       Containers:
        nginx:
         Image:              nginx:1.14.2
         Port:               80/TCP
         Environment:        <none>
         Mounts:             <none>
       Volumes:              <none>
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     True    MinimumReplicasAvailable
       Progressing   True    NewReplicaSetAvailable
     OldReplicaSets:   <none>
     NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
     No events.
    
  3. 디플로이먼트에 의해 생성된 파드를 나열한다.

     kubectl get pods -l app=nginx
    

    출력은 다음과 유사하다.

     NAME                                READY     STATUS    RESTARTS   AGE
     nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
     nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  4. 파드에 대한 정보를 살펴본다.

     kubectl describe pod <pod-name>
    

    <pod-name>은 파드 중 하나의 이름이다.

디플로이먼트 업데이트하기

새 YAML 파일을 적용하여 디플로이먼트를 업데이트할 수 있다. 이 YAML 파일은 nginx 1.16.1을 사용하도록 디플로이먼트를 업데이트해야 함을 명시하고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80
  1. 새 YAML 파일을 적용한다.

      kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  2. 디플로이먼트가 새 이름으로 파드를 생성하고 이전 파드를 삭제하는 것을 확인한다.

      kubectl get pods -l app=nginx
    

레플리카 수를 늘려 애플리케이션 확장하기

새 YAML 파일을 적용하여 디플로이먼트의 파드 수를 늘릴 수 있다. 이 YAML 파일은 replicas를 4로 설정하여 디플로이먼트에 4개의 파드가 있어야 함을 명시하고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. 새 YAML 파일을 적용한다.

     kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  2. 디플로이먼트에 4개의 파드가 있는지 확인한다.

     kubectl get pods -l app=nginx
    

    출력은 다음과 유사하다.

     NAME                               READY     STATUS    RESTARTS   AGE
     nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
     nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
     nginx-deployment-148880595-fxcez   1/1       Running   0          2m
     nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

디플로이먼트 삭제하기

이름으로 디플로이먼트를 삭제한다.

kubectl delete deployment nginx-deployment

ReplicationControllers -- 예전 방식

애플리케이션을 복제하여 생성하는 기본적인 방법은 내부적으로 레플리카셋(ReplicaSet)을 활용하는 디플로이먼트를 사용하는 것이다. 쿠버네티스에 디플로이먼트 및 레플리카셋이 도입되기 전에는 레플리케이션컨트롤러(ReplicationController)를 사용하여 복제 애플리케이션을 구성했었다.

다음 내용

4.7.2 - 단일 인스턴스 스테이트풀 애플리케이션 실행하기

이 페이지에서는 쿠버네티스 클러스터에서 퍼시스턴트볼륨(PersistentVolume)과 디플로이먼트(Deployment)를 사용하여, 단일 인스턴스 스테이트풀 애플리케이션을 실행하는 방법을 보인다. 해당 애플리케이션은 MySQL이다.

목적

  • 사용자 환경의 디스크를 참조하는 퍼시스턴트볼륨 생성하기
  • MySQL 디플로이먼트 생성하기
  • 알려진 DNS 이름으로 클러스터의 다른 파드에 MySQL 서비스 노출하기

시작하기 전에

  • 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

    버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

  • 여기에서 사용된 퍼시스턴트볼륨클레임을 만족하기 위하여, 기본 스토리지클래스정적으로(statically) 프로비저닝 된 퍼시스턴트볼륨을 갖는 퍼시스턴트 볼륨 프로비저너가 필요하다.

MySQL 배포하기

쿠버네티스 디플로이먼트를 생성하고 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 사용하는 기존 퍼시스턴트볼륨에 연결하여 스테이트풀 애플리케이션을 실행할 수 있다. 예를 들어, 다음 YAML 파일은 MySQL을 실행하고 퍼시스턴트볼륨클레임을 참조하는 디플로이먼트를 기술한다. 이 파일은 /var/lib/mysql에 대한 볼륨 마운트를 정의한 후에, 20G의 볼륨을 요청하는 퍼시트턴트볼륨클레임을 생성한다. 이 클레임은 요구 사항에 적합한 기존 볼륨이나 동적 프로비저너에 의해서 충족된다.

참고: config yaml 파일에 정의된 비밀번호는 안전하지 않다. 더 안전한 해결방법을 위해 쿠버네티스 시크릿 을 보자

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

  1. YAML 파일의 PV와 PVC를 배포한다.

     kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  2. YAML 파일의 다른 오브젝트들을 배포한다.

     kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  3. 디플로이먼트에 관한 정보를 확인한다.

     kubectl describe deployment mysql
    

    출력은 다음과 유사하다.

     Name:                 mysql
     Namespace:            default
     CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
     Labels:               app=mysql
     Annotations:          deployment.kubernetes.io/revision=1
     Selector:             app=mysql
     Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
     StrategyType:         Recreate
     MinReadySeconds:      0
     Pod Template:
       Labels:       app=mysql
       Containers:
        mysql:
         Image:      mysql:5.6
         Port:       3306/TCP
         Environment:
           MYSQL_ROOT_PASSWORD:      password
         Mounts:
           /var/lib/mysql from mysql-persistent-storage (rw)
       Volumes:
        mysql-persistent-storage:
         Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
         ClaimName:  mysql-pv-claim
         ReadOnly:   false
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     False   MinimumReplicasUnavailable
       Progressing   True    ReplicaSetUpdated
     OldReplicaSets:       <none>
     NewReplicaSet:        mysql-63082529 (1/1 replicas created)
     Events:
       FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
       ---------    --------    -----    ----                -------------    --------    ------            -------
       33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  4. 디플로이먼트로 생성된 파드를 나열한다.

     kubectl get pods -l app=mysql
    

    출력은 다음과 유사하다.

     NAME                   READY     STATUS    RESTARTS   AGE
     mysql-63082529-2z3ki   1/1       Running   0          3m
    
  5. 퍼시스턴트볼륨클레임을 살펴본다.

     kubectl describe pvc mysql-pv-claim
    

    출력은 다음과 유사하다.

     Name:         mysql-pv-claim
     Namespace:    default
     StorageClass:
     Status:       Bound
     Volume:       mysql-pv-volume
     Labels:       <none>
     Annotations:    pv.kubernetes.io/bind-completed=yes
                     pv.kubernetes.io/bound-by-controller=yes
     Capacity:     20Gi
     Access Modes: RWO
     Events:       <none>
    

MySQL 인스턴스 접근하기

이전의 YAML 파일은 클러스터의 다른 파드가 데이터베이스에 접근할 수 있는 서비스를 생성한다. clusterIP: None 서비스 옵션을 사용하면 서비스의 DNS 이름을 직접 파드의 IP 주소로 해석하도록 처리한다. 이 방법은 서비스에서 연결되는 파드가 오직 하나 뿐이고, 파드의 수를 더 늘릴 필요가 없는 경우에 가장 적합하다.

서버에 접속하기 위하여 MySQL 클라이언트를 실행한다.

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

이 명령어는 MySQL 클라이언트를 실행하는 파드를 클러스터에 생성하고, 서비스를 통하여 서버에 연결한다. 연결된다면, 스테이트풀 MySQL 데이터베이스가 실행 중임을 알 수 있다.

Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

mysql>

업데이트하기

kubectl apply 명령을 사용하여 기존과 동일한 방식으로 디플로이먼트의 이미지나 다른 부분을 변경할 수 있다. 스테이트풀 애플리케이션과 관련하여 몇 가지 주의 사항이 있다.

  • 애플리케이션을 스케일링하지 않는다. 이 설정은 단일 인스턴스 애플리케이션 전용이다. 기본적인 퍼시스턴트볼륨은 하나의 파드에서만 마운트할 수 있다. 클러스터 형태의 스테이트풀 애플리케이션에 대해서는 스테이트풀셋을 보자.
  • 디플로이먼트 구성 YAML 파일에서 strategy: type: Recreate 를 사용한다. 이는 쿠버네티스가 롤링 업데이트를 사용하지 않도록 지시한다. 동시에 두 개 이상의 파드를 생성할 수 없으므로, 롤링 업데이트는 일어나지 않게 된다. Recreate 전략을 사용하면 변경된 구성으로 새로운 파드를 생성하기에 앞서 기존의 파드를 중단한다.

디플로이먼트 삭제하기

이름으로 배포된 오브젝트를 삭제한다.

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

퍼시스턴트볼륨을 수동으로 프로비저닝한 경우라면, 동일하게 수동으로 삭제하고 기본 리소스도 해제해야 한다. 동적 프로비저너를 사용한 경우, 퍼시스턴트볼륨클레임이 삭제되었을 때에 퍼시스턴트볼륨 또한 자동으로 삭제된다. 일부 동적 프로비저너(EBS 와 PD와 같은)는 퍼시스턴트볼륨을 삭제할 때에 기본 리소스도 해제한다.

다음 내용

4.7.3 - 스테이트풀셋(StatefulSet) 삭제하기

이 작업은 스테이트풀셋을 삭제하는 방법을 설명한다.

시작하기 전에

  • 이 작업은 클러스터에 스테이트풀셋으로 표시되는 애플리케이션이 있다고 가정한다.

스테이트풀셋 삭제

쿠버네티스에서 다른 리소스를 삭제하는 것과 같은 방식으로 스테이트풀셋을 삭제할 수 있다. kubectl delete 명령어를 사용하고 파일 또는 이름으로 스테이트풀셋을 지정하자.

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>

스테이트풀셋 자체를 삭제한 후 연결된 헤드리스 서비스는 별도로 삭제해야 할 수도 있다.

kubectl delete service <service-name>

kubectl을 통해 스테이트풀셋을 삭제하면, 스테이트풀셋의 크기가 0으로 설정되고 이로 인해 스테이트풀셋에 포함된 모든 파드가 삭제된다. 파드가 아닌 스테이트풀셋만 삭제하려면, --cascade=orphan 옵션을 사용한다. 예시는 다음과 같다.

kubectl delete -f <file.yaml> --cascade=orphan

kubectl delete--cascade=orphan 를 사용하면 스테이트풀셋 오브젝트가 삭제된 후에도 스테이트풀셋에 의해 관리된 파드는 남게 된다. 만약 파드가 app=myapp 레이블을 갖고 있다면, 다음과 같이 파드를 삭제할 수 있다.

kubectl delete pods -l app=myapp

퍼시스턴트볼륨(PersistentVolume)

스테이트풀셋의 파드들을 삭제하는 것이 연결된 볼륨을 삭제하는 것은 아니다. 이것은 볼륨을 삭제하기 전에 볼륨에서 데이터를 복사할 수 있는 기회를 준다. 파드가 종료된 후 PVC를 삭제하면 스토리지 클래스와 반환 정책에 따라 백업 퍼시스턴트볼륨 삭제가 트리거될 수 있다. 클레임 삭제 후 볼륨에 접근할 수 있다고 가정하면 안된다.

스테이트풀셋의 완벽한 삭제

연결된 파드를 포함해서 스테이트풀셋의 모든 것을 삭제하기 위해 다음과 같이 일련의 명령을 실행한다.

grace=$(kubectl get pods <stateful-set-pod> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app=myapp
sleep $grace
kubectl delete pvc -l app=myapp

위의 예에서 파드에는 app=myapp 라는 레이블이 있다. 사용자에게 적절한 레이블로 대체하자.

스테이트풀셋 파드의 강제 삭제

스테이트풀셋의 일부 파드가 오랫동안 'Terminating' 또는 'Unknown' 상태에 있는 경우, apiserver에 수동적으로 개입하여 파드를 강제 삭제할 수도 있다. 이것은 잠재적으로 위험한 작업이다. 자세한 설명은 스테이트풀셋 파드 강제 삭제하기를 참고한다.

다음 내용

스테이트풀셋 파드 강제 삭제하기에 대해 더 알아보기.

4.7.4 - 스테이트풀셋(StatefulSet) 파드 강제 삭제하기

이 페이지에서는 스테이트풀셋의 일부인 파드를 삭제하는 방법을 보여주고 이 과정에서 고려해야 할 사항을 설명한다.

시작하기 전에

  • 이것은 상당히 고급 태스크이며 스테이트풀셋 고유의 속성 중 일부를 위반할 가능성이 있다.
  • 계속하기 전에 아래 나열된 고려 사항을 숙지하도록 한다.

스테이트풀셋 고려 사항

정상적인 스테이트풀셋의 작동에서는 스테이트풀셋 파드를 강제로 삭제할 필요가 절대 없다. 스테이트풀셋 컨트롤러는 스테이트풀셋의 멤버 생성, 스케일링, 삭제를 담당한다. 서수 0부터 N-1까지 지정된 수의 파드가 활성 상태이고 준비되었는지 확인한다. 스테이트풀셋은 언제든지 클러스터에서 실행 중인 지정된 신원을 가진 최대 하나의 파드가 있는지 확인한다. 이를 스테이트풀셋에서 제공하는 최대 하나 의미론이라고 한다.

수동 강제 삭제는 스테이트풀셋 고유의 최대 하나 의미론을 위반할 가능성이 있으므로 주의해서 수행해야 한다. 스테이트풀셋은 안정적인 네트워크 신원과 안정적인 스토리지가 필요한 분산 클러스터 애플리케이션을 실행하는 데 사용할 수 있다. 이러한 애플리케이션은 종종 고정된 신원을 가진 고정된 수의 구성원 앙상블에 의존하는 구성을 가진다. 여러 구성원이 동일한 신원을 갖는 것은 재앙이 될 수 있으며 데이터 손실로 이어질 수 있다(예: 쿼럼 기반 시스템의 스플릿 브레인(split-brain) 시나리오).

파드 삭제

다음 명령을 사용하여 파드를 단계적으로 삭제할 수 있다.

kubectl delete pods <pod>

위의 내용이 단계적인 종료로 이어지려면 파드가 pod.Spec.TerminationGracePeriodSeconds를 0으로 지정하지 않아야 한다. pod.Spec.TerminationGracePeriodSeconds를 0초로 설정하는 관행은 안전하지 않으며 스테이트풀셋 파드에서 강력히 권장하지 않는다. 단계적 삭제는 안전하며 kubelet이 apiserver에서 이름을 삭제하기 전에 파드가 정상적으로 종료되도록 한다.

노드에 연결할 수 없는 경우 파드는 자동으로 삭제되지 않는다. 연결할 수 없는 노드에서 실행 중인 파드는 타임아웃 후에 'Terminating'이나 'Unknown' 상태가 된다.

사용자가 연결할 수 없는 노드에서 파드를 단계적으로 삭제하려고 하면 파드가 이러한 상태에 들어갈 수도 있다. 이러한 상태의 파드를 apiserver에서 제거할 수 있는 유일한 방법은 다음과 같다.

  • 노드 오브젝트가 삭제된다(사용자 또는 노드 컨트롤러에 의해).
  • 응답하지 않는 노드의 kubelet이 응답을 시작하고 파드를 종료하여 apiserver에서 항목을 제거한다.
  • 사용자가 파드를 강제로 삭제한다.

권장되는 모범 사례는 첫 번째 또는 두 번째 방법을 사용하는 것이다. 노드가 죽은 것으로 확인되면(예: 네트워크에서 영구적으로 연결이 끊기거나 전원이 꺼진 경우 등) 노드 오브젝트를 삭제한다. 노드에 네트워크 파티션이 있는 경우 이를 해결하거나 해결될 때까지 기다린다. 파티션이 복구되면 kubelet은 파드 삭제를 완료하고 apiserver에서 해당 이름을 해제한다.

일반적으로 시스템은 파드가 노드에서 더 이상 실행되지 않거나 노드가 관리자에 의해 삭제되면 삭제를 완료한다. 파드를 강제로 삭제하여 이를 재정의할 수 있다.

강제 삭제

강제 삭제는 파드가 종료되었다는 kubelet의 확인을 기다리지 않는다. 강제 삭제가 파드를 죽이는 데 성공했는지 여부와 관계없이 즉시 apiserver에서 이름을 해제한다. 이렇게 하면 스테이트풀셋 컨트롤러가 동일한 신원으로 대체 파드를 생성할 수 있다. 이것은 여전히 실행 중인 파드와 중복될 수 있으며, 해당 파드가 여전히 스테이트풀셋의 다른 멤버와 통신할 수 있다면 스테이트풀셋이 보장하도록 설계된 최대 하나 의미론을 위반할 것이다.

스테이트풀셋 파드를 강제로 삭제하는 것은 문제의 파드가 스테이트풀셋의 다른 파드와 다시는 접촉하지 않으며 대체 생성을 위해 해당 이름을 안전하게 해제할 수 있다고 주장하는 것이다.

kubectl 버전 >= 1.5를 사용하여 파드를 강제로 삭제하려면, 다음을 수행한다.

kubectl delete pods <pod> --grace-period=0 --force

kubectl <= 1.4 버전을 사용하는 경우, --force 옵션을 생략하고 다음을 사용해야 한다.

kubectl delete pods <pod> --grace-period=0

이러한 명령 후에도 파드가 Unknown 상태에서 멈추면, 다음 명령을 사용하여 클러스터에서 파드를 제거한다.

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

항상 관련된 위험에 대해 완전히 이해한 상태에서 주의 깊게 스테이트풀셋 파드의 강제 삭제를 수행한다.

다음 내용

스테이트풀셋 디버깅하기에 대해 더 알아보기.

4.7.5 - Horizontal Pod Autoscaler

Horizontal Pod Autoscaler는 CPU 사용량 (또는 사용자 정의 메트릭, 아니면 다른 애플리케이션 지원 메트릭)을 관찰하여 레플리케이션 컨트롤러(ReplicationController), 디플로이먼트(Deployment), 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)의 파드 개수를 자동으로 스케일한다. Horizontal Pod Autoscaler는 크기를 조정할 수 없는 오브젝트(예: 데몬셋(DaemonSet))에는 적용되지 않는다.

Horizontal Pod Autoscaler는 쿠버네티스 API 리소스 및 컨트롤러로 구현된다. 리소스는 컨트롤러의 동작을 결정한다. 컨트롤러는 평균 CPU 사용률, 평균 메모리 사용률 또는 다른 커스텀 메트릭과 같은 관찰 대상 메트릭이 사용자가 지정한 목표값과 일치하도록 레플리케이션 컨트롤러 또는 디플로이먼트에서 레플리카 개수를 주기적으로 조정한다.

Horizontal Pod Autoscaler는 어떻게 작동하는가?

Horizontal Pod Autoscaler 다이어그램

Horizontal Pod Autoscaler는 컨트롤러 관리자의 --horizontal-pod-autoscaler-sync-period 플래그(기본값은 15초)에 의해 제어되는 주기를 가진 컨트롤 루프로 구현된다.

각 주기 동안 컨트롤러 관리자는 각 HorizontalPodAutoscaler 정의에 지정된 메트릭에 대해 리소스 사용률을 질의한다. 컨트롤러 관리자는 리소스 메트릭 API(파드 단위 리소스 메트릭 용) 또는 사용자 지정 메트릭 API(다른 모든 메트릭 용)에서 메트릭을 가져온다.

  • 파드 단위 리소스 메트릭(예 : CPU)의 경우 컨트롤러는 HorizontalPodAutoscaler가 대상으로하는 각 파드에 대한 리소스 메트릭 API에서 메트릭을 가져온다. 그런 다음, 목표 사용률 값이 설정되면, 컨트롤러는 각 파드의 컨테이너에 대한 동등한 자원 요청을 퍼센트 단위로 하여 사용률 값을 계산한다. 대상 원시 값이 설정된 경우 원시 메트릭 값이 직접 사용된다. 그리고, 컨트롤러는 모든 대상 파드에서 사용된 사용률의 평균 또는 원시 값(지정된 대상 유형에 따라 다름)을 가져와서 원하는 레플리카의 개수를 스케일하는데 사용되는 비율을 생성한다.

    파드의 컨테이너 중 일부에 적절한 리소스 요청이 설정되지 않은 경우, 파드의 CPU 사용률은 정의되지 않으며, 따라서 오토스케일러는 해당 메트릭에 대해 아무런 조치도 취하지 않는다. 오토스케일링 알고리즘의 작동 방식에 대한 자세한 내용은 아래 알고리즘 세부 정보 섹션을 참조하기 바란다.

  • 파드 단위 사용자 정의 메트릭의 경우, 컨트롤러는 사용률 값이 아닌 원시 값을 사용한다는 점을 제외하고는 파드 단위 리소스 메트릭과 유사하게 작동한다.

  • 오브젝트 메트릭 및 외부 메트릭의 경우, 문제의 오브젝트를 표현하는 단일 메트릭을 가져온다. 이 메트릭은 목표 값과 비교되어 위와 같은 비율을 생성한다. autoscaling/v2beta2 API 버전에서는, 비교가 이루어지기 전에 해당 값을 파드의 개수로 선택적으로 나눌 수 있다.

HorizontalPodAutoscaler는 보통 일련의 API 집합(metrics.k8s.io, custom.metrics.k8s.io, external.metrics.k8s.io)에서 메트릭을 가져온다. metrics.k8s.io API는 대개 별도로 시작해야 하는 메트릭-서버에 의해 제공된다. 더 자세한 정보는 메트릭-서버를 참조한다.

자세한 사항은 메트릭 API를 위한 지원을 참조한다.

오토스케일러는 스케일 하위 리소스를 사용하여 상응하는 확장 가능 컨트롤러(예: 레플리케이션 컨트롤러, 디플로이먼트, 레플리케이션 셋)에 접근한다. 스케일은 레플리카의 개수를 동적으로 설정하고 각 현재 상태를 검사 할 수 있게 해주는 인터페이스이다. 하위 리소스 스케일에 대한 자세한 내용은 여기에서 확인할 수 있다.

알고리즘 세부 정보

가장 기본적인 관점에서, Horizontal Pod Autoscaler 컨트롤러는 원하는(desired) 메트릭 값과 현재(current) 메트릭 값 사이의 비율로 작동한다.

원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]

예를 들어 현재 메트릭 값이 200m이고 원하는 값이 100m인 경우 200.0 / 100.0 == 2.0이므로 복제본 수가 두 배가 된다. 만약 현재 값이 50m 이면, 50.0 / 100.0 == 0.5 이므로 복제본 수를 반으로 줄일 것이다. 비율이 1.0(0.1을 기본값으로 사용하는 -horizontal-pod-autoscaler-tolerance 플래그를 사용하여 전역적으로 구성 가능한 허용 오차 내)에 충분히 가깝다면 스케일링을 건너 뛸 것이다.

targetAverageValue 또는 targetAverageUtilization가 지정되면, currentMetricValue는 HorizontalPodAutoscaler의 스케일 목표 안에 있는 모든 파드에서 주어진 메트릭의 평균을 취하여 계산된다. 허용치를 확인하고 최종 값을 결정하기 전에, 파드 준비 상태와 누락된 메트릭을 고려한다.

삭제 타임 스탬프가 설정된 모든 파드(즉, 종료 중인 파드) 및 실패한 파드는 모두 폐기된다.

특정 파드에 메트릭이 누락된 경우, 나중을 위해 처리를 미뤄두는데, 이와 같이 누락된 메트릭이 있는 모든 파드는 최종 스케일 량을 조정하는데 사용된다.

CPU를 스케일할 때, 어떤 파드라도 아직 준비가 안되었거나 (즉, 여전히 초기화 중인 경우) * 또는 * 파드의 최신 메트릭 포인트가 준비되기 전이라면, 마찬가지로 해당 파드는 나중에 처리된다.

기술적 제약으로 인해, HorizontalPodAutoscaler 컨트롤러는 특정 CPU 메트릭을 나중에 사용할지 말지 결정할 때, 파드가 준비되는 시작 시간을 정확하게 알 수 없다. 대신, 파드가 아직 준비되지 않았고 시작 이후 짧은 시간 내에 파드가 준비되지 않은 상태로 전환된다면, 해당 파드를 "아직 준비되지 않음(not yet ready)"으로 간주한다. 이 값은 --horizontal-pod-autoscaler-initial-readiness-delay 플래그로 설정되며, 기본값은 30초 이다. 일단 파드가 준비되고 시작된 후 구성 가능한 시간 이내이면, 준비를 위한 어떠한 전환이라도 이를 시작 시간으로 간주한다. 이 값은 --horizontal-pod-autoscaler-cpu-initialization-period 플래그로 설정되며 기본값은 5분이다.

현재 메트릭 값 / 원하는 메트릭 값 기본 스케일 비율은 나중에 사용하기로 되어 있거나 위에서 폐기되지 않은 남아있는 파드를 사용하여 계산된다.

누락된 메트릭이 있는 경우, 파드가 스케일 다운의 경우 원하는 값의 100%를 소비하고 스케일 업의 경우 0%를 소비한다고 가정하여 평균을 보다 보수적으로 재계산한다. 이것은 잠재적인 스케일의 크기를 약화시킨다.

또한 아직-준비되지-않은 파드가 있는 경우 누락된 메트릭이나 아직-준비되지-않은 파드를 고려하지 않고 스케일 업했을 경우, 아직-준비되지-않은 파드가 원하는 메트릭의 0%를 소비한다고 보수적으로 가정하고 스케일 확장의 크기를 약화시킨다.

아직-준비되지-않은 파드나 누락된 메트릭을 고려한 후에 사용 비율을 다시 계산한다. 새 비율이 스케일 방향을 바꾸거나, 허용 오차 내에 있으면 스케일링을 건너뛴다. 그렇지 않으면, 새 비율을 사용하여 스케일한다.

평균 사용량에 대한 원래 값은 새로운 사용 비율이 사용되는 경우에도 아직-준비되지-않은 파드 또는 누락된 메트릭에 대한 고려없이 HorizontalPodAutoscaler 상태를 통해 다시 보고된다.

HorizontalPodAutoscaler에 여러 메트릭이 지정된 경우, 이 계산은 각 메트릭에 대해 수행된 다음 원하는 레플리카 수 중 가장 큰 값이 선택된다. 이러한 메트릭 중 어떠한 것도 원하는 레플리카 수로 변환할 수 없는 경우(예 : 메트릭 API에서 메트릭을 가져오는 중 오류 발생) 스케일을 건너뛴다. 이는 하나 이상의 메트릭이 현재 값보다 높은 desiredReplicas 을 제공하는 경우 HPA가 여전히 확장할 수 있음을 의미한다.

마지막으로, HPA가 목표를 스케일하기 직전에 스케일 권장 사항이 기록된다. 컨트롤러는 구성 가능한 창(window) 내에서 가장 높은 권장 사항을 선택하도록 해당 창 내의 모든 권장 사항을 고려한다. 이 값은 --horizontal-pod-autoscaler-downscale-stabilization 플래그를 사용하여 설정할 수 있고, 기본값은 5분이다. 즉, 스케일 다운이 점진적으로 발생하여 급격히 변동하는 메트릭 값의 영향을 완만하게 한다.

API 오브젝트

Horizontal Pod Autoscaler는 쿠버네티스 autoscaling API 그룹의 API 리소스이다. CPU에 대한 오토스케일링 지원만 포함하는 안정된 버전은 autoscaling/v1 API 버전에서 찾을 수 있다.

메모리 및 사용자 정의 메트릭에 대한 스케일링 지원을 포함하는 베타 버전은 autoscaling/v2beta2에서 확인할 수 있다. autoscaling/v2beta2에서 소개된 새로운 필드는 autoscaling/v1로 작업할 때 어노테이션으로 보존된다.

HorizontalPodAutoscaler API 오브젝트 생성시 지정된 이름이 유효한 DNS 서브도메인 이름인지 확인해야 한다. API 오브젝트에 대한 자세한 내용은 HorizontalPodAutoscaler 오브젝트에서 찾을 수 있다.

kubectl에서 Horizontal Pod Autoscaler 지원

Horizontal Pod Autoscaler는 모든 API 리소스와 마찬가지로 kubectl에 의해 표준 방식으로 지원된다. kubectl create 커맨드를 사용하여 새로운 오토스케일러를 만들 수 있다. kubectl get hpa로 오토스케일러 목록을 조회할 수 있고, kubectl describe hpa로 세부 사항을 확인할 수 있다. 마지막으로 kubectl delete hpa를 사용하여 오토스케일러를 삭제할 수 있다.

또한 Horizontal Pod Autoscaler를 생성할 수 있는 kubectl autoscale이라는 특별한 명령이 있다. 예를 들어 kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80을 실행하면 레플리케이션 셋 foo 에 대한 오토스케일러가 생성되고, 목표 CPU 사용률은 80 %, 그리고 2와 5 사이의 레플리카 개수로 설정된다. kubectl autoscale에 대한 자세한 문서는 여기에서 찾을 수 있다.

롤링 업데이트 중 오토스케일링

쿠버네티스는 디플로이먼트에 대한 롤링 업데이트를 지원한다. 이 경우, 디플로이먼트가 기저 레플리카셋을 알아서 관리한다. 디플로이먼트에 오토스케일링을 설정하려면, 각 디플로이먼트에 대한 HorizontalPodAutoscaler를 생성한다. HorizontalPodAutoscaler는 디플로이먼트의 replicas 필드를 관리한다. 디플로이먼트 컨트롤러는 기저 레플리카셋에 replicas 값을 적용하여 롤아웃 과정 중/이후에 적절한 숫자까지 늘어나도록 한다.

오토스케일된 레플리카가 있는 스테이트풀셋의 롤링 업데이트를 수행하면, 스테이트풀셋이 직접 파드의 숫자를 관리한다(즉, 레플리카셋과 같은 중간 리소스가 없다).

쿨-다운 / 지연에 대한 지원

Horizontal Pod Autoscaler를 사용하여 레플리카 그룹의 스케일을 관리할 때, 평가된 메트릭의 동적인 특징 때문에 레플리카 수가 자주 변동할 수 있다. 이것은 때로는 스래싱 (thrashing) 이라고도 한다.

v1.6 부터 클러스터 운영자는 kube-controller-manager 컴포넌트의 플래그로 노출된 글로벌 HPA 설정을 튜닝하여 이 문제를 완화할 수 있다.

v1.12부터는 새로운 알고리즘 업데이트가 업스케일 지연에 대한 필요성을 제거하였다.

  • --horizontal-pod-autoscaler-downscale-delay : 다운스케일이 안정화되기까지의 시간 간격을 지정한다. Horizontal Pod Autoscaler는 이전의 권장하는 크기를 기억하고, 이 시간 간격에서의 가장 큰 크기에서만 작동한다. 기본값은 5분(5m0s)이다.

리소스 메트릭 지원

모든 HPA 대상은 스케일링 대상에서 파드의 리소스 사용량을 기준으로 스케일링할 수 있다. 파드의 명세를 정의할 때는 cpumemory 와 같은 리소스 요청을 지정해야 한다. 이것은 리소스 사용률을 결정하는 데 사용되며 HPA 컨트롤러에서 대상을 스케일링하거나 축소하는 데 사용한다. 리소스 사용률 기반 스케일링을 사용하려면 다음과 같은 메트릭 소스를 지정해야 한다.

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

이 메트릭을 사용하면 HPA 컨트롤러는 스케일링 대상에서 파드의 평균 사용률을 60%로 유지한다. 사용률은 파드의 요청된 리소스에 대한 현재 리소스 사용량 간의 비율이다. 사용률 계산 및 평균 산출 방법에 대한 자세한 내용은 알고리즘을 참조한다.

컨테이너 리소스 메트릭

FEATURE STATE: Kubernetes v1.20 [alpha]

HorizontalPodAutoscaler 는 대상 리소스를 스케일링하기 위해 HPA가 파드 집합에서 개별 컨테이너의 리소스 사용량을 추적할 수 있는 컨테이너 메트릭 소스도 지원한다. 이를 통해 특정 파드에서 가장 중요한 컨테이너의 스케일링 임계값을 구성할 수 있다. 예를 들어 웹 애플리케이션 프로그램과 로깅 사이드카가 있는 경우 사이드카 컨테이너와 해당 리소스 사용을 무시하고 웹 애플리케이션의 리소스 사용을 기준으로 스케일링할 수 있다.

대상 리소스를 다른 컨테이너 세트를 사용하여 새 파드 명세를 갖도록 수정하는 경우 새로 추가된 컨테이너도 스케일링에 사용해야 한다면 HPA 사양을 수정해야 한다. 메트릭 소스에 지정된 컨테이너가 없거나 파드의 하위 집합에만 있는 경우 해당 파드는 무시되고 권장 사항이 다시 계산된다. 계산에 대한 자세한 내용은 알고리즘을 을 참조한다. 컨테이너 리소스를 오토스케일링에 사용하려면 다음과 같이 메트릭 소스를 정의한다.

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

위의 예에서 HPA 컨트롤러는 모든 파드의 application 컨테이너에 있는 CPU의 평균 사용률이 60%가 되도록 대상을 조정한다.

멀티 메트릭을 위한 지원

Kubernetes 1.6은 멀티 메트릭을 기반으로 스케일링을 지원한다. autoscaling/v2beta2 API 버전을 사용하여 Horizontal Pod Autoscaler가 스케일을 조정할 멀티 메트릭을 지정할 수 있다. 그런 다음 Horizontal Pod Autoscaler 컨트롤러가 각 메트릭을 평가하고, 해당 메트릭을 기반으로 새 스케일을 제안한다. 제안된 스케일 중 가장 큰 것이 새로운 스케일로 사용된다.

사용자 정의 메트릭을 위한 지원

쿠버네티스 1.6에서는 Horizontal Pod Autoscaler에서 사용자 정의 메트릭을 사용할 수 있도록 지원한다. autoscaling/v2beta2 API에서 사용할 Horizontal Pod Autoscaler에 대한 사용자 정의 메트릭을 추가 할 수 있다. 그리고 쿠버네티스는 새 사용자 정의 메트릭 API에 질의하여 적절한 사용자 정의 메트릭의 값을 가져온다.

요구 사항은 메트릭을 위한 지원을 참조한다.

메트릭 API를 위한 지원

기본적으로 HorizontalPodAutoscaler 컨트롤러는 일련의 API에서 메트릭을 검색한다. 이러한 API에 접속하려면 클러스터 관리자는 다음을 확인해야 한다.

  • API 애그리게이션 레이어 활성화

  • 해당 API 등록:

    • 리소스 메트릭의 경우, 일반적으로 이것은 메트릭-서버가 제공하는 metrics.k8s.io API이다. 클러스터 애드온으로 시작할 수 있다.

    • 사용자 정의 메트릭의 경우, 이것은 custom.metrics.k8s.io API이다. 메트릭 솔루션 공급 업체에서 제공하는 "어댑터" API 서버에서 제공한다. 메트릭 파이프라인 또는 알려진 솔루션 목록으로 확인한다. 직접 작성하고 싶다면 샘플을 확인한다.

    • 외부 메트릭의 경우, 이것은 external.metrics.k8s.io API이다. 위에 제공된 사용자 정의 메트릭 어댑터에서 제공될 수 있다.

이런 다양한 메트릭 경로와 각각의 다른 점에 대한 상세 내용은 관련 디자인 제안서인 HPA V2, custom.metrics.k8s.io, external.metrics.k8s.io를 참조한다.

어떻게 사용하는지에 대한 예시는 커스텀 메트릭 사용하는 작업 과정외부 메트릭스 사용하는 작업 과정을 참조한다.

구성가능한 스케일링 동작 지원

v1.18 부터 v2beta2 API는 HPA behavior 필드를 통해 스케일링 동작을 구성할 수 있다. 동작은 behavior 필드 아래의 scaleUp 또는 scaleDown 섹션에서 스케일링 업과 다운을 위해 별도로 지정된다. 안정화 윈도우는 스케일링 대상에서 레플리카 수의 플래핑(flapping)을 방지하는 양방향에 대해 지정할 수 있다. 마찬가지로 스케일링 정책을 지정하면 스케일링 중 레플리카 변경 속도를 제어할 수 있다.

스케일링 정책

스펙의 behavior 섹션에 하나 이상의 스케일링 폴리시를 지정할 수 있다. 폴리시가 여러 개 지정된 경우 가장 많은 양의 변경을 허용하는 정책이 기본적으로 선택된 폴리시이다. 다음 예시는 스케일 다운 중 이 동작을 보여준다.

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds 는 폴리시가 참(true)으로 유지되어야 하는 기간을 나타낸다. 첫 번째 정책은 (파드들) 이 1분 내에 최대 4개의 레플리카를 스케일 다운할 수 있도록 허용한다. 두 번째 정책은 비율 로 현재 레플리카의 최대 10%를 1분 내에 스케일 다운할 수 있도록 허용한다.

기본적으로 가장 많은 변경을 허용하는 정책이 선택되기에 두 번째 정책은 파드의 레플리카 수가 40개를 초과하는 경우에만 사용된다. 레플리카가 40개 이하인 경우 첫 번째 정책이 적용된다. 예를 들어 80개의 레플리카가 있고 대상을 10개의 레플리카로 축소해야 하는 경우 첫 번째 단계에서 8개의 레플리카가 스케일 다운 된다. 레플리카의 수가 72개일 때 다음 반복에서 파드의 10%는 7.2 이지만, 숫자는 8로 올림된다. 오토스케일러 컨트롤러의 각 루프에서 변경될 파드의 수는 현재 레플리카의 수에 따라 재계산된다. 레플리카의 수가 40 미만으로 떨어지면 첫 번째 폴리시 (파드들) 가 적용되고 한번에 4개의 레플리카가 줄어든다.

확장 방향에 대해 selectPolicy 필드를 확인하여 폴리시 선택을 변경할 수 있다. 레플리카의 수를 최소로 변경할 수 있는 폴리시를 선택하는 최소(Min)로 값을 설정한다. 값을 Disabled 로 설정하면 해당 방향으로 스케일링이 완전히 비활성화 된다.

안정화 윈도우

안정화 윈도우는 스케일링에 사용되는 메트릭이 계속 변동할 때 레플리카의 플래핑을 다시 제한하기 위해 사용된다. 안정화 윈도우는 스케일링을 방지하기 위해 과거부터 계산된 의도한 상태를 고려하는 오토스케일링 알고리즘에 의해 사용된다. 다음의 예시에서 scaleDown 에 대해 안정화 윈도우가 지정되어 있다.

scaleDown:
  stabilizationWindowSeconds: 300

메트릭이 대상을 축소해야하는 것을 나타내는 경우 알고리즘은 이전에 계산된 의도한 상태를 살펴보고 지정된 간격의 최고 값을 사용한다. 위의 예시에서 지난 5분 동안 모든 의도한 상태가 고려된다.

기본 동작

사용자 지정 스케일링을 사용하려면 일부 필드를 지정해야 한다. 사용자 정의해야 하는 값만 지정할 수 있다. 이러한 사용자 지정 값은 기본값과 병합된다. 기본값은 HPA 알고리즘의 기존 동작과 일치한다.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

안정화 윈도우의 스케일링 다운의 경우 300 초 (또는 제공된 경우--horizontal-pod-autoscaler-downscale-stabilization 플래그의 값)이다. 스케일링 다운에서는 현재 실행 중인 레플리카의 100%를 제거할 수 있는 단일 정책만 있으며, 이는 스케일링 대상을 최소 허용 레플리카로 축소할 수 있음을 의미한다. 스케일링 업에는 안정화 윈도우가 없다. 메트릭이 대상을 스케일 업해야 한다고 표시된다면 대상이 즉시 스케일 업된다. 두 가지 폴리시가 있다. HPA가 정상 상태에 도달 할 때까지 15초 마다 4개의 파드 또는 현재 실행 중인 레플리카의 100% 가 추가된다.

예시: 다운스케일 안정화 윈도우 변경

사용자 지정 다운스케일 안정화 윈도우를 1분 동안 제공하기 위해 다음 동작이 HPA에 추가된다.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

예시: 스케일 다운 비율 제한

HPA에 의해 파드가 제거되는 속도를 분당 10%로 제한하기 위해 다음 동작이 HPA에 추가된다.

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

마지막으로 5개의 파드를 드롭하기 위해 다른 폴리시를 추가하고, 최소 선택 전략을 추가할 수 있다. 분당 5개 이하의 파드가 제거되지 않도록, 고정 크기가 5인 두 번째 축소 정책을 추가하고, selectPolicy 를 최소로 설정하면 된다. selectPolicyMin 으로 설정하면 자동 스케일러가 가장 적은 수의 파드에 영향을 주는 정책을 선택함을 의미한다.

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

예시: 스케일 다운 비활성화

selectPolicyDisabled 값은 주어진 방향으로의 스케일링을 끈다. 따라서 다운 스케일링을 방지하기 위해 다음 폴리시가 사용된다.

behavior:
  scaleDown:
    selectPolicy: Disabled

암시적 유지 관리 모드 비활성화

HPA 구성 자체를 변경할 필요없이 대상에 대한 HPA를 암시적으로 비활성화할 수 있다. 대상의 의도한 레플리카 수가 0으로 설정되고, HPA의 최소 레플리카 수가 0 보다 크면, 대상의 의도한 레플리카 수 또는 HPA의 최소 레플리카 수를 수동으로 조정하여 다시 활성화할 때까지 HPA는 대상 조정을 중지한다(그리고 ScalingActive 조건 자체를 false로 설정).

다음 내용

4.7.6 - Horizontal Pod Autoscaler 연습

Horizontal Pod Autoscaler는 CPU 사용량(또는 베타 지원의 다른 애플리케이션 지원 메트릭)을 관찰하여 레플리케이션 컨트롤러, 디플로이먼트, 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)의 파드 개수를 자동으로 스케일한다.

이 문서는 php-apache 서버를 대상으로 Horizontal Pod Autoscaler를 동작해보는 예제이다. Horizontal Pod Autoscaler 동작과 관련된 더 많은 정보를 위해서는 Horizontal Pod Autoscaler 사용자 가이드를 참고하기 바란다.

시작하기 전에

이 예제는 버전 1.2 또는 이상의 쿠버네티스 클러스터와 kubectl을 필요로 한다. 메트릭 서버 모니터링을 클러스터에 배포하여 메트릭 API를 통해 메트릭을 제공해야 한다. Horizontal Pod Autoscaler가 메트릭을 수집할때 해당 API를 사용한다. 메트릭-서버를 배포하는 방법에 대해 배우려면, 메트릭-서버 문서를 참고한다.

Horizontal Pod Autoscaler에 다양한 자원 메트릭을 적용하고자 하는 경우, 버전 1.6 또는 이상의 쿠버네티스 클러스터와 kubectl를 사용해야 한다. 사용자 정의 메트릭을 사용하기 위해서는, 클러스터가 사용자 정의 메트릭 API를 제공하는 API 서버와 통신할 수 있어야 한다. 마지막으로 쿠버네티스 오브젝트와 관련이 없는 메트릭을 사용하는 경우, 버전 1.10 또는 이상의 쿠버네티스 클러스터와 kubectl을 사용해야 하며, 외부 메트릭 API와 통신이 가능해야 한다. 자세한 사항은 Horizontal Pod Autoscaler 사용자 가이드를 참고하길 바란다.

php-apache 서버 구동 및 노출

Horizontal Pod Autoscaler 시연을 위해 php-apache 이미지를 맞춤 제작한 Docker 이미지를 사용한다. Dockerfile은 다음과 같다.

FROM php:5-apache
COPY index.php /var/www/html/index.php
RUN chmod a+rx index.php

index.php는 CPU 과부하 연산을 수행한다.

<?php
  $x = 0.0001;
  for ($i = 0; $i <= 1000000; $i++) {
    $x += sqrt($x);
  }
  echo "OK!";
?>

첫 번째 단계로, 다음 구성을 사용해서 실행 중인 이미지의 디플로이먼트를 시작하고 서비스로 노출시킨다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  replicas: 1
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: k8s.gcr.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m

---

apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

다음의 명령어를 실행한다.

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created

Horizontal Pod Autoscaler 생성

이제 서비스가 동작중이므로, kubectl autoscale를 사용하여 오토스케일러를 생성한다. 다음 명령어는 첫 번째 단계에서 만든 php-apache 디플로이먼트 파드의 개수를 1부터 10 사이로 유지하는 Horizontal Pod Autoscaler를 생성한다. 간단히 얘기하면, HPA는 (디플로이먼트를 통한) 평균 CPU 사용량을 50%로 유지하기 위하여 레플리카의 개수를 늘리고 줄인다. kubectl run으로 각 파드는 200 밀리코어를 요청하므로, 여기서 말하는 평균 CPU 사용은 100 밀리코어를 말한다. 이에 대한 자세한 알고리즘은 여기를 참고하기 바란다.

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled

실행 중인 오토스케일러의 현재 상태를 확인해본다.

kubectl get hpa
NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

아직 서버로 어떠한 요청도 하지 않았기 때문에, 현재 CPU 소비는 0%임을 확인할 수 있다. (TARGET은 디플로이먼트에 의해 제어되는 파드들의 평균을 나타낸다)

부하 증가

이번에는 부하가 증가함에 따라 오토스케일러가 어떻게 반응하는지를 살펴볼 것이다. 먼저 컨테이너를 하나 실행하고, php-apache 서비스에 무한루프의 쿼리를 전송한다(다른 터미널을 열어 수행하기 바란다).

kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

실행 후, 약 1분 정도 후에 CPU 부하가 올라가는 것을 볼 수 있다.

kubectl get hpa
NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

CPU 소비가 305%까지 증가하였다. 결과적으로, 디플로이먼트의 레플리카 개수는 7개까지 증가하였다.

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

부하 중지

본 예제를 마무리하기 위해 부하를 중단시킨다.

busybox 컨테이너를 띄운 터미널에서, <Ctrl> + C로 부하 발생을 중단시킨다.

그런 다음 (몇 분 후에) 결과를 확인한다.

kubectl get hpa
NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m
kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

CPU 사용량은 0으로 떨어졌고, HPA는 레플리카의 개수를 1로 낮췄다.

다양한 메트릭 및 사용자 정의 메트릭을 기초로한 오토스케일링

php-apache 디플로이먼트를 오토스케일링할 때, autoscaling/v2beta2 API 버전을 사용하여 추가적인 메트릭을 제공할 수 있다.

첫 번째로, autoscaling/v2beta2 형식으로 HorizontalPodAutoscaler YAML 파일을 생성한다.

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

에디터로 /tmp/hpa-v2.yaml 파일을 열면, 다음과 같은 YAML을 확인할 수 있다.

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
      current:
        averageUtilization: 0
        averageValue: 0

targetCPUUtilizationPercentage 필드가 metrics 배열로 대체되었다. CPU 사용량 메트릭은 resource metric 으로 파드 컨테이너 자원의 백분율로 표현된다. CPU 외에 다른 메트릭을 지정할 수 있는데, 기본적으로 지원되는 다른 메트릭은 메모리뿐이다. 이 자원들은 한 클러스터에서 다른 클러스터로 이름을 변경할 수 없으며, metrics.k8s.io API가 가용한 경우 언제든지 사용할 수 있어야 한다.

또한, Utilization 대신 AverageValuetarget 타입을, 그리고 target.averageUtilization 대신 target.averageValue로 설정하여 자원 메트릭을 퍼센트 대신 값으로 명시할 수 있다.

파드 메트릭과 오브젝트 메트릭 두 가지의 사용자 정의 메트릭 이 있다. 파드 메트릭과 오브젝트 메트릭. 이 메트릭은 클러스터에 특화된 이름을 가지고 있으며, 더 고급화된 클러스터 모니터링 설정이 필요하다.

이러한 대체 메트릭 타입중 첫 번째는 파드 메트릭 이다. 이 메트릭은 파드들을 설명하고, 파드들 간의 평균을 내며, 대상 값과 비교하여 레플리카 개수를 결정한다. 이것들은 AverageValuetarget만을 지원한다는 것을 제외하면, 자원 메트릭과 매우 유사하게 동작한다.

파드 메트릭은 이처럼 메트릭 블록을 사용하여 정의된다.

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

두 번째 대체 메트릭 타입은 오브젝트 메트릭 이다. 이 메트릭은 파드를 기술하는 대신에 동일한 네임스페이스 내에 다른 오브젝트를 표현한다. 이 메트릭은 반드시 오브젝트로부터 가져올 필요는 없다. 단지 오브젝트를 기술할 뿐이다. 오브젝트 메트릭은 ValueAverageValuetarget 타입을 지원한다. Value를 사용할 경우 대상은 API로부터 반환되는 메트릭과 직접 비교된다. AverageValue를 사용할 경우, 대상 값과 비교되기 이전에 사용자 정의 메트릭 API로부터 반환된 값은 파드의 개수로 나눠진다. 다음은 requests-per-second 메트릭을 YAML로 기술한 예제이다.

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

이러한 메트릭 블록을 여러 개 제공하면, HorizontalPodAutoscaler는 각 메트릭을 차례로 고려한다. HorizontalPodAutoscaler는 각 메트릭에 대해 제안된 레플리카 개수를 계산하고, 그중 가장 높은 레플리카 개수를 선정한다.

예를 들어, 네트워크 트래픽 메트릭을 수집하는 모니터링 시스템이 있는 경우, kubectl edit 명령어를 이용하여 다음과 같이 정의를 업데이트 할 수 있다.

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      current:
        value: 10k

이후, HorizontalPodAutoscaler는 각 파드가 요청 된 약 50%의 CPU 사용률을 소모하는지, 초당 1000 패킷을 처리하는지, 메인-루트 인그레스 뒤의 모든 파드들이 초당 10000 요청을 처리하는지 확인한다.

보다 구체적인 메트릭을 기초로한 오토스케일링

많은 메트릭 파이프라인들을 사용하면 이름 또는 labels 이라 불리는 추가적인 식별자로 메트릭을 설명할 수 있다. 그리고, 모든 비 자원 메트릭 타입(파드, 오브젝트 그리고 아래 기술된 외부 타입)에 대해, 메트릭 파이프라인으로 전달되는 추가 레이블 셀렉터를 지정할 수 있다. 예를 들면, verb 레이블로 http_requests 메트릭을 수집하는 경우, 다음과 같이 메트릭 블록을 지정하여 GET 요청에 대해 크기를 조정할 수 있다.

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

이 셀렉터는 쿠버네티스의 레이블 셀렉터와 동일한 문법이다. 모니터링 파이프라인은 네임과 셀렉터가 여러 시리즈와 일치하는 경우, 해당 여러 시리즈를 단일 값으로 축소하는 방법을 결정한다. 셀렉터는 부가적인 속성이며, 대상 오브젝트(Pods 타입의 대상 파드, Object 타입으로 기술된 오브젝트)가 아닌 메트릭을 선택할 수 없다.

쿠버네티스 오브젝트와 관련이 없는 메트릭을 기초로한 오토스케일링

쿠버네티스 위에서 동작하는 애플리케이션은, 쿠버네티스 클러스터의 어떤 오브젝트와도 관련이 없는 메트릭에 기반하여 오토스케일링을 할 수도 있다. 예로, 쿠버네티스 네임스페이스와 관련이 없는 서비스를 기초로한 메트릭을 들 수 있다. 쿠버네티스 버전 1.10 포함 이후 버전에서, 외부 메트릭 을 사용하여 이러한 유스케이스를 해결할 수 있다.

외부 메트릭 사용시, 먼저 모니터링 시스템에 대한 이해가 있어야 한다. 이 설치는 사용자 정의 메트릭과 유사하다. 외부 메트릭을 사용하면 모니터링 시스템의 사용 가능한 메트릭에 기반하여 클러스터를 오토스케일링 할 수 있다. 위의 예제처럼 nameselector를 갖는 metric 블록을 명시하고, Object 대신에 External 메트릭 타입을 사용한다. 만일 여러 개의 시계열이 metricSelector와 일치하면, HorizontalPodAutoscaler가 값의 합을 사용한다. 외부 메트릭들은 ValueAverageValue 대상 타입을 모두 지원하고, Object 타입을 사용할 때와 똑같이 동작한다.

예를 들면 애플리케이션이 호스팅 된 대기열 서비스에서 작업을 처리하는 경우, 다음과 같이 HorizontalPodAutoscaler 매니퍼스트에 30개의 미해결 태스크 당 한 개의 워커를 지정하도록 추가할 수 있다.

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

가능하다면, 외부 메트릭 대신 사용자 정의 메트릭 대상 타입을 사용하길 권장한다. 왜냐하면, 클러스터 관리자가 사용자 정의 메트릭 API를 보안관점에서 더 쉽게 보호할 수 있기 때문이다. 외부 메트릭 API는 잠재적으로 어떠한 메트릭에도 접근할 수 있기에, 클러스터 관리자는 API를 노출시킬때 신중해야 한다.

부록: Horizontal Pod Autoscaler 상태 조건

HorizontalPodAutoscaler의 autoscaling/v2beta2 형식을 사용하면, HorizontalPodAutoscaler에서 쿠버네티스가 설정한 상태 조건 을 확인할 수 있다. 이 상태 조건들은 HorizontalPodAutoscaler가 스케일을 할 수 있는지, 어떤 방식으로든 제한되어 있는지 여부를 나타낸다.

이 조건은 status.conditions에 나타난다. HorizontalPodAutoscaler에 영향을 주는 조건을 보기 위해 kubectl describe hpa를 사용할 수 있다.

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

이 HorizontalPodAutoscaler 경우, 건강 상태의 여러 조건들을 볼 수 있다. 첫 번째 AbleToScale는 HPA가 스케일을 가져오고 업데이트할 수 있는지, 백 오프 관련 조건으로 스케일링이 방지되는지 여부를 나타낸다. 두 번째 ScalingActive는 HPA가 활성화되어 있는지(즉 대상 레플리카 개수가 0이 아닌지), 원하는 스케일을 계산할 수 있는지 여부를 나타낸다. 만약 False 인 경우, 일반적으로 메트릭을 가져오는데 문제가 있다. 마지막으로, 마지막 조건인 ScalingLimited는 원하는 스케일 한도가 HorizontalPodAutoscaler의 최대/최소값으로 제한돼있음을 나타낸다. 이는 HorizontalPodAutoscaler에서 레플리카의 개수 제한을 최대/최소값으로 올리거나 낮추려는 것이다.

부록: 수량

HorizontalPodAutoscaler와 메트릭 API에서 모든 메트릭은 쿠버네티스에서 사용하는 수량 숫자 표기법을 사용한다. 예를 들면, 10500m 수량은 10진법 10.5으로 쓰인다. 메트릭 API들은 가능한 경우 접미사 없이 정수를 반환하며, 일반적으로 수량을 밀리단위로 반환한다. 10진수로 표현했을때, 11500m 또는 11.5 로 메트릭 값을 나타낼 수 있다.

부록: 다른 가능한 시나리오

명시적으로 오토스케일러 만들기

HorizontalPodAutoscaler를 생성하기 위해 kubectl autoscale 명령어를 사용하지 않고, 명시적으로 다음 파일을 사용하여 만들 수 있다.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

다음 명령어를 실행하여 오토스케일러를 생성할 것이다.

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

4.7.7 - 파드 내에서 쿠버네티스 API에 접근

이 페이지는 파드 내에서 쿠버네티스 API에 접근하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

파드 내에서 API에 접근

파드 내에서 API에 접근할 때, API 서버를 찾아 인증하는 것은 위에서 설명한 외부 클라이언트 사례와 약간 다르다.

파드에서 쿠버네티스 API를 사용하는 가장 쉬운 방법은 공식 클라이언트 라이브러리 중 하나를 사용하는 것이다. 이러한 라이브러리는 API 서버를 자동으로 감지하고 인증할 수 있다.

공식 클라이언트 라이브러리 사용

파드 내에서, 쿠버네티스 API에 연결하는 권장 방법은 다음과 같다.

각각의 경우, 파드의 서비스 어카운트 자격 증명은 API 서버와 안전하게 통신하는 데 사용된다.

REST API에 직접 접근

파드에서 실행되는 동안, 쿠버네티스 apiserver는 default 네임스페이스에서 kubernetes라는 서비스를 통해 접근할 수 있다. 따라서, 파드는 kubernetes.default.svc 호스트 이름을 사용하여 API 서버를 쿼리할 수 있다. 공식 클라이언트 라이브러리는 이를 자동으로 수행한다.

API 서버를 인증하는 권장 방법은 서비스 어카운트 자격 증명을 사용하는 것이다. 기본적으로, 파드는 서비스 어카운트와 연결되어 있으며, 해당 서비스 어카운트에 대한 자격 증명(토큰)은 해당 파드에 있는 각 컨테이너의 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/token 에 있다.

사용 가능한 경우, 인증서 번들은 각 컨테이너의 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 에 배치되며, API 서버의 제공 인증서를 확인하는 데 사용해야 한다.

마지막으로, 네임스페이스가 지정된 API 작업에 사용되는 기본 네임스페이스는 각 컨테이너의 /var/run/secrets/kubernetes.io/serviceaccount/namespace 에 있는 파일에 배치된다.

kubectl 프록시 사용

공식 클라이언트 라이브러리 없이 API를 쿼리하려면, 파드에서 새 사이드카 컨테이너의 명령으로 kubectl proxy 를 실행할 수 있다. 이런 식으로, kubectl proxy 는 API를 인증하고 이를 파드의 localhost 인터페이스에 노출시켜서, 파드의 다른 컨테이너가 직접 사용할 수 있도록 한다.

프록시를 사용하지 않고 접근

인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다. 내부 인증서는 연결을 보호한다.

# 내부 API 서버 호스트 이름을 가리킨다
APISERVER=https://kubernetes.default.svc

# 서비스어카운트(ServiceAccount) 토큰 경로
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# 이 파드의 네임스페이스를 읽는다
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# 서비스어카운트 베어러 토큰을 읽는다
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# 내부 인증 기관(CA)을 참조한다
CACERT=${SERVICEACCOUNT}/ca.crt

# TOKEN으로 API를 탐색한다
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

출력은 다음과 비슷하다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

4.8 - 잡(Job) 실행

병렬 처리를 사용하여 잡을 실행한다.

4.8.1 - 크론잡(CronJob)으로 자동화된 작업 실행

쿠버네티스 버전 1.21에서 크론잡이 GA (General Availability)로 승격되었다. 이전 버전의 쿠버네티스를 사용하고 있다면, 해당 쿠버네티스 버전의 문서를 참고하여 정확한 정보를 확인할 수 있다. 이전 버전의 쿠버네티스는 batch/v1 크론잡 API를 지원하지 않는다.

시간 기반의 스케줄에 따라 크론잡을 이용해서 잡(Job)을 실행할 수 있다. 이러한 자동화된 잡은 리눅스 또는 유닉스 시스템에서 크론 작업처럼 실행된다.

크론 잡은 백업을 수행하거나 이메일을 보내는 것과 같이 주기적이고 반복적인 작업들을 생성하는 데 유용하다. 크론 잡은 시스템 사용이 적은 시간에 잡을 스케줄하려는 경우처럼 특정 시간에 개별 작업을 스케줄할 수도 있다.

크론 잡에는 제한 사항과 특이점이 있다. 예를 들어, 특정 상황에서는 하나의 크론 잡이 여러 잡을 생성할 수 있다. 따라서, 잡은 멱등성을 가져야 한다.

제한 사항에 대한 자세한 내용은 크론잡을 참고한다.

시작하기 전에

  • 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

크론 잡 생성

크론 잡은 구성 파일이 필요하다. 아래의 크론 잡 구성 .spec 파일의 예제는 매 분마다 현재 시간과 hello 메시지를 출력한다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

다음 명령을 사용하여 크론잡 예제를 실행한다.

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml

출력 결과는 다음과 비슷하다.

cronjob.batch/hello created

크론 잡을 생성한 후, 다음 명령을 사용하여 상태를 가져온다.

kubectl get cronjob hello

출력 결과는 다음과 비슷하다.

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          10s

명령의 결과에서 알 수 있듯이, 크론 잡은 아직 잡을 스케줄하거나 실행하지 않았다. 약 1분 내로 잡이 생성되는지 확인한다.

kubectl get jobs --watch

출력 결과는 다음과 비슷하다.

NAME               COMPLETIONS   DURATION   AGE
hello-4111706356   0/1                      0s
hello-4111706356   0/1           0s         0s
hello-4111706356   1/1           5s         5s

이제 "hello" 크론 잡에 의해 스케줄된 실행 중인 작업을 확인했다. 잡 감시를 중지한 뒤에 크론 잡이 다시 스케줄되었는지를 확인할 수 있다.

kubectl get cronjob hello

출력 결과는 다음과 비슷하다.

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        50s             75s

크론 잡 helloLAST SCHEDULE 에 지정된 시간에 성공적으로 잡을 스케줄했는지 확인해야 한다. 현재는 0개의 활성 잡이 있고, 이는 작업이 완료되었거나 실패했음을 의미한다.

이제, 마지막으로 스케줄된 잡이 생성한 파드를 찾고 생성된 파드 중 하나의 표준 출력을 확인한다.

# "hello-4111706356"을 사용자의 시스템에 있는 잡 이름으로 바꾼다
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})

파드의 로그를 출력한다.

kubectl logs $pods

출력 결과는 다음과 비슷하다.

Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster

크론 잡 삭제

더 이상 크론 잡이 필요하지 않으면, kubectl delete cronjob <cronjob name> 명령을 사용해서 삭제한다.

kubectl delete cronjob hello

크론 잡을 삭제하면 생성된 모든 잡과 파드가 제거되고 추가 잡 생성이 중지된다. 가비지(garbage) 수집에서 잡 제거에 대해 상세한 내용을 읽을 수 있다.

크론 잡 명세 작성

다른 모든 쿠버네티스 구성과 마찬가지로, 크론 잡은 apiVersion, kind 그리고 metadata 필드가 필요하다. 구성 파일 작업에 대한 일반적인 정보는 애플리케이션 배포kubectl을 사용하여 리소스 관리하기 문서를 참고한다.

크론 잡 구성에는 .spec 섹션도 필요하다.

스케줄

.spec.schedule.spec 의 필수 필드이다. 이는 해당 잡이 생성되고 실행되는 스케줄 시간으로 0 * * * * 또는 @hourly 와 같이 크론 형식의 문자열을 받아들인다.

이 형식은 확장된 "Vixie cron" 스텝(step) 값도 포함한다. 이 내용은 FreeBSD 매뉴얼에 설명되어 있다.

스텝 값은 범위(range)와 함께 사용할 수 있다. 범위 뒤에 /<number> 를 지정하여 범위 내에서 숫자만큼의 값을 건너뛴다. 예를 들어, 시간 필드에 0-23/2 를 사용하여 매 2시간마다 명령 실행을 지정할 수 있다(V7 표준의 대안은 0,2,4,6,8,10,12,14,16,18,20,22 이다). 별표(asterisk) 뒤에 붙이는 스텝도 허용되며, "2시간마다"라고 지정하고 싶으면, 간단히 */2 를 사용하면 된다.

잡 템플릿

.spec.jobTemplate 은 잡에 대한 템플릿이며, 이것은 필수 필드다. 이것은 중첩되고 apiVersion 이나 kind 가 없는 것을 제외하고 과 정확히 같은 스키마를 가진다. 잡 .spec 을 작성하는 것에 대한 내용은 잡 명세 작성하기를 참고한다.

시작 기한

.spec.startingDeadlineSeconds 필드는 선택 사항이다. 어떤 이유로든 스케줄된 시간을 놓친 경우 잡의 시작 기한을 초 단위로 나타낸다. 기한이 지나면, 크론 잡이 잡을 시작하지 않는다. 이러한 방식으로 기한을 맞추지 못한 잡은 실패한 작업으로 간주된다. 이 필드를 지정하지 않으면, 잡에 기한이 없다.

.spec.startingDeadlineSeconds 필드가 (null이 아닌 값으로) 설정되어 있다면, 크론잡 컨트롤러는 잡 생성 완료 예상 시각과 현재 시각의 차이를 측정하고, 시각 차이가 설정한 값보다 커지면 잡 생성 동작을 스킵한다.

예를 들어, 200 으로 설정되었다면, 잡 생성 완료 예상 시각으로부터 200초까지는 잡이 생성될 수 있다.

동시성 정책

.spec.concurrencyPolicy 필드도 선택 사항이다. 이것은 이 크론 잡에 의해 생성된 잡의 동시 실행을 처리하는 방법을 지정한다. 명세는 다음의 동시성 정책 중 하나만 지정할 수 있다.

  • Allow(기본값): 크론 잡은 동시에 실행되는 잡을 허용한다.
  • Forbid: 크론 잡은 동시 실행을 허용하지 않는다. 새로운 잡을 실행할 시간이고 이전 잡 실행이 아직 완료되지 않은 경우, 크론 잡은 새로운 잡 실행을 건너뛴다.
  • Replace: 새로운 잡을 실행할 시간이고 이전 잡 실행이 아직 완료되지 않은 경우, 크론 잡은 현재 실행 중인 잡 실행을 새로운 잡 실행으로 대체한다.

참고로 동시성 정책은 동일한 크론 잡에 의해 생성된 잡에만 적용된다. 크론 잡이 여러 개인 경우, 각각의 잡은 항상 동시에 실행될 수 있다.

일시 정지

.spec.suspend 필드도 선택 사항이다. true 로 설정되면, 모든 후속 실행이 일시 정지된다. 이 설정은 이미 시작된 실행에는 적용되지 않는다. 기본값은 false이다.

잡 히스토리 한도

.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit 필드는 선택 사항이다. 이들 필드는 기록을 보관해야 하는 완료 및 실패한 잡의 개수를 지정한다. 기본적으로, 각각 3과 1로 설정된다. 한도를 0 으로 설정하는 것은 잡 완료 후에 해당 잡 유형의 기록을 보관하지 않는다는 것이다.

4.8.2 - 작업 대기열을 사용한 거친 병렬 처리

이 예제에서는, 여러 병렬 워커 프로세스를 활용해 쿠버네티스 잡(Job)을 실행한다.

이 예제에서는, 각 파드가 생성될 때 작업 대기열에서 하나의 작업 단위를 선택하여, 완료하고, 대기열에서 삭제하고, 종료한다.

이 예제에서의 단계에 대한 개요는 다음과 같다.

  1. 메시지 대기열 서비스를 시작한다. 이 예에서는, RabbitMQ를 사용하지만, 다른 메시지 대기열을 이용해도 된다. 실제로 사용할 때는, 한 번 메시지 대기열 서비스를 구축하고서 이를 여러 잡을 위해 재사용하기도 한다.
  2. 대기열을 만들고, 메시지로 채운다. 각 메시지는 수행할 하나의 작업을 나타낸다. 이 예제에서, 메시지는 긴 계산을 수행할 정수다.
  3. 대기열에서 작업을 수행하는 잡을 시작한다. 잡은 여러 파드를 시작한다. 각 파드는 메시지 대기열에서 하나의 작업을 가져와서, 처리한 다음, 대기열이 비워질 때까지 반복한다.

시작하기 전에

기본적이고, 병렬 작업이 아닌, 의 사용법에 대해 잘 알고 있어야 한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

메시지 대기열 서비스 시작

이 예시에서는 RabbitMQ를 사용하지만, 다른 AMQP 유형의 메시지 서비스를 사용하도록 예시를 조정할 수 있다.

실제로 사용할 때는, 클러스터에 메시지 대기열 서비스를 한 번 구축하고서, 여러 많은 잡이나 오래 동작하는 서비스에 재사용할 수 있다.

다음과 같이 RabbitMQ를 시작한다.

kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.3/examples/celery-rabbitmq/rabbitmq-service.yaml
service "rabbitmq-service" created
kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.3/examples/celery-rabbitmq/rabbitmq-controller.yaml
replicationcontroller "rabbitmq-controller" created

이 문서에서는 celery-rabbitmq 예제에 나오는 정도로만 rabbitmq를 사용한다.

메시지 대기열 서비스 테스트하기

이제, 메시지 대기열을 이용해 실험할 수 있다. 임시 대화형 파드를 만들어 그 위에 도구들을 설치하고, 대기열을 실험해본다.

먼저 임시 대화형 파드를 만든다.

# 임시 대화형 컨테이너를 만든다.
kubectl run -i --tty temp --image ubuntu:18.04
Waiting for pod default/temp-loe07 to be running, status is Pending, pod ready: false
... [ previous line repeats several times .. hit return when it stops ] ...

참고로 파드 이름과 명령 프롬프트는 위와 다를 수 있다.

다음으로 amqp-tools를 설치하여 메시지 대기열을 활용할 수 있게 한다.

# 도구들을 설치한다.
root@temp-loe07:/# apt-get update
.... [ lots of output ] ....
root@temp-loe07:/# apt-get install -y curl ca-certificates amqp-tools python dnsutils
.... [ lots of output ] ....

후에, 이 패키지들을 포함하는 도커 이미지를 만든다.

다음으로, rabbitmq 서비스를 발견할 수 있는지 확인한다.

# rabbitmq-service가 쿠버네티스로부터 주어진 DNS 이름을 갖는다.

root@temp-loe07:/# nslookup rabbitmq-service
Server:        10.0.0.10
Address:    10.0.0.10#53

Name:    rabbitmq-service.default.svc.cluster.local
Address: 10.0.147.152

# 주소는 다를 수 있다.

만약 Kube-DNS가 적절히 구축되지 않았다면, 전 단계 작업이 작동하지 않을 수 있다. 환경 변수를 통해서도 서비스 IP를 찾을 수 있다.

# env | grep RABBIT | grep HOST
RABBITMQ_SERVICE_SERVICE_HOST=10.0.147.152
# 주소는 다를 수 있다.

다음으로 대기열을 생성하고, 메시지를 발행하고 사용할 수 있는지 확인한다.

# 다음 줄에서, rabbitmq-service는 rabbitmq-service에 접근할 수 있는 
# 호스트네임이다. 5672는 rabbitmq의 표준 포트이다.

root@temp-loe07:/# export BROKER_URL=amqp://guest:guest@rabbitmq-service:5672
# 만약 전 단계에서 "rabbitmq-service"가 주소로 변환되지 않는다면,
# 이 커맨드를 대신 사용하면 된다.
# root@temp-loe07:/# BROKER_URL=amqp://guest:guest@$RABBITMQ_SERVICE_SERVICE_HOST:5672

# 이제 대기열을 생성한다.

root@temp-loe07:/# /usr/bin/amqp-declare-queue --url=$BROKER_URL -q foo -d
foo

# 대기열에 메시지를 하나 발행한다.

root@temp-loe07:/# /usr/bin/amqp-publish --url=$BROKER_URL -r foo -p -b Hello

# 다시 메시지를 돌려받는다.

root@temp-loe07:/# /usr/bin/amqp-consume --url=$BROKER_URL -q foo -c 1 cat && echo
Hello
root@temp-loe07:/#

마지막 커맨드에서, amqp-consume 도구는 대기열로부터 하나의 메시지를 받고(-c 1), 그 메시지를 임의의 명령 표준입력으로 전달한다. 이 경우에는, cat 프로그램이 표준입력으로부터 받은 값을 출력하고, echo가 캐리지 리턴을 더해주어 출력 결과가 보여진다.

작업으로 대기열 채우기

이제 몇 가지 "작업"으로 대기열을 채운다. 이 예제에서의 작업은 문자열을 출력하는 것이다.

실제로 사용할 때는, 메시지의 내용이 다음과 같을 수 있다.

  • 처리되어야 하는 파일들의 이름
  • 프로그램의 추가 플래그
  • 데이터베이스 테이블의 키(key) 범위
  • 시뮬레이션의 구성 파라미터
  • 렌더링해야 하는 씬(scene)의 프레임 번호

실제로는, 잡의 모든 파드에서 읽기-전용 모드로 필요한 큰 데이터가 있다면, 일반적으로 그 데이터를 NFS와 같은 공유 파일시스템에 넣고 모든 파드에 읽기 전용으로 마운트하거나, 파드 안에 있는 프로그램이 기본적으로 HDFS와 같은 클러스터 파일시스템으로부터 데이터를 불러들인다.

본 예제에서는, 대기열을 만들고 amqp 커맨드라인 도구를 이용해 대기열을 채울 것이다. 실제로는, amqp 라이브러리를 이용해 대기열을 채우는 프로그램을 작성하게 된다.

/usr/bin/amqp-declare-queue --url=$BROKER_URL -q job1  -d
job1
for f in apple banana cherry date fig grape lemon melon
do
  /usr/bin/amqp-publish --url=$BROKER_URL -r job1 -p -b $f
done

8개의 메시지로 대기열을 채웠다.

이미지 생성

이제 잡으로 실행할 이미지를 만들 준비가 되었다.

amqp-consume 유틸리티를 이용해 대기열로부터 메시지를 읽고, 실제 프로그램을 실행해 볼 것이다. 여기에 아주 간단한 예제 프로그램이 있다.

#!/usr/bin/env python

# 표준 출력만 출력하고 10초 동안 대기한다.
import sys
import time
print("Processing " + sys.stdin.readlines()[0])
time.sleep(10)

스크립트에 실행 권한을 준다.

chmod +x worker.py

이제 이미지를 빌드한다. 만약 소스 트리 안에서 작업하고 있다면, examples/job/work-queue-1로 디렉터리를 옮긴다. 아니면, 임시 디렉터리를 만들고, 그 디렉터리로 옮긴다. Dockerfileworker.py를 다운로드한다. 위 두 경우 모두, 다음의 명령을 이용해 이미지를 빌드한다.

docker build -t job-wq-1 .

도커 허브를 이용하기 위해, 앱 이미지를 사용자의 username으로 태깅하고 아래의 명령어를 이용해 허브에 푸시한다. <username>을 사용자의 허브 username으로 대체한다.

docker tag job-wq-1 <username>/job-wq-1
docker push <username>/job-wq-1

만약 구글 컨테이너 레지스트리를 이용하고 있다면, 앱 이미지를 사용자의 프로젝트 ID를 이용해 태깅하고, GCR에 푸시한다. <proejct> 부분을 사용자의 프로젝트 ID로 대체한다.

docker tag job-wq-1 gcr.io/<project>/job-wq-1
gcloud docker -- push gcr.io/<project>/job-wq-1

잡 정의

다음은 잡 정의이다. 잡의 사본을 만들고 위에서 정한 이름에 맞게 이미지를 수정하고, 파일 이름을 ./job.yaml이라 정한다.

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

이 예시에서는, 각 파드가 대기열로부터 얻은 하나의 아이템을 수행하고 종료한다. 그래서, 잡의 완료 횟수가 완료된 작업 아이템의 숫자에 대응한다. 예시에서 .spec.completions: 8이라 정한 것도, 대기열에 8개의 아이템을 넣었기 때문이다.

잡 실행

이제 잡을 실행한다.

kubectl apply -f ./job.yaml

이제 조금 기다린 다음, 잡을 확인한다.

kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2017 16:42:02 +0800
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      gcr.io/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

모든 파드가 성공했다. 야호.

대안

이러한 접근은 "워커" 프로그램을 작업 대기열에 맞게 수정하지 않아도 된다는 장점이 있다.

이 접근을 이용하려면, 메시지 대기열 서비스를 실행해야만 한다. 만약 메시지 대기열 서비스를 실행하는 게 불편하다면, 다른 잡 패턴을 고려해볼 수 있다.

이 접근은 모든 작업 아이템에 대해 파드를 생성한다. 만약 작업 아이템이 오직 몇 초밖에 걸리지 않는 작업이라면, 매 작업마다 파드를 생성하는 것은 아주 큰 오버헤드를 더할 수 있다. 하나의 파드가 여러 작업 아이템을 수행하는 이 예제를 고려해보자.

이 예제에서는, amqp-consume 유틸리티를 이용해 대기열로부터 메시지를 읽어 실제 프로그램을 실행했다. 이러면 메시지 대기열을 이용하기 위해 프로그램을 수정하지 않아도 된다는 장점이 있다. 다른 예제는 클라이언트 라이브러리를 이용해 작업 대기열과 소통하는 방법을 보여준다.

주의 사항

만약 작업 완료 수가 대기열에 있는 아이템의 숫자보다 적게 설정되면, 모든 아이템 처리되지 않는다.

만약 작업 완료 수가 큐에 있는 아이템의 숫자보다 많게 설정되면, 대기열에 있는 아이템이 모두 처리되어도, 잡이 완료됐다고 표시되지 않고, 메시지를 기다리는 과정에서 막히는 파드를 추가적으로 실행시킨다.

이 패턴에서는 경쟁 상태(race)가 잘 나타나지 않는다. 만약 amqp-consume 명령으로부터 메시지가 인정되는 시간과 컨테이너가 성공적으로 종료되는 시간 사이에 컨테이너가 종료되거나, kubelet이 api-server에게 파드가 성공했음을 알리기 전에 노드가 비정상적으로 종료되면, 대기열의 모든 아이템이 처리되었다 해도, 잡이 완료되었다고 표시되지 않는다.

4.8.3 - 작업 대기열을 사용한 정밀 병렬 처리

이 예에서는, 지정된 파드에서 여러 병렬 워커 프로세스가 있는 쿠버네티스 잡(Job)을 실행한다.

이 예에서는, 각 파드가 생성될 때, 작업 대기열에서 하나의 작업 단위를 선택하여, 처리하고, 대기열이 비워질 때까지 반복한다.

이 예에서의 단계에 대한 개요는 다음과 같다.

  1. 작업 대기열을 보관할 스토리지 서비스를 시작한다. 이 예에서는, Redis를 사용하여 작업 항목을 저장한다. 이전 예에서는, RabbitMQ를 사용했다. 이 예에서는, AMQP가 길이가 정해져 있는 작업 대기열이 비어있을 때 클라이언트가 이를 감지할 수 있는 좋은 방법을 제공하지 않기 때문에 Redis 및 사용자 지정의 작업 대기열 클라이언트 라이브러리를 사용한다. 실제로는 Redis와 같은 저장소를 한 번 설정하고 여러 작업과 다른 것들의 작업 대기열로 재사용한다.
  2. 대기열을 만들고, 메시지로 채운다. 각 메시지는 수행할 하나의 작업을 나타낸다. 이 예에서, 메시지는 긴 계산을 수행할 정수다.
  3. 대기열에서 작업을 수행하는 잡을 시작한다. 잡은 여러 파드를 시작한다. 각 파드는 메시지 대기열에서 하나의 작업을 가져와서, 처리한 다음, 대기열이 비워질 때까지 반복한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

의 기본적이고, 병렬 작업이 아닌, 사용법에 대해 잘 알고 있어야 한다.

Redis 시작

이 문서의 예시에서는, 단순함을 위해, Redis의 단일 인스턴스를 시작한다. Redis를 확장 가능하고 중복적으로 배포하는 예에 대해서는 Redis 예시를 참고한다.

다음 파일을 직접 다운로드할 수도 있다.

작업으로 대기열 채우기

이제 몇 가지 "작업"으로 대기열을 채운다. 이 예제의 작업은 문자열을 출력하는 것이다.

Redis CLI를 실행하기 위한 임시 대화형 파드를 시작한다.

kubectl run -i --tty temp --image redis --command "/bin/sh"
Waiting for pod default/redis2-c7h78 to be running, status is Pending, pod ready: false
Hit enter for command prompt

이제 엔터 키를 누르고, redis CLI를 시작하고, 몇몇 작업 항목이 포함된 목록을 생성한다.

# redis-cli -h redis
redis:6379> rpush job2 "apple"
(integer) 1
redis:6379> rpush job2 "banana"
(integer) 2
redis:6379> rpush job2 "cherry"
(integer) 3
redis:6379> rpush job2 "date"
(integer) 4
redis:6379> rpush job2 "fig"
(integer) 5
redis:6379> rpush job2 "grape"
(integer) 6
redis:6379> rpush job2 "lemon"
(integer) 7
redis:6379> rpush job2 "melon"
(integer) 8
redis:6379> rpush job2 "orange"
(integer) 9
redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

자, 키 job2 가 있는 목록이 작업 대기열이 된다.

참고: Kube DNS를 올바르게 설정하지 않은 경우, 위 블록의 첫 번째 단계를 redis-cli -h $REDIS_SERVICE_HOST 로 변경해야 할 수 있다.

이미지 생성

이제 실행할 이미지를 만들 준비가 되었다.

redis 클라이언트와 함께 python 워커 프로그램을 사용하여 메시지 큐에서 메시지를 읽는다.

rediswq.py(다운로드)라는 간단한 Redis 작업 대기열 클라이언트 라이브러리가 제공된다.

잡의 각 파드에 있는 "워커" 프로그램은 작업 대기열 클라이언트 라이브러리를 사용하여 작업을 가져온다. 다음은 워커 프로그램이다.

#!/usr/bin/env python

import time
import rediswq

host="redis"
# Uncomment next two lines if you do not have Kube-DNS working.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")

q = rediswq.RedisWQ(name="job2", host=host)
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2)
  if item is not None:
    itemstr = item.decode("utf-8")
    print("Working on " + itemstr)
    time.sleep(10) # Put your actual work here instead of sleep.
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

worker.py, rediswq.pyDockerfile 파일을 다운로드할 수 있고, 그런 다음 이미지를 만들 수도 있다.

docker build -t job-wq-2 .

이미지 푸시

도커 허브(Docker Hub)를 위해, 아래 명령으로 사용자의 username과 앱 이미지에 태그하고 허브에 푸시한다. <username> 을 사용자의 허브 username으로 바꾼다.

docker tag job-wq-2 <username>/job-wq-2
docker push <username>/job-wq-2

공용 저장소로 푸시하거나 개인 저장소에 접근할 수 있도록 클러스터를 구성해야 한다.

Google Container Registry를 사용하는 경우, 사용자의 프로젝트 ID로 앱 이미지에 태그를 지정하고 GCR로 푸시한다. <project> 를 사용자의 프로젝트 ID로 바꾼다.

docker tag job-wq-2 gcr.io/<project>/job-wq-2
gcloud docker -- push gcr.io/<project>/job-wq-2

잡 정의

다음은 잡 정의이다.

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
      restartPolicy: OnFailure

사용자 자신의 경로로 gcr.io/myproject 를 변경하려면 잡 템플릿을 편집해야 한다.

이 예에서, 각 파드는 대기열의 여러 항목에 대해 작업한 다음 더 이상 항목이 없을 때 종료된다. 워커는 작업 대기열이 비어있을 때를 감지하고 잡 컨트롤러는 작업 대기열에 대해 알지 못하기 때문에, 작업이 완료되면 워커에게 신호를 보낸다. 워커는 성공적으로 종료하여 대기열이 비어 있음을 알린다. 따라서, 워커가 성공적으로 종료하자마자, 컨트롤러는 작업이 완료되었음을 인식하고, 파드가 곧 종료된다. 따라서, 잡 완료 횟수를 1로 설정했다. 잡 컨트롤러는 다른 파드도 완료될 때까지 기다린다.

잡 실행

이제 잡을 실행한다.

kubectl apply -f ./job.yaml

이제 조금 기다린 다음, 잡을 확인한다.

kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2016 17:07:59 -0800
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              gcr.io/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8


kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

보시다시피, 사용자의 파드 중 하나가 여러 작업 단위에서 작업했다.

대안

대기열 서비스를 실행하거나 작업 대기열을 사용하도록 컨테이너를 수정하는 것이 불편한 경우, 다른 잡 패턴 중 하나를 고려할 수 있다.

만약 실행할 백그라운드 처리 작업의 연속 스트림이 있는 경우, ReplicaSet 이 있는 백그라운드 워커를 실행하는 것과, https://github.com/resque/resque와 같은 백그라운드 처리 라이브러리를 실행하는 것이 좋다.

4.8.4 - 확장을 사용한 병렬 처리

이 태스크는 공통 템플릿을 기반으로 하는 여러 개의 잡(Job)을 실행하는 것을 보여준다. 이 접근 방식을 사용하여 일괄 작업을 병렬로 처리할 수 있다.

이 예에는 apple, banana 그리고 cherry 세 항목만 있다. 샘플 잡들은 문자열을 출력한 다음 일시 정지하는 각 항목을 처리한다.

이 패턴이 보다 실질적인 유스케이스에 어떻게 부합하는지 알아 보려면 실제 워크로드에서 잡 사용하기를 참고한다.

시작하기 전에

사용자는 기본적인 내용과, 병렬 작업이 아닌 사용에 대해 익숙해야 한다.

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

기본 템플릿을 사용하려면 커맨드 라인 유틸리티 sed 가 필요하다.

고급 템플릿 예제를 따라하려면, 파이썬(Python)과 파이썬용 Jinja2 템플릿 라이브러리의 설치가 필요하다.

파이썬을 설정했으면, 다음을 실행하여 Jinja2를 설치할 수 있다.

pip install --user jinja2

템플릿 기반의 잡 생성하기

먼저, 다음의 잡 템플릿을 다운로드해서 job-tmpl.yaml 파일로 저장한다. 다운로드할 내용은 다음과 같다.

apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never
# job-tmpl.yaml를 다운로드하기 위해 curl을 사용한다
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml

다운로드한 파일은 아직 유효한 쿠버네티스 매니페스트가 아니다. 대신 해당 템플릿은 사용하기 전에 채워야하는 자리 표시자가 있는 잡 오브젝트의 YAML 표현이다. $ITEM 구문은 쿠버네티스에 의미가 있지 않다.

템플릿에서 매니페스트 생성하기

다음의 셸 스니펫은 sed 를 사용하여 루프 변수로 $ITEM 문자열을 바꾸고, jobs 라는 임시 디렉터리에 기록한다. 다음과 같이 실행한다.

# 처리할 각 항목에 대해 하나씩, 템플릿을 여러 파일로 확장한다.
mkdir ./jobs
for i in apple banana cherry
do
  cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
done

작동하는지 확인한다.

ls jobs/

출력 결과는 다음과 비슷하다.

job-apple.yaml
job-banana.yaml
job-cherry.yaml

모든 유형의 템플릿 언어(예를 들어, Jinja2, ERB)를 사용하거나, 프로그램을 작성하여 잡 매니페스트를 생성할 수 있다.

매니페스트에서 잡 생성하기

다음으로, 하나의 kubectl 명령으로 모든 잡을 생성한다.

kubectl create -f ./jobs

출력 결과는 다음과 비슷하다.

job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created

이제, 작업을 확인한다.

kubectl get jobs -l jobgroup=jobexample

출력 결과는 다음과 비슷하다.

NAME                  COMPLETIONS   DURATION   AGE
process-item-apple    1/1           14s        22s
process-item-banana   1/1           12s        21s
process-item-cherry   1/1           12s        20s

kubectl 명령에 -l 옵션을 사용하면 이 잡 그룹의 일부인 잡만 선택된다(시스템에서 관련이 없는 다른 잡이 있을 수 있음).

파드도 동일한 레이블 셀렉터를 사용하여 확인할 수 있다.

kubectl get pods -l jobgroup=jobexample

출력 결과는 다음과 비슷하다.

NAME                        READY     STATUS      RESTARTS   AGE
process-item-apple-kixwv    0/1       Completed   0          4m
process-item-banana-wrsf7   0/1       Completed   0          4m
process-item-cherry-dnfu9   0/1       Completed   0          4m

이 단일 명령을 사용하여 모든 잡의 출력을 한 번에 확인할 수 있다.

kubectl logs -f -l jobgroup=jobexample

출력 결과는 다음과 같아야 한다.

Processing item apple
Processing item banana
Processing item cherry

정리

# 생성한 잡 제거
# 클러스터가 자동으로 잡의 파드들을 정리
kubectl delete job -l jobgroup=jobexample

고급 템플릿 파라미터 사용하기

첫 번째 예제에서, 템플릿의 각 인스턴스는 하나의 파라미터를 가지고, 해당 파라미터는 잡의 이름에도 사용되었다. 그러나, 이름은 특정 문자들만 포함하도록 제한된다.

이런 약간 더 복잡한 예제는 Jinja 템플릿 언어를 사용하여 각 잡에 대한 여러 파라미터로 매니페스트를 생성한 다음 해당 매니페스트에서 오브젝트를 생성한다.

태스크의 이 부분에서는, 한줄 파이썬 스크립트를 사용하여 매니페스트 집합으로 템플릿을 변환한다.

먼저, 다음의 잡 오브젝트 템플릿을 복사하고 붙여넣기하여, job.yaml.jinja2 파일로 저장한다.

{%- set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
                  { "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
                  { "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{%- for p in params %}
{%- set name = p["name"] %}
{%- set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: jobexample-{{ name }}
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox
        command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
      restartPolicy: Never
{%- endfor %}

위의 템플릿은 파이썬 딕셔너리(dicts)로 구성된 항목(1-4행)을 사용하여 각 잡 오브젝트에 대해 두 개의 파라미터를 정의한다. for 루프는 각 파라미터의 집합(나머지 행)에 대해 하나의 잡 매니페스트를 방출한다.

이 예제는 YAML의 기능에 의존한다. 하나의 YAML 파일은 여러 문서(이 경우, 쿠버네티스 매니페스트)를 포함할 수 있으며, 행에 있는 --- 로 구분된다. 출력 결과를 kubectl 에 직접 파이프를 사용해 잡을 생성할 수 있다.

다음으로, 이 한 줄 파이썬 프로그램을 사용하여 템플릿을 확장한다.

alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'

render_template 을 사용해서 파라미터와 템플릿을 쿠버네티스 매니페스트가 포함된 하나의 YAML 파일로 변환한다.

# 앞에서 정의한 앨리어스(alias)가 필요하다
cat job.yaml.jinja2 | render_template > jobs.yaml

render_template 스크립트가 제대로 동작하는지 확인하기 위해 jobs.yaml 을 볼 수 있다.

render_template 스크립트가 원하는대로 동작하는 것을 확인했다면, 스크립트의 출력 결과를 파이프를 사용하여 kubectl 에 보낼 수 있다.

cat job.yaml.jinja2 | render_template | kubectl apply -f -

쿠버네티스는 생성한 잡을 수락하고 실행한다.

정리

# 생성한 잡 제거
# 클러스터가 자동으로 잡이 있던 파드를 정리
kubectl delete job -l jobgroup=jobexample

실제 워크로드에서 잡 사용하기

실제 유스케이스에서, 각 잡은 동영상의 프레임을 렌더링하거나, 데이터베이스에서 행 범위를 처리하는 것과 같은 상당한 규모의 계산을 수행한다. 동영상을 렌더링하는 경우 프레임 번호에 $ITEM 을 설정한다. 데이터베이스에서 행을 처리하는 경우, 처리할 데이터베이스 행의 범위를 나타내도록 $ITEM 을 설정한다.

이번 태스크에서, 로그를 가져와 파드에서 출력 결과를 수집하는 명령어를 실행했다. 실제 유스케이스에서, 잡의 각 파드는 완료하기 전에 출력 결과를 내구성있는 스토리지에 기록한다. 각 잡에 대해 퍼시스턴트볼륨(PersistentVolume)을 사용하거나 외장 스토리지 서비스를 사용할 수 있다. 예를 들어, 동영상의 프레임을 렌더링하는 경우, HTTP를 사용하여 렌더링된 프레임 데이터를 각 프레임에 대한 다른 URL을 사용해서 URL에 PUT 한다.

잡과 파드의 레이블

잡을 생성한 후, 쿠버네티스는 한 잡의 파드를 다른 잡의 파드와 구별하기 위해서 추가 레이블을 자동으로 추가한다.

이 예시에서, 각 잡과 잡의 파드 템플릿은 jobgroup=jobexample 레이블을 갖는다.

쿠버네티스 자체는 jobgroup 이라는 레이블에 신경쓰지 않는다. 템플릿에서 생성한 모든 잡에 대해 레이블을 설정하면 한번에 모든 잡을 편리하게 조작할 수 있다. 첫 번째 예제에서 템플릿을 사용해서 여러 잡을 생성했다. 템플릿은 각 파드도 동일한 레이블을 가질 수 있도록 보장하므로, 단일 명령어로 이러한 템플릿 기반 잡들의 모든 파드에서 확인할 수 있다.

대안

많은 수의 잡 오브젝트의 생성을 계획 중이라면, 아마도 다음의 사항을 파악하게 될 것이다.

  • 레이블을 사용해도, 너무 많은 잡을 관리하는 것은 번거롭다.
  • 일괄적으로 많은 잡을 생성하는 경우, 쿠버네티스 컨트롤 플레인에 높음 부하를 가할 수 있다. 대안으로, 쿠버네티스 API 서버가 속도를 제한하여 429 상태의 사용자 요청을 일시적으로 거부할 수 있다.
  • 사용자는 잡의 리소스 쿼터로 제한될 수 있다. 한번에 많은 작업을 생성하면 API 서버가 사용자의 요청 중 일부를 영구적으로 거부한다.

아주 많은 잡 오브젝트를 생성하지 않고 많은 양의 작업을 처리하는데 사용할 수 있는 다른 잡 패턴도 있다.

잡 오브젝트를 자동으로 관리하기 위해 자체 컨트롤러를 작성하는 것도 고려할 수 있다.

4.9 - 클러스터 내 어플리케이션 접근

클러스터의 애플리케이션에 접근하기 위해 로드 밸런싱, 포트 포워딩, 방화벽 설정 또는 DNS 구성을 설정한다.

4.9.1 - 쿠버네티스 대시보드를 배포하고 접속하기

웹 UI(쿠버네티스 대시보드)를 배포하고 접속한다.

대시보드는 웹 기반 쿠버네티스 유저 인터페이스이다. 대시보드를 통해 컨테이너화 된 애플리케이션을 쿠버네티스 클러스터에 배포할 수 있고, 컨테이너화 된 애플리케이션을 트러블슈팅할 수 있으며, 클러스터 리소스들을 관리할 수 있다. 대시보드를 통해 클러스터에서 동작 중인 애플리케이션의 정보를 볼 수 있고, 개별적인 쿠버네티스 리소스들을(예를 들면 디플로이먼트, 잡, 데몬셋 등) 생성하거나 수정할 수 있다. 예를 들면, 디플로이먼트를 스케일하거나, 롤링 업데이트를 초기화하거나, 파드를 재시작하거나 또는 배포 마법사를 이용해 새로운 애플리케이션을 배포할 수 있다.

또한 대시보드는 클러스터 내 쿠버네티스 리소스들의 상태와 발생하는 모든 에러 정보를 제공한다.

Kubernetes Dashboard UI

대시보드 UI 배포

대시보드 UI는 기본으로 배포되지 않는다. 배포하려면 다음 커맨드를 실행한다.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml

대시보드 UI 접근

클러스터 데이터를 보호하기 위해, 대시보드는 기본적으로 최소한의 RBAC 설정을 제공한다. 현재, 대시보드는 Bearer 토큰으로 로그인하는 방법을 제공한다. 본 시연을 위한 토큰을 생성하기 위해서는, 샘플 사용자 만들기 가이드를 따른다.

커맨드 라인 프록시

kubectl 커맨드라인 도구를 이용해 다음 커맨드를 실행함으로써 대시보드로의 접속을 활성화할 수 있다.

kubectl proxy

kubectl은 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/를 통해 대시보드에 접속할 수 있게 해줄 것이다.

UI는 오직 커맨드가 실행된 머신에서만 접근 가능하다. 상세 내용은 kubectl proxy --help 옵션을 확인한다.

웰컴 뷰

초기 클러스터 대시보드에 접근하면, 환영 페이지를 볼 수 있다. 이 페이지는 첫 애플리케이션을 배포하는 버튼이 있을 뿐만 아니라, 이 문서의 링크를 포함하고 있다. 게다가, 대시보드가 있는 클러스터에서 기본적으로 kube-system 네임스페이스이 동작중인 시스템 애플리케이션을 볼 수 있다.

Kubernetes Dashboard welcome page

컨테이너화 된 애플리케이션 배포

대시보드를 이용하여 컨테이너화 된 애플리케이션을 디플로이먼트와 간단한 마법사를 통한 선택적인 서비스로 생성하고 배포할 수 있다. 애플리케이션 세부 정보를 수동으로 지정할 수 있고, 또는 애플리케이션 구성을 포함한 YAML 또는 JSON 매니페스트(manifest) 파일을 업로드할 수 있다.

시작하는 페이지의 상위 오른쪽 코너에 있는 CREATE 버튼을 클릭한다.

애플리케이션 세부 정보 지정

배포 마법사는 다음 정보를 제공한다.

  • 앱 이름 (필수): 애플리케이션 이름. 레이블 이름은 배포할 모든 디플로이먼트와 서비스에 추가되어야 한다.

    애플리케이션 이름은 선택된 쿠버네티스 네임스페이스 안에서 유일해야 한다. 소문자로 시작해야 하며, 소문자 또는 숫자로 끝나고, 소문자, 숫자 및 대쉬(-)만을 포함해야 한다. 24 문자만을 제한한다. 처음과 끝의 스페이스는 무시된다.

  • 컨테이너 이미지 (필수): 레지스트리에 올라간 퍼블릭 도커 컨테이너 이미지 또는 프라이빗 이미지(대체로 Google Container Registry 또는 도커 허브에 올라간)의 URL. 컨테이너 이미지 사양은 콜론으로 끝난다.

  • 파드의 수 (필수): 배포하고 싶은 애플리케이션의 원하는 목표 파드 개수. 값은 양의 정수만 허용됩니다.

    클러스터에 의도한 파드의 수를 유지하기 위해서 디플로이먼트가 생성될 것이다.

  • 서비스 (선택): 일부 애플리케이션의 경우, (예를 들어, 프론트엔드) 아마도 클러스터 바깥의 퍼블릭 IP 주소를 가진 (외부 서비스) 외부에 서비스를 노출시키고 싶을 수 있다.

    클러스터 내부에서만 보고 싶은 어떤 서비스들이 있을 것이다. 이를 내부 서비스라고 한다.

    서비스 타입과는 무관하게, 서비스 생성을 선택해서 컨테이너의 (들어오는 패킷의) 포트를 리슨한다면, 두 개의 포트를 정의해야 한다. 서비스는 컨테이너가 바라보는 타겟 포트와 (들어오는 패킷의) 맵핑하는 포트가 만들어져야 할 것이다. 서비스는 배포된 파드에 라우팅 될 것이다. 지원하는 프로토콜은 TCP와 UDP이다. 서비스가 이용하는 내부 DNS 이름은 애플리케이션 이름으로 지정한 값이 될 것이다.

만약 필요하다면, 더 많은 세팅을 지정할 수 있는 자세한 옵션 보기 섹션에서 확장할 수 있다.

  • 설명: 입력하는 텍스트값은 디플로이먼트에 어노테이션으로 추가될 것이고, 애플리케이션의 세부사항에 표시될 것이다.

  • 레이블: 애플리케이션에 사용되는 기본적인 레이블은 애플리케이션 이름과 버전이다. 릴리스, 환경, 티어, 파티션, 그리고 릴리스 트랙과 같은 레이블을 디플로이먼트, 서비스, 그리고 파드를 생성할 때 추가적으로 정의할 수 있다.

    예를 들면:

    release=1.0
    tier=frontend
    environment=pod
    track=stable
    
  • 네임스페이스: 쿠버네티스는 동일한 물리 클러스터를 바탕으로 여러 가상의 클러스터를 제공한다. 이러한 가상 클러스터들을 네임스페이스라고 부른다. 논리적으로 명명된 그룹으로 리소스들을 분할할 수 있다.

    대시보드는 드롭다운 리스트로 가능한 모든 네임스페이스를 제공하고, 새로운 네임스페이스를 생성할 수 있도록 한다. 네임스페이스 이름은 최대 63개의 영숫자 단어와 대시(-)를 포함하고 있지만 대문자를 가지지 못한다. 네임스페이스 이름은 숫자로만 구성할 수 없다. 만약 이름을 10이라는 숫자로 세팅한다면, 파드는 기본 네임스페이스로 배정하게 될 것이다.

    네임스페이스 생성이 성공하는 경우, 생성된 네임스페이스가 기본으로 선택된다. 만약 생성에 실패하면, 첫 번째 네임스페이스가 선택된다.

  • 이미지 풀(Pull) 시크릿: 특정 도커 컨테이너 이미지가 프라이빗한 경우, 풀(Pull) 시크릿 자격 증명을 요구한다.

    대시보드는 가능한 모든 시크릿을 드롭다운 리스트로 제공하며, 새로운 시크릿을 생성할 수 있도록 한다. 시크릿 이름은 예를 들어 new.image-pull.secret 과 같이 DNS 도메인 이름 구문으로 따르기로 한다. 시크릿 내용은 base64 인코딩 방식이며, .dockercfg 파일로 정의된다. 시크릿 이름은 최대 253 문자를 포함할 수 있다.

    이미지 풀(Pull) 시크릿의 생성이 성공한 경우, 기본으로 선택된다. 만약 생성에 실패하면, 시크릿은 허용되지 않는다.

  • CPU 요구 사항 (cores)메모리 요구 사항 (MiB): 컨테이너를 위한 최소 리소스 상한을 정의할 수 있다. 기본적으로, 파드는 CPU와 메모리 상한을 두지 않고 동작한다.

  • 커맨드 실행커맨드 인수 실행: 기본적으로, 컨테이너는 선택된 도커 이미지의 기본 엔트리포인트 커맨드를 실행한다. 커맨드 옵션과 인자를 기본 옵션에 우선 적용하여 사용할 수 있다.

  • 특권을 가진(privileged) 상태로 실행: 다음 세팅은 호스트에서 루트 권한을 가진 프로세스들이 특권을 가진 컨테이너의 프로세스들과 동등한지 아닌지 정의한다. 특권을 가진(privileged) 컨테이너는 네트워크 스택과 디바이스에 접근하는 것을 조작하도록 활용할 수 있다.

  • 환경 변수: 쿠버네티스 서비스를 환경 변수를 통해 노출한다. 환경 변수 또는 인자를 환경 변수들의 값으로 커맨드를 통해 구성할 수 있다. 애플리케이션들이 서비스를 찾는데 사용된다. 값들은 $(VAR_NAME) 구문을 사용하는 다른 변수들로 참조할 수 있다.

YAML 또는 JSON 파일 업로드

쿠버네티스는 선언적인 설정을 제공한다. 이 방식에서는 모든 설정이 매니페스트(YAML 또는 JSON 설정 파일)에 저장된다. 매니페스트는 쿠버네티스 API 리소스 스키마를 사용한다.

배포 마법사를 통해 애플리케이션 세부 사항들을 지정하는 대신, 애플리케이션을 하나 이상의 매니페스트로 정의할 수 있고 대시보드를 이용해서 파일을 업로드할 수 있다.

대시보드 사용

다음 섹션들은 어떻게 제공하고 어떻게 사용할 수 있는지에 대한 쿠버네티스 대시보드 UI의 모습을 보여준다.

탐색

클러스터에 정의된 쿠버네티스 오프젝트가 있으면, 대시보드는 초기화된 뷰를 제공한다. 기본적으로 기본 네임스페이스의 오프젝트만이 보이는데, 이는 탐색 창에 위치한 네임스페이스 셀렉터를 이용해 변경할 수 있다.

대시보드는 몇 가지 메뉴 카테고리 중에서 대부분의 쿠버네티스 오브젝트 종류와 그룹을 보여준다.

어드민 개요

클러스터와 네임스페이스 관리자에게 대시보드는 노드, 네임스페이스 그리고 퍼시스턴트 볼륨과 세부사항들이 보여진다. 노드는 모든 노드를 통틀어 CPU와 메모리 사용량을 보여준다. 세부사항은 각 노드들에 대한 사용량, 사양, 상태, 할당된 리소스, 이벤트 그리고 노드에서 돌아가는 파드를 보여준다.

워크로드

선택된 네임스페이스에서 구동되는 모든 애플리케이션을 보여준다. 애플리케이션의 워크로드 종류(예시: 디플로이먼트, 레플리카셋(ReplicaSet), 스테이트풀셋(StatefulSet))를 보여주고 각각의 워크로드 종류는 따로 보여진다. 리스트는 예를 들어 레플리카셋에서 준비된 파드의 숫자 또는 파드의 현재 메모리 사용량과 같은 워크로드에 대한 실용적인 정보를 요약한다.

워크로드에 대한 세부적인 것들은 상태와 사양 정보, 오프젝트들 간의 관계를 보여준다. 예를 들어, 레플리카셋으로 관리하는 파드들 또는 새로운 레플리카셋과 디플로이먼트를 위한 Horizontal Pod Autoscalers 이다.

서비스

외부로 노출되는 서비스들과 클러스터 내에 발견되는 서비스들을 허용하는 쿠버네티스 리소스들을 보여준다. 이러한 이유로 서비스와 인그레스는 클러스터간의 연결을 위한 내부 엔드포인트들과 외부 사용자를 위한 외부 엔드포인트들에 의해 타게팅된 파드들을 보여준다.

스토리지

스토리지는 애플리케이션이 데이터를 저장하기 위해 사용하는 퍼시턴트볼륨클레임 리소스들을 보여준다.

컨피그맵과 시크릿

클러스터에서 동작 중인 애플리케이션의 라이브 설정을 사용하는 모든 쿠버네티스 리소스들을 보여준다. 컨피그 오브젝트들을 수정하고 관리할 수 있도록 허용하며, 기본적으로는 숨겨져 있는 시크릿들을 보여준다.

로그 뷰어

파드 목록과 세부사항 페이지들은 대시보드에 구현된 로그 뷰어에 링크된다. 뷰어는 단일 파드에 있는 컨테이너들의 로그들을 내려가면 볼 수 있도록 한다.

Logs viewer

다음 내용

더 많은 정보는 쿠버네티스 대시보드 프로젝트 페이지를 참고한다.

4.9.2 - 클러스터 접근

여기에서는 클러스터와 통신을 하는 다양한 방식에 대해서 다룰 것이다.

처음이라면 kubectl을 사용하여 접근

최초로 쿠버네티스 API에 접근할 때 우리는 쿠버네티스 CLI인 kubectl을 사용하는 것을 추천한다.

클러스터에 접근하려면 클러스터의 위치정보를 알아야 하고 클러스터에 접속하기 위한 인증정보를 가져야 한다. 일반적으로 이는 당신이 Getting started guide를 다 진행했을 때 자동으로 구성되거나, 다른 사람이 클러스터를 구성하고 당신에게 인증정보와 위치정보를 제공할 수도 있다.

kubectl이 인지하는 위치정보와 인증정보는 다음 커맨드로 확인한다.

kubectl config view

여기에서 kubectl 사용 예시를 볼 수 있으며, 완전한 문서는 kubectl 매뉴얼에서 확인할 수 있다.

REST API에 직접 접근

kubectl은 apiserver의 위치 파악과 인증을 처리한다. 만약 당신이 curl, wget 또는 웹브라우저와 같은 http 클라이언트로 REST API에 직접 접근하려고 한다면 위치 파악과 인증을 하는 몇 가지 방법이 존재한다.

  • kubectl을 proxy 모드로 실행.
    • 권장하는 접근 방식.
    • 저장된 apiserver 위치를 사용.
    • self-signed 인증서를 사용하여 apiserver의 identity를 검증. MITM은 불가능.
    • apiserver 인증.
    • 앞으로는 클라이언트 측의 지능형 load balancing과 failover가 될 것이다.
  • 직접적으로 http 클라이언트에 위치정보와 인증정보를 제공.
    • 대안적인 접근 방식.
    • proxy 사용과 혼동되는 몇 가지 타입의 클라이언트 코드와 같이 동작한다.
    • MITM로부터 보호를 위해 root 인증서를 당신의 브라우저로 임포트해야 한다.

kubectl proxy 사용

다음 커맨드는 kubectl을 리버스 프록시(reverse proxy)처럼 동작하는 모드를 실행한다. 이는 apiserver의 위치지정과 인증을 처리한다. 다음과 같이 실행한다.

kubectl proxy --port=8080

상세 내용은 kubectl proxy를 참조한다

이후에 당신은 curl, wget, 웹브라우저로 다음과 같이 API를 탐색할 수 있다. localhost는 IPv6 주소 [::1]로도 대체할 수 있다.

curl http://localhost:8080/api/

결괏값은 다음과 같을 것이다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

kubectl proxy를 사용하지 않음

기본 서비스 어카운트의 토큰을 얻어내려면 kubectl describe secret...을 grep/cut과 함께 사용한다.

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
SECRET_NAME=$(kubectl get secrets | grep ^default | cut -f1 -d ' ')
TOKEN=$(kubectl describe secret $SECRET_NAME | grep -E '^token' | cut -f2 -d':' | tr -d " ")

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

결과값은 다음과 같을 것이다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

jsonpath를 사용한다면 다음과 같다.

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
SECRET_NAME=$(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}')
TOKEN=$(kubectl get secret $SECRET_NAME -o jsonpath='{.data.token}' | base64 --decode)

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

결과값은 다음과 같을 것이다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

위 예제에서는 --insecure flag를 사용했다. 이는 MITM 공격을 받을 수 있는 상태로 두는 것이다. kubectl로 클러스터에 접속할 때 저장된 root 인증서와 클라이언트 인증서들을 서버 접속에 사용한다. (이들은 ~/.kube 디렉터리에 설치된다.) 일반적으로 self-signed 인증서가 클러스터 인증서로 사용되므로 당신의 http 클라이언트가 root 인증서를 사용하려면 특수한 설정을 필요로 할 것이다.

localhost에서 제공되거나 방화벽으로 보호되는 몇몇 클러스터들에서는 apiserver가 인증을 요구하지 않지만 이는 표준이 아니다. API에 대한 접근 제어은 클러스터 관리자가 이를 어떻게 구성할 수 있는지를 설명한다.

API에 프로그래밍 방식으로 접근

쿠버네티스는 공식적으로 GoPython 클라이언트 라이브러리를 지원한다.

Go 클라이언트

  • 라이브러리를 취득하려면 go get k8s.io/client-go@kubernetes-<kubernetes-version-number> 커맨드를 실행한다. INSTALL.md에서 상세한 설치 방법을 알 수 있다. https://github.com/kubernetes/client-go에서 어떤 버젼이 지원되는지 확인할 수 있다.
  • client-go 클라이언트 위에 애플리케이션을 작성하자. client-go는 자체적으로 API 오브젝트를 정의하므로 필요하다면 main 레포지터리보다는 client-go에서 API 정의들을 import하기를 바란다. 정확하게 import "k8s.io/client-go/kubernetes"로 import하는 것을 예로 들 수 있다.

Go 클라이언트는 apiserver의 위치지정과 인증에 kubectl CLI와 동일하게 kubeconfig file을 사용할 수 있다. 예제를 참고한다.

만약 애플리케이션이 클러스터 내에 파드로 배포되었다면 다음 장을 참조하기를 바란다.

Python 클라이언트

Python 클라이언트를 사용하려면 pip install kubernetes 커맨드를 실행한다. 설치 옵션에 대한 상세 사항은 Python Client Library page를 참조한다.

Python 클라이언트는 apiserver의 위치지정과 인증에 kubectl CLI와 동일하게 kubeconfig file을 사용할 수 있다. 예제를 참조한다.

다른 언어

다른 언어에서 API를 접속하기 위한 클라이언트 라이브러리들도 존재한다. 이들이 어떻게 인증하는지는 다른 라이브러리들의 문서를 참조한다.

파드에서 API 접근

파드에서 API를 접속한다면 apiserver의 위치지정과 인증은 다소 다르다.

파드 내에서 apiserver의 위치를 지정하는데 추천하는 방식은 kubernetes.default.svc DNS 네임을 사용하는 것이다. 이 DNS 네임은 apiserver로 라우팅되는 서비스 IP로 resolve된다.

apiserver 인증에 추천되는 방식은 서비스 어카운트 인증정보를 사용하는 것이다. kube-system에 의해 파드는 서비스 어카운트와 연계되며 해당 서비스 어카운트의 인증정보(토큰)은 파드 내 각 컨테이너의 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/token에 위치한다.

사용 가능한 경우, 인증서 번들은 각 컨테이너 내 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt에 위치하며 apiserver의 인증서 제공을 검증하는데 사용되어야 한다.

마지막으로 네임스페이스 한정의 API 조작에 사용되는 기본 네임스페이스는 각 컨테이터 내의 /var/run/secrets/kubernetes.io/serviceaccount/namespace 파일로 존재한다.

파드 내에서 API에 접근하는데 권장되는 방식은 다음과 같다.

  • 파드의 sidecar 컨테이너 내에서 kubectl proxy를 실행하거나, 컨테이너 내부에서 백그라운드 프로세스로 실행한다. 이는 쿠버네티스 API를 파드의 localhost 인터페이스로 프록시하여 해당 파드의 컨테이너 내에 다른 프로세스가 API에 접속할 수 있게 해준다.
  • Go 클라이언트 라이브러리를 이용하여 rest.InClusterConfig()kubernetes.NewForConfig() 함수들을 사용하도록 클라이언트를 만든다. 이는 apiserver의 위치지정과 인증을 처리한다. 예제

각각의 사례에서 apiserver와의 보안 통신에 파드의 인증정보가 사용된다.

클러스터에서 실행되는 서비스로 접근

이전 장은 쿠버네티스 API server 접속에 대한 내용을 다루었다. 이번 장은 쿠버네티스 클러스터 상에서 실행되는 다른 서비스로의 연결을 다룰 것이다.

쿠버네티스에서, 노드, 파드서비스는 모두 고유한 IP를 가진다. 당신의 데스크탑 PC와 같은 클러스터 외부 장비에서는 클러스터 상의 노드 IP, 파드 IP, 서비스 IP로 라우팅되지 않아서 접근을 할 수 없을 것이다.

통신을 위한 방식들

클러스터 외부에서 노드, 파드 및 서비스에 접속하기 위한 몇 가지 옵션이 있다.

  • 공인 IP를 통해 서비스에 접근.
    • 클러스터 외부에서 접근할 수 있도록 NodePort 또는 LoadBalancer 타입의 서비스를 사용한다. 서비스kubectl expose 문서를 참조한다.
    • 클러스터 환경에 따라, 서비스는 회사 네트워크에만 노출되기도 하며, 인터넷에 노출되는 경우도 있다. 이 경우 노출되는 서비스의 보안 여부를 고려해야 한다. 해당 서비스는 자체적으로 인증을 수행하는가?
    • 파드는 서비스 뒤에 위치시킨다. 레플리카들의 집합에서 특정 파드 하나에 debugging 같은 목적으로 접근하려면 해당 파드에 고유의 레이블을 붙이고 셀렉터에 해당 레이블을 선택하는 신규 서비스를 생성한다.
    • 대부분의 경우에는 애플리케이션 개발자가 노드 IP를 통해 직접 노드에 접근할 필요는 없다.
  • Proxy Verb를 사용하여 서비스, 노드, 파드에 접근.
    • 원격 서비스에 접근하기에 앞서 apiserver의 인증과 인가를 받아야 한다. 서비스가 인터넷에 노출하기에 보안이 충분하지 않거나 노드 IP 상의 포트에 접근을 하려고 하거나 debugging을 하려면 이를 사용한다.
    • 어떤 web 애플리케이션에서는 프록시가 문제를 일으킬 수 있다.
    • HTTP/HTTPS에서만 동작한다.
    • 여기에서 설명하고 있다.
  • 클러스터 내 노드 또는 파드에서 접근.
    • 파드를 실행한 다음, kubectl exec를 사용하여 해당 파드의 셸로 접속한다. 해당 셸에서 다른 노드, 파드, 서비스에 연결한다.
    • 어떤 클러스터는 클러스터 내의 노드에 ssh 접속을 허용하기도 한다. 이런 클러스터에서는 클러스터 서비스에 접근도 가능하다. 이는 비표준 방식으로 특정 클러스터에서는 동작하지만 다른 클러스터에서는 동작하지 않을 수 있다. 브라우저와 다른 도구들이 설치되지 않았거나 설치되었을 수 있다. 클러스터 DNS가 동작하지 않을 수도 있다.

빌트인 서비스 검색

일반적으로 kube-system에 의해 클러스터에 실행되는 몇 가지 서비스가 있다. kubectl cluster-info 커맨드로 이 서비스의 리스트를 볼 수 있다.

kubectl cluster-info

결괏값은 다음과 같을 것이다.

Kubernetes master is running at https://104.197.5.247
elasticsearch-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy

이는 각 서비스에 접근하기 위한 proxy-verb URL을 보여준다. 예를 들어 위 클러스터는 클러스터 수준의 logging(Elasticsearch 사용)이 활성화되었으므로 적절한 인증을 통과하여 https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/로 접근할 수 있다. 예를 들어 kubectl proxy로 http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/를 통해 logging에 접근할 수도 있다. (인증을 통과하는 방법이나 kubectl proxy를 사용하는 것은 쿠버네티스 API를 사용해서 클러스터에 접근하기을 참조한다.)

수작업으로 apiserver proxy URL을 구축

위에서 언급한 것처럼 서비스의 proxy URL을 검색하는 데 kubectl cluster-info 커맨드를 사용할 수 있다. 서비스 endpoint, 접미사, 매개변수를 포함하는 proxy URL을 생성하려면 해당 서비스에 http://kubernetes_master_address/api/v1/namespaces/namespace_name/services/service_name[:port_name]/proxy 형식의 proxy URL을 덧붙인다.

당신이 포트에 이름을 지정하지 않았다면 URL에 port_name 을 지정할 필요는 없다. 이름이 있는 포트와 이름이 없는 포트 모두에 대하여, port_name 이 들어갈 자리에 포트 번호를 기재할 수도 있다.

기본적으로 API server는 http를 사용하여 서비스를 프록시한다. https를 사용하려면 다음과 같이 서비스 네임의 접두사에 https:를 붙인다. http://kubernetes_master_address/api/v1/namespaces/namespace_name/services/https:service_name:[port_name]/proxy

URL의 네임 부분에 지원되는 양식은 다음과 같다.

  • <service_name> - http를 사용하여 기본값 또는 이름이 없는 포트로 프록시한다.
  • <service_name>:<port_name> - http를 사용하여 지정된 포트 이름 또는 포트 번호로 프록시한다.
  • https:<service_name>: - https를 사용하여 기본값 또는 이름이 없는 포트로 프록시한다. (마지막 콜론:에 주의)
  • https:<service_name>:<port_name> - https를 사용하여 지정된 포트 이름 또는 포트 번호로 프록시한다.
예제들
  • Elasticsearch 서비스 endpoint _search?q=user:kimchy에 접근하려면 http://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy를 사용할 수 있다.
  • Elasticsearch 클러스터 상태 정보 _cluster/health?pretty=true에 접근하려면 https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true를 사용할 수 있다.
{
  "cluster_name" : "kubernetes_logging",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 5,
  "active_shards" : 5,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 5
}

클러스터 상에서 실행되는 서비스에 웹브라우저를 사용하여 접근

브라우저의 주소창에 apiserver proxy url을 넣을 수도 있다. 하지만

  • 웹브라우저는 일반적으로 토큰을 전달할 수 없으므로 basic (password) auth를 사용해야 할 것이다. basic auth를 수용할 수 있도록 apiserver를 구성할 수 있지만, 당신의 클러스터가 basic auth를 수용할 수 있도록 구성되어 있지 않을 수도 있다.
  • 몇몇 web app은 동작하지 않을 수도 있다. 특히 proxy path prefix를 인식하지 않는 방식으로 url을 구성하는 client side javascript를 가진 web app은 동작하지 않을 수 있다.

요청 redirect

redirect 기능은 deprecated되고 제거 되었다. 대신 (아래의) 프록시를 사용하기를 바란다.

다양한 프록시들

쿠버네티스를 사용하면서 당신이 접할 수 있는 몇 가지 다른 프록시들이 존재한다.

  1. kubectl proxy:

    • 사용자의 데스크탑이나 파드 내에서 실행한다
    • localhost 주소에서 쿠버네티스 apiserver로 프록시한다
    • 프록시하는 클라이언트는 HTTP를 사용한다
    • apiserver의 프록시는 HTTPS를 사용한다
    • apiserver를 위치지정한다
    • 인증 header들을 추가한다
  2. apiserver proxy:

    • apiserver 내의 빌트인 bastion이다
    • 다른 방식으로는 연결할 수 없는 클러스터 외부의 사용자를 클러스터 IP로 연결한다
    • apiserver process들 내에서 실행된다
    • 프록시하는 클라이언트는 HTTPS를 사용한다(또는 apiserver가 http로 구성되었다면 http)
    • 타겟으로의 프록시는 가용정보를 사용하는 프록시에 의해서 HTTP 또는 HTTPS를 사용할 수도 있다
    • 노드, 파드, 서비스에 접근하는 데 사용될 수 있다
    • 서비스에 접근하는 데 사용되면 load balacing한다
  3. kube proxy:

    • 각 노드 상에서 실행된다
    • UDP와 TCP를 프록시한다
    • HTTP를 인지하지 않는다
    • load balancing을 제공한다
    • 서비스에 접근하는 데에만 사용된다
  4. apiserver(s) 전면의 Proxy/Load-balancer:

    • 존재내용과 구현사항은 클러스터 별로 다양하다(예. nginx)
    • 모든 클라이언트와 하나 이상의 apiserver들의 사이에 위치한다
    • apiserver가 여러 대 존재한다면 load balancer로 동작한다
  5. 외부 서비스의 Cloud Load Balancer들:

    • Cloud provider들에 의해서 제공된다(예. AWS ELB, Google Cloud Load Balancer)
    • 쿠버네티스 서비스의 타입이 LoadBalancer라면 자동으로 생성된다
    • UDP/TCP 만 사용한다
    • cloud provider마다 구현된 내용이 상이하다

일반적으로 쿠버네티스 사용자들은 처음 두 타입이 아닌 다른 방식은 고려할 필요가 없지만 클러스터 관리자는 나머지 타입을 적절하게 구성해줘야 한다.

4.9.3 - 다중 클러스터 접근 구성

이 페이지에서는 구성 파일을 사용하여 다수의 클러스터에 접근할 수 있도록 설정하는 방식을 보여준다. 클러스터, 사용자, 컨텍스트가 하나 이상의 구성 파일에 정의된 다음 kubectl config use-context 커맨드를 사용하여 클러스터를 빠르게 변경할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

kubectl이 설치되었는지 확인하려면, kubectl version --client을 실행한다. kubectl 버전은 클러스터의 API 서버 버전과 마이너 버전 하나 차이 이내여야 한다.

클러스터, 사용자, 컨텍스트 정의

당신이 개발 작업을 위한 클러스터와 스크래치 작업을 위한 클러스터를 가지고 있다고 가정해보자. development 클러스터에서는 프런트 엔드 개발자들이 frontend라는 네임스페이스에서 작업을 하고 있고, 스토리지 개발자들은 storage라는 네임스페이스에서 작업을 하고 있다. scratch 클러스터에서는 개발자들이 default 네임스페이스에서 개발하거나 필요에 따라 보조 네임스페이스들을 생성하고 있다. development 클러스터에 접근하려면 인증서로 인증을 해야 하고, scratch 클러스터에 접근하려면 사용자네임과 패스워드로 인증을 해야 한다.

config-exercise라는 디렉터리를 생성한다. config-exercise 디렉터리에 다음 내용을 가진 config-demo라는 파일을 생성한다.

apiVersion: v1
kind: Config
preferences: {}

clusters:
- cluster:
  name: development
- cluster:
  name: scratch

users:
- name: developer
- name: experimenter

contexts:
- context:
  name: dev-frontend
- context:
  name: dev-storage
- context:
  name: exp-scratch

구성 파일은 클러스터들, 사용자들, 컨텍스트들을 기술한다. config-demo 파일은 두 클러스터들과 두 사용자들, 세 컨텍스트들을 기술하기 위한 프레임워크를 가진다.

config-exercise 디렉터리로 이동한다. 그리고 다음 커맨드들을 실행하여 구성 파일에 클러스터의 세부사항들을 추가한다.

kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster scratch --server=https://5.6.7.8 --insecure-skip-tls-verify

사용자의 세부사항들을 구성 파일에 추가한다.

kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-seefile
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password

컨텍스트 세부사항들을 구성 파일에 추가한다.

kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-scratch --cluster=scratch --namespace=default --user=experimenter

config-demo 파일을 열어서 세부사항들이 추가되었는지 확인한다. config-demo 파일을 열어보는 것 대신에 config view 커맨드를 사용할 수도 있다.

kubectl config --kubeconfig=config-demo view

두 클러스터, 두 사용자, 세 컨텍스트들이 출력 결과로 나온다.

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
- cluster:
    insecure-skip-tls-verify: true
    server: https://5.6.7.8
  name: scratch
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: scratch
    namespace: default
    user: experimenter
  name: exp-scratch
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file
- name: experimenter
  user:
    password: some-password
    username: exp

fake-ca-file, fake-cert-file, fake-key-file은 인증서 파일들의 실제 경로 이름을 위한 플레이스홀더(placeholder)이다. 당신의 환경에 맞게 이들을 실제 인증서 경로로 변경해줘야 한다.

만약 당신이 인증서 파일들의 경로 대신에 여기에 포함된 base64로 인코딩된 데이터를 사용하려고 한다면 이 경우 키에 -data 접미사를 추가해야 한다. 예를 들면 certificate-authority-data, client-certificate-data, client-key-data 같이 사용할 수 있다.

컨텍스트는 세 가지(클러스터, 사용자, 네임스페이스) 요소들로 이뤄진다. 예를 들어 dev-frontend 컨텍스트는 "development 클러스터의 frontend 네임스페이스에 접근하는데 developer 사용자 자격증명을 사용하라고 알려준다."

현재 컨텍스트를 설정한다.

kubectl config --kubeconfig=config-demo use-context dev-frontend

이제 당신이 kubectl 커맨드를 입력할 때마다 dev-frontend 컨텍스트에 명시된 클러스터와 네임스페이스 상에서 동작하게 될 것이다. 그리고 커맨드는 dev-frontend 컨텍스트 내에 명시된 사용자 자격증명을 사용할 것이다.

현재 컨텍스트에 관련된 구성 정보만을 보려면 --minify 플래그를 사용한다.

kubectl config --kubeconfig=config-demo view --minify

dev-frontend 컨텍스트에 관련된 구성 정보가 출력 결과로 표시될 것이다.

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file

이제 당신이 잠시 scratch 클러스터에서 작업하려고 한다고 가정해보자.

현재 컨텍스트를 exp-scratch로 변경한다.

kubectl config --kubeconfig=config-demo use-context exp-scratch

이제 당신이 실행하는 모든 kubectl 커맨드는 scratch 클러스터의 default 네임스페이스에 적용되며 exp-scratch 컨텍스트에 나열된 사용자의 자격증명을 사용할 것이다.

현재의 컨텍스트인 exp-scratch에 관련된 설정을 보자.

kubectl config --kubeconfig=config-demo view --minify

마지막으로 당신이 development 클러스터의 storage 네임스페이스에서 잠시 작업을 하려고 한다고 가정해보자.

현재 컨텍스트를 dev-storage로 변경한다.

kubectl config --kubeconfig=config-demo use-context dev-storage

현재 컨텍스트인 dev-storage에 관련된 설정을 보자.

kubectl config --kubeconfig=config-demo view --minify

두 번째 구성 파일 생성

config-exercise 디렉터리에서 다음 내용으로 config-demo-2라는 파일을 생성한다.

apiVersion: v1
kind: Config
preferences: {}

contexts:
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up

위 구성 파일은 dev-ramp-up이라는 신규 컨텍스트를 정의한다.

KUBECONFIG 환경 변수 설정

KUBECONFIG라는 환경 변수를 가지고 있는지 확인해보자. 만약 가지고 있다면, 이후에 복원할 수 있도록 KUBECONFIG 환경 변수의 현재 값을 저장한다. 예:

리눅스

export KUBECONFIG_SAVED=$KUBECONFIG

윈도우 PowerShell

$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG

KUBECONFIG 환경 변수는 구성 파일들의 경로의 리스트이다. 이 리스트는 리눅스와 Mac에서는 콜론으로 구분되며 윈도우에서는 세미콜론으로 구분된다. KUBECONFIG 환경 변수를 가지고 있다면, 리스트에 포함된 구성 파일들에 익숙해지길 바란다.

다음 예와 같이 임시로 KUBECONFIG 환경 변수에 두 개의 경로들을 덧붙여보자.

리눅스

export KUBECONFIG=$KUBECONFIG:config-demo:config-demo-2

윈도우 PowerShell

$Env:KUBECONFIG=("config-demo;config-demo-2")

config-exercise 디렉터리에서 다음 커맨드를 입력한다.

kubectl config view

당신의 KUBECONFIG 환경 변수에 나열된 모든 파일들이 합쳐진 정보가 출력 결과로 표시될 것이다. 특히, 합쳐진 정보가 config-demo-2 파일의 dev-ramp-up 컨텍스트와 config-demo 파일의 세 개의 컨텍스트들을 가지고 있다는 것에 주목하길 바란다.

contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: scratch
    namespace: default
    user: experimenter
  name: exp-scratch

kubeconfig 파일들을 어떻게 병합하는지에 대한 상세정보는 kubeconfig 파일을 사용하여 클러스터 접근 구성하기를 참조한다.

$HOME/.kube 디렉터리 탐색

만약 당신이 이미 클러스터를 가지고 있고 kubectl을 사용하여 해당 클러스터를 제어하고 있다면, 아마 $HOME/.kube 디렉터리에 config라는 파일을 가지고 있을 것이다.

$HOME/.kube로 가서 어떤 파일들이 존재하는지 보자. 보통 config라는 파일이 존재할 것이다. 해당 디렉터리 내에는 다른 구성 파일들도 있을 수 있다. 간단하게 말하자면 당신은 이 파일들의 컨텐츠에 익숙해져야 한다.

$HOME/.kube/config를 KUBECONFIG 환경 변수에 추가

당신이 $HOME/.kube/config 파일을 가지고 있는데 KUBECONFIG 환경 변수에 나타나지 않는다면 KUBECONFIG 환경 변수에 추가해보자. 예:

리눅스

export KUBECONFIG=$KUBECONFIG:$HOME/.kube/config

윈도우 Powershell

$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME\.kube\config"

이제 KUBECONFIG 환경 변수에 리스트에 포함된 모든 파일들이 합쳐진 구성 정보를 보자. config-exercise 디렉터리에서 다음 커맨드를 실행한다.

kubectl config view

정리

KUBECONFIG 환경 변수를 원래 값으로 되돌려 놓자. 예를 들면:

리눅스

export KUBECONFIG=$KUBECONFIG_SAVED

윈도우 PowerShell

$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED

다음 내용

4.9.4 - 포트 포워딩을 사용해서 클러스터 내 애플리케이션에 접근하기

이 페이지는 kubectl port-forward 를 사용해서 쿠버네티스 클러스터 내에서 실행중인 MongoDB 서버에 연결하는 방법을 보여준다. 이 유형의 연결은 데이터베이스 디버깅에 유용할 수 있다.

시작하기 전에

  • 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

    쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.10. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

  • MongoDB Shell을 설치한다.

MongoDB 디플로이먼트와 서비스 생성하기

  1. MongoDB를 실행하기 위해 디플로이먼트를 생성한다.

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-deployment.yaml
    

    성공적인 명령어의 출력은 디플로이먼트가 생성됐다는 것을 확인해준다.

    deployment.apps/mongo created
    

    파드 상태를 조회하여 파드가 준비되었는지 확인한다.

    kubectl get pods
    

    출력은 파드가 생성되었다는 것을 보여준다.

    NAME                     READY   STATUS    RESTARTS   AGE
    mongo-75f59d57f4-4nd6q   1/1     Running   0          2m4s
    

    디플로이먼트 상태를 조회한다.

    kubectl get deployment
    

    출력은 디플로이먼트가 생성되었다는 것을 보여준다.

    NAME    READY   UP-TO-DATE   AVAILABLE   AGE
    mongo   1/1     1            1           2m21s
    

    디플로이먼트는 자동으로 레플리카셋을 관리한다. 아래의 명령어를 사용하여 레플리카셋 상태를 조회한다.

    kubectl get replicaset
    

    출력은 레플리카셋이 생성되었다는 것을 보여준다.

    NAME               DESIRED   CURRENT   READY   AGE
    mongo-75f59d57f4   1         1         1       3m12s
    
  2. MongoDB를 네트워크에 노출시키기 위해 서비스를 생성한다.

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-service.yaml
    

    성공적인 커맨드의 출력은 서비스가 생성되었다는 것을 확인해준다.

    service/mongo created
    

    서비스가 생성되었는지 확인한다.

    kubectl get service mongo
    

    출력은 서비스가 생성되었다는 것을 보여준다.

    NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE
    mongo   ClusterIP   10.96.41.183   <none>        27017/TCP   11s
    
  3. MongoDB 서버가 파드 안에서 실행되고 있고, 27017번 포트에서 수신하고 있는지 확인한다.

    # mongo-75f59d57f4-4nd6q 를 당신의 파드 이름으로 대체한다.
    kubectl get pod mongo-75f59d57f4-4nd6q --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
    

    출력은 파드 내 MongoDB 포트 번호를 보여준다.

    27017
    

    (이는 인터넷 상의 MongoDB에 할당된 TCP 포트이다.)

파드의 포트를 로컬 포트로 포워딩하기

  1. kubectl port-forward 명령어는 파드 이름과 같이 리소스 이름을 사용하여 일치하는 파드를 선택해 포트 포워딩하는 것을 허용한다.

    # mongo-75f59d57f4-4nd6q 를 당신의 파드 이름으로 대체한다.
    kubectl port-forward mongo-75f59d57f4-4nd6q 28015:27017
    

    이것은

    kubectl port-forward pods/mongo-75f59d57f4-4nd6q 28015:27017
    

    또는

    kubectl port-forward deployment/mongo 28015:27017
    

    또는

    kubectl port-forward replicaset/mongo-75f59d57f4 28015:27017
    

    또는 다음과 같다.

    kubectl port-forward service/mongo 28015:27017
    

    위의 명령어들은 모두 동일하게 동작한다. 이와 유사하게 출력된다.

    Forwarding from 127.0.0.1:28015 -> 27017
    Forwarding from [::1]:28015 -> 27017
    
  1. MongoDB 커맨드라인 인터페이스를 실행한다.

    mongosh --port 28015
    
  2. MongoDB 커맨드라인 프롬프트에 ping 명령을 입력한다.

    db.runCommand( { ping: 1 } )
    

    성공적인 핑 요청을 반환한다.

    { ok: 1 }
    

선택적으로 kubectl 이 로컬 포트를 선택하게 하기

만약 특정 로컬 포트가 필요하지 않다면, kubectl 이 로컬 포트를 선택 및 할당하게 하여, 조금 더 단순한 문법으로 로컬 포트 충돌 관리를 위한 부담을 줄일 수 있다.

kubectl port-forward deployment/mongo :27017

kubectl 도구는 사용 중이 아닌 로컬 포트 번호를 찾는다 (낮은 포트 번호는 다른 애플리케이션에서 사용될 것이므로, 낮은 포트 번호를 피해서). 출력은 다음과 같을 것이다.

Forwarding from 127.0.0.1:63753 -> 27017
Forwarding from [::1]:63753 -> 27017

토의

로컬 28015 포트에 대한 연결은 MongoDB 서버가 실행중인 파드의 27017 포트로 포워딩된다. 이 연결로 로컬 워크스테이션에서 파드 안에서 실행 중인 데이터베이스를 디버깅하는데 사용할 수 있다.

다음 내용

kubectl port-forward에 대해 더 알아본다.

4.9.5 - 클러스터 내 애플리케이션에 접근하기 위해 서비스 사용하기

이 문서는 외부 클라이언트가 클러스터에서 실행 중인 애플리케이션에 접근하기 위해 사용하는 쿠버네티스 서비스 오브젝트를 생성하는 방법을 설명한다. 서비스는 실행 중인 두 개의 인스턴스를 갖는 애플리케이션에 대한 로드 밸런싱을 제공한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

목적

  • Hello World 애플리케이션 인스턴스 두 개를 실행한다.
  • 노드 포트를 노출하는 서비스 오브젝트를 생성한다.
  • 실행 중인 애플리케이션에 접근하기 위해 서비스 오브젝트를 사용한다.

두 개의 파드에서 실행 중인 애플리케이션에 대한 서비스 생성하기

다음은 애플리케이션 디플로이먼트(Deployment) 설정 파일이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  selector:
    matchLabels:
      run: load-balancer-example
  replicas: 2
  template:
    metadata:
      labels:
        run: load-balancer-example
    spec:
      containers:
        - name: hello-world
          image: gcr.io/google-samples/node-hello:1.0
          ports:
            - containerPort: 8080
              protocol: TCP
  1. 클러스터 내 Hello World 애플리케이션을 실행하자. 위 파일을 사용하여 애플리케이션 디플로이먼트를 생성하자.

    kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
    

    앞의 명령은 디플로이먼트 오브젝트와 연관된 레플리카셋(ReplicaSet) 오브젝트를 생성한다. 레플리카셋은 두 개의 파드를 갖고, 각각은 Hello World 애플리케이션을 실행한다.

  2. 디플로이먼트에 대한 정보를 보여준다.

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  3. 레플리카셋 오브젝트에 대한 정보를 보여준다.

    kubectl get replicasets
    kubectl describe replicasets
    
  4. 디플로이먼트를 노출하는 서비스 오브젝트를 생성한다.

    kubectl expose deployment hello-world --type=NodePort --name=example-service
    
  5. 서비스에 대한 정보를 보여준다.

    kubectl describe services example-service
    

    결과는 아래와 같다.

    Name:                   example-service
    Namespace:              default
    Labels:                 run=load-balancer-example
    Annotations:            <none>
    Selector:               run=load-balancer-example
    Type:                   NodePort
    IP:                     10.32.0.16
    Port:                   <unset> 8080/TCP
    TargetPort:             8080/TCP
    NodePort:               <unset> 31496/TCP
    Endpoints:              10.200.1.4:8080,10.200.2.5:8080
    Session Affinity:       None
    Events:                 <none>
    

    서비스의 노드포트(NodePort) 값을 메모하자. 예를 들어, 앞선 결과에서, 노드포트 값은 31496이다.

  6. Hello World 애플리케이션이 실행 중인 파드를 나열한다.

    kubectl get pods --selector="run=load-balancer-example" --output=wide
    

    결과는 아래와 같다.

    NAME                           READY   STATUS    ...  IP           NODE
    hello-world-2895499144-bsbk5   1/1     Running   ...  10.200.1.4   worker1
    hello-world-2895499144-m1pwt   1/1     Running   ...  10.200.2.5   worker2
    
  7. Hello World 파드가 실행 중인 노드들 중 하나의 노드에 대해 공용 IP 주소를 얻자. 이 주소를 얻는 방법은 어떻게 클러스터를 설치했는지에 따라 다르다. 예를 들어, Minikube를 사용하면, kubectl cluster-info를 실행하여 노드 주소를 알 수 있다. Google Compute Engine 인스턴스를 사용하면, gcloud compute instances list 명령어를 사용하여 노드들의 공용 주소를 알 수 있다.

  8. 선택한 노드에서 노드 포트에 대해 TCP 통신을 허용하도록 방화벽 규칙을 생성하자. 예를 들어, 서비스의 노드포트 값이 31568인 경우, 31568 포트로 TCP 통신을 허용하도록 방화벽 규칙을 생성하자. 다른 클라우드 공급자는 방화벽 규칙을 설정하는 다른 방법을 제공한다.

  9. Hello World 애플리케이션 접근을 위해 노드 주소와 노드 포트를 사용하자.

    curl http://<public-node-ip>:<node-port>
    

    <public-node-ip>는 노드의 공용 IP 주소이고, <node-port>는 서비스의 노드포트 값이다. 성공적인 요청에 대한 응답은 hello 메시지이다.

    Hello Kubernetes!
    

서비스 설정 파일 사용하기

kubectl expose를 사용하는 대신, 서비스 설정 파일을 사용해 서비스를 생성할 수 있다.

정리하기

서비스를 삭제하기 위해 다음 명령어를 입력하자.

kubectl delete services example-service

디플로이먼트, 레플리카셋, Hello World 애플리케이션이 실행 중인 파드를 삭제하기 위해 다음 명령어를 입력하자.

kubectl delete deployment hello-world

다음 내용

서비스와 애플리케이션 연결하기에 대해 더 알아본다.

4.9.6 - 서비스를 사용하여 프론트엔드를 백엔드에 연결

이 작업은 프론트엔드백엔드 마이크로서비스를 어떻게 생성하는지를 설명한다. 백엔드 마이크로서비스는 인사하기(hello greeter)이다. 프론트엔드는 nginx 및 쿠버네티스 서비스 오브젝트를 사용해 백엔드를 노출한다.

목적

  • 디플로이먼트(Deployment) 오브젝트를 사용해 샘플 hello 백엔드 마이크로서비스를 생성하고 실행한다.
  • 서비스 오브젝트를 사용하여 백엔드 마이크로서비스의 여러 복제본으로 트래픽을 보낸다.
  • 디플로이먼트 오브젝트를 사용하여 nginx 프론트엔드 마이크로서비스를 생성하고 실행한다.
  • 트래픽을 백엔드 마이크로서비스로 보내도록 프론트엔드 마이크로서비스를 구성한다.
  • type=LoadBalancer 의 서비스 오브젝트를 사용해 클러스터 외부에 프론트엔드 마이크로서비스를 노출한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

이 작업은 지원되는 환경이 필요한 외부 로드밸런서가 있는 서비스를 사용한다. 만약, 이를 지원하지 않는 환경이라면, 노드포트 서비스 타입을 대신 사용할 수 있다.

디플로이먼트를 사용해 백엔드 생성하기

백엔드는 인사하기라는 간단한 마이크로서비스이다. 여기에 백엔드 디플로이먼트 구성 파일이 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
      tier: backend
      track: stable
  replicas: 7
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80

백엔드 디플로이먼트를 생성한다.

kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml

백엔드 디플로이먼트에 관한 정보를 본다.

kubectl describe deployment backend

결과는 아래와 같다.

Name:                           backend
Namespace:                      default
CreationTimestamp:              Mon, 24 Oct 2016 14:21:02 -0700
Labels:                         app=hello
                                tier=backend
                                track=stable
Annotations:                    deployment.kubernetes.io/revision=1
Selector:                       app=hello,tier=backend,track=stable
Replicas:                       3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:                   RollingUpdate
MinReadySeconds:                0
RollingUpdateStrategy:          1 max unavailable, 1 max surge
Pod Template:
  Labels:       app=hello
                tier=backend
                track=stable
  Containers:
   hello:
    Image:              "gcr.io/google-samples/hello-go-gke:1.0"
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable
OldReplicaSets:                 <none>
NewReplicaSet:                  hello-3621623197 (3/3 replicas created)
Events:
...

hello 서비스 오브젝트 생성하기

프론트엔드에서 백엔드로 요청을 보내는 핵심은 백엔드 서비스이다. 서비스는 백엔드 마이크로서비스에 언제든 도달하기 위해 변하지 않는 IP 주소와 DNS 이름 항목을 생성한다. 서비스는 트래픽을 보내는 파드를 찾기 위해 셀렉터를 사용한다.

먼저, 서비스 구성 파일을 살펴보자.

apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: http

구성 파일에서 hello 라는 이름의 서비스가 app: hellotier: backend 레이블을 갖는 파드에 트래픽을 보내는 것을 볼 수 있다.

백엔드 서비스를 생성한다.

kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml

이 시점에서 hello 애플리케이션의 복제본 3개를 실행하는 backend 디플로이먼트가 있고, 해당 백엔드로 트래픽을 보내는 서비스가 있다. 그러나, 이 서비스는 클러스터 외부에서 사용할 수 없거나 확인할 수 없다.

프론트엔드 생성하기

이제 백엔드를 실행했으므로, 클러스터 외부에서 접근할 수 있는 프론트엔드를 만들고, 백엔드로의 요청을 프록시하여 백엔드에 연결할 수 있다.

프론트엔드는 백엔드 서비스에 지정된 DNS 이름을 사용하여 백엔드 워커 파드에 요청을 보낸다. DNS 이름은 examples/service/access/backend-service.yaml 구성 파일의 name 필드 값인 hello 이다.

프론트엔드 디플로이먼트 안의 파드는 hello 백엔드 서비스에 대한 요청을 프록시하도록 구성된 nginx 이미지를 실행한다. 다음은 nginx 구성 파일이다.

# The identifier Backend is internal to nginx, and used to name this specific upstream
upstream Backend {
    # hello is the internal DNS name used by the backend Service inside Kubernetes
    server hello;
}

server {
    listen 80;

    location / {
        # The following statement will proxy traffic to the upstream named Backend
        proxy_pass http://Backend;
    }
}

백엔드와 같이, 프론트엔드는 디플로이먼트와 서비스를 갖고 있다. 백엔드 서비스와 프론트엔드 서비스 간에 주목해야 할 중요한 차이점은 프론트엔드 서비스의 구성에 type: LoadBalancer 가 있다는 것이다. 즉, 서비스가 클라우드 공급자가 프로비저닝한 로드 밸런서를 사용하고 클러스터 외부에서 접근할 수 있음을 의미한다.

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
  - protocol: "TCP"
    port: 80
    targetPort: 80
  type: LoadBalancer
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: hello
      tier: frontend
      track: stable
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
        - name: nginx
          image: "gcr.io/google-samples/hello-frontend:1.0"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx","-s","quit"]
...

프론트엔드 디플로이먼트와 서비스를 생성한다.

kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/frontend-service.yaml

결과는 두 리소스가 생성되었음을 확인한다.

deployment.apps/frontend created
service/frontend created

프론트엔드 서비스와 통신하기

일단 로드밸런서 타입의 서비스를 생성하면, 이 명령어를 사용해 외부 IP를 찾을 수 있다.

kubectl get service frontend --watch

frontend 서비스의 구성을 보여주고, 변경 사항을 주시한다. 처음에, 외부 IP는 <pending> 으로 나열된다.

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   <pending>     80/TCP   10s

하지만, 외부 IP가 생성되자마자 구성은 EXTERNAL-IP 제목 아래에 새로운 IP를 포함하여 갱신한다.

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   XXX.XXX.XXX.XXX    80/TCP   1m

이제 해당 IP는 클러스터 외부에서 frontend 서비스와 통신하는데 사용된다.

프론트엔드 통해서 트래픽 보내기

이제 프론트엔드와 백엔드가 연결되었다. 프론트엔드 서비스의 외부 IP에서 curl 명령을 사용해 엔드포인트에 도달할 수 있다.

curl http://${EXTERNAL_IP} # 앞의 예에서 본 EXTERNAL-IP로 수정한다

결과로 백엔드에서 생성된 메시지가 보인다.

{"message":"Hello"}

정리하기

서비스를 삭제하기 위해, 아래 명령어를 입력하자.

kubectl delete services frontend backend

백엔드와 프론트엔드 애플리케이션에서 실행 중인 디플로이먼트, 레플리카셋, 파드를 삭제하기 위해, 아래 명령어를 입력하자.

kubectl delete deployment frontend backend

다음 내용

4.9.7 - NGINX 인그레스(Ingress) 컨트롤러로 Minikube에서 인그레스 설정하기

인그레스는 클러스터의 서비스에 대한 외부 액세스를 허용하는 규칙을 정의하는 API 객체이다. 인그레스 컨트롤러는 인그레스에 설정된 규칙을 이행한다.

이 페이지에서는 HTTP URI에 따라 요청을 Service web 또는 web2로 라우팅하는 간단한 인그레스를 설정하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.19. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version. 만약 이보다 더 이전 버전의 쿠버네티스를 사용하고 있다면, 해당 쿠버네티스 버전의 문서를 참고한다.

Minikube 클러스터 생성하기

Katacoda 활용하기
로컬에서 생성하기
이미 로컬에 Minikube를 설치했다면, minikube start를 실행하여 클러스터를 생성한다.

인그레스 컨트롤러 활성화

  1. NGINX 인그레스 컨트롤러를 활성화하기 위해 다음 명령을 실행한다.

    minikube addons enable ingress
    
  2. NGINX 인그레스 컨트롤러가 실행 중인지 확인한다.

    kubectl get pods -n ingress-nginx
    

    결과는 다음과 같다.

    NAME                                        READY   STATUS      RESTARTS    AGE
    ingress-nginx-admission-create-g9g49        0/1     Completed   0          11m
    ingress-nginx-admission-patch-rqp78         0/1     Completed   1          11m
    ingress-nginx-controller-59b45fb494-26npt   1/1     Running     0          11m
    

    kubectl get pods -n kube-system
    

    결과는 다음과 같다.

    NAME                                        READY     STATUS    RESTARTS   AGE
    default-http-backend-59868b7dd6-xb8tq       1/1       Running   0          1m
    kube-addon-manager-minikube                 1/1       Running   0          3m
    kube-dns-6dcb57bcc8-n4xd4                   3/3       Running   0          2m
    kubernetes-dashboard-5498ccf677-b8p5h       1/1       Running   0          2m
    nginx-ingress-controller-5984b97644-rnkrg   1/1       Running   0          1m
    storage-provisioner                         1/1       Running   0          2m
    

    nginx-ingress-controller-로 시작하는 파드가 있는지 확인한다.

hello, world 앱 배포하기

  1. 다음 명령을 사용하여 디플로이먼트(Deployment)를 생성한다.

    kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
    

    결과는 다음과 같다.

    deployment.apps/web created
    
  2. 디플로이먼트를 노출시킨다.

    kubectl expose deployment web --type=NodePort --port=8080
    

    결과는 다음과 같다.

    service/web exposed
    
  3. 서비스(Service)가 생성되고 노드 포트에서 사용할 수 있는지 확인한다.

    kubectl get service web
    

    결과는 다음과 같다.

    NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    web       NodePort   10.104.133.249   <none>        8080:31637/TCP   12m
    
  4. 노드포트(NodePort)를 통해 서비스에 접속한다.

    minikube service web --url
    

    결과는 다음과 같다.

    http://172.17.0.15:31637
    

    결과는 다음과 같다.

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

    이제 Minikube IP 주소와 노드포트를 통해 샘플 앱에 액세스할 수 있다. 다음 단계에서는 인그레스 리소스를 사용하여 앱에 액세스할 수 있다.

인그레스 생성하기

다음 매니페스트는 hello-world.info를 통해 서비스로 트래픽을 보내는 인그레스를 정의한다.

  1. 다음 파일을 통해 example-ingress.yaml을 만든다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080
  1. 다음 명령어를 실행하여 인그레스 오브젝트를 생성한다.

    kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml
    

    결과는 다음과 같다.

    ingress.networking.k8s.io/example-ingress created
    
  2. IP 주소가 설정되었는지 확인한다.

    kubectl get ingress
    

다음 예시와 같이, ADDRESS 열에서 IPv4 주소를 확인할 수 있다.

NAME              CLASS    HOSTS              ADDRESS        PORTS   AGE
example-ingress   <none>   hello-world.info   172.17.0.15    80      38s
  1. 호스트 컴퓨터의 /etc/hosts 파일 맨 아래에 다음 행을 추가한다 (관리자 권한 필요).

    172.17.0.15 hello-world.info
    

    이렇게 하면, 웹 브라우저가 hello-world.info URL에 대한 요청을 Minikube로 전송한다.

  2. 인그레스 컨트롤러가 트래픽을 전달하는지 확인한다.

    curl hello-world.info
    

    결과는 다음과 같다.

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

두 번째 디플로이먼트 생성하기

  1. 다음 명령을 사용하여 두 번째 디플로이먼트를 생성한다.

    kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
    

    결과는 다음과 같다.

    deployment.apps/web2 created
    
  2. 두 번째 디플로이먼트를 노출시킨다.

    kubectl expose deployment web2 --port=8080 --type=NodePort
    

    결과는 다음과 같다.

    service/web2 exposed
    

기존 인그레스 수정하기

  1. 기존 example-ingress.yaml 매니페스트를 편집하고, 하단에 다음 줄을 추가한다.

          - path: /v2
            pathType: Prefix
            backend:
              service:
                name: web2
                port:
                  number: 8080
    
  2. 변경 사항을 적용한다.

    kubectl apply -f example-ingress.yaml
    

    결과는 다음과 같다.

    ingress.networking/example-ingress configured
    

인그레스 테스트하기

  1. Hello World 앱의 첫 번째 버전에 액세스한다.

    curl hello-world.info
    

    결과는 다음과 같다.

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    
  2. Hello World 앱의 두 번째 버전에 액세스한다.

    curl hello-world.info/v2
    

    결과는 다음과 같다.

    Hello, world!
    Version: 2.0.0
    Hostname: web2-75cd47646f-t8cjk
    

다음 내용

4.9.8 - 클러스터 내 모든 컨테이너 이미지 목록 보기

이 문서는 kubectl을 이용하여 클러스터 내 모든 컨테이너 이미지 목록을 조회하는 방법에 관해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

이 작업에서는 kubectl을 사용하여 클러스터 내 모든 파드의 정보를 조회하고, 결과값의 서식을 변경하여 각 파드에 대한 컨테이너 이미지 목록으로 재구성할 것이다.

모든 네임스페이스의 모든 컨테이너 이미지 가져오기

  • kubectl get pods --all-namespaces 를 사용하여 모든 네임스페이스의 모든 파드 정보를 가져온다.
  • 컨테이너 이미지 이름만 출력하기 위해 -o jsonpath={.items[*].spec.containers[*].image} 를 사용한다. 이 명령어는 결과값으로 받은 json을 반복적으로 파싱하여, image 필드만을 출력한다.
    • jsonpath를 사용하는 방법에 대해 더 많은 정보를 얻고 싶다면 Jsonpath 지원을 확인한다.
  • 다음의 표준 툴을 이용해서 결과값을 처리한다. tr, sort, uniq
    • tr 을 사용하여 공백을 줄 바꾸기로 대체한다.
    • sort 를 사용하여 결과값을 정렬한다.
    • uniq 를 사용하여 이미지 개수를 합산한다.
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

이 커맨드는 결과값으로 나온 모든 아이템 중에 image 라고 명명된 필드를 모두 출력한다.

이와 다른 방법으로 파드 이미지 필드 값의 절대 경로를 사용할 수 있다. 이것은 필드명이 반복될 때에도 정확한 값을 출력하도록 보장한다. 예) 결과값 중에 많은 필드들이 name으로 명명되었을 경우,

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}"

이 jsonpath는 다음과 같이 해석할 수 있다.

  • .items[*]: 각 결과값에 대하여
  • .spec: spec 값을 가져온다.
  • .containers[*]: 각 컨테이너에 대하여
  • .image: image 값을 가져온다.

각 파드의 컨테이너 이미지 보기

range 연산을 사용하여 명령어의 결과값에서 각각의 요소들을 반복하여 출력할 수 있다.

kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort

파드 레이블로 필터링된 컨테이너 이미지 목록 보기

특정 레이블에 맞는 파드를 지정하기 위해서 -l 플래그를 사용한다. 아래의 명령어 결과값은 app=nginx 레이블에 일치하는 파드만 출력한다.

kubectl get pods --all-namespaces -o=jsonpath="{.items[*].spec.containers[*].image}" -l app=nginx

파드 네임스페이스로 필터링된 컨테이너 이미지 목록 보기

특정 네임스페이스의 파드를 지정하려면, 네임스페이스 플래그를 사용한다. 아래의 명령어 결과값은 kube-system 네임스페이스에 있는 파드만 출력한다.

kubectl get pods --namespace kube-system -o jsonpath="{.items[*].spec.containers[*].image}"

jsonpath 대신 Go 템플릿을 사용하여 컨테이너 이미지 목록 보기

jsonpath의 대안으로 Kubectl은 Go 템플릿을 지원한다. 다음과 같이 결과값의 서식을 지정할 수 있다.

kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"

다음 내용

참조

4.9.9 - 공유 볼륨을 이용하여 동일한 파드의 컨테이너 간에 통신하기

이 페이지에서는 동일한 파드에서 실행 중인 두 개의 컨테이너 간에 통신할 때에, 어떻게 볼륨을 이용하는지 살펴본다. 컨테이너 간에 프로세스 네임스페이스 공유하기를 통해 통신할 수 있는 방법을 참고하자.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

두 개의 컨테이너를 실행하는 파드 생성

이 실습에서 두 개의 컨테이너를 실행하는 파드를 생성한다. 이 컨테이너들은 통신에 사용할 수 있는 볼륨을 공유한다. 아래는 이 파드의 구성 파일이다.

apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:

  restartPolicy: Never

  volumes:
  - name: shared-data
    emptyDir: {}

  containers:

  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo debian 컨테이너에서 안녕하세요 > /pod-data/index.html"]

이 구성 파일에는 파드가 shared-data로 명명한 볼륨을 가진 것을 알 수 있다.

첫 번째 컨테이너에는 nginx 웹 서버를 실행하는 구성 파일이 나열되어 있다. 공유 볼륨의 마운트 경로는 /usr/share/nginx/html이다. 두 번째 컨테이너는 debian 이미지 기반이고, 마운트 경로는 /pod-data이다. 두 번째 컨테이너는 다음 명령어를 실행한 후에 종료한다.

echo debian 컨테이너에서 안녕하세요 > /pod-data/index.html

두 번째 컨테이너는 index.html 파일을 nginx 웹 서버에서 호스팅하는 문서의 루트 디렉터리(/usr/share/nginx/html/)에 저장한다.

이제, 파드와 두 개의 컨테이너를 생성한다.

kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml

파드와 컨테이너의 정보를 확인한다.

kubectl get pod two-containers --output=yaml

출력의 일부는 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  ...
  name: two-containers
  namespace: default
  ...
spec:
  ...
  containerStatuses:

  - containerID: docker://c1d8abd1 ...
    image: debian
    ...
    lastState:
      terminated:
        ...
    name: debian-container
    ...

  - containerID: docker://96c1ff2c5bb ...
    image: nginx
    ...
    name: nginx-container
    ...
    state:
      running:
    ...

Debian 컨테이너가 종료되었음을 알 수 있고, nginx 컨테이너는 아직 실행 중이다.

nginx 컨테이너의 쉘(shell)을 실행한다.

kubectl exec -it two-containers -c nginx-container -- /bin/bash

쉘에서 nginx 웹 서버가 실행 중인지 확인한다.

root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl procps
root@two-containers:/# ps aux

출력은 아래와 유사하다.

USER       PID  ...  STAT START   TIME COMMAND
root         1  ...  Ss   21:12   0:00 nginx: master process nginx -g daemon off;

Debian 컨테이너에서 nginx 웹 서버가 호스팅하는 문서의 루트 디렉터리에 index.html 파일을 생성했었음을 상기하자. curl을 이용하여 nginx 웹 서버에 HTTP GET 요청을 보낸다.

root@two-containers:/# curl localhost

출력을 보면, nginx 웹 서버에서 debian 컨테이너에서 쓰여진 웹 페이지를 제공하는 것을 알 수 있다.

debian 컨테이너에서 안녕하세요

토의

파드가 여러 컨테이너를 가질 수 있는 주요 이유는 기본 애플리케이션을 보조할 도우미(helper) 애플리케이션을 제공하기 위해서이다. 도우미 애플리케이션의 일반적인 예로는 데이터를 가지고 오는 경우(data puller)나 데이터를 보내주는 경우(data pusher)이거나 프록시가 있다. 도우미와 기본 애플리케이션은 종종 서로 간에 통신을 해야 할 수 있다. 일반적으로 이는 이번 예제에서 살펴본 것 같이, 공유 파일 시스템을 통하거나, 루프백 네트워크 인터페이스 곧 로컬 호스트(localhost)를 통해서 이뤄진다. 이 패턴의 한가지 예는 웹 서버가 도우미 프로그램과 함께 Git 저장소에서 새 업데이트를 받아오는 경우이다.

이 예제에서 볼륨은 파드의 생명 주기 동안 컨테이너를 위한 통신 방법으로 이용했다. 파드가 삭제되고 재생성되면, 공유 볼륨에 저장된 데이터는 잃어버린다.

다음 내용

4.9.10 - 클러스터의 DNS 구성하기

쿠버네티스는 지원하는 모든 환경에서 기본으로 활성화된 DNS 클러스터 애드온을 제공한다. 쿠버네티스 1.11과 이후 버전에서는, CoreDNS가 권장되고 기본적으로 kubeadm과 함께 설치 된다.

쿠버네티스 클러스터의 CoreDNS 설정에 대한 더 많은 정보는, DNS 서비스 사용자화 하기을 본다. kube-dns와 함께 쿠버네티스 DNS를 사용하는 방법을 보여주는 예시는 쿠버네티스 DNS 샘플 플러그인을 본다.

4.10 - 모니터링, 로깅, 그리고 디버깅

모니터링 및 로깅을 설정하여 클러스터 문제를 해결하거나, 컨테이너화된 애플리케이션을 디버깅한다.

4.10.1 - 동작 중인 파드 디버그

이 페이지는 노드에서 동작 중인(혹은 크래시된) 파드를 디버그하는 방법에 대해 설명한다.

시작하기 전에

  • 여러분의 파드는 이미 스케줄링 되어 동작하고 있을 것이다. 만약 파드가 아직 동작중이지 않다면, 애플리케이션 트러블슈팅을 참고한다.
  • 일부 고급 디버깅 과정에서는 해당 파드가 어떤 노드에서 동작하고 있는지 알아야 하고, 해당 노드에서 쉘 명령어를 실행시킬 수 있어야 한다. kubectl을 사용하는 일반적인 디버깅 과정에서는 이러한 접근 권한이 필요하지 않다.

파드의 로그 확인하기

먼저, 확인하고자 하는 컨테이너의 로그를 확인한다.

kubectl logs ${POD_NAME} ${CONTAINER_NAME}

만약 컨테이너가 이전에 크래시 되었다면, 다음의 명령을 통해 컨테이너의 크래시 로그를 살펴볼 수 있다.

kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}

exec를 통해 컨테이너 디버깅하기

만약 컨테이너 이미지에 디버깅 도구가 포함되어 있다면, kubectl exec을 통해 특정 컨테이너에서 해당 명령들을 실행할 수 있다. (리눅스나 윈도우 OS를 기반으로 만들어진 이미지에는 대부분 디버깅 도구를 포함하고 있다.)

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

예를 들어, 동작 중인 카산드라 파드의 로그를 살펴보기 위해서는 다음과 같은 명령을 실행할 수 있다.

kubectl exec cassandra -- cat /var/log/cassandra/system.log

kubectl exec-i-t 옵션을 사용해서 터미널에서 접근할 수 있는 쉘을 실행시킬 수도 있다. 예를 들면 다음과 같다.

kubectl exec -it cassandra -- sh

더욱 상세한 내용은 다음 동작중인 컨테이너의 쉘에 접근하기를 참고하라.

임시(ephemeral) 디버그 컨테이너를 사용해서 디버깅하기

FEATURE STATE: Kubernetes v1.18 [alpha]

컨테이너가 크래시 됐거나 distroless 이미지처럼 컨테이너 이미지에 디버깅 도구를 포함하고 있지 않아 kubectl exec가 충분하지 않을 경우에는 임시(Ephemeral) 컨테이너를 사용하는 것이 인터랙티브한 트러블슈팅에 유용하다. kubectl v1.18 버전부터는 임시 컨테이너를 생성할 수 있는 알파 단계의 명령어가 있다.

임시 컨테이너를 사용한 디버깅 예시

kubectl debug 명령어를 사용해서 동작 중인 파드에 임시 컨테이너를 추가할 수 있다. 먼저, 다음과 같이 파드를 추가한다.

kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never

이 섹션의 예시에서는 디버깅 도구가 포함되지 않은 이미지의 사례를 보여드리기 위해 pause 컨테이너 이미지를 사용했는데, 이 대신 어떠한 이미지를 사용해도 될 것이다.

만약 kubectl exec을 통해 쉘을 생성하려 한다면 다음과 같은 에러를 확인할 수 있을 텐데, 그 이유는 이 이미지에 쉘이 존재하지 않기 때문이다.

kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown

이 명령어 대신 kubectl debug을 사용해서 디버깅 컨테이너를 생성할 수 있다. 만약 -i/--interactive 인자를 사용한다면, kubectl은 임시 컨테이너의 콘솔에 자동으로 연결할 것이다.

kubectl debug -it ephemeral-demo --image=busybox --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #

이 명령어는 새로운 busybox 컨테이너를 추가하고 해당 컨테이너로 연결한다. --target 파라미터를 사용하면 다른 컨테이너의 프로세스 네임스페이스를 대상으로 하게 된다. 여기서는 이 옵션이 꼭 필요한데, kubectl run이 생성하는 파드에 대해 프로세스 네임스페이스 공유를 활성화하지 않기 때문이다.

kubectl describe 명령을 사용하면 새롭게 생성된 임시 컨테이너의 상태를 확인할 수 있다.

kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
  debugger-8xzrl:
    Container ID:   docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
    Image:          busybox
    Image ID:       docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 12 Feb 2020 14:25:42 +0100
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

디버깅이 다 끝나면 kubectl delete을 통해 파드를 제거할 수 있다.

kubectl delete pod ephemeral-demo

파드의 복제본을 이용해서 디버깅하기

때때로 파드의 설정 옵션에 따라 특정 상황에서 트러블슈팅을 하기가 어려울 수 있다. 예를 들어, 만일 여러분의 컨테이너 이미지가 쉘을 포함하고 있지 않거나, 여러분의 애플리케이션이 컨테이너 시작에서 크래시가 발생한다면 kubectl exec을 이용해서 컨테이너를 트러블슈팅할 수 없을 수 있다. 이러한 상황에서는 kubectl debug을 사용해서 파드의 복제본을 디버깅을 위한 추가적인 설정 옵션과 함께 생성할 수 있다.

새 컨테이너와 함께 파드의 복제본 생성하기

만일 여러분의 애플리케이션이 동작은 하고 있지만 예상과는 다르게 동작하는 경우, 파드의 복제본에 새로운 컨테이너를 추가함으로써 추가적인 트러블슈팅 도구들을 파드에 함께 추가할 수 있다.

가령, 여러분의 애플리케이션 컨테이너 이미지는 busybox를 기반으로 하고 있는데 여러분은 busybox에는 없는 디버깅 도구를 필요로 한다고 가정해 보자. 이러한 시나리오는 kubectl run 명령을 통해 시뮬레이션 해볼 수 있다.

kubectl run myapp --image=busybox --restart=Never -- sleep 1d

다음의 명령을 실행시켜 디버깅을 위한 새로운 우분투 컨테이너와 함께 myapp-debug이란 이름의 myapp 컨테이너 복제본을 생성할 수 있다.

kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

명령어를 변경하며 파드의 복제본 생성하기

때때로 컨테이너의 명령어를 변경하는 것이 유용한 경우가 있는데, 예를 들면 디버그 플래그를 추가하기 위해서나 애플리케이션이 크래시 되는 경우이다.

다음의 kubectl run 명령을 통해 즉각적으로 크래시가 발생하는 애플리케이션의 사례를 시뮬레이션해 볼 수 있다.

kubectl run --image=busybox myapp -- false

kubectl describe pod myapp 명령을 통해 이 컨테이너에 크래시가 발생하고 있음을 확인할 수 있다.

Containers:
  myapp:
    Image:         busybox
    ...
    Args:
      false
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1

이러한 경우에 kubectl debug을 통해 명령어를 지정함으로써 해당 파드의 복제본을 인터랙티브 쉘로 생성할 수 있다.

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

이제 인터랙티브 쉘에 접근할 수 있으니 파일 시스템 경로를 확인하거나 동작 중인 컨테이너의 명령어를 직접 확인하는 등의 작업이 가능하다.

사용이 모두 끝나면, 디버깅에 사용된 파드들을 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

컨테이너 이미지를 변경하며 파드의 복제본 생성하기

특정한 경우에 여러분은 제대로 동작하지 않는 파드의 이미지를 기존 프로덕션 컨테이너 이미지에서 디버깅 빌드나 추가적인 도구를 포함한 이미지로 변경하고 싶을 수 있다.

이 사례를 보여주기 위해 kubectl run 명령을 통해 파드를 생성하였다.

kubectl run myapp --image=busybox --restart=Never -- sleep 1d

여기서는 kubectl debug 명령을 통해 해당 컨테이너 이미지를 ubuntu로 변경하며 복제본을 생성하였다.

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

--set-image의 문법은 kubectl set image와 동일하게 container_name=image 형식의 문법을 사용한다. *=ubuntu라는 의미는 모든 컨테이너의 이미지를 ubuntu로 변경하겠다는 의미이다.

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

노드의 쉘을 사용해서 디버깅하기

만약 위의 어떠한 방법도 사용할 수 없다면, 파드가 현재 동작 중인 노드를 찾아 호스트의 네임스페이스로 동작하는 특권 파드를 생성할 수 있다. 다음 kubectl debug 명령을 통해 해당 노드에서 인터랙티브한 쉘을 생성할 수 있다.

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#

노드에서 디버깅 세션을 생성할 때 유의해야 할 점은 다음과 같다.

  • kubectl debug는 노드의 이름에 기반해 새로운 파드의 이름을 자동으로 생성한다.
  • 컨테이너는 호스트 네임스페이스(IPC, 네트워크, PID 네임스페이스)에서 동작한다.
  • 노드의 루트 파일시스템은 /host에 마운트된다.

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod node-debugger-mynode-pdx84

4.10.2 - 동작중인 컨테이너의 셸에 접근하기

이 페이지는 동작중인 컨테이너에 접근하기 위해 kubectl exec을 사용하는 방법에 대해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

컨테이너의 셸에 접근하기

이 예시에서는 하나의 컨테이너를 가진 파드를 생성할 것이다. 이 컨테이너는 nginx 이미지를 실행한다. 해당 파드에 대한 설정 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  hostNetwork: true
  dnsPolicy: Default

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

다음을 통해 컨테이너가 동작하고 있는지 확인할 수 있다.

kubectl get pod shell-demo

동작중인 컨테이너의 셸에 접근한다.

kubectl exec --stdin --tty shell-demo -- /bin/bash

셸에 접근해서 다음처럼 루트 디렉토리를 확인해 볼 수 있다.

# Run this inside the container
ls /

접근한 셸에서 다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.

# You can run these example commands inside the container
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

nginx의 최상단 페이지 작성하기

앞에서 생성한 파드에 대한 설정을 살펴보아라. 파드에는 emptyDir 볼륨이 사용되었고, 이 컨테이너는 해당 볼륨을 /usr/share/nginx/html 경로에 마운트하였다.

접근한 셸 환경에서 /usr/share/nginx/html 디렉터리에 index.html 파일을 생성해 보아라.

# Run this inside the container
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

셸 환경에서 nginx 서버에 GET 요청을 시도해보면 다음과 같다.

# Run this in the shell inside your container
apt-get update
apt-get install curl
curl http://localhost/

출력 결과는 여러분이 index.html 파일에 작성한 텍스트를 출력할 것이다.

Hello shell demo

셸 사용이 모두 끝났다면 exit을 입력해 종료하라.

exit # To quit the shell in the container

컨테이너에서 개별 명령어 실행하기

셸이 아닌 일반적인 커맨드 환경에서 다음처럼 동작중인 컨테이너의 환경 변수를 출력할 수 있다.

kubectl exec shell-demo env

다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

파드에 한 개 이상의 컨테이너가 있을 경우 셸에 접근하기

만일 파드에 한 개 이상의 컨테이너가 있을 경우, kubectl exec 명령어에 --container 혹은 -c 옵션을 사용해서 컨테이너를 지정하라. 예를 들어, 여러분이 my-pod라는 이름의 파드가 있다고 가정해 보자. 이 파드에는 main-apphelper-app 이라는 이름의 두 컨테이너가 있다. 다음 명령어는 main-app 컨테이너에 대한 셸에 접근할 것이다.

kubectl exec -i -t my-pod --container main-app -- /bin/bash

다음 내용

  • [kubectl
  • exec](/docs/reference/generated/kubectl/kubectl-commands/#exec)를참고한다.

4.10.3 - 리소스 메트릭 파이프라인

컨테이너 CPU 및 메모리 사용량과 같은 리소스 사용량 메트릭은 쿠버네티스의 메트릭 API를 통해 사용할 수 있다. 이 메트릭은 kubectl top 커맨드 사용하여 사용자가 직접적으로 액세스하거나, Horizontal Pod Autoscaler 같은 클러스터의 컨트롤러에서 결정을 내릴 때 사용될 수 있다.

메트릭 API

메트릭 API를 통해, 주어진 노드나 파드에서 현재 사용중인 리소스의 양을 알 수 있다. 이 API는 메트릭 값을 저장하지 않으므로, 예를 들어, 지정된 노드에서 10분 전에 사용된 리소스의 양을 가져오는 것과 같은 일을 할 수는 없다.

이 API와 다른 API는 차이가 없다.

  • 다른 쿠버네티스 API의 엔드포인트와 같이 /apis/metrics.k8s.io/ 하위 경로에서 발견될 수 있다
  • 동일한 보안, 확장성 및 신뢰성 보장을 제공한다

k8s.io/metrics 리포지터리에서 이 API를 정의하고 있다. 여기에서 이 API에 대한 더 상세한 정보를 찾을 수 있다.

리소스 사용량 측정

CPU

CPU는 일정 기간 동안 CPU 코어에서 평균 사용량으로 리포트된다. 이 값은 커널(리눅스와 윈도우 커널 모두)에서 제공하는 누적 CPU 카운터보다 높은 비율을 적용해서 얻는다. kubelet은 비율 계산에 사용할 윈도우를 선택한다.

메모리

메모리는 메트릭이 수집된 순간 작업 집합으로 리포트 된다. 이상적인 환경에서 "작업 집합(working set)"은 압박(memory pressure)에서 풀려날 수 없는 사용 중인(in-use) 메모리의 양이다. 그러나 작업 집합의 계산은 호스트 OS에 따라 다르며, 일반적으로 휴리스틱스를 사용해서 평가한다. 쿠버네티스는 스왑(swap)을 지원하지 않기 때문에 모든 익명(파일로 백업되지 않은) 메모리를 포함한다. 호스트 OS가 항상 이러한 페이지를 회수할 수 없기 때문에 메트릭에는 일반적으로 일부 캐시된(파일 백업) 메모리도 포함된다.

메트릭 서버

메트릭 서버는 클러스터 전역에서 리소스 사용량 데이터를 집계한다. kube-up.sh 스크립트에 의해 생성된 클러스터에는 기본적으로 메트릭 서버가 디플로이먼트 오브젝트로 배포된다. 만약 다른 쿠버네티스 설치 메커니즘을 사용한다면, 제공된 디플로이먼트 components.yaml 파일을 사용하여 메트릭 서버를 배포할 수 있다.

메트릭 서버는 각 노드에서 Kubelet에 의해 노출된 Summary API에서 메트릭을 수집하고, 쿠버네티스 aggregator를 통해 메인 API 서버에 등록된다.

설계 문서에서 메트릭 서버에 대해 자세하게 배울 수 있다.

4.10.4 - 리소스 모니터링 도구

애플리케이션을 스케일하여 신뢰할 수 있는 서비스를 제공하려면, 애플리케이션이 배포되었을 때 애플리케이션이 어떻게 동작하는지를 이해해야 한다. 컨테이너, 파드, 서비스, 그리고 전체 클러스터의 특성을 검사하여 쿠버네티스 클러스터 내의 애플리케이션 성능을 검사할 수 있다. 쿠버네티스는 각 레벨에서 애플리케이션의 리소스 사용량에 대한 상세 정보를 제공한다. 이 정보는 애플리케이션의 성능을 평가하고 병목 현상을 제거하여 전체 성능을 향상할 수 있게 해준다.

쿠버네티스에서 애플리케이션 모니터링은 단일 모니터링 솔루션에 의존하지 않는다. 신규 클러스터에서는, 리소스 메트릭 또는 완전한 메트릭 파이프라인으로 모니터링 통계를 수집할 수 있다.

리소스 메트릭 파이프라인

리소스 메트릭 파이프라인은 Horizontal Pod Autoscaler 컨트롤러와 같은 클러스터 구성요소나 kubectl top 유틸리티에 관련되어 있는 메트릭들로 제한된 집합을 제공한다. 이 메트릭은 경량의 단기 인메모리 저장소인 metrics-server에 의해서 수집되며 metrics.k8s.io API를 통해 노출된다.

metrics-server는 클러스터 상의 모든 노드를 발견하고 각 노드의 Kubelet에 CPU와 메모리 사용량을 질의한다. Kubelet은 쿠버네티스 마스터와 노드 간의 다리 역할을 해서 머신에서 구동되는 파드와 컨테이너를 관리한다. Kubelet은 각각의 파드를 해당하는 컨테이너로 변환하고 컨테이너 런타임 인터페이스를 통해서 컨테이너 런타임에서 개별 컨테이너의 사용량 통계를 가져온다. Kubelet은 이 정보를 레거시 도커와의 통합을 위해 kubelet에 통합된 cAdvisor를 통해 가져온다. 그 다음으로 취합된 파드 리소스 사용량 통계를 metric-server 리소스 메트릭 API를 통해 노출한다. 이 API는 kubelet의 인증이 필요한 읽기 전용 포트 상의 /metrics/resource/v1beta1에서 제공된다.

완전한 메트릭 파이프라인

완전한 메트릭 파이프라인은 보다 풍부한 메트릭에 접근할 수 있도록 해준다. 쿠버네티스는 Horizontal Pod Autoscaler와 같은 메커니즘을 활용해서 이런 메트릭에 대한 반응으로 클러스터의 현재 상태를 기반으로 자동으로 스케일링하거나 클러스터를 조정할 수 있다. 모니터링 파이프라인은 kubelet에서 메트릭을 가져와서 쿠버네티스에 custom.metrics.k8s.ioexternal.metrics.k8s.io API를 구현한 어댑터를 통해 노출한다.

CNCF 프로젝트인, 프로메테우스는 기본적으로 쿠버네티스, 노드, 프로메테우스 자체를 모니터링할 수 있다. CNCF 프로젝트가 아닌 완전한 메트릭 파이프라인 프로젝트는 쿠버네티스 문서의 범위가 아니다.

4.10.5 - 스테이트풀셋 디버깅하기

이 문서에서는 스테이트풀셋을 디버깅 방법에 대해 설명한다.

시작하기 전에

  • 쿠버네티스 클러스터가 준비되어 있어야 하고, kubectl 커맨드 라인 도구가 클러스터와 통신할 수 있게 사전에 설정되어 있어야 한다.
  • 조사하고자 하는 스테이트풀셋이 사전에 준비되어 있어야 한다.

스테이트풀셋 디버깅하기

레이블이 app=myapp으로 지정된 스테이트풀셋 파드를 전부 나열하기 위해서는 다음의 명령을 사용할 수 있다.

kubectl get pods -l app=myapp

만약 오랜 시간동안 Unknown이나 Terminating 상태에 있는 파드들을 발견하였다면, 이러한 파드들을 어떻게 다루는지 알아보기 위해 스테이트풀셋 파드 삭제하기를 참고하길 바란다. 스테이트풀셋에 포함된 개별 파드들을 디버깅하기 위해서는 파드 디버그하기 가이드를 참고하길 바란다.

다음 내용

초기화 컨테이너(Init container) 디버그하기를 참고길 바란다.

4.10.6 - 초기화 컨테이너(Init Containers) 디버그하기

이 페이지는 초기화 컨테이너의 실행과 관련된 문제를 조사하는 방법에 대해 보여준다. 아래 예제의 커맨드 라인은 파드(Pod)를 <pod-name> 으로, 초기화 컨테이너를 <init-container-1><init-container-2> 로 표시한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

초기화 컨테이너의 상태 체크하기

사용자 파드의 상태를 표시한다.

kubectl get pod <pod-name>

예를 들어, Init:1/2 상태는 두 개의 초기화 컨테이너 중 하나가 성공적으로 완료되었음을 나타낸다.

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

상태값과 그 의미에 대한 추가 예제는 파드 상태 이해하기를 참조한다.

초기화 컨테이너에 대한 상세 정보 조회하기

초기화 컨테이너의 실행에 대한 상세 정보를 확인한다.

kubectl describe pod <pod-name>

예를 들어, 2개의 초기화 컨테이너가 있는 파드는 다음과 같이 표시될 수 있다.

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

파드 스펙의 status.initContainerStatuses 필드를 읽어서 프로그래밍 방식으로 초기화 컨테이너의 상태를 조회할 수도 있다.

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

이 명령은 원시 JSON 방식으로 위와 동일한 정보를 반환한다.

초기화 컨테이너의 로그 조회하기

초기화 컨테이너의 로그를 확인하기 위해 파드의 이름과 초기화 컨테이너의 이름을 같이 전달한다.

kubectl logs <pod-name> -c <init-container-2>

셸 스크립트를 실행하는 초기화 컨테이너는, 초기화 컨테이너가 실행될 때 명령어를 출력한다. 예를 들어, 스크립트의 시작 부분에 set -x 를 추가하고 실행하여 Bash에서 명령어를 출력할 수 있도록 수행할 수 있다.

파드의 상태 이해하기

Init: 으로 시작하는 파드 상태는 초기화 컨테이너의 실행 상태를 요약한다. 아래 표는 초기화 컨테이너를 디버깅하는 동안 사용자가 확인할 수 있는 몇 가지 상태값의 예이다.

상태 의미
Init:N/M 파드가 M 개의 초기화 컨테이너를 갖고 있으며, 현재까지 N 개가 완료.
Init:Error 초기화 컨테이너 실행 실패.
Init:CrashLoopBackOff 초기화 컨테이너가 반복적으로 실행 실패.
Pending 파드가 아직 초기화 컨테이너를 실행하지 않음.
PodInitializing or Running 파드가 이미 초기화 컨테이너 실행을 완료.

4.10.7 - 파드 실패의 원인 검증하기

이 페이지는 컨테이너 종료 메시지를 읽고 쓰는 방법을 보여준다.

종료 메시지는 컨테이너가 치명적인 이벤트에 대한 정보를, 대시보드나 모니터링 소프트웨어 도구와 같이 쉽게 조회 및 표시할 수 있는 위치에 기록하는 방법을 제공한다. 대부분의 경우에 종료 메시지에 넣는 정보는 일반 쿠버네티스 로그에도 쓰여져야 한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

종료 메시지 읽기 및 쓰기

이 예제에서는, 하나의 컨테이너를 실행하는 파드를 생성한다. 하단의 설정 파일은 컨테이너가 시작될 때 수행하는 명령어를 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. 다음의 YAML 설정 파일에 기반한 파드를 생성한다.

     kubectl apply -f https://k8s.io/examples/debug/termination.yaml
    
    YAML 파일에 있는 `command` 와 `args` 필드에서 컨테이너가 10초 간 잠든 뒤에
    "Sleep expired" 문자열을 `/dev/termination-log` 파일에 기록하는
    것을 확인할 수 있다. 컨테이너는 "Sleep expired" 메시지를
    기록한 후에 종료된다.
    
  2. 파드와 관련된 정보를 출력한다.

     kubectl get pod termination-demo
    
    파드가 더 이상 실행되지 않을 때까지 앞선 명령어를 반복한다.
    
  3. 파드에 관한 상세 정보를 출력한다.

     kubectl get pod termination-demo --output=yaml
    
    결과는 "Sleep expired" 메시지를 포함한다.
    
     apiVersion: v1
     kind: Pod
     ...
         lastState:
           terminated:
             containerID: ...
             exitCode: 0
             finishedAt: ...
             message: |
               Sleep expired
             ...
    
  4. 종료 메시지만을 포함하는 출력 결과를 보기 위해서는 Go 템플릿을 사용한다.

     kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

종료 메시지 사용자 정의하기

쿠버네티스는 컨테이너의 terminationMessagePath 필드에 지정된 종료 메시지 파일에서 종료 메시지를 검색하며, 이 필드의 기본값은 /dev/termination-log 이다. 이 필드를 사용자 정의 함으로써 쿠버네티스가 종료 메시지를 검색할 때 다른 파일을 사용하도록 조정할 수 있다. 쿠버네티스는 지정된 파일의 내용을 사용하여 컨테이너의 성공 및 실패에 대한 상태 메시지를 채운다.

종료 메시지는 assertion failure 메세지처럼 간결한 최종 상태로 생성된다. kubelet은 4096 바이트보다 긴 메시지를 자른다. 모든 컨테이너의 총 메시지 길이는 12KiB로 제한된다. 기본 종료 메시지 경로는 /dev/termination-log이다. 파드가 시작된 후에는 종료 메시지 경로를 설정할 수 없다.

다음의 예제에서 컨테이너는, 쿠버네티스가 조회할 수 있도록 /tmp/my-log 파일에 종료 메시지를 기록한다.

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

또한 사용자는 추가적인 사용자 정의를 위해 컨테이너의 terminationMessagePolicy 필드를 설정할 수 있다. 이 필드의 기본 값은 File 이며, 이는 오직 종료 메시지 파일에서만 종료 메시지가 조회되는 것을 의미한다. terminationMessagePolicy 필드의 값을 "FallbackToLogsOnError 으로 설정함으로써, 종료 메시지 파일이 비어 있고 컨테이너가 오류와 함께 종료 되었을 경우 쿠버네티스가 컨테이너 로그 출력의 마지막 청크를 사용하도록 지시할 수 있다. 로그 출력은 2048 바이트나 80 행 중 더 작은 값으로 제한된다.

다음 내용

4.10.8 - 파드와 레플리케이션컨트롤러(ReplicationController) 디버그하기

이 페이지에서는 파드와 레플리케이션컨트롤러를 디버깅하는 방법을 소개한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

파드 디버깅

파드 디버깅의 첫 번째 단계는 파드를 살펴 보는 것이다. 다음의 명령어를 사용하여 파드의 현재 상태와 최근 이벤트를 점검한다.

kubectl describe pods ${POD_NAME}

파드 내부 컨테이너의 상태를 확인한다. 모두 Running 상태인가? 최근에 재시작 되었는가?

파드의 상태에 따라 디버깅을 계속한다.

파드가 pending 상태로 유지

파드가 Pending 상태로 멈춰 있는 경우는, 노드에 스케줄 될 수 없음을 의미한다. 일반적으로 이것은 어떤 유형의 리소스가 부족하거나 스케줄링을 방해하는 다른 요인 때문이다. 상단의 kubectl describe ... 명령의 결과를 확인하자. 파드를 스케줄 할 수 없는 이유에 대한 스케줄러의 메세지가 있어야 한다. 이유는 다음과 같다.

부족한 리소스

사용자 클러스터의 CPU 나 Memory의 공급이 소진되었을 수 있다. 이 경우 몇 가지 방법을 시도할 수 있다.

  • 클러스터에 노드를 더 추가하기.

  • pending 상태인 파드를 위한 공간을 확보하기 위해 불필요한 파드 종료하기

  • 파드가 노드보다 크지 않은지 확인한다. 예를 들어 모든 노드가 cpu:1 의 용량을 가지고 있을 경우, cpu: 1.1 을 요청하는 파드는 절대 스케줄 될 수 없다.

    사용자는 kubectl get nodes -o <format> 명령으로 노드의 용량을 점검할 수 있다. 다음은 필요한 정보를 추출하는 몇 가지 명령의 예이다.

    kubectl get nodes -o yaml | egrep '\sname:|cpu:|memory:'
    kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cap: .status.capacity}'
    

    리소스 쿼터 기능은 사용할 수 있는 전체 리소스의 양을 제한하도록 설정할 수 있다. 네임스페이스와 함께 사용하면, 한 팀이 모든 리소스를 점유하는 것을 방지할 수 있다.

hostPort 사용하기

파드를 hostPort 에 바인딩 할 때 파드를 스케줄링 할 수 있는 위치는 제한되어 있다. 대부분의 경우 hostPort 는 불필요하다. 서비스 오브젝트를 사용하여 파드를 노출하도록 한다. hostPort 가 필요한 경우 컨테이너 클러스터에 있는 노드의 수만큼 파드를 스케줄 할 수 있다.

파드가 waiting 상태로 유지

파드가 Waiting 상태에서 멈춘 경우, 워커 노드에 스케줄 되었지만, 해당 장비에서 사용할 수 없다. 거듭 강조하지만, kubectl describe ... 의 정보는 유익하게 사용되어야 한다. Waiting 파드의 가장 일반적인 원인은 이미지를 가져오지 못하는 경우이다. 확인해야 할 3가지 사항이 있다.

  • 이미지 이름이 올바른지 확인한다.
  • 이미지를 저장소에 푸시하였는가?
  • 이미지가 풀 될 수 있는지 보기 위해, 사용자의 장비에서 docker pull <image> 를 수동으로 실행한다.

파드가 손상(crashing)되었거나 양호하지 않을(unhealthy) 경우

일단 사용자의 파드가 스케줄 되면, 구동중인 파드 디버그하기에 기술된 메서드를 디버깅에 사용할 수 있다.

레플리케이션컨트롤러 디버깅

레플리케이션컨트롤러는 매우 간단하다. 이 오브젝트는 파드를 만들거나 만들 수 없는 경우뿐이다. 만약 파드를 만들 수 없는 경우, 위의 지침을 참조하여 파드를 디버그한다.

사용자는 kubectl describe rc ${CONTROLLER_NAME} 을 사용하여 레플리케이션 컨트롤러와 관련된 이벤트를 검사할 수도 있다.

4.11 - 쿠버네티스 확장

쿠버네티스 클러스터를 작업 환경의 요구에 맞게 조정하는 고급 과정을 이해한다.

4.11.1 - 확장 API 서버 설정

애그리게이션 레이어(aggregation layer)와 작동하도록 확장 API 서버를 설정하면 쿠버네티스 API 서버를 쿠버네티스의 핵심 API의 일부가 아닌 추가 API로 확장할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

애그리게이션 레이어와 작동하도록 확장 API 서버 설정

다음 단계는 확장 API 서버를 높은 수준 으로 설정하는 방법을 설명한다. 이 단계는 YAML 구성을 사용하거나 API를 사용하는 것에 상관없이 적용된다. 둘 사이의 차이점을 구체적으로 식별하려고 시도한다. YAML 구성을 사용하여 구현하는 방법에 대한 구체적인 예를 보려면, 쿠버네티스 리포지터리에서 sample-apiserver를 참고할 수 있다.

또는, apiserver-builder와 같은 기존의 타사 솔루션을 사용하여 스켈레톤(skeleton)을 생성하고 다음 단계를 모두 자동화해야 한다.

  1. API서비스(APIService) API가 활성화되어 있는지 확인한다(--runtime-config 확인). 클러스터에서 일부러 해제하지 않았다면, 기본적으로 활성화되어 있어야 한다.
  2. API서비스 오브젝트를 추가하거나 클러스터 관리자가 작성하도록 RBAC 규칙을 작성해야 할 수도 있다. (API 확장은 전체 클러스터에 영향을 주기 때문에, 운영 중인 클러스터에서 API 확장에 대한 테스트/개발/디버깅을 수행하지 않는 것이 좋다.)
  3. 확장 API 서비스를 실행하려는 쿠버네티스 네임스페이스를 생성한다.
  4. HTTPS를 위해 확장 API 서버가 사용하는 서버 인증서에 서명하는 데 사용할 CA 인증서를 생성하거나 가져온다.
  5. HTTPS를 위해 API 서버가 사용할 서버 인증서/키를 생성한다. 이 인증서는 위의 CA 인증서에 의해 서명해야 한다. 또한 Kube DNS 이름의 CN이 있어야 한다. 이것은 쿠버네티스 서비스에서 파생되었으며 <service name>.<service name namespace>.svc 형식이다.
  6. 네임스페이스에 서버 인증서/키를 사용하여 쿠버네티스 시크릿을 생성한다.
  7. 확장 API 서버에 대한 쿠버네티스 디플로이먼트를 생성하고 시크릿을 볼륨으로 로드하는지 확인한다. 확장 API 서버의 작동하는(working) 이미지에 대한 참조를 포함해야 한다. 디플로이먼트는 네임스페이스에도 있어야 한다.
  8. 확장 API 서버가 해당 볼륨에서 해당 인증서를 로드하고 HTTPS 핸드셰이크에 사용되는지 확인한다.
  9. 네임스페이스에서 쿠버네티스 서비스 어카운트를 생성한다.
  10. 리소스에 허용하려는 작업에 대한 쿠버네티스 클러스터 롤(role)을 생성한다.
  11. 네임스페이스의 서비스 어카운트에서 방금 만든 클러스터 롤로 쿠버네티스 클러스터 롤 바인딩을 생성한다.
  12. 네임스페이스의 서비스 어카운트에서 system:auth-delegator 클러스터 롤로 쿠버네티스 클러스터 롤 바인딩을 만들어 인증 결정을 쿠버네티스 핵심 API 서버에 위임한다.
  13. 네임스페이스의 서비스 어카운트에서 extension-apiserver-authentication-reader 롤로 쿠버네티스 롤 바인딩을 생성한다. 이를 통해 확장 API 서버가 extension-apiserver-authentication 컨피그맵(configmap)에 접근할 수 있다.
  14. 쿠버네티스 API 서비스를 생성한다. 위의 CA 인증서는 base64로 인코딩되어, 새로운 라인이 제거되고 API 서비스에서 spec.caBundle로 사용되어야 한다. 이것은 namespaced가 아니어야 한다. kube-aggregator API를 사용하는 경우, base64 인코딩이 수행되므로 PEM 인코딩된 CA 번들만 통과한다.
  15. kubectl을 사용하여 리소스를 얻는다. kubectl을 실행하면, "No resources found."가 반환된다. 이 메시지는 모든 것이 작동됐지만 현재 해당 리소스 유형의 오브젝트가 생성되지 않았음을 나타낸다.

다음 내용

4.11.2 - Konnectivity 서비스 설정

Konnectivity 서비스는 컨트롤 플레인에 클러스터 통신을 위한 TCP 수준 프록시를 제공한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

Konnectivity 서비스 설정

다음 단계에는 송신(egress) 설정이 필요하다. 예를 들면 다음과 같다.

apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
# 클러스터에 대한 송신(egress) 트래픽을 제어하기 위해 
# "cluster"를 name으로 사용한다. 기타 지원되는 값은 "etcd" 및 "master"이다.
- name: cluster
  connection:
    # API 서버와 Konnectivity 서버 간의 프로토콜을
    # 제어한다. 지원되는 값은 "GRPC" 및 "HTTPConnect"이다. 두 모드 간에
    # 최종 사용자가 볼 수 있는 차이점은 없다. 동일한 모드에서 작동하도록
    # Konnectivity 서버를 설정해야 한다.
    proxyProtocol: GRPC
    transport:
      # API 서버가 Konnectivity 서버와 통신하는 데 사용하는 
      # transport를 제어한다. Konnectivity 서버가 API 서버와 동일한 시스템에 
      # 있는 경우 UDS를 사용하는 것이 좋다. 동일한 UDS 소켓에서 
      # 수신 대기하도록 Konnectivity 서버를 구성해야 한다. 
      # 지원되는 다른 전송은 "tcp"이다. TCP 전송을 보호하려면 TLS 구성을 설정해야 한다.
      uds:
        udsName: /etc/kubernetes/konnectivity-server/konnectivity-server.socket

Konnectivity 서비스를 사용하고 네트워크 트래픽을 클러스터 노드로 보내도록 API 서버를 구성해야 한다.

  1. ServiceAccountTokenVolumeProjection 기능 게이트(feature gate)가 활성화되어 있는지 확인한다. kube-apiserver에 다음과 같은 플래그를 제공하여 서비스 어카운트 토큰 볼륨 보호를 활성화할 수 있다.
    --service-account-issuer=api
    --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
    --api-audiences=system:konnectivity-server
    
  2. admin/konnectivity/egress-selector-configuration.yaml과 같은 송신 구성 파일을 생성한다.
  3. API 서버의 --egress-selector-config-file 플래그를 API 서버 송신 구성 파일의 경로로 설정한다.
  4. UDS 연결을 사용하는 경우 kube-apiserver에 볼륨 구성을 추가한다.
    spec:
      containers:
        volumeMounts:
        - name: konnectivity-uds
          mountPath: /etc/kubernetes/konnectivity-server
          readOnly: false
      volumes:
      - name: konnectivity-uds
        hostPath:
          path: /etc/kubernetes/konnectivity-server
          type: DirectoryOrCreate
    

konnectivity-server에 대한 인증서 및 kubeconfig를 생성하거나 얻는다. 예를 들어 OpenSSL 커맨드라인 툴을 사용하여 컨트롤 플레인 호스트에서 클러스터 CA 인증서 /etc/kubernetes/pki/ca.crt를 사용하여 X.509 인증서를 발급할 수 있다.

openssl req -subj "/CN=system:konnectivity-server" -new -newkey rsa:2048 -nodes -out konnectivity.csr -keyout konnectivity.key -out konnectivity.csr
openssl x509 -req -in konnectivity.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out konnectivity.crt -days 375 -sha256
SERVER=$(kubectl config view -o jsonpath='{.clusters..server}')
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-credentials system:konnectivity-server --client-certificate konnectivity.crt --client-key konnectivity.key --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-cluster kubernetes --server "$SERVER" --certificate-authority /etc/kubernetes/pki/ca.crt --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-context system:konnectivity-server@kubernetes --cluster kubernetes --user system:konnectivity-server
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config use-context system:konnectivity-server@kubernetes
rm -f konnectivity.crt konnectivity.key konnectivity.csr

다음으로 Konnectivity 서버와 에이전트를 배포해야 한다. kubernetes-sigs/apiserver-network-proxy에서 구현을 참조할 수 있다.

컨트롤 플레인 노드에 Konnectivity 서버를 배포한다. 제공된 konnectivity-server.yaml 매니페스트는 쿠버네티스 구성 요소가 클러스터에 스태틱 파드(static Pod)로 배포되었다고 가정한다. 그렇지 않은 경우에는 Konnectivity 서버를 데몬셋(DaemonSet)으로 배포할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: konnectivity-server
  namespace: kube-system
spec:
  priorityClassName: system-cluster-critical
  hostNetwork: true
  containers:
  - name: konnectivity-server-container
    image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-server:v0.0.16
    command: ["/proxy-server"]
    args: [
            "--logtostderr=true",
            # 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
            "--uds-name=/etc/kubernetes/konnectivity-server/konnectivity-server.socket",
            # 다음 두 줄은 Konnectivity 서버가 apiserver와 
            # 동일한 시스템에 배포되고 API 서버의 인증서와 
            # 키가 지정된 위치에 있다고 가정한다.
            "--cluster-cert=/etc/kubernetes/pki/apiserver.crt",
            "--cluster-key=/etc/kubernetes/pki/apiserver.key",
            # 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
            "--mode=grpc",
            "--server-port=0",
            "--agent-port=8132",
            "--admin-port=8133",
            "--health-port=8134",
            "--agent-namespace=kube-system",
            "--agent-service-account=konnectivity-agent",
            "--kubeconfig=/etc/kubernetes/konnectivity-server.conf",
            "--authentication-audience=system:konnectivity-server"
            ]
    livenessProbe:
      httpGet:
        scheme: HTTP
        host: 127.0.0.1
        port: 8134
        path: /healthz
      initialDelaySeconds: 30
      timeoutSeconds: 60
    ports:
    - name: agentport
      containerPort: 8132
      hostPort: 8132
    - name: adminport
      containerPort: 8133
      hostPort: 8133
    - name: healthport
      containerPort: 8134
      hostPort: 8134
    volumeMounts:
    - name: k8s-certs
      mountPath: /etc/kubernetes/pki
      readOnly: true
    - name: kubeconfig
      mountPath: /etc/kubernetes/konnectivity-server.conf
      readOnly: true
    - name: konnectivity-uds
      mountPath: /etc/kubernetes/konnectivity-server
      readOnly: false
  volumes:
  - name: k8s-certs
    hostPath:
      path: /etc/kubernetes/pki
  - name: kubeconfig
    hostPath:
      path: /etc/kubernetes/konnectivity-server.conf
      type: FileOrCreate
  - name: konnectivity-uds
    hostPath:
      path: /etc/kubernetes/konnectivity-server
      type: DirectoryOrCreate

그런 다음 클러스터에 Konnectivity 에이전트를 배포한다.

apiVersion: apps/v1
# 에이전트를 Deployment(디플로이먼트)로 배포할 수도 있다. 각 노드에 에이전트가
# 있을 필요는 없다.
kind: DaemonSet
metadata:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: konnectivity-agent
  namespace: kube-system
  name: konnectivity-agent
spec:
  selector:
    matchLabels:
      k8s-app: konnectivity-agent
  template:
    metadata:
      labels:
        k8s-app: konnectivity-agent
    spec:
      priorityClassName: system-cluster-critical
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      containers:
        - image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.16
          name: konnectivity-agent
          command: ["/proxy-agent"]
          args: [
                  "--logtostderr=true",
                  "--ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
                  # konnectivity 서버는 hostNetwork=true로 실행되기 때문에,
                  # 이것은 마스터 머신의 IP 주소이다.
                  "--proxy-server-host=35.225.206.7",
                  "--proxy-server-port=8132",
                  "--admin-server-port=8133",
                  "--health-server-port=8134",
                  "--service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token"
                  ]
          volumeMounts:
            - mountPath: /var/run/secrets/tokens
              name: konnectivity-agent-token
          livenessProbe:
            httpGet:
              port: 8134
              path: /healthz
            initialDelaySeconds: 15
            timeoutSeconds: 15
      serviceAccountName: konnectivity-agent
      volumes:
        - name: konnectivity-agent-token
          projected:
            sources:
              - serviceAccountToken:
                  path: konnectivity-agent-token
                  audience: system:konnectivity-server

마지막으로 클러스터에서 RBAC가 활성화된 경우 관련 RBAC 규칙을 생성한다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:konnectivity-server
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: system:konnectivity-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: konnectivity-agent
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile

4.12 - TLS

TLS(Transport Layer Security)를 사용하여 클러스터 내 트래픽을 보호하는 방법을 이해한다.

4.12.1 - Kubelet의 인증서 갱신 구성

이 페이지는 kubelet에 대한 인증서 갱신을 활성화하고 구성하는 방법을 보여준다.

FEATURE STATE: Kubernetes v1.19 [stable]

시작하기 전에

  • 쿠버네티스 1.8.0 버전 혹은 그 이상의 버전이 요구됨

개요

kubelet은 쿠버네티스 API 인증을 위해 인증서를 사용한다. 기본적으로 이러한 인증서는 1년 만기로 발급되므로 너무 자주 갱신할 필요는 없다.

쿠버네티스는 kubelet 인증서 갱신을 포함하며, 이 기능은 현재 인증서의 만료 시한이 임박한 경우, 새로운 키를 자동으로 생성하고 쿠버네티스 API에서 새로운 인증서를 요청하는 기능이다. 새로운 인증서를 사용할 수 있게 되면 쿠버네티스 API에 대한 연결을 인증하는데 사용된다.

클라이언트 인증서 갱신 활성화하기

kubelet 프로세스는 현재 사용 중인 인증서의 만료 시한이 다가옴에 따라 kubelet이 자동으로 새 인증서를 요청할지 여부를 제어하는 --rotate-certificates 인자를 허용한다.

kube-controller-manager 프로세스는 얼마나 오랜 기간 인증서가 유효한지를 제어하는 --cluster-signing-duration (1.19 이전은 --experimental-cluster-signing-duration) 인자를 허용한다.

인증서 갱신 구성에 대한 이해

kubelet이 시작할 때 부트 스트랩 (--bootstrap-kubeconfig 플래그를 사용) 을 구성하면 초기 인증서를 사용하여 쿠버네티스 API에 연결하고 인증서 서명 요청을 발행한다. 다음을 사용하여 인증서 서명 요청 상태를 볼 수 있다.

kubectl get csr

초기에 노드의 kubelet에서 인증서 서명 요청은 Pending 상태이다. 인증서 서명 요청이 특정 기준을 충족하면 컨트롤러 관리자가 자동으로 승인한 후 상태가 Approved 가 된다. 다음으로, 컨트롤러 관리자는 --cluster-signing-duration 파라미터에 의해 지정된 기간 동안 발행된 인증서에 서명하고 서명된 인증서는 인증서 서명 요청에 첨부된다.

kubelet은 쿠버네티스 API로 서명된 인증서를 가져와서 --cert-dir에 지정된 위치에 디스크에 기록한다. 그런 다음 kubelet은 쿠버네티스 API에 연결해서 새로운 인증서를 사용한다.

서명된 인증서의 만료가 다가오면 kubelet은 쿠버네티스 API를 사용하여 새로운 인증서 서명 요청을 자동으로 발행한다. 이는 인증서 유효 기간이 30%-10% 남은 시점에 언제든지 실행될 수 있다. 또한, 컨트롤러 관리자는 인증서 요청을 자동으로 승인하고 서명된 인증서를 인증서 서명 요청에 첨부한다. kubelet은 쿠버네티스 API로 서명된 새로운 인증서를 가져와서 디스크에 쓴다. 그런 다음 새로운 인증서를 사용한 재연결을 위해서 가지고 있는 쿠버네티스 API로의 연결을 업데이트 한다.

4.12.2 - 클러스터에서 TLS 인증서 관리

쿠버네티스는 사용자가 제어하는 ​​인증 기관 (CA)에서 서명한 TLS 인증서를 프로비저닝 할 수 있는 certificates.k8s.io API를 제공한다. 이러한 CA 및 인증서는 워크로드 간의 신뢰 관계를 구성하는 용도로 사용할 수 있다.

certificates.k8s.io API는 ACME 초안과 유사한 프로토콜을 사용한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

클러스터에서 TLS 신뢰

파드로 실행되는 애플리케이션에서 사용자 정의 CA를 신뢰하려면 일반적으로 몇 가지 추가 애플리케이션 구성이 필요하다. TLS 클라이언트 또는 서버가 신뢰하는 CA 인증서 목록에 CA 인증서 번들을 추가해야 한다. 예를 들어 인증서 체인을 파싱하고, 파싱된 인증서를 tls.Config 구조체의 RootCAs 필드에 추가하여, golang TLS 구성으로 이를 수행할 수 있다.

CA 인증서를 파드에서 사용할 수 있는 ConfigMap으로 배포할 수 있다.

인증서 요청

다음 섹션에서는 DNS를 통해 액세스되는 쿠버네티스 서비스의 TLS 인증서를 생성하는 방법을 보여준다.

CFSSL 다운로드 및 설치

이 예제에 사용된 cfssl 도구는 https://github.com/cloudflare/cfssl/releases에서 다운로드 할 수 있다.

인증서 서명 요청 (CSR) 생성

다음 명령을 실행하여 개인 키 및 인증서 서명 요청(또는 CSR)을 생성한다.

cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "my-svc.my-namespace.svc.cluster.local",
    "my-pod.my-namespace.pod.cluster.local",
    "192.0.2.24",
    "10.0.34.2"
  ],
  "CN": "system:node:my-pod.my-namespace.pod.cluster.local",
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "O": "system:nodes"
    }
  ]
}
EOF

여기서 192.0.2.24는 서비스의 클러스터 IP, my-svc.my-namespace.svc.cluster.local은 서비스의 DNS 이름, 10.0.34.2는 파드의 IP,my-pod.my-namespace.pod.cluster.local은 파드의 DNS 이름이다. 다음 출력이 표시되어야 한다.

2017/03/21 06:48:17 [INFO] generate received request
2017/03/21 06:48:17 [INFO] received CSR
2017/03/21 06:48:17 [INFO] generating key: ecdsa-256
2017/03/21 06:48:17 [INFO] encoded CSR

이 명령은 두 개의 파일을 생성한다. PEM으로 인코딩된 pkcs#10 인증 요청이 포함된 server.csr과 생성할 인증서 키를 PEM 인코딩한 값이 포함된 server-key.pem을 생성한다.

쿠버네티스 API로 보낼 인증서 서명 요청 객체 만들기

CSR yaml blob을 생성하고 다음 명령을 실행하여 apiserver로 보낸다.

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: my-svc.my-namespace
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  signerName: kubernetes.io/kubelet-serving
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

1단계에서 만든 server.csr 파일은 base64로 인코딩되고 .spec.request 필드에 숨겨져 있다. 또한 kubernetes.io/kubelet-serving 서명자가 서명한 "digitalSignature", "keyEnciperment" 및 "serverAuth" 키 사용(keyUsage)이 있는 인증서를 요청한다. 특정 signerName을 요청해야 한다. 자세한 내용은 지원되는 서명자 이름 문서를 참조한다.

이제 CSR이 API에서 보류 상태로 표시되어야 한다. 다음을 실행하여 확인할 수 있다.

kubectl describe csr my-svc.my-namespace
Name:                   my-svc.my-namespace
Labels:                 <none>
Annotations:            <none>
CreationTimestamp:      Tue, 21 Mar 2017 07:03:51 -0700
Requesting User:        [email protected]
Status:                 Pending
Subject:
        Common Name:    my-svc.my-namespace.svc.cluster.local
        Serial Number:
Subject Alternative Names:
        DNS Names:      my-svc.my-namespace.svc.cluster.local
        IP Addresses:   192.0.2.24
                        10.0.34.2
Events: <none>

인증서 서명 요청 승인 받기

인증서 서명 요청을 승인하는 것은 자동화된 승인 프로세스나 클러스터 관리자에 의해 일회성으로 수행한다. 여기에 관련된 내용에 대한 자세한 내용은 아래에서 설명한다.

인증서 다운로드 및 사용

CSR이 서명되고 승인되면 다음이 표시된다.

kubectl get csr
NAME                  AGE       REQUESTOR               CONDITION
my-svc.my-namespace   10m       [email protected]    Approved,Issued

다음을 실행하여 발급된 인증서를 다운로드하고 server.crt 파일에 저장할 수 있다.

kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
    | base64 --decode > server.crt

이제 server.crtserver-key.pem을 키페어(keypair)로 사용하여 HTTPS 서버를 시작할 수 있다.

인증서 서명 요청 승인

(적절한 권한이 있는) 쿠버네티스 관리자는 kubectl certificate approvekubectl certificate deny 명령을 사용하여 인증서 서명 요청을 수동으로 승인 (또는 거부) 할 수 있다. 그러나 이 API를 많이 사용한다면, 자동화된 인증서 컨트롤러 작성을 고려할 수 있다.

위와 같이 kubectl을 사용하는 시스템이든 사람이든, 승인자의 역할은 CSR이 다음 두 가지 요구 사항을 충족하는지 확인하는 것이다.

  1. CSR은 CSR에 서명하는 데 사용되는 개인 키를 제어하는 것이다. 이는 승인된 대상으로 가장하는 제 3자의 위협을 해결한다. 위의 예에서 이 단계는 파드(pod)가 CSR을 생성하는 데 사용되는 개인 키를 제어하는지 확인하는 것이다.
  2. CSR은 요청된 상황에서 작동할 권한이 있다. 이것은 원하지 않는 대상이 클러스터에 합류(join)하는 위협을 해결한다. 위의 예에서, 이 단계는 파드가 요청된 서비스에 참여할 수 있는지 확인하는 것이다.

이 두 가지 요구 사항이 충족되는 경우에만, 승인자가 CSR을 승인하고 그렇지 않으면 CSR을 거부해야 한다.

승인 허가에 대한 경고문

CSR을 승인하는 능력은 환경 내에서 누구를 신뢰하는지 결정한다. CSR 승인 능력은 광범위하거나 가볍게 부여해서는 안된다. 이 권한을 부여하기 전에 이전 섹션에서 언급한 요청의 요구 사항과 특정 인증서 발급의 영향을 완전히 이해해야 한다.

클러스터 관리자를 위한 참고 사항

이 가이드에서는 서명자가 인증서 API를 제공하도록 설정되었다고 가정한다. 쿠버네티스 컨트롤러 관리자는 서명자의 기본 구현을 제공한다. 이를 활성화하려면 인증 기관(CA)의 키 쌍에 대한 경로와 함께 --cluster-signing-cert-file--cluster-signing-key-file 매개 변수를 컨트롤러 관리자에 전달한다.

4.13 - 클러스터 데몬 관리

롤링 업데이트 수행과 같은 데몬셋 관리를 위한 일반적인 작업을 수행한다.

4.13.1 - 데몬셋(DaemonSet)에서 롤링 업데이트 수행

이 페이지는 데몬셋에서 롤링 업데이트를 수행하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

데몬셋 업데이트 전략

데몬셋에는 두 가지 업데이트 전략 유형이 있다.

  • OnDelete: OnDelete 업데이트 전략을 사용하여, 데몬셋 템플릿을 업데이트한 후, 이전 데몬셋 파드를 수동으로 삭제할 때 새 데몬셋 파드가 생성된다. 이것은 쿠버네티스 버전 1.5 이하에서의 데몬셋의 동작과 동일하다.
  • RollingUpdate: 기본 업데이트 전략이다. RollingUpdate 업데이트 전략을 사용하여, 데몬셋 템플릿을 업데이트한 후, 오래된 데몬셋 파드가 종료되고, 새로운 데몬셋 파드는 제어 방식으로 자동 생성된다. 전체 업데이트 프로세스 동안 데몬셋의 최대 하나의 파드가 각 노드에서 실행된다.

롤링 업데이트 수행

데몬셋의 롤링 업데이트 기능을 사용하려면, .spec.updateStrategy.typeRollingUpdate 를 설정해야 한다.

.spec.updateStrategy.rollingUpdate.maxUnavailable (기본값은 1), .spec.minReadySeconds (기본값은 0), .spec.maxSurge (베타 기능, 기본값은 25%)를 설정할 수도 있다.

RollingUpdate 업데이트 전략으로 데몬셋 생성

이 YAML 파일은 'RollingUpdate'를 업데이트 전략으로 사용하여 데몬셋을 명시한다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 이 톨러레이션(toleration)은 마스터 노드에서 실행 가능한 데몬셋이
      # 마스터에서 파드를 실행할 수 없는 경우 이를 제거하는 것이다
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

데몬셋 매니페스트의 업데이트 전략을 확인한 후, 데몬셋을 생성한다.

kubectl create -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

또는, kubectl apply 로 데몬셋을 업데이트하려는 경우, 동일한 데몬셋을 생성하는 데 kubectl apply 를 사용한다.

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

데몬셋 RollingUpdate 업데이트 전략 확인

데몬셋의 업데이트 전략을 확인하고, RollingUpdate 로 설정되어 있는지 확인한다.

kubectl get ds/fluentd-elasticsearch -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}' -n kube-system

시스템에서 데몬셋을 생성하지 않은 경우, 대신 다음의 명령으로 데몬셋 매니페스트를 확인한다.

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml --dry-run=client -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'

두 명령의 출력 결과는 다음과 같아야 한다.

RollingUpdate

출력 결과가 RollingUpdate 가 아닌 경우, 이전 단계로 돌아가서 데몬셋 오브젝트나 매니페스트를 적절히 수정한다.

데몬셋 템플릿 업데이트

RollingUpdate 데몬셋 .spec.template 에 대한 업데이트는 롤링 업데이트를 트리거한다. 새 YAML 파일을 적용하여 데몬셋을 업데이트한다. 이것은 여러 가지 다른 kubectl 명령으로 수행할 수 있다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 이 톨러레이션(toleration)은 마스터 노드에서 실행 가능한 데몬셋이
      # 마스터에서 파드를 실행할 수 없는 경우 이를 제거하는 것이다
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

선언적 커맨드

구성 파일을 사용하여 데몬셋을 업데이트하는 경우, kubectl apply 를 사용한다.

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset-update.yaml

명령형 커맨드

명령형 커맨드를 사용하여 데몬셋을 업데이트하는 경우, kubectl edit 를 사용한다.

kubectl edit ds/fluentd-elasticsearch -n kube-system
컨테이너 이미지만 업데이트

데몬셋 템플릿(예: .spec.template.spec.containers[*].image)에 의해 정의된 컨테이너 이미지만 업데이트하려면, kubectl set image 를 사용한다.

kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=quay.io/fluentd_elasticsearch/fluentd:v2.6.0 -n kube-system

롤링 업데이트 상태 관찰

마지막으로, 최신 데몬셋 롤링 업데이트의 롤아웃 상태를 관찰한다.

kubectl rollout status ds/fluentd-elasticsearch -n kube-system

롤아웃이 완료되면, 출력 결과는 다음과 비슷하다.

daemonset "fluentd-elasticsearch" successfully rolled out

문제 해결

데몬셋 롤링 업데이트가 더 이상 진행되지 않는다(stuck)

가끔씩, 데몬셋 롤링 업데이트가 더 이상 진행되지 않을 수 있다. 이와 같은 상황이 발생할 수 있는 원인은 다음과 같다.

일부 노드에 리소스가 부족하다

적어도 하나의 노드에서 새 데몬셋 파드를 스케줄링할 수 없어서 롤아웃이 중단되었다. 노드에 리소스가 부족할 때 발생할 수 있다.

이 경우, kubectl get nodes 의 출력 결과와 다음의 출력 결과를 비교하여 데몬셋 파드가 스케줄링되지 않은 노드를 찾는다.

kubectl get pods -l name=fluentd-elasticsearch -o wide -n kube-system

해당 노드를 찾으면, 데몬셋이 아닌 파드를 노드에서 삭제하여 새 데몬셋 파드를 위한 공간을 생성한다.

롤아웃 실패

최근 데몬셋 템플릿 업데이트가 중단된 경우(예를 들어, 컨테이너가 계속 크래시되거나, 컨테이너 이미지가 존재하지 않는 경우(종종 오타로 인해)), 데몬셋 롤아웃이 진행되지 않는다.

이 문제를 해결하려면, 데몬셋 템플릿을 다시 업데이트한다. 이전의 비정상 롤아웃으로 인해 새로운 롤아웃이 차단되지는 않는다.

클럭 차이(skew)

데몬셋에 .spec.minReadySeconds 가 명시된 경우, 마스터와 노드 사이의 클럭 차이로 인해 데몬셋이 올바른 롤아웃 진행 상황을 감지할 수 없다.

정리

네임스페이스에서 데몬셋을 삭제한다.

kubectl delete ds fluentd-elasticsearch -n kube-system

다음 내용

4.13.2 - 데몬셋(DaemonSet)에서 롤백 수행

이 페이지는 데몬셋에서 롤백을 수행하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.7. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

데몬셋에서 롤링 업데이트를 수행하는 방법을 이미 알고 있어야 한다.

데몬셋에서 롤백 수행

1단계: 롤백할 데몬셋 리비전 찾기

마지막 리비전으로 롤백하려는 경우 이 단계를 건너뛸 수 있다.

데몬셋의 모든 리비전을 나열한다.

kubectl rollout history daemonset <daemonset-name>

이 명령은 데몬셋 리비전 목록을 반환한다.

daemonsets "<daemonset-name>"
REVISION        CHANGE-CAUSE
1               ...
2               ...
...
  • 변경 원인은 데몬셋 어노테이션 kubernetes.io/change-cause 에서 생성 시의 리비전으로 복사된다. 변경 원인 어노테이션에서 실행된 명령을 기록하도록 kubectl--record=true 를 지정할 수 있다.

특정 리비전의 세부 사항을 보려면 다음을 수행한다.

kubectl rollout history daemonset <daemonset-name> --revision=1

이 명령은 해당 리비전의 세부 사항을 반환한다.

daemonsets "<daemonset-name>" with revision #1
Pod Template:
Labels:       foo=bar
Containers:
app:
 Image:        ...
 Port:         ...
 Environment:  ...
 Mounts:       ...
Volumes:      ...

2단계: 특정 리비전으로 롤백

# --to-revision에 1단계에서 얻는 리비전 번호를 지정한다
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>

성공하면, 명령은 다음을 반환한다.

daemonset "<daemonset-name>" rolled back

3단계: 데몬셋 롤백 진행 상황 확인

kubectl rollout undo daemonset 은 서버에 데몬셋 롤백을 시작하도록 지시한다. 실제 롤백은 클러스터 컨트롤 플레인 내에서 비동기적으로 수행된다.

롤백 진행 상황을 보려면 다음의 명령을 수행한다.

kubectl rollout status ds/<daemonset-name>

롤백이 완료되면, 출력 결과는 다음과 비슷하다.

daemonset "<daemonset-name>" successfully rolled out

데몬셋 리비전의 이해

이전 kubectl rollout history 단계에서, 데몬셋 리비전 목록을 얻었다. 각 리비전은 ControllerRevision이라는 리소스에 저장된다.

각 리비전에 저장된 내용을 보려면, 데몬셋 리비전 원시 리소스를 찾는다.

kubectl get controllerrevision -l <daemonset-selector-key>=<daemonset-selector-value>

이 명령은 ControllerRevision의 목록을 반환한다.

NAME                               CONTROLLER                     REVISION   AGE
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     1          1h
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     2          1h

각 ControllerRevision은 데몬셋 리비전의 어노테이션과 템플릿을 저장한다.

kubectl rollout undo 는 특정 ControllerRevision을 가져와 데몬셋 템플릿을 ControllerRevision에 저장된 템플릿으로 바꾼다. kubectl rollout undokubectl edit 또는 kubectl apply 와 같은 다른 명령을 통해 데몬셋 템플릿을 이전 리비전으로 업데이트하는 것과 같다.

문제 해결

4.14 - 서비스 카탈로그

서비스 카탈로그 익스텐션(extension) API를 설치한다.

4.14.1 - SC로 서비스 카탈로그 설치하기

서비스 카탈로그는 쿠버네티스 클러스터 내에서 실행되는 응용 프로그램이 클라우드 공급자가 제공하는 데이터 저장소 서비스와 같은 외부 관리 소프트웨어 제품을 쉽게 사용할 수 있도록하는 확장 API이다.

서비스 생성 또는 관리에 대한 자세한 지식 없이도 서비스 브로커를 통해 외부의 매니지드 서비스의 목록과 프로비전, 바인딩하는 방법을 제공한다.

GCP 서비스 카탈로그 설치 프로그램 도구로 쿠버네티스 클러스터에 서비스 카탈로그를 쉽게 설치하거나 제거하여 Google Cloud 프로젝트에 연결할 수 있다.

서비스 카탈로그는 Google Cloud뿐 아니라 모든 종류의 관리형 서비스와 함께 작동할 수 있다.

시작하기 전에

  • 서비스 카탈로그의 핵심 개념을 이해한다.

  • Go 1.6+를 설치하고 GOPATH를 설정한다.

  • SSL 아티팩트 생성에 필요한 cfssl 도구를 설치한다.

  • 서비스 카탈로그에는 Kubernetes 버전 1.7 이상이 필요하다.

  • kubectl 설치 및 설정을 사용하여 Kubernetes 버전 1.7 이상의 클러스터에 연결하도록 구성한다.

  • kubectl 사용자는 서비스 카탈로그를 설치하기 위해 cluster-admin 역할에 바인딩되어야 한다. 이것이 사실인지 확인하려면 다음 명령을 실행한다.

      kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=<user-name>
    

로컬 환경에 sc 설치하기

설치 프로그램은 로컬 컴퓨터에서 sc라는 CLI 도구로 실행된다.

go get을 사용하여 설치한다.

go get github.com/GoogleCloudPlatform/k8s-service-catalog/installer/cmd/sc

sc는 이제 GOPATH/bin 디렉토리에 설치되어야 한다.

쿠버네티스 클러스터에 서비스 카탈로그 설치하기

먼저 명령을 실행하여 모든 종속성이 설치되었는지 확인한다.

sc check

확인에 성공하면 다음을 반환해야 한다.

Dependency check passed. You are good to go.

그런 다음 설치 명령을 실행하고 백업에 사용할 storageclass를 지정한다.

sc install --etcd-backup-storageclass "standard"

서비스 카탈로그 제거하기

sc 도구를 사용하여 쿠버네티스 클러스터에서 서비스 카탈로그를 제거하려면 다음을 실행한다.

sc uninstall

다음 내용

4.15 - 네트워킹

클러스터에 대한 네트워킹 설정 방법에 대해 배운다.

4.15.1 - HostAliases로 파드의 /etc/hosts 항목 추가하기

파드의 /etc/hosts 파일에 항목을 추가하는 것은 DNS나 다른 방법들이 적용되지 않을 때 파드 수준의 호스트네임 해석을 제공한다. PodSpec의 HostAliases 항목을 사용하여 이러한 사용자 정의 항목들을 추가할 수 있다.

HostAliases를 사용하지 않은 수정은 권장하지 않는데, 이는 호스트 파일이 kubelet에 의해 관리되고, 파드 생성/재시작 중에 덮어쓰여질 수 있기 때문이다.

기본 호스트 파일 내용

파드 IP가 할당된 Nginx 파드를 시작한다.

kubectl run nginx --image nginx
pod/nginx created

파드 IP를 확인해보자.

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

호스트 파일의 내용은 아래와 같을 것이다.

kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

기본적으로, hosts 파일은 localhost와 자기 자신의 호스트네임과 같은 IPv4와 IPv6 상용구들만 포함하고 있다.

hostAliases를 사용하여 추가 항목들 추가하기

기본 상용구 이외에, 추가 항목들을 hosts 파일에 추가할 수 있다. 예를 들어, foo.local, bar.local127.0.0.1로, foo.remote, bar.remote10.1.2.3로 해석될 수 있도록, .spec.hostAliases 항목에서 정의하여 파드에 HostAliases를 추가하면 가능하다.

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox
    command:
    - cat
    args:
    - "/etc/hosts"

다음을 실행하여 해당 구성으로 파드를 실행할 수 있다.

kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created

파드의 세부 정보를 검토하여 IPv4 주소와 상태를 확인해보자.

kubectl get pod --output=wide
NAME                           READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod                0/1       Completed   0          6s        10.200.0.5      worker0

hosts 파일 내용은 아래와 같다.

kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

가장 마지막에 추가 항목들이 정의되어 있는 것을 확인할 수 있다.

왜 Kubelet이 호스트 파일을 관리하는가?

컨테이너가 이미 시작되고 난 후 도커가 파일을 수정하는 것을 방지하기 위해 Kubelet은 파드의 각 컨테이너의 hosts 파일을 관리한다.

4.15.2 - IPv4/IPv6 이중 스택 검증

이 문서는 IPv4/IPv6 이중 스택이 활성화된 쿠버네티스 클러스터들을 어떻게 검증하는지 설명한다.

시작하기 전에

  • 이중 스택 네트워킹을 위한 제공자 지원 (클라우드 제공자 또는 기타 제공자들은 라우팅 가능한 IPv4/IPv6 네트워크 인터페이스를 제공하는 쿠버네티스 노드들을 제공해야 한다.)
  • 이중 스택을 지원하는 네트워크 플러그인 (예: Calico, Cilium 또는 Kubenet)
  • 이중 스택 활성화 클러스터
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.20. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

어드레싱 검증

노드 어드레싱 검증

각각의 이중 스택 노드는 단일 IPv4 블록 및 단일 IPv6 블록을 할당받아야 한다. IPv4/IPv6 파드 주소 범위를 다음 커맨드를 실행하여 검증한다. 샘플 노드 이름을 클러스터 내 검증된 이중 스택 노드로 대체한다. 본 예제에서, 노드 이름은 k8s-linuxpool1-34450317-0 이다.

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
a00:100::/24

단일 IPv4 블록과 단일 IPv6 블록이 할당되어야 한다.

노드가 IPv4 및 IPv6 인터페이스를 가지고 있는지 검증한다. 노드 이름을 클러스터의 검증된 노드로 대체한다. 본 예제에서 노드 이름은 k8s-linuxpool1-34450317-0 이다.

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.240.0.5
InternalIP: 2001:1234:5678:9abc::5

파드 어드레싱 검증

파드가 IPv4 및 IPv6 주소를 할당받았는지 검증한다. 파드 이름을 클러스터에서 검증된 파드로 대체한다. 본 예제에서 파드 이름은 pod01 이다.

kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
a00:100::4

status.podIPs fieldPath를 통한 다운워드(downward) API로 파드 IP들을 검증할 수도 있다. 다음 스니펫은 컨테이너 내 MY_POD_IPS 라는 환경 변수를 통해 파드 IP들을 어떻게 노출시킬 수 있는지 보여준다.

        env:
        - name: MY_POD_IPS
          valueFrom:
            fieldRef:
              fieldPath: status.podIPs

다음 커맨드는 컨테이너 내 MY_POD_IPS 환경 변수의 값을 출력한다. 해당 값은 파드의 IPv4 및 IPv6 주소를 나타내는 쉼표로 구분된 목록이다.

kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,a00:100::4

파드의 IP 주소는 또한 컨테이너 내 /etc/hosts 에 적힐 것이다. 다음 커맨드는 이중 스택 파드의 /etc/hosts 에 cat을 실행시킨다. 출력 값을 통해 파드의 IPv4 및 IPv6 주소 모두 검증할 수 있다.

kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.244.1.4    pod01
a00:100::4    pod01

서비스 검증

.spec.ipFamilyPolicy 를 명시적으로 정의하지 않은 다음의 서비스를 생성한다. 쿠버네티스는 처음 구성된 service-cluster-ip-range 에서 서비스에 대한 클러스터 IP를 할당하고 .spec.ipFamilyPolicySingleStack 으로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl 을 사용하여 서비스의 YAML을 확인한다.

kubectl get svc my-service -o yaml

이 서비스에서 .spec.ipFamilyPolicySingleStack 으로 설정하고 .spec.clusterIP 를 kube-controller-manager의 --service-cluster-ip-range 플래그를 통해 설정된 첫 번째 구성 범위에서 IPv4 주소로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
spec:
  clusterIP: 10.0.217.164
  clusterIPs:
  - 10.0.217.164
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    app: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.ipFamilies 의 첫 번째 배열 요소로 IPv6 을 명시적으로 정의하는 다음 서비스를 생성한다. Kubernetes는 service-cluster-ip-range로 구성된 IPv6 범위에서 서비스용 클러스터 IP를 할당하고 .spec.ipFamilyPolicySingleStack 으로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv4
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

kubectl 를 사용하여 서비스의 YAML을 확인한다.

kubectl get svc my-service -o yaml

이 서비스에서 .spec.ipFamilyPolicySingleStack 으로 설정하고 .spec.clusterIP 를 kube-controller-manager의 --service-cluster-ip-range 플래그를 통해 설정된 IPv6 범위에서 IPv6 주소로 설정한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: MyApp
  name: my-service
spec:
  clusterIP: fd00::5118
  clusterIPs:
  - fd00::5118
  ipFamilies:
  - IPv6
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

PreferDualStack.spec.ipFamilyPolicy 을 명시적으로 정의하는 다음 서비스를 생성한다. 쿠버네티스는 IPv4 및 IPv6 주소를 모두 할당하고 (이 클러스터에는 이중 스택을 사용하도록 설정되었으므로) .spec.ipFamilies 배열에 있는 첫 번째 요소의 주소 계열을 기반으로.spec.ClusterIP 목록에서 .spec.ClusterIPs 를 선택한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

서비스가 kubectl describe 를 사용하여 IPv4 및 IPv6 주소 블록에서 클러스터 IP를 가져오는지 확인한다. 그런 다음 IP 및 포트를 통해 서비스에 대한 접속을 확인할 수 있다.

kubectl describe svc -l app=MyApp
Name:              my-service
Namespace:         default
Labels:            app=MyApp
Annotations:       <none>
Selector:          app=MyApp
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.0.216.242
IPs:               10.0.216.242,fd00::af55
Port:              <unset>  80/TCP
TargetPort:        9376/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

이중 스택 로드 밸런싱 서비스 생성

만약 클라우드 제공자가 IPv6 기반 외부 로드 밸런서 구성을 지원한다면 .spec.ipFamilyPolicyPreferDualStack.spec.ipFamilies 배열의 첫 번째 요소로 IPv6LoadBalancer 로 설정된 type 필드를 사용하여 다음 서비스를 생성한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamily: IPv6
  type: LoadBalancer
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Check the Service:

kubectl get svc -l app=MyApp

서비스가 IPv6 주소 블록에서 CLUSTER-IP 주소 및 EXTERNAL-IP 주소를 할당받는지 검증한다. 그리고 나서 IP 및 포트로 서비스 접근이 가능한지 검증할 수 있다.

NAME         TYPE           CLUSTER-IP   EXTERNAL-IP        PORT(S)        AGE
my-service   LoadBalancer   fd00::7ebc   2603:1030:805::5   80:30790/TCP   35s

4.16 - GPU 스케줄링

클러스터의 노드별로 리소스로 사용할 GPU를 구성하고 스케줄링한다.
FEATURE STATE: Kubernetes v1.10 [beta]

쿠버네티스는 AMD 및 NVIDIA GPU(그래픽 프로세싱 유닛)를 노드들에 걸쳐 관리하기 위한 실험적인 지원을 포함한다.

이 페이지는 여러 쿠버네티스 버전에서 사용자가 GPU를 활용할 수 있는 방법과 현재의 제약 사항을 설명한다.

디바이스 플러그인 사용하기

쿠버네티스는 디바이스 플러그인을 구현하여 파드가 GPU와 같이 특별한 하드웨어 기능에 접근할 수 있게 한다.

관리자는 해당하는 하드웨어 벤더의 GPU 드라이버를 노드에 설치해야 하며, GPU 벤더가 제공하는 디바이스 플러그인을 실행해야 한다.

위의 조건이 만족되면, 쿠버네티스는 amd.com/gpu 또는 nvidia.com/gpu 를 스케줄 가능한 리소스로써 노출시킨다.

사용자는 이 GPU들을 cpumemory 를 요청하는 방식과 동일하게 <vendor>.com/gpu 를 요청함으로써 컨테이너에서 활용할 수 있다. 그러나 GPU를 사용할 때는 리소스 요구 사항을 명시하는 방식에 약간의 제약이 있다.

  • GPU는 limits 섹션에서만 명시되는 것을 가정한다. 그 의미는 다음과 같다.
    • 쿠버네티스는 limits를 requests의 기본 값으로 사용하게 되므로 사용자는 GPU limits 를 명시할 때 requests 명시하지 않아도 된다.
    • 사용자는 limitsrequests 를 모두 명시할 수 있지만, 두 값은 동일해야 한다.
    • 사용자는 limits 명시 없이는 GPU requests 를 명시할 수 없다.
  • 컨테이너들(그리고 파드들)은 GPU를 공유하지 않는다. GPU에 대한 초과 할당(overcommitting)은 제공되지 않는다.
  • 각 컨테이너는 하나 이상의 GPU를 요청할 수 있다. GPU의 일부(fraction)를 요청하는 것은 불가능하다.

다음은 한 예제를 보여준다.

apiVersion: v1
kind: Pod
metadata:
  name: cuda-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: cuda-vector-add
      # https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
      image: "k8s.gcr.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1 # GPU 1개 요청하기

AMD GPU 디바이스 플러그인 배치하기

공식 AMD GPU 디바이스 플러그인에는 다음의 요구 사항이 있다.

  • 쿠버네티스 노드들에는 AMD GPU 리눅스 드라이버가 미리 설치되어 있어야 한다.

클러스터가 실행 중이고 위의 요구 사항이 만족된 후, AMD 디바이스 플러그인을 배치하기 위해서는 아래 명령어를 실행한다.

kubectl create -f https://raw.githubusercontent.com/RadeonOpenCompute/k8s-device-plugin/v1.10/k8s-ds-amdgpu-dp.yaml

RadeonOpenCompute/k8s-device-plugin에 이슈를 로깅하여 해당 서드 파티 디바이스 플러그인에 대한 이슈를 리포트할 수 있다.

NVIDIA GPU 디바이스 플러그인 배치하기

현재는 NVIDIA GPU에 대한 두 개의 디바이스 플러그인 구현체가 있다.

공식 NVIDIA GPU 디바이스 플러그인

공식 NVIDIA GPU 디바이스 플러그인은 다음의 요구 사항을 가진다.

  • 쿠버네티스 노드에는 NVIDIA 드라이버가 미리 설치되어 있어야 한다.
  • 쿠버네티스 노드에는 nvidia-docker 2.0이 미리 설치되어 있어야 한다.
  • Kubelet은 자신의 컨테이너 런타임으로 도커를 사용해야 한다.
  • 도커는 runc 대신 nvidia-container-runtime기본 런타임으로 설정되어야 한다.
  • NVIDIA 드라이버의 버전은 조건 ~= 384.81을 만족해야 한다.

클러스터가 실행 중이고 위의 요구 사항이 만족된 후, NVIDIA 디바이스 플러그인을 배치하기 위해서는 아래 명령어를 실행한다.

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/1.0.0-beta4/nvidia-device-plugin.yml

NVIDIA/k8s-device-plugin에 이슈를 로깅하여 해당 서드 파티 디바이스 플러그인에 대한 이슈를 리포트할 수 있다.

GCE에서 사용되는 NVIDIA GPU 디바이스 플러그인

GCE에서 사용되는 NVIDIA GPU 디바이스 플러그인은 nvidia-docker의 사용이 필수가 아니며 컨테이너 런타임 인터페이스(CRI)에 호환되는 다른 컨테이너 런타임을 사용할 수 있다. 해당 사항은 컨테이너에 최적화된 OS에서 테스트되었고, 우분투 1.9 이후 버전에 대한 실험적인 코드를 가지고 있다.

사용자는 다음 커맨드를 사용하여 NVIDIA 드라이버와 디바이스 플러그인을 설치할 수 있다.

# 컨테이너에 최적회된 OS에 NVIDIA 드라이버 설치:
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/stable/daemonset.yaml

# 우분투에 NVIDIA 드라이버 설치(실험적):
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/stable/nvidia-driver-installer/ubuntu/daemonset.yaml

# 디바이스 플러그인 설치:
kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.14/cluster/addons/device-plugins/nvidia-gpu/daemonset.yaml

GoogleCloudPlatform/container-engine-accelerators에 이슈를 로깅하여 해당 서드 파티 디바이스 플러그인에 대한 이슈를 리포트할 수 있다.

Google은 GKE에서 NVIDIA GPU 사용에 대한 자체 설명서를 게재하고 있다.

다른 타입의 GPU들을 포함하는 클러스터

만약 클러스터의 노드들이 서로 다른 타입의 GPU를 가지고 있다면, 사용자는 파드를 적합한 노드에 스케줄 하기 위해서 노드 레이블과 노드 셀렉터를 사용할 수 있다.

예를 들면,

# 노드가 가진 가속기 타입에 따라 레이블을 단다.
kubectl label nodes <node-with-k80> accelerator=nvidia-tesla-k80
kubectl label nodes <node-with-p100> accelerator=nvidia-tesla-p100

노드 레이블링 자동화

만약 AMD GPU 디바이스를 사용하고 있다면, 노드 레이블러를 배치할 수 있다. 노드 레이블러는 GPU 디바이스의 속성에 따라서 노드에 자동으로 레이블을 달아 주는 컨트롤러이다.

현재 이 컨트롤러는 다음의 속성에 대해 레이블을 추가할 수 있다.

  • 디바이스 ID (-device-id)
  • VRAM 크기 (-vram)
  • SIMD 개수 (-simd-count)
  • 계산 유닛 개수 (-cu-count)
  • 펌웨어 및 기능 버전 (-firmware)
  • GPU 계열, 두 개 문자 형태의 축약어 (-family)
    • SI - Southern Islands
    • CI - Sea Islands
    • KV - Kaveri
    • VI - Volcanic Islands
    • CZ - Carrizo
    • AI - Arctic Islands
    • RV - Raven
kubectl describe node cluster-node-23
    Name:               cluster-node-23
    Roles:              <none>
    Labels:             beta.amd.com/gpu.cu-count.64=1
                        beta.amd.com/gpu.device-id.6860=1
                        beta.amd.com/gpu.family.AI=1
                        beta.amd.com/gpu.simd-count.256=1
                        beta.amd.com/gpu.vram.16G=1
                        beta.kubernetes.io/arch=amd64
                        beta.kubernetes.io/os=linux
                        kubernetes.io/hostname=cluster-node-23
    Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                        node.alpha.kubernetes.io/ttl: 0
    …

노드 레이블러가 사용된 경우, GPU 타입을 파드 스펙에 명시할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: cuda-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: cuda-vector-add
      # https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
      image: "k8s.gcr.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100 # 또는 nvidia-tesla-k80 등.

이것은 파드가 사용자가 지정한 GPU 타입을 가진 노드에 스케줄 되도록 만든다.

4.17 - HugePages 관리

클러스터에서 huge page를 스케줄할 수 있는 리소스로 구성하고 관리한다.
FEATURE STATE: Kubernetes v1.23 [stable]

쿠버네티스는 파드의 애플리케이션에 미리 할당된 huge page의 할당과 사용을 지원한다. 이 페이지에서는 사용자가 huge page를 사용하는 방법에 대해 설명한다.

시작하기 전에

  1. 쿠버네티스 노드는 노드에 대한 huge page 용량을 보고하기 위해 huge page를 미리 할당해야 한다. 노드는 여러 크기의 huge page를 미리 할당할 수 있다.

노드는 모든 huge page 리소스를 스케줄 가능한 리소스로 자동 검색하고 보고한다.

API

리소스 이름 hugepages-<size> (<size> 는 특정 노드에서 지원되는 정수값을 사용하는 가장 간단한 2진 표기법)를 사용하여 컨테이너 레벨의 리소스 요구 사항을 통해 huge page를 사용할 수 있다. 예를 들어, 노드가 2048KiB 및 1048576KiB 페이지 크기를 지원하는 경우, 스케줄 가능한 리소스인 hugepages-2Mihugepages-1Gi 를 노출한다. CPU나 메모리와 달리, huge page는 오버커밋을 지원하지 않는다. 참고로 hugepage 리소스를 요청하는 경우, 메모리 또는 CPU 리소스도 요청해야 한다.

파드는 단일 파드 스펙에 여러 개의 huge page 크기를 사용할 수 있다. 이 경우 모든 볼륨 마운트에 대해 medium: HugePages-<hugepagesize> 표기법을 사용해야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages-2Mi
      name: hugepage-2mi
    - mountPath: /hugepages-1Gi
      name: hugepage-1gi
    resources:
      limits:
        hugepages-2Mi: 100Mi
        hugepages-1Gi: 2Gi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage-2mi
    emptyDir:
      medium: HugePages-2Mi
  - name: hugepage-1gi
    emptyDir:
      medium: HugePages-1Gi

파드는 동일한 크기의 huge page들을 요청하는 경우에만 medium: HugePages 를 사용할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages
      name: hugepage
    resources:
      limits:
        hugepages-2Mi: 100Mi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
  • Huge page 요청(requests)은 제한(limits)과 같아야 한다. 제한이 지정되었지만, 요청은 지정되지 않은 경우 이것이 기본값이다.
  • Huge page는 컨테이너 범위에서 격리되므로, 각 컨테이너에는 컨테이너 사양에서 요청한대로 cgroup 샌드박스에 대한 제한이 있다.
  • Huge page가 지원하는 EmptyDir 볼륨은 파드 요청보다 더 많은 huge page 메모리를 사용하지 말아야 한다.
  • shmget()SHM_HUGETLB 를 통해 huge page를 사용하는 애플리케이션은 proc/sys/vm/hugetlb_shm_group 과 일치하는 보충 그룹(supplemental group)으로 실행해야 한다.
  • 네임스페이스에서의 huge page 사용은 hugepages-<size> 토큰을 사용하는 cpu 또는 memory 와 같은 다른 컴퓨트 리소스와 비슷한 리소스쿼터(ResourceQuota)를 통해 제어할 수 있다.

4.18 - 플러그인으로 kubectl 확장

kubectl 플러그인을 작성하고 설치해서 kubectl을 확장한다.

이 가이드는 kubectl 확장을 설치하고 작성하는 방법을 보여준다. 핵심 kubectl 명령을 쿠버네티스 클러스터와 상호 작용하기 위한 필수 구성 요소로 생각함으로써, 클러스터 관리자는 플러그인을 이러한 구성 요소를 활용하여 보다 복잡한 동작을 만드는 수단으로 생각할 수 있다. 플러그인은 새로운 하위 명령으로 kubectl 을 확장하고, 주요 배포판에 포함되지 않은 kubectl 의 새로운 사용자 정의 기능을 허용한다.

시작하기 전에

동작하는 kubectl 바이너리가 설치되어 있어야 한다.

kubectl 플러그인 설치

플러그인은 이름이 kubectl- 로 시작되는 독립형 실행 파일이다. 플러그인을 설치하려면, 실행 파일을 PATH 에 지정된 디렉터리로 옮기면 된다.

Krew를 사용하여 오픈소스에서 사용 가능한 kubectl 플러그인을 검색하고 설치할 수도 있다. Krew는 쿠버네티스 SIG CLI 커뮤니티에서 관리하는 플러그인 관리자이다.

플러그인 디스커버리

kubectl 은 유효한 플러그인 실행 파일을 PATH 에서 검색하는 kubectl plugin list 명령을 제공한다. 이 명령을 실행하면 PATH 에 있는 모든 파일을 탐색한다. 실행 가능하고, kubectl- 로 시작하는 모든 파일은 이 명령의 출력 결과에 PATH 에 있는 순서대로 표시된다. 실행 파일이 아닌 파일이 kubectl- 시작하는 경우 경고가 포함된다. 서로의 이름과 겹치는 유효한 플러그인 파일에 대한 경고도 포함된다.

Krew를 사용하여 커뮤니티가 관리하는 플러그인 인덱스에서 kubectl 플러그인을 검색하고 설치할 수 있다.

제한 사항

현재 기존 kubectl 명령을 덮어 쓰는 플러그인을 생성할 수 없다. 예를 들어, 플러그인 kubectl-version 을 만들면 기존의 kubectl version 명령이 항상 우선하므로, 플러그인이 실행되지 않는다. 이 제한으로 인해, 플러그인을 사용하여 기존 kubectl 명령에 새로운 하위 명령을 추가할 수도 없다. 예를 들어, 플러그인 이름을 kubectl-create-foo 로 지정하여 kubectl create foo 하위 명령을 추가하면 해당 플러그인이 무시된다.

kubectl plugin list 는 이를 시도하는 유효한 플러그인에 대한 경고를 표시한다.

kubectl 플러그인 작성

커맨드-라인 명령을 작성할 수 있는 프로그래밍 언어나 스크립트로 플러그인을 작성할 수 있다.

플러그인 설치 또는 사전 로딩이 필요하지 않다. 플러그인 실행 파일은 kubectl 바이너리에서 상속된 환경을 받는다. 플러그인은 이름을 기반으로 구현할 명령 경로를 결정한다. 예를 들어, kubectl-foo 라는 플러그인은 kubectl foo 명령을 제공한다. PATH 어딘가에 플러그인 실행 파일을 설치해야 한다.

플러그인 예제

#!/bin/bash

# 선택적 인수 처리
if [[ "$1" == "version" ]]
then
    echo "1.0.0"
    exit 0
fi

# 선택적 인수 처리
if [[ "$1" == "config" ]]
then
    echo "$KUBECONFIG"
    exit 0
fi

echo "I am a plugin named kubectl-foo"

플러그인 사용

플러그인을 사용하려면, 실행 가능하게 만든다.

sudo chmod +x ./kubectl-foo

그리고 PATH 의 어느 곳에나 옮겨 놓는다.

sudo mv ./kubectl-foo /usr/local/bin

이제 플러그인을 kubectl 명령으로 호출할 수 있다.

kubectl foo
I am a plugin named kubectl-foo

모든 인수와 플래그는 그대로 실행 파일로 전달된다.

kubectl foo version
1.0.0

모든 환경 변수도 실행 파일로 그대로 전달된다.

export KUBECONFIG=~/.kube/config
kubectl foo config

/home/<user>/.kube/config
KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config

또한, 플러그인으로 전달되는 첫 번째 인수는 항상 호출된 위치의 전체 경로이다(위의 예에서 $0/usr/local/bin/kubectl-foo 와 동일하다).

플러그인 이름 지정

위 예제에서 볼 수 있듯이, 플러그인은 파일명을 기반으로 구현할 명령 경로를 결정한다. 플러그인이 대상으로 하는 명령 경로의 모든 하위 명령은 대시(-)로 구분된다. 예를 들어, 사용자가 kubectl foo bar baz 명령을 호출할 때마다 호출되는 플러그인은 파일명이 kubectl-foo-bar-baz 이다.

플래그와 인수 처리

kubectl 플러그인은 전달된 모든 인수를 파싱하고 유효성을 검사해야 한다. 플러그인 작성자를 대상으로 하는 Go 라이브러리에 대한 자세한 내용은 커맨드 라인 런타임 패키지 사용을 참고한다.

다음은 사용자가 추가 플래그와 인수를 제공하면서 플러그인을 호출하는 추가 사례이다. 이것은 위 시나리오의 kubectl-foo-bar-baz 플러그인을 기반으로한다.

kubectl foo bar baz arg1 --flag=value arg2 를 실행하면, kubectl의 플러그인 메커니즘은 먼저 가장 긴 가능한 이름을 가진 플러그인을 찾으려고 시도한다. 여기서는 kubectl-foo-bar-baz-arg1 이다. 해당 플러그인을 찾지 못하면, kubectl은 대시로 구분된 마지막 값을 인수(여기서는 arg1)로 취급하고, 다음으로 가장 긴 가능한 이름인 kubectl-foo-bar-baz 를 찾는다. 이 이름의 플러그인을 찾으면, kubectl은 해당 플러그인을 호출하여, 플러그인 이름 뒤에 모든 인수와 플래그를 플러그인 프로세스의 인수로 전달한다.

예:

# 플러그인 생성
echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
sudo chmod +x ./kubectl-foo-bar-baz

# $PATH에 있는 디렉터리로 옮겨 플러그인을 "설치"
sudo mv ./kubectl-foo-bar-baz /usr/local/bin

# 플러그인을 kubectl이 인식하는지 확인
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-foo-bar-baz
# test that calling your plugin via a "kubectl" command works
# even when additional arguments and flags are passed to your
# plugin executable by the user.
kubectl foo bar baz arg1 --meaningless-flag=true
My first command-line argument was arg1

보시다시피, 사용자가 지정한 kubectl 명령을 기반으로 플러그인을 찾았으며, 모든 추가 인수와 플래그는 플러그인 실행 파일이 발견되면 그대로 전달된다.

대시와 언더스코어가 있는 이름

kubectl 플러그인 메커니즘은 플러그인 파일명에 대시(-)를 사용하여 플러그인이 처리하는 하위 명령 시퀀스를 분리하지만, 파일명에 언더스코어(_)를 사용하여 커맨드 라인 호출에 대시를 포함하는 플러그인 명령을 생성할 수 있다.

예:

# 파일명에 언더스코어(_)가 있는 플러그인 생성
echo -e '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
sudo chmod +x ./kubectl-foo_bar

# $PATH에 플러그인을 옮긴다
sudo mv ./kubectl-foo_bar /usr/local/bin

# 이제 kubectl을 통해 플러그인을 사용할 수 있다
kubectl foo-bar
I am a plugin with a dash in my name

참고로 플러그인 파일명에 언더스코어를 추가해도 kubectl foo_bar 와 같은 명령을 사용할 수 있다. 위 예에서 명령은 대시(-) 또는 언더스코어(_)을 사용하여 호출할 수 있다.

# 대시를 포함한 사용자 정의 명령을 사용할 수 있다
kubectl foo-bar
I am a plugin with a dash in my name
# 언더스코어를 포함한 사용자 정의 명령을 사용할 수도 있다
kubectl foo_bar
I am a plugin with a dash in my name

이름 충돌과 오버셰도잉(overshadowing)

PATH 의 다른 위치에 동일한 파일명을 가진 여러 플러그인이 있을 수 있다. 예를 들어, PATH 값이 PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins 로 주어지고, kubectl-foo 플러그인을 복사한 파일이 /usr/local/bin/plugins/usr/local/bin/moreplugins 에 있을 수 있다. kubectl plugin list 명령의 출력 결과는 다음과 같다.

PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/plugins/kubectl-foo
/usr/local/bin/moreplugins/kubectl-foo
  - warning: /usr/local/bin/moreplugins/kubectl-foo is overshadowed by a similarly named plugin: /usr/local/bin/plugins/kubectl-foo

error: one plugin warning was found

위 시나리오에서, /usr/local/bin/moreplugins/kubectl-foo 아래의 경고는 이 플러그인이 실행되지 않을 것임을 알려준다. 대신, PATH 에 먼저 나타나는 실행 파일인 /usr/local/bin/plugins/kubectl-foo 는 항상 발견되고 kubectl 플러그인 메카니즘에 의해 먼저 실행된다.

이 문제를 해결하는 방법은 kubectl 와 함께 사용하려는 플러그인의 위치가 PATH 에 항상에서 먼저 오도록 하는 것이다. 예를 들어, kubectl 명령 kubectl foo 가 호출될 때마다 항상 /usr/local/bin/moreplugins/kubectl-foo 를 사용하려면, PATH 의 값을 /usr/local/bin/moreplugins:/usr/local/bin/plugins 로 변경한다.

가장 긴 실행 파일명의 호출

플러그인 파일명으로 발생할 수 있는 또 다른 종류의 오버셰도잉이 있다. 사용자의 PATHkubectl-foo-barkubectl-foo-bar-baz 라는 두 개의 플러그인이 있다면, kubectl 플러그인 메커니즘은 항상 주어진 사용자의 명령에 대해 가장 긴 가능한 플러그인 이름을 선택한다. 아래에 몇 가지 예가 있다.

# 주어진 kubectl 명령의 경우, 가장 긴 가능한 파일명을 가진 플러그인이 항상 선호된다
kubectl foo bar baz
Plugin kubectl-foo-bar-baz is executed
kubectl foo bar
Plugin kubectl-foo-bar is executed
kubectl foo bar baz buz
Plugin kubectl-foo-bar-baz is executed, with "buz" as its first argument
kubectl foo bar buz
Plugin kubectl-foo-bar is executed, with "buz" as its first argument

이 디자인 선택은 필요한 경우 여러 파일에 플러그인 하위 명령을 구현할 수 있도록 하고 이러한 하위 명령을 "부모" 플러그인 명령 아래에 중첩할 수 있도록 한다.

ls ./plugin_command_tree
kubectl-parent
kubectl-parent-subcommand
kubectl-parent-subcommand-subsubcommand

플러그인 경고 확인

위에서 언급한 kubectl plugin list 명령을 사용하여 kubectl 에 의해 플러그인이 표시되는지 확인하고, kubectl 명령으로 호출되지 못하게 하는 경고가 없는지 확인할 수 있다.

kubectl plugin list
The following kubectl-compatible plugins are available:

test/fixtures/pkg/kubectl/plugins/kubectl-foo
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo is overshadowed by a similarly named plugin: test/fixtures/pkg/kubectl/plugins/kubectl-foo
plugins/kubectl-invalid
  - warning: plugins/kubectl-invalid identified as a kubectl plugin, but it is not executable

error: 2 plugin warnings were found

커맨드 라인 런타임 패키지 사용

kubectl 용 플러그인을 작성하고 있고 Go를 사용한다면, cli-runtime 유틸리티 라이브러리를 사용할 수 있다.

이 라이브러리는 사용자의 kubeconfig 파일을 파싱이나 업데이트하거나, REST 스타일의 요청을 API 서버에 작성하거나, 구성 및 출력과 관련된 플래그를 바인딩하기 위한 헬퍼를 제공한다.

CLI 런타임 리포지터리에 제공된 도구 사용법의 예제는 샘플 CLI 플러그인을 참고한다.

kubectl 플러그인 배포

다른 사람이 사용할 수 있는 플러그인을 개발한 경우, 이를 패키징하고, 배포하고 사용자에게 업데이트를 제공하는 방법을 고려해야 한다.

Krew

Krew는 플러그인을 패키징하고 배포하는 크로스-플랫폼 방식을 제공한다. 이렇게 하면, 모든 대상 플랫폼(리눅스, 윈도우, macOS 등)에 단일 패키징 형식을 사용하고 사용자에게 업데이트를 제공한다. Krew는 또한 다른 사람들이 여러분의 플러그인을 검색하고 설치할 수 있도록 플러그인 인덱스를 유지 관리한다.

네이티브 / 플랫폼 별 패키지 관리

다른 방법으로는, 리눅스의 aptyum, 윈도우의 Chocolatey, macOS의 Homebrew와 같은 전통적인 패키지 관리자를 사용할 수 있다. 새 실행 파일을 사용자의 PATH 어딘가에 배치할 수 있는 패키지 관리자라면 어떤 패키지 관리자도 괜찮다. 플러그인 작성자로서, 이 옵션을 선택하면 각 릴리스의 여러 플랫폼에서 kubectl 플러그인의 배포 패키지를 업데이트해야 한다.

소스 코드

소스 코드를 게시(예를 들어, Git 리포지터리)할 수 있다. 이 옵션을 선택하면, 해당 플러그인을 사용하려는 사람이 코드를 가져와서, 빌드 환경을 설정하고(컴파일이 필요한 경우), 플러그인을 배포해야 한다. 컴파일된 패키지를 사용 가능하게 하거나, Krew를 사용하면 설치가 더 쉬워진다.

다음 내용

  • Go로 작성된 플러그인의 자세한 예제에 대해서는 샘플 CLI 플러그인 리포지터리를 확인한다. 궁금한 사항이 있으면, SIG CLI 팀에 문의한다.
  • kubectl 플러그인 패키지 관리자인 Krew에 대해 읽어본다.

5 - 튜토리얼

쿠버네티스 문서의 본 섹션은 튜토리얼을 포함하고 있다. 튜토리얼은 개별 작업 단위보다 더 큰 목표를 달성하기 위한 방법을 보여준다. 일반적으로 튜토리얼은 각각 순차적 단계가 있는 여러 섹션으로 구성된다. 각 튜토리얼을 따라하기 전에, 나중에 참조할 수 있도록 표준 용어집 페이지를 북마크하기를 권한다.

기초

구성

상태 유지를 하지 않는(stateless) 애플리케이션

상태 유지가 필요한(stateful) 애플리케이션

클러스터

서비스

다음 내용

튜토리얼을 작성하고 싶다면, 튜토리얼 페이지 유형에 대한 정보가 있는 콘텐츠 페이지 유형 페이지를 참조한다.

5.1 - Hello Minikube

이 튜토리얼에서는 Minikube와 Katacoda를 이용하여 쿠버네티스에서 샘플 애플리케이션을 어떻게 실행하는지 살펴본다. Katacode는 무료로 브라우저에서 쿠버네티스 환경을 제공한다.

목적

  • 샘플 애플리케이션을 minikube에 배포한다.
  • 배포한 애플리케이션을 실행한다.
  • 애플리케이션의 로그를 확인한다.

시작하기 전에

이 튜토리얼은 NGINX를 사용해서 모든 요청에 응답하는 컨테이너 이미지를 제공한다.

minikube 클러스터 만들기

  1. Launch Terminal 을 클릭

  1. 브라우저에서 쿠버네티스 대시보드를 열어보자.

    minikube dashboard
    
  2. Katacoda 환경에서는: 터미널 패널의 상단에서 플러스를 클릭하고, 이어서 Select port to view on Host 1 을 클릭

  3. Katacoda 환경에서는: 30000 을 입력하고 Display Port 를 클릭.

URL을 이용하여 대시보드 접속하기

자동으로 웹 브라우저가 열리는 것을 원치 않는다면, --url 플래그와 함께 다음과 같은 명령어를 실행하여 대시보드 접속 URL을 출력할 수 있다.

minikube dashboard --url

디플로이먼트 만들기

쿠버네티스 파드는 관리와 네트워킹 목적으로 함께 묶여 있는 하나 이상의 컨테이너 그룹이다. 이 튜토리얼의 파드에는 단 하나의 컨테이너만 있다. 쿠버네티스 디플로이먼트는 파드의 헬스를 검사해서 파드의 컨테이너가 종료되었다면 재시작해준다. 파드의 생성 및 스케일링을 관리하는 방법으로 디플로이먼트를 권장한다.

  1. kubectl create 명령어를 실행하여 파드를 관리할 디플로이먼트를 만든다. 이 파드는 제공된 Docker 이미지를 기반으로 한 컨테이너를 실행한다.

    kubectl create deployment hello-node --image=k8s.gcr.io/echoserver:1.4
    
  2. 디플로이먼트 보기

    kubectl get deployments
    

    다음과 유사하게 출력된다.

    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    hello-node   1/1     1            1           1m
    
  3. 파드 보기

    kubectl get pods
    

    다음과 유사하게 출력된다.

    NAME                          READY     STATUS    RESTARTS   AGE
    hello-node-5f76cf6ccf-br9b5   1/1       Running   0          1m
    
  4. 클러스터 이벤트 보기

    kubectl get events
    
  5. kubectl 환경설정 보기

    kubectl config view
    

서비스 만들기

기본적으로 파드는 쿠버네티스 클러스터 내부의 IP 주소로만 접근할 수 있다. hello-node 컨테이너를 쿠버네티스 가상 네트워크 외부에서 접근하려면 파드를 쿠버네티스 서비스로 노출해야 한다.

  1. kubectl expose 명령어로 퍼블릭 인터넷에 파드 노출하기

    kubectl expose deployment hello-node --type=LoadBalancer --port=8080
    

    --type=LoadBalancer플래그는 클러스터 밖의 서비스로 노출하기 원한다는 뜻이다.

    k8s.gcr.io/echoserver 이미지 내의 애플리케이션 코드는 TCP 포트 8080에서만 수신한다. kubectl expose를 사용하여 다른 포트를 노출한 경우, 클라이언트는 다른 포트에 연결할 수 없다.

  2. 생성한 서비스 살펴보기

    kubectl get services
    

    다음과 유사하게 출력된다.

    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    hello-node   LoadBalancer   10.108.144.78   <pending>     8080:30369/TCP   21s
    kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          23m
    

    로드 밸런서를 지원하는 클라우드 공급자의 경우에는 서비스에 접근할 수 있도록 외부 IP 주소가 프로비저닝 한다. minikube에서 LoadBalancer타입은 minikube service 명령어를 통해서 해당 서비스를 접근할 수 있게 한다.

  3. 다음 명령어를 실행한다

    minikube service hello-node
    
  4. Katacoda 환경에서만: 플러스를 클릭한 후에 Select port to view on Host 1 를 클릭.

  5. Katacoda 환경에서만: 서비스 출력에서 8080의 반대편에 표시되는 5자리 포트 번호를 기록 한다. 이 포트 번호는 무작위로 생성되며, 사용자마다 다를 수 있다. 포트 번호 텍스트 상자에 포트 번호를 입력한 다음, 포트 표시를 클릭한다. 이전 예시를 사용해서 30369 를 입력한다.

    이렇게 하면 당신의 앱을 서비스하는 브라우저 윈도우를 띄우고 애플리케이션의 응답을 볼 수 있다.

애드온 사용하기

minikube 툴은 활성화하거나 비활성화할 수 있고 로컬 쿠버네티스 환경에서 접속해 볼 수 있는 내장 애드온 셋이 포함되어 있다.

  1. 현재 지원하는 애드온 목록을 확인한다.

    minikube addons list
    

    다음과 유사하게 출력된다.

    addon-manager: enabled
    dashboard: enabled
    default-storageclass: enabled
    efk: disabled
    freshpod: disabled
    gvisor: disabled
    helm-tiller: disabled
    ingress: disabled
    ingress-dns: disabled
    logviewer: disabled
    metrics-server: disabled
    nvidia-driver-installer: disabled
    nvidia-gpu-device-plugin: disabled
    registry: disabled
    registry-creds: disabled
    storage-provisioner: enabled
    storage-provisioner-gluster: disabled
    
  2. 애드온을 활성화 한다. 여기서는 metrics-server를 예시로 사용한다.

    minikube addons enable metrics-server
    

    다음과 유사하게 출력된다.

    The 'metrics-server' addon is enabled
    
  3. 생성한 파드와 서비스를 확인한다.

    kubectl get pod,svc -n kube-system
    

    다음과 유사하게 출력된다.

    NAME                                        READY     STATUS    RESTARTS   AGE
    pod/coredns-5644d7b6d9-mh9ll                1/1       Running   0          34m
    pod/coredns-5644d7b6d9-pqd2t                1/1       Running   0          34m
    pod/metrics-server-67fb648c5                1/1       Running   0          26s
    pod/etcd-minikube                           1/1       Running   0          34m
    pod/influxdb-grafana-b29w8                  2/2       Running   0          26s
    pod/kube-addon-manager-minikube             1/1       Running   0          34m
    pod/kube-apiserver-minikube                 1/1       Running   0          34m
    pod/kube-controller-manager-minikube        1/1       Running   0          34m
    pod/kube-proxy-rnlps                        1/1       Running   0          34m
    pod/kube-scheduler-minikube                 1/1       Running   0          34m
    pod/storage-provisioner                     1/1       Running   0          34m
    
    NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    service/metrics-server         ClusterIP   10.96.241.45    <none>        80/TCP              26s
    service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       34m
    service/monitoring-grafana     NodePort    10.99.24.54     <none>        80:30002/TCP        26s
    service/monitoring-influxdb    ClusterIP   10.111.169.94   <none>        8083/TCP,8086/TCP   26s
    
  4. metrics-server 비활성화

    minikube addons disable metrics-server
    

    다음과 유사하게 출력된다.

    metrics-server was successfully disabled
    

제거하기

이제 클러스터에서 만들어진 리소스를 제거할 수 있다.

kubectl delete service hello-node
kubectl delete deployment hello-node

필요하면 Minikube 가상 머신(VM)을 정지한다.

minikube stop

필요하면 minikube VM을 삭제한다.

minikube delete

다음 내용

5.2 - 쿠버네티스 기초 학습

쿠버네티스 기초

이 튜토리얼에서는 쿠버네티스 클러스터 오케스트레이션 시스템의 기초를 익힐 수 있는 가이드를 제공한다. 각각의 모듈에는 쿠버네티스의 주요 기능과 개념에 대한 배경 지식이 담겨 있으며 대화형 온라인 튜토리얼도 포함되어 있다. 대화형 튜토리얼에서 간단한 클러스터와 그 클러스터 상의 컨테이너화 된 애플리케이션을 직접 관리해볼 수 있다.

대화형 튜토리얼을 사용해서 다음의 내용을 배울 수 있다.

  • 컨테이너화된 애플리케이션을 클러스터에 배포하기.
  • 디플로이먼트를 스케일링하기.
  • 컨테이너화된 애플리케이션을 새로운 소프트웨어 버전으로 업데이트하기.
  • 컨테이너화된 애플리케이션을 디버그하기.

이 튜토리얼에서는 Katacoda를 사용해서 독자의 웹브라우저에서 Minikube가 동작하는 가상 터미널을 구동시킨다. Minikube는 로컬에 설치할 수 있는 작은 규모의 쿠버네티스로써 어디에서든 작동된다. 어떤 소프트웨어도 설치할 필요가 없고, 아무 것도 설정할 필요가 없다. 왜냐하면 대화형 튜토리얼이 웹브라우저 자체에서 바로 동작하기 때문이다.


쿠버네티스가 어떤 도움이 될까?

오늘날의 웹서비스에 대해서, 사용자는 애플리케이션이 24/7 가용하기를 바라고, 개발자는 하루에도 몇 번이고 새로운 버전의 애플리케이션을 배포하기를 바란다. 컨테이너화를 통해 소프트웨어를 패키지하면 애플리케이션을 다운타임 없이 릴리스 및 업데이트할 수 있게 되어서 이런 목표를 달성하는데 도움이 된다. 쿠버네티스는 이렇게 컨테이너화된 애플리케이션을 원하는 곳 어디에든 또 언제든 구동시킬 수 있다는 확신을 갖는데 도움을 주며, 그 애플리케이션이 작동하는데 필요한 자원과 도구를 찾는 것을 도와준다. 쿠버네티스는 구글의 컨테이너 오케스트레이션 부문의 축적된 경험으로 설계되고 커뮤니티로부터 도출된 최고의 아이디어가 결합된 운영 수준의 오픈 소스 플랫폼이다.


5.2.1 - 클러스터 생성하기

5.2.1.1 - Minikube를 사용해서 클러스터 생성하기

목표

  • 쿠버네티스 클러스터가 무엇인지 배운다.
  • Minikube가 무엇인지 배운다.
  • 온라인 터미널을 사용해서 쿠버네티스 클러스터를 시작한다.

쿠버네티스 클러스터

쿠버네티스는 컴퓨터들을 연결하여 단일 형상으로 동작하도록 컴퓨팅 클러스터를 구성하고 높은 가용성을 제공하도록 조율한다. 사용자는 쿠버네티스의 추상화 개념을 통해 개별 머신에 얽매이지 않고 컨테이너화된 애플리케이션을 클러스터에 배포할 수 있다. 이렇게 새로운 배포 모델을 활용하려면, 애플리케이션을 개별 호스트에 독립적인 방식으로 패키징할 필요가 있다. 즉, 컨테이너화가 필요하다. 예전 배치 모델인 설치형 애플리케이션이 특정 머신의 호스트와 밀접하게 통합되는 패키지인 것에 비해, 컨테이너화된 애플리케이션은 유연성(flexible)과 가용성(available)이 훨씬 높다. 쿠버네티스는 이러한 애플리케이션 컨테이너를 클러스터에 분산시키고 스케줄링하는 일을 더욱 효율적으로 자동화한다. 쿠버네티스는 오픈소스 플랫폼이며 운영 수준의 안정성(production-ready)을 제공한다.

쿠버네티스 클러스터는 두 가지 형태의 자원으로 구성된다.

  • 컨트롤 플레인은 클러스터를 조율한다.
  • 노드는 애플리케이션을 구동하는 작업자(worker)이다.

요약:

  • 쿠버네티스 클러스터
  • Minikube

쿠버네티스는 컴퓨터 클러스터에 애플리케이션 컨테이너의 배치(스케줄링) 및 실행을 오케스트레이션하는 운영 수준의 오픈소스 플랫폼이다.


클러스터 다이어그램


컨트롤 플레인은 클러스터 관리를 담당한다. 컨트롤 플레인은 애플리케이션을 스케줄링하거나, 애플리케이션의 항상성을 유지하거나, 애플리케이션을 스케일링하고, 새로운 변경사항을 순서대로 반영(rolling out)하는 일과 같은 클러스터 내 모든 활동을 조율한다.

노드는 쿠버네티스 클러스터 내 워커 머신으로 동작하는 VM 또는 물리적인 컴퓨터다. 각 노드는 노드를 관리하고 쿠버네티스 컨트롤 플레인과 통신하는 Kubelet이라는 에이전트를 갖는다. 노드는 컨테이너 운영을 담당하는 containerd 또는 도커와 같은 툴도 갖는다. 운영 트래픽을 처리하는 쿠버네티스 클러스터는 최소 세 대의 노드를 가져야 한다.

컨트롤 플레인은 실행 중인 애플리케이션을 호스팅하기 위해 사용되는 노드와 클러스터를 관리한다.

애플리케이션을 쿠버네티스에 배포하기 위해서는, 컨트롤 플레인에 애플리케이션 컨테이너의 구동을 지시하면 된다. 그러면 컨트롤 플레인은 컨테이너를 클러스터의 어느 노드에 구동시킬지 스케줄한다. 노드는 컨트롤 플레인이 제공하는 쿠버네티스 API를 통해서 컨트롤 플레인과 통신한다. 최종 사용자도 쿠버네티스 API를 사용해서 클러스터와 직접 상호작용(interact)할 수 있다.

쿠버네티스 클러스터는 물리 및 가상 머신 모두에 설치될 수 있다. 쿠버네티스 개발을 시작하려면 Minikube를 사용할 수 있다. Minikube는 가벼운 쿠버네티스 구현체이며, 로컬 머신에 VM을 만들고 하나의 노드로 구성된 간단한 클러스터를 생성한다. Minikube는 리눅스, 맥, 그리고 윈도우 시스템에서 구동이 가능하다. Minikube CLI는 클러스터에 대해 시작, 중지, 상태 조회 및 삭제 등의 기본적인 부트스트래핑(bootstrapping) 기능을 제공한다. 하지만, 본 튜토리얼에서는 Minikube가 미리 설치된 채로 제공되는 온라인 터미널을 사용할 것이다.

쿠버네티스가 무엇인지 알아봤으니, 이제 온라인 튜토리얼로 이동해서 우리의 첫 번째 클러스터를 시작해보자!


5.2.1.2 - 대화형 튜토리얼 - 클러스터 생성하기

화면이 너무 좁아 터미널과 상호작용할 수 없습니다. 데스크톱/태블릿을 사용해주세요.

5.2.2 - 앱 배포하기

5.2.2.1 - kubectl을 사용해서 디플로이먼트 생성하기

목표

  • 애플리케이션 디플로이먼트(Deployment)에 대해 배운다.
  • kubectl로 첫 애플리케이션을 쿠버네티스에 배포한다.

쿠버네티스 디플로이먼트

일단 쿠버네티스 클러스터를 구동시키면, 그 위에 컨테이너화된 애플리케이션을 배포할 수 있다. 그러기 위해서, 쿠버네티스 디플로이먼트 설정을 만들어야 한다. 디플로이먼트는 쿠버네티스가 애플리케이션의 인스턴스를 어떻게 생성하고 업데이트해야 하는지를 지시한다. 디플로이먼트가 만들어지면, 쿠버네티스 컨트롤 플레인이 해당 디플로이먼트에 포함된 애플리케이션 인스턴스가 클러스터의 개별 노드에서 실행되도록 스케줄한다.

애플리케이션 인스턴스가 생성되면, 쿠버네티스 디플로이먼트 컨트롤러는 지속적으로 이들 인스턴스를 모니터링한다. 인스턴스를 구동 중인 노드가 다운되거나 삭제되면, 디플로이먼트 컨트롤러가 인스턴스를 클러스터 내부의 다른 노드의 인스턴스로 교체시켜준다.이렇게 머신의 장애나 정비에 대응할 수 있는 자동 복구(self-healing) 메커니즘을 제공한다.

오케스트레이션 기능이 없던 환경에서는, 설치 스크립트가 애플리케이션을 시작하는데 종종 사용되곤 했지만, 머신의 장애가 발생한 경우 복구를 해주지는 않았다. 쿠버네티스 디플로이먼트는 애플리케이션 인스턴스를 생성해주고 여러 노드에 걸쳐서 지속적으로 인스턴스가 구동되도록 하는 두 가지를 모두 하기 때문에 애플리케이션 관리를 위한 접근법에서 근본적인 차이를 가져다준다.

요약:

  • 디플로이먼트
  • Kubectl

디플로이먼트는 애플리케이션 인스턴스를 생성하고 업데이트하는 역할을 담당한다.


쿠버네티스에 첫 번째 애플리케이션 배포하기


Kubectl이라는 쿠버네티스 CLI를 통해 디플로이먼트를 생성하고 관리할 수 있다. Kubectl은 클러스터와 상호 작용하기 위해 쿠버네티스 API를 사용한다. 이 모듈에서는, 쿠버네티스 클러스터 상에 애플리케이션을 구동시키는 디플로이먼트를 생성하기 위해 필요한 가장 일반적인 Kubectl 명령어를 배우게 된다.

디플로이먼트를 생성할 때, 애플리케이션에 대한 컨테이너 이미지와 구동시키고자 하는 복제 수를 지정해야 한다. 디플로이먼트를 업데이트해서 이런 정보를 나중에 변경할 수 있다. 모듈 56의 부트캠프에서 어떻게 스케일하고 업데이트하는지에 대해 다룬다.

애플리케이션이 쿠버네티스 상에 배포되려면 지원되는 컨테이너 형식 중 하나로 패키지 되어야한다.

첫 번째 디플로이먼트로, NGINX를 사용해 모든 요청을 에코(echo)하는 도커 컨테이너로 패키지한 hello-node 애플리케이션을 사용해보자. (아직 hello-node 애플리케이션을 작성하고 컨테이너를 활용해서 배포해보지 않았다면, Hello Minikube 튜토리얼의 지시를 따른다.)

이제 디플로이먼트를 이해했으니, 온라인 튜토리얼을 통해 우리의 첫 번째 애플리케이션을 배포해보자!


5.2.2.2 - 대화형 튜토리얼 - 앱 배포하기

파드는 쿠버네티스 애플리케이션의 기본 실행 단위이다. 각 파드는 클러스터에서 실행중인 워크로드의 일부를 나타낸다. 파드에 대해 더 자세히 알아본다.


터미널로 상호 작용하기 위해서, 데스크탑/태블릿 버전을 사용해주세요

5.2.3 - 앱 조사하기

5.2.3.1 - 파드와 노드 보기

목표

  • 쿠버네티스 파드에 대해 배운다.
  • 쿠버네티스 노드에 대해 배운다.
  • 배포된 애플리케이션의 문제를 해결한다.

쿠버네티스 파드

모듈 2에서 배포를 생성했을 때, 쿠버네티스는 여러분의 애플리케이션 인스턴스에 파드를 생성했다. 파드는 하나 또는 그 이상의 애플리케이션 컨테이너 (도커와 같은)들의 그룹을 나타내는 쿠버네티스의 추상적 개념으로 일부는 컨테이너에 대한 자원을 공유한다. 그 자원은 다음을 포함한다:

  • 볼륨과 같은, 공유 스토리지
  • 클러스터 IP 주소와 같은, 네트워킹
  • 컨테이너 이미지 버전 또는 사용할 특정 포트와 같이, 각 컨테이너가 동작하는 방식에 대한 정보

파드는 특유한 "로컬호스트" 애플리케이션 모형을 만들어. 상대적으로 밀접하게 결합되어진 상이한 애플리케이션 컨테이너들을 수용할 수 있다. 가령, 파드는 Node.js 앱과 더불어 Node.js 웹서버에 의해 발행되는 데이터를 공급하는 상이한 컨테이너를 함께 수용할 수 있다. 파드 내 컨테이너는 IP 주소, 그리고 포트 스페이스를 공유하고 항상 함께 위치하고 함께 스케쥴링 되고 동일 노드 상의 컨텍스트를 공유하면서 동작한다.

파드는 쿠버네티스 플랫폼 상에서 최소 단위가 된다. 우리가 쿠버네티스에서 배포를 생성할 때, 그 배포는 컨테이너 내부에서 컨테이너와 함께 파드를 생성한다. 각 파드는 스케쥴 되어진 노드에게 묶여지게 된다. 그리고 (재구동 정책에 따라) 소멸되거나 삭제되기 전까지 그 노드에 유지된다. 노드에 실패가 발생할 경우, 클러스터 내에 가용한 다른 노드들을 대상으로 스케쥴되어진다.

요약:

  • 파드
  • 노드
  • Kubectl 주요 명령어

파드는 하나 또는 그 이상의 애플리케이션 컨테이너 (도커와 같은)들의 그룹이고 공유 스토리지 (볼륨), IP 주소 그리고 그것을 동작시키는 방식에 대한 정보를 포함하고 있다.


파드 개요


노드

파드는 언제나 노드 상에서 동작한다. 노드는 쿠버네티스에서 워커 머신을 말하며 클러스터에 따라 가상 또는 물리 머신일 수 있다. 각 노드는 컨트롤 플레인에 의해 관리된다. 하나의 노드는 여러 개의 파드를 가질 수 있고, 쿠버네티스 컨트롤 플레인은 클러스터 내 노드를 통해서 파드에 대한 스케쥴링을 자동으로 처리한다. 컨트롤 플레인의 자동 스케줄링은 각 노드의 사용 가능한 리소스를 모두 고려합니다.

모든 쿠버네티스 노드는 최소한 다음과 같이 동작한다.

  • Kubelet은, 쿠버네티스 컨트롤 플레인과 노드 간 통신을 책임지는 프로세스이며, 하나의 머신 상에서 동작하는 파드와 컨테이너를 관리한다.
  • 컨테이너 런타임(도커와 같은)은 레지스트리에서 컨테이너 이미지를 가져와 묶여 있는 것을 풀고 애플리케이션을 동작시키는 책임을 맡는다.

만약 컨테이너들이 밀접하고 결합되어 있고 디스크와 같은 자원을 공유해야 한다면 오직 하나의 단일 파드에 함께 스케쥴되어져야 한다.


노드 개요


kubectl로 문제해결하기

모듈 2에서, Kubectl 커맨드-라인 인터페이스를 사용했다. 배포된 애플리케이션과 그 환경에 대한 정보를 얻기 위해 모듈3에서도 계속 그것을 사용할 것이다. 가장 보편적인 운용업무는 다음 kubectl 명령어를 이용하여 처리할 수 있다:

  • kubectl get - 자원을 나열한다
  • kubectl describe - 자원에 대해 상세한 정보를 보여준다.
  • kubectl logs - 파드 내 컨테이너의 로그들을 출력한다
  • kubectl exec - 파드 내 컨테이너에 대한 명령을 실행한다.

언제 애플리케이션이 배포되었으며, 현재 상태가 어떠한지, 그것의 구성은 어떠한지 등을 보기 위해 이러한 명령을 이용할 수 있다.

이제 클러스터 컴포넌트와 커맨드 라인에 대해 알아 보았으니, 애플리케이션을 조사해 보자.

노드는 쿠버네티스에 있어서 워커 머신이며 클러스터에 따라 VM 또는 물리 머신이 될 수 있다. 여러 개의 파드는 하나의 노드 위에서 동작할 수 있다.


5.2.3.2 - 대화형 튜토리얼 - 앱 조사하기


터미널과 상호작용하기 위해, 데스크탑/태블릿 버전을 이용한다.

5.2.4 - 앱 외부로 노출하기

5.2.4.1 - 앱 노출을 위해 서비스 이용하기

목표

  • 쿠버네티스의 서비스에 대해 배운다.
  • 레이블과 레이블셀랙터 오브젝트가 어떻게 서비스와 연관되는지 이해한다.
  • 서비스를 이용하여 쿠버네티스 클러스터 외부로 애플리케이션을 노출한다.

쿠버네티스 서비스들에 대한 개요

쿠버네티스 파드들 은 언젠가는 죽게된다. 실제 파드들은 생명주기를 갖는다. 워커 노드가 죽으면, 노드 상에서 동작하는 파드들 또한 종료된다. 레플리카셋(ReplicaSet)은 여러분의 애플리케이션이 지속적으로 동작할 수 있도록 새로운 파드들의 생성을 통해 동적으로 클러스터를 미리 지정해 둔 상태로 되돌려 줄 수도 있다. 또 다른 예시로서, 3개의 복제본을 갖는 이미지 처리용 백엔드를 고려해 보자. 그 복제본들은 교체 가능한 상태이다. 그래서 프론트엔드 시스템은 하나의 파드가 소멸되어 재생성이 되더라도, 백엔드 복제본들에 의한 영향을 받아서는 안된다. 즉, 동일 노드 상의 파드들이라 할지라도, 쿠버네티스 클러스터 내 각 파드는 유일한 IP 주소를 가지며, 여러분의 애플리케이션들이 지속적으로 기능할 수 있도록 파드들 속에서 발생하는 변화에 대해 자동으로 조정해 줄 방법이 있어야 한다.

쿠버네티스에서 서비스는 하나의 논리적인 파드 셋과 그 파드들에 접근할 수 있는 정책을 정의하는 추상적 개념이다. 서비스는 종속적인 파드들 사이를 느슨하게 결합되도록 해준다. 서비스는 모든 쿠버네티스 오브젝트들과 같이 YAML (보다 선호하는) 또는 JSON을 이용하여 정의된다. 서비스가 대상으로 하는 파드 셋은 보통 LabelSelector에 의해 결정된다 (여러분이 왜 스펙에 selector가 포함되지 않은 서비스를 필요로 하게 될 수도 있는지에 대해 아래에서 확인해 보자).

비록 각 파드들이 고유의 IP를 갖고 있기는 하지만, 그 IP들은 서비스의 도움없이 클러스터 외부로 노출되어질 수 없다. 서비스들은 여러분의 애플리케이션들에게 트래픽이 실릴 수 있도록 허용해준다. 서비스들은 ServiceSpec에서 type을 지정함으로써 다양한 방식들로 노출시킬 수 있다:

  • ClusterIP (기본값) - 클러스터 내에서 내부 IP 에 대해 서비스를 노출해준다. 이 방식은 오직 클러스터 내에서만 서비스가 접근될 수 있도록 해준다.
  • NodePort - NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출시켜준다. <NodeIP>:<NodePort>를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해준다. ClusterIP의 상위 집합이다.
  • LoadBalancer - (지원 가능한 경우) 기존 클라우드에서 외부용 로드밸런서를 생성하고 서비스에 고정된 공인 IP를 할당해준다. NodePort의 상위 집합이다.
  • ExternalName - CNAME 레코드 및 값을 반환함으로써 서비스를 externalName 필드의 내용(예를 들면, foo.bar.example.com)에 매핑한다. 어떠한 종류의 프록시도 설정되지 않는다. 이 방식은 kube-dns v1.7 이상 또는 CoreDNS 버전 0.0.8 이상을 필요로 한다.

다른 서비스 타입들에 대한 추가 정보는 소스 IP 이용하기 튜토리얼에서 확인 가능하다. 또한 서비스들로 애플리케이션에 접속하기도 참고해 보자.

부가적으로, spec에 selector를 정의하지 않고 말아넣은 서비스들의 몇 가지 유즈케이스들이 있음을 주의하자. selector 없이 생성된 서비스는 상응하는 엔드포인트 오브젝트들 또한 생성하지 않는다. 이로써 사용자들로 하여금 하나의 서비스를 특정한 엔드포인트에 매핑 시킬수 있도록 해준다. selector를 생략하게 되는 또 다른 가능성은 여러분이 type: ExternalName을 이용하겠다고 확고하게 의도하는 경우이다.

요약

  • 파드들을 외부 트래픽에 노출하기
  • 여러 파드에 걸쳐서 트래픽 로드밸런싱 하기
  • 레이블 사용하기

쿠버네티스 서비스는 논리적 파드 셋을 정의하고 외부 트래픽 노출, 로드밸런싱 그리고 그 파드들에 대한 서비스 디스커버리를 가능하게 해주는 추상 계층이다.


서비스와 레이블

서비스는 파드 셋에 걸쳐서 트래픽을 라우트한다. 여러분의 애플리케이션에 영향을 주지 않으면서 쿠버네티스에서 파드들이 죽게도 하고, 복제가 되게도 해주는 추상적 개념이다. 종속적인 파드들 사이에서의 디스커버리와 라우팅은 (하나의 애플리케이션에서 프로트엔드와 백엔드 컴포넌트와 같은) 쿠버네티스 서비스들에 의해 처리된다.

서비스는 쿠버네티스의 객체들에 대해 논리 연산을 허용해주는 기본 그룹핑 단위인, 레이블과 셀렉터를 이용하여 파드 셋과 매치시킨다. 레이블은 오브젝트들에 붙여진 키/밸류 쌍으로 다양한 방식으로 이용 가능하다:

  • 개발, 테스트, 그리고 상용환경에 대한 객체들의 지정
  • 임베디드된 버전 태그들
  • 태그들을 이용하는 객체들에 대한 분류


레이블은 오브젝트의 생성 시점 또는 이후 시점에 붙여질 수 있다. 언제든지 수정이 가능하다. 이제 서비스를 이용하여 우리의 애플리케이션을 노출도 시켜보고 레이블도 적용해 보자.


5.2.4.2 - 대화형 튜토리얼 - 앱 노출하기

터미널과 상호작용하기 위해, 데스크탑/태블릿 버전을 이용한다.

5.2.5 - 앱 스케일링하기

5.2.5.1 - 복수의 앱 인스턴스를 구동하기

목표

  • kubectl을 사용해서 애플리케이션을 스케일한다.

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

지난 모듈에서 디플로이먼트를 만들고, 서비스를 통해서 디플로이먼트를 외부에 노출시켜 봤다. 해당 디플로이먼트는 애플리케이션을 구동하기 위해 단 하나의 파드만을 생성했었다. 트래픽이 증가하면, 사용자 요청에 맞추어 애플리케이션의 규모를 조정할 필요가 있다.

디플로이먼트의 복제 수를 변경하면 스케일링이 수행된다

요약:

  • 디플로이먼트 스케일링하기

kubectl create deployment 명령에 --replicas 파라미터를 사용해서 처음부터 복수의 인스턴스로 구동되는 디플로이먼트를 만들 수도 있다


스케일링 개요


디플로이먼트를 스케일 아웃하면 신규 파드가 생성되어서 가용한 자원이 있는 노드에 스케줄된다. 스케일링 기능은 새로 의도한 상태(desired state)까지 파드의 수를 늘린다. 쿠버네티스는 파드의 오토스케일링 도 지원하지만 본 튜토리얼에서는 다루지 않는다. 0까지 스케일링하는 것도 가능하다. 이 경우 해당 디플로이먼트의 모든 파드가 종료된다.

애플리케이션의 인스턴스를 복수로 구동하게 되면 트래픽을 해당 인스턴스 모두에 분산시킬 방법이 필요해진다. 서비스는 노출된 디플로이먼트의 모든 파드에 네트워크 트래픽을 분산시켜줄 통합된 로드밸런서를 갖는다. 서비스는 엔드포인트를 이용해서 구동중인 파드를 지속적으로 모니터링함으로써 가용한 파드에만 트래픽이 전달되도록 해준다.

디플로이먼트의 복제 수를 변경하면 스케일링이 수행된다.


일단 복수의 애플리케이션의 인스턴스가 구동 중이면, 다운타임 없이 롤링 업데이트를 할 수 있다. 다음 모듈에서 이 내용을 다루도록 하겠다. 이제 온라인 터미널로 가서 애플리케이션을 스케일해보자.


5.2.5.2 - 대화형 튜토리얼 - 앱 스케일링하기

터미널로 상호 작용하기 위해서, 데스크탑/태블릿 버전을 사용해주세요

5.2.6 - 앱 업데이트하기

5.2.6.1 - 롤링 업데이트 수행하기

목표

  • kubectl을 이용하여 롤링 업데이트 수행하기

애플리케이션 업데이트하기

사용자들은 애플리케이션이 항상 가용한 상태일 것이라 여기고 개발자들은 하루에 여러번씩 새로운 버전을 배포하도록 요구 받고있다. 쿠버네티스에서는 이것을 롤링 업데이트를 통해 이루고 있다. 롤링 업데이트는 파드 인스턴스를 점진적으로 새로운 것으로 업데이트하여 디플로이먼트 업데이트가 서비스 중단 없이 이루어질 수 있도록 해준다. 새로운 파드는 가용한 자원을 보유한 노드로 스케줄될 것이다.

이전 모듈에서 여러 개의 인스턴스를 동작시키도록 애플리케이션을 스케일했다. 이것은 애플리케이션의 가용성에 영향을 미치지 않으면서 업데이트를 수행하는 것에 대한 요구이다. 기본적으로, 업데이트가 이루어지는 동안 이용 불가한 파드의 최대 개수와 생성 가능한 새로운 파드의 최대 개수는 하나다. 두 옵션은 (파드에 대한) 개수 또는 백분율로 구성될 수 있다. 쿠버네티스에서, 업데이트는 버전으로 관리되고 어떠한 디플로이먼트 업데이트라도 이전의 (안정적인) 버전으로 원복이 가능하다.

요약:

  • 앱 업데이트하기

롤링 업데이트는 파드 인스턴스를 점진적으로 새로운 것으로 업데이트하여 디플로이먼트 업데이트가 서비스 중단 없이 이루어질 수 있도록 해준다.


롤링 업데이트 개요


애플리케이션 스케일링과 유사하게, 디플로이먼트가 외부로 노출되면, 서비스는 업데이트가 이루어지는 동안 오직 가용한 파드에게만 트래픽을 로드밸런스 할 것이다. 가용한 파드란 애플리케이션의 사용자들에게 가용한 상태의 인스턴스를 말한다.

롤링 업데이트는 다음 동작들을 허용해준다:

  • 하나의 환경에서 또 다른 환경으로의 애플리케이션 프로모션 (컨테이너 이미지 업데이트를 통해)
  • 이전 버전으로의 롤백
  • 서비스 중단 없는 애플리케이션의 지속적인 통합과 지속적인 전달

디플로이먼트가 외부로 노출되면, 서비스는 업데이트가 이루어지는 동안 오직 가용한 파드에게만 트래픽을 로드밸런스 할 것이다.


다음 대화형 튜토리얼에서, 새로운 버전으로 애플리케이션을 업데이트하고, 롤백 또한 수행해 볼 것이다.


5.2.6.2 - 대화형 튜토리얼 - 앱 업데이트 하기

터미널과 상호작용하기 위해, 데스크탑/태블릿 버전을 이용한다.

5.3 - 설정

5.3.1 - 예제: Java 마이크로서비스 구성하기

5.3.1.1 - MicroProfile, 컨피그맵(ConfigMaps) 및 시크릿(Secrets)을 사용하여 구성 외부화(externalizing)

이 튜토리얼에서는 마이크로서비스의 구성을 외부화하는 방법과 이유를 알아본다. 특히, 쿠버네티스 컨피그맵과 시크릿을 사용하여 환경 변수를 설정한 다음 MicroProfile Config를 이용한 사용 방법을 배운다.

시작하기 전에

쿠버네티스 컨피그맵 및 시크릿 생성하기

쿠버네티스에서 도커 컨테이너에 대한 환경 변수를 설정하는 방법에는 Dockerfile, kubernetes.yml, 쿠버네티스 컨피그맵 및 쿠버네티스 시크릿이 있다. 이 튜토리얼에서는 사용자의 마이크로서비스에 값을 주입하기 위한 환경 변수를 설정하기 위해 후자의 두 가지를 사용하는 방법에 대해 배운다. 컨피그맵 및 시크릿을 사용할 때의 이점 중 하나는 여러 다른 컨테이너에 대해 서로 다른 환경 변수에 할당되는 것을 포함하여, 여러 컨테이너에서 다시 사용할 수 있다는 것이다.

컨피그맵은 기밀이 아닌 키-값 쌍을 저장하는 API 오브젝트이다. 대화형 튜토리얼에서는 컨피그맵을 사용하여 애플리케이션의 이름을 저장하는 방법을 배운다. 컨피그맵에 대한 자세한 정보는 여기에서 문서를 찾을 수 있다.

시크릿은 키-값 쌍을 저장하는 데도 사용되지만, 기밀/민감한 정보를 위한 것이며 Base64 인코딩을 사용하여 저장된다는 점에서 컨피그맵과 다르다. 따라서 시크릿은 자격 증명, 키 및 토큰과 같은 항목을 저장하는 데 적합한 선택이 된다. 이 내용은 대화형 튜토리얼에서 수행할 것이다. 시크릿에 대한 자세한 내용은 여기에서 문서를 찾을 수 있다.

코드로부터 구성 외부화

구성은 일반적으로 환경에 따라 변경되기 때문에, 외부화된 애플리케이션 구성(externalized application configuration)은 유용하다. 이를 이루기 위해, Java의 CDI(콘텍스트와 의존성 주입) 및 MicroProfile Config를 사용한다. MicroProfile Config는 클라우드 네이티브 마이크로서비스를 개발하고 배포하기 위한 개방형 Java 기술 세트인 MicroProfile의 기능이다.

CDI는 느슨하게 결합된 협업 빈(beans)을 통해 애플리케이션을 어셈블할 수 있는 표준 종속성 주입(standard dependency injection) 기능을 제공한다. MicroProfile Config는 애플리케이션, 런타임 및 환경을 포함한 다양한 소스에서 구성 속성을 가져오는 표준 방법을 앱과 마이크로서비스에 제공한다. 소스의 정의된 우선순위에 따라 속성은 애플리케이션이 API를 통해 접근할 수 있는 단일 속성 집합으로 자동 결합된다. 대화형 튜토리얼에서는 CDI와 MicroProfile을 함께 사용하여 쿠버네티스 컨피그맵 및 시크릿을 통한 외부 제공 속성을 검색하고 애플리케이션 코드에 삽입한다.

많은 오픈 소스 프레임워크와 런타임이 MicroProfile Config를 구현하고 지원한다. 대화형 튜토리얼을 통해 클라우드 네이티브 앱과 마이크로서비스를 빌드하고 실행하기 위한 유연한 오픈 소스 Java 런타임인 ​​Open Liberty를 사용하게 될 것이다. 그러나, 모든 MicroProfile 호환 런타임을 대신 사용할 수 있다.

목적

  • 쿠버네티스 컨피그맵 및 시크릿 생성
  • MicroProfile Config를 사용하여 마이크로서비스 구성 주입

예제: MicroProfile, 컨피그맵 및 시크릿를 사용하여 구성 외부화

대화형 튜토리얼 시작

5.3.1.2 - 대화형 튜토리얼 - Java 마이크로서비스 구성하기

터미널 화면을 조작하려면, 데스크톱/태블릿 버전을 사용하기 바란다.

5.3.2 - 컨피그맵을 사용해서 Redis 설정하기

이 페이지에서는 컨피그맵(ConfigMap)을 사용해서 Redis를 설정하는 방법에 대한 실세계 예제를 제공하고, 컨피그맵을 사용해서 컨테이너 설정하기 태스크로 빌드를 한다.

목적

  • Redis 설정값으로 컨피그맵을 생성한다.
  • 생성된 컨피그맵을 마운트하고 사용하는 Redis 파드를 생성한다.
  • 설정이 잘 적용되었는지 확인한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

실세상 예제: 컨피그맵을 사용해서 Redis 설정하기

아래 단계를 통해서, 컨피그맵에 저장된 데이터를 사용하는 Redis 캐시를 설정한다.

우선, 비어 있는 설정으로 컨피그맵을 생성한다.

cat <<EOF >./example-redis-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: ""
EOF

위에서 생성한 컨피그맵을 Redis 파드 매니페스트와 함께 적용한다.

kubectl apply -f example-redis-config.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml

Redis 파드 매니페스트의 내용을 검토하고 다음의 사항을 염두에 둔다.

  • config 라는 이름의 볼륨은 spec.volumes[1] 에 의해서 생성된다.
  • spec.volumes[1].items[0] 내부의 keypathconfig 볼륨에 redis.conf 라는 파일명으로 지정된 example-redis-config 컨피그맵의 redis-config 키를 노출시킨다.
  • 그리고 config 볼륨은 spec.containers[0].volumeMounts[1] 에 의해서 /redis-master 에 마운트된다.

이 내용은 위의 example-redis-config 컨피그맵의 data.redis-config 내부 데이터를 파드 안에 있는 /redis-master/redis.conf 파일의 내용으로 노출시키는 순효과(net effect)를 낸다.

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis:5.0.4
    command:
      - redis-server
      - "/redis-master/redis.conf"
    env:
    - name: MASTER
      value: "true"
    ports:
    - containerPort: 6379
    resources:
      limits:
        cpu: "0.1"
    volumeMounts:
    - mountPath: /redis-master-data
      name: data
    - mountPath: /redis-master
      name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap:
        name: example-redis-config
        items:
        - key: redis-config
          path: redis.conf

생성된 오브젝트를 확인한다.

kubectl get pod/redis configmap/example-redis-config

다음의 결과를 볼 수 있다.

NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          8s

NAME                             DATA   AGE
configmap/example-redis-config   1      14s

example-redis-config 컨피그맵의 redis-config 키를 공란으로 둔 것을 기억하자.

kubectl describe configmap/example-redis-config

redis-config 키가 비어 있는 것을 확인할 수 있다.

Name:         example-redis-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis-config:

kubectl exec 를 사용하여 파드에 접속하고, 현재 설정 확인을 위해서 redis-cli 도구를 실행한다.

kubectl exec -it redis -- redis-cli

maxmemory 를 확인한다.

127.0.0.1:6379> CONFIG GET maxmemory

기본값인 0을 볼 수 있을 것이다.

1) "maxmemory"
2) "0"

유사하게, maxmemory-policy 를 확인한다.

127.0.0.1:6379> CONFIG GET maxmemory-policy

이것도 기본값인 noeviction 을 보여줄 것이다.

1) "maxmemory-policy"
2) "noeviction"

이제 example-redis-config 컨피그맵에 몇 가지 설정값을 추가해 본다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: |
    maxmemory 2mb
    maxmemory-policy allkeys-lru    

갱신된 컨피그맵을 적용한다.

kubectl apply -f example-redis-config.yaml

컨피그맵이 갱신된 것을 확인한다.

kubectl describe configmap/example-redis-config

방금 추가한 설정값을 확인할 수 있을 것이다.

Name:         example-redis-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis-config:
----
maxmemory 2mb
maxmemory-policy allkeys-lru

설정이 적용되었는지 확인하려면, kubectl exec 를 통한 redis-cli 로 Redis 파드를 다시 확인한다.

kubectl exec -it redis -- redis-cli

maxmemory 를 확인한다.

127.0.0.1:6379> CONFIG GET maxmemory

기본값인 0을 볼 수 있을 것이다.

1) "maxmemory"
2) "0"

유사하게, maxmemory-policy 도 기본 설정인 noeviction 을 보여줄 것이다.

127.0.0.1:6379> CONFIG GET maxmemory-policy

위의 명령은 다음을 반환한다.

1) "maxmemory-policy"
2) "noeviction"

파드는 연관된 컨피그맵에서 갱신된 값을 인지하기 위해서 재시작이 필요하므로 해당 설정값이 변경되지 않은 상태이다. 파드를 삭제하고 다시 생성한다.

kubectl delete pod redis
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml

이제 마지막으로 설정값을 다시 확인해 본다.

kubectl exec -it redis -- redis-cli

maxmemory 를 확인한다.

127.0.0.1:6379> CONFIG GET maxmemory

이것은 이제 갱신된 값인 2097152를 반환한다.

1) "maxmemory"
2) "2097152"

유사하게, maxmemory-policy 도 갱신되어 있다.

127.0.0.1:6379> CONFIG GET maxmemory-policy

이것은 원하는 값인 allkeys-lru 를 반환한다.

1) "maxmemory-policy"
2) "allkeys-lru"

생성된 자원을 삭제하여 작업을 정리한다.

kubectl delete pod/redis configmap/example-redis-config

다음 내용

5.4 - 상태 유지를 하지 않는 애플리케이션

5.4.1 - 외부 IP 주소를 노출하여 클러스터의 애플리케이션에 접속하기

이 페이지에서는 외부 IP 주소를 노출하는 쿠버네티스 서비스 오브젝트를 생성하는 방법에 대해 설명한다.

시작하기 전에

  • kubectl을 설치한다.
  • Google Kubernetes Engine 또는 Amazon Web Services와 같은 클라우드 공급자를 사용하여 쿠버네티스 클러스터를 생성한다. 이 튜토리얼은 외부 로드 밸런서를 생성하는데, 클라우드 공급자가 필요하다.
  • kubectl이 쿠버네티스 API 서버와 통신하도록 설정한다. 자세한 내용은 클라우드 공급자의 설명을 참고한다.

목적

  • Hello World 애플리케이션을 다섯 개의 인스턴스로 실행한다.
  • 외부 IP 주소를 노출하는 서비스를 생성한다.
  • 실행 중인 애플리케이션에 접근하기 위해 서비스 오브젝트를 사용한다.

다섯 개의 파드에서 실행되는 애플리케이션에 대한 서비스 만들기

  1. 클러스터에서 Hello World 애플리케이션을 실행한다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app.kubernetes.io/name: load-balancer-example
      name: hello-world
    spec:
      replicas: 5
      selector:
        matchLabels:
          app.kubernetes.io/name: load-balancer-example
      template:
        metadata:
          labels:
            app.kubernetes.io/name: load-balancer-example
        spec:
          containers:
          - image: gcr.io/google-samples/node-hello:1.0
            name: hello-world
            ports:
            - containerPort: 8080
    
    kubectl apply -f https://k8s.io/examples/service/load-balancer-example.yaml
    

    위의 명령어는 디플로이먼트(Deployment) 오브젝트와 관련된 레플리카셋(ReplicaSet) 오브젝트를 생성한다. 레플리카셋은 다섯 개의 파드가 있으며, 각 파드는 Hello World 애플리케이션을 실행한다.

  2. 디플로이먼트에 대한 정보를 확인한다.

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  3. 레플리카셋 오브젝트에 대한 정보를 확인한다.

    kubectl get replicasets
    kubectl describe replicasets
    
  4. 디플로이먼트를 외부로 노출시키는 서비스 오브젝트를 생성한다.

    kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
    
  5. 서비스에 대한 정보를 확인한다.

    kubectl get services my-service
    

    결과는 아래와 같은 형태로 나타난다.

    NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
    my-service   LoadBalancer   10.3.245.137   104.198.205.71   8080/TCP   54s
    
  6. 서비스에 대한 자세한 정보를 확인한다.

    kubectl describe services my-service
    

    출력 결과는 다음과 유사하다.

    Name:           my-service
    Namespace:      default
    Labels:         app.kubernetes.io/name=load-balancer-example
    Annotations:    <none>
    Selector:       app.kubernetes.io/name=load-balancer-example
    Type:           LoadBalancer
    IP:             10.3.245.137
    LoadBalancer Ingress:   104.198.205.71
    Port:           <unset> 8080/TCP
    NodePort:       <unset> 32377/TCP
    Endpoints:      10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2 more...
    Session Affinity:   None
    Events:         <none>
    

    서비스에 의해 노출된 외부 IP 주소 (LoadBalancer Ingress)를 기억해두자. 예시에서 외부 IP 주소는 104.198.205.71이다. 그리고 PortNodePort의 값을 기억해두자. 예시에서 Port는 8080이고 NodePort는 32377이다.

  7. 위의 출력 결과를 통해, 서비스에 여러 엔드포인트가 있음을 알 수 있다. 10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2. 이 주소는 Hello World 애플리케이션을 실행 중인 파드의 내부 주소다. 해당 주소가 파드 주소인지 확인하려면, 아래 명령어를 입력하면 된다.

    kubectl get pods --output=wide
    

    출력 결과는 다음과 유사하다.

    NAME                         ...  IP         NODE
    hello-world-2895499144-1jaz9 ...  10.0.1.6   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-2e5uh ...  10.0.1.8   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-9m4h1 ...  10.0.0.6   gke-cluster-1-default-pool-e0b8d269-5v7a
    hello-world-2895499144-o4z13 ...  10.0.1.7   gke-cluster-1-default-pool-e0b8d269-1afc
    hello-world-2895499144-segjf ...  10.0.2.5   gke-cluster-1-default-pool-e0b8d269-cpuc
    
  8. Hello World 애플리케이션에 접근하기 위해 외부 IP 주소 (LoadBalancer Ingress)를 사용한다.

    curl http://<external-ip>:<port>
    

    <external-ip>는 서비스의 외부 IP 주소 (LoadBalancer Ingress)를 의미하며, <port>는 서비스 정보에서 Port 값을 의미한다. 만약 minikube를 사용하고 있다면, minikube service my-service 명령어를 통해, 자동으로 브라우저 내에서 Hello World 애플리케이션에 접근할 수 있다.

    성공적인 요청에 대한 응답으로 hello 메세지가 나타난다.

    Hello Kubernetes!
    

정리하기

서비스를 삭제하려면, 아래의 명령어를 입력한다.

kubectl delete services my-service

Hello World 애플리케이션을 실행 중인 디플로이먼트, 레플리카셋, 파드를 삭제하려면, 아래의 명령어를 입력한다.

kubectl delete deployment hello-world

다음 내용

애플리케이션과 서비스 연결하기에 대해 더 배워 본다.

5.4.2 - 예시: Redis를 사용한 PHP 방명록 애플리케이션 배포하기

이 튜토리얼에서는 쿠버네티스와 Docker를 사용하여 간단한 (운영 수준이 아닌) 멀티 티어 웹 애플리케이션을 빌드하고 배포하는 방법을 보여준다. 이 예제는 다음과 같은 구성으로 이루어져 있다.

  • 방명록 항목을 저장하기 위한 단일 인스턴스 Redis
  • 여러 개의 웹 프론트엔드 인스턴스

목적

  • Redis 리더를 실행
  • 2개의 Redis 팔로워를 실행
  • 방명록 프론트엔드를 실행
  • 프론트엔드 서비스를 노출하고 확인
  • 정리하기

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.14. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

Redis 데이터베이스를 실행

방명록 애플리케이션은 Redis를 사용하여 데이터를 저장한다.

Redis 디플로이먼트를 생성하기

아래의 매니페스트 파일은 단일 복제본 Redis 파드를 실행하는 디플로이먼트 컨트롤러에 대한 명세를 담고 있다.

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: leader
        tier: backend
    spec:
      containers:
      - name: leader
        image: "docker.io/redis:6.0.5"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
  1. 매니페스트 파일을 다운로드한 디렉터리에서 터미널 창을 시작한다.

  2. redis-leader-deployment.yaml 파일을 이용하여 Redis 디플로이먼트를 생성한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
    
  3. 파드의 목록을 질의하여 Redis 파드가 실행 중인지 확인한다.

    kubectl get pods
    

    결과는 아래와 같은 형태로 나타난다.

    NAME                            READY     STATUS    RESTARTS   AGE
    redis-leader-fb76b4755-xjr2n   1/1     Running   0          13s
    
  4. Redis 리더 파드의 로그를 보려면 다음 명령어를 실행한다.

    kubectl logs -f deployment/redis-leader
    

Redis 리더 서비스 생성하기

방명록 애플리케이션에서 데이터를 쓰려면 Redis와 통신해야 한다. Redis 파드로 트래픽을 프록시하려면 서비스를 생성해야 한다. 서비스는 파드에 접근하기 위한 정책을 정의한다.

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: redis-leader
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: leader
    tier: backend
  1. redis-leader-service.yaml 파일을 이용하여 Redis 서비스를 실행한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
    
  2. 서비스의 목록을 질의하여 Redis 서비스가 실행 중인지 확인한다.

    kubectl get service
    

    결과는 아래와 같은 형태로 나타난다.

    NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    1m
    redis-leader   ClusterIP   10.103.78.24 <none>        6379/TCP   16s
    

Redis 팔로워 구성하기

Redis 리더는 단일 파드이지만, 몇 개의 Redis 팔로워 또는 복제본을 추가하여 가용성을 높이고 트래픽 요구를 충족할 수 있다.

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: follower
        tier: backend
    spec:
      containers:
      - name: follower
        image: gcr.io/google_samples/gb-redis-follower:v2
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
  1. redis-follower-deployment.yaml 파일을 이용하여 Redis 서비스를 실행한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
    
  2. 파드의 목록을 질의하여 2개의 Redis 팔로워 레플리카가 실행 중인지 확인한다.

    kubectl get pods
    

    결과는 아래와 같은 형태로 나타난다.

    NAME                             READY   STATUS    RESTARTS   AGE
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          37s
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          38s
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          11m
    

Redis 팔로워 서비스 생성하기

방명록 애플리케이션이 데이터를 읽으려면 Redis 팔로워와 통신해야 한다. Redis 팔로워를 발견 가능(discoverable)하게 만드려면, 새로운 서비스를 구성해야 한다.

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: redis-follower
  labels:
    app: redis
    role: follower
    tier: backend
spec:
  ports:
    # the port that this service should serve on
  - port: 6379
  selector:
    app: redis
    role: follower
    tier: backend
  1. redis-follower-service.yaml 파일을 이용하여 Redis 서비스를 실행한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml
    
  2. 서비스의 목록을 질의하여 Redis 서비스가 실행 중인지 확인한다.

    kubectl get service
    

    결과는 아래와 같은 형태로 나타난다.

    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP    3d19h
    redis-follower   ClusterIP   10.110.162.42   <none>        6379/TCP   9s
    redis-leader     ClusterIP   10.103.78.24    <none>        6379/TCP   6m10s
    

방명록 프론트엔드를 설정하고 노출하기

방명록을 위한 Redis 저장소를 구성하고 실행했으므로, 이제 방명록 웹 서버를 실행한다. Redis 팔로워와 마찬가지로, 프론트엔드는 쿠버네티스 디플로이먼트(Deployment)를 사용하여 배포된다.

방명록 앱은 PHP 프론트엔드를 사용한다. DB에 대한 요청이 읽기인지 쓰기인지에 따라, Redis 팔로워 또는 리더 서비스와 통신하도록 구성된다. 프론트엔드는 JSON 인터페이스를 노출하고, jQuery-Ajax 기반 UX를 제공한다.

방명록 프론트엔드의 디플로이먼트 생성하기

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
        app: guestbook
        tier: frontend
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v5
        env:
        - name: GET_HOSTS_FROM
          value: "dns"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80
  1. frontend-deployment.yaml 파일을 이용하여 프론트엔드 디플로이먼트를 생성한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
    
  2. 파드의 목록을 질의하여 세 개의 프론트엔드 복제본이 실행되고 있는지 확인한다.

    kubectl get pods -l app=guestbook -l tier=frontend
    

    결과는 아래와 같은 형태로 나타난다.

    NAME                        READY   STATUS    RESTARTS   AGE
    frontend-85595f5bf9-5tqhb   1/1     Running   0          47s
    frontend-85595f5bf9-qbzwm   1/1     Running   0          47s
    frontend-85595f5bf9-zchwc   1/1     Running   0          47s
    

프론트엔드 서비스 생성하기

서비스의 기본 유형은 ClusterIP 이기 때문에 생성한 Redis 서비스는 컨테이너 클러스터 내에서만 접근할 수 있다. ClusterIP는 서비스가 가리키는 파드 집합에 대한 단일 IP 주소를 제공한다. 이 IP 주소는 클러스터 내에서만 접근할 수 있다.

게스트가 방명록에 접근할 수 있도록 하려면, 외부에서 볼 수 있도록 프론트엔드 서비스를 구성해야 한다. 그렇게 하면 클라이언트가 쿠버네티스 클러스터 외부에서 서비스를 요청할 수 있다. 그러나 쿠버네티스 사용자는 ClusterIP를 사용하더라도 kubectl port-forward를 사용해서 서비스에 접근할 수 있다.

# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # if your cluster supports it, uncomment the following to automatically create
  # an external load-balanced IP for the frontend service.
  # type: LoadBalancer
  #type: LoadBalancer
  ports:
    # the port that this service should serve on
  - port: 80
  selector:
    app: guestbook
    tier: frontend
  1. frontend-service.yaml 파일을 이용하여 프론트엔드 서비스를 실행한다.

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
    
  2. 서비스의 목록을 질의하여 프론트엔드 서비스가 실행 중인지 확인한다.

    kubectl get services
    

    결과는 아래와 같은 형태로 나타난다.

    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    frontend         ClusterIP   10.97.28.230    <none>        80/TCP     19s
    kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP    3d19h
    redis-follower   ClusterIP   10.110.162.42   <none>        6379/TCP   5m48s
    redis-leader     ClusterIP   10.103.78.24    <none>        6379/TCP   11m
    

kubectl port-forward를 통해 프론트엔드 서비스 확인하기

  1. 다음 명령어를 실행해서 로컬 머신의 8080 포트를 서비스의 80 포트로 전달한다.

    kubectl port-forward svc/frontend 8080:80
    

    결과는 아래와 같은 형태로 나타난다.

    Forwarding from 127.0.0.1:8080 -> 80
    Forwarding from [::1]:8080 -> 80
    
  2. 방명록을 보기 위해 브라우저에서 http://localhost:8080 페이지를 로드한다.

LoadBalancer를 통해 프론트엔드 서비스 확인하기

frontend-service.yaml 매니페스트를 LoadBalancer와 함께 배포한 경우, 방명록을 보기 위해 IP 주소를 찾아야 한다.

  1. 프론트엔드 서비스의 IP 주소를 얻기 위해 아래 명령어를 실행한다.

    kubectl get service frontend
    

    결과는 아래와 같은 형태로 나타난다.

    NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
    frontend   LoadBalancer   10.51.242.136   109.197.92.229     80:32372/TCP   1m
    
  2. IP 주소를 복사하고, 방명록을 보기 위해 브라우저에서 페이지를 로드한다.

웹 프론트엔드 확장하기

서버가 디플로이먼트 컨트롤러를 사용하는 서비스로 정의되어 있으므로 필요에 따라 확장 또는 축소할 수 있다.

  1. 프론트엔드 파드의 수를 확장하기 위해 아래 명령어를 실행한다.

    kubectl scale deployment frontend --replicas=5
    
  2. 파드의 목록을 질의하여 실행 중인 프론트엔드 파드의 수를 확인한다.

    kubectl get pods
    

    결과는 아래와 같은 형태로 나타난다.

    NAME                             READY   STATUS    RESTARTS   AGE
    frontend-85595f5bf9-5df5m        1/1     Running   0          83s
    frontend-85595f5bf9-7zmg5        1/1     Running   0          83s
    frontend-85595f5bf9-cpskg        1/1     Running   0          15m
    frontend-85595f5bf9-l2l54        1/1     Running   0          14m
    frontend-85595f5bf9-l9c8z        1/1     Running   0          14m
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          97m
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          97m
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          108m
    
  3. 프론트엔드 파드의 수를 축소하기 위해 아래 명령어를 실행한다.

    kubectl scale deployment frontend --replicas=2
    
  4. 파드의 목록을 질의하여 실행 중인 프론트엔드 파드의 수를 확인한다.

    kubectl get pods
    

    결과는 아래와 같은 형태로 나타난다.

    NAME                            READY     STATUS    RESTARTS   AGE
    frontend-85595f5bf9-cpskg        1/1     Running   0          16m
    frontend-85595f5bf9-l9c8z        1/1     Running   0          15m
    redis-follower-dddfbdcc9-82sfr   1/1     Running   0          98m
    redis-follower-dddfbdcc9-qrt5k   1/1     Running   0          98m
    redis-leader-fb76b4755-xjr2n     1/1     Running   0          109m
    

정리하기

디플로이먼트 및 서비스를 삭제하면 실행 중인 모든 파드도 삭제된다. 레이블을 사용하여 하나의 명령어로 여러 자원을 삭제해보자.

  1. 모든 파드, 디플로이먼트, 서비스를 삭제하기 위해 아래 명령어를 실행한다.

    kubectl delete deployment -l app=redis
    kubectl delete service -l app=redis
    kubectl delete deployment frontend
    kubectl delete service frontend
    

    결과는 아래와 같은 형태로 나타난다.

    deployment.apps "redis-follower" deleted
    deployment.apps "redis-leader" deleted
    deployment.apps "frontend" deleted
    service "frontend" deleted
    
  2. 파드의 목록을 질의하여 실행 중인 파드가 없는지 확인한다.

    kubectl get pods
    

    결과는 아래와 같은 형태로 나타난다.

    No resources found in default namespace.
    

다음 내용

5.5 - 상태 유지가 필요한(stateful) 애플리케이션

5.5.1 - 스테이트풀셋 기본

이 튜토리얼은 스테이트풀셋(StatefulSet)을 이용하여 애플리케이션을 관리하는 방법을 소개한다. 어떻게 스테이트풀셋의 파드를 생성하고, 삭제하며, 스케일링하고, 업데이트하는지 시연한다.

시작하기 전에

튜토리얼을 시작하기 전에 다음의 쿠버네티스 컨셉에 대해 익숙해야 한다.

목적

스테이트풀셋은 상태 유지가 필요한(stateful) 애플리케이션과 분산시스템에서 이용하도록 의도했다. 그러나 쿠버네티스 상에 스테이트풀 애플리케이션과 분산시스템을 관리하는 것은 광범위하고 복잡한 주제이다. 스테이트풀셋의 기본 기능을 보여주기 위해 이 둘을 결합하지 않고, 스테이트풀셋을 사용한 단순 웹 애플리케이션을 배포할 것이다.

이 튜토리얼을 마치면 다음 항목에 대해 익숙해질 것이다.

  • 스테이트풀셋을 어떻게 생성하는지
  • 스테이트풀셋이 어떻게 파드를 관리하는지
  • 스테이트풀셋을 어떻게 삭제하는지
  • 스테이트풀셋은 어떻게 스케일링하는지
  • 스테이트풀셋의 파드는 어떻게 업데이트하는지

스테이트풀셋 생성하기

아래 예제를 이용해서 스테이트풀셋을 생성하자. 이는 스테이트풀셋 개념에서 보인 예제와 유사하다. 이것은 web과 이 스테이트풀셋 파드의 IP 주소를 게시하는 헤드리스 서비스nginx 를 생성한다.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

위에 예제를 다운로드 받아서 파일이름을 web.yaml으로 저장하자.

2개의 터미널창을 사용한다. 첫째 터미널에서 kubectl get을 이용해서 스테이트풀셋의 파드가 생성되는지 감시하자.

kubectl get pods -w -l app=nginx

두 번째 터미널에서 kubectl applyweb.yaml에 정의된 헤드리스 서비스와 스테이트풀셋을 생성한다.

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

상기 명령어는 NGINX 웹 서버를 실행하는 2개의 파드를 생성한다. nginx 서비스의 정보를 가져온다.

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

그리고 web 스테이트풀셋 정보를 가져와서 모두 성공적으로 생성되었는지 확인한다.

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

차례대로 파드 생성하기

N개의 레플리카를 가진 스테이트풀셋은 배포 시에 순차적으로 {0..N-1} 순으로 생성된다. 첫째 터미널에서 kubectl get 명령의 출력 내용을 살펴보자. 결국 그 내용은 아래 예와 비슷할 것이다.

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

참고로 web-1 파드는 web-0 파드가 Running (파드의 단계 참고) 및 Ready (파드의 조건에서 type 참고) 상태가 되기 전에 시작하지 않음을 주의하자.

스테이트풀셋 안에 파드

스테이트풀셋 안에 파드는 고유한 순번과 동일한 네트워크 신원을 가진다.

파드 순번 살펴보기

스테이트풀셋의 파드를 가져오자.

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

스테이트풀셋 개념에서 언급했듯 스테이트풀셋의 파드는 끈끈하고 고유한 정체성을 가진다. 이 정체성은 스테이트풀셋 컨트롤러에서 각 파드에 주어지는 고유한 순번에 기인한다. 파드의 이름의 형식은 <스테이트풀셋 이름>-<순번> 이다. 앞서 web 스테이트풀셋은 2개의 레플리카를 가졌으므로 web-0web-1 2개 파드를 생성한다.

안정적인 네트워크 신원 사용하기

각 파드는 각 순번에 따른 안정적인 호스트네임을 갖는다. 각 파드에서 hostname 명령어를 실행하도록 kubectl exec를 이용하자.

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

dnsutils 패키지에서 nslookup 명령을 제공하는 컨테이너를 실행하도록 kubectl run을 이용하자. 파드의 호스트네임에 nslookup을 이용하면 클러스터 내부 DNS 주소를 확인할 수 있다.

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

위 명령으로 새로운 셸을 시작한다. 새 셸에서 다음을 실행한다.

# dns-test 컨테이너 셸에서 다음을 실행한다.
nslookup web-0.nginx

출력 결과는 다음과 비슷하다.

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(이제 exit 명령으로 컨테이너 셸에서 종료한다.)

헤드리스 서비스의 CNAME은 SRV 레코드를 지칭한다 (Running과 Ready 상태의 각 파드마다 1개). SRV 레코드는 파드의 IP 주소를 포함한 A 레코드 엔트리를 지칭한다.

첫째 터미널에서 스테이트풀셋의 파드를 가져오자.

kubectl get pod -w -l app=nginx

두 번째 터미널에서 스테이트풀셋 내에 파드를 모두 삭제하기 위해 kubectl delete를 이용하자.

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

스테이트풀셋이 재시작되고 두 파드가 Running과 Ready 상태로 전환되도록 기다리자.

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

파드의 호스트네임과 클러스터 내부 DNS 엔트리를 보기 위해 kubectl execkubectl run을 이용하자. 먼저, 파드의 호스트네임을 확인한다.

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

그리고 다음을 실행한다.

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm /bin/sh

이 명령으로 새로운 셸이 시작된다. 새 셸에서 다음을 실행한다.

# dns-test 컨테이너 셸에서 이것을 실행한다.
nslookup web-0.nginx

출력 결과는 다음과 비슷하다.

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(이제 exit 명령으로 컨테이너 셸을 종료한다.)

파드의 순번, 호스트네임, SRV 레코드와 A 레코드이름은 변경되지 않지만 파드의 IP 주소는 변경될 수 있다. 이는 튜토리얼에서 사용하는 클러스터나 다른 클러스터에도 동일하다. 따라서 다른 애플리케이션이 IP 주소로 스테이트풀셋의 파드에 접속하지 않도록 하는 것이 중요하다.

스테이트풀셋의 활성 멤버를 찾아 연결할 경우 헤드리스 서비스(nginx.default.svc.cluster.local)의 CNAME을 쿼리해야 한다. CNAME과 연관된 SRV 레코드는 스테이트풀셋의 Running과 Ready 상태의 모든 파드들을 담고 있다.

애플리케이션에서 이미 활성상태(liveness)와 준비성(readiness) 테스트하는 연결 로직을 구현되어 있다면 파드web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local)의 SRV레코드를 안정적으로 사용할 수 있어 애플리케이션은 파드가 Running과 Ready 상태로 전환할 때 파드의 주소를 검색할 수 있다.

안정적인 스토리지에 쓰기

web-0web-1에 대해 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 가져오자.

kubectl get pvc -l app=nginx

출력 결과는 다음과 비슷하다.

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

스테이트풀셋 컨트롤러는 2개의 퍼시스턴트볼륨에 묶인 2개의 퍼시스턴트볼륨클레임을 생성했다.

본 튜토리얼에서 사용되는 클러스터는 퍼시스턴트볼륨을 동적으로 프로비저닝하도록 설정되었으므로 생성된 퍼시스턴트볼륨도 자동으로 묶인다.

NGINX 웹서버는 기본 색인 파일로 /usr/share/nginx/html/index.html을 이용합니다. 스테이트풀셋 spec내의 volumeMounts 필드는 /usr/share/nginx/html 디렉터리가 퍼시스턴트볼륨으로 제공되는지 보증합니다.

파드의 호스트네임을 index.html 파일에 작성하고 NGINX 웹서버가 해당 호스트네임을 제공하는지 확인해보자.

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -it "web-$i" -- curl localhost; done
web-0
web-1

첫째 터미널에서 스테이트풀셋의 파드를 감시하자.

kubectl get pod -w -l app=nginx

두 번째 터미널에서 스테이트풀셋의 모든 파드를 삭제하자.

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

첫 번째 터미널에서 실행 중인 kubectl get명령어의 출력을 확인하고, 모든 파드가 Running과 Ready 상태로 전환될 때까지 기다리자.

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

웹서버에서 자신의 호스트네임을 계속 제공하는지 확인하자.

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

비록 web-0web-1이 재스케줄링되어도 계속해서 자신의 호스트네임을 제공하는데 이는 각 퍼시스턴트볼륨클레임에 연관된 퍼시스턴트볼륨이 해당 volumeMounts로 재마운트되기 때문이다. web-0web-1의 스케줄링에 관계없이 각각의 퍼시스턴트볼륨은 적절하게 마운트된다.

스테이트풀셋 스케일링

스테이트풀셋을 스케일링하는 것은 레플리카 개수를 늘리거나 줄이는 것을 의미한다. 이것은 replicas 필드를 갱신하여 이뤄진다. kubectl scale이나 kubectl patch을 이용해서 스테이트풀셋을 스케일링할 수 있다.

스케일 업

터미널창에서 스테이트풀셋의 파드를 감시하자.

kubectl get pods -w -l app=nginx

다른 터미널창에서 kubectl scale을 이용하여 레플리카 개수를 5로 스케일링하자.

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

첫 번째 터미널에서 실행 중인 kubectl get명령어의 출력을 확인하고, 3개의 추가 파드가 Running과 Ready 상태로 전환될 때까지 기다리자.

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

스테이트풀셋 컨트롤러는 레플리카 개수를 스케일링한다. 스테이트풀셋 생성으로 스테이트풀셋 컨트롤러는 각 파드을 순차적으로 각 순번에 따라 생성하고 후속 파드 시작 전에 이전 파드가 Running과 Ready 상태가 될 때까지 기다린다.

스케일 다운

터미널에서 스테이트풀셋의 파드를 감시하자.

kubectl get pods -w -l app=nginx

다른 터미널에서 kubectl patch으로 스테이트풀셋을 다시 3개의 레플리카로 스케일링하자.

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

web-4web-3이 Terminating으로 전환되기까지 기다리자.

kubectl get pods -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

순차 파드 종료

컨트롤러는 순번의 역순으로 한 번에 1개 파드를 삭제하고 다음 파드를 삭제하기 전에 각각이 완전하게 종료되기까지 기다린다.

스테이트풀셋의 퍼시스턴트볼륨클레임을 가져오자.

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

여전히 5개의 퍼시스턴트볼륨클레임과 5개의 퍼시스턴트볼륨이 있다. 파드의 안전한 스토리지를 탐색하면서 스테이트풀셋의 파드가 삭제될 때에 파드에 마운트된 스테이트풀셋의 퍼시스턴트볼륨이 삭제되지 않은 것을 보았다. 스테이트풀셋 스케일 다운으로 파드 삭제할 때에도 여전히 사실이다.

스테이트풀셋 업데이트하기

쿠버네티스 1.7 이상에서 스테이트풀셋 컨트롤러는 자동 업데이트를 지원한다. 전략은 스테이트풀셋 API 오브젝트의 spec.updateStrategy 필드로 결정된다. 이 기능은 컨테이너 이미지, 스테이트풀셋의 리소스 요청이나 혹은 한계와 레이블과 파드의 어노테이션을 업그레이드하기 위해 사용될 수 있다. RollingUpdateOnDelete의 2개의 유효한 업데이트 전략이 있다.

RollingUpdate 업데이트 전략은 스테이트풀셋에서 기본 값이다.

롤링 업데이트

RollingUpdate 업데이트 전략은 스테이트풀셋을 보장하면서 스테이트풀셋 내에 파드를 역순으로 업데이트합니다.

스테이트풀셋 web의 업데이트 전략을 RollingUpdate으로 패치하자.

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched

터미널 창에서 스테이트풀셋 web의 컨테이너 이미지를 바꾸도록 또 패치하자.

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]'
statefulset.apps/web patched

다른 터미널창에서 스테이트풀셋의 파드를 감시하자.

kubectl get pod -l app=nginx -w

출력 결과는 다음과 비슷하다.

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

스테이트풀셋 내에 파드는 순번의 역순으로 업데이트된다. 이 스테이트풀셋 컨트롤러는 각 파드를 종료시키고 다음 파드를 업데이트하기 전에 그것이 Running과 Ready 상태로 전환될 때까지 기다린다. 알아둘 것은 비록 스테이트풀셋 컨트롤러에서 이전 파드가 Running과 Ready 상태가 되기까지 다음 파드를 업데이트하지 않아도 현재 버전으로 파드를 업데이트하다 실패하면 복원한다는 것이다.

업데이트를 이미 받은 파드는 업데이트된 버전으로 복원되고 아직 업데이트를 받지 못한 파드는 이전 버전으로 복원한다. 이런 식으로 컨트롤러는 간헐적인 오류가 발생해도 애플리케이션을 계속 건강하게 유지하고 업데이트도 일관되게 유지하려 한다.

컨테이너 이미지를 살펴보기 위해 파드를 가져오자.

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
k8s.gcr.io/nginx-slim:0.8
k8s.gcr.io/nginx-slim:0.8
k8s.gcr.io/nginx-slim:0.8

스테이트풀셋의 모든 파드가 지금은 이전 컨테이너 이미지를 실행 중이이다.

단계적으로 업데이트 하기

RollingUpdate 업데이트 전략의 파라미터인 partition를 이용하여 스테이트풀셋의 단계적으로 업데이트할 수 있다. 단계적 업데이트는 스테이트풀셋의 모든 파드를 현재 버전으로 유지하면서 스테이트풀셋의 .spec.template에 변경을 허용한다.

스테이트풀셋 webupdateStrategy 필드에 partition을 추가하자.

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

컨테이너의 이미지를 바꾸도록 스테이트풀셋을 또 패치하자.

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"k8s.gcr.io/nginx-slim:0.7"}]'
statefulset.apps/web patched

스테이트풀셋의 파드를 삭제하자.

kubectl delete pod web-2
pod "web-2" deleted

파드가 Running과 Ready 상태가 되기까지 기다리자.

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

파드의 컨테이너 이미지를 가져오자.

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.8

비록 업데이트 전략이 RollingUpdate이지만 스테이트풀셋은 파드를 그것의 원래 컨테이너로 복원한다. 파드의 순번이 updateStrategy에서 지정된 파티션보다 작기 때문이다.

카나리(Canary) 롤링 아웃

위에서 지정한 partition값을 차감시키면 변경사항을 테스트하기 위해 카나리 롤아웃을 할 수 있다.

스테이트풀셋에 partition을 차감하도록 패치하자.

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

web-2 파드가 Running과 Ready 상태가 되기까지 기다리자.

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

파드의 컨테이너를 가져오자.

kubectl get po web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.7

partition을 바꾸면 스테이트풀셋 컨트롤러는 자동으로 web-2 파드를 업데이트하는데 이는 해당 파드의 순번이 partition 이상이기 때문이다.

web-1 파드를 삭제하자.

kubectl delete pod web-1
pod "web-1" deleted

web-1 파드가 Running과 Ready 상태가 되기까지 기다리자.

kubectl get pod -l app=nginx -w

출력 결과는 다음과 비슷하다.

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

web-1 파드의 컨테이너 이미지를 가져오자.

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.8

web-1 는 원래 환경설정으로 복원되었는데 이는 파드의 순번이 partition보다 작기 때문이다. 스테이트풀셋의 .spec.template이 갱신되면, 지정된 partition 이상의 순번을 가진 모든 파드는 업데이트된다. 미만의 순번을 가진 파드라면 삭제되거나 종료되어 원래 환경설정으로 복원된다.

단계적 롤아웃

카나리 롤아웃에서 했던 방법과 비슷하게 분할된 롤링 업데이트를 이용하여 단계적 롤아웃(e.g. 선형, 기하 또는 지수적 롤아웃)을 수행할 수 있다. 단계적 롤아웃을 수행하려면 컨트롤러가 업데이트를 일시 중지할 순번으로 partition를 정하자.

partition은 현재 2이다. partition을 0으로 바꾸자.

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

스테이트풀셋의 모든 파드가 Running과 Ready 상태가 되기까지 기다리자.

kubectl get pod -l app=nginx -w

출력 결과는 다음과 비슷하다.

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

스테이트풀셋에 있는 파드의 컨테이너 이미지 상세 정보를 가져오자.

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
k8s.gcr.io/nginx-slim:0.7
k8s.gcr.io/nginx-slim:0.7
k8s.gcr.io/nginx-slim:0.7

partition0으로 이동하여 스테이트풀셋에서 계속해서 업데이트 처리를 하도록 허용하였다.

삭제 시 동작

OnDelete 업데이트 전략은 예전 동작(1.6 이하)으로, 이 업데이트 전략을 선택하면 스테이트풀셋 컨트롤러는 스테이트풀셋의 .spec.template 필드에 수정 사항이 발생해도 자동으로 파드를 업데이트하지 않는다. 이 전략은 .spec.template.updateStrategy.typeOnDelete로 설정하여 선택할 수 있다.

스테이트풀셋 삭제하기

스테이트풀셋은 비종속적(non-cascading), 종속적(cascading) 삭제를 둘 다 지원한다. 비종속적 삭제에서는 스테이트풀셋이 지워질 때에 스테이트풀셋의 파드는 지워지지 않는다. 종속적 삭제에서는 스테이트풀셋과 그에 속한 파드가 모두 지워진다.

비종속적 삭제

터미널창에서 스테이트풀셋의 파드를 감시하자.

kubectl get pods -w -l app=nginx

다른 터미널에서는 스테이트풀셋을 지우기 위해 kubectl delete 명령어를 이용하자. 이 명령어에 --cascade=orphan 파라미터가 추가되었다. 이 파라미터는 쿠버네티스에 스테이트풀셋만 삭제하고 그에 속한 파드는 지우지 않도록 요청한다.

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

상태를 확인하기 위해 파드를 가져오자.

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

비록 web이 삭제되고 있어도, 모든 파드는 여전히 Running과 Ready 상태이다. web-0을 삭제하자.

kubectl delete pod web-0
pod "web-0" deleted

스테이트풀셋의 파드를 가져오자.

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

스테이트풀셋 web이 삭제되는 동안 web-0은 재시작하지 않았다.

첫째 터미널에서 스테이트풀셋의 파드를 감시하자.

kubectl get pods -w -l app=nginx

두 번째 터미널에서 스테이트풀셋을 다시 생성하자. nginx 서비스(가지지 말았어야 하는)를 삭제하기 전까지는 그 서비스가 이미 존재한다는 에러를 볼 것이라는 것을 명심하자.

kubectl apply -f web.yaml
statefulset.apps/web created
service/nginx unchanged

이 에러는 무시하자. 이것은 다만 해당 서비스가 있더라도 nginx 헤드리스 서비스를 생성하려고 했음을 뜻한다.

첫째 터미널에서 실행 중인 kubectl get 명령어의 출력을 살펴보자.

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

web 스테이트풀셋이 다시 생성될 때 먼저 web-0 시작한다. web-1은 이미 Running과 Ready 상태이므로 web-0이 Running과 Ready 상태로 전환될 때는 이 파드에 적용됐다. 스테이트풀셋에 replicas를 2로 하고 web-0을 재생성했다면 web-1이 이미 Running과 Ready 상태이고, web-2은 종료되었을 것이다.

파드의 웹서버에서 제공한 index.html 파일 내용을 다른 관점으로 살펴보자.

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

스테이트풀셋과 web-0 파드를 둘 다 삭제했으나 여전히 index.html 파일에 입력했던 원래 호스트네임을 제공한다. 스테이트풀셋은 파드에 할당된 퍼시스턴트볼륨을 결코 삭제하지 않기때문이다. 다시 스테이트풀셋을 생성하면 web-0을 시작하며 원래 퍼시스턴트볼륨을 다시 마운트한다.

단계식 삭제

터미널창에서 스테이트풀셋의 파드를 감시하자.

kubectl get pods -w -l app=nginx

다른 터미널창에서 스테이트풀셋을 다시 지우자. 이번에는 --cascade=orphan 파라미터를 생략하자.

kubectl delete statefulset web
statefulset.apps "web" deleted

첫째 터미널에서 실행 중인 kubectl get 명령어의 출력을 살펴보고 모든 파드가 Terminating 상태로 전환될 때까지 기다리자.

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

스케일 다운 섹션에서 보았듯 파드는 각 순번의 역순으로 하나씩 종료된다. 파드가 종료될 때 스테이트풀 컨트롤러는 이전 파드가 완전히 종료되기까지 기다린다.

kubectl delete service nginx
service "nginx" deleted

스테이트풀셋과 헤드리스 서비스를 한번 더 다시 생성하자.

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

스테이트풀셋의 모든 파드가 Running과 Ready 상태로 전환될 때 index.html 파일 내용을 검색하자.

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

스테이트풀셋과 그 내부의 모든 파드를 삭제했지만 퍼시스턴트볼륨이 마운트된 채로 다시 생성되고 web-0web-1은 계속 각 호스트네임을 제공한다.

최종적으로 nginx 서비스를 삭제한다.

kubectl delete service nginx
service "nginx" deleted

그리고 web 스테이트풀셋을 삭제한다.

kubectl delete statefulset web
statefulset "web" deleted

파드 관리 정책

일부 분산 시스템의 경우 스테이트풀셋의 순서 보증은 불필요하거나 바람직하지 않다. 이러한 시스템은 고유성과 신원만 필요하다. 이를 해결하기 위해 쿠버네티스 1.7에서 .spec.podManagementPolicy를 스테이트풀셋 API 오브젝트에 도입했다.

OrderedReady 파드 관리

OrderedReady 파드 관리는 스테이트풀셋에서는 기본이다. 이는 스테이트풀셋 컨트롤러가 지금까지 위에서 설명했던 순서를 보증함을 뜻한다.

Parallel 파드 관리

Parallel 파드 관리는 스테이트풀셋 컨트롤러가 모든 파드를 병렬로 시작하고 종료하는 것으로, 다른 파드를 시작/종료하기 전에 파드가 Running과 Ready 상태로 전환되거나 완전히 종료되기까지 기다리지 않음을 뜻한다. 이 옵션은 스케일링 동작에만 영향을 미치며, 업데이트 동작에는 영향을 미치지 않는다.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

상기 예제를 다운로드받아 파일 이름을 web-parallel.yaml로 저장하자.

이 매니페스트는 web 스테이트풀셋의 .spec.podManagementPolicyParallel인 것 말고는 이전에 다운로드 받았던 것과 동일하다.

터미널에서 스테이트풀셋의 파드를 감시하자.

kubectl get pod -l app=nginx -w

다른 터미널에서 매니페스트 안에 스테이트풀셋과 서비스를 생성하자.

kubectl apply -f web-parallel.yaml
service/nginx created
statefulset.apps/web created

첫째 터미널에서 실행했던 kubectl get 명령어의 출력을 살펴보자.

kubectl get pod -l app=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

스테이트풀셋 컨트롤러는 web-0web-1를 둘 다 동시에 시작했다.

두 번째 터미널을 열어 놓고 다른 터미널창에서 스테이트풀셋을 스케일링하자.

kubectl scale statefulset/web --replicas=4
statefulset.apps/web scaled

kubectl get 명령어를 실행 중인 터미널의 출력을 살펴보자.

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s

스테이트풀셋은 두 개의 새 파드를 시작하였다. 두 번째 것을 런칭하기 위해 먼저 런칭한 것이 Running과 Ready 상태가 될 때까지 기다리지 않는다.

정리하기

정리의 일환으로 kubectl 명령을 실행할 준비가 된 두 개의 터미널이 열려 있어야 한다.

kubectl delete sts web
# sts는 statefulset의 약자이다.

kubectl get 명령으로 해당 파드가 삭제된 것을 확인할 수 있다.

kubectl get pod -l app=nginx -w
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

삭제하는 동안, 스테이트풀셋은 모든 파드를 동시에 삭제한다. 해당 파드를 삭제하기 전에 그 파드의 순서상 후계자를 기다리지 않는다.

kubectl get 명령어가 실행된 터미널을 닫고 nginx 서비스를 삭제하자.

kubectl delete svc nginx

5.5.2 - 예시: WordPress와 MySQL을 퍼시스턴트 볼륨에 배포하기

이 튜토리얼은 WordPress 사이트와 MySQL 데이터베이스를 Minikube를 이용하여 어떻게 배포하는지 보여준다. 애플리케이션 둘 다 퍼시스턴트 볼륨과 퍼시스턴트볼륨클레임을 데이터를 저장하기 위해 사용한다.

퍼시스턴트볼륨(PV)는 관리자가 수동으로 프로비저닝한 클러스터나 쿠버네티스 스토리지클래스를 이용해 동적으로 프로비저닝된 저장소의 일부이다. 퍼시스턴트볼륨클레임(PVC)은 PV로 충족할 수 있는 사용자에 의한 스토리지 요청이다. 퍼시스턴트볼륨은 파드 라이프사이클과 독립적이며 재시작, 재스케줄링이나 파드를 삭제할 때에도 데이터를 보존한다.

목적

  • 퍼시스턴트볼륨클레임과 퍼시스턴트볼륨 생성
  • 다음을 포함하는 kustomization.yaml 생성
    • 시크릿 생성자
    • MySQL 리소스 구성
    • WordPress 리소스 구성
  • kubectl apply -k ./로 생성한 kustomization 을 적용
  • 정리

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version. 이 예시는 kubectl 1.14 이상 버전에서 동작한다.

다음 설정 파일을 다운로드한다.

  1. mysql-deployment.yaml

  2. wordpress-deployment.yaml

퍼시스턴트볼륨클레임과 퍼시스턴트볼륨 생성

MySQL과 Wordpress는 각각 데이터를 저장할 퍼시스턴트볼륨이 필요하다. 퍼시스턴트볼륨클레임은 배포 단계에 생성된다.

많은 클러스터 환경에서 설치된 기본 스토리지클래스(StorageClass)가 있다. 퍼시스턴트볼륨클레임에 스토리지클래스를 지정하지 않으면 클러스터의 기본 스토리지클래스를 사용한다.

퍼시스턴트볼륨클레임이 생성되면 퍼시스턴트볼륨이 스토리지클래스 설정을 기초로 동적으로 프로비저닝된다.

kustomization.yaml 생성하기

시크릿 생성자 추가

시크릿은 암호나 키 같은 민감한 데이터들을 저장하는 오브젝트이다. 1.14 버전부터 kubectl은 kustomization 파일을 이용해서 쿠버네티스 오브젝트를 관리한다. kustomization.yaml의 제너레이터로 시크릿을 생성할 수 있다.

다음 명령어로 kustomization.yaml 내에 시크릿 제네레이터를 추가한다. YOUR_PASSWORD는 사용하기 원하는 암호로 변경해야 한다.

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
  literals:
  - password=YOUR_PASSWORD
EOF

MySQL과 WordPress에 필요한 리소스 구성 추가하기

다음 매니페스트는 MySQL 디플로이먼트 단일 인스턴스를 기술한다. MySQL 컨케이너는 퍼시스턴트볼륨을 /var/lib/mysql에 마운트한다. MYSQL_ROOT_PASSWORD 환경 변수는 시크릿에서 가져와 데이터베이스 암호로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

다음의 매니페스트는 단일-인스턴스 WordPress 디플로이먼트를 기술한다. WordPress 컨테이너는 웹사이트 데이터 파일을 위해 /var/www/html에 퍼시스턴트볼륨을 마운트한다. WORDPRESS_DB_HOST 환경 변수에는 위에서 정의한 MySQL 서비스의 이름이 설정되며, WordPress는 서비스를 통해 데이터베이스에 접근한다. WORDPRESS_DB_PASSWORD 환경 변수에는 kustomize가 생성한 데이터베이스 패스워드가 설정된다.

apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress:4.8-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
  1. MySQL 디플로이먼트 구성 파일을 다운로드한다.

    curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
    
  2. WordPress 구성 파일을 다운로드한다.

    curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
    
  3. 두 파일을 kustomization.yaml에 추가하자.

cat <<EOF >>./kustomization.yaml
resources:
  - mysql-deployment.yaml
  - wordpress-deployment.yaml
EOF

적용하고 확인하기

kustomization.yaml은 WordPress 사이트와 MySQL 데이터베이스를 배포하는 모든 리소스를 포함한다. 다음과 같이 디렉터리를 적용할 수 있다.

kubectl apply -k ./

이제 모든 오브젝트가 존재하는지 확인할 수 있다.

  1. 시크릿이 존재하는지 다음 명령어를 실행하여 확인한다.

    kubectl get secrets
    

    응답은 아래와 비슷해야 한다.

    NAME                    TYPE                                  DATA   AGE
    mysql-pass-c57bb4t7mf   Opaque                                1      9s
    
  2. 퍼시스턴트볼륨이 동적으로 프로비저닝되었는지 확인한다.

    kubectl get pvc
    

    응답은 아래와 비슷해야 한다.

    NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
    mysql-pv-claim   Bound     pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    wp-pv-claim      Bound     pvc-8cd0df54-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    
  3. 다음 명령어를 실행하여 파드가 실행 중인지 확인한다.

    kubectl get pods
    

    응답은 아래와 비슷해야 한다.

    NAME                               READY     STATUS    RESTARTS   AGE
    wordpress-mysql-1894417608-x5dzt   1/1       Running   0          40s
    
  4. 다음 명령어를 실행하여 서비스가 실행 중인지 확인해보자.

    kubectl get services wordpress
    

    응답은 아래와 비슷해야 한다.

    NAME        TYPE            CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
    wordpress   LoadBalancer    10.0.0.89    <pending>     80:32406/TCP   4m
    
  5. 다음 명령어를 실행하여 WordPress 서비스의 IP 주소를 얻어온다.

    minikube service wordpress --url
    

    응답은 아래와 비슷해야 한다.

    http://1.2.3.4:32406
    
  6. IP 주소를 복사해서 웹 브라우저에서 사이트를 열어 보자.

    아래 스크린샷과 유사한 WordPress 설정 페이지를 볼 수 있어야 한다.

    wordpress-init

정리하기

  1. 다음 명령을 실행하여 시크릿, 디플로이먼트, 서비스와 퍼시스턴트볼륨클레임을 삭제하자.

    kubectl delete -k ./
    

다음 내용

5.5.3 - 예시: 카산드라를 스테이트풀셋으로 배포하기

이 튜토리얼은 쿠버네티스에서 아파치 카산드라를 실행하는 방법을 소개한다. 데이터베이스인 카산드라는 데이터 내구성을 제공하기 위해 퍼시스턴트 스토리지가 필요하다(애플리케이션 상태). 이 예제에서 사용자 지정 카산드라 시드 공급자는 카산드라가 클러스터에 가입할 때 카산드라가 인스턴스를 검색할 수 있도록 한다.

스테이트풀셋 은 상태있는 애플리케이션을 쿠버네티스 클러스터에 쉽게 배포할 수 있게 한다. 이 튜토리얼에서 이용할 기능의 자세한 정보는 스테이트풀셋을 참조한다.

목적

  • 카산드라 헤드리스 Service를 생성하고 검증한다.
  • 스테이트풀셋(StatefulSet)을 이용하여 카산드라 링을 생성한다.
  • 스테이트풀셋을 검증한다.
  • 스테이트풀셋을 수정한다.
  • 스테이트풀셋과 포함된 파드를 삭제한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

이 튜토리얼을 완료하려면, 파드, 서비스, 스테이트풀셋에 대한 기본 지식이 있어야 한다.

추가적인 Minikube 설정 요령

카산드라를 위한 헤드리스 서비스 생성하기

쿠버네티스 에서 서비스는 동일 작업을 수행하는 파드의 집합을 기술한다.

다음의 서비스는 클러스터에서 카산드라 파드와 클라이언트 간에 DNS 찾아보기 용도로 사용한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra

cassandra-service.yaml 파일에서 카산드라 스테이트풀셋 노드를 모두 추적하는 서비스를 생성한다.

kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml

검증하기(선택)

카산드라 서비스 살펴보기

kubectl get svc cassandra

결과는 다음과 같다.

NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   ClusterIP   None         <none>        9042/TCP   45s

cassandra 서비스가 보이지 않는다면, 이와 다른 응답이라면 서비스 생성에 실패한 것이다. 일반적인 문제에 대한 서비스 디버깅하기를 읽어보자.

카산드라 링을 생성하는 스테이트풀셋 이용하기

스테이트풀셋 매니페스트에는 다음을 포함하는데 3개 파드로 구성된 카산드라 링을 생성한다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # These volume mounts are persistent. They are like inline claims,
        # but not exactly because the names need to match exactly one of
        # the stateful pod volumes.
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  # do not use these in production until ssd GCEPersistentDisk or other ssd pd
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

cassandra-statefulset.yaml 파일로 카산드라 스테이트풀셋 생성

# cassandra-statefulset.yaml을 수정하지 않은 경우에 이것을 사용한다.
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml

클러스터에 맞게 cassandra-statefulset.yaml 를 수정해야 하는 경우 다음을 다운로드한 다음 수정된 버전을 저장한 폴더에서 해당 매니페스트를 적용한다. https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml

# cassandra-statefulset.yaml을 로컬에서 수정한 경우에 사용한다.
kubectl apply -f cassandra-statefulset.yaml

카산드라 스테이트풀셋 검증하기

  1. 카산드라 스테이트풀셋 얻기

    kubectl get statefulset cassandra
    

    응답은 다음과 유사하다.

    NAME        DESIRED   CURRENT   AGE
    cassandra   3         0         13s
    

    StatefulSet리소스는 순차적으로 파드를 배포한다.

  2. 순차적으로 생성된 현황을 보기 위해 파드를 살펴보자.

    kubectl get pods -l="app=cassandra"
    

    응답은 다음과 유사하다.

    NAME          READY     STATUS              RESTARTS   AGE
    cassandra-0   1/1       Running             0          1m
    cassandra-1   0/1       ContainerCreating   0          8s
    

    모든 3개 파드가 배포되기까지 몇 분이 소요될 수 있다. 배포 후, 동일 명령은 다음과 유사하게 응답한다.

    NAME          READY     STATUS    RESTARTS   AGE
    cassandra-0   1/1       Running   0          10m
    cassandra-1   1/1       Running   0          9m
    cassandra-2   1/1       Running   0          8m
    
  3. 첫 번째 파드 내부에 링의 상태를 보여주는 카산드라 nodetool을 실행하자.

    kubectl exec -it cassandra-0 -- nodetool status
    

    이 응답은 다음과 비슷하게 보일 것이다.

    Datacenter: DC1-K8Demo
    ======================
    Status=Up/Down
    |/ State=Normal/Leaving/Joining/Moving
    --  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
    UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
    UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
    UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
    

카산드라 스테이트풀셋 수정하기

kubectl edit를 사용하여 카산드라 스테이트풀셋의 크기를 수정한다.

  1. 다음 명령어를 실행한다.

    kubectl edit statefulset cassandra
    

    이 명령은 터미널에서 편집기를 연다. 변경해야할 행은 replicas 필드이다. 다음 예제는 스테이트풀셋 파일에서 발췌했다.

    # 다음의 오브젝트를 수정한다. '#'로 시작하는 행은 무시되고,
    # 빈 파일은 편집을 중단한다. 저장할 때 오류가 발생하면 이 파일이
    # 관련 실패와 함께 다시 열린다.
    #
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      creationTimestamp: 2016-08-13T18:40:58Z
      generation: 1
      labels:
      app: cassandra
      name: cassandra
      namespace: default
      resourceVersion: "323"
      uid: 7a219483-6185-11e6-a910-42010a8a0fc0
    spec:
      replicas: 3
    
  2. 레플리카 개수를 4로 바꾸고, 매니페스트를 저장한다.

    스테이트풀셋은 4개의 파드를 실행하기 위해 스케일 한다.

  3. 검증하기 위해 카산드라 스테이트풀셋을 살펴보자

    kubectl get statefulset cassandra
    

    결과는 다음과 유사하다.

    NAME        DESIRED   CURRENT   AGE
    cassandra   4         4         36m
    

정리하기

스테이트풀셋을 삭제하거나 스케일링하는 것은 스테이트풀셋에 연관된 볼륨을 삭제하지 않는다. 당신의 데이터가 스테이트풀셋의 관련된 모든 리소스를 자동으로 제거하는 것보다 더 가치있기에 이 설정은 당신의 안전을 위한 것이다.

  1. 다음 명령어(한 줄로 연결된)를 실행하여 카산드라 스테이트풀셋을 모두 제거하자.

    grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
      && kubectl delete statefulset -l app=cassandra \
      && echo "Sleeping ${grace} seconds" 1>&2 \
      && sleep $grace \
      && kubectl delete persistentvolumeclaim -l app=cassandra
    
  2. 다음 명령어를 실행하여 카산드라에 대해 설정한 서비스를 제거하자.

    kubectl delete service -l app=cassandra
    

카산드라 컨테이너 환경 변수

이 튜토리얼의 파드 는 구글의 컨테이너 레지스트리gcr.io/google-samples/cassandra:v13 이미지를 이용한다. 이 도커 이미지는 debian-base에 기반하였고 OpenJDK 8을 포함한다.

이 이미지는 아파치 데비안 리포의 표준 카산드라 설치본을 포함한다. 환경 변수를 이용하여 cassandra.yaml에 삽입된 값을 바꿀 수 있다.

환경 변수 기본값
CASSANDRA_CLUSTER_NAME 'Test Cluster'
CASSANDRA_NUM_TOKENS 32
CASSANDRA_RPC_ADDRESS 0.0.0.0

다음 내용

5.5.4 - 분산 시스템 코디네이터 ZooKeeper 실행하기

이 튜토리얼은 아파치 ZooKeeper 쿠버네티스에서 스테이트풀셋PodDisruptionBudget파드안티어피니티(PodAntiAffinity)를 이용한 Apache Zookeeper 실행을 설명한다.

시작하기 전에

이 튜토리얼을 시작하기 전에 다음 쿠버네티스 개념에 친숙해야 한다.

반드시 최소한 4개의 노드가 있는 클러스터가 필요하며, 각 노드는 적어도 2 개의 CPU와 4 GiB 메모리가 필요하다. 이 튜토리얼에서 클러스터 노드를 통제(cordon)하고 비우게(drain) 할 것이다. 이것은 클러스터를 종료하여 노드의 모든 파드를 축출(evict)하는 것으로, 모든 파드는 임시로 언스케줄된다는 의미이다. 이 튜토리얼을 위해 전용 클러스터를 이용하거나, 다른 테넌트에 간섭을 하는 혼란이 발생하지 않도록 해야 합니다.

이 튜토리얼은 클러스터가 동적으로 퍼시스턴트볼륨을 프로비저닝하도록 구성한다고 가정한다. 그렇게 설정되어 있지 않다면 튜토리얼을 시작하기 전에 수동으로 3개의 20 GiB 볼륨을 프로비저닝해야 한다.

목적

이 튜토리얼을 마치면 다음에 대해 알게 된다.

  • 어떻게 스테이트풀셋을 이용하여 ZooKeeper 앙상블을 배포하는가.
  • 어떻게 앙상블을 일관되게 설정하는가.
  • 어떻게 ZooKeeper 서버 디플로이먼트를 앙상블 안에서 퍼뜨리는가.
  • 어떻게 PodDisruptionBudget을 이용하여 계획된 점검 기간 동안 서비스 가용성을 보장하는가.

ZooKeeper

아파치 ZooKeeper는 분산 애플리케이션을 위한 분산 오픈 소스 코디네이션 서비스이다. ZooKeeper는 데이터를 읽고 쓰고 갱신을 지켜보도록 한다. 데이터는 파일시스템처럼 계층적으로 관리되고 앙상블(ZooKeeper 서버의 집합) 내에 모든 ZooKeeper서버에 복제된다. 데이터에 모든 연산은 원자적이고 순처적으로 일관된다. ZooKeeper는 Zab 합의 프로토콜을 이용하여 앙상블 내에 모든 서버에 걸쳐 상태 머신을 복제하여 이를 보장한다.

앙상블은 리더 선출을 위해 Zab 프로토콜을 사용하고, 리더 선출과 선거가 완료되기 전까지 앙상블은 데이터를 쓸 수 없다. 완료되면 앙상블은 Zab을 이용하여 확인하고 클라이언트에 보이도록 모든 쓰기를 쿼럼(quorum)에 복제한다. 가중치있는 쿼럼과 관련 없이, 쿼럼은 현재 리더를 포함하는 앙상블의 대다수 컴포넌트이다. 예를 들어 앙상블이 3개 서버인 경우, 리더와 다른 서버로 쿼럼을 구성한다. 앙상블이 쿼럼을 달성할 수 없다면, 앙상블은 데이터를 쓸 수 없다.

ZooKeeper는 전체 상태 머신을 메모리에 보존하고 모든 돌연변이를 저장 미디어의 내구성 있는 WAL(Write Ahead Log)에 기록한다. 서버 장애시 WAL을 재생하여 이전 상태를 복원할 수 있다. WAL이 무제한으로 커지는 것을 방지하기 위해 ZooKeeper는 주기적으로 저장 미디어에 메모리 상태의 스냅샷을 저장한다. 이 스냅샷은 메모리에 직접 적재할 수 있고 스냅샷 이전의 모든 WAL 항목은 삭제될 수 있다.

ZooKeeper 앙상블 생성하기

아래 매니페스트에는 헤드리스 서비스, 서비스, PodDisruptionBudget, 스테이트풀셋을 포함한다.

apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zk
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  selector:
    matchLabels:
      app: zk
  maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
spec:
  selector:
    matchLabels:
      app: zk
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: OrderedReady
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: kubernetes-zookeeper
        imagePullPolicy: Always
        image: "k8s.gcr.io/kubernetes-zookeeper:1.0-3.4.10"
        resources:
          requests:
            memory: "1Gi"
            cpu: "0.5"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        command:
        - sh
        - -c
        - "start-zookeeper \
          --servers=3 \
          --data_dir=/var/lib/zookeeper/data \
          --data_log_dir=/var/lib/zookeeper/data/log \
          --conf_dir=/opt/zookeeper/conf \
          --client_port=2181 \
          --election_port=3888 \
          --server_port=2888 \
          --tick_time=2000 \
          --init_limit=10 \
          --sync_limit=5 \
          --heap=512M \
          --max_client_cnxns=60 \
          --snap_retain_count=3 \
          --purge_interval=12 \
          --max_session_timeout=40000 \
          --min_session_timeout=4000 \
          --log_level=INFO"
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi

터미널을 열고 kubectl apply 명령어로 매니페스트를 생성하자.

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

이는 zk-hs 헤드리스 서비스, zk-cs 서비스, zk-pdb PodDisruptionBudget과 zk 스테이트풀셋을 생성한다.

service/zk-hs created
service/zk-cs created
poddisruptionbudget.policy/zk-pdb created
statefulset.apps/zk created

kubectl get을 사용하여 스테이트풀셋 컨트롤러가 스테이트풀셋 파드를 생성하는지 확인한다.

kubectl get pods -w -l app=zk

zk-2 파드가 Running and Ready 상태가 되면, CTRL-C를 눌러 kubectl을 종료하자.

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

스테이트풀셋 컨트롤러는 3개의 파드를 생성하고, 각 파드는 ZooKeeper 서버를 포함한 컨테이너를 가진다.

리더 선출 촉진

익명 네트워크에서 리더 선출을 위한 종료 알고리즘이 없기에, Zab은 리더 선출을 위해 명시적인 멤버 구성을 해야 한다. 앙상블의 각 서버는 고유 식별자를 가져야 하고, 모든 서버는 식별자 전역 집합을 알아야 하며, 각 식별자는 네트워크 주소에 연관되어야 한다.

kubectl exec를 이용하여 zk 스테이트풀셋의 파드의 호스트네임을 알아내자.

for i in 0 1 2; do kubectl exec zk-$i -- hostname; done

스테이트풀셋 컨트롤러는 각 순번 인덱스에 기초하여 각 파드에 고유한 호스트네임을 부여한다. 각 호스트네임은 <스테이트풀셋 이름>-<순번 인덱스> 형식을 취한다. zk 스테이트풀셋의 replicas 필드는 3으로 설정되었기 때문에, 그 스테이트풀셋 컨트롤러는 3개 파드의 호스트네임을 zk-0, zk-1, zk-2로 정한다.

zk-0
zk-1
zk-2

ZooKeeper 앙상블에 서버들은 고유 식별자로서 자연수를 이용하고 서버 데이터 디렉터리에 my 라는 파일로 서버 식별자를 저장한다.

각 서버에서 다음 명령어를 이용하여 myid 파일의 내용을 확인하자.

for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done

식별자는 자연수이고, 순번 인덱스들도 음수가 아니므로, 순번에 1을 더하여 순번을 만들 수 있다.

myid zk-0
1
myid zk-1
2
myid zk-2
3

zk 스테이트풀셋의 각 파드 Fully Qualified Domain Name (FQDN)을 얻기 위해 다음 명령어를 이용하자.

for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done

zk-hs 서비스는 모든 파드를 위한 도메인인 zk-hs.default.svc.cluster.local을 만든다.

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

쿠버네티스 DNS의 A 레코드는 FQDN을 파드의 IP 주소로 풀어낸다. 쿠버네티스가 파드를 리스케줄하면, 파드의 새 IP 주소로 A 레코드를 갱신하지만, A 레코드의 이름은 바뀌지 않는다.

ZooKeeper는 그것의 애플리케이션 환경설정을 zoo.cfg 파일에 저장한다. kubectl exec를 이용하여 zk-0 파드의 zoo.cfg 내용을 보자.

kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg

아래 파일의 server.1, server.2, server.3 속성에서 1, 2, 3은 ZooKeeper 서버의 myid 파일에 구분자와 연관된다. 이들은 zk 스테이트풀셋의 파드의 FQDNS을 설정한다.

clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

합의 달성

합의 프로토콜에서 각 참가자의 식별자는 유일해야 한다. Zab 프로토콜에서 동일한 고유 식별자를 요청하는 참가자는 없다. 이는 시스템 프로세스가 어떤 프로세스가 어떤 데이터를 커밋했는지 동의하게 하는데 필요하다. 2개 파드를 동일 순번으로 시작하였다면 두 대의 ZooKeeper 서버는 둘 다 스스로를 동일 서버로 식별한다.

합의 프로토콜에서 각 참여자의 식별자는 고유해야 한다. Zab 프로토콜에 두 참여자가 동일한 고유 식별자로 요청해서는 안된다. 이는 시스템 프로세스가 어떤 프로세스가 어떤 데이터를 커밋했는지 동의하도록 하기 위해 필수적이다. 동일 순번으로 두 개의 파드가 실행했다면 두 ZooKeeper 서버는 모두 동일한 서버로 식별된다.

kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

각 파드의 A 레코드는 파드가 Ready 상태가 되면 입력된다. 따라서 ZooKeeper 서버의 FQDN은 단일 엔드포인트로 확인되고 해당 엔드포인트는 myid 파일에 구성된 식별자를 가진 고유한 ZooKeeper 서버가 된다.

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

이것은 ZooKeeper의 zoo.cfg 파일에 servers 속성이 정확히 구성된 앙상블로 나타나는 것을 보증한다.

server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

서버가 Zab 프로토콜로 값을 커밋 시도하면, 합의를 이루어 값을 커밋하거나(리더 선출에 성공했고 나머지 두 개 파드도 Running과 Ready 상태라면) 실패한다(조건 중 하나라도 충족하지 않으면). 다른 서버를 대신하여 쓰기를 승인하는 상태는 발생하지 않는다.

앙상블 무결성 테스트

가장 기본적인 테스트는 한 ZooKeeper 서버에 데이터를 쓰고 다른 ZooKeeper 서버에서 데이터를 읽는 것이다.

아래 명령어는 앙상블 내에 zk-0 파드에서 /hello 경로로 world를 쓰는 스크립트인 zkCli.sh를 실행한다.

kubectl exec zk-0 -- zkCli.sh create /hello world
WATCHER::

WatchedEvent state:SyncConnected type:None path:null
Created /hello

zk-1 파드에서 데이터를 읽기 위해 다음 명령어를 이용하자.

kubectl exec zk-1 -- zkCli.sh get /hello

zk-0에서 생성한 그 데이터는 앙상블 내에 모든 서버에서 사용할 수 있다.

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

내구성있는 저장소 제공

ZooKeeper 기본 섹션에서 언급했듯이 ZooKeeper는 모든 항목을 내구성있는 WAL에 커밋하고 메모리 상태의 스냅샷을 저장 미디에에 주기적으로 저장한다. 내구성을 제공하기 위해 WAL을 이용하는 것은 복제된 상태 머신을 이루는 합의 프로토콜에서 이용하는 일반적인 기법이다.

kubectl delete 명령을 이용하여 zk 스테이트풀셋을 삭제하자.

kubectl delete statefulset zk
statefulset.apps "zk" deleted

스테이트풀셋의 파드가 종료되는 것을 지켜보자.

kubectl get pods -w -l app=zk

zk-0이 완전히 종료되면 CTRL-C를 이용해 kubectl을 종료하자.

zk-2      1/1       Terminating   0         9m
zk-0      1/1       Terminating   0         11m
zk-1      1/1       Terminating   0         10m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m

zookeeper.yaml 매니페스트를 다시 적용한다.

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

zk 스테이트풀셋 오브젝트를 생성하지만, 매니페스트에 다른 API 오브젝트는 이미 존재하므로 수정되지 않는다.

스테이트풀셋 컨트롤러가 스테트풀셋의 파드를 재생성하는 것을 확인한다.

kubectl get pods -w -l app=zk

zk-2 파드가 Running과 Ready가 되면 CTRL-C를 이용하여 kubectl을 종료한다.

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

아래 명령어로 무결성 테스트에서 입력한 값을 zk-2 파드에서 얻어온다.

kubectl exec zk-2 zkCli.sh get /hello

zk 스테이트풀셋의 모든 파드를 종료하고 재생성했음에도, 앙상블은 여전히 원래 값을 돌려준다.

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

zk 스테이트풀셋의 specvolumeClaimTemplates 필드는 각 파드에 프로비전될 퍼시스턴트볼륨을 지정한다.

volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi

스테이트풀셋 컨트롤러는 스테이트풀셋의 각 파드에 대한 퍼시스턴트볼륨클레임을 생성한다.

다음 명령어를 이용하여 스테이트풀셋퍼시스턴트볼륨클레임을 살펴보자.

kubectl get pvc -l app=zk

스테이트풀셋의 파드를 재생성할 때에 파드의 퍼시스턴트볼륨도 다시 마운트한다.

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
datadir-zk-0   Bound     pvc-bed742cd-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-1   Bound     pvc-bedd27d2-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-2   Bound     pvc-bee0817e-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h

스테이트풀셋의 컨테이너 templatevolumeMounts 부분이 ZooKeeper 서버의 데이터 디렉터리에 퍼시스턴트볼륨 마운트하는 내용이다.

volumeMounts:
- name: datadir
  mountPath: /var/lib/zookeeper

zk 스테이트풀셋이 (재)스케줄링될 때 항상 동일한 퍼시스턴트볼륨을 ZooKeeper의 서버 디렉터리에 마운트한다. 파드를 재스케줄할 때에도 ZooKeeper의 WAL을 통해 이뤄진 모든 쓰기와 모든 그 스냅샷도 내구성을 유지한다.

일관된 구성 보장하기

리더 선출 촉진합의 달성 섹션에서 알렸듯이, ZooKeeper 앙상블에 서버는 리더 선출과 쿼럼을 구성하기 위한 일관된 설정이 필요하다. 또한 Zab 프로토콜의 일관된 설정도 네트워크에 걸쳐 올바르게 동작하기 위해서 필요하다. 이 예시에서는 매니페스트에 구성을 직접 포함시켜서 일관된 구성을 달성한다.

zk 스테이트풀셋을 살펴보자.

kubectl get sts zk -o yaml
…
command:
      - sh
      - -c
      - "start-zookeeper \
        --servers=3 \
        --data_dir=/var/lib/zookeeper/data \
        --data_log_dir=/var/lib/zookeeper/data/log \
        --conf_dir=/opt/zookeeper/conf \
        --client_port=2181 \
        --election_port=3888 \
        --server_port=2888 \
        --tick_time=2000 \
        --init_limit=10 \
        --sync_limit=5 \
        --heap=512M \
        --max_client_cnxns=60 \
        --snap_retain_count=3 \
        --purge_interval=12 \
        --max_session_timeout=40000 \
        --min_session_timeout=4000 \
        --log_level=INFO"
…

ZooKeeper 서버를 시작하는데 사용한 명령어는 커맨드라인 파라미터로 환경 구성을 전달했다. 환경 변수를 이용하여서도 앙상블에 환경 구성을 전달할 수 있다.

로깅 설정하기

zkGenConfig.sh 스크립트로 생성된 파일 중 하나는 ZooKeeper의 로깅을 제어한다. ZooKeeper는 Log4j를 이용하며 기본 로깅 구성으로는 시간과 파일 크기 기준의 롤링 파일 어펜더를 사용한다.

zk 스테이트풀셋의 한 파드에서 로깅 설정을 살펴보는 아래 명령어를 이용하자.

kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties

아래 로깅 구성은 ZooKeeper가 모든 로그를 표준 출력 스트림으로 처리하게 한다.

zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n

이는 컨테이너 내에서 안전하게 로깅하는 가장 단순한 방법이다. 표준 출력으로 애플리케이션 로그를 작성하면, 쿠버네티스는 로그 로테이션을 처리한다. 또한 쿠버네티스는 애플리케이션이 표준 출력과 표준 오류에 쓰인 로그로 인하여 로컬 저장 미디어가 고갈되지 않도록 보장하는 정상적인 보존 정책을 구현한다.

파드의 마지막 20줄의 로그를 가져오는 kubectl logs 명령을 이용하자.

kubectl logs zk-0 --tail 20

kubectl logs를 이용하거나 쿠버네티스 대시보드에서 표준 출력과 표준 오류로 쓰인 애플리케이션 로그를 볼 수 있다.

2016-12-06 19:34:16,236 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO  [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO  [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO  [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)

쿠버네티스는 많은 로그 솔루션과 통합된다. 클러스터와 애플리케이션에 가장 적합한 로그 솔루션을 선택할 수 있다. 클러스터 수준의 로그 적재(ship)와 통합을 위해서는 로그 순환과 적재를 위해 사이드카 컨테이너를 배포하는 것을 고려한다.

권한 없는 사용자를 위해 구성하기

컨테이너 내부의 권한있는 유저로 애플리케이션을 실행할 수 있도록 하는 최상의 방법은 논쟁거리이다. 조직에서 애플리케이션을 권한 없는 사용자가 실행한다면, 진입점을 실행할 사용자를 제어하기 위해 시큐리티컨텍스트를 이용할 수 있다.

zk 스테이트풀셋의 파드 templateSecurityContext를 포함한다.

securityContext:
  runAsUser: 1000
  fsGroup: 1000

파드 컨테이너에서 UID 1000은 ZooKeeper 사용자이며, GID 1000은 ZooKeeper의 그룹에 해당한다.

zk-0 파드에서 프로세스 정보를 얻어오자.

kubectl exec zk-0 -- ps -elf

securityContext 오브젝트의 runAsUser 필드 값이 1000 이므로 루트 사용자로 실행하는 대신 ZooKeeper 프로세스는 ZooKeeper 사용자로 실행된다.

F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S zookeep+     1     0  0  80   0 -  1127 -      20:46 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+    27     1  0  80   0 - 1155556 -    20:46 ?        00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

기본적으로 파드의 퍼시스턴트볼륨은 ZooKeeper 서버의 데이터 디렉터리에 마운트되고, 루트 사용자만이 접근 가능하다. 이 구성은 ZooKeeper 프로세스가 WAL에 기록하고 스냅샷을 저장하는 것을 방지한다.

zk-0 파드의 ZooKeeper 데이터 디렉터리의 권한을 얻어오는 아래 명령어를 이용하자.

kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data

securityContext 오브젝트의 fsGroup 필드 값이 1000 이므로, 파드의 퍼시스턴트 볼륨의 소유권은 ZooKeeper 그룹으로 지정되어 ZooKeeper 프로세스에서 읽고 쓸 수 있다.

drwxr-sr-x 3 zookeeper zookeeper 4096 Dec  5 20:45 /var/lib/zookeeper/data

ZooKeeper 프로세스 관리하기

ZooKeeper 문서에서는 "ZooKeeper의 서버 프로세스(JVM)을 관리할 감독 프로세스를 필요할 것이다."라고 말한다. 와치독(감독 프로세스)를 활용하여 실패한 프로세스를 재시작하는 것은 분산시스템에서 일반적인 방식이다. 쿠버네티스에서 애플리케이션을 배포할 때에는 감독 프로세스로 외부 유틸리티를 사용하기보다 쿠버네티스를 애플리케이션의 와치독으로서 사용해야 한다.

앙상블 관리하기

zk 스테이트풀셋RollingUpdate 업데이트 전략을 이용하도록 구성되었다.

kubectl patch로 서버에 할당된 cpu 수를 갱신할 수 있다.

kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset.apps/zk patched

업데이트 상황을 지켜보기 위해 kubectl rollout status 이용하자.

kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...

이것은 파드를 역순으로 한 번에 하나씩 종료하고, 새로운 구성으로 재생성한다. 이는 롤링업데이트 동안에 쿼럼을 유지하도록 보장한다.

이력과 이전 구성을 보기 위해 kubectl rollout history 명령을 이용하자.

kubectl rollout history sts/zk
statefulsets "zk"
REVISION
1
2

수정사항을 롤백하기 위해 kubectl rollout undo 명령을 이용하자.

kubectl rollout undo sts/zk
statefulset.apps/zk rolled back

프로세스 장애 관리하기

재시작 정책은 쿠버네티스가 파드 내에 컨테이너의 진입점에서 프로세스 실패를 어떻게 다루는지 제어한다. 스테이트풀셋의 파드에서 오직 적절한 재시작 정책는 Always이며 이것이 기본 값이다. 상태가 유지되는 애플리케이션을 위해 기본 정책을 절대로 변경하지 말자.

zk-0 파드에서 실행 중인 ZooKeeper 서버에서 프로세스 트리를 살펴보기 위해 다음 명령어를 이용하자.

kubectl exec zk-0 -- ps -ef

컨테이너의 엔트리 포인트로 PID 1 인 명령이 사용되었으며 ZooKeeper 프로세스는 엔트리 포인트의 자식 프로세스로 PID 27 이다.

UID        PID  PPID  C STIME TTY          TIME CMD
zookeep+     1     0  0 15:03 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
zookeep+    27     1  0 15:03 ?        00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

다른 터미널에서 다음 명령어로 zk 스테이트풀셋의 파드를 확인한다.

kubectl get pod -w -l app=zk

또 다른 터미널에서 다음 명령어로 zk-0 파드의 ZooKeeper 프로세스를 종료시킨다.

kubectl exec zk-0 -- pkill java

ZooKeeper 프로세스의 종료는 부모 프로세스의 종료를 일으킨다. 컨테이너 재시작정책이 Always이기 때문에 부모 프로세스를 재시작했다.

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          21m
zk-1      1/1       Running   0          20m
zk-2      1/1       Running   0          19m
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Error     0          29m
zk-0      0/1       Running   1         29m
zk-0      1/1       Running   1         29m

애플리케이션이 스크립트(zkServer.sh 같은)를 애플리케이션의 비지니스 로직을 구현한 프로세스를 시작하기 위해 이용한다면, 그 스크립트는 자식 프로세스와 함께 반드시 종료되어야 한다. 이는 쿠버네티스가 애플리케이션의 비지니스 로직을 구현한 프로세스가 실패할 때에 애플리케이션 컨테이너를 재시작하는 것을 보증한다.

활성도(Liveness) 테스트하기

실패한 애플리케이션을 재시작하도록 구성하는 것은 분산 시스템을 건강하게 유지하는데 충분하지 않다. 시스템의 프로세스는 살아있지만 응답이 없을 수 있고, 혹은 다른 건강하지 않은 경우의 시나리오가 있다. 애플리케이션 프로세스가 건강하지 않고 재시작해야만 한다는 것을 쿠버네티스에게 알리도록 활성도 검사를 이용해야 한다.

zk 스테이트풀셋에 파드 template에 활성도 검사를 명시한다.

 livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 15
          timeoutSeconds: 5

검사는 ZooKeeper의 ruok 4 글자 단어를 이용해서 서버의 건강을 테스트하는 배쉬 스크립트를 호출한다.

OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
    exit 0
else
    exit 1
fi

한 터미널에서 zk 스테이트풀셋의 파드를 지켜보기 위해 다음 명령어를 이용하자.

kubectl get pod -w -l app=zk

다른 창에서 zk-0 파드의 파일시스템에서 zookeeper-ready 스크립트를 삭제하기 위해 다음 명령어를 이용하자.

kubectl exec zk-0 -- rm /usr/bin/zookeeper-ready

ZooKeeper의 활성도 검사에 실패하면, 쿠버네티스는 자동으로 프로세스를 재시작하여 앙상블에 건강하지 않은 프로세스를 재시작하는 것을 보증한다.

kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Running   0          1h
zk-0      0/1       Running   1         1h
zk-0      1/1       Running   1         1h

준비도 테스트

준비도는 활성도와 동일하지 않다. 프로세스가 살아 있다면, 스케줄링되고 건강하다. 프로세스가 준비되면 입력을 처리할 수 있다. 활성도는 필수적이나 준비도의 조건으로는 충분하지 않다. 몇몇 경우 특별히 초기화와 종료 시에 프로세스는 살아있지만 준비되지 않을 수 있다.

준비도 검사를 지정하면, 쿠버네티스는 준비도가 통과할 때까지 애플리케이션 프로세스가 네트워크 트래픽을 수신하지 않게 한다.

ZooKeeper 서버에서는 준비도가 활성도를 내포한다. 그러므로 zookeeper.yaml 매니페스트에서 준비도 검사는 활성도 검사와 동일하다.

  readinessProbe:
    exec:
      command:
      - sh
      - -c
      - "zookeeper-ready 2181"
    initialDelaySeconds: 15
    timeoutSeconds: 5

활성도와 준비도 검사가 동일함에도 둘 다 지정하는 것은 중요하다. 이는 ZooKeeper 앙상블에 건강한 서버만 아니라 네트워크 트래픽을 수신하는 것을 보장한다.

노드 실패 방지

ZooKeeper는 변조된 데이터를 성공적으로 커밋하기 위한 서버의 쿼럼이 필요하다. 3개의 서버 앙상블에서 성공적으로 저장하려면 2개 서버는 반드시 건강해야 한다. 쿼럼 기반 시스템에서, 멤버는 가용성을 보장하는 실패 영역에 걸쳐 배포된다. 중단을 방지하기 위해 개별 시스템의 손실로 인해 모범 사례에서는 동일한 시스템에 여러 인스턴스의 응용 프로그램을 함께 배치하는 것을 배제한다.

기본적으로 쿠버네티스는 동일 노드상에 스테이트풀셋의 파드를 위치시킬 수 있다. 생성한 3개의 서버 앙상블에서 2개의 서버가 같은 노드에 있다면, 그 노드는 실패하고 ZooKeeper 서비스 클라이언트는 그 파드들의 최소 하나가 재스케줄링될 때까지 작동 중단을 경험할 것이다.

노드 실패하는 사건 중에도 중요 시스템의 프로세스가 재스케줄될 수 있게 항상 추가적인 용량을 프로비전해야 한다. 그렇게 하면 쿠버네티스 스케줄러가 ZooKeeper 서버 하나를 다시 스케줄하는 동안까지만 작동 중단될 것이다. 그러나 서비스에서 노드 실패로 인한 다운타임을 방지하려 한다면, 파드안티어피니티를 설정해야 한다.

zk 스테이트풀셋의 파드의 노드를 알아보기 위해 다음 명령어를 이용하자.

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done

zk 스테이트풀셋에 모든 파드는 다른 노드에 배포된다.

kubernetes-node-cxpk
kubernetes-node-a5aq
kubernetes-node-2g2d

이는 zk 스테이트풀셋의 파드에 파드안티어피니티(PodAntiAffinity)를 지정했기 때문이다.

      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

requiredDuringSchedulingIgnoredDuringExecution 필드는 쿠버네티스 스케줄러에 topologyKey로 정의된 도메인에서 appzk라고 레이블링된 두 개 파드가 위치하지 않도록 한다. topologyKey kubernetes.io/hostname은 도메인이 개별 노드임을 나타낸다. 다른 규칙과 레이블, 셀렉터를 사용하여 앙상블을 물리적인, 네트워크, 전원 장애 분야에 걸쳐 확산하도록 이 기법을 확장할 수 있다.

생존 유지

이 섹션에서는 노드를 통제(cordon)하고 비운다(drain). 공유된 클러스터에서 이 튜토리얼을 진행한다면, 다른 테넌트에 부정적인 영향을 비치지 않음을 보증해야 한다.

이전 섹션은 계획되지 않은 노드 실패에서 살아남도록 어떻게 파드를 확산할 것인가에 대해 알아보았다. 그러나 계획된 점검으로 인해 발생하는 일시적인 노드 실패에 대한 계획도 필요하다.

클러스터에서 다음 명령으로 노드를 살펴보자.

kubectl get nodes

kubectl cordon을 이용하여 클러스터 내에 4개 노드를 제외하고 다른 모든 노드를 통제해보자.

kubectl cordon <노드-이름>

zk-pdb PodDisruptionBudget을 살펴보고자 이 명령어를 이용하자.

kubectl get pdb zk-pdb

max-unavailable 필드는 쿠버네티스가 zk 스테이트풀셋에서 최대 1개의 파드는 언제든지 가용하지 않을 수 있음을 나타낸다.

NAME      MIN-AVAILABLE   MAX-UNAVAILABLE   ALLOWED-DISRUPTIONS   AGE
zk-pdb    N/A             1                 1

한 터미널에서 zk 스테이트풀셋의 파드를 지켜보는 이 명령어를 이용하자.

kubectl get pods -w -l app=zk

다른 터미널에서 현재 스케줄되는 파드의 노드를 살펴보자.

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
kubernetes-node-pb41
kubernetes-node-ixsl
kubernetes-node-i4c4

zk-0파드가 스케줄되는 노드를 통제하기 위해 kubectl drain를 이용하자.

kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
node "kubernetes-node-group-pb41" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-group-pb41, kube-proxy-kubernetes-node-group-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz
pod "zk-0" deleted
node "kubernetes-node-group-pb41" drained

클러스터에 4개 노드가 있기 때문에 kubectl drain이 성공하여 zk-0을 다른 노드로 재스케줄링 된다.

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m

계속해서 스테이트풀셋의 파드를 첫 터미널에서 지켜보고 zk-1 이 스케줄된 노드를 비워보자.

kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data "kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained

zk-1 파드는 스케줄되지 않는데 이는 zk StatefulSet이 오직 2개 노드가 스케줄되도록 파드를 위치시키는 것을 금하는 PodAntiAffinity 규칙을 포함하였기 때문이고 그 파드는 Pending 상태로 남을 것이다.

kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s

계속해서 스테이트풀셋의 파드를 지켜보고 zk-2가 스케줄된 노드를 비워보자.

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
node "kubernetes-node-i4c4" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2

kubectl 을 종료하기 위해 CTRL-C를 이용하자.

zk-2를 추출하는 것은 zk-budget을 위반하기 때문에 셋째 노드를 비울 수 없다. 그러나 그 노드는 통제 상태로 남는다.

zk-0에서 온전성 테스트 때에 입력한 값을 가져오는 zkCli.sh를 이용하자.

kubectl exec zk-0 zkCli.sh get /hello

PodDisruptionBudget이 존중되기 때문에 서비스는 여전히 가용하다.

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

kubectl uncordon 이용하여 첫 노드의 통제를 풀자.

kubectl uncordon kubernetes-node-pb41
node "kubernetes-node-pb41" uncordoned

zk-1은 이 노드에서 재스케줄된다. zk-1이 Running과 Ready가 될 때까지 기다리자.

kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         12m
zk-1      0/1       ContainerCreating   0         12m
zk-1      0/1       Running   0         13m
zk-1      1/1       Running   0         13m

zk-2가 스케줄된 노드를 비워보자.

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

출력은

node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained

이번엔 kubectl drain 이 성공한다.

zk-2가 재스케줄되도록 두 번째 노드의 통제를 풀어보자.

kubectl uncordon kubernetes-node-ixsl
node "kubernetes-node-ixsl" uncordoned

kubectl drainPodDisruptionBudget과 결합하면 유지보수 중에도 서비스를 가용하게 할 수 있다. drain으로 노드를 통제하고 유지보수를 위해 노드를 오프라인하기 전에 파드를 추출하기 위해 사용한다면 서비스는 혼란 예산을 표기한 서비스는 그 예산이 존중은 존중될 것이다. 파드가 즉각적으로 재스케줄 할 수 있도록 항상 중요 서비스를 위한 추가 용량을 할당해야 한다.

정리하기

  • kubectl uncordon은 클러스터 내에 모든 노드를 통제 해제한다.
  • 반드시 이 튜토리얼에서 사용한 퍼시스턴트 볼륨을 위한 퍼시스턴트 스토리지 미디어를 삭제하자. 귀하의 환경과 스토리지 구성과 프로비저닝 방법에서 필요한 절차를 따라서 모든 스토리지가 재확보되도록 하자.

5.6 - 클러스터

5.6.1 - AppArmor를 사용하여 리소스에 대한 컨테이너의 접근 제한

FEATURE STATE: Kubernetes v1.4 [beta]

AppArmor는 표준 리눅스 사용자와 그룹 기반의 권한을 보완하여, 한정된 리소스 집합으로 프로그램을 제한하는 리눅스 커널 보안 모듈이다. AppArmor는 임의의 애플리케이션에 대해서 잠재적인 공격 범위를 줄이고 더욱 심층적인 방어를 제공하도록 구성할 수 있다. 이 기능은 특정 프로그램이나 컨테이너에서 필요한 리눅스 기능, 네트워크 사용, 파일 권한 등에 대한 접근을 허용하는 프로파일로 구성한다. 각 프로파일은 허용하지 않은 리소스 접근을 차단하는 강제(enforcing) 모드 또는 위반만을 보고하는 불평(complain) 모드로 실행할 수 있다.

AppArmor를 이용하면 컨테이너가 수행할 수 있는 작업을 제한하고 또는 시스템 로그를 통해 더 나은 감사를 제공하여 더 안전한 배포를 실행할 수 있다. 그러나 AppArmor가 은탄환(언제나 통하는 무적의 방법)이 아니며, 애플리케이션 코드 취약점을 보호하기 위한 여러 조치를 할 수 있는 것 뿐임을 잊으면 안된다. 양호하고 제한적인 프로파일을 제공하고, 애플리케이션과 클러스터를 여러 측면에서 강화하는 것이 중요하다.

목적

  • 노드에 프로파일을 어떻게 적재하는지 예시를 본다.
  • 파드에 프로파일을 어떻게 강제 적용하는지 배운다.
  • 프로파일이 적재되었는지 확인하는 방법을 배운다.
  • 프로파일을 위반하는 경우를 살펴본다.
  • 프로파일을 적재할 수 없을 경우를 살펴본다.

시작하기 전에

다음을 보장해야 한다.

  1. 쿠버네티스 버전은 최소 1.4 이다. -- 쿠버네티스 v1.4부터 AppArmor 지원을 추가했다. v1.4 이전 쿠버네티스 컴포넌트는 새로운 AppArmor 어노테이션을 인식하지 못하고 제공되는 AppArmor 설정을 조용히 무시할 것이다. 파드에서 예상하는 보호를 받고 있는지 확인하려면 해당 노드의 Kubelet 버전을 확인하는 것이 중요하다.

    $ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.kubeletVersion}\n{end}'
    
    gke-test-default-pool-239f5d02-gyn2: v1.4.0
    gke-test-default-pool-239f5d02-x1kf: v1.4.0
    gke-test-default-pool-239f5d02-xwux: v1.4.0
    
  2. AppArmor 커널 모듈을 사용 가능해야 한다. -- 리눅스 커널에 AppArmor 프로파일을 강제 적용하기 위해 AppArmor 커널 모듈은 반드시 설치되어 있고 사용 가능해야 한다. 예를 들어 Ubuntu 및 SUSE 같은 배포판은 모듈을 기본값으로 지원하고, 그 외 많은 다른 배포판들은 선택적으로 지원한다. 모듈이 사용 가능한지 확인하려면 /sys/module/apparmor/parameters/enabled 파일을 확인한다.

    $ cat /sys/module/apparmor/parameters/enabled
    Y
    

    Kubelet(>=v1.4)이 AppArmor 기능 지원을 포함하지만, 커널 모듈을 사용할 수 없으면 파드에서 AppArmor 옵션을 실행하는 것이 거부된다.

  1. 컨테이너 런타임이 AppArmor을 지원한다. -- 현재 모든 일반적인 쿠버네티스를 지원하는 도커(Docker), CRI-O 또는 containerd 와 같은 컨테이너 런타임들은 AppArmor를 지원해야 한다. 이 런타임 설명서를 참조해서 클러스터가 AppArmor를 사용하기 위한 요구 사항을 충족하는지 확인해야 한다.

  2. 프로파일이 적재되어 있다. -- AppArmor는 각 컨테이너와 함께 실행해야 하는 AppArmor 프로파일을 지정하여 파드에 적용한다. 커널에 지정한 프로파일이 적재되지 않았다면, Kubelet(>= v1.4)은 파드를 거부한다. 해당 노드에 어떤 프로파일이 적재되었는지는 /sys/kernel/security/apparmor/profiles 파일을 통해 확인할 수 있다. 예를 들어,

    $ ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort"
    
    apparmor-test-deny-write (enforce)
    apparmor-test-audit-write (enforce)
    docker-default (enforce)
    k8s-nginx (enforce)
    

    노드에 프로파일을 적재하는 것에 대해 더 자세한 내용은 프로파일과 함께 노드 설정하기.

AppArmor 지원이 포함된 Kubelet (>= v1.4)이면 어떤 전제 조건이 충족되지 않으면 AppArmor와 함께한 파드를 거부한다. 노드 상에 AppArmor 지원 여부는 노드 준비 조건 메시지를 확인하여(이후 릴리스에서는 삭제될 것 같지만) 검증할 수 있다.

kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}\n{end}'
gke-test-default-pool-239f5d02-gyn2: kubelet is posting ready status. AppArmor enabled
gke-test-default-pool-239f5d02-x1kf: kubelet is posting ready status. AppArmor enabled
gke-test-default-pool-239f5d02-xwux: kubelet is posting ready status. AppArmor enabled

파드 보안 강화하기

AppArmor 프로파일은 컨테이너마다 지정된다. 함께 실행할 파드 컨테이너에 AppArmor 프로파일을 지정하려면 파드의 메타데이터에 어노테이션을 추가한다.

container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>

<container_name>은 프로파일을 적용하는 컨테이너 이름이고, <profile_ref>는 적용할 프로파일을 지정한다. profile_ref는 다음 중에 하나이다.

  • 런타임의 기본 프로파일을 적용하기 위한 runtime/default
  • <profile_name>로 이름한 호스트에 적재되는 프로파일을 적용하기 위한 localhost/<profile_name>
  • 적재할 프로파일이 없음을 나타내는 unconfined

어노테이션과 프로파일 이름 형식의 자세한 내용은 API 참조를 살펴본다.

쿠버네티스 AppArmor 의 작동 순서는 모든 선행 조건이 충족되었는지 확인하고, 적용을 위해 선택한 프로파일을 컨테이너 런타임으로 전달하여 이루어진다. 만약 선행 조건이 충족되지 않으면 파드는 거부되고 실행되지 않는다.

프로파일이 적용되었는지 확인하기 위해, 컨테이너 생성 이벤트에 나열된 AppArmor 보안 옵션을 찾아 볼 수 있다.

kubectl get events | grep Created
22s        22s         1         hello-apparmor     Pod       spec.containers{hello}   Normal    Created     {kubelet e2e-test-stclair-node-pool-31nt}   Created container with docker id 269a53b202d3; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]apparmor=k8s-apparmor-example-deny-write]

컨테이너의 루트 프로세스가 올바른 프로파일로 실행되는지는 proc attr을 확인하여 직접 검증할 수 있다.

kubectl exec <pod_name> cat /proc/1/attr/current
k8s-apparmor-example-deny-write (enforce)

예시

이 예시는 AppArmor를 지원하는 클러스터를 이미 구성하였다고 가정한다.

먼저 노드에서 사용하려는 프로파일을 적재해야 한다. 사용할 프로파일은 파일 쓰기를 거부한다.

#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

파드를 언제 스케줄할지 알지 못하므로 모든 노드에 프로파일을 적재해야 한다. 이 예시에서는 SSH를 이용하여 프로파일을 설치할 것이나 다른 방법은 프로파일과 함께 노드 설정하기에서 논의한다.

NODES=(
    # The SSH-accessible domain names of your nodes
    gke-test-default-pool-239f5d02-gyn2.us-central1-a.my-k8s
    gke-test-default-pool-239f5d02-x1kf.us-central1-a.my-k8s
    gke-test-default-pool-239f5d02-xwux.us-central1-a.my-k8s)
for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}
EOF'
done

다음으로 쓰기 금지 프로파일된 "Hello AppArmor" 파드를 실행한다.

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # 쿠버네티스에 'k8s-apparmor-example-deny-write' AppArmor 프로파일을 적용함을 알린다.
    # 잊지 말 것은 쿠버네티스 노드에서 실행 중인 버전이 1.4 이상이 아닌 경우에는 이 설정은 무시된다는 것이다.
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
kubectl create -f ./hello-apparmor.yaml

파드 이벤트를 살펴보면, 'k8s-apparmor-example-deny-write' AppArmor 프로파일로 생성된 파드 컨테이너를 확인할 수 있다.

kubectl get events | grep hello-apparmor
14s        14s         1         hello-apparmor   Pod                                Normal    Scheduled   {default-scheduler }                           Successfully assigned hello-apparmor to gke-test-default-pool-239f5d02-gyn2
14s        14s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Pulling     {kubelet gke-test-default-pool-239f5d02-gyn2}   pulling image "busybox"
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Pulled      {kubelet gke-test-default-pool-239f5d02-gyn2}   Successfully pulled image "busybox"
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Created     {kubelet gke-test-default-pool-239f5d02-gyn2}   Created container with docker id 06b6cd1c0989; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Started     {kubelet gke-test-default-pool-239f5d02-gyn2}   Started container with docker id 06b6cd1c0989

proc attr을 확인하여 컨테이너가 실제로 해당 프로파일로 실행 중인지 확인할 수 있다.

kubectl exec hello-apparmor cat /proc/1/attr/current
k8s-apparmor-example-deny-write (enforce)

마지막으로 파일 쓰기를 통해 프로파일을 위반하면 어떻게 되는지 확인할 수 있다.

kubectl exec hello-apparmor touch /tmp/test
touch: /tmp/test: Permission denied
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1

이제 정리하면서, 적재되지 않은 프로파일을 지정하면 어떻게 되는지 살펴본다.

kubectl create -f /dev/stdin <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor-2
  annotations:
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
EOF
pod/hello-apparmor-2 created
kubectl describe pod hello-apparmor-2
Name:          hello-apparmor-2
Namespace:     default
Node:          gke-test-default-pool-239f5d02-x1kf/
Start Time:    Tue, 30 Aug 2016 17:58:56 -0700
Labels:        <none>
Annotations:   container.apparmor.security.beta.kubernetes.io/hello=localhost/k8s-apparmor-example-allow-write
Status:        Pending
Reason:        AppArmor
Message:       Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
IP:
Controllers:   <none>
Containers:
  hello:
    Container ID:
    Image:     busybox
    Image ID:
    Port:
    Command:
      sh
      -c
      echo 'Hello AppArmor!' && sleep 1h
    State:              Waiting
      Reason:           Blocked
    Ready:              False
    Restart Count:      0
    Environment:        <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-dnz7v (ro)
Conditions:
  Type          Status
  Initialized   True
  Ready         False
  PodScheduled  True
Volumes:
  default-token-dnz7v:
    Type:    Secret (a volume populated by a Secret)
    SecretName:    default-token-dnz7v
    Optional:   false
QoS Class:      BestEffort
Node-Selectors: <none>
Tolerations:    <none>
Events:
  FirstSeen    LastSeen    Count    From                        SubobjectPath    Type        Reason        Message
  ---------    --------    -----    ----                        -------------    --------    ------        -------
  23s          23s         1        {default-scheduler }                         Normal      Scheduled     Successfully assigned hello-apparmor-2 to e2e-test-stclair-minion-group-t1f5
  23s          23s         1        {kubelet e2e-test-stclair-node-pool-t1f5}             Warning        AppArmor    Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded

파드 상태는 Pending이며, 오류 메시지는 Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded이다. 이벤트도 동일한 메시지로 기록되었다.

관리

프로파일과 함께 노드 설정하기

현재 쿠버네티스는 AppArmor 프로파일을 노드에 적재하기 위한 네이티브 메커니즘을 제공하지 않는다. 프로파일을 설정하는 여러 방법이 있다. 예를 들면 다음과 같다.

  • 각 노드에서 파드를 실행하는 데몬셋을 통해서 올바른 프로파일이 적재되었는지 확인한다. 예시 구현은 여기에서 찾아볼 수 있다.
  • 노드 초기화 시간에 노드 초기화 스크립트(예를 들어 Salt, Ansible 등)나 이미지를 이용
  • 예시에서 보여준 것처럼, 프로파일을 각 노드에 복사하고 SSH를 통해 적재한다.

스케줄러는 어떤 프로파일이 어떤 노드에 적재되는지 고려하지 않으니, 프로파일 전체 집합이 모든 노드에 적재되어야 한다. 대안적인 방법은 각 프로파일(혹은 프로파일의 클래스)을 위한 노드 레이블을 노드에 추가하고, 노드 셀렉터를 이용하여 파드가 필요한 프로파일이 있는 노드에서 실행되도록 한다.

파드시큐리티폴리시(PodSecurityPolicy)로 프로파일 제한하기

만약 파드시큐리티폴리시 확장을 사용하면, 클러스터 단위로 AppArmor 제한을 적용할 수 있다. 파드시큐리티폴리시를 사용하려면 위해 다음의 플래그를 반드시 apiserver에 설정해야 한다.

--enable-admission-plugins=PodSecurityPolicy[,others...]

AppArmor 옵션은 파드시큐리티폴리시의 어노테이션으로 지정할 수 있다.

apparmor.security.beta.kubernetes.io/defaultProfileName: <profile_ref>
apparmor.security.beta.kubernetes.io/allowedProfileNames: <profile_ref>[,others...]

기본 프로파일 이름 옵션은 프로파일을 지정하지 않았을 때에 컨테이너에 기본으로 적용하는 프로파일을 지정한다. 허용하는 프로파일 이름 옵션은 파드 컨테이너가 함께 실행하도록 허용되는 프로파일 목록을 지정한다. 두 옵션을 모두 사용하는 경우, 기본값은 반드시 필요하다. 프로파일은 컨테이너에서 같은 형식으로 지정된다. 전체 사양은 API 참조를 찾아본다.

AppArmor 비활성화

클러스터에서 AppArmor 사용하지 않으려면, 커맨드라인 플래그로 비활성화 할 수 있다.

--feature-gates=AppArmor=false

비활성화되면 AppArmor 프로파일을 포함한 파드는 "Forbidden" 오류로 검증 실패한다. 기본적으로 도커는 항상 비특권 파드에 "docker-default" 프로파일을 활성화하고(AppArmor 커널모듈이 활성화되었다면), 기능 게이트가 비활성화된 것처럼 진행한다. AppArmor를 비활성화하는 이 옵션은 AppArmor가 일반 사용자 버전이 되면 제거된다.

AppArmor와 함께 쿠버네티스 1.4로 업그레이드 하기

클러스터 버전을 v1.4로 업그레이드하기 위해 AppArmor쪽 작업은 없다. 그러나 AppArmor 어노테이션을 가진 파드는 유효성 검사(혹은 파드시큐리티폴리시 승인)을 거치지 않는다. 그 노드에 허용 프로파일이 로드되면, 악의적인 사용자가 허가 프로필을 미리 적용하여 파드의 권한을 docker-default 보다 높일 수 있다. 이것이 염려된다면 apparmor.security.beta.kubernetes.io 어노테이션이 포함된 모든 파드의 클러스터를 제거하는 것이 좋다.

일반 사용자 버전으로 업그레이드 방법

AppArmor는 일반 사용자 버전(general available)으로 준비되면 현재 어노테이션으로 지정되는 옵션은 필드로 변경될 것이다. 모든 업그레이드와 다운그레이드 방법은 전환을 통해 지원하기에는 매우 미묘하니 전환이 필요할 때에 상세히 설명할 것이다. 최소 두 번의 릴리스에 대해서는 필드와 어노테이션 모두를 지원할 것이고, 그 이후부터는 어노테이션은 명확히 거부된다.

프로파일 제작

AppArmor 프로파일을 만들고 올바르게 지정하는 것은 매우 까다로울 수 있다. 다행히 이 작업에 도움 되는 도구가 있다.

  • aa-genprofaa-logprof는 애플리케이션 활동과 로그와 수행에 필요한 행동을 모니터링하여 일반 프로파일 규칙을 생성한다. 자세한 사용방법은 AppArmor 문서에서 제공한다.
  • bane은 단순화된 프로파일 언어를 이용하는 도커를 위한 AppArmor 프로파일 생성기이다.

개발 장비의 도커를 통해 애플리케이션을 실행하여 프로파일을 생성하는 것을 권장하지만, 파드가 실행 중인 쿠버네티스 노드에서 도구 실행을 금하지는 않는다.

AppArmor 문제를 디버깅하기 위해서 거부된 것으로 보이는 시스템 로그를 확인할 수 있다. AppArmor 로그는 dmesg에서 보이며, 오류는 보통 시스템 로그나 journalctl에서 볼 수 있다. 더 많은 정보는 AppArmor 실패에서 제공한다.

API 참조

파드 어노테이션

컨테이너를 실행할 프로파일을 지정한다.

  • : container.apparmor.security.beta.kubernetes.io/<container_name> <container_name>는 파드 내에 컨테이너 이름과 일치한다. 분리된 프로파일은 파드 내에 각 컨테이너로 지정할 수 있다.
  • : 아래 기술된 프로파일 참조

프로파일 참조

  • runtime/default: 기본 런타임 프로파일을 참조한다.
    • (기본 파드시큐리티폴리시 없이) 프로파일을 지정하지 않고 AppArmor를 사용하는 것과 동등하다.
    • 도커에서는 권한 없는 컨테이너의 경우는 docker-default 프로파일로, 권한이 있는 컨테이너의 경우 unconfined(프로파일 없음)으로 해석한다.
  • localhost/<profile_name>: 노드(localhost)에 적재된 프로파일을 이름으로 참조한다.
  • unconfined: 이것은 컨테이너에서 AppArmor를 효과적으로 비활성시킨다.

다른 어떤 프로파일 참조 형식도 유효하지 않다.

파드시큐리티폴리시 어노테이션

아무 프로파일도 제공하지 않을 때에 컨테이너에 적용할 기본 프로파일을 지정하기

  • : apparmor.security.beta.kubernetes.io/defaultProfileName
  • : 프로파일 참조. 위에 기술됨.

파드 컨테이너에서 지정을 허용하는 프로파일 목록 지정하기

  • : apparmor.security.beta.kubernetes.io/allowedProfileNames
  • : 컴마로 구분된 참조 프로파일 목록(위에 기술함)
    • 비록 이스케이프된 쉼표(%2C ',')도 프로파일 이름에서 유효한 문자이지만 여기에서 명시적으로 허용하지 않는다.

다음 내용

참고 자료

5.7 - 서비스

5.7.1 - 소스 IP 주소 이용하기

쿠버네티스 클러스터에서 실행 중인 애플리케이션은 서비스 추상화를 통해서 서로를, 그리고 외부 세계를 찾고 통신한다. 이 문서는 다른 종류의 서비스로 전송된 패킷의 소스 IP에 어떤 일이 벌어지는지와 이 동작을 필요에 따라 어떻게 전환할 수 있는지 설명한다.

시작하기 전에

용어

이 문서는 다음 용어를 사용한다.

NAT
네트워크 주소 변환
소스 NAT
패킷 상의 소스 IP 주소를 변경하는 것. 이 페이지에서는 일반적으로 노드 IP 주소로의 변경을 의미함.
대상 NAT
패킷 상의 대상 IP 주소를 변경하는 것. 이 페이지에서는 일반적으로 파드 IP 주소로의 변경을 의미함.
VIP
쿠버네티스의 모든 서비스에 할당되어 있는 것과 같은, 가상 IP 주소.
Kube-proxy
모든 노드에서 서비스 VIP 관리를 조율하는 네트워크 데몬.

전제 조건

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

이 예시는 HTTP 헤더로 수신한 요청의 소스 IP 주소를 회신하는 작은 nginx 웹 서버를 이용한다. 다음과 같이 생성할 수 있다.

kubectl create deployment source-ip-app --image=k8s.gcr.io/echoserver:1.4

출력은 다음과 같다.

deployment.apps/source-ip-app created

목적

  • 간단한 애플리케이션을 다양한 서비스 종류로 노출하기
  • 각 서비스 유형에 따른 소스 IP NAT 의 동작 이해하기
  • 소스 IP 주소 보존에 관한 절충 사항 이해

Type=ClusterIP 인 서비스에서 소스 IP

iptables 모드 (기본값)에서 kube-proxy를 운영하는 경우 클러스터 내에서 클러스터IP로 패킷을 보내면 소스 NAT를 통과하지 않는다. kube-proxy가 실행중인 노드에서 http://localhost:10249/proxyMode 를 입력해서 kube-proxy 모드를 조회할 수 있다.

kubectl get nodes

출력은 다음과 유사하다.

NAME                           STATUS     ROLES    AGE     VERSION
kubernetes-node-6jst   Ready      <none>   2h      v1.13.0
kubernetes-node-cx31   Ready      <none>   2h      v1.13.0
kubernetes-node-jj1t   Ready      <none>   2h      v1.13.0

한 노드의 프록시 모드를 확인한다. (kube-proxy는 포트 10249에서 수신대기한다.)

# 질의 할 노드의 쉘에서 이것을 실행한다.
curl localhost:10249/proxyMode

출력은 다음과 같다.

iptables

소스 IP 애플리케이션을 통해 서비스를 생성하여 소스 IP 주소 보존 여부를 테스트할 수 있다.

kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080

출력은 다음과 같다.

service/clusterip exposed
kubectl get svc clusterip

출력은 다음과 같다.

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
clusterip    ClusterIP   10.0.170.92   <none>        80/TCP    51s

그리고 동일한 클러스터의 파드에서 클러스터IP를 치면:

kubectl run busybox -it --image=busybox --restart=Never --rm

출력은 다음과 같다.

Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

그런 다음 해당 파드 내에서 명령을 실행할 수 있다.

# "kubectl run" 으로 터미널 내에서 이것을 실행한다.
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue
    link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff
    inet 10.244.3.8/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::188a:84ff:feb0:26a5/64 scope link
       valid_lft forever preferred_lft forever

그런 다음 wget 을 사용해서 로컬 웹 서버에 쿼리한다.

# "10.0.170.92"를 "clusterip"라는 이름의 서비스의 IPv4 주소로 변경한다.
wget -qO - 10.0.170.92
CLIENT VALUES:
client_address=10.244.3.8
command=GET
...

client_address 는 클라이언트 파드와 서버 파드가 같은 노드 또는 다른 노드에 있는지 여부에 관계없이 항상 클라이언트 파드의 IP 주소이다.

Type=NodePort 인 서비스에서 소스 IP

Type=NodePort인 서비스로 보내진 패킷은 소스 NAT가 기본으로 적용된다. NodePort 서비스를 생성하여 이것을 테스트할 수 있다.

kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort

출력은 다음과 같다.

service/nodeport exposed
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')

클라우드 공급자 상에서 실행한다면, 위에 보고된 nodes:nodeport를 위한 방화벽 규칙을 열어주어야 한다. 이제 위에 노드 포트로 할당받은 포트를 통해 클러스터 외부에서 서비스에 도달할 수 있다.

for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done

출력은 다음과 유사하다.

client_address=10.180.1.1
client_address=10.240.0.5
client_address=10.240.0.3

명심할 것은 정확한 클라이언트 IP 주소가 아니고, 클러스터 내부 IP 주소이다. 왜 이런 일이 발생했는지 설명한다.

  • 클라이언트는 node2:nodePort로 패킷을 보낸다.
  • node2는 소스 IP 주소(SNAT)를 패킷 상에서 자신의 IP 주소로 교체한다.
  • noee2는 대상 IP를 패킷 상에서 파드의 IP로 교체한다.
  • 패킷은 node 1로 라우팅 된 다음 엔드포인트로 라우팅 된다.
  • 파드의 응답은 node2로 다시 라우팅된다.
  • 파드의 응답은 클라이언트로 다시 전송된다.

시각적으로

graph LR; client(client)-->node2[Node 2]; node2-->client; node2-. SNAT .->node1[Node 1]; node1-. SNAT .->node2; node1-->endpoint(Endpoint); classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class node1,node2,endpoint k8s; class client plain;

이를 피하기 위해 쿠버네티스는 클라이언트 소스 IP 주소를 보존하는 기능이 있다. service.spec.externalTrafficPolicy 의 값을 Local 로 하면 오직 로컬 엔드포인트로만 프록시 요청하고 다른 노드로 트래픽 전달하지 않는다. 이 방법은 원본 소스 IP 주소를 보존한다. 만약 로컬 엔드 포인트가 없다면, 그 노드로 보내진 패킷은 버려지므로 패킷 처리 규칙에서 정확한 소스 IP 임을 신뢰할 수 있으므로, 패킷을 엔드포인트까지 전달할 수 있다.

다음과 같이 service.spec.externalTrafficPolicy 필드를 설정하자.

kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'

출력은 다음과 같다.

service/nodeport patched

이제 다시 테스트를 실행해보자.

for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done

출력은 다음과 유사하다.

client_address=104.132.1.79

엔드포인트 파드가 실행 중인 노드에서 올바른 클라이언트 IP 주소인 딱 한 종류의 응답만 수신한다.

어떻게 이렇게 되었는가:

  • 클라이언트는 패킷을 엔드포인트가 없는 node2:nodePort 보낸다.
  • 패킷은 버려진다.
  • 클라이언트는 패킷을 엔드포인트를 가진 node1:nodePort 보낸다.
  • node1은 패킷을 올바른 소스 IP 주소로 엔드포인트로 라우팅 한다.

시각적으로

graph TD; client --> node1[Node 1]; client(client) --x node2[Node 2]; node1 --> endpoint(endpoint); endpoint --> node1; classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class node1,node2,endpoint k8s; class client plain;

Type=LoadBalancer 인 서비스에서 소스 IP

Type=LoadBalancer인 서비스로 보낸 패킷은 소스 NAT를 기본으로 하는데, Ready 상태로 모든 스케줄된 모든 쿠버네티스 노드는 로드 밸런싱 트래픽에 적합하다. 따라서 엔드포인트가 없는 노드에 패킷이 도착하면 시스템은 엔드포인트를 포함한 노드에 프록시를 수행하고 패킷 상에서 노드의 IP 주소로 소스 IP 주소를 변경한다 (이전 섹션에서 기술한 것처럼).

로드밸런서를 통해 source-ip-app을 노출하여 테스트할 수 있다.

kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer

출력은 다음과 같다.

service/loadbalancer exposed

서비스의 IP 주소를 출력한다.

kubectl get svc loadbalancer

다음과 유사하게 출력된다.

NAME           TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE
loadbalancer   LoadBalancer   10.0.65.118   203.0.113.140     80/TCP    5m

다음으로 이 서비스의 외부 IP에 요청을 전송한다.

curl 203.0.113.140

다음과 유사하게 출력된다.

CLIENT VALUES:
client_address=10.240.0.5
...

그러나 구글 클라우드 엔진/GCE 에서 실행 중이라면 동일한 service.spec.externalTrafficPolicy 필드를 Local로 설정하면 서비스 엔드포인트가 없는 노드는 고의로 헬스 체크에 실패하여 강제로 로드밸런싱 트래픽을 받을 수 있는 노드 목록에서 자신을 스스로 제거한다.

시각적으로:

Source IP with externalTrafficPolicy

이것은 어노테이션을 설정하여 테스트할 수 있다.

kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'

쿠버네티스에 의해 service.spec.healthCheckNodePort 필드가 즉각적으로 할당되는 것을 봐야 한다.

kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort

출력은 다음과 유사하다.

  healthCheckNodePort: 32122

service.spec.healthCheckNodePort 필드는 /healthz에서 헬스 체크를 제공하는 모든 노드의 포트를 가르킨다. 이것을 테스트할 수 있다.

kubectl get pod -o wide -l run=source-ip-app

출력은 다음과 유사하다.

NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE
source-ip-app-826191075-qehz4   1/1       Running   0          20h       10.180.1.136   kubernetes-node-6jst

다양한 노드에서 /healthz 엔드포인트를 가져오려면 curl 을 사용한다.

# 선택한 노드에서 로컬로 이것을 실행한다.
curl localhost:32122/healthz
1 Service Endpoints found

다른 노드에서는 다른 결과를 얻을 수 있다.

# 선택한 노드에서 로컬로 이것을 실행한다.
curl localhost:32122/healthz
No Service Endpoints Found

컨트롤 플레인에서 실행중인 컨트롤러는 클라우드 로드 밸런서를 할당한다. 또한 같은 컨트롤러는 각 노드에서 포트/경로(port/path)를 가르키는 HTTP 상태 확인도 할당한다. 엔드포인트가 없는 2개의 노드가 상태 확인에 실패할 때까지 약 10초간 대기한 다음, curl 을 사용해서 로드밸런서의 IPv4 주소를 쿼리한다.

curl 203.0.113.140

출력은 다음과 유사하다.

CLIENT VALUES:
client_address=198.51.100.79
...

크로스-플랫폼 지원

일부 클라우드 공급자만 Type=LoadBalancer 를 사용하는 서비스를 통해 소스 IP 보존을 지원한다. 실행 중인 클라우드 공급자에서 몇 가지 다른 방법으로 로드밸런서를 요청한다.

  1. 클라이언트 연결을 종료하고 새 연결을 여는 프록시를 이용한다. 이 경우 소스 IP 주소는 클라이언트 IP 주소가 아니고 항상 클라우드 로드밸런서의 IP 주소이다.

  2. 로드밸런서의 VIP에 전달된 클라이언트가 보낸 요청을 중간 프록시가 아닌 클라이언트 소스 IP 주소가 있는 노드로 끝나는 패킷 전달자를 이용한다.

첫 번째 범주의 로드밸런서는 진짜 클라이언트 IP를 통신하기 위해 HTTP Forwarded 또는 X-FORWARDED-FOR 헤더 또는 프록시 프로토콜과 같은 로드밸런서와 백엔드 간에 합의된 프로토콜을 사용해야 한다. 두 번째 범주의 로드밸런서는 서비스의 service.spec.healthCheckNodePort 필드의 저장된 포트를 가르키는 HTTP 헬스 체크를 생성하여 위에서 설명한 기능을 활용할 수 있다.

정리하기

서비스를 삭제한다.

kubectl delete svc -l app=source-ip-app

디플로이먼트, 레플리카셋 그리고 파드를 삭제한다.

kubectl delete deployment source-ip-app

다음 내용

6 - 레퍼런스

쿠버네티스 문서의 본 섹션에서는 레퍼런스를 다룬다.

API 레퍼런스

공식적으로 지원되는 클라이언트 라이브러리

프로그래밍 언어에서 쿠버네티스 API를 호출하기 위해서, 클라이언트 라이브러리를 사용할 수 있다. 공식적으로 지원되는 클라이언트 라이브러리는 다음과 같다.

CLI

  • kubectl - 명령어를 실행하거나 쿠버네티스 클러스터를 관리하기 위해 사용하는 주된 CLI 도구.
  • kubeadm - 안정적인 쿠버네티스 클러스터를 쉽게 프로비전하기 위한 CLI 도구.

컴포넌트

  • kubelet - 각 노드에서 구동되는 주요한 에이전트. kubelet은 PodSpecs 집합을 가지며 기술된 컨테이너가 구동되고 있는지, 정상 작동하는지를 보장한다.

  • kube-apiserver - 파드, 서비스, 레플리케이션 컨트롤러와 같은 API 오브젝트에 대한 검증과 구성을 수행하는 REST API.

  • kube-controller-manager - 쿠버네티스에 탑재된 핵심 제어 루프를 포함하는 데몬.

  • kube-proxy - 간단한 TCP/UDP 스트림 포워딩이나 백-엔드 집합에 걸쳐서 라운드-로빈 TCP/UDP 포워딩을 할 수 있다.

  • kube-scheduler - 가용성, 성능 및 용량을 관리하는 스케줄러.

  • 컨트롤 플레인과 워커 노드에서 꼭 열어야 하는 포트와 프로토콜 리스트

API 설정

이 섹션은 쿠버네티스 구성요소 또는 도구를 환경설정하는 데에 사용되는 "미발표된" API를 다룬다. 이 API들은 사용자나 관리자가 클러스터를 사용/관리하는 데에 중요하지만, 이들 API의 대부분은 아직 API 서버가 제공하지 않는다.

kubeadm을 위한 API 설정

설계 문서

쿠버네티스 기능에 대한 설계 문서의 아카이브. 쿠버네티스 아키텍처쿠버네티스 디자인 개요가 좋은 출발점이다.

6.1 - 용어집

6.2 - API 개요

이 섹션은 쿠버네티스 API에 대한 참조 정보를 제공한다.

REST API는 쿠버네티스의 근본적인 구조이다. 모든 조작, 컴포넌트 간의 통신과 외부 사용자의 명령은 API 서버에서 처리할 수 있는 REST API 호출이다. 따라서, 쿠버네티스 플랫폼 안의 모든 것은 API 오브젝트로 취급되고, API에 상응하는 항목이 있다.

쿠버네티스 API 참조는 쿠버네티스 버전 v1.23에 대한 API가 나열되어 있다.

일반적인 배경 정보를 보려면, 쿠버네티스 API를 참고한다. 쿠버네티스 API에 대한 접근 제어는 클라이언트가 쿠버네티스 API 서버에 인증하는 방법과 요청이 승인되는 방법을 설명한다.

API 버전 규칙

JSON과 Protobuf 직렬화 스키마 모두 스키마 변경에 대해서 동일한 가이드라인을 따른다. 이후 설명에서는 이 형식 모두를 다룬다.

API 버전 규칙과 소프트웨어 버전 규칙은 간접적으로 연관된다. API와 릴리스 버전 부여에 관한 제안에는 API 버전 규칙과 소프트웨어 버전 규칙 간의 관계가 기술되어 있다.

API 버전의 차이는 수준의 안정성과 지원의 차이를 나타낸다. API 변경 문서에서 각 수준의 기준에 대한 더 많은 정보를 찾을 수 있다.

아래는 각 수준의 기준에 대한 요약이다.

  • 알파(Alpha):

    • 버전 이름에 alpha가 포함된다(예: v1alpha1).
    • 버그가 있을 수도 있다. 이 기능을 활성화하면 버그에 노출될 수 있다. 기본적으로 비활성화되어 있다.
    • 기능에 대한 기술 지원이 언제든 공지 없이 중단될 수 있다.
    • 다음 소프트웨어를 릴리스할 때 공지 없이 API의 호환성이 깨지는 방식으로 변경될 수 있다.
    • 버그에 대한 위험이 높고 장기간 지원되지 않으므로 단기간 테스트 용도의 클러스터에서만 사용하기를 권장한다.
  • 베타(Beta):

    • 버전 이름에 beta가 포함된다(예: v2beta3).

    • 코드가 잘 테스트 되었다. 이 기능을 활성화해도 안전하다. 기본적으로 활성화되어 있다.

    • 구체적인 내용이 바뀔 수는 있지만, 전반적인 기능에 대한 기술 지원이 중단되지 않는다.

    • 오브젝트에 대한 스키마나 문법이 다음 베타 또는 안정화 릴리스에서 호환되지 않는 방식으로 바뀔 수도 있다. 이런 경우, 다음 버전으로 이관할 수 있는 가이드가 제공된다. 스키마 변경은 API 오브젝트의 삭제, 편집 또는 재생성이 필요할 수도 있다. 편집 절차는 좀 생각해볼 필요가 있다. 이 기능에 의존하고 있는 애플리케이션은 다운타임이 필요할 수도 있다.

    • 이 소프트웨어는 프로덕션 용도로 권장하지 않는다. 이후 여러 버전에서 호환되지 않는 변경 사항이 적용될 수 있다. 복수의 클러스터를 가지고 있어서 독립적으로 업그레이드할 수 있다면, 이런 제약에서 벗어날 수도 있다.

  • 안정화(Stable):

    • 버전 이름이 vX이고 X 는 정수다.
    • 안정화 버전의 기능은 이후 여러 버전에 걸쳐서 소프트웨어 릴리스에 포함된다.

API 그룹

API 그룹은 쿠버네티스 API를 더 쉽게 확장하게 해준다. API 그룹은 REST 경로와 직렬화된 오브젝트의 apiVersion 필드에 명시된다.

쿠버네티스에는 다음과 같은 다양한 API 그룹이 있다.

  • 핵심 (또는 레거시 라고 불리는) 그룹은 REST 경로 /api/v1에 있다. 핵심 그룹은 apiVersion 필드의 일부로 명시되지 않는다. 예를 들어, apiVersion: v1 과 같다.
  • 이름이 있는 그룹은 REST 경로 /apis/$GROUP_NAME/$VERSION에 있으며 apiVersion: $GROUP_NAME/$VERSION을 사용한다(예를 들어, apiVersion: batch/v1). 지원되는 API 그룹 전체의 목록은 쿠버네티스 API 참조 문서에서 확인할 수 있다.

API 그룹 활성화 또는 비활성화

특정 리소스 및 API 그룹은 기본적으로 활성화된다. API 서버에서 --runtime-config 를 설정하여 활성화 또는 비활성화할 수 있다. --runtime-config 플래그는 API 서버의 런타임 구성을 설명하는 쉼표로 구분된 <key>=<value> 쌍을 허용한다. 만약 =<value> 부분을 생략하면, =true 가 명시된 것처럼 취급한다. 예를 들면, 다음과 같다.

  • batch/v1 을 비활성화하려면, --runtime-config=batch/v1=false 로 설정
  • batch/v2alpha1 을 활성화하려면, --runtime-config=batch/v2alpha1 으로 설정

지속성

쿠버네티스는 etcd에 기록하여 API 리소스 측면에서 직렬화된 상태를 저장한다.

다음 내용

6.2.1 - 클라이언트 라이브러리

이 페이지는 다양한 프로그래밍 언어에서 쿠버네티스 API를 사용하기 위한 클라이언트 라이브러리에 대한 개요를 포함하고 있다.

쿠버네티스 REST API를 사용해 애플리케이션을 작성하기 위해 API 호출 또는 요청/응답 타입을 직접 구현할 필요는 없다. 사용하고 있는 프로그래밍 언어를 위한 클라이언트 라이브러리를 사용하면 된다.

클라이언트 라이브러리는 대체로 인증과 같은 공통의 태스크를 처리한다. 대부분의 클라이언트 라이브러리들은 API 클라이언트가 쿠버네티스 클러스터 내부에서 동작하는 경우 인증 또는 kubeconfig 파일 포맷을 통해 자격증명과 API 서버 주소를 읽을 수 있게 쿠버네티스 서비스 어카운트를 발견하고 사용할 수 있다.

공식적으로 지원되는 쿠버네티스 클라이언트 라이브러리

다음의 클라이언트 라이브러리들은 쿠버네티스 SIG API Machinery에서 공식적으로 관리된다.

언어 클라이언트 라이브러리 예제 프로그램
dotnet github.com/kubernetes-client/csharp 둘러보기
Go github.com/kubernetes/client-go/ 둘러보기
Haskell github.com/kubernetes-client/haskell 둘러보기
Java github.com/kubernetes-client/java 둘러보기
JavaScript github.com/kubernetes-client/javascript 둘러보기
Python github.com/kubernetes-client/python/ 둘러보기

커뮤니티에 의해 관리되는 클라이언트 라이브러리

다음의 쿠버네티스 API 클라이언트 라이브러리들은 쿠버네티스 팀이 아닌 각각의 저자들이 제공하고 관리한다.

언어 클라이언트 라이브러리
Clojure github.com/yanatan16/clj-kubernetes-api
DotNet github.com/tonnyeremin/kubernetes_gen
DotNet (RestSharp) github.com/masroorhasan/Kubernetes.DotNet
Elixir github.com/obmarg/kazan
Elixir github.com/coryodaniel/k8s
Go github.com/ericchiang/k8s
Java (OSGi) bitbucket.org/amdatulabs/amdatu-kubernetes
Java (Fabric8, OSGi) github.com/fabric8io/kubernetes-client
Java github.com/manusa/yakc
Lisp github.com/brendandburns/cl-k8s
Lisp github.com/xh4/cube
Node.js (TypeScript) github.com/Goyoo/node-k8s-client
Node.js github.com/ajpauwels/easy-k8s
Node.js github.com/godaddy/kubernetes-client
Node.js github.com/tenxcloud/node-kubernetes-client
Perl metacpan.org/pod/Net::Kubernetes
PHP github.com/allansun/kubernetes-php-client
PHP github.com/maclof/kubernetes-client
PHP github.com/travisghansen/kubernetes-client-php
PHP github.com/renoki-co/php-k8s
Python github.com/fiaas/k8s
Python github.com/mnubo/kubernetes-py
Python github.com/tomplus/kubernetes_asyncio
Python github.com/Frankkkkk/pykorm
Ruby github.com/abonas/kubeclient
Ruby github.com/Ch00k/kuber
Ruby github.com/k8s-ruby/k8s-ruby
Ruby github.com/kontena/k8s-client
Rust github.com/clux/kube-rs
Rust github.com/ynqa/kubernetes-rust
Scala github.com/hagay3/skuber
Scala github.com/joan38/kubernetes-client
Swift github.com/swiftkube/client

6.2.2 - 쿠버네티스 API 헬스(health) 엔드포인트

쿠버네티스 API 서버는 현재 상태를 나타내는 API 엔드포인트를 제공한다. 이 페이지에서는 API 엔드포인트들에 대해 설명하고 이를 사용하는 방법을 다룬다.

헬스를 위한 API 엔드포인트

쿠버네티스 API 서버는 현재 상태를 나타내는 세 가지 API 엔드포인트(healthz, livezreadyz)를 제공한다. healthz 엔드포인트는 사용 중단(deprecated)됐으며 (쿠버네티스 v1.16 버전 이후), 대신 보다 구체적인 livezreadyz 엔드포인트를 사용해야 한다. livez 엔드포인트는 --livez-grace-period 플래그 옵션을 사용하여 시작 대기 시간을 지정할 수 있다. /readyz 엔드포인트는 --shutdown-delay-duration 플래그 옵션을 사용하여 정상적(graceful)으로 셧다운할 수 있다. API 서버의 healthz/livez/readyz 를 사용하는 머신은 HTTP 상태 코드에 의존해야 한다. 상태 코드 200은 호출된 엔드포인트에 따라 API 서버의 healthy/live/ready 상태를 나타낸다. 아래 표시된 더 자세한 옵션은 운영자가 클러스터나 특정 API 서버의 상태를 디버깅하는데 사용할 수 있다.

다음의 예시는 헬스 API 엔드포인트와 상호 작용할 수 있는 방법을 보여준다.

모든 엔드포인트는 verbose 파라미터를 사용하여 검사 항목과 상태를 출력할 수 있다. 이는 운영자가 머신 사용을 위한 것이 아닌, API 서버의 현재 상태를 디버깅하는데 유용하다.

curl -k https://localhost:6443/livez?verbose

인증을 사용하는 원격 호스트에서 사용할 경우에는 다음과 같이 수행한다.

kubectl get --raw='/readyz?verbose'

출력은 다음과 같다.

[+]ping ok
[+]log ok
[+]etcd ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
healthz check passed

또한 쿠버네티스 API 서버는 특정 체크를 제외할 수 있다. 쿼리 파라미터는 다음 예와 같이 조합될 수 있다.

curl -k 'https://localhost:6443/readyz?verbose&exclude=etcd'

출력에서 etcd 체크가 제외된 것을 보여준다.

[+]ping ok
[+]log ok
[+]etcd excluded: ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
[+]shutdown ok
healthz check passed

개별 헬스 체크

FEATURE STATE: Kubernetes v1.23 [alpha]

각 개별 헬스 체크는 HTTP 엔드포인트를 노출하고 개별적으로 체크가 가능하다. 개별 체크를 위한 스키마는 /livez/<healthcheck-name> 이고, 여기서 livezreadyz 는 API 서버의 활성 상태 또는 준비 상태인지를 확인할 때 사용한다. <healthcheck-name> 경로 위에서 설명한 verbose 플래그를 사용해서 찾을 수 있고, [+]ok 사이의 경로를 사용한다. 이러한 개별 헬스 체크는 머신에서 사용되서는 안되며, 운영자가 시스템의 현재 상태를 디버깅하는데 유용하다.

curl -k https://localhost:6443/livez/etcd

6.3 - API 접근 제어

쿠버네티스가 API 접근을 구현 및 제어하는 방법에 대한 자세한 내용은 쿠버네티스 API에 대한 접근 제어를 참고한다.

참조 문헌

6.3.1 - 서비스 어카운트 관리하기

이것은 서비스 어카운트에 대한 클러스터 관리자 안내서다. 독자는 쿠버네티스 서비스 어카운트 설정에 익숙하다고 가정한다.

인증 및 사용자 어카운트에 대한 지원은 아직 준비 중이다. 서비스 어카운트를 더 잘 설명하기 위해, 때때로 미완성 기능이 언급될 수 있다.

사용자 어카운트와 서비스 어카운트 비교

쿠버네티스는 여러 가지 이유로 사용자 어카운트와 서비스 어카운트의 개념을 구분한다.

  • 사용자 어카운트는 사람을 위한 것이다. 서비스 어카운트는 파드에서 실행되는 프로세스를 위한 것이다.
  • 사용자 어카운트는 전역을 대상으로 고려된다. 클러스터의 모든 네임스페이스에 걸쳐 이름이 고유해야 한다. 서비스 어카운트는 네임스페이스에 할당된다.
  • 일반적으로 클러스터의 사용자 어카운트는 기업 데이터베이스로부터 동기화될 수 있으며, 여기서 새로운 사용자 어카운트를 생성하려면 특별한 권한이 필요하며 복잡한 비즈니스 프로세스에 연결된다. 서비스 어카운트 생성은 클러스터 사용자가 최소 권한 원칙에 따라 특정 작업을 위한 서비스 어카운트를 만들 수 있도록 보다 가볍게 만들어졌다.
  • 사람과 서비스 어카운트에 대한 감사 항목은 다를 수 있다.
  • 복잡한 시스템의 설정들은 그 시스템의 구성요소에 대한 다양한 서비스 어카운트 정의를 포함할 수 있다. 서비스 어카운트는 많은 제약없이 만들 수 있고 네임스페이스에 할당된 이름을 가질 수 있기 때문에 이러한 설정은 이식성이 좋다.

서비스 어카운트 자동화

서비스 계정 자동화를 구현하기 위해 세 가지 개별 요소가 협력한다.

  • ServiceAccount 어드미션 컨트롤러
  • 토큰 컨트롤러
  • ServiceAccount 컨트롤러

서비스어카운트(ServiceAccount) 어드미션 컨트롤러

파드 수정은 어드미션 컨트롤러라는 플러그인을 통해 구현된다. 이것은 API 서버의 일부이다. 파드가 생성되거나 수정될 때 파드를 수정하기 위해 동기적으로 동작한다. 이 플러그인이 활성 상태(대부분의 배포에서 기본값)인 경우 파드 생성 또는 수정 시 다음 작업을 수행한다.

  1. 파드에 ServiceAccount 가 없다면, ServiceAccountdefault 로 설정한다.
  2. 이전 단계는 파드에 참조되는 ServiceAccount 가 있도록 하고, 그렇지 않으면 이를 거부한다.
  3. 서비스어카운트 automountServiceAccountToken 와 파드의 automountServiceAccountToken 중 어느 것도 false 로 설정되어 있지 않다면, API 접근을 위한 토큰이 포함된 volume 을 파드에 추가한다.
  4. 이전 단계에서 서비스어카운트 토큰을 위한 볼륨이 만들어졌다면, /var/run/secrets/kubernetes.io/serviceaccount 에 마운트된 파드의 각 컨테이너에 volumeSource 를 추가한다.
  5. 파드에 imagePullSecrets 이 없는 경우, ServiceAccountimagePullSecrets 이 파드에 추가된다.

바인딩된 서비스 어카운트 토큰 볼륨

FEATURE STATE: Kubernetes v1.22 [stable]

서비스 어카운트 어드미션 컨트롤러는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가한다.

- name: kube-api-access-<random-suffix>
  projected:
    defaultMode: 420 # 0644
    sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
            - key: ca.crt
              path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
            - fieldRef:
                apiVersion: v1
                fieldPath: metadata.namespace
              path: namespace

프로젝티드 볼륨은 세 가지로 구성된다.

  1. kube-apiserver로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken). 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.
  2. kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함하는 컨피그맵(ConfigMap). 이 기능은 모든 네임스페이스에 "kube-root-ca.crt" 컨피그맵을 게시하는 기능 게이트인 RootCAConfigMap에 의해 동작한다. RootCAConfigMap 기능 게이트는 1.21에서 GA로 전환되었으며 기본적으로 활성화되어 있다. (이 플래그는 1.22에서 --feature-gate 인자에서 제외될 예정이다.)
  3. 파드의 네임스페이스를 참조하는 DownwardAPI.

상세 사항은 프로젝티드 볼륨을 참고한다.

토큰 컨트롤러

토큰컨트롤러는 kube-controller-manager 의 일부로 실행된다. 이것은 비동기적으로 동작한다. 토큰 컨트롤러는,

  • 서비스어카운트 생성을 감시하고 API에 접근할 수 있는 해당 서비스어카운트 토큰 시크릿을 생성한다.
  • 서비스어카운트 삭제를 감시하고 해당하는 모든 서비스어카운트 토큰 시크릿을 삭제한다.
  • 서비스어카운트 토큰 시크릿 추가를 감시하고, 참조된 서비스어카운트가 존재하는지 확인하고, 필요한 경우 시크릿에 토큰을 추가한다.
  • 시크릿 삭제를 감시하고 필요한 경우 해당 서비스어카운트에서 참조를 제거한다.

서비스 어카운트 개인키 파일은 --service-account-private-key-file 플래그를 사용하여 kube-controller-manager 의 토큰 컨트롤러에 전달해야 한다. 개인키는 생성된 서비스 어카운트 토큰에 서명하는 데 사용될 것이다. 마찬가지로 --service-account-key-file 플래그를 사용하여 해당 공개키를 kube-apiserver 에 전달해야 한다. 공개키는 인증 과정에서 토큰을 검증하는 데 사용될 것이다.

추가적인 API 토큰 생성

컨트롤러 루프는 API 토큰이 포함된 시크릿이 각 서비스어카운트에 존재하도록 보장한다. 서비스어카운트에 대한 추가적인 API 토큰을 생성하기 위해 서비스어카운트를 참조하는 어노테이션과 함께 kubernetes.io/service-account-token 유형의 시크릿을 생성하면 컨트롤러가 새로 생성된 토큰으로 갱신한다.

다음은 시크릿에 대한 샘플 구성이다.

apiVersion: v1
kind: Secret
metadata:
  name: mysecretname
  annotations:
    kubernetes.io/service-account.name: myserviceaccount
type: kubernetes.io/service-account-token
kubectl create -f ./secret.yaml
kubectl describe secret mysecretname

서비스 어카운트 토큰 시크릿 삭제/무효화

kubectl delete secret mysecretname

서비스어카운트 컨트롤러

서비스어카운트 컨트롤러는 네임스페이스에 있는 서비스어카운트를 관리하고 "default"라는 이름의 서비스어카운트가 모든 활성 네임스페이스에 존재하는지 확인한다.

6.3.2 - 인가 개요

지원되는 인가 모듈을 사용하여 정책을 만드는 방법을 포함한 쿠버네티스 인가에 대해 자세히 알아보자.

쿠버네티스에서는 사용자의 요청이 인가(접근 권한을 부여) 받기 전에 사용자가 인증(로그인)되어야 한다. 인증에 대한 자세한 내용은 쿠버네티스 API 접근 제어하기를 참고한다.

쿠버네티스는 REST API 요청에 공통적인 속성을 요구한다. 이는 쿠버네티스 인가가 쿠버네티스 API 이외에 다른 API를 처리할 수 있는 기존 조직 전체 또는 클라우드 제공자 전체의 접근 제어 시스템과 연동된다는 것을 의미한다.

요청 허용 또는 거부 결정

쿠버네티스는 API 서버를 이용하여 API 요청을 인가한다. 모든 정책과 비교하여 모든 요청 속성을 평가하고 요청을 허용하거나 거부한다. 계속 진행하려면 API 요청의 모든 부분이 일부 정책에 의해 반드시 허용되어야 한다. 이는 기본적으로 승인이 거부된다는 것을 의미한다.

(쿠버네티스는 API 서버를 사용하지만, 특정 오브젝트의 특정 필드에 의존하는 접근 제어 및 정책은 어드미션 컨트롤러에 의해 처리된다.)

여러 개의 인가 모듈이 구성되면 각 모듈이 순서대로 확인된다. 어느 인가 모듈이 요청을 승인하거나 거부할 경우, 그 결정은 즉시 반환되며 다른 인가 모듈이 참고되지 않는다. 모든 모듈에서 요청에 대한 평가가 없으면 요청이 거부된다. 요청 거부는 HTTP 상태 코드 403을 반환한다.

요청 속성 검토

쿠버네티스는 다음 API 요청 속성만 검토한다.

  • user - 인증 중에 제공된 user 문자열.
  • group - 인증된 사용자가 속한 그룹 이름 목록.
  • extra - 인증 계층에서 제공하는 문자열 값에 대한 임의의 문자열 키 맵.
  • API - 요청이 API 리소스에 대한 것인지 여부.
  • Request path - /api 또는 /healthz와 같이 다양한 리소스가 아닌 엔드포인트의 경로.
  • API request verb - get, list, create, update, patch, watch, delete, deletecollection과 같은 리소스 요청에 사용하는 API 동사. 리소스 API 엔드포인트의 요청 동사를 결정하려면 요청 동사 결정을 참고한다.
  • HTTP request verb - get, post, put, delete처럼 소문자 HTTP 메서드는 리소스가 아닌 요청에 사용한다.
  • Resource - 접근 중인 리소스의 ID 또는 이름(리소스 요청만 해당) -- get, update, patch, delete 동사를 사용하는 리소스 요청의 경우 리소스 이름을 지정해야 한다.
  • Subresource - 접근 중인 하위 리소스(리소스 요청만 해당).
  • Namespace - 접근 중인 오브젝트의 네임스페이스(네임스페이스에 할당된 리소스 요청만 해당)
  • API group - 접근 중인 API 그룹(리소스 요청에만 해당). 빈 문자열은 핵심(core) API 그룹을 지정한다.

요청 동사 결정

리소스가 아닌 요청 /api/v1/... 또는 /apis/<group>/<version>/... 이외에 다른 엔드포인트에 대한 요청은 "리소스가 아닌 요청"으로 간주되며, 요청의 소문자 HTTP 메서드를 동사로 사용한다. 예를 들어, /api 또는 /healthz와 같은 엔드포인트에 대한 GET 요청은 get을 동사로 사용할 것이다.

리소스 요청 리소스 API 엔드포인트에 대한 요청 동사를 결정하려면 사용된 HTTP 동사와 해당 요청이 개별 리소스 또는 리소스 모음에 적용되는지 여부를 검토한다.

HTTP 동사 요청 동사
POST create
GET, HEAD get(개별 리소스), list(전체 오브젝트 내용을 포함한 리소스 모음), watch(개별 리소스 또는 리소스 모음을 주시)
PUT update
PATCH patch
DELETE delete(개별 리소스), deletecollection(리소스 모음)

쿠버네티스는 종종 전문 동사를 사용하여 부가적인 권한 인가를 확인한다. 예를 들면,

  • 파드시큐리티폴리시(PodSecurityPolicy)
    • policy API 그룹의 podsecuritypolicies 리소스에 대한 use 동사.
  • RBAC
    • rbac.authorization.k8s.io API 그룹의 rolesclusterroles 리소스에 대한 bind 동사.
  • 인증
    • 핵심 API 그룹의 users, groups, serviceaccountsauthentication.k8s.io API 그룹의 userextras 동사.

인가 모드

쿠버네티스 API 서버는 몇 가지 인가 모드 중 하나를 사용하여 요청을 승인할 수 있다.

  • Node - 실행되도록 스케줄된 파드에 따라 kubelet에게 권한을 부여하는 특수 목적 인가 모드. Node 인가 모드 사용에 대한 자세한 내용은 Node 인가을 참조한다.
  • ABAC - 속성 기반 접근 제어 (ABAC, Attribute-based access control)는 속성과 결합한 정책의 사용을 통해 사용자에게 접근 권한을 부여하는 접근 제어 패러다임을 말한다. 이 정책은 모든 유형의 속성(사용자 속성, 리소스 속성, 오브젝트, 환경 속성 등)을 사용할 수 있다. ABAC 모드 사용에 대한 자세한 내용은 ABAC 모드를 참조한다.
  • RBAC - 역할 기반 접근 제어(RBAC, Role-based access control)는 기업 내 개별 사용자의 역할을 기반으로 컴퓨터나 네트워크 리소스에 대한 접근을 규제하는 방식이다. 이 맥락에서 접근은 개별 사용자가 파일을 보거나 만들거나 수정하는 것과 같은 특정 작업을 수행할 수 있는 능력이다. RBAC 모드 사용에 대한 자세한 내용은 RBAC 모드를 참조한다.
    • 지정된 RBAC(역할 기반 접근 제어)이 인가 결정을 위해 rbac.authorization.k8s.io API 그룹을 사용하면, 관리자가 쿠버네티스 API를 통해 권한 정책을 동적으로 구성할 수 있다.
    • RBAC을 활성화하려면 --authorization-mode=RBAC로 API 서버를 시작한다.
  • Webhook - WebHook은 HTTP 콜백이다(어떤 일이 일어날 때 발생하는 HTTP POST와 HTTP POST를 통한 간단한 이벤트 알림). WebHook을 구현하는 웹 애플리케이션은 특정한 일이 발생할 때 URL에 메시지를 POST 할 것이다. Webhook 모드 사용에 대한 자세한 내용은 Webhook 모드를 참조한다.

API 접근 확인

kubectl은 API 인증 계층을 신속하게 쿼리하기 위한 "auth can-i" 하위 명령어를 제공한다. 이 명령은 현재 사용자가 지정된 작업을 수행할 수 있는지 여부를 알아내기 위해 SelfSubjectAccessReview API를 사용하며, 사용되는 인가 모드에 관계없이 작동한다.

kubectl auth can-i create deployments --namespace dev

다음과 유사하게 출력된다.

yes
kubectl auth can-i create deployments --namespace prod

다음과 유사하게 출력된다.

no

관리자는 이를 사용자 가장(impersonation)과 병행하여 다른 사용자가 수행할 수 있는 작업을 결정할 수 있다.

kubectl auth can-i list secrets --namespace dev --as dave

다음과 유사하게 출력된다.

no

유사하게, dev 네임스페이스의 dev-sa 서비스 어카운트가 target 네임스페이스의 파드 목록을 볼 수 있는지 확인하려면 다음을 실행한다.

kubectl auth can-i list pods \
	--namespace target \
	--as system:serviceaccount:dev:dev-sa

다음과 유사하게 출력된다.

yes

SelfSubjectAccessReviewauthorization.k8s.io API 그룹의 일부로서 API 서버 인가를 외부 서비스에 노출시킨다. 이 그룹의 기타 리소스에는 다음이 포함된다.

  • SubjectAccessReview - 현재 사용자뿐만 아니라 모든 사용자에 대한 접근 검토. API 서버에 인가 결정을 위임하는 데 유용하다. 예를 들어, kubelet 및 확장(extension) API 서버는 자신의 API에 대한 사용자 접근을 결정하기 위해 해당 리소스를 사용한다.
  • LocalSubjectAccessReview - SubjectAccessReview와 비슷하지만 특정 네임스페이스로 제한된다.
  • SelfSubjectRulesReview - 사용자가 네임스페이스 안에서 수행할 수 있는 작업 집합을 반환하는 검토. 사용자가 자신의 접근을 빠르게 요약해서 보거나 UI가 작업을 숨기거나 표시하는 데 유용하다.

이러한 API는 반환된 오브젝트의 응답 "status" 필드가 쿼리의 결과인 일반 쿠버네티스 리소스를 생성하여 쿼리할 수 있다.

kubectl create -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: apps
    resource: deployments
    verb: create
    namespace: dev
EOF

생성된 SelfSubjectAccessReview 는 다음과 같다.

apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
metadata:
  creationTimestamp: null
spec:
  resourceAttributes:
    group: apps
    resource: deployments
    namespace: dev
    verb: create
status:
  allowed: true
  denied: false

인가 모듈에 플래그 사용

정책에 포함된 인가 모듈을 나타내기 위해 정책에 플래그를 포함시켜야 한다.

다음 플래그를 사용할 수 있다.

  • --authorization-mode=ABAC 속성 기반 접근 제어(ABAC) 모드를 사용하면 로컬 파일을 사용하여 정책을 구성할 수 있다.
  • --authorization-mode=RBAC 역할 기반 접근 제어(RBAC) 모드를 사용하면 쿠버네티스 API를 사용하여 정책을 만들고 저장할 수 있다.
  • --authorization-mode=Webhook WebHook은 원격 REST 엔드포인트를 사용하여 인가를 관리할 수 있는 HTTP 콜백 모드다.
  • --authorization-mode=Node 노드 인가는 kubelet이 생성한 API 요청을 특별히 인가시키는 특수 목적 인가 모드다.
  • --authorization-mode=AlwaysDeny 이 플래그는 모든 요청을 차단한다. 이 플래그는 테스트에만 사용한다.
  • --authorization-mode=AlwaysAllow 이 플래그는 모든 요청을 허용한다. API 요청에 대한 인가가 필요하지 않은 경우에만 이 플래그를 사용한다.

하나 이상의 인가 모듈을 선택할 수 있다. 모듈이 순서대로 확인되기 때문에 우선 순위가 더 높은 모듈이 요청을 허용하거나 거부할 수 있다.

워크로드 생성 및 수정을 통한 권한 확대

네임스페이스에서 파드를 직접, 또는 오퍼레이터와 같은 컨트롤러를 통해 생성/수정할 수 있는 사용자는 해당 네임스페이스 안에서 자신의 권한을 확대할 수 있다.

권한 확대 경로

  • 네임스페이스 내의 임의의 시크릿을 마운트
    • 다른 워크로드를 위한 시크릿으로의 접근에 사용될 수 있음
    • 더 권한이 많은 서비스 어카운트의 서비스 어카운트 토큰 획득에 사용될 수 있음
  • 네임스페이스 내의 임의의 서비스 어카운트를 사용
    • 다른 워크로드인것처럼 사칭하여 쿠버네티스 API 액션을 수행할 수 있음
    • 서비스 어카운트가 갖고 있는 '권한이 필요한 액션'을 수행할 수 있음
  • 네임스페이스 내의 다른 워크로드를 위한 컨피그맵을 마운트
    • 다른 워크로드를 위한 정보(예: DB 호스트 이름) 획득에 사용될 수 있음
  • 네임스페이스 내의 다른 워크로드를 위한 볼륨을 마운트
    • 다른 워크로드를 위한 정보의 획득 및 수정에 사용될 수 있음

다음 내용

6.4 - 잘 알려진 레이블, 어노테이션, 테인트(Taint)

쿠버네티스는 모든 레이블과 어노테이션을 kubernetes.io 네임스페이스 아래에 정의해 놓았다.

이 문서는 각 값에 대한 레퍼런스를 제공하며, 값을 할당하기 위한 협력 포인트도 제공한다.

kubernetes.io/arch

예시: kubernetes.io/arch=amd64

적용 대상: 노드

Go에 의해 정의된 runtime.GOARCH 값을 kubelet이 읽어서 이 레이블의 값으로 채운다. arm 노드와 x86 노드를 혼합하여 사용하는 경우 유용할 수 있다.

kubernetes.io/os

예시: kubernetes.io/os=linux

적용 대상: 노드

Go에 의해 정의된 runtime.GOOS 값을 kubelet이 읽어서 이 레이블의 값으로 채운다. 클러스터에서 여러 운영체제를 혼합하여 사용(예: 리눅스 및 윈도우 노드)하는 경우 유용할 수 있다.

kubernetes.io/metadata.name

예시: kubernetes.io/metadata.name=mynamespace

적용 대상: 네임스페이스

NamespaceDefaultLabelName 기능 게이트가 활성화되어 있으면, 쿠버네티스 API 서버가 모든 네임스페이스에 이 레이블을 적용한다. 레이블의 값은 네임스페이스의 이름으로 적용된다.

레이블 셀렉터를 이용하여 특정 네임스페이스를 지정하고 싶다면 이 레이블이 유용할 수 있다.

beta.kubernetes.io/arch (사용 중단됨)

이 레이블은 사용 중단되었다. 대신 kubernetes.io/arch 을 사용한다.

beta.kubernetes.io/os (사용 중단됨)

이 레이블은 사용 중단되었다. 대신 kubernetes.io/os 을 사용한다.

kubernetes.io/hostname

예시: kubernetes.io/hostname=ip-172-20-114-199.ec2.internal

적용 대상: 노드

kubelet이 호스트네임을 읽어서 이 레이블의 값으로 채운다. kubelet--hostname-override 플래그를 전달하여 실제 호스트네임과 다른 값으로 설정할 수도 있다.

이 레이블은 토폴로지 계층의 일부로도 사용된다. topology.kubernetes.io/zone에서 세부 사항을 확인한다.

controller.kubernetes.io/pod-deletion-cost

예시: controller.kubernetes.io/pod-deletion-cost=10

적용 대상: Pod

이 어노테이션은 레플리카셋(ReplicaSet) 다운스케일 순서를 조정할 수 있는 요소인 파드 삭제 비용을 설정하기 위해 사용한다. 명시된 값은 int32 타입으로 파싱된다.

beta.kubernetes.io/instance-type (사용 중단됨)

node.kubernetes.io/instance-type

예시: node.kubernetes.io/instance-type=m3.medium

적용 대상: 노드

클라우드 제공자에 의해 정의된 인스턴스 타입의 값을 kubelet이 읽어서 이 레이블의 값으로 채운다. 클라우드 제공자를 사용하는 경우에만 이 레이블이 설정된다. 특정 워크로드를 특정 인스턴스 타입에 할당하고 싶다면 이 레이블이 유용할 수 있다. 하지만 일반적으로는 자원 기반 스케줄링을 수행하는 쿠버네티스 스케줄러를 이용하게 된다. 인스턴스 타입 보다는 특성을 기준으로 스케줄링을 고려해야 한다(예: g2.2xlarge 를 요구하기보다는, GPU가 필요하다고 요구한다).

failure-domain.beta.kubernetes.io/region (사용 중단됨)

topology.kubernetes.io/region을 확인한다.

failure-domain.beta.kubernetes.io/zone (사용 중단됨)

topology.kubernetes.io/zone을 확인한다.

statefulset.kubernetes.io/pod-name

예시:

statefulset.kubernetes.io/pod-name=mystatefulset-7

스테이트풀셋(StatefulSet) 컨트롤러가 파드를 위한 스테이트풀셋을 생성하면, 컨트롤 플레인이 파드에 이 레이블을 설정한다. 생성되는 파드의 이름을 이 레이블의 값으로 설정한다.

스테이트풀셋 문서의 파드 이름 레이블에서 상세 사항을 확인한다.

topology.kubernetes.io/region

예시:

topology.kubernetes.io/region=us-east-1

topology.kubernetes.io/zone을 확인한다.

topology.kubernetes.io/zone

예시:

topology.kubernetes.io/zone=us-east-1c

적용 대상: 노드, 퍼시스턴트볼륨(PersistentVolume)

노드의 경우: 클라우드 제공자가 제공하는 값을 이용하여 kubelet 또는 외부 cloud-controller-manager가 이 어노테이션의 값을 설정한다. 클라우드 제공자를 사용하는 경우에만 이 레이블이 설정된다. 하지만, 토폴로지 내에서 의미가 있는 경우에만 이 레이블을 노드에 설정해야 한다.

퍼시스턴트볼륨의 경우: 토폴로지 어웨어 볼륨 프로비저너가 자동으로 퍼시스턴트볼륨에 노드 어피니티 제약을 설정한다.

영역(zone)은 논리적 고장 도메인을 나타낸다. 가용성 향상을 위해 일반적으로 쿠버네티스 클러스터는 여러 영역에 걸쳐 구성된다. 영역에 대한 정확한 정의는 사업자 별 인프라 구현에 따라 다르지만, 일반적으로 영역은 '영역 내 매우 낮은 네트워크 지연시간, 영역 내 네트워크 트래픽 비용 없음, 다른 영역의 고장에 독립적임' 등의 공통적인 특성을 갖는다. 예를 들어, 같은 영역 내의 노드는 하나의 네트워크 스위치를 공유하여 활용할 수 있으며, 반대로 다른 영역에 있는 노드는 하나의 네트워크 스위치를 공유해서는 안 된다.

지역(region)은 하나 이상의 영역으로 구성된 더 큰 도메인을 나타낸다. 쿠버네티스 클러스터가 여러 지역에 걸쳐 있는 경우는 드물다. 영역이나 지역에 대한 정확한 정의는 사업자 별 인프라 구현에 따라 다르지만, 일반적으로 지역은 '지역 내 네트워크 지연시간보다 지역 간 네트워크 지연시간이 큼, 지역 간 네트워크 트래픽은 비용이 발생함, 다른 영역/지역의 고장에 독립적임' 등의 공통적인 특성을 갖는다. 예를 들어, 같은 지역 내의 노드는 전력 인프라(예: UPS 또는 발전기)를 공유하여 활용할 수 있으며, 반대로 다른 지역에 있는 노드는 일반적으로 전력 인프라를 공유하지 않는다.

쿠버네티스는 영역과 지역의 구조에 대해 다음과 같이 가정한다.

  1. 지역과 영역은 계층적이다. 영역은 지역의 엄격한 부분집합(strict subset)이며, 하나의 영역이 두 개의 지역에 속할 수는 없다.
  2. 영역 이름은 모든 지역에 걸쳐서 유일하다. 예를 들어, "africa-east-1" 라는 지역은 "africa-east-1a" 와 "africa-east-1b" 라는 영역으로 구성될 수 있다.

토폴로지 레이블이 변경되는 일은 없다고 가정할 수 있다. 일반적으로 레이블의 값은 변경될 수 있지만, 특정 노드가 삭제 후 재생성되지 않고서는 다른 영역으로 이동할 수 없기 때문이다.

쿠버네티스는 이 정보를 다양한 방식으로 활용할 수 있다. 예를 들어, 단일 영역 클러스터에서는 스케줄러가 자동으로 레플리카셋의 파드를 여러 노드에 퍼뜨린다(노드 고장의 영향을 줄이기 위해 - kubernetes.io/hostname 참고). 복수 영역 클러스터에서는, 여러 영역에 퍼뜨린다(영역 고장의 영향을 줄이기 위해). 이는 SelectorSpreadPriority 를 통해 실현된다.

SelectorSpreadPriority 는 최선 노력(best effort) 배치 방법이다. 클러스터가 위치한 영역들의 특성이 서로 다르다면(예: 노드 숫자가 다름, 노드 타입이 다름, 파드 자원 요구사항이 다름), 파드 숫자를 영역별로 다르게 하여 배치할 수 있다. 필요하다면, 영역들의 특성(노드 숫자/타입)을 일치시켜 불균형 배치의 가능성을 줄일 수 있다.

스케줄러도 (VolumeZonePredicate 표시자를 이용하여) '파드가 요청하는 볼륨'이 위치하는 영역과 같은 영역에 파드를 배치한다. 여러 영역에서 볼륨에 접근할 수는 없다.

PersistentVolumeLabel이 퍼시스턴트볼륨의 자동 레이블링을 지원하지 않는다면, 레이블을 수동으로 추가하거나 PersistentVolumeLabel이 동작하도록 변경할 수 있다. PersistentVolumeLabel이 설정되어 있으면, 스케줄러는 파드가 다른 영역에 있는 볼륨에 마운트하는 것을 막는다. 만약 사용 중인 인프라에 이러한 제약이 없다면, 볼륨에 영역 레이블을 추가할 필요가 전혀 없다.

node.kubernetes.io/windows-build

예시: node.kubernetes.io/windows-build=10.0.17763

적용 대상: 노드

kubelet이 Microsoft 윈도우에서 실행되고 있다면, 사용 중인 Windows Server 버전을 기록하기 위해 kubelet이 노드에 이 레이블을 추가한다.

이 레이블의 값은 "MajorVersion.MinorVersion.BuildNumber"의 형태를 갖는다.

service.kubernetes.io/headless

예시: service.kubernetes.io/headless=""

적용 대상: 서비스

서비스가 헤드리스(headless)이면, 컨트롤 플레인이 엔드포인트(Endpoints) 오브젝트에 이 레이블을 추가한다.

kubernetes.io/service-name

예시: kubernetes.io/service-name="nginx"

적용 대상: 서비스

쿠버네티스가 여러 서비스를 구분하기 위해 이 레이블을 사용한다. 현재는 ELB(Elastic Load Balancer) 를 위해서만 사용되고 있다.

endpointslice.kubernetes.io/managed-by

예시: endpointslice.kubernetes.io/managed-by="controller"

적용 대상: 엔드포인트슬라이스(EndpointSlices)

이 레이블은 엔드포인트슬라이스(EndpointSlice)를 어떤 컨트롤러나 엔티티가 관리하는지를 나타내기 위해 사용된다. 이 레이블을 사용함으로써 한 클러스터 내에서 여러 엔드포인트슬라이스 오브젝트가 각각 다른 컨트롤러나 엔티티에 의해 관리될 수 있다.

endpointslice.kubernetes.io/skip-mirror

예시: endpointslice.kubernetes.io/skip-mirror="true"

적용 대상: 엔드포인트(Endpoints)

특정 자원에 이 레이블을 "true" 로 설정하여, EndpointSliceMirroring 컨트롤러가 엔드포인트슬라이스를 이용하여 해당 자원을 미러링하지 않도록 지시할 수 있다.

service.kubernetes.io/service-proxy-name

예시: service.kubernetes.io/service-proxy-name="foo-bar"

적용 대상: 서비스

kube-proxy 에는 커스텀 프록시를 위한 이와 같은 레이블이 있으며, 이 레이블은 서비스 컨트롤을 커스텀 프록시에 위임한다.

experimental.windows.kubernetes.io/isolation-type (deprecated)

예시: experimental.windows.kubernetes.io/isolation-type: "hyperv"

적용 대상: 파드

Hyper-V 격리(isolation)를 사용하여 윈도우 컨테이너를 실행하려면 이 어노테이션을 사용한다. Hyper-V 격리 기능을 활성화하고 Hyper-V 격리가 적용된 컨테이너를 생성하기 위해, kubelet은 기능 게이트 HyperVContainer=true 로 설정하여 실행되어야 하며, 파드에는 experimental.windows.kubernetes.io/isolation-type=hyperv 어노테이션이 설정되어 있어야 한다.

ingressclass.kubernetes.io/is-default-class

예시: ingressclass.kubernetes.io/is-default-class: "true"

적용 대상: 인그레스클래스(IngressClass)

하나의 인그레스클래스 리소스에 이 어노테이션이 "true"로 설정된 경우, 클래스가 명시되지 않은 새로운 인그레스(Ingress) 리소스는 해당 기본 클래스로 할당될 것이다.

kubernetes.io/ingress.class (사용 중단됨)

storageclass.kubernetes.io/is-default-class

예시: storageclass.kubernetes.io/is-default-class=true

적용 대상: 스토리지클래스(StorageClass)

하나의 스토리지클래스(StorageClass) 리소스에 이 어노테이션이 "true"로 설정된 경우, 클래스가 명시되지 않은 새로운 퍼시스턴트볼륨클레임(PersistentVolumeClaim) 리소스는 해당 기본 클래스로 할당될 것이다.

alpha.kubernetes.io/provided-node-ip

예시: alpha.kubernetes.io/provided-node-ip: "10.0.0.1"

적용 대상: 노드

kubelet이 노드에 할당된 IPv4 주소를 명시하기 위해 이 어노테이션을 사용할 수 있다.

kubelet이 "외부" 클라우드 제공자에 의해 실행되었다면, 명령줄 플래그(--node-ip)를 통해 설정된 IP 주소를 명시하기 위해 kubelet이 이 어노테이션을 노드에 설정한다. cloud-controller-manager는 클라우드 제공자에게 이 IP 주소가 유효한지를 검증한다.

batch.kubernetes.io/job-completion-index

예시: batch.kubernetes.io/job-completion-index: "3"

적용 대상: 파드

kube-controller-manager의 잡(Job) 컨트롤러는 Indexed 완료 모드로 생성된 파드에 이 어노테이션을 추가한다.

kubectl.kubernetes.io/default-container

예시: kubectl.kubernetes.io/default-container: "front-end-app"

파드의 기본 컨테이너로 사용할 컨테이너 이름을 지정하는 어노테이션이다. 예를 들어, kubectl logs 또는 kubectl exec 명령을 사용할 때 -c 또는 --container 플래그를 지정하지 않으면, 이 어노테이션으로 명시된 기본 컨테이너를 대상으로 실행될 것이다.

endpoints.kubernetes.io/over-capacity

예시: endpoints.kubernetes.io/over-capacity:truncated

적용 대상: 엔드포인트(Endpoints)

v1.22 이상의 쿠버네티스 클러스터에서, 한 엔드포인트(Endpoints) 리소스가 관리하고 있는 엔드포인트의 수가 1000개 이상이면 엔드포인트 컨트롤러가 해당 엔드포인트 리소스에 이 어노테이션을 추가한다. 이 어노테이션은 해당 엔드포인트 리소스가 용량 초과 되었으며 엔드포인트 컨트롤러가 엔드포인트의 수를 1000으로 줄였음을 나타낸다.

batch.kubernetes.io/job-tracking

예시: batch.kubernetes.io/job-tracking: ""

적용 대상: 잡

잡에 어노테이션이 있으면 컨트롤 플레인은 finalizers를 사용하여 잡 상태 추적 중임을 나타낸다. 어노테이션을 수동으로 추가하거나 제거하지 않는다.

scheduler.alpha.kubernetes.io/preferAvoidPods (deprecated)

적용 대상: 노드

이 어노테이션을 사용하려면 NodePreferAvoidPods 스케줄링 플러그인이 활성화되어 있어야 한다. 해당 플러그인은 쿠버네티스 1.22에서 사용 중단되었다. 대신 테인트와 톨러레이션을 사용한다.

이 이후로 나오는 테인트는 모두 '적용 대상: 노드' 이다.

node.kubernetes.io/not-ready

예시: node.kubernetes.io/not-ready:NoExecute

노드 컨트롤러는 노드의 헬스를 모니터링하여 노드가 사용 가능한 상태인지를 감지하고 그에 따라 이 테인트를 추가하거나 제거한다.

node.kubernetes.io/unreachable

예시: node.kubernetes.io/unreachable:NoExecute

노드 컨트롤러는 노드 컨디션Ready에서 Unknown으로 변경된 노드에 이 테인트를 추가한다.

node.kubernetes.io/unschedulable

예시: node.kubernetes.io/unschedulable:NoSchedule

경쟁 상태(race condition) 발생을 막기 위해, 생성 중인 노드에 이 테인트가 추가된다.

node.kubernetes.io/memory-pressure

예시: node.kubernetes.io/memory-pressure:NoSchedule

kubelet은 노드의 memory.availableallocatableMemory.available을 관측하여 메모리 압박을 감지한다. 그 뒤, 관측한 값을 kubelet에 설정된 문턱값(threshold)과 비교하여 노드 컨디션과 테인트의 추가/삭제 여부를 결정한다.

node.kubernetes.io/disk-pressure

예시: node.kubernetes.io/disk-pressure:NoSchedule

kubelet은 노드의 imagefs.available, imagefs.inodesFree, nodefs.available, nodefs.inodesFree(리눅스에 대해서만)를 관측하여 디스크 압박을 감지한다. 그 뒤, 관측한 값을 kubelet에 설정된 문턱값(threshold)과 비교하여 노드 컨디션과 테인트의 추가/삭제 여부를 결정한다.

node.kubernetes.io/network-unavailable

예시: node.kubernetes.io/network-unavailable:NoSchedule

사용 중인 클라우드 공급자가 추가 네트워크 환경설정을 필요로 한다고 명시하면, kubelet이 이 테인트를 설정한다. 클라우드 상의 네트워크 경로가 올바르게 구성되어야, 클라우드 공급자가 이 테인트를 제거할 것이다.

node.kubernetes.io/pid-pressure

예시: node.kubernetes.io/pid-pressure:NoSchedule

kubelet은 '/proc/sys/kernel/pid_max의 크기의 D-값'과 노드에서 쿠버네티스가 사용 중인 PID를 확인하여, pid.available 지표라고 불리는 '사용 가능한 PID 수'를 가져온다. 그 뒤, 관측한 지표를 kubelet에 설정된 문턱값(threshold)과 비교하여 노드 컨디션과 테인트의 추가/삭제 여부를 결정한다.

node.cloudprovider.kubernetes.io/uninitialized

예시: node.cloudprovider.kubernetes.io/uninitialized:NoSchedule

kubelet이 "외부" 클라우드 공급자에 의해 실행되었다면 노드가 '사용 불가능'한 상태라고 표시하기 위해 이 테인트가 추가되며, 추후 cloud-controller-manager가 이 노드를 초기화하고 이 테인트를 제거한다.

node.cloudprovider.kubernetes.io/shutdown

예시: node.cloudprovider.kubernetes.io/shutdown:NoSchedule

노드의 상태가 클라우드 공급자가 정의한 'shutdown' 상태이면, 이에 따라 노드에 node.cloudprovider.kubernetes.io/shutdown 테인트가 NoSchedule 값으로 설정된다.

pod-security.kubernetes.io/enforce

예시: pod-security.kubernetes.io/enforce: baseline

적용 대상: 네임스페이스

값은 반드시 파드 보안 표준 레벨과 상응하는 privileged, baseline, 또는 restricted 중 하나여야 한다. 특히 enforce 레이블은 표시된 수준에 정의된 요구 사항을 충족하지 않는 레이블 네임스페이스에 모든 파드의 생성을 금지한다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

pod-security.kubernetes.io/enforce-version

예시: pod-security.kubernetes.io/enforce-version: 1.23

적용 대상: 네임스페이스

값은 반드시 latest이거나 v<MAJOR>.<MINOR> 형식의 유효한 쿠버네티스 버전이어야 한다. 설정된 파드의 유효성을 검사할 때 적용할 파드 보안 표준 정책의 버전이 결정된다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

pod-security.kubernetes.io/audit

예시: pod-security.kubernetes.io/audit: baseline

적용 대상: 네임스페이스

값은 반드시 파드 보안 표준 레벨과 상응하는 privileged, baseline, 또는 restricted 중 하나여야 한다. 특히 audit 레이블은 표시된 수준에 정의된 요구 사항을 충족하지 않는 레이블 네임스페이스에 파드를 생성하는 것을 방지하지 않지만, 해당 파드에 audit 어노테이션을 추가한다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

pod-security.kubernetes.io/audit-version

예시: pod-security.kubernetes.io/audit-version: 1.23

적용 대상: 네임스페이스

값은 반드시 latest이거나 v<MAJOR>.<MINOR> 형식의 유효한 쿠버네티스 버전이어야 한다. 설정된 파드의 유효성을 검사할 때 적용할 파드 보안 표준 정책의 버전이 결정된다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

pod-security.kubernetes.io/warn

예시: pod-security.kubernetes.io/warn: baseline

적용 대상: 네임스페이스

값은 반드시 파드 보안 표준 레벨과 상응하는 privileged, baseline, 또는 restricted 중 하나여야 한다. 특히 warn 레이블은 해당 레이블이 달린 네임스페이스에, 표시된 레벨에 명시된 요구 사항을 충족하지 않는 파드를 생성하는 것을 방지하지는 않지만, 그러한 파드가 생성되면 사용자에게 경고를 반환한다. 디플로이먼트, 잡, 스테이트풀셋 등과 같은 파드 템플릿을 포함하는 객체를 만들거나 업데이트할 때에도 경고가 표시된다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

pod-security.kubernetes.io/warn-version

예시: pod-security.kubernetes.io/warn-version: 1.23

적용 대상: 네임스페이스

값은 반드시 latest이거나 v<MAJOR>.<MINOR> 형식의 유효한 쿠버네티스 버전이어야 한다. 설정된 파드의 유효성을 검사할 때 적용할 파드 보안 표준 정책의 버전이 결정된다. 디플로이먼트, 잡, 스테이트풀셋 등과 같은 파드 템플릿을 포함하는 객체를 만들거나 업데이트할 때에도 경고가 표시된다.

더 많은 정보는 네임스페이스에서 파드 보안 적용을 참고한다.

6.5 - 쿠버네티스 이슈와 보안

6.5.1 - 쿠버네티스 이슈 트래커

보안 문제를 보고하려면 쿠버네티스 보안 공개 프로세스를 따른다.

쿠버네티스 코드 작업 및 공개 이슈는 깃허브 이슈를 사용하여 추적된다.

보안에 관련된 공지사항은 [email protected] 메일 리스트로 전송된다.

6.5.2 - 쿠버네티스 보안과 공개 정보

이 페이지는 쿠버네티스 보안 및 공개 정보를 설명한다.

보안 공지

보안 및 주요 API 공지에 대한 이메일을 위해서는 kubernetes-security-announce) 그룹에 가입한다.

이 링크를 사용하여 RSS 피드도 구독할 수 있다.

취약점 보고

우리는 쿠버네티스 오픈소스 커뮤니티에 취약점을 보고하는 보안 연구원들과 사용자들에게 매우 감사하고 있다. 모든 보고서는 커뮤니티 자원 봉사자들에 의해 철저히 조사된다.

보고서를 작성하려면, 쿠버네티스 버그 현상금 프로그램에 취약점을 제출한다. 이를 통해 표준화된 응답시간으로 취약점을 분류하고 처리할 수 있다.

또한, 보안 세부 내용과 모든 쿠버네티스 버그 보고서로 부터 예상되는 세부사항을 [email protected]로 이메일을 보낸다.

보안 대응 위원회(Security Response Committee) 구성원의 GPG 키를 사용하여 이 목록으로 이메일을 암호화할 수 있다. GPG를 사용한 암호화는 공개할 필요가 없다.

언제 취약점을 보고해야 하는가?

  • 쿠버네티스에서 잠재적인 보안 취약점을 발견했다고 생각하는 경우
  • 취약성이 쿠버네티스에 어떤 영향을 미치는지 확신할 수 없는 경우
  • 쿠버네티스가 의존하는 다른 프로젝트에서 취약점을 발견한 경우
    • 자체 취약성 보고 및 공개 프로세스가 있는 프로젝트의 경우 직접 보고한다.

언제 취약점을 보고하지 말아야 하는가?

  • 보안을 위해 쿠버네티스 구성요소를 조정하는데 도움이 필요한 경우
  • 보안 관련 업데이트를 적용하는 데 도움이 필요한 경우
  • 보안 관련 문제가 아닌 경우

보안 취약점 대응

각 보고서는 보안 대응 위원회 위원들에 의해 작업일 3일 이내에 인정되고 분석된다. 이렇게 하면 보안 릴리스 프로세스가 시작된다.

보안 대응 위원회와 공유하는 모든 취약성 정보는 쿠버네티스 프로젝트 내에 있으며, 문제를 해결할 필요가 없는 한 다른 프로젝트에 전파되지 않는다.

보안 문제가 심사에서 확인된 수정, 릴리스 계획으로 이동함에 따라 리포터를 계속 업데이트할 것이다.

공개 시기

공개 날짜는 쿠버네티스 보안 대응 위원회와 버그 제출자가 협상한다. 사용자 완화가 가능해지면 가능한 빨리 버그를 완전히 공개하는 것이 좋다. 버그 또는 픽스가 아직 완전히 이해되지 않았거나 솔루션이 제대로 테스트되지 않았거나 벤더 협력을 위해 공개를 지연시키는 것이 합리적이다. 공개 기간은 즉시(특히 이미 공개적으로 알려진 경우)부터 몇 주까지다. 간단한 완화 기능이 있는 취약점의 경우 보고 날짜부터 공개 날짜까지는 7일 정도 소요될 것으로 예상된다. 쿠버네티스 보안 대응 위원회는 공개 날짜를 설정할 때 최종 결정권을 갖는다.

6.6 - 설치 도구

6.6.1 - Kubeadm

Kubeadm은 쿠버네티스 클러스터 생성을 위한 "빠른 경로"의 모범 사례로 kubeadm initkubeadm join 을 제공하도록 만들어진 도구이다.

kubeadm은 실행 가능한 최소 클러스터를 시작하고 실행하는 데 필요한 작업을 수행한다. 설계 상, 시스템 프로비저닝이 아닌 부트스트랩(bootstrapping)만 다룬다. 마찬가지로, 쿠버네티스 대시보드, 모니터링 솔루션 및 클라우드별 애드온과 같은 다양한 있으면 좋은(nice-to-have) 애드온을 설치하는 것은 범위에 포함되지 않는다.

대신, 우리는 더 높은 수준의 맞춤형 도구가 kubeadm 위에 구축될 것으로 기대하며, 이상적으로는, 모든 배포의 기반으로 kubeadm을 사용하면 규격을 따르는 클러스터를 더 쉽게 생성할 수 있다.

설치 방법

kubeadm을 설치하려면, 설치 가이드를 참고한다.

다음 내용

  • kubeadm init: 쿠버네티스 컨트롤 플레인 노드를 부트스트랩한다.
  • kubeadm join: 쿠버네티스 워커(worker) 노드를 부트스트랩하고 클러스터에 조인시킨다.
  • kubeadm upgrade: 쿠버네티스 클러스터를 새로운 버전으로 업그레이드한다.
  • kubeadm config: kubeadm v1.7.x 이하의 버전을 사용하여 클러스터를 초기화한 경우, kubeadm upgrade 를 위해 사용자의 클러스터를 구성한다.
  • kubeadm token: kubeadm join 을 위한 토큰을 관리한다.
  • kubeadm reset: kubeadm init 또는 kubeadm join 에 의한 호스트의 모든 변경 사항을 되돌린다.
  • kubeadm certs: 쿠버네티스 인증서를 관리한다.
  • kubeadm kubeconfig: kubeconfig 파일을 관리한다.
  • kubeadm version: kubeadm 버전을 출력한다.
  • kubeadm alpha: 커뮤니티에서 피드백을 수집하기 위해서 기능 미리 보기를 제공한다.

6.6.1.1 - Kubeadm Generated

6.7 - 포트와 프로토콜

물리적 네트워크 방화벽이 있는 온프레미스 데이터 센터 또는 퍼블릭 클라우드의 가상 네트워크와 같이 네트워크 경계가 엄격한 환경에서 쿠버네티스를 실행할 때, 쿠버네티스 구성 요소에서 사용하는 포트와 프로토콜을 알고 있는 것이 유용하다.

컨트롤 플레인

프로토콜 방향 포트 범위 용도 사용 주체
TCP 인바운드 6443 쿠버네티스 API 서버 전부
TCP 인바운드 2379-2380 etcd 서버 클라이언트 API kube-apiserver, etcd
TCP 인바운드 10250 Kubelet API Self, 컨트롤 플레인
TCP 인바운드 10259 kube-scheduler Self
TCP 인바운드 10257 kube-controller-manager Self

etcd 포트가 컨트롤 플레인 섹션에 포함되어 있지만, 외부 또는 사용자 지정 포트에서 자체 etcd 클러스터를 호스팅할 수도 있다.

워커 노드

프로토콜 방향 포트 범위 용도 사용 주체
TCP 인바운드 10250 Kubelet API Self, 컨트롤 플레인
TCP 인바운드 30000-32767 NodePort 서비스† 전부

노드포트(NodePort) 서비스의 기본 포트 범위.

모든 기본 포트 번호를 재정의할 수 있다. 사용자 지정 포트를 사용하는 경우 여기에 언급된 기본값 대신 해당 포트를 열어야 한다.

종종 발생하는 한 가지 일반적인 예는 API 서버 포트를 443으로 변경하는 경우이다. 또는, API 서버의 기본 포트를 그대로 유지하고, 443 포트에서 수신 대기하는 로드 밸런서 뒤에 API 서버를 두고, 로드 밸런서에서 API 서버로 가는 요청을 API 서버의 기본 포트로 라우팅할 수도 있다.

6.8 - kubectl

6.8.1 - kubectl 개요

Kubectl은 쿠버네티스 클러스터를 제어하기 위한 커맨드 라인 도구이다. 구성을 위해, kubectl 은 config 파일을 $HOME/.kube 에서 찾는다. KUBECONFIG 환경 변수를 설정하거나 --kubeconfig 플래그를 설정하여 다른 kubeconfig 파일을 지정할 수 있다.

이 개요는 kubectl 구문을 다루고, 커맨드 동작을 설명하며, 일반적인 예제를 제공한다. 지원되는 모든 플래그 및 하위 명령을 포함한 각 명령에 대한 자세한 내용은 kubectl 참조 문서를 참고한다. 설치 방법에 대해서는 kubectl 설치를 참고한다.

구문

터미널 창에서 kubectl 명령을 실행하려면 다음의 구문을 사용한다.

kubectl [command] [TYPE] [NAME] [flags]

다음은 command, TYPE, NAMEflags 에 대한 설명이다.

  • command: 하나 이상의 리소스에서 수행하려는 동작을 지정한다. 예: create, get, describe, delete

  • TYPE: 리소스 타입을 지정한다. 리소스 타입은 대소문자를 구분하지 않으며 단수형, 복수형 또는 약어 형식을 지정할 수 있다. 예를 들어, 다음의 명령은 동일한 출력 결과를 생성한다.

    kubectl get pod pod1
    kubectl get pods pod1
    kubectl get po pod1
    
  • NAME: 리소스 이름을 지정한다. 이름은 대소문자를 구분한다. 이름을 생략하면, 모든 리소스에 대한 세부 사항이 표시된다. 예: kubectl get pods

    여러 리소스에 대한 작업을 수행할 때, 타입 및 이름별로 각 리소스를 지정하거나 하나 이상의 파일을 지정할 수 있다.

    • 타입 및 이름으로 리소스를 지정하려면 다음을 참고한다.

      • 리소스가 모두 동일한 타입인 경우 리소스를 그룹화하려면 다음을 사용한다. TYPE1 name1 name2 name<#>
        예: kubectl get pod example-pod1 example-pod2

      • 여러 리소스 타입을 개별적으로 지정하려면 다음을 사용한다. TYPE1/name1 TYPE1/name2 TYPE2/name3 TYPE<#>/name<#>
        예: kubectl get pod/example-pod1 replicationcontroller/example-rc1

    • 하나 이상의 파일로 리소스를 지정하려면 다음을 사용한다. -f file1 -f file2 -f file<#>

  • flags: 선택적 플래그를 지정한다. 예를 들어, -s 또는 --server 플래그를 사용하여 쿠버네티스 API 서버의 주소와 포트를 지정할 수 있다.

도움이 필요하다면, 터미널 창에서 kubectl help 를 실행한다.

클러스터 내 인증과 네임스페이스 오버라이드

기본적으로 kubectl은 먼저 자신이 파드 안에서 실행되고 있는지, 즉 클러스터 안에 있는지를 판별한다. 이를 위해 KUBERNETES_SERVICE_HOSTKUBERNETES_SERVICE_PORT 환경 변수, 그리고 서비스 어카운트 토큰 파일이 /var/run/secrets/kubernetes.io/serviceaccount/token 경로에 있는지를 확인한다. 세 가지가 모두 감지되면, 클러스터 내 인증이 적용된다.

하위 호환성을 위해, 클러스터 내 인증 시에 POD_NAMESPACE 환경 변수가 설정되어 있으면, 서비스 어카운트 토큰의 기본 네임스페이스 설정을 오버라이드한다. 기본 네임스페이스 설정에 의존하는 모든 매니페스트와 도구가 영향을 받을 것이다.

POD_NAMESPACE 환경 변수

POD_NAMESPACE 환경 변수가 설정되어 있으면, 네임스페이스에 속하는 자원에 대한 CLI 작업은 환경 변수에 설정된 네임스페이스를 기본값으로 사용한다. 예를 들어, 환경 변수가 seattle로 설정되어 있으면, kubectl get pods 명령은 seattle 네임스페이스에 있는 파드 목록을 반환한다. 이는 파드가 네임스페이스에 속하는 자원이며, 명령어에 네임스페이스를 특정하지 않았기 때문이다. kubectl api-resources 명령을 실행하고 결과를 확인하여 특정 자원이 네임스페이스에 속하는 자원인지 판별한다.

명시적으로 --namespace <value> 인자를 사용하면 위와 같은 동작을 오버라이드한다.

kubectl이 서비스어카운트 토큰을 관리하는 방법

만약

  • 쿠버네티스 서비스 어카운트 토큰 파일이 /var/run/secrets/kubernetes.io/serviceaccount/token 경로에 마운트되어 있고,
  • KUBERNETES_SERVICE_HOST 환경 변수가 설정되어 있고,
  • KUBERNETES_SERVICE_PORT 환경 변수가 설정되어 있고,
  • kubectl 명령에 네임스페이스를 명시하지 않으면 kubectl은 자신이 클러스터 내부에서 실행되고 있다고 가정한다. kubectl은 해당 서비스어카운트의 네임스페이스(파드의 네임스페이스와 동일하다)를 인식하고 해당 네임스페이스에 대해 동작한다. 이는 클러스터 외부에서 실행되었을 때와는 다른데, kubectl이 클러스터 외부에서 실행되었으며 네임스페이스가 명시되지 않은 경우 kubectl은 default 네임스페이스에 대해 동작한다.

명령어

다음 표에는 모든 kubectl 작업에 대한 간단한 설명과 일반적인 구문이 포함되어 있다.

명령어 구문 설명
alpha kubectl alpha SUBCOMMAND [flags] 쿠버네티스 클러스터에서 기본적으로 활성화되어 있지 않은 알파 기능의 사용할 수 있는 명령을 나열한다.
annotate kubectl annotate (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags] 하나 이상의 리소스 어노테이션을 추가하거나 업데이트한다.
api-resources kubectl api-resources [flags] 사용 가능한 API 리소스를 나열한다.
api-versions kubectl api-versions [flags] 사용 가능한 API 버전을 나열한다.
apply kubectl apply -f FILENAME [flags] 파일이나 표준입력(stdin)으로부터 리소스에 구성 변경 사항을 적용한다.
attach kubectl attach POD -c CONTAINER [-i] [-t] [flags] 실행 중인 컨테이너에 연결하여 출력 스트림을 보거나 표준입력을 통해 컨테이너와 상호 작용한다.
auth kubectl auth [flags] [options] 승인을 검사한다.
autoscale kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags] 레플리케이션 컨트롤러에서 관리하는 파드 집합을 자동으로 조정한다.
certificate kubectl certificate SUBCOMMAND [options] 인증서 리소스를 수정한다.
cluster-info kubectl cluster-info [flags] 클러스터의 마스터와 서비스에 대한 엔드포인트 정보를 표시한다.
completion kubectl completion SHELL [options] 지정된 셸(bash 또는 zsh)에 대한 셸 완성 코드를 출력한다.
config kubectl config SUBCOMMAND [flags] kubeconfig 파일을 수정한다. 세부 사항은 개별 하위 명령을 참고한다.
convert kubectl convert -f FILENAME [options] 다른 API 버전 간에 구성 파일을 변환한다. YAML 및 JSON 형식이 모두 허용된다. 참고 - kubectl-convert 플러그인을 설치해야 한다.
cordon kubectl cordon NODE [options] 노드를 스케줄 불가능(unschedulable)으로 표시한다.
cp kubectl cp <file-spec-src> <file-spec-dest> [options] 컨테이너에서 그리고 컨테이너로 파일 및 디렉터리를 복사한다.
create kubectl create -f FILENAME [flags] 파일이나 표준입력에서 하나 이상의 리소스를 생성한다.
delete kubectl delete (-f FILENAME | TYPE [NAME | /NAME | -l label | --all]) [flags] 파일, 표준입력 또는 레이블 셀렉터, 이름, 리소스 셀렉터 또는 리소스를 지정하여 리소스를 삭제한다.
describe kubectl describe (-f FILENAME | TYPE [NAME_PREFIX | /NAME | -l label]) [flags] 하나 이상의 리소스의 자세한 상태를 표시한다.
diff kubectl diff -f FILENAME [flags] 라이브 구성에 대해 파일이나 표준입력의 차이점을 출력한다.
drain kubectl drain NODE [options] 유지 보수를 준비 중인 노드를 드레인한다.
edit kubectl edit (-f FILENAME | TYPE NAME | TYPE/NAME) [flags] 기본 편집기를 사용하여 서버에서 하나 이상의 리소스 정의를 편집하고 업데이트한다.
exec kubectl exec POD [-c CONTAINER] [-i] [-t] [flags] [-- COMMAND [args...]] 파드의 컨테이너에 대해 명령을 실행한다.
explain kubectl explain [--recursive=false] [flags] 파드, 노드, 서비스 등의 다양한 리소스에 대한 문서를 출력한다.
expose kubectl expose (-f FILENAME | TYPE NAME | TYPE/NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type] [flags] 레플리케이션 컨트롤러, 서비스 또는 파드를 새로운 쿠버네티스 서비스로 노출한다.
get kubectl get (-f FILENAME | TYPE [NAME | /NAME | -l label]) [--watch] [--sort-by=FIELD] [[-o | --output]=OUTPUT_FORMAT] [flags] 하나 이상의 리소스를 나열한다.
kustomize kubectl kustomize <dir> [flags] [options] kustomization.yaml 파일의 지시 사항에서 생성된 API 리소스 집합을 나열한다. 인수는 파일을 포함하는 디렉터리의 경로이거나, 리포지터리 루트와 관련하여 경로 접미사가 동일한 git 리포지터리 URL이어야 한다.
label kubectl label (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags] 하나 이상의 리소스 레이블을 추가하거나 업데이트한다.
logs kubectl logs POD [-c CONTAINER] [--follow] [flags] 파드의 컨테이너에 대한 로그를 출력한다.
options kubectl options 모든 명령에 적용되는 전역 커맨드 라인 옵션을 나열한다.
patch kubectl patch (-f FILENAME | TYPE NAME | TYPE/NAME) --patch PATCH [flags] 전략적 병합 패치 프로세스를 사용하여 리소스의 하나 이상의 필드를 업데이트한다.
plugin kubectl plugin [flags] [options] 플러그인과 상호 작용하기 위한 유틸리티를 제공한다.
port-forward kubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N] [flags] 하나 이상의 로컬 포트를 파드로 전달한다.
proxy kubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] [flags] 쿠버네티스 API 서버에 프록시를 실행한다.
replace kubectl replace -f FILENAME 파일 또는 표준입력에서 리소스를 교체한다.
rollout kubectl rollout SUBCOMMAND [options] 리소스의 롤아웃을 관리한다. 유효한 리소스 타입에는 디플로이먼트(deployment), 데몬셋(daemonset)과 스테이트풀셋(statefulset)이 포함된다.
run kubectl run NAME --image=image [--env="key=value"] [--port=port] [--dry-run=server|client|none] [--overrides=inline-json] [flags] 클러스터에서 지정된 이미지를 실행한다.
scale kubectl scale (-f FILENAME | TYPE NAME | TYPE/NAME) --replicas=COUNT [--resource-version=version] [--current-replicas=count] [flags] 지정된 레플리케이션 컨트롤러의 크기를 업데이트한다.
set kubectl set SUBCOMMAND [options] 애플리케이션 리소스를 구성한다.
taint kubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options] 하나 이상의 노드에서 테인트(taint)를 업데이트한다.
top kubectl top [flags] [options] 리소스(CPU/메모리/스토리지) 사용량을 표시한다.
uncordon kubectl uncordon NODE [options] 노드를 스케줄 가능(schedulable)으로 표시한다.
version kubectl version [--client] [flags] 클라이언트와 서버에서 실행 중인 쿠버네티스 버전을 표시한다.
wait kubectl wait ([-f FILENAME] | resource.group/resource.name | resource.group [(-l label | --all)]) [--for=delete|--for condition=available] [options] 실험(experimental) 기능: 하나 이상의 리소스에서 특정 조건을 기다린다.

명령 동작에 대한 자세한 내용을 배우려면 kubectl 참조 문서를 참고한다.

리소스 타입

다음 표에는 지원되는 모든 리소스 타입과 해당 약어가 나열되어 있다.

(이 출력은 kubectl api-resources 에서 확인할 수 있으며, 쿠버네티스 1.19.1 에서의 출력을 기준으로 한다.)

NAME SHORTNAMES APIGROUP NAMESPACED KIND
bindings true Binding
componentstatuses cs false ComponentStatus
configmaps cm true ConfigMap
endpoints ep true Endpoints
events ev true Event
limitranges limits true LimitRange
namespaces ns false Namespace
nodes no false Node
persistentvolumeclaims pvc true PersistentVolumeClaim
persistentvolumes pv false PersistentVolume
pods po true Pod
podtemplates true PodTemplate
replicationcontrollers rc true ReplicationController
resourcequotas quota true ResourceQuota
secrets true Secret
serviceaccounts sa true ServiceAccount
services svc true Service
mutatingwebhookconfigurations admissionregistration.k8s.io false MutatingWebhookConfiguration
validatingwebhookconfigurations admissionregistration.k8s.io false ValidatingWebhookConfiguration
customresourcedefinitions crd,crds apiextensions.k8s.io false CustomResourceDefinition
apiservices apiregistration.k8s.io false APIService
controllerrevisions apps true ControllerRevision
daemonsets ds apps true DaemonSet
deployments deploy apps true Deployment
replicasets rs apps true ReplicaSet
statefulsets sts apps true StatefulSet
tokenreviews authentication.k8s.io false TokenReview
localsubjectaccessreviews authorization.k8s.io true LocalSubjectAccessReview
selfsubjectaccessreviews authorization.k8s.io false SelfSubjectAccessReview
selfsubjectrulesreviews authorization.k8s.io false SelfSubjectRulesReview
subjectaccessreviews authorization.k8s.io false SubjectAccessReview
horizontalpodautoscalers hpa autoscaling true HorizontalPodAutoscaler
cronjobs cj batch true CronJob
jobs batch true Job
certificatesigningrequests csr certificates.k8s.io false CertificateSigningRequest
leases coordination.k8s.io true Lease
endpointslices discovery.k8s.io true EndpointSlice
events ev events.k8s.io true Event
ingresses ing extensions true Ingress
flowschemas flowcontrol.apiserver.k8s.io false FlowSchema
prioritylevelconfigurations flowcontrol.apiserver.k8s.io false PriorityLevelConfiguration
ingressclasses networking.k8s.io false IngressClass
ingresses ing networking.k8s.io true Ingress
networkpolicies netpol networking.k8s.io true NetworkPolicy
runtimeclasses node.k8s.io false RuntimeClass
poddisruptionbudgets pdb policy true PodDisruptionBudget
podsecuritypolicies psp policy false PodSecurityPolicy
clusterrolebindings rbac.authorization.k8s.io false ClusterRoleBinding
clusterroles rbac.authorization.k8s.io false ClusterRole
rolebindings rbac.authorization.k8s.io true RoleBinding
roles rbac.authorization.k8s.io true Role
priorityclasses pc scheduling.k8s.io false PriorityClass
csidrivers storage.k8s.io false CSIDriver
csinodes storage.k8s.io false CSINode
storageclasses sc storage.k8s.io false StorageClass
volumeattachments storage.k8s.io false VolumeAttachment

출력 옵션

특정 명령의 출력을 서식화하거나 정렬하는 방법에 대한 정보는 다음 섹션을 참고한다. 다양한 출력 옵션을 지원하는 명령에 대한 자세한 내용은 kubectl 참조 문서를 참고한다.

출력 서식화

모든 kubectl 명령의 기본 출력 형식은 사람이 읽을 수 있는 일반 텍스트 형식이다. 특정 형식으로 터미널 창에 세부 정보를 출력하려면, 지원되는 kubectl 명령에 -o 또는 --output 플래그를 추가할 수 있다.

구문

kubectl [command] [TYPE] [NAME] -o <output_format>

kubectl 명령에 따라, 다음과 같은 출력 형식이 지원된다.

출력 형식 설명
-o custom-columns=<spec> 쉼표로 구분된 사용자 정의 열 목록을 사용하여 테이블을 출력한다.
-o custom-columns-file=<filename> <filename> 파일에서 사용자 정의 열 템플릿을 사용하여 테이블을 출력한다.
-o json JSON 형식의 API 오브젝트를 출력한다.
-o jsonpath=<template> jsonpath 표현식에 정의된 필드를 출력한다.
-o jsonpath-file=<filename> <filename> 파일에서 jsonpath 표현식으로 정의된 필드를 출력한다.
-o name 리소스 이름만 출력한다.
-o wide 추가 정보가 포함된 일반 텍스트 형식으로 출력된다. 파드의 경우, 노드 이름이 포함된다.
-o yaml YAML 형식의 API 오브젝트를 출력한다.
예제

이 예제에서, 다음의 명령은 단일 파드에 대한 세부 정보를 YAML 형식의 오브젝트로 출력한다.

kubectl get pod web-pod-13je7 -o yaml

기억하기: 각 명령이 지원하는 출력 형식에 대한 자세한 내용은 kubectl 참조 문서를 참고한다.

사용자 정의 열

사용자 정의 열을 정의하고 원하는 세부 정보만 테이블에 출력하려면, custom-columns 옵션을 사용할 수 있다. 사용자 정의 열을 인라인으로 정의하거나 템플릿 파일을 사용하도록 선택할 수 있다. -o custom-columns=<spec> 또는 -o custom-columns-file=<filename>

예제

인라인:

kubectl get pods <pod-name> -o custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion

템플릿 파일:

kubectl get pods <pod-name> -o custom-columns-file=template.txt

template.txt 파일에 포함된 내용은 다음과 같다.

NAME          RSRC
metadata.name metadata.resourceVersion

두 명령 중 하나를 실행한 결과는 다음과 비슷하다.

NAME           RSRC
submit-queue   610995

서버측 열

kubectl 는 서버에서 오브젝트에 대한 특정 열 정보 수신을 지원한다. 이는 클라이언트가 출력할 수 있도록, 주어진 리소스에 대해 서버가 해당 리소스와 관련된 열과 행을 반환한다는 것을 의미한다. 이는 서버가 출력의 세부 사항을 캡슐화하도록 하여, 동일한 클러스터에 대해 사용된 클라이언트에서 사람이 읽을 수 있는 일관된 출력을 허용한다.

이 기능은 기본적으로 활성화되어 있다. 사용하지 않으려면, kubectl get 명령에 --server-print=false 플래그를 추가한다.

예제

파드 상태에 대한 정보를 출력하려면, 다음과 같은 명령을 사용한다.

kubectl get pods <pod-name> --server-print=false

출력 결과는 다음과 비슷하다.

NAME       AGE
pod-name   1m

오브젝트 목록 정렬

터미널 창에서 정렬된 목록으로 오브젝트를 출력하기 위해, 지원되는 kubectl 명령에 --sort-by 플래그를 추가할 수 있다. --sort-by 플래그와 함께 숫자나 문자열 필드를 지정하여 오브젝트를 정렬한다. 필드를 지정하려면, jsonpath 표현식을 사용한다.

구문

kubectl [command] [TYPE] [NAME] --sort-by=<jsonpath_exp>
예제

이름별로 정렬된 파드 목록을 출력하려면, 다음을 실행한다.

kubectl get pods --sort-by=.metadata.name

예제: 일반적인 작업

다음 예제 세트를 사용하여 일반적으로 사용되는 kubectl 조작 실행에 익숙해진다.

kubectl apply - 파일 또는 표준입력에서 리소스를 적용하거나 업데이트한다.

# example-service.yaml의 정의를 사용하여 서비스를 생성한다.
kubectl apply -f example-service.yaml

# example-controller.yaml의 정의를 사용하여 레플리케이션 컨트롤러를 생성한다.
kubectl apply -f example-controller.yaml

# <directory> 디렉터리 내의 .yaml, .yml 또는 .json 파일에 정의된 오브젝트를 생성한다.
kubectl apply -f <directory>

kubectl get - 하나 이상의 리소스를 나열한다.

# 모든 파드를 일반 텍스트 출력 형식으로 나열한다.
kubectl get pods

# 모든 파드를 일반 텍스트 출력 형식으로 나열하고 추가 정보(예: 노드 이름)를 포함한다.
kubectl get pods -o wide

# 지정된 이름의 레플리케이션 컨트롤러를 일반 텍스트 출력 형식으로 나열한다. 팁: 'replicationcontroller' 리소스 타입을 'rc'로 짧게 바꿔쓸 수 있다.
kubectl get replicationcontroller <rc-name>

# 모든 레플리케이션 컨트롤러와 서비스를 일반 텍스트 출력 형식으로 함께 나열한다.
kubectl get rc,services

# 모든 데몬 셋을 일반 텍스트 출력 형식으로 나열한다.
kubectl get ds

# 노드 server01에서 실행 중인 모든 파드를 나열한다.
kubectl get pods --field-selector=spec.nodeName=server01

kubectl describe - 초기화되지 않은 리소스를 포함하여 하나 이상의 리소스의 기본 상태를 디폴트로 표시한다.

# 노드 이름이 <node-name>인 노드의 세부 사항을 표시한다.
kubectl describe nodes <node-name>

# 파드 이름이 <pod-name> 인 파드의 세부 정보를 표시한다.
kubectl describe pods/<pod-name>

# 이름이 <rc-name>인 레플리케이션 컨트롤러가 관리하는 모든 파드의 세부 정보를 표시한다.
# 기억하기: 레플리케이션 컨트롤러에서 생성된 모든 파드에는 레플리케이션 컨트롤러 이름이 접두사로 붙는다.
kubectl describe pods <rc-name>

# 모든 파드의 정보를 출력한다.
kubectl describe pods

kubectl delete - 파일, 표준입력 또는 레이블 선택기, 이름, 리소스 선택기나 리소스를 지정하여 리소스를 삭제한다.

# pod.yaml 파일에 지정된 타입과 이름을 사용하여 파드를 삭제한다.
kubectl delete -f pod.yaml

# '<label-key>=<label-value>' 레이블이 있는 모든 파드와 서비스를 삭제한다.
kubectl delete pods,services -l <label-key>=<label-value>

# 초기화되지 않은 파드를 포함한 모든 파드를 삭제한다.
kubectl delete pods --all

kubectl exec - 파드의 컨테이너에 대해 명령을 실행한다.

# 파드 <pod-name>에서 'date'를 실행한 결과를 얻는다. 기본적으로, 첫 번째 컨테이너에서 출력된다.
kubectl exec <pod-name> -- date

# 파드 <pod-name>의 <container-name> 컨테이너에서 'date'를 실행하여 출력 결과를 얻는다.
kubectl exec <pod-name> -c <container-name> -- date

# 파드 <pod-name>에서 대화식 TTY를 연결해 /bin/bash를 실행한다. 기본적으로, 첫 번째 컨테이너에서 출력된다.
kubectl exec -ti <pod-name> -- /bin/bash

kubectl logs - 파드의 컨테이너에 대한 로그를 출력한다.

# 파드 <pod-name>에서 로그의 스냅샷을 반환한다.
kubectl logs <pod-name>

# 파드 <pod-name>에서 로그 스트리밍을 시작한다. 이것은 리눅스 명령 'tail -f'와 비슷하다.
kubectl logs -f <pod-name>

kubectl diff - 제안된 클러스터 업데이트의 차이점을 본다.

# "pod.json"에 포함된 리소스의 차이점을 출력한다.
kubectl diff -f pod.json

# 표준입력에서 파일을 읽어 차이점을 출력한다.
cat service.yaml | kubectl diff -f -

예제: 플러그인 작성 및 사용

kubectl 플러그인 작성과 사용에 익숙해지려면 다음의 예제 세트를 사용한다.

# 어떤 언어로든 간단한 플러그인을 만들고 "kubectl-" 접두사로
# 시작하도록 실행 파일의 이름을 지정한다.
cat ./kubectl-hello
#!/bin/sh

# 이 플러그인은 "hello world"라는 단어를 출력한다
echo "hello world"

작성한 플러그인을 실행 가능하게 한다

chmod a+x ./kubectl-hello

# 그리고 PATH의 위치로 옮긴다
sudo mv ./kubectl-hello /usr/local/bin
sudo chown root:root /usr/local/bin

# 이제 kubectl 플러그인을 만들고 "설치했다".
# kubectl에서 플러그인을 일반 명령처럼 호출하여 플러그인을 사용할 수 있다
kubectl hello
hello world
# 플러그인을 배치한 $PATH의 폴더에서 플러그인을 삭제하여,
# 플러그인을 "제거"할 수 있다
sudo rm /usr/local/bin/kubectl-hello

kubectl 에 사용할 수 있는 모든 플러그인을 보려면, kubectl plugin list 하위 명령을 사용한다.

kubectl plugin list

출력 결과는 다음과 비슷하다.

The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
/usr/local/bin/kubectl-bar

kubectl plugin list 는 또한 실행 가능하지 않거나, 다른 플러그인에 의해 차단된 플러그인에 대해 경고한다. 예를 들면 다음과 같다.

sudo chmod -x /usr/local/bin/kubectl-foo # 실행 권한 제거
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo identified as a plugin, but it is not executable
/usr/local/bin/kubectl-bar

error: one plugin warning was found

플러그인은 기존 kubectl 명령 위에 보다 복잡한 기능을 구축하는 수단으로 생각할 수 있다.

cat ./kubectl-whoami

다음 몇 가지 예는 이미 kubectl-whoami 에 다음 내용이 있다고 가정한다.

#!/bin/bash

# 이 플러그인은 현재 선택된 컨텍스트를 기반으로 현재 사용자에 대한
# 정보를 출력하기 위해 'kubectl config' 명령을 사용한다.
kubectl config view --template='{{ range .contexts }}{{ if eq .name "'$(kubectl config current-context)'" }}Current user: {{ printf "%s\n" .context.user }}{{ end }}{{ end }}'

위의 플러그인을 실행하면 KUBECONFIG 파일에서 현재의 컨텍스트에 대한 사용자가 포함된 출력이 제공된다.

# 파일을 실행 가능하게 한다
sudo chmod +x ./kubectl-whoami

# 그리고 PATH로 옮긴다
sudo mv ./kubectl-whoami /usr/local/bin

kubectl whoami
Current user: plugins-user

다음 내용

  • kubectl 명령을 사용하여 시작한다.

  • 플러그인에 대한 자세한 내용은 cli plugin 예제를 참고한다.

6.8.2 - JSONPath 지원

Kubectl은 JSONPath 템플릿을 지원한다.

JSONPath 템플릿은 중괄호 {}로 둘러싸인 JSONPath 표현식으로 구성된다. Kubectl은 JSONPath 표현식을 사용하여 JSON 오브젝트의 특정 필드를 필터링하고 출력 형식을 지정한다. 원본 JSONPath 템플릿 구문 외에도 다음과 같은 기능과 구문이 유효하다.

  1. 큰따옴표를 사용하여 JSONPath 표현식 내부의 텍스트를 인용한다.
  2. 목록을 반복하려면 range, end 오퍼레이터를 사용한다.
  3. 목록에서 뒤로 이동하려면 negative slice 인덱스를 사용한다. negative 인덱스는 목록을 "순환(wrap around)" 하지 않으며, -index + listLength >= 0 인 한 유효하다.

JSON 입력 시 다음과 같다.

{
  "kind": "List",
  "items":[
    {
      "kind":"None",
      "metadata":{"name":"127.0.0.1"},
      "status":{
        "capacity":{"cpu":"4"},
        "addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}]
      }
    },
    {
      "kind":"None",
      "metadata":{"name":"127.0.0.2"},
      "status":{
        "capacity":{"cpu":"8"},
        "addresses":[
          {"type": "LegacyHostIP", "address":"127.0.0.2"},
          {"type": "another", "address":"127.0.0.3"}
        ]
      }
    }
  ],
  "users":[
    {
      "name": "myself",
      "user": {}
    },
    {
      "name": "e2e",
      "user": {"username": "admin", "password": "secret"}
    }
  ]
}
Function Description Example Result
text 일반 텍스트 kind is {.kind} kind is List
@ 현재 오브젝트 {@} 입력과 동일
. or [] 자식 오퍼레이터 {.kind}, {['kind']} or {['name\.type']} List
.. 재귀 하향(recursive descent) {..name} 127.0.0.1 127.0.0.2 myself e2e
* 와일드 카드. 모든 오브젝트 가져오기 {.items[*].metadata.name} [127.0.0.1 127.0.0.2]
[start:end:step] 아래 첨자 오퍼레이터 {.users[0].name} myself
[,] 조합 오퍼레이터 {.items[*]['metadata.name', 'status.capacity']} 127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]
?() 필터 {.users[?(@.name=="e2e")].user.password} secret
range, end 반복 목록 {range .items[*]}[{.metadata.name}, {.status.capacity}] {end} [127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]]
'' 해석된 문자열 인용 {range .items[*]}{.metadata.name}{'\t'}{end} 127.0.0.1 127.0.0.2

kubectl 및 JSONPath 표현식을 사용하는 예는 다음과 같다.

kubectl get pods -o json
kubectl get pods -o=jsonpath='{@}'
kubectl get pods -o=jsonpath='{.items[0]}'
kubectl get pods -o=jsonpath='{.items[0].metadata.name}'
kubectl get pods -o=jsonpath="{.items[*]['metadata.name', 'status.capacity']}"
kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.startTime}{"\n"}{end}'

6.8.3 - kubectl

시놉시스

kubectl은 쿠버네티스 클러스터 관리자를 제어한다.

자세한 정보는 kubectl 개요를 확인한다.

kubectl [flags]

옵션

--add-dir-header
true인 경우, 로그 메시지의 헤더에 파일 디렉터리를 추가한다.
--alsologtostderr
표준 에러와 파일에 로그를 기록한다.
--as string
작업을 수행할 사용자 이름
--as-group stringArray
작업을 수행할 그룹. 이 플래그를 반복해서 여러 그룹을 지정할 수 있다.
--azure-container-registry-config string
Azure 컨테이너 레지스트리 구성 정보가 포함된 파일의 경로이다.
--cache-dir string     기본값: "$HOME/.kube/cache"
기본 캐시 디렉터리
--certificate-authority string
인증 기관의 인증서에 대한 파일 경로
--client-certificate string
TLS용 클라이언트 인증서의 파일 경로
--client-key string
TLS용 클라이언트 키의 파일 경로
--cloud-provider-gce-l7lb-src-cidrs cidrs     기본값: 130.211.0.0/22,35.191.0.0/16
L7 LB 트래픽 프록시 및 상태 확인을 위해 GCE 방화벽에서 오픈된 CIDR
--cloud-provider-gce-lb-src-cidrs cidrs     기본값: 130.211.0.0/22,209.85.152.0/22,209.85.204.0/22,35.191.0.0/16
L4 LB 트래픽 프록시 및 상태 확인을 위해 GCE 방화벽에서 오픈된 CIDR
--cluster string
사용할 kubeconfig 클러스터의 이름
--context string
사용할 kubeconfig 콘텍스트의 이름
--default-not-ready-toleration-seconds int     기본값: 300
아직 톨러레이션(toleration)이 없는 모든 파드에 기본적으로 추가되는 notReady:NoExecute에 대한 톨러레이션의 tolerationSeconds를 나타낸다.
--default-unreachable-toleration-seconds int     기본값: 300
아직 톨러레이션이 없어서 기본인 unreachable:NoExecute가 추가된 모든 파드에 대한 톨러레이션의 tolerationSeconds를 나타낸다.
-h, --help
kubectl에 대한 도움말
--insecure-skip-tls-verify
true인 경우, 서버 인증서의 유효성을 확인하지 않는다. 이렇게 하면 사용자의 HTTPS 연결이 안전하지 않게 된다.
--kubeconfig string
CLI 요청에 사용할 kubeconfig 파일의 경로이다.
--log-backtrace-at traceLocation     기본값: :0
로깅이 file:N에 도달했을 때 스택 트레이스를 내보낸다.
--log-dir string
비어 있지 않으면, 이 디렉터리에 로그 파일을 작성한다.
--log-file string
비어 있지 않으면, 이 로그 파일을 사용한다.
--log-file-max-size uint     기본값: 1800
로그 파일이 커질 수 있는 최대 크기를 정의한다. 단위는 메가 바이트이다. 값이 0이면, 파일의 최대 크기는 무제한이다.
--log-flush-frequency duration     기본값: 5s
로그를 비우는 간격의 최대 시간(초)
--logtostderr     기본값: true
파일 대신 표준 에러에 기록
--match-server-version
클라이언트 버전과 일치하는 서버 버전 필요
-n, --namespace string
지정된 경우, 해당 네임스페이스가 CLI 요청의 범위가 됨
--one-output
true이면, 로그를 기본 심각도 수준으로만 기록한다(그렇지 않으면 각각의 더 낮은 심각도 수준에도 기록함).
--password string
API 서버에 대한 기본 인증을 위한 비밀번호
--profile string     기본값: "none"
캡처할 프로파일의 이름. (none|cpu|heap|goroutine|threadcreate|block|mutex) 중 하나
--profile-output string     기본값: "profile.pprof"
프로파일을 쓸 파일의 이름
--request-timeout string     기본값: "0"
단일 서버 요청을 포기하기 전에 대기하는 시간이다. 0이 아닌 값에는 해당 시간 단위(예: 1s, 2m, 3h)가 포함되어야 한다. 값이 0이면 요청 시간이 초과되지 않는다.
-s, --server string
쿠버네티스 API 서버의 주소와 포트
--skip-headers
true이면, 로그 메시지에서 헤더 접두사를 사용하지 않는다.
--skip-log-headers
true이면, 로그 파일을 열 때 헤더를 사용하지 않는다.
--stderrthreshold severity     기본값: 2
이 임계값 이상의 로그는 표준 에러로 이동한다.
--tls-server-name string
서버 인증서 유효성 검사에 사용할 서버 이름. 제공되지 않으면, 서버에 접속하는 데 사용되는 호스트 이름이 사용된다.
--token string
API 서버 인증을 위한 베어러(Bearer) 토큰
--user string
사용할 kubeconfig 사용자의 이름
--username string
API 서버에 대한 기본 인증을 위한 사용자 이름
-v, --v Level
로그 수준의 자세한 정도를 나타내는 숫자
--version version[=true]
버전 정보를 출력하고 종료
--vmodule moduleSpec
파일 필터링 로깅을 위한 쉼표로 구분된 pattern=N 설정 목록
--warnings-as-errors
서버에서 받은 경고를 오류로 처리하고 0이 아닌 종료 코드로 종료

Environment variables

KUBECONFIG
kubectl 구성 ("kubeconfig") 파일 경로. 기본: "$HOME/.kube/config"
KUBECTL_COMMAND_HEADERS
false로 설정하면, 호출된 kubectl 명령(쿠버네티스 버전 v1.22 이상)을 자세히 설명하는 추가 HTTP 헤더를 해제

더 보기

  • kubectl annotate - 리소스에 대한 어노테이션 업데이트
  • kubectl api-resources - 서버에서 지원되는 API 리소스 출력
  • kubectl api-versions - "그룹/버전" 형식으로 서버에서 지원되는 API 버전을 출력
  • kubectl apply - 파일명 또는 표준 입력으로 리소스에 구성 적용
  • kubectl attach - 실행 중인 컨테이너에 연결
  • kubectl auth - 권한 검사
  • kubectl autoscale - 디플로이먼트(Deployment), 레플리카셋(ReplicaSet) 또는 레플리케이션컨트롤러(ReplicationController) 자동 스케일링
  • kubectl certificate - 인증서 리소스 수정
  • kubectl cluster-info - 클러스터 정보 표시
  • kubectl completion - 지정된 셸(bash 또는 zsh)에 대한 셸 완성 코드 출력
  • kubectl config - kubeconfig 파일 수정
  • kubectl cordon - 노드를 unschedulable로 표시
  • kubectl cp - 컨테이너 간에 파일과 디렉터리 복사
  • kubectl create - 파일 또는 표준 입력에서 리소스를 생성
  • kubectl debug - 워크로드와 노드의 문제 해결을 위한 디버깅 세션 생성
  • kubectl delete - 파일명, 표준 입력, 리소스 및 이름, 또는 리소스 및 레이블 셀렉터로 리소스 삭제
  • kubectl describe - 특정 리소스 또는 리소스 그룹의 세부 정보를 표시
  • kubectl diff - 적용 예정 버전과 라이브 버전 비교
  • kubectl drain - 유지 보수 준비 중 노드 드레인
  • kubectl edit - 서버에서 리소스 편집
  • kubectl exec - 컨테이너에서 커맨드 실행
  • kubectl explain - 리소스의 문서
  • kubectl expose - 레플리케이션 컨트롤러, 서비스, 디플로이먼트 또는 파드를 가져와서 새로운 쿠버네티스 서비스로 노출
  • kubectl get - 하나 이상의 리소스 표시
  • kubectl kustomize - 디렉터리 또는 원격 URL에서 kustomization 대상을 빌드
  • kubectl label - 리소스의 레이블 업데이트
  • kubectl logs - 파드의 컨테이너에 대한 로그 출력
  • kubectl options - 모든 커맨드에서 상속된 플래그 목록을 출력
  • kubectl patch - 리소스 필드를 업데이트
  • kubectl plugin - 플러그인과 상호 작용하기 위한 유틸리티를 제공
  • kubectl port-forward - 하나 이상의 로컬 포트를 파드로 전달
  • kubectl proxy - 쿠버네티스 API 서버에 대한 프록시 실행
  • kubectl replace - 파일명 또는 표준 입력으로 리소스 교체
  • kubectl rollout - 리소스 롤아웃 관리
  • kubectl run - 클러스터에서 특정 이미지 실행
  • kubectl scale - 디플로이먼트, 레플리카셋 또는 레플리케이션 컨트롤러의 새 크기 설정
  • kubectl set - 오브젝트에 특정 기능 설정
  • kubectl taint - 하나 이상의 노드에서 테인트(taint) 업데이트
  • kubectl top - 리소스(CPU/메모리/스토리지) 사용량을 표시
  • kubectl uncordon - 노드를 schedulable로 표시
  • kubectl version - 클라이언트 및 서버 버전 정보 출력
  • kubectl wait - 실험적(experimental) 기능: 하나 이상의 리소스에 대해서 특정 조건이 만족될 때까지 대기(wait)

6.8.4 - kubectl 사용 규칙

kubectl에 대한 권장 사용 규칙.

재사용 가능한 스크립트에서 kubectl 사용

스크립트의 안정적인 출력을 위해서

  • -o name, -o json, -o yaml, -o go-template 혹은 -o jsonpath와 같은 머신 지향(machine-oriented) 출력 양식 중 하나를 요청한다.
  • 예를 들어 jobs.v1.batch/myjob과 같이 전체 버전을 사용한다. 이를 통해 kubectl이 시간이 지남에 따라 변경될 수 있는 기본 버전을 사용하지 않도록 한다.
  • 문맥, 설정 또는 기타 암묵적 상태에 의존하지 않는다.

모범 사례

kubectl run

kubectl run으로 infrastructure as code를 충족시키기 위해서

  • 버전이 명시된 태그로 이미지를 태그하고 그 태그를 새로운 버전으로 이동하지 않는다. 예를 들어, :latest가 아닌 :v1234, v1.2.3, r03062016-1-4를 사용한다(자세한 정보는 구성 모범 사례를 참고한다).
  • 많은 파라미터가 적용된 이미지를 위한 스크립트를 작성한다.
  • 필요하지만 kubectl run 플래그를 통해 표현할 수 없는 기능은 구성 파일을 소스 코드 버전 관리 시스템에 넣어서 전환한다.

--dry-run 플래그를 사용하여 실제로 제출하지 않고 클러스터로 보낼 오브젝트를 미리 볼 수 있다.

생성기

kubectl create --dry-run -o yaml라는 kubectl 커맨드를 통해 다음과 같은 리소스를 생성할 수 있다.

  • clusterrole: 클러스터롤(ClusterRole)를 생성한다.
  • clusterrolebinding: 특정 클러스터롤에 대한 클러스터롤바인딩(ClusterRoleBinding)을 생성한다.
  • configmap: 로컬 파일, 디렉토리 또는 문자 그대로의 값으로 컨피그맵(ConfigMap)을 생성한다.
  • cronjob: 지정된 이름으로 크론잡(CronJob)을 생성한다.
  • deployment: 지정된 이름으로 디플로이먼트(Deployment)를 생성한다.
  • job: 지정된 이름으로 잡(Job)을 생성한다.
  • namespace: 지정된 이름으로 네임스페이스(Namespace)를 생성한다.
  • poddisruptionbudget: 지정된 이름으로 PodDisruptionBudget을 생성한다.
  • priorityclass: 지정된 이름으로 프라이어리티클래스(PriorityClass)을 생성한다.
  • quota: 지정된 이름으로 쿼터(Quota)를 생성한다.
  • role: 단일 규칙으로 롤(Role)을 생성한다.
  • rolebinding: 특정 롤 또는 클러스터롤에 대한 롤바인딩(RoleBinding)을 생성한다.
  • secret: 지정된 하위 커맨드를 사용하여 시크릿(Secret)을 생성한다.
  • service: 지정된 하위 커맨드를 사용하여 서비스(Service)를 생성한다.
  • serviceaccount: 지정된 이름으로 서비스어카운트(ServiceAccount)을 생성한다.

kubectl apply

  • kubectl apply를 사용해서 리소스를 생성하거나 업데이트 할 수 있다. kubectl apply를 사용하여 리소스를 업데이트하는 방법에 대한 자세한 정보는 Kubectl 책을 참고한다.

6.8.5 - kubectl 치트 시트

이 페이지는 일반적으로 사용하는 kubectl 커맨드와 플래그에 대한 목록을 포함한다.

Kubectl 자동 완성

BASH

source <(kubectl completion bash) # bash-completion 패키지를 먼저 설치한 후, bash의 자동 완성을 현재 셸에 설정한다
echo "source <(kubectl completion bash)" >> ~/.bashrc # 자동 완성을 bash 셸에 영구적으로 추가한다

또한, kubectl의 의미로 사용되는 약칭을 사용할 수 있다.

alias k=kubectl
complete -F __start_kubectl k

ZSH

source <(kubectl completion zsh)  # 현재 셸에 zsh의 자동 완성 설정
echo "[[ $commands[kubectl] ]] && source <(kubectl completion zsh)" >> ~/.zshrc # 자동 완성을 zsh 셸에 영구적으로 추가한다.

Kubectl 컨텍스트와 설정

kubectl이 통신하고 설정 정보를 수정하는 쿠버네티스 클러스터를 지정한다. 설정 파일에 대한 자세한 정보는 kubeconfig를 이용한 클러스터 간 인증 문서를 참고한다.

kubectl config view # 병합된 kubeconfig 설정을 표시한다.

# 동시에 여러 kubeconfig 파일을 사용하고 병합된 구성을 확인한다
KUBECONFIG=~/.kube/config:~/.kube/kubconfig2

kubectl config view

# e2e 사용자의 암호를 확인한다
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'

kubectl config view -o jsonpath='{.users[].name}'    # 첫 번째 사용자 출력
kubectl config view -o jsonpath='{.users[*].name}'    # 사용자 리스트 조회
kubectl config get-contexts                          # 컨텍스트 리스트 출력
kubectl config current-context              # 현재 컨텍스트 출력
kubectl config use-context my-cluster-name  # my-cluster-name를 기본 컨텍스트로 설정

# 기본 인증을 지원하는 새로운 사용자를 kubeconf에 추가한다
kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword

# 해당 컨텍스트에서 모든 후속 kubectl 커맨드에 대한 네임스페이스를 영구적으로 저장한다
kubectl config set-context --current --namespace=ggckad-s2

# 특정 사용자와 네임스페이스를 사용하는 컨텍스트 설정
kubectl config set-context gce --user=cluster-admin --namespace=foo \
  && kubectl config use-context gce

kubectl config unset users.foo                       # foo 사용자 삭제

Kubectl apply

apply는 쿠버네티스 리소스를 정의하는 파일을 통해 애플리케이션을 관리한다. kubectl apply를 실행하여 클러스터에 리소스를 생성하고 업데이트한다. 이것은 프로덕션 환경에서 쿠버네티스 애플리케이션을 관리할 때 권장된다. Kubectl Book을 참고한다.

오브젝트 생성

쿠버네티스 매니페스트는 JSON이나 YAML로 정의된다. 파일 확장자는 .yaml , .yml, .json 이 사용된다.

kubectl apply -f ./my-manifest.yaml            # 리소스(들) 생성
kubectl apply -f ./my1.yaml -f ./my2.yaml      # 여러 파일로 부터 생성
kubectl apply -f ./dir                         # dir 내 모든 매니페스트 파일에서 리소스(들) 생성
kubectl apply -f https://git.io/vPieo          # url로부터 리소스(들) 생성
kubectl create deployment nginx --image=nginx  # nginx 단일 인스턴스를 시작

# "Hello World"를 출력하는 잡(Job) 생성
kubectl create job hello --image=busybox -- echo "Hello World"

# 매분마다 "Hello World"를 출력하는 크론잡(CronJob) 생성
kubectl create cronjob hello --image=busybox   --schedule="*/1 * * * *" -- echo "Hello World"    

kubectl explain pods                           # 파드 매니페스트 문서를 조회

# stdin으로 다수의 YAML 오브젝트 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000"
EOF

# 여러 개의 키로 시크릿 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo -n "s33msi4" | base64 -w0)
  username: $(echo -n "jane" | base64 -w0)
EOF

리소스 조회 및 찾기

# 기본 출력을 위한 Get 커맨드
kubectl get services                          # 네임스페이스 내 모든 서비스의 목록 조회
kubectl get pods --all-namespaces             # 모든 네임스페이스 내 모든 파드의 목록 조회
kubectl get pods -o wide                      # 해당하는 네임스페이스 내 모든 파드의 상세 목록 조회
kubectl get deployment my-dep                 # 특정 디플로이먼트의 목록 조회
kubectl get pods                              # 네임스페이스 내 모든 파드의 목록 조회
kubectl get pod my-pod -o yaml                # 파드의 YAML 조회

# 상세 출력을 위한 Describe 커맨드
kubectl describe nodes my-node
kubectl describe pods my-pod

# Name으로 정렬된 서비스의 목록 조회
kubectl get services --sort-by=.metadata.name

# 재시작 횟수로 정렬된 파드의 목록 조회
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# PersistentVolumes을 용량별로 정렬해서 조회
kubectl get pv --sort-by=.spec.capacity.storage

# app=cassandra 레이블을 가진 모든 파드의 레이블 버전 조회
kubectl get pods --selector=app=cassandra -o \
  jsonpath='{.items[*].metadata.labels.version}'

# 예를 들어 'ca.crt'와 같이 점이 있는 키값을 검색한다
kubectl get configmap myconfig \
  -o jsonpath='{.data.ca\.crt}'

# 모든 워커 노드 조회 (셀렉터를 사용하여 'node-role.kubernetes.io/master'
# 으로 명명된 라벨의 결과를 제외)
kubectl get node --selector='!node-role.kubernetes.io/master'

# 네임스페이스의 모든 실행 중인 파드를 조회
kubectl get pods --field-selector=status.phase=Running

# 모든 노드의 외부IP를 조회
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 특정 RC에 속해있는 파드 이름의 목록 조회
# "jq" 커맨드는 jsonpath를 사용하는 매우 복잡한 변환에 유용하다. https://stedolan.github.io/jq/ 에서 확인할 수 있다.
sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 모든 파드(또는 레이블을 지원하는 다른 쿠버네티스 오브젝트)의 레이블 조회
kubectl get pods --show-labels

# 어떤 노드가 준비됐는지 확인
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# 외부 도구 없이 디코딩된 시크릿 출력
kubectl get secret my-secret -o go-template='{{range $k,$v := .data}}{{"### "}}{{$k}}{{"\n"}}{{$v|base64decode}}{{"\n\n"}}{{end}}'

# 파드에 의해 현재 사용되고 있는 모든 시크릿 목록 조회
kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

# 모든 파드의 초기화 컨테이너(initContainer)의 컨테이너ID 목록 조회
# 초기화 컨테이너(initContainer)를 제거하지 않고 정지된 모든 컨테이너를 정리할 때 유용하다.
kubectl get pods --all-namespaces -o jsonpath='{range .items[*].status.initContainerStatuses[*]}{.containerID}{"\n"}{end}' | cut -d/ -f3

# 타임스탬프로 정렬된 이벤트 목록 조회
kubectl get events --sort-by=.metadata.creationTimestamp

# 매니페스트가 적용된 경우 클러스터의 현재 상태와 클러스터의 상태를 비교한다.
kubectl diff -f ./my-manifest.yaml

# 노드에 대해 반환된 모든 키의 마침표로 구분된 트리를 생성한다.
# 복잡한 중첩 JSON 구조 내에서 키를 찾을 때 유용하다.
kubectl get nodes -o json | jq -c 'path(..)|[.[]|tostring]|join(".")'

# 파드 등에 대해 반환된 모든 키의 마침표로 구분된 트리를 생성한다.
kubectl get pods -o json | jq -c 'path(..)|[.[]|tostring]|join(".")'

# 모든 파드에 대해 ENV를 생성한다(각 파드에 기본 컨테이너가 있고, 기본 네임스페이스가 있고, `env` 명령어가 동작한다고 가정).
# `env` 뿐만 아니라 다른 지원되는 명령어를 모든 파드에 실행할 때에도 참고할 수 있다.
for pod in $(kubectl get po --output=jsonpath={.items..metadata.name}); do echo $pod && kubectl exec -it $pod env; done

리소스 업데이트

kubectl set image deployment/frontend www=image:v2               # "frontend" 디플로이먼트의 "www" 컨테이너 이미지를 업데이트하는 롤링 업데이트
kubectl rollout history deployment/frontend                      # 현 리비전을 포함한 디플로이먼트의 이력을 체크
kubectl rollout undo deployment/frontend                         # 이전 디플로이먼트로 롤백
kubectl rollout undo deployment/frontend --to-revision=2         # 특정 리비전으로 롤백
kubectl rollout status -w deployment/frontend                    # 완료될 때까지 "frontend" 디플로이먼트의 롤링 업데이트 상태를 감시
kubectl rollout restart deployment/frontend                      # "frontend" 디플로이먼트의 롤링 재시작


cat pod.json | kubectl replace -f -                              # std로 전달된 JSON을 기반으로 파드 교체

# 리소스를 강제 교체, 삭제 후 재생성함. 이것은 서비스를 중단시킴.
kubectl replace --force -f ./pod.json

# 복제된 nginx를 위한 서비스를 생성한다. 80 포트로 서비스하고, 컨테이너는 8000 포트로 연결한다.
kubectl expose rc nginx --port=80 --target-port=8000

# 단일-컨테이너 파드의 이미지 버전(태그)을 v4로 업데이트
kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

kubectl label pods my-pod new-label=awesome                      # 레이블 추가
kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # 어노테이션 추가
kubectl autoscale deployment foo --min=2 --max=10                # 디플로이먼트 "foo" 오토스케일

리소스 패치

kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 노드를 부분적으로 업데이트

# 컨테이너의 이미지를 업데이트. 병합(merge) 키이므로, spec.containers[*].name이 필요.
kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# 위치 배열을 이용한 json 패치를 사용하여, 컨테이너의 이미지를 업데이트.
kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# 위치 배열을 이용한 json 패치를 사용하여 livenessProbe 디플로이먼트 비활성화.
kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

# 위치 배열에 새 요소 추가
kubectl patch sa default --type='json' -p='[{"op": "add", "path": "/secrets/1", "value": {"name": "whatever" } }]'

리소스 편집

편집기로 모든 API 리소스를 편집.

kubectl edit svc/docker-registry                      # docker-registry라는 서비스 편집
KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # 다른 편집기 사용

리소스 스케일링

kubectl scale --replicas=3 rs/foo                                 # 'foo'라는 레플리카셋을 3으로 스케일
kubectl scale --replicas=3 -f foo.yaml                            # "foo.yaml"에 지정된 리소스의 크기를 3으로 스케일
kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # mysql이라는 디플로이먼트의 현재 크기가 2인 경우, mysql을 3으로 스케일
kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # 여러 개의 레플리케이션 컨트롤러 스케일

리소스 삭제

kubectl delete -f ./pod.json                                              # pod.json에 지정된 유형 및 이름을 사용하여 파드 삭제
kubectl delete pod,service baz foo                                        # "baz", "foo"와 동일한 이름을 가진 파드와 서비스 삭제
kubectl delete pods,services -l name=myLabel                              # name=myLabel 라벨을 가진 파드와 서비스 삭제
kubectl delete pods,services -l name=myLabel --include-uninitialized      # 초기화되지 않은 것을 포함하여, name=myLabel 라벨을 가진 파드와 서비스 삭제
kubectl -n my-ns delete pod,svc --all                                      # 초기화되지 않은 것을 포함하여, my-ns 네임스페이스 내 모든 파드와 서비스 삭제
# awk pattern1 또는 pattern2에 매칭되는 모든 파드 삭제
kubectl get pods  -n mynamespace --no-headers=true | awk '/pattern1|pattern2/{print $1}' | xargs  kubectl delete -n mynamespace pod

실행 중인 파드와 상호 작용

kubectl logs my-pod                                 # 파드 로그 덤프 (stdout)
kubectl logs -l name=myLabel                        # name이 myLabel인 파드 로그 덤프 (stdout)
kubectl logs my-pod --previous                      # 컨테이너의 이전 인스턴스 생성에 대한 파드 로그 덤프 (stdout)
kubectl logs my-pod -c my-container                 # 파드 로그 덤프 (stdout, 멀티-컨테이너 경우)
kubectl logs -l name=myLabel -c my-container        # name이 myLabel인 파드 로그 덤프 (stdout)
kubectl logs my-pod -c my-container --previous      # 컨테이너의 이전 인스턴스 생성에 대한 파드 로그 덤프 (stdout, 멀티-컨테이너 경우)
kubectl logs -f my-pod                              # 실시간 스트림 파드 로그(stdout)
kubectl logs -f my-pod -c my-container              # 실시간 스트림 파드 로그(stdout, 멀티-컨테이너 경우)
kubectl logs -f -l name=myLabel --all-containers    # name이 myLabel인 모든 파드의 로그 스트리밍 (stdout)
kubectl run -i --tty busybox --image=busybox -- sh  # 대화형 셸로 파드를 실행
kubectl run nginx --image=nginx -n
mynamespace                                         # 특정 네임스페이스에서 nginx 파드 실행
kubectl run nginx --image=nginx                     # nginx 파드를 실행하고 해당 스펙을 pod.yaml 파일에 기록
--dry-run=client -o yaml > pod.yaml

kubectl attach my-pod -i                            # 실행 중인 컨테이너에 연결
kubectl port-forward my-pod 5000:6000               # 로컬 머신의 5000번 포트를 리스닝하고, my-pod의 6000번 포트로 전달
kubectl exec my-pod -- ls /                         # 기존 파드에서 명령 실행(한 개 컨테이너 경우)
kubectl exec --stdin --tty my-pod -- /bin/sh        # 실행 중인 파드로 대화형 셸 액세스(1 컨테이너 경우)
kubectl exec my-pod -c my-container -- ls /         # 기존 파드에서 명령 실행(멀티-컨테이너 경우)
kubectl top pod POD_NAME --containers               # 특정 파드와 해당 컨테이너에 대한 메트릭 표시
kubectl top pod POD_NAME --sort-by=cpu              # 지정한 파드에 대한 메트릭을 표시하고 'cpu' 또는 'memory'별로 정렬

디플로이먼트, 서비스와 상호 작용

kubectl logs deploy/my-deployment                         # 디플로이먼트에 대한 파드 로그 덤프 (단일-컨테이너 경우)
kubectl logs deploy/my-deployment -c my-container         # 디플로이먼트에 대한 파드 로그 덤프 (멀티-컨테이너 경우)

kubectl port-forward svc/my-service 5000                  # 로컬 머신의 5000번 포트를 리스닝하고, my-service의 동일한(5000번) 포트로 전달
kubectl port-forward svc/my-service 5000:my-service-port  # 로컬 머신의 5000번 포트를 리스닝하고, my-service의 <my-service-port> 라는 이름을 가진 포트로 전달

kubectl port-forward deploy/my-deployment 5000:6000       # 로컬 머신의 5000번 포트를 리스닝하고, <my-deployment> 에 의해 생성된 파드의 6000번 포트로 전달
kubectl exec deploy/my-deployment -- ls                   # <my-deployment> 에 의해 생성된 첫번째 파드의 첫번째 컨테이너에 명령어 실행 (단일- 또는 다중-컨테이너 경우)

노드, 클러스터와 상호 작용

kubectl cordon my-node                                                # my-node를 스케줄링할 수 없도록 표기
kubectl drain my-node                                                 # 유지 보수를 위해서 my-node를 준비 상태로 비움
kubectl uncordon my-node                                              # my-node를 스케줄링할 수 있도록 표기
kubectl top node my-node                                              # 주어진 노드에 대한 메트릭 표시
kubectl cluster-info                                                  # 마스터 및 서비스의 주소 표시
kubectl cluster-info dump                                             # 현재 클러스터 상태를 stdout으로 덤프
kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 현재 클러스터 상태를 /path/to/cluster-state으로 덤프

# key와 effect가 있는 테인트(taint)가 이미 존재하면, 그 값이 지정된 대로 대체된다.
kubectl taint nodes foo dedicated=special-user:NoSchedule

리소스 타입

단축명, API 그룹과 함께 지원되는 모든 리소스 유형들, 그것들의 네임스페이스종류(Kind)를 나열:

kubectl api-resources

API 리소스를 탐색하기 위한 다른 작업:

kubectl api-resources --namespaced=true      # 네임스페이스를 가지는 모든 리소스
kubectl api-resources --namespaced=false     # 네임스페이스를 가지지 않는 모든 리소스
kubectl api-resources -o name                # 모든 리소스의 단순한 (리소스 이름만) 출력
kubectl api-resources -o wide                # 모든 리소스의 확장된 ("wide"로 알려진) 출력
kubectl api-resources --verbs=list,get       # "list"와 "get"의 요청 동사를 지원하는 모든 리소스 출력
kubectl api-resources --api-group=extensions # "extensions" API 그룹의 모든 리소스

출력 형식 지정

특정 형식으로 터미널 창에 세부 사항을 출력하려면, 지원되는 kubectl 명령에 -o (또는 --output) 플래그를 추가한다.

출력 형식 세부 사항
-o=custom-columns=<명세> 쉼표로 구분된 사용자 정의 열 목록을 사용하여 테이블 출력
-o=custom-columns-file=<파일명> <파일명>파일에서 사용자 정의 열 템플릿을 사용하여 테이블 출력
-o=json JSON 형식의 API 오브젝트 출력
-o=jsonpath=<템플릿> jsonpath 표현식에 정의된 필드 출력
-o=jsonpath-file=<파일명> <파일명> 파일에서 jsonpath 표현식에 정의된 필드 출력
-o=name 리소스 명만 출력하고 그 외에는 출력하지 않음
-o=wide 추가 정보가 포함된 일반-텍스트 형식으로 출력하고, 파드의 경우 노드 명이 포함
-o=yaml YAML 형식의 API 오브젝트 출력

-o=custom-columns 의 사용 예시:

# 클러스터에서 실행 중인 모든 이미지
kubectl get pods -A -o=custom-columns='DATA:spec.containers[*].image'

# `default` 네임스페이스의 모든 이미지를 파드별로 그룹지어 출력
kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image"

 # "k8s.gcr.io/coredns:1.6.2" 를 제외한 모든 이미지
kubectl get pods -A -o=custom-columns='DATA:spec.containers[?(@.image!="k8s.gcr.io/coredns:1.6.2")].image'

# 이름에 관계없이 메타데이터 아래의 모든 필드
kubectl get pods -A -o=custom-columns='DATA:metadata.*'

더 많은 예제는 kubectl 참조 문서를 참고한다.

Kubectl 출력 로그 상세 레벨(verbosity)과 디버깅

Kubectl 로그 상세 레벨(verbosity)은 -v 또는--v 플래그와 로그 레벨을 나타내는 정수로 제어된다. 일반적인 쿠버네티스 로깅 규칙과 관련 로그 레벨이 여기에 설명되어 있다.

로그 레벨 세부 사항
--v=0 일반적으로 클러스터 운영자(operator)에게 항상 보여지게 하기에는 유용함.
--v=1 자세한 정보를 원하지 않는 경우, 적절한 기본 로그 수준.
--v=2 서비스와 시스템의 중요한 변화와 관련이있는 중요한 로그 메시지에 대한 유용한 정상 상태 정보. 이는 대부분의 시스템에서 권장되는 기본 로그 수준이다.
--v=3 변경 사항에 대한 확장 정보.
--v=4 디버그 수준 상세화.
--v=5 트레이스 수준 상세화.
--v=6 요청한 리소스를 표시.
--v=7 HTTP 요청 헤더를 표시.
--v=8 HTTP 요청 내용을 표시.
--v=9 내용을 잘라 내지 않고 HTTP 요청 내용을 표시.

다음 내용

6.8.6 - 도커 사용자를 위한 kubectl

당신은 쿠버네티스 커맨드 라인 도구인 kubectl을 사용하여 API 서버와 상호 작용할 수 있다. 만약 도커 커맨드 라인 도구에 익숙하다면 kubectl을 사용하는 것은 간단하다. 다음 섹션에서는 도커의 하위 명령을 보여주고 kubectl과 같은 명령어를 설명한다.

docker run

nginx 디플로이먼트(Deployment)를 실행하고 해당 디플로이먼트를 노출시키려면, kubectl create deployment을 참고한다. docker:

docker run -d --restart=always -e DOMAIN=cluster --name nginx-app -p 80:80 nginx
55c103fa129692154a7652490236fee9be47d70a8dd562281ae7d2f9a339a6db
docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   9 seconds ago       Up 9 seconds        0.0.0.0:80->80/tcp   nginx-app

kubectl:

# nginx 실행하는 파드를 시작한다
kubectl create deployment --image=nginx nginx-app
deployment.apps/nginx-app created
# nginx-app 에 env를 추가한다
kubectl set env deployment/nginx-app  DOMAIN=cluster
deployment.apps/nginx-app env updated
# 서비스를 통해 포트를 노출
kubectl expose deployment nginx-app --port=80 --name=nginx-http
service "nginx-http" exposed

kubectl을 사용하면, N개의 파드가 nginx를 실행하도록 디플로이먼트를 생성할 수 있다. 여기서 N은 스펙에 명시된 레플리카 수이며, 기본값은 1이다. 또한 파드의 레이블과 셀럭터를 사용하여 서비스를 생성할 수 있다. 자세한 내용은 클러스터 내 애플리케이션에 접근하기 위해 서비스 사용하기를 참고한다.

기본적으로 이미지는 docker run -d ... 와 비슷하게 백그라운드로 실행된다. 포그라운드로 실행하려면 kubectl run을 이용하여 파드를 생성한다.

kubectl run [-i] [--tty] --attach <name> --image=<image>

docker run ... 과 달리 --attach 를 지정하면 표준 입력(stdin), 표준 출력(stdout)표준 오류(stderr)가 붙는다. 연결된(attached) 스트림을 제어할 수 없다(docker -a ...). 해당 컨테이너에서 분리(detach)하려면 이스케이프 시퀀스(escape sequence) Ctrl+P를 입력한 다음 Ctrl+Q를 입력한다.

docker ps

현재 실행 중인 목록을 보기 위해서는 kubectl get을 참고한다.

docker:

docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                     PORTS                NAMES
14636241935f        ubuntu:16.04        "echo test"              5 seconds ago        Exited (0) 5 seconds ago                        cocky_fermi
55c103fa1296        nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute          0.0.0.0:80->80/tcp   nginx-app

kubectl:

kubectl get po
NAME                        READY     STATUS      RESTARTS   AGE
nginx-app-8df569cb7-4gd89   1/1       Running     0          3m
ubuntu                      0/1       Completed   0          20s

docker attach

이미 실행 중인 컨테이너에 연결하려면 kubectl attach를 참고한다.

docker:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        0.0.0.0:80->80/tcp   nginx-app
docker attach 55c103fa1296
...

kubectl:

kubectl get pods
NAME              READY     STATUS    RESTARTS   AGE
nginx-app-5jyvm   1/1       Running   0          10m
kubectl attach -it nginx-app-5jyvm
...

컨테이너에서 분리하려면 이스케이프 시퀀스 Ctrl+P를 입력한 다음 Ctrl+Q를 입력한다.

docker exec

컨테이너에서 커맨드를 실행하려면 kubectl exec를 참고한다.

docker:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
55c103fa1296        nginx               "nginx -g 'daemon of…"   6 minutes ago       Up 6 minutes        0.0.0.0:80->80/tcp   nginx-app
docker exec 55c103fa1296 cat /etc/hostname
55c103fa1296

kubectl:

kubectl get po
NAME              READY     STATUS    RESTARTS   AGE
nginx-app-5jyvm   1/1       Running   0          10m
kubectl exec nginx-app-5jyvm -- cat /etc/hostname
nginx-app-5jyvm

대화형 커맨드를 사용한다.

docker:

docker exec -ti 55c103fa1296 /bin/sh
# exit

kubectl:

kubectl exec -ti nginx-app-5jyvm -- /bin/sh
# exit

자세한 내용은 실행 중인 컨테이너의 셸 얻기를 참고한다.

docker logs

실행 중인 프로세스의 표준 입력(stdout)/표준 오류(stderr)를 수행하려면 kubectl logs를 참고한다.

docker:

docker logs -f a9e
192.168.9.1 - - [14/Jul/2015:01:04:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.35.0" "-"
192.168.9.1 - - [14/Jul/2015:01:04:03 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.35.0" "-"

kubectl:

kubectl logs -f nginx-app-zibvs
10.240.63.110 - - [14/Jul/2015:01:09:01 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"
10.240.63.110 - - [14/Jul/2015:01:09:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"

파드와 컨테이너에는 근소한 차이가 있다. 기본적으로 파드는 프로세스가 종료되어도 종료되지 않는다. 대신 파드가 프로세스를 다시 시작한다. 이는 도커의 실행 옵션인 --restart=always와 유사하지만, 한 가지 큰 차이점이 있다. 도커에서는 프로세스의 각 호출에 대한 출력이 연결되지만, 쿠버네티스의 경우 각 호출은 별개다. 쿠버네티스에서 이전 실행의 출력 내용을 보려면 다음을 수행한다.

kubectl logs --previous nginx-app-zibvs
10.240.63.110 - - [14/Jul/2015:01:09:01 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"
10.240.63.110 - - [14/Jul/2015:01:09:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.26.0" "-"

자세한 정보는 로깅 아키텍처를 참고한다.

docker stop 과 docker rm

실행 중인 프로세스를 중지하고 삭제하려면 kubectl delete을 참고한다.

docker:

docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                         NAMES
a9ec34d98787        nginx               "nginx -g 'daemon of"  22 hours ago        Up 22 hours         0.0.0.0:80->80/tcp, 443/tcp   nginx-app
docker stop a9ec34d98787
a9ec34d98787
docker rm a9ec34d98787
a9ec34d98787

kubectl:

kubectl get deployment nginx-app
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app    1/1     1            1           2m
kubectl get po -l app=nginx-app
NAME                         READY     STATUS    RESTARTS   AGE
nginx-app-2883164633-aklf7   1/1       Running   0          2m
kubectl delete deployment nginx-app
deployment "nginx-app" deleted
kubectl get po -l app=nginx-app
# 아무것도 반환하지 않는다

docker login

kubectl은 docker login와 직접적인 유사점은 없다. 프라이빗 레지스트리와 함께 쿠버네티스를 사용하려면 프라이빗 레지스트리 사용을 참고한다.

docker version

클라이언트와 서버의 버전을 가져오려면 kubectl version을 참고한다.

docker:

docker version
Client version: 1.7.0
Client API version: 1.19
Go version (client): go1.4.2
Git commit (client): 0baf609
OS/Arch (client): linux/amd64
Server version: 1.7.0
Server API version: 1.19
Go version (server): go1.4.2
Git commit (server): 0baf609
OS/Arch (server): linux/amd64

kubectl:

kubectl version
Client Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.9+a3d1dfa6f4335", GitCommit:"9b77fed11a9843ce3780f70dd251e92901c43072", GitTreeState:"dirty", BuildDate:"2017-08-29T20:32:58Z", OpenPaasKubernetesVersion:"v1.03.02", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.9+a3d1dfa6f4335", GitCommit:"9b77fed11a9843ce3780f70dd251e92901c43072", GitTreeState:"dirty", BuildDate:"2017-08-29T20:32:58Z", OpenPaasKubernetesVersion:"v1.03.02", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}

docker info

환경 및 설정에 대한 자세한 정보는 kubectl cluster-info를 참고한다.

docker:

docker info
Containers: 40
Images: 168
Storage Driver: aufs
 Root Dir: /usr/local/google/docker/aufs
 Backing Filesystem: extfs
 Dirs: 248
 Dirperm1 Supported: false
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.13.0-53-generic
Operating System: Ubuntu 14.04.2 LTS
CPUs: 12
Total Memory: 31.32 GiB
Name: k8s-is-fun.mtv.corp.google.com
ID: ADUV:GCYR:B3VJ:HMPO:LNPQ:KD5S:YKFQ:76VN:IANZ:7TFV:ZBF4:BYJO
WARNING: No swap limit support

kubectl:

kubectl cluster-info
Kubernetes master is running at https://203.0.113.141
KubeDNS is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/kube-dns/proxy
kubernetes-dashboard is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
Grafana is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
Heapster is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy
InfluxDB is running at https://203.0.113.141/api/v1/namespaces/kube-system/services/monitoring-influxdb/proxy

6.9 - 컴포넌트 도구

6.9.1 - 기능 게이트

이 페이지에는 관리자가 다른 쿠버네티스 컴포넌트에서 지정할 수 있는 다양한 기능 게이트에 대한 개요가 포함되어 있다.

기능의 단계(stage)에 대한 설명은 기능 단계를 참고한다.

개요

기능 게이트는 쿠버네티스 기능을 설명하는 일련의 키=값 쌍이다. 각 쿠버네티스 컴포넌트에서 --feature-gates 커맨드 라인 플래그를 사용하여 이러한 기능을 켜거나 끌 수 있다.

각 쿠버네티스 컴포넌트를 사용하면 해당 컴포넌트와 관련된 기능 게이트 집합을 활성화 또는 비활성화할 수 있다. 모든 컴포넌트에 대한 전체 기능 게이트 집합을 보려면 -h 플래그를 사용한다. kubelet과 같은 컴포넌트의 기능 게이트를 설정하려면, 기능 쌍 목록에 지정된 --feature-gates 플래그를 사용한다.

--feature-gates="...,GracefulNodeShutdown=true"

다음 표는 다른 쿠버네티스 컴포넌트에서 설정할 수 있는 기능 게이트를 요약한 것이다.

알파 또는 베타 기능을 위한 기능 게이트

알파 또는 베타 단계에 있는 기능을 위한 기능 게이트
기능 디폴트 단계 도입 종료
APIListChunking false 알파 1.8 1.8
APIListChunking true 베타 1.9
APIPriorityAndFairness false 알파 1.17 1.19
APIPriorityAndFairness true 베타 1.20
APIResponseCompression false 알파 1.7 1.15
APIResponseCompression true 베타 1.16
APIServerIdentity false 알파 1.20
APIServerTracing false 알파 1.22
AllowInsecureBackendProxy true 베타 1.17
AnyVolumeDataSource false 알파 1.18
AppArmor true 베타 1.4
ControllerManagerLeaderMigration false 알파 1.21
CPUManager false 알파 1.8 1.9
CPUManager true 베타 1.10
CPUManagerPolicyOptions false 알파 1.22
CSIInlineVolume false 알파 1.15 1.15
CSIInlineVolume true 베타 1.16 -
CSIMigration false 알파 1.14 1.16
CSIMigration true 베타 1.17
CSIMigrationAWS false 알파 1.14
CSIMigrationAWS false 베타 1.17
CSIMigrationAzureDisk false 알파 1.15 1.18
CSIMigrationAzureDisk false 베타 1.19
CSIMigrationAzureFile false 알파 1.15 1.19
CSIMigrationAzureFile false 베타 1.21
CSIMigrationGCE false 알파 1.14 1.16
CSIMigrationGCE false 베타 1.17
CSIMigrationOpenStack false 알파 1.14 1.17
CSIMigrationOpenStack true 베타 1.18
CSIMigrationvSphere false 베타 1.19
CSIStorageCapacity false 알파 1.19 1.20
CSIStorageCapacity true 베타 1.21
CSIVolumeFSGroupPolicy false 알파 1.19 1.19
CSIVolumeFSGroupPolicy true 베타 1.20
CSIVolumeHealth false 알파 1.21
CSRDuration true 베타 1.22
ConfigurableFSGroupPolicy false 알파 1.18 1.19
ConfigurableFSGroupPolicy true 베타 1.20
ControllerManagerLeaderMigration false 알파 1.21 1.21
ControllerManagerLeaderMigration true 베타 1.22
CustomCPUCFSQuotaPeriod false 알파 1.12
DaemonSetUpdateSurge false 알파 1.21 1.21
DaemonSetUpdateSurge true 베타 1.22
DefaultPodTopologySpread false 알파 1.19 1.19
DefaultPodTopologySpread true 베타 1.20
DelegateFSGroupToCSIDriver false 알파 1.22
DevicePlugins false 알파 1.8 1.9
DevicePlugins true 베타 1.10
DisableAcceleratorUsageMetrics false 알파 1.19 1.19
DisableAcceleratorUsageMetrics true 베타 1.20
DisableCloudProviders false 알파 1.22
DownwardAPIHugePages false 알파 1.20 1.20
DownwardAPIHugePages false 베타 1.21
EfficientWatchResumption false 알파 1.20 1.20
EfficientWatchResumption true 베타 1.21
EndpointSliceTerminatingCondition false 알파 1.20 1.21
EndpointSliceTerminatingCondition true 베타 1.22
EphemeralContainers false 알파 1.16
ExpandCSIVolumes false 알파 1.14 1.15
ExpandCSIVolumes true 베타 1.16
ExpandedDNSConfig false 알파 1.22
ExpandInUsePersistentVolumes false 알파 1.11 1.14
ExpandInUsePersistentVolumes true 베타 1.15
ExpandPersistentVolumes false 알파 1.8 1.10
ExpandPersistentVolumes true 베타 1.11
ExperimentalHostUserNamespaceDefaulting false 베타 1.5
GenericEphemeralVolume false 알파 1.19 1.20
GenericEphemeralVolume true 베타 1.21
GracefulNodeShutdown false 알파 1.20 1.20
GracefulNodeShutdown true 베타 1.21
HPAContainerMetrics false 알파 1.20
HPAScaleToZero false 알파 1.16
IndexedJob false 알파 1.21 1.21
IndexedJob true 베타 1.22
IngressClassNamespacedParams false 알파 1.21 1.21
IngressClassNamespacedParams true 베타 1.22
InTreePluginAWSUnregister false 알파 1.21
InTreePluginAzureDiskUnregister false 알파 1.21
InTreePluginAzureFileUnregister false 알파 1.21
InTreePluginGCEUnregister false 알파 1.21
InTreePluginOpenStackUnregister false 알파 1.21
InTreePluginvSphereUnregister false 알파 1.21
IPv6DualStack false 알파 1.15 1.20
IPv6DualStack true 베타 1.21
JobTrackingWithFinalizers false 알파 1.22
KubeletCredentialProviders false 알파 1.20
KubeletInUserNamespace false 알파 1.22
KubeletPodResourcesGetAllocatable false 알파 1.21
LocalStorageCapacityIsolation false 알파 1.7 1.9
LocalStorageCapacityIsolation true 베타 1.10
LocalStorageCapacityIsolationFSQuotaMonitoring false 알파 1.15
LogarithmicScaleDown false 알파 1.21 1.21
LogarithmicScaleDown true 베타 1.22
MemoryManager false 알파 1.21 1.21
MemoryManager true 베타 1.22
MemoryQoS false 알파 1.22
MixedProtocolLBService false 알파 1.20
NetworkPolicyEndPort false 알파 1.21 1.21
NetworkPolicyEndPort true 베타 1.22
NodeSwap false 알파 1.22
NonPreemptingPriority false 알파 1.15 1.18
NonPreemptingPriority true 베타 1.19
PodDeletionCost false 알파 1.21 1.21
PodDeletionCost true 베타 1.22
PodAffinityNamespaceSelector false 알파 1.21 1.21
PodAffinityNamespaceSelector true 베타 1.22
PodOverhead false 알파 1.16 1.17
PodOverhead true 베타 1.18
PodSecurity false 알파 1.22
PreferNominatedNode false 알파 1.21 1.21
PreferNominatedNode true 베타 1.22
ProbeTerminationGracePeriod false 알파 1.21 1.21
ProbeTerminationGracePeriod false 베타 1.22
ProcMountType false 알파 1.12
ProxyTerminatingEndpoints false 알파 1.22
QOSReserved false 알파 1.11
ReadWriteOncePod false 알파 1.22
RemainingItemCount false 알파 1.15 1.15
RemainingItemCount true 베타 1.16
RemoveSelfLink false 알파 1.16 1.19
RemoveSelfLink true 베타 1.20
RotateKubeletServerCertificate false 알파 1.7 1.11
RotateKubeletServerCertificate true 베타 1.12
SeccompDefault false 알파 1.22
ServiceInternalTrafficPolicy false 알파 1.21 1.21
ServiceInternalTrafficPolicy true 베타 1.22
ServiceLBNodePortControl false 알파 1.20 1.21
ServiceLBNodePortControl true 베타 1.22
ServiceLoadBalancerClass false 알파 1.21 1.21
ServiceLoadBalancerClass true 베타 1.22
SizeMemoryBackedVolumes false 알파 1.20 1.21
SizeMemoryBackedVolumes true 베타 1.22
StatefulSetMinReadySeconds false 알파 1.22
StorageVersionAPI false 알파 1.20
StorageVersionHash false 알파 1.14 1.14
StorageVersionHash true 베타 1.15
SuspendJob false 알파 1.21 1.21
SuspendJob true 베타 1.22
TTLAfterFinished false 알파 1.12 1.20
TTLAfterFinished true 베타 1.21
TopologyAwareHints false 알파 1.21
TopologyManager false 알파 1.16 1.17
TopologyManager true 베타 1.18
VolumeCapacityPriority false 알파 1.21 -
WinDSR false 알파 1.14
WinOverlay false 알파 1.14 1.19
WinOverlay true 베타 1.20
WindowsHostProcessContainers false 알파 1.22

GA 또는 사용 중단된 기능을 위한 기능 게이트

GA 또는 사용 중단 기능을 위한 기능 게이트
기능 디폴트 단계 도입 종료
Accelerators false 알파 1.6 1.10
Accelerators - 사용중단 1.11 -
AdvancedAuditing false 알파 1.7 1.7
AdvancedAuditing true 베타 1.8 1.11
AdvancedAuditing true GA 1.12 -
AffinityInAnnotations false 알파 1.6 1.7
AffinityInAnnotations - 사용중단 1.8 -
AllowExtTrafficLocalEndpoints false 베타 1.4 1.6
AllowExtTrafficLocalEndpoints true GA 1.7 -
AttachVolumeLimit false 알파 1.11 1.11
AttachVolumeLimit true 베타 1.12 1.16
AttachVolumeLimit true GA 1.17 -
BalanceAttachedNodeVolumes false 알파 1.11 1.21
BalanceAttachedNodeVolumes false 사용중단 1.22
BlockVolume false 알파 1.9 1.12
BlockVolume true 베타 1.13 1.17
BlockVolume true GA 1.18 -
BoundServiceAccountTokenVolume false 알파 1.13 1.20
BoundServiceAccountTokenVolume true 베타 1.21 1.21
BoundServiceAccountTokenVolume true GA 1.22 -
CRIContainerLogRotation false 알파 1.10 1.10
CRIContainerLogRotation true 베타 1.11 1.20
CRIContainerLogRotation true GA 1.21 -
CSIBlockVolume false 알파 1.11 1.13
CSIBlockVolume true 베타 1.14 1.17
CSIBlockVolume true GA 1.18 -
CSIDriverRegistry false 알파 1.12 1.13
CSIDriverRegistry true 베타 1.14 1.17
CSIDriverRegistry true GA 1.18
CSIMigrationAWSComplete false 알파 1.17 1.20
CSIMigrationAWSComplete - 사용중단 1.21 -
CSIMigrationAzureDiskComplete false 알파 1.17 1.20
CSIMigrationAzureDiskComplete - 사용중단 1.21 -
CSIMigrationAzureFileComplete false 알파 1.17 1.20
CSIMigrationAzureFileComplete - 사용중단 1.21 -
CSIMigrationGCEComplete false 알파 1.17 1.20
CSIMigrationGCEComplete - 사용중단 1.21 -
CSIMigrationOpenStackComplete false 알파 1.17 1.20
CSIMigrationOpenStackComplete - 사용중단 1.21 -
CSIMigrationvSphereComplete false 베타 1.19 1.21
CSIMigrationvSphereComplete - 사용중단 1.22 -
CSINodeInfo false 알파 1.12 1.13
CSINodeInfo true 베타 1.14 1.16
CSINodeInfo true GA 1.17
CSIPersistentVolume false 알파 1.9 1.9
CSIPersistentVolume true 베타 1.10 1.12
CSIPersistentVolume true GA 1.13 -
CSIServiceAccountToken false 알파 1.20 1.20
CSIServiceAccountToken true 베타 1.21 1.21
CSIServiceAccountToken true GA 1.22
CronJobControllerV2 false 알파 1.20 1.20
CronJobControllerV2 true 베타 1.21 1.21
CronJobControllerV2 true GA 1.22 -
CustomPodDNS false 알파 1.9 1.9
CustomPodDNS true 베타 1.10 1.13
CustomPodDNS true GA 1.14 -
CustomResourceDefaulting false 알파 1.15 1.15
CustomResourceDefaulting true 베타 1.16 1.16
CustomResourceDefaulting true GA 1.17 -
CustomResourcePublishOpenAPI false 알파 1.14 1.14
CustomResourcePublishOpenAPI true 베타 1.15 1.15
CustomResourcePublishOpenAPI true GA 1.16 -
CustomResourceSubresources false 알파 1.10 1.10
CustomResourceSubresources true 베타 1.11 1.15
CustomResourceSubresources true GA 1.16 -
CustomResourceValidation false 알파 1.8 1.8
CustomResourceValidation true 베타 1.9 1.15
CustomResourceValidation true GA 1.16 -
CustomResourceWebhookConversion false 알파 1.13 1.14
CustomResourceWebhookConversion true 베타 1.15 1.15
CustomResourceWebhookConversion true GA 1.16 -
DryRun false 알파 1.12 1.12
DryRun true 베타 1.13 1.18
DryRun true GA 1.19 -
DynamicAuditing false 알파 1.13 1.18
DynamicAuditing - 사용중단 1.19 -
DynamicKubeletConfig false 알파 1.4 1.10
DynamicKubeletConfig true 베타 1.11 1.21
DynamicKubeletConfig false 사용중단 1.22 -
DynamicProvisioningScheduling false 알파 1.11 1.11
DynamicProvisioningScheduling - 사용중단 1.12 -
DynamicVolumeProvisioning true 알파 1.3 1.7
DynamicVolumeProvisioning true GA 1.8 -
EnableAggregatedDiscoveryTimeout true 사용중단 1.16 -
EnableEquivalenceClassCache false 알파 1.8 1.14
EnableEquivalenceClassCache - 사용중단 1.15 -
EndpointSlice false 알파 1.16 1.16
EndpointSlice false 베타 1.17 1.17
EndpointSlice true 베타 1.18 1.20
EndpointSlice true GA 1.21 -
EndpointSliceNodeName false 알파 1.20 1.20
EndpointSliceNodeName true GA 1.21 -
EndpointSliceProxying false 알파 1.18 1.18
EndpointSliceProxying true 베타 1.19 1.21
EndpointSliceProxying true GA 1.22 -
ExperimentalCriticalPodAnnotation false 알파 1.5 1.12
ExperimentalCriticalPodAnnotation false 사용중단 1.13 -
EvenPodsSpread false 알파 1.16 1.17
EvenPodsSpread true 베타 1.18 1.18
EvenPodsSpread true GA 1.19 -
ExecProbeTimeout true GA 1.20 -
ExternalPolicyForExternalIP true GA 1.18 -
GCERegionalPersistentDisk true 베타 1.10 1.12
GCERegionalPersistentDisk true GA 1.13 -
HugePageStorageMediumSize false 알파 1.18 1.18
HugePageStorageMediumSize true 베타 1.19 1.21
HugePageStorageMediumSize true GA 1.22 -
HugePages false 알파 1.8 1.9
HugePages true 베타 1.10 1.13
HugePages true GA 1.14 -
HugePageStorageMediumSize false 알파 1.18 1.18
HugePageStorageMediumSize true 베타 1.19 1.21
HugePageStorageMediumSize true GA 1.22 -
HyperVContainer false 알파 1.10 1.19
HyperVContainer false 사용중단 1.20 -
ImmutableEphemeralVolumes false 알파 1.18 1.18
ImmutableEphemeralVolumes true 베타 1.19 1.20
ImmutableEphemeralVolumes true GA 1.21
Initializers false 알파 1.7 1.13
Initializers - 사용중단 1.14 -
KubeletConfigFile false 알파 1.8 1.9
KubeletConfigFile - 사용중단 1.10 -
KubeletPluginsWatcher false 알파 1.11 1.11
KubeletPluginsWatcher true 베타 1.12 1.12
KubeletPluginsWatcher true GA 1.13 -
KubeletPodResources false 알파 1.13 1.14
KubeletPodResources true 베타 1.15
KubeletPodResources true GA 1.20
LegacyNodeRoleBehavior false 알파 1.16 1.18
LegacyNodeRoleBehavior true 베타 1.19 1.20
LegacyNodeRoleBehavior false GA 1.21 -
MountContainers false 알파 1.9 1.16
MountContainers false 사용중단 1.17 -
MountPropagation false 알파 1.8 1.9
MountPropagation true 베타 1.10 1.11
MountPropagation true GA 1.12 -
NodeDisruptionExclusion false 알파 1.16 1.18
NodeDisruptionExclusion true 베타 1.19 1.20
NodeDisruptionExclusion true GA 1.21 -
NodeLease false 알파 1.12 1.13
NodeLease true 베타 1.14 1.16
NodeLease true GA 1.17 -
NamespaceDefaultLabelName true 베타 1.21 1.21
NamespaceDefaultLabelName true GA 1.22 -
PVCProtection false 알파 1.9 1.9
PVCProtection - 사용중단 1.10 -
PersistentLocalVolumes false 알파 1.7 1.9
PersistentLocalVolumes true 베타 1.10 1.13
PersistentLocalVolumes true GA 1.14 -
PodDisruptionBudget false 알파 1.3 1.4
PodDisruptionBudget true 베타 1.5 1.20
PodDisruptionBudget true GA 1.21 -
PodPriority false 알파 1.8 1.10
PodPriority true 베타 1.11 1.13
PodPriority true GA 1.14 -
PodReadinessGates false 알파 1.11 1.11
PodReadinessGates true 베타 1.12 1.13
PodReadinessGates true GA 1.14 -
PodShareProcessNamespace false 알파 1.10 1.11
PodShareProcessNamespace true 베타 1.12 1.16
PodShareProcessNamespace true GA 1.17 -
RequestManagement false 알파 1.15 1.16
RequestManagement - 사용중단 1.17 -
ResourceLimitsPriorityFunction false 알파 1.9 1.18
ResourceLimitsPriorityFunction - 사용중단 1.19 -
ResourceQuotaScopeSelectors false 알파 1.11 1.11
ResourceQuotaScopeSelectors true 베타 1.12 1.16
ResourceQuotaScopeSelectors true GA 1.17 -
RootCAConfigMap false 알파 1.13 1.19
RootCAConfigMap true 베타 1.20 1.20
RootCAConfigMap true GA 1.21 -
RotateKubeletClientCertificate true 베타 1.8 1.18
RotateKubeletClientCertificate true GA 1.19 -
RunAsGroup true 베타 1.14 1.20
RunAsGroup true GA 1.21 -
RuntimeClass false 알파 1.12 1.13
RuntimeClass true 베타 1.14 1.19
RuntimeClass true GA 1.20 -
SCTPSupport false 알파 1.12 1.18
SCTPSupport true 베타 1.19 1.19
SCTPSupport true GA 1.20 -
ScheduleDaemonSetPods false 알파 1.11 1.11
ScheduleDaemonSetPods true 베타 1.12 1.16
ScheduleDaemonSetPods true GA 1.17 -
SelectorIndex false 알파 1.18 1.18
SelectorIndex true 베타 1.19 1.19
SelectorIndex true GA 1.20 -
ServerSideApply false 알파 1.14 1.15
ServerSideApply true 베타 1.16 1.21
ServerSideApply true GA 1.22 -
ServiceAccountIssuerDiscovery false 알파 1.18 1.19
ServiceAccountIssuerDiscovery true 베타 1.20 1.20
ServiceAccountIssuerDiscovery true GA 1.21 -
ServiceAppProtocol false 알파 1.18 1.18
ServiceAppProtocol true 베타 1.19 1.19
ServiceAppProtocol true GA 1.20 -
ServiceLoadBalancerFinalizer false 알파 1.15 1.15
ServiceLoadBalancerFinalizer true 베타 1.16 1.16
ServiceLoadBalancerFinalizer true GA 1.17 -
ServiceNodeExclusion false 알파 1.8 1.18
ServiceNodeExclusion true 베타 1.19 1.20
ServiceNodeExclusion true GA 1.21 -
ServiceTopology false 알파 1.17 1.19
ServiceTopology false 사용중단 1.20 -
SetHostnameAsFQDN false 알파 1.19 1.19
SetHostnameAsFQDN true 베타 1.20 1.21
SetHostnameAsFQDN true GA 1.22 -
StartupProbe false 알파 1.16 1.17
StartupProbe true 베타 1.18 1.19
StartupProbe true GA 1.20 -
StorageObjectInUseProtection true 베타 1.10 1.10
StorageObjectInUseProtection true GA 1.11 -
StreamingProxyRedirects false 베타 1.5 1.5
StreamingProxyRedirects true 베타 1.6 1.17
StreamingProxyRedirects true 사용중단 1.18 1.21
StreamingProxyRedirects false 사용중단 1.22 -
SupportIPVSProxyMode false 알파 1.8 1.8
SupportIPVSProxyMode false 베타 1.9 1.9
SupportIPVSProxyMode true 베타 1.10 1.10
SupportIPVSProxyMode true GA 1.11 -
SupportNodePidsLimit false 알파 1.14 1.14
SupportNodePidsLimit true 베타 1.15 1.19
SupportNodePidsLimit true GA 1.20 -
SupportPodPidsLimit false 알파 1.10 1.13
SupportPodPidsLimit true 베타 1.14 1.19
SupportPodPidsLimit true GA 1.20 -
Sysctls true 베타 1.11 1.20
Sysctls true GA 1.21
TaintBasedEvictions false 알파 1.6 1.12
TaintBasedEvictions true 베타 1.13 1.17
TaintBasedEvictions true GA 1.18 -
TaintNodesByCondition false 알파 1.8 1.11
TaintNodesByCondition true 베타 1.12 1.16
TaintNodesByCondition true GA 1.17 -
TokenRequest false 알파 1.10 1.11
TokenRequest true 베타 1.12 1.19
TokenRequest true GA 1.20 -
TokenRequestProjection false 알파 1.11 1.11
TokenRequestProjection true 베타 1.12 1.19
TokenRequestProjection true GA 1.20 -
ValidateProxyRedirects false 알파 1.12 1.13
ValidateProxyRedirects true 베타 1.14 1.21
ValidateProxyRedirects true 사용중단 1.22 -
VolumePVCDataSource false 알파 1.15 1.15
VolumePVCDataSource true 베타 1.16 1.17
VolumePVCDataSource true GA 1.18 -
VolumeScheduling false 알파 1.9 1.9
VolumeScheduling true 베타 1.10 1.12
VolumeScheduling true GA 1.13 -
VolumeSnapshotDataSource false 알파 1.12 1.16
VolumeSnapshotDataSource true 베타 1.17 1.19
VolumeSnapshotDataSource true GA 1.20 -
VolumeSubpath true GA 1.10 -
VolumeSubpathEnvExpansion false 알파 1.14 1.14
VolumeSubpathEnvExpansion true 베타 1.15 1.16
VolumeSubpathEnvExpansion true GA 1.17 -
WarningHeaders true 베타 1.19 1.21
WarningHeaders true GA 1.22 -
WatchBookmark false 알파 1.15 1.15
WatchBookmark true 베타 1.16 1.16
WatchBookmark true GA 1.17 -
WindowsEndpointSliceProxying false 알파 1.19 1.20
WindowsEndpointSliceProxying true 베타 1.21 1.21
WindowsEndpointSliceProxying true GA 1.22 -
WindowsGMSA false 알파 1.14 1.15
WindowsGMSA true 베타 1.16 1.17
WindowsGMSA true GA 1.18 -
WindowsHostProcessContainers false 알파 1.22
WindowsRunAsUserName false 알파 1.16 1.16
WindowsRunAsUserName true 베타 1.17 1.17
WindowsRunAsUserName true GA 1.18 -

기능 사용

기능 단계

기능은 알파, 베타 또는 GA 단계일 수 있다. 알파 기능은 다음을 의미한다.

  • 기본적으로 비활성화되어 있다.
  • 버그가 있을 수 있다. 이 기능을 사용하면 버그에 노출될 수 있다.
  • 기능에 대한 지원은 사전 통지없이 언제든지 중단될 수 있다.
  • API는 이후 소프트웨어 릴리스에서 예고없이 호환되지 않는 방식으로 변경될 수 있다.
  • 버그의 위험이 증가하고 장기 지원이 부족하여, 단기 테스트 클러스터에서만 사용하는 것이 좋다.

베타 기능은 다음을 의미한다.

  • 기본적으로 활성화되어 있다.
  • 이 기능은 잘 테스트되었다. 이 기능을 활성화하면 안전한 것으로 간주된다.
  • 세부 내용은 변경될 수 있지만, 전체 기능에 대한 지원은 중단되지 않는다.
  • 오브젝트의 스키마 및/또는 시맨틱은 후속 베타 또는 안정 릴리스에서 호환되지 않는 방식으로 변경될 수 있다. 이러한 상황이 발생하면, 다음 버전으로 마이그레이션하기 위한 지침을 제공한다. API 오브젝트를 삭제, 편집 및 재작성해야 할 수도 있다. 편집 과정에서 약간의 생각이 필요할 수 있다. 해당 기능에 의존하는 애플리케이션의 경우 다운타임이 필요할 수 있다.
  • 후속 릴리스에서 호환되지 않는 변경이 발생할 수 있으므로 업무상 중요하지 않은(non-business-critical) 용도로만 권장한다. 독립적으로 업그레이드할 수 있는 여러 클러스터가 있는 경우, 이 제한을 완화할 수 있다.

GA(General Availability) 기능은 안정 기능이라고도 한다. 이 의미는 다음과 같다.

  • 이 기능은 항상 활성화되어 있다. 비활성화할 수 없다.
  • 해당 기능 게이트는 더 이상 필요하지 않다.
  • 여러 후속 버전의 릴리스된 소프트웨어에 안정적인 기능의 버전이 포함된다.

기능 게이트 목록

각 기능 게이트는 특정 기능을 활성화/비활성화하도록 설계되었다.

  • APIListChunking: API 클라이언트가 API 서버에서 (LIST 또는 GET) 리소스를 청크(chunks)로 검색할 수 있도록 한다.
  • APIPriorityAndFairness: 각 서버의 우선 순위와 공정성을 통해 동시 요청을 관리할 수 있다. (RequestManagement 에서 이름이 변경됨)
  • APIResponseCompression: LIST 또는 GET 요청에 대한 API 응답을 압축한다.
  • APIServerIdentity: 클러스터의 각 API 서버에 ID를 할당한다.
  • APIServerTracing: API 서버에서 분산 추적(tracing)에 대한 지원을 추가한다.
  • Accelerators: 도커 사용 시 Nvidia GPU 지원 활성화한다.
  • AdvancedAuditing: 고급 감사 기능을 활성화한다.
  • AffinityInAnnotations: 파드 어피니티 또는 안티-어피니티 설정을 활성화한다.
  • AllowExtTrafficLocalEndpoints: 서비스가 외부 요청을 노드의 로컬 엔드포인트로 라우팅할 수 있도록 한다.
  • AllowInsecureBackendProxy: 사용자가 파드 로그 요청에서 kubelet의 TLS 확인을 건너뛸 수 있도록 한다.
  • AnyVolumeDataSource: PVCDataSource 로 모든 사용자 정의 리소스 사용을 활성화한다.
  • AppArmor: 도커를 사용할 때 리눅스 노드에서 AppArmor 기반의 필수 접근 제어를 활성화한다. 자세한 내용은 AppArmor 튜토리얼을 참고한다.
  • AttachVolumeLimit: 볼륨 플러그인이 노드에 연결될 수 있는 볼륨 수에 대한 제한을 보고하도록 한다. 자세한 내용은 동적 볼륨 제한을 참고한다.
  • BalanceAttachedNodeVolumes: 스케줄링 시 균형 잡힌 리소스 할당을 위해 고려할 노드의 볼륨 수를 포함한다. 스케줄러가 결정을 내리는 동안 CPU, 메모리 사용률 및 볼륨 수가 더 가까운 노드가 선호된다.
  • BlockVolume: 파드에서 원시 블록 장치의 정의와 사용을 활성화한다. 자세한 내용은 원시 블록 볼륨 지원을 참고한다.
  • BoundServiceAccountTokenVolume: ServiceAccountTokenVolumeProjection으로 구성된 프로젝션 볼륨을 사용하도록 서비스어카운트 볼륨을 마이그레이션한다. 클러스터 관리자는 serviceaccount_stale_tokens_total 메트릭을 사용하여 확장 토큰에 의존하는 워크로드를 모니터링 할 수 있다. 이러한 워크로드가 없는 경우 --service-account-extend-token-expiration=false 플래그로 kube-apiserver를 시작하여 확장 토큰 기능을 끈다. 자세한 내용은 바운드 서비스 계정 토큰을 확인한다.
  • ControllerManagerLeaderMigration: HA 클러스터에서 클러스터 오퍼레이터가 kube-controller-manager의 컨트롤러들을 외부 controller-manager(예를 들면, cloud-controller-manager)로 다운타임 없이 라이브 마이그레이션할 수 있도록 허용하도록 kube-controller-managercloud-controller-manager의 리더 마이그레이션(Leader Migration)을 활성화한다.
  • CPUManager: 컨테이너 수준의 CPU 어피니티 지원을 활성화한다. CPU 관리 정책을 참고한다.
  • CPUManagerPolicyOptions: CPUManager 정책의 미세 조정을 허용한다.
  • CRIContainerLogRotation: cri 컨테이너 런타임에 컨테이너 로그 로테이션을 활성화한다. 로그 파일 사이즈 기본값은 10MB이며, 컨테이너 당 최대 로그 파일 수 기본값은 5이다. 이 값은 kubelet 환경설정으로 변경할 수 있다. 더 자세한 내용은 노드 레벨에서의 로깅을 참고한다.
  • CSIBlockVolume: 외부 CSI 볼륨 드라이버가 블록 스토리지를 지원할 수 있게 한다. 자세한 내용은 csi 원시 블록 볼륨 지원 문서를 참고한다.
  • CSIDriverRegistry: csi.storage.k8s.io에서 CSIDriver API 오브젝트와 관련된 모든 로직을 활성화한다.
  • CSIInlineVolume: 파드에 대한 CSI 인라인 볼륨 지원을 활성화한다.
  • CSIMigration: shim 및 변환 로직을 통해 볼륨 작업을 인-트리 플러그인에서 사전 설치된 해당 CSI 플러그인으로 라우팅할 수 있다.
  • CSIMigrationAWS: shim 및 변환 로직을 통해 볼륨 작업을 AWS-EBS 인-트리 플러그인에서 EBS CSI 플러그인으로 라우팅할 수 있다. 노드에 EBS CSI 플러그인이 설치와 구성이 되어 있지 않은 경우 인-트리 EBS 플러그인으로 폴백(falling back)을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationAWSComplete: kubelet 및 볼륨 컨트롤러에서 EBS 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직을 사용하여 볼륨 작업을 AWS-EBS 인-트리 플러그인에서 EBS CSI 플러그인으로 라우팅할 수 있다. 클러스터의 모든 노드에 CSIMigration과 CSIMigrationAWS 기능 플래그가 활성화되고 EBS CSI 플러그인이 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 EBS 플러그인의 등록을 막는 InTreePluginAWSUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSIMigrationAzureDisk: shim 및 변환 로직을 통해 볼륨 작업을 Azure-Disk 인-트리 플러그인에서 AzureDisk CSI 플러그인으로 라우팅할 수 있다. 노드에 AzureDisk CSI 플러그인이 설치와 구성이 되어 있지 않은 경우 인-트리 AzureDisk 플러그인으로 폴백을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationAzureDiskComplete: kubelet 및 볼륨 컨트롤러에서 Azure-Disk 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직을 사용하여 볼륨 작업을 Azure-Disk 인-트리 플러그인에서 AzureDisk CSI 플러그인으로 라우팅할 수 있다. 클러스터의 모든 노드에 CSIMigration과 CSIMigrationAzureDisk 기능 플래그가 활성화되고 AzureDisk CSI 플러그인이 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 AzureDisk 플러그인의 등록을 막는 InTreePluginAzureDiskUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSIMigrationAzureFile: shim 및 변환 로직을 통해 볼륨 작업을 Azure-File 인-트리 플러그인에서 AzureFile CSI 플러그인으로 라우팅할 수 있다. 노드에 AzureFile CSI 플러그인이 설치 및 구성이 되어 있지 않은 경우 인-트리 AzureFile 플러그인으로 폴백을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationAzureFileComplete: kubelet 및 볼륨 컨트롤러에서 Azure 파일 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직을 통해 볼륨 작업을 Azure 파일 인-트리 플러그인에서 AzureFile CSI 플러그인으로 라우팅할 수 있다. 클러스터의 모든 노드에 CSIMigration과 CSIMigrationAzureFile 기능 플래그가 활성화되고 AzureFile CSI 플러그인이 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 AzureFile 플러그인의 등록을 막는 InTreePluginAzureFileUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSIMigrationGCE: shim 및 변환 로직을 통해 볼륨 작업을 GCE-PD 인-트리 플러그인에서 PD CSI 플러그인으로 라우팅할 수 있다. 노드에 PD CSI 플러그인이 설치 및 구성이 되어 있지 않은 경우 인-트리 GCE 플러그인으로 폴백을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationGCEComplete: kubelet 및 볼륨 컨트롤러에서 GCE-PD 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직을 통해 볼륨 작업을 GCE-PD 인-트리 플러그인에서 PD CSI 플러그인으로 라우팅할 수 있다. CSIMigration과 CSIMigrationGCE 기능 플래그가 활성화되고 PD CSI 플러그인이 클러스터의 모든 노드에 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 GCE PD 플러그인의 등록을 막는 InTreePluginGCEUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSIMigrationOpenStack: shim 및 변환 로직을 통해 볼륨 작업을 Cinder 인-트리 플러그인에서 Cinder CSI 플러그인으로 라우팅할 수 있다. 노드에 Cinder CSI 플러그인이 설치 및 구성이 되어 있지 않은 경우 인-트리 Cinder 플러그인으로 폴백을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationOpenStackComplete: kubelet 및 볼륨 컨트롤러에서 Cinder 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직이 Cinder 인-트리 플러그인에서 Cinder CSI 플러그인으로 볼륨 작업을 라우팅할 수 있도록 한다. 클러스터의 모든 노드에 CSIMigration과 CSIMigrationOpenStack 기능 플래그가 활성화되고 Cinder CSI 플러그인이 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 openstack cinder 플러그인의 등록을 막는 InTreePluginOpenStackUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSIMigrationvSphere: vSphere 인-트리 플러그인에서 vSphere CSI 플러그인으로 볼륨 작업을 라우팅하는 shim 및 변환 로직을 사용한다. 노드에 vSphere CSI 플러그인이 설치 및 구성이 되어 있지 않은 경우 인-트리 vSphere 플러그인으로 폴백을 지원한다. CSIMigration 기능 플래그가 필요하다.
  • CSIMigrationvSphereComplete: kubelet 및 볼륨 컨트롤러에서 vSphere 인-트리 플러그인 등록을 중지하고 shim 및 변환 로직을 활성화하여 vSphere 인-트리 플러그인에서 vSphere CSI 플러그인으로 볼륨 작업을 라우팅할 수 있도록 한다. CSIMigration 및 CSIMigrationvSphere 기능 플래그가 활성화되고 vSphere CSI 플러그인이 클러스터의 모든 노드에 설치 및 구성이 되어 있어야 한다. 이 플래그는 인-트리 vsphere 플러그인의 등록을 막는 InTreePluginvSphereUnregister 기능 플래그로 인해 더 이상 사용되지 않는다.
  • CSINodeInfo: csi.storage.k8s.io에서 CSINodeInfo API 오브젝트와 관련된 모든 로직을 활성화한다.
  • CSIPersistentVolume: CSI (Container Storage Interface) 호환 볼륨 플러그인을 통해 프로비저닝된 볼륨을 감지하고 마운트할 수 있다.
  • CSIServiceAccountToken : 볼륨을 마운트하는 파드의 서비스 계정 토큰을 받을 수 있도록 CSI 드라이버를 활성화한다. 토큰 요청을 참조한다.
  • CSIStorageCapacity: CSI 드라이버가 스토리지 용량 정보를 게시하고 쿠버네티스 스케줄러가 파드를 스케줄할 때 해당 정보를 사용하도록 한다. 스토리지 용량을 참고한다. 자세한 내용은 csi 볼륨 유형 문서를 확인한다.
  • CSIVolumeFSGroupPolicy: CSI드라이버가 fsGroupPolicy 필드를 사용하도록 허용한다. 이 필드는 CSI드라이버에서 생성된 볼륨이 마운트될 때 볼륨 소유권과 권한 수정을 지원하는지 여부를 제어한다.
  • CSIVolumeHealth: 노드에서의 CSI 볼륨 상태 모니터링 기능을 활성화한다.
  • CSRDuration: 클라이언트가 쿠버네티스 CSR API를 통해 발급된 인증서의 기간을 요청할 수 있다.
  • ConfigurableFSGroupPolicy: 사용자가 파드에 볼륨을 마운트할 때 fsGroups에 대한 볼륨 권한 변경 정책을 구성할 수 있다. 자세한 내용은 파드의 볼륨 권한 및 소유권 변경 정책 구성을 참고한다.
  • ControllerManagerLeaderMigration: kube-controller-managercloud-controller-manager에 대한 리더 마이그레이션을 지원한다.
  • CronJobControllerV2: 크론잡(CronJob) 컨트롤러의 대체 구현을 사용한다. 그렇지 않으면, 동일한 컨트롤러의 버전 1이 선택된다.
  • CustomCPUCFSQuotaPeriod: kubelet config에서 cpuCFSQuotaPeriod 를 노드가 변경할 수 있도록 한다.
  • CustomPodDNS: dnsConfig 속성을 사용하여 파드의 DNS 설정을 사용자 정의할 수 있다. 자세한 내용은 파드의 DNS 설정을 확인한다.
  • CustomResourceDefaulting: OpenAPI v3 유효성 검사 스키마에서 기본값에 대한 CRD 지원을 활성화한다.
  • CustomResourcePublishOpenAPI: CRD OpenAPI 사양을 게시할 수 있다.
  • CustomResourceSubresources: 커스텀리소스데피니션에서 생성된 리소스에서 /status/scale 하위 리소스를 활성화한다.
  • CustomResourceValidation: 커스텀리소스데피니션에서 생성된 리소스에서 스키마 기반 유효성 검사를 활성화한다.
  • CustomResourceWebhookConversion: 커스텀리소스데피니션에서 생성된 리소스에 대해 웹 훅 기반의 변환을 활성화한다.
  • DaemonSetUpdateSurge: 노드당 업데이트 중 가용성을 유지하도록 데몬셋 워크로드를 사용하도록 설정한다.
  • DefaultPodTopologySpread: PodTopologySpread 스케줄링 플러그인을 사용하여 기본 분배를 수행한다.
  • DelegateFSGroupToCSIDriver: CSI 드라이버가 지원할 경우, NodeStageVolume 및 NodePublishVolume CSI 호출을 통해 fsGroup를 전달하여 파드의 securityContext에서 fsGroup를 드라이브에 적용하는 역할을 위임한다.
  • DevicePlugins: 노드에서 장치 플러그인 기반 리소스 프로비저닝을 활성화한다.
  • DisableAcceleratorUsageMetrics: kubelet이 수집한 액셀러레이터 지표 비활성화.
  • DisableCloudProviders: kube-apiserver, kube-controller-manager, --cloud-provider 컴포넌트 플래그와 관련된 kubelet의 모든 기능을 비활성화한다.
  • DownwardAPIHugePages: 다운워드 API에서 hugepages 사용을 활성화한다.
  • DryRun: 서버 측의 dry run 요청을 요청을 활성화하여 커밋하지 않고 유효성 검사, 병합 및 변화를 테스트할 수 있다.
  • DynamicAuditing: v1.19 이전의 버전에서 동적 감사를 활성화하는 데 사용된다.
  • DynamicKubeletConfig: kubelet의 동적 구성을 활성화한다. kubelet 재구성을 참고한다.
  • DynamicProvisioningScheduling: 볼륨 토폴로지를 인식하고 PV 프로비저닝을 처리하도록 기본 스케줄러를 확장한다. 이 기능은 v1.12의 VolumeScheduling 기능으로 대체되었다.
  • DynamicVolumeProvisioning: 파드에 퍼시스턴트 볼륨의 동적 프로비저닝을 활성화한다.
  • EfficientWatchResumption: 스토리지에서 생성된 북마크(진행 알림) 이벤트를 사용자에게 전달할 수 있다. 이것은 감시 작업에만 적용된다.
  • EnableAggregatedDiscoveryTimeout: 수집된 검색 호출에서 5초 시간 초과를 활성화한다.
  • EnableEquivalenceClassCache: 스케줄러가 파드를 스케줄링할 때 노드의 동등성을 캐시할 수 있게 한다.
  • EndpointSlice: 보다 스케일링 가능하고 확장 가능한 네트워크 엔드포인트에 대한 엔드포인트슬라이스(EndpointSlices)를 활성화한다. 엔드포인트슬라이스 활성화를 참고한다.
  • EndpointSliceNodeName : 엔드포인트슬라이스 nodeName 필드를 활성화한다.
  • EndpointSliceProxying: 활성화되면, 리눅스에서 실행되는 kube-proxy는 엔드포인트 대신 엔드포인트슬라이스를 기본 데이터 소스로 사용하여 확장성과 성능을 향상시킨다. 엔드포인트슬라이스 활성화를 참고한다.
  • EndpointSliceTerminatingCondition: 엔드포인트슬라이스 terminatingserving 조건 필드를 활성화한다.
  • EphemeralContainers: 파드를 실행하기 위한 임시 컨테이너를 추가할 수 있다.
  • EvenPodsSpread: 토폴로지 도메인 간에 파드를 균등하게 스케줄링할 수 있다. 파드 토폴로지 분배 제약 조건을 참고한다.
  • ExecProbeTimeout : kubelet이 exec 프로브 시간 초과를 준수하는지 확인한다. 이 기능 게이트는 기존 워크로드가 쿠버네티스가 exec 프로브 제한 시간을 무시한 현재 수정된 결함에 의존하는 경우 존재한다. 준비성 프로브를 참조한다.
  • ExpandCSIVolumes: CSI 볼륨 확장을 활성화한다.
  • ExpandedDNSConfig: 더 많은 DNS 검색 경로와 더 긴 DNS 검색 경로 목록을 허용하려면 kubelet과 kube-apiserver를 사용하도록 설정한다. 확장된 DNS 구성을 참고한다.
  • ExpandInUsePersistentVolumes: 사용 중인 PVC를 확장할 수 있다. 사용 중인 퍼시스턴트볼륨클레임 크기 조정을 참고한다.
  • ExpandPersistentVolumes: 퍼시스턴트 볼륨 확장을 활성화한다. 퍼시스턴트 볼륨 클레임 확장을 참고한다.
  • ExperimentalCriticalPodAnnotation: 특정 파드에 critical 로 어노테이션을 달아서 스케줄링이 보장되도록 한다. 이 기능은 v1.13부터 파드 우선 순위 및 선점으로 인해 사용 중단되었다.
  • ExperimentalHostUserNamespaceDefaulting: 사용자 네임스페이스를 호스트로 기본 활성화한다. 이것은 다른 호스트 네임스페이스, 호스트 마운트, 권한이 있는 컨테이너 또는 특정 비-네임스페이스(non-namespaced) 기능(예: MKNODE, SYS_MODULE 등)을 사용하는 컨테이너를 위한 것이다. 도커 데몬에서 사용자 네임스페이스 재 매핑이 활성화된 경우에만 활성화해야 한다.
  • ExternalPolicyForExternalIP: ExternalTrafficPolicy가 서비스(Service) ExternalIP에 적용되지 않는 버그를 수정한다.
  • GCERegionalPersistentDisk: GCE에서 지역 PD 기능을 활성화한다.
  • GenericEphemeralVolume: 일반 볼륨의 모든 기능을 지원하는 임시, 인라인 볼륨을 활성화한다(타사 스토리지 공급 업체, 스토리지 용량 추적, 스냅샷으로부터 복원 등에서 제공할 수 있음). 임시 볼륨을 참고한다.
  • GracefulNodeShutdown : kubelet에서 정상 종료를 지원한다. 시스템 종료 중에 kubelet은 종료 이벤트를 감지하고 노드에서 실행 중인 파드를 정상적으로 종료하려고 시도한다. 자세한 내용은 Graceful Node Shutdown을 참조한다.
  • HPAContainerMetrics: HorizontalPodAutoscaler를 활성화하여 대상 파드의 개별 컨테이너 메트릭을 기반으로 확장한다.
  • HPAScaleToZero: 사용자 정의 또는 외부 메트릭을 사용할 때 HorizontalPodAutoscaler 리소스에 대해 minReplicas 를 0으로 설정한다.
  • HugePages: 사전 할당된 huge page의 할당 및 사용을 활성화한다.
  • HugePageStorageMediumSize: 사전 할당된 huge page의 여러 크기를 지원한다.
  • HyperVContainer: 윈도우 컨테이너를 위한 Hyper-V 격리 기능을 활성화한다.
  • ImmutableEphemeralVolumes: 안정성과 성능 향상을 위해 개별 시크릿(Secret)과 컨피그맵(ConfigMap)을 변경할 수 없는(immutable) 것으로 표시할 수 있다.
  • InTreePluginAWSUnregister: kubelet 및 볼륨 컨트롤러에 aws-ebs 인-트리 플러그인의 등록을 중지한다.
  • InTreePluginAzureDiskUnregister: kubelet 및 볼륨 컨트롤러에 azuredisk 인-트리 플러그인의 등록을 중지한다.
  • InTreePluginAzureFileUnregister: kubelet 및 볼륨 컨트롤러에 azurefile 인-트리 플러그인의 등록을 중지한다.
  • InTreePluginGCEUnregister: kubelet 및 볼륨 컨트롤러에 gce-pd 인-트리 플러그인의 등록을 중지한다.
  • InTreePluginOpenStackUnregister: kubelet 및 볼륨 컨트롤러에 오픈스택 cinder 인-트리 플러그인의 등록을 중지한다.
  • InTreePluginvSphereUnregister: kubelet 및 볼륨 컨트롤러에 vSphere 인-트리 플러그인의 등록을 중지한다.
  • IndexedJob: 컨트롤러가 완료 횟수를 기반으로 파드 완료를 관리할 수 있도록 한다.
  • IngressClassNamespacedParams: IngressClass 리소스가 네임스페이스 범위로 한정된 파라미터를 이용할 수 있도록 한다. 이 기능은 IngressClass.spec.parametersScopeNamespace 2개의 필드를 추가한다.
  • Initializers: Initializers 어드미션 플러그인을 사용하여 오브젝트 생성의 비동기 조정을 허용한다.
  • IPv6DualStack: IPv6을 위한 이중 스택 기능을 활성화한다.
  • JobTrackingWithFinalizers: 클러스터에 무제한으로 남아 있는 파드에 의존하지 않고 의 완료를 추적할 수 있다. 잡 컨트롤러는 완료된 파드를 추적하기 위해 완료된 파드의 잡 상태 필드를 사용한다.
  • KubeletConfigFile: 구성 파일을 사용하여 지정된 파일에서 kubelet 구성을 로드할 수 있다. 자세한 내용은 구성 파일을 통해 kubelet 파라미터 설정을 참고한다.
  • KubeletCredentialProviders: 이미지 풀 자격 증명에 대해 kubelet exec 자격 증명 공급자를 활성화한다.
  • KubeletInUserNamespace: user namespace에서 kubelet 실행을 활성화한다. 루트가 아닌 유저로 쿠버네티스 노드 컴포넌트 실행을 참고한다.
  • KubeletPluginsWatcher: kubelet이 CSI 볼륨 드라이버와 같은 플러그인을 검색할 수 있도록 프로브 기반 플러그인 감시자(watcher) 유틸리티를 사용한다.
  • KubeletPodResources: kubelet의 파드 리소스 gPRC 엔드포인트를 활성화한다. 자세한 내용은 장치 모니터링 지원을 참고한다.
  • KubeletPodResourcesGetAllocatable: kubelet의 파드 리소스 GetAllocatableResources 기능을 활성화한다. 이 API는 클라이언트가 노드의 여유 컴퓨팅 자원을 잘 파악할 수 있도록, 할당 가능 자원에 대한 정보를 자원 할당 보고한다.
  • LegacyNodeRoleBehavior: 비활성화되면, 서비스 로드 밸런서 및 노드 중단의 레거시 동작은 NodeDisruptionExclusionServiceNodeExclusion 에 의해 제공된 기능별 레이블을 대신하여 node-role.kubernetes.io/master 레이블을 무시한다.
  • LocalStorageCapacityIsolation: 로컬 임시 스토리지emptyDir 볼륨sizeLimit 속성을 사용할 수 있게 한다.
  • LocalStorageCapacityIsolationFSQuotaMonitoring: 로컬 임시 스토리지LocalStorageCapacityIsolation 이 활성화되고 emptyDir 볼륨의 백업 파일시스템이 프로젝트 쿼터를 지원하고 활성화된 경우, 파일시스템 사용보다는 프로젝트 쿼터를 사용하여 emptyDir 볼륨 스토리지 사용을 모니터링하여 성능과 정확성을 향상시킨다.
  • LogarithmicScaleDown: 컨트롤러 스케일 다운 시에 파드 타임스탬프를 로그 스케일로 버켓화하여 축출할 파드를 반-랜덤하게 선택하는 기법을 활성화한다.
  • MemoryManager: NUMA 토폴로지를 기반으로 컨테이너에 대한 메모리 어피니티를 설정할 수 있다.
  • MemoryQoS: cgroup v2 메모리 컨트롤러를 사용하여 파드/컨테이너에서 메모리 보호 및 사용 제한을 사용하도록 설정한다.
  • MixedProtocolLBService: 동일한 로드밸런서 유형 서비스 인스턴스에서 다른 프로토콜 사용을 활성화한다.
  • MountContainers: 호스트의 유틸리티 컨테이너를 볼륨 마운터로 사용할 수 있다.
  • MountPropagation: 한 컨테이너에서 다른 컨테이너 또는 파드로 마운트된 볼륨을 공유할 수 있다. 자세한 내용은 마운트 전파(propagation)을 참고한다.
  • NamespaceDefaultLabelName: API 서버로 하여금 모든 네임스페이스에 대해 변경할 수 없는 (immutable) 레이블 kubernetes.io/metadata.name을 설정하도록 한다. (네임스페이스의 이름도 변경 불가)
  • NetworkPolicyEndPort: 네트워크폴리시(NetworkPolicy) 오브젝트에서 단일 포트를 지정하는 것 대신에 포트 범위를 지정할 수 있도록, endPort 필드의 사용을 활성화한다.
  • NodeDisruptionExclusion: 영역(zone) 장애 시 노드가 제외되지 않도록 노드 레이블 node.kubernetes.io/exclude-disruption 사용을 활성화한다.
  • NodeLease: 새로운 리스(Lease) API가 노드 상태 신호로 사용될 수 있는 노드 하트비트(heartbeats)를 보고할 수 있게 한다.
  • NodeSwap: 노드의 쿠버네티스 워크로드용 스왑 메모리를 할당하려면 kubelet을 활성화한다. 반드시 KubeletConfiguration.failSwapOn를 false로 설정한 후 사용해야 한다. 더 자세한 정보는 스왑 메모리를 참고한다.
  • NonPreemptingPriority: 프라이어리티클래스(PriorityClass)와 파드에 preemptionPolicy 필드를 활성화한다.
  • PVCProtection: 파드에서 사용 중일 때 퍼시스턴트볼륨클레임(PVC)이 삭제되지 않도록 한다.
  • PodDeletionCost: 레플리카셋 다운스케일 시 삭제될 파드의 우선순위를 사용자가 조절할 수 있도록, 파드 삭제 비용 기능을 활성화한다.
  • PersistentLocalVolumes: 파드에서 local 볼륨 유형의 사용을 활성화한다. local 볼륨을 요청하는 경우 파드 어피니티를 지정해야 한다.
  • PodDisruptionBudget: PodDisruptionBudget 기능을 활성화한다.
  • PodAffinityNamespaceSelector: 파드 어피니티 네임스페이스 셀렉터 기능과 CrossNamespacePodAffinity 쿼터 범위 기능을 활성화한다.
  • PodOverhead: 파드 오버헤드를 판단하기 위해 파드오버헤드(PodOverhead) 기능을 활성화한다.
  • PodPriority: 우선 순위를 기반으로 파드의 스케줄링 취소와 선점을 활성화한다.
  • PodReadinessGates: 파드 준비성 평가를 확장하기 위해 PodReadinessGate 필드 설정을 활성화한다. 자세한 내용은 파드의 준비성 게이트를 참고한다.
  • PodSecurity: PodSecurity 어드미션 플러그인을 사용하도록 설정한다.
  • PodShareProcessNamespace: 파드에서 실행되는 컨테이너 간에 단일 프로세스 네임스페이스를 공유하기 위해 파드에서 shareProcessNamespace 설정을 활성화한다. 자세한 내용은 파드의 컨테이너 간 프로세스 네임스페이스 공유에서 확인할 수 있다.
  • PreferNominatedNode: 이 플래그는 클러스터에 존재하는 다른 노드를 반복해서 검사하기 전에 지정된 노드를 먼저 검사할지 여부를 스케줄러에 알려준다.
  • ProbeTerminationGracePeriod: 파드의 프로브-수준 terminationGracePeriodSeconds 설정하기 기능을 활성화한다. 더 자세한 사항은 기능개선 제안을 참고한다.
  • ProcMountType: SecurityContext의 procMount 필드를 설정하여 컨테이너의 proc 타입의 마운트를 제어할 수 있다.
  • ProxyTerminatingEndpoints: ExternalTrafficPolicy=Local일 때 종료 엔드포인트를 처리하도록 kube-proxy를 활성화한다.
  • QOSReserved: QoS 수준에서 리소스 예약을 허용하여 낮은 QoS 수준의 파드가 더 높은 QoS 수준에서 요청된 리소스로 파열되는 것을 방지한다 (현재 메모리만 해당).
  • ReadWriteOncePod: ReadWriteOncePod 퍼시스턴트 볼륨 엑세스 모드를 사용한다.
  • RemainingItemCount: API 서버가 청크(chunking) 목록 요청에 대한 응답에서 남은 항목 수를 표시하도록 허용한다.
  • RemoveSelfLink: ObjectMeta 및 ListMeta에서 selfLink 를 사용하지 않고 제거한다.
  • RequestManagement: 각 API 서버에서 우선 순위 및 공정성으로 요청 동시성을 관리할 수 있다. 1.17 이후 APIPriorityAndFairness 에서 사용 중단되었다.
  • ResourceLimitsPriorityFunction: 입력 파드의 CPU 및 메모리 한도 중 하나 이상을 만족하는 노드에 가능한 최저 점수 1을 할당하는 스케줄러 우선 순위 기능을 활성화한다. 의도는 동일한 점수를 가진 노드 사이의 관계를 끊는 것이다.
  • ResourceQuotaScopeSelectors: 리소스 쿼터 범위 셀렉터를 활성화한다.
  • RootCAConfigMap: 모든 네임스페이스에 kube-root-ca.crt라는 컨피그맵을 게시하도록 kube-controller-manager 를 구성한다. 이 컨피그맵에는 kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들이 포함되어 있다. 자세한 내용은 바운드 서비스 계정 토큰을 참조한다.
  • RotateKubeletClientCertificate: kubelet에서 클라이언트 TLS 인증서의 로테이션을 활성화한다. 자세한 내용은 kubelet 구성을 참고한다.
  • RotateKubeletServerCertificate: kubelet에서 서버 TLS 인증서의 로테이션을 활성화한다. 자세한 사항은 kubelet 구성을 확인한다.
  • RunAsGroup: 컨테이너의 init 프로세스에 설정된 기본 그룹 ID 제어를 활성화한다.
  • RuntimeClass: 컨테이너 런타임 구성을 선택하기 위해 런타임클래스(RuntimeClass) 기능을 활성화한다.
  • ScheduleDaemonSetPods: 데몬셋(DaemonSet) 컨트롤러 대신 기본 스케줄러로 데몬셋 파드를 스케줄링할 수 있다.
  • SCTPSupport: 파드, 서비스, 엔드포인트, 엔드포인트슬라이스 및 네트워크폴리시 정의에서 SCTP protocol 값을 활성화한다.
  • SeccompDefault: 모든 워크로드의 기본 구분 프로파일로 RuntimeDefault을 사용한다. seccomp 프로파일은 파드 및 컨테이너 securityContext에 지정되어 있다.
  • SelectorIndex: API 서버 감시(watch) 캐시의 레이블 및 필드 기반 인덱스를 사용하여 목록 작업을 가속화할 수 있다.
  • ServerSideApply: API 서버에서 SSA(Sever Side Apply) 경로를 활성화한다.
  • ServiceAccountIssuerDiscovery: API 서버에서 서비스 어카운트 발행자에 대해 OIDC 디스커버리 엔드포인트(발급자 및 JWKS URL)를 활성화한다. 자세한 내용은 파드의 서비스 어카운트 구성을 참고한다.
  • ServiceAppProtocol: 서비스와 엔드포인트에서 appProtocol 필드를 활성화한다.
  • ServiceInternalTrafficPolicy: 서비스에서 internalTrafficPolicy 필드를 활성화한다.
  • ServiceLBNodePortControl: 서비스에서 allocateLoadBalancerNodePorts 필드를 활성화한다.
  • ServiceLoadBalancerClass: 서비스에서 loadBalancerClass 필드를 활성화한다. 자세한 내용은 로드밸런서 구현체의 종류 확인하기를 참고한다.
  • ServiceLoadBalancerFinalizer: 서비스 로드 밸런서에 대한 Finalizer 보호를 활성화한다.
  • ServiceNodeExclusion: 클라우드 제공자가 생성한 로드 밸런서에서 노드를 제외할 수 있다. "node.kubernetes.io/exclude-from-external-load-balancers"로 레이블이 지정된 경우 노드를 제외할 수 있다.
  • ServiceTopology: 서비스가 클러스터의 노드 토폴로지를 기반으로 트래픽을 라우팅할 수 있도록 한다. 자세한 내용은 서비스토폴로지(ServiceTopology)를 참고한다.
  • SetHostnameAsFQDN: 전체 주소 도메인 이름(FQDN)을 파드의 호스트 이름으로 설정하는 기능을 활성화한다. 파드의 setHostnameAsFQDN 필드를 참고한다.
  • SizeMemoryBackedVolumes: memory-backed 볼륨(보통 emptyDir 볼륨)의 크기 상한을 지정할 수 있도록 kubelets를 활성화한다.
  • StartupProbe: kubelet에서 스타트업 프로브를 활성화한다.
  • StatefulSetMinReadySeconds: 스테이트풀셋 컨트롤러가 minReadySeconds를 반영할 수 있다.
  • StorageObjectInUseProtection: 퍼시스턴트볼륨 또는 퍼시스턴트볼륨클레임 오브젝트가 여전히 사용 중인 경우 삭제를 연기한다.
  • StorageVersionAPI: 스토리지 버전 API를 활성화한다.
  • StorageVersionHash: API 서버가 디스커버리에서 스토리지 버전 해시를 노출하도록 허용한다.
  • StreamingProxyRedirects: 스트리밍 요청을 위해 백엔드(kubelet)에서 리디렉션을 가로채서 따르도록 API 서버에 지시한다. 스트리밍 요청의 예로는 exec, attachport-forward 요청이 있다.
  • SupportIPVSProxyMode: IPVS를 사용하여 클러스터 내 서비스 로드 밸런싱을 제공한다. 자세한 내용은 서비스 프록시를 참고한다.
  • SupportNodePidsLimit: 노드에서 PID 제한 지원을 활성화한다. --system-reserved--kube-reserved 옵션의 pid=<number> 파라미터를 지정하여 지정된 수의 프로세스 ID가 시스템 전체와 각각 쿠버네티스 시스템 데몬에 대해 예약되도록 할 수 있다.
  • SupportPodPidsLimit: 파드의 PID 제한에 대한 지원을 활성화한다.
  • SuspendJob: 잡 중지/재시작 기능을 활성화한다. 자세한 내용은 잡 문서를 참고한다.
  • Sysctls: 각 파드에 설정할 수 있는 네임스페이스 커널 파라미터(sysctl)를 지원한다. 자세한 내용은 sysctl을 참고한다.
  • TTLAfterFinished: TTL 컨트롤러가 실행이 끝난 후 리소스를 정리하도록 허용한다.
  • TaintBasedEvictions: 노드의 테인트(taint) 및 파드의 톨러레이션(toleration)을 기반으로 노드에서 파드를 축출할 수 있다. 자세한 내용은 테인트와 톨러레이션을 참고한다.
  • TaintNodesByCondition: 노드 컨디션을 기반으로 자동 테인트 노드를 활성화한다.
  • TokenRequest: 서비스 어카운트 리소스에서 TokenRequest 엔드포인트를 활성화한다.
  • TokenRequestProjection: projected 볼륨을 통해 서비스 어카운트 토큰을 파드에 주입할 수 있다.
  • TopologyAwareHints: 엔드포인트슬라이스(EndpointSlices)에서 토폴로지 힌트 기반 토폴로지-어웨어 라우팅을 활성화한다. 자세한 내용은 토폴로지 어웨어 힌트 를 참고한다.
  • TopologyManager: 쿠버네티스의 다른 컴포넌트에 대한 세분화된 하드웨어 리소스 할당을 조정하는 메커니즘을 활성화한다. 노드의 토폴로지 관리 정책 제어를 참고한다.
  • ValidateProxyRedirects: 이 플래그는 API 서버가 동일한 호스트로만 리디렉션되는가를 확인해야 하는지 여부를 제어한다. StreamingProxyRedirects 플래그가 활성화된 경우에만 사용된다.
  • VolumeCapacityPriority: 가용 PV 용량을 기반으로 여러 토폴로지에 있는 노드들의 우선순위를 정하는 기능을 활성화한다.
  • VolumePVCDataSource: 기존 PVC를 데이터 소스로 지정하는 기능을 지원한다.
  • VolumeScheduling: 볼륨 토폴로지 인식 스케줄링을 활성화하고 퍼시스턴트볼륨클레임(PVC) 바인딩이 스케줄링 결정을 인식하도록 한다. 또한 PersistentLocalVolumes 기능 게이트와 함께 사용될 때 local 볼륨 유형을 사용할 수 있다.
  • VolumeSnapshotDataSource: 볼륨 스냅샷 데이터 소스 지원을 활성화한다.
  • VolumeSubpath: 컨테이너에 볼륨의 하위 경로(subpath)를 마운트할 수 있다.
  • VolumeSubpathEnvExpansion: 환경 변수를 subPath로 확장하기 위해 subPathExpr 필드를 활성화한다.
  • WarningHeaders: API 응답에서 경고 헤더를 보낼 수 있다.
  • WatchBookmark: 감시자 북마크(watch bookmark) 이벤트 지원을 활성화한다.
  • WinDSR: kube-proxy가 윈도우용 DSR 로드 밸런서를 생성할 수 있다.
  • WinOverlay: kube-proxy가 윈도우용 오버레이 모드에서 실행될 수 있도록 한다.
  • WindowsEndpointSliceProxying: 활성화되면, 윈도우에서 실행되는 kube-proxy는 엔드포인트 대신 엔드포인트슬라이스를 기본 데이터 소스로 사용하여 확장성과 성능을 향상시킨다. 엔드포인트슬라이스 활성화하기를 참고한다.
  • WindowsGMSA: 파드에서 컨테이너 런타임으로 GMSA 자격 증명 스펙을 전달할 수 있다.
  • WindowsHostProcessContainers: 윈도우 HostProcess 컨테이너에 대한 지원을 사용하도록 설정한다.
  • WindowsRunAsUserName : 기본 사용자가 아닌(non-default) 사용자로 윈도우 컨테이너에서 애플리케이션을 실행할 수 있도록 지원한다. 자세한 내용은 RunAsUserName 구성을 참고한다.

다음 내용

  • 사용 중단 정책은 쿠버네티스에 대한 기능과 컴포넌트를 제거하는 프로젝트의 접근 방법을 설명한다.

6.9.2 - kube-proxy

시놉시스

쿠버네티스 네트워크 프록시는 각 노드에서 실행된다. 이는 각 노드의 쿠버네티스 API에 정의된 서비스를 반영하며 단순한 TCP, UDP 및 SCTP 스트림 포워딩 또는 라운드 로빈 TCP, UDP 및 SCTP 포워딩을 백엔드 셋에서 수행 할 수 있다. 서비스 클러스트 IP 및 포트는 현재 서비스 프록시에 의해 열린 포트를 지정하는 Docker-links-compatible 환경 변수를 통해 찾을 수 있다. 이러한 클러스터 IP에 클러스터 DNS를 제공하는 선택적 애드온이 있다. 유저는 apiserver API로 서비스를 생성하여 프록시를 구성해야 한다.

kube-proxy [flags]

옵션

--add-dir-header

true로 되어 있으면, 로그 메시지의 헤더에 파일 디렉터리를 기재한다.

--alsologtostderr

로그를 파일뿐만 아니라 표준 에러(standard error)로도 출력한다.

--azure-container-registry-config string

Azure 컨테이너 레지스트리 구성 정보가 들어 있는 파일의 경로.

--bind-address string     기본값: 0.0.0.0

프록시 서버가 서비스할 IP 주소(모든 IPv4 인터페이스의 경우 '0.0.0.0'으로 설정, 모든 IPv6 인터페이스의 경우 '::'로 설정)

--bind-address-hard-fail

true인 경우 kube-proxy는 포트 바인딩 실패를 치명적인 것으로 간주하고 종료한다.

--boot-id-file string     기본값: "/proc/sys/kernel/random/boot_id"

boot-id를 위해 확인할 파일 목록(쉼표로 분리). 가장 먼저 발견되는 항목을 사용한다.

--cleanup

true인 경우 iptables 및 ipvs 규칙을 제거하고 종료한다.

--cloud-provider-gce-l7lb-src-cidrs cidrs     기본값: 130.211.0.0/22,35.191.0.0/16

GCE 방화벽에서, L7 로드밸런싱 트래픽 프록시와 헬스 체크를 위해 개방할 CIDR 목록

--cloud-provider-gce-lb-src-cidrs cidrs     기본값: 130.211.0.0/22,209.85.152.0/22,209.85.204.0/22,35.191.0.0/16

GCE 방화벽에서, L4 로드밸런싱 트래픽 프록시와 헬스 체크를 위해 개방할 CIDR 목록

--cluster-cidr string

클러스터에 있는 파드의 CIDR 범위. 구성 후에는 이 범위 밖에서 서비스 클러스터 IP로 전송되는 트래픽은 마스커레이드되고 파드에서 외부 LoadBalancer IP로 전송된 트래픽은 대신 해당 클러스터 IP로 전송된다

--config string

설정 파일의 경로.

--config-sync-period duration     기본값: 15m0s

apiserver의 설정이 갱신되는 빈도. 0보다 커야 한다.

--conntrack-max-per-core int32     기본값: 32768

CPU 코어당 추적할 최대 NAT 연결 수(한도(limit)를 그대로 두고 contrack-min을 무시하려면 0으로 설정한다)(

--conntrack-min int32     기본값: 131072

conntrack-max-per-core와 관계없이 할당할 최소 conntrack 항목 수(한도를 그대로 두려면 conntrack-max-per-core값을 0으로 설정).

--conntrack-tcp-timeout-close-wait duration     기본값: 1h0m0s

CLOSE_WAIT 상태의 TCP 연결에 대한 NAT 시간 초과

--conntrack-tcp-timeout-established duration     기본값: 24h0m0s

설정된 TCP 연결에 대한 유휴시간 초과(값이 0이면 그대로 유지)

--default-not-ready-toleration-seconds int     기본값: 300

notReady:NoExecute 상태에 대한 톨러레이션(toleration) 시간이 지정되지 않은 모든 파드에 기본값으로 지정될 톨러레이션 시간(단위: 초)

--default-unreachable-toleration-seconds int     기본값: 300

unreachable:NoExecute 상태에 대한 톨러레이션 시간이 지정되지 않은 모든 파드에 기본값으로 지정될 톨러레이션 시간(단위: 초)

--detect-local-mode LocalMode

로컬 트래픽을 탐지하는 데 사용할 모드

--feature-gates mapStringBool

알파/실험 기능에 대한 기능 게이트를 설명하는 키=값 쌍 집합. 옵션은 다음과 같다.
APIListChunking=true|false (BETA - 기본값=true)
APIPriorityAndFairness=true|false (BETA - 기본값=true)
APIResponseCompression=true|false (BETA - 기본값=true)
APIServerIdentity=true|false (ALPHA - 기본값=false)
APIServerTracing=true|false (ALPHA - 기본값=false)
AllAlpha=true|false (ALPHA - 기본값=false)
AllBeta=true|false (BETA - 기본값=false)
AnyVolumeDataSource=true|false (ALPHA - 기본값=false)
AppArmor=true|false (BETA - 기본값=true)
CPUManager=true|false (BETA - 기본값=true)
CPUManagerPolicyOptions=true|false (ALPHA - 기본값=false)
CSIInlineVolume=true|false (BETA - 기본값=true)
CSIMigration=true|false (BETA - 기본값=true)
CSIMigrationAWS=true|false (BETA - 기본값=false)
CSIMigrationAzureDisk=true|false (BETA - 기본값=false)
CSIMigrationAzureFile=true|false (BETA - 기본값=false)
CSIMigrationGCE=true|false (BETA - 기본값=false)
CSIMigrationOpenStack=true|false (BETA - 기본값=true)
CSIMigrationvSphere=true|false (BETA - 기본값=false)
CSIStorageCapacity=true|false (BETA - 기본값=true)
CSIVolumeFSGroupPolicy=true|false (BETA - 기본값=true)
CSIVolumeHealth=true|false (ALPHA - 기본값=false)
CSRDuration=true|false (BETA - 기본값=true)
ConfigurableFSGroupPolicy=true|false (BETA - 기본값=true)
ControllerManagerLeaderMigration=true|false (BETA - 기본값=true)
CustomCPUCFSQuotaPeriod=true|false (ALPHA - 기본값=false)
DaemonSetUpdateSurge=true|false (BETA - 기본값=true)
DefaultPodTopologySpread=true|false (BETA - 기본값=true)
DelegateFSGroupToCSIDriver=true|false (ALPHA - 기본값=false)
DevicePlugins=true|false (BETA - 기본값=true)
DisableAcceleratorUsageMetrics=true|false (BETA - 기본값=true)
DisableCloudProviders=true|false (ALPHA - 기본값=false)
DownwardAPIHugePages=true|false (BETA - 기본값=false)
EfficientWatchResumption=true|false (BETA - 기본값=true)
EndpointSliceTerminatingCondition=true|false (BETA - 기본값=true)
EphemeralContainers=true|false (ALPHA - 기본값=false)
ExpandCSIVolumes=true|false (BETA - 기본값=true)
ExpandInUsePersistentVolumes=true|false (BETA - 기본값=true)
ExpandPersistentVolumes=true|false (BETA - 기본값=true)
ExpandedDNSConfig=true|false (ALPHA - 기본값=false)
ExperimentalHostUserNamespaceDefaulting=true|false (BETA - 기본값=false)
GenericEphemeralVolume=true|false (BETA - 기본값=true)
GracefulNodeShutdown=true|false (BETA - 기본값=true)
HPAContainerMetrics=true|false (ALPHA - 기본값=false)
HPAScaleToZero=true|false (ALPHA - 기본값=false)
IPv6DualStack=true|false (BETA - 기본값=true)
InTreePluginAWSUnregister=true|false (ALPHA - 기본값=false)
InTreePluginAzureDiskUnregister=true|false (ALPHA - 기본값=false)
InTreePluginAzureFileUnregister=true|false (ALPHA - 기본값=false)
InTreePluginGCEUnregister=true|false (ALPHA - 기본값=false)
InTreePluginOpenStackUnregister=true|false (ALPHA - 기본값=false)
InTreePluginvSphereUnregister=true|false (ALPHA - 기본값=false)
IndexedJob=true|false (BETA - 기본값=true)
IngressClassNamespacedParams=true|false (BETA - 기본값=true)
JobTrackingWithFinalizers=true|false (ALPHA - 기본값=false)
KubeletCredentialProviders=true|false (ALPHA - 기본값=false)
KubeletInUserNamespace=true|false (ALPHA - 기본값=false)
KubeletPodResources=true|false (BETA - 기본값=true)
KubeletPodResourcesGetAllocatable=true|false (ALPHA - 기본값=false)
LocalStorageCapacityIsolation=true|false (BETA - 기본값=true)
LocalStorageCapacityIsolationFSQuotaMonitoring=true|false (ALPHA - 기본값=false)
LogarithmicScaleDown=true|false (BETA - 기본값=true)
MemoryManager=true|false (BETA - 기본값=true)
MemoryQoS=true|false (ALPHA - 기본값=false)
MixedProtocolLBService=true|false (ALPHA - 기본값=false)
NetworkPolicyEndPort=true|false (BETA - 기본값=true)
NodeSwap=true|false (ALPHA - 기본값=false)
NonPreemptingPriority=true|false (BETA - 기본값=true)
PodAffinityNamespaceSelector=true|false (BETA - 기본값=true)
PodDeletionCost=true|false (BETA - 기본값=true)
PodOverhead=true|false (BETA - 기본값=true)
PodSecurity=true|false (ALPHA - 기본값=false)
PreferNominatedNode=true|false (BETA - 기본값=true)
ProbeTerminationGracePeriod=true|false (BETA - 기본값=false)
ProcMountType=true|false (ALPHA - 기본값=false)
ProxyTerminatingEndpoints=true|false (ALPHA - 기본값=false)
QOSReserved=true|false (ALPHA - 기본값=false)
ReadWriteOncePod=true|false (ALPHA - 기본값=false)
RemainingItemCount=true|false (BETA - 기본값=true)
RemoveSelfLink=true|false (BETA - 기본값=true)
RotateKubeletServerCertificate=true|false (BETA - 기본값=true)
Seccomp기본값=true|false (ALPHA - 기본값=false)
ServiceInternalTrafficPolicy=true|false (BETA - 기본값=true)
ServiceLBNodePortControl=true|false (BETA - 기본값=true)
ServiceLoadBalancerClass=true|false (BETA - 기본값=true)
SizeMemoryBackedVolumes=true|false (BETA - 기본값=true)
StatefulSetMinReadySeconds=true|false (ALPHA - 기본값=false)
StorageVersionAPI=true|false (ALPHA - 기본값=false)
StorageVersionHash=true|false (BETA - 기본값=true)
SuspendJob=true|false (BETA - 기본값=true)
TTLAfterFinished=true|false (BETA - 기본값=true)
TopologyAwareHints=true|false (ALPHA - 기본값=false)
TopologyManager=true|false (BETA - 기본값=true)
VolumeCapacityPriority=true|false (ALPHA - 기본값=false)
WinDSR=true|false (ALPHA - 기본값=false)
WinOverlay=true|false (BETA - 기본값=true)
WindowsHostProcessContainers=true|false (ALPHA - 기본값=false)

--healthz-bind-address ipport     기본값: 0.0.0.0:10256

헬스 체크 서버가 서비스할 포트가 있는 IP 주소(모든 IPv4의 인터페이스의 경우 '0.0.0.0:10256', 모든 IPv6의 인터페이스인 경우 '[::]:10256'로 설정). 사용 안 할 경우 빈칸으로 둠.

-h, --help

kube-proxy에 대한 도움말.

--hostname-override string

문자열 값이 있으면, 이 값을 실제 호스트네임 대신에 ID로 사용한다.

--iptables-masquerade-bit int32     기본값: 14

순수 iptable 프록시를 사용하는 경우 SNAT가 필요한 패킷을 표시하는 fwmark 스페이스 비트. [0, 31] 범위 안에 있어야 한다.

--iptables-min-sync-period duration     기본값: 1s

엔드포인트 및 서비스가 변경될 때 iptable 규칙을 새로 고칠 수 있는 빈도의 최소 간격(예: '5s', '1m', '2h22m').

--iptables-sync-period duration     기본값: 30s

iptable 규칙을 새로 고치는 빈도의 최대 간격(예: '5s', '1m', '2h22m'). 0 보다 커야 한다.

--ipvs-exclude-cidrs stringSlice

IPVS 규칙을 정리할 때 ipvs 프록시가 건드리지 않아야 하는 쉼표로 구분된 CIDR 목록.

--ipvs-min-sync-period duration

엔드포인트 및 서비스가 변경될 때 ipvs 규칙을 새로 고칠 수 있는 빈도의 최소 간격(예: '5s', '1m', '2h22m').

--ipvs-scheduler string

프록시 모드가 ipvs인 경우 ipvs 스케줄러 유형.

--ipvs-strict-arp

arp_ignore를 1로 설정하고 arp_annotes를 2로 설정하여 엄격한 ARP를 사용.

--ipvs-sync-period duration     기본값: 30s

ipvs 규칙이 새로 갱신되는 빈도의 최대 간격(예: '5s', '1m', '2h22m'). 0 보다 커야 한다.

--ipvs-tcp-timeout duration

유휴 IPVS TCP 연결에 대한 시간 초과. 0이면 그대로 유지(예: '5s', '1m', '2h22m').

--ipvs-tcpfin-timeout duration

FIN 패킷을 수신한 후 IPVS TCP 연결에 대한 시간 초과. 0이면 그대로 유지(예: '5s', '1m', '2h22m').

--ipvs-udp-timeout duration

IPVS UDP 패킷에 대한 시간 초과. 0이면 그대로 유지(예: '5s', '1m', '2h22m').

--kube-api-burst int32     기본값: 10

쿠버네티스 api 서버와 통신하는 동안 사용할 burst.

--kube-api-content-type string     기본값: "application/vnd.kubernetes.protobuf"

api 서버에 보낸 요청의 내용 유형.

--kube-api-qps float32     기본값: 5

쿠버네티스 api 서버와 통신할 때 사용할 QPS.

--kubeconfig string

인증 정보가 있는 kubeconfig 파일의 경로(마스터 위치는 마스터 플래그로 설정됨).

--log-backtrace-at <'file:N' 형태의 문자열>     기본값: :0

로깅 과정에서 file:N 번째 라인에 도달하면 스택 트레이스를 출력한다.

--log-dir string

로그 파일이 저장될 디렉터리

--log-file string

사용할 로그 파일

--log-file-max-size uint     기본값: 1800

로그 파일의 최대 크기(단위: MB). 0으로 설정하면 무제한이다.

--log-flush-frequency duration     기본값: 5s

로그 플러시 사이의 최대 시간

--logtostderr     기본값: true

로그를 파일에 기록하지 않고 표준 에러로만 출력

--machine-id-file string     기본값: "/etc/machine-id,/var/lib/dbus/machine-id"

machine-id를 위해 확인할 파일 목록(쉼표로 분리). 가장 먼저 발견되는 항목을 사용한다.

--masquerade-all

순수 iptables 프록시를 사용하는 경우 서비스 클러스터 IP를 통해 전송된 모든 트래픽을 SNAT함(일반적으로 필요하지 않음).

--master string

쿠버네티스 API 서버의 주소(kubeconfig의 모든 값 덮어쓰기).

--metrics-bind-address ipport     기본값: 127.0.0.1:10249

메트릭 서버가 서비스할 포트가 있는 IP 주소(모든 IPv4 인터페이스의 경우 '0.0.0.0:10249', 모든 IPv6 인터페이스의 경우 '[::]:10249'로 설정됨). 사용하지 않으려면 비워둘 것.

--nodeport-addresses stringSlice

NodePort에 사용할 주소를 지정하는 값의 문자열 조각. 값은 유효한 IP 블록(예: 1.2.3.0/24, 1.2.3.4/32). 기본값인 빈 문자열 조각값은([]) 모든 로컬 주소를 사용하는 것을 의미한다.

--one-output

true이면, 해당 로그가 속하는 심각성 레벨에만 각 로그를 기록한다(원래는 하위 심각성 레벨에도 기록한다).

--oom-score-adj int32     기본값: -999

kube-proxy 프로세스에 대한 oom-score-adj 값. 값은 [-1000, 1000] 범위 내에 있어야 한다.

--profiling

값이 true이면 /debug/pprof 핸들러에서 웹 인터페이스를 통한 프로파일링을 활성화한다.

--proxy-mode ProxyMode

사용할 프록시 모드: 'userspace' (이전) or 'iptables' (빠름) or 'ipvs' or 'kernelspace' (윈도우). 공백인 경우 가장 잘 사용할 수 있는 프록시(현재는 iptables)를 사용한다. iptables 프록시를 선택했지만, 시스템의 커널 또는 iptables 버전이 맞지 않으면, 항상 userspace 프록시로 변경된다.

--proxy-port-range port-range

서비스 트래픽을 프록시하기 위해 사용할 수 있는 호스트 포트 범위(beginPort-endPort, single port 또는 beginPort+offset 포함). 만약 범위가 0, 0-0, 혹은 지정되지 않으면, 포트는 무작위로 선택된다.

--show-hidden-metrics-for-version string

숨겨진 메트릭을 표시하려는 이전 버전. 이전 마이너 버전만 인식하며, 다른 값은 허용하지 않는다. 포멧은 <메이저>.<마이너> 와 같으며, 예를 들면 '1.16' 과 같다. 이 포멧의 목적은, 다음 릴리스가 숨길 추가적인 메트릭을 사용자에게 공지하여, 그 이후 릴리스에서 메트릭이 영구적으로 삭제됐을 때 사용자가 놀라지 않도록 하기 위함이다.

--skip-headers

true이면, 로그 메시지에서 헤더 접두사를 붙이지 않는다.

--skip-log-headers

true이면, 로그 파일을 열 때 헤더를 붙이지 않는다.

--stderrthreshold int     기본값: 2

이 값 이상의 로그는 표준 에러(stderr)로 출력되도록 한다.

--udp-timeout duration     기본값: 250ms

유휴 UDP 연결이 열린 상태로 유지되는 시간(예: '250ms', '2s'). 값은 0보다 커야 한다. 프록시 모드 userspace에만 적용 가능함.

-v, --v int

로그 상세 레벨(verbosity)

--version version[=true]

버전 정보를 출력하고 종료

--vmodule <쉼표로 구분된 'pattern=N' 설정>

파일-필터된 로깅을 위한 'pattern=N' 설정들(쉼표로 구분됨)

--write-config-to string

기본 구성 값을 이 파일에 옮겨쓰고 종료한다.

6.9.3 - Kubelet 인증/인가

개요

kubelet의 HTTPS 엔드포인트는 다양한 민감도의 데이터에 대한 접근을 제공하는 API를 노출하며, 노드와 컨테이너 내에서 다양한 수준의 권한으로 작업을 수행할 수 있도록 허용한다.

이 문서는 kubelet의 HTTPS 엔드포인트에 대한 접근을 인증하고 인가하는 방법을 설명한다.

Kubelet 인증

기본적으로, 다른 구성의 인증 방법에 의해 거부되지 않은 kubelet의 HTTPS 엔드포인트에 대한 요청은 익명의 요청으로 처리되며, system:anonymous의 사용자 이름과 system:unauthenticated 의 그룹이 부여된다.

익명의 접근을 비활성화하고 인증되지 않은 요청에 401 Unauthorized 응답을 보내려면 아래를 참고한다.

  • --anonymous-auth=false 플래그로 kubelet을 시작

kubelet의 HTTPS 엔드포인트에 대한 X509 클라이언트 인증서 인증을 활성화하려면 아래를 참고한다.

  • --client-ca-file 플래그로 kubelet을 시작하면 클라이언트 인증서를 확인할 수 있는 CA 번들을 제공
  • --kubelet-client-certificate--kubelet-client-key 플래그로 apiserver를 시작
  • 자세한 내용은 apiserver 인증 문서를 참고

API bearer 토큰(서비스 계정 토큰 포함)을 kubelet의 HTTPS 엔드포인트 인증에 사용하려면 아래를 참고한다.

  • API 서버에서 authentication.k8s.io/v1beta1 API 그룹이 사용 가능한지 확인
  • --authentication-token-webhook--kubeconfig 플래그로 kubelet을 시작
  • kubelet은 구성된 API 서버의 TokenReview API를 호출하여 bearer 토큰에서 사용자 정보를 결정

Kubelet 승인

성공적으로 인증된 모든 요청(익명 요청 포함)이 승인된다. 기본 인가 모드는 모든 요청을 허용하는 AlwaysAllow 이다.

kubelet API에 대한 접근을 세분화하는 데는 다양한 이유가 있다.

  • 익명 인증을 사용할 수 있지만, 익명 사용자의 kubelet API 호출 기능은 제한되어야 함
  • bearer 토큰 인증을 사용할 수 있지만, 임의의 API 사용자(API 계정)의 kubelet API 호출 기능은 제한되어야 함
  • 클라이언트 인증을 사용할 수 있지만, 구성된 CA에서 서명한 일부 클라이언트 인증서만 kubelet API를 사용하도록 허용해야 함

kubelet API에 대한 접근을 세분화하려면 API 서버에 권한을 위임한다.

  • authorization.k8s.io/v1beta1 API 그룹이 API 서버에서 사용 가능한지 확인
  • --authorization-mode=Webhook--kubeconfig 플래그로 kubelet을 시작
  • kubelet은 구성된 API 서버의 SubjectAccessReview API를 호출하여 각각의 요청이 승인되었는지 여부를 확인

kubelet은 API 요청을 apiserver와 동일한 요청 속성 접근 방식을 사용하여 승인한다.

동사는 들어오는 요청의 HTTP 동사로부터 결정된다.

HTTP 동사 요청 동사
POST create
GET, HEAD get
PUT update
PATCH patch
DELETE delete

리소스 및 하위 리소스는 들어오는 요청의 경로로부터 결정된다.

Kubelet API 리소스 하위 리소스
/stats/* nodes stats
/metrics/* nodes metrics
/logs/* nodes log
/spec/* nodes spec
all others nodes proxy

네임스페이스와 API 그룹 속성은 항상 빈 문자열이며, 리소스 이름은 항상 kubelet의 Node API 오브젝트 이름이다.

이 모드로 실행할 때, --kubelet-client-certificate--kubelet-client-key 플래그로 식별된 사용자에게 다음 속성에 대한 권한이 있는지 확인한다.

  • verb=*, resource=nodes, subresource=proxy
  • verb=*, resource=nodes, subresource=stats
  • verb=*, resource=nodes, subresource=log
  • verb=*, resource=nodes, subresource=spec
  • verb=*, resource=nodes, subresource=metrics

6.10 - 스케줄링

6.10.1 - 스케줄링 정책

스케줄링 정책을 사용하여 kube-scheduler가 각각 노드를 필터링하고 스코어링(scoring)하기 위해 실행하는 단정(predicates)우선순위(priorities) 를 지정할 수 있다.

kube-scheduler --policy-config-file <filename> 또는 kube-scheduler --policy-configmap <ConfigMap>을 실행하고 정책 유형을 사용하여 스케줄링 정책을 설정할 수 있다.

단정

다음의 단정 은 필터링을 구현한다.

  • PodFitsHostPorts: 파드가 요청하는 파드의 포트에 대해 노드에 사용할 수 있는 포트(네트워크 프로토콜 종류)가 있는지 확인한다.

  • PodFitsHost: 파드가 호스트 이름으로 특정 노드를 지정하는지 확인한다.

  • PodFitsResources: 파드의 요구 사항을 충족할 만큼 노드에 사용할 수 있는 리소스(예: CPU 및 메모리)가 있는지 확인한다.

  • MatchNodeSelector: 파드의 노드 셀렉터가 노드의 레이블과 일치하는지 확인한다.

  • NoVolumeZoneConflict: 해당 스토리지에 대한 장애 영역 제한이 주어지면 파드가 요청하는 볼륨을 노드에서 사용할 수 있는지 평가한다.

  • NoDiskConflict: 요청하는 볼륨과 이미 마운트된 볼륨으로 인해 파드가 노드에 적합한지 평가한다.

  • MaxCSIVolumeCount: 연결해야 하는 CSI 볼륨의 수와 구성된 제한을 초과하는지 여부를 결정한다.

  • PodToleratesNodeTaints: 파드의 톨러레이션이 노드의 테인트를 용인할 수 있는지 확인한다.

  • CheckVolumeBinding: 파드가 요청한 볼륨에 적합할 수 있는지 평가한다. 이는 바인딩된 PVC와 바인딩되지 않은 PVC 모두에 적용된다.

우선순위

다음의 우선순위 는 스코어링을 구현한다.

  • SelectorSpreadPriority: 동일한 서비스, 스테이트풀셋(StatefulSet) 또는 레플리카셋(ReplicaSet)에 속하는 파드를 고려하여, 파드를 여러 호스트에 파드를 분산한다.

  • InterPodAffinityPriority: 선호된 파드간 어피니티와 안티-어피니티를 구현한다.

  • LeastRequestedPriority: 요청된 리소스가 적은 노드를 선호한다. 즉, 노드에 배치되는 파드가 많고, 해당 파드가 사용하는 리소스가 많을수록 이 정책이 부여하는 순위가 낮아진다.

  • MostRequestedPriority: 요청된 리소스가 가장 많은 노드를 선호한다. 이 정책은 전체 워크로드 세트를 실행하는 데 필요한 최소 노드 수에 스케줄된 파드를 맞춘다.

  • RequestedToCapacityRatioPriority: 기본 리소스 스코어링 기능을 사용하여 ResourceAllocationPriority에 기반한 requestedToCapacity를 생성한다.

  • BalancedResourceAllocation: 균형 잡힌 리소스 사용의 노드를 선호한다.

  • NodePreferAvoidPodsPriority: 노드 어노테이션 scheduler.alpha.kubernetes.io/preferAvoidPods에 따라 노드의 우선순위를 지정한다. 이를 사용하여 두 개의 다른 파드가 동일한 노드에서 실행되면 안된다는 힌트를 줄 수 있다.

  • NodeAffinityPriority: PreferredDuringSchedulingIgnoredDuringExecution에 표시된 노드 어피니티 스케줄링 설정에 따라 노드의 우선순위를 지정한다. 이에 대한 자세한 내용은 노드에 파드 할당하기에서 확인할 수 있다.

  • TaintTolerationPriority: 노드에서 용인할 수 없는 테인트 수를 기반으로, 모든 노드의 우선순위 목록을 준비한다. 이 정책은 해당 목록을 고려하여 노드의 순위를 조정한다.

  • ImageLocalityPriority: 해당 파드의 컨테이너 이미지가 이미 로컬로 캐시된 노드를 선호한다.

  • ServiceSpreadingPriority: 특정 서비스에 대해, 이 정책은 해당 서비스에 대한 파드가 서로 다른 노드에서 실행되는 것을 목표로 한다. 해당 서비스에 대한 파드가 이미 할당되지 않은 노드에 스케줄링하는 것을 선호한다. 전반적인 결과는 서비스가 단일 노드 장애에 대해 더 탄력적이라는 것이다.

  • EqualPriority: 모든 노드에 동일한 가중치를 부여한다.

  • EvenPodsSpreadPriority: 선호된 파드 토폴로지 분배 제약 조건을 구현한다.

다음 내용

6.10.2 - 스케줄러 구성

FEATURE STATE: Kubernetes v1.19 [beta]

구성 파일을 작성하고 해당 경로를 커맨드 라인 인수로 전달하여 kube-scheduler 의 동작을 사용자 정의할 수 있다.

스케줄링 프로파일(Profile)을 사용하면 kube-scheduler에서 여러 단계의 스케줄링을 구성할 수 있다. 각 단계는 익스텐션 포인트(extension point)를 통해 노출된다. 플러그인은 이러한 익스텐션 포인트 중 하나 이상을 구현하여 스케줄링 동작을 제공한다.

KubeSchedulerConfiguration (v1beta1 또는 v1beta2) 구조에 맞게 파일을 작성하고, kube-scheduler --config <filename>을 실행하여 스케줄링 프로파일을 지정할 수 있다.

최소 구성은 다음과 같다.

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: /etc/srv/kubernetes/kube-scheduler/kubeconfig

프로파일

스케줄링 프로파일을 사용하면 kube-scheduler에서 여러 단계의 스케줄링을 구성할 수 있다. 각 단계는 익스텐션 포인트에 노출된다. 플러그인은 이러한 익스텐션 포인트 중 하나 이상을 구현하여 스케줄링 동작을 제공한다.

kube-scheduler 의 단일 인스턴스를 구성하여 여러 프로파일을 실행할 수 있다.

익스텐션 포인트

스케줄링은 다음 익스텐션 포인트를 통해 노출되는 일련의 단계에서 발생한다.

  1. queueSort: 이 플러그인은 스케줄링 대기열에서 보류 중인 파드를 정렬하는 데 사용되는 정렬 기능을 제공한다. 대기열 정렬 플러그인은 한 번에 단 하나만 활성화될 수 있다. 사용할 수 있다.
  2. preFilter: 이 플러그인은 필터링하기 전에 파드 또는 클러스터에 대한 정보를 사전 처리하거나 확인하는 데 사용된다. 이 플러그인은 파드를 unschedulable로 표시할 수 있다.
  3. filter: 이 플러그인은 스케줄링 정책의 단정(Predicates)과 동일하며 파드를 실행할 수 없는 노드를 필터링하는 데 사용된다. 필터는 구성된 순서대로 호출된다. 노드가 모든 필터를 통과하지 않으면 파드는 unschedulable로 표시된다.
  4. postFilter: 이 플러그인은 파드의 실행 가능한 노드를 찾을 수 없을 때, 구성된 순서대로 호출된다. postFilter 플러그인이 파드 schedulable 을 표시하는 경우, 나머지 플러그인은 호출 되지 않는다.
  5. preScore: 이것은 사전 스코어링 작업을 수행하는 데 사용할 수 있는 정보성 익스텐션 포인트이다.
  6. score: 이 플러그인은 필터링 단계를 통과한 각 노드에 점수를 제공한다. 그런 다음 스케줄러는 가중치 합계가 가장 높은 노드를 선택한다.
  7. reserve: 지정된 파드에 리소스가 예약된 경우 플러그인에 알리는 정보성 익스텐션 포인트이다. 플러그인은 또한 Reserve 도중 또는 이후에 실패한 경우 호출 되는 Unreserve 호출을 구현한다.
  8. permit: 이 플러그인은 파드 바인딩을 방지하거나 지연시킬 수 있다.
  9. preBind: 이 플러그인은 파드가 바인딩되기 전에 필요한 모든 작업을 수행한다.
  10. bind: 플러그인은 파드를 노드에 바인딩한다. bind 플러그인은 순서대로 호출되며 일단 바인딩이 완료되면 나머지 플러그인은 건너뛴다. bind 플러그인은 적어도 하나 이상 필요하다.
  11. postBind: 파드가 바인드된 후 호출되는 정보성 익스텐션 포인트이다.

각 익스텐션 포인트에 대해 특정 기본 플러그인을 비활성화하거나 자체 플러그인을 활성화할 수 있다. 예를 들면, 다음과 같다.

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - plugins:
      score:
        disabled:
        - name: PodTopologySpread
        enabled:
        - name: MyCustomPluginA
          weight: 2
        - name: MyCustomPluginB
          weight: 1

비활성화된 배열의 이름으로 * 를 사용하여 해당 익스텐션 포인트에 대한 모든 기본 플러그인을 비활성화할 수 있다. 원하는 경우, 플러그인 순서를 재정렬하는 데 사용할 수도 있다.

스케줄링 플러그인

기본적으로 활성화된 다음의 플러그인은 이들 익스텐션 포인트 중 하나 이상을 구현한다.

  • ImageLocality: 파드가 실행하는 컨테이너 이미지가 이미 있는 노드를 선호한다. 익스텐션 포인트: score.
  • TaintToleration: 테인트(taint)와 톨러레이션(toleration)을 구현한다. 익스텐션 포인트 구현: filter, preScore, score.
  • NodeName: 파드 명세 노드 이름이 현재 노드와 일치하는지 확인한다. 익스텐션 포인트: filter.
  • NodePorts: 노드에 요청된 파드 포트에 대해 사용 가능한 포트가 있는지 확인한다. 익스텐션 포인트: preFilter, filter.
  • NodeAffinity: 노드 셀렉터노드 어피니티를 구현한다. 익스텐션 포인트: filter, score.
  • PodTopologySpread: 파드 토폴로지 분배를 구현한다. 익스텐션 포인트: preFilter, filter, preScore, score.
  • NodeUnschedulable: .spec.unschedulable 이 true로 설정된 노드를 필터링한다. 익스텐션 포인트: filter.
  • NodeResourcesFit: 노드에 파드가 요청하는 모든 리소스가 있는지 확인한다. 점수는 LeastAllocated(기본값), MostAllocated, RequestedToCapacityRatio 등 3가지 전략 중 하나를 사용할 수 있다. 익스텐션 포인트: preFilter, filter, score.
  • NodeResourcesBalancedAllocation: 파드가 스케줄된 경우, 보다 균형잡힌 리소스 사용량을 얻을 수 있는 노드를 선호한다. 익스텐션 포인트: score.
  • VolumeBinding: 노드에 요청된 볼륨이 있는지 또는 바인딩할 수 있는지 확인한다. 익스텐션 포인트: preFilter, filter, reserve, preBind, score.
  • VolumeRestrictions: 노드에 마운트된 볼륨이 볼륨 제공자에 특정한 제한 사항을 충족하는지 확인한다. 익스텐션 포인트: filter.
  • VolumeZone: 요청된 볼륨이 가질 수 있는 영역 요구 사항을 충족하는지 확인한다. 익스텐션 포인트: filter.
  • NodeVolumeLimits: 노드에 대해 CSI 볼륨 제한을 충족할 수 있는지 확인한다. 익스텐션 포인트: filter.
  • EBSLimits: 노드에 대해 AWS EBS 볼륨 제한을 충족할 수 있는지 확인한다. 익스텐션 포인트: filter.
  • GCEPDLimits: 노드에 대해 GCP-PD 볼륨 제한을 충족할 수 있는지 확인한다. 익스텐션 포인트: filter.
  • AzureDiskLimits: 노드에 대해 Azure 디스크 볼륨 제한을 충족할 수 있는지 확인한다. 익스텐션 포인트: filter.
  • InterPodAffinity: 파드 간 어피니티 및 안티-어피니티를 구현한다. 익스텐션 포인트: preFilter, filter, preScore, score.
  • PrioritySort: 기본 우선 순위 기반 정렬을 제공한다. 익스텐션 포인트: queueSort.
  • DefaultBinder: 기본 바인딩 메커니즘을 제공한다. 익스텐션 포인트: bind.
  • DefaultPreemption: 기본 선점 메커니즘을 제공한다. 익스텐션 포인트: postFilter.

기본으로 활성화되지 않는 다음의 플러그인을 컴포넌트 구성 API를 통해 활성화할 수도 있다.

  • SelectorSpread: Services, ReplicaSetsStatefulSets에 속하는 파드의 경우, 노드간에 퍼지는 것을 선호한다. 익스텐션 포인트: preScore, score.
  • CinderLimits: 노드에 대해 OpenStack Cinder 볼륨 제한이 충족될 수 있는지 확인한다. 익스텐션 포인트: filter.

다음 플러그인은 더 이상 사용되지 않으며 v1beta1에서만 사용할 수 있다.

  • NodeResourcesLeastAllocated: 리소스 할당이 낮은 노드를 선호한다. Extension points: score.
  • NodeResourcesMostAllocated: 리소스 할당이 많은 노드를 선호한다. 익스텐션 포인트: score.
  • RequestedToCapacityRatio: 할당된 리소스의 구성된 기능에 따라 노드를 선호한다. 익스텐션 포인트: score.
  • NodeLabel: 설정된 레이블에 따라 노드를 필터링하거나 스코어링한다. 익스텐션 포인트: Filter, Score.
  • ServiceAffinity: 서비스에 속한 파드가 구성된 레이블로 정의된 노드 집합에 맞는지 확인한다. 이 플러그인은 또한 서비스에 속한 파드를 노드 간에 분산하는 것을 선호한다. 익스텐션 포인트: preFilter, filter, score.
  • NodePreferAvoidPods: 노드 주석 scheduler.alpha.kubernetes.io/preferAvoidPods에 따라 노드의 우선 순위를 지정한다. 익스텐션 포인트: score.

여러 프로파일

둘 이상의 프로파일을 실행하도록 kube-scheduler 를 구성할 수 있다. 각 프로파일에는 연관된 스케줄러 이름이 있으며 익스텐션 포인트에 구성된 다른 플러그인 세트를 가질 수 있다.

다음의 샘플 구성을 사용하면, 스케줄러는 기본 플러그인이 있는 프로파일과 모든 스코어링 플러그인이 비활성화된 프로파일의 두 가지 프로파일로 실행된다.

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
  - schedulerName: no-scoring-scheduler
    plugins:
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

특정 프로파일에 따라 스케줄하려는 파드는 .spec.schedulerName 에 해당 스케줄러 이름을 포함할 수 있다.

기본적으로, 스케줄러 이름 default-scheduler 를 가진 하나의 프로파일이 생성된다. 이 프로파일에는 위에서 설명한 기본 플러그인이 포함되어 있다. 둘 이상의 프로파일을 선언할 때, 각각에 대한 고유한 스케줄러 이름이 필요하다.

파드가 스케줄러 이름을 지정하지 않으면, kube-apiserver는 이를 default-scheduler 로 설정한다. 따라서, 해당 파드를 스케줄하려면 이 스케줄러 이름을 가진 프로파일이 있어야 한다.

스케줄러 설정 전환

  • 설정 버전 v1beta2 에서는, NodeResourcesFit 플러그인을 위한 새로운 스코어링 확장을 이용할 수 있다. 새 확장은 NodeResourcesLeastAllocated, NodeResourcesMostAllocated, RequestedToCapacityRatio 플러그인의 기능을 통합하여 제공한다. 예를 들어, 이전에 NodeResourcesMostAllocated 플러그인을 사용했다면, 대신 NodeResourcesFit(기본적으로 활성화되어 있음)을 사용하면서 다음과 같이 scoreStrategy를 포함하는 pluginConfig를 추가할 수 있다.

    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
    - pluginConfig:
      - args:
          scoringStrategy:
            resources:
            - name: cpu
              weight: 1
            type: MostAllocated
        name: NodeResourcesFit
    
  • 스케줄러 플러그인 NodeLabel은 사용 중단되었다. 대신, 비슷한 효과를 얻기 위해 NodeAffinity 플러그인(기본적으로 활성화되어 있음)을 사용한다.

  • 스케줄러 플러그인 ServiceAffinity은 사용 중단되었다. 대신, 비슷한 효과를 얻기 위해 InterPodAffinity 플러그인(기본적으로 활성화되어 있음)을 사용한다.

  • 스케줄러 플러그인 NodePreferAvoidPods은 사용 중단되었다. 대신, 비슷한 효과를 얻기 위해 노드 테인트를 사용한다.

  • v1beta2 설정 파일에서 활성화된 플러그인은 해당 플러그인의 기본 설정값보다 v1beta2 설정 파일의 값이 우선 적용된다.

  • 스케줄러 healthz와 metrics 바인드 주소에 대해 host 또는 port가 잘못 설정되면 검증 실패를 유발한다.

다음 내용

6.11 - 도구

쿠버네티스는 쿠버네티스 시스템으로 작업하는 데 도움이 되는 몇 가지 도구를 포함한다.

Minikube

minikube는 개발과 테스팅 목적으로 단일 노드 쿠버네티스 클러스터를 로컬 워크스테이션에서 실행하는 도구이다.

대시보드

대시보드, 는 쿠버네티스의 웹기반 유저 인터페이스이며 컨테이너화된 애플리케이션을 쿠버네티스 클러스터로 배포하고 클러스터 및 클러스터 자원의 문제를 해결하며 관리할 수 있게 해준다.

Helm

Helm은 사전 구성된 쿠버네티스 리소스 패키지를 관리하기 위한 도구이다. 이 패키지는 Helm charts 라고 알려져 있다.

Helm은 미리 구성된 쿠버네티스 리소스 패키지를 관리하기 위한 제 3자가 관리하는 도구로, 쿠버네티스 차트(charts)라고도 알려져 있다.

Helm의 용도

  • 쿠버네티스 차트로 배포된 인기있는 소프트웨어를 검색하고 사용
  • 쿠버네티스 차트로 나의 애플리케이션을 공유
  • 쿠버네티스 애플리케이션의 반복가능한 빌드 및 생성
  • 매니페스트 파일의 지능화된 관리
  • Helm 패키지의 릴리스 관리

Kompose

Kompose는 도커 컴포즈(Compose) 유저들이 쿠버네티스로 이동하는데 도움이 되는 도구이다.

Kompose의 용도

  • 도커 컴포즈 파일을 쿠버네티스 오브젝트로 변환
  • 로컬 도커 개발 환경에서 나의 애플리케이션을 쿠버네티스를 통해 관리하도록 이전
  • V1 또는 V2 도커 컴포즈 yaml 파일 또는 분산 애플리케이션 번들을 변환

7 - K8s 문서에 기여하기

쿠버네티스는 신규 및 숙련된 모든 기여자의 개선을 환영합니다!

이 웹사이트는 쿠버네티스 SIG Docs에 의해서 관리됩니다.

쿠버네티스 문서 기여자들은

  • 기존 콘텐츠를 개선합니다.
  • 새 콘텐츠를 만듭니다.
  • 문서를 번역합니다.
  • 쿠버네티스 릴리스 주기에 맞추어 문서 부분을 관리하고 발행합니다.

시작하기

누구든지 문서에 대한 이슈를 오픈 또는 풀 리퀘스트(PR)를 사용해서 kubernetes/website GitHub 리포지터리에 변경하는 기여를 할 수 있습니다. 쿠버네티스 커뮤니티에 효과적으로 기여하려면 gitGitHub에 익숙해야 합니다.

문서에 참여하려면

  1. CNCF Contributor License Agreement에 서명합니다.
  2. 문서 리포지터리와 웹사이트의 정적 사이트 생성기를 숙지합니다.
  3. 풀 리퀘스트 열기변경 검토의 기본 프로세스를 이해하도록 합니다.

flowchart TB subgraph third[PR 열기] direction TB U[ ] -.- Q[컨텐츠 향상시키기] --- N[컨텐츠 생성하기] N --- O[문서 번역하기] O --- P[K8s 릴리스 사이클의 문서 파트
관리/퍼블리싱하기] end subgraph second[리뷰] direction TB T[ ] -.- D[K8s/website
저장소 살펴보기] --- E[Hugo 정적 사이트
생성기 확인하기] E --- F[기본 GitHub 명령어
이해하기] F --- G[열려 있는 PR을 리뷰하기] end subgraph first[가입] direction TB S[ ] -.- B[CNCF
Contributor
License Agreement
서명하기] --- C[sig-docs 슬랙 채널
가입하기] C --- V[kubernetes-sig-docs
메일링 리스트 가입하기] V --- M[주간
sig-docs 회의/
슬랙 미팅 참여하기] end A([fa:fa-user 신규
기여자]) --> first A --> second A --> third A --> H[질문하세요!!!] classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H,M,Q,N,O,P,V grey class S,T,U spacewhite class first,second,third white
그림 - 신규 기여자를 위한 시작 가이드

위의 그림은 신규 기여자를 위한 로드맵을 간략하게 보여줍니다. 가입리뷰 단계의 일부 또는 전체를 따를 수 있습니다. 이제 PR 열기 아래에 나열된 항목들을 수행하여 당신의 기여 목표를 달성할 수 있습니다. 다시 말하지만 질문은 언제나 환영입니다!

일부 작업에는 쿠버네티스 조직에서 더 많은 신뢰와 더 많은 접근이 필요할 수 있습니다. 역할과 권한에 대한 자세한 내용은 SIG Docs 참여를 봅니다.

첫 번째 기여

몇 가지 단계를 미리 검토하여 첫 번째 기여를 준비할 수 있습니다. 아래 그림은 각 단계를 설명하며, 그 다음에 세부 사항도 설명되어 있습니다.

flowchart LR subgraph second[첫 기여] direction TB S[ ] -.- G[다른 K8s 멤버의 PR 리뷰하기] --> A[K8s/website 이슈 리스트에서
good first issue 확인하기] --> B[PR을 여세요!!] end subgraph first[추천 준비 사항] direction TB T[ ] -.- D[기여 개요 읽기] -->E[K8s 컨텐츠 및
스타일 가이드 읽기] E --> F[Hugo 페이지 컨텐츠 종류와
shortcode 숙지하기] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,D,E,F,G grey class S,T spacewhite class first,second white
그림 - 첫 기여를 위한 준비

다음 단계

SIG Docs에 참여

SIG Docs는 쿠버네티스 문서와 웹 사이트를 게시하고 관리하는 기여자 그룹입니다. SIG Docs에 참여하는 것은 쿠버네티스 기여자(기능 개발 및 다른 여러가지)가 쿠버네티스 프로젝트에 가장 큰 영향을 미칠 수 있는 좋은 방법입니다.

SIG Docs는 여러가지 방법으로 의견을 나누고 있습니다.

다른 기여 방법들

7.1 - 콘텐츠 개선 제안

쿠버네티스 문서의 문제를 발견하거나 새로운 내용에 대한 아이디어가 있으면, 이슈를 연다. GitHub 계정과 웹 브라우저만 있으면 된다.

대부분의 경우, 쿠버네티스 문서에 대한 새로운 작업은 GitHub의 이슈로 시작된다. 그런 다음 쿠버네티스 기여자는 필요에 따라 이슈를 리뷰, 분류하고 태그를 지정한다. 다음으로, 여러분이나 다른 쿠버네티스 커뮤니티 멤버가 문제를 해결하기 위한 변경 사항이 있는 풀 리퀘스트를 연다.

이슈 열기

기존 콘텐츠에 대한 개선을 제안하고 싶거나 오류를 발견하면, 이슈를 연다.

  1. 오른쪽 사이드바에서 문서에 이슈 생성 링크를 클릭한다. 그러면 헤더가 미리 채워진 GitHub 이슈 페이지로 리디렉션된다.
  2. 개선을 위한 문제나 제안 사항을 설명한다. 가능한 한 많은 정보를 제공한다.
  3. Submit new issue 를 클릭한다.

이슈를 제출한 후, 가끔 확인하거나 GitHub 알림을 켜자. 리뷰어와 다른 커뮤니티 멤버가 이슈에 대한 조치를 취하기 전에 여러분에게 질문을 할 수 있다.

새로운 콘텐츠 제안

새로운 콘텐츠에 대한 아이디어가 있지만, 어디로 가야 할지 확실하지 않은 경우에도 이슈를 제기할 수 있다. 다음 중 하나를 선택하면 된다.

  • 콘텐츠가 속한 섹션에서 기존 페이지를 선택하고 이슈 생성하기 를 클릭한다.
  • GitHub으로 이동하여 이슈를 직접 제기한다.

이슈를 제기하는 좋은 방법

이슈를 제기할 때 다음의 사항에 유의한다.

  • 명확하게 이슈에 대한 설명을 제공한다. 구체적으로 무엇이 누락되었거나, 오래되었거나, 잘못되었거나, 개선이 필요한 사항인지를 설명한다.
  • 이 이슈가 사용자에게 미치는 영향을 설명한다.
  • 주어진 이슈의 범위를 합리적인 작업 단위로 제한한다. 넓은 범위에 대한 이슈의 경우 더 작은 이슈로 분류한다. 예를 들어, "보안 문서 수정"은 너무 광범위하지만, "'네트워크 접근 제한' 주제에 세부 사항 추가"는 실행할 수 있을 정도로 구체적이다.
  • 기존 이슈를 검색하여 새로운 이슈와 관련이 있거나 비슷한 것이 있는지 확인한다.
  • 새로운 이슈가 다른 이슈나 풀 리퀘스트와 관련이 있는 경우, 전체 URL이나 # 문자로 시작하는 이슈나 풀 리퀘스트의 번호로 참고한다. 예를 들면, #987654 와 관련있습니다. 와 같이 하면 된다.
  • 행동 강령을 따른다. 동료 기여자를 존중한다. 예를 들어, "문서가 끔찍하다"는 도움이 되지 않거나 예의 바르지 않은 피드백이다.

7.2 - 새로운 콘텐츠 기여하기

이 섹션에는 새로운 콘텐츠를 기여하기 전에 알아야 할 정보가 있다.

flowchart LR subgraph second[시작하기 전에] direction TB S[ ] -.- A[CNCF CLA 서명하기] --> B[Git 브랜치 선택하기] B --> C[한 PR에는 한 언어에 대한 변경사항만] C --> F[기여자 도구 확인하기] end subgraph first[기여 기초] direction TB T[ ] -.- D[문서를 마크다운으로 작성하고
Hugo로 사이트 빌드] --- E[GitHub에 있는 소스] E --- G['/content/../docs' 폴더에
각 언어 컨텐츠가 있음] G --- H[Hugo 페이지 컨텐츠 종류와
shortcode 숙지하기] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H grey class S,T spacewhite class first,second white

Figure - Contributing new content preparation

The figure above depicts the information you should know prior to submitting new content. The information details follow.

기여에 대한 기본 사항

  • 마크다운(Markdown)으로 쿠버네티스 문서를 작성하고 Hugo를 사용하여 쿠버네티스 사이트를 구축한다.
  • 소스는 GitHub에 있다. 쿠버네티스 문서는 /content/ko/docs/ 에서 찾을 수 있다. 일부 참조 문서는 update-imported-docs/ 디렉터리의 스크립트를 이용하여 자동으로 생성된다.
  • 페이지 템플릿은 Hugo에서 문서 콘텐츠의 프리젠테이션을 제어한다.
  • 표준 Hugo 단축코드(shortcode) 이외에도 설명서에서 여러 사용자 정의 Hugo 단축코드를 사용하여 콘텐츠 표시를 제어한다.
  • 문서 소스는 /content/ 에서 여러 언어로 제공된다. 각 언어는 ISO 639-1 표준에 의해 결정된 2문자 코드가 있는 자체 폴더가 있다. 예를 들어, 한글 문서의 소스는 /content/ko/docs/ 에 저장된다.
  • 여러 언어로 문서화에 기여하거나 새로운 번역을 시작하는 방법에 대한 자세한 내용은 현지화를 참고한다.

시작하기 전에

CNCF CLA 서명

모든 쿠버네티스 기여자는 반드시 기여자 가이드를 읽고 기여자 라이선스 계약(CLA)에 서명해야 한다 .

CLA에 서명하지 않은 기여자의 풀 리퀘스트(pull request)는 자동 테스트에 실패한다. 제공한 이름과 이메일은 git config 에 있는 것과 일치해야 하며, git 이름과 이메일은 CNCF CLA에 사용된 것과 일치해야 한다.

사용할 Git 브랜치를 선택한다

풀 리퀘스트를 열 때는, 작업의 기반이 되는 브랜치를 미리 알아야 한다.

시나리오 브랜치
현재 릴리스의 기존 또는 새로운 영어 콘텐츠 main
기능 변경 릴리스의 콘텐츠 dev-<version> 패턴을 사용하여 기능 변경이 있는 주 버전과 부 버전에 해당하는 브랜치. 예를 들어, v1.24 에서 기능이 변경된 경우, dev-1.24 에 문서 변경을 추가한다.
다른 언어로된 콘텐츠(현지화) 현지화 규칙을 사용. 자세한 내용은 현지화 브랜치 전략을 참고한다.

어떤 브랜치를 선택해야 할지 잘 모르는 경우 슬랙의 #sig-docs 에 문의한다.

PR 당 언어

PR 당 하나의 언어로 풀 리퀘스트를 제한한다. 여러 언어로 동일한 코드 샘플을 동일하게 변경해야 하는 경우 각 언어마다 별도의 PR을 연다.

기여자를 위한 도구들

kubernetes/website 리포지터리의 문서 기여자를 위한 도구 디렉터리에는 기여 여정을 좀 더 순조롭게 도와주는 도구들이 포함되어 있다.

7.2.1 - 풀 리퀘스트 열기

새 콘텐츠 페이지를 기여하거나 기존 콘텐츠 페이지를 개선하려면, 풀 리퀘스트(PR)를 연다. 시작하기 전에 섹션의 모든 요구 사항을 준수해야 한다.

변경 사항이 작거나, git에 익숙하지 않은 경우, GitHub을 사용하여 변경하기를 읽고 페이지를 편집하는 방법을 알아보자.

변경 사항이 많으면, 로컬 포크에서 작업하기를 읽고 컴퓨터에서 로컬로 변경하는 방법을 배운다.

GitHub을 사용하여 변경하기

git 워크플로에 익숙하지 않은 경우, 풀 리퀘스트를 여는 쉬운 방법이 있다. 아래의 그림은 각 단계를 보여주며, 상세사항은 그 아래에 나온다.

flowchart LR A([fa:fa-user 신규
기여자]) --- id1[(K8s/Website
GitHub)] subgraph tasks[GitHub 상에서 변경하기] direction TB 0[ ] -.- 1[1. '페이지 편집' 누르기] --> 2[2. GitHub 마크다운
편집기로 편집하기] 2 --> 3[3. 'Propose file change'에
추가 내용 기재하기] end subgraph tasks2[ ] direction TB 4[4. 'Propose changes' 누르기] --> 5[5. 'Create pull request' 누르기] --> 6[6. 'Open a pull request'에
추가 내용 기재하기] 6 --> 7[7. 'Create pull request' 누르기] end id1 --> tasks --> tasks2 classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,1,2,3,4,5,6,7 grey class 0 spacewhite class tasks,tasks2 white class id1 k8s

그림 - GitHub 상에서 PR을 여는 단계

  1. 이슈가 있는 페이지에서, 오른쪽 상단에 있는 연필 아이콘을 선택한다. 페이지 하단으로 스크롤 하여 페이지 편집하기 를 선택할 수도 있다.

  2. GitHub 마크다운 편집기에서 수정한다.

  3. 편집기 아래에서, Propose file change 양식을 작성한다. 첫 번째 필드에서, 커밋 메시지 제목을 지정한다. 두 번째 필드에는, 설명을 제공한다.

  4. Propose file change 를 선택한다.

  5. Create pull requests 를 선택한다.

  6. Open a pull request 화면이 나타난다. 양식을 작성한다.

    • 풀 리퀘스트의 Subject 필드는 기본적으로 커밋의 요약으로 설정한다. 필요한 경우 변경할 수 있다.
    • Body 는 만약 내용이 있다면, 확장된 커밋 메시지를 포함한다. 그리고 일부 템플릿 텍스트를 포함한다. 템플릿 텍스트에 필요한 세부 정보를 추가한 다음, 추가 템플릿 텍스트를 삭제한다.
    • Allow edits from maintainers 체크박스는 선택된 상태로 둔다.
  7. Create pull request 를 선택한다.

GitHub에서 피드백 해결

풀 리퀘스트를 병합하기 전에, 쿠버네티스 커뮤니티 회원은 이를 리뷰하고 승인한다. k8s-ci-robot 은 이 페이지에 나와있는 가까운 멤버에게 리뷰를 제안한다. 특정한 사람을 염두에 두고 있다면, GitHub 사용자 이름을 코멘트로 남긴다.

리뷰어가 변경을 요청하는 경우, 다음과 같이 한다.

  1. Files changed 탭으로 이동 한다.
  2. 풀 리퀘스트에 의해 변경된 파일에서 연필(편집) 아이콘을 선택한다.
  3. 요청된 변경에 대한 수정을 한다.
  4. 변경 사항을 커밋한다.

리뷰어를 기다리고 있는 경우, 7일마다 한 번씩 연락한다. 슬랙 채널 #sig-docs 에 메시지를 게시할 수도 있다.

리뷰가 완료되면, 리뷰어가 PR을 병합하고 몇 분 후에 변경 사항이 적용된다.

로컬 포크에서 작업하기

git에 익숙하거나, 변경 사항이 몇 줄보다 클 경우, 로컬 포크로 작업한다.

컴퓨터에 git이 설치되어 있는지 확인한다. git UI 애플리케이션을 사용할 수도 있다.

아래 그림은 로컬 포크에서 작업할 때의 단계를 나타낸다. 상세 사항도 소개되어 있다.

flowchart LR 1[K8s/website
저장소 포크하기] --> 2[로컬 클론 생성
및 upstream 설정] subgraph changes[당신의 변경사항] direction TB S[ ] -.- 3[브랜치 생성
예: my_new_branch] --> 3a[텍스트 편집기로
변경사항 만들기] --> 4["Hugo (localhost:1313)
를 이용하거나
컨테이너 이미지를 빌드하여
변경사항을 로컬에서 미리보기"] end subgraph changes2[커밋 / 푸시] direction TB T[ ] -.- 5[변경사항 커밋하기] --> 6[커밋을
origin/my_new_branch
로 푸시하기] end 2 --> changes --> changes2 classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class 1,2,3,3a,4,5,6 grey class S,T spacewhite class changes,changes2 white
그림 - 로컬 포크에서 변경 사항 작업하기

kubernetes/website 리포지터리 포크하기

  1. kubernetes/website 리포지터리로 이동한다.
  2. Fork 를 선택한다.

로컬 클론 생성 및 업스트림 설정

  1. 터미널 창에서, 포크를 클론하고 Docsy Hugo 테마를 업데이트한다.

    git clone [email protected]/<github_username>/website
    cd website
    git submodule update --init --recursive --depth 1
    
  2. website 디렉터리로 이동한다. kubernetes/website 리포지터리를 upstream 원격으로 설정한다.

    cd website
    
    git remote add upstream https://github.com/kubernetes/website.git
    
  3. originupstream 리포지터리를 확인한다.

    git remote -v
    

    출력은 다음과 비슷하다.

    origin	[email protected]:<github_username>/website.git (fetch)
    origin	[email protected]:<github_username>/website.git (push)
    upstream	https://github.com/kubernetes/website.git (fetch)
    upstream	https://github.com/kubernetes/website.git (push)
    
  4. 포크의 origin/mainkubernetes/websiteupstream/main 에서 커밋을 가져온다.

    git fetch origin
    git fetch upstream
    

    이를 통해 변경을 시작하기 전에 로컬 리포지터리가 최신 상태인지 확인한다.

브랜치 만들기

  1. 작업할 브랜치 기반을 결정한다.
  • 기존 콘텐츠를 개선하려면, upstream/main 를 사용한다.

  • 기존 기능에 대한 새로운 콘텐츠를 작성하려면, upstream/main 를 사용한다.

  • 현지화된 콘텐츠의 경우, 현지화 규칙을 사용한다. 자세한 내용은 쿠버네티스 문서 현지화를 참고한다.

  • 다가오는 쿠버네티스 릴리스의 새로운 기능에 대해서는 기능 브랜치(feature branch)를 사용한다. 자세한 정보는 릴리스 문서화를 참고한다.

  • 콘텐츠 재구성과 같이 여러 SIG Docs 기여자들이 협업하는 장기적인 작업에는, 해당 작업을 위해 작성된 특정 기능 브랜치를 사용한다.

    브랜치 선택에 도움이 필요하면, 슬랙 채널 #sig-docs 에 문의한다.

  1. 1단계에서 식별된 브랜치를 기반으로 새 브랜치를 작성한다. 이 예에서는 기본 브랜치가 upstream/main 라고 가정한다.

    git checkout -b <my_new_branch> upstream/main
    
  2. 텍스트 편집기를 사용하여 변경한다.

언제든지, git status 명령을 사용하여 변경한 파일을 본다.

변경 사항 커밋

풀 리퀘스트를 제출할 준비가 되면, 변경 사항을 커밋한다.

  1. 로컬 리포지터리에서 커밋해야 할 파일을 확인한다.

    git status
    

    출력은 다음과 비슷하다.

    On branch <my_new_branch>
    Your branch is up to date with 'origin/<my_new_branch>'.
    
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    
    modified:   content/en/docs/contribute/new-content/contributing-content.md
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
  2. Changes not staged for commit 에 나열된 파일을 커밋에 추가한다.

    git add <your_file_name>
    

    각 파일에 대해 이 작업을 반복한다.

  3. 모든 파일을 추가한 후, 커밋을 생성한다.

    git commit -m "Your commit message"
    
  4. 로컬 브랜치와 새로운 커밋을 원격 포크로 푸시한다.

    git push origin <my_new_branch>
    

로컬에서 변경 사항 미리보기

변경 사항을 푸시하거나 풀 리퀘스트를 열기 전에 변경 사항을 로컬에서 미리 보는 것이 좋다. 미리보기를 사용하면 빌드 오류나 마크다운 형식 문제를 알아낼 수 있다.

website의 컨테이너 이미지를 만들거나 Hugo를 로컬에서 실행할 수 있다. 도커 이미지 빌드는 느리지만 Hugo 단축코드를 표시하므로, 디버깅에 유용할 수 있다.

  1. 로컬에서 이미지를 빌드한다.

    # docker 사용(기본값)
    make container-image
    
    ### 또는 ###
    
    # podman 사용
    CONTAINER_ENGINE=podman make container-image
    
  2. 로컬에서 kubernetes-hugo 이미지를 빌드한 후, 사이트를 빌드하고 서비스한다.

    # docker 사용(기본값)
    make container-serve
    
    ### 또는 ###
    
    # podman 사용
    CONTAINER_ENGINE=podman make container-serve
    
  3. 웹 브라우저에서 https://localhost:1313 로 이동한다. Hugo는 변경 사항을 보고 필요에 따라 사이트를 다시 구축한다.

  4. 로컬의 Hugo 인스턴스를 중지하려면, 터미널로 돌아가서 Ctrl+C 를 입력하거나, 터미널 창을 닫는다.

또는, 컴퓨터에 hugo 명령을 설치하여 사용한다.

  1. website/netlify.toml에 지정된 Hugo 버전을 설치한다.

  2. website 리포지터리를 업데이트하지 않았다면, website/themes/docsy 디렉터리가 비어 있다. 테마의 로컬 복제본이 없으면 사이트를 빌드할 수 없다. website 테마를 업데이트하려면, 다음을 실행한다.

    git submodule update --init --recursive --depth 1
    
  3. 터미널에서, 쿠버네티스 website 리포지터리로 이동하여 Hugo 서버를 시작한다.

    cd <path_to_your_repo>/website
    hugo server --buildFuture
    
  4. 웹 브라우저에서 https://localhost:1313 으로 이동한다. Hugo는 변경 사항을 보고 필요에 따라 사이트를 다시 구축한다.

  5. 로컬의 Hugo 인스턴스를 중지하려면, 터미널로 돌아가서 Ctrl+C 를 입력하거나,     터미널 창을 닫는다.

포크에서 kubernetes/website로 풀 리퀘스트 열기

아래 그림은 당신의 포크에서 K8s/website 저장소로 PR을 여는 단계를 보여 준다. 상세 사항은 아래에 등장한다.

flowchart LR subgraph first[ ] direction TB 1[1. K8s/website 저장소로 이동] --> 2[2. 'New Pull Request' 클릭] 2 --> 3[3. 'Compare across forks' 클릭] 3 --> 4[4. 'head repository' 드롭다운 메뉴에서
당신의 포크 선택] end subgraph second [ ] direction TB 5[5. 'compare' 드롭다운 메뉴에서
당신의 브랜치 선택] --> 6[6. 'Create Pull Request' 클릭] 6 --> 7[7. PR 본문에 상세 설명 기재] 7 --> 8[8. 'Create pull request' 클릭] end first --> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold class 1,2,3,4,5,6,7,8 grey class first,second white
그림 - 당신의 포크에서 K8s/website 저장소로 PR을 여는 단계

  1. 웹 브라우저에서 kubernetes/website 리포지터리로 이동한다.

  2. New Pull Request 를 선택한다.

  3. compare across forks 를 선택한다.

  4. head repository 드롭다운 메뉴에서, 포크를 선택한다.

  5. compare 드롭다운 메뉴에서, 브랜치를 선택한다.

  6. Create Pull Request 를 선택한다.

  7. 풀 리퀘스트에 대한 설명을 추가한다.

    • Title(50자 이하): 변경 사항에 대한 의도를 요약한다.
    • Description: 변경 사항을 자세히 설명한다.
      • 관련된 GitHub 이슈가 있는 경우, Fixes #12345 또는 Closes #12345 를 설명에 포함한다. 이렇게 하면 GitHub의 자동화 기능이 PR을 병합한 후 언급된 이슈를 닫는다. 다른 관련된 PR이 있는 경우, 이들 PR도 연결한다.
      • 구체적인 내용에 대한 조언이 필요한 경우, 원하는 질문을 리뷰어가 생각해볼 수 있도록 설명에 포함한다.
  8. Create pull request 버튼을 선택한다.

축하한다! 여러분의 풀 리퀘스트가 풀 리퀘스트에 열렸다.

PR을 연 후, GitHub는 자동 테스트를 실행하고 Netlify를 사용하여 미리보기를 배포하려고 시도한다.

  • Netlify 빌드가 실패하면, 자세한 정보를 위해 Details 를 선택한다.
  • Netlify 빌드가 성공하면, Details 를 선택하면 변경 사항이 적용된 쿠버네티스 website의 커밋하기 직전의 버전(staged version)이 열린다. 리뷰어가 변경 사항을 확인하는 방법이다.

또한 GitHub는 리뷰어에게 도움을 주기 위해 PR에 레이블을 자동으로 할당한다. 필요한 경우 직접 추가할 수도 있다. 자세한 내용은 이슈 레이블 추가와 제거를 참고한다.

로컬에서 피드백 해결

  1. 변경한 후, 이전 커밋을 수정한다.

    git commit -a --amend
    
    • -a: 모든 변경 사항을 커밋
    • --amend: 새로운 커밋을 만들지 않고, 이전 커밋을 수정한다.
  2. 필요한 경우 커밋 메시지를 업데이트한다.

  3. git push origin <my_new_branch> 를 사용해서 변경 사항을 푸시하고 Netlify 테스트를 다시 실행한다.

리뷰어의 변경

때때로 리뷰어가 여러분의 풀 리퀘스트를 커밋한다. 다른 변경을 하기 전에, 커밋을 가져온다.

  1. 원격 포크에서 커밋을 가져오고 작업 브랜치를 리베이스한다.

    git fetch origin
    git rebase origin/<your-branch-name>
    
  2. 리베이스한 후, 포크에 새로운 변경 사항을 강제로 푸시한다.

    git push --force-with-lease origin <your-branch-name>
    

충돌 병합 및 리베이스

다른 기여자가 다른 PR에서 동일한 파일에 대한 변경 사항을 커밋하면, 병합 충돌이 발생할 수 있다. PR의 모든 병합 충돌을 해결해야 한다.

  1. 포크를 업데이트하고 로컬 브랜치를 리베이스한다.

    git fetch origin
    git rebase origin/<your-branch-name>
    

    그런 다음 포크에 변경 사항을 강제로 푸시한다.

    git push --force-with-lease origin <your-branch-name>
    
  2. kubernetes/websiteupstream/main 에 대한 변경 사항을 가져오고 브랜치를 리베이스한다.

    git fetch upstream
    git rebase upstream/main
    
  3. 리베이스의 결과를 검사한다.

    git status
    

이 명령의 결과에 여러 파일이 충돌된 것으로 표시된다.

  1. 충돌하는 각 파일을 열고 충돌 마커(>>>,<<< 그리고 ===)를 찾는다. 충돌을 해결하고 충돌 마커를 삭제한다.

  2. 변경 세트에 파일을 추가한다.

    git add <filename>
    
  3. 리베이스를 계속한다.

    git rebase --continue
    
  4. 필요에 따라 2단계에서 5단계를 반복한다.

    모든 커밋을 적용한 후, git status 명령은 리베이스가 완료되었음을 나타낸다.

  5. 브랜치를 포크에 강제로 푸시한다.

    git push --force-with-lease origin <your-branch-name>
    

    풀 리퀘스트에 더 이상 충돌이 표시되지 않는다.

커밋 스쿼시하기

PR에 여러 커밋이 있는 경우, PR을 병합하기 전에 해당 커밋을 단일 커밋으로 스쿼시해야 한다. PR의 Commits 탭에서 또는 git log 명령을 로컬에서 실행하여 커밋 수를 확인할 수 있다.

  1. 대화식 리베이스를 시작한다.

    git rebase -i HEAD~<number_of_commits_in_branch>
    

    커밋을 스쿼시하는 것은 일종의 리베이스이다. git의 -i 스위치는 리베이스를 대화형으로 할 수 있게 한다. HEAD~<number_of_commits_in_branch> 는 리베이스를 위해 살펴볼 커밋 수를 나타낸다.

    출력은 다음과 비슷하다.

    pick d875112ca Original commit
    pick 4fa167b80 Address feedback 1
    pick 7d54e15ee Address feedback 2
    
    # 3d18sf680..7d54e15ee 를 3d183f680 으로 리베이스한다 (3개 명령)
    
    ...
    
    # 이 행들은 순서를 바꿀 수 있다. 이들은 위에서 아래로 실행된다.
    

    출력의 첫 번째 섹션에는 리베이스의 커밋이 나열된다. 두 번째 섹션에는 각 커밋에 대한 옵션이 나열되어 있다. pick 단어를 바꾸면 리베이스가 완료되었을 때 커밋 상태가 변경된다.

    리베이스를 하는 목적인 squashpick 에 집중한다.

  2. 파일 편집을 시작한다.

    다음의 원본 텍스트를 변경한다.

    pick d875112ca Original commit
    pick 4fa167b80 Address feedback 1
    pick 7d54e15ee Address feedback 2
    

    아래와 같이 변경한다.

    pick d875112ca Original commit
    squash 4fa167b80 Address feedback 1
    squash 7d54e15ee Address feedback 2
    

    이것은 커밋 4fa167b80 Address feedback 17d54e15ee Address feedback 2d875112ca Original commit 으로 스쿼시한다. 타임라인의 일부로 d875112ca Original commit 만 남긴다.

  3. 파일을 저장하고 종료한다.

  4. 스쿼시된 커밋을 푸시한다.

    git push --force-with-lease origin <branch_name>
    

다른 리포지터리에 기여하기

쿠버네티스 프로젝트에는 50개 이상의 리포지터리가 포함되어 있다. 이러한 리포지터리에는 사용자용 도움말 텍스트, 오류 메시지, API 레퍼런스 또는 코드 주석과 같은 문서가 포함되어 있다.

개선하려는 텍스트가 보이면, GitHub을 사용하여 쿠버네티스 조직의 모든 리포지터리를 검색한다. 이를 통해 어디에 이슈나 PR을 제출할지를 파악할 수 있다.

각 리포지터리에는 고유한 프로세스와 절차가 있다. 여러분이 이슈를 제기하거나 PR을 제출하기 전에, 그 리포지터리의 README.md, CONTRIBUTING.md 그리고 code-of-conduct.md(만약 이들 문서가 있다면)를 읽어본다.

대부분의 리포지터리에는 이슈와 PR 템플릿이 사용된다. 팀의 프로세스에 대한 느낌을 얻으려면 열린 이슈와 PR을 살펴보자. 이슈나 PR을 제출할 때 가능한 한 상세하게 템플릿의 내용을 작성한다.

다음 내용

  • 리뷰 프로세스에 대한 자세한 내용은 리뷰하기를 읽어본다.

7.3 - 변경 사항 리뷰하기

이 섹션은 콘텐츠를 리뷰하는 방법에 대해 설명한다.

7.3.1 - 풀 리퀘스트 리뷰

누구나 문서화에 대한 풀 리퀘스트를 리뷰할 수 있다. 쿠버네티스 website 리포지터리의 풀 리퀘스트 섹션을 방문하여 열린(open) 풀 리퀘스트를 확인한다.

문서화에 대한 풀 리퀘스트를 리뷰하는 것은 쿠버네티스 커뮤니티에 자신을 소개하는 훌륭한 방법이다. 아울러, 코드 베이스(code base)를 배우고 다른 기여자와 신뢰를 구축하는 데 도움이 된다.

리뷰하기 전에, 다음을 수행하는 것이 좋다.

시작하기 전에

리뷰를 시작하기 전에 다음을 명심하자.

  • CNCF 행동 강령을 읽고 항상 준수한다.
  • 정중하고, 사려 깊고, 도움이 되자.
  • PR의 긍정적인 측면과 변화에 대한 의견을 남긴다.
  • 당신의 리뷰를 어떻게 받아들일지에 대해 공감하고 주의한다.
  • 좋은 의도를 가지고 명확한 질문을 한다.
  • 숙련된 기여자인 경우, 작업에 광범위한 변경이 필요한 새 기여자와 쌍을 이루어 리뷰해 본다.

리뷰 과정

일반적으로, 영어로 콘텐츠와 스타일에 대한 풀 리퀘스트를 리뷰한다. 아래의 그림은 리뷰 과정의 단계를 보여 준다. 각 단계에 대한 상세 사항은 아래에 나와 있다.

flowchart LR subgraph fourth[리뷰 시작] direction TB S[ ] -.- M[코멘트 작성] --> N[변경사항 리뷰] N --> O[새 기여자가 어떤 코멘트를
반영할지 선택해야 함] end subgraph third[PR 선택] direction TB T[ ] -.- J[본문과 코멘트 확인]--> K[Netlify 미리보기 빌드로
변경사항 미리보기] end A[열려 있는 PR 목록 확인]--> B[레이블을 이용하여
PR을 필터링] B --> third --> fourth classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,J,K,M,N,O grey class S,T spacewhite class third,fourth white

그림 - 리뷰 과정 절차

  1. https://github.com/kubernetes/website/pulls로 이동한다. 쿠버네티스 website와 문서에 대한 모든 열린 풀 리퀘스트 목록이 표시된다.

  2. 다음 레이블 중 하나 또는 모두를 사용하여 열린 PR을 필터링한다.

    • cncf-cla: yes(권장): CLA에 서명하지 않은 기여자가 제출한 PR은 병합할 수 없다. 자세한 내용은 CLA 서명을 참고한다.
    • language/en(권장): 영어 문서에 대한 PR 전용 필터이다.
    • size/<size>: 특정 크기의 PR을 필터링한다. 새로 시작하는 사람이라면, 더 작은 PR로 시작한다.

    또한, PR이 진행 중인 작업으로 표시되지 않았는지 확인한다. work in progress 레이블을 사용하는 PR은 아직 리뷰할 준비가 되지 않은 PR이다.

  3. 리뷰할 PR을 선택한 후, 다음을 통해 변경 사항을 이해한다.

    • PR 설명을 통해 변경 사항을 이해하고, 연결된 이슈 읽기
    • 다른 리뷰어의 의견 읽기
    • Files changed 탭을 클릭하여 변경된 파일과 행 보기
    • Conversation 탭의 맨 아래에 있는 PR의 빌드 확인 섹션으로 스크롤하여 deploy/netlify 행의 Details 링크를 클릭하고 Netlify 미리보기 빌드의 변경 사항을 확인
  4. Files changed 탭으로 이동하여 리뷰를 시작한다.

    1. 코멘트을 달려는 줄 옆에 있는 + 기호를 클릭한다.
    2. 행에 대한 의견을 작성하고 Add single comments(작성할 의견이 하나만 있는 경우) 또는 Start a review(작성할 의견이 여러 개인 경우)를 클릭한다.
    3. 완료되면, 페이지 상단에서 Review changes 를 클릭한다. 여기에서 리뷰에 대한 요약을 추가하고(기여자에게 긍정적인 의견을 남겨주기 바란다!), PR을 승인하거나, 의견을 보내거나 필요에 따라 변경을 요청할 수 있다. 새로운 기여자는 항상 Comment 를 선택해야 한다.

리뷰 체크리스트

리뷰할 때, 다음을 시작점으로 사용한다.

언어와 문법

  • 언어나 문법에 명백한 오류가 있는가? 무언가를 표현하는 더 좋은 방법이 있는가?
  • 더 간단한 단어로 대체될 수 있는 복잡하거나 오래된 단어가 있는가?
  • 비 차별적 대안으로 대체될 수 있는 단어, 용어 또는 문구가 있는가?
  • 단어 선택과 대소문자는 스타일 가이드를 따르는가?
  • 더 짧고 간결하게 만들 수 있는 긴 문장이 있는가?
  • 목록이나 표로 더 잘 표현할 수 있는 긴 단락이 있는가?

콘텐츠

  • 쿠버네티스 사이트의 다른 곳에도 비슷한 콘텐츠가 있는가?
  • 콘텐츠가 오프-사이트, 개별 업체, 또는 공개되지 않은 소스 문서에 과도하게 링크되는가?

웹 사이트

  • 이 PR이 페이지 제목, slug/alias 또는 앵커(anchor) 링크를 변경 또는 제거하는가? 그렇다면, 이 PR의 결과로 끊어진 링크가 있는가? slug를 변경 없이 페이지 제목을 변경하는 등의 다른 옵션이 있는가?
  • PR이 새로운 페이지를 소개하는가? 그렇다면,
    • 페이지가 올바른 페이지 콘텐츠 타입과 연관된 Hugo 단축 코드를 사용하는가?
    • 섹션의 측면 탐색에 페이지가 올바르게 나타나는가?
    • 페이지가 문서 홈 목록에 나타나야 하는가?
  • 변경 사항이 Netlify 미리보기에 표시되는가? 목록, 코드 블록, 표, 메모 및 이미지에 특히 주의한다.

기타

오타나 공백과 같은 작은 이슈의 PR인 경우, 코멘트 앞에 nit: 를 추가한다. 이를 통해 문서의 저자는 이슈가 긴급하지 않다는 것을 알 수 있다.

7.3.2 - 승인자와 리뷰어의 리뷰

SIG Docs 리뷰어승인자는 변경 사항을 리뷰할 때 몇 가지 추가 작업을 수행한다.

매주 특정 문서 승인자 역할의 지원자가 풀 리퀘스트를 심사하고 리뷰한다. 이 사람은 일주일 동안 "PR 랭글러(Wrangler)"이다. 자세한 정보는 PR 랭글러 스케줄러를 참고한다. PR 랭글러가 되려면, 매주 SIG Docs 회의에 참석하고 자원한다. 이번 주에 해당 일정이 없는 경우에도, 아직 리뷰 중이 아닌 풀 리퀘스트(PR)를 여전히 리뷰할 수 있다.

로테이션 외에도, 봇은 영향을 받는 파일의 소유자를 기반으로 PR에 대한 리뷰어와 승인자를 할당한다.

PR 리뷰

쿠버네티스의 문서는 쿠버네티스의 코드 리뷰 프로세스를 따른다.

풀 리퀘스트 리뷰에 설명된 모든 내용이 적용되지만, 리뷰어와 승인자도 다음을 수행해야 한다.

  • /assign Prow 명령을 사용하여 필요에 따라 특정 리뷰어를 PR에 할당한다. 이는 코드 기여자에게 기술 리뷰를 요청할 때 특히 중요하다.

  • PR이 콘텐츠스타일 가이드를 따르는 지 확인한다. 그렇지 않은 경우 가이드의 관련 부분에 작성자를 연결한다.

  • 적용이 가능한 경우 GitHub Request Changes 옵션을 사용하여 PR 작성자에게 변경을 제안한다.

  • 제안한 사항이 구현된 경우, /approve 또는 /lgtm Prow 명령을 사용하여 GitHub에서 리뷰 상태를 변경한다.

다른 사람의 PR에 커밋

PR 코멘트를 남기는 것이 도움이 되지만, 대신 다른 사람의 PR에 커밋을 해야 하는 경우가 있다.

다른 사람이 명시적으로 요청하거나, 오랫동안 중단된 PR을 재개하려는 경우가 아니라면 다른 사람에게서 "가져오지" 마라. 단기적으로는 작업이 빠를 수 있지만, 그 사람이 기여할 기회를 박탈하게 된다.

사용할 프로세스는 이미 PR의 범위에 있는 파일을 편집해야 하는지, 또는 PR이 아직 다루지 않은 파일을 편집해야 하는지에 따라 다르다.

다음 중 하나에 해당하면 다른 사람의 PR에 커밋할 수 없다.

  • PR 작성자가 브랜치를 https://github.com/kubernetes/website/ 리포지터리로 직접 푸시한 경우, 푸시 접근 권한이 있는 리뷰어만 다른 사용자의 PR에 커밋할 수 있다.

  • PR 작성자가 승인자의 수정을 명시적으로 허용하지 않는다.

리뷰를 위한 Prow 명령

Prow는 풀 리퀘스트 (PR)에 대한 작업을 실행하는 쿠버네티스 기반 CI/CD 시스템이다. Prow는 챗봇 스타일 명령으로 쿠버네티스 조직 전체에서 레이블 추가와 제거, 이슈 종료 및 승인자 할당과 같은 GitHub 작업을 처리할 수 ​​있다. /<command-name> 형식을 사용하여 Prow 명령을 GitHub 코멘트로 입력한다.

리뷰어와 승인자가 사용하는 가장 일반적인 Prow 명령은 다음과 같다.

리뷰를 위한 Prow 명령
Prow 명령 역할 제한 설명
/lgtm 누구나, 리뷰어나 승인자가 사용한다면 자동화를 트리거한다. PR 리뷰를 마치고 변경 사항에 만족했음을 나타낸다.
/approve 승인자 PR을 병합(merge)하기 위해 승인한다.
/assign 리뷰어 또는 승인자 PR을 리뷰하거나 승인할 사람을 지정한다.
/close 리뷰어 또는 승인자 이슈 또는 PR을 닫는다.
/hold 누구나 자동으로 병합할 수 없음을 나타내는 do-not-merge/hold 레이블을 추가한다.
/hold cancel 누구나 do-not-merge/hold 레이블을 제거한다.

PR에서 사용할 수 있는 명령의 전체 목록을 보려면 Prow 명령 레퍼런스를 참고한다.

이슈 심사와 분류

일반적으로, SIG Docs는 쿠버네티스 이슈 심사 프로세스를 따르며 동일한 레이블을 사용한다.

이 GitHub 이슈 필터는 심사가 필요한 이슈를 찾는다.

이슈 심사

  1. 이슈 확인
  • 이슈가 website 문서에 관한 것인지 확인한다. 질문에 답하거나 리소스에 리포터를 지정하면 일부 이슈를 신속하게 종결할 수 있다. 자세한 내용은 지원 요청 또는 코드 버그 리포트 섹션을 참고한다.
  • 이슈가 가치가 있는지 평가한다.
  • 이슈의 내용에 실행할 수 있는 세부 사항이 충분하지 않거나 템플릿의 내용이 제대로 작성되지 않은 경우 triage/needs-information 레이블을 추가한다.
  • lifecycle/staletriage/needs-information 레이블이 모두 있으면 이슈를 닫는다.
  1. 우선순위 레이블을 추가한다(이슈 심사 가이드라인은 우선순위 레이블을 자세히 정의함).
이슈 레이블
레이블 설명
priority/critical-urgent 이 작업을 지금 즉시 수행한다.
priority/important-soon 3개월 이내에 이 작업을 수행한다.
priority/important-longterm 6개월 이내에 이 작업을 수행한다.
priority/backlog 무기한 연기할 수 있다. 자원이 있을 때 수행한다.
priority/awaiting-more-evidence 잠재적으로 좋은 이슈에 대해 잊지 않도록 표시한다.
help 또는 good first issue 쿠버네티스나 SIG Docs 경험이 거의 없는 사람에게 적합하다. 자세한 내용은 도움이 필요함 및 좋은 첫 번째 이슈 레이블을 참고한다.

재량에 따라, 이슈의 소유권을 가져와서 PR을 제출한다(특히, 이미 수행 중인 작업과 관련이 있거나 빠르다면).

이슈 심사에 대해 질문이 있다면, 슬랙의 #sig-docs 채널이나 kubernetes-sig-docs 메일링리스트에 문의한다.

이슈 레이블 추가와 제거

레이블을 추가하려면, 다음의 형식 중 하나로 코멘트를 남긴다.

  • /<label-to-add> (예: /good-first-issue)
  • /<label-category> <label-to-add> (예: /triage needs-information 또는 /language ko)

레이블을 제거하려면, 다음의 형식 중 하나로 코멘트를 남긴다.

  • /remove-<label-to-remove> (예: /remove-help)
  • /remove-<label-category> <label-to-remove> (예: /remove-triage needs-information)

두 경우 모두, 사용하려는 레이블은 이미 존재하는 레이블이어야 한다. 존재하지 않는 레이블을 추가하려고 하면, 명령이 자동으로 무시된다.

모든 레이블 목록에 대해서는 website 리포지터리의 레이블 섹션을 참고한다. SIG Docs에서 모든 레이블을 사용하는 것은 아니다.

이슈의 lifecycle 레이블

이슈는 일반적으로 신속하게 열리고 닫힌다. 그러나, 가끔씩은 이슈가 열린 후 비활성 상태로 있다. 어떤 경우에는 이슈가 90일 이상 열려 있을 수도 있다.

이슈의 lifecycle 레이블
레이블 설명
lifecycle/stale 90일이 지나도 아무런 활동이 없는 이슈는 자동으로 오래된 것(stale)으로 표시된다. /remove-lifecycle stale 명령을 사용하여 라이프사이클을 수동으로 되돌리지 않으면 이슈가 자동으로 닫힌다.
lifecycle/frozen 90일 동안 활동이 없어도 이 레이블의 이슈는 오래된 것(stale)으로 바뀌지 않는다. 사용자는 priority/important-longterm 레이블이 있는 이슈처럼 90일보다 훨씬 오래 열려 있어야 하는 이슈에 이 레이블을 수동으로 추가한다.

특별한 이슈 유형의 처리

SIG Docs가 처리 방법을 문서화할 정도로 다음과 같은 유형의 이슈를 자주 경험하게 된다.

중복된 이슈

단일 문제에 대해 하나 이상의 이슈가 열려 있으면, 이를 단일 이슈로 합친다. 열린 상태를 유지할 이슈를 결정한 다음(또는 새로운 이슈를 열어야 함), 모든 관련 정보로 이동하여 관련 이슈를 연결해야 한다. 마지막으로, 동일한 문제를 설명하는 다른 모든 이슈에 triage/duplicate 레이블을 지정하고 닫는다. 하나의 이슈만 해결하는 것으로 혼동을 줄이고 같은 문제에 대한 중복 작업을 피할 수 있다.

깨진 링크 이슈

깨진 링크 이슈가 API 문서나 kubectl 문서에 있는 경우, 문제가 완전히 이해될 때까지 /priority critical-urgent 레이블을 할당한다. 다른 모든 깨진 링크 이슈는 수동으로 수정해야하므로, /priority important-longterm 를 할당한다.

블로그 이슈

쿠버네티스 블로그 항목은 시간이 지남에 따라 구식이 될 것으로 예상한다. 따라서, 1년 미만의 블로그 항목만 유지 관리한다. 1년이 지난 블로그 항목과 관련된 이슈일 경우, 수정하지 않고 이슈를 닫는다.

지원 요청 또는 코드 버그 리포트

문서에 대한 일부 이슈는 실제로 기본 코드와 관련된 이슈이거나, 튜토리얼과 같은 무언가가 작동하지 않을 때 도움을 요청하는 것이다. 문서와 관련이 없는 이슈의 경우, triage/support 레이블과 함께 요청자에게 지원받을 수 있는 곳(슬랙, Stack Overflow)을 알려주며 이슈를 닫고, 기능 관련 버그에 대한 이슈인 경우, 관련 리포지터리를 코멘트로 남긴다(kubernetes/kubernetes 는 시작하기 좋은 곳이다).

지원 요청에 대한 샘플 응답은 다음과 같다.

이 이슈는 지원 요청과 비슷하지만
문서 관련 이슈와는 관련이 없는 것 같습니다.
[쿠버네티스 슬랙](https://slack.k8s.io/)의
`#kubernetes-users` 채널에서 질문을 하시기 바랍니다. 또한,
[Stack Overflow](https://stackoverflow.com/questions/tagged/kubernetes)와
같은 리소스를 검색하여 유사한 질문에 대한 답변을
얻을 수도 있습니다.

https://github.com/kubernetes/kubernetes 에서
쿠버네티스 기능 관련 이슈를 열 수도 있습니다.

문서에 대한 이슈인 경우 이 이슈를 다시 여십시오.

샘플 코드 버그 리포트 응답은 다음과 같다.

이 이슈는 문서에 대한 이슈보다 코드에 대한 이슈와
비슷합니다. https://github.com/kubernetes/kubernetes/issues 에서
이슈를 여십시오.

문서에 대한 이슈인 경우 이 이슈를 다시 여십시오.

7.4 - SIG Docs에 참여하기

SIG Docs는 쿠버네티스 프로젝트의 분과회(special interest group) 중 하나로, 쿠버네티스 전반에 대한 문서를 작성하고, 업데이트하며 유지보수하는 일을 주로 수행한다. 분과회에 대한 보다 자세한 정보는 커뮤니티 GitHub 저장소 내 SIG Docs 를 참조한다.

SIG Docs는 모든 컨트리뷰터의 콘텐츠와 리뷰를 환영한다. 누구나 풀 리퀘스트(PR)를 요청할 수 있고, 누구나 콘텐츠에 대해 이슈를 등록하거나 진행 중인 풀 리퀘스트에 코멘트를 등록할 수 있다.

멤버, 리뷰어, 또는 승인자가 될 수 있다. 이런 역할은 변경을 승인하고 커밋할 수 있도록 보다 많은 접근 권한과 이에 상응하는 책임이 수반된다. 쿠버네티스 커뮤니티 내에서 멤버십이 운영되는 방식에 대한 보다 많은 정보를 확인하려면 커뮤니티 멤버십 문서를 확인한다.

문서의 나머지에서는 대외적으로 쿠버네티스를 가장 잘 드러내는 수단 중 하나인 쿠버네티스 웹사이트와 문서를 관리하는 책임을 가지는 SIG Docs에서, 이런 체계가 작동하는 특유의 방식에 대한 윤곽을 잡아보겠다.

SIG Docs 의장

SIG Docs를 포함한 각 SIG는, 한 명 이상의 SIG 멤버가 의장 역할을 하도록 선정한다. 이들은 SIG Docs와 다른 쿠버네티스 조직 간 연락책(point of contact)이 된다. 이들은 쿠버네티스 프로젝트 전반의 조직과 그 안에서 SIG Docs가 어떻게 운영되는지에 대한 폭넓은 지식을 갖추어야한다. 현재 의장의 목록을 확인하려면 리더십 문서를 참조한다.

SIG Docs 팀과 자동화

SIG Docs의 자동화는 다음의 두 가지 메커니즘에 의존한다. GitHub 팀과 OWNERS 파일이다.

GitHub 팀

GitHub의 SIG Docs [팀]에는 두 분류가 있다.

  • 승인자와 리더를 위한 @sig-docs-{language}-owners
  • 리뷰어를 위한 @sig-docs-{language}-reviewers

그룹의 전원과 의사소통하기 위해서 각각 GitHub 코멘트에서 그룹의 @name으로 참조할 수 있다.

가끔은 Prow와 GitHub 팀은 정확히 일치하지 않고 중복된다. 이슈, 풀 리퀘스트를 할당하고, PR 승인을 지원하기 위해서 자동화 시스템이 OWNERS 파일의 정보를 활용한다.

OWNERS 파일과 전문(front-matter)

쿠버네티스 프로젝트는 GitHub 이슈와 풀 리퀘스트 자동화와 관련해서 prow라고 부르는 자동화 툴을 사용한다. 쿠버네티스 웹사이트 리포지터리는 다음의 두개의 prow 플러그인을 사용한다.

  • blunderbuss
  • approve

이 두 플러그인은 kubernetes/website GitHub 리포지터리 최상위 수준에 있는 OWNERSOWNERS_ALIASES 파일을 사용해서 해당 리포지터리에 대해 prow가 작동하는 방식을 제어한다.

OWNERS 파일은 SIG Docs 리뷰어와 승인자의 목록을 포함한다. OWNERS 파일은 하위 디렉터리에 있을 수 있고, 해당 하위 디렉터리와 그 이하의 파일에 대해 리뷰어와 승인자 역할을 수행할 사람을 새로 지정할 수 있다. 일반적인 OWNERS 파일에 대한 보다 많은 정보는 OWNERS 문서를 참고한다.

추가로, 개별 마크다운(Markdown) 파일 내 전문에 리뷰어와 승인자를 개별 GitHub 사용자 이름이나 GitHub 그룹으로 열거할 수 있다.

OWNERS 파일과 마크다운 파일 내 전문의 조합은 자동화 시스템이 누구에게 기술적, 편집적 리뷰를 요청해야 할지를 PR 소유자에게 조언하는데 활용된다.

병합 작업 방식

풀 리퀘스트 요청이 콘텐츠를 발행하는데 사용하는 브랜치에 병합되면, 해당 콘텐츠는 https://kubernetes.io 에 공개된다. 게시된 콘텐츠의 품질을 높히기 위해 SIG Docs 승인자가 풀 리퀘스트를 병합하는 것을 제한한다. 작동 방식은 다음과 같다.

  • 풀 리퀘스트에 lgtmapprove 레이블이 있고, hold 레이블이 없고, 모든 테스트를 통과하면 풀 리퀘스트는 자동으로 병합된다.
  • 쿠버네티스 조직의 멤버와 SIG Docs 승인자들은 지정된 풀 리퀘스트의 자동 병합을 방지하기 위해 코멘트를 추가할 수 있다(코멘트에 /hold 추가 또는 /lgtm 코멘트 보류).
  • 모든 쿠버네티스 멤버는 코멘트에 /lgtm 을 추가해서 lgtm 레이블을 추가할 수 있다.
  • SIG Docs 승인자들만이 코멘트에 /approve 를 추가해서 풀 리퀘스트를 병합할 수 있다. 일부 승인자들은 PR Wrangler 또는 SIG Docs 의장과 같은 특정 역할도 수행한다.

다음 내용

쿠버네티스 문서화에 기여하는 일에 대한 보다 많은 정보는 다음 문서를 참고한다.

7.4.1 - 역할과 책임

누구나 쿠버네티스에 기여할 수 있다. SIG Docs에 대한 기여가 커짐에 따라, 커뮤니티의 다양한 멤버십을 신청할 수 있다. 이러한 역할을 통해 커뮤니티 내에서 더 많은 책임을 질 수 있다. 각 역할마다 많은 시간과 노력이 필요하다. 역할은 다음과 같다.

  • 모든 사람: 쿠버네티스 문서에 정기적으로 기여하는 기여자
  • 멤버: 이슈를 할당, 심사하고 풀 리퀘스트에 대한 구속력 없는 리뷰를 제공할 수 있다.
  • 리뷰어: 문서의 풀 리퀘스트에 대한 리뷰를 리딩할 수 있으며 변경 사항에 대한 품질을 보증할 수 있다.
  • 승인자: 문서에 대한 리뷰를 리딩하고 변경 사항을 병합할 수 있다

모든 사람

GitHub 계정을 가진 누구나 쿠버네티스에 기여할 수 있다. SIG Docs는 모든 새로운 기여자를 환영한다!

모든 사람은 다음의 작업을 할 수 있다.

CLA에 서명 후에 누구나 다음을 할 수 있다.

  • 기존 콘텐츠를 개선하거나, 새 콘텐츠를 추가하거나, 블로그 게시물 또는 사례연구 작성을 위해 풀 리퀘스트를 연다.
  • 다이어그램, 그래픽 자산 그리고 포함할 수 있는 스크린캐스트와 비디오를 제작한다.

자세한 내용은 새로운 콘텐츠 기여하기를 참고한다.

멤버

멤버는 kubernetes/website 에 여러 개의 풀 리퀘스트를 제출한 사람이다. 멤버는 쿠버네티스 GitHub 조직의 회원이다.

멤버는 다음의 작업을 할 수 있다.

  • 모든 사람에 나열된 모든 것을 한다.

  • 풀 리퀘스트에 /lgtm 코멘트를 사용하여 LGTM(looks good to me) 레이블을 추가한다.

  • /hold 코멘트를 사용하여 풀 리퀘스트에 대한 병합을 차단한다.

  • /assign 코멘트를 사용하여 풀 리퀘스트에 리뷰어를 지정한다.

  • 풀 리퀘스트에 구속력 없는 리뷰를 제공한다.

  • 자동화를 사용하여 이슈를 심사하고 분류한다.

  • 새로운 기능에 대한 문서를 작성한다.

멤버 되기

최소 5개의 실질적인 풀 리퀘스트를 제출하고 다른 요구 사항을 충족시킨 후, 다음의 단계를 따른다.

  1. 멤버십을 후원해줄 두 명의 리뷰어 또는 승인자를 찾는다.

    슬랙의 #sig-docs 채널 또는 SIG Docs 메일링 리스트에서 후원을 요청한다.

  2. kubernetes/org 리포지터리에 GitHub 이슈를 등록한다. Organization Membership Request 이슈 템플릿을 사용한다.

  3. 후원자에게 GitHub 이슈를 알린다. 다음 중 하나를 수행할 수 있다.

    • 이슈에서 후원자의 GitHub 사용자 이름을 코멘트로 추가한다. (@<GitHub-username>)

    • 슬랙 또는 이메일을 사용해 이슈 링크를 후원자에게 보낸다.

      후원자는 +1 투표로 여러분의 요청을 승인할 것이다. 후원자가 요청을 승인하면, 쿠버네티스 GitHub 관리자가 여러분을 멤버로 추가한다. 축하한다!

      만약 멤버십이 수락되지 않으면 피드백을 받게 될 것이다. 피드백의 내용을 해결한 후, 다시 지원하자.

  4. 여러분의 이메일 계정으로 수신된 쿠버네티스 GitHub 조직으로의 초대를 수락한다.

리뷰어

리뷰어는 열린 풀 리퀘스트를 리뷰할 책임이 있다. 멤버 피드백과는 달리, 여러분은 리뷰어의 피드백을 반드시 해결해야 한다. 리뷰어는 @kubernetes/sig-docs-{language}-reviews GitHub 팀의 멤버이다.

리뷰어는 다음의 작업을 수행할 수 있다.

  • 모든 사람멤버에 나열된 모든 것을 수행한다.

  • 풀 리퀘스트 리뷰와 구속력 있는 피드백을 제공한다.

  • 코드에서 사용자 화면 문자열 편집

  • 코드 코멘트 개선

여러분은 SIG DOcs 리뷰어이거나, 특정 주제 영역의 문서에 대한 리뷰어일 수 있다.

풀 리퀘스트에 대한 리뷰어 할당

자동화 시스템은 모든 풀 리퀘스트에 대해 리뷰어를 할당한다. /assign [@_github_handle] 코멘트를 남겨 특정 사람에게 리뷰를 요청할 수 있다.

지정된 리뷰어가 PR에 코멘트를 남기지 않는다면, 다른 리뷰어가 개입할 수 있다. 필요에 따라 기술 리뷰어를 지정할 수도 있다.

/lgtm 사용하기

LGTM은 "Looks good to me"의 약자이며 풀 리퀘스트가 기술적으로 정확하고 병합할 준비가 되었음을 나타낸다. 모든 PR은 리뷰어의 /lgtm 코멘트가 필요하고 병합을 위해 승인자의 /approve 코멘트가 필요하다.

리뷰어의 /lgtm 코멘트는 구속력 있고 자동화 시스템이 lgtm 레이블을 추가하도록 트리거한다.

리뷰어 되기

요건을 충족하면, SIG Docs 리뷰어가 될 수 있다. 다른 SIG의 리뷰어는 SIG Docs의 리뷰어 자격에 반드시 별도로 지원해야 한다.

지원하려면, 다음을 수행한다.

  1. kubernetes/website 리포지터리 내 OWNERS_ALIASES 파일의 섹션에 여러분의 GitHub 사용자 이름을 추가하는 풀 리퀘스트를 연다.

  2. PR을 하나 이상의 SIG-Docs 승인자(sig-docs-{language}-owners 에 나열된 사용자 이름)에게 지정한다.

승인되면, SIG Docs 리더가 적당한 GitHub 팀에 여러분을 추가한다. 일단 추가되면, K8s-ci-robot이 새로운 풀 리퀘스트에서 리뷰어로 여러분을 할당하고 제안한다.

승인자

승인자는 병합하기 위해 풀 리퀘스트를 리뷰하고 승인한다. 승인자는 @kubernetes/sig-docs-{language}-owners GitHub 팀의 멤버이다.

승인자는 다음의 작업을 할 수 있다.

  • 모든 사람, 멤버 그리고 리뷰어 하위의 모든 목록을 할 수 있다.
  • 코멘트에 /approve 를 사용해서 풀 리퀘스트를 승인하고, 병합해서 기여자의 컨텐츠를 게시한다.
  • 스타일 가이드 개선을 제안한다.
  • 문서 테스트 개선을 제안한다.
  • 쿠버네티스 웹사이트 또는 다른 도구 개선을 제안한다.

PR에 이미 /lgtm 이 있거나, 승인자도 /lgtm 코멘트를 남긴다면, PR은 자동으로 병합된다. SIG Docs 승인자는 추가적인 기술 리뷰가 필요치 않는 변경에 대해서만 /lgtm 을 남겨야 한다.

풀 리퀘스트 승인

승인자와 SIG Docs 리더는 website 리포지터리로 풀 리퀘스트를 병합할 수 있는 유일한 사람들이다. 이것은 특정한 책임이 따른다.

  • 승인자는 PR들을 리포지터리에 병합하는 /approve 명령을 사용할 수 있다.

  • 제안된 변경이 컨트리뷰션 가이드 라인에 적합한지 확인한다.

    질문이 생기거나 확실하지 않다면 자유롭게 추가 리뷰를 요청한다.

  • PR을 /approve 하기 전에 Netlify 테스트 결과를 검토한다.

    승인 전에 반드시 Netlify 테스트를 통과해야 한다
  • 승인 전에 PR에 대한 Netlify 프리뷰 페이지를 방문하여, 제대로 보이는지 확인한다.

  • 주간 로테이션을 위해 PR Wrangler 로테이션 스케줄에 참여한다. SIG Docs는 모든 승인자들이 이 로테이션에 참여할 것으로 기대한다. 자세한 내용은 PR 랭글러(PR wrangler)를 참고한다.

승인자 되기

요구 사항을 충족하면 SIG Docs 승인자가 될 수 있다. 다른 SIG의 승인자는 SIG Docs의 승인자 자격에 대해 별도로 신청해야 한다.

지원하려면 다음을 수행한다.

  1. kubernetes/website 리포지터리 내 OWNERS_ALIASES 파일의 섹션에 자신을 추가하는 풀 리퀘스트를 연다.

  2. PR에 한 명 이상의 현재 SIG Docs 승인자를 지정한다.

승인되면, SIG Docs 리더가 적당한 GitHub 팀에 여러분을 추가한다. 일단 추가되면, @k8s-ci-robot이 새로운 풀 리퀘스트에서 승인자로 여러분을 할당하고 제안한다.

다음 내용

  • 모든 승인자가 교대로 수행하는 역할인 PR 랭글러에 대해 읽어보기

7.4.2 - PR 랭글러(PR Wrangler)

SIG Docs 승인자는 리포지터리에 대해 일주일 동안 교대로 풀 리퀘스트 관리를 수행한다.

이 섹션은 PR 랭글러의 의무에 대해 다룬다. 좋은 리뷰 제공에 대한 자세한 내용은 Reviewing changes를 참고한다.

의무

PR 랭글러는 일주일 간 매일 다음의 일을 해야 한다.

  • 매일 새로 올라오는 이슈를 심사하고 태그를 지정한다. SIG Docs가 메타데이터를 사용하는 방법에 대한 지침은 이슈 심사 및 분류를 참고한다.
  • 스타일콘텐츠 가이드를 준수하는지에 대해 열린(open) 풀 리퀘스트를 매일 리뷰한다.
    • 가장 작은 PR(size/XS)부터 시작하고, 가장 큰(size/XXL) PR까지 리뷰한다. 가능한 한 많은 PR을 리뷰한다.
  • PR 기여자들이 CLA에 서명했는지 확인한다.
    • CLA에 서명하지 않은 기여자에게 CLA에 서명하도록 알리려면 스크립트를 사용한다.
  • 제안된 변경 사항에 대한 피드백을 제공하고 다른 SIG의 멤버에게 기술 리뷰를 요청한다.
    • 제안된 콘텐츠 변경에 대해 PR에 인라인 제안(inline suggestion)을 제공한다.
    • 내용을 확인해야 하는 경우, PR에 코멘트를 달고 자세한 내용을 요청한다.
    • 관련 sig/ 레이블을 할당한다.
    • 필요한 경우, 파일의 머리말(front matter)에 있는 reviewers: 블록의 리뷰어를 할당한다.
  • PR을 병합하려면 승인을 위한 approve 코멘트를 사용한다. 준비가 되면 PR을 병합한다.
    • 병합하기 전에 PR은 다른 멤버의 /lgtm 코멘트를 받아야 한다.
    • [스타일 지침]을 충족하지 않지만 기술적으로는 정확한 PR은 수락하는 것을 고려한다. 스타일 문제를 해결하는 good first issue 레이블의 새로운 이슈를 올리면 된다.

랭글러를 위해 도움이 되는 GitHub 쿼리

다음의 쿼리는 랭글러에게 도움이 된다. 이 쿼리들을 수행하여 작업한 후에는, 리뷰할 나머지 PR 목록은 일반적으로 작다. 이 쿼리들은 특히 현지화 PR을 제외한다. 모든 쿼리는 마지막 쿼리를 제외하고 메인 브렌치를 대상으로 한다.

  • CLA 서명 없음, 병합할 수 없음: CLA에 서명하도록 기여자에게 상기시킨다. 봇과 사람이 이미 알렸다면, PR을 닫고 CLA에 서명한 후 PR을 열 수 있음을 알린다. 작성자가 CLA에 서명하지 않은 PR은 리뷰하지 않는다!
  • LGTM 필요: 멤버의 LGTM이 필요한 PR을 나열한다. PR에 기술 리뷰가 필요한 경우, 봇이 제안한 리뷰어 중 한 명을 지정한다. 콘텐츠에 대한 작업이 필요하다면, 제안하거나 인라인 피드백을 추가한다.
  • LGTM 보유, 문서 승인 필요: 병합을 위해 /approve 코멘트가 필요한 PR을 나열한다.
  • 퀵윈(Quick Wins): 명확한 결격 사유가 없는 메인 브랜치에 대한 PR을 나열한다. ([XS, S, M, L, XL, XXL] 크기의 PR을 작업할 때 크기 레이블에서 "XS"를 변경한다)
  • 메인 브랜치이외의 브랜치에 대한 PR: dev- 브랜치에 대한 것일 경우, 곧 출시될 예정인 릴리스이다. /assign @<meister's_github-username> 을 사용하여 문서 릴리스 관리자를 할당한다. 오래된 브랜치에 대한 PR인 경우, PR 작성자가 가장 적합한 브랜치를 대상으로 하고 있는지 여부를 파악할 수 있도록 도와준다.

랭글러를 위한 유용한 Prow 명령어

# 한글 레이블 추가
/language ko

# 둘 이상의 커밋인 경우 PR에 스쿼시 레이블 추가
/label tide/merge-method-squash

# Prow를 통해 PR 제목 변경(예: 진행 중인 작업(work-in-progress) [WIP] 또는 PR의 더 상세한 내용)
/retitle [WIP] <TITLE>

풀 리퀘스트를 종료하는 시기

리뷰와 승인은 PR 대기열을 최신 상태로 유지하는 도구 중 하나이다. 또 다른 도구는 종료(closure)이다.

다음의 상황에서 PR을 닫는다.

  • 작성자가 CLA에 2주 동안 서명하지 않았다.

    작성자는 CLA에 서명한 후 PR을 다시 열 수 있다. 이는 어떤 것도 CLA 서명없이 병합되지 않게 하는 위험이 적은 방법이다.

  • 작성자가 2주 이상 동안 코멘트나 피드백에 응답하지 않았다.

풀 리퀘스트를 닫는 것을 두려워하지 말자. 기여자는 진행 중인 작업을 쉽게 다시 열고 다시 시작할 수 있다. 종종 종료 통지는 작성자가 기여를 재개하고 끝내도록 자극하는 것이다.

풀 리퀘스트를 닫으려면, PR에 /close 코멘트를 남긴다.

7.5 - 레퍼런스 문서 개요

이 섹션은 쿠버네티스 레퍼런스 가이드를 생성하는 방법에 대해 설명한다.

레퍼런스 문서를 생성하려면, 다음의 가이드를 참고한다.

7.5.1 - 퀵스타트 가이드

이 문서에서는 update-imported-docs.py 스크립트를 사용하여 쿠버네티스 레퍼런스 문서를 생성하는 방법에 대해 설명한다. 이 스크립트는 특정 쿠버네티스 릴리스 버전에 대해 빌드 설정을 자동으로 수행하고 레퍼런스 문서를 생성한다.

시작하기 전에

필요 사항:

  • 리눅스 또는 macOS 로 구동되는 개발 환경이 필요하다.

  • 다음의 도구들이 설치되어 있어야 한다.

  • 위에 나열된 도구들 (예: Go 바이너리나 python) 을 사용할 수 있도록 PATH 환경 변수를 알맞게 설정해야 한다.

  • GitHub 저장소로 풀 리퀘스트를 생성하는 방법을 알고 있어야 한다. 이를 위해 kubernetes/website 저장소를 개인 계정으로 포크해야 한다. 더 자세한 내용은 로컬 포크에서 작업하기를 참조한다.

website 저장소 클론하기

개인 계정에 있는 포크 버전의 website 저장소가 GitHub에 있는 kubernetes/website 저장소(main 브랜치)의 최신 상태와 일치하는지 확인한 뒤, 개인 계정에 있는 포크 버전의 website 저장소를 로컬 개발 환경으로 클론한다.

mkdir github.com
cd github.com
git clone [email protected]:<your_github_username>/website.git

아래에서 사용될 '베이스 디렉터리'를 숙지해야 한다. 예를 들어 위에 안내된 대로 저장소를 클론했다면, 베이스 디렉터리는 github.com/website 가 된다. 이제 이 문서의 나머지 부분에서 <web-base> 라는 구문이 나오면 이 부분에 당신의 베이스 디렉터리를 대입하면 된다.

update-imported-docs 스크립트 개요

update-imported-docs.py 스크립트는 <web-base>/update-imported-docs/ 디렉터리에 존재한다.

이 스크립트는 다음 레퍼런스를 생성한다.

  • 구성요소 및 도구 레퍼런스 페이지
  • kubectl 명령어 레퍼런스
  • 쿠버네티스 API 레퍼런스

update-imported-docs.py 스크립트는 쿠버네티스 소스코드로부터 레퍼런스 문서를 생성한다. 스크립트가 실행되면 개발 머신의 /tmp 디렉터리 아래에 임시 디렉터리를 생성하고, 이 임시 디렉터리 아래에 레퍼런스 문서 생성에 필요한 kubernetes/kubernetes 저장소와 kubernetes-sigs/reference-docs 저장소를 클론하며, GOPATH 환경 변수를 이 임시 디렉터리로 지정한다. 또한 이 스크립트는 다음의 환경 변수를 설정한다.

  • K8S_RELEASE
  • K8S_ROOT
  • K8S_WEBROOT

스크립트가 정상적으로 실행되려면 인자 2개를 전달해야 한다.

  • 환경설정 YAML 파일 (reference.yml)
  • 쿠버네티스 릴리스 버전 (예: 1.17)

환경설정 파일은 generate-command 라는 필드를 포함하는데, 이 필드에는 kubernetes-sigs/reference-docs/Makefile 에 있는 Make 타겟들을 활용하여 빌드하는 일련의 과정이 명시되어 있다. K8S_RELEASE 환경 변수는 릴리스 버전을 결정한다.

update-imported-docs.py 스크립트는 다음의 과정을 수행한다.

  1. 환경설정 파일에 있는 관련 저장소를 클론한다. 레퍼런스 문서 생성을 위해 기본적으로는 kubernetes-sigs/reference-docs 저장소를 클론하도록 되어 있다.
  2. 클론한 안에서, 문서 생성에 필요한 사항을 준비하기 위한 명령어를 실행한 뒤, HTML 파일과 마크다운 파일을 생성한다.
  3. 생성된 HTML 파일과 마크다운 파일을 환경설정 파일에 명시된 규칙에 따라 <web-base> 로 복사한다.
  4. kubectl.md 에 있는 kubectl 명령어 링크들이 kubectl 명령어 레퍼런스 페이지의 올바른 섹션으로 연결되도록 업데이트한다.

생성된 파일이 <web-base> 아래에 복사되었으면, kubernetes/website 저장소로 풀 리퀘스트를 생성 할 수 있다.

환경설정 파일 형식

각 환경설정 파일은 레퍼런스 생성을 위해 필요한 여러 저장소의 정보를 담을 수 있다. 필요한 경우, 환경설정 파일을 직접 수정하여 사용할 수도 있다. 또는, 다른 그룹의 문서를 임포트하기 위해 새로운 환경설정 파일을 작성할 수도 있다. 다음은 환경설정 YAML 파일의 예시이다.

repos:
- name: community
  remote: https://github.com/kubernetes/community.git
  branch: master
  files:
  - src: contributors/devel/README.md
    dst: docs/imported/community/devel.md
  - src: contributors/guide/README.md
    dst: docs/imported/community/guide.md

이 도구에 의해 처리될 단일 페이지 마크다운 문서는 문서 스타일 가이드의 내용을 만족해야 한다.

reference.yml 환경설정 파일 다루기

<web-base>/update-imported-docs/reference.yml 환경설정 파일을 열어 수정할 수 있다. 레퍼런스 문서 생성을 위해 명령어들이 어떻게 사용되고 있는지 파악하지 못했다면, generate-command 필드의 내용은 수정하지 말아야 한다. 대부분의 경우 reference.yml 을 직접 수정해야 할 필요는 없다. 때때로, 업스트림 소스코드 업데이트 때문에 이 환경설정 파일을 수정해야 할 수도 있다. (예: Golang 버전 의존성, 서드파티 라이브러리 변경 등) 만약 스크립트 사용 시 빌드 문제가 있다면, 쿠버네티스 슬랙의 #sig-docs 채널에서 SIG-Docs 팀에 문의하면 된다.

reference.yml 환경설정 파일에서, files 필드는 srcdst 필드를 포함한다. src 필드에는 kubernetes-sigs/reference-docs 디렉터리 아래에 있는 생성된 마크다운 파일의 위치를 명시하고, dst 필드에는 이 파일을 kubernetes/website 디렉터리 아래의 어느 위치로 복사할지를 명시한다. 예시는 다음과 같다.

repos:
- name: reference-docs
  remote: https://github.com/kubernetes-sigs/reference-docs.git
  files:
  - src: gen-compdocs/build/kube-apiserver.md
    dst: content/en/docs/reference/command-line-tools-reference/kube-apiserver.md
  ...

만약 하나의 src 디렉터리에서 하나의 dst 디렉터리로 많은 파일이 복사되어야 한다면, src 필드에 와일드카드를 사용할 수 있다. 이 경우, dst 필드에는 단일 파일의 경로가 아니라 디렉터리의 경로를 명시해야 한다. 예시는 다음과 같다.

  files:
  - src: gen-compdocs/build/kubeadm*.md
    dst: content/en/docs/reference/setup-tools/kubeadm/generated/

update-imported-docs 도구 실행하기

다음과 같이 update-imported-docs.py 도구를 실행할 수 있다.

cd <web-base>/update-imported-docs
./update-imported-docs.py <configuration-file.yml> <release-version>

예를 들면 다음과 같다.

./update-imported-docs reference.yml 1.17

release.yml 환경설정 파일은 상대경로 링크를 수정하는 방법을 포함하고 있다. 임포트하는 파일 안에 있는 상대경로 링크를 수정하려면, gen-absolute-links 필드를 true 로 명시한다. 이에 대한 예시는 release.yml 에서 볼 수 있다.

kubernetes/website 의 변경사항을 커밋하기

다음의 명령을 실행하여, 스크립트에 의해 생성된 뒤 <web-base> 아래에 복사된 파일의 목록을 볼 수 있다.

cd <web-base>
git status

위의 명령을 실행하면 새로 추가된 파일과 수정된 파일의 목록을 볼 수 있다. 아래의 결과 예시는 업스트림 소스코드의 변경사항에 따라 다르게 나타날 수 있다.

생성된 구성요소 도구 레퍼런스

content/en/docs/reference/command-line-tools-reference/cloud-controller-manager.md
content/en/docs/reference/command-line-tools-reference/kube-apiserver.md
content/en/docs/reference/command-line-tools-reference/kube-controller-manager.md
content/en/docs/reference/command-line-tools-reference/kube-proxy.md
content/en/docs/reference/command-line-tools-reference/kube-scheduler.md
content/en/docs/reference/setup-tools/kubeadm/generated/kubeadm.md
content/en/docs/reference/kubectl/kubectl.md

생성된 kubectl 명령어 레퍼런스

static/docs/reference/generated/kubectl/kubectl-commands.html
static/docs/reference/generated/kubectl/navData.js
static/docs/reference/generated/kubectl/scroll.js
static/docs/reference/generated/kubectl/stylesheet.css
static/docs/reference/generated/kubectl/tabvisibility.js
static/docs/reference/generated/kubectl/node_modules/bootstrap/dist/css/bootstrap.min.css
static/docs/reference/generated/kubectl/node_modules/highlight.js/styles/default.css
static/docs/reference/generated/kubectl/node_modules/jquery.scrollto/jquery.scrollTo.min.js
static/docs/reference/generated/kubectl/node_modules/jquery/dist/jquery.min.js
static/docs/reference/generated/kubectl/css/font-awesome.min.css

생성된 쿠버네티스 API 레퍼런스 와 파일

static/docs/reference/generated/kubernetes-api/v1.23/index.html
static/docs/reference/generated/kubernetes-api/v1.23/js/navData.js
static/docs/reference/generated/kubernetes-api/v1.23/js/scroll.js
static/docs/reference/generated/kubernetes-api/v1.23/js/query.scrollTo.min.js
static/docs/reference/generated/kubernetes-api/v1.23/css/font-awesome.min.css
static/docs/reference/generated/kubernetes-api/v1.23/css/bootstrap.min.css
static/docs/reference/generated/kubernetes-api/v1.23/css/stylesheet.css
static/docs/reference/generated/kubernetes-api/v1.23/fonts/FontAwesome.otf
static/docs/reference/generated/kubernetes-api/v1.23/fonts/fontawesome-webfont.eot
static/docs/reference/generated/kubernetes-api/v1.23/fonts/fontawesome-webfont.svg
static/docs/reference/generated/kubernetes-api/v1.23/fonts/fontawesome-webfont.ttf
static/docs/reference/generated/kubernetes-api/v1.23/fonts/fontawesome-webfont.woff
static/docs/reference/generated/kubernetes-api/v1.23/fonts/fontawesome-webfont.woff2

git addgit commit 명령을 실행하여 추가/변경된 파일을 커밋한다.

풀 리퀘스트 만들기

kubernetes/website 저장소에 풀 리퀘스트를 등록한다. 등록한 풀 리퀘스트를 모니터하고, 리뷰 커멘트가 달리면 그에 대해 대응을 한다. 풀 리퀘스트가 머지될 때 까지 계속 모니터한다.

풀 리퀘스트가 머지된 뒤 몇 분이 지나면, 변경사항을 쿠버네티스 문서 홈페이지에서 확인할 수 있다.

다음 내용

수동으로 빌드 저장소를 설정하고 빌드 타겟을 실행하여 개별 레퍼런스 문서를 생성하려면, 다음의 가이드를 참고한다.

7.5.2 -

필요 사항:

  • 리눅스 또는 macOS 로 구동되는 개발 환경이 필요하다.

  • 다음의 도구들이 설치되어 있어야 한다.

  • 위에 나열된 도구들 (예: Go 바이너리나 python) 을 사용할 수 있도록 PATH 환경 변수를 알맞게 설정해야 한다.

  • GitHub 저장소로 풀 리퀘스트를 생성하는 방법을 알고 있어야 한다. 이를 위해 kubernetes/website 저장소를 개인 계정으로 포크해야 한다. 더 자세한 내용은 로컬 포크에서 작업하기를 참조한다.

7.6 - 문서 스타일 개요

이 섹션의 주제는 문서 작성 스타일, 컨텐츠 형식과 구성, 쿠버네티스 문서화에 적합하게 사용자 정의된 Hugo 사용 방법에 대한 가이드를 제공한다.

7.6.1 - 새로운 주제의 문서 작성

이 페이지는 쿠버네티스 문서에서 새로운 주제를 생성하는 방법을 보여준다.

시작하기 전에

PR 열기에 설명된 대로 쿠버네티스 문서 저장소의 포크(fork)를 생성하자.

페이지 타입 선택

새로운 주제 작성을 준비할 때는, 콘텐츠에 가장 적합한 페이지 타입을 고려하자.

페이지 타입 선택 지침
타입 설명
개념 개념 페이지는 쿠버네티스의 일부 측면을 설명한다. 예를 들어 개념 페이지는 쿠버네티스 디플로이먼트 오브젝트를 설명하고 배치, 확장 및 업데이트되는 동안 애플리케이션으로서 수행하는 역할을 설명할 수 있다. 일반적으로 개념 페이지는 일련의 단계가 포함되지 않지만 대신 태스크나 튜토리얼에 대한 링크를 제공한다. 개념 문서의 예로서 노드를 참조하자.
태스크 태스크 페이지는 단일 작업을 수행하는 방법을 보여준다. 아이디어는 독자가 페이지를 읽을 때 실제로 수행할 수 있는 일련의 단계를 제공하는 것이다. 태스크 페이지는 한 영역에 집중되어 있으면 짧거나 길 수 있다. 태스크 페이지에서 수행할 단계와 간단한 설명을 혼합하는 것은 괜찮지만, 긴 설명을 제공해야 하는 경우에는 개념 문서에서 수행해야 한다. 관련 태스크와 개념 문서는 서로 연결되어야 한다. 짧은 태스크 페이지의 예제는 저장소에 볼륨을 사용하도록 파드 구성을 참조하자. 더 긴 태스크 페이지의 예제는 활동성 및 준비성 프로브 구성을 참조하자.
튜토리얼 튜토리얼 페이지는 여러 쿠버네티스의 특징들을 하나로 묶어서 목적을 달성하는 방법을 보여준다. 튜토리얼은 독자들이 페이지를 읽을 때 실제로 할 수 있는 몇 가지 단계의 순서를 제공한다. 또는 관련 코드 일부에 대한 설명을 제공할 수도 있다. 예를 들어 튜토리얼은 코드 샘플의 연습을 제공할 수 있다. 튜토리얼에는 쿠버네티스의 특징에 대한 간략한 설명이 포함될 수 있지만 개별 기능에 대한 자세한 설명은 관련 개념 문서과 연결지어야 한다.

새 페이지 작성

작성하는 각각의 새 페이지에 대해 콘텐츠 타입을 사용하자. 문서 사이트는 새 콘텐츠 페이지를 작성하기 위한 템플리트 또는 Hugo archetypes을 제공한다. 새로운 타입의 페이지를 작성하려면, 작성하려는 파일의 경로로 hugo new 를 실행한다. 예를 들면, 다음과 같다.

hugo new docs/concepts/my-first-concept.md

제목과 파일 이름 선택

검색 엔진에서 찾을 키워드가 있는 제목을 선택하자. 제목에 있는 단어를 하이픈으로 구분하여 사용하는 파일 이름을 만들자. 예를 들어 HTTP 프록시를 사용하여 쿠버네티스 API에 접근 이라는 제목의 문서는 http-proxy-access-api.md라는 이름의 파일을 가진다. "쿠버네티스"가 이미 해당 주제의 URL에 있기 때문에 파일 이름에 "쿠버네티스" 를 넣을 필요가 없다. 예를 들면 다음과 같다.

   /docs/tasks/extend-kubernetes/http-proxy-access-api/

전문에 항목 제목 추가

문서에서 전문title 필드를 입력하자. 전문은 페이지 상단의 3중 점선 사이에 있는 YAML 블록이다. 여기 예시가 있다.

---
title: HTTP 프록시를 사용하여 쿠버네티스 API에 접근
---

디렉터리 선택

페이지 타입에 따라 새로운 파일을 다음 중 하나의 하위 디렉터리에 넣자.

  • /content/en/docs/tasks/
  • /content/en/docs/tutorials/
  • /content/en/docs/concepts/

파일을 기존 하위 디렉터리에 넣거나 새 하위 디렉터리에 넣을 수 있다.

목차에 항목 배치

목차는 문서 소스의 디렉터리 구조를 사용하여 동적으로 작성된다. /content/en/docs/ 아래의 최상위 디렉터리는 최상위 레벨 탐색 기능을 생성하고, 하위 디렉터리는 각각 목차에 항목을 갖는다.

각 하위 디렉터리에는 _index.md 파일이 있으며 이는 해당 하위 디렉터리의 컨텐츠에 대한 "홈" 페이지를 나타낸다. _index.md에는 템플릿이 필요없다. 그것은 하위 디렉터리의 항목에 대한 개요 내용을 포함할 수 있다.

디렉터리의 다른 파일들은 기본적으로 알파벳순으로 정렬된다. 이것은 거의 최적의 순서가 아니다. 하위 디렉터리에서 항목의 상대적 정렬을 제어하려면 가중치: 전문의 키를 정수로 설정하자. 일반적으로 우리는 나중에 항목을 추가하기 위해 10의 배수를 사용한다. 예를 들어 가중치가 10인 항목은 가중치가 20인 항목보다 우선한다.

문서에 코드 포함

문서에 코드를 포함시키려면 마크다운 코드 블록 구문을 사용하여 파일에 코드를 직접 삽입하자. 다음 경우에 권장된다. (전체 목록은 아님)

  • 이 코드는 kubectl get deploy mydeployment -o json | jq '.status'와 같은 명령어의 출력을 보여준다.
  • 이 코드는 시도해보기에 적절하지 않다. 예를 들어 특정 FlexVolume 구현에 따라 파드를 만들기 위해 YAML 파일을 포함할 수 있다.
  • 이 코드의 목적은 더 큰 파일의 일부를 강조하는 것이기 때문에 불완전한 예제다. 예를 들어 몇 가지 이유로 PodSecurityPolicy 를 사용자 정의 방법을 설명할 때 문서 파일에서 직접 짧은 요약 정보를 제공할 수 있다.
  • 이 코드는 사용자가 다른 이유로 시도하기 위한 것이 아니다. 예를 들어 kubectl edit 명령을 사용하여 리소스에 새 속성을 추가하는 방법을 설명할 때 추가할 만한 속성을 포함하는 간단한 예를 제공할 수 있다.

다른 파일의 코드 포함

문서에 코드를 포함시키는 또 다른 방법은 새로운 완전한 샘플 파일 (또는 샘플 파일 그룹)을 만든 다음 문서의 샘플을 참조하는 것이다. 일반적이고 재사용 가능하며 독자가 스스로 실행해 볼 수 있도록 하는 샘플 YAML 파일을 포함시키려면 이 방법을 사용하자.

YAML 파일과 같은 새로운 독립형 샘플 파일을 추가할 때 <LANG>/examples/ 의 하위 디렉터리 중 하나에 코드를 배치하자. 여기서 <LANG>은 주제에 관한 언어이다. 문서 파일에서 codenew 단축 코드(shortcode)를 사용하자.

{{< codenew file="<RELPATH>/my-example-yaml>" >}}

여기서 <RELPATH>examples 디렉터리와 관련하여 포함될 파일의 경로이다. 다음 Hugo 단축 코드(shortcode)는 /content/en/examples/pods/storage/gce-volume.yaml 에 있는 YAML 파일을 참조한다.

{{< codenew file="pods/storage/gce-volume.yaml" >}}

구성 파일에서 API 오브젝트를 작성하는 방법 표시

구성 파일을 기반으로 API 오브젝트를 생성하는 방법을 보여주려면 <LANG>/examples 아래의 하위 디렉터리 중 하나에 구성 파일을 배치하자.

문서에서 이 명령을 띄워보자.

kubectl create -f https://k8s.io/examples/pods/storage/gce-volume.yaml

이 기술을 사용하는 문서의 예로 단일 인스턴스 스테이트풀 어플리케이션 실행을 참조하자.

문서에 이미지 추가

이미지 파일을 /images 디렉터리에 넣는다. 기본 이미지 형식은 SVG 이다.

다음 내용

7.7 - 고급 기여

이 페이지에서는 당신이 새로운 콘텐츠에 기여하고 다른 사람의 작업을 리뷰하는 방법을 이해한다고 가정한다. 또한 기여하기 위한 더 많은 방법에 대해 배울 준비가 되었다고 가정한다. 이러한 작업 중 일부에는 Git 커맨드 라인 클라이언트와 다른 도구를 사용해야 한다.

개선 제안

SIG Docs 멤버는 개선을 제안할 수 있다.

한 동안 쿠버네티스 문서에 기여한 후에, 스타일 가이드, 컨텐츠 가이드, 문서 작성에 사용되는 툴체인, website 스타일, 풀 리퀘스트 리뷰와 병합 프로세스 또는 문서 작성의 다른 측면을 개선하기 위한 아이디어가 있을 수 있다. 투명성을 극대화하려면, 이러한 유형의 제안을 SIG Docs 회의나 kubernetes-sig-docs 메일링리스트에서 논의해야 한다. 또한, 현재의 작업 방식과 과거의 결정이 왜 획기적인 변경을 제안하기 전에 결정되었는지에 대한 맥락을 이해하는 데 실제로 도움이 될 수 있다. 현재의 문서 작업이 어떻게 진행되는지에 대한 질문의 답변을 얻는 가장 빠른 방법은 kubernetes.slack.com#sig-docs 슬랙 채널에 문의하는 것이다.

토론이 진행되고 원하는 결과에 SIG가 동의한 후에는, 제안한 변경 사항과 가장 적합한 방식으로 작업할 수 있다. 예를 들어, 스타일 가이드나 website의 기능을 업데이트하려면, sig-testing과 함께 작업이 필요할 수 있는 문서 테스트와 관련된 변경에 대해 풀 리퀘스트를 열어야 할 수 있다.

쿠버네티스 릴리스를 위한 문서 조정

SIG Docs 승인자는 쿠버네티스 릴리스에 대한 문서를 조정할 수 있다.

각 쿠버네티스 릴리스는 sig-release SIG(Special Interest Group)에 참여하는 사람들의 팀에 의해 조정된다. 특정 릴리스에 대한 릴리스 팀의 다른 구성원에는 전체 릴리스 리드와 sig-testing 및 기타 담당자가 포함된다. 쿠버네티스 릴리스 프로세스에 대한 자세한 내용은 https://github.com/kubernetes/sig-release를 참고한다.

특정 릴리스의 SIG Docs 담당자는 다음 작업을 조정한다.

  • 문서에 영향을 미치는 새로운 기능이나 변경된 기능이 있는지 기능-추적 스프레드시트를 모니터링한다. 특정 기능에 대한 문서가 릴리스를 위해 준비가 되지 않은 경우, 해당 기능이 릴리스 되지 않을 수 있다.
  • sig-release 미팅에 정기적으로 참석하고 릴리스에 대한 문서의 상태를 업데이트한다.
  • 기능 구현을 담당하는 SIG가 작성한 기능 문서를 리뷰하고 교정한다.
  • 릴리스 관련 풀 리퀘스트를 병합하고 릴리스에 대한 Git 기능 브랜치를 유지 보수한다.
  • 앞으로 이 역할을 수행하는 방법을 배우려는 다른 SIG Docs 기여자들을 멘토링한다. 이것을 "섀도잉"이라고 한다.
  • 릴리스에 대한 산출물이 공개될 때 릴리스와 관련된 문서 변경 사항을 공개한다.

릴리스 조정은 일반적으로 3-4개월의 책임이며, SIG Docs 승인자 사이에서 의무가 순환된다.

새로운 기여자 홍보대사로 봉사

SIG Docs 승인자는 새로운 기여자 홍보대사로 활동할 수 있다.

새로운 기여자 홍보대사는 SIG-Docs에 기여한 새 기여자를 환영하고, 새 기여자에게 PR을 제안하고, 첫 몇 번의 PR 제출을 통해 새 기여자를 멘토링한다.

새로운 기여자 홍보대사의 책임은 다음과 같다.

  • #sig-docs 슬랙 채널에서 새로운 기여자의 질문을 모니터링한다.
  • PR 랭글러와 협력하여 새로운 기여자에게 좋은 첫 이슈를 파악한다.
  • 문서 리포지터리에 대한 처음 몇 번의 PR을 통해 새로운 기여자를 멘토링한다.
  • 새로운 기여자가 쿠버네티스 멤버가 되기 위해 필요한 보다 복잡한 PR을 작성하도록 지원한다.
  • 쿠버네티스 멤버 가입을 위해 기여자를 후원한다.

현재 새로운 기여자 홍보대사는 각 SIG-Docs 회의와 쿠버네티스 #sig-docs 채널에서 발표된다.

새로운 기여자 후원

SIG Docs 리뷰어는 새로운 기여자를 후원할 수 있다.

새로운 기여자가 하나 이상의 쿠버네티스 리포지터리에 5개의 실질적인 풀 리퀘스트를 성공적으로 제출한 후에는 쿠버네티스 조직의 멤버십을 신청할 수 있다. 기여자의 멤버십은 이미 리뷰어인 두 명의 스폰서가 후원해야 한다.

새로운 문서 기여자는 쿠버네티스 슬랙 인스턴스의 #sig-docs 채널이나 SIG Docs 메일링리스트에서 스폰서를 요청할 수 있다. 신청자의 작업에 대해 확신이 있다면, 리뷰어는 신청자를 후원한다. 신청자가 멤버십 신청서를 제출할 때, 신청서에 "+1"로 코멘트를 남기고 신청자가 쿠버네티스 조직의 멤버십에 적합한 이유에 대한 세부 정보를 포함한다.

SIG 공동 의장으로 봉사

SIG Docs 승인자는 SIG Docs의 공동 의장 역할을 할 수 있다.

전제 조건

승인자는 공동 의장이 되려면 다음의 요구 사항을 충족해야 한다.

  • 6개월 이상 SIG Docs 승인자로 활동한다.
  • 쿠버네티스 문서 릴리스 주도 또는 두 개의 릴리스에서 섀도잉을 수행한다.
  • SIG Docs 워크플로와 툴링을 이해한다(git, Hugo, 현지화, 블로그 하위 프로젝트).
  • k/org의 팀, k/community의 프로세스, k/test-infra의 플러그인 및 SIG 아키텍처의 역할을 포함하여 다른 쿠버네티스 SIG와 리포지터리가 SIG Docs 워크플로에 미치는 영향을 이해한다.
  • 최소 6개월 동안 일주일에 5시간 이상(대부분 더)을 역할에 책임진다.

책임

공동 의장의 역할은 서비스 중 하나이다. 공동 의장은 기여자 역량 확보, 프로세스와 정책 처리, 회의 예약과 진행, PR 랭글러 스케줄링, 쿠버네티스 커뮤니티의 문서에 대한 지지, 쿠버네티스 릴리스 주기에서 성공적으로 문서화되는지 확인하고, SIG Docs를 효과적인 우선순위에 놓이도록 집중한다.

다음과 같은 책임을 가진다.

  • SIG Docs가 우수한 문서화를 통해 개발자의 행복을 극대화하는 데 집중한다.
  • 스스로가 커뮤니티 행동 강령을 준수하여 예를 보이고, SIG 멤버들이 지킬 수 있도록 책임을 진다.
  • 기여에 대한 새로운 지침을 확인하여 SIG에 대한 모범 사례를 배우고 설정한다.
  • SIG 회의를 예약하고 진행한다. 주간 상태 업데이트, 브랜치별 회고/기획 세션과 필요에 따라 그 외 세션을 진행한다.
  • KubeCon 이벤트 및 기타 컨퍼런스에서 문서 스프린트를 스케줄링하고 진행한다.
  • CNCF와 플래티넘 파트너인 Google, Oracle, Azure, IBM 및 Huawei를 통해 SIG Docs를 대신하여 채용과 지지를 보낸다.
  • SIG를 원활하게 운영한다.

효과적인 회의 운영

효과적으로 회의를 예약하고 진행하기 위해, 이 지침은 수행할 작업, 수행 방법과 이유를 보여준다.

커뮤니티 행동 강령을 지킨다.

  • 정중함을 유지하고, 포괄적인 언어로, 정중하고 포괄적인 토론을 한다.

명확한 안건을 설정한다.

  • 주제의 명확한 안건을 설정한다.
  • 안건을 미리 게시한다.

주간 회의의 경우, 지난 주 회의록을 회의록의 "지난 회의" 섹션에 복사하여 붙여 넣는다.

정확한 회의록에 대해 공동 작업한다.

  • 회의의 토론 내용을 기록한다.
  • 회의록 작성자의 역할을 위임한다.

조치 항목을 명확하고 정확하게 지정한다.

  • 조치 항목, 조치 항목에 할당된 멤버 그리고 예상 완료 날짜를 기록한다.

필요에 따라 완급을 조절한다.

  • 토론이 안건에서 벗어난 경우, 참가자의 초점을 현재의 주제로 다시 맞춘다.
  • 토론에 집중하고 사람들의 시간을 존중하면서 다양한 토론 스타일을 위한 공간을 확보한다.

사람들의 시간을 존중한다.

정시에 회의를 시작하고 끝낸다.

줌(Zoom)을 효과적으로 사용한다.

줌에서 호스트 역할 신청

줌에서 회의 녹화

녹화를 시작할 준비가 되면, Record to Cloud를 클릭한다.

녹화를 중지하려면, Stop을 클릭한다.

비디오가 자동으로 유튜브에 업로드된다.

7.8 - 사이트 분석 보기

이 페이지는 kubernetes.io 사이트 분석을 제공하는 대시보드에 대한 정보를 담고 있다.

대시보드 보기.

이 대시보드는 Google Data Studio를 사용하여 구축되었으며 kubernetes.io에서 Google Analytics를 사용하여 수집한 정보를 보여준다.

대시보드 사용

기본적으로 대시보드는 지난 30일 동안 수집된 모든 데이터의 분석을 제공한다. 날짜 선택을 통해 특정 날짜 범위의 데이터를 볼 수 있다. 그 외 필터링 옵션을 사용하면, 사용자의 위치, 사이트에 접속하는데 사용된 장치, 번역된 문서 언어 등을 기준으로 데이터를 확인할 수 있다.

이 대시보드에 문제가 있거나 개선을 요청하려면, 이슈를 오픈 한다.

7.9 - 쿠버네티스 문서 한글화 가이드

쿠버네티스 문서 한글화를 위한 가이드

팀 마일스톤 관리

쿠버네티스 문서 한글화팀은 커뮤니티의 현지화 가이드에 따라 한글화를 위한 팀 마일스톤과 개발 브랜치를 관리한다. 본 섹션은 한글화팀의 팀 마일스톤 관리에 특화된 내용을 다룬다.

한글화팀은 master 브랜치에서 분기한 개발 브랜치를 사용한다. 개발 브랜치 이름은 다음과 같은 구조를 갖는다.

dev-<소스 버전>-ko.<팀 마일스톤>

개발 브랜치는 약 2주에서 3주 사이의 팀 마일스톤 기간 동안 공동의 작업을 위해 사용되며, 팀 마일스톤이 종료될 때 원 브랜치로 병합(merge)된다.

업스트림(upstream)의 릴리스 주기(약 3개월)에 따라 다음 버전으로 마일스톤을 변경하는 시점에는 일시적으로 release-<소스 버전> 브랜치를 원 브랜치로 사용하는 개발 브랜치를 추가로 운영한다.

한글화팀의 정기 화상 회의 일정과 팀 마일스톤 주기는 대체로 일치하며, 정기 회의를 통해 팀 마일스톤마다 PR 랭글러(wrangler)를 지정한다.

한글화팀의 PR 랭글러가 갖는 의무는 업스트림의 PR 랭글러가 갖는 의무와 유사하다. 단, 업스트림의 PR 랭글러와는 달리 승인자가 아니어도 팀 마일스톤의 PR 랭글러가 될 수 있다. 그래서, 보다 상위 권한이 필요한 업무가 발생한 경우, PR 랭글러는 해당 권한을 가진 한글화팀 멤버에게 처리를 요청한다.

업스트림의 PR 랭글러에게 유용한 GitHub 쿼리를 기반으로 작성한, 한글화팀의 PR 랭글러에게 유용한 쿼리를 아래에 나열한다.

팀 마일스톤 일정과 PR 랭글러는 커뮤니티 슬랙 내 #kubernetes-docs-ko 채널에 공지된다.

문체 가이드

높임말

평어체 사용을 원칙으로 하나, 일부 페이지(예: https://kubernetes.io/ko)에 한해 예외적으로 높임말을 사용한다.

명령형

어떤 일을 하도록 청자에게 요구하는 명령형 표현은 간결하고 부드러운 표현으로 정확한 의미를 전달하기 위해 어떤 일을 함께 하기를 요청하는 청유형 또는 객관적으로 진술하는 평어체로 번역한다.

명령형 문장 번역체
사이트를 방문하라 사이트를 방문하자 (청유형)
가이드를 참고하라 가이드를 참고한다 (평어체)

번역체 지양

우리글로서 자연스럽고 무리가 없도록 번역한다.

번역체 자연스러운 문장
-되어지다 (이중 피동 표현) -되다
짧은 다리를 가진 돼지 다리가 짧은 돼지
그는 그의 손으로 숟가락을 들어 그의 밥을 먹었다 그는 손으로 숟가락을 들어 밥을 먹었다
접시를 씻고 설거지를 하고
가게에 배들, 사과들, 복숭아들이 있다 (과다한 복수형) 가게에 배, 사과, 복숭아들이 있다

문서 코딩 가이드

가로폭은 원문을 따름

유지보수의 편의를 위해서 원문의 가로폭을 따른다.

즉, 원문이 한 문단을 줄바꿈하지 않고 한 행에 길게 기술했다면 한글화 시에도 한 행에 길게 기술하고, 원문이 한 문단을 줄바꿈해서 여러 행으로 기술한 경우에는 한글화 시에도 가로폭을 원문과 비슷하게 유지한다.

리뷰어 삭제

때때로 원문의 코드 상단에 리뷰어가 명시되어 있는 경우가 있다. 일반적으로 원문 페이지의 리뷰어가 한글화 된 페이지를 리뷰하기 어려우므로 리소스 메타데이터에서 리뷰어 관련 코드를 삭제한다.

아래는 리뷰어 관련 코드를 삭제하는 예시를 보여준다.

- reviews:
- - reviewer1
- - reviewer
- title: Kubernetes Components
+
+
+
+ title: 쿠버네티스 컴포넌트
content_type: concept
weight: 10

용어 한글화 가이드

용어 한글화의 일반적 방침

영문 용어를 한글화할 때는 다음의 우선 순위를 따르나, 자연스럽지 않은 용어를 무리하게 선택하지는 않는다.

  • 한글 단어
    • 순 우리말 단어
    • 한자어 (예: 운영 체제), 외래어 (예: 쿠버네티스, 파드)
  • 한영 병기 (예: 훅(hook))
  • 영어 단어 (예: Kubelet)

단, 자연스러움을 판단하는 기준은 주관적이므로 한글화 용어집을 준용하고 기존에 번역된 문서를 참고한다.

API 오브젝트 용어 한글화 방침

일반적으로 kubectl api-resources 결과의 kind 에 해당하는 API 오브젝트는 국립국어원 외래어 표기법에 따라 한글로 표기하고 영문을 병기한다. 예를 들면 다음과 같다.

API 오브젝트(kind) 한글화(외래어 표기 및 영문 병기)
ClusterRoleBinding 클러스터롤바인딩(ClusterRoleBinding)
ConfigMap 컨피그맵(ConfigMap)
Deployment 디플로이먼트(Deployment)
PersistentVolumeClaim 퍼시스턴트볼륨클레임(PersistentVolumeClaim)
... ...

kind 에 속하는 API 오브젝트 중에서도 일부는 표현의 간결함을 위해 한영병기를 하지 않는 등의 예외가 있으며, 예외에 대해서는 한글화 용어집에 등록된 방식을 준용한다. 예를 들면 다음과 같다.

API 오브젝트(kind) 한글화(외래어 표기)
Pod 파드
Service 서비스
... ...

kind 에 속하지 않는 API 오브젝트는, 한글화 용어집에 등록된 용어인 경우를 제외하고, 모두 원문 그대로 표기한다. 예를 들면 다음과 같다.

API 오브젝트(kind가 아닌 경우) 한글화(원문 유지)
ClusterRoleBindingList ClusterRoleBindingList
ClusterRoleList ClusterRoleList
ConfigMapEnvSource ConfigMapEnvSource
ConfigMapKeySelector ConfigMapKeySelector
PersistentVolumeClaimList PersistentVolumeClaimList
PersistentVolumeClaimSpec PersistentVolumeClaimSpec
... ...

기능 게이트(feature gate) 한글화 방침

쿠버네티스의 기능 게이트를 의미하는 용어는 한글화하지 않고 원문 형태를 유지한다.

기능 게이트의 예시는 다음과 같다.

  • Accelerators
  • AdvancedAuditing
  • AffinityInAnnotations
  • AllowExtTrafficLocalEndpoints
  • ...

전체 기능 게이트 목록은 여기를 참고한다.

한글화 용어집 정보

쿠버네티스 한글화 용어집은 한글화된 쿠버네티스 문서의 일관성을 위해서 각 영문 용어에 대한 한글화 방법을 제시한다. 한글화 용어집에 포함된 용어는 쿠버네티스 문서 한글화팀 회의를 통해 결정되었으며, 본 문서에 대한 ISSUE 및 PR을 통해서 개선한다.

한글화 용어집 개선 가이드

한글화 용어집의 개선(추가, 수정, 삭제 등)을 위한 과정은 다음과 같다.

  1. 컨트리뷰터가 개선이 필요한 용어을 파악하면, ISSUE를 생성하여 개선 필요성을 공유하거나 master 브랜치에 PR을 생성하여 개선된 용어를 제안한다.

  2. 개선 제안에 대한 논의는 ISSUE 및 PR을 통해서 이루어지며, 한글화팀 회의를 통해 확정한다.

  3. 리뷰어 및 승인자는 작업 중인 한글화 작업 브랜치(예: dev-1.17-ko.3)에 영향을 최소화 하기 위해서, 신규 한글화 작업 브랜치(예: dev-1.17-ko.4) 생성 시점에 맞춰 확정된 PR을 승인한다. 개선된 한글화 용어집은 신규 한글화 작업 브랜치부터 적용한다.

  4. 개선된 한글화 용어집에 따라 기존의 한글 문서에 대한 업데이트가 필요하며, 컨트리뷰션을 통해서 업데이트를 진행한다.

한글화 용어집

English 한글 비고
Access 접근
Active Active 잡의 상태
Active Job 액티브 잡
Addons 애드온
admission controller 어드미션 컨트롤러
Age 기간
Allocation 할당량
alphanumeric 영숫자
Annotation 어노테이션
APIService API서비스(APIService) API 오브젝트인 경우
App
Appendix 부록
Application 애플리케이션
Args Args 약어의 형태이므로 한글화하지 않고 영문 표기
array 배열
autoscaler 오토스케일러
availability zone 가용성 영역(availability zone)
bare pod 베어(bare) 파드
beta 베타
Binding 바인딩(Binding) API 오브젝트인 경우
boilerplate 상용구
Boot 부트
Build 빌드
Cache 캐시
Calico 캘리코(Calico)
canary 카나리(canary) 릴리스 방식에 관련한 용어인 경우에 한함
cascading 캐스케이딩(cascading)
CertificateSigningRequest CertificateSigningRequest API 오브젝트인 경우
character set 캐릭터 셋
Charts 차트
checkpoint 체크포인트
Cilium 실리움(Cilium)
CLI CLI
Cluster 클러스터
ClusterRole 클러스터롤(ClusterRole) API 오브젝트인 경우
ClusterRoleBinding 클러스터롤바인딩(ClusterRoleBinding) API 오브젝트인 경우
Command Line Tool 커맨드라인 툴
ComponentStatus 컴포넌트스테이터스(ComponentStatus) API 오브젝트인 경우
ConfigMap 컨피그맵(ConfigMap) API 오브젝트인 경우
configuration 구성, 설정
Connection 연결
containerized 컨테이너화 된
Context 컨텍스트
Control Plane 컨트롤 플레인
controller 컨트롤러
ControllerRevision 컨트롤러리비전(ControllerRevision) API 오브젝트인 경우
cron job 크론 잡
CronJob 크론잡(CronJob) API 오브젝트인 경우
CSIDriver CSI드라이버(CSIDriver) API 오브젝트인 경우
CSINode CSI노드(CSINode) API 오브젝트인 경우
custom metrics 사용자 정의 메트릭
custom resource 사용자 정의 리소스
CustomResourceDefinition 커스텀리소스데피니션(CustomResourceDefinition) API 오브젝트인 경우
Daemon 데몬
DaemonSet 데몬셋(DaemonSet) API 오브젝트인 경우
Dashboard 대시보드
Data Plane 데이터 플레인
Deployment 디플로이먼트(Deployment) API 오브젝트인 경우
deprecated 사용 중단(deprecated)
descriptor 디스크립터, 식별자
Desired number of pods 의도한 파드의 수
Desired State 의도한 상태
disruption 중단(disruption)
distros 배포판
Docker 도커
Dockerfile Dockerfile
Docker Swarm Docker Swarm
Downward API 다운워드(Downward) API
draining 드레이닝(draining)
egress 이그레스, 송신(egress)
endpoint 엔드포인트
EndpointSlice 엔드포인트슬라이스(EndpointSlice) API 오브젝트인 경우
Endpoints 엔드포인트(Endpoints) API 오브젝트인 경우
entry point 진입점
Event 이벤트(Event) API 오브젝트인 경우
evict 축출하다
eviction 축출
Exec Exec
expose 노출시키다
extension 익스텐션(extension)
Failed Failed 파드의 상태에 한함
Federation 페더레이션
field 필드
Flannel 플란넬(Flannel)
form 형식
Google Compute Engine Google Compute Engine
hash 해시
headless 헤드리스
health check 헬스 체크
Heapster 힙스터(Heapster)
Heartbeat 하트비트
Homebrew Homebrew
hook 훅(hook)
Horizontal Pod Autoscaler Horizontal Pod Autoscaler 예외적으로 API 오브젝트에 대해 외래어 표기법 적용하지 않고 원문 그대로 표기
hosted zone 호스팅 영역
hostname 호스트네임
Huge page Huge page
Hypervisor 하이퍼바이저
idempotent 멱등성
Image 이미지
Image Pull Secrets 이미지 풀(Pull) 시크릿
Ingress 인그레스(Ingress) API 오브젝트인 경우
IngressClass 인그레스클래스(IngressClass) API 오브젝트인 경우
Init Container 초기화 컨테이너
Instance group 인스턴스 그룹
introspection 인트로스펙션(introspection)
Istio 이스티오(Istio)
Job 잡(Job) API 오브젝트인 경우
kube-proxy kube-proxy
Kubelet Kubelet
Kubernetes 쿠버네티스
Kube-router Kube-router
label 레이블
Lease 리스(Lease) API 오브젝트인 경우
lifecycle 라이프사이클
LimitRange 리밋레인지(LimitRange) API 오브젝트인 경우
limit 한도(limit) 리소스의 개수나 용량을 한정하기 위한 수치로 사용된 경우 선택적으로 사용 (API 오브젝트의 속성으로 limit을 사용한 경우는 가능한 영문 유지)
Linux 리눅스
load 부하
LocalSubjectAccessReview 로컬서브젝트액세스리뷰(LocalSubjectAccessReview) API 오브젝트인 경우
Log 로그
loopback 루프백(loopback)
Lost Lost 클레임의 상태에 한함
Machine 머신
manifest 매니페스트
Master 마스터
metadata 메타데이터
metric 메트릭
masquerading 마스커레이딩
Minikube Minikube
Mirror pod 미러 파드(mirror pod)
monitoring 모니터링
multihomed 멀티홈드(multihomed)
MutatingWebhookConfiguration MutatingWebhookConfiguration API 오브젝트인 경우
naked pod 네이키드(naked) 파드
Namespace 네임스페이스(Namespace) API 오브젝트인 경우
netfilter 넷필터(netfilter)
NetworkPolicy 네트워크폴리시(NetworkPolicy) API 오브젝트인 경우
Node 노드(Node) API 오브젝트인 경우
node lease 노드 리스(lease)
Object 오브젝트
Orchestrate 오케스트레이션하다
Output 출력
parameter 파라미터
patch 패치
Pending Pending 파드, 클레임의 상태에 한함
PersistentVolume 퍼시스턴트볼륨(PersistentVolume) API 오브젝트인 경우
PersistentVolumeClaim 퍼시스턴트볼륨클레임(PersistentVolumeClaim) API 오브젝트인 경우
pipeline 파이프라인
placeholder pod 플레이스홀더(placeholder) 파드
Pod 파드 API 오브젝트인 경우에도 표현의 간결함을 위해 한영병기를 하지 않음
Pod Preset 파드 프리셋
PodAntiAffinity 파드안티어피니티(PodAntiAffinity)
PodDisruptionBudget PodDisruptionBudget API 오브젝트인 경우
PodSecurityPolicy 파드시큐리티폴리시(PodSecurityPolicy) API 오브젝트인 경우
PodTemplate 파드템플릿(PodTemplate) API 오브젝트인 경우
postfix 접미사
prefix 접두사
PriorityClass 프라이어리티클래스(PriorityClass) API 오브젝트인 경우
Privileged 특권을 가진(privileged)
Prometheus 프로메테우스
proof of concept 개념 증명
Pull Request 풀 리퀘스트
Pull Secret Credentials 풀(Pull) 시크릿 자격증명
QoS Class QoS 클래스
Quota 쿼터
readiness gate 준비성 게이트(readiness gate)
readiness probe 준비성 프로브(readiness probe)
Ready Ready
Reclaim Policy 반환 정책
redirect 리다이렉트(redirect)
redirection 리다이렉션
Registry 레지스트리
Release 릴리스
ReplicaSet 레플리카셋(ReplicaSet) API 오브젝트인 경우
replicas 레플리카
ReplicationController 레플리케이션컨트롤러(ReplicationController) API 오브젝트인 경우
repository 리포지터리
request 요청(request) 리소스의 개수나 용량에 대한 요청 수치를 표현하기 위해 사용된 경우 선택적으로 사용 (API 오브젝트 속성으로 request를 사용한 경우는 가능한 영문을 유지)
resource 리소스
ResourceQuota 리소스쿼터(ResourceQuota) API 오브젝트인 경우
return 반환하다
revision 리비전
Role 롤(Role) API 오브젝트인 경우
RoleBinding 롤바인딩(RoleBinding) API 오브젝트인 경우
rollback 롤백
rolling update 롤링 업데이트
rollout 롤아웃
Romana 로마나(Romana)
Running Running 파드의 상태에 한함
runtime 런타임
RuntimeClass 런타임클래스(RuntimeClass) API 오브젝트인 경우
Scale 스케일
Secret 시크릿(Secret) API 오브젝트인 경우
segment 세그먼트
Selector 셀렉터
Self-healing 자가 치유
SelfSubjectAccessReview 셀프서브젝트액세스리뷰(SelfSubjectAccessReview) API 오브젝트인 경우
SelfSubjectRulesReview SelfSubjectRulesReview API 오브젝트이지만 용어를 구성하는 단어 중 복수형 Rules를 '룰스'로 외래어 표기하는 경우 한국어 독자에게 다소 생경할 수 있어 예외적으로 영문 용어를 사용함
Service 서비스 API 오브젝트인 경우에도 표현의 간결함을 위해 한영병기를 하지 않음
ServiceAccount 서비스어카운트(ServiceAccount) API 오브젝트인 경우
service discovery 서비스 디스커버리
service mesh 서비스 메시
Session 세션
Session Affinity 세션 어피니티(Affinity)
Setting 세팅
Shell
Sign In 로그인
Sign Out 로그아웃
skew 차이(skew)
snippet 스니펫(snippet)
spec 명세, 스펙, 사양
specification 명세
StatefulSet 스테이트풀셋(StatefulSet) API 오브젝트인 경우
stateless 스테이트리스
Static pod 스태틱(static) 파드
StorageClass 스토리지클래스(StorageClass) API 오브젝트인 경우
SubjectAccessReview 서브젝트액세스리뷰(SubjectAccessReview) API 오브젝트인 경우
Sub-Object 서브-오브젝트
support 지원
Surge 증가율 롤링업데이트 전략에 한함
System 시스템
taint 테인트(taint)
Task 태스크
Terminated Terminated 파드의 상태에 한함
TokenReview 토큰리뷰(TokenReview) API 오브젝트인 경우
tolerations 톨러레이션(toleration)
Topology spread constraints 토폴로지 분배 제약 조건
Tools 도구
traffic 트래픽
Type 타입
ubuntu 우분투
use case 유스케이스
userspace 유저스페이스(userspace)
Utilization 사용량, 사용률
ValidatingWebhookConfiguration ValidatingWebhookConfiguration API 오브젝트인 경우
verbosity 로그 상세 레벨(verbosity)
virtualization 가상화
Volume 볼륨
VolumeAttachment 볼륨어태치먼트(VolumeAttachment) API 오브젝트인 경우
Waiting Waiting 파드의 상태에 한함
Walkthrough 연습
Weave-net 위브넷(Weave Net) Weaveworks 사의 솔루션 공식 명칭은 'Weave Net'이므로 한영병기 시 공식 명칭 사용
Windows 윈도우
Worker 워커 노드의 형태에 한함
Workload 워크로드
YAML YAML