docker單宿主機(jī)多容器實(shí)現(xiàn)自動(dòng)化編譯部署

一斑鸦、應(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)

各容器組織架構(gòu).png
  1. 訪問(wèn)jenkins.***.domain域名捧灰,通過(guò)nginx容器代理,指向jenkins容器
  2. 通過(guò)jenkins構(gòu)建“服務(wù)端鏡像”统锤,實(shí)現(xiàn)“服務(wù)端容器”的自動(dòng)化編譯部署
  3. 通過(guò)jenkins構(gòu)建“前端鏡像”毛俏,實(shí)現(xiàn)“前端容器”的自動(dòng)化編譯打包并共享數(shù)據(jù)卷給nginx
  4. 訪問(wèn)antd.***.domain域名,通過(guò)nginx容器代理跪另,指向antd容器共享的前端靜態(tài)文件
  5. 訪問(wèn)antd.***.domain/api路由拧抖,通過(guò)nginx容器代理,指向api服務(wù)器容器
  6. 實(shí)現(xiàn)api服務(wù)器容器與Mysql免绿、Redis容器間的數(shù)據(jù)訪問(wèn)(處于安全考慮唧席,數(shù)據(jù)容器不對(duì)外開(kāi)放端口,無(wú)法通過(guò)域名直接訪問(wèn))
  7. 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 軟件安裝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仪或,一起剝皮案震驚了整個(gè)濱河市确镊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌范删,老刑警劉巖蕾域,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異到旦,居然都是意外死亡旨巷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門添忘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)采呐,“玉大人,你說(shuō)我怎么就攤上這事搁骑⌒竿颍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵靶病,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我口予,道長(zhǎng)娄周,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任沪停,我火速辦了婚禮煤辨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘木张。我一直安慰自己众辨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布舷礼。 她就那樣靜靜地躺著鹃彻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妻献。 梳的紋絲不亂的頭發(fā)上蛛株,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天团赁,我揣著相機(jī)與錄音,去河邊找鬼谨履。 笑死欢摄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笋粟。 我是一名探鬼主播怀挠,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼害捕!你這毒婦竟也來(lái)了绿淋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吨艇,失蹤者是張志新(化名)和其女友劉穎躬它,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體东涡,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冯吓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疮跑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片组贺。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祖娘,靈堂內(nèi)的尸體忽然破棺而出失尖,到底是詐尸還是另有隱情,我是刑警寧澤渐苏,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布掀潮,位于F島的核電站,受9級(jí)特大地震影響琼富,放射性物質(zhì)發(fā)生泄漏仪吧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一鞠眉、第九天 我趴在偏房一處隱蔽的房頂上張望薯鼠。 院中可真熱鬧,春花似錦械蹋、人聲如沸出皇。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)郊艘。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暇仲,已是汗流浹背步做。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奈附,地道東北人全度。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像斥滤,于是被迫代替她去往敵國(guó)和親将鸵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354