1. 整体思路与部署架构选型
1.1 为什么选择离线手动搭建
先聊一个大家都会遇到的问题:学习K8S到底应该从哪里入手?很多人第一反应是装个minikube或者kind,一条命令拉起来,kubectl get nodes能看到一个Ready的节点,就觉得"我会K8S了"。但等真正上了生产,出了问题才发现自己完全不知道集群内部是怎么运转的。这就跟开车一样,你在驾校开自动挡和上了路开手动挡完全是两码事。K8S这个东西,如果不去手动搭建一次,你就永远停留在"会用工具"的层面,没有真正理解"工具"本身。
我这次选择离线手动搭建,其实是被环境"逼"出来的。某公司的内网服务器完全不能访问外网,但又需要一套K8S环境来做容器化部署的测试和验证。最开始我也想过曲线救国:在一台能上网的机器上装好所有镜像,然后打包带进内网。后来发现这个思路本身就是K8S离线部署的标配玩法。索性就把整个过程完整走了一遍,顺便把之前的很多知识盲区补上了。
手动搭建的好处在哪里?首先是你能真正搞清楚每个组件是干什么的。etcd存什么数据、kube-apiserver怎么和etcd交互、kube-scheduler怎么调度Pod、kubelet怎么和容器运行时通信。这些东西在自动化的安装工具里全都是黑盒,你只知道装完就能用,但出了问题就抓瞎。其次是手动搭建的过程中,你会被迫理解证书体系、网络插件的选择、Pod网段和Service网段的规划,这些才是K8S运维的核心能力。
1.2 核心组件与架构设计
在动手之前,先把K8S的架构理清楚。我这次搭建的是最经典的单Master双Worker架构,三个节点分别是master01、worker01、worker02。为什么不用高可用?因为这是做学习和验证用的环境,单Master足够覆盖绝大部分测试场景。如果以后要上生产,再做HA,原理是一样的,只是多了负载均衡这一层。
K8S的核心组件可以分成两个部分:控制平面和节点平面。控制平面包含kube-apiserver、kube-controller-manager、kube-scheduler、etcd四个组件,节点平面包含kubelet、kube-proxy和容器运行时。
组件角色一览:
| 组件 | 职责 | 关键点 |
|---|---|---|
| etcd | 集群状态存储 | 所有数据的唯一来源,坏了集群就瞎了 |
| kube-apiserver | 所有请求的入口 | 唯一能直接访问etcd的组件 |
| kube-controller-manager | 维持期望状态 | 节点控制器、副本控制器等一堆控制器 |
| kube-scheduler | 决定Pod跑在哪个节点 | 根据资源和约束做调度决策 |
| kubelet | 节点上的“眼睛和手” | 负责Pod的生命周期管理 |
| kube-proxy | Service流量转发 | 实现负载均衡和服务发现 |
这里最需要理解的是kube-apiserver的地位。所有的组件——kubelet、kube-scheduler、kube-controller-manager、kubectl——要读写集群状态,都必须经过kube-apiserver。它就像整个集群的“总闸门”,所有请求进来先经过认证、授权、准入控制三道关卡,然后才会真正写入etcd。理解了这一点,你就知道为什么在排查问题的时候,第一步总是看kube-apiserver是否正常。
1.3 版本选型与配套组件
版本选型这个问题,网上说法很多,但我给的建议很简单:选一个经过时间验证的稳定版本。我这次用的是1.23.17,为什么选这个版本?因为1.24开始,K8S把Dockershim移除了,如果你还习惯用Docker作为容器运行时,就需要额外安装CRI-Dockerd这个适配层。而1.23及之前的版本,Docker还是"原生支持"的,对初学者来说少了一道折腾。当然如果你已经适应containerd作为运行时,用更新的版本也没问题,只是我这次为了减少变量,选择了更稳妥的1.23。
配套组件版本也是一个萝卜一个坑:
| 组件 | 版本 | 说明 |
|---|---|---|
| Kubernetes | v1.23.17 | 主版本 |
| etcd | v3.5.6 | 存储集群数据 |
| containerd | v1.6.19 | 容器运行时,替代Docker |
| crictl | v1.24.0 | 命令行调试工具 |
| calico | v3.25.0 | CNI网络插件 |
这里多说一句为什么要用containerd而不是Docker。刚开始我其实想用Docker,毕竟熟悉嘛。但研究了一下发现,在当前这个阶段,containerd才是更"正统"的容器运行时。K8S通过CRI(Container Runtime Interface)和containerd直接交互,中间少了一个Docker的转换层,更稳定也更高效。而且后面如果想把集群升级到更新版本,用containerd可以省掉很多麻烦。
2. 离线环境的准备与前置条件
2.1 离线安装包的准备策略
离线安装最大的难点不是"装",而是"怎么把需要的东西都提前准备好"。
首先明确需要准备的几个包:K8S的三个核心组件包——kubelet、kubeadm、kubectl,而且必须指定版本。这三个东西在K8S官方的yum仓库或者apt仓库里都有,关键是要在能联网的机器上先下载好。我用的是yum方式,所以需要下载kubelet-1.23.17、kubeadm-1.23.17、kubectl-1.23.17这三个rpm包,以及它们的一堆依赖。
然后就是容器运行时。我选了containerd,需要下载containerd.io这个包。这里必须提醒一下:containerd的版本和K8S版本要兼容,1.6.x的containerd配合1.23.x的K8S是经过验证的组合。
接下来是镜像。K8S集群跑起来需要一堆镜像:kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、pause、coredns,还有网络插件calico需要的几个镜像。这一块是离线部署的重头戏,也是最容易出问题的地方。
提示:镜像的获取有一个小技巧,通过
kubeadm config images list可以列出指定版本K8S需要的全部镜像清单,然后逐个拉取、打包、拷贝。这一步必须在能联网的机器上提前完成。
2.2 服务器基础环境配置
三台服务器的操作系统我用的都是CentOS 7.9,配置是4核8G、100G磁盘。这个配置跑一个测试集群完全够了,但如果想让集群运行得更从容,建议Master节点给到8核16G,毕竟etcd和kube-apiserver都是吃内存的主儿。
系统层面的准备工作集中在三个方面:主机名、IP和防火墙。
主机规划:
| 主机名 | IP地址 | 角色 |
|---|---|---|
| master01 | 192.168.1.101 | 控制平面节点 |
| worker01 | 192.168.1.102 | 工作节点 |
| worker02 | 192.168.1.103 | 工作节点 |
设置主机名很简单,hostnamectl set-hostname master01,然后三台机器的/etc/hosts都要加上三行映射关系。这一步很多人会忽略,但非常重要——K8S组件之间通信依赖主机名解析,如果你不配hosts,后面kubeadm init的时候就会报"hostname not found"的错误。
然后就是关闭防火墙。很多人对"关闭防火墙"这件事有心理障碍,觉得不安全。这里要说明一下:在K8S集群内部,组件之间需要大量端口互通,你要是开着firewalld,就得额外配置一堆端口放行规则,把K8S的端口清单一个个加进去。对于学习和测试环境,直接systemctl stop firewalld加systemctl disable firewalld是最省事的。生产环境当然不能这么干,需要按端口白名单严格配置,这是后话。
SELinux也要关掉。这个说法可能有人不认同,但我的经验是:在CentOS 7上用K8S,关SELinux能省掉80%的疑难杂症。设置setenforce 0,然后修改/etc/selinux/config把SELINUX=enforcing改成permissive。网上也有不关SELinux硬上的,但那是给追求极致安全的强迫症准备的,不适合我们这种要快速落地场景。
2.3 内核参数与系统调优
这一块是很多人容易忽略的。K8S对系统的要求不只是"能跑",还需要一些内核参数的配合。核心是开启iptables对桥接流量的处理能力。
cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF sysctl --system逐条解释一下。bridge-nf-call-iptables表示让iptables能够处理经过Linux网桥的流量。这是K8S网络能不能正常工作的关键:Pod之间的通信要经过CNI网桥,如果这个参数没开,流量就无法被netfilter正确过滤和转发。ip_forward是开启IP转发,这个几乎是所有容器网络方案的硬性前提。vm.swappiness=0说得直白点就是尽量别用swap,因为容器场景下内存被swap出去会导致性能急剧下降,而且K8S在计算节点内存资源时,默认是不考虑swap的。
还有一个容易被忽视的点:需要加载br_netfilter模块。虽然在CentOS 7.9上默认会加载,但稳妥起见还是提前确认一下:
modprobe br_netfilter lsmod | grep br_netfilter3. 容器运行时与K8S组件安装
3.1 手动安装containerd
containerd的安装方式其实很简单,就是一个rpm包。但在装完之后,有两件重要的事情要做:一是配置cgroup驱动,二是配置系统服务。
containerd默认的cgroup驱动是systemd还是cgroupfs?这里有一个历史问题。K8S从1.21版本开始,kubelet默认使用systemd作为cgroup驱动,而containerd默认用cgroupfs。这俩如果不一致,就会出现一个让人很困惑的现象:kubelet起来之后报"failed to run Kubelet: failed to validate kubelet flags"或者cgroup相关的错误。解决办法是修改containerd的配置文件,让它也用systemd。
containerd的默认配置文件可以通过containerd config default > /etc/containerd/config.toml生成,然后修改两个地方。一个是SystemdCgroup,把它从false改成true;另一个是sandbox_image,这是Pause镜像的地址。
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sed -i 's#sandbox_image = "registry.k8s.io/pause.*#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#' /etc/containerd/config.toml这里要补充说一下sandbox_image这个参数的含义。Pause容器是每个Pod里的"根容器",它不干任何正事,就是占个位置,负责创建和维护Pod的网络命名空间,Pod里其他容器才能共享网络。如果Pause镜像拉不下来,整个Pod就创建不起来。所以在离线环境里,这一步提前在配置里指定好镜像源位置,能避免很多麻烦。
3.2 K8S核心组件包的安装
在安装之前,先把chache的/etc/yum.repos.d/kubernetes.repo配置好。但离线环境是访问不了这个仓库的,所以正确姿势是:在联网机器上把rpm包下载好,然后拷贝到三台服务器上,用yum install -y ./kubelet-1.23.17.rpm ./kubeadm-1.23.17.rpm ./kubectl-1.23.17.rpm本地安装。
如果遇到依赖问题,比如某个so文件缺失,我的建议是直接使用yum install --downloadonly方式把依赖也一并拉下来。
装完之后把kubelet、kubeadm、kubectl加开机自启并启动:
systemctl enable kubelet && systemctl start kubelet注意一点:现在kubelet会起得很早,它会因为找不到集群而疯狂报错,刷一堆"failed to connect to API server"的日志。这是正常的,不用慌。kubelet本身就是个不断重试的"打不死的小强",等kubeadm init完成之后它自然就正常了。
3.3 镜像的导入与校验
离线环境最大的坎儿就是镜像。K8S核心组件加上Calico网络插件,总共需要大概十四五个镜像,每一个都必须提前准备好。
具体流程是这样:在联网机器上,先拉取所有需要的镜像,然后通过docker save -o k8s-images.tar或者ctr -n k8s.io images export把镜像打包成一个tar文件,拷贝到内网服务器后再导入。
如果你用的是containerd,导入命令是:
ctr -n k8s.io images import k8s-images.tar注意-n k8s.io这个命名空间参数,这是K8S专用的镜像命名空间,如果不指定默认是default,导进去的镜像在K8S里根本看不到。
之前提到的kubeadm config images list是获取镜像清单的快捷方式。列出后逐个拉取即可,如下是1.23.17核心组件需要的镜像列表:
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.6-0 registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.6Calico需要的镜像相对独立,后面网络配置部分再单独说。
4. 证书体系与控制平面初始化
4.1 手动生成PKI证书
K8S的证书体系是整个集群安全的基础,众多组件之间通信全靠TLS双向认证。手动搭建的好处是在这个环节你会被迫搞清楚谁给谁签发证书,以及每个证书的用途。
大体的证书逻辑是:有一个集群的CA根证书,然后给apiserver签发一个服务端证书,给etcd签发证书,给每个节点的kubelet签发客户端证书。K8S集群里的所有组件都信任这个根CA,只要持有根CA签发的证书,就能互相认证。
证书要放在/etc/kubernetes/pki目录下。核心的证书文件如下:
| 证书文件 | 用途 | 说明 |
|---|---|---|
| ca.crt, ca.key | 集群根CA | 所有证书的信任根 |
| apiserver.crt, apiserver.key | API Server服务端证书 | 需要包含MasterIP和Service网段 |
| apiserver-kubelet-client.crt | 连接kubelet的客户端证书 | API Server访问kubelet时使用 |
| front-proxy-ca.crt | 聚合层CA | 扩展API Server时使用 |
| etcd/ca.crt | etcd集群CA | etcd组件专用 |
其实手动生成这些证书并不算太复杂,关键在于别把SAN(Subject Alternative Name)搞错。apiserver的证书SAN必须包含所有可能访问apiserver的地址,包括Master节点的IP、集群的Service网段第一个地址(通常是10.96.0.1)、还有"kubernetes"和"kubernetes.default"这些内置域名。漏掉任何一个,后面都会出现诡异的证书校验失败。
4.2 kubeadm init的执行与参数解析
说起来,手动搭建K8S其实很少有人真的去逐个证书手动用openssl生成,更多是用kubeadm init来"半自动"完成。这里的"手动"指的是不用自动化脚本来做,而不是说连kubeadm都不用。你依然需要自己规划IP、网段、镜像仓库,只是证书、配置文件的生成这一步kubeadm帮我们完成了大部分工作。
执行初始化:
kubeadm init \ --apiserver-advertise-address=192.168.1.101 \ --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \ --kubernetes-version=v1.23.17 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16逐个参数拆解一下。apiserver-advertise-address指定apiserver对外广播的地址,必须填Master节点的内网IP,不然其他节点找不到它。image-repository是镜像仓库地址,因为离线环境我们已经手动导入镜像了,这里其实不拉取,但这个地址会被写入kubeadm的配置里,后面token验证的时候还会用到。service-cidr是Service的虚拟IP网段,pod-network-cidr是Pod的IP网段,这两个地址池不能和物理机网段冲突,相互之间也不能重叠。我这次选的10.244.0.0/16是Calico默认推荐的Pod网段,10.96.0.0/12是K8S默认的Service网段。
提示:Pod网段和Service网段一旦确定,后期修改极麻烦,会牵涉到CNI配置和CoreDNS配置。集群一经初始化,这两个网段基本就锁死了。所以动手之前一定想清楚。
成功初始化之后,kubeadm会在末尾输出一串关键信息,包括:加入集群的token和证书校验哈希值、kubectl的config文件位置。务必截图保存,后面两个Worker节点加入集群就靠它了。
4.3 部署CoreDNS扩展组件
CoreDNS是K8S集群内部DNS服务,负责Service名的解析。如果你不在集群里部署CoreDNS,Pod之间就不能通过Service名访问彼此,只能靠IP,这在微服务架构中是不可接受的。
部署CoreDNS其实很简单,因为kubeadm会在init完成后自动部署,但前提是网络插件已经就绪。在没有CNI的情况下,CoreDNS会一直处于Pending状态。所以正确的顺序是先部署网络插件,再等CoreDNS正常。
在初始化之后检查:
kubectl get pods -n kube-system如果看到coredns的Pod是Pending,不用着急,这是正常的。后面部署好Calico之后,它会自动从Pending变成Running。
5. 网络插件的部署与集群联调
5.1 Calico的离线部署
集群的网络方案我选了Calico,它的优势在于支持多种网络模式,性能在CNI插件里属于第一梯队,而且配置灵活,既能做纯三层的BGP网络,也能用Overlay模式。
离线部署Calico的流程是:先把calico.yaml配置文件拿下来,然后把配置里镜像地址改成本地已经导入的镜像地址,再kubectl apply -f calico.yaml。
在配置Calico之前,有一个重要的参数要确认:CALICO_IPV4POOL_CIDR。这个参数是Calico里面Pod地址池的配置,必须和之前kubeadm init时设置的pod-network-cidr一致。比如我之前设置的Pod网段是10.244.0.0/16,那么calico.yaml里的CALICO_IPV4POOL_CIDR也要改成10.244.0.0/16,集群的网络才能正常通信。
注意:Calico部署的先后顺序虽然可以在kubeadm init之前或之后,但有一个大原则是必须在kubelet和kube-proxy工作正常之后、CoreDNS调度之前完成。因为在没有任何CNI插件的情况下,K8S是没有能力为Pod分配IP的,所有Pod都只能卡在ContainerCreating状态。
5.2 Worker节点加入集群
Worker节点的加入是整个过程中最流畅也最容易出错的环节。kubeadm init结束时的输出里有完整的join命令模板,把token和ca-cert-hash替换成自己环境的实际值即可:
kubeadm join 192.168.1.101:6443 \ --token l5d3qn.abcdef1234567890 \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx这个命令在worker01和worker02上各执行一次。执行成功之后,Master节点上验证:
kubectl get nodes正常情况下会看到三个节点处于NotReady状态。为什么是NotReady?因为kubelet虽然起来了,但它并不会主动告诉apiserver"我准备好了",要由kubelet上报自己的资源信息和状态,由controller-manager给它打上Ready标签。如果节点上网络插件还没就绪,或者kubelet上报有问题,节点就会一直停留在NotReady。
如果join的时候提示token过期或者证书校验失败,不要慌。直接用下面的命令在Master节点上重新生成:
kubeadm token create --print-join-command这条命令会输出一个新的join命令,直接复制就行。
5.3 网络状态验证与联通性测试
集群初始化的最后一步,是验证网络是否真的通了。我的验证方法是三板斧:看节点状态、看系统Pod状态、跑一个测试Deployment。
第一步,kubectl get nodes,看到三个节点都是Ready,说明节点层面的网络配置正确了。
第二步,kubectl get pods -n kube-system,看到所有Pod都是Running,包括calico-kube-controllers和calico-node。
第三步,创建测试用Pod,验证跨节点网络互通:
kubectl run test-nginx --image=nginx如果这个Pod能进入Running状态,说明整个网络链路已经打通了。再进一步做一次跨节点连通性测试:
kubectl exec -it test-nginx -- curl <pod_ip>能通,说明Calico的路由规则已经生效,Pod之间的三层网络没有问题了。
6. 实战坑点与排查经验
6.1 版本不匹配问题
在手动搭建过程中,版本不匹配是最折磨人的一类问题。这里分享一个我踩过的具体案例。
某次初始化的时候,kubeadm init报了一个"error execution phase preflight: unable to fetch the kubeadm-config ConfigMap"的错误。排查了很久,最后发现是kubeadm和kubelet的版本不一致。kubeadm是1.23.17,但kubelet被yum自动升级到了1.24。版本不同导致kubeadm生成的配置和kubelet实际行为不一致,最终卡在preflight检查。
这个问题常规文档不太会提,因为它和"正常安装流程"没关系,纯粹是离线环境里手忙脚乱搞出来的版本错乱。排查思路是:
kubeadm version kubelet --version kubectl version三个版本必须完全一致,差一个minor版本都不行。解决方法是把kubelet降级到和kubeadm一致的版本:
yum install -y kubelet-1.23.17 --nogpgcheck systemctl restart kubelet6.2 镜像拉取失败与命名空间陷阱
离线环境里镜像拉取失败是另一个高频问题。这里的坑点往往不在"拉取"本身,而在于镜像导入的命名空间不对。
之前提过,K8S使用的是containerd的k8s.io命名空间。如果你导入镜像时用了默认命名空间ctr images import而不是ctr -n k8s.io images import,kubelet在拉取镜像时会报"image not found",但实际上镜像已经在本地了。这个问题我至少遇到过三次,每次都是因为命令少写了-n k8s.io参数。
还有一个容易忽略的点:如果K8S组件使用默认的registry.k8s.io仓库,而你导入镜像时用的是阿里云仓库路径,也会导致镜像找不到。一定要保证镜像仓库地址一致。比如配置里写着registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6,那么导入的镜像也必须是这个完整路径。
6.3 节点NotReady的排查路径
节点一直NotReady是最让人头疼的问题,因为它可能由多个原因导致。我的排查路径有一个固定的优先顺序:
第一步,看kubelet状态:systemctl status kubelet,看有没有报错。如果是证书相关错误,去检查/etc/kubernetes/kubelet.conf是否存在且权限是否正确。
第二步,看节点上的容器运行时:crictl ps,如果这个命令报错,说明crictl连接不上containerd,检查/run/containerd/containerd.sock是否存在、crictl的runtime-endpoint配置是否正确。
第三步,看CNI插件是否就绪:ls /opt/cni/bin/,如果没有calico相关的可执行文件,说明CNI还在启动中或安装不完整。
第四步,看apiserver上的节点事件:kubectl describe node worker01,这里能看到具体的时间线,比如哪个探针失败、什么原因导致的NotReady。
这套排查路径在我手动搭建过程中多次验证过,效率很高。你不需要一开始就去翻日志,按照这个顺序一步步排除,基本十分钟内能定位到根因。
6.4 常见问题速查表
把这次手动搭建过程中遇到的坑整理成了一个速查表,方便大家对照:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| kubeadm init失败:port 6443 already in use | 端口被占用 | ss -lntp查端口占用的进程,释放端口 |
| 节点一直NotReady | CNI未就绪或kubelet异常 | 先看CNI状态,再看kubelet日志 |
| 镜像报not found | 导入命名空间不对 | 用ctr -n k8s.io images import重新导入 |
| CoreDNS一直Pending | 网络插件没跑起来 | 确认Calico部署成功后再观察 |
| kubectl无法连接apiserver | kubeconfig配置错误 | 检查~/.kube/config的server字段和证书路径 |
| 跨节点Pod网络不通 | Calico配置的Pod网段不一致 | 核对CALICO_IPV4POOL_CIDR和kubeadm的pod-network-cidr |
| kubelet疯狂重启 | 版本不一致或缺少依赖 | 核对kubelet/kubeadm/kubectl版本,检查依赖库 |
7. 常用运维命令与集群日常管理
7.1 节点与Pod的日常操作
集群搭好之后,日常管理主要是通过kubectl完成。这里把我常用的命令整理一下,都是一些高频操作。
查看节点状态:
kubectl get nodes -o wide查看集群里所有命名空间的Pod:
kubectl get pods -A -o wide查看某个Pod的详细信息和事件:
kubectl describe pod <pod-name> -n <namespace>查看Pod日志:
kubectl logs -f <pod-name> -n <namespace> --tail=200进入Pod内执行命令:
kubectl exec -it <pod-name> -n <namespace> -- /bin/bash这些命令是基础中的基础,但恰恰是日常最常用的部分。建议大家把这些命令的-o wide参数用起来,它能直接显示Pod跑在哪个节点、IP是什么,排查问题的时候非常高效。
7.2 证书过期与更新策略
K8S的证书有效期默认是一年。这个时间说长不长,说短不短,很多集群跑着跑着突然出现一堆奇怪的报错,一查才发现是证书过期了。
如果是kubeadm部署的集群,更新证书比较简单:
kubeadm certs renew all执行完之后重启相关组件:
systemctl restart kubelet对于自签的apiserver证书,更新之后需要重启kube-apiserver。但要注意,kube-apiserver是静态Pod,修改/etc/kubernetes/manifests/kube-apiserver.yaml之后会自动重启,不需要手动干预。
7.3 节点维护与移除
有时候需要把某个节点摘下来做维护,这时候要让K8S把Pod都赶走:
kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data维护完成后让节点重新参与调度:
kubectl uncordon worker02如果要彻底移除一个节点,顺序是:先drain、再删除节点、然后在该节点上执行kubeadm reset清空本地数据。
这两个命令在运维中必不可少,特别是当某个Worker节点要换硬件的时候,drain + uncordon的组合能让业务无感知地完成迁移。
8. 手动搭建的经验总结与后续扩展
这次离线手动搭建K8S集群,前后花了两天时间。第一天准备环境、拉镜像、踩版本坑,第二天正式部署然后反复调试网络。整个过程走完之后,我对K8S的理解和在"一条命令装好"时代形成的认知完全不一样了。你开始明白每个组件为什么要这么设计,哪些参数是牵一发动全身的,哪些错误信息背后真正的原因是什么。
如果让我给准备入门的同学一个建议:即便你以后用开源的集群管理平台做生产部署,也一定要找一套离线环境手动把集群搭一遍。因为你在手动搭建过程中积累的底层认知,会在未来每一次集群故障排查中变成你的直觉。
关于后续扩展,我打算在搭建好的集群上做三件事。第一件事是部署一套完整的微服务Demo,把Ingress、ConfigMap、Secret、PV/PVC这些核心资源都用起来,看看它们在真实业务中的应用方式。第二件事是尝试把集群升级到更新的版本,练练兵,毕竟版本升级在生产中是个高频话题。第三件事是试一下集群监控那套体系,看看从零开始构建可观测性的完整路径。
这一趟下来,我现在碰到网络问题第一反应不是去看什么知名博主写的"十种排查方法",而是自己从Pod网段、Service网段、Calico的路由规则这几个维度去推,反而解决问题更快一些。这就是手动搭建带来的底层能力,也是我愿意把这套流程完整记录下来的原因。希望这篇实践记录能给同样准备手动搭建K8S集群的同学一点参考,少走一些我曾经踩过的弯路。