# Istio 端口 与 组件

Istio 的每个组件都监听一堆端口。对于初学者，可能很难弄明白每个端口的作用。这里，用 {ref}`图：Istio端口与组件` 说明 Istio 在默认的部署下，各组件的通讯端口和相关的功能。


:::{figure-md} 图：Istio端口与组件
:class: full-width

<img src="istio-ports-components.assets/istio-ports-components.drawio.svg" alt="Istio端口与组件">

*图：Istio 端口与组件*  
:::
*[用 Draw.io 打开](https://app.diagrams.net/?ui=sketch#Uhttps%3A%2F%2Fenvoy-insider.mygraphql.com%2Fzh_CN%2Flatest%2F_images%2Fistio-ports-components.drawio.svg)*

上图需要说明的是：
- istio-proxy 容器与 Pod内其它容器(app container) 共享同一 Linux `network namespace`。 
- `network namespace` 是内核内用于隔离多个不同网络配置的技术。其中一个配置就是 `netfilter`，即我们常说的 `iptables`。我们将在后面说说它的故事。

## 监听端口

可以用以下方式查看监听的端口：

```bash

$ nsenter -n -t $PID_OF_ENVOY
$ ss -ln

u_str    LISTEN     etc/istio/proxy/SDS 34782                        * 0            users:(("pilot-agent",pid=3406,fd=13))                                             
u_str    LISTEN     etc/istio/proxy/XDS 34783                        * 0            users:(("pilot-agent",pid=3406,fd=16))                                             
u_str    ESTAB      etc/istio/proxy/XDS 1379729                      * 1379728      users:(("pilot-agent",pid=3406,fd=8))                                              
u_str    ESTAB                        * 1379728                      * 1379729      users:(("envoy",pid=3555,fd=37))                                                   
u_str    ESTAB      etc/istio/proxy/SDS 45274                        * 46319        users:(("pilot-agent",pid=3406,fd=15))                                             
u_str    ESTAB                        * 46319                        * 45274        users:(("envoy",pid=3555,fd=19))                                                   
tcp      LISTEN                 0.0.0.0:15021                  0.0.0.0:*            users:(("envoy",pid=3555,fd=40),("envoy",pid=3555,fd=34),("envoy",pid=3555,fd=22)) 
tcp      LISTEN                 0.0.0.0:15090                  0.0.0.0:*            users:(("envoy",pid=3555,fd=39),("envoy",pid=3555,fd=33),("envoy",pid=3555,fd=21)) 
tcp      LISTEN               127.0.0.1:15000                  0.0.0.0:*            users:(("envoy",pid=3555,fd=18))                                                   
tcp      LISTEN                 0.0.0.0:15001                  0.0.0.0:*            users:(("envoy",pid=3555,fd=41),("envoy",pid=3555,fd=35),("envoy",pid=3555,fd=31)) 
tcp      LISTEN               127.0.0.1:15004                  0.0.0.0:*            users:(("pilot-agent",pid=3406,fd=17))                                             
tcp      LISTEN                 0.0.0.0:15006                  0.0.0.0:*            users:(("envoy",pid=3555,fd=42),("envoy",pid=3555,fd=36),("envoy",pid=3555,fd=32)) 
tcp      ESTAB           172.21.206.227:40560            10.108.217.90:15012        users:(("pilot-agent",pid=3406,fd=19))                                             
tcp      ESTAB           172.21.206.227:43240            10.108.217.90:15012        users:(("pilot-agent",pid=3406,fd=14))                                             
tcp      LISTEN                       *:15020                        *:*            users:(("pilot-agent",pid=3406,fd=12))                                             
tcp      ESTAB                127.0.0.1:35256                127.0.0.1:15020        users:(("envoy",pid=3555,fd=43))                                                   
tcp      ESTAB                127.0.0.1:35238                127.0.0.1:15020        users:(("envoy",pid=3555,fd=20))                                                   
tcp      ESTAB       [::ffff:127.0.0.1]:15020       [::ffff:127.0.0.1]:35238        users:(("pilot-agent",pid=3406,fd=6))                                              
tcp      ESTAB       [::ffff:127.0.0.1]:15020       [::ffff:127.0.0.1]:35256        users:(("pilot-agent",pid=3406,fd=18))                                             
```

## iptables

```
$ iptables-save

# Generated by iptables-save v1.8.7 on Fri Dec 16 16:18:31 2022
*nat
:PREROUTING ACCEPT [104490:5433496]
:INPUT ACCEPT [105123:5471476]
:OUTPUT ACCEPT [24745:1633905]
:POSTROUTING ACCEPT [42627:2706825]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT
```

## 连接
{ref}`图：Istio端口与组件` 中包含了一些在 Istio 中时的 pod 的 TCP 连接的说明（见其中的 `ss` 命令输出）。在此不再赘述。


## 运维杂项

### 抓包

#### Sidecar 抓包

{ref}`图：Istio端口与组件` 中包含了一些在 Istio 中时行 tcpdump 的说明。在此不再赘述。要补充一下是的 tcpdump 的抓包点。因为，这个点影响了，tcpdump 的过滤条件与输出。主要是这个点与 iptables 的 redirect 规则生效的前后问题。

> tcpdump capture pinpoint:
>
> tcpdump will see inbound traffic before iptables, but will see
> outbound traffic only after the firewall has processed it.
>
> As a matter of fact, tcpdump is the first software found after the wire (and the NIC, if you will) on the way IN, and the last one on the way OUT.
>
> * Wire -> NIC -> tcpdump -> netfilter/iptables
> * iptables -> tcpdump -> NIC -> Wire



```bash
######## sidecar ########

sudo nsenter -t $PID_OF_SIDECAR_ENVOY -n -u

export ETH0_IP=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
export LOCAL_IP=$(ip addr show lo | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)

# inbound mTLS
sudo tcpdump -i eth0 -n -vvvv  "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"
# inbound 明文
sudo tcpdump -i lo -n -vvvv -A "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"

# outbound 明文
sudo tcpdump -i lo -n -vvvv -A  "((dst port 15001 and dst 127.0.0.1) or (dst portrange 20000-65535 and dst $ETH0_IP))"
# outbound mTLS
sudo tcpdump -i eth0 -n -vvvv -A  "((src portrange 20000-65535 and src $ETH0_IP) or (dst portrange 20000-65535 and dst $ETH0_IP))"
```



有一点比较麻烦的是，`outbound 明文`抓包，出向 IP packet 抓到的是 redirect 后的 127.0.0.1，入向 IP packet 抓到的是未 redirect 前的 ip 地址。如果你用 Wireshark 等工具分析。是无法 Follow TCP Stream 的。



#### Istio Gateway 抓包

一般，Istio Gateway 的 upstream （Cluster 内部），与 downstream（Cluster 外部）会在不同的 subnet，所以，可以用 CIDR 去区分。



首先，看看 kubernetes cluster 的 pod 的 CIDR 范围：

```bash
ps -ef | grep cidr
root      48587  20177  0 Dec08 ?        00:21:25 kube-controller-manager ... --cluster-cidr=192.168.0.0/12 ...--service-cluster-ip-range=10.96.0.0/12 ...
```



这时，如果尝试直接使用上面的参数会出错：

```bash
$ sudo tcpdump -i br0 -vvvv -A  net 192.168.0.0/12 #168 here
tcpdump: non-network bits set in "192.168.0.0/12"
```



发现，tcpdump 对 cidr 的格式要求比较严格，要求用首个可用 ip 地址段。使用 [https://cidr.xyz/](https://cidr.xyz/) 分析出 `192.168.0.0/12` 的首个可用 ip 地址段 为 `192.160.0.1`，固：

```bash
$ sudo tcpdump -i br0 -vvvv -A  net 192.160.0.0/12 #160(NOT 168) here
```



### 神奇的 127.0.0.6

出于非常多的原因，inbound 流量在由 Envoy 转到 app 时，Envoy 建立的 TCP 连接 bind 的 ip 地址是一个`ip addr`也查不到的，在 `lo` 接口上的 `127.0.0.6`。如果你好奇，可以看：

> Why the bind magic `127.0.0.6` ?
>
> * [Document the 127.0.0.6 magic #29603](https://github.com/istio/istio/issues/29603)
> * [Inbound Forwarding](https://docs.google.com/document/d/1j-5_XpeMTnT9mV_8dbSOeU7rfH-5YNtN_JJFZ2mmQ_w/edit#heading=h.xw1gqgyqs5b)
> * [Understanding the Sidecar Injection, Traffic Intercepting & Routing Process in Istio](https://jimmysong.io/en/blog/sidecar-injection-iptables-and-traffic-routing/)
> * [Upcoming networking changes in Istio 1.10](https://istio.io/latest/blog/2021/upcoming-networking-changes/)



## 一点感想

如果用传统 Linux 网络运维的方法，去解决 Istio 复杂实现下的网络问题，非常不直观和容易。在 Service Mesh 应用的同时，可观察性的工具和方法应该作出相应的变化。不然大规模使用后，问题的解决将花费相当大的代价。



