Fig + Docker: один yaml-файл вместо тридцати строк баша
Пробуем Fig - инструмент для описания многоконтейнерных приложений через YAML. Web + db + cache в одном файле, один командой запускается и останавливается.
Fig - инструмент управления многоконтейнерными приложениями на Docker, появившийся параллельно с выходом Docker 1.0 в июне 2014
В посте про Docker 1.0 мы упоминали shell-скрипт на тридцать строк, который поднимает тестовый стенд: запустить postgres, дождаться готовности, запустить flask, запустить nginx, прибить всё в правильном порядке при остановке. Работает, но страшновато смотреть. Тогда же мельком упомянули Fig - инструмент, который обещает описать это всё в одном YAML-файле. На этой неделе взяли и попробовали нормально.
Что такое Fig
Fig - это отдельная утилита поверх Docker, написанная на Python. Не часть Docker, а сторонний проект от orchardup. Установка банальная:
pip install fig
Суть проста: вместо набора docker run с флагами описываешь все контейнеры в fig.yml в корне проекта. Дальше:
fig up- поднять весь стекfig stop- остановитьfig rm- убрать контейнерыfig logs- собрать логи из всех контейнеров в один поток
Это то, чего нам не хватало. Не какая-то абстрактная оркестрация, а просто «описать локальный стенд один раз».
Конфигурация нашего стенда
Взяли задачу, которую уже делали руками: Flask-приложение, PostgreSQL, Redis для кеширования сессий. Три контейнера, два из которых Flask должен видеть.
Вот fig.yml который у нас получился:
web:
build: .
ports:
- "5000:5000"
links:
- db
- cache
volumes:
- .:/app
environment:
- FLASK_ENV=development
db:
image: postgres:9.3
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=myapp
- POSTGRES_PASSWORD=devpassword
cache:
image: redis:2.8
fig up - и через несколько секунд все три контейнера запущены, Flask видит db и cache через переменные окружения (те же POSTGRES_PORT_5432_TCP_ADDR что и при ручном --link). fig stop - всё остановлено. Для локальной разработки это именно то что нужно.
Что реально изменилось в работе
Несколько наблюдений после недели использования.
Первое - новый разработчик поднимает стенд за минуту. Раньше инструкция по развёртыванию локального окружения занимала страницу в вики: установи PostgreSQL, создай пользователя, создай базу, запусти Redis, пропиши переменные. Теперь это pip install fig && fig up. Всё. Это не абстрактное «экономит время» - это конкретная страница вики, которую мы удалили.
Второе - fig logs сделал жизнь заметно проще. При ручном запуске трёх контейнеров логи жили в трёх местах. Когда Flask падал из-за проблем с базой - нужно было открыть отдельный терминал, сделать docker logs pg, потом вернуться к Flask-логу, сопоставить временные метки. С fig logs всё течёт в один поток с именем контейнера в начале каждой строки. Диагностика стала быстрее.
Третье - volumes: - .:/app меняет подход к разработке. Flask видит изменения в коде сразу, без пересборки образа. Сохранил файл - обновление живое. Это не новая идея, docker run -v так умел всегда, но когда это прописано в fig.yml рядом с остальной конфигурацией - не забываешь и не путаешься.
Где Fig пока скрипит
Было бы нечестно писать только хорошее.
Порядок запуска. Fig запускает контейнеры параллельно, учитывая links, но не проверяет готовность сервиса внутри контейнера. Та же проблема что с ручным --link - Flask может стартовать пока Postgres ещё инициализирует базу. Наш pg_isready-цикл в entrypoint никуда не делся.
Тома. В примере выше pgdata - это относительный путь, Docker монтирует директорию pgdata/ рядом с fig.yml. Fig обрабатывает это корректно, но документация по этому месту скудная. Пришлось проверять docker inspect чтобы убедиться что данные куда надо пишутся.
Пересборка образа. fig up не пересобирает образ автоматически если Dockerfile изменился. Нужно явно fig build перед fig up. Это контр-интуитивно когда только начинаешь - несколько раз получали «почему мои изменения не применились».
Это только для локальной разработки
Важно понимать: мы используем Fig исключительно для dev-окружений. Деплой через Fig на production-сервер - идея плохая по тем же причинам, по которым мы не торопимся с Docker в продакшн вообще. Fig знает об одном хосте, не умеет в рестарт при падении хоста, не имеет механизма обновления без даунтайма.
Но для локальной разработки - это ровно та инструментальная ступенька, которой не хватало. Docker научился изолировать контейнеры, а Fig научил нас описывать их связи в коде, а не в голове или в вики. fig.yml живёт в репозитории рядом с Dockerfile и requirements.txt, коммитится, ревьюируется, обновляется вместе с проектом.
Наш shell-скрипт на тридцать строк удалили. Немного грустно - мы в него вложили душу. Но нет.
Инструмент в активной разработке, API ещё может меняться - это надо учитывать. Но основная идея уже работает надёжно, и мы видим как несколько коллег-разработчиков подхватили подход без нашей инициативы. Хороший знак.