Иногда необходимо запустить какой-то повторяющийся процесс внутри докера. В зависимости от того что конкретно нужно сделать есть несколько способов решить эту задачу.
Вот пример задачи: каждую минуту нужно запускать в докере скрипт, который записывает в файл текущую дату и время. Задача искусственная, но на ее примере станет понятно как решать подобные задачи.
Итак. Создаем файл cron со следующим содержимым:
* * * * * root date >> /data/asdf
В той же папке с файлом cron создаем файл Dockerfile:
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y \
cron \
rsyslog
COPY cron /etc/cron.d/sample
RUN mkdir /data
CMD service rsyslog start && service cron start && tail -f /var/log/syslog
Теперь нужно собрать образ. Выполняем команду:
docker build --tag sample_cron .
Через некоторое время команда отработает и у нас появится новый докерный образ с именем sample_cron. А дальше его нужно запустить (при запуске образа появляется работающий контейнер):
docker run \
--detach \
--volume `pwd`/data:/data \
--name sample_cron \
sample_cron
Эта команда мгновенно отработает и выдаст на экран id контейнера, что-то вроде:
6c5544ff1b0cea7e235aa53904b8372c766444633f60896b1fd86681480e5a3e
Итак — мы сделали докерный образ и запустили из него контейнер. Внутри контейнера работает крон, который каждую минуту записывает в файл /data/asdf текущую дату и время. При запуске контейнера мы указали
--volume `pwd`/data:/data
благодаря этому то что находится внутри контейнера в папке /data становится доступно на хост машине в папке data, там где мы запускали контейнер.
Действительно, если заглянуть в эту папку на хост машине, то можно увидеть этот файл:
$ tree data
data
└── asdf
0 directories, 1 file
$ cat data/asdf
Sun Jun 11 12:04:01 UTC 2017
Sun Jun 11 12:05:01 UTC 2017
Sun Jun 11 12:06:01 UTC 2017
Sun Jun 11 12:07:01 UTC 2017
Sun Jun 11 12:08:01 UTC 2017
Sun Jun 11 12:09:01 UTC 2017
Так же можно подключится к докерному контейнеру и посмотреть что выводить процесс, который работает в контейнере:
docker logs -f sample_cron
Это в выведет что-то вроде этого и каждую минут будет появляться новая строчка:
Jun 11 12:04:01 6c5544ff1b0c CRON[38]: (root) CMD ( date >> /data/asdf)
Jun 11 12:05:01 6c5544ff1b0c CRON[41]: (root) CMD ( date >> /data/asdf)
Jun 11 12:06:01 6c5544ff1b0c CRON[44]: (root) CMD ( date >> /data/asdf)
Jun 11 12:07:01 6c5544ff1b0c CRON[47]: (root) CMD ( date >> /data/asdf)
Jun 11 12:08:01 6c5544ff1b0c CRON[50]: (root) CMD ( date >> /data/asdf)
Jun 11 12:09:01 6c5544ff1b0c CRON[53]: (root) CMD ( date >> /data/asdf)
Jun 11 12:10:01 6c5544ff1b0c CRON[56]: (root) CMD ( date >> /data/asdf)
Jun 11 12:11:01 6c5544ff1b0c CRON[59]: (root) CMD ( date >> /data/asdf)
После нужно выполнить команду:
docker rm -f sample_cron
Это остановит и удалит контейнер.
Итак, нам удалось запустить крон в докере. Чуть-чуть подробностей про содержимое Dockerfile.
Первая команда в Dockerfile — это FROM. Эта команда говорит на базе какого образа мы создаем новый образ. Мы используем ubuntu:16.04. На текущий момент — это последняя LTS версия ubuntu, сейчас стоит использовать именно эту версию. Можно бы бы использовать другой образ в качестве базы, но ubuntu — это хороший дистрибутив.
FROM ubuntu:16.04
Следующая команда RUN — она написана на трех строчках. Эта команда запускается при сборке образа и она устанавливает указанные пакеты в систему.
RUN apt-get update && apt-get install -y \
cron \
rsyslog
Дальше идет команда COPY — она копирует файл cron в создаваемый докерный образ. Результат работы этой команды — в докерном образе появляется файл /etc/cron.d/sample
COPY cron /etc/cron.d/sample
Следующая команда опять RUN — она создает папку /data внутри докерного образа.
RUN mkdir /data
Последняя команда — это CMD, эта команда говорит что нужно выполнить при при запуске докерного образа. Тут мы говорим что нужно запустить сервис rsyslog, потом нужно запустить сервис cron, а потом нужно запустить бесконечный вывод на экран содержимого файла. Докер контейнер работает до тех пор пока работает процесс указанный в CMD. Если бы мы не указали tail -f, то при запуске контейнера, он бы запустился, запустил в фоне сервисы rsyslog и cron и тут же бы вышел. Команда tail -f нужна для того чтобы контейнер работал.
CMD service rsyslog start && service cron start && tail -f /var/log/syslog
Вот пример задачи. Нужно в докере запустить две вещи. Во-первых, нужно чтобы в докере работал крон который каждую минуту создает пустой файл с текущим timestamp в качестве имени. Во-вторых, нужно чтобы из контейнера торчал веб-сервер, который позволит просмотреть эти файлы.
Снова пишем файл cron. Для того чтобы создать файл с текущим timestamp нужно выполнить вот такую команду:
touch /data/`date +%s`
но когда записываешь эту команду в cron файл нужно заэскейпить символ процента, т.е. файл cron будет выглядеть вот так:
* * * * * root touch /data/`date +\%s`
В качестве веб сервера можно использовать кучу всего. Например, можно использовать python однострочник, но в этом примере я буду использовать nginx. Создаем файл nginx.conf:
daemon off;
events {
}
http {
server {
listen 80;
location / {
autoindex on;
root /data/;
}
}
}
А дальше начинается специфика докера. В контейнере может быть запущен только один главный процесс. Поскольку нам нужно запустить сразу 2 процесса, то нам нужна еще некая обертка которая запустит оба эти процесса. Вот эта обертка — это программа supervisor, штука, которая позволяет запускать другие процессы.
Пишем файл supervisor.conf, в нем описываем оба процесса которые нужно будет запускать:
[supervisord]
nodaemon=true
[program:nginx]
command=nginx -c /etc/nginx/nginx.conf
[program:cron]
command=cron -f
И дальше пишем Dockerfile:
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y \
cron \
nginx \
supervisor
COPY nginx.conf /etc/nginx/nginx.conf
COPY supervisor.conf /etc/supervisor/conf.d/supervisor.conf
COPY cron /etc/cron.d/sample
RUN mkdir /data
EXPOSE 80
CMD /usr/bin/supervisord
Собираем образ:
docker build --tag sample_cron2 .
И запускаем его:
docker run \
--detach \
--publish 80:80 \
--name sample_cron2 \
sample_cron2
После этого можно зайти браузером на адрес на котором работает докер и сначала увидеть там пустую папку, а через минуту увидеть файл, который создал крон.
Можно зайти в контейнер и увидеть иерархию процессов:
$ docker exec -it sample_cron2 bash
root@59cbe56fce04:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4508 796 ? Ss 17:58 0:00 /bin/sh -c /usr/bin/supervisord
root 5 0.0 1.7 48036 18256 ? S 17:58 0:00 /usr/bin/python /usr/bin/supervisord
root 8 0.0 0.2 26068 2548 ? S 17:58 0:00 \_ cron -f
root 9 0.0 0.9 124972 9728 ? S 17:58 0:00 \_ nginx: master process nginx -c /etc/nginx/nginx.conf
nobody 10 0.0 0.3 125164 3300 ? S 17:58 0:00 \_ nginx: worker process
root 37 0.0 0.3 18208 3212 ? Ss 18:01 0:00 bash
root 49 0.0 0.2 34424 2860 ? R+ 18:01 0:00 \_ ps auxf
root@59cbe56fce04:/#
Мы рассмотрели два способа работы с кроном в докере:
Но так же возможен и другой способ работы. Можно использовать cron не внутри докера, а на той же машине на которой работает докер. Например, можно написать такой крон на хост машине:
* * * * * root docker run --rm ubuntu:16.04 date >> /tmp/dates
Каждую минуту будет запускаться короткоживущий контейнер. Он будет выполнять команду date и тут же завершать свою работу. Результат этой работы будет сохранятся в файл /tmp/dates на хост машине.
Еще один пример. На хост машине работает докер контейнер с nginx. Он был запущен вот так:
docker run \
--detach \
--publish 80:80 \
--name nginx \
nginx:1.13.1
Если на хост машине создать вот такой cron:
* * * * * root docker exec nginx sh -c 'date >> /usr/share/nginx/html/dates.html'
То каждую минуту внутри контейнера (именно внутри контейнера, а не на хост машину) будет выполнятся команда:
date >> /usr/share/nginx/html/dates.html
И к этому файлу можно будет обратится через браузер.
11 июня 2017