Back
Featured image of post Node 服务快速部署指南

Node 服务快速部署指南

从开始到上线,你可能只需要花 10 分钟。千里之行,始于足下,一起来尝试下吧。

项目源码:chhpt/egg-app-demo

选型

第一步,我们要根据自己的需求选择合适的框架。目前流行的 Node 框架有:Express、Koa、Egg、Nest 等。因为是个人项目,我希望这个框架功能比较齐全,不用花费较多时间就能开始使用,有一定的定制能力,同时可以方便部署。综合以上的需求,我选择了 Egg。 Egg 是一个企业级框架,意味着它有比较丰富的能力,考虑了许多业务通用能力,如路由、日志、安全、部署等,同时底层基于 Koa,拥有比较丰富的社区生态,支持 TypeScript,也拥有不错的定制能力,比较好的满足了我的需求。

初始化项目

Egg 提供了脚手架快速初始化项目的能力,只需要几条命令即可快速开始一个项目,使用起来非常方便

mkdir example && cd example
npm init egg
npm i

Egg 创建的项目已经默认包含了 ESLint 规则,但是我个人更倾向于使用 eslint-config-alloy,并结合 Prettier 进行代码格式化。如果是多人协作的项目,建议使用 EditorConfig规范编辑器行为。

日志

日志对服务的重要性是毋庸置疑的,它对错误排查,监控服务运行状态都有非常的意义,我们可以根据自己的需求输出所需的日志信息。基于 Koa 的洋葱模型,日志通常作为一个中间件存在,通常我们需要监控整个服务运行的生命周期,所以日志中间件一般为第一个使用,下面是 Egg 日志中间件的一个实例:

const logger = () => {
  return async (ctx: Context, next) => {
    const start = Date.now();
    await next();
    const end = Date.now();
    const url = `${ctx.request.method} => ${ctx.request.url}`;
    ctx.logger.info(`${url} Cost Time: ${end - start}ms.`);
  };
};

异常处理

我们的应用在运行时可会出现一些异常情况,如错误的参数输入,代码逻辑错误等。这时如果直接抛出一个 500 服务不可用的错误,会很不友好,也无法根据异常信息定位错误。所以我们最好对一些常见的异常错误进行处理、封装,添加一些可读性更高的提示信息。 错误的处理可以在对应的逻辑处理函数中进行,也可以在中间件里捕获服务中的所有异常,统一处理,或者二者结合,达到比较理想的状态

export default () => {
  return async (ctx: Context, next) => {
    try {
      await next();
    } catch (e) {
      ctx.logger.error(e);
      ctx.resError(e.message, e.code);
    }
  };
};

数据库

通常后台服务都需要读写数据库,Egg 提供了应用自定义启动的能力,我们可以在应用启动时初始化数据库连接池,保证逻辑代码可以直接操作数据库。 mongodb.ts

import { MongoClient, Db } from "mongodb";
const url = "mongodb://127.0.0.1:3717";
const dbName = "test";
const client = new MongoClient(url, { useUnifiedTopology: true });

export async function mongodbConnect(): Promise<Db> {
  await client.connect();
  const db = client.db(dbName);
  return db;
}

app.ts

import { IBoot, Application } from "egg";
import { mongodbConnect } from "./mongodb";
class AppBootHook implements IBoot {
  app: Application;
  constructor(app: Application) {
    this.app = app;
  }
  async willReady() {
    const { app } = this;
    const db = await mongodbConnect();
    app.db = db;
    app.boxCollRef = db.collection("test");
    this.app.logger.info("Mongodb is ready.");
  }
}
module.exports = AppBootHook;

应用部署

应用开发完成后就可以部署上线了,Node 应用无需编译,安装依赖即可运行,如果你使用了 TypeScript 编写代码,则需要先把 TypeScript 编译为 JavaScript 代码。

新浪云 SAE

新浪云 SAE(或 LeanCloud)提供了容器运行环境,可以直接部署 Node 应用,无需关注服务器运维问题,最适合服务快速上线。

由于容器特殊的环境,我们需要修改 Egg 的启动方式为单进程,并根据环境变量启动应用监听对应的端口,创建 index.ts 文件,写入如下内容,并修改 start 脚本为 node index.js(直接运行编译后的文件)

import { startCluster } from "egg";
const port =
  Number(process.env.PORT) || Number(process.env.LEANCLOUD_APP_PORT) || 5050;
startCluster(
  {
    port,
    workers: 1,
  },
  () => {
    console.log("App start successful.");
  }
);

在环境创建完成后,可以在运行环境管理/代码管理中找部署代码的仓库地址:https://git.sinacloud.com/appname,然后我们就可以将 Egg 项目的代码推送到这个仓库中了。

推送之后,SAE 会自动安装依赖并运行,需要注意的是 SAE 只会安装 dependencies 中的模块,而不会安装 devDependencies 中的模块。

服务器直接运行

我们也可以在服务器直接运行 Node 应用,Egg 提供了内置模块来启动 Egg 应用,并保证应用进程有足够的稳定性,不会因为异常而停止服务。直接使用 npm start 命令即可启动应用

{
  "scripts": {
    "start": "egg-scripts start --daemon",
    "stop": "egg-scripts stop"
  }
}

当然,我们也可以使用 PM2 等进程守护工具运行 Node 应用。

容器

容器是现在部署应用的主流方式之一,我们可以使用下面的 dockerfile 构建 docker 镜像,传输到服务器上使用 docker 运行。

FROM node:12.15.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/app
WORKDIR /usr/src/app
# add npm package
COPY package.json /usr/src/app/package.json
COPY yarn.lock /usr/src/app/yarn.lock
# RUN npm i --registry=https://registry.npm.taobao.org
RUN yarn --registry=https://registry.npm.taobao.org
# copy code
COPY . /usr/src/app
EXPOSE 5050
CMD npm start

构建镜像

docker build -t egg-app .

运行

docker run -d --name egg-project -p 9002:9002 egg-app

下一步

项目基本框架搭建完成后,就可以继续编写的你的项目逻辑了~

Licensed under CC BY-NC-SA 4.0