时间:2021-05-02
本篇不会讲解 docker 命令的使用、安装等,因为在之前一篇文章一文零基础教你学会 docker 入门到实践中也已经讲解的很详细了,不清楚的可以点击链接回头在重新看下,本篇重点是介绍 node.js 项目如何进行 docker 容器化及一些实践优化,还有一些常见的问题,当然如果还有其它使用上的问题也欢迎大家在评论区进行留言补充。
作者简介:五月君,nodejs developer,热爱技术、喜欢分享的 90 后青年,公众号「nodejs技术栈」,github 开源项目www.nodejs.red
通过本篇文章能学到什么?
docker 化一个 node.js 应用程序
在本篇开始我们先创建一个简单的 node.js 应用,然后为这个应用创建一个 docker 镜像,并构建和运行它
创建 node.js 项目
首先我们需要创建一个 app.js 开启一个 http 服务,后面会借助 docker 来运行这个程序
? 1 2 3 4 5 6 7 8 9 10 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); });然后我们创建一个 package.json 文件,这里是描述你的应用程序以及需要的依赖,写过 node.js 的同学应该会很熟悉的,这里我在 scripts 里面增加了npm run dev、npm run pro两个命令,因为我想在这里介绍如何在构建时传入参数来动态设置环境变量。
? 1 2 3 4 5 6 7 8 9 10 11 { "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 文件
这是一个 dockerfile 文件所包含的信息,这些命令在docker 入门与实践中也有讲解过
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 的同级文件下创建一个 .dockerignore 文件,避免将你本地的调试文件、node_modules 等一些文件放入 docker 容器中
? 1 2 3 .git node_modules npm-debug.log此时通过以下命令即可构建一个 docker 镜像
? 1 $ docker image build -t mayjun/hello-docker再通过 docker run -d -p 30010:30010 mayjun/hello-docker 命令可运行一个 docker 容器,但是有个疑问我是有生产和测试之分的,按照上面cmd npm run dev这样写死只能打包一种环境,当然你也可以在建一个文件来实现或者一些其它的方法。
动态设置环境变量
为了解决上面的疑问,我的想法是在镜像构建时传入参数来动态设置环境变量,对 dockerfile 文件做下修改,看以下实现:
? 1 2 3 4 5 expose 30010 arg node_env # 新增加 env node_env=$node_env # 新增加 cmd npm run ${node_env} # 修改下面对上面的代码做个解释
剩下的就是在构建镜像时动态传入参数了
? 1 2 $ docker image build --build-arg node_env=dev -t mayjun/hello-docker:1.0.2 . # 构建测试环境 $ docker image build --build-arg node_env=pro -t mayjun/hello-docker:1.0.2 . # 构建生产环境运行容器
? 1 2 3 4 $ 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查看容器日志
? 1 2 3 4 5 6 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 包
如果你的项目中使用了私有 npm 包,在 dcoker 构建镜像过程中会出现 npm 私有包安装 404 的错误,如果是在容器外部我们可以 npm login 登陆拥有 npm 私有包权限的账户,来解决这个问题,但是在 docker 的时候是不能这样做的。
创建身份验证令牌
为了安装私有包我们需要 “创建身份验证令牌” 以便在持续集成环境、docker 容器内部能访问我们的私有 npm 包,如何创建可参考https://docs.npmjs.com/creating-and-viewing-authentication-tokens
实现方法
我们在创建 dockerfile 文件过程中就需要增加以下两条命令:
? 1 2 3 # 528das62-e03e-4dc2-ba67-********** 这个 token 就为你创建的身份验证令牌 token run echo "//registry.npmjs.org/:_authtoken=528das62-e03e-4dc2-ba67-**********" > /root/.npmrc run cat /root/.npmrcegg 框架 docker 容器化
在 egg 里面,如果是egg-scripts start --daemon,去掉 --daemon直接 egg-scripts start 即可,否则 docker 容器会无法启动。
看以下代码示例,修改下 package.json 即可,dockerfile 文件同上面第一个docker 化一个 node.js 应用程序是一样的
package.json
? 1 2 3 4 5 { "scripts": { "start": "egg-scripts start" // 去掉 --daemon } }也可参考 egg issues “docker容器不能run起来,请问有碰到的吗?”https://github.com/eggjs/egg/issues/1543
docker 镜像体积与构建时间优化
如果一个镜像在不经过优化的情况下体积通常都是会很大的,以下也是在实践过程中做的几点优化。
run/copy 分层
dockerfile 中的每条指令都会创建一个镜像层,dockerfile 指令或复制的项目文件在没有修改变动的情况下,每个镜像层是可以被复用和缓存的。
以下代码可在 mayjun/hello-docker:latest 镜像仓库找到,以下示例中,源码改变之后,不管 package.json 有没有改变的情况下都会重新安装 npm 模块,这样显然是不好的,因此下面我们要改进
? 1 2 3 4 5 6 7 8 # ... workdir /usr/src/nodejs/hello-docker copy . /usr/src/nodejs/hello-docker run npm install # ...改进之后的代码如下所示,我们让 package.json 提前,在 package.json 没有修改的情况下是不会重新安装 npm 包的,也会减少部署的时间。
? 1 2 3 4 5 6 7 8 9 10 11 12 13 # ... 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 镜像优化
mayjun/hello-docker:1.0.0 这个镜像在 docker 仓库也可搜索到,在未优化之前大约在 688mb
? 1 $ docker imagesrepository tag image id created sizemayjun/hello-docker 1.0.0 7217fb3e9daa 5 seconds ago 688mb使用 alpine 优化
alpine 是一个很小的 linux 发行版,想要大幅度减小镜像体积选择 node.js 的 alpine 版本也是最简单的,另外 -alpine 的时区默认不是国内的,需要 dockerfile 配置时区。
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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重新打包了一个版本 mayjun/hello-docker:1.1.0 再次查看下效果,可以看到镜像文件从 688mb 减少至 85.3mb,这个体积优化还是很大的
? 1 2 3 $ docker images repository tag image id created size mayjun/hello-docker 1.1.0 169e05b8197d 3 minutes ago 85.3mb生产环境不要打包 devdependencies 包
有些测试环境用的包,在进行生产环境打镜像时不要包含进去,也就是 package.json 文件 devdependencies 对象,通过在 npm i 之后指定 --production 参数过滤
改进如下所示:
? 1 2 3 4 5 6 7 8 9 10 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 # 改变在这了 # 省略 ...重新打包了一个版本 mayjun/hello-docker:1.2.0 再次查看下效果,可以看到镜像文件从 85.3mb 又减少至 72.3mb
? 1 2 3 $ docker images repository tag image id created size mayjun/hello-docker 1.2.0 f018aa578711 3 seconds ago 72.3mb常见问题
question1
以下命令在删除镜像的时候报如下错误:
$ docker rmi 6b1c2775591eerror response from daemon: conflict: unable to delete 6b1c2775591e (must be forced) - image is referenced in multiple repositories细心的你也许会发现镜像 id 6b1c2775591e 同时指向了 hello-docker 和 mayjun/hello-docker 仓库,这也是造成删除失败的原因
? 1 2 3 4 5 $ 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 来删除,执行删除命令之后再次查看 mayjun/hello-docker 仓库就已经没有了
? 1 2 3 4 5 $ 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 675mbquestion2
执行删除镜像命令报如下错误:
? 1 2 $ docker rmi 9be467fd1285 error response from daemon: conflict: unable to delete 9be467fd1285 (cannot be forced) - image is being used by running container 1febfb05b850根据提示是有正在运行的容器,需先停止容器、删除容器之后在删除镜像
? 1 2 3 $ docker container kill 1febfb05b850 # 停止容器 $ docker rm 1febfb05b850 # 删除容器 $ docker rmi 9be467fd1285 # 删除镜像question3
设定的工作目录(workdir)要与下面的要保持一致
? 1 2 3 4 5 6 7 8 ... workdir /usr/src/nodejs/ # add npm package copy package.json /usr/src/node/package.json # 目录不一致 run cd /usr/src/node/ # 目录不一致 run npm i ...例如,如以上配置因为工作目录与实际 copy 的目录不一致,会导致报以下错误:
再按照以下方式更改为一致即可
? 1 2 3 4 5 6 7 8 ... workdir /usr/src/nodejs/ # add npm package copy package.json /usr/src/nodejs/package.json # 更改为一致 run cd /usr/src/nodejs/ # 更改为一致 run npm i ...以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://juejin.im/post/5d8d61b8f265da5b76372850
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
什么是Node.js的模块(Module)?在Node.js中,模块是一个库或框架,也是一个Node.js项目。Node.js项目遵循模块化的架构,当我们创建了
Node.js是一个开源JavaScript运行时环境。在这里,您将学习如何在CentOS8服务器上安装Node.js。什么是Node.js?Node.js是一
Node.js是最流行的JavaScript服务端平台,它允许建立可扩展的Web应用程序。Node.js包含不同类型的框架,如MVC框架、全栈框架、RESTAP
本文介绍了Node.js五大应用性能技巧,分享给大家,具体如下:一、实现一个反向代理服务器相比大多数应用服务器,Node.js可以很轻松的处理大量的网络流量,但
Linux使用Node.js建立访问静态网页的服务实例详解一、安装node.js运行所需要的环境,:二、创建node目录(/node/www),并在目录下创建n