实现细节
Kubernetes v1.10 [stable]
kubeadm init
和 kubeadm join
结合在一起提供了良好的用户体验,因为从头开始创建实践最佳而配置最基本的 Kubernetes 集群。
但是,kubeadm 如何 做到这一点可能并不明显。
本文档提供了更多幕后的详细信息,旨在分享有关 Kubernetes 集群最佳实践的知识。
核心设计原则
kubeadm init
和 kubeadm join
设置的集群该是:
- 安全的:它应采用最新的最佳实践,例如:
- 实施 RBAC 访问控制
- 使用节点鉴权机制(Node Authorizer)
- 在控制平面组件之间使用安全通信
- 在 API 服务器和 kubelet 之间使用安全通信
- 锁定 kubelet API
- 锁定对系统组件(例如 kube-proxy 和 CoreDNS)的 API 的访问
- 锁定启动引导令牌(Bootstrap Token)可以访问的内容
- 用户友好:用户只需要运行几个命令即可:
kubeadm init
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl apply -f <所选网络.yaml>
kubeadm join --token <令牌> <端点>:<端口>
- 可扩展的:
- 不 应偏向任何特定的网络提供商。不涉及配置集群网络
- 应该可以使用配置文件来自定义各种参数
常量以及众所周知的值和路径
为了降低复杂性并简化基于 kubeadm 的高级工具的开发,对于众所周知的路径和文件名, kubeadm 使用了一组有限的常量值。
Kubernetes 目录 /etc/kubernetes
在应用程序中是一个常量,因为在大多数情况下
它显然是给定的路径,并且是最直观的位置;其他路径常量和文件名有:
/etc/kubernetes/manifests
作为 kubelet 查找静态 Pod 清单的路径。静态 Pod 清单的名称为:etcd.yaml
kube-apiserver.yaml
kube-controller-manager.yaml
kube-scheduler.yaml
/etc/kubernetes/
作为带有控制平面组件身份标识的 kubeconfig 文件的路径。kubeconfig 文件的名称为:kubelet.conf
(在 TLS 引导时名称为bootstrap-kubelet.conf
)controller-manager.conf
scheduler.conf
admin.conf
用于集群管理员和 kubeadm 本身
- 证书和密钥文件的名称:
ca.crt
,ca.key
用于 Kubernetes 证书颁发机构apiserver.crt
,apiserver.key
用于 API 服务器证书apiserver-kubelet-client.crt
,apiserver-kubelet-client.key
用于 API 服务器安全地连接到 kubelet 的客户端证书sa.pub
,sa.key
用于控制器管理器签署 ServiceAccount 时使用的密钥front-proxy-ca.crt
,front-proxy-ca.key
用于前端代理证书颁发机构front-proxy-client.crt
,front-proxy-client.key
用于前端代理客户端
kubeadm init 工作流程内部设计
kubeadm init
内部工作流程
包含一系列要执行的原子性工作任务,如 kubeadm init
中所述。
kubeadm init phase
命令允许用户分别调用每个任务,并最终提供可重用且可组合的 API 或工具箱,
其他 Kubernetes 引导工具、任何 IT 自动化工具和高级用户都可以使用它来
创建自定义集群。
预检
Kubeadm 在启动 init 之前执行一组预检,目的是验证先决条件并避免常见的集群启动问题。
用户可以使用 --ignore-preflight-errors
选项跳过特定的预检查或全部检查。
- [警告] 如果要使用的 Kubernetes 版本(由
--kubernetes-version
标志指定)比 kubeadm CLI 版本至少高一个小版本。 - Kubernetes 系统要求:
- 如果在 linux上运行:
- [错误] 如果内核早于最低要求的版本
- [错误] 如果未设置所需的 cgroups 子系统
- 如果使用 docker:
- [警告/错误] 如果 Docker 服务不存在、被禁用或未激活。
- [错误] 如果 Docker 端点不存在或不起作用
- [警告] 如果 docker 版本不在经过验证的 docker 版本列表中
- 如果使用其他 cri 引擎:
- [错误] 如果 crictl 套接字未应答
- 如果在 linux上运行:
- [错误] 如果用户不是 root 用户
- [错误] 如果机器主机名不是有效的 DNS 子域
- [警告] 如果通过网络查找无法访问主机名
- [错误] 如果 kubelet 版本低于 kubeadm 支持的最低 kubelet 版本(当前小版本 -1)
- [错误] 如果 kubelet 版本比所需的控制平面板版本至少高一个小(不支持的版本偏斜)
- [警告] 如果 kubelet 服务不存在或已被禁用
- [警告] 如果 firewalld 处于活动状态
- [错误] 如果 API 服务器绑定的端口或 10250/10251/10252 端口已被占用
- [错误] 如果
/etc/kubernetes/manifest
文件夹已经存在并且不为空 - [错误] 如果
/proc/sys/net/bridge/bridge-nf-call-iptables
文件不存在或不包含 1 - [错误] 如果建议地址是 ipv6,并且
/proc/sys/net/bridge/bridge-nf-call-ip6tables
不存在或不包含 1 - [错误] 如果启用了交换分区
- [错误] 如果命令路径中没有
conntrack
、ip
、iptables
、mount
、nsenter
命令 - [警告] 如果命令路径中没有
ebtables
、ethtool
、socat
、tc
、touch
、crictl
命令 - [警告] 如果 API 服务器、控制器管理器、调度程序的其他参数标志包含一些无效选项
- [警告] 如果与 https://API.AdvertiseAddress:API.BindPort 的连接通过代理
- [警告] 如果服务子网的连接通过代理(仅检查第一个地址)
- [警告] 如果 Pod 子网的连接通过代理(仅检查第一个地址)
- 如果提供了外部 etcd:
- [错误] 如果 etcd 版本低于最低要求版本
- [错误] 如果指定了 etcd 证书或密钥,但无法找到
- 如果未提供外部 etcd(因此将安装本地 etcd):
- [错误] 如果端口 2379 已被占用
- [错误] 如果 Etcd.DataDir 文件夹已经存在并且不为空
- 如果授权模式为 ABAC:
- [错误] 如果 abac_policy.json 不存在
- 如果授权方式为 Webhook
- [错误] 如果 webhook_authz.conf 不存在
请注意:
- 可以使用
kubeadm init phase preflight
命令单独触发预检。
生成必要的证书
Kubeadm 生成用于不同目的的证书和私钥对:
-
Kubernetes 集群的自签名证书颁发机构会保存到
ca.crt
文件和ca.key
私钥文件中 -
用于 API 服务器的服务证书,使用
ca.crt
作为 CA 生成,并将证书保存到apiserver.crt
文件中,私钥保存到apiserver.key
文件中 该证书应包含以下备用名称:- Kubernetes 服务的内部 clusterIP(服务 CIDR 的第一个地址。
例如:如果服务的子网是
10.96.0.0/12
,则为10.96.0.1
) - Kubernetes DNS 名称,例如:如果
--service-dns-domain
标志值是cluster.local
, 则为kubernetes.default.svc.cluster.local
; 加上默认的 DNS 名称kubernetes.default.svc
、kubernetes.default
和kubernetes
, - 节点名称
--apiserver-advertise-address
- 用户指定的其他备用名称
- Kubernetes 服务的内部 clusterIP(服务 CIDR 的第一个地址。
例如:如果服务的子网是
-
用于 API 服务器安全连接到 kubelet 的客户端证书,使用
ca.crt
作为 CA 生成, 并保存到apiserver-kubelet-client.crt
,私钥保存到apiserver-kubelet-client.key
文件中。该证书应该在system:masters
组织中。 -
用于签名 ServiceAccount 令牌的私钥保存到
sa.key
文件中,公钥保存到sa.pub
文件中 -
用于前端代理的证书颁发机构保存到
front-proxy-ca.crt
文件中,私钥保存到front-proxy-ca.key
文件中 -
前端代理客户端的客户端证书,使用
front-proxy-ca.crt
作为 CA 生成,并保存到front-proxy-client.crt
文件中,私钥保存到front-proxy-client.key
文件中
证书默认情况下存储在 /etc/kubernetes/pki
中,但是该目录可以使用 --cert-dir
标志进行配置。
请注意:
- 如果证书和私钥对都存在,并且其内容经过评估符合上述规范,将使用现有文件,
并且跳过给定证书的生成阶段。
这意味着用户可以将现有的 CA 复制到
/etc/kubernetes/pki/ca.{crt,key}
, kubeadm 将使用这些文件对其余证书进行签名。 请参阅使用自定义证书。 - 仅对 CA 来说,如果所有其他证书和 kubeconfig 文件都已就位,则可以只提供
ca.crt
文件, 而不提供ca.key
文件。 kubeadm 能够识别出这种情况并启用 ExternalCA,这也意味着了控制器管理器中的csrsigner
控制器将不会启动 - 如果 kubeadm 在 外部 CA 模式 下运行,所有证书必须由用户提供,因为 kubeadm 无法自行生成它们。
- 如果在
--dry-run
模式下执行 kubeadm,证书文件将写入一个临时文件夹中 - 可以使用
kubeadm init phase certs all
命令单独生成证书。
为控制平面组件生成 kubeconfig 文件
Kubeadm 生成具有用于控制平面组件身份标识的 kubeconfig 文件:
-
供 kubelet 在 TLS 引导期间使用的 kubeconfig 文件 ——
/etc/kubernetes/bootstrap-kubelet.conf
。 在此文件中,有一个引导令牌或内嵌的客户端证书,向集群表明此节点身份。 此客户端证书应:- 根据节点鉴权模块的要求,属于
system:nodes
组织 - 具有通用名称(CN):
system:node:<小写主机名>
- 根据节点鉴权模块的要求,属于
-
控制器管理器的 kubeconfig 文件 ——
/etc/kubernetes/controller-manager.conf
; 在此文件中嵌入了一个具有控制器管理器身份标识的客户端证书。 此客户端证书应具有 CN:system:kube-controller-manager
, 该 CN 由 RBAC 核心组件角色 默认定义的。 -
调度器的 kubeconfig 文件 ——
/etc/kubernetes/scheduler.conf
; 此文件中嵌入了具有调度器身份标识的客户端证书。此客户端证书应具有 CN:system:kube-scheduler
, 该 CN 由 RBAC 核心组件角色 默认定义的。
另外,用于 kubeadm 本身和 admin 的 kubeconfig 文件也被生成并保存到
/etc/kubernetes/admin.conf
文件中。
此处的 admin 定义为正在管理集群并希望完全控制集群(root)的实际人员。
内嵌的 admin 客户端证书应是 system:masters
组织的成员,
这一组织名由默认的 RBAC 面向用户的角色绑定
定义。它还应包括一个 CN。kubeadm 使用 kubernetes-admin
CN。
请注意:
ca.crt
证书内嵌在所有 kubeconfig 文件中。- 如果给定的 kubeconfig 文件存在且其内容经过评估符合上述规范,则 kubeadm 将使用现有文件, 并跳过给定 kubeconfig 的生成阶段
- 如果 kubeadm 以 ExternalCA 模式 运行,则所有必需的 kubeconfig 也必须由用户提供,因为 kubeadm 不能自己生成
- 如果在
--dry-run
模式下执行 kubeadm,则 kubeconfig 文件将写入一个临时文件夹中 - 可以使用
kubeadm init phase kubeconfig all
命令分别生成 kubeconfig 文件。
为控制平面组件生成静态 Pod 清单
Kubeadm 将用于控制平面组件的静态 Pod 清单文件写入 /etc/kubernetes/manifests
目录。
Kubelet 启动后会监视这个目录以便创建 Pod。
静态 Pod 清单有一些共同的属性:
-
所有静态 Pod 都部署在
kube-system
名字空间 -
所有静态 Pod 都打上
tier:ontrol-plane
和component:{组件名称}
标签 -
所有静态 Pod 均使用
system-node-critical
优先级 -
所有静态 Pod 都设置了
hostNetwork:true
,使得控制平面在配置网络之前启动;结果导致:- 控制器管理器和调度器用来调用 API 服务器的地址为 127.0.0.1。
- 如果使用本地 etcd 服务器,则
etcd-servers
地址将设置为127.0.0.1:2379
-
同时为控制器管理器和调度器启用了领导者选举
-
控制器管理器和调度器将引用 kubeconfig 文件及其各自的唯一标识
-
如将自定义参数传递给控制平面组件 中所述,所有静态 Pod 都会获得用户指定的额外标志
-
所有静态 Pod 都会获得用户指定的额外卷(主机路径)
请注意:
- 所有镜像默认从 k8s.gcr.io 拉取。 关于自定义镜像仓库,请参阅 使用自定义镜像。
- 如果在
--dry-run
模式下执行 kubeadm,则静态 Pod 文件写入一个临时文件夹中。 - 可以使用
kubeadm init phase control-plane all
命令分别生成主控组件的静态 Pod 清单。
API 服务器
API 服务器的静态 Pod 清单会受到用户提供的以下参数的影响:
- 要绑定的
apiserver-advertise-address
和apiserver-bind-port
; 如果未提供,则这些值默认为机器上默认网络接口的 IP 地址和 6443 端口。 service-cluster-ip-range
给 service 使用- 如果指定了外部 etcd 服务器,则应指定
etcd-servers
地址和相关的 TLS 设置 (etcd-cafile
,etcd-certfile
,etcd-keyfile
); 如果未提供外部 etcd 服务器,则将使用本地 etcd(通过主机网络) - 如果指定了云提供商,则配置相应的
--cloud-provider
,如果该路径存在,则配置--cloud-config
(这是实验性的,是 Alpha 版本,将在以后的版本中删除)
无条件设置的其他 API 服务器标志有:
--insecure-port=0
禁止到 API 服务器不安全的连接--enable-bootstrap-token-auth=true
启用BootstrapTokenAuthenticator
身份验证模块。 更多细节请参见 TLS 引导。--allow-privileged
设为true
(诸如 kube-proxy 这些组件有此要求)--requestheader-client-ca-file
设为front-proxy-ca.crt
--enable-admission-plugins
设为:NamespaceLifecycle
例如,避免删除系统保留的名字空间LimitRanger
和ResourceQuota
对名字空间实施限制ServiceAccount
实施服务账户自动化PersistentVolumeLabel
将区域(Region)或区(Zone)标签附加到由云提供商定义的 PersistentVolumes (此准入控制器已被弃用并将在以后的版本中删除)。 如果未明确选择使用gce
或aws
作为云提供商,则默认情况下,v1.9 以后的版本 kubeadm 都不会部署。DefaultStorageClass
在PersistentVolumeClaim
对象上强制使用默认存储类型DefaultTolerationSeconds
NodeRestriction
限制 kubelet 可以修改的内容(例如,仅此节点上的 pod)
-
--kubelet-preferred-address-types
设为InternalIP,ExternalIP,Hostname;
这使得在节点的主机名无法解析的环境中,kubectl log
和 API 服务器与 kubelet 的其他通信可以工作 -
使用在前面步骤中生成的证书的标志:
--client-ca-file
设为ca.crt
--tls-cert-file
设为apiserver.crt
--tls-private-key-file
设为apiserver.key
--kubelet-client-certificate
设为apiserver-kubelet-client.crt
--kubelet-client-key
设为apiserver-kubelet-client.key
--service-account-key-file
设为sa.pub
--requestheader-client-ca-file
设为front-proxy-ca.crt
--proxy-client-cert-file
设为front-proxy-client.crt
--proxy-client-key-file
设为front-proxy-client.key
-
其他用于保护前端代理( API 聚合层) 通信的标志:
--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-allowed-names=front-proxy-client
控制器管理器
控制器管理器的静态 Pod 清单受用户提供的以下参数的影响:
- 如果调用 kubeadm 时指定了
--pod-network-cidr
参数,则可以通过以下方式启用 某些 CNI 网络插件所需的子网管理器功能:- 设置
--allocate-node-cidrs=true
- 根据给定 CIDR 设置
--cluster-cidr
和--node-cidr-mask-size
标志
- 设置
- 如果指定了云提供商,则指定相应的
--cloud-provider
,如果存在这样的配置文件, 则指定--cloud-config
路径(此为试验性功能,是 Alpha 版本,将在以后的版本中删除)。
其他无条件设置的标志包括:
-
--controllers
为 TLS 引导程序启用所有默认控制器以及BootstrapSigner
和TokenCleaner
控制器。详细信息请参阅 TLS 引导 -
--use-service-account-credentials
设为true
-
使用先前步骤中生成的证书的标志:
-
--root-ca-file
设为ca.crt
- 如果禁用了 External CA 模式,则
--cluster-signing-cert-file
设为ca.crt
,否则设为""
- 如果禁用了 External CA 模式,则
--cluster-signing-key-file
设为ca.key
,否则设为""
--service-account-private-key-file
设为sa.key
- 如果禁用了 External CA 模式,则
调度器
调度器的静态 Pod 清单不受用户提供的参数的影响。
为本地 etcd 生成静态 Pod 清单
如果用户指定了外部 etcd,则将跳过此步骤,否则 kubeadm 会生成静态 Pod 清单文件, 以创建在 Pod 中运行的具有以下属性的本地 etcd 实例:
- 在
localhost:2379
上监听并使用HostNetwork=true
- 将
hostPath
从dataDir
挂载到主机的文件系统 - 用户指定的任何其他标志
请注意:
- etcd 镜像默认从
k8s.gcr.io
拉取。有关自定义镜像仓库,请参阅 使用自定义镜像。 - 如果 kubeadm 以
--dry-run
模式执行,etcd 静态 Pod 清单将写入一个临时文件夹。 - 可以使用 'kubeadm init phase etcd local' 命令单独为本地 etcd 生成静态 Pod 清单
等待控制平面启动
kubeadm 等待(最多 4m0s),直到 localhost:6443/healthz
(kube-apiserver 存活)返回 ok
。
但是为了检测死锁条件,如果 localhost:10255/healthz
(kubelet 存活)或
localhost:10255/healthz/syncloop
(kubelet 就绪)未能在 40s 和 60s 内未返回 ok
,
则 kubeadm 会快速失败。
kubeadm 依靠 kubelet 拉取控制平面镜像并将其作为静态 Pod 正确运行。 控制平面启动后,kubeadm 将完成以下段落中描述的任务。
将 kubeadm ClusterConfiguration 保存在 ConfigMap 中以供以后参考
kubeadm 将传递给 kubeadm init
的配置保存在 kube-system
名字空间下名为
kubeadm-config
的 ConfigMap 中。
这将确保将来执行的 kubeadm 操作(例如 kubeadm upgrade
)将能够确定实际/当前集群状态,
并根据该数据做出新的决策。
请注意:
- 在保存 ClusterConfiguration 之前,从配置中删除令牌等敏感信息。
- 可以使用
kubeadm init phase upload-config
命令单独上传主控节点配置。
将节点标记为控制平面
一旦控制平面可用,kubeadm 将执行以下操作:
- 给节点打上
node-role.kubernetes.io/master=""
标签,标记其为控制平面 - 给节点打上
node-role.kubernetes.io/master:NoSchedule
污点
请注意:
- 可以使用
kubeadm init phase mark-control-plane
命令单独触发控制平面标记
为即将加入的节点加入 TLS 启动引导
Kubeadm 使用引导令牌认证 将新节点连接到现有集群; 更多的详细信息,请参见 设计提案。
kubeadm init
确保为该过程正确配置了所有内容,这包括以下步骤以及设置 API 服务器
和控制器标志,如前几段所述。
请注意:
- 可以使用
kubeadm init phase bootstrap-token
命令配置节点的 TLS 引导,执行以下段落中描述的所有配置步骤; 或者每个步骤都单独触发。
创建引导令牌
kubeadm init
创建第一个引导令牌,该令牌是自动生成的或由用户提供的 --token
标志的值;如引导令牌规范中记录的那样,
令牌应保存在 kube-system
名字空间下名为 bootstrap-token-<令牌-id>
的 Secret 中。
请注意:
- 由
kubeadm init
创建的默认令牌将用于在 TLS 引导过程中验证临时用户; 这些用户会成为system:bootstrappers:kubeadm:default-node-token
组的成员。 - 令牌的有效期有限,默认为 24 小时(间隔可以通过
-token-ttl
标志进行更改) - 可以使用
kubeadm token
命令创建其他令牌,这些令牌还提供其他有用的令牌管理功能
允许加入的节点调用 CSR API
Kubeadm 确保 system:bootstrappers:kubeadm:default-node-token
组中的用户
能够访问证书签名 API。
这是通过在上述组与默认 RBAC 角色 system:node-bootstrapper
之间创建名为
kubeadm:kubelet-bootstrap
的 ClusterRoleBinding 来实现的。
为新的引导令牌设置自动批准
Kubeadm 确保 csrapprover 控制器自动批准引导令牌的 CSR 请求。
这是通过在 system:bootstrappers:kubeadm:default-node-token
用户组和
system:certificates.k8s.io:certificatesigningrequests:nodeclient
默认角色之间
创建名为 kubeadm:node-autoapprove-bootstrap
的 ClusterRoleBinding 来实现的。
还应创建 system:certificates.k8s.io:certificatesigningrequests:nodeclient
角色,
授予对 /apis/certificates.k8s.io/certificatesigningrequests/nodeclient
执行 POST 的权限。
通过自动批准设置节点证书轮换
Kubeadm 确保节点启用了证书轮换,csrapprover 控制器将自动批准节点的 新证书的 CSR 请求。
这是通过在 system:nodes
组和
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
默认角色之间创建名为 kubeadm:node-autoapprove-certificate-rotation
的
ClusterRoleBinding 来实现的。
创建公共 cluster-info ConfigMap
本步骤在 kube-public
名字空间中创建名为 cluster-info
的 ConfigMap。
另外,它创建一个 Role 和一个 RoleBinding,为未经身份验证的用户授予对 ConfigMap
的访问权限(即 RBAC 组 system:unauthenticated
中的用户)。
请注意:
- 对
cluster-info
ConfigMap 的访问 不受 速率限制。 如果你把 API 服务器暴露到外网,这可能是一个问题,也可能不是; 这里最坏的情况是 DoS 攻击,攻击者使用 kube-apiserver 能够处理的所有动态请求 来为cluster-info
ConfigMap 提供服务。
安装插件
Kubeadm 通过 API 服务器安装内部 DNS 服务器和 kube-proxy 插件。
请注意:
- 此步骤可以调用 'kubeadm init phase addon all' 命令单独执行。
代理
在 kube-system
名字空间中创建一个用于 kube-proxy
的 ServiceAccount;
然后以 DaemonSet 的方式部署 kube-proxy:
- 主控节点凭据(
ca.crt
和token
)来自 ServiceAccount - API 服务器节点的位置(URL)来自 ConfigMap
kube-proxy
的 ServiceAccount 绑定了system:node-proxier
ClusterRole 中的特权
DNS
- CoreDNS 服务的名称为
kube-dns
。这样做是为了防止当用户将集群 DNS 从 kube-dns 切换到 CoreDNS 时出现服务中断。--config
方法在 这里 有描述。 - 在
kube-system
名字空间中创建 CoreDNS 的 ServiceAccount coredns
的 ServiceAccount 绑定了system:coredns
ClusterRole 中的特权
在 Kubernetes 1.21 版本中,kubeadm 对 kube-dns
的支持被移除。
你可以在 kubeadm 使用 CoreDNS,即使相关的 Service 名字仍然是 kube-dns
。
kubeadm join 步骤内部设计
与 kubeadm init
类似,kubeadm join
内部工作流由一系列待执行的原子工作任务组成。
这分为发现(让该节点信任 Kubernetes 的主控节点)和 TLS 引导 (让 Kubernetes 的主控节点信任该节点)。
请参阅使用引导令牌进行身份验证 或相应的设计提案。
预检
kubeadm
在开始执行之前执行一组预检,目的是验证先决条件,避免常见的集群启动问题。
请注意:
kubeadm join
预检基本上是kubeadm init
预检的一个子集- 从 1.9 开始,kubeadm 为 CRI 通用的功能提供了更好的支持;在这种情况下, Docker 特定的控制参数将跳过或替换为 crictl 中与之相似的控制参数。
- 从 1.9 开始,kubeadm 支持加入在 Windows 上运行的节点;在这种情况下, 将跳过 Linux 特定的控制参数。
- 在任何情况下,用户都可以通过
--ignore-preflight-errors
选项跳过 特定的预检(或者进而跳过所有预检)。
发现 cluster-info
主要有两种发现方案。第一种是使用一个共享令牌以及 API 服务器的 IP 地址。 第二种是提供一个文件(它是标准 kubeconfig 文件的子集)。
共享令牌发现
如果带 --discovery-token
参数调用 kubeadm join
,则使用了令牌发现功能;
在这种情况下,节点基本上从 kube-public
名字空间中的 cluster-info
ConfigMap
中检索集群 CA 证书。
为了防止“中间人”攻击,采取了以下步骤:
- 首先,通过不安全连接检索 CA 证书(这是可能的,因为
kubeadm init
授予system:unauthenticated
的用户对cluster-info
访问权限) - 然后 CA 证书通过以下验证步骤:
- 基本验证:使用令牌 ID 而不是 JWT 签名
- 公钥验证:使用提供的
--discovery-token-ca-cert-hash
。这个值来自kubeadm init
的输出, 或者可以使用标准工具计算(哈希值是按 RFC7469 中主体公钥信息(SPKI)对象的字节计算的)--discovery-token-ca-cert-hash
标志可以重复多次,以允许多个公钥。 - 作为附加验证,通过安全连接检索 CA 证书,然后与初始检索的 CA 进行比较
请注意:
- 通过
--discovery-token-unsafe-skip-ca-verification
标志可以跳过公钥验证; 这削弱了 kubeadm 安全模型,因为其他人可能冒充 Kubernetes 主控节点。
文件/HTTPS 发现
如果带 --discovery-file
参数调用 kubeadm join
,则使用文件发现功能;
该文件可以是本地文件或通过 HTTPS URL 下载;对于 HTTPS,主机安装的 CA 包
用于验证连接。
通过文件发现,集群 CA 证书是文件本身提供;事实上,这个发现文件是一个 kubeconfig 文件,
只设置了 server
和 certificate-authority-data
属性,
如 kubeadm join
参考文档中所述,当与集群建立连接时,kubeadm 尝试访问 cluster-info
ConfigMap,
如果可用,就使用它。
TLS 引导
知道集群信息后,kubeadm 将写入文件 bootstrap-kubelet.conf
,从而允许 kubelet 执行
TLS 引导。
TLS 引导机制使用共享令牌对 Kubernetes API 服务器进行临时身份验证,以便 为本地创建的密钥对提交证书签名请求(CSR)。
该请求会被自动批准,并且该操作保存 ca.crt
文件和 kubelet.conf
文件,用于
kubelet 加入集群,同时删除 bootstrap-kubelet.conf
。
请注意:
- 临时身份验证根据
kubeadm init
过程中保存的令牌进行验证(或者使用kubeadm token
创建的其他令牌) - 临时身份验证解析到
system:bootstrappers:kubeadm:default-node-token
组的一个用户成员, 该成员在kubeadm init
过程中被授予对 CSR API 的访问权 - 根据
kubeadm init
过程的配置,自动 CSR 审批由 csrapprover 控制器管理