第一次把应用装进 Docker 容器时,大多数人会先遇到三件事:镜像拉不下来,开发机和线上环境对不上,发版还得登录服务器手动操作。阿里云的几项服务正好能把这些环节接起来。下面以一个小团队的迁移过程为例,说明 Docker 和阿里云各自负责什么,以及具体怎么配。文中的时间、体积和费用均为示意数据,实际操作前请以阿里云官网的最新说明为准。

迁移前的情况
这个团队有一个用 Node.js 写的订单查询服务,依赖 Redis 和 MySQL。以前的做法是直接在一台 ECS 上装运行环境,每次发版都要手动拷文件、重启进程。开发机是 macOS,服务器是 Ubuntu,两边的依赖版本经常对不上,已经因此出过两次”本地正常、线上报错”。他们决定把服务容器化,镜像统一放到阿里云上。
第一步:在 ECS 上装 Docker,并配置镜像加速
实例选的是 Ubuntu 22.04,Docker Engine 按官方文档安装。国内直接连 Docker Hub 比较慢,拉 node:18-alpine 这类基础镜像经常超时。阿里云容器镜像服务会给每个账号分配一个专属的加速器地址,把它写进 /etc/docker/daemon.json 的 registry-mirrors 字段,重启 Docker 之后,同一条 pull 命令的耗时从几分钟降到十几秒。注意地址要用自己账号控制台里的,不要直接抄别人的配置。
第二步:Dockerfile 用多阶段构建
编译需要 devDependencies,运行时只需要生产依赖。所以第一阶段负责编译,第二阶段只复制编译产物和生产依赖。这一改,镜像从约 1.2 GB 缩到约 180 MB。体积小了,推送和拉取都快,运行环境里多余的系统工具也少,暴露面跟着变小。
第三步:推送到容器镜像服务
在控制台里建了命名空间 shop-team 和镜像仓库 order-service。CI 用的是单独创建的 RAM 子账号,只给这个命名空间的推送权限,不使用主账号的 AccessKey。这一点很容易被忽略:CI 凭证如果拿到了主账号权限,一旦泄露,影响范围会大得多。
镜像标签格式是”地域域名/命名空间/仓库名:版本号”。版本号由日期加 Git 提交的短哈希组成,回滚时能精确找到某一次构建。团队没有把 latest 当成唯一标签,原因很直接:线上出问题时,latest 说明不了运行的是哪一版代码。
第四步:在 ECS 上拉取并运行
ECS 和镜像仓库在同一地域时,拉取地址用 VPC 内网域名。流量不走公网,速度更快,也不产生公网流量费用。应用、Redis、MySQL 三个容器用 docker compose 编排,走同一个自定义网络互相访问。数据库密码从环境变量文件读取,不写进镜像。
第五步:流量上来之后换方案
三个月后,促销期间的订单量达到平时的六倍,单台 ECS 扛不住了。团队比较过两条路:在 ECS 上手动加实例再接负载均衡,或者迁到容器服务 ACK(Kubernetes 版)。最后选了 ACK,主要有三个理由:
- 滚动更新和失败回滚由系统自动完成,不用再逐台手动重启;
- 副本数可以按 CPU 使用率自动伸缩,活动结束后自动缩回;
- 健康检查失败的容器会被自动替换,不用人工逐个处理。
Kubernetes 的学习成本不低。如果服务只跑在一两台机器上,流量也比较稳,Docker Compose 加 ECS 完全够用,没必要急着迁。
第六步:每晚的对账脚本交给弹性容器实例
还有一个每晚运行的对账脚本,每次只跑十几分钟。为它长期留一台服务器很浪费。团队改用弹性容器实例 ECI,任务开始时创建容器,结束后释放,只按实际运行时长计费。镜像仍从容器镜像服务拉取,构建和发布流程和其他服务保持一致。
几个容易踩的安全问题
- AccessKey 和数据库密码不能写进 Dockerfile 或镜像层。即使后面删掉,历史层里仍然可以查到;
- 仓库里的旧版本镜像要定期清理,生产环境的拉取权限设为只读;
- RAM 权限按最小权限原则分配,避免一个凭证能操作所有资源;
- 基础镜像要定期更新,并开启镜像漏洞扫描。
回头看这套流程
Docker 让开发机和线上跑的是同一份环境。镜像加速和容器镜像服务解决了拉取慢、镜像存放乱的问题,ACK 和 ECI 则管的是规模上来以后的运行。这个团队先用一台 ECS 加 Docker Compose 起步,流量和团队规模涨起来以后,再按需引入 Kubernetes 和弹性实例。不必一步到位。