私の

记录一下给 Nuxt 博客的部署方案。

核心思路

构建和运行分离

  • Builder 服务:拉代码、装依赖、打包,完事就退出
  • App 服务:跑应用,监听构建完成后自动重启

Docker Compose 配置

YAML
services:
  builder:
    build:
      dockerfile: Dockerfile.builder
    volumes:
      - workspace:/workspace
      - shared:/shared
    restart: "no"
    profiles: [build] # 手动触发,不自动启动

  app:
    build:
      dockerfile: Dockerfile.app
    volumes:
      - workspace:/workspace
      - shared:/shared
    ports:
      - "80:3000"
    restart: unless-stopped # 开机自启

volumes:
  workspace: # 存代码和产物
  shared: # 进程间通信

关键:

  • profiles: [build] 让构建服务手动触发
  • 用 named volume 不用 bind mount,避免权限问题
  • 两个容器通过 shared volume 里的标记文件通信

构建服务

build.sh,拉代码、装依赖、打包,最后写标记文件:

Bash
#!/bin/sh
REPO_URL="https://gitee.com/mHaoza/sprout.git"
PROJECT_DIR="/workspace/sprout"
MARKER_FILE="/shared/.build-complete"

rm -f "$MARKER_FILE"

if [ -d "$PROJECT_DIR" ]; then
    cd "$PROJECT_DIR"
    git reset --hard origin/main    # 强制以远程为准
    git pull origin main
else
    git clone "$REPO_URL" "$PROJECT_DIR"
    cd "$PROJECT_DIR"
fi

pnpm install --frozen-lockfile --registry=https://registry.npmmirror.com
pnpm build

echo "$(date -Iseconds)" > "$MARKER_FILE"    # 通知 app 重启

运行服务

inotify-tools 监听标记文件,检测到新构建就重启:

Bash
#!/bin/sh
MARKER_FILE="/shared/.build-complete"

start_app() {
    cd /workspace/sprout
    NODE_ENV=production node .output/server/index.mjs &
    APP_PID=$!
}

start_app

while true; do
    inotifywait -e create,modify -t 10 /shared 2>/dev/null || true

    if [ -f "$MARKER_FILE" ]; then
        kill "$APP_PID" && wait "$APP_PID" 2>/dev/null || true
        start_app
        rm -f "$MARKER_FILE"
    fi

    sleep 2
done

使用

Bash
# 首次启动
docker compose up -d app

# 触发构建
docker compose run --rm builder

# 看日志
docker compose logs -f app

构建完成后 app 会自动重启。