一个文章从 Docker
介绍
连续集成(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 工作流程,独立于正在测试的应用程序和其基础设施。
要求
在你开始之前,你需要:
- 一个 Ubuntu 14.04 服务器 * 一个具有 sudo 特权的非根用户. 您可以遵循此 教程来设置此设置 * 一些熟悉 Docker和 Docker Compose;链接是参考的,因为我们将在本教程中安装该软件 * ** 测试,测试和更多(自动)测试!** 您应该已经相信测试和应用 CI 到您的开发工作流的优势
第1步:安装Docker
如果 Docker 尚未在您的服务器上使用,最简单的方法是下载并执行官方 Docker 安装脚本,该脚本提示您使用 sudo 密码(有关安装 Docker 的更多信息请参阅此处):
1wget -qO- https://get.docker.com/ | sh
为了更轻松地使用 Docker,请使用以下命令将用户添加到 docker 组:
1sudo usermod -aG docker $(whoami)
退出,然后登录您的服务器,为您的用户激活 docker 组。
第2步:安装Docker Compose
Docker Compose 是一个开源工具,用于使用声明方法定义和运行多容器应用程序. 要安装 Docker Compose,请执行以下命令:
1sudo apt-get update
2sudo apt-get -y install python-pip
3sudo pip install docker-compose
確認「docker-compose」是否正確安裝,執行:
1docker-compose --version
你应该看到这样的东西:
1[label Output]
2docker-compose version 1.6.2, build 4d72027
这应该告诉您所安装的docker-compose
版本。
步骤 3 — 创建 Python 应用程序Hello World
在此步骤中,我们将创建一个简单的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
步骤 4 — 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
应用程序。
它定义了两个容器,Web
和redis
。
*「Web」使用「build」背景的当前文件夹,并从我们刚刚创建的「Dockerfile」文件中构建我们的Python应用程序。这是我们仅为我们的Python应用程序创建的本地Docker图像。它定义了向「redis」容器的链接,以便访问「redis」容器IP。
第5步:部署Hello World
应用程序
在此步骤中,我们将部署应用程序,到最后它将通过互联网访问。 对于您的部署工作流程,您可以认为这是一个开发,舞台或生产环境,因为您可以以相同的方式部署应用程序多次。
docker-compose.yml
和 Dockerfile
文件允许您通过执行以下操作来自动部署本地环境:
1docker-compose -f ~/hello_world/docker-compose.yml build
2docker-compose -f ~/hello_world/docker-compose.yml up -d
第一行将我们的本地应用程序图像从Dockerfile
文件中构建出来,第二行将web
和redis
容器运行在daemon
模式(-d
)中,如docker-compose.yml
文件中所述。
检查应用程序容器是否已通过执行创建:
1docker ps
这应该显示两个运行容器,名为helloworld_web_1
和helloworld_redis_1
。
我们可以通过执行helloworld_web_1
容器的IP:
1WEB_APP_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' helloworld_web_1)
2echo $WEB_APP_IP
检查网页应用程序是否返回正确的消息:
1curl http://${WEB_APP_IP}:80
这应该返回一些像:
1[label Output]
2<h3>Hello World!</h3><b>Visits:</b> 1<br/>
你也可以从你的浏览器访问你的Ubuntu服务器的公共IP地址访问的Hello World
应用程序。
** 如何定制您自己的应用程序**
設定自己的應用程式的關鍵是將應用程式放入自己的 Docker 容器,並從自己的容器運行每個依賴。
有关如何在多个容器上运行应用程序的另一个示例,请阅读有关运行 WordPress 和 phpMyAdmin with Docker Compose的这篇文章。
步骤6 - 创建测试脚本
现在我们将为我们的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来检索访问次数和报告是否通过了测试。
第7步:创建测试环境
为了测试我们的应用程序,我们需要部署一个测试环境,并希望确保它与我们在 Step 5 中创建的实时应用程序环境相同。
首先,我们需要通过创建一个新的Dockerfile文件来编辑我们的测试脚本。
1nano Dockerfile.test
添加以下内容:
1[label Dockerfile.test]
2FROM ubuntu:trusty
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:trusty
图像来安装curl
依赖性,将tests.sh
添加到图像文件系统中,并指示了CMD
命令,用Bash执行测试脚本。
一旦我们的测试被 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。
第8步:测试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
检查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
,以在新鲜和不变的环境中测试您的应用程序。