资讯详情

资讯详情

私有Docker Registry部署运维与镜像分发实践

1. 自建镜像仓库的动机与边界私有 Docker Registry 这个词我在过去几年里被问过不下二十次。有人是被公共镜像托管服务的拉取频率限制卡住了脖子流水线一跑就是几十个节点同时拉取有人是内网环境根本出不去构建机与外网完全隔离镜像只能靠 U 盘来回拷还有人遇到的是更隐蔽的问题——某次线上回滚发现要用的那个镜像版本在公共仓库里被覆盖了tag 还在内容已经不是当初那一份了。这几种场景归根到底指向同一件事把容器镜像的分发链路从别人手里拿回到自己手里。所谓私有 Docker Registry本质上就是一个遵守容器镜像分发协议Distribution API的 HTTP 服务它负责接收docker push上传的镜像层按仓库名和 tag 组织存放响应docker pull与 manifest 查询请求。它能做的事情很朴素给镜像一个稳定、内网可达、权限可控的落点。它解决的不是能不能跑容器的问题而是镜像从哪来、谁能推、谁能拉、留多久、怎么不留垃圾这一整套供应链问题。适合谁来参照这份笔记团队里负责构建流水线、负责基础环境、或者手上有几台机器想先跑起来试试的开发者对容器有基本操作经验就够不需要提前懂 Go 或者看源码。我先把边界划清楚因为很多坑来源于期望错位。纯 Registry 只做存取它不做 Web 界面、不做用户组、不做漏洞扫描、不做镜像签名验证的策略拦截、也不做跨实例的自动同步。它像一个只提供 HTTP 接口的对象存储门面剩下的东西得你自己搭。想清楚这一点以后选型就不会纠结要的是够用的存储与分发还是带完整权限体系和界面的一体化平台。这两者的运维成本差一个数量级我见过太多团队一上来就堆一个重型平台结果一年下来只用到 push 和 pull 两个动作反而因为组件太多、依赖太杂出问题时排查链路长得让人崩溃。1.1 三个反复出现的真实痛点第一个痛点是拉取的稳定性。公共镜像托管服务对匿名拉取有频率限制这个限制是按来源 IP 统计的落到企业出口那条共享带宽上意味着构建流水线的高峰期很容易触发限流报错就是toomanyrequests重试几次可能好也可能一直不好。构建机被卡住的时候整个发布窗口都要跟着推迟。自建以后这个限制就变成了你自己磁盘和网卡的问题能不能扛住是容量规划问题不是别人的配额问题。第二个痛点是内容的不可变性与可追溯。公共仓库里的 tag 是可以被覆盖的镜像的所有者随时可以推一个新的覆盖掉旧的。对于生产环境来说这是个相当大的隐患你的部署清单里写的是myapp:stable三个月后重拉拿到的可能是一个完全不同的东西。私有仓库里我们可以强制一套规范——生产用的镜像只能用 digest 引用或者用版本号短提交号构成不可变 tag且推上去之后不允许覆盖需要新版本就换 tag。要真正做到不覆盖还需要仓库侧配合删除权限的收紧这个后面会讲。第三个痛点是带宽与分发效率。同一个基础镜像层在几十个服务的镜像里都出现从外网拉一次和拉一百次耗时可差两个数量级。Registry 的存储模型天然按内容寻址content-addressable相同的层只存一份内网再拉取时走的是局域网带宽。我实测过一个具体场景二十个服务镜像总逻辑大小约 9GB去重之后仓库实际占用 3.2GB 左右首次全量拉取对一台内网机器来说是从十几分钟的等待变成两分多钟而且不再依赖出口网络状况。1.2 该放什么、不该放什么我给自己定的规矩是只放构建产物和上游镜像的内网快照不放任何密钥、配置文件、数据文件。很多人在打包时图省事把.env、证书、私有 pip 源凭据塞进镜像层然后推到仓库里。镜像层是不可变的你把凭据从后一层删掉它仍然留存在前面的层里任何人拉到镜像后用docker save或者直接读层内容都能拿到。仓库加了认证也不等于安全因为能拉的人本来就多。另一条规矩是控制上游镜像快照的数量。早期我喜欢把用到的每个上游镜像都pull下来再tag再push到内网仓库后来发现仓库里堆了几百个 tag一半是三个月没人碰的。正确的做法是只镜像当前实际依赖的版本并且定期用脚本比对哪些 tag 在近 90 天内被拉取过没有访问记录的进入淘汰清单。这个动作看着琐碎但能避免仓库磁盘以一种润物细无声的方式被吃满而磁盘满导致的 push 失败往往是在发布最紧张的时候暴露的。还有一类内容我坚决不放构建缓存。有些流水线会把中间层镜像也推上去做缓存复用这在大型项目里确实能省时间但它让仓库的容量增长曲线变得不可预测。如果确实需要缓存复用我会单独开一个仓库名前缀比如cache/并且在清理策略上和正式镜像完全隔离容量单独设阈值。1.3 三种落地形态的取舍方案形态组成上手成本适合场景主要短板单机最小可用Registry 容器 本地磁盘 htpasswd半天内可跑通小团队、内网实验、流水线初建无高可用磁盘单点前置网关型Registry Nginx/网关做 TLS 与访问日志一到两天需要统一入口、需要审计日志仍需自己补备份与清理一体化平台型带权限、复制、扫描的重型组件集一周以上需专人维护多团队、多集群、合规要求高组件多故障面大升级风险高我一般的建议是从第一档起步把镜像命名的规范和清理脚本先跑顺等确实出现了多团队互相覆盖、需要跨机房复制这类具体需求再往上加。先搭重型平台再补规范通常会变成平台功能一大堆实际行为没人说得清。2. 私有 Registry 的存储模型与运行骨架很多人用 Registry 用了很久但对它的存储结构没有概念结果一遇到磁盘告警、删除不生效、GC 之后空间没释放这几个问题就抓瞎。这一节把模型说清楚后面所有运维动作都能对上号。Docker 分发协议里有两个核心概念blob和manifest。blob 是内容块可能是镜像的某一层layer也可能是配置 JSON。每个 blob 用它内容的 SHA256 摘要作为唯一标识摘要一样就是同一份内容所以天然去重。manifest 是一份清单它记录了这个镜像由哪些 blob 组成、各自的摘要和大小、以及配置 blob 是谁。tag 则是一个可变的指针指向某个 manifest 的摘要。docker pull myapp:1.2.0的过程是先按仓库名和 tag 拿到 manifest再按 manifest 里列出的摘要逐个去取 blob每取到一个都做一次摘要校验对不上就报错。这个校验机制就是为什么镜像传输中途损坏不会静默通过——它一定会报digest mismatch或者解压失败。理解了这一层几个常见困惑就自解了。为什么同一个基础镜像占用的空间不随镜像数量线性增长因为层内容相同摘要相同物理上只有一份。为什么删了 tag 磁盘没变因为 tag 只是个指针删指针不动 blobblob 要靠垃圾回收才会真正消失。为什么 GC 之后空间还是没释放多少因为还有别的 manifest 在引用那些层引用计数不为零就不能删。2.1 磁盘布局与容量估算的做法文件系统存储驱动下典型目录结构是这样根目录下有个docker/registry/v2/里面blobs/sha256/存放所有内容块按摘要前两位分目录repositories/仓库名/_manifests/下按 tag 和 revision 存放 manifest 引用_layers/下是层与仓库的关联链接。这里的链接是普通的空文件靠路径表达引用关系——所以备份时必须保留目录结构不能只拷 blob 内容否则引用全丢。容量估算我用的公式是这样设基础镜像层集合去重后大小为B团队有N个服务镜像每个服务在基础层之上独占的层合计约S每个服务保留的历史版本数为V则粗略占用 ≈B N × S × V × 0.6。最后那个 0.6 是经验系数因为同一服务的多个版本之间往往也共享大量层实测比线性增长要缓。举个具体数B取 900MB一个精简运行时加常用系统包N30S120MB业务代码与依赖V5算下来约 900MB 30×120MB×5×0.6 ≈ 11.7GB。但实际跑三个月后我看过一眼是 8GB 出头。做规划时我一般按公式值再乘 1.5 留余量因为临时调试用的镜像、CI 中间产物、误推的东西都会偷偷加进来。注意不要把仓库根目录放在系统盘。磁盘写满时如果系统盘就是数据盘docker 守护进程自己都可能起不来连排查的机会都没了。单独挂一块盘或者至少单独分一个卷配一个 80% 水位的告警。2.2 认证与加密的最小组合生产环境我从来不用裸 HTTP 无认证的组合哪怕它只在内网。原因不是怕外部攻击而是怕内部误操作没有认证任何能访问到端口的机器都能往里推东西一个写错地址的流水线就能把测试镜像推成生产 tag。最小的安全组合是HTTPS basic auth两者都不复杂。证书有两种走法。有内部域名和签发通道的直接申请正式证书省心。没有的话就用自签证书但必须注意自签证书的 SANsubjectAltName里要把客户端实际访问用的域名和 IP 都列上只写 Common Name 是不够的现代客户端的严格校验会直接拒绝报错是x509: certificate is valid for X, not Y。我踩过这个坑当时证书里只写了主机名结果用 IP 访问的所有构建机全部失败排查了一圈才发现是 SAN 不匹配。认证用 htpasswd 文件最省事Registry 原生支持。密码必须用 bcrypt 格式生成用普通的 MD5 或者 crypt 格式生成出来Registry 会认证失败而且报错信息不明显只返回 401很容易被误判成密码错了。生成命令后面会写。这个方案的局限是只有用户名密码这一维没有细粒度的读/写权限区分——能认证的人都能推。团队规模小的时候够用一旦出现外包人员只能拉不能推的需求就得在前面加一个能按 HTTP 方法做鉴权的网关或者换带权限模型的一体化方案。2.3 三个必须提前定好的参数第一个是监听地址与端口。默认 5000容器内监听的地址建议显式写成0.0.0.0:5000否则在某些网络模式下只绑 IPv6 或者只绑回环。端口映射到宿主机时也别用 5000 直接对外前面套一层网关用 443 更规范客户端配置也简单。第二个是存储的delete_enabled。这个开关默认是关的意味着即使你调删除 API 也会被拒绝。很多人以为删除失败是权限问题其实是没开这个开关。开了之后删除仍然只是删引用空间要等 GC这两步是分开的必须都做。第三个是单次请求的最大体积与超时。大镜像层上传时前置网关如果默认限制请求体大小常见是 1MB 或者 10MB会直接返回 413现象是docker push推到某个层突然中断。网关侧要把请求体限制关掉设成 0 表示不限并且关闭请求缓冲让数据流式转发否则大层会在网关侧先落盘一份既慢又吃磁盘。3. 从零到可用的完整部署过程这一节是能直接照做的那种。我假设你有一台内网 Linux 机器已经装好容器运行时有 root 或者 sudo能访问到镜像源。下面所有路径和参数都是我在多个环境里用过的稳定组合你按自己的域名和端口替换即可。3.1 前置检查清单先花五分钟把这几项确认掉能省掉后面一小时的瞎猜。容器运行时版本docker version看客户端与服务端都能通。磁盘df -h确认数据盘挂载点预留至少 50GB 起步。端口ss -lntp | grep 5000确认没被占。域名解析客户端机器上getent hosts registry.internal.example能解析到目标 IP。这里用示例域名你换成自己的内部域名。时间同步证书校验依赖时间节点时间偏差太大会报证书过期或者未生效。我还会提前把目录建好并设权限/data/registry数据、/data/registry-auth认证文件、/data/registry-certs证书。认证文件目录权限收一下只给运行容器的用户读chmod 700比较稳妥。3.2 生成证书与认证文件自签证书一条命令搞定关键是-addext里把 SAN 写全。下面这条生成的是带 SAN 的自签证书有效期 825 天mkdir -p /data/registry-certs cd /data/registry-certs openssl req -x509 -nodes -newkey rsa:4096 -days 825 \ -keyout registry.key \ -out registry.crt \ -subj /CNregistry.internal.example \ -addext subjectAltNameDNS:registry.internal.example,IP:10.20.30.40 chmod 600 registry.key生成完用这条自检一下确认 SAN 里有你要用的地址openssl x509 -in registry.crt -noout -text | grep -A1 Subject Alternative Name认证文件用 httpd 镜像里的 htpasswd 工具生成注意-B是 bcrypt千万别漏mkdir -p /data/registry-auth docker run --rm --entrypoint htpasswd httpd:2 \ -Bbn deploy_user 换成你的强密码 /data/registry-auth/htpasswd # 追加第二个用户时用 -B不带 n表示写入文件 docker run --rm -v /data/registry-auth:/auth --entrypoint htpasswd httpd:2 \ -Bb /auth/htpasswd ci_user 另一个强密码注意-bn生成到标准输出的写法在部分 shell 下会被重定向吃掉换行导致文件里多条记录粘在一行认证一定失败。追加用户时尽量用带-v挂载目录的写法直接在容器内写文件。3.3 Registry 配置与启动我不用一堆环境变量拼配置而是写一个完整的config.yml可读性和可维护性都好得多版本也能进版本库。version: 0.1 log: level: info fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true cache: blobdescriptor: inmemory maintenance: uploadpurging: enabled: true age: 168h interval: 24h http: addr: 0.0.0.0:5000 headers: X-Content-Type-Options: [nosniff] http2: disabled: false auth: htpasswd: realm: internal-registry path: /auth/htpasswd health: storagedriver: enabled: true interval: 10s threshold: 3uploadpurging这段值得单独说客户端上传中断网络抖、CI 容器被杀会留下一堆没完成的上传临时文件默认策略会清理但如果你把这项关掉或者把age设得很大这些碎片会一直占空间。我把它设成 7 天每天扫一次省心。对应的 compose 文件services: registry: image: registry:2 container_name: registry restart: always ports: - 10.20.30.40:5000:5000 volumes: - /data/registry:/var/lib/registry - /data/registry-auth:/auth:ro - /data/registry-certs:/certs:ro - ./config.yml:/etc/docker/registry/config.yml:ro environment: - TZAsia/Shanghai healthcheck: test: [CMD, /bin/registry, serve, /etc/docker/registry/config.yml] interval: 30s注意ports里我绑了具体的内网 IP而不是0.0.0.0这在有多网卡的机器上能避免意外暴露到不该通的网段。启动后先本地验一下docker compose up -d curl -k -u deploy_user:密码 https://registry.internal.example:5000/v2/ # 期望返回 {}如果返回 401 且WWW-Authenticate头存在说明认证生效了但你没带凭据返回{}说明认证通过。这个/v2/端点同时也是最好的健康检查目标——它活着服务基本就是活的。3.4 客户端打通证书与 insecure 的取舍客户端要信任这张自签证书。最规范的做法是把registry.crt分发到每台机器的/etc/docker/certs.d/registry.internal.example:5000/ca.crt注意目录名必须带端口少了端口 docker 找不到。分发完不需要重启 docker直接docker login就行。另一种做法是在/etc/docker/daemon.json里把地址加进insecure-registries跳过 TLS 校验。我把它当作过渡手段新环境先这样跑通流程然后在同一周内把证书补上。长期用 insecure 的代价不只是不加密——它让中间人替换镜像层成为可能。虽然层有摘要校验但 manifest 本身也是可以被伪造的最终你拉到的就是一个内容一致但来源不对的镜像集合。改daemon.json之后必须重启 docker 服务很多人改完直接 login报的还是老错误就是忘了重启。{ insecure-registries: [registry.internal.example:5000], max-concurrent-downloads: 6 }max-concurrent-downloads这个参数在内网拉大镜像时提一下有用默认 3 有时候跑不满带宽。3.5 第一次推拉验证与命名规范流程验证我按这个顺序走每步都能快速定位问题出在哪一层# 1. 登录 docker login registry.internal.example:5000 -u deploy_user # 2. 从小镜像开始减少变量 docker pull alpine:3.19 docker tag alpine:3.19 registry.internal.example:5000/base/alpine:3.19 # 3. 推送并看分层进度 docker push registry.internal.example:5000/base/alpine:3.19 # 4. 换一台机器或先本地删掉拉取 docker rmi registry.internal.example:5000/base/alpine:3.19 docker pull registry.internal.example:5000/base/alpine:3.19命名规范我建议在第一天就定死因为后期改名要动所有流水线。我的习惯是仓库地址/项目组/服务名:tagtag 用三段式主版本.次版本.构建号另外允许一个sha-短提交号形式的不可变 tag 供生产引用。生产部署的清单里只用后者不用latest也不用浮动 tag。这条规矩执行起来有点烦但它把回滚时拿到的东西和当初一模一样变成了默认行为而不是靠人记性。4. 长期运行的维护动作跑起来只是开始真正花时间的是后面这些删镜像、回收空间、备份、看日志、控容量。这一节按我实际的维护节奏来写。4.1 删除镜像与垃圾回收的正确顺序因为 tag 只是指针删除必须走 API而不是进目录 rm -rf。直接删目录会导致元数据与 blob 不一致后续 push 同名镜像可能出现奇怪的冲突。正确流程分三步。第一步拿到 manifest 的 digest。注意Accept头必须指定否则可能拿到 v1 格式或者被拒curl -s -k -u deploy_user:密码 \ -H Accept: application/vnd.docker.distribution.manifest.v2json \ -I https://registry.internal.example:5000/v2/myteam/myapp/manifests/1.2.0 \ | grep -i docker-content-digest第二步用 digest 删除curl -s -k -X DELETE -u deploy_user:密码 \ https://registry.internal.example:5000/v2/myteam/myapp/manifests/sha256:xxxx # 期望 202 Accepted第三步停机做 GC。GC 必须让仓库处于只读或停止状态否则并发写入可能被误判为无引用docker compose stop registry docker run --rm \ -v /data/registry:/var/lib/registry \ -v $(pwd)/config.yml:/etc/docker/registry/config.yml:ro \ registry:2 garbage-collect --delete-untagged /etc/docker/registry/config.yml docker compose start registry--delete-untagged会把所有没有 tag 指向的 manifest 一并清掉能明显多回收空间但要小心如果你的流程里有先推一个 sha tag之后用另一个 tag 指向同一个 manifest的操作中间窗口做 GC 可能误删。我的做法是 GC 只在固定的维护窗口做且做之前确认没有正在跑的流水线。GC 时间跟仓库规模相关10GB 级别的仓库一般一两分钟上百 GB 的可能要十几分钟期间服务不可用得安排在低峰。4.2 备份策略与一次真实的恢复演练Registry 的数据是元数据 blob两者必须一致所以备份不能只拷 blob。我用的方案是文件级冷备先停服务用tar打包整个根目录保留硬链接和目录结构再启动。增量场景下 rsync 也行但必须保证引用文件一起同步。docker compose stop registry tar -C /data -czf /backup/registry-$(date %F).tar.gz registry docker compose start registry频率取决于镜像更新节奏我服务的团队一般一周一次全量保留最近四周。备份文件放另一台机器别放在同一个盘上——这条不用解释。恢复演练我建议至少做一次因为它会暴露几个真实问题一是只恢复 blob 目录不恢复repositories/下的引用文件仓库会显示空目录二是恢复后htpasswd和证书如果不在备份范围内服务起不来三是权限位丢失导致容器内进程读不了文件。我第二次演练就栽在第三条上解包后所有文件属主变成了解包用户容器里那个非 root 用户读不了日志里报permission denied当时还以为是数据损坏。4.3 前置网关TLS 卸载、日志与限流把网关放在 Registry 前面好处有三个统一入口与正式证书、拿到清晰的访问日志、能按路径做粗粒度限流。配置要点不多但每个都不能漏。server { listen 443 ssl; server_name registry.internal.example; ssl_certificate /etc/nginx/certs/registry.crt; ssl_certificate_key /etc/nginx/certs/registry.key; client_max_body_size 0; # 不限请求体大层必须 proxy_request_buffering off; # 流式转发避免网关落盘 proxy_buffering off; chunked_transfer_encoding on; location /v2/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900s; proxy_send_timeout 900s; } }proxy_set_header Host $http_host这一行不能省成$host因为$host不带端口而 Registry 在做重定向比如分片上传返回的 location时依赖原始 Host 拼地址搞错了客户端会去一个错误地址继续上传现象是 push 卡在某一层不动超时后失败。日志里我会定期看两类一是 401 的分布某个 IP 401 特别多通常是密码过期或者流水线里的凭据写错了二是出现 413、499 的次数413 说明还有地方没放开请求体限制499 是客户端主动断开多半是超时设太短。这些统计不需要复杂工具grep awk按天聚合就够用。4.4 容量监控与清理自动化监控我只要三个指标用最简单的脚本实现指标采集方式告警阈值触发后的动作磁盘使用率df取数据盘80% 预警 / 90% 严重跑清理脚本 通知/v2/可用性curl 带凭据请求连续 3 次失败检查容器与网关上传失败数网关日志聚合 4xx/5xx单日超过阈值查流水线凭据与超时清理脚本的逻辑很简单但有几个细节要处理。先按仓库列举 tag取每个 tag 最后一次被访问的时间这个信息纯 Registry 不提供得从网关日志里捞或者干脆用镜像创建时间近似超过 N 天没有访问记录的列入候选。清单先输出不删人工扫一眼确认再执行删除。我给自己加了一条硬规则prod/前缀的镜像禁止自动删除只能人工处理。这条规则救过一次急脚本逻辑写错的时候把某个前缀匹配错了因为排除了生产前缀最坏情况没有发生。自动清理的另一个细节是不要一次删太多。GC 是全量的、要停服务的如果一次删了 200 个 tagGC 时长得让人焦虑。分批删每次几十个维护窗口内完成。5. 常见问题与排查实录这一节是我这几年攒下来的对照表大部分问题看报错信息就能定位。先说一个通用思路push/pull 失败时先判断是认证层、传输层还是存储层的问题。认证层报 401/403传输层报 TLS 或者连接超时存储层报 blob unknown、digest 不匹配、磁盘满。按这个分类走比漫无目的地看日志快得多。5.1 常见报错速查表报错信息片段真实原因处理方式http: server gave HTTP response to HTTPS client客户端用 https 访问了一个只提供 http 的服务或证书未受信任配置证书或临时加入 insecure-registries 并重启 dockerx509: certificate signed by unknown authority客户端未信任自签 CA把 ca.crt 放到/etc/docker/certs.d/域名:端口/x509: certificate is valid for A, not B证书 SAN 里没有当前访问用的地址重新签发SAN 写全域名与 IPno basic auth credentials/ 反复 401htpasswd 密码不是 bcrypt或-bn写入时记录粘连用-B重新生成检查文件行数blob unknown to registrymanifest 引用的层在上传阶段中断元数据与内容不一致重新 push检查 uploadpurging 配置与磁盘空间413 Request Entity Too Large网关默认请求体限制网关client_max_body_size 0received unexpected HTTP status: 500出现在 push 末尾磁盘写满或 GC 期间写入看磁盘水位检查是否在 GC 窗口内操作manifest unknowntag 不存在或该镜像已被删除列 tag 确认/v2/name/tags/listpull 成功但容器起不来报解压失败镜像层损坏或传输中断删本地镜像重拉检查仓库侧 blob 完整性5.2 几个值得单独讲的坑第一个坑push 到一半卡住没有任何报错。现象是进度条停在某个层十分钟后超时。排查顺序是先看网关日志有没有对应请求如果没有说明请求卡在客户端到网关这一段通常是 MTU 或者中间设备的问题如果有请求但长时间无响应看proxy_request_buffering是否关了以及 Registry 容器的磁盘 IO 是不是被打满。我遇到过一次是备份任务和 push 撞在一起备份把磁盘 IO 吃满了push 的写入被拖到超时。后来把备份挪到了更深的时间段。第二个坑GC 之后空间没释放甚至 service 起不来。前者通常是别的 manifest 还在引用那些层属于正常后者多半是 GC 过程中被中断留下了不一致状态。我的处理办法是先确认服务能正常读能正常 push/pull 就先不动然后在一个维护窗口里重跑一次完整 GC。如果实在起不来用备份回滚别硬修——存储层的元数据手工修复的成功率不高代价却是时间。第三个坑多台构建机只有一台登录成功。九成是证书目录名少了端口。/etc/docker/certs.d/下的目录必须严格等于域名:端口写成registry.internal.example而不是registry.internal.example:5000docker 就不会去读里面的证书然后回退到系统信任链失败。这个错误没有任何提示非常隐蔽我第一次遇到时查了快两个小时。第四个坑tag 被覆盖导致回滚异常。有人在流水线里用固定 taglatest或者dev两个流水线并行跑的时候互相覆盖回滚时拉到的不是预期版本。这个没法靠 Registry 拦只能靠规范生产环境引用 digesttag 不允许覆盖可以配一个校验步骤push 前先查这个 tag 存不存在存在就失败。这条规矩的价值在事故复盘时才体现得最明显。5.3 我个人的几条实操心得第一条永远先在小镜像上验证链路。alpine几百 KB从 push 到 pull 走完只要几秒任何配置问题都能立刻暴露。用几百 MB 的业务镜像去调环境光是等传输就耗掉耐心还容易把传输问题误判成配置问题。第二条把证书有效期写进日历。自签证书过期那天所有构建机同时失败报错是certificate has expired场面很难看。我现在的做法是签发时就用 825 天同时在到期前 60 天设一个提醒提醒内容里直接写清楚重新签发的命令免得到时候还要翻记录。第三条仓库的日志一定要留够时间。排查某个镜像什么时候被谁推的这类问题时容器日志经常被覆盖网关的访问日志是最可靠的证据来源。至少留 30 天按天切割加压缩占用不大。第四条别在没有验证过恢复流程的情况下认为备份是有效的。我见过备份任务每天成功、文件也在但从来没解包过真出事的时候才发现打包时用了排除规则把元数据目录排掉了。抽半小时做一次恢复演练比每天看备份成功的日志更让人安心。第五条把镜像仓库不可用当成一个需要预案的故障。它一旦挂了所有依赖新镜像的发布都会停。预案很简单本地保留最近几个生产版本镜像或者保持一个只读的备用仓库地址客户端配置里能快速切换。这个准备成本很低但真出事的时候能让你从容很多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →