1 Jenkins
Jenkins是一个开源的自动化服务器,用于支持软件开发中的持续集成与持续部署(CI/CD)。它是一个自由及开源的自动化工具,提供了友好的操作界面,允许开发团队自动化地执行各种任务,如代码构建、测试、部署等。Jenkins由Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。
前置知识:
1.1 Jenkins 核心概念
- 开源性:Jenkins是一个开放源代码软件,任何人都可以查看其源代码、进行修改和贡献。这使得Jenkins能够免费提供,并确保其长期的发展和维护。
- 持续集成与持续部署:Jenkins为开发团队提供了一个平台,使其能够自动化地执行构建、测试和部署等任务。这种自动化使得开发流程更为流畅,可以快速地发现和修复问题,从而加快软件的发布速度。
- 丰富的插件生态系统:Jenkins拥有一个强大的插件生态系统,涵盖了从源代码管理、构建、测试到部署的各个环节。这些插件允许用户根据特定需求定制Jenkins,以满足不同的自动化需求。
- 跨平台支持:Jenkins可以在Windows、Linux和Mac OS等操作系统上运行,具有很强的跨平台支持能力。
1.2 Jenkins 优点
- 提高开发效率:
- Jenkins 自动化了构建、测试和部署等任务,减少了人工操作的时间和错误,使开发团队能够更专注于编写高质量的代码。
- 自动化流程缩短了反馈周期,使问题能够更快地被发现和修复。
- 增强软件质量:
- 持续的集成和测试有助于在早期发现并修复问题,避免问题累积到后期造成更大的影响。
- Jenkins 支持多种测试框架,可以执行单元测试、集成测试等多种类型的测试,确保软件的全面质量。
- 支持快速迭代:
- Jenkins 使得软件发布过程更加高效和灵活,支持频繁的发布和迭代,满足现代软件开发快速变化的需求。
- 自动化部署减少了人为错误,提高了部署的可靠性和稳定性。
- 易于配置和扩展:
- Jenkins 提供了丰富的插件生态系统,用户可以根据需要选择和安装各种插件来扩展其功能。
- 通过简单的配置和脚本编写,用户可以轻松定义自己的构建和部署流程。
- 社区支持和文档丰富:
- Jenkins 是一个开源项目,拥有庞大的用户社区和活跃的开发者贡献者,用户可以获得及时的技术支持和解决方案。
- Jenkins 的官方文档和社区资源也非常丰富,有助于用户快速上手和解决问题。
- 跨平台支持:
- Jenkins 可以在多种操作系统上运行,包括Linux、Windows和Mac OS等,提供了良好的跨平台支持能力。
2 Jenkins 安装
2.1 前置准备工作
安装docker 请移步 手把手教你部署前端项目CI/CD Docker 篇
2.1.1 git 安装
# enter 到底
yum install -y git
# 查看git版本号 验证git安装成功
# git version 1.8.3.1
git --version
2.1.2 配置SSH
# Enter到底,最终会生成以下文件
# /root/.ssh/authorized_keys 允许无密码登录的公钥列表
# /root/.ssh/id_rsa 私钥文件
# /root/.ssh/id_rsa.pub 公钥文件 注意该文件里的内容是接下来要用的
ssh-keygen -t rsa -C "root"
# 复制公钥文件的内容,添加到GitHub 的 SSH keys 或 任意其他远程仓库
vim /root/.ssh/id_rsa.pub
2.1.3 远程仓库添加 SSH keys
(以github为例)
- 点击用户头像,进入 settings 页面
- 点击左侧菜单,进入 SSH and GPG keys
- 点击 new SSH keys
- title =>
<你的ip SSH keys>
仅为示例命名 - key type => 默认选择Authentication Keys
- keys => 黏贴上面公钥文件里的内容到这边,保存完成。
2.1.4 服务器拉取项目
# root 目录新建 home文件夹
sudu mkdir /root/home
# 进入 home 文件夹
cd /root/home
# 免登录拉取前端项目源码,注意服务器首次拉取github会提示确认指纹,点击确定。
# 指纹一般存于 ~/.ssh/known_hosts
git clone https://github.com/wanglei1900/React18_Vite4_Admin.git
2.2 安装 nginx 和 jenkins 镜像
docker 拉取镜像
# 拉取nginx
docker pull nginx
# 拉取jenkins
docker pull jenkins/jenkins:lts
# 查看镜像是否安装成功
docker images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest fffffc90d343 2 weeks ago 188MB
# jenkins/jenkins lts 5dea1f4edf69 3 weeks ago 470MB
# hello-world latest d2c94e258dcb 14 months ago 13.3kB
创建docker 相关目录
# 创建docker的相关目录
mkdir -p /docker/{compose,jenkins_home,nginx/conf,html/origin}
# 创建docker-compose.yml配置文件
cd /docker/compose
# 具体配置内容见下面
touch docker-compose.yml
# 创建nginx.conf配置文件
cd /docker/nginx/conf
# 具体配置内容见下面
touch nginx.conf
最终文件结构
+ docker
+ compose
- docker-compose.yml // docker-compose配置
+ html // 各环境代码目录(实际项目可能不在同一目录)
+ origin
+ master // master分支代码,后续会自动创建
+ dev // dev分支代码,后续会自动创建
+ 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;
#这里两个环境使用一个nginx.conf文件,也可以单独分开来
#pro环境
server {
#监听的端口
listen 8001;
server_name localhost;
#设置日志
# access_log logs/dev.access.log main;
#定位到index.html
location / {
#linux下HTML文件夹,就是你的前端项目文件夹
root /usr/share/nginx/html/origin/master/dist;
# root /home/html/dev/dist;
#输入网址(server_name:port)后,默认的访问页面
index index.html;
try_files $uri $uri/ /index.html;
}
}
#dev环境
server {
#监听的端口
listen 8002;
server_name localhost;
#设置日志
# access_log logs/sit.access.log main;
#定位到index.html
location / {
#linux下HTML文件夹,就是你的前端项目文件夹
root /usr/share/nginx/html/origin/dev/dist;
# root /home/html/dev/dist;
#输入网址(server_name:port)后,默认的访问页面
index index.html;
try_files $uri $uri/ /index.html;
}
}
# include /etc/nginx/conf.d/*.conf;
}
docker-compose.yml配置
8080:对应jenkins 容器
8001:对应master环境 容器
8002:对应dev环境 容器
- 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 是容器的名称。
# 新版docker-compose不用写自动引用最新版本
# 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目录
- /usr/local/bin/docker-compose:/usr/local/bin/docker-compose
docker_nginx_pro: # nginx-pro环境
restart: always
image: nginx
container_name: nginx_pro
ports:
- 8001:8001
volumes:
- /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- /docker/html:/usr/share/nginx/html # 宿主机/docker/html 映射docker容器内的/usr/share/nginx/html
- /docker/nginx/logs:/var/log/nginx
docker_nginx_dev: # nginx-dev环境
restart: always
image: nginx
container_name: nginx_dev
ports:
- 8002:8002
volumes:
- /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- /docker/html:/usr/share/nginx/html # 宿主机/docker/html 映射docker容器内的/usr/share/nginx/html
- /docker/nginx/logs:/var/log/nginx
2.3 启动docker
# 启动docker
systemctl start docker
# 启动docker-compose
# 这里我们使用docker-compose.yml配置文件启动,所以不需要另外手动创建容器里,这也是为什么使用docker-compose.yml配置文件的原因
cd /docker/compose/
docker-compose up -d
# 输出以下内容
# WARN[0000] /docker/compose/docker-compose.yml: `version` is obsolete
# NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
# jenkins jenkins/jenkins:lts "/usr/bin/tini -- /u…" docker_jenkins 16 seconds ago Up 15 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp
# nginx_pro nginx "/docker-entrypoint.…" docker_nginx_pro 16 seconds ago Up 15 seconds 80/tcp, 0.0.0.0:8001->8001/tcp, :::8001->8001/tcp
# nginx_dev nginx "/docker-entrypoint.…" docker_nginx_dev 16 seconds ago Up 15 seconds 80/tcp, 0.0.0.0:8002->8002/tcp, :::8002->8002/tcp
2.4 检查nginx配置
测试nginx配置是否正确
# 目录下创建index.html文件
cd /docker/html/dev/dist
# 黏贴下面的html内容
vim index.html
<!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>
浏览器打开 ip:8001
可以看到welcome to Nginx 页面,证明nginx配置正确。
2.5 登录jenkins web端
- 登录
服务器地址:8080/jenkins_home
进入jenkins 的web端 - 去服务器上
vim /docker/jenkins_home/secrets/initialAdminPassword
复制初始密码 - 回到jenkins 的web端,输入初始密码。
- 登录后,安装推荐的插件(不要选择自定义插件)。
- 注册管理员账号完成登录
3 Jenkins 配置
第一遍操作,建议完全照抄。后面再遇到在自己拓展
3.1 安装必要插件
dashbord => Manage Jenkins(系统管理) => plugin(插件) => Available plugins
- local (如果界面是全英文还需要安装中文插件,如果已经有部分中文请跳过这个插件)
- Publish Over SSH 配置远程服务器
- NodeJS 服务端打包
- Git Parameter 参数化构建
3.2 Publish Over SSH 配置远程服务器
dashbord => Manage Jenkins(系统管理) => 系统配置 => ctrl F Publish over SSH
- 找到SSH Servers 并新增
- 填写 name 自定义填写名称(示例:jenkins_node)
- 填写 Hostname 服务器ip地址
- 填写 Username 服务器登录名称
- 填写 Remote Directory 登录后访问的地址
- 点击 高级 弹出额外配置
- 勾选 Use password authentication, or use a different key。
- 出现 Passphrase / Password,输入服务器登录密码
- 检查默认port 是否为22 。一般不用修改
- 点击 Test Configuration,显示success则 jenkins 配置SSH远程服务器成功
- 点击 应用按钮 点击 保存按钮
3.3 NodeJS配置
dashbord => Manage Jenkins(系统管理) => 全局工具配置 => ctrl F NodeJS 安装
- 新增 NodeJS
- 填写 别名 自定义填写名称,照抄下面的版本号(示例:NodeJS 22.4.1)
- 选择 NodeJS版本,之前已经下载好插件,这里应该自动跳出来的
- 点击 应用按钮 点击 保存按钮
3.4 凭据配置
dashbord => Manage Jenkins(系统管理) => 凭据管理 => 全局 按钮下拉选择 添加凭据
此处以添加github仓库为例
- 填写 用户名 github用户名
- 填写 密码 github密码
- 填写 描述 github 登录凭证
- 点击 创建 按钮
3.5 创建任务
dashbord => 点击屏幕中间 create a job
- 名称填写 你的项目名字(示例:React_PC)
- 点击 构建一个自由风格的软件项目
- 点击 确定按钮
- 点击 github项目 填写项目URL
- 源码管理 勾选git
- 填写 Repository URL 这里用github仓库的http地址
- Credentials 选择之前配置的凭证
- 指定分支暂时填写 */dev
- 点击 应用按钮 点击 保存按钮
- 点击立即构建按钮
3.6 Github webHooks 配置
github代码仓库 => Settings => Webhooks
- 填写 Payload URL http://ip:8080/jenkins_home/github-webhook/
- 选择 Content type application/json
- 点击 add webbhook 按钮
jenjins => dashbord => 点击之前创建的项目 => 配置
- 点击 构建触发器
- 勾选
GitHub hook trigger for GITScm polling
3.7 构建环境
ctrl F 构建环境
- 点击 构建环境
- 勾选
Provide Node & npm bin/ folder to PATH
- 选择 我们刚刚配置过的nodejs版本
- 点击 应用按钮
3.8 Build Steps
ctrl F Build Steps
- 增加构建步骤
- 选择
Execute NodeJS script
- 选择 我们刚刚配置过的nodejs版本
- 点击 应用、保存按钮
- 构建项目测试目前为止步骤是否正确,并学会手动构建流程
3.9 执行sell
dashbord => 点击之前创建的项目 => 配置 => ctrl F Build Steps
- 增加构建步骤
- 选择 执行shell
- 填写下面bash指令
- 点击 应用、保存按钮
- 构建项目测试,控制台输出应该能看到打印nodejs版本和npm 版本
#!/bin/bash
echo "Node.js 版本:" node -v
echo "npm 版本:" npm -v
echo $PATH
dashbord => 点击之前创建的项目 => 配置 => ctrl F 执行sell
- 填写下面bash指令
- 点击 应用、保存按钮
- 构建项目测试,控制台输出应该能看到依赖安装成功和打包成功字样
ls /docker/html/origin/master/dist/
应该还能查看到dist文件夹下的前端打包项目- 上面步骤成功后,远程仓库master分支push一次测试提交,jenkins会触发自动构建
- 如果没有自动构建成功,可能是网络问题。先去github查看该webhook触发了没有,再去jenkins web端 Github Hook log选项查看推送日志
#!/bin/bash
# 查看信息
echo "Node.js 版本:" node -v
echo "npm 版本:" npm -v
# 安装依赖
npm install || { echo "npm install 失败"; exit 1; }
echo "依赖安装成功"
# 打包构建
rm -rf ./dist # 清理旧的 dist 目录
# 这里是用你项目里的打包脚本,比如你的可能是npm run build
npm run build:jenkins || { echo "构建失败"; exit 1; }
echo "构建成功"
3.10 自动部署到对应环境
dashbord => 点击之前创建的项目 => 配置 => ctrl F 执行sell
- 填写下面bash指令
- 点击应用按钮
#!/bin/bash
# 查看信息
echo "当前打包项目 Github 仓库分支:"
echo "GIT_BRANCH" $GIT_BRANCH
echo "Node.js 版本:" node -v
echo "npm 版本:" npm -v
# 安装依赖
npm install || { echo "npm install 失败"; exit 1; }
echo "依赖安装成功"
# 打包构建
rm -rf ./dist # 清理旧的 dist 目录
# 这里是用你项目里的打包脚本,比如你的可能是npm run build
npm run build:jenkins || { echo "构建失败"; exit 1; }
echo "构建成功"
if [ ! -d "./dist" ]; then
echo "构建未生成 dist 目录"
exit 1
fi
echo "开始打包..."
rm -rf dist.tar # 每次构建删除已存在的dist压缩包
tar -zcvf dist.tar ./dist # 将dist文件压缩成dist.tar
if [ $? -eq 0 ]; then
echo "打包成功"
else
echo "打包失败"
exit 1
fi
echo "构建和打包完成"
echo $PATH
这里先明确一点,本次部署到宿主机的环境地址,为什么路径写成这样,是为了后续参数化构建的时候,可以动态获取分支名,然后根据分支名动态部署到对应环境。
生产环境 master分支部署到 /docker/html/origin/master/dist/
开发环境 dev分支部署到 /docker/html/origin/dev/dist/
- 增加构建步骤
- 选择
Send files or execute commands over SSH
- 填写 服务名称Name 为 jenkins_node
- 填写 源文件Source 为 files dist.tar
- 填写 目标路径Remote directory 为 /dokcer/html/origin/dev
- 填写 执行脚本Exec command 如下
cd /docker/html/dev
rm -rf dist/
tar zxvf dist.tar
rm dist.tar
让我们捋一捋以上步骤做了哪些事情?
正式生产 宿主机和docker大概率不是部署在一台服务器上的,这里我们自己学习使用了一台服务器
- 删除docker容器内,之前打包的dist下所有文件
- docker容器内,打包本次源文件为dist.tar
- 连接到宿主机(远程服务器),放在
/docker/html/origin/dev
文件价下 - 解压缩dist.tar,并且删除压缩包
- Source files:准备发送的文件,该文件是相对于这个项目的workspace目录。例如要发送/docker/jenkins_home/workspace/gitlab_web/dist.- tar到目标目录,则设置Source files为dist.tar
- Remove prefix:目标文件前缀添加,例如要操作src下面的某个文件,就设置成src,本案例是跟目录,无需设置
- Remote directory:目标目录,本案例要复制到dev环境下的dist文件,/docker/html/dev
- Exec command:最后执行的命令,可在这里进行解压,删除,复制等操作
到这里,访问你的项目地址 localhost:8082
就可以看到你打包好的前端项目了
3.11 参数化构建
前面我们都是手动构建dev分支,现在我们使用参数化构建,可以动态获取分支名,然后根据分支名动态部署到对应环境
dashbord => 点击之前创建的项目 => 配置 => ctrl F 参数化构建过程
- 添加参数,选择GIT参数
- 填写 名称 为
BRANCH
- 填写 描述 为 生产环境
origin/master
开发环境origin/dev
- 填写 默认值 为
origin/dev
ctrl F Branches to build
- 填写 指定分支 由之前的
origin/dev
变成$BRANCH
ctrl F Transfer Set
- 填写 Remote directory 由之前的
/docker/html/origin/dev
变成/docker/html/${GIT_BRANCH}
- 填写 Exec command 如下
cd /docker/html/${GIT_BRANCH}
rm -rf dist/
tar zxvf dist.tar
rm dist.tar
这里需要强调几点
- 由于我们本次jenkins构建使用的自由风格,相对pipeline(流水线)的方式没有那么灵活,比如webhook推送不同分支,如果不使用参数化构建则必须写死分支名,使用了参数化构建也只能使用默认值,本次是
origin/dev
即dev分支 - 为什么前面项目的路径要用
origin/*
,因为构建分支是动态获取当前仓库的分支的,默认是带origin前缀的 - 目前配置能达到的效果是dev分支会可以根据webhook自动推送到jenkins触发自动更新,master主分支则需要手动触发更新。