news 2026/10/23 5:41:09

Kubernetes Pod启动从90秒优化到8秒:本地Harbor私有镜像仓库加速实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes Pod启动从90秒优化到8秒:本地Harbor私有镜像仓库加速实战

1. 问题现场还原:90秒的Pod启动到底卡在哪

先交代一下背景。我手头维护着一套中等规模的容器集群,大概三十来个节点,跑的是标准的上游Kubernetes发行版,容器运行时用的是containerd。日常发布频率不算高,一天十来次滚动更新,但每次发布都让人抓狂——一个普通的业务Pod,从kubectl apply到真正Ready,稳定在90秒上下。注意,是稳定,不是偶发。这意味着它不是网络抖动,而是一个系统性的瓶颈。

一开始我怀疑是调度慢、探针配置不合理、镜像太大。挨个排查:调度日志显示Pod秒级就绑定了节点;readinessProbe的initialDelaySeconds只有5秒;镜像本身压缩后也就两百多兆,不算离谱。那问题只能出在镜像拉取环节。于是我在节点上手动crictl pull了一下同一个镜像,掐表一算,光拉取就花了七十多秒。真相大白——Pod启动的90秒里,有八成时间耗在了从公网镜像仓库下载镜像上。

这个场景其实非常普遍。很多团队在集群规模小的时候,直接用公网镜像仓库,觉得"能拉下来就行"。可一旦节点数量上去、发布频率变高,公网拉取的问题就会集中爆发:带宽被多个节点同时抢占、跨境链路抖动、仓库限流、DNS解析慢,任何一个环节都能把几十秒的时间叠加到你的部署流程里。这篇文章我就把从"90秒"优化到"8秒"的完整过程拆开讲清楚,包括为什么选本地Harbor、containerd怎么改配置、证书怎么处理、以及我踩过的那些坑。适合正在被镜像拉取速度折磨的运维和平台工程师参考,也适合刚接触containerd配置的同学照着抄作业。

需要先说明一点:本文讨论的是在自有网络环境内搭建私有镜像仓库来加速拉取,所有操作都在合规的内网环境中完成,涉及的都是标准的企业级容器基础设施实践。

2. 方案选型:为什么是本地Harbor而不是别的

2.1 先搞清楚瓶颈的本质

在动手之前,得先想明白一件事:镜像拉取慢,慢的到底是哪一段。一次完整的镜像拉取,大致分这么几步:解析仓库域名、建立连接、鉴权、拉取manifest、按层下载blob、解压落盘。公网仓库慢,通常慢在"建立连接"和"按层下载"这两步,因为数据要跨越很长的物理链路,还要经过多个网络节点转发,每一跳都可能引入延迟和丢包。

这就引出一个关键判断:如果瓶颈在链路,那么优化方向就应该是"把镜像放到离节点尽可能近的地方"。离节点最近的地方是什么?就是集群自己所在的局域网。所以思路很清晰——在集群内部或者同机房部署一个私有镜像仓库,让节点从局域网拉镜像,而不是从公网拉。

2.2 为什么选Harbor

私有仓库的方案其实不少,最朴素的是用registry官方镜像起一个,但那个太简陋,没有Web界面、没有权限管理、没有镜像清理策略、没有复制功能。对于一个要长期维护的生产集群来说,这些能力迟早都要补上,与其后面再折腾,不如一步到位。

Harbor是我最终的选择,理由有这么几条。第一,它是CNCF的毕业项目,成熟度和社区活跃度都够,出问题好查资料。第二,它自带基于角色的访问控制、镜像漏洞扫描、垃圾回收、远程复制,这些在生产里都用得上。第三,它支持HTTPS和多种认证方式,和containerd对接很顺。第四,部署方式灵活,既可以用docker-compose快速起一个,也可以用Helm部署到K8s里。

提示:如果你的集群规模很小,比如就三五个节点,且只是想要个缓存,其实registry加一个反向缓存代理也能凑合。但只要你有权限隔离、审计、清理这些需求,Harbor的性价比明显更高。

2.3 一个容易被忽略的选型点:存储后端

Harbor自己不管存储,它把镜像层存到后端。默认用本地文件系统,也可以接对象存储。我这里选的是本地SSD盘,因为镜像仓库的读多写少,SSD能显著提升blob的读取速度。如果你节点多、并发拉取量大,本地盘可能成为瓶颈,那就得上对象存储或者做仓库的水平扩展。这一点在规划阶段就要想清楚,别等上线了才发现磁盘IO打满。

另外,Harbor的部署位置也有讲究。最理想的是部署在和K8s节点同一个局域网、同一个交换机下的机器上,这样拉取走的是内网高速链路。如果条件允许,给仓库机器配上千兆甚至万兆网卡,效果立竿见影。

3. Harbor部署实操:从零到能用的完整步骤

3.1 环境准备与依赖检查

我用的是一台独立的Linux机器做仓库服务器,配置是4核8G、500G SSD。系统是最常见的那个企业级Linux发行版。部署前先确认几件事:机器能通外网(用于拉取Harbor的安装包和基础镜像)、防火墙放行了需要的端口、时间同步正常。

Harbor默认需要这些端口:443(HTTPS)、80(HTTP,可选)、以及内部组件之间通信的一堆端口。生产环境我建议只开443,把HTTP关掉。时间同步很重要,因为后面证书和token都和时钟有关,时间偏差大了会导致鉴权失败。

# 检查时间同步状态 timedatectl status # 确认磁盘空间 df -h /data # 确认内存 free -h

3.2 下载与配置Harbor

从官方发布页下载离线安装包,解压后会得到一个harbor目录。核心配置文件是harbor.yml,这个文件决定了Harbor怎么跑。我重点改了几个地方。

第一是hostname,填仓库服务器的域名或者IP。这里有个坑:如果你后面要用HTTPS并且用域名访问,hostname必须和证书里的域名一致,否则containerd校验会失败。第二是harbor_admin_password,改掉默认密码。第三是data_volume,指向大容量磁盘。第四是配置HTTPS证书。

# harbor.yml 关键片段 hostname: registry.internal.example https: port: 443 certificate: /data/certs/registry.crt private_key: /data/certs/registry.key harbor_admin_password: 换成你自己的强密码 data_volume: /data/harbor

证书这块,如果你们内网有自己的证书签发体系,直接申请一张就行。如果没有,可以自签一张,但自签证书需要把CA分发给所有节点,否则containerd不认。自签的命令大致是这样:

# 生成私钥 openssl genrsa -out registry.key 4096 # 生成证书签名请求 openssl req -new -key registry.key -out registry.csr \ -subj "/CN=registry.internal.example" # 自签证书,有效期十年 openssl x509 -req -days 3650 -in registry.csr \ -signkey registry.key -out registry.crt

注意:自签证书的CN必须和hostname完全一致,包括大小写。我见过有人CN写的是IP,访问用的是域名,结果containerd报x509错误,排查半天。

3.3 启动与验证

配置改完,执行安装脚本,它会拉起一堆容器。等几分钟,用浏览器或者curl访问https://registry.internal.example,能看到登录页就说明起来了。然后用admin账号登录,建一个项目,比如叫prod,把权限设好。

# 执行安装 ./install.sh # 验证服务状态 docker compose ps # 用curl测试 curl -k https://registry.internal.example/api/v2.0/health

验证通过后,先手动推一个测试镜像上去,确认推拉都正常,再进入下一步改containerd。

4. containerd对接Harbor:配置细节与踩坑记录

4.1 containerd的镜像仓库配置机制

containerd和docker不一样,docker的配置在daemon.json里,而containerd的仓库配置在/etc/containerd/config.toml里,而且是通过registry这个插件来管理的。从containerd 1.4开始,配置结构有过一次比较大的调整,网上很多老教程已经对不上了,这点要特别注意。

核心逻辑是这样的:containerd会根据镜像地址的host部分去匹配配置。比如你的镜像地址是registry.internal.example/prod/myapp:1.0,containerd就会去找host为registry.internal.example的配置。如果没找到,就用默认配置去公网拉。所以我们要做的,就是给这个host单独配一段,告诉它走HTTPS、用哪个CA、要不要跳过校验。

4.2 修改config.toml

先备份原配置,然后编辑。找到[plugins."io.containerd.grpc.v1.cri".registry]这一段,加上mirrors和configs。

[plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.internal.example"] endpoint = ["https://registry.internal.example"] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.internal.example".tls] ca_file = "/etc/containerd/certs.d/registry.internal.example/ca.crt"

这里有几个关键点。第一,endpoint必须带https://前缀,不带的话containerd会当成HTTP处理,直接失败。第二,ca_file指向你那张CA证书的路径,注意是CA证书,不是服务器证书本身。第三,如果你用的是自签证书且不想分发CA,可以设insecure_skip_verify = true,但生产环境强烈不建议,这等于放弃了中间人攻击的防护。

4.3 分发CA证书并重启

把CA证书拷到每个节点的对应目录,然后重启containerd。

# 在每个节点上创建目录 mkdir -p /etc/containerd/certs.d/registry.internal.example # 拷贝CA证书(用你的分发工具,比如ansible) scp ca.crt node:/etc/containerd/certs.d/registry.internal.example/ # 重启containerd systemctl restart containerd

重启后,用crictl pull测试一下。如果能看到拉取进度条飞快地走完,基本就成了。

实操心得:改完配置后,别急着在业务集群上试,先在一台测试节点上验证。containerd重启会导致该节点上所有容器短暂中断,生产节点上操作要挑低峰期,并且做好节点排水。

4.4 镜像迁移:把公网镜像搬到Harbor

配置改好了,但Harbor里还是空的,得把业务要用的镜像搬过去。最直接的办法是在一台能同时访问公网和Harbor的机器上,先pull再tag再push。

# 从公网拉取 docker pull somepublicrepo/myapp:1.0 # 重新打标签指向Harbor docker tag somepublicrepo/myapp:1.0 registry.internal.example/prod/myapp:1.0 # 推送到Harbor docker push registry.internal.example/prod/myapp:1.0

如果镜像多,写个脚本批量处理。更优雅的方式是用Harbor的远程复制功能,配置一个指向公网仓库的复制规则,让它自动同步。但复制功能对某些仓库的兼容性一般,我最后还是用了脚本批量搬,可控性更强。

搬完之后,把业务YAML里的image字段全部改成Harbor的地址。这一步建议用CI流水线统一替换,别手动改,容易漏。

5. 效果验证与性能对比:8秒是怎么来的

5.1 实测数据对比

改完之后,我在同一批节点上做了对比测试。测试方法是清空节点本地镜像缓存,然后crictl pull同一个镜像,记录耗时。为了排除偶然性,每个场景测五次取平均。

场景平均拉取耗时Pod启动总耗时
公网仓库直连72秒90秒
本地Harbor(千兆内网)3秒8秒
本地Harbor(万兆内网)1.5秒6秒

可以看到,拉取耗时从72秒降到3秒,是二十多倍的提升。Pod启动总耗时从90秒降到8秒,这里面剩下的8秒主要是调度、网络插件配置、容器创建和探针等待的时间,这些是正常开销,没法再压了。

5.2 为什么提升这么夸张

有人可能会问,内网再快,能快二十多倍吗?答案是能,而且合理。公网拉取慢,不只是带宽问题,更多是延迟和丢包。镜像分层下载时,每一层都要单独发请求,公网环境下每个请求的往返延迟可能几十毫秒,层数一多,光握手就耗掉大量时间。再加上公网仓库常有并发限流,多个节点同时拉会被限速。内网环境下,往返延迟降到亚毫秒级,带宽也充足,自然快得多。

5.3 顺带解决的其他问题

除了速度,改本地Harbor还顺手解决了几个隐性痛点。一是发布不再受公网仓库可用性影响,以前公网仓库一抖动,我们的发布就得停。二是镜像版本可控,不会出现"同一个tag被覆盖"导致的环境不一致。三是可以做漏洞扫描,上线前先扫一遍,安全上多一道防线。

6. 常见问题与排查技巧实录

6.1 拉取报x509证书错误

这是最高频的问题。现象是crictl pull报x509: certificate signed by unknown authority。原因通常是CA证书没分发到位,或者CA证书路径配错了,或者证书的CN和访问的hostname不一致。排查顺序:先确认节点上CA文件存在且内容正确,再确认config.toml里的路径没写错,最后用openssl s_client -connect registry.internal.example:443看证书链是否完整。

6.2 报401未授权

现象是unauthorized: authentication required。这说明Harbor要求鉴权,但containerd没提供凭据。解决办法是在config.toml里配置auth,或者在Harbor里把对应项目设为公开。生产环境建议配auth,把用户名密码写进配置,注意文件权限要收紧。

[plugins."io.containerd.grpc.v1.cri".registry.configs."registry.internal.example".auth] username = "youruser" password = "yourpassword"

6.3 改了配置不生效

containerd的配置改完必须重启才生效,而且有时候systemctl restart不够,得先systemctl daemon-reload。另外,如果你用的是kubelet管理的containerd,还要确认kubelet没有覆盖配置。最稳妥的验证方式是ctr命令直接测试,绕开K8s。

6.4 常见问题速查表

现象可能原因解决方向
x509错误CA未分发/CN不匹配检查CA路径与证书CN
401未授权缺少凭据配置auth或项目设为公开
连接超时防火墙/端口未放行检查443端口连通性
配置不生效未重启/被覆盖重启containerd并确认配置
拉取仍然慢走了公网确认镜像地址已改为Harbor

避坑技巧:每次改完containerd配置,先用ctr -n k8s.io images pull测一下,这个命令直接调containerd,不经过K8s,能最快定位是配置问题还是K8s问题。

7. 长期维护与扩展建议

7.1 镜像清理不能忘

Harbor用久了,镜像会越堆越多,磁盘迟早爆。一定要配垃圾回收策略,定期清理没有tag的悬空镜像,以及设置保留规则,比如每个仓库只保留最近十个版本。Harbor的GC要手动触发或者配定时任务,别指望它自动清。

7.2 高可用与备份

单点Harbor挂了,整个集群就没法拉新镜像了。规模大了一定要做高可用,Harbor支持多实例共享后端存储。另外,Harbor的数据库和配置要定期备份,镜像层如果用的是对象存储,本身就有冗余,但数据库丢了就麻烦了。

7.3 和CI/CD打通

最理想的状态是,CI构建完镜像直接推到Harbor,CD部署时从Harbor拉。这样镜像的流转全程在内网,又快又安全。我们后来把流水线改成了这个模式,发布体验又上了一个台阶。

我个人在实际操作中的体会是,镜像拉取这个环节,很多人一开始都不当回事,觉得"能拉就行"。但它是整个部署链路里最容易被忽视、又最容易优化的地方。把仓库搬到本地,投入不大,收益却非常直接。如果你现在还在被Pod启动慢困扰,不妨先掐表测一下拉取耗时,很可能问题就出在这儿。最后再分享一个小技巧:给Harbor配一个内网DNS短域名,比如reg.internal,比记一长串域名省事,配置里也清爽。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 5:40:36

ESP32-S3量产交付:Windows一键烧录包完整方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:40:09

DDU显卡驱动彻底清理指南:原理、实操与避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:40:03

汽车ECU BootLoader升级实战:PBL/SBL架构与UDS刷写全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:39:23

Ubuntu固定IP配置全攻略:Netplan原理、实操与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:39:05

Redis分布式锁全解析:原理、坑点与最佳实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 5:37:11

门面模式实战:从下单功能看如何收敛调用链与降低耦合

门面这个词,最容易让人联想到楼盘入户大堂那种"所有访客都从这一个口进出"的秩序感。而在代码世界里,门面模式(Facade Pattern)的任务也差不多——当你的业务子系统越来越多,调用方的代码开始散发一种刺鼻气…

作者头像 李华