26 November 2019

Про зеркала репозиториев Centos и выбор лучшего из них

Open sourceSystem administration*nix
В прошлом году мы организовали у себя в сети общедоступные зеркала для нескольких Linux дистрибутивов. Это не сложный процесс и для больших проектов, вроде Ubuntu, почти полностью автоматизированный. В других случаях необходимо тем или иным способом связаться с проектом, например, в списке рассылки и явно высказать своё желание.

yum repolist

Технически это rsync, обычно по расписанию. Кто-то для этого предоставляет готовый набор скриптов, как Fedora, а кто-то просто говорит что надо синхронизироваться вот с этого сервера и рекомендуемый набор параметров. Самый затратный ресурс это место, мы недавно добрались до 4 терабайт и это дорого в нашем случае для того что не генерирует никакой прибыли. Взамен мы получили локальную доступность используемых нами дистрибутивов, это позволило упростить первоначальную настройку серверов исключив из неё обязательный доступ к Интернет. А ещё конечно мы рады что приобщились к чем-то большому, даже если наше участие в этом не сильно заметно.

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

Будем говорить о пакетном менеджере yum с установленным по умолчанию плагином fastestmirror и нас будет интересовать только процесс выбора конкретного зеркала.

Список зеркал


Известно, что список репозиториев задаётся в файлах в каталоге /etc/yum.repos.d/ если не указано иное. Вот так выглядят настройки репозитория Base в файле /etc/yum.repos.d/Centos-Base.repo:

[base]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

Здесь видно две опции которые задают место откуда можно получать данные. Первая baseurl непосредственно указывает зеркало, никакого выбора тут не нужно. Вторая mirrorlist указывает ссылку по которой будет возвращён список из 10 зеркал из которых и будет делаться выбор, именно эта опция активна. Также мы видим несколько переменных внутри ссылки, все они в конечном итоге отражают конкретное место в дереве каталогов репозитория:

  • release – версия: 6,7,8,8-stream. Более старые версии выведены из действующей инфраструктуры, но снимки их репозиториев можно найти на http://vault.centos.org/
  • arch — архитектура, например i386 или x86_64. Для некоторых версий некоторые архитектуры будут отсылать к альтернативным репозиториям и другой системе зеркал, но при этом инфраструктура выбора зеркала остаётся общей. Для 7 версии единственно поддерживаемой базовыми зеркалами является x86_64, альтернативной i386. Для первой будет формироваться список в базовой структуре зеркал, для второй из альтернативной
  • repo – в нашем случае os, но может быть updates или другим, собственно тип репозитория отражает какие данные нам нужны. Для 8 версии аналогом os будет baseos
  • infra – могу осторожно предположить, что до сих пор не используется, как минимум я не смог найти его обработку при формировании списка зеркал. Он равен stock, но если опустить этот параметр никаких видимых изменений не последует
  • cc – код страны, для США и Канады ещё и код штата. Его нету в примере из файла выше, так как страна вычисляется при запросе по IP адресу запрашиваемого. Эту переменную можно использовать чтобы исключить ошибки геопозиционирования. Для России будет cc=ru

Для 7-й версии Centos получим вот такую строчку http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock по которой будет выдан список из 10 зеркал примерно такого вида:

http://<путь до зеркала 1>/7.xxx/os/x86_64/
http://<путь до зеркала 2>/7.xxx/os/x86_64/
...
http://<путь до зеркала 10>/7.xxx/os/x86_64/

Обратите внимание, что конкретный путь для каждого зеркала точно соответствует переменным в запросе. Указана полная версия вместо старшей, хотя можно использовать и её: http://<путь до зеркала>/7/os/x86_64/ тоже существует, реализовано с помощью символической ссылки на последнюю версию.

И всё это вместе соответствует иерархии каталогов непосредственно на сервере.
./centos
├── 6
│   ├── centosplus
│   │   ├── i386
... │   └── x86_64
    ...
│
├── 7
│   ├── atomic
│   │   └── x86_64
│   ├── centosplus
│   │   └── x86_64
│   ├── cr
│   │   └── x86_64
│   ├── dotnet
│   │   └── x86_64
│   ├── extras
│   │   └── x86_64
│   ├── opstools
│   │   └── x86_64
│   ├── os
│   │   └── x86_64
│   │       └── repodata
│   │           └── [repomd.xml]
│   ├── rt
│   │   └── x86_64
│   ├── updates
│   │   └── x86_64
│   ...
│
├── 7.0.1406
├── 7.1.1503
├── 7.2.1511
├── 7.3.1611
├── 7.4.1708
├── 7.5.1804
├── 7.6.1810
├── 7.7.1908
├── 8
├── 8-stream
├── 8.0.1905 
...

Реализация


Теперь к реализации, которую можно найти на https://github.com/CentOS/mirrorlists-code, нам интересно то как формируется список зеркал. Этим занимается перловый скрипт makemirrorlists-combined.pl. Основная его задача проверить живость зеркала путём сравнения хеша файла repodata/repomd.xml с эталонным для данной версии и архитектуры. Соответственно файл должен существовать для всех доступных комбинаций версий, типов репозиториев и архитектуры.

Проверка осуществляется по списку в следующем порядке (цитирую из кода):

# phase 1 -- this U.S. state or Canada province
# phase 2 -- this country
# phase 3 -- other nearby countries
# phase 4 -- mirrors from the same continent
# phase 5 -- a random mirror from each continent (for fallback only)
# note: phase 5 will be executed once for each continent (with "redo")
# phase 6 -- random mirrors from any continent (for fallback only)
# phase 7 -- centos servers from the same continent
# phase 8 -- centos servers from other continents (used also for fallback)

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

SELECT $columns FROM mirrors WHERE type IN ('Direct', 'Indirect') AND status = 'Active' AND cc = '$cc' AND $commonqueryparams $skipregion $altarch_where ORDER BY RAND();

Потом идёт проверка живости зеркал по этому списку путём сравнения хешей, как написано выше. Если набирается 10 живых, то на этом требуемая задача выполнена, работа завершена. Если не набирается то переходим к следующему шагу чтобы добрать общий список до 10 или пока не исчерпаем все варианты. Результат сохраняется в файле и отдаётся на откуп фронтенд части представленной Python скриптом ml.py, задача которого выбрать и отдать нужный файл, предварительно выяснив страну источника запроса по IP адресу. Также учитывается и тип протокола IPv4 или IPv6, для которых формируются разные списки.

Вернёмся к запросу в котором используется ORDER BY RAND(), означает ли это что список будет случайным? Да, насколько случайна само реализация в СУБД, но это касается только порядка внутри каждого шага. То есть, если для конкретной страны набирается больше 10 зеркал нужного типа репозитория и архитектуры (для России всего 17), то в итоге каждый раз мы будем получать перетасованный список из 10 разных зеркал в одной стране. Если их меньше, то вверху списка всегда будут перетасованные репозитории из одной страны, дальше перетасованные репозитории из ближайших стран и так далее по шагам. В итоге получаем не совсем случайный список состоящий из нескольких случайных частей. Это имеет значение когда рабочих зеркал внутри одной страны не так много, например IPv6 зеркал в России всего 7 и они всегда будут вверху списка:

http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock&cc=ru (запрос надо делать с IPv6 адреса)
http://mirror.corbina.net/pub/Linux/centos/7.7.1908/os/x86_64/
http://mirror.yandex.ru/centos/7.7.1908/os/x86_64/
http://mirrors.powernet.com.ru/centos/7.7.1908/os/x86_64/
http://mirror.reconn.ru/centos/7.7.1908/os/x86_64/
http://mirror.sale-dedic.com/centos/7.7.1908/os/x86_64/
http://ftp.nsc.ru/pub/centos/7.7.1908/os/x86_64/
http://dedic.sh/centos/7.7.1908/os/x86_64/

http://ftp.funet.fi/pub/mirrors/centos.org/7.7.1908/os/x86_64/
http://centos.mirror.far.fi/7.7.1908/os/x86_64/
http://mirrors.glesys.net/CentOS/7.7.1908/os/x86_64/

Ещё хуже дело обстоит с архитектурой i386 это альтернативная архитектура для Centos 7 и отдельная система зеркал. В России всего один такой сервер который поддерживает альтернативные архитектуры, он всегда будет на первом месте:

http://mirrorlist.centos.org/?release=7&arch=i386&repo=os&infra=stock&cc=ru
http://mirrors.powernet.com.ru/centos-altarch/7.7.1908/os/i386/

http://mirror.vpsnet.com/centos-altarch/7.7.1908/os/i386/
http://mirrors.huaweicloud.com/centos-altarch/7.7.1908/os/i386/
http://linux.darkpenguin.net/distros/CentOS-AltArch/7.7.1908/os/i386/
http://ftp.agdsn.de/pub/mirrors/centos-altarch/7.7.1908/os/i386/
http://mirror1.hs-esslingen.de/pub/Mirrors/centos-altarch/7.7.1908/os/i386/
http://mirror.infonline.de/centos-altarch/7.7.1908/os/i386/
http://ftp.rz.uni-frankfurt.de/pub/mirrors/centos-altarch/7.7.1908/os/i386/
http://ftp.yz.yamagata-u.ac.jp/pub/linux/centos-altarch/7.7.1908/os/i386/
http://mirrors.daticum.com/centos-altarch/7.7.1908/os/i386/

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

Сканирование репозиториев происходит постоянно и не учитывая механизмы кеширования результат обновляется примерно раз в 3 часа. Цитата из mirrorlist_crawler_deployment_notes.txt:
- A full scan of all repos and all versions without any cached data is going to take close to 3 hours, but typically altarch is <10min, C6 <25min, C7 <25min with all the repos enabled

На практике, я запустил получение вариантов списка зеркал в России для IPv4 и IPv6 раз в минуту для репозитория Base Centos 7 — http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock&cc=ru и вышло вот так за пару суток наблюдений:

mirrors list updates count

синим IPv4 список, красным IPv6. Изменение происходит не каждые 25 минут, но и не раз в 3 часа — всё умещается в интервале от получаса до двух. Картина с архитектурой i386 и альтернативными зеркалами кардинально другая — http://mirrorlist.centos.org/?release=7&arch=i386&repo=os&infra=stock&cc=ru около недели наблюдений:

altmirrors list updates count

Можно ожидать что каждые 15 минут у нас будет новый список, за 30 минут какие-то изменения в нём произойдут обязательно. Но! Помним что чем меньше активных зеркал тем менее случаен порядок и на первом месте сейчас в России всегда одно и то же зеркало.

Fastestmirror


Итого, список зеркал формируется в лучшем случае случайно, что наверное не плохо для балансировки нагрузки. В худшем мы можем ожидать что выбирая только первое зеркало из списка у нас будет отсутствовать выбор как таковой. В дополнение к этому территориальное деление по странам в случае большой страны, как наша, может сыграть злую шутку, поставив наверх списка зеркало из Петропавловск-Камчатского http://mirror.vilkam.ru для клиента из Сочи. Поэтому неплохо бы оценить доступность зеркал из этого списка. Этим занимается плагин fastestmirror. Суть которого сводится к простому действию:

time_before = time.time()
sock.connect((self.host, self.port))
result = time.time() - time_before

Никаких HTTP или ICMP, открываем сокет для узла, считаем сколько на это ушло времени, к результатам применяем sort(). На вход параметрами подаются все те 10 зеркал которые были получены на предыдущем шаге, в виде полного URL из которого используется только имя узла. Плагин легко заставить работать отдельно от yum, надо только закомментировать строчки 52 и 55:

# from yum.plugins import TYPE_CORE

requires_api_version = '2.5'
# plugin_type = (TYPE_CORE,)

и можно пользоваться.

$ ./fastestmirror.py http://mirror.sale-dedic.com/centos/7.7.1908/os/x86_64/ http://mirror.corbina.net/pub/Linux/centos/7.7.1908/os/x86_64/ http://mirrors.powernet.com.ru/centos/7.7.1908/os/x86_64/ http://ftp.nsc.ru/pub/centos/7.7.1908/os/x86_64/ http://mirror.reconn.ru/centos/7.7.1908/os/x86_64/ http://mirror.yandex.ru/centos/7.7.1908/os/x86_64/ http://dedic.sh/centos/7.7.1908/os/x86_64/ http://mirror.tversu.ru/centos/7.7.1908/os/x86_64/ http://mirror.awanti.com/centos/7.7.1908/os/x86_64/ http://mirror.linux-ia64.org/centos/7.7.1908/os/x86_64/repodata/repodm.xml http://mirrors.datahouse.ru/centos/7.7.1908/os/x86_64/ http://mirror.docker.ru/centos/7.7.1908/os/x86_64/ http://mirror.logol.ru/centos/7.7.1908/os/x86_64/ http://centos-mirror.rbc.ru/centos/7.7.1908/os/x86_64/ http://mirror.truenetwork.ru/centos/7.7.1908/os/x86_64/ http://mirror.vilkam.ru/centos/7.7.1908/os/x86_64/ http://mirror.axelname.ru/centos/7.7.1908/os/x86_64/
 * mirror.corbina.net : 0.085000 secs
 * mirrors.powernet.com.ru : 0.097000 secs
 * mirror.sale-dedic.com : 0.117000 secs
 * ftp.nsc.ru : 0.181000 secs
 * mirror.reconn.ru : 0.184000 secs
 * mirror.yandex.ru : 0.222000 secs
 * dedic.sh : 0.261000 secs
 * mirror.tversu.ru : 0.295000 secs
 * mirror.awanti.com : 0.345000 secs
 * mirror.linux-ia64.org : 0.386000 secs
 * mirrors.datahouse.ru : 0.403000 secs
 * mirror.docker.ru : 0.435000 secs
 * mirror.logol.ru : 0.474000 secs
 * centos-mirror.rbc.ru : 0.519000 secs
 * mirror.truenetwork.ru : 0.587000 secs
 * mirror.axelname.ru : 0.528000 secs
 * mirror.vilkam.ru : 0.709000 secs
Result: ['http://mirror.corbina.net/pub/Linux/centos/7.7.1908/os/x86_64/', 'http://mirrors.powernet.com.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.sale-dedic.com/centos/7.7.1908/os/x86_64/', 'http://ftp.nsc.ru/pub/centos/7.7.1908/os/x86_64/', 'http://mirror.reconn.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.yandex.ru/centos/7.7.1908/os/x86_64/', 'http://dedic.sh/centos/7.7.1908/os/x86_64/', 'http://mirror.tversu.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.awanti.com/centos/7.7.1908/os/x86_64/', 'http://mirror.linux-ia64.org/centos/7.7.1908/os/x86_64/', 'http://mirrors.datahouse.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.docker.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.logol.ru/centos/7.7.1908/os/x86_64/', 'http://centos-mirror.rbc.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.axelname.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.truenetwork.ru/centos/7.7.1908/os/x86_64/', 'http://mirror.vilkam.ru/centos/7.7.1908/os/x86_64/']

Обработка происходит в несколько потоков поэтому ждать долго не приходится, даже для всех 17 зеркал которые числятся в России. Результат работы в виде двух списков, первый в котором указано время выполнения является отладочным и он не отсортирован, хотя может так показаться. Второй после слова Result отсортирован от меньшего отклика к большему, именно он используется для дальнейшей работы в полученном порядке.

В dnf тоже используется fastestmirror из набора библиотек RPM, но там это решено через libcurl и в целом всё сложнее устроено.

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

На практике получились следующие результаты первого места на недельном опросе через каждые 10 минут. Цифры — количество раз сколько узел выигрывал при сравнении, для fastestmirror и fping:
1422,1013 mirror.logol.ru
534,986 mirror.docker.ru
28,8 mirrors.datahouse.ru
16 centos-mirror.rbc.ru
6 mirror.sale-dedic.com
5,7 mirror.reconn.ru
2 dedic.sh
1 mirror.corbina.net

И IPv6 версия списка:
1989,1980 mirror.reconn.ru
18,34 mirror.sale-dedic.com
7 dedic.sh

Видно что выбор не прямо однозначный, но тут скорее всего играет роль близость точки с которой я делал опрос — до нескольких зеркал, разница меньше миллисекунды. Второй момент, IPv6 список отличается и он хуже — ближайший IPv6 узел дальше чем ближайший IPv4 и выбор меньше. С учётом приоритета IPv6 получается не очень. Результаты fping лаконичнее, меньше выигрывавших узлов, но в целом одинаковы с fastestmirror.

RIPE Atlas


С локальным выбором более менее всё понятно, теперь посмотрим как обстоят дела в рамках всей страны. Для чего воспользуемся услугами https://atlas.ripe.net/ — набор зондов (пробников) и инструментария для работы с ними под эгидой RIPE NCC, сразу предупрежу, это не самый быстрый и поворотливый сайт. Получить пробник может любой желающий, так же как и воспользоваться этой сетью. Но для запуска тестов нужна внутренняя валюта, которая копится с тех устройств которые за вами числятся и активны. Я выбрал все устройства в России которые поддерживают одновременно IPv6 и IPv4, таких получилось 82. По результатам измерений часть пришлось отсеять, поэтому осталось 67 из более чем 400.

Вариантов тестов которые можно запустить несколько, конечно там нет такого какой используется в fastestmirror, но есть обычный ping. Для всех зеркал, за исключением mirror.linux-ia64.org для которого ICMP зафильтрован и mirror.axelname.ru которое появилось после того как были сделаны тесты, со всех выбранных устройств, отдельно для IPv6 и IPv4, каждые 3 часа в течение недели выполнялось по 10 запросов. Среднее время ответа записывалось. Из всего набора измерений, около 50 по каждому зеркалу, бралась медиана и сравнивалась с остальными зеркалами на этом пробнике. Зеркало с наименьшим временем ответа выигрывало.

mirrors and probes map

Пробники (синие маркеры) и зеркала (красные маркеры) распределены слишком неравномерно и тяготеют к столицам, поэтому результаты не стоит воспринимать как что-то значимое. Сырые данные доступны начиная с измерения номер 23159879 по 23159901, при желании их можно проанализировать более строго. Обобщённый итог количество первых мест для IPv4:
22 mirror.docker.ru
12 mirror.awanti.com
10 centos-mirror.rbc.ru
6 dedic.sh
4 mirror.truenetwork.ru
3 mirror.corbina.net
3 mirrors.powernet.com.ru
2 ftp.nsc.ru
2 mirrors.datahouse.ru
1 mirror.reconn.ru
1 mirror.vilkam.ru
1 mirror.yandex.ru

и IPv6:
20 dedic.sh
20 mirror.reconn.ru
10 mirror.yandex.ru
8 mirror.sale-dedic.com
5 ftp.nsc.ru
3 mirror.corbina.net
1 mirrors.powernet.com.ru

Можно сказать что почти для всех зеркал, напомню их 17, есть свои потребители. Интересно, что зеркало mirror.yandex.ru на последних местах вместе с mirror.vilkam.ru из Петропавловск-Камчатского. При этом Yandex доступен хорошо почти отовсюду, просто каждый раз чуть-чуть проигрывает более отзывчивому, а вот mirror.vilkam.ru выигрывает один раз, зато более чем честно в сравнении с другими на пробнике 6606 из Хабаровска (RTT в миллисекундах):
centos-mirror.rbc.ru,105.4163369
dedic.sh,109.2160474
ftp.nsc.ru,106.4012836
mirror.awanti.com,107.0802782
mirror.corbina.net,98.5339837
mirror.docker.ru,102.7883347
mirror.logol.ru,105.9588192
mirror.reconn.ru,106.5717624
mirror.sale-dedic.com,106.1676841
mirror.truenetwork.ru,110.9780753
mirror.tversu.ru,107.9966083
mirror.vilkam.ru,25.1486164
mirror.yandex.ru,99.7320257
mirrors.datahouse.ru,103.6546383
mirrors.powernet.com.ru,116.4087614

Лучшие результаты в районе одной или нескольких миллисекунд, а самый худший результат по отклику в Салехарде, пробник 22767:
centos-mirror.rbc.ru,59.9632155
dedic.sh,63.765421
ftp.nsc.ru,59.349309
mirror.awanti.com,75.8998928571
mirror.corbina.net,59.906047
mirror.docker.ru,65.8720585
mirror.logol.ru,63.2041255
mirror.reconn.ru,63.7120505
mirror.sale-dedic.com,63.052342
mirror.truenetwork.ru,61.2567465
mirror.tversu.ru,66.2593372222
mirror.vilkam.ru,138.3730595
mirror.yandex.ru,63.4150445
mirrors.datahouse.ru,59.304435
mirrors.powernet.com.ru,78.7411795

Послесловие


Для тех кто добрался до этого места отвечу на вопрос который будет обязательно задан — наше зеркало http://mirrors.powernet.com.ru и вот как его используют:

mirrors.powernet.com.ru data traffic

mirrors.powernet.com.ru bandwidth traffic

При этом доля этого трафика изнутри нашей сети ничтожна.

Вот так на нём заканчивается место:

mirrors.powernet.com.ru disk

Хорошо различимые ступеньки — моменты когда мы добавляем новый дистрибутив. Когда всё начиналось это был VirtualBox работающий под Windows на офисном компьютере на котором хранились видеоархивы нашего рекламного отдела. Потом мы перебрались на боевую систему виртуализации и стало чуть полегче:

image

Для сравнения у mirror.truenetwork.ru отдача в 3 Гбит/c трафика с двух серверов. У нас всё гораздо скромнее и в общем нам более чем достаточно получать удовольствие от процесса, а также редкие удивлённо-восторженные отзывы от друзей и абонентов, когда кто-то замечает внезапно знакомые строчки во время обновления своей системы. А ещё, иногда мелькать на заднем плане в различных статьях и руководствах, где в логах или на снимках экрана видны ссылки на наше зеркало.

Посмотреть статус всех известных в Centos зеркал можно вот тут, а прочитать про то как присоединиться к проекту вот тут, это и просто и ответственно одновременно, но точно не бесполезно.
Tags:centoscentos 7зеркалорепозиторийобновлениеyumпакетный менеджерatlas ripe
Hubs: Open source System administration *nix
+8
7.3k 30
Comments 4
Popular right now