Docker 1.10: обновили production до новой сетевой подсистемы
Обновили production с Docker 1.9 до 1.10: новая сеть по умолчанию включила user-defined networks, изоляция сервисов без дополнительных прокси.
Docker 1.10 вышел с переработанной сетевой подсистемой: user-defined networks включены по умолчанию, volumes получили собственный lifecycle независимо от контейнеров
Docker 1.10 вышел в начале февраля, и мы достаточно быстро прогнали его через тестовый стенд и обновили несколько production-окружений на сопровождении. Рассказываем, что поменялось на практике - без копирования changelog-а.
Контекст: что нас не устраивало в 1.9
На Docker 1.9 у нас работало несколько проектов с типичной схемой: backend, frontend, база, redis, nginx как точка входа. Для связи контейнеров между собой использовались links - механизм, который работает, но с оговорками.
Проблема links. При перезапуске контейнера его IP менялся. Links прописывались статически при docker run. Если backend перезапускался - nginx переставал его находить, пока его самого не перезапустишь тоже. На небольших проектах с ручным управлением это терпимо. На проектах, где контейнеры перезапускаются по расписанию или при деплое - источник боли.
Обходили по-разному: кто-то держал свой DNS внутри Docker-сети (weave, ambassador-паттерн), кто-то просто перезапускал всё разом и молился. Docker Compose в 1.9 уже умел это чуть лучше через свои внутренние сети, но в ручном управлении ничего не изменилось.
Что изменилось в 1.10
Главное - новая сетевая подсистема теперь не опциональная, она включена по умолчанию и использует DNS-резолвинг для обнаружения контейнеров по имени. Схема простая:
- Создаёшь user-defined network:
docker network create backend-net - Запускаешь контейнеры с
--network=backend-net --name=app-backend - Из любого контейнера той же сети можно обратиться по имени:
http://app-backend:8080
Docker при этом держит внутренний DNS-сервер, который резолвит имя контейнера в актуальный IP. Перезапустился контейнер, получил новый IP - DNS сразу знает. Никаких links, никаких ambassador-ов.
Это то, что мы раньше получали через сторонние решения, теперь из коробки.
Как мигрировали
Миграция с 1.9 на 1.10 прошла без сюрпризов на уровне самого демона. Обновление через apt-get на Ubuntu, перезапуск, контейнеры поднялись. На CentOS 7 через yum - аналогично.
Сложность была не в обновлении движка, а в том, что мы решили одновременно перевести проекты с links на user-defined networks. Делали побъектно: один проект, проверили, следующий.
Типичный переход для проекта с nginx + backend + postgres:
# Создаём сеть для проекта
docker network create --driver bridge proj-net
# Postgres
docker run -d \
--network=proj-net \
--name=proj-postgres \
-v /data/postgres:/var/lib/postgresql/data \
postgres:9.5
# Backend (раньше был --link proj-postgres:db)
docker run -d \
--network=proj-net \
--name=proj-backend \
-e DB_HOST=proj-postgres \
myapp:1.2.3
# Nginx
docker run -d \
--network=proj-net \
--name=proj-nginx \
-p 80:80 -p 443:443 \
nginx:1.9
В конфиге nginx теперь просто proxy_pass http://proj-backend:8080 - и это работает. Не нужен отдельный DNS-контейнер, не нужен weave.
Volumes: наконец-то self-contained
Второе заметное изменение - volumes в 1.10 получили жизненный цикл, независимый от контейнеров. В 1.9 именованные volumes существовали, но управлялись чуть менее удобно. Теперь есть полноценный docker volume API:
docker volume create --name proj-data
docker volume ls
docker volume inspect proj-data
docker volume rm proj-data
Главный эффект: volume больше не удаляется автоматически при docker rm. Чтобы удалить volume вместе с контейнером - нужен явный docker rm -v. Это ломает привычку некоторых скриптов, которые рассчитывали на автоматическую очистку. Мы на нескольких стендах поймали ситуацию, когда при регулярных пересборках начало накапливаться место под старыми volumes.
docker volume ls -f dangling=true и docker volume rm $(docker volume ls -qf dangling=true) стали стандартной строчкой в cron на dev-стендах.
Что насторожило при обновлении
Сети по умолчанию. При обновлении движка существующие контейнеры на default bridge-сети (docker0) остаются там. Links на них продолжают работать. Никакой автомиграции на новые сети нет - это ручная работа. Хорошо для стабильности, но не очевидно: можно думать что всё работает по-новому, а на самом деле работает по-старому.
DNS в user-defined сетях не работает на default bridge. Встроенный DNS-резолвинг по имени контейнера работает только в user-defined networks. В стандартной docker0-сети - нет. Если оставить контейнеры на дефолтной сети и ожидать что имена резолвятся - ничего не выйдет.
Обратная совместимость --link. В 1.10 --link официально помечен как legacy и не рекомендуется. Работает, но предупреждение в документации появилось. Мы не стали тянуть с миграцией.
Текущий статус
Несколько production-проектов уже работают на 1.10 с user-defined networks. Субъективно - схема стала проще. Нет слоя из ambassador-контейнеров или weave-сети, которые добавляли свою сложность и точки отказа.
Изоляция между проектами на одном хосте стала честнее: разные проекты в разных сетях, и по умолчанию они друг друга не видят - без дополнительных firewall-правил. Раньше для этого нужно было либо iptables-правила руками, либо мириться с тем, что контейнеры разных клиентов технически могут достучаться друг до друга.
Производительность сети не замеряли детально, это не было целью обновления. Поведение под нагрузкой - посмотрим на горизонте нескольких недель.