第5章 在测试中使用Docker–小铁笔记

2年前 (2016-02-16) admin 《第一本Docker书》 0评论 已收录 1155℃

第5章 在测试中使用Docker

5.1 使用Docker测试静态网站

将Docker作为本地Web开发环境是使用Docker的一个最简单的场景。这个环境可以完全重新生成

环境,保证开发环境和部署环境一致。

5.1.1 Sample网站的初始Dockerfile

-->mkdir sample

-->cd sample

-->touch Dockerfile

。创建nginx目录并下载实例文件

-->mkdir nginx && cd nginx #路径为:/root/sample

-->wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/global.conf

-->wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/nginx.conf

-->cd ..

。查看Dockerfile

-->vim Dockerfile

FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2015-09-07
RUN apt-get update
RUN apt-get -y -q install nginx #安装Nginx
RUN mkdir -p /var/www/html #创建目录/var/www/html
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80 #公开镜像的80端口

这个Nginx配置文件是为了运行Sample网站而配置的。将文件nginx/global.conf用ADD指令复制到/etc/nginx/conf.d/

目录中。

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;
}

表示将Nginx设置为监听80端口,并将网络服务的根路径设置为/var/www/html/website,目录使用RUN指令创建

配置Nginx为非守护进程的模式,这样可以让Nginx在Docker容器里工作。将这个nginx/nginx.conf复制到/etc/nginx

目录就可以达到这个目的。nginx.conf文件的内容:

user www-data;
worker_processes 4;
pid /run/nginx.pid
daemon off; #阻止Nginx进入后台,强制在其前台运行。

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/err.log
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf"
}

可以去以下网站复制nginx.conf和global.conf配置文件

http://www.dockerbook.com/code/index.html
git clone git://github.com/jamtur01/dockerbook-code.git

5.1.2 构建Sample网站和Nginx镜像

。构建镜像,并将镜像命名为: jamtur01/nginx

-->docker build -t jamtur01/nginx .

Sending build context to Docker daemon 1.651 MB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> 3f126a502d09
Step 1 : MAINTAINER James Turnbull "james@example.com"
---> Running in 68bd6bc133bd
---> e2876107add9
Removing intermediate container 68bd6bc133bd
Step 2 : ENV REFRESHED_AT 2015-09-07
---> Running in 7990b720cfe4
---> 40fb158aefc3
Removing intermediate container 7990b720cfe4
............................................
---> Running in e99778c58b6e
---> 4b7e716edcf2
Removing intermediate container e99778c58b6e
Step 6 : ADD nginx/global.conf /etc/nginx/conf.d/
---> 3da7b6da3be4
Removing intermediate container d0776baa2c73
Step 7 : ADD nginx/nginx.conf /etc/nginx/nginx.conf
---> c450af46f8b3
Removing intermediate container d455f9f86f8e
Step 8 : EXPOSE 80
---> Running in 2d24309fe029
---> 932ac9a67524
Removing intermediate container 2d24309fe029
Successfully built 932ac9a67524

。查看构建新镜像的步骤和层级

-->docker history jamtur01/nginx

IMAGE CREATED CREATED BY SIZE COMMENT
932ac9a67524 2 minutes ago /bin/sh -c #(nop) EXPOSE 80/tcp 0 B
c450af46f8b3 2 minutes ago /bin/sh -c #(nop) ADD file:8a4bb14bb8640aad85 415 B
3da7b6da3be4 2 minutes ago /bin/sh -c #(nop) ADD file:8865a4116b5c36a7d3 286 B
4b7e716edcf2 2 minutes ago /bin/sh -c mkdir -p /var/www/html 0 B
ea708e7c487d 2 minutes ago /bin/sh -c apt-get -y -q install nginx 15.41 MB
da672559cab3 3 minutes ago /bin/sh -c apt-get update 21.21 MB
40fb158aefc3 17 minutes ago /bin/sh -c #(nop) ENV REFRESHED_AT=2015-09-07 0 B
e2876107add9 17 minutes ago /bin/sh -c #(nop) MAINTAINER James Turnbull " 0 B
3f126a502d09 10 days ago /bin/sh -c #(nop) CMD ["/root/run.sh"] 0 B
81204241e37e 10 days ago /bin/sh -c #(nop) EXPOSE 22/tcp 0 B
0e9add2dc6f1 10 days ago /bin/sh -c chmod u+x /root/run.sh 30 B
2840077403c0 10 days ago /bin/sh -c echo "/usr/sbin/sshd -D" >> /root/ 30 B
0be466e628c6 10 days ago /bin/sh -c echo "#!/bin/bash" > /root/run.sh 12 B
b6234bc54a86 10 days ago /bin/sh -c #(nop) ADD file:013838b086146f3ae1 394 B
7ea248f92875 10 days ago /bin/sh -c sed -ri 's/session required 2.139 kB
f6e913655456 10 days ago /bin/sh -c mkdir -p /root/.ssh 0 B
62ffad3db9e0 10 days ago /bin/sh -c mkdir -p /var/run/sshd 0 B
fbd09100e81c 10 days ago /bin/sh -c apt-get install -y openssh-server 42.03 MB
08e1ad6ba259 10 days ago /bin/sh -c #(nop) MAINTAINER kaixin 424329402 0 B
91e54dfb1179 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
d74508fb6632 2 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.895 kB
c22013c84729 2 weeks ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB
d3a1f33e8a5a 2 weeks ago /bin/sh -c #(nop) ADD file:5a3f9e9ab88e725d60 188.2 MB

history命令从新构建的jjamtur01/nginx镜像的最后一层开始,追溯到最开始的父镜像ubuntu:14.04。

这个命令也展示了每步之间创建的新层,以及创建这个层所使用的Dockerfile里的指令。

5.1.3 从Sample网站和Nginx镜像构建容器

。测试Sample网站的容器:

-->cd sample

-->mkdir website && cd website

-->wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/website/index.html

。创建一个容器:

-->docker run -d -p 80 --name website \
-v $PWD/website:/var/www/html/website \
jamtur01/nginx nginx

b752ebcf4b780efd8dd30102809d6b32ae042b41e157a02e066a916543313303

-v:选项允许将宿主机的目录作为卷,挂载到容器里。指定了源目录(本地宿主机的目录)和容器例的目的目录,这两个目录

通过:来分隔。如果目的目录不存在,Docker会自动创建一个。

可以通过在目的目录后面加上rw或者ro来指定目的目录的读写状态。

-->docker run -d -p 80 --name website \
--v $PWD/website:/var/www/html/website:ro \
jamtur01/nginx nginx

在Nginx网络容器例,可以通过卷将$PWD/website挂载到容器的/var/www/html/webiste 目录,顺利挂载了正在开发的本地网站。

在Nginx配置里(在配置文件/etc/nginx/conf.d/global.conf中),已经指定了这个目录为Nginx服务器的工作目录。

。查看运行的容器

-->CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

b752ebcf4b78 jamtur01/nginx "nginx" 6 minutes ago Up 6 minutes 22/tcp, 0.0.0.0:32768->80/tcp website

可以看到,名为website的容器正处于活跃状态,其80端口被映射到本地32768端口。

。修改配置文件(上面的nginc.conf、global.conf等)

进入容器:

-->docker exec -it b752ebcf4b78 /bin/bash

root@b752ebcf4b78:/#

。创建网站

-->cd /var/www/html/website/ #容器中

-->wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/website/index.html

-->ls

index.html

。流量Sample网站

在Docker宿主机上流量32768端口:

-->curl localhost:32768

 

Test website

 

 

This is a test website

#网站内容为:This is a test website

 

5.1.4 修改网站

可以直接打开本地宿主机的website目录下的index.html文件并修改

-->cd /root/sample/website

-->vi $PWD/website/index.html

将内容改为:

This is a test website for Docker

再次浏览网站:

-->curl localhost:32768

Test website

 

 

This is a test website for Docker

#发现内容已经改变

 

注意:正在测试网站的运行环境,完全是生产环境例的真实状态。

5.2 使用Docker构建并测试Web应用程序

测试一个基于Sinatra的Web应用程序

5.2.1 构建Sinatra应用程序

-->mkdir Sinatra

-->cd Sinatra/

-->vim Dockerfile

FROM ubuntu:14.04

maintainer James Turnbull james@example.com

ENV REFRESHED_AT 2015-09-07

RUN apt-get update

RUN apt-get -y install ruby ruby-dev build-essential redis-tools

RUN apt-get -y install gem

RUN gem install --no-rdoc --no-ri sinatra json redis

RUN mkdir -p /opt/webapp

EXPOSE 4567

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

说明:创建了另一个基于Ubuntu的镜像,安装Ruby和RubyGem,并且使用gem命令安装了siatra、json和

redis包,然后创建了一个eMule来存放新的Web应用程序,并公开了 WEBrick的默认端口4567,最后,使用

CMD指定/opt/webapp/bin/webapp作为web应用程序的启动文件。

。构建新的镜像

先安装gem

-->docker build -t jamtur01/sinatra .

Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> 3f126a502d09
Step 1 : MAINTAINER James Turnbull james@example.com
---> Running in 59a8009682d5
---> d00a5b962879
Removing intermediate container 59a8009682d5
Step 2 : ENV REFRESHED_AT 2015-09-07
---> Running in ff8fd6e21b47
---> a3b48050cc13
Removing intermediate container ff8fd6e21b47
Step 3 : RUN apt-get update
---> Running in 4c1e71edfaf6
....................................................
Successfully installed tilt-2.0.1
Successfully installed rack-protection-1.5.3
Successfully installed sinatra-1.4.6
Building native extensions. This could take a while...
Successfully installed json-1.8.3
Successfully installed redis-3.2.1
6 gems installed
---> f605b5f14c2a
Removing intermediate container 8a913d5a70ae
Step 7 : RUN mkdir -p /opt/webapp
---> Running in bf738b33b443
---> e30c2194e3be
Removing intermediate container bf738b33b443
Step 8 : EXPOSE 4567
---> Running in fc25be9d5ec7
---> 76b0eb62a265
Removing intermediate container fc25be9d5ec7
Step 9 : CMD /opt/webapp/bin/webapp
---> Running in 8c8939793b92
---> fd38f8191394
Removing intermediate container 8c8939793b92
Successfully built fd38f8191394

。查看创建的镜像

-->docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
jamtur01/sinatra latest fd38f8191394 About a minute ago 566.5 MB

5.2.2 创建Sinatra容器

下载Sinatra Web应用程序的源代码。

在源码中的:/root/dockerbook-code/code/5/sinatra/webapp

或下载:

-->wget --cut-dirs=3 -nH -r --no-parent http://dockerbook.com/code/5/sinatra/webapp

-->mkdir /root/Sinatra

-->cd /root/Sinatra

-->mv bin webapp

-->ls -l webapp

总用量 8
drwxr-xr-x 2 root root 4096 9月 7 17:36 bin
-rw-r--r-- 1 root root 303 8月 17 00:39 Dockerfile

-->chmod +x $PWD/webapp/bin/webapp #保证文件有执行权限

。创建一个新的容器

-->docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp jamtur01/sinatra

0fad3436049e34f11370c7a4813a19bc2ab86a3b47358897faa8a97d303f7963

这里从jamtur01/sinatra 镜像创建一个名为webapp2的容器,指定了一个新卷$PWD/webapp,来存放新的Sinatra Web

应用程序,并将这个卷挂载到DockerFile里创建的目录/opt/webapp。

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

。查看日志:

-->docker logs webapp
----------------------------------------------------------------------------------------------------------------
报错:

/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- app (LoadError)
from /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /opt/webapp/bin/webapp:5:in `'
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- app (LoadError)
from /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /opt/webapp/bin/webapp:5:in `'

解决方法:

----------------------------------------------------------------------------------------------------------------

运行docker logs命令加上-f标志可以达到与执行tail -f命令一样的效果

-->docker logs -f webapp2

。查看进程

-->docker top webapp

从日志看出,容器中已经启动了Sinatra,而且WEBrick服务进程正在监听4567端口,等待测试。

。查看端口映射到本地宿主机的端口

-->docker port webapp 4567

。测试应用程序:

-->curl -i -H 'Accept: application/jsonl' \
-d 'name=Foo&status=Bar' http://localhost:49160/json

可以看出,给Sinatra应用程序传入了一些参数,并看到这些参数转化为JSON散列后的输出:

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

5.2.3 构建Redis镜像和容器

扩展Sinatra应用程序,加入Redis后端数据库,并在Redis数据库中存储输入的参数,为了达到这个目的,

要构建全新的镜像和容器来运行Redis数据库,之后,要利用Docker的特性来关联两个容器。

。构建新的镜像

-->mkdir Redis

-->cd Redis

-->vim Dockerfile

FROM ubuntu:14.04

MAINTAINER James Turnbull james@example.com

ENV REFRESHED_AT 2015-09-07

RUN apt-get update

RUN apt-get -y install redis-server redis-tools

EXPOSE 6379

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

CMD []

说明,在Dockerfile里指定了安装Redis服务器,公开了6379端口,并指定了启动Redis服务器的

ENTRYPOINT。

。构建镜像

-->docker build -t jamtur01/redis .

Sending build context to Docker daemon 3.072 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> 3f126a502d09
Step 1 : MAINTAINER James Turnbull "james@example.com"
---> Using cache
---> e2876107add9
Step 2 : ENV REFRESHED_AT 2014-06-01
---> Using cache
---> b04e897be1c9
Step 3 : RUN apt-get update
---> Using cache
---> e304c00e9422
Step 4 : RUN apt-get -y install redis-server redis-tools
---> Running in 5da9fa259de3
Reading package lists...
Building dependency tree...
Reading state information...
................................................
Removing intermediate container 5da9fa259de3
Step 5 : EXPOSE 6379
---> Running in d75247a79093
---> ef1f773af66a
Removing intermediate container d75247a79093
Step 6 : ENTRYPOINT /usr/bin/redis-server
---> Running in ca43de153645
---> 5112b502a3b9
Removing intermediate container ca43de153645
Step 7 : CMD
---> Running in ddfc532588ce
---> a61c446b7a5b
Removing intermediate container ddfc532588ce
Successfully built a61c446b7a5b

。查看创建的镜像

-->docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
jamtur01/redis latest a61c446b7a5b About a minute ago 253.9 MB

。启动容器

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

c8fe5914b78205a57a59a99abd6dda852f07c67baa8f06d84d16453183f58844

注意:使用-p标志来公开6379端口。

。查看端口映射到宿主机的端口

-->docker port redis 6379

0.0.0.0:32801

看到Redis端口映射到了32801端口。

。连接到Redis实例

需客户端测试(宿主机)

-->apt-get -y install redis-tools

#确认服务工作是否正常。

-->redis-cli -h 127.0.0.1 -p 32801
127.0.0.1:32801>

5.2.4 连接到Redis容器

第一种做法:公开容器端口并绑定到本地网络接口。

第二种做法:内部网络

在安装Docker时,会创建一个新的网络接口,名字是docker0,每个Docker容器都会在这个接口

上分配一个IP地址。Docker0接口有符合RFC1918的私有IP地址,范围是172.16~172.30。接口本身的

地址172.17.42.1 是这个Docker网络的网关地址,也是所有Docker容器的网关地址

docker0 Link encap:以太网 硬件地址 56:84:7a:fe:97:99
inet 地址:172.17.42.1 广播:0.0.0.0 掩码:255.255.0.0
inet6 地址: fe80::5484:7aff:fefe:9799/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:37751 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:37805 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:0
接收字节:1546633 (1.5 MB) 发送字节:154515229 (154.5 MB)

提示:Docker默认会使用172.17.x.x作为子网地址,除非已经有别人占用这个子网。如果被占用,

Docker会在172.16~172.30这个范围内尝试创建子网。

Docker每创建一个容器就会创建一组互联的网络接口,这组接口就像管道的两端(就是说,从一端

发送的数据会在另一端接收到)。这组接口其中一端作为容器里的eth0接口,而另一端统一命名为类似

vethec6a这种名字,作为宿主机的一个端口,可以把veth接口认为是虚拟网线的一端。这个虚拟线一端

插在名为docker0的网桥上,另一端查到容器里。通过把每个veth*接口绑定到docker0网桥,Docker创建

了虚拟子网。这个子网由宿主机和所有的Docker容器共享。

。查看子网管道的另一端

-->docker run -t -i ubuntu:14.04 /bin/bash

-->ip a show eth0

96: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:2f brd ff:ff:ff:ff:ff:ff
inet 172.17.0.47/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2f/64 scope link
valid_lft forever preferred_lft forever
root@826672b6b1ab:/#

可以看到,Dockerfile为容器分配了IP地址 作为宿主虚拟接口的另一端。这样就能够让宿主网络

和容器相互通信了。

。从容器内跟踪对外通信的路由,看看是如何建立建立的。

-->apt-get update

-->apt-get -y install traceroute

-->traceroute www.baidu.com

traceroute to www.baidu.com (220.181.112.244), 30 hops max, 60 byte packets
1 172.17.42.1 (172.17.42.1) 0.043 ms 0.009 ms 0.006 ms
2 192.168.1.1 (192.168.1.1) 0.140 ms 0.090 ms 0.113 ms
3 * * *
4 * * *
5 * * *
6 * * *
7 * * *
8 * * *
9 * * *
10 * * *

可以看到,容器地址的下一跳是宿主网络上docker 0接口的网关IP 172.17.42.1

Docker还需要防火墙规则和NAT配置才能允许Docker在宿主网络和容器间路由。

。查看宿主机上的IPTables NAT配置

-->iptables -t nat -L -n

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
MASQUERADE tcp -- 172.17.0.46 172.17.0.46 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:32801 to:172.17.0.46:6379

注意,从宿主网络与容器通信时,必须明确指定打开的端口,以DNAT(即目标NAT)这个规则为例,这个规则

把容器例的访问路由到Docker宿主机的32801端口。

5.2.5 连接Redis

。查看新的Redis容器的网络配置

-->docker inspect redis

[
{
"Id": "c8fe5914b78205a57a59a99abd6dda852f07c67baa8f06d84d16453183f58844",
"Created": "2015-09-08T07:36:58.483209097Z",
"NetworkSettings": {
"Bridge": "",
"EndpointID": "277a0e10a89c5bb48551bc55a28b4303cbe17e3309852f948a6e58dbf5da23fa",
"Gateway": "172.17.42.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"HairpinMode": false,
"IPAddress": "172.17.0.46",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:2e",
"NetworkID": "1e8bb683109d069b582f1bed1da5e342186a41913a7cf9f4d6fb717c837ff2d5",
"PortMapping": null,
"Ports": {
"22/tcp": null,
"6379/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "32801"
}
]
},

"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null,
"Labels": {}
}
}
]

。获取容器IP信息

-->docker inspect -f '{{ .NetworkSettings.IPAddress }}' redis

172.17.0.46

可以看到,容器的IP地址为172.17.0.46 并使用了docker 0接口作为网关地址。可以看到6379端口被映射到本地宿

主机的32801端口。只是,因为运行在本地的Docker宿主机上,所以不是一定要用映射后的端口,也可以直接使用172.17.0.46

地址与Redis服务器的6379端口通信。

-->redis-cli -h 172.17.0.46

172.17.0.46:6379>

注意:Docker默认会把公开的端口绑定到所有的网络接口上,因此,可以通过localhost或者127.0.0.1来访问Redis

服务器。

这种方法有两个问题:

第一,要在应用程序里对Redis容器的IP地址做硬编码;

第二,如果要重启服务器,Dockr会改变容器的IP地址;

。查看地址的变化:

-->docker restart redis

redis

。查看容器的IP地址

-->docker inspect -f '{{ .NetworkSettings.IPAddress }}' redis

172.17.0.48

可以看到,Redis容器有了新的IP地址172.17.0.48。

Docker有个叫做连接(link)的功能非常有用,这个功能可以把一个或者多个Docker容器连接起来,让其通信。

5.2.6 让Docker容器互联

。使用之前的容器:

-->docker rm redis

-->docker run -d --name redis jamtur01/redis

0ff13c1f37474621db994429ebeded430e4dab095c944fbb8b4cb777ddac7844

(注意,需要删除原来的jamtur01/redis容器,名称不能重复)

。启动Web应用程序容器

启动后,并把它连接到新的Redis容器上去:

-->docker rm webapp

-->docker run -p 4567 --name webapp --link redis:db -t -i -v $PWD/webapp:/opt/webapp jamtur01/sinatra /bin/bash

root@c2d75fe5b266:/#

参数:

--name:标志容器命名为webapp

-v: 把Web应用程序作为卷挂载到了容器里。

--link:标志创建了两个容器间的父子连接。这个标志需要两个参数:一个是要连接的容器的名字,另一个是连接后容器的别名。

此例中把新容器连接到redis容器,并使用db作为别名。别名可以访问公开的信息,而无需关注底层容器的名字。连接让父容器有

能力访问子容器,并且把子容器的一些连接细节分享给父容器。这些细节有助于配置应用程序并使用这个连接。

提示:处于安全原因(或者其它原因),可以强制Docker只允许有链接的容器之间相互通信。需要在启动Docker守护进程时加上

--icc=false标志,关闭所有没有连接的容器间的通信。

。将多个容器连接起来,如果想让这个redis实例服务于多个Web应用程序,可以把每个Web应用程序的容器和同一个redis容器连接

在一起。

-->docker run -p 4567 --name webapp2 --link redis:db ...

-->docker run -p 4567 --name webapp3 --link redis:db ...

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

最后,让容器启动时加载shell,而不是服务守护进程

Docker在父容器例的以下两个地方写入了连接信息:

。/etc/hosts 文件中

。包含连接信息的环境变量中

-->root@c2d75fe5b266:/# cat /etc/hosts

172.17.0.50 c2d75fe5b266
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.49 db 0ff13c1f3747 redis

可以看到:

第一项是容器自己的IP地址和主机名。

第二项是由该连接指令创建的,它是redis容器的IP地址和从该连接的别名衍生的主机名db。

-->ping db

(测试未通,书中是通的)

提示:重启容器时,容器的IP地址会发生变化,从Docker 1.3开始,如果被连接的容器重启了,/etc/host

文件中的IP地址被新的IP地址更新。

。查看环境变量:

-->root@c2d75fe5b266:/# env

HOSTNAME=c2d75fe5b266
DB_NAME=/webapp/db
DB_PORT_6379_TCP_PORT=6379
TERM=xterm
DB_PORT_22_TCP=tcp://172.17.0.49:22
DB_PORT=tcp://172.17.0.49:22
DB_PORT_6379_TCP=tcp://172.17.0.49: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: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:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=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:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=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:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=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:*.axa=00;36:*.oga=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.49
DB_PORT_6379_TCP_PROTO=tcp
SHLVL=1
HOME=/root
DB_PORT_22_TCP_PORT=22
LESSOPEN=| /usr/bin/lesspipe %s
DB_PORT_22_TCP_PROTO=tcp
LESSCLOSE=/usr/bin/lesspipe %s %s
DB_PORT_22_TCP_ADDR=172.17.0.49
_=/usr/bin/env

以DB开头时因为DB是创建连接时使用的别名。

自动创建的环境变量包含以下信息:

。子容器的名字:

。容器里运行的服务所使用的协议、IP和端口号

。容器里运行的不同服务所指定的协议、IP和端口号。

。容器里由Docker设置的环境变量的值。

5.2.7 使用容器连接来通信

给Sinatra应用程序加入一些连接信息,以便与Redis通信,有以下两种方法可以让应用程序

连接到Redis。

。使用环境变量里的一些连接信息

。使用DNS和/etc/hosts信息

第一种方法:使用环境变量例的一些连接信息

require 'uri'

....

uri=URI.parse(ENV['DB_PORT'])

redis = Redis.new(:host => uri.host, :port => uri.port)

...

上面代码中使用Ruby的URI模板来解析DB_PORT环境变量,并使用解析后的结果来配置Redis连接。应用程序

现在可以使用这个连接信息找到相连的Redis容器。通过环境变量,不再需要硬编码IP地址和端口来进行连接。

这是一种发现服务的方法。

第二种方法:使用本地DNS。

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

使用DNS和/etc/hosts信息

提示:可以在docker run命令中加入--dns或者--dns-search标志来为某个容器单独配制DNS。可以设置本地

DNS解析的路径或搜索域。

应用程序会在本地查找名叫db的主机,找到/etc/hosts文件里的项并解析到正确的IP地址。这也解决了硬编码

IP地址的问题。

-->cd /opt/webapp

下载代码:

http://dockerbook.com/code/5/sinatra/webapp_redis/

https://github.com/jamtur01/dockerbook-code

下载后的目录如下:(都在容器中操作)

-->ls

total 48
drwxr-xr-x 4 root root 4096 Sep 8 10:00 ./
drwx------ 6 root root 4096 Sep 8 09:55 ../
-rw-r--r-- 1 root root 30694 Sep 8 10:00 Dockerfile
drwxr-xr-x 2 root root 4096 Sep 8 09:55 bin/
drwxr-xr-x 2 root root 4096 Sep 8 09:58 lib/

-->chmod +x /opt/webapp/bin/webapp

。在容器里启动应用程序,查看DNS本地解析能不能工作:

-->nohup /opt/webapp/bin/webapp &

[1] 381
root@c2d75fe5b266:~/webapp_redis# nohup: ignoring input and appending output to 'nohup.out'
nohup: failed to run command '/opt/weapp/bin/webapp': No such file or directory
nohup /opt/webapp/bin/webapp &
[2] 382
[1] Exit 127 nohup /opt/weapp/bin/webapp
root@c2d75fe5b266:~/webapp_redis# nohup: ignoring input and appending output to 'nohup.out'

这里启动了Sinatra应用程序并让其在后台运行。

。Docker宿主机上再次使用curl命令测试应用程序。

-->curl -i -H 'Accept" application/json' -d 'name=Foo&status=Bar' http://localhost:49161/json

说明:49161为之前启动的redis端口

。确认下Redis实例接收到了这个更新

-->curl -i http://localhost:49161/json

现在已经连接到了应用程序(应用程序连接到了Redis),应用程序会检查Redis里存储的键,找出一个叫params

的键,然后查询这个键来看看我们的两个参数(name=Foo和status=Bar)已经存入了Redis。应用程序工作了。

这个Web应用程序栈由以下几部分组成:

。一个运行Sinatra的Web服务器容器

。一个Redis数据库容器

。这两个容器间的安全连接

可以很容易把这个概念扩展到别的应用程序栈,并用其在本地开发中做复杂的管理:

。Wordpress、HTML、CSS和JavaScript

。Ruby On Rails

。Django 和 Flask

。Node.js

。Play!

这样就可以在本地环境构建、复制、迭代开发用于生产的应用程序。

5.3 Docker用于持续集成

在持续集成环境里,每天要执行好几次安装并分发到宿主机的过程。这为测试生命周期增加了构建

和配置开销。打包和安装也消耗了很多时间,而且这个过程很恼人,尤其是需求变化频繁或者需要复杂、

耗时的处理步骤进行清理的情况下。

Docker让部署以及这些步骤和宿主机的清理变得开销很低。

5.3.1 构建Jenkins和Docker服务器

为了提供一个Jenkins服务器,从Dockerfile开始构建一个安装了Jenkins和Docker的Ubuntu 14.04镜像。

-->cd ~

-->mkdir jenkins

-->vim Dockerfile

FROM ubuntu:14.04
MAINTAINER james@example.com
ENV REFRESHED_AT 2014-06-01

RUN apt-get update -qq && apt-get install -y curl apt-transport-https
RUN curl https://get.docker.com/gpg | apt-key add -
RUN echo deb http://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list
RUN apt-get update -y && apt-get install -y iptables ca-certificates lxc openjdk-7-jdk git-core lxc-docker

ENV JENKINS_HOME /opt/jenkins/data
ENV JENKINS_MIRROR http://mirrors.jenkins-ci.org

RUN mkdir -p $JENKINS_HOME/plugins
RUN curl -sf -o /opt/jenkins/jenkins.war -L $JENKINS_MIRROR/war-stable/latest/jenkins.war

RUN for plugin in chucknorris greenballs scm-api git-client git ws-cleanup ;\
do curl -sf -o $JENKINS_HOME/plugins/${plugin}.hpi \
-L $JENKINS_MIRROR/plugins/${plugin}/latest/${plugin}.hpi ; done

ADD ./dockerjenkins.sh /usr/local/bin/dockerjenkins.sh
RUN chmod +x /usr/local/bin/dockerjenkins.sh

VOLUME /var/lib/docker

EXPOSE 8080

ENTRYPOINT [ "/usr/local/bin/dockerjenkins.sh" ]

说明:

首先,设置了Ubuntu环境,加入了需要的Docker APT仓库,并加入了对应的GPG key。之后更新了包列表,

并安装执行Docker和Jenkins所需要的包。

然后创建了/opt/jenkins目录,并把最新稳定版本的Jenkins下载到这个目录。还需要一些Jenkins插件,

给Jenkins提供额外的功能。

还使用ENV指令把JENKINS_HOME和JENKINS_MIRROR环境变量设置为Jenkins的数据目录和镜像站点。

VOLUME指令从容器运行的宿主机上挂载一个卷。在这里,为了“骗过”Docker,指定/var/lib/docker作为卷。

这是因为/var/lib/docker目录是Docker用来存储其容器的目录。这个位置必须是真实的文件系统,而不能是像Docker

镜像层那种挂载点。

使用VOLUME指令告诉Docker进程,在容器运行内部使用宿主机的文件系统作为容器的存储。这样,容器内嵌Docker

的/var/lib/docker目录将保存在宿主机系统的/var/lib/docker/volumes目录下的某个位置。

EXPOSE 8080,表示公开容器8080端口。

最后,指定了一个要运行shell脚本作为容器的启动命令。这个shell脚本帮助在宿主机上配置Docker,允许在Docker

里运行Docker,开启Docker守护进程,并且启动Jenkins。

。构建镜像

-->docker build -t jamtru01/dockerjenkins .

Sending build context to Docker daemon 7.168 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> 3f126a502d09
Step 1 : MAINTAINER james@example.com
---> Running in 4846428d0458
---> cbbf206699e6
Removing intermediate container 4846428d0458
Step 2 : ENV REFRESHED_AT 2014-06-01
---> Running in 5084db130a4e
---> 472da7a36209
Removing intermediate container 5084db130a4e
Step 3 : RUN apt-get update -y && apt-get install -y curl apt-transport-https
---> Running in 2deef32dac89
..............................................................................
Removing intermediate container 10150bd6f98e
Step 15 : EXPOSE 8080
---> Running in 0056a30b4369
---> bde981e55cd1
Removing intermediate container 0056a30b4369
Step 16 : ENTRYPOINT /usr/local/bin/dockerjenkins.sh
---> Running in 0a115716139b
---> c991c538c02d
Removing intermediate container 0a115716139b
Successfully built c991c538c02d

。从镜像创建容器

-->docker run -p 8080:8080 --name jenkins --privileged -d jamtru01/dockerjenkins

2a8cb2911eebc86131fde05c57a8690b0d330341b002b609805f202eb4c3fb9c

。查看容器:

-->docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

2a8cb2911eeb jamtru01/dockerjenkins "/usr/local/bin/dock 37 seconds ago Up 37 seconds 22/tcp, 0.0.0.0:8080->8080/tcp jenkins

参数:privileged标志可以启动Docker的特权模式,这种模式允许以其宿主机具有的所有能力

来运行容器,包括一些内核特性和设备访问。可以在Docker中运行Docker必要的魔法。

警告:让Docker运行在privileged特权模式会有一些安全风险,在这种模式下运行容器读Docker

宿主机拥有root访问权限。确保已经对Docker宿主机进行了恰当的安全保护,并且只在确实可信的域

里使用特权访问Docker宿主机,或者仅在由类似信任的情况下运行容器。

。查看容器日志

-->docker logs jenkins

/proc/self/fd /
/
time="2015-09-09T08:43:38.687741250Z" level=info msg="Listening for HTTP on unix (/var/run/docker.sock)"
time="2015-09-09T08:43:38.887342552Z" level=warning msg="Running modprobe bridge nf_nat failed with message: , error: exit status 1"
time="2015-09-09T08:43:39.549357269Z" level=warning msg="Your kernel does not support swap memory limit."
time="2015-09-09T08:43:39.553074301Z" level=info msg="Loading containers: start."

..................................................................................
INFO: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller
Sep 09, 2015 8:45:56 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Ant.AntInstaller
Sep 09, 2015 8:46:14 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tools.JDKInstaller
Sep 09, 2015 8:46:15 AM hudson.model.AsyncPeriodicWork$1 run
INFO: Finished Download metadata. 122,177 ms

-->docker logs -f jenkins

/proc/self/fd /
/
time="2015-09-09T08:43:38.687741250Z" level=info msg="Listening for HTTP on unix (/var/run/docker.sock)"
time="2015-09-09T08:43:38.887342552Z" level=warning msg="Running modprobe bridge nf_nat failed with message: , error: exit status 1"
time="2015-09-09T08:43:39.549357269Z" level=warning msg="Your kernel does not support swap memory limit."
time="2015-09-09T08:43:39.553074301Z" level=info msg="Loading containers: start."

................................................................................
INFO: Jenkins is fully up and running
Sep 09, 2015 8:45:37 AM hudson.model.UpdateSite updateData
INFO: Obtained the latest update center data file for UpdateSource default
Sep 09, 2015 8:45:54 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller
Sep 09, 2015 8:45:56 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Ant.AntInstaller
Sep 09, 2015 8:46:14 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tools.JDKInstaller
Sep 09, 2015 8:46:15 AM hudson.model.AsyncPeriodicWork$1 run
INFO: Finished Download metadata. 122,177 ms

。访问Jenkins服务器(8080)

-->http://ip:8080 (宿主机IP)

界面中间显示“欢迎使用Jenkins!”

5.3.2 创建新的Jenkins作业

在上面打开的web中(Jenkins服务),在界面上单击“create new jobs”(创建新作业)链接。

-->创建一个新任务(create now job)

-->Item名称:Docker_test_job-->选择“构建一个自由风格的软件项目”-->OK

-->项目名称:Docker_test_job

描述:docker test

(单击右侧“高级”)

勾选“使用自定义的工作空间”-->目录:/tmp/jenkins-buildenv/$(JOB_NAME}/workspace

显示名称:jenkins-workspace

。源码管理:

选择“Git”-->Repository URL: https://github.com/jamtur01/docker-jenkins-sample.git

。构建-->增加构建步骤-->选择"Execute Shell"

# 构建用于此作业的镜像

IMAGE=$(docker build . | tail -l | awk ' { print $NF }')

# 构建挂载到Docker的目录

MNT="$WORKSPACE/..."

# 在Docker里执行编译测试

CONTAINER=$(docker run -d -v "$MNT:/opt/project" $IMAGE /bin/bash -c 'cd /opt/project/workspace && rake spec')

# 进入容器,这样可以看到输出的内容

docker attach $CONTAINER

# 等待程序退出,得到返回码
RC=$(docker wait $CONTAINER)

# 删除刚刚用到的容器
docker rm $CONAINER

# 使用刚才的返回码退出整个脚本
exit $RC

脚本说明:它将使用包含刚刚指定的Git仓库的Dockerfile创建一个新的Docker镜像。这个Dockerfile提供了想要执行的

测试环境。

这个Dockerfile如下:

FROM ubuntu:14.04

MAINTAINER James Turnbull "james@example.com"

ENV REFRESHED_AT 2015-09-09

RUN apt-get update

RUN apt-get -y install ruby rake

RUN gem install --no-rdoc --no-ri rspec ci_reporter_rspec

提示:Docker在启动容器时支持--cidfile选项,这个选项会让Docker截获容器ID并将其存到--cidfile选项指定的

文件里,如--cidfile=/tmp/containerid.txt

-->增加构建后操作步骤-->"Publish JUnit test result report(公布JUint测试结果报告的动作)"-->测试报告(XML): spec/reports/*.xml

(后面指定的目录会让Jenkins处理测试的历史结果和输出结果)

-->保存

5.3.3 运行jenkins作业

在界面左侧单击“立即构建”,会单独打开另一个界面,出现“构建历史”进度方框。

(第一次运行测试时,可能会因为构建新的镜像而等待较长一段时间,但是,下次运行测试时,因为Docker已经准备好了镜像,执行速度

就会比第一次快多了)。

-->在“工作空间"右侧-->相关链接-->点击"Last unsuccessful build(#1),1 分之前" 查看测试作业已经执行的命令。

-->点击"Console Output"->右侧显示“控制台输出”及相关日志

从日志看出:

可以看到,Jenkins正在将Git仓库下载到工作空间,然后会执行shell脚本并使用docker build构建Docker镜像,然后捕获镜像的ID并用

docker run创建一个新容器。正在运行的这个新容器内会执行RSpec测试并且捕获测试结果和返回码。如果这个作业使用返回码0退出,这个作业

就会被标识为测试成功。

(左侧没找到此项)
-->单击“Test Result(测试结果)”,可以看到详细的测试报告,这个报告是从测试的RSpec结果转换为JUnit格式后得到的。这个转换由

ci_reporter gem完成,并在“构建后的步骤”里被捕获。

5.3.4 与Jenkins作业有关的下一步

可以同启用SCM轮询,让Jenkins作业自动执行。它会在由新的改动签入Git仓库后,触发自动构建。类似的自动化还可以通过提交后的钩子

或者GitHub或者Bitbucket仓库的勾子来完成。

5.3.5 Jenkins设置小结

到目前为止,已经做了不少事情,安装并运行了Jeknins,创建了第一个作业。这个Jenkins作业使用Docker创建了一个镜像,而这个镜像仓库

里的Dockerfile管理和更新。这种情况下,不但架构配置和代码可以同步更新,管理配置的郭晨也变得简单。然后通过镜像创建了运行测试的容器。

测试完成时,可以丢弃这个容器。整个测试过程轻量且快速。将这个例子适配到其它不同的测试凭他或者其它语言的测试框架也很容易。

5.4 多配置的Jenkins

如果要测试的应用依赖多个平台,如Ubuntu、Debian和CentOS,在多平台测试,可以利用Jenkins里叫“多配置作业”的作业类型的特性。

多配置作业运行运行一系列的测试作业。当Jenkins多配置作业运行时,会运行多个配置不同的子作业。

5.4.1 创建多配置作业

在Jenkins控制台里:

-->单击“新建” -->Item名称:Docker_matrix_job-->OK

选择“创建一个多配置项目”

-->项目名称:Docker_matrix_job

描述:This is a Docker multi-configuration job.
,

-->源码管理-->Git-->Repository URL: https://github.com/jamtur01/docker-jenkins-sample.git

(开始多维度的配置(axis)。维度是指作为作业的一部分执行的一系列元素)

-->触发器-->"Configuration Matrix"-->Add axis-->User-defined Axis(用户自定义维度)-->Name;OS

Values:centos debian ubuntu

(当执行多配置作业是,Jenkins会查找这个维度,并生成三个作业:维度上的每个值对应一个作业)

-->构建-->增加构建步骤-->Execute shell-->

# 构建此次运行需要的镜像
cd $OS && IMAGE=$(docker build . | tail -l | awk '{ print $NF }')

# 构建挂载到Docker的目录
MNT="$WORKSPACE/.."

# 在Docker内执行构建过程
CONTAINER=$(docker run -d -v "$MNT:/opt/project" $IMAGE /bin/bash -c "cd /opt/project/$OS && rake spec")

#进入容器,以便可以看到输出的内容
docker attach $CONTAINER

#进程退出后,得到返回值
RC=$(docker wait $CONTAINER)

#删除刚刚使用的容器
docker rm $CONTAINER

#使用刚才的返回值退出脚本
exit $RC

脚本说明:每次执行作业都会进入不同的操作系统为名的目录。在测试仓库里有三个目录:centos debian ubuntu。

每个目录里的Dockerfile都不同,分别包含构建CentOS、Debian和Ubuntu镜像的指令。这意味着每个被启动的作业都会

进入对应的目录,构建对应的操作系统的镜像,安装相应的环境需求,并启动基于这个镜像的容器,最后在容器里运行测试。

注意:在单击build starts(开始构建)之前,先单击了Build Environment(构建环境)部分的Delete workspace

before build starts(构建前删除工作空间)。这个选项会在一系列作业初始化之前,先删除已经签出的仓库,清理构建

环境。

新的Dockerfile:

FROM centos:latest

MAINTAINER James Turnbull "james@example.com"

ENV REFRESHED_AT 2015-09-09

RUN yum -y install ruby rubygems rubygem-rake

RUN gem install --no-rdoc --no-ri rspec ci_reporter_rspec

这是一个基于以前 的作业针对CentOS修改过的Dockerfile。这个Dockerfile和之前的做的事情一样,

只是改为适合CentOS的命令,比如使用yum来安装包。

-->保存

5.4.2 测试多配置作业

-->单击“立即构建”

-->右侧显示:

配置:

。centos 。debian 。ubuntu #出现在闪烁的灰色小圆球

构建完成的显示为:红色

开始运行时,会先创建三个子作业,每个子作业会使用选定的三个平台中的一个来进行测试。

会看到,主作业会先执行,然后执行每个子作业,其中centos子作业的输出可查看:

-->Console Outoput:右侧日志

可以看到,这个作业复制了仓库,构建了需要的Docker镜像,从镜像启动了容器,最后运行了所有的

测试。所有测试都成功了。

5.4.3 Jenkins多配置作业小结

可以对例子加以修改,实现多平台、多架构、多版本的多级作业矩阵等功能。

5.5 其他选择

在Docker的生态环境中,持续集成和持续部署(CI/CD)是很有意思的一部分。除了与现有的Jenkins这种

工具集成,也有很多人直接使用Docker来构建这类工具。

5.5.1 Drone

Drone是著名的基于Docker开发的CI/CD工具之一。它是一个SaaS持续集成平台,可以与GitHub、Bitbucket和

Bitbucket和Google仓库关联,支持各种语言,包括Python、Node.js、Ruby、Go等。Drone在一个Docker容器内运行

加到其中的仓库的测试集。

5.5.2 Shippable

Shippable是免费的迟勋集成和部署服务,基于Github和Bitbucket。它非常快,也很轻量,原生支持Docker。

博主

让学习成为习惯,坚持-共享-开源-自由! 成功者决不放弃,放弃者绝不成功!

相关推荐

嗨、骚年、快来消灭0回复。