기준: Kubernetes v1.35 (kind
kindest/node:v1.35.8에서 명령어·YAML 확인)
목차
- Pod 안의 container 종류
- init container
- sidecar
- ambassador와 adapter
- ephemeral container
- 예시
- 헷갈리는 것 비교
- 시험 포인트
- 참고 문서
Pod 안의 container 종류
| 종류 | 정의 위치 | 언제 실행 | 재시작 | probe |
|---|---|---|---|---|
| app container | spec.containers | init 단계가 끝난 뒤, 병렬 | Pod restartPolicy | 지원 |
| init container | spec.initContainers | app보다 먼저, 순서대로, 끝까지 | 실패 시 재시도 (아래 참고) | 불가 |
| sidecar (native) | spec.initContainers + restartPolicy: Always | 정의된 순서대로 시작 후 계속 | 항상 | 지원 |
| ephemeral container | ephemeralcontainers subresource | 사용자가 추가할 때 | 안 함 | 불가 |
공유하는 것
- network: Pod IP 하나를 같이 씀. container끼리
localhost:<port>로 통신, 대신 포트가 겹치면 안 됨 - storage:
spec.volumes에 정의한 volume을 각 container가volumeMounts로 mount. 주고받는 파일은 보통emptyDir - IPC: SystemV semaphore, POSIX shared memory도 사용 가능
- process namespace는 기본으로 분리. 필요하면 process namespace sharing을 켬
init container
Pod 시작 전에 준비 작업을 하는 container. 문서가 드는 용도는 Service가 생길 때까지 기다리기, Git repository를 volume에 clone, 설정 파일 템플릿 렌더링 같은 것들.
- app 이미지와 별도 이미지라서
sed,dig같은 도구를 app 이미지에 넣을 필요 없음 - app container가 못 보는 Secret에 접근하게 할 수 있음
- 조건이 맞을 때까지 app 시작을 막는 용도로 씀
동작 방식
- kubelet은 network와 storage가 준비된 뒤 init container를 spec에 적힌 순서대로 실행
- 각 init container는 성공(exit 0)해야 다음 것이 시작됨
- 전부 끝나면 app container들을 시작
실패하면:
- Pod
restartPolicy가Never: Pod 전체가 실패 처리 OnFailure: 해당 init container를 재시도Always: init container에는OnFailure로 적용됨
그 밖의 규칙:
- init이 끝나기 전 Pod phase는
Pending, conditionInitialized는 false.kubectl getSTATUS에는Init:0/2형태로 표시 - 일반 init container는
lifecycle,livenessProbe,readinessProbe,startupProbe를 쓸 수 없음. 넣으면 API server가Forbidden: may not be set for init containers without restartPolicy=Always로 거부 - 다시 실행될 수 있으므로 idempotent하게 작성.
emptyDir에 쓸 파일이 이미 있을 수 있음 - 실행 중인 Pod에서 init container는
image만 바꿀 수 있고, 바꿔도 Pod가 재시작되지 않음 - container 이름은 init·app 전체에서 유일해야 함
- init container의 포트는 Service에 묶이지 않음
리소스 계산
- init container들 중 가장 큰 request/limit = effective init request/limit
- Pod의 effective request/limit = max(app·sidecar container 합, effective init 값) + pod overhead
- 스케줄링은 이 effective 값 기준이라, init이 크면 초기화 때만 쓰는 리소스도 예약됨
sidecar
메인 앱 옆에서 로그 수집, 모니터링, 데이터 동기화 같은 보조 기능을 하는 container. 앱 코드를 고치지 않고 기능을 붙이는 게 목적입니다.
native sidecar
SidecarContainers feature gate(v1.29부터 기본 활성화)로 생긴 방식. initContainers 항목에 restartPolicy: Always를 주면 sidecar가 됩니다.
- init container처럼 순서가 보장됨. sidecar가
started상태가 되면 다음 init container가 시작.startupProbe가 있으면 그게 성공해야started - 시작 후 Pod가 끝날 때까지 계속 실행. 종료되면 Pod
restartPolicy와 상관없이 재시작 - probe 지원.
readinessProbe결과가 Pod ready 판정에 들어감 - Pod 종료 시: 메인 container가 완전히 끝난 뒤에야 sidecar에 TERM을 보내고, 정의된 순서의 역순으로 종료
- Job에서 메인 container가 끝나면 sidecar가 남아 있어도 Job은 완료됨
- image를 바꾸면 Pod가 아니라 그 container만 재시작
- 종료 과정에서 grace period를 메인 container가 다 쓰면 sidecar는 곧바로 SIGKILL을 받을 수 있음. 이때 0이 아닌 exit code는 정상으로 봐도 됨
-
grace period: Pod 삭제 요청부터 강제 종료까지 주는 유예 시간.
spec.terminationGracePeriodSeconds로 정하고 기본 30초. 이 안에 안 끝난 container는 SIGKILL로 죽음 -
container마다 따로 주는 게 아니라 Pod 전체가 타이머 하나를 나눠 씀. 메인이 SIGTERM을 받고 정리하는 동안에도 시간이 흐르니, 메인이 오래 걸릴수록 sidecar 몫이 줄어듦
-
예: grace period 30초, 메인은 정리에 28초, 로그 수집 sidecar는 남은 로그를 보내는 데 5초가 필요한 경우
t=0s Pod 삭제 요청. 메인에 SIGTERM t=28s 메인 종료 (exit 0). 이제야 sidecar에 SIGTERM t=30s 기한 도달. 정리 중이던 sidecar에 SIGKILL -> exit 137 (128+9)메인이 30초를 다 쓰면 sidecar는 SIGTERM도 못 받고 메인과 함께 SIGKILL을 받음
-
sidecar는 메인을 돕는 container라, 메인이 정상 종료했다면 Pod 종료는 제 할 일을 한 것. 공식 문서도 이때 sidecar의 0이 아닌 exit code는 외부 도구에서 무시하라고 안내함. 모니터링이 sidecar의 137을 장애로 잡지 않게 해 둘 것
-
기존 방식 sidecar
containers에 app container를 하나 더 넣는 방식. container 단위 restartPolicy를 지원하지 않는 옛 버전에서도 동작하고, 지금도 유효합니다. 다만 일반 app container라서:
- 앱보다 먼저 뜬다는 보장이 없음
- Job에서는 문제가 됨. Pod가
Succeeded가 되려면 모든 container가 종료돼야 하는데, 계속 도는 sidecar가 있으면 끝나지 않음
시작·종료 순서가 상관없고 모든 container가 Pod 동작에 필요하다면 이 방식으로도 충분하다고 공식 문서도 설명합니다.
ambassador와 adapter
Kubernetes 블로그 2015년 글 “Patterns for Composite Containers”에서 sidecar와 함께 소개한 패턴. 현재 concepts 문서에는 따로 항목이 없고, 구현도 별도 필드 없이 container를 추가하는 방식입니다.
| 패턴 | 역할 | 예시 (블로그 기준) |
|---|---|---|
| sidecar | 메인 container 기능 확장 | nginx + Git 저장소를 동기화하는 container, 파일 시스템 공유 |
| ambassador | 외부 연결을 대신하는 로컬 proxy | 앱은 localhost의 Redis에 붙고, ambassador가 읽기/쓰기를 나눠 실제 서버로 전달 |
| adapter | 출력을 표준 형식으로 변환 | 앱마다 다른 모니터링 데이터 형식을 공통 형식으로 바꿔 내보냄 |
ambassador는 network namespace 공유, adapter는 volume이나 localhost 공유에 기대는 구조. 셋 다 구현은 containers 또는 native sidecar로 합니다.
ephemeral container
실행 중인 Pod에 임시로 붙이는 디버깅용 container. v1.25부터 stable.
- Pod에는 생성 후 container를 추가할 수 없지만, ephemeral container는
ephemeralcontainerssubresource로 추가. 그래서kubectl edit로는 못 넣음 - 추가한 뒤에는 수정·삭제 불가
- 자동 재시작 없음, 리소스 보장 없음
ports,livenessProbe,readinessProbe,resources사용 불가- static Pod에는 사용 불가
- 쓰는 상황: container가 crash했거나, distroless처럼 shell이 없어
kubectl exec가 안 될 때
kubectl debug -it <pod> --image=busybox:1.28 --target=<container>
--target은 지정한 container의 process namespace에 붙어서 그 프로세스를 볼 수 있게 함. container runtime이 지원해야 동작하고, 지원하지 않으면 process namespace가 분리된 채로 뜰 수 있음.
예시
init container로 설정 파일을 만들고, native sidecar가 앱 로그를 읽는 Pod.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
initContainers:
- name: init-config
image: busybox:1.36
command: ["sh", "-c", 'echo "server_name=web" > /config/app.conf']
volumeMounts:
- name: config
mountPath: /config
- name: log-shipper
image: busybox:1.36
restartPolicy: Always
command: ["sh", "-c", "touch /logs/app.log; tail -F /logs/app.log"]
volumeMounts:
- name: logs
mountPath: /logs
containers:
- name: app
image: busybox:1.36
command:
[
"sh",
"-c",
"while true; do cat /config/app.conf >> /logs/app.log; sleep 5; done",
]
volumeMounts:
- name: config
mountPath: /config
- name: logs
mountPath: /logs
volumes:
- name: config
emptyDir: {}
- name: logs
emptyDir: {}
실행 순서: init-config 완료 → log-shipper 시작(started) → app 시작. log-shipper는 app과 함께 계속 실행.
같은 sidecar를 기존 방식으로 쓰면 containers 아래로 옮기고 restartPolicy를 빼면 됩니다. 이 경우 log-shipper와 app은 순서 없이 함께 시작.
kubectl apply -f web.yaml
kubectl get pod web # STATUS: Init:0/2 → Running
kubectl logs web -c init-config # init container 로그
kubectl logs web -c log-shipper -f # sidecar 로그
kubectl logs web --all-containers
kubectl exec -it web -c app -- sh
kubectl describe pod web # Init Containers 섹션의 State/Reason
헷갈리는 것 비교
init container, native sidecar, 기존 sidecar
| init container | native sidecar | 기존 sidecar | |
|---|---|---|---|
| 위치 | initContainers | initContainers | containers |
container restartPolicy | 없음 | Always | 없음 |
| 실행 기간 | 끝날 때까지만 | Pod 수명 전체 | Pod 수명 전체 |
| app보다 먼저 시작 | 예 (완료까지) | 예 (started까지) | 보장 없음 |
| probe | 불가 | 지원 | 지원 |
| app과 데이터 교환 | 한 방향 (volume에 써 두기) | 양방향 | 양방향 |
| Job 완료 막음 | 아니오 | 아니오 | 예 |
| 종료 순서 | 해당 없음 | 메인 뒤, 역순 | 순서 없음 |
kubectl exec vs kubectl debug
kubectl exec | kubectl debug (ephemeral) | |
|---|---|---|
| 사용하는 이미지 | 기존 container 이미지 | 새로 지정한 이미지 |
| shell 없는 이미지 | 불가 | 가능 |
| crash한 container | 불가 | 가능 |
| Pod spec 변화 | 없음 | ephemeral container가 남음 |
시험 포인트
명령어
멀티 container Pod를 한 번에 만드는 imperative 명령어는 없음. 뼈대를 만든 뒤 YAML을 고치는 흐름.
kubectl run web --image=busybox:1.36 --dry-run=client -o yaml \
--command -- sh -c 'sleep 3600' > web.yaml
# initContainers, 추가 container, volumes를 직접 작성
kubectl logs <pod> -c <container>
kubectl exec -it <pod> -c <container> -- sh
kubectl debug -it <pod> --image=busybox:1.28 --target=<container>
kubectl explain
kubectl explain pod.spec.initContainers
kubectl explain pod.spec.initContainers.restartPolicy # Always = native sidecar
kubectl explain pod.spec.shareProcessNamespace # container 간 프로세스 공유
kubectl explain pod.spec.volumes.emptyDir # container 간 파일 공유
kubectl explain pod.spec.containers.volumeMounts
참고 문서
- Init Containers | Kubernetes
- Sidecar Containers | Kubernetes
- Ephemeral Containers | Kubernetes
- Pods: Pods with multiple containers | Kubernetes
- Pod Lifecycle: Pod phase, Restart policy, Pod shutdown and sidecar containers | Kubernetes
- Debug Running Pods: ephemeral container | Kubernetes
- Share Process Namespace between Containers in a Pod | Kubernetes
- The Distributed System ToolKit: Patterns for Composite Containers | Kubernetes Blog (2015)
- kubectl debug
- CNCF CKAD curriculum