Node.js服務(wù)Docker容器化應(yīng)用實(shí)踐小結(jié)
本篇不會(huì)講解 Docker 命令的使用、安裝等,因?yàn)樵谥耙黄恼?a target="_blank" >一文零基礎(chǔ)教你學(xué)會(huì) Docker 入門到實(shí)踐中也已經(jīng)講解的很詳細(xì)了,不清楚的可以點(diǎn)擊鏈接回頭在重新看下,本篇重點(diǎn)是介紹 Node.js 項(xiàng)目如何進(jìn)行 Docker 容器化及一些實(shí)踐優(yōu)化,還有一些常見(jiàn)的問(wèn)題,當(dāng)然如果還有其它使用上的問(wèn)題也歡迎大家在評(píng)論區(qū)進(jìn)行留言補(bǔ)充。
作者簡(jiǎn)介:五月君,Nodejs Developer,熱愛(ài)技術(shù)、喜歡分享的 90 后青年,公眾號(hào)「Nodejs技術(shù)?!?,Github 開(kāi)源項(xiàng)目 www.nodejs.red
通過(guò)本篇文章能學(xué)到什么?
- 學(xué)會(huì)如何用 Docker 容器化一個(gè) Node.js 服務(wù)
- 動(dòng)態(tài)設(shè)置環(huán)境變量一份 Dockerfile 文件構(gòu)建不同的版本
- Node.js 私有 NPM 包在構(gòu)建鏡像時(shí)如何認(rèn)證
- Egg.js 框架 Docker 容器化應(yīng)該注意的問(wèn)題
- Docker 鏡像體積與構(gòu)建時(shí)間的優(yōu)化
Docker 化一個(gè) Node.js 應(yīng)用程序
在本篇開(kāi)始我們先創(chuàng)建一個(gè)簡(jiǎn)單的 Node.js 應(yīng)用,然后為這個(gè)應(yīng)用創(chuàng)建一個(gè) Docker 鏡像,并構(gòu)建和運(yùn)行它
創(chuàng)建 Node.js 項(xiàng)目
首先我們需要?jiǎng)?chuàng)建一個(gè) app.js 開(kāi)啟一個(gè) HTTP 服務(wù),后面會(huì)借助 Docker 來(lái)運(yùn)行這個(gè)程序
const http = require('http');
const PORT = 30010;
const server = http.createServer((req, res) => {
res.end('Hello Docker');
})
server.listen(PORT, () => {
console.log('Running on http://localhost:', PORT, 'NODE_ENV', process.env.NODE_ENV);
});
然后我們創(chuàng)建一個(gè) package.json 文件,這里是描述你的應(yīng)用程序以及需要的依賴,寫過(guò) Node.js 的同學(xué)應(yīng)該會(huì)很熟悉的,這里我在 scripts 里面增加了npm run dev、npm run pro兩個(gè)命令,因?yàn)槲蚁朐谶@里介紹如何在構(gòu)建時(shí)傳入?yún)?shù)來(lái)動(dòng)態(tài)設(shè)置環(huán)境變量。
{
"name": "hello-docker",
"version": "1.0.2",
"description": "",
"author": "May",
"main": "app.js",
"scripts": {
"dev": "NODE_ENV=dev node app.js",
"pro": "NODE_ENV=pro node app.js"
}
}
Dockerfile 文件
這是一個(gè) Dockerfile 文件所包含的信息,這些命令在Docker 入門與實(shí)踐中也有講解過(guò)
FROM node:10.0-alpine RUN apk --update add tzdata \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && apk del tzdata RUN mkdir -p /usr/src/nodejs/ WORKDIR /usr/src/nodejs/ # add npm package COPY package.json /usr/src/nodejs/package.json RUN cd /usr/src/nodejs/ RUN npm i # copy code COPY . /usr/src/nodejs/ EXPOSE 30010 CMD npm run dev
在 Dockerfile 的同級(jí)文件下創(chuàng)建一個(gè) .dockerignore 文件,避免將你本地的調(diào)試文件、node_modules 等一些文件放入 Docker 容器中
.git node_modules npm-debug.log
此時(shí)通過(guò)以下命令即可構(gòu)建一個(gè) Docker 鏡像
$ docker image build -t mayjun/hello-docker
再通過(guò) docker run -d -p 30010:30010 mayjun/hello-docker 命令可運(yùn)行一個(gè) Docker 容器,但是有個(gè)疑問(wèn)我是有生產(chǎn)和測(cè)試之分的,按照上面CMD npm run dev這樣寫死只能打包一種環(huán)境,當(dāng)然你也可以在建一個(gè)文件來(lái)實(shí)現(xiàn)或者一些其它的方法。
動(dòng)態(tài)設(shè)置環(huán)境變量
為了解決上面的疑問(wèn),我的想法是在鏡像構(gòu)建時(shí)傳入?yún)?shù)來(lái)動(dòng)態(tài)設(shè)置環(huán)境變量,對(duì) Dockerfile 文件做下修改,看以下實(shí)現(xiàn):
EXPOSE 30010
ARG node_env # 新增加
ENV NODE_ENV=$node_env # 新增加
CMD npm run ${NODE_ENV} # 修改
下面對(duì)上面的代碼做個(gè)解釋
- 通過(guò) ARG 指令定義了一個(gè)變量,用戶可以在構(gòu)建時(shí)通過(guò)使用 --build-arg = 標(biāo)志的 docker build 命令將其傳遞給構(gòu)建器 ARG node_env
- 在 Dockerfile 中使用 ENV 引用這個(gè)變量 ENV NODE_ENV=$node_env
- 這一步就是使用了 CMD npm run ${NODE_ENV}
剩下的就是在構(gòu)建鏡像時(shí)動(dòng)態(tài)傳入?yún)?shù)了
$ docker image build --build-arg node_env=dev -t mayjun/hello-docker:1.0.2 . # 構(gòu)建測(cè)試環(huán)境 $ docker image build --build-arg node_env=pro -t mayjun/hello-docker:1.0.2 . # 構(gòu)建生產(chǎn)環(huán)境
運(yùn)行容器
$ docker run -d -p 30010:30010 mayjun/hello-docker:1.0.2 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2bc6e62cd0e8 mayjun/hello-docker:1.0.2 "/bin/sh -c 'npm run…" 3 minutes ago Up 3 minutes 0.0.0.0:30010->30010/tcp elastic_bouman
查看容器日志
docker logs -f 2bc6e62cd0e8 > hello-docker@1.0.0 dev /usr/src/nodejs > NODE_ENV=dev node app.js Running on http://localhost: 30010 NODE_ENV dev
我將以上代碼打包成了鏡像 mayjun/hello-docker:1.0.2,可以拉取查看 docker pull mayjun/hello-docker:1.0.2
Docker 與 Node.js 私有 NPM 包
如果你的項(xiàng)目中使用了私有 NPM 包,在 Dcoker 構(gòu)建鏡像過(guò)程中會(huì)出現(xiàn) npm 私有包安裝 404 的錯(cuò)誤,如果是在容器外部我們可以 npm login 登陸擁有 NPM 私有包權(quán)限的賬戶,來(lái)解決這個(gè)問(wèn)題,但是在 Docker 的時(shí)候是不能這樣做的。
創(chuàng)建身份驗(yàn)證令牌
為了安裝私有包我們需要 “創(chuàng)建身份驗(yàn)證令牌” 以便在持續(xù)集成環(huán)境、Docker 容器內(nèi)部能訪問(wèn)我們的私有 NPM 包,如何創(chuàng)建可參考https://docs.npmjs.com/creating-and-viewing-authentication-tokens
實(shí)現(xiàn)方法
我們?cè)趧?chuàng)建 Dockerfile 文件過(guò)程中就需要增加以下兩條命令:
# 528das62-e03e-4dc2-ba67-********** 這個(gè) Token 就為你創(chuàng)建的身份驗(yàn)證令牌 token RUN echo "http://registry.npmjs.org/:_authToken=528das62-e03e-4dc2-ba67-**********" > /root/.npmrc RUN cat /root/.npmrc
Egg 框架 Docker 容器化
在 Egg 里面,如果是egg-scripts start --daemon,去掉 --daemon直接 egg-scripts start 即可,否則 Docker 容器會(huì)無(wú)法啟動(dòng)。
看以下代碼示例,修改下 package.json 即可,Dockerfile 文件同上面第一個(gè)Docker 化一個(gè) Node.js 應(yīng)用程序是一樣的
package.json
{
"scripts": {
"start": "egg-scripts start" // 去掉 --daemon
}
}
也可參考 Egg Issues “docker容器不能run起來(lái),請(qǐng)問(wèn)有碰到的嗎?”https://github.com/eggjs/egg/issues/1543
Docker 鏡像體積與構(gòu)建時(shí)間優(yōu)化
如果一個(gè)鏡像在不經(jīng)過(guò)優(yōu)化的情況下體積通常都是會(huì)很大的,以下也是在實(shí)踐過(guò)程中做的幾點(diǎn)優(yōu)化。
RUN/COPY 分層
Dockerfile 中的每條指令都會(huì)創(chuàng)建一個(gè)鏡像層,Dockerfile 指令或復(fù)制的項(xiàng)目文件在沒(méi)有修改變動(dòng)的情況下,每個(gè)鏡像層是可以被復(fù)用和緩存的。
以下代碼可在 mayjun/hello-docker:latest 鏡像倉(cāng)庫(kù)找到,以下示例中,源碼改變之后,不管 package.json 有沒(méi)有改變的情況下都會(huì)重新安裝 NPM 模塊,這樣顯然是不好的,因此下面我們要改進(jìn)
# ... WORKDIR /usr/src/nodejs/hello-docker COPY . /usr/src/nodejs/hello-docker RUN npm install # ...
改進(jìn)之后的代碼如下所示,我們讓 package.json 提前,在 package.json 沒(méi)有修改的情況下是不會(huì)重新安裝 NPM 包的,也會(huì)減少部署的時(shí)間。
# ... WORKDIR /usr/src/nodejs/ # add npm package COPY package.json /usr/src/app/package.json RUN cd /usr/src/app/ RUN npm i # copy code COPY . /usr/src/app/ # ...
Node.js Alpine 鏡像優(yōu)化
mayjun/hello-docker:1.0.0 這個(gè)鏡像在 Docker 倉(cāng)庫(kù)也可搜索到,在未優(yōu)化之前大約在 688MB
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmayjun/hello-docker 1.0.0 7217fb3e9daa 5 seconds ago 688MB
使用 Alpine 優(yōu)化
Alpine 是一個(gè)很小的 Linux 發(fā)行版,想要大幅度減小鏡像體積選擇 Node.js 的 Alpine 版本也是最簡(jiǎn)單的,另外 -alpine 的時(shí)區(qū)默認(rèn)不是國(guó)內(nèi)的,需要 Dockerfile 配置時(shí)區(qū)。
FROM node:10.0-alpine RUN apk --update add tzdata \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && apk del tzdata RUN echo "Asia/Shanghai" > /etc/timezone RUN mkdir -p /usr/src/nodejs/ WORKDIR /usr/src/nodejs/ # add npm package COPY package.json /usr/src/app/package.json RUN cd /usr/src/app/ RUN npm i # copy code COPY . /usr/src/app/ EXPOSE 30010 CMD npm start
重新打包了一個(gè)版本 mayjun/hello-docker:1.1.0 再次查看下效果,可以看到鏡像文件從 688MB 減少至 85.3MB,這個(gè)體積優(yōu)化還是很大的
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mayjun/hello-docker 1.1.0 169e05b8197d 3 minutes ago 85.3MB
生產(chǎn)環(huán)境不要打包 devDependencies 包
有些測(cè)試環(huán)境用的包,在進(jìn)行生產(chǎn)環(huán)境打鏡像時(shí)不要包含進(jìn)去,也就是 package.json 文件 devDependencies 對(duì)象,通過(guò)在 npm i 之后指定 --production 參數(shù)過(guò)濾
改進(jìn)如下所示:
FROM node:10.0-alpine # 省略 ... # add npm package COPY package.json /usr/src/app/package.json RUN cd /usr/src/app/ RUN npm i --production # 改變?cè)谶@了 # 省略 ...
重新打包了一個(gè)版本 mayjun/hello-docker:1.2.0 再次查看下效果,可以看到鏡像文件從 85.3MB 又減少至 72.3MB
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mayjun/hello-docker 1.2.0 f018aa578711 3 seconds ago 72.3MB
常見(jiàn)問(wèn)題
Question1
以下命令在刪除鏡像的時(shí)候報(bào)如下錯(cuò)誤:
$ docker rmi 6b1c2775591e Error response from daemon: conflict: unable to delete 6b1c2775591e (must be forced) - image is referenced in multiple repositories
細(xì)心的你也許會(huì)發(fā)現(xiàn)鏡像 ID 6b1c2775591e 同時(shí)指向了 hello-docker 和 mayjun/hello-docker 倉(cāng)庫(kù),這也是造成刪除失敗的原因
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7 383867b75fd2 6 days ago 373MB hello-docker latest 6b1c2775591e 7 days ago 675MB mayjun/hello-docker latest 6b1c2775591e 7 days ago 675MB
指定 repository 和 tag 來(lái)刪除,執(zhí)行刪除命令之后再次查看 mayjun/hello-docker 倉(cāng)庫(kù)就已經(jīng)沒(méi)有了
$ docker rmi mayjun/hello-docker $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7 383867b75fd2 6 days ago 373MB hello-docker latest 6b1c2775591e 7 days ago 675MB
Question2
執(zhí)行刪除鏡像命令報(bào)如下錯(cuò)誤:
$ docker rmi 9be467fd1285 Error response from daemon: conflict: unable to delete 9be467fd1285 (cannot be forced) - image is being used by running container 1febfb05b850
根據(jù)提示是有正在運(yùn)行的容器,需先停止容器、刪除容器之后在刪除鏡像
$ docker container kill 1febfb05b850 # 停止容器 $ docker rm 1febfb05b850 # 刪除容器 $ docker rmi 9be467fd1285 # 刪除鏡像
Question3
設(shè)定的工作目錄(WORKDIR)要與下面的要保持一致
... WORKDIR /usr/src/nodejs/ # add npm package COPY package.json /usr/src/node/package.json # 目錄不一致 RUN cd /usr/src/node/ # 目錄不一致 RUN npm i ...
例如,如以上配置因?yàn)楣ぷ髂夸浥c實(shí)際 COPY 的目錄不一致,會(huì)導(dǎo)致報(bào)以下錯(cuò)誤:

再按照以下方式更改為一致即可
... WORKDIR /usr/src/nodejs/ # add npm package COPY package.json /usr/src/nodejs/package.json # 更改為一致 RUN cd /usr/src/nodejs/ # 更改為一致 RUN npm i ...
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Docker部署Nuxt.js項(xiàng)目的實(shí)現(xiàn)
這篇文章主要介紹了Docker部署Nuxt.js項(xiàng)目的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
使用Docker時(shí)內(nèi)存占用過(guò)高的解決方案
在使用 Docker 進(jìn)行容器化部署時(shí),內(nèi)存占用過(guò)高是一個(gè)常見(jiàn)問(wèn)題,當(dāng)服務(wù)器內(nèi)存被耗盡時(shí),會(huì)導(dǎo)致系統(tǒng)性能下降,甚至可能導(dǎo)致服務(wù)宕機(jī),本文將介紹如何排查和解決 Docker 環(huán)境下的內(nèi)存占用問(wèn)題,需要的朋友可以參考下2024-12-12
使用minikube安裝k8s單節(jié)點(diǎn)集群方式
本文主要介紹了minikube的使用方法,包括安裝、啟動(dòng)、鏡像管理等步驟,minikube是一個(gè)專注于讓Kubernetes更易于學(xué)習(xí)和開(kāi)發(fā)的本地Kubernetes環(huán)境,支持在macOS、Linux和Windows上快速設(shè)置本地Kubernetes集群2024-10-10
基于CentOS8系統(tǒng)使用Docker搭建Gitlab的詳細(xì)教程
這篇文章主要介紹了基于CentOS8系統(tǒng)使用Docker搭建Gitlab教程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Windows/Mac系統(tǒng)Docker方式安裝Mysql(包含utf8)
本文主要介紹了Windows/Mac系統(tǒng)Docker方式安裝Mysql(包含utf8),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
docker搭建minio服務(wù)器,解決內(nèi)網(wǎng)穿透后外網(wǎng)無(wú)法訪問(wèn)
這篇文章主要介紹了docker搭建minio服務(wù)器,解決內(nèi)網(wǎng)穿透后外網(wǎng)無(wú)法訪問(wèn)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
Docker跨平臺(tái)和環(huán)境部署的過(guò)程詳解
Docker是一個(gè)開(kāi)放源代碼的容器化平臺(tái),它能夠?qū)崿F(xiàn)應(yīng)用及其依賴的打包,從而實(shí)現(xiàn)跨平臺(tái)和環(huán)境的快速部署,本文介紹了Docker的基本概念、優(yōu)勢(shì)和基本使用方法,包括安裝、啟動(dòng)、構(gòu)建鏡像和部署應(yīng)用等步驟2024-11-11
docker使用Dockerfile構(gòu)建鏡像的方法
這篇文章主要介紹了docker使用Dockerfile構(gòu)建鏡像的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
docker-compose---安裝掛載本地文件啟動(dòng)mariadb問(wèn)題
這篇文章主要介紹了docker-compose---安裝掛載本地文件啟動(dòng)mariadb問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03

