Compose: 有没有一种方法可以延迟容器启动以支持具有较长启动时间的相关服务

创建于 2014-08-05  ·  314评论  ·  资料来源: docker/compose

我有一个MySQL容器,它需要一些时间来启动,因为它需要导入数据。

我有一个依赖于MySQL容器的Alfresco容器。

此刻,当我使用无花果时,Alfresco容器中的Alfresco服务在尝试连接到MySQL容器时失败了……表面上看是因为MySQL服务尚未在监听。

有图解决这种问题的方法吗?

最有用的评论

是的,我会对类似的东西感兴趣-打算早些发布。

我认为可以为我们解决此用例的最小影响模式如下:

在“ fig.yml”中添加“ wait”作为新键,并将类似的值语义作为链接。 Docker将其视为先决条件,并等待该容器退出后再继续操作。

因此,我的docker文件将类似于:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

在运行的应用程序上,它将启动所有链接容器,然后运行等待容器,并且仅在退出等待容器(initdb)后才前进到实际的应用程序容器。 initdb将运行一个脚本,等待数据库可用,然后运行所有初始化/迁移/任何操作,然后退出。

无论如何,这就是我的想法。

所有314条评论

在工作中,我们将依赖服务包装在脚本中,该脚本检查链接是否已建立。 我知道我的一位同事也会对此感兴趣! 我个人认为等待服务可用是容器级别的问题,但是我可能是错的:)

我们用包装做同样的事情。 您可以在此处查看示例: https :

拥有一个入口点脚本可以遍历所有链接,并等它们开始工作,然后再启动传递给它的命令,这很方便。

这应该内置在Docker本身中,但是该解决方案还有一段路要走。 在容器公开的链接打开之前,不应将其视为已启动。

@bfirsh超出了我的想象,但效果非常好。

在容器公开的链接打开之前,不应将其视为已启动。

我认为这正是人们所需要的。

现在,我将在https://github.com/aanand/docker-wait使用变体

是的,我会对类似的东西感兴趣-打算早些发布。

我认为可以为我们解决此用例的最小影响模式如下:

在“ fig.yml”中添加“ wait”作为新键,并将类似的值语义作为链接。 Docker将其视为先决条件,并等待该容器退出后再继续操作。

因此,我的docker文件将类似于:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

在运行的应用程序上,它将启动所有链接容器,然后运行等待容器,并且仅在退出等待容器(initdb)后才前进到实际的应用程序容器。 initdb将运行一个脚本,等待数据库可用,然后运行所有初始化/迁移/任何操作,然后退出。

无论如何,这就是我的想法。

(修订,请参见下文)

这里也+1。 必须在命令本身中执行此操作不是很吸引人。

+1。 刚遇到这个问题。 顺带一提,很棒的工具让我的生活更加轻松!

+1很棒。

+1。 最近遇到了同样的问题

+1。 dockerguys有任何声明吗?

我现在正在将包装器脚本作为入口点进行同步,不确定是否在图中具有某种机制是否明智,如果您的容器还有其他目标以不同方式执行编排。 似乎非常特定于我的应用程序,例如容器在执行工作。

经过一番思考和实验,我对此表示同意。

正因为如此,我正在构建的应用程序基本上具有同步
waitfor(host,port)函数,让我等待应用程序的服务
取决于(通过环境检测或显式检测
通过cli选项进行配置)。

干杯
詹姆士

詹姆斯·米尔斯(James Mills)/ prologic

电子信箱
W:prologic.shortcircuit.net.au

2014年8月22日星期五,下午6:34,Mark Stuart [email protected]
写道:

我正在将包装器脚本作为入口点进行同步,
如果您有其他目标,则不确定是否在图中具有某种机制是明智的
您以不同方式执行编排的容器。 似乎非常
特定于我的应用程序,例如容器的责任
做工作。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/docker/fig/issues/374#issuecomment -53036154。

是的,这里需要一些基本的“依赖” ...
因此,如果您有20个容器,则只需要运行无花果,一切就以正确的顺序开始...
但是,它也有一些超时选项或其他故障捕获机制

这里另外+1。 我让Postgres花了比Django更长的时间来启动数据库,因此没有黑客就没有DB来执行迁移命令。

@ahknight很有趣,为什么迁移在run期间运行?

您是否不想在build阶段实际运行迁移? 这样,您可以更快地启动新图像。

the,有一个较大的启动脚本用于该应用程序。 现在,我们首先进行非数据库工作,在循环中使用nc -w 1等待数据库,然后执行数据库操作。 它有效,但是却让我感到肮脏。

fig build阶段,我在这项工作上取得了很多成功。 我有一个与django项目相关的示例(仍在进行中): https :

无需轮询启动。 尽管我已经对mysql做过类似的事情,但是我不得不轮询启动,因为mysqld初始化脚本尚未这样做。 这个postgres初始化脚本似乎要好得多。

这是我的想法:

使用docker / docker#7445的想法,我们可以在无花果中实现此“ wait_for_helth_check”属性?
那么这将是一个无花果,而不是Docker问题吗?

无论如何,是否有必要使无花果检查链接容器上的tcp状态,如果是的话,我认为这是要走的路。 =)

@dnephin您能解释一下您在Dockerfiles中正在做什么以帮助实现这一点吗?
生成阶段不影响运行时间吗?

@docteurklein,我可以。 我从上面修复了链接(https://github.com/dnephin/readthedocs.org/blob/fig-demo/dockerfiles/database/Dockerfile#L21)

这个想法是,您在构建过程中会执行所有较慢的“设置”操作,因此您不必在容器启动过程中等待任何事情。 对于数据库或搜索索引,您将:

  1. 启动服务
  2. 创建用户,数据库,表和灯具数据
  3. 关闭服务

全部作为一个单独的步骤。 稍后,当您fig up数据库容器就可以立即开始使用了,您还可以利用Docker构建缓存来进行这些较慢的操作。

真好! 谢谢 :)

@dnephin很好,没想到。

+1这绝对是必要的。
在大多数情况下,丑陋的时间延迟破解就足够了,但是欢迎使用_real_解决方案。

您能否举例说明为什么/何时需要它?

在我的用例中,我有一个Elasticsearch服务器,然后有一个连接到Elasticsearch的应用程序服务器。 Elasticsearch花费了几秒钟的时间来启动,所以我不能简单地执行fig up -d因为在连接到Elasticsearch服务器时,应用程序服务器将立即失败。

假设一个容器启动了MySQL,另一个容器启动了一个需要MySQL的应用程序,结果另一个容器启动得更快。 因此,我们有暂时的fig up故障。

Crane让您创建可以单独启动的组来解决此问题。 因此,您可以启动MySQL组,等待5秒钟,然后启动其他依赖于它的东西。
规模很小,但不是真正的解决方案。

@oskarhane不确定这种“等待5秒”是否有帮助,在某些情况下可能需要等待更多(或者无法确定它不会超过5秒)......依靠时间等待。
另外,您还必须手动执行此操作以等待并加载另一个组,这有点la脚,图应该为您完成此操作= /

@oskarhane@ dacort@ ddos​​sot :请记住,在现实世界中,事情崩溃并重新启动,网络连接来

没错,但是直到我们修复所有现有的应用程序,以使其能够从启动时就从缺少关键资源(例如DB)的状态下正常恢复(这是一件很棒的事情,但不幸的是很少受到框架的支持)之前,我们应该使用fig start以一定的顺序启动单个容器,但有延迟,而不是fig up

我可以看到一个shell脚本来控制图来控制docker:wink:

我没有将它内置在无花果中,但关于等待准备的最佳做法的一些建议会很好

我在以前的评论链接的一些代码中看到这样做:

while ! exec 6<>/dev/tcp/${MONGO_1_PORT_27017_TCP_ADDR}/${MONGO_1_PORT_27017_TCP_PORT}; do
    echo "$(date) - still trying to connect to mongo at ${TESTING_MONGO_URL}"
    sleep 1
done

在我的情况下,虽然没有/dev/tcp路径,也许它是不同的Linux发行版(?)-我在Ubuntu上

我发现这种方法似乎可以正常工作:

until nc -z postgres 5432; do
    echo "$(date) - waiting for postgres..."
    sleep 1
done

这似乎可行,但我对这些事情还不了解,是否知道它是否健壮。有人知道端口显示nc与postgres服务器_really_之间是否存在可能的竞争条件。命令?

如果可以反转检查,我会更开心-可以从目标容器(即postgres服务器)向所有依赖者发送信号,而不是从依赖容器进行轮询,而不是轮询?

也许这是一个愚蠢的主意,有人有什么想法吗?

@anentropic Docker链接是单向的,因此从下游容器进行轮询是目前唯一的方法。

有谁知道在显示nc的端口和postgres服务器真正能够接受命令之间是否存在竞争情况?

在一般情况下,没有办法知道-对于postgres可能是正确的,对于其他服务可能是错误的-这是图3中不这样做的另一个论点。

@aanand我尝试使用您的

我希望通过将等待容器链接到Orientdb,我不会看到此错误。 但是不幸的是我还是随机得到它。 这是我的设置(Docker版本1.4.1,在Ubuntu 14.04 Box上的图1.0.1):

orientdb:
    build: ./Docker/orientdb
    ports:
        -   "2424:2424"
        -   "2480:2480"
wait:
    build: ./Docker/wait
    links:
        - orientdb:orientdb
....
core:
    build:  ./Docker/core
    ports:
        -   "3000:3000"
    links:
        -   orientdb:orientdb
        -   nsqd:nsqd

任何帮助表示赞赏。 谢谢。

@mindnutswait图片更多是演示; 它不适合在fig.yml 。 您应该在core容器中使用相同的技术(重复轮询)来等待orientdb容器启动,然后再开始主进程。

+1刚开始遇到这个问题,因为我要提取自定义生成的图片,而不是在fig.yml中生成它们。 节点应用失败,因为mongodb尚未准备好...

我只是花了几个小时来调试为什么用Docker手动启动WordPress时MySQL可以访问,以及为什么从Fig启动时它却脱机。直到现在,我才意识到,只要启动应用程序,Fig总是会重启MySQL容器,所以WordPress entrypoint.sh死了。尚无法连接到MySQL。

我添加了自己的覆盖的entrypoint.sh,它会在执行实际的entrypoint.sh之前等待5秒钟。 但是很明显,如果使用Docker / Fig启动MySQL + WordPress容器组合很容易,那么这是一个需要通用解决方案的用例。

因此WordPress entrypoint.sh死亡,无法连接到MySQL。

我认为这是WordPress容器的问题。

当我最初是这个想法的粉丝时,在阅读https://github.com/docker/docker/issues/7445#issuecomment -56391294之后,我认为这样的功能将是错误的方法,实际上会鼓励不良做法。

这个问题似乎旨在解决两种情况:

需要使用依赖项服务来执行一些初始化。

任何容器初始化都应该在build期间完成。 这样,就可以对其进行缓存,并且不需要图像的每个用户重复进行此工作。

依赖项服务需要可用,以便可以打开连接

该应用程序确实应该能够抵抗连接失败,然后重试连接。

我认为问题的根源在于,对于等待服务准备就绪是谁的责任没有根本性的规定。 但是,即使有,我认为期望开发人员将重试数据库连接到每个初始化脚本中也有些不切实际。 通常,需要这样的脚本来准备刚刚挂载的空数据卷(例如,创建数据库)。

如果Fig在重新启动应用程序容器时不总是重新启动链接的容器(即数据库服务器),则该问题实际上不会那么麻烦。 我真的不知道为什么要这么做。

如果Fig在重新启动应用程序容器时不总是重新启动链接的容器(即数据库服务器),则该问题实际上不会那么麻烦。 我真的不知道为什么要这么做。

实际上,它不仅_restart_容器,还_destroys和recreates_它们,因为这是确保对fig.yml进行更改的最简单方法。 我们最终应该实现一个更智能的解决方案,该解决方案可以将“当前配置”与“所需配置”进行比较,并且仅重新创建已更改的内容。

回到最初的问题,我真的不认为期望容器具有连接重试逻辑是不现实的-这是设计有效的分布式系统的基础。 如果需要共享不同的脚本,则应将其分解为可执行文件(如果不使用shell,则应分解为特定于语言的模块),因此每个脚本只能在顶部调用waitfor db

@kennu --no-recreate怎么样? / cc @aanand

@aanand我的意思是,从Docker Hub的观点来看,这种不切实际的评论已经充满了已发布的映像,这些映像可能无法处理其初始化脚本中的连接重试,并且让所有人添加它都是一项艰巨的任务。 但是我想如果Docker公司发布某种官方指南/要求可以做到。

就我个人而言,我宁愿使容器/图像保持简单,并让底层系统担心解决依赖关系。 实际上,Docker的重启策略可能已经解决了所有问题(如果应用程序容器无法连接到数据库,它将重新启动并重试,直到数据库可用为止)。

但是依靠重启策略意味着默认情况下应该启用它,否则人们会花费数小时来调试问题(就像我刚才所做的那样)。 例如,对于Pod,Kubernetes默认为RestartPolicyAlways。

有什么进展吗? 我想回应一下,期望所有docker映像都发生变化并且整个社区实施连接重试实践是不合理的。 Fig是Docker的编排工具,问题在于它的执行顺序,因此需要在Fig中进行更改,而不是Docker或社区。

期望所有docker映像都发生更改并且整个社区实施连接重试实践是不合理的

这并不是因为docker或fig导致应用程序需要重试。 由于网络不可靠,因此应用程序应能够应对断开的连接。 任何应用程序都应该已经以这种方式构建。

我个人不必在任何容器中实施重试,也不需要任何延迟或等待启动。 我相信大多数此问题都属于这两类(我在这里对“重试”的使用可能不太好,我的意思是,如果连接关闭,它将重新建立连接,不一定要在一段时间内尝试多次尝试建立连接)次)。

如果您确保所有初始化都在“构建”阶段进行,并且确保在下一个请求时重新建立连接,则无需重试(或等待其他容器启动)。 如果连接是延迟打开的(在发出第一个请求时),而不是急切地打开(在启动过程中),我怀疑您根本不需要重试。

问题在于[fig]做事的顺序

到目前为止,在本次讨论中我没有提到任何内容。 图基于配置中指定的链接命令启动,因此它应始终以正确的顺序启动容器。 您可以提供订单不正确的测试用例吗?

我必须在这里同意@dnephin 。 当然,如果compose / fig能够执行一些魔术操作并检查服务的可用性会很方便,但是,如果服务不响应,预期的行为是什么? _really_取决于您的应用程序/堆栈的要求。 在某些情况下,整个堆栈应被销毁并替换为新的堆栈,在其他情况下,应使用故障转移堆栈。 可以考虑许多其他方案。

Compose / Fig无法做出这些决定,监视服务应由容器内运行的应用程序负责。

我想建议@dnephin只是幸运的。 如果您并行分叉两个进程,其中一个将连接到另一个将监听的端口,则实际上是在引入竞争条件。 彩票,看看哪个过程发生初始化更快。

我还想重复WordPress的初始化示例:如果MySQL容器尚不具有该数据库,它将运行一个启动shell脚本来创建一个新数据库(构建Docker映像时不能这样做,因为它依赖于外部安装的数据量)。 如果必须将通用数据库错误与“数据库尚未准备好”错误区分开来,并且在shell脚本中实现一些合理的重试逻辑,则该脚本将变得更加复杂。 我认为该图像的作者极有可能永远不会根据上述竞争条件实际测试启动脚本。

不过,如果您准备接受容器偶尔会启动失败并定期在日志中打印错误,则Docker的内置重启策略提供了解决方法。 (如果您记得将其打开。)

就个人而言,我将使Fig自动检测哪些容器端口暴露于链接的容器,并在启动链接的容器之前对其进行ping操作(合理超时),从而使事情正常工作,并最终提供配置设置以覆盖/禁用此功能。

构建Docker映像时无法完成此操作,因为它取决于外部安装的数据量

真正。 这里的一种方法是只启动数据库容器一次(如果需要,使用不同的入口点/命令),以初始化数据库,或使用仅数据容器作为数据库的容器是从与数据库容器本身相同的映像创建的。

如果必须将通用数据库错误与“数据库尚未准备好”错误区分开来,那么该脚本将变得更加复杂。

Compose / Fig将在那里遇到相同的问题; 如何检查MySQL是否已启动以及_accepting_连接? (和PostgreSQL,以及(_在此处插入您的服务_))。 另外,应该从何处执行“ ping”? 在您要启动的容器中,是从主机开始的?

据我所知,官方的WordPress图片中包含检查以查看MySQL是否接受docker-entrypoint.sh

@thaJeztah “在PHP中为MySQL连接错误添加一些简单的重试逻辑”,由tianon于2天前撰写-不错。 :-)谁知道,也许毕竟这将成为一种标准方法,但是我仍然有我的疑问,尤其是对于这种重试实现实际上已经被所有图像作者测试过的情况。

关于端口ping-我不能随便说说最佳实现是什么。 我想也许可以从一个临时链接的容器中进行简单的连接检查,然后在获取ECONNREFUSED时重试。 无论解决了80%(或可能是99%)的问题,用户都不必一次又一次地自行解决。

@kennu啊! 谢谢,不知道它是最近才添加的,由于此讨论,现在才检查脚本。

明确地说,我了解您所遇到的问题,但是我不确定Compose / Fig是否能够以一种干净的方式解决问题,从而对每个人都有效(可靠)。 我知道注册表中的许多映像都没有“安全措施”来处理这些问题,但是我怀疑Compose / Fig的责任是要解决此问题。

说了以上; 我想最好

人们应该意识到这一点,并应该添加一些示例来说明如何处理服务“中断”。 包括指向@dnephin提及的WikiPedia文章的链接(可能还有其他来源),以供参考。

我遇到了同样的问题,喜欢@kennu的想法

Personally, I would make Things Just Work, by making Fig autodetect which container ports are exposed to a linked container, ping them before starting the linked container (with a sane timeout), and ultimately provide a configuration setting to override/disable this functionality.

我认为这可以解决很多典型的用例,例如对我来说,取决于官方的mongodb容器。

我同意@soupdiver。 我在与mongo容器结合使用时也遇到了麻烦,尽管我可以将它与start.sh脚本一起使用,但该脚本不是很动态,并添加了另一个我需要保留在存储库中的文件(我想拥有我的节点存储库中的Dockerfile和docker-compose.yml)。 如果有某种方法可以使它正常工作,那就太好了,但我认为,诸如等待计时器之类的简单事情在大多数情况下都无法解决。

IMO ping操作还不够,因为基本的网络连接可能可用,但是服务本身仍未准备就绪。
例如,在MySQL映像中就是这种情况,使用curl或telnet在裸露的端口上进行连接检查会更安全,尽管我不知道这是否足够。 但是大多数容器默认没有安装这些工具。

码头工人或无花果可以处理这些检查吗?

码头工人或无花果可以处理这些检查吗?

简而言之:_no_。 由于各种原因;

  • 从容器内执行“ ping”将意味着运行第二个过程。 Fig / Compose无法自动启动该过程,我不认为您希望Fig / Compose通过其中的_installing_软件(例如curl或telnet)来修改容器。
  • (正如我在前面的评论中提到的那样),每个服务需要不同的方式来检查它是否接受连接/是否可以使用。 某些服务可能需要凭据或证书才能建立连接。 Fig / Compose无法自动发明如何做到这一点。

而且我认为您不希望Fig / Compose通过在其中安装软件(例如curl或telnet)来修改容器。

不,当然不能。

Fig / Compose无法自动发明如何做到这一点。

没有发明。 我在考虑更多关于无花果或码头工人的指令,例如如何检查它。

web:
    image: nginx
    link: db
db:
   is_available: "curl DB_TCP_ADDR:DB_TCP_PORT"

telnet命令将在docker-host上执行,而不是在容器中执行。
但是我只是大声思考,我知道这不是完美的解决方案。 但是可以改进当前对容器使用自定义检查脚本的方式。

telnet命令将在docker-host上执行,而不是在容器中执行。

然后必须在主机上安装curl<name a tool that's needed> 。 这甚至可能会带来巨大的安全性问题(例如,有人想变得有趣并使用is_available: "rm -rf /" )。 除此之外,不能从_host_访问数据库不能保证也可以从容器内部对其进行访问。

但我只是大声思考,...

我知道,我对此表示感谢。 只是认为没有可靠的方法可以自动执行此操作,否则将无法满足大多数用例的需要。 在许多情况下,您最终会遇到一些复杂的事情(例如,以curl为例;尝试连接多长时间?重试?)。 这样的复杂性最好在容器内部移动,如果容器是用Docker而不是Fig / Compose启动的,则这也很有用。

@thaJeztah我完全同意你的看法。 而且很可能不会有100%的解决方案。

我将重复我先前提出的建议:如果我可以在fig.yml中声明“在运行另一个容器之前等待此容器退出”,这对我就足够了。

这将使我能够设计出一个容器,该容器知道如何等待其所有依赖关系-检查端口,初始化数据库等,并且需要使fig尽可能少地了解。

我将其配置为:

“”
应用程式:
链接:
-db:db
先决条件:
-先运行

首先运行this:
链接:
-db:db
“””

runthisfirst具有一个链接,它表示数据库已启动,因此它可以检查访问。 应用程序仅在runthisfirst退出后才能运行(如果runthisfirst必须成功退出,则奖励积分)。

这是可行的答案吗?

J

2015年2月10日,下午5:28,Tobias Munk [email protected]写道:

@thaJeztah https://github.com/thaJeztah我完全同意你的看法。 而且很可能不会有100%的解决方案。

-
直接回复此电子邮件,或在GitHub https://github.com/docker/fig/issues/374#issuecomment -73561930上查看。

我刚刚尝试了迁移我的Shell脚本启动器,并遇到了这个问题。 即使只是添加一个简单的睡眠/等待键也很不错,该键在启动下一个容器之前睡眠了该秒数。

db:
  image: tutum/mysql:5.6
  sleep: 10
app:
  link:
    - db:db

由于种种原因,我真的不喜欢这样。

a)我认为这是错误的地方
b)你睡多久了?
c)如果超时时间不长怎么办?

除了明显的问题,我真的不认为
基础设施应该关心什么应用程序
是,反之亦然。 IHMO应用程序应写为
对自己的要求更宽容和/或更精明。

话虽这么说,现有应用程序和遗留应用程序
将需要一些东西-但是可能应该更进一步
的行:

一个docker-compose.yml

db:
  image: tutum/mysql:5.6
app:
  wait: db
  link:
    - db:db

wait等待db上的“公开”服务可用。

问题是您如何确定呢?

在最简单的情况下,您要等到成功打开
与公开服务的tcp或udp连接。

对于这个问题,这可能是多余的,但是如果docker提供了一个事件触发系统,您可以在其中从一个容器启动触发器,从而在另一个容器中造成某种回调,那么这将是一个不错的解决方案。 在等待在启动另一个服务之前将数据导入MySQL数据库中的情况下,仅监视端口是否可用是不够的。

让入口点脚本从容器内部向Docker设置警报(例如,设置预定义的环境变量),从而在另一个容器中触发事件(也许设置了相同的同步环境变量),这将使双方的脚本都能知道何时任务完成。

当然,我们可以设置自己的套接字服务器或其他方式,但这对于解决容器编排问题很繁琐。

@aanand我_almost_有什么用你的等待方式为出发点的工作。 但是,在docker-compose rundocker run之间还有其他事情发生,前者似乎挂起,而后者则很有魅力。

示例docker-compose.yml:

db:
  image: postgres
  ports:
    - "5432"
es:
  image: dockerfile/elasticsearch
  ports:
    - "9200"
wait:
  image: n3llyb0y/wait
  environment:
    PORTS: "5432 9200"
  links:
    - es
    - db

然后使用...

docker-compose run wait

但是,事实并非如此。 链接的服务启动,看起来我们将只等它被阻塞(至少在我的virtualbox环境中。我进入nc循环,然后得到一个点,然后……什么都没有)。

但是,在链接服务运行时,我可以使用此方法(本质上,这是我为CI构建所做的工作)

docker run -e PORTS="5432 9200" --links service_db_1:wait1 --links service_es_1:wait2 n3llyb0y/wait

感觉docker-compose run应该以相同的方式工作。 区别在于,当将docker-compose run与分离标志-d ,由于等待容器背景的存在,您没有任何等待收益,并且我认为(此时此刻)不使用该标志会导致等待阻塞其他非后台服务。 我要仔细看看

经过一番尝试和错误后,上述方法似乎确实有效! 只是busybox的基础上没有运行良好的netcat util。 当使用docker-compose run <util label>而不是docker-compose up时,我的修改后@aanand wait实用程序确实适用于

虽然不确定是否可以按照原始问题处理链接情况。 可能不会。

让我知道你的想法。

这是一个非常有趣的问题。 我认为,有一种方法可以让一个容器等待直到另一个容器准备就绪,这真的很有趣。 但是正如大家所说,准备好意味着什么? 就我而言,我有一个用于MySQL的容器,另一个用于管理其备份并负责导入初始数据库的容器,然后是每个需要该数据库的应用程序的容器。 显然,仅等待端口暴露是不够的。 首先必须启动mysql容器,然后其余的应等待mysql服务准备好使用,而不是之前。 为此,我需要实现一个使用docker exec功能在重新启动时执行的简单脚本。 伪代码基本上是这样的:

run mysql
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"show tables\""
run mysql-backup
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"describe my_table\""
run web1
waitUntil "dexec web1 curl localhost:9000 | grep '<h1>Home</h1>'"
run web2
waitUntil "dexec web2 curl localhost:9000 | grep '<h1>Home</h1>'"
run nginx

其中waitUntil函数有一个带有超时的循环,该循环将超时docker exec …命令,并检查退出代码是否为0。

这样,我可以确保每个容器都可以等待,直到其依赖项准备就绪为止。

因此,我认为将其整合到compose实用程序中是一种选择。 也许是这样的,其中wait_until声明其他依赖项(容器)的列表,并等待每个依赖项,直到它们对相应的命令做出正确的响应(或者可以使用可选的模式或regex来检查结果是否与即使使用grep命令也足够了)。

mysql:
  image: mysql
  ...
mysql-backup:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "show tables"
  ...
web1:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
web2:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
nginx:
  links:
   - web1
   - web2
  wait_until:
   - web1: curl localhost:9000 | grep '<h1>Home</h1>'
   - web2: curl localhost:9000 | grep '<h1>Home</h1>'
  ...

这样的港口难道不是一件容易的事吗?
http://docs.azk.io/en/azkfilejs/wait.html#

@robsonpeixoto :对于许多用例来说,等待端口是不够的。 例如,假设您正在使用创建的数据来播种数据库,并且在数据操作完成之前,您不希望Web服务器启动并连接到该数据库。 该端口将一直处于打开状态,从而不会阻止Web服务器启动。

像AWS CloudFormation的WaitCondition这样的东西会很好。 http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-waitcondition.html

+1在使用Docker测试依赖MySQL的Rails应用程序时遇到相同的问题

+1我也有这个问题。 我喜欢@adrianhurt的想法,您实际上在其中提供要评估的条件以确定等待是否完成。 这样,您仍然拥有一个不错的声明性yml,并且不必具有“就绪”的任意定义。

+1

我有一段时间打开了这个标签: http :

+1

+1表示简单超时

+1准备就绪

+1

一段时间以来,我一直在应用程序级别上非常可靠地解决此问题,就像在此线程中建议的那样。

只是为了让您了解如何在MySQL + PHP中实现此功能,这是我的代码。

从igorw /重试:)

由于网络是可靠的,因此事情应该总是有效。 我对吗? 对于不这样做的情况,可以重试。

+1

@ schmunk42好东西-我喜欢这是建立连接和执行幂等数据库设置操作的一个很好的例子。

对于不同的情况,例如NodeJS,Ruby,PHP,最好创建一个(一些)基本示例以包含在文档中。

+1,至少应该提供一些选项来增加成功启动容器之前的延迟。

+1

尝试连接非代码的服务时如何解决问题。
例如,如果a具有服务Service和数据库InfluxDBServices需要InfluxDB ,而InfluxDB启动缓慢。

docker-compose如何等待InfluxDB准备就绪?

我的代码是我的,我可以解决重试问题。 但是对于第三个应用程序,我无法更改代码。

@robsonpeixoto这张票中有一些使用netcat或类似方式的示例。 您可以在另一张票据中查看我的MySQL示例: https :

这就是为什么我认为每个容器都应该具有指示其自身准备状态的可选功能的原因。 例如,对于一个数据库,我想等到服务完全准备好,而不是在创建进程时。 例如,我通过使用docker exec进行自定义检查并检查它是否可以解决简单的查询来解决此问题。

docker run一些可选标志表示内部检查命令,以后使用链接的特殊标志从另一个容器链接它时,将非常有用。

就像是:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python app.py

其中is_ready.sh是一个简单的布尔测试,它负责确定何时将容器视为就绪状态。

+1

@ schmunk42不错的报价!

+1

+1

+1

+1

+1

+1

+1

+1

+1

其实我改变了主意,所以-1

对于您的容器来说,检查是否有第三方服务可用更有意义,例如,使用一个使用nc的小bash包装器脚本即可轻松完成此操作。

依靠延迟是很诱人的,但这是一个糟糕的解决方案,因为:

  • 您的容器将总是准备等待X秒钟,然后准备就绪。
  • 在某些情况下(例如,主机上的I / O或CPU过多),X秒可能仍然不够,因此您的容器仍然不是故障安全的。
  • 没有失败策略。

依靠编写包装器bash脚本更好,因为:

  • 您的容器已准备就绪。
  • 您可以执行任何失败策略,例如尝试10次然后失败,再尝试一次,等等。您甚至可以通过在尝试之前睡觉来实现延迟!

通过阅读本主题文章,我发现没有人提及秘密。 我正在尝试使用仅数据容器,该容器在运行时会请求机密。 我遇到的问题是:如果我的机密花费太长时间来传输/解密,那么我的依赖容器就会失败,因为它所期望的数据不存在。 我真的不能使用“在运行之前将所有内容正确放入容器中”的方法,因为它们是秘密。

我知道由于返回代码的上下文,在仅数据容器中存在一些歧义,但是有没有更好的方法呢?

同样,我改变主意,-1。 @dnephin的方法是完全正确的。 如果您的_application_依赖于_service_,则应用程序本身应能够正常处理该服务的不可用性(例如,重新建立连接)。 它不应该是某些bash包装脚本或Compose或Docker中的某些逻辑,而是应用程序本身的责任。 任何不在应用程序级别的内容也将仅在初始化时起作用。 如果该服务出现故障,则将不会执行包装脚本或其他操作。

现在,如果我们能让应用程序/库/框架开发人员意识到并支持这一职责,那就太好了。

考虑要采取的方法很困难,需要将其他守护进程侧载,这是不推荐的。 在我的示例中,我有一个Rails应用程序尝试连接到MySQL数据库,而另一个Rails应用程序当前正在迁移并在初始启动时为DB播种,以便让我知道Rails应用程序不尝试使用该数据库,我可以必须修改ActiveRecord库(不会发生)或运行一个脚本,该脚本不断检查数据库是否已迁移并播种。 但是,我如何确定却不知道应该在其中存放什么数据和/或在系统中运行了一些脚本,这些脚本正在为DB播种,以告知其余的连接数据库。

也许我错过了明显的解决方案,但是当您使用现成的库并且不“应该”将守护程序侧载到其中时,您对“开发人员应该能够在自己的代码中处理此问题”的回答就破裂了一个容器。

@mattwallington我什至不确定Compose如何为这种情况提供解决方案...
这也是非常具体的,这会使Compose发明起来更加困难。 我会阅读上面有关初始化/迁移/播种的@dnephin技巧,因为这可能对您有所帮助。

我认为您错过了我最后一行的要点,许多现成的库无法_precisely_正常工作,因为它们不是弹性构建的。 没有一个神奇的解决方案可以解决Compose可以实现的所有问题。

明白了我先前的建议对许多这些用例都适用,那就是在容器之间有一个共享的环境变量。 一侧可以锁定轮询变量,而另一侧可以执行操作,然后设置变量。

+1 @mattwallington容器之间共享环境变量的想法

谢谢。 它很简单(在产品级别。由于我不看代码,所以我不知道从开发方面会采取什么措施),但是它可以解决很多这些问题,并且可能解决很多其他问题,因为这些问题并非特定于这个问题。

但是,我如何确定却不知道应该在其中存放什么数据和/或在系统中运行了一些脚本,这些脚本正在为DB播种,以告知其余的连接数据库。

@mattwallington :检查架构表中的迁移号。 如果数字正确,则说明迁移已完成。

它不应该是某些bash包装脚本或Compose或Docker中的某些逻辑,而是应用程序本身的责任。 任何不在应用程序级别的内容也将仅在初始化时起作用。 如果该服务出现故障,则将不会执行包装脚本或其他操作。

@ agilgur5 :是的,我同意它将由应用程序处理,但是bash脚本是一种简单的解决方案,用于处理未采用这种方式编码的应用程序,例如,在服务不可用时通过重新启动应用程序。

可以一整天就应用程序级别上应该或可以做的事情争论不休,而不是期望市场上的每个应用程序都可以解决这个问题并在自我恢复方面变得很棒(不太可能),为什么我们反对添加一些可以解决的功能无论第三方应用程序的编写方式或应该执行但不会执行的操作,在docker中运行的应用程序都会出现此问题。 这就是我们可以控制的。 让我们解决问题,而不是决定谁来解决这个问题,因为我们对此无法控制。

我同意@mattwallington。 您可以要求每个容器映像的每个开发人员都付出额外的努力来进行应用程序级的自我恢复,但是其中很大一部分注定太无知或太忙而无法仔细实施和测试。 最终结果将是一些容器知道如何自我恢复,而许多容器则不知道。 作为用户,您将没有任何工具来管理那些没有的工具。

我想到的一个主意是:Compose可以尝试恢复出现故障的容器,而不是通过延迟容器启动来解决问题。

recover: auto会在2、4、8、16和32秒内将失败的容器重新启动5次,然后完全放弃。

有没有人想到容器依赖容器的概念?

例如:

```#!yml
D b:
图片:mysql

等待:
链接:
- D b
数量:
-/var/lib/docker.sock:/docker.sock
-$ {PWD} /docker-compose.yml:/docker-compose.yml
命令:docker-compose up -d app

应用程式:
图片:myuser / myapp
链接:
- D b
```

这里的基本思想是通过创建专用的可重用服务来解决不具有自我恢复机制的容器的问题,该服务可以发布到Docker Hub,每个人都可以将其注入到其组成中。

我什至愿意为这样的服务/容器/图像制作原型,并让其他人对此有所了解...

@prologic依赖与之通话的服务实际上已启动?

您的db容器可能会对ping作出响应,但在$$$ mysql / psql命令实际可用之前,正在对数据库进行一些启动前清理/初始化。

可以以可配置的方式定义该测试和/或在脚本中将其提供给可重复使用的waitfor类型服务吗?

恕我直言,这是一个非常普遍的问题,每个人都有自己的特定要求。 正如之前在本期中所评论的,我认为docker可以(并且应该)为容器提供一种方法来指定一个简单的命令来检查其自身的就绪状态。 显然,作为开发人员,我们必须特别指出如何检查每个容器的准备情况。

某些用于docker run的可选标志,用于指示内部检查命令,以后再使用链接的特殊标志将其从另一个容器链接时,将非常有用。

就像是:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python 

其中is_ready.sh是一个简单的布尔测试,负责确定何时将容器视为就绪状态。

它也可以是容器手动检查其准备情况的命令。

其中is_ready.sh是一个简单的布尔测试,负责确定何时将容器视为就绪状态。

这意味着每个开发人员都应准备其图像/容器以包含_something_,该_something_可用于检查容器是否已准备就绪。

这使我们回到平方1。 开发人员是负责使他们的容器适应服务中断/启动时间的人员,因为他们是唯一可以告诉“什么”对他们的情况意味着什么的人员?

还是在这里俯视?

我同意。 责任在于开发人员/容器/服务

2015年7月30日,星期四,Sebastiaan van Stijn [email protected]
写道:

其中is_ready.sh是一个简单的布尔测试,它负责
决定何时将容器视为就绪。

这意味着每个开发人员都应准备其图像/容器以
包含_something_,可用于检查容器是否
准备。

这使我们回到平方1。 开发人员负责
使它们的容器能够抵抗服务中断/启动时间,因为
他们是唯一可以说出“什么”对他们的情况意味着什么的人?

还是在这里俯视?

-
直接回复此电子邮件或在GitHub上查看
https://github.com/docker/compose/issues/374#issuecomment -126278215。

詹姆斯·米尔斯(James Mills)/ prologic

电子信箱
W:prologic.shortcircuit.net.au

当然是。 对我而言,唯一真正知道容器何时准备就绪的是自己的容器。 Docker对容器的内容一无所知。 这是一个黑匣子。 它唯一可以做的就是询问容器(如我所建议的那样,在要运行时指定一个自定义操作,或者测试它的任何其他常用方法)。 显然,开发人员是唯一知道他需要什么以及黑匣子内容的人。

是的,没错!

2015年7月30日星期四,adrianhurt [email protected]写道:

当然是。 对我来说,唯一真正知道何时容器是
准备好了自己的容器。 Docker对内容一无所知
一个容器。 这是一个黑匣子。 它唯一能做的就是问
容器(在您要运行时指定了自定义操作,例如
提议的或其他任何常见的测试方式)。 显然是开发商
是唯一知道他需要什么以及那个黑匣子内容的人。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/docker/compose/issues/374#issuecomment -126285056。

詹姆斯·米尔斯(James Mills)/ prologic

电子信箱
W:prologic.shortcircuit.net.au

好的-为了解决所有无法解决网络故障的开发中或旧版软件的问题,让我们假设我们毕竟想解决此问题。 我并不是说我们这样做,我只是想对语法,语义和复杂性有所了解。

最低要求集似乎是:

  • 我希望Compose等待启动服务,直到另一个服务“就绪”为止。
  • 我想将“就绪”定义为“正在接受端口X上的TCP连接”或其他。

我们还假设,运行状况检查不会暂时使它进入Docker。

我想知道在一般情况下是否可以通过_等待另一个服务的容器退出_来解决。 然后,您可以将健康检查写为另一项服务。

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db_wait"]

db_wait:
  image: netcat
  links: ["db"]
  command: sh -c "while ! nc -w 1 -z db 5432; do sleep 1; done"

db:
  image: postgres

如果要进行某种自定义运行状况检查,请在“等待”服务中进行定义。 在这里,仅在mydb数据库中存在mytabledb_wait才会退出:

db_wait:
  image: postgres
  links: ["db"]
  command: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

如果您有一个数据库准备脚本首先运行,则可以让它等待:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["prepare_db"]

prepare_db:
  image: prepare_db
  links: ["db"]
  command: ./prepare.sh

db:
  image: postgres

第一种情况(等待容器接受TCP连接)可能很常见,值得立即使用。

web:
  image: mywebapp
  links: ["db"]
  wait_for_tcp: ["db:5432"]

db:
  image: postgres

所有这些都有一个隐含的含义: docker-compose up -d在中间健康检查或准备服务运行时必须阻塞,以便它可以在完成后启动消费者服务。

是。 但是,我认为,泊坞窗本身应提供一种确定容器何时准备就绪的方式,然后compose可以对其进行管理。 然后我们可以直接在docker中提出一个新问题。 对我来说,可能是这样的:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db"]

db:
  image: postgres
  ready_when: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

然后,无需创建新服务作为解决方法

同意给开发人员以灵活性。 同样,暂停容器可能不是开发人员在等待时需要容器执行的操作。 也许有一些自己的初始化需要发生,但是然后等待数据库准备好进行连接。 因此,如果它是一个简单的共享环境变量,则它使开发人员可以根据需要使用它。

共享环境变量将如何更新? 据我所知,启动流程后,您将无法对其进行更改。

您可以在bash会话中更改它们,但是它们不会在其他所有层中传播。

为此,必须提交更改并重新启动容器。 在这种情况下,这是毫无用处的。

更重要的是,即使变量可以更改,容器中的进程也需要知道对其进行轮询,这对于表面上该功能的旧版软件来说并不是一个解决方案。

由于我们实际上只在谈论在开发环境中为旧式容器解决此问题,因此我认为可以将其作为位于撰写docker-compose ps -s (按依赖性顺序列出服务,#1077)之上的工具来解决。

使用这两个命令,可以编写执行以下操作的工具:

  1. 运行docker-compose ps -s以依赖性顺序获取服务名称列表
  2. 运行docker-compose up -d --no-recreate <first service from the list>
  3. 运行该服务的“运行状况检查”命令,直到该服务运行正常或达到超时为止。 这可能是HTTP请求,也可能是docker exec调用
  4. 对列表中的每个服务重复2和3
  5. 运行docker-compose logs (如果传递了-d则不执行)

通过这种方式,“运行状况检查”和“等待”配置可以在外部进行编写,但是我认为开发人员不需要提供超出将其作为组成部分本身实现的内容的任何内容。

有什么理由行不通吗?

我很高兴我们将范围限制为旧式容器,这更合理了:+1:

@dnephin从即时角度来看,我认为这很棒,它很灵活,并且Compose不必对旧版容器有一些内置的支持,但是我看到一个类似于@mattwallington所描述的紧迫问题,如果其他容器可以在连接到此服务之前运行一些东西(例如init)? 在那个容器_works_上阻止,但是不是理想的(也就是说,我不确定是否有针对旧容器的理想解决方案)。 那至少可以解决我的问题,现在我必须找到那张票!

+1

能够在docker-compose文件中指定依赖项...

...不使用链接(因为它们与net = host不兼容)。 我本来以为知道事物应该以什么顺序开始是多容器管理工具的工作,或者至少是关心这个问题,就像puppet有其自己的依赖树一样,但是有时用户最了解并且可以覆盖。 在我看来,阅读以上所有内容,唯一的困难是确定容器何时“启动”,以便可以启动依赖关系链中的下一个容器。 这可能是与链接机制完全相同的机制(目前而言)-任何事物总比没有好。 稍后,可以通过docker-compose文件指定“满足依赖项”的澄清-这种依赖关系对于容器正在运行很重要

depends_on:
  container: foo
  requires: running

或此依赖性很重要,容器TCP端口正在侦听。

depends_on:
  container: foo
  requires: listening

说这是docker-compose之上的某些外部工具或脚本的工作,无异于说docker-compose对在同一台机器上运行2个或更多容器的编排没有真正的兴趣或责任。 那么它的目的是什么?

我本来以为知道什么东西应该以什么顺序开始是多容器管理工具的工作或至少要关注的问题

不,不一定。 出于在该线程中已经多次阐述的原因,我相信只有两个借口将这项工作交给容器管理工具:

  1. 您正在运行的现成容器映像对于它们所依赖的上游服务的不可用性没有弹性,并且由于技术或业务原因,您不能更改或扩展它们。
  2. 您纯粹在开发环境中运行软件,而不是在生产环境中运行软件,与实施弹性相比,您有更好的东西可以花时间在上面。

但是,如果您拥有对软件的控制权,并计划将其部署到生产环境中,则即使您已经仔细定义了准备条件,也不能指望仅依靠正确顺序启动事物的外部工具。 这不是根本问题的解决方案,一旦出现网络故障,您的系统就会崩溃。 充其量,每次发生时,您都必须自动重新启动所有Web前端容器,我看不到任何人都可以接受的停机时间。

同意您的意见,精心编写的软件将可以应对网络中断。 我认为我们都同意并非所有软件的编写都很好,并且开发人员并不总是考虑所有可能的用例。

也许我想在主机PID名称空间中运行一个运行JVM的客户端容器,以及另一个要附加到它的监视工具,以便它们可以监视另一个,而我仅受支持并被许可通过供应商授权的映像运行这种工具。 。 如果监视工具监视现有的JVM,那么它们以什么顺序(显然)启动就很重要。 可能有数百个用例对顺序很重要,一些涉及网络(已经提到了mysql,elasticsearch,服务发现集群),但是一些涉及其他可以通过有效使用不同名称空间共享的用例。

因此,我绝对同意用例(1),在某些情况下,您无法更改容器

而且,只要工具本身涉及多个要素,它就会立即遇到订购问题。 如果排序很重要,并且保证订单的唯一方法是在docker-compose周围编写bash脚本以首先添加内容,然后再添加其他内容,则docker-compose可能甚至不存在于链中,除了事实JSON / YAML比cmdline参数更漂亮。

恕我直言,最终,对于一堆用例来说,工具是有用的还是没有用。 Docker-compose对于同一主机上多个容器的无序启动显然非常有用。 如果有足够的人员和足够的用例来进行订购,而某个工具不能解决问题,那么人们只会将这些用例转移到其他地方,这是很可惜的。

无论如何,我看到我只是在重新整理旧的参数,所以现在就从该线程退出。

这是在许多不同的开发主题上的数千个线程中陈述的同一论点:“如果每个人都正确地编写了所有代码,那么就不需要这样,那就不要这样做”。 这相当于说:如果世界上所有的人都停止燃烧化石燃料或使用电力,我们就可以解决造成我们星球的问题。 你是对的。 如果所有应用程序都做了应有的工作,我们就不会在这里谈论这个。 但是我们生活在地球上。 这个地方的生物种类繁多,往往需要艰难地学习。 那些仅仅因为建立“最小可行产品”而使公司落地的人,以祈祷他们将获得下一轮资金或下一天可能会允许他们构建他们希望的版本的下一个客户的产品。 。

世界不是现在,也永远不会是完美的,因此我们只能做自己能控制的事情。 在这种情况下,我唯一能控制的就是说服所有人(也就是开发我绝对喜欢的工具的人,如果只有这个功能,他会发疯)。一种可以处理我们所生活的世界中存在的软件的方法。不是我们希望我们所生活的软件。

2015年7月31日上午3:42,Aanand Prasad [email protected]写道:

我本来以为知道什么东西应该以什么顺序开始是多容器管理工具的工作或至少要关注的问题

不,不一定。 出于在该线程中已经多次阐述的原因,我相信只有两个借口将这项工作交给容器管理工具:

您正在运行的现成容器映像对于它们所依赖的上游服务的不可用性没有弹性,并且由于技术或业务原因,您不能更改或扩展它们。

您纯粹在开发环境中运行软件,而不是在生产环境中运行软件,与实施弹性相比,您有更好的东西可以花时间在上面。

但是,如果您拥有对软件的控制权,并计划将其部署到生产环境中,则即使您已经仔细定义了准备条件,也不能指望仅依靠正确顺序启动事物的外部工具。 这不是根本问题的解决方案,一旦出现网络故障,您的系统就会崩溃。 充其量,每次发生时,您都必须自动重新启动所有Web前端容器,我看不到任何人都可以接受的停机时间。

-
直接回复此电子邮件或在GitHub上查看。

+1 @aanand。 这不是您要限制的范围。 该功能(如果完成)需要成为人们可以信赖的东西。 他们已经为“耐用的”基础架构进行了很长时间的编码,而转换群众需要很长时间。 他们会在很长一段时间内使用它。

我想重申一下,我们还没有排除实现某些方法来减轻容器之间的启动时间依赖性的问题-我昨天在这个线程中草拟了一个可能的解决方案。

但是,我希望我们在同一页面上讨论此功能的用途,将解决的问题,不会解决的问题以及可以依靠的程度。 这就是我要尝试的感觉。

此外,考虑到存在的准备就绪状态的多种定义(“容器已启动”与“容器正在接受TCP连接”与“自定义运行状况检查通过”),以及每种实现的不同复杂性,我想比较一下多少人将从每个人的开箱即用支持中受益。

同步服务的常用技术是使用etcd或类似工具的“服务注册表”。 如何使用wait_for_service:

web:
  image: mywebapp
  links: ["db"]
  wait_for_service:
    type: etcd (or consul, or zk)    -- or use swarm type notation
    addr: http://my.etcd.com/
    path: postgres.service

db:
  image: postgres

compose并不知道该服务是否真的准备就绪,但是它可以在注册表中查找数据并基于此启动依赖容器。 服务(例如postgres)的职责是将其可用性发布到注册表,因此对于旧版应用程序,某种包装脚本可以做到:启动应用程序,观察端口上线,然后发布到注册表。

嗨,@ aanand。

我今天在Twitter上与

具体来说,当用许多小的dockerized组件构建分布式系统时,我发现需要进行集成测试,以测试所有主要界面应用及其依赖项,然后对它们进行各种测试,然后再将其全部拆除。

这就导致了例如Riak与使用它的所有其他事物相比需要花一些时间才能开始的问题。

我不会特别反对大家都说“容器开始异步,处理它”,但是围绕每个服务进行设计以公开一致的运行状况检查至少会包含对每个服务一个实现的“处理”,而不是拥有代码处理依赖于该服务的每个应用程序中的重试连接。

定义自己的健康检查的服务也有利于监视。

@elliotcm同意两点。

为了在CI上进行测试,我们构建了一个小的实用程序,可以在Docker容器中使用它来等待链接服务准备就绪。 它会自动从其环境变量中查找所有链接的TCP服务,并反复并发尝试建立TCP连接,直到成功或超时为止。

我们还写了一篇博客文章,描述了我们为什么要构建它以及如何使用它

@meeee看起来非常好用! 您能否向我们展示其旋转方式的示例? 特别是与docker-compose一起使用?

今天我遇到了一个比较棘手的案例,这是我第一次启动mysql容器。 容器在首次运行时会自行引导,并在配置数据库守护程序后重新启动。 这导致我的端口可用性检查过早触发,并且相关容器已启动但无法连接。 这是在CI环境中,我们希望从头开始完全设置一切。

我非常渴望能够支持某种等待/检查行为,但是这里有一些欺骗性的棘手案例。 我很高兴参加讨论。

@prologic您所要做的就是在运行应用程序或测试之前,根据其他服务/容器在Docker容器中运行waitforservices命令。 它会找到所有链接的服务并运行,直到可以连接到它们或经过一定时间(默认为60秒)。 二进制退出后,只需运行依赖于其他服务的所有代码即可(尽管您可能想检查退出状态)。

@pugnascotia您的数据库服务器仅在引导时才能在localhost上侦听-您必须公开某种指示符以确保容器是否已就绪。 我们不使用MySQL,但是waitforservices与正式的postgres映像完美配合。

@aanand Postgres是实际选择的一个很好的例子,因为等待TCP端口打开是_not_不够的-如果您在这种类型的(docker)场景中这样做,则有时会遇到FATAL: the database system is starting up.这样的错误psql似乎确实是必要的-我使用select version()但也许有更简单的选择。

有一个特定的Maven码头工人插件,具有有趣的等待实现。 目前,我正在使用bash包装器来启动每个服务,而这与使用compose的观点相违背。

使用此方法作为变通办法(不确定它是否防弹):

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
createdbs:
  image: postgres:9.3
  links:
    - db
  command: >
    /bin/bash -c "
      while ! psql --host=db --username=postgres; do sleep 1; done;
      psql --host=db --username=postgres -c 'CREATE DATABASE \"somedatabase\";';
    "

我一直在使用与@olalonde类似的方法。 当在/bin/bash -c之后执行的命令中使用单引号时,我还能够利用其他应用程序从链接中重复使用的环境变量,因此我可以使用用户名和密码,而不必将用户名和密码维护为两个的地方。 当我拥有需要启动数据库的服务(例如API)并通过运行查询以检查特定表或记录是否存在来引导正确的数据时,这非常适用。 这也意味着我需要在容器中安装某种客户端以正确查询数据库,但是它可以工作。

+1我对此功能真的很感兴趣

+1依赖项。 我同意原则上架构应足够强大以支持任何启动顺序。 但是,很多时候这样做是不切实际的。

感觉到此功能的退缩源于其他人试图从远方决定体系结构。 他们真的无权这样做。 此功能可以解决耗时的重构。 长期价值不高。 (为了重构而进行重构)。

是的,我说“变通”; 我觉得很脏但是,对我而言,真正的写作是关于使他人提高生产力。 这种简单的配置可以实现这一点。

如果该功能存在于工具中,那么我可以在一分钟内解决我的问题,然后继续增加实际价值。 相反,我把头撞在墙上,试图解决外部依赖项的启动订单问题。

@beardface和其他所有人:具体来说,哪个功能使可以继续开发应用程序?

  1. 是否可以指定服务A必须等待启动才能启动服务B? (请记住,当容器启动但尚未准备好接受连接时,仍然无法解决竞争条件)
  2. 指定服务A必须等待启动,直到服务B接受连接的能力? (请记住,当容器正在侦听但尚未完成初始化时,仍然无法解决竞争条件-例如,postgres容器在启动时创建数据库,使用@rarkins示例)
  3. 是否可以在服务B上定义运行状况检查并指定服务A必须等待启动才能通过服务B的运行状况检查?

@aanand 3号是我的投票。 它使开发人员可以灵活地选择运行状况检查逻辑,而不必再次依赖服务来决定何时开始。 对于此特定用例,更多的开发人员自由度更好。 无法预期将在容器中安装的所有类型的应用程序。

+3

不过,最好包括一些基本的健康检查。 在端口80_上的_http 200 ok之类的东西非常普遍,值得付出努力。

最好有一个“包含电池”的数字3(所以我敢说以上所有内容吗?)

即内置的“容器已启动”,“文件存在”和“端口打开”类型的等待能力,然后是让人们定义自己的“应用程序层”检查的方法。

3得到我的投票

3是更普遍的2是更普遍的1。每个人都喜欢3,但是2或1对某些人来说已经足够。

投票3

显然是第三个选项。 仅在此讨论中有大量案例。 因此,第一种和第二种选择将是不错的选择,很多人会很高兴,但是问题仍然存在。

投票3

投票3

投票给3。对beta测试也感兴趣。

投票3。

投票给3。对beta测试也感兴趣。

投票3

3

谢谢大家。 您现在可以停止投票-我认为信息很明确。

我会对在https://github.com/docker/compose/issues/374#issuecomment -126312313中提出的设计提出的反馈感兴趣-任何替代设计提议,以及对它们的优缺点的讨论。

wait_for_tcp便捷方法会很有用,但是我不清楚与在@olalonde@mbentley所述的同一个容器中进行单独的健康检查相比,在同一个容器中进行检查是否容易。

像alexec在docker-maven-plugin中所做的事情怎么样? 他明确地将自己的配置设计为类似于docker-compse / fig,到目前为止,它对我的​​项目非常有用。

healthChecks:
  pings:
     # check this URL for 200 OK
     - https://localhost:8446/info
     # check another URL with non-default time out, with a pattern, and non checking SSL certificates
     - url: https://localhost:8446/info
       timeout: 60000
       pattern: pattern that must be in the body of the return value
       sslVerify: false
  logPatterns:
     - pattern that must be in log file
     - pattern: another pattern with non-default timeout
       timeout: 30000

来源: https :

检查tcp连接是否启动不足以知道数据库已启动。 我认为最好有一些命令来检查数据库的运行状况。

@ceagan就是这样。 自定义命令也将很有用。 例如

healthChecks:
  custom:
    # retry this command until it returns success exit code
    - cmd: psql --host=localhost --username=postgres
      sleep: 1s

我还认为checkshealthChecks更好,因为不需要记住大小写约定。 设置wait (开始运行运行状况检查之前需要等待几秒钟), attempts (放弃之前应该检查运行状况多少次), retire可能也很有用。

这朝着马拉松框架解决它的方向发展。 基本依赖项健康检查。 由于启动Docker容器非常流行,因此值得检查选择它们的选项(超时,间隔,响应代码等)以采用它们进行编写。

同样,选项3。

+1

+1

+1

我将坚持使用自定义的等待脚本,但是,这将非常好。

+1

+1

选项3-healthChecks听起来不错。

+1

+1

+1

+3。
要在讨论中添加其他想法, @ aanand提出的选择docker的责任,而不是docker-compose的责任。 如果docker提供了状态信息,则Docker-compose可以以简洁明了的方式实现所有这些用例。 但是码头工人没有。 它似乎遵循立即无状态容器的愿景,并且启动如此之快,以至于此类同步问题都不重要。
就我而言,我追求的是能够针对每种情况为我的服务选择最佳架构的想法。 例如,有时候我想要多个MariaDB实例,每个实例都服务于一个应用程序。 其他时候,我想要一个可以服务多个应用程序的MariaDB实例。 我不希望Docker告诉我什么是最好的,或者我应该做什么。 Docker似乎总是有这种诱惑;)。
我认为最好的解决方案是说服Docker让容器声明有关其自身的任意元数据,并使用该功能让docker-compose找出容器是否被视为“就绪”,以便其他人可以依赖。
至于“单个数据库多个应用程序”方法,我想要一个定义,例如:

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
app1:
  image: wordpress
  links:
    - db [WP]
app2:
  image: ghost
  links:
    - db [GST]

Docker Compose将启动“ db”,并询问其元数据(与Docker Compose相关)。 从yml文件中,它知道“ app1”期望“ db”是“ wordpress ready”(意味着不仅接受连接,而且还包含必需的对象)。

对于如何解决这种情况,我没有简单的解决方案。 我目前分两个步骤手动进行操作:一个自定义的postgresql-bootstrap映像,在其中创建db,并使用数据库用户访问它。 以及一个自定义的liquibase-postgresql映像,以从Wordpress容器提供(或从中提取)的DDL生成数据库对象。 只有这样,我才能启动“ app1”。
如果“基础结构”容器服务于不同的应用程序,则这迫使我将容器分为“基础结构”和“应用”组。

Docker Compose希望像Docker本身一样无状态。 我不知道它是否真的有用,是否有可能。

为选项3 +1

+1

为选项3 +1。

为选项3 +1

为选项3 +1

这个问题已经存在了一段时间,如何解决?

@ bweston92我认为状态是@aanand在此线程的前面提出了一个解决方案,正在寻找

任何其他设计方案,以及对它们的优点/缺点的讨论。

就我个人而言,我认为@aanand提出的解决方案很有道理。 这对我来说似乎很明确,同时也很灵活。 它可以满足我的需要,例如等待TCP端口打开或仅等待固定的时间。

我的用例仅用于测试。 如果在创建数据库之前启动测试,我的测试将失败,因此我将其命令更改为bash -c "sleep 2; python manage.py test --keepdb" ,如下所示:

db:
    image: postgres:9.5
test:
    build: .
    command: bash -c "sleep 2; python manage.py test --keepdb"
    volumes:
        - ./test_project:/app
    links:
        - db
        - selenium
    environment:
        - EXTERNAL_TEST_SERVER=http://testserver:8000/
        - SELENIUM_HOST=http://selenium:4444/wd/hub
selenium:
    image: selenium/standalone-chrome:2.48.2
    links:
        - testserver
testserver:
    build: .
    command: bash -c "sleep 5; python manage.py testserver 8000 --static"
    volumes:
        - ./test_project:/app
    ports:
      - "8000:8000"
    links:
        - db

这样我就可以运行docker-compose run test而无需先启动数据库并等待。

很难说这几天哪个问题是投票支持新的docker compose功能的“正确”地方,该功能允许声明明确的依赖关系,但我认为这是一个有力的选择。 有了新的Docker 1.9网络功能以及迫切需要弃用容器链接,现在没有什么好方法来确保容器A在容器B之前启动-因为如果您使用Docker 1.9用户定义的网络,则可以不再指定容器链接。 坏了

我同意。 是否有获取选项3的时间表? 尽快这样做会很棒。

值得注意的是,依存关系排序实际上不能解决此问题。 此问题也适用于链接。 在许多情况下,容器启动得足够快,您可能不会注意到问题,但是问题仍然存在。

解决此问题所需的是应用程序感知的运行状况检查。 运行状况检查基本上是一个循环,该循环将重试某些操作,直到执行以下操作:操作成功或超时。 对于HTTP服务,这可能会发出http请求,直到您获得2xx代码为止。 对于数据库,它可能正在连接并从表中选择。

无论如何,它都是特定于应用程序的,因此需要由开发人员定义。 如果我们要从https://github.com/docker/compose/issues/374#issuecomment -135090543实现选项3,您仍然需要实现此运行状况检查逻辑。

已经在本期中多次提到(https://github.com/docker/compose/issues/374#issuecomment-53036154,https://github.com/docker/compose/issues/374#issuecomment-71342299 ),但要重申一下,今天您可以通过重试连接使应用程序对故障具有弹性来解决此问题。 无论如何,您都需要针对任何生产系统执行此操作。

事实证明,使应用程序能够应对故障的功能实际上与运行状况检查相同。 因此,无论哪种方式,您仍然需要实现相同的逻辑。 唯一的区别是您将其包括在何处。 现在,您可以将其包含在应用程序或入口点脚本中。 通过建议的更改,您将能够在Compose文件中对其进行定义。 无论哪种方式,您仍然必须为每个服务实施运行状况检查。

将其包括在Compose文件中而不是入口点脚本中是否有明显的优势? 也许还有待辩论。

将其放入Compose文件中的最大缺点是它使up速度明显变慢。

借助新的联网功能,我们可以使up并行发生(就像我们为止损,均方根和标度所做的那样)。 每个容器可以立即启动,进行一些初始化,然后等待其依赖项可用。 这使得启动环境非常快。

如果Compose必须等待运行状况检查完成,则启动实际上是顺序的。 容器启动和应用程序初始化不会并行发生,而且一切都变慢了。

由于LB落后,外部受监视等原因,大多数应用程序都会进行运行状况检查。打开一个新应用程序并不困难。 因此,如果compose支持它,那么这是人们可以使用的选择。 这不是强制性的。 在现实世界中,人们必须处理各种各样的应用程序,并且突然之间可以使所有应用程序变得智能的想法是不现实和不切实际的。 入口点的包装器逻辑很难看。 我认为社区中对功能的需求已经足够,正如您所看到的,选项3获得了很多选票。

从我的iPhone发送

2015年11月18日上午11:01,Daniel Nephin [email protected]写道:

值得注意的是,依存关系排序实际上不能解决此问题。 此问题也适用于链接。 在许多情况下,容器启动得足够快,您可能不会注意到问题,但是问题仍然存在。

解决此问题所需的是应用程序感知的运行状况检查。 运行状况检查基本上是一个循环,该循环将重试某些操作,直到执行以下操作:操作成功或超时。 对于HTTP服务,这可能会发出http请求,直到您获得2xx代码为止。 对于数据库,它可能正在连接并从表中选择。

无论如何,它都是特定于应用程序的,因此需要由开发人员定义。 如果我们要实现#374(注释)中的选项3,您仍然需要实现此运行状况检查逻辑。

在本期中已经提到过几次(#374(注释),#374(注释)),但要重申一下,您今天可以通过重试连接使应用程序对失败具有弹性来解决此问题。 无论如何,您都需要针对任何生产系统执行此操作。

事实证明,使应用程序能够应对故障的功能实际上与运行状况检查相同。 因此,无论哪种方式,您仍然需要实现相同的逻辑。 唯一的区别是您将其包括在何处。 现在,您可以将其包含在应用程序或入口点脚本中。 通过建议的更改,您将能够在Compose文件中对其进行定义。 无论哪种方式,您仍然必须为每个服务实施运行状况检查。

将其包括在Compose文件中而不是入口点脚本中是否有明显的优势? 也许还有待辩论。

将其放置在Compose文件中的最大缺点是,它的构成要慢得多。

借助新的网络,我们可以并行进行弥补(就像我们为停止,均方根和规模所做的那样)。 每个容器可以立即启动,进行一些初始化,然后等待其依赖项可用。 这使得启动环境非常快。

如果Compose必须等待运行状况检查完成,则启动实际上是顺序的。 容器启动和应用程序初始化不会并行发生,而且一切都变慢了。

-
直接回复此电子邮件或在GitHub上查看。

@dnephin最终结果是,每个服务都有相同的包装脚本。 我的观点是,有些事情非常普遍(例如80和443上的HTTP 200或5432上的TCP),因此最好将它们与compose一起交付。

当然,在应用程序级别解决所有这些问题很酷,但实际上,您只能控制自己的应用程序,而不能控制所有其他活动部分,如数据库,缓存或消息队列。

我同意@mbdas@jayfk ,并且只会添加:如果对此的阻力是,即使依赖项规范和容器启动的结果排序,也会出现故障,然后使用容器链接和量-from到控制容器的启动顺序永远不会发生—我们要的是,既然新的网络模型意味着不赞成使用链接(并且新的网络模型在字面上不能与链接共存),那么同一个启动链接的订购功能以某种方式还给了我们。 当然,基于链接的容器订购可能发生的任何失败案例仍可能在新的网络模型和容器依赖关系中发生,但是我们都学会了忍受这种情况。

@delfuego :您能否详细说明不推荐使用链接的方式,尤其是替换链接的方式? 链接到一些文档/示例就足够了

@ h17liner :是的,很有趣! 谢谢

虽然我同意@dnephin的观点

在本期中已经提到过几次了,但是要重申一下,今天您可以通过重试连接使应用程序对失败具有弹性来解决此问题。 无论如何,您都需要针对任何生产系统执行此操作。

我不认为这是因为运行测试之类的东西。 如果我只是测试模型是否在Django应用程序中正确保存,则不知道将模型添加到数据库连接有多大意义。

@delfuego我认为您最初在该问题上的正确位置(#686)。 此问题与订购无关,而与启动的人为延迟(已存在订单)有关。 虽然这些事情相关,但它们是独立的问题。

我不同意用户创建的网桥网络中不支持的链接,并且有记录表明该链接已被弃用,通常没有排序。 因此,选项3同时处理订购和何时开始发行。

从我的iPhone发送

2015年11月19日上午8:14,Daniel Nephin [email protected]写道:

@delfuego我认为您最初在该问题上的正确位置(#686)。 此问题与订购无关,而与启动的人为延迟(已存在订单)有关。 虽然这些事情相关,但它们是独立的问题。

-
直接回复此电子邮件或在GitHub上查看。

我想提出选项4(有些人可能说它实际上是3的变体)
直到所有启动命令都以0退出代码完成后,容器才准备就绪。 必须有可能在yml文件中为每个容器定义这些启动命令。 这些命令的执行就像您对正在运行的容器使用“ docker exec”一样。 考虑经典单元测试中的setUp()和tearDown()方法。 是的,我们也可以使用“关机”命令。
显然,直到它依赖的所有容器都准备好后,层次结构中的下一个容器才启动。
PS感谢您出色的DockerCon.Eu 2015

HEALTHCHECK指令更加灵活(即可以在以后使用)并且很有帮助。 设置应在CMD或(更好) ENTRYPOINT脚本内完成,通过处理过程信号进行拆除。

我认为问题的症结在于人们希望一个docker-compose up命令调出一个堆栈,而所有内容都可以神奇地工作。

基于所有反馈,很明显有许多针对不同用例的解决方案,但没有“一刀切”的解决方案。

您可以通过执行多个docker-compose命令非常轻松地执行“初始化”任务-我认为这种方法是最通用,最灵活的方法。

例如,我通过一个任务在“代理”容器中运行Ansible剧本,该任务等待我的数据库(MySQL)容器在端口3306上运行。此“代理”容器自动链接到我的“ db”容器在执行以下命令时启动它:

$ docker-compose run --rm agent
Creating db_1

PLAY [Probe Host] *************************************************************

TASK: [Set facts] *************************************************************
ok: [localhost]

TASK: [Message] ***************************************************************
ok: [localhost] => {
    "msg": "Probing db:3306 with delay=0s and timeout=180s"
}

TASK: [Waiting for host to respond...] ****************************************
ok: [localhost -> 127.0.0.1]

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0

之后,我可以运行docker-compose up知道db容器可以完全运行。

这是一个支持此功能的简单docker-compose.yml文件:

...
...
db:
  image: mysql
  hostname: db
  expose:
    - "3306"
  environment:
    MYSQL_DATABASE: xxx
    MYSQL_USER: xxx
    MYSQL_PASSWORD: xxx
    MYSQL_ROOT_PASSWORD: xxx

agent:
  image: cloudhotspot/ansible
  links:
    - db
  volumes:
    - ../../ansible/probe:/ansible
  environment:
    PROBE_HOST: "db"
    PROBE_PORT: "3306"

“代理”容器在/ansible装入的卷中运行名为site.yml的剧本,如下所示:

- name: Probe Host
  hosts: localhost
  connection: local
  gather_facts: no
  tasks: 
    - name: Set facts
      set_fact: 
        probe_host: "{{ lookup('env','PROBE_HOST') }}"
        probe_port: "{{ lookup('env','PROBE_PORT') }}"
        probe_delay: "{{ lookup('env','PROBE_DELAY') | default(0, true) }}"
        probe_timeout: "{{ lookup('env','PROBE_TIMEOUT') | default (180, true) }}"
    - name: Message
      debug: msg="Probing {{ probe_host }}:{{ probe_port }} with delay={{ probe_delay }}s and timeout={{ probe_timeout}}s"
    - name: Waiting for host to respond...
      local_action: >
        wait_for host={{ probe_host }}
        port={{ probe_port }}
        delay={{ probe_delay }}
        timeout={{ probe_timeout }}
      sudo: false

单个docker-compose up目标的一种解决方案可能是在docker compose中引入“工作流”功能,并包括一个可选的工作流规范文件,该文件通过指定一个或多个docker-compose命令来实现更复杂和受控的编排方案应该执行的“任务”:

# The default workflow, specified tasks will be run before docker-compose up
# The "up" task is implicit and automatically invoked for the default workflow
# The "up" task is explicit for custom workflows as some workflows may not want docker-compose up
default:
  tasks:
    - run --rm agent
    - up

# Custom workflows that can be invoked via a new docker-compose command option
# This example:
# 1. Runs agent container that waits until database container is up on port 3306
# 2. Runs Django database migrations from app container
# 3. Runs Django collect static task from app container
# 4. Runs test container that runs acceptance tests against linked app container
# Does not execute a docker-compose up afterwards

test:
  tasks:
    - run --rm agent 
    - run --rm app manage.py migrate
    - run --rm app manage.py collectstatic --noinput
    - run --rm test

今天,我使用Makefile实现了上述目标,Makefile提供了针对不同场景定义自己的工作流的高级能力。

可以将“工作流”功能或类似功能引入docker compose,这将是一个非常通用且灵活的解决方案,可以解决此特定问题,甚至更多。

同意,问题在于人们期望docker-compose足以满足生产部署的需要。 就我个人而言,我认为要实现这一目标还需要很长的时间,而Kubernetes / Helm似乎离这一目标更近了。

@olalonde ,请确保我们愿意将其准备就绪以供生产使用...但是我们将采用它来支持重要的现有功能,鉴于容器链接的弃用,除非将其复制到新用户,否则它将消失-created-networks模型。 (再次,也许此请求与该特定问题并不完全吻合-我仍然不清楚是否只是让容器启动命令“属于”此处还是有问题的#686 ...)

使用HEALTCHECK Docker指令, depends_on功能可以等待容器启动(无运行状况检查)或运行状况检查脚本成功退出(退出代码0 )。 这非常灵活(您可以定义任意逻辑),并将运行状况检查逻辑保留在它所属的位置(在检查的容器内)。

@delfuego即使对于开发和测试,此功能也将有所帮助。 就个人而言,我希望能够执行docker-compose run test并使其工作而无需事先提供服务并手动等待。 尽管这是可行的,但这只是使项目开始变得更加痛苦,并增加了测试失败的更多方式。

+1

我认为解决方案需要一种中间方法-组合永远无法考虑可以认为应用程序可用与否的所有不同方法。 健康检查的想法对不同的人来说意味着不同的事情,可能不会像“是否检查”那么简单。 在生产中,如果容器显示出异常长的响应时间,即使它通过了任何HTTP检查,也可能将其关闭。

因此,我觉得对HTTP响应,打开的端口,创建的文件或发出的日志行的基本支持应该足以进行开发。 任何比这还先进的东西几乎都会立即成为特定于应用程序的东西。 我也喜欢鼓励开发人员使应用程序堆栈的各个部分更强大的想法。

@pugnascotia,谢谢,这是建设性的意见,也是一种合理的做法(“两全其美”?)

当前讨论的解决方案似乎并没有真正解决原始报告的问题,该问题要简单得多……这不是在等待服务可用,而是在等待服务退出。

我有一个用例,其中有两个暴露相同端口的容器。 第一次运行15-60秒,然后退出。 然后,第二个服务应该启动。 今天,在组合过程中没有(明显的?)方法可以执行此操作,因为它将检测端口冲突并退出。 甚至没有“重启:总是”的解决方案。

是的,Compose不是针对该用例设计的。 Compose专注于运行时环境,而不是构建管道。 我认为这不是最初报告的问题。

有人要求提供更多面向构建的功能,但是我认为它们对于组合没有任何意义。 这两个功能有很大的不同,试图使它们适合相同的配置格式可能会导致很多混乱和糟糕的用户体验。

@ewindisch您的用例可以概括为运行一系列批处理作业。 在这种情况下,Compose很有用(尽管并未为其设计),因为它维护了服务之间的依赖关系,例如那些链。 但是它不处理排序,恕我直言,它不应该处理,因为Compose在容器的“外面”并且不知道容器要执行的处理是什么。

Compose文档的这一部分涵盖了为何Compose没有此功能的问题:

https://docs.docker.com/compose/faq/#how -do-i-get-compose-to-wait-for-my-database-to-be-ready-before-starting-my-application

但是这些页面根本没有提到该问题:

https://docs.docker.com/compose/django/
https://docs.docker.com/compose/rails/
https://docs.docker.com/compose/wordpress/

至少,这些页面应包含一个确认,即Compose不会等待数据库容器准备就绪。 它们还可以包括处理方法的示例。

@ewindisch实际上,这正是我在https://github.com/docker/compose/issues/374#issuecomment -126312313中提出的假设-假设_that_问题的解决还为用户提供了解决启动顺序问题的工具(如果不是这样的话,则需要长期的弹性)。

如果还有其他人,我仍然有兴趣探索该解决方案空间。

我是。

我也是。

+1

+3

+1

+1用于实现https://github.com/docker/compose/issues/374#issuecomment -126312313解决方案。

大规模投票!
当前,这会影响在容器中运行但依赖于docker事件的工具的使用(例如jwilder / nginx-proxy)。 我这样做的方法只是docker-手动组成侦听器,然后再运行所有其他容器(这将docker-compose的所有美感破坏为单个入口点)。

@meetmatt您是否尝试过随后运行jwilder / nginx-proxy? 起始顺序无关紧要,它会在启动时拾取现有的(运行中的)容器

+1

+1

我真的很想看到一个基于渠道的透明解决方案。 基本上像libchan。 这样,如果我查询数据库,则请求将被缓冲直到数据库准备就绪。

我真的不认为在分布式系统中加载顺序是足够的解决方案。 例如,如果您需要重新启动数据库但其他服务可能会崩溃,该怎么办? 真正的解决方案也可以处理此用例。

值得一提的是,执行管道的可预测性是我们工作的关键。 +1

+1

我肯定错过了什么。

为什么没有人提倡在docker run中添加“等到”(docker引擎本身)。 在我能想到的所有情况下,从属容器都知道何时“准备就绪”,但Docker不遵守。

在原始情况下(mysql加载大量数据集和alfresco),mysql容器可以返回或发信号通知就绪,并且alfresco容器直到那时才启动。

我想根据我的决定准备好后运行任意逻辑并向docker发出信号(例如,当日志中出现特定消息时-> signal CONTAINER_UP)。

net: "container:[name or id]"为什么不订购我的容器的启动程序? 我不得不放弃links因为它将被弃用,并且我希望整个堆栈使用net: "host"网络。 不幸的是, links不允许这样做。 是否有另一种方法可以更改容器的启动顺序,或者我必须在它们之间共享无用的卷?

更新:

我只是用无用的卷而不是links进行了重新排序:

base:
  build: ./base
  net: "host"
  volumes:
    - /root/lemp_base
phpmyadmin:
  build: ./phpmyadmin
  net: "host"
  volumes_from:
    - base
  volumes:
    - /root/lemp_phpmyadmin
ffmpeg:
  build: ./ffmpeg
  net: "host"
  volumes_from:
    - phpmyadmin
  volumes:
    - /root/lemp_ffmpeg
mariadb:
  build: ./mariadb
  net: "host"
  volumes_from:
    - ffmpeg
  volumes:
    - /root/lemp_mariadb
php:
  build: ./php
  net: "host"
  volumes_from:
    - mariadb
  volumes:
    - /root/lemp_php
nginx:
  build: ./nginx
  net: "host"
  volumes_from:
    - php
  volumes:
    - /root/lemp_nginx

(我从堆栈中清除了其他共享卷,并清除了其他信息,例如container_name,端口,看起来很简单。)

如果我想与net: "container:base ,则在docker-compose build命令上会出现错误消息。

ERROR: Service "mariadb" is trying to use the network of "lemp_base", which is not the name of a service or container.

我对该解决方案不满意的是,每个其他容器都会从base获得$$$ /var/www文件夹中的Web服务器文件。

编辑:
由于某种原因,此堆栈在启动时会删除整个/var/www文件夹。

我的拙见是,任何以Docker Compose结尾的机制都知道容器之间的依赖关系会违反关注分离。 Docker Compose负责运行容器A和B。容器A和B负责其自身的服务。 如果B依赖于A才能正常工作,则B有责任等待A处于工作状态。 正如讨论中所说,这可以通过超时,重试或其他方法来完成,但这是B的问题,而不是Docker Compose或A。SoC对于服务独立性和适当的扩展至关重要。

您的想法3 @aanand有什么工作吗? 最好知道是否有任何进展,这听起来像是一个有前途的开始,即使不是一个完美的解决方案,也可以帮助解决一些非常常见的用例

+1

+1

也许我错了,但是args: buildno:可以订购docker-compose.yml版本2中的容器吗?

我倾向于同意这是Compose中不存在的问题。 @jwilder出色的Dockerize刚刚获得了支持以等待依赖容器的

api:
  build: .
  ports:
   - "8000:80"
  expose:
  - "80"

test:
  build: test
  command: dockerize -wait http://api:80 -wait tcp://db:5432 somecommand -some arg -another arg2
  links:
    - api:api

理想情况下,我们使用Docker Events API自动检测到这一点,但这意味着每个容器还需要访问Docker运行时,这可能是不可行的/我们想要的。

我认为等待应该在撰写之外进行。 在我的开发人员中,我将使用@mefellows建议的内容和timercheck.io状态页面的混合形式。 我的事情会完全满足我的需求,而无需使用RabbitMQ或类似工具。

我们正在使用等待开放端口的shell脚本入口点,超时时间为15秒:

#!/usr/bin/env bash

# wait for db to come up before starting tests, as shown in https://github.com/docker/compose/issues/374#issuecomment-126312313
# uses bash instead of netcat, because netcat is less likely to be installed
# strategy from http://superuser.com/a/806331/98716
set -e

echoerr() { echo "$@" 1>&2; }

echoerr wait-for-db: waiting for db:5432

timeout 15 bash <<EOT
while ! (echo > /dev/tcp/db/5432) >/dev/null 2>&1;
    do sleep 1;
done;
EOT
RESULT=$?

if [ $RESULT -eq 0 ]; then
  # sleep another second for so that we don't get a "the database system is starting up" error
  sleep 1
  echoerr wait-for-db: done
else
  echoerr wait-for-db: timeout out after 15 seconds waiting for db:5432
fi

exec "$@"

这应该可以解决即将到来的(显然迫在眉睫与正在更新的文档) depend_on ,是吗?

不。 depends_on仅可订购。 为了实际延迟另一个容器的启动,将需要某种方法来检测进程何时完成了自身的初始化。

嗯,谢谢您的澄清。 =)

我编写了一个纯净的bash命令行实用程序,名为wait-for-it ,可以包含在docker部署中,以帮助同步服务部署。

对我来说,硬编码任意一组“可用性检查”不是一个好主意。 有许多情况是特定于一种部署的,您永远无法涵盖所有​​情况。 举个例子,在我的多容器应用程序中,我需要等待某个日志消息出现在某个日志文件中-直到容器服务准备就绪。
相反,我需要实现一个SPI。 如果Docker提供了一些最常见用例的示例实现(例如TCP连接),那很好。 但是我需要一种方法来插入我自己的功能并让Docker对其进行调用。
如果我不能可靠地启动和运行容器,则Docker Compose对我整个产品几乎毫无用处。 因此,需要一个稳定且统一的“容器服务准备就绪SPI”。 并且“就绪”不应该是布尔值,因为可能存在更多的就绪级别(例如:“现在可以阅读”和“现在可以写作”)。

@realulim好文章。 我完全同意让我们通过插件定义服务的“就绪”状态意味着什么的想法。 我还认为,为插件设置默认值是一个好主意,该插件仅检查服务是否正在侦听http / tcp连接。 那将覆盖那里的大多数情况。

这是我在入口点文件中想到的。

until netcat -z -w 2 database 5432; do sleep 1; done
# do the job here, database host on port 5432 accepts connections

@kulbida
我对MySQL做了类似的事情。 在这种情况下,“数据库”是撰写文件中的链接。

if [[ "$APP_ENV" == "local" ]]; then
    while ! mysqladmin ping -h database --silent; do
        sleep 1
    done
    # Load in the schema or whatever else is needed here.
fi

此线程中有一些评论声称启动顺序只是应用程序级错误恢复的子集,无论如何,您的应用程序都应处理该错误。 我想举一个例子来说明情况并非总是如此。 考虑一下某些服务是否依赖于群集数据库,以及每当由于崩溃等原因导致仲裁丢失时,您确实不希望从应用程序中自动重试。 例如,如果数据库恢复需要一些手动步骤,并且在执行这些步骤之前您需要服务保持明确关闭,则可能是这种情况。

现在,应用程序的错误处理逻辑可能与启动逻辑大不相同:

  • 如果数据库由于我们刚刚启动而关闭,请等待它变得可用。
  • 如果数据库因崩溃而关闭,请记录严重错误并死亡。

这可能不是最常见的情况,但是您偶尔会看到这种模式。 在这种情况下,通常使用群集来解决“网络不可靠”的问题,该问题改变了一些期望,在该期望中应在应用中重试错误条件。 群集崩溃可能很少发生,并且自动重新启动它们可能会带来足够的风险,因此手动重新启动服务比在应用程序中重试更可取。 我怀疑还有其他情况可能会挑战何时重试的假设。

更笼统地说,我声称启动顺序和错误处理并不总是等效的,并且它适合框架提供(可选)功能来管理启动顺序。 我确实想知道这是否属于docker-engine,而不是compose。 无论docker是否启动,无论何时启动,都可能需要它。

在提案https://github.com/docker/docker/issues/21142中,有关docker引擎存储库的讨论开始,以增加对运行状况检查的支持。 一旦获得此支持,Compose就有可能提供一种配置它的方法,并将其用于延迟启动。

如何使用文件系统检查文件是否存在?

ready_on: /tmp/this_container_is_up_and_ready

这样,由容器开发人员决定何时启动,但是compose可以等到容器声明自己准备就绪。 这是一个明确的约定,但是可以轻松地将其作为不具有此行为的图像的附加层添加。

对健康检查的内置支持会很好; 同时,这是我在本地docker-compose设置中工作的hack:

    nginx:
        image: nginx:latest
        command: /bin/bash -c "sleep 2 && echo starting && nginx -g 'daemon off;'"
        ...

(在生产中,我的应用使用proxy_pass代理一些已经在运行的上游服务器;在本地开发和测试中,我启动了这些的docker实例,而nginx需要稍等片刻才能启动,否则daemon off东西将nginx保留在单个进程中,否则Docker将在父进程生成其守护进程子进程后立即停止容器。)

只需加上我的两分钱,如果您碰巧正在使用ANT构建工具,它附带的内置支持可将执行延迟到打开某个套接字为止。

我们的Jenkins CI服务器使用Docker Compose启动项目容器,然后从主容器中运行ANT,如下所示:

docker-compose up -d
docker exec -it projectx-fpm-jenkins ant -f /var/www/projectX/build.xml

这是docker-compose.yml文件中的相关配置。 请注意,如上所述,使fpm依赖于mysql不足以保证在实际需要MySQL服务时就可以准备就绪。

version: '2'
services:
  nginx:
    build: ./docker/nginx
    depends_on:
      - fpm
  fpm:
    build: ./docker/fpm
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=projectx
      - MYSQL_DATABASE=projectx

但是您可以在ANT任务期间等待它:

<!-- other targets... -->

<target name="setup db">
    <!-- wait until the 3306 TCP port in the "mysql" host is open -->
    <waitfor>
        <socket server="mysql" port="3306"/>
    </waitfor>

    <exec executable="php">
        <arg value="${consoledir}/console"/>
        <arg value="doctrine:database:create"/>
        <arg value="--no-interaction"/>
    </exec>
</target>

@kulbida做到了,谢谢。 更快一些:

while ! nc -w 1 -z db 5432; do sleep 0.1; done

_depends_on_可能会解决此问题。
docker-compose文档中。
表达服务之间的依赖性,这具有两个作用:

  1. docker-compose up将以依赖关系顺序启动服务。 在以下示例中,db和redis将在web之前启动。
  2. docker-compose up SERVICE将自动包含SERVICE的依赖项。 在以下示例中,docker-compose up网站还将创建并启动db和redis。

版本:“ 2”
服务:
网络:
建立:。
取决于:
- D b
-重做
Redis:
图片:Redis
D b:
图片:postgres

@alexch :在客户端性能测试(通过nginx +路由的微服务)上。 Dockerized nginx测试-从极高的负载到接近零的低负载的下降每1-2分钟重复一次。 最终决定以非dockerized Nginx作为VM运行(只是由于巨大的性能差异),也许是网络驱动程序插件/ libNetwork问题。

@syamsathyan depends_on似乎没有帮助。

@skorokithakis@kulbida这是一个很好的解决方案。 不幸的是,默认情况下netcat在我需要连接到数据库的任何服务(包括postgres )中都不可用。 您知道其他替代方法吗?

@nottrobin恐怕不是,我只是将其安装在映像中:/

我的团队正在处理@nottrobin ,请在一两天内通知您!

对于那些最近刚参加bash的人,有一个无netcat的解决方案(灵感来自:http://stackoverflow.com/a/19866239/1581069):

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432'; do sleep 0.1; done

或更详细的版本:

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432' >/dev/null 2>/dev/null; do sleep 0.1; done

完美工作的@typekpb 。 谢谢!

现在,已经按照https://github.com/docker/docker/pull/23218在上游合并了HEALTHCHECK支持-在开始下一个订单之前,可以考虑确定容器何时处于健康状态。 一半的难题解决了:)

现在,已经按照docker / docker#23218在上游合并了HEALTHCHECK支持-在开始执行下一个命令之前,可以考虑使用此方法确定容器的运行状况。 一半的难题解决了:)

看起来不错。 如何在docker-compose.yml上实现它?

看起来不错。 如何在docker-compose.yml上实现它?

另一个难题是让docker-compose监视健康的容器,并使用类似本问题中进一步提到的depends_on语法的东西。 需要使用docker-compose补丁才能正常工作。

另请注意,Docker中的运行状况检查功能当前尚未发布,因此可能需要与Docker / Docker Compose发布周期保持一致。

我编写了一个方法.waitForPort()的js库。 就像前面提到的那样,这可能不适用于所有情况,但对于大多数用例来说都可以。
看我的博客

HEALTHCHECK合并是个好消息。

同时,本文档介绍了该问题和一些解决方案。

@pablofmorales Nope,因为depends_on只是检查容器是否已启动。

一些守护程序需要一些额外的时间来引导自己,并开始监听分配给它们的端口和地址,特别是MySQL。

我仍然认为“ READY_ON”声明仍然是最好的。 它可以决定何时什么时候容器本身已经准备就绪,而不管映像是什么,选择加入是明确的,并且Docker Remote API中的资源路径(容器内)功能可确保所需的更改最少。

容器处于“运行状态”时的行为是此行为应具有的唯一影响。 当READY_ON文件存在时,它只会报告为“ up”。

我认为这是每个人都在讨论的行为的90%。 我认为这里的“健康检查”被归类为两个不同的事件,但试图将其限制为一个。 一个是在准备基础架构时“准备好”事件链,另一个是“健康”以便可以保持基础架构。

“就绪”完全是码头工人提供帮助的合适地方。 至于“健康”,它在系统方面千差万别,我认为应该由容器来应对。

为了更好地替代运行状况检查,您可能希望查看类似于containerpilot的内容,该内容不仅涵盖健康状况,还涵盖服务发现和监视。 https://github.com/joyent/containerpilot

是的,这是一个准确而重要的区别。 但是,容器如何在不使图像变得更加复杂的情况下写入该文件? 在我看来,每个想要使用此容器的容器都需要一个包装脚本。

好吧,无论如何,您都必须启动一个脚本来初始化实例……脚本需要做的最后一件事就是触摸文件。 对我而言,这似乎比尝试在远程计算机上运行exec进行运行状况检查要容易得多。 至少使用触摸文件,可以完全通过API被动地查看它,而无需输入容器的上下文。

我同意,但是许多容器不使用脚本,它们只是安装诸如Postgres或Redis之类的服务,而无需观看即可启动它。

就我而言,我正在使用Kong API Gateway

在运行kong容器之前,我只是检查Cassandra是否正在使用此脚本

while true; do
    CHECK=`kong-database/check`
    if [[ $CHECK =~ "system.dateof" ]]; then
        break
    fi
    sleep 1;
done

检查文件包含此

#!/bin/bash
docker cp cassandra-checker kong-database:/root/
docker exec -i kong-database cqlsh -f /root/cassandra-checker

cassandra-checker只是一个简单的查询

SELECT dateof(now()) FROM system.local ;

可以,但是替代方法是运行状况检查,它需要您仍然必须编写的脚本,因此没有开销上的差异。 这也是一个明确的选择加入,这意味着您要声明自己想要这种行为。 至于某些不运行脚本的东西,您总是可以通过ready_on路径检查pid文件或unix套接字; 不需要脚本。

是的,你是对的。

在许多情况下,检查文件是否存在可能是不错的选择,但是在容器原本不需要容器的情况下强制使用它们的启动脚本是一件令人讨厌的事情。 为什么也不能检查其他非常简单的条件? 等到进程正在侦听特定的tcp端口时特别有用。

这个想法是可选的,因此没有任何强制性。 实际上,您在明确地说出应该做什么。

侦听tcp端口可能不足以告诉容器何时已初始化,因为可能需要运行大量设置数据。 天哪,如果您甚至过tcp太快地连接到postgres容器,也会收到一条错误消息,指出该数据库尚未准备好。

如果我对您的理解正确,那就是“选择加入,否则您将无法使用此功能”。 太好了,如果我需要此功能并且我的应用程序不使用pid文件,则必须使用启动脚本。

对于MySQL(OP的情况),一旦监听,就可以使用了。 他们要确保做到这一点很麻烦,可能是在类似这样的情况下。 我的看法是,可能会列举一小段条件,以便您可以“选择加入”针对这些条件中的任何条件配置就绪检查。 我认为没有理由必须采取一种唯一的方法。

对于mysql来说,一旦监听,就还没有准备好。 在简单的一个节点的情况下,它已经准备就绪,但是如果您有多个节点,那么它肯定还没有准备好。 我理解您所说的“一种唯一的方式”是什么意思,但我认为作为一种基本的抽象方法是完美的。 我将其更多地看作是可以在其中应用所需工具的地方。 哎呀,您的脚本甚至可以与外部服务进行通信,并让它们验证容器,在这种情况下,您的外部服务可能会向您的容器代理发出信号以写入文件。 灵活性

如果您尝试此“条件”列表中的任何事情,总会有情况不起作用。 但是触摸文件将始终有效,因为图像会知道它何时认为已准备就绪(哦,我必须在其他主机上等待,我需要下载文件,我需要确保$ external_service也可用,我可以使用正确,但是由于某些原因,我没有对数据库的正确权限,为什么此映像为只读...等等,等等。

这些脚本已经遍地开花……哎呀,必须编写这些脚本,因为我们以前没有像这样的功能。 因此,放入这样的脚本是最少的,因为它可能已经存在一个脚本。

另一种可能的情况是,您将在该主机上运行诸如Chef或ansible的操作,然后写入文件。

如果这是Docker端检查的问题,则类似;

UPCHECK --port=7474 --interval=0.5s --response="Please log in"

作为记录,我认为文件解决方案有很多优点,但同时也带来了复杂性。
80%的时间中,验证tcp响应是否可以正常工作。

好吧...我想:

UPCHECK --file=/tmp/container_is_ready --interval=0.5s --timeout=2m

是一样的。

我实际上是在重新实现docker-compose,该功能增加了等待特定条件的功能。 它使用libcompose(因此我不必重建docker交互)并为此添加了一堆配置命令。 在这里查看: https :

请注意,代码已完成,但是我等待一些上游问题待解决,然后才能真正使用它。

Goss可以用作延迟容器启动的相当灵活的填充程序,我写了一篇博客文章解释了如何在此处稍作更改即可实现此目的:

Kubernetes有init-containers的概念,我想知道是否compose / swarm将从类似的概念中受益。

+1

我认为最好让您要在容器上公开的服务确定其是否准备就绪或能够公开其服务。

例如,对于PHP应用程序,可能取决于MySQL的连接。 因此,在PHP容器的ENTRYPOINT上,我写了这样的内容。

#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
\$connected = false;
while(!\$connected) {
    try{
        \$dbh = new pdo( 
            'mysql:host=mysql:3306;dbname=db_name', 'db_user', 'db_pass',
            array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
        );
        \$connected = true;
    }
    catch(PDOException \$ex){
        error_log("Could not connect to MySQL");
        error_log(\$ex->getMessage());
        error_log("Waiting for MySQL Connection.");
        sleep(5);
    }
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping

这样,我可以添加任何逻辑以确保所公开的服务(即php)的依赖性已得到解决。

纳宾尼schrieb:

我认为最好让您要在容器上公开的服务确定其是否准备就绪或能够公开其服务。

您当然可以将此行为硬编码到使用您的容器的每个容器中
MySql容器。 但是,如果您的MySql服务中的某些内容发生了变化,那么您就是
更改所有从属容器,更不用说重复编码了
每个都需要。 这不是DRY,没有稳定的合约,因此它将
导致系统变脆。

从软件工艺的角度来看,应该有某种
容器开发人员可以实现的“容器就绪SPI”。 上
另一边应该有一个“容器就绪API”,
服务可以依靠。

乌尔里希

@realulim我同意必须将MySQL容器中的任何更改复制或传播到所有受影响或链接的容器。

但是,如果更改与DB_HOST,DB_NAME,DB_USER和DB_PASSWORD之类的参数有关。 这些可以作为ARG (参数)传递,并由所有相关容器共享。 如果您使用docker-compose.yml文件,则更改发生在一个文件上。

完全同意拥有API来检查容器的就绪状态是解决此问题的真正方法,但我仍然相信公开的服务将是声明此问题的更好人选。

解决方法until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done

@ piotr-s-brainhub从上面的评论中,它提到拥有开放的端口并不意味着服务已准备就绪。

我们是否可以通过日志,端口打开或时间延迟触发可选的就绪状态? 就像是:

ready_when:
  in_logs: `MySQL init process done`
  ports_open:
  - 3306

我只是意识到,可以使用ansible等工具轻松实现等待依赖容器准备就绪的过程。 有人使用过这种方法吗? 您可以轻松地用ansible / chef / puppet替换docker-compose吗? github上的任何项目都证明了这种方法?

注意:我了解编写健壮的服务的重要性,即使该服务目前不可用,该服务也可以运行。 那不是问题。

如今,我用自己编写的工具解决了这个问题: https :

它可以等到给定的资源列表可用,然后继续执行要继续的操作,方法是隐式转到下一个命令或显式调用它。

@djui ,它在等待给定资源时会做什么?

@derekmahar投票。 默认超时为60秒。 每次看不到资源时,它将以1秒的间隔重试。 当前,它不执行并发资源检测,因此它是顺序的,但是事实证明它足够好并且可以修复。

我在以下情况下使用它:

我启动了一个由docker组成的基础架构,然后运行一个集成测试驱动程序。 仅在基础结构中的所有组件都可用之后才使用await启动驱动程序服务; 因此等待最终会调用驱动程序的运行命令。

这是使用make使用新的Docker HEALTHCHECK指令执行此操作的方法:

https://gist.github.com/mixja/1ed1314525ba4a04807303dad229f2e1

[更新:更新要点以处理容器是否以错误代码退出,因为Docker 1.12有点愚蠢地将已停止容器的Healthcheck状态报告为“正在启动”]

感谢@mixja ,很好的解决方案。

@mixja ,不错的解决方案! 这正是我期望开箱即用的功能。 但是现在的问题是,如果您手动启动容器,为什么根本需要docker-compose?

为了进行测试,我使用https://github.com/avast/docker-compose-gradle-plugin ,它也使用Docker运行状况检查-不再需要人为的暂停和更快的构建。

@korya -Docker compose并不是真正的编排工具-它更多地是环境规范和管理工具。 我使用Make来提供基于Docker Compose和Docker(以及其他所需工具)的过程式业务流程。 Make,Docker和Docker Compose的组合非常强大,您可以使用这些构建块实现很多不同的场景。

@mixja好吧,也许你是对的。 但是正如许多人在该线程中指出的那样,在测试环境中非常需要编排功能,并且当您的工具箱中有docker-compose时,很诱人的需要docker-compose中的这种功能。

实际上,根据文档“ Compose是用于定义和运行多容器Docker应用程序的工具”。 尽管并没有说compose是一个编排工具,但我认为从用户(例如我自己)的角度来看,很自然地期望“一种用于定义和运行多容器Docker应用程序的工具”支持受管容器之间的基本依赖关系管理。盒子外面。

我并不是说该工具必须支持它。 我要说的是,期待它是很自然的。 否则,每个人都必须想出超聪明的方法来做到这一点。 实际上,我们使用bash脚本执行与您的makefile相似的操作。

@mixja @korya我想提高我的工具的await和想问你反馈你的Makefile版本提供了一个缺少/更方便/启用了await

看来healthcheck + make版本似乎是一个“全局”视图,没有一个容器知道全局状态(但是makefile知道),而await是一个“本地”视图,每个启用的容器都知道(仅)它需要知道的内容,类似于depends_onlinks 。 此外,您更喜欢为容器提供运行状况检查所需的工具(有时是默认的,例如mysqlshow ),否则不影响Dockerfile。 另外,您似乎不再主要使用docker-compose了,而是主要用于灵活的配置(例如docker-compose up -d mysql应该等效于docker run -d -e ... -v ... -p ... mysql )。

@djui ,您好-这可能是一种哲学观点,但是我认为HEALTHCHECK的整个前提是促进正确的行为-即容器可以提供一种建立容器健康的方法,而无需任何外部依赖。

这绝不会降低外部验证连接的价值,但是我通常会运行一套验收测试来涵盖这一点,因为您想验证连接以及更多(例如,应用程序功能)。 当然,在建立完整的环境以及await工具的范围以及我过去使用的其他方法之前,您通常无法运行此级别的测试(Ansible剧本包装在agent容器)实际上专注于正确地调整环境设置(不是验收测试的最终目标),并且直到现在,它仍然是Docker世界中唯一可用的方法。

有了Docker 1.12,我们现在有了一种反思Docker环境的方法,并且能够使用成熟的构造(即bash / shell机制)来“等待”某种状态,当然,只要我们的容器定义了自己的健康检查。 在利用平台的本机功能并鼓励容器所有者定义自己的运行状况检查时,我看到了更多的价值,而不必依靠历史悠久的外部方法(我已经开始了我的申请流程,这不再是我的问题)了诉诸。

作为一个类似的类比,请考虑AWS CloudFormation以及自动伸缩组和编排滚动更新的概念。 CloudFormation如何知道一个新实例是否准备好“健康”,并且我们可以杀死一个旧实例并转入另一个新实例? 我们是否编写外部运行状况检查,还是依靠实例本身来发出运行状况信号? 答案是后者,这意味着实例所有者可以设置其实例所需的任何成功标准,然后向总体编排系统(即CloudFormation)发送信号,表明实例“状况良好”。

关于您对Docker Compose的评论-该工具可以提供您提到的两个方面。 docker-compose.yml部分是所需的状态组成环境规范,而​​各种docker-compose命令提供了以多种方式与环境交互的能力。 目前,我们需要外部编排工具,因为从根本上说docker-compose不能很好地执行服务之间的依赖关系管理。 随着docker-compose获得本机运行状况检查支持等功能,假设我们能够指定例如在服务之前必须标记为健康,则单个docker-compose up命令的目标将更加现实。它被认为是“启动”,这意味着我们的依赖服务有效地等待,直到依赖关系正常为止。

@mixja感谢您的详细解释。 我认为

利用平台的本机功能,我看到更多的价值

是一个好/要点。 只是等待Docker Compose可以在Depends_on或新密钥中本地使用健康检查。 只是想知道是否/应该比这更进一步,并且如果设置了例如--abort-on-container-exit并且运行时进行的运行状况检查将运行状况检查标签设置为_unhealthy_,则基本上会关闭链接的容器。

对于正在寻找delay功能来运行测试的您来说,可能的临时解决方法:

我有两个docker-compose yml文件。 一种用于测试,另一种用于开发。 所不同的只是在为sut集装箱docker-compose.test.ymlsut容器运行pytest 。 我的目标是运行测试docker-compose ,如果pytest sut容器中的pytest命令失败,请不要运行开发docker-compose 。 这是我想出的:

# launch test docker-compose; note: I'm starting it with -p argument
docker-compose -f docker-compose.test.yml -p ci up --build -d
# simply get ID of sut container
tests_container_id=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut)
# wait for sut container to finish (pytest will return 0 if all tests passed)
docker wait $tests_container_id
# get exit code of sut container
tests_status=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut | xargs docker inspect -f '{{ .State.ExitCode  }}' | grep -v 0 | wc -l | tr -d ' ')
# print logs if tests didn't pass and return exit code
if [ $tests_status = "1" ] ; then
    docker-compose -f docker-compose.test.yml -p ci logs sut
    return 1
else
    return 0
fi

现在,您可以在您选择的任何函数中使用上面的代码(我的称为test ),然后执行以下操作:

test
test_result=$?
if [[ $test_result -eq 0 ]] ; then
    docker-compose -f docker-compose.yml up --build -d
fi

对我来说效果很好,但是我仍然期待看到docker-compose本地支持这种东西:)

+1

也许可以通过允许插件来支持在docker-compose核心之外考虑的事情? 类似于请求#1341,似乎还有一些其他功能可能会有用,但不一定完全符合当前的愿景。 也许支持如#3905提出的插件系统将提供一种方法,使您可以专注于一组核心功能,如果不是这样,则针对特定用例的人可以编写一个插件来处理执行up不同吗?

能够让docker-compose充当我们在docker env安装程序本地存在的所有项目的入口点,这很好,而不是需要添加位于所有人前面的脚本作为默认入口点,而不是人们需要记住在特殊情况下运行脚本。

这是一种使用healthcheck和docker-compose 2.1+

version: "2.1"
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    healthcheck:
      test: mysqladmin -uroot -ppassword ping
      interval: 2s
      timeout: 5s
      retries: 30
  web:
    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

只有在db容器认为运行状况良好之后,此处docker-compose up才会启动Web容器。

抱歉,如果已经提到过,但是我认为没有发布完整的解决方案。

这是PostgreSQL的一种方法。

谢谢@ Silex👍

version: '2.1'
services:
  db:
    image: postgres:9.6.1
    healthcheck:
      test: "pg_isready -h localhost -p 5432 -q -U postgres"
      interval: 3s
      timeout: 5s
      retries: 5

@Silex可悲的是版本为“ 3”,格式为:

    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

我得到ERROR: The Compose file './docker-compose.yml' is invalid because: depends_on contains an invalid type, it should be an array

2.1会继续支持它,不会被弃用。 3.x主要用于群集服务模式(非本地)。

  From: Vlad Filippov <[email protected]>

发送至: docker / compose
抄送:mbdas [email protected] ; 提及提及@ noreply.github.com
发送:2017年3月8日,星期三,11:45
主题:Re:[docker / compose]有没有一种方法可以延迟容器启动以支持具有较长启动时间的依赖服务(#374)

@Silex可悲的是版本为“ 3”,格式为:image: nginx:latest #您的图像
取决于:
D b:
条件:service_healthy
我收到错误消息:撰写文件'./docker-compose.yml'无效,因为:services.auth.depends_on包含无效的类型,它应该是一个数组-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看,或使该线程静音。

2.1会继续支持它,不会被弃用。 3.x主要用于群集服务模式(非本地)。

谢谢!

@vladikoff :有关版本3的更多信息,访问

基本上,将不支持它,您必须使容器具有容错能力,而不是依赖docker-compose。

我相信现在可以关闭。

不幸的是,v3中不再支持该条件。 这是我发现的解决方法:

website:
    depends_on:
      - 'postgres'
    build: .
    ports:
      - '3000'
    volumes:
      - '.:/news_app'
      - 'bundle_data:/bundle'
    entrypoint: ./wait-for-postgres.sh postgres 5432

  postgres:
    image: 'postgres:9.6.2'
    ports:
      - '5432'

等待postgres.sh:

#!/bin/sh

postgres_host=$1
postgres_port=$2
shift 2
cmd="$@"

# wait for the postgres docker to be running
while ! pg_isready -h $postgres_host -p $postgres_port -q -U postgres; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"

# run the command
exec $cmd

@ slava-nikulin自定义入口点是一种常见的做法,几乎是在将应用程序放入容器之前定义和检查所需所有条件的唯一(docker本地方法)方式。

真相是,有很多争论,而且我认为对条件支持的2.x支持与运行状况检查本地集成,是非常需要的支持。 Docker本身并不支持容器的本地容器,而当它支持时,它将不得不再次支持类似的东西,例如kubernetes提供了语义。

Docker 3.x是一系列将组合支持纳入其中的系列,因此在考虑分布式特性的情况下放弃了很多选择。

2.x系列保留了原始的撰写/本地拓扑功能。

Docker必须找出如何合并这两个版本的方法,因为通过减少compose功能集将群集中到compose上并不是一个受欢迎的方向。

2017年5月10日晚上8:15,Slava Nikulin [email protected]写道:

不幸的是,v3中不再支持该条件。 这是我发现的解决方法:

网站:
取决于:
-'postgres'
建立:。
端口:
-'3000'
数量:
-'。:// news_app'
-'bundle_data:/ bundle'
入口点:./wait-for-postgres.sh postgres 5432

postgres:
图片:' postgres:9.6.2 '
端口:
-'5432'
等待postgres.sh:

!/ bin / sh

postgres_host = $ 1
postgres_port = $ 2
cmd =“ $ @”

等待postgres docker运行

一会儿! pg_isready -h $ postgres_host -p $ postgres_port -q -U postgres; 做

&2回显“ Postgres不可用-睡眠”
睡1
做完了

&2 echo“ Postgres已启动-正在执行命令”

运行命令

exec $ cmd
-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看,或使该线程静音。

我能够做这样的事情
// start.sh

#!/bin/sh
set -eu

docker volume create --name=gql-sync
echo "Building docker containers"
docker-compose build
echo "Running tests inside docker container"
docker-compose up -d pubsub
docker-compose up -d mongo
docker-compose up -d botms
docker-compose up -d events
docker-compose up -d identity
docker-compose up -d importer
docker-compose run status
docker-compose run testing

exit $?

// status.sh

#!/bin/sh

set -eu pipefail

echo "Attempting to connect to bots"
until $(nc -zv botms 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to events"
until $(nc -zv events 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to identity"
until $(nc -zv identity 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to importer"
until $(nc -zv importer 8080); do
    printf '.'
    sleep 5
done
echo "Was able to connect to all"

exit 0

//在我的docker compose文件中

  status:
    image: yikaus/alpine-bash
    volumes:
      - "./internals/scripts:/scripts"
    command: "sh /scripts/status.sh"
    depends_on:
      - "mongo"
      - "importer"
      - "events"
      - "identity"
      - "botms"

我有类似的问题,但有一点不同。 我必须等待MongoDB启动并初始化副本集。
我在码头工人做所有的程序。 即创建和认证副本集。 但是我还有另一个python脚本,我必须在其中连接到副本集的主节点。 我在那里出错了。

docker-compose.txt
Dockerfile.txt
并且在python脚本中,即时通讯试图做这样的事情
for x in range(1, 4): client = MongoClient(host='node' + str(x), port=27017, username='admin', password='password') if client.is_primary: print('the client.address is: ' + str(client.address)) print(dbName) print(collectionName) break

这样做有任何困难吗?

@patrickml如果我不使用
我需要'cqlsh'来执行我的build_all.cql。 但是,“ cqlsh”尚未准备好……必须等待60秒才能准备好。

猫Dockerfile

FROM store / datastax / dse-服务器:5.1.8

USER根

运行apt更新
运行apt-get install -y vim

添加db-scripts-2.1.33.2-RFT-01.tar / docker / cms /
COPY entrypoint.sh /entrypoint.sh

WORKDIR /docker/cms/db-scripts-2.1.33.2/
运行cqlsh -f build_all.cql

用户dse

=============

步骤8/9:运行cqlsh -f build_all.cql
--->在08c8a854ebf4中运行
连接错误:(“无法连接到任何服务器”,{'127.0.0.1':错误(111,“尝试连接到[('127.0.0.1',9042)]。最后一个错误:连接被拒绝”)})
命令'/ bin / sh -c cqlsh -f build_all.cql'返回了非零代码:1

要求= var-lib-libvirt.mount var-lib-libvirt-images-ram.mount

此页面是否有帮助?
0 / 5 - 0 等级