Ansible 自动化部署 Kubernetes 集群实施手册 📅 发布时间:2026/8/26 15:11:12 👁 浏览次数: 文档版本V1.0增强版编写日期2026-08-24适用场景测试环境及小型企业内网节点规划k8s-master 192.168.64.63含 Ansible 控制端/ k8s-worker1 192.168.64.64 / k8s-worker2 192.168.64.65软件版本Kubernetes 1.28 / containerd / Calico v3.27.4 / CentOS 7.9目录1. 建设目标2. 版本与拓扑3. 端口与安全组4. 三台主机首次准备4.0 配置 CentOS Vault 源必做三台机器都要执行4.1 设置主机名4.2 配置 hosts 和时间5. 在 Master 安装 Ansible 并建立 SSH 信任6. 创建 Ansible 项目6.1 ansible.cfg6.2 inventory.ini6.3 连接测试7. 自动化部署 Playbook8. 执行自动化部署8.1 预期结果9. 集群初始化后的日常操作9.1 为普通用户配置 kubectl9.2 常用检查命令9.3 测试调度和网络10. 常见故障处理10.1 YUM 报 404 或无法解析镜像10.2 containerd 启动失败10.3 kubeadm init 失败10.4 节点 NotReady 或 Calico 不正常kubectl get pods -A -o wide重点关注 kube-system 命名空间下的 calico-node、calico-kube-controllers、coredns、kube-proxy 等组件。如果 calico-node 处于 Init:0/3、ImagePullBackOff、CrashLoopBackOff 或 Pending说明网络插件尚未正常启动。kubectl apply -f /root/calico.yaml如果节点仍然 NotReady再检查 kubelet 和 containerd 的运行状态systemctl status containerd kubelet --no-pagercrictl --runtime-endpoint unix:///run/containerd/containerd.sock ps若 kubelet 已启动但 CNI 未初始化通常是因为 Calico 还未完成安装或者节点间网络通信被拦截。此时可继续观察几分钟并再次执行 kubectl get pods -A -o wide 和 kubectl describe pod -n kube-system 异常Pod名 查看具体报错。10.5 Worker 加入失败或 token 过期11. 验收清单12. 回滚与重建说明13. 实际环境补充说明 手册摘要本手册使用Ansible 自动化完成三台 CentOS 7.9 虚拟机上Kubernetes 1.28 集群的部署主节点 k8s-master192.168.64.63同时作为 Ansible 控制端两个工作节点通过 kubeadm 加入集群。自动化覆盖系统准备hosts、时间、防火墙、SELinux、swap、containerd 安装与 CRI 配置、Kubernetes 组件安装、控制平面初始化、Calico 网络插件部署、工作节点自动加入与集群验证。本增强版已前置解决 CentOS Vault 源失效、Calico 清单下载、容器镜像加速三个环境问题按章节顺序执行即可完成部署。文档版本V1.0 编写日期2026-08-24 适用场景课程实训、测试环境及小型企业内网执行边界本文按三台全新 CentOS 7.9 虚拟机编写。主节点同时作为 Ansible 控制端工作节点不安装 Ansible。生产环境应另设控制端、配置高可用控制平面、私有镜像仓库和证书/密钥管理。1. 建设目标使用 Ansible 一次性完成三台主机的系统准备、containerd 安装、Kubernetes 组件安装、主节点初始化、Calico 网络安装及工作节点加入最终得到 1 个 Master 和 2 个 Worker 的 Kubernetes 集群。2. 版本与拓扑工作节点2k8s-worker2 / 192.168.64.65运行工作负载。项目规划说明操作系统CentOS 7.9 x86_64CentOS 7 已停止生命周期支持必须准备可用的内网 /YUM 镜像或 CentOS Vault 源。Kubernetes1.28 系列使用 pkgs.k8s.io 的 v1.28 RPM 仓库避免使用已废弃的 yum.kubernetes.io 。容器运行时containerd.io 1.7.x使用 systemd cgroupCRI socket 为 unix:///run/containerd/containerd.sock 。集群网络Calico v3.27.4Pod 网段 192.168.0.0/16节点网段为 192.168.64.0/24 。主节点k8s-master / 192.168.64.63控制平面、etcd、Ansible 控制端。工作节点 1k8s-worker1 / 192.168.64.64运行工作负载。工作节点 2k8s-worker2 / 192.168.64.65运行工作负载。建议每台虚拟机至少 2 vCPU、4 GB RAM、40 GB 磁盘主节点建议 4 vCPU、8 GB RAM。三台主机需要互通主节点能访问软件仓库和 GitHub Raw用于下载 Calico 清单。图 1图 23. 端口与安全组端口方向用途22/TCPAnsible 控制端到所有节点SSH6443/TCP所有节点到 MasterKubernetes API Server2379-2380/TCPMaster 内部etcd10250/TCP节点之间kubelet API30000-32767/TCP/UDP节点之间 / 外部NodePort按需放行本实训为简化部署自动化脚本会停止 firewalld并将 SELinux 调整为 permissive。生产环境不应直接关闭防火墙应按上表和实际网络插件要求编写最小放行策略。4. 三台主机首次准备4.0 配置 CentOS Vault 源必做三台机器都要执行CentOS 7 已停止维护EOL默认的 mirror.centos.org 源已失效。三台机器在安装任何软件前必须先把 yum 源切换为可用的 Vault 镜像以下以阿里云为例否则后续所有 yum 操作都会报 404# 三台机器63/64/65都执行 cd /etc/yum.repos.d sed -i s|^mirrorlist|#mirrorlist|g CentOS-*.repo sed -i s|^#baseurlhttp://mirror.centos.org/centos/$releasever|baseurlhttps://mirrors.aliyun.com/centos-vault/7.9.2009|g CentOS-*.repo yum clean all yum makecache # 验证能列出仓库即成功 yum repolist注意$releasever 会被自动替换为 7若某些 .repo 文件里的 baseurl 写法不同可手动把 mirror.centos.org/centos/7 替换为 mirrors.aliyun.com/centos-vault/7.9.2009。4.1 设置主机名分别在 63、64、65 上以 root 执行对应命令# 192.168.64.63 hostnamectl set-hostname k8s-master # 192.168.64.64 hostnamectl set-hostname k8s-worker1 # 192.168.64.65 hostnamectl set-hostname k8s-worker2图 3图 44.2 配置 hosts 和时间三台机器的 /etc/hosts 都写入以下内容192.168.64.63 k8s-master 192.168.64.64 k8s-worker1 192.168.64.65 k8s-worker2 yum install -y chrony openssh-server openssh-clients systemctl enable --now chronyd sshd timedatectl set-timezone Asia/Shanghai chronyc sources -v确认三台主机时间相差不超过几秒否则 kubeadm 证书和 TLS 握手可能失败。图 5图 6图 7图 85. 在 Master 安装 Ansible 并建立 SSH 信任以下命令只在 192.168.64.63 执行。Ansible 使用 root SSH便于对内核、服务和软件包进行统一管理。yum install -y epel-release yum install -y ansible git openssh-clients ansible --version # 如果尚未有密钥直接回车接受默认路径不要设置口令 ssh-keygen -t ed25519 -N -f /root/.ssh/id_ed25519 # 按提示输入三台主机的 root 密码 ssh-copy-id root192.168.64.63 ssh-copy-id root192.168.64.64 ssh-copy-id root192.168.64.65 # 验证免密登录 ssh root192.168.64.64 hostname ssh root192.168.64.65 hostname安全要求实训完成后可改用专用运维用户和 sudo不要把私钥放进 Git 仓库也不要把 root 密码写进 inventory。图 9图 10图 11图 12图 136. 创建 Ansible 项目mkdir -p /root/k8s-ansible cd /root/k8s-ansible图 146.1 ansible.cfg[defaults] inventory ./inventory.ini host_key_checking False retry_files_enabled False deprecation_warnings False interpreter_python auto_silent timeout 30 [privilege_escalation] become False图 15图 166.2 inventory.ini[all] k8s-master ansible_host192.168.64.63 ansible_userroot k8s-worker1 ansible_host192.168.64.64 ansible_userroot k8s-worker2 ansible_host192.168.64.65 ansible_userroot [kube_control_plane] k8s-master [kube_node] k8s-worker1 k8s-worker2图 176.3 连接测试cd /root/k8s-ansible ansible all -m ping ansible all -m shell -a hostname cat /etc/centos-release三台都返回 pong 才继续。若出现 UNREACHABLE先检查 IP、sshd、root 登录和 SSH 公钥不要继续执行 playbook。图 18图 197. 自动化部署 Playbook在 Master 创建 /root/k8s-ansible/site.yml完整内容如下。Playbook 可重复执行系统准备、仓库和配置任务幂等已经初始化或加入集群的节点不会重复执行 kubeadm。在 Master 创建 /root/k8s-ansible/site.yml完整内容如下。Playbook 可重复执行系统准备、仓库和配置任务幂等已经初始化或加入集群的节点不会重复执行 kubeadm。---- name: Prepare all Kubernetes nodeshosts: allgather_facts: truebecome: truetasks:- name: Set hostname from inventory namehostname:name: {{ inventory_hostname }}- name: Configure cluster hostsblockinfile:path: /etc/hostsmarker: # {mark} K8S CLUSTERblock: |192.168.64.63 k8s-master192.168.64.64 k8s-worker1192.168.64.65 k8s-worker2- name: Stop and disable firewalld for lab environmentsystemd:name: firewalldstate: stoppedenabled: falsefailed_when: false- name: Put SELinux into permissive mode nowcommand: setenforce 0failed_when: falsechanged_when: false- name: Persist SELinux permissive modelineinfile:path: /etc/selinux/configregexp: ^SELINUXline: SELINUXpermissive- name: Disable swap immediatelycommand: swapoff -achanged_when: false- name: Disable swap in fstabreplace:path: /etc/fstabregexp: ^([^#].*\sswap\s.*)$replace: # \1- name: Install base packages and repository toolsyum:name:- yum-utils- device-mapper-persistent-data- lvm2- curl- wget- iproute- conntrack-tools- socat- ebtables- ipsetstate: present- name: Add Docker CE repository for containerd.ioget_url:url: https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repodest: /etc/yum.repos.d/docker-ce.repomode: 0644- name: Install containerdyum:name: containerd.iostate: present- name: Check containerd configuration filestat:path: /etc/containerd/config.tomlregister: ct_conf- name: Create containerd configurationshell: containerd config default /etc/containerd/config.tomlwhen: not ct_conf.stat.exists or ct_conf.stat.size 1000- name: Enable CRI plugin in containerdreplace:path: /etc/containerd/config.tomlregexp: disabled_plugins \[cri\]replace: disabled_plugins []- name: Use systemd cgroups in containerdreplace:path: /etc/containerd/config.tomlregexp: SystemdCgroup falsereplace: SystemdCgroup true- name: Use domestic pause imagereplace:path: /etc/containerd/config.tomlregexp: sandbox_image .*replace: sandbox_image registry.aliyuncs.com/google_containers/pause:3.9- name: Enable containerdsystemd:name: containerdstate: restartedenabled: true- name: Load Kubernetes kernel modulescopy:dest: /etc/modules-load.d/k8s.confmode: 0644content: |overlaybr_netfilter- name: Load overlay modulecommand: modprobe overlaychanged_when: false- name: Load br_netfilter modulecommand: modprobe br_netfilterchanged_when: false- name: Configure Kubernetes sysctl valuescopy:dest: /etc/sysctl.d/k8s.confmode: 0644content: |net.bridge.bridge-nf-call-iptables 1net.bridge.bridge-nf-call-ip6tables 1net.ipv4.ip_forward 1- name: Apply sysctl valuescommand: sysctl --systemchanged_when: false- name: Configure Kubernetes RPM repositorycopy:dest: /etc/yum.repos.d/kubernetes.repomode: 0644content: |[kubernetes]nameKubernetesbaseurlhttps://pkgs.k8s.io/core:/stable:/v1.28/rpm/enabled1gpgcheck1gpgkeyhttps://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.keyexcludekubelet kubeadm kubectl cri-tools kubernetes-cni- name: Install Kubernetes components from v1.28 repositoryyum:name:- kubelet- kubeadm- kubectl- cri-toolsstate: presentdisable_excludes: kubernetes- name: Enable kubelet (kubeadm will start it after configuration)systemd:name: kubeletstate: stoppedenabled: true- name: Initialize the control planehosts: kube_control_planegather_facts: falsebecome: truetasks:- name: Check existing cluster configurationstat:path: /etc/kubernetes/admin.confregister: admin_conf- name: Initialize Kubernetes control planecommand: -kubeadm init--apiserver-advertise-address192.168.64.63--pod-network-cidr192.168.0.0/16--cri-socketunix:///run/containerd/containerd.sock--image-repositoryregistry.aliyuncs.com/google_containerswhen: not admin_conf.stat.existsregister: kubeadm_init- name: Create root kubeconfig directoryfile:path: /root/.kubestate: directorymode: 0700- name: Install kubeconfig for rootcopy:src: /etc/kubernetes/admin.confdest: /root/.kube/configremote_src: truemode: 0600- name: Download Calico manifestget_url:url: https://raw.githubusercontent.com/projectcalico/calico/v3.27.4/manifests/calico.yamldest: /root/calico.yamlmode: 0644- name: Apply Calico network plugincommand: kubectl --kubeconfig/etc/kubernetes/admin.conf apply -f /root/calico.yamlregister: calico_applychanged_when: created in calico_apply.stdout or configured in calico_apply.stdout- name: Create a fresh worker join commandcommand: kubeadm token create --print-join-commandregister: join_command_rawchanged_when: false- name: Add containerd CRI socket to join commandset_fact:worker_join_command: {{ join_command_raw.stdout }} --cri-socketunix:///run/containerd/containerd.sock- name: Join worker nodeshosts: kube_nodegather_facts: falsebecome: truetasks:- name: Check whether worker is already joinedstat:path: /etc/kubernetes/kubelet.confregister: worker_kubelet_conf- name: Join worker to the clustercommand: {{ hostvars[k8s-master][worker_join_command] }}when: not worker_kubelet_conf.stat.exists- name: Ensure kubelet is enabled after joiningsystemd:name: kubeletstate: startedenabled: true- name: Verify clusterhosts: kube_control_planegather_facts: falsebecome: truetasks:- name: Wait until all three nodes are registeredcommand: kubectl --kubeconfig/etc/kubernetes/admin.conf get nodes --no-headersregister: nodes_outputuntil: nodes_output.stdout_lines | length 3retries: 20delay: 15changed_when: false- name: Show cluster node statuscommand: kubectl --kubeconfig/etc/kubernetes/admin.conf get nodes -o wideregister: final_nodeschanged_when: false- name: Show cluster node status in playbook outputdebug:var: final_nodes.stdout_lines图 20图 21控制平面初始化完成后必须先安装 Pod 网络插件否则节点状态会一直停留在 NotReadyCoreDNS 也会处于 Pending 状态。本文档使用 Calico 作为集群网络方案Pod 网段为 192.168.0.0/16清单文件统一保存为 /root/calico.yaml。如果主节点可以直接访问 GitHub Raw可先下载 Calico 官方清单文件curl -L https://raw.githubusercontent.com/projectcalico/calico/v3.27.4/manifests/calico.yaml -o /root/calico.yaml如果当前网络无法访问该地址则应提前将 calico.yaml 文件放置到 Master 节点的 /root/calico.yaml再继续后续步骤。由于部分网络环境无法访问 docker.io/calico 镜像源需要先将清单中的镜像地址统一替换为可访问的镜像源sed -i s#docker.io/calico#quay.io/calico#g /root/calico.yaml镜像地址修改完成后使用 kubectl 应用网络插件清单kubectl --kubeconfig/etc/kubernetes/admin.conf apply -f /root/calico.yaml应用成功后使用以下命令检查 Calico 和节点状态kubectl --kubeconfig/etc/kubernetes/admin.conf get pods -n kube-system -o wide kubectl --kubeconfig/etc/kubernetes/admin.conf get nodes -o wide当 calico-node 处于 Running 状态且 k8s-master 节点从 NotReady 变为 Ready 时说明网络插件安装成功。8. 执行自动化部署执行前确认① 三台机器已完成 4.0 的 Vault 源配置② /root/calico.yaml 已存在GitHub 不通时手动放置③ ansible all -m ping 全部 pong。三项都满足再执行 playbook。cd /root/k8s-ansible ansible all -m shell -a yum clean all ansible-playbook /root/k8s-ansible/site.yml第一次运行约需 515 分钟时间取决于 YUM 和 GitHub 下载速度。出现红色任务失败时先处理失败原因再重新执行同一条命令Playbook 的检查项会避免重复初始化。图 22图 23图 248.1 预期结果kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane ... v1.28.x k8s-worker1 Ready none ... v1.28.x k8s-worker2 Ready none ... v1.28.x如果节点显示 NotReady先等待 Calico DaemonSet 拉取镜像使用 kubectl get pods -A -o wide 查看具体 Pod 状态。kubectl get pods -A图 259. 集群初始化后的日常操作9.1 为普通用户配置 kubectl# 在 Master 上执行替换为实际用户名 mkdir -p /home/用户名/.kube cp -f /etc/kubernetes/admin.conf /home/用户名/.kube/config chown -R 用户名:用户名 /home/用户名/.kube9.2 常用检查命令kubectl cluster-info kubectl get nodes -o wide kubectl get pods -A kubectl get --raw/readyz?verbose crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps systemctl status containerd kubelet --no-pager9.3 测试调度和网络提示若 nginx:1.25 镜像拉取超时说明 docker.io 仍不可达可改用加速前缀kubectl create deployment nginx --imagedocker.m.daocloud.io/library/nginx:1.25与上面配置的 mirror 等效或先验证 containerd 拉取crictl pull docker.io/library/nginx:1.25。kubectl create deployment nginx --imagenginx:1.25 kubectl expose deployment nginx --port80 --typeNodePort kubectl get svc nginx kubectl delete svc nginx kubectl delete deployment nginx10. 常见故障处理10.1 YUM 报 404 或无法解析镜像CentOS 7 已 EOL官方镜像经常被迁移到 Vault。先确认 DNS 和网络ping -c 3 192.168.64.63 curl -I https://pkgs.k8s.io/core:/stable:/v1.28/rpm/ yum clean all yum makecache如果 CentOS 基础源报 404将 /etc/yum.repos.d/CentOS-*.repo 中的 mirror.centos.org 替换为 vault.centos.org然后重建缓存。企业环境推荐使用内部 YUM 镜像不建议长期依赖公网源。10.2 containerd 启动失败systemctl status containerd --no-pager -l journalctl -u containerd -n 100 --no-pager grep -n SystemdCgroup /etc/containerd/config.toml systemctl restart containerd必须看到 SystemdCgroup true。如果配置文件损坏可备份后重新生成containerd config default /etc/containerd/config.toml再改为 true 并重启。10.3 kubeadm init 失败swapoff -a systemctl status containerd kubelet --no-pager crictl --runtime-endpoint unix:///run/containerd/containerd.sock info kubeadm reset -f rm -rf /etc/cni/net.d /var/lib/cni ansible-playbook site.ymlkubeadm reset -f 会清理该节点的集群状态只在初始化失败且确认要重来时使用。10.4 节点 NotReady 或 Calico 不正常如果 kubeadm init 已执行成功但 Master 或 Worker 节点仍显示 NotReady通常说明集群网络尚未就绪或者 Calico 组件未能正常拉起。此时不要直接执行 kubeadm reset应先确认问题发生在哪一层。首先查看系统 Pod 状态kubectl get pods -A -o wide重点关注 kube-system 命名空间下的 calico-node、calico-kube-controllers、coredns、kube-proxy 等组件。如果 calico-node 处于 Init:0/3、ImagePullBackOff、CrashLoopBackOff 或 Pending说明网络插件尚未正常启动。其次检查 Calico 清单是否已正确应用kubectl apply -f /root/calico.yaml如果清单中镜像源默认指向 docker.io/calico而当前环境无法访问该源应先修改镜像地址再重新执行 apply。若镜像拉取超时可根据实际可访问性改为 quay.io/calico或者换成实验环境内可用的私有镜像仓库地址。如果节点仍然 NotReady再检查 kubelet 和 containerd 的运行状态systemctl status containerd kubelet --no-pagercrictl --runtime-endpoint unix:///run/containerd/containerd.sock ps若 kubelet 已启动但 CNI 未初始化通常是因为 Calico 还未完成安装或者节点间网络通信被拦截。此时可继续观察几分钟并再次执行 kubectl get pods -A -o wide 和 kubectl describe pod -n kube-system 异常Pod名 查看具体报错。当 Calico 全部正常后节点状态会逐步变为 Ready。此时再进行 Worker 加入和业务部署。10.5 Worker 加入失败或 token 过期# Master 上重新生成命令 kubeadm token create --print-join-command # Worker 上清理半加入状态后重新执行新命令 kubeadm reset -f rm -rf /etc/cni/net.d /var/lib/cni systemctl restart containerd kubelet11. 验收清单检查项命令通过标准Ansible 连通ansible all -m ping三台均返回 pong运行时systemctl is-active containerdactiveKubernetes 组件kubeadm version; kubectl version --client已安装且为 v1.28 系列节点状态kubectl get nodes1 个 control-plane、2 个节点全部 Ready系统 Podkubectl get pods -Akube-system 关键 Pod 为 Running/Completed调度验证创建并暴露 nginx DeploymentPod RunningService 有 NodePort交付物将 /root/k8s-ansible/ansible.cfg、inventory.ini、site.yml 和本手册一并归档。执行日志可用 script -a /root/k8s-ansible/deploy.log 留存便于考试验收和故障追踪。12. 回滚与重建说明仅重建某个 Worker先在 Master 执行 kubectl drain k8s-worker1 --ignore-daemonsets --delete-emptydir-data 和 kubectl delete node k8s-worker1再在 Worker 执行 kubeadm reset -f最后重新执行 Playbook。不要在 Master 上执行 reset除非明确要销毁整个集群。13. 实际环境补充说明本次实测环境中主机地址为 192.168.64.63、192.168.64.64、192.168.64.65Kubernetes 版本为 v1.28.15容器运行时为 containerd 1.6.33。控制平面初始化完成后如果 k8s-master 显示 NotReady优先检查 CNI 网络插件是否就绪。Calico 清单先放到 /root/calico.yaml再执行下面命令完成网络插件安装。kubectl apply -f /root/calico.yaml当 docker.io/calico 镜像访问超时后将清单中的镜像源改为 quay.io/calico并重新应用。sed -i s#docker.io/calico#quay.io/calico#g /root/calico.yaml生成 Worker 加入命令使用下面命令在 k8s-worker1 和 k8s-worker2 上执行时末尾补上 --cri-socketunix:///run/containerd/containerd.sock。kubeadm token create --print-join-command最终通过 kubectl get nodes -o wide 和 kubectl get pods -A 验证集群状态三节点均为 Ready 后即可结束。 一键复制全文含图片