# NodeJS运行环境

# 运行环境说明

# 工作流程

NodeJS运行环境是新浪云推出的本地可写的NodeJS语言运行环境,通过Git hook触发自动构建逻辑,将提交的代码打包为Docker镜像,然后由新浪云的容器管理引擎启动生成的镜像实例运行。

# 工作流程示意图

容器运行环境示意图

总体的流程分为以下步骤:

  1. 从本地通过Git客户端提交代码到仓库,如何部署请参考容器环境Git管理示例
  2. 新浪云的Git仓库通过触发器启动一个同步构建镜像的任务,解析代码中指定的需要安装的NodeJS版本、下载依赖的包等,制作一个Docker的镜像;
  3. 如果以上过程没有错误,则将镜像推送到新浪云的镜像中心,并通知托管服务管理系统;如果出错,从Git的回显通知具体的错误;
  4. 调度新建或者替换正在运行的镜像实例,启动后将更新后的运行环境更新到负载均衡;
  5. 所有的实例更新或则启动完成后,通过Git回显告知提交成功。

# 创建应用

  1. 登录云应用管理平台https://sae.sinacloud.com (opens new window),如果没有账号请先注册;

  2. 点击“+创建应用”进入应用的创建页面; 点击创建应用进入应用管理页面

  3. 开发语言选择“NodeJS”;

  4. 运行环境选择“独享环境”;

  5. 选择单实例的配置; 选择NodeJS应用配置

  6. 选择实例个数,开发阶段可以选择1,可以随时调整;

  7. 输入应用的二级域名前缀,二级域名的组成规则请参考应用名,应用名称(应用的中文介绍); 输入应用名、应用描述等

  8. 点击“确认创建”完成创建。

说明

  • 每个实例中将会拥有相同的代码,每个实例的资源配额为创建时指定的单实例配置
  • 实例的个数可以随时修改;
  • 计费的规则为:单实例价格 * 实例总数 * 实例的运行时长,按分钟计费。

# 计费说明

# 计费规则

计费的规则为:单实例价格 * 实例总数 * 实例的运行时长,按分钟计费。

其中单实例的价格可以从价格中心 (opens new window)查询。

例如单实例的配置为标准型,单价为0.06元每实例每小时,开启的实例个数为2,则一天的价格为:

0.06 * 2 * 24 = 2.88

# 流量计费

当前独享型NodeJS的流量不计费。

# 指定NodeJS的版本

package.json文件的engines字段可以指定node.js的版本。

{
  "name": "nodejs-getting-started",
  "description": "getting started with nodejs on sinacloud platform",
  "version": "0.0.1",
  "private": true,
  "author": {
    "name": "sae",
    "email": "saemail@sina.cn"
  },
  "dependencies": {
    "express": "^4.13.3"
  },
  "engines": {
    "node": "10.10.0"
  },
  "scripts": {
    "start": "node index.js"
  }
}

如果不清楚本地环境使用的NodeJS的版本可以执行:

$ node --version
v4.1.1

获取,写到engines的node字段的需要去掉v

提示

如果指定的NodeJS版本不存在,构建程序将默认使用最高的stable版本,如果你的程序在最新版本下无法运行,请从package.json中强制指定需要的node.js版本。

# 指定Npm版本

大部分时候,不需要单独指定npm版本,使用NodeJS自带的npm就可以了。

Npm 2.1.x 以前的版本有很多已知的问题,如果你使用的NodeJS版本的npm比较老,你可以通过以下方法指定你要使用的npm版本。

{
  "name": "myapp",
  "description": "a really cool app",
  "version": "0.0.1",
  "engines": {
    "npm": "2.1.x"
  }
}

# 自定义构建过程

如果你有一些自定义的脚本需要在构建的时候运行,你可以使用npmpostinstall脚本,该脚本会在所有构建动作完成后最后执行,例:

{
  "scripts": {
    "start": "node index.js",
    "test": "mocha",
    "postinstall": "bower install && grunt build"
  }
} 

# 依赖管理

请将项目的依赖加到package.json的dependencies中,例如:

{
  "name": "nodejs-getting-started",
  "description": "getting started with nodejs on sinacloud platform",
  "version": "0.0.1",
  "private": true,
  "author": {
    "name": "sae",
    "email": "saemail@sina.cn"
  },
  "dependencies": {
    "express": "^4.13.3"
  },
  "engines": {
    "node": "10.10.0"
  },
  "scripts": {
    "start": "node index.js"
  }
}

默认情况下,npm install会安装在package.json中的dependencies下列出的所有模块,如果指定了--production参数或者NODE_ENV环境变量被设置为production时,devDependencies中指定的依赖不会被安装。

说明

  • 执行npm install --save-dev时会将模块保存在devDependencies下
  • 执行npm install --save时会将模块保存在dependencies下

您在上传代码时不需要上传node_modules文件夹,这个文件夹在上传后将重新生成,因此如果有项目必须依赖的模块,请将模块保存到dependencies中,否则会导致项目启动失败。

另外,请不要使用-g将模块安装到全局,项目依赖全局模块上传后也会导致启动失败。

# 进程启动

服务端最终调用 npm start 来启动应用,请将启动脚本放到package.json的scripts中的start中,如果没有启动命令,应用将无法启动。

请注意

代码提交后我们会调用npm start启动程序,因此start中必须指定项目启动的命令,且该命令需要在前台执行,如果npm start指定的进程退出,运行项目的容器会重启。

# Hello world

# 准备工作

首先,我们从github上clone一个nodejs的示例程序https://github.com/sinacloud/nodejs-getting-started (opens new window)

git clone https://github.com/sinacloud/nodejs-getting-started.git
cd nodejs-getting-started
ls
README.md    index.js     package.json

示例代码中包含了三个文件,其中 index.js 是代码文件, package.jsonnpm 的包管理描述文件。

index.js 的代码如下,这是一个简单的 Express (opens new window) 应用,注意代码的最后一行,所有的NodeJS应用都需要监听环境变量的 PORT 指定的端口(默认为5050),并处理这个端口收到的所有HTTP请求。

var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(process.env.PORT || 5050)

下面是 package.json 的代码,其中的 dependencies 指定了本示例程序的依赖包, scripts/start 里指定了如何运行本示例代码。(新浪云在部署应用的时候会根据这个文件来安装应用相关的依赖以及来启动应用程序)。

{
  "name": "nodejs-getting-started",
  "description": "getting started with nodejs on sinacloud platform",
  "version": "0.0.1",
  "private": true,
  "author": {
    "name": "sae",
    "email": "saemail@sina.cn"
  },
  "dependencies": {
    "express": "^4.13.3"
  },
  "engines": {
    "node": "10.10.0"
  },
  "scripts": {
    "start": "node index.js"
  }
}

我们执行 npm install && npm start ,在本地运行这个示例程序,现在我们可以通过 http://localhost:5050 (opens new window) 来访问我们的应用了。

$ npm install && npm start
express@4.13.3 node_modules/express
├── escape-html@1.0.2
...
└── send@0.13.0 (destroy@1.0.3, statuses@1.2.1, ms@0.7.1, mime@1.3.4, http-errors@1.3.1)

> nodejs-getting-started@0.0.1 start /private/tmp/nodejs_helloworld
> node index.js

# 创建一个NodeJS应用

在新浪云创建一个NodeJS应用,如何创建请参考创建应用章节说明。

# 部署应用

应用创建完后,会显示你的应用的 git 仓库地址以及如何将代码提交到该 git 仓库,按照代码部署说明提交代码。

$ git remote add sinacloud https://git.sinacloud.com/helloworld
$ git push sinacloud master
...
remote: Exporting git code...
remote: Uploading...
-----> Xxx app detected
-----> Creating runtime environment
...
remote: Deploy and waiting for app to be ready .....
To https://git.sinacloud.com/ymp9vm2385
 + 74d7d50...470cb46 master -> master

部署的时候,git 会显示当前的进度,如果部署失败,会提示相应的错误信息(比如 package.json 格式问题等)。

部署完成后,我们就可以通过 http://helloworld.applinzi.com (opens new window) 来访问我们的应用了。

说明

请把helloworld换成您自己创建的应用名。

# 访问应用

部署成功后,通过应用的二级域名即可访问应用:

访问应用

# 管理容器

部署成功后,从应用的管理中心,选择左侧导航“运行环境管理”,进入“容器管理”中心可以看到正在运行的容器。

容器列表

点击操作中的“终端”即可进入容器的终端:

进入容器终端

执行ls -al可以看到运行环境的目录结构:

u12345@5a482d0f0e24:~$ ls -al 
total 48
drwxr-xr-x  7 u12345 u12345   232 Sep 20 19:12 .
drwxr-xr-x 22 root   root     291 Oct  9 14:20 ..
drwxr-xr-x  2 u12345 u12345    18 Sep 20 19:12 .basher
drwx------  3 u12345 u12345    25 Sep 20 19:12 .config
drwxr-xr-x  3 u12345 u12345    18 Sep 20 19:12 .heroku
drwxr-xr-x  2 u12345 u12345    23 Sep 20 19:12 .profile.d
-rw-r--r--  1 u12345 u12345    51 Sep 20 19:12 .release
-rw-r--r--  1 u12345 u12345    15 Sep 20 19:12 Procfile
-rw-r--r--  1 u12345 u12345   849 Sep 20 19:12 README.md
-rw-r--r--  1 u12345 u12345    75 Sep 20 19:12 config.yaml
-rw-r--r--  1 u12345 u12345   156 Sep 20 19:12 index.js
-rw-r--r--  1 u12345 u12345  2073 Sep 20 19:12 master
drwxr-xr-x 50 u12345 u12345  4096 Sep 20 19:12 node_modules
-rw-r--r--  1 u12345 u12345 13116 Sep 20 19:12 package-lock.json
-rw-r--r--  1 u12345 u12345   360 Sep 20 19:12 package.json

关于node_modules

可以看到node_modules目录已经自动创建,因此提交的代码中不需要包含node_modules目录及其目录下的文件。

# 协议支持

应用支持http、https和websocket协议(ws、wss)。

# 端口映射

# 工作原理

应用的负载均衡(新浪云提供)监听80和443端口,然后将请求转发到容器中的5050端口,示意图如下:

协议及端口示意

# 支持的端口

容器中可以启动多个端口,但是仅5050端口会映射到负载均衡上,其他的端口只能在本地访问。可以从环境变量中获取支持的端口,环境变量的key为:

PORT 

可以通过:

process.env.PORT

获取支持的端口。

请注意

  • 另外HTTP等服务器应该监听在0.0.0.0,不要监听在127.0.0.1,如果监听在127.0.0.1,仅在容器上运行的程序才可以访问,外部无法访问;
  • 如果使用HTTPS访问,在负载均衡到容器间会转变为HTTP协议,可以从HTTP的header头中判断是否有HTTP-X-PROTO头,如果这个header头的值为SSL表示本次请求为HTTPS访问;
  • 如上所述,HTTPS证书应该传到负载均衡处(从管理面板的域名绑定处上传),不应该放到容器中。

# 日志系统

# 容器日志分类

容器日志分类stdoutstderr分类,分别是标准输出和标准错误输出的捕获。

因为容器的磁盘空间有限,大量的写入可能会占满磁盘导致服务不可用,请从程序中配置日志输出路径,将日志写到stdoutstderr分类,其中:

  • /proc/1/fd/1stdout的绝对路径
  • /proc/1/fd/2stderr的绝对路径

# 查询容器stdout日志

  1. 进入应用的列表页面https://sae.sinacloud.com (opens new window)
  2. 点击操作处的管理进入应用的管理中心
  3. 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
  4. 选择“容器日志”的“stdout”分类 查询stdout分类日志

# 查询容器stderr日志

  1. 进入应用的列表页面https://sae.sinacloud.com (opens new window)
  2. 点击操作处的管理进入应用的管理中心
  3. 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
  4. 选择“容器日志”的“stderr”分类 查询stderr分类日志

# SSH登录

# 说明

NodeJS容器支持通过在线SSH终端登录到容器中查看进程的状态和系统的信息。

# 如何登录

  1. 进入应用的列表页面https://sae.sinacloud.com (opens new window)
  2. 点击操作处的管理进入应用的管理中心
  3. 左侧导航选择“运行环境管理”,选择“容器管理”进入应用的容器列表页面
  4. 点击需要管理的容器示例的“操作”区域的“终端”即可进入web版本的终端 进入web terminal
  5. 输入Linux命令可以查询进程的状态、执行其他的操作 在web terminal执行ps -ef命令

# 文件系统

# 本地文件系统

实例在创建时将会分配5GB的本地磁盘,用于存放程序的代码和系统文件等,你可以从程序往本地写文件,但本地的文件不是持久化存储,在重启后文件会删除

警告

  • 如果你需要持久化存储文件,请不要将文件写到本地文件系统,可以通过以下几种方式存储文件:

# 挂载共享存储

共享存储服务是为容器云提供的分布式存储服务,利用多副本写入实现数据高可靠性,并提升读取性能,可以提供数十M/S的峰值读写性能。

您可以通过挂载,将对应的共享存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。

通过共享存储,还可以在不同的容器之间共享数据,实现跨容器的数据共享和交互。

有关共享存储的更多介绍,请参考共享存储章节,请参考以下步骤挂载共享存储:

  1. 共享存储 (opens new window)管理面板创建一个共享存储;
  2. 云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
  3. 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面; 如何进入容器管理页面
  4. 点击“挂载管理”进入挂载管理页面; 进入磁盘挂载的页面
  5. 点击“+新增挂载”,存储的分类选择“共享存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。 填写挂载的信息

    说明

    挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。

  6. 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情: 挂载详情

挂载完成后,从容器的实例中可以看到挂载的路径: 在容器中查看挂载的路径

共享存储中的文件支持FTP管理,详情请参考共享存储的FTP管理章节。

挂载完共享存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径下了。

# 挂载本地存储

本地存储服务是为容器云提供的本地问年间存储服务,可以和本地磁盘读写一样速度的磁盘。

您可以通过挂载,将对应的本地存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。

有关共享存储的更多介绍,请参考本地存储章节,请参考以下步骤挂载本地存储:

  1. 本地存储 (opens new window)管理面板创建一个本地存储;
  2. 云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
  3. 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面; 如何进入容器管理页面
  4. 点击“挂载管理”进入挂载管理页面; 进入磁盘挂载的页面
  5. 点击“+新增挂载”,存储的分类选择“本地存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。 填写挂载的信息

    说明

    挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。

  6. 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情: 挂载详情

挂载完成后,从容器的实例中可以看到挂载的路径: 在容器中查看挂载的路径

本地存储中的文件支持FTP管理,详情请参考本地存储的FTP管理章节。

挂载完地存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径了。