如何使用Docker和Docker配置持续集成测试环境在Ubuntu 16.04上构建

持续集成(CI)是指开发人员尽可能经常地集成代码,每个提交在通过自动化构建合并到共享存储库之前和之后进行测试。 CI ...

介绍

持续集成 (CI)指的是开发商经常集成代码尽可能和每个提交之前,并自动构建被合并到一个共享资源库后,测试的做法。 CI加速您的开发过程,并尽量减少生产中关键问题的风险,但它不是微不足道的设置;自动构建在不同的环境下运行时依赖的安装和对外服务的配置可能会比在本地和开发环境上不同的运行。 Docker是一个容器的平台,旨在简化环境标准化的问题,使应用程序的部署,也可以标准化( 了解更多Docker )。 对于开发人员,Docker允许您通过在本地容器中运行应用程序组件来在本地机器上模拟生产环境。 这些容器是很容易实现自动化使用Docker Compose独立应用程序和底层操作系统。 本教程使用Docker Compose来演示CI工作流的自动化。 我们将创建一个Dockerized“Hello world”类型的Python应用程序和一个Bash测试脚本。 Python应用程序将需要运行两个容器:一个用于应用程序本身,一个用于存储的Redis容器,作为应用程序的依赖关系。 然后,测试脚本会在自己的容器中Dockerized和整个测试环境移动到一个Docker,compose.test.yml文件,这样我们可以确保我们正在运行在一个新的,统一的应用环境中的每个测试执行。 这种方法显示了如何为您的应用程序构建一个相同的,全新的测试环境,包括其依赖性,每次测试它。 因此,我们自动化CI工作流独立于被测试的应用程序和底层基础设施。

先决条件

在开始之前,您需要:

第1步 - 创建“Hello World”Python应用程序

在这一步中,我们将创建一个简单的Python应用程序作为您可以使用此设置测试的应用程序类型的示例。 通过执行以下操作为我们的应用程序创建一个新目录:
cd ~
mkdir hello_world
cd hello_world
编辑新文件app.py 纳米
nano app.py
添加以下内容:
app.py
from flask import Flask
from redis import Redis




app = Flask(__name__)
redis = Redis(host="redis")




@app.route("/")
def hello():
    visits = redis.incr('counter')
    html = "<h3>Hello World!</h3>" \
           "<b>Visits:</b> {visits}" \
           "<br/>"
    return html.format(visits=visits)




if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80)
完成后,保存并退出文件。 app.py是基于web应用Flask连接到一个Redis的数据服务。 该生产线visits = redis.incr('counter')增加访问次数和坚持Redis的这个值。 最后, Hello World的HTML被返回与访问次数的消息。 我们的应用有两个依赖, FlaskRedis ,你可以在头两行看到的。这些依赖关系必须在我们执行应用程序之前定义。 打开一个新文件:
nano requirements.txt
添加内容:
requirements.txt
Flask
Redis
完成后,保存并退出文件。现在我们已经定义了我们的要求,我们将在以后的落实到位docker-compose.yml ,我们已经准备好进行下一步。

第2步 - Dockerize“Hello World”应用程序

Docker使用一个名为Dockerfile指示所需的步骤来建立一个Docker形象对于一个给定的应用程序。编辑新文件:
nano Dockerfile
添加以下内容:
Dockerfile
FROM python:2.7


WORKDIR /app


ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt


ADD app.py /app/app.py


EXPOSE 80


CMD ["python", "app.py"]
让我们分析每一行的含义:
  • FROM python:2.7 :表示我们的“Hello World”应用图像从官方内置python:2.7Docker图像
  • WORKDIR /app :设置工作目录Docker图像的内部/app
  • ADD requirements.txt /app/requirements.txt :将文件添加requirements.txt我们的Docker形象
  • RUN pip install -r requirements.txt :安装应用程序的pip依赖
  • ADD app.py /app/app.py :增加了我们的应用程序源代码,以Docker图像
  • EXPOSE 80 :表示我们的应用程序可以在端口80(标准的公共网络接口)到达
  • CMD ["python", "app.py"] :命令启动我们的应用程序
保存并退出文件。这Dockerfile文件要打造我们的“Hello World”应用的主要成分所需的所有信息。

依赖

现在我们来到更复杂的部分的例子。我们的应用程序需要Redis作为外部服务。这是一种依赖类型,在传统的Linux环境中可能难以以相同的方式设置,但是使用Docker Compose,我们可以每次都以可重复的方式设置它。 让我们创建一个docker-compose.yml文件,开始使用Docker Compose。 编辑新文件:
nano docker-compose.yml
添加以下内容:
docker-compose.yml
web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
  ports:
    - "80:80"
redis:
  image: redis
这个Docker Compose文件指示如何在两个Docker容器中本地调动“Hello World”应用程序。 它定义了两个容器, webredis
  • web使用为当前目录build方面,并建立从我们的Python应用程序Dockerfile我们刚刚创建的文件。 这是我们为我们的Python应用程序所做的本地Docker镜像。 它定义链接到redis以存取容器redis容器的IP。它还使端口80可以使用您的Ubuntu服务器的公共IP从Internet上公开访问
  • redis是从一个标准的公共Docker的形象,命名为执行redis
完成后,保存并退出文件。

第3步 - 部署“Hello World”应用程序

在这一步中,我们将部署应用程序,最终可以通过Internet访问。出于部署工作流程的目的,您可以将其视为开发环境,临时环境或生产环境,因为您可以多次部署该应用程序。 该docker-compose.ymlDockerfile文件允许您通过执行自动化当地环境的部署:
docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d
第一行构建我们从本地应用程序镜像Dockerfile文件。 第二行运行webredis守护进程方式容器( -d ),在指定的docker-compose.yml文件。 通过执行以下操作来检查应用程序容器是否已创建:
docker ps
这应该显示两个运行容器,命名helloworld_web_1helloworld_redis_1 。 让我们检查应用程序是否已启动。我们可以得到的IP helloworld_web_1执行容器:
WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
echo $WEB_APP_IP
检查Web应用程序是否返回正确的消息:
curl http://${WEB_APP_IP}:80
这应该返回类似:
输出
<h3>Hello World!</h3><b>Visits:</b> 2<br/>
每次您访问此端点时,访问次数都会增加。您还可以通过访问您的Ubuntu服务器的公共IP地址从浏览器访问“Hello World”应用程序。

如何为自己的应用程序自定义

设置自己的应用程序的关键是将您的应用程序放在自己的Docker容器中,并从自己的容器运行每个依赖关系。然后,您可以使用Docker Compose定义容器之间的关系,如示例中所示。Docker Compose的这包括在更详细Docker Compose文章 。 有关如何获得跨多个容器中运行的应用程序的另一个例子,读这篇文章有关运行WordPress和phpMyAdmin的与Docker Compose

第4步 - 创建测试脚本

现在我们将为我们的Python应用程序创建一个测试脚本。这将是一个简单的脚本,检查应用程序的HTTP输出。该脚本是您可能希望作为持续集成部署过程的一部分运行的测试类型的示例。 编辑新文件:
nano test.sh
添加以下内容:
test.sh
sleep 5
if curl web | grep -q '<b>Visits:</b> '; then
  echo "Tests passed!"
  exit 0
else
  echo "Tests failed!"
  exit 1
fi
test.sh测试我们的“Hello World”应用的基本的网络连接。它使用cURL来检索访问次数和报告关于测试是否通过。

第5步 - 创建测试环境

为了测试我们的应用程序,我们需要部署一个测试环境。而且,我们要确保它是相同的,我们在第3步中创建实时应用环境。 首先,我们需要通过创建一个新的Dockerfile文件来Dockerize我们的测试脚本。编辑新文件:
nano Dockerfile.test
添加以下内容:
Dockerfile.test
FROM ubuntu:xenial


RUN apt-get update && apt-get install -yq curl && apt-get clean


WORKDIR /app


ADD test.sh /app/test.sh


CMD ["bash", "test.sh"]
Dockerfile.test扩展了官方ubuntu:xenial安装映像curl的依赖,增加tests.sh到图像文件系统,并指示CMD的执行与Bash测试脚本命令。 一旦我们的测试是Dockerized,它们可以以可复制和不可知的方式执行。 下一步是将我们的测试容器链接到我们的“Hello World”应用程序。这里是Docker Compose再次来到救援的地方。编辑新文件:
nano docker-compose.test.yml
添加以下内容:
docker-compose.test.yml
sut:
  build: .
  dockerfile: Dockerfile.test
  links:
    - web
web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
redis:
  image: redis
该Docker撰写文件的后半部分部署主web应用程序及其redis以同样的方式与先前的依赖性docker-compose.yml文件。 这是指定的文件的一部分, webredis容器。 唯一的区别是, web容器不再暴露端口80,因此在测试期间的应用程序将不提供在公共互联网。因此,您可以看到,我们正在构建应用程序及其依赖关系,与在实时部署中的方式完全相同。 该docker-compose.test.yml文件还定义了一个sut容器( 下测试命名为系统 )负责执行我们的集成测试。 该sut容器指定当前目录作为我们build目录,并指定我们Dockerfile.test文件。 它链接到web容器,这样应用程序容器的IP地址是我们的访问test.sh脚本。 如何自定义您自己的应用程序 需要注意的是docker-compose.test.yml可能包括数十家外部服务和多个测试容器。 Docker将能够在单个主机上运行所有这些依赖关系,因为每个容器共享基础操作系统。 如果您有更多的测试您的应用程序运行,可以为他们创造更多的Dockerfiles,类似Dockerfile.test上述文件。 然后,您可以添加额外的容器下面sut在容器docker-compose.test.yml文件,引用额外Dockerfiles。

第6步 - 测试“Hello World”应用程序

最后,将Docker想法从本地环境扩展到测试环境,我们有一种使用Docker测试我们的应用程序的自动化方法:
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build
此命令通过建立所需要的本地图片docker-compose.test.yml 。 请注意,我们使用-f指向docker-compose.test.yml-p表示一个特定的项目名称。 现在,通过执行以下命令来启动您的新测试环境:
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
Creating ci_redis_1
Creating ci_web_1
Creating ci_sut_1
检查的输出sut执行容器:
docker logs -f ci_sut_1
输出
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    42  100    42    0     0   3902      0 --:--:-- --:--:-- --:--:--  4200
Tests passed!
最后,检查的退出代码sut容器来验证,如果你的测试都通过:
docker wait ci_sut_1
输出
0
该命令执行后,价值$?将是0 ,如果测试通过。否则,我们的应用程序测试失败。 注意,其他CI工具可以克隆我们的代码存储库,并执行这些几个命令来验证测试是否通过应用程序的最新位,而不必担心运行时依赖性或外部服务配置。 而已!我们已经在与我们的生产环境相同的新建环境中成功运行我们的测试。

结论

由于Docker和Docker Compose,我们已经能够自动生成应用程序( Dockerfile ),部署本地环境( docker-compose.yml ),建立一个测试图像( Dockerfile.test ),和执行(集成)测试( docker-compose.test.yml )的任何应用程序。 特别是,使用的优点docker-compose.test.yml用于测试文件是测试过程是:
  • 可自动化 :工具执行的方式docker-compose.test.yml独立测试的应用程序
  • 重量轻 :数以百计的外部服务可以部署一台主机上,模拟复杂(集成)测试环境
  • 不可知论 :避免CI供应商锁定,和您的测试可以在任何基础设施和支持Docker任何操作系统上运行
  • 一成不变的 :通过你的本地计算机上的测试将通过在你的CI工具
本教程显示了如何测试一个简单的“Hello World”应用程序的示例。 现在是时候使用自己的应用程序文件,Dockerize自己的应用程序的测试脚本,并创建自己的docker-compose.test.yml在一个新的和不可改变的环境中测试您的应用程序。