乐趣区

关于云原生:OpenKruise-v080-核心能力解读管理-Sidecar-容器的利器

作者 | 赵明山(立衡)
起源 | 阿里巴巴云原生公众号

前言

OpenKruise 是阿里云开源的云原生利用自动化治理套件,也是以后托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 我的项目。它来自阿里巴巴多年来容器化、云原生的技术积淀,是阿里外部生产环境大规模利用的基于 Kubernetes 之上的规范扩大组件,也是紧贴上游社区规范、适应互联网规模化场景的技术理念与最佳实际。

OpenKruise 在 2021 年 3 月 4 日公布了最新的 v0.8.0 版本(ChangeLog),其中加强了 SidecarSet 的能力,特地是对日志治理类 Sidecar 有了更加欠缺的反对。

背景

Sidecar 是云原生中一种十分重要的容器设计模式,它将辅助能力从主容器中剥离进去成为独自的 sidecar 容器。在微服务架构中,通常也应用 sidecar 模式将微服务中的配置管理、服务发现、路由、熔断等通用能力从主程序中剥离进去,从而极大升高了微服务架构中的复杂性。随着 Service Mesh 的逐渐风靡,sidecar 模式也日益深入人心,在阿里巴巴团体外部也大量应用 sidecar 模式来治理诸如运维、平安、消息中间件等通用组件。

在 Kubernetes 集群中,Pod 不仅能够实现主容器与 sidecar 容器的构建,同时提供了许多功能强大的 workload(例如:deployment、statefulset)来对 Pod 进行治理、降级。然而随着 Kubernetes 集群上的业务日益增多,sidecar 容器的品种与规模也随之日益宏大,对线上 sidecar 容器的治理和降级成为了愈发繁冗的工作:

  • 业务 Pod 外面蕴含了运维、平安、代理等多个 sidecar 容器,业务线同学不仅要实现本身主容器的配置,而且还须要相熟这些 sidecar 容器的配置,这不仅减少了业务同学的工作量,同时也有形减少了 sidecar 容器配置的危险。
  • sidecar 容器的降级须要连同业务主容器一起重启(deployment、statefulset 等 workload 基于 Pod 销毁、重建的模式,来实现 Pod 的滚动降级),推动和降级撑持着线上数百款业务的 sidecar 容器,必然存在着极大的业务阻力。
  • 作为 sidecar 容器的提供者对线上诸多各种配置以及版本的 sidecar 容器没有间接无效的降级伎俩,这对 sidecar 容器的治理意味着极大的潜在危险。

阿里巴巴团体外部领有着百万级的容器数量连同下面承载的上千个业务,因而,sidecar 容器的治理与降级也就成为了亟待欠缺的主题。因而,咱们总结了外部许多 sidecar 容器的通用化需要,并将其积淀到 OpenKruise 下面,最终形象为 SidecarSet 作为治理和降级品种繁多 sidecar 容器的利器。

OpenKruise SidecarSet

SidecarSet 是 OpenKruise 中针对 sidecar 形象进去的概念,负责注入和降级 Kubernetes 集群中的 sidecar 容器,是 OpenKruise 的外围 workload 之一。它提供了十分丰盛的性能,用户应用 SidecarSet 能够十分不便实现 sidecar 容器的治理。次要个性如下:

  • 配置独自治理:为每一个 sidecar 容器配置独自的 SidecarSet 配置,方便管理。
  • 主动注入:在新建、扩容、重建 pod 的场景中,实现 sidecar 容器的主动注入。
  • 原地降级:反对不重建 pod 的形式实现 sidecar 容器的原地降级,不影响业务主容器,并蕴含丰盛的灰度公布策略

留神:针对 Pod 中蕴含多个容器的模式,其中对外提供次要业务逻辑能力的容器称之为 主容器,其它一些如日志采集、平安、代理等辅助能力的容器称之为 Sidecar 容器。例如:一个对外提供 web 能力的 pod,nginx 容器提供次要的 web server 能力即为主容器,logtail 容器负责采集、上报 nginx 日志即为 Sidecar 容器。本文中的 SidecarSet 资源形象也是为解决 Sidecar 容器的一些问题。

1. Sidecar logging architectures

利用日志能够让你理解利用外部的运行状况,日志对调试问题和监控集群流动十分有用。利用容器化后,最简略且最宽泛采纳的日志记录形式就是写入规范输入和规范谬误。

然而,在以后分布式系统、大规模集群的时代下,上述计划还不足以达到生产环境的规范。首先,对于分布式系统而言,日志都是扩散在单个容器外面,没有一个对立汇总的中央。其次,如果产生容器解体、Pod 被驱赶等场景,会呈现日志失落的状况。因而,须要一种更加牢靠,独立于容器生命周期的日志解决方案。

Sidecar logging architectures 是将 logging agent 放到一个独立的 sidecar 容器中,通过共享日志目录的形式,实现容器日志的采集,而后存储到日志平台的后端存储。
                               

阿里巴巴以及蚂蚁团体外部同样也是基于这种架构实现了容器的日志采集,上面我将介 绍 OpenKruise SidecarSet 如何助力 Sidecar 日志架构在 Kubernetes 集群中的大规模落地实际

2. 主动注入

OpenKruise SidecarSet 基于 Kubernetes AdmissionWebhook 机制实现了 sidecar 容器的主动注入,因而只有将 sidecar 配置到 SidecarSet 中,不论用户用 CloneSet、Deployment、StatefulSet 等任何形式部署,扩进去的 Pod 中都会注入定义好的 sidecar 容器

Sidecar 容器的所有者只须要配置本身的 SidecarSet,就能够在业务无感知的状况下实现 sidecar 容器的注入,这种形式极大的升高了 sidecar 容器应用的门槛,也不便了 sidecar 所有者的管理工作。为了满足 sidecar 注入的多种场景,SidecarSet 除 containers 之外还扩大了如下字段:

# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  # 通过 selector 抉择 pod
  selector:
    matchLabels:
      app: web-server
  # 指定 namespace 失效    
  namespace: ns-1    
  # container definition
  containers:
  - name: logtail
    image: logtail:1.0.0
    # 共享指定卷
    volumeMounts:
    - name: web-log
      mountPath: /var/log/web 
    # 共享所有卷
    shareVolumePolicy: disabled
    # 环境变量共享
    transferEnv:
    - sourceContainerName: web-server
      # TZ 代表时区,例如:web-server 容器中存在环境变量 TZ=Asia/Shanghai
        envName: TZ 
  volumes:
  - name: web-log
    emptyDir: {}
  • Pod 选择器

    • 反对 selector 来抉择要注入的 Pod,如示例中将抉择 labels[app] = web-server 的 pod,将 logtail 容器注入进去,也能够在所有的 pod 中增加一个 labels[inject/logtail] = true 的形式,来实现全局性的 sidecar 注入。
    • namespace:sidecarSet 默认是全局失效的,如果只想对某一个 namespace 失效,则配置该参数。
  • 数据卷共享

    • 共享指定卷:通过 volumeMounts 和 volumes 能够实现与主容器的特定卷的共享,如示例中通过共享 web-log volume 来达到日志采集的成果。
    • 共享所有卷:通过 shareVolumePolicy = enabled | disabled 来管制是否挂载 pod 主容器的所有卷卷,罕用于日志收集等 sidecar,配置为 enabled 后会把利用容器中所有挂载点注入 sidecar 同一路经下(sidecar 中自身就有申明的数据卷和挂载点除外)。
  • 环境变量共享:能够通过 transferEnv 从其它容器中获取环境变量,会把名为 sourceContainerName 容器中名为 envName 的环境变量拷贝到本 sidecar 容器,如示例中日志 sidecar 容器共享了主容器的时区 TZ,这在海内环境中尤其常见。

留神:Kubernetes 社区对于曾经创立的 Pod 不容许批改 container 数量,所以上述注入能力只能产生在 Pod 创立阶段,对于曾经创立的 Pod 须要通过重建的形式来注入。

3. 原地降级

SidecarSet 不仅实现 sidecar 容器的注入,而且复用了 OpenKruise 中原地降级的个性,实现了在不重启 Pod 和主容器的前提下独自降级 sidecar 容器的能力。因为这种降级形式基本上能做到业务方无感知的水平,所以 sidecar 容器的降级已不再是上下交困的难题,从而极大解放了 sidecar 的所有者,晋升了 sidecar 版本迭代的速度。

留神:Kubernetes 社区对于曾经创立的 Pod 只容许批改 container.image 字段,因而对于 sidecar 容器的批改蕴含除 container.image 的其它字段,则须要通过 Pod 重建的形式,不能间接原地降级。

为了满足一些简单的 sidecar 降级场景,SidecarSet 除了原地降级以外,还提供了十分丰盛的灰度公布策略。

4. 灰度公布

灰度公布应该算是日常公布中最常见的一种伎俩,它可能比拟平滑的实现 sidecar 容器的公布,尤其是在大规模集群的场景下,强烈建议应用这种形式。上面是 首批暂停 后续基于最大不可用滚动公布 的例子,假如一个有 1000 个 pod 须要公布:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    partition: 980
    maxUnavailable: 10%

上述配置首先公布(1000 – 980)= 20 个 pod 之后就会暂停公布,业务能够察看一段时间发现 sidecar 容器失常后,调整从新 update SidecarSet 配置:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 10%

这样调整后,对于余下的 980 个 pod,将会依照最大不可用的数量(10% * 1000 = 100)的程序进行公布,直到所有的 pod 都公布实现。

Partition 的语义是保留旧版本 Pod 的数量或百分比,默认为 0。这里的 partition 不示意任何 order 序号。如果在公布过程中设置了 partition:

  • 如果是数字,控制器会将 (replicas - partition) 数量的 Pod 更新到最新版本。
  • 如果是百分比,控制器会将 (replicas * (100% - partition)) 数量的 Pod 更新到最新版本。

MaxUnavailable 是公布过程中保障的,同一时间下最大不可用的 Pod 数量,默认值为 1。用户能够将其设置为绝对值或百分比(百分比会被控制器依照 selected pod 做基数来计算出一个背地的绝对值)。

留神:maxUnavailable 和 partition 两个值是没有必然关联。举例:

  • 当 {matched pod}=100,partition=50,maxUnavailable=10,控制器会公布 50 个 Pod 到新版本,然而公布窗口为 10,即同一时间只会公布 10 个 Pod,每公布好一个 Pod 才会再找一个公布,直到 50 个公布实现。
  • 当 {matched pod}=100,partition=80,maxUnavailable=30,控制器会公布 20 个 Pod 到新版本,因为满足 maxUnavailable 数量,所以这 20 个 Pod 会同时公布。

5. 金丝雀公布

对于有金丝雀公布需要的业务,能够通过 strategy.selector 来实现。形式:对于须要率先金丝雀灰度的 pod 打上固定的 labels[canary.release] = true,再通过 strategy.selector.matchLabels 来选中该 pod。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    selector:
      matchLabels:
      - canary.release: true
    maxUnavailable: 10%  

上述配置只会公布打上金丝雀 labels 的容器,在实现金丝雀验证之后,通过将 updateStrategy.selector 配置去掉,就会持续通过 最大不可用 来滚动公布。

6. 打散公布

SidecarSet 对于 pod 的降级程序,默认依照如下规定:

  • 对降级的 pod 汇合,保障屡次降级的程序统一
  • 抉择优先程序是(越小优先级越高):unscheduled < scheduled, pending < unknown < running, not-ready < ready, newer pods < older pods。

除了上述默认公布程序之外,scatter 打散策略容许用户 自定义将合乎某些标签的 Pod 打散 到整个公布过程中。比方,对于像 logtail 这种全局性的 sidecar container,一个集群当中很可能注入了几十个业务 pod,因而能够应用基于 利用名 的形式来打散 logtail 的形式进行公布,实现 不同利用间打散灰度公布 的成果,并且这种形式能够同 最大不可用 一起应用。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    # 配置 pod labels,假如所有的 pod 都蕴含 labels[app_name]
    scatterStrategy:
    - key: app_name
      value: nginx
    - key: app_name
      value: web-server
    - key: app_name
      value: api-gateway
    maxUnavailable: 10%  

留神:以后版本必须要列举所有的利用名称,咱们将在下个版本反对只配置 label key 的智能打散形式。

7. 实际

阿里巴巴以及蚂蚁团体外部曾经大规模的应用 SidecarSet 来治理 sidecar 容器,上面我将通过日志采集 Logtail sidecar 来作为一个示例。

  1. 基于 sidecarSet.yaml 配置文件创立 SidecarSet 资源。
# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: logtail-sidecarset
spec:
  selector:
    matchLabels:
      app: nginx
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 10%
  containers:
  - name: logtail
    image: log-service/logtail:0.16.16
    # when recevie sigterm, logtail will delay 10 seconds and then stop
    command:
    - sh
    - -c
    - /usr/local/ilogtail/run_logtail.sh 10
    livenessProbe:
      exec:
        command:
        - /etc/init.d/ilogtaild
        - status
    resources:
      limits:
        memory: 512Mi
      requests:
        cpu: 10m
        memory: 30Mi
    ##### share this volume    
    volumeMounts:
    - name: nginx-log
        mountPath: /var/log/nginx
    transferEnv:
    - sourceContainerName: nginx
        envName: TZ 
  volumes:
  - name: nginx-log
      emptyDir: {}
  1. 基于 pod.yaml 创立 Pod。
apiVersion: v1
kind: Pod
metadata:
  labels:
    # matches the SidecarSet's selector
    app: nginx 
  name: test-pod
spec:
  containers:
  - name: nginx
    image: log-service/docker-log-test:latest
    command: ["/bin/mock_log"]
    args: ["--log-type=nginx", "--stdout=false", "--stderr=true", "--path=/var/log/nginx/access.log", "--total-count=1000000000", "--logs-per-sec=100"]
    volumeMounts:
    - name: nginx-log
        mountPath: /var/log/nginx
    envs:
    - name: TZ
      value: Asia/Shanghai
  volumes:
  - name: nginx-log
      emptyDir: {}    
  1. 创立这个 Pod,你会发现其中被注入了 logtail 容器:
$ kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
test-pod   2/2     Running   0          118s

$ kubectl get pods test-pod -o yaml |grep 'logtail:0.16.16'
    image: log-service/logtail:0.16.16
  1. 此时,SidecarSet status 被更新为:
$ kubectl get sidecarset logtail-sidecarset -o yaml | grep -A4 status
status:
  matchedPods: 1
  observedGeneration: 1
  readyPods: 1
  updatedPods: 1
  1. 更新 sidecarSet 中 sidecar container 的 image logtail:0.16.18。
$ kubectl edit sidecarsets logtail-sidecarset

# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: logtail-sidecarset
spec:
  containers:
    - name: logtail
      image: log-service/logtail:0.16.18
  1. 此时,发现 pod 中的 logtail 容器曾经被更新为了 logtail:0.16.18 版本,并且 pod 以及其它的容器没有重启。
$ kubectl get pods |grep test-pod
test-pod                            2/2     Running   1          7m34s

$ kubectl get pods test-pod -o yaml |grep 'image: logtail:0.16.18'
    image: log-service/logtail:0.16.18
    
$ kubectl describe pods test-pod
Events:
  Type    Reason     Age                 From               Message
  ----    ------     ----                ----               -------
  Normal  Killing    5m47s               kubelet            Container logtail definition changed, will be restarted
  Normal  Pulling    5m17s               kubelet            Pulling image "log-service/logtail:0.16.18"
  Normal  Created    5m5s (x2 over 12m)  kubelet            Created container logtail
  Normal  Started    5m5s (x2 over 12m)  kubelet            Started container logtail
  Normal  Pulled     5m5s                kubelet            Successfully pulled image "log-service/logtail:0.16.18"

总结

本次 OpenKruise v0.8.0 版本的降级,SidecarSet 个性次要是欠缺了日志治理类 Sidecar 场景的能力,后续咱们在继续深耕 SidecarSet 稳定性、性能的同时,也将笼罩更多的场景,比方下一个版本将会减少针对 Service Mesh 场景的反对。同时,咱们也欢送更多的同学参加到 OpenKruise 社区来,独特建设一个场景更加丰盛、欠缺的 K8s 利用治理、交付扩大能力,可能面向更加规模化、复杂化、极致性能的场景。

如果大家对 OpenKruise 我的项目感兴趣,有任何心愿交换的话题,欢送大家拜访 OpenKruise 官网、GitHub,以及钉钉搜寻群号:23330762,退出交换群!

退出移动版