2016年11月23日星期三

MAC_046:如何把 PowerPoint 中的背景图片设置为 Keynote 的主题背景

环境:MAC OS X 10.12.1

公司给了一套 PowerPoint 模板 PPT,我想使用 Keynote 做出同样效果,这时需要把 PowerPoint 中的背景图片设置为 Keynote 的主题背景。
以首页为例,步骤如下:

1. 直接复制 PPT 的首页,粘贴到 Keynote 的某页中
这时,虽然粘贴到了 Keynote 中,但是并不能按图片保存。

2. 把粘贴到 Keynote 中的“图片”再粘贴回 PPT 的某页中
这时,会发现已经可以按另存为图片了。

3. 在 Keynote 的首页中,更改母版,替换图片

MAC_045:Keynote 的“格式刷”

1. 选中要拷贝样式的文字

2. 格式 -> 拷贝样式 (option + command + C)

3. 选中要粘贴样式的文字

4. 格式 -> 拷贝样式(option + command + V)

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"

}

Docker_013:构建一个 Java Web 应用

环境:MAC OS  X 10.12.1 + Docker 1.12.3

该应用包括两个镜像:
(1)一个镜像从 URL 拉取指定的 war 文件,并将其保存到卷里。
(2)一个镜像安装了 Tomcat,运行这些 war 文件。

1. 构建 fetcher 镜像
$ cd /Users/maping/mygit/dockerbook-code/code/6/tomcat

$ cd fetcher

$ 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

VOLUME [ "/var/lib/tomcat7/webapps/" ]
WORKDIR /var/lib/tomcat7/webapps/

ENTRYPOINT [ "wget" ]
CMD [ "--help" ]

$ docker build -t jamtur01/fetcher .

$ docker images

2. 获取 war 文件
$ docker run -it --name sample jamtur01/fetcher https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war
因为设置了容器的工作目录,sample.war 最终会保存到 /var/lib/tomcat7/webapps/ 目录中。
由于长城墙原因,导致无法获取  https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war。
因此,提前下好 https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war,并拷贝到 fetcher 目录下。

修改 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

VOLUME [ "/var/lib/tomcat7/webapps/" ]
WORKDIR /var/lib/tomcat7/webapps/

COPY sample.war /var/lib/tomcat7/webapps/

ENTRYPOINT [ "wget" ]
CMD [ "--help" ]

运行命令改为 $ docker run -it --name sample jamtur01/fetcher 

查看某个卷的具体位置
$ docker inspect -f "{{ .Config.Volumes }}" sample
map[/var/lib/tomcat7/webapps/:{}]

$ ls -l /var/lib/docker/vfs/dir // 我是 MAC,没有此目录

3. 构建 tomcat7 镜像
$ /Users/maping/mygit/dockerbook-code/code/6/tomcat

$ cd tomcat7

$ 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 tomcat7 default-jdk

ENV CATALINA_HOME /usr/share/tomcat7
ENV CATALINA_BASE /var/lib/tomcat7
ENV CATALINA_PID /var/run/tomcat7.pid
ENV CATALINA_SH /usr/share/tomcat7/bin/catalina.sh
ENV CATALINA_TMPDIR /tmp/tomcat7-tomcat7-tmp

RUN mkdir -p $CATALINA_TMPDIR

VOLUME [ "/var/lib/tomcat7/webapps/" ]

EXPOSE 8080

ENTRYPOINT [ "/usr/share/tomcat7/bin/catalina.sh", "run" ]

$ docker build -t jamtur01/tomcat7 .
$ docker images

4. 运行 war 文件
$ docker run --name sample_app --volumes-from sample -d -P jamtur01/tomcat7
说明:-P (大写的P)表示发布容器所有暴露的端口给宿主机

$ docker port sample_app 8080
0.0.0.0:32769

访问 http://localhost:32769/sample


Docker_012:构建使用 Jekyll 框架的网站

环境: MAC OS X 10.12.1 + Docker 1.12.3

该应用包括两个镜像:
(1)一个镜像安装了 Jekyll 及其用于构建 Jekyll 网站的必要的软件包。
(2)一个镜像安装了 Apache 来让 Jekyll 网站运行起来。
启动容器时
(1)从 Jekyll 镜像创建一个容器,通过卷挂载网站的源代码。
(2)从 Apache 镜像创建了一个容器,利用包含网站的编译后的源代码的卷。 

1. 构建 Jekyll 镜像
$ cd /Users/maping/mygit/dockerbook-code/code/6/jekyll/
$ cd jekyll
$ 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 ruby ruby-dev build-essential nodejs
RUN gem install --no-rdoc --no-ri jekyll -v 2.5.3

VOLUME /data
VOLUME /var/www/html
WORKDIR /data

ENTRYPOINT [ "jekyll", "build", "--destination=/var/www/html" ]

说明:
(1)使用 VOLUME 建立卷:/data,用来存放网站的源代码。
(2)使用 VOLUME 建立卷:/var/www/html,用来存放编译后的代码。
(3)设置工作目录为 /data
(4)ENTRYPOINT 指令执行编译。

$ docker build -t jamtur01/jekyll .
$ docker images

2. 构建 Apache 镜像
$ cd /Users/maping/mygit/dockerbook-code/code/6/jekyll/
$ cd apache
$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull

RUN apt-get -yqq update
RUN apt-get -yqq install apache2

VOLUME [ "/var/www/html" ]
WORKDIR /var/www/html

ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2

RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR

EXPOSE 80

ENTRYPOINT [ "/usr/sbin/apache2" ]
CMD ["-D", "FOREGROUND"]

说明:
(1)使用 VOLUME 建立卷:/var/www/html,用来存放编译后的代码。
(2)设置工作目录为 /var/www/html,用来存放编译后的代码。
(3)使用 ENV 设置必要的环境变量
(4)公开 80 端口
(5)ENTRYPOINT 指令启动 Apache

$ docker build -t jamtur01/apache .
$ docker images

3. 启动 Jekyll
$ cd /Users/maping/mygit
$ git clone https://github.com/jamtur01/james_blog.git
$ docker run  -v /Users/maping/mygit/james_blog:/data/ --name james_blog jamtur01/jekyll

输出如下:
Configuration file: /data/_config.yml
            Source: /data
       Destination: /var/www/html
      Generating...
                    done.
 Auto-regeneration: disabled. Use --watch to enable.


说明:把宿主机的 james_blog 目录作为源代码 /data/卷挂载到容器里。容器拿到源代码后,编译,然后存放到 /var/www/html/ 目录,然后容器停止。
卷在 Docker 宿主机的 /var/lib/docker/volumes 目录中,我是 MAC,没有此目录,应该被“转义”了。
要查看某个卷的具体位置,可以使用如下命令:

$ docker inspect -f "{{ .Config.Volumes }}" james_blog
map[/data:{} /var/www/html:{}]

4. 启动 Apache
$ docker run -d -P --volumes-from james_blog jamtur01/apache
5d55d8dc426ff94bbdd610dcbcbe0a65733df224fd5a9856ed1224ba44829853
说明:--volumes-from 把指定容器里的所有卷都加入新创建的容器里。
这意味着,Apache 容器可以访问之前创建的 james_blog 容器里的 /var/www/html 卷中的编译后的代码。
前提是 james_blog 容器必须存在,可以不运行,但必须存在。

$ docker port 80
$ docker port 5d55d8dc426ff94bbdd610dcbcbe0a65733df224fd5a9856ed1224ba44829853 80
0.0.0.0:32768
访问 http://localhost:32768


现在 Jekyll 网站终于运行起来了。

5. 更新代码
$ cd /Users/maping/mygit/james_blog
$ vim _config.yml
修改 title 域为 James' Dynamic Docker-driven Blog。

那么如何才能更新博客网站呢?只需要再次启动 james_blog 网站。
$ docker start james_blog

看上去什么都没发生,查看一下容器日志
$ docker logs james_blog
输出如下:
Configuration file: /data/_config.yml
            Source: /data
       Destination: /var/www/html
      Generating...
                    done.
 Auto-regeneration: disabled. Use --watch to enable.
Configuration file: /data/_config.yml
            Source: /data
       Destination: /var/www/html
      Generating...
                    done.
 Auto-regeneration: disabled. Use --watch to enable.
可以看到进行了第 2 次编译,并且将更新写入了对应的卷。

不用重启 Apache 容器,直接刷新访问 http://localhost:32768

6. 备份 Jekyll 应用卷
$ docker run --rm --volumes-from james_blog \
-v $(pwd):/backup ubuntu \
tar cvf /backup/james_blog_backup.tar /var/www/html
说明:--rm 参数表示只用一次的容器,用完即扔的容器。
该方法很简单,只要把卷挂载到新容器,完成备份,然后废弃这个用于备份的容器就可以了。

7. 进一步扩展 Jekyll 应用
(1)运行多个 Apache 容器,这些容器都使用来自 james_blog 容器的卷,组成一个 Web 集群。
(2)构建一个新镜像,该镜像把用户提供的源代码复制(比如 git clone)到卷里,再把这个卷挂载到 JekyII 镜像创建的容器,这样宿主机上就不需要包含任何源代码。
(3)构建一个 Web 前端,该服务从指定的源自动构建和部署网站。

2016年11月22日星期二

OpenShift_009:使用 oc cluster up 一键安装 OpenShift Origin

环境: MAC OS X 10.12.1 +

1. 安装 Docker for MAC

2. 启动 Docker 后,增加一个 Insecure registries: 172.30.0.0/16


重启 Docker。

3. 安装 socat
$ brew install socat

4. 下载 oc 命令
下载地址:https://github.com/openshift/origin/releases
选择 oc for MAC:openshift-origin-client-tools-v1.4.0-rc1.b4e0954-mac.zip

5. 安装  OpenShift Origin
$ cd ~/Software/openshift/origin
$ unzip  openshift-origin-client-tools-v1.4.0-rc1.b4e0954-mac.zip
$ cp oc /usr/local/bin/
$ oc cluster up // 启动 cluster
输出如下:
-- Checking OpenShift client ... OK
-- Checking Docker client ... OK
-- Checking Docker version ... OK
-- Checking for existing OpenShift container ... OK
-- Checking for openshift/origin:v1.3.2 image ... OK
-- Checking Docker daemon configuration ... OK
-- Checking for available ports ... OK
-- Checking type of volume mount ...
   Using Docker shared volumes for OpenShift volumes
-- Creating host directories ... OK
-- Finding server IP ...
   Using 10.210.8.180 as the server IP
-- Starting OpenShift container ...
   Creating initial OpenShift configuration
   Starting OpenShift using container 'origin'
   Waiting for API server to start listening
   OpenShift server started
-- Installing registry ... OK
-- Installing router ... OK
-- Importing image streams ... OK
-- Importing templates ... OK
-- Login to server ... OK
-- Creating initial project "myproject" ... OK
-- Server Information ...
   OpenShift server started.
   The server is accessible via web console at:
       https://10.210.8.180:8443

   You are logged in as:
       User:     developer
       Password: developer

   To login as administrator:
       oc login -u system:admin

$ oc login -u system:admin https://127.0.0.1:8443 // 以超级管理员账户登录
$ oc login -u devloper -p developer https://127.0.0.1:8443 // 以开发人员账户登录
给 developer 用户授超级管理员权限
$ oc login -u system:admin
$ oc adm policy add-cluster-role-to-user admin developer
访问 https://127.0.0.1:8443/console/ 账户:developer/developer



$ oc cluster down // 停止 cluster

6. 手工下载需要的其它镜像

VERSION=v1.4.1
docker pull openshift/origin-docker-registry:v1.4.1

7. 删除旧的镜像

VERSION=v1.4.0
docker rmi openshift/origin:$VERSION
docker rmi openshift/origin-deployer:$VERSION
docker rmi openshift/origin-docker-builder:$VERSION
docker rmi openshift/origin-docker-registry:$VERSION
docker rmi openshift/origin-haproxy-router:$VERSION
docker rmi openshift/origin-pod:$VERSION

参考文献:
1. https://github.com/openshift/origin/blob/master/docs/cluster_up_down.md

Hawkular_003:运行 APM 监控 Java Web 应用

环境:MAC OS X 10.12.1 + Hawkular APM 0.12.0.Final + JBoss EAP 7.0.3

以 JBoss EAP 7 的官方 helloworld 为例说明,如何使用 Hawkular APM 监控 Java Web 应用。

1. 设置 JDK 8 环境
Hawkular APM  和 JBoss EAP 7 都需要在 JDK 8 才能运行。
$ . ~/setJdk8Env.sh

2. 配置 helloworld 应用的 pom.xml 文件
(1)增加 properties
<version.org.hawkular.apm>0.12.0.Final</version.org.hawkular.apm>
(2)增加 dependencies
<!-- Import the Hawkular API -->
<dependency>
    <groupId>org.hawkular.apm</groupId>
    <artifactId>hawkular-apm-client-opentracing</artifactId>
    <version>${version.org.hawkular.apm}</version>
</dependency>
<dependency>
    <groupId>org.hawkular.apm</groupId>
    <artifactId>hawkular-apm-trace-publisher-rest-client</artifactId>
    <version>${version.org.hawkular.apm}</version>
</dependency>
(3)修改 WildFly plugin,指向 JBoss EAP 7 实例
<!-- WildFly plug-in to deploy the WAR -->
<plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-maven-plugin</artifactId>
    <version>${version.wildfly.maven.plugin}</version>
    <configuration>
        <hostname>127.0.0.1</hostname>
        <port>19990</port>
        <username>admin</username>
        <password>welcome@1</password>
    </configuration>
</plugin>

3. 启动 Hawkular APM
Hawkular APM 已经安装上一篇文章安装完毕。
$ . ~/setJdk8Env.sh
$ cd /Users/maping/Tools/hawkular/apm/dist/bin
$ ./standalone.sh -Djboss.http.port=9411访问 http://localhost:9411/ 登录账户 jdoe/password

4. 启动 JBoss EAP 7
$ . ~/setJdk8Env.sh
启动 JBoss EAP 7 前,要设置 Hawkular APM 环境变量,此步很重要!!!
$ . ~/Tools/hawkular/apm/dist/apm/setenv.sh 9411
$ cd /Users/maping/Redhat/eap/demo/7.0/2017-02-04
$ ./jboss-eap-7.0/bin/standalone.sh -Djboss.server.base.dir=./myeap -c standalone-full.xml -Djboss.socket.binding.port-offset=10000

5. 发布 helloworld 应用
$ . ~/setJdk8Env.sh
$ cd /Users/maping/Software/eap/7.0/jboss-eap-7.0.0.GA-quickstarts/helloworld
$ mvn clean install wildfly:deploy
访问 http://localhost:18080/jboss-helloworld
$ mvn wildfly:undeploy // 卸载

6. 发布 helloworld-mdb 应用
$ . ~/setJdk8Env.sh
$ cd /Users/maping/Software/eap/7.0/jboss-eap-7.0.0.GA-quickstarts/helloworld-mdb
$ mvn clean install wildfly:deploy
访问 http://localhost:18080/jboss-helloworld-mdb/ 向 Queue 中发送消息
访问 http://localhost:18080/jboss-helloworld-mdb/HelloWorldMDBServletClient?topic 向 Topic 中发送消息
$ mvn wildfly:undeploy // 卸载

7. 查看 Hawkular APM 控制台




参考文献:
1. https://hawkular.gitbooks.io/hawkular-apm-user-guide/content/quickstart/

Hawkular_002:安装并运行 APM

环境:MAC OS X 10.12.1 + Hawkular APM 0.12.0.Final

1. 下载
从 https://github.com/hawkular/hawkular-apm/releases 下载如下如文件:
(1)hawkular-apm-agent-opentracing.jar
(2)hawkular-apm-agent.jar
(3)hawkular-apm-dist-0.12.0.Final.zip

2.  设置 JDK 8 环境
Hawkular APM 需要在 JDK 8 才能运行。
$ . ~/setJdk8Env.sh

3. 安装
$ unzip hawkular-apm-dist-0.12.0.Final.zip -d dist
$ cd dist/bin
$ ./add-user.sh -a -u jdoe -p password -g read-write,read-only

4. 运行
$ ./standalone.sh -Djboss.http.port=9411
访问 http://localhost:9411/ 登录账户 jdoe/password



参考文献:
1. https://hawkular.gitbooks.io/hawkular-apm-user-guide/content/quickstart/

Hawkular_001:从源代码编译并安装 APM

环境:MAC OS X 10.12.1 + Hawkular APM 0.12.0.Final

Distributed Tracing and Application Performance Management
 

1. 克隆源代码
$ cd ~/mygit
$ git clone https://github.com/hawkular/hawkular-apm

2.  设置 JDK 8 环境
Hawkular APM 需要在 JDK 8 才能运行。
$ . ~/setJdk8Env.sh

3. 编译
$ cd hawkular-apm
$ mvn clean install -Pdev -DskipTests // 跳过测试
$ mvn clean install -Pitest // 运行集成测试
$ dist/target/hawkular-apm-${version}/bin/standalone.sh -Djboss.http.port=9411
说明:
(1)-Pdev - 创建登录账户: jdoe/password
(2)-Pitest - 运行集成测试
(3)-Djboss.http.port=9411 - 绑定 APM 端口为 9411, 默认 APM 端口为 8080

4. 运行
访问 http://localhost:9411 登录账户: jdoe/password



2016年11月20日星期日

Docker_011:在节点之间导出容器/镜像和导入镜像

1. 找到要导出的容器 ID
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
f4b0d7285fec        ubuntu:14.04        /bin/bash           38 minutes ago      Exit 0                                  hungry_thompson
8ae64c0faa34        ubuntu:14.04        /bin/bash           41 minutes ago      Exit 0                                  jovial_hawking
3a09b2588478        ubuntu:14.04        /bin/bash           45 minutes ago      Exit 0                                  kickass_lovelace

以 3a09b2588478 这个容器 ID 作为示例。

2. 使用 docker commit + docker save + docker load 命令导出容器和导入镜像

2.1 提交变更,并且把容器保存成镜像,命名为 mynewimage。
$ docker commit 3a09b2588478 mynewimage
$ docker images

2.2 把镜像保存为 tar 文件
$ docker save mynewimage > /tmp/mynewimage.tar
把 mynewimage.tar 拷贝到新的 Docker 实例中,比如 FTP、SCP 或者直接使用 NFS 共享。

2.3 从 tar 文件导入为镜像
$ docker load < /tmp/mynewimage.tar

2.4 检查镜像是否导入成功
$ docker images

3. 使用 docker export + docker import 命令导出容器和导入镜像

3.1 导出容器快照为 tar 文件
$ docker export 3a09b2588478 > mynewimage.tar

3.2 导入容器快照为镜像
$ cat mynewimage.tar | docker import test/mynewimage:v1.0
此外,也可以通过指定 URL 或者某个目录来导入,例如
$ docker import http://example.com/exampleimage.tgz example/imagerepo
$ tar -c .| docker import exampleimagedir

小结:既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。
两者区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。
此外,从容器快照文件导入时,可以重新指定标签等元数据信息。

Docker_010:多配置作业的 Jenkins

环境:MAC OS  X 10.12.1 + Docker 1.12.3

假设要在 Ubuntu、Debian 和 CentOS 上测试同一个应用程序。
要在多平台测试,可以利用 Jenkins 中的“多配置作业”特性。
当 Jenkins 多配置作业运行时,会运行多个配置不同的子作业。

1. 创建多配置作业
(1)创建一个多配置项目,命名为 Docker_matrix_job
(2)在 Souce Code Management 区域里,选择 Git 并指定仓库为 https://github.com/jamtur01/docker-jenkins-sample.git 。
(3)单击 Add Axis,选择 User-defined Axis,输入维度的名字为 OS,并设置三个值:centos、debian、ubuntu。
当执行多配置作业时,Jenkins 会查找这个维度,并生成三个子作业——维度上的每个值对应一个作业。
(4)勾选 Build Environment 中的 Delete workspace before build starts
(5)单击 Add Build Step,选择 Execute shelll,shell 脚本内容如下:
# Build the image to be used for this run.
cd $OS && IMAGE=$(sudo docker build . | tail -1 | awk '{ print $NF }')

# Build the directory to be mounted into Docker.

MNT="$WORKSPACE/.."

# Execute the build inside Docker.
CONTAINER=$(sudo docker run -d -v "$MNT:/opt/project" $IMAGE /bin/bash -c "cd /opt/project/$OS && rake spec")

# Attach to the container's streams so that we can see the output.
sudo docker attach $CONTAINER

# As soon as the process exits, get its return value.
RC=$(sudo docker wait $CONTAINER)

# Delete the container we've just used to free disk space.
sudo docker rm $CONTAINER

# Exit with the same value that the process exited with.
exit $RC

这个脚本都做了什么呢?
首先,每次执行作业都会进入不同的以操作系统为名的目录。每个目录里的 Dockerfile 文件都不同。
以 centos 为例,其 Dockerfile 内容如下:
  FROM ubuntu:14.04
  MAINTAINER James Turnbull "james@example.com"
  ENV REFRESHED_AT 2016-06-01
  RUN apt-get update
  RUN apt-get -y install ruby rake
  RUN gem install --no-rdoc --no-ri rspec ci_reporter_rspec

(6)单击 Add post-build action,加入一个 Publish JUnit test result report (JUnit 测试结果报告)
在 Test report XMLs 域,指定 spec/reports/*.xml。这个目录是 ci_reporter gem 的 XML 输出位置。
Jenkins 会处理测试的历史结果并输出结果。

2. 运行多配置作业
 

Docker_009:使用 Jenkins 测试持续集成

环境:MAC OS  X 10.12.1 + Docker 1.12.3

架构设计:构建一个运行在 Docker 容器中的 Jenkins 服务器,测试应用作为 Jenkins 的一个作业,以 Docker 容器的方式递归地运行在以 Docker 容器的方式运行的 Jenkins 服务器 中。

Jenkins 作业使用 Docker 创建了一个镜像,而这个镜像使用仓库的 Dockerfile 管理和更新。
这样的设计不仅架构配置和代码可以同步更新,管理配置也变得很简单。
通过镜像创建了运行测试的容器,测试完成时,可以丢弃测试容器。

1. 构建 Jenkins 镜像
$ cd /Users/maping/mygit/dockerbook-code/code/5/jenkins

$ cat Dockerfile
FROM jenkins
MAINTAINER james@example.com
ENV REFRESHED_AT 2016-06-01

USER root
RUN apt-get -qqy update && apt-get install -qqy sudo
RUN echo "jenkins ALL=NOPASSWD: ALL" >> /etc/sudoers
RUN wget http://get.docker.com/builds/Linux/x86_64/docker-latest.tgz
RUN tar -xvzf docker-latest.tgz
RUN mv docker/* /usr/bin/

USER jenkins
RUN /usr/local/bin/install-plugins.sh junit git git-client ssh-slaves greenballs chucknorris

$ docker build -t jamtur01/dockerjenkins .

2. 运行 Jenkins 容器
$ docker run -p 8080:8080 --name jenkins --privileged -d jamtur01/dockerjenkins
说明:
--privileged 启动 Docker 的特权模式,允许容器以宿主机具有的权限来运行容器,包括访问宿主机的内核和设备。这是让我们可以在 Docker 中运行 Docker 的必要设置。

$ docker logs -f jenkins
INFO: Jenkins is fully up and running
访问 http://localhost:8080/ 提示你输入管理员口令,口令在 /var/jenkins_home/secrets/initialAdminPassword

$ docker exec -it jenkins bash

jenkins@309e0a788aae:/$ cat /var/jenkins_home/secrets/initialAdminPassword
1bbd854f84b04b93865e0fad6a5aff68

输入口令后,进入安装插件界面。

2.1 安装插件

选择 Install Suggested Plugin,这一步需要翻墙,否则很慢,且有些插件安装不成功。
建议在公司网络环境下一次性安装成功所有插件。

2.2 创建管理员
maping/maping maping930883@hotmail.com

$ docker stop jenkins
$ docker start jenkins

2.3 保存安装好插件的 jenkins image
$ docker commit jenkins maping930883/jenkins:2.19.1
$ docker save -o jenkins.2.19.1.tar maping930883/jenkins
$ gzip -v jenkins.2.19.1.tar

2.4 启动新的 jenkins image
$ docker stop jenkins
$ docker rm jenkins
$ docker rmi jamtur01/dockerjenkins
$ docker run -p 8080:8080 --name jenkins --privileged -d maping930883/jenkins:2.19.1

2.5 创建 Jenkins 作业
(1)创建一个自由风格的作业,命名为 Docker_test_job
(2)点击 Advanced...按钮,单击 Use Custom workspace,指定 Directory 为 /tmp/jenkins-buildenv/${JOB_NAME}/workspace 。
(3)在 Souce Code Management 区域里,选择 Git 并指定仓库为 https://github.com/jamtur01/docker-jenkins-sample.git 。
(4)单击 Add Build Step,选择 Execute shelll,shell 脚本内容如下:
# Build the image to be used for this job.
IMAGE=$(sudo docker build . | tail -1 | awk '{ print $NF }')

# Build the directory to be mounted into Docker.
MNT="$WORKSPACE/.."

# Execute the build inside Docker.
CONTAINER=$(sudo docker run -d -v $MNT:/opt/project/ $IMAGE /bin/bash -c 'cd /opt/project/workspace && rake spec')

# Attach to the container so that we can see the output.
sudo docker attach $CONTAINER

# Get its exit code as soon as the container stops.
RC=$(sudo docker wait $CONTAINER)

# Delete the container we've just used.
sudo docker rm $CONTAINER

# Exit with the same value as that with which the process exited.
exit $RC

这个脚本都做了什么呢?
首先,它使用 Git 仓库中的 Dockerfile 创建一个新的镜像。Dockerfile 内容如下:
  FROM ubuntu:14.04
  MAINTAINER James Turnbull "james@example.com"
  ENV REFRESHED_AT 2016-06-01
  RUN apt-get update
  RUN apt-get -y install ruby rake
  RUN gem install --no-rdoc --no-ri rspec ci_reporter_rspec
可以看出,这样构建出来的镜像可以用于测试 Ruby 且使用 RSpec 测试框架的应用。
接下来,创建一个包含 Jenkins 工作空间的目录,并把这个目录挂载到 Docker 容器,并在这个目录里执行测试。
然后使用 docker attach 命令进入容器,得到容器输出的内容。
然后使用 docker wait 命令,该命令会一直被阻塞,直到容器里的命令执行完毕,返回容器退出时的返回码。
最后,删除刚刚创建的容器,并使用容器的返回码退出。Jenkins 根据这个返回码得知作业的测试结果是成功还是失败。
(5)单击 Add post-build action,加入一个 Publish JUnit test result report (JUnit 测试结果报告)
在 Test report XMLs 域,指定 spec/reports/*.xml。这个目录是 ci_reporter gem 的 XML 输出位置。
Jenkins 会处理测试的历史结果并输出结果。

保存。

3. 运行 Jenkins 作业
第一次运行时,会下载构建基础镜像,会慢一些,下次运行时就快了。

运行时报告如下错误:
Cannot connect to the Docker daemon. Is the docker daemon running on this host?

2016年11月19日星期六

Vagrant_002:Build RHEL 7.2 Virtual Box

环境:MAC OS X 10.12.1 + Vagrant 1.8.6

Vagrant 官网上没有 RHEL 7 的 Box,估计是版权问题,没关系,咱自己动手,丰衣足食。

1. 安装必要软件
(1)Vagrant
(2)Packer
下载地址:https://www.packer.io/
选择下载 Packer for MAC 64 bit:packer_0.12.0_darwin_amd64.zip。
$ unzip  packer_0.12.0_darwin_amd64.zip
$ mv  packer ~/Tools
(3)Git
(4)VirtualBox

2. 获取 build template
$ git clone https://github.com/opscode/bento.git
$ cd bento
$ ls -1 rhel*
rhel-5.11-i386.json
rhel-5.11-x86_64.json
rhel-6.6-i386.json
rhel-6.6-x86_64.json
rhel-6.7-i386.json
rhel-6.7-x86_64.json
rhel-7.2-x86_64.json

3. 下载 rhel-server-7.2-x86_64-dvd.iso

4. Build RHEL 7.2 Vagrant Box
$ cd bento
$ ~/Tools/packer build -only=virtualbox-iso -var "mirror=file:///Users/maping/Software" rhel-7.2-x86_64.json
在 builds 目录下会生成 rhel-7.2.virtualbox.box。

参考文献:
1. https://stomp.colorado.edu/blog/blog/2015/12/24/on-building-red-hat-enterprise-linux-vagrant-boxes/

2016年11月17日星期四

Docker_008:测试动态网站

环境:MAC OS  X 10.12.1 + Docker 1.12.1

Sinatra 是个开发框架,基于 Sinatra 开发的应用接收输入参数,然后将其转化为 JSON 输出。

1. 基于 Sinatra 框架的应用
$ cd /Users/maping/mygit/dockerbook-code/code/5/sinatra

$ cat Dockerfile

FROM ubuntu:16.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update
RUN apt-get -y install ruby ruby-dev build-essential redis-tools
RUN gem install --no-rdoc --no-ri sinatra json redis

RUN mkdir -p /opt/webapp

EXPOSE 4567

CMD [ "/opt/webapp/bin/webapp" ]

$ cat webapp/bin/webapp
#!/usr/bin/ruby

$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))

require 'app'

App.run!

$ docker build -t jamtur01/sinatra .

$ chmod +x webapp/bin/webapp

$ docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp jamtur01/sinatra
注意,这里没有在 docker run 命令行上提供要运行的命令,而是定义在 Dockerfile 中。

$ docker logs -f webapp // 持续输出容器的 STDERR 和 STDOUT 里的内容

$ docker top webapp // 查看容器里正在运行的进程

$ docker port webapp 4567
访问 http://localhost:<port>

$ curl -i -H 'Accept: application/json' -d 'name=Foo&status=Bar' http://localhost:<port>/json
输出如下:
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 29
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Sat, 19 Nov 2016 14:02:52 GMT
Connection: Keep-Alive

{"name":"Foo","status":"Bar"}

2. 扩展 Sinatra 应用,加入 Redis 后端数据库。
$ cd /Users/maping/mygit/dockerbook-code/code/5/sinatra/redis

$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update && apt-get -yqq install redis-server redis-tools

EXPOSE 6379

ENTRYPOINT ["/usr/bin/redis-server"]
CMD []

$ docker build -t jamtur01/redis .

$ docker run -d -p 6379 --name redis jamtur01/redis

$ docker port redis 6379

2.1 安装 Redis 客户端,我的 MAC 已安装,验证可以连到 Redis Server。
$ redis-cli -h 127.0.0.1 -p < port >
127.0.0.1:32769>

2.2 如何让 Sinatra 应用容器与 Redis 容器通信?
方式1:通过容器的公开端口绑定到本地网络接口。
方式2:使用 Docker 内部网络
安装 Docker 时,会创建一个新的网络接口:docker0。每个容器都会在这个接口上分配一个 IP 地址。docker0 是一个虚拟的以太网桥,用于连接容器和本地宿主网络。Docker 每创建一个容器就会创建一组互联的网络接口,一端作为容器里的 eth0 接口,另一端类似 vethec6a 这种名字,作为宿主机的一个端口。每个 veth*接口绑定到 docker0 网桥,Docker 创建了一个虚拟子网,这个子网由宿主机和所有的Docker 容器共享。

$ ip a show docker0
可以看到所有 Docker 容器的网关地址。

$ iptables -t nat -L -n

启动并进入一个容器,查看 IP 地址
$ docker run -it ubuntu bash
root@63725436b96f:/# ip a shwow eth0
root@63725436b96f:/# traceroute www.baidu.com

以上两种方式看上去都可行,但是要在应用程序里对 Redis 容器的 IP 地址硬编码;另外,如果重启容器,Docker 会改变容器的 IP 地址。
那么应该怎么办呢?
谢天谢地,Docker 有个叫做 link 的功能非常有用,它可以把多个 Docker 容器连接起来。

先删除旧的容器
$ docker stop redis
$ docker rm redis
$ docker run -d --name redis jamtur01/redis
$ docker stop webapp
$ docker rm webapp


2.3 让 Sinatra 应用容器和 Redis 容器互联
$ docker run -p 4567 --name webapp --link redis:db -it -v $PWD/webapp:/opt/webapp jamtur01/sinatra /bin/bash

说明:
(1)--name 给容器命名
(2)--link 创建两个容器之间的父子连接,一个是要连接的容器名字,另一个是连接后容器的别名。别名可以让我们访问公开的信息,而无需关注底层容器的名字。这里是 redis:db。
(3)启动 Redis 容器时,没有使用 -p 公开端口,因为不需要这样做。父容器直接访问任意子容器的公开端口。并且,只有使用 --link 连接到子容器的父容器才能连接到这个端口。子容器的端口不需要对宿主机公开,这是一个非常安全的模型。

可以让这个 Redis 实例服务于多个 Web 应用程序,如下:
$ docker run -p 4567 --name webapp2 --link redis:db -it -v $PWD/webapp:/opt/webapp jamtur01/sinatra /bin/bash
$ docker run -p 4567 --name webapp3 --link redis:db -it -v $PWD/webapp:/opt/webapp jamtur01/sinatra /bin/bash

注意,被连接的容器必须运行在同一个 Docker 宿主机上,不同宿主机上运行的容器无法连接。

Docker 在父容器的以下两个地方写入了连接信息:
(1)/etc/hosts 文件中
root@7844b851130b:/# cat /etc/hosts
输出如下:
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
ff00::0    ip6-mcastprefix
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters
172.17.0.2    db c6aaeb59594c redis // redis 容器的 IP 地址和主机名
172.17.0.3    7844b851130b // 容器自己的 IP 地址和主机名
(2)包含连接信息的环境变量中
root@7844b851130b:/# env
输出如下:
HOSTNAME=7844b851130b
DB_NAME=/webapp/db
DB_PORT_6379_TCP_PORT=6379
TERM=xterm
DB_PORT=tcp://172.17.0.2:6379
DB_PORT_6379_TCP=tcp://172.17.0.2:6379
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
DB_ENV_REFRESHED_AT=2014-06-01
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
REFRESHED_AT=2014-06-01
PWD=/
DB_PORT_6379_TCP_ADDR=172.17.0.2
DB_PORT_6379_TCP_PROTO=tcp
SHLVL=1
HOME=/root
no_proxy=localhost、*.local、169.254/16
DB_ENV_no_proxy=localhost、*.local、169.254/16
_=/usr/bin/env

Docker 在连接 webapp 和 redis 容器时,自动创建了这些以 DB 开头的环境变量。以 DB 开头是因为 DB 是创建连接时的别名。

当容器重启时,这两个地方的变量值会自动修改,这样就避免了程序硬编码的问题。

在容器里启动应用程序
root@
7844b851130b:/# nohup /opt/webapp/bin/webapp &

在宿主机上测试应用程序
$ docker port webapp
$ curl -i -H 'Accept: application/json' -d 'name=Foo&status=Bar' http://localhost:<port>/json

输出如下:
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 33
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Sat, 19 Nov 2016 14:11:56 GMT
Connection: Keep-Alive



{"name":"Foo","status":"Bar"}


访问 http://localhost:<port>

2.4 程序是如何连接 Redis 的? 
$ cat webapp_redis/lib/app.rb 
require "rubygems"
require "sinatra"
require "json"
require "redis"

class App < Sinatra::Application

      redis = Redis.new(:host => 'db', :port => '6379')

      set :bind, '0.0.0.0'

      get '/' do
        "<h1>DockerBook Test Redis-enabled Sinatra app</h1>"
      end

      get '/json' do
        params = redis.get "params"
        params.to_json
      end

      post '/json/?' do
        redis.set "params", [params].to_json
        params.to_json
      end
end

应用程序会在本地查找名叫 db 的主机,找到 /etc/hosts 文件里的项并正确解析。
这种方式称为使用本地 DNS 方法,注意这里硬编码了 redis 端口。
还有一种方法是读取环境变量。
require "uri"
......
uri = URI.parse(ENV['DB_PORT'])
redis = Redis.new(:host => uri.host, :port => uri.port)
......

Docker_007:测试静态网站

环境:MAC OS  X 10.12.1 + Docker 1.12.1

$ cd /Users/maping/mygit/dockerbook-code/code/5/sample/

$ cat Dockerfile
FROM ubuntu:16.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update && apt-get -yqq install nginx

RUN mkdir -p /var/www/html/website
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/

EXPOSE 80

$ cat global.conf
server {
        listen          0.0.0.0:80;
        server_name     _;

        root            /var/www/html/website;
        index           index.html index.htm;

        access_log      /var/log/nginx/default_access.log;
        error_log       /var/log/nginx/default_error.log;
}

$ cat nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;
daemon off;

events {  }

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  gzip on;
  gzip_disable "msie6";
  include /etc/nginx/conf.d/*.conf;
}
说明:daemon off; 选项阻止 Nginx 进入后台,强制其在前台运行。
这样做是为了保持 Docker 容器的活跃状态,否则,默认情况下 Nginx 会以守护进程的方式启动,当守护进程被 fork 启动后,发起守护进程的原始进程就会退出,这时容器就停止了。

$ docker build -t jamtur01/nginx .

$ docker history jamtur01/nginx

$ docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website jamtur01/nginx nginx
-v 选项允许将宿主机的目录作为卷,挂载到容器里。
或者
$ docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website:ro jamtur01/nginx nginx
使目录 /var/www/html/website 变成只读状态。
当我们不想把源代码构建到镜像中时,就体现出卷的价值了。

$ cat $PWD/website/index.html // 一个静态的 html 文件

$ docker ps
访问 http://localhost:<port>

$ vim $PWD/website/index.html // 修改网站
直接刷新浏览器,可以看到修改结果。

2016年11月15日星期二

Marathonn_012:2016嵩山少林马拉松

2016-11-13 膝盖受伤 + 空气重度污染 + 攀登嵩山(三皇寨-->少林寺),最终不得不放弃本次马拉松。


官方网址:http://songshan.malasong.org/
比赛时间:2016年11月13日
报名时间:2016年5月23日~7月31日
重要景点:中岳嵩山 少林寺
行程安排:

11月11日 北京 -> 郑州 -> 登封
火车票查询:北京 -> 郑州
http://trains.ctrip.com/TrainBooking/Search.aspx?from=beijing&to=zhengzhou&day=3&number=&fromCn=%B1%B1%BE%A9&toCn=%D6%A3%D6%DD
车次信息     出发时间     到达时间        出发站            到达站            运行时长     票价        备注
K507     21:23    05:40 +1        北京西(始发)    郑州(经停)        8:17         ¥ 283     软卧
K179     22:32    06:45 +1        北京西(始发)    郑州(经停)        8:13         ¥ 283     软卧

汽车票查询:郑州 -> 登封
http://bus.ctrip.com/busList.html?from=%E7%99%BB%E5%B0%81&to=%E9%83%91%E5%B7%9E&date=2016-08-16
首班车 6:00 ,每30分钟发一班,末班车 20:00,票价 ¥ 28 元。

11月12日 少林寺--三皇寨--嵩阳书院--太室山--法王寺--嵩岳寺塔,总共游玩需要 6 小时。

酒店查询:塔沟武校南门附近酒店
http://hotels.ctrip.com/hotel/dengfeng222/map/sl2863828#ctm_ref=hod_hp_sb_lst
入住酒店:登封精武大酒店 11月12日~11月13日,详细信息:http://hotels.ctrip.com/hotel/1759499.html,价格 ¥134 标间。

11月13日  马拉松,登封 -> 郑州 -> 北京
汽车票查询:登封 -> 郑州
http://bus.ctrip.com/busList.html?from=%E7%99%BB%E5%B0%81&to=%E9%83%91%E5%B7%9E&date=2016-08-17
首班车 5:00 ,每10分钟发一班,末班车 19:30,票价 ¥ 23 元。

火车票查询:郑州 -> 北京
http://trains.ctrip.com/TrainBooking/Search.aspx?from=zhengzhou&to=beijing&day=3&number=&fromCn=%D6%A3%D6%DD&toCn=%B1%B1%BE%A9###
车次信息     出发时间     到达时间        出发站            到达站            运行时长     票价        备注
G570     18:05    21:36        郑州    (始发)        北京西(经停)    3:31         ¥ 315     二等座  

2016年11月9日星期三

Vagrant_001:Mac 下安装与使用 Vagrant

环境:MAC OS X 10.12.1 + Vagrant 1.8.6

Vagrant 可以用来在单台物理机器编程管理多个虚拟机。其支持原生VirtualBox,并同时提供了对 VMware Fusion、Amazon EC2 虚拟机集群的插件支持。
Vagrant 提供了极易使用、基于Ruby的内部DSL,允许用户使用它们的配置参数定义一个或多个虚拟机。
对于自动部署,Vagrant 支持多种机制:可以使用 puppet、chef 或者在 Vagrant 配置文件中定义的所有虚拟机上自动安装软件程序和配置的 shell 脚本等。
因此,Vagrant 可以在运行着多台虚机的系统上定义复杂的虚拟框架,非常酷。

1. 下载并安装 VirtualBox
下载地址:https://www.virtualbox.org/wiki/Downloads

2. 下载并安装 Vagrant
下载地址:https://www.vagrantup.com/downloads.html
$ vagrant -v
Vagrant 1.8.6

3. 下载 Vagrant 官方提供的 OS 镜像
下载地址:http://www.vagrantbox.es/
选择 CentOS 7.0 x64 (Minimal, VirtualBox Guest Additions 4.3.28, Puppet 3.8.1 - see here for more infos)。

4. 创建开发目录

5. 添加下载好的 OS 镜像到 Vagrant
$ cd ~/Vagrant/projects/test
$ vagrant box add centos-7.0 ~/Vagrant/boxes/centos-7.0-x86_64.box

6. 初始化开发环境
$ vagrant init centos7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

7. 启动开发环境
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos-7.0'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: test_default_1478242742435_87282
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 4.3.28
    default: VirtualBox Version: 5.0
==> default: Mounting shared folders...
    default: /vagrant => /Users/maping/Vagrant/projects/test

8. 登录到虚机
$ vagrant ssh

9. 修改 Vagrantfile,去掉下面这行前面的注释
config.vm.network "private_network", ip: "192.168.33.10"

$ vagrant reload // 重启虚机
$ ping 192.168.33.10

10. 在虚机中安装 http 服务
[vagrant@localhost ~]$ sudo yum install httpd // 安装 http 服务
[vagrant@localhost ~]$ sudo systemctl start httpd.service // 启动 http 服务
[vagrant@localhost ~]$ sudo systemctl enable httpd // 设置 http 服务状态
[vagrant@localhost ~]$ sudo systemctl list-unit-files | grep enabled // 查看 http 服务的状态
[vagrant@localhost ~]$ sudo firewall-cmd --stat // 检测防火墙状态
[vagrant@localhost ~]$ sudo firewall-cmd --permanent --zone=public --add-service=http // 永久性开放 http 端口
[vagrant@localhost ~]$ sudo iptables -L -n | grep 21 // 查看80端口是否开放
[vagrant@localhost ~]$ sudo systemctl restart firewalld // 重启防火墙
[vagrant@localhost ~]$ exit
$ vagrant reload // 重启虚机
访问 http://192.168.33.10/

11. 修改 Vagrantfile,设置共享文件夹
(1) config.vm.synced_folder "../data", "/vagrant_data"
第1个目录是你本地的文件夹,第2个是挂在到虚拟机上的文件夹。
(2) 给共享文件夹设置权限

12. 打包分发
$ vagrant package

13. 其它 vagrant 常用命令
$ vagrant halt  // 关闭虚拟机
$ vagrant status  // 查看虚拟机运行状态
$ vagrant destroy  // 销毁当前虚拟机

参考文献:
1. http://www.jianshu.com/p/7747c31012f8 Mac OSX+VirtualBox+Vagrant+CentOS初体验
2. http://blog.csdn.net/ebw123/article/details/44138725 mac 下 安装vagrant