Istio在服务网络中统一提供了许多关键功能:
-
流量管理:控制服务之间的流量和API调用的流向,使得调用更可靠,并使网络在恶劣情况下更加健壮。
-
可观察性:了解服务之间的依赖关系,以及它们之间流量的本质和流向,从而提供快速识别问题的能力。
-
策略执行:将组织策略应用于服务之间的互动,确保访问策略得以执行,资源在消费者之间良好分配。策略的更改是通过配置网格而不是修改应用程序代码。
-
服务身份和安全:为网格中的服务提供可验证身份,并提供保护服务流量的能力,使其可以在不同可信度的网络上流转。
Istio针对可扩展性进行了设计,以满足不同的部署需要:
-
平台支持:Istio旨在在各种环境中运行,包括跨云, 预置,Kubernetes,Mesos等。最初专注于Kubernetes,但很快将支持其他环境。
-
集成和定制:策略执行组件可以扩展和定制,以便与现有的ACL,日志,监控,配额,审核等解决方案集成。
Envoy:
istio 使用Envoy 代理的扩展版本,Envoy是以c++开发的高性能代理,用于解释服务网格中所有服务的所有入站和出站的流量,Envoy的许多内置功能被iosio发扬光大,例如动态发现,负载均衡,TLS终止,HTTP1/2&Grpc代理,熔断器,健康检查,基于百分比流量拆分的分段推出,故障注入和丰富指标。
Envoy被部署为边车,和对应服务在同一个pod中,这允许istio将大量关于流量行为的型号作为属性提取出来,而这些属性又可以在Mixer中用于执行策略决策,并发送到监控系统,以提供整个网格行为的信息
Mixer:
负责在服务网格上执行访问控制和使用策略,并从Envoy代理和其他服务收集遥测数据,代理提取请求级属性,发送到Mixer进行评估。
Pilot
负责手机和验证配置,并将其传播到各种istio组件,它从Mixer和Envoy中提取环境特定的实现细节,为他们提供用户服务的抽象表示,独立于底层平台,此外,流量管理规则可以在运行时通过pilot进行编程。
lstio-auth
提供强大的服务间认证和终端用户认证,使用交互tls,内置身份和证书管理,可以升级服务网格中的未加密流量,并为运维人员提供基于服务身份而不是网络控制来执行策略的能力。
安装Istio Sidecar
在Kubernetes 1.9及更高版本中默认启用。从Istio 0.5.0开始,自动代理注入使用突变webhooks,并且已经删除了对初始化程序注入的支持。无法升级到Kubernetes 1.9的用户应使用手动注入。
自动把 sidecar 插入到 pod中, 需要在api中打开
[root@master1 addons]# cat /lib/systemd/system/kube-apiserver.service [Unit] Description=Kubernetes API Server Documentation=https://github.com/GoogleCloudPlatform/kubernetes After=network.target [Service] User=root ExecStart=/usr/local/bin/kube-apiserver \ --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota,,MutatingAdmissionWebhook,ValidatingAdmissionWebhook \ --advertise-address=192.168.200.51 \ --allow-privileged=true \ --anonymous-auth=false \ --apiserver-count=1 \ --audit-policy-file=/etc/kubernetes/audit-policy.yaml \ --audit-log-maxage=30 \ --audit-log-maxbackup=3 \ --audit-log-maxsize=100 \ --audit-log-path=/var/log/kubernetes/audit.log \ --authorization-mode=Node,RBAC \ --bind-address=0.0.0.0 \ --secure-port=6443 \ --client-ca-file=/etc/kubernetes/ssl/ca.pem \ --enable-swagger-ui=true \ --etcd-cafile=/etc/kubernetes/ssl/ca.pem \ --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \ --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \ --etcd-servers=https://192.168.200.51:2379,https://192.168.200.52:2379,https://192.168.200.53:2379 \ --event-ttl=1h \ --kubelet-https=true \ --insecure-bind-address=192.168.200.51 \ --insecure-port=8080 \ --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \ --service-cluster-ip-range=10.254.0.0/16 \ --service-node-port-range=30000-32000 \ --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \ --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \ --enable-bootstrap-token-auth=true \ --token-auth-file=/etc/kubernetes/token.csv \ --requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.pem \ --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.pem \ --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client-key.pem \ --requestheader-allowed-names=aggregator \ --requestheader-group-headers=X-Remote-Group \ --requestheader-extra-headers-prefix=X-Remote-Extra- \ --requestheader-username-headers=X-Remote-User \ --runtime-config=admissionregistration.k8s.io/v1alpha1 \ --runtime-config=api/all=true \ --enable-aggregator-routing=true \ --v=0 Restart=on-failure RestartSec=5 Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target
Pod规格要求
1.服务关联
2.命名端口
服务端口必须命名。端口名称必须是形式<protocol>[-<suffix>]
与 HTTP, http2, GRPC,或 Redis的作为<protocol>
,以便采取的Istio的路由功能。例如,name: http2-foo
或者name: http
是有效的端口名称,但name: http2foo
不是。如果端口名称未以可识别的前缀开头,或者端口未命名,则端口上的流量将被视为纯TCP流量(除非端口明确用于Protocol: UDP
表示UDP端口
3. 使用应用标签进行部署:建议使用Kubernetes部署的Pod在部署规范中Deployment
具有明确的app
标签。每个部署规范都应该有一个明确的app
标签,其中有一个值表示有意义的值 该app
标签用于在分布式跟踪中添加上下文信息
自动边车注入
使用webhook准入控制器,可将Sidecars自动添加到适用的Kubernetes Pod中。此功能需要Kubernetes 1.9或更高版本。验证kube-apiserver进程是否已设置admission-control
标记,MutatingAdmissionWebhook
并ValidatingAdmissionWebhook
按照正确的顺序添加并准许控制器,并且启用许可注册API。
kubectl api-versions | grep admissionregistration admissionregistration.k8s.io/v1beta1
安装webhook
kubectl apply -f install/kubernetes/istio.yaml
Webhooks需要签名的证书/密钥对。使用install/kubernetes/webhook-create-signed-cert.sh
来产生由Kubernetes' CA签名的证书/密钥对 生成的证书/密钥文件作为Kubernetes机密文件存储,供边车注入器webhook使用。
注意:Kubernetes CA批准需要创建和批准CSR的权限
./install/kubernetes/webhook-create-signed-cert.sh \ --service istio-sidecar-injector \ --namespace istio-system \ --secret sidecar-injector-certs
安装边车注入配置图。
kubectl apply -f install/kubernetes/istio-sidecar-injector-configmap-release.yaml
Set the caBundle
in the webhook install YAML that the Kubernetes api-server uses to invoke the webhook.
cat install/kubernetes/istio-sidecar-injector.yaml | \ ./install/kubernetes/webhook-patch-ca-bundle.sh > \ install/kubernetes/istio-sidecar-injector-with-ca-bundle.yaml
Install the sidecar injector webhook.
kubectl apply -f install/kubernetes/istio-sidecar-injector-with-ca-bundle.yaml
The sidecar injector webhook should now be running.
kubectl -n istio-system get deployment -listio=sidecar-injector NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE istio-sidecar-injector 1 1 1 1 1d
NamespaceSelector根据该对象的名称空间是否与选择器匹配来决定是否在对象上运行webhook,默认的webhook配置使用istio-injection=enabled
。
查看显示istio-injection
标签的default
名称空间并验证名称空间未被标记。
kubectl get namespace -L istio-injection NAME STATUS AGE ISTIO-INJECTION default Active 1h istio-system Active 1h kube-public Active 1h kube-system Active 1h
用default
名称空间标签istio-injection=enabled
kubectl label namespace default istio-injection=enabled
kubectl get namespace -L istio-injection NAME STATUS AGE ISTIO-INJECTION default Active 1h enabled istio-system Active 1h kube-public Active 1h kube-system Active 1h
部署一个应用程序
kubectl apply -f samples/sleep/sleep.yaml
禁用default
名称空间的注入
kubectl label namespace default istio-injection-
卸载webhook
kubectl delete -f install/kubernetes/istio-sidecar-injector-with-ca-bundle.yaml
kubectl -n istio-system delete secret sidecar-injector-certs kubectl delete csr istio-sidecar-injector.istio-system kubectl label namespace default istio-injection-
流量管理
介绍演示Istio服务网格的流量路由功能的任务。
配置请求路由: 此任务向您展示如何根据权重和HTTP标头配置动态请求路由。
故障注入 : 此任务显示如何注入延迟并测试应用程序的弹性。
交通转移 : 此任务向您显示如何将流量从旧版本迁移到新版本的服务。
设置请求超时: 本任务向您展示如何使用Istio在Envoy中设置请求超时。
Istio Ingress : 介绍如何在Kubernetes上配置Istio Ingress。
控制出口流量: 介绍如何配置Istio以将流量从网格中的服务路由到外部服务。
控制出口TCP流量:介绍如何配置Istio以将TCP流量从网格中的服务路由到外部服务。
断路 : 这项任务演示了弹性应用的断路能力
镜像 : 演示Istio的流量投影/镜像功能
deployment 和 service 模版 ,使用标签 app version, 一个服务对应不同的deployment
apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews spec: type: NodePort ports: - port: 9080 name: http selector: app: reviews --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: reviews-v1 spec: replicas: 1 template: metadata: labels: app: reviews version: v1 spec: containers: - name: reviews image: 192.168.200.10/istio/examples-bookinfo-reviews-v1:1.5.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: reviews-v2 spec: replicas: 1 template: metadata: labels: app: reviews version: v2 spec: containers: - name: reviews image: 192.168.200.10/istio/examples-bookinfo-reviews-v2:1.5.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: reviews-v3 spec: replicas: 1 template: metadata: labels: app: reviews version: v3 spec: containers: - name: reviews image: 192.168.200.10/istio/examples-bookinfo-reviews-v3:1.5.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080
配置请求路由
原理: 首先使用istio将100%的请求流量路由到了 v1 版本上, 然后设置了一条路由规则, 该规则基于请求header(一个用户的cookie)将流量路由到了 v2 版本, 如果v2 版本经过测试后满足要求,我们就可以一次性
或者渐进的路由到v2版本上。
1. 默认访问 version v1 版本
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews precedence: 1 route: - labels: version: v1
2. 将特定用户的请求路由到 version v2上
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-test-v2 spec: destination: name: reviews precedence: 2 match: request: headers: cookie: regex: "^(.*?;)?(user=jason)(;.*)?$" route: - labels: version: v2
故障注入
istio 注入延迟 测试应用弹性
2个服务, serverA 和 serverB
serverA 用户(feng) 和 serverB 之间 注入7秒延迟, serverA 针对 serverB 服务设置了10秒超时,期望 端对端的流程能无错持续。
注意: 其他用户没有注入7秒延迟。
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: ratings-test-delay spec: destination: name: ratings precedence: 2 match: request: headers: cookie: regex: "^(.*?;)?(user=jason)(;.*)?$" route: - labels: version: v1 httpFault: delay: percent: 100 fixedDelay: 7s
使用账户 feng 登录, 如果应用首页已经设置了正确处理延迟,那么首页将会在 7秒加载完成,但是 serverA 到 serverB 需要等待10秒,那么将超时。
流量转移:
应用流量 逐步有旧版本的服务 迁移到新版本, 通过istio 可以使用不同权重的规则,将流量平稳的从旧版本服务 迁移到新版本上, 金丝雀 灰度发布。
1. 请求v1 版本
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews precedence: 1 route: - labels: version: v1
2. 把50% 的流量从 v1 版本 转移到 v3版本
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews precedence: 1 route: - labels: version: v1 weight: 50 - labels: version: v3 weight: 50
用 kubectl apply/ replace 替换
3. v3 和 v1 版本交替出现后, 将100%的流量路由到v3 版本中
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews precedence: 1 route: - labels: version: v3 weight: 100
###################################################################################################
实战:
一个服务,对应3个deployment, 最后通过istio-ingress 映射域名
nginx.yaml
apiVersion: v1 kind: Service metadata: namespace: fengjian name: nginx labels: app: nginx spec: type: NodePort ports: - port: 80 name: http protocol: TCP targetPort: 80 selector: app: nginx --- apiVersion: extensions/v1beta1 kind: Deployment metadata: namespace: fengjian name: nginx-v1 spec: replicas: 1 template: metadata: labels: app: nginx version: v1 spec: containers: - name: nginx image: 192.168.200.10/test/nginx:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 80 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: namespace: fengjian name: nginx-v2 spec: replicas: 1 template: metadata: labels: app: nginx version: v2 spec: containers: - name: nginx image: 192.168.200.10/test/nginx:v2 imagePullPolicy: IfNotPresent ports: - containerPort: 80 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: namespace: fengjian name: nginx-v3 spec: replicas: 1 template: metadata: labels: app: nginx version: v3 spec: containers: - name: nginx image: 192.168.200.10/test/nginx:v3 imagePullPolicy: IfNotPresent ports: - containerPort: 80
2. 通过istio-ingress 让外网访问,注意: pod 注入 enovy后,nodeport 不起作用了,服务无法访问。
istio-ingress-bookinfo.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: gatewayfengjian namespace: fengjian annotations: kubernetes.io/ingress.class: "istio" spec: rules: - http: paths: - path: / backend: serviceName: nginx servicePort: 80
创建执行
[root@master1 nginx-istio]# kubectl create -f booktest.yaml -f istio-ingress-bookinfo.yaml
默认情况, 访问应该轮训到 3个deployment的上的nginx , 通过流量管理中的 配置请求路由规则,指定到 deployment nginx:v1 版本上
vim rule-choice-deployment-nginx-v1.yaml
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: namespace: fengjian name: nginx-default spec: destination: name: nginx precedence: 1 route: - labels: version: v1
创建规则
kubectl create -f rule-choice-deployment-nginx-v1.yaml
访问istio-ingress
注意的问题:
流量转移
流量从 旧版本中迁移到新版本中, 通过istio,可以使用一系列不同权重的规则(10%,20%.....100%),将流量平稳的迁移到新版本中
1. 迁移50%到新版本中
route-rule-reviews-50.yaml
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: namespace: fengjian name: nginx-50 spec: destination: name: nginx precedence: 1 route: - labels: version: v1 weight: 50 - labels: version: v3 weight: 50
通过访问页面,基本上是50%的轮训到页面。
2. 迁移100%到新版本中
route-rule-reviews-100.yaml
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: namespace: fengjian name: nginx-50 spec: destination: name: nginx precedence: 1 - labels: version: v3 weight: 100
是更新,不是创建
kubectl apply -f route-rule-reviews-100.yaml
注意该方式和使用容器编排平台的部署特点来进行版本迁移是完全不同的,容器编排平台使用了实例scaling来对流量进行管理,而通过istio,两个版本的的服务可以独立进行
scale up 和 scale down,并不会影响这两个版本的服务之间的流量分发。
熔断
限制因为故障、延迟高峰以及其他预计外的网络异常所造成的影响范围。
服务端使用 nginx
配置目的策略,对nginx 的调用过程进行断路设置。
nginx-destinaionpolicy.yaml
apiVersion: config.istio.io/v1beta1 kind: DestinationPolicy metadata: name: nginx-circuit-breaker spec: destination: name: nginx labels: version: v1 circuitBreaker: simpleCb: maxConnections: 1 httpMaxPendingRequests: 1 sleepWindow: 3m httpDetectionInterval: 1s httpMaxEjectionPercent: 100 httpConsecutiveErrors: 1 httpMaxRequestsPerConnection: 1
kubectl create -f nginx-destinaionpolicy.yaml istioctl get destinationpolicy NAME KIND NAMESPACE nginx-circuit-breaker DestinationPolicy.v1alpha2.config.istio.io istio-samples
设置客户端
客户端调用ginx服务的规则, 创建一个客户端,向服务端发送流量, 使用负载测试工具fortio, 使用fortio 可以控制连接数量,并发数以及外发http调用的延迟。
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/sample-client/fortio-deploy.yaml)
使用-curl 指定只执行一次调用。
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }') kubectl exec -it $FORTIO_POD -c fortio /usr/local/bin/fortio -- load -curl http://nginx:8000
测试断路器
在断路器设置中,我们指定maxConnections: 1
以及httpMaxPendingRequests: 1
。这样的设置下,如果我们并发超过一个连接和请求,istio-proxy就会断掉后续的请求和连接。设置两个并发链接(-c 2
),发送20个请求(-n 20
):
[root@master1 httpbin]# kubectl exec -it fortio-deploy-d86464f9d-ks4hm -c fortio /usr/local/bin/fortio -- load -c 2 -qps 0 -n 20 -loglevel Warning http://nginx.fengjian 08:10:21 I logger.go:97> Log level is now 3 Warning (was 2 Info) Fortio 0.9.0 running at 0 queries per second, 4->4 procs, for 20 calls: http://nginx.fengjian Starting at max qps with 2 thread(s) [gomax 4] for exactly 20 calls (10 per thread + 0) Ended after 18.157183ms : 20 calls. qps=1101.5 Aggregated Function Time : count 20 avg 0.0016854434 +/- 0.0008323 min 0.000847509 max 0.004309432 sum 0.033708867 # range, mid point, percentile, count >= 0.000847509 <= 0.001 , 0.000923755 , 15.00, 3 > 0.001 <= 0.002 , 0.0015 , 65.00, 10 > 0.002 <= 0.003 , 0.0025 , 95.00, 6 > 0.004 <= 0.00430943 , 0.00415472 , 100.00, 1 # target 50% 0.0017 # target 75% 0.00233333 # target 90% 0.00283333 # target 99% 0.00424755 # target 99.9% 0.00430324 Sockets used: 2 (for perfect keepalive, would be 2) Code 200 : 20 (100.0 %) Response Header Sizes : count 20 avg 237 +/- 0 min 237 max 237 sum 4740 Response Body/Total Sizes : count 20 avg 246 +/- 0 min 246 max 246 sum 4920 All done 20 calls (plus 0 warmup) 1.685 ms avg, 1101.5 qps
设置两个并发链接(-c 100000
),发送20个请求(-n 200000
)
kubectl exec -it fortio-deploy-d86464f9d-ks4hm -c fortio /usr/local/bin/fortio -- load -c 100000 -qps 0 -n 20000 -loglevel Warning http://nginx.fengjian > 50 <= 75 , 62.5 , 42.37, 12001 > 75 <= 100 , 87.5 , 47.88, 5515 > 100 <= 283.465 , 191.733 , 100.00, 52118 # target 50% 107.452 # target 75% 195.459 # target 90% 248.263 # target 99% 279.945 # target 99.9% 283.113 Sockets used: 100001 (for perfect keepalive, would be 100000) Code -1 : 62857 (62.9 %) Code 200 : 19587 (19.6 %) Code 503 : 17558 (17.6 %) Response Header Sizes : count 100002 avg 46.731345 +/- 94.69 min 0 max 240 sum 4673228 Response Body/Total Sizes : count 100002 avg 86.594238 +/- 113 min 0 max 249 sum 8659597 All done 100002 calls (plus 0 warmup) 123391.636 ms avg, 352.1 qps
策略实施
限流
如果是一个外部付费API服务, 限制1 qps免费, 使用istio 控制。