Запустите команду Docker внутри контейнера Docker Cron
Alpine Docker image позволяет очень легко создать контейнер Cron.
При использовании Docker ваше приложение обычно состоит из нескольких контейнеров Docker . Часто вы хотите запускать сценарии внутри этих контейнеров в определенные моменты времени, например, каждые пять минут, раз в час, раз в день.
Здесь на помощь приходит планировщик заданий Cron, и есть несколько вариантов, как это сделать. В этой заметке я создаю отдельный контейнер Cron и использую команду Docker Exec для выполнения команд и сценариев в другом контейнере.
У меня было рабочее решение с использованием базового образа Ubuntu , но мне хотелось более простой настройки и меньшей площади. Поэтому здесь я использую Alpine Docker image . Как всегда, я запускаю это на Ubuntu 22.04.
Alpine и Cron
Alpine уже поставляется с настроенным Cron. Внутри контейнера есть директории, куда мы можем поместить наши скрипты:
docker run alpine:3.17 ls -l /etc/periodic
Результат:
/etc/periodic:
total 20
drwxr-xr-x 2 root root 4096 Mar 29 14:45 15min
drwxr-xr-x 2 root root 4096 Mar 29 14:45 daily
drwxr-xr-x 2 root root 4096 Mar 29 14:45 hourly
drwxr-xr-x 2 root root 4096 Mar 29 14:45 monthly
drwxr-xr-x 2 root root 4096 Mar 29 14:45 weekly
Скрипты в этих директориях (периодически) запускаются Cron следующим образом:
docker run alpine:3.17 crontab -l
Результат:
# do daily/weekly/monthly maintenance
# min hour day month weekday command
*/15 * * * * run-parts /etc/periodic/15min
0 * * * * run-parts /etc/periodic/hourly
0 2 * * * run-parts /etc/periodic/daily
0 3 * * 6 run-parts /etc/periodic/weekly
0 5 1 * * run-parts /etc/periodic/monthly
Используя тома Docker , мы можем отобразить (смонтировать) эти каталоги на нашем хосте. Возможно, вы захотите запускать некоторые команды или скрипты в другие моменты времени, например:
- Каждую минуту.
- Каждый час с 6 до 23.
- В полночь.
Это означает, что мы должны добавить строки в наш файл crontab и создать новые каталоги внутри контейнера. Мы можем сделать это в файле запуска или в Dockerfile. Например, добавить строку для каталога со скриптами, которые будут запускаться каждый час между 6 и 23 в Dockerfile:
RUN echo "0 6-23 * * * run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root
Мы также хотим использовать Bash. А чтобы иметь возможность запускать в нашем контейнере команды Docker , которые могут ссылаться на другие контейнеры Docker , мы добавляем Docker в наш Dockerfile и отображение тома в наш файл docker-compose.yml:
RUN apk add --update --no-cache bash && \
apk add --update --no-cache docker && \
...
volumes:
# access docker containers outside this container
- /var/run/docker.sock:/var/run/docker.sock
Каталоги и файлы
Здесь представлен древовидный список всех каталогов и файлов в проекте:
├── project
│ ├── cron
│ │ ├── jobs
│ │ │ ├── 15min
│ │ │ ├── 1min
│ │ │ │ └── run_every_minute
│ │ │ ├── 5min
│ │ │ │ └── run_every_5_minutes
│ │ │ ├── daily
│ │ │ ├── daily_0000
│ │ │ ├── hourly
│ │ │ │ └── run_every_hour
│ │ │ └── hourly_0600_2300
│ │ │ └── run_every_hour_0600_2300
│ │ └── Dockerfile
│ ├── docker-compose.yml
│ └── .env
Для тестирования я добавил несколько Bash-скриптов. Важно: Согласно документации Alpine : "Не используйте точки в именах файлов скриптов - это мешает их работе.
- run_every_minute
- run_every_5_minutes
- run_every_hour
- run_every_hour_0600_2300
Содержание всех этих скриптов одинаково:
#!/bin/bash
script_name=`basename "$0"`
echo "Running ${script_name} ..."
Убедитесь, что все эти Bash-скрипты являются исполняемыми, например:
chmod a+x run_every_5_minutes
Файлы
Вот другие файлы в проекте:
- Dockerfile
- docker-compose.yml
- .env
Файл: Dockerfile
Обратите внимание, что Cron должен запускаться от имени root, а не от имени user.
# Dockerfile
FROM alpine:3.17
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com
# Install required packages
RUN apk add --update --no-cache bash && \
apk add --update --no-cache docker && \
# every minute
echo "* * * * * run-parts /etc/periodic/1min" >> /etc/crontabs/root && \
# every 5 minutes
echo "*/5 * * * * run-parts /etc/periodic/5min" >> /etc/crontabs/root && \
# every hour between 06:00 and 23:00
echo "0 6-23 * * * run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root && \
# every day at 00:00
echo "0 0 * * * run-parts /etc/periodic/daily_0000" >> /etc/crontabs/root
# show all run-parts
# RUN crontab -l
WORKDIR /
File: docker-compose.yml
Обратите внимание, что мы установили контекст на './cron'. В этом каталоге находятся все файлы, связанные с этим контейнером.
# docker-compose.yml
version: "3.7"
x-service_defaults: &service_defaults
env_file:
- ./.env
restart: always
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
services:
cron:
<< : *service_defaults
build:
context: ./cron
dockerfile: Dockerfile
volumes:
- ./cron/jobs/1min:/etc/periodic/1min/:ro
- ./cron/jobs/5min:/etc/periodic/5min/:ro
- ./cron/jobs/15min:/etc/periodic/15min/:ro
- ./cron/jobs/hourly:/etc/periodic/hourly/:ro
- ./cron/jobs/hourly_0600_2300:/etc/periodic/hourly_0600_2300/:ro
- ./cron/jobs/daily:/etc/periodic/daily/:ro
- ./cron/jobs/daily_0000:/etc/periodic/daily_0000/:ro
# access docker containers outside this container
- /var/run/docker.sock:/var/run/docker.sock
command: crond -f -l 8
Файл: .env
В файле .env мы указываем только имя проекта:
# .env
COMPOSE_PROJECT_NAME=myapp
Сборка, запуск и некоторые тесты
Для (пере)сборки контейнера:
docker-compose build --no-cache
Для запуска контейнера Cron:
docker-compose up
Выходные данные будут выглядеть следующим образом:
Attaching to myapp_cron_1
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_5_minutes ...
cron_1 | Running run_every_minute ...
...
Имя контейнера Cron:
myapp_cron_1
Показать журналы контейнера:
docker logs -t -f myapp_cron_1
Результат:
2023-04-18T11:52:00.871554414Z Running run_every_minute ...
2023-04-18T11:53:00.872386069Z Running run_every_minute ...
2023-04-18T11:54:00.872337812Z Running run_every_minute ...
2023-04-18T11:55:00.872792007Z Running run_every_minute ...
2023-04-18T11:55:00.872825556Z Running run_every_5_minutes ...
2023-04-18T11:56:00.875379199Z Running run_every_minute ...
Чтобы проверить, какие скрипты будут запущены из каталога ./cron/jobs/hourly_0600_2300, выполните следующую команду в другом терминале:
docker exec myapp_cron_1 run-parts --test /etc/periodic/hourly_0600_2300
Это не приведет к выполнению скриптов! Результат:
/etc/periodic/hourly_0600_2300/run_every_hour_0600_2300
Запуск сценария или команды в другом контейнере Docker
Давайте запустим команду в контейнере BusyBox. В другом терминале запустите этот контейнер BusyBox и назовите его 'my_busybox':
docker run -it --name my_busybox --rm busybox
Создайте новый скрипт 'run_busybox_env' в каталоге './cron/jobs/1min' и сделайте его исполняемым:
#!/bin/bash
# run_busybox_env
DOCKER=/usr/bin/docker
${DOCKER} exec my_busybox env
Этот скрипт запускает 'env'-команду в контейнере 'my_busybox' и перечисляет переменные окружения контейнера BusyBox.
В течение минуты результат должен появиться в нашем контейнере Cron:
cron_1 | Running run_every_minute ...
cron_1 | Running run_every_5_minutes ...
cron_1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cron_1 | HOSTNAME=dc74e9704d49
cron_1 | HOME=/root
cron_1 | Running run_every_minute ...
HOSTNAME имеет ContainerId контейнера BusyBox, в вашем случае он будет отличаться. Вы можете получить ContainerId контейнера BusyBox, выполнив команду:
docker ps | grep my_busybox
Summary
Использовать Alpine Docker image для нашего Cron-контейнера было очень просто. Все работало сразу. В результате мы получили меньше конфигурации и меньшую площадь.
Ссылки / кредиты
Alpine Linux:FAQ
https://wiki.alpinelinux.org/wiki/Alpine_Linux:FAQ
CRON Jobs with Alpine Linux and Docker
https://lucshelton.com/blog/cron-jobs-with-alpine-linux-and-docker
Docker Tip #40: Running Cron Jobs on the Host vs. in a Container
https://nickjanetakis.com/blog/docker-tip-40-running-cron-jobs-on-the-host-vs-in-a-container
Running cron jobs in a Docker Alpine container
https://devopscell.com/cron/docker/alpine/linux/2017/10/30/run-cron-docker-alpine.html
Недавний
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
- Нужно ли переносить Docker Swarm на Kubernetes?
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Использование PyInstaller и Cython для создания исполняемого файла Python
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу