ADG Оставить заявку
Блог DevOps 5 мин чтения

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-резолвинг для обнаружения контейнеров по имени. Схема простая:

  1. Создаёшь user-defined network: docker network create backend-net
  2. Запускаешь контейнеры с --network=backend-net --name=app-backend
  3. Из любого контейнера той же сети можно обратиться по имени: 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-правила руками, либо мириться с тем, что контейнеры разных клиентов технически могут достучаться друг до друга.

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

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.