为什么选择 Docker
Docker 将应用及其依赖打包成可移植的容器镜像,实现了"一次构建,到处运行"的能力。无论是在开发笔记本、测试服务器还是云主机上,同一个镜像都能提供一致的运行环境,从而彻底消除"在我机器上能跑"的问题。这种环境一致性是后续所有自动化部署与升级的基础。
来源:cloud.tencent.com 与 dataintellect.com 均指出,容器化确保应用在不同环境中行为一致,极大简化了部署与管理。
使用 Docker Compose 编排多容器应用
对于包含多个服务(如 Web 应用、数据库、缓存)的项目,手动启动每个容器既繁琐又容易出错。Docker Compose 允许在一条命令中定义并启动整个应用栈:
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "80:80"
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
restart: unless-stopped
db:
image: postgres:alpine
environment:
- POSTGRES_PASSWORD=password
执行 docker compose up -d 即可后台启动所有服务。通过 docker compose ps 和 docker compose logs -f 可以快速查看状态与日志。
来源:circleci.com 与 cnblogs.com。
自动化版本管理与 CI/CD 集成
在持续交付流水线中,手动为镜像指定版本号容易出错。更好的做法是根据提交信息自动进行语义版本控制(SemVer):
- 默认递增补丁版本(PATCH)。
- 若提交信息包含
bump: minor,则递增次要版本(MINOR)。 - 若包含
bump: major,则递增主版本(MAJOR)。
以下 GitHub Actions 片段展示了如何实现该逻辑:
- name: Determine Version Bump Type
id: determine-bump-type
run: |
echo "level=patch" >> $GITHUB_ENV
if git log -1 --pretty=%B | grep -q 'bump: major'; then
echo "level=major" >> $GITHUB_ENV
elif git log -1 --pretty=%B | grep -q 'bump: minor'; then
echo "level=minor" >> $GITHUB_ENV
随后,版本号可传递给构建步骤,生成形如 ghcr.io/用户名/仓库:1.2.3 的标签。结合 Trivy 等工具进行镜像漏洞扫描,还能在部署前拦截已知安全问题。
来源:dev.to 与 iamraghuveer.com。
蓝绿部署:实现零停机切换
蓝绿部署通过维护两套完全相同的环境来降低风险:一套(蓝)正在生产环境运行,另一套(绿)处于空闲状态。新版本先部署到空闲环境,验证通过后,通过切换流量(例如修改 Nginx 上游配置)将用户引导至新环境。整个过程用户无感知。
在单台 VPS 上,可以利用 Docker 的隔离性与 Nginx 的动态加载实现这一模式:
- 启动新容器,映射到不同的主机端口(如
3001为蓝,3002为绿)。 - 通过健康检查确认新容器就绪。
- 修改 Nginx 配置,将上游从旧端口指向新端口,然后执行
nginx -s reload(重载而非重启,避免断开已有连接)。 - 停止并删除旧容器。
# 启动绿环境
docker compose -p green up -d
# 健康检查
curl -sf http://localhost:3002/health
# 切换 Nginx 上游
sed -i 's/3001/3002/g' /etc/nginx/sites-available/myapp.conf
nginx -s reload
# 清理旧容器
docker compose -p blue down
如果新环境出现问题,回滚同样简单:只需将 Nginx 重新指向旧端口并重载,旧容器仍在运行,可立即恢复服务。
来源:github.com、dev.to 与 readyserver.sg。
数据持久化:Docker 卷的实践
容器是可销毁的,但业务数据不能随容器一同消失。Docker 卷(Volume)提供了一种独立于容器生命周期的持久化存储方案,适合保存数据库文件、用户上传内容、日志等。
- 创建命名卷:
docker volume create myapp-data - 在 Compose 中声明卷:
volumes:
myapp-data:
driver: local
- 挂载到容器:
services:
app:
volumes:
- myapp-data:/app/data
通过 --volumes-from 还可以方便地备份和恢复卷数据:
# 备份卷
docker run --rm --volumes-from myapp -v $(pwd)/backup:/backup ubuntu tar cvf /backup/backup.tar -C /app/data .
# 恢复卷
docker run --rm --volumes-from myapp -v $(pwd)/backup:/backup ubuntu bash -c "cd /app/data && tar xvf /backup/backup.tar --strip 1"
来源:semaphore.io 与 dev.to。
可复用的部署与升级流程
将以上技术整合,即可得到一套完整的实践方案:
- 提交代码:使用语义化提交信息(如
bump: minor)触发 CI/CD 流水线。 - 构建与版本化:流水线自动构建镜像,根据提交信息确定版本号,并推送到容器仓库。
- 蓝绿部署:在目标服务器上,通过脚本自动将新版本部署到空闲环境,执行健康检查,通过后切换流量。
- 数据保障:所有有状态数据通过 Docker 卷持久化,升级前可选的备份步骤确保数据安全。
- 回滚就绪:旧环境在一段时间内保留,一旦新版本异常,可秒级回退。
该流程不仅适用于单机环境,也可扩展到多主机集群(结合 Docker Swarm 或 Kubernetes)。
来源:综合自本文引用材料。
验证与故障排除
部署完成后,应通过以下方式验证:
- 检查容器状态:
docker ps或docker compose ps。 - 查看应用日志:
docker logs <容器名>或docker compose logs -f。 - 直接访问健康端点:
curl http://你的域名/health。 - 确认数据卷挂载正确:
docker inspect <容器名>查看挂载点。
常见问题与解决:
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 镜像构建失败 | Dockerfile 错误或依赖缺失 | 检查构建上下文与基础镜像版本 |
| 容器启动后立即退出 | 环境变量未配置或端口冲突 | 使用 docker logs 查看错误信息,核对 .env 文件 |
| 蓝绿切换后无法访问 | Nginx 配置未重载或上游端口错误 | 执行 nginx -t 测试配置,手动 nginx -s reload |
| 数据未持久化 | 卷未正确挂载或使用了匿名卷 | 确认 docker-compose.yml 中卷定义与挂载路径 |
注意事项
- 资源限制:在单机运行两套环境时,确保主机有足够的 CPU 和内存。可以通过
--memory和--cpu-shares限制容器资源。 - 数据库迁移:蓝绿部署期间,新旧版本可能同时访问数据库。建议采用"扩展-收缩"模式:先添加新字段,保持旧字段,待新版本稳定后再删除旧字段。
- 镜像安全:始终使用官方或受信任的基础镜像,并定期进行漏洞扫描(如 Trivy)。
- 网络隔离:生产环境应使用自定义桥接网络,并限制容器间的不必要通信。
通过本文介绍的方法,你可以在不需要复杂编排工具的前提下,构建一套可靠、可复用的 Docker 部署与升级流程。从环境一致性到零停机切换,每一步都有具体的命令和配置可以参照。现在就可以在测试环境中实践,并根据实际需求调整细节。