最佳实践

1 - 大规模集群的注意事项

集群包含多个运行着 Kubernetes 代理程序、 由控制平面管理的一组节点(物理机或虚拟机)。 Kubernetes v1.35 单个集群支持的最大节点数为 5,000。 更具体地说,Kubernetes 设计为满足以下所有标准的配置:

你可以通过添加或删除节点来对集群扩缩容。集群扩缩容的方式取决于集群的部署方式。

云供应商资源配额

为避免遇到云供应商配额问题,在创建具有大规模节点的集群时,请考虑以下事项:

控制面组件

对于大型集群,你需要一个具有足够计算能力和其他资源的控制平面。

通常,你将在每个故障区域运行一个或两个控制平面实例, 先垂直缩放这些实例,然后在到达下降点(垂直)后再水平缩放。

你应该在每个故障区域至少应运行一个实例,以提供容错能力。 Kubernetes 节点不会自动将流量引向相同故障区域中的控制平面端点。 但是,你的云供应商可能有自己的机制来执行此操作。

例如,使用托管的负载均衡器时,你可以配置负载均衡器发送源自故障区域 A 中的 kubelet 和 Pod 的流量, 并将该流量仅定向到也位于区域 A 中的控制平面主机。 如果单个控制平面主机或端点故障区域 A 脱机,则意味着区域 A 中的节点的所有控制平面流量现在都在区域之间发送。 在每个区域中运行多个控制平面主机能降低出现这种结果的可能性。

etcd 存储

为了提高大规模集群的性能,你可以将事件对象存储在单独的专用 etcd 实例中。

在创建集群时,你可以(使用自定义工具):

有关为大型集群配置和管理 etcd 的详细信息, 请参阅为 Kubernetes 运行 etcd 集群和使用 kubeadm 创建一个高可用 etcd 集群

插件资源

Kubernetes 资源限制有助于最大程度地减少内存泄漏的影响以及 Pod 和容器可能对其他组件的其他方式的影响。 这些资源限制适用于插件资源, 就像它们适用于应用程序工作负载一样。

例如,你可以对日志组件设置 CPU 和内存限制:

  ...
  containers:
  - name: fluentd-cloud-logging
    image: fluent/fluentd-kubernetes-daemonset:v1
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

插件的默认限制通常基于从中小规模 Kubernetes 集群上运行每个插件的经验收集的数据。 插件在大规模集群上运行时,某些资源消耗常常比其默认限制更多。 如果在不调整这些值的情况下部署了大规模集群,则插件可能会不断被杀死,因为它们不断达到内存限制。 或者,插件可能会运行,但由于 CPU 时间片的限制而导致性能不佳。

为避免遇到集群插件资源问题,在创建大规模集群时,请考虑以下事项:

优先处理集群关键组件

为确保集群关键组件(例如 CoreDNS、metrics-server 和其他关键插件)优先于其他工作负载运行, 并且不会被低优先级 Pod 抢占,请使用系统 PriorityClass 运行它们,例如 system-cluster-criticalsystem-node-critical

接下来

2 - 运行于多可用区环境

本页描述如何跨多个区(Zone)运行集群。

背景

Kubernetes 从设计上允许同一个 Kubernetes 集群跨多个失效区来运行, 通常这些区位于某个称作 区域(Region) 逻辑分组中。 主要的云提供商都将区域定义为一组失效区的集合(也称作 可用区(Availability Zones)), 能够提供一组一致的功能特性:每个区域内,各个可用区提供相同的 API 和服务。

典型的云体系结构都会尝试降低某个区中的失效影响到其他区中服务的概率。

控制面行为

所有的控制面组件 都支持以一组可相互替换的资源池的形式来运行,每个组件都有多个副本。

当你部署集群控制面时,应将控制面组件的副本跨多个失效区来部署。 如果可用性是一个很重要的指标,应该选择至少三个失效区, 并将每个控制面组件(API 服务器、调度器、etcd、控制器管理器)复制多个副本, 跨至少三个失效区来部署。如果你在运行云控制器管理器, 则也应该将该组件跨所选的三个失效区来部署。

说明:

Kubernetes 并不会为 API 服务器端点提供跨失效区的弹性。 你可以为集群 API 服务器使用多种技术来提升其可用性,包括使用 DNS 轮转、SRV 记录或者带健康检查的第三方负载均衡解决方案等等。

节点行为

Kubernetes 自动为负载资源(如 DeploymentStatefulSet) 跨集群中不同节点来部署其 Pod。 这种分布逻辑有助于降低失效带来的影响。

节点启动时,每个节点上的 kubelet 会向 Kubernetes API 中代表该 kubelet 的 Node 对象添加标签。 这些标签可能包含区信息

如果你的集群跨了多个可用区或者地理区域,你可以使用节点标签,结合 Pod 拓扑分布约束 来控制如何在你的集群中多个失效域之间分布 Pod。这里的失效域可以是地理区域、可用区甚至是特定节点。 这些提示信息使得调度器能够更好地调度 Pod,以实现更好的可用性,降低因为某种失效给整个工作负载带来的风险。

例如,你可以设置一种约束,确保某个 StatefulSet 中的 3 个副本都运行在不同的可用区中, 只要其他条件允许。你可以通过声明的方式来定义这种约束, 而不需要显式指定每个工作负载使用哪些可用区。

跨多个区分布节点

Kubernetes 的核心逻辑并不会帮你创建节点,你需要自行完成此操作,或者使用类似 Cluster API 这类工具来替你管理节点。

使用类似 Cluster API 这类工具,你可以跨多个失效域来定义一组用做你的集群工作节点的机器, 以及当整个区的服务出现中断时如何自动治愈集群的策略。

为 Pod 手动指定区

你可以应用节点选择算符约束到你所创建的 Pod 上,或者为 Deployment、StatefulSet 或 Job 这类工作负载资源中的 Pod 模板设置此类约束。

跨区的存储访问

当创建持久卷时,Kubernetes 会自动向那些链接到特定区的 PersistentVolume 添加区标签。 调度器通过其 NoVolumeZoneConflict 断言确保申领给定 PersistentVolume 的 Pod 只会被调度到该卷所在的可用区。

请注意,添加区标签的方法可能取决于你的云提供商和存储制备器。 请参阅具体的环境文档,确保配置正确。

请注意,添加区标签的方法可能会根据你所使用的云提供商和存储制备器而有所不同。 为确保配置正确,请始终参阅你的环境的特定文档。

你可以为 PersistentVolumeClaim 指定 StorageClass 以设置该类中的存储可以使用的失效域(区)。 要了解如何配置能够感知失效域或区的 StorageClass, 请参阅可用的拓扑逻辑

网络

Kubernetes 自身不提供与可用区相关的联网配置。 你可以使用网络插件 来配置集群的联网,该网络解决方案可能拥有一些与可用区相关的元素。 例如,如果你的云提供商支持 type=LoadBalancer 的 Service, 则负载均衡器可能仅会将请求流量发送到运行在负责处理给定连接的负载均衡器组件所在的区。 请查阅云提供商的文档了解详细信息。

对于自定义的或本地集群部署,也可以考虑这些因素。 ServiceIngress 的行为, 包括处理不同失效区的方法,在很大程度上取决于你的集群是如何搭建的。

失效恢复

在搭建集群时,你可能需要考虑当某区域中的所有失效区都同时掉线时,是否以及如何恢复服务。 例如,你是否要求在某个区中至少有一个节点能够运行 Pod? 请确保任何对集群很关键的修复工作都不要指望集群中至少有一个健康节点。 例如:当所有节点都不健康时,你可能需要运行某个修复性的 Job, 该 Job 要设置特定的容忍度, 以便修复操作能够至少将一个节点恢复为可用状态。

Kubernetes 对这类问题没有现成的解决方案;不过这也是要考虑的因素之一。

接下来

要了解调度器如何在集群中放置 Pod 并遵从所配置的约束, 可参阅调度与驱逐

3 - 校验节点设置

节点一致性测试

节点一致性测试是一个容器化的测试框架,提供了针对节点的系统验证和功能测试。 测试验证节点是否满足 Kubernetes 的最低要求;通过测试的节点有资格加入 Kubernetes 集群。

该测试主要检测节点是否满足 Kubernetes 的最低要求,通过检测的节点有资格加入 Kubernetes 集群。

节点的前提条件

要运行节点一致性测试,节点必须满足与标准 Kubernetes 节点相同的前提条件。 节点至少应安装以下守护程序:

运行节点一致性测试

要运行节点一致性测试,请执行以下步骤:

  1. 得出 kubelet 的 --kubeconfig 的值;例如:--kubeconfig=/var/lib/kubelet/config.yaml。 由于测试框架启动了本地控制平面来测试 kubelet,因此使用 http://localhost:8080 作为API 服务器的 URL。 一些其他的 kubelet 命令行参数可能会被用到:
  1. 使用以下命令运行节点一致性测试:

    # $CONFIG_DIR 是你 kubelet 的 Pod manifest 路径。
    # $LOG_DIR 是测试的输出路径。
    sudo docker run -it --rm --privileged --net=host \
      -v /:/rootfs -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
      registry.k8s.io/node-test:0.2
    

针对其他硬件体系结构运行节点一致性测试

Kubernetes 也为其他硬件体系结构的系统提供了节点一致性测试的 Docker 镜像:

架构 镜像
amd64 node-test-amd64
arm node-test-arm
arm64 node-test-arm64

运行特定的测试

要运行特定测试,请使用你希望运行的测试的特定表达式覆盖环境变量 FOCUS

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e FOCUS=MirrorPod \ # Only run MirrorPod test
  registry.k8s.io/node-test:0.2

要跳过特定的测试,请使用你希望跳过的测试的常规表达式覆盖环境变量 SKIP

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e SKIP=MirrorPod \ # 运行除 MirrorPod 测试外的所有一致性测试内容
  registry.k8s.io/node-test:0.2

节点一致性测试是 节点端到端测试的容器化版本。 默认情况下,它会运行所有一致性测试。

理论上,只要合理地配置容器和挂载所需的卷,就可以运行任何的节点端到端测试用例。 但是这里强烈建议只运行一致性测试,因为运行非一致性测试需要很多复杂的配置。

注意事项

4 - 强制实施 Pod 安全性标准

本页提供实施 Pod 安全标准(Pod Security Standards) 时的一些最佳实践。

使用内置的 Pod 安全性准入控制器

特性状态: Kubernetes v1.25 [stable]

Pod 安全性准入控制器 尝试替换已被废弃的 PodSecurityPolicies。

配置所有集群名字空间

完全未经配置的名字空间应该被视为集群安全模型中的重大缺陷。 我们建议花一些时间来分析在每个名字空间中执行的负载的类型, 并通过引用 Pod 安全性标准来确定每个负载的合适级别。 未设置标签的名字空间应该视为尚未被评估。

针对所有名字空间中的所有负载都具有相同的安全性需求的场景, 我们提供了一个示例 用来展示如何批量应用 Pod 安全性标签。

拥抱最小特权原则

在一个理想环境中,每个名字空间中的每个 Pod 都会满足 restricted 策略的需求。 不过,这既不可能也不现实,某些负载会因为合理的原因而需要特权上的提升。

采用多种模式的策略

Pod 安全性标准准入控制器的 auditwarn 模式(mode) 能够在不影响现有负载的前提下,让该控制器更方便地收集关于 Pod 的重要的安全信息。

针对所有名字空间启用这些模式是一种好的实践,将它们设置为你最终打算 enforce期望的 级别和版本。这一阶段中所生成的警告和审计注解信息可以帮助你到达这一状态。 如果你期望负载的作者能够作出变更以便适应期望的级别,可以启用 warn 模式。 如果你希望使用审计日志了监控和驱动变更,以便负载能够适应期望的级别,可以启用 audit 模式。

当你将 enforce 模式设置为期望的取值时,这些模式在不同的场合下仍然是有用的:

第三方替代方案

说明: 本部分链接到提供 Kubernetes 所需功能的第三方项目。Kubernetes 项目作者不负责这些项目。此页面遵循CNCF 网站指南,按字母顺序列出项目。要将项目添加到此列表中,请在提交更改之前阅读内容指南

Kubernetes 生态系统中也有一些其他强制实施安全设置的替代方案处于开发状态中:

采用 内置的 方案(例如 PodSecurity 准入控制器)还是第三方工具, 这一决策完全取决于你自己的情况。在评估任何解决方案时,对供应链的信任都是至关重要的。 最终,使用前述方案中的 任何 一种都好过放任自流。

5 - PKI 证书和要求

Kubernetes 需要 PKI 证书才能进行基于 TLS 的身份验证。如果你是使用 kubeadm 安装的 Kubernetes, 则会自动生成集群所需的证书。 你也可以自己生成证书 --- 例如,不将私钥存储在 API 服务器上, 可以让私钥更加安全。此页面说明了集群必需的证书。

集群是如何使用证书的

Kubernetes 需要 PKI 才能执行以下操作:

服务器证书

客户端证书

kubelet 的服务器和客户端证书

为了建立安全连接并向 kubelet 进行身份验证,API 服务器需要客户端证书和密钥对。

在此场景中,证书的使用有两种方法:

说明:

只有当你运行 kube-proxy 并要支持扩展 API 服务器时, 才需要 front-proxy 证书。

etcd 还实现了双向 TLS 来对客户端和对其他对等节点进行身份验证。

证书存储位置

假如你通过 kubeadm 安装 Kubernetes,大多数证书会被存储在 /etc/kubernetes/pki 中。 本文档中的所有路径都是相对于该目录的,但用户账号证书除外,kubeadm 将其放在 /etc/kubernetes 中。

手动配置证书

如果你不想通过 kubeadm 生成所需证书,你可以使用一个单根 CA 来创建这些证书,或者直接提供所有证书。 参见证书以进一步了解如何创建自己的证书授权机构。 更多关于管理证书的信息,请参阅使用 kubeadm 进行证书管理

单根 CA

你可以创建由管理员控制的单根 CA。这个根 CA 可以创建多个中间 CA, 并将所有进一步的创建委托给 Kubernetes 本身。

需要这些 CA:

路径 默认 CN 描述
ca.crt、key kubernetes-ca Kubernetes 通用 CA
etcd/ca.crt、key etcd-ca 与 etcd 相关的所有功能
front-proxy-ca.crt、key kubernetes-front-proxy-ca 用于前端代理

上面的 CA 之外,还需要获取用于服务账号管理的密钥对,也就是 sa.keysa.pub。 下面的例子说明了上表中所示的 CA 密钥和证书文件。

/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-ca.key

所有的证书

如果你不想将 CA 的私钥拷贝至你的集群中,你也可以自己生成全部的证书。

需要这些证书:

默认 CN 父级 CA O(位于 Subject 中) kind 主机(SAN)
kube-etcd etcd-ca server、client <hostname><Host_IP>localhost127.0.0.1
kube-etcd-peer etcd-ca server、client <hostname><Host_IP>localhost127.0.0.1
kube-etcd-healthcheck-client etcd-ca client
kube-apiserver-etcd-client etcd-ca client
kube-apiserver kubernetes-ca server <hostname><Host_IP><advertise_IP>1
kube-apiserver-kubelet-client kubernetes-ca system:masters client
front-proxy-client kubernetes-front-proxy-ca client

说明:

不使用超级用户组 system:masters 来控制 kube-apiserver-kubelet-client, 可以使用一个权限较低的组。kubeadm 使用 kubeadm:cluster-admins 组来达到这个目的。

其中 kind 对应一种或多种类型的 x509 密钥用途,也可记录在 CertificateSigningRequest 类型的 .spec.usages 中:

kind 密钥用途
server 数字签名、密钥加密、服务端认证
client 数字签名、密钥加密、客户端认证

说明:

上面列出的 Host/SAN 是获取工作集群的推荐配置方式; 如果需要特殊安装,则可以在所有服务器证书上添加其他 SAN。

说明:

对于 kubeadm 用户:

证书路径

证书应放置在建议的路径中(以便 kubeadm 使用)。无论使用什么位置,都应使用给定的参数指定路径。

默认 CN 建议的密钥路径 建议的证书路径 命令 密钥参数 证书参数
etcd-ca etcd/ca.key etcd/ca.crt kube-apiserver --etcd-cafile
kube-apiserver-etcd-client apiserver-etcd-client.key apiserver-etcd-client.crt kube-apiserver --etcd-keyfile --etcd-certfile
kubernetes-ca ca.key ca.crt kube-apiserver --client-ca-file
kubernetes-ca ca.key ca.crt kube-controller-manager --cluster-signing-key-file --client-ca-file, --root-ca-file, --cluster-signing-cert-file
kube-apiserver apiserver.key apiserver.crt kube-apiserver --tls-private-key-file --tls-cert-file
kube-apiserver-kubelet-client apiserver-kubelet-client.key apiserver-kubelet-client.crt kube-apiserver --kubelet-client-key --kubelet-client-certificate
front-proxy-ca front-proxy-ca.key front-proxy-ca.crt kube-apiserver --requestheader-client-ca-file
front-proxy-ca front-proxy-ca.key front-proxy-ca.crt kube-controller-manager --requestheader-client-ca-file
front-proxy-client front-proxy-client.key front-proxy-client.crt kube-apiserver --proxy-client-key-file --proxy-client-cert-file
etcd-ca etcd/ca.key etcd/ca.crt etcd --trusted-ca-file, --peer-trusted-ca-file
kube-etcd etcd/server.key etcd/server.crt etcd --key-file --cert-file
kube-etcd-peer etcd/peer.key etcd/peer.crt etcd --peer-key-file --peer-cert-file
etcd-ca etcd/ca.crt etcdctl --cacert
kube-etcd-healthcheck-client etcd/healthcheck-client.key etcd/healthcheck-client.crt etcdctl --key --cert

注意事项同样适用于服务账号密钥对:

私钥路径 公钥路径 命令 参数
sa.key kube-controller-manager --service-account-private-key-file
sa.pub kube-apiserver --service-account-key-file

下面的例子展示了自行生成所有密钥和证书时所需要提供的文件路径。 这些路径基于前面的表格

/etc/kubernetes/pki/etcd/ca.key
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/apiserver-etcd-client.key
/etc/kubernetes/pki/apiserver-etcd-client.crt
/etc/kubernetes/pki/ca.key
/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/apiserver.key
/etc/kubernetes/pki/apiserver.crt
/etc/kubernetes/pki/apiserver-kubelet-client.key
/etc/kubernetes/pki/apiserver-kubelet-client.crt
/etc/kubernetes/pki/front-proxy-ca.key
/etc/kubernetes/pki/front-proxy-ca.crt
/etc/kubernetes/pki/front-proxy-client.key
/etc/kubernetes/pki/front-proxy-client.crt
/etc/kubernetes/pki/etcd/server.key
/etc/kubernetes/pki/etcd/server.crt
/etc/kubernetes/pki/etcd/peer.key
/etc/kubernetes/pki/etcd/peer.crt
/etc/kubernetes/pki/etcd/healthcheck-client.key
/etc/kubernetes/pki/etcd/healthcheck-client.crt
/etc/kubernetes/pki/sa.key
/etc/kubernetes/pki/sa.pub

为用户账号配置证书

你必须手动配置以下管理员账号和服务账号:

文件名 凭据名称 默认 CN O (位于 Subject 中)
admin.conf default-admin kubernetes-admin <admin-group>
super-admin.conf default-super-admin kubernetes-super-admin system:masters
kubelet.conf default-auth system:node:<nodeName>(参阅注释) system:nodes
controller-manager.conf default-controller-manager system:kube-controller-manager
scheduler.conf default-scheduler system:kube-scheduler

说明:

kubelet.conf<nodeName> 的值必须与 kubelet 向 apiserver 注册时提供的节点名称的值完全匹配。 有关更多详细信息,请阅读节点授权

说明:

在上面的例子中,<admin-group> 是实现特定的。 一些工具在默认的 admin.conf 中签署证书,以成为 system:masters 组的一部分。 system:masters 是一个紧急情况下的超级用户组,可以绕过 Kubernetes 的授权层,如 RBAC。 另外,某些工具不会生成单独的 super-admin.conf 将证书绑定到这个超级用户组。

kubeadm 在 kubeconfig 文件中生成两个单独的管理员证书。 一个是在 admin.conf 中,带有 Subject: O = kubeadm:cluster-admins, CN = kubernetes-adminkubeadm:cluster-admins 是绑定到 cluster-admin ClusterRole 的自定义组。 这个文件在所有由 kubeadm 管理的控制平面机器上生成。

另一个是在 super-admin.conf 中,具有 Subject: O = system:masters, CN = kubernetes-super-admin。 这个文件只在调用了 kubeadm init 的节点上生成。

  1. 对于每个配置,请都使用给定的通用名称(CN)和组织(O)生成 x509 证书/密钥对。

  2. 为每个配置运行下面的 kubectl 命令:

    KUBECONFIG=<文件名> kubectl config set-cluster default-cluster --server=https://<主机ip>:6443 --certificate-authority <kubernetes-ca路径> --embed-certs
    KUBECONFIG=<文件名> kubectl config set-credentials <凭据名称> --client-key <密钥路径>.pem --client-certificate <证书路径>.pem --embed-certs
    KUBECONFIG=<文件名> kubectl config set-context default-system --cluster default-cluster --user <凭据名称>
    KUBECONFIG=<文件名> kubectl config use-context default-system
    

这些文件用途如下:

文件名 命令 说明
admin.conf kubectl 配置集群的管理员
super-admin.conf kubectl 为集群配置超级管理员用户
kubelet.conf kubelet 集群中的每个节点都需要一份
controller-manager.conf kube-controller-manager 必须添加到 manifests/kube-controller-manager.yaml 清单中
scheduler.conf kube-scheduler 必须添加到 manifests/kube-scheduler.yaml 清单中

下面是前表中所列文件的完整路径。

/etc/kubernetes/admin.conf
/etc/kubernetes/super-admin.conf
/etc/kubernetes/kubelet.conf
/etc/kubernetes/controller-manager.conf
/etc/kubernetes/scheduler.conf

  1. 用来连接到集群的不同 IP 或 DNS 名称 (就像 kubeadm 为负载均衡所使用的固定 IP 或 DNS 名称:kuberneteskubernetes.defaultkubernetes.default.svckubernetes.default.svc.clusterkubernetes.default.svc.cluster.local)。 ↩︎