# Kubernetes 无状态应用的一般特征

以 12 要素为代表的微服务标准，很好地给微服务的特征做出了指导。然而具体到以容器形式在 Kubernetes 上运行的无状态业务应用上，这个标准是有些高层的——它看重的是方法和架构。如果仅从外在视角来对一个“顺眼”的 Kubernetes 应用进行观察，这个应用应该有什么特征呢？

## 依赖关系清晰

微服务应用通常会有各种外部依赖，例如数据库、缓存、队列等平台能力，或者业务上的依赖服务等，因此一个健康的微服务组合而成的应用，必须能处理好依赖关系。

微服务的启动顺序不是固定的，并且存在独立更新、重启的可能。而很多应用仅在启动时进行连接，这就要求在 Kubernetes 上运行的应用，首先在启动时，不会因为暂时无法连接依赖服务直接崩溃；同时在运行期间，也有处理这种随时处理重连的能力。

## 具备自检能力

存活检测关注的是进程是否活跃，是否应该重新启动；就绪检测代表的是服务能力，是否应该保存在 Service 的负载均衡池中。

在没有设置就绪检测的情况下，Pod 一旦启动成功，K8s 就会把相关服务的请求发给该实例，如果这个实例启动较慢，就有可能对业务造成损失。同理，存活和就绪检测应该分别进行，例如业务阻塞时，暂时将实例摘除，但是无需重启，即可逐步恢复服务能力。

联系到前面的依赖关系问题，在微服务环境中，一个服务的就绪检测应该仅仅关注本应用的情况，检测过程中不应包含对依赖服务的调用——否则所有依赖故障服务的其它服务的就绪检查失败，造成大面积故障。

## 日志采集和处理

- 应用不应继续把日志输出到本地文件，而应该输出到 `stdout` 和 `stderr`；
- 集群应该针对容器的 `stdout`、`stderr` 提供统一的日志采集，建议使用 Daemonset 而非 Sidecar；
- 进行日志采集的同时，集群应提供 ES、Loki 或其它类似机制来对日志进行处理，并且其处理和存储能力应该有初步预案；
- 应用日志应提供分级开关，保证同一镜像在不同环境中可以输出不同数量和级别的日志信息。

## 尽量优雅关停

- 容器命令入口应该有能力接收 `SIGTERM`，并在需要的情况下传递给业务主进程；
- 应用进程接收到 `SIGTERM` 信号之后，不应立刻关停，而是处理好剩余的在途业务；
- 使用 `preStop` 等 Pod 生命周期手段来完成特定任务；
- 避免使用长连接，保持简单负载均衡的有效性。

## 故障预防和应对

- 避免运行单 Pod 的 Deployment；
- 使用 Pod 软亲和避免同 Deployment 中的不同 Pod 分布在同一节点上；
- 遭遇不可恢复的故障，应该允许应用崩溃，由 K8s 重新启动；
- 定义 PDB（Pod disruption budgets），告知 K8s 为应用提供最低 Pod 数量保障。

## 资源使用

- 必须定义 CPU 和内存的 Requests；
- 必须定义内存的 Limits；
- 同一集群中的不同微服务，如果有不同 QoS 要求，应该定义不同的 `qosClass`，避免被无差别驱逐。

## 安全相关

- 应清晰掌握并声明应用运行所需的 Linux Capabiltiy；
- 避免使用 Root 身份运行容器；
- 使用只读的 RootFS，所有写入需求应该使用存储卷来完成；
- 避免特权逃逸。
