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

介绍

连续集成(CI)是指开发人员尽可能频繁地集成代码的做法,并且每个委托在被自动构建测试之前和之后被合并到共享存储库中。

CI可加速您的开发过程,并最大限度地减少生产中关键问题的风险,但设置并非微不足道;自动构建在不同的环境中运行,在那里安装 运行时间依赖性 和配置 ** 外部服务** 可能不同于您的本地和开发环境。

Docker是一个集装箱化平台,旨在简化环境标准化问题,从而使应用程序的部署也可得到标准化(https://andsky.com/tech/tutorials/the-docker-ecosystem-an-overview-of-containerization)。

本教程使用 Docker Compose 来演示 CI 工作流程的自动化。

我们将创建一个 Dockerized Hello world 类型的 Python 应用程序和一个 Bash 测试脚本. 该 Python 应用程序将需要两个容器来运行:一个用于应用程序本身,一个用于存储的 Redis 容器作为应用程序的依赖。

然后,测试脚本将在其自己的容器中被Dockerized,整个测试环境将移动到一个 docker-compose.test.yml 文件,以便我们可以确保我们在新鲜和统一的应用环境中运行每个测试执行。

这种方法表明,您可以为您的应用程序构建一个相同的,新鲜的测试环境,包括其依赖性,每次测试它。

因此,我们自动化了 CI 工作流程,独立于正在测试的应用程序和其基础设施。

前提条件

在你开始之前,你需要:

  • 具有 sudo 特权的 Ubuntu 16.04 服务器 非 root 用户。 使用 Ubuntu 16.04 的初始服务器设置(https://andsky.com/tech/tutorials/initial-server-setup-with-ubuntu-16-04)解释了如何设置此设置。 * ** Docker ,安装在 Ubuntu 16.04 上如何安装和使用 Docker 的步骤 1 和 2 后。 * ** Docker Compose** ,安装在 Ubuntu 16.04 上如何安装 Docker Compose 的步骤 1 后。

第1步:创建Hello World Python应用程序

在此步骤中,我们将创建一个简单的Python应用程序,作为您可以使用此设置测试的应用程序类型的示例。

通过执行我们的应用程序创建新目录:

1cd ~
2mkdir hello_world
3cd hello_world

nano 编辑一个新的文件 app.py:

1nano app.py

添加以下内容:

 1[label app.py]
 2from flask import Flask
 3from redis import Redis
 4
 5app = Flask(__name__)
 6redis = Redis(host="redis")
 7
 8@app.route("/")
 9def hello():
10    visits = redis.incr('counter')
11    html = "<h3>Hello World!</h3>" \
12           "<b>Visits:</b> {visits}" \
13           "<br/>"
14    return html.format(visits=visits)
15
16if __name__ == "__main__":
17    app.run(host="0.0.0.0", port=80)

完成后,保存并退出文件。

「app.py」是一個基於 Flask的網頁應用程式,可以連接到 Redis 資料服務. 行 visits = redis.incr('counter') 會增加訪問次數,並且在 Redis 中保持這個值。

我们的应用程序有两个依赖,‘Flask’和‘Redis’,你可以在前两个行中看到。

打开新文件:

1nano requirements.txt

添加内容:

1[label requirements.txt]
2Flask
3Redis

当你完成时,保存并退出文件. 现在我们已经定义了我们的要求,我们将在docker-compose.yml中放置,我们已经准备好迈出下一步。

步骤 2 — Dockerize Hello World 应用程序

Docker 使用一个名为Dockerfile的文件来表示构建特定应用程序的 Docker 图像所需的步骤。

1nano Dockerfile

添加以下内容:

 1[label Dockerfile]
 2FROM python:2.7
 3
 4WORKDIR /app
 5
 6ADD requirements.txt /app/requirements.txt
 7RUN pip install -r requirements.txt
 8
 9ADD app.py /app/app.py
10
11EXPOSE 80
12
13CMD ["python", "app.py"]

让我们来分析每个字节的含义:

*FROM python:2.7':表示我们的"Hello World"应用图像由官方‘python:2.7'所建. Docker 图像 *WORKDIR/app':将多克图像内的工作目录设置为`/app'

  • ADD要求.txt/应用/要求.txt':在我们Docker图像中添加文件要求.txt'
  • RUN pip安装-r 要求.txt: 安装应用程序的 " pip " 依赖性
  • `ADD app.py/app.py':在多克图像中添加我们的应用程序源代码
  • `EXPOSE 80':表示我们的应用程序可以在80号港口(标准公共网络端口)到达。
  • `CMD ['python','app.py'] : 启动我们应用程序的命令

保存和退出文件. 此 Dockerfile 文件包含构建我们的 "Hello World" 应用程序的主要组件所需的所有信息。

依赖性

现在我们进入了更复杂的例子部分,我们的应用程序需要Redis作为外部服务,这是一种依赖的类型,在传统的Linux环境中每次都很难以相同的方式设置,但通过Docker Compose,我们可以每次以重复的方式设置它。

让我们创建一个docker-compose.yml文件,开始使用Docker Compose。

编辑新文件:

1nano docker-compose.yml

添加以下内容:

 1[label docker-compose.yml]
 2web:
 3  build: .
 4  dockerfile: Dockerfile
 5  links:
 6    - redis
 7  ports:
 8    - "80:80"
 9redis:
10  image: redis

此 Docker Compose 文件说明如何在两个 Docker 容器中本地旋转Hello World应用程序。

它定义了两个容器,Webredis

*Web使用当前的构建背景目录,并从我们刚刚创建的Dockerfile文件中构建我们的Python应用程序. 这是我们为我们的Python应用程序创建的本地Docker图像. 它定义了一个链接到redis容器,以便访问redis容器IP。

完成后,保存并退出文件。

第3步:部署Hello World应用程序

在此步骤中,我们将部署应用程序,到最后它将通过互联网访问。 对于您的部署工作流程,您可以认为这是一个开发,舞台或生产环境,因为您可以以相同的方式部署应用程序多次。

docker-compose.ymlDockerfile 文件允许您通过执行以下操作来自动部署本地环境:

1docker-compose -f ~/hello_world/docker-compose.yml build
2docker-compose -f ~/hello_world/docker-compose.yml up -d

第一行将我们的本地应用程序图像从Dockerfile文件中构建出来,第二行将webredis容器运行在daemon模式(-d)中,如docker-compose.yml文件中所述。

检查应用程序容器是否已通过执行创建:

1docker ps

这应该显示两个运行容器,名为helloworld_web_1helloworld_redis_1

我们可以通过执行helloworld_web_1容器的IP:

1WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
2echo $WEB_APP_IP

检查网页应用程序是否返回正确的消息:

1curl http://${WEB_APP_IP}:80

这应该返回一些像:

1[label Output]
2<h3>Hello World!</h3><b>Visits:</b> 2<br/>

你也可以从你的浏览器访问你的Ubuntu服务器的公共IP地址访问的Hello World应用程序。

如何定制自己的应用程序

設定自己的應用程式的關鍵是將應用程式放入自己的 Docker 容器,並從自己的容器運行每個依賴。

有关如何在多个容器上运行应用程序的另一个示例,请阅读有关运行 WordPress 和 phpMyAdmin with Docker Compose的这篇文章。

步骤4 - 创建测试脚本

现在我们将为我们的Python应用程序创建一个测试脚本,这将是一个简单的脚本,检查应用程序的HTTP输出。

编辑新文件:

1nano test.sh

添加以下内容:

1[label test.sh]
2sleep 5
3if curl web | grep -q '<b>Visits:</b> '; then
4  echo "Tests passed!"
5  exit 0
6else
7  echo "Tests failed!"
8  exit 1
9fi

test.sh测试了我们的Hello World应用程序的基本网络连接性,它使用cURL来检索访问次数和报告是否通过了测试。

步骤5:创建测试环境

为了测试我们的应用程序,我们需要部署一个测试环境,并希望确保它与我们在 Step 3 中创建的实时应用程序环境相同。

首先,我们需要通过创建一个新的Dockerfile文件来编辑我们的测试脚本。

1nano Dockerfile.test

添加以下内容:

 1[label Dockerfile.test]
 2FROM ubuntu:xenial
 3
 4RUN apt-get update && apt-get install -yq curl && apt-get clean
 5
 6WORKDIR /app
 7
 8ADD test.sh /app/test.sh
 9
10CMD ["bash", "test.sh"]

Dockerfile.test扩展了官方的ubuntu:xenial图像,以安装curl依赖性,将tests.sh添加到图像文件系统中,并表示执行测试脚本的CMD命令。

一旦我们的测试被 Dockerized,他们可以以可复制和无神论的方式执行。

下一步是将我们的测试容器链接到我们的Hello World应用程序。这里是Docker Compose再次来救援的地方。

1nano docker-compose.test.yml

添加以下内容:

 1[label docker-compose.test.yml]
 2sut:
 3  build: .
 4  dockerfile: Dockerfile.test
 5  links:
 6    - web
 7web:
 8  build: .
 9  dockerfile: Dockerfile
10  links:
11    - redis
12redis:
13  image: redis

Docker Compose文件的后半部分以与先前的 " docker-compose.yml " 文件相同的方式部署主要 " web " 应用程序及其 " redis " 依赖性。 这是档案中具体规定了 " 网络 " 和 " redis " 容器的部分。 唯一的区别是 " web " 集装箱不再暴露出第80号港口,因此在测试期间该应用程序不会通过公共互联网提供。 所以,你可以看到,我们正在构建 应用程序和它的依赖性 与他们在现场部署的方式完全一样.

docker-compose.test.yml文件还定义了负责执行我们的集成测试的sut容器(在测试中命名为 _system)。 sut容器将当前目录指定为我们的build目录,并指定我们的Dockerfile.test文件。它链接到web容器,因此应用容器的IP地址可以访问我们的test.sh脚本。

** 如何定制您自己的应用程序**

请注意,docker-compose.test.yml可能包括数十个外部服务和多个测试容器。

如果您在您的应用程序上有更多的测试,您可以为它们创建额外的 Dockerfiles,类似于上文所示的Dockerfile.test文件。

然后,您可以在docker-compose.test.yml文件中添加sut容器下的额外容器,引用额外的 Dockerfiles。

第6步:测试Hello World应用程序

最后,将Docker的想法从本地环境扩展到测试环境,我们有一个自动的方式来使用Docker来测试我们的应用程序,通过执行:

1docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build

此命令构建了docker-compose.test.yml所需的本地图像,请注意,我们使用-f指向docker-compose.test.yml-p指向特定项目名称。

现在,通过执行以下操作来刷新您新鲜的测试环境:

1docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
1[secondary_label Output]
2Creating ci_redis_1
3Creating ci_web_1
4Creating ci_sut_1

检查sut容器的输出,执行:

1docker logs -f ci_sut_1
1[label Output]
2  % Total    % Received % Xferd Average Speed Time Time Time Current
3                                 Dload Upload Total Spent Left Speed
4100 42 100 42 0 0 3902 0 --:--:-- --:--:-- --:--:--  4200
5Tests passed!

最后,检查sut容器的输出代码,以验证您的测试是否通过:

1docker wait ci_sut_1
1[label Output]
20

執行此命令後,如果測試通過,「$?」的值將為「0」。

请注意,其他 CI 工具可以克隆我们的代码存储库并执行这些几个命令,以验证测试是否通过您的应用程序的最新部分,而不必担心运行时间依赖或外部服务配置。

我们在与我们的生产环境相同的新建环境中成功进行了测试。

结论

借助Docker和Docker Compose,我们能够自动构建应用程序(‘Dockerfile’),部署本地环境(‘docker-compose.yml’),构建测试图像(‘Dockerfile.test’),以及执行(集成)测试(‘docker-compose.test.yml’)的任何应用程序。

特别是使用docker-compose.test.yml 文件进行测试的优点是测试过程是:

  • 自动化 :工具执行docker-compose.test.yml的方式是独立于正在测试的应用程序 * ** 轻量化** :可以在单个主机上部署数百个外部服务,模拟复杂的(集成)测试环境 * ** Agnostic** :避免CI提供商锁定,您的测试可以在任何环境中运行

基础设施和支持 Docker 的任何操作系统

  • 不变 :通过您的本地机器的测试将通过您的CI工具

本教程展示了如何测试一个简单的Hello World应用程序的例子。

现在是时候使用自己的应用程序文件,Dockerize自己的应用程序测试脚本,并创建自己的docker-compose.test.yml,以在新鲜和不变的环境中测试您的应用程序。

Published At
Categories with 技术
comments powered by Disqus