早在去年,Service Mesh 这个概念就开始火起来了,今年的时候 Service Mesh 更是爆发式地发展,Service Mesh 中的明星项目 Istio 更是只用了几个月的时间就已经从 0.1 到了 1.0 了。
Istio 的背景本文不再赘述,G 家等大厂开发出来并且在后面推动支持的肯定不会弱。
根据 Istio 的官方文档,是这么定义自己的:一个用来连接、管理和加密微服务(流量)的开放平台。
an open platform to connect, manage, and secure microservices
Istio 可以让你在不修改微服务源代码的情况之下,很轻松地给微服务加上诸如负载均衡、身份验证、监控等等的功能。 Istio 通过在你的微服务中部署一个 sidecar 作为所有流量的代理来达成这个目标。
总结下来,Istio 提供了以下功能:
除了这些之外,Istio 还 支持很多不同的平台(尤其是 Kubernetes) ,并且支持自定义的组件和集成。
通过这些功能,微服务的开发和迁移变得更加容易,对于运维人员来说,也可以更方便地更改部署策略。
接下来介绍 Istio 的架构。Istio 是两层架构的,分别是数据层和控制层:
数据层是由所有的部署为 sidecar 的 Envoy 所组成的。
控制层有三个组件:Pilot、Mixer 和 Citadel,顾名思义是用来控制 Service Mesh 的行为的。
总体的架构如下图:
首先来看数据层的 Envoy。Istio 用了一个扩展版本的 Envoy 作为底层的代理。Envoy 是一个用 C++ 开发的高性能的代理,具有非常多功能,具体的可以参考官方文档,在此不做赘述。
Envoy 在 Istio 中是以 sidecar 模式部署在 pod 里面的,Istio 通过控制 Envoy 来控制所有的流量,获取监控数据等。
接着,来看控制层的 Pilot、Mixer 和 Citadel。Pilot 为 Envoy 提供服务发现、智能路由(如 AB 测试、金丝雀部署)和弹性流量管理功能(如超时、重试、熔断)。它负责将高层的抽象的路由规则转化成低级的 envoy 的配置。
Mixer 是一个平台无关的组件,用来控制访问策略和使用策略,同时会收集监控信息,将收集到的信息传给用户可以自定义的后端进行处理。
Citadel 提供了服务间和服务到终端用户的认证,同时可以直接将 http 流量升级成 https 流量。具体的可以查看官方文档。
下面简要介绍一下安装。在这里我打算使用 helm 进行安装。
Prerequisite
首先,你得有一个可运行的 Kubernetes 集群,我是在 GKE 上开了一个三节点的集群作为测试使用。
其次,你得需要有 helm 的客户端。mac 用户可以通过 brew 来安装。
下载 release
Istio 提供了一个很方便的脚本来下载并解压最新版的 Istio,如下:
$ curl -L https://git.io/getLatestIstio | sh -
复制代码
等下载完之后,我们可以进入文件夹,并把 bin 目录加到 path 里面:
$ cd istio-0.8.0
$ export PATH=$PWD/bin:$PATH
复制代码
使用 helm 进行安装
要使用 helm 来安装 istio,首先需要在集群里面配置好 helm 和 tiller,如下:
$ kubectl create -f install/kubernetes/helm/helm-service-account.yaml
$ helm init --service-account tiller
复制代码
等 helm 和 tiller 配置完之后,就可以使用 helm 来一键安装 Istio 了:
$ helm install install/kubernetes/helm/istio --name istio --namespace istio-system
复制代码
这样,Istio 就安装好了。
为了验证安装是否成功,我们可以看一下是否部署了以下的 service:
$ 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 状态:
$ 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
复制代码
当然,我们也可以自定义一些参数,具体的请看官方文档 –name istio –namespace istio-system)。
接下来我们部署一个应用,看看 Istio 是如何运作的。我们的样例应用叫做 BookInfo,这个应用由四个微服务所组成,具体架构图如下:
这个应用是用不同的语言所写的,让我们来见识一下 Istio 的魔力吧。
安装这个应用非常简单,我们只要执行以下命令即可:
$ kubectl apply -f samples/bookinfo/kube/bookinfo.yaml
$ istioctl create -f samples/bookinfo/routing/bookinfo-gateway.yaml
复制代码
我们可以注意一下,在 bookinfo.yaml 中的 manifest 如下:
# 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
---
...
复制代码
但是我们真正部署出来后,变成了这样:
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 版本上(就是没有星星的版本),路由规则如下:
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
...
复制代码
命令如下:
$ istioctl create -f samples/bookinfo/routing/route-rule-all-v1.yaml
复制代码
然后我们再去刷新,就会发现不管怎么刷新星星都不见了。
接着,假如我们有一个用户是 jason,我们希望他能测试 v2 的 backend,就可以用下面的路由规则:
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
复制代码
命令如下:
$ istioctl replace -f samples/bookinfo/routing/route-rule-reviews-test-v2.yaml
复制代码
这时候,我们打开网页,以 jason 这个用户登录(密码随便填),就会发现每一次访问到的都是带有黑星星的版本。
这就是 Istio 提供的路由功能。
想要了解更多可以直接访问官网: https://istio.io/
评论