본문 바로가기

개발관련/Kubernetes

kubernetes Pod의 진단을 담당하는 리소스 : probe

반응형

프로브(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 이다.

 

언제 활성 프로브(liveness probe)를 사용해야 하는가?

  • 만약 컨테이너 속 프로세스가 어떠한 이슈에 직면하거나 건강하지 못한 상태(unhealthy)가 되는 등 프로세스 자체의 문제로 중단될 수 있더라도, 활성 프로브가 반드시 필요한 것은 아니다. 이러한 경우에는 kubelet이 파드의 재시작 정책(restart policy)에 따라서 올바른 대처를 자동적으로 수행한다.
  • 프로브가 실패한 후 컨테이너가 종료되거나 재시작 되기를 원한다면, 활성 프로브를 지정하고, 재시작 정책을 항상(Always) 또는 실패 시(OnFailure)로 지정한다.

 

언제 준비성 프로브(readiness probe)를 사용해야 하는가?

  • 프로브가 성공한 경우에만 파드에 트래픽 전송을 시작하려고 한다면, 준비성 프로브를 지정하기를 바란다. 이 경우에서는, 준비성 프로브가 활성 프로브와 유사해 보일 수도 있지만, 스팩에 준비성 프로브가 존재한다는 것은 파드가 트래픽을 받지 않는 상태에서 시작되고 프로브가 성공하기 시작한 이후에만 트래픽을 받는다는 것이다. 만약 컨테이너가 대량의 데이터, 설정 파일들, 또는 시동 중 마이그레이션을 처리해야 한다면, 준비성 프로브를 지정하기 바란다.
  • 만약 당신의 컨테이너가 유지 관리를 위해서 자체 중단되게 하려면, 준비성 프로브를 지정하길 바란다. 준비성 프로브는 활성 프로브와는 다르게 준비성에 특정된 엔드포인트를 확인한다.
  • 파드가 삭제될 때 단지 요청들이 흘려 보낼(drain) 목적으로, 준비성 프로브가 필요하지 않다는 점을 유념해야한다. 삭제 시에, 파드는 프로브의 존재 여부와 무관하게 자동으로 스스로를 준비되지 않은 상태(unready)로 변경한다. 파드는 파드 내의 모든 컨테이너들이 중지될 때 까지 준비되지 않은 상태로 남아있는다.

 

언제 스타트업 프로브(startup probe)를 사용해야 하는가?

  • 컨테이너가 보통 초기 지연 시간 + 실패 임계 값 * 대기 초(initialDelaySeconds + failureThreshold * periodSeconds) 이후에 기동된다면, 스타트업 프로브가 활성화 프로브와 같은 엔드포인트를 체크하도록 명시해야 한다.
  • periodSeconds의 기본 값은 30초 이다. 이 때 컨테이너가 활성화 프로브의 기본 값 변경 없이 기동되도록 하려면 failureThreshold를 충분히 높게 설정해주어야 한다. 그래야 데드락(deadlock)을 방지하는데 도움이 된다.

 

활성, 준비성 및 스타트업 프로브를 설정하는 방법에 대한 추가적인 정보는, 활성, 준비성 및 스타트업 프로브 설정하기를 참조하면 된다.

 


 

Kubernetes에서 새로운 버전의 어플리케이션을 배포하기 위해 Pod를 Rolling update할 때, Pod는 띄워졌지만 내부의 프로세스(예를 들면 서버 어플리케이션)가 제대로 뜨지 못한 상태에서 기존 Pod를 terminate하면 새로 띄워진 Pod 내부의 프로세스가 다 뜨기 전까지는 해당 프로세스에 요청을 날릴 수 없으므로 다운타임(504 Bad gateway)이 발생한다.

 

따라서 이를 방지하기 위해 kubernetes에서는 probe라는 이벤트 리스너를 생성하여 kubelet이 Pod 내부의 컨테이너를 진단하게 하여 컨테이너가 실행 중인지(livenessProbe), HTTP 요청을 처리할 준비가 되었는지(readinessProbe), 컨테이너 내에서 응용 프로그램이 시작되었는지(startupProbe) 확인할 수 있도록 기능을 제공한다.

 

예제를 위한 deployment, service manifest를 아래와 같이 작성해보자.

간단한 웹 서버를 띄워주는 manifest 이다.

 

test.yaml

kind: Deployment
apiVersion: apps/v1
metadata:
  name: hello-world-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world-app
  template:
    metadata:
      labels:
        app: hello-world-app
    spec:
      containers:
      - name: hello-world-app
        image: python:2.7
        imagePullPolicy: IfNotPresent
        command: ["/bin/bash"]
        args: ["-c", "echo \"<p>Hello from $(hostname)</p>\" > index.html; python -m SimpleHTTPServer 8080"]
        ports:
        - name: http
          containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello-world-app-service
spec:
  type: NodePort
  selector:
    app: hello-world-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 30000
      nodePort: 30000

 

 

작성한 manifest를 아래 명령어를 통해 kubernetes 클러스터에 배포해보자.

$ kubectl apply -f ./test.yaml

 

이제 다시 deployment 부분만 아래와 같이 수정하여 다시 apply 해보자.

kind: Deployment
apiVersion: apps/v1
metadata:
  name: hello-world-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world-app
  template:
    metadata:
      labels:
        app: hello-world-app
    spec:
      containers:
      - name: hello-world-app
        image: python:2.7
        imagePullPolicy: IfNotPresent
        command: ["/bin/bash"]
        args: ["-c", "echo \"<p>Hello from $(hostname)</p>\" > index.html; sleep 30000 && python -m SimpleHTTPServer 8080"]
        ports:
        - name: http
          containerPort: 8080

 

 

args 부분에서 index.html을 만든 다음 sleep 30000 조건을 넣어 30초 정도 딜레이 되도록 변경했다.

 

만약 컨테이너를 띄우는 데 많은 시간이 들지 않는다면 상관 없겠지만, 수정한 내용과 같이 Pod 내부에 새로운 프로세스가 뜨는데 30초 정도 걸린다고 가정한다면 Pod를 새로 배포할 때 Rolling Update시 30초 정도 다운 타임이 발생하게 되는 것이다.

 

이를 방지하기 위해 probe를 설정하여 프로세스가 뜰 때 까지는 기존 Pod를 terminate시키지 않도록 아래와 같이 deployment를 수정해보자.

kind: Deployment
apiVersion: apps/v1
metadata:
  name: hello-world-app
spec:
  replicas: 4
  selector:
    matchLabels:
      app: hello-world-app
  template:
    metadata:
      labels:
        app: hello-world-app
    spec:
      containers:
      - name: hello-world-app
        image: python:2.7
        imagePullPolicy: IfNotPresent
        command: ["/bin/bash"]
        args: ["-c", "echo \"<p>Hello from $(hostname)</p>\" > index.html; sleep 30000 && python -m SimpleHTTPServer 8080"]
        ports:
        - name: http
          containerPort: 8080
        readinessProbe: # 이 부분부터 추가하면 된다.
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10

 

위와 같이 readinessProbe 라는 옵션을 추가했는데, 이를 설명하면 아래와 같다.

 

readinessProbe는 컨테이너가 요청을 처리할 준비가 되었는지 여부를 확인해준다.

Pod가 뜨게되면 readinessProbeinitialDelaySeconds에 지정한 값 만큼 기다렸다가 periodSeconds의 값만큼 한번씩 httpGet에 세팅한 pathport로 health check를 위한 HTTP Get 요청을 날린다.

여기서 전달받은 response의 stauts code가 성공(200) 이라면 kubelet은 해당 Pod의 컨테이너가 살아있다고 간주하게 된다.

 

따라서 Rolling Update시에 새로 뜬 Pod에 대해서 readinessProbe의 역할을 마치기 전까지 기존 Pod를 terminate 시키지 않기 때문에 Rolling Update로 인한 다운 타임이 방지된다.

 

하지만 반대로 httpGet에 세팅한 경로로 health check를 했는데 성공(200)이 아닌 400 혹은 500의 status 를 리턴 받는다면, 이 Pod는 이상이 있는 것으로 간주하고 kubelet은 새로운 Pod를 다시 띄우게 된다.

 

따라서 이 옵션은 Pod 내에 구동되고 있는 프로세스(어플리케이션)의 health check를 담당하고 있다고 볼 수 있다.

 


참고

 

 

Pod Lifecycle

 

kubernetes.io

 

 

Configure Liveness, Readiness and Startup Probes

 

kubernetes.io

 

 

파드 라이프사이클

 

kubernetes.io

 

반응형