1 - 运行于多区环境

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

介绍

Kubernetes 1.2 添加了跨多个失效区(Failure Zone)运行同一集群的能力 (GCE 把它们称作“区(Zones)”,AWS 把它们称作“可用区(Availability Zones)”, 这里我们用“区(Zones)”指代它们)。 此能力是更广泛的集群联邦(Cluster Federation)特性的一个轻量级版本。 集群联邦之前有一个昵称 "Ubernetes")。 完全的集群联邦可以将运行在多个区域(Region)或云供应商(或本地数据中心)的多个 Kubernetes 集群组合起来。 不过,很多用户仅仅是希望在同一云厂商平台的多个区域运行一个可用性更好的集群, 而这恰恰是 1.2 引入的多区支持所带来的特性 (此特性之前有一个昵称 “Ubernetes Lite”)。

多区支持有意实现的有局限性:可以在跨多个区域运行同一 Kubernetes 集群,但只能 在同一区域(Region)和云厂商平台。目前仅自动支持 GCE 和 AWS,尽管为其他云平台 或裸金属平台添加支持页相对容易,只需要确保节点和卷上添加合适的标签即可。

功能

节点启动时,kubelet 自动向其上添加区信息标签。

在单区(Single-Zone)集群中, Kubernetes 会自动将副本控制器或服务中的 Pod 分布到不同节点,以降低节点失效的影响。 在多区集群中,这一分布负载的行为被扩展到跨区分布,以降低区失效的影响, 跨区分布的能力是通过 SelectorSpreadPriority 实现的。此放置策略亦仅仅是 尽力而为,所以如果你的集群所跨区是异质的(例如,节点个数不同、节点类型 不同或者 Pod 资源需求不同),放置策略都可能无法完美地跨区完成 Pod 的 均衡分布。如果需要,你可以使用同质区(节点个数和类型相同)以降低不均衡 分布的可能性。

持久卷被创建时,PersistentVolumeLabel 准入控制器会自动为其添加区标签。 调度器使用 VolumeZonePredicate 断言确保申领某给定卷的 Pod 只会被放到 该卷所在的区。这是因为卷不可以跨区挂载。

局限性

多区支持有一些很重要的局限性:

  • 我们假定不同的区之间在网络上彼此距离很近,所以我们不执行可感知区的路由。 尤其是,即使某些负责提供该服务的 Pod 与客户端位于同一区,通过服务末端 进入的流量可能会跨区,因而会导致一些额外的延迟和开销。
  • 卷与区之间的亲和性仅适用于 PV 持久卷。例如,如果你直接在 Pod 规约中指定某 EBS 卷,这种亲和性支持就无法工作。

  • 集群无法跨多个云平台或者地理区域运行。这类功能需要完整的联邦特性支持。

  • 尽管你的节点位于多个区中,kube-up 脚本目前默认只能构造一个主控节点。 尽管服务是高可用的,能够忍受失去某个区的问题,控制面位于某一个区中。 希望运行高可用控制面的用户应该遵照 高可用性 中的指令构建。

卷局限性

以下局限性通过 拓扑感知的卷绑定解决:

  • 使用动态卷供应时,StatefulSet 卷的跨区分布目前与 Pod 亲和性和反亲和性策略不兼容。
  • 如果 StatefulSet 的名字中包含连字符("-"),卷的跨区分布可能无法实现存储的 跨区同一分布。

  • 当在一个 Deployment 或 Pod 规约中指定多个 PVC 申领时,则需要为某特定区域 配置 StorageClass,或者在某一特定区域中需要静态供应 PV 卷。 另一种解决方案是使用 StatefulSet,确保给定副本的所有卷都从同一区中供应。

演练

我们现在准备对在 GCE 和 AWS 上配置和使用多区集群进行演练。为了完成此演练, 你需要设置 MULTIZONE=true 来启动一个完整的集群,之后指定 KUBE_USE_EXISTING_MASTER=true 并再次运行 kube-up 添加其他区中的节点。

建立集群

和往常一样创建集群,不过需要设置 MULTIZONE,以便告诉集群需要管理多个区。 这里我们在 us-central1-a 创建节点。

GCE:

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-a NUM_NODES=3 bash

AWS:

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2a NUM_NODES=3 bash

这一步骤和往常一样启动一个集群,不过尽管 MULTIZONE=true 标志已经启用了多区功能特性支持,集群仍然运行在一个区内。

节点已被打标签

查看节点,你会看到节点上已经有了区信息标签。 目前这些节点都在 us-central1-a (GCE) 或 us-west-2a (AWS)。 对于区域(Region),标签为 topology.kubernetes.io/region, 对于区(Zone),标签为 topology.kubernetes.io/zone

kubectl get nodes --show-labels

输出类似于:

NAME                     STATUS                     ROLES    AGE   VERSION          LABELS
kubernetes-master        Ready,SchedulingDisabled   <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-1,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-master
kubernetes-minion-87j9   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-a12q

添加第二个区中的节点

让我们向现有集群中添加另外一组节点,复用现有的主控节点,但运行在不同的区 (us-central1-bus-west-2b)。 我们再次运行 kube-up,不过设置 KUBE_USE_EXISTING_MASTER=truekube-up 不会创建新的主控节点,而会复用之前创建的主控节点。

GCE:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-b NUM_NODES=3 kubernetes/cluster/kube-up.sh

在 AWS 上,我们还需要为额外的子网指定网络 CIDR,以及主控节点的内部 IP 地址:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2b NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.1.0/24 MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

再次查看节点,你会看到新启动了三个节点并且其标签表明运行在 us-central1-b 区:

kubectl get nodes --show-labels

输出类似于:

NAME                     STATUS                     ROLES    AGE   VERSION           LABELS
kubernetes-master        Ready,SchedulingDisabled   <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-1,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-master
kubernetes-minion-281d   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-87j9   Ready                      <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv   Ready                      <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q   Ready                      <none>   17m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-a12q
kubernetes-minion-pp2f   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-pp2f
kubernetes-minion-wf8i   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-wf8i

卷亲和性

通过动态卷供应创建一个卷(只有 PV 持久卷支持区亲和性):

kubectl apply -f - <<EOF
{
  "apiVersion": "v1",
  "kind": "PersistentVolumeClaim",
  "metadata": {
    "name": "claim1",
    "annotations": {
        "volume.alpha.kubernetes.io/storage-class": "foo"
    }
  },
  "spec": {
    "accessModes": [
      "ReadWriteOnce"
    ],
    "resources": {
      "requests": {
        "storage": "5Gi"
      }
    }
  }
}
EOF
说明: Kubernetes 1.3 及以上版本会将动态 PV 申领散布到所配置的各个区。 在 1.2 版本中,动态持久卷总是在集群主控节点所在的区 (这里的 us-central1-aus-west-2a), 对应的 Issue (#23330) 在 1.3 及以上版本中已经解决。

现在我们来验证 Kubernetes 自动为 PV 打上了所在区或区域的标签:

kubectl get pv --show-labels

输出类似于:

NAME           CAPACITY   ACCESSMODES   RECLAIM POLICY   STATUS    CLAIM            STORAGECLASS    REASON    AGE       LABELS
pv-gce-mj4gm   5Gi        RWO           Retain           Bound     default/claim1   manual                    46s       topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a

现在我们将创建一个使用 PVC 申领的 Pod。 由于 GCE PD 或 AWS EBS 卷都不能跨区挂载,这意味着 Pod 只能创建在卷所在的区:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: claim1
EOF

注意 Pod 自动创建在卷所在的区,因为云平台提供商一般不允许跨区挂接存储卷。

kubectl describe pod mypod | grep Node
Node:        kubernetes-minion-9vlv/10.240.0.5

检查节点标签:

kubectl get node kubernetes-minion-9vlv --show-labels
NAME                     STATUS    AGE    VERSION          LABELS
kubernetes-minion-9vlv   Ready     22m    v1.6.0+fff5156   beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv

Pod 跨区分布

同一副本控制器或服务的多个 Pod 会自动完成跨区分布。 首先,我们现在第三个区启动一些节点:

GCE:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-f NUM_NODES=3 kubernetes/cluster/kube-up.sh

AWS:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2c NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.2.0/24 MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

验证你现在有来自三个区的节点:

kubectl get nodes --show-labels

创建 guestbook-go 示例,其中包含副本个数为 3 的 RC,运行一个简单的 Web 应用:

find kubernetes/examples/guestbook-go/ -name '*.json' | xargs -I {} kubectl apply -f {}

Pod 应该跨三个区分布:

kubectl describe pod -l app=guestbook | grep Node
Node:        kubernetes-minion-9vlv/10.240.0.5
Node:        kubernetes-minion-281d/10.240.0.8
Node:        kubernetes-minion-olsh/10.240.0.11
kubectl get node kubernetes-minion-9vlv kubernetes-minion-281d kubernetes-minion-olsh --show-labels
NAME                     STATUS    ROLES    AGE    VERSION          LABELS
kubernetes-minion-9vlv   Ready     <none>   34m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-281d   Ready     <none>   20m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-olsh   Ready     <none>   3m     v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-f,kubernetes.io/hostname=kubernetes-minion-olsh

负载均衡器也会跨集群中的所有区;guestbook-go 示例中包含了一个负载均衡 服务的例子:

kubectl describe service guestbook | grep LoadBalancer.Ingress

输出类似于:

LoadBalancer Ingress:   130.211.126.21

设置上面的 IP 地址:

export IP=130.211.126.21

使用 curl 访问该 IP:

curl -s http://${IP}:3000/env | grep HOSTNAME

输出类似于:

  "HOSTNAME": "guestbook-44sep",

如果多次尝试该命令:

(for i in `seq 20`; do curl -s http://${IP}:3000/env | grep HOSTNAME; done)  | sort | uniq

输出类似于:

  "HOSTNAME": "guestbook-44sep",
  "HOSTNAME": "guestbook-hum5n",
  "HOSTNAME": "guestbook-ppm40",

负载均衡器正确地选择不同的 Pod,即使它们跨了多个区。

停止集群

当完成以上工作之后,清理任务现场:

GCE:

KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true KUBE_GCE_ZONE=us-central1-f kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true KUBE_GCE_ZONE=us-central1-b kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-a kubernetes/cluster/kube-down.sh

AWS:

KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2c kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2b kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2a kubernetes/cluster/kube-down.sh

2 - 创建大型集群

支持

在 v1.20 版本中, Kubernetes 支持的最大节点数为 5000。更具体地说,我们支持满足以下所有条件的配置:

  • 节点数不超过 5000
  • Pod 总数不超过 150000
  • 容器总数不超过 300000
  • 每个节点的 pod 数量不超过 100

设定

集群是一组运行着 Kubernetes 代理的节点(物理机或者虚拟机),这些节点由主控节点(集群级控制面)控制。

通常,集群中的节点数由特定于云平台的配置文件 config-default.sh (可以参考 GCE 平台的 config-default.sh) 中的 NUM_NODES 参数控制。

但是,在许多云供应商的平台上,仅将该值更改为非常大的值,可能会导致安装脚本运行失败。例如,在 GCE,由于配额问题,集群会启动失败。

因此,在创建大型 Kubernetes 集群时,必须考虑以下问题。

配额问题

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

  • 增加诸如 CPU,IP 等资源的配额。
    • 例如,在 GCE,您需要增加以下资源的配额:
      • CPUs
      • VM 实例
      • 永久磁盘总量
      • 使用中的 IP 地址
      • 防火墙规则
      • 转发规则
      • 路由
      • 目标池
  • 由于某些云供应商会对虚拟机的创建进行流控,因此需要对设置脚本进行更改,使其以较小的批次启动新的节点,并且之间有等待时间。

Etcd 存储

为了提高大规模集群的性能,我们将事件存储在专用的 etcd 实例中。

在创建集群时,现有 salt 脚本可以:

  • 启动并配置其它 etcd 实例
  • 配置 API 服务器以使用 etcd 存储事件

主控节点大小和主控组件

在 GCE/Google Kubernetes Engine 和 AWS 上,kube-up 会根据节点数量自动为您集群中的 master 节点配置适当的虚拟机大小。在其它云供应商的平台上,您将需要手动配置它。作为参考,我们在 GCE 上使用的规格为:

  • 1-5 个节点:n1-standard-1
  • 6-10 个节点:n1-standard-2
  • 11-100 个节点:n1-standard-4
  • 101-250 个节点:n1-standard-8
  • 251-500 个节点:n1-standard-16
  • 超过 500 节点:n1-standard-32

在 AWS 上使用的规格为

  • 1-5 个节点:m3.medium
  • 6-10 个节点:m3.large
  • 11-100 个节点:m3.xlarge
  • 101-250 个节点:m3.2xlarge
  • 251-500 个节点:c4.4xlarge
  • 超过 500 节点:c4.8xlarge
说明:

在 Google Kubernetes Engine 上,主控节点的大小会根据集群的大小自动调整。更多有关信息,请参阅 此博客文章

在 AWS 上,主控节点的规格是在集群启动时设置的,并且,即使以后通过手动删除或添加节点的方式使集群缩容或扩容,主控节点的大小也不会更改。

插件资源

为了防止内存泄漏或 集群插件 中的其它资源问题导致节点上所有可用资源被消耗,Kubernetes 限制了插件容器可以消耗的 CPU 和内存资源 (请参阅 PR #10653#10778)。

例如:

  containers:
  - name: fluentd-cloud-logging
    image: k8s.gcr.io/fluentd-gcp:1.16
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

除了 Heapster 之外,这些限制都是静态的,并且限制是基于 4 节点集群上运行的插件数据得出的(请参阅 #10335)。在大规模集群上运行时,插件会消耗大量资源(请参阅 #5880)。因此,如果在不调整这些值的情况下部署了大规模集群,插件容器可能会由于达到限制而不断被杀死。

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

  • 根据集群的规模,如果使用了以下插件,提高其内存和 CPU 上限(每个插件都有一个副本处理整个群集,因此内存和 CPU 使用率往往与集群的规模/负载成比例增长) :
  • 根据集群的规模,如果使用了以下插件,调整其副本数量(每个插件都有多个副本,增加副本数量有助于处理增加的负载,但是,由于每个副本的负载也略有增加,因此也请考虑增加 CPU/内存限制):
  • 根据集群的规模,如果使用了以下插件,限制其内存和 CPU 上限(这些插件在每个节点上都有一个副本,但是 CPU/内存使用量也会随集群负载/规模而略有增加):

Heapster 的资源限制与您集群的初始大小有关(请参阅 #16185#22940)。如果您发现 Heapster 资源不足,您应该调整堆内存请求的计算公式(有关详细信息,请参阅相关 PR)。

关于如何检测插件容器是否达到资源限制,参见 计算资源的故障排除 部分。

未来,我们期望根据集群规模大小来设置所有群集附加资源限制,并在集群扩缩容时动态调整它们。 我们欢迎您来实现这些功能。

允许启动时次要节点失败

出于各种原因(更多详细信息,请参见 #18969), 在 kube-up.sh 中设置很大的 NUM_NODES 时,可能会由于少数节点无法正常启动而失败。 此时,您有两个选择:重新启动集群(运行 kube-down.sh,然后再运行 kube-up.sh),或者在运行 kube-up.sh 之前将环境变量 ALLOWED_NOTREADY_NODES 设置为您认为合适的任何值。采取后者时,即使运行成功的节点数量少于 NUM_NODESkube-up.sh 仍可以运行成功。根据失败的原因,这些节点可能会稍后加入集群,又或者群集的大小保持在 NUM_NODES-ALLOWED_NOTREADY_NODES

3 - 校验节点设置

节点一致性测试

节点一致性测试 是一个容器化的测试框架,提供了针对节点的系统验证和功能测试。

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

节点的前提条件

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

  • 容器运行时 (Docker)
  • Kubelet

运行节点一致性测试

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

  1. 得出 kubelet 的 --kubeconfig 的值;例如:--kubeconfig=/var/lib/kubelet/config.yaml. 由于测试框架启动了本地控制平面来测试 kubelet, 因此使用 http://localhost:8080 作为API 服务器的 URL。 一些其他的 kubelet 命令行参数可能会被用到:
    • --pod-cidr: 如果使用 kubenet, 需要为 Kubelet 任意指定一个 CIDR, 例如 --pod-cidr=10.180.0.0/24
    • --cloud-provider: 如果使用 --cloud-provider=gce,需要移除这个参数 来运行测试。
  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 \
      k8s.gcr.io/node-test:0.2
    

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

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

架构镜像
amd64node-test-amd64
armnode-test-arm
arm64node-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
k8s.gcr.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 测试外的所有一致性测试内容
k8s.gcr.io/node-test:0.2

节点一致性测试是节点端到端测试的容器化版本。

默认情况下,它会运行所有一致性测试。

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

注意

  • 测试会在节点上遗留一些 Docker 镜像, 包括节点一致性测试本身的镜像和功能测试相关的镜像。
  • 测试会在节点上遗留一些死的容器。这些容器是在功能测试的过程中创建的。

4 - PKI 证书和要求

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

集群是如何使用证书的

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

  • Kubelet 的客户端证书,用于 API 服务器身份验证
  • API 服务器端点的证书
  • 集群管理员的客户端证书,用于 API 服务器身份认证
  • API 服务器的客户端证书,用于和 Kubelet 的会话
  • API 服务器的客户端证书,用于和 etcd 的会话
  • 控制器管理器的客户端证书/kubeconfig,用于和 API 服务器的会话
  • 调度器的客户端证书/kubeconfig,用于和 API 服务器的会话
  • 前端代理 的客户端及服务端证书
说明: 只有当你运行 kube-proxy 并要支持 扩展 API 服务器 时,才需要 front-proxy 证书

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

证书存放的位置

如果你是通过 kubeadm 安装的 Kubernetes,所有证书都存放在 /etc/kubernetes/pki 目录下。本文所有相关的路径都是基于该路径的相对路径。

手动配置证书

如果你不想通过 kubeadm 生成这些必需的证书,你可以通过下面两种方式之一来手动创建他们。

单根 CA

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

需要这些 CA:

路径默认 CN描述
ca.crt,keykubernetes-caKubernetes 通用 CA
etcd/ca.crt,keyetcd-ca与 etcd 相关的所有功能
front-proxy-ca.crt,keykubernetes-front-proxy-ca用于 前端代理

上面的 CA 之外,还需要获取用于服务账户管理的密钥对,也就是 sa.keysa.pub

所有的证书

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

需要这些证书:

默认 CN父级 CAO (位于 Subject 中)类型主机 (SAN)
kube-etcdetcd-caserver, clientlocalhost, 127.0.0.1
kube-etcd-peeretcd-caserver, client<hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-healthcheck-clientetcd-caclient
kube-apiserver-etcd-clientetcd-casystem:mastersclient
kube-apiserverkubernetes-caserver<hostname>, <Host_IP>, <advertise_IP>, [1]
kube-apiserver-kubelet-clientkubernetes-casystem:mastersclient
front-proxy-clientkubernetes-front-proxy-caclient

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

其中,kind 对应一种或多种类型的 x509 密钥用途

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

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

说明:

对于 kubeadm 用户:

  • 不使用私钥,将证书复制到集群 CA 的方案,在 kubeadm 文档中将这种方案称为外部 CA。
  • 如果将以上列表与 kubeadm 生成的 PKI 进行比较,你会注意到,如果使用外部 etcd,则不会生成 kube-etcdkube-etcd-peerkube-etcd-healthcheck-client 证书。

证书路径

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

默认 CN建议的密钥路径建议的证书路径命令密钥参数证书参数
etcd-caetcd/ca.keyetcd/ca.crtkube-apiserver--etcd-cafile
kube-apiserver-etcd-clientapiserver-etcd-client.keyapiserver-etcd-client.crtkube-apiserver--etcd-keyfile--etcd-certfile
kubernetes-caca.keyca.crtkube-apiserver--client-ca-file
kubernetes-caca.keyca.crtkube-controller-manager--cluster-signing-key-file--client-ca-file, --root-ca-file, --cluster-signing-cert-file
kube-apiserverapiserver.keyapiserver.crtkube-apiserver--tls-private-key-file--tls-cert-file
kube-apiserver-kubelet-clientapiserver-kubelet-client.keyapiserver-kubelet-client.crtkube-apiserver--kubelet-client-key--kubelet-client-certificate
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-apiserver--requestheader-client-ca-file
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-controller-manager--requestheader-client-ca-file
front-proxy-clientfront-proxy-client.keyfront-proxy-client.crtkube-apiserver--proxy-client-key-file--proxy-client-cert-file
etcd-caetcd/ca.keyetcd/ca.crtetcd--trusted-ca-file, --peer-trusted-ca-file
kube-etcdetcd/server.keyetcd/server.crtetcd--key-file--cert-file
kube-etcd-peeretcd/peer.keyetcd/peer.crtetcd--peer-key-file--peer-cert-file
etcd-caetcd/ca.crtetcdctl--cacert
kube-etcd-healthcheck-clientetcd/healthcheck-client.keyetcd/healthcheck-client.crtetcdctl--key--cert

注意事项同样适用于服务帐户密钥对:

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

为用户帐户配置证书

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

文件名凭据名称默认 CNO (位于 Subject 中)
admin.confdefault-adminkubernetes-adminsystem:masters
kubelet.confdefault-authsystem:node:<nodeName> (参阅注释)system:nodes
controller-manager.confdefault-controller-managersystem:kube-controller-manager
scheduler.confdefault-schedulersystem:kube-scheduler
说明: kubelet.conf<nodeName> 的值 必须 与 kubelet 向 apiserver 注册时提供的节点名称的值完全匹配。 有关更多详细信息,请阅读节点授权
  1. 对于每个配置,请都使用给定的 CN 和 O 生成 x509 证书/密钥偶对。

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

KUBECONFIG=<filename> kubectl config set-cluster default-cluster --server=https://<host ip>:6443 --certificate-authority <path-to-kubernetes-ca> --embed-certs
KUBECONFIG=<filename> kubectl config set-credentials <credential-name> --client-key <path-to-key>.pem --client-certificate <path-to-cert>.pem --embed-certs
KUBECONFIG=<filename> kubectl config set-context default-system --cluster default-cluster --user <credential-name>
KUBECONFIG=<filename> kubectl config use-context default-system

这些文件用途如下:

文件名命令说明
admin.confkubectl配置集群的管理员
kubelet.confkubelet集群中的每个节点都需要一份
controller-manager.confkube-controller-manager必需添加到 manifests/kube-controller-manager.yaml 清单中
scheduler.confkube-scheduler必需添加到 manifests/kube-scheduler.yaml 清单中