在业务更新迭代快速发展时代,更新版本只靠
Kubernetes
实现简单的更新发布是不行的,如果想要实现对业务流量访问限制还需要借用Istio的能力,比如升级到v2版本,将v2版本接入流量占比要到10%
,Kubernetes是无法实现。下面就是整个实现过程。
主机名 | IP | 角色 |
---|---|---|
k8s-master | eth0:10.1.1.100、docker:172.17.100.0/24 | K8S-master |
k8s-node1 | eth0:10.1.1.120、docker:172.17.120.0/24 | K8S-node |
k8s-node2 | eth0:10.1.1.130、docker:172.17.130.0/24 | K8S-node |
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
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
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
$ 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
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%的流量分配。
$ kubectl apply -f bill-service-dpl-v2.yaml
$ kubectl exec front-tomcat-v1-5fdcb465f6-2j86p -- curl -s bill-service:9999
你好, 这是(王先森)账单v2服务中心
你好, 这是(王先森)账单v2服务中心
你好, 这是(王先森)账单v1服务中心
你好, 这是(王先森)账单v1服务中心
我们都知道默认访问规则会按照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%的规则。
执行curl -s bill-service:9999
操作时,因为容器内默认dns解析,实际上是在curl后端服务svc地址
。
[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
通过默认路由(0.0.0.0)规则转到172.17.100.1网桥
[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的网关上。
宿主机并没有维护规则,流量而是跳转到iptables查看规则
[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
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)
通过向名称空间加入自动注入标签
kubectl label namespace bookinfo istio-injection=enabled
若想实现上述需求,需要解决如下两个问题:
这里需要创建两个新的资源类型:VirtualService
和DestinationRule
VirtualService
是一个虚拟的service,描述的是满足什么条件的流量被那个后端处理。类似于根据路径去匹配方法,是更开放的match条件。
DestinationRule
描述的是这个请求到达某个后端后怎么去处理,是方法内的处理逻辑。
所以负载均衡和熔断策略是定义在DestinationRule中的,还可以配置连接池大小、异常实例驱逐规则等功能。
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
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验证流量分配是否生效。
$ 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版本,就已经实现了。
[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