本篇文章将会用一个小例子来展示 Istio 在流量管理方面的能力,流程如下:
- 使用官方的
istioctl以默认配置来完成 Istio 的安装; - 使用 Deployment 将一个应用的两个版本作为测试服务部署到网格中;
- 将一个客户端服务部署到网格中进行测试;
- 为我们的目标服务编写策略文件,对目标服务的流量进行管理;
环境介绍
这里给出对 Kubernetes 环境的要求:
- Kubernetes 1.9或以上版本;
- 具备管理权限的
kubectl及其配置文件,能够操作测试集群; - Kubernetes 集群要有获取互联网镜像的能力;
- 要支持 lstio 的自动注入功能,需要检查 Kubernetes APIServer 的启动参数,保证其中的
admissioncontrol部分按顺序启用MutatingAdmissionWebhook和ValidatingAdmissionWebhook。
我这里准备了一个版本为 v1.19.3 的 Kubernetes 集群来做测试,查看版本如下:
$ kubectl version --short
Client Version: v1.19.3
Server Version: v1.19.3
快速部署 Istio
Istio 的 Github 发布地址为:
其中包含各个客户端平台下的 Istio 的各个版本,以在我们常用的 Linux AMD 平台下部署为例,直接下载对应的版本的 tar 包即可:
$ wget https://github.com/istio/istio/releases/download/1.10.1/istioctl-1.10.1-linux-amd64.tar.gz
改 tar 包中仅包含一个 istioctl 可执行文件,我们就直接将它解压到 $PATH 包含的路径下:
$ tar xf istioctl-1.10.1-linux-amd64.tar.gz -C /usr/local/bin/
然后就可以直接执行 istioctl 来进行安装操作,但在这之前你要检查一下你的 Kubernetes Node 中是否安装了 socat,我这里 Node 节点的系统发行版为 CentOS 7,所以直接 yum 查看即可:
$ yum list installed | grep socat
socat.x86_64 1.7.3.2-2.el7 @base
如果没有安装,则可以使用 yum 安装:
$ yum install socat -y
然后我们就可以执行下面命令开始在 Kubernetes 集群中注册和安装 Istio 相关组件了:
$ istioctl install
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/v1.10/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
! values.global.jwtPolicy is deprecated; use Values.global.jwtPolicy=third-party-jwt. See http://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for more information instead
This will install the Istio 1.10.1 profile with ["Istio core" "Istiod" "Ingress gateways"] components into the cluster. Proceed? (y/N) y
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ Installation complete Thank you for installing Istio 1.10. Please take a few minutes to tell us about your install/upgrade experience! https://forms.gle/KjkrDnMPByq7akrYA
安装完成后可以执行下面命令来验证是否执行成功:
$ istioctl verify-install
1 Istio control planes detected, checking --revision "default" only
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/v1.10/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
! values.global.jwtPolicy is deprecated; use Values.global.jwtPolicy=third-party-jwt. See http://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for more information instead
✔ ClusterRole: istiod-istio-system.istio-system checked successfully
✔ ClusterRole: istio-reader-istio-system.istio-system checked successfully
✔ ClusterRoleBinding: istio-reader-istio-system.istio-system checked successfully
✔ ClusterRoleBinding: istiod-istio-system.istio-system checked successfully
✔ Role: istiod-istio-system.istio-system checked successfully
✔ RoleBinding: istiod-istio-system.istio-system checked successfully
✔ ServiceAccount: istio-reader-service-account.istio-system checked successfully
✔ ServiceAccount: istiod-service-account.istio-system checked successfully
✔ ValidatingWebhookConfiguration: istiod-istio-system.istio-system checked successfully
✔ CustomResourceDefinition: destinationrules.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: envoyfilters.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: gateways.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: serviceentries.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: sidecars.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: virtualservices.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: workloadentries.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: workloadgroups.networking.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: authorizationpolicies.security.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: peerauthentications.security.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: requestauthentications.security.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: telemetries.telemetry.istio.io.istio-system checked successfully
✔ CustomResourceDefinition: istiooperators.install.istio.io.istio-system checked successfully
✔ HorizontalPodAutoscaler: istiod.istio-system checked successfully
✔ ConfigMap: istio.istio-system checked successfully
✔ Deployment: istiod.istio-system checked successfully
✔ ConfigMap: istio-sidecar-injector.istio-system checked successfully
✔ MutatingWebhookConfiguration: istio-sidecar-injector.istio-system checked successfully
✔ PodDisruptionBudget: istiod.istio-system checked successfully
✔ Service: istiod.istio-system checked successfully
✔ EnvoyFilter: metadata-exchange-1.10.istio-system checked successfully
✔ EnvoyFilter: tcp-metadata-exchange-1.10.istio-system checked successfully
✔ EnvoyFilter: stats-filter-1.10.istio-system checked successfully
✔ EnvoyFilter: tcp-stats-filter-1.10.istio-system checked successfully
✔ EnvoyFilter: metadata-exchange-1.9.istio-system checked successfully
✔ EnvoyFilter: tcp-metadata-exchange-1.9.istio-system checked successfully
✔ EnvoyFilter: stats-filter-1.9.istio-system checked successfully
✔ EnvoyFilter: tcp-stats-filter-1.9.istio-system checked successfully
✔ HorizontalPodAutoscaler: istio-ingressgateway.istio-system checked successfully
✔ Deployment: istio-ingressgateway.istio-system checked successfully
✔ PodDisruptionBudget: istio-ingressgateway.istio-system checked successfully
✔ Role: istio-ingressgateway-sds.istio-system checked successfully
✔ RoleBinding: istio-ingressgateway-sds.istio-system checked successfully
✔ Service: istio-ingressgateway.istio-system checked successfully
✔ ServiceAccount: istio-ingressgateway-service-account.istio-system checked successfully
Checked 13 custom resource definitions
Checked 2 Istio Deployments
✔ Istio is installed and verified successfully
安装完成后会在 Kubernetes 集群的 istio-system 命名空间下创建如下两个 Deployment 资源:
$ kubectl get deploy -n istio-system
NAME READY UP-TO-DATE AVAILABLE AGE
istio-ingressgateway 1/1 1 1 21h
istiod 1/1 1 1 21h
至此我们在 Kubernetes 集群中安装 Istio 就成功啦~
部署两个版本的服务
这里将使用一个简单的提供 HTTP 服务的程序作为服务端,以及还有一个包含 curl 命令的镜像用作客户端进行测试,相关镜像已经在 Dockerhub 公开,所以下面的 manifests 都可以直接使用。
创建两个 Deployment 将其分别命名为 app-v1 和 app-v2,同时创建一个 Service 将其命名为 app,将下面内容保存为 app.istio.yaml:
---
apiVersion: v1
kind: Service
metadata:
name: app
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
spec:
replicas: 1
selector:
matchLabels:
app: app
version: v1
template:
metadata:
labels:
app: app
version: v1
spec:
containers:
- image: dustise/flaskapp
name: app
env:
- name: version
value: v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
spec:
replicas: 1
selector:
matchLabels:
app: app
version: v2
template:
metadata:
labels:
app: app
version: v2
spec:
containers:
- image: dustise/flaskapp
name: app
env:
- name: version
value: v2
在上面的 YAML 源码中有以下需要注意的地方。
- 两个版本的
Deployment的镜像是一致的,但使用了不同的version标签进行区分,分别是v1和v2; - 在两个版本的
Deployment容器中都注册了一个被命名为version的环境变量,取值分别为v1和v2; - 两个
Deployment都使用了app和version标签,在 Istio 网格应用中通常会使用这两个标签作为应用和版本的标识; Service中的Selector仅使用了一个app标签,这意味着该Service对两个Deployment都是有效的;- 将在
Service中定义的端口根据 Istio 规范命名为http。
接下来使用 istioctl 进行注入。之后会一直用到 istioctl 命令,它的基本作用就是修改 Kubernetes Deployment,在 Pod 中注入在前面提到的 Sidecar 容器,通常为了方便,我们会使用一个管道命令,在将 YAML 文件通过 istioctl 处理之后,通过命令行管道输出给 kubectl,最终提交到 Kubernetes 集群。命令如下:
$ istioctl kube-inject -f app.istio.yaml | kubectl apply -f -
service/app created
deployment.apps/app-v1 created
deployment.apps/app-v2 created
查看创建出来的 Pod:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
app-v1-7b9b4899f9-h78h9 2/2 Running 0 10m
app-v2-855f645c54-mqz9q 2/2 Running 0 10m
可以看到,每个 Pod 都变成了两个容器,这也就是 Istio 注入 Sidecar 的结果。
查看 Pod 的容器:
$ kubectl describe pod app-v1-7b9b4899f9-h78h9 | grep -i -B2 'image:'
istio-init:
Container ID: docker://45d471111cc0ba5f175872d77d6a99462eaf49fba935062827ec838173672285
Image: docker.io/istio/proxyv2:1.10.1
--
app:
Container ID: docker://b85c09574566c8d7aa637aa8011d096d624ecd6a3afa8dd206ae6875bd61b1b6
Image: dustise/flaskapp
--
istio-proxy:
Container ID: docker://8d3d24b57261afcbcb0bd4b35ea49f9c9137b439be68ead7b294a168868c7ca1
Image: docker.io/istio/proxyv2:1.10.1
不难发现,在这个 Pod 中多了一个容器,名称为 istio-proxy,这就是注入的结果。另外,前面还有一个名称为 istio-init 的初始化容器,这个容器是用于初始化劫持流量的。
部署客户端服务
客户端服务很简单,只是使用了一个已安装好各种测试工具的镜像,具体的测试可以在其内部的 Shell 中完成。同样,编写一个 YAML 文件,将其命名为 testclient.yaml:
---
apiVersion: v1
kind: Service
metadata:
labels:
app: testclient
name: testclient
spec:
ports:
- name: ssh
port: 22
protocol: TCP
targetPort: 22
selector:
app: testclient
version: v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: testclient
name: testclient
spec:
replicas: 1
selector:
matchLabels:
app: testclient
version: v1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: testclient
version: v1
spec:
containers:
- image: zze326/test-tools
command: ['init']
name: test-tools
你可能会注意到,这个应用并没有提供对外服务的能力,我们也还是给它创建了一个 Service 对象,这同样是 Istio 的注入要求:没有 Service 的 Deployment 是无法被 Istio 发现并进行操作的。
同样,对该文件进行注入,并提交到 Kubernetes 上运行:
$ istioctl kube-inject -f testclient-deploy.yaml | kubectl apply -f -
service/testclient created
deployment.apps/testclient created
等待 Pod 成功进入 Running 状态:
$ kubectl get pod -lapp=testclient
NAME READY STATUS RESTARTS AGE
testclient-7447bc998d-v5h9t 2/2 Running 0 65s
可以看到,testclient 应用的 Pod 已经开始运行。
验证服务
接下来,我们可以通过 kubectl exec -it 命令进入客户端 Pod,来测试 app 服务的具体表现。
使用一个简单的 for 循环,重复获取 http://flaskapp/env/version 的内容,也就是调用 app 服务,查看其返回结果:
$ kubectl exec -it testclient-7447bc998d-v5h9t -- bash
Defaulting container name to test-tools.
Use 'kubectl describe pod/testclient-7447bc998d-v5h9t -n default' to see all of the containers in this pod.
bash-5.1# for i in `seq 10`;do curl http://app/env/version;echo; done;
v1
v1
v2
v2
v1
v2
v1
v1
v2
v2
从上面的运行结果中可以看到,v1 和 v2 这两种结果随机出现,大约各占一半。这很容易理解,因为我们的 app 服务的选择器被定义为只根据 app 标签进行选择,两个版本的服务 Pod 数量相同,因此会出现轮流输出的效果。
创建目标规则和路由
接下来使用 Istio 来管理这两个服务的流量。首先创建 app 应用的目标规则,输入以下内容井将其保存为 app-destinationrule.yaml:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: app
spec:
host: app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
这里定义了一个名称为 app 的 DestinationRule,它利用 Pod 标签把 app 服务分成两个 subset,将其分别命名为 v1 和 v2。下面将 app-destinationrule.yaml 提交到集群上:
$ kubectl apply -f app-destinationrule.yaml
destinationrule.networking.istio.io/app created
接下来就需要为 app 服务创建默认的路由规则了,不论是否进行进一步的流量控制,都建议为网格中的服务创建默认的路由规则,以防发生意料之外的访问结果。
使用下面的内容创建文本文件 app-default-vs-v2.yaml:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-default-v2
spec:
hosts:
- app
http:
- route:
- destination:
host: app
subset: v2
在该文本文件中,我们定义了一个 VirtualService 对象,将其命名为 app-default-v2,它负责接管对 app 这一主机名的访问,会将所有流量都转发到 DestinationRule 定义的 v2 subset 上。
再次执行 kubectl 将 VirtualService 提交到集群上:
$ kubectl apply -f app-default-vs-v2.yaml
virtualservice.networking.istio.io/app-default-v2 created
在创建成功后,可以再次进入客户端 Pod,看看新定义的流量管理规则是否生效:
$ kubectl exec -it testclient-7447bc998d-v5h9t -- bash
Defaulting container name to test-tools.
Use 'kubectl describe pod/testclient-7447bc998d-v5h9t -n default' to see all of the containers in this pod.
bash-5.1# for i in `seq 10`;do curl http://app/env/version;echo; done;
v2
v2
v2
v2
v2
v2
v2
v2
v2
v2
可以看到,默认的路由已经生效,现在重复多次访问,返回的内容来自环境变量 version 被设置为 v2 的版本,也就是 v2 版本。
小结
本篇实践了一个较为典型的 Istio 服务上线流程:注入→部署→创建目标规则→创建默认路由。绝大多数 lstio 网格应用都会遵循这一流程进行上线。
评论区