☰
LiteIO离线部署实战:镜像同步、Harbor、Helm与排坑指南
2026/10/2 9:15:30 网站建设 项目流程

最近帮客户在隔离网环境里落了套LiteIO,整个过程比想象中复杂不少。早先我以为离线安装就是把镜像包装好拷进去,实际踩了一圈才发现,真正的难点根本不在软件本身,而在于“离线”这两个字带来的连锁反应:私有仓库要自建、镜像要提前同步、证书要自己签、helm chart要手动导入,甚至连kubelet拉镜像时的仓库地址都要重新规划。这篇文章就围绕着“离线环境部署liteio”这件事,把我实际操作过程中的设计思路、完整命令、踩坑记录全部写出来。无论你是刚接触IaaS平台的新手,还是被离线部署折磨过几次的运维老手,只要照着这套流程走,基本能顺利把LiteIO跑起来。

先说清楚LiteIO是什么。它是一套面向私有云的IaaS管理平台,基于Kubernetes构建,能够统一管理计算、存储、网络资源,对外提供虚拟机、负载均衡、云盘等云主机能力。它本身不是一个单一二进制程序,而是一整套由几十个容器服务组成的云平台套件,依赖Kubernetes、容器镜像仓库、数据库、消息队列等众多基础组件。正因如此,它的离线部署本质上就是“一整套Kubernetes生态的离线安装”。

1. 离线部署的整体思路与方案设计

1.1 为什么离线部署难度不在软件本身

在联网环境里装LiteIO,一台机器一条命令就能把镜像拉下来,helm一键安装,整个过程十分钟搞定。但一旦进入离线环境,所有“默认行为”都失效了:容器运行时拉不到镜像、helm仓库没法访问、在线charts无法下载、NTP时间同步中断、DNS解析受限。表面上是在装一个IaaS平台,实际上要先在一台能联网的机器上准备好所有离线资产,再把这一整套资产搬运到目标环境里。

离线部署的核心链路其实只有一条:从联网环境获取制品,把制品转移到离线环境,在离线环境完成私有化安装。但这条链路上的每一步都有坑。制品获取阶段要清楚平台到底依赖哪些容器镜像和helm charts,哪些是平台自身的服务,哪些是依赖组件;制品转移阶段要解决镜像文件体积大、数量多的问题,需要考虑用压缩传输还是用移动硬盘搬运;离线安装阶段要配置私有镜像仓库、修改kubelet指向、处理证书信任、保证时间同步。

我把整个方案拆成了几个层面:

  • 镜像层面:把所有依赖镜像同步到一台联网机器,导出后搬进离线环境,推送到私有仓库
  • 包管理层面:准备所有需要的helm chart包文件,本地安装,不走在线仓库
  • 系统层面:准备NTP服务、DNS解析、私有CA证书,保证离线集群的基础服务可用
  • 存储层面:提前规划LiteIO需要的存储类型,是使用已有的NFS还是自建分布式存储

这个方案的关键在于“预先把所有不确定性消灭在联网环境里”,到了离线现场只做搬运和安装,不做发明创造。

1.2 离线部署的核心链路拆解

如果从整体架构角度看,一套离线LiteIO环境会包含以下组件层次:

层次组件说明
基础设施Kubernetes集群LiteIO的运行底座,需要提前完成离线安装
镜像仓库Harbor或Registry离线环境内的私有容器镜像仓库,所有镜像从这里分发
平台服务LiteIO核心模块包含控制面、计算节点agent、API服务等全部容器服务
依赖组件MySQL、Redis、Kafka、MinIO等平台运行依赖的中间件服务,以容器方式部署
网络组件Calico、CoreDNS、Ingress等保证集群网络、服务发现与南北向流量正常
时间与证书NTP服务、私有CA保证集群时间一致和组件间TLS可信

从部署顺序上看,必须先有Kubernetes集群,才能在上面跑Harbor;有了Harbor才能导入LiteIO镜像;有了镜像才能helm install。也就是说,仓库本身也跑在K8s里,这就会产生一个“鸡生蛋”的经典问题:K8s集群安装过程用到的镜像从哪来?答案是使用docker save/load或者ctr images的方式先导入节点本地,让集群先跑起来;集群起来后部署Harbor,再把所有镜像统一推到Harbor里,后续的控制器、监控组件全部从Harbor拉取。

这个设计思路我建议大家一定要画清楚:第一阶段导入本地节点,解决“K8s怎么起来”的问题;第二阶段部署Harbor,解决“后续服务镜像从哪拉”的问题。两阶段分开处理,手上才不慌。

2. 离线环境的前置准备与工具选型

2.1 硬件与系统基线

离线部署LiteIO之前,建议先给目标环境定一个硬件基线,省得装到一半发现资源不够。这里不写死某个配置,因为不同规模场景差异很大,但有几个最低参考值:

  • 控制节点至少4核8G,存储建议不低于100G,用于跑K8s控制面组件和Harbor
  • 计算节点至少8核16G,存储建议不低于200G,用于承载实际虚拟机业务
  • 所有节点要求能通过交换机二层互通,节点名、IP规划要提前固定
  • 操作系统选择主流的CentOS 7.9、RHEL 8.x或openEuler,Linux内核建议4.18以上

操作系统层面有几项检查必须在部署前完成:

先关闭交换分区,swapoff -a并注释掉/etc/fstab里的swap行,否则kubelet会因为swap开启而报错。然后开启模块br_netfilter,设置net.bridge.bridge-nf-call-iptables=1,否则K8s的Service转发会有问题。SELinux要设为permissive或者关闭,防火墙建议在部署阶段先停掉,后期再按需细化策略。这些属于K8s部署老生常谈的问题,但离线环境里每多一个报错,排查成本都比在线环境高不少。

时间同步这件事单独拿出来说。离线环境通常没有外网NTP源,但Kubernetes和LiteIO组件对时间偏差非常敏感,节点间时间差超过几十秒就会出现证书验证失败、日志时间错乱。所以一种做法是选一个控制节点作为NTP服务器,其余节点全部指向它同步。我见过不少人在离线部署时忽略这一步,结果安装LiteIO时频繁报x509证书过期或“clock skew too great”,问题根源都是时间没同步。

2.2 私有镜像仓库选型:Harbor还是轻量Registry

离线环境需要一个私有镜像仓库,选型上我建议分场景看。如果只是验证环境或者集群规模很小,用Docker Registry就够了,部署简单,占用资源低。但LiteIO这种IaaS平台包含的镜像往往有几十个甚至上百个,后续还要持续发布、重新打tag、按项目隔离,这种情况下我强烈建议部署Harbor。

Harbor的优势不只是界面好看,它提供了完整的镜像复制、项目级权限控制、漏洞扫描和审计日志。在离线环境里,权限控制和审计尤其重要,因为平台维护方和业务方可能要分项目管理镜像;如果只有一套裸Registry,所有用户都能随意推拉,后期治理成本很高。Harbor本身也可以离线安装,用官方提供的离线安装包,装上docker-compose后直接执行install.sh就能跑起来。

另一种情况是环境里已经有企业级的容器镜像仓库,比如内部统一建设的Harbor、Nexus或者JFrog Artifactory,那就不必再单独部署一套。直接把离线镜像包推送到企业仓库,再把kubelet的镜像源地址指过去即可。这里需要注意仓库认证:如果私有仓库开了认证,K8s节点需要提前配置/etc/docker/daemon.json里的auths或者使用kubectl create secret docker-registry来保存凭据。

2.3 镜像同步工具选型

镜像同步是整个离线部署里工作量最大的环节,工具选型直接决定效率。我试过几种方案,在这里做个对比。

docker save是大多数人最先想到的方案,把镜像打包成tar文件再拷贝。优点是简单直接,缺点是镜像多的时候效率很低,几十个镜像一个个save,tag、架构容易搞混,而且如果镜像本身没有docker环境还导不出来。

skopeo是我现在主力使用的工具。它可以不经过本地docker daemon直接拷贝镜像,支持从源仓库复制到目标目录、再复制到私有仓库,整个过程不占用本地磁盘镜像缓存。最关键的一项能力是镜像同步时能同时完成多架构复制,比如同时处理amd64和arm64的镜像清单,这对混合架构集群非常有用。

还有一个思路是直接使用Harbor的镜像复制功能。如果源端和离线网络之间存在一条可用的网络通道,可以在Harbor里配置一个复制规则,让Harbor自动从源仓库拉取镜像到本地仓库。但这个方案请求环境具备一定的网络连通性,纯物理隔离环境用不上。

我用skopeo的典型流程是这样的:

# 在联网机器上,同步指定仓库下的全部镜像到本地目录 skopeo sync --src docker --dest dir registry.example.com/liteio /data/liteio-images # 把同步后的目录整个打包 tar czf liteio-images.tar.gz /data/liteio-images # 到离线环境后解压,再同步到私有仓库 skopeo sync --src dir --dest docker /data/liteio-images harbor.internal/liteio

这个流程的好处是中间产物是目录结构,而不是单个tar包,可以随时查看哪个镜像同步了、哪个没有。而且skopeo同步时能保留镜像的digest信息,避免重复拉取相同层。实际使用中,几百个镜像的同步时间取决于网络带宽,但整个流程基本无人值守。

3. 实操过程:从镜像同步到helm部署

3.1 一次成功的镜像外网同步演练

我把一次实际项目里的同步操作完整记录下来。假设LiteIO平台由以下几个镜像仓库组成:

  • registry.liteio.io/liteio/{api-server, controller-manager, scheduler, node-agent}等等
  • 一系列依赖中间件镜像,来自docker.io和quay.io
  • K8s基础组件镜像,比如pause、etcd、coredns、calico、metrics-server

在联网环境里,我先用skopeo把所有镜像同步到本地目录:

mkdir -p /data/liteio-images skopeo sync --src docker --dest dir registry.liteio.io/liteio /data/liteio-images/liteio skopeo sync --src docker --dest dir docker.io/library /data/liteio-images/docker-library

同步完成之后,查看一下目录结构,确认镜像仓库名和tag没有丢失:

find /data/liteio-images -maxdepth 3 -type d

然后打包。这里我有个小经验,tar压缩时不要追求极限压缩率,用gzip的默认级别就好,压缩时间短,传输效率不差太多。如果镜像文件总量特别大,比如超过50G,建议分卷打包,方便用移动硬盘或者U盘分批拷贝:

tar czf liteio-images-part1.tar.gz -C /data/liteio-images liteio tar czf liteio-images-part2.tar.gz -C /data/liteio-images docker-library

这个环节里一个容易忽略的点是Harbor或源仓库的registry认证。skopeo同步时如果源仓库需要登录,得提前执行:

skopeo login registry.example.com --username admin --password yourpass

避免等到同步中途报unauthorized错误。另外,如果目标是docker.io官方仓库,注意限流问题,批量拉取时容易触发Docker Hub的rate limit。解决办法是把官方镜像提前转存到自己的私有仓库里,再从私有仓库同步到离线环境,不要直接从Docker Hub拉。

3.2 私有仓库初始化与镜像推送

离线环境里把Harbor跑起来之后,下一步就是创建项目并推送镜像。这里规划一个项目结构,我习惯按模块分:

  • liteio:LiteIO平台自己的镜像
  • system:K8s基础设施组件,比如calico、coredns、metrics-server
  • middleware:MySQL、Redis、Kafka、MinIO等中间件
  • library:公共依赖,比如pause、busybox等基础工具镜像

这样做的好处是权限好控制,不同团队按项目授权;生产环境中如果有多套LiteIO环境,还可以按环境再拆分项目目录。

推送镜像之前,先确认/etc/docker/daemon.json已经设置了insecure-registries。Harbor如果使用的是自签证书,没加入系统信任库的话,docker pull会直接报certificate signed by unknown authority。两个办法:一是把Harbor的CA证书放到/etc/docker/certs.d/harbor.internal/目录下;二是直接在daemon.json里把Harbor地址列入insecure-registries。从生产环境的安全角度考虑,我推荐第一种做法,但某些内部环境图省事也会直接走insecure。

推送大量镜像时,如果逐个docker push太费劲,可以用脚本批量处理。核心逻辑就是从解压目录里读取每个镜像的信息,重新打上Harbor地址的tag再push:

#!/bin/bash HARBOR=harbor.internal/liteio find /data/liteio-images -name "version" | while read f; do dir=$(dirname "$f") repo=$(basename "$dir") tag=$(cat "$f") src="${dir#/data/liteio-images/}" docker pull "${src}:${tag}" docker tag "${src}:${tag}" "${HARBOR}/${repo}:${tag}" docker push "${HARBOR}/${repo}:${tag}" done

不过这个方式依赖本机docker daemon,镜像一多,本机磁盘容易被打满。实际上用skopeo直接dir转docker推送更省空间,不用把镜像落地到本地docker里:

skopeo sync --src dir --dest docker /data/liteio-images harbor.internal

这命令会自动把目录结构的镜像全部推送到Harbor,推送完以后在Harbor界面上能看到目录结构和tag都完整保留。我建议把上面这个脚本和skopeo两种方式都掌握,根据现场环境选择。如果目标机器上docker daemon可用、网络吞吐稳定,用脚本批量处理也没问题;如果镜像数量多且磁盘紧张,skopeo明显更合适。

3.3 Helm部署LiteIO及依赖组件

镜像都进了Harbor,接下来是部署环节。离线环境下没法直接helm repo add在线仓库,所以得提前在联网环境把repo的index和chart包拉下来。

这里重点讲一下helm离线安装的完整流程。先做一次chart下载:

helm repo add liteio https://charts.liteio.io helm repo update helm pull liteio/liteio --version 1.0.0 helm pull bitnami/mysql --version 9.0.0

在联网机器上下载好所有需要的chart包以后,打包传到离线环境。在离线环境里建一个charts目录:

mkdir -p /data/charts/ # 把xxx.tgz全部放到这个目录里 cd /data/charts/ helm repo index .

执行完之后,本地就生成了一个index.yaml,这个目录就能被本地helm当作仓库使用。然后:

helm repo add local http://127.0.0.1/charts/

如果本地没有HTTP服务,也可以直接helm install指定tgz包,不经过repo:

helm install liteio ./liteio-1.0.0.tgz --namespace liteio-system --create-namespace \ --set global.imageRegistry=harbor.internal \ --set global.imageNamespace=liteio

这里最重要的参数是global.imageRegistry,因为默认chart里的镜像地址都指向外网或官方仓库,离线环境必须统一替换成本地Harbor地址。如果LiteIO的chart支持global.imageNamespace,也好一起设置,否则镜像tag里可能还带着原来的命名空间前缀。

依赖组件我一般也是一并helm安装,统一用一套values文件管理。比如部署MySQL和Kafka:

helm install mysql ./mysql-9.0.0.tgz --namespace liteio-system --set auth.rootPassword=xxx helm install kafka ./kafka-1.0.0.tgz --namespace liteio-system --set replicas=3

注意LiteIO安装时如果要求指定中间件连接信息,这些values要提前准备好。我在部署时习惯把中间件和应用本身拆成两批安装,先中间件后LiteIO主程序,中间件Service稳定可用后再装平台,这样隔离问题域,排错时不会互相干扰。

3.4 部署后的关键验证

部署完成不代表LiteIO就能用了,验证环节少一环都可能埋雷。我的验证清单大概是这样的:

# 1. 检查所有Pod是否Running kubectl get pods -A | grep -v Running | grep -v Completed # 2. 检查LiteIO服务API是否就绪 kubectl -n liteio-system get svc liteio-api curl -k https://<api-svc-cluster-ip>:<port>/healthz # 3. 验证计算节点agent已上线 kubectl -n liteio-system logs deploy/liteio-controller-manager | grep -i "node" # 4. 检查存储后端是否正常 kubectl -n liteio-system get storageclass

Pod状态正常只是第一层,真正要紧的是平台功能是否可用。我会在LiteIO控制台上创建一个测试虚拟机,确认计算节点能调起qemu进程,虚拟机能拿到IP、能通过VNC连接、拷贝文件进去正常。这一步验证的是KVM嵌套虚拟化和网络CNI的连通性,很多环境里Pod都正常但虚拟机起不来,问题可能出在/dev/kvm不存在或者网络插件没有开启VXLAN模式。

还有一项容易被忽略的验证是平台自带监控组件,比如Prometheus和Grafana,要确认监控数据采集正常。离线环境里时钟源本身不可靠,监控时间轴如果混乱,告警规则全都会失真。先让NTP同步跑一会儿,再检查Grafana的时区配置是否跟随宿主机,避免界面看到一个偏移好几个小时的时间线。

4. 离线部署常见的坑与排查技巧

4.1 镜像拉取失败的几种典型场景

离线部署里遇到最多的就是镜像拉取失败,表象都是ErrImagePull或ImagePullBackOff,但根因各不相同。我把自己踩过的、帮别人排查过的场景整理成一张速查表:

错误信息特征根本原因处理手段
manifest unknown镜像tag不存在或push时丢了tag检查Harbor里的tag列表;重新执行skopeo同步
x509 certificate signed by unknown authority仓库证书未加入节点信任拷贝CA证书到节点的cert.d目录
no basic auth credentialskubelet没有仓库凭据创建docker-registry secret并在ServiceAccount或Pod中引用
502/504 from harborHarbor自身存储异常或内存不足查看Harbor容器日志;适当调大harbor-db或registry的资源限制
connection refused节点网络到仓库端口不通检查防火墙和路由策略

最隐蔽的一种是“tag存在但架构不对”。比如在x86的节点上去拉一个仅arm64的镜像,报错信息同样很模糊。离线环境里如果同时有x86和ARM节点混部,一定要在同步时把多架构manifest完整保留,别因为用了旧的docker save导致单架构镜像覆盖了多架构索引。skopeo在这方面更可靠。

4.2 certificate与时间同步问题

证书问题在离线环境里几乎是不定时炸弹。在线环境里大部分证书链完整,离线环境如果自建私有CA,任何一个组件配置不当都会冒出来。最典型的是K8s节点与Harbor仓库之间的TLS信任。如果Harbor使用自签证书,除docker外,containerd也要信任同一个CA。不同容器运行时配置不一样,docker在/etc/docker/certs.d/下,containerd通常在/etc/containerd/certs.d/下,而且还要在config.toml里显式开启。

时间同步问题容易被忽略,但严重时会让全部TLS校验失败,因为证书生效期限判断依赖客户端和服务端的当前时间。离线环境里如果没有内部NTP,所有节点时间都会慢慢漂移。部署K8s之前先把chrony配置好,选一个节点做server,其他节点做client:

# 服务端节点 /etc/chrony.conf local stratum 8 allow 192.168.0.0/16 # 客户端节点 /etc/chrony.conf server 192.168.1.10 iburst

配置完执行systemctl restart chronyd,然后chronyc sources确认同步状态。别小看这一步,很多“莫名报错”排查到最后一查时间差了几分钟。

4.3 K8s组件访问私有仓库的配置陷阱

这里单独说一个容易让人崩溃的细节:K8s节点上kubelet不会像docker CLI那样天然读取~/.docker/config.json的登录信息。如果你的仓库需要认证,得有Secret配合工作。

具体做法是:

kubectl create secret docker-registry harbor-secret \ --docker-server=harbor.internal \ --docker-username=admin \ --docker-password=yourpass \ -n liteio-system

然后在你需要拉镜像的Deployment里显式声明:

spec: template: spec: imagePullSecrets: - name: harbor-secret

很多人在部署LiteIO时发现Pod老是ImagePullBackOff,查看Harbor日志却没有任何拉取记录,就是在这里卡住了。另外有一个全局兜底的方式,在namespace级别通过serviceaccount关联secret,这样该Namespace下所有Pod都默认拥有拉取私仓镜像的权限:

kubectl patch serviceaccount default -n liteio-system \ -p '{"imagePullSecrets": [{"name": "harbor-secret"}]}'

还有一种情况是hostAliases问题。离线环境的DNS未必能解析Harbor域名,节点上一看harbor.internal解析到公网地址或解析失败。如果不改DNS,可以在所有节点的/etc/hosts里加上仓库条目:

192.168.1.10 harbor.internal

这个操作我建议放在开局阶段就执行,并写进部署脚本里,不然K8s集群重建后漏配hosts,又会出现一次类故障。

我实测下来,离线部署LiteIO最核心的排错思路就是“分环排查”:先确认仓库能连,再确认Pod能拉,最后确认平台组件之间能互通。按这个顺序来,大部分压轴问题都能在一个小时内定位。

最后分享一个个人的习惯:我在做离线部署前,一定会在联网环境里先跑一遍完整安装流程,把所有需要下载的chart包、镜像、甚至yaml里隐含的镜像地址全部记录下来,生成一份物料清单。物料清单上不仅要有镜像名和tag,还要有所属仓库和版本来源。到了离线现场,这份清单就是唯一的依赖说明书,软件装完复盘时也能清楚到底缺什么、多什么。这个习惯帮我少走了太多弯路,建议你也试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询