一斑鸦、應(yīng)用環(huán)境
操作系統(tǒng):
CentOS 7.4
應(yīng)用軟件:
Docker 19.03.4味混、 Certbot 0.40.1舵盈、nginx 1.17.5论矾、Jenkins 2.202剃允、Mysql Server 8.0.18沛简、Redis server 5.0.6
api服務(wù)器:
ThinkJS 3.0 (基于 Koa 2.x)
前端應(yīng)用:
Ant Design Pro V1
域名解析:(指向當(dāng)前主機(jī))
jenkins.***.domain、antd.***.domain斥废、api.***.domain
二椒楣、自動(dòng)化編譯部署的需求分析:
前后端開(kāi)發(fā)上傳代碼至git服務(wù)器,通過(guò)設(shè)置的webhooks調(diào)起jenkins中配置的編譯部署流程完成自動(dòng)化編譯部署
三牡肉、容器部署的整體思路與架構(gòu)
- 訪問(wèn)jenkins.***.domain域名捧灰,通過(guò)nginx容器代理,指向jenkins容器
- 通過(guò)jenkins構(gòu)建“服務(wù)端鏡像”统锤,實(shí)現(xiàn)“服務(wù)端容器”的自動(dòng)化編譯部署
- 通過(guò)jenkins構(gòu)建“前端鏡像”毛俏,實(shí)現(xiàn)“前端容器”的自動(dòng)化編譯打包并共享數(shù)據(jù)卷給nginx
- 訪問(wèn)antd.***.domain域名,通過(guò)nginx容器代理跪另,指向antd容器共享的前端靜態(tài)文件
- 訪問(wèn)antd.***.domain/api路由拧抖,通過(guò)nginx容器代理,指向api服務(wù)器容器
- 實(shí)現(xiàn)api服務(wù)器容器與Mysql免绿、Redis容器間的數(shù)據(jù)訪問(wèn)(處于安全考慮唧席,數(shù)據(jù)容器不對(duì)外開(kāi)放端口,無(wú)法通過(guò)域名直接訪問(wèn))
- Certbot生成泛域名證書(shū)支持https訪問(wèn)
四嘲驾、容器部署需解決的問(wèn)題
1淌哟、容器間通信:
1)容器每次重啟分配給容器的內(nèi)部ip都會(huì)改變,所以無(wú)法通過(guò)訪問(wèn)容器ip的形式進(jìn)行容器間通信辽故;
2)一個(gè)容器如何與不同網(wǎng)絡(luò)間的容器通信
解決方案:
4.1.1 創(chuàng)建兩個(gè)橋接網(wǎng)絡(luò)net0 - 網(wǎng)絡(luò)名稱natnet徒仓、net1 - 網(wǎng)絡(luò)名稱intranet
4.1.2 natnet網(wǎng)絡(luò)用于nginx容器與jenkins、api服務(wù)器容器通信(jenkins誊垢、api服務(wù)器容器需設(shè)置該網(wǎng)絡(luò)下的網(wǎng)絡(luò)別名)
4.1.3 intranet網(wǎng)絡(luò)用于api服務(wù)器容器與Mysql掉弛、Redis容器通信(Mysql症见、Redis容器需設(shè)置該網(wǎng)絡(luò)下的網(wǎng)絡(luò)別名)
2、數(shù)據(jù)(文件)共享:
容器間是相互獨(dú)立的殃饿,前端容器打包生成的文件如何共享給nginx容器使用
解決方案:
4.2.1 通過(guò)掛載數(shù)據(jù)卷的形式谋作,將宿主機(jī)下的數(shù)據(jù)共享目錄分別掛載到多個(gè)容器下用于共享數(shù)據(jù)
3、自定義鏡像中依賴庫(kù)的重復(fù)安裝:
鏡像的創(chuàng)建基于一個(gè)已有的基礎(chǔ)鏡像乎芳,每次重新構(gòu)建鏡像時(shí)都必須重新下載依賴遵蚜,如何減少依賴的重復(fù)下載
解決方案:
4.3.1 判斷目標(biāo)鏡像是否構(gòu)建,未構(gòu)建則基于基礎(chǔ)鏡像構(gòu)建新的鏡像奈惑,已構(gòu)建則基于已構(gòu)建的鏡像更新鏡像
五吭净、具體實(shí)現(xiàn)步驟
ps:docker安裝,鏡像獲取及使用參考底部鏈接此處不再贅述
1肴甸、網(wǎng)絡(luò)設(shè)置
#1寂殉、創(chuàng)建轉(zhuǎn)發(fā)網(wǎng)絡(luò),供nginx代理轉(zhuǎn)發(fā)
docker network create natnet
#2原在、創(chuàng)建內(nèi)部網(wǎng)絡(luò)不撑,供服務(wù)訪問(wèn)數(shù)據(jù)庫(kù)
docker network create intranet
2、容器設(shè)置
#創(chuàng)建nginx容器晤斩,加入natnet網(wǎng)絡(luò),映射主機(jī)80姆坚、433端口澳泵,
#掛載nginx配置文件路徑,日志路徑兼呵,網(wǎng)站路徑兔辅、證書(shū)路徑,并在后臺(tái)運(yùn)行
docker run --name nginx \
--network natnet \
-p 80:80 -p 443:443 \
-v /var/nginx/conf.d:/etc/nginx/conf.d \
-v /var/nginx/logs:/var/log/nginx \
-v /var/website:/var/website \
-v /etc/letsencrypt:/etc/letsencrypt \
-d nginx
#創(chuàng)建mysql容器击喂,加入intranet網(wǎng)絡(luò)并設(shè)置別名维苔,掛載文件路徑,并在后臺(tái)運(yùn)行
docker run --name mysql \
--network intranet --network-alias mysql \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=MyPassW0rd.. \
-d mysql
#創(chuàng)建redis容器懂昂,加入intranet網(wǎng)絡(luò)并設(shè)置別名介时,掛載文件路徑,并在后臺(tái)運(yùn)行
docker run --name redis \
--network intranet --network-alias redis \
-v redis-data:/data \
-d redis
#創(chuàng)建jenkins容器凌彬,加入natnet網(wǎng)絡(luò)并設(shè)置別名沸柔,掛載文件路徑,并在后臺(tái)運(yùn)行
docker run --name jenkins \
-u root \
--network natnet --network-alias jenkins \
-v jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/usr/bin/docker \
-v "$HOME":/home \
-d jenkins/jenkins
3铲敛、Nginx解析
創(chuàng)建nginx容器時(shí)已將配置目錄掛載至宿主機(jī)/var/nginx/conf.d
目錄下(在該目錄下添加如下配置文件)
forbidden.conf
(顯示的定義一個(gè) default server 禁止ip以及未綁定域名的訪問(wèn))
# 顯示的定義一個(gè) default server 禁止ip以及未綁定域名的訪問(wèn)
server {
listen 80 default_server;
server_name _;
return 403; # 403 forbidden
}
server {
listen 443 default_server;
server_name _;
return 403; # 403 forbidden
}
ssl_certificate.conf
(泛域名證書(shū)路徑)證書(shū)的申請(qǐng)請(qǐng)自行百度
# 證書(shū)路徑
ssl_certificate /etc/letsencrypt/live/***.domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/***.domain/privkey.pem;
jenkins.conf
(jenkins服務(wù)配置)
server {
listen 80;
listen [::]:80;
server_name jenkins.***.domain;
location / {
# 重定向到https
rewrite ^/(.*)$ https://${server_name}$1 permanent;
}
}
server {
listen 443 ssl http2;
server_name jenkins.***.domain;
# 證書(shū)的公私鑰
include conf.d/ssl_certificate.conf;
location / {
proxy_pass http://jenkins:8080; #此處的jenkins為運(yùn)行jenkins容器時(shí)配置的網(wǎng)絡(luò)別名
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
ps:至此褐澎,重載nginx配置即可訪問(wèn)jenkins服務(wù)
proj_name.conf
(項(xiàng)目服務(wù)配置)
server {
listen 80;
listen [::]:80;
server_name ***.domain www.***.domain proj_name.***.domain;
location / {
# 重定向到https
rewrite ^/(.*)$ https://${server_name}$1 permanent;
}
}
server {
listen 443 ssl http2;
server_name ***.domain www.***.domain proj_name.***.domain;
# gzip config
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
root /var/website/proj_name; #此路徑為前端容器共享數(shù)據(jù)卷目錄
# 證書(shū)的公私鑰
include conf.d/ssl_certificate.conf;
location / {
# 用于配合 browserHistory使用
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://proj_name.api:8360/api; #此處為api容器網(wǎng)絡(luò)別名,端口及模塊路由
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
}
4伐蒋、自動(dòng)化編譯部署
4.1 構(gòu)建proj_name/api鏡像并運(yùn)行
在api工程根目錄創(chuàng)建Dockerfile并根據(jù)自身項(xiàng)目修改具體配置
示例內(nèi)容如下(工程基于ThinkJS 3.0)
ARG BASE_IMAGE=node
FROM ${BASE_IMAGE}
WORKDIR /proj/server
COPY package.json ./package.json
RUN npm i --production --registry=https://registry.npm.taobao.org
COPY src ./src
COPY view ./view
#COPY www ./www
COPY production.js ./production.js
ENV DOCKER=true
EXPOSE 8360
CMD [ "node", "./production.js" ]
將如下內(nèi)容復(fù)制到j(luò)enkins相應(yīng)項(xiàng)目配置 -> 構(gòu)建
中工三,(或在api工程根目錄創(chuàng)建build.sh并復(fù)制如下內(nèi)容迁酸,而后在jenkins相應(yīng)項(xiàng)目配置 -> 構(gòu)建
中運(yùn)行該腳本)
示例內(nèi)容如下(工程基于ThinkJS 3.0)
ps:注意修改鏡像名稱、容器名稱俭正,及運(yùn)行容器時(shí)的配置
#!/bin/bash
#構(gòu)建的鏡像名稱
IMAGE='proj_name/api'
#運(yùn)行的容器名稱
CONTAINER='proj_name.api'
#構(gòu)建鏡像并啟動(dòng)容器
function build_run {
#使用根目錄下的Dockerfile構(gòu)建鏡像奸鬓,默認(rèn)使用node作為基鏡像
docker build -t $IMAGE \
--build-arg BASE_IMAGE=${1:-"node"} .
#停止并移除舊容器
remove_container
#創(chuàng)建容器,加入指定網(wǎng)絡(luò)段审,并在后臺(tái)運(yùn)行
docker run --name $CONTAINER \
--network intranet \
-d $IMAGE
#連接其他網(wǎng)絡(luò)并設(shè)置別名
docker network connect --alias $CONTAINER natnet $CONTAINER
}
#移除舊容器
function remove_container {
#判斷容器是否已存在
cID=`docker ps -aqf 'name='$CONTAINER`
if [ -z "$cID" ]; then
#容器不存在
echo '未找到該容器全蝶,將創(chuàng)建新的容器并啟動(dòng)'
return 1
fi
#判斷容器是否運(yùn)行
cID=`docker ps -qf 'name='$CONTAINER`
if [ -n "$cID" ]; then
#停止容器
echo '該容器已運(yùn)行,將關(guān)閉該容器'
docker stop $CONTAINER
fi
#移除容器
echo '該容器已停止運(yùn)行寺枉,將移除該容器'
docker rm $CONTAINER
}
#判斷鏡像是否已存在
imgID=`docker images -q $IMAGE`
if [ -z "$imgID" ]; then
#鏡像不存在抑淫,構(gòu)建鏡像并運(yùn)行容器
echo '未找到該鏡像,開(kāi)始構(gòu)建新的鏡像姥闪。始苇。。筐喳。'
build_run
else
#鏡像已存在催式,更新鏡像并運(yùn)行容器
echo '該鏡像已存在,開(kāi)始更新鏡像避归。荣月。。梳毙。'
build_run $IMAGE
fi
4.2 構(gòu)建proj_name.web鏡像并運(yùn)行
在web工程根目錄創(chuàng)建Dockerfile并根據(jù)自身項(xiàng)目修改具體配置
示例內(nèi)容如下(工程基于Ant Design Pro)
ARG BASE_IMAGE=node
FROM ${BASE_IMAGE}
WORKDIR /usr/src/app/
COPY package.json ./
RUN npm install --registry=https://registry.npm.taobao.org
COPY ./ ./
CMD ["npm", "run", "build"]
將如下內(nèi)容復(fù)制到j(luò)enkins相應(yīng)項(xiàng)目配置 -> 構(gòu)建
中哺窄,(或在api工程根目錄創(chuàng)建build.sh并復(fù)制如下內(nèi)容,而后在jenkins相應(yīng)項(xiàng)目配置 -> 構(gòu)建
中運(yùn)行該腳本)
示例內(nèi)容如下(工程基于Ant Design Pro)
ps:注意修改鏡像名稱账锹、容器名稱萌业,及容器共享數(shù)據(jù)卷的掛載目錄(供nginx容器讀取)
#!/bin/bash
#構(gòu)建的鏡像名稱
IMAGE='proj_name/web'
#運(yùn)行的容器名稱
CONTAINER='proj_name.web'
#構(gòu)建鏡像并啟動(dòng)容器
function build_run {
#使用根目錄下的Dockerfile構(gòu)建鏡像奸柬,默認(rèn)使用node作為基鏡像
docker build -t $IMAGE \
--build-arg BASE_IMAGE=${1:-"node"} .
#停止并移除舊容器
remove_container
#創(chuàng)建容器生年,掛載編譯后的文件路徑,并在后臺(tái)運(yùn)行
docker run --name $CONTAINER \
-v /var/website/proj_name:/usr/src/app/dist \
-d $IMAGE
}
#移除舊容器
function remove_container {
#判斷容器是否已存在
cID=`docker ps -aqf 'name='$CONTAINER`
if [ -z "$cID" ]; then
#容器不存在
echo '未找到該容器廓奕,將創(chuàng)建新的容器并啟動(dòng)'
return 1
fi
#判斷容器是否運(yùn)行
cID=`docker ps -qf 'name='$CONTAINER`
if [ -n "$cID" ]; then
#停止容器
echo '該容器已運(yùn)行抱婉,將關(guān)閉該容器'
docker stop $CONTAINER
fi
#移除容器
echo '該容器已停止運(yùn)行,將移除該容器'
docker rm $CONTAINER
}
#判斷鏡像是否已存在
imgID=`docker images -q $IMAGE`
if [ -z "$imgID" ]; then
#鏡像不存在桌粉,構(gòu)建鏡像并運(yùn)行容器
echo '未找到該鏡像授段,開(kāi)始構(gòu)建新的鏡像。番甩。侵贵。。'
build_run
else
#鏡像已存在缘薛,更新鏡像并運(yùn)行容器
echo '該鏡像已存在窍育,開(kāi)始更新鏡像卡睦。。漱抓。表锻。'
build_run $IMAGE
fi
5、配置jenkins與git服務(wù)端的Webhooks
請(qǐng)自行百度乞娄,不再贅述瞬逊!
六、結(jié)束:
ps:最后可將上訴步驟自行整合成 docker-compose.yml
參考:
Docker 軟件安裝