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 방식) |
핵심 동작 원리
네이티브 사이드카의 라이프사이클은 다음과 같이 동작합니다:
- 시작 단계: Init Container 순서대로 실행되며,
restartPolicy: Always가 설정된 컨테이너는 startup probe가 성공하면 다음 init container로 넘어갑니다. - 실행 단계: 모든 init container 완료 후 메인 컨테이너가 시작됩니다. 이때 사이드카는 이미 실행 중입니다.
- 종료 단계: 메인 컨테이너가 종료되면, 사이드카에 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+를 사용 중이라면 적극적으로 마이그레이션을 검토해보시기 바랍니다.