Istio에서 각 마이크로서비스간 트래픽은 Sidecar 패턴으로 추가된 프록시 컨테이너(Envoy)를 통해 전달됩니다. Istio 공식 문서에 따르면 sidecar injection은 아래와 같은 3가지 설정(Configuration)과 2가지 보안 규칙(Security rules)에 의해 수행됩니다.
Configuration:
- webhooks namespaceSelector
- default policy
- per-pod override annotation
Security rules:
- sidecars cannot be injected in the kube-system or kube-public namespaces
- sidecars cannot be injected into pods that use the host network
이 글에서는 쿠버네티스 클러스터에서 istio를 사용할 때, 새로 생성되는 Pod 내부에 추가 프록시 컨테이너를 주입하는 sidecar injection 설정에 대해 알아보고 각 설정값에 따라 프록시 컨테이너의 생성 여부를 살펴보겠습니다. 또한 이러한 설정이 올바르게 적용되어 있음에도 불구하고 sidecar injection이 수행되지 않는 예외 규칙에 대해서도 알아보겠습니다.
Configuration
먼저 sidecar injection이 설정되지 않은 경우를 살펴보겠습니다. 컨테이너 1개를 포함하는 Pod를 생성하고, 정상적으로 완료된 후 kubectl get 명령으로 조회하면 다음과 같이 READY 열에 Pod 내 전체 컨테이너 1개 중 1개가 준비되었다는 것을 표시해 줍니다. 1/1로 표기되는 READY 상태를 확인한 다음 Pod를 삭제해줍니다.
$ kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
pod/nginx created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 9s
$ kubectl delete po nginx
pod "nginx" deleted
1. webhooks namespaceSelector
가장 먼저 살펴볼 설정은 특정 네임스페이스 내에서 생성되는 모든 Pod에 대한 sidecar injection 설정입니다. 다음과 같이 네임스페이스에 label을 추가하여 설정할 수 있습니다.
$ kubectl label namespace default istio-injection=enabled
default 네임스페이스에 sidecar injection 설정을 완료하였습니다. 이제 Pod를 생성해보겠습니다.
$ kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
pod/nginx created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 2/2 Running 0 8s
컨테이너 1개가 포함된 Pod를 생성했지만 READY 상태가 2/2로 바뀌었습니다! 실제 Pod 내 어떤 컨테이너들이 생성되어 있는지 확인해봅시다.
$ kubectl get po nginx -o jsonpath="{.spec.containers[*].name}{'\n'}"
nginx istio-proxy
nginx 컨테이너 외에도 istio-proxy 라는 이름의 컨테이너가 하나 추가된 것을 알 수 있습니다. 이름에서 알 수 있듯 이 컨테이너가 바로 Istio의 프록시 컨테이너입니다.
단순히 label만 추가했을 뿐인데 어떻게 프록시 컨테이너가 추가된 것일까요? 이를 이해하기 위해서는 쿠버네티스의 Admission Controller에 대한 이해가 필요합니다.
Admission Controller는 쿠버네티스의 오브젝트가 수정되었을 때, 수정 요청을 그대로 적용하지 않고 승인, 변경, 검증 등의 절차를 수행합니다. istio를 설치하면 Admission Controller 중 하나인 mutating webhook이 추가되는데, 이것이 Pod 생성 요청을 감지하고, 여러가지 설정값들을 검토하여 프록시 컨테이너를 포함하도록 요청 내용을 변경하게 됩니다.
mutatingwebhookconfigurations 오브젝트는 mutating webhook에 대한 설정값을 가지고 있습니다. 설정 일부를 살펴보면 istio-injection 값이 enabled 로 설정된 네임스페이스를 선택하는 부분이 포함되어 있음을 확인할 수 있습니다. Admission Controller에 대한 상세한 내용은 다른 글에서 다루겠습니다.
$ kubectl describe mutatingwebhookconfigurations istio-sidecar-injector -n istio-system
Name: istio-sidecar-injector
...
Namespace Selector:
Match Expressions:
Key: istio-injection
Operator: In
Values:
enabled
...
2. default policy
default policy는 istio-sidecar-injector라는 ConfigMap의 policy 필드로 관리되며, 기본값으로는 enabled로 되어 있습니다.
$ kubectl describe cm istio-sidecar-injector -n istio-system | grep policy
policy: enabled
이 값을 disbaled로 변경한 뒤, 이전에 테스트한 것과 같이 새로운 Pod를 생성해 보겠습니다.
$ kubectl edit cm istio-sidecar-injector -n istio-system
apiVersion: v1
data:
config: |-
# defaultTemplates defines the default template to use for pods that do not explicitly specify a template
defaultTemplates: [sidecar]
policy: disabled # default: enabled
기존에 생성한 Pod를 지우고 새로 Pod 생성한 결과는 다음과 같습니다.
$ kubectl delete po nginx
pod "nginx" deleted
$ kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
pod/nginx created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3s
$ kubectl get ns -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 3h18m enabled
istio-system Active 3h13m
kube-node-lease Active 3h18m
kube-public Active 3h18m
kube-system Active 3h18m
네임스페이스의 istio-injection 레이블은 enabled로 되어 있지만 이번에는 프록시 컨테이너가 생성되지 않았습니다. sidecar injection이 필요없다면 네임스페이스의 레이블을 disabled로 조정하면 될 것 같은데, 그렇다면 policy는 대체 어떤 역할을 하는걸까요?
비슷해 보이지만 두 설정은 확연히 다른 역할을 합니다. 네임스페이스의 istio-injection 레이블은 프록시 컨테이너 생성이 필요한 네임스페이스에 지정하여 sidecar injection이 수행 가능하도록 만듭니다. default policy는 네임스페이스의 레이블이 enabled로 설정되었다 하더라도 sidecar injection이 동작하지 않도록 기본 동작을 설정할 수 있습니다. 이러한 경우 아래 이어지는 per-pod override annotation 설정과 함께 사용하여 네임스페이스 전체 단위가 아닌 Pod(혹은 Deployment) 단위로 세밀하게 sidecar injection 설정을 할 수 있습니다.
3. per-pod override annotation
Pod별 annotation을 추가하여 sidecar injection 설정을 할 수도 있습니다. 테스트를 위해 simple-pod.yaml 파일을 작성합니다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
annotations:
sidecar.istio.io/inject: "true"
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
기존 Pod를 삭제하고 위에서 작성한 매니페스트 파일로 Pod를 생성해 보겠습니다. 현재까지의 네임스페이스 레이블 및 default policy는 아래와 같이 구성되어 있습니다.
- default 네임스페이스의 레이블 istio-injection=enabled
- default policy 값 disabled
- pod annotation의 sidecar.istio.io/inject: "true"
$ kubectl delete po nginx
pod "nginx" deleted
$ kubectl apply -f simple-pod.yaml
pod/nginx created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 2/2 Running 0 3s
default policy 값이 disabled 이지만 Pod annotation을 추가하여 sidecar injection이 수행되었습니다. 이와 유사하게 default policy 값을 enabled로 둔 채 sidecar.istio.io/inject: "false" 로 설정하여 해당 네임스페이스에서 sidecar injection이 이 일반적으로 이루어지되, 특정 Pod는 sidecar injection이 이루어지지 않도록 구성할 수도 있습니다.
Summary - Configuration
기본적으로 sidecar injection은 위에서 살펴본 3가지 설정에 의해 수행됩니다. 그러므로 각 설정값에 따라 sidecar injection이 실제로 수행되는지 아닌지 헷갈리기 쉽습니다. 최종적으로 sidecar injection이 이루어지는지에 대한 진리표는 여기서 확인할 수 있습니다. 지금까지의 내용을 정리하자면 다음과 같습니다.
- sidecar injection이 필요한 네임스페이스에는 istio-injection=enabled 레이블을 추가합니다.
- pod annotation 설정이 없을 때 기본 동작은 istio-sidecar-injector ConfigMap의 policy로 설정할 수 있습니다.
- Pod(혹은 Deployment) 별 annotation으로 sidecar injection 설정을 할 수 있습니다. 단, sidecar injection이 수행되려면 네임스페이스에 istio-injection=enabled 레이블이 포함되어야 합니다.
Security rules
1. sidecars cannot be injected in the kube-system or kube-public namespaces
이 규칙은 Configuration 설정을 올바르게 해 두더라도 kube-system 및 kube-public 네임스페이스에서는 sidecar injection이 이루어지지 않는다는 규칙입니다. 직접 kube-system 네임스페이스에 sidecar injection이 이루어지도록 label 설정 후 Pod를 하나 생성하여 sidecar injection이 이루어지는지를 살펴보겠습니다.
먼저 kube-system 네임스페이스에 istio-injection 레이블을 enabled 로 설정하여 sidecar injection을 설정합니다.
$ kubectl label ns kube-system istio-injection=enabled
namespace/kube-system labeled
$ kubectl get ns kube-system -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
kube-system Active 22h enabled
또한 Configuration을 테스트하며 변경했던 default policy를 다시 기본값(enabled)으로 변경합니다.
$ kubectl edit cm istio-sidecar-injector -n istio-system
apiVersion: v1
data:
config: |-
# defaultTemplates defines the default template to use for pods that do not explicitly specify a template
defaultTemplates: [sidecar]
policy: enabled
그 다음 kube-system 네임스페이스에 nginx Pod를 하나 생성해보겠습니다.
$ kubectl run nginx --image=nginx -n kube-system
pod/nginx created
$ kubectl get po nginx -n kube-system
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 11s
$ kubectl get po nginx -n kube-system -o jsonpath="{.spec.containers[*].name}{'\n'}"
nginx
$ kubectl delete po nginx -n kube-system
pod "nginx" deleted
같은 조건에서 default 네임스페이스에서는 sidecar injection이 수행되었지만, kube-system 네임스페이스에서는 프록시 컨테이너가 생성되지 않았습니다. 이는 Security rule에 따라 sidecar injection이 이루어지지 않았음을 의미합니다. kube-public 네임스페이스에서 테스트했을 때에도 동일한 결과를 보여줍니다.
2. sidecars cannot be injected into pods that use the host network
테스트를 위해 호스트 네트워크를 사용하지 않는 Pod와 호스트 네트워크를 사용하는 Pod를 하나씩 생성하여 비교해보겠습니다. 먼저 default 네임스페이스에 istio-injection 설정을 하여 sidecar injection이 가능하도록 설정해둡니다.
$ kubectl label ns default istio-injection=enabled --overwrite
namespace/default labeled
그 다음 아래 양식을 참고하여 호스트 네트워크를 사용하지 않는 일반적인 Pod(nginx) 와 호스트 네트워크를 사용하는 Pod(nginx-hostnetwork)를 생성할 pod.yaml 파일을 작성합니다. 호스트 네트워크를 사용하는 Pod는 .spec.hostNetwork 필드 값을 true로 설정합니다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
hostNetwork: false
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-hostnetwork
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
위에서 생성한 yaml 파일로 Pod를 생성합니다. nginx Pod는 컨테이너가 2개, nginx-hostnetwork Pod는 컨테이너가 1개 생성된 것을 확인할 수 있습니다. default 네임스페이스에 sidecar injection 설정을 해 두었지만, 호스트 네트워크를 사용하는 nginx-hostnetwork Pod에서만 프록시 컨테이너가 추가되지 않았음을 확인할 수 있습니다.
$ kubectl apply -f pod.yaml
pod/nginx created
pod/nginx-hostnetwork created
$ kubectl get po -n default
NAME READY STATUS RESTARTS AGE
nginx 2/2 Running 0 16s
nginx-hostnetwork 1/1 Running 0 16s
$ kubectl get po nginx -n default -o jsonpath="{.spec.containers[*].name}{'\n'}"
nginx istio-proxy
$ kubectl get po nginx-hostnetwork -n default -o jsonpath="{.spec.containers[*].name}{'\n'}"
nginx
아래과 같이 테스트 용도로 생성한 Pod를 삭제할 수 있습니다.
$ kubectl delete -f pod.yaml
pod "nginx" deleted
pod "nginx-hostnetwork" deleted
Summary - Security rules
Configuration 설정이 올바르게 되어있다 하더라도 kube-system 혹은 kube-public 와 같이 특정 네임스페이스에는 sidecar injection이 불필요하게 일어나지 않았고, Pod가 호스트 네트워크를 사용하는 경우에도 sidecar injection이 수행되지 않도록 하는 예외 규칙이 존재함을 확인해 보았습니다.
References
[1] https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/
[2] https://istio.io/latest/docs/ops/configuration/mesh/injection-concepts/
[3] https://cloud.google.com/traffic-director/docs/auto-gke-options?hl=ko#injection_overrides
'Software Architecture > Service Mesh' 카테고리의 다른 글
[Istio] istioctl 로 쿠버네티스 클러스터에 istio 설치하기 (0) | 2022.03.02 |
---|
댓글