– А где Gentoo, он еще не пришел?
– Нет, он еще только собирается…
© Byron, linux.org.ru
Небольшое введение. Как должно быть известно большинству читателей, замечательная во всех отношениях система Gentoo имеет одно весьма утомительное качество: время на сборку пакетов из исходных кодов может быть достаточно велико, особенно на слабых машинах.
Для решения этой проблемы существует два известных мне вменяемых способа. Первый — это использование бинарных пакетов (которые предварительно нужно собрать на другой машине). Второй — это использование распределенной компиляции. Оба метода имеют свои преимущества и недостатки, и оба совершенно не препятствуют друг другу.
В этой статье, как можно догадаться из названия, рассматривается второй способ.
В общих чертах смысл распределенной компиляции можно изложить таким образом: части исходных текстов (а точнее, отдельные .c или .cpp файлы) посылаются с «клиента» по сети вместе с заголовочными файлами и компилируются на удаленной машине. Полученный объектный файл возвращается к «клиенту». В последствии все объектные файлы линкуются на машине «клиента». Число удаленных машин («серверов») в принципе ничем не ограничено, разве что бюджетом ;)
Исходя из описаной схемы несложно догадаться, что все учавствующие в компиляции машины должны по меньшей собирать исходники под одну и ту же архитектуру, и крайне желательно — иметь одинаковые версии компонент тулчейна (binutils, glib, gcc, linux-headers), по очевидным причинам.
В этой статье я хочу рассмотреть «сферичиских коней в ваккууме»: «клиент» и все «серверы» имеют одну и ту же архитектуру, и одинаковые версии тулчейна.
Тогда, собственно, настройка distcc оказывается достаточно простой. Для начала, на каждом учавствующем в компиляции хосте (то есть и на «клиенте» и на «серверах») нужно установить distcc
emerge -a distccЗатем, настроить portage на использование distcc
nano /etc/make.conf MAKEOPTS="-jN" FEATURES="distcc"
Здесь N — это число, рассчитываемое в общем случае следующим образом: (число процессорных ядер учавствующих хостов)*2-2
Таким образом, если имеется две двухядерных машины («клиент» и «сервер»), то N=6
Это не истина в последней инстанции и с другими значениями N результаты могут быть лучше.
Дальше нужно настроить «сервера». На каждом из них нужно отредактировать файл /etc/conf.d/distccd дабы разрешить «клиенту» или, если уж на то пошло, «клиентам» подключаться к серверу компиляции:
nano /etc/conf.d/distccd
DISTCCD_OPTS="${DISTCCD_OPTS} --allow 192.168.0.0/24"Ясно, что 192.168.0.0/24 нужно заменить на IP клиента или адрес Вашей подсети, или… в конфиге есть примеры, и эта операция не должна вызывать затруднений.
Далее, на каждом «сервере» нужно запустить distccd:
/etc/init.d/distccd start
По желанию так же можно запускать его вместе с системой:
rc-update add distccd
Наконец, последняя операция — настроить список «серверов» на «клиенте».
Проще всего это сделать, отредактировав файл /etc/distcc/hosts:
nano /etc/distcc/hosts localhost server1,lzo,cpp server2,lzo,cpp ...
Здесь localhost — ключевое слово, означающее локальную компиляцию. На очень медленных машинах при наличии намного более быстрых серверов, эту строчку лучше убрать.
server1, server2, … — имена или IP-адреса серверов.
lzo включает пакование исходников перед пересылкой
cpp включает препроцессинг на «серверах» (особо имеет смысл на слабых машинах), требует lzo
Собственно, с базовым случаем разобрались. Запускать emerge теперь, правда, придется через обертку:
pump emerge ...
Однако с этим можно и смириться.
Осталось только заметить, что понятия «клиент» и «сервер» не просто так взяты в кавычки. Дело в том, что совершенно ничто не мешает поднять на «клиенте» distccd, а «сервер» настроить и как «клиента».
На всякий случай приведу пример: имеются host1 и host2. На каждом запущен distccd, разрешающий подключения с другого. /etc/distcc/hosts на каждом имеет записи localhost и второго хоста. И на обоих portage настроен на distcc. В таком случае оба хоста при вызове emerge будут радостно использовать ресурсы соседа. Надеюсь, идея понятна.
Так же замечу, что совершенно ничто не препятствует использовать и банальный make с distcc. Для этого всего лишь надо изменить пути поиска таким образом, чтобы ссылки distcc находились раньше, чем gcc, но после ccache, если он установлен. Добиться этого можно, например, так:
export PATH="/usr/lib/ccache/bin:/usr/lib/distcc/bin:${PATH}"
Эту строку можно, например, добавить в .bashrc или куда-то в подобное место.
В следующей статье расскажу, как настроить распределенную кросс-компиляцию, то есть что делать, если один из хостов имеет архитектуру x86_64, а другой i386/i586/i686, или более экзотические варианты (как то комбинации ARM-SPARC-ia64).
Материалы по теме не по-русски:
Gentoo Distcc Documentation
man distcc
Материалы по теме по-русски мне как-то не очень понравились. Поэтому решил сделать свое :)
Связанные посты:

Никакие «заголовочные файлы» не передаются.
Отправляется только результат работы препроцессора, обратно приходит obj-файл.
Препроцессированный файл не требует уже никаких внешних h-файлов, поэтому требование об одинаковости linux-headers тоже лишнее. Они вообще могут отсутствовать на распределённых машинах.
Комментарий by qehgt — Сентябрь 12, 2009 @ 18:32
Утверждение верно лишь отчасти, точнее при использовании т.н. plain режима.
При использовании опции cpp и соответственно при запуске через pump инклюды разрешаются по сети и препроцессингом занимается «сервер». Безусловно, часть из них берется с «клиента». Системные берутся с «сервера».
man distcc в помощь:
Комментарий by Livid — Сентябрь 12, 2009 @ 18:45
мне вот интересно следующее – как канал связывающий сервер влияет на общую скорость. Точнее на сколько сильно? Допустим на расстоянии 1мбит у меня есть 2 свободных сервера каждый из них двухголовый четырехядерник с хорошим рейдом на САС и большим кэшем? Но не достаток что канал до них всего 1мбит. При тайкой сети нет смысла в этом?
Комментарий by TheMixa — Сентябрь 13, 2009 @ 05:47
Зависит от «клиентской» машины.
Если она достаточно медленная, то смысл есть. Если компиляция на клиенте заведомо быстрее, чем загрузка исходников по 1М, то смысла нет.
В качестве справочной информации, скажу, что при сборке gnutls-2.8.3 с pump и одним «сервером», «клиент» использует 8Mbit исходящего канала. При этом входящий канал используется скромнее, чем на 1Mbit. Канал гораздо шире, поэтому эти цифры можно использовать как своего рода реперную точку.
Вообще в такой конфигурации при медленном клиенте я бы предложил использовать бинарные пакеты. Если сервера связаны друг с другом хотя бы 8Mbit каналом, то это особенно эффективно: собирать пакеты на серверах (emerge -B …), а потом загружать уже готовые на «клиента» (emerge -g …)
Из недостатков, правда, приходится с сожалением отметить то, что на сервере должны быть установлены все зависимости для сборки бинарного пакета, что не всегда возможно/желательно.
Комментарий by Livid — Сентябрь 13, 2009 @ 12:40