1. 为什么到现在还有人用 kubeadm 装 1.18.6
先把话说在前头:Kubernetes 1.18.6 这个版本放在今天确实不算新,但它是一个很特殊的存在。1.18 是 2020 年上半年的版本,而 1.18.6 是这条线上的后期补丁版本,稳定性已经被打磨得比较到位。更关键的是,很多早期搭建的私有化环境、离线机房、教学实验平台,底座就是这一版,短期内不会动。你要做的不是追新,而是在一个已经确定版本的环境里,把集群稳稳当当地立起来。
kubeadm 这个工具从 1.13 之后逐渐成熟,到了 1.18 已经算是“开箱可用”的状态。它把证书生成、控制面组件静态 Pod 编排、kubeconfig 分发、Token 管理这些脏活累活全包了。你只需要准备好机器、装好容器运行时、拉齐镜像,剩下的基本就是几条命令的事。这也是为什么即便到了今天,仍然有人愿意用 kubeadm 来搭 1.18.6——它足够简单,也足够透明,出问题你能顺着日志一层层往下查。
这篇文章面向的是手上有几台机器、想自己搭一套能跑起来的 Kubernetes 集群的人。不管你是刚接触容器编排的新手,还是被某个老项目绑在 1.18 上的运维,我都会把整个流程拆开讲清楚:每一步在干什么、为什么这么干、哪里容易翻车。我不会只给你一串命令让你复制粘贴,而是把背后的逻辑讲透,这样你换一台机器、换一个环境,照样能搭起来。
2. 搭建前的整体设计与关键取舍
2.1 集群拓扑怎么定:单 Master 还是多 Master
搭集群第一件事不是敲命令,而是想清楚要几个节点、怎么分工。kubeadm 支持单控制面和多控制面两种模式。单 Master 就是一台机器同时跑控制面和 etcd,再加若干 Worker;多 Master 则是三台控制面做高可用,etcd 要么堆叠在控制面上,要么单独部署。
对于 1.18.6 这种偏实验、偏内部使用的场景,我的建议很直接:先用单 Master 把流程跑通,再考虑高可用。原因很简单,多 Master 会引入负载均衡、证书 SAN、etcd 集群一致性等一堆额外变量,新手很容易在某个环节卡住却不知道问题出在哪。单 Master 虽然存在单点故障,但对于学习、测试、内部小规模使用完全够用。
如果你确实要做高可用,那至少需要三台控制面节点,前面挂一个四层负载均衡把 6443 端口转发到三台机器上,kubeadm 初始化时通过--control-plane-endpoint指定这个统一入口。这个后面我会单独说,这里先记住拓扑决定了你后面所有命令的写法。
2.2 容器运行时选 Docker 还是 containerd
1.18 这个时间点,Docker 还是绝对主流。虽然 Kubernetes 后来逐步往 containerd 迁移,但在 1.18.6 上,用 Docker 是最省心的选择,因为 kubeadm 默认就是去找 Docker 的 socket,社区文档、镜像仓库也全是围绕 Docker 写的。
containerd 在 1.18 上也能用,但需要额外配置SystemdCgroup,还要注意crictl和docker命令的差异,排查问题时参考资料少很多。所以我的实操建议是:1.18.6 就老老实实用 Docker,别给自己找麻烦。等以后升级到更高版本,再考虑切 containerd 也不迟。
2.3 网络插件怎么选:Flannel 还是 Calico
Pod 网络是集群能不能通的关键。1.18 时代最常用的两个选择是 Flannel 和 Calico。Flannel 配置简单,一个 YAML 下去基本就能通,适合快速验证;Calico 功能更强,支持网络策略,但配置项多,对网卡名、CIDR 有要求。
我的经验是:如果你只是想先把集群跑起来,选 Flannel。它的 vxlan 模式几乎不需要改配置,只要保证podSubnet和 Flannel 的网段一致就行。等你确认集群没问题了,再考虑要不要换 Calico 做策略控制。这里有个坑我后面会细讲——网段冲突是 Flannel 最常见的翻车点。
2.4 版本锁定的意义:为什么要写死 1.18.6
kubeadm、kubelet、kubectl 这三个组件的版本必须严格一致,而且要和你要装的 Kubernetes 版本对齐。1.18.6 对应的就是这三个组件的 1.18.6。如果你用apt install kubeadm不加版本号,装出来的可能是 1.18.x 里最新的,也可能是别的版本,一旦和 kubelet 对不上,kubelet 就起不来。
所以安装时一定要用apt install kubeadm=1.18.6-00 kubelet=1.18.6-00 kubectl=1.18.6-00这种带版本号的方式,并且用apt-mark hold把它们锁住,防止系统自动升级。这个细节看着小,但它是很多“明明照着教程做却起不来”的根源。
3. 环境准备与系统层配置实操
3.1 机器规划与基础信息确认
假设我们有三台机器,一台 Master、两台 Worker,操作系统用 Ubuntu 18.04 或 CentOS 7 都可以,我这里以 Ubuntu 18.04 为例。先确认每台机器的主机名、IP、时间同步状态。
主机名规划建议有意义一点,比如k8s-master、k8s-node1、k8s-node2,因为 kubeadm 初始化时会把主机名注册进集群,后面kubectl get nodes看到的就是这些名字。IP 方面,确保三台机器在同一个内网网段,能互相 ping 通。
时间同步必须做,etcd 对时间敏感,节点间时间差太大会导致证书校验失败。执行timedatectl看时间是否同步,没同步就装ntp或chrony对齐。
3.2 关闭 Swap:不是可选项而是必选项
kubelet 在 1.18 上默认不允许 Swap 开启,如果你不关,kubelet 启动时会直接报错退出。关闭方法:
swapoff -a sed -i '/swap/s/^/#/' /etc/fstab第一行是临时关闭,第二行是注释掉 fstab 里的 swap 挂载,保证重启后不会自动开。执行完用free -h确认 Swap 那一行是 0。
注意:有些云主机默认没有 Swap,这一步可以跳过,但一定要用
free -h确认,别想当然。
3.3 内核参数与模块加载
Kubernetes 需要br_netfilter模块来做网桥流量过滤,还需要开启 IP 转发。配置如下:
cat <<EOF | tee /etc/modules-load.d/k8s.conf br_netfilter EOF modprobe br_netfilter cat <<EOF | 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 sysctl --system这几行的作用是让 iptables 能正确处理网桥流量,并允许内核转发数据包。Flannel 的 vxlan 封装依赖 IP 转发,不开的话 Pod 之间根本通不了。执行完用sysctl net.ipv4.ip_forward确认返回 1。
3.4 安装 Docker 并配置 cgroup 驱动
Docker 的安装用官方脚本或者系统包都行,关键是装完之后要确认 cgroup 驱动是systemd,因为 kubelet 在 1.18 上默认用 systemd 驱动,两边不一致会导致 kubelet 反复重启。
cat <<EOF | tee /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2" } EOF systemctl daemon-reload systemctl restart dockernative.cgroupdriver=systemd是核心,日志大小限制和 overlay2 是常规优化。装完用docker info | grep Cgroup确认输出是 systemd。
3.5 安装 kubeadm、kubelet、kubectl 并锁版本
先加软件源,再装指定版本:
apt update && apt install -y apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list deb https://apt.kubernetes.io/ kubernetes-xenial main EOF apt update apt install -y kubelet=1.18.6-00 kubeadm=1.18.6-00 kubectl=1.18.6-00 apt-mark hold kubelet kubeadm kubectlapt-mark hold这一步非常重要,它防止系统在后续apt upgrade时把这三个组件升级到不兼容的版本。我见过太多人装完当时是好的,过几天系统自动更新后集群就崩了,根源就在这里。
4. 初始化 Master 与网络插件部署
4.1 拉取镜像:国内环境的现实问题
kubeadm 初始化时会去k8s.gcr.io拉镜像,这个地址在国内很多环境里拉不动。解决办法有两个:一是提前用kubeadm config images pull配合镜像加速,二是手动从能访问的镜像仓库拉下来再打 tag。
kubeadm config images list --kubernetes-version v1.18.6这条命令会列出需要的所有镜像。你可以写个脚本,把每个镜像从可用的镜像源拉下来,再docker tag成k8s.gcr.io/xxx的名字。这一步做完,初始化时 kubeadm 发现本地已有镜像,就不会再去拉。
提示:镜像的 tag 必须和
kubeadm config images list输出的完全一致,差一个字符都会导致重新拉取。
4.2 kubeadm init 的关键参数
初始化命令的核心参数有三个:--kubernetes-version、--pod-network-cidr、--apiserver-advertise-address。
kubeadm init \ --kubernetes-version=v1.18.6 \ --pod-network-cidr=10.244.0.0/16 \ --apiserver-advertise-address=192.168.1.10 \ --image-repository=k8s.gcr.io--pod-network-cidr设成10.244.0.0/16是因为 Flannel 默认用这个网段,两边必须一致。--apiserver-advertise-address填 Master 的内网 IP,Worker 节点通过这个地址和 API Server 通信。如果你做了高可用,这里换成--control-plane-endpoint指向负载均衡地址。
初始化成功后,终端会输出一段kubeadm join命令,里面有 Token 和证书哈希值。这段命令一定要存下来,Worker 节点加入集群全靠它。Token 默认 24 小时过期,过期了可以用kubeadm token create --print-join-command重新生成。
4.3 配置 kubectl 与验证控制面
初始化完成后,按提示配置 kubectl:
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config然后kubectl get nodes应该能看到 Master 节点,状态是NotReady,这是正常的,因为网络插件还没装。kubectl get pods -n kube-system能看到 apiserver、controller-manager、scheduler、etcd 四个静态 Pod 在跑。
4.4 部署 Flannel 并确认 Pod 网络
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml如果你的环境访问不了这个地址,就提前把 YAML 下载下来再 apply。部署完等一两分钟,kubectl get pods -n kube-system里会出现 flannel 的 Pod,kubectl get nodes里 Master 会变成Ready。
这里有个高频坑:Flannel 的 YAML 里默认网段是10.244.0.0/16,如果你kubeadm init时写的pod-network-cidr不是这个,Flannel 就会起不来或者 Pod 之间不通。两边必须严格对应。
4.5 Worker 节点加入集群
在每台 Worker 上执行之前保存的kubeadm join命令。执行完回到 Master,kubectl get nodes应该能看到新节点,状态先是NotReady,等 flannel 镜像拉下来、Pod 起来后就变Ready。
如果 Worker 一直NotReady,先看kubectl describe node <节点名>的 Conditions,再看该节点上journalctl -u kubelet -f的日志,八成是镜像没拉下来或者网络插件没起来。
5. 常见问题排查与避坑经验
5.1 初始化卡在 etcd 或 apiserver 起不来
这种情况通常是镜像没拉全,或者端口被占用。先用docker images | grep k8s确认镜像是否齐全,再用netstat -tlnp | grep 6443看 6443 端口是否被别的进程占了。如果之前初始化失败过,要先kubeadm reset清理干净再重来,否则残留的配置会干扰下一次初始化。
kubeadm reset之后还要手动清理 CNI 配置和 iptables:
rm -rf /etc/cni/net.d iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X5.2 节点 NotReady 的排查顺序
我总结了一个固定的排查顺序,照着走基本能定位:
| 排查项 | 命令 | 常见原因 |
|---|---|---|
| 节点状态详情 | kubectl describe node <名> | 看 Conditions 里的报错信息 |
| kubelet 日志 | journalctl -u kubelet -f | 证书、cgroup、镜像问题 |
| 容器运行时 | systemctl status docker | Docker 没起或 cgroup 不匹配 |
| 网络插件 | kubectl get pods -n kube-system | flannel Pod 是否 Running |
| 网络连通 | ping <节点IP> | 基础网络不通 |
5.3 Token 过期与重新加入
Token 默认 24 小时有效,过期后 Worker 加入会报token not found。重新生成:
kubeadm token create --print-join-command这条命令会输出完整的 join 命令,直接复制到 Worker 上执行即可。如果证书哈希也变了,命令里会带上新的哈希,不用你手动算。
5.4 证书过期:1.18 集群放久了必踩的坑
kubeadm 生成的证书默认有效期一年。1.18.6 的集群如果放了一年多没动,某天重启后 apiserver 可能就起不来了,日志里全是证书过期。查看证书有效期:
kubeadm alpha certs check-expiration续期用kubeadm alpha certs renew all,然后重启控制面组件。这个命令在 1.18 上还是 alpha 阶段,但已经可用。我的建议是集群搭好后就把证书续期做成定时任务,别等出问题再救火。
5.5 网段冲突:最隐蔽的翻车点
如果你的内网本身用了10.244.0.0/16或者172.17.0.0/16,和 Pod 网段、Docker 默认网段撞了,会出现“节点 Ready 但 Pod 不通”的诡异现象。排查方法是ip route看路由表,确认 Pod 网段没有被物理网络占用。解决方法是重新规划pod-network-cidr,选一个内网没用的网段,比如10.200.0.0/16,然后 Flannel 配置同步改掉。
6. 集群可用性验证与日常维护建议
6.1 用一个 Deployment 验证集群真的能用
节点 Ready 不代表集群能用,得跑个实际负载。创建一个简单的 Deployment:
kubectl create deployment nginx-test --image=nginx:1.18 kubectl expose deployment nginx-test --port=80 --type=NodePort kubectl get svc nginx-test拿到 NodePort 后,用curl <任意节点IP>:<NodePort>看能不能返回 nginx 欢迎页。能返回说明 Pod 调度、网络、Service 转发整条链路都通了。这一步是验收集群的黄金标准,别省。
6.2 日常维护的几个习惯
集群搭起来只是开始,后面要养成几个习惯。第一,定期kubectl get nodes和kubectl get pods -A扫一眼,别等出事才发现。第二,etcd 要定期备份,1.18 的 etcd 备份命令是ETCDCTL_API=3 etcdctl snapshot save,配合证书参数使用。第三,kubelet 和 Docker 的日志要配置轮转,不然磁盘很快被日志撑满。
6.3 关于升级的现实建议
1.18.6 往上升级,kubeadm 只支持逐个小版本升,不能从 1.18 直接跳到 1.20。路径是 1.18 → 1.19 → 1.20,每个版本都要先升 kubeadm,再kubeadm upgrade plan和kubeadm upgrade apply,最后升 kubelet 和 kubectl。这个过程对生产环境有风险,升级前一定备份 etcd 和所有 YAML。如果集群只是内部用、没特殊需求,我的个人建议是能不升就不升,稳定比新功能重要。
我自己搭过好几套 1.18.6 的集群,踩过的坑基本都写在上面了。最想强调的一点是:镜像准备和版本锁定这两件事做扎实,整个搭建过程能省掉八成的麻烦。很多人一上来就急着敲kubeadm init,结果卡在拉镜像上半天,或者装完没锁版本过几天集群自己崩了。把准备工作做足,后面就是顺水推舟的事。