Skip to content

用Traefik实现灰度发布

Traefik 2.0 新增了很多高级功能,灰度发布就是其中一个,本文我们来介绍一下基于 Traefik 2.0 实现灰度发布。

注意:

目前最新版本是 v2.1.1,截至目前版本要实现灰度发布、流量复制等高级功能,只能通过 File Provider 来实现。不能使用 KubernetesCRD Provider 了。

灰度发布,就是将测试的服务放到生产去跑,然后观察是否符合上线要求,如下:

61264afa74b76d37a00940d3b6d209bb MD5

1、我们把测试服务放到生产,接入少部分流量来进行观察;

2、如果测试观察没问题,就符合上线要求,可以慢慢增加新版本数量,减少旧版本数量;

3、如果测试观察有问题,就需要切掉该版本,对测试用户进行修数处理;

比如现在我们有两个名为 appv1 和 appv2 的 Nginx 服务,我们希望通过 Traefik 来控制我们的流量,将  3⁄4  的流量路由到 appv1,1/4 的流量路由到 appv2 去,这个时候就可以利用 Traefik2.0 中提供的带权重的轮询(WRR)来实现该功能,首先在 Kubernetes 集群中部署上面的两个服务。

appv1 的资源清单(appv1.yaml)

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)

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

然后创建这两个资源清单:

bash
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

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  的文件,内容如下所示:

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:

bash
kubectl create configmap traefik-dynamic-conf --from-file=traefik-dynamic.toml -n kube-system

创建完成后,再更新 Traefik2.0,就可以将配置文件通过 ConfigMap 挂载到 Traefik Pod 的  /config/traefik-dynamic.toml  路径下面去了。

然后我们测试观察如下:

a68a316a66d697b2300053acd9b6effd MD5

最近更新