Использование эфемерных контейнеров Docker в качестве приложений командной строки

Контейнеры Docker зарекомендовали себя как чрезвычайно полезные, позволяя разработчикам создавать изолированную среду и упрощать развертывание сервисов. У вас сложная настройка службы? Не стоит беспокоиться. Просто опишите шаги в Dockerfile, и вы сможете реплицировать процесс на каждой ОС хоста, поддерживающей Docker.

Когда я говорил о службах, готов поспорить, что первое, о чем вы подумали, - это HTTP-серверы или какие-то постоянные, всегда выполняющиеся процессы, которые отправляют или принимают запросы к таким службам. Хотя большинство вариантов использования Docker точно вписываются в этот сценарий, круг возможных приложений, которые он предлагает, намного превосходит это.

Одним из таких приложений является использование контейнеров Docker для изолирования сложной внутренней работы приложения командной строки (CLI). Большинство проектов данных включают выполнение запланированных сценариев, которые обращаются к удаленным службам, выполняют обработку данных и, в конечном итоге, записывают результаты либо на диск, либо в выделенное хранилище данных. Такие сценарии имеют необходимые зависимости, и часто зависимости одного могут конфликтовать с зависимостями, требуемыми другим, что приводит к большой неразберихе. Это основная причина, по которой вообще существуют такие вещи, как виртуальные среды в Python или пресловутая папка node_modules в Node.js. Тем не менее, все мы кое-что знаем о _3 _...

Идеальным случаем для таких приложений были бы статически привязанные исполняемые файлы, содержащие все необходимое и работающие в своих собственных выделенных процессах. Один из таких подходов отстаивают такие языки, как Go, Swift и вариант Kotlin's Native (и, конечно же, старый добрый C / C ++). Другие, такие как Java (толстые Jars) и Python (колеса), предлагают несколько разумный компромисс, помещая код и все зависимости в один развертываемый артефакт. Я называю это компромиссом, потому что он по-прежнему требует наличия JDK или среды выполнения Python в ОС хоста.

Однако в большинстве случаев у нас не будет возможности развернуть все в одном исполняемом файле. В большинстве случаев у нас также не будет возможности устанавливать разные среды выполнения по всему миру. В таких случаях хорошим вариантом может быть запекание приложения CLI со всеми его зависимостями в выделенный образ Docker. Давайте попробуем простой пример:

Пример

Вот простой скрипт Python, который извлекает текущую цену биткойна и конвертирует ее в желаемую фиатную валюту по нашему выбору («доллары США», «евро»).

В этом примере нужно отметить две вещи. Во-первых, это использование requests, популярной библиотеки HTTP-запросов / ответов, но не являющейся частью стандартной библиотеки Python, поэтому ее необходимо устанавливать отдельно. Во-вторых, это использование аргументов командной строки, а именно --currency.

Далее идет Dockerfile:

Опять же, очень просто. Для простоты примера мы установим requests прямо в Dockerfile и будем использовать файл requirements.txt или какой-нибудь сценарий установки, что является обычной практикой.

Давайте создадим образ, предполагая, что оба файла находятся в нашем текущем каталоге:

docker build -t my-command .

Отлично, теперь мы можем взять наше свежеиспеченное изображение и запустить его:

docker run --rm -it my-command --currency USD

Параметр --rm удалит контейнер сразу после выполнения команды. Если вы когда-либо запускали, вы бы знали, что неактивные контейнеры не удаляются немедленно, а остаются для возможного последующего перезапуска. Если предположить, что мы хотим запускать нашу команду несколько раз в день, это приведет к потере большого количества ресурсов.

Другой интересный вариант - -i. Этот, в сочетании с тем фактом, что мы выбрали ENTRYPOINT вместо CMD в качестве начальной точки в Dockerfile, позволит нам передать аргумент --currency в самом конце. Есть несколько тонких различий между ENTRYPOINT и CMD, но самое основное - это способность адаптироваться ENTRYPOINT, в то время как CMD является более или менее окончательным.

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

alias my-cmd="docker run --rm -i my-command" my-cmd --currency USD

Вот и все! Теперь вы можете запустить свое приложение как запланированную задачу cron и, например, накапливать данные с течением времени:

*/10 * * * * user ./my-cmd --currency USD >> prices.csv

Ограничения

И снова решение - это далеко не простота перетаскивания, которую допускают исполняемые файлы Go. Кроме того, он предполагает работающую настройку Docker (в настоящее время все меньше и меньше проблем) и довольно много места из-за изображений. Это также, по большей части, требует создания образа локально или, по крайней мере, извлечения ингредиентов из реестра образов. Docker позволяет сохранить образ со всеми его зависимостями и загрузить его в ОС удаленного хоста. Думайте об этом как о чем-то вроде толстой банки. Я попробовал это просто для демонстрации, но это кажется очень непрактичным, поскольку в результате получился огромный zip-файл для этого крошечного скрипта Python (конечно, он запекает внутри целое бистро Linux):

docker save -o my-command.zip my-command
rw------- 1 user user 920M Mar 17 08:47 my-command.zip

Первоначально опубликовано на preslav.me 18 марта 2019 г.