2016年11月23日星期三

Docker_014:Nodejs + Express + Redis 多容器的应用

环境:MAC OS  X 10.12.1 + Docker 1.12.3

该应用包括两个镜像:
(1)一个 Node 容器,用来运行 Node 应用。
(2)一个 Redis 主容器,用来保存应用状态。
(3)两个 Redis 备份容器。
(4)一个日志容器,用来捕获应用日志。

1. Node.js 镜像
$ cd /Users/maping/mygit/dockerbook-code/code/6/node

$ cd nodejs

$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull
ENV REFRESHED_AT 2016-06-01

RUN apt-get -yqq update
RUN apt-get -yqq install nodejs npm
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN mkdir -p /var/log/nodeapp

ADD nodeapp /opt/nodeapp/

WORKDIR /opt/nodeapp
RUN npm install

VOLUME [ "/var/log/nodeapp" ]

EXPOSE 3000

ENTRYPOINT [ "nodejs", "server.js" ]

$ cat nodeapp/server.js
var fs = require('fs');
var express = require('express'),
    app = express(),
    redis = require('redis'),
    RedisStore = require('connect-redis')(express),
    server = require('http').createServer(app);

var logFile = fs.createWriteStream('/var/log/nodeapp/nodeapp.log', {flags: 'a'});

app.configure(function() {
  app.use(express.logger({stream: logFile}));
  app.use(express.cookieParser('keyboard-cat'));
  app.use(express.session({
        store: new RedisStore({
            host: process.env.REDIS_HOST || 'redis_primary',
            port: process.env.REDIS_PORT || 6379,
            db: process.env.REDIS_DB || 0
        }),
        cookie: {
            expires: false,
            maxAge: 30 * 24 * 60 * 60 * 1000
        }
    }));
});

app.get('/', function(req, res) {
  res.json({
    status: "ok"
  });
});

app.get('/hello/:name', function(req, res) {
  res.json({
    hello: req.params.name
  });
});

var port = process.env.HTTP_PORT || 3000;
server.listen(port);
console.log('Listening on port ' + port);

$ docker build -t jamtur01/nodejs .

$ docker images

2. Redis 基础镜像
$ cd /Users/maping/mygit/dockerbook-code/code/6/node

$ cd redis_base

$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull
ENV REFRESHED_AT 2016-06-01

RUN apt-get -yqq update
RUN apt-get install -yqq software-properties-common python-software-properties
RUN add-apt-repository ppa:chris-lea/redis-server
RUN apt-get -yqq update
RUN apt-get -yqq install redis-server redis-tools

VOLUME [ "/var/lib/redis", "/var/log/redis" ]

EXPOSE 6379

CMD []

说明:这个 Redis 基础镜像安装了最新版的 Redis,指定了两个卷(/var/lib/redis 和 /var/log/redis),公开了 Redis 默认端口 6379。
因为不会执行该镜像,所以没有包括 ENTRYPOINT 或 CMD 指令。

$ docker build -t jamtur01/redis .

3. Redis 主镜像
$ cd redis_primary

Redis 主镜像基于 jamtur01/redis 镜像,并通过 ENTRYPOINT 指令指定了启动命令。
$ cat Dockerfile
FROM jamtur01/redis
MAINTAINER James Turnbull
ENV REFRESHED_AT 2016-06-01

ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-server.log" ]

$ docker build -t jamtur01/redis_primary .

4. Redis 从镜像
$ cd redis_replica

Redis 从镜像基于 jamtur01/redis 镜像,并通过 ENTRYPOINT 指令指定了启动命令,将 redis_primary 主机的 Redis 作为主服务,连接其 6379 端口。
$ cat Dockerfile
FROM jamtur01/redis
MAINTAINER James Turnbull
ENV REFRESHED_AT 2016-06-01

ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-replica.log", "--slaveof redis-primary 6379" ]

$ docker build -t jamtur01/redis_replica .

5. 创建 Redis 集群

5.1 启动 Redis 主容器
$ docker run -d -h redis-primary --name redis-primary jamtur01/redis_primary
参数说明:
-h 设置容器的主机名(默认容器的主机名为容器 ID),使用该标志确保容器使用 redis-primary 作为主机名,并被本地的 DNS 服务正确解析。

$ docker logs redis-primary
看不到任何日志信息? 原来 Redis 不会将日志记录到标准输出,而是将日志记录到一个文件。

利用之前创建的 /var/log/redis 卷来看 Redis 日志。
$ docker run -it --rm --volumes-from redis-primary ubuntu cat /var/log/redis/redis-server.log
输出如下:
         _.-``__ ''-._                                            
      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 1
  `-._    `-._  `-./  _.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |           http://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |                                 
  `-._    `-._`-.__.-'_.-'    _.-'                                  
      `-._    `-.__.-'    _.-'                                      
          `-._        _.-'                                          
              `-.__.-'                                              

1:M 01 Jan 08:36:44.502 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 01 Jan 08:36:44.503 # Server started, Redis version 3.0.7
1:M 01 Jan 08:36:44.503 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 01 Jan 08:36:44.503 * The server is now ready to accept connections on port 6379

5.2 启动 Redis 从容器1
$ docker run -d -h redis-replica1 --name redis-replica1 --link redis-primary:redis-primary jamtur01/redis_replica
参数说明:
--link 将 redis-primary 容器以别名 redis-primary 连接到 Redis 从容器。

$ docker run -it --rm --volumes-from redis-replica1 ubuntu cat /var/log/redis/redis-replica.log
输出如下:
              _._                                                 
           _.-``__ ''-._                                            
      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 1
  `-._    `-._  `-./  _.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |           http://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |                                 
  `-._    `-._`-.__.-'_.-'    _.-'                                  
      `-._    `-.__.-'    _.-'                                      
          `-._        _.-'                                          
              `-.__.-'                                              

1:S 01 Jan 08:41:07.191 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:S 01 Jan 08:41:07.191 # Server started, Redis version 3.0.7
1:S 01 Jan 08:41:07.191 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:S 01 Jan 08:41:07.191 * The server is now ready to accept connections on port 6379
1:S 01 Jan 08:41:07.191 * Connecting to MASTER redis-primary:6379
1:S 01 Jan 08:41:07.198 * MASTER <-> SLAVE sync started
1:S 01 Jan 08:41:07.198 * Non blocking connect for SYNC fired the event.
1:S 01 Jan 08:41:07.199 * Master replied to PING, replication can continue...
1:S 01 Jan 08:41:07.199 * Partial resynchronization not possible (no cached master)
1:S 01 Jan 08:41:07.200 * Full resync from master: 8381dd721752fec0f9c2f833c3c9b15783db6dc9:1
1:S 01 Jan 08:41:07.232 * MASTER <-> SLAVE sync: receiving 18 bytes from master
1:S 01 Jan 08:41:07.232 * MASTER <-> SLAVE sync: Flushing old data
1:S 01 Jan 08:41:07.232 * MASTER <-> SLAVE sync: Loading DB in memory
1:S 01 Jan 08:41:07.232 * MASTER <-> SLAVE sync: Finished with success

5.3 再加入另一个 Redis 从容器2
$ docker run -d -h redis-replica2 --name redis-replica2 --link redis-primary:redis-primary jamtur01/redis_replica

$ docker run -it --rm --volumes-from redis-replica2 ubuntu cat /var/log/redis/redis-replica.log
输出如下:
                _._                                                 
           _.-``__ ''-._                                            
      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 1
  `-._    `-._  `-./  _.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |           http://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                  
 |`-._`-._    `-.__.-'    _.-'_.-'|                                 
 |    `-._`-._        _.-'_.-'    |                                 
  `-._    `-._`-.__.-'_.-'    _.-'                                  
      `-._    `-.__.-'    _.-'                                      
          `-._        _.-'                                          
              `-.__.-'                                              

1:S 01 Jan 08:42:59.453 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:S 01 Jan 08:42:59.453 # Server started, Redis version 3.0.7
1:S 01 Jan 08:42:59.453 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:S 01 Jan 08:42:59.453 * The server is now ready to accept connections on port 6379
1:S 01 Jan 08:43:00.455 * Connecting to MASTER redis-primary:6379
1:S 01 Jan 08:43:00.456 * MASTER <-> SLAVE sync started
1:S 01 Jan 08:43:00.456 * Non blocking connect for SYNC fired the event.
1:S 01 Jan 08:43:00.456 * Master replied to PING, replication can continue...
1:S 01 Jan 08:43:00.457 * Partial resynchronization not possible (no cached master)
1:S 01 Jan 08:43:00.458 * Full resync from master: 8381dd721752fec0f9c2f833c3c9b15783db6dc9:155
1:S 01 Jan 08:43:00.509 * MASTER <-> SLAVE sync: receiving 18 bytes from master
1:S 01 Jan 08:43:00.509 * MASTER <-> SLAVE sync: Flushing old data
1:S 01 Jan 08:43:00.509 * MASTER <-> SLAVE sync: Loading DB in memory
1:S 01 Jan 08:43:00.509 * MASTER <-> SLAVE sync: Finished with success

6. 创建 Node 容器
$ docker run -d --name nodeapp -p 3000:3000 --link redis-primary:redis-primary jamtur01/nodejs

$ docker logs nodeapp
访问 localhost:3000


7. 捕获应用日志
应用运行起来后,需要把应用日志保存到日志服务器,将使用 Logstash 捕获应用日志。
$ cd logstash

$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull
ENV REFRESHED_AT 2016-06-01

RUN apt-get -yqq update
RUN apt-get -yqq install wget
RUN wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch |  apt-key add -
RUN echo 'deb http://packages.elasticsearch.org/logstash/1.5/debian stable main' > /etc/apt/sources.list.d/logstash.list
RUN apt-get -yqq update
RUN apt-get -yqq install logstash default-jdk

ADD logstash.conf /etc/

WORKDIR /opt/logstash

ENTRYPOINT [ "bin/logstash" ]
CMD [ "--config=/etc/logstash.conf" ]

$ cat logstash.conf
input {
  file {
    type => "syslog"
    path => ["/var/log/nodeapp/nodeapp.log", "/var/log/redis/redis-server.log"]
  }
}
output {
  stdout {
    codec => rubydebug
  }
}
说明:Logstash 会监控两个文件(var/log/nodeapp/nodeapp.log 和 /var/log/redis/redis-server.log),将其中的内容发送到 Logstash。
出于演示目的,这里把所有的 Logstash 输入的内容会输出到标准输出上。
实际使用中,一般会将 Logstash 输入的内容输出到 Elasticsearch 集群。

$ docker build -t jamtur01/logstash .

$ docker run -d --name logstash --volumes-from redis-primary --volumes-from nodeapp jamtur01/logstash
这个容器使用了两次 --volumes-from,挂载了 redis_primary 和 nodeapp 里的卷,这样就可以访问 Redis 和 Node 的日志文件了。

$ docker logs -f logstash
多刷新几次 http://localhost:3000/,会看到日志不断增长。

       "message" => "- - - [Wed, 23 Nov 2016 09:59:12 GMT] \"GET / HTTP/1.1\" 500 506 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36\"",
      "@version" => "1",
    "@timestamp" => "2016-11-23T09:59:13.040Z",
          "host" => "54b744606562",
          "path" => "/var/log/nodeapp/nodeapp.log",
          "type" => "syslog"
}
{
       "message" => "::ffff:172.17.0.1 - - [Wed, 23 Nov 2016 09:59:12 GMT] \"GET / HTTP/1.1\" 500 506 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36\"",
      "@version" => "1",
    "@timestamp" => "2016-11-23T09:59:13.043Z",
          "host" => "54b744606562",
          "path" => "/var/log/nodeapp/nodeapp.log",
          "type" => "syslog"

}

没有评论: