华尔街见闻基于istio的服务网格实践

777 阅读21分钟

距离2017 年的见闻技术架构调整接近 2 年,随着业务线的发展,见闻技术部的项目数量、项目架构类型、基础设施规模、服务变更频率都在不断地增长,带给 SRE 的挑战是如何能更快地助力于开发人员更快更稳定地部署服务,保障线上服务的稳定。

我们的后端开发团队仍然以 Golang 为主,不同业务线的技术选型不尽相同,同时存在 Python,Java 服务,这就需要 SRE 提供更易接入的微服务基础组件,常见的方案就是为每种语言提供适配的微服务基础组件,但痛点是基础组件更新维护的成本较高。

为了解决痛点,我们将目光放到服务网格,它能利用基础设施下沉解决多语言基础库依赖问题,不同的语言不需要再引入各种不同的服务发现、监控等依赖库,只需简单的配置并运行在给定的环境下,就能享有以上功能,同时网络作为最重要的通信组件,可以基于它实现很多复杂的功能,譬如根据不同可用区进行的智能路由、服务熔断降级等。

为此,我们调研了一些服务网格方案,包括IstioLinkerd,基于我们的当前的后端架构特点:

  • 服务通信协议主要基于 gRPC、HTTP
  • 基于 Kubernetes 的 Docker 部署
  • 拥抱开源,使用了 Prometheus、Grafana 作为监控,Zipkin 作为链路追踪等

对比下来,Istio 拥有更多活跃的开源贡献者,迭代速度快,以及 Istio 架构可行性讨论,我们选择 Istio 作为实践方案。

架构

Service Mesh

这张图介绍了见闻典型的服务网格架构,左半图介绍了一个用户请求是如何处理,右半图介绍运维系统是如何监控服务,若无特殊说明,服务都是部署在腾讯云托管 Kubernetes。

组件一览

  • Go(1.11) 后端语言。
  • Docker(17.12.1-ce) 容器技术。
  • Kubernetes(1.10.5,托管于腾讯云容器平台) 容器编排工具。
  • Istio(1.0.0) 服务网格开源方案,支持与 Kubernetes 集成。
    • Ingress, Proxy(Istio 1.0.0) 服务流量转发、智能代理,基于Envoy实现,Istio 二次开发Proxy
    • Pilot Discovery(Istio 1.0.5) 负责服务发现,通过监听 Kubernetes 中ServicePod等资源维护服务映射表。
    • Mixer Policy(Istio 1.0.5) 管理服务之间访问权限,提供 gRPC 形式的 Check 接口。
    • Mixer Telemetry(Istio 1.0.5) 负责接收服务指标数据,提供 gRPC 形式的 Report 接口。
    • Citadel 负责管理代理证书。
  • Dashboard 基于Kubernetes Dashboard二次开发的Istio Dashboard,负责管理 Istio 服务发布,配置变更等。
  • Grafana 负责监控数据可视化。
  • Prometheus 时序数据库,常用于监控系统。
  • Jaeger 负责服务链路追踪,组件包括 collector、Jaeger UI。
  • Cassandra 分布式 NoSQL 数据库,用于 Jaeger 指标数据存储。

用户请求分析

  1. 我们先从用户请求端开始,用户的请求通过 Tencent 4 层 LB 转发到基于 Envoy 的 Istio Ingress,Ingress 根据配置将请求路由到 Service A 所在的 Pod
  2. Service A 所在 Pod 接收 Ingress 请求
    • 访问 Service A 的请求会先到达 Proxy 再由它转发到 Service A 进程
    • Service A 向 Service B 发出的请求被 iptables 路由到 Proxy(下文会提到 iptables 的初始化)
    • Proxy 进程发起对 Service B 所在 Pod 的请求
      • Proxy 进程同步请求 Mixer Policy 服务,检查是否允许访问 Service B,检查通过,开始请求
      • Proxy 进程记录请求的指标(QPS,Latency,Status Code 分布等),异步并批量上报到 Mixer Telemetry 服务,这里是客户端指标
  3. Service B 所在 Pod 接收请求
    • Service B Proxy 接收请求并路由到 Service B 所在进程
      • Proxy 进程记录请求的指标(QPS,Latency,Status Code 分布等),异步并批量上报到 Mixer Telemetry 服务,这里是服务端指标
    • Service B 进程处理完请求并返回
  4. 数据原路返回到用户端

以上的流程可以观察到,服务之间通信完全依靠 Proxy 进程完成,Proxy 进程接管同一个 Pod 中服务的出入流量,完成请求的路由。

架构可行性

通过架构图以及以上流程,我们拆分出以下关键组件,观察其性能、可用性、拓展性。

  1. Istio Ingress 高性能,可拓展 Istio Ingress 用来处理用户入流量,使用 Envoy 实现,转发性能高。挂载在负载均衡后,通过增加实例实现可拓展。

  2. Istio Proxy 随应用部署,轻微性能损耗,可随应用数量拓展 Istio Proxy 以Sidecar形式随应用一起部署,增加 2 次流量转发,存在性能损耗。 性能: 4 核 8G 服务器,上面运行 Proxy 服务和 API 服务,API 服务只返回 ok 字样。(此测试只测试极限 QPS) 单独测试 API 服务的 QPS 在 59k+,平均延时在 1.68ms,CPU 占用 4 核。 通过代理访问的 QPS7k+,平均延时 14.97ms,代理 CPU 占用 2 核,API 服务 CPU 占用 2 核。 CPU 消耗以及转发消耗降低了 QPS,增加了延时,通过增加机器核数并增加服务部署数量缓解该问题,经过测试环境测试,延时可以接受。 可用性:基于 Envoy,我们认为 Envoy 的可用性高于应用。依赖 Pilot Discovery 进行服务路由,可用性受 Pilot Discovery 影响。 拓展性:Sidecar 形式,随应用数拓展

  3. Istio Policy 服务可拓展,但同步调用存在风险 Istio Policy 需要在服务调用前访问,是同步请求,会增加服务调用延时,通过拓展服务数量增加处理能力。属于可选服务,见闻生产未使用该组件。 性能: 未测试 可用性:若开启 Policy,必须保证 Policy 高可用,否则正常服务将不可用 拓展性:增加实例数量进行拓展

  4. Istio Telemetry 监控收集服务 性能: 从监控上观察 Report 5000qps,使用 25 核,响应时间 p99 在 72ms。异步调用不影响应用的响应时间。 可用性:Telemetry 不影响服务可用性 拓展性:增加实例数量进行拓展

  5. Pilot Discovery 性能: 服务发现组件 1.0.5 版本经过监控观察,300 个 Service,1000 个 Pod,服务变更次数 1 天 100 次,平均 CPU 消耗在 0.01 核,内存占用在 1G 以内。 可用性: 在服务更新时需要保证可用,否则新创建的 Pod 无法获取最新路由规则,对于已运行 Pod 由于 Proxy 存在路由缓存不受 Pilot Discovery 关闭的影响。 拓展性:增加实例数量可以增加处理量。

可以看到各个组件的可用性、拓展性都有相应的策略达到保障,我们认为 Istio 是具有可实施性的。

Istio 流量控制背后

Pilot Discovery: Istio 的交通大脑

Pilot Discovery 负责 Istio 服务发现,支持在 Kubernetes 里部署,它读取 K8S 资源配置,并生成 Proxy 可用的路由表。以下面的 Service A 服务为例,介绍 Istio 如何进行精细路由。

Pilot Discovery生成配置

  1. Discovery 与对应 K8S 集群的 API server 相连,拉取全量资源信息,并使用 Watch 方法对增量变更进行同步。
  2. 根据 Service A 的配置,生成对应 Service A 的路由配置,通过 gRPC 形式的 ADS 接口提供给 Proxy。
  3. Proxy 同步到最新配置,热更新后配置生效。

要知道如果在 Istio 访问一个服务,必须得声明 K8S Service

Istio 通过 K8S CRD拓展 K8S 已有的服务访问能力,我们列举网络相关常用的配置:

  • Gateway 控制 Istio Ingress 的路由转发及 TLS 证书绑定。
  • VirtualService 服务流量控制,实现如 A/B 测试、错误注入、服务保护等。
  • DestinationRule 用于目标服务的版本管理,根据 Pod 的 Label 区分目标服务的版本,联合 VirtualService 进行流量控制。

以下举个例子介绍如何利用它们配置同样大小流量到服务的不同版本,

# serviceA.yaml
kind: Service
apiVersion: v1
metadata:
  name: serviceA
  labels:
    app: serviceA
spec:
  ports:
    - name: http-8080
      protocol: TCP
      port: 8080
      targetPort: 8080
  selector:
    app: serviceA
  type: ClusterIP

# virtualServiceA.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: serviceA
spec:
  hosts:
    - serviceA
  http:
    - route:
        - destination:
            host: serviceA
            subset: v1
    - route:
        - destination:
            host: serviceA
            subset: v2
---
# destinationRule
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: serviceA
spec:
  host: serviceA
  subsets:
    - labels:
        version: v1
      name: v1
    - labels:
        version: v2
      name: v2

以上实现了 Istio 服务调用 serviceA 时,会随机地 50%概率到 serviceA 的 v1 版本,50%概率到 serviceA 的 v2 版本。

可以看到,VirtualService 通过 hosts 关联 serviceA,在 http 区域有两个 route,分别是 subset v1, subset v2,v1,v2 依赖 DestinationRule 来定义,同样用 host 来标注该 DestinationRule 控制哪个 host 的访问,以及通过 pod label 中 version 来划分不同版本。

流量控制方面,Istio 有相当丰富的功能支持,同时也带来了相当的复杂度,建议用户根据日常的使用频率在后台实现相应的前端控制台,利用自动化来完成流量控制。

Proxy 工作机制

  1. 自动注入 在 K8S 1.9 之后的版本,Istio 利用 K8S 提供的MutatingAdmissionWebhook在 K8S 创建 Pod 前回调 Istio 提供的 istio-sidecar-injector 动态修改 Pod 配置,添加以 Sidecar 形式运行的 Proxy。这里开启自动注入有两个维度,一是 namespace,namespace 需要添加istio-injection : enabled标签,这样实现该 namespace 下的所有 Pod 自动注入 Proxy;二是 deployment 可以设置 annotation 关闭自动注入。 如果 K8S 版本不够,可以利用命令行工具修改 Deployment 的配置。
  2. 接管 Pod 流量 Service A 所在 Pod 至少运行 Service A 应用容器以及用于代理的 Envoy 容器,创建 Pod 时 proxy-init 命令负责获取 Pod 监听的端口和具体协议,以此初始化网络,利用 iptables 将容器网络出入流量都转发到 Proxy 监听的 localhost 端口。 若 Service A 的 Pod 声明 servicePort 为 8080:HTTP,最终 Proxy 将会接收 8080 端口的 Pod 入流量和全部的 Pod 出流量。
  3. 服务发现 Proxy 基于 Envoy,与 Pilot Discovery 连接,动态同步 Kubernetes 集群中所有的服务信息:服务与 Pod IP、端口之间的映射表,通过路由信息实现智能路由,从而使服务发现从业务代码中剥离。
  4. 链路追踪 Proxy 支持设置 Zipkin URL,异步上报链路追踪数据。
  5. 服务质量监控 Proxy 将属性包上报给 Telemetry 服务,Telemetry 根据用户的配置生成指标数据并由 Prometheus 收集。

适配 Istio

我们目前的服务部署在腾讯云托管 Kubernetes,节点使用 16 核 32G 的网络增强型机器,所有的后端服务都以 Docker 部署,K8S 集群外部署高可用 ETCD 支持集群内服务发现,数据库以 MySQL、Cassandra、MongoDB 为主,消息队列采用 Kafka、NSQ。在应用 Istio 的过程中,我们对基础库进行了修改,删减了 Istio 已提供的功能并完成了对 Istio 的适配。

前情提要

见闻旧后端服务架构,所有 Golang 服务以打包成 Docker 镜像,以"gRPC"协议通信。

old-infrastructure

去"框架"

见闻 Golang 后端使用go-micro 框架,一个支持多插件的 Golang 微服务框架,作者将组件分成 transport,server,client,registry,codec 等,通过组合不同类型的组件非常灵活地配置微服务技术栈。对于有定制需求的微服务架构,是值得推荐的选择。

通信协议作为服务互通的基石,Istio 对 gRPC 和 HTTP 非常友好,根据协议 Istio 能解析 HTTP 头中的信息,支持提取指标以供分析。go-micro 只是利用 HTTP 和 gRPC 作为通信协议,但是协议的解析逻辑是协议无关的,所以可以说它只是用了这些通信协议的外壳,传输的报文内容是"micro 方言",这就导致了 Golang 暴露的服务无法被其它语言其它框架调用。为了将协议能在多语言中完全统一,也为了更好地使用 Istio 的监控统计功能,这个时候我们开始对 go-micro 的存留有一些新的思考,我们是否还需要 go-micro?经过近 2 年的生产实践,我们是不是可以更精简我们的框架?

经过这些思考过后,我们的决定是去 go-micro 框架,拥抱更轻量级的基础框架,这个框架只要支持:

  • gRPC 纯原生即可
  • Istio 支持 Istio 的基础功能,譬如一些 HTTP header 转发等
  • 改动尽量小 我们已经存在上百个 Golang 项目,避免改动 Golang 项目代码,将改动放到基础库为佳

go-micro 通过定义自制 protobuf 插件的方式在 stub 代码中集成框架功能,经过对逻辑的梳理,我们决定复写 protobuf 插件,生成兼容 micro 的 stub 代码,通过对 micro 接口的向后兼容,开发人员不需要修改代码,只需要在 CI 阶段运行 protoc 即时生成新版代码。

详情可见再见,micro

运维流程

右半图描述运维人员如何利用运维后台运维 Kubernetes 集群,Istio 的运维必须有自动化的工具来减少人工配置带来的错误,见闻的旧运维后台基于腾讯云容器平台暴露的开放 API,在引入 Istio 后,功能依赖于更细节的 label 以及 CRD(Custom Resource Definition),于是得依托更细粒度的 Kubernetes API,新的后台需要能完成基本的 Kubernetes 运维,而且结合 Istio 的实际进行日常更新,经过选型,见闻基于 Kubernetes Dashboard 二次开发了 Istio 部分的一些功能(APP 部署、更新,Istio 配置更新等),利用 Istio Dashboard 实现 APP 创建、部署接口,并由此重构原有的运维后台。

最终,SRE 提供两个后台,精细控制的 Istio Dashboard;提供给开发人员日常更新使用的简化版后台。

服务发布

日常最重要、最高频的功能,服务版本变更。

服务创建

服务创建包括对老服务的改造,一个 K8S 服务要经过一些配置上的更新才能成为 Istio APP。一个 Kubernetes 服务需要满足以下要求来获得 Istio 的功能支持:

  • Service资源声明服务监听的端口号以及协议 这里的服务端口声明中 name 字段值需要以协议名为起始值,譬如 grpc、http、tcp、udp 等,istio 识别前缀,用于初始化 Proxy,譬如grpc-svchttp-svc,不正确的端口命名会引起 Proxy 的异常行为,以及监控服务无法捕获该协议下的指标。

  • 服务探活接口 每个后端服务提供一个 HTTP 探活接口,这样服务启动时,不至于让其它服务访问到未就绪的状态。 对于 HTTP 探活接口的定义包括 Proxy 以及 APP 是否初始化完成,见闻的实践是在基础镜像中打入一个探活程序:

    1. 检测 Proxy 是否初始化 通过 Proxy 的配置同步时间与 Pilot Discovery 的配置更新时间对比,相同时认为其就绪。
    2. APP 自定义接口 APP 可以在指定端口提供就绪 API。
    # serviceA.yaml
    kind: Service
    apiVersion: v1
    metadata:
      name: serviceA
      namespace: default
      labels:
        app: serviceA
    spec:
      ports:
        - name: grpc
          protocol: TCP
          port: 10088
          targetPort: 10088
      selector:
        app: serviceA
      type: ClusterIP
    
  • Deployment 资源要求

    1. 有必要的两个 label:app 和 version,明确定义流量控制中的目标服务。 可以看例子中 deployment 的 app 为 serviceA,version 为 v1。
    2. 声明对外服务的端口,要求 Proxy 对指定端口入流量的接管。 例子中 ports 声明了 10088 端口。
    # deploymentA.yaml
    kind: Deployment
    apiVersion: extensions/v1beta1
    metadata:
      name: serviceA-v1
      labels:
        app: serviceA
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: serviceA
          version: v1
      template:
        metadata:
          labels:
            app: serviceA
            version: v1
        spec:
          containers:
            - name: serviceA
              image: "some-image"
              ports:
                - containerPort: 10088
                  protocol: TCP
              resources:
                requests:
                  cpu: 1000m
              livenessProbe:
                httpGet:
                  path: /health
                  port: 54321
                  scheme: HTTP
                initialDelaySeconds: 1
                timeoutSeconds: 2
                periodSeconds: 5
                successThreshold: 1
                failureThreshold: 3
              terminationMessagePath: /dev/termination-log
              terminationMessagePolicy: File
              imagePullPolicy: Always
              securityContext:
                privileged: false
          restartPolicy: Always
          terminationGracePeriodSeconds: 30
          dnsPolicy: ClusterFirst
    

符合以上要求,服务能正确接入 Istio 系统并获得服务发现和监控的能力。

服务更新

Istio 提供流量控制,给运维带来方便的 A/B 测试,用于根据指定规则迁移流量。 见闻的服务更新依靠 Istio 流量迁移功能,发布服务的新版本,并将流量通过 Istio 规则迁移到新版本,实际细节如下:

  1. 更新流量控制将流量指向已有版本 以下实例利用 VirtualService 将 ServiceA 的服务流量全部指向已存在的 v1 版本
    # virtualService
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: serviceA
    spec:
      hosts:
        - serviceA
      http:
        - route:
            - destination:
                host: serviceA
                subset: v1
    ---
    # destinationRule
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: serviceA
    spec:
      host: serviceA
      subsets:
        - labels:
            version: v1
          name: v1
    
  2. 部署新版本的 Deployment 查找符合 app label 的 deployment,运维人员基于该 deployment 创建 v2 版本的 deployment,并向 destinationRule 中增加 v2 版本。 yaml # destinationRule apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: serviceA spec: host: serviceA subsets: - labels: version: v1 name: v1 - labels: version: v2 name: v2
  3. 更新流量控制将流量指向新版本 以下实例利用 VirtualService 将 ServiceA 的服务流量全部指向 v2 版本 yaml # virtualService apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: serviceA spec: hosts: - serviceA http: - route: - destination: host: serviceA subset: v2
  4. 下线 v1 版本的 deployment,删除 DestinationRule 中的 v1

使用Istio Dashboard来实现上述流程

canary-deployment

Ingress 配置

为什么使用 Istio Ingress 作为新的 Ingress 方案?

过去我们使用腾讯云托管的 Kubernetes Ingress,为了对 Ingress 流量控制而引入 Istio Ingress。我们之前提到 Istio Ingress 是基于 Envoy,它读取 Istio 控制的配置进行路由,与其它内部服务一样方便地接入 Istio 所有功能。

除了 VirtualService 和 DestinationRule,Istio 定义了 Gateway 来控制实例支持的 Host 和证书。具体的流程是:

  1. 创建 Istio Ingress 提供的 Deployment 和 Service 创建 Deployment ingressgateway 时,以 ConfigMap 的形式挂载 Ingress 需要的证书。
  2. 配置 Gateway 配置 Ingress 接收具体域名(如 wallstreetcn.com)的流量,以及对应的 TLS 证书位置,这里的证书路径已经挂在到 Ingress 的 Deployment 上。以下是一个典型的 Gateway 配置。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: wallstreetcn-com
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - wallstreetcn.com
      port:
        name: http
        number: 80
        protocol: HTTP
    - hosts:
        - wallstreetcn.com
      port:
        name: https
        number: 443
        protocol: HTTPS
      tls:
        mode: SIMPLE
        privateKey: /etc/istio/ingressgateway-certs/tls.key
        serverCertificate: /etc/istio/ingressgateway-certs/tls.crt

配置完成后,再配合 VirtualService 的路由控制,控制 Ingress 的反向代理到 default 命名空间下的 gateway 服务 80 端口,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: wallstreetcn-com
  namespace: istio-system
spec:
  gateways:
    - wallstreetcn-com
  hosts:
    - wallstreetcn.com
  http:
    - route:
        - destination:
            host: gateway.default.svc.cluster.local
            port:
              number: 80

监控指标

Istio 支持 Prometheus 拉取集群指标,并提供 Grafana 看板展示。这里建议初期使用 Istio 自带的 Grafana 看板配置,并且注意 Kubernetes 主机的类型划分,Prometheus 服务适合放在内存型机器。可以与 Dashboard 集成,在发布服务过程中即时查看指标。

服务质量

Istio 自带一些默认的 Grafana 面板,统计所有可以被访问的 HTTP/gRPC 服务的返回码以及延时情况。

对于返回码,认为 5xx 为错误,并在面板上使用label_join((sum(rate(istio_requests_total{reporter="destination", response_code!~"5.*"}[1m])) by (destination_workload, destination_workload_namespace) / sum(rate(istio_requests_total{reporter="destination"}[1m])) by (destination_workload, destination_workload_namespace)), "destination_workload_var", ".", "destination_workload", "destination_workload_namespace")计算服务错误率。

对于延时情况采用histogram_quantile获取多维度 p50、p90、p95、p99 的延时分布。

istio-mesh

链路追踪

之前提到 Proxy 由 Envoy 实现,Envoy 支持设置 Zipkin 上报 API,Proxy 在收发请求时将链路指标上报到 Zipkin,为了实现链路追踪,Proxy 在流量转发中解析协议中的 HTTP 或 gRPC 请求头,找出其中的追踪头,组装成指标。 所以应用端需要在收到调用方请求时解析出请求头,并持续携带该请求头向后传递。 由于见闻在 Ingress 之后映射一个 HTTP gateway,请求从 Ingress 转发到 HTTP gateway,再发送到后续的 gRPC 服务,所以 HTTP gateway 有段代码生成 gRPC 请求头。

import (
  "github.com/labstack/echo"
  gmeta "google.golang.org/grpc/metadata"
)

// Create a gRPC context from Echo.
func NewContextFromEcho(ec echo.Context) context.Context {
  md := gmeta.MD{}
  for _, header := range []string{
    "x-request-id",
    "x-b3-traceid",
    "x-b3-spanid",
    "x-b3-parentspanid",
    "x-b3-sampled",
    "x-b3-flags",
    "x-ot-span-context",
  } {
    md.Set(header, ec.Request().Header.Get(header))
  }

  md.Set("x-b3-parentspanid", ec.Request().Header.Get("x-b3-spanid"))
  return gmeta.NewOutgoingContext(context.Background(), md)
}

在后续的 gRPC 服务调用中使用该 Context,至于 gRPC 服务之间的调用,我们发现会自动将 context 传递到下一个服务,所以没有做类似处理。

这里追踪的数据如果全量捕获将会是非常大的,并且对于监控来说也不必要,所以可以设置抽样率,Istio 提供 ConfigMap 中设置抽样率,一般来说设置成 1%即可。

实践中的宝贵经验

在 Istio 实践过程中,有哪些需要注意的问题。

  1. API server 的强依赖,单点故障 Istio 对 Kubernetes 的 API 有很强的依赖,诸如流量控制(Kubernetes 资源)、集群监控(Prometheues 通过 Kubernetes 服务发现查找 Pod)、服务权限控制(Mixer Policy)。所以需要保障 API server 的高可用,我们曾遇到 Policy 组件疯狂请求 Kubernetes API server 使 API server 无法服务,从而导致服务发现等服务无法更新配置。 _ 为避免这种请求,建议使用者了解与 API server 直接通信组件的原理,并尽量减少直接通信的组件数量,增加必要的 Rate limit。 _ 尽量将与 API server 通信的服务置于可以随时关闭的环境,这是考虑如果部署在同一 Kubernetes 集群,如果 API server 挂掉,无法关闭这些有问题的服务,导致死锁(又想恢复 API server,又要依靠 API server 关闭服务)

  2. 服务配置的自动化 服务配置是 Istio 部署后的重头戏,避免使用手动方式更改配置,使用代码更新配置,将常用的几个配置更新操作做到运维后台,相信手动一定会犯错的事实。

  3. 关于 Pilot Discovery Pilot Discovery 1.0.0 版本有很大的性能问题,1.0.4 有很大的性能提升,但引入了一个新 bug,所以请使用 1.0.5 及以上的版本,该版本在见闻的平均 CPU 负载从 10 核降到了 0.5 核,大大降低了 Proxy 同步配置的延时。

  4. 关于 Mixer Policy 1.0.0 这个组件曾导致 API server 负载过高(很高的 list pods 请求),所以我们暂时束之高阁,慎用。

  5. 性能调优 在使用 Proxy、Telemetry 时,默认它们会打印访问日志,我们选择在生产上关闭该日志。 时刻观察 Istio 社区的最新版本,查看新版本各个组件的性能优化以及 bug 修复情况,将 Istio 当做高度模块化的系统,单独升级某些组件。上面就提到我们在 Istio1.0 的基础上使用了 1.0.5 版本的 Policy、Telemetry、Pilot Discovery 等组件。

  6. 服务平滑更新和关闭 Istio 依靠 Proxy 来帮助 APP 进行路由,考虑几种情况会出现意外的状态: _ APP 启动先于 Proxy,并开始调用其它服务,这时 Proxy 尚未初始化完毕,APP 调用失败。 _ Service B 关闭时,调用者 Service A 的 Proxy 尚未同步更新 Service A 关闭的状态,向 Service B 发送请求,调用失败。

第一种情况要求 APP 有重试机制,能适当重试请求,避免启动时的 Proxy 初始化与 APP 初始化的时差。 第二种情况,一种是服务更新时,我们使用新建新服务,再切流量;一种是服务异常退出,这种情况是在客户端重试机制。希望使用 Istio 的开发人员有更好的解决方案。

下一步计划

见闻 Istio 化已于去年 10 月份完成并上线,我们的线上集群中 Istio 和非 Istio 的 APP 混合部署,上千的 Pod 数量曾对不够健壮的服务发现组件造成巨大的压力,在这期间曾遇到 Istio 的一些惊喜,并不断总结经验,希望给之后使用 Istio 的同学一些借鉴。之后的过程中,SRE 的目标依然是保障线上服务的健壮性。

  1. Istio Dashboard 优化 APP 部署流程,考虑自动部署的功能,通过服务指标自动完成灰度发布和流量迁移。
  2. Prometheus Prometheus 的高可用、可拓展方案的探索。