ServiceMesh-初识Linkerd2.0(三)

ServiceMesh-初识Linkerd2.0(三)

十一月 03, 2018

     我们先回顾下前面的内容,Linkerd2与Linkerd相比,它专用于Kubernetes集群中,并且比Linkerd更轻量级(基于Rust和Go,没有了JVM 等大内存的开销)。以sidecar的方式把高性能、轻量级代理跟实际服务的Pod运行在一起(这点跟Istio类似),而不再是DaemonSet。支持代理所有TCP流量,包括WebSockets和HTTP通道,支持图形报告HTTP/1.x、HTTP/2和gRPC流量的遥测数据,流量加密(TLS)等。所以Linkerd2主要作用总结下来就是解决云原生应用的监控、可靠性以及安全性,例如帮助用户快速定位服务故障是什么原因导致的、是不是延迟的问题、特定的端点是否返回了非法的响应或者含有不可靠的依赖等等。所以这一节我们主要讨论Linkerd 2.0的功能。

1. CLI功能介绍:

     前面我们使用linkerd install命令在Kubernetes集群中安装了Linkerd2的控制平面,下面我们着重来看他的解析

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
[root@master ~]# linkerd2 install -h

Flags:
--api-port uint Linkerd controller服务运行的端口,可以在安装Linkerd时修改 (默认 8086)
--control-port uint Pod和控制平面通信使用的代理端口(默认 4190,如果服务已占用此端口可以使用此选项修改 )
--controller-log-level string controller和web组件的日志等级 (默认 "info")
--controller-replicas uint 期望部署controller的副本数 (默认 1)
-h, --help help for install
--image-pull-policy string 镜像拉取策略(默认 "IfNotPresent")
--inbound-port uint 入站流量的代理端口 (默认 4143)
--init-image string 指定init容器的镜像名,比如heisenbergye/proxy-init
-v, --linkerd-version string 指定镜像tag (此版本默认 "edge-18.10.4")
--metrics-port uint 遥测代理端口 (默认 4191)
--outbound-port uint 出站流量的代理端口 (默认 4140)
--prometheus-replicas uint 期望部署prometheus的副本数 (default 1)
--proxy-auto-inject 测试阶段: 启用自动注入SideCar的webhook,默认不启用
--proxy-bind-timeout string 允许Proxy绑定Pod网卡并开始代理接收流量(默认 "10s")
--proxy-cpu string 代理容器的CPU的requests值
--proxy-image string 指定Proxy容器的镜像名,比如heisenbergye/proxy
--proxy-log-level string Proxy容器的日志级别 (默认 "warn,linkerd2_proxy=info")
--proxy-memory string Proxy容器的内存的requests值
--proxy-uid int 运行Proxy容器的用户ID (默认 2102)
--registry string 镜像仓库,比如docker.io/heisenbergye
--single-namespace 测试阶段: 配置控制平面只在其所在namespace操作,默认不启用
--skip-inbound-ports uintSlice 不希望被代理的入站流量的端口 (默认[4190,4191])
--skip-outbound-ports uintSlice 不希望被代理的出站流量的端口 (默认 [])
--tls string 测试阶段:启用流量加密,只能填 "optional",默认不启用
--web-replicas uint 期望部署web的副本数 (默认 1)

还有一些Kubernetes中的配置,比如指定kubeconfig和context,或者apiserver,还有前面说的指定namespace等,这里不再赘述。

     前文中步骤我们先用kubectl部署了应用,再使用linkerd inject命令对正在运行中的服务注入SideCar配置,通过使用Kubernetes的滚动更新功能生成新的Pod替代,新的Pod中先由init容器配置iptables来重定向所有流量,再在原服务旁添加一个Proxy SideCar容器,更新过程中应用的可用性不会受到影响。这里我们直接用linkerd inject命令对服务的YAML加入SideCar配置,再去创建带有SideCar的服务Pod。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@master ~]# linkerd2 inject emojivoto.yml | kubectl  apply -f -

hostNetwork: pods do not use host networking...............................[ok]
sidecar: pods do not have a proxy or initContainer already injected........[ok]
supported: at least one resource injected..................................[ok]
udp: pod specs do not include UDP ports....................................[ok]

Summary: 4 of 8 YAML document(s) injected
deployment/emoji
deployment/voting
deployment/web
deployment/vote-bot

namespace/emojivoto configured
deployment.apps/emoji created
service/emoji-svc created
deployment.apps/voting created
service/voting-svc created
deployment.apps/web created
service/web-svc created
deployment.apps/vote-bot created

如下图所示,在Linkerd Dashboard中显示服务的所有Pod都被meshed,说明服务已经成功加入到服务网格中。

linkerd inject命令分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@master ~]# linkerd2 inject --help

Flags:
--api-port uint Linkerd controller服务运行的端口,可以在安装Linkerd时修改 (默认 8086)
--control-port uint Pod和控制平面通信使用的代理端口(默认 4190,如果服务已占用此端口可以使用此选项修改 )
-h, --help help for inject
--image-pull-policy string 镜像拉取策略(默认 "IfNotPresent")
--inbound-port uint 入站流量的代理端口 (默认 4143)
--init-image string 指定init容器的镜像名,比如heisenbergye/proxy-init
-v, --linkerd-version string 指定镜像tag (此版本默认 "edge-18.10.4")
--metrics-port uint 遥测代理端口 (默认 4191)
--outbound-port uint 出站流量的代理端口 (默认 4140)
--proxy-bind-timeout string 允许Proxy绑定Pod网卡并开始代理接收流量(默认 "10s")
--proxy-cpu string 代理容器的CPU的requests值
--proxy-image string 指定Proxy容器的镜像名,比如heisenbergye/proxy
--proxy-log-level string Proxy容器的日志级别 (默认 "warn,linkerd2_proxy=info")
--proxy-memory string Proxy容器的内存的requests值
--proxy-uid int 运行Proxy容器的用户ID (默认 2102)
--registry string 镜像仓库,比如docker.io/heisenbergye
--skip-inbound-ports uintSlice 不希望被代理的入站流量的端口 (默认[4190,4191])
--skip-outbound-ports uintSlice 不希望被代理的出站流量的端口 (默认 [])
--tls string 启用流量加密时,只能填 "optional"

PS:自动流量加密(TLS)和自动注入SideCar功能还在测试阶段,安装Linkerd2默认不开启,只有edge版本才可以试用,期待这两个功能GA…………

自动流量加密

     Linkerd能用来为应用间通信自动协商TLS。当此功能被启用时,Linkerd自动在Proxy之间建立和认证安全、私密地连接,且不会中断未启用TLS的proxy之间不加密的通信。

启用TLS,需要重新安装linkerd,他会在控制平台运行一个CA,CA监控被Linkerd meshed的Pod的创建和更新,每个pod会生成一个私钥,颁发一张证书,并且私钥和证书会以Secret形式分配和pod

1
[root@master ~]# linkerd2 install --tls=optional | kubectl apply -f -

在控制平面配置启用TLS后,在向应用注入proxy的时候也要启用TLS

1
[root@master ~]# linkerd2 inject  --tls=optional emojivoto.yml | kubectl apply -f -

Dashboard, linkerd stat和linkerd tap命令可以显示加密流量的状态:




可以看到emoji、voting、web三个服务都是加密的,voting和web服务的成功率较低是因为web服务的延迟较高

自动注入SideCar

     自动注入SideCar,顾名思义,即在使用kubectl部署应用的时候自动添加Proxy到pod中。此功能需要用到Kubernetes的动态准入控制(Dynamic Admission Control,1.9+),所以需要安装一个Admission webhook,先确认Kubernetes集群的apiserver是否是1.9以上版本并且启用admissionregistration API

1
2
[root@master ~]# kubectl api-versions | grep admissionregistration
admissionregistration.k8s.io/v1beta1

该功能的特性如下:

  • 集成Linkerd CA(所以要同时启用TLS)来管理kube-apiserver和自动注入代理的webhook之间的安全、私密的连接
  • 所有需要的权限定义在一个叫linkerd-linkerd-proxy-injector的新创建的cluster role中,和控制平台的cluster role是分开的
  • 在有linkerd.io/auto-inject: enabled标签或者没有打标签的namespace中,当创建Deployment时都会被自动注入代理
  • 带有linked.io/auto-inject: disabled标签的Namespace不会启用该功能
  • 在启用该功能的标签的namespace中,Deployment中的pod模板带有linkerd.io/auto-inject: disabled或者linkerd.io/auto-inject: completed的标签都不会在pod注入代理;Deployment中的pod模板有linkerd.io/auto-inject: enabled标签或者没有打标签,pod里都会被自动注入代理
  • 该功能目前只能用在Deployment的资源类型中,像StatefulSet, DaemonSet, Pod资源类型中都不能使用

启用自动注入代理必须要同时启用–tls=optional和–proxy-auto-inject ,需要重新安装linkerd

1
linkerd2 install --tls=optional --proxy-auto-inject | kubectl apply -f -

确认安装成功,控制平面除添加了CA外还增加了一个proxy-injector webhook的Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@master ~]# kubectl get pod -n linkerd
NAME READY STATUS RESTARTS AGE
ca-7d67fd474f-62psq 2/2 Running 0 8h
controller-695f97b4d7-qzmbm 5/5 Running 0 8h
grafana-654bbc7857-q9zz4 2/2 Running 0 8h
prometheus-559bdd9dfc-g64th 2/2 Running 0 8h
proxy-injector-646874cb74-6fsv6 2/2 Running 0 8h
web-854ddcb68c-mms8f 2/2 Running 0 8h

[root@master ~]# linkerd2 check
kubernetes-api: can initialize the client..................................[ok]
kubernetes-api: can query the Kubernetes API...............................[ok]
kubernetes-api: is running the minimum Kubernetes API version..............[ok]
linkerd-api: control plane namespace exists................................[ok]
linkerd-api: control plane pods are ready..................................[ok]
linkerd-api: can initialize the client.....................................[ok]
linkerd-api: can query the control plane API...............................[ok]
linkerd-api[kubernetes]: control plane can talk to Kubernetes..............[ok]
linkerd-api[prometheus]: control plane can talk to Prometheus..............[ok]
linkerd-api: no invalid service profiles...................................[ok]
linkerd-version: can determine the latest version..........................[ok]
Status check results are [ok]

我们先创建三个namespace,namespace:unlabeled中是没有打标签的,namespace:enabled中带有标签linkerd.io/auto-inject: enabled,namespace:disabled中带有标签linkerd.io/auto-inject: disabled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建不打标签的namespace
[root@master ~]# kubectl create ns unlabeled
namespace/unlabeled created

# 创建带linkerd.io/auto-inject=enabled标签的namespace
[root@master ~]# kubectl create ns enabled
namespace/enabled created
[root@master ~]# kubectl label ns enabled linkerd.io/auto-inject=enabled
namespace/enabled labeled

# 创建带linkerd.io/auto-inject=disabled标签的namespace
[root@master ~]# kubectl create ns disabled
namespace/disabled created
[root@master ~]# kubectl label ns disabled linkerd.io/auto-inject=disabled
namespace/disabled labeled

分别使用kubectl在三个namespace中创建应用,观察是否自动注入了SideCar到Pod中

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
# 分别在不同的namespace中创建Deployment
[root@master ~]# kubectl run nginx --image=nginx:alpine --port=80 -n unlabeled
[root@master ~]# kubectl run nginx --image=nginx:alpine --port=80 -n enabled
[root@master ~]# kubectl run nginx --image=nginx:alpine --port=80 -n disabled

#
[root@master ~]# kubectl -n unlabeled get po
NAME READY STATUS RESTARTS AGE
nginx-6745c9f95b-lh54d 2/2 Running 0 21s
[root@master ~]# kubectl -n enabled get po
NAME READY STATUS RESTARTS AGE
nginx-5bdbb9bdb5-fdclz 2/2 Running 0 12m
[root@master ~]# kubectl -n disabled get po
NAME READY STATUS RESTARTS AGE
nginx-5dbb4c75cd-mnfp8 1/1 Running 0 12m

# 查看是否有secret生成
[root@master ~]# kubectl -n unlabeled get secret
NAME TYPE DATA AGE
default-token-cgs5g kubernetes.io/service-account-token 3 2m
nginx-deployment-tls-linkerd-io Opaque 2 1m
[root@master ~]# kubectl -n enabled get secret
NAME TYPE DATA AGE
default-token-6zxbk kubernetes.io/service-account-token 3 16m
nginx-deployment-tls-linkerd-io Opaque 2 13m
[root@master ~]# kubectl -n disabled get secret
NAME TYPE DATA AGE
default-token-s5mhw kubernetes.io/service-account-token 3 16m

查看注入的SideCar容器中是否在和控制平面通信中启用TLS

     说明在有linkerd.io/auto-inject: enabled标签或者没有打标签的namespace中,当创建Deployment时都会被自动注入代理,并且在和控制平面通信中自动启用TLS对流量加密;带有linked.io/auto-inject: disabled标签的Namespace不会自动注入SideCar。这样可以实现在不同的namespace有效隔离需要被meshed和不需要被meshed的应用。那在一个有linkerd.io/auto-inject: enabled标签或者没有打标签的namespace中是否也可以隔离需要被meshed和不需要被meshed的应用呢?

我们在namespace:enabled中创建四个Deployment,分别带有linkerd.io/auto-inject: disabled、linkerd.io/auto-inject: completed、linkerd.io/auto-inject: enabled标签和不打标签四种,观察是否自动注入了SideCar到Pod中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master ~]# kubectl -n enabled run nginx-disabled --image=nginx:alpine --port=80 --labels="linkerd.io/auto-inject=disabled"
[root@master ~]# kubectl -n enabled run nginx-completed --image=nginx:alpine --port=80 --labels="linkerd.io/auto-inject=completed"
[root@master ~]# kubectl -n enabled run nginx-enabled --image=nginx:alpine --port=80 --labels="linkerd.io/auto-inject=disabled"
[root@master ~]# kubectl -n enabled run nginx-unlabeled --image=nginx:alpine --port=80

[root@master nginx]# kubectl -n enabled get pod
NAME READY STATUS RESTARTS AGE
nginx-completed-655cdfb7bc-25zr4 1/1 Running 0 22m
nginx-disabled-f78895799-ttt8w 1/1 Running 0 5s
nginx-enabled-64ff6f6798-68n4c 2/2 Running 0 13m
nginx-unlabeled-7f7fd88fb-5vjxp 2/2 Running 0 25m

# 查看是否有secret生成
[root@master nginx]# kubectl -n enabled get secret
NAME TYPE DATA AGE
default-token-6zxbk kubernetes.io/service-account-token 3 1h
nginx-enabled-deployment-tls-linkerd-io Opaque 2 13m
nginx-unlabeled-deployment-tls-linkerd-io Opaque 2 25m

查看注入的SideCar容器中是否在和控制平面通信中启用TLS

     说明在有linkerd.io/auto-inject: enabled标签或者没有打标签的namespace中,Deployment中的pod模板有linkerd.io/auto-inject: enabled标签或者没有打标签,pod里都会被自动注入代理,并且在和控制平面通信中启用TLS;强调必须是Deployment中的pod模板带有linkerd.io/auto-inject: disabled或者linkerd.io/auto-inject: completed的标签都不会在pod注入代理。这样可以实现在启用自动注入的namespace中有效隔离需要被meshed和不需要被meshed的应用。

     本节就先介绍完CLI的使用和两个特性,当然CLI还可以查看应用状态,但是我们有更直观的Dashboard,所以下一节介绍如何通过Dashboard去查看应用状态和排查应用故障。