如何在 Kubernetes 实现容灾调度?
1. 背景知识
在开始真正的案例讲解前,需要和你同步一下对一些名词和概念的共识。
- 业务组:多个有强关联的Pod 可以组成一个业务组
- 多副本:每个 Pod 可以由单独的 Pod 来定义,也可以由 Deployment 来定义,区别在于 Deployment 有定义多副本的能力,因此后面的案例都采用Deployment
- 域:也称为 zone,是定义在 Node 上的标签,K8S 内置三种域,分别是
kubernetes.io/hostname
和topology.kubernetes.io/region
和topology.kubernetes.io/zone
- 容灾:真正的容灾需要在一个集群内有多个域,而这些域通常要是不同的物理区域(机房或者机架),但由于测试环境有限,以下只能使用
kubernetes.io/hostname
这个域来演示。 - 容灾类型:以下我的演示有两种容灾类型,分别是
业务组副本容灾
和分散容灾
,这些是我自己方便总结记忆而自定义的类型,不是专有名词。
2. 业务组副本容灾
多业务组副本容灾是在同一个域内,最多只能有一个业务组部署,这样能绝对安全的保证其中一个域故障后,服务不会受太大影响。
多业务组副本容灾需要满足如下几个要求:
- 同一业务组内有多个 Pod(以两个 Pod 为例),必要放在同一 zone (假设以节点级的域为例)
- 该业务组需要多个副本(假设是3个副本),副本要分别位于多个不同的 zone 内
拆解需求并提出对应解决方案:
- 需要多副本:因此要使用 Deployment
- 两个Pod:使用两个 Deployment
- 副本要位于同的 zone:使用反亲和调度策略
- 同业务组的Pod在同个zone:使用亲和调度策略
不管是反亲和调度还是亲和调度策略,其中最关键的是反亲和调度的单位,由于这里以节点域为例,因此 topologyKey
应该填写 kubernetes.io/hostname
。
如下的 yaml 中,定义的是第一个 Pod 的内容,提取关键点
replicas: 3
:三副本app: store
:给该 Pod 的每个副本都打上app=store
标签,后面反亲和性调度需要使用podAntiAffinity
:反亲和性调度,确保所有副本不会在同一个域(节点)中
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
第一个 Pod 已经保证了在不同的 zone 中了,那么对于第二个 Pod 它更严格,它需要满足
- 在不同域中 –> 可以参考 第一个 Pod 的方案使用反亲和调度策略
- 每个副本还要创建在已经有第一个 Pod 的域中 –> 可以使用亲和性调度策略
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.16-alpine
3. 分散容灾
同类型Pod分散容灾与多业务组副本容不同,由于域的数量大于业务组的副本数量,所以它允许在同一个域内有多个业务组。
为了实现因单台节点故障而带来影响最小化,可以借助 Kubernetes 1.16 引入的 Even Pod Spreading
特性让同类型的 Pod 均匀的分布的不同的域中。
而如果每个 Pod 也都有与之强关联的 Pod 需要部署在一个域中,可以再加入亲和性调度策略。
具体的步骤如下:
- 先选择一个 Pod 使用
Even Pod Spreading
让他们尽量分散在不同的域中。 - 再对另一个 Pod 使用亲和性调度与第一个 Pod 绑定在一起,但为了避免所有的 Pod 都集中在第一个 Pod 中的某个副本节点上,该 Pod 也应该使用
Even Pod Spreading
让他们尽量分散在不同的域中。
第一个 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "kubernetes.io/hostname"
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: store
containers:
- name: redis-server
image: redis:3.2-alpine
第二个Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "kubernetes.io/hostname"
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-store
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.16-alpine