在Docker中运行Node.js的Web应用
jopen
10年前
本文是十七蝉同学撰写的基础实战类博客,作者通过代码的形式Step by step介绍了如何在Docker中运行Node.js应用。初学的同学可以一读。
在Docker环境下搭建了Node.js的Web应用运行环境:
Node.js
MongoDB
Redis
winston和morgan,日志
以下介绍一下搭建环境的步骤和注意事项。
准备工作
需要安装Docker,我的环境是Ubuntu Serer 14.04虚拟机。如果直接用apt-get install docker.io无法获得比较新的Docker版本。我参照这里:Docker 1.2 on Ubuntu 14.04.1,安装了Docker 1.2版本。即使用Docker官方的第三方Ubuntu源。加入Docker的GPG Key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
加入Docker的源:
sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
更新包列表:
sudo apt-get update
安装Docker
sudo apt-get install lxc-docker
重启系统:
sudo reboot
如果执行下面命令并看到类似的结果就说明安装成功了:
$ docker version Client version: 1.2.0
最简单的通过Docker执行Node.js
执行一个简单的Node.js命令:node --version
不是使用本地的Node.js,而是使用Docker,只需执行:
$ sudo docker run -it --rm node node --version v0.10.33
对于第一次运行上面命令,会出现类似:
Unable to find image 'node' locally Pulling repository node 63d7e1e1d897: Pulling dependent layers 511136ea3c5a: Download complete 36fd425d7d8a: Download complete aaabd2b41e22: Download complete f99c114b8ec1: Downloading [==> ...
Docker本地并没有node的镜像(image),需要到官网(https://hub.docker.com)上查询这个名字的镜像,并下载到本地。这个过程可能比较漫长,在我这里需要30分钟左右。总之,下载完镜像(700多MB)后,镜像会启动一个容器(container)。可以把镜像看做Java的类(class),容器看做对象(object)。
这个容器包含一个最小的可运行的轻量级的虚拟机,当然还有Node.js。
说下命令的参数:
docker run -it --rm node node --version
其中--it:
- i,容器的标准输入保持打开
- t,Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入
--rm,运行结束后删除容器。再后面就是我们要执行的命令。
将Web Application跑起来
首先,要准备一个简单的Web Application。我这里写好了一个简单的应用ProtoWebApp。拿到项目文件后,先用宿主的node安装:$ sudo npm install
然后跑起来测试一下,看是否能在浏览器上访问。
下面,是用Docker里的Node.js跑这个Web Application了(在项目的根目录下):
sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp node npm start
在这里:
-v后:分割的路径,前者表示宿主的路径(在这里也就是expressjs项目的主目录),后者表示映射到Docker容器的路径。
-w,表示将-v映射的/webapp目录设置为work directory,也就是运行node命令的目录。这个设置将覆盖Dockfiie中的设置:/Data。
如果需要让Docker容器跑在后台,可以加上-d:
sudo docker run --rm -itd -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp node npm start
另外,如想了解这个镜像都包含哪些内容,可以看这里:Dockerfile/Node.js
日志的处理
运维中需要记录几种日志:- HTTP请求日志,为了以后分析访问量等数据时使用
- 应用日志,可能有错误或者其他调试信息,便于发现错误,排错
HTTP请求日志
很多情况下未必用到这个,因为在Node.js的Web Appp前,可能还有Nginx,用后者做端口代理。目前的Expressjs,是4.x版本,使用的HTTP日志,是morgan。可以在app.js中看到:var logger = require('morgan');
默认的日志是对接到标准输出上的。我们希望在生产环境(production)下和开发环境(development)情况下不一样:
生产环境(production):HTTP日志记录到文件
开发环境(development):打印到标准输出
这需要做两件事:
1. 通过docker命令设置为production
1.app.js在production情况下记录日志到文件中
docker run命令中加入production变量设置:
sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production node npm start
即,-e NODE_ENV=production。
设置保存日志到文件。找到app.js的这行:
app.use(logger('dev'));
改为:
if (app.get('env') === 'development') { app.use(logger('dev')); } if (app.get('env') === 'production') { var fs = require('fs') var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'}) app.use(logger('combined', {stream: accessLogStream})) }
这样,当development模式打印到标准输出,production模式下输出到项目根目录下的access.log文件中。源代码见这里:https://github.com/MarshalW/ProtoWebApp/tree/m2
应用日志
这个日志是必须要有的,可帮助开发者发现和诊断问题。使用的是winston。需要将winston加入到package.json中:"winston":""
然后引入库:
var winston = require('winston');
再设置文件路径(我这里是app.log):
if (app.get('env') === 'production') { var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'}); app.use(logger('combined', {stream: accessLogStream})); winston.add(winston.transports.File, { filename: 'app.log' }); }
Docker不需要设置什么,就可以在项目的根目录下看到app.log文件了,如果运行没有问题的话。
连接Redis
和Node.js镜像类似,可以通过如下命令将Redis跑起来:$ sudo docker run -d --name redis -p 6379:6379 redis
当Docker本地没有redis镜像的时候,会自动先下载该镜像的最新版本。redis镜像内容见:Dockerfile/redis。然后,我们可以启动Web App:
$ sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp --link redis:redis -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production node npm start > ProtoWebApp@0.0.0 start /webapp > node ./bin/www info: Hello again distributed logs Reply: OK Reply: 0 Reply: 0 2 replies: 0: hashtest 1 1: hashtest 2
比上面启动Node.js的方式,多了:--link redis:redis,冒号前的redis表示镜像名称,后面的redis表示这里使用的别名。
另外,创建Client的代码有点不同:
var redis = require("redis"), client = redis.createClient(6379, "redis");
其中redis是redis容器的别名。或者讲究点也可以这样:
var redisHost = process.env.REDIS_PORT_6379_TCP_ADDR; var redis = require("redis"), client = redis.createClient(6379, redisHost);
源代码见这里:https://github.com/MarshalW/ProtoWebApp/tree/m4
连接MongoDB
执行命令,启动mongoDB:sudo docker run -d -p 27017:27017 -v "$(pwd)"/db:/data/db --name mongodb dockerfile/mongodb
数据库文件保存在当前目录下的db目录下,如果不存在目录的话会自动创建。
在package.json中加入:
"mongoose":""
在app.js代码中加入:
//测试mongoDB var mongoose = require('mongoose'); mongoose.connect('mongodb://mongodb/test'); var Cat = mongoose.model('Cat', { name: String }); var kitty = new Cat({ name: 'Zildjian' }); kitty.save(function (err) { if (err) console.log(err); console.log('meow'); });
执行Docker命令:
$ sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp --link redis:redis --link mongodb:mongodb -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production node npm start > ProtoWebApp@0.0.0 start /webapp > node ./bin/www info: Hello again distributed logs js-bson: Failed to load c++ bson extension, using pure JS version Reply: OK Reply: 0 Reply: 0 2 replies: 0: hashtest 1 1: hashtest 2 meow
源代码见这里:https://github.com/MarshalW/ProtoWebApp/tree/m5
本文收发于我的个人博客:http://blog.shiqichan.com/Depl ... cker/