Build Node.Js web server in Docker containers: nodejs+pm2+mongodb+redis
来源:互联网 发布:淘宝里怎么打开链接 编辑:程序博客网 时间:2024/06/05 17:18
Build Node.Js web server in Docker containers: nodejs+pm2+mongodb+redis
环境
- 系统环境 centos 6.5 64bit
- docker v1.7.1
下载使用镜像
[root@dss ~]# docker search node # 搜素镜像NAME DESCRIPTION STARS OFFICIAL AUTOMATEDnode Node.js is a JavaScript-based platform for... 2462 [OK] strongloop/node StrongLoop, Node.js, and tools. 30 [OK]bitnami/node Bitnami Node.js Docker Image 16 [OK]siomiz/node-opencv _/node + node-opencv 8 [OK]dahlb/alpine-node small node for gitlab ci runner 7 [OK]tatsushid/tinycore-node A small but a fully functional Node.js run... 6 [OK]cusspvz/node Super small Node.js container (~20MB)... 4 [OK]azukiapp/node Docker image to run Node.js by Azuki - htt... 4 [OK]tutum/node Run a Tutum node inside a container 3 [OK]anigeo/node-forever Daily build node.js with forever 3 [OK]redjack/node Node + Nave 1 [OK]xataz/node very light node image 1 [OK]joxit/node Slim node docker with some utils for dev 1 [OK]centralping/node Bare bones CentOS 7 NodeJS container. 1 [OK]urbanmassage/node Some handy (read, better) docker node images 1 [OK]tectoro/node-compass Node JS minimal version with compass and b... 1 [OK]starefossen/ruby-node Docker Image with Ruby and Node.js installed 1 [OK]atomiq/node Use as base image for new Node.js images. 1 [OK]robbertkl/node Docker container running Node.js 0 [OK]lynxtp/node https://github.com/lynxtp/docker-node 0 [OK]codexsystems/node Node.js for Development and Production 0 [OK]instructure/node Instructure node images 0 [OK]stylisted/node Stylisted's base Docker node image with gr... 0 [OK]maxird/node Node 0 [OK]c4tech/node NodeJS images, aimed at generated single-p... 0 [OK]
安装 Node.Js 官方镜像 [root@dss ~]# yum pull node
查看已安装镜像
[root@dss workspace]# docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEnode latest 47abb70784d6 8 days ago 660.6 MB
基于 Node.Js 镜像创建服务镜像
新建目录 src
, 在src目录下新建 package.json
:
{ "name": "docker-node-test", "private": true, "version": "0.0.1", "description": "Node.js test app on node docker image", "author": "Shaoshuai Dong <dongsoso@hotmail.com>", "dependencies": { "express": "^4.13.4" }}
在src目录下新建index.js
:
const express = require('express'); // Creates an Express application let app = express(); // Assigns settings app.set('port', 8080); // Routes HTTP GET requests app.get('/', (req, res) => { res.send('Hello world --- from nodejs docker image\n'); }); // Binds and listens app.listen(app.get('port'), () => { console.info("Listen port %s for app", app.get("port"));
在src同级目录下新建文件 Dockerfile
:
From node:6.3ADD src/ /srcRUN cd /src; npm installEXPOSE 8080CMD ["node", "/src/index.js"]
创建镜像 docker build --tag="node/app-test" ./
, docker build --help
查看详细参数
创建成功会提示 Successfully built 2199b2aa9cd1
查看创建的镜像
[root@dss test]# docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEnode/app-test latest 2199b2aa9cd1 7 seconds ago 663.6 MBnode 6.3 47abb70784d6 8 days ago 660.6 MBnode latest 47abb70784d6 8 days ago 660.6 MB
运行刚刚创建的镜像, docker run --help
查看详细参数:
[root@dss test]# docker run -d -p 8081:8080 node/app-test 873ac198368799f9bf56262e097f55e66241f47cedd336349236b43e9001539d
查看运行中的容器,是否存在:
[root@dss test]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES873ac1983687 node/app-test "node /src/index.js" 53 seconds ago Up 52 seconds 0.0.0.0:8081->8080/tcp desperate_swartz
测试 Node.Js 服务:
[root@dss ~]# curl localhost:8081 -iHTTP/1.1 200 OKX-Powered-By: ExpressContent-Type: text/html; charset=utf-8Content-Length: 41ETag: W/"29-onjHxbOtTbPH0+vlfe2YtA"Date: Fri, 15 Jul 2016 10:49:54 GMTConnection: keep-aliveHello world --- from nodejs docker image
基于 Node.Js 镜像使用 pm2
使用 pm2
创建 Dockerfile
:
From node:6.3RUN npm install pm2 -g --registry=https://registry.npm.taobao.orgRUN mkdir -p /usr/src/node-appWORKDIR /usr/src/node-appCOPY src/. /usr/src/node-app/RUN npm install --registry=https://registry.npm.taobao.orgEXPOSE 8080CMD ["npm", "start"]
先看看目录结构吧
[root@dss workspace]# tree test/test/├── Dockerfile└── src ├── index.js ├── package.json └── pm2.json
package.json
:
{ "name": "docker-node-test", "private": true, "version": "0.0.1", "description": "Node.js test app on node docker image", "author": "Shaoshuai Dong <dongsoso@hotmail.com>", "scripts": { "start": "pm2 startOrGracefulReload ./pm2.json --no-daemon" }, "dependencies": { "express": "^4.13.4" } }
index.js
const express = require('express');// Creates an Express applicationlet app = express();// Assigns settingsapp.set('port', 8080);// Routes HTTP GET requests app.get('/', (req, res) => { res.send('Hello world --- from nodejs docker image\n');});// Binds and listensapp.listen(app.get('port'), () => { console.info("Listen port %s for app", app.get("port"));});
pm2.json
:
{ "apps": [ { "name": "app-test", "script" : "index.js", "args" : [], "watch" : false, "merge_logs" : true, "max_memory_restart": "100M", "env": { "NODE_ENV": "local" } } ]}
创建镜像 docker build --tag="node/app-test" ./
查看创建的镜像 docker images
:
[root@dss test]# docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEnode/app-test latest 1a3325c28c8c 14 minutes ago 678 MBnode latest 47abb70784d6 10 days ago 660.6 MBnode 6.3 47abb70784d6 10 days ago 660.6 MB
运行 docker run -d -p 8081:8080 node/app-test
查看运行中的容器
[root@dss test]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9e49f7c5270a node/app-test "pm2 start index.js 7 minutes ago Up 7 minutes 0.0.0.0:8081->8080/tcp gloomy_sammet
测试
[root@dss test]# curl -i localhost:8081HTTP/1.1 200 OKX-Powered-By: ExpressContent-Type: text/html; charset=utf-8Content-Length: 41ETag: W/"29-onjHxbOtTbPH0+vlfe2YtA"Date: Sun, 17 Jul 2016 09:12:48 GMTConnection: keep-aliveHello world --- from nodejs docker image
在容器内运行命令(docker exec --help
查看更多用法)
[root@dss test]# docker exec -i 9e49f7c5270a pm2 list┌──────────┬────┬──────┬─────┬────────┬─────────┬────────┬─────────────┬──────────┐│ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │├──────────┼────┼──────┼─────┼────────┼─────────┼────────┼─────────────┼──────────┤│ app-test │ 0 │ fork │ 17 │ online │ 0 │ 9m │ 23.730 MB │ disabled │└──────────┴────┴──────┴─────┴────────┴─────────┴────────┴─────────────┴──────────┘
将 pm2、log4js 日志记录到宿主机文件
这里介绍将 pm2 日志和程序日志记录在宿主机文件之中, 目标是:
- pm2 out logs =>
/data/logs/app-test-out.log
- pm2 error logs =>
/data/logs/app-test-err.log
- logs by log4js =>
/data/logs/hello.log
./├── Dockerfile└── src ├── index.js ├── package.json └── pm2.json
Dockerfile
From node:6.3RUN npm install pm2 -g --registry=https://registry.npm.taobao.orgRUN mkdir -p /usr/src/node-appWORKDIR /usr/src/node-appCOPY src/. /usr/src/node-app/RUN npm install --registry=https://registry.npm.taobao.orgEXPOSE 8080CMD ["npm", "start"]
index.js
const express = require('express');const log4js = require('log4js');// Creates an Express applicationlet app = express();// Init loggerlog4js.configure({ appenders: [{ type: 'dateFile', pattern: '-yyyyMMddhh.log', filename: '/data/logs/hello.log', category: 'hello' }], levels: { action: 'INFO', alarm_api: 'INFO' }});global.logger = log4js.getLogger('hello');// Assigns settingsapp.set('port', 8080);// Routes HTTP GET requests app.get('/', (req, res) => { console.info('On request: hello world'); global.logger.info(req.method, req.path); res.send('Hello world --- from nodejs docker image\n');});// Binds and listensapp.listen(app.get('port'), () => { console.info('Listen port %s for app', app.get('port'));});
package.json
{ "name": "docker-node-test", "private": true, "version": "0.0.1", "description": "Node.js test app on node docker image", "author": "Shaoshuai Dong <dongsoso@hotmail.com>", "scripts": { "start": "pm2 startOrGracefulReload ./pm2.json --no-daemon" }, "dependencies": { "express": "^4.13.4", "log4js": "^0.6.33" }}
pm2.json
{ "apps": [ { "name": "app-test", "script": "index.js", "args": [], "watch": false, "merge_logs": true, "error_file": "/data/logs/app-test-err.log", "out_file": "/data/logs/app-test-out.log", "log_date_format": "YYYY-MM-DD HH:mm:ss.SSS", "max_memory_restart": "100M", "env": { "NODE_ENV": "local" } } ]}
创建镜像 docker build --tag="node/app-test:0.1" ./
运行容器 docker run -p 8081:8080 -v /data/logs:/data/logs --name hello node/app-test:0.1
使用 -v
参数在容器上挂载宿主机上的指定目录
查看容器和测试:
[root@dss test]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES63eb2a3aea8b node/app-test:0.1 "npm start" 17 seconds ago Up 16 seconds 0.0.0.0:8081->8080/tcp hello[root@dss test]# curl localhost:8081Hello world --- from nodejs docker image[root@dss test]#
在宿主机上查看日志
[root@dss test]# ls /data/logs/app-test-err.log app-test-out.log hello.log[root@dss test]# cat /data/logs/app-test-out.log 2016-07-18 05:56:56.406: Listen port 8080 for app2016-07-18 05:57:21.289: On request: hello world[root@dss test]# cat /data/logs/hello.log [2016-07-18 05:57:21.290] [INFO] hello - GET /
命令 docker inspect hello
可以看到容器的 volume 配置
... "Volumes": { "/data/logs": "/data/logs" }, "VolumesRW": { "/data/logs": true },...
Data volumes
有如下特性:
- 当容器被创建时 volumes 会被初始化
- Data volumes 可以在容器间共享复用
- 对 data volume 的修改直接生效
- 更新镜像时, 原来 data volume 的修改不会被影响
- 即使容器被删除, 对应的 data volumes 依然会存在
使用 Volume
可以将容器与容器产生的数据分离,容器产生的数据可以持久化。
Docker volumes 使用 -v
指定和 在 Dockerfile 指定 VOLUME
的区别:
-v /data/logs:/data/logs
: 将宿主机的/data/logs
目录挂载到容器的/data/logs
目录(如果目录不存在会被创建), 宿主机和容器共享该目录,二者对该目录下的修改相互受影响。- Dockerfile 指定
VOLUME /data/logs
: 在宿主机创建一个目录(默认是/var/lib/docker/volumes/
)并挂载到容器的/data/logs
目录(如果目录不存在会被创建), 宿主机和容> 器共享该目录,二者对该目录下的修改相互受影响。 -v /data/logs
: 同上, 在宿主机创建一个目录(默认是/var/lib/docker/volumes/
)并挂载到容器的/data/logs
目录(如果目录不存在会被创建), 宿主机和容器共享该目录,二者对该目录下的修改相互受影响。
Docker volumes 默认是 read-write 模式,也可以设置为 read-only 模式。
应用实践 nodejs&mongodb&redis
目标
- container: nodejs
- pm2
- logs on host
- container: mongodb
- container: redis
基于 centos 镜像创建 mongodb 镜像
新建 Dockerfile
# Mongodb image from centos:6# run -v /data/db:/data/db -p 27017:27017From centos:6MAINTAINER Shaoshuai Dong <dongsoso@hotmail.com># install mongodb v3.2COPY mongodb.repo /etc/yum.repos.d/RUN yum install -y mongodb-org# volumeVOLUME ["/data/db"]# expose portEXPOSE 27017# commandCMD ["mongod"]
mongodb.repo
[MongoDB]name=MongoDB Repositorybaseurl=http://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/gpgcheck=0enabled=1
创建镜像 docker build --tag="centos/mongo:0.1" ./
运行容器 docker run -d -p 27017:27017 -v /data/db:/data/db --name mongo centos/mongo:0.1
查看容器运行状态
[root@dss mongodb]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESc9ddd14992e6 centos/mongo:0.1 "mongod" 5 seconds ago Up 4 seconds 0.0.0.0:27017->27017/tcp mongo
测试远程连接 mongodb
[dongshaoshuai~/test] ]$mongo --port 27017 --host x.x.x.x^[[AMongoDB shell version: 3.0.6connecting to: x.x.x.x:27017/test
也可以通过 Robomongo
远程连接(如果mongodb 版本 >= v3,Robomongo 的版本需 > v0.8.5)
基于 centos 镜像创建 redis 镜像
Dockerfile
From centos:6MAINTAINER Shaoshuai Dong <dongsoso@hotmail.com># install redis v3.2.1RUN yum install -y gcc-c++RUN yum install -y tclRUN yum install -y wgetRUN wget http://download.redis.io/releases/redis-3.2.1.tar.gzRUN tar -xzvf redis-3.2.1.tar.gzRUN mv redis-3.2.1 /usr/local/redisRUN cd /usr/local/redis; make; make installCOPY redis.conf /etc/redis.confEXPOSE 6379CMD ["/usr/local/bin/redis-server", "/etc/redis.conf"]
redis.conf: wget http://download.redis.io/redis-stable/redis.conf
将 protected-mode yes
改为 protected-mode no
创建镜像 docker build --tag="centos/redis:0.1" ./
运行容器 docker run -d -p 6379:6379 --name redis centos/redis:0.1
查看运行容器
[root@dss redis]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES5aa2aa34764e centos/redis:0.1 "/usr/local/bin/redi 6 seconds ago Up 3 seconds 0.0.0.0:6379->6379/tcp redisc9ddd14992e6 centos/mongo:0.1 "mongod" 2 hours ago Up 2 hours 0.0.0.0:27017->27017/tcp mongo
基于 centos 镜像创建 nodejs 服务镜像
目录结构
├── app│ ├── index.js│ ├── package.json│ └── pm2.json└── Dockerfile
Dockerfile
# run -v /data/logs:/data/logs --link mongo:mongo From centos:6MAINTAINER Shaoshuai Dong <dongsoso@hotmail.com># install nodejs v6RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -RUN yum -y install nodejs# install pm2RUN npm install pm2 -g --registry=https://registry.npm.taobao.org# make dirRUN mkdir -p /usr/src/myapp# envsENV NODE_ENV=local# copy filesCOPY app/. /usr/src/myapp/# work dirWORKDIR /usr/src/myapp# install dependenciesRUN npm install --registry=https://registry.npm.taobao.org# expose portEXPOSE 8080# cmdCMD ["npm", "start"]
package.json
{ "name": "docker-node-test", "private": true, "version": "0.0.1", "description": "Node.js test app on node docker image", "author": "Shaoshuai Dong <dongsoso@hotmail.com>", "scripts": { "start": "pm2 startOrGracefulReload ./pm2.json --no-daemon" }, "dependencies": { "express": "^4.13.4", "log4js": "^0.6.33", "mongoose": "^4.0.8", "express-session": "^1.13.0", "connect-redis": "^3.1.0" }}
pm2.json
{ "apps": [ { "name": "app", "script": "index.js", "args": [], "watch": false, "merge_logs": true, "error_file": "/data/logs/app-err.log", "out_file": "/data/logs/app-out.log", "log_date_format": "YYYY-MM-DD HH:mm:ss.SSS", "max_memory_restart": "100M", "env": { "NODE_ENV": "local" } } ]}
一个简单的服务,仅作为演示 index.js
const express = require('express'); const log4js = require('log4js');const mongoose = require('mongoose');const cookieParser = require("cookie-parser");const session = require('express-session');const redisStore = require('connect-redis')(session);// Creates an Express applicationlet app = express();// NODE_ENVconst ENV = app.get('env');// Init loggerlog4js.configure({ appenders: [{ type: 'dateFile', pattern: '-yyyyMMddhh.log', filename: '/data/logs/app.log', category: 'app' }], levels: { app: 'INFO', }});global.logger = log4js.getLogger('app');// Assigns settingsapp.set('port', 8080);// Middlewaresapp.use(cookieParser('tadf@a213!sdfa6'));app.use(session({ secret: 'tadf@a213!sdfa6', resave: false, saveUninitialized: false, store: new redisStore({ logErrors: true, // host: 'redis', // Can be solved by docker-compose host: process.env.REDIS_PORT_6379_TCP_ADDR, port: 6379, ttl: 60 // expiration in 60s })}));// MongoDB connection: 'mongo' resolves to a docker containerlet mongoUrl = 'mongodb://mongo:27017/test';mongoose.connect(mongoUrl);let Monkey = mongoose.model('Monkey', { name: String });/** * Routes HTTP GET requests */app.all('*', (req, res, next) => { global.logger.info(req.method, req.path); if (req.path === '/login') { next(); return; } if (!req.session || !req.session.isLoggedIn) { // TODO: redirect to login page res.send('Please login first'); return; } next();});app.get('/login', (req, res) => { let userName = 'admin'; let password = 'admin'; if (req.query.name === userName && req.query.password === password) { req.session.isLoggedIn = true; res.json({ RetCode: 0 }); return; } res.json({ RetCode: 5, ErrMsg: 'Username or password wrong' });});app.get('/hello', (req, res) => { console.info('On request: hello world'); res.send('Hello world --- from nodejs docker image\n'); });app.get('/mongo/insert', (req, res) => { let newData = { name: req.query.name }; let newMonkey = new Monkey(newData); newMonkey.save(err => { if (err) { global.logger.error('Insert data failed: ', err, newData); res.json({ RetCode: 100, ErrMsg: 'Insert data failed' }); return; } global.logger.info('Insert data success:', newData); res.json({ RetCode: 0 }); });});app.get('/mongo/find', (req, res) => { Monkey.find((err, monkeys) => { if (err) { global.logger.error('Find data failed: ', err); res.json({ RetCode: 200, ErrMsg: 'Find data failed' }); return; } global.logger.info('Find data success:', monkeys); res.json({ RetCode: 0, Data: monkeys }); });});// Binds and listensapp.listen(app.get('port'), () => { console.info('Listen port %s for app', app.get('port')); console.info('NODE_ENV', ENV);});
创建镜像 docker build --tag="centos/node-app:0.1" ./
运行容器 docker run -d -p 8181:8080 -v /data/logs:/data/logs --link mongo:mongo --link redis:redis --name app centos/node-app:0.1
查看服务状态
[root@dss app]# docker exec app pm2 list┌──────────┬────┬──────┬─────┬────────┬─────────┬────────┬─────────────┬──────────┐│ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │├──────────┼────┼──────┼─────┼────────┼─────────┼────────┼─────────────┼──────────┤│ app │ 0 │ fork │ 25 │ online │ 0 │ 21s │ 47.879 MB │ disabled │└──────────┴────┴──────┴─────┴────────┴─────────┴────────┴─────────────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
测试
未登录, 在浏览器访问 yourhost:8181/hello
Please log in first
登录, yourhost:8181/login?name=admin&password=admin
{ "RetCode": 0}
登陆后,测试 yourhost:8181/hello
Hello world --- from nodejs docker image
测试 yourhost:8181/mongo/find
{"RetCode":0,"Data":[{"_id":"578ded8321be5919003c0dd8","name":"mmmm","__v":0},{"_id":"578df6f638a6a11b008a9bd0","name":"mmmmmm","__v":0}]}
查看日志
[root@dss logs]# cat /data/logs/app.log [2016-07-20 02:31:40.097] [INFO] app - GET /hello[2016-07-20 02:31:45.088] [INFO] app - GET /login[2016-07-20 02:32:12.375] [INFO] app - GET /hello[2016-07-20 02:32:16.476] [INFO] app - GET /mongo/find[2016-07-20 02:32:16.477] [INFO] app - Find data success: [ { _id: 578ded8321be5919003c0dd8, name: 'mmmm', __v: 0 }, { _id: 578df6f638a6a11b008a9bd0, name: 'mmmmmm', __v: 0 } ]
其他
修改镜像、容器存储目录
docker 镜像、容器默认存储在 /var/lib/docker
,对于云主机通常系统盘不大,最好存储在数据盘。
通过 --graph
参数可以指定存储位置。修改 /etc/sysconfig/docker
文件:
# 数据盘other_args="--graph=/data/docker"
停止 docker 服务 service docker stop
copy 数据至数据盘 cp -r /var/lib/docker /data/docker/
移除、备份原数据 mv /var/lib/docker /data/bak/docker.bak
重新启动 docker 服务 service docker start
测试镜像和容器是否正常 docker images
, docker ps -a
原文
- Build Node.Js web server in Docker containers: nodejs+pm2+mongodb+redis
- node.js+mongodb+redis+elasticsearch+koa2+pm2+atom 系统centos6.5
- docker部署node及mongodb,node.js读取mongodb数据以Web显示给用户
- TODO:Node.js pm2使用方法
- node.js&pm2搭建node生产环境
- node.js&pm2搭建node生产环境
- docker部署nodejs,mongodb
- 【基于node.js+express的web开发备忘】--nodejs v0.11.13 + express v4.2.0+mongodb v2.0.6
- Node.js+MongoDB+AngularJS Web开发
- Node.js MongoDB Angular Web开发 ( 1 )
- nodejs+pm2
- Node.js进程管理器PM2浅析
- node.js进程管理 PM2 && forever
- Node.js clustering made easy with PM2
- PM2 开机自启Node.js 项目
- pm2--node.js服务管理工具使用说明
- 【实战】基于Nginx、Node.js和Redis的Docker工作流
- “ Hello World” Node.js Web Server
- 团体程序设计天梯赛-练习集 L3-010. 是否完全二叉搜索树
- 变态跳台阶
- LeetCode 53. Maximum Subarray
- 安卓学习笔记---popwindow类似的三级联动
- iOS核心动画-UIView封装动画
- Build Node.Js web server in Docker containers: nodejs+pm2+mongodb+redis
- [LeetCode] 35. Search Insert Position
- 安卓学习笔记---好看的UI界面
- CSS3 @font-face 显示特殊字体
- [Funkunux] Linux_2.6.22.6 内核start_kernel函数分析之parse_args
- 04 OC 字典 Dictionary
- 我所理解的Cocos2d-x 全新的Cocos2d-x3.0
- python多线程(3)---生产者与消费者(线程通信)和Queue模块
- Hibernate JdbcTemplate的queryForInt的些许问题