// Created At 2026-03-16

GitHub Actions + Docker 极简自动化部署教程(入门篇)

📚 系列导航

本系列共四篇,覆盖从静态网站到生产级 Docker 部署的全流程,建议按顺序阅读:

  1. 静态网站自动化部署(静态篇)
    —— 纯前端资源的自动化发布,Caddy 自动 HTTPS 和 SPA 路由支持。
  2. 动态网站自动化部署(动态篇)
    —— 后端服务进程管理、环境变量注入、数据库迁移,结合 Caddy 反向代理。
  3. Docker 极简入门(入门篇) —— 从零开始用 Docker + GitHub Actions 实现 CI/CD 流水线。
  4. Docker 生产级部署(进阶篇)
    —— 多容器编排、健康检查、数据库迁移、自动 HTTPS,打造可靠的生产环境。

本教程将带你用最简洁的方式,将你的应用打包成 Docker 镜像,并通过 GitHub Actions 自动部署到服务器。你将学会:

  • 编写一个极简的 Dockerfile
  • 使用 docker-compose 管理应用 + 数据库
  • 配置 GitHub Actions 实现 CI/CD
  • 处理环境变量、端口冲突等常见问题

适用人群:对 Docker 有基础了解,想快速搭建自动化部署的新手。


🚀 最终效果

本地 git push → 自动构建镜像 → 推送到阿里云 ACR → 服务器自动拉取 → 服务重启 → 网站更新。


📌 版本声明

本文档所有工具均采用 2026 年最新稳定版,具体版本如下:

工具版本说明
Node.js24.x最新的主要版本,支持所有现代 JavaScript 特性
pnpm10.x高性能包管理器,与 Node.js 24 完美兼容
Docker Engine29.x容器运行时,支持 BuildKit 和多阶段构建
Docker Composev5全新的 Compose 规范,支持 name 项目和 depends_on 条件
Caddy2.8+自动 HTTPS 的反向代理服务器
PostgreSQL17 (alpine)轻量级关系型数据库,alpine 版本镜像小巧
Drizzle ORM0.30+TypeScript 原生 ORM,支持迁移和类型安全查询
PM25+生产级 Node.js 进程管理工具
GitHub Actions最新CI/CD 平台,所有 Action 均为当前最新版本(如 checkout@v4ssh-action@v1.0.0 等)
阿里云 ACR容器镜像服务,需使用固定密码进行认证

注意:请根据你的项目实际需求调整具体版本号。若使用其他技术栈(如 Python、Java 等),请替换对应的运行时版本。


📦 第一步:准备项目

1.1 项目结构

my-app/
├── .github/workflows/deploy.yml   # GitHub Actions 配置
├── Dockerfile                     # 镜像构建文件
├── docker-compose.yml             # 容器编排文件
├── .env.example                   # 环境变量示例(用于参考,不提交)
└── ... 你的应用代码

1.2 编写 Dockerfile(以 Node.js 为例)

# 构建阶段
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build   # 根据项目调整构建命令,如 npm run generate

# 运行阶段
FROM node:24-alpine
WORKDIR /app
# 根据你的构建输出目录调整以下路径(常见:dist/、.output/、build/)
COPY --from=builder /app/.output ./.output   # 若使用 Nuxt
# 若使用其他框架,请替换为对应的输出目录,例如:
# COPY --from=builder /app/dist ./dist
EXPOSE 3000
# 启动命令也需对应调整,例如:
CMD ["node", ".output/server/index.mjs"]
# 若使用 Express,可能是 CMD ["node", "server.js"]

提示:请根据你的项目框架调整构建输出目录和启动命令。


🛠️ 第二步:编写 docker-compose.yml

创建一个包含应用和数据库的极简编排文件。注意:首次部署前,请确保服务器上已创建 .env 文件(见第五步)。

name: my-app

services:
  postgres:
    image: postgres:alpine
    container_name: my-app-db
    restart: always
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    image: ${ACR_REGISTRY}/my-app:latest
    container_name: my-app
    restart: always
    ports:
      - "3000:3000"          # 直接暴露端口(生产建议用反向代理)
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:

🔐 第三步:配置 GitHub Secrets

在 GitHub 仓库 Settings → Secrets and variables → Actions 中添加:

Secret 名称说明
ACR_REGISTRY阿里云镜像仓库地址(例如 crpi-xxx.cn-beijing.personal.cr.aliyuncs.com不要带 https://
ACR_USERNAME阿里云账号(邮箱)
ACR_PASSWORD阿里云容器镜像服务固定密码
SERVER_HOST服务器公网 IP
SERVER_USERSSH 用户名(如 rootubuntu
SSH_PRIVATE_KEY服务器的 SSH 私钥(包含 BEGINEND 行,保持完整换行)
POSTGRES_DB数据库名
POSTGRES_USER数据库用户
POSTGRES_PASSWORD数据库密码

⚠️ 重要.env 文件包含敏感信息,切勿提交到 Git(已包含在 .gitignore 中)。首次部署前需手动在服务器上创建(见第五步)。


⚙️ 第四步:创建 GitHub Actions 工作流

.github/workflows/deploy.yml 中写入:

name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Login to ACR
        uses: docker/login-action@v3
        with:
          registry: ${{ secrets.ACR_REGISTRY }}
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ${{ secrets.ACR_REGISTRY }}/my-app:latest

      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        env:
          ACR_REGISTRY: ${{ secrets.ACR_REGISTRY }}
          ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
          ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
          POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
          POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
          POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          envs: ACR_REGISTRY, ACR_USERNAME, ACR_PASSWORD, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD
          script: |
            cd /var/www/my-app
            # 写入环境变量(注意不要用单引号,否则变量不会展开)
            cat > .env << EOF
            POSTGRES_DB=$POSTGRES_DB
            POSTGRES_USER=$POSTGRES_USER
            POSTGRES_PASSWORD=$POSTGRES_PASSWORD
            ACR_REGISTRY=$ACR_REGISTRY
            EOF
            # 登录 ACR
            echo "$ACR_PASSWORD" | docker login "$ACR_REGISTRY" -u "$ACR_USERNAME" --password-stdin
            # 拉取新镜像
            docker compose pull app
            # 重启应用(若端口冲突可添加 --force-recreate)
            docker compose up -d --no-deps --force-recreate app

说明:使用 --force-recreate 确保旧容器被强制重新创建,避免端口冲突。若你的应用需要数据库迁移,请在重启前添加迁移命令,例如 docker compose exec app npm run migrate


🖥️ 第五步:服务器初始化(只需一次)

5.1 安装 Docker 并配置镜像加速器

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 重新登录或执行 newgrp docker 使组生效
# 配置阿里云镜像加速器(替换为你的加速地址)
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://your-mirror-id.mirror.aliyuncs.com"]
}
EOF
sudo systemctl restart docker

5.2 创建项目目录并准备环境

mkdir -p /var/www/my-app
cd /var/www/my-app

5.3 首次部署:创建 .env 文件并启动服务

由于首次部署时 GitHub Actions 还未运行,需要手动创建 .env 文件并启动一次服务,以便数据库初始化。

# 根据你的实际值创建 .env 文件
cat > .env << EOF
POSTGRES_DB=myapp
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your-strong-password
ACR_REGISTRY=crpi-xxx.cn-beijing.personal.cr.aliyuncs.com
EOF

然后将本地的 docker-compose.yml 上传到该目录(例如使用 scp):

# 在本地执行
scp docker-compose.yml root@your-server:/var/www/my-app/

首次启动:

docker compose up -d

注意:首次启动后,PostgreSQL 会创建数据卷,后续部署时数据不会丢失。

5.4 测试服务

curl http://localhost:3000
# 或通过浏览器访问 http://你的服务器IP:3000

🔍 第六步:常见问题极简排查

现象可能原因解决
Actions 中 SSH 连接失败私钥格式错误 / 端口未开放检查私钥是否包含完整换行;确保安全组开放 22 端口
容器启动失败,端口占用宿主机有其他进程占用了 3000 端口sudo lsof -i :3000 找到进程并停止
应用无法连接数据库环境变量 DATABASE_URL 错误检查 .env 文件中的连接串,确认主机名为 postgres(服务名)
镜像拉取慢 / 超时未配置镜像加速器按 5.1 配置阿里云加速器并重启 Docker
容器重启后数据丢失数据库卷未正确挂载确认 docker-compose.yml 中有 volumes 定义,且数据卷存在
首次部署时数据库未初始化.env 文件缺失或变量错误按 5.3 手动创建 .env 并再次执行 docker compose up -d
应用更新后未生效镜像未拉取或容器未重启检查 Actions 日志;手动执行 docker compose pull && docker compose up -d

📖 附录:常用运维命令

# 查看所有容器状态
docker compose ps

# 查看应用日志
docker compose logs -f app

# 进入应用容器内部调试
docker exec -it my-app sh

# 停止所有服务
docker compose down

# 重新构建并启动(如需重新构建镜像)
docker compose up -d --build

🎉 完成

现在你已经拥有了一套极简但可工作的自动化部署流水线。每次推送代码到 main 分支,都会自动构建镜像、推送到阿里云、并在服务器上重启应用。

接下来可以探索:添加健康检查、使用 Caddy 反向代理(避免直接暴露端口)、多环境配置、数据库迁移自动化等进阶功能(参见本系列进阶篇)。

如果这篇文档对你有帮助,可以请我喝杯咖啡 ☕️
Ali PayWechat Pay
评论区
© 2026 MOONGATE