Liveness, Readiness, Startup 프로브 구성하기
Liveness, Readiness, Startup 프로브 구성하기
이 페이지에서는 컨테이너에 대한 liveness, readiness, startup 프로브를 구성하는 방법을 보여드려요. 프로브에 대한 자세한 내용은 Liveness, Readiness, Startup 프로브를 참고하세요.
출처: 문서
본문
시작하기 전에 (Before you begin)
쿠버네티스 클러스터가 필요하고, kubectl 명령줄 도구가 클러스터와 통신하도록 구성돼 있어야 해요. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 두 개 이상 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube로 만들거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있어요.
- iximiuz Labs
- Killercoda
- KodeKloud
liveness 명령 정의하기 (Define a liveness command)
오랫동안 실행되는 많은 애플리케이션은 결국 깨진 상태로 전환되고, 재시작하지 않으면 회복할 수 없어요. 쿠버네티스는 이런 상황을 감지하고 해결하기 위해 liveness 프로브를 제공해요.
이 실습에서는 registry.k8s.io/busybox:1.27.2 이미지 기반의 컨테이너를 실행하는 Pod를 만들어요. 다음은 이 Pod의 구성 파일이에요.
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.k8s.io/busybox:1.27.2
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
pods/probe/exec-liveness.yaml — 구성 파일에서 Pod가 단일 Container를 가진 것을 볼 수 있어요. periodSeconds 필드는 kubelet이 5초마다 liveness 프로브를 수행해야 한다고 지정해요. initialDelaySeconds 필드는 첫 프로브를 수행하기 전에 5초를 기다려야 한다고 알려줘요. 프로브를 수행하기 위해 kubelet은 대상 컨테이너에서 cat /tmp/healthy 명령을 실행해요. 명령이 성공하면 0을 반환하고, kubelet은 컨테이너가 살아 있고 건강하다고 간주해요. 명령이 0이 아닌 값을 반환하면 kubelet은 컨테이너를 죽이고 재시작해요.
컨테이너가 시작되면 다음 명령을 실행해요.
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"
컨테이너 수명의 처음 30초 동안은 /tmp/healthy 파일이 존재해요. 그래서 처음 30초 동안 cat /tmp/healthy 명령은 성공 코드를 반환해요. 30초가 지나면 cat /tmp/healthy는 실패 코드를 반환해요.
Pod를 생성해요.
kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml
30초 안에 Pod 이벤트를 확인해요.
kubectl describe pod liveness-exec
출력은 아직 liveness 프로브가 실패하지 않았음을 나타내요.
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 11s default-scheduler Successfully assigned default/liveness-exec to node01
Normal Pulling 9s kubelet, node01 Pulling image "registry.k8s.io/busybox:1.27.2"
Normal Pulled 7s kubelet, node01 Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal Created 7s kubelet, node01 Created container liveness
Normal Started 7s kubelet, node01 Started container liveness
35초 후에 Pod 이벤트를 다시 확인해요.
kubectl describe pod liveness-exec
출력의 맨 아래에는 liveness 프로브가 실패했고, 실패한 컨테이너가 죽고 다시 생성됐다는 메시지가 있어요.
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 57s default-scheduler Successfully assigned default/liveness-exec to node01
Normal Pulling 55s kubelet, node01 Pulling image "registry.k8s.io/busybox:1.27.2"
Normal Pulled 53s kubelet, node01 Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal Created 53s kubelet, node01 Created container liveness
Normal Started 53s kubelet, node01 Started container liveness
Warning Unhealthy 10s (x3 over 20s) kubelet, node01 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 10s kubelet, node01 Container liveness failed liveness probe, will be restarted
30초 더 기다렸다가 컨테이너가 재시작됐는지 확인해요.
kubectl get pod liveness-exec
출력은 RESTARTS가 증가했음을 보여줘요. RESTARTS 카운터는 실패한 컨테이너가 실행 상태로 돌아오는 즉시 증가한다는 점을 주의하세요.
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
liveness HTTP 요청 정의하기 (Define a liveness HTTP request)
또 다른 종류의 liveness 프로브는 HTTP GET 요청을 사용해요. 다음은 registry.k8s.io/e2e-test-images/agnhost 이미지 기반 컨테이너를 실행하는 Pod의 구성 파일이에요.
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: registry.k8s.io/e2e-test-images/agnhost:2.40
args:
- liveness
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
pods/probe/http-liveness.yaml — 구성 파일에서 Pod가 단일 컨테이너를 가진 것을 볼 수 있어요. periodSeconds 필드는 kubelet이 3초마다 liveness 프로브를 수행해야 한다고 지정해요. initialDelaySeconds 필드는 첫 프로브를 수행하기 전에 3초를 기다려야 한다고 알려줘요. 프로브를 수행하기 위해 kubelet은 컨테이너에서 실행 중이고 포트 8080에서 수신 중인 서버로 HTTP GET 요청을 보내요. 서버의 /healthz 경로 핸들러가 성공 코드를 반환하면 kubelet은 컨테이너가 살아 있고 건강하다고 간주해요. 핸들러가 실패 코드를 반환하면 kubelet은 컨테이너를 죽이고 재시작해요.
200 이상 400 미만의 모든 코드는 성공을 나타내요. 그 외의 코드는 실패를 나타내요.
server.go에서 서버의 소스 코드를 볼 수 있어요.
컨테이너가 살아 있는 처음 10초 동안 /healthz 핸들러는 상태 200을 반환해요. 그 후에는 상태 500을 반환해요.
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})
kubelet은 컨테이너 시작 후 3초에 헬스 체크를 시작해요. 그래서 처음 몇 번의 헬스 체크는 성공할 거예요. 하지만 10초 후에는 헬스 체크가 실패하고, kubelet이 컨테이너를 죽이고 재시작할 거예요.
HTTP liveness 체크를 시도하려면 Pod를 생성해요.
kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml
10초 후에 Pod 이벤트를 확인해 liveness 프로브가 실패하고 컨테이너가 재시작됐는지 검증해요.
kubectl describe pod liveness-http
v1.13 이후 릴리스에서는 로컬 HTTP 프록시 환경 변수 설정이 HTTP liveness 프로브에 영향을 주지 않아요.
HTTP 프로브에서 HTTP/2 cleartext (h2c) 사용하기 (Use HTTP/2 cleartext (h2c) with HTTP probes)
기능 상태: Kubernetes v1.37부터 Alpha; 기본적으로 비활성화.
이 기능을 사용하려면 여러분(또는 클러스터 관리자)이 클러스터의 모든 관련 컴포넌트에 대해 H2CContainerProbe 기능 게이트를 활성화해야 해요. 기능 게이트 활성화/비활성화에 대한 자세한 내용은 참고하세요.
기본적으로 kubelet은 HTTP 프로브를 실행할 때 HTTP/1.1 요청을 보내요. 애플리케이션이 HTTP/2 cleartext (h2c)에서만 헬스 엔드포인트를 제공한다면, 프로브 사양의 httpGet 필드에 protocol 필드를 추가하고 HTTP2 값을 지정할 수 있어요. 이를 위해서는 H2CContainerProbe 기능 게이트가 kube-apiserver와 kubelet 모두에서 활성화돼 있어야 해요.
apiVersion: v1
kind: Pod
metadata:
name: h2c-app
spec:
containers:
- name: app
image: registry.k8s.io/e2e-test-images/agnhost:2.64.0
command: ["/agnhost", "h2c-server", "--port=8080"]
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: 8080
protocol: HTTP2
initialDelaySeconds: 5
periodSeconds: 10
pods/probe/h2c-liveness.yaml — protocol이 HTTP2로 설정되면 kubelet은 HTTP/2 cleartext (h2c), 즉 TLS 없이 일반 TCP 위의 HTTP/2로 연결해요. 다음 구성은 지원되지 않아요.
protocol: HTTP2와scheme: HTTPSprotocol: HTTP2와host필드의 값
기능 게이트가 비활성화되면 API 서버는 새 Pod나 업데이트된 Pod에서 protocol 필드를 제거해요.
TCP liveness 프로브 정의하기 (Define a TCP liveness probe)
세 번째 유형의 liveness 프로브는 TCP 소켓을 사용해요. 이 구성에서 kubelet은 지정된 포트의 컨테이너에 소켓을 열려고 시도해요. 연결을 수립할 수 있으면 컨테이너가 건강한 것으로 간주되고, 그렇지 못하면 실패로 간주돼요.
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: registry.k8s.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
pods/probe/tcp-liveness-readiness.yaml — 보시다시피 TCP 체크 구성은 HTTP 체크와 꽤 비슷해요. 이 예시는 readiness와 liveness 프로브를 모두 사용해요. kubelet은 컨테이너 시작 후 15초에 첫 liveness 프로브를 실행해요. 이 프로브는 포트 8080에서 goproxy 컨테이너에 연결을 시도해요. liveness 프로브가 실패하면 컨테이너가 재시작돼요. kubelet은 이 체크를 10초마다 계속 실행해요.
liveness 프로브 외에도 이 구성은 readiness 프로브를 포함해요. kubelet은 컨테이너 시작 후 15초에 첫 readiness 프로브를 실행해요. liveness 프로브와 비슷하게 이 프로브는 포트 8080에서 goproxy 컨테이너에 연결을 시도해요. 프로브가 성공하면 Pod가 준비됨(ready)으로 표시되고 서비스로부터 트래픽을 받아요. readiness 프로브가 실패하면 파드가 준비 안 됨(unready)으로 표시되고 어떤 서비스로부터도 트래픽을 받지 않아요.
TCP liveness 체크를 시도하려면 Pod를 생성해요.
kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml
15초 후에 Pod 이벤트를 확인해 liveness 프로브를 검증해요.
kubectl describe pod goproxy
gRPC liveness 프로브 정의하기 (Define a gRPC liveness probe)
기능 상태: Kubernetes v1.27부터 Stable.
애플리케이션이 gRPC Health Checking Protocol을 구현한다면, 이 예시는 애플리케이션 liveness 체크에 사용하도록 쿠버네티스를 구성하는 방법을 보여드려요. 비슷하게 readiness와 startup 프로브도 구성할 수 있어요.
다음은 예시 매니페스트예요.
apiVersion: v1
kind: Pod
metadata:
name: etcd-with-grpc
spec:
containers:
- name: etcd
image: registry.k8s.io/etcd:3.5.1-0
command: [ "/usr/local/bin/etcd", "--data-dir", "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
ports:
- containerPort: 2379
livenessProbe:
grpc:
port: 2379
initialDelaySeconds: 10
pods/probe/grpc-liveness.yaml — gRPC liveness 체크를 시도하려면 아래 명령으로 Pod를 생성해요. 아래 예시에서 etcd 파드는 gRPC liveness 프로브를 사용하도록 구성돼 있어요.
kubectl apply -f https://k8s.io/examples/pods/probe/grpc-liveness.yaml
15초 후에 Pod 이벤트를 확인해 liveness 체크가 실패하지 않았는지 검증해요.
kubectl describe pod etcd-with-grpc
gRPC 프로브를 사용할 때 알아둬야 할 몇 가지 기술적 세부 사항이 있어요.
- 프로브는 파드 IP 주소나 호스트네임에 대해 실행돼요. gRPC 엔드포인트가 Pod의 IP 주소에서 수신하도록 구성해야 해요.
- 프로브는 기본적으로 평문(plaintext)으로 연결돼요. TLS 활성화 방법은 gRPC 프로브에서 TLS 사용하기를 참고하세요.
- 내장 프로브에는 오류 코드가 없어요. 모든 오류는 프로브 실패로 간주돼요.
ExecProbeTimeout기능 게이트가false로 설정되면 grpc-health-probe는timeoutSeconds설정(기본 1s)을 존중하지 않는 반면, 내장 프로브는 타임아웃 시 실패해요.
gRPC 프로브에서 TLS 사용하기 (Use TLS with gRPC probes)
기능 상태: Kubernetes v1.37부터 Alpha; 기본적으로 비활성화.
이 기능을 사용하려면 여러분(또는 클러스터 관리자)이 클러스터의 모든 관련 컴포넌트에 대해 GRPCContainerProbeTLS 기능 게이트를 활성화해야 해요. 기능 게이트 활성화/비활성화에 대한 자세한 내용은 참고하세요.
기본적으로 kubelet은 gRPC 헬스 엔드포인트에 평문으로 연결해요. 애플리케이션이 TLS에서만 gRPC를 제공한다면, 프로브 사양의 grpc 필드에 mode 필드를 추가하고 TLS 값을 지정할 수 있어요. 이를 위해서는 GRPCContainerProbeTLS 기능 게이트가 kube-apiserver와 kubelet 모두에서 활성화돼 있어야 해요.
apiVersion: v1
kind: Pod
metadata:
name: grpc-tls-app
spec:
containers:
- name: app
image: registry.k8s.io/e2e-test-images/agnhost:2.40
command: ["/agnhost", "grpc-health-checking", "--port=8443"]
ports:
- containerPort: 8443
livenessProbe:
grpc:
port: 8443
mode: TLS
initialDelaySeconds: 5
periodSeconds: 10
pods/probe/grpc-tls-liveness.yaml — mode가 TLS로 설정되면 kubelet은 InsecureSkipVerify로 TLS를 통해 연결하고 서버 인증서를 검증하지 않아요. 이는 HTTPS 프로브의 동작과 일치해요. 인증서 검증은 지원되지 않아요.
기능 게이트가 비활성화되면 kube-apiserver가 새 Pod나 업데이트된 Pod에서 mode 필드를 제거해요.
이름이 있는 포트 사용하기 (Use a named port)
HTTP와 TCP 프로브에 이름이 있는 port를 사용할 수 있어요. gRPC 프로브는 이름이 있는 포트를 지원하지 않아요.
예를 들어:
ports:
- name: liveness-port
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
startup 프로브로 느리게 시작하는 컨테이너 보호하기 (Protect slow starting containers with startup probes)
때로는 첫 초기화에 추가 시작 시간이 필요한 애플리케이션을 다뤄야 해요. 그런 경우 데드락에 대한 빠른 응답을 희생하지 않으면서 liveness 프로브 매개변수를 설정하는 것이 까다로울 수 있어요. 해결책은 failureThreshold * periodSeconds가 최악의 시작 시간을 충분히 덮을 만큼 긴, 동일한 명령/HTTP/TCP 체크의 startup 프로브를 설정하는 것이에요.
그래서 앞선 예시는 다음과 같이 되요.
ports:
- name: liveness-port
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 10
startupProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10
startup 프로브 덕분에 애플리케이션은 시작을 끝내는 데 최대 5분(30 * 10 = 300s)을 가질 수 있어요. startup 프로브가 한 번 성공하면 liveness 프로브가 컨테이너 데드락에 대한 빠른 응답을 제공하기 시작해요. startup 프로브가 결코 성공하지 않으면 컨테이너는 300초 후에 죽고 파드의 restartPolicy에 따르게 돼요.
readiness 프로브 정의하기 (Define readiness probes)
때로는 애플리케이션이 일시적으로 트래픽을 서빙할 수 없는 경우가 있어요. 예를 들어 애플리케이션이 시작 중에 큰 데이터나 구성 파일을 로드해야 하거나, 시작 후 외부 서비스에 의존할 수도 있어요. 그런 경우 애플리케이션을 죽이지도 않으면서 요청을 보내지도 않길 원해요. 쿠버네티스는 이런 상황을 감지하고 완화하기 위해 readiness 프로브를 제공해요. 준비 안 됐다고 보고하는 컨테이너가 있는 파드는 쿠버네티스 Service를 통해 트래픽을 받지 않아요.
참고: readiness 프로브는 컨테이너의 전체 수명 동안 실행돼요.
주의: readiness와 liveness 프로브는 성공하기 위해 서로 의존하지 않아요. readiness 프로브를 실행하기 전에 기다리려면
initialDelaySeconds나startupProbe를 사용해야 해요.
readiness 프로브는 liveness 프로브와 비슷하게 구성돼요. 유일한 차이는 livenessProbe 필드 대신 readinessProbe 필드를 사용한다는 점이에요.
readinessProbe:
exec:
command:
- /bin/cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
HTTP와 TCP readiness 프로브의 구성도 liveness 프로브와 동일해요.
readiness와 liveness 프로브는 같은 컨테이너에 병렬로 사용할 수 있어요. 둘 다 사용하면 준비되지 않은 컨테이너에 트래픽이 도달하지 않고, 실패한 컨테이너가 재시작되도록 보장할 수 있어요.
다음 단계 (What's next)
- Liveness, Readiness, Startup 프로브에 대해 더 배우기
- 프로브 관련 필드의 전체 사양은 API 참조(Pod, Container, Probe)를 참고