I have a Laravel application built on top of Docker linked to AWS ECS. The application is dockerized, and I wish to run crontab on that application
Hi
Are you using laravels internal scheduler?
If so, i just run “php artisan schedule:work &” before the main application/apache starts in my entrypoint script
We are using crontab:
- /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1
But as far as I have understood, crontab will not get effective unless it is executed by some supervisor or service (i.e. docker-compose). Is that right?
docker-compose is a way of managing your containers, instead of using “docker run”, you can use compose to make a recipe of your container, so you dont have to remember your docker run command.
The supervisor part is true though, what docker does, is that it starts 1 application when it starts, it could be apache, but it could also be something like supervisor, and then supervisor spawns cron and apache.
There is some examples of this here or i made an complete example here
I have had some success using multipurpose images to run the Laravel Scheduler and Laravel Queues.
Means I build my image using a “start.sh” that will be my entrypoint.
In that “start.sh” I read a passed environment var like “CONTAINER_ROLE” and start what I want with that.
Example in a docker-compose.yml:
environment:
CONTAINER_ROLE: app // will start php-fpm
CONTAINER_ROLE: scheduler // will start php artisan scheduler:work
CONTAINER_ROLE: queue // will start php artisan queue:work
This way it is a breeze to scale the queue on demand.
You can scale it through the CLI or you can deploy more than one container through docker-compose.
I have never have success using the cron, because my containers cant run as root on kubernetes, so I have found out some solutions.
Here is how I build my container:
FROM php:8.2-fpm-alpine
# Arguments
ARG USER=deployer
ARG USER_GROUP=www-data
# Environment
ENV TZ=Europe/Berlin
# Add APK repositories
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
# Update / Upgrade / Install apk packages
&& apk update \
&& apk upgrade \
&& apk add \
sudo \
git \
tzdata \
7zip \
unzip \
nano \
nodejs@main \
npm@community \
# Set Timezone
&& cp /usr/share/zoneinfo/${TZ} /etc/localtime
# Add custom php.ini
ADD /Dockerfiles/php/php.ini /usr/local/etc/php/conf.d/php.ini
# Add custom www.conf
ADD /Dockerfiles/php/www.conf /usr/local/etc/php-fpm.d/www.conf
# Install "install-php-extensions"
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
# Install PHP extensions and latest Composer
RUN chmod +x /usr/local/bin/install-php-extensions \
&& install-php-extensions bcmath ctype curl dom fileinfo json mbstring openssl pcre pdo pdo_mysql pdo_pgsql tokenizer xml zip \
&& install-php-extensions @composer \
# Add non root user
&& adduser -D ${USER} \
&& mkdir -p /etc/sudoers.d \
&& echo "${USER} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${USER} \
&& addgroup ${USER} ${USER_GROUP} \
&& chmod 0440 /etc/sudoers.d/${USER} \
# Remove cache and tmp files
&& rm -rf /var/cache/apk/* /tmp/*
# Add start script / entrypoint
COPY --chown=${USER}:${USER} ./Dockerfiles/entrypoint/start.sh /usr/local/bin/start
RUN chmod u+x /usr/local/bin/start
# Change user
USER ${USER}
# Set Workdir
WORKDIR /var/www/html
# Entrypoint
ENTRYPOINT ["/usr/local/bin/start"]
Here is the start.sh:
#!/bin/sh
set -e
role=${CONTAINER_ROLE:-app}
work_on_queues=${WORK_ON_QUEUES:-default}
if [ "$role" = "app" ]; then
php-fpm
elif [ "$role" = "queue" ]; then
echo "Running the queue..."
php /var/www/html/artisan queue:work --queue="$work_on_queues" --sleep=3 --tries=3 -v
elif [ "$role" = "scheduler" ]; then
echo "Running the scheduler..."
php /var/www/html/artisan schedule:work
else
echo "Could not match the container role \"$role\""
exit 1
fi
And here is a docker-compose.yml example:
version: '3'
services:
nginx:
image: nginxinc/nginx-unprivileged:alpine-slim
restart: always
tty: true
environment:
COLORTERM: true
ports:
- '${APP_PORT:-80}:8080'
volumes:
- ./docker/nginx/conf.d/:/etc/nginx/conf.d/
- '.:/var/www/html'
networks:
- network
depends_on:
- app
app:
image: <your-build-image>
restart: always
tty: true
environment:
COLORTERM: true
CONTAINER_ROLE: app
volumes:
- '.:/var/www/html'
networks:
- network
depends_on:
- mysql
queue:
image: <your-build-image>
restart: always
tty: true
deploy:
replicas: 2
environment:
COLORTERM: true
CONTAINER_ROLE: queue
# WORK_ON_QUEUES: "default,test"
volumes:
- '.:/var/www/html'
networks:
- network
depends_on:
- mysql
- app
scheduler:
image: <your-build-image>
restart: always
tty: true
environment:
COLORTERM: true
CONTAINER_ROLE: scheduler
volumes:
- '.:/var/www/html'
networks:
- network
depends_on:
- mysql
- app
mysql:
image: mysql/mysql-server:8.0
command: --local-infile=ON --innodb_file_per_table=ON --innodb_log_file_size=512M --innodb_strict_mode=OFF
restart: always
ports:
- '${FORWARD_DB_PORT:-3306}:3306'
environment:
COLORTERM: true
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
TZ: Europe/Berlin
volumes:
- 'mysql:/var/lib/mysql'
networks:
- network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
retries: 3
timeout: 5s
networks:
network:
driver: bridge
volumes:
mysql:
driver: local
I hope I could help, because it took a long time for me to get this to work…