├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── custom-php.ini.tpl ├── init.sh ├── laravel-horizon.conf.tpl ├── laravel-worker.conf.tpl └── supervisord-watchdog.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-alpine 2 | 3 | LABEL maintainer="ipunkt Business Solutions " \ 4 | version.image="v4.4" \ 5 | version.php=$PHP_VERSION \ 6 | description="A supervisor configured to run with laravel artisan queue:work or artisan horizon command" 7 | 8 | ENV QUEUE_CONNECTION=redis 9 | ENV QUEUE_NAME=default 10 | ENV LARAVEL_HORIZON=false 11 | 12 | RUN apk add --no-cache coreutils sqlite-dev libxml2-dev curl-dev gmp-dev icu-dev libpng-dev jpeg-dev freetype-dev autoconf imagemagick-dev gcc libc-dev libzip-dev rabbitmq-c-dev make libtool \ 13 | && docker-php-ext-configure gd --with-freetype=/usr/include/freetype2 --with-jpeg=/usr/include \ 14 | && docker-php-ext-install -j$(nproc) bcmath pdo pdo_mysql pdo_sqlite json xml zip curl gmp intl gd soap sockets pcntl \ 15 | && pecl install imagick \ 16 | && pecl install amqp \ 17 | && pecl install redis \ 18 | && docker-php-ext-enable imagick redis 19 | 20 | ADD https://github.com/kelseyhightower/confd/releases/download/v0.10.0/confd-0.10.0-linux-amd64 /usr/local/bin/confd 21 | 22 | RUN chmod +x /usr/local/bin/confd \ 23 | && apk add --no-cache sqlite libxml2 curl gmp icu libpng jpeg freetype libzip imagemagick gcc ssmtp rabbitmq-c \ 24 | # Fix alpine iconv problems part 1 25 | # See https://github.com/docker-library/php/issues/240 26 | && apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community gnu-libiconv \ 27 | && docker-php-ext-enable bcmath pdo pdo_mysql pdo_sqlite json xml zip curl gmp intl gd imagick soap amqp sockets 28 | 29 | # Install pdo if you want to use database queue and install supervisor 30 | RUN apk add --update supervisor && rm -rf /tmp/* /var/cache/apk/* 31 | 32 | # Define working directory 33 | WORKDIR /etc/supervisor/conf.d 34 | 35 | # Use local configuration 36 | COPY laravel-worker.conf.tpl /etc/supervisor/conf.d/laravel-worker.conf.tpl 37 | COPY laravel-horizon.conf.tpl /etc/supervisor/conf.d/laravel-horizon.conf.tpl 38 | COPY custom-php.ini.tpl /opt/etc/custom-php.ini.tpl 39 | COPY supervisord-watchdog.py /opt/supervisord-watchdog.py 40 | 41 | # Copy scripts 42 | COPY init.sh /usr/local/bin/init.sh 43 | 44 | VOLUME /var/www/app 45 | 46 | # Run supervisor 47 | ENTRYPOINT ["/bin/sh", "/usr/local/bin/init.sh"] 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ipunkt Business Solutions OHG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Queue Worker 2 | 3 | A docker image for working with queues being monitored by supervisor as recommended by laravel. 4 | 5 | ## Environment Configuration 6 | 7 | Running with redis you can configure your `QUEUE_CONNECTION` environment variable to match your redis link. In our case the link is called `redis` so the default value will be `redis`. 8 | 9 | The default queue name in laravel is called `default`. So we configured the `QUEUE_NAME` environment variable to this value. 10 | 11 | If you want to use [Laravel Horizon](https://laravel.com/docs/horizon) then you have to set the environment `LARAVEL_HORIZON` to `true`. By default it is `false`. 12 | 13 | Since version v4.1 is it possible to modify the php memory limit. The environment variable `PHP_MEMORY_LIMIT` is by default set to `512` (MB). For unlimited memory usage just set it to `-1`. The queue worker command will also be called with the memory limit given to this value to be consistent. 14 | 15 | For Laravel Horizon you have to configure your memory limit in your `horizon.php` configuration. The default php memory limit has to set within the container by setting `PHP_MEMORY_LIMIT` as well. 16 | 17 | 18 | ## Docker Images 19 | 20 | | php | docker image | 21 | | --- | ------------ | 22 | | 7.0 | ipunktbs/laravel-queue-worker:php7.0-v1.0 | 23 | | 7.1 | ipunktbs/laravel-queue-worker:php7.1-v2.0 | 24 | | 7.2 | ipunktbs/laravel-queue-worker:php7.2-v3.0 | 25 | | 7.3 | ipunktbs/laravel-queue-worker:php7.3-v4.0 - deprecated | 26 | | 7.3 | ipunktbs/laravel-queue-worker:php7.3-v4.1 | 27 | | 7.3 | ipunktbs/laravel-queue-worker:php7.3-v4.2 | 28 | | 7.3 | ipunktbs/laravel-queue-worker:php7.3-v4.3 | 29 | | 7.3 | ipunktbs/laravel-queue-worker:php7.3-v4.4 - recommended | 30 | 31 | ### Changelog 32 | 33 | #### v4.4 34 | 35 | Added: 36 | - pcntl extension 37 | 38 | #### v4.3 39 | 40 | Added: 41 | - amqp extension 42 | 43 | #### v4.2 44 | 45 | Added: 46 | - bcmath extension for php (to support [Laravel Telescope](https://laravel.com/docs/telescope) as well) 47 | - watchdog for supervisord to let the container restart when some of the programs died 48 | -------------------------------------------------------------------------------- /custom-php.ini.tpl: -------------------------------------------------------------------------------- 1 | memory_limit=%%MEMORY_LIMIT%% -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "$QUEUE_CONNECTION" ]; then 4 | QUEUE_CONNECTION="redis" 5 | fi 6 | 7 | if [ -z "$QUEUE_NAME" ]; then 8 | QUEUE_NAME="default" 9 | fi 10 | 11 | if [ -z "$LARAVEL_HORIZON" ]; then 12 | LARAVEL_HORIZON=false 13 | fi 14 | 15 | PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-512} 16 | sed -e "s~%%MEMORY_LIMIT%%~${PHP_MEMORY_LIMIT}m~" \ 17 | /opt/etc/custom-php.ini.tpl > /usr/local/etc/php/conf.d/custom-php.ini 18 | 19 | if [ "$LARAVEL_HORIZON" = "true" ] || [ "$LARAVEL_HORIZON" = "1" ] ; then 20 | cp /etc/supervisor/conf.d/laravel-horizon.conf.tpl /etc/supervisor/supervisord.conf 21 | else 22 | sed -e "s~%%QUEUE_CONNECTION%%~$QUEUE_CONNECTION~" \ 23 | -e "s~%%QUEUE_NAME%%~$QUEUE_NAME~" \ 24 | -e "s~%%MEMORY_LIMIT%%~$PHP_MEMORY_LIMIT~" \ 25 | /etc/supervisor/conf.d/laravel-worker.conf.tpl > /etc/supervisor/supervisord.conf 26 | fi 27 | 28 | exec supervisord --nodaemon --configuration /etc/supervisor/supervisord.conf 29 | -------------------------------------------------------------------------------- /laravel-horizon.conf.tpl: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | 7 | [program:laravel-horizon] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php /var/www/app/artisan horizon 10 | autostart=true 11 | autorestart=true 12 | stdout_events_enabled=1 13 | redirect_stderr=true 14 | 15 | [eventlistener:supervisord-watchdog] 16 | command=/usr/bin/python3 /opt/supervisord-watchdog.py 17 | events=PROCESS_STATE_FATAL 18 | -------------------------------------------------------------------------------- /laravel-worker.conf.tpl: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | 7 | [program:laravel-worker] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php /var/www/app/artisan queue:work %%QUEUE_CONNECTION%% --queue=%%QUEUE_NAME%% --memory=%%MEMORY_LIMIT%% 10 | autostart=true 11 | autorestart=true 12 | numprocs=1 13 | startretries=10 14 | stdout_events_enabled=1 15 | redirect_stderr=true 16 | 17 | [eventlistener:supervisord-watchdog] 18 | command=/usr/bin/python3 /opt/supervisord-watchdog.py 19 | events=PROCESS_STATE_FATAL 20 | -------------------------------------------------------------------------------- /supervisord-watchdog.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | import subprocess 5 | import time 6 | 7 | from supervisor.childutils import listener 8 | 9 | def main(args): 10 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(asctime)s %(levelname)s %(filename)s: %(message)s') 11 | logger = logging.getLogger("supervisord-watchdog") 12 | debug_mode = True if 'DEBUG' in os.environ else False 13 | 14 | while True: 15 | logger.info("Listening for events...") 16 | headers, body = listener.wait(sys.stdin, sys.stdout) 17 | body = dict([pair.split(":") for pair in body.split(" ")]) 18 | 19 | logger.debug("Headers: %r", repr(headers)) 20 | logger.debug("Body: %r", repr(body)) 21 | logger.debug("Args: %r", repr(args)) 22 | 23 | if debug_mode: continue 24 | 25 | try: 26 | if headers["eventname"] == "PROCESS_STATE_FATAL": 27 | logger.info("Process entered FATAL state...") 28 | if not args or body["processname"] in args: 29 | logger.error("Killing off supervisord instance ...") 30 | res = subprocess.call(["/usr/bin/pkill", "-15", "supervisord"], stdout=sys.stderr) 31 | logger.info("Sent TERM signal to init process") 32 | time.sleep( 5 ) 33 | logger.critical("Why am I still alive? Send KILL to all processes...") 34 | res = subprocess.call(["/usr/bin/pkill", "-9", "supervisord"], stdout=sys.stderr) 35 | except Exception as e: 36 | logger.critical("Unexpected Exception: %s", str(e)) 37 | listener.fail(sys.stdout) 38 | exit(1) 39 | else: 40 | listener.ok(sys.stdout) 41 | 42 | if __name__ == '__main__': 43 | main(sys.argv[1:]) --------------------------------------------------------------------------------