쿠버네티스는 크게 오브젝트와 오브젝트를 관리하는 컨트롤러로 나눕니다. 사용자는 템플릿 등으로 쿠버네티스에 자원의 '바라는 상태(desired state)'를 정의하고 컨트롤러는 바라는 상태와 현재 상태가 일치하도록 오브젝트들을 생성/삭제합니다. 오브젝트에는 Pod, Service, Volume, Namespace 등이 있습니다. 컨트롤러에는 ReplicaSet, Deployment, StatefulSet, DaemonSet, Job 등이 있습니다.
네임스페이스
네임스페이스는 쿠버네티스 클러스터 하나를 여러 개 논리적인 단위로 나눠서 사용하는 것입니다.
기본 네임스페이스는 다음과 같습니다.
- default : 기본 네임스페이스입니다. 쿠버네티스에서 명령을 실행할 때 별도의 네임스페이스를 지정하지 않는다면 항상 default 네임스페이스에 명령을 적용합니다.
- kube-system : 쿠버네티스 시스템에서 관리하는 네임스페이스입니다. 관리용 파드나 설정이 있습니다.
- kube-public : 클러스터 안 모든 사용자가 읽을 수 있는 네임스페이스입니다. 보통 클러스터 사용량 같은 정보를 이 네임스페이스에서 관리합니다. 클러스터를 사용하는 모두가 볼 수 있기 때문입니다.
- kube-node-lease : 각 노드의 임대 오브젝트들을 관리하는 네임스페이스입니다.
템플릿
쿠버네티스 클러스터의 오브젝트나 컨트롤러가 어떤 상태여야 하는지를 적용할 때는 YAML 형식의 템플릿을 사용합니다.
템플릿의 내용을 표현하는 YAML은 JSON과 비교했을 때 간결하며, 주석도 지원하므로 가독성이 좋습니다. 단, 들여쓰기에 따라 구조가 바뀌므로 작성할 때 올바르게 들여쓰기했는지 반드시 확인이 필요합니다.
YAML은 3가지 기초 요소로 표현합니다.
템플릿의 기본 형식입니다.
---
apiVersion: v1
kind: Pod
metadata:
spec:
각 항목은 필드라고 합니다.
- apiVersion : 사용하려는 쿠버네티스 API 버전을 명시합니다.
- kind : 어떤 종류의 오브젝트 혹은 컨트롤러에 작업인지 명시합니다. Pod라고 설정하면 파드에 관한 템플릿입니다. 여기에는 Deployment, Ingress 등 다양한 오브젝트나 컨트롤러를 설정할 수 있습니다.
- metadata : 메타데이터를 설정합니다. 해당 오브젝트의 이름이나 레이블 등을 설정합니다.
- spec : 파드가 어떤 컨테이너를 갖고 실행하며, 실행할 때 어떻게 동작해야 할지 명시합니다.
파드
파드는 보통 컨테이너 하나가 아닌 여러 개 컨테이너로 구성됩니다. 컨테이너 여러 개를 한꺼번에 관리할 때는 컨테이너마다 역할을 부여할 수 있습니다. 파드 하나에 속한 컨테이너들은 모두 노드 안에서 실행됩니다. 파드의 역할 중 하나가 컨테이너들이 같은 목적으로 자원을 공유하는 것이므로 가능한 일입니다.
파드의 기본적인 템플릿 입니다.
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-simple-pod
labels:
app: kubernetes-simple-pod
spec:
containers:
- name: kubernetes-simple-pod
image: arisu1000/simple-container-app:latest
ports:
- containerPort: 8080
- .metadata.name 필드는 파드 이름을 설정합니다. 여기에서는 kubernetes-simple-pod라고 설정했습니다.
- .metadata.labels.app 필드는 오브젝트를 식별하는 레이블을 설정합니다. 여기에서는 해당 파드가 앱 컨테이너고 kubernetes-simple-pod라고 식별한다고 설정했습니다.
- .spec.containers[].name 필드는 컨테이너의 이름을 설정합니다. 여기에서는 kubernetes-simple-pod라고 설정했습니다. 참고로 name 앞에 설정한 '-' 은 .spec.containers 의 하위 필드를 배열 형태로 묶겠다는 뜻 입니다. 그래서 .spec.containers[] 라고 표기합니다.
- .spec.containers[].image 필드는 컨테이너에서 사용할 이미지를 정합니다.
- .spec.containers[].ports[].containerPort 필드는 해당 컨테이너에 접속할 포트 번호를 설정합니다.
파드 생명 주기
파드는 생성부터 삭제까지 생명주기가 있습니다. 다음과 같습니다.
- Pending : 시스템에서 파드를 생성하는 중임을 뜻합니다. 컨테이너 이미지를 다운로드한 후 전체 컨테이너를 실행하는 도중이므로 파드 안의 전체 컨테이너가 실행될 때까지 시간이 걸립니다.
- Running : 파드 안의 모든 컨테이너가 실행 중인 상태입니다. 1개 이상의 컨테이너가 실행 중이거나 시작 또는 재시작 상태일 수 있습니다.
- Succeeded : 파드 안의 모든 컨테이너가 정상 실행 종료된 상태로 재시작되지 않습니다.
- Failed : 파드 안 모든 컨테이너 중 정상적으로 실행 종료되지 않은 컨테이너가 있는 상태입니다. 컨테이너 종료 코드가 0이 아니면 비정상 종료(예 : Out of memory는 종료 코드 137)이거나 시스템이 직접 컨테이너를 종료한 것입니다.
- Unknown : 파드의 상태를 확인할 수 없는 상태입니다. 보통 파드가 있는 노드와 통신할 수 없을 때입니다.
파드 생명 주기는 kubectl describe pods {파드 이름} 명령을 실행한 후 Status 항목을 살펴보면 확인할 수 있습니다.
Conditions 항목은 파드의 현재 상태 정보를 나타내며 Type과 Status로 구분되어 있습니다.
Type에는 다음 같은 정보를 나타냅니다.
- Initialized : 모든 초기화 컨테이너가 성공적으로 시작 완료되었다는 뜻입니다.
- Ready : 파드는 요청들을 실행할 수 있고 연결된 모든 서비스의 로드밸런싱 풀에 추가되어야 한다는 뜻입니다.
- ContainersReady : 파드 안 모든 컨테이너가 준비 상태라는 뜻입니다.
- PodScheduled : 파드가 하나의 노드로 스케줄을 완료했다는 뜻입니다.
- Unschedulable : 스케줄러가 자원의 부족이나 다른 제약 등으로 지금 당장 파드를 스케줄 할 수 없다는 뜻입니다.
kubelet으로 컨테이너 진단하기
컨테이너가 실행된 후에는 kubelet이 컨테이너를 주기적으로 진단합니다.
이때 필요한 프로브(Probe)에는 다음 세 가지가 있습니다.
- livenessProbe : 컨테이너가 실행됐는지 확인합니다. 실패하면 kubelet은 컨테이너를 종료하고, 재시작 정책에 따라서 컨테이너를 재시작합니다. 컨테이너에 livenessProbe를 어떻게 할지 명시되지 않았다면 기본 상태 값은 Success입니다.
- readinessProbe : 컨테이너가 실행된 후 실제로 서비스 요청에 응답할 수 있는지 진단합니다. 이 진단이 실패하면 엔드포인트 컨트롤러는 해당 파드에 연결된 모든 서비스를 대상으로 엔드포인트 정보를 제거합니다. 첫 번째 readinessProbe를 하기 전까지의 기본 상태 값은 Failure입니다. readinessProbe를 지원하지 않는 컨테이너라면 기본 상태 값은 Success입니다.
- startupProbe : 컨테이너 안 애플리케이션이 시작되었는지 나타냅니다. 스타트업 프로브는 진단이 성공할 때까지 다른 나머지 프로브는 활성화되지 않으며, 진단이 실패하면 kubelet이 컨테이너를 종료시키고, 컨테이너를 재시작 정책에 따라 처리합니다. 컨테이너에 스타트업 프로브가 없으면 기본 상태 값은 Success입니다.
초기화 컨테이너
초기화 컨테이너는 앱 컨테이너가 실행되기 전 파드를 초기화합니다. 보안상 이유로 앱 컨테이너 이미지와 같이 두면 안 되는 앱의 소스코드로 별도로 관리할 때 유용합니다.
- 초기화 컨테이너는 여러 개를 구성할 수 있습니다. 초기화 컨테이너가 여러 개 있다면 파드 템플릿에 명시한 순서대로 초기화 컨테이너가 실행됩니다.
- 초기화 컨테이너 실행이 실패하면 성공할 때까지 재시작합니다. 이런 초기화 컨테이너의 특성을 이용하면 쿠버네티스의 '선언적'이라는 특징에서 벗어날 수 있습니다. 즉, 필요한 명령들을 순서대로 실행하는 데 사용되는 것입니다.
- 초기화 컨테이너가 모두 실행된 후 앱 컨테이너 실행이 시작됩니다.
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-simple-pod
labels:
app: kubernetes-simple-pod
spec:
initContainers:
- name: init-myservice
image: arisu1000/simple-container-app:latest
command: ['sh', '-c', 'sleep 2; echo helloworld01;']
- name: init-mydb
image: arisu1000/simple-container-app:latest
command: ['sh', '-c', 'sleep 2; echo helloworld02;']
containers:
- name: kubernetes-simple-pod
image: arisu1000/simple-container-app:latest
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
초기화 컨테이너를 설정하는 부분은 .spec.initContainers[]의 하위 필드 입니다.
- 첫 번째 초기화 컨테이너의 .spec.initContainers[].name필드 값으로는 init-myservice라는 이름을 설정했고 .image 필드 값으로는 arisu1000/simple-container-app:latest를 설정했습니다. .command 필드 값으로는 해당 컨테이너를 실행할 때 2초 대기한 후 helloworld01라는 메세지를 출력하라고 설정했습니다.
- 두 번째 초기화 컨테이너의 .spec.initContainers[].name필드 값으로는 init-mydb라는 이름을 사용하였고, .image 필드 값으로는 arisu1000/simple-container-app:latest를 설정했습니다. .command 필드 값으로는 해당 컨테이너를 실행할 때 2초 대기 후 helloworld02라는 메세지를 출력하도록 설정했습니다.
- .spec.containers[]의 하위 필드는 실제로 생성할 파드를 설정했습니다. .spec.containers[].name 필드 값으로는 kubernetes-simple-pod라는 이름을 설정했고, .image 필드 값으로는 역시 arisu1000/simple-container-app:latest를 설정했습니다. .command 필드 값으로는 해당 컨테이너를 실행할 때 The app is running! 이라는 메세지를 출력하고 3600초 대기하라고 설정했습니다.
즉, kubernetes-simple-pod 파드를 실행하기 전에 초기화 컨테이너로 init-myservice, init-mydb를 실행합니다.
파드 인프라 컨테이너
모든 파드에서 항상 실행되는 pause라는 컨테이너가 있습니다. 이 pause를 '파드 인프라 컨테이너'라고 합니다.
pause는 파드 안 기본 네트워크로 실행되며, 프로세스 식별자가 1(PID 1)로 설정되므로 다른 컨테이너 부모 컨테이너 역할을 합니다. 파드 안 다른 컨테이너는 pause 컨테이너가 제공하는 네트워크를 공유해서 사용합니다. 그래서 파드 안 다른 컨테이너가 재시작됐을 때는 파드의 IP를 유지하지만, pause 컨테이너가 재시작되면 파드 안 모든 컨테이너도 재시작합니다.
스태틱 파드
kube-apiserver를 통하지 않고 kubelet이 직접 실행하는 파드들이 있습니다. 이를 스태틱 파드라고 합니다. kubelet 설정의 --pod-manifest-path라는 옵션에 지정한 디렉터리에 스태틱 파드로 실행하려는 파드들을 넣어두면 kubelet이 그걸 감지해서 파드로 실행합니다.
스태틱 파드는 kubelet이 직접 관리하면서 이상이 생기면 재시작합니다. 또한 kubelet이 실행 중인 노드에서만 실행되고 다른 노드에서는 실행되지 않습니다. kube-apiserver로 조회를 할 수 있지만 스태틱 파드에 어떤 명령을 실행할 수는 없습니다. 보통 스태틱 파드는 kube-apiserver 라던지 etcd 같은 시스템 파드를 실행하는 용도로 많이 사용합니다.
쿠버네티스에서 파드를 실행하려면 kube-apiserver가 필요한ㄴ데 kube-apiserver 자체를 처음 실행하는 별도의 수단으로 스태틱 파드를 이용하는 것입니다.
파드에 CPU, MEM 자원 할당
MSA기반으로 여러 개 작은 프로세스를 실행하면 노드 하나에 여러 개 파드를 실행하는 일이 자주 있습니다. 이때 자원 사용량이 많은 파드가 노드 하나에 모여 있다면 파드들의 성능이 나빠집니다. 또한 전체 클러스터의 자원 사용 효율도 낮습니다. 예를 들어 어떤 노드에는 파드가 없어서 CPU나 메모리가 같은 자원이 남고, 어떤 노드에는 파드들이 많아서 파드에서 사용해야하는 CPU, 메모리가 부족한 현상이 발생할 수도 있습니다.
이런 상황을 막는 여러 가지 방법이 있습니다. 가장 기본적인 방법으로는 파드를 설정할 때 파드 안 각 컨테이너가 CPU, 메모리를 얼마나 사용할 수 있는지 조건을 지정하는 것입니다.
- .spec.containers[].resources.limits.cpu
- .spec.containers[].resources.limits.memory
- .spec.containers[].resources.requests.cpu
- .spec.containers[].resources.requests.memory
apiVersion: v1
...
containers:
- name: kubernetes-simple-pod
...
resources:
requests:
cpu: 0.1
memory: 200M
limits:
cpu: 0.5
memory: 1G
...
.spec.containers[].resources.requests 필드는 최소 자원 요구량을 나타냅니다. 파드가 실행될 때 최소한 설정된 만큼의 자원 여유가 있는 노드가 있어야 파드를 그곳에 스케줄링해 실행합니다. 만약 요구하는 만큼의 여유 자원이 있는 노드가 없다면 Pending 상태로 실행되지 않을것입니다. 여유가 생길 때까지 대기합니다.
.spec.containers[].resources.limits 필드는 자원을 최대로 얼마까지 사용할 수 있는지 제한하는 설정입니다. 웹 서비스를 제공하는 컨테이너가 있다고 가정했을 때, 평소에는 사용량이 적더라도 어떤 이벤트 때문에 갑자기 사용량이 늘어날 수도 있습니다. 이 때 설정이 따로 없다면 해당 컨테이너가 노드의 모든 자원을 점유하여 사용할 수 있습니다. 그러면 같은 노드에 실행된 다른 컨테이너가 모두 영향을 받아 최악의 상황에는 클러스터 안 모든 서비스에 영향을 끼칠 수 있습니다.
자원 사용량을 할당할 때의 단위는 어떨까요?
메모리는 바이트 단위로 측정됩니다. 별도로 용량을 나타내는 기호를 사용하지 않는다면 기본 단위는 바이트입니다. 하지만 대용량을 바이트로 표기하기에는 불편하므로 보통 바이트 단위 용량을 나타내는 기호인 십진법 접두어 E, P, T, G, M, K를 사용합니다. 이진법 접두어인 Ei, Pi, Ti, Gi, Mi, Ki 같은 기호도 그대로 사용할 수 있습니다.
CPU는 메모리와 좀 다릅니다. 보통 본인이 사용할 수 있는 자원의 코어 개수로 표시합니다. 그래서 할당할 수 있는 CPU 자원을 1, 2, 3, 4처럼 정수로만 설정할 수 있다고 생각하는 분이 있을것입니다.
예를 들어, 0.1로 설정했을 때는 CPU 코어 하나의 연산량을 100이라고 했을 때 10%만큼 연산능력을 할당한다는 뜻입니다. 0.5면 코어 하나의 50% 연산 능력까지만 활용할 수 있도록 제한한다는 뜻입니다.
파드에 환경 변수 설정하기
apiVersion: v1
kind: Pod
...
spec:
containers:
- name: kubernetes-simple-pod
image: arisu1000/simple-container-app:latest
...
env:
- name: TESTENV01
value: "testvalue01"
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: kubernetes-simple-pod
resource: requests.cpu
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: kubernetes-simple-pod
resource: limits.cpu
.spec.containers[]의 하위 필드를 살펴보면 .spec.containers[].env[]라는 하위 필드를 확인할 수 있습니다.
- name : 사용할 환경 변수의 이름을 설정합니다.
- value : 문자열이나 숫자 형식의 값을 설정합니다.
- valueFrom : 값을 직접 할당하는 것이 아니라 어딘가 다른 곳에서 참조하는 값을 설정합니다.
- fieldRef : 파드의 현재 설정 내용을 값으로 설정한다는 선언입니다.
- fieldPath : .fieldRef에서 어디서 값을 가져올 것인지를 지정합니다. 즉, 값을 참조하려는 항목의 위치를 지정합니다.
- resourceFieldRef : 컨테이너에 CPU, Mem 사용량을 얼마나 할당했는지에 관한 정보를 가져옵니다.
- containerName : 환경 변수 설정을 가져올 컨테이너 이름을 설정합니다.
- resource : 어떤 자원의 정보를 가져올지 설정합니다.
이렇게 파드와 관련 있는 여러 가지 정보를 환경 변수로 설정해서 파드에 적용할 수 있습니다.
파드 구성 패턴
구성 패턴에는 사이드카 패턴, 앰배서더 패턴, 어댑터 패턴으로 총 3가지의 패턴이 있습니다.
사이드카 패턴은 원래 사용하려던 기본 컨테이너의 기능을 확장하거나 강화하는 용도의 컨테이너를 추가하는 것입니다. 기본 컨테이너는 원래 목적의 기능에만 충실하도록 구성하고, 나머지 공통 부가 기능들은 사이드카 컨테이너를 추가해서 사용합니다.
이렇게 구성 시 웹 서버 컨테이너를 다른 역할을 하는 컨테이너로 변경했을 때도 로그 수집 컨테이너는 그대로 사용할 수 있습니다.
앰배서더 패턴은 파드 안에서 프록시 역할을 하는 컨테이너를 추가하는 패턴입니다. 파드 안에서 외부 서버에 접근할 때 내부 프록시에 접근하도록 설정하고 실제 외부와의 연결은 프록시에서 알아서 처리합니다.
웹 서버 컨테이너는 캐시에 localhost로만 접근하고 실제 외부 캐시 중 어디로 접근할지는 프록시 컨테이너가 처리합니다.
이런 방식으로 파드의 트래픽을 더 세밀하게 제어할 수 있습니다.
파드마다 프록시를 추가해서 트래픽을 처리하도록 구성하는 것임을 알 수 있습니다.
어댑터 패턴은 파드 외부로 노출되는 정보를 표준화하는 어댑터 컨테이너를 사용한다는 뜻입니다. 컨테이너로 파드의 모니터링 지표를 표준화한 형식으로 노출시키고, 외부의 모니터링 시스템에서 해당 데이터를 주기적으로 가져가서 모니터링하는데 이용합니다.
어댑터 패턴은 오픈 소스 모니터링 시스템인 프로메테우스에서도 사용합니다.
컨트롤러
컨트롤러는 파드들을 관리하는 역할을 합니다. 예를 들어 웹 서비스처럼 오랜 시간동안 계속 실행되어야 하는 파드들을 관리할 때는 레플리케이션 컨트롤러, 레플리카세트, 디플로이먼트 등을 사용합니다. 클러스터의 전체 노드에 같은 파드를 실행할 때는 데몬세트를 사용합니다. 보통 상태가 없는(stateless) 앱을 실행하는데 사용하는 컨테이너를 상태가 있는(statefull) 앱을 실행할 때 사용하도록 만드는 스테이트 풀세트도 있습니다.
레플리케이션 컨트롤러
레플리케이션 컨트롤러는 쿠버네티스 프로젝트의 초기부터 있었던 가장 기본적인 컨트롤러입니다. 지정한 숫자만큼의 파드가 항상 클러스터 안에서 실행되도록 관리합니다. 예를 들어 파드 2개를 명시해둔 레플리케이션 컨트롤러가 있다면 장애나 다른 이유로 파드 개수가 2개보다 적을 때 다시 새로운 파드를 실행해서 파드 개수를 2개로 맞춥니다. 또한 파드가 2개보다 많아졌을 때도 파드를 줄여서 2개만큼만 실행되도록 조정합니다.
컨트롤러를 사용하지 않고 파드를 직접 실행해서 파드에 이상이 생겨서 종료되거나 삭제됐을 때 재시작하기 어렵습니다. 파드를 실행하던 노드에 장애가 발생해서 파드가 종료되었을 때, 레플리케이션 컨트롤러를 이용해서 실행한 파드라면 클러스터 안 다른 노드에 다시 파드를 실행시킵니다.
레플리케이션 컨트롤러는 쿠버네티스에 처음부터 있었지만 요즘은 비슷한 역할을 하는 레플리카세트를 사용하는 추세입니다. 앱의 배포에는 디플로이먼트를 주로 사용합니다.
레플리카세트
레플리카세트는 레플리케이션 컨트롤러의 발전형입니다. 레플리케이션 컨트롤러와 같은 동작을 하지만 집합 기반의 셀럭터를 지원하는 차이점이 있습니다. 예를 들어 레플리케이션 컨트롤러는 셀렉터가 등호 기반이므로 레이블을 선택할 때 같은지 다른지만 확인합니다. 하지만 집합 기반의 셀렉터는 in, notin, exists 같은 연산자를 지원합니다.
또한 레플리케이션 컨트롤러는 kubectl에서 rolling-update 옵션을 사용할 수 있지만 레플리카세트는 사용할 수 없다는 차이도 있습니다. rolling-update 옵션이 필요할 때는 디플로이먼트를 사용해야 합니다.
레플리카세트 사용하기
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
spec:
template:
metadata:
name: nginx-replicaset
labels:
app: nginx-replicaset
spec:
containers:
- name: nginx-replicaset
image: nginx
ports:
- containerPort: 80
replicas: 3
selector:
matchLabels:
app: nginx-replicaset
레플리카세트 템플릿의 주요 설정은 다음과 같습니다.
- .spec.template.spec.containers[] 필드는 하위에 .name, .image, .ports[], .containerPort 필드를 이용해 컨테이너의 구체적인 명세를 설정합니다. 여기에서는 컨테이너의 이름을 nginx-replicaset, 사용할 컨테이너 이미지는 nginx, 해당 컨테이너에 접속할 포트 번호는 80으로 설정했습니다.
- .spec.replicas는 파드를 몇 개 유지할지 개수를 설정하는 필드입니다. 따로 설정하지않으면 기본값은 1입니다.
- .spec.selector는 어떤 레이블의 파드를 선택해서 관리할지를 설정합니다. 레이블을 기준으로 파드를 관리하므로 실행 중인 파드를 중단하거나 재시작하지 않고 레플리케이션 컨트롤러가 관리하는 파드를 변경할 수 있습니다. 그래서 처음 레플리케이션 컨트롤러를 생성할 때 .spec.template.metadata.labels의 하위 필드 설정과 .spec.selector.matchLabels의 하위 필드 설정이 같아야 합니다. 다르면 kube-apiserver에서 유효하지 않은 요청이라고 판단해서 파드 변경을 거부합니다. 템플릿에 별도의 .spec.selector 설정이 없으면 .spec.template.metadata.labels.app에 있는 내용을 기본값으로 설정합니다.
레플리카세트와 파드의 연관 관계
파드는 레이블 기준으로 관리하므로 레플리카세트와 파드는 느슨하게 결합되어 있습니다.
즉, 레플리카세트와 파드를 한꺼번에 삭제할 때는 kubectl delete replicaset {컨테이너이름} 명령을 실행하지만, "--cascade=orphan" 옵션을 사용하면 레플리카세트가 관리하는 파드에 영향을 끼치지 않고 레플리카세트만 삭제할 수 있습니다.
레플리카세트 상태 항목 중 DESIRED 와 CURRENT는 다음 뜻이 있습니다.
- DESIRED : 레플리카세트 설정에 지정한 파드 개수입니다.
- CURRENT : 레플리카세트를 이용해 현재 클러스터에서 동작하는 실제 파드 개수 입니다.
디플로이먼트
디플로이먼트는 쿠버네티스에서 상태가 없는 앱을 배포할 때 사용하는 가장 기본적인 컨트롤러입니다. 쿠버네티스가 처음 등장했을 때는 레플리케이션 컨트롤러에서 앱을 배포했는데 최근에는 디플로이먼트를 기본적인 앱 배포에 사용합니다.
디플로이먼트는 레플리카세트를 관리하면서 앱 배포를 더 세밀하게 관리합니다. 디플로이먼트라는 이름처럼 배포 기능을 세분화한 것입니다. 단순히 실행시켜야 할 파드 개수를 유지하는 것 뿐만 아니라 앱을 배포할 때 롤링 업데이트를 하거나, 앱 배포 도중 잠시 멈췄다가 다시 배포할 수 있습니다.
디플로이먼트 사용하기
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx-deployment
# annotations:
# kubernetes.io/change-cause: version 1.10.1
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- name: nginx-deployment
image: nginx
ports:
- containerPort: 80
- 파드를 몇개 실행할 것인지는 레플리케이션 컨트롤러나 레플리카세트와 마찬가지로 .spec.replicas 필드에 지정합니다.
- .spec.selector.matchLabels의 하위 필드는 .metadata.labels의 하위 필드와 같은 설정을 해야합니다. 여기에서는 .spec.selector.matchLabels.app 필드를 .metadata.labels.app 필드와 같은 nginx-deployment로 설정했습니다.
- 파드의 설정 정보가 있는 .spec.template.spec.containers[] 필드를 살펴보면 실제 사용하려는 컨테이너 이름과 이미지 정보가 있습니다.
컨테이너 이미지 설정 정보를 업데이트 하는 방법은 크게 3가지가 있습니다.
- kubectl set 명령으로 직접 컨테이너 이미지를 지정
- kubectl edit 명령으로 현재 파드의 설정 정보를 연 다음 컨테이너 이미지 정보를 수정
- 적용했던 템플릿의 컨테이너 이미지 정보를 수정한 다음 kubectl apply 명령을 실행해서 변경
디플로이먼트 롤백하기
컨테이너 이미지를 변경한 내역은 kubectl rollout history deploy {디플로이먼트이름} 명령으로 확인할 수 있습니다.
명령 실행 시 리비전(REVISION)이 여러개 보이게 됩니다. 특정 리비전의 상세 내용을 확인하려면 "--revision={리비전숫자}" 옵션을 사용합니다. 특정 리비전으로 실행 중인 파드를 되돌리려면 "--to-revision={리비전숫자}" 옵션을 사용합니다.
파드 개수 조정하기
실행 중인 디플로이먼트의 파드 개수를 조정하려면 kubectl scale 명령을 사용합니다. --replicas 옵션에 파드 개수를 입력해서 조정합니다. "kubectl scale deploy nginx-deployment --replicas=5"
디플로이먼트 배포 정지, 배포 재개, 재시작하기
rollout 명령을 이용해서 진행 중인 배포를 잠시 멈췄다가 다시 시작할 수 있습니다. 우선 "kubectl rollout pause deployment/디플로이먼트이름" 명령으로 업데이트를 멈춥니다. 그리고 kubectl set image deploy/디플로이먼트이름 컨테이너이름=nginx:버전숫자 명령과 kubectl patch deployment/nginx-deployment -p "{"metadata":{"annotations":{"kubernetes.io/change-cause":"version 1.11"}}}" 명령으로 컨테이너 이미지와 CHANGE-CAUSE 항목에 메시지를 변경해보겠습니다.
컨테이너 이미지와 CHANGE-CAUSE 항목에 나타나는 메시지를 실제로 업데이트했다는 메시지가 등장합니다. 그런데 kubectl rollout history deploy/디플로이먼트이름 명령으로 확인해 보면 배포가 진행되지 않았다는 것을 알 수 있습니다. 업데이트를 정지시켰으므로 디플로이먼트 작업이 진행되지 않는 것입니다.
여기에서는 배포를 정지해둔 후 다시 배포했지만 배포를 중간에도 pause를 이용해서 배포를 멈출 수 있습니다. 멈췄던 배포는 resume을 이용해서 다시 진행할 수 있습니다.
운영하다보면 파드 수정없이 단순 재시작을 해야할 때는 kubectl rollout restart deployment/디플로이먼트이름 명령어를 사용하면 됩니다.
디플로이먼트 상태
배포 중에는 디플로이먼트 상태가 변합니다. 우선 진행(Progressing)이었다가 성공이면 완료(complete), 실패면 실패(Failed)상태로 바뀝니다. kubectl rollout status 명령으로 배포 진행 상태를 확인할 수 있습니다.
다음 작업을 하는 동안에는 상태가 진행(Progressing)으로 표시됩니다.
- 디플로이먼트가 새로운 레플리카세트를 만들 때
- 디플로이먼트가 새로운 레플리카세트의 파드 개수를 늘릴 때
- 디플로이먼트가 예전 레플리카세트의 파드 개수를 줄일 때
- 새로운 파드가 준비 상태가 되거나 이용 가능한 상태가 되었을 때
배포가 이상없이 끝나면 상태는 완료(complete)가 됩니다. 다음 조건을 확인해서 완료로 표시합니다. 종료 코드가 0으로 표시됩니다.
- 디플로이먼트가 관리하는 모든 레플리카세트가 업데이트 완료되었을 때
- 모든 레플리카세트가 사용 가능해졌을 때
- 예전 레플리카세트가 모두 종료되었을 때
배포 중 이상이 있으면 실패(Failed)가 됩니다. 보통 다음과 같은 이유로 실패합니다.
- 쿼터 부족
- readinessProbe 진단 실패
- 컨테이너 이미지 가져오기 에러
- 권한 부족
- 제한 범위 초과
- 앱 실행 조건을 잘못 지정
데몬 세트
데몬세트는 클러스터 전체 노드에 특정 파드를 실행할 때 사용하는 컨트롤러입니다. 클러스터 안에 새롭게 노드가 추가되었을 때 데몬세트가 자동으로 해당 노드에 파드를 실행시킵니다. 반대로 노드가 클러스터에서 빠졌을 때는 해당 노드에 있던 파드는 그대로 사라진 뿐 다른 곳으로 옮겨가서 실행되거나 하지 않습니다.
그러므로 데몬세트는 보통 로그 수집기를 실행하거나 노드를 모니터링하는 모니터링용 데몬 등 클러스터 전체에 항상 실행시켜두어야하는 파드에 사용합니다.
데몬세트 사용하기
로그 수집기를 실행하는 데몬세트 설정의 예는 다음과 같습니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
containers:
- name: fluentd-elasticsearch
image: fluent/fluentd-kubernetes-daemonset:elasticsearch
env:
- name: testenv
value: value
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
주요 설정은 다음과 같습니다.
- 데몬세트의 파드를 업데이트하는 방법은 .spec.updateStrategy.type 필드 값을 설정하는 것입니다. OnDelete와 RollingUpdate 두 가지 값 중 하나를 선택할 수 있습니다. 기본 값은 RollingUpdate 입니다. k8s 1.15 version 이하는 기본값이 OnDelete 입니다.
RollingUpdate를 설정하면 템플릿을 변경했을 때 바로 변경 사항을 반영합니다. 이 때 모든 파드가 한꺼번에 변경되는 것이 아니라 지정한 개수만큼 이전 파드를 삭제하고 새 파드를 실행합니다. .spec.updateStrategy.rollingUpdate.maxUnavailable(기본값 1)필드와 .spec.minReadySeconds 필드를 추가로 설정해 한번에 교체하는 파드 개수를 조정합니다. .spec.updateStrategy.rollingUpdate.maxUnavailable 필드는 한번에 삭제 하는 파드 개수고 .spec.minReadySeconds 필드는 새로 실행하는 파드가 준비 상태가 되는 최소 시간입니다. 기본값은 0입니다.
'Books' 카테고리의 다른 글
2. 쿠버네티스 아케틱처 (0) | 2023.03.28 |
---|---|
1. 쿠버네티스 소개 (0) | 2023.01.29 |