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

В этой статье мы рассмотрим четыре механизма создания
бинарных патчей и поговорим о назначении каждого из них.
В первой части статьи я расскажу о Ksplice — технологии,
способной довести uptime сервера до 100 %, далее мы поговорим об
инструментах xdelta и bsdiff, с помощью которых можно прилично
сэкономить на трафике при обновлении системы, в третьей части
уделим внимание механизму deltup, экономия трафика при использовании которого может достигнуть 95 %. Четвертая часть посвящена
фреймворку binpatchng, предназначенному для создания пакетов с
патчами для базовой инсталляции OpenBSD в домашних условиях.

 

Ksplice

Наверное, о Ksplice и его недавней покупке компанией Oracle слышали все. Это такой хитрый механизм (или даже, лучше сказать, хак),
который позволяет накатывать обновления на ядро Linux на лету, не
требуя перезагрузки машины или даже какой-то работы по сборке
модулей ядра.


Принцип работы Ksplice

Механизм Ksplice использует довольно интересный метод патчинга ядра, в основе которого лежит техника изменения компонентов
ядра прямо во время его работы. Делается это в два шага. На первом
этапе отрабатывает утилита генерации бинарного патча, которая
получает на вход месторасположение исходных текстов Linux-ядра и
файл, содержащий стандартный патч на исходный код в diff-формате.
Наложив этот патч на исходники, утилита компилирует ядро, сравнивает получившийся образ с ядром предыдущей версии и генерирует
модуль ядра, содержащий измененные части, а если конкретнее — код
измененных функций. Этот патч-модуль передается на машину, ядро
которой должно быть обновлено. На втором этапе в дело вступает
модуль ядра ksplice.ko, который вместе с патч-модулем загружается
в ядро целевой машины. После загрузки ksplice.ko анализирует патч-модуль на предмет наличия новых версий функций ядра и изменяет
адреса настоящих функций ядра, так чтобы они указывали на функции, содержащиеся в патч-модуле. Таким образом удается изменить
ядро, не перекомпилируя его.

Минус такого подхода в том, что фактически он подходит только
для исправления небольших ошибок и латания дыр, более крупные
обновления, затрагивающие множество функций, добавляющие
новый функционал и изменяющие внутренние структуры ядра, таким
образом не сделаешь. Тем не менее автор Ksplice отмечает достаточно высокую эффективность системы, она смогла в полностью автоматическом режиме сгенерировать 84 % патчей для bugfix-обновлений
ядра, выпущенных за три года. Для остальных обновлений пришлось
немного поработать руками.

К сожалению, в 2009 г., после открытия компании Ksplice Inc.,
автор решил закрыть код системы, не позволив частным лицам и сторонним компаниям самим создавать Ksplice-патчи. Поэтому в этой
статье мы не будем обсуждать эту тему и рассмотрим вопрос только
с клиентской точки зрения. А выглядит она так: сегодня Ksplice
принадлежит Oracle, которая продолжает предоставлять сервис
абсолютно бесплатного онлайн-обновления ядра для дистрибутивов
Ubuntu и Fedora, а также для собственного варианта RHEL под названием Unbreakable Linux. Пользователи всех остальных корпоративных дистрибутивов пролетают.

Теперь о том, как этим пользоваться. Для Ubuntu и Fedora на сайте
ksplice.com есть пакет, его следует скачать и установить
стандартными средствами:

$ sudo apt-get install curl
$ sudo dpkg -i ksplice-uptrack.deb

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

 

xdelta, bsdiff

Итак, с Ksplice и его извращенным подходом к накладыванию патчей
разобрались, теперь настало время поговорить об утилитах, которые
позволяют применить бинарные патчи на отдельно взятые приложения. В UNIX существует как минимум три таких инструмента:
это старейшая разработка xdelta, выросшая из куска кода rsync,
отвечающего за инкрементальный бэкап, его более современная и
развитая версия под названием xdelta3 и bsdiff, разработанные для
использования в BSD-системах.

По своей сути все три инструмента представляют собой аналог
всем нам известной программы diff, который оперирует не строками,
а последовательностью байт. Это дает возможность использовать
утилиты не только для вычисления разницы между файлами с
исходным кодом, но и для любых других данных, будь то бинарник
приложения, tar.gz-архив или даже видеофайл. Если тебе интересно,
как это работает, то рекомендую ознакомиться со статьей "Дельта-кодирование", опубликованной
в Wikipedia, я же лучше расскажу о том, зачем всё это
нужно и как с этим работать.

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

Для того чтобы механизм инкрементальных апдейтов заработал,
должно быть выполнено три условия. Во-первых, пакетный менеджер дистрибутива должен поддерживать работу с утилитой инкрементального апдейта. Такая поддержка есть в pacman из ArchLinux
и через установку дополнительного пакета debdelta, в Debian.
Во-вторых, должен существовать специальный delta-репозиторий,
который будет отвечать за хранение патчей к пакетам и своевременно генерировать новые патчи для обновлений. Такие серверы обычно
держат энтузиасты, поэтому обычно их довольно трудно найти, а если
найдешь, никто не даст гарантии, что завтра сервер не исчезнет (это
главная беда бинарных патчей). И в-третьих, в кэше пакетного менеджера всегда должны лежать закэшированные во время предыдущей установки пакеты, на которые как раз и будут накладываться
патчи. Это важный момент, так как многие из нас (и я в том числе)
любят время от времени этот кэш подчищать (однако для debdelta, о
котором мы поговорим ниже, и они не нужны).

Теперь о том, как происходит настройка пакетного менеджера:

В ArchLinux всё решается довольно просто. Устанавливаем третью
версию xdelta:

$ sudo pacman -S xdelta3

Открываем /etc/pacman.conf, снимаем знак комментария со
строки UseDelta. Открываем список репозиториев /etc/pacman.d/mirrorlist и в самое его начало добавляем следующую строку:

Server = http://delta.archlinux.fr/$repo/os/$arch

Сохраняем файл и пробуем выполнить полное обновление дистрибутива:

$ sudo pacman -Syu

Разница должна быть, что называется, налицо. Проблема только в
том, что archlinux.fr содержит дельты далеко не для всех пакетов, но,
увы, альтернативы здесь просто нет.

В Debian это делается несколько иначе. Существует специальная
утилита (а точнее, набор утилит) под названием debdelta, которая
умеет разбирать deb-пакеты по косточкам, вычислять разницу между
файлами двух версий пакетов с помощью xdelta и генерировать
новый пакет на основании этих данных. При накатывании патчей это
чудо использует уже установленные в систему пакеты, а потому не
требует хранения старых версий пакетов в кэше apt-get. Проблема
только в том, что debdelta никак не интегрируется с apt-get, а поэтому
его надо вызывать вручную перед каждым обновлением системы:

$ sudo apt-get update
$ sudo debdelta-upgrade
$ sudo apt-get upgrade

Зато не нужно никакой настройки и возни с репозиториями
(официальный репозиторий успешно работает уже многие годы). Сам
debdelta устанавливается так:

$ sudo apt-get install debdelta

Стоит сказать о том, что BSD-аналог xdelta под названием
bsdiff также имеет большое применение. Он
был написан для утилиты обновления системы freebsd-update и стал
частью базовой установки FreeBSD в 2005 г. Каждый раз, когда ты делаешь "freebsd-update install", в дело вступает bsdiff (а точнее, его
часть bspatch), который прозрачно обновляет систему с помощью бинарных патчей. Благодаря BSD-лицензии, разрешающей включать
код в закрытые приложения, bsdiff получил большое распространение и за пределами BSD.

 

deltup

Интересную альтернативу бинарным патчам предложил в свое время
один из поклонников дистрибутива, Gentoo. Он создал утилиту
deltup, которая брала два архива с исходниками разных
версий приложения, распаковывала их, генерировала патч с помощью
стандартного diff, упаковывала его и снабжала информацией, нужной
для получения не отличимого от оригинала архива с одной версией
приложения из архива с другой. Говоря простым языком, deltup-файлом
можно пропатчить тарболл старой версии программы, чтобы получить
тарболл с ее новой версией, избежав необходимости в загрузке всего
тарболла. Результаты работы утилиты оказались просто поразительными: средний размер deltup-патча составляет всего 15 % от размера
оригинального архива, а зачастую и 5 % (с этими цифрами можно ознакомиться на
странице статистики головного deltup-сервера).

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

В Gentoo порядок действий следующий. Устанавливаем инструменты deltup и getdelta:

$ sudo emerge deltup getdelta

Добавляем в /etc/make.conf следующую строку:

$ sudo vi /etc/make.conf
FETCHCOMMAND="/usr/bin/getdelta.sh \"\${URI}\" -O
\"\${DISTDIR}/\${FILE}\""

Так мы сообщим emerge о том, что хотим использовать команду
getdelta для получения архивов с исходниками. Далее открываем
конфигурационный файл /etc/deltup/getdelta.rc и пишем туда следующее:

$ sudo vi /etc/deltup/getdelta.rc
# Адрес локального репозитория (если есть)
LOCAL_MIRROR=1.2.3.4
# Максимальная позиция в очереди на ожидание дельты
MAXIMUM_ACCEPTABLE_QUEUEPOS=10
# Удалять старые версии файлов
REMOVE_OLD=yes

Опцию LOCAL_MIRROR можно не добавлять, она нужна только в том
случае, если в локалке есть Gentoo-репозиторий, который можно использовать вместо запроса патча от deltup-сервера. Опция MAXIMUM_ACCEPTABLE_QUEUEPOS задает максимальную позицию в очереди на
создание дельты. Большинство deltup-серверов генерируют дельты
во время первого обращения клиента за архивом, поэтому очередь за
особо тяжеловесными приложениями и последними обновлениями
может выстроиться большая. Нет каких-то определенных рекомендаций по поводу размера очереди, так как нагрузка на сервер может
быть разной и время, которое ты можешь прождать, — тоже. Самостоятельно указывать какой-либо deltup-сервер не требуется, сегодня все
серверы подключены к linux01.gwdg.de, который вписан в getdelta по
умолчанию.
Это всё, при следующем обновлении пакета ты должен заметить
разницу во времени скачивания.

Порт deltup есть и для FreeBSD. Он не требует своего собственного
репозитория и может использовать deltup-сервера Gentoo (все-таки
исходники приложения для разных платформ одни и те же). Чтобы
научить систему портов FreeBSD использовать deltup для обновления софта, необходимо сделать следующее:

1. Установить deltup и wget из портов:

$ cd /usr/ports/sysutils/deltup
$ sudo make install clean
$ cd /usr/ports/ftp/wget
$ sudo make install clean

2. Добавить в файл /etc/make.conf следующую строку:

$ sudo vi /etc/make.conf
FETCH_CMD=/usr/local/bin/getdelta.sh

Теперь независимо от того, используешь ли ты систему портов напрямую или различные фронт-энды типа portupgrade, обновления будут происходить с помощью deltup-сервера. Однако необходимо быть
готовым к тому, что иногда deltup ошибается и собирает не совсем
точную копию архива, которая вполне нормально распаковывается,
но имеет неправильную контрольную сумму (это следствие микроразличий в разных версиях gzip и bzip2). В этом случае сборку пакета
можно осуществить, добавив предписание NO_CHECKSUM:

$ cd /usr/ports/games/cowsay
$ make NO_CHECKSUM install clean

 

Шпаргалка по бинарным патчам

$ bsdiff старый_файл новый_файл файл_патча
$ bspatch старый_файл новый_файл файл_патча
$ xdelta3 -e -s старый_файл новый_файл файл_патча
$ xdelta3 -d -s старый_файл файл_патча новый_файл
$ deltup -mjb 9 старый_файл новый_файл файл_патча
$ deltup -p файл_патча
$ debdelta старый_файл новый_файл файл_патча
$ debpatch -A файл_патча / новый_файл

 

OpenBSD Binpatch

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

Все, кто имеет хоть малейшее представление о процессе выпуска
релизов и заплаток для OpenBSD, знают, насколько он самобытен и
прост. При выпуске релизов разработчики скорее ориентируются на
план, чем на накопление достаточного количества важных изменений. После выпуска новой версии ОС сразу начинается работа над
следующей, а все ошибки и баги, найденные в это время, фиксятся с
помощью заплаток, которые каждый пользователь/админ должен
скачать, наложить на исходный код и пересобрать его. Делать это
не только жутко неудобно, но порой просто не представляется возможным. Многие железки под управлением OpenBSD не обладают
достаточной мощностью и дисковым пространством, для того чтобы
содержать в себе всё дерево исходных текстов OpenBSD, компилятор,
линковщик и промежуточные результаты компиляции. Но даже если
всё это на железке есть, но сама железка при этом не единственная
в подчинении, однотипные действия придется выполнять несколько
раз, что тоже не слишком интересно.

Чтобы решить эти проблемы,был придуман фреймворк
binpatch, позволяющий скачивать патчи, накладывать их на исходный код, собирать
пропатченное приложение или ядро и помещать его в архив в полуавтоматическом режиме. Позднее появилась модификация фреймворка под названием
binpatchng, которая
позволяла упаковывать пропатченные приложения не только в
архив, но и в пакет OpenBSD-формата, да так, что при удалении этого
пакета система откатывалась в первоначальное состояние.
Binpatchng полностью основан на Makefile’ах и концептуально
очень близок к системе портов. Чтобы создать новый патч, необходимо прописать в нужный Makefile пару простых правил, описывающих патч и способ его сборки, а затем выполнить команду make. Всё
остальное система возьмет на себя и вскоре сгенерирует готовый
к установке архив или пакет, который достаточно скопировать на
нужную машину и установить с помощью стандартных средств.

На
пальцах всё это выглядит следующим образом:

1. Скачиваем фреймворк и распаковываем его в каталог /usr
(на самом деле можно и в другое место):

$ cd /tmp; wget http://goo.gl/hvF7O
$ su
# tar -xzf /tmp/binpatchng-1.1.tar.gz -C /usr

2. Скачиваем архивы sys.tar.gz и src.tar.gz с официального FTP
и помещаем их в каталог distfiles, внутри binpatchng:

# cd /usr/binpatchng-1.1/
# mkdir distfi les
# cd distfi les
# wget ftp://ftp.openbsd.org/pub/OpenBSD/4.9/sys.tar.gz
# wget
ftp://ftp.openbsd.org/pub/OpenBSD/4.9/src.tar.gz

3. Скачиваем инсталляционные архивы для нужной архитектуры
(например ftp://ftp.openbsd.org/pub/OpenBSD/4.9/i386/) и помещаем
их в distfiles/имя_архитектуры.

4. Пишем Makefile. Для этого переходим на страницу
www.openbsd.org/errata.html, выбираем нужный релиз (для 4.9 пока патчей нет, поэтому я
возьму за пример 4.8), выбираем интересующий нас патч (например 001_bgpd.patch), открываем его. В первой строке будет указан способ сборки
патча. Наша задача — переложить его на язык Makefile. Это просто:
создаем файл /usr/binpatchng-1.1/Makefile и пишем в него следующее:

# vi /usr/binpatchng-1.1/Makefi le
# Для какой архитектуры собираем? (Можно не указывать,
# если совпадает с архитектурой текущей машины.)
ARCH=i386
# Здесь перечисляем патчи (просто откидываем расширение patch)
PATCH_COMMON=001_bgpd
# Здесь идут инструкции для сборки патча 001_bgpd.patch
001_bgpd:
cd ${WRKSRC}/usr.sbin/bgpd
(${_obj}; ${_depend}; ${_build})
# Далее можно поместить инструкции по сборке остальных патчей…


Правим binpatch Makefile

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

cd usr.sbin/bgpd
make obj
make depend
make
make install

Сравни их с директивами в Makefile, и всё поймешь. За дополнительными подробностями обращайся к примеру, описанному в файле
Makefile.sample. Теперь можно собрать пакет с нужным патчем:

# cd /usr/binpatchng-1.1/
# make PATCH="001" build
# make PATCH="001" plist
# make PATCH="001" package

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

# pkg_add binpatch-4.9-i386-001.tgz z

 

INFO

Debdelta имеет
опцию '--delta-algo',
с помощью которой
можно указать предпочитаемую утилиту для создания
патчей. Доступные
варианты: xdelta,
xdelta-bzip, xdelta3
и bsdiff.

Работу по написанию Makefile
для binpatchng
можно полностью
автоматизировать,
если воспользоваться скриптом,
опубликованным
в дискуссионном
листе OpenBSD
.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии