前言
容器化,云原生越演越烈,新概念非常之多。信息爆炸的同时,带来层层迷雾。我尝试从扩容出发理解其脉路,经过实践探索,整理形成一个入门教程,包括下面四篇文章。
这是第四篇,istio分流应用。
istio
服务网格(Service Mesh)这个术语通常用于描述构成这些应用程序的微服务网络以及应用之间的交互。随着规模和复杂性的增长,服务网格越来越难以理解和管理。它的需求包括服务发现、负载均衡、故障恢复、指标收集和监控以及通常更加复杂的运维需求,例如 A/B 测试、金丝雀发布、限流、访问控制和端到端认证等。
Istio 提供了一个完整的解决方案,通过为整个服务网格提供行为洞察和操作控制来满足微服务应用程序的多样化需求。
安装istio
istio1.1
支持kubernetes1.11,1.12,1.13,Docker for mac的stable版本太低,需要切换Docker for mac 到 edge版本,kubernetes会升级到1.13。使用 kubectl version
确认kubernetes版本:
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.0", GitCommit:"ddf47ac13c1a9483ea035a79cd7c10005ff21a6d", GitTreeState:"clean", BuildDate:"2018-12-03T21:04:45Z", GoVersion:"go1.11.2", Compiler:"gc", Platform:"darwin/amd64"}Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.0", GitCommit:"ddf47ac13c1a9483ea035a79cd7c10005ff21a6d", GitTreeState:"clean", BuildDate:"2018-12-03T20:56:12Z", GoVersion:"go1.11.2", Compiler:"gc", Platform:"linux/amd64"}复制代码
按照安装istio到k8s。 kubectl get svc -n istio-system
查看isto服务(svc是service的速记别名)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEgrafana ClusterIP 10.96.238.843000/TCP 19sistio-citadel ClusterIP 10.110.233.235 8060/TCP,15014/TCP 19sistio-egressgateway ClusterIP 10.107.81.184 80/TCP,443/TCP,15443/TCP 20sistio-galley ClusterIP 10.108.48.185 443/TCP,15014/TCP,9901/TCP 20sistio-ingressgateway LoadBalancer 10.99.64.197 localhost 15020:31681/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:32145/TCP,15030:31376/TCP,15031:30092/TCP,15032:31491/TCP,15443:32689/TCP 19sistio-pilot ClusterIP 10.101.105.130 15010/TCP,15011/TCP,8080/TCP,15014/TCP 19sistio-policy ClusterIP 10.107.208.244 9091/TCP,15004/TCP,15014/TCP 19sistio-sidecar-injector ClusterIP 10.104.152.19 443/TCP 19sistio-telemetry ClusterIP 10.102.233.77 9091/TCP,15004/TCP,15014/TCP,42422/TCP 19sjaeger-agent ClusterIP None 5775/UDP,6831/UDP,6832/UDP 18sjaeger-collector ClusterIP 10.100.208.250 14267/TCP,14268/TCP 18sjaeger-query ClusterIP 10.104.246.180 16686/TCP 18skiali ClusterIP 10.100.34.9 20001/TCP 19sprometheus ClusterIP 10.103.238.247 9090/TCP 19stracing ClusterIP 10.103.49.38 80/TCP 18szipkin ClusterIP 10.105.126.70 9411/TCP复制代码
查看isto的pod是否全部正常启动:
➜ kubectl get pods -n istio-systemNAME READY STATUS RESTARTS AGEgrafana-749c78bcc5-jczt2 1/1 Running 1 20mistio-citadel-899dfb67c-r6cmx 1/1 Running 0 20mistio-cleanup-secrets-1.1.3-d42jx 0/1 Completed 0 20mistio-egressgateway-748d5fd794-zkcjs 1/1 Running 1 20mistio-galley-555dd7c7d7-t9fhz 1/1 Running 0 20mistio-grafana-post-install-1.1.3-2qzg5 0/1 Completed 0 20mistio-ingressgateway-55dd86767f-n5v7m 1/1 Running 1 20mistio-pilot-7979d58649-lg6vq 2/2 Running 0 20mistio-policy-f89c945dc-8d6mn 2/2 Running 0 55sistio-security-post-install-1.1.3-4ggpf 0/1 Completed 0 20mistio-sidecar-injector-998dd6cbb-fbdgw 1/1 Running 0 20mistio-telemetry-7d9d866c65-2pngr 2/2 Running 0 16sistio-tracing-595796cf54-mp6tp 1/1 Running 1 20mkiali-5df77dc9b6-jq26m 1/1 Running 0 20mprometheus-7f87866f5f-p2568 1/1 Running 0 20m复制代码
部署应用
调整pods符合istio规范
部署引用前,需要按照,进行微调。
微调后的istio\flaskapp.yaml
如下:
apiVersion: v1kind: Servicemetadata: name: flaskappspec: ports: - port: 5000 name: http selector: app: business---apiVersion: extensions/v1beta1 kind: Deploymentmetadata: name: flaskappspec: replicas: 1 template: metadata: labels: app: business version: v1 spec: containers: - image: flaskapp:0.0.2 name: flaskapp ports: - containerPort: 5000复制代码
微调后的istio\redis.yaml
如下:
apiVersion: v1kind: Servicemetadata: name: redisspec: ports: - port: 6379 name: redis selector: app: redis---apiVersion: extensions/v1beta1 kind: Deploymentmetadata: name: redisspec: replicas: 1 template: metadata: labels: app: redis version: v1 spec: containers: - image: redis:4-alpine3.8 name: redisdb ports: - containerPort: 6379复制代码
因为istio附带ingress,所以我们取消了前置的nginx负载,直接使用ingress。
启动istio的sidecar自动注入
istio通过在pod中输入sidecar,用来管理流量,设置default名称空间下默认注入:
➜ docker2istio kubectl label namespace default istio-injection=enablednamespace/default labeled复制代码
启动服务
启动服务方式和k8s没有区别
➜ docker2istio kubectl apply -f k8s/redis.yaml -f k8s/flaskapp.yamlservice/redis createddeployment.extensions/redis createdservice/flaskapp createddeployment.extensions/flaskapp created复制代码
使用kubectl describe pod/flaskapp-757cd47df4-zkzv2
确认istio正常管理app:
Name: flaskapp-757cd47df4-zkzv2Namespace: defaultPriority: 0PriorityClassName:Node: docker-desktop/192.168.65.3Start Time: Wed, 24 Apr 2019 18:43:16 +0800Labels: app=business pod-template-hash=757cd47df4 version=v1Annotations: sidecar.istio.io/status: { "version":"3420543c87a5049f8ec099530c3992a5c1f06bf54fa56d7a3877e7ffc658ea8d","initContainers":["istio-init"],"containers":["istio-proxy"]...Status: RunningIP: 10.1.0.45Controlled By: ReplicaSet/flaskapp-757cd47df4Init Containers: istio-init: Container ID: docker://0bac5e70832c287a1c446d38034c75ebe5b8e66b9e2fb6d5a1fc3bcb8a9f644c Image: docker.io/istio/proxy_init:1.1.3 Image ID: docker-pullable://istio/proxy_init@sha256:000d022d27c198faa6cc9b03d806482d08071e146423d6e9f81aa135499c4ed3 Port: Host Port: Args: ... Containers: flaskapp: Container ID: docker://9cdf76e12c710f1d50c2b3a2ac349909f481975f220af5b31e1b76717bcabd95 Image: flaskapp:0.0.2 Image ID: docker://sha256:d52877069956696bb9009c0af43a0d339d2d253e67f5fb09c9f1f052a35528de Port: 5000/TCP Host Port: 0/TCP State: Running Started: Wed, 24 Apr 2019 18:43:19 +0800 Ready: True Restart Count: 0 Environment: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-ndv6q (ro) istio-proxy: Container ID: docker://84eecd89d7c203b7b97749d2a02c63cb8b4c57cfda96f0c7585547a8918344c1 Image: docker.io/istio/proxyv2:1.1.3 Image ID: docker-pullable://istio/proxyv2@sha256:b682918f2f8fcca14b3a61bbd58f4118311eebc20799f24b72ceddc5cd749306 Port: 15090/TCP Host Port: 0/TCP Args: ... State: Running Started: Wed, 24 Apr 2019 18:43:19 +0800 Ready: True Restart Count: 0 Limits: cpu: 2 memory: 128Mi Requests: cpu: 10m memory: 40Mi Readiness: http-get http://:15020/healthz/ready delay=1s timeout=1s period=2s #success=1 #failure=30 Environment: POD_NAME: flaskapp-757cd47df4-zkzv2 (v1:metadata.name) POD_NAMESPACE: default (v1:metadata.namespace) INSTANCE_IP: (v1:status.podIP) ISTIO_META_POD_NAME: flaskapp-757cd47df4-zkzv2 (v1:metadata.name) ISTIO_META_CONFIG_NAMESPACE: default (v1:metadata.namespace) ISTIO_META_INTERCEPTION_MODE: REDIRECT ISTIO_METAJSON_LABELS: { "app":"business","pod-template-hash":"757cd47df4","version":"v1"} Mounts: /etc/certs/ from istio-certs (ro) /etc/istio/proxy from istio-envoy (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-ndv6q (ro)...复制代码
!!!注意 这里的pod,会被自动注入名为istio-init的initContainer和名为**istio-proxy的container。具有这2个container,标志pod接受istio流量管理。
确认服务正常启动
➜ docker2istio kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEflaskapp ClusterIP 10.107.67.445000/TCP 4m31skubernetes ClusterIP 10.96.0.1 443/TCP 36mredis ClusterIP 10.100.210.72 6379/TCP 4m31s复制代码
确认pod正常启动
➜ docker2istio kubectl get podsNAME READY STATUS RESTARTS AGEflaskapp-77ddb9698c-xb2gm 1/1 Running 0 4m2sredis-c5dd6fcfc-8tqwm 1/1 Running 0 4m2s复制代码
部署istio的gateway
因为取消了前置的nginx负载,需要设置gateway,集群外部才能够访问服务。istio\gateway.yaml
:
apiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata: name: flaskapp-gatewayspec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*"---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: flaskappspec: hosts: - "*" gateways: - flaskapp-gateway http: - match: - uri: exact: / route: - destination: host: flaskapp port: number: 5000复制代码
kubectl apply -f istio/gateway.yaml
提交后,查看gameway的端口:
➜ docker2istio kubectl get svc istio-ingressgateway -n istio-systemNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEistio-ingressgateway LoadBalancer 10.99.64.197 localhost 15020:31681/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:32145/TCP,15030:31376/TCP,15031:30092/TCP,15032:31491/TCP,15443:32689/TCP 179m复制代码
这里的服务类型为LoadBalancer,可以直接使用80端口访问服务:
➜ docker2istio curl http://localhostHello World by 10.1.0.40 from 10.1.0.30 ! 该页面已被访问 1 次。复制代码
金丝雀发布
istio的流量管理提供了非常便捷的金丝雀发布功能。
首先,我们调整app/flaskapp.py
,将域名获取调整成为主机名称:
#-*- coding:utf-8 -*-import socketfrom flask import Flaskfrom redis import Redisapp = Flask(__name__)redis = Redis(host='redis', port=6379)def get_host_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(('8.8.8.8', 80)) ip = s.getsockname()[0] except: ip = '127.0.0.1' finally: s.close() return ipdef get_hostname(): return socket.gethostname()@app.route('/')def hello(): app.logger.debug('hello in') from flask import request count = redis.incr('hits') # host_ip = get_host_ip() host_id = get_hostname() client_ip= request.headers['X-Real-Ip'] if 'X-Real-Ip' in request.headers else request.remote_addr app.logger.debug("Hello out {} {} {}:".format(host_id,client_ip,count)) return 'Hello World by {} from {} ! 该页面已被访问 {} 次。\n'.format(host_id,client_ip,count)if __name__ == "__main__": app.run(host="0.0.0.0", debug=True)复制代码
使用 docker build -f app/Dockerfile -t flaskapp:0.0.3 app
编译新的镜像。
制作新的deployment,使其使用新的镜像:
apiVersion: extensions/v1beta1 kind: Deploymentmetadata: name: flaskapp-v2spec: replicas: 1 template: metadata: labels: app: business version: v2 spec: containers: - image: flaskapp:0.0.3 name: flaskapp ports: - containerPort: 5000复制代码
创建并确认新的部署生效:
➜ docker2istio kubectl apply -f istio/flaskappv2.yamldeployment.extensions/flaskapp-v2 created➜ docker2istio kubectl get deploymentNAME READY UP-TO-DATE AVAILABLE AGEflaskapp 3/3 3 3 72mflaskapp-v2 1/1 1 1 7sredis 1/1 1 1 129m复制代码
调整VirtualService,控制流量分发策略:
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: flaskappspec: hosts: - "*" gateways: - flaskapp-gateway http: - match: - uri: exact: / route: - destination: host: flaskapp port: number: 5000 - route: - destination: host: flaskapp subset: v1 weight: 80 - destination: host: flaskapp subset: v2 weight: 20复制代码
这里重点是指定规则,按照8:2,将flaskapp的访问分配到deployment v1 和 deployment v2 上。
kubectl apply -f istio/gateway2.yaml
应用新的流量分配策略:
访问观察服务:
➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 7 次。➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 8 次。➜ docker2istio curl http://localhostHello World by flaskapp-v2-ff9f8cfdb-vh788 from 127.0.0.1 ! 该页面已被访问 9 次。➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 10 次。➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 11 次。➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 12 次。➜ docker2istio curl http://localhostHello World by 10.1.0.45 from 127.0.0.1 ! 该页面已被访问 13 次。➜ docker2istio curl http://localhostHello World by flaskapp-v2-ff9f8cfdb-vh788 from 127.0.0.1 ! 该页面已被访问 14 次。复制代码
可以看到,访问被按比例分配到v1和v2两个版本上。
总结
从上面实践过程空可见,istio完善了k8s提供的编排功能,在纵向扩容的基础上可以实现横向扩展。