사이드카 주입 — 자동·수동 방식과 커스터마이징
사이드카 주입 — 자동·수동 방식과 커스터마이징
Istio의 모든 기능을 쓰려면 메시 안의 파드마다 Istio 사이드카 프록시가 떠 있어야 해요. 이 프록시를 파드에 넣는 작업을 "주입(injection)"이라고 부르는데, 네임스페이스 단위로 자동으로 넣는 방식과 istioctl로 수동으로 넣는 방식 두 가지가 있어요. 어떤 걸 써야 할지 고민된다면 자동 주입을 권장해요.
자동 사이드카 주입
Istio가 제공하는 mutating webhook admission controller가 적합한 쿠버네티스 파드에 사이드카를 자동으로 넣어줘요. 네임스페이스에 istio-injection=enabled 레이블을 달고 주입 웹훅이 켜져 있으면, 그 네임스페이스에 새로 생기는 파드는 자동으로 사이드카를 얻게 돼요.
자동 주입은 파드 레벨에서 일어나요. 배포(deployment) 자체에는 변화가 보이지 않고, 개별 파드를 kubectl describe로 봐야 주입된 프록시를 확인할 수 있어요.
예제 앱을 배포하고 컨테이너 수를 확인해 볼게요.
$ kubectl apply -f @samples/curl/curl.yaml@
$ kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
curl 1/1 1 1 12s 1 curlimages/curl app=curl
파드도 하나만 떠 있죠.
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
curl-8f795f47d-hdcgs 1/1 Running 0 42s
이제 default 네임스페이스에 istio-injection=enabled 레이블을 붙여요.
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 5m9s enabled
주입은 파드가 생성될 때 일어나요. 도는 파드를 지우고 새로 만들어 보면, 원래 파드가 컨테이너 1/1, 주입된 파드는 2/2인 걸 볼 수 있어요.
$ kubectl delete pod -l app=curl
pod "curl-776b7bcdcd-7hpnk" deleted
$ kubectl get pod -l app=curl
NAME READY STATUS RESTARTS AGE
curl-776b7bcdcd-bhn9m 2/2 Running 0 7s
주입된 파드를 자세히 보면 istio-proxy 컨테이너와 그에 딸린 볼륨이 추가된 걸 확인할 수 있어요.
$ kubectl describe pod -l app=curl
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal Created 11s kubelet Created container istio-init
Normal Started 11s kubelet Started container istio-init
...
Normal Created 10s kubelet Created container curl
Normal Started 10s kubelet Started container curl
...
Normal Created 9s kubelet Created container istio-proxy
Normal Started 8s kubelet Started container istio-proxy
주입을 끄려면 네임스페이스에서 istio-injection 레이블을 제거하고 파드를 새로 만들면 돼요.
$ kubectl label namespace default istio-injection-
$ kubectl delete pod -l app=curl
$ kubectl get pod
namespace/default labeled
pod "curl-776b7bcdcd-bhn9m" deleted
NAME READY STATUS RESTARTS AGE
curl-776b7bcdcd-gmvnr 1/1 Running 0 2s
주입 정책 제어
네임스페이스뿐 아니라 파드마다 sidecar.istio.io/inject 레이블로 주입 여부를 제어할 수 있어요.
| 리소스 | 레이블 | 켜는 값 | 끄는 값 |
|---|---|---|---|
| Namespace | istio-injection |
enabled |
disabled |
| Pod | sidecar.istio.io/inject |
"true" |
"false" |
컨트롤 플레인 리비전(canary 업그레이드)을 쓰면 istio.io/rev 레이블로 리비전을 구분해요. 예를 들어 리비전 이름이 canary라면:
| 리소스 | 켜는 레이블 | 끄는 레이블 |
|---|---|---|
| Namespace | istio.io/rev=canary |
istio-injection=disabled |
| Pod | istio.io/rev=canary |
sidecar.istio.io/inject="false" |
같은 네임스페이스에 istio-injection 레이블과 istio.io/rev 레이블이 동시에 있으면 istio-injection 레이블이 우선해요. 주입기는 다음 논리로 동작해요.
- 둘 중 하나(
istio-injection또는sidecar.istio.io/inject)가 꺼져 있으면 → 주입하지 않아요. - 셋 중 하나(
istio-injection,sidecar.istio.io/inject,istio.io/rev)가 켜져 있으면 → 주입해요. - 아무 레이블도 없으면 →
.values.sidecarInjectorWebhook.enableNamespacesByDefault가 켜져 있는 경우에만 주입해요. 기본값은 꺼져 있어서 대부분 파드는 주입되지 않아요.
수동 사이드카 주입
배포를 수동으로 주입하려면 istioctl kube-inject를 써요.
$ istioctl kube-inject -f @samples/curl/curl.yaml@ | kubectl apply -f -
serviceaccount/curl created
service/curl created
deployment.apps/curl created
기본적으로 클러스터 안의 설정을 사용하는데, 설정을 로컬로 받아 쓸 수도 있어요.
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
$ kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
파일을 명시해서 kube-inject를 실행하고 배포해요.
$ istioctl kube-inject \
--injectConfigFile inject-config.yaml \
--meshConfigFile mesh-config.yaml \
--valuesFile inject-values.yaml \
--filename @samples/curl/curl.yaml@ \
| kubectl apply -f -
serviceaccount/curl created
service/curl created
deployment.apps/curl created
주입된 curl 파드가 READY 컬럼에 2/2로 보이면 성공이에요.
$ kubectl get pod -l app=curl
NAME READY STATUS RESTARTS AGE
curl-64c6f57bc8-f5n4x 2/2 Running 0 24s
주입 커스터마이징
파드는 기본적으로 istio-sidecar-injector configmap에 정의된 사이드카 주입 템플릿으로 주입돼요. 파드에 직접 istio-proxy 컨테이너를 추가하면 그 설정이 기본 템플릿에 대한 오버라이드로 동작해요. 여기엔 주의가 필요한 게, 결과적으로 만들어지는 Pod 전체를 바꿀 수 있어서 사이드카 컨테이너가 제대로 동작하지 않게 만들 수도 있어요.
예를 들어 다음 설정은 CPU 요청을 낮추고 볼륨 마운트와 preStop 훅을 추가해요.
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: hello
image: alpine
- name: istio-proxy
image: auto
resources:
requests:
cpu: "100m"
volumeMounts:
- mountPath: /etc/certs
name: certs
lifecycle:
preStop:
exec:
command: ["curl", "10"]
volumes:
- name: certs
secret:
secretName: istio-certs
일반적으로 파드의 어떤 필드든 설정할 수 있지만 몇 가지는 조심해야 해요.
- 쿠버네티스는 주입이 실행되기 전에
image필드가 설정돼 있어야 해요. 특정 이미지를 오버라이드할 수도 있지만,auto로 두면 주입기가 자동으로 이미지를 골라줘요. Pod의 일부 필드는 서로 의존해요. 예를 들어 CPU request는 limit보다 작아야 해요. 둘을 함께 설정하지 않으면 파드가 시작에 실패할 수 있어요.securityContext.RunAsUser와securityContext.RunAsGroup은 어떤 경우엔 반영되지 않을 수 있어요.TPROXY모드라면 사이드카를 사용자0으로 돌려야 하거든요. 이 필드를 잘못 덮어쓰면 트래픽이 끊길 수 있으니 극도로 조심해야 해요.
어떤 필드는 파드의 애너테이션으로도 설정할 수 있어요. 다음처럼 부분만 쓴 애너테이션 설정이 있을 때:
spec:
template:
metadata:
annotations:
sidecar.istio.io/proxyCPU: "200m"
sidecar.istio.io/proxyMemoryLimit: "5Gi"
주입된 리소스는 이런 모습이 돼요. proxyCPU를 설정했으면 proxyCPULimit도 꼭 함께 설정해야 CPU limit이 무한대로 남지 않아요.
spec:
containers:
- name: istio-proxy
resources:
limits:
memory: 5Gi
requests:
cpu: 200m
memory: 5Gi
securityContext:
allowPrivilegeEscalation: false
커스텀 템플릿 (실험적)
설치 시점에 완전히 새로운 템플릿도 정의할 수 있어요. istio-proxy 컨테이너에 GREETING 환경변수를 주입하는 커스텀 템플릿을 예로 들어 볼게요.
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio
spec:
values:
sidecarInjectorWebhook:
templates:
custom: |
spec:
containers:
- name: istio-proxy
env:
- name: GREETING
value: hello-world
파드는 기본적으로 자동 생성된 sidecar 주입 템플릿을 사용해요. inject.istio.io/templates 애너테이션으로 덮어쓸 수 있는데, 기본 템플릿과 커스텀 템플릿을 함께 쓰려면 inject.istio.io/templates=sidecar,custom으로 설정하면 돼요. 기본 sidecar 외에 Gateway 배포용 프록시 주입을 지원하는 gateway 템플릿도 기본 제공돼요.