手把手教你部署前端專案CI/CD Jenkins 篇

wanglei1900發表於2024-07-20

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 優點

  1. 提高開發效率:
    • Jenkins 自動化了構建、測試和部署等任務,減少了人工操作的時間和錯誤,使開發團隊能夠更專注於編寫高質量的程式碼。
    • 自動化流程縮短了反饋週期,使問題能夠更快地被發現和修復。
  2. 增強軟體質量:
    • 持續的整合和測試有助於在早期發現並修復問題,避免問題累積到後期造成更大的影響。
    • Jenkins 支援多種測試框架,可以執行單元測試、整合測試等多種型別的測試,確保軟體的全面質量。
  3. 支援快速迭代:
    • Jenkins 使得軟體釋出過程更加高效和靈活,支援頻繁的釋出和迭代,滿足現代軟體開發快速變化的需求。
    • 自動化部署減少了人為錯誤,提高了部署的可靠性和穩定性。
  4. 易於配置和擴充套件:
    • Jenkins 提供了豐富的外掛生態系統,使用者可以根據需要選擇和安裝各種外掛來擴充套件其功能。
    • 透過簡單的配置和指令碼編寫,使用者可以輕鬆定義自己的構建和部署流程。
  5. 社群支援和文件豐富:
    • Jenkins 是一個開源專案,擁有龐大的使用者社群和活躍的開發者貢獻者,使用者可以獲得及時的技術支援和解決方案。
    • Jenkins 的官方文件和社群資源也非常豐富,有助於使用者快速上手和解決問題。
  6. 跨平臺支援:
    • 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為例)

  1. 點選使用者頭像,進入 settings 頁面
  2. 點選左側選單,進入 SSH and GPG keys
  3. 點選 new SSH keys
  4. title => <你的ip SSH keys> 僅為示例命名
  5. key type => 預設選擇Authentication Keys
  6. 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端

  1. 登入 伺服器地址:8080/jenkins_home 進入jenkins 的web端
  2. 去伺服器上 vim /docker/jenkins_home/secrets/initialAdminPassword 複製初始密碼
  3. 回到jenkins 的web端,輸入初始密碼。
  4. 登入後,安裝推薦的外掛(不要選擇自定義外掛)。
  5. 註冊管理員賬號完成登入



3 Jenkins 配置

第一遍操作,建議完全照抄。後面再遇到在自己擴充

3.1 安裝必要外掛

dashbord => Manage Jenkins(系統管理) => plugin(外掛) => Available plugins

  1. local (如果介面是全英文還需要安裝中文外掛,如果已經有部分中文請跳過這個外掛)
  2. Publish Over SSH 配置遠端伺服器
  3. NodeJS 服務端打包
  4. Git Parameter 引數化構建


3.2 Publish Over SSH 配置遠端伺服器

dashbord => Manage Jenkins(系統管理) => 系統配置 => ctrl F Publish over SSH

  1. 找到SSH Servers 並新增
  2. 填寫 name 自定義填寫名稱(示例:jenkins_node)
  3. 填寫 Hostname 伺服器ip地址
  4. 填寫 Username 伺服器登入名稱
  5. 填寫 Remote Directory 登入後訪問的地址
  6. 點選 高階 彈出額外配置
  7. 勾選 Use password authentication, or use a different key。
  8. 出現 Passphrase / Password,輸入伺服器登入密碼
  9. 檢查預設port 是否為22 。一般不用修改
  10. 點選 Test Configuration,顯示success則 jenkins 配置SSH遠端伺服器成功
  11. 點選 應用按鈕 點選 儲存按鈕



3.3 NodeJS配置

dashbord => Manage Jenkins(系統管理) => 全域性工具配置 => ctrl F NodeJS 安裝

  1. 新增 NodeJS
  2. 填寫 別名 自定義填寫名稱,照抄下面的版本號(示例:NodeJS 22.4.1)
  3. 選擇 NodeJS版本,之前已經下載好外掛,這裡應該自動跳出來的
  4. 點選 應用按鈕 點選 儲存按鈕



3.4 憑據配置

dashbord => Manage Jenkins(系統管理) => 憑據管理 => 全域性 按鈕下拉選擇 新增憑據

此處以新增github倉庫為例

  1. 填寫 使用者名稱 github使用者名稱
  2. 填寫 密碼 github密碼
  3. 填寫 描述 github 登入憑證
  4. 點選 建立 按鈕



3.5 建立任務

dashbord => 點選螢幕中間 create a job

  1. 名稱填寫 你的專案名字(示例:React_PC)
  2. 點選 構建一個自由風格的軟體專案
  3. 點選 確定按鈕
  4. 點選 github專案 填寫專案URL
  5. 原始碼管理 勾選git
  6. 填寫 Repository URL 這裡用github倉庫的http地址
  7. Credentials 選擇之前配置的憑證
  8. 指定分支暫時填寫 */dev
  9. 點選 應用按鈕 點選 儲存按鈕
  10. 點選立即構建按鈕




3.6 Github webHooks 配置

github程式碼倉庫 => Settings => Webhooks

  1. 填寫 Payload URL http://ip:8080/jenkins_home/github-webhook/
  2. 選擇 Content type application/json
  3. 點選 add webbhook 按鈕

jenjins => dashbord => 點選之前建立的專案 => 配置

  1. 點選 構建觸發器
  2. 勾選 GitHub hook trigger for GITScm polling




3.7 構建環境

ctrl F 構建環境

  1. 點選 構建環境
  2. 勾選 Provide Node & npm bin/ folder to PATH
  3. 選擇 我們剛剛配置過的nodejs版本
  4. 點選 應用按鈕

3.8 Build Steps

ctrl F Build Steps

  1. 增加構建步驟
  2. 選擇 Execute NodeJS script
  3. 選擇 我們剛剛配置過的nodejs版本
  4. 點選 應用、儲存按鈕
  5. 構建專案測試目前為止步驟是否正確,並學會手動構建流程


3.9 執行sell

dashbord => 點選之前建立的專案 => 配置 => ctrl F Build Steps

  1. 增加構建步驟
  2. 選擇 執行shell
  3. 填寫下面bash指令
  4. 點選 應用、儲存按鈕
  5. 構建專案測試,控制檯輸出應該能看到列印nodejs版本和npm 版本
#!/bin/bash
echo "Node.js 版本:"  node -v
echo "npm 版本:"  npm -v
echo $PATH

dashbord => 點選之前建立的專案 => 配置 => ctrl F 執行sell

  1. 填寫下面bash指令
  2. 點選 應用、儲存按鈕
  3. 構建專案測試,控制檯輸出應該能看到依賴安裝成功和打包成功字樣
  4. ls /docker/html/origin/master/dist/ 應該還能檢視到dist資料夾下的前端打包專案
  5. 上面步驟成功後,遠端倉庫master分支push一次測試提交,jenkins會觸發自動構建
  6. 如果沒有自動構建成功,可能是網路問題。先去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

  1. 填寫下面bash指令
  2. 點選應用按鈕
#!/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/

  1. 增加構建步驟
  2. 選擇 Send files or execute commands over SSH
  3. 填寫 服務名稱Name 為 jenkins_node
  4. 填寫 原始檔Source 為 files dist.tar
  5. 填寫 目標路徑Remote directory 為 /dokcer/html/origin/dev
  6. 填寫 執行指令碼Exec command 如下
cd /docker/html/dev
rm  -rf   dist/
tar zxvf dist.tar
rm dist.tar

讓我們捋一捋以上步驟做了哪些事情?
正式生產 宿主機和docker大機率不是部署在一臺伺服器上的,這裡我們自己學習使用了一臺伺服器

  1. 刪除docker容器內,之前打包的dist下所有檔案
  2. docker容器內,打包本次原始檔為dist.tar
  3. 連線到宿主機(遠端伺服器),放在/docker/html/origin/dev檔案價下
  4. 解壓縮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 引數化構建過程

  1. 新增引數,選擇GIT引數
  2. 填寫 名稱 為 BRANCH
  3. 填寫 描述 為 生產環境 origin/master 開發環境 origin/dev
  4. 填寫 預設值 為 origin/dev

ctrl F Branches to build

  1. 填寫 指定分支 由之前的 origin/dev 變成 $BRANCH

ctrl F Transfer Set

  1. 填寫 Remote directory 由之前的 /docker/html/origin/dev 變成 /docker/html/${GIT_BRANCH}
  2. 填寫 Exec command 如下
cd /docker/html/${GIT_BRANCH}
rm  -rf   dist/
tar zxvf dist.tar
rm dist.tar

這裡需要強調幾點

  1. 由於我們本次jenkins構建使用的自由風格,相對pipeline(流水線)的方式沒有那麼靈活,比如webhook推送不同分支,如果不使用引數化構建則必須寫死分支名,使用了引數化構建也只能使用預設值,本次是origin/dev即dev分支
  2. 為什麼前面專案的路徑要用origin/*,因為構建分支是動態獲取當前倉庫的分支的,預設是帶origin字首的
  3. 目前配置能達到的效果是dev分支會可以根據webhook自動推送到jenkins觸發自動更新,master主分支則需要手動觸發更新。

相關文章