K8s 네이티브 사이드카 컨테이너 심화

K8s 네이티브 사이드카란?

Kubernetes 1.28부터 도입된 네이티브 사이드카 컨테이너(KEP-753)는 Init Container에 restartPolicy: Always를 지정하여, Pod 전체 라이프사이클 동안 실행되는 공식 사이드카 패턴입니다. 기존에는 일반 컨테이너를 사이드카로 사용했지만, 시작 순서 보장 불가종료 순서 미제어 문제가 있었습니다.

이 글에서는 네이티브 사이드카의 동작 원리, 기존 패턴과의 차이, 실전 구성 예제, 그리고 운영 시 주의점까지 심층적으로 다룹니다. 기존 Init·Sidecar 컨테이너 패턴 글과 함께 읽으면 전체 그림을 이해할 수 있습니다.

기존 사이드카 vs 네이티브 사이드카

항목 기존 사이드카 (일반 컨테이너) 네이티브 사이드카 (1.28+)
시작 순서 보장 안됨 (모든 컨테이너 동시 시작) Init Container 순서대로 → 메인보다 먼저 시작
종료 순서 미제어 (사이드카가 먼저 죽을 수 있음) 메인 컨테이너 종료 후 사이드카 종료
Job 호환성 사이드카가 안 죽어서 Job 완료 안됨 메인 종료 시 자동 종료 → Job 정상 완료
Probe 지원 지원 startup/liveness Probe 지원
리소스 관리 Pod 리소스에 합산 Init Container 규칙 적용 (max 방식)

핵심 동작 원리

네이티브 사이드카의 라이프사이클은 다음과 같이 동작합니다:

  1. 시작 단계: Init Container 순서대로 실행되며, restartPolicy: Always가 설정된 컨테이너는 startup probe가 성공하면 다음 init container로 넘어갑니다.
  2. 실행 단계: 모든 init container 완료 후 메인 컨테이너가 시작됩니다. 이때 사이드카는 이미 실행 중입니다.
  3. 종료 단계: 메인 컨테이너가 종료되면, 사이드카에 SIGTERM이 전달됩니다. Init container의 역순으로 종료됩니다.
# 시작 순서 예시
initContainers[0] (restartPolicy: Always) → startup probe 성공
initContainers[1] (일반 init)             → 완료
initContainers[2] (restartPolicy: Always) → startup probe 성공
containers[0] (메인)                       → 시작

# 종료 순서
containers[0] (메인)                       → SIGTERM → 종료
initContainers[2] (사이드카)              → SIGTERM → 종료
initContainers[0] (사이드카)              → SIGTERM → 종료

실전 구성: Envoy 프록시 사이드카

서비스 메시 없이 Envoy를 사이드카로 붙이는 패턴입니다. 네이티브 사이드카를 사용하면 Envoy가 앱보다 먼저 시작되어 트래픽 유실을 방지합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    metadata:
      labels:
        app: api-server
    spec:
      initContainers:
        # 네이티브 사이드카: Envoy 프록시
        - name: envoy-proxy
          image: envoyproxy/envoy:v1.30-latest
          restartPolicy: Always
          ports:
            - containerPort: 9901
              name: admin
          startupProbe:
            httpGet:
              path: /ready
              port: 9901
            initialDelaySeconds: 2
            periodSeconds: 3
            failureThreshold: 10
          livenessProbe:
            httpGet:
              path: /ready
              port: 9901
            periodSeconds: 10
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
          volumeMounts:
            - name: envoy-config
              mountPath: /etc/envoy
        # 일반 init: DB 마이그레이션
        - name: db-migrate
          image: api-server:latest
          command: ["npm", "run", "migrate"]
      containers:
        - name: api
          image: api-server:latest
          ports:
            - containerPort: 3000
          env:
            - name: PROXY_URL
              value: "http://localhost:10000"
      volumes:
        - name: envoy-config
          configMap:
            name: envoy-config

실전 구성: 로그 수집 사이드카

Fluent Bit를 사이드카로 구성하여 앱 로그를 수집하는 패턴입니다. 네이티브 사이드카를 사용하면 앱 종료 후에도 남은 로그를 모두 전송한 뒤 종료됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: log-collector-demo
spec:
  initContainers:
    - name: fluent-bit
      image: fluent/fluent-bit:3.0
      restartPolicy: Always
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/app
        - name: fluent-config
          mountPath: /fluent-bit/etc
      startupProbe:
        httpGet:
          path: /api/v1/health
          port: 2020
        periodSeconds: 3
        failureThreshold: 10
      resources:
        requests:
          cpu: 50m
          memory: 64Mi
        limits:
          cpu: 200m
          memory: 128Mi
  containers:
    - name: app
      image: myapp:latest
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/app
      command:
        - sh
        - -c
        - |
          echo "앱 시작" >> /var/log/app/app.log
          # 앱 로직 실행
          sleep 30
          echo "앱 종료" >> /var/log/app/app.log
  terminationGracePeriodSeconds: 60
  volumes:
    - name: shared-logs
      emptyDir: {}
    - name: fluent-config
      configMap:
        name: fluent-bit-config

Job에서의 사이드카 문제 해결

기존 사이드카 패턴의 가장 큰 고통점은 Job/CronJob에서 사이드카가 종료되지 않는 문제였습니다. 메인 컨테이너가 완료되어도 사이드카(Istio proxy, CloudSQL proxy 등)가 계속 실행되어 Job이 영원히 완료되지 않았습니다.

# 기존 워크어라운드 (더 이상 불필요!)
# 메인 컨테이너에서 사이드카를 직접 종료하는 hack
command:
  - sh
  - -c
  - |
    npm run batch-job
    # Istio proxy 종료 hack
    curl -X POST http://localhost:15020/quitquitquit

# 네이티브 사이드카로 깔끔하게 해결
apiVersion: batch/v1
kind: Job
metadata:
  name: data-pipeline
spec:
  template:
    spec:
      initContainers:
        - name: cloudsql-proxy
          image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.11
          restartPolicy: Always
          args:
            - "--port=5432"
            - "project:region:instance"
          startupProbe:
            tcpSocket:
              port: 5432
            periodSeconds: 3
            failureThreshold: 10
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
      containers:
        - name: batch
          image: data-pipeline:latest
          command: ["python", "run_pipeline.py"]
          env:
            - name: DB_HOST
              value: "localhost"
            - name: DB_PORT
              value: "5432"
      restartPolicy: Never
  backoffLimit: 3

이제 batch 컨테이너가 완료되면 cloudsql-proxy 사이드카도 자동으로 종료되어 Job이 정상 완료됩니다.

리소스 계산 방식 변화

네이티브 사이드카는 Init Container로 선언되지만 Pod 전체 수명 동안 실행됩니다. 이에 따라 리소스 계산 방식이 중요합니다:

# Pod 리소스 계산 공식
# 기존: max(sum(initContainers), sum(containers))
# 변경: max(sum(regular_init_containers) + sum(sidecar_containers), sum(containers) + sum(sidecar_containers))

# 예시:
# initContainers:
#   - sidecar-a: 100m CPU (restartPolicy: Always)
#   - regular-init: 500m CPU
#   - sidecar-b: 200m CPU (restartPolicy: Always)
# containers:
#   - main: 1000m CPU

# Init 단계 최대: 500m + 100m + 200m = 800m (regular-init 실행 시)
# Run 단계: 1000m + 100m + 200m = 1300m
# Pod effective request: max(800m, 1300m) = 1300m

주의: 사이드카의 리소스는 메인 컨테이너와 합산되므로, 노드 리소스 계획 시 사이드카 리소스를 반드시 고려해야 합니다. K8s 리소스 Right-Sizing 실전 가이드를 참고하세요.

Startup Probe 설계 전략

네이티브 사이드카에서 startup probe는 단순 헬스체크가 아니라 다음 컨테이너의 시작 게이트 역할을 합니다. probe가 성공해야 다음 init container나 메인 컨테이너가 시작됩니다.

# ❌ 안티패턴: startup probe 없음
initContainers:
  - name: envoy
    restartPolicy: Always
    image: envoyproxy/envoy:v1.30
    # startup probe가 없으면 컨테이너 시작 즉시 "준비됨"으로 간주
    # → 메인 앱이 시작되었을 때 Envoy가 아직 설정 로딩 중일 수 있음

# ✅ 권장: startup probe로 실제 준비 상태 확인
initContainers:
  - name: envoy
    restartPolicy: Always
    image: envoyproxy/envoy:v1.30
    startupProbe:
      httpGet:
        path: /ready
        port: 9901
      initialDelaySeconds: 1
      periodSeconds: 2
      failureThreshold: 30  # 최대 60초 대기
      # successThreshold는 항상 1 (변경 불가)
    livenessProbe:
      httpGet:
        path: /ready
        port: 9901
      periodSeconds: 15
      failureThreshold: 3

Istio와의 호환성

Istio 1.22+에서는 네이티브 사이드카를 공식 지원합니다. istio-proxy가 네이티브 사이드카로 주입되어 앱보다 먼저 시작되고, 앱 종료 후 마지막에 종료됩니다.

# Istio 네이티브 사이드카 활성화
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    pilot:
      env:
        ENABLE_NATIVE_SIDECARS: "true"

# 적용 후 Pod 구조 (자동 주입)
# initContainers:
#   - name: istio-init        (일반 init: iptables 설정)
#   - name: istio-proxy       (restartPolicy: Always → 네이티브 사이드카)
# containers:
#   - name: app               (메인 앱)

이로써 기존의 holdApplicationUntilProxyStarts 설정이 불필요해지고, 종료 시 EXIT_ON_ZERO_ACTIVE_CONNECTIONS 핵도 필요 없어집니다.

운영 시 주의사항

1. 버전 요구사항

네이티브 사이드카는 Kubernetes 1.28에서 alpha로 도입되었고, 1.29에서 beta(기본 활성화)가 되었습니다. 1.28에서는 feature gate를 명시적으로 활성화해야 합니다:

# kube-apiserver, kube-scheduler, kubelet에 추가
--feature-gates=SidecarContainers=true

2. terminationGracePeriodSeconds 설계

사이드카는 메인 컨테이너 종료 후에 SIGTERM을 받습니다. 전체 terminationGracePeriodSeconds 안에 메인 + 사이드카 종료가 모두 완료되어야 합니다.

# 예: 메인 앱 30초 + 로그 수집 사이드카 15초 + Envoy 10초
# → terminationGracePeriodSeconds: 60 (여유 포함)
spec:
  terminationGracePeriodSeconds: 60

3. preStop Hook 활용

사이드카에 preStop hook을 설정하여 메인 컨테이너 종료 후 남은 작업(로그 전송, 커넥션 drain 등)을 처리할 수 있습니다:

initContainers:
  - name: fluent-bit
    restartPolicy: Always
    image: fluent/fluent-bit:3.0
    lifecycle:
      preStop:
        exec:
          command:
            - sh
            - -c
            - "sleep 5 && kill -SIGTERM 1"  # 남은 로그 전송 대기

4. 사이드카 크래시 시 동작

restartPolicy: Always이므로 사이드카가 크래시하면 자동 재시작됩니다. 하지만 메인 컨테이너에는 영향을 주지 않습니다. liveness probe 실패 시에도 사이드카만 재시작됩니다.

마이그레이션 체크리스트

기존 사이드카 패턴에서 네이티브 사이드카로 전환할 때 확인할 사항입니다:

체크 항목 조치
클러스터 버전 1.29+ 확인 kubectl version으로 확인
사이드카 컨테이너를 initContainers로 이동 restartPolicy: Always 추가
startup probe 추가 준비 상태 확인 엔드포인트 설정
사이드카 종료 hack 제거 quitquitquit, shareProcessNamespace
terminationGracePeriodSeconds 재계산 메인 + 사이드카 종료 시간 합산
리소스 request/limit 재계산 사이드카가 init에서 합산 → run에서 합산으로 변경
HPA 설정 확인 container metric 기준 HPA 시 사이드카 제외 확인

정리

Kubernetes 네이티브 사이드카는 오랫동안 커뮤니티가 요청해온 기능입니다. 시작/종료 순서 보장, Job 호환성, 그리고 다양한 핵 제거 등 운영 편의성이 크게 향상됩니다. 1.29+를 사용 중이라면 적극적으로 마이그레이션을 검토해보시기 바랍니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux