Heroku风格的部署与Docker和Git标签

2021-04-26 11:01:16

在这篇文章中,我想解释一项新的部署方法,我在Drwn.io工作时想出。

想象一下,您已经拥有了一个与Gitub这样的Git服务同步的代码的项目。要具有基于Git推送的部署,我们需要拥有自己的遥控器。我们将使用对GitHub的方式推送我们的代码。

您可以通过创建文件夹(我将调用Gitem /)并在新创建的文件夹内执行Git init --bare来执行此操作。该命令将初始化您可以使用的空Git远程。

(我还将运行所有命令,好像我对服务器的root访问权限)。

您可能需要将root与您的用户名和/ root / gitrem更改为您所使用的任何文件夹。

该命令将为我们的项目添加一个新的遥控器。您可以使用命令git远程-v查看所有遥控器。它会显示出类似的东西:

起源[电子邮件保护]:[您的github用户名] / [repo name] .git(获取)原点[电子邮件保护]:[您的github用户名] / [repo name] .git(推送)制作[电子邮件保护] [您的服务器IP]:/ root / gitrem(获取)生产[电子邮件保护] [您的服务器IP]:/ root / gitrem(推送)

在Git操作之后执行某些东西,我们可以使用Git Hooks。 git hooks live .git / hooks文件夹。有客户端挂钩和服务器端挂钩。客户端挂钩在计算机上执行操作(您的本地计算机)。服务器端挂钩在计算机“接收”操作(我们的远程服务器)上运行。我们需要设置一个接收后钩子。在将新代码被推送到遥控器后,挂钩将运行。

关于接收后挂钩的一些注释:它无法阻止代码被推动,并且在执行时将保持连接到远程服务器。

在经历我们的Git挂钩之前,让我解释架构的外观。我们有一个负载平衡器/反向代理,我将在这里使用Caddy。然后我们将运行2个Web后端副本。我们将把它们与不同名称的2个不同的服务将它们视为2种不同的服务,而不是复制Docker Compose Service,而是运行相同的代码。 2份具有不同的名称,以便如果我们的部署不成功,我们可以保持一个活力。这是Docker-compose.yaml(省略了一些细节):

版本:" 3.8"服务:CADDY:构建:上下文:。 dockerfile:dockerfile.caddy#给它一个与UFW-Docker Container_Name:Caddy_Cont_1端口一起使用的名称: - " 80:80" - " 443:443"卷: - caddy_data:/ data - caddy_config:/ config web:build:context:。 dockerfile:dockerfile端口:#toplopehost ove port to localhost - " 8000:8000" WEB2:构建:上下文:。 dockerfile:dockerfile端口: - " 8000"卷:CADDY_DATA:CADDY_CONFIG:

Web和Web2是相同的容器,但名称不同。唯一的区别是Web将端口暴露于内部Docker组合网络和LocalHost(稍后将重要)。 Web2仅将其公开到内部Docker组合网络。

我为反向代理图像提供了自定义名称。 Docker不适用于Iptables,因此我使用UFW-Docker来设置防火墙。

构建并启动Web2。由于它与Web相同,如果Web建立并正确运行,我们可以立即使用Web2安全地完成。

我们将使用一些Git环境变量来运行和移动代码。我们对其中的2个感兴趣:

我们已经拥有git_dir的文件夹(一个名为gitrem /)的文件夹,但我们需要我们代码的另一个文件夹。我们可以在任何我们想要的地方创建它,让我们调用它appcode /。

然后我们将有一个循环检查输入。如果[$ ref =〜。* / main $]];正在检查我们正在推送到主分支(将其更改为掌握或无论您需要的内容)。然后使用git - Work-tree = $ git_work_tree --git-dir = $ git_dir checkout -f main我们正在将该分支中的内容复制到我们的git_ward_tree文件夹(appcode /)。

最后,我们将在循环内使用卷曲等待,直到第一个副本活着并重新创建第二个。

#!/ / usr / bin / env bash set -euo pipefailfail(){echo $ 1> 2;退出1; } [[$(id -u)= 0]] ||失败"请运行' sudo $ 0'"未命令git_index_fileunset git_dirunset git_work_treeexport git_dir = / root / gitremexport docker_opts =""导出git_work_tree = / root / appcode读取时读取OldRev NewRev Ref Do#更改标签IF [[$ REF =〜。* / main $]];然后回声"主裁判收到。将主分支部署到生产..." git - work-tree = $ git_work_tree --git-dir = $ git_dir checkout -f main else echo" ref $ ref成功收到。什么都没有:只有主分支机构可以部署在此服务器上。" FI完成#注意#运行应用程序需要的其他操作,如设置适当的#权限到访问文件夹等。#UFW允许HTTP&&& UFW允许https#以下是使用自定义容器名称的反向代理#这2个命令将从外部打开端口80和443到指定的Docker Compose服务(Caddy_cont_1)UFW-Docker允许CDDY_CONT_1 443UFW-Docker允许CDDY_CONT_1 80CD $ git_work_tree#构建ContainersDocker-compose -f docker-compose.yaml build#退出代码上次执行的命令#如果[" $ 0,停止和退出,如果[" $ " !=" 0" ]];然后回声"构建图像时出错。"退出1 Fiecho"启动新的容器..."睡眠1 ##开始反转代理和副本1docker-compose -f docker-compose.yaml up -d --d - n-deps caddydocker-compose -f docker- compose.yaml Up -d --no-deps web#如果第一个副本没有正确启动,则退出[" $? " !=" 0" ]];然后回声"部署图像时出错。"退出1 fi#(可选)一些睡眠时间让第一个副本startsleep 5#等待它是availableattempt_counter = 0#max_attempsmax_attempts = 10#以来" web"服务是将端口8000曝光到localhost,我们可以从我们的#脚本#下面的循环向其发送请求。它将查询/ healthz Enpoint,直到它收到#An" OK"回复。 #它将每5秒重试,每个请求都有6秒的超时。 #它最多可以做10次尝试。 #总结,如果应用程序尚未在10 * 6 * 5 = 300秒内启动,则= 5分钟,退出。 #此数字对于大多数用例来说可能很高,因此更改这些变量以满足您的需求。直到$(curl --output / dev / null --max-time 6 - get --fail localhost:8000 / healthz);如果[$ {expert_counter} -eq $ {max_attempts}];然后回声"最大尝试到达"退出1 fi printf''尝试_counter = $(($ collect_counter + 1))睡眠5 do do#如果循环完成正确,则表示" web"服务已启动,/ healthz endpoint#正在工作,因此我们可以重新创建第二个副本(" web2")回声" replica,创建第二副本" docker-compose -f docker -compose.yaml Up -d --no-deps web2docker-compose -f docker-compose.yaml Up -d

您可以在反向代理中自定义健康检查,以减少在应用程序重新创建应用程序时遇到失败请求的可能性。在我的情况下(带有一个caddyfile)我正在使用:

从技术上讲,有一个300ms的窗口,其中一个请求可能失败,因为反向代理没有发现这个上游服务器已关闭,并且它可以将请求转发到它。如果需要,我们可以将其降低到较低的值。

我们需要将该代码放在我们的远程服务器中.git / hooks / host-recept。在我们的示例中,它将是/克罗特/克鲁斯/普斯特 - 奖励。不要忘记使用chmod + x /root/gitrem/.git/hooks/post-receive提供执行权限。

我们现在已经设置了我们遥控器中所需的一切。在我们的本地计算机中,我们可以运行:

这将推动我们的代码到我们的自定义git远程,后接收钩子将运行,我们将拥有我们的应用程序构建和运行!

现在我们基于Git推动的部署,但我们可以做得更好。我们将使用git标签进行。如果您不知道Git标签是什么,您可以想象您正在播放视频游戏,您可以在那里保存游戏。您经常节省(Git Push),但一些节省更重要,您将给出一个名称或ID,这是一个Git标记。 Git标签通常用于识别发布版本。我们将使用它们来识别我们的应用程序中的版本,我们需要以下内容:

将我们的自定义远程移动到该标签。后接收后钩子将使用我们创建标记时的代码执行。

要创建标记,我们可以运行以下命令。它们将创建一个新的提交和一个名为v2的关联标记。

#添加filesgit添加.git commit -allow-name-m"标签" git标签-a v2-m"版本v2"

我们可以使用git rev-list与该标签关联的提交哈希列表:git rev-list-n 1 v2。

对于这个例子,我们想象我们的哈希是425368b5(真正的哈希比这更长)。

好的,我们有标签和与该标签关联的提交哈希。我们需要的最后一件事是让我们的自定义远程移动到该提交的方法。幸运的是,还有一个命令:

这将使我们的自定义远程转到该特定提交,请结帐并触发接收后挂钩。现在也是在BASH功能中包装东西的好时机:

函数标记{git add -u。 git commit --allow-name-m"标记" git标签-a" $ 1" -m"版本$ 1"}函数totag {tagname =" $ 1" git推-f生产+" $(git rev-list -n 1 $ tagname)":main}函数部署{tag" $ 1" Totag" $ 1"}

现在我们可以运行部署v2和bam!我们有我们的应用程序运行。如果要滚动到以前的标记,则可以通过运行totag v1(或任何其他标记名称)来执行此操作。

额外:您可以使用创建日期排序的GIT存储库中的所有标记:

我们创建了一个带有后接收钩子的自定义git远程。 当我们推动新代码时,它将将我们的应用程序作为Docker容器构建。 我们可以使用一些git命令将远程移动到特定提交。 最后,我们还使用Git标签来标识重要提交(发布)。 这曾经是促进通讯的盒子,但我认为我们已经有足够的新闻稿。 每个人都想在邮箱中找到一个位置。 而不是那个,订阅我的RSS饲料将在您的生活中更好,更少侵入。