【kubernetes v1.21】(七)kubeadm 组件超深度分析
kubeadm 组件超深度分析
基于
github.com/kubernetes/cmd/kubeadm/源码逐行解读
总计约 312 个 Go 文件(含测试),非测试文件约 170+ 个
一、模块定位
1.1 业务职责
kubeadm 是 Kubernetes 官方集群引导工具,核心职责是将一个裸机环境转变为一个运行中的 Kubernetes 集群。具体而言:
- 初始化控制平面(
kubeadm init):在首节点上引导出完整的 Kubernetes 控制平面(API Server、Controller Manager、Scheduler、etcd),包括 PKI 证书体系、kubeconfig 文件、Static Pod 清单等 - 节点加入集群(
kubeadm join):worker 节点或额外控制平面节点通过 Bootstrap Token 和 TLS Bootstrap 机制安全加入已有集群 - 集群升级(
kubeadm upgrade):滚动升级控制平面组件和 kubelet 版本 - 证书管理(
kubeadm certs):生成、续期、检查集群 PKI 资产 - 配置管理(
kubeadm config):打印默认配置、上传/查看集群配置 - 令牌管理(
kubeadm token):创建/删除/列出 Bootstrap Token - 重置节点(
kubeadm reset):回滚节点到未加入集群状态
1.2 在系统中的位置
kubeadm 在 Kubernetes 生态中处于基础设施层,位于底层容器运行时(containerd/Docker)与上层 Kubernetes 控制平面之间:
┌─────────────────────────────────────────────┐
│ 用户 / 运维工具 │
├─────────────────────────────────────────────┤
│ kubeadm CLI │ ← 本分析对象
├─────────────────────────────────────────────┤
│ kubelet / 容器运行时 (CRI) │
├─────────────────────────────────────────────┤
│ 操作系统 (Linux/Windows) │
└─────────────────────────────────────────────┘
kubeadm 不负责:网络插件安装(CNI)、持久化存储配置、负载均衡器部署。它专注于将控制平面组件以 Static Pod 方式拉起,完成安全引导的"最后一公里"。
二、模块整体结构
2.1 目录树与包组织
cmd/kubeadm/
├── kubeadm.go # 程序入口 main()
├── app/
│ ├── kubeadm.go # Run() 入口函数:初始化 flag、创建 cobra 根命令
│ ├── cmd/ # CLI 命令层(cobra 命令定义)
│ │ ├── cmd.go # NewKubeadmCommand():注册所有子命令
│ │ ├── init.go # kubeadm init 命令定义 + initData
│ │ ├── join.go # kubeadm join 命令定义 + joinData
│ │ ├── reset.go # kubeadm reset 命令
│ │ ├── config.go # kubeadm config 子命令
│ │ ├── token.go # kubeadm token 子命令(create/list/delete/generate)
│ │ ├── certs.go # kubeadm certs 子命令
│ │ ├── version.go # kubeadm version
│ │ ├── completion.go # shell 补全
│ │ ├── upgrade/ # kubeadm upgrade 子命令(plan/apply/node/diff)
│ │ ├── alpha/ # 实验性子命令
│ │ ├── options/ # 通用 CLI 选项常量和 flag 定义
│ │ ├── phases/ # 阶段子命令定义层
│ │ │ ├── workflow/ # ★ 核心:workflow 引擎(Runner/Phase)
│ │ │ ├── init/ # init 各阶段的 Phase 定义
│ │ │ ├── join/ # join 各阶段的 Phase 定义
│ │ │ ├── reset/ # reset 各阶段的 Phase 定义
│ │ │ └── upgrade/ # upgrade node 各阶段的 Phase 定义
│ │ └── util/ # CLI 工具函数
│ ├── apis/ # API 类型定义
│ │ ├── kubeadm/ # 内部 API 类型
│ │ │ ├── types.go # InitConfiguration, ClusterConfiguration, JoinConfiguration 等
│ │ │ ├── register.go # Scheme 注册
│ │ │ ├── bootstraptokenstring.go # Token 字符串类型
│ │ │ ├── bootstraptokenhelpers.go # Token 辅助函数
│ │ │ ├── apiendpoint.go # APIEndpoint 类型
│ │ │ ├── scheme/ # Scheme 单例
│ │ │ ├── v1beta1/ # v1beta1 外部版本 API
│ │ │ ├── v1beta2/ # v1beta2 外部版本 API(当前默认)
│ │ │ ├── validation/ # 配置验证逻辑
│ │ │ └── fuzzer/ # 模糊测试
│ │ └── output/ # 输出 API 类型
│ │ ├── types.go
│ │ └── v1alpha1/
│ ├── phases/ # ★ 核心业务逻辑层
│ │ ├── certs/ # 证书生成与管理
│ │ │ ├── certs.go # CreatePKIAssets, CreateCACertAndKeyFiles 等
│ │ │ ├── certlist.go # KubeadmCert 类型定义 + 证书清单
│ │ │ └── renewal/ # 证书续期
│ │ ├── kubeconfig/ # kubeconfig 文件生成
│ │ │ └── kubeconfig.go
│ │ ├── controlplane/ # 控制平面 Static Pod 清单生成
│ │ │ ├── manifests.go # GetStaticPodSpecs, CreateStaticPodFiles
│ │ │ └── volumes.go # HostPath 卷定义
│ │ ├── etcd/ # etcd Static Pod 管理
│ │ │ └── local.go # 本地 etcd 集群初始化/扩容
│ │ ├── kubelet/ # kubelet 启动/重启/配置
│ │ │ ├── kubelet.go # TryStartKubelet, TryStopKubelet
│ │ │ ├── config.go # kubelet 配置写入
│ │ │ └── flags.go # kubelet 命令行参数
│ │ ├── uploadconfig/ # 上传集群配置到 ConfigMap
│ │ │ └── uploadconfig.go
│ │ ├── copycerts/ # 证书上传/下载(HA 控制平面)
│ │ │ └── copycerts.go # UploadCerts, DownloadCerts
│ │ ├── bootstraptoken/ # Bootstrap Token 管理
│ │ │ ├── node/ # 节点 Token 创建
│ │ │ └── clusterinfo/ # cluster-info ConfigMap
│ │ ├── markcontrolplane/ # 控制平面标签/污点
│ │ ├── patchnode/ # 节点注解打补丁
│ │ ├── addons/ # 附加组件(CoreDNS、kube-proxy)
│ │ │ ├── dns/ # CoreDNS
│ │ │ └── proxy/ # kube-proxy
│ │ └── upgrade/ # 升级逻辑
│ │ ├── compute.go # 版本计算
│ │ ├── policy.go # 升级策略
│ │ ├── staticpods.go # Static Pod 升级
│ │ ├── health.go # 健康检查
│ │ └── postupgrade.go # 升级后处理
│ ├── preflight/ # 预检查
│ │ ├── checks.go # Checker 接口 + 各种检查实现
│ │ ├── checks_linux.go # Linux 特有检查
│ │ └── utils.go
│ ├── discovery/ # 集群发现机制
│ │ ├── discovery.go # For() 入口
│ │ ├── token/ # Token 发现
│ │ ├── file/ # 文件发现
│ │ └── https/ # HTTPS 发现
│ ├── componentconfigs/ # 组件配置(kubelet、kube-proxy)
│ ├── constants/ # 常量定义
│ │ └── constants.go # 路径、端口、超时、证书名等
│ ├── features/ # Feature Gate 定义
│ ├── images/ # 镜像拉取工具
│ └── util/ # 工具函数
│ ├── apiclient/ # API 客户端封装(幂等操作、dry-run、wait)
│ ├── pkiutil/ # PKI 工具(证书生成/加载/验证)
│ ├── kubeconfig/ # kubeconfig 工具
│ ├── staticpod/ # Static Pod 工具
│ ├── etcd/ # etcd 客户端工具
│ ├── config/ # 配置加载/解析
│ ├── crypto/ # 加密工具
│ └── ...
└── test/ # 测试辅助
2.2 核心接口与依赖注入
2.2.1 workflow.RunData 接口体系
kubeadm 采用 Phase Workflow 模式,通过 RunData 接口实现阶段间数据共享与依赖注入:
// Runner 引擎使用 RunData = interface{} 作为数据传递载体
type RunData = interface{}
// InitData 接口(init 命令使用)
type InitData interface {
UploadCerts() bool
CertificateKey() string
SetCertificateKey(key string)
SkipCertificateKeyPrint() bool
Cfg() *kubeadmapi.InitConfiguration
DryRun() bool
SkipTokenPrint() bool
IgnorePreflightErrors() sets.String
CertificateWriteDir() string
CertificateDir() string
KubeConfigDir() string
KubeConfigPath() string
ManifestDir() string
KubeletDir() string
ExternalCA() bool
OutputWriter() io.Writer
Client() (clientset.Interface, error)
Tokens() []string
PatchesDir() string
}
initData 结构体实现 InitData,持有运行时上下文:
cfg *kubeadmapi.InitConfiguration:核心配置client clientset.Interface:Kubernetes API 客户端(懒初始化单例)dryRun bool:干跑模式externalCA bool:外部 CA 标记- 各种路径(certificatesDir, kubeconfigDir, manifestDir 等)
joinData 类似地实现 JoinData 接口,额外持有:
tlsBootstrapCfg *clientcmdapi.Config:TLS 引导 kubeconfiginitCfg *kubeadmapi.InitConfiguration:从集群拉取的 Init 配置clientSet *clientset.Clientset:API 客户端
2.2.2 workflow.Phase 结构体
type Phase struct {
Name string // 阶段名称
Aliases []string // 别名
Short string // 短描述
Long string // 长描述
Example string // 示例
Hidden bool // 是否隐藏
Phases []Phase // 嵌套子阶段
RunAllSiblings bool // 是否运行所有同级阶段
Run func(data RunData) error // 执行函数
RunIf func(data RunData) (bool, error) // 条件执行
InheritFlags []string // 从父命令继承的 flag
LocalFlags *pflag.FlagSet // 本地 flag
ArgsValidator cobra.PositionalArgs // 参数验证器
}
2.2.3 KubeadmCert 证书描述器
type KubeadmCert struct {
Name string
LongName string
BaseName string
CAName string
configMutators []configMutatorsFunc // 运行时配置修改器
config pkiutil.CertConfig // 基础证书配置
}
证书系统采用树形结构:CertificateTree map[*KubeadmCert]Certificates,CA 证书作为根,叶子证书由 CA 签发。
2.3 核心方法清单
| 包 | 方法 | 作用 |
|---|---|---|
app |
Run() |
初始化日志/flag,创建根命令并执行 |
cmd |
NewKubeadmCommand() |
注册 init/join/reset/token/certs/config/upgrade/alpha/version 子命令 |
cmd |
newCmdInit() |
定义 init 命令,注册 13 个 Phase |
cmd |
newCmdJoin() |
定义 join 命令,注册 5 个 Phase |
cmd |
newInitData() |
校验 flag → 加载/合并配置 → 返回 initData |
cmd |
newJoinData() |
校验 flag → 发现配置 → 返回 joinData |
workflow |
Runner.Run() |
遍历 Phase 列表,按序执行 |
workflow |
Runner.BindToCommand() |
为 Phase 生成 cobra 子命令 |
certs |
CreatePKIAssets() |
创建完整 PKI 证书树 |
certs |
CertificateTree.CreateTree() |
按 CA→leaf 顺序创建证书 |
kubeconfig |
CreateKubeConfigFile() |
生成单个 kubeconfig |
kubeconfig |
getKubeConfigSpecs() |
构建 4 个 kubeconfig 规格 |
controlplane |
GetStaticPodSpecs() |
生成 3 个控制平面 Pod 规格 |
controlplane |
CreateStaticPodFiles() |
写 Static Pod 清单到磁盘 |
etcd |
CreateLocalEtcdStaticPodManifestFile() |
生成初始 etcd Static Pod |
etcd |
CreateStackedEtcdStaticPodManifestFile() |
扩容 etcd 成员 |
copycerts |
UploadCerts() |
加密上传证书到 Secret |
copycerts |
DownloadCerts() |
下载解密证书 |
uploadconfig |
UploadConfiguration() |
上传 ClusterConfiguration 到 ConfigMap |
bootstraptoken |
UpdateOrCreateTokens() |
创建/更新 Bootstrap Token Secret |
discovery |
For() |
集群发现入口 → 返回 TLS Bootstrap kubeconfig |
preflight |
RunInitNodeChecks() |
执行 init 预检查 |
pkiutil |
NewCertificateAuthority() |
生成 CA 证书+密钥 |
pkiutil |
NewCertAndKey() |
由 CA 签发证书+密钥 |
2.4 内部调用关系
kubeadm.go:main()
└→ app.Run()
└→ cmd.NewKubeadmCommand()
├→ newCmdInit()
│ └→ workflow.Runner
│ ├→ Phase: preflight → preflight.RunInitNodeChecks()
│ ├→ Phase: certs → certs.CreatePKIAssets()
│ │ └→ CertificateTree.CreateTree()
│ │ ├→ pkiutil.NewCertificateAuthority() (CA)
│ │ └→ KubeadmCert.CreateFromCA() (leaf)
│ │ └→ pkiutil.NewCertAndKey()
│ ├→ Phase: kubeconfig → kubeconfig.CreateKubeConfigFile()
│ ├→ Phase: kubelet-start → kubelet.WriteKubeletConfig()
│ ├→ Phase: control-plane → controlplane.CreateStaticPodFiles()
│ │ └→ GetStaticPodSpecs()
│ ├→ Phase: etcd → etcd.CreateLocalEtcdStaticPodManifestFile()
│ ├→ Phase: wait-control-plane → wait for API Server
│ ├→ Phase: upload-config → uploadconfig.UploadConfiguration()
│ ├→ Phase: upload-certs → copycerts.UploadCerts()
│ ├→ Phase: mark-control-plane → markcontrolplane.MarkControlPlane()
│ ├→ Phase: bootstrap-token → bootstraptoken.UpdateOrCreateTokens()
│ ├→ Phase: kubelet-finalize → kubelet 配置收尾
│ └→ Phase: addons → dns + proxy 安装
├→ newCmdJoin()
│ └→ workflow.Runner
│ ├→ Phase: preflight → preflight.RunJoinNodeChecks()
│ ├→ Phase: control-plane-prepare → download-certs + certs + kubeconfig + control-plane
│ ├→ Phase: check-etcd → etcd.CheckLocalEtcdClusterStatus()
│ ├→ Phase: kubelet-start → kubelet.WriteConfig + 启动
│ └→ Phase: control-plane-join → etcd 扩容 + 标记节点
└→ upgrade/.../...
2.5 数据流入流出
数据流入:
- CLI 参数(
--config,--token,--apiserver-advertise-address等) - 配置文件(YAML 格式
InitConfiguration/JoinConfiguration) - 已有集群的 ConfigMap(
kubeadm-config、cluster-info) - 磁盘上的已有证书/kubeconfig 文件
- 环境变量(
KUBEADM_INIT_DRYRUN_DIR等)
数据流出:
- 磁盘文件:证书(
/etc/kubernetes/pki/)、kubeconfig(/etc/kubernetes/)、Static Pod 清单(/etc/kubernetes/manifests/)、kubelet 配置 - Kubernetes API 对象:Secret(Bootstrap Token、kubeadm-certs)、ConfigMap(kubeadm-config、cluster-info)、RBAC(Role/RoleBinding)
- Node 对象:标签(
node-role.kubernetes.io/master)、注解(kubeadm 相关)、污点
三、核心业务逻辑深度解析
3.1 kubeadm init 完整执行流程
3.1.1 入口路径
// kubeadm.go
func main() {
kubeadmutil.CheckErr(app.Run())
}
// app/kubeadm.go
func Run() error {
klog.InitFlags(nil)
pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Set("logtostderr", "true")
// 隐藏内部 flag...
cmd := cmd.NewKubeadmCommand(os.Stdin, os.Stdout, os.Stderr)
return cmd.Execute()
}
Run() 做了四件事:
- 初始化 klog 日志 flag
- 设置 flag 名规范化函数(将
--foo-bar和--foo_bar等价) - 合并 Go flag 到 pflag
- 创建并执行 cobra 根命令
3.1.2 命令注册
NewKubeadmCommand() 构建命令树:
cmds.AddCommand(newCmdCertsUtility(out))
cmds.AddCommand(newCmdCompletion(out, ""))
cmds.AddCommand(newCmdConfig(out))
cmds.AddCommand(newCmdInit(out, nil))
cmds.AddCommand(newCmdJoin(out, nil))
cmds.AddCommand(newCmdReset(in, out, nil))
cmds.AddCommand(newCmdVersion(out))
cmds.AddCommand(newCmdToken(out, err))
cmds.AddCommand(upgrade.NewCmdUpgrade(out))
cmds.AddCommand(alpha.NewCmdAlpha(in, out))
PersistentPreRunE 支持 --rootfs 参数,在执行任何子命令前 chroot 到指定目录,这在容器化场景中很有用。
3.1.3 init 命令的 Phase 注册
initRunner.AppendPhase(phases.NewPreflightPhase()) // 1. 预检查
initRunner.AppendPhase(phases.NewCertsPhase()) // 2. 证书生成
initRunner.AppendPhase(phases.NewKubeConfigPhase()) // 3. kubeconfig 生成
initRunner.AppendPhase(phases.NewKubeletStartPhase()) // 4. kubelet 启动
initRunner.AppendPhase(phases.NewControlPlanePhase()) // 5. 控制平面清单
initRunner.AppendPhase(phases.NewEtcdPhase()) // 6. etcd 初始化
initRunner.AppendPhase(phases.NewWaitControlPlanePhase()) // 7. 等待控制平面就绪
initRunner.AppendPhase(phases.NewUploadConfigPhase()) // 8. 上传配置
initRunner.AppendPhase(phases.NewUploadCertsPhase()) // 9. 上传证书
initRunner.AppendPhase(phases.NewMarkControlPlanePhase()) // 10. 标记控制平面
initRunner.AppendPhase(phases.NewBootstrapTokenPhase()) // 11. 创建 Bootstrap Token
initRunner.AppendPhase(phases.NewKubeletFinalizePhase()) // 12. kubelet 收尾
initRunner.AppendPhase(phases.NewAddonPhase()) // 13. 安装附加组件
用户可通过 --skip-phases 跳过特定阶段,或通过 kubeadm init phase <phase-name> 单独执行某个阶段。
3.1.4 initData 构建
newInitData() 是 init 命令的核心数据准备函数,逐步解析:
func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io.Writer) (*initData, error) {
// Step 1: 重新应用默认值(设置未通过 flag 暴露的默认值)
kubeadmscheme.Scheme.Default(options.externalInitCfg)
kubeadmscheme.Scheme.Default(options.externalClusterCfg)
// Step 2: 解析 Feature Gates 字符串
options.externalClusterCfg.FeatureGates, err = features.NewFeatureGate(...)
// Step 3: 校验混合参数(不能同时用 --config 和其他 flag 覆盖相同字段)
validation.ValidateMixedArguments(cmd.Flags())
// Step 4: 应用 Bootstrap Token 选项到 InitConfiguration
options.bto.ApplyTo(options.externalInitCfg)
// Step 5: 加载配置文件或合并默认配置
cfg, err := configutil.LoadOrDefaultInitConfiguration(
options.cfgPath, options.externalInitCfg, options.externalClusterCfg)
// Step 6: 为新集群设置 kubelet cgroup driver 为 systemd
componentconfigs.MutateCgroupDriver(&cfg.ClusterConfiguration)
// Step 7: 校验忽略的预检查列表
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(...)
// Step 8: 校验 API Server 绑定地址
configutil.VerifyAPIServerBindAddress(cfg.LocalAPIEndpoint.AdvertiseAddress)
// Step 9: 校验 Feature Gate 版本兼容性
features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion)
// Step 10: 干跑模式创建临时目录
if options.dryRun {
dryRunDir, err = kubeadmconstants.CreateTempDirForKubeadm(...)
}
// Step 11: 检测外部 CA(CA cert 存在但 CA key 不存在)
externalCA, err := certsphase.UsingExternalCA(&cfg.ClusterConfiguration)
// Step 12: 检测外部 Front-Proxy CA
externalFrontProxyCA, err := certsphase.UsingExternalFrontProxyCA(&cfg.ClusterConfiguration)
// Step 13: 外部 CA 模式下禁止 upload-certs
if options.uploadCerts && (externalCA || externalFrontProxyCA) {
return nil, errors.New("can't use upload-certs with external CA")
}
return &initData{...}, nil
}
关键设计决策:
- 外部 CA 检测:通过文件存在性判定——
ca.crt存在但ca.key不存在 → 外部 CA 模式 - cgroup driver 变异:新集群默认使用
systemdcgroup driver,但升级时不改变(避免重启 kubelet 导致已有节点异常) - 混合参数校验:防止用户同时用
--config和 CLI flag 设置同一字段产生歧义
3.1.5 Runner 执行引擎
workflow.Runner 是 kubeadm 的核心编排引擎:
func (e *Runner) Run(args []string) error {
e.prepareForExecution() // 构建 phaseRunner 列表
// 根据FilterPhases/SkipPhases计算哪些阶段需要执行
phaseRunFlags, err := e.computePhaseRunFlags()
// 构建运行时数据
data, err := e.InitData(args)
// 遍历执行
err = e.visitAll(func(p *phaseRunner) error {
if !phaseRunFlags[p.generatedName] { return nil } // 跳过
if p.RunIf != nil {
ok, err := p.RunIf(data) // 条件检查
if !ok { return nil }
}
if p.Run != nil {
return p.Run(data) // 执行阶段
}
return nil
})
return err
}
phaseRunner 包含运行时上下文:
parent *phaseRunner:父阶段level int:嵌套层级generatedName string:全路径名(如control-plane/apiserver)use string:相对路径名
BindToCommand() 将 Phase 树映射为 cobra 子命令树,自动生成 kubeadm init phase control-plane apiserver 等子命令。
3.2 证书管理深度解析
3.2.1 证书清单
kubeadm 生成以下证书体系:
| CA 证书 | 签发的叶子证书 |
|---|---|
ca (Kubernetes Root CA) |
apiserver (服务端证书), apiserver-kubelet-client (客户端证书) |
front-proxy-ca |
front-proxy-client |
etcd/ca |
etcd/server, etcd/peer, etcd/healthcheck-client, apiserver-etcd-client |
另外还有 Service Account 密钥对:sa.key / sa.pub(非 x509 证书,独立处理)。
当使用外部 etcd 时,etcd 相关证书不生成;当使用外部 CA 时,仅生成 CSR 和私钥。
3.2.2 证书树创建流程
func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
// 根据是否有本地 etcd 选择证书列表
var certList Certificates
if cfg.Etcd.Local == nil {
certList = GetCertsWithoutEtcd() // 5 个证书
} else {
certList = GetDefaultCertList() // 10 个证书
}
// 构建证书树:CA → 叶子证书映射
certTree, err := certList.AsMap().CertTree()
// 创建证书树
certTree.CreateTree(cfg)
// 单独处理 Service Account 密钥
CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir, cfg.ClusterConfiguration.PublicKeyAlgorithm())
}
CertTree() 将扁平列表转换为树:
func (m CertificateMap) CertTree() (CertificateTree, error) {
caMap := make(CertificateTree)
for _, cert := range m {
if cert.CAName == "" {
// CA 证书,作为根节点
caMap[cert] = []*KubeadmCert{}
} else {
// 叶子证书,挂到对应 CA 下
ca := m[cert.CAName]
caMap[ca] = append(caMap[ca], cert)
}
}
return caMap, nil
}
CreateTree() 逐个处理 CA:
func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
for ca, leaves := range t {
// 尝试从磁盘加载已有 CA 证书
caCert, err := pkiutil.TryLoadCertFromDisk(ic.CertificatesDir, ca.BaseName)
if err == nil {
// CA 已存在:验证 IsCA → 尝试加载 CA Key
if !caCert.IsCA { return error }
caKey, err := pkiutil.TryLoadKeyFromDisk(...)
if err != nil {
// CA Key 不存在(外部 CA)→ 验证所有叶子证书已存在
for _, leaf := range leaves {
validateSignedCertWithCA(cl, caCert)
}
continue
}
// CA Key 存在 → 用它签发缺失的叶子证书
} else {
// CA 不存在 → 创建新 CA
caCert, caKey, err = pkiutil.NewCertificateAuthority(cfg)
writeCertificateAuthorityFilesIfNotExist(...)
}
// 签发所有叶子证书
for _, leaf := range leaves {
leaf.CreateFromCA(ic, caCert, caKey)
}
}
}
关键逻辑:
- 幂等性:已有证书不会覆盖,先加载验证,匹配则跳过
- 外部 CA 检测:CA cert 存在 + CA key 不存在 → 外部 CA 模式,此时必须所有叶子证书已预置
- 证书验证:检查有效期、SAN、CA 签名链
3.2.3 KubeadmCert 定义示例
// API Server 证书
func KubeadmCertAPIServer() *KubeadmCert {
return &KubeadmCert{
Name: "apiserver",
LongName: "certificate for serving the Kubernetes API",
BaseName: "apiserver",
CAName: "ca", // 由 Kubernetes Root CA 签发
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "kube-apiserver",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
},
configMutators: []configMutatorsFunc{
makeAltNamesMutator(pkiutil.GetAPIServerAltNames), // 运行时填充 SAN
},
}
}
configMutators 是关键——在运行时根据 InitConfiguration 动态修改证书配置。例如 API Server 证书的 SAN 需要包含:AdvertiseAddress、Service CIDR 的第一个 IP、各种 DNS 名称等,这些只能在运行时确定。
3.3 kubeconfig 生成深度解析
kubeadm 生成 4 个 kubeconfig 文件:
| 文件名 | 用户/身份 | 连接目标 | 组织 |
|---|---|---|---|
admin.conf |
kubernetes-admin |
ControlPlaneEndpoint | system:masters |
kubelet.conf |
system:node:<nodename> |
ControlPlaneEndpoint | system:nodes |
controller-manager.conf |
system:kube-controller-manager |
LocalAPIEndpoint | - |
scheduler.conf |
system:kube-scheduler |
LocalAPIEndpoint | - |
注意:admin.conf 和 kubelet.conf 使用 ControlPlaneEndpoint(全局负载均衡地址),而 controller-manager.conf 和 scheduler.conf 使用 LocalAPIEndpoint(本地 API Server 地址),因为控制平面组件应直接连接本地实例以避免负载均衡器单点故障。
生成流程:
func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kubeConfigFileNames ...string) error {
specs, _ := getKubeConfigSpecs(cfg) // 构建规格
for _, name := range kubeConfigFileNames {
spec := specs[name]
config, _ := buildKubeConfigFromSpec(spec, cfg.ClusterName) // 构建 kubeconfig 对象
createKubeConfigFileIfNotExists(outDir, name, config) // 写入磁盘(幂等)
}
}
幂等逻辑:如果文件已存在,验证 CA 和 Server URL 是否匹配,匹配则跳过,不匹配则报错。
3.4 控制平面 Static Pod 生成深度解析
3.4.1 GetStaticPodSpecs
为三个组件构建 Pod 规格:
kube-apiserver:
staticPodSpecs[kubeadmconstants.KubeAPIServer] = staticpodutil.ComponentPod(v1.Container{
Name: "kube-apiserver",
Image: images.GetKubernetesImage("kube-apiserver", cfg),
Command: getAPIServerCommand(cfg, endpoint), // ★ 核心命令构建
VolumeMounts: staticpodutil.VolumeMountMapToSlice(mounts.GetVolumeMounts("kube-apiserver")),
LivenessProbe: staticpodutil.LivenessProbe(..., "/livez", ...),
ReadinessProbe: staticpodutil.ReadinessProbe(..., "/readyz", ...),
StartupProbe: staticpodutil.StartupProbe(...),
Resources: staticpodutil.ComponentResources("250m"),
Env: kubeadmutil.GetProxyEnvVars(),
}, mounts.GetVolumes("kube-apiserver"), annotations)
API Server 命令构建 (getAPIServerCommand):
- 基础参数:
--advertise-address,--secure-port,--enable-admission-plugins=NodeRestriction - 证书路径:
--tls-cert-file,--tls-private-key-file,--client-ca-file,--kubelet-client-certificate等 - etcd 连接:根据 External/Local etcd 选择不同参数
- Front Proxy:
--requestheader-*,--proxy-client-* - Service Account:
--service-account-key-file,--service-account-signing-key-file - 授权模式:默认
Node,RBAC ExtraArgs通过BuildArgumentListFromMap合并,用户可覆盖默认值
kube-controller-manager:
- 默认
--port=0(禁用不安全端口) --bind-address=127.0.0.1(仅本地访问)--leader-elect=true--controllers=*,bootstrapsigner,tokencleaner- 外部 CA 时清空
--cluster-signing-key-file和--cluster-signing-cert-file - 如果设置了 PodSubnet,启用
--allocate-node-cidrs
kube-scheduler:
- 类似配置:
--port=0,--bind-address=127.0.0.1,--leader-elect=true
3.5 etcd 集群初始化深度解析
3.5.1 初始节点(init)
CreateLocalEtcdStaticPodManifestFile 生成单节点 etcd Static Pod:
func getEtcdCommand(cfg, endpoint, nodeName, initialCluster) []string {
defaultArguments := map[string]string{
"name": nodeName,
"listen-client-urls": "http://127.0.0.1:2379,https://<advertise>:2379",
"advertise-client-urls": "https://<advertise>:2379",
"listen-peer-urls": "https://<advertise>:2380",
"initial-advertise-peer-urls": "https://<advertise>:2380",
"data-dir": cfg.Etcd.Local.DataDir,
"cert-file": "etcd/server.crt",
"key-file": "etcd/server.key",
"trusted-ca-file": "etcd/ca.crt",
"client-cert-auth": "true",
"peer-cert-file": "etcd/peer.crt",
"peer-key-file": "etcd/peer.key",
"peer-trusted-ca-file": "etcd/ca.crt",
"peer-client-cert-auth": "true",
}
if len(initialCluster) == 0 {
// init 场景:只有自己
defaultArguments["initial-cluster"] = fmt.Sprintf("%s=https://<advertise>:2380", nodeName)
} else {
// join 场景:已有成员列表
defaultArguments["initial-cluster"] = strings.Join(endpoints, ",")
defaultArguments["initial-cluster-state"] = "existing" // ★ 关键标志
}
}
3.5.2 扩容 etcd 成员(join --control-plane)
CreateStackedEtcdStaticPodManifestFile 是 HA 控制平面加入时的核心:
- 通过 API Server 连接已有 etcd 集群
ListMembers()获取当前成员列表- 检查新成员是否已存在(通过 peer URL 匹配)
- 如果不存在,
AddMember()向集群添加新成员 - 构建包含新成员的
initial-cluster列表 - 生成 Static Pod 清单,设置
initial-cluster-state=existing - 等待新 etcd 成员加入集群(最多
5s × 8 = 40s)
3.6 kubeadm join 完整执行流程
3.6.1 发现机制
discovery.For() 实现双向信任建立:
func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
// Step 1: 获取已验证的 kubeconfig(建立 Node → API Server 信任)
config, err := DiscoverValidatedKubeConfig(cfg)
// Step 2: 使用 TLSBootstrapToken 建立 API Server → Node 信任
if len(cfg.Discovery.TLSBootstrapToken) != 0 {
return kubeconfigutil.CreateWithToken(
clusterinfo.Server, ..., cfg.Discovery.TLSBootstrapToken), nil
}
// 或者使用 discovery file 中嵌入的认证信息
}
发现有两种模式:
- Token 发现:
--discovery-token <token> <api-server-endpoint>→ 从cluster-infoConfigMap 获取 CA 证书,通过--discovery-token-ca-cert-hash验证 - File 发现:
--discovery-file <path-or-https-url>→ 从 kubeconfig 文件直接获取
3.6.2 joinData 构建
func newJoinData(cmd, args, opt, out, adminKubeConfigPath) (*joinData, error) {
// 合并 token 到 discovery 和 TLS bootstrap
if len(opt.token) > 0 {
opt.externalcfg.Discovery.TLSBootstrapToken = opt.token
opt.externalcfg.Discovery.BootstrapToken.Token = opt.token
}
// 如果 admin.conf 已存在(控制平面节点),跳过发现
if _, err := os.Stat(adminKubeConfigPath); err == nil && opt.controlPlane {
tlsBootstrapCfg, _ = clientcmd.LoadFromFile(adminKubeConfigPath)
}
// 加载/合并配置
cfg, _ := configutil.LoadOrDefaultJoinConfiguration(opt.cfgPath, opt.externalcfg)
}
3.6.3 Join Phase 执行
1. Preflight → 系统预检查
2. ControlPlanePrepare → 仅 --control-plane 时:
a. download-certs → 从 kubeadm-certs Secret 下载证书
b. certs → 生成缺失证书
c. kubeconfig → 生成 admin/controller-manager/scheduler kubeconfig
d. control-plane → 生成 Static Pod 清单
3. CheckEtcd → 检查 etcd 集群健康
4. KubeletStart → 写入 kubelet 配置,启动 kubelet
5. ControlPlaneJoin → 仅 --control-plane 时:
a. etcd 扩容 → 添加新 etcd 成员
b. 更新 kubeadm-config ConfigMap
c. 标记节点
3.7 证书上传/下载机制(HA 控制平面)
UploadCerts 用于 kubeadm init --upload-certs,将共享证书加密后存储到 Secret:
func UploadCerts(client, cfg, key) error {
// 1. 创建短期 Bootstrap Token(用于管理 Secret 的 TTL)
tokenID, _ := createShortLivedBootstrapToken(client)
// 2. 从磁盘读取证书并加密
secretData, _ := getDataFromDisk(cfg, decodedKey)
// 加密的证书列表:ca.crt, ca.key, front-proxy-ca.crt, front-proxy-ca.key,
// sa.pub, sa.key, etcd/ca.crt, etcd/ca.key
// 3. 创建 Secret,设置 OwnerReference 为 Token Secret(Token 过期 → Secret 自动清理)
apiclient.CreateOrUpdateSecret(client, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "kubeadm-certs",
Namespace: "kube-system",
OwnerReferences: ref, // 引用 Bootstrap Token Secret
},
Data: secretData, // 加密后的证书
})
// 4. 创建 RBAC 允许 Bootstrap Token 读取 Secret
createRBAC(client)
}
DownloadCerts 在 join 控制平面时下载解密:
func DownloadCerts(client, cfg, key) error {
secret, _ := getSecret(client) // 获取 kubeadm-certs Secret
secretData, _ := getDataFromSecret(secret, decodedKey) // 解密
for certOrKeyName, certOrKeyPath := range certsToTransfer(cfg) {
writeCertOrKey(certOrKeyPath, secretData[certOrKeyName]) // 写入磁盘
}
}
安全设计:
- 证书用 32 字节随机 key 加密(AES)
- Secret 的 OwnerReference 指向 Bootstrap Token Secret(默认 2 小时 TTL → Secret 自动被 GC)
- RBAC 限制只有
system:bootstrappers:kubeadm:default-node-token组可读取
3.8 配置上传机制
UploadConfiguration 将集群配置持久化到 kubeadm-config ConfigMap:
func UploadConfiguration(cfg, client) error {
// 1. 准备 ClusterConfiguration(清除 ComponentConfig 避免冗余)
clusterConfigurationToUpload := cfg.ClusterConfiguration.DeepCopy()
clusterConfigurationToUpload.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
// 2. 准备 ClusterStatus(记录控制平面节点端点)
clusterStatus := &kubeadmapi.ClusterStatus{
APIEndpoints: map[string]kubeadmapi.APIEndpoint{
cfg.NodeRegistration.Name: cfg.LocalAPIEndpoint,
},
}
// 3. 创建或更新 ConfigMap
apiclient.CreateOrMutateConfigMap(client, &v1.ConfigMap{
Data: map[string]string{
"ClusterConfiguration": clusterConfigurationYaml,
"ClusterStatus": clusterStatusYaml,
},
}, func(cm) {
cm.Data["ClusterConfiguration"] = clusterConfigurationYaml
// 更新 ClusterStatus,追加新端点
cs.APIEndpoints[cfg.NodeRegistration.Name] = cfg.LocalAPIEndpoint
})
// 4. 创建 RBAC 允许节点读取 ConfigMap(用于 join 和 upgrade)
}
3.9 Token 管理深度解析
Bootstrap Token 本质是 kube-system namespace 中类型为 bootstrap.kubernetes.io/token 的 Secret:
- 格式:
[a-z0-9]{6}.[a-z0-9]{16}(前 6 位是 Token ID,后 16 位是 Secret) - Secret 名称:
bootstrap-token-<token-id> - Token 有 TTL,过期后由
tokencleanercontroller 自动清理 - 用途(Usages):
signing(签名 cluster-info)、authentication(TLS Bootstrap 认证) - 组成员(Groups):默认加入
system:bootstrappers:kubeadm:default-node-token
3.10 预检查系统
preflight 包实现了一套 Checker 接口体系:
type Checker interface {
Check() (warnings, errorList []error)
Name() string
}
内置检查器包括:
ContainerRuntimeCheck:容器运行时是否运行ServiceCheck:kubelet 服务是否启用PortOpenCheck:端口是否可用DirAvailableCheck:目录是否可用(如/etc/kubernetes/)FileAvailableCheck:文件是否不存在SystemVerificationCheck:内核参数、cgroup 等IsPrivilegedUserCheck:是否以 root 运行SwapCheck:是否关闭 swapMemCheck:内存是否足够ExternalEtcdVersionCheck:外部 etcd 版本ImagePullCheck:镜像是否可拉取
RunInitNodeChecks 和 RunJoinNodeChecks 分别组装不同的检查器列表执行。
四、Mermaid 图
图1:kubeadm 整体架构图
图2:kubeadm init 流程图
图3:kubeadm join 流程图
图4:证书管理流程图
图5:配置文件处理流程图
图6:Control Plane 部署流程图
图7:etcd 集群初始化流程图
图8:Token 管理流程图
五、核心设计模式总结
5.1 Phase Workflow 模式
kubeadm 最核心的架构模式。将复杂的 init/join 流程拆解为有序的 Phase 序列,每个 Phase 可独立执行、跳过或条件执行。Runner 引擎管理 Phase 的注册、排序、过滤和执行,并自动生成 CLI 子命令。
5.2 幂等性设计
几乎所有写入操作都遵循"先检查后写入"模式:
- 证书:
writeCertificateFilesIfNotExist→ 已存在则验证有效性,有效则跳过 - kubeconfig:
createKubeConfigFileIfNotExists→ 已存在则验证 CA/ServerURL - Static Pod:
WriteStaticPodToDisk直接覆盖(期望内容相同) - API 对象:
CreateOrUpdate*→ 存在则更新,不存在则创建
5.3 外部 CA 适配
通过文件存在性检测外部 CA:
ca.crt存在 +ca.key不存在 → 外部 CA 模式- 外部 CA 模式下:跳过证书生成,只生成 CSR + 私钥;禁止 upload-certs;清空 controller-manager 的 signing key
5.4 Dry-Run 支持
initData 中 DryRun() 标志影响所有路径选择:
- 证书/kubeconfig/清单 写入临时目录而非真实路径
- API 客户端使用
DryRunClient(仅打印操作不实际执行) InitDryRunGetter模拟部分 GET 请求(节点、ConfigMap 等)
5.5 配置层叠
配置合并优先级(从高到低):
- CLI flag 显式设置
- 配置文件 YAML
- 内部默认值(
Scheme.Default()) - 运行时变异(
MutateCgroupDriver等)
5.6 安全引导(TLS Bootstrap)
双向信任建立流程:
- Node → API Server:通过 Discovery(Token 或 File)获取 CA 证书,验证 API Server 身份
- API Server → Node:通过 TLS Bootstrap(CSR 自动签发)让 API Server 信任 Node
六、关键常量参考
| 常量 | 值 | 含义 |
|---|---|---|
KubernetesDir |
/etc/kubernetes |
Kubernetes 配置根目录 |
CertificateValidity |
10 年 | 所有签发证书有效期 |
DefaultControlPlaneTimeout |
4 分钟 | 等待控制平面就绪超时 |
APICallRetryInterval |
500ms | API 调用重试间隔 |
EtcdListenClientPort |
2379 | etcd 客户端端口 |
EtcdListenPeerPort |
2380 | etcd 对等端口 |
DefaultCertTokenDuration |
2 小时 | 上传证书 Token TTL |
TLSBootstrapTimeout |
5 分钟 | TLS Bootstrap 超时 |
ClusterConfigurationConfigMapKey |
ClusterConfiguration |
ConfigMap 中 ClusterConfiguration 键 |
ClusterStatusConfigMapKey |
ClusterStatus |
ConfigMap 中 ClusterStatus 键 |
KubeadmCertsSecret |
kubeadm-certs |
加密证书 Secret 名称 |
KubeadmConfigConfigMap |
kubeadm-config |
集群配置 ConfigMap 名称 |
CertificateKeySize |
32 字节 | 证书加密密钥长度 |
本文档严格基于
github.com/kubernetes/cmd/kubeadm/源码分析,覆盖了 CLI 层、Workflow 引擎、各 Phase 业务逻辑、API 类型体系、工具函数层的核心实现。
更多推荐


所有评论(0)