介绍
持续集成 (CI)指的是开发商经常 集成代码尽可能和每个提交之前,并 自动构建被合并到一个共享资源库后,测试的做法。 CI加速您的开发过程,并尽量减少生产中关键问题的风险,但它不是微不足道的设置;自动构建在不同的环境下 运行时依赖的安装和 对外服务的配置可能会比在本地和开发环境上不同的运行。 Docker是一个容器的平台,旨在简化环境标准化的问题,使应用程序的部署,也可以标准化( 了解更多Docker )。 对于开发人员,Docker允许您通过在本地容器中运行应用程序组件来在本地机器上模拟生产环境。 这些容器是很容易实现自动化使用 Docker Compose独立应用程序和底层操作系统。 本教程使用Docker Compose来演示CI工作流的自动化。 我们将创建一个Dockerized“Hello world”类型的Python应用程序和一个Bash测试脚本。 Python应用程序将需要运行两个容器:一个用于应用程序本身,一个用于存储的Redis容器,作为应用程序的依赖关系。 然后,测试脚本会在自己的容器中Dockerized和整个测试环境移动到一个 Docker,compose.test.yml文件,这样我们可以确保我们正在运行在一个新的,统一的应用环境中的每个测试执行。 这种方法显示了如何为您的应用程序构建一个相同的,全新的测试环境,包括其依赖性,每次测试它。 因此,我们自动化CI工作流独立于被测试的应用程序和底层基础设施。先决条件
在开始之前,您需要:- 一个Ubuntu 16.04服务器使用sudo特权的非root用户 。 在使用Ubuntu 16.04初始服务器设置介绍了如何设置起来。
- Docker ,安装以下第1步和2 如何安装和使用Docker在Ubuntu 16.04 。
- Docker撰写 ,下面安装第1步 如何在Ubuntu 16.04安装Docker Compose
第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被返回与访问次数的消息。 我们的应用有两个依赖,
Flask
和
Redis
,你可以在头两行看到的。这些依赖关系必须在我们执行应用程序之前定义。 打开一个新文件:
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.7
Docker图像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”应用程序。 它定义了两个容器,
web
和
redis
。
web
使用为当前目录build
方面,并建立从我们的Python应用程序Dockerfile
我们刚刚创建的文件。 这是我们为我们的Python应用程序所做的本地Docker镜像。 它定义链接到redis
以存取容器redis
容器的IP。它还使端口80可以使用您的Ubuntu服务器的公共IP从Internet上公开访问redis
是从一个标准的公共Docker的形象,命名为执行redis
。
第3步 - 部署“Hello World”应用程序
在这一步中,我们将部署应用程序,最终可以通过Internet访问。出于部署工作流程的目的,您可以将其视为开发环境,临时环境或生产环境,因为您可以多次部署该应用程序。 该docker-compose.yml
和
Dockerfile
文件允许您通过执行自动化当地环境的部署:
docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d
第一行构建我们从本地应用程序镜像
Dockerfile
文件。 第二行运行
web
和
redis
守护进程方式容器(
-d
),在指定的
docker-compose.yml
文件。 通过执行以下操作来检查应用程序容器是否已创建:
docker ps
这应该显示两个运行容器,命名
helloworld_web_1
和
helloworld_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
文件。 这是指定的文件的一部分,
web
和
redis
容器。 唯一的区别是,
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
OutputCreating 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工具
docker-compose.test.yml
在一个新的和不可改变的环境中测试您的应用程序。