使用Docker构建并测试Web应用程序
使用sinatra作为web框架,redis作为后方的数据库。
1.1 创建Sinatra 容器
dongli@ubuntu:~/Docker/Sinatra
$ cat Dockerfile
FROM ubuntu
RUN apt-get 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"]
#
sudo docker build -t sinatra .
下载sinatra webapp 的应用程序
#wget --cut-dirs=3 -nH -r --no-parent http://dockerbook.com/code/5/sinatra/webapp
dongli@ubuntu:~/Docker/Sinatra$
chmod +x $PWD/webapp/bin/webapp
dongli@ubuntu:~/Docker/Sinatra/webapp/bin$ ls
index.html
webapp
dongli@ubuntu:~/Docker/Sinatra$
sudo docker run -d -p 127.0.0.1::4567 --name webapp -v $PWD/webapp:/opt/webapp sinatra
6a9a0b7403a311592035b8315a819510dd459d15f0a8936e1597b7ace6fc883c
dongli@ubuntu:~/Docker/Sinatra$
sudo docker logs webapp
[2017-10-29 08:21:20] INFO WEBrick 1.3.1
[2017-10-29 08:21:20] INFO ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.0) has taken the stage on 4567 for development with backup from WEBrick
[2017-10-29 08:21:20] INFO WEBrick::HTTPServer#start: pid=1 port=4567
-f : tail -f
测试:
dongli@ubuntu:~/Docker/Sinatra/webapp$ curl -i -H 'Accept: application/json' -d 'name=Foo1&status=bar2' http://127.0.0.1:32768/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: Sun, 29 Oct 2017 08:34:38 GMT
Connection: Keep-Alive
{"name":"Foo","status":"bar"}
dongli@ubuntu:~/Docker/Sinatra$ sudo docker logs -f webapp
[2017-10-29 08:21:20] INFO WEBrick 1.3.1
[2017-10-29 08:21:20] INFO ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.0) has taken the stage on 4567 for development with backup from WEBrick
[2017-10-29 08:21:20] INFO WEBrick::HTTPServer#start: pid=1 port=4567
172.17.0.1 - - [29/Oct/2017:08:34:38 +0000] "POST /json HTTP/1.1" 200 29 0.0337
172.17.0.1 - - [29/Oct/2017:08:34:38 UTC] "POST /json HTTP/1.1" 200 29
- -> /json
构建容器的Redis镜像的Dockerfile:
dongli@ubuntu:~/Docker/redis$
cat Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get -y install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
CMD []
dongli@ubuntu:~/Docker/redis$
构建redis 镜像:
dongli@ubuntu:~/Docker/redis$
sudo docker build -t redis .
[sudo] password for dongli:
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu
---> ae81bbda2b6c
---> Running in d881ffa2a86b
---> d7bf03fc11f2
Removing intermediate container d881ffa2a86b
Step 3 : RUN apt-get update
---> Running in 88f0348ec5ef
Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
Get:4 http://archive.ubuntu.com/ubuntu xenial/main Sources [1103 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial/restricted Sources [5179 B]
Get:6 http://archive.ubuntu.com/ubuntu xenial/universe Sources [9802 kB]
Get:7 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1558 kB]
Get:8 http://archive.ubuntu.com/ubuntu xenial/restricted amd64 Packages [14.1 kB]
Get:9 http://archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [9827 kB]
Get:10 http://archive.ubuntu.com/ubuntu xenial-updates/main Sources [352 kB]
Get:11 http://archive.ubuntu.com/ubuntu xenial-updates/restricted Sources [3617 B]
Get:12 http://archive.ubuntu.com/ubuntu xenial-updates/universe Sources [221 kB]
Get:13 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [833 kB]
Get:14 http://archive.ubuntu.com/ubuntu xenial-updates/restricted amd64 Packages [13.7 kB]
Get:15 http://archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [692 kB]
Get:16 http://archive.ubuntu.com/ubuntu xenial-security/main Sources [121 kB]
Get:17 http://archive.ubuntu.com/ubuntu xenial-security/restricted Sources [2770 B]
Get:18 http://archive.ubuntu.com/ubuntu xenial-security/universe Sources [50.7 kB]
Get:19 http://archive.ubuntu.com/ubuntu xenial-security/main amd64 Packages [476 kB]
Get:20 http://archive.ubuntu.com/ubuntu xenial-security/restricted amd64 Packages [12.9 kB]
Get:21 http://archive.ubuntu.com/ubuntu xenial-security/universe amd64 Packages [220 kB]
Fetched 25.8 MB in 2min 47s (154 kB/s)
Reading package lists...
---> af8251a47910
Removing intermediate container 88f0348ec5ef
Step 4 : RUN apt-get -y install redis-server redis-tools
---> Running in c97e4dba51d7
Reading package lists...
Building dependency tree...
Reading state information...
The following additional packages will be installed:
libjemalloc1
Suggested packages:
ruby-redis
The following NEW packages will be installed:
libjemalloc1 redis-server redis-tools
0 upgraded, 3 newly installed, 0 to remove and 38 not upgraded.
Need to get 517 kB of archives.
After this operation, 1505 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu xenial/universe amd64 libjemalloc1 amd64 3.6.0-9ubuntu1 [78.9 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial/universe amd64 redis-tools amd64 2:3.0.6-1 [95.3 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial/universe amd64 redis-server amd64 2:3.0.6-1 [343 kB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 517 kB in 3s (141 kB/s)
Selecting previously unselected package libjemalloc1.
(Reading database ... 7256 files and directories currently installed.)
Preparing to unpack .../libjemalloc1_3.6.0-9ubuntu1_amd64.deb ...
Unpacking libjemalloc1 (3.6.0-9ubuntu1) ...
Selecting previously unselected package redis-tools.
Preparing to unpack .../redis-tools_2%3a3.0.6-1_amd64.deb ...
Unpacking redis-tools (2:3.0.6-1) ...
Selecting previously unselected package redis-server.
Preparing to unpack .../redis-server_2%3a3.0.6-1_amd64.deb ...
Unpacking redis-server (2:3.0.6-1) ...
Processing triggers for libc-bin (2.23-0ubuntu3) ...
Processing triggers for systemd (229-4ubuntu7) ...
Setting up libjemalloc1 (3.6.0-9ubuntu1) ...
Setting up redis-tools (2:3.0.6-1) ...
Setting up redis-server (2:3.0.6-1) ...
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
Processing triggers for libc-bin (2.23-0ubuntu3) ...
Processing triggers for systemd (229-4ubuntu7) ...
---> fc09e8f1a5c0
Removing intermediate container c97e4dba51d7
Step 5 : EXPOSE 6379
---> Running in ed5e1a3e4ea5
---> e4ec8c27ce8c
Removing intermediate container ed5e1a3e4ea5
Step 6 : ENTRYPOINT /usr/bin/redis-server
---> Running in 23e00b6b9c1a
---> a8f9dbc36c96
Removing intermediate container 23e00b6b9c1a
Step 7 : CMD
---> Running in 8ea0fbff8d87
---> f4f39de16993
Removing intermediate container 8ea0fbff8d87
Successfully built f4f39de16993
dongli@ubuntu:~/Docker/redis$
启动redis容器:
dongli@ubuntu:~/Docker/redis$ sudo docker run -d -p 6379 --name redis redis
b3c8a62211e8447fe35cd82502e4602b571bca4d0dfbcc0f96cacc72b8171f0b
// 安装 redis-tools 在宿主主机上。
dongli@ubuntu:~/Docker/redis$ sudo apt-get -y install redis-tools
//验证 redis服务器工作正常
dongli@ubuntu:~/Docker/redis$ redis-cli -h 127.0.0.1 -p 32769
127.0.0.1:32769>
1.3 连接到容器
第一种方法,涉及Docker自己的网络栈, Docker 容器公开端口绑定到本地网络接口。再启动容器的时候指定的本地网络IP:port 和docker 端口的对应关系。
第二种, 内部网络:
Docker service 启动后出现新的网络接口 docker0.
dongli@ubuntu
:~$ ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:6b:4b:fe:66
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Docker0 符合RFC1918的私有IP。默认使用172.17.x.x 作为子网地址。如果有人占用了,在172.16 ~172.30 范围内尝试。Docker0 也是所有docker的网关。
docker0 是个虚拟的以太网桥,连接容器和本地宿主网络。启动一个容器,就会创建一组互联的网络接口
dongli@ubuntu
:~$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02426b4bfe66 no vethb232e5e
virbr0 8000.5254009cf352 yes virbr0-nic
一端
vethb232e5e 宿主机的一个端口, 另外一端是容器的eth0接口。
root@d6827c0c126b:/# ip a show eth0
10: eth0@if11: mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet
172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:3/64 scope link
valid_lft forever preferred_lft forever
traceroute to baidu.com (111.13.101.208), 30 hops max, 60 byte packets
1
172.17.0.1 (172.17.0.1) 0.068 ms 0.032 ms 0.024 ms
2 192.168.126.2 (192.168.126.2) 0.225 ms 0.165 ms 0.215 ms^C
command:
sudo iptables -t net -L -n 产看 IPtable 规则。
dongli@ubuntu:~$ sudo iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
RETURN all -- 192.168.122.0/24 224.0.0.0/24
RETURN all -- 192.168.122.0/24 255.255.255.255
MASQUERADE tcp -- 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
MASQUERADE udp -- 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
MASQUERADE all -- 192.168.122.0/24 !192.168.122.0/24
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:6379
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.2:6379
dongli@ubuntu:~$
容器默认是不能访问, 必须要指定端口。
1.4 连接到Redis 容器
sudo docker inspect redis
查看redie容器活动的IP:
sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' redis
dongli@ubuntu:~$ sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' redis
172.17.0.2
测试redis
dongli@ubuntu:~$ redis-cli -h 172.17.0.2
172.17.0.2:6379>
问题: docker 重启后IP会变
Docker link 功能将容器连接起来。
1.5 让 Docker容器互联
建立一个新的Redis 容器 ( 注意: 这里没有指定端口) 连接后父容器可以直接访问子容器。也只有使用--link标志连接到这个容器的容器才能连接到这个端口。容器端口不需要对宿主主机公开。
dongli@ubuntu:~$ sudo docker run -d --name redis redis
244d27f44982964cbd4fdafb3cc630624f03f64b8acdc319333be778d8e9ff19
建立一个webapp容器
dongli@ubuntu:~/Docker$ ls
redis sample Sinatra static_web test test2
dongli@ubuntu:~/Docker$ cd Sinatra/
dongli@ubuntu:~/Docker/Sinatra$ sudo docker run -p 4567 --name webapp --link redis:db -t -i -v $PWD/webapp:/opt/webapp sinatra /bin/bash
root@a70a6628bd08:/#
--link 标准创建了两个容器间的父子连接。两个参数:1.要连接的容器的名字, 2 连接后容器的名字。 要连接到redis,使用db作为别名。 让父容器访问子容器。
(处于安全考虑,可以强制Dokcer只允许有连接的容器间互相通信。 需要在启动Docker守护进程时加上 --ice=false,关闭所有没有连接的容器间的通信。
可以将多个web应用程序连接到redis容器。(同一台宿主)
root@a70a6628bd08:/# cat /etc/hosts
172.17.0.3 a70a6628bd08 // webapp
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 244d27f44982 redis //redis IP 连接指令建立的。 如果redis重启, IP会自动更新。
root@a70a6628bd08:/# env //注意DB开头的参数
HOSTNAME=a70a6628bd08
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:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
DB_PORT_6379_TCP_ADDR=172.17.0.2
DB_PORT_6379_TCP_PROTO=tcp
SHLVL=1
HOME=/root
_=/usr/bin/env
root@a70a6628bd08:/#
1.6 使用容器连接通信
1. 使用环境变量里边的信息
2. 使用DNS和/etc/hosts 信息
app.br:
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
"
DockerBook Test Redis-enabled Sinatra app
"
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
测试:
dongli@ubuntu:~/Docker/Sinatra/webapp/lib$ curl -i -H 'Accept: application/json' -d 'name=Foo1&status=bar2' http://localhost:32768/json
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 31
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: Fri, 03 Nov 2017 14:33:14 GMT
Connection: Keep-Alive
{"name":"Foo1","status":"bar2"} dongli@ubuntu:~/Docker/Sinatra/webapp/lib$
dongli@ubuntu:~/Docker/Sinatra/webapp/lib$ curl -i http://localhost:32768/json
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 43
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: Fri, 03 Nov 2017 14:33:23 GMT
Connection: Keep-Alive
"[{\"name\":\"Foo1\",\"status\":\"bar2\"}]" dongli@ubuntu:~/Docker/Sinatra/webapp/lib$