简介:《Kubernetes部署指南》是一份面向容器运维、平台工程师及K8s初学者的系统部署手册,聚焦集群从零搭建与落地部署中的真实问题。文档覆盖单机部署、kubeadm、kops、Kubespray、Azure、Windows、LinuxKit、kubeasz及Kubernetes-The-Hard-Way等主流路线,并详细列出Etcd、Docker、Go、CNI、CSI等组件的版本依赖关系,帮助读者规避版本兼容风险;同时包含kubectl安装、证书与密钥生成、Etcd集群、控制节点与计算节点部署、Sonobuoy测试以及国内镜像加速等实战步骤。资源包共1个PDF文件,大小3.91MB,目录结构清晰,便于翻阅检索。已有178人学习下载。通过这份指南,读者可选择适合自身环境的部署方式,按步骤搭建高可用Kubernetes集群,并快速定位镜像拉取慢、配置证书等典型难题,显著提升容器平台交付效率。
1. 这份Kubernetes部署指南,到底在解决什么问题?
如果你刚被分到一套新环境,急着把Kubernetes集群跑起来,大概率会在“初始化”“节点加入”“网络插件”这几个环节反复折腾。Kubernetes部署这件事,难的不是把kubeadm安装上,而是从一台干净的Linux机器到能稳定运行工作负载的完整链路:内核参数、容器运行时、证书、网络组件、DNS,任何一环没对齐,集群就会用各种玄学报错回应你。这份部署指南要解决的正是这个完整链路,覆盖从选型到踩坑的落地细节。适合三类人:第一次搭集群的新手、准备把测试环境升级成生产环境的运维、以及想搞明白部署参数背后逻辑的开发者。读完你至少能照着跑通一个可用集群,并在出问题时知道先查哪、去哪查。
2. 先选型再动手:部署方式对比与版本参数决策
2.1 三种主流部署路径:kubeadm、二进制、发行版
我一般会先问一句话:你要的是可控性还是速度?Kubernetes部署没有银弹,常见做法是在三种路径里选。第一种是kubeadm,这是官方推荐的社区标准方案。它把集群搭建拆成kubeadm init和kubeadm join两步,证书、kubeconfig、控制平面组件都由工具自动生成,你只需要准备容器镜像和网络插件。第二种是纯二进制部署,下载kube-apiserver、kube-controller-manager、kube-scheduler等二进制文件,用systemd管理。这种方式能让你看清每个组件的启动参数,排障时直接面对最底层逻辑,但步骤多、易出错。第三种是直接用发行版,比如k3s、k0s或RKE2,它们把etcd、控制面封装成一个包,适合边缘或资源受限场景。
这三者并不互斥。生产环境我常看到“kubeadm搭底座 + 定制参数”的组合:kubeadm负责证书和静态Pod编排,你通过自定义配置文件控制apiserver的审计日志、etcd的磁盘配额等细节。二进制部署虽然原始,但在需要接入公司内部CA、或对组件参数有极端定制时,反而是唯一选择。而k3s这类发行版则不适合需要完整Kubernetes API清单的团队,因为部分特性被裁剪或延长了启动流程。选择的关键是:你的团队修复一个组件故障的容忍时间是多少。如果只想要最小精力维护,优先kubeadm;如果对每个进程都要去进程树里数一遍,再考虑二进制。
当你决定走kubeadm路线后,第一个需要确认的是版本对齐。这里有一个常被忽略的约束:kubeadm、kubelet、kubectl与Kubernetes控制面组件之间的版本偏移不能超过一个次要版本。比如控制面是1.28,工作节点上的kubelet可以是1.27、1.28或1.29,但不能是1.26。更稳妥的做法是让所有节点版本完全一致。同时,kubeadm会安装最新稳定版,如果你不显式指定--kubernetes-version,很可能拉到一个刚发布的新版本,而镜像仓库的tag还没同步全,直接导致拉取失败。我在部署前一定会去官方CHANGELOG确认目标版本的补丁号,再配合crictl pull先手动验证镜像存在。
2.2 版本匹配与镜像源:最容易在第一步翻车的地方
很多人会在kubeadm init时被一个超时错误卡住:failed to pull image "registry.k8s.io/...:latest"。原因通常是两个:一是默认镜像源在你的网络环境不可达或极慢;二是没指定kubeadm和kubelet的版本,导致它默认拉取latest,而你本地只有特定版本。解决方法是提前确定好一个Kubernetes版本,并用kubeadm config images list查看所需镜像列表。然后有两种做法:在企业内网环境,你可以用私有镜像仓库中转,先把镜像从可访问的源拉下来,重新tag成registry.k8s.io的地址,再推送到内网仓库,最后在kubeadm init时指定--image-repository指向内网地址。另一种更省事的做法是直接设代理或配置docker daemon的registry-mirrors,但这在部分生产环境不现实。
有些同学会忽略kubeadm配置文件的作用。命令行虽然能跑通基础集群,但多个参数堆在命令行里很难维护。我习惯写一个kubeadm-config.yaml:
apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.1.21 bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 controlPlaneEndpoint: "192.168.1.20:6443" networking: podSubnet: "10.244.0.0/16" serviceSubnet: "10.96.0.0/12" --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration maxPods: 110 systemReserved: cpu: "500m" memory: "1Gi"这里controlPlaneEndpoint写的是VIP或负载均衡地址,适用于高可用部署;如果只是单控制面,可以省略,kubeadm会自动用初始化节点的IP。podSubnet必须和后面要安装的网络插件对应,比如Flannel默认用10.244.0.0/16,Calico默认用192.168.0.0/16,这写错会导致Pod间无法通信。criSocket指定为containerd的socket,因为从Kubernetes 1.24之后dockershim被移除,docker不再是默认运行时。systemReserved是给系统进程预留的CPU和内存,如果机器配置不高,这里可以调低,否则调度器会认为节点可分配资源过少。
提示:
kubeadm config images list --kubernetes-version v1.28.2可以查看该版本需要哪些镜像。结合--image-repository和--image-pull-timeout调节拉取策略,超时时间默认5分钟,网络差时先调大到10分钟。
3. 用kubeadm一步步拉起集群:控制平面与工作节点的完整操作
3.1 初始化前的主机准备:内核参数、swap、容器运行时
先把操作系统层面的三个坑填平:关闭swap、加载内核模块、设置转发参数。swap不关闭的话,kubelet会拒绝启动,因为kubelet默认要求cgroup的swap功能关闭。可以用swapoff -a临时关闭,再编辑/etc/fstab注释掉swap行永久生效。接着需要加载br_netfilter和overlay模块,让iptables能处理桥接流量,这样Pod之间的流量才能被正确转发和过滤。
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system上面这段把overlay和br_netfilter模块配置开机加载,并设置三个内核参数。net.bridge.bridge-nf-call-iptables是让宿主机iptables规则对桥接的容器流量也生效;net.ipv4.ip_forward是开启路由转发,kube-proxy的iptables模式和Pod间通信都依赖它。执行完sysctl --system后用lsmod | grep br_netfilter验证一下,没有输出就是没加载成功,继续查模块路径或重启。
容器运行时这里选containerd,它比docker更轻,也是Kubernetes社区默认的选择。安装后需要修改它的sandbox镜像地址,因为默认的registry.k8s.io/pause:3.9在部分地区拉取困难。修改/etc/containerd/config.toml容易踩一个隐藏坑:containerd默认没有完整的config.toml,需要先执行containerd config default > /etc/containerd/config.toml生成默认配置,再修改其中的sandbox_image为内网镜像地址。改完后systemctl restart containerd。
3.2 控制平面初始化:kubeadm init的关键参数与网络插件
控制平面初始化是整条链路里最需要耐心的步骤。我通常先把镜像拉齐,而不是等kubeadm init一边初始化一边拉镜像,这样可以更快定位网络问题。使用kubeadm config images pull带上--image-repository参数先拉一遍,看到所有镜像都是Pulled状态后,再执行真正的初始化。
sudo kubeadm init \ --config=/etc/kubernetes/kubeadm-config.yaml \ --upload-certs \ --ignore-preflight-errors=Swap mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config--config指定我们刚才写好的配置文件,这样命令行里不需要堆一长串参数。--upload-certs用于多控制面场景,它会把证书上传到kubeadm-managed的secret里,后续其他控制面节点加入时可自动获取。--ignore-preflight-errors=Swap是兜底参数,如果你已经关过swap就不需要,但我习惯保留它,避免云镜像预装的swap分区清理不干净导致卡在检查。初始化成功后,终端会输出一个kubeadm join命令,务必保存,这是工作节点接入集群的凭证。
初始化完成不代表集群可用,还差最后一层:CNI网络插件。这一步晚于控制平面启动,因为网络插件需要操作集群内的CRD和ServiceAccount。以Calico为例,先确认etcd没有异常,然后应用官方manifest:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26/manifests/tigera-operator.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26/manifests/custom-resources.yaml如果你的Pod网段不是192.168.0.0/16,需要先修改custom-resources.yaml里的cidr字段。很多人省略这步,最终节点NotReady或Pod一直ContainerCreating,只能回头看日志。安装完Calico,等到calico-system命名空间下的Pod都变为Running,控制平面才算就绪。
3.3 工作节点加入与集群状态验证
工作节点只需要装好kubeadm、kubelet、containerd,在主节点准备好的join命令后面加上--node-name指定节点名,就可以接入。这里有个容易忽略的细节:如果主节点初始化时用了--cri-socket,工作节点也要保持相同的criSocket路径,否则kubelet探不到运行时。join成功后,观察kubectl get nodes的状态:
kubectl get nodes -o wide输出中STATUS列从NotReady变到Ready通常需要几十秒。如果卡在NotReady,查看kubelet日志是第一步:journalctl -u kubelet -f。常见原因有CNI未生效、Pod网段冲突、或节点时间不一致。这里推荐一个相对冷门但高效的验证手法:在所有节点上执行kubectl get pod -n kube-system -o wide | grep,核对每个Pod是否被调度到预期节点,而不是只看节点状态。节点Ready只是表象,coredns调度到某个节点后是否能解析集群内域名,才代表网络链路真的OK。
4. 部署后的必备检查:命名空间、核心组件与DNS验证清单
4.1 用kubectl get pods验证核心组件
集群跑起来后第一件事不是急着部署业务,而是把控制面组件一个个梳理一遍。kubectl get pods -n kube-system能看到apiserver、controller-manager、scheduler、etcd、coredns、kube-proxy这些系统级Pod。注意观察每个Pod的READY状态,如果有0/1或CrashLoopBackOff,说明启动过程有问题。此时不要盲目重启,先看描述和日志:
kubectl describe pod -n kube-system coredns-xxxx -n kube-system kubectl logs -n kube-system coredns-xxxxdescribe能告诉你是镜像拉取失败、探针失败还是Volume挂载错误。比如之前遇到Readiness probe failed: HTTP probe failed with statuscode: 503,这种通常是coredns的Corefile被改坏了,或者网络插件还没就绪。日志则更直接,看见plugin/errors: etcd dial tcp 127.0.0.1:2379: connect: connection refused,说明coredns想连本机etcd但被拒绝,大概率是网络插件没起来导致coredns的Pod网络没有打通。
控制面组件的静态Pod清单在/etc/kubernetes/manifests/,如果你改过apiserver的启动参数,这里的yaml文件会被kubelet感知并自动重启。所以检查核心组件时,先看这个目录下的文件内容是否和预期一致,再去看Pod状态,能少走很多弯路。
4.2 部署nginx验证DNS与调度
系统组件全Running后,我会部署一个无状态应用来验证从容器调度到服务发现的全链路。最常见的验证对象是nginx,正好对应很多线上实际场景。创建一个最小部署:
kubectl create deployment nginx-test --image=nginx:1.25 --replicas=3 kubectl expose deployment nginx-test --port=80 --type=NodePort这里--replicas=3同时验证了多副本调度;--type=NodePort让外界能访问。接着检查Pod是否分布在多个节点,再通过ClusterIP和NodePort两种方式访问。kubectl get svc会看到ClusterIP和NodePort端口。在集群内用curl http://<cluster-ip>能通,说明kube-proxy的iptables规则正常;在节点外访问http://<node-ip>:<nodeport>能通,说明转发链没问题。再进到另一个Pod里测试DNS:
kubectl run -it --rm debug-pod --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default.svc.cluster.localkubernetes.default这个服务名解析成功,说明coredns工作正常。如果nslookup提示server can't find kubernetes.default,检查coredns的ConfigMap和网络策略。很多部署翻车不是主链路挂了,而是这类细节没有验证。比如DNS Pod运行在节点A,但业务Pod在节点B,B上的Pod访问DNS时走的是ClusterIP,如果当前节点的iptables规则没更新或conntrack表满了,就会出现间歇性解析失败。
5. Kubernetes部署避坑:5条血泪教训
5.1 现象:kubeadm init卡在拉镜像超时
这是我见过最多的问题。现象是执行kubeadm init后长时间停在[wait-for-control-plane],然后报错failed to pull image ... context deadline exceeded。原因是默认的registry.k8s.io镜像源在多数网络环境下连接不稳定。解决方法是先用ctr image pull测试到镜像仓库的连通性,再决定是配代理还是切内网仓库。我习惯在初始化前执行kubeadm init --dry-run,它能快速校验配置文件和镜像拉取能力,不会真的创建集群。如果发现仓库不走代理,把containerd的proxy配置加到/etc/systemd/system/containerd.service.d/http-proxy.conf里,然后daemon-reload重启containerd。
5.2 现象:节点NotReady但coredns一直CrashLoopBackOff
节点状态一直NotReady,kubectl get pods -n kube-system看到coredns反复重启。用journalctl -u kubelet查日志,看到Container runtime network not ready: NetworkPlugin cni failed to set up pod ...。这个问题几乎都是CNI网络插件没装好。Calico或Flannel的Pod没有Running,或者Pod网段与CNI配置不一致。解决办法是先卸载再重新安装CNI,并且严格核对podSubnet。很多人在kubeadm init时顺手写了10.244.0.0/16,但Calico的默认IPPool是192.168.0.0/16,两者不对齐,Calico的Pod能起来,但无法给业务Pod分配IP。卸载Calico时还要清理残留的/etc/cni/net.d/里的配置文件,不然重装后仍会读到旧的网段配置。
5.3 现象:svc无法访问但pod正常
Pod都Running,kubectl describe svc也看到Endpoints有地址,但从集群内部访问Service ClusterIP就是不通。排查方法从kubectl get svc -o yaml看selector匹配是否精确,再到节点上检查kube-proxy的iptables规则:iptables -t nat -L KUBE-SERVICES | grep <cluster-ip>。如果规则存在但请求不通,可能是开启了nf_conntrack的BE_liberal问题,或节点上的rp_filter配置太严格丢弃了返程包。最让人困惑的一次是多个节点上iptables规则完全一样,但只有某个节点上应用访问svc失败,后来发现是这个节点的conntrack_max太小,导致连接被丢弃。这类问题外表像网络插件故障,实际是内核参数问题。
5.4 现象:证书过期后集群拒绝连接
部署完三个月后,同事跑kubectl get nodes报Unable to connect to the server: x509: certificate has expired or is not yet valid。原因是kubeadm签发的证书默认有效期是一年,控制面证书、etcd证书、admin.conf里的客户端证书都会过期。解决方法是先备份/etc/kubernetes目录,然后用kubeadm certs renew all更新证书,更新完需要重启apiserver、controller-manager、scheduler这些静态Pod容器,以及更新用户侧的~/.kube/config:
sudo kubeadm certs renew all sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo systemctl restart kubelet这里要注意:etcd的证书虽然也会renew,但如果etcd是外部集群,kubeadm无法管理它的证书。生产环境我在部署第一天就会加一个定时任务,每个月检查一次kubeadm certs check-expiration,避免集群在凌晨悄无声息地断掉。
5.5 现象:部署nginx时镜像拉取失败
工作节点把业务Pod调度上去后,容器长时间ImagePullBackOff。kubectl describe pod看到Failed to pull image "nginx:1.25": rpc error: code = Unknown desc = failed to pull and unpack image ... connection refused。这不是Kubernetes的问题,而是工作节点上的containerd拉取Docker Hub镜像被限制。解决方式是给容器运行时配置registry mirror,如果你的环境允许,可以在/etc/containerd/config.toml里配置镜像仓库加速地址。对于生产环境,更可靠的做法是部署一个内部镜像仓库,业务镜像统一推送到内网,节点上的containerd将docker.io前缀替换为内网地址。这里尤其注意不要只改主节点,每个工作节点都要同步配置。
6. 部署完后的进阶验证:用kubectl top与etcd健康检查摸清集群家底
集群能跑不算完,我还会花一刻钟做一次“体检”,重点看资源水位和etcd状态。首先部署metrics-server,否则kubectl top nodes会报Metrics API not available。metrics-server从节点上通过kubelet的10250端口采集指标,但新集群的聚合层默认是打开的,主要障碍是证书。它需要连接kubelet的只读端口并校验server证书,反映到现象就是Pod起来了但无法采集数据。常见做法是在metrics-server部署参数里加--kubelet-insecure-tls或--kubelet-preferred-address-types=InternalIP,前者跳过证书校验,适合内网,后者避免它拿着节点hostname去解析。装上后:
kubectl top nodes kubectl top pods -n kube-systemtop nodes能看到每个节点的CPU和内存请求与实际的对比。如果某个节点CPU使用率已经上了70%,后续调度就要小心。top pods则能发现那些占了资源但没有实际工作的Pod。
etcd是Kubernetes的黑匣子,它不健康往往表现为集群间歇性不可用。我用三种方式验证它。第一种是看etcd Pod日志,kubectl logs -n kube-system etcd-<node>,如果有leader changed频繁出现,说明etcd在选主,可能是磁盘慢或网络抖动。第二种是直接调用etcd的metrics接口:
curl -s http://127.0.0.1:2379/metrics | grep etcd_server_has_leader返回1说明当前实例认为集群有leader,返回0说明它失去了leader视图。第三种是检查etcdctl endpoint health --cluster,如果某个节点返回failed,基本可以判定它的数据目录或WAL有问题。这些检查做完,你对集群的脾气就有数了。
最后分享一个我吃了不少亏后养成的习惯:每次部署完,把kubeadm init的配置、镜像版本、内核参数、CNI版本全部记到一个Markdown文件里,命名带日期。三个月后你大概率会忘了当初用的Calico是哪个补丁版本,找这份存档比翻历史记录快得多,也是换人接锅时唯一能靠住的文档。希望这个落地流程能帮你在Kubernetes部署上少走几趟夜路。
本文还有配套的精品资源,点击获取