前面演示了 Ingress 在 Kubernetes 中的应用,这章介绍下 Istio Gateway 入站网关。
下载 Istio
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.9.0
cat >> /etc/profile <
安装 Istio
# istioctl install -f /opt/cloud/istio-operator.yml -y
istioctl install --set profile=demo -y
kubectl label namespace cloud istio-injection=enabled
输出信息:
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete
Istio 控制器运行起来后,可以通过编辑 IstioOperator
对象来改变 Istio 的配置。控制器会检测到改变,继而用相应配置更新 Istio 的安装内容。istio-operator.yml
这个文件我也放在了项目的根目录下。
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: cloud-istio-operator
namespace: istio-system
spec:
profile: demo
meshConfig:
accessLogFile: /dev/stdout
accessLogEncoding: TEXT
安装完成后,即可在 Dashboard 界面看到 istio-system 命名空间下的 Workloads、Discovery and Load Balancing 信息。
在 istio-1.9.0/samples/addons 下有一些监控、追踪的插件 Prometheus、 Grafana、Jaeger,安装很容易,下面一条命令即可,你可以去部署了解一下。
# 在 istio-1.9.0 文件夹下
kubectl apply -f samples/addons
下图是 Istio 可视化工具 Kiali 的界面:
上面在安装 Istio 的时候,执行了这一句:kubectl label namespace cloud istio-injection=enabled
它会给我们微服务所在的 cloud 命名空间添加 istio-injection=enabled
标签,这样在部署应用的时候,Istio 会自动的将 sidecar 容器注入到 Pod 中 ,查看命名空间标签信息:
[root@k8s002 ~]# kubectl get namespaces --show-labels
NAME STATUS AGE LABELS
cloud Active 4d istio-injection=enabled
default Active 4d istio-injection=enabled
ingress-nginx Active 3d1h app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
istio-system Active 34h istio-injection=disabled
Istio 的流量管理模型源于和服务一起部署的 Envoy 代理,网格内服务发送和接收的所有流量都经由 Envoy 代理,这让控制网格内的流量变得非常简单,而且不需要对服务做任何的更改,它是一种与语言彻底解偶的服务治理方案。
上一节,我们已经将几个微服务部署到了 Kubernetes 平台,现在为了给我们的服务添加 Istio 支持,通过下面的命令将 cloud 命名空间下的所有 Pod 都删除,然后 Deployment、ReplicaSet 控制器模式会重建所有 Pod,这时 Istio 会自动为我们的服务注入 sidecar 容器。
kubectl delete --all pods --namespace=cloud
这时候再去看下新的 Pod 信息,已经被自动注入了下面两个容器:istio-proxy、istio-init。(或者通过命令 kubectl describe pod {name} -n cloud
查看 Pod 信息)
sidecar 注入其实就是在 Pod 的 YML 模板中添加额外的容器配置,这个模板在名称为 istio-sidecar-injector 的 Config Maps 对象中。
-
istio-init
它的作用是配置 iptables 规则,将入站、出站流量交由 istio-proxy 代理,init 容器会在应用容器启动之前运行,并且执行完成后就退出了,通常用来完成一些初始化任务。
-
istio-proxy
真正的 sidecar 代理,基于 Envoy 实现。
istio-proxy 是如何获取应用容器的入站和出站流量?答案就在 istio-init 这个容器中。
initContainers:
- name: istio-init
image: docker.io/istio/proxyv2:1.9.0
args:
- istio-iptables
- '-p'
- '15001'
- '-z'
- '15006'
- '-u'
- '1337'
- '-m'
- REDIRECT
- '-i'
- '*'
- '-x'
- ''
- '-b'
- '*'
- '-d'
- 15090,15021,15020
-
-p 15001
表示出站流量被 iptable 重定向到 Envoy 的 15001 端口 -
-z 15006
表示入站流量被 iptable 重定向到 Envoy 的 15006 端口 -
-u 1337
用于排除用户 ID 为 1337 即 istio-proxy 自身的流量
[root@k8s002 ~]# docker ps -a | grep istio-init
a06bf54904f9 istio/proxyv2 "/usr/local/bin/pilo…" 5 hours ago Exited (0) 5 hours ago k8s_istio-init_user-server-9b9656cbb-7vsx2_cloud_2809324e-6fc6-4f32-8fec-c7dfa95f0497_0
ab76b4bfb6f1 istio/proxyv2 "/usr/local/bin/pilo…" 5 hours ago Exited (0) 5 hours ago k8s_istio-init_order-server-d5f799765-9w7vn_cloud_98772936-423f-4796-b6d4-fdf170b8bb2a_0
35149e8f46b1 istio/proxyv2 "/usr/local/bin/pilo…" 5 hours ago Exited (0) 5 hours ago k8s_istio-init_actuator-admin-7c595b74d9-kn6wk_cloud_3ce9994b-0b52-4b81-b93d-978134bbbeb7_0
通过 docker logs a06bf54904f9 查看一个 istio-init 容器的日志,会输出 istio-init 所配置的 iptables 规则。
*nat
......
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
......
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
......
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT
-
PREROUTING
所有到达 Pod 的入站流量被重定向到端口 15006 -
OUTPUT
所有出站流量被重定向到端口 15001,这些都是 istio-proxy 正在监听的端口。
[root@k8s002 ~]# docker exec -it b5e4c3488480 netstat -tnlp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 13/envoy
tcp 0 0 0.0.0.0:9097 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 13/envoy
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 13/envoy
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 13/envoy
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 13/envoy
tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
在 Kubernetes 中,代理被注入到 Pod 中,通过配置 iptables 规则来捕获流量,一旦 sidecar 代理被注入,Istio 就可以通过 sidecar 代理来协调所有的流量。
下面是 order-server 中 sidecar 代理的部分日志,会看到所有 inbound 和 outbound 的请求信息,下面一个是 actuator-admin 服务探测客户端是否在线的请求,另一个是 eureka 客户端的续约请求。查看 istio-proxy 日志命令:kubectl logs -f -l app=order-server -c istio-proxy -n cloud
- -l 指定标签信息
- -c 指定容器名称
- -n 指定命名空间
[2021-02-24] "GET /actuator/health HTTP/1.1" 200 - via_upstream - "-" 0 15 2 1 "-" "ReactorNetty/0.8.15.RELEASE" "8bcd13fa-2e95-90c6-8fa3-a579c36facf2" "10.244.1.70:9099" "127.0.0.1:9099" inbound|9099|| 127.0.0.1:38332 10.244.1.70:9099 10.244.1.69:34062 - default
[2021-02-24] "PUT /eureka/apps/ORDER-SERVER/10.244.1.70:9099?status=UP&lastDirtyTimestamp=1614156772911 HTTP/1.1" 200 - via_upstream - "-" 0 0 1 1 "-" "Java-EurekaClient/v1.9.8" "6b711a7c-1d72-9f0b-a825-1fafdb9da462" "eureka-server:18761" "10.244.0.16:18761" outbound|18761||eureka-server.cloud.svc.cluster.local 10.244.1.70:53380 10.97.40.48:18761 10.244.1.70:45068 - default
[2021-02-24] "GET /eureka/apps/delta HTTP/1.1" 200 - via_upstream - "-" 0 87 1 1 "-" "Java-EurekaClient/v1.9.8" "179d11d3-5591-92c7-871b-05bfd71e98fd" "eureka-server:18761" "10.244.0.16:18761" outbound|18761||eureka-server.cloud.svc.cluster.local 10.244.1.70:53380 10.97.40.48:18761 10.244.1.70:45068 - default
下面演示如何通过 Istio Ingress Gateway 来转发外部请求。
在 Kubernetes 环境中,使用 Ingress 对象来指定需要暴露到集群外的服务,但是 Ingress 本身是不支持 TCP 协议的,只能用于 HTTP 流量。在 Istio 服务网格中,使用了一种新的配置模型 Istio Gateway 将服务暴露至服务网格之外,它通过将 L4 配置与 L7 配置分离的方式克服了 Ingress 不支持 TCP 路由的问题。
- Gateway
- VirtualService
Istio Gateway 是运行在网格边界的独立 Envoy 代理,而不是工作负载的 sidecar 代理,Istio 为我们提供了一些预先配置好的网关代理部署 istio-ingressgateway 和 istio-egressgateway,下面要创建的 cloud-gateway 就是基于 istio-ingressgateway 实现的 selector:istio:ingressgateway
。
cloud-gateway 网关配置让 HTTP 流量从 47.103.80.230 通过 80 端口流入网格,这里没有为请求指定任何的路由规则,要为网关指定路由,需要把网关绑定到虚拟服务 VirtualService 上。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: cloud-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- 47.103.80.230
VirtualService
在 VirtualService 中使用路由规则,告诉 istio-ingressgateway 将请求路由到哪个服务,路由目标地址可以是同一服务的不同版本,也可以是完全不同的服务(DestinationRule),未匹配任何路由规则的请求均会被拒绝并返回 404 响应。
VirtualService 中的路由规则要比 Ingress 强大很多,它可以配置请求超时时间、失败重试次数、熔断器,可以进行故障注入测试,而且它提供的匹配规则非常丰富,可以在端口、header 字段、URI 、queryParams 查询参数等内容上设置匹配条件,还可以对请求进行重定向 HTTPRedirect
、重写 HTTPRewrite
,这些功能在项目中均提供了示例。
spec:hosts
虚拟服务主机名可以是 IP 地址,或者是完全限定域名 FQDN,也可以使用通配符 "*" 前缀,匹配所有服务的路由规则。
destination:host
必须是存在于 Istio 服务注册中心的实际目标地址,否则 Envoy 代理不知道将请求发送到哪里。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user
spec:
hosts:
- "*"
gateways:
- cloud-gateway
http:
- match:
- uri:
prefix: /user
route:
- destination:
port:
number: 9097
host: user-server.cloud.svc.cluster.local
timeout: 10s
retries:
attempts: 3
perTryTimeout: 2s
...... 完整配置,请 clone 项目后查看
我们 cloud 微服务演示项目提供的 Istio 网关路由配置文件在项目的根目录下,名称为 istio-gateway.yml。
部署命令 kubectl apply -f /opt/cloud/istio-gateway.yml,部署成功后即可进行下面的测试。
Ingress Gateway 使用了 Loadbalancer 的方式暴露,通过 kubectl get svc -n istio-system
命令查看到 istio-ingressgateway 暴露到了 31963 端口,下面通过几个微服务的接口来测试下(注意暴露端口需要添加 ECS 安全组规则)。
[root@k8s001 ~]# kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egressgateway ClusterIP 10.109.231.239 80/TCP,443/TCP,15443/TCP 165m
istio-ingressgateway LoadBalancer 10.99.186.2 15021:31872/TCP,80:31963/TCP,443:31474/TCP,31400:30316/TCP,15443:32160/TCP 165m
istiod ClusterIP 10.109.254.235 15010/TCP,15012/TCP,443/TCP,15014/TCP 165m
下面是每个微服务中提供的一些接口,可以访问测试下连通性:
接口地址 | 调用服务 |
---|---|
http://IP:31963/order/init | order-server |
http://IP:31963/auth/init | auth-server |
http://IP:31963/order/user/list | order-server -> user-server -> auth-server |
http://IP:31963/user/init | user-server |
访问后,可以查看 istio-ingressgateway 的日志,会输出一次请求的具体信息。
查看日志命令为:kubectl logs -f -l istio=ingressgateway -c istio-proxy -n istio-system
[2021-02-24T06:30:07.775Z] "GET /order/init HTTP/1.1" 200 - via_upstream - "-" 0 86 9 8 "10.244.1.1" "PostmanRuntime/7.26.10" "e304f605-0047-93b8-b201-10da3ce764fe" "47.103.80.230:31963" "10.244.1.66:9099" outbound|9099||order-server.cloud.svc.cluster.local 10.244.1.57:58992 10.244.1.57:8080 10.244.1.1:24551 - -
[2021-02-24T06:30:12.370Z] "GET /auth/init HTTP/1.1" 200 - via_upstream - "-" 0 85 6 6 "10.244.1.1" "PostmanRuntime/7.26.10" "d1afb272-413c-987b-92c4-d6dc5514823b" "47.103.80.230:31963" "10.244.1.63:9096" outbound|9096||auth-server.cloud.svc.cluster.local 10.244.1.57:60132 10.244.1.57:8080 10.244.1.1:24551 - -
[2021-02-24T06:30:21.660Z] "GET /order/user/list HTTP/1.1" 200 - via_upstream - "-" 0 204 22 22 "10.244.1.1" "PostmanRuntime/7.26.10" "b7ba1167-4507-9f9a-8ef9-2a2b5dc09c51" "47.103.80.230:31963" "10.244.1.66:9099" outbound|9099||order-server.cloud.svc.cluster.local 10.244.1.57:58992 10.244.1.57:8080 10.244.1.1:24551 - -
[2021-02-24T06:31:24.201Z] "GET /user/init HTTP/1.1" 200 - via_upstream - "-" 0 85 3 2 "10.244.1.1" "PostmanRuntime/7.26.10" "d880d400-ff95-9958-b004-e39eca0cafc8" "47.103.80.230:31963" "10.244.1.67:9097" outbound|9097||user-server.cloud.svc.cluster.local 10.244.1.57:44756 10.244.1.57:8080 10.244.1.1:24551 - -
为了在网格中导流,Istio 需要知道所有的 endpoint 在哪并且属于哪个服务,Istio 自身并不提供服务发现功能,它会连接到一个服务发现系统,我们这里是在 Kubernetes 平台中部署 Istio,它会自动检测该集群中的服务和 endpoint。
Istiod
在 Istio 1.5 之后,控制平面进行了简化,将 Pilot、Galley、Citadel 和 sidecar 注入器执行的功能都合并成了单一的 istiod 服务,只需启动单个 Pod,就可以启用一个包含了所有功能的 Istio 控制平面。
istio-proxy@istiod-6f984b7878-8zxjc:/$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
istio-p+ 1 0.2 1.0 784732 79848 ? Ssl 09:15 0:33 /usr/local/bin/pilot-discovery discovery --monitoringAddr=:15014 --log_output_level=default:info --domain cluster.local --keepalive
istio-p+ 53 0.0 0.0 18504 2072 pts/0 Ss 12:52 0:00 bash
istio-proxy@istiod-6f984b7878-8zxjc:/$ netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:9876 0.0.0.0:* LISTEN 1/pilot-discovery
tcp6 0 0 :::15010 :::* LISTEN 1/pilot-discovery
tcp6 0 0 :::15012 :::* LISTEN 1/pilot-discovery
tcp6 0 0 :::15014 :::* LISTEN 1/pilot-discovery
tcp6 0 0 :::15017 :::* LISTEN 1/pilot-discovery
tcp6 0 0 :::8080 :::* LISTEN 1/pilot-discovery
Istio 提供的几个分析、诊断命令:
- istioctl analyze 可以检测 Istio 配置的潜在问题
[root@k8s002 istio-1.9.0]# istioctl analyze -n cloud
Info [IST0118] (Service actuator-admin.cloud) Port name (port: 5000, targetPort: 5000) doesn't follow the naming convention of Istio port.
Info [IST0118] (Service api-gateway.cloud) Port name (port: 4000, targetPort: 4000) doesn't follow the naming convention of Istio port.
......
上面诊断我们微服务示例命名空间 cloud,提示 Service 对象没有遵循 Istio 的端口命名规则。参看官方说明
# 正确写法如下
spec:
ports:
- name: tcp
port: 5000
protocol: TCP
targetPort: 5000
nodePort: 32700
- istioctl describe 验证 Pod 网络
[root@k8s002 istio-1.9.0]# istioctl experimental describe pod user-server-9b9656cbb-t8dx6 -n cloud
Pod: user-server-9b9656cbb-t8dx6
Pod Ports: 9097 (user-server), 15090 (istio-proxy)
Suggestion: add 'app' label to pod for Istio telemetry.
Suggestion: add 'version' label to pod for Istio telemetry.
--------------------
Service: user-server
Port: 9097/auto-detect targets pod port 9097
Exposed on Ingress Gateway http://172.24.251.203
VirtualService: user.default
/user*
2 additional destination(s) that will not reach this pod
Pod 内的服务容器的端口 9097,Pod 内的 istio-proxy 容器的端口 15090,通过 Ingress Gateway 暴露的 virtual service 规则。
- istioctl proxy-config endpoints 查看当前可用的 endpoint
[root@k8s002 ~]# istioctl proxy-config endpoints order-server-5676b7dcd6-85xrn.cloud | grep cloud
10.244.0.39:4000 HEALTHY OK outbound|4000||api-gateway.cloud.svc.cluster.local
10.244.0.40:9099 HEALTHY OK outbound|9099||order-server.cloud.svc.cluster.local
10.244.0.41:9097 HEALTHY OK outbound|9097||user-server.cloud.svc.cluster.local
10.244.1.134:5000 HEALTHY OK outbound|5000||actuator-admin.cloud.svc.cluster.local
10.244.1.135:18761 HEALTHY OK outbound|18761||eureka-server.cloud.svc.cluster.local
10.244.1.136:9096 HEALTHY OK outbound|9096||auth-server.cloud.svc.cluster.local
卸载 Istio 命令:
istioctl x uninstall --purge
kubectl delete namespace istio-system
~ 终 ~