Как работает PCI-Express и почему вам это нужно? #GPU



Что такое PCI-Express?

Все, и я имею в виду все, должны обращать внимание, когда они проходят интенсивное обучение машинному обучению / глубокому обучению.

Как я объяснял в предыдущем сообщении блога, графические процессоры значительно ускорили эволюцию искусственного интеллекта.



Однако создать сервер на графических процессорах не так-то просто. А отсутствие соответствующей инфраструктуры может сказаться на времени обучения.

Если вы используете графические процессоры, вы должны знать, что есть 2 способа подключить их к материнской плате, чтобы позволить ей подключаться к другим компонентам (сети, ЦП, устройству хранения). Решение 1 — через PCI Express, а решение 2 — через SXM2 . Мы поговорим о SXM2 в будущем. Сегодня мы сосредоточимся на PCI Express . Это связано с тем, что он сильно зависит от выбора соседнего оборудования, такого как шина PCI или ЦП.



Дизайн SXM2 VS Дизайн PCI Express
Это основной элемент, который следует учитывать при разговоре о глубоком обучении, поскольку этап загрузки данных — это пустая трата времени вычислений, поэтому пропускная способность между компонентами и графическими процессорами является ключевым узким местом в большинстве контекстов обучения глубокому обучению.

Как работает PCI-Express и почему нужно заботиться о количестве линий PCIe? Что такое линии PCI-Express и есть ли связанные с этим ограничения ЦП?

Каждый GPU V100 использует 16 линий PCI-e. Что именно это означает?

Выписка из NVidia V100 продукта спецификации листа

В «х16» означает, что PCIe имеет 16 выделенных полос. Итак… следующий вопрос: что такое полоса PCI Express?

Что такое линия PCI Express?

2 устройства PCI Express с его внутренним соединением: рисунок, вдохновленный потрясающей статьей - что такое чипсет и почему меня это должно волновать

Дорожки PCIe используются для связи между устройствами PCIe или между PCIe и ЦП. Полоса состоит из двух проводов: один для входящей связи, а другой с удвоенной пропускной способностью трафика для исходящего.

Связь по дорожкам похожа на связь сетевого уровня 1 — все дело в максимально быстрой передаче битов по электрическим проводам! Однако метод, используемый для PCIe Link, немного отличается, поскольку устройство PCIe состоит из линий xN. В нашем предыдущем примере N = 16, но это может быть любая степень двойки от 1 до 16 (1/2/4/8/16).

Итак… если PCIe похож на сетевую архитектуру, это означает, что уровни PCIe существуют, не так ли?

Да! Вы правы, PCIe имеет 4 слоя:



Физический уровень (также известный как уровень больших переговоров )

Физический уровень (PL) несет ответственность за ведение переговоров условия для получения исходных пакетов (PLP для пакетов физического уровня) , то есть ширина полосы частот , и с другим устройством.

Вы должны знать, что будет использоваться только наименьшее количество полос из двух устройств. Вот почему так важен выбор подходящего процессора. ЦП имеют ограниченное количество линий, которыми они могут управлять, поэтому наличие хорошего графического процессора с 16 линиями PCIe и наличие ЦП с 8 линиями шины PCIe будет так же эффективно, как выбросить половину ваших денег, потому что она не помещается в ваш кошелек.

Пакеты, полученные на физическом уровне (также известный как PHY) , поступают от других устройств PCIe или из системы (например, через память прямого доступа — DAM или от ЦП) и инкапсулируются в кадр.

Назначение Start-of-Frame — сказать: «Я отправляю вам данные, это начало», и для этого требуется всего 1 байт!


Слово « конец кадра» также составляет 1 байт, чтобы сказать «до свидания, я сделал это».


Этот уровень реализует декодирование 8b / 10b или 128b / 130b, которое мы объясним позже и в основном используется для восстановления тактовой частоты.

Пакет уровня канала передачи данных (он же давайте разберем этот беспорядок в правильном порядке )

Пакет уровня канала передачи данных (DLLP) начинается с порядкового номера пакета. Это действительно важно, поскольку в какой-то момент пакет может быть поврежден, поэтому может потребоваться однозначная идентификация для повторных попыток. Порядковый номер кодируется на 2 байта.

Layer Data Link Packet
 затем следуют уровне транзакций пакетов , а затем закрывается с МЦРК (Local Циклические Redundancy Check) и используется для проверки слоя транзакции пакета (значение фактического Payload) целостность.

Если LCRC подтвержден, то уровень звена данных отправляет сигнал ACK (ACKnowledge) на передатчик через физический уровень . В противном случае он отправляет сигнал NAK (Not AcKnowledge) на передатчик, который повторно отправит кадр, связанный с порядковым номером, для повторной попытки; эта часть обрабатывает буфер воспроизведения на стороне получателя .

Уровень транзакций

Уровень транзакции отвечает за управление фактической полезной нагрузкой (заголовок + данные), а также (необязательный) дайджест сообщения ECRC (сквозная циклическая проверка избыточности) . Этот пакет уровня транзакции поступает с уровня звена данных, где он декапсулирован .

При необходимости / запросе выполняется проверка целостности . Этот шаг будет проверять целостность бизнес - логику и не будет гарантировать , нет коррупции пакетов при передаче данных от Data Link Layer для транзакций уровня.

Заголовок описывает тип транзакции, например:

  • Операция памяти
  • Транзакция ввода-вывода
  • Транзакция конфигурации
  • или сообщение транзакции



Уровень приложения

Роль прикладного уровня  — обрабатывать логику пользователя . Этот уровень отправляет заголовок и полезные данные на уровень транзакции . Магия происходит на этом уровне, где данные привязаны к разным аппаратным компонентам.

Как PCIe общается с остальным миром?

PCIe Link использует концепцию коммутации пакетов, используемую в сети в полнодуплексном режиме.

Устройство PCIe имеет внутренние часы для управления циклами передачи данных PCIe Этот цикл передачи данных также организуется благодаря ссылочным часам. Последний отправляет сигнал через выделенную полосу (которая не является частью x1 / 2/4/8/16/32, упомянутой выше) . Эти часы помогут как принимающим, так и передающим устройствам синхронизироваться для передачи пакетов.

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

x16 означает 16 линий параллельной связи по протоколу PCIe 3 поколения

У вас может быть порядок байтов, но есть ли у вас целостность данных на физическом уровне?

Для обеспечения целостности устройство PCIe использует кодировку 8b / 10b для PCIe поколений 1 и 2 или схему кодирования 128b / 130b для поколений 3 и 4.

Эти кодировки используются для предотвращения потери временных ориентиров, особенно при передаче последовательных одинаковых битов. Этот процесс называется « Восстановление часов ».

Эти 128 бит данных полезной нагрузки отправляются, и к ним добавляются 2 байта управления.

Быстрые примеры

Давайте упростим это на примере 8b / 10b: согласно IEEE 802.3, пункт 36, таблица 36–1a на основе спецификаций Ethernet здесь представляет собой кодировку таблицы 8b / 10b:

IEEE 802.3 пункт 36, таблица 36–1a - таблица кодирования 8b / 10b

Итак, как получатель может различить всех, кто повторяет 0 (имя кодовой группы D0.0)?



Кодирование 8b / 10b состоит из кодировок 5b / 6b + 3b / 4b.

Следовательно, 00000 000 будет закодировано в 100111 0100, 5 первых битов исходных данных 00000 будут закодированы в 100111 с использованием кодирования 5b / 6b ( rd + ); то же самое касается второй группы из 3 бит исходных данных 000, закодированных в 0100 с использованием кодирования 3b / 4b ( rd- ).

Это могло бы быть также 5b / 6b кодирования й + и 3b / 4b кодирование RD- решений 00000 000 превращается в 011000 1011

Следовательно, исходные данные, которые были 8-битными, теперь являются 10-битными из-за управления битами (1 управляющий бит для 5b / 6b и 1 для 3b / 4b).


Но не волнуйтесь, я позже напишу в блоге сообщение, посвященное кодированию.

PCIe поколения 1 и 2 были разработаны с кодированием 8b / 10b,
 что означает, что фактические передаваемые данные составляли только 80% от общей нагрузки (поскольку 20% — 2 бита используются для синхронизации часов).

PCIe Gen3 и 4 были разработаны с 128b / 130b,
 что означает, что управляющие биты теперь составляют только 1,56% полезной нагрузки. Неплохо, не правда ли?

Давайте вместе рассчитаем пропускную способность PCIe

Вот таблица спецификаций версий PCIe



Консорциум PCI-SIG Теоретическая пропускная способность PCIe / Технические характеристики полосы / пути



консорциум PCI-SIG PCIe теоретическая необработанная спецификация скорости передачи данных. Чтобы получить такие числа, давайте посмотрим на общую формулу пропускной способности:



  • BW означает пропускную способность
  • MT / s: мегапереводы в секунду
  • Кодирование может быть 4b / 5b /, 8b / 10b, 128b / 130b,…

Для PCIe v1.0:





Таким образом, с 16 полосами для NVIDIA V100, подключенными к PCIe v3.0 , эффективная скорость передачи данных (пропускная способность данных) составляет почти 16 ГБ / с / путь ( фактическая пропускная способность составляет 15,75 ГБ / с / путь ).

Вы должны быть осторожны, чтобы не запутаться, поскольку общую пропускную способность также можно интерпретировать как двухстороннюю пропускную способность; в этом случае мы считаем, что общая пропускная способность x16 составляет около 32 ГБ / с.

Примечание: Еще один элементкоторый мы не рассматривал, что максимальные потребности теоретической пропускной способности будут снижены примерно1 Гбит / с для протоколов коррекции ошибок ( ЦЭР и МЦРК ), а также в заголовках ( Начальный тег, последовательность теги, заголовок ) иНакладные расходы на нижний колонтитул ( конечный тег) объяснялись ранее в этом сообщении в блоге.

В заключение

Мы видели, что PCI Express сильно изменился и основан на тех же концепциях, что и сеть. Чтобы извлечь максимум из устройств PCIe, необходимо понимать основы базовой инфраструктуры.

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

Подводить итоги:

Друзья не позволяют друзьям создавать собственные хосты на GPU

Переход к хранилищу Ceph нового поколения в OVHcloud с LXD

Вступление

Меня зовут Филип Дорош. Я работаю в OVHcloud с 2017 года инженером DevOps. Сегодня я хочу рассказать вам историю о том, как мы развернули Ceph следующего поколения в OVHcloud. Но сначала несколько слов о Ceph: Ceph — это программно определяемое решение для хранения данных, которое поддерживает дополнительные тома публичного облака OVHcloud, а также наш продукт Cloud Disk Array. Но я не буду утомлять вас маркетинговыми вещами — давайте начнем!



Похоже, это интересная задача ...

Полтора года назад мы начали очень знакомый спринт. Помимо обычных вещей, с которыми нам приходится иметь дело, была одна задача, которая выглядела немного интереснее. Заголовок гласил: « Оцените, можем ли мы запускать новые версии Ceph на нашем текущем программном обеспечении ». Нам нужны были более новые версии Ceph и Bluestore для создания решения Ceph следующего поколения со всеми флеш-хранилищами.

Наше программное решение (которое мы называем устаревшим решением) основано на Docker. Звучит действительно круто, но мы запускаем Docker немного иначе, чем по назначению. Наши контейнеры очень сохраняют состояние. Мы запускаем полноценную систему инициализации внутри контейнера в качестве точки входа в докер. Затем эта система инициализации запускает все необходимое программное обеспечение внутри контейнера, включая Puppet, который мы используем для управления «вещами». Похоже, мы используем контейнеры Docker так же, как контейнеры LXC, не так ли?…

Наша унаследованная инфраструктура Ceph (аллегория)

Вскоре выяснилось, что невозможно запускать более новые выпуски Ceph в нашем внутреннем решении, потому что новые версии Ceph используют systemd, а в нашем текущем решении мы вообще не запускаем systemd — ни внутри контейнеров, ни на хозяева, которые их принимают.

Началась охота за решениями. Одна из возможностей заключалась в том, чтобы упаковать Ceph самостоятельно и избавиться от systemd, но это большой объем работы с небольшой добавленной стоимостью. Сообщество Ceph предоставляет проверенные пакеты, которые необходимо использовать, так что этот вариант не рассматривался.

Второй вариант — запустить Ceph с супервизором внутри контейнера Docker. Хотя это звучит как план, даже в документации supervisord четко указано, что supervisord «не предназначен для запуска вместо init как« process id 1 ».». Так что это тоже явно не вариант.

Нам нужен был systemd!

На этом этапе стало ясно, что нам нужно решение, позволяющее запускать systemd как внутри контейнера, так и на хосте. Похоже, настало идеальное время для перехода на совершенно новое решение — решение, которое было разработано для запуска полной ОС внутри контейнера. Поскольку наш Docker использовал бэкэнд LXC, это было естественным выбором для оценки LXC. В нем были все необходимые функции, но с LXC нам пришлось бы самостоятельно кодировать всю автоматизацию, связанную с контейнерами. Но можно ли избежать всей этой дополнительной работы? Оказывается, может…

Поскольку я использовал LXD в своем предыдущем проекте, я знал, что он способен управлять образами, сетями, блочными устройствами и всеми хорошими функциями, которые необходимы для настройки полнофункционального кластера Ceph.

Поэтому я переустановил свои серверы разработчиков с выпуском Ubuntu Server LTS и установил на них LXD.



LXD имеет все необходимое для создания полнофункциональных кластеров Ceph:

  • он поддерживает «толстые» контейнеры с отслеживанием состояния,
  • он поддерживает systemd внутри контейнера,
  • он поддерживает образы контейнеров, поэтому мы можем подготовить индивидуальные изображения и без проблем использовать их,
  • передача целых блочных устройств в контейнеры,
  • передача обычных каталогов в контейнеры,
  • поддержка простого запуска, остановки, перезапуска контейнера,
  • REST API, который будет рассмотрен в следующих частях статьи,
  • поддержка нескольких сетевых интерфейсов в контейнерах с использованием macvlan.

После нескольких часов ручной работы у меня был кластер Ceph, на котором запущен выпуск Mimic внутри контейнеров LXD. Я набрал ceph health и получил «HEALTH_OK». Приятно! Это сработало отлично.

Как нам это индустриализировать?

Чтобы индустриализировать его и подключить к нашей плоскости управления, нам нужен был модуль Puppet для LXD, чтобы Puppet мог управлять всеми элементами, связанными с контейнером, на хосте. Такого модуля, который предоставлял бы необходимую нам функциональность, не существовало, поэтому нам пришлось его кодировать самостоятельно.

Демон LXD предоставляет удобный REST API, который мы использовали для создания нашего модуля Puppet. Вы можете общаться с API локально через сокет unix и через сеть, если вы настроили его раскрытие. Для использования в модуле было действительно удобно использовать команду запроса lxc, которая работает, отправляя необработанные запросы в LXD через сокет unix. Модуль теперь имеет открытый исходный код на GitHub, поэтому вы можете скачать его и поиграть с ним. Он позволяет настраивать основные параметры LXD, а также создавать контейнеры, профили, пулы хранения и т. Д.

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



Модуль LXD Puppet на момент написания этого документа предоставляет следующие определения:

  • lxd :: profile
  • lxd :: изображение
  • lxd :: хранилище
  • lxd :: container

Для получения полной справки посетите его страницу GitHub — github.com/ovh/lxd-puppet-module.

Ручная установка VS Автоматическая установка с Puppet

Я покажу вам простой пример того, как создать точно такую ​​же настройку вручную, а затем снова автоматически с помощью Puppet. Для целей этой статьи я создал новый экземпляр Public Cloud с Ubuntu 18.04, один дополнительный диск и уже настроенное мостовое устройство br0. Предположим, есть также DHCP-сервер, прослушивающий интерфейс br0.

Стоит отметить, что обычно вам не нужно создавать собственное изображение, вы можете просто использовать вышестоящие со встроенными командами. Но для целей этой статьи давайте создадим собственный образ, который будет в точности похож на исходный. Чтобы создать такой образ, вам просто нужно ввести несколько команд, чтобы перепаковать исходный образ в Unified Tarball.

root @ ubuntu: ~ # wget https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64-root.tar.xz 
root @ ubuntu: ~ # wget https: // cloud-images .ubuntu.com / bionic / current / bionic-server-cloudimg-amd64-lxd.tar.xz 
root @ ubuntu: ~ # mkdir -p ubuntu1804 / rootfs 
root @ ubuntu: ~ # tar -xf bionic-server-cloudimg-amd64 -lxd.tar.xz -C ubuntu1804 / 
root @ ubuntu: ~ # tar -xf bionic-server-cloudimg-amd64-root.tar.xz -C ubuntu1804 / rootfs / 
root @ ubuntu: ~ # cd ubuntu1804 / 
root @ ubuntu : ~ / ubuntu1804 # tar -czf ../ubuntu1804.tar.gz *


В конце вы получите образ ubuntu1804.tar.gz, который можно использовать с LXD. Для целей этой статьи я поместил это изображение в каталог, доступный через HTTP, например: example.net/lxd-image/

Ручная настройка

Первым делом давайте установим LXD.

root @ ubuntu: ~ # apt install lxd

Во время установки пакета вы увидите сообщение: «Чтобы пройти начальную конфигурацию LXD, запустите: lxd init», но мы просто сделаем все шаги вручную.

Следующим шагом будет добавление нового пула хранения.

root @ ubuntu: ~ # lxc storage create default dir source = / var / lib / lxd / storage-pools / 
default Создание пула хранения по умолчанию


Затем создайте собственный профиль, который будет иметь: переменную окружения http_proxy, установленную на ”, ограничение памяти 2 ГБ, крыши в пуле хранения по умолчанию и eth0, который будет частью моста br0.

root@ubuntu:~# lxc profile create customprofile
Profile customprofile created
root@ubuntu:~# lxc profile device add customprofile root disk path=/ pool=default
Device root added to customprofile
root@ubuntu:~# lxc profile device add customprofile eth0 nic nictype=bridged parent=br0
Device eth0 added to customprofile
root@ubuntu:~# lxc profile set customprofile limits.memory 2GB


Распечатываем весь профиль, чтобы проверить, все ли в порядке:

root@ubuntu:~# lxc profile show customprofile
config:
  environment.http_proxy: ""
  limits.memory: 2GB
description: ""
devices:
  eth0:
    nictype: bridged
    parent: br0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: customprofile
used_by: []


Затем давайте загрузим изображение LXD в формате Unified Tarball:

root@ubuntu:~# wget -O /tmp/ubuntu1804.tar.gz http://example.net/lxd-images/ubuntu1804.tar.gz


И импортируйте его:

root@ubuntu:~# lxc image import /tmp/ubuntu1804.tar.gz --alias ubuntu1804
Image imported with fingerprint: dc6f4c678e68cfd4d166afbaddf5287b65d2327659a6d51264ee05774c819e70


Когда все будет готово, давайте создадим наш первый контейнер:

root@ubuntu:~# lxc init ubuntu1804 container01 --profile customprofile
Creating container01


Теперь давайте добавим несколько каталогов хоста в контейнер:
обратите внимание, что вы должны установить правильного владельца каталога на хосте!

root@ubuntu:~# mkdir /srv/log01
root@ubuntu:~# lxc config device add container01 log disk source=/srv/log01 path=/var/log/


И в качестве последнего штриха добавьте в контейнер раздел хоста:

root@ubuntu:~# lxc config device add container01 bluestore unix-block source=/dev/sdb1 path=/dev/bluestore


/ dev / sdb1 будет доступен внутри контейнера. Мы используем его для передачи устройств для Ceph's Bluestore в контейнер.

Контейнер готов к запуску.

root@ubuntu:~# lxc start container01


Вуаля! Контейнер запущен и работает. Мы настраиваем наши контейнеры так же, как указано выше.

Хотя это было довольно просто настроить вручную. Для массового развертывания вам необходимо автоматизировать вещи. Итак, теперь давайте сделаем это, используя наш модуль LXD Puppet.

Автоматическая установка с Puppet

Чтобы использовать модуль, загрузите его на свой марионеточный сервер и поместите в путь к модулю.

Затем создайте новый класс или добавьте его к одному из существующих классов; все, что вам подходит.



Я подключил его к своему марионеточному серверу. Обратите внимание, что я использую мостовое устройство br0, которое было подготовлено ранее другими модулями, и что изображения LXD размещаются на веб-сервере example.net/lxd-images/ в виде унифицированных тарболов.

Полный пример модуля, использующего модуль LXD Puppet:

class mymodule { 
 
    class {':: lxd':} 
 
    lxd :: storage {'default': 
        driver => 'dir', 
        config => { 
            'source' => '/ var / lib / lxd / storage-pools / default ' 
        } 
    } 
 
    lxd :: profile {' exampleprofile ': sure 
        =>' present ', 
        config => { 
            ' environment.http_proxy '=>' ', 
            ' limits.memory '=>' 2GB ', 
        }, 
        devices => { 
            'root' => { 
                'path' => '/', 
                'pool' => 'default',
                'type' => 'disk', 
            }, 
            'eth0' => {
                'nictype' => 'bridged', 
                'parent' => 'br0', 
                'type' => 'nic', 
            } 
        } 
    } 
 
    lxd :: image {'ubuntu1804': sure 
        => 'present', 
        repo_url => ' http://example.net/lxd-images/ ', 
        image_file =>' ubuntu1804.tar.gz ', 
        image_alias =>' ubuntu1804 ', 
    } 
 
    lxd :: container {' container01 ': 
        state =>' start ', 
        config => { 
            'user.somecustomconfig' => 'Моя потрясающая пользовательская переменная env',
        }, 
        profiles => ['exampleprofile'], 
        image => 'ubuntu1804',
        устройства => { 
            'log' => { 
                'path' => '/ var / log /', 
                'source' => '/ srv / log01', 
                'type' => 'disk', 
            }, 
            'bluestore' = > { 
                'путь' => '/ dev / bluestore', 
                'source' => '/ dev / sdb1', 
                'type' => 'unix-block', 
            } 
        } 
    } 
}


Теперь осталось только запустить марионеточный агент на машине. Он применит желаемое состояние:

root@ubuntu:~# puppet agent -t
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Loading facts
Info: Caching catalog for ubuntu.openstacklocal
Info: Applying configuration version '1588767214'
Notice: /Stage[main]/Lxd::Install/Package[lxd]/ensure: created
Notice: /Stage[main]/Lxd::Config/Lxd_config[global_images.auto_update_interval]/ensure: created
Notice: /Stage[main]/Mymodule/Lxd::Storage[default]/Lxd_storage[default]/ensure: created
Notice: /Stage[main]/Mymodule/Lxd::Profile[exampleprofile]/Lxd_profile[exampleprofile]/ensure: created
Notice: /Stage[main]/Mymodule/Lxd::Image[ubuntu1804]/Exec[lxd image present http://example.net/lxd-images//ubuntu1804.tar.gz]/returns: executed successfully
Notice: /Stage[main]/Mymodule/Lxd::Container[container01]/Lxd_container[container01]/ensure: created
Notice: Applied catalog in 37.56 seconds


В конце концов, у вас будет запущен новый контейнер:

root@ubuntu:~# lxc ls
+-------------+---------+--------------------+------+------------+-----------+
|    NAME     |  STATE  |        IPV4        | IPV6 |    TYPE    | SNAPSHOTS |
+-------------+---------+--------------------+------+------------+-----------+
| container01 | RUNNING | 192.168.0.5 (eth0) |      | PERSISTENT | 0         |
+-------------+---------+--------------------+------+------------+-----------+


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



Как это хорошо !?

Я призываю всех внести свой вклад в модуль или поставить ему звезду на GitHub, если вы сочтете его полезным.

Планы на будущее

После всестороннего тестирования мы уверены, что все работает, как задумано, и были уверены, что сможем приступить к разработке нового решения с хранилищем Ceph на всех флеш-хранилищах, без жестких дисков.

В будущем мы планируем перенести всю нашу устаревшую инфраструктуру на новое решение на основе LXD. Это будет гигантский проект для миграции: более 50 ПБ размещается на более чем 2000 выделенных серверах, но это уже история для другого раза.

Еще один день в жизни ProxySQL: делиться заботой

Этот пост — еще одна часть нашего списка коротких постов, в которых описываются конкретные случаи, с которыми сталкивался OVHcloud как при подготовке к миграции, так и во время нее. Здесь мы рассказываем историю о том, как иногда небольшое неожиданное поведение может привести к исправлению ошибки в программном обеспечении, используемом миллионами людей по всему миру.



В этом посте я объяснил, как нам пришлось продвинуть proxySQL за его пределы.

Жюльен объяснил здесь, насколько сложно перехватить часть программы и заставить ее работать для очень специфического случая использования. Один настолько специфический, что мы просто не могли продвинуть его вверх по течению, поскольку мы нарушали некоторые предположения в базовом коде (что-то вроде «смотри, мы не используем это, может быть, мы можем отклонить его, чтобы он соответствовал нашему использованию»).

Но иногда, копаясь все глубже и глубже в строках кода, расширяя границы программ с открытым исходным кодом все дальше и дальше, мы обнаруживаем ошибки.

Вот два реальных случая, когда у нас было неожиданное поведение ProxySQL, в результате чего появился патч для MariaDB / MySQL.

1. Важность цитирования


Неожиданное поведение

Когда мы впервые начали использовать ProxySQL, у нас было неожиданное поведение. ProxySQL раньше зависал. Ни предупреждения, ни предвестника. Зависание означает, что ProxySQL не может обрабатывать ваши запросы, в результате чего ваши веб-сайты становятся недоступными.

Копать

Системные администраторы знают, что в случае сомнений проверяйте журналы. Вот что мы сделали и заметили вот это:

[ОШИБКА] Обнаружено разорванное соединение во время SET NAMES на 192.168.59.272, 3306: 2019, Невозможно инициализировать набор символов (null) (путь: compiled_in)


ОШИБКА 1064 (42000): у вас есть ошибка в синтаксисе SQL; проверьте руководство, которое соответствует вашей версии сервера MySQL, чтобы найти правильный синтаксис для использования рядом с «двоичным» в строке 1


Это явно ошибка, возвращенная сервером MySQL. Поскольку эта строка журнала была создана на ProxySQL, это означает, что ошибка произошла между нашим ProxySQL и сервером MySQL.

В поисках золота

Жюльен много работал — он прочитал тонны журналов, отследил множество PID и отследил проблему вплоть до ИСПОЛЬЗОВАНИЯ триггерного случая, выполнив эту команду:

set names binary COLLATE binary;


Сопоставление — это своего рода набор правил для сравнения строк. Он может определять, например, при сортировке по алфавиту, если Aидет раньше a, éследует ли рассматривать как eили нет, или где œдолжно быть в алфавите.

Вы можете узнать больше об этом в Базе знаний MariaDB.

Исправление

Отправив сообщение об обнаруженной нами проблеме в системе отслеживания ошибок ProxySQL и предложив исправление, автор ProxySQL взглянул и подтвердил ошибку.

При написании ProxySQL Рене Канна делал то же, что и все разработчики, — следовал документации коннектора MariaDB. И ошибка была отсюда:

According to the documentation on SET NAMES syntax (https://dev.mysql.com/doc/refman/5.7/en/set-names.html) :

charset_name and collation_name may be quoted or unquoted

This doesn't seem true for "SET NAMES binary COLLATE binary" , that requires the collation name to be quoted.


(https://bugs.mysql.com/bug.php?172id=93692)

Эффект бабочки

Итак, из-за зависания нашей инфраструктуры мы сообщили об ошибке создателю проекта, который затем отследил ее до ошибки в коннекторе MariaDB, проследил ее до родительского MySQL и исправил в восходящем направлении. Ошибка была закрыта в начале 2020 года.

Тем временем мы работали с г-ном Канна, чтобы найти обходной путь (в основном, заставляя имя сортировки указывать в коде ProxySQL).

2. Когда провода перекрещиваются

Во время написания этой статьи я вспомнил еще один забавный баг. Я решил проверить статус этой ошибки и заметил, что мы никогда о ней не сообщали. Моя ошибка исправлена.

Неожиданное поведение

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

MySQL_Session.cpp:2964:handler(): [WARNING] Error during query on (42,192.168.59.272,3306): 1267, Illegal mix of collations (utf8_general_ci,IMPLICIT) and (dec8_swedish_ci,COERCIBLE) for operation '='


Копать

Конечно, в первую очередь мы проверили, что мы используем настоящую и допустимую кодировку и соответствующее сопоставление. Мы дважды и трижды проверили, что нам разрешено использовать dec8_swedish_ci.

Мы решили взглянуть на исходный код коннектора.

В поисках золота

Если вам интересно, вы можете взглянуть на более старые версии кода libmariadb / my_charset.c, начиная со строки 541. Вы заметите, что dec8_swedish_ci нигде не найти. Но если вы присмотритесь, вы заметите dec8_swedisch_ci. Дорогие друзья из Швеции, опечатка была сделана не только вами.

Исправление

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

Мы разделили репозиторий GitHub mariadb-corporation / mariadb-connector-c , исправили несколько опечаток и предложили запрос на перенос, который затем был объединен в середине февраля.

Эффект бабочки

Все склонны к опечаткам и ошибкам. С некоторыми мы столкнулись, они были исправлены и портированы для всех.



Вывод

Как ведущий поставщик веб-хостинга в Европе, мы сталкиваемся с законом действительно больших чисел ( en.wikipedia.org/wiki/Law_of_truly_large_numbers ). TL; DR: произойдет каждое событие с ненулевой вероятностью.

У нас размещено более 1,3 миллиона веб-сайтов. Предположим, что 0,0001% наших клиентов могут вызвать одну конкретную ошибку — у нас есть по крайней мере 1 клиент, который ее вызовет. Вопрос не в том, если, а в том, когда нам придется с этим бороться и как это сделать.

Хотите знать, как мы заметили, что попали в Закон действительно больших чисел? Следите за обновлениями, ведь мы скоро напишем об этом.

Поделиться заботой

OVHCloud любит открытый исходный код и любит делиться. Это не первый раз, когда мы вносим свой вклад в мир открытого исходного кода, и определенно не последний.

Объясняя медленные запросы моему менеджеру

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



Прежде чем идти дальше, давайте резюмируем, что это за четыре типа запросов:

  • Выбрать для чтения данных
  • Вставка предназначена для добавления данных
  • Обновление предназначено для изменения данных, которые уже существуют
  • Удалить для удаления данных

my-database=# select * from customers where nic = XXX;
my-database=# insert into customers values (1, 'user-firstname', 'user-lastname', '21');
my-database=# update customers set address = 'xxx xxx' where nic = 'XXX';
my-database=# delete from customers where nic = XXX


Когда дело доходит до рабочих нагрузок SQL, существует два разных типа: OLTP и OLAP .

Рабочие нагрузки OLTP

OLTP (для O н L ине Т ransactional Р rocess) рабочие нагрузки соответствуют «органической» использованием баз данных. Эти операции используются для более эффективного использования баз данных веб-сайтов, API-интерфейсов, платформ электронной коммерции и т. Д. В то время как OLAP полагается исключительно на чтение, рабочие нагрузки OLTP полагаются на все типы запросов, включая выбор, вставку, обновление и удаление. В OLTP мы хотим, чтобы запросы отвечали как можно быстрее, обычно менее чем за несколько сотен миллисекунд. Причина этой потребности в скорости состоит в том, чтобы уменьшить влияние запросов на взаимодействие с пользователем вашего приложения. В конце концов, кто любит веб-сайты, которые загружаются бесконечно? Что касается количества запросов, мы обычно считаем их тысячами в секунду.

my-database=# insert into cart values (...); -- create a new cart
my-database=# insert into cart_content values (...); -- add items
my-database=# update cart_content set ... where ...; -- modify your cart
my-database=# select item_name, count from cart_content where ...; -- check the content
my-database=# insert into sales values (...); -- validate the cart


Рабочая нагрузка OLAP

OLAP (для O н L INE A nalytic P rocess) рабочие нагрузки используются для извлечения и анализа больших объемов данных (отсюда и название). Они являются основным инструментом, используемым программными платформами бизнес-аналитики для составления прогнозов и отчетов. Что касается запросов, рабочие нагрузки OLAP обычно полагаются исключительно на несколько избранных, которые периодически выполняются и могут занять много времени (от минут до часов).

Как видите, рабочие нагрузки OLTP и OLAP очень разные. Их сравнение похоже на сравнение гоночного автомобиля (OLTP, надеюсь) с грузовиком (OLAP).

Классифицирующие запросы: хорошие, плохие, уродливые… и медленные

Теперь, когда у нас есть общее представление о двух типах рабочих нагрузок, давайте сосредоточимся на OLTP, поскольку они обычно наиболее актуальны для частей ваших платформ, ориентированных на клиентов. В начале этого поста я описал четыре различных типа SQL-запросов с точки зрения их назначения. Теперь мы классифицируем их по поведению: хорошее, плохое, уродливое и медленное. Что это значит, спросите вы? Разрешите пояснить (спойлер: вы хотите, чтобы ваши запросы попадали в категорию «хорошо»)…



Добро

Как и следовало ожидать, хорошие запросы — это те, которые выполняются должным образом и отвечают относительно быстро. В OVHcloud мы определяем «быстро» как время отклика менее одной секунды для наших внутренних баз данных. Но одна секунда — это еще долгое время для ожидания ответа, особенно когда выполняется несколько запросов для загрузки одной веб-страницы. Обычно мы стремимся к 10-20 мс. Вы должны нарисовать эту «быструю линию» в зависимости от ваших настроек, ресурсов и предполагаемого использования.

Ваш бэкэнд запросит базу данных и получит ответ, скажем, через 20 мс, что оставит достаточно времени для обработки данных и отправки результата. Чем быстрее будут выполняться ваши запросы, тем счастливее будут ваши клиенты и начальник.

Когда я хочу объяснить своему боссу, почему быстрые запросы хороши, это довольно просто: быстрые запросы означают хорошее взаимодействие с пользователем, быстрый заказ, быструю оплату и большую прибыль.

my-database=# select firstname, lastname from customers where id = 123;


Плохо

С другой стороны, плохие запросы — это запросы, которые не могут быть выполнены СУБД. Причин может быть несколько: ошибка в коде, отсутствие контроля где-то в процессе и т. Д.

Возьмем пример. На вашем веб-сайте есть форма, в которой пользователи могут создать учетную запись, в которой они указывают свой возраст. В пользовательском интерфейсе текстовое поле позволяет пользователю вводить все, что он хочет, и передавать значение в виде строки. Но если ваша схема хорошо спроектирована, в поле «возраст» должно быть указано целое число. Таким образом, если пользователь пытается ввести свой возраст в виде строки в поле, а не числа, СУБД должна вернуть ошибку. Решение простое: форма пользовательского интерфейса должна проверять тип данных, заполненных в поле, и возвращать сообщение об ошибке, например «недопустимые данные», в интерфейсе пользовательского интерфейса, вместо того, чтобы ждать, пока СУБД сделает это. В подобных случаях рекомендуется разрешать только числа.

my-database=# insert into user values (1, 'user-firstname', 'user-lastname', 'twenty years old');
ERROR:  invalid input syntax for integer: "twenty years old"


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

Моему боссу я бы объяснил, что неверные запросы являются препятствием для клиентов, желающих использовать ваш сервис, и приводят к потере прибыли. Однако из-за их простой природы их обычно относительно легко отлаживать.

Уродливый

Уродливые запросы более проблематичны. Это запросы, которые иногда работают, а иногда нет из-за тупиковых ситуаций.

Тупики — обширная тема, но пока давайте будем простыми. Тупиковая ситуация возникает, когда несколько запросов ждут завершения друг друга. Давайте посмотрим на следующий пример:

-- STEP #1
process 1: BEGIN; -- this will start the transaction
process 2: BEGIN;
 
-- STEP #2
process 1: UPDATE stock SET available = 10 WHERE id = 123; -- lock row 123
process 2: UPDATE stock SET available = 0 WHERE id = 456; -- lock row 456
 
-- STEP #3 The ugly part starts now
process 1: UPDATE stock SET available = 1 WHERE id = 456; -- wait for lock on row 456
process 2: UPDATE stock SET available = 9 WHERE id = 123; -- wait for lock on row 123


Мы видим, что у нас есть два процесса, пытающихся обновить запас в рамках транзакции. Процессы №1 и №2 блокируют разные строки. Процесс №1 блокирует строку 123, а процесс №2 блокирует строку 456 на этапе 2. На этапе 3, не снимая блокировки с текущей строки, процесс №1 пытается получить блокировку для строки 456, которая уже принадлежит процесс №2, и наоборот. Чтобы завершить транзакцию, они оба ждут друг друга. Я выбрал простой пример с двумя запросами, но эта проблема может возникнуть с десятками или тысячами запросов одновременно. Общие правила при работе с транзакциями — совершать их как можно быстрее.

Сложность заключается в том, что запрос может быть совершенно правильным и работать большую часть времени, особенно в вашем конвейере CI / CD, где угловые случаи и редкие события не обязательно выявляются и тестируются. Но чем больше растет ваш бизнес, тем выше вероятность возникновения этих редких событий, поскольку вы увеличиваете количество выполняемых одновременно запросов. И, к сожалению, наиболее вероятно, что проблемы с тупиками возникают во время пиков нагрузки, вызванных продажами, праздниками и т. Д. Другими словами, именно тогда, когда вам нужно, чтобы ваш рабочий процесс работал идеально.

Чтобы объяснить это своему боссу, я бы сказал, что когда происходит взаимоблокировка, что-то не так в серверной части, в схеме базы данных или в самой логике рабочего процесса. В худшем случае проблема возникнет в самый неудобный момент, поэтому клиент не сможет взаимодействовать с вашей системой и даже не получит понятное сообщение об ошибке, которое будет сложно исправить. Чтобы понять, отладить и исправить тупиковые ситуации, нужно время. К тому времени, как вы предложите исправление, ваши клиенты будут тратить свои деньги в другом месте, или ваша поддержка будет рушиться из-за билетов и звонков.

process 34099 detected deadlock while waiting for ShareLock on transaction 4026716977 after 1000.042 ms
DETAIL: Process holding the lock: 34239. Wait queue: .
CONTEXT: while locking tuple (259369,24) in relation "table"
STATEMENT: SELECT col from table where ...


Ваша любимая СУБД в конечном итоге убьет все запросы, но только по истечении заданного времени ожидания. И, конечно же, тайм-аут означает, что ваши клиенты будут ждать результата, прежде чем выдаст ошибку. Ага, это некрасиво…

И медленный…

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

  • Плохо написанные запросы (а в продукте этого нет?)
  • Отсутствующие индексы
  • Получено слишком много строк
  • Слишком много данных для обработки

В этом случае моему боссу объяснения не нужны. Медленные запросы означают более медленные вызовы API, более медленный пользовательский интерфейс и меньшее количество клиентов, достигающих стадии оформления заказа.

Исправление может быть простым: переписать запросы, найти и добавить недостающие индексы и получить только то, что необходимо. Однако уменьшить объем данных, которые должны проходить ваши запросы, немного сложнее. Это можно сделать с помощью регулярных чисток в вашей БД, архивирования, разбиения на разделы и т. Д. Но на практике вы должны хранить только актуальные и актуальные данные в ваших клиентских базах данных, чтобы избежать раздувания.

Вывод

Подведем итоги и подведем итоги:

  • Хорошие запросы — признак здоровой рабочей нагрузки. Чем больше у тебя есть, тем лучше
  • Плохие запросы означают, что где-то что-то сломано
  • Уродливые запросы ждут худшего момента, чтобы дать вам пощечину
  • Медленные запросы означают, что у вас что-то работает, но есть возможности для улучшения

И последний совет… это не разовая работа. Вам нужно внимательно следить за этими четырьмя категориями запросов.

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

migrate-datacentre –quiet: как легко перенести центр обработки данных?

В наших предыдущих статьях мы объяснили, почему нам пришлось перенести 800 000 баз данных из одного центра обработки данных в другой, на расстоянии 300 километров. Итак, мы… Мы сделали это! Теперь мы сосредоточимся на очень важной цели любой миграции. С вашей точки зрения, как покупатель, вы должны видеть… Ничего! Я полностью это понимаю, так как я также являюсь клиентом OVH. И как клиент, оплачивающий услугу, я хочу, чтобы она работала, независимо от того, что делают люди за кулисами.



В этом посте мы увидим, как нам удалось (почти) легко переместить их всех…

Основы

Как веб-сайт подключается к базе данных, размещенной на другом сервере? Краткий ответ: по сети. Поскольку диаграмма всегда лучше, чем длинный ответ, вот упрощенное представление о том, как она работает…

Веб-сервер проверяет конфигурацию веб-сайта, чтобы определить, где находится база данных:

$database_server = 'mydatabase.mysql.db';
$database_name = 'mydatabase';
$database_user = 'mydatabase';
$database_password = 'correct horse battery staple';
# ref: https://www.xkcd.com/936/


Затем он начинает диалог с сервером доменных имен (DNS), чтобы получить IP-адрес mydatabase.mysql.db. Как только IP известен, веб-сервер может взаимодействовать с сервером базы данных.



Но наличие одного сетевого имени для каждой базы данных — это что-то относительно новое в истории OVH. Впервые он был представлен пять лет назад, а здесь описана архитектура Gravelines.

20-летнее наследие

Вначале мы давали нашим клиентам IP-адрес сервера базы данных.

С положительной стороны, вы избежали запросов к DNS-серверу, вы сэкономили время, вы избежали возможного узкого места и, возможно, даже избежали потенциальной точки отказа. С другой стороны, если нам нужно было изменить сервер, а его IP-адрес не мог быть перемещен — скажем, когда происходила миграция — каждому клиенту приходилось вручную изменять конфигурацию своего веб-сайта.

$database_server = '10.0.59.42';
$database_name = 'mydatabase';
$database_user = 'mydatabase';
$database_password = 'correct horse battery staple';
# ref: https://www.xkcd.com/936/


Поскольку у нас есть постоянные клиенты (включая многих из вас, в данном случае!), Это была неплохая идея, но не очень масштабируемая.



Итак, чтобы упростить управление сервером, окончание срока службы оборудования и т. Д., OVHcloud перешла на предоставление нашим клиентам имени сервера вместо IP-адреса сервера.

$database_server = 'mysql55-59.pro';
$database_name = 'mydatabase';
$database_user = 'mydatabase';
$database_password = 'Brussels Sprouts Mandela Effect';
# ref: https://www.xkcd.com/2241/




Подводя итог, у нас есть три способа подключения к базе данных в P19:

  • Использование IP сервера
  • Использование имени сервера
  • Использование имени базы данных

Блокиратор

Следуя правилам, описанным здесь, поскольку мы перемещали ваши базы данных по отдельности, а не весь сервер за раз, а базы данных перетасовывались во все наши новые экземпляры, а не оставались с их соседом, мы не могли повторно использовать IP-адрес сервера. Кроме того, мы не могли повторно использовать имя сервера.

Мы серьезно подумали об автоматическом изменении всех конфигураций наших клиентов.

Значит, нам пришлось бы:

  • Разбирайте сотни терабайт. Выполнимо, но требует много времени.
  • Разберитесь в организации вашего сайта. Это можно автоматизировать для 99% всех различных случаев использования, но 1% из 1,5 млн веб-сайтов по-прежнему представляют собой 15 000 потенциально поврежденных веб-сайтов после замены.
  • Замените IP или имя сервера именем базы данных. Возможно, но не на 100% надежно.

Что, если заказчик:

  • Использует свою базу данных в двоичном файле, например, скомпилированный CGI, написанный на C? Не разбирается.
  • Использует другую базу данных, чем та, на которую есть ссылка? Как мы узнаем, что нужно использовать?
  • Не пользуется своей базой? Как мы узнаем, что это действительно правильный результат, если ничего не нашли?

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

Если конфигурация была:

  • mydatabase.mysql.db: Нет проблем. DNS был обновлен.
  • имя_сервера: запрос достигнет сервера и его нужно будет перенаправить.
  • IP-адрес сервера: запрос достигнет сервера и его необходимо будет перенаправить.

Решение

Может быть, некоторые из вас уже начали искать решение…

Нам требовалось программное обеспечение, которое маскировалось бы под сервер, но фактически перенаправляло входящие соединения на реальный сервер, стоящий за ним. Другими словами, прокси!

Мы решили использовать proxySQL Рене Канна.

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

Сторона Парижа

Прежде всего, нам пришлось развернуть его на сотнях серверов в P19.

Наша архитектура позволяла нам устанавливать по одному ProxySQL на каждый хост базы данных. Это было сделано в период с 27 ноября по 6 декабря 2019 года (http://travaux.ovh.net/?do=details&id=35499).



Как только все они будут запущены, мы сможем начать миграцию. Для каждой базы данных был обновлен ProxySQL, поэтому он отслеживал, где на самом деле находилась база данных, в Париже или в Gravelines.



Легко, не правда ли? Поскольку ProxySQL работает на том же хосте, нам не нужно было ничего подделывать, и это работало как шарм. Но что произойдет, если веб-сервер в Gravelines вызовет базу данных, которая все еще находится в Париже?



Сторона Гравелина

На этот раз у нас не было выбора, кроме как подделать имя сервера в Gravelines. Что мы могли использовать? Конечно же, ProxySQL! Но в нашей новой архитектуре мы просто не могли установить их на всех хостах, потому что, как было замечено ранее, у нас в Gravelines в 10 раз меньше клиентов на каждый экземпляр, чем в Париже.

Поэтому нам пришлось настроить ферму ProxySQL и ложный DNS, который разрешает члену фермы запросы о серверах в Париже. И, используя тот же механизм обновления конфигурации ProxySQL во время миграции, мы наконец получили такую ​​инфраструктуру:



Но как насчет конфигураций с IP-адресами в них? Поскольку у нас был ProxySQL, загруженный пользователями для группы серверов P19, мы могли — благодаря уловке с маршрутизацией — заставить веб-серверы думать, что IP-адреса P19 были настроены на ProxySQL!



Обратная сторона

Вы только что узнали, что для того, чтобы миграция произошла в требуемые сроки, нам пришлось внедрить ProxySQL. Даже если это отличное программное обеспечение, которое было почти спасением для нас, как системных администраторов, мы предпочитаем избегать обслуживания все большего и большего количества различных решений на ежедневной основе. Вот почему мы решили отказаться от поддержки способов подключения к вашей базе данных, кроме .mysql.db, в ближайшие месяцы. Подробности появятся в ближайшее время.

Если вы хотите сохранить наш современный способ решения задач (а также помочь нам обеспечить плавность изменений!), Вы можете проверить, что ваш веб-сайт уже использует .mysql.db в ваших файлах конфигурации.

Вывод

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

Однако вы могли заметить, что все прошло не так гладко, как мы хотели. Мы столкнулись с множеством мелких, иногда неожиданных проблем, которые нужно было исправить как можно скорее…

Вы, возможно, помните ненормальное количество «max_user_connections» ( travaux.ovh.net/?do=details&id=35689 ).
Нам пришлось поиграться с исходным кодом ProxySQL, чтобы разрешить его использование против mysql 5.1 в сочетании с алгоритмом хеширования паролей до mysql 4.0. Мы обнаружили забавные вещи о кодировании в исходном коде, который мы исправили в апстриме.Мы смогли заполнить таблицу ARP нашего хоста несколько раз в час. Вы узнаете больше об этих проблемах в наших следующих публикациях!

Как выиграть в игре по массовой миграции базы данных

В наших предыдущих статьях мы объяснили, почему нам пришлось перенести 800 000 баз данных из одного центра обработки данных в другой, на расстоянии 300 километров. Итак, мы… Моя команда и я сделали это! Это было настоящей головной болью, поэтому я надеюсь, что наша история поможет вам обратиться к большему количеству крупных технических проектов, с которыми мы любим играть.



Правила

  • Чтобы уменьшить задержку, базу данных необходимо переносить одновременно с веб-сайтом, который ее использует.
  • Поскольку базы данных распределены по всем доступным серверам MySQL, детализацией миграции должна быть база данных, а не экземпляр MySQL. Другими словами, мы не можем перенести весь сервер MySQL. Мы должны переместить только его часть.
  • Поскольку ссылка между веб-сайтом и его базой данных не обязательно указывается, веб-сайт в Gravelines должен иметь возможность связываться с базой данных в Париже (например), и наоборот.
  • Чтобы связаться со своей базой данных, веб-сайт использует имя хоста, имя пользователя и пароль. Мы хотим, чтобы миграция была прозрачной, чтобы никому не пришлось изменять какие-либо из этих элементов, чтобы связаться с их новой базой данных.
  • Платформы баз данных меняются между Paris и Gravelines, как показано ниже.

Подводя итог, вот что у нас было до миграции веб-кластера:



И это то, что мы хотим после миграции:



Еще несколько вещей…

  • Очевидно, что при работе с базами данных мы должны помнить об одном из самых важных моментов: согласованности. Для каждой базы данных нам нужно было определить точку согласованности. До этого момента на временной шкале чтение и запись производились в Париже. После этого чтение / запись производились в Gravelines.
  • Мы верим в прозрачность и обратимость. Это обе ключевые части нашего SMART-облака. Вот почему мы хотели предоставить вам доступ к этой точке согласованности в виде дампа на панели управления OVHcloud . Для каждой перенесенной базы данных мы решили предоставить вам доступ к дампу на один месяц.
  • Перенос 800 КБ баз данных примерно за 60 ночей означал, что мы должны были быть очень быстрыми и масштабируемыми. Наш рекорд был 1 июля 2019 года, когда мы успешно перенесли 13 502 базы данных за 1 час 13 минут и 31 секунду.
  • Если вы привыкли быть на работе, вы знаете, что ночью ваше внимание и работоспособность ниже. Повторение процесса миграции примерно 60 раз в год усилит это, поэтому мы хотели, чтобы все было максимально автоматизировано и как можно проще. Как мы увидим позже, для запуска миграции базы данных нам просто нужно было запустить одну команду на одном хосте:

migrate-p19


Теперь вы знаете правила, пора начинать игру!

1-й уровень

Первый уровень всегда легкий, где вы узнаете, как работает игра, с помощью своего рода учебника! Итак, начнем с небольшой миграции базы данных. Вот как мы это делаем:

1. У источника (Париж)

  • Установите режим только для чтения. Нам абсолютно необходимо избегать записи во время миграции, чтобы избежать известного  разделения мозга . Самый простой способ сделать это — перевести базу данных в режим только для чтения. В большинстве случаев веб-сайтам нужно только читать базы данных, но в некоторых случаях им нужно читать и писать, и поэтому они будут сломаны. Это не проблема, потому что сайт сейчас перенесен и закрыт. Мы заблокируем доступ на запись, если база данных используется другим хостом, на который ночная миграция не влияет.
  • Дамп базы данных и дамп куда-нибудь положить. Мы решили хранить дампы в публичном облачном хранилище (PCS) OVHcloud , так как мы уже используем это решение для хранения 36 миллионов дампов в месяц. Добавление 800 000 дампов за год — не проблема для этой потрясающей платформы!



2. В пункте назначения (Гравелин)

  • Получите дамп и импортируйте его.
  • Создайте пользователя и разрешения с правом записи.



3. Переключитесь на новую базу данных.

  • На данный момент веб-сайт все еще обращается к базе данных в Париже. Чтобы веб-сайт (независимо от того, размещен ли он в Париже или Gravelines) мог связаться с новой базой данных, мы обновим DNS, чтобы имя указывало на экземпляр Gravelines MySQL, а не на Париж.
  • Доступ для чтения к базе данных Paris также удален.
  • Наконец, мы обновим нашу информационную систему, чтобы вы могли получить дамп с PCS через панель управления. Это обновление также позволяет нам перенаправить все действия, доступные из Панели управления (например, изменение пароля, создание дампа…), в новую базу данных на Gravelines.



Уровень 2: «Децентрализованный государственный автомат»

Чтобы подтвердить концепцию миграции, мы сначала выполнили все эти шаги вручную и последовательно. Естественный способ автоматизировать это — написать сценарий, который сделает то же самое, но быстрее. Это централизованный метод, но такие методы рано или поздно обнаруживают узкие места и подразумевают единую точку отказа.

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



Используя этот конечный автомат, мы можем выполнить эти три больших шага на разных машинах для распараллеливания рабочей нагрузки:

  • Источник
  • Назначение
  • Тот, который обновляет DNS

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

Мозг миграции: CloudDB

Нам нравится концепция «ешьте свою собственную еду»! Это лучший контроль качества, который когда-либо был, и ваши отзывы — это наш первый источник запросов на добавление функций. Поэтому неудивительно, что мы использовали наш собственный продукт CloudDB для хранения графов состояний миграций баз данных.

Технически граф состояний — это строка в таблице. Упрощенная структура этой таблицы выглядит так:

- database_name VARCHAR(255) PRIMARY KEY,
- source VARCHAR(255),
- destination VARCHAR(255),
- status VARCHAR(255) NOT NULL DEFAULT 'Waiting',
- dump_url TEXT


За исключением dump_url, все поля заполняются до начала миграции. Другими словами, мы знаем, где находятся базы данных и где они будут.

Мы прошли все испытания этого уровня. Пришло время победить последнего монстра!

Уровень 3. Перенести 800 КБ баз данных.

Теперь, когда мы знаем, как перенести одну базу данных децентрализованно, давайте заполним CloudDB всеми базами данных, которые мы хотим перенести! Вот как теперь выглядит миграция:

В Париже

Примерно раз в минуту * каждый хост из 780 серверов баз данных спрашивает CloudDB, есть ли у них что-то для сброса. В sourceи statusстолбцах таблицы используются для получения этой информации:

SELECT … WHERE source = me AND status = 'To dump';


Если так, они выполняют свои задачи и обновляют CloudDB о том, что они делают. Когда они закончили, они передают эстафету перехода к Gravelines:

UPDATE … SET status = 'To import' WHERE database_name = '…';


В Gravelines

В то же время в 300 километрах сотни серверов баз данных также спрашивают CloudDB, есть ли у них что-то для импорта. Как и в Париже, они запрашивают CloudDB примерно раз в минуту *. В destinationи statusстолбцах таблицы используются для получения этой информации:

SELECT … WHERE destination = me AND status = 'To import';


Если да, они выполняют свои задачи и обновляют CloudDB о том, что они делают. По завершении они передают эстафету третьему роботу, который изменяет записи DNS для этой миграции базы данных:

UPDATE … SET status = 'DNS to update' WHERE database_name = '…';


(*) Чтобы избежать переполнения CloudDB, мы используем случайную частоту для запроса базы данных с графами состояний. Таким образом, соединения глобально распределяются во времени.

Обновление DNS

Робот, отвечающий за обновление DNS, является третьим игроком в процессе миграции и работает так же, как роботы дампа и импорта, описанные выше.

Не все так просто ...

Конечно, на самом деле игра была более сложной. Это была упрощенная версия миграции, в которой некоторые шаги отсутствовали или недостаточно детализированы, например:

  • Предотвращение записи в исходную базу данных
  • Обновление IS (среди прочего), чтобы вы могли видеть дамп в Панели управления
  • Установка пароля в пункте назначения (такого же, как у источника), не зная этого
  • И многие другие

Но теперь, когда у вас есть концепция основных шагов, вы можете представить, как мы справились с остальными.

Чит-код: Итерация!

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

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

Но есть чит-код, чтобы победить этого босса: повторять!

  • Начать миграцию
  • Столкнуться с проблемой
  • Исправьте это окончательно (не только для конкретного случая, в котором произошел сбой, но и для всех похожих случаев на всей платформе)
  • Тогда попробуйте еще раз, быстрее!

Этот способ возможен благодаря двум причинам:

  • Волшебная команда
  • Большая красная кнопка

Волшебная команда

Как упоминалось выше, для запуска миграции базы данных нам пришлось запустить одну команду на одном хосте:

migrate-p19


У этой волшебной команды есть один параметр: количество параллельных миграций, которые вы хотите сделать. Мы использовали 400для этого параметра.

migrate-p19 --max-procs 400


Это означает, что 400 баз данных выгружаются или импортируются одновременно — ни больше, ни меньше.

Команда migrate-p19- это планировщик. Он обновляет CloudDB каждые 10 секунд, поэтому эти 400 миграций всегда выполняются параллельно:

SELECT COUNT(database_name) … WHERE status in ('To dump', 'Dumping', 'Dump failed', 'To import', …);
42
UPDATE … SET status = 'To dump' WHERE status = 'Waiting' LIMIT (400 - 42);


Пауза в игре: большая красная кнопка

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

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

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

Продолжение следует…

Репликация базы данных 101

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



Узлов недостаточно: они должны работать вместе. Для этого мы используем функцию, называемую репликацией, для обеспечения синхронизации данных на всех узлах кластера. При включенной функции репликации все изменения, сделанные на основном узле, распространяются и применяются к остальной части кластера, так что одни и те же данные хранятся на каждом узле. При работе с репликацией необходимо учитывать определенные компромиссы. Поскольку существует несколько типов репликации, мы должны сначала взглянуть на то, как работает репликация.

Асинхронная репликация

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



Полусинхронная репликация

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



Синхронная репликация

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



Пример из реального мира

Но реальный пример стоит тысячи технических объяснений: представьте, что у вас есть веб-сайт, на котором продаются предметы ручной работы. У вас есть два покупателя и товар, у которого осталась одна единица. Теперь представьте, что первый покупатель покупает последний предмет, а второй покупатель проверяет наличие товара в то же самое время, когда первый завершает свою покупку.

Покупка товара — это запрос на запись, так как вам нужно обновить запас, поэтому вы должны выполнить его на основном узле. Отображение веб-страницы — это запрос на чтение, поэтому вы решаете выполнить его на узле реплики. Что произойдет со вторым покупателем, если он / она отобразит веб-страницу в тот же самый момент, когда первый покупатель получит подтверждение покупки? Он видит товар как в наличии, так и вне его.

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



Это может сбивать с толку: так бывает всегда. Настоящая разница между режимами здесь заключается в том, что в синхронном режиме покупка первого клиента займет больше времени (не завершится, пока все реплики не применили изменение). Логическая разница не между временем, которое требуется для применения изменения, а между тем, когда первый клиент видит, что его покупка завершена.

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

Теперь вы можете понять, к чему я иду: операции асинхронной репликации требуют меньше времени для завершения и, таким образом, делают ваши приложения более реактивными, но допускают нежелательные последствия, такие как предметы, появляющиеся на складе, когда их нет.

Как видите, вы выбираете либо пропускную способность и реактивность, либо безопасность. Нет правильного ответа, это в основном зависит от вашего варианта использования . К счастью, некоторые системы управления базами данных (СУБД), такие как PostgreSQL , позволяют вам определять уровень безопасности, который вы хотите для данного запроса. Это означает, что вы можете использовать синхронную репликацию, когда клиент совершает покупку на сумму не менее 1000 долларов, и использовать асинхронную репликацию в противном случае.

И какой метод использует OVHcloud?

В OVHcloud мы управляем несколькими критически важными базами данных , от банковских транзакций до параметров DNS для всех наших доменных имен или для наших панелей управления публичным и частным облаком , информации, передаваемой нашими API-интерфейсами , и так далее. Мы выбрали асинхронную репликацию для всех наших баз данных. Мы справляемся с недостатками асинхронности, максимально уменьшая задержку , делая ее незначительной. Более того, наши разработчики имеют опыт и, следовательно, знакомы с этим компромиссом и поэтому могут принимать лучшие проектные решения, соответствующие создаваемому ими приложению . Итак, ребята, помните об этом компромиссе, ипрежде чем настраивать СУБД, подумайте о том, чего вы действительно хотите от своего приложения .

Инфраструктура внутренних баз данных OVHcloud

Сегодня большинство приложений прямо или косвенно полагаются на базы данных. Я бы даже сделал ставку и сказал, что большая часть из них — реляционные базы данных. В OVHcloud мы используем несколько десятков кластеров, на которых размещены сотни баз данных, для работы тысяч приложений. Большинство из этих баз данных используют наш API, информацию для выставления счетов и сведения о клиентах.



Как член команды, ответственной за эту инфраструктуру, я могу сказать, что поддерживать такую ​​важную часть бьющегося сердца OVHcloud — огромная ответственность.

В этой новой серии сообщений блога мы более подробно рассмотрим внутреннюю инфраструктуру реляционных баз данных OVHcloud. Этот первый пост про инфраструктуру внутренних баз данных. В OVHcloud мы используем 3 основных СУБД (системы управления базами данных), PostgreSQL MariaDB и MySQL, каждая из которых опирается на одну и ту же кластерную архитектуру.

Но сначала, что такое кластер? Кластер — это группа узлов (физических или виртуальных), работающих вместе для предоставления службы SQL.

В OVHcloud у нас есть открытый исходный код и культура «сделай сам». Это позволяет нам контролировать наши расходы и, что более важно, осваивать технологии, на которые мы полагаемся.

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

Каждый кластер состоит из 3 узлов, каждый из которых выполняет свою роль — первичный, репликационный и резервный.

Первичный узел предполагает рабочие нагрузки чтения и записи, а реплики обрабатывают только запросы только для чтения. Когда основной узел выходит из строя, мы продвигаем узел-реплику, чтобы стать основным узлом. Поскольку в подавляющем большинстве случаев базы данных обрабатывают гораздо больше запросов только для чтения, чем запросы для чтения и записи, можно добавить узлы-реплики для масштабирования возможностей кластера только для чтения. Это называется горизонтальным масштабированием. Наш последний узел предназначен для операций резервного копирования. Резервные копии невероятно важны, мы поговорим о них чуть позже.



Поскольку каждый узел в кластере может быть повышен до основного, они должны иметь возможность обрабатывать одну и ту же рабочую нагрузку. Таким образом, у них должны быть точно такие же ресурсы (ЦП, ОЗУ, диск, сеть…). Это особенно важно, когда нам нужно продвинуть реплику, потому что она должна будет обрабатывать ту же рабочую нагрузку. В этом случае наличие неодинакового размера первичной и реплики может иметь катастрофические последствия для вашей рабочей нагрузки. Когда наши кластеры запущены и работают, мы можем начать опрашивать их. Каждый кластер может содержать одну или несколько баз данных в зависимости от нескольких факторов, таких как стоимость инфраструктуры и типы рабочих нагрузок (критически важные для бизнеса или нет, транзакционные или аналитические…).

Таким образом, в одном кластере может размещаться от одной большой базы данных до десятков более мелких. В этом контексте малые и большие определяются не только количеством данных, но и ожидаемой частотой запросов. По этой причине мы тщательно адаптируем каждый кластер, чтобы обеспечить их соответствующим образом. Когда база данных растет, а размер кластера больше не подходит, мы переносим базу данных в новый кластер.



Помимо производства, у нас есть еще одна среда меньшего размера, которая удовлетворяет две потребности. Это наша среда разработки. Мы используем его для тестирования наших резервных копий и для предоставления нашим разработчикам тестовой среды. Мы вернемся к этому вопросу через несколько строк.

Теперь поговорим о резервных копиях. Как я уже упоминал ранее, резервное копирование является важной частью баз данных корпоративного уровня. Чтобы избежать необходимости поддерживать разные процессы для разных типов СУБД, мы разработали общий процесс резервного копирования, который мы применяем ко всем этим.

Это позволило нам автоматизировать его более эффективно и абстрагироваться от сложности, лежащей в основе различного программного обеспечения.

Как вы уже, наверное, догадались, резервное копирование выполняется узлом резервного копирования. Этот узел является частью кластера, и данные на нем синхронно реплицируются, но он не получает никаких запросов. Когда создается моментальный снимок, процесс СУБД останавливается, и моментальный снимок файловой системы делается и отправляется на сервер хранения вне кластера для архивирования и обеспечения отказоустойчивости. Мы используем ZFS для этой цели из-за его надежности и увеличения пропускной способности, что снижает затраты на хранение, связанные с архивированием моментальных снимков.

Но основная причина наличия отдельного узла резервного копирования заключается в следующем: резервное копирование никак не влияет на кластер. Действительно, резервное копирование полной базы данных может иметь очень заметное влияние на производство (блокировки, потребление ЦП и ОЗУ и т. Д.), А мы не хотим этого на производственных узлах.

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



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

На этом наш обзор внутренней инфраструктуры баз данных OVHcloud завершен, и теперь вы готовы к следующему посту, который будет посвящен репликации. Будьте на связи!

Веб-хостинг - Как работают наши базы данных?

В нашей серии статей о переносе инфраструктуры веб-хостинга из Парижа на Gravelines (первая из которых находится здесь ) мы не упомянули базы данных. Однако это очень важная часть, так как необходимо перенести 800 000 баз данных!

В этой новой серии публикаций мы расскажем вам о нашем опыте работы с базами данных веб-хостинга.

Как обрабатывать 800 тыс. Баз данных?

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

Однако миграция баз данных также сопряжена с проблемами. Мы опишем самые важные из них. Но сначала небольшое напоминание о наших разных архитектурах.

OVH предлагает два типа баз данных:
  • Общие базы данных (которые мы называем SharedSQL )
  • Частные базы данных (которые мы называем, как вы уже догадались, PrivateSQL)

Что такое SharedSQL?

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

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

Этот метод позволяет нам использовать до 2500 баз данных на одном сервере. Это намного дешевле, чем предлагать физический сервер каждому клиенту. Таким образом, мы можем предложить недорогой доступ к нескольким базам данных в рамках наших предложений хостинга.

Что такое PrivateSQL?

В отличие от SharedSQL, PrivateSQL — это базы данных с гарантированными ресурсами, включая оперативную память. В PrivateSQL пользователи также имеют гораздо больше прав и большие возможности настройки.

У каждого пользователя есть свой сервер? На самом деле, нет! Несколько лет назад мы использовали технологию Docker для контейнеризации наших баз данных, мы уже обсуждали это в этом посте: www.ovh.com/en/blog/docker-administration-databases-a-flying-ideas/. В PrivateSQL закрыто не только пространство базы данных, но и гарантировано выделенное службе ОЗУ. Это означает, что в любых обстоятельствах производительность остается неизменной.

Семь отличий!

При рассмотрении вопроса о миграции нам пришлось изучить разницу в архитектуре между Paris и Gravelines.

Что касается PrivateSQL, все было просто: нет никакой разницы, мы уже согласовали архитектуры, когда помещали их в контейнер. Перенести их было бы проще простого: нам просто нужно было переместить контейнер из точки А в точку Б.

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

Вот упрощенная схема PrivateSQL



Напротив, базы данных SharedSQL на самом деле не были согласованы. Когда мы настраивали центр обработки данных Gravelines в 2016 году, мы воспользовались гибкостью Docker, чтобы пересмотреть нашу технологию баз данных и, следовательно, адаптировать наше прежнее решение.

Небольшая сравнительная схема SharedSQL в Париже и Gravelines:

SharedSQL на Paris P19 и Gravelines:



С одной стороны (в Париже) у нас есть серверы с единой системой управления базами данных (MySQL), на которых размещено 2500 баз данных на одной машине.

С другой стороны (в Gravelines) мы добавили уровень: контейнеры Docker имеют одну и ту же систему баз данных (MySQL), на которой размещается до 250 баз данных. На каждой машине размещается по 10 контейнеров.

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

Вот что он дает:



Конечно, запуск базы данных в контейнере Docker казался безумной идеей. Но после многих лет производства эта технология упрощает повседневную жизнь в центре обработки данных Gravelines.

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

И вдруг у нас возникла еще одна проблема миграции.

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

Представляем DepC: платформу OVH для вычисления QoS

В OVH наша первая задача как поставщика облачных услуг — предоставлять продукты с высоким качеством обслуживания (QoS). Будь то выделенные серверы, облачные серверы или размещенные веб-сайты, наши клиенты ожидают от наших решений очень высокого качества. И это именно то, что наши команды стараются предложить вам ежедневно!

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

Итак, как мы можем количественно оценить это качество обслуживания? Как мы понимаем качество, обеспечиваемое каждым продуктом каждый день, с максимальной точностью?

Первым шагом было найти существующие инструменты, но мы быстро поняли, что ни одно решение не отвечает нашим требованиям. Основываясь на этом наблюдении, мы решили разработать собственное решение для вычисления QoS: DepC. Первоначально созданная для команды WebHosting, эта платформа быстро распространилась по OVH. Сейчас он используется внутри компании десятками команд.

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

Чтобы быть максимально прозрачными, мы также решили объяснить и обосновать наши методы расчета. Вот почему мы решили сделать DepC открытым. Вы можете найти его на Github.

Прежде чем погрузиться в код, давайте посмотрим, как работает DepC, а также как мы используем его для расчета качества наших продуктов.



Что такое QoS?

Прежде всего, важно определить точный характер того, что мы хотим вычислить. QoS описывает состояние системы. Это может быть услуга (например, поддержка клиентов, время ожидания в кассе…), эксплуатация продукта (например, жизненный цикл посудомоечной машины) или сложные системы (например, инфраструктура веб-сайта).

Это состояние здоровья очень субъективно и будет различаться для каждого случая, но, как правило, оно будет основываться на вероятности того, что пользователь получит пользу от услуги в хороших условиях. В нашем случае хорошие условия означают как доступность услуги (т.е. инфраструктура работает), так и ее общее состояние (т.е. инфраструктура реагирует правильно).

QoS выражается в процентах, начиная со 100%, когда обслуживание выполняется идеально, а затем постепенно уменьшается в случае сбоя. Этот процент должен быть связан с периодом: месяц, день, время и т. Д. Таким образом, сервис может иметь 99,995% QoS в текущий день, тогда как накануне оно было 100%.

Важны и другие концепции:

  • SLA (соглашение об уровне обслуживания) : не путать с QoS, это договор между заказчиком и поставщиком, указывающий на ожидаемое качество обслуживания. Этот контракт может включать штрафы, предоставленные заказчику в случае невыполнения целей.
  • SLO (цель уровня обслуживания) : это относится к цели, которую поставщик услуг хочет достичь с точки зрения QoS.
  • SLI (индикатор уровня обслуживания) : это мера (время отклика ping, код состояния HTTP, задержка сети ...), используемая для оценки качества обслуживания. SLI лежат в основе DepC, поскольку они позволяют нам преобразовывать необработанные данные в QoS.

Цели

DepC изначально был создан для команды WebHosting. 5 миллионов веб-сайтов, распределенных на более чем 14 000 серверов, инфраструктура, необходимая для работы веб-сайтов (описанная в этой статье ), а также постоянные изменения затрудняют расчет качества обслуживания в реальном времени для каждого из наших клиентов.. Кроме того, чтобы идентифицировать проблему в прошлом, нам также нужно было знать, как восстановить QoS, чтобы отразить состояние инфраструктуры в то время.

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

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

  • Команда WebHosting управляет миллионами веб-сайтов, поэтому масштабирование было бы очень трудным.
  • Мы не единственные гаранты правильного функционирования веб-сайтов. Это также зависит от клиента, который может (намеренно или нет) генерировать ошибки, которые будут интерпретированы как ложные срабатывания.
  • Даже если бы мы решили предыдущие трудности и можно было бы рассчитать QoS веб-сайтов, было бы невозможно определить первопричины в случае сбоя.

Нам нужно было найти другое решение…

График зависимостей

Основываясь на этом наблюдении, мы решили обойти проблему: если невозможно напрямую рассчитать QoS веб-сайтов наших клиентов, мы рассчитаем его косвенно, исходя из их зависимостей.

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

  1. Исходный код ваших веб-сайтов размещен на серверах хранения (называемых filerz ).
  2. Базы данных, используемые сайтом, также размещены на серверах баз данных .

Если один из этих серверов выйдет из строя, это неизбежно повлияет на доступность веб-сайта, что приведет к ухудшению качества обслуживания клиента.



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

Этот пример намеренно упрощен, поскольку зависимости наших клиентов, конечно, гораздо более многочисленны (веб-серверы, почтовые серверы, балансировщики нагрузки и т. Д.), Даже без учета всех мер безопасности, принятых для снижения этих рисков сбоя.

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

Однако создание дерева зависимостей (узлов и их отношений) не требует от нас знания Neo4j, потому что мы разработали демон, который позволяет нам преобразовывать сообщения JSON в узлы на графе. DepC предоставляет API, так что каждая команда может добавлять новые элементы в свое дерево зависимостей, не изучая Cypher.

Принцип такой:
  1. Пользователи DepC отправляют сообщение JSON в потоке данных Kafka . Это сообщение указывает, какие новые узлы должны быть созданы, а также их взаимосвязь (например, узел веб-сайта, подключенный к анодному фильтру ). Все узлы и отношения содержат временную информацию, которая помогает поддерживать изменения инфраструктуры с течением времени.
  2. DepC анализирует эти сообщения, а затем обновляет график зависимостей в реальном времени.



Поскольку DepC доступен на Github, документация по этой части доступна в этом руководстве.

Расчет QoS

Платформа DepC предлагает API для хранения и запроса дерева зависимостей. Это может показаться тривиальным, но постоянное наблюдение за инфраструктурой — уже сложная задача. Это настолько мощно, что некоторые команды используют только эту часть платформы, используя DepC как эквивалент своей CMDB (инвентаризация своего технического парка).

Но ценность DepC идет дальше. Большинство наших пользователей рассчитывают качество обслуживания своего узла, но DepC предлагает два метода для разных случаев:

  • Узел представляет собой элемент, контролируемый одним или несколькими датчиками.
  • Целевой узел не является контролируемым элементом

Контролируемые узлы

Контролируемый узел может быть, например, сервером, службой или частью сетевого оборудования. Его основная характеристика заключается в том, что зонд отправляет измерения в базу данных временных рядов.

Здесь мы находим концепцию SLI, которую мы видели выше: DepC анализирует необработанные данные, отправленные зондами, чтобы преобразовать их в QoS.

Принцип очень простой:

  • Пользователи объявляют индикаторы в DepC, определяя запрос на получение данных из базы данных временных рядов, а также порог, который подразумевает снижение QoS для этого узла.
  • DepC запускает этот запрос для всех узлов, выбранных пользователем, затем каждый результат анализируется, чтобы вычислить QoS, как только пороговое значение будет превышено. Затем мы получаем QoS данного узла. Обратите внимание, что этот процесс выполняется каждую ночь благодаря инструменту планирования задач Airflow .

Технически анализ временных рядов DepC — это просто преобразование отсортированного по времени списка значений в отсортированный по времени список логических значений.



В этом случае расчет очень прост: значение «истина» увеличит QoS, а значение «ложь» - снизит его. Например, из 100 точек, когда 95 баллов ниже порогового значения ( верно ), QoS будет 95% (DepC запускает этот расчет каждую ночь; количество точек данных на самом деле намного выше).
Обратите внимание, что для завершения этой части DepC в настоящее время поддерживает базы данных временных рядов OpenTSDB и Warp10. Скоро будут добавлены другие базы данных временных рядов (InfluxDB, Prometheus…).

Неконтролируемые узлы

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

Представьте себе, например, узел, представляющий «клиента», связанный с несколькими контролируемыми узлами типа «сервер». У нас нет данных для анализа по этому клиенту. С другой стороны, для «серверных» узлов мы можем рассчитать их QoS благодаря наблюдаемым узлам. Затем мы объединяем эти показатели QoS, чтобы получить значение «клиентского» узла.

Для этого DepC вычисляет QoS отслеживаемых узлов, получая таким образом список логических значений. Затем между этими разными списками (по зависимости) применяется логическая операция И, чтобы получить уникальный список логических значений. Этот список затем используется для расчета QoS нашего неконтролируемого узла.




Затем вычисление выполняется таким же образом, как и для контролируемых узлов, с учетом количества «истинных» случаев по отношению к общему количеству точек.

В этом примере мы использовали только логический оператор. Однако DepC предоставляет несколько типов логических операций для разных приложений:


  • И : все зависимости должны работать, чтобы услуга была предоставлена.
  • ИЛИ : для оказания услуги достаточно одной зависимости.
  • СООТНОШЕНИЕ (N) : необходимо, чтобы N% зависимостей работали для предоставления услуги.
  • ATLEAST (N) : независимо от количества зависимостей, услуга предоставляется, если функционируют как минимум N зависимостей.

Мы не будем слишком углубляться во внутреннее функционирование, которое позволяет нам рассчитывать QoS в больших масштабах. Но если вас это интересует, я приглашаю вас посмотреть конференцию, которую мы провели на FOSDEM 2019 в Python devroom. Видео и слайды доступны по этому адресу .

Вывод


DepC уже используется десятками команд в OVH. Выбранная архитектура позволяет нам предлагать визуализацию QoS через встроенный веб-интерфейс с самим DepC или депортировать дисплей в Grafana.


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



Наша дорожная карта на следующие несколько месяцев очень загружена: всегда рассчитывать больше QoS, рассчитывать QoS этих узлов в соответствии с данными других команд и отображать все это в простой и понятной форме для наших клиентов…

Наша цель — стать стандартным решением для расчета QoS в OVH. Инструмент находится в производстве несколько месяцев, и мы получаем тысячи новых узлов в день. В нашей базе данных сейчас более 10 миллионов, и это только начало.

И, конечно же, если вы хотите протестировать или развернуть DepC дома, не сомневайтесь. Это открытый исходный код, и мы всегда в вашем распоряжении, если у вас есть вопросы или идеи по улучшению!

Ссылки