一、服務(wù)器環(huán)境配置
前面說(shuō)明了服務(wù)器Nginx的安裝和配置,這里稍微有些不同,但是因?yàn)榇宋氖怯肗ginx鏡像和前端鏡像頁(yè)面同時(shí)部署的方式來(lái)打包發(fā)布的,所以這里不再需要建立
/data/contAIner/nginx/html目錄,因?yàn)橐l(fā)布的靜態(tài)頁(yè)面已經(jīng)在Nginx鏡像中的/nginx/html目錄了。這里也減少了手動(dòng)部署安裝Nginx的步驟,而是在Jenkins任務(wù)中調(diào)用shell命令自動(dòng)執(zhí)行安裝。
1、新建Dockerfile文件,用于定義Nginx鏡像,及將打包成功的靜態(tài)文件復(fù)制到鏡像中,此文件放在前端項(xiàng)目的根目錄下,Jenkins打包時(shí)會(huì)從此處查找Dockerfile文件。
FROM nginx:latest
# 維護(hù)者信息
MAINTAINER gitegg
# 將生成的靜態(tài)頁(yè)面文件復(fù)制到nginx的/usr/share/nginx/html/目錄
COPY dist/ /usr/share/nginx/html/
# 容器啟動(dòng)時(shí)運(yùn)行的命令
CMD ["nginx", "-g", "daemon off;"]
2、部署及備份目錄準(zhǔn)備
- 新建 /opt/tmp 目錄,用于Jenkins打包后,通過(guò) Publish Over SSH插件將包傳輸?shù)椒?wù)器的臨時(shí)目錄(如果前面創(chuàng)建過(guò),這里無(wú)需再創(chuàng)建)。
- 新建 /opt/bak 目錄,用于存儲(chǔ)所有部署過(guò)的包備份,方便后續(xù)版本回滾。此目錄可能會(huì)占用很大空間,所以需要選擇一個(gè)磁盤(pán)空間大的掛載目錄(如果前面創(chuàng)建過(guò),這里無(wú)需再創(chuàng)建)。
- 新建 /opt/script 目錄,用于Jenkins將包傳輸完成之后,執(zhí)行安裝、備份操作的相關(guān)命令腳本(如果前面創(chuàng)建過(guò),這里無(wú)需再創(chuàng)建)。
- 新建 /data/container/nginx/www,映射N(xiāo)ginx容器內(nèi)的/var/www目錄。
- 新建 /data/container/nginx/logs,映射N(xiāo)ginx容器內(nèi)的/var/log/nginx目錄,存放nginx運(yùn)行日志。
- 新建 /data/container/nginx/etc,映射N(xiāo)ginx容器內(nèi)的/etc/nginx目錄
- 新建 /data/container/nginx/etc/nginx.conf,映射N(xiāo)ginx容器內(nèi)的/etc/nginx/nginx.conf配置文件
mkdir -p /opt/tmp /opt/bak /opt/script /data/container/nginx/www /data/container/nginx/logs /data/container/nginx/etc
chmod -R 777 /opt/tmp /opt/bak /opt/script /data/container/nginx/www /data/container/nginx/logs /data/container/nginx/etc
3、根據(jù)系統(tǒng)部署要求編寫(xiě)Nginx配置文件nginx.conf,以下是簡(jiǎn)單的配置方法,正常情況下https請(qǐng)求還需要配置ssl證書(shū),還有ipv6配置等,后面詳細(xì)講解Nginx配置。一定要將修改后的nginx.conf文件放到/data/container/nginx/etc/目錄下,否則nginx啟動(dòng)時(shí)會(huì)報(bào)錯(cuò)找不到配置文件。
server {
listen 80;
server_name 域名;
gzip on;
gzip_buffers 32 4K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types Application/JAVAscript text/css text/xml text/plain application/x-JavaScript image/jpeg image/gif image/png;
gzip_disable "MSIE [1-6].";
gzip_vary on;
#charset koi8-r;
access_log /var/log/nginx/portal.access.log main;
location / {
root /nginx/html/gitegg_portal;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /gitegg-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.1:8080/;
}
}
4、部署腳本編寫(xiě)說(shuō)明
- 定義入?yún)ⅲ梢酝ㄟ^(guò)Jenkins任務(wù)將參數(shù)傳入腳本中,我們定義了下面7個(gè)參數(shù):
container_name=portal-server : 容器名稱image_name=portal-server : 鏡像名稱version=latest : 鏡像版本portal_port=80: 宿主主機(jī)端口映射server_port=80: 容器內(nèi)服務(wù)端口portal_ssl_port=443: 宿主主機(jī)端口映射serve_sslr_port=443: 容器內(nèi)服務(wù)端口 - 對(duì)參數(shù)進(jìn)行檢查,是否未傳入?yún)?shù),這里根據(jù)自己的實(shí)際情況判斷,比如必須傳入哪些參數(shù),就設(shè)置參數(shù)的個(gè)數(shù)不能小于幾。
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_portal.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
exit
fi
- 入?yún)①x值,如果有參數(shù)傳入,則取服務(wù)參數(shù),如果沒(méi)有參數(shù)傳入則取默認(rèn)值
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
portal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; then
portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
serve_sslr_port="$7"
fi
echo "serve_sslr_port=" $serve_sslr_port
- 停止并刪除容器
echo "執(zhí)行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并刪除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
- 停止并刪除鏡像
# 刪除鏡像
echo "執(zhí)行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '鏡像存在,刪除鏡像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '鏡像不存在'
fi
- 備份本次安裝鏡像包
#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/portal-image.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
- 執(zhí)行安裝鏡像包命令
echo "docker load" $image_name
docker load --input /opt/tmp/portal-image.tar
- 執(zhí)行運(yùn)行命令
echo "docker run" $image_name
docker run -d -p $portal_port:$server_port -p $portal_ssl_port:$server_ssl_port --name=$container_name -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone -v /bxl/container/nginx/ssl:/nginx/ssl $image_name
- 刪除安裝文件,因?yàn)榍懊嬉呀?jīng)備份過(guò)了,所以這里將臨時(shí)安裝文件刪除
echo "remove tmp " $image_name
rm -rf /opt/tmp/portal-image.tar
- 打印執(zhí)行完成的命令
echo "Docker Portal is starting,please try to access $container_name conslone url"
- 完整的安裝部署腳本
container_name=portal-server
image_name=portal-server
version=latest
portal_port=80
server_port=80
portal_ssl_port=443
serve_sslr_port=443
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_portal.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
exit
fi
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
portal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; then
portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
serve_sslr_port="$7"
fi
echo "serve_sslr_port=" $serve_sslr_port
echo "執(zhí)行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并刪除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
# 刪除鏡像
echo "執(zhí)行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '鏡像存在,刪除鏡像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '鏡像不存在'
fi
#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/portal-image.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
echo "docker load" $image_name
docker load --input /opt/tmp/portal-image.tar
echo "docker run" $image_name
docker run -d -p $portal_port:$server_port -p $portal_ssl_port:$server_ssl_port --name=$container_name -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone -v /bxl/container/nginx/ssl:/nginx/ssl $image_name
echo "remove tmp " $image_name
rm -rf /opt/tmp/portal-image.tar
echo "Docker Portal is starting,please try to access $container_name conslone url"
二、新建Jenkins配置打包任務(wù),打包部署NodeJS(Vue)鏡像
1、新建任務(wù)前,安裝Docker Pipeline插件,使用Pipeline流水線任務(wù)構(gòu)建部署,安裝Jenkins插件相關(guān)內(nèi)容,請(qǐng)查看前面部署Jenkins相關(guān)文章。
2、安裝完插件之后,新建一個(gè)流水線任務(wù)。
3、和之前的任務(wù)一樣,選擇“丟棄舊的構(gòu)建”,設(shè)置保持構(gòu)建的最大個(gè)數(shù)為5。
4、下拉到“流水線”配置,選擇Pipeline script
流水線腳本如下:
node {
# 從gitlab下載代碼
stage('Preparation') { // for display purposes
// Get some code from a Github repository
echo "checkout from GitLab"
checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'git_username', url: 'http://127.0.0.1:9091/test/test.git']])
}
# NodeJS打包
stage('Build NodeJS Vue') {
echo "build nodejs code"
nodejs('Node17') {
sh 'echo $PATH'
sh 'node -v'
sh 'pnpm -v'
sh 'pnpm install'
sh 'pnpm run build'
}
}
# 此處判斷本機(jī)打包是否有容器,如果有的話需要?jiǎng)h除
stage('Delete Old Docker Container') {
echo "delete docker container"
sh '''if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并刪除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi'''
}
# 此處判斷本機(jī)打包是否有鏡像,如果有的話需要?jiǎng)h除
stage('Delete Old Docker Image') {
echo "delete docker image"
sh '''if [[ "$(docker images -q gitegg-portal 2> /dev/null)" != "" ]];
then
echo gitegg-portal '鏡像存在,刪除鏡像'
docker rmi $(docker images -q gitegg-portal 2> /dev/null) --force
else
echo gitegg-portal '鏡像不存在,創(chuàng)建鏡像'
fi'''
}
# Docker打包鏡像,并保存為tar
stage('Build Docker Image') {
echo "start docker build portal code"
// Run the docker build
docker.build 'gitegg-portal'
echo "save docker images tar"
sh 'docker save -o portal-image.tar gitegg-portal'
}
# 刪除安裝在本機(jī)的Docker鏡像,非tar包
stage('Delete New Docker Image') {
echo "delete docker image"
sh '''if [[ "$(docker images -q gitegg-portal 2> /dev/null)" != "" ]];
then
echo gitegg-portal '鏡像存在,刪除鏡像'
docker rmi $(docker images -q gitegg-portal 2> /dev/null) --force
else
echo gitegg-portal '鏡像不存在,創(chuàng)建鏡像'
fi'''
}
# 將Docker鏡像tar包發(fā)送到服務(wù)器并執(zhí)行部署命令
stage('Send Docker Image') {
echo "send docker image"
sshPublisher(publishers: [sshPublisherDesc(configName: 'Test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '/opt/script/publish_docker_portal.sh gitegg-portal gitegg-portal latest 8130 8130 4413 4413', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'portal-image.tar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
stage('Publish Results') {
echo "End Publish Portal"
}
}
5、在任務(wù)左側(cè)點(diǎn)擊立即構(gòu)建
- 立即構(gòu)建
- 流水線任務(wù)可以在右側(cè)顯示階段視圖
- 查看構(gòu)建日志:點(diǎn)擊立即構(gòu)建之后,下方會(huì)出現(xiàn)進(jìn)度條,點(diǎn)擊進(jìn)度條就可以進(jìn)入構(gòu)建日志界面。