大约一年前,我第一次接触Docker。 即将到来的这个新孩子承诺将减轻我们可怜的计算机安装所有工具,语言,依赖项和操作系统的负担。 孤立的运行环境出现在开发人员的计算机上。
当我的行动队友选择了一种更为保守的方法时,我开始非常高兴地使用Docker。 尽管许多人将Docker描述为开发人员为开发而编写的工具,但我们的行业发现了使用映像和容器的新方法。 我们的应用程序和服务的图像已成为Kubernetes,Docker Swarm或Marathon等工具的部署单元。
但是如何创建这些图像?
设置场景
从开发人员的角度来看,任何应用程序都可以通过其代码体现出来,但是要使该代码找到进入生产环境的路途还很长。 我想向您展示如何通过Docker和持续部署管道更轻松地完成此过程。
首先,我们需要一个带有HTTP API的小型应用程序,部署后可以调用它。 假设我们使用Gradle构建应用程序,并将TeamCity构建为持续集成服务器。
我们需要在每个TeamCity构建代理上安装Docker 。 我们还将使用这台机器来运行我们的应用程序。 在真实的项目中,我们不会在所有机器上都安装TeamCity代理。 相反,我们应该使用Kubernetes之类的工具来处理应用程序的分发。
建立
作为我们持续部署流程的第一步,在我们甚至未考虑Docker映像之前,我们需要构建应用程序。 在这一步中,我们将下载源代码,运行所有测试并生成包含启动和运行我们的应用程序所需的所有元素的工件 。
此构建配置与没有Docker的持续部署管道中的步骤没有太大不同。 除了通用参数外,我们还必须定义工件 ,这些工件将在每次构建运行后生成。 我们将使用它们作为后续步骤的基础。
在TeamCity中,我们通过定义工作目录(为每次配置运行创建的文件)的路径来定义工件 。 工作目录是从版本控制系统下载的文件和在执行构建步骤期间生成的文件的组合。 工作目录元素在版本控制设置和构建步骤中定义。
我们需要两个工件。 第一个是Dockerfile
。 我们已经在源代码中准备了该文件,并将其存储在docker
目录中。 下一个文件是tar文件,它将由我们的Gradle构建生成。 它包含执行我们的代码和所有必需库的脚本。
现在,我们准备指导我们的构建配置如何下载我们的源代码。 我们使用GitHub作为代码存储库,因此我们只需要选择Git作为版本控制系统的类型,并提供应用程序的URL。
使用工作目录中的源代码,我们可以描述如何处理它。 如前所述,我们运行所有测试并生成包含启动脚本和库的工件 。
TeamCity具有针对不同构建工具的预定义运行器。 在这种情况下,我们将使用Gradle Runner进行测试和构建 。
现在我们可以运行我们的构建,因此,我们应该在“工件”选项卡中看到工件 。
释放
不再需要我们的代码。 我们拥有构建docker映像所需的一切。 现在,我们必须创建图像并使用正确的版本发布它。 为了简化我们的示例,我们将使用当前的内部版本号来定义映像版本。 接下来,我们将使用此版本生成文件。 该文件使我们可以将有关版本的信息传递到后续步骤。
让我们仔细看看Dockerfile
。 我们使用ADD
命令将simple_application.tar
的内容(包含所有必需的库和脚本)复制到映像中。 此命令将自动解压缩映像中的所有文件。 接下来,我们公开HTTP API的端口,并通过添加ENTRYPOINT
命令定义如何启动应用程序。
例如,我们的Dockerfile
可能如下所示:
FROM java:8
ADD simple_application.tar .
EXPOSE 4567
ENTRYPOINT ["/simple_application/bin/simple_application"]
定义构建配置非常简单。 在常规设置中,我们定义一个新的工件: image.version
。 该文件的内容将在构建步骤之一中生成。
没有工件,我们将无法构建任何图像。 我们必须告诉我们的构建如何找到在构建阶段生成的工件 。 我们可以通过在TeamCity中定义工件依赖项来实现。 我们只需要选择一个构建配置,从该构建中定义工件 , TeamCity会将它们添加到工作目录中。
最后,在上一步通过所有测试之后,我们必须自动触发此构建。 通过引入完成构建触发器,我们可以在TeamCity成功完成应用程序构建后立即开始此构建。
现在我们准备发布。 必须引入三个构建步骤: Build Image , Push Image和Save Version 。 这次,我们将使用其他运行器类型: Command Line 。 我们可以在构建代理上执行Shell脚本。 因为我们已经在构建代理上安装了Docker ,所以我们可以在shell脚本中使用docker
命令。
建立影像
要构建映像,我们需要执行以下命令:
docker build --tag registry.private/simple_application:%build.number% .
Docker的build
命令将获取我们的Dockerfile
并构建标记为registry.private/simple_application
和版本%build.number%
。 变量%build.number%
是包含当前内部版本号的内置TeamCity变量。
推送图片
上一步中创建的映像仅存在于代理计算机上。 为了使图像可供其他人使用,我们需要将其存储在存储库中。 我们可以使用Docker Hub ,但是在我们的示例中,我们使用了一个位于地址repository.private
下的私有存储repository.private
。 我们可以执行以下命令将映像推送到存储库。
docker push registry.private/simple_application:%build.number%
保存版本
该图像已安全地存储在存储库中,但是我们需要再执行一个步骤:保存图像的版本。 我们再次运行一个shell脚本来生成一个image.version
文件:
echo %build.number% > image.version
部署
在上一步中,我们创建了一个映像,我们现在可以使用它来部署我们的应用程序。 我们将创建另一个构建配置: Deploy 。 此版本将根据发布阶段的映像在TeamCity代理上运行Docker容器。
我们的构建配置必须包含三个元素。 第一个是触发器。 同样,我们使用Finish Build Trigger ,并依赖于Release构建配置。
第二个元素是我们图像的版本字符串。 我们可以从Release版本创建的工件中获取此信息。
最后一步要复杂一些。 在部署新版本的应用程序之前,部署脚本需要检查具有先前版本的容器是否已部署在计算机上。 如果我们有这样的容器,我们需要停止并移除它。 接下来,我们从工件中读取当前版本,并基于该版本创建新容器。
if [ -n "$(docker ps --filter label=simple_application -q)" ]; then
docker stop simple_application
docker rm simple_application
fi;
version_to_deploy=$(cat image.version)
docker run -d -p 80:4567 \
--name simple_application \
--label simple_application \
registry.private/simple_application:${version_to_deploy}
连续部署管道已准备就绪。 现在,存储库的master分支中的每个更改都将构建,测试,发布和部署我们的应用程序。
完成。
当然,本文中描述的连续部署管道也得到了简化。 在我们的发布和部署步骤之间,我们想在类似于生产的环境中进行一些其他测试,或者引入零停机时间部署 ,但是我们的任何部署方法都应保持不变。
使用Docker映像。 您可以在持续部署的每个阶段确保您的应用程序具有一致的执行环境。 现在,您可以决定代码的执行方式,并且再也不必怪责管理员安装了错误版本的Java或Ruby。
翻译自: https://www.javacodegeeks.com/2016/03/docker-meets-continuous-deployment.html