1. 项目概述:一个为Kubernetes集群部署而生的自动化剧本
如果你和我一样,长期在运维和DevOps一线摸爬滚打,那么对Kubernetes集群的初始化部署一定又爱又恨。爱的是它带来的强大编排能力,恨的是那套繁琐、易错、文档分散的kubeadm init流程。每次搭建新集群,从系统初始化、容器运行时安装、kubeadm配置到网络插件部署,就像在走一条布满陷阱的钢丝绳,任何一个参数配错、一个依赖包版本不对,都可能让你在kubeadm init的报错信息里耗费半天。
ReSearchITEng/kubeadm-playbook这个项目,就是为解决这个痛点而生的。它不是一个全新的部署工具,而是一个基于Ansible Playbook的、高度自动化的“最佳实践集成器”。简单来说,它把社区里那些经过千锤百炼的Kubernetes部署经验、安全加固建议和性能调优参数,封装成了一组可重复执行、可版本控制的YAML文件。你只需要准备好目标服务器的IP地址清单,定义几个关键变量(比如Pod网段、Service网段、Kubernetes版本),然后运行一条Ansible命令,它就能帮你把一个生产就绪的Kubernetes集群从零搭建起来。
这个项目特别适合以下几类人:中小团队的基础设施工程师,没有精力维护复杂的部署流水线,但又需要稳定可靠的集群;个人学习者和开发者,希望快速搭建一个干净的实验环境,而不用每次都从头查文档;需要频繁创建和销毁集群的CI/CD环境,它提供了确定性的部署结果。它的核心价值在于“标准化”和“自动化”,将部署从一门艺术变成一项可重复的工程。
2. 核心设计思路:为何选择Ansible与声明式编排
在深入代码之前,我们先聊聊这个项目的设计哲学。市面上部署Kubernetes的工具很多,Kubespray、Kops、RKE各有千秋。kubeadm-playbook选择了一条看似传统但极其务实的路径:基于Ansible。这个选择背后有深刻的考量。
2.1 Ansible的优势:无代理、幂等性与可读性
首先,Ansible采用SSH协议进行通信,无需在目标服务器上安装任何常驻代理(Agent)。这对于初始化一个裸机或纯净的云服务器环境来说是巨大的优势。你只需要在控制机上安装Ansible,并确保能通过SSH密钥访问所有节点,剩下的工作就全交给Playbook了。这种“无侵入性”使得它非常适合作为基础设施代码(IaC)的起点。
其次,Ansible的核心特性是幂等性。这意味着同一个Playbook可以安全地重复运行多次,而不会导致系统状态混乱。如果某个步骤(比如安装containerd)已经完成,Ansible会识别出现状与期望状态一致,从而跳过该任务。这对于故障恢复和配置更新至关重要。在部署Kubernetes这种复杂系统时,难免会遇到网络超时或包管理器临时故障,幂等性保证了你可以重新运行Playbook来继续完成部署,而不是陷入如何回退的困境。
第三,Ansible Playbook使用YAML格式,可读性非常高。即使你不是Ansible专家,也能大致看懂每个任务在做什么:是安装软件包、修改配置文件,还是执行Shell命令。kubeadm-playbook项目结构清晰,将任务按模块划分(如preflight、container-runtime、kubernetes),使得定制和调试变得非常直观。你可以很容易地找到修改Docker镜像仓库地址的地方,或者调整kubelet的max-pods参数。
2.2 声明式编排:将“怎么做”封装为“要什么”
kubeadm-playbook的另一个设计精髓是采用了声明式的思想。你不需要编写一连串的apt-get install和systemctl start命令。相反,你在一个变量文件(如group_vars/all.yml)中声明你的期望状态:
kubernetes_version: "1.28.2" pod_network_cidr: "10.244.0.0/16" service_cidr: "10.96.0.0/12" container_runtime: containerdPlaybook的职责就是解读这些声明,并驱动系统达到这个状态。它内部处理了所有的兼容性问题,例如,针对Ubuntu 22.04和CentOS 8,它会自动选择正确的Kubernetes仓库源;针对你选择的containerd,它会配置正确的Cgroup驱动(systemd)和镜像仓库镜像。这种抽象让使用者更关注于架构设计(如网络方案选型),而非底层命令细节。
2.3 模块化与角色分离
项目采用了Ansible Roles的架构,将不同的功能解耦。通常你会看到类似以下的角色:
common:负责所有节点的通用初始化,如关闭Swap、配置防火墙、设置SELinux、配置内核参数、安装依赖包。container-runtime:根据选择安装和配置Docker或containerd。kubernetes:安装kubeadm,kubelet,kubectl,并执行kubeadm init或kubeadm join。network:部署CNI网络插件,如Calico或Flannel。addon:部署核心插件,如CoreDNS和Metrics Server。
这种模块化设计带来了极佳的灵活性。比如,如果你已经有一套成熟的系统初始化流程,你可以轻松地跳过common角色,只使用它的kubernetes角色。或者,你想尝试Cilium而不是Calico,只需替换或修改network角色的任务即可。
注意:虽然Playbook自动化了绝大部分工作,但它并不意味着你可以完全不懂Kubernetes。理解你声明的每一个变量(特别是网络CIDR)的含义,对于后续的问题排查和集群扩容至关重要。自动化工具放大了你的效率,同时也放大了错误配置的影响范围。
3. 环境准备与配置详解:魔鬼在细节中
在按下回车键执行Playbook之前,充分的准备工作能避免90%的失败。这一部分,我们结合实战经验,拆解每一个准备步骤的“为什么”和“怎么做”。
3.1 控制机与目标节点要求
- 控制机:任何可以安装Ansible(版本>=2.9)的Linux/macOS机器。关键是要能通过SSH连接到所有目标节点。实操心得:强烈建议在控制机上配置好到所有节点的SSH免密登录(使用
ssh-copy-id),这能避免Playbook运行过程中因密码输入而中断。同时,确保控制机的Python版本在3.6以上。 - 目标节点:至少2台(1主1从)或3台(高可用)的x86_64服务器。操作系统推荐Ubuntu 20.04/22.04 LTS或CentOS/RHEL 7/8及其衍生版。每台节点建议至少2核CPU、2GB内存(主节点建议4GB)、20GB磁盘。重要提示:所有节点的主机名必须唯一,且能正确解析(最好在
/etc/hosts中相互配置,或使用内部DNS)。
3.2 清单文件(Inventory)的学问
Ansible通过清单文件来管理主机。kubeadm-playbook通常需要一个结构清晰的清单。一个经典的例子inventory/my-cluster/hosts.ini:
[all] node1 ansible_host=192.168.1.101 ip=192.168.1.101 node2 ansible_host=192.168.1.102 ip=192.168.1.102 node3 ansible_host=192.168.1.103 ip=192.168.1.103 [kube_control_plane] node1 [kube_node] node2 node3 [etcd] node1 [calico_rr] [k8s_cluster:children] kube_control_plane kube_node[all]:定义所有节点,并设置了两个变量:ansible_host(SSH连接地址)和ip(节点自身IP,用于kubelet注册和网络插件)。[kube_control_plane]:指定主节点(或多个主节点用于高可用)。[kube_node]:指定工作节点。[etcd]:指定etcd成员。在单主节点部署中,etcd与主节点同居。[k8s_cluster:children]:一个主机组,包含所有集群节点。
为什么这么分组?这允许Playbook针对不同的角色执行不同的任务。例如,只在控制平面节点上运行kubeadm init,只在工作节点上运行kubeadm join。
3.3 变量配置:定制你的集群
核心配置都在group_vars/k8s_cluster.yml或group_vars/all.yml中。这里有几个关键变量需要你深刻理解:
# Kubernetes版本,必须与仓库中存在的版本严格一致 kubernetes_version: "1.28.2" # 容器运行时,containerd是当前主流和k8s官方推荐 container_runtime: containerd # 网络配置,这是最容易出错的区域之一 pod_network_cidr: "10.244.0.0/16" # Pod IP范围,必须与后续安装的CNI插件默认网段匹配! service_cidr: "10.96.0.0/12" # Service的ClusterIP范围 dns_domain: "cluster.local" # 集群DNS域名 # kube-proxy模式,ipvs性能优于传统的iptables,尤其在Service数量多时 kube_proxy_mode: ipvs # 控制平面端点,单主节点时就是主节点的IP或域名 control_plane_endpoint: "192.168.1.101:6443"避坑指南:
pod_network_cidr:这个值不是随便填的。如果你选择Calico作为CNI,其默认的IP池就是192.168.0.0/16。如果你填了10.244.0.0/16,就必须在Calico的安装清单中相应修改其IP池配置,否则网络会不通。kubeadm-playbook通常会在部署CNI时自动同步这个变量,但你必须知道这其中的关联。service_cidr:不要与你的物理网络或Pod网络重叠。例如,你的服务器局域网是192.168.1.0/24,那么Pod和Service网段就应该避开这个范围。control_plane_endpoint:如果是生产环境高可用集群,这里应该是一个负载均衡器(如HAProxy+Keepalived)的VIP地址,而不是单个主机的IP。
3.4 国内环境特殊配置:镜像加速
在国内环境,从k8s.gcr.io、quay.io拉取镜像几乎必然失败。kubeadm-playbook通常通过变量支持配置镜像仓库镜像。
# 示例:使用阿里云镜像仓库 kube_image_repository: "registry.aliyuncs.com/google_containers" # 对于其他组件镜像,如Calico,也可能有对应的镜像变量 calicoctl_image_repository: "docker.io/calico" # 或者更通用的方式,配置containerd的镜像加速器 containerd_registry_mirrors: - "https://docker.mirrors.ustc.edu.cn" - "https://hub-mirror.c.163.com"你需要仔细阅读项目的README或defaults/main.yml文件,找到所有与镜像相关的变量并进行覆盖。这是国内用户成功部署的第一步,也是最关键的一步。
4. Playbook执行流程与核心任务拆解
现在,让我们穿上“Ansible”的鞋子,一步步走完Playbook的执行旅程。假设你已经在项目根目录,配置好了清单和变量,执行命令通常是:
ansible-playbook -i inventory/my-cluster/hosts.ini cluster.yml4.1 第一阶段:预检与系统初始化(Preflight)
Playbook首先会执行一系列预检任务,这模仿了kubeadm init之前的kubeadm preflight检查,但更全面。
- 检查连通性:确保Ansible能连接到所有节点。
- 系统参数检查与配置:
- 关闭Swap:Kubernetes 1.8+要求必须关闭Swap,否则kubelet无法启动。Playbook会执行
swapoff -a并注释掉/etc/fstab中的Swap条目。 - 配置防火墙:如果系统启用了
firewalld或ufw,Playbook会配置规则,放行Kubernetes组件通信所需的端口(如6443, 2379-2380, 10250, 10259等)。注意事项:如果你有更严格的安全组策略(如在云平台上),需要确保这些端口在网络安全组层面也是开放的。 - 配置SELinux:在CentOS/RHEL系统上,Playbook会将SELinux设置为permissive模式,或安装必要的策略包。生产环境可能需要更精细的SELinux策略。
- 加载内核模块:确保
br_netfilter、ip_vs等模块被加载,这是网络和Service代理(ipvs)的基础。 - 配置sysctl参数:设置关键的网络内核参数,如
net.bridge.bridge-nf-call-iptables=1,这是CNI网络插件正常工作所必需的。
- 关闭Swap:Kubernetes 1.8+要求必须关闭Swap,否则kubelet无法启动。Playbook会执行
4.2 第二阶段:容器运行时安装与配置
根据container_runtime变量的选择,安装Docker或containerd。目前趋势是containerd,因为它更轻量,并且是Kubernetes CRI(容器运行时接口)的参考实现。
- 安装:从官方仓库安装指定版本的containerd和
runc。 - 配置:生成
/etc/containerd/config.toml。这里有一个关键细节:Playbook会配置systemd作为cgroup驱动,并与后续的kubelet配置保持一致。同时,会配置pause镜像地址(通常替换为国内镜像)。如果配置了镜像加速器,也会在这里写入。 - 启动:启动并启用
containerd服务。
4.3 第三阶段:Kubernetes组件安装
在所有节点上安装kubeadm、kubelet和kubectl。
- 配置仓库:添加Kubernetes官方APT/YUM仓库(国内用户可能需要替换为阿里云或清华源)。
- 锁定版本:安装指定
kubernetes_version的包,并阻止它们被意外升级(通过apt-mark hold或yum versionlock)。 - 配置kubelet:设置
cgroup-driver=systemd,使其与containerd匹配。这是一个经典的排错点,驱动不一致会导致kubelet无法启动容器。
4.4 第四阶段:控制平面初始化
仅在[kube_control_plane]节点上执行。
- 生成kubeadm配置:Playbook会根据你的变量,动态生成一个
kubeadm-config.yaml文件。这个文件包含了所有初始化参数,比直接在命令行写一长串参数更清晰、更易于版本管理。 - 执行
kubeadm init:使用上一步生成的配置文件进行初始化。Ansible会等待初始化完成,并从输出中提取kubeadm join命令的令牌和CA证书哈希,这些信息将被保存下来用于工作节点加入。 - 配置kubectl:将生成的admin配置文件(
/etc/kubernetes/admin.conf)复制到控制机当前用户的~/.kube/config,这样你就能立刻使用kubectl了。
4.5 第五阶段:部署Pod网络插件(CNI)
一个没有CNI的Kubernetes集群,Pod之间是无法通信的。Playbook通常会部署Calico或Flannel。
- 应用清单:通过
kubectl apply -f命令部署CNI插件的DaemonSet和其他资源。Calico的清单文件可能已经根据pod_network_cidr做了相应修改。 - 等待就绪:Playbook会等待所有CNI Pod(如
calico-node)变为Running状态。此时,集群网络才真正可用。
4.6 第六阶段:工作节点加入
将[kube_node]组中的节点加入到集群。
- 分发join命令:Playbook将第四阶段获取的
kubeadm join命令(包含令牌、CA哈希和API Server地址)发送到各个工作节点并执行。 - 验证:在工作节点上,Playbook会检查
kubelet服务状态,并在控制机上使用kubectl get nodes来验证节点是否成功加入并变为Ready状态。
4.7 第七阶段:部署核心插件
部署集群运行所必需的核心插件。
- CoreDNS:集群内部的DNS服务,通常由
kubeadm在初始化时自动部署,但Playbook可能会确保其配置正确或重新部署指定版本。 - Metrics Server:为Kubernetes Dashboard和HPA(水平Pod自动扩缩容)提供资源度量数据。需要单独部署。
至此,一个功能完整的Kubernetes集群就部署完成了。整个过程完全自动化,并且可以重复执行。
5. 高级配置与生产级调优
使用默认配置能快速搭起一个集群,但要用于生产或获得更好性能,就需要进行调优。kubeadm-playbook通常通过变量暴露了这些调优点。
5.1 高可用(HA)控制平面
对于生产环境,单主节点是单点故障。Playbook支持部署多主高可用集群,其核心是:
- 负载均衡器:在
control_plane_endpoint变量中配置一个VIP(虚拟IP),这个VIP由额外的负载均衡器(如HAProxy+Keepalived部署在另外的节点上)提供,或者使用云厂商的负载均衡服务。 - 多个控制平面节点:在清单文件的
[kube_control_plane]组中列出所有主节点IP。 - etcd集群:Playbook会配置一个分布式的etcd集群(成员通常与主节点一一对应),而不是单节点etcd。
执行流程会变为:在第一个主节点上执行kubeadm init时使用--upload-certs参数上传证书,后续其他主节点使用包含证书密钥的kubeadm join命令加入作为控制平面。
5.2 证书管理
Kubernetes集群依赖大量TLS证书进行组件间加密通信。kubeadm默认生成的证书有效期为1年。Playbook可以通过配置kubeadm_certificate_key和相关的更新任务,来集成证书轮换的流程,或者你可以后续手动使用kubeadm alpha certs renew命令。
5.3 节点资源预留与kubelet参数调优
在group_vars中,你可以为kubelet配置资源预留,确保系统进程和Kubernetes组件有足够资源,避免Pod驱逐系统关键进程。
kubelet_custom_flags: - "--system-reserved=cpu=500m,memory=1Gi" - "--kube-reserved=cpu=200m,memory=512Mi" - "--eviction-hard=memory.available<5%,nodefs.available<10%"同时,可以调整max-pods参数(默认110),以适应节点的实际网络和资源容量。
5.4 网络策略与网络插件选择
如果你选择Calico,还可以启用网络策略(NetworkPolicy)功能,这为集群提供了Pod级别的网络隔离能力,是实现微服务安全的重要一环。这通常需要在Calico的安装清单中启用typha组件以提升性能,并在kube-proxy中设置strictARP模式以兼容Calico的BGP。
6. 常见问题排查与运维心得
即使有自动化工具,踩坑仍是常态。以下是我在多次使用类似Playbook部署中积累的排查经验。
6.1 节点状态NotReady
这是最常见的问题。运行kubectl describe node <node-name>查看具体事件。
- 网络未就绪:检查CNI Pod(如
calico-node)是否在所有节点上都为Running状态。查看其日志kubectl logs -n kube-system -l k8s-app=calico-node。常见原因是pod_network_cidr配置冲突,或节点防火墙未放行BGP端口(179)或VXLAN端口(4789)。 - 容器运行时问题:检查
kubelet日志journalctl -u kubelet -f。如果看到cni plugin not initialized或failed to create containerd task等错误,重点检查containerd服务状态和配置,特别是cgroup驱动是否与kubelet一致(都应为systemd)。 - 镜像拉取失败:如果
kubelet日志显示镜像拉取错误,确认镜像仓库镜像配置是否正确。可以手动到节点上执行crictl pull命令测试。
6.2kubeadm init卡住或失败
- 检查预检:手动运行
kubeadm init phase preflight可以提前发现很多系统级问题,如端口占用、内核参数等。 - 查看具体Phase:
kubeadm init是分阶段的,使用kubeadm init phase --help可以分阶段执行和排查。Playbook失败时,查看Ansible输出,定位到具体失败的任务和错误信息。 - 证书问题:如果之前初始化失败过,再次初始化前需要彻底清理。使用
kubeadm reset -f,并手动删除/etc/kubernetes/和/etc/cni/net.d/目录以及$HOME/.kube目录。Playbook通常也会包含一个reset.yml的剧本用于清理。
6.3 核心组件CrashLoopBackOff
检查kube-system命名空间下的Pod状态。
- kube-apiserver无法启动:检查
/etc/kubernetes/manifests/kube-apiserver.yaml中的etcd-servers地址是否正确,以及SSL证书路径。 - CoreDNS一直
Pending:这通常是网络插件未就绪导致的。先解决CNI问题。 - Metrics Server无法采集数据:检查其日志,常见问题是证书问题(需要添加
--kubelet-insecure-tls参数)或网络问题。
6.4 日常运维建议
- 版本控制:将你的
inventory/和group_vars/目录纳入Git版本控制。任何对集群配置的修改都应通过修改Ansible变量和重新运行Playbook来完成,而不是手动登录服务器修改。这是基础设施即代码(IaC)的核心实践。 - 渐进式更新:升级Kubernetes版本时,不要直接修改变量并全量运行Playbook。应先在一个非关键节点或新节点上测试升级流程。
kubeadm-playbook项目通常有upgrade.yml剧本,它遵循官方的kubeadm upgrade流程。 - 备份etcd:定期备份etcd数据是集群灾难恢复的底线。Playbook可以集成etcd备份任务,使用
etcdctl snapshot save命令。 - 监控与日志:Playbook只部署核心集群。生产环境务必额外部署监控系统(如Prometheus+Grafana)和日志收集系统(如EFK/ELK栈)。
6.5 排错命令速查表
| 问题现象 | 首要检查命令 | 可能原因与下一步 |
|---|---|---|
| 节点NotReady | kubectl describe node <node> | 看事件,查网络插件或kubelet |
| Pod一直Pending | kubectl describe pod <pod> -n <ns> | 看事件,通常是资源不足、节点选择器不匹配或PV未绑定 |
| Pod启动失败/CrashLoopBackOff | kubectl logs <pod> -n <ns> | 看应用日志;kubectl describe pod看状态详情 |
| 服务无法访问 | kubectl get svc,ep -n <ns> | 检查Service的Selector与Pod Label是否匹配,Endpoints是否正常 |
| 网络不通 | kubectl get pods -n kube-system -l k8s-app=calico-node | CNI Pod是否正常;在Pod内执行ping或curl测试 |
| kubelet异常 | journalctl -u kubelet -f --no-pager | 查看实时日志,关注cgroup驱动、证书、镜像拉取错误 |
最后,我想分享一点个人体会:kubeadm-playbook这类工具的价值,不仅在于第一次成功部署,更在于它提供了一套可版本化、可重复的集群定义。当你的团队需要维护多个环境(开发、测试、生产),或者需要快速重建集群时,它的优势就无可比拟。它把部署从一种“手工技能”变成了“工程实践”。当然,它也不是银弹,理解其背后的每一步操作原理,仍然是运维工程师不可或缺的能力。当你遇到问题时,能够深入Ansible任务、查看生成的配置文件、理解kubeadm的每个phase,才是从“会用工具”到“掌握技术”的关键跨越。