Istio Sidecar Proxy
Analyze Istio’s Sidecar Proxy.
1. Istio Sidecar Proxy
![[Figure 1] Istio Sidecar](/blog-software/docs/theory-analysis/istio-sidecar-proxy/images/istio-sidecar.png)
[Figure 1] Istio Sidecar
Istio’s Sidecar Proxy technique refers to a method of launching a dedicated Proxy Server for each Pod. [Figure 1] shows the Architecture of Istio Sidecar Proxy. The Sidecar Proxy receives all Inbound Packets destined for the Pod, processes them, and then forwards them to the App Container in the Pod. Additionally, the Sidecar Proxy receives all Packets sent from the App Container, processes them, and then forwards them outside the Pod.
The Sidecar Proxy must know all the information necessary for packet transmission. The Sidecar Proxy receives this information necessary for packet transmission from a central Controller called Istiod. The information that Istiod sends to the Sidecar Proxy includes Service information provided by the App running in the Pod, Policy information that determines whether packet transmission/reception is allowed, and certificate information for packet encryption. Here, Service refers to Kubernetes Service Objects or Istio Virtual Service Objects. Based on the information received from Istiod, the Sidecar Proxy performs various roles such as Packet Load Balancing, Packet Encap/Decap, Rate Limit, and Circuit Breaker.
The Sidecar Proxy actually runs as a separate Container inside the App Pod, and the Sidecar Proxy Container consists of Envoy, which performs the actual Sidecar Proxy role, and pilot-agent, which receives information from Istiod and configures Envoy. Envoy supports various protocols such as HTTP, gRPC, and TCP, so Apps can exchange packets based on various protocols. pilot-agent and Envoy collect Metric information themselves, and the collected Metric information is collected by external Components such as Prometheus and Jaeger.
1.1. Sidecar Proxy Injection
The Sidecar Proxy runs as a Container inside the Pod. Therefore, it must be configured so that the Sidecar Proxy can be injected and run as a Container inside the Pod. Sidecar Proxy configuration can be set manually for each Pod, or it can be set at the Namespace level so that the Sidecar Proxy runs in all Pods created within the Namespace.
1.1.1. Manual Configuration
| |
| |
The istioctl kube-inject command modifies the Manifest of Objects that manage Pods such as Deployment, StatefulSet, and DaemonSet so that Sidecar Proxy is created in the Pod. [File 1] shows the nginx Deployment Manifest file before applying the istioctl kube-inject command, and [File 2] shows the nginx Deployment Manifest file after applying the istioctl kube-inject command. You can see that the istio-proxy Container, which is the Sidecar Proxy, has been added, and the istio-init Init Container, which is responsible for the Network configuration of the Sidecar Proxy, has been added.
| |
When you execute the istioctl kube-inject command as shown in [Shell 1], the Sidecar Proxy is injected when the Pod is newly created.
1.1.2. Namespace Configuration
When a Pod is created in a Namespace with the label istio-injection=enabled, Istio performs Sidecar Proxy Injection to forcibly create a Sidecar Proxy in the Pod without intervention from Kubernetes users. The reason why Sidecar Proxy Injection can be forcibly performed without intervention from Kubernetes users is because it uses the Admission Controller function provided by Kubernetes.
When a Pod-related Object creation request is sent to the Kubernetes API Server through the Mutating Webhook function of the Admission Controller, the Kubernetes API Server sends the Pod creation request to Istiod. Istiod adds (Mutating) Sidecar Proxy and Init Container settings to the Pod creation request and then delivers it to the Kubernetes API server so that Sidecar Proxy and Init Container are created.
1.2. Packet Capture
| |
The reason why all Inbound Packets sent to a Pod with Sidecar Proxy injected are sent to the Sidecar Proxy, or why all Outbound Packets sent from a Pod with Sidecar Proxy injected are sent to the Sidecar Proxy, is because of the Init Container that is configured together with the Sidecar Proxy. The Init Container sets Rules in the Pod’s Network Namespace using iptables to redirect the Pod’s Inbound/Outbound Packets to the Sidecar.
[Shell 2] is the result of querying the iptables NAT Table inside a Pod with Sidecar Proxy injected. Pod’s Inbound Packets are sent to the Sidecar Proxy through Port 15006, and Pod’s Outbound Packets are sent to the Sidecar Proxy through Port 15001. Ports 15020 and 15090 are used as Prometheus Ports for the Sidecar Proxy, and Port 15020 is used as the Health Check Port for the Sidecar Proxy. Therefore, related Ports are configured not to be redirected in the ISTIO-INBOUND Chain. Looking at [File 2], you can see that the Sidecar Proxy is configured to run with UID/GID 1337. Therefore, Packets sent by the Sidecar Proxy running with UID/GID 1337 are configured in the ISTIO-OUTPUT Chain not to be redirected from the Pod.
Since the Init Container must set iptables Rules, you can see in [File 2] that it is configured to run with NET-ADMIN and NET-RAW Capabilities. Init Containers running with NET-ADMIN and NET-RAW Capabilities can be a security vulnerability. To solve this problem, Istio provides the Istio CNI Plugin. Through the Istio CNI Plugin, iptables-based NAT Rules can be set in the Pod’s Network Namespace from the Host.
1.3. Traffic Load Balancing
The Sidecar Proxy does not perform Load Balancing using iptables/IPVS Rules set by kube-proxy when sending Traffic to Services. The Sidecar Proxy receives information about Services and Pods (Endpoints) connected to Services through Istiod and directly performs L7 Level Load Balancing. Therefore, various Load Balancing techniques that cannot be used in kube-proxy, which performs L3/L4 Level Load Balancing, can be applied through Istio’s Sidecar Proxy. Starting with basic Load Balancing techniques such as Round Robin, Least Connection, and Random, L7-based techniques such as Consistent Hash and Locality Base can also be used.
However, kube-proxy is still an essential element in Istio environments. Pods without Sidecar Proxy still use kube-proxy when sending Traffic to Services, and Sidecar Proxy also accesses Istiod through kube-proxy when communicating with Istiod.
1.4. Access Log
The Sidecar Proxy does not leave Access Logs by default, but it can be configured to leave Access Logs by activating them through separate settings. When Access Logs are activated, a large amount of Logs are generated, so it is good to disable Access Logs during normal times, but Access Logs can be temporarily activated and utilized during Network-related Troubleshooting. Methods to activate Access Logs include setting them in Mesh Config and using the Telemetry Object.
| |
[File 3] is a method to configure Access Logs to be output to stdout in Istio’s Mesh Config. When Access Logs are configured to be output to stdout, you can check the Sidecar’s Access Logs through the kubectl logs command, and they can also be collected by Log Aggregators such as fluentd. The Mesh Config method only supports Global methods that apply to all Sidecar Proxies, so it is impossible to configure only specific Pod’s Sidecar Proxy Access Logs, and in this case, you must use the Telemetry Object to configure it.
| |
[File 4] shows an example of activating Access Logs using the Telemetry Object. Access Logs can be configured at Global, Namespace, and Pod levels, and the [File 4] example is an example configured to activate Access Logs only for Sidecar Proxies of Pods with the app: productpage Label existing in the default Namespace. Pods can be selected through matchLabels, and if matchLabels does not exist, Access Logs of Sidecar Proxies of all Pods in the Namespace where the Telemetry Object exists are activated. If the Namespace where the Telemetry Object exists is istio-system, which is Istio’s Root Configuration Namespace, the Telemetry Object applies Global settings regardless of Namespace.
| |
[Shell 3] shows an example of the Sidecar’s Access Log. By default, it outputs in the format [%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.
| |
If you want to change the format of Access Logs, you can set accessLogFormat in Mesh Config as shown in [File 5]. [File 5] is an example configured to output Retry counts by adding the UPSTREAM_REQUEST_ATTEMPT_COUNT field.
2. References
- 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/