# Init Container（初始化容器）

{% hint style="warning" %}
#### <mark style="color:orange;">**在生产环境中，为了应用的安全和优化镜像的体积，业务镜像一般不会安装高危工具和并不常用的运维工具**</mark>，比如 curl、sed、awk、python 或 dig 等，<mark style="color:blue;">**同时建议使用非 root 用户去启动容器**</mark>。
{% endhint %}

但是**某些应用启动之前可能需要检测依赖的服务有没有成功运行，或者需要高权限去修改一些系统级配置，而这些检测或配置更改都是一次性的**，所以在制作业务镜像时没有必要为了一次配置的变更去安装一个配置工具，更没有必要因为使用一次高权限而把整个镜像改成以 root 身份运行。

考虑到上述问题和需求，Kubernetes 引入了初始化容器的概念，<mark style="color:blue;">**init container**</mark> 具有与应用容器分离的单独镜像，其启动相关代码具有如下优势：

* <mark style="color:blue;">**init container**</mark> 可以包含应用容器中不存在的实用工具或个性化代码。
* <mark style="color:blue;">**init container**</mark> 可以安全地运行这些工具，避免这些工具导致应用镜像的安全性降低。
* <mark style="color:blue;">**init container**</mark> 可以以 root 身份运行，执行一些高权限命令。
* <mark style="color:blue;">**init container**</mark> 相关操作执行完成后就会退出，不会给业务容器带来安全隐患。

## init container 简介

<mark style="color:blue;">**init container（初始化容器）用于在启动应用容器（app container）之前启动一个或多个初始化容器，完成应用容器所需的预置条件。**</mark>

**init container 与应用容器在本质上是一样的**，但它们是**仅运行一次就结束**的任务，并且**必须在成功执行完成后，系统才能继续执行下一个容器**。根据 Pod 的**重启策略（RestartPolicy）**，当 init container 执行失败，而且设置了 RestartPolicy=Never 时，Pod 将会启动失败；而设置 RestartPolicy=Always 时，Pod 将会被系统自动重启。

{% hint style="success" %}
#### **init container 与应用容器的区别如下：**

* **init container 的运行方式与应用容器不同**，它们必须先于应用容器执行完成，当设置了多个 init container 时，将按顺序逐个运行，并且只有前一个 init container 运行成功后才能运行后一个 init container。当所有 init container 都成功运行后，Kubernetes 才会初始化 Pod 的各种信息，并开始创建和运行应用容器。
* 在 init container 的定义中也可以设置资源限制、Volume 的使用和安全策略，等等。但**资源限制的设置与应用容器略有不同**。
  * [x] 如果多个 init container 都定义了资源请求/资源限制，则取**最大的值**作为所有 init container 的资源请求值/资源限制值。
  * [x] Pod 的**有效（effective）资源请求值/资源限制值**取以下二者中的较大值。
    * **所有应用容器的资源请求值/资源限制值之和。**
    * **init container 的有效资源请求值/资源限制值。**
  * [x] 调度算法将基于 Pod 的有效资源请求值/资源限制值进行计算，也就是说 **init container 可以为初始化操作预留系统资源，即使后续应用容器无须使用这些资源**。
  * [x] Pod 的有效 QoS 等级适用于 init container 和应用容器。
  * [x] 资源配额和限制将根据 Pod 的有效资源请求值/资源限制值计算生效。
  * [x] Pod 级别的 cgroup 将基于 Pod 的有效资源请求/限制，与调度机制一致。
* init container 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe，因为它们必须在 Pod 就绪之前运行完成。
{% endhint %}

{% hint style="info" %}
#### <mark style="color:blue;">**在 Pod 重新启动时，init container 将会重新运行。**</mark>

#### 常见的 Pod 重启场景如下：

*   **init container 的镜像被更新时，init container 将会重新运行，导致 Pod 重启。**

    > 仅更新应用容器的镜像只会使得应用容器被重启。
* **Pod 的 infrastructure 容器更新时，Pod 将会重启**。
* **若 Pod 中的所有应用容器都终止了，并且 RestartPolicy=Always，则 Pod 会重启。**
{% endhint %}

## **示例**

下面以 Nginx 应用为例，在启动 Nginx 之前，通过初始化容器 busybox 为 Nginx 创建一个 index.html 主页文件。这里**为 init container 和 Nginx 设置了一个共享的 Volume，供 Nginx 访问 init container 设置的 index.html 文件。**

<details>

<summary><mark style="color:purple;"><strong>示例 1：初始化文件</strong></mark></summary>

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-init-container
spec:
  # These containers are run during pod initialization
  initContainers:
  - name: install
    image: busybox:1.35.0-musl
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - https://kubernetes.io
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}
```

</details>

**有时某些服务需要依赖其他组件才能启动**，比如后端应用需要数据库启动之后，应用才能正常启动，所以此时需要检测数据库实例是否正常，待数据库可以正常使用时，再启动后端应用，此时可以使用初始化容器进行控制，对应的 YAML 文件如下（部分代码）：

<details>

<summary><mark style="color:purple;"><strong>示例2：等待依赖服务启动</strong></mark></summary>

```yaml
initContainers:
  - name: install
    image: centos:6
    command:
    - sh
    - -c
    - "for i in {1..100}; do 
         sleep 1; 
         echo $i; 
         if dig redis-standalone-svc; then 
           exit 0; 
         fi; 
       done; 
       exit 1"
```

</details>
