LabHub
배우기 러닝패스 코스

サイドカーとコンテナ構成パターン

サイドカーはタダではない

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

사이드카를 하나 붙이면 파드의 요청량이 더해지고, 기동 시간이 늘고, 실패 지점이 하나 생긴다. 그 대가를 넘는 이득이 있을 때만 붙인다.

フロー図: 파드의 요청량이 더해지고, 기동 시간이 늘고, 실패 지점이 하나 생긴다.・자원이 더해진다.・예약은 잡힌다.・기동이 느려진다.

왜 이게 필요했나

"기능을 하나 더 붙이자" 는 이유로 사이드카를 늘리면 어느 순간 파드가 컨테이너 다섯 개짜리가 된다. 그때 벌어지는 일은 이렇다.

자원이 더해진다. 파드의 요청량은 컨테이너들의 합이다. 사이드카에 requests.cpu: 100m 를 주면 파드 200개에 20코어가 예약된다. 스케줄러는 그 합을 보고 자리를 찾으므로 노드 밀도가 떨어진다. 실제로 쓰지 않아도 예약은 잡힌다.

기동이 느려진다. 정식 사이드카(restartPolicy: Always 인 init 컨테이너)는 본체보다 먼저 뜬다. 프록시가 뜨는 데 3초가 걸리면 모든 파드의 기동이 3초 늘어난다. 롤링 업데이트에서 그게 파드 수만큼 곱해진다.

실패 지점이 는다. 사이드카가 OOM 으로 죽으면 그 컨테이너가 재시작되는 동안 파드는 준비 상태를 잃고, 반복되면 CrashLoopBackOff 로 파드 전체를 못 쓰게 된다. 로그 수집기가 버퍼를 메모리에 쌓는 설정이면 트래픽이 몰릴 때 정확히 그 일이 난다.

어떻게 동작하나

자원을 정할 때의 기준은 단순하다.

컨테이너 requests limits
본체 실제 사용량의 p50 p99 + 여유
사이드카 아주 작게(10~50m) 넉넉하게

사이드카의 limits 를 조이면 본체까지 같이 죽는다. requests 는 작게, limits 는 넉넉하게가 정답이다. 이건 본체와 반대 방향이라 헷갈리기 쉽다.

종료 순서도 중요하다. 본체가 아직 요청을 처리하는 중에 프록시가 먼저 죽으면 그 요청들이 실패한다. 정식 사이드카는 본체가 끝난 뒤 종료되므로 이 문제가 해결됐지만, 그 전에는 preStop 훅으로 몇 초 버티게 하는 우회가 흔했다.

흔한 착각

"사이드카가 죽어도 본체는 산다" — 파드는 하나의 재시작 단위다. restartPolicy 는 파드 수준이고, 컨테이너 하나가 계속 죽으면 파드 전체가 CrashLoopBackOff 로 간다.

"자원을 안 주면 안 쓴다" — requests 를 안 주면 QoS 가 BestEffort 가 되어 메모리 압박 시 가장 먼저 죽는다. 사이드카가 먼저 죽으면 본체도 함께 간다.

시작과 종료 순서 문제

사이드카의 오래된 골칫거리는 순서 였습니다. 메인 컨테이너가 먼저 떠 사이드카 (프록시)가 아직 없으면 첫 요청이 실패하고, 끝날 때는 사이드카가 먼저 죽어 마지막 요청과 로그가 사라집니다.

❌ 옛 동작
   시작: 모든 컨테이너 동시 → 프록시가 늦으면 초기 요청 실패
   종료: 모든 컨테이너 동시 → 프록시가 먼저 죽으면 하류 호출 실패

✅ 네이티브 사이드카 (k8s 1.29+)
   initContainers 에 restartPolicy: Always 를 주면
   시작: 사이드카가 Ready 가 된 뒤에 메인이 시작
   종료: 메인이 끝난 뒤에 사이드카가 종료
spec:
  initContainers:
    - name: proxy
      image: envoyproxy/envoy:v1.31
      restartPolicy: Always        # ← 이 한 줄이 네이티브 사이드카로 만든다
  containers:
    - name: app
      image: myapp:1.0

이전에는 애플리케이션이 프록시를 기다리는 코드를 넣거나 preStopsleep 을 두는 우회로를 썼습니다. 지금은 필요 없습니다. 클러스터 버전을 먼저 확인 하고 쓰는 것이 이 기능의 유일한 조건입니다.

Job 이 끝나지 않는 문제

배치 작업 파드에 사이드카가 있으면 메인이 끝나도 사이드카가 계속 돌아 Job 이 영원히 Running 입니다. CronJob 이 매일 쌓이는 사고가 여기서 납니다.

네이티브 사이드카는 이것도 해결합니다 — 메인이 끝나면 쿠버네티스가 사이드카에 SIGTERM 을 보냅니다. 1.29 이전이라면 메인이 끝날 때 사이드카에 신호를 보내는 스크립트를 직접 넣어야 합니다.

자원을 두 배로 세지 않기

사이드카마다 요청·한도를 넉넉히 잡으면 파드 하나가 실제 필요의 두 배 를 예약합니다. 노드에 절반만 들어갑니다.

사이드카 흔한 요청 실제 필요
로그 수집기 100m / 128Mi 대개 20m / 64Mi
프록시(Envoy) 500m / 512Mi 트래픽에 따라 50~200m
지표 익스포터 100m / 128Mi 10m / 32Mi

실측하고 줄입니다. 파드 수백 개에 100m 씩 과하게 잡으면 코어 수십 개가 놀고 있습니다. kubectl top pod --containers 로 컨테이너별 실사용을 봅니다.

실무에서 진짜 중요한 것

사이드카를 넣기 전에 물어야 할 것은 하나다 — DaemonSet 으로 안 되나?

노드마다 하나면 되는 일(로그 수집, 노드 지표)에 파드마다 컨테이너를 붙이면 프로세스 수가 파드 수만큼 늘어난다. 사이드카가 정당한 경우는 세 가지뿐이다.

  1. 파드의 네트워크 네임스페이스가 필요하다(프록시, mTLS)
  2. 파드의 볼륨이 필요하다(특정 경로의 파일)
  3. 테넌트별로 격리해야 한다(설정이 파드마다 다르다)

셋 중 어디에도 안 걸리면 DaemonSet 이 싸다.