Jenkins+Docker+Nginx部署前端项目

在 Linux 上使用 Jenkins(Docker 安装)部署 Nuxt 前端项目完整指南

适用场景:Jenkins 以 Docker 容器运行 → 从 GitLab 拉取 Nuxt 源码 → 构建 Docker 镜像 → 通过 SSH 推送到目标服务器 → 容器化部署 并对外提供服务。

文档里的 IP(192.168.1.10/20)、域名、仓库地址都是占位示例,落地时替换成你的实际值即可


目录

  1. 整体架构
  2. 环境与版本准备
  3. 用 Docker 安装 Jenkins
  4. Jenkins 初始化
  5. 安装必要插件
  6. 配置凭据(GitLab / SSH / Registry)
  7. Nuxt 项目容器化改造
  8. 创建 Jenkins Pipeline
  9. 目标服务器部署与 Nginx
  10. GitLab Webhook 自动触发
  11. 验证、回滚与排错

1. 整体架构

flowchart LR
    Dev[开发者] -->|git push| GitLab[(GitLab 仓库)]
    GitLab -->|Webhook 触发| Jenkins[Jenkins 容器]
    Jenkins -->|1. 拉取源码| GitLab
    Jenkins -->|2. docker build| Image[Nuxt 镜像]
    Image -->|3. docker push| Registry[(镜像仓库 Harbor)]
    Jenkins -->|4. SSH 远程命令| Server[目标服务器]
    Server -->|5. docker pull & run| App[Nuxt 容器:3000]
    App -->|6. 反向代理| Nginx[Nginx :80/443]
    Nginx --> User[用户浏览器]

流程说明:

步骤 动作 执行方
开发者提交代码到 GitLab 开发者
GitLab Webhook 通知 Jenkins GitLab
Jenkins 拉取代码,docker build 构建镜像 Jenkins 容器
docker push 推送镜像到 Registry Jenkins
Jenkins 通过 SSH 登录目标服务器执行部署脚本 Jenkins → 服务器
服务器 docker pull 拉取镜像并启动容器,Nginx 反代 目标服务器

🖼️ 配图建议:将上方 Mermaid 流程图渲染后截图保存为 images/architecture.png,用 [图片上传失败...(image-af0c44-1781505015670)] 引用;或直接在支持 Mermaid 的 Markdown 阅读器中查看。


2. 环境与版本准备

2.1 服务器规划

角色 IP 示例 说明 最低配置
Jenkins 服务器 192.168.1.10 运行 Jenkins 容器、构建镜像 2C4G,磁盘 ≥ 40G
目标部署服务器 192.168.1.20 运行 Nuxt 容器对外服务 2C2G,磁盘 ≥ 20G
镜像仓库 Registry registry.example.com Harbor / 私服 / Docker Hub 2C4G,磁盘 ≥ 100G

💡 小型项目可三合一,但生产环境建议分离,避免构建占用影响线上服务。

2.2 软件版本(本文示例)

软件 版本
操作系统 Ubuntu 22.04 / CentOS 7+
Docker 24.x+
Jenkins LTS(jenkins/jenkins:lts-jdk17
Node(镜像内构建用) 20.x(Nuxt 3 推荐)
Nuxt 3.x

2.3 安装 Docker(三台服务器都要装)

# Ubuntu
curl -fsSL https://get.docker.com | bash -s docker
sudo systemctl enable --now docker

# 验证
docker --version          # Docker version 24.x
docker compose version
# CentOS
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker

2.4 配置镜像加速(国内推荐)

sudo mkdir -p /etc/docker
cat <<'EOF' | sudo tee /etc/docker/daemon.json
{
  "registry-mirrors": ["https://docker.m.daocloud.io"],
  "insecure-registries": ["registry.example.com"]
}
EOF
# 重新加载 systemd 配置
sudo systemctl daemon-reload

# 重启 Docker 服务
sudo systemctl restart docker

# 验证配置是否生效
docker info | grep -A 5 "Registry Mirrors"

insecure-registries 仅在私服未配 HTTPS 时使用;生产环境请为 Registry 配置 TLS 证书。


3. 用 Docker 安装 Jenkins

由于 Jenkins 需要在流水线中执行 docker build / docker push,本文采用 DooD(Docker outside of Docker) 方案:把宿主机的 Docker socket 挂载进 Jenkins 容器,让容器复用宿主机的 Docker 引擎。

3.1 拉取镜像

docker pull jenkins/jenkins:lts-jdk17

3.2 创建数据目录

mkdir -p /data/jenkins_home
# Jenkins 容器内默认 UID 为 1000
chown -R 1000:1000 /data/jenkins_home

3.3 启动 Jenkins 容器(DooD 方式)

docker run -d \
  --name jenkins \
  --restart=always \
  -p 8080:8080 \
  -p 50000:50000 \
  -v /data/jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /usr/bin/docker:/usr/bin/docker \
  -v /etc/localtime:/etc/localtime:ro \
  -e TZ=Asia/Shanghai \
  --group-add $(getent group docker | cut -d: -f3) \
  jenkins/jenkins:lts-jdk17

参数详解:

参数 说明
-p 8080:8080 Jenkins Web UI 端口
-p 50000:50000 Agent 节点通信端口
-v /data/jenkins_home:/var/jenkins_home 数据持久化,务必挂载,否则重启数据丢失
-v /var/run/docker.sock DooD 核心:容器内调用宿主机 Docker 引擎
-v /usr/bin/docker 把宿主机 docker 客户端注入容器
--group-add ... 把宿主机 docker 组 GID 加给容器用户,解决 socket 权限
-e TZ=Asia/Shanghai 时区东八区,构建时间戳正确

3.4 验证容器内可用 docker

docker exec jenkins docker version
# 能输出 Server 版本即说明 DooD 打通

⚠️ 若提示 permission denied,临时修复:docker exec -u root jenkins chmod 666 /var/run/docker.sock。更稳妥的方式是 3.3 步的 --group-add

3.5 查看状态与日志

docker ps | grep jenkins
docker logs -f jenkins

✅ 出现 Jenkins is fully up and running 即启动成功。


4. Jenkins 初始化

4.1 访问 Web UI

浏览器打开:http://192.168.1.10:8080

📸 首次访问出现「Unlock Jenkins / 解锁 Jenkins」页面。

4.2 获取初始密码

docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

复制输出粘贴到解锁框。

4.3 安装推荐插件

┌─────────────────────────────────────────┐
│  自定义 Jenkins                          │
│  ┌─────────────┐    ┌─────────────┐     │
│  │ 安装推荐插件 │    │ 选择插件安装 │     │
│  │  (推荐) ✅   │    │             │     │
│  └─────────────┘    └─────────────┘     │
└─────────────────────────────────────────┘

选择 「Install suggested plugins」,等待 5-10 分钟。

4.4 创建管理员

  • 用户名:admin
  • 密码:至少 8 位
  • 邮箱:admin@example.com

完成后进入主界面。


5. 安装必要插件

Manage Jenkins → Plugins → Available plugins,搜索安装:

插件 用途
GitLab Plugin 与 GitLab 集成、接收 Webhook
Git Parameter 支持选择分支构建
SSH Agent Pipeline 中通过 sshagent 执行远程命令
Docker Pipeline Pipeline 中使用 docker.build / docker.image
Pipeline 流水线核心(推荐插件已含)
Credentials Binding 把凭据注入为环境变量
Locale 中文界面(可选)
Blue Ocean 可视化流水线(可选)
Workspace Cleanup 调用cleanWs()清理 Jenkins 工作区文件

安装步骤:勾选 → Install → 勾选「Restart Jenkins when installation is complete」→ 等待重启。

💡 本文镜像构建在 Pipeline 中直接调用宿主机 docker 命令完成,Docker Pipeline 插件非强制,但它能让 docker.build() 语法更简洁。


6. 配置凭据

本流程需要 3 类凭据:GitLab 拉代码(SSH)目标服务器登录(SSH)镜像仓库登录(用户名密码)

6.1 在 Jenkins 容器中生成 SSH 密钥

docker exec -it jenkins bash
ssh-keygen -t ed25519 -C "jenkins@example.com" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub   # 公钥:用于 GitLab + 目标服务器
cat ~/.ssh/id_ed25519       # 私钥:用于 Jenkins 凭据
exit

这一对密钥同时用于「拉 GitLab 代码」和「SSH 登录目标服务器」,公钥分别添加到两边即可。

6.2 GitLab 添加公钥

  1. 复制 id_ed25519.pub
  2. GitLab → 头像 → Preferences → SSH Keys → 粘贴 → Add key

测试:

docker exec -it jenkins ssh -T git@gitlab.example.com
# 输出 Welcome to GitLab, @xxx! 即成功

若报 Host key verification failed

docker exec -it jenkins bash -c "ssh-keyscan -H gitlab.example.com >> ~/.ssh/known_hosts"

6.3 目标服务器添加公钥

# 在目标服务器(192.168.1.20)以 deploy 用户操作
useradd -m -s /bin/bash deploy
usermod -aG docker deploy          # 让 deploy 能执行 docker
mkdir -p /home/deploy/.ssh && chmod 700 /home/deploy/.ssh
echo "ssh-ed25519 AAAA... jenkins@example.com" >> /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

测试:

docker exec -it jenkins ssh -o StrictHostKeyChecking=no deploy@192.168.1.20 "docker version"

6.4 在 Jenkins 中登记凭据

Manage Jenkins → Credentials → System → Global credentials → Add Credentials

① GitLab / 服务器共用 SSH 私钥

┌─────────────────────────────────────────────────┐
│  Kind:        SSH Username with private key      │
│  ID:          deploy-ssh-key                     │
│  Username:    git        (拉代码用 git 用户)      │
│  Private Key: ● Enter directly → 粘贴 id_ed25519 │
└─────────────────────────────────────────────────┘

Username 填 git 用于拉 GitLab;SSH 登录服务器时在 Pipeline 里显式写 deploy@host 覆盖即可,密钥通用。

② 镜像仓库账号密码

┌─────────────────────────────────────────────────┐
│  Kind:        Username with password             │
│  ID:          registry-cred                      │
│  Username:    admin                              │
│  Password:    ********                           │
│  Description: Harbor 镜像仓库账号                 │
└─────────────────────────────────────────────────┘
凭据 ID 类型 用途
deploy-ssh-key SSH 私钥 拉 GitLab 代码 + SSH 登录目标服务器
registry-cred 用户名密码 docker login 推送镜像

🔐 凭据一律走 Credentials 管理,严禁在 Jenkinsfile 中明文写密码


7. Nuxt 项目容器化改造

在 Nuxt 项目根目录新增以下文件并提交到 GitLab。

7.1 Dockerfile(多阶段构建)

多阶段构建:第一阶段装依赖并 nuxt build,第二阶段只拷贝 .output,最终镜像精简、无源码与 devDependencies。

# ---------- 构建阶段 ----------
FROM node:20-alpine AS builder
WORKDIR /app

# 国内加速(可选)
RUN npm config set registry https://registry.npmmirror.com

# 先拷贝依赖清单,利用 Docker 层缓存
COPY package.json pnpm-lock.yaml* package-lock.json* ./
RUN corepack enable && \
    (pnpm install --frozen-lockfile || npm ci)

# 拷贝源码并构建
COPY . .
RUN (pnpm run build || npm run build)

# ---------- 运行阶段 ----------
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production \
    NITRO_HOST=0.0.0.0 \
    NITRO_PORT=3000

# 仅拷贝构建产物
COPY --from=builder /app/.output ./.output

EXPOSE 3000

# Nuxt 3 / Nitro 的 node 服务入口
CMD ["node", ".output/server/index.mjs"]

7.2 .dockerignore

避免把 node_modules.git 等塞进构建上下文,显著加快 docker build

node_modules
.nuxt
.output
.git
.gitignore
Dockerfile
.dockerignore
*.log
.env
.env.*
README.md

7.3 本地验证镜像(强烈建议先跑通)

# 在项目根目录
docker build -t nuxt-frontend:test .
docker run -d --name nuxt-test -p 3000:3000 nuxt-frontend:test

# 验证
curl -I http://localhost:3000        # 期望 HTTP 200
docker logs nuxt-test

# 清理
docker rm -f nuxt-test

⚠️ Nuxt 3 服务端必须监听 0.0.0.0 才能从容器外访问,已通过 NITRO_HOST=0.0.0.0 环境变量保证;切勿用默认的 localhost

7.4 镜像标签规范

建议用 构建号 + commit 短 SHA 做标签,便于追溯与回滚:

registry.example.com/web/nuxt-frontend:42-a1b2c3d
registry.example.com/web/nuxt-frontend:latest
标签 含义
42-a1b2c3d 第 42 次构建 + 代码 commit,唯一且可追溯
latest 始终指向最新成功构建,方便手动拉取

8. 创建 Jenkins Pipeline

8.1 新建任务

主界面 → New Item → 名称 nuxt-docker-deploy → 选 Pipeline → OK。

┌─────────────────────────────────────────────────┐
│  Enter an item name:  nuxt-docker-deploy         │
│  ○ Freestyle project                             │
│  ● Pipeline    ✅                                 │
│  ○ Multibranch Pipeline                          │
│                  [OK]                            │
└─────────────────────────────────────────────────┘

8.2 General / 触发器

☑ Discard old builds → keep 20 builds
☑ This project is parameterized
   └ Git Parameter: Name=BRANCH, Type=Branch, Default=origin/main
☑ Build when a change is pushed to GitLab
   └ 记下 webhook URL 并点 Generate 生成 Secret Token(第 10 章用)

8.3 Pipeline 来源(推荐 SCM 管理)

Definition:      Pipeline script from SCM
SCM:             Git
Repository URL:  git@gitlab.example.com:web/nuxt.git
Credentials:     git (deploy-ssh-key)
Branch:          */${BRANCH}
Script Path:     Jenkinsfile

Save

8.4 完整 Jenkinsfile(提交到项目根目录)

pipeline {
    agent any

    environment {
        // ===== 镜像仓库 =====
        REGISTRY      = 'registry.example.com'
        IMAGE_REPO    = 'web/nuxt-frontend'
        IMAGE_NAME    = "${REGISTRY}/${IMAGE_REPO}"
        IMAGE_TAG     = "${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"
        // ===== 目标服务器 =====
        REMOTE_HOST   = '192.168.1.20'
        REMOTE_USER   = 'deploy'
        APP_NAME      = 'nuxt-frontend'
        APP_PORT      = '3000'
        // ===== 凭据 ID =====
        SSH_CRED      = 'deploy-ssh-key'
        REGISTRY_CRED = 'registry-cred'
    }

    options {
        timestamps()
        timeout(time: 30, unit: 'MINUTES')
        disableConcurrentBuilds()
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    stages {

        stage('🔍 检出代码') {
            steps {
                checkout scm
                sh 'git log -1 --pretty=format:"%h - %an : %s"'
            }
        }

        stage('🏗️ 构建镜像') {
            steps {
                echo "构建镜像 ${IMAGE_NAME}:${IMAGE_TAG}"
                sh '''
                    docker build \
                      -t ${IMAGE_NAME}:${IMAGE_TAG} \
                      -t ${IMAGE_NAME}:latest .
                '''
            }
        }

        stage('📤 推送镜像') {
            steps {
                withCredentials([usernamePassword(
                    credentialsId: env.REGISTRY_CRED,
                    usernameVariable: 'REG_USER',
                    passwordVariable: 'REG_PASS')]) {
                    sh '''
                        echo "$REG_PASS" | docker login ${REGISTRY} -u "$REG_USER" --password-stdin
                        docker push ${IMAGE_NAME}:${IMAGE_TAG}
                        docker push ${IMAGE_NAME}:latest
                        docker logout ${REGISTRY}
                    '''
                }
            }
        }

        stage('🚀 远程部署') {
            steps {
                sshagent(credentials: [env.SSH_CRED]) {
                    withCredentials([usernamePassword(
                        credentialsId: env.REGISTRY_CRED,
                        usernameVariable: 'REG_USER',
                        passwordVariable: 'REG_PASS')]) {
                        sh '''
                            ssh -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} \
                              "REG_USER='$REG_USER' REG_PASS='$REG_PASS' \
                               IMAGE='${IMAGE_NAME}:${IMAGE_TAG}' \
                               APP_NAME='${APP_NAME}' APP_PORT='${APP_PORT}' \
                               REGISTRY='${REGISTRY}' bash -s" < deploy/remote_deploy.sh
                        '''
                    }
                }
            }
        }

        stage('🩺 健康检查') {
            steps {
                sh '''
                    sleep 5
                    code=$(curl -s -o /dev/null -w "%{http_code}" http://${REMOTE_HOST}:${APP_PORT} || echo 000)
                    echo "HTTP 状态码:$code"
                    [ "$code" = "200" ] || [ "$code" = "304" ] || exit 1
                '''
            }
        }
    }

    post {
        success { echo "✅ 部署成功:http://${REMOTE_HOST}:${APP_PORT}(镜像 ${IMAGE_TAG})" }
        failure { echo '❌ 失败,请查看上方日志' }
        always  {
            sh 'docker image prune -f || true'   // 清理 Jenkins 端悬空镜像
            cleanWs()
        }
    }
}

8.5 远程部署脚本 deploy/remote_deploy.sh

把部署逻辑放在仓库的脚本里(而非内联 SSH),更易维护、可单独测试。该脚本在目标服务器上执行:

#!/usr/bin/env bash
set -euo pipefail

# 由 Jenkins 通过环境变量传入:IMAGE / APP_NAME / APP_PORT / REGISTRY / REG_USER / REG_PASS

echo ">>> 登录镜像仓库"
echo "$REG_PASS" | docker login "$REGISTRY" -u "$REG_USER" --password-stdin

echo ">>> 拉取镜像:$IMAGE"
docker pull "$IMAGE"

echo ">>> 停止并移除旧容器(如存在)"
docker rm -f "$APP_NAME" 2>/dev/null || true

echo ">>> 启动新容器"
docker run -d \
  --name "$APP_NAME" \
  --restart=always \
  -p "${APP_PORT}:3000" \
  -e NODE_ENV=production \
  -e NITRO_HOST=0.0.0.0 \
  -e NITRO_PORT=3000 \
  "$IMAGE"

echo ">>> 记录当前镜像标签(用于回滚)"
echo "$IMAGE" > "/home/deploy/.${APP_NAME}.current"

echo ">>> 清理悬空镜像"
docker image prune -f || true
docker logout "$REGISTRY" || true

echo ">>> 部署完成"
docker ps --filter "name=${APP_NAME}"

记得 chmod +x deploy/remote_deploy.sh 并提交。脚本采用 docker rm -f + docker run 的「替换式」部署,简单可靠;若要零停机,见 9.3 蓝绿方案。


9. 目标服务器部署与 Nginx

9.1 目标服务器前置检查

# 在 192.168.1.20 上确认
docker version            # 已安装
groups deploy             # 输出应包含 docker
docker ps                 # deploy 用户能直接执行(无需 sudo)

容器由 Jenkins 流水线自动拉起,无需手动。架构如下:

flowchart LR
    Nginx[Nginx :80/443] -->|proxy_pass| Container[Nuxt 容器 127.0.0.1:3000]
    User[用户] --> Nginx

9.2 安装并配置 Nginx 反向代理

sudo apt install -y nginx     # Ubuntu
# sudo yum install -y nginx   # CentOS

创建 /etc/nginx/conf.d/nuxt.conf

server {
    listen 80;
    server_name www.example.com;

    client_max_body_size 50M;
    access_log /var/log/nginx/nuxt-access.log;
    error_log  /var/log/nginx/nuxt-error.log;

    # 静态资源长缓存
    location /_nuxt/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # SSR 反向代理
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 60s;
    }

    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 5;
    gzip_types text/plain text/css application/json application/javascript application/xml text/javascript;
}
sudo nginx -t && sudo systemctl restart nginx && sudo systemctl enable nginx

9.3 零停机蓝绿部署(进阶)

替换式部署在 rm -f 到新容器就绪之间有几秒不可用。零停机方案:新容器先在另一端口启动,健康检查通过后再切 Nginx upstream。

deploy/remote_deploy_bluegreen.sh 核心思路:

#!/usr/bin/env bash
set -euo pipefail
# 当前活跃端口记录在 /home/deploy/.${APP_NAME}.port,默认 3000
CUR_PORT=$(cat "/home/deploy/.${APP_NAME}.port" 2>/dev/null || echo 3000)
NEW_PORT=$([ "$CUR_PORT" = "3000" ] && echo 3001 || echo 3000)

echo "$REG_PASS" | docker login "$REGISTRY" -u "$REG_USER" --password-stdin
docker pull "$IMAGE"

# 启动新容器到 NEW_PORT
docker rm -f "${APP_NAME}-${NEW_PORT}" 2>/dev/null || true
docker run -d --name "${APP_NAME}-${NEW_PORT}" --restart=always \
  -p "${NEW_PORT}:3000" -e NITRO_HOST=0.0.0.0 "$IMAGE"

# 健康检查
for i in $(seq 1 15); do
  code=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${NEW_PORT}" || echo 000)
  [ "$code" = "200" ] && break
  sleep 2
done
[ "$code" = "200" ] || { echo "新容器健康检查失败"; docker rm -f "${APP_NAME}-${NEW_PORT}"; exit 1; }

# 切换 Nginx upstream 端口并 reload
sudo sed -i "s/127.0.0.1:${CUR_PORT}/127.0.0.1:${NEW_PORT}/g" /etc/nginx/conf.d/nuxt.conf
sudo nginx -t && sudo nginx -s reload

# 记录新端口、移除旧容器
echo "$NEW_PORT" > "/home/deploy/.${APP_NAME}.port"
docker rm -f "${APP_NAME}-${CUR_PORT}" 2>/dev/null || true
echo "$IMAGE" > "/home/deploy/.${APP_NAME}.current"

蓝绿方案需给 deploy 用户开放 nginx 相关命令的免密 sudo:
echo 'deploy ALL=(ALL) NOPASSWD:/usr/sbin/nginx' | sudo tee /etc/sudoers.d/deploy-nginx

9.4 防火墙开放端口

# Ubuntu ufw
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw reload

# CentOS firewalld
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload

容器的 3000 端口仅供 Nginx 本机访问,不建议对公网开放,省去暴露应用端口的风险。


10. GitLab Webhook 自动触发

实现「push 代码 → 自动构建镜像 → 自动部署」。

10.1 配置 Webhook

GitLab 项目 → Settings → Webhooks

┌──────────────────────────────────────────────────────────┐
│  URL:  http://192.168.1.10:8080/project/nuxt-docker-deploy │
│  Secret token: ****************  (来自 8.2 的 Generate)     │
│  Trigger:                                                  │
│    ☑ Push events      → 分支过滤: main                     │
│    ☑ Merge request events                                  │
│  ☐ Enable SSL verification  (内网可不勾)                    │
│              [Add webhook]  [Test]                         │
└──────────────────────────────────────────────────────────┘
字段
URL Jenkins 任务页面显示的 webhook 地址
Secret token 任务触发器中 Generate 的 Token
Trigger Push events、Merge request events

10.2 测试

Test → Push events,返回 200 OK 即连通。

10.3 验证全链路

git commit -am "feat: 测试 docker 自动部署"
git push origin main

回到 Jenkins,应看到自动触发的新构建,依次跑完:检出 → 构建镜像 → 推送 → 远程部署 → 健康检查。


11. 验证、回滚与排错

11.1 部署成功验证

# 目标服务器
docker ps --filter "name=nuxt-frontend"   # 容器 Up
curl -I http://127.0.0.1:3000             # 200
curl -I http://www.example.com            # 经 Nginx 200

11.2 快速回滚

镜像方式回滚极其简单——重新拉起任意历史标签即可:

# 方式一:在目标服务器手动回滚到指定历史版本
docker rm -f nuxt-frontend
docker run -d --name nuxt-frontend --restart=always \
  -p 3000:3000 -e NITRO_HOST=0.0.0.0 \
  registry.example.com/web/nuxt-frontend:41-9f8e7d6   # 上一个稳定标签

# 查看本地可回滚的镜像列表
docker images registry.example.com/web/nuxt-frontend
// 方式二:在 Jenkins 新建「回滚」参数化任务,输入目标 TAG 执行 remote_deploy.sh
// 把 IMAGE_TAG 改为手动参数 ROLLBACK_TAG 即可复用同一套部署脚本

因为每次构建都打了唯一标签 ${BUILD_NUMBER}-${SHA},任何一次历史构建都可精确还原。

11.3 常见问题

❌ Jenkins 容器内 docker: not found / permission denied

docker exec jenkins which docker                       # 确认已挂载
docker exec -u root jenkins chmod 666 /var/run/docker.sock
# 根治:启动时加 --group-add $(getent group docker | cut -d: -f3)

docker build 慢或拉基础镜像超时

  • 配置 /etc/docker/daemon.json 镜像加速(见 2.4)
  • Dockerfile 内 npm config set registry https://registry.npmmirror.com
  • 确认 .dockerignore 已排除 node_modules

docker login 失败 unauthorized

# 核对 registry-cred 账号密码;私服未配 HTTPS 时需加 insecure-registries
docker login registry.example.com -u admin

❌ 容器启动了但浏览器访问不到

docker logs nuxt-frontend                  # 看启动日志
docker exec nuxt-frontend env | grep NITRO # 确认 NITRO_HOST=0.0.0.0
# 若为 localhost,容器外无法访问——必须 0.0.0.0
ss -tlnp | grep 3000                       # 宿主机端口映射是否存在

❌ Webhook 不触发

响应码 原因
403 Secret Token 不一致
404 Jenkins URL 错误
Timeout GitLab 到 Jenkins 网络不通
# 在 GitLab 服务器测试连通
curl -v http://192.168.1.10:8080/project/nuxt-docker-deploy

❌ SSH 远程部署 Host key verification failed

docker exec -it jenkins bash -c "ssh-keyscan -H 192.168.1.20 >> ~/.ssh/known_hosts"
# 或部署命令已带 -o StrictHostKeyChecking=no

附录

A. 常用命令速查

# ---- Jenkins ----
docker logs -f jenkins                       # 日志
docker restart jenkins                       # 重启
tar -czf jenkins_backup_$(date +%F).tar.gz /data/jenkins_home   # 备份

# ---- 目标服务器容器 ----
docker ps                                    # 运行中容器
docker logs -f nuxt-frontend                 # 应用日志
docker stats nuxt-frontend                   # 资源占用
docker rm -f nuxt-frontend                   # 删除容器
docker image prune -f                        # 清理悬空镜像

B. Nuxt 项目结构(含部署文件)

nuxt-frontend/
├── deploy/
│   ├── remote_deploy.sh            # 远程部署脚本 ⭐
│   └── remote_deploy_bluegreen.sh  # 蓝绿部署(可选)
├── pages/  components/  server/    # Nuxt 源码
├── public/
├── .dockerignore                   # ⭐
├── Dockerfile                      # ⭐ 多阶段构建
├── Jenkinsfile                     # ⭐ CI/CD
├── nuxt.config.ts
└── package.json

C. 三种部署方式对比

维度 PM2 传产物 Docker 镜像(本文) K8s
环境一致性 依赖目标机 Node 版本 ✅ 镜像自带运行时
回滚 软链接切换 ✅ 切换镜像标签 ✅ 极强
学习成本
适用规模 单机/小型 单机~多机 集群

D. 安全加固

  1. Jenkins 前置 Nginx + HTTPS(Let's Encrypt),限制后台访问 IP
  2. 部署用户 deploy 非 root,仅授予 docker 组与必要 sudo
  3. 应用容器端口不对公网暴露,统一走 Nginx
  4. 镜像仓库启用 TLS,关闭匿名拉取
  5. /data/jenkins_home 每日定时备份
  6. 凭据全部走 Credentials,禁止明文;定期轮换密钥

总结

至此完成了 Docker 镜像方式 的全自动部署链路:

开发者 push → GitLab Webhook → Jenkins
   ↓
docker build → docker push(带唯一标签)
   ↓
SSH 远程 → docker pull → docker run
   ↓
Nginx 反代 → 用户访问 ✨

✅ Docker 安装 Jenkins(DooD) ✅ GitLab/SSH/Registry 凭据
✅ Nuxt 多阶段镜像构建 ✅ 镜像推送与远程容器化部署
✅ Webhook 自动触发 ✅ 健康检查与秒级回滚


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容