代理装上之后会发生什么——资源、启动顺序和结束不了的 Job
한국어 원문으로 표시합니다.
목표
istioctl kube-inject 를 오프라인으로 돌려 주석 하나하나가 주입 결과의 어느 필드로 내려가는지 확인한다. 자원 요청과 상한, 프록시 설정(concurrency·드레인 시간), 시작 순서 보장, 나갈 포트 제외, 주입 제외, 그리고 배치 작업이 끝나지 않는 고전적 사고와 그 해법까지 필드 단위로 본다.
왜 중요한가
사이드카를 붙이는 일은 한 줄이지만, 붙인 다음에 생기는 문제는 대개 설정 한 칸에서 온다. 기본 프록시는 CPU 100m 과 메모리 128Mi 를 요청한다 — 파드 2천 개면 그것만으로 CPU 200 코어다. 시작 순서도 문제다. 앱이 프록시보다 먼저 뜨면 첫 몇 초의 나가는 요청이 그냥 실패한다. 가장 오래된 함정은 배치 작업이다. 프록시가 일반 컨테이너로 들어가면 작업이 끝나도 파드가 완료로 넘어가지 못하고, 예전에는 앱이 끝나면서 프록시에게 종료를 요청하는 방식으로 우회했다. 지금은 쿠버네티스가 재시작 정책을 가진 초기화 컨테이너를 지원하므로 프록시를 그쪽으로 옮겨 근본적으로 해결한다. 이 실습은 이 모든 손잡이를 주입 결과 매니페스트에서 직접 확인하는 법을 익히는 자리다. 클러스터에 올리기 전에 확인할 수 있으면 설정 변경이 리뷰 대상이 된다.
단계
/root/ist-lifecycle를 만들고/root/ist-lifecycle/dep.yaml에lifecycle네임스페이스의 Deploymentorders를 쓰세요 —replicas2, 라벨app: orders, 컨테이너 하나(app, 이미지nginx:1.27, 컨테이너 포트 8080), 주석은 없습니다. 이 파일을 주입해 결과를/root/ist-lifecycle/baseline.yaml에 저장하고, 거기서 읽은 네 가지를/root/ist-lifecycle/baseline.tsv에containers·initContainers·proxyCpuRequest·proxyMemRequest네 줄로 적으세요(컨테이너 이름은 나온 순서대로 쉼표로 잇습니다)./root/ist-lifecycle/dep-resources.yaml은 1단계 Deployment 를 이름orders-res로 복사하고 파드 템플릿 주석 네 개를 더한 것입니다 —sidecar.istio.io/proxyCPU는250m,sidecar.istio.io/proxyMemory는192Mi,sidecar.istio.io/proxyCPULimit은1500m,sidecar.istio.io/proxyMemoryLimit은512Mi입니다. 주입 결과를/root/ist-lifecycle/resources.yaml에 저장하세요./root/ist-lifecycle/dep-proxyconfig.yaml은 이름orders-pc로 복사한 판에proxy.istio.io/config주석 하나를 넣은 것입니다 — 값은 여러 줄 YAML 로concurrency: 3과terminationDrainDuration: 45s두 가지입니다. 주입 결과를/root/ist-lifecycle/proxyconfig.yaml에 저장하고, 결과에서istio-proxy의 환경변수PROXY_CONFIG값을/root/ist-lifecycle/proxy-config.json에 저장하세요./root/ist-lifecycle/dep-hold.yaml은 이름orders-hold로 복사한 판에proxy.istio.io/config주석으로holdApplicationUntilProxyStarts: true하나만 넣은 것입니다. 주입 결과를/root/ist-lifecycle/hold.yaml에 저장하고, 컨테이너 순서와 프록시에 생긴 수명주기 훅을/root/ist-lifecycle/hold.tsv에containerOrder와postStart두 줄로 적으세요 (postStart칸에는 훅이 실행하는 명령을 공백으로 이어 씁니다)./root/ist-lifecycle/dep-exclude.yaml은 이름orders-exc로 복사한 판에 주석traffic.sidecar.istio.io/excludeOutboundPorts를"3306,5432"로 넣은 것입니다. 주입 결과를/root/ist-lifecycle/exclude.yaml에 저장하고,istio-init컨테이너의 인자를 공백으로 이어/root/ist-lifecycle/init-args.txt에 한 줄로 적으세요./root/ist-lifecycle/dep-nosidecar.yaml은 이름orders-off로 복사한 판에 주석sidecar.istio.io/inject를"false"로 넣은 것입니다. 주입 결과를/root/ist-lifecycle/nosidecar.yaml에 저장하세요 — 결과에 프록시가 없어야 합니다./root/ist-lifecycle/job.yaml에lifecycle네임스페이스의 Jobnightly-settle을 쓰세요 — 파드 라벨app: nightly-settle,restartPolicy: Never, 컨테이너 하나(worker, 이미지busybox:1.36)./root/ist-lifecycle/job-native.yaml은 이름만nightly-settle-native로 바꾸고 파드 주석sidecar.istio.io/nativeSidecar를"true"로 더한 판입니다. 둘을 각각 주입해/root/ist-lifecycle/job-injected.yaml과/root/ist-lifecycle/job-native-injected.yaml에 저장하고, 차이를/root/ist-lifecycle/job-compare.tsv에plain과native두 줄로<이름>\t<containers>\t<initContainers>\t<프록시의 restartPolicy>형식으로 적으세요(restartPolicy 가 없으면-를 씁니다)./root/ist-lifecycle/inject-summary.sh를 만들어 이 디렉터리의 원본 매니페스트 여덟 장(dep.yaml·dep-resources.yaml·dep-proxyconfig.yaml·dep-hold.yaml·dep-exclude.yaml·dep-nosidecar.yaml·job.yaml·job-native.yaml)을 차례로 다시 주입하고, 파일마다<파일이름>\t<containers>\t<initContainers>\t<프록시 CPU 요청>을 그 순서대로 표준출력에만 찍게 하세요(없는 칸은-). 출력을/root/ist-lifecycle/inject-summary.tsv에 저장하세요.
참고
- 주입 명령에는 설정 세 개가 필요합니다 —
--injectConfigFile,--meshConfigFile,--valuesFile. 이 실습 이미지에서는/opt/istio/아래에 있습니다. - 주석은 파드 템플릿에 붙입니다. Deployment 메타데이터에 붙이면 아무 일도 일어나지 않습니다.
proxy.istio.io/config는 값 안에 YAML 을 담는 주석이라 여러 줄 블록(|)으로 씁니다.- 흔한 실수: 프록시를
containers에서만 찾습니다. 네이티브 사이드카는initContainers에 있습니다. - 흔한 실수: 주석 값에 따옴표를 빼먹습니다.
"false"는 문자열이어야 합니다. - 참고: https://istio.io/v1.24/docs/reference/config/annotations/
- 참고: https://istio.io/v1.24/docs/setup/additional-setup/sidecar-injection/
- 참고: https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/
아무 주석도 없을 때 프록시가 어떤 모양으로 들어가나
/root/ist-lifecycle 를 만들고 /root/ist-lifecycle/dep.yaml 에 lifecycle 네임스페이스의 Deployment orders 를 쓰세요 — replicas 2, 라벨 app: orders, 컨테이너 하나(app, 이미지 nginx:1.27, 컨테이너 포트 8080), 주석은 없습니다. 이 파일을 주입해 결과를 /root/ist-lifecycle/baseline.yaml 에 저장하고, 거기서 읽은 네 가지를 /root/ist-lifecycle/baseline.tsv 에 containers·initContainers·proxyCpuRequest·proxyMemRequest 네 줄로 적으세요(컨테이너 이름은 나온 순서대로 쉼표로 잇습니다).
istiod 가 없으므로 주입 설정 세 개를 직접 줘야 합니다 — --injectConfigFile /opt/istio/inject-config.yaml --meshConfigFile /opt/istio/mesh-config.yaml --valuesFile /opt/istio/values-config.yaml. 결과에서 순서를 볼 때는 yq -N e '.spec.template.spec.containers[].name' 이 편합니다.
프록시의 자원을 주석으로 조정한다
/root/ist-lifecycle/dep-resources.yaml 은 1단계 Deployment 를 이름 orders-res 로 복사하고 파드 템플릿 주석 네 개를 더한 것입니다 — sidecar.istio.io/proxyCPU 는 250m, sidecar.istio.io/proxyMemory 는 192Mi, sidecar.istio.io/proxyCPULimit 은 1500m, sidecar.istio.io/proxyMemoryLimit 은 512Mi 입니다. 주입 결과를 /root/ist-lifecycle/resources.yaml 에 저장하세요.
주석은 파드 템플릿(spec.template.metadata.annotations)에 붙여야 합니다. Deployment 의 메타데이터에 붙이면 아무 일도 일어나지 않습니다 — 주입은 파드를 대상으로 하기 때문입니다. 기본값과 얼마나 달라졌는지 1단계 결과와 나란히 놓고 보세요.
프록시 자체의 설정은 주석 하나에 YAML 로 넣는다
/root/ist-lifecycle/dep-proxyconfig.yaml 은 이름 orders-pc 로 복사한 판에 proxy.istio.io/config 주석 하나를 넣은 것입니다 — 값은 여러 줄 YAML 로 concurrency: 3 과 terminationDrainDuration: 45s 두 가지입니다. 주입 결과를 /root/ist-lifecycle/proxyconfig.yaml 에 저장하고, 결과에서 istio-proxy 의 환경변수 PROXY_CONFIG 값을 /root/ist-lifecycle/proxy-config.json 에 저장하세요.
이 주석은 값이 문자열이지만 안에 YAML 을 담습니다. 여러 줄 블록(|)으로 쓰세요. 주입기는 이것을 JSON 으로 바꿔 컨테이너 환경변수 하나에 넣습니다 — 사람이 쓰는 형식과 프록시가 읽는 형식이 다르다는 점이 핵심입니다.
앱이 프록시보다 먼저 뜨는 경합을 막는다
/root/ist-lifecycle/dep-hold.yaml 은 이름 orders-hold 로 복사한 판에 proxy.istio.io/config 주석으로 holdApplicationUntilProxyStarts: true 하나만 넣은 것입니다. 주입 결과를 /root/ist-lifecycle/hold.yaml 에 저장하고, 컨테이너 순서와 프록시에 생긴 수명주기 훅을 /root/ist-lifecycle/hold.tsv 에 containerOrder 와 postStart 두 줄로 적으세요 (postStart 칸에는 훅이 실행하는 명령을 공백으로 이어 씁니다).
이 값을 켜면 주입기가 두 가지를 바꿉니다 — 프록시를 컨테이너 목록의 어디에 놓는지, 그리고 프록시에 무엇을 붙이는지. 1단계 결과와 컨테이너 순서를 비교해 보면 바로 보입니다. 훅은 .lifecycle.postStart.exec.command 에 있습니다.
프록시를 지나면 안 되는 포트를 빼 둔다
/root/ist-lifecycle/dep-exclude.yaml 은 이름 orders-exc 로 복사한 판에 주석 traffic.sidecar.istio.io/excludeOutboundPorts 를 "3306,5432" 로 넣은 것입니다. 주입 결과를 /root/ist-lifecycle/exclude.yaml 에 저장하고, istio-init 컨테이너의 인자를 공백으로 이어 /root/ist-lifecycle/init-args.txt 에 한 줄로 적으세요.
나가는 트래픽을 프록시로 돌리는 일은 초기화 컨테이너가 iptables 규칙으로 합니다. 주석은 그 명령줄의 인자로 내려갑니다 — 어느 플래그에 그 포트들이 붙는지 찾아보세요. 데이터베이스 프로토콜처럼 프록시가 잘못 해석할 수 있는 포트를 뺄 때 쓰는 손잡이입니다.
이 파드만 프록시를 받지 않게 한다
/root/ist-lifecycle/dep-nosidecar.yaml 은 이름 orders-off 로 복사한 판에 주석 sidecar.istio.io/inject 를 "false" 로 넣은 것입니다. 주입 결과를 /root/ist-lifecycle/nosidecar.yaml 에 저장하세요 — 결과에 프록시가 없어야 합니다.
주입을 켜고 끄는 손잡이는 네임스페이스 라벨과 파드 주석 두 층에 있고, 파드 쪽이 이깁니다. 프록시를 지나면 안 되는 워크로드(대용량 전송, 프록시가 이해하지 못하는 프로토콜)를 예외로 둘 때 쓰는 자리입니다. 초기화 컨테이너도 함께 사라지는지 확인해 보세요.
끝나지 않는 배치 작업과 그 해법
/root/ist-lifecycle/job.yaml 에 lifecycle 네임스페이스의 Job nightly-settle 을 쓰세요 — 파드 라벨 app: nightly-settle, restartPolicy: Never, 컨테이너 하나(worker, 이미지 busybox:1.36). /root/ist-lifecycle/job-native.yaml 은 이름만 nightly-settle-native 로 바꾸고 파드 주석 sidecar.istio.io/nativeSidecar 를 "true" 로 더한 판입니다. 둘을 각각 주입해 /root/ist-lifecycle/job-injected.yaml 과 /root/ist-lifecycle/job-native-injected.yaml 에 저장하고, 차이를 /root/ist-lifecycle/job-compare.tsv 에 plain 과 native 두 줄로 <이름>\t<containers>\t<initContainers>\t<프록시의 restartPolicy> 형식으로 적으세요(restartPolicy 가 없으면 - 를 씁니다).
일반 컨테이너로 들어간 프록시는 작업이 끝나도 계속 살아 있어서 Job 이 완료로 넘어가지 못합니다. 쿠버네티스 1.28 부터는 초기화 컨테이너에 재시작 정책을 주어 '먼저 뜨고 끝까지 살다가 본체가 끝나면 함께 정리되는' 컨테이너를 만들 수 있습니다. 주석을 켜면 프록시가 어느 목록으로 옮겨 가는지 보세요.
여덟 장을 한 번에 다시 주입해 표로 굳힌다
/root/ist-lifecycle/inject-summary.sh 를 만들어 이 디렉터리의 원본 매니페스트 여덟 장(dep.yaml·dep-resources.yaml·dep-proxyconfig.yaml·dep-hold.yaml·dep-exclude.yaml·dep-nosidecar.yaml·job.yaml·job-native.yaml)을 차례로 다시 주입하고, 파일마다 <파일이름>\t<containers>\t<initContainers>\t<프록시 CPU 요청> 을 그 순서대로 표준출력에만 찍게 하세요(없는 칸은 -). 출력을 /root/ist-lifecycle/inject-summary.tsv 에 저장하세요.
프록시가 일반 컨테이너에 있을 때와 초기화 컨테이너에 있을 때를 모두 찾아야 합니다 — 두 목록을 이어서 훑으면 한 식으로 끝납니다. 이런 표를 저장소에 함께 두면 주입 설정이 바뀐 날 차이가 바로 드러납니다.