I am writing a web app based on Docker Compose, and it has a number of background systems - an HTTP API, a HTTP proxy and a queue. Everything is in PHP in Alpine containers, either PHP 5.6 or 7.0.
I originally set up the queue in Supervisor inside the API container, which worked fine. However Supervisor/Python makes containers much fatter than they should be (80M rather than 25M) so I moved the queue to its own container. It lives for ~5 minutes and quits in order to be restarted, and I was using the auto-restart system in Supervisor, so I have swapped to the restart system in Docker Compose. I am using version 2 of the Compose YAML format.
When the queue starts up, it renders a simple message to stdout:
queue_instance | Starting queue watcher (path=/remote/queue, proxying to proximate-proxy:8081)
This is fine when I do docker-compose up
initially. However, for every restart, I get three of these messages, then five, and so on, with no limit. If I do docker ps
then it indicates there is only one queue running:
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
Here is my Compose file:
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:
The relevant container is proximate-queue
.
I am pretty sure that my container itself is not responsible for this oddity. My Dockerfile
enters thus:
ENTRYPOINT ["sh", "/var/app/bin/container-start.sh"]
And that just calls a launch script:
#!/bin/sh
php \
/var/app/bin/queue.php \
--queue-path /remote/queue \
--proxy-address ${PROXY_ADDRESS}
Which runs a queue process:
#!/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();
As you can see, the echo sprintf()
is what announces the start-up, and there's nothing like loops that could be doing this on my side. Could this be a bug in Docker Compose? I am using docker-compose version 1.9.0, build 2585387
on Ubuntu 14.04.
For reference, the Docker Compose stdout looks like this (the repeated lines from the queue are visible):
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)
One thing I could try is to get the app to sleep and do nothing else, to prove that some weird exit handler or something is not causing havoc. However, I expect that will do exactly the same thing.
Update
I have replaced the queue with a script that prints time information then sleeps for 20 seconds. This is what I get:
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
That seems to show that:
- Restarts are reported only every other restart
- My task will take exactly 20 seconds, but Compose is restarting them quite sluggishly (closer to every 30 seconds). That doesn't bother me too much, but may be a useful observation
- Where lines are being repeated, they are in fact old restart reports.
What is going on?