阿里巴巴云原生应用安全防御实践与 OpenKruise 的新领域

头图.png

做者 | 王思宇(酒祝)
来源|阿里巴巴云原生公众号node

得益于 Kubernetes 面向终态的理念,云原生架构自然具有高度自动化的能力。然而,面向终态的自动化是一把“双刃剑”,它既为应用带来了声明式的部署能力,同时也潜在地会将一些误操做行为被终态化放大。
所以,充分了解云原生环境下那些潜在的影响应用安全的问题,提早掌握多方位的安全防御、拦截、限流、熔断等技术手段来保障云原生应用的运行时稳定性相当重要。git

本文整理自做者阿里云容器服务技术专家,OpenKruise 做者 & 初创人员之一,Kubernetes、OAM 社区贡献者王思宇(酒祝)于 1 月 19 日在阿里云开发者社区“周二开源日”的直播分享,介绍了云原生环境下应用安全与可用性的“到处危机”,分享阿里巴巴保障云原生应用运行时稳定性经验,而且详细解读了后续这些能力将如何经过 OpenKruise 赋能给开源。github

点击回看完整视频:https://developer.aliyun.com/live/246065后端

云原生环境应用安全“危机”

1. 阿里巴巴云原生应用部署结构

这里的云原生应用部署结构是在阿里巴巴原生环境最简化的抽象图,以下图所示。api

1.jpg

首先咱们来看几个 CRD。CloneSet CRD 能够理解成 deployment 的一个 workload,也就是给应用部署 Pod 的模板。有了 CloneSet CRD 以后,不一样的业务以及不一样的应用会创建对应的 CloneSet,在 CloneSet 下面再创建对应的 Pod,以及对 Pod 作一些部署、发布相关的管理。安全

除了 CloneSet 以外,还提供了 SidecarSet CRD,这个 CRD 作的事情是在业务 Pod 建立阶段注入 SidecarSetCRD 中定义的 Sidecar 容器。也就是说,在 CloneSet 中业务只须要定义 Pod 中的 app 容器,也就是业务容器。在 Pod 建立过程当中,经过 SidecarSet 在其中定义业务中要注入哪些 sidecar 容器。网络

2. OpenKruise:阿里巴巴应用部署基座

开源的 OpenKruise 是阿里巴巴应用部署的基座。OpenKruise 提供了多种的 workload。其中包括:CloneSet、Advanced StatefulSet、SidecarSet、Advanced DaemonSet。架构

2.jpg

  • CloneSet:是面向无状态应用部署的工具,也是阿里巴巴中使用规模最大的部分,绝大部分泛电商业务都是经过 CloneSet 来部署发布,包括 UC 神马、饿了么、电商业务等。并发

  • Advanced StatefulSet:针对一个原生 StatefulSet 兼容的加强版本,是面向有状态应用部署的工具,目前主要是用于中间件在云原生环境的部署。app

  • SidecarSet:是在阿里巴巴环境中 sidecar 生命周期管理的工具。阿里巴巴的运维容器,以及阿里内部的 Mesh 容器,都是经过 SidecarSet 定义、部署以及注入到业务 Pod 中的。

  • Advanced DaemonSet:是针对原生 DaemonSet 兼容加强版本。将宿主机级别的守护进程部署到全部节点上,包括各类用于给业务容器配置网络、存储的基础组件。

介绍完基础环境以后,咱们已经对云原生部署结构有了一个基本的了解。下面,咱们来了解在云原生部署结构之下存在哪些云原生应用安全危机。

3. 云原生应用安全危机

1)workload 级联删除

Workload 级联删除,这一点不仅针对于 Kruise 的 CloneSet,对于 Deployment,对于原生的 StatefulSet 都存在相似的问题。指的是当咱们删除一个 Workload 以后,假设使用采用默认删除,没有使用 orphan 删除这种策略的话,底下的 Pod 都会被删掉,这里存在一种误删风险。也就是说,一旦某个 Deployment 被误删,那么它底下的全部 Pod 都会级联被删掉,致使整个应用不可用。若是有多个 Workload 被删掉,就可能致使不少个业务出现不可用的状况,这是一个对可用性形成的风险。以下图所示:

3.jpg

2)namespace 级联删除

那么咱们再往上看,若是 Namespace 被删掉,那么整个 Namespace 底下的全部资源,包括 Deployment、CloneSet 这些 Workload,也包括 Pod、Service 等全部资源都会被删除,这是一种很高的误删风险。

4.jpg

3)CRD 级联删除

若是有用 Helm 部署的同窗可能会遇到过相似的状况,也就是若是你的 Helm 中包含了一些 CRD,这些 CRD 都被定义在 template 中, 那么当 Helm uninstall 的时候,基本上这些 CRD 都会被 Helm 包级联删除掉,包括有人手动误删了某个 CRD,那么 CRD 底下对应的 CR 都会被清理。这是一个很高的风险。

若是 CRD 是 CloneSet 这种 Workload 级别的 CRD,那么一旦删除这个 CRD 以后,会致使全部 CRD 底下的 CloneSet 的 CR 对象所有被删掉,从而致使全部的业务 Pod 所有被删掉。也就是说,删除一个 Workload,只是这个 Workload 底下的 Pod 被删掉;删除一个 Namespace 可能只是 Namespace 底下的 Pod 被删掉。但若是像阿里巴巴这种场景下,若是有人把 CloneSet 或者一些很关键的 CRD 删掉的话 ,其实极可能致使整个集群环境全部 NameSpace 底下的 Pod 都会被级联删掉,或者说都会处于应用不可用的状态,形成云原生环境对于应用可用性的风险。以下图所示:

5.jpg

从上文能够看出来,云原生这种理念架构为咱们带来的好处是面向终态,也就是说咱们定义终态,从而整个 Kubernetes 集群就会向终态靠拢。而一旦出现一些误操做致使定义了一种错误的终态,那么 Kubernetes 也会向错误的终态靠拢,致使出现错误的结果,从而影响到整个应用的可用性。所以咱们说,面向终态是一把“双刃剑”。 

4)并发 Pod 更新/驱逐/删除

除了几种误删的状况,还有更多针对可用性的风险。以下图所示,假设左边 CloneSetA 部署了两个 Pod,这两个 Pod 中又被 SidecarSet 注入了对应的 sidecar 容器。在这种状况下,若是经过 CloneSet 作应用发布,假设说咱们设置的 Max Available 是 50%,也就是说,两个 Pod 是逐个升级,前一个升级完成,后一个才能开始升级,默认状况下这种发布策略是没有问题的。

可是若是 Pod 有多个 Owner,好比 CloneSet 是其中一个 Owner,CloneSet 对上面的 Pod 开始作原地升级,SidecarSet 对第二个 Pod 作 sidecar 的原地升级,那么同一时刻可能这个应用的两个 Pod 都在被升级。由于在 CloneSet 定义了 Max Unavailable 是 50%,从它的视角来看,只要选取两个 Pod 中的一个开始作升级。CloneSet 自己是没法感知到其它控制器甚至其余人为的行为去对其它 Pod 作操做,缺少全局视角,每个控制器都认为本身在升级的 Pod 是符合升级策略,符合最大不可用测略。但当多个控制器同时开始工做的时候,可能会致使整个应用 100% 不可用。

6.jpg

如上图右边的状况,CloneSetC 底下有 3 个 Pod,若是它开始作升级的时候只升级其中一个 Pod,假设是重建升级,它会把旧版本 Pod 删掉,先建新版本 Pod。在这过程当中,假设另外两个 Pod 一个可能被 Kubelet,或者 kube-controller-manager 中的 node lifecycle controller 驱逐,这时候已经有两个 Pod 不可用,已经超过 Workload 中定义的最大不可用发布策略。在这个过程当中,还可能有一些 Pod 被其余一些控制器其余有人工手动删除。种种可能性致使一个 Workload 下 Pod 的不可用数量,极可能是超过自己 workload 中定义的不可用发布策略的。

也就是说,在 Deployment 中定义了 Max Unavailable 是 25%,那么 Deployment 在发布的时候,从它自身角度来看保证 25% 的 Pod 在被发布。其余 75% 的 Pod 并不保证彻底可用,这 75% 的 Pod 可能被 Kubelet 驱逐、可能被人为手动删除、可能被 SidecarSet 外部热升级等等,种种状况可能会致使 Deployment 超过 50% 不可用,甚至更高,使整个应用受到影响。

云原生应用安全防御实践

针对以上种种危机,咱们能采起怎么样的措施,保证原生环境下应用安全的可用性、安全性。下面介绍一些实践的经验。

1. 防御实践 - 防级联删除

因为级联删除对应用可用性危害很是大,包括了删除 CRD 节点,删除 Namespace 节点,以及删除 Workload 节点。防级联删除定义了针对多种资源,包括 CRD、Namespace、包括原生 Deployment 在内的各类 Workload 等,对这些资源提供了针对的 labels 定义。

7.jpg

下面是针对各类重要节点防级联删除的语名:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  labels:
    policy.kruise.io/disable-cascading-deletion: true

---

apiVersion: v1
kind: Namespace
metadata:
  labels:
    policy.kruise.io/disable-cascading-deletion: true

---

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    policy.kruise.io/disable-cascading-deletion: true

---

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    policy.kruise.io/disable-cascading-deletion: true

labels 定义是关闭级联删除,用户的任何 CRD、Namespace、workload 里带有防级联删除标识以后,kruise 就会保证资源防级联删除校验。也就是说,当用户删除一个 CRD 时,若是这个 CRD 里带有防级联删除这个 label,那么 kruise 就会去查看 CRD 底下是否还有存量 CR,若是有存量 CR 那么 kruise 会禁止 CRD 删除。

同理,在 Namespace 删除时,也会校验 Namespace 底下是否还有存量的运行状态的 Pod,若是有,会禁止用户直接删除 Namespace。

对于 workload 逻辑相对简单,就对于 Deployment、CloneSet、SidecarSet,当用户去删除 workload 时,若是 workload 中用户已经定义了防级联删除的 label,那么 kruise 会检查 workload 的 replica 是否为 0,若是 replica 大于 0,那么 kruise 是禁止用户直接删除带有防级联删除标识的 workload。也就是说,当一个存量 Deployment,若是 replicas 大于 0 的状况下,若是 Deployment 中存在带有防级联删除标识,kruise 禁止用户直接删除。

若是真的须要删除 Deployment 有两种办法:

  • 第一,先把 replica 调为 “0”,这时底下 Pod 开始被删除,这时删除 Deployment 是没问题的。

  • 第二,能够把 Deployment 中防级联删除标识去掉。

以上是关于防级联删除的介绍,你们应该将防级联删除理解成安全防御最基础的一个策略,由于级联删除是 Kubernetes 中很是危险的一个面向终态的能力。

2. 防御实践 – Pod 删除流控 & 熔断

针对 Pod 删除流控 & 熔断的策略,指的是用户调用、或用控制器用 K8s 去作 Pod 驱逐时,一旦出现误操做或者出现逻辑异常,极可能致使在整个 K8s 集群范围内出现 Pod 大规模删除的状况。针对这种状况作了 Pod 删除留空策略,或者说是一个 CRD。这个 CRD 用户能够定义在一个集群中,不一样的时间窗口内,最多有多少 Pod 容许被删除。

apiVersion: policy.kruise.io/v1alpha1
kind: PodDeletionFlowControl
metadata:
  # ...
spec:
  limitRules:
  - interval: 10m
    limit: 100
  - interval: 1h
    limit: 500
  - interval: 24h
    limit: 5000
  whiteListSelector:
    matchExpressions:
    - key: xxx
      operator: In
      value: foo

如上面这个例子,10 分钟以内最多容许 100 个 Pod 被删除,1 小时以内最多容许 500 个 Pod 被删除,24 小时内最多容许 5000 个 Pod 被删除。固然也能够定义一些白名单,好比有些测试应用,天天频繁地巡检、测试,频繁删除会影响整个流控,能够提供一个白名单,符合白名单的应用不计算在窗口内。

除了白名单以外,可能 90% 的常规应用或者核心应用,是受到删除流控保护的。一旦存在规模性误删除操做,就会被删除流控以及熔断机制保护。包括在保护以后或者触发阈值以后,最好提供这种报警机制、监控机制,让集群的管理者能快速的感知到线上出现的熔断事件。还包括帮助管理者去判断熔断事件是一个正常的事件,仍是一个异常的事件。

若是在这段时间内,须要存在不少删除请求,能够把对应策略值相应放大。若是真的是一些误删除,拦截到以后,及时根据请求来源作溯源,及时在搜索层面作熔断,拒绝这些请求。

3. 防御实践 - 应用维度不可用数量保护

对应用维度不可用数量保护,对于 K8s 原生,原生的 Kubernetes 提供了 PDB(PodDisruptionBudge) 策略,可是 PDB 只能拦截 Pod eviction 驱逐操做,也就是 Pod 驱逐操做。

8.jpg

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: xxx
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: xxx

上面的这个例子,假设其中有 5 个 Pod,这时定义了 minAvailable=2,就保证最少有 2 个 Pod 处于可用。一旦有 3 个 Pod 不可用,还剩下 2 个 Pod 可用,这时候若是 Pod eviction 针对存量 2 个 Pod 作驱逐,这个时候 PDB 会保护 Pod 可用性,拒绝此次驱逐操做。可是相应的若是对存量 2 个 Pod 作删除或者原地升级,或者去作其余致使 Pod 不可用的事情,PDB 是没有办法拦截,尤为是针对 Pod 删除请求,比 Pod 驱逐更为常见,可是 PDB 是没办法拦截删除等请求。

对于这些问题,阿里巴巴作了 PodUnavailableBudget 拦截操做,也就是 PUB。这里的 Unavailable 能作的操做就更多了,基本上全部可能致使 Pod 不可用的操做,都在 PodUnavailableBudget 保护范围内,包括了驱逐请求、Pod 删除请求,应用原地升级、Sidecar 原地升级、容器重启等,全部致使应用不可用的操做都会被 PUB 拦截。

以下面这个例子:

apiVersion: policy.kruise.io/v1alpha1
kind: PodUnavailableBudget
spec:
  #selector:
  #  app: xxx
  targetRef:
    apiVersion: apps.kruise.io
    kind: CloneSet
    name: app-xxx
  maxUnavailable: 25%
  # minAvailable: 15
status:
  deletedPods:
    pod-uid-xxx: "116894821"
  unavailablePods:
    pod-name-xxx: "116893007"
  unavailableAllowed: 2
  currentAvailable: 17
  desiredAvailable: 15
  totalReplicas: 20

定义了一个 PUB,这个 PUB 能够像原生 PDB 同样写一个 selector 范围,也能够经过 targetRef 直接关联到某一个 Workload,保护范围就是在 Workload 底下的全部 Pod,一样也能够定义最大不可用数量,以及最小可用数量。

假设对于 CloneSet 底下总共 20 个 Pod,当定义了 maxUnavailable:25% 时,必定要保证至少有 15 个 Pod 处于可用状态。也就是说,PUB 会保证这 20 个 Pod 中最多有 5 个处于不可用状态。回到咱们以前在“危机”部分讲到的一个例子,若是这 20 个 Pod 同时在被 Cloneset 发布,以及被 Kubelet 驱逐,或是人工手动删除,一旦 Pod 不可用数量超过 5 个,无论是 Kubelet 对剩余 15 个 Pod 作驱逐,仍是人为手动删除剩余的某些 Pod,这些操做都会被 PUB 所拦截,这种策略能彻底保证应用在部署过程当中的可用性。PUB 能够保护的范围比 PDB 大不少,包括在实际使用过程当中预期以外的一些删除请求、升级请求,从而保证整个应用在运行时的稳定性和可用性。

4. 防御实践 - PUB/PDB 自动生成

对于真正的 Depoyment 应用开发者、运维人员来讲,通常而言,只须要定义自身 workload 中 template,业务方只关心 Depoyment templatek 中业务的版本、环境变量、端口、提供的服务,但咱们很难去强制每个业务方在定义应用时,另外写一个 PUB 或者 PDB 保护策略的 CR。那么,咱们怎样对每个应用提供自动保护呢?

在阿里巴巴内部,咱们针对每一个 Workload 提供自动生成 PUB/PDB 的能力。好比说,若是用户此时新建立了一个 Deployment,会经过控制器自动为该 Deployment 生成一个匹配的 PUB。这个自动生成的功能即能支持原生 Deployment/StatefulSet,也支持 Kruise 的 CloneSet / Advanced StatefulSet / UnitedDeployment。第二,默认根据 strategy 中 maxUnavailable 策略。第三,容许 annotation 中单独定义保护策略。以下面的语句所示:

apiVersion: apps
kind: Deployment
metadata:
  name: deploy-foo
  annotations:
    policy.kruise.io/generate-pub: "true"
    policy.kruise.io/generate-pub-maxUnavailable: "20%"
    # policy.kruise.io/generate-pub-minAvailable: "80%"
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  # ...

---
# auto generate:
apiVersion: policy.kruise.io/v1alpha1
kind: PodUnavailableBudget
spec:
  targetRef:
    apiVersion: apps
    kind: Deployment
    name: deploy-foo
  maxUnavailable: 20%

自动生成的 PUB/PDB 内部填写的 maxUnavailable,既可让用户在 kruise 中指定定义。好比用户能够直接把 kruise.io/generate-pub:"true",也能够 kruise.io/generate-pub-maxUnavailable:"20%",可让用户指定应用最多容许有多少个不可用。这是用户指定的策略。

若是用户没有指定策略,会根据在发布策略中存在的maxUnavailable生成 PUB。就是指在发布的阶段,有多少个不可用数量,作为应用运行时最大不可能数量。这是容许单独定义策略。

OpenKruise 的新领域

1. OpenKruise 介绍

最后,和你们介绍上述开放的能力在 OpenKruise 新领域如何去开放,以及怎么拓展对 OpenKruise 的认知。OpenKruise 是阿里云开源的 Kubernetes 扩展应用负载项目,本质上是围绕 Kubernetes 云原生应用去作一系列自动化能力的引擎,同时也是阿里巴巴经济体上云全面使用的部署基座。

OpenKruise 的定位,作的不是一个完整的平台,更相似因而 Kubernetes 中一个拓展的产品。这个拓展的产品做为一个 add on 的组件,提供了一系列针对在 Kubernetes 中部署应用,以及后续保护防御应用可用、围绕云原生应用的一些自动化的能力,这些拓展能力或者加强能力,是原生 Kubernetes 所不具有,但也是迫切须要它所拥有这些能力,是阿里巴巴内部在云原生逐渐演进过程当中去沉淀的一些通用能力。

目前,Kruise 提供了如下 workload 控制器:

  • CloneSet:提供了更加高效、肯定可控的应用管理和部署能力,支持优雅原地升级、指定删除、发布顺序可配置、并行/灰度发布等丰富的策略,能够知足更多样化的应用场景。

  • Advanced StatefulSet:基于原生 StatefulSet 之上的加强版本,默认行为与原生彻底一致,在此以外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。

  • SidecarSet:对 sidecar 容器作统一管理,在知足 selector 条件的 Pod 中注入指定的 sidecar 容器。

  • UnitedDeployment:经过多个 subset workload 将应用部署到多个可用区。

  • BroadcastJob:配置一个 job 在集群中全部知足条件的 Node 上都跑一个 Pod 任务。

  • Advanced DaemonSet:基于原生 DaemonSet 之上的加强版本,默认行为与原生一致,在此以外提供了灰度分批、按 Nodelabel 选择、暂停、热升级等发布策略。

  • AdvancedCronJob:一个扩展的 CronJob 控制器,目前 template 模板支持配置使用 Job 或 BroadcastJob。

2. 原生 workload 能力缺陷

9.jpg

根据 Deployment 去 CloneSet、AdcancedStatefulSet 是由于原生 workload 能力缺陷有不少。你们能够看到,基本上从 Kubernetes 1.10 版本以后,其实其余的功能,包括 pod 里面,它的字段仍是在不断丰富,包括更多的 pod 的能力支持、更多的策略等,可是对于 workload 层面,就是 deployment 和 StatefulSet 层面,已经不倾向于作任何改动。社区在这背后的考虑是由于在不一样公司、不一样业务场景下,应用部署发布层面需求不少。

Kubernetes 原生提供的 Deployment,是面向一些最通用最基础的一些环境,没办法用它去知足全部的业务场景,但实际上社区是很是鼓励有更高需求,更大更复杂场景规模需求的用户,自行经过 CRD 去拓展编写,利用更强大的 workload,来知足不一样的业务的场景需求。

3. OpenKruise与原生能力对比

10.jpg

橙色:开源中  /  绿色:已开源

那么,对于这场景而言,Kruise 已经作了比较完备的一个无状态以及有状态应用的部署,经过上图表格能看到 Kruise 提供的 workload 和原生 deployment、StatefulSet、DaemonSet 的对比。

4. OpenKruise 2021 规划

11.jpg

如上图所示,OpenKruise 是一个云原生应用自动化引擎,目前提供的 workload 能力在应用部署,但不会仅局限于应用部署这一个领域的。

1)风险防控

在 2021 年上半年的规划中,咱们会针对上面讲到的云原生应用的风险和防控的策略,会经过 OpenKruise 输出给社区。包括 CRD 删除防御、级联删除防御、全局 Pod 删除流控、Pod 删除/驱逐/原地升级防御、自动为 workload 生成 PDB/PUB 等。

2)Kruise-daemo

除此以外以前 OpenKruise 只是做为一个中心的控制器部署,下个版本中会提供一个 Kruise-daemon 经过 daemon set 部署到每一个节点上,能够帮用户去作一些镜像预热,发布加速,容器重启 ,单机调度优化的一些策略。

3)ControllerMesh

ControllerMesh 是 OpenKruise 提供出来帮助用户管理用户集群中其余运行时的一些控制器运行时的能力,经过流量控制等方式解决传统控制器单住模式带来的种种问题。

 最后,在 OpenKruise 项目社区建设方面,已经在 2020 年 11 月 11 号经 CNCF 技术监督委员会全体成员投票,一致赞成正式进入 CNCF Sanbox,在整个过程当中也获得了 CNCF 积极的回应,表示 OpenKruise 项目与 CNCF 倡导的理念很契合,鼓励有更多像 OpenKruise 这样能作一些通用化的,面向更复杂的场景,更大规模的一些这种自主的 Workload 能力的项目出现。

如今已经有不少公司在使用 OpenKruise 的这些能力,好比:

  • 基于原地升级、灰度发布等需求,携程在生产环境使用CloneSet、AdvancedStatefulSet 来分别管理无状态、有状态应用的服务,单集群 Kruise workload 数量达到万级别。

  • OPPO 公司不只大规模使用了 OpenKruise,还在下游配合其定制化的 Kubernetes 进一步增强了原地升级的能力,普遍应用在多个业务的后端运行服务中,经过原地更新覆盖了 87% 左右的升级部署需求。

  • 此外,国内的用户还有苏宁、斗鱼 TV、有赞、比心、Boss 直聘、申通、小红书、VIPKID、掌门教育、杭银消费、万翼 科技、多点 Dmall、佐疆科技、享住智慧、艾佳生活、永辉科技中心、跟谁学,国外的用户有 Lyft、Bringg、 Arkane Systems 等。

  • Maintainer 5 位成员来自阿里巴巴、腾讯、Lyft

  • 51 位贡献者

    • 国内:阿里云、蚂蚁集团、携程、腾讯、拼多多...

    • 国外:微软、Lyft、Spectro Cloud、Dsicord...
  • 2000+ GitHub Stars

  • 300+ Forks

若是你们对 OpenKruise 项目感兴趣,有任何但愿交流的话题,欢迎你们访问 OpenKruise 官网GitHub

相关文章
相关标签/搜索