Sidecar 주입 문제

Sidecar 주입 문제 (Sidecar Injection Problems)

Istio가 자동 sidecar 주입을 위해 쿠버네티스 웹훅을 사용할 때 발생하는 일반적인 문제를 해결하는 방법을 배워요.

출처: Istio 문서

본문

sidecar 주입 결과가 기대와 다를 때

여기에는 기대하지 않았는데 sidecar가 주입된 경우와, 기대했는데 sidecar가 주입되지 않은 경우가 모두 포함돼요.

  1. 파드가 kube-system이나 kube-public 네임스페이스에 없는지 확인하세요. 이 네임스페이스의 파드에 대해서는 자동 sidecar 주입이 무시돼요.
  2. 파드 스펙에 hostNetwork: true가 없는지 확인하세요. 호스트 네트워크에 있는 파드에 대해서는 자동 sidecar 주입이 무시돼요. sidecar 모델은 Envoy가 트래픽을 가로채기 위해 필요한 iptables 변경이 파드 내부에 있다고 가정해요. 호스트 네트워크의 파드에서는 이 가정이 위배되며, 이는 호스트 수준에서 라우팅 실패로 이어질 수 있어요.
  3. 웹훅의 namespaceSelector를 확인해서 웹훅이 대상 네임스페이스에 대해 opt-in으로 스코핑됐는지, opt-out으로 스코핑됐는지 결정하세요.

opt-in의 namespaceSelector는 다음과 같이 보일 거예요.

$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml | grep "namespaceSelector:" -A5
  namespaceSelector:
    matchLabels:
      istio-injection: enabled
  rules:
  - apiGroups:
    - ""

주입 웹훅은 istio-injection=enabled 라벨이 있는 네임스페이스에서 생성된 파드에 대해 호출돼요.

$ kubectl get namespace -L istio-injection
NAME           STATUS    AGE       ISTIO-INJECTION
default        Active    18d       enabled
istio-system   Active    3d
kube-public    Active    18d
kube-system    Active    18d

opt-out의 namespaceSelector는 다음과 같이 보일 거예요.

$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml | grep "namespaceSelector:" -A5
  namespaceSelector:
    matchExpressions:
    - key: istio-injection
      operator: NotIn
      values:
      - disabled
  rules:
  - apiGroups:
    - ""

주입 웹훅은 istio-injection=disabled 라벨이 없는 네임스페이스에서 생성된 파드에 대해 호출돼요.

$ kubectl get namespace -L istio-injection
NAME           STATUS    AGE       ISTIO-INJECTION
default        Active    18d
istio-system   Active    3d        disabled
kube-public    Active    18d       disabled
kube-system    Active    18d       disabled

애플리케이션 파드의 네임스페이스가 올바르게 라벨링되어 있는지 확인하고 그에 따라 (재)라벨링하세요. 예:

$ kubectl label namespace istio-system istio-injection=disabled --overwrite
(repeat for all namespaces in which the injection webhook should be invoked for new pods)
$ kubectl label namespace default istio-injection=enabled --overwrite
  1. 기본 정책 확인

istio-sidecar-injector configmap에서 기본 주입 정책을 확인하세요.

$ kubectl -n istio-system get configmap istio-sidecar-injector -o jsonpath='{.data.config}' | grep policy:
policy: enabled

허용되는 정책 값은 disabled와 enabled예요. 기본 정책은 웹훅의 namespaceSelector가 대상 네임스페이스와 일치하는 경우에만 적용돼요. 인식되지 않는 정책은 주입을 완전히 비활성화해요.

  1. 파드별 override 라벨 확인

기본 정책은 파드 템플릿 스펙의 메타데이터에 있는 sidecar.istio.io/inject 라벨로 재정의할 수 있어요. deployment의 메타데이터는 무시돼요. 라벨 값이 true면 sidecar가 주입되도록 강제하고, false면 sidecar가 주입되지 않도록 강제해요.

다음 라벨은 기본 정책이 무엇이든 재정의해서 sidecar가 주입되도록 강제해요.

$ kubectl get deployment curl -o yaml | grep "sidecar.istio.io/inject:" -B4
template:
  metadata:
    labels:
      app: curl
      sidecar.istio.io/inject: "true"

파드가 아예 생성되지 않는 경우

실패하는 파드의 deployment에 대해 kubectl describe -n namespace deployment name을 실행하세요. 주입 웹훅 호출 실패는 일반적으로 이벤트 로그에 캡처돼요.

x509 인증서 관련 오류

Warning  FailedCreate  3m (x17 over 8m)  replicaset-controller  Error creating: Internal error occurred: \
    failed calling admission webhook "sidecar-injector.istio.io": Post https://istiod.istio-system.svc:443/inject: \
    x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying \
    to verify candidate authority certificate "Kubernetes.cluster.local")

x509: certificate signed by unknown authority 오류는 일반적으로 웹훅 구성의 caBundle이 비어 있어서 발생해요.

mutatingwebhookconfiguration의 caBundle이 istiod 파드에 마운트된 루트 인증서와 일치하는지 확인하세요.

$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | md5sum
4b95d2ba22ce8971c7c92084da31faf0  -
$ kubectl -n istio-system get configmap istio-ca-root-cert -o jsonpath='{.data.root-cert\.pem}' | base64 -w 0 | md5sum
4b95d2ba22ce8971c7c92084da31faf0  -

CA 인증서는 일치해야 해요. 일치하지 않으면 istiod 파드를 재시작하세요.

$ kubectl -n istio-system patch deployment istiod \
    -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"`date +'%s'`\"}}}}}"
deployment.extensions "istiod" patched

deployment 상태의 오류

파드에 자동 sidecar 주입이 활성화되어 있고 어떤 이유로든 주입이 실패하면 파드 생성도 실패해요. 이런 경우 파드의 deployment 상태를 확인해서 오류를 식별할 수 있어요. 오류는 deployment와 연관된 네임스페이스의 이벤트에도 나타나요.

예를 들어 파드를 배포하려고 할 때 istiod 컨트롤 플레인 파드가 실행 중이 아니었다면, 이벤트에 다음 오류가 표시될 거예요.

$ kubectl get events -n curl
...
23m Normal   SuccessfulCreate replicaset/curl-9454cc476   Created pod: curl-9454cc476-khp45
22m Warning  FailedCreate     replicaset/curl-9454cc476   Error creating: Internal error occurred: failed calling webhook "namespace.sidecar-injector.istio.io": failed to call webhook: Post "https://istiod.istio-system.svc:443/inject?timeout=10s": dial tcp 10.96.44.51:443: connect: connection refused
$ kubectl -n istio-system get pod -lapp=istiod
NAME                            READY     STATUS    RESTARTS   AGE
istiod-7d46d8d9db-jz2mh         1/1       Running     0         2d
$ kubectl -n istio-system get endpoints istiod
NAME           ENDPOINTS                                                  AGE
istiod   10.244.2.8:15012,10.244.2.8:15010,10.244.2.8:15017 + 1 more...   3h18m

istiod 파드나 엔드포인트가 준비되지 않았다면 파드 로그와 상태를 확인해 웹훅 파드가 시작되어 트래픽을 서빙하지 못하는 이유를 찾아보세요.

$ for pod in $(kubectl -n istio-system get pod -lapp=istiod -o jsonpath='{.items[*].metadata.name}'); do \
    kubectl -n istio-system logs ${pod} \
done


$ for pod in $(kubectl -n istio-system get pod -l app=istiod -o name); do \
kubectl -n istio-system describe ${pod}; \
done
$

쿠버네티스 API 서버에 프록시 설정이 있으면 자동 sidecar 주입이 실패해요

쿠버네티스 API 서버에 다음과 같은 프록시 설정이 포함되어 있으면:

env:
  - name: http_proxy
    value: http://proxy-wsa.esl.foo.com:80
  - name: https_proxy
    value: http://proxy-wsa.esl.foo.com:80
  - name: no_proxy
    value: 127.0.0.1,localhost,dockerhub.foo.com,devhub-docker.foo.com,10.84.100.125,10.84.100.126,10.84.100.127

이 설정에서는 Sidecar 주입이 실패해요. 유일한 관련 실패 로그는 kube-apiserver 로그에서 찾을 수 있어요.

W0227 21:51:03.156818       1 admission.go:257] Failed calling webhook, failing open sidecar-injector.istio.io: failed calling admission webhook "sidecar-injector.istio.io": Post https://istio-sidecar-injector.istio-system.svc:443/inject: Service Unavailable

*_proxy 변수에 따라 파드와 서비스 CIDR 모두가 프록시되지 않는지 확인하세요. kube-apiserver 파일과 로그를 확인해 구성과 어떤 요청이 프록시되고 있는지 검증하세요.

한 가지 해결 방법은 kube-apiserver 매니페스트에서 프록시 설정을 제거하는 것이고, 다른 해결 방법은 istio-sidecar-injector.istio-system.svc 또는 .svc를 no_proxy 값에 포함하는 것이에요. 각 해결 방법 후에 kube-apiserver가 재시작되었는지 확인하세요.

이와 관련해 Kubernetes에 이슈가 제출됐으며 이후 닫혔어요.

파드에서 Tcpdump 사용의 제한

Tcpdump는 sidecar 파드에서 작동하지 않아요. 컨테이너가 root로 실행되지 않기 때문이에요. 하지만 같은 파드의 다른 어떤 컨테이너든 네트워크 네임스페이스가 공유되므로 모든 패킷을 볼 수 있어요. iptables도 파드 전체 구성을 볼 수 있어요.

Envoy와 앱 사이의 통신은 127.0.0.1에서 일어나며 암호화되지 않아요.

클러스터가 자동으로 스케일 다운되지 않아요

sidecar 컨테이너가 로컬 스토리지 볼륨을 마운트하기 때문에 노드 오토스케일러는 주입된 파드가 있는 노드를 제거할 수 없어요. 이는 알려진 이슈예요. 해결 방법은 주입된 파드에 파드 어노테이션 "cluster-autoscaler.kubernetes.io/safe-to-evict": "true"를 추가하는 것이에요.

istio-proxy가 준비되지 않으면 파드나 컨테이너가 네트워크 문제로 시작돼요

많은 애플리케이션은 시작 중에 네트워크 연결이 필요한 명령이나 검사를 실행해요. istio-proxy sidecar 컨테이너가 준비되지 않으면 이로 인해 애플리케이션 컨테이너가 멈추거나 재시작될 수 있어요.

이를 피하려면 holdApplicationUntilProxyStarts를 true로 설정하세요. 이는 sidecar 인젝터가 파드의 컨테이너 목록 시작 부분에 sidecar를 주입하고, 프록시가 준비될 때까지 다른 모든 컨테이너의 시작을 차단하도록 구성해요.

이는 전역 구성 옵션으로 추가할 수 있어요.

values.global.proxy.holdApplicationUntilProxyStarts: true

또는 파드 어노테이션으로 추가할 수 있어요.

proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'

더 알아보기 (Learn more)