Kubernetes微服务架构的演进与优化——从Ingress到Istio
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:Linux运维老纪的首页,持续学习,不断总结,共同进步,活到老学到老
导航剑指大厂系列:全面总结 运维核心技术:系统基础、数据库、网路技术、系统安全、自动化运维、容器技术、监控工具、脚本编程、云服务等。
常用运维工具系列:常用的运维开发工具, zabbix、nagios、docker、k8s、puppet、ansible等
数据库系列:详细总结了常用数据库 mysql、Redis、MongoDB、oracle 技术点,以及工作中遇到的 mysql 问题等
懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨
1、简介
在现代云原生应用中,服务网格和Ingress控制器都是常用的组件。服务网格(如Istio、Linkerd)提供了服务间的通信、监控、安全等功能,而Ingress控制器则帮助我们从外部访问集群内部的服务。
对于Kubernetes的Service,无论是Cluster-Ip和NodePort均是四层的负载,集群内的服务如何实现七层的负载均衡,这就需要借助于Ingress,Ingress控制器的实现方式有很多,比如nginx, Contour, Haproxy, trafik, Istio。几种常用的ingress功能对比和选型可以参考这里。
2、常用组件
Ingress-nginx是7层的负载均衡器 ,负责统一管理外部对k8s cluster中Service的请求。主要包含:
-
ingress-nginx-controller:根据用户编写的ingress规则(创建的ingress的yaml文件),动态的去更改nginx服务的配置文件,并且reload重载使其生效(是自动化的,通过lua脚本来实现)。
-
Ingress资源对象:将Nginx的配置抽象成一个Ingress对象。
3、原理![](https://i-blog.csdnimg.cn/direct/7e646144ff6147b299e5374ca98dec8e.png)
ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化。
-
然后读取ingress规则(规则就是写明了哪个域名对应哪个service),按照自定义的规则,生成一段nginx配置。
-
再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器把生成的nginx配置写入/etc/nginx/nginx.conf文件中。
-
然后reload一下使配置生效。以此达到域名分别配置和动态更新的问题。
4、安装Ingress控制器
[root@mast01 istio]# kubectl apply -f ingress-nginx.yaml [root@mast01 istio]# kubectl get pod -n ingress-nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ingress-nginx-admission-create-nrfz9 0/1 Completed 0 86m 10.244.205.195 work01 <none> <none> ingress-nginx-admission-patch-6pjjv 0/1 Completed 1 86m 10.244.75.85 work02 <none> <none> ingress-nginx-controller-b7qf6 1/1 Running 0 86m 192.168.58.117 work01 <none> <none> ingress-nginx-controller-szc5z 1/1 Running 0 86m 192.168.58.118 work02 <none> <none>
5、使用ingress访问网格服务
[root@mast01 istio]# vim front-tomcat-ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: front-tomcat namespace: istio-demo spec: rules: - host: tomcat.istio-demo.com http: paths: - backend: serviceName: front-tomcat servicePort: 8080 path: / status: loadBalancer: {} [root@mast01 ~]# kubectl exec -it ingress-nginx-controller-szc5z -n ingress-nginx bash bash-5.1$ ps -ef PID USER TIME COMMAND 1 www-data 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --configmap=ingress-nginx/ing 7 www-data 0:10 /nginx-ingress-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --configmap=ingress-nginx/ingress-nginx-controller 26 www-data 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf 31 www-data 0:02 nginx: worker process 32 www-data 0:02 nginx: worker process 33 www-data 0:00 nginx: cache manager process 108 www-data 0:00 bash 114 www-data 0:00 ps -ef bash-5.1$ cat /etc/nginx/nginx.conf ... server_name tomcat.istio-demo.com ; listen 80 ; listen [::]:80 ; listen 443 ssl http2 ; listen [::]:443 ssl http2 ; set $proxy_upstream_name "-"; ssl_certificate_by_lua_block { certificate.call() } location / { set $namespace "istio-demo"; set $ingress_name "front-tomcat"; set $service_name "front-tomcat"; set $service_port "8080"; set $location_path "/"; set $global_rate_limit_exceeding n; ...
查看效果![](https://i-blog.csdnimg.cn/direct/c702fe95d5a04c8d9969a027a201c725.png)
只有网格内部访问会遵从virtualservice
的规则,在宿主机中直接访问Service的ClusterIP还是按照默认的规则转发。流量比例1:1
多路径转发
myblog.luffy.com -> 172.21.51.67 -> /foo service1:4200 /bar service2:8080 / myblog:80 myblog.luffy.com/foo ----> myblog.luffy.com/service1:4200 myblog.luffy.com/bar ----> myblog.luffy.com/service2:8080 myblog.luffy.com/ ----> myblog.luffy.com/myblog:80
实现
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: simple-fanout-example namespace: luffy spec: rules: - host: myblog.luffy.com http: paths: - path: /foo backend: serviceName: service1 servicePort: 4200 - path: /bar backend: serviceName: service2 servicePort: 8080 - path: / backend: serviceName: myblog servicePort: 80
nginx的URL重写
myblog.luffy.com -> 172.21.51.67 -> /foo/ myblog:80/admin/ myblog.luffy.com/foo/ ----> myblog.luffy.com/admin/
实现
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: rewrite-path namespace: luffy annotations: nginx.ingress.kubernetes.io/rewrite-target: /admin/$1 spec: rules: - host: myblog.luffy.com http: paths: - path: /foo/(.*) backend: serviceName: myblog servicePort: 80
6、使用ingressgateway访问网格服务
Ingress本身无法实现流量权重转发,主要因为其设计初衷是用于在集群外部暴露HTTP和HTTPS路由,并基于路径和主机名分发请求,而不是高级流量管理,其设计并不是为了实现高级流量管理和控制。Ingress缺乏细粒度流量控制和动态调整能力,依赖于静态配置和简单的路由规则,无法根据权重进行复杂的流量分配。
对于入口流量管理,您可能会问:为什么不直接使用 Kubernetes Ingress API ?原因是 Ingress API 无法表达 Istio 的路由需求。Ingress 试图在不同的 HTTP 代理之间取一个公共的交集,因此只能支持最基本的 HTTP 路由,最终导致需要将代理的其他高级功能放入到注解(annotation)中,而注解的方式在多个代理之间是不兼容的,无法移植。
Istio Gateway
通过将 L4-L6 配置与 L7 配置分离的方式克服了 Ingress
的这些缺点。Gateway
只用于配置 L4-L6 功能(例如,对外公开的端口,TLS 配置),所有主流的L7代理均以统一的方式实现了这些功能。然后,通过在 Gateway
上绑定 VirtualService
的方式,可以使用标准的 Istio 规则来控制进入 Gateway
的 HTTP 和 TCP 流量。
[root@mast01 istio]# vim front-tomcat-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: front-tomcat-gateway namespace: istio-demo spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - tomcat.istio-demo.com [root@mast01 istio]# kubectl apply -f front-tomcat-gateway-virtualservice.yaml
效果是在Istio的ingress网关上加了一条规则,允许`tomcat.istio-demo.com
的外部http流量进入到网格中,但是只是接受访问和流量输入,当流量到达这个网关时,它还不知道发送到哪里去。
网关已准备好接收流量,我们必须告知它将收到的流量发往何处,这就用到了前面使用过的VirtualService
。
要为进入上面的 Gateway 的流量配置相应的路由,必须为同一个 host 定义一个 VirtualService
,并使用配置中的 gateways
字段绑定到前面定义的 Gateway
上 。
[root@mast01 istio]# vim front-tomcat-gateway-virtualservice.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: gateway-front-tomcat namespace: istio-demo spec: gateways: - front-tomcat-gateway hosts: - tomcat.istio-demo.com http: - name: front-tomcat-route route: - destination: host: front-tomcat subset: v1 weight: 90 - destination: host: front-tomcat subset: v2 weight: 10 [root@mast01 istio]# kubectl apply -f front-tomcat-gateway.yaml
该网关列表指定,只有通过我们指定的网关 front-tomcat-gateway
的流量是允许的。所有其他外部请求将被拒绝,并返回 404 响应。
请注意,在此配置中,来自网格中其他服务的内部请求不受这些规则约束
模拟访问
端口 31927 是通过 Kubernetes 中的 Service 类型为 NodePort 来公开服务的一种方式。在 Kubernetes 中,NodePort 允许将服务的端口映射到每个节点的某个固定端口上,从而可以从集群外部访问该服务。
在 Istio 中,istio-ingressgateway
是负责接收外部流量并进入 Istio 网格的组件。通过将 istio-ingressgateway
配置为 NodePort 类型,Kubernetes 将会把这个服务公开在集群的所有节点上,使用固定的端口号 31927。这样,外部用户可以通过访问任何一个集群节点的 IP 地址和端口 31927,来访问和使用 Istio 网格中的服务。
[root@mast01 istio]# kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
31927
[root@mast01 istio]# curl -HHost:tomcat.istio-demo.com 192.168.58.117:31927/
去除端口
# 在一台80端口未被占用的机器中,如ip为172.21.51.69
$ docker run -d --restart=always -p 80:80 --name istio-nginx nginx:alpine
# 在容器的/etc/nginx/conf.d/目录中,新增配置文件
$ cat front-tomcat.conf
upstream front-tomcat {
server 192.168.58.117:31927;
}
server {
listen 80;
listen [::]:80;
server_name tomcat.istio-demo.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_pass http://front-tomcat;
}
}
$ nginx -s reload
本地配置hosts
192.168.58.117 tomcat.istio-demo.com
查看效果
直接访问http://tomcat.istio-demo.com
即可实现外部域名访问到网格内部服务
7、使用服务网格访问网格服务
使用 Istio 的主要优势之一是它提供了更高级别的服务网格功能,包括流量管理、安全性、监控和可观察性等方面的解决方案。尽管 Kubernetes 的 Ingress 可以用于简单的 HTTP(S) 流量路由和负载均衡,但它在灵活性和功能上远不及 Istio 的服务网格。Istio 提供了诸如智能路由、故障恢复、流量调节、策略执行等功能,使得在微服务架构中管理和保护服务更加方便和强大。相比之下,服务网格(如Istio)专门设计用于微服务间通信管理,提供细粒度的流量控制、动态调整和全面的可观察性,因此能够实现流量权重转发等高级功能。
流程图![](https://i-blog.csdnimg.cn/direct/b6985ef8b0594612b1249c863bbf3098.png)
资源清单
[root@mast01 istio]# vim front-tomcat-service.yaml apiVersion: v1 kind: Service metadata: labels: app: front-tomcat name: front-tomcat namespace: istio-demo spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080 selector: app: front-tomcat type: ClusterIP [root@mast01 istio]# kubectl apply -f front-tomcat-service.yaml [root@mast01 istio]# vim front-tomcat-v2-dpl.yam apiVersion: apps/v1 kind: Deployment metadata: labels: app: front-tomcat version: v2 name: front-tomcat-v2 namespace: istio-demo spec: replicas: 1 selector: matchLabels: app: front-tomcat version: v2 template: metadata: labels: app: front-tomcat version: v2 spec: containers: - image: consol/tomcat-7.0:latest name: front-tomcat command: ["/bin/sh", "-c", "echo 'hello tomcat version2'>/opt/tomcat/webapps/ROOT/index.html;/opt/tomcat/bin/deploy-and-run.sh;"] [root@mast01 istio]# kubectl apply -f <(istioctl kube-inject -f front-tomcat-v2-dpl.yaml)
创建服务网格
[root@mast01 istio]# vim front-tomcat-virtualservice.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: front-tomcat namespace: istio-demo spec: hosts: - front-tomcat http: - name: front-tomcat-route route: - destination: host: front-tomcat subset: v1 weight: 90 - destination: host: front-tomcat subset: v2 weight: 10 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: front-tomcat namespace: istio-demo spec: host: front-tomcat subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 [root@mast01 istio]# kubectl apply -f front-tomcat-virtualservice.yaml [root@mast01 istio]# kubectl get pod -n istio-demo NAME READY STATUS RESTARTS AGE bill-service-v1-7bc6d7b575-tbgrs 2/2 Running 22 (2d23h ago) 15d bill-service-v2-6755bb6b6d-tkrzm 2/2 Running 22 (2d23h ago) 15d front-tomcat-v1-57bc6c5d9f-sfn52 2/2 Running 21 (2d23h ago) 15d front-tomcat-v2-876c9bffd-8ss6r 2/2 Running 0 3h19m # 多次请求,可以看到流量分摊9比1 [root@mast01 istio]# kubectl -n istio-demo exec -ti bill-service-v1-7bc6d7b575-tbgrs -c bill-service sh / # curl front-tomcat:8080 hello tomcat version2
8、总结
Ingress:对接ingress controller,实现外部流量进入集群内部,只适用于 HTTP 流量,使用方式也很简单,只能对 service、port、HTTP 路径等有限字段匹配来路由流量,这导致它无法路由如 MySQL、Redis 和各种私有 RPC 等 TCP 流量。要想直接路由南北向的流量,只能使用 Service 的 LoadBalancer 或 NodePort,前者需要云厂商支持,后者需要进行额外的端口管理。有些 Ingress controller 支持暴露 TCP 和 UDP 服务,但是只能使用 Service 来暴露,Ingress 本身是不支持的,例如 nginx ingress controller,服务暴露的端口是通过创建 ConfigMap 的方式来配置的。