Traefik 2.0新增了很多高级功能,灰度发布就是其中一个,本文我们来介绍一下基于Traefik 2.0实现灰度发布。
注意: 目前最新版本是v2.1.1,截至目前版本要实现灰度发布、流量复制等高级功能,只能通过File Provider来实现。不能使用KubernetesCRD Provider了。
灰度发布,就是将测试的服务放到生产去跑,然后观察是否符合上线要求,如下:
1、我们把测试服务放到生产,接入少部分流量来进行观察;
2、如果测试观察没问题,就符合上线要求,可以慢慢增加新版本数量,减少旧版本数量;
3、如果测试观察有问题,就需要切掉该版本,对测试用户进行修数处理;
比如现在我们有两个名为 appv1 和 appv2 的 Nginx 服务,我们希望通过 Traefik 来控制我们的流量,将 3⁄4 的流量路由到 appv1,1/4 的流量路由到 appv2 去,这个时候就可以利用 Traefik2.0 中提供的带权重的轮询(WRR)来实现该功能,首先在 Kubernetes 集群中部署上面的两个服务。
appv1的资源清单(appv1.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: appv1
namespace: default
spec:
selector:
matchLabels:
app: appv1
template:
metadata:
labels:
use: test
app: appv1
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: portv1
---
apiVersion: v1
kind: Service
metadata:
name: appv1
namespace: default
spec:
selector:
app: appv1
ports:
- name: http
port: 80
targetPort: portv1
appv2的资源清单(appv2.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: appv2
namespace: default
spec:
selector:
matchLabels:
app: appv2
template:
metadata:
labels:
use: test
app: appv2
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: portv2
---
apiVersion: v1
kind: Service
metadata:
name: appv2
namespace: default
spec:
selector:
app: appv2
ports:
- name: http
port: 80
targetPort: portv2
然后创建这两个资源清单:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
appv1-684f8cbc7-kr4d5 1/1 Running 0 3m53s
appv2-645d7666b5-thgt2 1/1 Running 0 3m53s
由于 WRR 这个功能目前只支持 File Provider,所以我们需要开启该 Provider 才能使用,这里需要注意的是由于需要开启 File Provider,所以我们需要提供一个文件用于该 Provider 的配置,我们这里是用在 Kubernetes 集群中的,所以可以通过一个 ConfigMap 对象,将配置文件内容挂载到 Traefik 的 Pod 中去,如下所示,我们通过将一个名为 traefik-dynamic-conf 的 ConfigMap 对象挂载到了 /config 目录下面去,然后通过 —providers.file.filename参数指定配置文件开启 File Provider,另外添加 - —providers.file.watch=true 参数可以让 Traefik 动态更新配置:
traefik.yaml
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress-controller
tolerations:
- operator: "Exists"
nodeSelector:
kubernetes.io/hostname: 172.16.0.33
containers:
- image: traefik:v2.0
name: traefik-ingress-lb
ports:
- name: web
containerPort: 80
- name: websecure
containerPort: 443
- name: admin
containerPort: 8080
- name: redis
containerPort: 6379
volumeMounts:
- name: config
mountPath: /config
args:
- --entrypoints.web.Address=:80
- --entrypoints.websecure.Address=:443
- --entrypoints.redis.Address=:6379
- --entrypoints.foo
- --api.insecure=true
- --providers.kubernetescrd
- --providers.file.watch=true
- --providers.file.filename=/config/traefik-dynamic.toml
- --api
- --api.dashboard=true
- --accesslog
# 使用 tls 验证这种方式
- --certificatesresolvers.default.acme.tlsChallenge=true
# # 邮箱配置
- --certificatesResolvers.default.acme.email=rookieops@163.com
# # 保存 ACME 证书的位置
- --certificatesResolvers.default.acme.storage="acme.json"
# # 下面是用于测试的ca服务,如果https证书生成成功了,则移除下面参数
#- --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesresolvers.default.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
volumes:
- name: config
configMap:
name: traefik-dynamic-conf
---
kind: Service
apiVersion: v1
metadata:
name: traefik
namespace: kube-system
spec:
type: NodePort
selector:
k8s-app: traefik-ingress-lb
ports:
- protocol: TCP
port: 8080
name: admin
- name: web
port: 80
protocol: TCP
- name: websecure
port: 443
protocol: TCP
- name: redis
port: 6379
protocol: TCP
上面是开启 File Provider 的配置,接下来需要创建对应的 ConfigMap 对象,首先创建一个名为 traefik-dynamic.toml 的文件,内容如下所示:
[http]
[http.routers]
[http.routers.Router0]
entryPoints = ["web"]
service = "app"
rule = "Host(`nginx.rookieops.top`)"
[http.services]
[http.services.app]
[[http.services.app.weighted.services]]
name = "appv1"
weight = 3
[[http.services.app.weighted.services]]
name = "appv2"
weight = 1
[http.services.appv1]
[http.services.appv1.loadBalancer]
[[http.services.appv1.loadBalancer.servers]]
url = "http://appv1.default.svc.cluster.local/"
[http.services.appv2]
[http.services.appv2.loadBalancer]
[[http.services.appv2.loadBalancer.servers]]
url = "http://appv2.default.svc.cluster.local/"
上面这个配置文件就是我们需要配置的灰度发布的规则,创建一个名为 Router0 的路由,在 web 这个入口点上面监听 Host=nginx.rookieops.top 这样的请求,将请求路由给名为 app 的服务,而该服务则将请求路由给了 appv1 这个服务,权重为 3,另外一部分请求路由给了 appv2 这个服务,权重为 1,也就是有 3⁄4 的请求会被路由到 http://appv1.default.svc.cluster.local/ 这个真实的服务上,同样的另外的 1⁄4 请求会被路由到 http://appv2.default.svc.cluster.local/ 这个真实的服务上。
然后创建这个ConfigMap:
# kubectl create configmap traefik-dynamic-conf --from-file=traefik-dynamic.toml -n kube-system
创建完成后,再更新 Traefik2.0,就可以将配置文件通过 ConfigMap 挂载到 Traefik Pod 的 /config/traefik-dynamic.toml 路径下面去了。
然后我们测试观察如下: