How to set up Supervisor and Laravel Horizon on Docker - php

I have successfully deployed a laravel application with horizon installed to AWS ECS. However horizon has remained Inactive and I am stuck trying to get it to work as my emails are getting queued and never sent. This is my docker file
FROM php:8.1.4-fpm-alpine3.14
RUN apk update
RUN apk add --no-cache git libzip-dev zip unzip php8-exif supervisor
RUN mkdir -p /usr/src/php/ext/redis; \
curl -fsSL https://pecl.php.net/get/redis --ipv4 | tar xvz -C "/usr/src/php/ext/redis" --strip 1; \
docker-php-ext-install redis;
RUN docker-php-ext-install pdo pdo_mysql zip exif \
&& curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin --filename=composer
WORKDIR /var/www/html
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY horizon.conf /etc/supervisor/conf.d/horizon.conf
COPY . .
RUN chown -R www-data:www-data /var/www/html
RUN composer install --ignore-platform-req=ext-pcntl
RUN chmod 544 startup.sh
CMD ["/usr/bin/supervisord"]
ENTRYPOINT ["./startup.sh"]
I created a supervisord.conf file with this content in it
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
a horizon.conf file with its content below
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/log/horizon.log
stopwaitsecs=3600
and this is what my startup script startup.sh looks like
#!/bin/sh
php artisan migrate
php artisan db:seed
php artisan config:cache
php artisan route:cache
php artisan horizon
php artisan queue:restart
php artisan serve --host=0.0.0.0 --port 80
I am not exactly very knowledgable with supervisor on containers so I need help figuring out why I am not able to get horizon to be active and fix it.

Related

Docker build success but page cannot open in browser?

I am trying to run symfony using docker. This is my dockerfile
# Dockerfile
FROM php:7.2-cli
RUN apt-get update -y && apt-get install -y libmcrypt-dev
ENV COMPOSER_ALLOW_SUPERUSER=1
# install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer --ansi --version --no-interaction
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer --ansi --version --no-interaction
RUN docker-php-ext-install pdo mbstring
WORKDIR /app
COPY . /app
# RUN composer install --no-scripts --no-autoloader --ansi --no-interaction
EXPOSE 8000
CMD php -S localhost:8000 -t public 0.0.0.0:8000
After I build and run $ docker run -it -p 8000:8000 symfony-tutorial I get following response
D:\docker_test\symfony-4-tutorial>docker run -it -p 8000:8000 symfony-tutorial
PHP 7.2.34 Development Server started at Wed Jan 11 16:40:32 2023
Listening on http://localhost:8000
Document root is /app/public
Press Ctrl-C to quit.
But on browser, its empty.
This page isn’t workinglocalhost didn’t send any data. ERR_EMPTY_RESPONSE. There is nothing in source and network tab
Your web server binds to localhost, which means that it'll only accept connections from inside the container.
You need to change
CMD php -S localhost:8000 -t public 0.0.0.0:8000
to
CMD php -S 0.0.0.0:8000 -t public 0.0.0.0:8000

Docker laravel container service exits with Supervisord

I have a laravel 9 application that I want to deploy to AWS ECS. The application uses redis for queue and I have installed horizon. I also installed Supervisor in the docker container but once I deploy my application and start up supervisor in my startup.sh script, my app is never reachable and I notice that both queue worker and horizon start and then exit.
Here's the dockerfile I am working with
FROM php:8.1.4-fpm-alpine3.14
RUN apk update
RUN apk add --no-cache git libzip-dev zip unzip php8-exif supervisor
RUN mkdir -p /usr/src/php/ext/redis; \
curl -fsSL https://pecl.php.net/get/redis --ipv4 | tar xvz -C "/usr/src/php/ext/redis" --strip 1; \
docker-php-ext-install redis;
RUN docker-php-ext-configure pcntl --enable-pcntl \
&& docker-php-ext-install \
pcntl
RUN docker-php-ext-install pdo pdo_mysql zip exif \
&& curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin --filename=composer
WORKDIR /var/www/html
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY . .
RUN chown -R www-data:www-data /var/www/html
RUN composer install --ignore-platform-req=ext-pcntl
RUN chmod 544 startup.sh
ENTRYPOINT ["./startup.sh"]
this is my supervisord.conf file content
[supervisord]
nodaemon=true
loglevel=debug
logfile=/var/www/html/storage/logs/supervisord.log
user=root
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=root
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log
stopwaitsecs=3600
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/horizon.log
stopwaitsecs=3600
and my startup.sh script content is
#!/bin/sh
/usr/bin/supervisord
php artisan migrate
php artisan db:seed
php artisan config:cache
php artisan route:cache
php artisan serve --host=0.0.0.0 --port 80
If I comment out /usr/bin/supervisord and deploy to aws ecs, I am able to reach my app again. So what exactly am I doing wrong with the supervisor that is causing it to break my app and how can I fix it?

Cannot run supervisor specifying custom user

I'm building a Laravel image which runs with the user backend that have the id 1000. This user should allow me to prevent the following error:
File could not be opened in append mode: failed to open stream: Permission denied
The problem's that when I run the supervisor I get this error:
IOError: [Errno 13] Permission denied: '/var/log/supervisord.log'
This is my Dockerfile:
FROM php:8.1.10-fpm-buster
WORKDIR /var/www
RUN docker-php-ext-install bcmath pdo_mysql
RUN apt-get update
RUN apt-get install -y git zip unzip netcat
# Supervisor
RUN apt-get install -y supervisor
# Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Configurations
COPY docker/php/config/supervisor.conf /etc/supervisord.conf
COPY docker/php/config/php.ini /usr/local/etc/php/conf.d/app.ini
# Log file
RUN mkdir /var/log/php
RUN touch /var/log/php/errors.log && chmod 777 /var/log/php/errors.log
# Deploy
COPY /docker/php/scripts/start.sh /start.sh
RUN chmod +x /start.sh
# Add user for laravel application
RUN groupadd -g 1000 backend
RUN useradd -u 1000 -ms /bin/bash -g backend backend
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=backend:backend . /var/www
USER backend
EXPOSE 9000
ENTRYPOINT /start.sh
This is my docker-compose file:
version: '3.9'
services:
php:
container_name: ${APP_NAME}_app
#user: ${CURRENT_UID}
restart: always
build:
context: .
dockerfile: ./docker/php/Dockerfile
volumes:
- ./src:/var/www
env_file: .env
And this is the entrypoint script:
#!/bin/bash
# Run composer install
composer install
# Generate key and clear cache
php artisan key:generate
php artisan config:clear
php artisan config:cache
# Wait db connection
until nc -z ${DB_HOST} ${DB_PORT}; do sleep 1; echo "Wait database ..."; done
# Execute migrations
php artisan migrate --seed
/usr/bin/supervisord -c /etc/supervisord.conf
Last but not least, the supervisor.conf:
[supervisord]
nodaemon=true
loglevel = info
logfile=/var/log/supervisord.log
pidfile=/var/run/supervisord.pid
[group:laravel-worker]
priority=999
programs=laravel-app,laravel-schedule,laravel-notification,laravel-queue
[program:laravel-app]
priority=5
autostart=true
autorestart=true
stderr_logfile_maxbytes=0
stdout_logfile_maxbytes=0
command=/usr/local/sbin/php-fpm -R
stderr_logfile=/var/log/php/php-error.log
stdout_logfile=/var/log/php/php-access.log
[program:laravel-schedule]
numprocs=1
autostart=true
autorestart=true
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan schedule:run
stdout_logfile=/var/log/php/schedule.log
[program:laravel-notification]
numprocs=1
autostart=true
autorestart=true
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan notification:worker
stdout_logfile=/var/log/php/notification.log
[program:laravel-queue]
numprocs=5
autostart=true
autorestart=true
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/var/log/php/worker.log
command=php /var/www/artisan queue:work sqs --sleep=3 --tries=3
Is there any way to fix the permission issue?
This is my supervisord.conf as an example.
I am always adding user=root row. And never got a permission error.
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=root
numprocs=1
redirect_stderr=true
this issue because using USER backend in Docker file, you need to configure supervisor for using same user in Docker file user=backend

Laravel Scheduler creates cache files as root user

I have a Laravel 9 application deployed on GKE. It has some background jobs which I have configured to run using supervisor (I will share snippets of config files below).
The Problem
The problem is when Jobs are run using scheduler or manually using the artisan command, there are cache files created in storage/framework/cache/data path with root user as owner. This causes the issues as errors keep logging with message Unable to create lockable file because all the other folders and files have user www-data which we set in Dockerfile. To fix it, I have to manually run chown -R www-data:www-data . in the above cache path.
Dockerfile
FROM php:8.0-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libonig-dev \
libicu-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
libzip-dev \
libpq-dev \
ca-certificates \
zip \
jpegoptim optipng pngquant gifsicle \
nano \
unzip \
git \
curl \
supervisor \
cron \
nginx
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo pdo_mysql mbstring zip exif bcmath
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install gd
RUN docker-php-ext-configure intl
RUN docker-php-ext-install intl
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
COPY scripts/supervisor.conf /etc/supervisor/conf.d/supervisor.conf
COPY /scripts/nginx/nginx.conf /etc/nginx/sites-enabled/default
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Setup cron job
COPY scripts/crontab /etc/cron.d/scheduler
RUN chmod 0644 /etc/cron.d/scheduler
RUN usermod -u 1000 www-data
RUN usermod -G staff www-data
COPY --chown=www-data:www-data . /var/www
RUN touch /var/www/storage/logs/laravel.log
RUN mkdir /var/www/storage/framework/cache/data
RUN chown -R www-data:www-data /var/www/storage
RUN chmod -R 777 /var/www/storage
RUN composer install --no-interaction
COPY /scripts/entrypoint.sh /etc/entrypoint.sh
RUN chmod +x /etc/entrypoint.sh
EXPOSE 80 443
ENTRYPOINT ["/etc/entrypoint.sh"]
crontab
* * * * * root echo "cron working..." >> /var/log/cron.log
* * * * * root /usr/local/bin/php /var/www/artisan schedule:run >> /var/log/cron.log
entrypoint.sh
#!/usr/bin/env bash
php artisan config:cache
service supervisor start
service nginx start
php-fpm
supervisor.conf
[program:cron]
process_name=%(program_name)s_%(process_num)02d
command=cron -f
autostart=true
autorestart=true
startretries=5
numprocs=1
stderr_logfile=/var/log/cron.log
stderr_logfile_maxbytes=10MB
stdout_logfile=/var/log/cron.log
stdout_logfile_maxbytes=10MB
Things I have tried so far
I have tried changing user group in crontab from root to www-data but that result in cron not working at all.
I have tried changing supervisor user to www-data so cron command is run by www-data instead of root.
Also setting user as www-data in Dockerfile, but all of the solutions either result in cron not running at all or the files created by jobs are still owned by root user.
After much investigation, I found that it is not a good practice to run laravel scheduler as root user because that can create files with root owner.
I updated my crontab file to following:
* * * * * root su -c "/usr/local/bin/php /var/www/artisan schedule:run >> /var/log/cron.log" -s /bin/bash www-data
This way the cache files created will be owned by www-data and no files from root owner are created.
Hope this helps someone who is facing the same issues.

laravel - Docker build failure in Production mode

I'm running a laravel 9 application (php v8) on docker. In my local setup docker is building without errors. When docker Up, I used cli and do the composer install and then artisan commands. I just do docker-compose up for that
After finishing initial developments, before pushing to Dockerhub, I checked by doing a build that is related to production. Here is my Dockerfile.
RUN apk add --no-cache php8 php8-cli php8-curl php8-intl php8-pdo_mysql php8-session php8-tokenizer php8-phar php8-xmlwriter php8-mbstring php8-simplexml php8-iconv php8-fileinfo php8-dom php8-xml php8-zip php8-openssl php8-pdo_sqlite php8-gd
RUN apk add --no-cache git
RUN apk add --no-cache composer
RUN apk add --no-cache npm
RUN cp /usr/bin/php8 /usr/bin/php
ARG BUILD_ENV
WORKDIR /
COPY conf/nginx.conf /etc/nginx/nginx.conf
COPY conf/php.ini /etc/php8/php.ini
COPY ./project/laravel /www
RUN if [[ "$BUILD_ENV" == "PRD" ]]; then \
cd /www && \
composer install; \
npm install; \
npm run dev; \
fi
RUN if [[ "$BUILD_ENV" == "PRD" ]]; then \
chown -R php:php /www; \
fi
Then I run the command
docker build -t my-laravel-web --build-arg BUILD_ENV=PRD .
But it shows this error
Any help to fix this issue before updating my dockerhub. I think the same problem will occur when I try to use the docker image to deploy in AWS.

Categories