13

I'm trying to run the following command after the container is up and running.

php artisan queue:work -n -q &

The "&" is there because the daemon option was deprecated and later removed from Laravel.

However, this breaks my container startup completely.

CMD ["php", "artisan", "queue:work", "-n", "-q", "&"]

How should I do this in a Docker way?

EDIT:

Using docker-compose I added this line to my docker-compose.yml file

command: bash -c 'php artisan queue:work -n -q;'

The container started but did not serve any requests :S

Using this:

command: bash -c 'php artisan queue:work -n -q &; echo "runs"; tail -f /dev/null'

The container stopped after starting up

Final solution

So in the end I thought that maybe the server in charge of delivering the app should not be the one running the queue.

Therefore I spin up another instance of the same docker image with the sole purpose of running artisan queue:work.

Borjante
  • 9,767
  • 6
  • 35
  • 61
  • 2
    spin up another instance of the same docker image with the sole purpose of running artisan queue:work - ANSWER – Bira Aug 14 '19 at 03:22

4 Answers4

16

The queue:work command runs in the foreground, so you should run it that way so the container doesn't exit immediately.

Since the application code in Laravel is the same for running a container as a web application, queue, or scheduler I build one image that I can use in these contexts. I use a bash start script with an environment variable to define a container role, and this is what I run for a queue worker container:

#!/bin/bash

# Defaults to an app server
role=${CONTAINER_ROLE:-app}

if [ "$role" = "queue" ]; then
    # Run queue
    php artisan queue:work --verbose --tries=3 --timeout=90
elif [ "$role" = "app" ]; then
    # Run the web application
    /usr/bin/caddy --agree=true --conf=/etc/Caddyfile
elif [ "$role" = "scheduler" ]; then
    while [ true ]
    do
      php artisan schedule:run --verbose --no-interaction &
      sleep 60
    done
else
    echo "Could not match the container role...."
    exit 1
fi

Also note the infinite while loop and sleep combo to keep the scheduler role running and running the schedule:run command in the background in case the scheduler runs overlap (since they need to run every minute regardless of if the last one finished).

Paul Redmond
  • 683
  • 7
  • 17
  • I'm using this currently, works fine... Just looking for ways to gracefully shutdown queue:work & schedule:run ... Have you found any solution to do this ? – Ahmedul Haque Abid Sep 10 '20 at 21:29
  • I finally found the graceful shutdown for queue. See https://stackoverflow.com/a/63851444/1414178 – Ahmedul Haque Abid Sep 11 '20 at 17:07
  • I know but this is a bit old now, but it seems your answer would potentially be the solution to my problem. Would it be too much to ask if you could add this in context to a docker-compose file? I am not that good with docker... also, is "app" the name of the container? Thank you in advance – Jim Bantanis-Kapirnas Sep 18 '20 at 10:36
  • CONTAINER_ROLE is an environment you set that defaults to `app` (`role=${CONTAINER_ROLE:-app})`. You configure what role you want to container to run as (web server, scheduler, or queue). – Paul Redmond Sep 22 '20 at 16:11
  • There is no need to create this script. You can just rewrite the docker command and call directly what you want. e.g. "docker run .... your_container php artisan queue:work --verbose --tries=3 --timeout=90" – Joao M Sep 15 '21 at 20:15
  • I don't get it. Why make it so complicated? I have only two lines in the shell script file and it works fine: php artisan serve --host=0.0.0.0 php artisan queue:work --verbose --tries=3 --timeout=90 – peeter Feb 19 '23 at 23:30
4

You can't run the queue in the background, otherwise the container stops, as the command has effectively finished. Remove the & and it will stay alive.

However, you can do something like tail -f /dev/null as the final command to keep the container running if you want to run the queue in the background and still be able to attach to the container and access the shell.

4

If you need graceful shutdown for queue, you can follow this.

This is taken from @Paul Redmond's article at Laravel News and extending his docker-entrypoint file so suite my need. After a lot of testing for graceful shutdown I finally was able to do.

First thing in docker-compose.yml file set stop_signal: SIGTERM for your queue service.

  queue:
    image: laravel-www
    container_name: laravel-queue
    stop_signal: SIGTERM
    depends_on:
      - app
    volumes:
      - .:/var/www/html
    ...

Next in the entrypoint.sh file, the main thing is to run the queue:work using the exec command.

#!/usr/bin/env bash

set -e

# Run our defined exec if args empty
if [ -z "$1" ]; then
    role=${CONTAINER_ROLE:-app}
    env=${APP_ENV:-production}

    if [ "$env" != "local" ]; then
        echo "Caching configuration..."
        (cd /var/www/html && php artisan cache:clear && php artisan config:clear && php artisan route:clear && php artisan view:clear)
        (cd /var/www/html && php artisan config:cache && php artisan event:cache && php artisan route:cache && php artisan view:cache)
    fi

    if [ "$role" = "app" ]; then

        echo "Running PHP-FPM..."
        exec php-fpm

    elif [ "$role" = "queue" ]; then

        echo "Running the queue..."
        exec php /var/www/html/artisan queue:work -vv --no-interaction --tries=3 --sleep=5 --timeout=300 --delay=10

    elif [ "$role" = "cron" ]; then

        echo "Running the cron..."
        while [ true ]
        do
          exec php /var/www/html/artisan schedule:run -vv --no-interaction
          sleep 60
        done

    else
        echo "Could not match the container role \"$role\""
        exit 1
    fi

else
    exec "$@"
fi

Your are done. Next time you stop queue service, it will stop gracefully and won't wait 10 seconds for SIGKILL. I think it has to do with the PID 1 thing.

Ahmedul Haque Abid
  • 550
  • 1
  • 5
  • 13
  • 3
    When you stumble over this while looking for a solution that works with Horizon, you might want to check out the [Horizon source code](https://github.com/laravel/horizon/blob/c06e06680a8177a571f502b45824f22ca4ec7ed3/src/Console/HorizonCommand.php#L50) which shows that instead of `SIGTERM`, you should use `SIGINT` to stop the process gracefully. – Namoshek Sep 25 '20 at 11:42
-3

use supervisor

apt-get install supervisor

cd /etc/supervisor/conf.d && sudo nano laravel-worker.conf

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=docker exec php php /path/to/artisan queue:work redis --sleep=3 --tries=5
autostart=true
autorestart=true
user=root
numprocs=8
redirect_stderr=true
stdout_logfile=/var/logs/worker.log
v1p3r
  • 17
  • 2
  • Yes that was an option too, thank you. In the end I thougth that just spinning up and integration and queue server was cleaner. I'll try supervisor on it thought – Borjante May 06 '18 at 09:04