文档章节

Docker学习笔记五 在测试中使用Docker

一万
 一万
发布于 2016/07/12 15:55
字数 3069
阅读 429
收藏 1

5.1 使用Docker测试静态网站(Nginx)

将项目命名为Sample

首先建立构建环境

mkdir sample
cd sample
touch Dockerfile

在构建环境中下载作者配置好的两个nginx配置文件:

mkdir nginx && cd nginx
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

写Dockerfile

FROM ubuntu:14.04
MAINTAINER Ivan Jiangzhi "ivanjz93@163.com"
ENV REFRESHED_AT 2016-07-09
RUN apt-get update
RUN apt-get -y -q install nginx
RUN mkdir -p /var/www/html
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

在nginx.conf配置文件中daemon off;选项阻止Nginx进入后台,强制其在前台运行。这是因为想要保持Docker容器的活跃状态,需要其中运行的进程不能中断。默认情况下,Nginx会以守护进程的方式启动,这会导致容器只是短暂运行,在守护进程被fork启动后,发起守护进程的原始进程就会退出,这时容器就停止了。

构建Sample

docker build -t ivan/nginx .

构建完成后可使用docker history查看构建步骤:

docker history ivan/nginx

下载作者写好的html(或者自己写一个也可以)

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 ivan/nginx nginx

-v 选项将宿主机的目录作为卷挂载到容器里。

卷是在一个或者多个容器内被选定的目录,可以绕过分层的联合文件系统,为Docker提供持久数据或者共享数据。这意味着对卷的修改会直接生效并绕过镜像。当提交或者创建镜像时,卷不被包含在镜像里。

在下面的时候可以考虑使用卷:

  • 希望同时对代码作开发和测试;
  • 代码改动很频繁,不想在开发过程中重构镜像;
  • 希望在多个容器间共享代码。

-v 指定了卷的源目录(宿主机目录)和容器里的目录,这两个目录用:分割。如果目的目录不存在,Docker会自动创建一个。

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

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

上例使目的目录变成只读状态。

docker run成功后在浏览器中访问 宿主机IP:映射端口即可看到页面,修改$PWD/website/index.html保存后刷新浏览器即时生效。

5.2 构建并测试Web应用程序(Sinatra + Redis)

1、构建Sinatra应用程序

创建sinatra构建环境:

mkdir sinatra
cd sinatra
touch Dockerfile

编写Dockerfile,书上的例子:

FROM ubuntu:14.04
MAINTAINER James Turnbull james@example.com
ENV REFRESHED_AT 2014-6-1

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"]

构建镜像

docker build -t ivan/sinatra .

2、创建Sinatra容器

下载作者编写的Sinatra Web应用程序的源码:

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

使用书中的命令下载时会报错,按报错的提示加入--no-check-certificate参数后成功下载。

为webapp/bin/webapp添加执行权限:

chmod +x $PWD/webapp/bin/webapp

启动容器并把$PWD/webapp按卷挂在到容器中:

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

我在操作时遇到的问题和解决方法

构建时会报错,说安装的ruby是1.9版本,而gem install json需要2.0以上的版本。

我直接从ruby镜像构建,去掉apt-get ruby的安装,容器可以正常构建。但是启动时会包找不到/opt/webapp/bin/webapp的错误,执行的权限已经添加,/bin/bash命令启动容器发现webapp用/usr/bin/ruby启动,但是容器里的ruby在/usr/local/bin/ruby,所以在构建时添加cp命令。我的Dockerfile如下:

FROM ruby
MAINTAINER James Turnbull james@example.com
ENV REFRESHED_AT 2014-6-1

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

RUN mkdir -p /opt/webapp
RUN cp /usr/local/bin/ruby /usr/bin/ruby

EXPOSE 4567

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

启动成功后查看执行命令的输出:

docker logs webapp

查看容器运行的进程:

docker top webapp

查看容器的4567端口映射到宿主机的端口:

docker port webapp 4567

测试Sinatra应用的运行情况,假设映射到宿主机的端口为49160(它接收输入参数,并将其转化为Json返回):

curl -l -H 'Accept:application/json' -d 'name=Foo&status=Bar' http://localhost:49160/json

3、构建Redis镜像和容器

创建redis构建环境:

mkdir redis
cd redis
touch Dockerfile

redis Dockerfile的内容:

FROM ubuntu:14.04
MAINTAINER James Turnbull james@example.com
ENV REFRESHED_AT 2016-07-11

RUN apt-get update
RUN apt-get -y install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/user/bin/redis-server"]
CMD []

构建redis镜像:

docker build -t ivan/redis .

创建并启动redis容器:

docker run -d -p 6379 --name redis ivan/redis

查看容器 6379端口映射到宿主机上的哪个端口:

docker port redis 6379

然后在宿主机上使用redis-cli测试redis-server在容器中的运行情况:

sudo apt-get -y install redis-tools
redis-cli -h 127.0.0.1 -p 49161

(假设容器的6379端口映射到了宿主机的49161端口上)。

我在宿主机的ubuntu上使用apt-get -y install redis-tools报无法定位redis-tools packages的错误,直接apt-get -y install redis-server 后可以使用redis-cli。

4、连接到Redis容器

一般有两种方法实现容器间的网络连接:

第一,Docker容器公开端口并绑定到本地网络接口,这样可以把容器里的服务在本地Docker宿主机所在的外部网络上公开(比如,把容器里的80端口绑到本地宿主机的高端口上)。

第二,内部网路。在安装Docker时,会创建一个新的网络接口,名字是docker0。docker0接口有符合RFC1918的私有IP地址,范围是172.16~172.30。docker0接口本身的地址是这个Docker网络的网关地址,也是所有Docker容器的网关地址。Docker默认会使用172.17.x.x作为子网地址,如果这个子网被占用,会在172.16~172.30这个范围内尝试创建子网。接口docker0是一个虚拟的以太网桥,用于连接容器和本地宿主网络。Docker宿主机的会有一系列名字以veth开头的接口,Docker每创建一个容器就会创建一组互联的网络接口。这组接口就像管道的两端,其中一端作为容器里的eth0接口,而另一端统一命名为以veth开头的名字,作为宿主机的一个端口。通过把每个veth*接口绑定到docker0网桥,Docker创建了一个虚拟子网,这个子网由宿主机和所有的Docker容器共享。

在运行的容器内部查看一下对望通信的路由信息,发现容器地址后的下一跳就是宿主网络上docker0的地址:

apt-get -yqq update && apt-get install -yqq traceroute
traceroute google.com

(3)Docker网络还有另外一个部分的配置才能允许建立连接:防火墙规则和NAT配置。这些配置允许Docker在宿主网络和容器间路由。在宿主机上查看IPTables NAT配置:

sudo iptables -t nat -L -n

容器默认是无法访问的。从宿主网络与容器通信时,必须明确指定打开的端口。

5、连接Redis

用docker inspect查看Redis容器的网络配置:

docker inspect redis

该命令展示了Docker容器的细节,包括配置信息和网络状况。可以使用-f标志只获取IP地址:

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

如果Docker宿主机运行在本地,可以直接使用Redis服务器的IP地址与Redis服务器通信。

这个方法有两个问题:第一,要在应用程序里对Redis容器的IP地址做硬编码;第二,如果重启容器,Docker可能会改变容器的IP地址。

6、让Docker容器互连

删除之前运行的redis容器和webapp容器:

docker rm -f webapp
docker rm -f redis

新建一个redis容器,与之前不同的是并不公开任何端口:

docker run -d --name redis ivan/redis

启动Web应用程序容器,并把它连接到新的Redis容器上去:

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

--link编制创建了两个容器间的父子连接。这个标志需要两个参数:一个是要连接的容器名字,另一个是连接后容器的别名。这个例子中,把新容器连接到redis容器,并使用db作为别名。连接让父容器有能力访问子容器,并把子容器的一些连接细节分享给父容器,这些细节有助于配置应用程序并使用这个连接。(新创建的容器时父容器,连接到的容器时子容器)

连接也能得到一些安全上的好处。容器的端口不需要对本地宿主机公开。

出于安全原因,可以强制Docker只允许有连接的容器之间相互通信,需要在启动Docker守护进程时加上--icc=false标志,关闭所有没有连接的容器间的通信。

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

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

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

  • /etc/hosts文件中;
  • 包含连接信息的环境变量中。

/etc/hosts文件中有连接指令创建的项,是redis容器的IP地址和该连接的对应的子容器别名。

如果重启了容器,容器的IP地址可能会发生变化。从Docker1.3开始,如果被连接的容器重启了,/etc/host文件中的IP地址会被新的IP地址更新。

在bash环境中运行env查看环境变量,可以看到一些以DB开头的环境变量。Docker在连接webapp和redis容器时,自动创建了以DB开头的环境变量。DB是创建连接时的子容器别名。这些环境变量主要包含以下信息:

  • 子容器的名字;
  • 子容器里运行服务使用的协议、IP和端口号;
  • 子容器里由Docker设置的环境变量的值。

这些环境变量会随容器不同而变化,取决于子容器是如何配置的。

7、测试容器连接情况

需要下载作者代码/code/5/sinatra/webapp_redis中的内容替换原sinatra/webapp的内容,然后后台启动webapp容器的/opt/webapp/bin/webapp。向web服务提交数据(假设映射到宿主机的端口是49160):

curl -l -H 'Accept:application/json' -d 'name=Foo&status=Bar' http://localhost:49160/json

用get方法测试提交情况:

cuil -i http://localhost:49160/json

5.3 Docker用于持续集成(Jenkins)

1、构建Jenkins和Docker容器

书中代码的Dockerfile无法构建成功,可能是由于版本升级造成的。去Github上下载更新过的代码把 5/jenkins/Dockerfile和dockerjenkins.sh放入构建环境的文件夹中。

Dockerfile中使用了VOLUME指令。从容器运行的宿主机上挂载一个卷。

构建成功后启动容器:

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

这里使用了一个新标志--privileged,它可以启动Docker的特权模式,允许我们以其宿主机具有的所有能力来运行容器,包括一些内核特性和设备访问。这是可以在Docker中运行Docker的必要能力。

让Docker在特权模式运行会有一些安全隐患。在这种模式下运行容器对Docker宿主机有root访问权限。

 

接下来是一些Jenkins的细节,笔记中略。

© 著作权归作者所有

一万
粉丝 30
博文 102
码字总数 173386
作品 0
朝阳
程序员
私信 提问
加载中

评论(1)

xiaozeng1
xiaozeng1
我根据你的这个解决方法进行后,都成功了但是后面docker logs webapp会显示/usr/bin/ruby: Permission denied -- /opt/webapp/bin/webapp (LoadError)这样的错误。这是怎么回事呢?还有一个就是这个容器创建完后立即停止,这是为什么呢?
Docker 学习笔记1. 安装

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/xundh/article/details/91846819 Docker 学习笔记1. 安装 一、简介 ...

谢厂节
06/14
0
0
Docker容器技术 学习笔记汇总

“如果你不出去走走,你就会以为这就是全世界。” 一直在嵌入式领域,这次遇到Docker算是长了见识。刚好公司同事们对这块了解地不多,那就把Docker容器技术的学习梳理成系列笔记,让其他伙伴少...

iotisan
2017/10/20
0
0
Docker入门教程 Part 2 容器操作

前言 本篇笔记是官方Get Started入门教程的第2步 容器操作,涉及Dockerfile镜像创建,容器中运行和停止镜像,仓库中分享和拉取镜像等,涵盖了Docker容器的常见操作,是特别经典的例子,值得好...

iotisan
2017/10/23
0
0
selenium 结合 docker 构建分布式测试环境

随着自动化测试越学越深,深深觉得有太多的东西需要总结。 1.记录下学习中遇到的坑,当做学习笔记。 2.有前人路过看到文章中比较落后的做法,请务必一定要指教。(因为是初学者视角,很多东西...

呐呐丶嘿
2018/12/12
230
0
Docker 学习笔记合集第一季 —— image container 基本操作

Docker 学习笔记合集第一季 —— image container 基本操作 Docker · chenhengjie123 · 于 1 年前发布 · 最后由 sanlengjingvv 于 1 年前回复 · 2262 次阅读 本帖已被设为精华帖! 学习 ...

小祁1124
2016/11/17
24
0

没有更多内容

加载失败,请刷新页面

加载更多

Eureka应用注册与集群数据同步源码解析

在之前的EurekaClient自动装配及启动流程解析一文中我们提到过,在构造DiscoveryClient类时,会把自身注册到服务端,本文就来分析一下这个注册流程 客户端发起注册 boolean register() t...

Java学习录
12分钟前
3
0
Java描述设计模式(15):责任链模式

本文源码:GitHub·点这里 || GitEE·点这里 一、生活场景描述 1、请假审批流程 公司常见的请假审批流程:请假天数 当 day<=3 天,项目经理审批当 3<day<=5 天,部门经理审批当 day>5 天...

知了一笑
22分钟前
3
0
总结:数组与链表

1、内存申请:数组在内存上是连续的空间;链表,内存地址上可以是不连续的。 2、查询速度:数组可以随机访问,链表必须顺序访问,即从首个元素开始遍历,逐个查找,所以数组查询很快。 3、写入...

浮躁的码农
31分钟前
3
0
HashMap源码分析

read

V丶zxw
49分钟前
5
0
Python字符串或JSON字符串转字典dict、列表list

有3种方法 1、使用ast模块 >>> import ast>>> s = '["test",1]'>>> ast.literal_eval(s)['test',1]>>> s = '{"test":1}'>>> ast.literal_eval(s){'test': 1} 2、eval函数,这个......

编程老陆
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部