워크로드는 쿠버네티스에서 구동되는 애플리케이션이다.
워크로드가 단일 컴포넌트이거나 함께 작동하는 여러 컴포넌트이든 관계없이, 쿠버네티스에서는 워크로드를 일련의
파드 집합 내에서 실행한다.
쿠버네티스에서 Pod 는 클러스터에서 실행 중인 컨테이너
집합을 나타낸다.
쿠버네티스 파드에는 정의된 라이프사이클이 있다.
예를 들어, 일단 파드가 클러스터에서 실행되고 나서
해당 파드가 동작 중인 노드에
심각한 오류가 발생하면 해당 노드의 모든 파드가 실패한다. 쿠버네티스는 이 수준의 실패를
최종(final)으로 취급한다. 사용자는 향후 노드가 복구되는 것과 상관 없이 Pod 를 새로 생성해야 한다.
그러나, 작업이 훨씬 쉽도록, 각 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 의 그룹을 실행하되
모든 파드가 가용한 경우가 아닌 경우 멈추고 싶다면(아마도 높은 처리량의 분산 처리를 하는 상황 같은),
사용자는 해당 기능을 제공하는 확장을 구현하거나 설치할 수 있다.
다음 내용
각 리소스에 대해 읽을 수 있을 뿐만 아니라, 리소스와 관련된 특정 작업에 대해서도 알아볼 수 있다.
일단 애플리케이션이 실행되면, 인터넷에서 서비스로
사용하거나, 웹 애플리케이션의 경우에만
인그레스(Ingress)를 이용하여 사용할 수 있다.
1 - 파드
파드(Pod) 는 쿠버네티스에서 생성하고 관리할 수 있는 배포 가능한 가장 작은 컴퓨팅 단위이다.
파드 (고래 떼(pod of whales)나 콩꼬투리(pea pod)와 마찬가지로)는 하나 이상의
컨테이너의 그룹이다. 이 그룹은 스토리지 및 네트워크를 공유하고, 해당 컨테이너를 구동하는 방식에 대한 명세를 갖는다. 파드의 콘텐츠는 항상 함께 배치되고,
함께 스케줄되며, 공유 콘텍스트에서 실행된다. 파드는
애플리케이션 별 "논리 호스트"를 모델링한다. 여기에는 상대적으로 밀접하게 결합된 하나 이상의
애플리케이션 컨테이너가 포함된다.
클라우드가 아닌 콘텍스트에서, 동일한 물리 또는 가상 머신에서 실행되는 애플리케이션은 동일한 논리 호스트에서 실행되는 클라우드 애플리케이션과 비슷하다.
애플리케이션 컨테이너와 마찬가지로, 파드에는
파드 시작 중에 실행되는 초기화 컨테이너가
포함될 수 있다. 클러스터가 제공하는 경우, 디버깅을 위해
임시 컨테이너를
삽입할 수도 있다.
파드란 무엇인가?
참고:도커가 가장 일반적으로 잘 알려진
컨테이너 런타임이지만,
쿠버네티스는 도커 외에도 다양한 컨테이너 런타임을 지원하며,
파드를 설명할 때 도커 관련 용어를 사용하면 더 쉽게 설명할 수 있다.
파드의 공유 콘텍스트는 리눅스 네임스페이스, 컨트롤 그룹(cgroup) 및
도커 컨테이너를 격리하는 것과 같이 잠재적으로 다른 격리 요소들이다.
파드의 콘텍스트 내에서 개별 애플리케이션은
추가적으로 하위 격리가 적용된다.
도커 개념 측면에서, 파드는 공유 네임스페이스와 공유 파일시스템 볼륨이
있는 도커 컨테이너 그룹과 비슷하다.
단일 컨테이너를 실행하는 파드. "파드 당 하나의 컨테이너" 모델은
가장 일반적인 쿠버네티스 유스케이스이다. 이 경우, 파드를 단일 컨테이너를 둘러싼
래퍼(wrapper)로 생각할 수 있다. 쿠버네티스는 컨테이너를 직접 관리하는 대신
파드를 관리한다.
함께 작동해야 하는 여러 컨테이너를 실행하는 파드. 파드는
밀접하게 결합되어 있고 리소스를 공유해야 하는 함께 배치된 여러 개의 컨테이너로
구성된 애플리케이션을 캡슐화할 수 있다. 이런 함께 배치된 컨테이너는
하나의 결합된 서비스 단위를 형성한다. 예를 들어, 하나의 컨테이너는 공유 볼륨에
저장된 데이터를 퍼블릭에 제공하는 반면, 별도의 사이드카 컨테이너는
해당 파일을 새로 고치거나 업데이트한다.
파드는 이러한 컨테이너, 스토리지 리소스, 임시 네트워크 ID를
단일 단위로 함께 래핑한다.
참고: 단일 파드에서 함께 배치된 또는 함께 관리되는 여러 컨테이너를 그룹화하는 것은
비교적 고급 유스케이스이다. 이 패턴은 컨테이너가 밀접하게 결합된
특정 인스턴스에서만 사용해야 한다.
각 파드는 특정 애플리케이션의 단일 인스턴스를 실행하기 위한 것이다. 더 많은
인스턴스를 실행하여 더 많은 전체 리소스를 제공하기 위해 애플리케이션을
수평적으로 확장하려면, 각 인스턴스에 하나씩, 여러 파드를 사용해야 한다.
쿠버네티스에서는 이를 일반적으로 레플리케이션 이라고 한다.
복제된 파드는 일반적으로 워크로드 리소스와
해당 컨트롤러에 의해 그룹으로 생성되고 관리된다.
쿠버네티스가 워크로드 리소스와 해당 컨트롤러를 사용하여 애플리케이션 스케일링과
자동 복구를 구현하는 방법에 대한 자세한 내용은
파드와 컨트롤러를 참고한다.
파드가 여러 컨테이너를 관리하는 방법
파드는 응집력있는 서비스 단위를 형성하는 여러 협력 프로세스(컨테이너)를
지원하도록 설계되었다. 파드의 컨테이너는 클러스터의 동일한 물리 또는 가상 머신에서
자동으로 같은 위치에 배치되고 함께 스케줄된다. 컨테이너는
리소스와 의존성을 공유하고, 서로 통신하고, 종료 시기와 방법을
조정할 수 있다.
예를 들어, 다음 다이어그램에서와 같이
공유 볼륨의 파일에 대한 웹 서버 역할을 하는 컨테이너와, 원격 소스에서 해당 파일을 업데이트하는
별도의 "사이드카" 컨테이너가 있을 수 있다.
일부 파드에는 앱 컨테이너 뿐만 아니라 초기화 컨테이너를 갖고 있다. 초기화 컨테이너는 앱 컨테이너가 시작되기 전에 실행되고 완료된다.
파드는 기본적으로 파드에 속한 컨테이너에 네트워킹과 스토리지라는
두 가지 종류의 공유 리소스를 제공한다.
파드 작업
사용자가 쿠버네티스에서 직접 개별 파드를 만드는 경우는 거의 없다. 싱글톤 파드도 마찬가지이다. 이는
파드가 상대적으로 일시적인, 일회용 엔티티로 설계되었기 때문이다. 파드가
생성될 때(사용자가 직접 또는
컨트롤러가 간접적으로), 새 파드는
클러스터의 노드에서 실행되도록 스케줄된다.
파드는 파드 실행이 완료되거나, 파드 오브젝트가 삭제되거나,
리소스 부족으로 인해 파드가 축출 되거나, 노드가 실패할 때까지 해당 노드에 남아있다.
참고: 파드에서 컨테이너를 다시 시작하는 것과 파드를 다시 시작하는 것을 혼동해서는 안된다. 파드는
프로세스가 아니라 컨테이너를 실행하기 위한 환경이다. 파드는
삭제될 때까지 유지된다.
파드 오브젝트에 대한 매니페스트를 만들 때, 지정된 이름이 유효한
DNS 서브도메인 이름인지 확인한다.
파드와 컨트롤러
워크로드 리소스를 사용하여 여러 파드를 만들고 관리할 수 있다. 리소스에 대한 컨트롤러는
파드 장애 시 복제 및 롤아웃과 자동 복구를
처리한다. 예를 들어, 노드가 실패하면, 컨트롤러는 해당 노드의 파드가 작동을 중지했음을
인식하고 대체 파드를 생성한다. 스케줄러는
대체 파드를 정상 노드에 배치한다.
파드 템플릿을 수정하거나 새로운 파드 템플릿으로 바꿔도 이미 존재하는
파드에는 직접적인 영향을 주지 않는다. 워크로드 리소스의 파드 템플릿을
변경하는 경우, 해당 리소스는 수정된 템플릿을 사용하는 대체 파드를 생성해야 한다.
예를 들어, 스테이트풀셋 컨트롤러는 실행 중인 파드가 각 스테이트풀셋 오브젝트에 대한 현재
파드 템플릿과 일치하는지 확인한다. 스테이트풀셋을 수정하여 파드 템플릿을
변경하면, 스테이트풀셋이 업데이트된 템플릿을 기반으로 새로운 파드를 생성하기 시작한다.
결국, 모든 이전의 파드가 새로운 파드로 교체되고, 업데이트가 완료된다.
각 워크로드 리소스는 파드 템플릿의 변경 사항을 처리하기 위한 자체 규칙을 구현한다.
스테이트풀셋에 대해 자세히 알아 보려면,
스테이트풀셋 기본 튜토리얼에서 업데이트 전략을 읽어본다.
노드에서 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 주소를 가지며
특별한 구성 없이 IPC로 통신할 수 없다.
다른 파드에서 실행되는 컨테이너와 상호 작용하려는 컨테이너는 IP 네트워킹을
사용하여 통신할 수 있다.
파드 내의 컨테이너는 시스템 호스트명이 파드에 대해 구성된
name 과 동일한 것으로 간주한다. 네트워킹 섹션에 이에 대한
자세한 내용이 있다.
컨테이너에 대한 특권 모드
리눅스에서, 파드의 모든 컨테이너는 컨테이너 명세의 보안 컨텍스트에 있는 privileged (리눅스) 플래그를 사용하여 특권 모드를 활성화할 수 있다. 이는 네트워크 스택 조작이나 하드웨어 장치 접근과 같은 운영 체제 관리 기능을 사용하려는 컨테이너에 유용하다.
클러스터가 WindowsHostProcessContainers 기능을 활성화하였다면, 파드 스펙의 보안 컨텍스트의 windowsOptions.hostProcess 에 의해 윈도우 HostProcess 파드를 생성할 수 있다. 이러한 모든 컨테이너는 윈도우 HostProcess 컨테이너로 실행해야 한다. HostProcess 파드는 직접적으로 호스트에서 실행하는 것으로, 리눅스 특권있는 컨테이너에서 수행되는 관리 태스크 수행에도 사용할 수 있다. 파드의 모든 컨테이너는 윈도우 HostProcess 컨테이너로 반드시 실행해야 한다. HostProcess 파드는 호스트에서 직접 실행되며 리눅스 특권있는 컨테이너에서 수행되는 것과 같은 관리 작업을 수행하는데도 사용할 수 있다.
참고: 이 설정을 사용하려면 사용자의 컨테이너 런타임이 특권이 있는 컨테이너의 개념을 지원해야 한다.
정적 파드
정적 파드 는 API 서버가
관찰하는 대신 특정 노드의 kubelet 데몬에 의해 직접
관리된다.
대부분의 파드는 컨트롤 플레인(예를 들어,
디플로이먼트)에 의해 관리되고, 정적 파드의
경우, kubelet이 각 정적 파드를 직접 감독한다(실패하면 다시 시작한다).
정적 파드는 항상 특정 노드의 Kubelet 하나에 바인딩된다.
정적 파드의 주요 용도는 자체 호스팅 컨트롤 플레인을 실행하는 것이다. 즉,
kubelet을 사용하여 개별 컨트롤 플레인 컴포넌트를 감독한다.
kubelet은 자동으로 각 정적 파드에 대한 쿠버네티스 API 서버에서 미러 파드를
생성하려고 한다.
즉, 노드에서 실행되는 파드는 API 서버에서 보이지만,
여기에서 제어할 수는 없다는 의미이다.
참고: 스태틱 파드의 스펙(spec)은 다른 API 오브젝트
(예를 들면, 서비스어카운트,
컨피그맵,
시크릿, 등)가 참조할 수 없다.
컨테이너 프로브
_프로브_는 컨테이너의 kubelet에 의해 주기적으로 실행되는 진단이다. 진단을 수행하기 위하여 kubelet은 다음과 같은 작업을 호출할 수 있다.
이 페이지에서는 파드의 라이프사이클을 설명한다. 파드는 정의된 라이프사이클을 따른다.
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 배열을 가진다.
Ready: 파드는 요청을 처리할 수 있으며 일치하는 모든 서비스의 로드
밸런싱 풀에 추가되어야 한다.
필드 이름
설명
type
이 파드 조건의 이름이다.
status
가능한 값이 "True", "False", 또는 "Unknown"으로, 해당 조건이 적용 가능한지 여부를 나타낸다.
lastProbeTime
파드 조건이 마지막으로 프로브된 시간의 타임스탬프이다.
lastTransitionTime
파드가 한 상태에서 다른 상태로 전환된 마지막 시간에 대한 타임스탬프이다.
reason
조건의 마지막 전환에 대한 이유를 나타내는 기계가 판독 가능한 UpperCamelCase 텍스트이다.
message
마지막 상태 전환에 대한 세부 정보를 나타내는 사람이 읽을 수 있는 메시지이다.
파드의 준비성(readiness)
FEATURE STATE: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:nulllastTransitionTime:2018-01-01T00:00:00Z- type:"www.example.com/feature-1"# 추가적인 PodConditionstatus:"False"lastProbeTime:nulllastTransitionTime:2018-01-01T00:00:00ZcontainerStatuses:- containerID:docker://abcd...ready:true...
kubectl patch 명령어는 아직 오브젝트 상태 패치(patching)를 지원하지 않는다.
이러한 status.conditions 을 파드에 설정하려면 애플리케이션과
오퍼레이터의
PATCH 액션을 필요로 한다.
쿠버네티스 클라이언트 라이브러리를
사용해서 파드 준비성에 대한 사용자 지정 파드 조건을 설정하는 코드를 작성할 수 있다.
사용자 지정 조건을 사용하는 파드의 경우, 다음 두 조건이 모두 적용되는
경우에 만 해당 파드가 준비된 것으로 평가된다.
파드 내의 모든 컨테이너들이 준비 상태이다.
readinessGates에 지정된 모든 조건들이 True 이다.
파드의 컨테이너가 Ready 이나 적어도 한 개의 사용자 지정 조건이 빠졌거나 False 이면,
kubelet은 파드의 조건을 ContainerReady 로 설정한다.
컨테이너 프로브(probe)
프로브는
컨테이너에서 kubelet에 의해
주기적으로 수행되는 진단(diagnostic)이다.
진단을 수행하기 위해서,
kubelet은 컨테이너에 의해서 구현된
핸들러를 호출한다.
핸들러에는 다음과 같이 세 가지 타입이 있다.
ExecAction은
컨테이너 내에서 지정된 명령어를 실행한다.
명령어가 상태 코드 0으로 종료되면 진단이 성공한 것으로 간주한다.
TCPSocketAction은
지정된 포트에서 컨테이너의 IP주소에 대해 TCP 검사를 수행한다.
포트가 활성화되어 있다면 진단이 성공한 것으로 간주한다.
HTTPGetAction은
지정한 포트 및 경로에서 컨테이너의 IP주소에
대한 HTTP GET 요청을 수행한다. 응답의 상태 코드가 200 이상 400 미만이면
진단이 성공한 것으로 간주한다.
각 probe는 다음 세 가지 결과 중 하나를 가진다.
Success: 컨테이너가 진단을 통과함.
Failure: 컨테이너가 진단에 실패함.
Unknown: 진단 자체가 실패하였으므로 아무런 액션도 수행되면 안됨.
kubelet은 실행 중인 컨테이너들에 대해서 선택적으로 세 가지 종류의 프로브를 수행하고
그에 반응할 수 있다.
livenessProbe: 컨테이너가 동작 중인지 여부를 나타낸다. 만약
활성 프로브(liveness probe)에 실패한다면, kubelet은 컨테이너를 죽이고, 해당 컨테이너는
재시작 정책의 대상이 된다. 만약 컨테이너가
활성 프로브를 제공하지 않는 경우, 기본 상태는 Success이다.
readinessProbe: 컨테이너가 요청을 처리할 준비가 되었는지 여부를 나타낸다.
만약 준비성 프로브(readiness probe)가 실패한다면, 엔드포인트 컨트롤러는
파드에 연관된 모든 서비스들의 엔드포인트에서 파드의 IP주소를 제거한다. 준비성 프로브의
초기 지연 이전의 기본 상태는 Failure이다. 만약 컨테이너가 준비성 프로브를
지원하지 않는다면, 기본 상태는 Success이다.
startupProbe: 컨테이너 내의 애플리케이션이 시작되었는지를 나타낸다.
스타트업 프로브(startup probe)가 주어진 경우, 성공할 때까지 다른 나머지 프로브는
활성화되지 않는다. 만약 스타트업 프로브가 실패하면, kubelet이 컨테이너를 죽이고,
컨테이너는 재시작 정책에 따라 처리된다. 컨테이너에 스타트업
프로브가 없는 경우, 기본 상태는 Success이다.
만약 컨테이너 속 프로세스가 어떠한 이슈에 직면하거나 건강하지 못한
상태(unhealthy)가 되는 등 프로세스 자체의 문제로 중단될 수 있더라도, 활성 프로브가
반드시 필요한 것은 아니다. 그 경우에는 kubelet이 파드의 restartPolicy에
따라서 올바른 대처를 자동적으로 수행할 것이다.
프로브가 실패한 후 컨테이너가 종료되거나 재시작되길 원한다면, 활성 프로브를
지정하고, restartPolicy를 항상(Always) 또는 실패 시(OnFailure)로 지정한다.
언제 준비성 프로브를 사용해야 하는가?
FEATURE STATE:Kubernetes v1.0 [stable]
프로브가 성공한 경우에만 파드에 트래픽 전송을 시작하려고 한다면,
준비성 프로브를 지정하길 바란다. 이 경우에서는, 준비성 프로브가 활성 프로브와 유사해
보일 수도 있지만, 스팩에 준비성 프로브가 존재한다는 것은 파드가
트래픽을 받지 않는 상태에서 시작되고 프로브가 성공하기 시작한 이후에만
트래픽을 받는다는 뜻이다.
만약 컨테이너가 유지 관리를 위해서 자체 중단되게 하려면,
준비성 프로브를 지정하길 바란다.
준비성 프로브는 활성 프로브와는 다르게 준비성에 특정된 엔드포인트를 확인한다.
만약 애플리케이션이 백엔드 서비스에 엄격한 의존성이 있다면,
활성 프로브와 준비성 프로브 모두 활용할 수도 있다. 활성 프로브는 애플리케이션 스스로가 건강한 상태면
통과하지만, 준비성 프로브는 추가적으로 요구되는 각 백-엔드 서비스가 가용한지 확인한다. 이를 이용하여,
오류 메시지만 응답하는 파드로
트래픽이 가는 것을 막을 수 있다.
만약 컨테이너가 시동 시 대량 데이터의 로딩, 구성 파일, 또는
마이그레이션에 대한 작업을
수행해야 한다면, 스타트업 프로브를 사용하면 된다. 그러나, 만약
failed 애플리케이션과 시동 중에 아직 데이터를 처리하고 있는 애플리케이션을 구분하여 탐지하고
싶다면, 준비성 프로브를 사용하는 것이 더 적합할 것이다.
참고: 파드가 삭제될 때 요청들을 흘려 보내기(drain) 위해
준비성 프로브가 꼭 필요한 것은 아니다. 삭제 시에, 파드는
프로브의 존재 여부와 무관하게 자동으로 스스로를 준비되지 않은 상태(unready)로 변경한다.
파드는 파드 내의 모든 컨테이너들이 중지될 때까지 준비되지 않은 상태로
남아 있다.
언제 스타트업 프로브를 사용해야 하는가?
FEATURE STATE: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이 정상 종료를 시작하는 동시에, 컨트롤 플레인은
구성된 셀렉터가 있는
서비스를 나타내는
엔드포인트(Endpoint)(그리고, 활성화된 경우, 엔드포인트슬라이스(EndpointSlice)) 오브젝트에서 종료된 파드를 제거한다.
레플리카셋(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에 남아 있다.
컨트롤 플레인은 파드 수가 구성된 임계값(kube-controller-manager에서
terminated-pod-gc-threshold 에 의해 결정됨)을 초과할 때 종료된 파드(Succeeded 또는
Failed 단계 포함)를 정리한다.
이렇게 하면 시간이 지남에 따라 파드가 생성되고 종료될 때 리소스 유출이 방지된다.
이 페이지는 초기화 컨테이너에 대한 개요를 제공한다. 초기화 컨테이너는
파드의 앱 컨테이너들이 실행되기 전에 실행되는 특수한 컨테이너이며, 앱 이미지에는 없는
유틸리티 또는 설정 스크립트 등을 포함할 수 있다.
초기화 컨테이너는 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) 새로운 이미지를 만들 필요가 없다.
애플리케이션 이미지 빌더와 디플로이어 역할은 독립적으로 동작될 수 있어서
공동의 단일 앱 이미지 형태로 빌드될 필요가 없다.
초기화 컨테이너는 앱 컨테이너와 다른 파일 시스템 뷰를 가지도록 리눅스 네임스페이스를 사용한다.
결과적으로, 초기화 컨테이너에는 앱 컨테이너가 가질 수 없는
시크릿에 접근 권한이 주어질 수 있다.
앱 컨테이너들은 병렬로 실행되는 반면, 초기화 컨테이너들은 어떠한 앱
컨테이너라도 시작되기 전에 실행 완료되어야 하므로, 초기화 컨테이너는 사전 조건들이
충족될 때까지 앱 컨테이너가 시동되는 것을 막거나 지연시키는 간편한 방법을 제공한다.
초기화 컨테이너는 앱 컨테이너 이미지의 보안성을 떨어뜨릴 수도 있는 유틸리티 혹은 커스텀 코드를 안전하게
실행할 수 있다. 불필요한 툴들을 분리한 채로 유지함으로써 앱 컨테이너 이미지의 공격에 대한
노출을 제한할 수 있다.
초기화 컨테이너들이 완료되는 것과 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 이상에
적용된다. 이전 버전의 쿠버네티스를 사용하는 경우 해당 쿠버네티스 버전의 문서를
참고한다.
사용자는 토폴로지 분배 제약 조건 을 사용해서 지역, 영역, 노드 그리고 기타 사용자-정의 토폴로지 도메인과 같이 장애-도메인으로 설정된 클러스터에 걸쳐 파드가 분산되는 방식을 제어할 수 있다. 이를 통해 고가용성뿐만 아니라, 효율적인 리소스 활용의 목적을 이루는 데 도움이 된다.
참고: v1.18 이전 버전의 쿠버네티스에서는 파드 토폴로지 분배 제약조건을 사용하려면
API 서버와
스케줄러에서
EvenPodsSpread기능 게이트를
활성화해야 한다
필수 구성 요소
노드 레이블
토폴로지 분배 제약 조건은 노드 레이블을 의지해서 각 노드가 속한 토폴로지 도메인(들)을 인식한다. 예를 들어, 노드에 다음과 같은 레이블을 가지고 있을 수 있다. node=node1,zone=us-east-1a,region=us-east-1
다음 레이블이 있고, 4개 노드를 가지는 클러스터가 있다고 가정한다.
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready <none> 4m26s v1.16.0 node=node1,zone=zoneA
node2 Ready <none> 3m58s v1.16.0 node=node2,zone=zoneA
node3 Ready <none> 3m17s v1.16.0 node=node3,zone=zoneB
node4 Ready <none> 2m43s v1.16.0 node=node4,zone=zoneB
그러면 클러스터는 논리적으로 다음과 같이 보이게 된다.
graph TB
subgraph "zoneB"
n3(Node3)
n4(Node4)
end
subgraph "zoneA"
n1(Node1)
n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4 k8s;
class zoneA,zoneB cluster;
레이블을 수동으로 적용하는 대신에, 사용자는 대부분의 클러스터에서 자동으로 생성되고 채워지는 잘-알려진 레이블을 재사용할 수 있다.
파드의 분배 제약 조건
API
API 필드 pod.spec.topologySpreadConstraints 는 다음과 같이 정의된다.
사용자는 하나 또는 다중 topologySpreadConstraint 를 정의해서 kube-scheduler 에게 클러스터에 걸쳐 있는 기존 파드와 시작하는 각각의 파드와 연관하여 배치하는 방법을 명령할 수 있다. 필드는 다음과 같다.
maxSkew 는 파드가 균등하지 않게 분산될 수 있는 정도를 나타낸다.
이것은 0보다는 커야 한다. 그 의미는 whenUnsatisfiable 의 값에 따라 다르다.
whenUnsatisfiable 이 "DoNotSchedule"과 같을 때, maxSkew 는
대상 토폴로지에서 일치하는 파드 수와 전역 최솟값
(토폴로지 도메인에서 레이블 셀렉터와 일치하는 최소 파드 수. 예를 들어 3개의 영역에 각각 0, 2, 3개의 일치하는 파드가 있으면, 전역 최솟값은 0)
사이에 허용되는 최대 차이이다.
whenUnsatisfiable 이 "ScheduleAnyway"와 같으면, 스케줄러는
왜곡을 줄이는데 도움이 되는 토폴로지에 더 높은 우선 순위를 부여한다.
topologyKey 는 노드 레이블의 키다. 만약 두 노드가 이 키로 레이블이 지정되고, 레이블이 동일한 값을 가진다면 스케줄러는 두 노드를 같은 토폴로지에 있는것으로 여기게 된다. 스케줄러는 각 토폴로지 도메인에 균형잡힌 수의 파드를 배치하려고 시도한다.
whenUnsatisfiable 는 분산 제약 조건을 만족하지 않을 경우에 처리하는 방법을 나타낸다.
DoNotSchedule (기본값)은 스케줄러에 스케줄링을 하지 말라고 알려준다.
ScheduleAnyway 는 스케줄러에게 차이(skew)를 최소화하는 노드에 높은 우선 순위를 부여하면서, 스케줄링을 계속하도록 지시한다.
labelSelector 는 일치하는 파드를 찾는데 사용된다. 이 레이블 셀렉터와 일치하는 파드의 수를 계산하여 해당 토폴로지 도메인에 속할 파드의 수를 결정한다. 자세한 내용은 레이블 셀렉터를 참조한다.
파드에 2개 이상의 topologySpreadConstraint가 정의되어 있으면, 각 제약 조건은 AND로 연결된다 - kube-scheduler는 새로운 파드의 모든 제약 조건을 만족하는 노드를 찾는다.
사용자는 kubectl explain Pod.spec.topologySpreadConstraints 를 실행해서 이 필드에 대한 자세한 내용을 알 수 있다.
예시: 단수 토폴로지 분배 제약 조건
4개 노드를 가지는 클러스터에 foo:bar 가 레이블된 3개의 파드가 node1, node2 그리고 node3에 각각 위치한다고 가정한다.
graph BT
subgraph "zoneB"
p3(Pod) --> n3(Node3)
n4(Node4)
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class zoneA,zoneB cluster;
신규 파드가 기존 파드와 함께 영역에 걸쳐서 균등하게 분배되도록 하려면, 스펙(spec)은 다음과 같이 주어질 수 있다.
topologyKey: zone 는 "zone:<any value>" 레이블 쌍을 가지는 노드에 대해서만 균등한 분배를 적용하는 것을 의미한다. whenUnsatisfiable: DoNotSchedule 은 만약 들어오는 파드가 제약 조건을 만족시키지 못하면 스케줄러에게 pending 상태를 유지하도록 지시한다.
만약 스케줄러가 이 신규 파드를 "zoneA"에 배치하면 파드 분포는 [3, 1]이 되며, 따라서 실제 차이(skew)는 2 (3 - 1)가 되어 maxSkew: 1 를 위반하게 된다. 이 예시에서는 들어오는 파드는 오직 "zoneB"에만 배치할 수 있다.
graph BT
subgraph "zoneB"
p3(Pod) --> n3(Node3)
p4(mypod) --> n4(Node4)
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class p4 plain;
class zoneA,zoneB cluster;
OR
graph BT
subgraph "zoneB"
p3(Pod) --> n3(Node3)
p4(mypod) --> n3
n4(Node4)
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class p4 plain;
class zoneA,zoneB cluster;
사용자는 파드 스펙을 조정해서 다음과 같은 다양한 요구사항을 충족할 수 있다.
maxSkew 를 "2" 보다 큰 값으로 변경해서 들어오는 파드들이 "zoneA"에도 배치할 수 있도록 한다.
topologyKey 를 "node"로 변경해서 파드가 영역이 아닌, 노드에 걸쳐 고르게 분산할 수 있게 한다. 위의 예시에서 만약 maxSkew 가 "1"로 유지되면 들어오는 파드는 오직 "node4"에만 배치할 수 있다.
whenUnsatisfiable: DoNotSchedule 에서 whenUnsatisfiable: ScheduleAnyway 로 변경하면 들어오는 파드는 항상 다른 스케줄링 API를 충족한다는 가정하에 스케줄할 수 있도록 보장한다. 그러나 일치하는 파드가 적은 토폴로지 도메인에 배치되는 것이 좋다. (이 선호도는 리소스 사용 비율 등과 같은 다른 내부 스케줄링 우선순위와 공동으로 정규화 된다는 것을 알아두자.)
예시: 다중 토폴로지 분배 제약 조건
4개 노드를 가지는 클러스터에 foo:bar 가 레이블된 3개의 파드가 node1, node2 그리고 node3에 각각 위치한다고 가정한다.
graph BT
subgraph "zoneB"
p3(Pod) --> n3(Node3)
n4(Node4)
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class p4 plain;
class zoneA,zoneB cluster;
사용자는 2개의 TopologySpreadConstraints를 사용해서 영역과 노드에 파드를 분배하는 것을 제어할 수 있다.
이 경우에는, 첫 번째 제약 조건에 부합시키려면, 신규 파드는 오직 "zoneB"에만 배치할 수 있다. 두 번째 제약 조건에서는 신규 파드는 오직 "node4"에만 배치할 수 있다. 그런 다음 두 가지 제약 조건의 결과는 AND 가 되므로, 실행 가능한 유일한 옵션은 "node4"에 배치하는 것이다.
다중 제약 조건은 충돌로 이어질 수 있다. 3개의 노드를 가지는 클러스터 하나가 2개의 영역에 걸쳐 있다고 가정한다.
graph BT
subgraph "zoneB"
p4(Pod) --> n3(Node3)
p5(Pod) --> n3
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n1
p3(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3,p4,p5 k8s;
class zoneA,zoneB cluster;
만약 사용자가 "two-constraints.yaml" 을 이 클러스터에 적용하면, "mypod"가 Pending 상태로 유지되는 것을 알게 된다. 이러한 이유는, 첫 번째 제약 조건을 충족하기 위해 "mypod"는 오직 "zoneB"에만 놓을 수 있다. 두 번째 제약 조건에서는 "mypod"는 오직 "node2"에만 놓을 수 있다. 그러면 "zoneB"와 "node2"의 공동 결과는 아무것도 반환되지 않는다.
이 상황을 극복하기 위해서는 사용자가 maxSkew 의 증가 또는 whenUnsatisfiable: ScheduleAnyway 를 사용하도록 제약 조건 중 하나를 수정할 수 있다.
노드 어피니티(Affinity) 및 노드 셀렉터(Selector)와의 상호 작용
스케줄러는 신규 파드에 spec.nodeSelector 또는 spec.affinity.nodeAffinity가 정의되어 있는 경우, 부합하지 않는 노드들을 차이(skew) 계산에서 생략한다.
zoneA 에서 zoneC에 걸쳐있고, 5개의 노드를 가지는 클러스터가 있다고 가정한다.
graph BT
subgraph "zoneB"
p3(Pod) --> n3(Node3)
n4(Node4)
end
subgraph "zoneA"
p1(Pod) --> n1(Node1)
p2(Pod) --> n2(Node2)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class p4 plain;
class zoneA,zoneB cluster;
graph BT
subgraph "zoneC"
n5(Node5)
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n5 k8s;
class zoneC cluster;
그리고 알다시피 "zoneC"는 제외해야 한다. 이 경우에, "mypod"가 "zoneC"가 아닌 "zoneB"에 배치되도록 yaml을 다음과 같이 구성할 수 있다. 마찬가지로 spec.nodeSelector 도 존중된다.
스케줄러는 클러스터에 있는 모든 영역(zone) 또는 다른 토폴로지 도메인에 대한 사전 지식이 없다. 스케줄링은 클러스터의 기존 노드에서 결정된다. 노드 풀(또는 노드 그룹)이 0개의 노드로 스케일(scale)되고 사용자는 노드가 확장될 것으로 예상하는 경우, 자동 스케일되는 클러스터에서 문제가 발생할 수 있다. 이러한 토폴로지 도메인은 스케줄링에서 해당 도메인에 노드가 하나 이상 있을 때까지 고려되지 않을 것이기 때문이다.
기타 눈에 띄는 의미(semantics)
여기에 주목할만한 몇 가지 암묵적인 규칙이 있다.
신규 파드와 같은 네임스페이스를 갖는 파드만이 매칭의 후보가 된다.
topologySpreadConstraints[*].topologyKey 가 없는 노드는 무시된다. 이것은 다음을 의미한다.
이러한 노드에 위치한 파드는 "maxSkew" 계산에 영향을 미치지 않는다. - 위의 예시에서, "node1"은 "zone" 레이블을 가지고 있지 않다고 가정하면, 파드 2개는 무시될 것이고, 이런 이유로 신규 파드는 "zoneA"로 스케줄된다.
신규 파드는 이런 종류의 노드에 스케줄 될 기회가 없다. - 위의 예시에서, 레이블로 {zone-typo: zoneC} 를 가지는 "node5"가 클러스터에 편입한다고 가정하면, 레이블 키에 "zone"이 없기 때문에 무시하게 된다.
들어오는 파드의 topologySpreadConstraints[*].labelSelector 와 자체 레이블과 일치하지 않을 경우 어떻게 되는지 알고 있어야 한다. 위의 예시에서, 만약 들어오는 파드의 레이블을 제거하더라도 여전히 제약 조건이 충족하기 때문에 "zoneB"에 배치할 수 있다. 그러나, 배치 이후에도 클러스터의 불균형 정도는 변경되지 않는다. - 여전히 zoneA는 {foo:bar} 레이블을 가지고 있는 2개의 파드를 가지고 있고, zoneB 도 {foo:bar}를 레이블로 가지는 파드 1개를 가지고 있다. 따라서 만약 예상과 다르면, 워크로드의 topologySpreadConstraints[*].labelSelector 가 자체 레이블과 일치하도록 하는 것을 권장한다.
클러스터 수준의 기본 제약 조건
클러스터에 대한 기본 토폴로지 분배 제약 조건을 설정할 수 있다. 기본
토폴로지 분배 제약 조건은 다음과 같은 경우에만 파드에 적용된다.
.spec.topologySpreadConstraints 에는 어떠한 제약도 정의되어 있지 않는 경우.
서비스, 레플리케이션컨트롤러(ReplicationController), 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)에 속해있는 경우.
기본 제약 조건은 스케줄링 프로파일에서
PodTopologySpread 플러그인의 일부로 설정할 수 있다.
제약 조건은 labelSelector 가 비어 있어야 한다는 점을 제외하고, 위와 동일한 API로
제약 조건을 지정한다. 셀렉터는 파드가 속한 서비스, 레플리케이션 컨트롤러,
레플리카셋 또는 스테이트풀셋에서 계산한다.
참고: 기본 스케줄링 제약 조건에 의해 생성된 점수는
SelectorSpread 플러그인에
의해 생성된 점수와 충돌 할 수 있다.
PodTopologySpread 에 대한 기본 제약 조건을 사용할 때 스케줄링 프로파일에서
이 플러그인을 비활성화 하는 것을 권장한다.
내부 기본 제약
FEATURE STATE:Kubernetes v1.20 [beta]
기본적으로 활성화된 DefaultPodTopologySpread 기능 게이트를 사용하면, 기존
SelectorSpread 플러그인이 비활성화된다.
kube-scheduler는 PodTopologySpread 플러그인 구성에 다음과 같은
기본 토폴로지 제약 조건을 사용한다.
파드어피니티(PodAffinity)/파드안티어피니티(PodAntiAffinity)와의 비교
쿠버네티스에서 "어피니티(Affinity)"와 관련된 지침은 파드가
더 많이 채워지거나 더 많이 분산되는 방식으로 스케줄 되는 방법을 제어한다.
PodAffinity 는, 사용자가 자격이 충족되는 토폴로지 도메인에
원하는 수의 파드를 얼마든지 채울 수 있다.
PodAntiAffinity 로는, 단일 토폴로지 도메인에
단 하나의 파드만 스케줄 될 수 있다.
더 세밀한 제어를 위해, 토폴로지 분배 제약 조건을 지정하여 다양한 토폴로지 도메인에 파드를
분배해서 고 가용성 또는 비용 절감을 달성할 수 있는 유연한 옵션을
제공한다. 또한 워크로드의 롤링 업데이트와 레플리카의 원활한 스케일링 아웃에 도움이 될 수 있다.
더 자세한 내용은
모티베이션(Motivation)를
참고한다.
알려진 제한사항
파드가 제거된 이후에도 제약 조건이 계속 충족된다는 보장은 없다. 예를 들어 디플로이먼트를 스케일링 다운하면 그 결과로 파드의 분포가 불균형해질 수 있다.
Descheduler를 사용하여 파드 분포를 다시 균형있게 만들 수 있다.
위 작업은 클러스터 관리자가 직접 수행하거나 자동화를 통해 수행하며,
클러스터 호스팅 공급자에 의해서도 수행된다.
클러스터에 자발적인 중단을 일으킬 수 있는 어떤 원인이 있는지
클러스터 관리자에게 문의하거나 클라우드 공급자에게 문의하고, 배포 문서를 참조해서 확인해야 한다.
만약 자발적인 중단을 일으킬 수 있는 원인이 없다면 Pod Disruption Budget의 생성을 넘길 수 있다.
주의: 모든 자발적인 중단이 Pod Disruption Budget에 연관되는 것은 아니다.
예를 들어 디플로이먼트 또는 파드의 삭제는 Pod Disruption Budget을 무시한다.
고가용성이 필요한 경우 애플리케이션을 복제한다.
(복제된 스테이트리스 및
스테이트풀 애플리케이션에 대해 알아보기.)
복제된 애플리케이션의 구동 시 훨씬 더 높은 가용성을 위해 랙 전체
(안티-어피니티 이용)
또는 영역 간
(다중 영역 클러스터를 이용한다면)에
애플리케이션을 분산해야 한다.
자발적 중단의 빈도는 다양하다. 기본적인 쿠버네티스 클러스터에서는 자동화된 자발적 중단은 발생하지 않는다(사용자가 지시한 자발적 중단만 발생한다).
그러나 클러스터 관리자 또는 호스팅 공급자가 자발적 중단이 발생할 수 있는 일부 부가 서비스를 운영할 수 있다.
예를 들어 노드 소프트웨어의 업데이트를 출시하는 경우 자발적 중단이 발생할 수 있다.
또한 클러스터(노드) 오토스케일링의 일부 구현에서는
단편화를 제거하고 노드의 효율을 높이는 과정에서 자발적 중단을 야기할 수 있다.
클러스터 관리자 또는 호스팅 공급자는
예측 가능한 자발적 중단 수준에 대해 문서화해야 한다.
파드 스펙 안에 프라이어리티클래스 사용하기와 같은 특정 환경설정 옵션
또한 자발적(+ 비자발적) 중단을 유발할 수 있다.
파드 disruption budgets
FEATURE STATE:Kubernetes v1.21 [stable]
쿠버네티스는 자발적인 중단이 자주 발생하는 경우에도 고 가용성 애플리케이션을
실행하는 데 도움이 되는 기능을 제공한다.
애플리케이션 소유자로써, 사용자는 각 애플리케이션에 대해 PodDisruptionBudget(PDB)을 만들 수 있다.
PDB는 자발적 중단으로
일시에 중지되는 복제된 애플리케이션 파드의 수를 제한한다.
예를 들어, 정족수 기반의 애플리케이션이
실행 중인 레플리카의 수가 정족수 이하로 떨어지지 않도록 한다.
웹 프런트 엔드는 부하를 처리하는 레플리카의 수가
일정 비율 이하로 떨어지지 않도록 보장할 수 있다.
클러스터 관리자와 호스팅 공급자는 직접적으로 파드나 디플로이먼트를 제거하는 대신
Eviction API로
불리는 PodDisruptionBudget을 준수하는 도구를 이용해야 한다.
예를 들어, kubectl drain 하위 명령을 사용하면 노드를 서비스 중단으로 표시할 수
있다. kubectl drain 을 실행하면, 도구는 사용자가 서비스를 중단하는 노드의
모든 파드를 축출하려고 한다. kubectl 이 사용자를 대신하여 수행하는
축출 요청은 일시적으로 거부될 수 있으며,
도구는 대상 노드의 모든 파드가 종료되거나
설정 가능한 타임아웃이 도래할 때까지 주기적으로 모든 실패된 요청을 다시 시도한다.
PDB는 애플리케이션이 필요로 하는 레플리카의 수에 상대적으로, 용인할 수 있는 레플리카의 수를 지정한다.
예를 들어 .spec.replicas: 5 의 값을 갖는 디플로이먼트는 어느 시점에든 5개의 파드를 가져야 한다.
만약 해당 디플로이먼트의 PDB가 특정 시점에 파드를 4개 허용한다면,
Eviction API는 한 번에 1개(2개의 파드가 아닌)의 파드의 자발적인 중단을 허용한다.
파드 그룹은 레이블 셀렉터를 사용해서 지정한 애플리케이션으로 구성되며
애플리케이션 컨트롤러(디플로이먼트, 스테이트풀셋 등)를 사용한 것과 같다.
파드의 "의도"하는 수량은 해당 파드를 관리하는 워크로드 리소스의 .spec.replicas 를
기반으로 계산한다. 컨트롤 플레인은 파드의 .metadata.ownerReferences 를 검사하여
소유하는 워크로드 리소스를 발견한다.
애플리케이션의 롤링 업그레이드로 파드가 삭제되거나 사용할 수 없는 경우 중단 버짓에 영향을 준다.
그러나 워크로드 리소스(디플로이먼트, 스테이트풀셋과 같은)는
롤링 업데이트 시 PDB의 제한을 받지 않는다. 대신, 애플리케이션 업데이트 중
실패 처리는 특정 워크로드 리소스에 대한 명세에서 구성된다.
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
이 시점에서 클러스터 관리자는
클러스터에 노드를 추가해서 업그레이드를 진행해야 한다.
쿠버네티스에 중단이 발생할 수 있는 비율을 어떻게 변화시키는지
다음의 사례를 통해 알 수 있다.
애플리케이션에 필요한 레플리카의 수
인스턴스를 정상적으로 종료하는데 소요되는 시간
새 인스턴스를 시작하는데 소요되는 시간
컨트롤러의 유형
클러스터의 리소스 용량
클러스터 소유자와 애플리케이션 소유자의 역할 분리
보통 클러스터 매니저와 애플리케이션 소유자는
서로에 대한 지식이 부족한 별도의 역할로 생각하는 것이 유용하다.
이와 같은 책임의 분리는
다음의 시나리오에서 타당할 수 있다.
쿠버네티스 클러스터를 공유하는 애플리케이션 팀이 많고, 자연스럽게 역할이 나누어진 경우
타사 도구 또는 타사 서비스를 이용해서
클러스터 관리를 자동화 하는 경우
Pod Disruption Budget은 역할 분리에 따라
역할에 맞는 인터페이스를 제공한다.
만약 조직에 역할 분리에 따른 책임의 분리가 없다면
Pod Disruption Budget을 사용할 필요가 없다.
클러스터에서 중단이 발생할 수 있는 작업을 하는 방법
만약 클러스터 관리자라면, 그리고 클러스터 전체 노드에 노드 또는 시스템 소프트웨어 업그레이드와 같은
중단이 발생할 수 있는 작업을 수행하는 경우 다음과 같은 옵션을 선택한다.
업그레이드 하는 동안 다운타임을 허용한다.
다른 레플리카 클러스터로 장애조치를 한다.
다운타임은 없지만, 노드 사본과
전환 작업을 조정하기 위한 인력 비용이 많이 발생할 수 있다.
PDB를 이용해서 애플리케이션의 중단에 견디도록 작성한다.
다운타임 없음
최소한의 리소스 중복
클러스터 관리의 자동화 확대 적용
내결함성이 있는 애플리케이션의 작성은 까다롭지만
자발적 중단를 허용하는 작업의 대부분은 오토스케일링과
비자발적 중단를 지원하는 작업과 겹친다.
이 페이지는 임시 컨테이너에 대한 개요를 제공한다.
이 특별한 유형의 컨테이너는 트러블슈팅과 같은 사용자가 시작한 작업을 완료하기 위해
기존 파드에서 임시적으로 실행된다.
임시 컨테이너는 애플리케이션을 빌드하는 경우보다는 서비스 점검과 같은 경우에 더 적합하다.
경고: 임시 컨테이너 기능은 알파 상태이며,
프로덕션 클러스터에는 적합하지 않다.
쿠버네티스 사용 중단(deprecation) 정책에 따라
이 알파 기능은 향후 크게 변경되거나, 완전히 제거될 수 있다.
임시 컨테이너 이해하기
파드 는 쿠버네티스 애플리케이션의
기본 구성 요소이다. 파드는 일회용이고, 교체 가능한 것으로 의도되었기
때문에, 사용자는 파드가 한번 생성되면, 컨테이너를 추가할 수 없다.
대신, 사용자는 보통 디플로이먼트 를
사용해서 제어하는 방식으로 파드를 삭제하고 교체한다.
그러나 때때로 재현하기 어려운 버그의 문제 해결을 위해
기존 파드의 상태를 검사해야 할 수 있다. 이 경우 사용자는
기존 파드에서 임시 컨테이너를 실행해서 상태를 검사하고, 임의의 명령을
실행할 수 있다.
임시 컨테이너는 무엇인가?
임시 컨테이너는 리소스 또는 실행에 대한 보증이 없다는 점에서
다른 컨테이너와 다르며, 결코 자동으로 재시작되지 않는다. 그래서
애플리케이션을 만드는데 적합하지 않다. 임시 컨테이너는
일반 컨테이너와 동일한 ContainerSpec 을 사용해서 명시하지만, 많은 필드가
호환되지 않으며 임시 컨테이너에는 허용되지 않는다.
임시 컨테이너는 포트를 가지지 않을 수 있으므로, ports,
livenessProbe, readinessProbe 와 같은 필드는 허용되지 않는다.
임시 컨테이너는 pod.spec 에 직접 추가하는 대신
API에서 특별한 ephemeralcontainers 핸들러를 사용해서 만들어지기 때문에
kubectl edit을 사용해서 임시 컨테이너를 추가할 수 없다.
일반 컨테이너와 마찬가지로, 사용자는 임시 컨테이너를 파드에 추가한
이후에 변경하거나 제거할 수 없다.
임시 컨테이너의 사용
임시 컨테이너는 컨테이너가 충돌 되거나 또는 컨테이너 이미지에
디버깅 도구가 포함되지 않은 이유로 kubectl exec 이 불충분할 때
대화형 문제 해결에 유용하다.
특히, distroless 이미지
를 사용하면 공격 표면(attack surface)과 버그 및 취약점의 노출을 줄이는 최소한의
컨테이너 이미지를 배포할 수 있다. distroless 이미지는 셸 또는 어떤 디버깅 도구를
포함하지 않기 때문에, kubectl exec 만으로는 distroless
이미지의 문제 해결이 어렵다.
임시 컨테이너 사용 시 프로세스 네임스페이스
공유를
활성화하면 다른 컨테이너 안의 프로세스를 보는 데 도움이 된다.
디플로이먼트에서 의도하는 상태 를 설명하고, 디플로이먼트 컨트롤러(Controller)는 현재 상태에서 의도하는 상태로 비율을 조정하며 변경한다. 새 레플리카셋을 생성하는 디플로이먼트를 정의하거나 기존 디플로이먼트를 제거하고, 모든 리소스를 새 디플로이먼트에 적용할 수 있다.
참고: 디플로이먼트가 소유하는 레플리카셋은 관리하지 말아야 한다. 사용자의 유스케이스가 다음에 포함되지 않는 경우 쿠버네티스 리포지터리에 이슈를 올릴 수 있다.
디플로이먼트의 PodTemplateSpec을 업데이트해서 파드의 새로운 상태를 선언한다. 새 레플리카셋이 생성되면, 디플로이먼트는 파드를 기존 레플리카셋에서 새로운 레플리카셋으로 속도를 제어하며 이동하는 것을 관리한다. 각각의 새로운 레플리카셋은 디플로이먼트의 수정 버전에 따라 업데이트한다.
만약 디플로이먼트의 현재 상태가 안정적이지 않은 경우 디플로이먼트의 이전 버전으로 롤백한다. 각 롤백은 디플로이먼트의 수정 버전에 따라 업데이트한다.
.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 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]-[RANDOM-STRING] 형식으로 된 것을 알 수 있다. 무작위 문자열은
무작위로 생성되며, pod-template-hash 를 시드(seed)로 사용한다.
각 파드에 자동으로 생성된 레이블을 보려면, 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 AGEnginx-deployment 3/3 3 3 36s
kubectl get rs 를 실행해서 디플로이먼트가 새 레플리카셋을 생성해서 파드를 업데이트 했는지 볼 수 있고,
새 레플리카셋을 최대 3개의 레플리카로 스케일 업, 이전 레플리카셋을 0개의 레플리카로 스케일 다운한다.
kubectl get rs
이와 유사하게 출력된다.
NAME DESIRED CURRENT READY AGE
nginx-deployment-1564180365 3 3 3 6s
nginx-deployment-2035384211 0 0 0 36s
get pods 를 실행하면 새 파드만 표시된다.
kubectl get pods
이와 유사하게 출력된다.
NAME READY STATUS RESTARTS AGE
nginx-deployment-1564180365-khku8 1/1 Running 0 14s
nginx-deployment-1564180365-nacti 1/1 Running 0 14s
nginx-deployment-1564180365-z9gth 1/1 Running 0 14s
다음에 이러한 파드를 업데이트 하려면 디플로이먼트의 파드 템플릿만 다시 업데이트 하면 된다.
디플로이먼트는 업데이트되는 동안 일정한 수의 파드만 중단되도록 보장한다. 기본적으로
적어도 의도한 파드 수의 75% 이상이 동작하도록 보장한다(최대 25% 불가).
또한 디플로이먼트는 의도한 파드 수 보다 더 많이 생성되는 파드의 수를 제한한다.
기본적으로, 의도한 파드의 수 기준 최대 125%까지만 추가 파드가 동작할 수 있도록 제한한다(최대 25% 까지).
예를 들어, 위 디플로이먼트를 자세히 살펴보면 먼저 새로운 파드를 생성한 다음
이전 파드를 삭제하고, 새로운 파드를 만든 것을 볼 수 있다. 충분한 수의 새로운 파드가 나올 때까지 이전 파드를 죽이지 않으며,
충분한 수의 이전 파드들이 죽기 전까지 새로운 파드를 만들지 않는다.
이것은 최소 2개의 파드를 사용할 수 있게 하고, 최대 4개의 파드를 사용할 수 있게 한다.
디플로이먼트의 세부 정보 가져오기
kubectl describe deployments
이와 유사하게 출력된다.
Name: nginx-deployment
Namespace: default
CreationTimestamp: Thu, 30 Nov 2017 10:56:25 +0000
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision=2
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.16.1
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set nginx-deployment-2035384211 to 3
Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 1
Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 2
Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 2
Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 3
Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 0
처음 디플로이먼트를 생성했을 때, 디플로이먼트가 레플리카셋(nginx-deployment-2035384211)을 생성해서
3개의 레플리카로 직접 스케일 업한 것을 볼 수 있다.
디플로이먼트를 업데이트할 때 새 레플리카셋(nginx-deployment-1564180365)을 생성하고, 1개로 스케일 업한 다음
이전 레플리카셋을 2개로 스케일 다운해서, 최소 2개의 파드를 사용할 수 있고 최대 4개의 파드가 항상 생성되어 있도록 하였다.
이후 지속해서 같은 롤링 업데이트 정책으로 새 레플리카셋은 스케일 업하고 이전 레플리카셋은 스케일 다운한다.
마지막으로 새로운 레플리카셋에 3개의 사용 가능한 레플리카가 구성되며, 이전 레플리카셋은 0개로 스케일 다운된다.
롤오버(일명 인-플라이트 다중 업데이트)
디플로이먼트 컨트롤러는 각 시간마다 새로운 디플로이먼트에서 레플리카셋이
의도한 파드를 생성하고 띄우는 것을 주시한다. 만약 디플로이먼트가 업데이트되면, 기존 레플리카셋에서
.spec.selector 레이블과 일치하는 파드를 컨트롤 하지만, 템플릿과 .spec.template 이 불일치하면 스케일 다운이 된다.
결국 새로운 레플리카셋은 .spec.replicas 로 스케일되고, 모든 기존 레플리카셋은 0개로 스케일된다.
만약 기존 롤아웃이 진행되는 중에 디플로이먼트를 업데이트하는 경우 디플로이먼트가 업데이트에 따라 새 레플리카셋을 생성하고,
스케일 업하기 시작한다. 그리고 이전에 스케일 업 하던 레플리카셋에 롤오버 한다.
--이것은 기존 레플리카셋 목록에 추가하고 스케일 다운을 할 것이다.
예를 들어 디플로이먼트로 nginx:1.14.2 레플리카를 5개 생성을 한다.
하지만 nginx:1.14.2 레플리카 3개가 생성되었을 때 디플로이먼트를 업데이트해서 nginx:1.16.1
레플리카 5개를 생성성하도록 업데이트를 한다고 가정한다. 이 경우 디플로이먼트는 즉시 생성된 3개의
nginx:1.14.2 파드 3개를 죽이기 시작하고 nginx:1.16.1 파드를 생성하기 시작한다.
이것은 과정이 변경되기 전 nginx:1.14.2 레플리카 5개가
생성되는 것을 기다리지 않는다.
레이블 셀렉터 업데이트
일반적으로 레이블 셀렉터를 업데이트 하는 것을 권장하지 않으며 셀렉터를 미리 계획하는 것을 권장한다.
어떤 경우든 레이블 셀렉터의 업데이트를 해야하는 경우 매우 주의하고,
모든 영향을 파악했는지 확인해야 한다.
참고: 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
이제 디플로이먼트가 이전 안정 수정 버전으로 롤백 된다. 버전 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
디플로이먼트 롤링업데이트는 여러 버전의 애플리케이션을 동시에 실행할 수 있도록 지원한다.
사용자 또는 오토스케일러가 롤아웃 중에 있는 디플로이먼트 롤링 업데이트를 스케일링 하는 경우(진행중 또는 일시 중지 중),
디플로이먼트 컨트롤러는 위험을 줄이기 위해 기존 활성화된 레플리카셋(파드와 레플리카셋)의 추가 레플리카의 균형을 조절 한다.
이것을 proportional scaling 라 부른다.
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
kubectl rollout status 를 사용해서 디플로이먼트의 진행사황을 모니터할 수 있다.
디플로이먼트 완료
쿠버네티스는 다음과 같은 특성을 가지게 되면 디플로이먼트를 완료 로 표시한다.
디플로이먼트과 관련된 모든 레플리카가 지정된 최신 버전으로 업데이트 되었을 때.
즉, 요청한 모든 업데이트가 완료되었을 때.
디플로이먼트와 관련한 모든 레플리카를 사용할 수 있을 때.
디플로이먼트에 대해 이전 복제본이 실행되고 있지 않을 때.
kubectl rollout status 를 사용해서 디플로이먼트가 완료되었는지 확인할 수 있다.
만약 롤아웃이 성공적으로 완료되면 kubectl rollout status 는 종료 코드로 0이 반환된다.
kubectl rollout status deployment/nginx-deployment
이와 유사하게 출력된다.
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
그리고 kubectl rollout 의 종료 상태는 0(success)이다.
echo$?
0
디플로이먼트 실패
디플로이먼트시 새 레플리카셋인 완료되지 않은 상태에서는 배포를 시도하면 고착될 수 있다.
이 문제는 다음 몇 가지 요인으로 인해 발생한다.
할당량 부족
준비성 프로브(readiness probe)의 실패
이미지 풀 에러
권한 부족
범위 제한
애플리케이션 런타임의 잘못된 구성
이 조건을 찾을 수 있는 한 가지 방법은 디플로이먼트 스펙에서 데드라인 파라미터를 지정하는 것이다
(.spec.progressDeadlineSeconds). .spec.progressDeadlineSeconds 는
(디플로이먼트 상태에서) 디플로이먼트의 진행이 정지되었음을 나타내는 디플로이먼트 컨트롤러가
대기하는 시간(초)를 나타낸다.
다음 kubectl 명령어로 progressDeadlineSeconds 를 설정해서 컨트롤러가
10분 후 디플로이먼트에 대한 진행 상태의 부족에 대한 리포트를 수행하게 한다.
참고: 쿠버네티스는 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 을 실행하면 디플로이먼트 상태는 다음과 유사하다.
결국, 디플로이먼트 진행 데드라인을 넘어서면, 쿠버네티스는 진행 컨디션의
상태와 이유를 업데이트한다.
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.replicas 은 필요한 파드의 수를 지정하는 선택적 필드이다. 이것의 기본값은 1이다.
셀렉터
.spec.selector 는 디플로이먼트의 대상이 되는 파드에 대해 레이블 셀렉터를
지정하는 필수 필드이다.
.spec.selector 는 .spec.template.metadata.labels 과 일치해야 하며, 그렇지 않으면 API에 의해 거부된다.
API 버전 apps/v1 에서는 .spec.selector 와 .metadata.labels 이 설정되지 않으면 .spec.template.metadata.labels 은 기본 설정되지 않는다. 그래서 이것들은 명시적으로 설정되어야 한다. 또한 apps/v1 에서는 디플로이먼트를 생성한 후에는 .spec.selector 이 변경되지 않는 점을 참고한다.
디플로이먼트는 템플릿의 .spec.template 와 다르거나 파드의 수가 .spec.replicas 를 초과할 경우
셀렉터와 일치하는 레이블을 가진 파드를 종료할 수 있다.
파드의 수가 의도한 수량보다 적을 경우 .spec.template 에 맞는 새 파드를 띄운다.
참고: 다른 디플로이먼트를 생성하거나, 레플리카셋 또는 레플리케이션컨트롤러와 같은 다른 컨트롤러를 사용해서 직접적으로 레이블과 셀렉터가 일치하는 다른 파드를 생성하지 말아야 한다.
만약 이렇게 하면 첫 번째 디플로이먼트는 다른 파드를 만들었다고 생각한다.
쿠버네티스는 이 일을 막지 않는다.
만약 셀렉터가 겹치는 컨트롤러가 어러 개 있는 경우, 컨트롤러는 서로 싸우고
올바르게 작동하지 않는다.
전략
.spec.strategy 는 이전 파드를 새로운 파드로 대체하는 전략을 명시한다.
.spec.strategy.type 은 "재생성" 또는 "롤링업데이트"가 될 수 있다.
"롤링업데이트"가 기본값이다.
디플로이먼트 재생성
기존의 모든 파드는 .spec.strategy.type==Recreate 이면 새 파드가 생성되기 전에 죽는다.
참고: 이렇게 하면 업그레이드를 생성하기 전에 파드 종료를 보장할 수 있다. 디플로이먼트를 업그레이드하면,
이전 버전의 모든 파드가 즉시 종료된다. 신규 버전의 파드가 생성되기 전에 성공적으로 제거가
완료되기를 대기한다. 파드를 수동으로 삭제하면, 라이프사이클은 레플리카셋에 의해
제어되며(이전 파드가 여전히 종료 상태에 있는 경우에도) 교체용 파드가 즉시 생성된다. 파드에
대해 "최대" 보장이 필요한 경우
스테이트풀셋의 사용을 고려해야 한다.
디플로이먼트 롤링 업데이트
디플로이먼트는 .spec.strategy.type==RollingUpdate 이면 파드를 롤링 업데이트
방식으로 업데이트 한다. 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에 대한 변경 사항이 일시중지 된 경우 새 롤아웃을 트리거 하지 않는다.
디플로이먼트는 생성시 기본적으로 일시 중지되지 않는다.
레플리카셋의 목적은 레플리카 파드 집합의 실행을 항상 안정적으로 유지하는 것이다.
이처럼 레플리카셋은 보통 명시된 동일 파드 개수에 대한 가용성을 보증하는데 사용한다.
레플리카셋의 작동 방식
레플리카셋을 정의하는 필드는 획득 가능한 파드를 식별하는 방법이 명시된 셀렉터, 유지해야 하는 파드 개수를 명시하는 레플리카의 개수,
그리고 레플리카 수 유지를 위해 생성하는 신규 파드에 대한 데이터를 명시하는 파드 템플릿을 포함한다.
그러면 레플리카셋은 필드에 지정된 설정을 충족하기 위해 필요한 만큼 파드를 만들고 삭제한다.
레플리카셋이 새로운 파드를 생성해야 할 경우, 명시된 파드 템플릿을
사용한다.
레플리카셋은 파드의 metadata.ownerReferences
필드를 통해 파드에 연결되며, 이는 현재 오브젝트가 소유한 리소스를 명시한다.
레플리카셋이 가지고 있는 모든 파드의 ownerReferences 필드는 해당 파드를 소유한 레플리카셋을 식별하기 위한 소유자 정보를 가진다.
이 링크를 통해 레플리카셋은 자신이 유지하는 파드의 상태를 확인하고 이에 따라 관리 한다.
레플리카셋은 셀렉터를 이용해서 필요한 새 파드를 식별한다. 만약 파드에 OwnerReference이 없거나
OwnerReference가 컨트롤러(Controller) 가 아니고 레플리카셋의 셀렉터와 일치한다면 레플리카셋이 즉각 파드를
가지게 될 것이다.
레플리카셋을 사용하는 시기
레플리카셋은 지정된 수의 파드 레플리카가 항상 실행되도록 보장한다.
그러나 디플로이먼트는 레플리카셋을 관리하고 다른 유용한 기능과 함께
파드에 대한 선언적 업데이트를 제공하는 상위 개념이다.
따라서 우리는 사용자 지정 오케스트레이션이 필요하거나 업데이트가 전혀 필요하지 않은 경우라면
레플리카셋을 직접적으로 사용하기 보다는 디플로이먼트를 사용하는 것을 권장한다.
이는 레플리카셋 오브젝트를 직접 조작할 필요가 없다는 것을 의미한다.
대신 디플로이먼트를 이용하고 사양 부분에서 애플리케이션을 정의하면 된다.
단독(bare) 파드를 생성하는 것에는 문제가 없지만, 단독 파드가 레플리카셋의 셀렉터와 일치하는 레이블을 가지지
않도록 하는 것을 강력하게 권장한다. 그 이유는 레플리카셋이 소유하는 파드가 템플릿에 명시된 파드에만 국한되지 않고,
이전 섹션에서 명시된 방식에 의해서도 다른 파드의 획득이 가능하기 때문이다.
레플리카셋이 해당 파드를 소유한 것을 볼 수 있으며 새 파드 및 기존 파드의 수가
레플리카셋이 필요로 하는 수와 일치할 때까지 사양에 따라 신규 파드만 생성한다. 파드를 가져온다.
kubectl get pods
다음 출력에서 볼 수 있다.
NAME READY STATUS RESTARTS AGE
frontend-hmmj2 1/1 Running 0 9s
pod1 1/1 Running 0 36s
pod2 1/1 Running 0 36s
이러한 방식으로 레플리카셋은 템플릿을 사용하지 않는 파드를 소유하게 된다.
레플리카셋 매니페스트 작성하기
레플리카셋은 모든 쿠버네티스 API 오브젝트와 마찬가지로 apiVersion, kind, metadata 필드가 필요하다.
레플리카셋에 대한 kind 필드의 값은 항상 레플리카셋이다.
쿠버네티스 1.9에서의 레플리카셋의 kind에 있는 API 버전 apps/v1은 현재 버전이며, 기본으로 활성화 되어 있다. API 버전 apps/v1beta2은 사용 중단(deprecated)되었다.
API 버전에 대해서는 frontend.yaml 예제의 첫 번째 줄을 참고한다.
.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를 설정해서 동시에 동작하는 파드의 수를 지정할 수 있다.
레플리카셋은 파드의 수가 일치하도록 생성 및 삭제한다.
레플리카셋을 --cascade=orphan 옵션과 함께 kubectl delete를 사용하면 연관 파드에 영향을 주지 않고 삭제할 수 있다.
REST API 또는 client-go 라이브러리를 이용할 때는 propagationPolicy에 Orphan을 설정해야 한다.
예시:
원본이 삭제되면 새 레플리카셋을 생성해서 대체할 수 있다.
기존 .spec.selector와 신규 .spec.selector가 같으면 새 레플리카셋은 기존 파드를 선택한다.
하지만 신규 레플리카셋은 기존 파드를 신규 레플리카셋의 새롭고 다른 파드 템플릿에 일치시키는 작업을 수행하지는 않는다.
컨트롤 방식으로 파드를 새로운 사양으로 업데이트 하기 위해서는 디플로이먼트를 이용하면 된다.
이는 레플리카셋이 롤링 업데이트를 직접적으로 지원하지 않기 때문이다.
레플리카셋에서 파드 격리
레이블을 변경하면 레플리카셋에서 파드를 제거할 수 있다. 이 방식은 디버깅과 데이터 복구 등을
위해 서비스에서 파드를 제거하는 데 사용할 수 있다. 이 방식으로 제거된 파드는 자동으로 교체된다(
레플리카의 수가 변경되지 않는다고 가정한다).
레플리카셋의 스케일링
레플리카셋을 손쉽게 스케일 업 또는 다운하는 방법은 단순히 .spec.replicas 필드를 업데이트하면 된다.
레플리카셋 컨트롤러는 일치하는 레이블 셀렉터가 있는 파드가 의도한 수 만큼 가용하고 운영 가능하도록 보장한다.
스케일 다운할 때, 레플리카셋 컨트롤러는 스케일 다운할 파드의
우선순위를 정하기 위해 다음의 기준으로 가용 파드를 정렬하여 삭제할 파드를 결정한다.
Pending 상태인 (+ 스케줄링할 수 없는) 파드가 먼저 스케일 다운된다.
controller.kubernetes.io/pod-deletion-cost 어노테이션이 설정되어 있는
파드에 대해서는, 낮은 값을 갖는 파드가 먼저 스케일 다운된다.
더 많은 레플리카가 있는 노드의 파드가 더 적은 레플리카가 있는 노드의 파드보다 먼저 스케일 다운된다.
파드 생성 시간이 다르면, 더 최근에 생성된 파드가
이전에 생성된 파드보다 먼저 스케일 다운된다.
(LogarithmicScaleDown기능 게이트가 활성화되어 있으면 생성 시간이 정수 로그 스케일로 버킷화된다)
이 어노테이션은 파드에 설정되어야 하며, [-2147483647, 2147483647] 범위를 갖는다.
이 어노테이션은 하나의 레플리카셋에 있는 다른 파드와의 상대적 삭제 비용을 나타낸다.
삭제 비용이 낮은 파드는 삭제 비용이 높은 파드보다 삭제 우선순위가 높다.
파드에 대해 이 값을 명시하지 않으면 기본값은 0이다. 음수로도 설정할 수 있다.
유효하지 않은 값은 API 서버가 거부한다.
이 기능은 베타 상태이며 기본적으로 활성화되어 있다.
kube-apiserver와 kube-controller-manager에 대해 PodDeletionCost기능 게이트를 이용하여 비활성화할 수 있다.
참고:
이 기능은 best-effort 방식으로 동작하므로, 파드 삭제 순서를 보장하지는 않는다.
이 값을 자주 바꾸는 것은 피해야 한다 (예: 메트릭 값에 따라 변경).
apiserver에서 많은 양의 파드 업데이트를 동반하기 때문이다.
사용 예시
한 애플리케이션 내의 여러 파드는 각각 사용률이 다를 수 있다. 스케일 다운 시,
애플리케이션은 사용률이 낮은 파드를 먼저 삭제하고 싶을 수 있다. 파드를 자주
업데이트하는 것을 피하기 위해, 애플리케이션은 controller.kubernetes.io/pod-deletion-cost 값을
스케일 다운하기 전에 1회만 업데이트해야 한다 (파드 사용률에 비례하는 값으로 설정).
이 방식은 Spark 애플리케이션의 드라이버 파드처럼 애플리케이션이 스스로 다운스케일링을 수행하는 경우에 유효하다.
디플로이먼트는 레플리카셋을 소유하거나 업데이트를 하고,
파드의 선언적인 업데이트와 서버측 롤링 업데이트를 할 수 있는 오브젝트이다.
레플리카셋은 단독으로 사용할 수 있지만, 오늘날에는 주로 디플로이먼트로 파드의 생성과 삭제 그리고 업데이트를 오케스트레이션하는 메커니즘으로 사용한다.
디플로이먼트를 이용해서 배포할 때 생성되는 레플리카셋을 관리하는 것에 대해 걱정하지 않아도 된다.
디플로이먼트는 레플리카셋을 소유하거나 관리한다.
따라서 레플리카셋을 원한다면 디플로이먼트를 사용하는 것을 권장한다.
기본 파드
사용자가 직접 파드를 생성하는 경우와는 다르게, 레플리카셋은 노드 장애 또는 노드의 커널 업그레이드와 같은 관리 목적의 중단 등 어떤 이유로든 종료되거나 삭제된 파드를 교체한다. 이런 이유로 애플리케이션이 단일 파드가 필요하더라도 레플리카셋을 이용하는 것을 권장한다. 레플리카셋을 프로세스 관리자와 비교해서 생각해본다면, 레플리카셋은 단일 노드에서의 개별 프로세스들이 아닌 다수의 노드에 걸쳐있는 다수의 파드를 관리하는 것이다. 레플리카셋은 로컬 컨테이너의 재시작을 노드에 있는 어떤 에이전트에게 위임한다(예를들어 Kubelet 또는 도커).
잡
스스로 종료되는 것이 예상되는 파드의 경우에는 레플리카셋 대신 잡을 이용한다
(즉, 배치 잡).
데몬셋
머신 모니터링 또는 머신 로깅과 같은 머신-레벨의 기능을 제공하는 파드를 위해서는 레플리카셋 대신
데몬셋을 사용한다.
이러한 파드의 수명은 머신의 수명과 연관되어 있고, 머신에서 다른 파드가 시작하기 전에 실행되어야 하며,
머신의 재부팅/종료가 준비되었을 때, 해당 파드를 종료하는 것이 안전하다.
레플리케이션 컨트롤러
레플리카셋은 레플리케이션 컨트롤러를 계승하였다.
이 두 개의 용도는 동일하고, 유사하게 동작하며, 레플리케이션 컨트롤러가 레이블 사용자 가이드에
설명된 설정-기반의 셀렉터의 요건을 지원하지 않는다는 점을 제외하면 유사하다.
따라서 레플리카셋이 레플리케이션 컨트롤러보다 선호된다.
디플로이먼트와 유사하게, 스테이트풀셋은 동일한 컨테이너 스펙을 기반으로 둔 파드들을 관리한다. 디플로이먼트와는 다르게, 스테이트풀셋은 각 파드의 독자성을 유지한다. 이 파드들은 동일한 스팩으로 생성되었지만, 서로 교체는 불가능하다. 다시 말해, 각각은 재스케줄링 간에도 지속적으로 유지되는 식별자를 가진다.
스토리지 볼륨을 사용해서 워크로드에 지속성을 제공하려는 경우, 솔루션의 일부로 스테이트풀셋을 사용할 수 있다. 스테이트풀셋의 개별 파드는 장애에 취약하지만, 퍼시스턴트 파드 식별자는 기존 볼륨을 실패한 볼륨을 대체하는 새 파드에 더 쉽게 일치시킬 수 있다.
스테이트풀셋 사용
스테이트풀셋은 다음 중 하나 또는 이상이 필요한 애플리케이션에
유용하다.
안정된, 고유한 네트워크 식별자.
안정된, 지속성을 갖는 스토리지.
순차적인, 정상 배포(graceful deployment)와 스케일링.
순차적인, 자동 롤링 업데이트.
위의 안정은 파드의 (재)스케줄링 전반에 걸친 지속성과 같은 의미이다.
만약 애플리케이션이 안정적인 식별자 또는 순차적인 배포,
삭제 또는 스케일링이 필요하지 않으면, 스테이트리스 레플리카셋(ReplicaSet)을
제공하는 워크로드 오브젝트를 사용해서 애플리케이션을 배포해야 한다.
디플로이먼트 또는
레플리카셋과 같은 컨트롤러가 스테이트리스 요구에 더 적합할 수 있다.
제한사항
파드에 지정된 스토리지는 관리자에 의해 퍼시스턴트 볼륨 프로비저너를 기반으로 하는 storage class 를 요청해서 프로비전하거나 사전에 프로비전이 되어야 한다.
스테이트풀셋을 삭제 또는 스케일 다운해도 스테이트풀셋과 연관된 볼륨이 삭제되지 않는다. 이는 일반적으로 스테이트풀셋과 연관된 모든 리소스를 자동으로 제거하는 것보다 더 중요한 데이터의 안전을 보장하기 위함이다.
스테이트풀셋은 현재 파드의 네트워크 신원을 책임지고 있는 헤드리스 서비스가 필요하다. 사용자가 이 서비스를 생성할 책임이 있다.
스테이트풀셋은 스테이트풀셋의 삭제 시 파드의 종료에 대해 어떠한 보증을 제공하지 않는다. 스테이트풀셋에서는 파드가 순차적이고 정상적으로 종료(graceful termination)되도록 하려면, 삭제 전 스테이트풀셋의 스케일을 0으로 축소할 수 있다.
apiVersion:v1kind:Servicemetadata:name:nginxlabels:app:nginxspec:ports:- port:80name:webclusterIP:Noneselector:app:nginx---apiVersion:apps/v1kind:StatefulSetmetadata:name:webspec:selector:matchLabels:app:nginx# has to match .spec.template.metadata.labelsserviceName:"nginx"replicas:3# by default is 1template:metadata:labels:app:nginx# has to match .spec.selector.matchLabelsspec:terminationGracePeriodSeconds:10containers:- name:nginximage:k8s.gcr.io/nginx-slim:0.8ports:- containerPort:80name:webvolumeMounts:- name:wwwmountPath:/usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name:wwwspec:accessModes:["ReadWriteOnce"]storageClassName:"my-storage-class"resources:requests:storage:1Gi
위의 예시에서:
이름이 nginx라는 헤드리스 서비스는 네트워크 도메인을 컨트롤하는데 사용 한다.
이름이 web인 스테이트풀셋은 3개의 nginx 컨테이너의 레플리카가 고유의 파드에서 구동될 것이라 지시하는 Spec을 갖는다.
volumeClaimTemplates은 퍼시스턴트 볼륨 프로비저너에서 프로비전한 퍼시스턴트 볼륨을 사용해서 안정적인 스토리지를 제공한다.
스테이트풀셋의 .spec.selector 필드는 .spec.template.metadata.labels 레이블과 일치하도록 설정해야 한다. 쿠버네티스 1.8 이전에서는 생략시에 .spec.selector 필드가 기본 설정 되었다. 1.8 과 이후 버전에서는 파드 셀렉터를 명시하지 않으면 스테이트풀셋 생성시 유효성 검증 오류가 발생하는 결과가 나오게 된다.
파드 신원
스테이트풀셋 파드는 순서, 안정적인 네트워크 신원
그리고 안정적인 스토리지로 구성되는 고유한 신원을 가진다.
신원은 파드가 어떤 노드에 있고, (재)스케줄과도 상관없이 파드에 붙어있다.
순서 색인
N개의 레플리카가 있는 스테이트풀셋은 스테이트풀셋에 있는
각 파드에 0에서 N-1 까지의 정수가 순서대로 할당되며 해당 스테이트풀셋 내에서 고유 하다.
안정적인 네트워크 신원
스테이트풀셋의 각 파드는 스테이트풀셋의 이름과 파드의 순번에서
호스트 이름을 얻는다. 호스트 이름을 구성하는 패턴은
$(statefulset name)-$(ordinal) 이다. 위의 예시에서 생성된 3개 파드의 이름은
web-0,web-1,web-2 이다.
스테이트풀셋은 스테이트풀셋에 있는 파드의 도메인을 제어하기위해
헤드리스 서비스를 사용할 수 있다.
이 서비스가 관리하는 도메인은 $(service name).$(namespace).svc.cluster.local 의 형식을 가지며,
여기서 "cluster.local"은 클러스터 도메인이다.
각 파드는 생성되면 $(podname).$(governing service domain) 형식을 가지고
일치되는 DNS 서브도메인을 가지며, 여기서 거버닝 서비스(governing service)는
스테이트풀셋의 serviceName 필드에 의해 정의된다.
클러스터에서 DNS가 구성된 방식에 따라, 새로 실행된 파드의 DNS 이름을
즉시 찾지 못할 수 있다. 이 동작은 클러스터의 다른 클라이언트가
파드가 생성되기 전에 파드의 호스트 이름에 대한 쿼리를 이미 보낸 경우에 발생할 수 있다.
네거티브 캐싱(DNS에서 일반적)은 이전에 실패한 조회 결과가
파드가 실행된 후에도 적어도 몇 초 동안 기억되고 재사용됨을 의미한다.
파드를 생성한 후 즉시 파드를 검색해야 하는 경우, 몇 가지 옵션이 있다.
DNS 조회에 의존하지 않고 쿠버네티스 API를 직접(예를 들어 watch 사용) 쿼리한다.
쿠버네티스 DNS 공급자의 캐싱 시간(일반적으로 CoreDNS의 컨피그맵을 편집하는 것을 의미하며, 현재 30초 동안 캐시함)을 줄인다.
제한사항 섹션에서 언급한 것처럼 사용자는
파드의 네트워크 신원을 책임지는
헤드리스 서비스를 생성할 책임이 있다.
여기 클러스터 도메인, 서비스 이름, 스테이트풀셋 이름을 선택을 하고,
그 선택이 스테이트풀셋 파드의 DNS이름에 어떻게 영향을 주는지에 대한 약간의 예시가 있다.
클러스터 도메인
서비스 (ns/이름)
스테이트풀셋 (ns/이름)
스테이트풀셋 도메인
파드 DNS
파드 호스트 이름
cluster.local
default/nginx
default/web
nginx.default.svc.cluster.local
web-{0..N-1}.nginx.default.svc.cluster.local
web-{0..N-1}
cluster.local
foo/nginx
foo/web
nginx.foo.svc.cluster.local
web-{0..N-1}.nginx.foo.svc.cluster.local
web-{0..N-1}
kube.local
foo/nginx
foo/web
nginx.foo.svc.kube.local
web-{0..N-1}.nginx.foo.svc.kube.local
web-{0..N-1}
참고: 클러스터 도메인이 달리 구성된 경우가
아니라면 cluster.local로 설정된다.
안정된 스토리지
쿠버네티스는 각 VolumeClaimTemplate마다 하나의 퍼시스턴트 볼륨을
생성한다. 위의 nginx 예시에서 각 파드는 my-storage-class 라는 스토리지 클래스와
1 Gib의 프로비전된 스토리지를 가지는 단일 퍼시스턴트 볼륨을 받게 된다. 만약 스토리지 클래스가
명시되지 않은 경우, 기본 스토리지 클래스가 사용된다. 파드가 노드에서 스케줄 혹은 재스케줄이 되면
파드의 volumeMounts 는 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨이 마운트 된다.
참고로, 파드 퍼시스턴트 볼륨 클레임과 관련된 퍼시스턴트 볼륨은
파드 또는 스테이트풀셋이 삭제되더라도 삭제되지 않는다.
이것은 반드시 수동으로 해야 한다.
파드 이름 레이블
스테이트풀셋 컨트롤러(Controller)
가 파드를 생성할 때 파드 이름으로 statefulset.kubernetes.io/pod-name
레이블이 추가된다. 이 레이블로 스테이트풀셋의 특정 파드에 서비스를
연결할 수 있다.
디플로이먼트와 스케일링 보증
N개의 레플리카가 있는 스테이트풀셋이 파드를 배포할 때 연속해서 {0..N-1}의 순서로 생성한다.
파드가 삭제될 때는 {N-1..0}의 순서인 역순으로 종료된다.
파드에 스케일링 작업을 적용하기 전에 모든 선행 파드가 Running 및 Ready 상태여야 한다.
파드가 종료되기 전에 모든 후속 파드가 완전히 종료 되어야 한다.
스테이트풀셋은 pod.Spec.TerminationGracePeriodSeconds 을 0으로 명시해서는 안된다. 이 방법은 안전하지 않으며, 사용하지 않기를 강권한다. 자세한 설명은 스테이트풀셋 파드 강제 삭제를 참고한다.
위의 nginx 예시가 생성될 때 web-0, web-1, web-2 순서로 3개 파드가
배포된다. web-1은 web-0이
Running 및 Ready 상태가 되기 전에는 배포되지 않으며,
web-2 도 web-1이 Running 및 Ready 상태가 되기 전에는 배포되지 않는다. 만약 web-1이 Running 및 Ready 상태가 된 이후,
web-2가 시작되기 전에 web-0이 실패하게 된다면, web-2는 web-0이 성공적으로 재시작이되고,
Running 및 Ready 상태가 되기 전까지 시작되지 않는다.
만약 사용자가 배포된 예제의 스테이트풀셋을 replicas=1 으로 패치해서
스케일한 경우 web-2가 먼저 종료된다. web-1은 web-2가 완전히 종료 및 삭제되기
전까지 정지되지 않는다. 만약 web-2의 종료 및 완전히 중지되고, web-1이 종료되기 전에
web-0이 실패할 경우 web-1은 web-0이 Running 및 Ready 상태가
되기 전까지 종료되지 않는다.
파드 관리 정책
쿠버네티스 1.7 및 이후에는 스테이트풀셋의 .spec.podManagementPolicy 필드를
통해 고유성 및 신원 보증을 유지하면서 순차 보증을 완화한다.
OrderedReady 파드 관리
OrderedReady 파드 관리는 스테이트풀셋의 기본이다.
이것은 위에서 설명한 행위를 구현한다.
Parallel 파드 관리
Parallel 파드 관리는 스테이트풀셋 컨트롤러에게 모든 파드를
병렬로 실행 또는 종료하게 한다. 그리고 다른 파드의 실행이나
종료에 앞서 파드가 Running 및 Ready 상태가 되거나 완전히 종료되기를 기다리지 않는다.
이 옵션은 오직 스케일링 작업에 대한 동작에만 영향을 미친다. 업데이트는 영향을
받지 않는다.
업데이트 전략
스테이트풀셋의 .spec.updateStrategy 필드는 스테이트풀셋의
파드에 대한 컨테이너, 레이블, 리소스의 요청/제한 그리고 주석에 대한 자동화된 롤링 업데이트를
구성하거나 비활성화할 수 있다. 두 가지 가능한 전략이 있다.
OnDelete(삭제시)
스테이트풀셋의 .spec.updateStrategy.type 은 OnDelete 를 설정하며,
스테이트풀셋 컨트롤러는 스테이트풀셋의 파드를 자동으로 업데이트하지 않는다.
사용자는 컨트롤러가 스테이트풀셋의
.spec.template를 반영하는 수정된 새로운 파드를 생성하도록 수동으로 파드를 삭제해야 한다.
RollingUpdate(롤링 업데이트)
롤링 업데이트 의 업데이트 전략은 스테이트풀셋의 파드에 대한 롤링 업데이트를
구현한다. 롤링 업데이트는 .spec.updateStrategy 가 지정되지 않으면 기본 전략이 된다.
롤링 업데이트
스테이트풀셋에 롤링 업데이트 가 .spec.updateStrategy.type 에 설정되면
스테이트풀셋 컨트롤러는 스테이트풀셋의 각 파드를 삭제 및 재생성한다. 이 과정에서 똑같이
순차적으로 파드가 종료되고(가장 큰 순서 색인에서부터에서 작은 순서 색인쪽으로),
각 파드의 업데이트는 한 번에 하나씩 한다.
쿠버네티스 컨트롤 플레인은 이전 버전을 업데이트 하기 전에, 업데이트된 파드가 실행 및 준비될 때까지 기다린다.
.spec.minReadySeconds(최소 준비 시간 초 참조)를 설정한 경우, 컨트롤 플레인은 파드가 준비 상태로 전환된 후 해당 시간을 추가로 기다린 후 이동한다.
파티션 롤링 업데이트
롤링 업데이트 의 업데이트 전략은 .spec.updateStrategy.rollingUpdate.partition
를 명시해서 파티션 할 수 있다. 만약 파티션을 명시하면 스테이트풀셋의 .spec.template 가
업데이트 될 때 부여된 수가 파티션보다 크거나 같은 모든 파드가 업데이트 된다.
파티션보다 작은 수를 가진 모든 파드는 업데이트 되지 않으며,
삭제 된 경우라도 이전 버전에서 재생성된다.
만약 스테이트풀셋의 .spec.updateStrategy.rollingUpdate.partition 이
.spec.replicas 보다 큰 경우 .spec.template 의 업데이트는 해당 파드에 전달하지 않는다.
대부분의 케이스는 파티션을 사용할 필요가 없지만 업데이트를 준비하거나,
카나리의 롤 아웃 또는 단계적인 롤 아웃을 행하려는 경우에는 유용하다.
강제 롤백
기본 파드 관리 정책 (OrderedReady)과
함께 롤링 업데이트를 사용할 경우
직접 수동으로 복구를 해야하는 고장난 상태가 될 수 있다.
만약 파드 템플릿을 Running 및 Ready 상태가 되지 않는 구성으로 업데이트하는
경우(예시: 잘못된 바이너리 또는 애플리케이션-레벨 구성 오류로 인한)
스테이트풀셋은 롤아웃을 중지하고 기다린다.
이 상태에서는 파드 템플릿을 올바른 구성으로 되돌리는 것으로 충분하지 않다.
알려진 이슈로
인해 스테이트풀셋은 손상된 파드가 준비(절대 되지 않음)될 때까지 기다리며
작동하는 구성으로 되돌아가는 시도를 하기
전까지 기다린다.
템플릿을 되돌린 이후에는 스테이트풀셋이 이미 잘못된 구성으로
실행하려고 시도한 모든 파드를 삭제해야 한다.
그러면 스테이트풀셋은 되돌린 템플릿을 사용해서 파드를 다시 생성하기 시작한다.
최소 준비 시간 초
FEATURE STATE:Kubernetes v1.22 [alpha]
.spec.minReadySeconds는 새로 생성된 파드가 사용가능하다고 간주되도록
컨테이너가 충돌되지 않고 준비되는 최소 시간 초를 지정하는 선택적 필드이다.
기본값은 0이다(파드는 준비되는 대로 사용 가능한 것으로 간주된다).
파드가 준비가 되는 시기에 대해 더 자세히 알아보고 싶다면,
컨테이너 프로브를 참고한다.
이 필드는 StatefulSetMinReadySeconds기능 게이트를 사용하도록 설정한 경우에만 작동한다.
apiVersion:apps/v1kind:DaemonSetmetadata:name:fluentd-elasticsearchnamespace:kube-systemlabels:k8s-app:fluentd-loggingspec:selector:matchLabels:name:fluentd-elasticsearchtemplate:metadata:labels:name:fluentd-elasticsearchspec:tolerations:# this toleration is to have the daemonset runnable on master nodes# remove it if your masters can't run pods- key:node-role.kubernetes.io/masteroperator:Existseffect:NoSchedulecontainers:- name:fluentd-elasticsearchimage:quay.io/fluentd_elasticsearch/fluentd:v2.5.2resources:limits:memory:200Mirequests:cpu:100mmemory:200MivolumeMounts:- name:varlogmountPath:/var/log- name:varlibdockercontainersmountPath:/var/lib/docker/containersreadOnly:trueterminationGracePeriodSeconds:30volumes:- name:varloghostPath:path:/var/log- name:varlibdockercontainershostPath:path:/var/lib/docker/containers
.spec.template 는 파드 템플릿이다.
이것은 중첩되어 있다는 점과 apiVersion 또는 kind 를 가지지 않는 것을 제외하면
파드와 정확히 같은 스키마를 가진다.
데몬셋의 파드 템플릿에는 파드의 필수 필드 외에도 적절한 레이블이 명시되어야
한다(파드 셀렉터를 본다).
데몬셋의 파드 템플릿의 RestartPolicy는 Always 를 가져야 하며,
명시되지 않은 경우 기본으로 Always가 된다.
파드 셀렉터
.spec.selector 필드는 파드 셀렉터이다. 이것은
잡의 .spec.selector 와 같은 동작을 한다.
쿠버네티스 1.8 부터는 레이블이 .spec.template 와 일치하는 파드 셀렉터를 명시해야 한다.
파드 셀렉터는 비워두면 더 이상 기본 값이 설정이 되지 않는다.
셀렉터의 기본 값은 kubectl apply 과 호환되지 않는다.
또한, 한 번 데몬셋이 만들어지면 .spec.selector 의 변형은 가능하지 않다.
파드 셀렉터를 변형하면 의도하지 않게 파드는 고아가 되거나 사용자에게 혼란을 주는 것으로 밝혀졌다.
.spec.selector 는 다음 2개의 필드로 구성된 오브젝트이다.
matchLabels - 레플리케이션 컨트롤러의
.spec.selector 와 동일하게 작동한다.
matchExpressions - 키, 값 목록 그리고 키 및 값에 관련된 연산자를
명시해서 보다 정교한 셀렉터를 만들 수 있다.
2개의 필드가 명시되면 두 필드를 모두 만족하는 것(ANDed)이 결과가 된다.
만약 .spec.selector 를 명시하면, 이것은 .spec.template.metadata.labels 와 일치해야 한다.
일치하지 않는 구성은 API에 의해 거부된다.
오직 일부 노드에서만 파드 실행
만약 .spec.template.spec.nodeSelector 를 명시하면 데몬셋 컨트롤러는
노드 셀렉터와
일치하는 노드에 파드를 생성한다.
마찬가지로 .spec.template.spec.affinity 를 명시하면
데몬셋 컨트롤러는 노드 어피니티와 일치하는 노드에 파드를 생성한다.
만약 둘 중 하나를 명시하지 않으면 데몬셋 컨트롤러는 모든 노드에서 파드를 생성한다.
데몬 파드가 스케줄 되는 방법
기본 스케줄러로 스케줄
FEATURE STATE:Kubernetes v1.23 [stable]
데몬셋은 자격이 되는 모든 노드에서 파드 사본이 실행하도록 보장한다. 일반적으로
쿠버네티스 스케줄러에 의해 파드가 실행되는 노드가 선택된다. 그러나
데몬셋 파드는 데몬셋 컨트롤러에 의해 생성되고 스케줄된다.
이에 대한 이슈를 소개한다.
파드 동작의 불일치: 스케줄 되기 위해서 대기 중인 일반 파드는 Pending 상태로 생성된다.
그러나 데몬셋 파드는 Pending 상태로 생성되지 않는다.
이것은 사용자에게 혼란을 준다.
파드 선점은
기본 스케줄러에서 처리한다. 선점이 활성화되면 데몬셋 컨트롤러는
파드 우선순위와 선점을 고려하지 않고 스케줄 한다.
ScheduleDaemonSetPods 로 데몬셋 파드에 .spec.nodeName 용어 대신
NodeAffinity 용어를 추가해서 데몬셋 컨트롤러 대신 기본
스케줄러를 사용해서 데몬셋을 스케줄할 수 있다. 이후에 기본
스케줄러를 사용해서 대상 호스트에 파드를 바인딩한다. 만약 데몬셋 파드에
이미 노드 선호도가 존재한다면 교체한다(대상 호스트를 선택하기 전에
원래 노드의 어피니티가 고려된다). 데몬셋 컨트롤러는
데몬셋 파드를 만들거나 수정할 때만 이런 작업을 수행하며,
데몬셋의 spec.template 은 변경되지 않는다.
또한, 데몬셋 파드에 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에 대해 이해하기 위해
오브젝트 정의를 읽는다.
2.5 - 잡
잡에서 하나 이상의 파드를 생성하고 지정된 수의 파드가 성공적으로 종료될 때까지 계속해서 파드의 실행을 재시도한다.
파드가 성공적으로 완료되면, 성공적으로 완료된 잡을 추적한다. 지정된 수의
성공 완료에 도달하면, 작업(즉, 잡)이 완료된다. 잡을 삭제하면 잡이 생성한
파드가 정리된다. 작업을 일시 중지하면 작업이 다시 재개될 때까지 활성 파드가
삭제된다.
간단한 사례는 잡 오브젝트를 하나 생성해서 파드 하나를 안정적으로 실행하고 완료하는 것이다.
첫 번째 파드가 실패 또는 삭제된 경우(예로는 노드 하드웨어의 실패 또는
노드 재부팅) 잡 오브젝트는 새로운 파드를 기동시킨다.
잡을 사용하면 여러 파드를 병렬로 실행할 수도 있다.
잡을 스케줄에 따라 구동하고 싶은 경우(단일 작업이든, 여러 작업의 병렬 수행이든), 크론잡(CronJob)을 참고한다.
예시 잡 실행하기
다음은 잡 설정 예시이다. 예시는 파이(π)의 2000 자리까지 계산해서 출력한다.
이를 완료하는 데 약 10초가 소요된다.
만약 잡 컨트롤러가 어떤 이유(ResourceQuota 의 부족, 권한 부족 등)로든 파드 생성에 실패한 경우,
요청한 것보다 적은 수의 파드가 있을 수 있다.
잡 컨트롤러는 동일한 잡에서 과도하게 실패한 이전 파드들로 인해 새로운 파드의 생성을 조절할 수 있다.
파드가 정상적으로(gracefully) 종료되면, 중지하는데 시간이 소요된다.
완료 모드
FEATURE STATE:Kubernetes v1.22 [beta]
완료 횟수가 고정적인 완료 횟수 즉, null이 아닌 .spec.completions 가 있는 잡은
.spec.completionMode 에 지정된 완료 모드를 가질 수 있다.
NonIndexed (기본값): .spec.completions 가 성공적으로
완료된 파드가 있는 경우 작업이 완료된 것으로 간주된다. 즉, 각 파드
완료는 서로 상동하다(homologous). null .spec.completions 가 있는
잡은 암시적으로 NonIndexed 이다.
Indexed: 잡의 파드는 연결된 완료 인덱스를 0에서 .spec.completions-1 까지
가져온다. 이 인덱스는 다음의 세 가지 메카니즘으로 얻을 수 있다.
파드 호스트네임 중 일부($(job-name)-$(index) 형태). 인덱스된(Indexed) 잡과
서비스를 결합하여 사용하고
있다면, 잡에 속한 파드는 DNS를 이용하여 서로를 디스커버 하기 위해 사전에 결정된
호스트네임을 사용할 수 있다.
컨테이너화된 태스크의 경우, JOB_COMPLETION_INDEX 환경 변수.
각 인덱스에 대해 성공적으로 완료된 파드가 하나 있으면 작업이 완료된 것으로
간주된다. 이 모드를 사용하는 방법에 대한 자세한 내용은
정적 작업 할당을 사용한 병렬 처리를 위해 인덱싱된 잡을 참고한다.
참고로, 드물기는 하지만, 동일한 인덱스에 대해 둘 이상의 파드를 시작할 수
있지만, 그 중 하나만 완료 횟수에 포함된다.
파드와 컨테이너 장애 처리하기
파드내 컨테이너의 프로세스가 0이 아닌 종료 코드로 종료되었거나 컨테이너 메모리 제한을
초과해서 죽는 등의 여러가지 이유로 실패할 수 있다. 만약 이런 일이
발생하고 .spec.template.spec.restartPolicy = "OnFailure" 라면 파드는
노드에 그대로 유지되지만, 컨테이너는 다시 실행된다. 따라서 프로그램은 로컬에서 재시작될 때의
케이스를 다루거나 .spec.template.spec.restartPolicy = "Never" 로 지정해야 한다.
더 자세한 정보는 파드 라이프사이클의 restartPolicy 를 본다.
파드가 노드에서 내보내지는 경우(노드 업그레이드, 재부팅, 삭제 등) 또는 파드의 컨테이너가 실패
되고 .spec.template.spec.restartPolicy = "Never" 로 설정됨과 같은 여러 이유로
전체 파드가 실패할 수 있다. 파드가 실패하면 잡 컨트롤러는
새 파드를 시작한다. 이 의미는 애플리케이션이 새 파드에서 재시작될 때 이 케이스를 처리해야
한다는 점이다. 특히, 이전 실행으로 인한 임시파일, 잠금, 불완전한 출력 그리고 이와 유사한
것들을 처리해야 한다.
.spec.parallelism = 1, .spec.completions = 1 그리고
.spec.template.spec.restartPolicy = "Never" 를 지정하더라도 같은 프로그램을
두 번 시작하는 경우가 있다는 점을 참고한다.
.spec.parallelism 그리고 .spec.completions 를 모두 1보다 크게 지정한다면 한번에
여러 개의 파드가 실행될 수 있다. 따라서 파드는 동시성에 대해서도 관대(tolerant)해야 한다.
파드 백오프(backoff) 실패 정책
구성 등의 논리적 오류로 인해 약간의 재시도 이후에
잡을 실패하게 만들려는 경우가 있다.
이렇게 하려면 .spec.backoffLimit 에 잡을 실패로 간주하기 이전에
재시도할 횟수를 설정한다. 백오프 제한은 기본적으로 6으로 설정되어 있다. 잡과
관련한 실패한 파드는 최대 6분안에서 기하급수적으로 증가하는 백-오프 지연 (10초, 20초, 40초 ...)
한도가 되어 잡 컨트롤러에 의해 재생성된다. 잡의 파드가 삭제되거나
해당 시간 동안 잡에 대한 다른 파드가 실패 없이 성공했을 때 백 오프
카운트가 재설정된다.
참고: 만약 잡에 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 에 지정된 시간 제한에 도달하면 추가 파드를 배포하지 않는다.
잡의 사양과 잡의 파드 템플릿 사양에는 모두 activeDeadlineSeconds 필드가 있다는 점을 참고한다. 이 필드를 적절한 레벨로 설정해야 한다.
restartPolicy 는 잡 자체에 적용되는 것이 아니라 파드에 적용된다는 점을 유념한다. 잡의 상태가 type: Failed 이 되면, 잡의 자동 재시작은 없다.
즉, .spec.activeDeadlineSeconds 와 .spec.backoffLimit 로 활성화된 잡의 종료 메커니즘은 영구적인 잡의 실패를 유발하며 이를 해결하기 위해 수동 개입이 필요하다.
완료된 잡을 자동으로 정리
완료된 잡은 일반적으로 시스템에서 더 이상 필요로 하지 않는다. 시스템 내에
이를 유지한다면 API 서버에 부담이 된다.
만약 크론잡과
같은 상위 레벨 컨트롤러가 잡을 직접 관리하는 경우,
지정된 용량 기반 정리 정책에 따라 크론잡이 잡을 정리할 수 있다.
완료된 잡을 위한 TTL 메커니즘
FEATURE STATE:Kubernetes v1.21 [beta]
완료된 잡 (Complete 또는 Failed)을 자동으로 정리하는 또 다른 방법은
잡의 .spec.ttlSecondsAfterFinished 필드를 지정해서 완료된 리소스에 대해
TTL 컨트롤러에서
제공하는 TTL 메커니즘을 사용하는
것이다.
TTL 컨트롤러는 잡을 정리하면 잡을 계단식으로 삭제한다.
즉, 잡과 함께 파드와 같은 종속 오브젝트를 삭제한다. 잡을
삭제하면 finalizer와 같은 라이프사이클 보증이 보장되는 것을
참고한다.
만약 필드를 0 으로 설정하면, 잡이 완료된 직후에 자동으로
삭제되도록 할 수 있다. 만약 필드를 설정하지 않으면, 이 잡이 완료된
후에 TTL 컨트롤러에 의해 정리되지 않는다.
잡 패턴
잡 오브젝트를 사용해서 신뢰할 수 있는 파드의 병렬 실행을 지원할 수 있다. 잡 오브젝트는 과학
컴퓨팅(scientific computing)에서 일반적으로 사용되는 밀접하게 통신하는 병렬 프로세스를 지원하도록
설계되지 않았다. 잡 오브젝트는 독립적이지만 관련된 작업 항목 집합의 병렬 처리를 지원한다.
여기에는 전송할 이메일들, 렌더링할 프레임, 코드 변환이 필요한 파일, NoSQL 데이터베이스에서의
키 범위 스캔 등이 있다.
복잡한 시스템에는 여러 개의 다른 작업 항목 집합이 있을 수 있다. 여기서는 사용자와
함께 관리하려는 하나의 작업 항목 집합 — 배치 잡 을 고려하고 있다.
병렬 계산에는 몇몇 다른 패턴이 있으며 각각의 장단점이 있다.
트레이드오프는 다음과 같다.
각 작업 항목에 대한 하나의 잡 오브젝트 vs 모든 작업 항목에 대한 단일 잡 오브젝트. 후자는
작업 항목 수가 많은 경우 더 적합하다. 전자는 사용자와 시스템이 많은 수의 잡 오브젝트를
관리해야 하는 약간의 오버헤드를 만든다.
작업 항목과 동일한 개수의 파드 생성 vs 각 파드에서 다수의 작업 항목을 처리.
전자는 일반적으로 기존 코드와 컨테이너를 거의 수정할 필요가 없다. 후자는
이전 글 머리표(-)와 비슷한 이유로 많은 수의 작업 항목에 적합하다.
여러 접근 방식이 작업 큐를 사용한다. 이를 위해서는 큐 서비스를 실행하고,
작업 큐를 사용하도록 기존 프로그램이나 컨테이너를 수정해야 한다.
다른 접근 방식들은 기존에 컨테이너화된 애플리케이션에 보다 쉽게 적용할 수 있다.
여기에 트레이드오프가 요약되어 있고, 2열에서 4열까지가 위의 트레이드오프에 해당한다.
패턴 이름은 예시와 더 자세한 설명을 위한 링크이다.
.spec.completions 로 완료를 지정할 때, 잡 컨트롤러에 의해 생성된 각 파드는
동일한 사양을 갖는다. 이 의미는
작업의 모든 파드는 동일한 명령 줄과 동일한 이미지,
동일한 볼륨, (거의) 동일한 환경 변수를 가진다는 점이다. 이 패턴은
파드가 다른 작업을 수행하도록 배열하는 다른 방법이다.
이 표는 각 패턴에 필요한 .spec.parallelism 그리고 .spec.completions 설정을 보여준다.
여기서 W 는 작업 항목의 수이다.
참고: 이 기능은 쿠버네티스 버전 1.21에서는 알파 상태였으며,
이 때문에 이 기능을 활성화하기 위해서는 추가적인 단계를 진행해야 한다.
현재 사용 중인 쿠버네티스 버전과 맞는 문서를 읽고 있는 것이 맞는지 다시 한번 확인한다.
잡이 생성되면, 잡 컨트롤러는 잡의 요구 사항을 충족하기 위해
즉시 파드 생성을 시작하고 잡이 완료될 때까지
계속한다. 그러나, 잡의 실행을 일시적으로 중단하고 나중에
다시 시작할 수도 있다. 잡을 일시 중지하려면, 잡의 .spec.suspend 필드를 true로
업데이트할 수 있다. 나중에, 다시 재개하려면, false로 업데이트한다.
.spec.suspend 로 설정된 잡을 생성하면 일시 중지된 상태로
생성된다.
잡이 일시 중지에서 재개되면, 해당 .status.startTime 필드가
현재 시간으로 재설정된다. 즉, 잡이 일시 중지 및 재개되면 .spec.activeDeadlineSeconds
타이머가 중지되고 재설정된다.
잡을 일시 중지하면 모든 활성 파드가 삭제된다. 잡이
일시 중지되면, SIGTERM 시그널로 파드가 종료된다.
파드의 정상 종료 기간이 적용되며 사용자의 파드는 이 기간 동안에
이 시그널을 처리해야 한다. 나중에 진행 상황을 저장하거나
변경 사항을 취소하는 작업이 포함될 수 있다. 이 방법으로 종료된 파드는
잡의 completions 수에 포함되지 않는다.
"True" 상태인 "Suspended" 유형의 잡의 컨디션은 잡이
일시 중지되었음을 의미한다. 이 lastTransitionTime 필드는 잡이 일시 중지된
기간을 결정하는 데 사용할 수 있다. 해당 컨디션의 상태가 "False"이면, 잡이
이전에 일시 중지되었다가 현재 실행 중이다. 이러한 컨디션이
잡의 상태에 없으면, 잡이 중지되지 않은 것이다.
잡이 일시 중지 및 재개될 때에도 이벤트가 생성된다.
kubectl describe jobs/myjob
Name: myjob
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 12m job-controller Created pod: myjob-hlrpl
Normal SuccessfulDelete 11m job-controller Deleted pod: myjob-hlrpl
Normal Suspended 11m job-controller Job suspended
Normal SuccessfulCreate 3s job-controller Created pod: myjob-jvb44
Normal Resumed 3s job-controller Job resumed
마지막 4개의 이벤트, 특히 "Suspended" 및 "Resumed" 이벤트는
.spec.suspend 필드를 전환한 결과이다. 이 두 이벤트 사이의 시간동안
파드가 생성되지 않았지만, 잡이 재개되자마자 파드 생성이 다시
시작되었음을 알 수 있다.
자신의 파드 셀렉터를 지정하기
일반적으로 잡 오브젝트를 생성할 때 .spec.selector 를 지정하지 않는다.
시스템의 기본적인 로직은 잡이 생성될 때 이 필드를 추가한다.
이것은 다른 잡과 겹치지 않는 셀렉터 값을 선택한다.
그러나, 일부 케이스에서는 이 자동화된 설정 셀렉터를 재정의해야 할 수도 있다.
이를 위해 잡의 .spec.selector 를 설정할 수 있다.
이 것을 할 때는 매우 주의해야 한다. 만약 해당 잡의 파드에 고유하지
않고 연관이 없는 파드와 일치하는 레이블 셀렉터를 지정하면, 연관이 없는 잡의 파드가 삭제되거나,
해당 잡이 다른 파드가 완료한 것으로 수를 세거나, 하나 또는
양쪽 잡 모두 파드 생성이나 실행 완료를 거부할 수도 있다. 만약 고유하지 않은 셀렉터가
선택된 경우, 다른 컨트롤러(예: 레플리케이션 컨트롤러)와 해당 파드는
예측할 수 없는 방식으로 작동할 수 있다. 쿠버네티스는 당신이 .spec.selector 를 지정할 때
발생하는 실수를 막을 수 없을 것이다.
다음은 이 기능을 사용하려는 경우의 예시이다.
잡 old 가 이미 실행 중이다. 기존 파드가 계속
실행되기를 원하지만, 잡이 생성한 나머지 파드에는 다른
파드 템플릿을 사용하고 잡으로 하여금 새 이름을 부여하기를 원한다.
그러나 관련된 필드들은 업데이트가 불가능하기 때문에 잡을 업데이트할 수 없다.
따라서 kubectl delete jobs/old --cascade=orphan 명령을 사용해서
잡 old 를 삭제하지만, 파드를 실행 상태로 둔다.
삭제하기 전에 어떤 셀렉터를 사용하는지 기록한다.
새 잡 자체는 a8f3d00d-c6d2-11e5-9f87-42010af00002 와 다른 uid 를 가지게 될 것이다.
manualSelector: true 를 설정하면 시스템에게 사용자가 무엇을 하는지 알고 있음을 알리고, 이런
불일치를 허용한다.
종료자(finalizers)를 이용한 잡 추적
FEATURE STATE:Kubernetes v1.22 [alpha]
참고:
이 기능을 이용하기 위해서는
API 서버와
컨트롤러 매니저에 대해
JobTrackingWithFinalizers기능 게이트를 활성화해야 한다.
기본적으로는 비활성화되어 있다.
이 기능이 활성화되면, 컨트롤 플레인은 아래에 설명할 동작을 이용하여 새로운 잡이 생성되는지 추적한다.
기존에 존재하던 잡은 영향을 받지 않는다.
사용자가 느낄 수 있는 유일한 차이점은 컨트롤 플레인이 잡 종료를 좀 더 정확하게 추적할 수 있다는 것이다.
이 기능이 활성화되지 않으면, 잡
컨트롤러는
succeeded와 failed 파드의 수를 세어 잡 상태를 추적한다.
그런데, 파드는 다음과 같은 이유로 제거될 수 있다.
노드가 다운되었을 때 가비지 콜렉터가 버려진(orphan) 파드를 제거
가비지 콜렉터가 (Succeeded 또는 Failed 단계에 있는) 완료된 파드를
일정 임계값 이후에 제거
잡에 속한 파드를 사용자가 임의로 제거
(쿠버네티스에 속하지 않는) 외부 컨트롤러가 파드를 제거하거나
교체
클러스터에서 JobTrackingWithFinalizers 기능을 활성화하면,
컨트롤 플레인은 잡에 속하는 파드의 상태를 추적하고
API 서버에서 파드가 제거되면 이를 알아챈다.
이를 위해, 잡 컨트롤러는 batch.kubernetes.io/job-tracking 종료자를 갖는 파드를 생성한다.
컨트롤러는 파드의 상태 변화가 잡 상태에 반영된 후에만 종료자를 제거하므로,
이후 다른 컨트롤러나 사용자가 파드를 제거할 수 있다.
잡 컨트롤러는 새로운 잡에 대해서만 새로운 알고리즘을 적용한다.
이 기능이 활성화되기 전에 생성된 잡은 영향을 받지 않는다.
잡에 batch.kubernetes.io/job-tracking 어노테이션이 있는지 확인하여,
잡 컨트롤러가 파드 종료자를 이용하여 잡을 추적하고 있는지 여부를 확인할 수 있다.
이 어노테이션을 잡에 수동으로 추가하거나 제거해서는 안 된다.
대안
베어(Bare) 파드
파드가 실행 중인 노드가 재부팅되거나 실패하면 파드가 종료되고
다시 시작되지 않는다. 그러나 잡은 종료된 항목을 대체하기 위해 새 파드를 생성한다.
따라서, 애플리케이션에 단일 파드만 필요한 경우에도 베어 파드 대신
잡을 사용하는 것을 권장한다.
레플리케이션 컨트롤러
잡은 레플리케이션 컨트롤러를 보완한다.
레플리케이션 컨트롤러는 종료하지 않을 파드(예: 웹 서버)를 관리하고, 잡은 종료될 것으로
예상되는 파드(예: 배치 작업)를 관리한다.
파드 라이프사이클에서 설명한 것처럼, 잡 은 오직OnFailure 또는 Never 와 같은 RestartPolicy 를 사용하는 파드에만 적절하다.
(참고: RestartPolicy 가 설정되지 않은 경우에는 기본값은 Always 이다.)
단일 잡으로 컨트롤러 파드 시작
또 다른 패턴은 단일 잡이 파드를 생성한 후 다른 파드들을 생성해서 해당 파드들에
일종의 사용자 정의 컨트롤러 역할을 하는 것이다. 이를 통해 최대한의 유연성을 얻을 수 있지만,
시작하기에는 다소 복잡할 수 있으며 쿠버네티스와의 통합성이 낮아진다.
이 패턴의 한 예시는 파드를 시작하는 잡이다. 파드는 스크립트를 실행해서
스파크(Spark) 마스터 컨트롤러 (스파크 예시를 본다)를 시작하고,
스파크 드라이버를 실행한 다음, 정리한다.
이 접근 방식의 장점은 전체 프로세스가 잡 오브젝트의 완료를 보장하면서도,
파드 생성과 작업 할당 방법을 완전히 제어하고 유지한다는 것이다.
쿠버네티스의 가비지 수집기는 한때 소유자가 있었지만, 더 이상
소유자가 없는 오브젝트들을 삭제하는 역할을 한다.
소유자(owner)와 종속(dependent)
일부 쿠버네티스 오브젝트는 다른 오브젝트의 소유자이다. 예를 들어 레플리카셋은
파드 집합의 소유자이다. 소유자 오브젝트에게 소유된 오브젝트를 종속
이라고 한다. 모든 종속 오브젝트는 소유하는 오브젝트를 가르키는 metadata.ownerReferences
필드를 가지고 있다.
때때로, 쿠버네티스는 ownerReference 값을 자동적으로 설정한다.
예를 들어 레플리카셋을 만들 때 쿠버네티스는 레플리카셋에 있는 각 파드의
ownerReference 필드를 자동으로 설정한다. 1.8 에서는 쿠버네티스가
레플리케이션컨트롤러, 레플리카셋, 스테이트풀셋, 데몬셋, 디플로이먼트, 잡
그리고 크론잡에 의해서 생성되거나 차용된 오브젝트의 ownerReference 값을
자동으로 설정한다.
또한 ownerReference 필드를 수동으로 설정해서 소유자와 종속 항목 간의
관계를 지정할 수도 있다.
교차 네임스페이스(cross-namespace)의 소유자 참조는 디자인상 허용되지 않는다.
네임스페이스 종속 항목은 클러스터 범위 또는 네임스페이스 소유자를 지정할 수 있다.
네임스페이스 소유자는 반드시 종속 항목과 동일한 네임스페이스에 있어야 한다.
그렇지 않은 경우, 소유자 참조는 없는 것으로 처리되며, 소유자가 없는 것으로 확인되면
종속 항목이 삭제될 수 있다.
클러스터 범위의 종속 항목은 클러스터 범위의 소유자만 지정할 수 있다.
v1.20 이상에서 클러스터 범위의 종속 항목이 네임스페이스 종류를 소유자로 지정하면,
확인할 수 없는 소유자 참조가 있는 것으로 처리되고 가비지 수집이 될 수 없다.
v1.20 이상에서 가비지 수집기가 잘못된 교차 네임스페이스 ownerReference
또는 네임스페이스 종류를 참조하는 ownerReference 가 있는 클러스터 범위의 종속 항목을 감지하면,
OwnerRefInvalidNamespace 의 원인이 있는 경고 이벤트와 유효하지 않은 종속 항목의 involvedObject 가 보고된다.
kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace 를 실행하여
이러한 종류의 이벤트를 확인할 수 있다.
가비지 수집기의 종속 항목 삭제 방식 제어
오브젝트를 삭제할 때, 오브젝트의 종속 항목을 자동으로 삭제하는지의
여부를 지정할 수 있다. 종속 항목을 자동으로 삭제하는 것을 캐스케이딩(cascading)
삭제 라고 한다. 캐스케이딩 삭제 에는 백그라운드 와 포어그라운드 2가지 모드가 있다.
만약 종속 항목을 자동으로 삭제하지 않고 오브젝트를 삭제한다면,
종속 항목은 분리됨(orphaned) 이라고 한다.
포어그라운드 캐스케이딩 삭제
포어그라운드 캐스케이딩 삭제 에서는 루트 오브젝트가 먼저
"삭제 중(deletion in progress)" 상태가 된다. "삭제 중" 상태에서는
다음 사항이 적용된다.
오브젝트는 REST API를 통해 여전히 볼 수 있음
오브젝트에 deletionTimestamp 가 설정됨
오브젝트의 "foregroundDeletion"에 metadata.finalizers 값이 포함됨.
"삭제 중" 상태가 설정되면, 가비지
수집기는 오브젝트의 종속 항목을 삭제한다. 가비지 수집기는 모든
"차단" 종속 항목(ownerReference.blockOwnerDeletion=true 가 있는 오브젝트)의 삭제가 완료되면,
소유자 오브젝트를 삭제한다.
"foregroundDeletion" 에서는 ownerReference.blockOwnerDeletion=true 로
설정된 종속 항목만 소유자 오브젝트의 삭제를 차단한다는 것을 참고한다.
쿠버네티스 버전 1.7에서는 소유자 오브젝트에 대한 삭제 권한에 따라 blockOwnerDeletion 를 true로 설정하기 위해 사용자 접근을 제어하는
어드미션 컨트롤러가
추가되었기에 권한이 없는 종속 항목은 소유자 오브젝트의 삭제를 지연시킬 수 없다.
만약 오브젝트의 ownerReferences 필드가 컨트롤러(디플로이먼트 또는 레플리카셋과 같은)에
의해 설정된 경우 blockOwnerDeletion이 자동으로 설정되므로 이 필드를 수동으로 수정할 필요가 없다.
백그라운드 캐스케이딩 삭제
백그라운드 캐스케이딩 삭제 에서 쿠버네티스는 소유자 오브젝트를
즉시 삭제하고, 가비지 수집기는 백그라운드에서 종속 항목을
삭제한다.
캐스케이딩 삭제 정책 설정하기
캐스케이딩 삭제 정책을 제어하려면, 오브젝트를 삭제할 때 deleteOptions
인수를 propagationPolicy 필드에 설정한다. 여기에 가능한 값으로는 "Orphan",
"Foreground" 또는 "Background" 이다.
1.7 이전에서는 디플로이먼트와 캐스케이딩 삭제를 사용하면 반드시 propagationPolicy: Foreground
를 사용해서 생성된 레플리카셋뿐만 아니라 해당 파드도 삭제해야 한다. 만약 이 propagationPolicy
유형을 사용하지 않는다면, 레플리카셋만 삭제되고 파드는 분리된 상태로 남을 것이다.
더 많은 정보는 kubeadm/#149를 본다.
TTL 컨트롤러는 실행이 완료된 리소스 오브젝트의 수명을
제한하는 TTL (time to live) 메커니즘을 제공한다. TTL 컨트롤러는 현재
잡(Job)만
처리하며, 파드와 커스텀 리소스와 같이 실행을 완료할 다른 리소스를
처리하도록 확장될 수 있다.
이 기능은 현재 베타이고 기본적으로 활성화되어 있다.
kube-apiserver와 kube-controller-manager에서 TTLAfterFinished기능 게이트를 이용하여 비활성화할 수 있다.
TTL 컨트롤러
현재의 TTL 컨트롤러는 잡만 지원한다. 클러스터 운영자는
예시
와 같이 .spec.ttlSecondsAfterFinished 필드를 명시하여
완료된 잡(완료 또는 실패)을 자동으로 정리하기 위해 이 기능을 사용할 수 있다.
리소스의 작업이 완료된 TTL 초(sec) 후 (다른 말로는, TTL이 만료되었을 때),
TTL 컨트롤러는 해당 리소스가 정리될 수 있다고 가정한다.
TTL 컨트롤러가 리소스를 정리할때 리소스를 연속적으로 삭제한다. 이는
의존하는 오브젝트도 해당 리소스와 함께 삭제되는 것을 의미한다. 리소스가 삭제되면 완료자(finalizers)와
같은 라이프 사이클 보증이 적용 된다.
TTL 초(sec)는 언제든지 설정이 가능하다. 여기에 잡 필드 중
.spec.ttlSecondsAfterFinished 를 설정하는 몇 가지 예시가 있다.
작업이 완료된 다음, 일정 시간 후에 자동으로 잡이 정리될 수 있도록
리소스 메니페스트에 이 필드를 지정한다.
이미 완료된 기존 리소스에 이 새 기능을 적용하기 위해서 이 필드를
설정한다.
어드미션 웹후크 변형
을 사용해서
리소스 생성시 이 필드를 동적으로 설정 한다. 클러스터 관리자는 이것을
사용해서 완료된 리소스에 대해 TTL 정책을 적용할 수 있다.
리소스가 완료된 이후에
어드미션 웹후크 변형
을 사용해서 이 필드를 동적으로 설정하고, 리소스의 상태,
레이블 등에 따라 다른 TTL 값을 선택한다.
경고
TTL 초(sec) 업데이트
TTL 기간은, 예를 들어 잡의 .spec.ttlSecondsAfterFinished 필드는
리소스를 생성하거나 완료한 후에 수정할 수 있다. 그러나, 잡을
삭제할 수 있게 되면(TTL이 만료된 경우) 시스템은 TTL을 연장하기
위한 업데이트가 성공적인 API 응답을 리턴하더라도
작업이 유지되도록 보장하지 않는다.
시간 차이(Skew)
TTL 컨트롤러는 쿠버네티스 리소스에
저장된 타임스탬프를 사용해서 TTL의 만료 여부를 결정하기 때문에, 이 기능은 클러스터 간의
시간 차이에 민감하며, 시간 차이에 의해서 TTL 컨트롤러가 잘못된 시간에 리소스
오브젝트를 정리하게 될 수 있다.
쿠버네티스에서는 시간 차이를 피하기 위해 모든 노드
(#6159를 본다)
에서 NTP를 실행해야 한다. 시계가 항상 정확한 것은 아니지만, 그 차이는
아주 작아야 한다. 0이 아닌 TTL을 설정할때는 이 위험에 대해 유의해야 한다.
컨트롤 플레인이 파드 또는 베어 컨테이너에서 kube-controller-manager를 실행하는 경우,
kube-controller-manager 컨테이너에 설정된 시간대는
크론잡 컨트롤러가 사용하는 시간대로 결정한다.
크론잡 리소스에 대한 매니페스트를 생성할 때에는 제공하는 이름이
유효한 DNS 서브도메인 이름이어야 한다.
이름은 52자 이하여야 한다. 이는 크론잡 컨트롤러는 제공된 잡 이름에
11자를 자동으로 추가하고, 작업 이름의 최대 길이는
63자라는 제약 조건이 있기 때문이다.
크론잡
크론잡은 백업, 리포트 생성 등의 정기적 작업을 수행하기 위해 사용된다.
각 작업은 무기한 반복되도록 구성해야 한다(예:
1일/1주/1달마다 1회).
작업을 시작해야 하는 해당 간격 내 특정 시점을 정의할 수 있다.
예를 들면, 다음은 해당 작업이 매주 금요일 자정에 시작되어야 하고, 매월 13일 자정(UTC 기준)에도 시작되어야 한다는 뜻이다.
CRON_TZ=UTC 0 0 13 * 5
크론잡 스케줄 표현을 생성하기 위해서 crontab.guru와 같은 웹 도구를 사용할 수도 있다.
크론잡의 한계
크론잡은 일정의 실행시간 마다 약 한 번의 잡 오브젝트를 생성한다. "약" 이라고 하는 이유는
특정 환경에서는 두 개의 잡이 만들어지거나, 잡이 생성되지 않기도 하기 때문이다. 보통 이렇게 하지
않도록 해야겠지만, 완벽히 그럴 수는 없다. 따라서 잡은 멱등원 이 된다.
만약 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초 내 몇 개의 잡이 누락되었는지 카운팅한다.
크론잡은 정해진 일정에 잡 실행을 실패하면 놓쳤다고 카운팅된다. 예를 들면, 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 로 설정한다. 예를 들면, 다음과 같다.
레플리케이션컨트롤러 는 언제든지 지정된 수의 파드 레플리카가
실행 중임을 보장한다.
다시 말하면, 레플리케이션 컨트롤러는 파드 또는 동일 종류의 파드의 셋이 항상 기동되고 사용 가능한지 확인한다.
레플리케이션 컨트롤러의 동작방식
파드가 너무 많으면 레플리케이션 컨트롤러가 추가적인 파드를 제거한다.
너무 적으면 레플리케이션 컨트롤러는 더 많은 파드를 시작한다.
수동으로 생성된 파드와 달리 레플리케이션 컨트롤러가 유지 관리하는 파드는 실패하거나 삭제되거나 종료되는 경우 자동으로 교체된다.
예를 들어, 커널 업그레이드와 같이 파괴적인 유지 보수 작업을 하고 난 이후의 노드에서 파드가 다시 생성된다.
따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용해야 한다.
레플리케이션 컨트롤러는 프로세스 감시자(supervisor)와 유사하지만
단일 노드에서 개별 프로세스를 감시하는 대신 레플리케이션 컨트롤러는
여러 노드에서 여러 파드를 감시한다.
레플리케이션 컨트롤러는 디스커션에서 종종 "rc"로 축약되며
kubectl 명령에서 숏컷으로 사용된다.
간단한 경우는 하나의 레플리케이션 컨트롤러 오브젝트를 생성하여
한 개의 파드 인스턴스를 영구히 안정적으로 실행하는 것이다.
보다 복잡한 사용 사례는 웹 서버와 같이 복제된 서비스의 동일한 레플리카를 여러 개 실행하는 것이다.
레플리케이션 컨트롤러에 속한 모든 파드를 머신이 읽을 수 있는 형식으로 나열하기 위해 다음과 같은 명령을 사용할 수 있다.
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 서브도메인 이름이어야 한다.
환경설정 파일의 동작에 관련된 일반적인 정보는 쿠버네티스 오브젝트 관리를 참고한다.
로컬 컨테이너의 재시작의 경우, 레플리케이션 컨트롤러는 노드의 에이전트에게 위임한다.
예를 들어 Kubelet 혹은 도커이다.
레플리케이션 컨트롤러에서 레이블
레플리케이션 컨트롤러 자체는 레이블 (.metadata.labels) 을 가질 수 있다. 일반적으로 이것을 .spec.template.metadata.labels 와 동일하게 설정할 것이다. .metadata.labels 가 지정되어 있지 않은 경우,
기본은 .spec.template.metadata.labels 이다. 하지만 레이블은
다른 것이 허용되며, .metadata.labels 라벨은 레플리케이션 컨트롤러의
동작에 영향을 미치지 않는다.
파드 셀렉터
.spec.selector 필드는 레이블 셀렉터이다. 레플리케이션 컨트롤러는 셀렉터와 일치하는 레이블이 있는 모든 파드를 관리한다.
직접 생성하거나 삭제된 파드와 다른 사람이나 프로세스가 생성하거나
삭제한 파드를 구분하지 않는다. 이렇게 하면 실행중인 파드에 영향을 주지 않고
레플리케이션 컨트롤러를 교체할 수 있다.
지정된 경우 .spec.template.metadata.labels 은
.spec.selector 와 동일해야 하며 그렇지 않으면 API에 의해 거부된다. .spec.selector 가 지정되지 않은 경우 기본값은
.spec.template.metadata.labels 이다.
또한 일반적으로 이 셀렉터와 레이블이 일치하는 파드를 직접
다른 레플리케이션 컨트롤러 또는 잡과 같은 다른 컨트롤러로 작성해서는 안된다.
그렇게 하면 레플리케이션 컨트롤러는 다른 파드를 생성했다고 생각한다.
쿠버네티스는 이런 작업을 중단해 주지 않는다.
중첩된 셀렉터들을 갖는 다수의 컨트롤러들을 종료하게 되면, 삭제된 것들은 스스로 관리를 해야 한다
(아래를 참조).
다수의 레플리카
.spec.replicas 를 동시에 실행하고 싶은 파드의 수로 설정함으로써
실행할 파드의 수를 지정할 수 있다. 레플리카가 증가 또는 감소한 경우 또는
파드가 정상적으로 종료되고 교체가 일찍 시작되는 경우라면
언제든지 실행중인 수가 더 높거나 낮을 수 있다.
.spec.replicas 를 지정하지 않으면 기본값은 1이다.
레플리케이션 컨트롤러 사용하기
레플리케이션 컨트롤러와 레플리케이션 컨트롤러의 파드 삭제
레플리케이션 컨트롤러와 레플리케이션의 모든 파드를 삭제하려면 kubectl delete 를 사용하라.
Kubectl은 레플리케이션 컨트롤러를 0으로 스케일하고 레플리케이션 컨트롤러 자체를
삭제하기 전에 각 파드를 삭제하기를 기다린다. 이 kubectl 명령이 인터럽트되면 다시 시작할 수 있다.
REST API나 Go 클라이언트 라이브러리를 사용하는 경우 명시적으로 단계를 수행해야 한다(레플리카를 0으로 스케일하고 파드의 삭제를 기다린 이후,
레플리케이션 컨트롤러를 삭제).
REST API나 Go 클라이언트 라이브러리를 사용하는 경우 레플리케이션 컨트롤러 오브젝트를 삭제하라.
원본이 삭제되면 대체할 새로운 레플리케이션 컨트롤러를 생성하여 교체할 수 있다. 오래된 파드와 새로운 파드의 .spec.selector 가 동일하다면,
새로운 레플리케이션 컨트롤러는 오래된 파드를 채택할 것이다. 그러나 기존 파드를
새로운 파드 템플릿과 일치시키려는 노력은 하지 않을 것이다.
새로운 spec에 대한 파드를 제어된 방법으로 업데이트하려면 롤링 업데이트를 사용하라.
레플리케이션 컨트롤러에서 파드 격리
파드는 레이블을 변경하여 레플리케이션 컨트롤러의 대상 셋에서 제거될 수 있다. 이 기술은 디버깅과 데이터 복구를 위해 서비스에서 파드를 제거하는 데 사용될 수 있다. 이 방법으로 제거된 파드는 자동으로 교체된다 (레플리카 수가 변경되지 않는다고 가정).
일반적인 사용법 패턴
다시 스케줄하기
위에서 언급했듯이, 실행하려는 파드가 한 개 혹은 1000개이든 관계없이 레플리케이션 컨트롤러는 노드 실패 또는 파드 종료시 지정된 수의 파드가 존재하도록 보장한다 (예 : 다른 제어 에이전트에 의한 동작으로 인해).
스케일링
레플리케이션컨트롤러는 replicas 필드를 업데이트하여, 수동으로 또는 오토 스케일링 제어 에이전트를 통해, 레플리카의 수를 늘리거나 줄일 수 있다.
롤링 업데이트
레플리케이션 컨트롤러는 파드를 하나씩 교체함으로써 서비스에 대한 롤링 업데이트를 쉽게 하도록 설계되었다.
#1353에서 설명한 것처럼, 권장되는 접근법은 1 개의 레플리카를 가진 새로운 레플리케이션 컨트롤러를 생성하고 새로운 (+1) 컨트롤러 및 이전 (-1) 컨트롤러를 차례대로 스케일한 후 0개의 레플리카가 되면 이전 컨트롤러를 삭제하는 것이다. 예상치 못한 오류와 상관없이 파드 세트를 예측 가능하게 업데이트한다.
이상적으로 롤링 업데이트 컨트롤러는 애플리케이션 준비 상태를 고려하며 주어진 시간에 충분한 수의 파드가 생산적으로 제공되도록 보장할 것이다.
두 레플리케이션 컨트롤러는 일반적으로 롤링 업데이트를 동기화 하는 이미지 업데이트이기 때문에 파드의 기본 컨테이너 이미지 태그와 같이 적어도 하나의 차별화된 레이블로 파드를 생성해야 한다.
다수의 릴리스 트랙
롤링 업데이트가 진행되는 동안 다수의 애플리케이션 릴리스를 실행하는 것 외에도 다수의 릴리스 트랙을 사용하여 장기간에 걸쳐 또는 연속적으로 실행하는 것이 일반적이다. 트랙은 레이블 별로 구분된다.
예를 들어, 서비스는 tier in (frontend), environment in (prod) 이 있는 모든 파드를 대상으로 할 수 있다. 이제 이 계층을 구성하는 10 개의 복제된 파드가 있다고 가정해 보자. 하지만 이 구성 요소의 새로운 버전을 '카나리' 하기를 원한다. 대량의 레플리카에 대해 replicas 를 9로 설정하고 tier=frontend, environment=prod, track=stable 레이블을 설정한 레플리케이션 컨트롤러와, 카나리에 replicas 가 1로 설정된 다른 레플리케이션 컨트롤러에 tier=frontend, environment=prod, track=canary 라는 레이블을 설정할 수 있다. 이제 이 서비스는 카나리와 카나리 이외의 파드 모두를 포함한다. 그러나 레플리케이션 컨트롤러를 별도로 조작하여 테스트하고 결과를 모니터링하는 등의 작업이 혼란스러울 수 있다.
서비스와 레플리케이션컨트롤러 사용
하나의 서비스 뒤에 여러 개의 레플리케이션컨트롤러가 있을 수 있다.
예를 들어 일부 트래픽은 이전 버전으로 이동하고 일부는 새 버전으로 이동한다.
레플리케이션컨트롤러는 자체적으로 종료되지 않지만, 서비스만큼 오래 지속될 것으로 기대되지는 않는다. 서비스는 여러 레플리케이션컨트롤러에 의해 제어되는 파드로 구성될 수 있으며, 서비스 라이프사이클 동안(예를 들어, 서비스를 실행하는 파드 업데이트 수행을 위해) 많은 레플리케이션컨트롤러가 생성 및 제거될 것으로 예상된다. 서비스 자체와 클라이언트 모두 파드를 유지하는 레플리케이션컨트롤러를 의식하지 않는 상태로 남아 있어야 한다.
레플리케이션을 위한 프로그램 작성
레플리케이션 컨트롤러에 의해 생성된 파드는 해당 구성이 시간이 지남에 따라 이질적이 될 수 있지만 균일하고 의미상 동일하도록 설계되었다. 이는 레플리카된 상태 스테이트리스 서버에 적합하지만 레플리케이션 컨트롤러를 사용하여 마스터 선출, 샤드 및 워크-풀 애플리케이션의 가용성을 유지할 수도 있다. RabbitMQ work queues와 같은 애플리케이션은 안티패턴으로 간주되는 각 파드의 구성에 대한 정적/일회성 사용자 정의와 반대로 동적 작업 할당 메커니즘을 사용해야 한다. 리소스의 수직 자동 크기 조정 (예 : CPU 또는 메모리)과 같은 수행된 모든 파드 사용자 정의는 레플리케이션 컨트롤러 자체와 달리 다른 온라인 컨트롤러 프로세스에 의해 수행되어야 한다.
레플리케이션 컨트롤러의 책임
레플리케이션 컨트롤러는 의도한 수의 파드가 해당 레이블 셀렉터와 일치하고 동작하는지를 확인한다. 현재, 종료된 파드만 해당 파드의 수에서 제외된다. 향후 시스템에서 사용할 수 있는 readiness 및 기타 정보가 고려될 수 있으며 교체 정책에 대한 통제를 더 추가 할 수 있고 외부 클라이언트가 임의로 정교한 교체 또는 스케일 다운 정책을 구현하기 위해 사용할 수 있는 이벤트를 내보낼 계획이다.
레플리케이션 컨트롤러는 이 좁은 책임에 영원히 제약을 받는다. 그 자체로는 준비성 또는 활성 프로브를 실행하지 않을 것이다. 오토 스케일링을 수행하는 대신, 외부 오토 스케일러 (#492에서 논의된)가 레플리케이션 컨트롤러의 replicas 필드를 변경함으로써 제어되도록 의도되었다. 레플리케이션 컨트롤러에 스케줄링 정책 (예를 들어 spreading)을 추가하지 않을 것이다. 오토사이징 및 기타 자동화 된 프로세스를 방해할 수 있으므로 제어된 파드가 현재 지정된 템플릿과 일치하는지 확인해야 한다. 마찬가지로 기한 완료, 순서 종속성, 구성 확장 및 기타 기능은 다른 곳에 속한다. 대량의 파드 생성 메커니즘 (#170)까지도 고려해야 한다.
레플리케이션 컨트롤러는 조합 가능한 빌딩-블록 프리미티브가 되도록 고안되었다. 향후 사용자의 편의를 위해 더 상위 수준의 API 및/또는 도구와 그리고 다른 보완적인 기본 요소가 그 위에 구축 될 것으로 기대한다. 현재 kubectl이 지원하는 "매크로" 작업 (실행, 스케일)은 개념 증명의 예시이다. 예를 들어 Asgard와 같이 레플리케이션 컨트롤러, 오토 스케일러, 서비스, 정책 스케줄링, 카나리 등을 관리할 수 있다.
ReplicaSet은 새로운 집합성 기준 레이블 셀렉터이다.
이것은 주로 디플로이먼트에 의해 파드의 생성, 삭제 및 업데이트를 오케스트레이션 하는 메커니즘으로 사용된다.
사용자 지정 업데이트 조정이 필요하거나 업데이트가 필요하지 않은 경우가 아니면 레플리카셋을 직접 사용하는 대신 디플로이먼트를 사용하는 것이 좋다.
디플로이먼트 (권장됨)
Deployment는 기본 레플리카셋과 그 파드를 업데이트하는 상위 수준의 API 오브젝트이다. 선언적이며, 서버 사이드이고, 추가 기능이 있기 때문에 롤링 업데이트 기능을 원한다면 디플로이먼트를 권장한다.
베어 파드
사용자가 직접 파드를 만든 경우와 달리 레플리케이션 컨트롤러는 노드 오류 또는 커널 업그레이드와 같은 장애가 발생하는 노드 유지 관리의 경우와 같이 어떤 이유로든 삭제되거나 종료된 파드를 대체한다. 따라서 애플리케이션에 하나의 파드만 필요한 경우에도 레플리케이션 컨트롤러를 사용하는 것이 좋다. 프로세스 관리자와 비슷하게 생각하면, 단지 단일 노드의 개별 프로세스가 아닌 여러 노드에서 여러 파드를 감독하는 것이다. 레플리케이션 컨트롤러는 로컬 컨테이너가 노드의 에이전트로 (예를 들어 Kubelet 또는 도커 ) 재시작하도록 위임한다.
잡
자체적으로 제거될 것으로 예상되는 파드 (즉, 배치 잡)의 경우
레플리케이션 컨트롤러 대신 Job을 사용하라.
데몬셋
머신 모니터링이나 머신 로깅과 같은 머신 레벨 기능을 제공하는 파드에는 레플리케이션 컨트롤러 대신
DaemonSet을 사용하라. 이런 파드들의 수명은 머신의 수명에 달려 있다.
다른 파드가 시작되기 전에 파드가 머신에서 실행되어야 하며,
머신이 재부팅/종료 준비가 되어 있을 때 안전하게 종료된다.