为什么Docker Compose重新启动会使容器输出变得越来越多?

hal*_*fer 6 php docker docker-compose

我正在编写一个基于Docker Compose的Web应用程序,它有许多后台系统 - 一个HTTP API,一个HTTP代理和一个队列.一切都在PHP的Alpine容器中,PHP 5.6或7.0.

我最初在API容器内的Supervisor中设置队列,工作正常.但是,Supervisor/Python使容器比它们应该更胖(80M而不是25M),所以我将队列移动到自己的容器中.它运行大约5分钟并退出以便重新启动,我在Supervisor中使用自动重启系统,所以我已经交换到Docker Compose中的重启系统.我正在使用Compose YAML格式的第2版.

当队列启动时,它会向stdout呈现一条简单的消息:

queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
Run Code Online (Sandbox Code Playgroud)

docker-compose up最初这样做很好.但是,对于每次重新启动,我会收到其中三条消息,然后是五条,依此类推,没有限制.如果我这样做,docker ps则表示只有一个队列正在运行:

halfer@machine:~/proximate-app$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
a9c94558769d        proximate-app       "/tmp/container-st..."   2 hours ago         Up 2 hours          0.0.0.0:8084->8084/tcp   app_instance
7e48d6aec459        proximate-api       "sh /tmp/bin/web-s..."   2 hours ago         Up 2 hours          0.0.0.0:8080->8080/tcp   api_instance
86c564becadf        proximate-queue     "sh /var/app/bin/c..."   2 hours ago         Up About a minute                            queue_instance
20c2145f80e4        proximate-proxy     "sh /var/proxy/con..."   2 hours ago         Up 2 hours          0.0.0.0:8081->8081/tcp   proxy_instance
Run Code Online (Sandbox Code Playgroud)

这是我的撰写文件:

version: '2'
services:

  proximate-app:
    container_name: "app_instance"
    image: proximate-app
    ports:
    - "8084:8084"
    links:
    - proximate-api

  # @todo Remove external ports once everything is happy
  proximate-api:
    container_name: "api_instance"
    image: proximate-api
    ports:
    - "8080:8080"
    links:
    - proximate-proxy
    - proximate-queue
    volumes:
    - proximate-volume:/remote/cache
    - proximate-q-volume:/remote/queue
    # Use time and TZ from the host, could alternatively use env vars and set it
    # manually in the container, see https://wiki.alpinelinux.org/wiki/Setting_the_timezone
    - /etc/localtime:/etc/localtime:ro
    - /etc/timezone:/etc/timezone:ro
    # Should perhaps pass this as a var to docker-compose so as not to hardwire it,
    # but it is fine for now
    environment:
    - PHP_TIMEZONE=Europe/London

  proximate-queue:
    container_name: "queue_instance"
    image: proximate-queue
    restart: always
    links:
    - proximate-proxy
    volumes:
    - proximate-volume:/remote/cache
    - proximate-q-volume:/remote/queue
    environment:
    - PROXY_ADDRESS=proximate-proxy:8081

  # @todo Remove external ports once everything is happy
  proximate-proxy:
    container_name: "proxy_instance"
    image: proximate-proxy
    ports:
    - "8081:8081"
    volumes:
    - proximate-volume:/remote/cache
    environment:
    - PROXY_LOG_PATH=/remote/cache/proxy.log

volumes:
  proximate-volume:
  proximate-q-volume:
Run Code Online (Sandbox Code Playgroud)

相关容器是proximate-queue.

我很确定我的容器本身不对这种奇怪负责.我Dockerfile这样进入:

ENTRYPOINT ["sh", "/var/app/bin/container-start.sh"]
Run Code Online (Sandbox Code Playgroud)

而这只是调用启动脚本:

#!/bin/sh

php \
    /var/app/bin/queue.php \
    --queue-path /remote/queue \
    --proxy-address ${PROXY_ADDRESS}
Run Code Online (Sandbox Code Playgroud)

哪个运行队列进程:

#!/usr/bin/env php
<?php

use Proximate\Service\File;
use Proximate\Service\SiteFetcher as SiteFetcherService;
use Proximate\Queue\Read as QueueReader;

$root = realpath(__DIR__ . '/..');
require_once $root . '/vendor/autoload.php';

$actions = getopt('p:q:', ['proxy-address:', 'queue-path:']);

$queuePath = isset($actions['queue-path']) ? $actions['queue-path'] : (isset($actions['q']) ? $actions['q'] : null);
$proxyAddress = isset($actions['proxy-address']) ? $actions['proxy-address'] : (isset($actions['p']) ? $actions['p'] : null);

if (!$queuePath || !$proxyAddress)
{
    $command = __FILE__;
    die(
        sprintf("Syntax: %s --proxy-address <proxy:port> --queue-path <queue-path>\n", $command)
    );
}

if (!file_exists($queuePath))
{
    die(
        sprintf("Error: the supplied queue path `%s` does not exist\n", $queuePath)
    );
}

echo sprintf(
    "Starting queue watcher (path=%s, proxying to %s)\n",
    $queuePath,
    $proxyAddress
);

$queue = new QueueReader($queuePath, new File());
$queue->
    setFetcher(new SiteFetcherService($proxyAddress))->
    process();
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,这echo sprintf()就是宣布启动的原因,并没有像我这样可以做到这一点的循环.这可能是Docker Compose中的一个错误吗?我docker-compose version 1.9.0, build 2585387在Ubuntu 14.04上使用.

作为参考,Docker Compose stdout看起来像这样(队列中重复的行是可见的):

halfer@machine:~/proximate-app$ docker-compose up
Creating network "proximateapp_default" with the default driver
Creating proxy_instance
Creating queue_instance
Creating api_instance
Creating app_instance
Attaching to proxy_instance, queue_instance, api_instance, app_instance
proxy_instance     | Teeing proxy logs also to /remote/cache/proxy.log
proxy_instance     | [2017-05-10 09:18:42] stdout.INFO: Setting up queue at `/remote/cache/data` [] []
proxy_instance     | [2017-05-10 09:18:42] stdout.INFO: Starting proxy listener on 172.18.0.2:8081 [] []
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
api_instance       | PHP 7.0.16 Development Server started at Wed May 10 10:19:00 2017
app_instance       | PHP 5.6.29 Development Server started at Wed May 10 09:19:10 2017
app_instance       | PHP 5.6.29 Development Server started at Wed May 10 09:19:10 2017
queue_instance exited with code 0
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance exited with code 0
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
queue_instance     | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
Run Code Online (Sandbox Code Playgroud)

我可以尝试的一件事是让应用程序进入睡眠状态,不做任何其他事情,以证明某些奇怪的退出处理程序或某些东西不会造成严重破坏.但是,我希望这会做同样的事情.

更新

我用脚本替换队列,打印时间信息然后睡20秒.这就是我得到的:

halfer@machine:~/proximate-app$ docker-compose up
Creating network "proximateapp_default" with the default driver
Creating proxy_instance
Creating queue_instance
Creating api_instance
Creating app_instance
Attaching to proxy_instance, queue_instance, api_instance, app_instance
proxy_instance     | Teeing proxy logs also to /remote/cache/proxy.log
proxy_instance     | [2017-05-10 11:51:17] stdout.INFO: Setting up queue at `/remote/cache/data` [] []
proxy_instance     | [2017-05-10 11:51:17] stdout.INFO: Starting proxy listener on 172.18.0.2:8081 [] []
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:51:27 +0000. Microtime=1494417087.107185
api_instance       | PHP 7.0.16 Development Server started at Wed May 10 12:51:37 2017
app_instance       | PHP 5.6.29 Development Server started at Wed May 10 11:51:46 2017
app_instance       | PHP 5.6.29 Development Server started at Wed May 10 11:51:46 2017
queue_instance exited with code 0
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:51:27 +0000. Microtime=1494417087.107185
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:51:55 +0000. Microtime=1494417115.178871
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:52:22 +0000. Microtime=1494417142.409513
queue_instance exited with code 0
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:51:27 +0000. Microtime=1494417087.107185
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:51:55 +0000. Microtime=1494417115.178871
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:52:22 +0000. Microtime=1494417142.409513
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:52:49 +0000. Microtime=1494417169.612523
queue_instance     | Hello everyone! Time=Wed, 10 May 2017 11:53:17 +0000. Microtime=1494417197.826749
Run Code Online (Sandbox Code Playgroud)

这似乎表明:

  • 重新启动仅在每次重启时报告
  • 我的任务将花费20秒,但Compose正在缓慢地重新启动它们(接近每30秒).这并没有太多困扰我,但可能是一个有用的观察
  • 在重复行的地方,它们实际上是旧的重启报告.

到底是怎么回事?

hak*_*kre 2

使用 Docker 重启功能来重启服务(长时间运行的队列任务)是完全没有必要的,因为操作系统附带了您需要的一切。

在使用 docker restart 功能之前,您需要重新启动自己的队列。但你写道你退休了Supervisor/Python,因为它在你眼中太胖了(~55M)。

所以我读到你想保持低足迹。

让我们这样做吧。所以我的建议是,从最低限度开始,操作系统。它应该具有队列进程管理所需的一切,因为这是操作系统的常见内容:调用和控制其他进程(以保持列表简洁)。

对于带有 Busybox 且命令和 shell 减少(例如没有 Bash)的 Alpine Linux PHP 映像来说也是如此。

因此,我假设您希望每 5 分钟启动一次 PHP 脚本,然后在我的示例中再次启动它(没有超时也是可能的,即使代码较少)。

docker 镜像附带了执行此操作的超时命令。您可以在 Dockerfile 入口点 sh 脚本中使用它。因此 PHP 被调用并在 300 秒(五分钟)后终止:

# executed by sh command from BusyBox v1.24.2 (2017-01-18 14:13:46 GMT) multi-call binary
#
# usage: ENTRYPOINT ["sh", "docker-entrypoint.sh"]
set -eu

timed_queue() {
  set +e
  timeout \
    -t 300 \
    php -d max_execution_time=301 \
      -f /queue.php -- \
      --proxy-address "${PROXY_ADDRESS}" \
      --queue-path /remote/queue \
    ;
  retval=$?
  set -e
}

timed_queue
while [ $retval -eq 143 -o $retval -eq 0 ]; do
  timed_queue
done
Run Code Online (Sandbox Code Playgroud)

对设置的设置是通过-t 300命令行参数 来完成的timeout。默认情况下,退出状态为 143 (SIGTERM),以防超时。

如果队列脚本成功结束,则返回 0。在这两种情况下,脚本都会再次启动(超时)。

这需要对您的队列脚本进行额外的小更改。您可以通过以下方式使用 die 命令来发出错误信号:

die(
    sprintf("Error: the supplied queue path `%s` does not exist\n", $queuePath)
);
Run Code Online (Sandbox Code Playgroud)

相反,将错误消息输出到标准错误输出并以非零状态代码退出(例如 1):

fprintf(STDERR, "Error: the supplied queue path `%s` does not exist\n", $queuePath);
die(1);
Run Code Online (Sandbox Code Playgroud)

这将使脚本与操作系统兼容。在命令行上,退出状态为零 (0) 表示正常(即die("string")与示例中的字符串消息一起使用),但非零代码显示错误(对于初学者使用 1)。

完成此操作后,您将获得重新启动和超时脚本的功能,几乎没有任何开销。这既体现在文件大小上,也体现在处理时间上。我对此进行了测试,超时时间为一秒,重新启动时仅显示几微秒的非常小的开销:

queue_instance     | info: 2017-05-19T14:00:24.34824700 queue.php started...
queue_instance     | info: 2017-05-19T14:00:25.35548200 queue.php started...
queue_instance     | info: 2017-05-19T14:00:26.34564400 queue.php started...
queue_instance     | info: 2017-05-19T14:00:27.35868700 queue.php started...
queue_instance     | info: 2017-05-19T14:00:28.34597300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:29.34139800 queue.php started...
queue_instance     | info: 2017-05-19T14:00:30.26049500 queue.php started...
queue_instance     | info: 2017-05-19T14:00:31.26174500 queue.php started...
queue_instance     | info: 2017-05-19T14:00:32.26322800 queue.php started...
queue_instance     | info: 2017-05-19T14:00:33.26352800 queue.php started...
queue_instance     | info: 2017-05-19T14:00:34.26533300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:35.26524300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:36.26743300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:37.26889500 queue.php started...
queue_instance     | info: 2017-05-19T14:00:38.27222300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:39.27209000 queue.php started...
queue_instance     | info: 2017-05-19T14:00:40.27620500 queue.php started...
queue_instance     | info: 2017-05-19T14:00:41.27985300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:42.28136100 queue.php started...
queue_instance     | info: 2017-05-19T14:00:43.28252200 queue.php started...
queue_instance     | info: 2017-05-19T14:00:44.28403600 queue.php started...
queue_instance     | info: 2017-05-19T14:00:45.28595300 queue.php started...
queue_instance     | info: 2017-05-19T14:00:46.28683900 queue.php started...
queue_instance     | info: 2017-05-19T14:00:47.28803800 queue.php started...
Run Code Online (Sandbox Code Playgroud)

当您想要运行脚本五分钟时,在您的场景中很可能会忽略细微的差异。

如果进程崩溃(致命错误等),PHP 的退出代码将为 127,因此当容器关闭时您会收到通知。如果 Dockerfile 入口点脚本中存在错误(例如未定义的环境变量),同样如此。

所以如果我理解正确的话,这应该就是你要找的。

根据最终通过CMD或调用的内容ENTRYPOINT,可能会应用更多流程处理。或者可能会进一步应用更多处理。一些参考: