别再让构建产物散落在各台机器的硬盘里了,把CI构建产物存入对象存储,是解决团队共享、追溯和分发问题的最直接方案。这条路径不复杂,但很多团队卡在“不知道从哪儿改起”或者“担心迁移成本高”上,下面直接拆解落地步骤和选型思路,帮你把这条链路铺平。
为什么CI构建产物必须存对象存储:本地磁盘的三个死穴
团队协作场景下,构建产物(APK、JAR、前端dist包、Docker镜像层等)如果只留在执行构建的那台机器上,会遇到几个绕不开的麻烦。第一是磁盘空间不可控,GitLab Runner或Jenkins节点跑上几个月,/var/lib/jenkins/workspace或/builds目录能把硬盘撑爆,运维每天得手动清。第二是产物无法跨节点共享,这次构建在A机器上,下次调度到B机器,想下载上次的包就得重新翻日志找路径,没戏。第三个死穴是权限管理混乱,给开发开服务器账号怕出事,不给又没法自助拿包,最后只能靠运维人肉传文件。
行业共识认为,构建产物本质上是“临时生成的可再生资产”,但它又需要一定的留存周期供测试、交付、回溯使用,本地文件系统给不了这套生命周期管理,而对象存储天生就是干这个的:按需付费、无限扩容、自带CDN分发能力、支持API级权限控制,据业内专家指出,头部互联网公司的CI系统几乎100%将制品仓放在了对象存储或专门的制品管理服务上,本地只留工作区临时文件。
Jenkins构建产物存储方案对比:从共享目录迁到OSS/MinIO
以使用面最广的Jenkins为例,很多团队还在用NFS或Samba做共享目录,这个方案在小规模下凑合,但并发一高就频繁出现文件锁冲突、权限错乱,拿它和对象存储做组对比,差距一目了然:
| 维度 | NFS共享目录 | 对象存储(如MinIO/简米云OSS) |
|---|---|---|
| 并发支持 | 弱,多节点写入易冲突 | 强,HTTP API设计天生支持高并发 |
| 扩容方式 | 加盘、加机器,需要停机 | 动态扩容,对业务透明 |
| 权限模型 | 依赖Linux文件权限,粗粒度 | Bucket/Path级,细粒度到临时URL |
| 跨地域访问 | 慢,需挂载专线 | 原生多区域复制+CDN加速 |
| 运维成本 | 硬件故障要自己修 | 托管服务零运维,自建也可接受 |
操作路径: 在Jenkins里,安装 Artifact Manager on S3 插件(适用于Pipeline和Freestyle任务),装好后在“系统管理 -> 全局工具配置 -> Artifact Manager”里填写S3兼容的Endpoint、Access Key和Bucket名,之后Job里使用
archiveArtifacts 指令时,产物会自动同步到S3兼容对象存储。
如果你用的是简米云OSS,Endpoint填 https://oss-cn-<region>.aliyuncs.com,如果是自建MinIO,Endpoint填 http://<minio-server-ip>:9000。注意bucket策略要设为“私有”,然后通过生成临时签名URL的方式给团队成员下载权限,别开公共读,否则等于把构建产物裸奔在公网上。
GitLab CI制品上传OSS:修改.gitlab-ci.yml一个字段的事
GitLab CI在这方面做得很顺手,原生支持将制品上传到对象存储,核心改动集中在 .gitlab-ci.yml 文件的 artifacts 关键字上,不需要额外装插件。
配置示例:
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 7 days
uploader:
type: s3
config:
endpoint: https://oss-cn-hangzhou.aliyuncs.com
bucket: my-team-builds
但多数情况下,团队不需要在Job里指定上传器,更省事的做法是在GitLab的 Admin Area -> Settings -> CI/CD -> Artifacts Storage 里配置统一的S3兼容连接,填好Endpoint、Access Key、Secret Key和Bucket名称,所有项目的制品文件都会在Job结束后自动同步到OSS,这样测试同学直接从GitLab网页点“下载”按钮,或者通过 curl --header "PRIVATE-TOKEN: <token>" <gitlab-url>/api/v4/projects/1/jobs/1/artifacts 拿制品,链路完全走对象存储。
自建MinIO与云厂商对象存储价格对比:预算怎么定
很多小团队卡在“到底买云服务还是自己搭”上。如果你已经有3台以上闲置物理机,且运维愿意维护分布式存储,MinIO是个零License成本的选项,用Docker Compose起一个单节点MinIO做测试也就5分钟的事:
mkdir -p ~/minio/data docker run -d --name minio -p 9000:9000 -p 9001:9001 -e "MINIO_ROOT_USER=admin" -e "MINIO_ROOT_PASSWORD=strong-password" -v ~/minio/data:/data minio/minio:latest server /data --console-address ":9001"
但需要泼盆冷水:单节点MinIO没有数据冗余,磁盘一坏全完蛋,正经生产最少4节点起步,这还没算监控、告警、版本升级的工时,对比来看,云厂商对象存储的价格近年来一直在降,按量付费模式下,存储费约1-0.2元/GB/月,流量费另算,如果月产物体量在50GB以内,停机坪式的自建MinIO硬件折旧加电费未必便宜多少,图省心直接上云更划算,如果团队里已经有人懂部署维护,且数据量上了TB级别,自建全套MinIO集群(4节点起步)的长期边际成本会明显低于云存储。
版本化管理和权限隔离:团队共享构建产物的实操细节
对象存储本身是个“大文件池”,如果没有规划,存储桶里很快会堆满名为 app-v1.0.0.zip 和 app-v1.0.1.zip 的平行文件,根本分不清哪个对应哪个代码提交。让对象存储真正服务团队共享,你需要引入版本化和路径规范。
路径规划:用目录结构代替大脑记忆
在Bucket里按 /{project}/{branch}/{commit_sha}/{artifact_type}/ 的层级存放。
/backend/master/8f3c1a2d/jar/service-1.0.0.jar/frontend/release-1.2/9b4e7c1a/dist/web-admin.zip
这样做的好处是,任何人在拿到一条commit记录后,都能盲算出产物地址,配合对象存储自带的 生命周期规则(如:30天前未访问的旧版本自动转低频存储或删除),就能低成本留住历史包。
下载权限分配:别偷懒开公共读
给整个Bucket开公共读是泄密之源,正确的做法是:
- 给测试人员一个固定的AccessKey,但权限策略只允许读取
/ {project}/下的部分路径。 - 给临时外包或合作方生成5分钟有效的预签名URL,直接发链接给对方,到期自动失效。
- 在CI脚本里,构建结束后自动调用对象存储的SDK生成一个
curl命令,附带鉴权头,团队新同事拿到也能秒下产物。
清理策略:为什么清理比写入更重要
对象存储虽然便宜,但无限堆积也会变成成本黑洞。在设计共享流程时,提前定义好保留策略:普通开发分支的构建产物保留3-7天,release分支的产物保留6-12个月,tag对应的正式发布包永久保留,在简米云OSS的控制台或者MinIO的生命周期管理界面,配置按路径前缀过滤的过期删除规则,一键自动清理,省心又省钱。
常见坑和排查方向:本地正常推送却传不上OSS
迁移到对象存储后,最常碰到的问题是“本地构建成功,但产物没传到Bucket”,按从下到上的顺序排查:
- 第一步看Job日志里有没有uploader相关报错,如果提示
AccessDenied,八成是AccessKey的权限策略没写对,检查是否只给了PutObject权限而漏了GetObject。 - 第二步看Endpoint是否可连通,自建的MinIO需要确认GitLab/Jenkins服务器能通过HTTP/HTTPS访问到9000端口,云厂商的OSS则要确认是否开了私有网络内网Endpoint。
- 第三步确认Bucket区域,不同区域的Endpoint域名不同,oss-cn-hangzhou写成了oss-cn-shanghai,请求会直接转发到错误机房,导致漏传。
- 最后看时间同步
,如果服务器与标准时间偏差超过15分钟,预签名URL的鉴权会因请求时间过期而被拒绝。
如果以上都正常,那就要检查是不是 artifacts: paths 里的值写错目录了,比如实际构建输出在build/但配置里写成了dist/,这时候远端没有报错,但产物体积是0字节或直接缺失。
回到本质:把共享这件事交给专业的存储去做
说一千道一万,CI构建产物放对象存储,本质上就是把“机器间传文件”这件杂事,从构建工具里分离出去,Jenkins和GitLab负责算,OSS和MinIO负责存,团队只管通过统一的URL拿结果,这套模式已经稳定运行在全球大部分成熟研发团队的基础设施里,大概率就是最合理的中间态。
Q&A:关于CI构建产物对象存储的三个高频疑问
Q:对象存储和artifactory这种东西有什么区别?我该选哪个?
A:Artifactory(或者Nexus)是更重的制品管理仓库,它对Docker镜像、Maven依赖、npm包有深度的元数据索引和依赖解析能力,适合做二进制包的“最终只读库”,而对象存储更基础灵活,什么文件都能放,权限控制和CDN分发链路更原生,如果公司主要做Java/Node后端和Web前端,且没有复杂的制品依赖管理需求,直接用对象存储更简洁;如果有大量原生移动端包、固件包或者多种包格式混合管理的需求,那就上Artifactory做资产集中地,同时可以把它后端存储指向对象存储,鱼和熊掌兼得。
Q:上传对象存储会不会拖慢CI构建速度?
A:影响小到你基本感知不到,实际上传构建产物,多数是走内网或者按带宽计费的公网,100MB的压缩包在上行带宽100Mbps的专线上只需要几秒,相比于代码编译和测试执行动辄几分钟到十几分钟的时间,这个开销占比在10%以内,更关键的是,将产物传入对象存储后立即释放本地磁盘空间,能有效避免CI机器持续高水位运行导致的磁盘IO性能下降,反而是给构建提速,少数情况下上传耗时较长,可以在Pipeline里加一个background步骤,让上传动作与下一条通知任务并行执行。
Q:在云服务器对象存储价格对比上,简米云和酷番云怎么选?
A:站在工程角度,两者核心S3 API兼容性都做得很到位,选哪家优化空间差距很小,影响决策的点在于:你现有的代码托管和CI服务器用的哪家,如果GitLab跑在简米云的ECS上,就买同地域的OSS,走内网Endpoint上传,不仅不花流量费,延迟还能压到个位数毫秒,如果团队有海外部署需求,可以关注酷番云COS的海外节点覆盖情况以及是否支持跨区域复制。别比单价,比内网联通性,不产生公网流量费就是最大的降本。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/645584.html





