Istio边界流量-Ingress Gateway

Ingress Gateway简介

传统上,Kubernetes使用Ingress控制器来处理从外部进入集群的流量。使用Istio时,情况不再如此。 Istio已用新的GatewayVirtualServices资源替换了熟悉的Ingress资源。它们协同工作,将流量路由到网格中。在网格内部,不需要Gateway,因为服务可以通过集群本地服务名称相互访问。

Istio流量分发控制

环境准备

主机名IP角色
k8s-mastereth0:10.1.1.100、docker:172.17.100.0/24K8S-master
k8s-node1eth0:10.1.1.120、docker:172.17.120.0/24K8S-node
k8s-node2eth0:10.1.1.130、docker:172.17.130.0/24K8S-node
nginx-proxyeth0:10.1.1.11代理节点

场景模型图

目前我们实现了后台服务层面,前端front调用后端service的流量策略,现在实现下客户访问前端的流量策略注入,区别是前端界面访问需要配置ingress域名。

准备资源配置清单

创建前端UI v2版本以及SVC资源配置清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ cat > front-tomcat-v2-dpl.yaml <<EOF 
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: front-tomcat
version: v2
name: front-tomcat-v2
spec:
replicas: 1
selector:
matchLabels:
app: front-tomcat
version: v2
template:
metadata:
labels:
app: front-tomcat
version: v2
spec:
containers:
- image: tomcat:9.0-jdk11
name: front-tomcat
command: ["/bin/sh", "-c", "mkdir /usr/local/tomcat/webapps/ROOT;echo '这是tomcat-v2版本:<iframe name=\"footer\" marginwidth=0 marginheight=0 width=100% height=50 src=\"http://bill.istio.com/\" frameborder=0></iframe>'>/usr/local/tomcat/webapps/ROOT/index.html;/usr/local/tomcat/bin/catalina.sh run;"]
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat > front-tomcat-service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
labels:
app: front-tomcat
name: front-tomcat
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: front-tomcat
type: ClusterIP
EOF

准备流量调度资源配置清单

将v1版本接入90%的流量,v2版本接入10%的流量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cat > front-tomcat-virtualservice.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: front-tomcat
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
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat > front-tomcat-destinationrule.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: front-tomcat
spec:
host: front-tomcat
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF

应用资源配置清单

1
2
3
4
$ kubectl apply -f front-tomcat-service.yaml
$ kubectl apply -f <(istioctl kube-inject -f front-tomcat-v2-dpl.yaml)
$ kubectl apply -f front-tomcat-virtualservice.yaml
$ kubectl apply -f front-tomcat-destinationrule.yaml

访问网格内服务

使用Ingress来访问网格服务]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > front-tomcat-ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: front-tomcat
spec:
rules:
- host: tomcat.istio.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: front-tomcat
port:
number: 8080
EOF

使用浏览器访问查看效果。

注意:只有网格内部访问会遵从virtualservice的规则,在宿主机中直接访问Service的ClusterIP还是按照默认的规则转发。此时还是各0.5权重,没有调度到istio。

Ingress:对接ingress controller,实现外部流量进入集群内部,只适用于 HTTP 流量,使用方式也很简单,只能对 service、port、HTTP 路径等有限字段匹配来路由流量,这导致它无法路由如 MySQLRedis 和各种私有 RPC 等 TCP 流量。要想直接路由南北向的流量,只能使用 Service 的 LoadBalancerNodePort,前者需要云厂商支持,后者需要进行额外的端口管理。有些 Ingress controller 支持暴露 TCP 和 UDP 服务,但是只能使用 Service 来暴露,Ingress 本身是不支持的,例如 nginx ingress controller,服务暴露的端口是通过创建 ConfigMap 的方式来配置的。

IngressGateway访问网格服务

对于入口流量管理,您可能会问: 为什么不直接使用 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 流量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cat > front-tomcat-gateway.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: front-tomcat-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- tomcat.istio.com
EOF

效果是在Istio的ingress网关上加了一条规则,允许``tomcat.istio.com` 的外部http流量进入到网格中,但是只是接受访问和流量输入,当流量到达这个网关时,它还不知道发送到哪里去。

网关已准备好接收流量,我们必须告知它将收到的流量发往何处,这就用到了前面使用过的VirtualService

要为进入上面的 Gateway 的流量配置相应的路由,必须为同一个 host 定义一个 VirtualService,并使用配置中的 gateways 字段绑定到前面定义的 Gateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat > front-tomcat-gateway-virtualservice.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: gateway-front-tomcat
spec:
gateways:
- front-tomcat-gateway
hosts:
- tomcat.istio.com
http:
- name: front-tomcat-route
route:
- destination:
host: front-tomcat
subset: v1
weight: 90
- destination:
host: front-tomcat
subset: v2
weight: 10
EOF

该网关列表指定,只有通过我们指定的网关 front-tomcat-gateway 的流量是允许的。所有其他外部请求将被拒绝,并返回 404 响应。

请注意,在此配置中,来自网格内部的其他服务请求不受这些规则约束。

1
2
3
4
5
6
7
8
9
10
$ kubectl apply -f front-tomcat-gateway-virtualservice.yaml
$ kubectl apply -f front-tomcat-gateway.yaml
# 验证
$ kubectl get vs gateway-front-tomcat
NAME GATEWAYS HOSTS AGE
gateway-front-tomcat ["front-tomcat-gateway"] ["tomcat.istio.com"] 1h

$ kubectl get gw front-tomcat-gateway
NAME AGE
front-tomcat-gateway 1h

模拟访问:

此时我们已经走到ingressgateway上面了,所以要访问需要确认这里的svc是哪个映射端口,如下ingress映射80:80;ingressgateway映射80:32000,所以要访问域名:32000

1
2
3
4
5
6
7
8
9
10
$ kubectl get svc -n istio-system istio-ingressgateway 
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 192.168.231.83 <pending> 15021:30789/TCP,80:32000/TCP,443:31504/TCP,31400:30737/TCP,15443:32222/TCP 22h

# 也可以直接获取ingressgateway的nodeport
$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
32000

# 访问
$ curl -HHost:tomcat.istio.com 10.1.1.100:32000/

访问流程图:

浏览器访问: http://tomcat.istio.com:30779/

配置Nginx转发域名请求

如何实现不加端口访问网格内服务?

在集群外Nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat > /etc/nginx/conf.d/istio.com.conf <<EOF
upstream default_backend_istio {
server 10.1.1.120:32000 max_fails=3 fail_timeout=10s;
server 10.1.1.100:32000 max_fails=3 fail_timeout=10s;
server 10.1.1.130:32000 max_fails=3 fail_timeout=10s;
}
server {
server_name *.istio.com;

location / {
proxy_pass http://default_backend_istio;
proxy_http_version 1.1; # Istio 默认只支持 HTTP/1.1 以上协议版本,并不支持 HTTP/1.0
proxy_set_header Host $http_host;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
}
}
EOF

在集群内Nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 在K8S集群中使用一台80端口未被占用的机器中,如ip为10.1.1.100
$ docker run -d --restart=always -p 80:80 --name istio-nginx nginx:alpine

# 在容器的/etc/nginx/conf.d/目录中,新增配置文件
$ docker exec -it istio-nginx sh
$ cat > /etc/nginx/conf.d/front-tomcat.conf <<EOF
upstream front-tomcat {
server 192.168.231.83:80;
}
server {
listen 80;
listen [::]:80;
server_name tomcat.istio.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;
}
}
EOF
$ nginx -s reload

绑定hosts

win本地配置hosts,注意配置nginx部署的机器,而不是k8s集群ingress-nginx服务的机器

1
10.1.1.11 tomcat.istio.com

访问结果: