上一篇刚把Pod调度策略讲完,这篇直接进入Kubernetes集群部署的完整实战。很多朋友手上有《深入理解Kubernetes源码》,但我的建议很直接:源码可以慢慢啃,集群先给我跑起来。你连一个多节点集群都没有,读调度器源码就像没摸过方向盘先看发动机原理,效果极差。这篇是Kubernetes基础系列的第四篇,核心只做一件事:从几台裸机或虚机开始,用kubeadm搭出一个可用的Kubernetes集群,并且让Nginx真实跑起来。适合已经玩过minikube、正准备搭多节点环境,或者被各种安装教程坑过几次的人。
这篇文章我尽量保持“给同事讲方案”的口吻,不会只丢命令,会把每个关键参数的来历和选择理由说清楚。整条链路包括环境准备、控制平面初始化、工作节点加入、CNI网络选型、业务应用部署和日常排障。看完之后,你应该能在半天内独立复现一套集群,并且以后遇到节点NotReady、镜像拉不下来这类问题,知道先查哪里、再查哪里,而不是漫无目的地百度。
1. 部署方式选型:为什么要以kubeadm为主线
1.1 四种常见部署方式的对比与选择
Kubernetes集群的部署方式五花八门,新手很容易被绕晕。我简单归纳成四类,各有各的适用场景:
二进制部署:从GitHub下载kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy等二进制文件,手动生成证书、配置systemd服务。这种方式最大的优点是“看得见每一块组件”,出了问题你知道是哪个服务挂了,排障思路特别清晰。但它的缺点也极其明显:部署时间极长,几百个步骤不能错一步,而且证书体系复杂,手动维护非常痛苦。我不推荐新手用这种方式作为学习路径,光生成CA证书和各个组件的kubeconfig就得花掉一整天。
kubeadm部署:这是CNCF官方推荐的引导工具。它把二进制部署里最繁琐的部分——证书生成、kubeconfig配置、控制平面组件静态Pod编排——全部自动化了。你只需要初始化控制平面,拿token让工作节点加入,再装一个CNI插件,集群就活了。我在这篇里主推的就是这种方式,也是目前社区里使用最广的部署方式。它足够透明:kubeadm生成的每个配置文件都在/etc/kubernetes目录下摆着,你随时可以查看。
发行版打包部署:比如RKE2、K3s、MicroK8s这类。K3s尤其适合边缘计算和资源受限的环境,把控制平面组件压缩进单个进程,安装一条命令就完成。这类工具的优点是快,缺点是抽象层太厚,很多底层细节被掩盖了。如果你刚开始学Kubernetes,用K3s搭集群很容易“知其然不知其所以然”。
云托管服务:比如各大云厂商的托管Kubernetes服务,你只需要创建集群,控制平面由平台帮你维护。这是生产环境最省心的选择,但完全屏蔽了集群创建过程。作为学习者,你不能只依赖它,因为一旦集群出问题,你根本不知道控制平面组件之间是怎么协作的。
1.2 kubeadm适合什么场景,不适合什么场景
kubeadm对自建集群来说是个“甜点级”工具。它默认把控制平面组件以静态Pod的方式托管给kubelet,这意味着apiserver、etcd这些组件在集群里是以容器形式运行的。这带来一个好处:以后控制平面组件升级,本质上是更换镜像版本,配合kubeadm upgrade命令就能一键完成。
它的适用场景非常明确:中小规模的自建集群、离线环境、混合部署环境,以及所有想深入理解Kubernetes工作原理的学习场景。如果你需要高可用,kubeadm也支持,只要在init时指定control-plane-endpoint指向一个负载均衡器,再在另外两台机器上执行kubeadm join --control-plane即可,这是生产环境比较推荐的组合。
不适合的场景也有:超大规模集群(千节点以上)建议走二进制或云托管,因为网络和etcd性能需要非常细致的调优;对非root权限环境、不可变基础设施要求高的场景,kubeadm的默认配置可能需要大量改写。另外,如果你完全没有Linux基础,也别急着上kubeadm,先把systemd、iptables、磁盘分区这些基本功补一补。
2. 动手前的主机规划与系统初始化
2.1 节点分工与网络CIDR设计
部署Kubernetes之前,先花十分钟规划网络,这个环节省掉,后面全是大坑。以三节点集群为例,标准分工是:一台控制平面节点(master),两台工作节点(worker)。生产环境建议控制平面至少三台做高可用,但对于学习环境,一台控制平面加两台工作节点完全够用。
网络规划核心是三段地址不能重叠:物理机网段、Pod网段、Service网段。物理机网段是你SSH连机器用的,假设是192.168.1.0/24。Pod网段是集群内部给每个Pod分配的IP地址池,kubeadm默认不指定的话是10.244.0.0/16或10.96.0.0/12(视CNI组件而定)。Service网段是给Service虚拟IP用的,默认是10.96.0.0/12。这两段地址必须和物理机网段、以及你公司内部其他网络完全隔离,否则路由器会把去往Pod的流量当成普通内网流量转发,造成诡异的路由冲突。
Pod网段的CIDR大小直接影响集群规模。10.244.0.0/16这个地址段一共包含65536个IP,假设每个节点默认最多跑110个Pod(kubelet的maxPods默认值),理论支撑500多个节点。如果你的规划超500节点,建议把Pod网段放大到/14甚至/12,同时把每节点Pod数上限下调,避免单节点资源争抢。Service网段设计要留余量,因为Service和Pod不同,Service本身数量有限,/12完全够用。
2.2 系统初始化五件事:内核模块、swap、防火墙、主机名、IP
在安装kubeadm之前,每个节点必须完成五项基础配置,我按顺序说明:
第一,加载内核模块。Kubernetes依赖iptables和IPVS做流量转发,需要确保br_netfilter、overlay等模块已经加载。建议在/etc/modules-load.d/k8s.conf里写入这两个模块,然后systemctl restart systemd-modules-load生效,再用lsmod | grep br_netfilter确认。
第二,关闭swap。这是新手最容易忽略的一步。kubelet默认要求swap必须为0,否则节点加入后kubelet会报错。虽然新版K8s支持Swap特性门控,但生产环境没人用它。执行swapoff -a只是临时关闭,必须把/etc/fstab里swap那行注释掉,否则重启后swap恢复,集群又挂。这一步不做,后续节点状态会反复NotReady。
第三,调整内核参数。重点设置net.bridge.bridge-nf-call-iptables=1,这个参数保证经过Linux bridge的流量能被iptables规则处理,是集群网络正常工作的前提。直接写进/etc/sysctl.d/k8s.conf并sysctl --system加载。
第四,hosts和主机名。每一台节点要有唯一的主机名,不能有重复。把集群所有节点的IP和主机名映射写进/etc/hosts,避免组件之间靠DNS解析互相找不到。
第五,防火墙和SELinux。协议层面,Kubernetes需要放行6443(apiserver)、2379/2380(etcd)、10250(kubelet)、以及Service NodePort的30000-32767端口段。很多人在集群外访问不到Pod,就是防火墙把这几个端口拦了。SELinux在RHEL系系统上建议先设为permissive,Kubernetes官方文档对SELinux的支持说明里,permissive是最稳妥的。
2.3 容器运行时选型与cgroup驱动对齐
容器运行时的选择直接影响kubelet能否正常监控容器资源。早期大家习惯用Docker,但Kubernetes从1.24版本开始移除了dockershim,现在主流是containerd和CRI-O。以containerd为例,它会随kubeadm一起安装或单独安装。安装完成后有一件事必须做:修改/etc/containerd/config.toml,把SystemdCgroup设置为true,同时把sandbox_image换成国内可访问的镜像地址。
为什么要这么改?Kubernetes的kubelet和容器运行时必须使用相同的cgroup驱动,否则kubelet无法准确感知容器占用的CPU和内存。用systemd作为cgroup驱动的逻辑是:systemd是pid 1,由它统一管理cgroup,容器运行时和kubelet都作为systemd的子进程存在,这样资源统计才准确。如果你用cgroupfs驱动,和systemd时不时产生“双重管理”的问题,轻则日志报错,重则节点资源统计错乱。我的建议非常直接:所有节点、所有组件,无脑统一SystemdCgroup=true,这是当前最不容易踩坑的配置。
3. 用kubeadm跑通控制平面初始化
3.1 安装kubeadm、kubelet、kubectl并锁定版本
安装这几个组件时,最大的坑是版本漂移。我见过很多人执行了yum install kubeadm kubelet kubectl以后,过一段时间跑yum update,结果kubelet升级而kubeadm还是旧版,或者kubelet和kubernetes控制平面版本不一致,整个集群翻车。正确的做法是安装时明确指定版本,并且把这些软件包加入yum的版本锁定列表。
以RHEL系的安装为例,先配置国内可访问的软件源(kubeadm官方源在部分网络环境下可能不稳定),再执行:
yum install -y kubelet-1.28.2 kubeadm-1.28.2 kubectl-1.28.2 systemctl enable kubelet注意,这个阶段只需要enable kubelet,不要start。因为kubelet还没有拿到集群配置,启动起来会一直报错找不到静态Pod配置,这会干扰后续初始化。等到kubeadm init完成,kubelet自动被唤醒。
用apt的Debian系也一样,关键是三个组件的版本必须一致。我的习惯是记一个版本组合,比如1.28.2就都是1.28.2,小版本差异也可能导致API兼容问题。
3.2 kubeadm init核心参数拆解与镜像源设置
环境准备好以后,在控制平面节点上执行初始化。这条命令我拆开讲解,每个参数都有自己的使命:
kubeadm init \ --kubernetes-version=v1.28.2 \ --apiserver-advertise-address=192.168.1.10 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --image-repository=registry.aliyuncs.com/google_containers--apiserver-advertise-address指定apiserver对外广播的地址,必须是控制平面节点的内网IP。注意不要用公网IP,除非你有特殊需求。--pod-network-cidr是Pod地址池,这里选的10.244.0.0/16是Calico默认推荐的网段,如果把Pod网段改了,后面安装Calico时也要同步改。
--image-repository的作用是换镜像仓库。kubeadm默认从registry.k8s.io拉取控制平面镜像,这个域名在国内的网络环境下经常超时。换成国内可访问的镜像仓库后,kubeadm会从对应镜像库拉取apiserver、etcd、coredns等镜像,速度稳定很多。企业环境更推荐的方式是提前用kubeadm config images pull把镜像拉到本地,再推到自建的Harbor私有仓库,集群安装时通过--image-repository指向内网地址,完全离线部署。
初始化成功后会输出一段提示,包括三个信息:/etc/kubernetes/admin.conf的位置、kubelet已启动的确认、以及一段worker节点加入的命令。这段join命令要保存下来,后面加节点要用。如果你当场忘了,也可以随时用kubeadm token create --print-join-command重新生成。
3.3 安装CNI插件:以Calico为例
kubeadm init完成后,集群的网络处于“没有路”的状态——Pod之间无法通信。这一步由CNI插件负责,主流选择是Calico和Flannel。我首选Calico,原因有三:性能好(基于BGP路由而非纯overlay封装)、支持NetworkPolicy、排障工具链完整。
安装Calico有两种方式。老版本是直接kubectl apply它的operator或manifest文件,新版本推荐用operator方式:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/tigera-operator.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/custom-resources.yaml如果之前的Pod网段不是10.244.0.0/16,需要先下载custom-resources.yaml,把里面的cidr字段改成你自己的网段再apply。Calico安装后,可以观察calico-system命名空间下的Pod是否全部Running,然后查看节点是否变为Ready。如果IPVS或iptables有历史残留配置,Calico可能会起不来,通常把节点重启一次就能解决。
4. 工作节点加入与集群状态验证
4.1 获取和重建join token
工作节点加入集群靠的是token和证书哈希。kubeadm init成功后会打印完整的join命令,格式类似:
kubeadm join 192.168.1.10:6443 --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>这个token默认有效期是24小时。如果你第二天才加节点,或者token过期了,不必重新初始化集群。在控制平面节点上重新生成即可:
kubeadm token create --print-join-command这条命令非常高频,值得记死。它会自动生成新的token,并且把discovery-token-ca-cert-hash也打印出来,直接复制到工作节点执行就行。
有一种情况容易让人困惑:如果你给工作节点加了--control-plane参数,那就是要把这个节点变成第二个控制平面节点(用于高可用),而不是普通工作节点。只有需要扩展控制平面时才能用这个参数。
4.2 节点加入后的验证清单:从节点状态到Pod调度
节点加入集群后,不能只看状态Ready就完事。我习惯按一个固定清单逐项验证:
第一步,在控制平面执行kubectl get nodes,确认所有节点状态都是Ready,并且角色标识正确。如果某个节点状态是NotReady,大概率是CRI配置或网络插件问题,先去排查kubelet日志。
第二步,查看节点详细信息kubectl describe node <node-name>,重点看Conditions部分是不是都显示True,以及底部的Events里有没有warning。Events里如果出现Failed to create pod sandbox,基本可以判定containerd配置没改对。
第三步,验证Pod能否跨节点调度。在部署Nginx时设置replicas为3,观察这3个Pod是不是分布到不同节点上。如果全部挤在同一个节点,建议检查节点是否有污点(taint)没去除。默认情况下控制平面节点有污点,工作节点没有,所以3个副本应该分布在两台worker上。
第四步,验证DNS是否工作。进入一个测试Pod执行nslookup kubernetes.default.svc.cluster.local,能解析出来说明CoreDNS正常。DNS是集群内部服务发现的基础,这一步挂了会导致各种奇怪问题。
5. 跑起第一个业务:Nginx从YAML到外部访问
5.1 用Deployment声明Nginx实例
集群搭好,接下来让业务跑起来。Deployment是Kubernetes里最常用的工作负载类型,它管理一组副本Pod,并且支持滚动更新和回滚。创建一个nginx-demo.yaml文件:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo namespace: default spec: replicas: 3 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80执行kubectl apply -f nginx-demo.yaml后,用kubectl get pods -o wide查看。这里有个细节:为什么image要用nginx:1.25-alpine而不是nginx:latest?因为latest标签不具备可重复性,今天拉的和明天拉的可能不是同一个版本。生产环境里镜像必须指定精确版本,这是最基础的可复现性要求。
另外,selector.matchLabels必须和template.metadata.labels完全匹配,否则Deployment创建时会报错。这个字段的设计是为了让Deployment能够找到它管理的Pod,两者的关系就像“招聘要求”和“候选人简历”,匹配不上就没法管理。
5.2 Service的三种暴露方式怎么选
Pod本身是“短命”的,随时可能被重新调度,IP地址会变。如果让外部流量直接访问Pod IP,那等于别人给你一个随时会变的电话号码。Service就是Pod前端的固定入口,它把一组Pod的流量汇聚到同一个虚拟IP上。
Service的类型选择,我按场景区分:
ClusterIP:默认类型,只能在集群内部访问。适合服务间互相调用,比如后端API给前端提供数据。你无法从集群外直接访问ClusterIP。
NodePort:在ClusterIP基础上,每个节点开放一个端口(默认范围30000-32767),流量经过节点端口转发到Service,再转发到Pod。适合测试环境和小规模暴露,比如开发阶段调试Nginx页面。它的劣势是每个服务都要占一个节点端口,服务多了端口管理混乱。
LoadBalancer:云环境下专用,会调用云厂商的负载均衡器把NodePort暴露到公网。自建集群通常没有这个外部组件,除非你装了MetalLB这类软件负载均衡。
三者关系可以理解为:ClusterIP是“内部电话分机”,NodePort是“外线总机”,LoadBalancer是“前台秘书”。生产环境推荐用Ingress统一入口,这个下面单独讲。
5.3 NodePort端口段与Ingress的关系
以Nginx为例,如果用NodePort暴露服务,yaml是这样的:
apiVersion: v1 kind: Service metadata: name: nginx-demo-svc spec: type: NodePort selector: app: nginx-demo ports: - port: 80 targetPort: 80 nodePort: 30080端口字段有讲究:port是Service对外暴露的端口,targetPort是容器监听的端口,nodePort是每个节点上开给外部访问的端口。nodePort如果不写,系统会在30000-32767里随机分配。显式指定30080的好处是可预期、方便防火墙放行。
这里必须提醒:NodePort的端口范围由apiserver的--service-node-port-range参数控制,如果想改范围,需要在kubeadm init或apiserver配置里预先设置,集群初始化以后再改非常麻烦,通常要改静态Pod参数并重启apiserver。
那Ingress和NodePort是什么关系?Ingress本质上是集群内部的七层负载均衡器,它通过一个统一的入口(通常也是NodePort或LoadBalancer暴露的入口)接收HTTP流量,再根据域名和路径路由到不同Service。比如nginx.example.com路由到Nginx服务,api.example.com路由到API服务,只需要一个入口IP。这就是生产环境服务多了以后,NodePort不够用的终极解法。部署Ingress Controller(比如nginx-ingress或traefik)的时候,它自身会暴露一个NodePort端口,后续所有服务都通过路径或域名转发,不需要每个服务单独申请端口。这套架构值得专门写一篇,这里先理解概念。
6. 集群日常运维:命令速查与证书管理
6.1 kubectl高频查询命令速查
集群部署完以后,日常用得最多的就是下面这组命令,我直接整理成速查表:
| 查询目标 | 命令 | 说明 |
|---|---|---|
| 全部节点状态 | kubectl get nodes -o wide | 查看节点IP、内核版本、运行时版本 |
| 命名空间下所有资源 | kubectl get all -n <ns> | 包含Pod、Service、Deployment等 |
| 查看Pod日志 | kubectl logs <pod> -c <container> --tail=100 | 多容器时必加-c参数 |
| 进入Pod交互 | kubectl exec -it <pod> -- /bin/sh | 注意镜像里有没有shell |
| 查看事件 | kubectl get events --sort-by=.metadata.creationTimestamp | 排障时第一手线索 |
| 查看资源YAML | kubectl get deploy nginx-demo -o yaml | 对比实际状态与期望状态 |
有一个技巧特别实用:kubectl get pod -o wide显示出的是Pod的IP和所在节点,但如果要确认Pod是不是真的在预期节点上,更准确的方式是kubectl get pod -o custom-columns=NODE:.spec.nodeName,POD:.metadata.name。这可以避免你对着-o wide的NODE列猜半天,尤其在网络组件有特定调度需求时非常有用。
再说一个很多人不知道的:kubectl top node可以看到节点CPU和内存实时使用率,前提是集群装了metrics-server。没装的话这条命令会报错。metrics-server是生产集群的基本组件,强烈建议装,一条kubectl apply就能搞定,它会通过kubelet的Summary API采集资源数据,不依赖第三方存储。
6.2 证书续期、版本升级与备份要点
Kubernetes的证书有效期问题非常经典。kubeadm默认给apiserver、etcd等组件签发的证书有效期是一年。集群跑了一年以后,你会发现kubectl突然报证书过期。这就是没有提前做证书管理的后果。
检查证书有效期的方法:
kubeadm certs check-expiration续期的标准动作:
kubeadm certs renew all systemctl restart kubelet这两个命令需要在控制平面节点执行,并且建议先备份/etc/kubernetes目录。kubeadm certs renew all会把所有证书统一续期,然后重启kubelet让组件加载新证书。注意证书续期后不需要重新初始化集群,也不需要重新加入工作节点,但如果你使用的是高可用控制平面,每台控制平面节点都要做一次续期。
版本升级方面,我的经验是:升级前先把文档读三遍,再在测试环境完整演练一遍。kubeadm升级路径是先升级kubeadm,再kubeadm upgrade plan查看可升级版本,然后执行kubeadm upgrade apply <版本>,最后逐个节点升级kubelet。升级过程中最怕的是跨大版本升级,比如1.27直接跳到1.29,很多API版本不兼容,遇到问题极难排查。
最后强调备份。etcd是集群的“数据库”,备份etcd是重中之重。最简单的方式:
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-snapshot-$(date +%F).db把这条命令加进crontab,每天凌晨执行一次,再结合对象存储异地保存,遇到误删资源或升级翻车时能救命。
7. 集群部署高频故障排查实录
7.1 镜像拉不下来的合规解决方式
新装集群时最常遇到的现象是Pod卡在ImagePullBackOff。拉取失败的原因通常有两类:一是镜像仓库网络不通,二是镜像名写错或源仓库不存在。
合规且高效的解决方案有两个思路。第一个是用国内可访问的公共镜像仓库,比如阿里云的容器镜像服务。kubeadm可以指定--image-repository=registry.aliyuncs.com/google_containers,对于业务镜像,可以在拉取前给镜像地址加前缀,比如docker pull registry.aliyuncs.com/google_containers/nginx:1.25-alpine,再通过docker tag打成不带前缀的名字。第二个方案是建立私有镜像仓库,比如Harbor,把需要的镜像提前pull下来再push到内网仓库,所有节点从内网拉取。这个方案对企业用户最友好,速度最快而且不受公网环境影响。
排查时先kubectl describe pod <pod>看Events里ImagePullBackOff的完整报错,再在有问题的节点上手动执行crictl images查看节点本地已有镜像。注意crictl是containerd的命令行工具,不是docker。很多用containerd的人习惯性敲docker images,发现什么都看不到,其实不是没有镜像,是工具用错了。
7.2 节点NotReady的排查顺序:kubelet、CRI与CNI
节点NotReady是集群运维里出现频率最高的问题。我的排查顺序是:先kubelet,再CRI,最后CNI。绝对不要一上来就重启节点,也不要直接重装系统。
第一步,登录节点执行journalctl -u kubelet -f --since 10min看kubelet日志。最典型的报错是failed to initialize top level QOS containers或container runtime is not running。前者通常是cgroup驱动不一致,后者是containerd没启动或配置坏了。cgroup驱动不一致的修复办法:检查/etc/containerd/config.toml里SystemdCgroup是不是true,以及kubelet的启动参数里cgroup-driver是不是cgroupfs,两者必须一致。
第二步,执行crictl ps和crictl pods,如果connect失败,那就是CRI的问题。常见原因是containerd服务挂了,或者containerd的socket路径不对。检查systemctl status containerd,如果服务正常但crictl连不上,大概率是把socket路径配错了。
第三步,如果kubelet正常、CRI也正常但节点依然NotReady,看CNI有没有问题。ls /etc/cni/net.d/目录是否存在配置,kubectl get pods -n kube-system看Calico相关Pod的状态。如果你之前装了Flannel又换成Calico,旧配置文件没删干净,CNI插件会加载混乱,节点状态直接给你好看。解决方式是删除旧的CNI配置文件和残留的虚拟网卡(比如flannel.1),重启kubelet,等Calico重新创建路由。
7.3 Pod Pending和CrashLoopBackOff的定位思路
Pod卡在Pending状态,代表它还没被调度到任何节点上。这时候kubectl describe pod显示的Events是排障的第一手资料:
0/3 nodes are available: 3 node(s) had untolerated taint表示节点有污点,你的Pod没有对应的容忍。最常见的是控制平面节点自带的node-role.kubernetes.io/control-plane污点,业务Pod默认调度不到控制平面节点上。Insufficient cpu或Insufficient memory表示节点资源不够,要么减少副本数,要么给节点扩容。FailedScheduling且没有其他详细原因,可能是某个节点的调度器异常,检查kube-scheduler Pod状态。
CrashLoopBackOff则是Pod启动后立刻退出,然后又反复重启。这一步先看日志:kubectl logs <pod>,如果日志为空,加--previous参数看上一次容器的日志。这个参数非常重要,因为容器crash后新容器启动,默认看的是新容器的日志,往往是空的。加上--previous才能看到崩溃时的完整输出,里面通常直接写着报错原因。
这类问题九成是配置错误:环境变量写错、配置文件挂载路径不对、启动命令里的占位符没替换。先把日志里的报错关键词搜索一遍,再检查启动命令和入参,最后才考虑是不是镜像本身有问题。我见过不少人一看到CrashLoopBackOff就重新构建镜像,实际上是yaml里少了几个引号,白折腾半天。
最后分享一点个人体会
Kubernetes集群的部署,本质上是一个“解决依赖”的过程:系统依赖、运行时依赖、网络依赖、调度依赖。你一步步把这些依赖理顺,集群自然就起来了。我前前后后搭过不下几十次集群,从最初的二进制逐组件拼装,到后来熟悉kubeadm的每个参数和默认值,最大的体会是:千万别急着“背命令”,而是要把每个操作背后的“为什么”想明白。比如为什么要关swap、为什么要让cgroup驱动统一、为什么Pod网段不能跟物理网段重叠——这些原理想通了,你遇到问题就有方向,而不是靠猜。
kubeadm这套部署流程,到现在依然是社区最稳的基线方案。照着这篇的顺序从头到尾走一遍,你会对“声明式API”有非常具体的体感:当你用kubectl apply一个YAML文件,集群自发地把Pod调度到了预期节点,网络策略生效,流量正常流转,那种“一切尽在掌握”的感觉,是看多少文档都换不来的。后续我会再抽时间写一篇关于单节点集群的性能优化和控制平面高可用的进阶文章,先把基础地基打扎实再说。