2017-07-15 10:25

(五)Docker 快速入门 - 第一个应用

这一节我们从最基本的 web 应用开始,分别演示3个不同的场景在 Docker 下的开发和测试。

简单的静态站

我们先从构建一个最简单的 nginx 服务器开始,先准备几个配置文件:

# cat nginx/default
server {
       listen 80 default_server;
       listen [::]:80 default_server;

       root /var/www/html/website;

       index index.html index.htm index.nginx-debian.html;
       server_name _;
       location / {
              try_files $uri $uri/ =404;
       }
}
# cat Dockerfile
FROM ubuntu:16.04
MAINTAINER Leon "leon@weippt.com"
ENV REFRESHED_AT 2017-07-15
RUN mkdir -p /var/www/html/website
RUN apt-get -yqq update && apt-get -yqq install nginx
ADD nginx/default /etc/nginx/sites-available/default
EXPOSE 80
# sudo docker build -t leon/nginx-sample .

现在构建上面的镜像,构建好之后,在当前目录创建一个 website 项目:

$ echo '<p>test</p>' > website/index.html

接着我们开始构建容器:

$ sudo docker run -d -p 80 --name nginx-sample-web \
-v $PWD/website:/var/www/html/website \
leon/nginx-sample nginx -g "daemon off"
# -v 将宿主目录作为卷挂载到容器里;
# -v $PWD/website:/var/www/html/website:ro 也可以在挂载同时指定可写状态;

一切顺利的话,此时通过 $sudo docker ps -l 可以找到这个容器映射到本地的端口,并在浏览器中测试会看到 test 页面。

动态站(+Redis)

这是一个基于Sinatra轻量级框架的Ruby应用,我们先准备项目 webapp 的源代码:

$ cat web/bin/webapp (chmod +x)

#!/usr/bin/ruby

$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require 'app'
App.run!

$ cat web/lib/app.rb

require "rubygems"
require "sinatra"
require "json"
require "redis"

class App < Sinatra::Application
      redis = Redis.new(:host => 'redis-net', :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

$ cat Dockerfile

# webapp Dockerfile
FROM ubuntu:16.04MAINTAINER Leon "leon@weippt.com"
ENV REFRESHED_AT 2017-07-15
RUN apt-get -yqq update && apt-get -yqq 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" ]
# sudo docker build -t test/webapp .
# sudo docker run -d -p 4567 --name webapp -v $PWD/web:/opt/webapp test/webapp

我们构建这个镜像,并启动这个 webapp 容器,启动成功之后可以通过 logs 查看启动命令的输出:

$ sudo docker logs webapp [-f 持续输出]    
$ sudo docker top webapp  [查看容器进程状态]
$ sudo docker port webapp 4567  [查看端口映射关系]

现在在浏览器中已经可以打开这个web的页面了。接下来,我们构建一个 Redis 数据库镜像:

$ redis Dockerfile
FROM ubuntu:16.04
MAINTAINER Leon "leon@weippt.com"
ENV REFRESHED_AT 2017-07-15

RUN apt-get -yqq update && apt-get -yqq install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT [ "/usr/bin/redis-server" ]
CMD []
# sudo docker build -t test/redis .
# sudo docker run -d -p 6379 --name redis test/redis

构建这个镜像并启动容器。接下来我们在宿主上测试 redis 容器:

$ sudo apt-get install redis-tools # redhat:yum install redis
$ sudo docker port redis 6379 # 查看映射到宿主的端口
$ redis-cli -h 127.0.0.1 -p [port] # 测试连接
$ redis-cli -h 容器IP  # 使用docker inspect找到容器ip测试也可以;
容器间的连接

现在 webapp 已经搭建完毕了,但 web 和 redis 还未连接。容器间的连接一般有以下几种方法:

  • Docker内部网络(不灵活,IP不固定,不推荐);
    在宿主上你可以发现有一个网络接口:docker0,每个容器都会在这个接口上分配一个IP;每创建一个容器在宿主网络接口上会创建一个接口,这组接口就像管道的两端(可以理解为虚拟网线):其中一端插到容器的eth0接口,另一端是命名为 veth* 的接口,插到docker0上;
    $ ip a show docker0  #宿主上可以查看docker0接口信息;
    $ traceroute weibo.com  #在容器中查看路由信息;
    $ ip a show eth0  # 在容器中查看eth0接口;
    $ sudo iptables -t nat -L -n  # 查看宿主的防火墙和NAT信息;

  • Docker链接(把容器连接起来进行通信的抽象层)(Ver1.9之前选择);
    链接是在创建容器时通过 --link 实现,这里不做过多介绍。

  • DockerNetworking 和 docker network命令(Ver1.9+选择,下面我们主要介绍这种方案);

DockerNetworking

要是用 DockerNetworking 必须先创建一个网络,然后在这个网络下启动容器即可。

$ sudo docker network create  app  #创建一个名为 app 的桥接网络;
$ sudo docker network inspect app  #查看app的网络信息;
$ sudo docker network ls  #查看宿主上的所有网络;
$ sudo docker network rm  #删除网络;

注:除了单主机网络,还可以创建允许跨宿主通信的 overlay 网络,后面我们用到 overlay 网络会着重介绍;

回到刚刚的 webapp,我们在 Docker 网络中分别启动一个 webapp 和 redis 容器:

$ sudo docker run -d --net=app --name redis-net test/redis
$ sudo docker run -d -p 4567 --net=app --name webapp-net -v $PWD/web:/opt/webapp test/webapp
# 注意,这里要确保容器 webapp-net 的源代码可以链接到正确的 redis host!

现在我们已经可以通过 webapp-net 映射的端口测试 curl 传参,正常情况下,webapp-net 会把参数存储到 Redis 中,然后在通过 curl / get 测试获取:

$ curl -i -H 'Accept: application/json' -d 'name=Foo&status=1' http://localhost:[port]/json
$ curl -i http://localhost:[port]/json (正常情况下会返回Redis中的数据)

也可以直接登陆到 Redis 服务器查看已存储的数据:

redis-cli -h 容器IP
> keys *
> get [key]

我们也可以将已经存在的容器连接到指定 Docker 网络:

$ sudo docker network connect app webapp

$ sudo docker network disconnect app webapp #断开