~/infra-dev.md
INFRA_DEV

사이드카는 왜 보조 컨테이너가 아니라 Pod 생명주기 계약일까

10분 읽기·2026.07.30·출처 4·00
오늘의 질문

로그 수집기, 프록시, 보안 에이전트를 앱 옆에 붙일 때 우리는 컨테이너 하나 추가만 생각하는가, 아니면 시작 순서·준비 상태·종료 순서·로그 소유권까지 계약으로 관리하는가?

infra-dev.md
Pod sidecar lifecycle

사이드카는 왜 보조 컨테이너가 아니라 Pod 생명주기 계약일까

1. 왜 지금 봐야 하나

Kubernetes v1.33 릴리스 글은 Sidecar Containers가 stable로 승격됐다고 설명한다. 여기서 말하는 native sidecar는 단순히 “Pod 안에 컨테이너를 두 개 넣는다”가 아니다. Kubernetes가 sidecar를 initContainers 안의 특별한 restartable 컨테이너로 모델링한다는 점이 핵심이다. restartPolicy: Always를 가진 sidecar는 앱 컨테이너보다 먼저 시작되고, Pod가 살아 있는 동안 계속 실행되며, 앱 컨테이너가 끝난 뒤 자동으로 종료된다.

이 변화가 인프라 개발자에게 중요한 이유는 사이드카 패턴이 이미 운영의 많은 부분을 떠받치고 있기 때문이다.

  • 로그 shipper나 OpenTelemetry Collector가 앱 로그와 trace를 외부로 보낸다.
  • service mesh proxy가 네트워크 정책, mTLS, retry, timeout을 대신 처리한다.
  • 보안 에이전트가 runtime event를 관찰한다.
  • 파일 동기화 sidecar가 config나 cert를 갱신한다.

예전에도 일반 컨테이너를 옆에 붙여 sidecar처럼 쓸 수 있었다. 하지만 Kubernetes 입장에서는 모든 일반 컨테이너가 “같이 시작해서 같이 살아야 하는 앱 컨테이너”에 가까웠다. 그러면 시작 순서, readiness, Job 완료 조건, 종료 순서가 애매해진다. 특히 batch Job에서 main container는 끝났는데 로그 수집 sidecar가 계속 살아 있어 Job이 완료되지 않는 식의 문제가 생긴다.

오늘의 핵심은 이것이다.

사이드카는 보조 기능이 아니라, 애플리케이션과 운영 파이프라인 사이의 생명주기 계약이다.

최근 infra_dev 글은 Kubernetes HPA/custom metrics, etcd control plane 보안, CloudNativePG Operator 계약, GKE rollout sequencing을 다뤘다. 오늘은 오토스케일링이나 DB 운영이 아니라, Pod 하나 안에서 앱과 운영 보조 프로세스의 시작·준비·종료·로그 경계를 어떻게 선언해야 하는가를 본다.

2. 핵심 개념

일반 sidecar와 native sidecar는 무엇이 다른가

Pod에는 여러 컨테이너를 넣을 수 있다. 전통적인 방식에서는 containers 배열에 앱 컨테이너와 보조 컨테이너를 나란히 적었다. 예를 들어 app, log-forwarder, mesh-proxy가 모두 일반 컨테이너다. Kubernetes는 이들을 같은 Pod 생명주기 안의 동료 컨테이너로 본다.

native sidecar는 다르게 모델링한다. Kubernetes 문서는 sidecar container를 init container의 특별한 경우로 설명한다. 즉 initContainers에 들어가지만, 일반 init container처럼 “성공하고 끝나야 다음 단계로 넘어가는” 컨테이너가 아니다. restartPolicy: Always를 지정하면 kubelet은 이 컨테이너를 restartable sidecar로 다룬다.

이 구조가 주는 의미는 세 가지다.

  1. 시작 순서가 명확하다. sidecar가 먼저 시작된다. sidecar에 startup probe가 있으면 그 probe가 성공한 뒤 다음 init container 또는 app container 시작으로 넘어갈 수 있다.
  2. Pod 실행 중 계속 살아 있다. 일반 init container는 완료되면 끝나지만, restartable sidecar는 Pod가 실행되는 동안 계속 재시작될 수 있다.
  3. 종료 의미가 달라진다. main app container가 종료된 뒤 sidecar는 자동 종료 대상이 된다. 그래서 Job 완료를 sidecar가 불필요하게 붙잡는 문제를 줄일 수 있다.

init container와 sidecar를 헷갈리면 운영 버그가 생긴다

Kubernetes init container 문서는 init container의 기본 규칙을 분명히 한다.

  • init container는 항상 run to completion이다.
  • 각 init container는 성공적으로 끝나야 다음 init container가 시작된다.
  • init container가 실패하면 kubelet은 성공할 때까지 재시작한다. 단 Pod restartPolicy: Never에서는 Pod 전체가 실패할 수 있다.

반면 native sidecar는 “먼저 시작하지만 끝나지 않아야 하는” 컨테이너다. 이 차이를 모르면 다음과 같은 설계 오류가 생긴다.

  • DB migration을 sidecar처럼 계속 실행되게 둬서 Pod 종료를 방해한다.
  • 로그 shipper를 일반 init container로 둬서 앱 실행 전에 끝나 버린다.
  • service mesh proxy를 일반 컨테이너로만 보고 app readiness와 proxy readiness의 관계를 정의하지 않는다.
  • batch Job에서 sidecar가 완료 조건을 붙잡아 queue worker가 끝나도 Job이 완료되지 않는다.

정리하면 이렇다.

zsh — 생존확인.sh
init container: 앱 시작 전에 한 번 끝나야 하는 준비 작업
native sidecar: 앱보다 먼저 시작하고 앱 동안 계속 살아야 하는 운영 보조 작업
app container: 실제 비즈니스 로직 또는 batch 본체

로그와 관측성은 sidecar의 대표 사용 사례지만, 소유권은 분리해야 한다

Kubernetes Logging Architecture 문서는 가장 기본적인 컨테이너 로그 방식이 stdout/stderr에 쓰는 것이라고 설명한다. 하지만 컨테이너 runtime의 기본 로그 기능만으로는 완전한 로깅 솔루션이 부족하고, Pod가 evict되거나 노드가 죽어도 로그를 보려면 node, Pod, container와 독립된 저장·분석 backend가 필요하다고 말한다.

그래서 sidecar log agent를 붙이고 싶은 유혹이 생긴다. 앱이 파일에 로그를 쓰고, sidecar가 그 파일을 읽어 외부 backend로 보내는 구조다. 이 구조는 편할 수 있지만 운영 소유권이 흐려지기 쉽다.

  • 앱은 어떤 파일 경로와 rotate 정책을 보장하는가?
  • sidecar는 로그를 어디까지 읽고 offset을 어디에 저장하는가?
  • sidecar가 죽으면 앱도 NotReady가 되어야 하는가, 아니면 로그 유실 위험만 알림으로 남겨야 하는가?
  • sidecar가 CPU를 과하게 쓰면 앱 QoS에 어떤 영향을 주는가?
  • stdout/stderr로 충분한데 파일 공유 volume을 만들고 있지는 않은가?

로그 sidecar를 쓰는 순간, “로그는 앱의 부수 효과”가 아니라 “운영 파이프라인의 입력 데이터”가 된다. 따라서 sidecar의 readiness, resource request, backpressure, drop 정책을 앱 배포 조건과 연결해야 한다.

3. 최신 이슈와 연결

Kubernetes v1.33 release note의 sidecar 설명은 몇 가지 중요한 운영 힌트를 준다.

첫째, sidecar는 restartPolicy: Always를 가진 init container로 구현된다. 이 말은 manifest 위치가 단순 취향이 아니라 lifecycle 의미를 바꾼다는 뜻이다. 같은 image라도 containers에 넣으면 일반 앱 컨테이너이고, initContainers에 넣고 restart policy를 지정하면 restartable sidecar다.

둘째, sidecar는 startup, readiness, liveness probe를 사용할 수 있다. 이는 보조 컨테이너도 “그냥 있으면 되는 프로세스”가 아니라, 운영 상태를 드러내야 하는 대상이라는 뜻이다. 예를 들어 mesh proxy가 준비되기 전에 앱이 외부 호출을 시작하면 첫 요청이 실패할 수 있다. 반대로 log shipper가 잠깐 느리다고 앱 트래픽까지 막으면 가용성이 불필요하게 떨어질 수 있다. 어떤 sidecar가 Pod readiness를 좌우해야 하는지는 기능마다 다르게 결정해야 한다.

셋째, OOM score 조정이 primary container와 정렬된다는 설명도 중요하다. 사이드카는 작은 보조 프로세스처럼 보이지만, 실제로는 같은 Pod의 CPU·메모리 envelope를 공유한다. 관측성 agent가 메모리를 급격히 쓰거나, proxy가 연결 폭증을 받아 버퍼를 키우면 앱 컨테이너가 압박을 받을 수 있다. native lifecycle만으로는 resource isolation이 자동 해결되지 않는다.

넷째, 문서의 logging architecture와 연결하면 sidecar는 “로그 수집을 가능하게 하는 도구”일 뿐, 로그 저장의 최종 책임자가 아니다. Kubernetes는 로그 데이터용 native storage solution을 제공하지 않는다. cluster-level logging은 별도 backend와 lifecycle을 가져야 한다. 따라서 sidecar를 붙였다고 로깅 설계가 끝난 것이 아니라, node failure와 pod eviction 이후에도 조회 가능한지까지 확인해야 한다.

4. 개발자 관점 해석

1) Sidecar는 애플리케이션 의존성인가, 운영 의존성인가

모든 sidecar를 같은 방식으로 다루면 안 된다. 먼저 sidecar의 실패가 앱의 실패인지 구분해야 한다.

  • mesh proxy: 트래픽 경로 자체이므로 준비되지 않으면 앱도 준비되지 않은 것으로 보는 경우가 많다.
  • log forwarder: 로그 유실은 심각하지만 사용자 요청을 즉시 막아야 하는지는 서비스별 판단이다.
  • secret/cert sync: 갱신 실패가 곧 장애인지, 만료 시점까지 유예가 있는지 봐야 한다.
  • security agent: 규정 준수상 필수면 readiness에 묶을 수 있지만, 에이전트 장애가 전체 서비스 장애로 번지는 위험도 봐야 한다.

질문은 단순하다.

zsh — 생존확인.sh
이 sidecar가 죽었을 때 앱은 요청을 계속 받아야 하는가?

이 질문에 답하지 않고 sidecar를 붙이면, 장애 때 readiness probe와 alert가 서로 다른 말을 하게 된다.

2) 시작 순서는 배포 안정성의 일부다

많은 장애는 앱 코드가 틀려서가 아니라, 앱이 의존하는 주변 프로세스보다 먼저 움직여서 생긴다. 예를 들어 앱은 localhost proxy로만 외부 API를 호출하도록 설정돼 있는데, proxy 준비 전에 앱이 bootstrap call을 보내면 실패한다. 이런 경우 sidecar startup probe를 통해 “proxy가 최소 기능을 수행할 수 있는가”를 확인한 뒤 앱을 시작해야 한다.

반대로 모든 sidecar 준비를 앱 시작 조건으로 묶으면 배포가 느려지고 약해진다. 로그 backend가 잠깐 느려졌다는 이유로 앱 rollout 전체가 막히면 사용자는 새 버전을 받지 못한다. 따라서 sidecar마다 startup gate, readiness gate, liveness restart의 의미를 나눠야 한다.

3) Job과 worker에서는 종료 순서가 더 중요하다

웹 서버에서는 sidecar가 오래 살아도 문제가 덜 보일 수 있다. 하지만 batch Job과 queue worker에서는 종료 의미가 훨씬 중요하다. main container가 “처리 완료”로 끝났는데 sidecar가 계속 실행 중이면 Job controller가 완료를 보지 못하거나, 반대로 sidecar가 너무 빨리 죽어서 마지막 로그·trace flush가 유실될 수 있다.

native sidecar는 이 문제를 줄이기 위해 등장한 모델이다. 그러나 flush timeout, preStop hook, terminationGracePeriodSeconds, queue ack 시점은 여전히 애플리케이션 설계에 남아 있다. 특히 “작업 성공을 기록한 뒤 로그가 flush된다”와 “로그가 flush된 뒤 작업 성공을 기록한다”는 장애 복구 의미가 다르다.

4) 리소스 요청값은 sidecar 비용의 계약서다

sidecar를 붙이면 Pod 단위 비용이 오른다. 작은 proxy 100MiB, collector 200MiB는 한 Pod에서는 작아도 replica 200개면 큰 비용이다. 더 위험한 것은 request를 낮게 잡아 scheduling은 잘 되지만 peak에서 앱 컨테이너와 경쟁하는 경우다.

sidecar resource request/limit은 “대충 낮게”가 아니라 다음 기준으로 잡아야 한다.

  • 평상시 CPU·메모리 사용량
  • 트래픽 burst 때 buffer 증가량
  • 로그/trace sampling rate 변화
  • downstream backend 지연 시 queueing 정책
  • 앱 컨테이너와 같은 Pod에 묶였을 때 OOM 우선순위

5. 내 프로젝트에 적용할 체크포인트

Sidecar inventory

  • 모든 Deployment/Job에서 컨테이너가 2개 이상인 Pod 목록을 뽑는다.
  • 각 보조 컨테이너가 일반 app container인지 native sidecar인지 구분한다.
  • initContainers에 있으면서 run to completion이어야 하는 작업과 계속 살아야 하는 작업을 분리한다.
  • sidecar owner를 앱 팀, 플랫폼 팀, 보안 팀 중 누구로 볼지 적는다.

시작·준비 계약

  • sidecar startup probe가 필요한가?
  • sidecar readiness가 Pod readiness에 영향을 줘야 하는가?
  • 앱이 sidecar 준비 전 localhost endpoint, socket, shared volume을 사용하지 않는가?
  • sidecar liveness restart가 앱 요청 중단을 만들 수 있는가?

종료·Job 계약

  • batch Job에서 main container 종료 후 sidecar가 완료를 붙잡지 않는가?
  • 마지막 로그, trace, metric flush에 필요한 grace period가 있는가?
  • queue ack 또는 DB commit 시점과 관측 데이터 flush 시점의 순서를 설명할 수 있는가?
  • sidecar 종료 실패가 Job retry를 유발해야 하는가, 별도 alert만 남겨야 하는가?

로그·관측성 계약

  • 앱은 가능하면 stdout/stderr에 구조화 로그를 쓰는가?
  • 파일 기반 sidecar라면 shared volume, rotate, offset, 중복 전송 정책이 있는가?
  • Pod/노드 삭제 이후에도 로그를 조회할 backend가 있는가?
  • log/trace backend 장애 때 sidecar가 메모리를 무한히 쓰지 않도록 queue limit/drop 정책이 있는가?

비용·리소스 계약

  • sidecar별 CPU/memory request와 limit가 실제 p95 사용량과 맞는가?
  • sidecar 추가로 replica당 비용이 얼마나 늘었는가?
  • HPA 기준 metric이 앱 부하인지 sidecar 부하인지 구분되는가?
  • sidecar가 DB connection, outbound connection, file descriptor를 추가로 얼마나 쓰는가?

6. 오늘 10분 액션

오늘은 코드를 고치지 않아도 된다. 운영 중인 Kubernetes repo에서 아래 표 하나만 만들어 보자.

zsh — 생존확인.sh
워크로드: payment-worker
보조 컨테이너: otel-collector
현재 위치: containers / initContainers / native sidecar?
앱 의존성 여부: collector 장애 시 요청 처리 계속 가능
시작 조건: collector health endpoint 준비 전 앱 시작 허용 여부
준비 조건: collector NotReady가 Pod NotReady로 이어지는가?
종료 조건: main 종료 후 trace flush 10초 필요
리소스: request 100m/256Mi, limit 500m/512Mi
위험: backend 지연 시 collector queue 증가로 앱 메모리 압박
다음 액션: readiness 영향과 queue limit을 플랫폼 팀과 리뷰

이 표의 목적은 sidecar를 없애는 것이 아니다. “우리가 앱 옆에 붙인 운영 프로세스가 어떤 생명주기 계약으로 움직이는지”를 팀이 설명할 수 있게 만드는 것이다.

7. 더 볼 자료

중복 회피 메모

로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 글은 CloudNativePG Operator의 DB 운영 계약, Kubernetes custom metrics 기반 HPA/backpressure, etcd control plane 보안 패치, PostgreSQL logical replication WAL 보존, GKE rollout sequencing, OpenTelemetry Collector 운영, Kubernetes Indexed Job 실패 예산을 다뤘다. 이번 글은 Kubernetes와 관측성을 다시 언급하지만 오토스케일링·컨트롤 플레인·DB Operator·Collector 파이프라인 자체가 아니라, Kubernetes v1.33에서 stable이 된 native sidecar를 계기로 Pod 내부 보조 컨테이너의 시작 순서, readiness, Job 종료, 로그 flush, 리소스 비용을 생명주기 계약으로 설계하는 법에 집중해 중복을 피했다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)