ServiceMesh-初识Linkerd2.0(二)

ServiceMesh-初识Linkerd2.0(二)

十一月 01, 2018

     上一节说到在Kubernetes中,Linkerd在原服务的pod中的运行一个sidecar容器作为透明代理,可以理解为和原服务容器共享存储、网络等资源。代理负载接管进出服务容器的所有流量,收集遥测数据并执行相关策略。
     Linkerd2.0 给应用 Pod 注入的配置主要包括:

  1. InitContainer: linkerd-init,用于给Sidecar容器做初始化,从Kubernetes API和Linkerd控制平面拉取配置,设置iptables端口转发。
  2. SideCarContainer: linkerd-proxy,在应用的每个pod中运行透明代理。
    下面我们来依次分析这两个容器。

linkerd-init解析:

     Linkerd2.0在Pod中注入的Init容器名为linkerd-init,该容器存在的意义就是让linkerd-proxy容器可以拦截所有的进出Pod的流量,即将入站流量重定向到Sidecar,再拦截应用容器的出站流量经过Sidecar处理后再出站。
在执行linkerd inject注入完成后的 YAML 文件和原应用YAML对比,看到了Init容器的启动参数:

1
--incoming-proxy-port 4143 --outgoing-proxy-port 4140 --proxy-uid 2102 --inbound-ports-to-ignore 4190,4191

看下linkerd-init的dockerfile,可以看到linkerd-init容器是go语言编写的,采用分步编译,入口是运行编译生成的/usr/local/bin/proxy-init这个二进制文件,其实看下源代码里面都是对iptables中NAT表的操作。istio里运行的是istio-iptables.sh的脚本

我们看下/usr/local/bin/proxy-init的用法,翻译过来如下:

现在我们来启动参数话的作用是:

1
/usr/local/bin/proxy-init --incoming-proxy-port 4143 --outgoing-proxy-port 4140 --proxy-uid 2102 --inbound-ports-to-ignore 4190,4191

所有端口流量都要被重定向,除了进入4190和4191的端口的流量(原因在后面解释),进入服务容器的流量重定向到4143端口,从服务容器出去的流量由4140端口拦截,代理使用UID为2102的身份运行。所以说linkerd-init容器实际上就是创建iptables规则让linkerd-proxy容器可以拦截所有的进出Pod的流量,即将入站流量重定向到 Sidecar,再拦截应用容器的出站流量经过 Sidecar 处理后再出站。

linkerd-proxy解析:

     因为 Init 容器初始化完毕后就会自动终止,所以无法登陆到容器中查看 iptables 信息,但是 Init 容器初始化结果会保留到应用容器和 Sidecar 容器中。如上所述Init容器启动参数是将出入pod流量重定向到代理,因此只创建了nat表。docker登陆到emoji-svc的容器中查看iptables配置,nat表的所有规则如下:

而filter、raw、mangle三张表为空

下面我们来分析下nat表中注入的规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
root@emoji-f87d7f847-nqh5k:/# iptables -L -t nat -v

# PREROUTING 链:用于目标地址转换(DNAT),所有入站流量要先进入PREROUTING 链,将所有入站流量跳转到 PROXY_INIT_REDIRECT 链上
Chain PREROUTING (policy ACCEPT 67 packets, 4020 bytes)
pkts bytes target prot opt in out source destination
69 4140 PROXY_INIT_REDIRECT all -- any any anywhere anywhere /* proxy-init/install-proxy-init-prerouting/1541098423 */

# INPUT 链:处理输入数据包,不做处理继续 OUTPUT 链
Chain INPUT (policy ACCEPT 69 packets, 4140 bytes)
pkts bytes target prot opt in out source destination

# OUTPUT 链:将所有出站数据包跳转到 PROXY_INIT_OUTPUT 链上
Chain OUTPUT (policy ACCEPT 3 packets, 240 bytes)
pkts bytes target prot opt in out source destination
3 240 PROXY_INIT_OUTPUT all -- any any anywhere anywhere /* proxy-init/install-proxy-init-output/1541098423 */

# POSTROUTING 链:所有数据包流出网卡时都要先进入POSTROUTING 链,内核根据数据包目的地判断是否需要转发出去,此处未做任何处理
Chain POSTROUTING (policy ACCEPT 3 packets, 240 bytes)
pkts bytes target prot opt in out source destination

# PROXY_INIT_OUTPUT 链:选择需要重定向到代理的出站流量,为了避免流量在该 Pod 中无限循环,所有UID 2102 的流量和从loopback口出去的流量都返回到调用点下一条规则处开始执行(POSTROUTING),其他所有出Pod的TCP流量重定向到4140端口
Chain PROXY_INIT_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
3 240 RETURN all -- any any anywhere anywhere owner UID match 2102 /* proxy-init/ignore-proxy-user-id/1541098423 */
0 0 RETURN all -- any lo anywhere anywhere /* proxy-init/ignore-loopback/1541098423 */
0 0 REDIRECT tcp -- any any anywhere anywhere /* proxy-init/redirect-all-outgoing-to-proxy-port/1541098423 */ redir ports 4140

# PROXY_INIT_REDIRECT 链:选择需要重定向到代理的入站流量,进入4190和4191的端口的TCP流量都返回到调用点下一条规则处开始执行(INPUT),其他所有进入Pod的TCP流量重定向到4143端口
Chain PROXY_INIT_REDIRECT (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:4190 /* proxy-init/ignore-port-4190/1541098423 */
67 4020 RETURN tcp -- any any anywhere anywhere tcp dpt:4191 /* proxy-init/ignore-port-4191/1541098423 */
2 120 REDIRECT tcp -- any any anywhere anywhere /* proxy-init/redirect-all-incoming-to-proxy-port/1541098423 */ redir ports 4143

总结:
进入pod的流量执行顺序:PREROUTING –> PROXY_INIT_REDIRECT –> INPUT,进入4190和4191的端口的TCP流量都不处理,其他所有进入Pod的TCP流量重定向到4143端口
出pod的流量执行顺序:OUTPUT –> PROXY_INIT_OUTPUT –> POSTROUTING,为了避免流量在该 Pod 中无限循环,所有UID 2102 的流量和从loopback口出去的流量都不处理,其他所有出Pod的TCP流量重定向到4140端口

     最后看下linkerd-proxy都做了什么,如下图,注入到Deployment中的配置:

proxy容器启动入口/linkerd/linkerd2-proxy,没有可选参数,需要注入固定的几个关键环境变量

linkerd2-proxy运行必须要先获取pod所在的namespace,Proxy容器的日志级别,允许代理绑定Pod网卡并开始代理接收流量需要的最大时间,proxy到control上注册地址,control监听端口4190,proxy向Prometheus暴露metrics的端口4191,所以前面这两个端口入站流量都不代理,入站流量代理端口4143,出站流量代理端口4140

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@registry harbor]# docker run -it heisenbergye/proxy:edge-18.10.4 /linkerd/linkerd2-proxy --help
ERR! linkerd2_proxy::config LINKERD2_PROXY_POD_NAMESPACE is not set
configuration error: InvalidEnvVar

- env:
- name: LINKERD2_PROXY_LOG
value: warn,linkerd2_proxy=info
- name: LINKERD2_PROXY_BIND_TIMEOUT
value: 10s
- name: LINKERD2_PROXY_CONTROL_URL
value: tcp://proxy-api.linkerd.svc.cluster.local:8086
- name: LINKERD2_PROXY_CONTROL_LISTENER
value: tcp://0.0.0.0:4190
- name: LINKERD2_PROXY_METRICS_LISTENER
value: tcp://0.0.0.0:4191
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
value: tcp://127.0.0.1:4140
- name: LINKERD2_PROXY_INBOUND_LISTENER
value: tcp://0.0.0.0:4143
- name: LINKERD2_PROXY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace

最后proxy还有个存活检测,通过HTTP GET请求127.0.0.1:4191/metrics,指定 kubelet 在该执行第一次探测之前需要等待10秒钟,可见proxy启动肯定在10s内

1
2
3
4
5
livenessProbe:
httpGet:
path: /metrics
port: 4191
initialDelaySeconds: 10

     今天就到先这里,我们理清了SideCar 注入的整个运作过程,下一节我们详细的来了解Linkerd2.0的功能,我们又可以用它来做些什么。