持续集成(CI)是指开发人员尽可能经常集成代码并在每个提交在通过自动构建合并到共享存储库之前和之后进行测试的实践。
CI加快了您的开发过程,并最大限度地降低了生产中关键问题的风险,但设置并非易事; 自动构建在不同的环境中运行,其中运行时依赖项的安装和外部服务的配置可能与本地和开发环境中的不同。
Docker是一个容器化平台,旨在简化环境标准化问题,因此应用程序的部署也可以标准化。对于开发人员,Docker允许您通过在本地容器中运行应用程序组件来模拟本地计算机上的生产环境。使用Docker Compose可以轻松自动化这些容器,而与应用程序和底层操作系统无关。
您也可以使用腾讯云容器服务。腾讯云容器服务基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务。腾讯云容器服务完全兼容原生 kubernetes API ,扩展了腾讯云的 CBS、CLB 等 kubernetes 插件,为容器化的应用提供高效部署、资源调度、服务发现和动态伸缩等一系列完整功能,解决用户开发、测试及运维过程的环境一致性问题,提高了大规模容器集群管理的便捷性,帮助用户降低成本,提高效率。容器服务提供免费使用,涉及的其他云产品另外单独计费。
本教程使用Docker Compose演示CI工作流的自动化。
我们将创建一个Dockerized“Hello world”类型的Python应用程序和一个Bash测试脚本。Python应用程序需要运行两个容器:一个用于应用程序本身,一个用于存储的Redis容器,作为应用程序的依赖项。
然后,测试脚本将在其自己的容器中进行Docker化,整个测试环境转移到docker-compose.test.yml文件,这样我们就可以确保在新的统一应用程序环境中运行每个测试执行。
此方法显示了每次测试时如何为应用程序构建相同的,全新的测试环境,包括其依赖关系。
因此,我们独立于测试中的应用程序和底层基础架构自动化CI工作流。
在开始之前,您将需要:
在这一步中,我们将创建一个简单的Python应用程序,作为您可以使用此设置测试的应用程序类型的示例。
通过执行以下命令为我们的应用程序创建新目
cd ~
mkdir hello_world
cd hello_world使用nano编辑新文件app.py:
nano 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是一个基于Flask的Web应用程序,它连接到Redis数据服务。visits = redis.incr('counter')行增加了访问次数并在Redis中保留了此值。最后,以HTML格式返回包含访问次数的消息Hello World。
我们的应用有两个依赖,Flask和Redis,你可以在头两行看到。必须先定义这些依赖项,然后才能执行应用程序。
打开一个新文件:
nano requirements.txt添加内容:
Flask
Redis完成后,保存并退出文件。现在我们已经定义了我们的要求,这个我们将在稍后的时间在docker-compose.yml内实施,我们已准备好进行下一步。
Docker使用一个调用的文件Dockerfile来指示为给定应用程序构建Docker镜像所需的步骤。编辑新文件:
nano 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镜像内的工作目录设置为 /appADD 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(标准公共Web端口)访问CMD ["python", "app.py"]:启动我们的应用程序的命令保存并退出该文件。此Dockerfile文件包含构建“Hello World”应用程序主要组件所需的所有信息。
现在我们来看一个更复杂的例子。我们的应用程序需要Redis作为外部服务。这种依赖类型在传统的Linux环境中每次都很难以相同的方式设置,但是使用Docker Compose我们每次都可以以可重复的方式进行设置。
让我们创建一个docker-compose.yml文件来开始使用Docker Compose。
编辑新文件:
nano docker-compose.yml添加以下内容:
web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
  ports:
    - "80:80"
redis:
  image: redis此Docker Compose文件指示如何在两个Docker容器中本地启动“Hello World”应用程序。
它定义了两个容器,web和redis。
web使用当前目录作为build的上下文,并从我们刚刚创建的文件Dockerfile中构建我们的Python应用程序。这是我们为Python应用程序制作的本地Docker镜像。它定义了一个到redis容器的链接,以便访问redis容器IP。它还使用您的Ubuntu服务器的公共IP从Internet公开访问端口80redis的标准公共Docker镜像执行redis。完成后,保存并退出文件。
在此步骤中,我们将部署应用程序,最后它将可通过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文件构建我们的本地应用程序映像。第二行以守护进程模式(-d)运行web和redis容器,如docker-compose.yml文件中所指定的那样。
通过执行以下命令检查是否已创建应用程序容器:
docker ps这应该显示两个运行容器,名为helloworld_web_1和helloworld_redis_1。
让我们检查应用程序是否已启动。我们可以通过执行以下命令获取容器helloworld_web_1的IP :
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定义容器之间的关系。
现在我们将为Python应用程序创建一个测试脚本。这将是一个检查应用程序HTTP输出的简单脚本。该脚本是您可能希望作为持续集成部署过程的一部分运行的测试类型的示例。
编辑新文件:
nano test.sh添加以下内容:
sleep 5
if curl web | grep -q '<b>Visits:</b> '; then
  echo "Tests passed!"
  exit 0
else
  echo "Tests failed!"
  exit 1
fitest.sh测试我们的“Hello World”应用程序的基本Web连接。它使用cURL来检索访问次数并报告测试是否通过。
为了测试我们的应用程序,我们需要部署一个测试环境。而且,我们希望确保它与我们在步骤3中创建的实时应用程序环境完全相同。
首先,我们需要通过创建一个新的Dockerfile文件来Docker化我们的测试脚本。编辑新文件:
nano 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到映像文件系统,并指示使用Bash执行测试脚本的命令CMD。
一旦我们的测试进行了Docker化,它们就可以以可复制和不可知的方式执行。
下一步是将我们的测试容器链接到我们的“Hello World”应用程序。这是Docker Compose再次拯救的地方。编辑新文件:
nano docker-compose.test.yml添加以下内容:
sut:
  build: .
  dockerfile: Dockerfile.test
  links:
    - web
web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
redis:
  image: redisDocker Compose文件的后半部分以与前一个文件docker-compose.yml相同的方式部署主web应用程序及其redis依赖项。这是指定web和redis容器的文件的一部分。唯一的区别是web容器不再暴露端口80,因此在测试期间应用程序将无法通过公共Internet访问。因此,您可以看到我们正在构建应用程序及其依赖项,与在实时部署中完全相同。
该docker-compose.test.yml文件还定义了一个sut容器(以测试中的系统命名 ),负责执行我们的集成测试。该sut容器指定当前目录为我们的build目录,并指定了Dockerfile.test文件。它链接到web容器,因此我们的test.sh脚本可以访问应用程序容器的IP地址。
如何自定义您自己的应用程序
请注意,docker-compose.test.yml可能包括许多外部服务和多个测试容器。Docker将能够在单个主机上运行所有这些依赖项,因为每个容器共享底层操作系统。
如果要在应用程序上运行更多测试,可以为它们创建其他Dockerfiles,类似于上面显示的文件Dockerfile.test。
然后,您可以在docker-compose.test.yml文件中的sut容器下添加其他容器,引用其他Dockerfiles。
最后,将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 -dOutput
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_10这个命令执行后,$?值将是0,如果测试通过。否则,我们的应用程序测试失败
请注意,其他CI工具可以克隆我们的代码存储库并执行这些命令来验证测试是否与应用程序的最新位一起传递,而不必担心运行时依赖性或外部服务配置。
至此,我们已经在与我们的生产环境相同的新建环境中成功运行了测试。
感谢Docker和Docker Compose,我们已经能够自动构建应用程序(Dockerfile),部署本地环境(docker-compose.yml),构建测试图像(Dockerfile.test),以及为任何应用程序执行(集成)测试(docker-compose.test.yml)。
特别是,使用docker-compose.test.yml文件进行测试的优点是测试过程是:
docker-compose.test.yml的方式独立于被测应用程序本教程展示了如何测试简单的“Hello World”应用程序的示例。
现在是时候使用您自己的应用程序文件,Dockerize您自己的应用程序测试脚本,并创建自己的docker-compose.test.yml以在新的和不可变的环境中测试您的应用程序。
想要了解更多关于配置持续集成测试环境的相关教程,请前往腾讯云+社区学习更多知识。
参考文献:《How To Automate Elixir-Phoenix Deployment with Distillery and edeliver on Ubuntu 16.04》
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。