如何在AWS CodeDeploy中用docker-compose部署最新代码且避免停机
问题描述
我通过AWS CodeDeploy结合docker-compose部署了一个Next.js应用,搭建了预发布环境:开发者向staging分支推送新提交时,会自动触发CodeDeploy创建新部署。
应用的appspec.yml配置了启动脚本,执行docker-compose build和docker-compose up命令。我原本以为构建时加--no-cache就能基于最新代码启动容器,但部署成功后,应用并未体现代码变更。
请问如何确保每次部署都基于最新代码创建容器,同时避免服务停机?
当前配置文件
appspec.yml
version: 0.0 os: linux files: - source: . destination: /home/ec2-user/app/ hooks: ApplicationStart: - location: scripts/run.sh timeout: 300 runas: root
run.sh
#!/bin/bash cd /home/ec2-user/app docker-compose build --no-cache docker-compose up -d
解决办法
1. 确认代码同步是否到位
CodeDeploy的文件复制环节可能出现缓存或遗漏,先验证目标服务器的代码是否为最新版本:
- 登录EC2实例,进入
/home/ec2-user/app目录,执行git log查看提交记录,和staging分支的最新提交ID对比 - 如果代码未更新,检查CodeDeploy部署日志,确认文件复制阶段是否报错,或者是否配置了
ignore_files规则排除了关键代码文件
2. 优化Docker构建逻辑,强制更新所有层级
即便加了--no-cache,Dockerfile的分层设计也可能导致旧层被复用,可做以下调整:
- 构建时添加
--pull参数,拉取基础镜像的最新版本:docker-compose build --no-cache --pull - 检查Dockerfile,确保先复制
package.json/package-lock.json安装依赖,再复制其余代码;如果依赖有更新,确保构建过程中重新执行了npm install - 确认Next.js的静态构建产物(
/.next目录)在构建时正确复制到容器内,且构建命令npm run build已执行
3. 实现滚动更新,避免服务停机
当前docker-compose up -d会直接重启容器,可能导致短暂停机。可以通过以下方式实现零停机部署:
- 在
docker-compose.yml中为服务配置deploy.replicas: 2(需启用Docker Swarm模式,或使用docker-compose up --scale动态调整副本数) - 修改启动脚本,先启动新容器,等健康检查通过后再停止旧容器:
#!/bin/bash cd /home/ec2-user/app # 给新镜像打唯一标签(用时间戳确保唯一性) TAG=$(date +%Y%m%d%H%M%S) docker-compose build --no-cache --pull docker tag your-app-image:latest your-app-image:$TAG # 启动新容器,临时扩容副本数 docker-compose up -d --scale your-app-service=2 # 等待新容器完成启动和健康检查 sleep 30 # 定位并停止旧容器 OLD_CONTAINER_ID=$(docker ps -q -f "name=your-app-service" -f "label=com.docker.compose.image=your-app-image:latest" | head -n 1) if [ ! -z "$OLD_CONTAINER_ID" ]; then docker stop $OLD_CONTAINER_ID docker rm $OLD_CONTAINER_ID fi # 可选:将副本数调回1 docker-compose up -d --scale your-app-service=1
4. 清理旧资源,避免误启动旧镜像
部署后清理未使用的容器和镜像,防止磁盘占用过高,同时避免意外启动旧版本:
# 清理已退出的容器 docker container prune -f # 清理未被使用的镜像 docker image prune -a -f
5. 完善CodeDeploy生命周期钩子
添加BeforeInstall钩子,在部署前清理目标目录的旧代码,避免残留文件干扰:
修改appspec.yml:
hooks: BeforeInstall: - location: scripts/cleanup.sh timeout: 60 runas: root ApplicationStart: - location: scripts/run.sh timeout: 300 runas: root
cleanup.sh内容:
#!/bin/bash rm -rf /home/ec2-user/app/*
内容的提问来源于stack exchange,提问作者everspader
相关产品推荐
相关产品推荐

