简介:这份压缩包是基于 sealos 打包的 Kubernetes 1.20.12 离线部署介质,面向需要快速搭建、升级或维护 Kubernetes 集群的运维工程师与开发人员;通过 sealos 自动化脚本,可降低手动配置 etcd、kubelet 与网络插件等环节的复杂度,尤其适合内网环境、生产集群初始化及版本固定场景。包内共 59 个文件,整体约 530.85MB,包含 shell 部署脚本、YAML 资源配置、systemd service 文件,以及 kubeadm、kubectl、crictl、nerdctl、conntrack 等核心二进制和镜像 tar,覆盖从容器运行时到集群初始化的关键组件。目前已有 226 人学习下载。借助该介质,用户可获得一套完整的离线安装链路:脚本负责初始化与 master 节点配置,YAML 定义 calico 网络策略,镜像包免去公网拉取麻烦,附带的说明文档与 libseccomp 等依赖也有助于排障和复现;对追求快速交付且需保持集群版本一致的场景,这份打包产物能有效提升部署效率与可维护性。
1. kube1.20.12.tar.gz 与 sealos:一套能离线交付 K8s 集群的组合拳
在内网环境里装一套 Kubernetes 1.20.12 集群,最让人头疼的不是 kubeadm 那几行命令,而是怎么把镜像、二进制、依赖包全都搬进去。kube1.20.12.tar.gz 这个压缩包加上 sealos,就是把这件事从"网络拉取+手动分发"变成"一个文件搞定"的方案。说到底,sealos 把 K8s 集群本身做成了可以分发、加载、复用的镜像,你拿到这个 tar.gz,就相当于拿到了整套集群的"安装盘",不管目标机器有没有外网,只要环境符合要求,就能在十几分钟里拉起一个带负载均衡的高可用集群。这篇笔记适合在内网交付 K8s 的运维,也适合刚接触 sealos、想知道这个离线包里到底有什么、装完怎么验证的人。
2. 先搞懂 sealos 怎么让一个 tar.gz 变成高可用集群:集群镜像的加载与部署链路
很多人第一次用 sealos 都会有个疑问:这不是一个普通的压缩包吗?怎么解压之后就能装集群?实际上 sealos 的核心思路是把 K8s 集群分发做成类似 Docker 镜像的机制,kube1.20.12.tar.gz 这个文件在 sealos 里叫"集群镜像",它不是一个简单的 tar,而是一套带元数据、脚本、依赖的完整交付物。
2.1 集群镜像与容器镜像的区别:包里面装的不只是 docker 镜像
容器镜像打包的是某个应用的运行环境,而 sealos 的集群镜像打包的是整个 K8s 控制面加工作节点的部署逻辑。打开一个 kube 离线包,里面通常有这几类内容:
- docker 镜像归档,比如 kube-apiserver、kube-controller-manager、kube-scheduler、etcd、pause、coredns 这些组件镜像的 tar 文件
- 二进制文件,比如 kubeadm、kubectl、kubelet、etcdctl,以及 sealos 自带的 lvscare 等工具
- 部署脚本和配置模板,包括 kubeadm 的配置、服务文件、证书生成逻辑、镜像加载逻辑
sealos 在加载这个包的时候,会先按脚本把内容分发到各个节点,然后执行类似 kubeadm init 和 kubeadm join 的流程。和你在每台机器上手动敲 kubeadm 相比,sealos 把"生成配置文件、启动静态 Pod、建立负载均衡、join 节点"这些步骤都编排好了,你只需要告诉它 master 和 node 的 IP 是什么。
这里要特别提一下 lvscare。这是 sealos 自带的一个轻量负载均衡器,它会在 master 节点上起一个 LVS 模式的代理,把访问集群 VIP 的请求转发到各个 apiserver 上。这也是为什么 sealos 装出来的集群没有靠 keepalived + haproxy 也能实现 apiserver 高可用,因为 lvscare 就是干这个活的。理解这一点,后面排错的时候思路就清晰了。
2.2 从 tar.gz 到 api-server:sealos 部署链路的关键节点
一次完整的 sealos 离线部署,内部大概要经过这几个阶段:
第一阶段是环境准备。sealos 会检查每台目标机器的操作系统版本、内核模块、swap 状态、端口占用情况,比如 6443、2379、10250 这些端口不能提前被别的进程占住。它还会把 /etc/hosts 里追加节点 hostname 的映射,避免后续 kubeadm 解析失败。
第二阶段是镜像分发。sealos 会通过 SSH 把 kube1.20.12.tar.gz 里的内容推送到所有 master 和 node 节点,然后在每个节点上解压、导入 docker 镜像。这一步耗时取决于网络和磁盘,如果节点多,建议先用内网的 HTTP 服务把包分出去,再让 sealos 直接加载本地文件,能省不少传输时间。
第三阶段是控制面初始化。sealos 会选择第一个 master 作为初始节点,生成 kubeadm 配置文件,指定 apiserver 的 VIP 地址和端口,然后执行 kubeadm init,把 etcd、apiserver、controller-manager、scheduler 以静态 Pod 的方式跑起来。
第四阶段是集群 join。剩下的 master 和所有 node 节点通过 kubeadm join 加入集群,sealos 把 join 所需的证书和 token 自动分发过去。整个链路里最容易出问题的是第二阶段的镜像导入和第三阶段的 apiserver VIP 配置,后面避坑章我会单独展开。
2.3 kube1.20.12 离线包里的组件构成
kube1.20.12 这个版本号指的就是 Kubernetes v1.20.12,它属于 1.20 系列的最后一个补丁版本,修复了之前若干安全问题,在 2022 年之前是很多内部系统的默认选择。这个版本对应的离线包里,核心组件版本大概是这样对应的:
- kube-apiserver / kube-controller-manager / kube-scheduler:v1.20.12
- etcd:3.4.13 左右,支持 kubeadm 默认集成
- coredns:1.7.0 左右,提供集群内 DNS 解析
- pause:3.2,作为 Pod 的基础容器
- kube-proxy:v1.20.12,iptables 模式默认开启
不要小看这个版本对应关系。如果你拿到的包是别人重新整合过的,组件版本可能不完全一致。我第一次用 sealos 装 1.20.12 时,遇到过 etcd 镜像版本和 kubeadm 默认值不一致的情况,导致 etcd 静态 Pod 启动后一直 CrashLoopBackOff。后来学到的办法是装完后立刻用kubectl get pods -n kube-system -o wide核对每个组件的镜像版本,别等业务上了再发现底层版本不对。
3. 用 sealos 部署 kube1.20.12 集群:从环境检查到 join 的完整流程
原理先放一边,直接进入能复现的步骤。这一章我按一次典型的三 master 两 node 部署来说,所有命令都是在离线内网环境里执行过的,参数以实际环境为准。
3.1 部署前检查:OS 版本、内核参数、swap 与端口占用
sealos 对操作系统有要求,一般推荐 CentOS 7.6 以上或者 Ubuntu 18.04/20.04,内核版本不能太低。装之前先把所有节点的检查做了,别等 sealos 跑到一半报错再回头补。
# 查看操作系统和内核版本 cat /etc/os-release uname -r # 关闭 swap,kubeadm 强制要求 swapoff -a sed -i 's/.*swap.*/#&/' /etc/fstab # 加载内核模块并设置参数 cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF modprobe br_netfilter sysctl -p /etc/sysctl.d/k8s.conf # 检查 6443/2379/2380/10250 端口是否被占用 ss -lntp | grep -E '6443|2379|2380|10250'这段命令做的事情分三块:第一块是确认发行版和内核,K8s 1.20 对内核没有强制最低版本,但实践中 CentOS 7 的内核要在 3.10 以上,Ubuntu 18.04 以上没问题。第二块是关 swap,kubelet 在 swap 开启时默认会报错,sed修改 fstab 是为了重启后仍然生效。第三块是内核参数和端口检查,net.bridge.bridge-nf-call-iptables不打开,集群内 Service 的 iptables 转发会异常,ss检查端口是为了避免后面 kubeadm 初始化时提示端口被占用。
如果你是 Ubuntu 系统,还需要额外执行apt-get install -y ipvsadm ipset,因为 lvscare 依赖这些工具来做 LVS 转发。CentOS 上则用yum install -y ipvsadm ipset,这个坑我踩过,缺了就报ipvsadm not found。
3.2 导入 kube1.20.12.tar.gz:sealos load 与本地离线加速
拿到 kube1.20.12.tar.gz 之后,第一步是把它放到所有节点都能访问的位置。对于单机测试,直接放在 /root 下就行;对于多节点集群,我一般先在一台节点上解压,然后通过内网 HTTP 或者 scp 分发给其他节点。
# 在第一个 master 节点上加载集群镜像 sealos load --input /root/kube1.20.12.tar.gz # 查看已有集群镜像 sealos imagessealos load会把 tar.gz 中的镜像和脚本注册到 sealos 的本地镜像仓库里,之后sealos images就能看到类似kube1.20.12的一条记录。这一步起到的作用是把"安装包"变成 sealos 能直接调用的"镜像模板"。如果你用的 sealos 版本比较老,命令可能是sealos init --pkg-url /root/kube1.20.12.tar.gz,这个也好理解,直接指定包路径。
加载完成后,建议把包文件传到内网的一台文件服务器上,给每个节点配置好能通过内网 IP 访问这个文件的路径。sealos 在后续分发的过程中会自动去对应节点拉这个包做本地加载,如果你的内网带宽够大,可以不做预分发;如果节点多,预分发到 /root 下再执行能减少整体等待时间。
3.3 初始化 master 并 join node:sealos 命令与参数说明
加载完集群镜像,就可以直接创建集群了。这里我以新版的sealos run为例,旧版sealos init的参数也基本对应。
# 三 master 两 node 的离线集群,指定 VIP 与服务网段 sealos run kube1.20.12 \ --masters 192.168.1.10,192.168.1.11,192.168.1.12 \ --nodes 192.168.1.20,192.168.1.21 \ --passwd 'YourPassword' \ --vip 192.168.1.100 \ --cni calico # 查看集群状态 kubectl get nodes -o wide kubectl get pods -n kube-system -o wide参数说明:
--masters和--nodes用逗号分隔 IP,sealos 会按顺序分配角色。第一个 IP 是初始 master,后面的做 HA 扩展--passwd是各节点的 SSH 密码,sealos 靠它来做免密登录和文件分发。如果你的环境已经配置了 SSH 密钥,也可以不传这个参数--vip是 apiserver 的虚拟 IP,所有 node 和客户端都通过这个地址访问控制面,必须是一个未被使用的 IP,并且和节点在同一网段--cni指定网络插件,离线环境里 calico 的镜像需要提前打进包,或者你自己准备好镜像仓库
执行之后 sealos 会输出一段很长的日志,包含每台节点的分发进度和 kubeadm init 结果。看到Your Kubernetes control-plane has been initialized successfully就说明初始化成功。如果日志停在某台节点的 SSH 连接上,先检查密码和 IP 是否正确,别急着重跑,sealos 重跑不会自动清理之前的残留文件,容易出现部分节点已加入、部分节点失败的情况。
3.4 验证集群可用性:kubectl 与核心插件清单
集群初始化完成不等于就能跑业务,核心插件必须确认都是 Running。
# 检查节点状态,Ready 才是正常的 kubectl get nodes -o wide # 检查 kube-system 下的所有 Pod kubectl get pods -n kube-system -o wide # 查看 apiserver VIP 是否正常转发 curl -k https://192.168.1.100:6443/healthz这一步要注意两个点:第一,节点状态如果一直是 NotReady,大多数情况是 CNI 网络插件的问题,calico 的 Pod 没起来或者 node 之间的网卡选错了,返回去看 calico 日志;第二,curl https://VIP:6443/healthz能通才说明 lvscare 的负载均衡在正常工作,如果不通,集群内部 join 的节点会间歇性访问 apiserver 失败。我一般还会加一步kubectl get cs看 controller-manager 和 scheduler 是否健康,虽然 1.20 版本这个命令可能返回一堆 Warning,但至少能确认组件进程还活着。
4. sealos 部署 kube1.20.12 避坑指南:5 个真实翻车点的现象与排查
这一章写的是我实际部署中遇到过、以及帮别人排查过的问题。每一条都是"现象 -> 原因 -> 解决"的结构,如果你在装的过程中卡住,按顺序对着查,大部分能省下半天时间。
4.1 离线包与 sealos 版本不匹配,报 "no such file or directory"
现象:执行sealos run或sealos load的时候,日志里出现open /root/kube1.20.12.tar.gz: no such file or directory,但文件明明存在。
原因:这是老版本 sealos 和 kube1.20.12.tar.gz 打包格式不兼容导致的问题。新版的集群镜像格式加了校验信息和新目录结构,旧版 sealos 无法识别。另一种可能是你是根目录执行的命令,但包实际放在了别的路径下。
解决:先检查你的 sealos 版本,sealos version,如果版本太老就先升级到能兼容集群镜像的版本。然后把包的绝对路径写全,不要用相对路径,比如sealos load --input /home/user/kube1.20.12.tar.gz。如果确认格式确实不兼容,用压缩工具打开包看一眼目录结构,新版包一般会有images、scripts、manifests这些目录,旧版包可能只有一堆 tar 文件,这时候你需要找对应版本的 sealos 二进制。
4.2 多网卡机器上 apiserver 地址漂移,join 总超时
现象:所有节点初始化都正常,node join 的时候报connection refused或者一直卡在kubelet-start阶段。检查发现 apiserver 监听地址变成了内网网卡,node 通过 VIP 访问不了。
原因:sealos 自动探测默认网卡时选错了一张网卡。比如机器同时有 eth0 和 eth1,一个负责管理流量,一个负责存储流量,sealos 可能选了那张不在集群网段里的网卡,导致 apiserver 地址不对。1.20.12 这个版本本身对单网卡环境假设很强,多网卡场景本身就是坑。
解决:在 sealos 初始化参数里显式指定网卡,或者在 kubeadm 配置里设置advertiseAddress。sealos 有的版本支持--iface参数,不支持的版本需要先跑一次kubeadm config print init-defaults,然后修改localAPIEndpoint下的advertiseAddress为正确 IP,再用 sealos 的--config传入。生产环境我建议所有节点统一为同一张网卡提供服务,避免各种地址错乱。
4.3 lvscare 与 keepalived 抢虚拟 IP,集群状态互相打架
现象:集群里有 old 的 keepalived 服务在跑,sealos 装好后,VIP 一会有时通,有时候不通,且主备节点同时持有 VIP 或者互相抢占。
原因:keepalived 和 sealos 自带的 lvscare 都用了 VRRP 或 LVS 的管理 VIP,两者机制冲突。keepalived 的虚拟实例和 lvscare 的 LVS 规则同时作用于同一个 IP,导致流量分发错乱。这是个典型的存量环境问题,新旧负载均衡互相踩。
解决:装之前先清理干净节点上的 keepalived,systemctl stop keepalived && systemctl disable keepalived,并删除/etc/keepalived下的配置。如果你无法完全停用 keepalived,那就给 sealos 换一个 VIP 网段或者干脆不用 VIP,直连第一个 master 的 IP 做测试。生产环境建议统一方案,不要两个负载均衡并存,我见过最离谱的情况是 VIP 被 keepalived 抢走,lvscare 还在按自己的健康检查结果改 LVS 规则,整个集群的网络入口像抽风一样。
4.4 pause 镜像拉取失败,Pod 一直 ContainerCreating
现象:kube-system 和业务 Pod 都卡在 ContainerCreating,describe 看事件是Failed to pull image "pause:3.2"或者image pull back off。
原因:kubelet 在启动任何 Pod 之前都会先拉 pause 镜像,sealos 部署时如果 pause 镜像没有正确导入到所有节点,或者节点上 docker 的拉取策略走了远程仓库,就会卡住。尤其是在离线环境里,节点无法访问外网,拉取必然失败。
解决:先确认每个节点上 pause 镜像是否真的存在,crictl images | grep pause或docker images | grep pause。如果不存在,手动从 master 节点导出再导入:docker save k8s.gcr.io/pause:3.2 -o pause.tar,然后 scp 到各节点docker load -i pause.tar。另外,检查/etc/containerd/config.toml或 docker daemon 的 registry-mirror 配置,确保没有强制走一个离线环境里根本不可达的镜像源。还有就是--cni指定的网络插件镜像,如果在离线包之外,也要一并导入。
4.5 etcd 写入慢与磁盘 inode 不足导致认证超时
现象:集群装完后 etcd 偶尔 unhealthy,kubectl get cs显示 etcd 健康检查失败,apiserver 日志里有etcdserver: request timed out,检查磁盘发现分区 inode 用完了。
原因:etcd 的数据目录默认放在 /var/lib/etcd,如果系统盘是一块容量小且 inode 数少的分区,安装过程中大量小文件写入会很快耗尽 inode。加上 1.20.12 的 etcd 对磁盘 IO 比较敏感,机械盘在高峰期会出现写超时。
解决:初始化之前用df -i检查各节点的 /var/lib/etcd 所在分区 inode,剩得少就先给系统盘扩容或者把数据目录放到独立的数据盘。通过修改 kubeadm 配置指定 etcd 数据目录路径,或者手动做软链接把 /var/lib/etcd 指向大分区。etcd 一旦启动后最好不要动数据目录,迁移要考虑快照备份。另外日志检查时journalctl -u etcd能看到took too long这种关键字,这说明磁盘 IO 已经不行了。
5. sealos 生产化参数调优:把默认集群改成能上线跑的配置
集群能起来只是第一步,真正要上线跑业务,还得调一批参数。默认配置解决的是"能用"问题,这一章解决的是"好用"问题。
5.1 证书时长、SANS 与 apiserver 参数:提前改掉默认值
K8s 1.20 的默认证书有效期是一年,sealos 部署的集群也是这个默认值。对内部系统来说,每年都要手动续期很烦,所以我在生产环境都会把证书时长改成 10 年,同时把 apiserver 的 SAN 加全。
# kubeadm-config.yaml 中关键的修改点 apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration kubernetesVersion: v1.20.12 controlPlaneEndpoint: "192.168.1.100:6443" apiServer: certSANs: - "192.168.1.100" - "k8s-cluster.example.com" - "127.0.0.1" networking: serviceSubnet: "10.96.0.0/12" podSubnet: "10.100.0.0/16" --- apiVersion: kubeadm.k8s.io/v1beta2 kind: InitConfiguration certificateValidityPeriod: 87600h这个配置需要你通过 sealos 的--config参数传入,具体路径不同版本略有差异。certificateValidityPeriod设置的是证书有效期,87600h是 10 年;controlPlaneEndpoint指定 VIP;certSANs里必须包含 VIP、域名和你需要通过外部访问 apiserver 的地址。漏掉 SAN 导致后续加节点或者用集群外部客户端访问时报证书验证失败,这是最常见的证书坑。
参数说明:serviceSubnet和podSubnet要跟 sealos 初始化时用的网段完全一致,不一致会导致 Service 和 Pod 的 IP 地址冲突。如果集群已经初始化,改这些参数只能重建集群,没有后悔药,必须在 init 前确认清楚。
5.2 离线环境的镜像仓库落地:用 sealos 自建 registry
装完集群只是第一步,你的业务镜像怎么进到集群里?离线环境最常见的手段是内网搭一个 Harbor 或者 registry,然后在所有节点上配置 docker 或者 containerd 信任这个仓库地址。sealos 本身不强制绑定某个 registry,但它在部署 CNI 和业务镜像之前,需要一个稳定的内网镜像源。
# 在任意一台节点上启动一个简单的 registry 容器 docker run -d --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ --restart=always \ registry:2 # 所有节点配置 insecure-registry cat > /etc/docker/daemon.json <<EOF { "insecure-registries": ["192.168.1.10:5000"] } EOF systemctl restart docker在这里registry:2这个镜像要提前在你的离线包里准备好,也可以用你已有的镜像仓库导入。把业务镜像 tag 成192.168.1.10:5000/namespace/app:tag然后 push 上去,各个节点就能直接拉取。注意配置 daemon.json 之后一定要重启 docker,重启后 kubelet 也会跟着重启,这个顺序别弄反。
实际生产里我会建议用带认证和镜像清理策略的 Harbor,而不是裸 registry。裸 registry 在镜像多的时候会占用大量磁盘,而且没有 Web UI,排错不方便。你先用 registry 跑通链路,再迁移到 Harbor,逻辑是一样的。
5.3 版本升级与回滚:从 kube1.20.12 往上的取舍
1.20.12 毕竟是 2021 年的版本,如果后续有安全扫描要求,你可能需要升级到更高版本。sealos 的场景里,升级通常意味着重新构建集群镜像或者下载新版离线包,然后用新版本的镜像重新跑一遍部署流程,但这个过程并不是无缝的。
核心要记住的是:K8s 小版本升级不能跨大版本,1.20 到 1.21、1.22 逐级走。sealos 部署的集群在升级前必须先备份 etcd,etcdctl snapshot save是唯一的后悔药。升级完还要检查 CNI 兼容性,calico 的版本和 K8s 1.20、1.2x 之间的 API 差异可能让网络插件直接崩掉。
如果你暂时不升级,就要接受 1.20 的一系列限制:内置的 Ingress 演进不完整、PodSecurityPolicy 在后续版本会被替换掉、kubectl 的新特性用不上。我的习惯是生产环境小版本持续跟踪补丁,大版本每年规划一次,不要让集群停在旧版本太长时间,等你想升级的时候,中间隔了太多小版本反而更难。
6. 用 sealos 装完 kube1.20.12 后,验证交付成功的三个细节
装好集群、调完参数,怎么判断这单活真的干完了?我一般不会只看kubectl get nodes全是 Ready 就收工,而是花十几分钟做三个层面的验证。
第一个是 etcd 的健康和切换验证。etcd 是三节点集群时,用etcdctl endpoint health逐个检查,然后在一台 master 上停掉 etcd 静态 Pod,等几秒看 apiserver 是否还能正常读写。如果 lvscare 和 kubeadm 配置正确,另外两个节点的 etcd 会接管,业务 Pod 不感知。这里要特别留意etcd member list输出的节点 ID 是否和 master IP 对应,IP 错位会导致后续异常时的排查非常痛苦。
第二个是 apiserver 高可用验证。从任意 node 上用curl -k https://VIP:6443/healthz重复请求几十次,观察是否有失败。然后手动停掉第一台 master 上的 kubelet,或者直接关机模拟故障,再看 node 的 kubelet 是否还能连接 apiserver 并继续维持 Pod 运行。Vip 背后的 lvscare 会把流量转到健康的 master 上,整个切换过程不应该出现 node 掉线。这个验证一定要在你预期要交付给客户的网络环境里做,而不是在临时环境里做,网络权限、防火墙差异可能在交付现场埋雷。
第三个是业务 Pod 的并发创建测试。我会准备一个简单的 deployment,副本数设置成 20,同时观察 Pod 调度速度、Service 访问正常、DNS 解析正常。重点看有没有 Pod 长时间 Pending,以及 coredns 的解析延迟。如果这里出现异常,多半是节点资源不足或者 CNI 配置遗漏,与其等客户发现,不如交付前自己先跑一遍。
这套验证跑下来,我对这个集群才算有底。这些年用 sealos 装过不少集群,最大的体会是:离线包解决了分发的麻烦,但真正决定交付质量的是验证环节。有些集群装完看着正常,一压流量就暴露问题。希望这篇笔记能帮你在内网里少走几次弯路,把时间省下来放在真正的业务上。
本文还有配套的精品资源,点击获取