라이브니스 프로브
컨테이너가 더 이상 정상적이지 않으면 쿠버네티스가 컨테이너를 다시 시작하도록 감시하는 기능을 라이브니스 프로브라고 한다.
라이브니스 프로브를 활용하면 컨테이너가 살아 있는지 주기적으로 확인할 수 있다.
쉽게 말해 헬스체크를 하고, 실패하면 컨테이너를 죽이고 다시 띄우는 것.
- HTTP GET 프로브: 지정한 IP, 포트, 경로에 GET 요청을 하고 2xx, 3xx 응답일 경우 컨테이너 연결이 성공한 것으로 간주한다.
- TCP 소켓 프로브: 컨테이너의 지정된 포트에 TCP 연결을 시도하고 연결이 성공하면 컨테이너가 살아 있는 것으로 간주한다.
- Exec 프로브: 컨테이너 내 임의의 명령을 실행하고 종료 상태 코드가 0이면 성공으로 간주한다.
라이브니스 프로브가 컨테이너 종료를 감지하면 종료 코드를 부여하는데, 128+x 형식이다.
x는 프로세스에 전송된 시그널 번호인데 9는 SIGKILL
을, 15는 SIGTERM
을 의미한다.
운영환경에서 실행 중인 파드는 반드시 라이브니스 프로브를 정의해 쿠버네티스가 애플리케이션이 살아있는지 모니터링하게 해야 한다.
운영 상의 주의점
- 라이브니스 프로브는 어디까지나 모니터링 툴이므로 너무 많은 연산 리소스를 사용하거나 완료까지 너무 오래 걸리지 않아야 한다.
- 라이브니스프로브는 애플리케이션 외부 요인의 영향을 받지 않아야 한다. 예를들어 웹 애플리케이션 서버의 라이브니스 프로브는 데이터베이스 연결 실패시 실패를 반환해서는 안 된다.
레플리케이션 컨트롤러
레플리케이션 컨트롤러는 파드가 항상 실행되도록 보장한다. 클러스터에서 노드가 사라지거나 노드에서 파드가 제거된 경우 레플리케이션컨트롤러는 사라진 파드를 감지해 교체 파드를 생성해준다.
레플리케이션 컨트롤러는 실행 중인 파드 목록을 지속적으로 모니터링하며 특정한 유형의 파드의 숫자가 지정된 만큼인지 체크해 많으면 줄이고, 적으면 늘리는 작업을 자동으로 진행한다.
파드의 유형은 레이블 셀렉터를 통해 지정하며, 레플리케이션컨트롤러는 레이블 셀렉터를 이용해 정확한 숫자의 파드가 실행 중인지 체크한다.
레플리케이션 컨트롤러의 3가지 요소
- 레이블 셀렉터: 레플리케이션컨트롤러에서 관리하는 파드를 결정
- 레플리카 수(Replica count): 파드의 목표 개수 지정
- 파드 템플릿: 새로운 파드 레플리카 생성시 사용
위 3개 모두 수정 가능하며, 레플리카 수를 수정할 경우 운영 중인 기존 파드에 영향을 미치게 된다.
레플리케이션컨트롤러의 기능
- 기존 파드가 사라지면 새 파드를 시작해줘서 항상 파드가 실행되도록 한다.
- 클러스터 노드에 장애 발생시 해당 노드의 모든 파드에 대한 교체 복제본을 생성해준다.
- 쉽게 스케일아웃할 수 있다.
레플리케이션컨트롤러 생성하기
파드를 생성할 때 처럼 yaml이나 json으로 생성할 수 있다.
apiVersion: v1
kind: ReplicationController
metadata:
name: leonk
spec:
replicas: 3
selector:
app: leonk
template:
metadata:
labels:
app: leonk
spec:
containers:
- name: leonk
image: chaewonkong/simple:server
ports:
- containerPort: 8081
마찬가지로 아래 명령어로 생성할 수 있다.
$ kubectl create -f repl.yml
아래 명령어를 실행해보면 파드가 3개 정상적으로 실행 중인 것을 확인할 수 있다.
$ kubectl get po
파드를 하나 삭제한 후(kubectl delete po {POD_NAME}
) 다시 파드 개수를 확인해 보면 여전히 3개의 파드가 실행 중인 것을 확인할 수 있다.
레플리케이션컨트롤러가 지정된 숫자만큼 바로 재생성해 주기 때문이다.
$ kubectl get rc
NAME DESIRED CURRENT READY AGE
leonk 3 3 3 2m23s
레플리케이션콘트롤러는 파드의 '삭제'에 반응해 새 파드를 생성한 것은 아니고, 기존 파드가 삭제되어 desired 파드 수와 실제 파드 수가 달라졌기 때문에 새 파드를 생성한 것이다.
만약 레플리케이션컨트롤러의 레이블 셀렉터를 수정하면 모든 파드가 레플리케이션컨트롤러의 범위를 벗어나기 때문에 desired 개수 만큼의 새 파드가 생성되게 된다.
파드 템플릿 교체
파드의 템플릿을 교체하더라도 레플리케이션컨트롤러가 새 파드를 띄우지는 않는다. 기존 파드의 수가 desired수와 동일하다면, 기존 파드가 그대로 운용된다.
만약 새 파드 템플릿으로 파드를 생성하고 싶다면 기존 파드를 삭제함으로써 레플리케이션컨트롤러가 새 템플릿 기반으로 파드를 생성하도록 해야 한다.
수평적 확장
레플리케이션컨트롤러를 이용하면 스케일아웃이 매우 간단해진다. desired를 설정함으로써 특정한 숫자만큼의 파드가 항상 실행되도록 보장할 수 있다. 따라서 이 desired 숫자를 늘려주면 그 증가분 만큼 새 파드가 생성된다.
템플릿에서 replicas
필드 값이 바로 이 desired 수인데, 이 값을 수정하면 새 값 만큼 파드가 실행됨을 보장받을 수 있다.
$ kubectl edit rc leonk
위 명령어를 실행하면 editor에서 yaml 파일을 수정할 수 있다. replicas
의 값을 수정하면 파드를 원하는 만큼 늘리거나 줄일 수 있다.
이처럼 쿠버네티스는 선언적으로 "원하는 상태"를 명시하면 알아서 그 상태를 맞춰주는 식으로 동작한다.
즉, 원하는 만큼 파드를 추가하라 라고 명령하는 것이 아니라 원하는 파드의 갯수를 명시하면, 쿠버네티스가 그 원하는 상태에 자동으로 맞춰주었다.
레플리케이션컨트롤러 삭제
레플리케이션컨트롤러도 kubectl delete
명령어로 삭제 가능하다.
참고로 --cascade=false
옵션을 주면 레플리케이션컨트롤러는 삭제하되, 파드들은 그대로 유지시킬 수 있다.
레플리카셋
레플리카셋은 파드를 복제하고 노드 장애시 재스케줄링을 하는 쿠버네티스 구성요소로 레플리케이션컨트롤러를 대체한다.
레플리케이션컨트롤러 vs 레플리카셋
레플리카셋은 레플리케이션컨트롤러를 대체할 수 있는 쿠버네티스의 구성 요소다. 레플리카셋과 레플리케이션컨트롤러는 거의 동일하다. 다만 몇가지 차이가 있다.
- 레플리카셋은 보다 풍부한 표현식의 파드 셀렉터 보유: 특정 레이블이 있/없는 파드, 특정 레이블의 키를 갖는 파드 등 매칭 가능
- 하나의 레플리카셋으로 동일 레이블 키의 서로 다른 값을 가지는 두 파드를 매칭시켜 그룹화할 수 있다. 레플리케이션컨트롤러는 불가능하다.
요약하자면 레플리카셋이 레플리케이션컨트롤러의 상위호환이다. 레플리카셋을 사용하자.
레플리카셋 생성하기
apiVersion: apps/v1beta2
kind: ReplicaSet
metadata:
name: leonk
spec:
replicas: 3
selector:
matchLabels:
app: leonk
template:
metadata:
labels:
app: leonk
spec:
containers:
- name: leonk
image: chaewonkong/simple:server
위에서 레플리케이션컨트롤러를 생성할 때 사용했던 yaml과 비교해보면 셀렉터가 다르다는 것을 알 수 있다.
셀렉터 밑에 파드가 가져야할 레이블을 바로 나열하는 대신 selector.matchLabels
아래에 나열한다.
이제 레플리카셋을 생성해보자.
$ kubectl create -f replica_set.yml
레플리카셋의 레이블 셀렉터
레플리카셋의 레이블 셀렉터는 더 풍부한 표현식을 지원하는 것이 레플리케이션컨트롤러와의 주요 차이점이다.
spec:
replicas: 3
selector:
matchExpressions:
- key: app
operator: In
values:
- leonk
key, 연산자, 값 형태로 표현식을 생성한다.
- In은 주어진 값 중 하나와 일치
- NotIn은 주어진 값과 일치하지 않음
- Exists는 지정된 키를 가진 레이블이 포함되야 함. 이 경우 values를 지정하면 안 된다.
- DoesNotExist 지정된 키를 갖는 레이블이 포함되지 않아야 함. values 지정하면 안 된다.
여러 표현식을 지정하는 경우 모든 표현식이 true여야 셀렉터가 그 파드를 매칭할 수 있다.
데몬셋
클러스터의 모든 노드에 노드당 하나의 파드만 실행되길 원할 때 데몬셋 오브젝트를 활용할 수 있다.
모든 노드에 로그 수집기와 리소스 모니터를 실행하려는 경우 등이 이에 해당한다.
데몬셋은 레플리카셋과 달리 복제본 수라는 개념이 없다. 하나의 노드에 하나만 실행되기 때문이다.
데몬셋은 파드 셀렉터와 일치하는 파드 하나가 각 노드에서 실행 중인지 지속적으로 확인해 한개의 파드가 각 노드에서 실행되는 것을 보장한다.
데몬셋은 노드가 다운되어 그 노드 내부의 파드도 함께 죽었다고 하더라도 새 파드를 생성하지 않는다. 하지만 노드가 살아 있는데 누군가 실수로 파드를 제거했거나, 새 노드가 추가된 경우, 각 노드에 1개의 파드가 존재하도록 데몬셋은 템플릿에 따라 파드를 생성한다.
특정 노드로 한정해 파드 실행
기본적으로 데몬셋은 모든 노드에 파드를 1개씩 배포하지만, 원할 경우 필요한 노드만 지정해 파드를 실행하게 할 수 있다.
데몬셋을 정의할 때 nodeSelector
속성을 지정하면 된다.
예를 들어, GPU를 갖는 모든 노드에서 실행되야 하는 gpu-monitor라는 데몬이 있다면 이 방식으로 특정 노드를 지정해 파드를 실행시킬 수 있다.
apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: gpu-monitor
spec:
template:
metadata:
labels:
app: gpu-monitor
spec:
containers:
- name: main
image: { GPU_MONITOR_IMAGE }
nodeSelector:
gpu: "true"
selector:
matchLabels:
app: gpu-monitor
참고로 노드의 레이블이 변경되게 되면 데몬셋은 해당 노드의 파드를 제거한다.
예를들어 gpu=true
가 gpu=false
로 바뀌었다면, 당연히 gpu=true
인 노드의 파드들은 제거되는 것이 맞고, 실제로도 제거된다.
완료 가능한 태스크
레플리케이션컨트롤러, 레플리카셋, 데몬셋은 "완료"라는 개념이 존재하지 않는다. 지속적으로 상태를 모니터링 하다가 선언된 상태와 실행 중인 상태가 다른 경우 일치시키는 작업을 수행한다. 만약 예기치 않게 프로세스가 종료되면 다시 시작된다.
한편 완료 가능한 태스크(Completable Task)의 경우 프로세스가 종료되면 다시 시작되지 않는다.
잡 리소스(Job Resource)
쿠버네티스는 잡 리소스라는 것을 제공하는 데, 잡을 이용하면 파드의 컨테이너 내부에서 실행되는 프로세스가 성공적으로 완료되면 컨테이너를 다시 시작하지 않는 파드를 실행할 수 있다.
노드에 장애가 발생한 경우, 해당 노드에 있던 잡이 관리하는 파드는 다른 노드로 다시 스케줄링된다. 다만, 프로세스 자체에 장애가 발생한 경우에는 미리 설정된 값에 따라 잡이 컨테이너를 다시 시작할 것인지 결정된다.
이런 잡(Job)은 작업이 제대로 완료되는 거이 중요한 '임시' 작업에 유용하다. 예를 들어 파드에 저장되는 데이터를 추출해 전송하는 작업 등이 있을 것이다.
잡 생성
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
labels:
app: batch-job
spec:
containers:
- name: main
image: {IMAGE}
restartPolicy: OnFailure
작업이 완료되어도 파드가 삭제되진 않는데, 해당 파드의 로그를 조회할 수 있도록 하기 위해서다.
은 순차적, 혹은 병렬적으로도 실행하게 설정할 수 있다.
아래처럼 설정하면 다섯개의 파드를 순차적으로 실행하게 할 수 있다.
spec:
completions: 5
혹은 병렬로 잡 파드를 실행하게 할 수도 있다. 아래 설정은 2개의 파드가 병렬로 실행되도록 선언한다.
spec:
parallelism: 2
크론잡(CronJob)
많은 배치 작업이 특정 시간 혹은 지정된 간격으로 반복 실행되어야 한다. 예를들어 매달 25일에 직원들 월급을 입금하는 작업이라거나, 매주 월요일 고객들에게 한 주의 예약 상황을 메일로 전달하는 작업 같은 경우다.
쿠버네티스는 크론잡 리소스를 제공한다. 크론 형식으로 지정하며 원하는 주기로 실행할 수 있다. 리눅스의 크론과 거의 유사하다.
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minues
spec:
schedule: "0,15,30,45 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: batch-job
spec:
containers:
- name: main
image: {IMAGE}
restartPolicy: OnFailure
예정된 시간이 되면 크론잡 리소스에서 잡 리소스가 생성된다. 잡은 파드를 생성하고 파드는 프로세스를 실행한다.
만약 잡이나 파드가 상대적으로 늦게 생성되면 안되는 경우, 어떻게 해야 할까?
엄격한 요구사항의 경우 데드라인을 설정할 수 있다. 물론 데드라인이 있다고 데드라인을 지키지는 않고, 다만 데드라인을 넘길 경우 실패로 간주한다.