Cron does not run in a PHP Docker container
I am using the php:7.4-fpm Docker image and I’m trying to set up cron to run but it’s not running.
Here is my Dockerfile:
When I enter the container and check the contents of /etc/cron.d/crontab it is correct
But it’s not being run. I’m not sure what’s going on here..
When I run service cron status it says [ ok ] cron is running. But nothing is happening.
2 Answers 2
I had the same bug, fixed by setting up the cron on the machine hosting the docker container instead of the container itself.
So the cron entry translates to something like :
* * * * * docker exec web php artisan schedule:run >> storage/logs/cron.log 2>&1
So I finally managed to solve it. I have no idea why COPYing the cron file wasn’t working. I still don’t know why. (maybe someone smarter than me can explain it). But I solved it very simply by appending my commands to the /etc/crontab file and now it works.
P.S. crontab file requires a new line character at the end so using echo adds it automatically.
Here is my updated Dockerfile (I deleted all the other lines where I copied the crontab):
Как запустить cron в docker
Иногда необходимо запустить какой-то повторяющийся процесс внутри докера. В зависимости от того что конкретно нужно сделать есть несколько способов решить эту задачу.
В docker нужно запускать только повторяющий процесс
Вот пример задачи: каждую минуту нужно запускать в докере скрипт, который записывает в файл текущую дату и время. Задача искусственная, но на ее примере станет понятно как решать подобные задачи.
Итак. Создаем файл cron со следующим содержимым:
В той же папке с файлом cron создаем файл Dockerfile:
Теперь нужно собрать образ. Выполняем команду:
Через некоторое время команда отработает и у нас появится новый докерный образ с именем sample_cron. А дальше его нужно запустить (при запуске образа появляется работающий контейнер):
Эта команда мгновенно отработает и выдаст на экран id контейнера, что-то вроде:
Итак — мы сделали докерный образ и запустили из него контейнер. Внутри контейнера работает крон, который каждую минуту записывает в файл /data/asdf текущую дату и время. При запуске контейнера мы указали
благодаря этому то что находится внутри контейнера в папке /data становится доступно на хост машине в папке data, там где мы запускали контейнер.
Действительно, если заглянуть в эту папку на хост машине, то можно увидеть этот файл:
Так же можно подключится к докерному контейнеру и посмотреть что выводить процесс, который работает в контейнере:
Это в выведет что-то вроде этого и каждую минут будет появляться новая строчка:
После нужно выполнить команду:
Это остановит и удалит контейнер.
Итак, нам удалось запустить крон в докере. Чуть-чуть подробностей про содержимое Dockerfile.
Первая команда в Dockerfile — это FROM. Эта команда говорит на базе какого образа мы создаем новый образ. Мы используем ubuntu:16.04. На текущий момент — это последняя LTS версия ubuntu, сейчас стоит использовать именно эту версию. Можно бы бы использовать другой образ в качестве базы, но ubuntu — это хороший дистрибутив.
Следующая команда RUN — она написана на трех строчках. Эта команда запускается при сборке образа и она устанавливает указанные пакеты в систему.
Дальше идет команда COPY — она копирует файл cron в создаваемый докерный образ. Результат работы этой команды — в докерном образе появляется файл /etc/cron.d/sample
Следующая команда опять RUN — она создает папку /data внутри докерного образа.
В docker нужно запускать повторяющийся процесс, плюс еще другие процессы
Вот пример задачи. Нужно в докере запустить две вещи. Во-первых, нужно чтобы в докере работал крон который каждую минуту создает пустой файл с текущим timestamp в качестве имени. Во-вторых, нужно чтобы из контейнера торчал веб-сервер, который позволит просмотреть эти файлы.
Снова пишем файл cron. Для того чтобы создать файл с текущим timestamp нужно выполнить вот такую команду:
но когда записываешь эту команду в cron файл нужно заэскейпить символ процента, т.е. файл cron будет выглядеть вот так:
В качестве веб сервера можно использовать кучу всего. Например, можно использовать python однострочник, но в этом примере я буду использовать nginx. Создаем файл nginx.conf:
А дальше начинается специфика докера. В контейнере может быть запущен только один главный процесс. Поскольку нам нужно запустить сразу 2 процесса, то нам нужна еще некая обертка которая запустит оба эти процесса. Вот эта обертка — это программа supervisor, штука, которая позволяет запускать другие процессы.
Пишем файл supervisor.conf, в нем описываем оба процесса которые нужно будет запускать:
И дальше пишем Dockerfile:
После этого можно зайти браузером на адрес на котором работает докер и сначала увидеть там пустую папку, а через минуту увидеть файл, который создал крон.
Можно зайти в контейнер и увидеть иерархию процессов:
Другие способы для работы с кроном и докером
Мы рассмотрели два способа работы с кроном в докере:
Но так же возможен и другой способ работы. Можно использовать cron не внутри докера, а на той же машине на которой работает докер. Например, можно написать такой крон на хост машине:
Каждую минуту будет запускаться короткоживущий контейнер. Он будет выполнять команду date и тут же завершать свою работу. Результат этой работы будет сохранятся в файл /tmp/dates на хост машине.
Еще один пример. На хост машине работает докер контейнер с nginx. Он был запущен вот так:
Если на хост машине создать вот такой cron:
То каждую минуту внутри контейнера (именно внутри контейнера, а не на хост машину) будет выполнятся команда:
И к этому файлу можно будет обратится через браузер.
Recommended way to run CRON job along with PHP-FPM #133
Comments
dominikkrulak commented Mar 3, 2020
I duplicated this issue and extended to specific image use. I will update it once I’ll get accepted answer.
— Start of duplicate
Heard of CRON job and I generally know what it does and I need to run some script in some time intervals.
This is my first encounter with setting up a CRON job on any system.
I’ve studied about it for a while but haven’t try it yet.
And than something about to run it in Docker:
How to run a cron job inside a docker container?
In minideb image I could locate /etc/cron.daily where I’ll may put files but if I’ll need to run CRON job hourly than I couldn’t locate /etc/crontab neither crontab executable.
Is there anything I should follow up or avoid to run a CRON job in minideb container?
— End of duplicate
What I did
The content of redisQueue file is
Despite that /etc/cron.daily will be executed once per day, every day I think I could test it with the actual cron job interval execution.
Now at the end of Dockerfile I should add
CMD [ «cron», «f» ]
since Dockerfile can have only one CMD instruction in format:
CMD [«executable»,»param1″,»param2″]
I have no idea how to proceed.
The text was updated successfully, but these errors were encountered:
We are unable to convert the task to an issue at this time. Please try again.
The issue was successfully created but we are unable to update the comment at this time.
Запуск cron внутри Docker-контейнера

Так уж вышло, что запуск cron в Docker-контейнере — дело весьма специфическое, если не сказать сложное. В сети полно решений и идей на эту тему. Вот один из самых популярных (и простых) способов запуска:
Проблему просмотра логов с использованием стандартных средств Docker устранить сравнительно легко. Для этого достаточно принять решение о том, в какой файл будут писать свои логи cron-задания. Предположим, что это /var/log/cron.log:
* * * * * www-data task.sh >> /var/log/cron.log 2>&1
Запуская после этого контейнер при помощи команды:
мы всегда сможем видеть результаты выполнения заданий при помощи «docker logs».
Аналогичного эффекта можно добиться воспользовавшись перенаправлением /var/log/cron.log в стандартный вывод контейнера:
UPD: Такой способ работать не будет по этой причине.
Если cron-задания пишут логи в разные файлы, то, скорее всего, предпочтительнее будет вариант с использованием tail, который может «следить» за несколькими логами одновременно:
UPD: Файл(ы) для лога удобнее создавать в виде named pipe (FIFO). Это позволит избежать накопления внутри контейнера ненужной информации, а задачи log rotate возложить на Docker. Пример:
Environment variables
Изучая информацию на тему назначения переменных окружения для задач cron, выяснил, что последний может использовать так называемые подключаемые модули аутентификации (PAM). Что на первый взгляд является не относящимся к сабжу теме фактом. Но у PAM есть возможность определять и переопределять любые переменные окружения для служб, которые его (точнее их, модули аутентификации) используют, в том числе и для cron. Вся настройка производится в файле /etc/security/pam_env.conf (в случае Debian/Ubuntu). То есть любая переменная, описанная в этом файле, автоматически попадает в Environment всех cron-заданий.
Но есть одна проблема, точнее даже две. Синтаксис файла (его описание) при первом взгляде может ввести в ступор обескуражить. Вторая проблема — это как при запуске контейнера перенести переменные окружения внутрь pam_env.conf.
Опытные Docker-пользователи насчет второй проблемы наверняка сразу скажут, что можно воспользоваться лайфхаком под названием docker-entrypoint.sh и будут правы. Суть этого лайфхака заключается в написании специального скрипта, запускаемого в момент старта контейнера, и являющегося входной точкой для параметров, перечисленных в CMD или переданных в командной строке. Скрипт можно прописать внутри Dockerfile, например, так:
А его код при этом должен быть написан специальным образом:
Вернемся к переносу переменных окружения немного позже, а пока остановимся на синтаксисе файла pam_env.conf. При описании любой переменной в этом файле значение можно указать c помощью двух директив: DEFAULT и OVERRIDE. Первая позволяет указать значение переменной по умолчанию (если та вообще не определена в текущем окружении), а вторая позволяет значение переменной переопределить (если значение этой переменной в текущем окружении есть). Помимо этих двух кейсов, в файле в качестве примера описаны более сложные кейсы, но нас по большому счету интересует только DEFAULT. Итого, чтобы определить значение для какой-нибудь переменной окружения, которая затем будет использовать в cron, можно воспользоваться таким примером:
Но можно поступить еще проще (и такой способ почему-то не описан в примерах pam_env.conf). Если вас устраивает, что переменная в целевом Environment будут иметь указанное значение, независимо от того, определена она уже в этом окружении или нет, то вместо вышеупомянутой строки можно записать просто:
Ну и наконец, если нужно перенести все текущие переменные в окружение cron-заданий, то в этом случае можно использовать такой скрипт:



