在Docker中部署NodeJS应用踩的一些坑

刚好,最近有个围绕前端工程化 + Docker 的分享,整理了一下最近自己的一些探索和实践,发现还真有不少坑。在这里记录下。废话就不说了,今天股市又绿油油一大片,赶快写完文章,回家关灯吃面(手动哭)。

我的惯例,先亮文章主题:Node 应用在 Docker 中部署的一些坑:

  • 使用 PM2 在 Docker 中部署 Node 应用
  • 使用 PM2 在 Docker 中部署 Es6 Node 应用
  • 绑定宿主目录,减少 NPM 安装,加快部署时间
  • docker + koajs + shelljs,一个吓死人的想法

docker-node

1. 使用 PM2 在 Docker 中部署 Node 应用

平时,我们部署 Node 应用时,都会使用 PM2 或 foreverjs 这样的进程管理工具。那么在 Node 中如何集成它们呢?这里仅给出集成 PM2 的方法,sorry,因为我没用过 foreverjs。Dockfile如下:

# Dockerfile

FROM node:carbon                  # 引入Node
COPY . /node-project              # 把当前目录所有内容拷贝到目录/node-project。拷贝内容受.dockerignore影响
WORKDIR /node-project             # 指定容器的工作目录为 /node-project 
RUN npm install pm2 -g            # 全局安装 pm2
RUN npm install                   # 安装项目依赖
EXPOSE 3001                       # 曝露容器端口
CMD ["pm2-runtime", "./app.js"]   # 执行启动命令

构建好 image 后,假设构建的 image 为 pm2-demo:0.0.1,则使用下列命令启动容器即可:

docker container run --name server-pm2-demo -p 4001:3001 -it pm2-demo:0.0.1

上面的 "-p 4001:3001" 表示把宿主的 4001 端口和容器的 3001 端口关联起来,外部访问时,记得用 4001 端口。这样我们就成功使用 PM2 部署了我们的 Node 应用。

2. 使用 PM2 在 Docker 中部署 Es6 Node 应用

如果你的 Node 应用是基于 ES6 甚至更高的 JS 版本。那么,启动时会报下面的错误:

import koa from 'koa';
^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)

很明显,import 语法还不支持。怎么办呢?相信做前端的都知道,用babel啊。那么在 Docker 中如何集成 babel 呢?看下面的 Dockerfile:

FROM node:carbon
COPY . /node-project 
WORKDIR /node-project 
RUN npm install pm2 -g
RUN npm install babel-cli -g      # 因为 PM2 是全局安装,所以 babel-cli 也要全局安装,避免找不到 babel-node 执行文件
RUN npm install
EXPOSE 3001
CMD ["pm2-runtime", "--interpreter", "babel-node", "./app.js"]  # 这里要注意,多了 "--interpreter", "babel-node"

Dockerfile 修改完了后,还有一部,在项目根目录加下 babel 配置:.babelrc

{
  "presets": ["env"]
}

完成这些后,就可以使用上面的提过的命令启动我们的 ES6 Node 应用啦!

3. 绑定宿主目录,减少 NPM 安装,加快部署时间

如果你在 Docker 中部署过 Node 应用,或者你按上面步骤练习过,你就会知道,不停构建和安装依赖真的非常麻烦。每次修改代码后,如果都要先构建 image 再运行容器真心太麻烦了。当然过程自动化了另说。这里提供一个解决办法:把宿主机的项目目录映射到容器中。

先修改Dockerfile,去掉项目文件拷贝和npm依赖安装:

FROM node:carbon
WORKDIR /app
RUN npm install pm2 -g
RUN npm install babel-cli -g
EXPOSE 3001
CMD ["pm2-runtime", "--interpreter", "babel-node", "./app.js"]

然后构建 image,假设这次构建的 image 为 bind-directory:0.0.1,则容器运行命令为

docker container run -v /usr/src/project:/app --name server- bind-directory -p 4000:3001 -it bind-directory:0.0.1

这里的 "-v /usr/src/project:/app" 就是把宿主机的 /usr/src/project 与容器目录 app 映射起来。这样当我们代码修改后,更新项目后,就不需要再构建 image 了,依赖修改也只需要在宿主机上直接安装即可,不用构建 image,部署方便了许多。

4. docker + koajs + shelljs,一个吓死人的想法

最近,华为发布了一个吓死人的技术,我这里也想吹下牛,说一个吓死人的想法。前面,我一直想为 Docker 做一个基于浏览器的管理平台,简单说呢,就是在浏览器中,通过 HTTP 请求操作 Docker。我最先发现的解决方案是:Docker Engine API。但我试过一些时间后,发现这玩意儿不靠谱,使用起来也非常不方便,网上文档也非常少,一时间,感觉陷入泥潭,不知道怎么办了。某天,突然灵光一闪,shelljs 不是可以执行 cmd 命令吗?于是乎,立马弄了个 koajs + shelljs 的项目,测试了下,完全满足要求。简单看下实现:

// router.js

import Router from 'koa-router';
import shell from 'shelljs';

const router = new Router({
 prefix: '/api'
});

const SUCCESS = {
 code: 0,
 service: 'docker-adapter'
};

// 展示 image 列表
router.get('/docker/image/ls', async (ctx, next) => {
 await new Promise(function (resolve, reject) {
     shell.exec('docker image ls -a', function(code, stdout, stderr) {
         ctx.body = {
             code,
             list: stdout
         };
         resolve();
     });
 });

 next();
});

// 提供 mysql 环境,为了测试方便,这里使用 GET 请求
router.get('/mysql/create/:port/:name/:password', async (ctx, next) => {
 let { port, name, password } = ctx.params;

 await new Promise(function (resolve) {
     shell.exec(`docker run --name ${name} -e MYSQL_ROOT_PASSWORD=${password} -p ${port}:3306 -d mysql:5.7`, function(code, stdout, stderr) {
         if (code === 0) {
             ctx.body = {
                 code: 0,
                 docker: {
                     name,
                     id: ''
                 },
                 mysql: {
                     port,
                     password,
                     ip: 'ip'
                 }
             }
         } else {
             ctx.body = {
                 code,
                 error: stderr
             };
         }

         resolve();
     });
 });

 await next();
});

// ...

发一个 GET 请求,就可以创建一个 mysql 环境,1s搞定,你说吓不吓人。作为 FEer,我们更擅长使用 ajax 来发请求。这是乎就是一个新大陆,可以做很多小事情了。

mysql

5. 小结

当然,Docker 技术后端使用得更多,更溜。但作为一个前端,学习与了解这些新技术个人觉得也是有必要的,除了能开阔眼界,对我们做前端工具系统也多了一些选择。由于初学 Docker,难免显得幼稚,甚至文中也有错误,欢迎交流指出。好了,以上!

6. 参考

留言列表
  • 吕小鸣:
    棍哥,最近也在用vuex,搜到你的文章,哈哈。
    • u3xyz:
      这就叫有缘千里来相会,哈哈
      2018年12月07日 20:25
    2018年12月05日 13:15 回复

发表评论: