在 Linux 上使用 Jenkins(Docker 安装)部署 Nuxt 前端项目完整指南
适用场景:Jenkins 以 Docker 容器运行 → 从 GitLab 拉取 Nuxt 源码 → 构建 Docker 镜像 → 通过 SSH 推送到目标服务器 → 容器化部署 并对外提供服务。
文档里的 IP(192.168.1.10/20)、域名、仓库地址都是占位示例,落地时替换成你的实际值即可
目录
- 整体架构
- 环境与版本准备
- 用 Docker 安装 Jenkins
- Jenkins 初始化
- 安装必要插件
- 配置凭据(GitLab / SSH / Registry)
- Nuxt 项目容器化改造
- 创建 Jenkins Pipeline
- 目标服务器部署与 Nginx
- GitLab Webhook 自动触发
- 验证、回滚与排错
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 添加公钥
- 复制
id_ed25519.pub - 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. 安全加固
- Jenkins 前置 Nginx + HTTPS(Let's Encrypt),限制后台访问 IP
- 部署用户
deploy非 root,仅授予 docker 组与必要 sudo - 应用容器端口不对公网暴露,统一走 Nginx
- 镜像仓库启用 TLS,关闭匿名拉取
-
/data/jenkins_home每日定时备份 - 凭据全部走 Credentials,禁止明文;定期轮换密钥
总结
至此完成了 Docker 镜像方式 的全自动部署链路:
开发者 push → GitLab Webhook → Jenkins
↓
docker build → docker push(带唯一标签)
↓
SSH 远程 → docker pull → docker run
↓
Nginx 反代 → 用户访问 ✨
✅ Docker 安装 Jenkins(DooD) ✅ GitLab/SSH/Registry 凭据
✅ Nuxt 多阶段镜像构建 ✅ 镜像推送与远程容器化部署
✅ Webhook 自动触发 ✅ 健康检查与秒级回滚