何を隣に置くのか
한국어 원문으로 표시합니다.
한 줄 요약
파드 안의 컨테이너들은 네트워크 네임스페이스와 볼륨을 공유한다. 그 두 가지를 공유하고 싶을 때만 컨테이너를 붙인다. 그 외에는 붙이지 않는다.
왜 이게 필요했나
"기능을 하나 더 붙이자" 는 이유로 사이드카를 늘리면 파드가 무거워지고, 배포 단위가 커지고, 하나가 죽으면 전체가 재시작된다. 사이드카는 공짜가 아니다.
붙일 만한 이유는 결국 셋 중 하나다.
| 패턴 | 무엇을 하나 | 예 |
|---|---|---|
| 사이드카 | 본체를 건드리지 않고 기능을 더한다 | 로그 수집, 지표 변환, 인증서 갱신 |
| 앰배서더 | 본체 대신 바깥과 통신한다 | 서비스 메시 프록시, DB 커넥션 풀 |
| 어댑터 | 본체의 출력을 표준 형식으로 바꾼다 | 앱 고유 지표 → 프로메테우스 형식 |
셋을 가르는 기준은 트래픽의 방향이다. 사이드카는 옆에서 거들고, 앰배서더는 나가는 길목에 서고, 어댑터는 들어오는 요청에 답할 형식을 바꾼다.
어떻게 동작하나
공유되는 것과 안 되는 것을 정확히 알아야 설계가 된다.
| 공유됨 | 공유 안 됨 |
|---|---|
네트워크(같은 localhost, 같은 IP) |
파일시스템 루트(각자 자기 이미지) |
| IPC, 볼륨(마운트한 것만) | 프로세스 목록(기본값 — shareProcessNamespace: true 로 켤 수 있다) |
그래서 사이드카가 본체의 로그를 읽으려면 같은 볼륨을 양쪽에 마운트해야 한다. emptyDir 하나를 본체는 /var/log/app 에, 수집기는 /logs 에 마운트하는 식이다.
1.29 가 고친 오래된 문제
예전에는 사이드카가 그냥 "컨테이너 하나 더" 였다. 그래서 두 가지가 어긋났다.
- 기동 순서를 보장할 수 없었다. 본체가 프록시보다 먼저 뜨면 첫 요청들이 실패한다.
- Job 이 끝나지 않았다. 본체가 끝나도 사이드카가 계속 살아 있어 Job 이 영원히 Running 이었다.
1.29 부터 initContainers 에 restartPolicy: Always 를 주면 정식 사이드카가 된다. 초기화 컨테이너처럼 본체보다 먼저 시작하고, 본체가 끝나면 함께 종료된다.
spec:
initContainers:
- name: proxy
image: envoyproxy/envoy:v1.31-latest
restartPolicy: Always # ← 이 한 줄이 사이드카로 만든다
containers:
- name: app
image: myapp:1.0
흔한 착각
리소스 요청이 더해진다는 것. 파드의 요청량은 컨테이너들의 합이다. 사이드카에 requests.cpu: 100m 를 주면 파드 100개에 10코어가 예약된다. 스케줄러는 그 합을 보고 자리를 찾으므로, 사이드카 하나가 클러스터 밀도를 크게 떨어뜨릴 수 있다.
로그 사이드카가 항상 옳다는 것. 노드마다 하나 도는 DaemonSet 수집기가 대개 더 싸다. 파드마다 수집기를 붙이면 그 수만큼 프로세스가 는다. 사이드카 수집기는 다른 파드와 격리해야 할 때(멀티테넌시)나 파일 경로가 특이할 때만 쓴다.
붙이지 않는 쪽을 먼저 생각한다
사이드카는 편해 보여서 늘어나기 쉽다. 그런데 붙이기 전에 같은 일을 다른 자리에서 할 수 있는지 먼저 보면, 상당수는 안 붙이는 쪽이 낫다는 결론이 난다.
| 하려는 일 | 사이드카 말고 | 언제 그래도 사이드카인가 |
|---|---|---|
| 로그 수집 | 노드마다 하나 도는 수집기 | 파일 경로가 특이하거나 테넌트를 격리해야 할 때 |
| 지표 노출 | 애플리케이션이 직접 노출 | 코드를 고칠 수 없는 상용 프로그램일 때 |
| 인증서 갱신 | 컨트롤러가 시크릿을 갱신하고 앱이 다시 읽기 | 앱이 파일만 읽고 다시 읽지 못할 때 |
| 설정 새로고침 | 설정 해시를 파드 템플릿에 넣어 롤링 | 재시작 없이 반영해야 할 때 |
표의 오른쪽 열에 해당하지 않으면 붙이지 않습니다. 사이드카 하나는 파드마다 곱해지므로, 파드가 200개면 200개의 프로세스와 200개 몫의 요청량이 늘어납니다. 그리고 그 컨테이너의 이미지 갱신, 취약점 대응, 설정 변경이 전부 본체의 배포에 얹힙니다.
붙이기로 했다면 몇 가지를 함께 정해 둔다. 본체보다 먼저 뜨고 나중에 죽는가(앞에서 본 restartPolicy: Always), 한도를 어떻게 잡을 것인가(버퍼를 쌓는 수집기는 특히 넉넉하게), 그리고 사이드카가 실패했을 때 본체가 어떻게 되어야 하는가이다. 마지막 것이 잘 잊히는데, 프록시가 죽었을 때 본체가 계속 요청을 받아 전부 실패하는 것보다, readiness 를 통해 트래픽에서 빠지는 편이 나은 경우가 대부분이다. 그러려면 본체의 readiness 가 사이드카의 상태를 함께 보아야 한다.
실무에서 진짜 중요한 것
사이드카는 본체와 생명주기를 공유한다. 사이드카가 OOM 으로 죽으면 그 컨테이너가 다시 뜨는 동안 본체가 기대던 기능이 멈추고, 재시작이 반복되면 파드가 CrashLoopBackOff 로 빠져 통째로 못 쓰게 된다. 그래서 사이드카의 메모리 한도는 넉넉하게, 그리고 본체보다 먼저 죽지 않도록 잡아야 한다. 로그 수집기가 버퍼를 메모리에 쌓는 설정이면 특히 그렇다.