개념 섹션을 통해 쿠버네티스 시스템을 구성하는 요소와 클러스터를 표현하는데 사용되는 추상 개념에 대해 배우고 쿠버네티스가 작동하는 방식에 대해 보다 깊이 이해할 수 있다.
이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.
개념
- 1: 쿠버네티스란 무엇인가?
- 1.1: 쿠버네티스 컴포넌트
- 1.2: 쿠버네티스 API
- 1.3: 쿠버네티스 오브젝트로 작업하기
- 1.3.1: 쿠버네티스 오브젝트 이해하기
- 1.3.2: 쿠버네티스 오브젝트 관리
- 1.3.3: 오브젝트 이름과 ID
- 1.3.4: 레이블과 셀렉터
- 1.3.5: 네임스페이스
- 1.3.6: 어노테이션
- 1.3.7: 필드 셀렉터
- 1.3.8: 파이널라이저
- 1.3.9: 권장 레이블
- 2: 클러스터 아키텍처
- 2.1: 노드
- 2.2: 컨트롤 플레인-노드 간 통신
- 2.3: 리스(Lease)
- 2.4: 컨트롤러
- 2.5: 클라우드 컨트롤러 매니저
- 2.6: 컨테이너 런타임 인터페이스(CRI)
- 2.7: 가비지(Garbage) 수집
- 3: 컨테이너
- 3.1: 이미지
- 3.2: 컨테이너 환경 변수
- 3.3: 런타임클래스(RuntimeClass)
- 3.4: 컨테이너 라이프사이클 훅(Hook)
- 4: 쿠버네티스에서의 윈도우
- 5: 워크로드
- 5.1: 파드
- 5.1.1: 파드 라이프사이클
- 5.1.2: 초기화 컨테이너
- 5.1.3: 중단(disruption)
- 5.1.4: 임시(Ephemeral) 컨테이너
- 5.1.5: 사용자 네임스페이스
- 5.1.6: 다운워드(Downward) API
- 5.2: 워크로드 리소스
- 5.2.1: 디플로이먼트
- 5.2.2: 레플리카셋
- 5.2.3: 스테이트풀셋
- 5.2.4: 데몬셋
- 5.2.5: 잡
- 5.2.6: 완료된 잡 자동 정리
- 5.2.7: 크론잡
- 5.2.8: 레플리케이션 컨트롤러
- 6: 서비스, 로드밸런싱, 네트워킹
- 6.1: 서비스
- 6.2: 인그레스(Ingress)
- 6.3: 인그레스 컨트롤러
- 6.4: 엔드포인트슬라이스
- 6.5: 네트워크 정책
- 6.6: 서비스 및 파드용 DNS
- 6.7: IPv4/IPv6 이중 스택
- 6.8: 토폴로지 인지 힌트
- 6.9: 윈도우에서의 네트워킹
- 6.10: 서비스 내부 트래픽 정책
- 6.11: 서비스 클러스터IP 할당
- 6.12: 토폴로지 키를 사용하여 토폴로지-인지 트래픽 라우팅
- 7: 스토리지
- 7.1: 볼륨
- 7.2: 퍼시스턴트 볼륨
- 7.3: 프로젝티드 볼륨
- 7.4: 임시 볼륨
- 7.5: 스토리지 클래스
- 7.6: 동적 볼륨 프로비저닝
- 7.7: 볼륨 스냅샷
- 7.8: 볼륨 스냅샷 클래스
- 7.9: CSI 볼륨 복제하기
- 7.10: 스토리지 용량
- 7.11: 노드 별 볼륨 한도
- 7.12: 볼륨 헬스 모니터링
- 7.13: 윈도우 스토리지
- 8: 구성
- 8.1: 구성 모범 사례
- 8.2: 컨피그맵(ConfigMap)
- 8.3: 시크릿(Secret)
- 8.4: 파드 및 컨테이너 리소스 관리
- 8.5: kubeconfig 파일을 사용하여 클러스터 접근 구성하기
- 8.6: 윈도우 노드의 자원 관리
- 9: 보안
- 9.1: 클라우드 네이티브 보안 개요
- 9.2: 파드 시큐리티 스탠다드
- 9.3: 파드 시큐리티 어드미션
- 9.4: 파드 시큐리티 폴리시
- 9.5: 윈도우 노드에서의 보안
- 9.6: 쿠버네티스 API 접근 제어하기
- 9.7: 역할 기반 접근 제어 (RBAC) 모범 사례
- 9.8: 쿠버네티스 시크릿 모범 사례
- 9.9: 멀티 테넌시(multi-tenancy)
- 10: 정책
- 10.1: 리밋 레인지(Limit Range)
- 10.2: 리소스 쿼터
- 10.3: 프로세스 ID 제한 및 예약
- 10.4: 노드 리소스 매니저
- 11: 스케줄링, 선점(Preemption), 축출(Eviction)
- 11.1: 쿠버네티스 스케줄러
- 11.2: 노드에 파드 할당하기
- 11.3: 파드 오버헤드
- 11.4: 파드 스케줄링 준비성(readiness)
- 11.5: 파드 토폴로지 분배 제약 조건
- 11.6: 테인트(Taints)와 톨러레이션(Tolerations)
- 11.7: 스케줄러 성능 튜닝
- 11.8: 리소스 빈 패킹(bin packing)
- 11.9: 파드 우선순위(priority)와 선점(preemption)
- 11.10: 노드-압박 축출
- 11.11: API를 이용한 축출(API-initiated Eviction)
- 12: 클러스터 관리
- 12.1: 인증서
- 12.2: 리소스 관리
- 12.3: 클러스터 네트워킹
- 12.4: 로깅 아키텍처
- 12.5: 쿠버네티스 시스템 컴포넌트에 대한 메트릭
- 12.6: 시스템 로그
- 12.7: 쿠버네티스 시스템 컴포넌트에 대한 추적(trace)
- 12.8: 쿠버네티스에서 프락시(Proxy)
- 12.9: 애드온 설치
- 13: 쿠버네티스 확장
- 13.1: 오퍼레이터(operator) 패턴
- 13.2: 컴퓨트, 스토리지 및 네트워킹 익스텐션
- 13.3: 쿠버네티스 API 확장하기
- 13.3.1: 커스텀 리소스
- 13.3.2: 쿠버네티스 API 애그리게이션 레이어(aggregation layer)
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로 어떻게 갔는지는 상관이 없다. 중앙화된 제어도 필요치 않다. 이로써 시스템이 보다 더 사용하기 쉬워지고, 강력해지며, 견고하고, 회복력을 갖추게 되며, 확장 가능해진다.
다음 내용
- 쿠버네티스 구성요소 살펴보기
- 쿠버네티스 API 살펴보기
- 클러스터 아키텍처 살펴보기
- 시작할 준비가 되었는가?
1.1 - 쿠버네티스 컴포넌트
쿠버네티스를 배포하면 클러스터를 얻는다.
쿠버네티스 클러스터는 컨테이너화된 애플리케이션을 실행하는 노드라고 하는 워커 머신의 집합. 모든 클러스터는 최소 한 개의 워커 노드를 가진다.
워커 노드는 애플리케이션의 구성요소인 파드를 호스트한다. 컨트롤 플레인은 워커 노드와 클러스터 내 파드를 관리한다. 프로덕션 환경에서는 일반적으로 컨트롤 플레인이 여러 컴퓨터에 걸쳐 실행되고, 클러스터는 일반적으로 여러 노드를 실행하므로 내결함성과 고가용성이 제공된다.
이 문서는 완전히 작동하는 쿠버네티스 클러스터를 갖기 위해 필요한 다양한 컴포넌트들에 대해 요약하고 정리한다.
컨트롤 플레인 컴포넌트
컨트롤 플레인 컴포넌트는 클러스터에 관한 전반적인 결정(예를 들어, 스케줄링)을 수행하고 클러스터 이벤트(예를 들어, 디플로이먼트의 replicas
필드에 대한 요구 조건이 충족되지 않을 경우 새로운 파드를 구동시키는 것)를 감지하고 반응한다.
컨트롤 플레인 컴포넌트는 클러스터 내 어떠한 머신에서든지 동작할 수 있다. 그러나 간결성을 위하여, 구성 스크립트는 보통 동일 머신 상에 모든 컨트롤 플레인 컴포넌트를 구동시키고, 사용자 컨테이너는 해당 머신 상에 동작시키지 않는다. 여러 머신에서 실행되는 컨트롤 플레인 설정의 예제를 보려면 kubeadm을 사용하여 고가용성 클러스터 만들기를 확인해본다.
kube-apiserver
API 서버는 쿠버네티스 API를 노출하는 쿠버네티스 컨트롤 플레인 컴포넌트이다. API 서버는 쿠버네티스 컨트롤 플레인의 프론트 엔드이다.
쿠버네티스 API 서버의 주요 구현은 kube-apiserver 이다. kube-apiserver는 수평으로 확장되도록 디자인되었다. 즉, 더 많은 인스턴스를 배포해서 확장할 수 있다. 여러 kube-apiserver 인스턴스를 실행하고, 인스턴스간의 트래픽을 균형있게 조절할 수 있다.
etcd
모든 클러스터 데이터를 담는 쿠버네티스 뒷단의 저장소로 사용되는 일관성·고가용성 키-값 저장소.
쿠버네티스 클러스터에서 etcd를 뒷단의 저장소로 사용한다면, 이 데이터를 백업하는 계획은 필수이다.
etcd에 대한 자세한 정보는, 공식 문서를 참고한다.
kube-scheduler
노드가 배정되지 않은 새로 생성된 파드 를 감지하고, 실행할 노드를 선택하는 컨트롤 플레인 컴포넌트.
스케줄링 결정을 위해서 고려되는 요소는 리소스에 대한 개별 및 총체적 요구 사항, 하드웨어/소프트웨어/정책적 제약, 어피니티(affinity) 및 안티-어피니티(anti-affinity) 명세, 데이터 지역성, 워크로드-간 간섭, 데드라인을 포함한다.
kube-controller-manager
컨트롤러 프로세스를 실행하는 컨트롤 플레인 컴포넌트.
논리적으로, 각 컨트롤러는 분리된 프로세스이지만, 복잡성을 낮추기 위해 모두 단일 바이너리로 컴파일되고 단일 프로세스 내에서 실행된다.
이들 컨트롤러는 다음을 포함한다.
- 노드 컨트롤러: 노드가 다운되었을 때 통지와 대응에 관한 책임을 가진다.
- 잡 컨트롤러: 일회성 작업을 나타내는 잡 오브젝트를 감시한 다음, 해당 작업을 완료할 때까지 동작하는 파드를 생성한다.
- 엔드포인트슬라이스 컨트롤러: (서비스와 파드 사이의 연결고리를 제공하기 위해) 엔드포인트슬라이스(EndpointSlice) 오브젝트를 채운다
- 서비스어카운트 컨트롤러: 새로운 네임스페이스에 대한 기본 서비스어카운트(ServiceAccount)를 생성한다.
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)한다.
컨테이너 런타임
컨테이너 런타임은 컨테이너 실행을 담당하는 소프트웨어이다.
쿠버네티스는 containerd, CRI-O와 같은 컨테이너 런타임 및 모든 Kubernetes CRI (컨테이너 런타임 인터페이스) 구현체를 지원한다.
애드온
애드온은 쿠버네티스 리소스(데몬셋,
디플로이먼트 등)를
이용하여 클러스터 기능을 구현한다. 이들은 클러스터 단위의 기능을 제공하기 때문에
애드온에 대한 네임스페이스 리소스는 kube-system
네임스페이스에 속한다.
선택된 일부 애드온은 아래에 설명하였고, 사용 가능한 전체 확장 애드온 리스트는 애드온을 참조한다.
DNS
여타 애드온들이 절대적으로 요구되지 않지만, 많은 예시에서 필요로 하기 때문에 모든 쿠버네티스 클러스터는 클러스터 DNS를 갖추어야만 한다.
클러스터 DNS는 구성환경 내 다른 DNS 서버와 더불어, 쿠버네티스 서비스를 위해 DNS 레코드를 제공해주는 DNS 서버다.
쿠버네티스에 의해 구동되는 컨테이너는 DNS 검색에서 이 DNS 서버를 자동으로 포함한다.
웹 UI (대시보드)
대시보드는 쿠버네티스 클러스터를 위한 범용의 웹 기반 UI다. 사용자가 클러스터 자체뿐만 아니라, 클러스터에서 동작하는 애플리케이션에 대한 관리와 문제 해결을 할 수 있도록 해준다.
컨테이너 리소스 모니터링
컨테이너 리소스 모니터링은 중앙 데이터베이스 내의 컨테이너들에 대한 포괄적인 시계열 매트릭스를 기록하고 그 데이터를 열람하기 위한 UI를 제공해 준다.
클러스터-레벨 로깅
클러스터-레벨 로깅 메커니즘은 검색/열람 인터페이스와 함께 중앙 로그 저장소에 컨테이너 로그를 저장하는 책임을 진다.
다음 내용
- 노드에 대해 더 배우기
- 컨트롤러에 대해 더 배우기
- kube-scheduler에 대해 더 배우기
- etcd의 공식 문서 읽기
1.2 - 쿠버네티스 API
쿠버네티스 컨트롤 플레인의 핵심은 API 서버이다. API 서버는 최종 사용자, 클러스터의 다른 부분 그리고 외부 컴포넌트가 서로 통신할 수 있도록 HTTP API를 제공한다.
쿠버네티스 API를 사용하면 쿠버네티스의 API 오브젝트(예: 파드(Pod), 네임스페이스(Namespace), 컨피그맵(ConfigMap) 그리고 이벤트(Event))를 질의(query)하고 조작할 수 있다.
대부분의 작업은 kubectl 커맨드 라인 인터페이스 또는 API를 사용하는 kubeadm과 같은 다른 커맨드 라인 도구를 통해 수행할 수 있다. 그러나, REST 호출을 사용하여 API에 직접 접근할 수도 있다.
쿠버네티스 API를 사용하여 애플리케이션을 작성하는 경우 클라이언트 라이브러리 중 하나를 사용하는 것이 좋다.
OpenAPI 명세
완전한 API 상세 내용은 OpenAPI를 활용해서 문서화했다.
OpenAPI V2
쿠버네티스 API 서버는 /openapi/v2
엔드포인트를 통해
통합된(aggregated) OpenAPI v2 스펙을 제공한다.
요청 헤더에 다음과 같이 기재하여 응답 형식을 지정할 수 있다.
헤더 | 사용할 수 있는 값 | 참고 |
---|---|---|
Accept-Encoding | gzip | 이 헤더를 제공하지 않는 것도 가능 |
Accept | application/com.github.proto-openapi.spec.v2@v1.0+protobuf | 주로 클러스터 내부 용도로 사용 |
application/json | 기본값 | |
* | JSON으로 응답 |
쿠버네티스는 주로 클러스터 내부 통신을 위해 대안적인 Protobuf에 기반한 직렬화 형식을 구현한다. 이 형식에 대한 자세한 내용은 쿠버네티스 Protobuf 직렬화 디자인 제안과 API 오브젝트를 정의하는 Go 패키지에 들어있는 각각의 스키마에 대한 IDL(인터페이스 정의 언어) 파일을 참고한다.
OpenAPI V3
Kubernetes v1.24 [beta]
쿠버네티스 v1.31 버전은 OpenAPI v3 API 발행(publishing)에 대한 베타 지원을 제공한다.
이는 베타 기능이며 기본적으로 활성화되어 있다.
kube-apiserver 구성 요소에
OpenAPIV3
기능 게이트를 비활성화하여
이 베타 기능을 비활성화할 수 있다.
/openapi/v3
디스커버리 엔드포인트는 사용 가능한 모든
그룹/버전의 목록을 제공한다. 이 엔드포인트는 JSON 만을 반환한다.
이러한 그룹/버전은 다음과 같은 형식으로 제공된다.
{
"paths": {
...,
"api/v1": {
"serverRelativeURL": "/openapi/v3/api/v1?hash=CC0E9BFD992D8C59AEC98A1E2336F899E8318D3CF4C68944C3DEC640AF5AB52D864AC50DAA8D145B3494F75FA3CFF939FCBDDA431DAD3CA79738B297795818CF"
},
"apis/admissionregistration.k8s.io/v1": {
"serverRelativeURL": "/openapi/v3/apis/admissionregistration.k8s.io/v1?hash=E19CC93A116982CE5422FC42B590A8AFAD92CDE9AE4D59B5CAAD568F083AD07946E6CB5817531680BCE6E215C16973CD39003B0425F3477CFD854E89A9DB6597"
},
....
}
}
위의 상대 URL은 변경 불가능한(immutable) OpenAPI 상세를 가리키고 있으며,
이는 클라이언트에서의 캐싱을 향상시키기 위함이다.
같은 목적을 위해 API 서버는 적절한 HTTP 캐싱 헤더를
설정한다(Expires
를 1년 뒤로, Cache-Control
을 immutable
).
사용 중단된 URL이 사용되면, API 서버는 최신 URL로의 리다이렉트를 반환한다.
쿠버네티스 API 서버는
쿠버네티스 그룹 버전에 따른 OpenAPI v3 스펙을
/openapi/v3/apis/<group>/<version>?hash=<hash>
엔드포인트에 게시한다.
사용 가능한 요청 헤더 목록은 아래의 표를 참고한다.
헤더 | 사용할 수 있는 값 | 참고 |
---|---|---|
Accept-Encoding | gzip | 이 헤더를 제공하지 않는 것도 가능 |
Accept | application/com.github.proto-openapi.spec.v3@v1.0+protobuf | 주로 클러스터 내부 용도로 사용 |
application/json | 기본값 | |
* | JSON으로 응답 |
지속성
쿠버네티스는 오브젝트의 직렬화된 상태를 etcd에 기록하여 저장한다.
API 그룹과 버전 규칙
필드를 쉽게 제거하거나 리소스 표현을 재구성하기 위해,
쿠버네티스는 각각 /api/v1
또는 /apis/rbac.authorization.k8s.io/v1alpha1
과
같은 서로 다른 API 경로에서 여러 API 버전을 지원한다.
버전 규칙은 리소스나 필드 수준이 아닌 API 수준에서 수행되어 API가 시스템 리소스 및 동작에 대한 명확하고 일관된 보기를 제공하고 수명 종료 및/또는 실험적 API에 대한 접근을 제어할 수 있도록 한다.
보다 쉽게 발전하고 API를 확장하기 위해, 쿠버네티스는 활성화 또는 비활성화가 가능한 API 그룹을 구현한다.
API 리소스는 API 그룹, 리소스 유형, 네임스페이스 (네임스페이스 리소스용) 및 이름으로 구분된다. API 서버는 API 버전 간의 변환을 투명하게 처리한다. 서로 다른 모든 버전은 실제로 동일한 지속 데이터의 표현이다. API 서버는 여러 API 버전을 통해 동일한 기본 데이터를 제공할 수 있다.
예를 들어, 동일한 리소스에 대해 v1
과 v1beta1
이라는 두 가지 API 버전이
있다고 가정하자. API의 v1beta1
버전을 사용하여 오브젝트를 만든 경우,
v1beta1
버전이 사용 중단(deprecated)되고 제거될 때까지는
v1beta1
또는 v1
API 버전을 사용하여 해당 오브젝트를 읽거나, 업데이트하거나, 삭제할 수 있다.
그 이후부터는 v1
API를 사용하여 계속 오브젝트에 접근하고 수정할 수 있다.
API 변경 사항
성공적인 시스템은 새로운 유스케이스가 등장하거나 기존 사례가 변경됨에 따라 성장하고 변화해야 한다. 따라서, 쿠버네티스는 쿠버네티스 API가 지속적으로 변경되고 성장할 수 있도록 설계했다. 쿠버네티스 프로젝트는 기존 클라이언트와의 호환성을 깨지 않고 다른 프로젝트가 적응할 기회를 가질 수 있도록 장기간 해당 호환성을 유지하는 것을 목표로 한다.
일반적으로, 새 API 리소스와 새 리소스 필드는 자주 추가될 수 있다. 리소스 또는 필드를 제거하려면 API 지원 중단 정책을 따라야 한다.
쿠버네티스는 일반적으로 API 버전 v1
에서 안정 버전(GA)에 도달하면, 공식 쿠버네티스 API에
대한 호환성 유지를 강력하게 이행한다. 또한,
쿠버네티스는 공식 쿠버네티스 API의 베타 API 버전으로 만들어진 데이터와도 호환성을 유지하며,
해당 기능이 안정화되었을 때 해당 데이터가 안정 버전(GA)의 API 버전들에 의해 변환되고 접근될 수 있도록 보장한다.
만약 베타 API 버전을 사용했다면, 해당 API가 승급했을 때 후속 베타 버전 혹은 안정된 버전의 API로 전환해야 한다. 해당 작업은 오브젝트 접근을 위해 두 API 버전 모두 사용할 수 있는 베타 API의 사용 중단(deprecation) 시기일 때 진행하는 것이 최선이다. 베타 API의 사용 중단(deprecation) 시기가 끝나고 더 이상 사용될 수 없다면 반드시 대체 API 버전을 사용해야 한다.
참고:
비록 쿠버네티스는 알파 API 버전에 대한 호환성을 유지하는 것을 목표로 하지만, 일부 상황에서 이는 불가능하다. 알파 API 버전을 사용하는 경우, 클러스터를 업그레이드해야 할 때에는 API 변경으로 인해 호환성이 깨지고 업그레이드 전에 기존 오브젝트를 전부 제거해야 하는 상황에 대비하기 위해 쿠버네티스의 릴리스 정보를 확인하자.API 버전 수준 정의에 대한 자세한 내용은 API 버전 레퍼런스를 참조한다.
API 확장
쿠버네티스 API는 다음 두 가지 방법 중 하나로 확장할 수 있다.
- 커스텀 리소스를 사용하면 API 서버가 선택한 리소스 API를 제공하는 방법을 선언적으로 정의할 수 있다.
- 애그리게이션 레이어(aggregation layer)를 구현하여 쿠버네티스 API를 확장할 수도 있다.
다음 내용
- 자체 CustomResourceDefinition을 추가하여 쿠버네티스 API를 확장하는 방법에 대해 배우기.
- 쿠버네티스 API 접근 제어하기는 클러스터가 API 접근을 위한 인증 및 권한을 관리하는 방법을 설명한다.
- API 레퍼런스를 읽고 API 엔드포인트, 리소스 유형 및 샘플에 대해 배우기.
- API 변경 사항에서 호환 가능한 변경 사항을 구성하고, API를 변경하는 방법에 대해 알아본다.
1.3 - 쿠버네티스 오브젝트로 작업하기
1.3.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
그 출력 내용은 다음과 유사하다.
deployment.apps/nginx-deployment created
요구되는 필드
생성하고자 하는 쿠버네티스 오브젝트에 대한 .yaml
파일 내, 다음 필드를 위한 값들을 설정해 줘야한다.
apiVersion
- 이 오브젝트를 생성하기 위해 사용하고 있는 쿠버네티스 API 버전이 어떤 것인지kind
- 어떤 종류의 오브젝트를 생성하고자 하는지metadata
-이름
문자열,UID
, 그리고 선택적인네임스페이스
를 포함하여 오브젝트를 유일하게 구분지어 줄 데이터spec
- 오브젝트에 대해 어떤 상태를 의도하는지
오브젝트 spec
에 대한 정확한 포맷은 모든 쿠버네티스 오브젝트마다 다르고, 그 오브젝트 특유의
중첩된 필드를 포함한다. 쿠버네티스 API 레퍼런스 는
쿠버네티스를 이용하여 생성할 수 있는 오브젝트에 대한 모든 spec 포맷을 살펴볼 수 있도록 해준다.
예를 들어, 파드 API 레퍼런스를 보려면
spec
필드를 참조한다.
각 파드에 대해, .spec
필드는 파드 및 파드의 원하는 상태(desired state)를
기술한다(예: 파드의 각 컨테이너에 대한 컨테이너 이미지).
오브젝트 상세에 대한 또 다른 예시는 스테이트풀셋 API의
spec
필드이다.
스테이트풀셋의 경우, .spec
필드는 스테이트풀셋 및 스테이트풀셋의 원하는 상태(desired state)를 기술한다.
스테이트풀셋의 .spec
에는 파드 오브젝트에 대한
템플릿이 존재한다.
이 템플릿은 스테이트풀셋 명세를 만족시키기 위해
스테이트풀셋 컨트롤러가 생성할 파드에 대한 상세 사항을 설명한다.
서로 다른 종류의 오브젝트는 서로 다른 .status
를 가질 수 있다.
다시 한번 말하자면, 각 API 레퍼런스 페이지는 각 오브젝트 타입에 대해 해당 .status
필드의 구조와 내용에 대해 소개한다.
다음 내용
- 파드와 같이, 가장 중요하고 기본적인 쿠버네티스 오브젝트에 대해 배운다.
- 쿠버네티스의 컨트롤러에 대해 배운다.
- API 개념의 더 많은 설명은 쿠버네티스 API 사용을 본다.
1.3.2 - 쿠버네티스 오브젝트 관리
kubectl
커맨드라인 툴은 쿠버네티스 오브젝트를 생성하고 관리하기 위한
몇 가지 상이한 방법을 지원한다. 이 문서는 여러가지 접근법에 대한 개요를
제공한다. Kubectl로 오브젝트 관리하기에 대한 자세한 설명은
Kubectl 서적에서 확인한다.
관리 기법
경고:
쿠버네티스 오브젝트는 하나의 기법만 사용하여 관리해야 한다. 동일한 오브젝트에 대해 여러 기법을 혼용하는 것은 예상치 못한 동작을 초래하게 된다.관리기법 | 대상 | 권장 환경 | 지원하는 작업자 수 | 학습 난이도 |
---|---|---|---|---|
명령형 커맨드 | 활성 오브젝트 | 개발 환경 | 1+ | 낮음 |
명령형 오브젝트 구성 | 개별 파일 | 프로덕션 환경 | 1 | 보통 |
선언형 오브젝트 구성 | 파일이 있는 디렉터리 | 프로덕션 환경 | 1+ | 높음 |
명령형 커맨드
명령형 커맨드를 사용할 경우, 사용자는 클러스터 내 활성 오브젝트를 대상으로
직접 동작시킨다. 사용자는 실행할 작업을 인수 또는 플래그로 kubectl
커맨드에
지정한다.
이것은 클러스터에서 일회성 작업을 개시시키거나 동작시키기 위한 추천 방법이다. 이 기법은 활성 오브젝트를 대상으로 직접적인 영향을 미치기 때문에, 이전 구성에 대한 이력을 제공해 주지 않는다.
예시
디플로이먼트 오브젝트를 생성하여 nginx 컨테이너의 인스턴스를 구동시킨다.
kubectl create deployment nginx --image nginx
트레이드 오프
오브젝트 구성에 비해 장점은 다음과 같다.
- 커맨드는 하나의 동작을 나타내는 단어로 표현된다.
- 커맨드는 클러스터를 수정하기 위해 단 하나의 단계만을 필요로 한다.
오브젝트 구성에 비해 단점은 다음과 같다.
- 커맨드는 변경 검토 프로세스와 통합되지 않는다.
- 커맨드는 변경에 관한 감사 추적(audit trail)을 제공하지 않는다.
- 커맨드는 활성 동작 중인 경우를 제외하고는 레코드의 소스를 제공하지 않는다.
- 커맨드는 새로운 오브젝트 생성을 위한 템플릿을 제공하지 않는다.
명령형 오브젝트 구성
명령형 오브젝트 구성에서는 kubectl 커맨드로 작업(생성, 교체 등), 선택적 플래그, 그리고 최소 하나의 파일 이름을 지정한다. 그 파일은 YAML 또는 JSON 형식으로 오브젝트의 완전한 정의를 포함해야만 한다.
오브젝트 정의에 대한 더 자세한 내용은 API 참조를 참고한다.
경고:
명령형replace
커맨드는 기존 spec을 새로 제공된 spec으로 바꾸고
구성 파일에서 누락된 오브젝트의 모든 변경 사항을 삭제한다.
이 방법은 spec이 구성 파일과는 별개로 업데이트되는 리소스 유형에는
사용하지 말아야한다.
예를 들어 LoadBalancer
유형의 서비스는 클러스터의 구성과 별도로
externalIPs
필드가 업데이트된다.예시
구성 파일에 정의된 오브젝트를 생성한다.
kubectl create -f nginx.yaml
두 개의 구성 파일에 정의된 오브젝트를 삭제한다.
kubectl delete -f nginx.yaml -f redis.yaml
활성 동작하는 구성을 덮어씀으로써 구성 파일에 정의된 오브젝트를 업데이트한다.
kubectl replace -f nginx.yaml
트레이드 오프
명령형 커맨드에 비해 장점은 다음과 같다.
- 오브젝트 구성은 Git과 같은 소스 컨트롤 시스템에 보관할 수 있다.
- 오브젝트 구성은 푸시와 감사 추적 전에 변경사항을 검토하는 것과 같은 프로세스들과 통합할 수 있다.
- 오브젝트 구성은 새로운 오브젝트 생성을 위한 템플릿을 제공한다.
명령형 커맨드에 비해 단점은 다음과 같다.
- 오브젝트 구성은 오브젝트 스키마에 대한 기본적인 이해를 필요로 한다.
- 오브젝트 구성은 YAML 파일을 기록하는 추가적인 과정을 필요로 한다.
선언형 오브젝트 구성에 비해 장점은 다음과 같다.
- 명령형 오브젝트 구성의 동작은 보다 간결하고 이해하기 쉽다.
- 쿠버네티스 버전 1.5 부터는 더 성숙한 명령형 오브젝트 구성을 제공한다.
선언형 오브젝트 구성에 비해 단점은 다음과 같다.
- 명령형 오브젝트 구성은 디렉터리가 아닌, 파일에 가장 적합하다.
- 활성 오브젝트에 대한 업데이트는 구성 파일에 반영되어야 한다. 그렇지 않으면 다음 교체 중에 손실된다.
선언형 오브젝트 구성
선언형 오브젝트 구성을 사용할 경우, 사용자는 로컬에 보관된 오브젝트
구성 파일을 대상으로 작동시키지만, 사용자는 파일에서 수행 할
작업을 정의하지 않는다. 생성, 업데이트, 그리고 삭제 작업은
kubectl
에 의해 오브젝트마다 자동으로 감지된다. 이를 통해 다른 오브젝트에 대해
다른 조작이 필요할 수 있는 디렉터리에서 작업할 수 있다.
참고:
선언형 오브젝트 구성은 변경 사항이 오브젝트 구성 파일에 다시 병합되지 않더라도 다른 작성자가 작성한 변경 사항을 유지한다. 이것은 전체 오브젝트 구성 변경을 위한replace
API를
사용하는 대신, patch
API를 사용하여 인지되는 차이만
작성하기 때문에 가능하다.예시
configs
디렉터리 내 모든 오브젝트 구성 파일을 처리하고 활성 오브젝트를
생성 또는 패치한다. 먼저 어떠한 변경이 이루어지게 될지 알아보기 위해 diff
하고 나서 적용할 수 있다.
kubectl diff -f configs/
kubectl apply -f configs/
재귀적으로 디렉터리를 처리한다.
kubectl diff -R -f configs/
kubectl apply -R -f configs/
트레이드 오프
명령형 오브젝트 구성에 비해 장점은 다음과 같다.
- 활성 오브젝트에 직접 작성된 변경 사항은 구성 파일로 다시 병합되지 않더라도 유지된다.
- 선언형 오브젝트 구성은 디렉터리에서의 작업 및 오브젝트 별 작업 유형(생성, 패치, 삭제)의 자동 감지에 더 나은 지원을 제공한다.
명령형 오브젝트 구성에 비해 단점은 다음과 같다.
- 선언형 오브젝트 구성은 예상치 못한 결과를 디버깅하고 이해하기가 더 어렵다.
- diff를 사용한 부분 업데이트는 복잡한 병합 및 패치 작업을 일으킨다.
다음 내용
1.3.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 로 표준화 되어 있다.
다음 내용
- 쿠버네티스의 레이블과 어노테이션에 대해 읽기.
- 쿠버네티스의 식별자와 이름 디자인 문서 읽기.
1.3.4 - 레이블과 셀렉터
레이블 은 파드와 같은 오브젝트에 첨부된 키와 값의 쌍이다. 레이블은 오브젝트의 특성을 식별하는 데 사용되어 사용자에게 중요하지만, 코어 시스템에 직접적인 의미는 없다. 레이블로 오브젝트의 하위 집합을 선택하고, 구성하는데 사용할 수 있다. 레이블은 오브젝트를 생성할 때에 붙이거나 생성 이후에 붙이거나 언제든지 수정이 가능하다. 오브젝트마다 키와 값으로 레이블을 정의할 수 있다. 오브젝트의 키는 고유한 값이어야 한다.
"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: production
과 app: 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 유형은 유효성과 의미를 문서화해야 한다.
참고:
레플리카셋(ReplicaSet)과 같은 일부 API 유형에서 두 인스턴스의 레이블 셀렉터는 네임스페이스 내에서 겹치지 않아야 한다. 그렇지 않으면 컨트롤러는 상충하는 명령으로 보고, 얼마나 많은 복제본이 필요한지 알 수 없다.주의:
일치성 기준과 집합성 기준 조건 모두에 대해 논리적인 OR (||
) 연산자가 없다. 필터 구문이 적절히 구성되어 있는지 확인해야 한다.일치성 기준 요건
일치성 기준 또는 불일치 기준 의 요구사항으로 레이블의 키와 값의 필터링을 허용한다. 일치하는 오브젝트는 추가 레이블을 가질 수 있지만, 레이블의 명시된 제약 조건을 모두 만족해야 한다.
=
,==
,!=
이 세 가지 연산자만 허용한다. 처음 두 개의 연산자의 일치성(그리고 동의어), 나머지는 불일치 를 의미한다. 예를 들면,
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: "registry.k8s.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1
nodeSelector:
accelerator: nvidia-tesla-p100
집합성 기준 요건
집합성 기준 레이블 요건에 따라 값 집합을 키로 필터링할 수 있다. in
,notin
과 exists
(키 식별자만 해당)의 3개의 연산자를 지원한다. 예를 들면,
environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
- 첫 번째 예시에서 키가
environment
이고 값이production
또는qa
인 모든 리소스를 선택한다. - 두 번째 예시에서 키가
tier
이고 값이frontend
와backend
를 가지는 리소스를 제외한 모든 리소스와 키로tier
를 가지고 값을 공백으로 가지는 모든 리소스를 선택한다. - 세 번째 예시에서 레이블의 값에 상관없이 키가
partition
을 포함하는 모든 리소스를 선택한다. - 네 번째 예시에서 레이블의 값에 상관없이 키가
partition
을 포함하지 않는 모든 리소스를 선택한다.
마찬가지로 쉼표는 AND 연산자로 작동한다. 따라서 partition,environment notin (qa)
와 같이 사용하면 값과 상관없이 키가 partition
인 것과 키가 environment
이고 값이 qa
와 다른 리소스를 필터링할 수 있다.
집합성 기준 레이블 셀렉터는 일반적으로 environment=production
과 environment 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 클라이언트를 통해 선택된 리소스를 확인하거나 목록을 볼 수 있다. 예를 들어, kubectl
로 apiserver
를 대상으로 일치성 기준 으로 하는 셀렉터를 다음과 같이 이용할 수 있다.
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 오브젝트에서 참조 설정
services
와
replicationcontrollers
와 같은
일부 쿠버네티스 오브젝트는 레이블 셀렉터를 사용해서
파드와 같은 다른 리소스 집합을 선택한다.
서비스와 레플리케이션 컨트롤러
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은 설정된 값이 있어야 한다. matchLabels
와 matchExpressions
모두 AND로 되어 있어 일치하기 위해서는 모든 요건을 만족해야 한다.
노드 셋 선택
레이블을 통해 선택하는 사용 사례 중 하나는 파드를 스케줄 할 수 있는 노드 셋을 제한하는 것이다. 자세한 내용은 노드 선택 문서를 참조한다.
1.3.5 - 네임스페이스
쿠버네티스에서, 네임스페이스 는 단일 클러스터 내에서의 리소스 그룹 격리 메커니즘을 제공한다. 리소스의 이름은 네임스페이스 내에서 유일해야 하며, 네임스페이스 간에서 유일할 필요는 없다. 네임스페이스 기반 스코핑은 네임스페이스 기반 오브젝트 (예: 디플로이먼트, 서비스 등) 에만 적용 가능하며 클러스터 범위의 오브젝트 (예: 스토리지클래스, 노드, 퍼시스턴트볼륨 등) 에는 적용 불가능하다.
여러 개의 네임스페이스를 사용하는 경우
네임스페이스는 여러 개의 팀이나, 프로젝트에 걸쳐서 많은 사용자가 있는 환경에서 사용하도록 만들어졌다. 사용자가 거의 없거나, 수 십명 정도가 되는 경우에는 네임스페이스를 전혀 고려할 필요가 없다. 네임스페이스가 제공하는 기능이 필요할 때 사용하도록 하자.
네임스페이스는 이름의 범위를 제공한다. 리소스의 이름은 네임스페이스 내에서 유일해야하지만, 네임스페이스를 통틀어서 유일할 필요는 없다. 네임스페이스는 서로 중첩될 수 없으며, 각 쿠버네티스 리소스는 하나의 네임스페이스에만 있을 수 있다.
네임스페이스는 클러스터 자원을 (리소스 쿼터를 통해) 여러 사용자 사이에서 나누는 방법이다.
동일한 소프트웨어의 다른 버전과 같이 약간 다른 리소스를 분리하기 위해 여러 네임스페이스를 사용할 필요는 없다. 동일한 네임스페이스 내에서 리소스를 구별하기 위해 레이블을 사용한다.
참고:
프로덕션 클러스터의 경우,default
네임스페이스를 사용하지 않는 것을 고려한다. 대신에, 다른 네임스페이스를 만들어 사용한다.초기 네임스페이스
쿠버네티스는 처음에 네 개의 초기 네임스페이스를 갖는다.
default
- 쿠버네티스에는 이 네임스페이스가 포함되어 있으므로 먼저 네임스페이스를 생성하지 않고도 새 클러스터를 사용할 수 있다.
kube-node-lease
- 이 네임스페이스는 각 노드와 연관된 리스 오브젝트를 갖는다. 노드 리스는 kubelet이 하트비트를 보내서 컨트롤 플레인이 노드의 장애를 탐지할 수 있게 한다.
kube-public
- 이 네임스페이스는 모든 클라이언트(인증되지 않은 클라이언트 포함)가 읽기 권한으로 접근할 수 있다. 이 네임스페이스는 주로 전체 클러스터 중에 공개적으로 드러나서 읽을 수 있는 리소스를 위해 예약되어 있다. 이 네임스페이스의 공개적인 성격은 단지 관례이지 요구 사항은 아니다.
kube-system
- 쿠버네티스 시스템에서 생성한 오브젝트를 위한 네임스페이스.
네임스페이스 다루기
네임스페이스의 생성과 삭제는 네임스페이스 관리자 가이드 문서에 기술되어 있다.
참고:
`kube-` 접두사로 시작하는 네임스페이스는 쿠버네티스 시스템용으로 예약되어 있으므로, 사용자는 이러한 네임스페이스를 생성하지 않는다.
네임스페이스 조회
사용 중인 클러스터의 현재 네임스페이스를 나열할 수 있다.
kubectl get namespace
NAME STATUS AGE
default Active 1d
kube-node-lease Active 1d
kube-public Active 1d
kube-system Active 1d
요청에 네임스페이스 설정하기
현재 요청에 대한 네임스페이스를 설정하기 위해서 --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)을 사용해야 한다.
그렇기 때문에, 모든 네임스페이스 이름은 유효한 RFC 1123 DNS 레이블이어야 한다.
경고:
네임스페이스의 이름을 공개 최상위 도메인 중 하나와 동일하게 만들면, 해당 네임스페이스 내의 서비스의 짧은 DNS 이름이 공개 DNS 레코드와 겹칠 수 있다. 어떠한 네임스페이스 내의 워크로드가 접미점(trailing dot) 없이 DNS 룩업을 수행하면 공개 DNS 레코드보다 우선하여 해당 서비스로 리다이렉트될 것이다.
이를 방지하기 위해, 신뢰하는 사용자만 네임스페이스를 생성할 수 있도록 권한을 제한한다. 필요한 경우, 추가적으로 써드파티 보안 컨트롤을 구성할 수 있으며, 예를 들어 어드미션 웹훅을 이용하여 공개 TLD와 동일한 이름의 네임스페이스 생성을 금지시킬 수 있다.
모든 오브젝트가 네임스페이스에 속하지는 않음
대부분의 쿠버네티스 리소스(예를 들어, 파드, 서비스, 레플리케이션 컨트롤러 외)는 네임스페이스에 속한다. 하지만 네임스페이스 리소스 자체는 네임스페이스에 속하지 않는다. 그리고 노드나 퍼시스턴트 볼륨과 같은 저수준 리소스는 어느 네임스페이스에도 속하지 않는다.
다음은 네임스페이스에 속하지 않는 쿠버네티스 리소스를 조회하는 방법이다.
# 네임스페이스에 속하는 리소스
kubectl api-resources --namespaced=true
# 네임스페이스에 속하지 않는 리소스
kubectl api-resources --namespaced=false
자동 레이블링
Kubernetes 1.21 [beta]
쿠버네티스 컨트롤 플레인은 NamespaceDefaultLabelName
기능 게이트가
활성화된 경우 모든 네임스페이스에 변경할 수 없는(immutable) 레이블
kubernetes.io / metadata.name
을 설정한다.
레이블 값은 네임스페이스 이름이다.
다음 내용
- 신규 네임스페이스 생성에 대해 더 배우기.
- 네임스페이스 삭제에 대해 더 배우기.
1.3.6 - 어노테이션
쿠버네티스 어노테이션을 사용하여 임의의 비-식별 메타데이터를 오브젝트에 첨부할 수 있다. 도구 및 라이브러리와 같은 클라이언트는 이 메타데이터를 검색할 수 있다.
오브젝트에 메타데이터 첨부
레이블이나 어노테이션을 사용하여 쿠버네티스 오브젝트에 메타데이터를 첨부할 수 있다. 레이블을 사용하여 오브젝트를 선택하고, 특정 조건을 만족하는 오브젝트 컬렉션을 찾을 수 있다. 반면에, 어노테이션은 오브젝트를 식별하고 선택하는데 사용되지 않는다. 어노테이션의 메타데이터는 작거나 크고, 구조적이거나 구조적이지 않을 수 있으며, 레이블에서 허용되지 않는 문자를 포함할 수 있다.
어노테이션은 레이블과 같이 키/값 맵이다.
"metadata": {
"annotations": {
"key1" : "value1",
"key2" : "value2"
}
}
참고:
맵의 키와 값은 문자열이어야 한다. 다르게 말해서, 숫자, 불리언(boolean), 리스트 등의 다른 형식을 키나 값에 사용할 수 없다.다음은 어노테이션에 기록할 수 있는 정보의 예제이다.
필드는 선언적 구성 계층에 의해 관리된다. 이러한 필드를 어노테이션으로 첨부하는 것은 클라이언트 또는 서버가 설정한 기본 값, 자동 생성된 필드, 그리고 오토사이징 또는 오토스케일링 시스템에 의해 설정된 필드와 구분된다.
빌드, 릴리스, 또는 타임 스탬프, 릴리스 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
다음 내용
레이블과 셀렉터에 대해 알아본다.
1.3.7 - 필드 셀렉터
필드 셀렉터 는 한 개 이상의 리소스 필드 값에 따라 쿠버네티스 리소스를 선택하기 위해 사용된다. 필드 셀렉터 쿼리의 예시는 다음과 같다.
metadata.name=my-service
metadata.namespace!=default
status.phase=Pending
다음의 kubectl
커맨드는 status.phase
필드의 값이 Running
인 모든 파드를 선택한다.
kubectl get pods --field-selector status.phase=Running
참고:
필드 셀렉터는 본질적으로 리소스 필터 이다. 기본적으로 적용되는 셀렉터나 필드는 없으며, 이는 명시된 종류의 모든 리소스가 선택된다는 것을 의미한다. 여기에 따라오는kubectl
쿼리인 kubectl get pods
와 kubectl get pods --field-selector ""
는 동일하다.사용 가능한 필드
사용 가능한 필드는 쿠버네티스의 리소스 종류에 따라서 다르다. 모든 리소스 종류는 metadata.name
과 metadata.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
1.3.8 - 파이널라이저
파이널라이저는 쿠버네티스가 오브젝트를 완전히 삭제하기 이전, 삭제 표시를 위해 특정 조건이 충족될 때까지 대기하도록 알려주기 위한 네임스페이스에 속한 키(namespaced key)이다. 파이널라이저는 삭제 완료된 오브젝트가 소유한 리소스를 정리하기 위해 컨트롤러에게 알린다.
파이널라이저를 가진 특정한 오브젝트를 쿠버네티스가 삭제하도록 지시할 때,
쿠버네티스 API는 .metadata.delationTimestamp
을 덧붙여 삭제하도록 오브젝트에 표시하며,
202
상태코드(HTTP "Accepted")을 리턴한다. 대상 오브젝트가 Terminating 상태를 유지하는 동안 컨트롤 플레인
또는 다른 컴포넌트는 하나의 파이널라이저에서 정의한 작업을 수행한다.
정의된 작업이 완료 후에, 그 컨트롤러는 대상 오브젝트로부터 연관된 파이널라이저을 삭제한다.
metadata.finalizers
필드가 비어 있을 때, 쿠버네티스는
삭제가 완료된 것으로 간주하고 오브젝트를 삭제한다.
파이널라이저가 리소스들의 가비지 컬렉션을 제어하도록 사용할 수 있다. 예를 들어, 하나의 파이널라이저를 컨트롤러가 대상 리소소를 삭제하기 전에 연관된 리소스들 또는 인프라를 정리하도록 정의할 수 있다.
파이널라이저(Finalizer)를 사용하면 리소스를 삭제하기 전 특정 정리 작업을 수행하도록 컨트롤러(Controller)에 경고하여 리소스의 가비지(Garbage) 수집을 제어할 수 있다.
파이널라이저는 보통 실행할 코드를 지정하지 않는다. 대신 파이널라이저는 일반적으로 어노테이션과 비슷하게 특정 리소스에 대한 키들의 목록이다. 일부 파이널라이저는 쿠버네티스가 자동으로 지정하지만, 사용자가 직접 지정할 수도 있다.
파이널라이저의 작동 방식
매니페스트 파일을 사용해 리소스를 생성하면
metadata.finalizers
필드에 파이널라이저를 명시할 수 있다.
리소스를 삭제하려 할 때는
삭제 요청을 처리하는 API 서버가 finalizers
필드의 값을 인식하고 다음을 수행한다.
- 삭제를 시작한 시각과 함께
metadata.deletionTimestamp
필드를 추가하도록 오브젝트를 수정한다. - 오브젝트의
metadata.finalizers
필드가 비워질 때까지 오브젝트가 제거되지 않도록 한다. 202
상태 코드를 리턴한다(HTTP "Accepted").
이 파이널라이저를 관리하는 컨트롤러는 metadata.deletionTimestamp
를 설정하는 오브젝트가 업데이트 되었음을 인지하여
오브젝트의 삭제가 요청되었음을 나타낸다.
그런 다음 컨트롤러는 그 리소스에 지정된 파이널라이저의 요구사항을 충족하려 시도한다.
컨트롤러는 파이널라이저 조건이 충족될 때 마다
리소스의 finalizers
필드에서 해당 키(key)를 제거한다.
finalizers
필드가 비워지면 deletionTimestamp
필드가 설정된 오브젝트는 자동으로 삭제된다.
또한 파이널라이저를 사용하여 관리되지 않는 리소스가 삭제되지 않도록 할 수 있다.
파이널라이저의 일반적인 예로는 퍼시스턴트 볼륨(Persistent Volume)
오브젝트가 실수로 삭제되는 것을 방지하는 kubernetes.io/pv-protection
가 있다.
파드가 퍼시스턴트 볼륨
오브젝트를 사용 중일 때
쿠버네티스는 pv-protection
파이널라이저를 추가한다.
퍼시스턴트 볼륨
을 삭제하려 하면 Terminating
상태가 되지만
파이널라이저가 존재하기 때문에 컨트롤러가 삭제할 수 없다.
파드가 퍼시스턴트 볼륨
의 사용을 중지하면
쿠버네티스가 pv-protection
파이널라이저를 해제하고 컨트롤러는 볼륨을 삭제한다.
소유자 참조, 레이블, 파이널라이저
레이블(Label)와 마찬가지로 쿠버네티스에서 소유자 참조(Owner reference)는 오브젝트 간의 관계를 설명하지만 다른 목적으로 사용된다. 컨트롤러(Controller)가 파드와 같은 오브젝트를 관리할 때 레이블을 사용하여 관련 오브젝트의 그룹에 대한 변경 사항을 추적한다. 예를 들어 잡(Job)이 하나 이상의 파드를 생성하면 잡 컨트롤러는 해당 파드에 레이블을 적용하고 클러스터 내 동일한 레이블을 갖는 파드에 대한 변경 사항을 추적한다.
또한, 잡 컨트롤러는 이러한 파드에 소유자 참조도 추가하여 파드를 생성한 잡을 가리킨다. 이 파드가 실행될 때 잡을 삭제하면 쿠버네티스는 사용자 참조(레이블 대신)를 사용하여 클러스터 내 어떤 파드가 정리되어야 하는지 결정한다.
쿠버네티스는 또한 삭제 대상 리소스에 대한 소유자 참조를 식별할 때 파이널라이저를 처리한다.
경우에 따라 파이널라이저는 종속 오브젝트의 삭제를 차단할 수 있으며 이로 인해 대상 소유자 오브젝트가 완전히 삭제되지 않고 예상보다 오래 유지될 수 있다. 이 경우 대상 소유자 및 종속 객체에 대한 파이널라이저와 소유자 참조를 확인해 원인을 해결해야 한다.
참고:
오브젝트가 삭제 상태에 있는 경우, 삭제를 계속하려면 파이널라이저를 수동으로 제거해서는 안 된다. 일반적으로 파이널라이저는 특정한 목적으로 가지고 리소스에 추가되므로, 강제로 제거하면 클러스터에 문제가 발생할 수 있다. 이는 파이널라이저의 목적을 이해하고 다른 방법(예를 들어, 일부 종속 객체를 수동으로 정리하는 것)으로 수행될 때만 수행해야 한다.다음 내용
- 쿠버네티스 블로그에서 파이널라이저를 사용해 삭제 제어하기를 읽어본다.
1.3.9 - 권장 레이블
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 | 문자열 |
위 레이블의 실제 예시는 다음 스테이트풀셋 오브젝트를 고려한다.
# 아래는 전체 명세의 일부분이다
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
애플리케이션과 애플리케이션 인스턴스
애플리케이션은 동일한 쿠버네티스 클러스터에, 심지어는 동일한 네임스페이스에도 한번 또는 그 이상 설치될 수 있다. 예를 들어, 하나의 쿠버네티스 클러스터에 WordPress가 여러 번 설치되어 각각 서로 다른 웹사이트를 서비스할 수 있다.
애플리케이션의 이름과 애플리케이션 인스턴스 이름은 별도로 기록된다.
예를 들어 WordPress는 애플리케이션 이름으로 app.kubernetes.io/name
이라는 레이블에 wordpress
라는 값을 가지며,
애플리케이션 인스턴스 이름으로는 app.kubernetes.io/instance
라는 레이블에
wordpress-abcxzy
라는 값을 가진다. 이를 통해 애플리케이션과 애플리케이션 인스턴스를
식별할 수 있다. 모든 애플리케이션 인스턴스는 고유한 이름을 가져야 한다.
예시
위 레이블을 사용하는 다른 방식에 대한 예시는 다양한 복잡성이 있다.
단순한 스테이트리스 서비스
Deployment
와 Service
오브젝트를 통해 배포된 단순한 스테이트리스 서비스의 경우를 보자. 다음 두 식별자는 레이블을 가장 간단한 형태로 사용하는 방법을 나타낸다.
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 StatefulSet
과 Service
로 MySQL과 WordPress가 더 큰 범위의 애플리케이션에 포함되어 있는 것을 알게 된다.
2 - 클러스터 아키텍처
2.1 - 노드
쿠버네티스는 컨테이너를 파드내에 배치하고 노드 에서 실행함으로 워크로드를 구동한다. 노드는 클러스터에 따라 가상 또는 물리적 머신일 수 있다. 각 노드는 컨트롤 플레인에 의해 관리되며 파드를 실행하는 데 필요한 서비스를 포함한다.
일반적으로 클러스터에는 여러 개의 노드가 있으며, 학습 또는 리소스가 제한되는 환경에서는 하나만 있을 수도 있다.
노드의 컴포넌트에는 kubelet, 컨테이너 런타임 그리고 kube-proxy가 포함된다.
관리
API 서버에 노드를 추가하는 두가지 주요 방법이 있다.
- 노드의 kubelet으로 컨트롤 플레인에 자체 등록
- 사용자(또는 다른 사용자)가 노드 오브젝트를 수동으로 추가
노드 오브젝트 또는 노드의 kubelet으로 자체 등록한 후 컨트롤 플레인은 새 노드 오브젝트가 유효한지 확인한다. 예를 들어 다음 JSON 매니페스트에서 노드를 만들려는 경우이다.
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "10.240.79.157",
"labels": {
"name": "my-first-k8s-node"
}
}
}
쿠버네티스는 내부적으로 노드 오브젝트를 생성한다(표시한다). 쿠버네티스는
kubelet이 노드의 metadata.name
필드와 일치하는 API 서버에 등록이 되어 있는지 확인한다.
노드가 정상이면(예를 들어 필요한 모든 서비스가 실행중인 경우) 파드를 실행할 수 있게 된다.
그렇지 않으면, 해당 노드는 정상이 될 때까지 모든 클러스터 활동에
대해 무시된다.
노드 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
노드 이름 고유성
이름은 노드를 식별한다. 두 노드는 동시에 같은 이름을 가질 수 없다. 쿠버네티스는 또한 같은 이름의 리소스가 동일한 객체라고 가정한다. 노드의 경우, 동일한 이름을 사용하는 인스턴스가 동일한 상태(예: 네트워크 설정, 루트 디스크 내용)와 노드 레이블과 같은 동일한 속성(attribute)을 갖는다고 암시적으로 가정한다. 인스턴스가 이름을 변경하지 않고 수정된 경우 이로 인해 불일치가 발생할 수 있다. 노드를 대폭 교체하거나 업데이트해야 하는 경우, 기존 노드 오브젝트를 먼저 API 서버에서 제거하고 업데이트 후 다시 추가해야 한다.
노드에 대한 자체-등록(self-registration)
kubelet 플래그 --register-node
가 참(기본값)일 경우, kubelet은 API 서버에
스스로 등록을 시도할 것이다. 이는 선호되는 패턴이며, 대부분의 배포판에서 사용된다.
자체-등록에 대해, kubelet은 다음 옵션과 함께 시작된다.
--kubeconfig
- apiserver에 스스로 인증하기 위한 자격증명에 대한 경로.--cloud-provider
- 자신에 대한 메터데이터를 읽기 위해 어떻게 클라우드 제공자와 소통할지에 대한 방법.--register-node
- 자동으로 API 서버에 등록.--register-with-taints
- 주어진 테인트(taint) 리스트(콤마로 분리된<key>=<value>:<effect>
)를 가진 노드 등록.register-node
가 거짓이면 동작 안 함.--node-ip
- 노드의 IP 주소.--node-labels
- 클러스터에 노드를 등록할 때 추가 할 레이블 (NodeRestriction admission plugin에 의해 적용되는 레이블 제한 사항 참고).--node-status-update-frequency
- 얼마나 자주 kubelet이 API 서버에 해당 노드 상태를 게시할 지 정의.
Node authorization mode와 NodeRestriction admission plugin이 활성화 되면, 각 kubelet은 자신이 속한 노드의 리소스에 대해서만 생성/수정할 권한을 가진다.
참고:
노드 이름 고유성 섹션에서 언급했듯이,
노드 구성을 업데이트해야 하는 경우 API 서버에 노드를
다시 등록하는 것이 좋다. 예를 들어 kubelet이 --node-labels
의 새로운 구성으로
다시 시작되더라도, 동일한 노드 이름이 사용된 경우
레이블이 해당 노드의 등록에 설정되기 때문에 변경 사항이 적용되지 않는다.
노드에 이미 스케줄된 파드는 해당 노드 구성이 kubelet 재시작에 의해 변경된 경우 오작동하거나 문제를 일으킬 수 있다. 예를 들어 이미 실행 중인 파드가 노드에 할당된 새 레이블에 대해 테인트(taint)될 수 있는 반면 해당 파드와 호환되지 않는 다른 파드는 새 레이블을 기반으로 스케줄링된다. 노드 재등록(re-registration)은 모든 파드를 비우고(drain) 다시 적절하게 스케줄링되도록 한다.
수동 노드 관리
kubectl을 사용해서 노드 오브젝트를 생성하고 수정할 수 있다.
노드 오브젝트를 수동으로 생성하려면 kubelet 플래그를 --register-node=false
로 설정한다.
--register-node
설정과 관계 없이 노드 오브젝트를 수정할 수 있다.
예를 들어 기존 노드에 레이블을 설정하거나, 스케줄 불가로 표시할 수 있다.
파드의 노드 셀렉터와 함께 노드의 레이블을 사용해서 스케줄링을 제어할 수 있다. 예를 들어, 사용 가능한 노드의 하위 집합에서만 실행되도록 파드를 제한할 수 있다.
노드를 스케줄 불가로 표시하면 스케줄러가 해당 노드에 새 파드를 배치할 수 없지만, 노드에 있는 기존 파드에는 영향을 미치지 않는다. 이는 노드 재부팅 또는 기타 유지보수 준비 단계에서 유용하다.
노드를 스케줄 불가로 표시하려면 다음을 실행한다.
kubectl cordon $NODENAME
보다 자세한 내용은 안전하게 노드를 드레인(drain)하기 를 참고한다.
참고:
데몬셋(DaemonSet)에 포함되는 일부 파드는 스케줄 불가 노드에서 실행될 수 있다. 일반적으로 데몬셋은 워크로드 애플리케이션을 비우는 경우에도 노드에서 실행되어야 하는 노드 로컬 서비스를 제공한다.노드 상태
노드의 상태는 다음의 정보를 포함한다.
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 |
참고:
커맨드 라인 도구를 사용해서 통제된(cordoned) 노드의 세부 정보를 출력하는 경우 조건에는SchedulingDisabled
이 포함된다. SchedulingDisabled
은 쿠버네티스 API의 조건이 아니며,
대신 통제된(cordoned) 노드는 사양에 스케줄 불가로 표시된다.쿠버네티스 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 컨디션의 status
가 pod-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
에 비하면, 리스는 경량의 리소스이다.
큰 규모의 클러스터에서는 리스를 하트비트에 사용하여
업데이트로 인한 성능 영향을 줄일 수 있다.
kubelet은 노드의 .status
생성과 업데이트 및
관련된 리스의 업데이트를 담당한다.
- kubelet은 상태가 변경되거나 설정된 인터벌보다 오래 업데이트가 없는 경우
노드의
.status
를 업데이트한다. 노드의.status
업데이트에 대한 기본 인터벌은 접근이 불가능한 노드에 대한 타임아웃인 40초 보다 훨씬 긴 5분이다. - kubelet은 리스 오브젝트를 (기본 업데이트 인터벌인) 매 10초마다
생성하고 업데이트한다. 리스 업데이트는 노드의
.status
업데이트와는 독립적이다. 만약 리스 업데이트가 실패하면, kubelet은 200밀리초에서 시작하고 7초의 상한을 갖는 지수적 백오프를 사용해서 재시도한다.
노드 컨트롤러
노드 컨트롤러는 노드의 다양한 측면을 관리하는 쿠버네티스 컨트롤 플레인 컴포넌트이다.
노드 컨트롤러는 노드가 생성되어 유지되는 동안 다양한 역할을 한다. 첫째는 등록 시점에 (CIDR 할당이 사용토록 설정된 경우) 노드에 CIDR 블럭을 할당하는 것이다.
두 번째는 노드 컨트롤러의 내부 노드 리스트를 클라우드 제공사업자의 사용 가능한 머신 리스트 정보를 근거로 최신상태로 유지하는 것이다. 클라우드 환경에서 동작 중일 경우, 노드상태가 불량할 때마다, 노드 컨트롤러는 해당 노드용 VM이 여전히 사용 가능한지에 대해 클라우드 제공사업자에게 묻는다. 사용 가능하지 않을 경우, 노드 컨트롤러는 노드 리스트로부터 그 노드를 삭제한다.
세 번째는 노드의 동작 상태를 모니터링하는 것이다. 노드 컨트롤러는 다음을 담당한다.
- 노드가 접근 불가능(unreachable) 상태가 되는 경우,
노드의
.status
필드의Ready
컨디션을 업데이트한다. 이 경우에는 노드 컨트롤러가Ready
컨디션을Unknown
으로 설정한다. - 노드가 계속 접근 불가능 상태로 남아있는 경우, 해당 노드의 모든 파드에 대해서
API를 이용한 축출을
트리거한다. 기본적으로, 노드 컨트롤러는 노드를
Unknown
으로 마킹한 뒤 5분을 기다렸다가 최초의 축출 요청을 시작한다.
기본적으로, 노드 컨트롤러는 5 초마다 각 노드의 상태를 체크한다.
체크 주기는 kube-controller-manager
구성 요소의
--node-monitor-period
플래그를 이용하여 설정할 수 있다.
축출 빈도 제한
대부분의 경우, 노드 컨트롤러는 초당 --node-eviction-rate
(기본값 0.1)로
축출 속도를 제한한다. 이 말은 10초당 1개의 노드를 초과하여
파드 축출을 하지 않는다는 의미가 된다.
노드 축출 행위는 주어진 가용성 영역 내 하나의 노드가 상태가 불량할
경우 변화한다. 노드 컨트롤러는 영역 내 동시에 상태가 불량한 노드의 퍼센티지가 얼마나 되는지
체크한다(Ready
컨디션은 Unknown
또는
False
값을 가진다).
- 상태가 불량한 노드의 비율이 최소
--unhealthy-zone-threshold
(기본값 0.55)가 되면 축출 속도가 감소한다. - 클러스터가 작으면 (즉
--large-cluster-size-threshold
노드 이하면 - 기본값 50) 축출이 중지된다. - 이외의 경우, 축출 속도는 초당
--secondary-node-eviction-rate
(기본값 0.01)로 감소된다.
이 정책들이 가용성 영역 단위로 실행되어지는 이유는 나머지가 연결되어 있는 동안 하나의 가용성 영역이 컨트롤 플레인으로부터 분할되어 질 수도 있기 때문이다. 만약 클러스터가 여러 클라우드 제공사업자의 가용성 영역에 걸쳐 있지 않는 이상, 축출 매커니즘은 영역 별 가용성을 고려하지 않는다.
노드가 가용성 영역들에 걸쳐 퍼져 있는 주된 이유는 하나의 전체 영역이
장애가 발생할 경우 워크로드가 상태 양호한 영역으로 이전되어질 수 있도록 하기 위해서이다.
그러므로, 하나의 영역 내 모든 노드들이 상태가 불량하면 노드 컨트롤러는
--node-eviction-rate
의 정상 속도로 축출한다. 코너 케이스란 모든 영역이
완전히 상태불량(클러스터 내 양호한 노드가 없는 경우)한 경우이다.
이러한 경우, 노드 컨트롤러는 컨트롤 플레인과 노드 간 연결에 문제가
있는 것으로 간주하고 축출을 실행하지 않는다. (중단 이후 일부 노드가
다시 보이는 경우 노드 컨트롤러는 상태가 양호하지 않거나 접근이 불가능한
나머지 노드에서 파드를 축출한다.)
또한, 노드 컨트롤러는 파드가 테인트를 허용하지 않을 때 NoExecute
테인트 상태의
노드에서 동작하는 파드에 대한 축출 책임을 가지고 있다.
추가로, 노드 컨틀로러는 연결할 수 없거나, 준비되지 않은 노드와 같은 노드 문제에 상응하는
테인트를 추가한다.
이는 스케줄러가 비정상적인 노드에 파드를 배치하지 않게 된다.
리소스 용량 추적
노드 오브젝트는 노드 리소스 용량에 대한 정보: 예를 들어, 사용 가능한 메모리의 양과 CPU의 수를 추적한다. 노드의 자체 등록은 등록하는 중에 용량을 보고한다. 수동으로 노드를 추가하는 경우 추가할 때 노드의 용량 정보를 설정해야 한다.
쿠버네티스 스케줄러는 노드 상에 모든 노드에 대해 충분한 리소스가 존재하도록 보장한다. 스케줄러는 노드 상에 컨테이너에 대한 요청의 합이 노드 용량보다 더 크지 않도록 체크한다. 요청의 합은 kubelet에서 관리하는 모든 컨테이너를 포함하지만, 컨테이너 런타임에 의해 직접적으로 시작된 컨 테이너는 제외되고 kubelet의 컨트롤 범위 밖에서 실행되는 모든 프로세스도 제외된다.
참고:
파드 형태가 아닌 프로세스에 대해 명시적으로 리소스를 확보하려면, 시스템 데몬에 사용할 리소스 예약하기을 본다.노드 토폴로지
Kubernetes v1.18 [beta]
TopologyManager
기능 게이트(feature gate)를
활성화 시켜두면, kubelet이 리소스 할당 결정을 할 때 토폴로지 힌트를 사용할 수 있다.
자세한 내용은
노드의 컨트롤 토폴로지 관리 정책을 본다.
그레이스풀(Graceful) 노드 셧다운(shutdown)
Kubernetes v1.21 [beta]
kubelet은 노드 시스템 셧다운을 감지하고 노드에서 실행 중인 파드를 종료하려고 시도한다.
Kubelet은 노드가 종료되는 동안 파드가 일반 파드 종료 프로세스를 따르도록 한다.
그레이스풀 노드 셧다운 기능은 systemd inhibitor locks를 사용하여 주어진 기간 동안 노드 종료를 지연시키므로 systemd에 의존한다.
그레이스풀 노드 셧다운은 1.21에서 기본적으로 활성화된 GracefulNodeShutdown
기능 게이트로
제어된다.
기본적으로, 아래 설명된 두 구성 옵션,
shutdownGracePeriod
및 shutdownGracePeriodCriticalPods
는 모두 0으로 설정되어 있으므로,
그레이스풀 노드 셧다운 기능이 활성화되지 않는다.
기능을 활성화하려면, 두 개의 kubelet 구성 설정을 적절하게 구성하고
0이 아닌 값으로 설정해야 한다.
그레이스풀 셧다운 중에 kubelet은 다음의 두 단계로 파드를 종료한다.
- 노드에서 실행 중인 일반 파드를 종료시킨다.
- 노드에서 실행 중인 중요(critical) 파드를 종료시킨다.
그레이스풀 노드 셧다운 기능은
두 개의 KubeletConfiguration
옵션으로 구성된다.
shutdownGracePeriod
:- 노드가 종료를 지연해야 하는 총 기간을 지정한다. 이것은 모든 일반 및 중요 파드의 파드 종료에 필요한 총 유예 기간에 해당한다.
shutdownGracePeriodCriticalPods
:- 노드 종료 중에 중요 파드를
종료하는 데 사용되는 기간을 지정한다.
이 값은
shutdownGracePeriod
보다 작아야 한다.
- 노드 종료 중에 중요 파드를
종료하는 데 사용되는 기간을 지정한다.
이 값은
예를 들어, shutdownGracePeriod=30s
,
shutdownGracePeriodCriticalPods=10s
인 경우, kubelet은 노드 종료를 30초까지
지연시킨다. 종료하는 동안 처음 20(30-10)초는 일반 파드의
유예 종료에 할당되고, 마지막 10초는
중요 파드의 종료에 할당된다.
참고:
그레이스풀 노드 셧다운 과정에서 축출된 파드는 셧다운(shutdown)된 것으로 표시된다.
kubectl get pods
명령을 실행하면 축출된 파드의 상태가 Terminated
으로 표시된다.
그리고 kubectl describe pod
명령을 실행하면 노드 셧다운으로 인해 파드가 축출되었음을 알 수 있다.
Reason: Terminated
Message: Pod was terminated in response to imminent node shutdown.
논 그레이스풀 노드 셧다운
Kubernetes v1.26 [beta]
전달한 명령이 kubelet에서 사용하는 금지 잠금 메커니즘(inhibitor locks mechanism)을 트리거하지 않거나, 또는 사용자 오류(예: ShutdownGracePeriod 및 ShutdownGracePeriodCriticalPods가 제대로 설정되지 않음)로 인해 kubelet의 노드 셧다운 관리자(Node Shutdown Mananger)가 노드 셧다운 액션을 감지하지 못할 수 있다. 자세한 내용은 위의 그레이스풀 노드 셧다운 섹션을 참조한다.
노드가 셧다운되었지만 kubelet의 노드 셧다운 관리자가 이를 감지하지 못하면, 스테이트풀셋에 속한 파드는 셧다운된 노드에 '종료 중(terminating)' 상태로 고착되어 다른 동작 중인 노드로 이전될 수 없다. 이는 셧다운된 노드의 kubelet이 파드를 지울 수 없어서 결국 스테이트풀셋이 동일한 이름으로 새 파드를 만들 수 없기 때문이다. 만약 파드가 사용하던 볼륨이 있다면, 볼륨어태치먼트(VolumeAttachment)도 기존의 셧다운된 노드에서 삭제되지 않아 결국 파드가 사용하던 볼륨이 다른 동작 중인 노드에 연결(attach)될 수 없다. 결과적으로, 스테이트풀셋에서 실행되는 애플리케이션이 제대로 작동하지 않는다. 기존의 셧다운된 노드가 정상으로 돌아오지 못하면, 이러한 파드는 셧다운된 노드에 '종료 중(terminating)' 상태로 영원히 고착될 것이다.
위와 같은 상황을 완화하기 위해, 사용자가 node.kubernetes.io/out-of-service
테인트를 NoExecute
또는 NoSchedule
값으로
추가하여 노드를 서비스 불가(out-of-service) 상태로 표시할 수 있다.
kube-controller-manager
에 NodeOutOfServiceVolumeDetach
기능 게이트
가 활성화되어 있고, 노드가 이 테인트에 의해 서비스 불가 상태로 표시되어 있는 경우,
노드에 매치되는 톨러레이션이 없다면 노드 상의 파드는 강제로 삭제될 것이고,
노드 상에서 종료되는 파드에 대한 볼륨 해제(detach) 작업은 즉시 수행될 것이다.
이를 통해 서비스 불가 상태 노드의 파드가 빠르게 다른 노드에서 복구될 수 있다.
논 그레이스풀 셧다운 과정 동안, 파드는 다음의 두 단계로 종료된다.
- 매치되는
out-of-service
톨러레이션이 없는 파드를 강제로 삭제한다. - 이러한 파드에 대한 볼륨 해제 작업을 즉시 수행한다.
참고:
node.kubernetes.io/out-of-service
테인트를 추가하기 전에, 노드가 완전한 셧다운 또는 전원 꺼짐 상태에 있는지 (재시작 중인 것은 아닌지) 확인한다.- 사용자가 서비스 불가 상태 테인트를 직접 추가한 것이기 때문에, 파드가 다른 노드로 옮겨졌고 셧다운 상태였던 노드가 복구된 것을 확인했다면 사용자가 서비스 불가 상태 테인트를 수동으로 제거해야 한다.
파드 우선순위 기반 그레이스풀 노드 셧다운
Kubernetes v1.23 [alpha]
그레이스풀 노드 셧다운 시 파드 셧다운 순서에 더 많은 유연성을 제공할 수 있도록, 클러스터에 프라이어리티클래스(PriorityClass) 기능이 활성화되어 있으면 그레이스풀 노드 셧다운 과정에서 파드의 프라이어리티클래스가 고려된다. 이 기능으로 그레이스풀 노드 셧다운 시 파드가 종료되는 순서를 클러스터 관리자가 프라이어리티 클래스 기반으로 명시적으로 정할 수 있다.
위에서 기술된 것처럼, 그레이스풀 노드 셧다운 기능은 파드를 중요하지 않은(non-critical) 파드와 중요한(critical) 파드 2단계(phase)로 구분하여 종료시킨다. 셧다운 시 파드가 종료되는 순서를 명시적으로 더 상세하게 정해야 한다면, 파드 우선순위 기반 그레이스풀 노드 셧다운을 사용할 수 있다.
그레이스풀 노드 셧다운 과정에서 파드 우선순위가 고려되기 때문에, 그레이스풀 노드 셧다운이 여러 단계로 일어날 수 있으며, 각 단계에서 특정 프라이어리티 클래스의 파드를 종료시킨다. 정확한 단계와 단계별 셧다운 시간은 kubelet에 설정할 수 있다.
다음과 같이 클러스터에 커스텀 파드 프라이어리티 클래스가 있다고 가정하자.
파드 프라이어리티 클래스 이름 | 파드 프라이어리티 클래스 값 |
---|---|
custom-class-a | 100000 |
custom-class-b | 10000 |
custom-class-c | 1000 |
regular/unset | 0 |
kubelet 환경 설정 안의
shutdownGracePeriodByPodPriority
설정은 다음과 같을 수 있다.
파드 프라이어리티 클래스 값 | 종료 대기 시간 |
---|---|
100000 | 10 seconds |
10000 | 180 seconds |
1000 | 120 seconds |
0 | 60 seconds |
이를 나타내는 kubelet 환경 설정 YAML은 다음과 같다.
shutdownGracePeriodByPodPriority:
- priority: 100000
shutdownGracePeriodSeconds: 10
- priority: 10000
shutdownGracePeriodSeconds: 180
- priority: 1000
shutdownGracePeriodSeconds: 120
- priority: 0
shutdownGracePeriodSeconds: 60
위의 표에 의하면 priority
값이 100000 이상인 파드는 종료까지 10초만 주어지며,
10000 이상 ~ 100000 미만이면 180초,
1000 이상 ~ 10000 미만이면 120초가 주어진다.
마지막으로, 다른 모든 파드는 종료까지 60초가 주어질 것이다.
모든 클래스에 대해 값을 명시할 필요는 없다. 예를 들어, 대신 다음과 같은 구성을 사용할 수도 있다.
파드 프라이어리티 클래스 값 | 종료 대기 시간 |
---|---|
100000 | 300 seconds |
1000 | 120 seconds |
0 | 60 seconds |
위의 경우, custom-class-b
에 속하는 파드와 custom-class-c
에 속하는 파드는
동일한 종료 대기 시간을 갖게 될 것이다.
특정 범위에 해당되는 파드가 없으면, kubelet은 해당 범위에 해당되는 파드를 위해 기다려 주지 않는다. 대신, kubelet은 즉시 다음 프라이어리티 클래스 값 범위로 넘어간다.
기능이 활성화되어 있지만 환경 설정이 되어 있지 않으면, 순서 지정 동작이 수행되지 않을 것이다.
이 기능을 사용하려면 GracefulNodeShutdownBasedOnPodPriority
기능 게이트를 활성화해야 하고,
kubelet config의
ShutdownGracePeriodByPodPriority
를
파드 프라이어리티 클래스 값과 각 값에 대한 종료 대기 시간을 명시하여
지정해야 한다.
참고:
그레이스풀 노드 셧다운 과정에서 파드 프라이어리티를 고려하는 기능은 쿠버네티스 v1.23에서 알파 기능으로 도입되었다. 쿠버네티스 1.31에서 이 기능은 베타 상태이며 기본적으로 활성화되어 있다.graceful_shutdown_start_time_seconds
및 graceful_shutdown_end_time_seconds
메트릭은
노드 셧다운을 모니터링하기 위해 kubelet 서브시스템에서 방출된다.
스왑(swap) 메모리 관리
Kubernetes v1.22 [alpha]
쿠버네티스 1.22 이전에는 노드가 스왑 메모리를 지원하지 않았다. 그리고 kubelet은 노드에서 스왑을 발견하지 못한 경우 시작과 동시에 실패하도록 되어 있었다. 1.22부터는 스왑 메모리 지원을 노드 단위로 활성화할 수 있다.
노드에서 스왑을 활성화하려면, NodeSwap
기능 게이트가 kubelet에서
활성화되어야 하며, 명령줄 플래그 --fail-swap-on
또는
구성 설정에서 failSwapOn
가
false로 지정되어야 한다.
경고:
메모리 스왑 기능이 활성화되면, 시크릿 오브젝트의 내용과 같은 tmpfs에 기록되었던 쿠버네티스 데이터가 디스크에 스왑될 수 있다.사용자는 또한 선택적으로 memorySwap.swapBehavior
를 구성할 수 있으며,
이를 통해 노드가 스왑 메모리를 사용하는 방식을 명시한다. 예를 들면,
memorySwap:
swapBehavior: LimitedSwap
swapBehavior
에 가용한 구성 옵션은 다음과 같다.
LimitedSwap
: 쿠버네티스 워크로드는 스왑을 사용할 수 있는 만큼으로 제한된다. 쿠버네티스에 의해 관리되지 않는 노드의 워크로드는 여전히 스왑될 수 있다.UnlimitedSwap
: 쿠버네티스 워크로드는 요청한 만큼 스왑 메모리를 사용할 수 있으며, 시스템의 최대치까지 사용 가능하다.
만약 memorySwap
구성이 명시되지 않았고 기능 게이트가 활성화되어 있다면,
kubelet은 LimitedSwap
설정과 같은 행동을
기본적으로 적용한다.
LimitedSwap
설정에 대한 행동은 노드가 ("cgroups"으로 알려진)
제어 그룹이 v1 또는 v2 중에서 무엇으로 동작하는가에 따라서 결정된다.
- cgroupsv1: 쿠버네티스 워크로드는 메모리와 스왑의 조합을 사용할 수 있다. 파드의 메모리 제한이 설정되어 있다면 가용 상한이 된다.
- cgroupsv2: 쿠버네티스 워크로드는 스왑 메모리를 사용할 수 없다.
테스트를 지원하고 피드벡을 제공하기 위한 정보는 KEP-2400 및 디자인 제안에서 찾을 수 있다.
다음 내용
- 노드를 구성하는 컴포넌트에 대해 알아본다.
- 노드에 대한 API 정의를 읽어본다.
- 아키텍처 디자인 문서의 노드 섹션을 읽어본다.
- 테인트와 톨러레이션을 읽어본다.
2.2 - 컨트롤 플레인-노드 간 통신
이 문서는 API 서버와 쿠버네티스 클러스터 사이에 대한 통신 경로의 목록을 작성한다. 이는 사용자가 신뢰할 수 없는 네트워크(또는 클라우드 공급자의 완전한 퍼블릭 IP)에서 클러스터를 실행할 수 있도록 네트워크 구성을 강화하기 위한 맞춤 설치를 할 수 있도록 한다.
노드에서 컨트롤 플레인으로의 통신
쿠버네티스에는 "허브 앤 스포크(hub-and-spoke)" API 패턴을 가지고 있다. 노드(또는 노드에서 실행되는 파드들)의 모든 API 사용은 API 서버에서 종료된다. 다른 컨트롤 플레인 컴포넌트 중 어느 것도 원격 서비스를 노출하도록 설계되지 않았다. API 서버는 하나 이상의 클라이언트 인증 형식이 활성화된 보안 HTTPS 포트(일반적으로 443)에서 원격 연결을 수신하도록 구성된다. 특히 익명의 요청 또는서비스 어카운트 토큰이 허용되는 경우, 하나 이상의 권한 부여 형식을 사용해야 한다.
노드는 유효한 클라이언트 자격 증명과 함께 API 서버에 안전하게 연결할 수 있도록 클러스터에 대한 공개 루트 인증서(root certificate)로 프로비전해야 한다. 클라이언트 인증서(client certificate) 형식으로 kubelet의 클라이언트 자격 증명을 사용하는 것은 좋은 방법이다. kubelet 클라이언트 인증서(client certificate)의 자동 프로비저닝은 kubelet TLS 부트스트랩을 참고한다.
API 서버에 연결하려는 파드는 서비스 어카운트를 활용하여 안전하게
쿠버네티스가 공개 루트 인증서(root certificate)와 유효한 베어러 토큰(bearer token)을 파드가 인스턴스화될 때
파드에 자동으로 주입할 수 있다.
kubernetes
서비스(default
네임스페이스의)는 API 서버의 HTTPS 엔드포인트로 리디렉션되는
가상 IP 주소(kube-proxy를 통해)로 구성되어 있다.
컨트롤 플레인 컴포넌트는 보안 포트를 통해 클러스터 API 서버와도 통신한다.
결과적으로, 노드 및 노드에서 실행되는 파드에서 컨트롤 플레인으로 연결하기 위한 기본 작동 모드는 기본적으로 보호되며 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행될 수 있다.
컨트롤 플레인에서 노드로의 통신
컨트롤 플레인(API 서버)에서 노드로는 두 가지 기본 통신 경로가 있다. 첫 번째는 API 서버에서 클러스터의 각 노드에서 실행되는 kubelet 프로세스이다. 두 번째는 API 서버의 프록시 기능을 통해 API 서버에서 모든 노드, 파드 또는 서비스에 이르는 것이다.
API 서버에서 kubelet으로의 통신
API 서버에서 kubelet으로의 연결은 다음의 용도로 사용된다.
- 파드에 대한 로그를 가져온다.
- 실행 중인 파드에 (보통의 경우 kubectl을 통해) 연결한다.
- kubelet의 포트-포워딩 기능을 제공한다.
위와 같은 연결은 kubelet의 HTTPS 엔드포인트에서 종료된다. 기본적으로, API 서버는 kubelet의 제공(serving) 인증서를 확인하지 않는다. 이는 연결이 중간자 공격(man-in-the-middle)에 시달리게 하며, 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행하기에 안전하지 않다 .
이 연결을 확인하려면, --kubelet-certificate-authority
플래그를 사용하여
API 서버에 kubelet의 제공(serving) 인증서를 확인하는데 사용할 루트 인증서 번들을 제공한다.
이것이 가능하지 않은 경우, 신뢰할 수 없는 네트워크 또는 공용 네트워크를 통한 연결을 피하기 위해 필요한 경우, API 서버와 kubelet 간 SSH 터널링을 사용한다.
마지막으로, kubelet API를 보호하려면 Kubelet 인증 및/또는 인가를 활성화해야 한다.
API 서버에서 노드, 파드 및 서비스로의 통신
API 서버에서 노드, 파드 또는 서비스로의 연결은 기본적으로 일반 HTTP 연결로 연결되므로
인증되거나 암호화되지 않는다. 이 연결에서 URL을 노드, 파드 또는 서비스 이름에 접두어 https:
을 붙여
보안 HTTPS 연결이 되도록 실행할 수 있지만, HTTPS 엔드포인트가 제공한 인증서의 유효성을 검증하지 않으며
클라이언트 자격 증명도 제공하지 않는다.
그래서 연결이 암호화되는 동안 그 어떤 무결성도 보장되지 않는다.
이러한 연결은 신뢰할 수 없는 네트워크 및/또는 공용 네트워크에서 실행하기에 현재는 안전하지 않다 .
SSH 터널
쿠버네티스는 SSH 터널을 지원하여 컨트롤 플레인에서 노드로의 통신 경로를 보호한다. 이 구성에서, API 서버는 클러스터의 각 노드에 SSH 터널을 시작하고 (포트 22에서 수신 대기하는 ssh 서버에 연결) 터널을 통해 kubelet, 노드, 파드 또는 서비스로 향하는 모든 트래픽을 전달한다. 이 터널은 노드가 실행 중인 네트워크의 외부로 트래픽이 노출되지 않도록 한다.
참고:
SSH 터널은 현재 더 이상 사용되지 않으므로, 수행 중인 작업이 어떤 것인지 모른다면 사용하면 안 된다. Konnectivity 서비스가 SSH 통신 채널을 대체한다.Konnectivity 서비스
Kubernetes v1.18 [beta]
SSH 터널을 대체로, Konnectivity 서비스는 컨트롤 플레인과 클러스터 간 통신에 TCP 레벨 프록시를 제공한다. Konnectivity 서비스는 컨트롤 플레인 네트워크의 Konnectivity 서버와 노드 네트워크의 Konnectivity 에이전트, 두 부분으로 구성된다. Konnectivity 에이전트는 Konnectivity 서버에 대한 연결을 시작하고 네트워크 연결을 유지한다. Konnectivity 서비스를 활성화한 후, 모든 컨트롤 플레인에서 노드로의 트래픽은 이 연결을 통과한다.
Konnectivity 서비스 태스크에 따라 클러스터에서 Konnectivity 서비스를 설정한다.
2.3 - 리스(Lease)
분산 시스템에는 종종 공유 리소스를 잠그고 노드 간의 활동을 조정하는 메커니즘을 제공하는 "리스(Lease)"가 필요하다.
쿠버네티스에서 "리스" 개념은 coordination.k8s.io
API 그룹에 있는 Lease
오브젝트로 표현되며,
노드 하트비트 및 컴포넌트 수준의 리더 선출과 같은 시스템 핵심 기능에서 사용된다.
노드 하트비트
쿠버네티스는 리스 API를 사용하여 kubelet 노드의 하트비트를 쿠버네티스 API 서버에 전달한다.
모든 노드
에는 같은 이름을 가진 Lease
오브젝트가 kube-node-lease
네임스페이스에 존재한다.
내부적으로, 모든 kubelet 하트비트는 이 Lease
오브젝트에 대한 업데이트 요청이며,
이 업데이트 요청은 spec.renewTime
필드를 업데이트한다.
쿠버네티스 컨트롤 플레인은 이 필드의 타임스탬프를 사용하여 해당 노드
의 가용성을 확인한다.
자세한 내용은 노드 리스 오브젝트를 참조한다.
리더 선출
리스는 쿠버네티스에서도 특정 시간 동안 컴포넌트의 인스턴스 하나만 실행되도록 보장하는 데에도 사용된다.
이는 구성 요소의 한 인스턴스만 활성 상태로 실행되고 다른 인스턴스는 대기 상태여야 하는
kube-controller-manager
및 kube-scheduler
와 같은 컨트롤 플레인 컴포넌트의
고가용성 설정에서 사용된다.
API 서버 신원
Kubernetes v1.26 [beta]
쿠버네티스 v1.26부터, 각 kube-apiserver
는 리스 API를 사용하여 시스템의 나머지 부분에 자신의 신원을 게시한다.
그 자체로는 특별히 유용하지는 않지만, 이것은 클라이언트가 쿠버네티스 컨트롤 플레인을 운영 중인 kube-apiserver
인스턴스 수를
파악할 수 있는 메커니즘을 제공한다.
kube-apiserver 리스의 존재는 향후 각 kube-apiserver 간의 조정이 필요할 때
기능을 제공해 줄 수 있다.
각 kube-apiserver가 소유한 리스는 kube-system
네임스페이스에서kube-apiserver-<sha256-hash>
라는 이름의
리스 오브젝트를 확인하여 볼 수 있다. 또는 k8s.io/component=kube-apiserver
레이블 설렉터를 사용하여 볼 수도 있다.
$ kubectl -n kube-system get lease -l k8s.io/component=kube-apiserver
NAME HOLDER AGE
kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4 5m33s
kube-apiserver-dz2dqprdpsgnm756t5rnov7yka kube-apiserver-dz2dqprdpsgnm756t5rnov7yka_84f2a85d-37c1-4b14-b6b9-603e62e4896f 4m23s
kube-apiserver-fyloo45sdenffw2ugwaz3likua kube-apiserver-fyloo45sdenffw2ugwaz3likua_c5ffa286-8a9a-45d4-91e7-61118ed58d2e 4m43s
리스 이름에 사용된 SHA256 해시는 kube-apiserver가 보는 OS 호스트 이름을 기반으로 한다.
각 kube-apiserver는 클러스터 내에서 고유한 호스트 이름을 사용하도록 구성해야 한다.
동일한 호스트명을 사용하는 새로운 kube-apiserver 인스턴스는 새 리스 오브젝트를 인스턴스화하는 대신 새로운 소유자 ID를 사용하여 기존 리스를 차지할 수 있다.
kube-apiserver가 사용하는 호스트네임은 kubernetes.io/hostname
레이블의 값을 확인하여 확인할 수 있다.
$ kubectl -n kube-system get lease kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
creationTimestamp: "2022-11-30T15:37:15Z"
labels:
k8s.io/component: kube-apiserver
kubernetes.io/hostname: kind-control-plane
name: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a
namespace: kube-system
resourceVersion: "18171"
uid: d6c68901-4ec5-4385-b1ef-2d783738da6c
spec:
holderIdentity: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4
leaseDurationSeconds: 3600
renewTime: "2022-11-30T18:04:27.912073Z"
더 이상 존재하지 않는 kube-apiserver의 만료된 임대는 1시간 후에 새로운 kube-apiserver에 의해 가비지 컬렉션된다.
2.4 - 컨트롤러
로보틱스와 자동화에서 컨트롤 루프 는 시스템 상태를 조절하는 종료되지 않는 루프이다.
컨트롤 루프의 예시: 실내 온도 조절기
사용자는 온도를 설정해서, 사용자가 의도한 상태 를 온도 조절기에 알려준다. 실제 실내 온도는 현재 상태 이다. 온도 조절기는 장비를 켜거나 꺼서 현재 상태를 의도한 상태에 가깝게 만든다.
쿠버네티스에서 컨트롤러는 클러스터 의 상태를 관찰 한 다음, 필요한 경우에 생성 또는 변경을 요청하는 컨트롤 루프이다. 각 컨트롤러는 현재 클러스터 상태를 의도한 상태에 가깝게 이동한다.컨트롤러 패턴
컨트롤러는 적어도 하나 이상의 쿠버네티스 리소스 유형을 추적한다. 이 오브젝트 는 의도한 상태를 표현하는 사양 필드를 가지고 있다. 해당 리소스의 컨트롤러(들)은 현재 상태를 의도한 상태에 가깝게 만드는 역할을 한다.
컨트롤러는 스스로 작업을 수행할 수 있다. 보다 일반적으로, 쿠버네티스에서는 컨트롤러가 API 서버 로 유용한 부수적인 효과가 있는 메시지를 발송한다. 그 예시는 아래에서 볼 수 있다.
API 서버를 통한 제어
잡(Job) 컨트롤러는 쿠버네티스 내장 컨트롤러의 예시이다. 내장 컨트롤러는 클러스터 API 서버와 상호 작용하며 상태를 관리한다.
잡은 단일 파드 또는 여러 파드를 실행하고, 작업을 수행한 다음 중지하는 쿠버네티스 리소스 이다.
(일단 스케줄되면, 파드 오브젝트는 kubelet 의 의도한 상태 중 일부가 된다.)
잡 컨트롤러가 새로운 작업을 확인하면, 클러스터 어딘가에서 노드 집합의 kubelet이 작업을 수행하기에 적합한 수의 파드를 실행하게 한다. 잡 컨트롤러는 어떤 파드 또는 컨테이너를 스스로 실행하지 않는다. 대신, 잡 컨트롤러는 API 서버에 파드를 생성하거나 삭제하도록 지시한다. 컨트롤 플레인의 다른 컴포넌트는 신규 정보 (예약 및 실행해야 하는 새 파드가 있다는 정보)에 대응하여, 결국 해당 작업을 완료시킨다.
새 잡을 생성하고 나면, 의도한 상태는 해당 잡을 완료하는 것이 된다. 잡 컨트롤러는 현재 상태를 의도한 상태에 가깝게 만들며, 사용자가 원하는 잡을 수행하기 위해 파드를 생성해서 잡이 완료에 가까워 지도록 한다.
또한, 컨트롤러는 오브젝트의 설정을 업데이트 한다.
예시: 잡을 위한 작업이 종료된 경우, 잡 컨트롤러는
잡 오브젝트가 Finished
로 표시되도록 업데이트한다.
(이것은 지금 방 온도가 설정한 온도인 것을 표시하기 위해 실내 온도 조절기의 빛을 끄는 것과 약간 비슷하다).
직접 제어
잡과는 대조적으로, 일부 컨트롤러는 클러스터 외부의 것을 변경해야 할 필요가 있다.
예를 들어, 만약 컨트롤 루프를 사용해서 클러스터에 충분한 노드들이 있도록 만드는 경우, 해당 컨트롤러는 필요할 때 새 노드를 설정할 수 있도록 현재 클러스터 외부의 무언가를 필요로 한다.
외부 상태와 상호 작용하는 컨트롤러는 API 서버에서 의도한 상태를 찾은 다음, 외부 시스템과 직접 통신해서 현재 상태를 보다 가깝게 만든다.
(실제로 클러스터의 노드를 수평으로 확장하는 컨트롤러가 있다.)
여기서 중요한 점은 컨트롤러가 의도한 상태를 가져오기 위해 약간의 변화를 주고, 현재 상태를 클러스터의 API 서버에 다시 보고한다는 것이다. 다른 컨트롤 루프는 보고된 데이터를 관찰하고 자체 조치를 할 수 있다.
온도 조절기 예에서 방이 매우 추우면 다른 컨트롤러가 서리 방지 히터를 켤 수도 있다. 쿠버네티스 클러스터에서는 쿠버네티스 확장을 통해 IP 주소 관리 도구, 스토리지 서비스, 클라우드 제공자의 API 및 기타 서비스 등과 간접적으로 연동하여 이를 구현한다.
의도한 상태와 현재 상태
쿠버네티스는 클라우드-네이티브 관점에서 시스템을 관찰하며, 지속적인 변화에 대응할 수 있다.
작업이 발생함에 따라 어떤 시점에서든 클러스터가 변경 될 수 있으며 컨트롤 루프가 자동으로 실패를 바로잡는다. 이는 잠재적으로, 클러스터가 안정적인 상태에 도달하지 못하는 것을 의미한다.
클러스터의 컨트롤러가 실행 중이고 유용한 변경을 수행할 수 있는 한, 전체 상태가 안정적인지 아닌지는 중요하지 않다.
디자인
디자인 원리에 따라, 쿠버네티스는 클러스터 상태의 각 특정 측면을 관리하는 많은 컨트롤러를 사용한다. 가장 일반적으로, 특정 컨트롤 루프 (컨트롤러)는 의도한 상태로서 한 종류의 리소스를 사용하고, 의도한 상태로 만들기 위해 다른 종류의 리소스를 관리한다. 예를 들어, 잡 컨트롤러는 잡 오브젝트(새 작업을 발견하기 위해)와 파드 오브젝트(잡을 실행하고, 완료된 시기를 확인하기 위해)를 추적한다. 이 경우 파드는 잡 컨트롤러가 생성하는 반면, 잡은 다른 컨트롤러가 생성한다.
컨트롤 루프들로 연결 구성된 하나의 모놀리식(monolithic) 집합보다, 간단한 컨트롤러를 여러 개 사용하는 것이 유용하다. 컨트롤러는 실패할 수 있으므로, 쿠버네티스는 이를 허용하도록 디자인되었다.
참고:
동일한 종류의 오브젝트를 만들거나 업데이트하는 여러 컨트롤러가 있을 수 있다. 이면에, 쿠버네티스 컨트롤러는 컨트롤 하고 있는 리소스에 연결된 리소스에만 주의를 기울인다.
예를 들어, 디플로이먼트와 잡을 가지고 있다. 이 두 가지 모두 파드를 생성한다. 잡 컨트롤러는 디플로이먼트가 생성한 파드를 삭제하지 않는다. 이는 컨트롤러가 해당 파드를 구별하기 위해 사용할 수 있는 정보(레이블)가 있기 때문이다.
컨트롤러를 실행하는 방법
쿠버네티스에는 kube-controller-manager 내부에서 실행되는 내장된 컨트롤러 집합이 있다. 이 내장 컨트롤러는 중요한 핵심 동작을 제공한다.
디플로이먼트 컨트롤러와 잡 컨트롤러는 쿠버네티스의 자체("내장" 컨트롤러)로 제공되는 컨트롤러 예시이다. 쿠버네티스를 사용하면 복원력이 뛰어난 컨트롤 플레인을 실행할 수 있으므로, 어떤 내장 컨트롤러가 실패하더라도 다른 컨트롤 플레인의 일부가 작업을 이어서 수행한다.
컨트롤 플레인의 외부에서 실행하는 컨트롤러를 찾아서 쿠버네티스를 확장할 수 있다. 또는, 원하는 경우 새 컨트롤러를 직접 작성할 수 있다. 소유하고 있는 컨트롤러를 파드 집합으로서 실행하거나, 또는 쿠버네티스 외부에서 실행할 수 있다. 가장 적합한 것은 특정 컨트롤러의 기능에 따라 달라진다.
다음 내용
- 쿠버네티스 컨트롤 플레인에 대해 읽기
- 쿠버네티스 오브젝트의 몇 가지 기본 사항을 알아보자.
- 쿠버네티스 API에 대해 더 배워 보자.
- 만약 자신만의 컨트롤러를 작성하기 원한다면, 쿠버네티스 확장하기의 확장 패턴을 본다.
2.5 - 클라우드 컨트롤러 매니저
Kubernetes v1.11 [beta]
클라우드 인프라스트럭처 기술을 통해 퍼블릭, 프라이빗 그리고 하이브리드 클라우드에서 쿠버네티스를 실행할 수 있다. 쿠버네티스는 컴포넌트간의 긴밀한 결합 없이 자동화된 API 기반의 인프라스트럭처를 신뢰한다.
클라우드 컨트롤러 매니저는 클라우드별 컨트롤 로직을 포함하는 쿠버네티스 컨트롤 플레인 컴포넌트이다. 클라우드 컨트롤러 매니저를 통해 클러스터를 클라우드 공급자의 API에 연결하고, 해당 클라우드 플랫폼과 상호 작용하는 컴포넌트와 클러스터와만 상호 작용하는 컴포넌트를 구분할 수 있게 해 준다.
쿠버네티스와 기본 클라우드 인프라스터럭처 간의 상호 운용성 로직을 분리함으로써, cloud-controller-manager 컴포넌트는 클라우드 공급자가 주요 쿠버네티스 프로젝트와 다른 속도로 기능들을 릴리스할 수 있도록 한다.
클라우드 컨트롤러 매니저는 다양한 클라우드 공급자가 자신의 플랫폼에 쿠버네티스를 통합할 수 있도록 하는 플러그인 메커니즘을 사용해서 구성된다.
디자인
클라우드 컨트롤러 매니저는 컨트롤 플레인에서 복제된 프로세스의 집합으로 실행된다(일반적으로, 파드의 컨테이너). 각 클라우드 컨트롤러 매니저는 단일 프로세스에 여러 컨트롤러를 구현한다.
참고:
또한 사용자는 클라우드 컨트롤러 매니저를 컨트롤 플레인의 일부가 아닌 쿠버네티스 애드온으로 실행할 수도 있다.클라우드 컨트롤러 매니저의 기능
클라우드 컨틀롤러 매니저의 내부 컨트롤러에는 다음 컨트롤러들이 포함된다.
노드 컨트롤러
노드 컨트롤러는 클라우드 인프라스트럭처에 새 서버가 생성될 때 노드 오브젝트를 업데이트하는 역할을 한다. 노드 컨트롤러는 클라우드 공급자의 사용자 테넌시 내에서 실행되는 호스트에 대한 정보를 가져온다. 노드 컨트롤러는 다음 기능들을 수행한다.
- 클라우드 공급자 API를 통해 획득한 해당 서버의 고유 ID를 노드 오브젝트에 업데이트한다.
- 클라우드 관련 정보(예를 들어, 노드가 배포되는 지역과 사용 가능한 리소스(CPU, 메모리 등))를 사용해서 노드 오브젝트에 어노테이션과 레이블을 작성한다.
- 노드의 호스트 이름과 네트워크 주소를 가져온다.
- 노드의 상태를 확인한다. 노드가 응답하지 않는 경우, 이 컨트롤러는 사용자가 이용하는 클라우드 공급자의 API를 통해 서버가 비활성화됨 / 삭제됨 / 종료됨인지 확인한다. 노드가 클라우드에서 삭제된 경우, 컨트롤러는 사용자의 쿠버네티스 클러스터에서 노드 오브젝트를 삭제한다.
일부 클라우드 공급자의 구현에서는 이를 노드 컨트롤러와 별도의 노드 라이프사이클 컨트롤러로 분리한다.
라우트 컨트롤러
라우트 컨트롤러는 사용자의 쿠버네티스 클러스터의 다른 노드에 있는 각각의 컨테이너가 서로 통신할 수 있도록 클라우드에서 라우트를 적절히 구성해야 한다.
클라우드 공급자에 따라 라우트 컨트롤러는 파드 네트워크 IP 주소 블록을 할당할 수도 있다.
서비스 컨트롤러
서비스 는 관리형 로드 밸런서, IP 주소, 네트워크 패킷 필터링 그리고 대상 상태 확인과 같은 클라우드 인프라스트럭처 컴포넌트와 통합된다. 서비스 컨트롤러는 사용자의 클라우드 공급자 API와 상호 작용해서 필요한 서비스 리소스를 선언할 때 로드 밸런서와 기타 인프라스트럭처 컴포넌트를 설정한다.
인가
이 섹션에서는 클라우드 컨트롤러 매니저가 작업을 수행하기 위해 다양한 API 오브젝트에 필요한 접근 권한을 세분화한다.
노드 컨트롤러
노드 컨트롤러는 노드 오브젝트에서만 작동한다. 노드 오브젝트를 읽고, 수정하려면 전체 접근 권한이 필요하다.
v1/Node
:
- Get
- List
- Create
- Update
- Patch
- Watch
- Delete
라우트 컨트롤러
라우트 컨트롤러가 노드 오브젝트의 생성을 수신하고 적절하게 라우트를 구성한다. 노드 오브젝트에 대한 접근 권한이 필요하다.
v1/Node
:
- Get
서비스 컨트롤러
서비스 컨트롤러는 서비스 오브젝트 생성, 업데이트 그리고 삭제 이벤트를 수신한 다음 해당 서비스에 대한 엔드포인트를 적절하게 구성한다(엔드포인트슬라이스(EndpointSlice)의 경우, kube-controller-manager가 필요에 따라 이들을 관리한다).
서비스에 접근하려면, 목록과 감시 접근 권한이 필요하다. 서비스를 업데이트하려면, 패치와 업데이트 접근 권한이 필요하다.
서비스에 대한 엔드포인트 리소스를 설정하려면 생성, 목록, 가져오기, 감시 그리고 업데이트에 대한 접근 권한이 필요하다.
v1/Service
:
- List
- Get
- Watch
- Patch
- Update
그 외의 것들
클라우드 컨트롤러 매니저의 핵심 구현을 위해 이벤트 오브젝트를 생성하고, 안전한 작동을 보장하기 위해 서비스어카운트(ServiceAccounts)를 생성해야 한다.
v1/Event
:
- Create
- Patch
- Update
v1/ServiceAccount
:
- Create
클라우드 컨트롤러 매니저의 RBAC 클러스터롤(ClusterRole)은 다음과 같다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cloud-controller-manager
rules:
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- update
- apiGroups:
- ""
resources:
- nodes
verbs:
- '*'
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
- apiGroups:
- ""
resources:
- services
verbs:
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- create
- apiGroups:
- ""
resources:
- persistentvolumes
verbs:
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
- endpoints
verbs:
- create
- get
- list
- watch
- update
다음 내용
클라우드 컨트롤러 매니저 관리에는 클라우드 컨트롤러 매니저의 실행과 관리에 대한 지침이 있다.
클라우드 컨트롤러 매니저를 사용하기 위해 HA 컨트롤 플레인을 업그레이드하려면, 클라우드 컨트롤러 매니저를 사용하기 위해 복제된 컨트롤 플레인 마이그레이션 하기를 참고한다.
자체 클라우드 컨트롤러 매니저를 구현하거나 기존 프로젝트를 확장하는 방법을 알고 싶은가?
클라우드 컨트롤러 매니저는 Go 인터페이스를 사용함으로써, 어떠한 클라우드에 대한 구현체(implementation)라도 플러그인 될 수 있도록 한다. 구체적으로는, kubernetes/cloud-provider의 cloud.go
에 정의된 CloudProvider
인터페이스를 사용한다.
이 문서(노드, 라우트와 서비스)에서 강조된 공유 컨트롤러의 구현과 공유 cloudprovider 인터페이스와 함께 일부 스캐폴딩(scaffolding)은 쿠버네티스 핵심의 일부이다. 클라우드 공급자 전용 구현은 쿠버네티스의 핵심 바깥에 있으며 CloudProvider
인터페이스를 구현한다.
플러그인 개발에 대한 자세한 내용은 클라우드 컨트롤러 매니저 개발하기를 참조한다.
2.6 - 컨테이너 런타임 인터페이스(CRI)
컨테이너 런타임 인터페이스(CRI)는 클러스터 컴포넌트를 다시 컴파일하지 않아도 Kubelet이 다양한 컨테이너 런타임을 사용할 수 있도록 하는 플러그인 인터페이스다.
클러스터의 모든 노드에 동작 중인 컨테이너 런타임이 존재해야, kubelet이 파드들과 컨테이너들을 구동할 수 있다.
컨테이너 런타임 인터페이스(CRI)는 kubelet과 컨테이너 런타임 사이의 통신을 위한 주요 프로토콜이다.
쿠버네티스 컨테이너 런타임 인터페이스(CRI)는 클러스터 컴포넌트 kubelet과 container runtime 사이의 통신을 위한 주요 gRPC 프로토콜을 정의한다.
API
Kubernetes v1.23 [stable]
Kubelet은 gRPC를 통해 컨테이너 런타임과 연결할 때 클라이언트의 역할을 수행한다.
런타임과 이미지 서비스 엔드포인트는 컨테이너 런타임 내에서 사용 가능해야 하며,
이는 각각 Kubelet 내에서 --image-service-endpoint
와 --container-runtime-endpoint
커맨드라인 플래그
를 통해 설정할 수 있다.
쿠버네티스 v1.31에서는, Kubelet은 CRI v1
을 사용하는 것을 권장한다.
컨테이너 런타임이 CRI v1
버전을 지원하지 않는다면,
Kubelet은 지원 가능한 이전 지원 버전으로 협상을 시도한다.
또한 v1.31 Kubelet은 CRI v1alpha2
버전도 협상할 수 있지만,
해당 버전은 사용 중단(deprecated)으로 간주한다.
Kubelet이 지원되는 CRI 버전을 협상할 수 없는 경우,
Kubelet은 협상을 포기하고 노드로 등록하지 않는다.
업그레이드
쿠버네티스를 업그레이드할 때, Kubelet은 컴포넌트의 재시작 시점에서 최신 CRI 버전을 자동으로 선택하려고 시도한다. 이 과정이 실패하면 위에서 언급한 대로 이전 버전을 선택하는 과정을 거친다. 컨테이너 런타임이 업그레이드되어 gRPC 재다이얼이 필요하다면, 컨테이너 런타임도 처음에 선택된 버전을 지원해야 하며, 그렇지 못한 경우 재다이얼은 실패하게 될 것이다. 이 과정은 Kubelet의 재시작이 필요하다.
다음 내용
- CRI 프로토콜 정의를 자세히 알아보자.
2.7 - 가비지(Garbage) 수집
쿠버네티스가 클러스터 자원을 정리하기 위해 사용하는 다양한 방법을 종합한 용어이다. 다음과 같은 리소스를 정리한다:
- 종료된 잡
- 소유자 참조가 없는 오브젝트
- 사용되지 않는 컨테이너와 컨테이너 이미지
- 반환 정책이 삭제인 스토리지클래스에 의해 동적으로 생성된 퍼시스턴트볼륨
- Stale 또는 만료된 CertificateSigningRequests (CSRs)
- 노드 는 다음과 같은 상황에서 삭제된다:
- 클러스터가 클라우드 컨트롤러 매니저를 사용하는 클라우드
- 클러스터가 클라우드 컨트롤러 매니저와 유사한 애드온을 사용하는 온프레미스
- 노드 리스(Lease) 오브젝트
소유자(Owners)와 종속(dependents)
쿠버네티스의 많은 오브젝트는 owner references를 통해 서로 연결되어 있다.
소유자 참조(Owner references)는 컨트롤 플레인에게 어떤 오브젝트가 서로 종속적인지를 알려준다. 쿠버네티스는 소유자 참조를 사용하여 컨트롤 플레인과 다른 API 클라이언트에게 오브젝트를 삭제하기 전 관련 리소스를 정리하는 기회를 제공한다. 대부분의 경우, 쿠버네티스는 소유자 참조를 자동으로 관리한다.
소유권(Ownership)은 일부 리소스가 사용하는 레이블과 셀렉터
메커니즘과는 다르다. 예를 들어,
EndpointSlice
오브젝트를 생성하는 서비스를
생각해보자. 서비스는 레이블을 사용해 컨트롤 플레인이
어떤 EndpointSlice
오브젝트가 해당 서비스에 의해 사용되는지 판단하는 데 도움을 준다. 레이블과 더불어,
서비스를 대신해 관리되는 각 EndpointSlice
오브젝트는
소유자 참조를 가진다. 소유자 참조는 쿠버네티스의 다른 부분이 제어하지 않는
오브젝트를 방해하는 것을 방지하는 데 도움을 준다.
참고:
교차 네임스페이스(cross-namespace)의 소유자 참조는 디자인상 허용되지 않는다. 네임스페이스 종속 오브젝트는 클러스터 범위 또는 네임스페이스 소유자를 지정할 수 있다. 네임스페이스 소유자는 반드시 종속 오브젝트와 동일한 네임스페이스에 존재해야 한다. 그렇지 않다면, 소유자 참조는 없는 것으로 간주되어, 종속 오브젝트는 모든 소유자가 없는 것으로 확인되면 삭제될 수 있다.
클러스터 범위의 종속 오브젝트는 클러스터 범위의 소유자만 지정할 수 있다. v1.20 이상에서, 클러스터 범위의 종속 오브젝트가 네임스페이스 종류를 소유자로 지정하면, 확인할 수 없는 소유자 참조가 있는 것으로 간주되어 가비지 수집이 될 수 없다.
v1.20 이상에서, 가비지 수집기가 잘못된 교차 네임스페이스 ownerReference
또는 네임스페이스 종류를 참조하는 ownerReference
가 있는 클러스터 범위의 종속 항목을 감지하면,
OwnerRefInvalidNamespace
가 원인인 경고 이벤트와 유효하지 않은 종속 항목의 involvedObject
가 보고된다.
kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace
를 실행하여 이러한 종류의 이벤트를 확인할 수 있다.
캐스케이딩(Cascading) 삭제
쿠버네티스는 오브젝트를 삭제할 때 더 이상 소유자 참조가 없는지,
예를 들어 레플리카셋을 삭제할 때, 남겨진 파드가 없는지 확인하고 삭제한다.
오브젝트를 삭제할 때 쿠버네티스가 오브젝트의 종속 오브젝트들을 자동으로 삭제할 지 여부를 제어할 수 있다.
이 과정을 캐스케이딩 삭제
라고 한다.
캐스케이딩 삭제에는 다음과 같은 두 가지 종류가 있다.
- 포그라운드 캐스케이딩 삭제(Foreground cascading deletion)
- 백그라운드 캐스케이딩 삭제(Background cascading deletion)
또한 쿠버네티스의 finalizers를 사용하여 가비지 수집이 소유자 참조가 있는 자원을 언제 어떻게 삭제할 것인지 제어할 수 있다.
포그라운드 캐스케이딩 삭제
포그라운드 캐스케이딩 삭제에서는 삭제하려는 소유자 오브젝트가 먼저 삭제 중 상태가 된다. 이 상태에서는 소유자 오브젝트에게 다음과 같은 일이 일어난다:
- 쿠버네티스 API 서버가 오브젝트의
metadata.deletionTimestamp
필드를 오브젝트가 삭제 표시된 시간으로 설정한다. - 쿠버네티스 API 서버가
metadata.finalizers
필드를foregroundDeletion
로 설정한다. - 오브젝트는 삭제 과정이 완료되기 전까지 쿠버네티스 API를 통해 조회할 수 있다.
소유자 오브젝트가 삭제 중 상태가 된 이후, 컨트롤러는 종속 오브젝트들을 삭제한다. 모든 종속 오브젝트들이 삭제되고나면, 컨트롤러가 소유자 오브젝트를 삭제한다. 이 시점에서 오브젝트는 더 이상 쿠버네티스 API를 통해 조회할 수 없다.
포그라운드 캐스케이딩 삭제 중에 소유자 오브젝트의 삭제를 막는
종속 오브젝트는ownerReference.blockOwnerDeletion=true
필드를 가진 오브젝트다.
더 자세한 내용은 Use foreground cascading deletion를
참고한다.
백그라운드 캐스케이딩 삭제
백그라운드 캐스케이딩 삭제에서는 쿠버네티스 API 서버가 소유자 오브젝트를 즉시 삭제하고 백그라운드에서 컨트롤러가 종속 오브젝트들을 삭제한다. 쿠버네티스는 수동으로 포그라운드 삭제를 사용하거나 종속 오브젝트를 분리하지 않는다면, 기본적으로 백그라운드 캐스케이딩 삭제를 사용한다.
더 자세한 내용은 Use background cascading deletion를 참고한다.
분리된 종속 (Orphaned dependents)
쿠버네티스가 소유자 오브젝트를 삭제할 때, 남은 종속 오브젝트는 분리된 오브젝트라고 부른다. 기본적으로 쿠버네티스는 종속 오브젝트를 삭제한다. 이 행동을 오버라이드하는 방법을 보려면, Delete owner objects and orphan dependents를 참고한다.
사용되지 않는 컨테이너와 이미지 가비지 수집
kubelet은 사용되지 않는 이미지에 대한 가비지 수집을 5분마다, 컨테이너에 대한 가비지 수집을 1분마다 수행한다. 외부 가비지 수집 도구는 kubelet 의 행동을 중단시키고 존재해야만 하는 컨테이너를 삭제할 수 있으므로 사용을 피해야 한다.
사용되지 않는 컨테이너와 이미지에 대한 가비지 수집 옵션을 구성하려면,
configuration file 사용하여
kubelet 을 수정하거나
KubeletConfiguration
리소스 타입의
가비지 수집과 관련된 파라미터를 수정한다.
컨테이너 이미지 라이프사이클
쿠버네티스는 kubelet의 일부인 이미지 관리자가 cadvisor와 협동하여 모든 이미지의 라이프사이클을 관리한다. kubelet은 가비지 수집 결정을 내릴 때, 다음 디스크 사용량 제한을 고려한다.
HighThresholdPercent
LowThresholdPercent
HighThresholdPercent
값을 초과한 디스크 사용량은
마지막으로 사용된 시간을 기준으로 오래된 이미지순서대로 이미지를 삭제하는
가비지 수집을 트리거한다. kubelet은 디스크 사용량이 LowThresholdPercent
값에 도달할 때까지
이미지를 삭제한다.
컨테이너 이미지 가비지 수집
kubelet은 사용자가 정의할 수 있는 다음 변수들을 기반으로 사용되지 않는 컨테이너들을 삭제한다:
MinAge
: kubelet이 가비지 수집할 수 있는 최소 나이.0
으로 세팅하여 비활성화할 수 있다.MaxPerPodContainer
: 각 파드 쌍이 가질 수 있는 죽은 컨테이너의 최대 개수.0
으로 세팅하여 비활성화할 수 있다.MaxContainers
: 클러스터가 가질 수 있는 죽은 컨테이너의 최대 개수0
으로 세팅하여 비활성화할 수 있다.
위 변수와 더불어, kubelet은 식별할 수 없고 삭제된 컨테이너들을 오래된 순서대로 가비지 수집한다.
MaxPerPodContainer
와 MaxContainer
는
파드의 최대 컨테이너 개수(MaxPerPodContainer
)를 유지하는 것이
전체 죽은 컨테이너의 개수 제한(MaxContainers
)을 초과하게 될 때,
서로 충돌이 발생할 수 있다.
이 상황에서 kubelet은 충돌을 해결하기 위해 MaxPodPerContainer
를 조절한다.
최악의 시나리오에서는 MaxPerPodContainer
를 1
로 다운그레이드하고
가장 오래된 컨테이너들을 축출한다.
또한, 삭제된 파드가 소유한 컨테이너들은 MinAge
보다 오래되었을 때 삭제된다.
참고:
kubelet은 자신이 관리하는 컨테이너에 대한 가비지 수집만을 수행한다.가비지 수집 구성하기
자원을 관리하는 컨트롤러의 옵션을 구성하여 가비지 컬렉션을 수정할 수 있다. 다음 페이지에서 어떻게 가비지 수집을 구성할 수 있는지 확인할 수 있다.
다음 내용
- 쿠버네티스 오브젝트의 소유권에 대해 알아보자.
- 쿠버네티스 finalizers에 대해 알아보자.
- 완료된 잡을 정리하는 TTL 컨트롤러 (beta) 에 대해 알아보자.
3 - 컨테이너
실행하는 각 컨테이너는 반복 가능하다. 의존성이 포함된 표준화는 어디에서 실행하던지 동일한 동작을 얻는다는 것을 의미한다.
컨테이너는 기본 호스트 인프라에서 애플리케이션을 분리한다. 따라서 다양한 클라우드 또는 OS 환경에서 보다 쉽게 배포할 수 있다.
쿠버네티스 클러스터에 있는 개별 node는 해당 노드에 할당된 파드를 구성하는 컨테이너들을 실행한다. 파드 내부에 컨테이너들은 같은 노드에서 실행될 수 있도록 같은 곳에 위치하고 함께 스케줄된다.
컨테이너 이미지
컨테이너 이미지는 애플리케이션을 실행하는 데 필요한 모든 것이 포함된 실행할 준비가 되어 있는(ready-to-run) 소프트웨어 패키지이다. 여기에는 실행하는 데 필요한 코드와 모든 런타임, 애플리케이션 및 시스템 라이브러리, 그리고 모든 필수 설정에 대한 기본값이 포함된다.
컨테이너는 스테이트리스하며 불변(immutable)일 것으로 계획되었다. 즉, 이미 실행 중인 컨테이너의 코드를 수정해서는 안된다. 만일 컨테이너화된 애플리케이션에 수정을 가하고 싶다면, 수정 내역을 포함한 새로운 이미지를 빌드하고, 업데이트된 이미지로부터 컨테이너를 새로 생성하는 것이 올바른 방법이다.
컨테이너 런타임
컨테이너 런타임은 컨테이너 실행을 담당하는 소프트웨어이다.
쿠버네티스는 containerd, CRI-O와 같은 컨테이너 런타임 및 모든 Kubernetes CRI (컨테이너 런타임 인터페이스) 구현체를 지원한다.
일반적으로 클러스터에서 파드의 기본 컨테이너 런타임을 선택하도록 허용할 수 있다. 만일 클러스터에서 복수의 컨테이너 런타임을 사용해야 하는 경우, 파드에 대해 런타임클래스(RuntimeClass)을 명시함으로써 쿠버네티스가 특정 컨테이너 런타임으로 해당 컨테이너들을 실행하도록 설정할 수 있다.
또한 런타임클래스을 사용하면 하나의 컨테이너 런타임을 사용하여 복수의 파드들을 각자 다른 설정으로 실행할 수도 있다.
3.1 - 이미지
컨테이너 이미지는 애플리케이션과 모든 소프트웨어 의존성을 캡슐화하는 바이너리 데이터를 나타낸다. 컨테이너 이미지는 독립적으로 실행할 수 있고 런타임 환경에 대해 잘 정의된 가정을 만드는 실행 가능한 소프트웨어 번들이다.
일반적으로 파드에서 참조하기 전에 애플리케이션의 컨테이너 이미지를 생성해서 레지스트리로 푸시한다.
이 페이지는 컨테이너 이미지 개념의 개요를 제공한다.
참고:
(v1.31와 같은 최신 마이너 릴리즈와 같은) 쿠버네티스 릴리즈에 대한 컨테이너 이미지를 찾고 있다면 쿠버네티스 다운로드를 확인하라.이미지 이름
컨테이너 이미지는 일반적으로 pause
, example/mycontainer
또는 kube-apiserver
와 같은 이름을 부여한다.
이미지는 또한 레지스트리 호스트 이름을 포함할 수 있다. 예를 들어 fictional.registry.example/imagename
와 같다. 그리고 fictional.registry.example:10443/imagename
와 같이 포트 번호도 포함할 수 있다.
레지스트리 호스트 이름을 지정하지 않으면, 쿠버네티스는 도커 퍼블릭 레지스트리를 의미한다고 가정한다.
이미지 이름 부분 다음에 tag 를 추가할 수 있다(docker
또는 podman
과 같은 명령을 사용할 때와 동일한 방식으로).
태그를 사용하면 동일한 시리즈 이미지의 다른 버전을 식별할 수 있다.
이미지 태그는 소문자와 대문자, 숫자, 밑줄(_
),
마침표(.
) 및 대시(-
)로 구성된다.
이미지 태그 안에서 구분 문자(_
, -
그리고 .
)를
배치할 수 있는 위치에 대한 추가 규칙이 있다.
태그를 지정하지 않으면, 쿠버네티스는 태그 latest
를 의미한다고 가정한다.
이미지 업데이트
디플로이먼트,
스테이트풀셋, 파드 또는 파드
템플릿은 포함하는 다른 오브젝트를 처음 만들 때 특별히 명시하지 않은 경우
기본적으로 해당 파드에 있는 모든 컨테이너의 풀(pull)
정책은 IfNotPresent
로 설정된다. 이 정책은
kubelet이 이미 존재하는
이미지에 대한 풀을 생략하게 한다.
이미지 풀(pull) 정책
컨테이너에 대한 imagePullPolicy
와 이미지의 태그는
kubelet이 특정 이미지를 풀(다운로드)하려고 할 때 영향을 준다.
다음은 imagePullPolicy
에 설정할 수 있는 값의 목록과
효과이다.
IfNotPresent
- 이미지가 로컬에 없는 경우에만 내려받는다.
Always
- kubelet이 컨테이너를 기동할 때마다, kubelet이 컨테이너 이미지 레지스트리에 이름과 이미지의 다이제스트가 있는지 질의한다. 일치하는 다이제스트를 가진 컨테이너 이미지가 로컬에 있는 경우, kubelet은 캐시된 이미지를 사용한다. 이외의 경우, kubelet은 검색된 다이제스트를 가진 이미지를 내려받아서 컨테이너를 기동할 때 사용한다.
Never
- kubelet은 이미지를 가져오려고 시도하지 않는다. 이미지가 어쨌든 이미 로컬에 존재하는 경우, kubelet은 컨테이너 기동을 시도한다. 이외의 경우 기동은 실패한다. 보다 자세한 내용은 미리 내려받은 이미지를 참조한다.
이미지 제공자에 앞서 깔린 캐시의 의미 체계는 레지스트리에 안정적으로 접근할 수 있는 한,
imagePullPolicy: Always
인 경우 조차도 효율적이다.
컨테이너 런타임은 노드에 이미 존재하는 이미지 레이어를 알고
다시 내려받지 않는다.
참고:
프로덕션 환경에서 컨테이너를 배포하는 경우 :latest
태그 사용을 지양해야 하는데,
이미지의 어떤 버전이 기동되고 있는지 추적이 어렵고
제대로 롤백하기 어렵게 되기 때문이다.
대신, v1.42.0
과 같이 의미있는 태그를 명기한다.
파드가 항상 컨테이너 이미지의 같은 버전을 사용하는 것을 확실히 하려면,
이미지의 다이제스트를 명기할 수 있다.
<image-name>:<tag>
를 <image-name>@<digest>
로 교체한다.
(예를 들어, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
).
이미지 태그를 사용하는 경우, 이미지 레지스트리에서 한 이미지를 나타내는 태그에 코드를 변경하게 되면, 기존 코드와 신규 코드를 구동하는 파드가 섞이게 되고 만다. 이미지 다이제스트를 통해 이미지의 특정 버전을 유일하게 식별할 수 있기 때문에, 쿠버네티스는 매번 해당 이미지 이름과 다이제스트가 명시된 컨테이너를 기동해서 같은 코드를 구동한다. 이미지를 다이제스트로 명시하면 구동할 코드를 고정시켜서 레지스트리에서의 변경으로 인해 버전이 섞이는 일이 발생하지 않도록 해 준다.
파드(및 파드 템플릿)가 생성될 때 구동 중인 워크로드가 태그가 아닌 이미지 다이제스트를 통해 정의되도록 조작해주는 서드-파티 어드미션 컨트롤러가 있다. 이는 레지스트리에서 태그가 변경되는 일이 발생해도 구동 중인 워크로드가 모두 같은 코드를 사용하고 있다는 것을 보장하기를 원하는 경우 유용할 것이다.
기본 이미지 풀 정책
사용자(또는 컨트롤러)가 신규 파드를 API 서버에 요청할 때,
특정 조건에 부합하면 클러스터가 imagePullPolicy
필드를 설정한다.
imagePullPolicy
필드를 생략하고 컨테이너 이미지의 태그가:latest
인 경우,imagePullPolicy
는 자동으로Always
로 설정된다.imagePullPolicy
필드를 생략하고 컨테이너 이미지의 태그를 명기하지 않은 경우,imagePullPolicy
는 자동으로Always
로 설정된다.imagePullPolicy
필드를 생략하고, 명기한 컨테이너 이미지의 태그가:latest
가 아니면,imagePullPolicy
는 자동으로IfNotPresent
로 설정된다.
참고:
컨테이너의 imagePullPolicy
값은 오브젝트가 처음 created 일 때 항상
설정되고 나중에 이미지 태그가 변경되더라도 업데이트되지 않는다.
예를 들어, 태그가 :latest
가 아닌 이미지로 디플로이먼트를 생성하고,
나중에 해당 디플로이먼트의 이미지를 :latest
태그로 업데이트하면
imagePullPolicy
필드가 Always
로 변경되지 않는다. 오브젝트를
처음 생성 한 후 모든 오브젝트의 풀 정책을 수동으로 변경해야 한다.
이미지 풀 강제
이미지를 내려받도록 강제하려면, 다음 중 한가지 방법을 사용한다.
- 컨테이너의
imagePullPolicy
를Always
로 설정한다. 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)은 여러 가지 방법으로 제공될 수 있다.
- 프라이빗 레지스트리에 대한 인증을 위한 노드 구성
- 모든 파드는 구성된 프라이빗 레지스트리를 읽을 수 있음
- 클러스터 관리자에 의한 노드 구성 필요
- Kubelet 자격증명 제공자(Credential Provider)를 통해 프라이빗 레지스트리로부터 동적으로 자격증명을 가져오기
- kubelet은 특정 프라이빗 레지스트리에 대해 자격증명 제공자 실행 플러그인(credential provider exec plugin)을 사용하도록 설정될 수 있다.
- 미리 내려받은(pre-pulled) 이미지
- 모든 파드는 노드에 캐시된 모든 이미지를 사용 가능
- 셋업을 위해서는 모든 노드에 대해서 root 접근이 필요
- 파드에 ImagePullSecrets을 명시
- 자신의 키를 제공하는 파드만 프라이빗 레지스트리에 접근 가능
- 공급 업체별 또는 로컬 확장
- 사용자 정의 노드 구성을 사용하는 경우, 사용자(또는 클라우드 제공자)가 컨테이너 레지스트리에 대한 노드 인증 메커니즘을 구현할 수 있다.
이들 옵션은 아래에서 더 자세히 설명한다.
프라이빗 레지스트리에 인증하도록 노드 구성
크리덴셜 설정에 대한 상세 지침은 사용하는 컨테이너 런타임 및 레지스트리에 따라 다르다. 가장 정확한 정보는 솔루션 설명서를 참조해야 한다.
프라이빗 컨테이너 이미지 레지스트리 구성 예시를 보려면, 프라이빗 레지스트리에서 이미지 가져오기를 참조한다. 해당 예시는 도커 허브에서 제공하는 프라이빗 레지스트리를 사용한다.
인증된 이미지를 가져오기 위한 Kubelet 자격증명 제공자(Credential Provider)
참고:
해당 방식은 kubelet이 자격증명을 동적으로 가져와야 할 때 특히 적절하다. 가장 일반적으로 클라우드 제공자로부터 제공받은 레지스트리에 대해 사용된다. 인증 토큰의 수명이 짧기 때문이다.kubelet에서 플러그인 바이너리를 호출하여 컨테이너 이미지에 대한 레지스트리 자격증명을 동적으로 가져오도록 설정할 수 있다. 이는 프라이빗 레지스트리에서 자격증명을 가져오는 방법 중 가장 강력하며 휘발성이 있는 방식이지만, 활성화하기 위해 kubelet 수준의 구성이 필요하다.
자세한 내용은 kubelet 이미지 자격증명 제공자 설정하기를 참고한다.
config.json 파일 해석
config.json
파일의 해석에 있어서, 기존 도커의 구현과 쿠버네티스의 구현에 차이가 있다.
도커에서는 auths
키에 특정 루트 URL만 기재할 수 있으나,
쿠버네티스에서는 glob URL과 접두사-매칭 경로도 기재할 수 있다.
이는 곧 다음과 같은 config.json
도 유효하다는 뜻이다.
{
"auths": {
"*my-registry.io/images": {
"auth": "…"
}
}
}
루트 URL(*my-registry.io
)은 다음 문법을 사용하여 매치된다.
pattern:
{ term }
term:
'*' 구분자가 아닌 모든 문자와 매치됨
'?' 구분자가 아닌 문자 1개와 매치됨
'[' [ '^' ] { character-range } ']'
문자 클래스 (비어 있으면 안 됨))
c 문자 c에 매치됨 (c != '*', '?', '\\', '[')
'\\' c 문자 c에 매치됨
character-range:
c 문자 c에 매치됨 (c != '\\', '-', ']')
'\\' c 문자 c에 매치됨
lo '-' hi lo <= c <= hi 인 문자 c에 매치됨
이미지 풀 작업 시, 모든 유효한 패턴에 대해 크리덴셜을 CRI 컨테이너 런타임에 제공할 것이다. 예를 들어 다음과 같은 컨테이너 이미지 이름은 성공적으로 매치될 것이다.
my-registry.io/images
my-registry.io/images/my-image
my-registry.io/images/another-image
sub.my-registry.io/images/my-image
a.sub.my-registry.io/images/my-image
kubelet은 인식된 모든 크리덴셜을 순차적으로 이용하여 이미지 풀을 수행한다. 즉,
config.json
에 다음과 같이 여러 항목을 기재할 수도 있다.
{
"auths": {
"my-registry.io/images": {
"auth": "…"
},
"my-registry.io/images/subpath": {
"auth": "…"
}
}
}
이제 컨테이너가 my-registry.io/images/subpath/my-image
이미지를 풀 해야 한다고 명시하면,
kubelet은 크리덴셜을 순차적으로 사용하여 풀을 시도한다.
미리 내려받은 이미지
참고:
이 방법은 노드의 구성을 제어할 수 있는 경우에만 적합하다. 이 방법은 클라우드 제공자가 노드를 관리하고 자동으로 교체한다면 안정적으로 작동하지 않을 것이다.기본적으로, kubelet은 지정된 레지스트리에서 각 이미지를 풀 하려고 한다.
그러나, 컨테이너의 imagePullPolicy
속성이 IfNotPresent
또는 Never
으로 설정되어 있다면,
로컬 이미지가 사용된다(우선적으로 또는 배타적으로).
레지스트리 인증의 대안으로 미리 풀 된 이미지에 의존하고 싶다면, 클러스터의 모든 노드가 동일한 미리 내려받은 이미지를 가지고 있는지 확인해야 한다.
이것은 특정 이미지를 속도를 위해 미리 로드하거나 프라이빗 레지스트리에 대한 인증의 대안으로 사용될 수 있다.
모든 파드는 미리 내려받은 이미지에 대해 읽기 접근 권한을 가질 것이다.
파드에 ImagePullSecrets 명시
참고:
이 방법은 프라이빗 레지스트리의 이미지를 기반으로 컨테이너를 실행하는 데 권장된다.쿠버네티스는 파드에 컨테이너 이미지 레지스트리 키를 명시하는 것을 지원한다.
imagePullSecrets
은 모두 파드와 동일한 네임스페이스에 있어야 한다.
참조되는 시크릿의 타입은 kubernetes.io/dockercfg
이거나 kubernetes.io/dockerconfigjson
이어야 한다.
도커 구성으로 시크릿 생성
레지스트리에 인증하기 위해서는, 레지스트리 호스트네임 뿐만 아니라, 사용자 이름, 비밀번호 및 클라이언트 이메일 주소를 알아야 한다. 대문자 값을 적절히 대체하여, 다음 커맨드를 실행한다.
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
와 함께 사용할 수 있다. 자격 증명은
병합될 것이다.
유스케이스
프라이빗 레지스트리를 구성하기 위한 많은 솔루션이 있다. 다음은 여러 가지 일반적인 유스케이스와 제안된 솔루션이다.
- 비소유 이미지(예를 들어, 오픈소스)만 실행하는 클러스터의 경우. 이미지를 숨길 필요가 없다.
- 퍼블릭 레지스트리의 퍼블릭 이미지를 사용한다.
- 설정이 필요 없다.
- 일부 클라우드 제공자는 퍼블릭 이미지를 자동으로 캐시하거나 미러링하므로, 가용성이 향상되고 이미지를 가져오는 시간이 줄어든다.
- 퍼블릭 레지스트리의 퍼블릭 이미지를 사용한다.
- 모든 클러스터 사용자에게는 보이지만, 회사 외부에는 숨겨야하는 일부 독점 이미지를
실행하는 클러스터의 경우.
- 호스트된 프라이빗 레지스트리를 사용한다.
- 프라이빗 레지스트리에 접근해야 하는 노드에 수동 설정이 필요할 수 있다
- 또는, 방화벽 뒤에서 읽기 접근 권한을 가진 내부 프라이빗 레지스트리를 실행한다.
- 쿠버네티스 구성은 필요하지 않다.
- 이미지 접근을 제어하는 호스팅된 컨테이너 이미지 레지스트리 서비스를 사용한다.
- 그것은 수동 노드 구성에 비해서 클러스터 오토스케일링과 더 잘 동작할 것이다.
- 또는, 노드의 구성 변경이 불편한 클러스터에서는,
imagePullSecrets
를 사용한다.
- 호스트된 프라이빗 레지스트리를 사용한다.
- 독점 이미지를 가진 클러스터로, 그 중 일부가 더 엄격한 접근 제어를 필요로 하는 경우.
- AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
- 민감한 데이터는 이미지 안에 포장하는 대신, "시크릿" 리소스로 이동한다.
- 멀티-테넌트 클러스터에서 각 테넌트가 자신의 프라이빗 레지스트리를 필요로 하는 경우.
- AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
- 인가가 요구되도록 프라이빗 레지스트리를 실행한다.
- 각 테넌트에 대한 레지스트리 자격 증명을 생성하고, 시크릿에 넣고, 각 테넌트 네임스페이스에 시크릿을 채운다.
- 테넌트는 해당 시크릿을 각 네임스페이스의 imagePullSecrets에 추가한다.
다중 레지스트리에 접근해야 하는 경우, 각 레지스트리에 대해 하나의 시크릿을 생성할 수 있다.
다음 내용
3.2 - 컨테이너 환경 변수
이 페이지는 컨테이너 환경에서 컨테이너에 가용한 리소스에 대해 설명한다.
컨테이너 환경
쿠버네티스 컨테이너 환경은 컨테이너에 몇 가지 중요한 리소스를 제공한다.
컨테이너 정보
컨테이너의 호스트네임 은 컨테이너가 동작 중인 파드의 이름과 같다.
그것은 hostname
커맨드 또는 libc의
gethostname
함수 호출을 통해서 구할 수 있다.
파드 이름과 네임스페이스는 다운워드(Downward) API를 통해 환경 변수로 구할 수 있다.
컨테이너 이미지에 정적으로 명시된 환경 변수와 마찬가지로, 파드 정의에서의 사용자 정의 환경 변수도 컨테이너가 사용할 수 있다.
클러스터 정보
컨테이너가 생성될 때 실행 중이던 모든 서비스의 목록은 환경 변수로 해당 컨테이너에서 사용할 수 있다. 이 목록은 새로운 컨테이너의 파드 및 쿠버네티스 컨트롤 플레인 서비스와 동일한 네임스페이스 내에 있는 서비스로 한정된다.
bar 라는 이름의 컨테이너에 매핑되는 foo 라는 이름의 서비스에 대해서는, 다음의 형태로 변수가 정의된다.
FOO_SERVICE_HOST=<서비스가 동작 중인 호스트>
FOO_SERVICE_PORT=<서비스가 동작 중인 포트>
서비스에 지정된 IP 주소가 있고 DNS 애드온이 활성화된 경우, DNS를 통해서 컨테이너가 서비스를 사용할 수 있다.
다음 내용
- 컨테이너 라이프사이클 훅(hooks)에 대해 더 배워 보기.
- 컨테이너 라이프사이클 이벤트에 핸들러 부착 실제 경험 얻기.
3.3 - 런타임클래스(RuntimeClass)
Kubernetes v1.20 [stable]
이 페이지는 런타임클래스 리소스와 런타임 선택 메커니즘에 대해서 설명한다.
런타임클래스는 컨테이너 런타임을 구성을 선택하는 기능이다. 컨테이너 런타임 구성은 파드의 컨테이너를 실행하는 데 사용된다.
동기
서로 다른 파드간에 런타임클래스를 설정하여 성능과 보안의 균형을 유지할 수 있다. 예를 들어, 일부 작업에서 높은 수준의 정보 보안 보증이 요구되는 경우, 하드웨어 가상화를 이용하는 컨테이너 런타임으로 파드를 실행하도록 예약하는 선택을 할 수 있다. 그러면 몇가지 추가적인 오버헤드는 있지만 대체 런타임을 추가 분리하는 유익이 있다.
또한 런타임클래스를 사용하여 컨테이너 런타임이 같으나 설정이 다른 여러 파드를 실행할 수 있다.
셋업
- CRI 구현(implementation)을 노드에 설정(런타임에 따라서).
- 상응하는 런타임클래스 리소스 생성.
1. CRI 구현을 노드에 설정
런타임클래스를 통한 가능한 구성은 컨테이너 런타임 인터페이스(CRI) 구현에 의존적이다. 사용자의 CRI 구현에 따른 설정 방법은 연관된 문서를 통해서 확인한다(아래).
참고:
런타임클래스는 기본적으로 클러스터 전체에 걸쳐 동질의 노드 설정 (모든 노드가 컨테이너 런타임에 준하는 동일한 방식으로 설정되었음을 의미)을 가정한다. 이종의(heterogeneous) 노드 설정을 지원하기 위해서는, 아래 스케줄을 참고한다.해당 설정은 상응하는 handler
이름을 가지며, 이는 런타임클래스에 의해서 참조된다.
런타임 핸들러는 유효한 DNS 1123 서브도메인(알파-숫자 + -
와 .
문자)을 가져야 한다.
2. 상응하는 런타임클래스 리소스 생성
1단계에서 셋업 한 설정은 연관된 handler
이름을 가져야 하며, 이를 통해서 설정을 식별할 수 있다.
각 런타임 핸들러(그리고 선택적으로 비어있는 ""
핸들러)에 대해서, 상응하는 런타임클래스 오브젝트를 생성한다.
현재 런타임클래스 리소스는 런타임클래스 이름(metadata.name
)과 런타임 핸들러
(handler
)로 단 2개의 중요 필드만 가지고 있다. 오브젝트 정의는 다음과 같은 형태이다.
# 런타임클래스는 node.k8s.io API 그룹에 정의되어 있음
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
# 런타임클래스 참조에 사용될 이름
# 런타임클래스는 네임스페이스가 없는 리소스임
name: myclass
# 상응하는 CRI 설정의 이름
handler: myconfiguration
런타임클래스 오브젝트의 이름은 유효한 DNS 레이블 이름어이야 한다.
참고:
런타임클래스 쓰기 작업(create/update/patch/delete)은 클러스터 관리자로 제한할 것을 권장한다. 이것은 일반적으로 기본 설정이다. 더 자세한 정보는 권한 개요를 참고한다.사용
클러스터에 런타임클래스를 설정하고 나면,
다음과 같이 파드 스펙에 runtimeClassName
를 명시하여 해당 런타임클래스를 사용할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
runtimeClassName: myclass
# ...
이것은 kubelet이 지명된 런타임클래스를 사용하여 해당 파드를 실행하도록 지시할 것이다.
만약 지명된 런타임클래스가 없거나, CRI가 상응하는 핸들러를 실행할 수 없는 경우, 파드는
Failed
터미널 단계로 들어간다.
에러 메시지에 상응하는 이벤트를
확인한다.
만약 명시된 runtimeClassName
가 없다면, 기본 런타임 핸들러가 사용되며,
런타임클래스 기능이 비활성화되었을 때와 동일하게 동작한다.
CRI 구성
CRI 런타임 설치에 대한 자세한 내용은 CRI 설치를 확인한다.
containerd
런타임 핸들러는 containerd의 구성 파일인 /etc/containerd/config.toml
통해 설정한다.
유효한 핸들러는 runtimes 단락 아래에서 설정한다.
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]
더 자세한 내용은 containerd의 구성 문서를 살펴본다.
CRI-O
런타임 핸들러는 CRI-O의 구성파일인 /etc/crio/crio.conf
을 통해 설정한다.
crio.runtime 테이블 아래에
유효한 핸들러를 설정한다.
[crio.runtime.runtimes.${HANDLER_NAME}]
runtime_path = "${PATH_TO_BINARY}"
더 자세한 것은 CRI-O의 설정 문서를 본다.
스케줄
Kubernetes v1.16 [beta]
RuntimeClass에 scheduling
필드를 지정하면, 이 RuntimeClass로 실행되는 파드가
이를 지원하는 노드로 예약되도록 제약 조건을 설정할 수 있다.
scheduling
이 설정되지 않은 경우 이 RuntimeClass는 모든 노드에서 지원되는 것으로 간주된다.
파드가 지정된 런타임클래스를 지원하는 노드에 안착한다는 것을 보장하려면,
해당 노드들은 runtimeClass.scheduling.nodeSelector
필드에서 선택되는 공통 레이블을 가져야한다.
런타임 클래스의 nodeSelector는 파드의 nodeSelector와 어드미션 시 병합되어서, 실질적으로
각각에 의해 선택된 노드의 교집합을 취한다. 충돌이 있는 경우,
파드는 거부된다.
지원되는 노드가 테인트(taint)되어서 다른 런타임클래스 파드가 노드에서 구동되는 것을 막고 있다면,
tolerations
를 런타임클래스에 추가할 수 있다. nodeSelector
를 사용하면, 어드미션 시
해당 톨러레이션(toleration)이 파드의 톨러레이션과 병합되어, 실질적으로 각각에 의해 선택된
노드의 합집합을 취한다.
노드 셀렉터와 톨러레이션 설정에 대해 더 배우려면 노드에 파드 할당을 참고한다.
파드 오버헤드
Kubernetes v1.24 [stable]
파드 실행과 연관되는 오버헤드 리소스를 지정할 수 있다. 오버헤드를 선언하면 클러스터(스케줄러 포함)가 파드와 리소스에 대한 결정을 내릴 때 처리를 할 수 있다.
파드 오버헤드는 런타임 클래스에서 overhead
필드를 통해 정의된다. 이 필드를 사용하면,
해당 런타임 클래스를 사용해서 구동 중인 파드의 오버헤드를 특정할 수 있고 이 오버헤드가
쿠버네티스 내에서 처리된다는 것을 보장할 수 있다.
다음 내용
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 요청을 실행.
훅 핸들러 실행
컨테이너 라이프사이클 관리 훅이 호출되면,
쿠버네티스 관리 시스템은 훅 동작에 따라 핸들러를 실행하고,
httpGet
와 tcpSocket
은 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
이벤트이다.
실패한 FailedPostStartHook
이벤트를 직접 생성하려면, lifecycle-events.yaml 파일을 수정하여 postStart 명령을 "badcommand"로 변경하고 이를 적용한다.
다음은 kubectl describe pod lifecycle-demo
를 실행하여 볼 수 있는 이벤트 출력 예시이다.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 7s default-scheduler Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
Normal Pulled 6s kubelet Successfully pulled image "nginx" in 229.604315ms
Normal Pulling 4s (x2 over 6s) kubelet Pulling image "nginx"
Normal Created 4s (x2 over 5s) kubelet Created container lifecycle-demo-container
Normal Started 4s (x2 over 5s) kubelet Started container lifecycle-demo-container
Warning FailedPostStartHook 4s (x2 over 5s) kubelet Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
Normal Killing 4s (x2 over 5s) kubelet FailedPostStartHook
Normal Pulled 4s kubelet Successfully pulled image "nginx" in 215.66395ms
Warning BackOff 2s (x2 over 3s) kubelet Back-off restarting failed container
다음 내용
- 컨테이너 환경에 대해 더 배우기.
- 컨테이너 라이프사이클 이벤트에 핸들러 부착 실습 경험하기.
4 - 쿠버네티스에서의 윈도우
4.1 - 쿠버네티스에서의 윈도우 컨테이너
윈도우 애플리케이션은 많은 조직에서 실행되는 서비스 및 애플리케이션의 상당 부분을 구성한다. 윈도우 컨테이너는 프로세스와 패키지 종속성을 캡슐화하는 현대적인 방법을 제공하여, 데브옵스(DevOps) 사례를 더욱 쉽게 사용하고 윈도우 애플리케이션의 클라우드 네이티브 패턴을 따르도록 한다.
윈도우 기반 애플리케이션과 리눅스 기반 애플리케이션에 투자한 조직은 워크로드를 관리하기 위해 별도의 오케스트레이터를 찾을 필요가 없으므로, 운영 체제와 관계없이 배포 전반에 걸쳐 운영 효율성이 향상된다.
쿠버네티스에서의 윈도우 노드
쿠버네티스에서 윈도우 컨테이너 오케스트레이션을 활성화하려면, 기존 리눅스 클러스터에 윈도우 노드를 추가한다. 쿠버네티스에서 파드 내의 윈도우 컨테이너를 스케줄링하는 것은 리눅스 기반 컨테이너를 스케줄링하는 것과 유사하다.
윈도우 컨테이너를 실행하려면, 쿠버네티스 클러스터가 여러 운영 체제를 포함하고 있어야 한다. 컨트롤 플레인은 리눅스에서만 실행할 수 있는 반면, 워커 노드는 윈도우 또는 리눅스를 실행할 수 있다.
노드의 운영 체제가 Windows Server 2019인 경우에만 윈도우 노드로써 사용할 수 있다.
이 문서에서 윈도우 컨테이너라는 용어는 프로세스 격리 기반의 윈도우 컨테이너를 의미한다. 쿠버네티스는 Hyper-V 격리 기반의 윈도우 컨테이너를 지원하지 않는다.
호환성 및 제한
일부 노드 기능은 특정 컨테이너 런타임을 사용할 때에만 이용 가능하며, 윈도우 노드에서 사용할 수 없는 기능도 있다. 예시는 다음과 같다.
- HugePages: 윈도우 컨테이너에서 지원되지 않음
- 특권을 가진(Privileged) 컨테이너: 윈도우 컨테이너에서 지원되지 않음. HostProcess 컨테이너가 비슷한 기능을 제공한다.
- TerminationGracePeriod: containerD를 필요로 한다.
공유 네임스페이스(shared namespaces)의 모든 기능이 지원되는 것은 아니다. 자세한 내용은 API 호환성을 참조한다.
윈도우 OS 버전 호환성에서 쿠버네티스와의 동작이 테스트된 윈도우 버전 상세사항을 확인한다.
API 및 kubectl의 관점에서, 윈도우 컨테이너는 리눅스 기반 컨테이너와 거의 같은 방식으로 작동한다. 그러나, 주요 기능에서 몇 가지 주목할 만한 차이점이 있으며, 이 섹션에서 소개된다.
리눅스와의 비교
윈도우에서 주요 쿠버네티스 요소는 리눅스와 동일한 방식으로 작동한다. 이 섹션에서는 몇 가지 주요 워크로드 추상화 및 이들이 윈도우에서 어떻게 매핑되는지를 다룬다.
파드는 쿠버네티스의 기본 빌딩 블록이며, 이는 쿠버네티스 오브젝트 모델에서 생성하고 배포하는 가장 작고 간단한 단위임을 의미한다. 동일한 파드에 윈도우 컨테이너와 리눅스 컨테이너를 배포할 수 없다. 파드의 모든 컨테이너는 단일 노드로 스케줄되며 이 때 각 노드는 특정 플랫폼 및 아키텍처를 갖는다. 다음과 같은 파드 기능, 속성 및 이벤트가 윈도우 컨테이너에서 지원된다.
프로세스 격리 및 볼륨 공유 기능을 갖춘 파드 당 하나 또는 여러 개의 컨테이너
파드의
status
필드준비성 프로브(readinessprobe), 활성 프로브(liveness probe) 및 시작 프로브(startup probe)
postStart 및 preStop 컨테이너 라이프사이클 훅
컨피그맵(ConfigMap), 시크릿(Secrets): 환경 변수 또는 볼륨 형태로
emptyDir
볼륨명명된 파이프 호스트 마운트
리소스 제한
OS 필드:
특정 파드가 윈도우 컨테이너를 사용하고 있다는 것을 나타내려면
.spec.os.name
필드를windows
로 설정해야 한다.참고:
쿠버네티스 1.25부터, `IdentifyPodOS` 기능 게이트는 GA 단계이며 기본적으로 활성화되어 있다.
.spec.os.name
필드를windows
로 설정했다면, 해당 파드의.spec
내의 다음 필드는 설정하지 않아야 한다.spec.hostPID
spec.hostIPC
spec.securityContext.seLinuxOptions
spec.securityContext.seccompProfile
spec.securityContext.fsGroup
spec.securityContext.fsGroupChangePolicy
spec.securityContext.sysctls
spec.shareProcessNamespace
spec.securityContext.runAsUser
spec.securityContext.runAsGroup
spec.securityContext.supplementalGroups
spec.containers[*].securityContext.seLinuxOptions
spec.containers[*].securityContext.seccompProfile
spec.containers[*].securityContext.capabilities
spec.containers[*].securityContext.readOnlyRootFilesystem
spec.containers[*].securityContext.privileged
spec.containers[*].securityContext.allowPrivilegeEscalation
spec.containers[*].securityContext.procMount
spec.containers[*].securityContext.runAsUser
spec.containers[*].securityContext.runAsGroup
위의 리스트에서 와일드카드(
*
)는 목록의 모든 요소를 가리킨다. 예를 들어,spec.containers[*].securityContext
는 모든 컨테이너의 시큐리티컨텍스트(SecurityContext) 오브젝트를 나타낸다. 위의 필드 중 하나라도 설정되어 있으면, API 서버는 해당 파드는 수용하지 않을 것이다.
다음과 같은 워크로드 리소스:
- 레플리카셋(ReplicaSet)
- 디플로이먼트(Deployment)
- 스테이트풀셋(StatefulSet)
- 데몬셋(DaemonSet)
- 잡(Job)
- 크론잡(CronJob)
- 레플리케이션컨트롤러(ReplicationController)
서비스 로드 밸런싱과 서비스에서 상세 사항을 확인한다.
파드, 워크로드 리소스 및 서비스는 쿠버네티스에서 윈도우 워크로드를 관리하는 데 중요한 요소이다. 그러나 그 자체만으로는 동적인 클라우드 네이티브 환경에서 윈도우 워크로드의 적절한 라이프사이클 관리를 수행하기에 충분하지 않다.
kubectl exec
- 파드 및 컨테이너 메트릭
- Horizontal pod autoscaling
- 리소스 쿼터(Resource quota)
- 스케쥴러 선점(preemption)
kubelet을 위한 명령줄 옵션
윈도우에서는 일부 kubelet 명령줄 옵션이 다르게 동작하며, 아래에 설명되어 있다.
--windows-priorityclass
를 사용하여 kubelet 프로세스의 스케줄링 우선 순위를 설정할 수 있다. (CPU 리소스 관리 참고)--kube-reserved
,--system-reserved
및--eviction-hard
플래그는 NodeAllocatable을 업데이트한다.--enforce-node-allocable
을 이용한 축출은 구현되어 있지 않다.--eviction-hard
및--eviction-soft
를 이용한 축출은 구현되어 있지 않다.- 윈도우 노드에서 실행되는 kubelet은 메모리 및 CPU 제한을 받지 않는다.
NodeAllocatable
에서--kube-reserved
와--system-reserved
가 차감될 뿐이며 워크로드에 제공될 리소스는 보장되지 않는다. 추가 정보는 윈도우 노드의 리소스 관리를 참고한다. MemoryPressure
컨디션은 구현되어 있지 않다.- kubelet은 메모리 부족(OOM, Out-of-Memory) 축출 동작을 수행하지 않는다.
API 호환성
운영 체제와 컨테이너 런타임의 차이로 인해, 윈도우에 대해 쿠버네티스 API가 동작하는 방식에 미묘한 차이가 있다. 일부 워크로드 속성은 리눅스에 맞게 설계되었으며, 이로 인해 윈도우에서 실행되지 않는다.
높은 수준에서, OS 개념에 대해 다음과 같은 차이점이 존재한다.
- ID - 리눅스는 정수형으로 표시되는 userID(UID) 및 groupID(GID)를 사용한다.
사용자와 그룹 이름은 정식 이름이 아니다.
UID+GID에 대한
/etc/groups
또는/etc/passwd
의 별칭일 뿐이다. 윈도우는 윈도우 보안 계정 관리자(Security Account Manager, SAM) 데이터베이스에 저장된 더 큰 이진 보안 식별자(SID)를 사용한다. 이 데이터베이스는 호스트와 컨테이너 간에 또는 컨테이너들 간에 공유되지 않는다. - 파일 퍼미션 - 윈도우는 SID 기반 접근 제어 목록을 사용하는 반면, 리눅스와 같은 POSIX 시스템은 오브젝트 권한 및 UID+GID 기반의 비트마스크(bitmask)를 사용하며, 접근 제어 목록도 선택적으로 사용한다.
- 파일 경로 - 윈도우의 규칙은
/
대신\
를 사용하는 것이다. Go IO 라이브러리는 두 가지 파일 경로 분리자를 모두 허용한다. 하지만, 컨테이너 내부에서 해석되는 경로 또는 커맨드 라인을 설정할 때\
가 필요할 수 있다. - 신호(Signals) - 윈도우 대화형(interactive) 앱은 종료를 다르게 처리하며,
다음 중 하나 이상을 구현할 수 있다.
- UI 스레드는
WM_CLOSE
등의 잘 정의된(well-defined) 메시지를 처리한다. - 콘솔 앱은 컨트롤 핸들러(Control Handler)를 사용하여 Ctrl-c 또는 Ctrl-break를 처리한다.
- 서비스는
SERVICE_CONTROL_STOP
제어 코드를 수용할 수 있는 Service Control Handler 함수를 등록한다.
- UI 스레드는
컨테이너 종료 코드는 리눅스와 동일하게 성공이면 0, 실패이면 0이 아닌 디른 수이다. 상세 오류 코드는 윈도우와 리눅스 간에 다를 수 있다. 그러나 쿠버네티스 컴포넌트(kubelet, kube-proxy)에서 전달된 종료 코드는 변경되지 않는다.
컨테이너 명세의 필드 호환성
다음 목록은 윈도우와 리눅스에서 파드 컨테이너 명세가 어떻게 다르게 동작하는지 기술한다.
- Huge page는 윈도우 컨테이너 런타임에서 구현되지 않았으며, 따라서 사용할 수 없다. 컨테이너에 대해 구성할 수 없는(not configurable) 사용자 권한(user privilege) 어설트(assert)가 필요하다.
requests.cpu
및requests.memory
- 요청(requests)이 노드의 사용 가능한 리소스에서 차감되며, 이는 노드 오버프로비저닝을 방지하기 위해 사용될 수 있다. 그러나, 오버프로비저닝된 노드 내에서 리소스를 보장하기 위해서는 사용될 수 없다. 운영자가 오버 프로비저닝을 완전히 피하려는 경우 모범 사례로 모든 컨테이너에 적용해야 한다.securityContext.allowPrivilegeEscalation
- 어떠한 기능도 연결되지 않아서, 윈도우에서는 사용할 수 없다.securityContext.capabilities
- POSIX 기능은 윈도우에서 구현되지 않았다.securityContext.privileged
- 윈도우는 특권을 가진(privileged) 컨테이너를 지원하지 않는다. 대신 호스트 프로세스 컨테이너를 사용한다.securityContext.procMount
- 윈도우에는/proc
파일시스템이 없다.securityContext.readOnlyRootFilesystem
- 윈도우에서는 사용할 수 없으며, 이는 레지스트리 및 시스템 프로세스가 컨테이너 내부에서 실행될 때 쓰기 권한이 필요하기 때문이다.securityContext.runAsGroup
- 윈도우에서는 GID가 지원되지 않으므로 사용할 수 없다.securityContext.runAsNonRoot
- 이 설정은 컨테이너가ContainerAdministrator
사용자로 실행되는 것을 방지하는데, 이는 리눅스의 root 사용자와 가장 가까운 윈도우 역할이다.securityContext.runAsUser
- 대신runAsUserName
을 사용한다.securityContext.seLinuxOptions
- SELinux는 리눅스 전용이므로 윈도우에서는 사용할 수 없다.terminationMessagePath
- 윈도우가 단일 파일 매핑을 지원하지 않음으로 인하여 몇 가지 제한이 있다. 기본값은/dev/termination-log
이며, 이 경로가 기본적으로 윈도우에 존재하지 않기 때문에 정상적으로 작동한다.
파드 명세의 필드 호환성
다음 목록은 윈도우와 리눅스에서 파드 명세가 어떻게 다르게 동작하는지 기술한다.
hostIPC
및hostpid
- 호스트 네임스페이스 공유 기능은 윈도우에서 사용할 수 없다.hostNetwork
- 하단 참조dnsPolicy
- 윈도우에서 호스트 네트워킹이 지원되지 않기 때문에dnsPolicy
를ClusterFirstWithHostNet
로 설정할 수 없다. 파드는 항상 컨테이너 네트워크와 함께 동작한다.podSecurityContext
하단 참조shareProcessNamespace
- 이것은 베타 기능이며, 윈도우에서 구현되지 않은 리눅스 네임스페이스에 의존한다. 윈도우는 프로세스 네임스페이스 또는 컨테이너의 루트 파일시스템을 공유할 수 없다. 네트워크만 공유할 수 있다.terminationGracePeriodSeconds
- 이것은 윈도우용 도커에서 완전히 구현되지 않았다. GitHub 이슈를 참고한다. 현재 동작은ENTRYPOINT
프로세스가CTRL_SHUTDOWN_EVENT
로 전송된 다음, 윈도우가 기본적으로 5초를 기다린 후, 마지막으로 정상적인 윈도우 종료 동작을 사용하여 모든 프로세스를 종료하는 것이다. 5초 기본값은 실제로는 컨테이너 내부 윈도우 레지스트리에 있으므로 컨테이너를 빌드할 때 재정의할 수 있다.volumeDevices
- 이것은 베타 기능이며, 윈도우에서 구현되지 않았다. 윈도우는 원시 블록 장치(raw block device)를 파드에 연결할 수 없다.volumes
emptyDir
볼륨을 정의한 경우, 이 볼륨의 소스(source)를memory
로 설정할 수는 없다.
mountPropagation
- 마운트 전파(propagation)는 윈도우에서 지원되지 않으므로 이 필드는 활성화할 수 없다.
호스트 네트워크(hostNetwork)의 필드 호환성
Kubernetes v1.26 [alpha]
이제 kubelet은, 윈도우 노드에서 실행되는 파드가 새로운 파드 네트워크 네임스페이스를 생성하는 대신 호스트의 네트워크 네임스페이스를 사용하도록 요청할 수 있다.
이 기능을 활성화하려면 kubelet에 --feature-gates=WindowsHostNetwork=true
를 전달한다.
참고:
이 기능을 지원하는 컨테이너 런타임을 필요로 한다.파드 시큐리티 컨텍스트의 필드 호환성
파드 securityContext
의 모든 필드는 윈도우에서 작동하지 않는다.
노드 문제 감지기
노드 문제 감지기(노드 헬스 모니터링하기 참조)는 기초적인 윈도우 지원을 포함한다. 더 자세한 정보는 프로젝트의 GitHub 페이지를 참고한다.
퍼즈(pause) 컨테이너
쿠버네티스 파드에서, 컨테이너를 호스팅하기 위해 먼저 "퍼즈" 컨테이너라는 인프라 컨테이너가 생성된다. 리눅스에서, 파드를 구성하는 cgroup과 네임스페이스가 계속 유지되기 위해서는 프로세스가 필요하며, 퍼즈 프로세스가 이를 담당한다. 동일한 파드에 속한 (인프라 및 워커) 컨테이너는 공통의 네트워크 엔드포인트(공통 IPv4/IPv6 주소, 공통 네트워크 포트 공간)를 공유한다. 쿠버네티스는 퍼즈 컨테이너를 사용하여 워커 컨테이너가 충돌하거나 재시작하여도 네트워킹 구성을 잃지 않도록 한다.
쿠버네티스는 윈도우 지원을 포함하는 다중 아키텍처 이미지를 유지보수한다.
쿠버네티스 v1.31의 경우
권장 퍼즈 이미지는 registry.k8s.io/pause:3.6
이다.
소스 코드는 GitHub에서 찾을 수 있다.
Microsoft는 리눅스 및 윈도우 amd64를 지원하는 다중 아키텍처 이미지를
mcr.microsoft.com/oss/kubernetes/pause:3.6
에서 유지보수하고 있다.
이 이미지는 쿠버네티스가 유지 관리하는 이미지와 동일한 소스코드에서 생성되었지만,
모든 윈도우 바이너리가 Microsoft에 의해
인증 코드(authenticode)로 서명되었다.
서명된 바이너리를 필요로 하는 프로덕션 또는 프로덕션에 준하는 환경에 파드를 배포하는 경우,
Microsoft가 유지 관리하는 이미지를 사용하는 것을 권장한다.
컨테이너 런타임
파드가 각 노드에서 실행될 수 있도록, 클러스터의 각 노드에 컨테이너 런타임을 설치해야 한다.
다음 컨테이너 런타임은 윈도우에서 동작한다.
ContainerD
Kubernetes v1.20 [stable]
ContainerD 1.4.0+를 윈도우 노드의 컨테이너 런타임으로 사용할 수 있다.
윈도우 노드에 ContainerD를 설치하는 방법을 확인한다.
참고:
containerd와 GMSA 사용 시 윈도우 네트워크 공유 접근에 대한 알려진 제한이 있으며, 이는 커널 패치를 필요로 한다.Mirantis Container Runtime
Mirantis Container Runtime(MCR)은 Windows Server 2019 및 이후 버전을 지원하는 컨테이너 런타임이다.
더 많은 정보는 Windows Server에 MCR 설치하기를 참고한다.
윈도우 운영 체제 버전 호환성
윈도우에는 호스트 OS 버전이 컨테이너 베이스 이미지 OS 버전과 일치해야 한다는 엄격한 호환성 규칙이 있다. 컨테이너 운영 체제가 Windows Server 2019인 윈도우 컨테이너만이 완전히 지원된다.
쿠버네티스 v1.31에서, 윈도우 노드(및 파드)에 대한 운영 체제 호환성은 다음과 같다.
- Windows Server LTSC 릴리스
- Windows Server 2019
- Windows Server 2022
- Windows Server SAC 릴리스
- Windows Server 버전 20H2
쿠버네티스 버전 차이 정책 또한 적용된다.
도움 받기 및 트러블슈팅
쿠버네티스 클러스터 트러블슈팅을 위한 기본 도움말은 이 섹션을 먼저 찾아 본다.
이 섹션에는 몇 가지 추가 윈도우 관련 트러블슈팅 도움말이 포함되어 있다. 로그는 쿠버네티스에서 트러블슈팅하는 데 중요한 요소이다. 다른 기여자로부터 트러블슈팅 지원을 구할 때마다 이를 포함시켜야 한다. SIG Windows의 로그 수집에 대한 기여 가이드의 지침을 따른다.
이슈 리포팅 및 기능 요청
버그처럼 보이는 부분이 있거나 기능 요청을 하고 싶다면, SIG Windows 기여 가이드를 참고하여 새 이슈를 연다. 먼저 이전에 이미 보고된 이슈가 있는지 검색하고, 이슈에 대한 경험과 추가 로그를 기재해야 한다. 티켓을 만들기 전에, 쿠버네티스 슬랙의 SIG Windows 채널 또한 초기 지원 및 트러블슈팅 아이디어를 얻을 수 있는 좋은 곳이다.
배포 도구
kubeadm 도구는 클러스터를 관리할 컨트롤 플레인과 워크로드를 실행할 노드를 제공함으로써 쿠버네티스 클러스터를 배포할 수 있게 해 준다. 윈도우 노드 추가하기 문서에서 kubeadm을 사용해 어떻게 클러스터에 윈도우 노드를 배포하는지를 설명한다.
쿠버네티스 클러스터 API 프로젝트는 윈도우 노드 배포를 자동화하는 수단을 제공한다.
윈도우 배포 채널
윈도우 배포 채널에 대한 자세한 설명은 Microsoft 문서를 참고한다.
각각의 Windows Server 서비스 채널 및 지원 모델은 Windows Server 서비스 채널에서 확인할 수 있다.
4.2 - 쿠버네티스에서 윈도우 컨테이너 스케줄링을 위한 가이드
많은 조직에서 실행하는 서비스와 애플리케이션의 상당 부분이 윈도우 애플리케이션으로 구성된다. 이 가이드는 쿠버네티스에서 윈도우 컨테이너를 구성하고 배포하는 단계를 안내한다.
목표
- 윈도우 노드에서 윈도우 컨테이너를 실행하는 예시 디플로이먼트를 구성한다.
- 쿠버네티스의 윈도우 관련 기능을 강조한다.
시작하기 전에
- 컨트롤 플레인과 윈도우 서버로 운영되는 워커 노드를 포함하는 쿠버네티스 클러스터를 생성한다.
- 쿠버네티스에서 서비스와 워크로드를 생성하고 배포하는 것은 리눅스나 윈도우 컨테이너 모두 비슷한 방식이라는 것이 중요하다. 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
참고:
포트 매핑도 지원하지만, 이 예시에서는 간결성을 위해 컨테이너 포트 80을 서비스로 직접 노출한다.모든 노드가 건강한지 확인한다.
kubectl get nodes
서비스를 배포하고 파드 갱신을 지켜보자.
kubectl apply -f win-webserver.yaml kubectl get pods -o wide -w
이 서비스가 정확히 배포되면 모든 파드는 Ready로 표기된다. 지켜보기를 중단하려면, Ctrl+C 를 누르자.
이 디플로이먼트가 성공적인지 확인한다. 다음을 검토하자.
- 리눅스 컨트롤 플레인 노드에서 나열된 두 파드가 존재하는지 확인하려면,
kubectl get pods
를 사용한다. - 네트워크를 통한 노드에서 파드로의 통신이 되는지 확인하려면, 리눅스 컨트롤 플레인 노드에서
curl
을 파드 IP 주소의 80 포트로 실행하여 웹 서버 응답을 확인한다. - 파드 간 통신이 되는지 확인하려면,
docker exec
나kubectl exec
를 이용해 파드 간에 핑(ping)한다(윈도우 노드가 2대 이상이라면, 서로 다른 노드에 있는 파드 간 통신도 확인할 수 있다). - 서비스에서 파드로의 통신이 되는지 확인하려면, 리눅스 컨트롤 플레인 노드와 독립 파드에서
curl
을 가상 서비스 IP 주소(kubectl get services
로 볼 수 있는)로 실행한다. - 서비스 검색(discovery)이 되는지 확인하려면, 쿠버네티스 기본 DNS 접미사와 서비스 이름으로
curl
을 실행한다. - 인바운드 연결이 되는지 확인하려면, 클러스터 외부 장비나 리눅스 컨트롤 플레인 노드에서 NodePort로
curl
을 실행한다. - 아웃바운드 연결이 되는지 확인하려면,
kubectl exec
를 이용해서 파드에서 외부 IP 주소로curl
을 실행한다.
- 리눅스 컨트롤 플레인 노드에서 나열된 두 파드가 존재하는지 확인하려면,
참고:
윈도우 컨테이너 호스트는 현재 윈도우 네트워킹 스택의 플랫폼 제한으로 인해, 그 안에서 스케줄링하는 서비스의 IP 주소로 접근할 수 없다. 윈도우 파드만 서비스 IP 주소로 접근할 수 있다.가시성
워크로드에서 로그 캡쳐하기
로그는 가시성의 중요한 요소이다. 로그는 사용자가 워크로드의 운영측면을
파악할 수 있도록 하며 문제 해결의 핵심 요소이다.
윈도우 컨테이너, 그리고 윈도우 컨테이너 내의 워크로드는 리눅스 컨테이너와는 다르게 동작하기 때문에,
사용자가 로그를 수집하기 어려웠고 이로 인해 운영 가시성이 제한되어 왔다.
예를 들어 윈도우 워크로드는 일반적으로 ETW(Event Tracing for Windows)에 로그인하거나
애플리케이션 이벤트 로그에 항목을 푸시하도록 구성한다.
Microsoft의 오픈 소스 도구인 LogMonitor는
윈도우 컨테이너 안에 구성된 로그 소스를 모니터링하는 권장하는 방법이다.
LogMonitor는 이벤트 로그, ETW 공급자 그리고 사용자 정의 애플리케이션 로그 모니터링을 지원하고
kubectl logs <pod>
에 의한 사용을 위해 STDOUT으로 파이프한다.
LogMonitor GitHub 페이지의 지침에 따라 모든 컨테이너 바이너리와 설정 파일을 복사하고, LogMonitor가 로그를 STDOUT으로 푸시할 수 있도록 필요한 엔트리포인트를 추가한다.
컨테이너 사용자 구성하기
설정 가능한 컨테이너 username 사용하기
윈도우 컨테이너는 이미지 기본값과는 다른 username으로 엔트리포인트와 프로세스를 실행하도록 설정할 수 있다. 여기에서 이에 대해 추가적으로 배울 수 있다.
그룹 매니지드 서비스 어카운트를 이용하여 워크로드 신원 관리하기
윈도우 컨테이너 워크로드는 그룹 매니지드 서비스 어카운트(GMSA, Group Managed Service Account)를 이용하여 구성할 수 있다. 그룹 매니지드 서비스 어카운트는 액티브 디렉터리 어카운트의 특정한 종류로 자동 암호 관리 기능, 단순화된 서비스 주체 이름(SPN, simplified service principal name), 여러 서버의 다른 관리자에게 관리를 위임하는 기능을 제공한다. GMSA로 구성한 컨테이너는 GMSA로 구성된 신원을 들고 있는 동안 외부 액티브 디렉터리 도메인 리소스를 접근할 수 있다. 윈도우 컨테이너를 위한 GMSA를 이용하고 구성하는 방법은 여기에서 알아보자.
테인트(Taint)와 톨러레이션(Toleration)
사용자는 리눅스와 윈도우 워크로드를 (동일한 OS를 실행하는) 적절한 노드에 스케줄링되도록 하기 위해 테인트와 노드셀렉터(nodeSelector)의 조합을 이용해야 한다. 아래는 권장되는 방식의 개요인데, 이것의 주요 목표 중에 하나는 이 방식이 기존 리눅스 워크로드와 호환되어야 한다는 것이다.
1.25부터, 파드의 컨테이너가 어떤 운영체제 용인지를 파드의 .spec.os.name
에 설정할 수 있다(그리고 설정해야 한다).
리눅스 컨테이너를 실행하는 파드에는 .spec.os.name
을 linux
로 설정한다.
윈도우 컨테이너를 실행하는 파드에는 .spec.os.name
을
windows
로 설정한다.
참고:
1.25부터,IdentifyPodOS
기능은 GA 단계이며 기본적으로 활성화되어 있다.스케줄러는 파드를 노드에 할당할 때
.spec.os.name
필드의 값을 사용하지 않는다.
컨트롤 플레인이 파드를 적절한 운영 체제가 실행되고 있는 노드에 배치하도록 하려면,
파드를 노드에 할당하는
일반적인 쿠버네티스 메카니즘을 사용해야 한다.
.spec.os.name
필드는 윈도우 파드의 스케줄링에는 영향을 미치지 않기 때문에,
윈도우 파드가 적절한 윈도우 노드에 할당되도록 하려면
테인트, 톨러레이션 및 노드 셀렉터가 여전히 필요하다.
특정 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
를 자동으로 추가한다.
만약 이전 버전을 실행 중인 경우, 이 레이블을 윈도우 노드에 수동으로 추가하는 것을 권장한다.
이 레이블은 호환성을 위해 일치시켜야 하는 윈도우 메이저, 마이너 및 빌드 번호를 나타낸다. 각 윈도우 서버 버전에 대해 현재 사용하고 있는 빌드 번호는 다음과 같다.
제품 이름 | 빌드 번호 |
---|---|
Windows Server 2019 | 10.0.17763 |
Windows Server, 버전 20H2 | 10.0.19042 |
Windows Server 2022 | 10.0.20348 |
RuntimeClass로 단순화
런타임클래스(RuntimeClass)를 사용해서 테인트(taint)와 톨러레이션(toleration)을 사용하는 프로세스를 간소화 할 수 있다.
클러스터 관리자는 이 테인트와 톨러레이션을 캡슐화하는 데 사용되는 RuntimeClass
오브젝트를 생성할 수 있다.
- 이 파일을
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"
- 클러스터 관리자로
kubectl create -f runtimeClasses.yml
를 실행해서 사용한다. - 파드 사양에 적합한
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
5 - 워크로드
워크로드는 쿠버네티스에서 구동되는 애플리케이션이다.
워크로드가 단일 컴포넌트이거나 함께 작동하는 여러 컴포넌트이든 관계없이, 쿠버네티스에서는 워크로드를 일련의
파드 집합 내에서 실행한다.
쿠버네티스에서 Pod
는 클러스터에서 실행 중인 컨테이너
집합을 나타낸다.
쿠버네티스 파드에는 정의된 라이프사이클이 있다.
예를 들어, 일단 파드가 클러스터에서 실행되고 나서
해당 파드가 동작 중인 노드에
심각한 오류가 발생하면 해당 노드의 모든 파드가 실패한다. 쿠버네티스는 이 수준의 실패를
최종(final)으로 취급한다. 사용자는 향후 노드가 복구되는 것과 상관 없이 Pod
를 새로 생성해야 한다.
그러나, 작업이 훨씬 쉽도록, 각 Pod
를 직접 관리할 필요는 없도록 만들었다.
대신, 사용자를 대신하여 파드 집합을 관리하는 워크로드 리소스 를 사용할 수 있다.
이러한 리소스는 지정한 상태와 일치하도록 올바른 수의 올바른 파드 유형이
실행되고 있는지 확인하는 컨트롤러를
구성한다.
쿠버네티스는 다음과 같이 여러 가지 빌트인(built-in) 워크로드 리소스를 제공한다.
Deployment
및ReplicaSet
(레거시 리소스 레플리케이션컨트롤러(ReplicationController)를 대체).Deployment
는Deployment
의 모든Pod
가 필요 시 교체 또는 상호 교체 가능한 경우, 클러스터의 스테이트리스 애플리케이션 워크로드를 관리하기에 적합하다.StatefulSet
는 어떻게든 스테이트(state)를 추적하는 하나 이상의 파드를 동작하게 해준다. 예를 들면, 워크로드가 데이터를 지속적으로 기록하는 경우, 사용자는Pod
와PersistentVolume
을 연계하는StatefulSet
을 실행할 수 있다. 전체적인 회복력 향상을 위해서,StatefulSet
의Pods
에서 동작 중인 코드는 동일한StatefulSet
의 다른Pods
로 데이터를 복제할 수 있다.DaemonSet
은 노드-로컬 기능(node-local facilities)을 제공하는Pods
를 정의한다. 이러한 기능들은 클러스터를 운용하는 데 기본적인 것일 것이다. 예를 들면, 네트워킹 지원 도구 또는 add-on 등이 있다.DaemonSet
의 명세에 맞는 노드를 클러스터에 추가할 때마다, 컨트롤 플레인은 해당 신규 노드에DaemonSet
을 위한Pod
를 스케줄한다.Job
및CronJob
은 실행 완료 후 중단되는 작업을 정의한다.CronJobs
이 스케줄에 따라 반복되는 반면, 잡은 단 한 번의 작업을 나타낸다.
더 넓은 쿠버네티스 에코시스템 내에서는 추가적인 동작을 제공하는 제 3자의 워크로드
리소스도 찾을 수 있다.
커스텀 리소스 데피니션을 사용하면,
쿠버네티스 코어에서 제공하지 않는 특별한 동작을 원하는 경우 제 3자의 워크로드 리소스를
추가할 수 있다. 예를 들어, 사용자 애플리케이션을 위한 Pods
의 그룹을 실행하되
모든 파드가 가용한 경우가 아닌 경우 멈추고 싶다면(아마도 높은 처리량의 분산 처리를 하는 상황 같은),
사용자는 해당 기능을 제공하는 확장을 구현하거나 설치할 수 있다.
다음 내용
각 리소스에 대해 읽을 수 있을 뿐만 아니라, 리소스와 관련된 특정 작업에 대해서도 알아볼 수 있다.
Deployment
를 사용하여 스테이트리스(stateless) 애플리케이션 실행- 스테이트풀(stateful) 애플리케이션을 단일 인스턴스 또는 복제된 세트로 실행
CronJob
을 사용하여 자동화된 작업 실행
코드를 구성(configuration)에서 분리하는 쿠버네티스의 메커니즘을 배우기 위해서는, 구성을 참고하길 바란다.
다음은 쿠버네티스가 애플리케이션의 파드를 어떻게 관리하는지를 알 수 있게 해주는 두 가지 개념이다.
- 가비지(Garbage) 수집은 소유하는 리소스 가 제거된 후 클러스터에서 오브젝트를 정리한다.
- time-to-live after finished 컨트롤러는 잡이 완료된 이후에 정의된 시간이 경과되면 잡을 제거한다.
일단 애플리케이션이 실행되면, 인터넷에서 서비스로 사용하거나, 웹 애플리케이션의 경우에만 인그레스(Ingress)를 이용하여 사용할 수 있다.
5.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 서브도메인 이름인지 확인한다.
파드 OS
Kubernetes v1.25 [stable]
파드를 실행할 때 OS를 표시하려면 .spec.os.name
필드를 windows
또는
linux
로 설정해야 한다. 이 두 가지 운영체제는 현재 쿠버네티스에서 지원되는
유일한 운영체제이다. 앞으로 이 목록이 확장될 수 있다.
쿠버네티스 v1.31에서, 이 필드에 대해 설정한 값은
파드의 스케줄링에 영향을 미치지 않는다.
.spec.os.name
을 설정하면 파드 검증 시
OS를 식별하는 데 도움이 된다.
kubelet은
자신이 실행되고 있는 노드의 운영체제와
동일하지 않은 파드 OS가 명시된 파드의 실행을 거부한다.
파드 시큐리티 스탠다드도 이 필드를 사용하여 해당 운영체제와 관련이 없는 정책을 시행하지 않도록 한다.
파드와 컨트롤러
워크로드 리소스를 사용하여 여러 파드를 만들고 관리할 수 있다. 리소스에 대한 컨트롤러는 파드 장애 시 복제 및 롤아웃과 자동 복구를 처리한다. 예를 들어, 노드가 실패하면, 컨트롤러는 해당 노드의 파드가 작동을 중지했음을 인식하고 대체 파드를 생성한다. 스케줄러는 대체 파드를 정상 노드에 배치한다.
다음은 하나 이상의 파드를 관리하는 워크로드 리소스의 몇 가지 예시이다.
파드 템플릿
워크로드 리소스에 대한 컨트롤러는 파드 템플릿 에서 파드를 생성하고 사용자 대신 해당 파드를 관리한다.
파드템플릿(PodTemplate)은 파드를 생성하기 위한 명세이며, 디플로이먼트, 잡 및 데몬셋과 같은 워크로드 리소스에 포함된다.
워크로드 리소스의 각 컨트롤러는 워크로드 오브젝트 내부의 PodTemplate
을
사용하여 실제 파드를 생성한다. PodTemplate
은 앱을 실행하는 데 사용되는 워크로드 리소스가
무엇이든지 원하는 상태의 일부이다.
아래 샘플은 하나의 컨테이너를 시작하는 template
이 있는 간단한 잡의
매니페스트이다. 해당 파드의 컨테이너는 메시지를 출력한 다음 일시 중지한다.
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# 여기서부터 파드 템플릿이다
spec:
containers:
- name: hello
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# 여기까지 파드 템플릿이다
파드 템플릿을 수정하거나 새로운 파드 템플릿으로 바꿔도 이미 존재하는 파드에는 직접적인 영향을 주지 않는다. 워크로드 리소스의 파드 템플릿을 변경하는 경우, 해당 리소스는 수정된 템플릿을 사용하는 대체 파드를 생성해야 한다.
예를 들어, 스테이트풀셋 컨트롤러는 실행 중인 파드가 각 스테이트풀셋 오브젝트에 대한 현재 파드 템플릿과 일치하는지 확인한다. 스테이트풀셋을 수정하여 파드 템플릿을 변경하면, 스테이트풀셋이 업데이트된 템플릿을 기반으로 새로운 파드를 생성하기 시작한다. 결국, 모든 이전의 파드가 새로운 파드로 교체되고, 업데이트가 완료된다.
각 워크로드 리소스는 파드 템플릿의 변경 사항을 처리하기 위한 자체 규칙을 구현한다. 스테이트풀셋에 대해 자세히 알아 보려면, 스테이트풀셋 기본 튜토리얼에서 업데이트 전략을 읽어본다.
노드에서 kubelet은 파드 템플릿과 업데이트에 대한 상세 정보를 직접 관찰하거나 관리하지 않는다. 이러한 상세 내용은 추상화된다. 이러한 추상화와 관심사 분리(separation of concerns)는 시스템 시맨틱을 단순화하고, 기존 코드를 변경하지 않고도 클러스터의 동작을 확장할 수 있게 한다.
파드 갱신 및 교체
이전 섹션에서 언급한 바와 같이, 워크로드 리소스의 파드 템플릿이 바뀌면, 컨트롤러는 기존의 파드를 갱신하거나 패치하는 대신 갱신된 템플릿을 기반으로 신규 파드를 생성한다.
쿠버네티스는 사용자가 파드를 직접 관리하는 것을 막지는 않는다.
동작 중인 파드의 필드를 갱신하는 것도 가능하다.
그러나,
patch
및
replace
와 같은
파드 갱신 작업에는 다음과 같은 제약이 있다.
파드에 대한 대부분의 메타데이터는 불변(immutable)이다. 예를 들면, 사용자는
namespace
,name
,uid
, 또는creationTimestamp
필드를 변경할 수 없다. 그리고generation
필드는 고유하다. 이 필드는 필드의 현재 값을 증가시키는 갱신만 허용한다.metadata.deletionTimestamp
가 설정된 경우,metadata.finalizers
리스트에 새로운 항목이 추가될 수 없다.파드 갱신은
spec.containers[*].image
,spec.initContainers[*].image
,spec.activeDeadlineSeconds
, 또는spec.tolerations
이외의 필드는 변경하지 않을 것이다.spec.tolerations
에 대해서만 새로운 항목을 추가할 수 있다.spec.activeDeadlineSeconds
필드를 추가할 때는, 다음의 두 가지 형태의 갱신만 허용한다.- 지정되지 않은 필드를 양수로 설정;
- 필드의 양수를 음수가 아닌 더 작은 숫자로 갱신.
리소스 공유와 통신
파드는 파드에 속한 컨테이너 간의 데이터 공유와 통신을 지원한다.
파드 스토리지
파드는 공유 스토리지 볼륨의 집합을 지정할 수 있다. 파드의 모든 컨테이너는 공유 볼륨에 접근할 수 있으므로, 해당 컨테이너가 데이터를 공유할 수 있다. 또한 볼륨은 내부 컨테이너 중 하나를 다시 시작해야 하는 경우 파드의 영구 데이터를 유지하도록 허용한다. 쿠버네티스가 공유 스토리지를 구현하고 파드에서 사용할 수 있도록 하는 방법에 대한 자세한 내용은 스토리지를 참고한다.
파드 네트워킹
각 파드에는 각 주소 패밀리에 대해 고유한 IP 주소가 할당된다.
파드의 모든 컨테이너는 네트워크 네임스페이스를 공유하며,
여기에는 IP 주소와 네트워크 포트가 포함된다.
파드 내부(이 경우에 만 해당)에서, 파드에 속한 컨테이너는
localhost
를 사용하여 서로 통신할 수 있다.
파드의 컨테이너가 파드 외부의 엔티티와 통신할 때,
공유 네트워크 리소스(포트와 같은)를 사용하는 방법을 조정해야 한다.
파드 내에서 컨테이너는 IP 주소와 포트 공간을 공유하며,
localhost
를 통해 서로를 찾을 수 있다.
파드의 컨테이너는 SystemV 세마포어 또는 POSIX 공유 메모리와 같은
표준 프로세스 간 통신을 사용하여 서로 통신할 수도 있다.
다른 파드의 컨테이너는 고유한 IP 주소를 가지며 특별한 구성 없이 OS 수준의 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에 의해 직접 검사)
프로브에 대한 자세한 내용은 파드 라이프사이클 문서를 참고한다.
다음 내용
- 파드의 라이프사이클에 대해 알아본다.
- 런타임클래스(RuntimeClass)와 이를 사용하여 다양한 컨테이너 런타임 구성으로 다양한 파드를 설정하는 방법에 대해 알아본다.
- PodDisruptionBudget과 이를 사용하여 서비스 중단 중에 애플리케이션 가용성을 관리하는 방법에 대해 읽어본다.
- 파드는 쿠버네티스 REST API의 최상위 리소스이다. Pod 오브젝트 정의는 오브젝트를 상세히 설명한다.
- 분산 시스템 툴킷: 컴포지트 컨테이너에 대한 패턴은 둘 이상의 컨테이너가 있는 파드의 일반적인 레이아웃을 설명한다.
- 파드 토폴로지 분배 제약 조건에 대해 읽어본다.
쿠버네티스가 다른 리소스(스테이트풀셋이나 디플로이먼트와 같은)에서 공통 파드 API를 래핑하는 이유에 대한 콘텍스트를 이해하기 위해서, 다음과 같은 선행 기술에 대해 읽어볼 수 있다.
5.1.1 - 파드 라이프사이클
이 페이지에서는 파드의 라이프사이클을 설명한다. 파드는 정의된 라이프사이클을 따른다.
Pending
단계에서 시작해서, 기본 컨테이너 중 적어도 하나
이상이 OK로 시작하면 Running
단계를 통과하고, 그런 다음 파드의 컨테이너가
실패로 종료되었는지 여부에 따라 Succeeded
또는 Failed
단계로 이동한다.
파드가 실행되는 동안, kubelet은 일종의 오류를 처리하기 위해 컨테이너를 다시 시작할 수 있다. 파드 내에서, 쿠버네티스는 다양한 컨테이너 상태를 추적하고 파드를 다시 정상 상태로 만들기 위해 취할 조치를 결정한다.
쿠버네티스 API에서 파드는 명세와 실제 상태를 모두 가진다. 파드 오브젝트의 상태는 일련의 파드 컨디션으로 구성된다. 사용자의 애플리케이션에 유용한 경우, 파드의 컨디션 데이터에 사용자 정의 준비성 정보를 삽입할 수도 있다.
파드는 파드의 수명 중 한 번만 스케줄된다. 파드가 노드에 스케줄(할당)되면, 파드는 중지되거나 종료될 때까지 해당 노드에서 실행된다.
파드의 수명
개별 애플리케이션 컨테이너와 마찬가지로, 파드는 비교적 임시(계속 이어지는 것이 아닌) 엔티티로 간주된다. 파드가 생성되고, 고유 ID(UID)가 할당되고, 종료(재시작 정책에 따라) 또는 삭제될 때까지 남아있는 노드에 스케줄된다. 만약 노드가 종료되면, 해당 노드에 스케줄된 파드는 타임아웃 기간 후에 삭제되도록 스케줄된다.
파드는 자체적으로 자가 치유되지 않는다. 파드가 노드에 스케줄된 후에 해당 노드가 실패하면, 파드는 삭제된다. 마찬가지로, 파드는 리소스 부족 또는 노드 유지 관리 작업으로 인한 축출에서 살아남지 못한다. 쿠버네티스는 컨트롤러라 부르는 하이-레벨 추상화를 사용하여 상대적으로 일회용인 파드 인스턴스를 관리하는 작업을 처리한다.
UID로 정의된 특정 파드는 다른 노드로 절대 "다시 스케줄"되지 않는다. 대신, 해당 파드는 사용자가 원한다면 이름은 같지만, UID가 다른, 거의 동일한 새 파드로 대체될 수 있다.
볼륨과 같은 어떤 것이 파드와 동일한 수명을 갖는다는 것은, 특정 파드(정확한 UID 포함)가 존재하는 한 그것이 존재함을 의미한다. 어떤 이유로든 해당 파드가 삭제되고, 동일한 대체 파드가 생성되더라도, 관련된 그것(이 예에서는 볼륨)도 폐기되고 새로 생성된다.
컨테이너 간의 공유 스토리지에 퍼시스턴트 볼륨을 사용하는 웹 서버와 파일 풀러(puller)가 포함된 다중 컨테이너 파드이다.
파드의 단계
파드의 status
필드는
phase
필드를 포함하는
PodStatus 오브젝트로 정의된다.
파드의 phase는 파드가 라이프사이클 중 어느 단계에 해당하는지 표현하는 간단한 고수준의 요약이다. Phase는 컨테이너나 파드의 관측 정보에 대한 포괄적인 롤업이나, 포괄적인 상태 머신을 표현하도록 의도되지는 않았다.
파드 phase 값에서 숫자와 의미는 엄격하게 지켜진다.
여기에 문서화된 내용 이외에는, 파드와 파드에 주어진 phase
값에 대해서
어떤 사항도 가정되어서는 안 된다.
phase
에 가능한 값은 다음과 같다.
값 | 의미 |
---|---|
Pending | 파드가 쿠버네티스 클러스터에서 승인되었지만, 하나 이상의 컨테이너가 설정되지 않았고 실행할 준비가 되지 않았다. 여기에는 파드가 스케줄되기 이전까지의 시간 뿐만 아니라 네트워크를 통한 컨테이너 이미지 다운로드 시간도 포함된다. |
Running | 파드가 노드에 바인딩되었고, 모든 컨테이너가 생성되었다. 적어도 하나의 컨테이너가 아직 실행 중이거나, 시작 또는 재시작 중에 있다. |
Succeeded | 파드에 있는 모든 컨테이너들이 성공적으로 종료되었고, 재시작되지 않을 것이다. |
Failed | 파드에 있는 모든 컨테이너가 종료되었고, 적어도 하나 이상의 컨테이너가 실패로 종료되었다. 즉, 해당 컨테이너는 non-zero 상태로 빠져나왔거나(exited) 시스템에 의해서 종료(terminated)되었다. |
Unknown | 어떤 이유에 의해서 파드의 상태를 얻을 수 없다. 이 단계는 일반적으로 파드가 실행되어야 하는 노드와의 통신 오류로 인해 발생한다. |
참고:
파드가 삭제될 때, 일부 kubectl 커맨드에서Terminating
이 표시된다.
이 Terminating
상태는 파드의 단계에 해당하지 않는다.
파드에는 그레이스풀하게(gracefully) 종료되도록 기간이 부여되며, 그 기본값은 30초이다.
강제로 파드를 종료하려면 --force
플래그를 설정하면 된다.노드가 죽거나 클러스터의 나머지와의 연결이 끊어지면, 쿠버네티스는
손실된 노드의 모든 파드의 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 배열을 가진다. kubelet은 다음 PodConditions를 관리한다.
PodScheduled
: 파드가 노드에 스케줄되었다.PodHasNetwork
: (알파 기능; 반드시 명시적으로 활성화해야 함) 샌드박스가 성공적으로 생성되고 네트워킹이 구성되었다.ContainersReady
: 파드의 모든 컨테이너가 준비되었다.Initialized
: 모든 초기화 컨테이너가 성공적으로 완료(completed)되었다.Ready
: 파드는 요청을 처리할 수 있으며 일치하는 모든 서비스의 로드 밸런싱 풀에 추가되어야 한다.
필드 이름 | 설명 |
---|---|
type | 이 파드 컨디션의 이름이다. |
status | 가능한 값이 "True ", "False ", 또는 "Unknown "으로, 해당 컨디션이 적용 가능한지 여부를 나타낸다. |
lastProbeTime | 파드 컨디션이 마지막으로 프로브된 시간의 타임스탬프이다. |
lastTransitionTime | 파드가 한 상태에서 다른 상태로 전환된 마지막 시간에 대한 타임스탬프이다. |
reason | 컨디션의 마지막 전환에 대한 이유를 나타내는 기계가 판독 가능한 UpperCamelCase 텍스트이다. |
message | 마지막 상태 전환에 대한 세부 정보를 나타내는 사람이 읽을 수 있는 메시지이다. |
파드의 준비성(readiness)
Kubernetes v1.14 [stable]
애플리케이션은 추가 피드백 또는 신호를 PodStatus: Pod readiness
와 같이 주입할 수 있다. 이를 사용하기 위해, kubelet이 파드의 준비성을 평가하기
위한 추가적인 컨디션들을 파드의 spec
내 readinessGate
필드를 통해서 지정할 수 있다.
준비성 게이트는 파드에 대한 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
로 설정한다.
파드 네트워크 준비성(readiness)
Kubernetes v1.25 [alpha]
파드가 노드에 스케줄링되면, kubelet이 이 파드를 승인해야 하고
모든 볼륨이 마운트되어야 한다. 이러한 단계가 완료되면 kubelet은
(컨테이너 런타임 인터페이스(Container runtime interface, CRI)를 사용하여) 컨테이너 런타임과
통신하여 런타임 샌드박스를 설정하고 파드에 대한 네트워킹을 구성한다. 만약
PodHasNetworkCondition
기능 게이트가 활성화되면,
Kubelet은 파드의 status.conditions
필드에 있는 PodHasNetwork
컨디션을 통해
파드가 초기화 마일스톤에 도달했는지 여부를 보고한다.
Kubelet이 파드에 네트워킹이 구성된 런타임 샌드박스가
없음을 탐지했을 때 PodHasNetwork
컨디션은 False
로 설정된다. 이것은 다음
시나리오에서 발생한다.
- 파드 라이프사이클 초기에, kubelet이 컨테이너 런타임을 사용하여 파드를 위한 샌드박스 생성을 아직 시작하지 않은 때.
- 파드 라이프사이클 후기에, 파드 샌드박스가 다음중 하나의 이유로
파괴되었을 때.
- 파드 축출 없이, 노드가 재부팅됨
- 격리를 위해 가상 머신을 사용하는 컨테이너 런타임을 사용하는 경우, 파드 샌드박스 가상 머신이 재부팅됨(이후, 새로운 샌드박스 및 새로운 컨테이너 네트워크 구성 생성이 필요함)
런타임 플러그인이 파드를 위한 샌드박스 생성 및 네트워크 구성을 성공적으로 완료하면
kubelet이 PodHasNetwork
컨디션을 True
로 설정한다.
PodHasNetwork
컨디션이 True
로 설정되면
kubelet이 컨테이너 이미지를 풀링하고 컨테이너를 생성할 수 있다.
초기화 컨테이너가 있는 파드의 경우, kubelet은 초기화 컨테이너가
성공적으로 완료(런타임 플러그인에 의한 성공적인 샌드박스 생성 및 네트워크 구성이 완료되었음을 의미)된 후
Initialized
컨디션을 True
로 설정한다.
초기화 컨테이너가 없는 파드의 경우, kubelet은 샌드박스 생성 및 네트워크 구성이
시작되기 전에 Initialized
컨디션을 True
로 설정한다.
컨테이너 프로브(probe)
프로브 는 컨테이너에서 kubelet에 의해 주기적으로 수행되는 진단(diagnostic)이다. 진단을 수행하기 위해서, kubelet은 컨테이너 안에서 코드를 실행하거나, 또는 네트워크 요청을 전송한다.
체크 메커니즘
프로브를 사용하여 컨테이너를 체크하는 방법에는 4가지가 있다. 각 프로브는 다음의 4가지 메커니즘 중 단 하나만을 정의해야 한다.
exec
- 컨테이너 내에서 지정된 명령어를 실행한다. 명령어가 상태 코드 0으로 종료되면 진단이 성공한 것으로 간주한다.
grpc
- gRPC를 사용하여
원격 프로시저 호출을 수행한다.
체크 대상이 gRPC 헬스 체크를 구현해야 한다.
응답의
status
가SERVING
이면 진단이 성공했다고 간주한다. gRPC 프로브는 알파 기능이며GRPCContainerProbe
기능 게이트를 활성화해야 사용할 수 있다. httpGet
- 지정한 포트 및 경로에서 컨테이너의 IP주소에 대한
HTTP
GET
요청을 수행한다. 응답의 상태 코드가 200 이상 400 미만이면 진단이 성공한 것으로 간주한다. tcpSocket
- 지정된 포트에서 컨테이너의 IP주소에 대해 TCP 검사를 수행한다. 포트가 활성화되어 있다면 진단이 성공한 것으로 간주한다. 원격 시스템(컨테이너)가 연결을 연 이후 즉시 닫는다면, 이 또한 진단이 성공한 것으로 간주한다.
프로브 결과
각 probe는 다음 세 가지 결과 중 하나를 가진다.
Success
- 컨테이너가 진단을 통과함.
Failure
- 컨테이너가 진단에 실패함.
Unknown
- 진단 자체가 실패함(아무런 조치를 수행해서는 안 되며, kubelet이 추가 체크를 수행할 것이다)
프로브 종류
kubelet은 실행 중인 컨테이너들에 대해서 선택적으로 세 가지 종류의 프로브를 수행하고 그에 반응할 수 있다.
livenessProbe
- 컨테이너가 동작 중인지 여부를 나타낸다. 만약
활성 프로브(liveness probe)에 실패한다면, kubelet은 컨테이너를 죽이고, 해당 컨테이너는
재시작 정책의 대상이 된다. 만약 컨테이너가
활성 프로브를 제공하지 않는 경우, 기본 상태는
Success
이다. readinessProbe
- 컨테이너가 요청을 처리할 준비가 되었는지 여부를 나타낸다.
만약 준비성 프로브(readiness probe)가 실패한다면, 엔드포인트 컨트롤러는
파드에 연관된 모든 서비스들의 엔드포인트에서 파드의 IP주소를 제거한다. 준비성 프로브의
초기 지연 이전의 기본 상태는
Failure
이다. 만약 컨테이너가 준비성 프로브를 지원하지 않는다면, 기본 상태는Success
이다. startupProbe
- 컨테이너 내의 애플리케이션이 시작되었는지를 나타낸다.
스타트업 프로브(startup probe)가 주어진 경우, 성공할 때까지 다른 나머지 프로브는
활성화되지 않는다. 만약 스타트업 프로브가 실패하면, kubelet이 컨테이너를 죽이고,
컨테이너는 재시작 정책에 따라 처리된다. 컨테이너에 스타트업
프로브가 없는 경우, 기본 상태는
Success
이다.
활성, 준비성 및 스타트업 프로브를 설정하는 방법에 대한 추가적인 정보는, 활성, 준비성 및 스타트업 프로브 설정하기를 참조하면 된다.
언제 활성 프로브를 사용해야 하는가?
Kubernetes v1.0 [stable]
만약 컨테이너 속 프로세스가 어떠한 이슈에 직면하거나 건강하지 못한
상태(unhealthy)가 되는 등 프로세스 자체의 문제로 중단될 수 있더라도, 활성 프로브가
반드시 필요한 것은 아니다. 그 경우에는 kubelet이 파드의 restartPolicy
에
따라서 올바른 대처를 자동적으로 수행할 것이다.
프로브가 실패한 후 컨테이너가 종료되거나 재시작되길 원한다면, 활성 프로브를
지정하고, restartPolicy
를 항상(Always) 또는 실패 시(OnFailure)로 지정한다.
언제 준비성 프로브를 사용해야 하는가?
Kubernetes v1.0 [stable]
프로브가 성공한 경우에만 파드에 트래픽 전송을 시작하려고 한다면, 준비성 프로브를 지정하길 바란다. 이 경우에서는, 준비성 프로브가 활성 프로브와 유사해 보일 수도 있지만, 스팩에 준비성 프로브가 존재한다는 것은 파드가 트래픽을 받지 않는 상태에서 시작되고 프로브가 성공하기 시작한 이후에만 트래픽을 받는다는 뜻이다.
만약 컨테이너가 유지 관리를 위해서 자체 중단되게 하려면, 준비성 프로브를 지정하길 바란다. 준비성 프로브는 활성 프로브와는 다르게 준비성에 특정된 엔드포인트를 확인한다.
만약 애플리케이션이 백엔드 서비스에 엄격한 의존성이 있다면, 활성 프로브와 준비성 프로브 모두 활용할 수도 있다. 활성 프로브는 애플리케이션 스스로가 건강한 상태면 통과하지만, 준비성 프로브는 추가적으로 요구되는 각 백-엔드 서비스가 가용한지 확인한다. 이를 이용하여, 오류 메시지만 응답하는 파드로 트래픽이 가는 것을 막을 수 있다.
만약 컨테이너가 시동 시 대량 데이터의 로딩, 구성 파일, 또는 마이그레이션에 대한 작업을 수행해야 한다면, 스타트업 프로브를 사용하면 된다. 그러나, 만약 failed 애플리케이션과 시동 중에 아직 데이터를 처리하고 있는 애플리케이션을 구분하여 탐지하고 싶다면, 준비성 프로브를 사용하는 것이 더 적합할 것이다.
참고:
파드가 삭제될 때 요청들을 흘려 보내기(drain) 위해 준비성 프로브가 꼭 필요한 것은 아니다. 삭제 시에, 파드는 프로브의 존재 여부와 무관하게 자동으로 스스로를 준비되지 않은 상태(unready)로 변경한다. 파드는 파드 내의 모든 컨테이너들이 중지될 때까지 준비되지 않은 상태로 남아 있다.언제 스타트업 프로브를 사용해야 하는가?
Kubernetes v1.20 [stable]
스타트업 프로브는 서비스를 시작하는 데 오랜 시간이 걸리는 컨테이너가 있는 파드에 유용하다. 긴 활성 간격을 설정하는 대신, 컨테이너가 시작될 때 프로브를 위한 별도의 구성을 설정하여, 활성 간격보다 긴 시간을 허용할 수 있다.
컨테이너가 보통 initialDelaySeconds + failureThreshold × periodSeconds
이후에 기동된다면, 스타트업 프로브가
활성화 프로브와 같은 엔드포인트를 확인하도록 지정해야 한다.
periodSeconds
의 기본값은 10s 이다. 이 때 컨테이너가 활성화 프로브의
기본값 변경 없이 기동되도록 하려면, failureThreshold
를 충분히 높게 설정해주어야
한다. 그래야 데드락(deadlocks)을 방지하는데 도움이 된다.
파드의 종료
파드는 클러스터의 노드에서 실행되는 프로세스를 나타내므로, 해당 프로세스가
더 이상 필요하지 않을 때 정상적으로 종료되도록 하는 것이 중요하다(KILL
시그널로 갑자기 중지되고 정리할 기회가 없는 것 보다).
디자인 목표는 삭제를 요청하고 프로세스가 종료되는 시기를 알 수 있을 뿐만 아니라, 삭제가 결국 완료되도록 하는 것이다. 사용자가 파드의 삭제를 요청하면, 클러스터는 파드가 강제로 종료되기 전에 의도한 유예 기간을 기록하고 추적한다. 강제 종료 추적이 적용되면, kubelet은 정상 종료를 시도한다.
일반적으로, 컨테이너 런타임은 각 컨테이너의 기본 프로세스에 TERM 신호를
전송한다. 많은 컨테이너 런타임은 컨테이너 이미지에 정의된 STOPSIGNAL
값을 존중하며
TERM 대신 이 값을 보낸다.
일단 유예 기간이 만료되면, KILL 시그널이 나머지 프로세스로
전송되고, 그런 다음 파드는
API 서버로부터 삭제된다. 프로세스가
종료될 때까지 기다리는 동안 kubelet 또는 컨테이너 런타임의 관리 서비스가 다시 시작되면, 클러스터는
전체 원래 유예 기간을 포함하여 처음부터 다시 시도한다.
플로우의 예는 다음과 같다.
- 이
kubectl
도구를 사용하여 기본 유예 기간(30초)으로 특정 파드를 수동으로 삭제한다. - API 서버의 파드는 유예 기간과 함께 파드가 "dead"로 간주되는
시간으로 업데이트된다.
kubectl describe
를 사용하여 삭제하려는 파드를 확인하면, 해당 파드가 "Terminating"으로 표시된다. 파드가 실행 중인 노드에서, kubelet이 파드가 종료된 것(terminating)으로 표시되었음을 확인하는 즉시(정상적인 종료 기간이 설정됨), kubelet은 로컬 파드의 종료 프로세스를 시작한다.- 파드의 컨테이너 중 하나가
preStop
훅을 정의한 경우, kubelet은 컨테이너 내부에서 해당 훅을 실행한다. 유예 기간이 만료된 후preStop
훅이 계속 실행되면, kubelet은 2초의 작은 일회성 유예 기간 연장을 요청한다.참고:
`preStop` 훅을 완료하는 데 기본 유예 기간이 허용하는 것보다 오랜 시간이 필요한 경우, 이에 맞게 `terminationGracePeriodSeconds` 를 수정해야 한다.
- kubelet은 컨테이너 런타임을 트리거하여 각 컨테이너 내부의 프로세스 1에 TERM 시그널을
보낸다.
참고:
파드의 컨테이너는 서로 다른 시간에 임의의 순서로 TERM 시그널을 수신한다. 종료 순서가 중요한 경우, `preStop` 훅을 사용하여 동기화하는 것이 좋다.
- 파드의 컨테이너 중 하나가
- kubelet이 정상 종료를 시작하는 동시에, 컨트롤 플레인은 구성된 셀렉터가 있는 서비스를 나타내는 엔드포인트슬라이스(EndpointSplice)(그리고 엔드포인트) 오브젝트에서 종료된 파드를 제거한다. 레플리카셋(ReplicaSet)과 기타 워크로드 리소스는 더 이상 종료된 파드를 유효한 서비스 내 복제본으로 취급하지 않는다. 로드 밸런서(서비스 프록시와 같은)가 종료 유예 기간이 시작되는 즉시 엔드포인트 목록에서 파드를 제거하므로 느리게 종료되는 파드는 트래픽을 계속 제공할 수 없다.
- 유예 기간이 만료되면, kubelet은 강제 종료를 트리거한다. 컨테이너 런타임은
SIGKILL
을 파드의 모든 컨테이너에서 여전히 실행 중인 모든 프로세스로 전송한다. kubelet은 해당 컨테이너 런타임이 하나를 사용하는 경우 숨겨진pause
컨테이너도 정리한다. - kubelet은 유예 기간을 0(즉시 삭제)으로 설정하여, API 서버에서 파드 오브젝트의 강제 삭제를 트리거한다.
- API 서버가 파드의 API 오브젝트를 삭제하면, 더 이상 클라이언트에서 볼 수 없다.
강제 파드 종료
주의:
강제 삭제는 일부 워크로드와 해당 파드에 대해서 잠재적으로 중단될 수 있다.기본적으로, 모든 삭제는 30초 이내에는 정상적으로 수행된다. kubectl delete
명령은
기본값을 재정의하고 사용자의 고유한 값을 지정할 수 있는 --grace-period=<seconds>
옵션을
지원한다.
유예 기간을 0
로 강제로 즉시 설정하면 API 서버에서 파드가
삭제된다. 파드가 노드에서 계속 실행 중인 경우, 강제 삭제는 kubelet을 트리거하여
즉시 정리를 시작한다.
참고:
강제 삭제를 수행하려면--grace-period=0
와 함께 추가 플래그 --force
를 지정해야 한다.강제 삭제가 수행되면, API 서버는 실행 중인 노드에서 파드가 종료되었다는 kubelet의 확인을 기다리지 않는다. API에서 즉시 파드를 제거하므로 동일한 이름으로 새로운 파드를 생성할 수 있다. 노드에서 즉시 종료되도록 설정된 파드는 강제 종료되기 전에 작은 유예 기간이 계속 제공된다.
주의:
즉시 제거는 실행 중인 자원이 정상적으로 종료되는 것을 보장하지 않는다. 자원은 클러스터에서 영원히 회수되지 않을 수 있다.스테이트풀셋(StatefulSet)의 일부인 파드를 강제 삭제해야 하는 경우, 스테이트풀셋에서 파드를 삭제하기에 대한 태스크 문서를 참고한다.
파드의 가비지 콜렉션
실패한 파드의 경우, API 오브젝트는 사람이나 컨트롤러 프로세스가 명시적으로 파드를 제거할 때까지 클러스터의 API에 남아 있다.
컨트롤 플레인에서의 컨트롤러 역할인 파드 가비지 콜렉터(PodGC)는, 파드 수가 구성된 임계값(kube-controller-manager에서
terminated-pod-gc-threshold
에 의해 결정됨)을 초과할 때 종료된 파드(Succeeded
또는
Failed
단계 포함)를 정리한다.
이렇게 하면 시간이 지남에 따라 파드가 생성되고 종료될 때 리소스 유출이 방지된다.
추가적으로 PodGC는 다음과 같은 조건들 중 하나라도 만족하는 파드들을 정리한다.
- 고아 파드 - 더 이상 존재하지 않는 노드에 종속되어있는 파드이거나,
- 스케줄되지 않은 종료 중인 파드이거나,
NodeOutOfServiceVolumeDetach
기능 게이트가 활성화되어 있을 때,node.kubernetes.io/out-of-service
에 테인트된 준비되지 않은 노드에 속한 종료 중인 파드인 경우에 적용된다.
PodDisruptionConditions
기능 게이트가 활성화된 경우, PodGC는
파드를 정리하는 것 뿐만 아니라 해당 파드들이 non-terminal 단계에 있는 경우
그들을 실패했다고 표시하기도 한다.
또한, PodGC는 고아 파드를 정리할 때 파드 중단 조건을 추가하기도 한다.
(자세한 내용은 파드 중단 조건을 확인한다.)
다음 내용
컨테이너 라이프사이클 이벤트에 핸들러를 연결하는 핸즈온 연습을 해보자.
활성, 준비성 및 스타트업 프로브 설정하는 핸즈온 연습을 해보자.
컨테이너 라이프사이클 훅에 대해 자세히 알아보자.
API의 파드와 컨테이너 상태에 대한 자세한 내용은 파드의
.status
에 대해 다루는 API 레퍼런스 문서를 참고한다.
5.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.kubernetes.io/name: 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.kubernetes.io/name=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 init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
파드의 초기화 컨테이너의 상태를 보기 위해, 다음을 실행한다.
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
mydb
및 myservice
서비스를 시작하고 나면, 초기화 컨테이너가 완료되고
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
mydb
와 myservice
서비스 생성하기.
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 이상에 적용된다. 이전 버전의 쿠버네티스를 사용하는 경우 해당 쿠버네티스 버전의 문서를 참고한다.
다음 내용
5.1.3 - 중단(disruption)
이 가이드는 고가용성 애플리케이션을 구성하려는 소유자와 파드에서 발생하는 장애 유형을 이해하기 원하는 애플리케이션 소유자를 위한 것이다.
또한 클러스터의 업그레이드와 오토스케일링과 같은 클러스터의 자동화 작업을 하려는 관리자를 위한 것이다.
자발적 중단과 비자발적 중단
파드는 누군가(사람 또는 컨트롤러)가 파괴하거나 불가피한 하드웨어 오류 또는 시스템 소프트웨어 오류가 아니면 사라지지 않는다.
우리는 이런 불가피한 상황을 애플리케이션의 비자발적 중단 으로 부른다. 예시:
- 노드를 지원하는 물리 머신의 하드웨어 오류
- 클러스터 관리자의 실수로 VM(인스턴스) 삭제
- 클라우드 공급자 또는 하이퍼바이저의 오류로 인한 VM 장애
- 커널 패닉
- 클러스터 네트워크 파티션의 발생으로 클러스터에서 노드가 사라짐
- 노드의 리소스 부족으로 파드가 축출됨
리소스 부족을 제외한 나머지 조건은 대부분의 사용자가 익숙할 것이다. 왜냐하면 그 조건은 쿠버네티스에 국한되지 않기 때문이다.
우리는 다른 상황을 자발적인 중단 으로 부른다. 여기에는 애플리케이션 소유자의 작업과 클러스터 관리자의 작업이 모두 포함된다. 다음은 대표적인 애플리케이션 소유자의 작업이다.
- 디플로이먼트 제거 또는 다른 파드를 관리하는 컨트롤러의 제거
- 재시작을 유발하는 디플로이먼트의 파드 템플릿 업데이트
- 파드를 직접 삭제(예: 우연히)
클러스터 관리자의 작업은 다음을 포함한다.
- 복구 또는 업그레이드를 위한 노드 드레이닝.
- 클러스터의 스케일 축소를 위한 노드 드레이닝(클러스터 오토스케일링에 대해 알아보기 ).
- 노드에 다른 무언가를 추가하기 위해 파드를 제거.
위 작업은 클러스터 관리자가 직접 수행하거나 자동화를 통해 수행하며, 클러스터 호스팅 공급자에 의해서도 수행된다.
클러스터에 자발적인 중단을 일으킬 수 있는 어떤 원인이 있는지 클러스터 관리자에게 문의하거나 클라우드 공급자에게 문의하고, 배포 문서를 참조해서 확인해야 한다. 만약 자발적인 중단을 일으킬 수 있는 원인이 없다면 Pod Disruption Budget의 생성을 넘길 수 있다.
주의:
모든 자발적인 중단이 Pod Disruption Budget에 연관되는 것은 아니다. 예를 들어 디플로이먼트 또는 파드의 삭제는 Pod Disruption Budget을 무시한다.중단 다루기
비자발적인 중단으로 인한 영향을 경감하기 위한 몇 가지 방법은 다음과 같다.
- 파드가 필요로 하는 리소스를 요청하는지 확인한다.
- 고가용성이 필요한 경우 애플리케이션을 복제한다. (복제된 스테이트리스 및 스테이트풀 애플리케이션에 대해 알아보기.)
- 복제된 애플리케이션의 구동 시 훨씬 더 높은 가용성을 위해 랙 전체 (안티-어피니티 이용) 또는 영역 간 (다중 영역 클러스터를 이용한다면)에 애플리케이션을 분산해야 한다.
자발적 중단의 빈도는 다양하다. 기본적인 쿠버네티스 클러스터에서는 자동화된 자발적 중단은 발생하지 않는다(사용자가 지시한 자발적 중단만 발생한다). 그러나 클러스터 관리자 또는 호스팅 공급자가 자발적 중단이 발생할 수 있는 일부 부가 서비스를 운영할 수 있다. 예를 들어 노드 소프트웨어의 업데이트를 출시하는 경우 자발적 중단이 발생할 수 있다. 또한 클러스터(노드) 오토스케일링의 일부 구현에서는 단편화를 제거하고 노드의 효율을 높이는 과정에서 자발적 중단을 야기할 수 있다. 클러스터 관리자 또는 호스팅 공급자는 예측 가능한 자발적 중단 수준에 대해 문서화해야 한다. 파드 스펙 안에 프라이어리티클래스 사용하기와 같은 특정 환경설정 옵션 또한 자발적(+ 비자발적) 중단을 유발할 수 있다.
파드 disruption budgets
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를 사용하여 파드를 축출하면,
PodSpec의
terminationGracePeriodSeconds
설정을 준수하여 정상적으로 종료됨 상태가 된다.
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-1
을 kubectl drain
명령어를 사용해서 비우려 한다.
kubectl
은 pod-a
과 pod-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 |
이 시점에서 클러스터 관리자는 클러스터에 노드를 추가해서 업그레이드를 진행해야 한다.
쿠버네티스에 중단이 발생할 수 있는 비율을 어떻게 변화시키는지 다음의 사례를 통해 알 수 있다.
- 애플리케이션에 필요한 레플리카의 수
- 인스턴스를 정상적으로 종료하는데 소요되는 시간
- 새 인스턴스를 시작하는데 소요되는 시간
- 컨트롤러의 유형
- 클러스터의 리소스 용량
파드 중단 조건
Kubernetes v1.26 [beta]
참고:
만약 쿠버네티스 1.31 보다 낮은 버전을 사용하고 있다면, 해당 버전의 문서를 참조하자.이 기능이 활성화되면, 파드 전용 DisruptionTarget
컨디션이 추가되어
중단(disruption)으로 인해 파드가 삭제될 예정임을 나타낸다.
추가로 컨디션의 reason
필드는
파드 종료에 대한 다음 원인 중 하나를 나타낸다.
PreemptionByKubeScheduler
- 파드는 더 높은 우선순위를 가진 새 파드를 수용하기 위해 스케줄러에 의해 선점(preempted)된다. 자세한 내용은 파드 우선순위(priority)와 선점(preemption)을 참조해보자.
DeletionByTaintManager
- 허용하지 않는
NoExecute
테인트(taint) 때문에 파드가 테인트 매니저(kube-controller-manager
내의 노드 라이프사이클 컨트롤러의 일부)에 의해 삭제될 예정이다. taint 기반 축출을 참조해보자. EvictionByEvictionAPI
- 파드에 쿠버네티스 API를 이용한 축출이 표시되었다.
DeletionByPodGC
- 더 이상 존재하지 않는 노드에 바인딩된 파드는 파드의 가비지 콜렉션에 의해 삭제될 예정이다.
TerminationByKubelet
- 노드 압박-축출 또는 그레이스풀 노드 셧다운으로 인해 kubelet이 파드를 종료시켰다.
참고:
파드 중단은 중단될 수 있다. 컨트롤 플레인은 동일한 파드의 중단을 계속 다시 시도하지만, 파드의 중단이 보장되지는 않는다. 결과적으로,DisruptionTarget
컨디션이 파드에 추가될 수 있지만, 해당 파드는 사실상
삭제되지 않았을 수 있다. 이러한 상황에서는, 일정 시간이 지난 뒤에
파드 중단 상태가 해제된다.잡(또는 크론잡(CronJob))을 사용할 때, 이러한 파드 중단 조건을 잡의 파드 실패 정책의 일부로 사용할 수 있다.
클러스터 소유자와 애플리케이션 소유자의 역할 분리
보통 클러스터 매니저와 애플리케이션 소유자는 서로에 대한 지식이 부족한 별도의 역할로 생각하는 것이 유용하다. 이와 같은 책임의 분리는 다음의 시나리오에서 타당할 수 있다.
- 쿠버네티스 클러스터를 공유하는 애플리케이션 팀이 많고, 자연스럽게 역할이 나누어진 경우
- 타사 도구 또는 타사 서비스를 이용해서 클러스터 관리를 자동화 하는 경우
Pod Disruption Budget은 역할 분리에 따라 역할에 맞는 인터페이스를 제공한다.
만약 조직에 역할 분리에 따른 책임의 분리가 없다면 Pod Disruption Budget을 사용할 필요가 없다.
클러스터에서 중단이 발생할 수 있는 작업을 하는 방법
만약 클러스터 관리자라면, 그리고 클러스터 전체 노드에 노드 또는 시스템 소프트웨어 업그레이드와 같은 중단이 발생할 수 있는 작업을 수행하는 경우 다음과 같은 옵션을 선택한다.
- 업그레이드 하는 동안 다운타임을 허용한다.
- 다른 레플리카 클러스터로 장애조치를 한다.
- 다운타임은 없지만, 노드 사본과 전환 작업을 조정하기 위한 인력 비용이 많이 발생할 수 있다.
- PDB를 이용해서 애플리케이션의 중단에 견디도록 작성한다.
- 다운타임 없음
- 최소한의 리소스 중복
- 클러스터 관리의 자동화 확대 적용
- 내결함성이 있는 애플리케이션의 작성은 까다롭지만 자발적 중단를 허용하는 작업의 대부분은 오토스케일링과 비자발적 중단를 지원하는 작업과 겹친다.
다음 내용
Pod Disruption Budget 설정하기의 단계를 따라서 애플리케이션을 보호한다.
노드 비우기에 대해 자세히 알아보기
롤아웃 중에 가용성을 유지하는 단계를 포함하여 디플로이먼트 업데이트에 대해 알아보기
5.1.4 - 임시(Ephemeral) 컨테이너
Kubernetes v1.25 [stable]
이 페이지는 임시 컨테이너에 대한 개요를 제공한다. 이 특별한 유형의 컨테이너는 트러블슈팅과 같은 사용자가 시작한 작업을 완료하기 위해 기존 파드에서 임시적으로 실행된다. 임시 컨테이너는 애플리케이션을 빌드하는 경우보다는 서비스 점검과 같은 경우에 더 적합하다.
임시 컨테이너 이해하기
파드 는 쿠버네티스 애플리케이션의 기본 구성 요소이다. 파드는 일회용이고, 교체 가능한 것으로 의도되었기 때문에, 사용자는 파드가 한번 생성되면, 컨테이너를 추가할 수 없다. 대신, 사용자는 보통 디플로이먼트 를 사용해서 제어하는 방식으로 파드를 삭제하고 교체한다.
그러나 때때로 재현하기 어려운 버그의 문제 해결을 위해 기존 파드의 상태를 검사해야 할 수 있다. 이 경우 사용자는 기존 파드에서 임시 컨테이너를 실행해서 상태를 검사하고, 임의의 명령을 실행할 수 있다.
임시 컨테이너는 무엇인가?
임시 컨테이너는 리소스 또는 실행에 대한 보증이 없다는 점에서
다른 컨테이너와 다르며, 결코 자동으로 재시작되지 않는다. 그래서
애플리케이션을 만드는데 적합하지 않다. 임시 컨테이너는
일반 컨테이너와 동일한 ContainerSpec
을 사용해서 명시하지만, 많은 필드가
호환되지 않으며 임시 컨테이너에는 허용되지 않는다.
- 임시 컨테이너는 포트를 가지지 않을 수 있으므로,
ports
,livenessProbe
,readinessProbe
와 같은 필드는 허용되지 않는다. - 파드에 할당된 리소스는 변경할 수 없으므로,
resources
설정이 허용되지 않는다. - 허용되는 필드의 전체 목록은 임시컨테이너 참조 문서를 본다.
임시 컨테이너는 pod.spec
에 직접 추가하는 대신
API에서 특별한 ephemeralcontainers
핸들러를 사용해서 만들어지기 때문에
kubectl edit
을 사용해서 임시 컨테이너를 추가할 수 없다.
일반 컨테이너와 마찬가지로, 사용자는 임시 컨테이너를 파드에 추가한 이후에 변경하거나 제거할 수 없다.
참고:
임시 컨테이너는 스태틱 파드에서 지원되지 않는다.임시 컨테이너의 사용
임시 컨테이너는 컨테이너가 충돌 되거나 또는 컨테이너 이미지에
디버깅 도구가 포함되지 않은 이유로 kubectl exec
이 불충분할 때
대화형 문제 해결에 유용하다.
특히, distroless 이미지
를 사용하면 공격 표면(attack surface)과 버그 및 취약점의 노출을 줄이는 최소한의
컨테이너 이미지를 배포할 수 있다. distroless 이미지는 셸 또는 어떤 디버깅 도구를
포함하지 않기 때문에, kubectl exec
만으로는 distroless
이미지의 문제 해결이 어렵다.
임시 컨테이너 사용 시 프로세스 네임스페이스 공유를 활성화하면 다른 컨테이너 안의 프로세스를 보는 데 도움이 된다.
다음 내용
- 임시 컨테이너 디버깅하기에 대해 알아보기.
5.1.5 - 사용자 네임스페이스
Kubernetes v1.25 [alpha]
이 페이지에서는 사용자 네임스페이스가 쿠버네티스 파드에서 사용되는 방법에 대해 설명한다. 사용자 네임스페이스는 컨테이너 내에서 실행 중인 사용자와 호스트의 사용자를 분리할 수 있도록 한다.
컨테이너에서 루트(root)로 실행되는 프로세스는 호스트에서 다른 (루트가 아닌) 사용자로 실행될 수 있다. 즉, 프로세스는 사용자 네임스페이스 내부 작업에 대해 특권을 갖지만(privileged) 네임스페이스 외부의 작업에서는 특권을 갖지 않는다.
이러한 기능을 사용하여 훼손(compromised)된 컨테이너가 호스트나 동일한 노드의 다른 파드에 미칠 수 있는 손상을 줄일 수 있다. HIGH 또는 CRITICAL 등급의 일부 보안 취약점은 사용자 네임스페이스가 Active 상태일 때 사용할 수 없다. 사용자 네임스페이스는 일부 미래의 취약점들도 완화시킬 수 있을 것으로 기대된다.
시작하기 전에
이것은 리눅스 전용 기능이다. 또한 컨테이너 런타임에서 이 기능을 쿠버네티스 스테이트리스(stateless) 파드와 함께 사용하려면 지원이 필요하다.
CRI-O: v1.25에는 사용자 네임스페이스가 지원된다.
containerd: 1.7 릴리즈에서 지원이 계획돼 있다. 자세한 내용은 containerd 이슈 #7063를 참조한다.
cri-dockerd에서의 지원은 아직 계획되지 않았다.
개요
사용자 네임스페이스는 컨테이너의 사용자를 호스트의 다른 사용자에게 매핑할 수 있는 리눅스 기능이다. 또한 사용자 네임스페이스의 파드에 부여된 기능은 네임스페이스 내부에서만 유효하고 외부에서는 무효하다.
파드는 pod.spec.hostUsers
필드를 false
로 설정해
사용자 네임스페이스를 사용하도록 선택할 수 있다.
kubelet은 동일 노드에 있는 두 개의 스테이트리스 파드가 동일한 매핑을 사용하지 않도록 보장하는 방식으로 파드가 매핑된 호스트 UID나 GID를 선택한다.
pod.spec
의 runAsUser
, runAsGroup
, fsGroup
등의 필드는 항상
컨테이너 내의 사용자를 가리킨다.
이 기능을 활성화한 경우 유효한 UID/GID의 범위는 0부터 65535까지이다.
이는 파일 및 프로세스(runAsUser
, runAsGroup
등)에 적용된다.
이 범위를 벗어나는 UID/GID를 사용하는 파일은 오버플로 ID,
주로 65534(/proc/sys/kernel/overflowuid
와 /proc/sys/kernel/overflowgid
에서 설정됨)에
속하는 것으로 간주된다.
그러나 65534 사용자/그룹으로 실행하더라도 이러한 파일을 수정할 수 없다.
루트로 실행돼야 하지만 다른 호스트 네임스페이스나 리소스에 접근하지 않는 대부분의 애플리케이션은 사용자 네임스페이스를 활성화한 경우 변경할 필요 없이 계속 정상적으로 실행돼야 한다.
스테이트리스 파드의 사용자 네임스페이스 이해하기
기본 설정의 여러 컨테이너 런타임(Docker Engine, containerd, CRI-O 등)은 격리를 위해 리눅스 네임스페이스를 사용한다. 다른 기술도 존재하며 위와 같은 런타임에도 사용할 수 있다. (예를 들어, Kata Container는 리눅스 네임스페이스 대신 VM을 사용한다.) 이 페이지는 격리를 위해 리눅스 네임스페이스를 사용하는 컨테이너 런타임에 주안을 둔다.
파드를 생성할 때 기본적으로 컨테이너의 네트워크를 분리하는 네임스페이스, 프로세스 보기를 분리하는 PID 네임스페이스 등 여러 새로운 네임스페이스가 격리에 사용된다. 사용자 네임스페이스를 사용하면 컨테이너의 사용자와 노드의 사용자가 격리된다.
이는 컨테이너가 루트로 실행될 수 있고 호스트의 루트가 아닌 사용자에게 매핑될 수 있다는 것을 의미한다.
컨테이너 내부에서 프로세스는 자신이 루트로 실행된다고 생각하지만
(따라서 apt
, yum
등과 같은 도구가 정상적으로 작동)
실제로 프로세스는 호스트에 대한 권한이 없다.
예를 들어, 호스트에서 ps aux
를 실행함으로써
컨테이너 프로세스가 어느 사용자에 의해 실행 중인지 확인할 수 있다.
사용자 ps
는 컨테이너 안에서 명령 id
를 실행하면 나타나는 사용자와 동일하지 않다.
이 추상화는 컨테이너가 호스트로 탈출하는 것과 같은 상황을 제한한다. 컨테이너가 호스트에서 권한이 없는 사용자로 실행 중이기 때문에 호스트에게 수행할 수 있는 작업은 제한된다.
또한, 각 파드의 사용자는 호스트의 서로 겹치지 않는 다른 사용자에게 매핑되므로 다른 파드에 대해서도 수행할 수 있는 작업이 제한된다.
파드에 부여된 기능 또한 파드 사용자 네임스페이스로 제한되는데 대부분 유효하지 않고 심지어 일부는 완전히 무효하다. 다음은 두 가지 예시이다.
CAP_SYS_MODULE
은 사용자 네임스페이스를 사용하여 파드에 부여돼도 아무런 효과가 없으며 파드는 커널 모듈을 로드할 수 없다.CAP_SYS_ADMIN
은 파드의 사용자 네임스페이스로 제한되며 네임스페이스 외부에서는 유효하지 않다.
사용자 네임스페이스를 사용하지 않고 루트로 실행되는 컨테이너는 컨테이너 브레이크아웃(container breakout)의 경우 노드에 대한 루트 권한을 가진다. 컨테이너에 일부 기능이 부여된 경우 호스트에서도 해당 기능이 유효하다. 사용자 네임스페이스를 사용할 경우 이 중 어느 것도 사실이 아니다.
사용자 네임스페이스 사용 시 변경사항에 대한 자세한 내용은
man 7 user_namespace
를 참조한다.
사용자 네임스페이스를 지원하도록 노드 설정하기
호스트의 파일 및 프로세스에서 0 ~ 65535 범위의 UID/GID를 사용하는 것이 좋다.
kubelet은 파드에 그것보다 더 높은 UID/GID를 할당한다. 따라서 가능한 많은 격리를 보장하려면 호스트의 파일 및 호스트의 프로세스에 사용되는 UID/GID가 0-65535 범위여야 한다.
이 권장 사항은 파드가 호스트의 임의의 파일을 잠재적으로 읽을 수 있는 CVE-2021-25741과 같은 CVE의 영향을 완화하기 위해 중요하다. 파드와 호스트의 UID/GID가 겹치지 않으면 파드가 수행할 수 있는 작업이 제한된다. 즉, 파드 UID/GID가 호스트의 파일 소유자/그룹과 일치하지 않게 된다.
제약 사항
파드에 사용자 네임스페이스를 사용하는 경우
다른 호스트 네임스페이스를 사용할 수 없다.
특히 hostUsers: false
를 설정하면 다음 중 하나를 설정할 수 없다.
hostNetwork: true
hostIPC: true
hostPID: true
파드는 볼륨을 전혀 사용하지 않거나 볼륨을 사용하는 경우에는 다음 볼륨 유형만 사용할 수 있다.
- configmap
- secret
- projected
- downwardAPI
- emptyDir
파드가 이러한 볼륨의 파일을 읽을 수 있도록 하기 위해
파드에 .spec.securityContext.fsGroup
을
0
으로 지정한 것처럼 볼륨이 생성된다.
이 값이 다른 값으로 지정되면 당연히 이 다른 값이 대신 적용된다.
이로 인해 볼륨의 특정 항목에 대한 defaultMode
나 mode
가
그룹에 대한 권한 없이 지정되었더라도
이러한 볼륨에 대한 폴더와 파일은 그룹에 대한 권한을 갖는다.
예를 들어 파일에 소유자에 대한 권한만 있는 방식으로
이러한 볼륨을 마운트할 수 없다.
5.1.6 - 다운워드(Downward) API
컨테이너가 쿠버네티스에 지나치게 종속되지 않으면서도 자기 자신에 대한 정보를 알고 있으면 유용할 때가 있다. 다운워드 API는 컨테이너가 자기 자신 혹은 클러스터에 대한 정보를, 쿠버네티스 클라이언트나 API 서버 없이도 사용할 수 있게 한다.
예를 들어, 잘 알려진 특정 환경 변수에다가 고유한 식별자를 넣어 사용하는 애플리케이션이 있다고 하자. 해당 애플리케이션에 맞게 작업할 수도 있겠지만, 이는 지루하고 오류가 나기 쉬울뿐더러, 낮은 결합이라는 원칙에도 위배된다. 대신, 파드의 이름을 식별자로 사용하고 잘 알려진 환경 변수에 파드의 이름을 넣는 것도 괜찮은 방법이다.
쿠버네티스에는 실행 중인 컨테이너에 파드 및 컨테이너 필드를 노출하는 두 가지 방법이 있다.
파드 및 컨테이너 필드를 노출하는 이 두 가지 방법을 다운워드 API라고 한다.
사용 가능한 필드
쿠버네티스 API 필드 중 일부만이 다운워드 API를 통해 접근 가능하다. 이 페이지에서는 사용 가능한 필드를 나열한다.
사용 가능한 파드 필드에 대한 정보는 fieldRef
를 통해 넘겨줄 수 있다.
API 레벨에서, 파드의 spec
은 항상 하나 이상의
컨테이너를 정의한다.
사용 가능한 컨테이너 필드에 대한 정보는
resourceFiledRef
를 통해 넘겨줄 수 있다.
fieldRef
를 통해 접근 가능한 정보
대부분의 파드 필드는 환경 변수로써,
또는 다운워드 API
볼륨을 사용하여 컨테이너에 제공할 수 있다.
이런 두 가지 방법을 통해 사용 가능한 필드는 다음과 같다.
metadata.name
- 파드의 이름
metadata.namespace
- 파드가 속한 네임스페이스
metadata.uid
- 파드의 고유 ID
metadata.annotations['<KEY>']
- 파드의 어노테이션에서
<KEY>
에 해당하는 값 (예를 들어,metadata.annotations['myannotation']
) metadata.labels['<KEY>']
- 파드의 레이블에서
<KEY>
에 해당하는 문자열 (예를 들어,metadata.labels['mylabel']
) spec.serviceAccountName
- 파드의 서비스 어카운트
spec.nodeName
- 파드가 실행중인 노드명
status.hostIP
- 파드가 할당된 노드의 기본 IP 주소
status.podIP
- 파드의 기본 IP 주소 (일반적으로 IPv4 주소)
추가적으로 아래 필드는 환경 변수가 아닌,
다운워드 API
볼륨의 fieldRef
로만 접근 가능하다.
metadata.labels
- 파드의 모든 레이블로, 한 줄마다 하나의 레이블을 갖는(
label-key="escaped-label-value"
) 형식을 취함 metadata.annotations
- 파드의 모든 어노테이션으로, 한 줄마다 하나의 어노테이션을 갖는(
annotation-key="escaped-annotation-value"
) 형식을 취함
resourceFieldRef
를 통해 접근 가능한 정보
컨테이너 필드는 CPU와 메모리 같은 리소스에 대한 요청 및 제한 값을 제공한다.
resource: limits.cpu
- 컨테이너의 CPU 제한
resource: requests.cpu
- 컨테이너의 CPU 요청
resource: limits.memory
- 컨테이너의 메모리 제한
resource: requests.memory
- 컨테이너의 메모리 요청
resource: limits.hugepages-*
- 컨테이너의 hugepage 제한 (
DownwardAPIHugePages
기능 게이트가 활성화 된 경우) resource: requests.hugepages-*
- 컨테이너의 hugepage 요청 (
DownwardAPIHugePages
기능 게이트가 활성화 된 경우) resource: limits.ephemeral-storage
- 컨테이너의 임시 스토리지 제한
resource: requests.ephemeral-storage
- 컨테이너의 임시 스토리지 요청
리소스 제한에 대한 참고 정보
컨테이너의 CPU와 메모리 제한을 명시하지 않고 다운워드 API로 이 정보들을 제공하려고 할 경우, kubelet은 기본적으로 노드의 할당 가능량에 기반하여 CPU와 메모리에 할당 가능한 최댓값을 노출시킨다.
다음 내용
자세한 정보는 다운워드API
볼륨를 참고한다.
다운워드 API를 사용하여 파드 및 컨테이너 정보를 노출시켜보자.
5.2 - 워크로드 리소스
5.2.1 - 디플로이먼트
디플로이먼트(Deployment) 는 파드와 레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공한다.
디플로이먼트에서 의도하는 상태 를 설명하고, 디플로이먼트 컨트롤러(Controller)는 현재 상태에서 의도하는 상태로 비율을 조정하며 변경한다. 새 레플리카셋을 생성하는 디플로이먼트를 정의하거나 기존 디플로이먼트를 제거하고, 모든 리소스를 새 디플로이먼트에 적용할 수 있다.
참고:
디플로이먼트가 소유하는 레플리카셋은 관리하지 말아야 한다. 사용자의 유스케이스가 다음에 포함되지 않는 경우 쿠버네티스 리포지터리에 이슈를 올릴 수 있다.유스케이스
다음은 디플로이먼트의 일반적인 유스케이스이다.
- 레플리카셋을 롤아웃 할 디플로이먼트 생성. 레플리카셋은 백그라운드에서 파드를 생성한다. 롤아웃 상태를 체크해서 성공 여부를 확인한다.
- 디플로이먼트의 PodTemplateSpec을 업데이트해서 파드의 새로운 상태를 선언한다. 새 레플리카셋이 생성되면, 디플로이먼트는 파드를 기존 레플리카셋에서 새로운 레플리카셋으로 속도를 제어하며 이동하는 것을 관리한다. 각각의 새로운 레플리카셋은 디플로이먼트의 수정 버전에 따라 업데이트한다.
- 만약 디플로이먼트의 현재 상태가 안정적이지 않은 경우 디플로이먼트의 이전 버전으로 롤백한다. 각 롤백은 디플로이먼트의 수정 버전에 따라 업데이트한다.
- 더 많은 로드를 위해 디플로이먼트의 스케일 업.
- 디플로이먼트 롤아웃 일시 중지로 PodTemplateSpec에 여러 수정 사항을 적용하고, 재개하여 새로운 롤아웃을 시작한다.
- 롤아웃이 막혀있는지를 나타내는 디플로이먼트 상태를 이용.
- 더 이상 필요 없는 이전 레플리카셋 정리.
디플로이먼트 생성
다음은 디플로이먼트의 예시이다. 예시는 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
)을 선택한다. 그러나 파드 템플릿 자체의 규칙이 만족되는 한, 보다 정교한 선택 규칙의 적용이 가능하다.참고:
.spec.selector.matchLabels
필드는 {key,value}의 쌍으로 매핑되어 있다.matchLabels
에 매핑된 단일 {key,value}은matchExpressions
의 요소에 해당하며,key
필드는 "key"에 그리고operator
는 "In"에 대응되며value
배열은 "value"만 포함한다. 매칭을 위해서는matchLabels
와matchExpressions
의 모든 요건이 충족되어야 한다.template
필드에는 다음 하위 필드가 포함되어 있다.- 파드는
.metadata.labels
필드를 사용해서app: nginx
라는 레이블을 붙인다. - 파드 템플릿의 사양 또는
.template.spec
필드는 파드가 도커 허브의nginx
1.14.2 버전 이미지를 실행하는nginx
컨테이너 1개를 실행하는 것을 나타낸다. - 컨테이너 1개를 생성하고,
.spec.template.spec.containers[0].name
필드를 사용해서nginx
이름을 붙인다.
- 파드는
시작하기 전에, 쿠버네티스 클러스터가 시작되고 실행 중인지 확인한다. 위의 디플로이먼트를 생성하려면 다음 단계를 따른다.
- 다음 명령어를 실행해서 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
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개인지 알 수 있다.
디플로이먼트의 롤아웃 상태를 보려면,
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 18s
디플로이먼트에서 3개의 레플리카가 생성되었고, 모든 레플리카는 최신 상태(최신 파드 템플릿을 포함)이며 사용 가능한 것을 알 수 있다.
디플로이먼트로 생성된 레플리카셋(
rs
)을 보려면,kubectl get rs
를 실행한다. 다음과 유사하게 출력된다.NAME DESIRED CURRENT READY AGE nginx-deployment-75675f5897 3 3 3 18s
레플리카셋의 출력에는 다음 필드가 표시된다.
NAME
은 네임스페이스에 있는 레플리카셋 이름의 목록이다.DESIRED
는 디플로이먼트의 생성 시 정의된 의도한 애플리케이션 레플리카 의 수를 표시한다. 이것이 의도한 상태 이다.CURRENT
는 현재 실행 중인 레플리카의 수를 표시한다.READY
는 사용자가 사용할 수 있는 애플리케이션의 레플리카의 수를 표시한다.AGE
는 애플리케이션의 실행된 시간을 표시한다.
레플리카셋의 이름은 항상
[DEPLOYMENT-NAME]-[HASH]
형식으로 된 것을 알 수 있다.HASH
문자열은 레플리카셋의pod-template-hash
레이블과 같다.각 파드에 자동으로 생성된 레이블을 보려면,
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
파드를 보장한다.
참고:
디플로이먼트에는 파드 템플릿 레이블과 적절한 셀렉터를 반드시 명시해야 한다
(이 예시에서는 app: nginx
).
레이블 또는 셀렉터는 다른 컨트롤러(다른 디플로이먼트와 스테이트풀셋(StatefulSet) 포함)와 겹치지 않아야 한다. 쿠버네티스는 겹치는 것을 막지 않으며, 만약 다중 컨트롤러가 겹치는 셀렉터를 가지는 경우 해당 컨트롤러의 충돌 또는 예기치 않은 동작을 야기할 수 있다.
Pod-template-hash 레이블
주의:
이 레이블은 변경하면 안 된다.pod-template-hash
레이블은 디플로이먼트 컨트롤러에 의해서 디플로이먼트가 생성 또는 채택한 모든 레플리카셋에 추가된다.
이 레이블은 디플로이먼트의 자식 레플리카셋이 겹치지 않도록 보장한다. 레플리카셋의 PodTemplate
을 해싱하고, 해시 결과를 레플리카셋 셀렉터,
파드 템플릿 레이블 및 레플리카셋 이 가질 수 있는 기존의 모든 파드에 레이블 값으로 추가해서 사용하도록 생성한다.
디플로이먼트 업데이트
참고:
디플로이먼트의 파드 템플릿(즉,.spec.template
)이 변경된 경우에만 디플로이먼트의 롤아웃이 트리거(trigger) 된다.
예를 들면 템플릿의 레이블이나 컨테이너 이미지가 업데이트된 경우이다. 디플로이먼트의 스케일링과 같은 다른 업데이트는 롤아웃을 트리거하지 말아야 한다.다음 단계에 따라 디플로이먼트를 업데이트한다.
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].image
를nginx:1.14.2
에서nginx:1.16.1
로 변경한다.kubectl edit deployment/nginx-deployment
다음과 유사하게 출력된다.
deployment.apps/nginx-deployment edited
롤아웃 상태를 보려면 다음을 실행한다.
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% 까지).
예를 들어, 위 디플로이먼트를 자세히 살펴보면 먼저 새로운 파드를 생성한 다음, 이전 파드를 삭제하고, 또 다른 새로운 파드를 만든 것을 볼 수 있다. 충분한 수의 새로운 파드가 나올 때까지 이전 파드를 죽이지 않으며, 충분한 수의 이전 파드들이 죽기 전까지 새로운 파드를 만들지 않는다. 이것은 최소 3개의 파드를 사용할 수 있게 하고, 최대 4개의 파드를 사용할 수 있게 한다. 디플로이먼트의 레플리카 크기가 4인 경우, 파드 숫자는 3개에서 5개 사이이다.
디플로이먼트의 세부 정보 가져오기
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개로 스케일 업하여 모든 시점에 대해 최소 3개 / 최대 3개의 파드가 존재하도록 하였다. 이후 지속해서 같은 롤링 업데이트 정책으로 새 레플리카셋은 스케일 업하고 이전 레플리카셋은 스케일 다운한다. 마지막으로 새로운 레플리카셋에 3개의 사용 가능한 레플리카가 구성되며, 이전 레플리카셋은 0개로 스케일 다운된다.
참고:
쿠버네티스가availableReplicas
수를 계산할 때 종료 중인(terminating) 파드는 포함하지 않으며,
이 수는 replicas - maxUnavailable
와 replicas + maxSurge
사이에 존재한다.
그 결과, 롤아웃 중에는 파드의 수가 예상보다 많을 수 있으며,
종료 중인 파드의 terminationGracePeriodSeconds
가 만료될 때까지는 디플로이먼트가 소비하는 총 리소스가 replicas + maxSurge
이상일 수 있다.롤오버(일명 인-플라이트 다중 업데이트)
디플로이먼트 컨트롤러는 각 시간마다 새로운 디플로이먼트에서 레플리카셋이
의도한 파드를 생성하고 띄우는 것을 주시한다. 만약 디플로이먼트가 업데이트되면, 기존 레플리카셋에서
.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개가
생성되는 것을 기다리지 않는다.
레이블 셀렉터 업데이트
일반적으로 레이블 셀렉터를 업데이트 하는 것을 권장하지 않으며 셀렉터를 미리 계획하는 것을 권장한다. 어떤 경우든 레이블 셀렉터의 업데이트를 해야하는 경우 매우 주의하고, 모든 영향을 파악했는지 확인해야 한다.
참고:
API 버전apps/v1
에서 디플로이먼트의 레이블 셀렉터는 생성 이후에는 변경할 수 없다.- 셀렉터 추가 시 디플로이먼트의 사양에 있는 파드 템플릿 레이블도 새 레이블로 업데이트해야 한다. 그렇지 않으면 유효성 검사 오류가 반환된다. 이 변경은 겹치지 않는 변경으로 새 셀렉터가 이전 셀렉터로 만든 레플리카셋과 파드를 선택하지 않게 되고, 그 결과로 모든 기존 레플리카셋은 고아가 되며, 새로운 레플리카셋을 생성하게 된다.
- 셀렉터 업데이트는 기존 셀렉터 키 값을 변경하며, 결과적으로 추가와 동일한 동작을 한다.
- 셀렉터 삭제는 디플로이먼트 셀렉터의 기존 키를 삭제하며 파드 템플릿 레이블의 변경을 필요로 하지 않는다. 기존 레플리카셋은 고아가 아니고, 새 레플리카셋은 생성되지 않는다. 그러나 제거된 레이블은 기존 파드와 레플리카셋에 여전히 존재한다는 점을 참고해야 한다.
디플로이먼트 롤백
때때로 디플로이먼트의 롤백을 원할 수도 있다. 예를 들어 디플로이먼트가 지속적인 충돌로 안정적이지 않은 경우. 기본적으로 모든 디플로이먼트의 롤아웃 기록은 시스템에 남아있어 언제든지 원할 때 롤백이 가능하다 (이 사항은 수정 기록에 대한 상한 수정을 통해서 변경할 수 있다).
참고:
디플로이먼트의 수정 버전은 디플로이먼트 롤아웃시 생성된다. 이는 디플로이먼트 파드 템플릿 (.spec.template
)이 변경되는 경우에만 새로운 수정 버전이 생성된다는 것을 의미한다.
예를 들어 템플릿의 레이블 또는 컨테이너 이미지를 업데이트 하는 경우.
디플로이먼트의 스케일링과 같은 다른 업데이트시 디플로이먼트 수정 버전은 생성되지 않으며 수동-스케일링 또는 자동-스케일링을 동시에 수행할 수 있다.
이는 이전 수정 버전으로 롤백을 하는 경우에 디플로이먼트 파드 템플릿 부분만
롤백된다는 것을 의미한다.디플로이먼트를 업데이트하는 동안 이미지 이름을
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-1564180365
과nginx-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
참고:
디플로이먼트 컨트롤러가 잘못된 롤아웃을 자동으로 중지하고, 새로운 레플리카셋의 스케일 업을 중지한다. 이는 지정한 롤링 업데이트의 파라미터(구체적으로 `maxUnavailable`)에 따라 달라진다. 쿠버네티스는 기본값으로 25%를 설정한다.
디플로이먼트에 대한 설명 보기
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
이 문제를 해결하려면 디플로이먼트를 안정적인 이전 수정 버전으로 롤백해야 한다.
디플로이먼트의 롤아웃 기록 확인
다음 순서에 따라 롤아웃 기록을 확인한다.
먼저 이 디플로이먼트의 수정 사항을 확인한다.
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"
로 주석을 단다. - 수동으로 리소스 매니페스트 편집.
- 디플로이먼트에
각 수정 버전의 세부 정보를 보려면 다음을 실행한다.
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로 롤백한다.
이제 현재 롤아웃의 실행 취소 및 이전 수정 버전으로 롤백 하기로 결정했다.
kubectl rollout undo deployment/nginx-deployment
이와 유사하게 출력된다.
deployment.apps/nginx-deployment rolled back
또는 특정 수정 버전으로 롤백하려면
--to-revision
옵션에 해당 수정 버전을 명시한다.kubectl rollout undo deployment/nginx-deployment --to-revision=2
이와 유사하게 출력된다.
deployment.apps/nginx-deployment rolled back
롤아웃 관련 명령에 대한 자세한 내용은
kubectl rollout
을 참조한다.이제 디플로이먼트가 이전 안정 수정 버전으로 롤백 된다. 버전 2로 롤백하기 위해
DeploymentRollback
이벤트가 디플로이먼트 컨트롤러에서 생성되는 것을 볼 수 있다.만약 롤백에 성공하고, 디플로이먼트가 예상대로 실행되는지 확인하려면 다음을 실행한다.
kubectl get deployment nginx-deployment
이와 유사하게 출력된다.
NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 30m
디플로이먼트의 설명 가져오기.
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
참고:
일시 중지된 디플로이먼트를 재개할 때까지 롤백할 수 없다.디플로이먼트 상태
디플로이먼트는 라이프사이클 동안 다양한 상태로 전환된다. 이는 새 레플리카셋을 롤아웃하는 동안 진행 중이 될 수 있고, 완료이거나 진행 실패일 수 있다.
디플로이먼트 진행 중
쿠버네티스는 다음 작업중 하나를 수행할 때 디플로이먼트를 진행 중 으로 표시한다.
- 디플로이먼트로 새 레플리카셋을 생성.
- 디플로이먼트로 새로운 레플리카셋을 스케일 업.
- 디플로이먼트로 기존 레플리카셋을 스케일 다운.
- 새 파드가 준비되거나 이용할 수 있음(최소 준비 시간(초) 동안 준비됨).
롤아웃이 "진행 중" 상태가 되면,
디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions
에 다음 속성을 포함하는 컨디션을 추가한다.
type: Progressing
status: "True"
reason: NewReplicaSetCreated
|reason: FoundNewReplicaSet
|reason: ReplicaSetUpdated
kubectl rollout status
를 사용해서 디플로이먼트의 진행사황을 모니터할 수 있다.
디플로이먼트 완료
쿠버네티스는 다음과 같은 특성을 가지게 되면 디플로이먼트를 완료 로 표시한다.
- 디플로이먼트과 관련된 모든 레플리카가 지정된 최신 버전으로 업데이트 되었을 때. 즉, 요청한 모든 업데이트가 완료되었을 때.
- 디플로이먼트와 관련한 모든 레플리카를 사용할 수 있을 때.
- 디플로이먼트에 대해 이전 복제본이 실행되고 있지 않을 때.
롤아웃이 "완료" 상태가 되면,
디플로이먼트 컨트롤러는 디플로이먼트의 .status.conditions
에 다음 속성을 포함하는 컨디션을 추가한다.
type: Progressing
status: "True"
reason: NewReplicaSetAvailable
이 Progressing
컨디션은 새로운 롤아웃이 시작되기 전까지는 "True"
상태값을 유지할 것이다.
레플리카의 가용성이 변경되는 경우에도(이 경우 Available
컨디션에 영향을 미침)
컨디션은 유지된다.
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
이 컨디션은 일찍 실패할 수도 있으며 이러한 경우 ReplicaSetCreateError
를 이유로 상태값을 "False"
로 설정한다.
또한, 디플로이먼트 롤아웃이 완료되면 데드라인은 더 이상 고려되지 않는다.
컨디션 상태에 대한 자세한 내용은 쿠버네티스 API 규칙을 참고한다.
참고:
쿠버네티스는reason: ProgressDeadlineExceeded
과 같은 상태 조건을
보고하는 것 이외에 정지된 디플로이먼트에 대해 조치를 취하지 않는다. 더 높은 수준의 오케스트레이터는 이를 활용할 수 있으며,
예를 들어 디플로이먼트를 이전 버전으로 롤백할 수 있다.참고:
만약 디플로이먼트 롤아웃을 일시 중지하면 쿠버네티스는 지정된 데드라인과 비교하여 진행 상황을 확인하지 않는다. 롤아웃 중에 디플로이먼트 롤아웃을 안전하게 일시 중지하고, 데드라인을 넘기도록 하는 조건을 트리거하지 않고 재개할 수 있다.설정한 타임아웃이 낮거나 일시적으로 처리될 수 있는 다른 종료의 에러로 인해 디플로이먼트에 일시적인 에러가 발생할 수 있다. 예를 들어, 할당량이 부족하다고 가정해보자. 만약 디플로이먼트를 설명하려면 다음 섹션을 확인한다.
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: "True"
와 reason: NewReplicaSetAvailable
).
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
type: Available
과 status: "True"
는 디플로이먼트가 최소한의 가용성을 가지고 있는 것을 의미한다.
최소한의 가용성은 디플로이먼트 계획에 명시된 파라미터에 의해 결정된다. type: Progressing
과 status: "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으로 되어 있다.
참고:
명시적으로 이 필드를 0으로 설정하면 그 결과로 디플로이먼트의 기록을 전부 초기화를 하고, 디플로이먼트는 롤백할 수 없게 된다.카나리 디플로이먼트
만약 디플로이먼트를 이용해서 일부 사용자 또는 서버에 릴리스를 롤아웃 하기 위해서는 리소스 관리에 설명된 카나리 패던에 따라 각 릴리스 마다 하나씩 여러 디플로이먼트를 생성할 수 있다.
디플로이먼트 사양 작성
다른 모든 쿠버네티스 설정과 마찬가지로 디플로이먼트에는 .apiVersion
, .kind
그리고 .metadata
필드가 필요하다.
설정 파일 작업에 대한 일반적인 내용은
애플리케이션 배포하기,
컨테이너 구성하기 그리고 kubectl을 사용해서 리소스 관리하기 문서를 참조한다.
디플로이먼트 오브젝트의 이름은 유효한
DNS 서브도메인 이름이어야 한다.
디플로이먼트에는 .spec
섹션도 필요하다.
파드 템플릿
.spec.template
과 .spec.selector
은 .spec
에서 유일한 필수 필드이다.
.spec.template
는 파드 템플릿이다. 이것은 파드와 정확하게 동일한 스키마를 가지고 있고, 중첩된 것을 제외하면 apiVersion
과 kind
를 가지고 있지 않는다.
파드에 필요한 필드 외에 디플로이먼트 파드 템플릿은 적절한 레이블과 적절한 재시작 정책을 명시해야 한다. 레이블의 경우 다른 컨트롤러와 겹치지 않도록 해야 한다. 자세한 것은 셀렉터를 참조한다.
.spec.template.spec.restartPolicy
에는 오직 Always
만 허용되고,
명시되지 않으면 기본값이 된다.
레플리카
.spec.replicas
은 필요한 파드의 수를 지정하는 선택적 필드이다. 이것의 기본값은 1이다.
예를 들어 kubectl scale deployment deployment --replicas=X
명령으로
디플로이먼트의 크기를 수동으로 조정한 뒤,
매니페스트를 이용하여 디플로이먼트를 업데이트하면(예: kubectl apply -f deployment.yaml
실행),
수동으로 설정했던 디플로이먼트의 크기가 오버라이드된다.
HorizontalPodAutoscaler(또는 수평 스케일링을 위한 유사 API)가
디플로이먼트 크기를 관리하고 있다면, .spec.replicas
를 설정해서는 안 된다.
대신, 쿠버네티스
컨트롤 플레인이
.spec.replicas
필드를 자동으로 관리한다.
셀렉터
.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
이면 파드를 롤링 업데이트
방식으로 업데이트 한다. maxUnavailable
와 maxSurge
를 명시해서
롤링 업데이트 프로세스를 제어할 수 있다.
최대 불가(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에 대한 변경 사항이 일시중지 된 경우 새 롤아웃을 트리거 하지 않는다.
디플로이먼트는 생성시 기본적으로 일시 중지되지 않는다.
다음 내용
- 파드에 대해 배운다.
- 디플로이먼트를 사용해서 상태를 유지하지 않는 애플리케이션을 구동한다.
Deployment
는 쿠버네티스 REST API에서 상위-수준 리소스이다. 디플로이먼트 API를 이해하기 위해서 Deployment 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
5.2.2 - 레플리카셋
레플리카셋의 목적은 레플리카 파드 집합의 실행을 항상 안정적으로 유지하는 것이다. 이처럼 레플리카셋은 보통 명시된 동일 파드 개수에 대한 가용성을 보증하는데 사용한다.
레플리카셋의 작동 방식
레플리카셋을 정의하는 필드는 획득 가능한 파드를 식별하는 방법이 명시된 셀렉터, 유지해야 하는 파드 개수를 명시하는 레플리카의 개수, 그리고 레플리카 수 유지를 위해 생성하는 신규 파드에 대한 데이터를 명시하는 파드 템플릿을 포함한다. 그러면 레플리카셋은 필드에 지정된 설정을 충족하기 위해 필요한 만큼 파드를 만들고 삭제한다. 레플리카셋이 새로운 파드를 생성해야 할 경우, 명시된 파드 템플릿을 사용한다.
레플리카셋은 파드의 metadata.ownerReferences 필드를 통해 파드에 연결되며, 이는 현재 오브젝트가 소유한 리소스를 명시한다. 레플리카셋이 가지고 있는 모든 파드의 ownerReferences 필드는 해당 파드를 소유한 레플리카셋을 식별하기 위한 소유자 정보를 가진다. 이 링크를 통해 레플리카셋은 자신이 유지하는 파드의 상태를 확인하고 이에 따라 관리 한다.
레플리카셋은 셀렉터를 이용해서 필요한 새 파드를 식별한다. 만약 파드에 OwnerReference가 없거나, OwnerReference가 컨트롤러(Controller) 가 아니고 레플리카셋의 셀렉터와 일치한다면 레플리카셋이 즉각 파드를 가지게 될 것이다.
레플리카셋을 사용하는 시기
레플리카셋은 지정된 수의 파드 레플리카가 항상 실행되도록 보장한다. 그러나 디플로이먼트는 레플리카셋을 관리하고 다른 유용한 기능과 함께 파드에 대한 선언적 업데이트를 제공하는 상위 개념이다. 따라서 별도의 사용자 정의(custom) 업데이트 오케스트레이션이 필요한 경우 또는 업데이트가 전혀 필요 없는 경우가 아니라면, 레플리카셋을 직접 사용하기보다는 디플로이먼트를 사용하는 것을 권장한다.
이는 레플리카셋 오브젝트를 직접 조작할 필요가 없다는 것을 의미한다. 대신 디플로이먼트를 이용하고 사양 부분에서 애플리케이션을 정의하면 된다.
예시
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
필드의 값은 항상 레플리카셋이다.
레플리카셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
레플리카셋도 .spec
섹션이 필요하다.
파드 템플릿
.spec.template
은 레이블을 붙이도록 되어 있는 파드 템플릿이다.
우리는 frontend.yaml
예제에서 tier: frontend
이라는 레이블을 하나 가지고 있다.
이 파드를 다른 컨트롤러가 취하지 않도록 다른 컨트롤러의 셀렉터와 겹치지 않도록 주의해야 한다.
템플릿의 재시작 정책 필드인
.spec.template.spec.restartPolicy
는 기본값인 Always
만 허용된다.
파드 셀렉터
.spec.selector
필드는 레이블 셀렉터이다.
앞서 논의한 것처럼 이 레이블은 소유될 가능성이 있는 파드를 식별하는데 사용된다.
우리 frontend.yaml
예제에서의 셀렉터는 다음과 같다.
matchLabels:
tier: frontend
레플리카셋에서 .spec.template.metadata.labels
는 spec.selector
과 일치해야 하며
그렇지 않으면 API에 의해 거부된다.
참고:
2개의 레플리카셋이 동일한.spec.selector
필드를 지정한 반면, 다른
.spec.template.metadata.labels
와 .spec.template.spec
필드를 명시한 경우, 각 레플리카셋은
다른 레플리카셋이 생성한 파드를 무시한다.레플리카
.spec.replicas
를 설정해서 동시에 동작하는 파드의 수를 지정할 수 있다.
레플리카셋은 파드의 수가 일치하도록 생성 및 삭제한다.
만약 .spec.replicas
를 지정하지 않으면 기본값은 1이다.
레플리카셋 작업
레플리카셋과 해당 파드 삭제
레플리카셋 및 모든 파드를 삭제하려면
kubectl delete
를 사용한다.
가비지 수집기는
기본적으로 종속되어 있는 모든 파드를 자동으로 삭제한다.
REST API 또는 client-go
라이브러리를 이용할 때는 -d 옵션으로 propagationPolicy
를
Background
또는 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"
레플리카셋만 삭제하기
kubectl delete
에
--cascade=orphan
옵션을 사용하여
연관 파드에 영향을 주지 않고 레플리카셋을 삭제할 수 있다.
REST API 또는 client-go
라이브러리를 이용할 때는 propagationPolicy
에 Orphan
을 설정해야 한다.
예시:
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
필드를 업데이트하면 된다.
레플리카셋 컨트롤러는 일치하는 레이블 셀렉터가 있는 파드가 의도한 수 만큼 가용하고 운영 가능하도록 보장한다.
스케일 다운할 때, 레플리카셋 컨트롤러는 스케일 다운할 파드의 우선순위를 정하기 위해 다음의 기준으로 가용 파드를 정렬하여 삭제할 파드를 결정한다.
- Pending 상태인 (+ 스케줄링할 수 없는) 파드가 먼저 스케일 다운된다.
controller.kubernetes.io/pod-deletion-cost
어노테이션이 설정되어 있는 파드에 대해서는, 낮은 값을 갖는 파드가 먼저 스케일 다운된다.- 더 많은 레플리카가 있는 노드의 파드가 더 적은 레플리카가 있는 노드의 파드보다 먼저 스케일 다운된다.
- 파드 생성 시간이 다르면, 더 최근에 생성된 파드가
이전에 생성된 파드보다 먼저 스케일 다운된다.
(
LogarithmicScaleDown
기능 게이트가 활성화되어 있으면 생성 시간이 정수 로그 스케일로 버킷화된다)
모든 기준에 대해 동등하다면, 스케일 다운할 파드가 임의로 선택된다.
파드 삭제 비용
Kubernetes v1.22 [beta]
controller.kubernetes.io/pod-deletion-cost
어노테이션을 이용하여,
레플리카셋을 스케일 다운할 때 어떤 파드부터 먼저 삭제할지에 대한 우선순위를 설정할 수 있다.
이 어노테이션은 파드에 설정되어야 하며, [-2147483647, 2147483647] 범위를 갖는다. 이 어노테이션은 하나의 레플리카셋에 있는 다른 파드와의 상대적 삭제 비용을 나타낸다. 삭제 비용이 낮은 파드는 삭제 비용이 높은 파드보다 삭제 우선순위가 높다.
파드에 대해 이 값을 명시하지 않으면 기본값은 0이다. 음수로도 설정할 수 있다. 유효하지 않은 값은 API 서버가 거부한다.
이 기능은 베타 상태이며 기본적으로 활성화되어 있다.
kube-apiserver와 kube-controller-manager에 대해 PodDeletionCost
기능 게이트를 이용하여 비활성화할 수 있다.
참고:
- 이 기능은 best-effort 방식으로 동작하므로, 파드 삭제 순서를 보장하지는 않는다.
- 이 값을 자주 바꾸는 것은 피해야 한다 (예: 메트릭 값에 따라 변경). apiserver에서 많은 양의 파드 업데이트를 동반하기 때문이다.
사용 예시
한 애플리케이션 내의 여러 파드는 각각 사용률이 다를 수 있다. 스케일 다운 시,
애플리케이션은 사용률이 낮은 파드를 먼저 삭제하고 싶을 수 있다. 파드를 자주
업데이트하는 것을 피하기 위해, 애플리케이션은 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과 같은 에이전트에게 위임한다.
잡
스스로 종료되는 것이 예상되는 파드의 경우에는 레플리카셋 대신
잡
을 이용한다 (즉, 배치 잡).
데몬셋
머신 모니터링 또는 머신 로깅과 같은 머신-레벨의 기능을 제공하는 파드를 위해서는 레플리카셋 대신
데몬셋
을 사용한다.
이러한 파드의 수명은 머신의 수명과 연관되어 있고, 머신에서 다른 파드가 시작하기 전에 실행되어야 하며,
머신의 재부팅/종료가 준비되었을 때, 해당 파드를 종료하는 것이 안전하다.
레플리케이션 컨트롤러
레플리카셋은 레플리케이션 컨트롤러를 계승하였다. 이 두 개의 용도는 동일하고, 유사하게 동작하며, 레플리케이션 컨트롤러가 레이블 사용자 가이드에 설명된 설정-기반의 셀렉터의 요건을 지원하지 않는다는 점을 제외하면 유사하다. 따라서 레플리카셋이 레플리케이션 컨트롤러보다 선호된다.
다음 내용
- 파드에 대해 배운다.
- 디플로이먼트에 대해 배운다.
- 레플리카셋에 의존해서 동작하는 디플로이먼트로 스테이트리스 애플리케이션을 실행한다.
ReplicaSet
는 쿠버네티스 REST API의 상위-수준 리소스이다. 레플리카셋 API에 대해 이해하기 위해 ReplicaSet 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
5.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 # .spec.template.metadata.labels 와 일치해야 한다
serviceName: "nginx"
replicas: 3 # 기본값은 1
minReadySeconds: 10 # 기본값은 0
template:
metadata:
labels:
app: nginx # .spec.selector.matchLabels 와 일치해야 한다
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.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
레이블과 일치하도록 설정해야 한다. 해당되는 파드 셀렉터를 찾지 못하면
스테이트풀셋 생성 과정에서 검증 오류가 발생한다.
볼륨 클레임 템플릿
.spec.volumeClaimTemplates
를 설정하여, 퍼시스턴트볼륨 프로비저너에 의해 프로비전된
퍼시스턴트볼륨을 이용하는 안정적인 스토리지를
제공할 수 있다.
최소 준비 시간 초
Kubernetes v1.25 [stable]
.spec.minReadySeconds
는 파드가 '사용 가능(available)'이라고 간주될 수 있도록 파드의 모든 컨테이너가
문제 없이 실행되고 준비되는 최소 시간(초)을 나타내는 선택적인 필드이다.
롤링 업데이트 전략을 사용할 때 롤아웃 진행 상황을 확인하는 데 사용된다.
이 필드의 기본값은 0이다(이 경우, 파드가 Ready 상태가 되면 바로 사용 가능하다고 간주된다.)
파드가 언제 사용 가능하다고 간주되는지에 대한 자세한 정보는 컨테이너 프로브(probe)를 참고한다.
파드 신원
스테이트풀셋 파드는 순서, 안정적인 네트워크 신원 그리고 안정적인 스토리지로 구성되는 고유한 신원을 가진다. 신원은 파드가 어떤 노드에 있고, (재)스케줄과도 상관없이 파드에 붙어있다.
순서 색인
N개의 레플리카가 있는 스테이트풀셋은 스테이트풀셋에 있는 각 파드에 0에서 N-1 까지의 정수가 순서대로 할당되며 해당 스테이트풀셋 내에서 고유 하다. 기본적으로 파드는 0부터 N-1까지의 순서대로 할당된다.
시작 순서
Kubernetes v1.26 [alpha]
.spec.ordinals
은 각 파드에 할당할 순서에 대한
정수값을 설정할 수 있게 해주는 선택적인 필드로, 기본값은 nil이다.
이 필드를 사용하기 위해서는
StatefulSetStartOrdinal
기능 게이트를 활성화해야 한다.
활성화 시, 다음과 같은 옵션들을 설정할 수 있다.
.spec.ordinals.start
: 만약.spec.ordinals.start
필드가 세팅되지 않을 경우, 파드는.spec.ordinals.start
부터.spec.ordinals.start + .spec.replicas - 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 항목마다, 각 파드는 하나의
PersistentVolumeClaim을 받는다. 위의 nginx 예시에서 각 파드는 my-storage-class
라는 스토리지 클래스와
1 Gib의 프로비전된 스토리지를 가지는 단일 퍼시스턴트 볼륨을 받게 된다. 만약 스토리지 클래스가
명시되지 않은 경우, 기본 스토리지 클래스가 사용된다. 파드가 노드에서 스케줄 혹은 재스케줄이 되면
파드의 volumeMounts
는 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨이 마운트 된다.
참고로, 파드 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨은
파드 또는 스테이트풀셋이 삭제되더라도 삭제되지 않는다.
이것은 반드시 수동으로 해야 한다.
파드 이름 레이블
스테이트풀셋 컨트롤러
가 파드를 생성할 때 파드 이름으로 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 상태가
되기 전까지 종료되지 않는다.
파드 관리 정책
스테이트풀셋의 .spec.podManagementPolicy
필드를 통해
고유성 및 신원 보증을 유지하면서 순차 보증을 완화한다.
OrderedReady 파드 관리
OrderedReady
파드 관리는 스테이트풀셋의 기본이다.
이것은 위에서 설명한 행위를 구현한다.
Parallel 파드 관리
Parallel
파드 관리는 스테이트풀셋 컨트롤러에게 모든 파드를
병렬로 실행 또는 종료하게 한다. 그리고 다른 파드의 실행이나
종료에 앞서 파드가 Running 및 Ready 상태가 되거나 완전히 종료되기를 기다리지 않는다.
이 옵션은 오직 스케일링 작업에 대한 동작에만 영향을 미친다. 업데이트는 영향을
받지 않는다.
업데이트 전략
스테이트풀셋의 .spec.updateStrategy
필드는 스테이트풀셋의
파드에 대한 컨테이너, 레이블, 리소스의 요청/제한 그리고 주석에 대한 자동화된 롤링 업데이트를
구성하거나 비활성화할 수 있다. 두 가지 가능한 전략이 있다.
OnDelete
(삭제시)- 스테이트풀셋의
.spec.updateStrategy.type
은OnDelete
를 설정하며, 스테이트풀셋 컨트롤러는 스테이트풀셋의 파드를 자동으로 업데이트하지 않는다. 사용자는 컨트롤러가 스테이트풀셋의.spec.template
를 반영하는 수정된 새로운 파드를 생성하도록 수동으로 파드를 삭제해야 한다. RollingUpdate
(롤링 업데이트)RollingUpdate
업데이트 전략은 스테이트풀셋의 파드에 대한 롤링 업데이트를 구현한다. 기본 업데이트 전략이다.
롤링 업데이트
스테이트풀셋에 롤링 업데이트
가 .spec.updateStrategy.type
에 설정되면
스테이트풀셋 컨트롤러는 스테이트풀셋의 각 파드를 삭제 및 재생성한다. 이 과정에서 똑같이
순차적으로 파드가 종료되고(가장 큰 순서 색인에서부터에서 작은 순서 색인쪽으로),
각 파드의 업데이트는 한 번에 하나씩 한다.
쿠버네티스 컨트롤 플레인은 이전 버전을 업데이트 하기 전에, 업데이트된 파드가 실행 및 준비될 때까지 기다린다.
.spec.minReadySeconds
(최소 준비 시간 초 참조)를
설정한 경우,
컨트롤 플레인은 파드가 준비 상태로 전환된 후 해당 시간을 추가로 기다린 후 이동한다.
파티션 롤링 업데이트
롤링 업데이트
의 업데이트 전략은 .spec.updateStrategy.rollingUpdate.partition
를 명시해서 파티션 할 수 있다. 만약 파티션을 명시하면 스테이트풀셋의 .spec.template
가
업데이트 될 때 부여된 수가 파티션보다 크거나 같은 모든 파드가 업데이트 된다.
파티션보다 작은 수를 가진 모든 파드는 업데이트 되지 않으며,
삭제 된 경우라도 이전 버전에서 재생성된다.
만약 스테이트풀셋의 .spec.updateStrategy.rollingUpdate.partition
이
.spec.replicas
보다 큰 경우 .spec.template
의 업데이트는 해당 파드에 전달하지 않는다.
대부분의 케이스는 파티션을 사용할 필요가 없지만 업데이트를 준비하거나,
카나리의 롤 아웃 또는 단계적인 롤 아웃을 행하려는 경우에는 유용하다.
최대 사용 불가능(unavailable) 파드 수
Kubernetes v1.24 [alpha]
.spec.updateStrategy.rollingUpdate.maxUnavailable
필드를 명시하여,
업데이트 과정에서 사용 불가능(unavailable) 파드를 최대 몇 개까지 허용할 것인지를 조절할 수 있다.
값은 절대값(예: 5
) 또는 목표 파드 퍼센티지(예: 10%
)로 명시할 수 있다.
절대값은 퍼센티지 값으로 계산한 뒤 올림하여 얻는다.
이 필드는 0일 수 없다. 기본값은 1이다.
이 필드는 0
에서 replicas - 1
사이 범위에 있는 모든 파드에 적용된다.
이 범위 내에 사용 불가능한 파드가 있으면,
maxUnavailable
로 집계된다.
강제 롤백
기본 파드 관리 정책 (OrderedReady
)과
함께 롤링 업데이트를 사용할 경우
직접 수동으로 복구를 해야하는 고장난 상태가 될 수 있다.
만약 파드 템플릿을 Running 및 Ready 상태가 되지 않는 구성으로 업데이트하는 경우(예시: 잘못된 바이너리 또는 애플리케이션-레벨 구성 오류로 인한) 스테이트풀셋은 롤아웃을 중지하고 기다린다.
이 상태에서는 파드 템플릿을 올바른 구성으로 되돌리는 것으로 충분하지 않다. 알려진 이슈로 인해 스테이트풀셋은 손상된 파드가 준비(절대 되지 않음)될 때까지 기다리며 작동하는 구성으로 되돌아가는 시도를 하기 전까지 기다린다.
템플릿을 되돌린 이후에는 스테이트풀셋이 이미 잘못된 구성으로 실행하려고 시도한 모든 파드를 삭제해야 한다. 그러면 스테이트풀셋은 되돌린 템플릿을 사용해서 파드를 다시 생성하기 시작한다.
퍼시스턴트볼륨클레임 유보
Kubernetes v1.23 [alpha]
선택적 필드인 .spec.persistentVolumeClaimRetentionPolicy
는
스테이트풀셋의 생애주기동안 PVC를 삭제할 것인지,
삭제한다면 어떻게 삭제하는지를 관리한다.
이 필드를 사용하려면 API 서버와 컨트롤러 매니저에 StatefulSetAutoDeletePVC
기능 게이트를 활성화해야 한다.
활성화 시, 각 스테이트풀셋에 대해 두 가지 정책을 설정할 수 있다.
whenDeleted
- 스테이트풀셋이 삭제될 때 적용될 볼륨 유보 동작을 설정한다.
whenScaled
- 스테이트풀셋의 레플리카 수가 줄어들 때, 예를 들면 스테이트풀셋을 스케일 다운할 때 적용될 볼륨 유보 동작을 설정한다.
설정 가능한 각 정책에 대해, 그 값을 Delete
또는 Retain
으로 설정할 수 있다.
Delete
volumeClaimTemplate
스테이트풀셋으로부터 생성된 PVC는 정책에 영향을 받는 각 파드에 대해 삭제된다.whenDeleted
가 이 값으로 설정되어 있으면volumeClaimTemplate
으로부터 생성된 모든 PVC는 파드가 삭제된 뒤에 삭제된다.whenScaled
가 이 값으로 설정되어 있으면 스케일 다운된 파드 레플리카가 삭제된 뒤, 삭제된 파드에 해당되는 PVC만 삭제된다.Retain
(기본값)- 파드가 삭제되어도
volumeClaimTemplate
으로부터 생성된 PVC는 영향을 받지 않는다. 이는 이 신기능이 도입되기 전의 기본 동작이다.
이러한 정책은 파드의 삭제가 스테이트풀셋 삭제 또는 스케일 다운으로 인한 것일 때에만 적용됨에 유의한다. 예를 들어, 스테이트풀셋의 파드가 노드 실패로 인해 실패했고, 컨트롤 플레인이 대체 파드를 생성했다면, 스테이트풀셋은 기존 PVC를 유지한다. 기존 볼륨은 영향을 받지 않으며, 새 파드가 실행될 노드에 클러스터가 볼륨을 연결(attach)한다.
정책의 기본값은 Retain
이며, 이는 이 신기능이 도입되기 전의 스테이트풀셋 기본 동작이다.
다음은 정책 예시이다.
apiVersion: apps/v1
kind: StatefulSet
...
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Delete
...
스테이트풀셋 컨트롤러는 자신이 소유한 PVC에
소유자 정보(reference)를
추가하며, 파드가 종료된 이후에는 가비지 콜렉터가 이 정보를 삭제한다.
이로 인해 PVC가 삭제되기 전에
(그리고 유보 정책에 따라, 매칭되는 기반 PV와 볼륨이 삭제되기 전에)
파드가 모든 볼륨을 깨끗하게 언마운트할 수 있다.
whenDeleted
정책을 Delete
로 설정하면,
해당 스테이트풀셋에 연결된 모든 PVC에 스테이트풀셋 인스턴스의 소유자 정보가 기록된다.
whenScaled
정책은 파드가 스케일 다운되었을 때에만 PVC를 삭제하며,
파드가 다른 원인으로 삭제되면 PVC를 삭제하지 않는다.
조정 상황 발생 시, 스테이트풀셋 컨트롤러는 목표 레플리카 수와 클러스터 상의 실제 파드 수를 비교한다.
레플리카 카운트보다 큰 ID를 갖는 스테이트풀셋 파드는 부적격 판정을 받으며 삭제 대상으로 표시된다.
whenScaled
정책이 Delete
이면, 부적격 파드는 삭제되기 전에,
연결된 스테이트풀셋 템플릿 PVC의 소유자로 지정된다.
이로 인해, 부적격 파드가 종료된 이후에만 PVC가 가비지 콜렉트된다.
이는 곧 만약 컨트롤러가 강제 종료되어 재시작되면, 파드의 소유자 정보가 정책에 적합하게 업데이트되기 전에는 어떤 파드도 삭제되지 않을 것임을 의미한다. 만약 컨트롤러가 다운된 동안 부적격 파드가 강제로 삭제되면, 컨트롤러가 강제 종료된 시점에 따라 소유자 정보가 설정되었을 수도 있고 설정되지 않았을 수도 있다. 소유자 정보가 업데이트되기까지 몇 번의 조정 절차가 필요할 수 있으며, 따라서 일부 부적격 파드는 소유자 정보 설정을 완료하고 나머지는 그러지 못했을 수 있다. 이러한 이유로, 컨트롤러가 다시 켜져서 파드를 종료하기 전에 소유자 정보를 검증할 때까지 기다리는 것을 추천한다. 이것이 불가능하다면, 관리자는 PVC의 소유자 정보를 확인하여 파드가 강제 삭제되었을 때 해당되는 오브젝트가 삭제되도록 해야 한다.
레플리카
.spec.replicas
은 필요한 파드의 수를 지정하는 선택적 필드이다. 기본값은 1이다.
예를 들어 kubectl scale deployment deployment --replicas=X
명령으로
디플로이먼트의 크기를 수동으로 조정한 뒤,
매니페스트를 이용하여 디플로이먼트를 업데이트하면(예: kubectl apply -f deployment.yaml
실행),
수동으로 설정했던 디플로이먼트의 크기가
오버라이드된다.
HorizontalPodAutoscaler(또는 수평 스케일링을 위한 유사 API)가
디플로이먼트 크기를 관리하고 있다면, .spec.replicas
를 설정해서는 안 된다.
대신, 쿠버네티스
컨트롤 플레인이
.spec.replicas
필드를 자동으로 관리한다.
다음 내용
- 파드에 대해 배운다.
- 스테이트풀셋을 사용하는 방법을 알아본다.
- 스테이트풀셋 애플리케이션 배포 예제를 따라한다.
- 스테이트풀셋으로 카산드라 배포 예제를 따라한다.
- 복제된 스테이트풀셋 애플리케이션 구동하기 예제를 따라한다.
- 스테이트풀셋 확장하기에 대해 배운다.
- 스테이트풀셋을 삭제하면 어떤 일이 수반되는지를 배운다.
- 스토리지의 볼륨을 사용하는 파드 구성을 하는 방법을 배운다.
- 스토리지로 퍼시스턴트볼륨(PersistentVolume)을 사용하도록 파드 설정하는 방법을 배운다.
StatefulSet
은 쿠버네티스 REST API의 상위-수준 리소스이다. 스테이트풀셋 API에 대해 이해하기 위해 StatefulSet 오브젝트 정의를 읽는다.- PodDisruptionBudget과 이를 사용해서 어떻게 중단 중에 애플리케이션 가용성을 관리할 수 있는지에 대해 읽는다.
5.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:
# 이 톨러레이션(toleration)은 데몬셋이 컨트롤 플레인 노드에서 실행될 수 있도록 만든다.
# 컨트롤 플레인 노드가 이 파드를 실행해서는 안 되는 경우, 이 톨러레이션을 제거한다.
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- 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
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
YAML 파일을 기반으로 데몬셋을 생성한다.
kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml
필수 필드
다른 모든 쿠버네티스 설정과 마찬가지로 데몬셋에는 apiVersion
, kind
그리고 metadata
필드가 필요하다.
일반적인 설정파일 작업에 대한 정보는
스테이트리스 애플리케이션 실행하기와
kubectl을 사용한 오브젝트 관리를 참고한다.
데몬셋 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
데몬셋에는
.spec
섹션도 필요하다.
파드 템플릿
.spec.template
는 .spec
의 필수 필드 중 하나이다.
.spec.template
는 파드 템플릿이다.
이것은 중첩되어 있다는 점과 apiVersion
또는 kind
를 가지지 않는 것을 제외하면
파드와 정확히 같은 스키마를 가진다.
데몬셋의 파드 템플릿에는 파드의 필수 필드 외에도 적절한 레이블이 명시되어야 한다(파드 셀렉터를 본다).
데몬셋의 파드 템플릿의 RestartPolicy
는 Always
를 가져야 하며,
명시되지 않은 경우 기본으로 Always
가 된다.
파드 셀렉터
.spec.selector
필드는 파드 셀렉터이다. 이것은
잡의 .spec.selector
와 같은 동작을 한다.
.spec.template
의 레이블과 매치되는
파드 셀렉터를 명시해야 한다.
또한, 한 번 데몬셋이 만들어지면
.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
를 명시하면
데몬셋 컨트롤러는 노드 어피니티와 일치하는 노드에 파드를 생성한다.
만약 둘 중 하나를 명시하지 않으면 데몬셋 컨트롤러는 모든 노드에서 파드를 생성한다.
데몬 파드가 스케줄 되는 방법
기본 스케줄러로 스케줄
Kubernetes 1.17 [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)하는 경우에 유용하다. 또한 스태틱 파드는 향후에 사용 중단될 수 있다.
디플로이먼트
데몬셋은 파드를 생성한다는 점에서 디플로이먼트와 유사하고, 해당 파드에서는 프로세스가 종료되지 않을 것으로 예상한다(예: 웹 서버).
파드가 실행되는 호스트를 정확하게 제어하는 것보다 레플리카의 수를 스케일링 업 및 다운 하고, 업데이트 롤아웃이 더 중요한 프런트 엔드와 같은 것은 스테이트리스 서비스의 디플로이먼트를 사용한다. 데몬셋이 특정 노드에서 다른 파드가 올바르게 실행되도록 하는 노드 수준 기능을 제공한다면, 파드 사본이 항상 모든 호스트 또는 특정 호스트에서 실행되는 것이 중요한 경우에 데몬셋을 사용한다.
예를 들어, 네트워크 플러그인은 데몬셋으로 실행되는 컴포넌트를 포함할 수 있다. 데몬셋 컴포넌트는 작동 중인 노드가 정상적인 클러스터 네트워킹을 할 수 있도록 한다.
다음 내용
- 파드에 대해 배운다.
- 데몬셋을 어떻게 사용하는지 알아본다.
- 데몬셋 롤링 업데이트 수행하기
- 데몬셋 롤백하기 (예를 들어, 롤 아웃이 예상대로 동작하지 않은 경우).
- 쿠버네티스가 파드를 노드에 할당하는 방법을 이해한다.
- 데몬셋으로 구동되곤 하는, 디바이스 플러그인과 애드온에 대해 배운다.
DaemonSet
은 쿠버네티스 REST API에서 상위-수준 리소스이다. 데몬셋 API에 대해 이해하기 위해 DaemonSet 오브젝트 정의를 읽는다.
5.2.5 - 잡
잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료될 때까지 계속해서 파드의 실행을 재시도한다. 파드가 성공적으로 완료되면, 성공적으로 완료된 잡을 추적한다. 지정된 수의 성공 완료에 도달하면, 작업(즉, 잡)이 완료된다. 잡을 삭제하면 잡이 생성한 파드가 정리된다. 작업을 일시 중지하면 작업이 다시 재개될 때까지 활성 파드가 삭제된다.
간단한 사례는 잡 오브젝트를 하나 생성해서 파드 하나를 안정적으로 실행하고 완료하는 것이다. 첫 번째 파드가 실패 또는 삭제된 경우(예로는 노드 하드웨어의 실패 또는 노드 재부팅) 잡 오브젝트는 새로운 파드를 기동시킨다.
잡을 사용하면 여러 파드를 병렬로 실행할 수도 있다.
잡을 스케줄에 따라 구동하고 싶은 경우(단일 작업이든, 여러 작업의 병렬 수행이든), 크론잡(CronJob)을 참고한다.
예시 잡 실행하기
다음은 잡 설정 예시이다. 예시는 파이(π)의 2000 자리까지 계산해서 출력한다. 이를 완료하는 데 약 10초가 소요된다.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
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
을 사용해서 잡 상태를 확인한다.
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:5.34.0
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
apiVersion: batch/v1
kind: Job
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":{"spec":{"containers":[{"command":["perl","-Mbignum=bpi","-wle","print bpi(2000)"],"image":"perl","name":"pi"}],"restartPolicy":"Never"}}}}
creationTimestamp: "2022-06-15T08:40:15Z"
generation: 1
labels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
job-name: pi
name: pi
namespace: default
resourceVersion: "987"
uid: 863452e6-270d-420e-9b94-53a54146c223
spec:
backoffLimit: 4
completionMode: NonIndexed
completions: 1
parallelism: 1
selector:
matchLabels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
suspend: false
template:
metadata:
creationTimestamp: null
labels:
controller-uid: 863452e6-270d-420e-9b94-53a54146c223
job-name: pi
spec:
containers:
- command:
- perl
- -Mbignum=bpi
- -wle
- print bpi(2000)
image: perl:5.34.0
imagePullPolicy: Always
name: pi
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
active: 1
ready: 1
startTime: "2022-06-15T08:40:15Z"
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
필드는 선택 사항이다. 대부분의 케이스에서 지정해서는 안된다.
자신의 파드 셀렉터를 지정하기 섹션을 참고한다.
잡에 대한 병렬 실행
잡으로 실행하기에 적합한 작업 유형은 크게 세 가지가 있다.
- 비-병렬(Non-parallel) 잡:
- 일반적으로, 파드가 실패하지 않은 한, 하나의 파드만 시작된다.
- 파드가 성공적으로 종료하자마자 즉시 잡이 완료된다.
- 고정적(fixed)인 완료 횟수 를 가진 병렬 잡:
.spec.completions
에 0이 아닌 양수 값을 지정한다.- 잡은 전체 작업을 나타내며,
.spec.completions
성공한 파드가 있을 때 완료된다. .spec.completionMode="Indexed"
를 사용할 때, 각 파드는 0에서.spec.completions-1
범위 내의 서로 다른 인덱스를 가져온다.
- 작업 큐(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) 종료되면, 중지하는데 시간이 소요된다.
완료 모드
Kubernetes v1.24 [stable]
완료 횟수가 고정적인 완료 횟수 즉, 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.backoffLimit
제한값으로 계산되며,
자세한 내용은 파드 백오프(backoff) 실패 정책을 확인한다. 그러나,
잡의 파드 실패 정책을 설정하여 파드의 실패 수준을 조절하여 사용할 수도 있다.
.spec.parallelism = 1
, .spec.completions = 1
그리고
.spec.template.spec.restartPolicy = "Never"
를 지정하더라도 같은 프로그램을
두 번 시작하는 경우가 있다는 점을 참고한다.
.spec.parallelism
그리고 .spec.completions
를 모두 1보다 크게 지정한다면 한번에
여러 개의 파드가 실행될 수 있다. 따라서 파드는 동시성에 대해서도 관대(tolerant)해야 한다.
만약 기능 게이트인
PodDisruptionConditions
와 JobPodFailurePolicy
가 모두 활성화되어 있고,
.spec.podFailurePolicy
필드가 설정되어 있다면, 잡 컨트롤러는 종료 중인
파드(.metadata.deletionTimestamp
필드가 설정된 파드)가 종료 상태(.status.phase
값이 Failed
혹은 Succeeded
)가 될 때 까지
해당 파드를 실패로 간주하지 않는다. 그러나, 잡 컨트롤러는
파드가 명백히 종료되었다고 판단하면 곧바로 대체 파드를 생성한다.
파드가 한 번 종료되면, 잡 컨트롤러는 방금 종료된 파드를 고려하여
관련 작업에 대해 .backoffLimit
과 .podFailurePolicy
를 평가한다.
이러한 조건들이 하나라도 충족되지 않을 경우, 잡 컨트롤러는
종료 중인 파드가 추후 phase: "Succeded"
로 종료된다고 할지라도,
해당 파드를 실패한 파드로 즉시 간주한다.
파드 백오프(backoff) 실패 정책
구성에 논리적 오류가 포함되어 있어서 몇 회의 재시도 이후에
잡이 실패되도록 만들어야 하는 경우가 있다.
이렇게 하려면 .spec.backoffLimit
의 값에
재시도(잡을 실패로 처리하기 이전까지) 횟수를 설정한다. 백오프 제한은 기본적으로 6으로 설정되어 있다.
잡에 연계된 실패 상태 파드는 6분 내에서 지수적으로 증가하는
백-오프 지연(10초, 20초, 40초 ...)을 적용하여, 잡 컨트롤러에 의해 재생성된다.
재시도 횟수는 다음 두 가지 방법으로 계산된다.
.status.phase = "Failed"
인 파드의 수.restartPolicy = "OnFailure"
를 사용하는 경우,.status.phase
가Pending
이거나Running
인 파드들이 가지고 있는 모든 컨테이너의 수.
계산 중 하나가 .spec.backoffLimit
에 도달하면, 잡이
실패한 것으로 간주한다.
참고:
만약 잡에restartPolicy = "OnFailure"
가 있는 경우 잡 백오프 한계에
도달하면 잡을 실행 중인 파드가 종료된다. 이로 인해 잡 실행 파일의 디버깅이
더 어려워질 수 있다. 디버깅하거나 로깅 시스템을 사용해서 실패한 작업의 결과를 실수로 손실되지 않도록
하려면 restartPolicy = "Never"
로 설정하는 것을 권장한다.잡의 종료와 정리
잡이 완료되면 파드가 더 이상 생성되지도 않지만, 일반적으로는 삭제되지도 않는다.
이를 유지하면
완료된 파드의 로그를 계속 보며 에러, 경고 또는 다른 기타 진단 출력을 확인할 수 있다.
잡 오브젝트는 완료된 후에도 상태를 볼 수 있도록 남아 있다. 상태를 확인한 후 이전 잡을 삭제하는 것은 사용자의 몫이다.
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:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
잡의 사양과 잡의 파드 템플릿 사양에는 모두 activeDeadlineSeconds
필드가 있다는 점을 참고한다. 이 필드를 적절한 레벨로 설정해야 한다.
restartPolicy
는 잡 자체에 적용되는 것이 아니라 파드에 적용된다는 점을 유념한다. 잡의 상태가 type: Failed
이 되면, 잡의 자동 재시작은 없다.
즉, .spec.activeDeadlineSeconds
와 .spec.backoffLimit
로 활성화된 잡의 종료 메커니즘은 영구적인 잡의 실패를 유발하며 이를 해결하기 위해 수동 개입이 필요하다.
완료된 잡을 자동으로 정리
완료된 잡은 일반적으로 시스템에서 더 이상 필요로 하지 않는다. 시스템 내에 이를 유지한다면 API 서버에 부담이 된다. 만약 크론잡과 같은 상위 레벨 컨트롤러가 잡을 직접 관리하는 경우, 지정된 용량 기반 정리 정책에 따라 크론잡이 잡을 정리할 수 있다.
완료된 잡을 위한 TTL 메커니즘
Kubernetes v1.23 [stable]
완료된 잡 (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:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
pi-with-ttl
잡은 완료 후 100
초 이후에
자동으로 삭제될 수 있다.
만약 필드를 0
으로 설정하면, 잡이 완료된 직후에 자동으로
삭제되도록 할 수 있다. 만약 필드를 설정하지 않으면, 이 잡이 완료된
후에 TTL 컨트롤러에 의해 정리되지 않는다.
참고:
ttlSecondsAfterFinished
필드를 설정하는 것을 권장하는데,
이는 관리되지 않는 잡(직접 생성한,
크론잡 등 다른 워크로드 API를 통해 간접적으로 생성하지 않은 잡)의
기본 삭제 정책이 orphanDependents
(관리되지 않는 잡이 완전히 삭제되어도
해당 잡에 의해 생성된 파드를 남겨둠)이기 때문이다.
삭제된 잡의 파드가 실패하거나 완료된 뒤
컨트롤 플레인이 언젠가
가비지 콜렉션을 한다고 해도,
이렇게 남아 있는 파드는 클러스터의 성능을 저하시키거나
최악의 경우에는 이 성능 저하로 인해 클러스터가 중단될 수도 있다.
리밋 레인지(Limit Range)와 리소스 쿼터를 사용하여 특정 네임스페이스가 사용할 수 있는 자원량을 제한할 수 있다.
잡 패턴
잡 오브젝트를 사용해서 신뢰할 수 있는 파드의 병렬 실행을 지원할 수 있다. 잡 오브젝트는 과학 컴퓨팅(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이어야 함 |
파드 간 통신을 위한 잡 | W | W |
고급 사용법
잡 일시 중지
Kubernetes v1.24 [stable]
잡이 생성되면, 잡 컨트롤러는 잡의 요구 사항을 충족하기 위해 즉시 파드 생성을 시작하고 잡이 완료될 때까지 계속한다. 그러나, 잡의 실행을 일시적으로 중단하고 나중에 재개하거나, 잡을 중단 상태로 생성하고 언제 시작할지를 커스텀 컨트롤러가 나중에 결정하도록 하고 싶을 수도 있다.
잡을 일시 중지하려면, 잡의 .spec.suspend
필드를 true로
업데이트할 수 있다. 이후에, 다시 재개하려면, false로 업데이트한다.
.spec.suspend
로 설정된 잡을 생성하면 일시 중지된 상태로
생성된다.
잡이 일시 중지에서 재개되면, 해당 .status.startTime
필드가
현재 시간으로 재설정된다. 즉, 잡이 일시 중지 및 재개되면 .spec.activeDeadlineSeconds
타이머가 중지되고 재설정된다.
잡을 일시 중지하면, Completed
상태가 아닌 모든 실행중인 파드가 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 patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'
일시 중지된 잡 재개
kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'
잡의 상태를 사용하여 잡이 일시 중지되었는지 또는 과거에 일시 중지되었는지 확인할 수 있다.
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
필드를 전환한 결과이다. 이 두 이벤트 사이의 시간동안
파드가 생성되지 않았지만, 잡이 재개되자마자 파드 생성이 다시
시작되었음을 알 수 있다.
가변적 스케줄링 지시
Kubernetes v1.23 [beta]
병렬 잡에서 대부분의 경우 파드를 특정 제약 조건 하에서 실행하고 싶을 것이다. (예를 들면 동일 존에서 실행하거나, 또는 GPU 모델 x 또는 y를 사용하지만 둘을 혼합하지는 않는 등)
suspend 필드는 이러한 목적을 달성하기 위한 첫 번째 단계이다. 이 필드를 사용하면 커스텀 큐(queue) 컨트롤러가 잡이 언제 시작될지를 결정할 수 있다. 그러나, 잡이 재개된 이후에는, 커스텀 큐 컨트롤러는 잡의 파드가 실제로 어디에 할당되는지에 대해서는 영향을 주지 않는다.
이 기능을 이용하여 잡이 실행되기 전에 잡의 스케줄링 지시를 업데이트할 수 있으며, 이를 통해 커스텀 큐 컨트롤러가 파드 배치에 영향을 줌과 동시에 노드로의 파드 실제 할당 작업을 kube-scheduler로부터 경감시켜 줄 수 있도록 한다. 이는 이전에 재개된 적이 없는 중지된 잡에 대해서만 허용된다.
잡의 파드 템플릿 필드 중, 노드 어피니티(node affinity), 노드 셀렉터(node selector), 톨러레이션(toleration), 레이블(label), 어노테이션(annotation)은 업데이트가 가능하다.
자신의 파드 셀렉터를 지정하기
일반적으로 잡 오브젝트를 생성할 때 .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
를 설정하면 시스템에게 사용자가 무엇을 하는지 알고 있으며
이런 불일치를 허용한다고 알릴 수 있다.
파드 실패 정책
Kubernetes v1.26 [beta]
참고:
잡(Job)에 대한 파드 실패 정책은JobPodFailurePolicy
기능 게이트가
클러스터에서 활성화됐을 경우에만 구성할 수 있다. 추가적으로,
파드 장애 정책의 파드 중단 조건 (참조:
파드 중단 조건)을
감지하고 처리할 수 있도록 PodDisruptionConditions
기능 게이트를 활성화하는 것을 권장한다. 두 기능 게이트 모두
쿠버네티스 1.31에서 사용할 수 있다..spec.podFailurePolicy
필드로 정의되는 파드 실패 정책은, 클러스터가
컨테이너 종료 코드와 파드 상태를 기반으로 파드의 실패를
처리하도록 활성화한다.
어떤 상황에서는, 파드의 실패를 처리할 때 잡(Job)의 .spec.backoffLimit
을 기반으로 하는
파드 백오프(backoff) 실패 정책에서
제공하는 제어보다 더 나은 제어를 원할 수 있다. 다음은 사용 사례의 몇 가지 예시다.
- 불필요한 파드 재시작을 방지하여 워크로드 실행 비용을 최적화하려면, 파드 중 하나가 소프트웨어 버그를 나타내는 종료 코드와 함께 실패하는 즉시 잡을 종료할 수 있다.
- 중단이 있더라도 잡이 완료되도록 하려면,
중단(예: 선점(preemption),
API를 이용한 축출(API-initiated Eviction)
또는 축출 기반 테인트(Taints))으로 인한
파드 실패를 무시하여
.spec.backoffLimit
재시도 한도에 포함되지 않도록 할 수 있다.
위의 사용 사례를 충족하기 위해
.spec.podFailurePolicy
필드에 파드 실패 정책을 구성할 수 있다.
이 정책은 컨테이너 종료 코드 및 파드 상태를 기반으로 파드 실패를 처리할 수 있다.
다음은 podFailurePolicy
를 정의하는 잡의 매니페스트이다.
apiVersion: batch/v1
kind: Job
metadata:
name: job-pod-failure-policy-example
spec:
completions: 12
parallelism: 3
template:
spec:
restartPolicy: Never
containers:
- name: main
image: docker.io/library/bash:5
command: ["bash"] # FailJob 액션을 트리거시키는 버그를 만들어내는 예제 명령어
args:
- -c
- echo "Hello world!" && sleep 5 && exit 42
backoffLimit: 6
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
containerName: main # 선택 사항
operator: In # [In / NotIn] 중 하나
values: [42]
- action: Ignore # [Ignore, FailJob, Count] 중 하나
onPodConditions:
- type: DisruptionTarget # 파드의 중단을 나타냄
위 예시에서, 파드 실패 정책의 첫 번째 규칙은 main
컨테이너가 42 종료코드와
함께 실패하면 잡도 실패로 표시되는 것으로
지정한다. 다음은 구체적으로 main
컨테이너에 대한 규칙이다.
- 종료 코드 0은 컨테이너가 성공했음을 의미한다.
- 종료 코드 42는 전체 잡이 실패했음을 의미한다.
- 다른 모든 종료 코드는 컨테이너 및 전체 파드가 실패했음을
나타낸다. 재시작 횟수인
backoffLimit
까지 파드가 다시 생성된다. 만약backoffLimit
에 도달하면 전체 잡이 실패한다.
참고:
파드 템플릿이restartPolicy: Never
로 지정되었기 때문에,
kubelet은 특정 파드에서 main
컨테이너를 재시작하지 않는다.DisruptionTarget
컨디션을 갖는 실패한 파드에 대해
Ignore
동작을 하도록 명시하고 있는 파드 실패 정책의 두 번째 규칙으로 인해,
.spec.backoffLimit
재시도 한도 계산 시 파드 중단(disruption)은 횟수에서 제외된다.
참고:
파드 실패 정책 또는 파드 백오프 실패 정책에 의해 잡이 실패하고, 잡이 여러 파드를 실행중이면, 쿠버네티스는 아직 보류(Pending) 또는 실행(Running) 중인 해당 잡의 모든 파드를 종료한다.다음은 API의 몇 가지 요구 사항 및 의미이다.
- 잡에
.spec.podFailurePolicy
필드를 사용하려면,.spec.restartPolicy
가Never
로 설정된 잡의 파드 템플릿 또한 정의해야 한다. spec.podFailurePolicy.rules
에 기재한 파드 실패 정책 규칙은 기재한 순서대로 평가된다. 파드 실패 정책 규칙이 파드 실패와 매치되면 나머지 규칙은 무시된다. 파드 실패와 매치되는 파드 실패 정책 규칙이 없으면 기본 처리 방식이 적용된다.spec.podFailurePolicy.rules[*].containerName
에 컨테이너 이름을 지정하여 파드 실패 규칙을 특정 컨테이너에게만 제한할 수 있다. 컨테이너 이름을 지정하지 않으면 파드 실패 규칙은 모든 컨테이너에 적용된다. 컨테이너 이름을 지정한 경우, 이는 파드 템플릿의 컨테이너 또는initContainer
이름 중 하나와 일치해야 한다.- 파드 실패 정책이
spec.podFailurePolicy.rules[*].action
과 일치할 때 취할 동작을 지정할 수 있다. 사용 가능한 값은 다음과 같다.FailJob
: 파드의 잡을Failed
로 표시하고 실행 중인 모든 파드를 종료해야 함을 나타낸다.Ignore
:.spec.backoffLimit
에 대한 카운터가 증가하지 않아야 하고 대체 파드가 생성되어야 함을 나타낸다.Count
: 파드가 기본 방식으로 처리되어야 함을 나타낸다..spec.backoffLimit
에 대한 카운터가 증가해야 한다.
종료자(finalizers)를 이용한 잡 추적
Kubernetes v1.26 [stable]
참고:
컨트롤 플레인을 1.26으로 업그레이드하더라도, 기능 게이트JobTrackingWithFinalizers
가 비활성화되어 있을 때 생성된 잡이라면,
컨트롤 플레인은 종료자를 사용하는 잡을 추적하지 않는다.컨트롤 플레인은 잡에 속한 파드들을 계속 추적하고,
API 서버로부터 제거된 파드가 있는지에 대해 알려준다. 이를 위해 잡 컨트롤러는
batch.kubernetes.io/job-tracking
종료자를 갖는 파드를 생성한다.
컨트롤러는 파드가 잡 상태로 처리된 이후에만 종료자를 제거하여,
다른 컨트롤러나 사용자가 파드를 제거할 수 있도록 한다.
쿠버네티스를 1.26으로 업그레이드하기 전이나, 기능 게이트
JobTrackingWithFinalizers
를 활성화시키기 전에 생성한 잡은 파드 종료자를 사용하지 않고
추적된다.
잡 컨트롤러는
클러스터에 존재하는 파드들에 대해서만 succeded
와 failed
파드들에 대한 상태 카운터를 갱신한다.
만약 파드가 클러스터에서 제거된다면,
컨트롤 플레인은 잡의 진행 상황을 제대로 추적하지 못할 수 있다.
잡이 batch.kubernetes.io/job-tracking
어노테이션을 가지고 있는지 확인함으로써
컨트롤 플레인이 파드 종료자를 사용하여 잡을 추적하고 있는지 알 수 있다.
따라서 잡으로부터 이 어노테이션을 수동으로 추가하거나 제거해서는 안 된다.
그 대신, 파드 종료자를 사용하여
추적이 가능한 잡을 재생성하면 된다.
대안
베어(Bare) 파드
파드가 실행 중인 노드가 재부팅되거나 실패하면 파드가 종료되고 재시작되지 않는다. 그러나 잡은 종료된 항목을 대체하기 위해 새 파드를 생성한다. 따라서, 애플리케이션에 단일 파드만 필요한 경우에도 베어 파드 대신 잡을 사용하는 것을 권장한다.
레플리케이션 컨트롤러
잡은 레플리케이션 컨트롤러를 보완한다. 레플리케이션 컨트롤러는 종료하지 않을 파드(예: 웹 서버)를 관리하고, 잡은 종료될 것으로 예상되는 파드(예: 배치 작업)를 관리한다.
파드 라이프사이클에서 설명한 것처럼, 잡
은 오직
OnFailure
또는 Never
와 같은 RestartPolicy
를 사용하는 파드에만 적절하다.
(참고: RestartPolicy
가 설정되지 않은 경우에는 기본값은 Always
이다.)
단일 잡으로 컨트롤러 파드 시작
또 다른 패턴은 단일 잡이 파드를 생성한 후 다른 파드들을 생성해서 해당 파드들에 일종의 사용자 정의 컨트롤러 역할을 하는 것이다. 이를 통해 최대한의 유연성을 얻을 수 있지만, 시작하기에는 다소 복잡할 수 있으며 쿠버네티스와의 통합성이 낮아진다.
이 패턴의 한 예시는 파드를 시작하는 잡이다. 파드는 스크립트를 실행해서 스파크(Spark) 마스터 컨트롤러 (스파크 예시를 본다)를 시작하고, 스파크 드라이버를 실행한 다음, 정리한다.
이 접근 방식의 장점은 전체 프로세스가 잡 오브젝트의 완료를 보장하면서도, 파드 생성과 작업 할당 방법을 완전히 제어하고 유지한다는 것이다.
다음 내용
- 파드에 대해 배운다.
- 다른 방식으로 잡을 구동하는 방법에 대해서 읽는다.
- 작업 대기열을 사용한 거친 병렬 처리
- 작업 대기열을 사용한 정밀 병렬 처리
- 병렬 처리를 위한 정적 작업 할당으로 인덱스된 잡 사용
- 템플릿 기반으로 복수의 잡 생성: 확장을 사용한 병렬 처리
- 완료된 잡을 자동으로 정리 섹션 내 링크를 따라서 클러스터가 완료되거나 실패된 태스크를 어떻게 정리하는지에 대해 더 배운다.
Job
은 쿠버네티스 REST API의 일부이다. 잡 API에 대해 이해하기 위해 Job 오브젝트 정의를 읽는다.- 스케줄을 기반으로 실행되는 일련의 잡을 정의하는데 사용할 수 있고, 유닉스 툴
cron
과 유사한CronJob
에 대해 읽는다. - 단계별로 구성된 예제를 통해,
podFailurePolicy
를 사용하여 재시도 가능 및 재시도 불가능 파드의 실패 처리를 하기위한 구성 방법을 연습한다.
5.2.6 - 완료된 잡 자동 정리
Kubernetes v1.23 [stable]
완료-이후-TTL(TTL-after-finished) 컨트롤러는 실행이 완료된 리소스 오브젝트의 수명을 제한하는 TTL (time to live) 메커니즘을 제공한다. TTL 컨트롤러는 잡만을 제어한다.
완료-이후-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 컨트롤러가 잘못된 시간에 잡 오브젝트를 정리하게 될 수 있다.
시계가 항상 정확한 것은 아니지만, 그 차이는 아주 작아야 한다. 0이 아닌 TTL을 설정할때는 이 위험에 대해 유의해야 한다.
다음 내용
5.2.7 - 크론잡
Kubernetes v1.21 [stable]
크론잡은 반복 일정에 따라 잡을 만든다.
하나의 크론잡 오브젝트는 크론탭 (크론 테이블) 파일의 한 줄과 같다. 크론잡은 잡을 크론 형식으로 쓰여진 주어진 일정에 따라 주기적으로 동작시킨다.
주의:
모든 크론잡 일정:
시간은
kube-controller-manager의 시간대를 기준으로 한다.
컨트롤 플레인이 파드 또는 베어 컨테이너에서 kube-controller-manager를 실행하는 경우, kube-controller-manager 컨테이너에 설정된 시간대는 크론잡 컨트롤러가 사용하는 시간대로 결정한다.
주의:
v1 CronJob API은 위에서 설명한 타임존 설정을 공식적으로 지원하지는 않는다.
CRON_TZ
또는 TZ
와 같은 변수를 설정하는 것은 쿠버네티스 프로젝트에서 공식적으로 지원하지는 않는다.
CRON_TZ
또는 TZ
와 같은 변수를 설정하는 것은
크론탭을 파싱하고 다음 잡 생성 시간을 계산하는 내부 라이브러리의 구현 상세사항이다.
프로덕션 클러스터에서는 사용을 권장하지 않는다.
크론잡 리소스에 대한 매니페스트를 생성할 때에는 제공하는 이름이 유효한 DNS 서브도메인 이름이어야 한다. 이름은 52자 이하여야 한다. 이는 크론잡 컨트롤러는 제공된 잡 이름에 11자를 자동으로 추가하고, 작업 이름의 최대 길이는 63자라는 제약 조건이 있기 때문이다.
크론잡
크론잡은 백업, 리포트 생성 등의 정기적 작업을 수행하기 위해 사용된다. 각 작업은 무기한 반복되도록 구성해야 한다(예: 1일/1주/1달마다 1회). 작업을 시작해야 하는 해당 간격 내 특정 시점을 정의할 수 있다.
예시
이 크론잡 매니페스트 예제는 현재 시간과 hello 메시지를 1분마다 출력한다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
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도 일요일)
# │ │ │ │ │ 또는 sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# * * * * *
항목 | 설명 | 상응 표현 |
---|---|---|
@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일 자정에도 시작되어야 한다는 뜻이다.
0 0 13 * 5
크론잡 스케줄 표현을 생성하기 위해서 crontab.guru와 같은 웹 도구를 사용할 수도 있다.
타임 존
크론잡에 타임 존이 명시되어 있지 않으면, kube-controller-manager는 로컬 타임 존을 기준으로 스케줄을 해석한다.
Kubernetes v1.25 [beta]
CronJobTimeZone
기능 게이트를 활성화하면,
크론잡에 대해 타임 존을 명시할 수 있다(기능 게이트를 활성화하지 않거나,
타임 존에 대한 실험적 지원을 제공하지 않는 쿠버네티스 버전을 사용 중인 경우,
클러스터의 모든 크론잡은 타임 존이 명시되지 않은 것으로 동작한다).
이 기능을 활성화하면, spec.timeZone
을 유효한 타임 존으로 지정할 수 있다.
예를 들어, spec.timeZone: "Etc/UTC"
와 같이 설정하면 쿠버네티스는 협정 세계시를 기준으로 스케줄을 해석한다.
Go 표준 라이브러리의 타임 존 데이터베이스가 바이너리로 인클루드되며, 시스템에서 외부 데이터베이스를 사용할 수 없을 때 폴백(fallback)으로 사용된다.
크론잡의 한계
크론잡은 일정의 실행시간 마다 약 한 번의 잡 오브젝트를 생성한다. "약" 이라고 하는 이유는 특정 환경에서는 두 개의 잡이 만들어지거나, 잡이 생성되지 않기도 하기 때문이다. 보통 이렇게 하지 않도록 해야겠지만, 완벽히 그럴 수는 없다. 따라서 잡은 멱등원 이 된다.
만약 startingDeadlineSeconds
가 큰 값으로 설정되거나, 설정되지 않고(디폴트 값),
concurrencyPolicy
가 Allow
로 설정될 경우, 잡은 항상 적어도 한 번은
실행될 것이다.
주의:
startingDeadlineSeconds
가 10초 미만의 값으로 설정되면, 크론잡이 스케줄되지 않을 수 있다. 이는 크론잡 컨트롤러가 10초마다 항목을 확인하기 때문이다.모든 크론잡에 대해 크론잡 컨트롤러 는 마지막 일정부터 지금까지 얼마나 많은 일정이 누락되었는지 확인한다. 만약 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
값에서 몇 개의 잡이 누락되었는지 카운팅한다. 예를 들면, startingDeadlineSeconds
가 200
이면, 컨트롤러는 최근 200초 내 몇 개의 잡이 누락되었는지 카운팅한다.
크론잡은 정해진 시간에 생성되는데 실패하면 누락(missed)된 것으로 집계된다. 예를 들어 concurrencyPolicy
가 Forbid
로 설정되어 있고 이전 크론잡이 여전히 실행 중인 상태라면, 크론잡은 일정에 따라 시도되었다가 생성을 실패하고 누락된 것으로 집계될 것이다.
즉, 크론잡이 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의 일부이다. CronJob 오브젝트 정의를 읽고 쿠버네티스 크론잡 API에 대해 이해한다.
5.2.8 - 레플리케이션 컨트롤러
레플리케이션컨트롤러 는 언제든지 지정된 수의 파드 레플리카가 실행 중임을 보장한다. 다시 말하면, 레플리케이션 컨트롤러는 파드 또는 동일 종류의 파드의 셋이 항상 기동되고 사용 가능한지 확인한다.
레플리케이션 컨트롤러의 동작방식
파드가 너무 많으면 레플리케이션 컨트롤러가 추가적인 파드를 제거한다. 너무 적으면 레플리케이션 컨트롤러는 더 많은 파드를 시작한다. 수동으로 생성된 파드와 달리 레플리케이션 컨트롤러가 유지 관리하는 파드는 실패하거나 삭제되거나 종료되는 경우 자동으로 교체된다. 예를 들어, 커널 업그레이드와 같이 파괴적인 유지 보수 작업을 하고 난 이후의 노드에서 파드가 다시 생성된다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용해야 한다. 레플리케이션 컨트롤러는 프로세스 감시자(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나 클라이언트 라이브러리를 사용하는 경우 명시적으로 단계를 수행해야 한다(레플리카를 0으로 스케일하고 파드의 삭제를 기다린 이후, 레플리케이션 컨트롤러를 삭제).
레플리케이션 컨트롤러만 삭제
해당 파드에 영향을 주지 않고 레플리케이션 컨트롤러를 삭제할 수 있다.
kubectl을 사용하여, kubectl delete
에 옵션으로 --cascade=orphan
을 지정하라.
REST API나 클라이언트 라이브러리를 사용하는 경우 레플리케이션 컨트롤러 오브젝트를 삭제하라.
원본이 삭제되면 대체할 새로운 레플리케이션 컨트롤러를 생성하여 교체할 수 있다. 오래된 파드와 새로운 파드의 .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에 대해 이해하기 위해 ReplicationController 오브젝트 정의를 읽는다.
6 - 서비스, 로드밸런싱, 네트워킹
쿠버네티스 네트워크 모델
클러스터의 모든 파드
는 고유한 IP 주소를 갖는다.
이는 즉 파드
간 연결을 명시적으로 만들 필요가 없으며
또한 컨테이너 포트를 호스트 포트에 매핑할 필요가 거의 없음을 의미한다.
이로 인해 포트 할당, 네이밍, 서비스 디스커버리,
로드 밸런싱,
애플리케이션 구성, 마이그레이션 관점에서 파드
를 VM 또는 물리 호스트처럼 다룰 수 있는 깔끔하고
하위 호환성을 갖는 모델이 제시된다.
쿠버네티스는 모든 네트워킹 구현에 대해 다음과 같은 근본적인 요구사항을 만족할 것을 요구한다. (이를 통해, 의도적인 네트워크 분할 정책을 방지)
- 파드는 NAT 없이 노드 상의 모든 파드와 통신할 수 있다.
- 노드 상의 에이전트(예: 시스템 데몬, kubelet)는 해당 노드의 모든 파드와 통신할 수 있다.
참고: 파드
를 호스트 네트워크에서 구동하는 것도 지원하는 플랫폼(예:
리눅스)에 대해서는, 파드가 노드의 호스트 네트워크에 연결되어 있을 때에도 파드는 여전히
NAT 없이 모든 노드의 모든 파드와 통신할 수 있다.
이 모델은 전반적으로 덜 복잡할 뿐만 아니라, 무엇보다도 VM에 있던 앱을 컨테이너로 손쉽게 포팅하려는 쿠버네티스 요구사항을 만족시킬 수 있다. 작업을 기존에는 VM에서 실행했었다면, VM은 IP주소를 가지며 프로젝트 내의 다른 VM과 통신할 수 있었을 것이다. 이는 동일한 기본 모델이다.
쿠버네티스 IP 주소는 파드
범주에 존재하며,
파드
내의 컨테이너들은 IP 주소, MAC 주소를 포함하는 네트워크 네임스페이스를 공유한다.
이는 곧 파드
내의 컨테이너들이 각자의 포트에 localhost
로 접근할 수 있음을 의미한다.
또한 파드
내의 컨테이너들이 포트 사용에 있어 서로 협조해야 하는데,
이는 VM 내 프로세스 간에도 마찬가지이다.
이러한 모델은 "파드 별 IP" 모델로 불린다.
이것이 어떻게 구현되는지는 사용하는 컨테이너 런타임의 상세사항이다.
노드
자체의 포트를 파드
로 포워드하도록 요청하는 것도 가능하지만(호스트 포트라고 불림),
이는 매우 비주류적인(niche) 동작이다.
포워딩이 어떻게 구현되는지도 컨테이너 런타임의 상세사항이다.
파드
자체는 호스트 포트 존재 유무를 인지할 수 없다.
쿠버네티스 네트워킹은 다음의 네 가지 문제를 해결한다.
파드 내의 컨테이너는 루프백(loopback)을 통한 네트워킹을 사용하여 통신한다.
클러스터 네트워킹은 서로 다른 파드 간의 통신을 제공한다.
서비스 API를 사용하면 파드에서 실행 중인 애플리케이션을 클러스터 외부에서 접근 할 수 있다.
- 인그레스는 특히 HTTP 애플리케이션, 웹사이트 그리고 API를 노출하기 위한 추가 기능을 제공한다.
또한 서비스를 사용하여 서비스를 클러스터 내부에서만 사용할 수 있도록 게시할 수 있다.
서비스와 애플리케이션 연결하기 튜토리얼에서는 실습 예제를 통해 서비스와 쿠버네티스 네트워킹에 대해 배울 수 있다.
클러스터 네트워킹은 클러스터에 네트워킹을 설정하는 방법과 관련된 기술에 대한 개요도 제공한다.
6.1 - 서비스
쿠버네티스를 사용하면 익숙하지 않은 서비스 디스커버리 메커니즘을 사용하기 위해 애플리케이션을 수정할 필요가 없다. 쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.
동기
쿠버네티스 파드는 클러스터 목표 상태(desired state)와 일치하도록 생성되고 삭제된다. 파드는 비영구적 리소스이다. 만약 앱을 실행하기 위해 디플로이먼트를 사용한다면, 동적으로 파드를 생성하고 제거할 수 있다.
각 파드는 고유한 IP 주소를 갖지만, 디플로이먼트에서는 한 시점에 실행되는 파드 집합이 잠시 후 실행되는 해당 파드 집합과 다를 수 있다.
이는 다음과 같은 문제를 야기한다. ("백엔드"라 불리는) 일부 파드 집합이 클러스터의 ("프론트엔드"라 불리는) 다른 파드에 기능을 제공하는 경우, 프론트엔드가 워크로드의 백엔드를 사용하기 위해, 프론트엔드가 어떻게 연결할 IP 주소를 찾아서 추적할 수 있는가?
서비스 로 들어가보자.
서비스 리소스
쿠버네티스에서 서비스는 파드의 논리적 집합과 그것들에 접근할 수 있는 정책을 정의하는 추상적 개념이다. (때로는 이 패턴을 마이크로-서비스라고 한다.) 서비스가 대상으로 하는 파드 집합은 일반적으로 셀렉터가 결정한다. 서비스 엔드포인트를 정의하는 다른 방법에 대한 자세한 내용은 셀렉터가 없는 서비스를 참고한다.
예를 들어, 3개의 레플리카로 실행되는 스테이트리스 이미지-처리 백엔드를 생각해보자. 이러한 레플리카는 대체 가능하다. 즉, 프론트엔드는 그것들이 사용하는 백엔드를 신경쓰지 않는다. 백엔드 세트를 구성하는 실제 파드는 변경될 수 있지만, 프론트엔드 클라이언트는 이를 인식할 필요가 없으며, 백엔드 세트 자체를 추적해야 할 필요도 없다.
서비스 추상화는 이러한 디커플링을 가능하게 한다.
클라우드-네이티브 서비스 디스커버리
애플리케이션에서 서비스 디스커버리를 위해 쿠버네티스 API를 사용할 수 있는 경우, 매치되는 엔드포인트슬라이스를 API 서버에 질의할 수 있다. 쿠버네티스는 서비스의 파드가 변경될 때마다 서비스의 엔드포인트슬라이스를 업데이트한다.
네이티브 애플리케이션이 아닌 (non-native applications) 경우, 쿠버네티스는 애플리케이션과 백엔드 파드 사이에 네트워크 포트 또는 로드 밸런서를 배치할 수 있는 방법을 제공한다.
서비스 정의
쿠버네티스의 서비스는 파드와 비슷한 REST 오브젝트이다. 모든 REST 오브젝트와
마찬가지로, 서비스 정의를 API 서버에 POST
하여
새 인스턴스를 생성할 수 있다.
서비스 오브젝트의 이름은 유효한
RFC 1035 레이블 이름이어야 한다.
예를 들어, 각각 TCP 포트 9376에서 수신하고
app.kubernetes.io/name=MyApp
레이블을 가지고 있는 파드 세트가 있다고 가정해 보자.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
이 명세는 "my-service"라는 새로운 서비스 오브젝트를 생성하고,
app.kubernetes.io/name=MyApp
레이블을 가진 파드의 TCP 9376 포트를 대상으로 한다.
쿠버네티스는 이 서비스에 서비스 프록시가 사용하는 IP 주소 ("cluster IP"라고도 함) 를 할당한다. (이하 가상 IP와 서비스 프록시 참고)
서비스 셀렉터의 컨트롤러는 셀렉터와 일치하는 파드를 지속적으로 검색하고, "my-service"라는 엔드포인트 오브젝트에 대한 모든 업데이트를 POST한다.
참고:
서비스는 모든 수신port
를 targetPort
에 매핑할 수 있다. 기본적으로 그리고
편의상, targetPort
는 port
필드와 같은 값으로 설정된다.파드의 포트 정의에 이름이 있으므로,
서비스의 targetPort
속성에서 이 이름을 참조할 수 있다.
예를 들어, 다음과 같은 방법으로 서비스의 targetPort
를 파드 포트에 바인딩할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
이것은 서로 다른 포트 번호를 통해 가용한 동일 네트워크 프로토콜이 있고, 단일 구성 이름을 사용하는 서비스 내에 혼합된 파드가 존재해도 가능하다. 이를 통해 서비스를 배포하고 진전시키는 데 많은 유연성을 제공한다. 예를 들어, 클라이언트를 망가뜨리지 않고, 백엔드 소프트웨어의 다음 버전에서 파드가 노출시키는 포트 번호를 변경할 수 있다.
서비스의 기본 프로토콜은 TCP이다. 다른 지원되는 프로토콜을 사용할 수도 있다.
많은 서비스가 하나 이상의 포트를 노출해야 하기 때문에, 쿠버네티스는 서비스 오브젝트에서 다중
포트 정의를 지원한다.
각 포트는 동일한 프로토콜
또는 다른 프로토콜로 정의될 수 있다.
셀렉터가 없는 서비스
서비스는 일반적으로 셀렉터를 이용하여 쿠버네티스 파드에 대한 접근을 추상화하지만, 셀렉터 대신 매칭되는(corresponding) 엔드포인트슬라이스 오브젝트와 함께 사용되면 다른 종류의 백엔드도 추상화할 수 있으며, 여기에는 클러스터 외부에서 실행되는 것도 포함된다.
예시는 다음과 같다.
- 프로덕션 환경에서는 외부 데이터베이스 클러스터를 사용하려고 하지만, 테스트 환경에서는 자체 데이터베이스를 사용한다.
- 한 서비스에서 다른 네임스페이스 또는 다른 클러스터의 서비스를 지정하려고 한다.
- 워크로드를 쿠버네티스로 마이그레이션하고 있다. 해당 방식을 평가하는 동안, 쿠버네티스에서는 백엔드의 일부만 실행한다.
이러한 시나리오에서는 파드 셀렉터 없이 서비스를 정의 할 수 있다. 예를 들면
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
이 서비스에는 셀렉터가 없으므로, 매칭되는 엔드포인트슬라이스 (및 레거시 엔드포인트) 오브젝트가 자동으로 생성되지 않는다. 엔드포인트슬라이스 오브젝트를 수동으로 추가하여, 서비스를 실행 중인 네트워크 주소 및 포트에 서비스를 수동으로 매핑할 수 있다. 예시는 다음과 같다.
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: my-service-1 # 관행적으로, 서비스의 이름을
# 엔드포인트슬라이스 이름의 접두어로 사용한다.
labels:
# "kubernetes.io/service-name" 레이블을 설정해야 한다.
# 이 레이블의 값은 서비스의 이름과 일치하도록 지정한다.
kubernetes.io/service-name: my-service
addressType: IPv4
ports:
- name: '' # 9376 포트는 (IANA에 의해) 잘 알려진 포트로 할당되어 있지 않으므로
# 이 칸은 비워 둔다.
appProtocol: http
protocol: TCP
port: 9376
endpoints:
- addresses:
- "10.4.5.6" # 이 목록에 IP 주소를 기재할 때 순서는 상관하지 않는다.
- "10.1.2.3"
커스텀 엔드포인트슬라이스
서비스를 위한 엔드포인트슬라이스 오브젝트를 생성할 때,
엔드포인트슬라이스 이름으로는 원하는 어떤 이름도 사용할 수 있다.
네임스페이스 내의 각 엔드포인트슬라이스 이름은 고유해야 한다.
해당 엔드포인트슬라이스에 kubernetes.io/service-name
레이블을 설정하여
엔드포인트슬라이스를 서비스와 연결할 수 있다.
참고:
엔드포인트 IP는 루프백(loopback) (IPv4의 경우 127.0.0.0/8, IPv6의 경우 ::1/128), 또는 링크-로컬 (IPv4의 경우 169.254.0.0/16와 224.0.0.0/24, IPv6의 경우 fe80::/64)이 되어서는 안된다.
엔드포인트 IP 주소는 다른 쿠버네티스 서비스의 클러스터 IP일 수 없는데, kube-proxy는 가상 IP를 목적지(destination)로 지원하지 않기 때문이다.
직접 생성했거나 직접 작성한 코드에 의해 생성된 엔드포인트슬라이스를 위해,
endpointslice.kubernetes.io/managed-by
레이블에 사용할 값을 골라야 한다.
엔드포인트슬라이스를 관리하는 컨트롤러 코드를 직접 작성하는 경우,
"my-domain.example/name-of-controller"
와 같은 값을 사용할 수 있다.
써드파티 도구를 사용하는 경우, 도구의 이름에서 대문자는 모두 소문자로 바꾸고
공백 및 다른 문장 부호는 하이픈(-
)으로 대체한 문자열을 사용한다.
kubectl
과 같은 도구를 사용하여 직접 엔드포인트슬라이스를 관리하는 경우,
"staff"
또는 "cluster-admins"
와 같이 이러한 수동 관리를 명시하는 이름을 사용한다.
쿠버네티스 자체 컨트롤 플레인이 관리하는 엔드포인트슬라이스를 가리키는
"controller"
라는 예약된 값은 사용하지 말아야 한다.
셀렉터가 없는 서비스에 접근하기
셀렉터가 없는 서비스에 접근하는 것은 셀렉터가 있는 서비스에 접근하는 것과 동일하게 동작한다. 셀렉터가 없는 서비스 예시에서, 트래픽은 엔드포인트슬라이스 매니페스트에 정의된 두 엔드포인트 중 하나로 라우트된다(10.1.2.3:9376 또는 10.4.5.6:9376으로의 TCP 연결).
ExternalName 서비스는 셀렉터가 없고 대신 DNS 이름을 사용하는 특이 케이스 서비스이다. 자세한 내용은 이 문서 뒷부분의 ExternalName 섹션을 참조한다.
엔드포인트슬라이스
Kubernetes v1.21 [stable]
엔드포인트슬라이스는 특정 서비스의 하위(backing) 네트워크 엔드포인트 부분집합(슬라이스)을 나타내는 오브젝트이다.
쿠버네티스 클러스터는 각 엔드포인트슬라이스가 얼마나 많은 엔드포인트를 나타내는지를 추적한다. 한 서비스의 엔드포인트가 너무 많아 역치에 도달하면, 쿠버네티스는 빈 엔드포인트슬라이스를 생성하고 여기에 새로운 엔드포인트 정보를 저장한다. 기본적으로, 쿠버네티스는 기존의 모든 엔드포인트슬라이스가 엔드포인트를 최소 100개 이상 갖게 되면 새 엔드포인트슬라이스를 생성한다. 쿠버네티스는 새 엔드포인트가 추가되어야 하는 상황이 아니라면 새 엔드포인트슬라이스를 생성하지 않는다.
이 API에 대한 더 많은 정보는 엔드포인트슬라이스를 참고한다.
엔드포인트
쿠버네티스 API에서, 엔드포인트(Endpoints)(리소스 명칭이 복수형임)는 네트워크 엔드포인트의 목록을 정의하며, 일반적으로 트래픽이 어떤 파드에 보내질 수 있는지를 정의하기 위해 서비스가 참조한다.
엔드포인트 대신 엔드포인트슬라이스 API를 사용하는 것을 권장한다.
용량 한계를 넘어선 엔드포인트
쿠버네티스는 단일 엔드포인트(Endpoints) 오브젝트에 포함될 수 있는 엔드포인트(endpoints)의 수를 제한한다. 단일 서비스에 1000개 이상의 하위(backing) 엔드포인트가 있으면, 쿠버네티스는 엔드포인트 오브젝트의 데이터를 덜어낸다(truncate). 서비스는 하나 이상의 엔드포인트슬라이스와 연결될 수 있기 때문에, 하위 엔드포인트 1000개 제한은 기존(legacy) 엔드포인트 API에만 적용된다.
이러한 경우, 쿠버네티스는 엔드포인트(Endpoints) 오브젝트에 저장될 수 있는
백엔드 엔드포인트(endpoints)를 최대 1000개 선정하고,
엔드포인트 오브젝트에 endpoints.kubernetes.io/over-capacity: truncated
어노테이션을 설정한다.
컨트롤 플레인은 또한 백엔드 파드 수가 1000 미만으로 내려가면
해당 어노테이션을 제거한다.
트래픽은 여전히 백엔드로 전송되지만, 기존(legacy) 엔드포인트 API에 의존하는 모든 로드 밸런싱 메커니즘은 사용 가능한 하위(backing) 엔드포인트 중에서 최대 1000개까지에만 트래픽을 전송한다.
동일한 API 상한은 곧 하나의 엔드포인트(Endpoints) 객체가 1000개 이상의 엔드포인트(endpoints)를 갖도록 수동으로 업데이트할 수는 없음을 의미한다.
애플리케이션 프로토콜
Kubernetes v1.20 [stable]
appProtocol
필드는 각 서비스 포트에 대한 애플리케이션 프로토콜을 지정하는 방법을 제공한다.
이 필드의 값은 상응하는 엔드포인트와 엔드포인트슬라이스
오브젝트에 의해 미러링된다.
이 필드는 표준 쿠버네티스 레이블 구문을 따른다. 값은
IANA 표준 서비스 이름 또는
mycompany.com/my-custom-protocol
과 같은 도메인 접두사 이름 중 하나여야 한다.
멀티-포트 서비스
일부 서비스의 경우, 둘 이상의 포트를 노출해야 한다. 쿠버네티스는 서비스 오브젝트에서 멀티 포트 정의를 구성할 수 있도록 지원한다. 서비스에 멀티 포트를 사용하는 경우, 모든 포트 이름을 명확하게 지정해야 한다. 예를 들면 다음과 같다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
참고:
쿠버네티스의 일반적인 이름과 마찬가지로, 포트 이름은
소문자 영숫자와 -
만 포함해야 한다. 포트 이름은
영숫자로 시작하고 끝나야 한다.
예를 들어, 123-abc
와 web
은 유효하지만, 123_abc
와 -web
은 유효하지 않다.
자신의 IP 주소 선택
서비스
생성 요청시 고유한 클러스터 IP 주소를 지정할 수
있다. 이를 위해, .spec.clusterIP
필드를 설정한다. 예를 들어,
재사용하려는 기존 DNS 항목이 있거나, 특정 IP 주소로 구성되어
재구성이 어려운 레거시 시스템인 경우이다.
선택한 IP 주소는 API 서버에 대해 구성된 service-cluster-ip-range
CIDR 범위 내의 유효한 IPv4 또는 IPv6 주소여야 한다.
유효하지 않은 clusterIP 주소 값으로 서비스를 생성하려고 하면, API 서버는
422 HTTP 상태 코드를 리턴하여 문제점이 있음을 알린다.
서비스 디스커버리하기
쿠버네티스는 서비스를 찾는 두 가지 기본 모드를 지원한다. - 환경 변수와 DNS
환경 변수
파드가 노드에서 실행될 때, kubelet은 각 활성화된 서비스에 대해 환경 변수 세트를 추가한다.
{SVCNAME}_SERVICE_HOST
및 {SVCNAME}_SERVICE_PORT
환경 변수가 추가되는데,
이 때 서비스 이름은 대문자로, 하이픈(-
)은 언더스코어(_
)로 변환하여 사용한다.
또한 도커 엔진의 "레거시 컨테이너 연결" 기능과
호환되는 변수(makeLinkVariables 참조)도
지원한다.
예를 들어, TCP 포트 6379를 개방하고
클러스터 IP 주소 10.0.0.11이 할당된 서비스 redis-primary
는,
다음 환경 변수를 생성한다.
REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11
참고:
서비스에 접근이 필요한 파드가 있고, 환경 변수를 사용해 포트 및 클러스터 IP를 클라이언트 파드에 부여하는 경우, 클라이언트 파드가 생성되기 전에 서비스를 만들어야 한다. 그렇지 않으면, 해당 클라이언트 파드는 환경 변수를 생성할 수 없다.
DNS 만 사용하여 서비스의 클러스터 IP를 검색하는 경우, 이 순서 이슈에 대해 신경 쓸 필요가 없다.
DNS
애드-온을 사용하여 쿠버네티스 클러스터의 DNS 서비스를 설정할 수(대개는 필수적임) 있다.
CoreDNS와 같은, 클러스터-인식 DNS 서버는 새로운 서비스를 위해 쿠버네티스 API를 감시하고 각각에 대한 DNS 레코드 세트를 생성한다. 클러스터 전체에서 DNS가 활성화된 경우 모든 파드는 DNS 이름으로 서비스를 자동으로 확인할 수 있어야 한다.
예를 들면, 쿠버네티스 네임스페이스 my-ns
에 my-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 내에서 엔드포인트슬라이스 오브젝트를 생성하고, 서비스 하위(backing) 파드들을 직접 가리키는 A 또는 AAAA 레코드(IPv4 또는 IPv6 주소)를 반환하도록 DNS 구성을 변경한다.
셀렉터가 없는 경우
셀렉터를 정의하지 않는 헤드리스 서비스의 경우, 쿠버네티스 컨트롤 플레인은 엔드포인트슬라이스 오브젝트를 생성하지 않는다. 하지만, DNS 시스템은 다음 중 하나를 탐색한 뒤 구성한다.
type: ExternalName
서비스에 대한 DNS CNAME 레코드ExternalName
이외의 모든 서비스 타입에 대해, 서비스의 활성(ready) 엔드포인트의 모든 IP 주소에 대한 DNS A / AAAA 레코드- IPv4 엔드포인트에 대해, DNS 시스템은 A 레코드를 생성한다.
- IPv6 엔드포인트에 대해, DNS 시스템은 AAAA 레코드를 생성한다.
서비스 퍼블리싱 (ServiceTypes)
애플리케이션 중 일부(예: 프론트엔드)는 서비스를 클러스터 밖에 위치한 외부 IP 주소에 노출하고 싶은 경우가 있을 것이다.
쿠버네티스 ServiceTypes
는 원하는 서비스 종류를 지정할 수 있도록 해 준다.
Type
값과 그 동작은 다음과 같다.
ClusterIP
: 서비스를 클러스터-내부 IP에 노출시킨다. 이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다. 이것은 서비스의type
을 명시적으로 지정하지 않았을 때의 기본값이다.NodePort
: 고정 포트 (NodePort
)로 각 노드의 IP에 서비스를 노출시킨다. 노드 포트를 사용할 수 있도록 하기 위해, 쿠버네티스는type: ClusterIP
인 서비스를 요청했을 때와 마찬가지로 클러스터 IP 주소를 구성한다.LoadBalancer
: 클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다.ExternalName
: 값과 함께 CNAME 레코드를 리턴하여, 서비스를externalName
필드의 내용(예:foo.bar.example.com
)에 매핑한다. 어떠한 종류의 프록시도 설정되지 않는다.참고:
ExternalName
유형을 사용하려면kube-dns
버전 1.7 또는 CoreDNS 버전 0.0.8 이상이 필요하다.
type
필드는 중첩(nested) 기능으로 설계되어, 각 단계는 이전 단계에 더해지는 형태이다.
이는 모든 클라우드 공급자에 대해 엄격히 요구되는 사항은 아니다(예:
Google Compute Engine에서는 type: LoadBalancer
가 동작하기 위해 노드 포트를 할당할 필요가 없지만,
다른 클라우드 공급자 통합 시에는 필요할 수 있음).
엄격한 중첩이 필수 사항은 아니지만, 서비스에 대한 쿠버네티스 API 디자인은 이와 상관없이 엄격한 중첩 구조를 가정한다.
인그레스를 사용하여 서비스를 노출시킬 수도 있다. 인그레스는 서비스 유형은 아니지만, 클러스터의 진입점 역할을 한다. 동일한 IP 주소로 여러 서비스를 노출시킬 수 있기 때문에 라우팅 규칙을 단일 리소스로 통합할 수 있다.
NodePort 유형
type
필드를 NodePort
로 설정하면, 쿠버네티스 컨트롤 플레인은
--service-node-port-range
플래그로 지정된 범위에서 포트를 할당한다 (기본값 : 30000-32767).
각 노드는 해당 포트 (모든 노드에서 동일한 포트 번호)를 서비스로 프록시한다.
서비스는 할당된 포트를 .spec.ports[*].nodePort
필드에 나타낸다.
NodePort를 사용하면 자유롭게 자체 로드 밸런싱 솔루션을 설정하거나, 쿠버네티스가 완벽하게 지원하지 않는 환경을 구성하거나, 하나 이상의 노드 IP를 직접 노출시킬 수 있다.
NodePort 서비스에 대해, 쿠버네티스는 포트를 추가로
할당한다(서비스의 프로토콜에 매치되도록 TCP, UDP, SCTP 중 하나).
클러스터의 모든 노드는 할당된 해당 포트를 리슨하고
해당 서비스에 연결된 활성(ready) 엔드포인트 중 하나로 트래픽을 전달하도록 자기 자신을 구성한다.
적절한 프로토콜(예: TCP) 및 적절한 포트(해당 서비스에 할당된 대로)로
클러스터 외부에서 클러스터의 아무 노드에 연결하여 type: NodePort
서비스로 접근할 수 있다.
포트 직접 선택하기
특정 포트 번호를 원한다면, nodePort
필드에 값을 명시할 수 있다.
컨트롤 플레인은 해당 포트를 할당해 주거나 또는
해당 API 트랜젝션이 실패했다고 알려줄 것이다.
이는 사용자 스스로 포트 충돌의 가능성을 고려해야 한다는 의미이다.
또한 유효한(NodePort용으로 사용할 수 있도록 구성된 범위 내의)
포트 번호를 사용해야 한다.
다음은 NodePort 값을 명시하는(이 예시에서는 30007)
type: NodePort
서비스에 대한 예시 매니페스트이다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app.kubernetes.io/name: MyApp
ports:
# 기본적으로 그리고 편의상 `targetPort` 는 `port` 필드와 동일한 값으로 설정된다.
- port: 80
targetPort: 80
# 선택적 필드
# 기본적으로 그리고 편의상 쿠버네티스 컨트롤 플레인은 포트 범위에서 할당한다(기본값: 30000-32767)
nodePort: 30007
type: NodePort
서비스를 위한 커스텀 IP 주소 구성
NodePort 서비스 노출에 특정 IP 주소를 사용하도록 클러스터의 노드를 설정할 수 있다. 각 노드가 여러 네트워크(예: 애플리케이션 트래픽용 네트워크 및 노드/컨트롤 플레인 간 트래픽용 네트워크)에 연결되어 있는 경우에 이러한 구성을 고려할 수 있다.
포트를 프록시하기 위해 특정 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에 대해 사용 가능한 모든 네트워크 인터페이스를 고려해야 한다는 것을 의미한다.
(이는 이전 쿠버네티스 릴리스와도 호환된다).
참고:
이 서비스는<NodeIP>:spec.ports[*].nodePort
와 .spec.clusterIP:spec.ports[*].port
로 표기된다.
kube-proxy에 대한 --nodeport-addresses
플래그 또는 kube-proxy 구성 파일의 동등한 필드가 설정된 경우,
<NodeIP>
는 노드 IP를 필터링한다.로드밸런서 유형
외부 로드 밸런서를 지원하는 클라우드 공급자 상에서, type
필드를 LoadBalancer
로 설정하면 서비스에 대한 로드 밸런서를 프로비저닝한다.
로드 밸런서의 실제 생성은 비동기적으로 수행되고,
프로비저닝된 밸런서에 대한 정보는 서비스의
.status.loadBalancer
필드에 발행된다.
예를 들면 다음과 같다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: 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
필드는
무시된다.
type: LoadBalancer
인 서비스를 구현하기 위해,
쿠버네티스는 일반적으로 type: NodePort
서비스를 요청했을 때와 동일한 변경사항을 적용하면서 시작한다.
그런 다음 cloud-controller-manager 컴포넌트는
할당된 해당 NodePort로 트래픽을 전달하도록 외부 로드 밸런서를 구성한다.
알파 기능으로서, 로드 밸런스된 서비스가 NodePort 할당을 생략하도록 구성할 수 있는데, 이는 클라우드 공급자의 구현이 이를 지원할 때에만 가능하다.
참고:
Azure 에서 사용자 지정 공개(public) 유형 loadBalancerIP
를 사용하려면, 먼저
정적 유형 공개 IP 주소 리소스를 생성해야 한다. 이 공개 IP 주소 리소스는
클러스터에서 자동으로 생성된 다른 리소스와 동일한 리소스 그룹에 있어야 한다.
예를 들면, MC_myResourceGroup_myAKSCluster_eastus
이다.
할당된 IP 주소를 loadBalancerIP로 지정한다. 클라우드 공급자 구성 파일에서
securityGroupName
을 업데이트했는지 확인한다.
CreatingLoadBalancerFailed
권한 문제 해결에 대한 자세한 내용은
Azure Kubernetes Service (AKS) 로드 밸런서에서 고정 IP 주소 사용
또는 고급 네트워킹 AKS 클러스터에서 CreateLoadBalancerFailed를 참고한다.
프로토콜 유형이 혼합된 로드밸런서
Kubernetes v1.24 [beta]
기본적으로 로드밸런서 서비스 유형의 경우 둘 이상의 포트가 정의되어 있을 때 모든 포트는 동일한 프로토콜을 가져야 하며 프로토콜은 클라우드 공급자가 지원하는 프로토콜이어야 한다.
MixedProtocolLBService
기능 게이트(v1.24에서 kube-apiserver에 대해 기본적으로 활성화되어 있음)는
둘 이상의 포트가 정의되어 있는 경우에 로드밸런서 타입의 서비스에 대해 서로 다른 프로토콜을 사용할 수 있도록 해 준다.
참고:
로드밸런서 서비스 유형에 사용할 수 있는 프로토콜 세트는 여전히 클라우드 제공 업체에서 정의한다. 클라우드 제공자가 혼합 프로토콜을 지원하지 않는다면 이는 단일 프로토콜만을 제공한다는 것을 의미한다.로드밸런서 NodePort 할당 비활성화
Kubernetes v1.24 [stable]
type=LoadBalancer
서비스에 대한 노드 포트 할당을 선택적으로 비활성화할 수 있으며,
이는 spec.allocateLoadBalancerNodePorts
필드를 false
로 설정하면 된다.
노드 포트를 사용하지 않고 트래픽을 파드로 직접 라우팅하는 로드 밸런서 구현에만 사용해야 한다.
기본적으로 spec.allocateLoadBalancerNodePorts
는 true
이며 로드밸런서 서비스 유형은 계속해서 노드 포트를 할당할 것이다.
노드 포트가 할당된 기존 서비스에서 spec.allocateLoadBalancerNodePorts
가 false
로 설정된 경우 해당 노드 포트는 자동으로 할당 해제되지 않는다.
이러한 노드 포트를 할당 해제하려면 모든 서비스 포트에서 nodePorts
항목을 명시적으로 제거해야 한다.
로드 밸런서 구현 클래스 지정
Kubernetes v1.24 [stable]
spec.loadBalancerClass
필드를 설정하여 클라우드 제공자가 설정한 기본값 이외의 로드 밸런서 구현을 사용할 수 있다.
기본적으로, spec.loadBalancerClass
는 nil
이고,
클러스터가 클라우드 제공자의 로드밸런서를 이용하도록 --cloud-provider
컴포넌트 플래그를 이용하여 설정되어 있으면
LoadBalancer
유형의 서비스는 클라우드 공급자의 기본 로드 밸런서 구현을 사용한다.
spec.loadBalancerClass
가 지정되면, 지정된 클래스와 일치하는 로드 밸런서
구현이 서비스를 감시하고 있다고 가정한다.
모든 기본 로드 밸런서 구현(예: 클라우드 공급자가 제공하는
로드 밸런서 구현)은 이 필드가 설정된 서비스를 무시한다.
spec.loadBalancerClass
는 LoadBalancer
유형의 서비스에서만 설정할 수 있다.
한 번 설정하면 변경할 수 없다.
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"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/oci-load-balancer-internal: true
[...]
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"
# 접근 로그를 게시하는 간격을 분 단위로 제어. 5분 또는 60분의 간격을 지정.
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
# 로드 밸런서 접근 로그가 저장되는 Amazon S3 버킷의 이름 명시.
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
# Amazon S3 버킷을 생성한 논리적 계층을 지정. 예: `my-bucket-prefix/prod`
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "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"
# 로드 밸런서에 교차-영역(cross-zone) 로드 밸런싱을 사용할 지 여부를 지정
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
# 쉼표로 구분된 key-value 목록은 ELB에
# 추가 태그로 기록됨
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
# 백엔드가 정상인 것으로 간주되는데 필요한 연속적인
# 헬스 체크 성공 횟수이다. 기본값은 2이며, 2와 10 사이여야 한다.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
# 백엔드가 비정상인 것으로 간주되는데 필요한
# 헬스 체크 실패 횟수이다. 기본값은 6이며, 2와 10 사이여야 한다.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
# 개별 인스턴스의 상태 점검 사이의
# 대략적인 간격 (초 단위). 기본값은 10이며, 5와 300 사이여야 한다.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
# 헬스 체크 실패를 의미하는 무 응답의 총 시간 (초 단위)
# 이 값은 service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
# 값 보다 작아야한다. 기본값은 5이며, 2와 60 사이여야 한다.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
# 생성된 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-security-groups: "sg-53fae93f"
# 생성된 ELB에 추가할 추가 보안 그룹 목록
# 이 방법을 사용하면 이전에 생성된 고유 보안 그룹이 그대로 유지되므로,
# 각 ELB가 고유 보안 그룹 ID와 그에 매칭되는 허가 규칙 라인을 소유하여
# 트래픽(서비스 트래픽과 헬스 체크)이 워커 노드로 향할 수 있도록 한다.
# 여기에 기재되는 보안 그룹은 여러 서비스 간 공유될 수 있다.
service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
# 로드 밸런서의 대상 노드를 선택하는 데
# 사용되는 키-값 쌍의 쉼표로 구분된 목록
service.beta.kubernetes.io/aws-load-balancer-target-node-labels: "ingress-gw,gw-name=public-api"
AWS의 네트워크 로드 밸런서 지원
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는 특정 인스턴스 클래스에서만 작동한다. 지원되는 인스턴스 유형 목록은 엘라스틱 로드 밸런싱에 대한 AWS 문서 를 참고한다.클래식 엘라스틱 로드 밸런서와 달리, 네트워크 로드 밸런서 (NLB)는
클라이언트의 IP 주소를 노드로 전달한다. 서비스의 .spec.externalTrafficPolicy
가
Cluster
로 설정되어 있으면, 클라이언트의 IP 주소가 종단 파드로
전파되지 않는다.
.spec.externalTrafficPolicy
를 Local
로 설정하면, 클라이언트 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"
참고:
.spec.loadBalancerSourceRanges
가 설정되어 있지 않으면, 쿠버네티스는
0.0.0.0/0
에서 노드 보안 그룹으로의 트래픽을 허용한다. 노드에 퍼블릭 IP 주소가
있는 경우, 비(non)-NLB 트래픽도 해당 수정된 보안 그룹의
모든 인스턴스에 도달할 수 있다.엘라스틱 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
참고:
ExternalName은 IPv4 주소 문자열을 허용하지만, IP 주소가 아닌 숫자로 구성된 DNS 이름을 허용한다. IPv4 주소와 유사한 ExternalName은 CoreDNS 또는 ingress-nginx에 의해 확인되지 않는데, ExternalName은 정식(canonical) DNS 이름을 지정하기 때문이다. IP 주소를 하드 코딩하려면, 헤드리스(headless) 서비스 사용을 고려한다.my-service.prod.svc.cluster.local
호스트를 검색하면, 클러스터 DNS 서비스는
my.database.example.com
값의 CNAME
레코드를 반환한다. my-service
에 접근하는 것은
다른 서비스와 같은 방식으로 작동하지만, 리다이렉션은 프록시 또는
포워딩을 통하지 않고 DNS 수준에서 발생한다는 중요한
차이점이 있다. 나중에 데이터베이스를 클러스터로 이동하기로 결정한 경우, 해당
파드를 시작하고 적절한 셀렉터 또는 엔드포인트를 추가하고,
서비스의 유형(type)
을 변경할 수 있다.
경고:
HTTP 및 HTTPS를 포함한, 몇몇 일반적인 프로토콜에 ExternalName을 사용하는 것은 문제가 있을 수 있다. ExternalName을 사용하는 경우, 클러스터 내부의 클라이언트가 사용하는 호스트 이름(hostname)이 ExternalName이 참조하는 이름과 다르다.
호스트 이름을 사용하는 프로토콜의 경우, 이러한 차이로 인해 오류가 발생하거나 예기치 않은 응답이 발생할 수 있다.
HTTP 요청에는 오리진(origin) 서버가 인식하지 못하는 Host :
헤더가 있다.
TLS 서버는 클라이언트가 연결된 호스트 이름과 일치하는 인증서를 제공할 수 없다.
외부 IP
하나 이상의 클러스터 노드로 라우팅되는 외부 IP가 있는 경우, 쿠버네티스 서비스는 이러한
externalIPs
에 노출될 수 있다. 서비스 포트에서 외부 IP (목적지 IP)를 사용하여 클러스터로 들어오는 트래픽은
서비스 엔드포인트 중 하나로 라우팅된다. externalIPs
는 쿠버네티스에 의해 관리되지 않으며
클러스터 관리자에게 책임이 있다.
서비스 명세에서, externalIPs
는 모든 ServiceTypes
와 함께 지정할 수 있다.
아래 예에서, 클라이언트는 "80.11.12.10:80
"(외부 IP:포트
)로 "my-service
"에 접근할 수 있다.
apiVersion: v1
kind: Service
metadata:
app.kubernetes.io/name: MyApp
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
세션 스티킹(stickiness)
특정 클라이언트로부터의 연결이 매번 동일한 파드로 전달되도록 하고 싶다면, 클라이언트의 IP 주소 기반으로 세션 어피니티를 구성할 수 있다. 더 자세한 정보는 세션 어피니티를 참고한다.
API 오브젝트
서비스는 쿠버네티스 REST API의 최상위 리소스이다. 서비스 API 오브젝트에 대한 자세한 내용을 참고할 수 있다.
가상 IP 주소 메커니즘
가상 IP 및 서비스 프록시에서 가상 IP 주소를 갖는 서비스를 노출하기 위해 쿠버네티스가 제공하는 메커니즘에 대해 알아본다.
다음 내용
- 서비스와 애플리케이션 연결 알아보기
- 인그레스에 대해 알아보기
- 엔드포인트슬라이스에 대해 알아보기
추가적으로,
- 가상 IP 및 서비스 프록시를 살펴보기
- 서비스(Service) API에 대한 API 레퍼런스 살펴보기
- 엔드포인트(Endpoints) API에 대한 API 레퍼런스 살펴보기
- 엔드포인트슬라이스 API에 대한 API 레퍼런스 살펴보기
6.2 - 인그레스(Ingress)
Kubernetes v1.19 [stable]
클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리함.
인그레스는 부하 분산, SSL 종료, 명칭 기반의 가상 호스팅을 제공할 수 있다.
용어
이 가이드는 용어의 명확성을 위해 다음과 같이 정의한다.
- 노드(Node): 클러스터의 일부이며, 쿠버네티스에 속한 워커 머신.
- 클러스터(Cluster): 쿠버네티스에서 관리되는 컨테이너화 된 애플리케이션을 실행하는 노드 집합. 이 예시와 대부분의 일반적인 쿠버네티스 배포에서 클러스터에 속한 노드는 퍼블릭 인터넷의 일부가 아니다.
- 에지 라우터(Edge router): 클러스터에 방화벽 정책을 적용하는 라우터. 이것은 클라우드 공급자 또는 물리적 하드웨어의 일부에서 관리하는 게이트웨이일 수 있다.
- 클러스터 네트워크(Cluster network): 쿠버네티스 네트워킹 모델에 따라 클러스터 내부에서 통신을 용이하게 하는 논리적 또는 물리적 링크 집합.
- 서비스: 레이블 셀렉터를 사용해서 파드 집합을 식별하는 쿠버네티스 서비스. 달리 언급하지 않으면 서비스는 클러스터 네트워크 내에서만 라우팅 가능한 가상 IP를 가지고 있다고 가정한다.
인그레스란?
인그레스는 클러스터 외부에서 클러스터 내부 서비스로 HTTP와 HTTPS 경로를 노출한다. 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 의해 컨트롤된다.
다음은 인그레스가 모든 트래픽을 하나의 서비스로 보내는 간단한 예시이다.
인그레스는 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL / TLS 종료 그리고 이름-기반의 가상 호스팅을 제공하도록 구성할 수 있다. 인그레스 컨트롤러는 일반적으로 로드 밸런서를 사용해서 인그레스를 수행할 책임이 있으며, 트래픽을 처리하는데 도움이 되도록 에지 라우터 또는 추가 프런트 엔드를 구성할 수도 있다.
인그레스는 임의의 포트 또는 프로토콜을 노출시키지 않는다. HTTP와 HTTPS 이외의 서비스를 인터넷에 노출하려면 보통 Service.Type=NodePort 또는 Service.Type=LoadBalancer 유형의 서비스를 사용한다.
전제 조건들
인그레스 컨트롤러가 있어야 인그레스를 충족할 수 있다. 인그레스 리소스만 생성한다면 효과가 없다.
ingress-nginx와 같은 인그레스 컨트롤러를 배포해야 할 수도 있다. 여러 인그레스 컨트롤러 중에서 선택할 수도 있다.
이상적으로, 모든 인그레스 컨트롤러는 참조 사양이 맞아야 한다. 실제로, 다양한 인그레스 컨트롤러는 조금 다르게 작동한다.
참고:
인그레스 컨트롤러의 설명서를 검토하여 선택 시 주의 사항을 이해해야 한다.인그레스 리소스
최소한의 인그레스 리소스 예제:
apiVersion: networking.k8s.io/v1