Docker 1.0 в staging: bridge-сеть не для нескольких хостов
Перевели staging на Docker 1.0 и упёрлись в сетевую модель: контейнеры на разных хостах не видят друг друга без ручного тоннеля. Смотрим на flannel и weave.
Docker 1.0 вышел в июне 2014 - первый стабильный релиз; сообщество активно обсуждает как организовать сети между контейнерами на разных хостах
После того как Docker 1.0 объявили стабильным, мы решили, что настало время перевести staging-окружение с разрозненных виртуалок на контейнеры. На одном хосте всё заработало быстро. Проблемы начались ровно тогда, когда мы попытались распределить нагрузку на два хоста.
Что такое bridge-сеть и где она заканчивается. Docker по умолчанию создаёт на хосте виртуальный мост docker0 и вешает контейнеры на него. Каждый контейнер получает адрес из подсети 172.17.0.0/16 - и в пределах одного хоста это работает отлично. Контейнеры видят друг друга, можно линковать их через --link, жизнь прекрасна. Но стоит поднять второй хост с Docker - и он тоже поднимает свой docker0 с той же подсетью 172.17.0.0/16. Два хоста, две одинаковые подсети, никакой маршрутизации между ними. Контейнер с хоста A понятия не имеет о существовании контейнера с хоста B.
Первая реакция - проброс портов. Выставляешь нужный порт наружу через -p, прописываешь внешний IP хоста в конфиг приложения и живёшь дальше. Это работает, но это именно то, от чего уходили: статические адреса, ручная конфигурация, хрупкость. К тому же часть сервисов общается не по одному порту, а по нескольким, и конфигурировать каждый отдельно - занятие унылое.
Вторая попытка - GRE-тоннель между хостами вручную. Подняли тоннель, настроили маршрутизацию, добились связности. Работает. Но это теперь отдельная инфраструктура, которую надо поддерживать, мониторить и пересоздавать при каждом добавлении хоста. При трёх хостах это ещё терпимо, при десяти - кошмар.
Что предлагает сообщество. Начали смотреть, что делают другие. Обнаружили два активно обсуждаемых варианта:
- Flannel от CoreOS. Идея простая: etcd хранит общий пул адресов, каждый хост получает из него подсеть и не пересекается с соседями. Трафик между хостами оборачивается в UDP. Настройка относительно прямолинейная, документация есть. Минус - нужен etcd, это ещё один компонент инфраструктуры.
- Pipework от Jérôme Petazzoni. Другой подход: shell-скрипт поверх стандартных сетевых инструментов Linux, создаёт оверлейную сеть без лишних демонов. Выглядит чуть примитивнее, зато не требует отдельного сервиса конфигурации и не добавляет новых движущихся частей.
Потыкали оба в лабе. Flannel поднялся предсказуемо - синтаксис понятный, пара команд, и контейнеры на разных хостах начинают видеть друг друга. Pipework удобен для быстрого старта, но при добавлении хоста конфигурацию придётся прописывать вручную.
Что настораживает. Оба инструмента молодые. Flannel появился совсем недавно вместе с CoreOS-экосистемой, pipework тоже не успел обрасти production-историей. Это не значит, что они плохие, но означает, что граблей впереди неизвестное количество. Операционный overhead тоже реальный: flannel добавляет дополнительный уровень инкапсуляции, и что это сделает с латентностью под нагрузкой - ещё предстоит выяснить.
Производительность пока не мерили как следует - это следующий шаг. Хочется прогнать iperf между контейнерами через нативный bridge, через GRE-тоннель и через каждый из кандидатов, прежде чем принимать решение.
Плюс отдельный вопрос - управление жизненным циклом клиентских инсталляций в рамках managed-инфраструктуры: когда у клиента несколько площадок, overlay-сеть добавляет ещё одну движущуюся часть, которую надо держать под наблюдением. Если что-то падает между хостами на уровне оверлея - отлаживать сложнее, чем обычные сетевые проблемы.
Пока решение такое: в staging оставляем GRE-тоннель с ручной конфигурацией, параллельно поднимаем стенд с flannel и гоняем на нём нагрузочные тесты. Если результаты окажутся приемлемыми - переходим. Если нет - смотрим дальше.
Docker 1.0 хорош для одного хоста. Для нескольких - пока приходится выбирать между «костыльно, но понятно» и «элегантно, но молодо».