作为一个久负盛名的容器编排平台,Kubernetes具有很多优势,其自动化能力尤其突出,具体体现在工作负载的发现、自愈和应用伸缩等方面。
然而,Kubernetes也在不断地进行升级,并不总是能开箱即用地用于生产环境。
本文提供了一系列应当重点考虑的Kubernetes最佳实践,用以提高您的 K8s 的安全性和性能,并控制成本。
1、始终保持与最新稳定版同步
通常,应该始终将 K8s 升级到最新稳定版本,以应用最新的安全或性能的修复与改进,也会获得更多来自社区和供应商提供的支持,从而避免可能危及服务交付的安全和性能问题,以及成本方面的异常。
2、对清单进行检查
可能你觉得 YAML 很难使用,那么不妨用一下 yamllint,它可以在一个文件中处理多个文档。
还有专门针对 Kubernetes 的 linter。比如,你可以使用 kube-score 来分析你的清单,从而遵循最佳实践。 Kubeval 也能用来分析你的清单,但是它只是检查有效性。Kubernetes 1.13 的 kubectl 提供的 dry-run 也可以用来检测清单而不实际应用它,所以,也可以使用这个特性来检查你为 K8s 准备的 YAML 文件。
3、对各种配置文件做好版本控制
最好将 Deployment、Service 和 Ingress 等所有配置文件存储在版本控制系统中。Github 是最流行的开源分布式版本控制平台,也可以使用 Gitlab、BitBucket 和 SourceForge 等其它平台。
要在将代码推送到 Kubernetes 集群之前就对它们进行版本化管理,这样就可以跟踪代码的变更内容及变更人。这样做,你就可以快速回滚更改、重建或恢复集群,来确保稳定性和安全性。
4、采用基于 Git 的工作流
在 GitOps,或者是基于 Git 的工作流中,Git 是唯一的真实来源,因而是用于自动化包括 CI/CD 管道等所有任务的优秀模型。
而除了可以提升生产力,GitOps 还有助于加快部署和增强错误跟踪,并使 CI/CD 工作流程自动化,同时简化集群管理,加快应用开发。
5、充分利用 YAML 文件的声明性
应当编写声明性的 YAML 文件加上 kubectl apply 命令来将对象添加到集群,而不是直接使用 kubectl run 的命令性方式。声明式方法允许您指定所需达到的目标状态,剩下的交给 Kubernetes 自己来决定如何达到这个状态。
这样,所有的对象,就和代码一样,可以被保存在 YAML 文件中,并进行版本控制。一旦出现错误,就可以通过恢复之前的 YAML 文件并重新应用它们来轻松地回滚部署。而且,采用这个模式,可以确保团队能够看到集群的当前状态,以及随着时间的推移做出的所有更改。
6、表明资源需求和上限
在为资源定义需求或上限时,Millicores 作为 CPU 的单位,而 MB 可用作内存的单位。如果资源需求超出了限制,那么容器将不会在吊舱中运行。
当资源稀缺时,如果没有配置资源需求和限制,生产集群就可能会失败。而资源充足时,多余的资源会被没有配置资源限制的 Pod 消耗掉,从而增加 Kubernetes 的使用成本。另外,如果 Pod 消耗了太多的 CPU 或内存,并且调度程序无法添加新的 Pod,则可能出现节点崩溃的情况。
7、将 Pod 连接到 Deployments、ReplicaSets 和 Jobs
要尽可能避免使用裸露的豆荚,否则,在节点失败的情况下,将无法重新调度它们,仅仅因为它们没有被绑定到 Deployment 或 ReplicaSet。
Deployment 用于创建 ReplicaSet 来保持所需的 Pod 数量,并定义 Pod 替换策略,如 RollingUPdate。
除非有严格的重新启动策略,否则 Deployment 几乎总是比直接创建 Pod 更为有效。
8、使用 Label 清楚地标记资源
Label 是可以帮助您识别 Kubernetes 集群中特定资源的键值对,还可以用在 kubectl 命令上来过滤和选取对象,允许根据特定特征快速识别对象。
即使你觉得暂时不会用到它们,但用 Label 为你的对象打上标签绝对是个好主意。另外,要使用尽可能多的描述性标签来区分团队要使用的资源。比如可以按所有者、版本、实例、组件、管理者、项目、团队、机密级别等等来进行标记。
9、在启动探测器之后运行活性探测器
活性探测器(Liveness probes)会定期检查长期存活 Pod 的健康状况,避免 Kubernetes 将流量导向不健康的 Pod。在默认情况下,Kubernetes 会重新启动未通过健康检查的 Pod,来确保应用程序的可用性。
探测器会向 Pod 发送一个 ping 信号,然后看是否可以接收到响应。如果没有响应,则表明应用程序没有在那个 Pod 中运行,导致探测器启动一个新 Pod,并在那里运行应用程序。
另外,必须首先运行启动探测器(startup probe),这是第三种探测器,当 Pod 的启动过程完成时,它会通知 Kubernetes。如果 Pod 的启动探测器不完整,活性探测器(Liveness probes)和就绪探测器(readiness probes)就不会起作用。
10、使用命名空间简化资源管理
命名空间可以在逻辑上将集群划分为子集群。当希望同时在多个项目或团队之间共享 Kubernetes 集群时,命名空间就非常有用。有了命名空间,开发、测试和生产团队就可以在同一个集群内协作,而不会覆盖或干扰彼此。
Kubernetes 自身提供了 default、kube-system 和 kube-public 三个命名空间。一个集群支持逻辑上独立但彼此可以通信的多个命名空间。
11、保持无状态
无状态应用通常比有状态应用更容易管理,尽管这种情况正在随着 Kubernetes Operators 越来越受欢迎而改变。
无状态后端可以不再需要刚开始使用 Kubernetes 的团队来维护对伸缩性有限制的长连接。
无状态应用也使得按需迁移和扩展变得更为容易。
另外,保持工作负载的无状态还允许使用 Spot Instance(AWS、Azure 等云提供商的一种优惠计价方式)。
Spot Instance 的缺点在于,像 AWS 和 Azure 这样的提供商经常需要在短时间内回收廉价的计算资源,这样就会干扰工作负载,而使应用成为无状态,就可以避免这个问题。
12、建立自己的网络策略
Kubernetes 的网络策略规定了哪些流量是允许的,哪些是不允许的。这类似于在 Kubernetes 集群的 Pod 之间设置防火墙。无论流量在 Pod 之间如何移动,只有在网络策略准许的情况下才被允许。
在创建网络策略之前,必须定义经过授权的连接并制定策略应该应用在哪些 Pod,由此过滤掉不符合标准的流量。
13、设置基于角色的访问控制(RBAC)
根据 Redhat 在2022年对300多名DevOps专业人士的调查,几乎所有被调查人(94%)在前一年至少发生过一次 Kubernetes 安全事件。
可以使用 RBAC 来制定哪些用户可以访问哪些 Kubernetes 资源,比如他们可以访问哪些集群,谁可以进行更改,以及可以在多大程度上进行更改。
RBAC 权限可以通过两种方式配置:
- 对于非命名空间下的资源,使用 ClusterRole
- 对于命名空间下的资源,使用 Role
14、为 Kubernetes 集群设置防火墙
这是另一个重要的 Kubernetes 最佳安全实践。
除了要控制集群内部流量的网络策略之外,还要在集群前面设置一道防火墙,以限制访问 API 服务的外部请求。这可以使用常规或端口防火墙规则来实现。
另外,确保访问者 IP 地址在白名单中,并且服务器开放端口是受限的。
15、小镜像更好
镜像需要是小而且分层的。镜像越小,其构建速度越快,所需的存储空间越少。镜像尺寸可以通过有效的分层来显著减小。
怎么做到?
如果需要许多不同的组件,可以在一个 Dockerfile 中使用多个 FROM 语句。安装程序将基于 FROM 命令从已部署的容器提取每一层。由此产生的 Docker 容器更小,因为它不再包含以前的层,而只包含需要的组件。