Открыть список
Как стать автором
Обновить

Комментарии 14

Спасибо за статью!
Сейчас в процессе переезда на CI deploy с помощью Rancher.
В похожих статьях всегда обходят стороной проблему «zero downtime deployment» для баз данных.
Если с приложением нет особых проблем держать рабочими сразу одновременно несколько версий, то с БД все сложнее.
Приложение при выкладке может накатывать миграции. Миграции могут поменять структуру базы, а во время переключения пользователи уже сгенерируют данные в БД под старую структуру.
Есть ли хорошее решение этой проблемы?
Поддерживаю вопрос. Особенно интересно если миграция идёт например час, на большой базе.
А без контейнеров как вы это делаете?
По-моему очевидного ответа тут не будет.

Да, вопрос с миграциями в базе очень важный. Решение есть, но серебрянной пули — нет. Нужно следовать одному простому правилу — миграции должны быть всегда обратносовместимы.


Катим новую версию, в ней миграции, эти миграции не должны ломать старую версию. Соответственнок катим сначала миграции, которые пусть и выполняются долго, но все продолжает работать, так как старый код с ними совместим. А уже как миграции прошли — катим новый код.


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


Какие это правило накладывает ограничения? С первого взгляда можно только добавлять колонки и только с необязательным значением. Если менять типы колонок, то только совместимо. Ну и добавлять новый таблицы безбоязненно. По началу выглядит пугающе, мол как так — мы не можем переименовать или удалить колонку. Но фактически, это надо делать редко. И это всегда можно сделать в два релиза (сначала выкатываем код, который больше не использует не нужную колонку/таблицу, а в следующем релизе — удаляем колонку/таблицу).


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

И еще момент. Если вдруг у вас миграции блокируют базу, то тут надо менять что-то в базе или в миграциях. В миграциях можно попробовать такие инструменты как pt-online-schema-change, но лучше подумать о том, как бы уменьшить базу.


Ну и есть еще схема с Blue Green Deployment, с остановкой слейва и накатом миграции только на нем, с SQL based репликацией. Но вживую я не видел.

Как обновлять с zero-downtime пару контейнеров nginx, которые содержат статику из приложения? Понятно, что их надо спрятать за haproxy и обновлять поочередно? Но как сделать так, чтобы коннекты при этом не оборвались?

Ещё не решал такую задачу, но мне видится это, как сочетание
http://serverfault.com/questions/249316/how-can-i-remove-balanced-node-from-haproxy-via-command-line и
https://nginx.org/ru/docs/http/ngx_http_stub_status_module.html


Трафик снимается, затем через периодические запросы stub_status проверяется, что коннектов не осталось, затем можно обновлять.


Можно даже не заморачиваться с sub_status, у haproxy можно забирать по csv текущую статистику, включая и количество активных запросов к бэкэндам.

Можно просто выключить nginx «gracefully», он подождет пока все открытые коннекты отработают и выключится

Тоже вариант. Но всё равно стоит это соединить с предварительной просьбой frontend'у не слать новые запросы на этот backend. Без этого несколько запросов могут потеряться, пока frontend сам не поймёт, что этот backend больше ему недоступен.

Если вкратце — то сочетание graceful shutdown, встроенного в nginx, и правильно подобранного таймаута. Если это обычная статика (js, css, картинки) и обычные клиенты, то таймаут в 10 секунд норм. Если превалируют медленные клиенты (мобилки, хот споты) или большие объемы (видео блобы и пр.) — таймаут можно увеличивать. Подбирать таймаут по перцентиль 99 response time в Nginx (он как раз и включает время до последнего отданного байта).


Ну и важный момент, что Docker по умолчанию шлет SIGTERM при остановке контейнера, а Nginx'у нужно послать SIGQUIT. Как этого добиться — очень сильно зависит (используете ли какой-то супервизор, или сразу запускаете Nginx, используете голый Docker, или с какой-то оркестрацией).

Можно реализовать примерно так.
На haproxy настраиваете проверку «здоровья» серверов
backend app
  ...
  option httpchk GET /health_check/
  http-check expect string success
  ...


А в приложении — ответ на этот урл. Для переключения можно использовать файл-флаг.

Одновременная работа двух версий приложений с разной структурой БД?

Выше писал, в коментах. Миграции по-определению должны быть обратно совместимы, хотя бы в рамках одной версии. Это фундоментальное ограничение, пока не слышал никаких альтернатив.

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

Информация

Дата основания
Местоположение
Россия
Сайт
flant.ru
Численность
101–200 человек
Дата регистрации
Представитель
Дмитрий Шурупов

Блог на Хабре