Docker + Jenkins + Nginx实现前端自动化部署(Mac)

77 阅读4分钟

一、示例环境版本

  1. Docker : v24.0.7
# 查看 docker 版本命令
docker -v
  1. Docker Compose : v2.23.3
# 查看 docker-compose 版本命令
docker-compose -v
  1. Git : v2.39.2
# 查看 git 版本命令
git -v
  1. Nginx : v1.29.8
# 查看 nginx 版本命令
# 首先进入 nginx 容器内
docker exec -it nginx_dev bin/bash
# 然后再查看版本信息
nginx -v
  1. Jenkins : v2.541.3
# 查看 Jenkins 版本命令
# 首先进入 Jenkins 容器内
docker exec -it jenkins bin/bash
# 然后再查看版本信息
java -jar /usr/share/jenkins/jenkins.war --version

二、环境准备

安装 Docker

在 MacOS 安装 Docker 相对简单,只需在 Docker 官网 下载 Docker Destop,安装就好了。

启动 Docker

直接打开 Docker Destop 应用程序或在终端输入以下命令:

open -a Docker

安装 Docker Compose

因为使用的是 Docker Desktop,所以 Docker Compose已经包含在内了。

安装 Git

首先,确保你已经安装了 Homebrew,如果没有,你可以访问 Homebrew官网 并按照指示进行安装。
或者你可以使用别的方式安装 git。

brew install git

安装 Nginx 和 Jenkins

使用 docker 拉取nginx、jenkins 镜像

docker pull nginx
docker pull jenkins/jenkins:lts

拉取完后查看镜像

docker images

image.png

创建目录结构

创建 docker 的相关目录,方便维护 最终的目录结构如下:

+ docker
    + compose
        - docker-compose.yml // docker-compose配置
    + html                   // 各环境代码目录(实际项目可能不在同一目录)
       + dev                // dev环境代码目录
       + release            // release 环境代码目录
       + sit               // sit环境目录
       + pro               // pro环境代码目录
    + jenkins_home         // jenkins 工程目录
    + nginx               // nginx 工程目录
       + conf
          = nginx.conf   // nginx配置

nginx.conf

# nginx.conf 例:
user  nginx;
worker_processes  1;
 
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
 
events {
    worker_connections  1024;
}
 
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  /var/log/nginx/access.log  main;
 
    sendfile        on;
    #tcp_nopush     on;
 
    keepalive_timeout  65;
 
    gzip  on;

    # dev环境
    server {
        # 监听的端口
        listen  8001;
        server_name  localhost;
        # 设置日志
        # access_log  logs/dev.access.log  main;
        
        # 定位到index.html
        location / {
            # linux下HTML文件夹,就是你的前端项目文件夹
            root  /usr/share/nginx/html/dev/dist;
            # 输入网址(server_name:port)后,默认的访问页面
            index  index.html;
            try_files $uri $uri/ /index.html;
        }
    }

    #sit环境
    server {
        # 监听的端口
        listen  8002;
        server_name  localhost;
        # 设置日志
        # access_log  logs/sit.access.log  main;

        # 定位到index.html
        location / {
            #容器内路径(宿主机html目录通过volume映射到/usr/share/nginx/html)
            root  /usr/share/nginx/html/sit/dist;
            #输入网址(server_name:port)后,默认的访问页面
            index  index.html;
            try_files $uri $uri/ /index.html;
        }
    }
 
    # include /etc/nginx/conf.d/*.conf;   
}

docker-compose.yml

version: '3'

networks:
  frontend:
    external: true

services:                                      # 容器
  docker_jenkins:
    user: root                                 # root权限
    restart: always                            # 重启方式
    image: jenkins/jenkins:lts                 # 使用的镜像
    container_name: jenkins                    # 容器名称
    environment:
      - TZ=Asia/Shanghai
      - "JENKINS_OPTS=--prefix=/jenkins_home" ## 自定义 jenkins 访问前缀(上下文context)


    ports:                                     # 对外暴露的端口定义
      - 8080:8080
      - 50000:50000
      

    volumes:                                   # 卷挂载路径
      - 上面创建的docker/jenkins_home的绝对路径:/var/jenkins_home     # 挂载到容器内的jenkins_home目录
      - 上面创建的docker/compose的绝对路径:/usr/local/bin/docker-compose
  
  docker_nginx_dev:                            # nginx-dev环境
    restart: always
    image: nginx
    container_name: nginx_dev
    ports:
      - 8001:8001
    volumes:
      - 上面创建的nginx.conf的绝对路径:/etc/nginx/nginx.conf
      - 上面创建的docker/html的绝对路径:/usr/share/nginx/html
      - 上面创建的docker/nginx/logs的绝对路径:/var/log/nginx

  docker_nginx_sit:                            # nginx-sit环境
    restart: always
    image: nginx
    container_name: nginx_sit
    ports:
      - 8002:8002
    volumes:
      - 上面创建的docker/nginx/conf/nginx.conf的绝对路径:/etc/nginx/nginx.conf
      - 上面创建的docker/html的绝对路径:/usr/share/nginx/html
      - 上面创建的docker/nginx/log的绝对路径:/var/log/nginx

配置说明
docker_jenkins 是一个定义的服务名称。
user: root 指定了 Jenkins 容器使用 root 权限。
restart: always 表示容器总是在退出时重启。
image: jenkins/jenkins:lts 指定了 Jenkins 镜像及其版本。
container_name: jenkins 是容器的名称。
ports 定义了容器内外端口的映射。
volumes 定义了主机文件系统路径与容器内路径的挂载关系。
image: nginx指定了 Nginx 镜像。
container_name: nginx_dev 是容器的名称。

三、启动环境

1. 启动 docker

open -a Docker 
# 或者直接打开Docker Destop应用程序

2. 启动 Jenkins、nginx

docker-compose up -d

3. 查看运行状态

要在 compose 文件夹下运行该命令(也就是要在 docker-compose.yml 文件目录下运行)

docker-compose ps -a

image.png

四、验证服务

1. 验证 Nginx

在 /docker/html/dev/dist 目录下新建 index.html,文件内容如下: 或者把你的前端打包好的项目把 dist 目录替换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Welcome to Nginx!</h1>
</body>
</html>

浏览器打开,输入 服务器地址:8001 看到下面的页面说明 nginx 配置没问题,同样的操作可以测试8002端口。 image.png

2. 验证 Jenkins

浏览器输入 服务器地址:8080/jenkins_home,看到下面的页面,会提示要输入密码,密码位于docker/jenkins_home/secrets/initialAdminPassword,

cat /docker/jenkins_home/secrets/initialAdminPassword

image.png

登录后,选择安装推荐的插件。 image.png

然后等待安装。 image.png

要是插件一直安装失败,最后经多番查找,是 jenkens 会对 update-center.json 做签名校验安全检查,需要我们先关闭掉才行。可以尝试以下方法:

  1. 关闭对 update-center.json 的安全检查,并禁用下载签名检查:
# 在 docker-compose.yml 中修改 jenkins 配置
environment:
  ...其他配置
  - "JAVA_OPTS=-Dhudson.model.DownloadService.noSignatureCheck=true" # 添加这行配置

安装完成! 创建管理员用户: image.png 保存并完成,继续下一步: image.png 开始使用Jenkins! image.png 到达Jenkins主页面,我的地址是:http://localhost:8080/jenkins_home/ image.png

五、Jenkins 自动化部署配置

1. 设置中文

提示:由于我在一开始安装插件的时候已经自动安装,已经是中文版本。 image.png jenkins 版本不一样,显示的页面可能不一样,如果跟我的界面不一样可以自行查找设置中文的教程,实际上大差不差。
第一步:点击右上角设置图标 image.png 第二步:点击插件管理 image.png 第三步:点击Available plugins,搜索框输入local,选择 Localization: Chinese (Simplified),点击安装。我已经安装所以搜索不到(请参考提示)。 image.png 安装完成后,重启 Jenkins 或者在浏览器输入服务器地址:8080/jenkins_home/restart。

2. 安装 Publish Over SSH 和 NodeJS 插件

同样的步骤安装插件,这里不再具体操作。

(1)Publish Over SSH 配置远程服务器

Publish Over SSH 用来连接远程服务器,接下来去设置连接远程服务器验证方式,本案例采用密码验证。 image.png

找到Publish Over SSH,点击新增。 image.png

在新增后出来的面板中输入信息,之后点击高级。 image.png

输入密码、服务器端口,并点击Test Configuration Success进行测试,显示successs则成功,之后先点击应用,再保存。

(2)NodeJS 配置

【系统管理】---->【全局工具配置】---->【NodeJS 安装】 image.png

点击新增 Node JS。 image.png

配置安装 NodeJS 的基本信息,自定义别名、选择安装版本,最后,先点击应用再保存。 image.png

3. 添加凭据

添加凭据,也就是 GitHub 或者其他远程仓库的账号密码,方便之后使用。
【系统管理】---> 【凭证管理】 image.png

点击全局凭证,再点击添加凭证,选择 Username with password。 image.png image.png 填写用户名、密码、描述 image.png

4. 创建 Job

image.png 输入项目名称:测试项目,选择构建一个自由风格的软件项目,点击确定。 image.png 接下来需要配置项目的一些信息
配置完成后,先点击应用后保存,然后返回首页,打开项目,点击立即构建 image.png image.png 可以看到构建过程中并没有出现error,并最终显示success,此时基础构建基本完成(我把我仓库地址打马赛克了) image.png

5. GitHub webHooks 配置

在git push代码之后,可以自动触发 Jenkins 自动拉取 GitHub 上的代码进行构建。打开GitHub中的项目,在Setting中选择Webhooks. image.png

payload URL 为:http://ip:8080/jenkins_home/github-webhook/,之后点击Add webHook就可以了。 image.png

然后需要在 Jenkins 为该项目配置一个选项。构建触发器模块中的GitHub hook trigger for GITScm polling,先点击应用后保存。 image.png

6. 构建环境

在 Jenkins 中将 Node.js 和 npm 的 bin 文件夹添加到 PATH 中,否则可能就会报错。 image.png

7. Build Steps

因为提交服务器代码是没有node_moudle,所以我们需要执行脚本去安装依赖。
源码管理可以看到Build Steps模块,选择Execute NodeJS script。 image.png

选择node版本,这个node版本是在上面我们已经配置过的,如果需要其他版本可通过上面操作新增NodeJs即可。
配置完成之后,建议 立即构建 项目。 image.png 如果这一步构建失败遇到跟我类似的错误,可以按照我下面的方式进行修复。 image.png

在compose文件夹下创建 Dockerfile:

FROM jenkins/jenkins:lts

USER root
RUN apt-get update && apt-get install -y libatomic1 || \
    (curl -L -o /tmp/libatomic1.deb "https://mirrors.aliyun.com/debian/pool/main/g/gcc-14/libatomic1_14.2.0-19_arm64.deb" && \
     dpkg-deb -x /tmp/libatomic1.deb /tmp/libatomic_extract && \
     cp /tmp/libatomic_extract/usr/lib/aarch64-linux-gnu/libatomic.so.1.2.0 /usr/lib/aarch64-linux-gnu/ && \
     cd /usr/lib/aarch64-linux-gnu && ln -sf libatomic.so.1.2.0 libatomic.so.1 && \
     ldconfig && rm -rf /tmp/libatomic1.deb /tmp/libatomic_extract)
USER jenkins

然后修改docker-compose.yml文件:

...
services:                                    
  docker_jenkins:
      build: .                    # 添加这一行:使用自定义 Dockerfile 构建
      ...
...

然后 Jenkins 重新构建就成功了!

8. Shell 命令

选择增加构建步骤,在下拉框选中执行 shell image.png 输入下面指令: image.png

#!/bin/bash
node -v
npm -v
echo $PATH

之后去构建,可以看到我们打印的版本信息都打印出了 image.png 接下来我们脚本中执行安装node_module以及打包命令。

#!/bin/bash
node -v
npm -v
npm install
# npm i esbuild-linux-64@0.13.4 -D 
echo "依赖安装成功"
npm run build
echo "打包成功"
echo $PATH

然后先点击应用后保存,去立即构建。 如果构建出现这个错误(Error: The package "@esbuild/linux-x64" could not be found, and is needed by esbuild.),就取消npm i esbuild-linux-64@0.13.4 -D注释。

【注意】:我把前端项目和docker配置放在了同一个仓库地址了,可以参考一下我的目录结构:

+ client -前端项目
+ docker -配置
    + compose
    + html
    + jenkins_home
    + nginx
- .gitignore
- README.md

所以我需要修改jenkins的构建步骤shell:

#!/bin/bash
set -e

cd client

node -v
npm -v

test -f package.json || {
  echo "错误:client 目录下不存在 package.json"
  exit 1
}

npm ci

echo "依赖安装成功"

npm run build

test -d dist || {
  echo "错误:构建完成,但没有生成 dist 目录"
  exit 1
}

echo "打包成功"
echo "构建产物:"
find dist -maxdepth 2 -type f -print

echo "PATH=$PATH"

9. 自动部署到对应环境项目目录

上面打包到了jenkins中的workspace中 docker/jenkins_home/workspace/测试项目/client/dist,但是我们设置的项目环境路径跟这个不同,比如开发环境项目目录是 /docker/html/dev/dist,所以需要打包后,把dist文件内容推送到 /docker/html/dev/dist/ 目录下。修改一下上面的脚本,改为下面:

#!/bin/bash
node -v
npm -v
npm install
# npm i esbuild-linux-64@0.13.4 -D
echo "依赖安装成功"
npm run build
echo "打包成功"
rm -rf dist.tar  #每次构建删除已存在的dist压缩包
tar -zcvf dist.tar ./dist #将dist文件压缩成dist.tar
echo $PATH

【注意】:我把前端项目和docker配置放在了同一个仓库地址了,所以上面的脚本,改为下面的:

#!/bin/bash
set -e

cd client

node -v
npm -v

test -f package.json || {
  echo "错误:client 目录下不存在 package.json"
  exit 1
}

npm ci

echo "依赖安装成功"

npm run build

test -d dist || {
  echo "错误:构建完成,但没有生成 dist 目录"
  exit 1
}

echo "打包成功"
echo "构建产物:"
find dist -maxdepth 2 -type f -print

# ========== 新增:部署到开发环境 ==========
DEPLOY_DIR="/html/dev/dist"

echo "开始部署到 ${DEPLOY_DIR}"

# 确保目标目录存在
mkdir -p "${DEPLOY_DIR}"

# 清空旧产物(保留目录本身,避免残留已删除文件)
rm -rf "${DEPLOY_DIR:?}"/*

# 复制新构建产物(dist/ 内容 → 目标目录)
cp -r dist/. "${DEPLOY_DIR}/"

echo "部署完成,目标目录内容:"
ls -la "${DEPLOY_DIR}"

【注意】由于我Jenkins 和 Nginx 是同一台机器上的两个 Docker 容器,通过 volume 共享文件就能完成部署,所以下面的步骤我不需要!

然后点击增加构建步骤,选择 Send files or execute commands over SSH,这个命令允许你在构建过程中通过SSH连接到远程服务器执行命令或发送文件。 image.png

然后填写源文件、目标路径、执行脚本

cd /docker/html/dev
rm -rf dist/
tar zxvf dist.tar
rm dist.tar

image.png

打包流程是:删除之前打包的 tar 压缩文件 dist,把本次打包的 dist 文件压缩,然后以压缩后的 dist.tar 为源文件,连接远程服务器,放到 docker/html/dev 目录下。然后删除 dist 目录,解压刚刚远道而来的 dist.tar 文件,之后删除压缩包。

  • Source files: 准备发送的文件,该文件是相对于这个项目的 workspace 目录。例如要发送 /docker/jenkins_home/workspace/gitlab_web/dist.tar 到目标目录,则设置 Source files 为 dist.tar
  • Remove prefix: 目标目录,本案例要复制到 dev 环境下的 dist 文件,/docker/html/dev
  • Exec Command: 最后执行的命令,可在这里进行解压,删除,复制等操作

最后再次执行 立即构建。打开 你的服务器地址:8001(我的是 http://localhost:8001/ ),然后就可以看到刚刚部署的项目啦。 image.png

六、踩坑记录

任务里虽然勾选了 GitHub Push 触发器,但 Jenkins 地址是 localhost,GitHub 服务器无法访问我电脑上的回调地址,所以任务配置里我把触发器改成 Poll SCM:

# 含义:每 5 分钟检查一次 GitHub,有新提交就自动构建
H/5 * * * *

或者你可以把本地 Jenkins 暴露成一个公网地址,给 GitHub webhook 用。用 ngrok:

# 安装
brew install ngrok

# 暴露 Jenkins 端口(假设 8080)
ngrok http 8080

执行后会给你一个公网地址,比如 https://xxxx.ngrok-free.app 。然后在 GitHub 仓库:

  • Settings → Webhooks → Add webhook
  • Payload URL:https://xxxx.ngrok-free.app/github-webhook/
  • Content type:application/json
  • 勾选 Just the push event

七、最后

恭喜你,已经学会 Docker + Jenkins + Nginx 前端自动化部署 啦~