k8s结合istio实现灰度发布

时间:2022-07-22
本文章向大家介绍k8s结合istio实现灰度发布,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

实现细节

准备镜像

这里使用nginx提供一个简单的页面,然后打包成一个镜像,标记tag为v1,修改页面,再次打包镜像,标记tag为v2,以此镜像做此次的示例

Dockerfile

# cat Dockerfile 
FROM nginx:latest
COPY index.html /usr/share/nginx/html/
EXPOSE 80

页面

cat index.html 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body>
        <h1>灰度发布</h1>
        <p>版本:v1</p>    #打完之后修改此文件,更改为v2,再次构建镜像
</body>
</html>

构建镜像

v1:
docker build -t harbor.yscloud.com/baseimg/nginx-server:v1 .
docker push harbor.yscloud.com/baseimg/nginx-server:v1

v2:
docker build -t harbor.yscloud.com/baseimg/nginx-server:v2 .
docker push harbor.yscloud.com/baseimg/nginx-server:v2

开启自动边车注入

就一条命令即可开启自动边车注入:

kubectl label namespace default istio-injection=enabled

查看开启后的状态:

# kubectl get namespace -L istio-injection
NAME              STATUS   AGE    ISTIO-INJECTION
default           Active   10d    enabled
istio-system      Active   3d6h   
kube-node-lease   Active   10d    
kube-public       Active   10d    
kube-system       Active   10d    

手动边车注入方法

istioctl kube-inject -f app-deploy-v1.yaml | kubectl apply -f -

准备yaml文件

整理流程:

1、先构建两个版本的deployment app-deploy-v1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-server-v1
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  selector:
    matchLabels:
      app: nginx-server
      version: v1
  template:
    metadata:
      labels:
        app: nginx-server
        version: v1
    spec:
      containers:
        - name: nginx-server
          image: harbor.yscloud.com/baseimg/nginx-server:v1
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: 1
              memory: 1024Mi
            requests:
              cpu: 100m
              memory: 100Mi
      imagePullSecrets:
        - name: mima

app-deploy-v2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-server-v2
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  selector:
    matchLabels:
      app: nginx-server
      version: v2
  template:
    metadata:
      labels:
        app: nginx-server
        version: v2
    spec:
      containers:
        - name: nginx-server
          image: harbor.yscloud.com/baseimg/nginx-server:v2
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: 1
              memory: 1024Mi
            requests:
              cpu: 100m
              memory: 100Mi
      imagePullSecrets:
        - name: mima

注意:这里的新老版本的version标签一定不能一样,因为我们做的灰度发布就是根据标签进行选择pod进行访问的。

2、创建service app-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-server
  labels:
    app: nginx-server
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
  selector:
    app: nginx-server
  sessionAffinity: None

这里要注意selector这个标签选择器,我们这里设置的标签app: nginx-server,即所有的流量都会通过svc这个控制器流入到所有包含app: nginx-server的pod里。

3、创建路由规则 app-destination-rule.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: nginx-server
spec:
  host: nginx-server
  subsets: 
  - name: v1
    labels:
      version: v1
  - name: v2
    labels: 
      version: v2

这里定义路由规则,定义两个版本,分别为v1和v2,这两个版本,又分别指定了一个标签,即我们说的路由规则,v1这个版本的流量是入到version: v1,v2则入到version: v2这个pod中

4、创建入口网关

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: nginx-server-gateway
spec:
  selector:
    istio: ingressgateway 
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "nginx.yscloud.com"

这个是流量的入口,指定了hosts,这里可以写*,也可以写具体的域名

5、创建virtualservice app-virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx-server
spec:
  hosts:
  - "nginx.yscloud.com"
  gateways:
  - nginx-server-gateway
  http:
  - route:
    - destination:
        host: nginx-server
        subset: v1
        port:
          number: 80
      weight: 90
    - destination:
        host: nginx-server
        subset: v2
        port:
          number: 80
      weight: 10
    timeout: 60s
    retries:
      attempts: 3
      perTryTimeout: 2s

这个首先要绑定gateway,将流量绑定过来,然后根据destinationrule设置的规则,配置该如何请求,然后根据设置的规则,进行流量拆分,例如这里就是设置的90%的流量分发到v1版本的pod,10%的流量分发到v2。

写好yaml文件后,首先把nginx-server启动起来,即对应的两个deployment文件和service文件,然后启动destinationrule这个规则文件,最后再启动gateway和virtualservice文件。

配置https

生成自签名的证书

生成私钥

# openssl genrsa -out server.key 1024
Generating RSA private key, 1024 bit long modulus
.....++++++
............................++++++
e is 65537 (0x10001)

根据私钥生成证书申请文件csr

# openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Beijing
Locality Name (eg, city) [Default City]:Beijing
Organization Name (eg, company) [Default Company Ltd]:Onair    
Organizational Unit Name (eg, section) []:Onair
Common Name (eg, your name or your server's hostname) []:nginx.yscloud.com
Email Address []:chenfei@cdvcloud.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:Onair

使用私钥对证书申请进行签名,生成证书

# openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 3650
Signature ok
subject=/C=CN/ST=Beijing/L=Beijing/O=Onair/OU=Onair/CN=nginx.yscloud.com/emailAddress=chenfei@cdvcloud.com
Getting Private key

配置nginx

这里需要说明下,我们服务部署完成后,所有的准备都完成后,会面临一个问题,就是外部如何访问我们的这个服务,k8s集群有几种方式可以将集群里的服务通过NodePort,Ingress,LoadBalance等映射出来,同样的istio也是通过这种方式(IngressGateway)

# kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)                                                                                                                                      AGE
istio-ingressgateway   LoadBalancer   10.98.48.40   <pending>     15020:31572/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:30148/TCP,15030:31944/TCP,15031:31757/TCP,15032:30403/TCP,15443:31539/TCP   5d5h

可以看到,80端口被映射到了31380,所以我们的nginx配置如下:

upstream nginx-server {
        server 192.168.1.119:31380;
        server 192.168.1.120:31380;
}

server {
        listen 80;
        server_name     nginx.yscloud.com;

        location / {
        proxy_http_version 1.1;
        proxy_redirect          off;
        proxy_set_header        Host $host; 
        proxy_set_header        X-Real-IP $remote_addr;  #获取真实ip
        proxy_set_header       X-Forwarded-For   $proxy_add_x_forwarded_for; #获取代理者的真实ip
        client_max_body_size    10m;
        client_body_buffer_size 128k;
        proxy_connect_timeout   300s;
        proxy_send_timeout      300s;
        proxy_read_timeout      300s;
        proxy_buffer_size       128k;
        proxy_buffers           2 256k;
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 64;
        proxy_busy_buffers_size 256k;
        proxy_temp_file_write_size 256k;
        proxy_pass http://nginx-server;
        }
}

server {
  listen 443;
  server_name nginx.yscloud.com;
  ssl on;
  ssl_certificate /k8s/tls/server.crt;
  ssl_certificate_key /k8s/tls/server.key;
    ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers          ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers  on;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  10m;
  
  location / {

        proxy_http_version 1.1;
        proxy_redirect          off;
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;  #获取真实ip
        proxy_set_header       X-Forwarded-For   $proxy_add_x_forwarded_for; #获取代理者的真实ip
        client_max_body_size    10m;
        client_body_buffer_size 128k;
        proxy_connect_timeout   300s;
        proxy_send_timeout      300s;
        proxy_read_timeout      300s;
        proxy_buffer_size       128k;
        proxy_buffers           2 256k;
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 64;
        proxy_busy_buffers_size 256k;
        proxy_temp_file_write_size 256k;
        proxy_pass http://nginx-server;
}

}

需要注意的是: 在进行代理的时候,header里一定有host信息,即proxy_set_header Host $host;

测试

内网环境可以访问nginx.yscloud.com这个域名,会看v1和v2出现的比例大概为9:1

如果不能访问,则需要指定DNS地址:192.168.0.88