Istio Sidecar
Istio의 Sidecar 기법을 분석한다.
1. Istio Sidecar
![[Figure 1] Istio Sidecar](/blog-software/docs/theory-analysis/istio-sidecar/images/istio-sidecar.png)
[Figure 1] Istio Sidecar
Istio의 Sidecar 기법은 각 Pod 마다 전용 Proxy Server를 띄우는 기법을 의미한다. [Figure 1]은 Istio Sidecar 기법의 Architecture를 나타내고 있다. Sidecar는 Pod으로 전달되는 모든 Inbound Packet을 대신 수신한 다음, 처리후 Pod의 App Container에게 대신 전송하는 역할을 수행한다. 또한 Sidecar는 App Container에서 밖으로 전송되는 모든 Packet을 대신 수신한 다음, 처리후 Pod의 외부로 대신 전송하는 역할을 수행한다.
이러한 Sidecar의 특징 때문에 Sidecar는 Packet 전송에 필요한 모든 정보를 알고 있어야 한다. Sidecar는 이러한 Packet 전송에 필요한 정보를 Istiod라고 불리는 중앙 Controller로 부터 받는다. Istiod가 Sidecar에게 전송하는 정보에는 Pod에서 동작하는 App이 제공하는 Service 정보, Packet 송수신 허용 여부를 결정하는 Policy 정보, Packet 암호화를 위한 인증서 정보등이 포함되어 있다. 여기서 Service는 Kubernetes의 Service Object 또는 Istio의 Virtual Service Object를 의미한다. Sidecar는 Istiod로 부터 받은 정보들을 바탕으로 Packet Load Balancing, Packet Encap/Decap, Rate Limit, Circuit Breaker 등의 역할을 수행한다.
Sidecar는 실제로 App Pod안에서 별도의 Container로 동작하며, Sidecar Container안에는 실제 Sidecar 역할을 수행하는 Envoy와 Istiod로부터 정보를 받아 Envoy를 설정하는 pilot-agent로 구성되어 있다. Envoy는 HTTP, gRPC, TCP등 다양한 Protocol을 지원하며 따라서 App에서도 다양한 Protocol을 기반으로 Packet을 주고받을 수 있다. pilot-agent 및 Envoy는 자체적으로 Metric 정보를 수집하며, 수집된 Metric 정보는 Prometheus, Jaeger와 같은 외부 Component에 의해서 수집된다.
1.1. Sidecar Injection
Sidecar는 Pod안에서 Container로 동작한다. 따라서 Sidecar가 Pod안에서 Container로 Injection되어 동작할 수 있도록 설정해주어야 한다. Sidecar 설정은 각 Pod마다 수동으로 설정할 수도 있고, Namespace 단위로 설정하여 Namespace안에서 생성되는 모든 Pod에서 Sidecar가 동작하도록 설정할 수 있다.
1.1.1. Manual 설정
|
|
|
|
istioctl kube-inject
명령어를 통해서 Deployment, StatefulSet, DaemonSet등의 Pod를 관리하는 Object의 Manifest를 변경하여 Pod에 Sidecar가 생성되도록 만든다. [File 1]은 istioctl kube-inject
명령어 적용 전 nginx Deployment Manifest 파일을 나타내고 있고, [File 2]는 istioctl kube-inject
명령어 적용 후 nginx Deployment Manifest 파일을 나타내고 있다. Sidecar인 istio-proxy
Container가 추가되었고, istio-init
Init Container가 추가된 것을 확인할 수 있다.
|
|
[Shell 1]과 같이 istioctl kube-inject
명령어를 수행하면 Pod이 새로 생성되면서 Sidecar가 Injection 된다.
1.1.2. Namespace 설정
Label에 istio-injection=enabled
이 붙어있는 Namespace안에 Pod이 생성될 경우에는 Istio는 Sidecar Injection을 수행하여 Kubernetes의 사용자의 개입없이 Pod안에 Sidecar를 강제로 생성한다. Kubernetes 사용자의 개입없이 Sidecar Injection을 강제로 수행할 수 있는 이유는 Kubernetes에서 제공하는 Admission Controller 기능을 이용하기 때문이다.
Admission Controller의 Mutating Webhook 기능을 통해서 Pod 관련 Object 생성 요청이 Kubernetes API Server에게 전달되면, Kubernetes API Server는 해당 Pod 생성 요청을 Istiod에게 전송한다. Istiod는 Pod 생성 요청에 Sidecar 및 Init Container 설정을 추가(Mutating)한 다음에 Kubernetes API 서버에 전달하여 Sidecar와 Init Container가 생성되도록 한다.
1.2. Packet Capture
|
|
Sidecar가 Injection된 Pod으로 전송되는 모든 Inbound Packet이 Sidecar로 전송되는 이유, 또는 Sidecar가 Injection된 Pod에서 전송되는 모든 Outbound Packet이 Sidecar로 전송되는 이유는 Pod에 Sidecar와 같이 설정되는 Init Container 때문이다. Init Container는 Pod의 Network Namespace에 iptables
를 이용하여 Pod의 Inbound/Outbound Packet이 Sidecar로 전송되도록 Redirect하는 Rule을 설정한다.
[Shell 2]는 Sidecar가 Injection된 Pod안에서 iptables
DNAT Table을 조회한 결과이다. Pod의 Inbound Packet은 15006
Port를 통해서 Sidecar로 전송되고, Pod의 Outbound Packet은 15001
Port를 통해서 Sidecar로 전송된다. 15020
, 15090
Port는 Sidecar의 Prometheus Port로 이용하고 있고, 15020
Port는 Sidecar의 Health Check Port로 이용하고 있다. 따라서 관련된 Port들은 ISTIO-INBOUND
Chain에서 Redirect되지 않도록 설정되어 있다. [File 2]를 보면 Sidecar는 UID/GID 1337
로 동작하도록 설정되어 있는것을 확인할 수 있다. 따라서 UID/GID가 1337
로 동작하는 Sidecar가 전송하는 Packet은 Pod에서 Redirect되지 않도록 ISTIO-OUTPUT
Chain에 설정되어 있다.
Init Container는 iptables
Rule을 설정해야하기 때문에 [File 2]에서 NET-ADMIN
, NET-RAW
Capability를 갖고 동작되도록 설정되어 있는것을 확인할 수 있다. NET-ADMIN
, NET-RAW
Capability를 갖고 동작하는 Init Container는 보안상 취약점이 될 수 있다. 이러한 문제를 해결하기 위해서 Istio는 Istio CNI Plugin을 제공한다. Istio CNI Plugin을 통해서 Pod의 Network Namespace에 iptables
기반 DNAT Rule을 Host에서 설정할 수 있다.
1.3. Traffic Load Balancing
Sidecar는 Service에게 Traffic 전송시 kube-proxy가 설정하는 iptables/IPVS Rule을 이용하여 Load Balancing을 수행하지 않는다. Sidecar는 Istiod를 통해서 받는 Service 및 Service와 연결되어 있는 Pod(Endpoint)의 정보를 받아서 직접 L7 Level의 Load Balancing을 수행한다. 따라서 L3/L4 Level의 Load Balancing을 수행하는 kube-proxy에서는 이용할 수 없는 다양한 Load Balancing 기법을 Istio의 Sidecar를 통해서 적용할 수 있다. Round Robin, Least Connection, Random과 같은 기본적인 Load Balancing 기법부터 시작하여, L7 기반의 Consistent Hash, Locality Base 기법들도 이용할 수 있다.
다만 Istio 환경에서도 여전히 kube-proxy는 필수적인 요소이다. Sidecar가 존재하지 않는 Pod는 여전히 Service에게 Traffic 전송시에 kube-proxy를 이용하며, Sidecar가 Istiod와 통신시에도 kube-proxy를 통해서 Istiod에 접근하기 때문이다.
1.4. Access Log
Sidecar는 기본적으로 Access Log를 남기지 않지만, 별도의 설정을 통해서 Access Log 활성하하여 남기도록 설정할 수 있다. Access Log를 활성화하면 많은양의 Log가 발생하기 때문에 평상시에는 Access Log를 비활성화 하는게 좋지만, Network 관련 Trouble Shooting 시에는 Access Log를 일시적으로 활성화하여 활용할 수 있다. Access Log를 활성화 할 수 있는 방법은 Mesh Config에 설정하는 방법과, Telemetry Object를 이용하는 방법이 있다.
|
|
[File 3]은 Istio의 Mesh Config에서 Access Log를 활성화하는 방법이다. Access Log를 stdout에 출력하도록 설정하면 kubectl logs
명령어를 통해서 Sidecar의 Access Log를 확인할 수 있으며, fluentd와 같은 Log Aggregator에서도 수집할 수 있게된다. Mesh Config 방식은 모든 Sidecar에 적용되는 Global 방식만 지원하기 때문에, 특정 Pod의 Sidecar의 Access Log만을 남기는 설정은 불가능하며 이 경우에는 Telemetry Object를 이용하여 설정해야 한다.
|
|
[File 4]는 Telemetry Object를 이용하여 Access Log를 활성한 예제를 나타내고 있다. Global, Namespace, Pod 단위로 Access Log를 설정할 수 있으며, [File 4] 예제는 default
Namespace에 존재하는 app: productpage
Label을 갖는 Pod의 Sidecar의 Access Log만 활성화하도록 설정한 예제이다. matchLabels
를 통해서 Pod를 선택할 수 있으며, matchLabels
가 존재하지 않으면 Telemetry Object가 존재하는 Namespace안에 있는 모든 Pod의 Sidecar의 Access Log가 활성화된다. 만약 Telemetry Object가 존재하는 Namespace가 Istio의 Root Configuration Namespace인 istio-system
이라면, 해당 Telemetry Object는 Namespace에 관계없이 Global 설정이 적용된다.
|
|
[Shell 3]은 Sidecar의 Access Log의 예제를 나타낸다. 기본적으로 [%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS% \"%UPSTREAM_TRANSPORT_FAILURE_REASON%\" %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" %UPSTREAM_CLUSTER_RAW% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME%\n
형태로 출력하며, Telemetry Object를 이용하여 출력되는 Log의 포맷도 변경할 수 있다.
2. 참조
- https://istio.io/latest/docs/reference/config/networking/virtual-service/
- https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/
- https://istio.io/latest/docs/ops/deployment/requirements/
- https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings
- http://itnp.kr/post/istio-routing-api
- Access Log : https://istio.io/latest/docs/tasks/observability/logs/access-log/