開發(fā)Nodejs應用通常要使用多個中間件,開發(fā)人員要把代碼跑起來就要在自己的機器上把中間件安裝一遍,費時費力态蒂,如果同時開發(fā)多個項目就更麻煩了堰汉,經(jīng)常要改來改去辽社。本文以一個Nodejs+MongoDB項目為例,展示Docker的基本使用方法翘鸭,同時提供了編寫對Docker友好代碼的方法滴铅。
項目說明
tms-api-gw是一個API網(wǎng)關(guān)項目,功能是將收到的http請求根據(jù)業(yè)務規(guī)則轉(zhuǎn)發(fā)到對應的服務就乓,每次收到的請求都要記錄日志汉匙,并進行計數(shù),結(jié)果保存到mongodb中生蚁。
希望通過Docker解決如下幾個問題:
- 簡化mongodb的部署噩翠,方便開發(fā)人員在本地運行應用。
- 對nodejs應用進行打包邦投,實現(xiàn)整體發(fā)布伤锚,方便運維人員部署。
Docker
首先請按照官網(wǎng)說明安裝Docker尼摹。
參考:https://docs.docker.com/get-started/#install-docker-desktop
使用Docker首先需要了解image
和container
的概念见芹,簡單說,image
是運行環(huán)境的模版蠢涝,container
是根據(jù)模版創(chuàng)建的實例玄呛。image
用Dockerfile
進行定義,可以認為Dockerfile
是一個批處理命令和二,通過執(zhí)行命令徘铝,在鏡像上安裝包、復制文件惯吕、設(shè)置參數(shù)惕它。有了image
就可以通過run
命令生成容器,生成的時候可以指定運行時的參數(shù)废登。如果有多個相關(guān)聯(lián)的容器淹魄,可以通過docker-compose
進行整體管理。docker-compose
根據(jù)編排文件docker-compose.yml
描述被管理的容器堡距,通過docker-compose up
命令啟動甲锡,docker-compose down
命令關(guān)閉兆蕉,這樣就不用對著每個容器單獨執(zhí)行命令。
參考:https://docs.docker.com/reference/
通常我們需要的image
都已經(jīng)有基礎(chǔ)版本缤沦,可以在hub.docker.com
上查找虎韵。每個鏡像通常都有一堆版本,最重要的區(qū)別在于image
是在哪個linux的版本上建的缸废,建議使用alpine
包蓝,因為這個版本最小。
下面我們結(jié)合項目需求具體跑一遍Docker企量。
MongoDB
查找基礎(chǔ)鏡像测萎。
docker search mongo
將鏡像拉到本地。
docker pull mongo
生成并運行容器届巩。
docker run --rm --name tms-api-gw-mongo -p 27017:27017 -d mongo:latest
--rm
當容器退出時自動刪除绳泉。
--name tms-api-gw-mongo
指定容器的名字,后面操作容器時用的到姆泻。
-p 27017:27017
將容器內(nèi)部的27017端口映射到主機的27017 端口零酪。
-d
是指定在后臺運行。
進入容器查看數(shù)據(jù)拇勃。
docker exec -it tms-api-gw-mongo /bin/bash
在容器中用exit
命令退出容器四苇。
這時在本地就有了個可用的MongoDB實例,數(shù)據(jù)保存在容器中方咆,每次刪除容器月腋,數(shù)據(jù)就會清除,這樣就總能用一個“干凈”的MongoDB進行開發(fā)瓣赂。
如果需要持久保留MongoDB中的數(shù)據(jù)榆骚,可以讓容器將數(shù)據(jù)寫到本地目錄中,執(zhí)行run
命令時指定參數(shù)煌集。
docker run --rm --name tms-api-gw-mongo -p 27017:27017 -v $PWD/storage/mongodb:/data/db -d mongo:latest
-v $PWD/db:/data/db
將主機中當前目錄下的db掛載到容器的/data/db妓肢,作為mongo數(shù)據(jù)存儲目錄。
為了管理容器需要用到幾條命令:
docker ps -a #查看全部容器苫纤,不加-a參數(shù)只顯示運行的碉钠。
docker stop container_name # 停止指定的容器
docker rm container_name # 刪除指定的容器
Docker命令參考:https://docs.docker.com/engine/reference/commandline/cli/
Nodejs
先看看Nodejs官網(wǎng)的這篇文章:https://nodejs.org/zh-cn/docs/guides/nodejs-docker-webapp/,下面是以該文章為基礎(chǔ)進行調(diào)整卷拘。
制作docker鏡像喊废。
在項目根目錄新建Dockerfile
文件,文件內(nèi)容如下栗弟,和Nodejs官網(wǎng)文章不一致的地方加了注釋污筷。
FROM node:alpine
# 安裝cnpm
RUN npm install cnpm -g
WORKDIR /usr/src/app
COPY package*.json ./
# 只安裝dependencies的包,不安裝devDependencies的包乍赫;額外安裝包瓣蛀。
RUN cnpm install --production \
&& cnpm install log4js
COPY . .
# 創(chuàng)建放配置文件的目錄
RUN mkdir config
# 設(shè)置應用的環(huán)境變量
ENV TMS_API_GW_ENV='docker'
EXPOSE 3000
CMD [ "node", "./app.js" ]
COPY . .
是把本地當前目錄下的內(nèi)容復制到鏡像的工作目錄下/usr/src/app
斤寂,但是,node_modules
揪惦,config
這些內(nèi)容不需要復制,因此要建立.dockerignore
文件罗侯,指定不需要復制的內(nèi)容器腋。
.*
node_modules
config
example
創(chuàng)建鏡像,注意不要丟了最后面的點钩杰。
docker build -t tms-api-gw-node .
用docker images可以查看已有鏡像纫塌。
運行容器
docker run --rm --name tms-api-gw-node -p 5678:3000 -v $PWD/config:/usr/src/app/config -d tms-api-gw-node
如果我們同時開發(fā)多個項目,經(jīng)常會發(fā)生端口沖突的問題讲弄,通過-p
參數(shù)就可以在啟動容器時指定端口了措左。
同一份代碼需要在多個環(huán)境中運行,包括:開發(fā)避除,測試怎披,生產(chǎn)等,我們通常是采用配置文件讓代碼和運行環(huán)境解耦瓶摆。利用Docker凉逛,可以把代碼和代碼依賴的標準環(huán)境制作成鏡像,在生成容器時再指定和環(huán)境相關(guān)的配置文件群井,這樣Docker鏡像的整體就變成了一個發(fā)布單元状飞,可以極大簡化運維工作。因此书斜,在Dockerfile中我們創(chuàng)建了空的config
目錄诬辈,通過參數(shù)-v $PWD/config:/usr/src/app/config
指定使用運行環(huán)境本地的配置文件。
前面介紹項目基本情況時提到需要在mongodb中存儲api訪問數(shù)據(jù)荐吉,當應用和mongodb都在容器中運行時就產(chǎn)生了一個問題:mongodb的地址是什么焙糟?
在本地開發(fā)環(huán)境我們通常寫個localhost
,但是容器中的localhost
是容器并不是宿主機样屠,應用無法訪問到mongodb酬荞。為了解決這個問題,在項目中引入了環(huán)境變量瞧哟,看代碼config/gateway.sample.js
混巧。
let host, port
if (process.env.TMS_API_GW_ENV === 'docker') {
host = 'docker.for.mac.host.internal'
port = 3000
} else {
host = 'localhost'
port = 5678
}
module.exports = {
...
}
前面Dockerfile
中指定了環(huán)境變量ENV TMS_API_GW_ENV='docker'
,代碼中可以根據(jù)這個環(huán)境變量進行相應的設(shè)置勤揩,在容器中docker.for.mac.host.internal
代表了宿主機的地址咧党,否則還是用localhost
,指定端口要和Dockerfile
中EXPOSE
的端口一致陨亡,這樣不論是否在容器中Nodejs應用都可以訪問到MongoDB傍衡。
如果容器是在后臺運行深员,想查看Nodejs應用輸出的日志,使用如下命令:
docker logs tms-api-gw-node
因為鏡像是以alpine
為基礎(chǔ)制作蛙埂,進入容器的命令需要調(diào)整倦畅,將bash
改為sh
。
docker exec -it tms-api-gw-node /bin/sh
至此我們已經(jīng)可以在容器中運行Nodejs應用绣的。
參考:https://github.com/nodejs/docker-node/blob/master/README.md#how-to-use-this-image
參考:一篇關(guān)于Nodejs使用Docker的最佳實踐叠赐,https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md
docker-compose
雖然已經(jīng)可以用容器把Nodejs應用跑起來,但是還是不夠方便屡江,mongodb和nodejs容器要分別啟停芭概,命令還都挺長。能不能更簡單呢惩嘉?可以罢洲,用docker-compose。
參考:https://docs.docker.com/compose/compose-file/
Docker for Mac已經(jīng)包含了Compose了文黎,所以Mac用戶不用單獨安裝Compose了惹苗。
創(chuàng)建docker-compose.yml
文件。
version: '3.7'
services:
app:
build: ./
image: tms-api-gw-node:latest
container_name: tms-api-gw-node
ports:
- '5678:3000'
volumes:
- ./config:/usr/src/app/config
depends_on:
- mongodb
mongodb:
image: mongo:latest
container_name: tms-api-gw-mongo
ports:
- '27017:27017'
logging:
driver: none
上面這個文件中指定的邏輯和前面通過run
命令分別運行容器是完全等效的耸峭。
啟動容器
docker-compose up -d
關(guān)閉容器
docker-compose down
更新鏡像
docker-compose build
反復更新鏡像會導致產(chǎn)生無效的鏡像鸽粉,通過下面的命令刪除這些無用鏡像。
docker images
<none> <none> cb7a87c0359b 22 minutes ago 170MB
docker rmi $(docker images | grep "^<none>" | awk "{print $3}")
提示:實際在本機寫代碼時并不需要將Nodejs應用做成鏡像再運行抓艳,因為這樣每次修改代碼都要重新build触机,既花費時間又產(chǎn)生許多無用鏡像。這里演示Nodejs應用鏡像主要是為了下一步進行發(fā)布做準備玷或。
總結(jié)
雖然Docker整體比較復雜儡首,但是作為開發(fā)人員只需要掌握基本概念和常用命令就可以把Docker跑起來,可以極大簡化本地開發(fā)環(huán)境的管理工作偏友,建議每個開發(fā)人員都嘗試一下蔬胯。
下一篇研究如何利用Docker進行Nodejs應用的部署。