1. 为什么我建议你在Ubuntu上部署k8s
先交代一下背景。我手头有台闲置的台式机,配置是i5-10400、16G内存、512G SSD,原本装的是Windows 10,跑几个虚拟机就卡得不行。后来我直接格式化装了Ubuntu 22.04 LTS,然后在这台机器上从零开始搭了一套单节点的Kubernetes集群,也就是大家常说的k8s。整个过程踩了不少坑,也总结出了一套比较顺手的流程,今天分享出来,希望能帮到正在折腾或者准备折腾的人。
先说结论:如果你手里有一台内存不低于8G的Linux机器,或者想在VMware虚拟机里练手,用Ubuntu部署k8s是非常合适的选择。原因有三点。第一,Ubuntu对容器相关的内核特性支持很完整,比如cgroups、namespace、overlayfs这些,开箱即用,不需要额外折腾内核参数。第二,Ubuntu的用户基数大,遇到的问题基本都能搜到答案,不会像某些小众发行版一样卡住半天查不到资料。第三,k8s官方文档和社区的安装脚本,默认就是针对Ubuntu/CentOS这类主流发行版写的,踩坑成本最低。
这篇文章适合谁看?如果你刚接触k8s,想在自己的电脑上搭一个环境练手;或者你已经在用Docker,想更进一步理解容器编排是怎么回事;再或者你要准备面试,需要一个本地环境验证各种概念——这篇文章都适合你。我会从环境准备、Docker安装、k8s组件部署、集群初始化、问题排查这几个角度,完整走一遍流程。内容偏实操,所有命令都是我在真实环境中跑过的,你可以直接照着复制。
另外多说一句,k8s和Docker的关系很多人容易搞混。简单理解就是:Docker是容器运行时,负责把容器跑起来;而k8s是容器编排平台,负责管理一堆容器——谁该跑、跑几个、挂了怎么恢复、流量怎么分,这些是k8s干的事。后面我会详细展开,现在先不说太多,直接从环境准备开始。整个部署过程大概需要40分钟到1小时,取决于你的网络状况和机器配置。
2. 部署前的环境准备与版本选型
2.1 硬件与系统要求
先聊聊硬件。k8s本身其实不算特别吃资源,真正吃资源的是跑在它上面的业务容器。如果只是搭一个学习环境,2核4G的机器就能跑起来,但体验会比较紧。我实际测试下来,单节点集群建议至少4核8G,这样既能跑系统组件,又能留出余量跑几个测试用的Pod。如果你想在VMware虚拟机里装,建议给虚拟机分配2核4G起步,4核8G会更舒服。
系统方面,我推荐Ubuntu 22.04 LTS或者24.04 LTS。LTS版本维护周期长,软件源里的包比较稳定,社区资料也最多。如果你手头是20.04也可以,但要特别注意内核版本和k8s版本的兼容性,后面我会详细说。
2.2 版本选型:别用最新的,用最稳的
这里必须给新手提个醒:k8s版本更新非常快,大概每三个月发一个大版本,每个版本只维护一年左右。如果你直接装最新版,遇到问题去搜资料,很多解决方案都是针对旧版本的,容易对不上号。我个人的习惯是选择当前时间点往前推一两个月的稳定版本,避开刚发布的大版本。
我这次部署用的是k8s v1.28.2,搭配containerd 1.7.x和crictl v1.28.0。选这个组合的原因是:v1.28是当时比较成熟的版本,containerd 1.7是稳定分支,两个版本在社区里经过了大量生产环境验证,兼容性问题最少。如果你是照着这篇文章操作,建议也锁定这些版本,不要随意升级,否则后面的配置可能会有出入。
2.3 网络规划与主机名设置
在开始安装之前,有几项基础配置需要先做好。首先是主机名。k8s集群的节点名称默认取主机名,所以最好提前把它设置成有意义的名称。我这台机器设置的是k8s-master,用下面的命令修改:
sudo hostnamectl set-hostname k8s-master其次是hosts文件。如果你的集群有多个节点,需要把各节点的IP和主机名对应关系写进去,方便互相解析。单节点集群虽然不需要,但养成好习惯没坏处:
sudo tee -a /etc/hosts <<EOF 192.168.1.100 k8s-master EOF还有一项是防火墙。Ubuntu默认安装了ufw,如果开着防火墙,k8s的很多端口会被拦截。学习环境最简单粗暴的做法是先关掉防火墙:
sudo ufw disable sudo systemctl stop ufw如果你在真实生产环境,不能直接关防火墙,那就需要把k8s相关的端口都放行。这里我把主要端口整理成了一个表,方便你参考:
| 组件 | 端口 | 用途 |
|---|---|---|
| kube-apiserver | 6443 | Kubernetes API 服务 |
| etcd | 2379-2380 | 集群数据存储 |
| kubelet | 10250 | 节点状态上报和容器管理 |
| kube-scheduler | 10259 | 调度器 |
| kube-controller-manager | 10257 | 控制器管理器 |
| NodePort 服务 | 30000-32767 | 外部访问服务端口 |
2.4 内核参数调整
接下来是关键的一步:调整内核参数。k8s依赖一些网络和文件系统相关的内核特性,需要手动开启。这里有个坑,如果你跳过这一步,后面初始化集群时大概率会报错,而且报错信息还不直观,排查起来很浪费时间。
需要修改的配置主要是两个:一个是启用overlay2文件系统驱动,一个是启用br_netfilter模块。overlay2是容器镜像分层存储的基础,br_netfilter则负责处理桥接流量的iptables规则。直接用下面的命令写入配置:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter然后创建sysctl配置:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system第一条net.bridge.bridge-nf-call-iptables的作用是让桥接的流量也经过iptables过滤。如果不开启,Service的ClusterIP转发会出问题,表现就是Pod之间通信正常,但通过Service访问就是不通。第二条net.ipv4.ip_forward是开启IP转发,这个是Pod网络和外部通信的基础。我当初漏掉第一条,结果集群初始化的网络插件一直报错,折腾了两个小时才定位到问题。
3. 容器运行时安装:containerd而不是Docker
3.1 为什么k8s用containerd而不是Docker
再说一次k8s和Docker的关系。早期的k8s确实直接支持Docker作为运行时,但后来Docker Inc.把containerd捐献给了CNCF,k8s从1.20版本开始逐渐弃用Docker作为运行时,1.24版本彻底移除了对Docker的直接支持。现在k8s默认的容器运行时是containerd,它更轻量,只负责容器的生命周期管理,不包含Docker那些构建镜像、管理网络等额外功能。
那是不是说Docker就没用了?不是。k8s仍然可以通过CRI接口兼容Docker生态的镜像,也就是说你之前用Docker打包的镜像,在k8s里照样能跑。只是k8s不再直接调用Docker命令来管理容器了,转而通过containerd来操作。
具体有个Linux命令能体现这个区别——docker ps和crictl ps。以前排查k8s节点上的容器,习惯用docker ps,现在要养成的习惯是用crictl命令。crictl是专门为CRI接口设计的命令行工具,社区标准的容器排查工具,后面我会详细演示它的用法。
3.2 安装containerd
这里我推荐直接用apt安装。Ubuntu 22.04的软件源里自带containerd 1.6.x版本,装起来最省事:
sudo apt update sudo apt install -y containerd装完之后先别急着用,需要生成一份默认配置文件,再改几个关键参数。containerd的默认配置不包含CRI插件的完整设置,必须手动生成:
sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml生成之后,有两个地方要改。第一个是SystemdCgroup参数,默认是false,要改成true。这个参数决定了容器的cgroup驱动方式。k8s的kubelet默认使用systemd作为cgroup驱动,如果containerd这边还是cgroupfs,两边就对不上,初始化集群时kubelet会一直报错。
修改方式是编辑config.toml,找到[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]这一段,把SystemdCgroup改成true:
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml第二个要改的是sandbox_image。这个是k8s用来启动Pod的pause镜像地址。默认指向的是k8s.gcr.io,这个地址在国内访问经常超时。我改成了国内镜像源提供的地址:
sudo sed -i 's#registry.k8s.io/pause:3.6#registry.aliyuncs.com/google_containers/pause:3.9#' /etc/containerd/config.toml改完之后重启containerd:
sudo systemctl restart containerd sudo systemctl enable containerd这里有个小细节。sandbox_image的版本要和k8s版本匹配,否则可能出现PullImage超时或者版本不兼容的问题。如果你的k8s版本比我这个新,注意检查pause镜像的版本号是否需要调整。
3.3 安装crictl
crictl是排查容器问题的利器。它专门用来和CRI兼容的运行时交互,比如containerd。安装方法很简单,下载二进制文件即可:
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.28.0/crictl-v1.28.0-linux-amd64.tar.gz tar -zxvf crictl-v1.28.0-linux-amd64.tar.gz sudo mv crictl /usr/local/bin/装好后配一个配置文件,告诉crictl怎么连接containerd的socket:
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF配置完成后,可以用crictl version验证是否正常。我装完后顺手测了一下,能看到containerd的版本信息,说明连接没问题。
注意:crictl版本最好和你的k8s大版本保持一致。比如k8s是v1.28,crictl也用v1.28.x,避免出现接口字段不兼容导致的异常输出。
4. k8s核心组件安装与集群初始化
4.1 添加apt源并安装kubeadm、kubelet、kubectl
k8s的官方apt源地址是pkgs.k8s.io,这个地址在部分网络环境下可能不太稳定。我这里用的配置方式是先指定版本号,避免apt自动装成最新版。具体操作如下:
sudo apt update sudo apt install -y apt-transport-https ca-certificates curl gpg sudo mkdir -p /etc/apt/keyrings curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt update sudo apt install -y kubelet=1.28.2-1.1 kubeadm=1.28.2-1.1 kubectl=1.28.2-1.1这里有几个细节值得展开说一下。
首先,为什么用kubeadm而不是二进制方式安装?kubeadm是k8s官方提供的集群初始化工具,它能自动完成证书生成、kubeconfig配置、组件静态Pod编排等一堆繁琐工作。对学习环境和中小型生产环境来说,kubeadm是效率最高的选择。二进制方式适合对原理有深入了解需求的人,但初次部署不建议这么折腾。
其次,apt源为什么要指定版本?因为k8s的apt源里默认只保留最新的几个版本,如果你直接apt install kubeadm,很可能装的就是最新版。而kubeadm、kubelet、kubectl这三个组件必须保持版本一致或在小版本范围内兼容,否则集群初始化时会报版本不匹配的错误。我习惯把它们锁死在同一个版本。
最后运行下面命令锁定版本,防止后续apt upgrade把k8s组件升级了:
sudo apt-mark hold kubelet kubeadm kubectl4.2 使用kubeadm初始化集群
初始化之前,需要先确认一件事:kubeadm会从镜像仓库拉取一组基础镜像。默认地址是registry.k8s.io,国内访问存在超时风险。如果你网络没问题可以直接初始化,否则建议先拉镜像再初始化。
查看需要哪些镜像:
kubeadm config images list输出大概长这样:
registry.k8s.io/kube-apiserver:v1.28.2 registry.k8s.io/kube-controller-manager:v1.28.2 registry.k8s.io/kube-scheduler:v1.28.2 registry.k8s.io/kube-proxy:v1.28.2 registry.k8s.io/pause:3.9 registry.k8s.io/etcd:3.5.9 registry.k8s.io/coredns/coredns:v1.10.1如果你发现拉不下来,可以先把镜像从国内源拉取,再重新打tag成registry.k8s.io的地址。比如这样:
docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 docker tag registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 registry.k8s.io/kube-apiserver:v1.28.2注意,我这里用了docker命令,但前提是你机器上已经装了docker。如果你没有docker,可以用crictl配合containerd来拉镜像,命令是:
sudo crictl pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 sudo crictl tag registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 registry.k8s.io/kube-apiserver:v1.28.2我实际测试下来,如果只跑单节点集群,最快的办法是直接用kubeadm的配置文件指定镜像仓库为国内源。创建一个kubeadm配置文件:
cat <<EOF > kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.1.100 bindPort: 6443 nodeRegistration: criSocket: unix:///run/containerd/containerd.sock --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 imageRepository: registry.aliyuncs.com/google_containers networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 EOF解释一下这个配置。InitConfiguration里的advertiseAddress是当前节点向集群宣告的IP地址,要填你机器的实际内网IP。criSocket指定容器运行时的socket路径,因为这里用的是containerd。ClusterConfiguration里的imageRepository指定了镜像仓库地址,podSubnet和serviceSubnet分别是Pod网段和Service网段的地址范围,这个要和后面安装的网络插件对应上。
我用的podSubnet是10.244.0.0/16,这是Flannel网络插件的默认网段。如果你后续打算装Calico,网段要换成192.168.0.0/16,否则会冲突。这一点新手特别容易踩坑,一定要提前规划好。
确认配置没问题后,执行初始化:
sudo kubeadm init --config kubeadm-config.yaml初始化过程大概需要几分钟。正常情况下,看到类似下面的输出就说明成功了:
Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config按提示配置kubectl的访问权限:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config配置好后,验证一下:
kubectl get nodes这时候节点状态应该是NotReady,因为还没有安装网络插件。这是正常现象,不用慌。
4.3 安装网络插件
网络插件是k8s集群能够正常工作的关键组件之一。没有网络插件,Pod之间无法通信,甚至无法分配IP。我这次用的是Flannel,它配置简单,资源占用少,适合学习环境。如果是生产环境,可以根据需求考虑Calico或Cilium,它们的功能更丰富,比如支持NetworkPolicy。
安装Flannel只需要一条命令:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml这里有个注意点:Flannel的部署文件里默认使用了Pod网段10.244.0.0/16,所以你必须在kubeadm配置里也用这个网段。如果你用的是Calico且Pod网段是192.168.0.0/16,那Flannel的部署文件就不适用,得用Calico自己的部署文件。
安装完等一两分钟,再查看节点状态:
kubectl get nodes如果显示Ready,说明控制面已经正常了。再查看所有命名空间的Pod:
kubectl get pods -A正常情况下,你会看到coredns、kube-flannel-ds等Pod处于Running状态。
4.4 去掉Master节点的污点(单节点集群必备)
集群初始化完成后,master节点默认带有污点(taint),不允许普通Pod调度上去。但在单节点集群里,我们只有这一台机器,如果不去除污点,测试Pod就永远无法运行。
执行以下命令去掉污点:
kubectl taint nodes --all node-role.kubernetes.io/control-plane-这个命令把所有节点上名为node-role.kubernetes.io/control-plane的污点都去掉了。这样Pod就能调度到master节点上运行了。对于学习环境来说,这是必要的。如果你以后搭了多节点集群,不建议在生产环境的master节点上去掉污点,因为master节点一般不建议跑业务负载。
4.5 验证集群功能
控制面Ready之后,最好跑一个简单的Deployment验证集群是否真正工作正常。我通常用nginx镜像做冒烟测试:
kubectl create deployment nginx-test --image=nginx kubectl expose deployment nginx-test --type=NodePort --port=80 kubectl get pods kubectl get svc等Pod变成Running后,用NodePort方式暴露服务。比如Service显示80:32761/TCP,那我就在浏览器访问http://192.168.1.100:32761,能看到nginx欢迎页说明整个集群链路已经打通了。
5. 常见问题与排查技巧实录
这一部分是我最想分享的。k8s部署过程中报错不可怕,可怕的是报错信息看不懂,或者干脆不报错但状态一直是Pending。我把这次部署过程中遇到的和之前帮别人排查过的几类典型问题整理出来,方便你对照排查。
5.1 kubeadm init报错:failed to pull image
这个错误本质上就是镜像拉不下来。如果你是使用默认镜像地址registry.k8s.io,在国内网络环境下非常容易碰到。解决办法很简单,就是使用国内镜像源。具体操作就是我前面提到的,使用kubeadm config images list查看需要哪些镜像,然后从国内源拉取并重新tag,或者干脆在kubeadm配置文件里把imageRepository指定为国内源。我个人推荐后者,因为一次性配置好,后续kubeadm init --config就不会再拉错地址了。
5.2 节点状态一直是NotReady
这个问题的原因非常多样,最常见的几个方向按顺序排查:网络插件没装、内核参数没生效、cgroup驱动不一致。
我先看网络插件有没有运行。kubectl get pods -n kube-flannel如果状态不是Running,多半是Flannel部署文件没生效。再检查内核参数,确认br_netfilter模块是否加载成功:
lsmod | grep br_netfilter如果没有任何输出,说明模块没加载,回去重新执行modprobe那一步。最后检查cgroup驱动,确认kubelet的配置和containerd一致:
kubectl describe node k8s-master | grep -i kubelet查看kubelet启动参数,确认里面有--cgroup-driver=systemd。如果kubelet是cgroupfs,而containerd是systemd,就会出现NotReady的状态。
5.3 kube-flannel-ds Pod一直CrashLoopBackOff
这个问题我在Flannel初始化的时候遇到过。日志里出现了类似"Failed to create subnet manager"或"Error registering network"的报错。原因通常是Pod网段冲突或者节点间的IP冲突。最常见的情况是,我在kubeadm配置文件里写的podSubnet是10.244.0.0/16,但实际上之前某次初始化用了其他网段,导致Flannel的配置被写入旧值。
解决办法是重置集群,重新初始化:
sudo kubeadm reset sudo rm -rf /etc/cni/net.d sudo ip link delete cni0 sudo ip link delete flannel.1然后重新执行kubeadm init --config kubeadm-config.yaml,装Flannel。如果你前面改了podSubnet,这里一定要保持一致。
5.4 coredns Pod Running但一直ContainerCreating
这个问题通常是CNI插件没装好,导致Pod分配不了IP或者网络插件没就绪。我先查看Pod的详细状态:
kubectl describe pod coredns-xxx -n kube-system如果事件里显示network plugin is not ready: cni config uninitialized,说明CNI配置目录是空的。这时候检查一下Flannel是否正常运行,或者说CNI配置是否生成到了/etc/cni/net.d目录。
还有一个非常隐蔽的问题。我遇到过Flannel正常运行,但coredns还是ContainerCreating的情况。后来发现是因为主机的iptables规则被之前的测试脚本搞乱了。重启机器就能解决,但如果你不想重启,可以手动刷新iptables规则:
sudo iptables -F sudo iptables -t nat -F sudo systemctl restart kubelet注意,这个操作会清掉所有自定义iptables规则,如果有其他服务在用会受影响,学习环境用就问题不大。
5.5 Pod一直Pending
如果创建Deployment后,Pod的状态一直Pending,最常见的原因是调度失败。单节点集群最常见的是master节点的污点没去掉,Pod调度不上去。照我前面说的kubectl taint nodes --all node-role.kubernetes.io/control-plane-执行一遍即可。
还有一种情况是资源不够。查看节点是否满足Pod的资源请求:
kubectl describe node k8s-master重点看Allocated resources部分,如果CPU或内存已经满了,Pod自然调度不上去。我的16G内存机器跑集群加几个测试Pod,内存还能剩个五六G,但如果同时跑MySQL、Redis、Nginx这些,也容易吃紧。建议测试用的Deployment资源请求尽量设小一点。
5.6 kubectl命令报错:The connection to the server was refused
这种情况一般是kube-apiserver没起来,或者kubectl配置里的地址不对。先检查apiserver容器状态:
sudo crictl ps -a | grep kube-apiserver如果容器没有运行,查看kubelet日志:
sudo journalctl -u kubelet -f常见原因是证书过期或者etcd有问题。不过第一次部署遇到这种情况,我更倾向于检查一下时间同步。如果本机时间和实际时间差太多,证书验证会失败。可以安装chrony或systemd-timesyncd来保持时间同步:
sudo apt install -y systemd-timesyncd sudo timedatectl set-ntp true5.7 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| kubeadm init 拉镜像失败 | 镜像仓库不可达 | 使用国内镜像源 |
| Node 状态 NotReady | 网络插件未安装 | 安装 Flannel |
| Pod 状态 Pending | 节点污点未去除 | 去污点 |
| Pod 状态 CrashLoopBackOff | 配置冲突或镜像问题 | 查看 Pod 日志 |
| Service 访问不通 | 防火墙或 IP 转发未开启 | 检查防火墙和 sysctl |
| kubectl 连接拒绝 | apiserver 未启动 | 检查 crictl ps 和 kubelet 日志 |
5.8 用好crictl:排查容器的正确姿势
最后专门说说crictl。很多从Docker迁移过来的同学,一开始不习惯用crictl,但它是排查k8s节点问题的标准工具。它和docker命令的语法很相似,但针对的是CRI接口,能直接查看k8s视角下的容器和镜像。
我最常用的几个crictl命令:
# 查看当前节点所有的Pod sudo crictl pods # 查看所有容器,加上-a能看到已退出的 sudo crictl ps -a # 查看容器详细信息,包括挂载、网络、环境变量 sudo crictl inspect <container-id> # 拉取镜像 sudo crictl pull nginx:latest # 查看镜像列表 sudo crictl images这里有个值得注意的差别:docker ps看到的是所有Docker管理的容器,而crictl ps看到的只是kubelet通过CRI接口启动的容器。k8s集群中,系统组件如kube-scheduler、kube-controller-manager等都是作为静态Pod运行的,所以用crictl查看它们比用docker更直观。
我踩过的一个坑是,想查看某个容器的日志,习惯性用了docker logs,结果提示容器找不到。后来才意识到,k8s节点上的容器运行时虽然是containerd,但容器ID和docker命令看到的不是同一个命名空间。所以记住,在k8s节点上排查容器问题,第一反应应该是crictl而不是docker。
6. 后续扩展与个人实操心得
集群搭好之后,怎么继续学习?我给你三个方向的建议。第一个是部署一个实际的应用,比如用Deployment跑一个Node.js或Python的Web服务,通过Service暴露出来,再试着配置HorizontalPodAutoscaler,体验一下弹性伸缩。第二个是尝试研究k8s的核心对象,比如Pod、Deployment、Service、ConfigMap、Secret、Ingress,每个对象都在k8s里扮演什么角色,实际创建一个看看效果。第三个是学习Helm,这是k8s的包管理工具,用Helm Chart来部署应用,能大大简化复杂应用的部署流程。
我在实际操作中有几点体会想单独分享。
第一个是千万别在生产环境贪新。k8s版本迭代太快,盲目追求新版本带来的往往是兼容性问题和文档缺失。选择生态成熟、社区验证充分的版本,比用最新版本更稳妥。我的建议是关注官方发布的版本支持周期表,选择还有一两年维护期的版本。
第二个是配置文件要养成版本管理的习惯。kubeadm的配置文件、Flannel的部署文件、还有你自己创建的Deployment清单,都应该纳入git管理。我有一个小目录专门存放集群相关的配置文件,每次修改都有记录,出了问题可以快速回退。这个习惯在集群出问题的时候价值巨大。
第三个是监控要早做。基础集群搭好之后,我第一时间装了metrics-server,虽然只是采集CPU和内存指标,但用kubectl top node查看资源占用比看一堆数字直观得多。后面慢慢加了Prometheus和Grafana,但现在回想起来,先把metrics-server搞定,再按需加其他组件就够了,没必要一上来就整大全套。
第四个是磁盘空间要注意。k8s的镜像、容器日志、etcd数据都占磁盘。我的512G SSD目前还剩不少空间,但容器日志增长很快。建议提前配置logrotate,把kubelet的日志轮转打开。具体做法是在/etc/logrotate.d/下创建kubelet的日志轮转配置,或者用kubelet的--container-log-max-files和--container-log-max-size参数控制容器日志大小。
最后再说一个直觉性的经验:k8s部署失败时,不要急着反复init或者reset。先看清报错信息,再对照日志去定位。比如journalctl -u kubelet能看到kubelet日志,crictl logs能看容器日志,kubectl describe pod能看事件。这几个命令配合使用,90%的问题都能快速定位。我见过很多新手一遇到问题就全网搜索,但在搜索之前,先把自己手里的日志看明白了,效率反而更高。
这套流程我后来又用新版本跑过一遍,整体框架没变,只是镜像版本号和一些配置字段略有差异。k8s的核心架构和部署逻辑是稳定不变的,学会了这套流程,换版本、换机器、换网络环境,都能从容应对。