В статье хочу познакомить читателя с идеей, что образы docker - это такие же простые приложения, как apt пакеты. А утилита DAM (Docker Application Manager) - пример их пакетного менеджера.

Статья может быть полезна:

  • тем, кто работает с docker на автономных системах или с ограниченным доступом к интернет

  • кому нравится идея - один пакетный менеджер для разных архитектур и операционок

  • кто пробует запихать свое легаси с linux дистрибутива в контейнеры и контролировать его обновление

  • или интересна тому, кто просто проектирует микросервисные архитектуры

Заметка робинзона 1

Пятилетие назад, была написана моя первая публикация про docker. Как он хорош и позитивен, как правильно им пользоваться.

Уже тогда люди строили космические корабли на докере, чтобы “полететь” в облака, одним “штурвалом” управляя сотнями кораблей.

Но меня туда не тянуло.

Я, как «обычный» робинзон, тянулся к маленькому, но уютному домишке на острове, куда стандартными средствами никак не попасть. Об этом и начнется моя теневая история…

Как появилась идея утилиты

Работая сисадмином в 15м году мне приходилось постоянно разворачивать дистрибутивы из микросервисов на OpenSuse. Чаще всего это были машины с ограниченным доступом к интернет в целях безопасности.
Самой большой болью я считал развертывание связки LDAP + Samba и последующее конфигурирование ее. Эти продукты ставились автоматизировано через роль Ansible. Но при настройке требовалось вручную менять конфигурации в системе.
Дистрибутив расползался по операционке, в виде конфигов, настроек сети, логов и прочих пользовательских настроек. Это создавало проблемы при обновлении продукта. Каждая установка становилась уникальной, что-то со временем забывалось.
В какой-то момент пришло понимание - надо что-то менять.

На этом фоне вызывала интерес модная и молодежная CoreOS:

  • файловая система была read-only

  • для записи были разрешены только временные системные и пользовательские каталоги

  • набором утилит был минимальным для работы с консолью

  • не было пакетного менеджера

  • вся работа продукта предусматривалась в контейнерах docker

Минимализм системы и изолированность запущенного микросервиса в docker контейнере восхищала.
Возникла идея - давайте запускать уже готовую связку LDAP + Samba в контейнере, а потом настраивать проброшенные конфиги bash-скриптом.

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

Но появились вопросы: где же их хранить? как обновлять эти скрипты при обновлении?
Ответ нашелся простой - в образе docker.
Так появился прототип утилиты DAM.

Отмечу, что в то время было еще интересное решение docker-compose. Но оно не подходило.

Для него были сложности с доставкой и последующим обновлением на машинах без доступа к интернет. Вот ряд пунктов, которые требовалось автоматизировать, чтобы обновить/установить продукт на машине:

  • скачать все архивы докер образов на машину с флешки

  • загрузить их в локальный кэш docker

  • остановить предыдущие контейнеры

  • удалить старые настройки системы

  • сделать настройки системы под новый дистрибутив

  • взять образцы конфигов из архивов докер образов и скопировать их в систему

  • запустить новые контейнеры с новым docker-compose.yaml конфигом - удалить старые образы из кэша

Был сделан вывод, что docker-compose это инструмент для управления контейнерами, а не доставкой продукта.
Поэтому был написан универсальный bash скрипт для развертывания образов docker.

Но возникла неожиданная проблема. Микросервисы легко обновляются в системе. Но как с дистрибутивом автоматизировано обновить устаревшую операционку CoreOS?

Чтобы это сделать - нужен сервер обновлений, исходники ядра для кастомных модулей ядра, само обновление.
Хорошим решением было запускать контейнер, который бы этими действиями занимался. Т.е. чтобы вся предварительная подготовка системы, ее настройка под продукт, даже создание пользователей - производилась в docker.

Так стала формироваться идея, что каждый из образов должен знать, чего он хочет настроить в системе, чтобы микросервис работал.
Образ должен иметь версию, поэтому сисадмины могут контролировать обновления в системе. Всегда можно посмотреть, что установлено на клиентской машине и составить список, что следует обновить.

В ходе получения опыта при работе с docker прототип DAM становился все более универсальным. Пока не сформировалась мысль, что образы - это самодостаточные приложения.

А DAM - это их пакетный менеджер, на подобии apt.

Почему был написан DAM

Прототип на bash использовал системные команды, такие как grep, awk, docker. Он был привязан к особенностям консоли, устройству tty. Т.е. прототип был хоть и универсальный, но только в рамках CoreOS.
Хотелось большей универсальности. Поэтому был создан опенсорсный, некомерческий проект с утилитой. Он написан на Golang. Использует API docker. Имеет базу данных в виде файлов на диске.

На данный момент утилита протестирована и используется на Ubuntu, Debian. Продумана, но не использовалась под Windows, Raspberry Pi.

По моим наблюдениям пакетные менеджеры чаще всего заточены под одно семейство дистрибутивов:

  • apt под Debian и основанных на них систем

  • chocko под Windows

  • yum под Centos, Fedora и RedHat

Разработка DAM имеет цель - сделать универсальный пакетный менеджер под все популярные для прода операционки. Связка Golang и docker позволяет реализовать на практике.

Далее будет подробнее рассказано, что нужно уметь такому пакетному менеджеру. Также добавлю часть материала в виде видео. Ссылки на исходники прикреплю внизу поста.

Заметка робинзона 2

Любые робинзоны хотят на необитаемом острове построить домик. Да, точно любые! Других не встречал.

Но в отличии от всех робинзонов, хочется чтобы домик был не только уютным, но и полностью мне подконтрольным, модульным, универсальным.

И как-то раз.. сидя на бережке, булькая удочкой в речке, стала намечаться...

Не поклевка, а возникать идея, которая бы приручила docker на своем изолированном от мира островке..

Создание приложения

По подобию пакетных менеджеров образ docker должен хранить в себе определенную файловую структуру с метаинформацией, это:

  • скрипт установки и удаления

  • конфиги и бинарные файлы, для развертывания в системе

  • описание пакета

    Исходя из этого для создания приложения необходимо:

В каталоге с проектом, рядом с Dockerfile создать meta директорию.
Которая при установке приложения будет копироваться в систему. Сформировать install и uninstall с набором команд.
В Dockerfile также добавить строчку, чтобы скопировать meta в корень образа.

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

Все файлы в каталоге meta, имеющие расширение .exp преобразуются после создания. В них все шаблоны переменных, которые имеют форму ${DAM_...}  заменяются, на значения переменных окружения.

Если прописать шаблон ${DAM_APP_TAG} в файле install.exp, то после создания приложения строчка
docker run -d --name=my-test ${DAM_APP_TAG}
преобразуется в
docker run -d --name=my-test mongodb:3.6.21

И которая при установке приложения запустит контейнер с MongoDB.

Если в каталог meta добавить файл mongo.exp, прописать в install, чтобы этот файл скопировался в систему и применить к нему шаблон.
То после установки приложения у нас в системе окажется bash скрипт mongo с командой:
docker run -i -t --rm=true --entrypoint=/usr/bin/mongo mongodb:3.6.21 "$@"

Этот скрипт будет работать с консолью MongoDB. В итогах установки окажется, что нет самой утилиты mongoв системе, только ее обертка. А значит нет и библиотек для ее работы и зависимостей, которые она тянет. Операционная система останется чистой.

Источником шаблонов значений переменных окружения могут быть:

  • файл в make/ENVIRONMENT. Он удобен для систем непрерывной интеграции

  • Dockerfile, заданные в ENV переменные

  • флаги для команды создания приложения, их можно посмотреть dam help create

  • переменные окружения системы с префиксом DAM_

  • зарезервированные переменные окружения, наподобие DAM_APP_TAG

В итоге на данную структуру проекта применяется команда
dam create ./my-app
и приложение создается в локальном кэше docker.

Больше примеров есть в видео:

Заметка робинзона 3

И вот все завертелось с довольно глупого вопроса. А чего мне надо на остров с собой?

Эммм... Конечно же.. топор, чтобы заготавливать дрова.. пила, чтобы пилить деревья для хижины.. лопата, чтобы…Так, стоп. Это совершенно неправильный подход. Так делают все робинзоны. Но мы же живем в цивилизованном мире и хотим комфорта и автоматизаций. Значит, не будем ничего строить на острове с нуля, а привезем все готовое и развернем его уже здесь на острове!

И чего не хватает на необитаемом острове? Даже если есть все инструменты? Конечно же комфорта.

И кажется пришла идейка, как это исправить..

Спустя несколько месяцев..

Дээээээм, где же ты куда запропастился! Брось этот репозиторий! не надо тебе туда лазать, там же невкусный тебе формат.

Вот знакомьтесь, у меня появился помощник Дэм, который мне уже помогает запаковывать все мои поделки, что пригодятся на необитаемом острове.

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

Поиск в репозиториях

Для Docker образов приложений выделю четыре вида хранилища:

  • Docker Hub

  • приватный Docker Registry

  • локальный кэш Docker

  • экспортированные файлы-архивы

При использовании репозиториев Docker Hub и Private Docker Registry возникает неудобство с поиском необходимого приложения и его версии. Для решения этой проблемы в утилиту добавлен поиск в репозиториях командой dam search.

Так же эти источники приложений имеют различное API, поэтому и определены, как два разных хранилища.
Приватных registry в утилите может быть несколько, они могут иметь различную авторизацию: логин и пароль.
Поэтому возникла идея управлять репозиториями. Появились команды:

  • для добавления репозитория
    dam addrepo --name my_repo2 --server test.example.com:5000

  • для изменения
    dam modifyrepo 2 --default #использовать репозиторий 2 по умолчанию

  • для удаления
    dam removerepo my_repo2

В утилите предварительно уже настроен Docker Hub c id репозитория 1.
Поиск в нем осуществляется командой:
$ dam search mongodb
amazonaws/mongodb_exporter:latest
anapsix/mongodb:latest
ansibleplaybookbundle/mongodb-apb:^C
$


Поиск без маски mongodbвозможен только в приватном registry и выводит полный список хранящихся образов.

Более подробно в видео:

Установка приложений

Установка приложения может быть из архива командой:
dam install ./mongodb-3.6.20.a99ac09f-162730321.dam
Перед установкой данный файл предварительно загружается в кэш докера.

А также из репозитория:
dam install mongodb:3.6.20
Здесь образ пулится в кэш. Его источник - настроенный дефолтный репозиторий. Соответственно тэг образа сгенерируется из настроек этого репозитория:

  • для Docker Hub - mongodb:3.6.20

  • для Private Registry - localhost:5000/mongodb:3.6.20

Далее в систему из образа копируется каталог meta со всем содержимым. После чего запускается скрипт install.

Удаление неиспользуемого приложения происходит командой:
dam remove mongodb
При этом запускается uninstall.

Обновление приложение включает в себя удаление старого и установку нового приложения.

Заметка робинзона 4

Лежу на песочке, на пляже, загораю.

Ну я странный робинзон на самом деле - ничего не умею. Ну и лаадно. У меня же есть помощник.

Оу, а с чем этот контейнер стоит на пляже? Как же я про него забыл? Кажется, припоминаю, что там..

Так! Дэм, инфо и инстал вот этот контейнер.

И тут передо мной появляется накрытый стол с бокалом, нет не вина! А обычного мороженного, ведь то, что лежит в таких доставленных контейнерах - не портится...

Работа с дистрибутивом

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

Для этого командой dam export создается текстовый файл, в котором указывается к какому набору приложений должна быть приведена система.

Пример файла:
$ cat app_list
dam:1.2.0
mongodb:3.6.20

Дистрибутив разворачивается командой dam import ./image_list

В случае если приложение уже установлено, то утилита пропустит установку.
Если приложения нет в системе, то она установит его из текущего репозитория или поищет в локальном кэше docker.

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

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

Команда импорта из файла выглядит:
dam import --all ./archive.gz

Заметка робинзона 5

Как говорится: жил, поживал на островке, да случилась беда.

Свой островок наскучил и взгляд нет-нет, да скашивается на соседний, где площади побольше, да песочек по нежнее.

Ну что ж, я знаю Дэм - для тебя не проблема туда сорваться. Погодь, только вещи соберу. И..дэм экспорт! … Тааак вот здесь самое то! … Дэээм импорт! Да! В самый раз. Молодец. Сейчас только свои маленькие вещички разложу и на песочек.
..


Подробнее в видео:

В заключение

Оставлю здесь ссылку на исходники проекта https://github.com/dam-utils/dam/commits/1.x.x

Прототип данной утилиты bash-скрипт для развертывания в docker успешно работает в продакшен компании Smilart около 6 лет.  Его опенсорсная реализация - DAM написанная Go, протестирована в отделе разработки Sedmax.

Надеюсь мне удалось поделиться опытом работы с docker, рассказать о подводных камнях и познакомить с утилитой.
Буду очень рад, если статья кому-нибудь даст новые идеи в работе.
Предлагаю их или возможные неточности обсудить в комментариях.
Благодарю за внимание.

Заметка робинзона 6

Ну что ж, даже комфортной робинзонской эпопее приходит конец. Сейчас все стало по другому. С докером не выживание, на острове где ничего нет, а комфортная жизнь.
И я мог бы до бесконечности получать новинки доставкой, жить на островках, перемещаясь по ним. Пока не случилось. Одно. Но.

Меня потянуло в облака. И со дня на день отправлюсь туда на ракете. А островки мои и помошник, увы, так и остаются здесь. Ведь Дэму далеко до облаков, да и не в этом заключается основная его помощь.

Пусть он так и остается здесь, встречая новых робинзонов!

Вспоминая этот путь жизни, я теперь буду знать, что всегда смогу заизолироваться на островке, как у себя дома, где меня будет ждать и встречать мой верный помощник DAM. Эхх.. Мечты..

Кстати о нем..

Дэээм! Тьфу зараза какой! брось образ! не бери из кэша..

Комментарии (1)


  1. ModestONE
    21.09.2021 11:59

    Прочитал с интересом. Спасибо, отличная статья!