Kubernetes 设计模式笔记 —— Init Container

Init Container 为初始化相关的任务提供了区别于主应用程序的独立的生命周期,从而实现关注点分离。

初始化在很多编程语言中都备受关注。比如在 Java 中,为了初始化某个需要配置的对象,我们使用 constructor。
Constructor 会保证在对象中是第一个运行的,且只被 runtime 运行一次。此外,还可以使用构造函数验证强制参数之类的先决条件,传入参数或默认值以初始化实例字段。

Init Container 与构造函数是类似的,只不过是在 Pod 级别而不是类级别。
假如 Pod 中有一个或多个容器代表主应用程序,这些容器可能需要一些启动前的先决条件。比如为文件系统设置特殊的权限,设置数据库 schema,应用程序种子数据的安装等。同时,这些初始化逻辑可能需要主容器镜像中不包含的工具和库。
又或者,用户想要延迟应用的启动,直到可以确认某个外部的依赖满足条件。
所有上述需求都可以通过 Kubernetes 提供的 Init Container 实现。

Kubernetes 中的 Init Container 属于 Pod 定义的一部分,可以将容器划分成两组:init containers 和 application containers。
所有的 init container 会以串行的顺序一个接一个地执行,在应用容器开始启动之前,所有 init container 的执行必须成功完成。
而应用容器是可以并行运行的,启动顺序也是任意的。
Init and application containers

通常情况下,init container 应该是小型快速且能成功完成的,除非它是用来延迟 Pod 的启动以等待某个依赖符合要求。
当 init container 失败时,整个 Pod 会重新启动(除非配置了 RestartNever),导致所有 init container 重新执行一遍。因而令它们符合幂等原则可以避免副作用。
一方面,init container 有着和应用容器一样的能力:位于同一个 Pod 中,共享资源限制、存储卷和安全设置。另一方面,它们的健康检查和资源处理在语义上有些许不同。它们不存在 rediness check,因为只有当所有的 init container 成功结束之后应用容器才会继续启动。

在容器调度、自动伸缩、配额管理方面,Init container 会影响 Pod 请求和计算资源的方式。由于所有的 init container 会先串行执行到终止,接着应用容器并行运行。因而高效的 Pod 级别的资源分配取决于以下两组值中较大的那个:

  • 所有 init container 中资源请求或限制值最大的那个
  • 所有应用容器资源请求或限制的总和

上述行为的限制在于,当 init container 的资源需求非常高而应用容器相对很低时,这种配置对于资源的利用就很低效。因为 init container 一般只运行很短的一段时间,其他 Pod 无法使用 init container 运行结束后空闲下来的资源。

Init container 能够实现关注点分离,从而使容器保持 single-purposed。应用容器可以由只关注应用逻辑的开发工程师创建,而部署工程师可以为其添加 init container 并只关注配置和初始化任务。比如下面的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: v1
kind: Pod
metadata:
name: www
labels:
app: www
spec:
initContainers:
- name: download
image: axeclbr/git
command:
- git
- clone
- https://github.com/mdn/beginner-html-site-scripted
- /var/lib/data
volumeMounts:
- mountPath: /var/lib/data
name: source
containers:
- name: run
image: docker.io/centos/httpd
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/www/html
name: source
volumes:
- emptyDir: {}
name: source

Init container 克隆外部 Git repo 到挂载的路径,该路径通过 emptyDir 挂载卷实现 init 容器和应用容器间的数据共享。

总结

Init container 将 Pod 中的容器分成两组,拥有不同的生命周期、目的甚至作者。
Init 容器分阶段地执行,且只有当前容器成功完成后才继续进行下一阶段,意味着我们可以确保应用初始化的每一个阶段都是有保障的。
应用容器则可以并行运行,没有提供 init 容器那样的保证。我们可以根据目的在 Pod 中合理地组织关注初始化的 init 容器和关注应用本身的应用容器。

参考资料

Kubernetes Patterns