Поучительная часть: Никогда не недооценивай зависимости зависимостей.
Вступление
Рядовой апгрейд в лаборатории с Openstack Mitaka до Openstack Newton (более новая версия). Несколько deprecated options в файлах конфигурации, keystone переехал с eventlet на WSGI и поломал существующую конфигурацию с haproxy; из-за типового «ipv6 listen» apache не стал конфликтовать с haproxy за одинаковые используемые порты на звезде (один слушал ipv6, другой ipv4 only), так что запросы уходили в haproxy вместо апача, где умирали с 503, т.к. апстрима не было… Впрочем, история не об этом.
После того, как основные проблемы были пофишкены, Nova (одна из компонент Openstack) при запуске начала падать с ошибкой:
ConfigFileValueError: Value for option url is not valid: invalid URI: 'http://neutron-server.example.com:21345'.
. Это было очень странно. С учётом, что в конфиге поменялось 100500 опций, возникло подозрение, что мы используем устаревшую опцию, которую больше не надо использовать. Однако, документация говорила, что пример опции — url = http://controller:9696
.Отладка
Очевидные шаги отладки:
- Закомментировать опцию — не падает
- Повторить опцию из примера — не падает
- Заменить в опции порт на «наш» — возможно, нельзя использовать слишком большой номер порта — не падает
- Заменить в опции url на наш — падает
- Вернуть «controller» на место — не падает
- Подозрение: не умеет fqdn: заменить controller на controller.dns — не падает
- Подозрение: слишком много точек (у нас в реальном коде было 8 точек в url) — controller.dns1.dns2.dns3.dns4 — не падает
- Оставить из нашего имени только первую часть:
http://neutron-server:9696
— падает! гипотеза уже понятна. - Проверка1:
http://neutronserver:9696
— не падает - Проверка2:
http://with-dashes:9696
— падает!
Итого, бага: наличие дефиса в hostname вызывает ConfigFileValueError. Баг репортится: bugs.launchpad.net/ubuntu/+source/nova/+bug/1653967
Проверка, что это баг: RFC3986 утвержает, что:
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
reg-name = *( unreserved / pct-encoded / sub-delims )
host = IP-literal / IPv4address / reg-name
(это такая BNF-нотация, которая говорит, что в host может использоваться дефис).
Мы это все и так знаем, но всегда лучше перепроверить.
Изучение кода, который сообщает об ошибке:
try:
return convert(opt._get_from_namespace(namespace, group_name))
except KeyError: # nosec: Valid control flow instruction
pass
except ValueError as ve:
raise ConfigFileValueError(
"Value for option %s is not valid: %s"
% (opt.name, str(ve)))
Ошибка возникала на двух опциях: url и novncproxy_base_url. Ошибка идентичная, хотя grep'ать удобнее по второму. Начинаем искать второе. Вот как она определяется в коде:
cfg.URIOpt(
'novncproxy_base_url',
default='http://127.0.0.1:6080/vnc_auto.html',
deprecated_group='DEFAULT',
help="""
Ага. А cfg — это из
from oslo_config import cfg
. oslo.config — это библиотека Openstack для работы с конфигами. Смотрим сырцы.Видим:
class URI(ConfigType):
...
def __call__(self, value):
if not rfc3986.is_valid_uri(value, require_scheme=True,
require_authority=True):
raise ValueError('invalid URI: %r' % value)
Внезапно:
>>> import rfc3986
>>> rfc3986.is_valid_uri('http://test.com')
True
>>> rfc3986.is_valid_uri('http://test-test.com')
False
Упс. Непорядок. Зато есть: github.com/sigmavirus24/rfc3986/issues/11
Бага давно пофикшена. В версии 0.2.2. А на хосте у нас:
apt-cache policy python-rfc3986
python-rfc3986:
Installed: 0.2.0-2
Candidate: 0.2.0-2
Version table:
*** 0.2.0-2 500
500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages
100 /var/lib/dpkg/status
Зато в более свежей версии в zesty есть версия 0.3.1-2, которая такой проблемой не страдает.
Дальнейшее разбирательство
Давным-давно был сделан Баг. Он был некоторое время, потом его пофиксили. Но за это время запакетировали Код, в котором был Баг, и никто не обратил внимание на Фикс Бага, и версия с Багом оставалась в deb-репозитории годами. Она никого не волновала — пока не случились два коммита в oslo.config и nova:
commit 45ee2bed52a57b9801435b43ad45d8f50204580d
Author: Masaki Matsushita <glass.saga@gmail.com>
Date: Mon Sep 28 20:28:28 2015 +0900
Add URIOpt
This change add URIOpt which validates string as URI.
Closes-Bug: #1500398
Change-Id: Ie8736b8654b9feb2a2b174159f08dbea03568d84
commit 6091de77eda12286786e28ae4f0779e7efc54634
Author: Maciej Szankin <maciej.szankin@intel.com>
Date: Thu Jul 28 10:30:59 2016 -0500
Improve consistency in VNC opts
* Updated header flags
* Moved all vars to list
* Removed possible values and related options sections where they were not
needed
* Changed IntOpt to PortOpt where needed
Change-Id: I3255a867091f8e14c907c7fde9a2aa3abc249ae9
Implements: Blueprint centralize-config-options-newton
Этот коммит сделал из StrOpt UriOpt и начал использовать (через oslo.conf) python-rfc3986. Из-за того, что была запакетирована старая версия python-rfc3986, в программном ПО возникла неожиданная регрессия.
Бонусное: как мы это будем фиксить
Обычно в таких случаях, если апгрейд на более новую версию даётся легко (и не вызывает других проблем), то мы просто забираем пакет из более новой версии дистрибутива (в данном случае — ещё не вышедшей zesty, ака ubuntu-17.04). Мы положим его в наш приватный репозиторий под управлением aptly (как есть) и будем его использовать при установке/настройки сервером. Если бы такого пакета не было в природе — мы бы настроили на CI джобу на его пакетирование и публикацию (в репозиторий aptly). Если бы этот вариант не был доступен (например, несовместимые изменения), то мы бы добавили в наш patchqueue для nova ещё один патч, который бы делал StrOpt вместо UriOpt. Это подразумевает, что мы будем пересобирать nova из ubuntu-пакета с нашими собственными патчами. Этим занимается CI, который публикует пакеты в тот самый наш приватный репозиторий.
Немного флейма
И как бы эта проблема решалась в проприетарной среде? Ошибки допускают все (иначе бы у нас было ПО без багов). После того, как ошибка была зарепорчена в поддержку первого уровня, после боданий про установленные версии, обновления и контракты, оно дошло бы до саппорта второго уровня, третьего уровня, и так вплоть до человека с реальной квалификацией, который может смотреть в код. Он нашёл и исправил проблему. Какой estimate для того баг-фикса? Два часа на первый уровень, ещё час на второй, business day на исследование проблемы, ещё один business day на фикс, возможно ещё один день на релиз и тестирование. Это идеальный сценарий. На практике, мои самые оптимистичные оценки говорят про недели, превращающиеся в «следующем релизе через пол-года поправили».
Сколько это заняло у меня, в opensource проекте, исправить проблему своими силами? ~14:30, сегодня проблема обнаружилась, и я зарепортил его на launchpad. В 15:20 уже было известно про проблему с зависимостью, в 15:30 было проверено, что с новой версией python-rfc3986 этой проблемы нет. В 16:50 (по кипрскому времени) я заканчиваю писать этот пост на Хабр.
Комментарии (26)
sshikov
04.01.2017 20:02+4>Поучительная часть: Никогда недооценивай зависимости зависимостей.
Что, простите? :)amarao
04.01.2017 20:17Было странно, если при зависимости зависимости я не отказался бы от одинарного отрицания.
a1111exe
05.01.2017 10:07+2До этого комментария даже не сомневался, что речь об опечатке.
Попробуйте заменить слово "недооценивай" на другое. Например:
"никогда оценивай зависимости зависимостей". Звучит по-русски?
Лично я не смог в ресурсах по правилам русского языка сходу найти примеры, когда "никогда" с глаголом используется без отрицания. Противоположных примеров тьма.
Примерный синоним слова "недооценивать" — преуменьшать значение. Тогда: "никогда преуменьшай значение зависимости зависимостей". Если целью было сказать, что преуменьшать значение зависимости зависимостей — это не есть хорошо, то нужно было использовать отрицание: "никогда не недооценивай зависимости зависимостей".
P.S. Спасибо за статью.
Starche
05.01.2017 10:21+3Правильно будет «никогда не недооценивай». Т.к. конструкция «никогда не» как раз таки и просит нас перестать что-то делать (или вовсе не начинать). И получится как раз почти эквивалент «перестань недооценивать», чего вы вроде и хотели добиться.
Ну и такое количество «не» ещё больше навернет фразу, к чему вы и стремились.
AVX
04.01.2017 20:14Надо читать: всегда учитывай зависимости.
amarao
04.01.2017 20:20+4Вы потеряли двойные зависимости. Это хуже, чем кажется, потому что обычно прямые зависимости контролируются худо-бедно, а вот второй-третий уровень — нет. Точнее, они контролируются, но в рамках видения мира того, для кого это зависимости первого порядка. И видение мира одного постепенно начинает отклоняться от видения мира другого, и чем больше слоёв зависимости, тем более различается цвет забора (если понимаете о чём я).
AVX
04.01.2017 21:31Никогда не — Так пишется правильно. Не важно, какие и сколько там отрицаний. Либо сформулировать как-то иначе.
Marsikus
04.01.2017 21:01А патчик вы будете выкладывать? Пока что к багу не приложено ни одной ссылки на геррит.
Интересно, сколько патч провисит потом на ревью.amarao
05.01.2017 01:03+2Патчик к чему? К python-rfc3986? Вот он, два года как зарелизен: https://github.com/sigmavirus24/rfc3986/pull/12/files
Проблема-то была не в коде, а в том?что зависимость слишком старая в дистрибутиве. Решается установкой правильной версии пакета python-rfc3986 (>=0.2.2), о чём в комментарии к ланчпадовому багу я и написал.Marsikus
05.01.2017 19:15+1Тогда понятно. А то смотрю ветку newton в опенстековском и убунтовском репозиториях Новы, а там везде уже свежая версия прописана в requirements.
amarao
05.01.2017 19:35О, кстати, да. Сейчас посмотрел — зависимость-то прописана. Видимо, когда бубунтоводы её пакетировали, они снасильничали зависимости. Сейчас проверю.
Вообще, картинка потрясающая.
В ubuntu apt-get source выдаёт в requirements.txt rfc3986>=0.2.0
А в oslo.config stable/newton написано rfc3986>=0.2.2
И это не вина бубунты.
На https://releases.openstack.org/newton/index.html#oslo-config в newton идёт версия 3.17, (и там 0.2.0), а в git'е в бранче stable/newton что-то другое. И я не вижу ни тегов ни каких-то других методов понять, какая версия какому коммиту соответствует.
Халтурка со стороны авторов oslo.config, да. Более того, в git'е даже нет слова 3.17 нигде — ни в коде, ни в тегах, ни в git log'ах.Marsikus
05.01.2017 22:48В oslo.config обновили requirements из global requirements до релиза Newton.
https://github.com/openstack/oslo.config/commit/2f2c1839b7423185a6a48e7b3ca3c3274d5ba8f3
Также наличие этих изменений соответствует тегу 3.18.0 в oslo.config, а в 3.17.0 действительно их еще нет.
Возможно, что ошибся мейнтейнер Openstack релиза Newton, приняв в него старые версии oslo.config и упустив следующую актуальную.amarao
05.01.2017 23:22У меня в связи с этим даже более серьёзный вопрос: а как я могу узнать какой коммит какой версии соответствует? Именно в oslo.config, где нет тегов в гите.
Marsikus
05.01.2017 23:29Есть теги: https://github.com/openstack/oslo.config/tags
И можно сравнить код под тегом и ветку, например:
https://github.com/openstack/oslo.config/compare/3.18.0...master
Marsikus
05.01.2017 23:36Вот теперь и у меня вопрос. Можно увидеть нужные изменения и в 3.18.0 и в stable/newton отдельно.
А если посмотреть сравнение 3.18.0 против stable/newton, то получается, что нужные изменения появляются только в stable/newton.amarao
05.01.2017 23:39окей, насчёт тэгов я был неправ: они есть (не знаю что за глюк, когда смотрел, вроде бы не было). Но сейчас вижу.
А вот разницу между релизом и его бранчем — вижу. Ща допишу в багрепорт в ланчпаде.
molnij
05.01.2017 21:36В проприетарной среде — сделать алиас без дефиса, отправить ошибку, [записать в wiki], забыть.
lovecraft
09.01.2017 07:10Подскажите, по каким причинам ваш выбор остановился именно на Ubuntu, а не на, скажем, Debian?
Впрочем, в Debian та же петрушка — в Jessie 0.2.0-2, в не вышедшем еще Stretch — 0.3.1-2
baldr
Ну, допустим, в OpenSource-среде это тоже не всегда быстро чинится.
Можно найти кучу багов, которые висят десятилетиями, с кучей жалоб в комментах, 100% reproducibility, даже с пулл-реквестами, но без попыток починить/вкоммитить это в mainline. Или даже с попытками reject по идеологическим причинам.
Растут форки и форки форков, которые начинают использовать, а потом их перестают поддерживать.
Простите, не буду гуглить и приводить примеры, но, думаю, многие приведут примеры с наболевшим.
В проприетарной среде — да, почти без исключений, это бы заняло от недели (в лучшем случае) до тех же десятилетий. И своя инициатива (fork/pull request) там уже не прокатит если нет знакомого высокого менеджера.
amarao
Смотрите: и в opensource, и в проприетарной среде баги могут мариноваться десятилетиями. У меня самого много таких «success story», когда баги были открыты ещё в 200х году, и до сих пор актуальны. Более того, можно утверждать, что в проприетарной среде в условиях финансовой заинтересованности они могут фикситься быстрее (могут — не значит, что будут).
Я про другое. Я про то, что opensource позволяет исправить проблему не ожидая реакции апстрима/реселлера/саппорта.
Собственно, баг из поста на launchpad'е хорошо показывает: его ещё никто даже не confirm. И зная ubuntu, он таким и будет. Возможно, его никогда и не закроют, потому что через 3 месяца выйдет новый openstack и эту версию пометят obsolete.
Но мы его для себя исправили. Именно тут, а не в волшебном (оно быстро исправится) и находится сила opensource'а. Можешь пойти и сделать сам. Если не можешь — разницы между проприетарным и opensource'ным нет.
lolikandr
Очень верно подмечено: если можешь исправить.
Как-то нашел очень похожий источник бага — используется старая версия библиотеки, выпиливающей некорректные теги из Markdown.
Для исправления нужно «всего лишь» обновить библиотеку, но сделать это с уверенностью, что ничего не сломается — не могу.
Потому запостил и жду.
amarao
Невозможно быть уверенным, что изменение ничего не сломает. Это такая ловушка пассивного бездействия.
crazylh
Пока это питон поправить легко. А вот когда джава-скала-го то фикс уже не настолько тривиальный по времени и поддержке
amarao
Какая разница? Мы не редактируем код на продакшене, он всегда должен проходить через CI/тесты перед деплоем. А в процессе сборки пакета компилируется он или нет — это уже не важно. В openstack тесты выполняются почти час, так что это всё равно сильно дольше, чем просто «скомпилировать».
Тут вопрос в том, что нужно иметь рабочий delivery pipeline из гита в продакшен — и его чаще всего не так уж и просто сделать, особенно, если часть сборки выполняется вручную и авторы ничего такого про CI не думали.