我是 LEE,老李,一个在 IT 职业摸爬滚打 16 年的技能老兵。

事件布景

忙完了公司的双 11 项目后,整个公司的大项目基本都结束了。整个出产体系进入了一年一度的维护和晋级阶段,那么首要底层体系也进入了版别更新和维护的阶段。写一篇文章的意图是处理前几个星期咱们要对承载租户流量的服务网格 Istio 做全体晋级,可是翻阅了整个网络材料后,发现没有一篇完好的能够供给 Istio 滑润晋级的文章以及可行的操作手册的问题。所以决议根据自己在实践作业中碰到的问题以及实践操作收拾一篇文章,希望能够对其他正在运用 Istio 的小伙伴供给一种“无损滑润”晋级的技能参阅。

晋级要求

  1. 租户运用与服务不能中止。
  2. 租户在晋级期间能够正常运用原有体系,对晋级无感。
  3. 晋级进程支持滑润回退,有完善的过滤流程和时刻。
  4. 计划可持续,对 Istio 体系没有侵入,利用社区内资源和材料。
  5. 操作可规范和流程化,方便后续人参阅和运用。

有了上面的晋级要求,那么咱们也就有寻求“无损滑润”处理计划的需求,可是现实总是严酷的,没有找到复合咱们需求的计划,哪怕官方文档供给的晋级计划都是有损的。我就很忧虑假如按照现在网络供给的材料和信息制作晋级计划和操作 SOP,满意不了晋级要求。怎么办?看来不得不有一次技能攻坚、计划设计、可行性验证了。我想要滑润晋级 Istio,那么就要回到源头,研讨 Istio 的结构,拟定真正可行的计划。

废话不多说,咱们一同进入“技能探究”环节。

技能探究

已然要拟定 Istio “无损滑润”晋级计划,就要知道为什么 Istio 晋级会有损。有一种头痛医头,脚痛医脚的感觉,这个是最有用的方法,不妨咱们耐心持续往下看。

咱们先看下 Istio 的架构简图:

Istio 如何无停机平滑升级 (1.7 跨版本升级 1.11)

实践 Pod 的 sidecar 和 IngressGateway 底层都是 Envoy,都是经过 Istiod 分发的 Dynamic config,也便是这些 envoy 都是注册在 Istiod 上的,由 Istiod 统一管理。

弥补阐明:Istio sidecar 注入是由于 MutatingWebhookConfigurations 的装备。在拥有 label 为 istio-injection=enabled 的 namespace 中创立 pod 会主动注入 pod 启动中。

咱们做一个假定,现在出产的 Istio 版别是 1.7,然后再把 Istio 版别晋级到了 1.11 这个版别。 Istiod 版别发生了变化,Istiod 版别成为了 1.11,而事务 Pod 的 sidecar 版别仍是 1.7,怎么办?

小伙伴榜首反响便是重启 pod,让事务 Pod 的 sidecar 重新注册到 1.11 上,看上去是那么回事,可是别忘了,你重启了事务 Pod,租户的恳求此时由于重启会出现中止或者服务不可用的状况。假如我不重启事务 Pod,就让 1.7 的 Pod sidecar 衔接 1.11 的 Istiod 不就好了,现实真的是你想的吗? No,No,No,太单纯。 Pod sidecar 会由于版别不兼容,丢掉装备,导致 Pod 由于健康监测失利而重启。

Istio 如何无停机平滑升级 (1.7 跨版本升级 1.11)

由于 Istio sidecar 的装备丢掉。假如你的 Istio-agent sidecar 无法与 Istiod 通讯,或与发送的装备不兼容,你的作业负载将无法参加网格或与网格通讯。这甚至会影响现有的作业负载,导致你可能会尝试拜访不再存在的作业负载,新的作业负载也将无法参加。

TIPS: 由于这种类型的中止,建议 istio-agents 匹配并保留与操控平面 (Istiod) 相同的版别。在晋级进程中,现有的操控平面布置保持原位而不是直接晋级。

假如有小伙伴想探究究竟,能够参阅我之前揭露的材料 《Istio Pilot 结构解析 PPT (已脱敏)》

已然有直接晋级不行,并且会断流,有没有其他的处理计划呢? 有,灰度晋级。官方文档:istio.io/latest/docs…

可是这个便是真的咱们想要的吗?不。在官方文档中有这么一步操作:

After the namespace updates, you need to restart the pods to trigger re-injection. One way to do this is using:

$ kubectl rollout restart deployment -n test-ns

看到这儿,很多人跟我一样,木了。。。 重启 pod ? 真实出产环境,你去重启出产 pod?就由于 Istio 软件晋级? 这个是滑润晋级计划?技能委员会能经过你的计划? 这么多问号都摆在面前,那么咱们该怎么做? 不着急, 咱们一同进入“技能计划”环节。

技能计划

技能应战

  1. 直接晋级 Istio 会导致 Istio-agent sidecar 与 Istiod 不兼容,终究导致事务 Pod 重启。
  2. 为了保证 Istio-agent 和 Istiod 之间的兼容性,必须要手动重启事务 Pod。
  3. 体系需求滑润过渡和晋级,事务 Pod 非到不必要,肯定不能重启。

其间 1,2 和 3 点之间是相悖的。

计划猜想

  1. Istio-agent sidecar 与 Istiod 兼容问题,可不能够老版别的 Istio-agent sidecar 与 Istiod 衔接,新版别的 Istio-agent sidecar 与 Istiod 衔接?
  2. 假如各个版别的 Istio-agent sidecar 与 Istiod 衔接,如何让老版别的 sidecar 的事务 pod 天然过渡到新版别的 sidecar 上?
  3. 如何保证真正的“无损滑润”晋级?

处理方法

带着这么多问题和技能应战,其时确实真的把我莫非了,可是经过差不多的 2 周的左右的计划设计以及技能验证,发现是完全能够做到真正的“无损滑润”晋级的。那么在这儿我就分享下其时我的思考方法和定位计划的进程。

兼容问题

这个问题好办,创立两个独立的 Istio 体系在一个 k8s 中,只需求在 istioctl 新装置的 Istio 参加一个参数 –set revision=xxx。 istioctl 装置时候就不会掩盖原有的 Istio 版别,而是启用一个全新的操控面和部分数据面(为什么是部分,后面会看到)。这样咱们就很好的做到了老版别的 Istio-agent sidecar 与 Istiod 衔接,新版别的 Istio-agent sidecar 与 Istiod 衔接。

过渡问题

首要咱们不能手动重启正在运转的事务 Pod,可是不代表事务 Pod 不会迭代和发版,只要他们重新发版,Pod 天然就完结了重启。只要经过一段满足长的时刻就能让运用老版别的 sidecar 的事务 pod 天然过渡到新版别的 sidecar 上。

提到这儿,有小伙伴觉得仍是没有提到重点,那么我觉得有必要来看看 MutatingWebhookConfigurations 的装备。

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
    annotations:
    creationTimestamp: "2022-11-30T10:41:51Z"
    generation: 5
    labels:
        app: sidecar-injector
        install.operator.istio.io/owning-resource: installed-state
        install.operator.istio.io/owning-resource-namespace: istio-system
        istio.io/rev: default
        operator.istio.io/component: Pilot
        operator.istio.io/managed: Reconcile
        operator.istio.io/version: 1.7.8
        release: istio
    name: istio-sidecar-injector
    resourceVersion: "9640"
    selfLink: /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/istio-sidecar-injector
    uid: b66c986a-9ad9-469f-bf14-eea3df74c876
webhooks:
    - admissionReviewVersions:
          - v1beta1
          - v1
      clientConfig:
          caBundle: <Secret>
          service:
              name: istiod ## 这儿很重要,便是在 Pod 创立时候,拜访 k8s istiod service 的 443 端口,然后注册自己,然后拉取 sidecar 启动装备
              namespace: istio-system
              path: /inject
              port: 443
      failurePolicy: Fail
      matchPolicy: Exact
      name: sidecar-injector.istio.io
      namespaceSelector:
          matchLabels:
              istio-injection: enabled
      objectSelector: {}
      reinvocationPolicy: Never
      rules:
          - apiGroups:
                - ""
            apiVersions:
                - v1
            operations:
                - CREATE ## 创立动作
            resources:
                - pods ## 只重视 Pod
            scope: "*"
      sideEffects: None
      timeoutSeconds: 30

(★) 在此看到 yaml 文件中 service 字段下有一个 name 为 istiod,咱们只要把这个值改成新版别的 Istiod 的 service 称号即可。 就能够让重启的 Pod 运用新版别的 Istio sidecar 启动了。

举个栗子:

[root@localhost ops]# kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                                         AGE
istio-ingressgateway   LoadBalancer   10.126.118.148   10.111.251.17   15021:30082/TCP,80:30080/TCP                    2d21h
istiod                 ClusterIP      10.126.118.150   <none>         15010/TCP,15012/TCP,443/TCP,15014/TCP,853/TCP   2d21h
istiod-1-11-8          ClusterIP      10.126.118.241   <none>         15010/TCP,15012/TCP,443/TCP,15014/TCP           2d21h

这儿我现已装置了一个 1.11.8 的新版别的 Istio 与老版别的并行,相互不冲突。 修正 MutatingWebhookConfiguration 中装备 sevice name 为 istiod-1-11-8,随后经过模拟“发布”新版别的运用,重启了事务 Pod,让其注册到了新版别的 Istio 上。

[root@localhost 1.11.8]# istioctl proxy-status
NAME                                                   CDS        LDS        EDS        RDS        ISTIOD                            VERSION
istio-ingressgateway-59b5b798d4-v9d98.istio-system     SYNCED     SYNCED     SYNCED     SYNCED     istiod-1-11-8-866bf44db-2wbqx     1.11.8
nginx-app-7b88698d6f-brhc4.nginx                       SYNCED     SYNCED     SYNCED     SYNCED     istiod-1-11-8-866bf44db-2wbqx     1.11.8
nginx-app-7b88698d6f-jhwr8.nginx2                      SYNCED     SYNCED     SYNCED     SYNCED     istiod-879f4d9bf-c5xz6            1.7.8

这个时候让咱们将目光重视到 VERSION 这个字段下,咱们发现 nginx-app-7b88698d6f-jhwr8.nginx2 这个 Pod 仍是在运用版别 Istio 1.7 并与老的 istiod-879f4d9bf-c5xz6 建立着衔接。而 nginx-app-7b88698d6f-brhc4.nginx 这个 Pod 在重启后就与新版别 Istio 1.11 的 istiod-1-11-8-866bf44db-2wbqx 建立着衔接。

到了这儿基本上咱们实现了 Istio 滑润晋级,经过天然迭代的方法重启 Pod,缓慢迁移到新版别 Istio 上。

平稳过渡

  1. 新版别 Istio 运用灰度装置晋级的方法布置
  2. 切换 Istio sidecar 注册方法 (MutatingWebhookConfiguration)
  3. 等候事务 Pod 天然迭代重启,假如长期没有切换重启的 Pod,能够组织恰当的停机时刻,完结切换
  4. 卸载老版别 Istio

实操步骤

装置新版 Istio

演示的 operator.yaml 文件内容

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
    meshConfig:
        accessLogFile: /dev/stdout
        accessLogEncoding: JSON
        outboundTrafficPolicy:
            mode: REGISTRY_ONLY
        defaultConfig:
            holdApplicationUntilProxyStarts: true
    components:
        ingressGateways:
            - name: istio-ingressgateway
              enabled: true
              k8s:
                  service:
                      ports:
                          - port: 15021
                            targetPort: 15021
                            nodePort: 30082
                            name: status-port
                          - port: 80
                            targetPort: 8080
                            nodePort: 30080
                            name: http2
    values:
        global:
            proxy:
                excludeIPRanges: "10.126.118.0/24"
        gateways:
            istio-ingressgateway:
                type: LoadBalancer
                autoscaleMin: 1
                autoscaleMax: 3
                resources:
                    requests:
                        cpu: 1500m
                        memory: 1Gi
                    limits:
                        cpu: "4"
                        memory: 8Gi
    addonComponents:
        prometheus:
            enabled: false

执行命令:

# istioctl install -f operator.yaml -s revision=1-11-8

切换注册到新版 Istio

切换 MutatingWebhookConfigurations ,完结 Namespace 中 Pod 启动主动注入 Sidecar 到 1.11.8 上,而不是用老的 1.7.8 (其间 istiod-1-11-8 是 k8s service 的称号,能够经过 kubectl get svc 取得)

执行命令:

# kubectl get mutatingwebhookconfigurations -n istio-system istio-sidecar-injector -o json | jq '.webhooks[0].clientConfig.service.name="istiod-1-11-8"' | kubectl replace -f -

(可选)卸载老版别 Istio

直接卸载可能导致默许的 mutatingwebhookconfigurations 装备丢掉,所以这儿要注意有备份和康复

备份 MutatingWebhookConfigurations 装备

# kubectl get mutatingwebhookconfigurations -n istio-system istio-sidecar-injector -o yaml > /tmp/mutatingwebhookconfigurations.backup.yaml

删去老版别的 Istio

# istioctl x uninstall -f operator.yaml

康复 MutatingWebhookConfigurations 装备

# kubectl apply -f /tmp/mutatingwebhookconfigurations.backup.yaml

终究作用

这儿经过模拟集群演示来展现咱们线上迁移全体作用,方法是一致的。

并行状况

[root@localhost 1.11.8]# istioctl proxy-status
NAME                                                   CDS        LDS        EDS        RDS        ISTIOD                            VERSION
istio-ingressgateway-59b5b798d4-v9d98.istio-system     SYNCED     SYNCED     SYNCED     SYNCED     istiod-1-11-8-866bf44db-2wbqx     1.11.8
nginx-app-7b88698d6f-brhc4.nginx                       SYNCED     SYNCED     SYNCED     SYNCED     istiod-1-11-8-866bf44db-2wbqx     1.11.8
nginx-app-7b88698d6f-jhwr8.nginx2                      SYNCED     SYNCED     SYNCED     SYNCED     istiod-879f4d9bf-c5xz6            1.7.8

日志检测

现已迁移到新版别 Istio 的事务 Pod 日志

[root@localhost 1.11.8]# kubectl logs -n nginx nginx-app-7b88698d6f-brhc4
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/12/02 07:34:59 [notice] 1#1: using the "epoll" event method
2022/12/02 07:34:59 [notice] 1#1: nginx/1.23.2
2022/12/02 07:34:59 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/12/02 07:34:59 [notice] 1#1: OS: Linux 5.18.19-051819-generic
2022/12/02 07:34:59 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 64000:64000
2022/12/02 07:34:59 [notice] 1#1: start worker processes
2022/12/02 07:34:59 [notice] 1#1: start worker process 29
2022/12/02 07:34:59 [notice] 1#1: start worker process 30
2022/12/02 07:34:59 [notice] 1#1: start worker process 31
2022/12/02 07:34:59 [notice] 1#1: start worker process 32
2022/12/02 07:34:59 [notice] 1#1: start worker process 33
2022/12/02 07:34:59 [notice] 1#1: start worker process 34
2022/12/02 07:34:59 [notice] 1#1: start worker process 35
2022/12/02 07:34:59 [notice] 1#1: start worker process 36
127.0.0.6 - - [02/Dec/2022:08:02:46 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:46 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:47 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:48 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:48 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:49 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:49 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:50 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:50 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:51 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:51 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.6 - - [02/Dec/2022:08:02:52 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"

持续运用老版别 Istio 的事务 Pod 日志

[root@localhost 1.11.8]# kubectl logs -n nginx2 nginx-app-7b88698d6f-jhwr8 -c app
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/11/30 11:22:37 [notice] 1#1: using the "epoll" event method
2022/11/30 11:22:37 [notice] 1#1: nginx/1.23.2
2022/11/30 11:22:37 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/11/30 11:22:37 [notice] 1#1: OS: Linux 5.4.185-1.el7.elrepo.x86_64
2022/11/30 11:22:37 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 64000:64000
2022/11/30 11:22:37 [notice] 1#1: start worker processes
2022/11/30 11:22:37 [notice] 1#1: start worker process 28
2022/11/30 11:22:37 [notice] 1#1: start worker process 29
2022/11/30 11:22:37 [notice] 1#1: start worker process 30
2022/11/30 11:22:37 [notice] 1#1: start worker process 31
2022/11/30 11:22:37 [notice] 1#1: start worker process 32
2022/11/30 11:22:37 [notice] 1#1: start worker process 33
2022/11/30 11:22:37 [notice] 1#1: start worker process 34
2022/11/30 11:22:37 [notice] 1#1: start worker process 35
127.0.0.1 - - [03/Dec/2022:08:31:17 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:18 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:19 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:20 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:20 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:21 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:21 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:22 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:23 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"
127.0.0.1 - - [03/Dec/2022:08:31:23 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "10.11.251.5"

经过上面的日志,咱们能够确认,两套 Istio 现已完结并行,并且两个运用 Pod 恳求相应正常。