我正在编写一个基于Docker Compose的Web应用程序,它有许多后台系统 - HTTP API,HTTP代理和队列。所有东西都是用Alpine容器中的PHP,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)
我最初做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
这是我的撰写文件:
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:
相关容器为proximate-queue
。
我很确定我的容器本身不对这种奇怪负责。我的Dockerfile
因此进入:
ENTRYPOINT ["sh", "/var/app/bin/container-start.sh"]
这只是调用启动脚本:
#!/bin/sh
php \
/var/app/bin/queue.php \
--queue-path /remote/queue \
--proxy-address ${PROXY_ADDRESS}
运行队列进程:
#!/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();
正如您所看到的,echo sprintf()
是宣布启动的原因,并且没有什么能像我这样做的循环。这可能是Docker Compose中的一个错误吗?我在Ubuntu 14.04上使用docker-compose version 1.9.0, build 2585387
。
作为参考,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)
我可以尝试的一件事是让应用程序进入睡眠状态,不做任何其他事情,以证明某些奇怪的退出处理程序或某些东西不会造成严重破坏。但是,我希望这会做同样的事情。
我已用脚本替换队列,该脚本打印时间信息然后休眠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
这似乎表明:
发生了什么事?
答案 0 :(得分:4)
我总结了我们发现的东西。
累积输出是由于每次退出时都没有删除容器。因此,docker-compose再次启动它,显示来自同一容器的docker logs -f
。这是按预期工作的。
我没有找到一种方法让docker-compose删除一个容器再重新启动它里面相同的docker-compose up
我喜欢你的基于队列的架构,但我认为如果你使用一个长期存在的容器而不是一个启动/退出/启动/退出的容器会更好...你可以做一个简单的{ {1}}在shell脚本中。
任何需要重现此功能的人都可以使用以下docker-compose.yml
while true; do ./queue-app-blabla.php; sleep 20; done
输出:
version: "2"
services:
ui:
image: ubuntu
command: tail -f /dev/null
links:
- queue
queue:
image: ubuntu
command: sh -c "date; echo Sleeping...; sleep 20; echo exit."
restart: always
我建议的解决方案:
queue_1 | Mon May 15 04:32:12 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
docker_queue_1 exited with code 0
queue_1 | Mon May 15 04:32:12 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:32:33 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:32:54 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
docker_queue_1 exited with code 0
queue_1 | Mon May 15 04:32:12 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:32:33 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:32:54 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:33:17 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
queue_1 | Mon May 15 04:33:38 UTC 2017
queue_1 | Sleeping...
queue_1 | exit.
输出:
version: "2"
services:
ui:
image: ubuntu
command: tail -f /dev/null
links:
- queue
queue:
image: ubuntu
command: sh -c "while true; do date; echo Sleeping...; sleep 20; echo loop.; done"
restart: always
IMO,最好是看到容器启动和工作,而不是打开启动/退出容器。 Docker对具有此行为的容器有特殊处理,请参阅docker docs。 “在每次重启之前添加一个不断增加的延迟(前一个延迟的两倍,从100毫秒开始)以防止服务器泛滥。”(但它不适用于您的情况,因为您的容器正在生存&gt ; 10s。
编辑:
检查可能的错误并退出。这可能很有用:
Attaching to docker_queue_1, docker_ui_1
queue_1 | Mon May 15 04:36:16 UTC 2017
queue_1 | Sleeping...
queue_1 | loop.
queue_1 | Mon May 15 04:36:36 UTC 2017
queue_1 | Sleeping...
queue_1 | loop.
queue_1 | Mon May 15 04:36:56 UTC 2017
queue_1 | Sleeping...
queue_1 | loop.
queue_1 | Mon May 15 04:37:16 UTC 2017
queue_1 | Sleeping...
输出:
#!/bin/bash
PERIOD=10 # seconds
while true; do
echo Starting process...
date # the process
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "Error found. Exiting" >&2
exit $STATUS
fi
echo End process. Sleeping ${PERIOD}s
sleep $PERIOD
done
答案 1 :(得分:2)
使用Docker重启功能只是为了重新启动服务(长时间运行的队列任务)根本不需要,因为操作系统带有你需要的一切。
在使用docker restart功能之前,您重新启动了自己的队列。但是你写了退休的主管/ Python ,因为它太胖了(约55M)。
所以我读到它想要保持足迹低。
让我们这样做。所以我的建议是,至少从操作系统开始。它应该包含队列进程管理所需的一切,因为这是操作系统常见的:调用和控制其他进程(以使列表简洁)。
对于带有Busybox的Alpine Linux PHP图像,使用一组减少的命令和shell(例如没有Bash),情况确实如此。
所以我假设您想每5分钟踢一次PHP脚本,然后在我的示例中再次启动它(没有超时也可以,即使代码更少)。
docker镜像附带 timeout 命令,可执行此操作。您可以在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
使用-t 300
命令行参数timeout
完成设置设置。默认情况下退出状态为143(SIGTERM),以防超时启动。
如果成功结束,您的队列脚本将返回0。在这两种情况下,脚本都会再次启动(超时)。
这需要对您的队列脚本进行额外的小改动。您可以按以下方式使用die命令来发出错误信号:
die(
sprintf("Error: the supplied queue path `%s` does not exist\n", $queuePath)
);
而是将错误消息输出到标准错误输出并以非零状态代码(例如1)退出:
fprintf(STDERR, "Error: the supplied queue path `%s` does not exist\n", $queuePath);
die(1);
这将使脚本与操作系统兼容。在命令行上,退出状态为零(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...
由于您希望运行脚本五分钟,因此在您的方案中很可能会忽略细微的差异。
如果进程崩溃(致命错误等),PHP的退出代码为127,因此当容器关闭时您会收到通知。如果Dockerfile入口点脚本中存在错误,则表示相同,例如未定义的环境变量。
所以,如果我理解你的话,这应该是你正在寻找的。 p>
根据最终通过CMD
或ENTRYPOINT
调用的内容,可能会应用更多流程处理。或者可以进一步处理更多处理。一些参考文献: