用Docker簡(jiǎn)化Nodejs開發(fā)1——開發(fā)環(huán)境

開發(fā)Nodejs應(yīng)用通常要使用多個(gè)中間件,開發(fā)人員要把代碼跑起來(lái)就要在自己的機(jī)器上把中間件安裝一遍,費(fèi)時(shí)費(fèi)力,如果同時(shí)開發(fā)多個(gè)項(xiàng)目就更麻煩了,經(jīng)常要改來(lái)改去。本文以一個(gè)Nodejs+MongoDB項(xiàng)目為例,展示Docker的基本使用方法,同時(shí)提供了編寫對(duì)Docker友好代碼的方法。

項(xiàng)目說(shuō)明

tms-api-gw是一個(gè)API網(wǎng)關(guān)項(xiàng)目,功能是將收到的http請(qǐng)求根據(jù)業(yè)務(wù)規(guī)則轉(zhuǎn)發(fā)到對(duì)應(yīng)的服務(wù),每次收到的請(qǐng)求都要記錄日志,并進(jìn)行計(jì)數(shù),結(jié)果保存到mongodb中。

希望通過(guò)Docker解決如下幾個(gè)問(wèn)題:

  • 簡(jiǎn)化mongodb的部署,方便開發(fā)人員在本地運(yùn)行應(yīng)用。
  • 對(duì)nodejs應(yīng)用進(jìn)行打包,實(shí)現(xiàn)整體發(fā)布,方便運(yùn)維人員部署。

Docker

首先請(qǐng)按照官網(wǎng)說(shuō)明安裝Docker。

參考:https://docs.docker.com/get-started/#install-docker-desktop

使用Docker首先需要了解imagecontainer的概念,簡(jiǎn)單說(shuō),image是運(yùn)行環(huán)境的模版,container是根據(jù)模版創(chuàng)建的實(shí)例。imageDockerfile進(jìn)行定義,可以認(rèn)為Dockerfile是一個(gè)批處理命令,通過(guò)執(zhí)行命令,在鏡像上安裝包、復(fù)制文件、設(shè)置參數(shù)。有了image就可以通過(guò)run命令生成容器,生成的時(shí)候可以指定運(yùn)行時(shí)的參數(shù)。如果有多個(gè)相關(guān)聯(lián)的容器,可以通過(guò)docker-compose進(jìn)行整體管理。docker-compose根據(jù)編排文件docker-compose.yml描述被管理的容器,通過(guò)docker-compose up命令啟動(dòng),docker-compose down命令關(guān)閉,這樣就不用對(duì)著每個(gè)容器單獨(dú)執(zhí)行命令。

參考:https://docs.docker.com/reference/

通常我們需要的image都已經(jīng)有基礎(chǔ)版本,可以在hub.docker.com上查找。每個(gè)鏡像通常都有一堆版本,最重要的區(qū)別在于image是在哪個(gè)linux的版本上建的,建議使用alpine,因?yàn)檫@個(gè)版本最小。

下面我們結(jié)合項(xiàng)目需求具體跑一遍Docker。

MongoDB

查找基礎(chǔ)鏡像。

docker search mongo

將鏡像拉到本地。

docker pull mongo

生成并運(yùn)行容器。

docker run --rm --name tms-api-gw-mongo -p 27017:27017 -d mongo:latest

--rm 當(dāng)容器退出時(shí)自動(dòng)刪除。

--name tms-api-gw-mongo 指定容器的名字,后面操作容器時(shí)用的到。

-p 27017:27017 將容器內(nèi)部的27017端口映射到主機(jī)的27017 端口。

-d 是指定在后臺(tái)運(yùn)行。

進(jìn)入容器查看數(shù)據(jù)。

docker exec -it tms-api-gw-mongo /bin/bash

在容器中用exit命令退出容器。

這時(shí)在本地就有了個(gè)可用的MongoDB實(shí)例,數(shù)據(jù)保存在容器中,每次刪除容器,數(shù)據(jù)就會(huì)清除,這樣就總能用一個(gè)“干凈”的MongoDB進(jìn)行開發(fā)。

如果需要持久保留MongoDB中的數(shù)據(jù),可以讓容器將數(shù)據(jù)寫到本地目錄中,執(zhí)行run命令時(shí)指定參數(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 將主機(jī)中當(dāng)前目錄下的db掛載到容器的/data/db,作為mongo數(shù)據(jù)存儲(chǔ)目錄。

為了管理容器需要用到幾條命令:

docker ps -a #查看全部容器,不加-a參數(shù)只顯示運(yùn)行的。

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ǔ)進(jìn)行調(diào)整。

制作docker鏡像。

在項(xiàng)目根目錄新建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è)置應(yīng)用的環(huán)境變量
ENV TMS_API_GW_ENV='docker'

EXPOSE 3000

CMD [ "node", "./app.js" ]

COPY . .是把本地當(dāng)前目錄下的內(nèi)容復(fù)制到鏡像的工作目錄下/usr/src/app,但是,node_modulesconfig這些內(nèi)容不需要復(fù)制,因此要建立.dockerignore文件,指定不需要復(fù)制的內(nèi)容。

.*
node_modules
config
example

創(chuàng)建鏡像,注意不要丟了最后面的點(diǎn)。

docker build -t tms-api-gw-node .

用docker images可以查看已有鏡像。

運(yùn)行容器

docker run --rm --name tms-api-gw-node -p 5678:3000 -v $PWD/config:/usr/src/app/config -d tms-api-gw-node

如果我們同時(shí)開發(fā)多個(gè)項(xiàng)目,經(jīng)常會(huì)發(fā)生端口沖突的問(wèn)題,通過(guò)-p參數(shù)就可以在啟動(dòng)容器時(shí)指定端口了。

同一份代碼需要在多個(gè)環(huán)境中運(yùn)行,包括:開發(fā),測(cè)試,生產(chǎn)等,我們通常是采用配置文件讓代碼和運(yùn)行環(huán)境解耦。利用Docker,可以把代碼和代碼依賴的標(biāo)準(zhǔn)環(huán)境制作成鏡像,在生成容器時(shí)再指定和環(huán)境相關(guān)的配置文件,這樣Docker鏡像的整體就變成了一個(gè)發(fā)布單元,可以極大簡(jiǎn)化運(yùn)維工作。因此,在Dockerfile中我們創(chuàng)建了空的config目錄,通過(guò)參數(shù)-v $PWD/config:/usr/src/app/config指定使用運(yùn)行環(huán)境本地的配置文件。

前面介紹項(xiàng)目基本情況時(shí)提到需要在mongodb中存儲(chǔ)api訪問(wèn)數(shù)據(jù),當(dāng)應(yīng)用和mongodb都在容器中運(yùn)行時(shí)就產(chǎn)生了一個(gè)問(wèn)題:mongodb的地址是什么?

在本地開發(fā)環(huán)境我們通常寫個(gè)localhost,但是容器中的localhost是容器并不是宿主機(jī),應(yīng)用無(wú)法訪問(wèn)到mongodb。為了解決這個(gè)問(wèn)題,在項(xiàng)目中引入了環(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ù)這個(gè)環(huán)境變量進(jìn)行相應(yīng)的設(shè)置,在容器中docker.for.mac.host.internal代表了宿主機(jī)的地址,否則還是用localhost,指定端口要和DockerfileEXPOSE的端口一致,這樣不論是否在容器中Nodejs應(yīng)用都可以訪問(wèn)到MongoDB。

如果容器是在后臺(tái)運(yùn)行,想查看Nodejs應(yīng)用輸出的日志,使用如下命令:

docker logs tms-api-gw-node

因?yàn)殓R像是以alpine為基礎(chǔ)制作,進(jìn)入容器的命令需要調(diào)整,將bash改為sh

docker exec -it tms-api-gw-node /bin/sh

至此我們已經(jīng)可以在容器中運(yùn)行Nodejs應(yīng)用。

參考:https://github.com/nodejs/docker-node/blob/master/README.md#how-to-use-this-image

參考:一篇關(guān)于Nodejs使用Docker的最佳實(shí)踐,https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md

docker-compose

雖然已經(jīng)可以用容器把Nodejs應(yīng)用跑起來(lái),但是還是不夠方便,mongodb和nodejs容器要分別啟停,命令還都挺長(zhǎng)。能不能更簡(jiǎn)單呢?可以,用docker-compose。

參考:https://docs.docker.com/compose/compose-file/

Docker for Mac已經(jīng)包含了Compose了,所以Mac用戶不用單獨(dú)安裝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

上面這個(gè)文件中指定的邏輯和前面通過(guò)run命令分別運(yùn)行容器是完全等效的。

啟動(dòng)容器

docker-compose up -d

關(guān)閉容器

docker-compose down

更新鏡像

docker-compose build

反復(fù)更新鏡像會(huì)導(dǎo)致產(chǎn)生無(wú)效的鏡像,通過(guò)下面的命令刪除這些無(wú)用鏡像。

docker images
<none> <none> cb7a87c0359b 22 minutes ago 170MB

docker rmi $(docker images | grep "^<none>" | awk "{print $3}")

提示:實(shí)際在本機(jī)寫代碼時(shí)并不需要將Nodejs應(yīng)用做成鏡像再運(yùn)行,因?yàn)檫@樣每次修改代碼都要重新build,既花費(fèi)時(shí)間又產(chǎn)生許多無(wú)用鏡像。這里演示Nodejs應(yīng)用鏡像主要是為了下一步進(jìn)行發(fā)布做準(zhǔn)備。

總結(jié)

雖然Docker整體比較復(fù)雜,但是作為開發(fā)人員只需要掌握基本概念和常用命令就可以把Docker跑起來(lái),可以極大簡(jiǎn)化本地開發(fā)環(huán)境的管理工作,建議每個(gè)開發(fā)人員都嘗試一下。

下一篇研究如何利用Docker進(jìn)行Nodejs應(yīng)用的部署。

本系列其他文章

用Docker簡(jiǎn)化Nodejs開發(fā)2——開發(fā)環(huán)境到測(cè)試環(huán)境

用Docker簡(jiǎn)化Nodejs開發(fā)3——用webhook實(shí)現(xiàn)自動(dòng)更新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容