简介:一套采用kubeadm实现的Kubernetes v1.18.2自动安装脚本集合,面向需要在Linux环境下搭建测试集群的开发者,借助环境检查与分步提示机制替代繁杂手动配置,用于快速部署并理解k8s运作原理;作者明确说明不宜用于生产环境。压缩包共6个文件,整体仅10KB,内置安装脚本、kubeadm初始化模板、flannel网络配置、环境参数和阅读说明,轻量但结构完整。目前已有3105人浏览学习。内置的环境检查会预先判断部署条件是否齐备,不满足时提示修正方向,让初次接触kubeadm的读者可以按提示逐步推进,降低试错成本;同时结合yaml配置和文档阅读,能够对照掌握master节点初始化、网络插件部署等关键环节,形成k8s安装的整体认知。作者也表示程序会持续完善,若有疑问可通过私信交流,便于后续优化。
1. 一键安装 k8s:把最容易翻车的初始化环节收进脚本
k8s 一键安装,听起来像偷懒,实际上是把镜像准备、容器运行时、证书签发、CNI 网络、kubeadm 初始化这些容易出错的黑匣子全部收敛成一份配置和一条命令。做过 k8s 集群搭建的人都清楚,kubeadm init本身不算难,真正把人卡住的往往是镜像拉不下来、cgroup driver 对不上、网络插件网段不一致,最后节点一直 NotReady。这个方案能解决部署教程里散落的十几个手动步骤,适合刚被初始化流程折腾到怀疑人生的开发者,也适合要快速交付多套同等配置集群的运维。前提是你得知道脚本替你做了什么,以及它不能替你做什么。
2. 从下载到 kubectl get nodes:一键安装的最小流程
2.1 安装前先做四项系统检查,别让脚本背锅
一键脚本普遍默认机器已经满足几个基础条件:至少 2 核 4G、主机名可解析、swap 已关闭、时间同步正常。这四项里任何一项缺失,kubelet 都可能先起来再崩溃,最后排查半天才发现是系统层面的问题,而不是脚本问题。常见做法是执行脚本的--check预检模式,如果脚本没提供,自己手动做也很快。
uname -a nproc && free -h df -h /var/lib/containerd timedatectl set-ntp true swapoff -a && sed -i '/swap/s/^/#/' /etc/fstab命令逻辑解释:uname -a确认内核与架构,k8s 对内核版本有底线要求;nproc && free -h看 CPU 和内存;df -h /var/lib/containerd是为镜像和卷预留空间;swapoff -a关闭当前会话的 swap,sed -i注释/etc/fstab里的 swap 行,保证重启后不复活。kubelet 在旧版里对 swap 的态度是直接拒绝启动,虽然新版本可以容忍,但多数一键安装脚本会在 init 前主动关掉,避免在failSwapOn参数上纠缠。
主机名检查容易被人忽略。如果机器 hostname 带下划线或者大写字母,kubelet 注册节点时可能报错,因为节点名要求符合 DNS 规范。常见做法是在安装前把每台机器的 hostname 统一改成小写短横线格式,同时保证/etc/hosts里写清楚了本机 IP 与主机名的映射。时间不同步也是隐蔽坑:apiserver 签发证书依赖时间校验,节点间时间差超过 5 分钟就会出证书校验失败,所以timedatectl set-ntp true这行最好每台机器都执行一次。
2.2 下载脚本、确认离线包、改一份配置文件
下载方式常见有两种:直接 clone GitHub 仓库,或者从 Release 页下载打包好的部署包再解压。Release 包通常会把所需镜像打成 tar 包一起附上,这对离线部署特别有用,因为 kubeadm init 阶段镜像能不能及时拉下来直接决定成败。脚本支持加载本地 tar 包的话,可以先执行 load 动作,再用crictl images确认镜像已经就位,而不是等到 init 卡住才回头看日志。
进入目录后不要急着执行,先找到配置文件。常见文件名是k8s.yml或config.yaml,结构大致如下:
cluster: name: demo mode: multi masters: - 192.168.1.11 - 192.168.1.12 - 192.168.1.13 vip: 192.168.1.10 cri: runtime: containerd cgroup_driver: systemd network: pod_subnet: 10.244.0.0/16 service_subnet: 10.96.0.0/12 plugin: flannel image: repository: "" pull_policy: "ifNotPresent" cert: auto_renew: true参数说明:mode决定单机还是多 master 高可用,生产环境直接用multi;masters列表填写各节点内网 IP;vip是高可用虚拟 IP,单机部署可以忽略。pod_subnet必须和后面选用的网络插件默认网段一致,flannel 固定用 10.244.0.0/16,calico 常见默认是 192.168.0.0/16,这个值一旦交给节点分发,后期改要重置集群,所以第一次就要选对。service_subnet用 10.96.0.0/12 是主流默认值,前提是别跟公司机房网段重叠。image.repository留空表示走默认仓库,内网环境改成自己的 registry 前缀。cert.auto_renew决定一年之后证书是否自动续期,留 false 的话后面要有自己的兜底方案,这个在排错章节还会展开说。
配置里的cri.runtime与cgroup_driver务必一起看。如今 containerd 成为主流,cgroup driver 推荐随 systemd 保持一致,这也是新版 kubeadm 的默认偏好。如果机器上还装着 docker,脚本生成的 containerd 配置偶尔会跟 docker 抢 socket,这种冲突在踩坑章节第一条会给出处理方式。
2.3 执行 init 并观察集群状态
配置确认无误后,在主节点执行安装:
./k8s.sh install --config k8s.yml # 脚本会在结束时打印 kubeconfig 路径、join 命令和证书目录 mkdir -p ~/.kube cp /etc/kubernetes/admin.conf ~/.kube/config这期间脚本做的事其实不算神秘:生成 kubeadm 配置,把image.repository拼到镜像名前缀上,预拉关键镜像包括 pause、etcd、apiserver、controller-manager、scheduler、coredns,然后执行kubeadm init。这一步耗时最久的是镜像拉取,离线包方案能省掉这段时间。看到 init 成功提示不代表集群可用,第一验证动作是检查节点和核心组件:
kubectl get nodes -o wide kubectl get pods -n kube-system -o wide期望结果:所有 master 节点状态为 Ready,kube-system命名空间下 etcd、kube-apiserver、kube-controller-manager、kube-scheduler、coredns 全部 Running。如果 coredns 显示 Pending 或 CrashLoopBackOff,基本就是网络插件没生效。多数一键脚本会在 init 之后自动部署 flannel 或 calico,但也存在只生成network.plugin字段、把插件部署留给用户手动执行的版本。看到 coredns 崩溃时,先检查network.plugin有没有真的填值,而不是一上来就查 deployment 怎么做。
2.4 脚本的幂等性和失败后的后悔药
k8s.sh install二次执行时,脚本会检测/etc/kubernetes目录和 kubelet 服务是否存在,避免重复 init。但如果第一次执行中断在半路,脚本不会自动帮你清残留,需要手动做一次 kubeadm 层面的清理:
kubeadm reset -f rm -rf /etc/kubernetes /var/lib/etcd /var/lib/containerd/* ip link delete cni0 2>/dev/null ip link delete flannel.1 2>/dev/null这段是 k8s 集群搭建里最常用的后悔药。kubeadm reset -f负责重置 kubelet 和容器运行时状态;删除/var/lib/etcd是为了避免旧集群数据影响新集群,这个目录在多 master 场景下同时存在于每台 master;最后两条删除虚拟网卡是清理网络插件残留,flannel 对应flannel.1,calico 还可能要删vxlan.calico,不确定时先用ip link show看一下有哪些虚拟网卡再动手。
一键安装的边界也在这里:脚本接手的是标准 kubeadm 流程,失败的中间态依然要自己处理。理解这点,后面遇到莫名奇妙的报错就不会第一时间怀疑脚本坏了,而是回到 kubeadm 和系统残留这个层面来找原因。
3. 三台 master 高可用:用同一套脚本做 k8s 集群搭建
3.1 为什么三台 master 是生产的最小配置
生产环境不会拿单机集群对外提供服务,这不是高可用问题,而是入口单点问题。kube-apiserver 是集群所有请求的入口,worker 节点、kubectl、控制器都要绕不开它。apiserver 挂了,整个集群对外表现为失联,后面 pod 该跑还是跑,但你什么都做不了。
多 master 架构里有个绕不开的概念:etcd 的多数派协议。etcd 按 raft 算法选主,写入必须获得多数节点确认。三台 master 对应三份 etcd 副本,允许挂一台还能继续选主;两台的话一旦一台宕机,剩下的一台永远凑不够多数派,集群等于锁死。所以三台不是锦上添花,是 etcd 机制倒逼出来的最小生产规模。如果你所在团队只有一台物理机能跑 master,那不如不要用多 master,而是在 apiserver 前面加一个外部负载均衡,把后端暂时指向单台 master,后续再扩容节点。
3.2 内置 vip 方案还是外部负载均衡
一键脚本在mode: multi下的常见做法,是内置 keepalived 加 HAProxy 组成虚拟 IP。三台 master 上同时跑 HAProxy 监听 6443,keepalived 负责把 VIP 绑定到其中一台 master。当前这一台宕机时,VIP 会漂移到另一台,对外还是同一个地址,worker 和 kubectl 不用改任何配置。
配置里vip: 192.168.1.10对应这套机制。这个 IP 必须是空闲地址,不能和任何物理机或已有服务冲突。HAProxy 的配置一般由脚本自动生成,backend 指向三台 master 的真实 IP:6443,健康检查用 tcp 方式探测端口。启用后可以手动验证漂移:
ip addr show | grep 192.168.1.10 # 找到当前持有 VIP 的节点 # 在该节点上执行 systemctl stop haproxy,VIP 应在几秒内漂移到其他节点另一种主流做法是外部负载均衡,比如机房已有的 F5、nginx stream、云平台的负载均衡实例。使用这种模式时,脚本配置里lb可以选 external 云 LB 形态,vip字段直接填 LB 的访问地址。kubekey 这类部署工具也是这样,本质都是把 kubeadm 的control-plane-endpoint指向一个永远可达的地址,同时 apiserver 证书 SAN 里必须包含这个地址,否则客户端校验证书会失败。脚本通常会把这个 SAN 自动加进 kubeadm 配置,但如果你是自己手写 kubeadm 配置,这条非常容易漏。
3.3 worker 节点加入与 token 过期处理
master 初始化完成后,脚本会生成 worker 加入命令。常见形式是:
./k8s.sh join --token <token> --control-plane-address 192.168.1.10:6443 --node-type worker # token 默认 24 小时过期,过期后在主节点重新生成 kubeadm token create --print-join-command--control-plane-address必须填 VIP 或外部 LB,不能填某一台 master 的真实内网 IP。如果填了真实 IP,worker 入集群后只会连向这一台 master,VIP 一旦漂移,worker 到 apiserver 的链路就断了,高可用也就名存实亡。token 过期是另外一个常见问题,在 worker 节点上看到 unauthorized 报错时,不用怀疑其他配置,先回到主节点用kubeadm token create --print-join-command拿到新命令再重试。
3.4 高可用装完必须做的两个验证
第一,把kubectl的 kubeconfig 里 server 地址改成 VIP,执行kubectl get nodes,确认能正常读到节点列表。第二,找到当前持有 VIP 的 master,直接重启这台机器,然后反复执行kubectl get nodes观察中断情况。这两个动作验证的是同一件事:高可用是不是只存在于配置里。脚本能帮你搭建架构,但 VIP 漂移是否成功,只能靠真实故障模拟来验证。生产环境里最怕的是自以为高可用,半夜 master 宕机才发现 VIP 根本没漂移过去。提前做一次重启测试,把这个问题留在白天解决,是每个运维值得养成的习惯。
4. 一键安装避坑:5 条常见报错与排错记录
4.1 kubelet 报 container runtime is down
现象:安装脚本跑完,kubectl get nodes里 master 一直 NotReady,journalctl -u kubelet连续滚 container runtime is down。
原因:系统里原本装过 docker,docker 或 containerd 的 socket 冲突导致 kubelet 连不上正常 CRI。脚本默认用 containerd,旧 docker 的containerd.sock路径与新配置不一致,或者 containerd 状态目录里有旧数据。
解决:先停掉 docker 及其相关服务,清掉 containerd 状态目录,再重启 containerd:
systemctl stop docker containerd systemctl disable docker rm -rf /var/lib/containerd systemctl start containerd提示:如果你选择用 docker 作为 CRI,反过来检查
cgroup_driver是否和 kubelet 保持一致,systemd 与 cgroupfs 不一致时 kubelet 会直接报 failed to run Kubelet,排查方向完全不同。
4.2 coredns 一直 CrashLoopBackOff
现象:master 节点正常 Ready,但 kube-system 里的 coredns 反复重启,pod 日志里看到 no servers found。
原因:网络插件没生效,pod 网络没起来。最常见的直接原因是pod_subnet与网络插件默认网段不一致,flannel 默认 10.244.0.0/16,calico 默认 192.168.0.0/16,配置文件里填了插件的网段配不上,CNI 插件分发的网段没有落到 pod。
解决:把配置里network.pod_subnet改成网络插件的默认网段,然后必须重置集群重新 init。这个参数一旦经过 kubeadm 下发到节点,几乎不可能热改生效,最快路径就是kubeadm reset再跑一遍 install。另外检查/etc/cni/net.d/下是不是只有一份 CNI 配置,残留两份配置会导致网络插件互相覆盖。
4.3 VIP 访问间歇性超时
现象:三台 master 高可用部署成功,访问 VIP 时有时秒回,有时卡到超时,kubectl偶发 TLS 握手失败。
原因:HAProxy 健康检查没有正确指向三台 master 的 6443 端口,或 keepalived 漂移后 kubeconfig 里的 server 地址没有用 VIP。
解决:先确认 HAProxy 配置里 backend 部分是否包含了三台 master:
echo | openssl s_client -connect 192.168.1.10:6443 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"这条命令检查 apiserver 证书 SAN 里是否包含 VIP 地址。如果输出里只有各节点 IP,没有 192.168.1.10,说明脚本生成证书时漏了 SAN,需要重新生成证书或修改 kubeadm 配置里的controlPlaneEndpoint。另外查看journalctl -u keepalived是否持续刷 state transition,VIP 反复漂移也容易造成连接不稳定。
4.4 证书过期:kubectl 直接报 expired
现象:部署满一年后访问集群,报x509: certificate has expired or is not yet valid。
原因:kubeadm 签发的证书有效期默认一年,很多一键安装脚本并没有真正实现自动续期,只是配置文件里留了auto_renew字段。
解决:在主节点手动续期所有证书,再重启 kubelet:
kubeadm certs renew all systemctl restart kubelet cp /etc/kubernetes/admin.conf ~/.kube/config关键点在于admin.conf里的客户端证书也参与续期,所以 renew 之后必须同步 kubeconfig,否则 kubectl 还是拿着旧证书去访问集群。长期维护的集群,我更建议把这条写进定时任务,每月跑一次,过期前就续好,比事后救火省心。
4.5 镜像拉取一直卡住或超时
现象:init 阶段卡在 pulling image 超过十分钟,网络正常但镜像迟迟拉不下来。
原因:默认镜像仓库在内网或部分地域访问不稳定,或者机器 DNS 解析异常。这不一定是脚本问题,更多是网络环境与默认仓库之间的连通性差异。
解决:在配置文件的image.repository里填内网镜像仓库地址,或在 containerd 配置里调整 mirror。如果脚本已经执行过半,照旧先kubeadm reset清干净再重新 init。离线包方案在这里最有效,Release 包里携带镜像 tar 时,优先用脚本的 load 能力提前导入,避免安装时依赖公网仓库。镜像能不能拉下来,是一键安装里最不可控的一环,脚本只能帮你把流程串起来,解决不了网络根源问题,提前准备好镜像仓库才是正道。
5. 进阶配置:从能跑到敢上线的几处调整
5.1 内网镜像仓库定制与离线安装
一键安装能跑通是最低标准,生产环境第一件事往往是改镜像仓库。配置里image.repository留空时,kubeadm 会从默认地址拉取镜像。内网环境里这既慢又不安全,更常见的是公司自建一套 registry,把所需镜像全部提前镜像进去,然后配置里这样改:
image: repository: registry.example.local:5000/k8s改完记得在每台节点上确认 containerd 能访问这个仓库。containerd 的镜像仓库认证配置在/etc/containerd/certs.d/或config.toml的 mirrors 段,如果 registry 用了自签名证书,还要把 CA 证书放到 containerd 信任目录里,否则 init 时一样拉取失败。离线部署更彻底的做法是用 Release 包自带的镜像 tar,先解压再 load 进 containerd,之后再执行 install,install 期间完全不依赖外网。
5.2 证书自动续期:把兜底做成定时任务
上一章的证书过期问题,值得专门做成一个机制。不少脚本的auto_renew只是配置项,实际没实现定时续期,所以不要相信字段,要在部署后主动验证。
验证方式很简单:看系统里有没有生成相关的 systemd timer 或 cron 任务。没有的话就自己补一个,每月执行一次:
kubeadm certs renew all >/var/log/k8s-cert-renew.log 2>&1 systemctl restart kubelet cp /etc/kubernetes/admin.conf ~/.kube/config这条命令和手动续期完全一致,放到 cron 里就是兜底。续期之后如果 kubeconfig 没有同步,kubectl 依然会报证书过期,所以把cp放进同一个任务里。这里踩过的重坑是只 renew 不重启 kubelet,证书文件换了,进程还拿着旧证书,现象和没续一样。记住顺序:renew、restart、copy kubeconfig,三步缺一不可。
5.3 flannel 换 calico 的正确姿势
默认 flannel 简单、部署快、无 BGP,适合大多数场景。但需要 NetworkPolicy 做网络策略、或者要跨子网精细路由时,calico 是更合适的选择。切换网络插件最稳妥的方式是重新搭一套集群,而不是在原集群上热替换。如果环境不允许重装,也能做,但要注意残留清理:
kubectl delete -f flannel.yaml rm -rf /etc/cni/net.d/* ip link delete flannel.1 kubectl apply -f calico.yaml顺序上必须先删 flannel 再删虚拟网卡,最后 apply calico。常见翻车是删了 flannel 配置但没有清理flannel.1虚拟网卡,calico 起来后路由表和 CNI 配置对不上,coredns 又开始 CrashLoopBackOff。切换完成后观察 coredns 和 kube-proxy 是否恢复正常,同时用跑一个跨节点的 pod 验证网络连通性。calico 默认网段是 192.168.0.0/16,如果配置里 pod_subnet 还是 flannel 的 10.244.0.0/16,需要同步改掉。
5.4 上线前必调的参数速查
| 参数 | 影响 | 默认值推荐值 |
|---|---|---|
pod_subnet | pod 网段,必须与 CNI 默认网段一致 | flannel:10.244.0.0/16;calico:192.168.0.0/16 |
service_subnet | service 虚拟 IP 段 | 10.96.0.0/12,避开机房网段 |
cgroup_driver | kubelet 与容器运行时一致性 | systemd,新版 containerd 推荐 |
cert_auto_renew | 证书过期修复策略 | false 时务必自建定时续期 |
coredns.replicas | DNS 服务可用性 | 生产 2~3 副本,配合反亲和 |
这五组参数里,前四组在 init 前定下来,改起来成本最高;最后一组可以在集群运行后用 deployment 滚动更新方式调整。给 coredns 多放两个副本能明显改善集群 DNS 的抖动问题,这个在排障时最容易被忽略。
6. 装完不等于能用:三步验收 k8s 集群
跑完一键安装,最后要做的不是看脚本输出里的一堆 success,而是自己做一次真实链路验证。第一步看节点和核心组件状态,第二步起一个实际负载,第三步模拟一次跨节点访问。
kubectl get nodes kubectl get pods -n kube-system kubectl create deployment smoke-test --image=nginx --replicas=2 kubectl expose deployment smoke-test --type=NodePort --port=80 curl <node-ip>:<node-port>curl 通了,说明 CNI、kube-proxy、调度器、kubelet 整条链路都是活的。如果这一步失败,先看 kube-proxy 日志,常见的坑是 node-port 端口被防火墙拦截,或者 kube-proxy 的 iptables 模式与服务器内核不匹配。
我养成的最后一个习惯,是每次安装完立刻把 init 日志归档。脚本输出的每一行警告都值得保留,集群三个月后出问题时,回看这些日志往往比在当时盯着更容易发现伏笔。k8s 集群搭建里很多所谓的玄学问题,几乎都能在当年安装日志里找到答案。希望这个方向能帮你的 k8s 集群搭建变成可复现、可回滚、可排查的例行工作,而不是每次从零开始的冒险。希望帮到你。
本文还有配套的精品资源,点击获取