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

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

到目前为止,已经使用了预构建的 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

image.png

输入基本的信息后,会在项目根目录下生成 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/,可以看到响应的响应,如下

image.png

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

image.png

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

Docker 配置

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

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

Dockerfiles

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

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

image.png

每个标签引用一个单独的镜像(用自己的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/app
ENV NODE_ENV=production
ENV 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 指南。下面列举了一些常见的命令:

命令 描述
# 注释的开始
FROM 指定开始的基础镜像,一般是从 Docker Hub 开始构建镜像
ARG 构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量
ENV 定义环境变量,在Dockerfile文件后续代码中使用,在容器运行时也可以使用
WORKDIR 设置工作目录,即在容器内部的路径
USER 用于指定镜像中执行后续命令的用户和用户组
VOLUME 挂载数据卷
EXPOSE 声明端口
COPY 添加文件(夹)到容器
RUN 在创建镜像是执行,即使用docker build命令时执行
CMD 在运行容器时执行,即使用docker run命令时执行

用户安全

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

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

启动命令

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

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

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

.dockerignore

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

Dockerfile
.git
.gitignore
.config
.npm
.vscode
node_modules
package-lock.json
README.md

构建镜像

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

docker image build -t nodehello .

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

image.png

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

image.png

从镜像启动容器

现在就可以使用一下命令启动 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

image.png

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

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

image.png

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

image.png

添加两个断点,如图:

image.png

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

image.png

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


相关文章
|
16天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
1月前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
407 3
|
7天前
|
SQL 安全 Go
如何在 Python 中进行 Web 应用程序的安全性管理,例如防止 SQL 注入?
在Python Web开发中,确保应用安全至关重要,主要防范SQL注入、XSS和CSRF攻击。措施包括:使用参数化查询或ORM防止SQL注入;过滤与转义用户输入抵御XSS;添加CSRF令牌抵挡CSRF;启用HTTPS保障数据传输安全;实现强身份验证和授权系统;智能处理错误信息;定期更新及审计以修复漏洞;严格输入验证;并培训开发者提升安全意识。持续关注和改进是保证安全的关键。
16 0
|
9天前
|
缓存 负载均衡 数据库
优化后端性能:提升Web应用响应速度的关键策略
在当今数字化时代,Web应用的性能对于用户体验至关重要。本文探讨了如何通过优化后端架构和技术手段,提升Web应用的响应速度。从数据库优化、缓存机制到异步处理等多个方面进行了深入分析,并提出了一系列实用的优化策略,以帮助开发者更好地应对日益增长的用户访问量和复杂的业务需求。
13 1
|
9天前
|
缓存 监控 数据库
Flask性能优化:打造高性能Web应用
【4月更文挑战第16天】本文介绍了提升Flask应用性能的七大策略:优化代码逻辑,减少数据库查询,使用WSGI服务器(如Gunicorn、uWSGI),启用缓存(如Flask-Caching),优化数据库操作,采用异步处理与并发(如Celery、Sanic),以及持续监控与调优。通过这些手段,开发者能有效优化Flask应用,适应大型或高并发场景,打造高性能的Web服务。
|
10天前
|
数据库 开发者 Python
Python中使用Flask构建简单Web应用的例子
【4月更文挑战第15天】Flask是一个轻量级的Python Web框架,它允许开发者快速搭建Web应用,同时保持代码的简洁和清晰。下面,我们将通过一个简单的例子来展示如何在Python中使用Flask创建一个基本的Web应用。
|
14天前
|
JavaScript 前端开发 API
Vue.js:构建高效且灵活的Web应用的利器
Vue.js:构建高效且灵活的Web应用的利器
|
15天前
|
JavaScript 前端开发 Docker
全栈开发实战:结合Python、Vue和Docker进行部署
【4月更文挑战第10天】本文介绍了如何使用Python、Vue.js和Docker进行全栈开发和部署。Python搭配Flask创建后端API,Vue.js构建前端界面,Docker负责应用的容器化部署。通过编写Dockerfile,将Python应用构建成Docker镜像并运行,前端部分使用Vue CLI创建项目并与后端交互。最后,通过Nginx和另一个Dockerfile部署前端应用。这种组合提升了开发效率,保证了应用的可维护性和扩展性,适合不同规模的企业使用。
|
22天前
|
XML JSON JavaScript
使用JSON和XML:数据交换格式在Java Web开发中的应用
【4月更文挑战第3天】本文比较了JSON和XML在Java Web开发中的应用。JSON是一种轻量级、易读的数据交换格式,适合快速解析和节省空间,常用于API和Web服务。XML则提供更强的灵活性和数据描述能力,适合复杂数据结构。Java有Jackson和Gson等库处理JSON,JAXB和DOM/SAX处理XML。选择格式需根据应用场景和需求。
|
26天前
|
前端开发 JavaScript 数据管理
描述一个使用Python开发Web应用程序的实际项目经验,包括所使用的框架和技术栈。
使用Flask开发Web应用,结合SQLite、Flask-SQLAlchemy进行数据管理,HTML/CSS/JS(Bootstrap和jQuery)构建前端。通过Flask路由处理用户请求,模块化代码提高可维护性。unittest进行测试,开发阶段用内置服务器,生产环境可选WSGI服务器或容器化部署。实现了用户注册登录和数据管理功能,展示Python Web开发的灵活性和效率。
14 4