概述
一次在跟同事討論中忽然萌生了自己手動搭建一套上線系統(tǒng)的想法,第一次上手撩轰,所以選用了業(yè)內比較成熟的方案-jenkins示弓。之前只是用過jenkins進行過一些操作,并未自己從0到1完成搭建,本文記錄下自己整個過程中的遇到的一些問題與解決方案撒犀。
準備知識
linux安裝軟件的方式(知道的可以直接跳過本節(jié)):
一般有三種方式:Linux系統(tǒng)中安裝軟件的幾種方式
源碼包安裝:
下載源碼 -> 解壓 -> 運行configure配置等 -> make 編譯 -> make install 安裝rpm包安裝:
RedHat Package Manager福压,由紅帽公司提出,建議統(tǒng)一的數(shù)據(jù)庫文件或舞,詳細記錄軟件包的安裝荆姆、卸載等變化信息,能夠自動分析軟件包依賴關系映凳。用RPM工具可以將二進制程序進行打包胆筒,包被稱為RPM包。RPM包并不是跨平臺的。yum源安裝:
Yellow dog Updater, Modified, 是一個在Fedora和RedHat以及CentOS中的Shell前端軟件包管理器仆救∈愫停基于RPM包管理,能夠從指定的服務器自動下載RPM包并且安裝彤蔽,可以自動處理依賴性關系摧莽,并且一次安裝所有依賴的軟件包,無須繁瑣地一次次下載顿痪、安裝
linux啟動服務管理兩種方式service和systemctl:
service作為啟動init進程的主命令存在一些歷史缺陷镊辕,Systemd就是他的升級版,他為系統(tǒng)的啟動和管理提供一套完整的解決方案蚁袭。Systemd 并不是一個命令征懈,而是一組命令,涉及到系統(tǒng)管理的方方面面:systemctl是 Systemd 的主命令揩悄,用于管理系統(tǒng)卖哎。
方案一: docker安裝
docker作為目前比較火的一個名詞,自己一直沒機會使用虏束,了解到jenkins可以通過docker來安裝棉饶,于是,從docker開始镇匀,進入了采坑之旅照藻。
centos上安裝docker
自己的服務器為阿里云,版本如下(以下所有操作均是基于此臺機器):
uname -a
Linux iZ2zeb34hcp1ui0lowu4atZ 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
docker的安裝還是比較簡單的汗侵,參照阿里云官方文檔幾分鐘搞定:
添加yum源
# yum install epel-release –y // 安裝并啟用 EPEL 源幸缕。
# yum clean all
# yum list
安裝并運行Docker
# yum install docker-io –y
# systemctl start docker // 啟動docker服務
檢查安裝結果。
# docker info
關于 docker
"Docker" 的本質其實是解決了應用服務的 "隱私" 問題晰韵,實現(xiàn)進程发乔、內存、文件雪猪、網(wǎng)絡之間相互隔離栏尚。也可以簡單把 Docker 理解成一種虛擬機,很多應用服務可以像桌面軟件那樣一鍵安裝只恨,免部署和環(huán)境配置译仗。
前端為什么需要使用 Docker?
- 對于 Full Stack 工程師官觅。Docker 可以提供一種簡單輕便的服務器編程環(huán)境纵菌,而且可以隨用隨刪、降低環(huán)境配置成本休涤。
- 很多 FE 日常工作中需要跟 Nginx咱圆、MongoDB、MySQL 等服務器應用打交道。用 Docker 可以很容易部署一個測試環(huán)境序苏,學習和倒騰.
Docker 中的三個概念
Container - 容器
Image - 鏡像
Registry - 倉庫
可以像下面這張圖來類比:
docker 安裝jenkins
使用下面的 docker run 命令運行 jenkinsci/blueocean 鏡像作為Docker中的一個容器(如果本地沒有鏡像手幢,這個命令會自動下載):
docker run \
--rm \
-u root \
-p 8080:8080 \
-v jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME":/home \
jenkinsci/blueocean
此時就進入jenkins安裝流程,具體參照此處jenkins官網(wǎng)安裝示例:
啟動服務杠览,輸入密碼繼續(xù)(安裝過程忘記截圖了弯菊,以下圖收集于網(wǎng)絡);
啟動后安裝推薦插件:
安裝插件完成后踱阿,進入設置初始賬號密碼:
設置完畢即可進入jenkins主頁:
可能遇到的問題:
1.訪問接口出現(xiàn):Error:403 No valid crumb was included in the request
解決:關閉安全設置里面的-防止款站點請求偽造選項管钳,具體參照此處。
jenkins 配置nodejs
進入插件管理软舌,安裝Nodejs Plugin:
進入全局工具配置才漆,配置項目中會用到的nodejs版本,可以配置多個
jenkins + github 配置項目
開始創(chuàng)建項目佛点,選擇自由風格:
開始配置項目(以github私有項目為例)
此處我們填寫完git地址后需要添加憑證醇滥,添加其他個人私有密鑰不知道為啥一直在下拉菜單中選不到,添加用戶名密碼則可以超营,暫時還不知道啥原因~
觸發(fā)器中我們選擇hook鸳玩,push之后,自動觸發(fā)構建
此處在github中也要做對應配置才可出發(fā)hook功能:
也可以自定義配置演闭,譬如不跟,push 與 merge時觸發(fā),選擇Let me select individual events米碰,勾選以下選項:
Deployments
Deployment statuses
Pull requests
Pushes
接下來我們配置拉去完代碼后需要執(zhí)行的腳本窝革,centos上我們選擇shell腳本:
至此,一個簡單的配置已經(jīng)完畢吕座,可以進行自動構建測試了E耙搿!
doker 模式下遇到的問題
我們隨便改一點東西吴趴,向master分支push代碼漆诽,觸發(fā)構建;構建任務正常觸發(fā)锣枝,但執(zhí)行到shell腳本時卻出現(xiàn)了異常:
[test-project] $ /bin/sh -xe /tmp/jenkins6958996694563138608.sh
+ node -v
/tmp/jenkins6958996694563138608.sh: line 2: node: not found
Build step 'Execute shell' marked build as failure
Finished: FAILURE
通過docker exec -it 4c0fd5e5f2c5 bash
命令進入容器內部bash訪問:
cd /var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs_10.15.3/
nodejs文件夾存在厢拭,但執(zhí)行 bin/node -v
報錯,提示 : No such file or directory
解決:終于在stackoverflow 翻到一個大佬的回答惊橱,原因如下:
This happens because the image doesn't contain libstdc++.so.6 as needed by nodejs
In other words, node: not found does not mean node is not installed (it is, it is executable and found in the $PATH).
It means one of node dependencies is not found.
我們通過蚪腐,手動安裝:
apk add --no-cache --update nodejs nodejs-npm
拉取的是Node.js Alpine 鏡像箭昵,這個鏡像做個優(yōu)化税朴,并沒有內置npm包(本人驗證 10.14.2是沒有的)需要手動再安裝npm;
再次構建
/var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs_10.15.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
+ node -v
v10.14.2
+ npm -v
6.4.1
Finished: SUCCESS
執(zhí)行成功,考慮到調試困難和后續(xù)還需要全局安裝vue-cli3.0 于是決定放棄docker方式正林,改用RPM方式再次安裝jenkins
RPM方式安裝jenkins
因為Jenkins是基于java的,所以需要先安裝jdk
安裝java環(huán)境
sudo yum install java
下載rpm包:
通過RedHat Linux RPM packages for Jenkins網(wǎng)站下載所需的rpm包泡一,我安裝的是jenkins-2.195-1.1.noarch.rpm
安裝jenkins
sudo rpm -ih jenkins-2.73.2-1.1.noarch.rpm
自動安裝完成之后:
/usr/lib/jenkins/jenkins.war WAR包
/etc/sysconfig/jenkins 配置文件
/var/lib/jenkins/ 默認的JENKINS_HOME目錄
/var/log/jenkins/jenkins.log Jenkins日志文件
啟動jenkins
sudo systemctl start jenkins.service
之后的操作與上一步docker安裝方式基本一摸一樣,唯一區(qū)別的就是器nodejs安裝方式
node調用centos全局配置
取消構建環(huán)境配置下的:Provide Node & npm bin/ folder to PATH 即可
完善整個構建流程
其實主要就是shell腳本的編寫觅廓,項目中配置如下(新建了另外一個項目vue-build-project):
node -v
echo "開始安裝依賴..."
npm install
echo "開始打包..."
npm run build
base="/home/upload-upyun"
dest="vue-build-project"
cp -r ./dist ${base}/${dest}
echo "開始上傳..."
node ${base}/index.js ${dest}
rm -rf ${base}/${dest}
/home/upload-upyun/index.js
內容如下:
const argPath=(process.argv.splice(2));//通過透傳參數(shù)獲取文件夾目錄
const uploadFolder=(argPath[0]?argPath[0]:'').replace(/^--/g,'');
const rootPath=(argPath[1]?argPath[1]:'').replace(/^--/g,''); //靜態(tài)資源服務器的上傳根路徑
const fs=require("fs");
const path=require("path");
const upyun = require("upyun/dist/upyun.common");
const localFileArr=[];
function readPathSync (p) {
if(!fs.existsSync(p)) return;
const stat = fs.statSync(p)
if (stat.isDirectory()) {
const ls = fs.readdirSync(p).map(file => path.join(p, file))
for (let i = 0; i < ls.length; i++) {
readPathSync(ls[i])
}
} else {
localFileArr.push(p)
}
}
const ypyConf={
"serviceName" : "xxx",
"operatorName" : "xxx",
"password" :"xxx",
"remotePath" rootPath,
};
// 需要填寫本地路徑鼻忠,云存儲路徑
const remoteRoot = ypyConf.remotePath;
const upService = new upyun.Service(ypyConf.serviceName, ypyConf.operatorName, ypyConf.password);
const upClient = new upyun.Client(upService);
const prefix=(path.join(__dirname))
// 上傳參數(shù)
// console.log(upClient)
function uploadFile(localFile){
const remoteFile= remoteRoot+(localFile.replace(prefix,'')).split(path.sep).join("/")
upClient.putFile(remoteFile, fs.createReadStream(localFile), {
'Date': new Date(),
'Content-Length': fs.statSync(localFile).size,
}).then(res => {
if (res) {
console.log(remoteFile+":上傳成功")
} else {
console.log(remoteFile+":上傳失敗")
}
}).catch(err => {
console.log("上傳出現(xiàn)異常!")
})
}
readPathSync(path.join(__dirname,uploadFolder));
localFileArr.map((item)=>{
uploadFile(item)
})
最終構建效果
16:21:48 [vue-build-project] $ /bin/sh -xe /tmp/jenkins8111626610056806132.sh
16:21:48 + node -v
16:21:48 v10.15.3
16:21:48 + echo 開始安裝依賴...
16:21:48 開始安裝依賴...
16:21:48 + echo 開始打包...
16:21:48 開始打包...
16:21:48 [vue-build-project] $ /bin/sh -xe /tmp/jenkins6507915611034625552.sh
16:21:48 + cd /var/lib/jenkins/workspace
16:21:48 + cd vue-build-project
16:21:48 + cp -r ./dist /home/upload-upyun/vue-build-project
16:21:48 + echo 開始上傳...
16:21:48 開始上傳...
16:21:48 + node /home/upload-upyun/index.js vue-build-project
16:21:48 /test/vue-build-project/favicon.ico:上傳成功
16:21:48 /test/vue-build-project/index.html:上傳成功
16:21:48 /test/vue-build-project/static/js/app.6e3674b9.js:上傳成功
16:21:48 /test/vue-build-project/static/css/app.e2713bb0.css:上傳成功
16:21:48 /test/vue-build-project/static/img/logo.82b9c7a5.png:上傳成功
16:21:48 /test/vue-build-project/static/js/chunk-vendors.a1771d7d.js:上傳成功
16:21:48 + rm -rf /home/upload-upyun/vue-build-project
16:21:48 Finished: SUCCESS
遇到的問題
在npm install 這一步出現(xiàn)了一個問題困擾了我兩天杈绸,執(zhí)行過程中jenkins進程會忽然掛掉,一直以為是配置的問題帖蔓,后來忽然意識到可能是被centos給干掉了,查看日志:
grep "Out of memory" /var/log/messages
得到日志如下:
時間節(jié)點與我構建時完全相同MАK芙俊!劫侧!埋酬,坑啊I斩啊P赐住!查閱資料明白是觸發(fā)了 linux 的 OOM killer 機制:
于是乎重啟服務器审姓,只開啟Jenkins珍特,重新構建。我的阿里云服務器可憐的1G內存在構建過程中的內存變化如下:
所以邑跪,都是貧窮惹的禍~
根據(jù)git diff 選擇性上傳文件
有些情況下次坡,我們只需要上傳變動的文件,并不需要上傳所有文件画畅,此時shell部分可以這樣編寫:
diff=`git diff --name-only HEAD~1 HEAD~0`
base="/home/upload-upyun"
dest="static-demo"
rm -rf ${base}/${dest}
# 創(chuàng)建目標文件夾
mkdir -p ${base}/${dest}
# 循環(huán)復制變動文件(被刪除文件忽略砸琅,只做新增與覆蓋)
for line in $diff
do
if [ -f $line ];then
cp --parents -afv $line ${base}/${dest}
fi
done
echo "開始上傳..."
node ${base}/index.js --${dest}
rm -rf ${base}/${dest}
jenkins的其他安裝方式
也可以通過以下方式安裝Jenkins,本文不再嘗試轴踱,效果與rpm方式理論應該等同症脂,具體可參照:
- yum進行安裝:jenkins yum 安裝
- war包安裝:WAR包方式安裝Jenkins
Generic Webhook Trigger Plugin
上述的webhook是通過github-plugin 進行觸發(fā),但對于其他git托管淫僻,如騰訊云诱篷,阿里云,碼云等其他第三方git服務提供平臺雳灵,并沒有對應的插件棕所,此時,就輪到 Generic Webhook Trigger Plugin
插件登場:
首先悯辙,進行安裝琳省,過程省略迎吵。安裝完畢后在項目配置頁面可以看到多出一個配置:
我們需要拿到webhook的地址,根據(jù)官方文檔可以知道针贬,我們需要拿到Jenkins管理員的API token, 進入管理用戶頁面击费,選擇admin生成一個:
最終得到的webhook地址為:http://xxx:8080/generic-webhook-trigger/invoke?token=11d85c3af55e018axxxxxx
, 這里我們以騰訊云為例,配置下webhook:
我們再回到項目配置頁面桦他,需要對該插件做一些配置, 默認我們配置好webhook后蔫巩,所有配置過該webhook的頁面,所有項目與分支的任意一個變動都可以觸發(fā)所有項目的構建快压,這顯然不是我想要的圆仔,我們需要做一些區(qū)分,參照此文章:
- 區(qū)分分支
- 區(qū)分項目(不同服務提供商字段會有差異蔫劣,騰訊云是在repository下)
- 配置token
- 過濾字段匹配項目(此處為每個項目特有配置荧缘,區(qū)分好項目與分支)
我們手動構建一次可以看到webhook返回結果如下:
{
"ref": "refs/heads/dev",
"before": "6b53d96f0ee3dc5b1b60d389105d330641ac1612",
"after": "9370030232017ff649a070ae6bf1bdd7522a037c",
"created": false,
"deleted": false,
"compare": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/compare/6b53d96f0ee3d...9370030232017",
"commits": [{
"id": "9370030232017ff649a070ae6bf1bdd7522a037c",
"distinct": false,
"message": "fix:添加文件\n",
"timestamp": 1569831379000,
"url": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/commit/9370030232017ff649a070ae6bf1bdd7522a037c",
"author": {
"name": "wangxx",
"email": "m.h.wang@foxmail.com",
"username": "wangxx"
},
"committer": {
"name": "wangxx",
"email": "m.h.wang@foxmail.com",
"username": "wangxx"
},
"added": ["dofun.png"],
"removed": [],
"modified": []
}],
"head_commit": {
"id": "9370030232017ff649a070ae6bf1bdd7522a037c",
"distinct": false,
"message": "fix:添加文件\n",
"timestamp": 1569831379000,
"url": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/commit/9370030232017ff649a070ae6bf1bdd7522a037c",
"author": {
"name": "wangxx",
"email": "m.h.wang@foxmail.com",
"username": "wangxx"
},
"committer": {
"name": "wangxx",
"email": "m.h.wang@foxmail.com",
"username": "wangxx"
},
"added": ["dofun.png"],
"removed": [],
"modified": []
},
"pusher": {
"name": "王xx",
"email": "m.h.wang@foxmail.com",
"username": "wangxx"
},
"sender": {
"id": 2222972,
"login": "wangxx",
"avatar_url": "https://coding-net-production-static-ci.codehub.cn/e4ed6f51-7033-4c66-bb1c-d567795c88a9.jpg?imageMogr2/auto-orient/format/jpeg/cut/!640x640x0x0",
"url": "https://dev.tencent.com/api/user/key/wangxx",
"html_url": "https://dev.tencent.com/u/wangxx",
"name": "王xx",
"name_pinyin": "|wmh|wangxx"
},
"repository": {
"id": 4733490,
"name": "jenkins-autoupload",
"full_name": "wangxx/jenkins-autoupload",
"owner": {
"id": 2222972,
"login": "wangxx",
"avatar_url": "https://coding-net-production-static-ci.codehub.cn/e4ed6f51-7033-4c66-bb1c-d567795c88a9.jpg?imageMogr2/auto-orient/format/jpeg/cut/!640x640x0x0",
"url": "https://dev.tencent.com/api/user/key/wangxx",
"html_url": "https://dev.tencent.com/u/wangxx",
"name": "王xx",
"name_pinyin": "|wmh|wangxx"
},
"private": true,
"html_url": "\u003ca href\u003d\u0027https://dev.tencent.com/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e",
"description": "測試jenkins自動化部署上傳",
"fork": false,
"url": "https://dev.tencent.com/api/user/wangxx/project/jenkins-autoupload",
"created_at": 1568866930000,
"updated_at": 1568866930000,
"clone_url": "https://git.dev.tencent.com/wangxx/jenkins-autoupload.git",
"ssh_url": "git@git.dev.tencent.com:wangxx/jenkins-autoupload.git",
"default_branch": "master"
}
}
至此,配置基本完畢拦宣。
權限部分配置
通過插件 Role-based Authorization Strategy
配置完成截粗,安裝完插件,重啟Jenkins鸵隧,會發(fā)現(xiàn)配置頁面多一個選項:
首先需要在全局安全配置頁面將授權策略改為:Role-Based Strategy
進入 Manage Role 選項绸罗,配置用戶角色與所在組角色權限
進入 Assign Roles 選項,為用戶分配角色(首先需要在用戶管理板塊創(chuàng)建用戶)
具體可參考jenkins配置用戶角色權限豆瘫,根據(jù)不同權限顯示視圖珊蟀、Job
總結
docker安裝方式比較傻瓜,而且整個插件安裝的過程都比較快外驱,但是因為docker就像一個封閉的黑盒育灸,很多東西與系統(tǒng)是隔離開的(廢話,設計初衷就是這樣)昵宇,導致我這個菜鳥遇到問題想不到好的解決方案磅崭。嗯,還是RPM用起來更順手一些瓦哎,遇到問題基本還能應對砸喻。最后,不差錢的蒋譬,請上大內存服務器~
可能用到的linux命令
# 啟動docker
systemctl start docker
# 停止docker
systemctl stop docker
# 列出docker內正在運行的容器
docker ps
# 列出docker內的容器
docker ps -a
# 啟動docker內容器
docker start <id>
docker restart <id>
docker stop <id>
# 進入docker容器內部執(zhí)行命令
docker exec -it <id> bash
# 查看jenkins啟動狀態(tài)
systemctl status jenkins.service
# 查看jenkins日志
sudo tail -f /var/log/jenkins/jenkins.log 查看日志
補充:增加swap分區(qū)解決阿里云內存不足
發(fā)現(xiàn)可以擴展Swap分區(qū)割岛,即交換區(qū),系統(tǒng)在物理內存(這里應該是運行內存)不夠時犯助,與Swap進行交換癣漆,來解決內存不足的問題。設置步奏如下:
-
首先創(chuàng)建用于交換分區(qū)的文件剂买,并設置交換分區(qū)文件
dd if=/dev/zero of=/var/swap bs=1024 count=4096000
-
創(chuàng)建 swap 文件
mkswap /var/swap
-
加載這個文件
swapon /var/swap
執(zhí)行以上命令可能會出現(xiàn):“不安全的權限 0644惠爽,建議使用 0600”提示腰湾,其實已經(jīng)激活了,可以忽略疆股,修改權限:
chmod -R 0600 /var/swap
-
設置系統(tǒng)啟動時自動掛載分區(qū)
echo "/var/swap swap swap defaults 0 0" >> /etc/fstab
-
確定系統(tǒng)對SWAP分區(qū)的使用原則,當swappiness內容的值為0時倒槐,表示最大限度地使用物理內存旬痹,物理內存使用完畢后,才會使用SWAP分區(qū)讨越。當swappiness內容的值為100時两残,表示積極地使用SWAP分區(qū),并且把內存中的數(shù)據(jù)及時地置換到SWAP分區(qū)把跨。liunx默認為60人弓,此處我們設置為默認大小60
echo 60 > /proc/sys/vm/swappiness
再次運用構建命令,查看內存變化着逐,會發(fā)現(xiàn)swap區(qū)已經(jīng)得到了利用: