☰
Docker私有仓库与重启策略实战:从搭建到自愈的完整指南
2026/10/28 3:50:57 网站建设 项目流程

1. 私有仓库与重启策略:一次把两个硬骨头啃透

搞容器化落地到一定阶段,几乎所有人都会撞上同一堵墙:镜像总不能一直靠公网拉,容器总不能一崩就靠人肉重启。前者逼着你把私有仓库搭起来,后者逼着你把重启策略吃透。这两件事看着不搭界,实际上是一套组合拳——仓库解决“镜像从哪来”,重启策略解决“容器挂了怎么办”,一个管供给,一个管自愈,缺了哪个,生产环境都跑不踏实。

我见过太多团队,本地开发时docker run用得飞起,一上测试环境就抓瞎:镜像推不上去,因为没仓库;容器半夜挂了没人管,因为没配重启策略。等到业务方早上来问“服务怎么挂了”,运维才手忙脚乱登机器手动start。这种场景反复出现几次,团队自然会把私有仓库和重启策略提上日程。

这篇内容就是把这套组合拳拆开揉碎讲清楚。私有仓库部分,我会从最朴素的registry镜像讲起,一路说到带认证、带 TLS、带存储规划的可用方案,把“为什么这么配”讲透;重启策略部分,把no、on-failure、always、unless-stopped四种策略的边界条件、踩坑点和选型逻辑掰开讲。适合已经会用 Docker 基本命令、准备把容器往准生产环境推的开发者,也适合被“容器又挂了”折磨过的运维同学。看完你至少能做到两件事:自己搭一个能长期用的私有仓库,以及给每个容器选对重启策略而不是无脑always。

2. 私有仓库:为什么非搭不可,以及方案怎么选

2.1 先想清楚:私有仓库到底解决什么问题

很多人对私有仓库的理解停留在“公司内网不能连外网,所以要有个地方存镜像”。这个理解只对了一半。私有仓库真正的价值有三层,一层比一层实际。

第一层是供给可控。公网镜像源受网络波动、限流、镜像下架影响,你今天pull得到的镜像,明天可能就拉不到了。业务镜像一旦依赖公网,部署的确定性就没了。私有仓库把镜像攥在自己手里,拉取速度和可用性都由自己说了算。

第二层是版本可追溯。镜像推到私有仓库,配合 tag 规范,每一次构建产物都有据可查。出问题时能精确回滚到某个历史版本,而不是“上次那个能跑的镜像去哪了”。

第三层是安全边界。内部镜像里可能有业务配置、内部依赖,这些东西不该出现在任何公网可达的地方。私有仓库配合认证和网络隔离,把镜像的流转限制在可控范围内。

理解了这三层,你就明白为什么不能随便找个网盘存tar包了事——docker save出来的包没有分层复用、没有 tag 管理、没有并发拉取能力,规模一上来就是灾难。

2.2 方案选型:从 registry 到 Harbor,按需上台阶

私有仓库的方案不止一个,选型的关键是看团队规模和需求复杂度,不是越重越好。我按从轻到重列一下常见选择,以及各自适合的场景。

方案部署复杂度认证与权限Web 界面适合场景
registry(官方镜像)极低需自行叠加无个人、小团队、内网临时用
registry + basic auth + TLS低基础认证无小团队准生产
Harbor中完整 RBAC有中型团队、多项目
云厂商托管仓库极低完整有不想自运维的团队

我个人的建议很直接:如果你只是想先把镜像存起来、团队不超过十个人、没有复杂的权限隔离需求,就用官方registry镜像加认证和 TLS,够用且好维护。Harbor 功能确实全,但它自带数据库、缓存、任务队列一堆组件,运维成本不低,小团队上 Harbor 经常是“为了用 20% 的功能维护 80% 的复杂度”。

这里要解释一个常见误区:有人觉得 registry 太简陋,没有界面不好管理。实际上镜像管理靠的是 tag 规范和清理脚本,不是靠界面点来点去。界面在镜像数量上千之后才有明显价值,在那之前,一套清晰的 tag 命名规则比任何界面都管用。

2.3 存储规划:别等磁盘满了才想起来

私有仓库的镜像存在本地磁盘上,这是最容易被忽视的坑。registry默认把数据存在容器内的/var/lib/registry,如果不做卷映射,容器一删镜像全没;如果做了映射但不做清理,磁盘迟早被历史镜像撑爆。

存储规划要提前想清楚三件事。第一,数据目录挂到宿主机独立分区,别和系统盘混在一起,镜像增长是不可控的,系统盘被撑满会连带整机出问题。第二,预留清理机制,registry 本身不自动删旧镜像,需要配合垃圾回收(garbage collect)定期跑。第三,考虑备份,镜像仓库挂了重建成本很高,数据目录要纳入备份范围。

注意:registry 的垃圾回收必须在仓库只读或停止写入时进行,否则可能删掉正在被引用的层。生产环境一般安排在低峰期,先把仓库切只读,跑完 GC 再切回来。

3. 私有仓库实操:从裸奔到带认证带 TLS

3.1 第一步:先把最朴素的 registry 跑起来

任何方案都从最小可用开始。先跑一个不带认证的 registry,验证基本推拉流程,这一步的目的是排除网络和基础配置问题,别一上来就叠认证和 TLS,出了问题分不清是哪层的事。

docker run -d \ --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ --restart unless-stopped \ registry:2

这里几个参数值得说清楚。-v /data/registry:/var/lib/registry是数据持久化的关键,宿主机目录选独立分区。--restart unless-stopped让仓库容器自己也能自愈——仓库是基础设施,它挂了所有推拉都停,必须配重启策略,这里先用unless-stopped,后面讲策略时会解释为什么不用always。

跑起来之后验证一下。先给本地镜像打个 tag 指向仓库地址:

docker tag nginx:latest 127.0.0.1:5000/nginx:test docker push 127.0.0.1:5000/nginx:test

如果 push 报http: server gave HTTP response to HTTPS client,别慌,这是 Docker 默认要求 HTTPS 导致的。临时验证可以在 daemon 配置里把该地址加入insecure-registries,但这只是验证手段,生产环境必须上 TLS,原因后面讲。

3.2 第二步:加上 basic auth,把门锁上

裸奔的 registry 任何人只要能访问端口就能推拉镜像,内网也不行——内网横向移动的风险是真实存在的。加 basic auth 是最低成本的门锁。

先生成密码文件。用htpasswd工具,没有的话装httpd-tools或直接用容器跑:

mkdir -p /data/registry-auth docker run --rm --entrypoint htpasswd httpd:2 -Bbn myuser mypassword > /data/registry-auth/htpasswd

-B表示用 bcrypt 加密,-b表示从命令行读密码,-n表示输出到标准输出。密码别用弱口令,也别把明文密码写进任何脚本,这是基本纪律。

然后带认证重启 registry:

docker run -d \ --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ -v /data/registry-auth:/auth \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ --restart unless-stopped \ registry:2

推拉之前先登录:

docker login 127.0.0.1:5000 -u myuser -p mypassword

登录成功后凭据存在~/.docker/config.json,后续推拉自动带上。这里有个实操心得:多用户场景下,htpasswd 文件是共享的,新增用户直接往文件里追加即可,不用重启 registry,registry 每次请求都会重新读文件。这个特性很多人不知道,白白重启了好几次。

3.3 第三步:上 TLS,把传输加密补上

basic auth 解决了“谁能进”,但密码和镜像内容在网络上还是明文传输的。TLS 解决的是“传输过程不被窥探和篡改”。生产环境这两者必须一起上,缺一不可。

证书可以自签也可以走正规签发。内网环境自签完全够用,关键是让所有客户端信任这张证书。自签流程大致是:生成私钥、生成证书签名请求、自签证书,指定好 SAN(Subject Alternative Name),把仓库访问用的域名或 IP 写进去。

openssl req -newkey rsa:4096 -nodes -sha256 \ -keyout /data/registry-certs/domain.key \ -x509 -days 3650 \ -out /data/registry-certs/domain.crt \ -subj "/CN=registry.internal" \ -addext "subjectAltName=DNS:registry.internal,IP:10.0.0.10"

-addext subjectAltName这行是关键,现代客户端校验证书时看的是 SAN 而不是 CN,漏了这行会报证书不匹配。-days 3650给十年有效期,内网自签可以这么干,省得频繁换证书,但要记在日历上别忘了。

带 TLS 启动 registry:

docker run -d \ --name registry \ -p 443:443 \ -v /data/registry:/var/lib/registry \ -v /data/registry-auth:/auth \ -v /data/registry-certs:/certs \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPWD_PATH=/auth/htpasswd \ --restart unless-stopped \ registry:2

客户端这边,把domain.crt放到 Docker 信任的证书目录(Linux 下通常是/etc/docker/certs.d/registry.internal/ca.crt),然后docker login registry.internal就能正常走 HTTPS 了。这一步最容易踩的坑是证书路径和文件名,Docker 对目录结构有严格要求,路径错了报错信息又很含糊,排查时先确认这个目录结构对不对。

3.4 第四步:垃圾回收与日常维护

仓库跑起来只是开始,长期维护才是真功夫。镜像越推越多,磁盘迟早告急。registry 提供垃圾回收命令,但要注意它的工作方式:它扫描所有 manifest,把没有被任何 manifest 引用的层删掉。这意味着光删 tag 不够,得先删 manifest,再跑 GC。

删除 manifest 通过 API 操作,需要先拿到 digest:

curl -u myuser:mypassword -k \ https://registry.internal/v2/nginx/manifests/test \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ -I

响应头里的Docker-Content-Digest就是 digest,然后 DELETE 掉:

curl -u myuser:mypassword -k -X DELETE \ https://registry.internal/v2/nginx/manifests/<digest>

最后在仓库容器里跑 GC:

docker exec registry \ registry garbage-collect /etc/docker/registry/config.yml

注意:GC 期间仓库最好只读,否则有极小概率误删正在被引用的层。另外 GC 不会清理空的 blob 目录,磁盘占用不会立刻下降,这是正常现象,别以为没生效。

日常维护我建议做成定时任务:每周跑一次 GC,每月检查一次磁盘水位,tag 命名强制带日期或构建号,方便按规则批量清理。这些规矩定下来,仓库能稳定跑很久。

4. 四大重启策略:选错一个,半夜就得爬起来

4.1 先把四种策略的语义掰清楚

Docker 的重启策略有四种:no、on-failure、always、unless-stopped。名字看着直白,但边界条件很多人没搞明白,选错策略导致的“该重启的没重启、不该重启的疯狂重启”是运维事故的常见来源。

no:默认值,容器退出后不重启。适合一次性任务、调试容器、明确不需要自愈的场景。

on-failure:容器以非零状态码退出时重启。可以带最大重试次数,比如on-failure:3表示最多重启三次。适合批处理任务、可能因临时错误失败的作业。

always:无论退出状态码是什么都重启。容器被手动stop后,如果 Docker 守护进程重启,它也会被拉起来。适合必须常驻的服务。

unless-stopped:和always类似,但有一个关键区别——如果容器是被手动stop的,那么即使 Docker 守护进程重启,它也不会被拉起来。适合大多数常驻服务。

这四种策略的核心差异其实就两个维度:退出状态码是否影响重启,以及手动停止后守护进程重启时是否恢复。把这两个维度想清楚,选型就不会错。

策略非零退出重启零退出重启手动 stop 后守护进程重启是否恢复典型场景
no否否否一次性任务、调试
on-failure是否否批处理、可失败作业
always是是是必须常驻且不接受手动干预的服务
unless-stopped是是否绝大多数常驻服务

4.2 always 和 unless-stopped 的真实差异,很多人理解反了

这两个策略最容易被混用,因为日常表现几乎一样。差异只在一种场景下暴露:你手动docker stop了一个容器,然后重启了 Docker 守护进程。

用always的容器,守护进程重启后会被拉起来——哪怕你之前明确 stop 过它。用unless-stopped的容器,守护进程重启后保持停止状态,尊重你之前的 stop 操作。

这个差异为什么重要?想象一个场景:线上出问题,你手动停掉某个容器做排查,然后顺手重启了 Docker 守护进程做配置调整。如果那个容器是always,它会自己爬起来,可能在你还没排查完的时候又制造问题;如果是unless-stopped,它老老实实停着,等你处理完再手动起。

所以我的选型原则很明确:绝大多数常驻服务用unless-stopped,只有那些“无论如何都必须运行、不接受任何手动停止”的特殊服务才用always。私有仓库本身我就用unless-stopped,因为维护时我需要能把它停住。

4.3 on-failure 的重试次数:不设上限等于埋雷

on-failure不带次数时,失败就无限重启。这看起来是“尽力自愈”,实际上是灾难——如果容器因为配置错误启动即崩,它会陷入“启动-崩溃-重启”的死循环,疯狂刷日志、吃 CPU,还可能触发依赖服务的连锁反应。

带次数限制才是正确用法,比如on-failure:5。五次还起不来,说明不是临时故障,该让人介入了。这里有个实操细节:重试次数是累计的,不是“连续失败”计数。容器成功运行一段时间后再失败,计数不会清零。所以对于长期运行的服务,on-failure其实不太合适,它更适合生命周期短、失败可预期的批处理任务。

批处理任务的典型配置是这样:

docker run --rm \ --restart on-failure:3 \ my-batch-job:latest

--rm配合on-failure有个微妙之处:容器成功退出后会被自动删除,失败重启时容器还在。这个组合适合“跑完就清理”的作业,但要注意失败后容器会保留,方便查日志。

4.4 重启策略与容器编排的边界

有个问题必须说清楚:Docker 单机的重启策略,和编排系统的自愈是两套机制,别混着用。如果你把容器交给编排系统管理,重启策略应该交给编排层,容器本身的重启策略要相应调整,否则两套机制打架,行为会变得难以预测。

单机场景下,重启策略是最后一道防线,它只能做到“进程挂了拉起来”,做不到“健康检查失败换实例”“节点挂了迁移”。所以别指望配个always就高枕无忧,它解决的是进程级自愈,不是服务级高可用。这个认知边界很重要,很多“配了 always 怎么还挂”的困惑,根源就是把进程自愈当成了服务高可用。

5. 组合实战:把仓库和策略配成一套能长期跑的方案

5.1 一个完整的仓库启动配置

把前面所有要素合起来,给一个可以直接抄的仓库启动方案。这个配置包含数据持久化、认证、TLS、重启策略,是我实际用下来比较稳的组合。

docker run -d \ --name registry \ -p 443:443 \ -v /data/registry:/var/lib/registry \ -v /data/registry-auth:/auth \ -v /data/registry-certs:/certs \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ -e REGISTRY_STORAGE_DELETE_ENABLED=true \ --restart unless-stopped \ registry:2

REGISTRY_STORAGE_DELETE_ENABLED=true这行容易被漏掉,不开启的话 DELETE manifest 的 API 会返回不支持,垃圾回收就无从谈起。这个参数是删除功能的总开关,务必加上。

5.2 业务容器的策略选型清单

仓库配好了,业务容器怎么配重启策略?我整理了一个按容器类型的选型清单,照着对号入座即可。

  • 常驻 Web 服务、API 服务:unless-stopped。需要自愈,但也要保留手动停止的能力。
  • 数据库、缓存等有状态服务:unless-stopped,但要配合健康检查和数据卷。注意重启策略救不了数据损坏,有状态服务的自愈要更谨慎。
  • 定时任务、批处理:on-failure:N,N 取 3 到 5。跑完即止,失败有限重试。
  • 一次性迁移脚本、调试容器:no。跑完就结束,不需要自愈。
  • 基础设施组件(仓库、监控代理等):unless-stopped。它们本身要稳,但维护时需要能停。

这个清单不是教条,核心逻辑是:问自己两个问题——这个容器挂了要不要自动起来?我手动停它的时候,希望它尊重我的操作吗?两个问题的答案组合起来,策略自然就定了。

5.3 验证重启策略是否真的生效

配完策略别假设它生效了,动手验证。验证方法很直接:进容器把主进程杀掉,看它是否按预期重启。

docker exec -it my-service sh -c "kill 1"

kill 1杀掉 PID 1 进程,容器退出。然后观察:

docker ps -a --filter name=my-service

看容器的状态和重启次数。always和unless-stopped应该看到它已经重新跑起来,RESTARTS计数增加。on-failure要看退出码,非零才会重启。no则保持退出状态。

再验证手动停止的行为:docker stop my-service,然后重启 Docker 守护进程,看容器是否被拉起。这一步能区分always和unless-stopped,是验证策略语义是否如你所愿的关键测试。这个测试建议在准生产环境做一次,别等真出事才发现策略配反了。

6. 踩坑实录:那些文档里不会写的教训

6.1 仓库相关的高频问题速查

私有仓库的坑集中在认证、证书、存储三块。我把实际遇到过的问题整理成速查表,遇到报错先对号入座。

现象可能原因排查方向
push 报 HTTPS client 错误未配 TLS 且未加 insecure 白名单检查 daemon 配置或补 TLS
login 成功但 push 401htpasswd 路径或 realm 配置错核对环境变量与文件挂载
证书报 not trusted客户端未信任 CA 或 SAN 不匹配检查证书目录与 SAN 字段
磁盘持续增长未配 GC 或未开删除开关开启 DELETE_ENABLED 并定期 GC
拉取镜像很慢存储盘 IO 瓶颈或网络问题检查磁盘类型与网络链路

这里重点说证书那个坑。自签证书最常见的失败是 SAN 不匹配——你用 IP 访问,但证书 SAN 里只有域名,就会报错。解决办法是签发时把 IP 和域名都写进 SAN,或者统一用域名访问。别图省事用 CN 糊弄,现代客户端不认。

6.2 重启策略的隐蔽陷阱

重启策略有几个坑,属于“不看日志根本想不到”的类型。

第一个坑:容器启动即崩导致的重启风暴。配了always但容器因为配置错误启动就退出,会陷入无限重启。表现是docker ps里状态疯狂闪烁,日志刷屏,CPU 被吃满。这种问题重启策略本身救不了,得靠日志定位根因。所以配always之前,先确保容器能稳定启动。

第二个坑:健康检查与重启策略的错位。重启策略只看进程是否退出,不看进程是否健康。一个进程活着但已经卡死的容器,重启策略不会管它。这种场景需要健康检查配合,健康检查失败后由外部机制处理,单靠重启策略兜不住。

第三个坑:--restart和docker update的交互。运行中的容器可以用docker update --restart改策略,但改完之后的行为要重新验证,别假设立即按新策略执行。我遇到过改完策略以为生效了,结果下次重启才发现没按预期走,白排查半天。

提示:排查重启相关问题时,docker inspect里的RestartCount和State字段是第一手信息,先看这两个再往下查,能省很多时间。

6.3 我个人的几条硬规矩

用久了会形成一些不写在文档里但很管用的习惯,分享几条。

规矩一:任何基础设施容器都必须配重启策略,且优先unless-stopped。仓库、监控、日志收集这些组件挂了影响面大,必须自愈,但维护时又要能停住,unless-stopped是平衡点。

规矩二:有状态服务的重启策略要配,但别迷信它。数据库容器重启后能不能正常服务,取决于数据卷和恢复逻辑,重启策略只保证进程起来,不保证服务可用。有状态服务的自愈要配合健康检查和数据校验。

规矩三:策略变更要走验证流程。改重启策略不是改个参数就完事,要在准生产环境验证“杀进程重启”和“手动停止后守护进程重启”两个场景,确认行为符合预期再上生产。

规矩四:仓库的 GC 和备份做成定时任务,别靠人记。人一定会忘,脚本不会。把 GC 和备份写进定时任务,跑失败了要有告警,这是仓库能长期稳定运行的基础。

这套组合拳打下来,镜像供给和容器自愈两个基础能力就都有了。剩下的就是根据业务实际情况微调参数、补充监控。我在实际使用中的体会是,基础设施的稳定性不取决于配置多复杂,而取决于每个配置项背后的逻辑你是否真的想清楚了——想清楚了,出问题能快速定位;没想清楚,配得再全也是碰运气。

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

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

立即咨询