Windows上的Docker中的Node.js开发环境

时间:2015-11-23 00:23:21

标签: node.js windows docker

我已经尝试了所有我能想到的东西。我已阅读文档,博客并尝试在github上进行以下示例。

但我似乎无法让它发挥作用。

我想做的很简单。我想在我的Windows 8.1机器上编写我的node.js代码,我还想从Docker容器中运行代码,而不必一直重建容器。所以我想将Windows主机上的目录映射到容器内的目录。

我创建了这个Dockerfile

FROM node:0.10.38

RUN apt-get update -qq && apt-get install -y build-essential

ENV ZMQ_VERSION 4.1.3
ENV LIBSODIUM_VERSION 1.0.3

RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \
    && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \
    && cd libsodium-$LIBSODIUM_VERSION \
    && ./configure \
    && make \
    && make install \
    && cd .. \
    && rm -r libsodium-$LIBSODIUM_VERSION \
    && rm libsodium-$LIBSODIUM_VERSION.tar.gz
RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \
    && tar xvf zeromq-$ZMQ_VERSION.tar.gz \
    && cd zeromq-$ZMQ_VERSION \
    && ./configure \
    && make \
    && make install \
    && cd .. \
    && rm -r zeromq-$ZMQ_VERSION \
    && rm zeromq-$ZMQ_VERSION.tar.gz
RUN ldconfig

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

RUN mkdir -p /usr/src/app
ADD . /usr/src/app
WORKDIR /usr/src/app
RUN npm install

EXPOSE 3000
EXPOSE 35729

CMD ["npm", "start"]

我有这个简单的server.js文件

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

app.get('/', function (req, res) {
  res.send('ZMQ: ' + zmq.version);
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

这个简单的package.json

{
  "name": "docker-node-hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.13.3",
    "zmq": "^2.14.0"
  }
}

我安装了最新的Docker Toolbox,可以运行Docker Hello World示例。当我在Dockerfile所在的目录中时,我尝试构建这样的docker镜像。

docker build -t dockernodetest:dockerfile .

然后我尝试从Docker Quickstart终端内的相同位置运行它。我使用哈希,因为标签不出于某种原因:

docker run -v //c/Bitbucket/docker-node-hello-world/:/usr/src/app -p 3000:3000 -i -t 9cfd34e046a5 ls ./usr/src/app

结果是一个空目录。我希望我可以调用

docker run -v //c/Bitbucket/docker-node-hello-world/:/usr/src/app -p 3000:3000 -i -t 9cfd34e046a5 npm start

但由于主机目录不可用,因此失败。我觉得我误解了一些非常基本的东西。我只是不知道是什么。

2 个答案:

答案 0 :(得分:4)

首先让我们从Dockerfile

开始
FROM node:0.10.38-onbuild

RUN apt-get update -qq && apt-get install -y build-essential

ENV ZMQ_VERSION 4.1.3
ENV LIBSODIUM_VERSION 1.0.3

RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \
    && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \
    && cd libsodium-$LIBSODIUM_VERSION \
    && ./configure \
    && make \
    && make install \
    && cd .. \
    && rm -r libsodium-$LIBSODIUM_VERSION \
    && rm libsodium-$LIBSODIUM_VERSION.tar.gz
RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \
    && tar xvf zeromq-$ZMQ_VERSION.tar.gz \
    && cd zeromq-$ZMQ_VERSION \
    && ./configure \
    && make \
    && make install \
    && cd .. \
    && rm -r zeromq-$ZMQ_VERSION \
    && rm zeromq-$ZMQ_VERSION.tar.gz
RUN ldconfig

EXPOSE 3000 35729

从第1行开始,我使用了0.10.38-onbuild代码,因为我想利用onbuild scripts运行来创建/usr/src/app目录并运行npm install

然后server.jspackage.json就像你写的一样。它们和Dockerfile都在同一个工作目录中。

接下来我们构建图像

docker build -t dockernodetest .

我省略了dockerfile标签,因为它似乎没必要。无论如何,客户端将自动添加latest标记。要查看您在本地运行的图像docker images

此时我们应该准备好运行一个映像但是让我们先检查一下我们要加载的文件然后npm install创建了node_modules目录

$ docker run dockernodetest ls /usr/src/app
Dockerfile
node_modules
package.json
server.js

我们已准备好运行我们的小nodejs app

$ docker run -it -p 8080:3000 dockernodetest

> docker-node-hello-world@1.0.0 start /usr/src/app
> node server.js

Example app listening at http://0.0.0.0:3000

在这个例子中,我使用-p 8080:3000标志将容器的3000端口映射到主机上的端口8080。请注意,我最后没有任何其他命令,因为我提取的-onbuild图像具有CMD [ "npm", "start" ],因此默认操作是运行start包脚本。

因此,为了使开发周期更快,您希望通过-v选项将工作目录挂载到容器

$ docker run -it -p 8080:3000 -v "$PWD":/usr/src/app dockernodetest

> docker-node-hello-world@1.0.0 start /usr/src/app
> node server.js


module.js:340
    throw err;
          ^
Error: Cannot find module 'express'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/usr/src/app/server.js:1:77)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)

npm ERR! Linux 4.1.7-15.23.amzn1.x86_64
npm ERR! argv "node" "/usr/local/bin/npm" "start"
npm ERR! node v0.10.38
npm ERR! npm  v2.11.1
npm ERR! code ELIFECYCLE
npm ERR! docker-node-hello-world@1.0.0 start: `node server.js`
npm ERR! Exit status 8

但是这里发生了什么?因为我们挂载了当前的工作目录,所以它覆盖了/usr/src/app中包含我们node_modules目录的内容。

所以这里的快速解决方法是在我们当前的工作目录中运行npm install并重新运行docker run命令。

$ npm install
docker-node-hello-world@1.0.0 /home/ec2-user/dockernode
└─┬ express@4.13.3
  ├─┬ accepts@1.2.13
  │ ├─┬ mime-types@2.1.7
...
$ docker run -it -p 8080:3000 -v "$PWD":/usr/src/app dockernodetest

> docker-node-hello-world@1.0.0 start /usr/src/app
> node server.js

Example app listening at http://0.0.0.0:3000

现在,如果您对server.js文件进行更新,只需按 Ctrl + c 并重新启动docker image,尽管您可能希望使用类似{ {3}}使这更加无缝。

答案 1 :(得分:0)

非常感谢您的回答。这无疑使我走上了正确的道路。我花了比我想象的更长的时间,但我得到了它的工作。

有些事情没有用,但我终于开始工作了。以下是我所经历的内容的简要说明。

  1. 我无法按照您的建议使用onbuild脚本。原因是当我将我的图像基于onbuild脚本时,它在我安装ZeroMQ之前运行npm install

  2. 在我映射驱动器时,无法按照您的建议在我的泊坞窗图像外运行npm install。我想在Docker中运行它的原因之一是我想要一个所有依赖项(如ZeroMQ)安装并可用的环境。这在某些环境中是困难的,我想在Windows和Linux主机上运行它。

  3. 我真的想在开发时使用nodemon,所以我还必须在映像中全局安装它,并在启动容器时调用nodemon。因此,我必须按照您的建议扩展您的示例。

  4. 我在使映射的卷工作时遇到了很多麻烦。事实证明,在Windows主机上,您必须位于C:\users\<username>的上下文中才能从容器映射主机上的卷。有一次,我想出了所有奇怪的实验,并完成了所有工作,我完成了工作。在调用"$PWD"时建议使用docker run在Windows主机上也很奇怪。您必须使用这样的正斜杠作为前缀:/"$PWD"

  5. 当我想到这一切时,我开始关注docker compose,部分是因为我不想继续输入长的docker run命令(我一直都错了),还因为在我真正的项目中我想要我的数据库和我需要的其他服务的多个容器。

  6. 所以这就是现在的样子。它的工作方式与我希望它的工作方式完全一致。我现在有一个容器,其中所有依赖项都安装在容器内,每当我运行我的容器时,它首先调用npm installnodemon server.js。所有文件(包括已安装的npm模块)都在主机上,但映射到运行所有内容的容器内。

    文件1 - docker-compose.yml(注意我不需要$ PWD变量,但只能使用.相对路径作为主机路径)

    web:
      build: .
      volumes:
        - ".:/usr/src/app"
      ports:
        - "3000:3000"
    

    文件2 - Dockerfile

    FROM node:0.10.40
    
    RUN mkdir /usr/src/app
    RUN mkdir /usr/src/node_modules
    
    RUN apt-get update -qq && apt-get install -y build-essential
    
    ENV ZMQ_VERSION 4.1.3
    ENV LIBSODIUM_VERSION 1.0.3
    
    RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \
        && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \
        && cd libsodium-$LIBSODIUM_VERSION \
        && ./configure \
        && make \
        && make install \
        && cd .. \
        && rm -r libsodium-$LIBSODIUM_VERSION \
        && rm libsodium-$LIBSODIUM_VERSION.tar.gz
    RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \
        && tar xvf zeromq-$ZMQ_VERSION.tar.gz \
        && cd zeromq-$ZMQ_VERSION \
        && ./configure \
        && make \
        && make install \
        && cd .. \
        && rm -r zeromq-$ZMQ_VERSION \
        && rm zeromq-$ZMQ_VERSION.tar.gz
    RUN ldconfig
    
    RUN npm install -g nodemon
    WORKDIR /usr/src/app
    
    CMD export NODE_PATH=/usr/src/node_modules && cp package.json /usr/src && npm install --prefix /usr/src && npm start
    
    EXPOSE 3000 35729
    

    文件3 - package.json(注意我在调用nodemon时使用-L标志。在容器内运行时需要这样做。

    {
      "name": "docker-node-hello-world",
      "version": "1.0.0",
      "description": "node hello world",
      "main": "server.js",
      "scripts": {
        "start": "nodemon -L server.js"
      },
      "author": "Author",
      "license": "ISC",
      "dependencies": {
        "express": "^4.13.3",
        "zmq": "^2.14.0"
      }
    }
    

    文件4 - server.js

    var express = require('express');
    var app = express();
    var zmq = require('zmq');
    
    app.get('/', function (req, res) {
      res.send('ZMQ Version: ' + zmq.version);
    });
    
    var server = app.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;
    
      console.log('Example app listening at http://%s:%s', host, port);
    });
    

    要构建使用以下命令

    docker-compose build
    

    要运行,请使用以下命令

    docker-compose up
    

    此时我可以在我的主机上开发我的应用程序,每当我更改文件时,我的节点服务器都会重新启动。当我向package.json添加新依赖项时,我必须重新启动容器才能运行npm install

    我唯一没有工作的是如何使我的图像重建顺利进行。发生的事情是,在我构建新映像之后,我必须删除旧容器才能运行新容器。映射由旧容器锁定。

    我真的很想给你@jeedo。没有你的帮助,我不会达到这个解决方案。我不知道该怎么做,并将此标记为解决方案。

    编辑:我刚刚编辑了这个,以便为我的Dockerfile添加一些内容。我将node_modules文件夹移离主机文件系统。我在Windows上路径变得太长时遇到了一些麻烦。这些更改确保node_modules始终安装在容器内。