Node.js服务Docker容器化应用实践小结

本篇不会讲解 Docker 命令的使用、安装等,因为在之前一篇文章一文零基础教你学会 Docker 入门到实践中也已经讲解的很详细了,不清楚的可以点击链接回头在重新看下,本篇重点是介绍 Node.js 项目如何进行 Docker 容器化及一些实践优化,还有一些常见的问题,当然如果还有其它使用上的问题也欢迎大家在评论区进行留言补充。

作者简介:五月君,Nodejs Developer,热爱技术、喜欢分享的 90 后青年,公众号「Nodejs技术栈」,Github 开源项目 www.nodejs.red

通过本篇文章能学到什么?

  • 学会如何用 Docker 容器化一个 Node.js 服务
  • 动态设置环境变量一份 Dockerfile 文件构建不同的版本
  • Node.js 私有 NPM 包在构建镜像时如何认证
  • Egg.js 框架 Docker 容器化应该注意的问题
  • Docker 镜像体积与构建时间的优化

Docker 化一个 Node.js 应用程序

在本篇开始我们先创建一个简单的 Node.js 应用,然后为这个应用创建一个 Docker 镜像,并构建和运行它

创建 Node.js 项目

首先我们需要创建一个 app.js 开启一个 HTTP 服务,后面会借助 Docker 来运行这个程序

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 devnpm run pro两个命令,因为我想在这里介绍如何在构建时传入参数来动态设置环境变量。

{
 "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 入门与实践中也有讲解过

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 容器中

.git
node_modules
npm-debug.log

此时通过以下命令即可构建一个 Docker 镜像

$ docker image build -t mayjun/hello-docker

再通过 docker run -d -p 30010:30010 mayjun/hello-docker 命令可运行一个 Docker 容器,但是有个疑问我是有生产和测试之分的,按照上面CMD npm run dev这样写死只能打包一种环境,当然你也可以在建一个文件来实现或者一些其它的方法。

动态设置环境变量

为了解决上面的疑问,我的想法是在镜像构建时传入参数来动态设置环境变量,对 Dockerfile 文件做下修改,看以下实现:

EXPOSE 30010

ARG node_env # 新增加
ENV NODE_ENV=$node_env # 新增加
CMD npm run ${NODE_ENV} # 修改

下面对上面的代码做个解释

  • 通过 ARG 指令定义了一个变量,用户可以在构建时通过使用 --build-arg = 标志的 docker build 命令将其传递给构建器 ARG node_env
  • 在 Dockerfile 中使用 ENV 引用这个变量 ENV NODE_ENV=$node_env
  • 这一步就是使用了 CMD npm run ${NODE_ENV}

剩下的就是在构建镜像时动态传入参数了

$ 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 . # 构建生产环境

运行容器

$ 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 包

如果你的项目中使用了私有 NPM 包,在 Dcoker 构建镜像过程中会出现 npm 私有包安装 404 的错误,如果是在容器外部我们可以 npm login 登陆拥有 NPM 私有包权限的账户,来解决这个问题,但是在 Docker 的时候是不能这样做的。

创建身份验证令牌

为了安装私有包我们需要 “创建身份验证令牌” 以便在持续集成环境、Docker 容器内部能访问我们的私有 NPM 包,如何创建可参考https://docs.npmjs.com/creating-and-viewing-authentication-tokens

实现方法

我们在创建 Dockerfile 文件过程中就需要增加以下两条命令:

# 528das62-e03e-4dc2-ba67-********** 这个 Token 就为你创建的身份验证令牌 token
RUN echo "//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 容器会无法启动。

看以下代码示例,修改下 package.json 即可,Dockerfile 文件同上面第一个Docker 化一个 Node.js 应用程序是一样的

package.json

{
 "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 模块,这样显然是不好的,因此下面我们要改进

# ...

WORKDIR /usr/src/nodejs/hello-docker
COPY . /usr/src/nodejs/hello-docker

RUN npm install

# ...

改进之后的代码如下所示,我们让 package.json 提前,在 package.json 没有修改的情况下是不会重新安装 NPM 包的,也会减少部署的时间。

# ...

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

$ 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 配置时区。

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,这个体积优化还是很大的

$ 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 参数过滤

改进如下所示:

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

$ docker images
REPOSITORY  TAG   IMAGE ID  CREATED  SIZE
mayjun/hello-docker 1.2.0  f018aa578711 3 seconds ago 72.3MB

常见问题

Question1

以下命令在删除镜像的时候报如下错误:

$ docker rmi 6b1c2775591e
Error response from daemon: conflict: unable to delete 6b1c2775591e (must be forced) - image is referenced in multiple repositories

细心的你也许会发现镜像 ID 6b1c2775591e 同时指向了 hello-docker 和 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
mayjun/hello-docker latest  6b1c2775591e 7 days ago  675MB

指定 repository 和 tag 来删除,执行删除命令之后再次查看 mayjun/hello-docker 仓库就已经没有了

$ 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

执行删除镜像命令报如下错误:

$ docker rmi 9be467fd1285
Error response from daemon: conflict: unable to delete 9be467fd1285 (cannot be forced) - image is being used by running container 1febfb05b850

根据提示是有正在运行的容器,需先停止容器、删除容器之后在删除镜像

$ docker container kill 1febfb05b850 # 停止容器
$ docker rm 1febfb05b850 # 删除容器
$ docker rmi 9be467fd1285 # 删除镜像

Question3

设定的工作目录(WORKDIR)要与下面的要保持一致

...
WORKDIR /usr/src/nodejs/

# add npm package
COPY package.json /usr/src/node/package.json # 目录不一致
RUN cd /usr/src/node/ # 目录不一致
RUN npm i
...

例如,如以上配置因为工作目录与实际 COPY 的目录不一致,会导致报以下错误:

再按照以下方式更改为一致即可

...
WORKDIR /usr/src/nodejs/

# add npm package
COPY package.json /usr/src/nodejs/package.json # 更改为一致
RUN cd /usr/src/nodejs/ # 更改为一致
RUN npm i
...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 一步步教你利用Docker设置Node.js

    前言 docker是一个开源的应用容器引擎,可以为我们提供安全.可移植.可重复的自动化部署的方式.docker采用虚拟化的技术来虚拟化出应用程序的运行环境.如上图一样.docker就像一艘轮船.而轮船上面的每个小箱子可以看成我们需要部署的一个个应用.使用docker可以充分利用服务器的系统资源,简化了自动化部署和运维的繁琐流程,减少很多因为开发环境中和生产环境中的不同引发的异常问题.从而提高生产力. docker三个核心概念如下: 镜像(images):一个只读的模板,可以理解为应用程序的运行环

  • Docker使用编写dockerfile启动node.js应用

    编写 Dockerfile 以 express 自动创建的目录为例,目录结构如下: ├── /bin │ └── www ├── /node_modules ├── /public ├── /routes ├── /views ├── package-lock.json ├── package.json ├── ecosystem.config.js ├── app.js └── Dockerfile 在项目目录下新建 Dockerfile 文件 FROM node:10.15 MAINTAIN

  • 从零搭建docker+jenkins+node.js自动化部署环境的方法

    本次案例基于CentOS 7系统 适合有一定docker使用经验的人阅读 适合有一定linux命令使用经验的人阅读 1.docker部分 1.1.docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何接口 1.2.docker架构 简单的说,docker就是一个轻量级的linux系统.Docker 容器通过 Docker 镜像来创建.

  • 详解Windows下运用Docker部署Node.js开发环境

    开始 在windows下部署nodejs开发环境着实遍地坑,每遇到一个问题都要去google原因再试图解决.而且如果你想把你写好的应用交给别人跑跑看,他可能同样需要折腾很久才能真正在他的环境下运行起来.被坑了好些时日最终还是放弃,转战Docker. 文章开头先明确一下我们希望实现的效果: 1.依然在Windows下编辑源代码,在Docker容器中运行代码,最后在Windows的浏览器中看到运行结果,方便后续debug. 2.可以将我开发完成的程序和运行环境一起打包制作成Docker的image,

  • 在Docker快速部署Node.js应用的详细步骤

    一.前言 可能还有一些同学不了解docker这个项目,docker是由go语言编写的,一个快速部署的轻量级虚拟技术项目,他允许开发人员将自己的程序和运行环境一起打包,制作成一个docker的image(镜像),这样部署到服务器上,也只需要下载这个image就可以将程序跑起来,免去每次都安装各种依赖和环境的麻烦,还能够做到应用程序之间的隔离 二.实现准备 我会先创建一个简单的Node.js web app,来构建一个镜像.然后基于这个Image运行一个container.从而实现快速部署. 由于网

  • Docker部署Node.js的方法步骤

    前言 项目中会用到node做中间层,部署node则是用到了docker,在这里总结.记录下部署要点和步骤:关于docker的介绍和安装这里就不赘述了,网上也有很多相关的教程和文章了,需要可自行搜索查看. 项目结构 `-- docker-node |-- data |-- server |-- app.js |-- Dockerfile |-- process.yml |-- package.json |-- docker-compose.yml 1.创建Node.js程序 app.js cons

  • Node.js服务Docker容器化应用实践小结

    本篇不会讲解 Docker 命令的使用.安装等,因为在之前一篇文章一文零基础教你学会 Docker 入门到实践中也已经讲解的很详细了,不清楚的可以点击链接回头在重新看下,本篇重点是介绍 Node.js 项目如何进行 Docker 容器化及一些实践优化,还有一些常见的问题,当然如果还有其它使用上的问题也欢迎大家在评论区进行留言补充. 作者简介:五月君,Nodejs Developer,热爱技术.喜欢分享的 90 后青年,公众号「Nodejs技术栈」,Github 开源项目 www.nodejs.r

  • 使用Portainer部署Docker容器的项目实践

    一.背景 最近在使用rancher2.5.5部署Redis主从复制的时候,发现rancher会产生很多iptables的规则,这些规则导致我们在部署了rancher的机器上无法使用Redis的主从复制功能,因为我对rancher和k8s的了解也仅限于了解网络架构和使用,对底层并不深入,短期内无法解决这个网络冲突的问题: 因此我将rancher管理docker的模式换成使用protainer的方式,这个portainer相对来说更加轻量级,在搭建过程中也使用了几个小时学习,现在讲整个过程尽量复原给

  • Docker容器化应用与结构

    目录 容器化应用 什么是容器化应用 应用怎么打包 Docker 镜像组成 联合文件系统 Linux 内核 Docker 结构 Docker 服务与客户端 Docker 客户端 容器运行时 Docker 引擎 Docker 引擎变化 Docker 引擎的架构 containerd shim runc 容器化应用 什么是容器化应用 containerized applications 指容器化的应用,我们常常说使用镜像打包应用程序,使用 Docker 发布.部署应用程序,那么当你的应用成功在 Doc

  • Docker容器的Tengine实践

    作为目前最火的应用,Docker 确实存在着其独到之处,无论是程序猿还是运维都应该听说过 Docker 的大名,Docker 已经走过了许多的坑,目前最新版本是 v1.11.0 版本,应该说是完全能承载开发使用和运维监控,这款工具能帮助我们高效的打包.发布和运行承载着应用程序的容器系统.而且收集日志.帮助 App 的快速开发都有很大作用. 容器和虚拟机,经常是被拿出来对比的两款产品,实际上两者有着根本的差别,虚拟机是完全模拟了一台真实计算机,在上面运行的系统可能或者不可能知道自己运行在虚拟化环境

  • Docker容器化部署尝试——多容器通信(node+mongoDB+nginx)

    原因是这样的 想要部署一个mocker平台,就在朋友的推荐下选择了 api-mocker 这个现成的项目 该项目分为服务端node.客户端vue.以及数据库mongoDB 在尝试直接部署的时候发现需要装一大堆的环境,node.mongo.nginx啊,特别的麻烦,之前简单的使用过docker,就在想能不能用docker免环境直接部署呢?于是就有了这次的尝试 多容器通信 该项目分为3个部分,于是就要建立3个容器(node.mongo.nginx) 那容器之间怎么实现通信呢? # 通过link指令建

  • Django Docker容器化部署之Django-Docker本地部署

    本章将在本地搭建一个容器化的 Django 项目,感受 Docker 的运作方式. 前期准备 开发环境 虽然有基于 Windows 的 Docker 版本,但各方面兼容做得都不太好(安装也麻烦些),因此建议读者在学习前,自行安装好 Linux 或 Mac 系统.当然你愿意折腾的话,在 Windows 上搞也行. 别担心,以后开发 Django 项目仍然可以在 Windows 下进行,仅仅是开发时不使用 Docker 而已. 软件安装 Docker:学习 Docker 当然要安装 Docker 软

  • 基于NodeJS的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践

    淘宝网线上应用的传统软件栈结构为 Nginx + Velocity + Java,即: 在这个体系中,Nginx 将请求转发给 Java 应用,后者处理完事务,再将数据用 Velocity 模板渲染成最终的页面. 引入 Node.js 之后,我们势必要面临以下几个问题: 技术栈的拓扑结构该如何设计,部署方式该如何选择,才算是科学合理?项目完成后,该如何切分流量,对运维来说才算是方便快捷?遇到线上的问题,如何最快地解除险情,避免更大的损失?如何确保应用的健康情况,在负载均衡调度的层面加以管理?承系

  • Docker容器化spring boot应用详解

    前置条件 容器化spring boot应用所需环境: jdk 1.8 + maven 3.0 + 我们的需求是:使用maven打包,将spring boot应用制作成docker镜像并上传到docker hub.在其他机器上,可以直接docker pull并运行容器. 创建spring boot应用 spring boot 包结构为: └── src └── main └── java └── me └── ithakar 创建spring boot Application主类,src/main

  • 详解利用ELK搭建Docker容器化应用日志中心

    概述 应用一旦容器化以后,需要考虑的就是如何采集位于Docker容器中的应用程序的打印日志供运维分析.典型的比如SpringBoot应用的日志 收集.本文即将阐述如何利用ELK日志中心来收集容器化应用程序所产生的日志,并且可以用可视化的方式对日志进行查询与分析,其架构如下图所示: 架构图 镜像准备 镜像准备 ElasticSearch镜像 Logstash镜像 Kibana镜像 Nginx镜像(作为容器化应用来生产日志) 开启Linux系统Rsyslog服务 修改Rsyslog服务配置文件: v

  • Docker容器化部署Python应用过程解析

    简单应用部署 一.目录结构: └── Pythonpro #目录 └── test.py #文件 └── requirements.txt #文件 └── Dockerfile #文件 二.编写Dockerfile文件 # 基于镜像基础 FROM python:3.6.4 # 创建代码文件夹工作目录 /code RUN mkdir /code # 复制当前代码文件到容器中 /code COPY . /code # 安装所需的包 RUN pip install -r /code/requireme

随机推荐