Docker + Jenkins + Nginx 实现前端自动化构建与静态资源发布(含一键初始化脚本)

背景:以前没使用 docker 时,要搭建一个 jenkins 环境,还是蛮麻烦的,需要下载 java 的 jdk,并且还要在 /etc/profile 中配置环境变量,将 java 文件位置写进 jenkins 配置内。 并且在安装过程中,难免要处理报错或异常,玩着玩着,就把服务器搞得乱七八糟....
本文主要记录一下,通过写好配置 docker-compose.yml,执行 sh 脚本、自动创建 jenkins 和 nginx 相关容器。


1. 最终目标 🎯

实现下列体验:

  1. 首次执行 init.sh:自动创建目录、生成 Nginx 配置、初始化 Jenkins、拉起容器。
  2. 在 Jenkins 创建一个构建任务(或 Pipeline):
    • 拉取前端仓库代码。
    • npm ci && npm run build
    • dist/ 复制到共享目录 /app/shared-dist
  3. 浏览器访问:
    • Jenkins 管理界面:http://<HOST>:8080
    • 已发布前端:http://<HOST> (默认 80 端口)
  4. 再次构建 → Nginx 直接读取最新静态资源(无需重启)。

2. 架构设计概览

scss 复制代码
┌──────────────────────────────────────────┐
│                Host (Host)               │
│  /app                                    │
│  ├── jenkins_home  (Jenkins data/plugins)│
│  ├── shared-dist   (built static assets) │
│  └── nginx                               │
│       └── default.conf (Nginx vhost)     │
│                                          │
│  Docker Bridge Network: ci_net           │
│      ┌──────────────┐   ┌──────────────┐ │
│      │  Jenkins     │   │    Nginx     │ │
│      │ :8080 /50000 │   │ :80          │ │
│      │ build → dist │   │ read-only    │ │
│      └──────────────┘   └──────────────┘ │
└──────────────────────────────────────────┘
  • 解耦:Jenkins 专注构建,Nginx 专注静态文件分发。
  • 持久化:删除容器不丢 Jenkins 数据与构建产物。
  • 原子部署:构建后直接同步文件。

3. 目录设计

目录 用途 注意事项
/app/jenkins_home Jenkins 工作目录(插件、配置、Job、用户、密钥等) 勿轻易清空,可做定期备份
/app/shared-dist 最新前端构建产物 Nginx 以只读方式挂载
/app/nginx/default.conf Nginx 相关配置 可扩展 HTTPS、缓存策略

提示:上面3个目录会在 sh 脚本执行时创建。
docker 创建启动容器时,会做目录挂载;其中 jenkins_home 目录初始为空,但是 jenkins 启动脚本首次初始化时,会自动写入相关文件,不必担心宿主机覆盖容器内配置。


4. docker-compose.yml 配置

yaml 复制代码
version: "3.9"                                     # 使用 Compose 3.9 语法版本,通用且新
services:                                          # 定义要运行的服务集合
  jenkins:                                         # Jenkins 服务:负责前端构建
    image: jenkins/jenkins:lts-jdk17
    container_name: wj-jenkins                     # 固定容器名,方便 docker logs / ps 查询
    restart: unless-stopped                        # 除非手动停止,否则异常退出会自动拉起
    ports:
      - "8080:8080"                                # 宿主 8080 → Jenkins Web 界面
      - "50000:50000"                              # Jenkins inbound agent 端口(保留,可能未来用)
    environment:
      TZ: Asia/Shanghai                            # 时区,影响日志时间显示
    volumes:
      - /app/jenkins_home:/var/jenkins_home        # 绝对路径:持久化 Jenkins 主目录;删除该目录相当于重装,第一次执行脚本,jenkins_home 空目录会自动初始化
      - /app/shared-dist:/var/jenkins_home/deploy/frontend_dist             # 构建产物共享目录;Jenkins 将 dist/* 复制到这里
    networks:
      - ci_net                                       # 加入自定义网络,使与 Nginx 容器互通(未来扩展方便)

  nginx:                                           # Nginx 服务:仅提供静态文件访问
    image: nginx:1.28.0
    container_name: wj-nginx                       # 固定容器名
    restart: unless-stopped                        # 异常自动重启,保证前端站点存活
    depends_on:
      - jenkins                                    # 启动顺序提示:先启动 Jenkins(不是硬依赖,只是声明)
    ports:
      - "80:80"                                    # 宿主 80 → Nginx 80;浏览器 http://localhost 访问
    volumes:
      - /app/shared-dist:/usr/share/nginx/html:ro  # 将 Jenkins 产物目录挂载为 Nginx 静态根,只读防误改
      - /app/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro   # 将 Jenkins 产物目录挂载为 Nginx 静态根,只读防误改
    networks:
      - ci_net                                       # 同网络:未来可加反向代理、健康检查等

networks:                                          # 自定义网络声明
  ci_net:
    driver: bridge                                 # 使用默认桥接模式(本地开发足够)

# 注意:
# 1. 所有宿主机目录使用绝对路径 /app/... ,需要你在宿主机先创建并赋予权限。
# 2. shared-dist 与 jenkins_home 目录不可与其他无关服务混用,避免污染。

关键说明:

  • jenkins:lts-jdk17nginx:1.28.0 是在 docker hub 上挑选的 2 个镜像版本。
  • 上面比较关键的是 /app/shared-dist 分别和 jenkins 和 nginx 做了目录挂载 ,其中 nginx 使用 :ro 只读挂载,避免误写。
  • jenkins 服务把 /app/shared-dist 挂载进它的工作目录中,便于复制产物。
  • 自定义网络 ci_net:可忽略,为后续拓展更灵活。

5. init.sh 一键初始化脚本

bash 复制代码
#!/usr/bin/env bash
set -e

echo "[+] 创建目录"
sudo mkdir -p /app/jenkins_home /app/shared-dist /app/nginx

echo "[+] 写 Nginx default.conf (若不存在)"
if [ ! -f /app/nginx/default.conf ]; then
  sudo tee /app/nginx/default.conf >/dev/null <<'EOF'
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;
    location / { try_files $uri $uri/ /index.html; }
}
EOF
else
  echo "    已存在,跳过。"
fi

echo "[+] 写占位 index.html (若不存在)"
[ -f /app/shared-dist/index.html ] || echo "<h1>Waiting for build</h1>" | sudo tee /app/shared-dist/index.html >/dev/null

echo "[+] 设置 Jenkins 可写权限"
sudo chown -R 1000:1000 /app/jenkins_home /app/shared-dist || true

echo "[+] 启动 docker compose"
if docker compose version >/dev/null 2>&1; then
  docker compose up -d
else
  docker-compose up -d
fi

echo "[✓] 完成:Jenkins -> http://<host>:8080  Nginx -> http://<host>:80"
echo "查看初始密码: docker logs wj-jenkins | grep -i 'initialAdminPassword'"

设计:

  • 创建挂载需要的目录。
  • 当目录为空时,生成默认配置 default.confindex.html,避免403/404.
  • 自动权限修复:Jenkins 在容器内默认 UID=1000,必须让宿主目录属主匹配。
  • 兼容新版 docker compose 与旧版 docker-compose

6. 快速启动步骤

bash 复制代码
# 1. 准备环境:安装 Docker / Docker Compose
# 2. 上传 docker-compose.yml、init.sh 两个文件到同一目录
chmod +x init.sh

# 3. 执行初始化
./init.sh

# 4. 查看 Jenkins 初始管理员密码
docker logs wj-jenkins | grep -i 'initialAdminPassword'

# 5. 浏览器访问
# Jenkins: http://服务器IP:8080
# 前端站点: http://服务器IP

经过上面的配置后,打开 8080 端口就可以看到下面页面,执行命令获取初始密码,粘贴进去就可以了。


7. Jenkins 首次配置建议

登录后:

  1. 选择:安装推荐插件/选择插件安装。推荐自己选择插件按照,默认会勾选一些,自己只需要安装一个 NodeJS Plugin 就可以了。
  2. 创建管理员账号。
  3. 创建一个全局凭据,这个凭据是为了拉取 github/gitlab 代码的,使用下面命令,在 /app 目录下创建一个密钥对。
bash 复制代码
mkdir -p /app/jenkins_keys && ssh-keygen -t ed25519 -C "jenkins-ci" -f /app/jenkins_keys/id_ed25519 -N ''
  1. 复制出来公钥 id_ed25519.pub 加到代码库的 SSH 里;复制 id_ed25519 私钥,去 jenkins 里创建一个全局凭据。

8. 创建 jenkins job

可以使用 Freestyle Job 或者 Pipeline 的,以前者为例。

注意,由于加了 ssh,所以git仓库就不要使用 https:这种了,否则拉取时可能出错。

环境这里,首先需要去 tool 里面找到之前安装的 NodeJS 插件,选择一个版本,创建完这里就可以选了。

Shell 脚本配置

  • 执行命令
  • 复制到指定目录
sh 复制代码
#!/bin/bash
set -e

echo "[1] 显示 Node 版本"
node -v
npm -v

echo "[2] 安装依赖(使用 package-lock 有的话优先 npm ci)"
if [ -f package-lock.json ]; then
  npm ci
else
  npm install
fi

echo "[3] 构建"
npm run build

echo "[4] 发布到共享目录"
TARGET_DIR="$JENKINS_HOME/deploy/frontend_dist"
mkdir -p "$TARGET_DIR"
# 清空旧内容
rm -rf "${TARGET_DIR:?}/"*
# 复制新产物(假设 dist 为输出目录)
cp -r dist/* "$TARGET_DIR"/

echo "[5] 完成。Nginx 将立即提供新的静态文件。"

访问 http://<HOST> 即看到最新页面,无需重启 Nginx。


9. Nginx 配置拓展

当前 default.conf

nginx 复制代码
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;
    # SPA 前端:路径不匹配时回退 index.html
    location / { try_files $uri $uri/ /index.html; }
}

可扩展:

  1. 添加 gzip:

    nginx 复制代码
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
  2. 添加缓存策略:

    nginx 复制代码
    location ~* \.(js|css|png|jpg|jpeg|svg|ico)$ {
        add_header Cache-Control "public,max-age=31536000,immutable";
    }

10.常见问题:

  1. 第一次拉代码,会有这个提示,按照提示,去对应位置选择 Accept first connection 就可以了!
  1. 如果有需要,可以和进一步自动化:Git Webhook + 自动触发,实现 CICD 闭环(下面未经验证,个人觉得自动触发在生产环境不太适用)

    1. Jenkins → 新建 Pipeline → 构建触发器勾选 GitHub hook trigger for GITScm polling
    2. 在 Git 平台(GitHub/GitLab)配置 Webhook 指向:http://<HOST>:8080/github-webhook/
    3. Push 即自动触发构建 → 发布。

完结~撒花 💐

至此,就完成了 docker + jenkins + nginx 的自动化配置,学废了吗?

本文主要是 CD 相关,如果需要 GitLab CI 相关配置,可看另一篇关于 gitlab-runner 文章。

相关推荐
THMAIL9 小时前
机器学习从入门到精通 - Python环境搭建与Jupyter魔法:机器学习起航必备
linux·人工智能·python·算法·机器学习·docker·逻辑回归
zzu123zsw10 小时前
第11章 分布式构建
分布式·jenkins
13线12 小时前
Windows+Docker一键部署CozeStudio私有化,保姆级
docker·容器·开源
0wioiw012 小时前
Docker(②创造nginx容器)
docker·容器
菜鸟IT胡12 小时前
docker更新jar包,懒人执行脚本
运维·docker·容器
天道有情战天下13 小时前
ClickHouse使用Docker部署
clickhouse·docker·容器
小池先生13 小时前
docker中的mysql变更宿主机映射端口
android·mysql·docker
zzu123zsw14 小时前
第13章 Jenkins性能优化
运维·性能优化·jenkins
THMAIL14 小时前
机器学习从入门到精通 - 模型部署落地:Docker+Flask构建API服务全流程
人工智能·python·算法·机器学习·docker·flask·逻辑回归