写点什么

面向 WEB 开发人员的 Docker(七):使用 Docker 开发 Node 应用程序

作者:devpoint
  • 2021 年 12 月 17 日
  • 本文字数:4267 字

    阅读完需:约 14 分钟

面向WEB开发人员的Docker(七):使用 Docker 开发Node应用程序

到目前为止,已经使用了预构建的 Docker 镜像,例如MySQLVueNginxWordPress。都比较实用,本节来介绍在容器中开发 NodeJs 应用程序。


在本节中,将创建一个 Node.js 的“Hello”应用程序,并将该应用构建成 Docker 镜像,并从容器启动。正常情况下,该镜像可以部署到生产服务器上,Docker Compose 将用于覆盖一些设置以创建开发和调试环境。这样可以在主机 PC 上进行编码开发,这样文件将在一个持续运行的容器中执行。这有几个好处:


  • Docker 将管理所有依赖项—— 不需要安装和维护 runtimes

  • 这个过程与本地开发没什么不同——可以使用任何喜欢的编辑器和工具

  • 容器是隔离的——应用程序影响到主机 PC,如使删除文件

  • 任何时候都可以将应用程序分发给其他开发人员或测试人员——应用程序可以在任何其他设备上以零配置相同的方式运行。


本节创建的代码文件在项目 https://github.com/QuintionTang/docker-nodejs

基于容器的应用开发

Docker 简化了 Web 开发:任何的 Web 应用程序都可以在单个容器中运行。


但是……如果想将类似的容器部署到实时生产服务器,应用程序通常是无状态的。这样可以启动任意数量的实例,任何实例都可以对请求做出响应。实际上,应用程序不应该将基本状态数据存储在本地文件或内存中。


例如:当用户登录时,应用程序将登录凭据存储在内存中。在开发过程中使用单个容器,都可以按预期运行没有问题。


如果将应用程序部署到生产服务器并在两个以上容器中运行,这些容器通过负载均衡接收请求。用户访问系统由 container1 处理其登录。那么下一个请求可能就由 container2 提供服务,容器之间并没有共享登录状态,这个时候就会出现未登录的情况。


当然上面的问题是可以通过解决的,为隔离的容器提供一个中心存储服务,维护应用的持久化存储数据,例如数据库。


无状态 Web 应用程序是一个不错的方式。这样在生产环境中随着用户情况的增加可以快速进行扩缩容,自动添加更多的机器/容器。在解决实际需求的时候就需要考虑是否适合无状态,如果对有状态的应用程序进行转换可能是不可行的。


这些在开发过程中都无关紧要,因为通常只会在单个容器中运行应用程序。如果不实用,就不必在生产中使用容器。

什么是 Node.js

这个想必大部份掘金的小伙伴都知道,这里不展开介绍,引用一段简单的说明。


Node.js 是一种流行的、高性能 JavaScript 运行时,使用 Chrome 浏览器的 V8 JavaScript 引擎构建。它通常用于服务器端 Web 开发,但也已被前端或客户端用来构建工具、桌面应用程序、嵌入式系统等所采用。


安装 Node.js 后,可以使用以下命令执行 JavaScript 文件:


node index.js
复制代码


单入口脚本文件是什么?理论上它可以命名为任何名称,通常项目都使用index.js 作为入口。


前面的内容一直在使用 Docker Hub 提供的 Docker 镜像。本节将介绍如何构建自己的 Docker 镜像,该镜像可以在开发和生产环境中安装和执行应用程序。


可能你对 Node.js 不感兴趣,但是不管使用何种语言(PHP、Python、Ruby、Go、Rust 等)都适合使用 Docker 。

Hello 应用概述

该项目将使用 Node.js 的 Express.js 框架创建了一个“Hello”应用程序。


应用运行地址为:http://localhost:3005/,返回纯文本格式:Hello Devpoint!


从客户端 Ajax 请求调用相同的 URL 会返回 JSON 编码的对象:


{ "message": "Hello Devpoint!" }
复制代码


当传入请求的 HTTP 标头设置为时,可以识别 Ajax 调用。这是由大多数 Ajax 库添加了: X-Requested-WithXMLHttpRequest


可以向 URL 路径添加字符串,例如http://localhost:3005/devpoint 将返回 Hello Devpint!,响应内容为:


{ "message": "Hello Devpoint!" }
复制代码

项目初始化

在项目目录中执行以下代码初始化项目:


npm init
复制代码



输入基本的信息后,会在项目根目录下生成 package.json


接下来安装 express ,执行一下命令:


npm install express --save
复制代码


为了开发过程中能够响应代码的变更,接下来安装 Nodemon,执行以下命令:


npm install nodemon --save-dev
复制代码


nodemon 用来监听 node.js 项目中文件的更改并自动重启服务的工具,接下来为项目增加监听规则,如需要忽略的目录:


{    "script": "./index.js",    "ext": "js json",    "ignore": [        "node_modules/"    ],    "legacyWatch": true,    "delay": 200,    "verbose": true}
复制代码


修改项目 package.json ,在 scripts 属性下添加启动命令:


"start": "node ./index.js","debug": "nodemon --trace-warnings --inspect=0.0.0.0:9229 ./index.js",
复制代码


这样在终端可以执行一下的命令:


  • npm start : 一般用于生产环境

  • npm run debug :用于开发调试

应用脚本 index.js

脚本在根路由下定义简单的响应请求


"use strict";const port = process.env.NODE_PORT || 3005, // 定义HTTP默认端口或者从NODE_PORT环境变量获取    express = require("express"),    app = express();// 根路由app.get("/:title?", (req, res) => {    const message = `Hello ${req.params.title || "Devpoint"}!`;    if (req.xhr) {        res.set("Access-Control-Allow-Origin", "*").json({ message });    } else {        res.send(message);    }});// 启动HTTP服务app.listen(port, () => console.log(`server running on port ${port}`));
复制代码


接下来开始执行脚本:


npm run debug
复制代码


打开浏览器输入http://localhost:3005/,可以看到响应的响应,如下



现在可以尝试去修改脚本 index.js 的内容,当有更新的时候,终端会重启服务,刷新浏览器即可看到更新。



到目前为止,一个简单的 NodeJS 应用程序已经完成。接下来将介绍如何在 Docker 环境里面运行调试。

Docker 配置

前面章节有介绍如何在 Docker 里面运行应用程序,主要介绍 docker compose,镜像文件一般都是现成的,可以查看《面向WEB开发人员的Docker(六):使用nginx部署静态网站


本文将介绍另一种方式,自己制定 Docker 镜像,一般建议为项目制定两个镜像,一个用于开发调试,一个用于生产环境。

Dockerfiles

Dockerfile 定义了安装和执行应用程序所需的构建环境,一个可以随时方便运行的镜像。


通常从 Docker Hub 基础镜像开始,本文的应用程序需要的 Nodejs 镜像:



每个标签引用一个单独的镜像(用自己的 Dockerfile 创建)如Node.js


  • 很多镜像都很大,一般 100MB 以上,因为它们包含完整的Linux OS操作系统。

  • slim 镜像是一般是精简版Linux OS ,包含运行Node.js所需的最小软件包集。如果希望将Node.js容器部署到有限的空间环境中,这些就会很有用。

  • alpine 镜像基于 Alpine Linux ,通常是 5MB 左右。如果需要尽可能小的镜像,并且对操作系统库的依赖有限,这个版本就非常有用。


lts-alpine 对于本文的应用程序已经足够了:它提供了一个带有 Node.js 最新版本的小镜像。


在应用程序的根目录中创建一个Dockerfile,代码如下:


# 基于 Node.js 的 lts镜像FROM node:lts-alpine
# 定义环境变量ENV WORKDIR=/data/node/appENV NODE_ENV=productionENV NODE_PORT=3005
# 创建应用程序文件夹并分配权限给 node 用户RUN mkdir -p $WORKDIR && chown -R node:node $WORKDIR
# 设置工作目录WORKDIR $WORKDIR
# 设置活动用户USER node
# 复制 package.json 到工作目录COPY --chown=node:node package.json $WORKDIR/
# 安装依赖RUN npm install && npm cache clean --force
# 复制其他文件COPY --chown=node:node . .
# 暴露主机端口EXPOSE $NODE_PORT
# 应用程序启动命令CMD [ "node", "./index.js" ]
复制代码


基础镜像一般都是从 FROM node:lts-alpine 开始,每一行定义了一个步骤,用于安装和运行 Node.js 应用程序。关于 Dockerfile 的语法可以参阅 Dockerfile 指南。下面列举了一些常见的命令:



用户安全

创建镜像时,Dockerfile 命令以 root(超级)用户身份运行。这个操作一般是安全的,因为当发生严重异常事件的时候,可以自动重新启动容器。


当然以更受限制的用户身份运行应用程序更安全,本文示例创建用户 node 来启动应用程序。这样即便应用程序被不法分子恶意控制,它也没有权限操作应用程序所在文件夹以外的文件,将安全风险限制在应用程序所在的文件夹内。

启动命令

最佳的方式就是通过直接调用其可执行文件来启动应用程序:


CMD [ "node", "./index.js" ]
复制代码


这样可以保证将 STDERR 等系统消息返回给 Docker,以便可以做出相应的响应,如当应用程序崩溃时重新启动容器。

.dockerignore

COPY命令将所有应用程序文件从主机目录复制到 Docker 镜像,通常情况下不需要复制所有文件,这个时候可以通过 .dockerignore 来定义不需要复制的文件或者文件夹,本实例定义的规则如下:


Dockerfile
.git.gitignore.config
.npm.vscodenode_modulespackage-lock.jsonREADME.md
复制代码

构建镜像

下面就从 Dockerfile 构建铭文 nodehello 的镜像,在根目录下执行一下命令:


docker image build -t nodehello .
复制代码


命令末尾的 . 点是必不可少的,它代表着应用程序路径。



确定是否构建成功,可以执行命令 docker image ls nodehello,将看到:


从镜像启动容器

现在就可以使用一下命令启动 nodehello 镜像


docker run -it --rm --name nodehello -p 3005:3005 nodehello
复制代码


在浏览器中打开 http://localhost:3005/ 可以看到 Hello Devpoint! 内容。


打开新的终端,启动开发环境,同样在项目根目录下,执行以下命令:


docker run -it --rm  --name nodehello  -p 3005:3005  -p 9229:9229  -e NODE_ENV=development  -v $PWD:/data/node/app --entrypoint '/bin/sh'  nodehello  -c 'npm install && npm run debug'
复制代码


上面的命令以开发模式启动容器并将主机上的项目目录挂载到容器中的路径 /data/node/app


现在来修改 index.js 文件,将 Devpoint 改为 Juejin,回到浏览器刷新可以看到内容变更为:Hello Juejin!

使用 Chrome 调试 Node.js

谷歌 Chrome 和基于 Chrome 的浏览器,如 Edge、Opera,都有一个内置的Node.js调试器,确保应用程序运行在一个开发模式容器中,然后启动浏览器输入:chrome://inspect



如果没有看到Remote Target,请确保选中Discover network targets,单击Configure... 并为 <_229 class="calibre"> 添加一个连接(如果是在另一台设备上运行容器,则添加一个网络地址)。


点击 Target 下方的链接 inspect 启动 DevTools ,切换到 Console,可以日志 server running on port 3005



切换到 Source 面板,按照提示按下 Ctrl|Cmd + P , 输入 index.js , 选择第一个路径为:/data/node/app/index.js



添加两个断点,如图:



回到打开 http://localhost:3005/ 的页面刷新页面,可以看到程序执行到断点。



接下来,如何调试,应该都很熟悉了,这里不继续展开了。

发布于: 1 小时前阅读数: 6
用户头像

devpoint

关注

细节的追求者 2011.11.12 加入

专注前端开发,用技术创造价值!

评论

发布
暂无评论
面向WEB开发人员的Docker(七):使用 Docker 开发Node应用程序