angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Запустите команду Docker внутри контейнера Docker Cron

Alpine Docker image позволяет очень легко создать контейнер Cron.

18 апреля 2023
В Docker
post main image
https://unsplash.com/@unsplash

При использовании 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

Подробнее

Alpine Cron Docker

Оставить комментарий

Комментируйте анонимно или войдите в систему, чтобы прокомментировать.

Комментарии

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.