这两年和 Docker 打交道,我最大的感受是:本地镜像越来越多,管理就成了一件很头疼的事。docker pull偶尔会慢得让人怀疑人生,docker run启动一台容器倒是简单,但镜像怎么分发、备份、让其他机器或队友一起用,才是真正麻烦的地方。自己用registry搭一个私有仓库,又要处理存储、认证、HTTPS、权限,对一个开发者来说投入产出比实在太低。所以我一直推荐直接用云厂商的镜像仓库,省心也省力。2021 年我照着阿里云官方教程完整走了一遍创建镜像仓库的流程,从开通到把第一个镜像推上去,前后不超过二十分钟,后来换机器、做自动化发布都靠它兜底。这篇笔记就是当时全过程的整理,我把每一步的命令、控制台里的选项、以及那些容易踩的坑都写清楚了。哪怕你现在打开控制台发现界面和我当时不一样,只要理解了命名空间、仓库、Tag 这套底层逻辑,换个皮肤照样能上手。
1. 为什么需要自己的镜像仓库
1.1 镜像仓库到底解决了什么问题
很多人刚接触 Docker 时,习惯本地构建完镜像就直接docker run,跑通了就完事。等镜像多起来,问题就暴露了。第一是分散,你在一台机器上docker build成功,想挪到另一台机器跑,最原始的办法是把镜像docker save成 tar 包再拷过去,几十 MB 还好,几百 MB、几个 GB 的镜像传输和维护成本让人崩溃。第二是版本,今天构建的镜像和昨天构建的镜像可能差了好几个依赖版本,没有统一的中心化存储,几天后你根本分不清哪个是能用的、哪个是坏的。第三是协作,交给队友一个镜像,你不能指望人家手动导入 tar 包,大家需要一个都能访问的公共位置。
镜像仓库本质上就是这样一个"中央集散地",你只管把构建好的镜像推到仓库里,其他人或者任何一台服务器只要执行docker pull,就能把完整的镜像拉回来运行。Docker Hub 虽然是默认的公共仓库,但免费账户的私有镜像有数量限制,而且从国内访问速度不稳定,排队时间长。自己搭一个仓库又涉及域名证书、认证体系、存储配额、日志监控,一个人维护这样的基础设施,等于从开发又变成运维,纯亏。云厂商提供的托管镜像仓库,就是帮你把这些基础设施全部消化掉,你只需要注册一个账号、创建几个命名空间,剩下的高可用、存储扩容、权限隔离都不用管,专注把镜像推上去就行。
1.2 阿里云镜像仓库和 Docker Hub 怎么选
我这个标题说的是阿里云镜像仓库,很多人会问:直接用 Docker Hub 不行吗?行,当然行,但分场景。我整理过一张简单的对比表,用下来感受比较直观。
| 对比项 | Docker Hub | 阿里云容器镜像服务 |
|---|---|---|
| 默认访问速度 | 国内访问波动大,大镜像经常卡住 | 有国内节点,整体更稳 |
| 私有仓库免费额度 | 免费账户只有一个私有镜像 | 个人版支持多个私有仓库,基本够用 |
| 命名空间隔离 | 组织和使用者区分 | 支持命名空间隔离,适合小组协作 |
| 仓库地址个性化 | 地址固定为官方域名 | 每个地域有自己的独立域名,按部署区域选择 |
| 团队权限管理 | 要 Teams 账号才方便 | 企业版有 RAM 子账号权限体系 |
我个人的结论很简单:如果是练手、发布开源项目,Docker Hub 足够;如果你在国内环境部署、希望私有镜像不被公开、又不想承担自建仓库的运维压力,阿里云容器镜像服务是一个很务实的选项。尤其是当你的服务器本身就在阿里云上,VPC 内网拉镜像速度极快,这是 Docker Hub 比不了的。2021 年那会儿个人版入口免费开通,私有仓库数量给得还算宽松,对小团队和个人开发者非常友好,这也是我当初选它的直接原因。
1.3 什么人适合用这个方案
这类托管镜像仓库,最适合下面这几类人。第一类是个人开发者,自己电脑上东一个镜像西一个镜像,换电脑、重装系统以后能直接从云端拉回来,不用重新构建一遍依赖全部环境。第二类是两三个人的小团队,共用同一个账号下一组镜像仓库,每个人只关心自己的命名空间,互不干扰,比微信群传 tar 包不知道高到哪里去了。第三类是项目里已经用了自动化流水线的人,构建机负责docker build,构建完直接docker push推给测试机或生产机去拉,整个链路闭环,不需要运维手动干预。
也有人觉得"我就一个人写脚本,镜像无所谓",但只要你连续用过一个月 Docker,本地缓存和一层层构建产物迟早会堆到让你烦。把镜像仓库配好,相当于给自己买了一份"镜像保险",什么时候需要什么时候拉,成本就是前期十几分钟的配置时间。这篇笔记虽然是 2021 版教程的整理,但流程骨架到今天依然适用。
2. 动手前的准备与核心概念
2.1 环境准备清单
在开始创建仓库之前,先把环境检查一遍,省得到时候卡在某个地方还以为是阿里云的问题。你需要准备的东西非常简单:
- 一个可以正常使用的 Docker 环境,本地执行
docker -v能看到版本信息; - 一个阿里云账号,并且已经完成实名认证(个人版一般要求实名认证才能开通容器镜像服务);
- 如果懒得用命令行登录,顺手把最常用的
docker images看一下,明确自己本地有哪些镜像。
我当时是在一台 CentOS 服务器上操作的,Docker 版本是 19.03 那一代,后来在 Windows 的 Docker Desktop 上也试过,命令完全一样。只要 Docker 能正常执行,平台不影响后续操作。
这里提醒一下:阿里云容器镜像服务分为个人版和企业版,我教程里用的是免费的个人版。企业版的计费方式和功能有差异,比如支持企业实例、VPC 专有网络加速、RAM 子账号细粒度授权等,小项目和个人使用个人版完全足够。开通之前可以先看一下产品页面的免费额度说明,心里有个数。
2.2 三个绕不开的名词:命名空间、仓库、Tag
要理解镜像仓库的完整使用方式,先记住三个概念。第一个是命名空间。你可以把它理解成仓库的顶层目录,用来做租户隔离。比方说同一个账号下,你搞了一个demo命名空间,一个test命名空间,不同项目组或不同业务的镜像就分别放在各自的命名空间里面,互不干扰。命名空间一旦创建,一般不能改名,所以创建前想清楚,别随随便便起个临时名。
第二个是仓库。命名空间下面可以建多个仓库,每个仓库对应一个具体项目的镜像集合。比如命名空间demo下可以有一个nginx-app仓库,也可以有一个python-worker仓库。仓库名的意义在于区分同一命名空间下的不同应用,所以最好用能一眼看懂的名字,不要用111、test123这种连自己都认不出来的名字。
第三个是Tag。Tag 就是镜像的版本标签,通常用来标记一个仓库下不同版本的镜像。同一个镜像仓库下的镜像,靠 Tag 区分,v1.0.0、v20210101、latest都行。理解了三者的关系,再回头看一个完整的镜像仓库地址,就豁然开朗了。
完整地址的格式大概长这样:
registry.cn-hangzhou.aliyuncs.com/demo/nginx-app:v1.0.0
拆解来看,registry.cn-hangzhou.aliyuncs.com是阿里云某个地域的镜像服务域名,demo是命名空间,nginx-app是仓库名,最后的v1.0.0是 Tag。本地镜像要想推送到远端,必须把自己本地镜像的 Tag 改成与这个完整地址对应,Docker 才会知道要往哪儿推、推到哪个仓库下。
2.3 2021版教程的整体流程
当年阿里云官方教程的操作链路,我总结下来是六个步骤:
- 登录阿里云控制台,搜索"容器镜像服务",进入产品页并开通;
- 在左侧菜单找到"命名空间",创建一个命名空间;
- 在命名空间下创建镜像仓库,设置仓库名、地区、仓库类型;
- 配置访问凭证,设置 Docker 登录密码;
- 在本地执行
docker login登录镜像服务; - 本地镜像打好 Tag 后执行
docker push推送,再去docker pull验证。
这套流程每一步之间有严格的前后依赖关系:不创建命名空间就选不了仓库;不创建仓库就没有完整地址;不设置访问凭证,本地登录就会失败;不打 Tag,docker push就会报错。所以别跳步,也别急着并发操作。下面我从头到尾挨个演示。
3. 一步步创建自己的阿里云镜像仓库
3.1 开通容器镜像服务
第一步,打开浏览器登录阿里云控制台,在顶部搜索框输入"容器镜像服务"就能找到对应产品入口。点进去之后,如果你是第一次使用,页面一般会有一个明显的"开通"按钮。点击开通后,系统会自动为你创建个人版实例,这个过程不需要任何费用。
这里请注意:某些账号如果没有完成实名认证,开通可能失败,页面会跳出提示让你先补全实名信息。我身边还真有人卡在这一步,以为是产品没开放,其实是实名认证没做完。开通完成之后,你会进入容器镜像服务的控制台首页,左侧菜单能看到"实例列表""命名空间""镜像仓库""访问凭证"等入口,界面到 2021 年的时候基本已经比较成熟了。
我对 2021 版印象最深的是,个人版默认实例不需要自己创建,阿里云后台已经帮你初始化好了一个默认实例。你不需要管实例 ID 是什么,只要记住你自己的仓库地址域名按地域分配就够了。不同地域的域名不一样,比如华东 1(杭州)对应registry.cn-hangzhou.aliyuncs.com,华北 2(北京)对应registry.cn-beijing.aliyuncs.com。第一次创建命名空间时,选一个离你服务器或网络最近的地域,后面推送拉取都会快一些。
3.2 创建命名空间
进入"命名空间"管理页后,点击"创建命名空间",会弹出一个表单。需要填的就是命名空间名称。名称要求一般为小写字母、数字、连接符(-)或下划线(_),不能包含大写字母和特殊符号。命名空间在同一账号同一地域下是唯一的,换句话说,你整个账号的命名空间不能重名。
我当时在填名称时踩过一个坑:随手写了My Docker Images,带了空格和大写字母,结果系统直接报格式错误。后来老老实实改成demo-images才创建成功。所以建议所有名称的格式统一走小写加中划线,这个习惯在镜像 Tag 上也同样适用。
创建命名空间时,有的版本会让你选"自动创建仓库",我当时没有勾选,手工创建更可控。因为自动创建仓库往往是针对特定代码源场景设计的,如果只是本地上传镜像,完全没必要让它自动建,手动一个仓库一个仓库地加,反而更清楚。
3.3 创建镜像仓库
命名空间创建好了,接下来在"镜像仓库"菜单下点"创建镜像仓库"。需要填写的信息包括:
- 所在地域:选择你之前创建命名空间时选好的地域;
- 命名空间:下拉选择刚建好的命名空间;
- 仓库名称:填项目名,比如
nginx-app; - 仓库类型:选公开或私有;
- 摘要:简单说明这个仓库装的是什么镜像,选填。
仓库类型这一栏要特别说两句。公开仓库意味着任何人知道地址都能拉取镜像,如果你的镜像涉及业务代码、内部配置,一定不要选公开。私有仓库则需要登录才能拉取,适合个人开发和团队内部使用。个人版虽然私有仓库有名额限制,但对大多数场景来说够用,我建议默认选私有,防止某些镜像因为名称太直观被别人抓走。
创建完成后,进入仓库详情页,你会看到系统自动生成的操作指南,里面就是本地 Docker 登录、打 Tag、推送、拉取的命令。不同地域和命名空间拼接出来的地址可能不一样,所以不要背命令,直接用页面里展示的为准。
3.4 设置访问凭证
仓库建好了,但你还不能立刻docker login,因为缺一个用于 Docker 登录的密码。阿里云容器镜像服务的登录密码和你的阿里云控制台登录密码是两码事,它是在"访问凭证"页面单独设置的。
我当年的做法是:左侧菜单找到"访问凭证",然后选择"设置固定密码",输入密码确认后保存。官方也提供"临时密码"选项,临时密码有效期为一段时间,适合临时给没有固定账号的同事用,但我自己用下来还是觉得固定密码省事,不用频繁刷新。密码设置完成后,本地执行docker login registry.cn-hangzhou.aliyuncs.com,用户名填你的阿里云账号登录名,密码填刚才设置的固定密码。
这里最容易搞混的就是用户名。个人版 Docker 登录的用户名不是你随意起的昵称,而是你用来登录阿里云控制台的那个账号名,常见的是手机号或邮箱。你要是不确定,可以打开阿里云控制台右上角的账号头像,查看"账号管理"里显示的登录账号是什么,就把那个值填进去。
3.5 本地登录测试
以上配置完成,就可以在终端里测试登录了。执行:
docker login registry.cn-hangzhou.aliyuncs.com按提示输入用户名和密码,如果看到Login Succeeded,就说明配置通了。我另外补充一点自查技巧:登录成功后,可以查看本机的 Docker 配置文件,确认凭证已经写入。
cat ~/.docker/config.json你会发现里面有一段auths信息,内容是经过 base64 编码的凭证。这个文件很关键,以后每次docker push和docker pull到该域名,Docker 都会自动使用这里面的凭证,不需要每次都重新登录。
如果登录报错,先检查密码是不是访问凭证里的固定密码,而不是控制台登录密码。再检查用户名是不是账号登录名,而不是 AccessKey ID。这两个是大家最常踩的点。
4. 推送与拉取镜像的完整实操
4.1 给本地镜像打 Tag
现在仓库和登录凭证都准备好了,接下来就是最核心的操作:把本地镜像推送上去。在推送之前,先看清本地有哪些镜像:
docker images假设我有一个本地镜像叫my-nginx:v1,现在我要把它变成阿里云仓库地址对应的镜像标签。在 Docker 里,docker tag的命令格式是:
docker tag 源镜像名[:源标签] 目标镜像名[:目标标签]也就是说,执行:
docker tag my-nginx:v1 registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0这句话的意思是:把本地的my-nginx:v1镜像复制一个标签,新标签指向阿里云仓库的v1.0.0版本。执行完后你再运行docker images,会发现镜像列表里多了一条registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app的记录,其 Image ID 和原来的my-nginx:v1一模一样,因为 Tag 本质上只是对同一个镜像的引用别名。
打 Tag 是一个很灵活的操作,同一个本地镜像可以打多个 Tag,分别推送到不同的仓库。比如你构建了一套基础环境镜像,既想推给测试环境用,又想产出一份归档版本,那么可以分别打上:latest和:v1.0.0两个 Tag。很多人忽略 Tag 规范,全部用latest,真的出了线上问题时,想回滚旧版本都找不到具体快照,我的建议是推送时尽量用语义化版本号,比如v1.0.0、v2.1.0,同时保留一个latest指向最新一次成功构建。
4.2 推送镜像到仓库
镜像打好 Tag 之后,执行推送:
docker push registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0推送开始后,终端会显示多层layer already exists或上传进度信息。Docker 镜像的推送本质上是按层传输的,每一层对应 Dockerfile 里的一条构建指令。由于 Docker 有层缓存机制,如果你之前推过同基础镜像的另一个项目,本次推送时大部分公共层会被跳过,只上传新增层,所以整体速度往往比想象中快。
推送完成后,回到阿里云控制台,打开仓库详情页,你会看到该仓库下已经出现了一个 Tagv1.0.0,镜像大小也会有记录。我每次推送完都会去控制台刷新一眼,确认记录生成,防止命令执行完却因为网络问题实际没有上传完全,虽然概率低,但确认一眼更安心。
4.3 换一台机器拉取镜像
仓库里的镜像最终是要被拉取的,现在模拟一下场景。你换了一台全新的服务器,上面已经装好 Docker,想跑刚才推送的镜像,首先还是登录:
docker login registry.cn-hangzhou.aliyuncs.com然后直接拉取:
docker pull registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0拉取完成后,docker run就能直接用了:
docker run -d -p 8080:80 registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0这里有一个关键点:如果仓库类型是私有,那新机器上必须先docker login再docker pull。如果不登录直接拉,会报pull access denied或类似错误。公开仓库则没有这个问题。所以我的习惯是,涉及敏感业务一律私有,登录只在这些机器上配一次,后面就不用反复折腾了。
4.4 删除镜像与仓库管理
用了一段时间后,仓库里的镜像版本会越来越多,控制台端和本地端都需要清理。控制台端删除很简单:在仓库详情页,进入"镜像版本"标签页,可以按 Tag 删除不再使用的镜像版本。这里注意,删掉远端 Tag,并不会影响本地镜像,只有本地执行docker rmi才会把本机缓存删掉。
本地删除命令一般是:
docker rmi registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0如果你的本地镜像被多个 Tag 引用,执行docker rmi只会删除你指定的那个 Tag 标签,真正释放镜像空间只有在该镜像的所有 Tag 都被删除或者容器不再使用时才会发生。这些细节知道后,就不会出现"我明明删了 Tag,磁盘空间怎么没变"的疑问了。
仓库本身的删除在控制台操作,但一旦删除,仓库下所有版本都没了,不可恢复,所以别轻易删仓库,最多删个别 Tag。团队协作时我也建议给仓库设置清晰的命名规则,防止误删。
5. 常见问题与排查技巧实录
5.1 登录失败:unauthorized / 密码错误
登录失败是新手遇到最多的问题,报错形式多种多样,比如unauthorized: incorrect username or password或者error getting credentials - err: exit status 1。原因大概率是下面几种:
- 密码用了阿里云控制台登录密码,而不是访问凭证里的固定密码;
- 用户名填了 AccessKey ID,而不是账号登录名;
- 之前设置过旧密码,重置后本机 Docker 配置里还存着旧凭证。
排查看这里:先确认访问凭证里的固定密码已经设置并且修改正确,再到命令行手动执行一次docker login,注意观察输入用户名时是不是账号登录名。如果之前输入过错误密码,Docker 的凭证缓存会一直尝试使用错误信息,建议先执行docker logout registry.cn-hangzhou.aliyuncs.com,再重新登录。
5.2 push 报 denied: requested access to the resource is denied
这个报错通常是docker push时被拒绝访问,字面意思是"请求访问资源被拒绝"。原因主要有两个:一是你要推送的镜像 Tag 里的命名空间或仓库名拼写不对,和你在控制台创建的不一致;二是你的账号对这个仓库没有写权限,常见于多账号协作时别人创建了私有仓库但你只是旁观者。
排查方法是先检查 Tag 的完整地址,尤其注意命名空间和仓库名是否准确。地址是严格区分大小写的,而且必须全部小写。再确认你登录的就是拥有该命名空间权限的账号。个人版中同一个账号创建的仓库自己肯定能 push,所以更多时候还是地址拼写问题。
5.3 拉取时报 manifest unknown 或 not found
manifest unknown意味着你指定 Tag 对应的镜像在仓库里不存在。常见原因有:
- Tag 名字拼错,比如
v1.0和v1.0.0是两个完全不同的版本; - 远端仓库该版本被人为删除;
- 仓库本身是空的,还没推送过任何镜像。
解决方式很简单:去控制台仓库详情页的"镜像版本"列表里确认现有 Tag 到底叫什么,然后重新执行docker pull。我每次写拉取命令时都会顺手点开控制台看一下,而不是靠记忆敲,这个小习惯帮我避掉了不少低级错误。
5.4 镜像层传输慢或超时
这种问题在 2021 年很典型,大一点的镜像推送或拉取时,中间层可能长时间不更新甚至断线。慢的原因可能有地域节点远、网络波动、镜像体积太大等。我的经验技巧是:
- 优先选择离自己机器最近的阿里云地域创建命名空间和镜像仓库;
- 把 Docker 的镜像源配置好,使用阿里云容器镜像服务的加速器地址,在 Docker 的 daemon.json 里加
registry-mirrors配置,然后重启 Docker。
配置加速器是通用的镜像加速方法,具体值直接写 Docker 的配置文件中即可。如果你操作的机器本身就是阿里云 ECS,还可以进一步走 VPC 网络地址拉取,速度会快很多。不过这一步不是必须的,小镜像或者不频繁传输时体感不明显,传输大镜像时差距才拉开。
5.5 2021版教程和现在控制台界面的差异
老实说,阿里云控制台这几年改版次数不少,现在再打开可能找不到我当年截图里的一模一样入口。但产品逻辑基本没有变:依然是从开通、命名空间、仓库、访问凭证到本地 Docker 登录推送。只是有的入口挪了位置,比如个人版实例名变成了默认实例,有的地方把"创建镜像仓库"的按钮合并到了仓库列表中。
我的建议是:不用死记界面走向,而是抓住"必须依次完成命名空间、仓库、访问凭证"这条主线。界面只是皮,数据模型才是里。遇到找不到入口的时候,优先看控制台左侧菜单里有没有"镜像仓库"和"访问凭证"两个核心入口,然后顺着页面提示走,一样能完成操作。
6. 进阶玩法与个人经验总结
6.1 让推送自动化:在流水线里完成 tag 和 push
既然镜像仓库配好了,别浪费,最实用的进阶场景是把它接入自动化流水线。我在 2021 年搭过一个很简单的流程:代码推送到仓库后,构建机执行固定的脚本,脚本里依次执行构建、打 Tag、推送三个动作。核心命令其实就是前文那些,没有任何神秘之处:
docker build -t registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0 . docker push registry.cn-hangzhou.aliyuncs.com/demo-images/nginx-app:v1.0.0你会看到我把构建标签直接写成远端地址,这样连docker tag都省了。构建机只需要保持登录状态,流水线每次执行完自动把产物镜像推到远端,测试同学直接在测试服务器上docker pull就能拿到最新版本。小团队没有专职运维也能运转得挺顺。
6.2 镜像命名规范和版本管理
长期使用后最重要的体会就是:命名和版本规范一定要提前定好。我目前比较喜欢的规范是:
- 命名空间按团队或项目方向划分,比如
team-backend、devops-tools; - 仓库名按业务模块或组件划分,如
user-service、gateway、base-image; - Tag 用语义化版本号,如
v1.0.0、v1.1.0,同时维护一个latest指向最新稳定版本。
版本号的好处是能给回滚留后路。上线出了问题,直接docker pull上一个 Tag 的镜像,重新部署,一分钟就完成了。如果你整天只有latest,等下次构建覆盖了它,想要旧版就真的找不回来了。
6.3 定期清理和备份习惯
镜像仓库免费额度虽然不是无限,但个人使用基本充裕。真正需要留意的反而是控制台里的镜像版本越堆越多,可能造成管理混乱。我习惯每两周看一眼仓库的镜像版本列表,把已经不再使用或者已经被新版本替换的旧 Tag 清理掉。本地则执行docker image prune清理悬空镜像,释放磁盘空间。
备份方面,重要环境的镜像最好不要只压在远端,偶尔用docker save把最核心的镜像导出一份放到离线存储,形成一个双保险。我见过一个项目因为误删了远端仓库里的旧版本,本地缓存也清掉了,最后只能重新构建,白白花了好几个小时,提前备份几 GB 的镜像成本要低得多。
6.4 最后聊几句个人体会
折腾完这一整套,最大的感受是:托管镜像仓库是一个"低投入、高回报"的基础设施。它的学习成本不高,十几分钟配置好之后,日常使用基本无感,但每一次换机器、加节点、别人找你要镜像,它都在背后帮你减少麻烦。
2021 版教程和现在看,界面早就不一样了,可 Docker 那套镜像、Tag、仓库地址的逻辑没变。你理解了这些底层的概念,以后无论换到哪个云厂商,甚至自己搭一个私有 registry,思路都是通的。如果你还在犹豫要不要配,我建议直接找一块空闲时间,跟着步骤走一遍,把第一个镜像推上去,你就再也不想用 U 盘和 tar 包传镜像了。