Web應(yīng)用通常由多個部分組成,包括:前端、后端和基礎(chǔ)中間件。前端代碼是靜態(tài)的(html+js),可以放在nginx中運(yùn)行;后端業(yè)務(wù)邏輯在nodejs或java容器中運(yùn)行;mysql、mongodb等通用中間件進(jìn)行數(shù)據(jù)持久存儲。本文以一個實(shí)際項(xiàng)目為例,演示如何利用docker方便運(yùn)維對應(yīng)用的多個部分進(jìn)行發(fā)布。
項(xiàng)目概述

項(xiàng)目地址:https://github.com/jasony62/tms-mongodb-web
項(xiàng)目包括3個部分:
- ue_admin:前端代碼,采用VUE開發(fā),放在nginx中獨(dú)立運(yùn)行
- back:后端代碼,采用nodejs開發(fā),獨(dú)立運(yùn)行
- mongodb:數(shù)據(jù)持久化,獨(dú)立運(yùn)行
通過docker要解決兩方面問題:1、減輕開發(fā)人員個人開發(fā)環(huán)境的搭建,讓項(xiàng)目具備單機(jī)開箱即用能力;2、實(shí)現(xiàn)代碼和運(yùn)行環(huán)境的整體發(fā)布,簡化并規(guī)范運(yùn)維工作。
問題1通過編寫docker-compose.yml解決。相關(guān)知識可以參考前一篇文章:用Docker簡化Nodejs開發(fā)1——開發(fā)環(huán)境。需要注意的是前端代碼獨(dú)立部署有跨域(CORS)問題,需要在nginx上設(shè)置反向代理。
本文的重點(diǎn)是關(guān)注問題2,通過docker實(shí)現(xiàn)一個完整的從開發(fā)到測試的發(fā)布流程。

基本發(fā)布流程如下:
- 開發(fā)人員在開發(fā)環(huán)境將代碼發(fā)布到git倉庫;
- 運(yùn)維人員在構(gòu)建環(huán)境從git倉庫拉取代碼;編譯前端代碼;分別將不同模塊打包成鏡像;
- 構(gòu)建環(huán)境將鏡像發(fā)布到私有鏡像倉庫;
- 測試環(huán)境從鏡像倉庫拉取鏡像,啟動運(yùn)行;
- 測試通過后,生產(chǎn)環(huán)境從鏡像倉庫拉取鏡像,啟動運(yùn)行。
為演示上述過程,準(zhǔn)備3臺機(jī)器,開發(fā),構(gòu)建和測試,都安裝好docker和docker-compose。
開發(fā)環(huán)境
為了讓前端運(yùn)行環(huán)境更干凈,鏡像中只包含編譯完的靜態(tài)內(nèi)容,所以需要在制作鏡像前執(zhí)行命令生成前端代碼(默認(rèn)在dist目錄)
cnpm i
yarn build 或 npm run build
ue_admin目錄下的nginx.conf,back目錄下的config目錄不放在鏡像中,它們需要在運(yùn)行環(huán)境中進(jìn)行指定。
docker-compose.yml
volumes:
- ./back/config:/usr/src/app/config
volumes:
- ./ue_admin/nginx.conf:/etc/nginx/nginx.conf:ro
If neither ‘rw’ or ‘ro’ is specified then the volume is mounted in read-write mode.
參考:https://docs.docker.com/engine/reference/run/#volume-shared-filesystems
參考:https://hub.docker.com/_/nginx
參考:https://www.nginx.com/blog/deploying-nginx-nginx-plus-docker/
參考:JS-Web項(xiàng)目常見問題(3)-前后端分離導(dǎo)致的跨域問題分析
構(gòu)建環(huán)境
用樹莓派3B+作為構(gòu)建環(huán)境(其他環(huán)境也可以),安裝docker和docker-compose,建立私有docker倉庫,從github上拉取代碼,制作鏡像,發(fā)布鏡像到私有倉庫。
安裝docker
sudo curl -sSL https://get.docker.com | sh
時間有些長,大約等3分鐘。
按照提示執(zhí)行下面的命令,否則會報(bào)權(quán)限錯誤。
sudo usermod -aG docker pi
安裝docker-compose
通過pyenv安裝python,通過pip安裝docker-compose。
sudo apt install zlib1g-dev libreadline-dev libssl-dev libbz2-dev libsqlite3-dev libffi-dev
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
vi ~/.bash_profile
export PATH="/hom/pi/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
source ~/.bash_profile
pyenv install 3.7.0
pyenv global 3.7.0
pip install docker-compose
搭建私有鏡像庫
拉?。╬ull)arm版本的registry鏡像。
docker pull budry/registry-arm
創(chuàng)建存儲鏡像文件的目錄。
mkdir docker-registry
啟動鏡像倉庫容器。
docker run --name registry-arm -d -p 5000:5000 -v /home/pi/docker-registry:/var/lib/registry --restart always budry/registry-arm
從github拉取代碼
安裝git(如果沒有安裝過)
sudo apt-get install git
從git庫拉取代碼。
編譯代碼
cd tms-mongodb-web/ue_admin
cnpm i
npm run build
檢查是否生成dist目錄。
制作鏡像
官方MongoDb鏡像沒有樹莓派的版本,需要換成rpi3-mongodb3。注意,這時并不需要修改docker-compose.yml,docker支持用docker-compose.override.yml文件指定需要覆蓋的選項(xiàng)(查看官網(wǎng)文檔中關(guān)于-f選項(xiàng)的說明)。
version: '3.7'
services:
mongodb:
image: andresvidal/rpi3-mongodb3
logging:
driver: "json-file"
參考:https://hub.docker.com/r/andresvidal/rpi3-mongodb3
執(zhí)行命令,生成鏡像。
docker-compose build
發(fā)布鏡像
在發(fā)布和獲取鏡像前,需要讓客戶端支持以http(非https)訪問私庫。
客戶端直接在界面中設(shè)置。

在樹莓派中新建文件/etc/docker/daemon.json,添加如下內(nèi)容:
{ "insecure-registries": ["192.168.43.30:5000"] }
修改完成后需要重啟docker才能生效。
完成上面的工作,在樹莓派(私庫)上查看已有的鏡像。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tms-mw-ue_admin latest bb34d5e9ee16 13 hours ago 21.8MB
tms-mw-back latest 0abb9b5c6cb6 13 hours ago 162MB
nginx alpine 7e9d7b4eafa6 7 days ago 18.4MB
node alpine 30bb03f6ec2e 2 weeks ago 96.6MB
andresvidal/rpi3-mongodb3 latest fca24dc11d8c 23 months ago 366MB
budry/registry-arm latest c440ef8a31ab 24 months ago 139MB
tms-mw-ue_admin和tms-mw-back是應(yīng)用的鏡像,需要發(fā)布到私有鏡像庫。
docker tag tms-mw-back 192.168.43.30:5000/tms-mw-back
docker push 192.168.43.30:5000/tms-mw-back
用開發(fā)機(jī)或測試機(jī)看看是否能夠成功拉取鏡像。
docker pull 192.168.43.30:5000/tms-mw-back
注意:可以不用tag的操作,直接在docker-compose.override.yml文件中將鏡像指定為私有庫鏡像。
back:
image: 192.168.43.30:5000/tms-mw-back
ue_admin:
image: 192.168.43.30:5000/tms-mw-ue_admin
運(yùn)行命令
docker-compose build
鏡像版本
代碼更新了就需要生成新的鏡像版本,為了方便回滾等需要,可以將現(xiàn)有的鏡像版本保留下來。
docker tag 192.168.43.30:5000/tms-mw-ue_admin:latest 192.168.43.30:5000/tms-mw-ue_admin:v1
原則上,每一次生成新版本鏡像前,都應(yīng)該將現(xiàn)有最新版本(latest)標(biāo)記為一個指定版本號的鏡像。
參考:https://docs.docker.com/engine/reference/commandline/tag/
測試環(huán)境
安裝docker和docker-compose。
復(fù)制docker-compose.yml,ue_admin/nginx.conf和back/config到測試環(huán)境項(xiàng)目根目錄下。修改docker-compose.yml文件中進(jìn)行的位置和文件的位置。
version: '3.7'
services:
mongodb:
image: mongo:latest
container_name: tms-mw-mongo
ports:
- '27017:27017'
logging:
driver: none
back:
image: 192.168.43.30:5000/tms-mw-back
container_name: tms-mw-back
ports:
- '3000:3000'
volumes:
- ./config:/usr/src/app/config
depends_on:
- mongodb
ue_admin:
image: 192.168.43.30:5000/tms-mw-ue_admin
container_name: tms-mw-ue_admin
ports:
- '8080:80'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- back
docker-compose up
如果鏡像的版本更新了,需要主動拉取新版本的鏡像。
docker-compose pull
開發(fā)環(huán)境
如果開發(fā)環(huán)境可以連私有倉庫,那么開發(fā)環(huán)境和測試環(huán)境的部署就沒有什么區(qū)別,如果不通,可以采用手工導(dǎo)入導(dǎo)出鏡像的方法。
導(dǎo)出鏡像
docker save ID > xxx.tar
導(dǎo)入鏡像
docker load < xxx.tar
總結(jié)
通過docker可以將代碼和它依賴的運(yùn)行環(huán)境打包成一個整體進(jìn)行發(fā)布,通過docker-compose.override.yml文件修改鏡像制作參數(shù),通過啟動容器時用-v選項(xiàng)指定配置文件和數(shù)據(jù)目錄,可以解決設(shè)置與特定運(yùn)行環(huán)境相關(guān)參數(shù)的需求。
按照上面的流程基本可以實(shí)現(xiàn)基于docker的版本發(fā)布,但是需要手工操作的環(huán)節(jié)太多,下一步研究如何利用工具將這個過程自動化。