Istio流量管理快速入门

Istio使用场景

在业务更新迭代快速发展时代,更新版本只靠Kubernetes实现简单的更新发布是不行的,如果想要实现对业务流量访问限制还需要借用Istio的能力,比如升级到v2版本,将v2版本接入流量占比要到10%,Kubernetes是无法实现。下面就是整个实现过程。

快速入门

环境准备

主机名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

场景一

模型图

创建资源配置清单

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-dpl-v1.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: front-tomcat
version: v1
name: front-tomcat-v1
spec:
replicas: 1
selector:
matchLabels:
app: front-tomcat
version: v1
template:
metadata:
labels:
app: front-tomcat
version: v1
spec:
containers:
- image: tomcat:9.0-jdk11
name: front-tomcat
command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)账单v1服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
EOF
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 > bill-service-dpl-v1.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
service: bill-service
version: v1
name: bill-service-v1
spec:
replicas: 1
selector:
matchLabels:
service: bill-service
version: v1
template:
metadata:
labels:
service: bill-service
version: v1
spec:
containers:
- image: nginx:alpine
name: bill-service
command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)账单v1服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat > bill-service-svc.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
labels:
service: bill-service
name: bill-service
spec:
ports:
- name: http
port: 9999
protocol: TCP
targetPort: 80
selector:
service: bill-service
type: ClusterIP
EOF

应用资源配置清单

1
2
3
4
5
6
$ kubectl apply -f front-tomcat-dpl-v1.yaml
$ kubectl apply -f bill-service-dpl-v1.yaml
$ kubectl apply -f bill-service-svc.yaml

$ kubectl exec front-tomcat-v1-548b46d488-r7wv8 -- curl -s bill-service:9999
你好, 这是(王先森)账单v1服务中心

场景二

后台账单服务更新v2版本,前期规划90%的流量访问v1版本,接入10%的流量到v2版本

模型图

创建资源配置清单

新增bill-service-dpl-v2.yaml

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 > bill-service-dpl-v2.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
service: bill-service
version: v2
name: bill-service-v2
spec:
replicas: 1
selector:
matchLabels:
service: bill-service
version: v2
template:
metadata:
labels:
service: bill-service
version: v2
spec:
containers:
- image: nginx:alpine
name: bill-service
command: ["/bin/sh", "-c", "echo '你好, 这是(王先森)账单v2服务中心'>/usr/share/nginx/html/index.html;nginx -g 'daemon off;'"]
EOF

此时,访问规则会按照v1和v2的pod各50%的流量分配。

1
2
3
4
5
6
$ kubectl apply -f bill-service-dpl-v2.yaml
$ kubectl exec front-tomcat-v1-5fdcb465f6-2j86p -- curl -s bill-service:9999
你好, 这是(王先森)账单v2服务中心
你好, 这是(王先森)账单v2服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心

K8S默认流量调度机制

集群流量调度规则详解

我们都知道默认访问规则会按照v1和v2的pod各50%的流量分配,那k8s默认的调度机制是怎么实现的呢,现在从网络层面解释下。

curl bill-service:9999相当于请求k8s中SVCip:9999–>查找本地路由通过route -n没有符合进入0.0.0.0转到172.7.100.1网桥到10.1.1.100宿主机上面,然后宿主机看kube-proxy组件通过iptables-save | grep svcip找到链路–>iptables-save | grep 链路–>就可以找到对应pod两个地址的random各自50%的规则。

流量分析详细过程

  1. 执行curl -s bill-service:9999操作时,因为容器内默认dns解析,实际上是在curl后端服务svc地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [root@k8s-master1 bill]# kubectl exec -ti bill-service-v1-59b4f4ccc7-qvhw6 -c bill-server -- sh
    / # nslookup bill-service
    Server: 192.168.0.2
    Address: 192.168.0.2:53

    Name: bill-service.default.svc.cluster.local
    Address: 192.168.18.251
    [root@k8s-master1 bill]# kubectl get svc
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    bill-service ClusterIP 192.168.18.251 <none> 9999/TCP 2d23h
    kubernetes ClusterIP 192.168.0.1 <none> 443/TCP 217d
  2. 通过默认路由(0.0.0.0)规则转到172.17.100.1网桥

    1
    2
    3
    4
    5
    6
    7
    8
    [root@k8s-master1 bill]# kubectl exec -ti bill-service-v1-59b4f4ccc7-qvhw6 -c bill-server -- sh
    / # route
    Kernel IP routing table
    Destination Gateway Genmask Flags Metric Ref Use Iface
    default 172.7.100.1 0.0.0.0 UG 0 0 0 eth0
    172.7.100.0 * 255.255.255.0 U 0 0 0 eth0

    # 没有找到192.168.18.251 规则,就通过默认路由(0.0.0.0)转到172.17.100.1的网关上。
  3. 宿主机并没有维护规则,流量而是跳转到iptables查看规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@k8s-master1 bill]# route -n
    Kernel IP routing table
    Destination Gateway Genmask Flags Metric Ref Use Iface
    0.0.0.0 10.1.1.2 0.0.0.0 UG 0 0 0 eth0
    10.1.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
    169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
    172.7.100.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
    172.7.120.0 10.1.1.120 255.255.255.0 UG 0 0 0 eth0
    172.7.130.0 10.1.1.130 255.255.255.0 UG 0 0 0 eth0
    # 实际上宿主机部署kube-proxy组件,kube-proxy维护了iptables规则,流量虽然没有直接的宿主机route规则,但是流量访问时已经被iptables拦截,我们看下iptable有没有配置相关规则
    [root@k8s-master1 bill]# iptables-save | grep 192.168.18.251
    -A KUBE-SERVICES -d 192.168.18.251/32 -p tcp -m comment --comment "default/bill-service:http cluster IP" -m tcp --dport 9999 -j KUBE-SVC-BY6GMFMEZFXQ2BDP
    -A KUBE-SVC-BY6GMFMEZFXQ2BDP ! -s 172.7.0.0/16 -d 192.168.18.251/32 -p tcp -m comment --comment "default/bill-service:http cluster IP" -m tcp --dport 9999 -j KUBE-MARK-MASQ

Istio注入资源配置清单

Istio注入方法1

1
2
3
4
5
6
7
istioctl kube-inject -f bill-service-dpl-v1.yaml|kubectl apply -f -
istioctl kube-inject -f bill-service-dpl-v2.yaml|kubectl apply -f -
istioctl kube-inject -f front-tomcat-dpl-v1.yaml|kubectl apply -f -
#
kuubectl apply -f <(istioctl kube-inject -f bill-service-dpl-v1.yaml)
kuubectl apply -f <(istioctl kube-inject -f bill-service-dpl-v2.yaml)
kuubectl apply -f <(istioctl kube-inject -f front-tomcat-dpl-v1.yaml)

Istio注入方法2

通过向名称空间加入自动注入标签

1
kubectl label namespace bookinfo istio-injection=enabled

创建流量调度资源配置清单

若想实现上述需求,需要解决如下两个问题:

  • 让访问账单服务的流量按照我们期望的比例,其实是一条路由规则,如何定义这个规则
  • 如何区分两个版本的服务

这里需要创建两个新的资源类型:VirtualServiceDestinationRule

VirtualService是一个虚拟的service,描述的是满足什么条件的流量被那个后端处理。类似于根据路径去匹配方法,是更开放的match条件。

DestinationRule描述的是这个请求到达某个后端后怎么去处理,是方法内的处理逻辑。
所以负载均衡和熔断策略是定义在DestinationRule中的,还可以配置连接池大小、异常实例驱逐规则等功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat > bill-service-destnation-rule.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: dest-bill-service
spec:
host: bill-service # 标识规则的适用对象,取值是Service资源名称,可以是网格内或以serviceEntry方式注册的网格外的服务
subsets: # 定义的服务子集,经常用来定义一个服务版本。
- name: v1
labels: # 标签选择器
version: v1
- name: v2
labels:
version: v2
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cat > bill-service-virtualservice.yaml <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-bill-service
spec:
hosts: # 列出VirtualService的hosts,可以是IP、DNS名称、FQDN或*
- bill-service
http: # 指定符合规则的流量到那些Destination,支持HTTP/1.1、HTTP2,及gRPC等协议
- name: bill-service-route
route:
- destination: # 满足条件会把流量打入具体的Destination
host: bill-service
subset: v1 # 选择服务版本
weight: 90 # 权重
- destination:
host: bill-service
subset: v2
weight: 10
EOF

使用client验证流量分配是否生效。

1
2
3
4
5
6
7
8
9
10
11
$ kubectl apply -f bill-service-virtualservice.yaml
$ kubectl apply -f bill-service-destnation-rule.yaml
$ kubectl exec front-tomcat-v1-598964b695-868vf -c front-tomcat -- curl -s bill-service:9999
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v2服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心

这里就会发现规划90%的流量访问v1版本、接入10%的流量到v2版本,就已经实现了。

Istio注入报错

1
2
[root@k8s-master1 ~]# istioctl version
2023-02-23T07:44:24.585408Z error klog an error occurred forwarding 42999 -> 15014: error forwarding port 15014 to pod b56d93be9c44a8f9e668503d735bed98386b5bda9fdadc761b814b0582ef6e14, uid : unable to do port forwarding: socat not found

需要安装socat:yum -y install socat