본문 바로가기
1. K8s Core & Architecture/1.3. 파드(Pod)와 워크로드 아키텍처

사이드카 컨테이너 (Sidecar Containers) 기본 지원 (v1.28+) 아키텍처 분석

by K8s Architect 2026. 4. 8.

사이드카 컨테이너 (Sidecar Containers) 기본 지원 (v1.28+) 아키텍처 분석

쿠버네티스 인프라 생태계에서 가장 오랫동안 해결되지 않았던 아키텍처의 난제 중 하나가 바로 '사이드카(Sidecar) 생명주기 제어'였습니다. 서비스 메시(Istio, Linkerd)의 네트워크 프록시나 로깅 수집기(Fluent Bit)를 구현하기 위해 사이드카 패턴은 필수적이었지만, 쿠버네티스 엔진 자체는 어떤 컨테이너가 비즈니스를 수행하는 메인 컨테이너인지, 어떤 컨테이너가 보조 역할을 하는 사이드카인지 문맥적으로 구분하지 못했습니다.

이로 인해 발생했던 수많은 운영상의 고통을 근본적으로 해결하기 위해, 쿠버네티스 v1.28부터 네이티브 사이드카 컨테이너(Native Sidecar Containers) 기능이 도입되었습니다. 이 글에서는 기존 사이드카 패턴이 안고 있던 근본적인 한계와 새로운 네이티브 사이드카의 내부 동작 아키텍처, 그리고 파드 리소스 산정 방식의 변화를 상세히 분석합니다.


1. 레거시 사이드카 패턴의 치명적 한계

과거에는 메인 애플리케이션과 사이드카를 단순히 동일한 containers 배열 안에 병렬로 선언하여 띄우는 방식을 사용했습니다. 이 방식은 Kubelet의 관점에서 두 컨테이너를 완전히 동등하게 취급하게 만들었으며, 크게 두 가지 치명적인 라이프사이클(Lifecycle) 충돌을 발생시켰습니다.

  • 시작 순서(Startup Order) 제어 불가: Kubelet은 containers 내부의 컨테이너들을 거의 동시에 병렬로 띄웁니다. 만약 메인 애플리케이션이 네트워크 프록시(Envoy)보다 1초라도 먼저 켜질 경우, 메인 앱이 외부 데이터베이스에 연결을 시도하지만 프록시가 통신을 낚아챌 준비가 되지 않아 연결 에러(Connection Refused)를 뿜어내며 크래시 루프(CrashLoopBackOff)에 빠지는 장애가 잦았습니다.
  • 종료 순서(Shutdown Order)와 좀비 파드 현상: 이 문제는 지속해서 실행되는 웹 서버보다, 작업이 끝나면 종료되어야 하는 Job이나 CronJob 컨트롤러 환경에서 최악의 부작용을 일으켰습니다. 메인 배치(Batch) 작업이 성공적으로 끝났음에도 불구하고, 옆에 있던 로깅 사이드카는 자신이 언제 종료되어야 하는지 알 수 없어 무한히 대기했습니다. 결과적으로 파드가 절대 Completed 상태로 넘어가지 못하는 '좀비 파드' 현상이 발생했습니다. 이를 해결하기 위해 공유 볼륨에 특정 더미(Dummy) 파일을 생성하고, 사이드카가 이를 감지하면 스스로 셸 스크립트를 통해 종료되게 만드는 기형적인 우회(Workaround) 로직이 필수적이었습니다.

2. v1.28의 혁신: initContainers의 재발견

쿠버네티스 개발진(SIG-Node)은 완전히 새로운 필드를 명세서에 추가하는 대신, 파드의 탄생 시퀀스를 담당하는 initContainers 아키텍처를 영리하게 확장하는 방식을 채택했습니다.

네이티브 사이드카를 선언하는 방법은 매우 직관적입니다. 사이드카 컨테이너를 initContainers 배열 안에 넣고, 컨테이너 스펙에 restartPolicy: Always라는 속성을 부여하기만 하면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: native-sidecar-pod
spec:
  initContainers:
  - name: istio-proxy
    image: proxy-image:latest
    restartPolicy: Always  # 이 속성이 컨테이너를 '네이티브 사이드카'로 승격시킵니다.
    startupProbe:
      tcpSocket:
        port: 15000
  containers:
  - name: main-application
    image: app-image:latest

3. 네이티브 사이드카의 생명주기 상태 머신

Kubelet은 파드를 생성할 때 restartPolicy: Always가 선언된 초기화 컨테이너를 발견하면, 이를 일반적인 일회성(Run-to-completion) 초기화 작업이 아닌 '영구적인 백그라운드 사이드카'로 인식하고 완전히 다른 생명주기 트리를 적용합니다.

3.1. 초기화 단계 (Startup Phase)

Kubelet은 명세서에 적힌 순서대로 initContainers를 훑습니다. 네이티브 사이드카를 실행한 뒤, 곧바로 다음 컨테이너로 넘어가지 않습니다. 대신 사이드카 컨테이너의 startupProbereadinessProbe가 성공(Ready 상태)할 때까지 Kubelet은 대기합니다.
네트워크 프록시나 로깅 에이전트가 완벽하게 구동된 것을 시스템적으로 확인한 후에야 비로소 다음 초기화 컨테이너나 메인 containers를 구동하기 시작합니다. 고질적인 시작 순서 꼬임 현상이 프레임워크 레벨에서 완벽하게 해결된 것입니다.

3.2. 실행 단계 (Running Phase)

일반적인 초기화 컨테이너는 자신의 셸 스크립트 실행을 마치면 상태 코드를 반환하고 종료(Terminated)되어야 합니다. 하지만 네이티브 사이드카는 메인 애플리케이션과 함께 파드의 생명주기 내내 살아서 백그라운드로 실행됩니다. 만약 런타임 중간에 사이드카가 OOM 등으로 죽게 되면, 파드 전체를 죽이지 않고 설정된 정책(Always)에 따라 Kubelet이 해당 사이드카만 즉시 다시 살려냅니다.

3.3. 종료 단계 (Termination Phase)

가장 혁신적인 변화를 가져온 지점입니다. Kubelet은 파드 종료(방출 또는 삭제) 명령을 받거나 Job이 완료되었을 때, 메인 containers 배열에 있는 비즈니스 애플리케이션들에게 먼저 SIGTERM을 보내 안전하게 종료시킵니다.
모든 메인 컨테이너가 성공적으로 종료된 것을 Kubelet이 확인한 직후, 알아서 네이티브 사이드카들에게 마지막으로 종료 시그널을 보내 파드를 완전히 소멸시킵니다. 이를 통해 Job 컨트롤러 환경에서 애플리케이션의 찌꺼기 로그가 전송되기도 전에 로깅 사이드카가 먼저 죽어버리거나, 반대로 영원히 살아남아 리소스를 낭비하는 문제를 근본적으로 타파했습니다.


4. 파드 자원 산정(Resource Calculation) 로직의 변화

네이티브 사이드카 기능이 편입되면서, 쿠버네티스 스케줄러(kube-scheduler)가 특정 파드를 노드에 배치하기 위해 전체 리소스(Requests/Limits)를 계산하는 공식도 더욱 정교하게 진화했습니다.

과거에는 파드의 스케줄링 리소스를 MAX(모든 일반 Init 컨테이너의 요구량) vs SUM(모든 메인 컨테이너의 요구량) 중 큰 값으로 산정했습니다.
하지만 네이티브 사이드카는 메인 컨테이너와 '동시'에 영원히 실행되므로, 새로운 산정 공식은 사이드카의 자원을 파드 베이스라인에 영구적으로 합산하게 됩니다.

  • 새로운 스케줄링 요구량 계산 공식:
    1. 초기화 구간 최대 리소스 = MAX(일반 Init 컨테이너) + SUM(네이티브 사이드카)
    2. 실행 구간 전체 리소스 = SUM(메인 컨테이너) + SUM(네이티브 사이드카)
    3. 최종적으로 1번과 2번 중 더 큰 값이 파드의 노드 스케줄링 예약 자원으로 결정됩니다.

이를 통해 사이드카가 상시 점유하는 리소스가 스케줄러에 정확하게 보고되어, 노드의 메모리가 꽉 차는 OOM 현상을 파드 스케줄링 단계에서 더욱 안전하고 꼼꼼하게 차단할 수 있습니다.


5. 프로덕션 마이그레이션 시 핵심 고려사항

해당 기능은 v1.28에서 알파/베타를 거쳐 v1.29 버전부터 공식적으로 기본 활성화(GA 수준) 되었습니다. 프로덕션 환경의 인프라스트럭처 애즈 코드(IaC) 파이프라인에 이 명세서를 도입하기 전, 다음 사항을 반드시 검증해야 합니다.

  • 버전 파편화 주의: 클러스터의 컨트롤 플레인과 모든 워커 노드의 Kubelet 버전이 최소 1.28 이상이어야 합니다. 만약 하위 버전의 Kubelet이 동작하는 노드에 이 파드가 스케줄링될 경우, 구버전 Kubelet은 restartPolicy: Always 필드를 이해하지 못하고 무시해 버립니다. 이 경우 해당 컨테이너를 일반적인 일회성 initContainer로 취급하게 되며, 네트워크 프록시처럼 무한 루프를 도는 사이드카는 영원히 종료되지 않아 메인 앱이 영영 켜지지 않는 치명적인 배포 장애가 발생하게 됩니다.
  • 활성 프로브 필수 탑재: 네이티브 사이드카의 시작 대기 로직은 완벽하게 프로브(Probe)에 의존합니다. readinessProbestartupProbe를 누락하면 Kubelet은 컨테이너 프로세스가 켜진 직후 1밀리초 만에 바로 다음 컨테이너를 구동시켜 버려 시작 순서 동기화의 이점을 잃게 됩니다. 반드시 정확한 헬스체크 엔드포인트를 프로브 명세에 포함해야 합니다.

결론적으로 v1.28의 네이티브 사이드카 지원은 쿠버네티스 워크로드 아키텍처의 오랜 숙원을 우아하게 풀어낸 기념비적인 진화입니다. 인프라 엔지니어는 앱의 시작과 종료 타이밍을 억지로 맞추기 위해 작성하던 복잡한 셸 스크립트를 완전히 걷어내고, 쿠버네티스의 네이티브한 통제 로직에 생명주기를 온전히 맡길 수 있게 되었습니다.