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主分支則需要手動觸發更新。