Service Mesh Istio 初探

早在去年,Service Mesh这个概念就开始火起来了,今年的时候Service Mesh更是爆发式地发展,Service Mesh中的明星项目Istio更是只用了几个月的时间就已经从0.1到了0.8 LTS了。由于工作和毕业的压力,之前一直没有时间深入研究Service Mesh。现在稍微有些时间了,所以打算写点什么关于Service Mesh的。

介绍

首先,我们需要了解一下什么是Service Mesh。今天我们的主角是Istio,Istio的背景我不过多介绍,G家等大厂搞出来并且在后面推动支持的肯定不会弱。

根据Istio的官方文档,是这么定义自己的:一个用来连接、管理和加密微服务(流量)的开放平台。

an open platform to connect, manage, and secure microservices

Istio可以让你在不修改微服务源代码的情况之下,很轻松地给微服务加上诸如负载均衡、身份验证、监控等等的功能。Istio通过在你的微服务中部署一个sidecar作为所有流量的代理来达成这个目标。

总结下来,Istio提供了以下功能:

  • 流量管理(Traffic Management)
  • 服务的身份认证和安全(Service Identity and Security)
  • 策略配置(Policy Enforcement)
  • 遥感(Telemetry)

除了这些之外,Istio还支持很多不同的平台(尤其是Kubernetes),并且支持自定义的组件和集成。

通过这些功能,微服务的开发和迁移会变得非常容易,而运维人员也可以更方便的更改部署的策略。

架构

Istio是两层架构的,分别是数据层控制层

  • 数据层是由所有的部署为sidecar的Envoy所组成的。
  • 控制层有三个组件:Pilot、Mixer和Citadel,顾名思义是用来控制Service Mesh的行为的。

总体的架构如下图:

Istio Architecture

Envoy

Istio用了一个扩展版本的Envoy作为底层的代理。Envoy是一个用C++开发的高性能的代理,具有非常多功能,具体的可以参考官方文档,在此不做赘述。

Envoy在Istio中是以sidecar模式部署在pod里面的,Istio通过控制Envoy来控制所有的流量,获取监控数据等。

Mixer

Mixer是一个平台无关的组件,用来控制访问策略和使用策略,同时会收集监控信息,将收集到的信息传给用户可以自定义的后端进行处理。

Pilot

Pilot为Envoy提供服务发现、智能路由(如AB测试、金丝雀部署)和弹性流量管理功能(如超时、重试、熔断)。它负责将高层的抽象的路由规则转化成低级的envoy的配置。

Citadel

Citadel提供了服务间和服务到终端用户的认证,同时可以直接将http流量升级成https流量。具体的可以查看官方文档。

安装

在这里我打算使用helm进行安装。

Prerequisite

首先,你得有一个可运行的k8s集群,我是在gke上开了一个三节点的集群作为测试使用。

其次,你得需要有helm的客户端。mac用户可以通过brew来安装。

下载release

Istio提供了一个很方便的脚本来下载并解压最新版的Istio,如下:

1
$ curl -L https://git.io/getLatestIstio | sh -

等下载完之后,我们可以进入文件夹,并把bin目录加到path里面:

1
2
$ cd istio-0.8.0
$ export PATH=$PWD/bin:$PATH

使用helm进行安装

要使用helm来安装istio,首先需要在集群里面配置好helm和tiller,如下:

1
2
$ kubectl create -f install/kubernetes/helm/helm-service-account.yaml
$ helm init --service-account tiller

等helm和tiller配置完之后,就可以使用helm来一键安装Istio了:

1
$ helm install install/kubernetes/helm/istio --name istio --namespace istio-system

这样,Istio就安装好了。

为了验证安装是否成功,我们可以看一下是否部署了以下的service:

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-citadel ClusterIP 10.19.247.33 <none> 8060/TCP,9093/TCP 2m
istio-egressgateway ClusterIP 10.19.244.143 <none> 80/TCP,443/TCP 2m
istio-ingress LoadBalancer 10.19.248.42 104.199.155.220 80:32000/TCP,443:30434/TCP 2m
istio-ingressgateway LoadBalancer 10.19.254.155 35.229.183.83 80:31380/TCP,443:31390/TCP,31400:31400/TCP 2m
istio-pilot ClusterIP 10.19.252.30 <none> 15003/TCP,15005/TCP,15007/TCP,15010/TCP,15011/TCP,8080/TCP,9093/TCP 2m
istio-policy ClusterIP 10.19.242.187 <none> 9091/TCP,15004/TCP,9093/TCP 2m
istio-sidecar-injector ClusterIP 10.19.252.155 <none> 443/TCP 2m
istio-statsd-prom-bridge ClusterIP 10.19.246.99 <none> 9102/TCP,9125/UDP 2m
istio-telemetry ClusterIP 10.19.240.18 <none> 9091/TCP,15004/TCP,9093/TCP,42422/TCP 2m
prometheus ClusterIP 10.19.255.53 <none> 9090/TCP 2m

并且确认以下的Pod是否在running状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istio-citadel-7bdc7775c7-ntfkf 1/1 Running 0 3m
istio-egressgateway-795fc9b47-2hw69 1/1 Running 0 3m
istio-ingress-84659cf44c-dkgf4 1/1 Running 0 3m
istio-ingressgateway-7d89dbf85f-9kgth 1/1 Running 0 3m
istio-mixer-post-install-vg5gh 0/1 Completed 0 3m
istio-pilot-66f4dd866c-nwr2j 2/2 Running 0 3m
istio-policy-76c8896799-7l9nz 2/2 Running 0 3m
istio-sidecar-injector-645c89bc64-6rs5k 1/1 Running 0 3m
istio-statsd-prom-bridge-949999c4c-mpk6d 1/1 Running 0 3m
istio-telemetry-6554768879-vqmjd 2/2 Running 0 3m
prometheus-86cb6dd77c-vhf9s 1/1 Running 0 3m

当然,我们也可以自定义一些参数,具体的请看[官方文档]($ helm install install/kubernetes/helm/istio –name istio –namespace istio-system)。

样例应用

让我们部署我们的一个样例应用来看看Istio到底干了啥。

我们的样例应用叫做BookInfo,这个应用由四个微服务所组成,具体架构图如下:

Bookinfo Application without Istio

这个应用是用不同的语言所写的,让我们来见识一下Istio的魔力吧。

安装这个应用非常简单,我们只要执行以下命令即可:

1
2
$ kubectl apply -f samples/bookinfo/kube/bookinfo.yaml
$ istioctl create -f samples/bookinfo/routing/bookinfo-gateway.yaml

我们可以注意一下,在bookinfo.yaml中的manifest如下:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Copyright 2017 Istio Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

##################################################################################################
# Details service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: details-v1
spec:
replicas: 1
template:
metadata:
labels:
app: details
version: v1
spec:
containers:
- name: details
image: istio/examples-bookinfo-details-v1:1.5.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
---
...

但是我们真正部署出来后,变成了这样:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
apiVersion: v1
kind: Pod
metadata:
annotations:
sidecar.istio.io/status: '{"version":"55c9e544b52e1d4e45d18a58d0b34ba4b72531e45fb6d1572c77191422556ffc","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}'
creationTimestamp: 2018-07-05T09:10:55Z
generateName: details-v1-5f94c6d66b-
labels:
app: details
pod-template-hash: "1950728226"
version: v1
name: details-v1-5f94c6d66b-jj6lz
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: details-v1-5f94c6d66b
uid: 528aa360-8033-11e8-8cec-0e04fb7e7092
resourceVersion: "15620"
selfLink: /api/v1/namespaces/default/pods/details-v1-5f94c6d66b-jj6lz
uid: 528d5618-8033-11e8-8cec-0e04fb7e7092
spec:
containers:
- image: istio/examples-bookinfo-details-v1:1.5.0
imagePullPolicy: IfNotPresent
name: details
ports:
- containerPort: 9080
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-f9mls
readOnly: true
- args:
- proxy
- sidecar
- --configPath
- /etc/istio/proxy
- --binaryPath
- /usr/local/bin/envoy
- --serviceCluster
- details
- --drainDuration
- 45s
- --parentShutdownDuration
- 1m0s
- --discoveryAddress
- istio-pilot.istio-system:15007
- --discoveryRefreshDelay
- 10s
- --zipkinAddress
- zipkin.istio-system:9411
- --connectTimeout
- 10s
- --statsdUdpAddress
- istio-statsd-prom-bridge.istio-system:9125
- --proxyAdminPort
- "15000"
- --controlPlaneAuthPolicy
- NONE
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: ISTIO_META_POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: ISTIO_META_INTERCEPTION_MODE
value: REDIRECT
image: docker.io/istio/proxyv2:0.8.0
imagePullPolicy: IfNotPresent
name: istio-proxy
resources:
requests:
cpu: 100m
memory: 128Mi
securityContext:
privileged: false
readOnlyRootFilesystem: true
runAsUser: 1337
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /etc/certs/
name: istio-certs
readOnly: true
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-f9mls
readOnly: true
dnsPolicy: ClusterFirst
initContainers:
- args:
- -p
- "15001"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- 9080,
- -d
- ""
image: docker.io/istio/proxy_init:0.8.0
imagePullPolicy: IfNotPresent
name: istio-init
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-f9mls
readOnly: true
nodeName: ip-172-31-39-23
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- emptyDir:
medium: Memory
name: istio-envoy
- name: istio-certs
secret:
defaultMode: 420
optional: true
secretName: istio.default
- name: default-token-f9mls
secret:
defaultMode: 420
secretName: default-token-f9mls

可以看到,本来只有一个container的,现在里面多了一个container和initContainer。这个就是Istio的Auto Injection,可以自动把sidecar注入到Pod里面,让我们不需要手动一个一个修改yaml文件,也防止手动修改过程中出错的可能。

使用实例

这里我们以路由设置为例子。

首先我们打开刚才部署好的这个应用的网页,可以看到页面右方的Book Reviews部分里面每次刷新都会随机性地出现黑星星、红星星和没有星星三种情况,这是因为我们有三个不同的backend,路由在默认情况下会随机路由到任意一个backend上。

我们先尝试把所有的路由都路由到v1版本上(就是没有星星的版本),路由规则如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: details
...
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage
...

命令如下:

1
$ istioctl create -f samples/bookinfo/routing/route-rule-all-v1.yaml

然后我们再去刷新,就会发现不管怎么刷新星星都不见了。

接着,假如我们有一个用户是jason,我们希望他能测试v2的backend,就可以用下面的路由规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kind: VirtualService
metadata:
name: reviews
...
spec:
hosts:
- reviews
http:
- match:
- headers:
cookie:
regex: ^(.*?;)?(user=jason)(;.*)?$
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1

命令如下:

1
$ istioctl replace -f samples/bookinfo/routing/route-rule-reviews-test-v2.yaml

这时候,我们打开网页,以jason这个用户登录(密码随便填),就会发现每一次访问到的都是带有黑星星的版本。

这就是Istio提供的路由功能。

总结

本文中我们简单讲了Service Mesh的概念,如何创建Istio以及简单的使用过程,如果大家有兴趣探索Istio更多的功能,可以直接访问Istio的官网