本文共 3382 字,大约阅读时间需要 11 分钟。
随着工程数量的快速增长,在实践基于Node.js的微服务开发过程中,我们遇到了以下问题:
为了解决上述问题,我们开发了一个命令行小工具来标准化项目初始化流程、简化配置甚至是零配置,提供基于Docker的一致构建、运行环境。
新建一个Node.js项目时,我们通常会执行以下操作:
然而,选择复制现成项目进行修改的做法,导致了众多看似相似却又不完全相同的项目。对于同时跨多个工程的开发人员来说,众多配置组合会显著增加工作难度。此外,当安全审计发现某些npm包存在安全隐患时,开发人员需要对每个引用这些包的项目逐一检查和修正。
在确定的开发场景下,几乎所有项目的开发依赖都非常相似,开发配置也非常一致。因此,我们基于commander.js开发了一个init工具,它会通过命令行向导自动安装依赖、初始化项目目录结构和配置,从而创建项目,并按照场景将所有配置收缩为特定几种模板,进行统一处理。
随后,我们开发了build、test、pack命令,托管了tsconfig、jest配置、打包配置,自动调用tsc编译,构建测试环境,然后调用Jest进行测试,进行标准化打包。CI脚本基本可以简化为几行标准脚本。
在介绍这个命令前,需要先简单了解个推的镜像体系。前面提到我们将大部分依赖封装到了一个npm包,这一层封装也反映在个推的Docker镜像体系内。以下是一个简化的Dockerfile示例:
# 公共依赖层的DockerfileFROM node:10RUN mkdir -p /usr/local/lib/webnode/node_modules && \ cd /usr/local/lib/webnode && \ npm install webnodeENV NODE_PATH /usr/local/lib/webnode/node_modules# 项目的DockerfileFROM getui/webnode:1.2.3COPY package*.json ./RUN npm installCOPY . .
当将这层依赖直接做进Docker镜像时,尽管每个镜像的大小还是1G多,但每个镜像的UNIQUE SIZE都非常小,仅有数M的差分层。
一个简单的对比:
在考虑应用的多版本之后,依赖分层共享带来的存储优势会更加明显。我们以一定的依赖锁定周期和控制为代价,换取了以下优势:
webnode docker build命令可以帮助开发者进行统一化的镜像构建、统一实践最佳优化,节约资源,还能避免所有开发人员都需要接触优化细节,省时省力。
在本地调试开发过程中,我们遇到了一些环境差异引起的问题:
与本地直接启动Node.js程序不同,这个命令会优先基于当前项目利用webnode docker build命令构建Docker镜像,然后启动镜像。
Docker可以帮助消解环境差异:
容器化的Node.js调试方法有些许变化,需要暴露Node.js的Inspector端口,然后配上Visual Studio Code的localRoot和remoteRoot。以下是一个示例:
WEBNODE_HOST=${WEBNODE_HOST:-127.0.0.1}WEBNODE_PORT=${WEBNODE_PORT:-3000}DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS \ -it \ --rm \ --network=\"getui-dev\" \ -p $WEBNODE_HOST:$WEBNODE_PORT:3000 \ -p 127.0.0.1:9229:9229 \ -e NODE_FLAGS=--inspect=0.0.0.0:9229 \ --name $CONTAINER"docker run \ $DOCKER_RUN_OPTIONS \ $DOCKER_IMAGE_TAG 基于容器的开发可以带来诸多好处:
sed在Linux和Mac下的工作行为不一致;所有的依赖通过容器带进来,简洁而高效。
在基于Docker的工具开发过程中,我们也遇到了一些问题:
docker run,会导致容器内程序在挂载的目录产生的文件权限与当前用户不一致;gosu去做降权来处理:CLI_EXEC_UID=${CLI_EXEC_UID:-0}CLI_EXEC_GID=${CLI_EXEC_GID:-0}exec gosu $CLI_EXEC_UID:$CLI_EXEC_GID env "$@"实际RedHat旗下用于设计container runtime的daemonless(例如podman),很适合做CLI工具,可以rootless运行,又尊重系统的权限配置。然而其目前尚未成熟,业界采用率也不高,仍需要继续观望。
此外,有时候docker run速度较慢。个推的解决方案是在首次启动时启动一个docker run --detach,然后后续的CLI执行完全通过docker exec来进行,这样避免掉了每次执行命令时启动的开销,速度提升明显。
以上便是个推Node.js微服务开发实践中关于CLI工具的实践。个推试图通过标准化、优化项目结构以及镜像构建,减少组合的可能性,有效降低了存储、传输、构建的成本,让开发人员更加省时省力。
后续我们还会继续为大家介绍个推的Docker镜像体系设计以及Node.js微服务开发框架,敬请期待。
转载地址:http://kpjfk.baihongyu.com/