拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Kubernetes离线部署实战:内网环境搭建开发测试集群的完整指南

我们做项目交付或者内部研发的时候经常会碰到一个很头疼的场景客户的机房是内网隔离的没有外网更不可能让你随便连Docker Hub和Google的镜像仓库。但业务又跑在Kubernetes上还得在开发测试环境里把整套东西落地。我这些年经手了不少这类环境从最早手动rpm一个个装到后来整理出一套相对标准的离线部署流程踩过的坑确实不少。这篇就专门聊一下Kubernetes离线部署到开发测试环境的完整操作思路和实操细节。这篇文章适合谁看准备在内网、隔离网络、或者临时无外网环境搭K8s集群的运维和开发同学也包括需要在离线环境部署Superset、OnlyOffice、甚至大模型推理服务的场景。总体思路是——把“在线安装”里所有依赖的外部资源提前打包成内网可用的离线物料然后通过本地仓库分发。整个过程看起来环节多但只要把物料清单理清楚实际执行起来比在线安装还稳因为不受网络波动影响。1. 项目背景与离线部署的整体思路1.1 为什么开发测试环境也要做离线部署很多人觉得开发测试环境嘛机器都在公司内网能上外网直接在线装不就行了但真实情况往往不是这样。大一点的公司都有安全策略研发网和办公网隔离生产环境更是严格禁止外网连接。开发测试环境作为生产环境的预演很多时候也需要模拟和它一致的部署条件这就有两个刚性需求一是环境一致性二是可复现性。还有一个容易被忽略的点——软件供应链的确定性。在线部署Kubernetes集群版本是浮动的今天装可能是v1.28.3过两个月再装就成了v1.28.5如果刚好碰到上游仓库调整或者依赖版本不兼容整个环境都起不来。离线部署把所有的安装包、镜像、依赖都固化在一个物料包里版本一锁定什么时候装、由谁来装结果都是一样的。这个确定性对开发测试环境的稳定性非常关键。1.2 离线部署的范围边界与核心挑战离线部署Kubernetes要处理的东西可以拆成三层一层是二进制和系统依赖一层是容器镜像最后一层是配置和运维工具。具体到组件包括kubeadm、kubelet、kubectl三个核心二进制容器运行时containerd或DockerCNI网络插件Flannel或CalicoCoreDNS等集群插件以及后续要用的Ingress Controller、存储插件、监控组件。如果还要跑应用那Superset、OnlyOffice、DeepSeek这类服务的镜像也得一并准备好。离线部署的核心挑战一句话概括就是“物料管理”。在线安装时kubeadm会自动到官方仓库拉取所需镜像网络通就行离线环境下这些全都得自己解决。镜像的导出、导入、版本对齐是最容易出问题的环节。比如你在联网机器上docker pull拉取的镜像版本必须和kubeadm默认拉取的版本严格一致否则kubeadm init的时候会直接报错连不上镜像仓库。2. 离线部署前的物料准备与基础环境配置2.1 基础运行环境与版本选型离线部署第一步是确定一套基础环境的“黄金组合”。我在多个项目里验证下来比较稳的组合是操作系统用CentOS 7.9或Rocky Linux 8.x架构x86_64内核版本不低于3.10Kubernetes版本选1.23到1.28之间的稳定版本容器运行时选containerd 1.6以上版本网络插件用Flannel或Calico二选一。为什么把版本范围框得这么死因为新老版本之间API和能力变化很大。比如Kubernetes从1.24版本开始彻底移除了dockershim支持如果你用1.24以上版本还打算直接用Docker作为运行时就需要额外装cri-dockerd适配器。开发测试环境追求的是稳定可复现没必要追新选一个自己熟悉的稳定版把流程跑通比什么都重要。两个关键选型逻辑先说明白容器运行时新版本建议直接上containerd少一层Docker到containerd的转换排错也简单网络插件Flannel配置简单、适合开发测试环境Calico功能强大但配置项多离线场景下建议先用Flannel跑通基础集群后续再按需切换。2.2 系统初始化与离线源配置在开始装Kubernetes之前系统层面的初始化必须先做好这些操作和在线部署一样但有几个注意事项。操作系统装好后需要关闭SELinux关闭swap配置好主机名和hosts解析。具体操作如下# 关闭SELinux sudo setenforce 0 sudo sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config # 关闭swap sudo swapoff -a sudo sed -i / swap / s/^/#/ /etc/fstab # 主机名与hosts sudo hostnamectl set-hostname k8s-master01 cat /etc/hosts EOF 192.168.10.11 k8s-master01 192.168.10.12 k8s-node01 192.168.10.13 k8s-node02 EOF内核网络参数也需要调整主要是让iptables能正确处理K8s的流量转发cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 net.bridge.bridge-nf-call-ip6tables 1 EOF sudo sysctl --system这里我踩过一个坑如果在云服务器上做有些云厂商的安全组策略会影响overlay网络通信K8s集群即使起来了跨节点的Pod网络也可能不通。所以在初始化之前先确认节点之间的端口放行情况TCP 6443、2379-2380、10250、10255这些端口必须互通。2.3 离线软件物料清单的整理这是离线部署最核心的一步要把所有需要用到的安装包、镜像提前准备好。我一般会建一个offline-k8s目录目录结构大概是这样的offline-k8s/ ├── bin/ # kubeadm, kubelet, kubectl ├── images/ # 所有容器镜像tar包 ├── rpm/ # 系统依赖包 ├── containerd/ # 容器运行时安装包 ├── cni/ # CNI插件二进制 └── helm/ # Helm工具及安装包rpm依赖包怎么收集找一个同样操作系统的能联网的机器用yumdownloader把需要的包全部下载下来再拷贝到离线机器上。yum install -y yum-utils yumdownloader --resolve --destdir/tmp/k8s-rpm kubeadm kubelet kubectl镜像准备这里多说几句。Kubeadm初始化集群时会拉取一堆镜像包括kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、coredns、etcd、pause。具体版本可以先用kubeadm config images list --kubernetes-versionv1.28.2查出来然后在联网机器上拉取并导出kubeadm config images list --kubernetes-versionv1.28.2 docker pull registry.k8s.io/kube-apiserver:v1.28.2 docker save registry.k8s.io/kube-apiserver:v1.28.2 -o kube-apiserver-v1.28.2.tar注意如果你有代理或者镜像加速源导出的镜像仓路径必须和离线环境里kubeadm配置的image-repository一致否则kubeadm实际拉取时还是找不到。我通常建议整个集群的镜像统一导入到自建的Harbor私有仓库然后kubeadm init时指定--image-repositoryregistry.offline.local这样版本和路径都在自己的掌控之内。3. 集群组件的离线安装与初始化3.1 containerd与kubeadm的安装物料准备好之后就进入正式的安装阶段。先装containerd我推荐用官方提供的rpm包或者tar包安装不要用yum源自带的版本因为版本可能比较老和K8s的兼容性没有保证。# 解压并安装containerd tar -C /usr/local -xzf containerd-1.7.13-linux-amd64.tar.gz mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml这里有个关键配置要改containerd默认的sandbox_image是指向registry.k8s.io的pause镜像离线环境必须改成我们自己镜像仓库里的pause镜像路径。修改/etc/containerd/config.toml[plugins.io.containerd.grpc.v1.cri] sandbox_image registry.offline.local/k8s/pause:3.9同时为了能让containerd正常从私有仓库拉镜像还得在config.toml里配置好镜像仓库的endpoint和认证信息。改完配置重启containerdsudo systemctl enable containerd sudo systemctl restart containerd接着安装kubeadm、kubelet、kubectl。离线机器上没法直接用yum源装所以直接把之前准备的文件拷过去装# rpm包安装 rpm -ivh kubeadm-1.28.2-0.x86_64.rpm kubelet-1.28.2-0.x86_64.rpm kubectl-1.28.2-0.x86_64.rpm # 或者使用二进制方式 cp kubeadm kubelet kubectl /usr/local/bin/ systemctl enable kubelet注意kubelet装好后默认是起不来的因为还没有集群配置这是正常现象不用慌。下一步才是关键。3.2 kubeadm init初始化与网络插件部署集群初始化之前先创建配置文件这里建议不要全用命令行参数而是写一个完整的kubeadm配置文件方便后续追溯和重复执行。配置文件示例如下# kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 imageRepository: registry.offline.local/k8s controlPlaneEndpoint: 192.168.10.11:6443 # VIP或Master节点IP networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.11 bindPort: 6443有个容易踩坑的点imageRepository一定要和你在私有仓库里的路径完全匹配包括前缀和层级。比如我在Harbor里建了k8s这个项目镜像路径是registry.offline.local/k8s/kube-apiserver那配置里就写registry.offline.local/k8s。如果多了或少了层级初始化必失败。执行初始化kubeadm init --config kubeadm-config.yaml --upload-certs初始化成功后会输出一段kubeadm join命令务必保存好。如果中途失败不要直接重复init先排查用kubeadm reset清理残留再继续。网络插件部署在离线环境里也要用离线镜像。对于Flannel可以直接用它的manifest文件把镜像地址替换成私有仓库地址后kubectl apply -f。manifest文件本身可以从联网机器上提前下载也可以从GitHub Releases获取。我会在准备阶段把常用的Deployment YAML都下好统一放到offline-k8s目录。安装完成后验证集群状态kubectl get nodes kubectl get pods -n kube-system全部Running状态就说明集群基础架构搭起来了。3.3 工作节点加入集群工作节点的加入流程和Master节点基本一致唯一区别是不用执行kubeadm init直接执行join命令就行。但要注意工作节点上的containerd配置必须和Master一致特别是sandbox_image要指向同一个私有仓库的pause镜像。kubeadm join 192.168.10.11:6443 --token token --discovery-token-ca-cert-hash sha256:hash如果token过期了用下面的命令重新生成kubeadm token create --print-join-command还有一个常见的坑工作节点加入后状态卡在NotReady。这时候不要慌先看节点上的kubelet日志journalctl -u kubelet -f大部分情况不是CNI没部署好就是pause镜像拉不下来要么就是节点时间不同步。开发测试环境里节点时间不同步其实是特别常见的隐蔽问题K8s对证书和TLS非常敏感节点间时间差超过5分钟很多请求都会报证书校验错误。建议在所有节点上都配好NTP或者至少手动校时。4. 容器镜像离线导入与开发测试环境的常用组件部署4.1 镜像仓库选型与离线镜像导入方案集群起来了后面的事情其实更磨人——各种业务镜像怎么在离线环境里分发。方案一不搭私有仓库直接在每台节点上导入镜像。用ctr命令把导出的tar包导入到K8s使用的命名空间ctr -n k8s.io images import kube-apiserver-v1.28.2.tar这个方式适合集群规模小、节点少的场景但如果你有几十个节点每个节点手动导入效率太低镜像多了管理起来也是一团乱麻。方案二推荐搭一个Harbor私有仓库。Harbor本身支持离线安装安装包里包含了它自己需要的所有镜像按照官方文档就能跑起来。装好Harbor之后在联网机器上把需要的镜像统一打好tagpush到Harbor。然后所有节点上的containerd配置都指向这个Harbor。这样后续不管是部署Superset、OnlyOffice还是大模型推理服务都是标准的pull和push流程跟在线环境没有任何区别。Harbor离线安装包可以从官方GitHub Releases下载里面有个harbor-offline-installer-v2.9.0.tgz解压后修改harbor.yml把hostname改成你的内网IP执行install.sh就行。它依赖Docker和Docker Compose这两样也要在离线环境下先准备好。镜像导出和导入的过程有一个细节特别重要tag的完整路径。比如你要部署Superset原始的镜像路径可能是apache/superset:4.0.1你推到私有仓库后变成registry.offline.local/library/apache-superset:4.0.1那么在Deployment的YAML里image字段就必须写这个完整的私有仓库路径。很多同学在这个环节翻车拉下来用了原始的镜像名私有仓库又没有这个路径自然就ImagePullBackOff了。4.2 典型开发测试服务的离线部署实操开发测试环境里最常碰到的三类离线需求BI可视化工具、在线文档服务、大模型推理。以Superset为例离线部署其实不复杂但有几个坑比较典型。Superset依赖一个元数据库默认是SQLite生产建议用PostgreSQL我们需要把Superset和PostgreSQL两个镜像都准备好。在联网机器上docker pull apache/superset:4.0.1 docker pull postgres:15 docker tag apache/superset:4.0.1 registry.offline.local/library/apache-superset:4.0.1 docker push registry.offline.local/library/apache-superset:4.0.1然后在K8s里部署一个简单的StatefulSet或Deployment把Superset的7777端口暴露出来配置好环境变量和持久化存储就行了。Superset需要初始化数据库启动命令里要加superset db upgrade superset init这两个步骤很容易被忽略导致服务看似起来了但访问的时候一堆报错。OnlyOffice的离线部署也类似它依赖PostgreSQL、RabbitMQ和Redis整套联动的服务比较多。这里更推荐用Helm方式部署在联网机器上把OnlyOffice的Helm Chart和依赖的所有镜像都提前拉下来然后离线install。OnlyOffice性能调优时要注意JVM内存参数默认配置在开发环境够用但如果你要测试大文档并发转换建议单独把DocumentServer的内存调大。4.3 大模型服务在离线K8s上的部署要点最近的离线部署需求里很大一部分是跑大模型推理服务比如DeepSeek的8B模型。这类服务的核心问题是镜像大、显存要求高、推理框架版本敏感。部署在K8s上有几点和普通服务不一样。第一镜像双层准备。大模型服务的镜像一般分两层概念推理框架镜像和模型权重。推理框架镜像可以用docker pull拉取后离线导入模型权重建议不要打进镜像里而是用持久化存储挂载进去。原因很简单模型文件几个GB到几十GB不等打镜像会导致每次分发都非常慢而用存储挂载可以一次分发、多处使用。第二GPU调度。如果你在开发测试环境里要验证GPU推理节点上需要提前装好NVIDIA驱动、nvidia-container-toolkit并且K8s集群要部署对应的Device Plugin才能识别GPU资源。离线环境装GPU的坑更多驱动依赖的kernel-devel版本必须和当前内核完全一致稍微不对就编译不了需要在物料准备阶段就严格锁定环境。第三推理服务的健康检查。大模型启动往往要拉取和加载模型这个过程可能持续几十秒到几分钟如果默认的livenessProbe设置太激进Pod会被反复重启。实际配置时我会把initialDelaySeconds设到120秒以上先用readinessProbe确认服务真正就绪。5. 常见问题与排查技巧实录5.1 镜像拉取失败类问题这是离线部署里最高频的一类问题。症状很简单Pod的状态一直是ImagePullBackOffdescribe一下能看到拉取失败的具体报错。排查路径基本是固定的先看镜像路径是不是合理是不是包含了私有仓库地址再确认节点上containerd能不能正常连通私有仓库。离线环境里DNS配置经常被忽略。如果你的Harbor用的主机名访问而节点上的/etc/resolv.conf没有配置对应的DNS服务器解析就会失败。我一般会建议直接用IP地址访问Harbor省掉DNS这层麻烦。还有一种情况是镜像仓库HTTPS证书不受信任。containerd对HTTPS证书的校验比较严格如果Harbor用的是自签名证书需要在所有节点上把CA证书拷贝到/etc/docker/certs.d或配置containerd跳过证书校验。前者更安全后者更省事开发测试环境我会选择后者少折腾[plugins.io.containerd.grpc.v1.cri.registry.configs.registry.offline.local.tls] insecure_skip_verify true5.2 集群初始化失败与节点NotReady排查kubeadm init失败第一步永远是看日志不要反复试。常见失败原因有这么几类镜像拉不下来apiserver容器启动失败etcd卷权限不对端口被占用或防火墙没关。看etcd检查docker ps -a | grep etcd docker logs etcd-container-id如果是端口被占用要么释放端口要么改配置换端口。防火墙的问题开发测试环境我通常直接关闭firewalld省得排查这种网络不通的问题。节点NotReady最常见的就是CNI网络没起来。检查方式kubectl get pods -n kube-flannel kubectl logs -n kube-flannel -l appflannel如果是Flannel一直CrashLoopBackOff大概率是镜像没导入全或者RBAC权限配置有问题。另外一个隐蔽原因是节点的主机名带大写字母或下划线Flannel会直接不理会这种节点建议主机名一律小写、用中划线连接。节点时间不同步的问题前面也提到了。在离线环境里没有公网NTP服务我一般会在一个能访问公网的跳板机上搭NTP服务器然后让所有节点指向它。5.3 离线环境资源不足与调优开发测试环境的机器配置通常不会太高常见的瓶颈是内存。K8s集群本身的组件加上业务服务内存很容易吃紧。有几个优化手段第一Master节点不给业务调度。加一个污点让业务Pod默认不调度到Master节点上kubectl taint nodes k8s-master01 node-role.kubernetes.io/mastertrue:NoSchedule第二etcd的存储放在单独的盘上避免和系统日志抢IO。开发测试环境虽然没有生产那么高的QPS但etcd坏了整个集群的元数据都读不出来这个底线的投入还是值得的。第三kubelet的垃圾回收参数。磁盘空间紧张时K8s在imageGC和containerGC之间会左右摇摆比如反复拉镜像导致磁盘写满。建议显式配置# /var/lib/kubelet/config.yaml imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 705.4 常用问题排查速查表我把实际操作中最高频的几个问题整理成表格方便你们照着排查症状可能原因排查命令处理方法Pod一直ContainerCreating镜像拉取失败或存储卷挂载失败describe podkubectl logs确认镜像路径检查存储类型节点NotReadyCNI未就绪或kubelet异常journalctl -u kubelet -f; 查看CNI Pod状态重新部署CNI检查镜像导入Flannel CrashLoopBackOff镜像版本不对或配置错误kubectl logs -n kube-flannel确认Flannel镜像版本检查配置Kubeadm init卡在拉镜像私有仓库镜像不存在ctr -n k8s.io images list导入缺失镜像核对版本APIServer反复重启etcd连接失败或证书过期docker logs kube-apiserver检查etcd健康重置集群配置跨节点Pod网络不通安全组或iptables规则ping Pod IPtraceroute检查节点防火墙和安全组访问Service超时kube-proxy未正常工作kubectl get pods -n kube-system确认kube-proxy镜像检查IPVS模式这几条是离线部署中最高频的问题自己动手搭过一遍基本就全见过了。下次碰到同样的报错直接照表排查效率翻倍。5.5 几个容易忽略但致命的细节最后分享几个我多次踩坑后留下的操作习惯。第一所有配置文件、镜像清单、版本号必须集中存档不要散落在各个服务器上。我通常是维护一份versions.md把Kubernetes版本、containerd版本、CNI版本、所有镜像的tag、私有仓库地址全部记下来每次部署前对照这份文档检查一遍。第二离线物料包一定要做完整性校验。传文件传坏了在离线环境里特别难发现表现为安装过程中出现莫名其妙的问题比如解压失败、二进制损坏。我会给每个tar包生成SHA256校验文件传到目标机器后先校验一遍再开始操作。第三所有节点的系统时间必须统一。这个前面提过但值得再强调一次尤其是在有Windows和Linux混合环境时系统默认时间格式和时区如果不一致后面排查超时问题会让你怀疑人生。这套离线部署方法我已经在不同的项目里验证过多次从两三个节点的开发测试集群到几十个节点的业务预生产环境都跑得比较稳定。如果你正好要搞离线部署希望这篇能帮你少走点弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门