[istio] istio proxy 의 Graceful Startup & Termination

2025. 8. 7. 00:26·Service Mesh/Istio 운영 특이사항

들어가기 앞서 

이 글은 이전 포스팅 내용에 이어집니다.

https://hyukops.tistory.com/140

 

[istio] istio 의 Access Log 활성화

들어가기 앞서파드를 재배포하며 Rolling Update를 수행하는 과정에서, APM 모니터링을 통해 일시적으로 응답 속도가 지연되는 현상을 확인하였다. 이 현상은 Istio proxy(Envoy)가 종료 중인 파드로 트래

hyukops.tistory.com

현재 운영 중인 서비스는 파드 재배포 시 Kubernetes의 Rolling Update 전략을 사용하고 있다. maxSurge, maxUnavailable 등의 설정을 통해 최소한의 파드 수를 유지하며 점진적으로 교체가 이루어지도록 구성되어 있다. 그러나 이 과정에서 APM 모니터링 툴(x-view)의 지표를 통해 일시적으로 응답 속도가 증가하는 현상이 관측되었다.

메인 애플리케이션 컨테이너는 graceful shutdown을 설정해두었기 때문에, 해당 문제가 애플리케이션 자체보다는 Istio의 proxy 컨테이너(Envoy)에 기인한 것일 수 있다는 의심이 들었다. 이 글에서는 해당 현상의 원인을 분석하고, 이를 통해 어떤 문제가 발생할 수 있는지를 설명한다. 또한 이러한 문제를 완화하거나 방지할 수 있는 구성 방법에 대해서도 함께 제시하고자 한다.

1. Sidecar Graceful Startup

Sidecar Graceful Startup은 사이드카 프록시의 라이프사이클을 관리하기 위한 기능이다. 기본적으로 이 설정은 활성화되어 있으며, 사이드카 프록시가 주입된 파드는 애플리케이션 컨테이너보다 먼저 사이드카 프록시가 시작되도록 보장한다. 이 기능의 목적은 사이드카 프록시가 시작되지 않은 상태에서 애플리케이션 컨테이너로 향하는 트래픽이 손실되는 것을 방지하기 위함이다.

이 설정을 비활성화하면, 사이드카 프록시 컨테이너와 애플리케이션 컨테이너가 동시에 시작된다. 클러스터에 많은 파드가 배포되는 경우, API 서버의 부하로 인해 사이드카 프록시 컨테이너가 느리게 시작될 수 있다. 이런 경우 이 설정을 비활성화하면 배포 속도를 높일 수 있다.

1-1. 구성 예시: Deployment Annotation을 이용한 사이드카 Graceful Startup 비활성화

Istio는 사이드카가 먼저 시작되도록 기본 설정되어 있다. 이를 애플리케이션과 동시에 시작되도록 변경하려면 아래와 같이 holdApplicationUntilProxyStarts: false 옵션을 포함한 proxy.istio.io/config annotation을 사용하면 된다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: graceful-startup-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: graceful-startup-test
  template:
    metadata:
      labels:
        app: graceful-startup-test
      annotations:
        proxy.istio.io/config: |
          holdApplicationUntilProxyStarts: false
    spec:
      containers:
      - name: app
        image: kennethreitz/httpbin
kubectl apply -f graceful-startup-test.yaml

1-2. 구성 예시: Operator 설정을 이용한 사이드카 Graceful Startup 비활성화

Istio에서 Operator를 사용해 사이드카의 Graceful Startup 기능을 비활성화하려면, IstioOperator 리소스의 proxyMetadata 또는 meshConfig.defaultConfig 항목에 해당 설정을 명시해야 한다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-controlplane
  namespace: istio-system
spec:
  profile: default
  meshConfig:
    defaultConfig:
      proxyMetadata:
        PROXY_CONFIG: '{"holdApplicationUntilProxyStarts":false}'

 

istioctl install -f istio-operator.yaml 
OR 
kubectl apply -f
  • proxyMetadata.PROXY_CONFIG: Istio Proxy가 부팅 시 사용할 JSON 형식의 설정
  • "holdApplicationUntilProxyStarts": false → 애플리케이션이 사이드카보다 먼저 또는 동시에 시작해도 됨

2. Istio 사이드카와 파드 종료 시 안정적인 연결 종료 및 종료 동기화 방법 01

2-1. Istio 서비스 메쉬와 Envoy 사이드카 역할

Istio service mesh는 Envoy 프록시를 파드의 사이드카로 사용하여 애플리케이션으로 가는 모든 트래픽을 가로챈다. 이를 통해 Istio 사용자들은 트래픽 관리, 보안 정책 적용, 관찰성 등의 기능을 활용할 수 있다. 파드 삭제 시에는 최적의 종료 과정을 위해 몇 가지 조치를 취할 필요가 있습니다.

2-2. 파드 종료 시 Kubernetes 동작 방식

파드를 삭제할 때, kubelet은 파드 내 모든 컨테이너의 PID 1 프로세스에 TERM 신호를 보내 정상 종료할 시간을 부여한다. 이 정상 종료 시간은 기본 30초(terminationGracePeriodSeconds)이며, 만약 이 시간을 초과하면 kubelet은 2초를 추가로 기다린 뒤 KILL 신호를 보내 PID 1을 강제로 종료한다.

2-3. 사이드카와 애플리케이션 종료 동기화 문제

이 과정에서 사이드카가 애플리케이션 컨테이너와 적절히 동기화되지 않으면 문제가 발생한다. 사이드카가 애플리케이션의 모든 요청에 대해 응답하기 전에 먼저 종료되면, 5xx 에러가 발생하거나 요청이 재시도되면서 지연이 증가할 수 있다. 

2-4. terminationGracePeriodSeconds 기본값과 조정 필요성

만약 최대 40초가 걸리는 데이터베이스 애플리케이션이라면 기본 terminationGracePeriodSeconds 30초는 이 경우 부족하다. 따라서 deployment의 spec.template.spec.terminationGracePeriodSeconds를 40초로 설정해야 한다. 

2-5. Envoy 프록시 종료 지연 설정 - terminationDrainDuration

terminationDrainDuration은 프록시 종료 시 연결이 완료될 때까지 허용되는 시간이다. SIGTERM 또는 SIGINT 신호를 받으면 istio-agent가 활성 Envoy에게 정상 종료를 위해 점진적으로 연결을 차단하고, 새로운 연결을 막으며 기존 연결이 완료될 시간을 준다. 그 후 terminationDrainDuration 동안 대기한 뒤 남아있는 Envoy 프로세스를 강제로 종료한다. (기본값 5s)

하지만 kubelet의 종료 대기 시간만 늘리는 것으로는 부족하며, 사이드카가 이 40초 동안 종료되지 않고 새 연결 수락을 중지하며 기존 연결을 정상 종료하도록 해야 한다. Istio는 이를 위해 terminationDrainDuration 설정을 제공한다. 이 설정은 프록시 종료 시 연결이 완료될 때까지 허용되는 시간이다. SIGTERM 또는 SIGINT 신호를 받으면 istio-agent가 Envoy에게 정상적으로 연결을 드레인하도록 지시하며, 지정된 시간 동안 대기 후 남은 Envoy 프로세스를 강제 종료한다. (기본값 5s) terminationDrainDuration은 meshConfig (istio-system 네임스페이스의 configmap)에서 전체 메시에 적용하거나, 파드 단위로 annotation을 통해 설정할 수 있다.

template:
  metadata:
    annotations:
      proxy.istio.io/config: |
        terminationDrainDuration: 40s
data:
  mesh: |-
    accessLogFile: /dev/stdout
    defaultConfig:
      discoveryAddress: istiod.istio-system.svc:15012
      proxyMetadata: {}
      tracing:
        zipkin:
          address: zipkin.istio-system:9411
      terminationDrainDuration: 40s

2-6. 컨테이너 종료 순서와 동기화 방법

Kubernetes는 파드 내 컨테이너들에 SIGTERM 신호를 임의 순서로 보낼 수 있으므로, 종료 시점 동기화가 필요하다. 이를 위해 사이드카에도 preStop 훅을 설정해 데이터베이스 컨테이너와 동시에 종료 카운트를 시작하게 해야 한다. 예시로 데이터베이스 Deployment에 사이드카의 preStop을 추가하면 다음과 같다.

spec:
  containers:
  - name: mydatabase
    image: database-image
  - name: istio-proxy
    image: auto
    lifecycle:
      preStop:
        exec:
          command: ["kill", "-SIGTERM", "1"]

이렇게 하면 데이터베이스가 40초 동안 sleep을 시작하는 동시에 사이드카도 SIGTERM을 받아 활성 연결 드레인을 시작하여 종료 시나리오가 조화롭게 맞춰진다.

  • Pod 종료 시그널이 오면
  • istio-proxy 컨테이너의 preStop 훅이 먼저 실행됨
  • 이 훅이 Envoy 프로세스(PID 1)에게 SIGTERM을 전달함
  • Envoy는 연결 종료 절차를 시작하고, 설정된 terminationDrainDuration 등을 고려하여 graceful shutdown을 진행함
  • 그 후 컨테이너가 완전히 종료됨

3. Istio 사이드카와 파드 종료 시 안정적인 연결 종료 및 종료 동기화 방법 02

2번과 비슷한 방식으로 볼 수 있다. 서비스를 배포하면 관련된 워크로드는 롤링 업데이트 방식으로 순차적으로 교체된다. 이 과정에서 파드가 종료될 때, Istio의 사이드카(proxy)는 기본적으로 몇 초만 기다리고 바로 종료된다. 만약 서비스 요청 처리 시간이 길거나, 지속 연결(persistent connection)을 사용하는 경우라면 이 짧은 대기 시간 동안 정상적인 요청이나 연결이 끊길 수 있다. 이 문제를 해결하려면, 메인 컨테이너가 현재 처리 중인 요청이나 연결이 모두 종료된 후에 사이드카가 종료되도록 만들어야 한다. 이를 위해 Istio 1.12부터는 EXIT_ON_ZERO_ACTIVE_CONNECTIONS 환경 변수를 사이드카에 설정할 수 있게 되었다. 이 설정을 사용하면, 서버는 클라이언트에게 연결 종료를 명확하게 지시한다.

3-1. Pod가 종료될 때 무슨 일이 벌어질까?

Pod가 종료되면 istio-proxy 컨테이너는 SIGTERM 시그널을 받고, Envoy Proxy는 새 커넥션을 받지 않도록 전환한 후 기본적으로 5초 뒤에 종료된다. 이 5초는 Envoy의 draining duration 기본값이다. 우리는 Pod가 종료되더라도 기존의 연결이 모두 정상적으로 마무리된 후에 Envoy가 종료되기를 기대한다. 하지만 실제로는, 5초 이내에 커넥션이 종료되지 않으면 해당 요청은 중단되고 연결이 끊기게 된다. 처리 시간이 긴 요청일수록 이러한 상황에 취약하다.

3-2. 설정 없이 종료하면 어떻게 될까?

delay/10 경로로 요청을 보낸 후 즉시 Deployment의 복제 수를 0으로 줄여 Pod를 종료한다.

$ curl -I https://graceful-shutdown-app.jinsu.me/delay/10 & \
kubectl scale deployment graceful-shutdown-app --replicas=0; \
TZ=GMT date +%T;

deployment.apps/graceful-shutdown-app scaled
18:40:30
HTTP/2 503
server: istio-envoy
date: Sat, 04 Feb 2023 18:40:35 GMT

정상적이라면 10초 뒤에 응답을 받아야 하지만, 약 5초 만에 503 에러가 발생했다. 이는 Envoy Proxy가 연결을 유지하지 못한 채 먼저 종료되었기 때문이다. 참고로 클라이언트가 받은 503 응답은 애플리케이션에서 발생한 것이 아니라 Istio Ingress Gateway가 백엔드 커넥션 종료(connection reset)를 감지해 대신 전달한 결과다. 이 과정은 Ingress Gateway의 디버그 로그를 통해 확인할 수 있다.

3-3. Envoy 종료를 지연시키는 설정: EXIT_ON_ZERO_ACTIVE_CONNECTIONS

Istio v1.12부터는 이 문제를 해결하기 위해 EXIT_ON_ZERO_ACTIVE_CONNECTIONS 환경 변수가 도입되었다. 이 값을 활성화하면 Envoy는 단순히 정해진 시간(기본 5초)을 기다리는 것이 아니라, 모든 커넥션이 종료될 때까지 기다린 후에 종료된다. 또한 이때 서버는 클라이언트에게 명시적으로 커넥션을 닫으라고 알린다.

  • HTTP/1: 응답 헤더에 Connection: close 추가
  • HTTP/2: GOAWAY 프레임 전송

Envoy는 MINIMUM_DRAIN_DURATION(기본 5초) 이상만 유지되면 커넥션 수가 0이 될 때까지 종료를 지연시킬 수 있다. 이 설정을 통해 요청 처리 중 Envoy가 먼저 종료되어 커넥션이 끊기는 상황을 방지할 수 있다.

3-4. 커넥션 보호 기능 적용하기

Operator 기준으로 변경 시에는 ProxyConfig CR에서 설정해야 한다. 아래와 같이 proxyMetadata 부분에EXIT_ON_ZERO_ACTIVE_CONNECTIONS를 추가하면 된다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: example-istiocontrolplane
spec:
  components:
    proxy:
      proxyMetadata:
        EXIT_ON_ZERO_ACTIVE_CONNECTIONS: "true"

혹은, Deployment에 다음과 같은 Annotation을 추가하여 기능을 활성화한다.

proxy.istio.io/config: |
  proxyMetadata:
    EXIT_ON_ZERO_ACTIVE_CONNECTIONS: 'true'

이제 새로 생성되는 Pod에는 EXIT_ON_ZERO_ACTIVE_CONNECTIONS=true 환경 변수가 자동으로 주입된다. 이 설정 덕분에 Envoy는 SIGTERM 신호를 받은 후 활성 연결이 모두 종료될 때까지 기다렸다가 종료된다.

$ curl -I https://graceful-shutdown-app.jinsu.me/delay/10 & \
kubectl scale deployment graceful-shutdown-app --replicas=0; \
TZ=GMT date +%T;

deployment.apps/graceful-shutdown-app scaled
19:00:00
HTTP/2 200
server: istio-envoy
date: Sat, 04 Feb 2023 19:00:11 GMT

이번에는 요청이 정상적으로 처리되어 200 OK 응답을 받는다. Pod가 종료 중일 때도 연결이 안전하게 유지된 결과다.

3-5. terminationGracePeriodSeconds

EXIT_ON_ZERO_ACTIVE_CONNECTIONS를 설정하더라도, Pod의 terminationGracePeriodSeconds를 초과하면 컨테이너는 SIGKILL로 강제 종료된다. 따라서 요청 처리 시간이 긴 경우, 해당 값을 충분히 크게 설정해줘야 한다. 예를 들어 httpbin 이미지의 /delay/10 테스트를 안정적으로 수행하려면 terminationGracePeriodSeconds를 최소 10초 이상(기본값은 30s)으로 설정해야 한다. 

4. 각 항목 관계 및 차이 요약

  • terminationGracePeriodSeconds (Kubernetes)
    • Pod 종료 시 Kubernetes가 전체 종료를 기다려주는 최대 시간
    • 이 시간이 지나면 SIGKILL로 컨테이너를 강제로 종료
    • 최상위 제약 조건이라고 보면 됨
  • terminationDrainDuration (Istio / Envoy)
    • Envoy가 SIGTERM을 받고 기존 연결을 처리하며 기다리는 시간
    • proxy가 drain된 후 이 시간이 지난 후에 Envoy는 종료됨
    • Kubernetes 수준의 terminationGracePeriodSeconds보다 길면 무시됨
  • EXIT_ON_ZERO_ACTIVE_CONNECTIONS (Envoy)
    • 활성 연결이 0이 되면 즉시 Envoy 종료
    • 종료 조건을 시간이 아니라 상태로 제어

4-1.  예시로 설명

terminationGracePeriodSeconds: 45
terminationDrainDuration: 40
EXIT_ON_ZERO_ACTIVE_CONNECTIONS: true
  • Pod이 종료되면 Kubernetes는 최대 45초 동안 종료를 기다림
  • Envoy는 SIGTERM 받고 최대 40초 동안 connection draining을 수행
  • 단, 그 전에 연결이 0개가 되면 EXIT_ON_ZERO_ACTIVE_CONNECTIONS에 의해 즉시 종료됨
  • 만약 Envoy가 45초가 지나도록 종료되지 않으면, Kubernetes는 SIGKILL로 강제 종료

 

 

'Service Mesh > Istio 운영 특이사항' 카테고리의 다른 글

[istio] Default Health Check Route and Port  (0) 2025.10.26
[istio] argo rollout + istio 를 통한 canary 배포 전략 도입  (0) 2025.10.26
[istio] istioOperator 커스텀  (0) 2025.08.20
[istio] istio 의 Access Log 활성화  (3) 2025.08.06
[Istio] NLB 의 Target Group 헬스 체크 실패  (0) 2025.04.30
'Service Mesh/Istio 운영 특이사항' 카테고리의 다른 글
  • [istio] argo rollout + istio 를 통한 canary 배포 전략 도입
  • [istio] istioOperator 커스텀
  • [istio] istio 의 Access Log 활성화
  • [Istio] NLB 의 Target Group 헬스 체크 실패
Hyukops
Hyukops
안녕하세요
  • Hyukops
    Hyukops 님의 Tech Blog
    Hyukops
    • 분류 전체보기 (141)
      • Introduction (1)
      • Kubernetes & EKS (43)
        • k8s in action (9)
        • k8s 공부 기록 (17)
        • k8s 운영 가이드 (10)
        • k8s 운영 특이사항 (7)
      • Service Mesh (29)
        • Istio 공부 기록 (20)
        • Istio 운영 특이사항 (9)
      • CICD (10)
        • argoCD 공부 기록 (6)
        • argoCD 운영 특이사항 (4)
      • Logging & Monitoring (5)
        • Prometheus 운영 특이사항 (0)
        • fluent bit 운영 특이사항 (5)
      • Infrastructure as Code (8)
        • terraform 공부 기록 (3)
        • terraform 운영 특이사항 (5)
      • AWS (40)
        • aws 공부 기록 (29)
        • 솔루션 사례 & 문제 해결 (11)
      • Database (5)
        • postgreSQL (5)
  • 태그

    canary
    MSK
    eks
    fluentbit
    Logging
    Terraform
    kubernetes
    Istio
    AWS
    Database
    PostgreSQL
    fluent bit
    k8s in action
    prometheus
    argocd
    aws saa
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Hyukops
[istio] istio proxy 의 Graceful Startup & Termination
상단으로

티스토리툴바