Pull to refresh

Установка Perl-модулей в Gentoo

Reading time 8 min
Views 5.3K
imageСамый большой недостаток экосистемы языка Perl — управление модулями (другой кандидат на эту роль — долгострой Perl6, но не будем о нём). Что любопытно, самое большое достоинство этой же экосистемы — наличие единого архива модулей CPAN. Поразительно, собрать и организовать модули смогли, а реализовать удобную установку/обновление/удаление — нет.

Богатство выбора… или очередное TIMTOWTDI


imageСуществует множество альтернативных подходов к этой задаче (и их количество тоже косвенно указывает на то, что ни один из них не решает проблему достаточно хорошо): cpan, cpanplus, cpanminus, pip, cpansite, minicpan/mcpani, perlbrew, cpan-outdated, cpan-listchanges, local::lib, …

Итак, у нас может быть:
  • Несколько версий самого perl (разумеется, каждая со своими глобальными модулями), в т.ч. установленные в домашний каталог юзера (см. perlbrew).
  • Глобальные (доступные при запуске perl) и локальные (подключаемые из любого каталога/каталогов, обычно располагающиеся внутри отдельного проекта или в домашнем каталоге пользователя) модули.
  • Глобальные модули бывают трёх типов: core (идущие вместе с perl), site (устанавливаемые вручную админом) и vendor (устанавливаемые менеджером пакетов вашей ОС).
  • Все глобальные модули находятся в подкаталогах «номер.версии.perl/», и эти каталоги никто автоматически не чистит. А при установке новой версии perl создаются новые аналогичные каталоги. И perl подгружает модули из каталогов всех доступных предыдущих версий. Так что умножьте core+site+vendor на количество обновлений perl — вот в таком количестве каталогов/вариантов находятся ваши глобальные модули.
  • Источники модулей тоже бывают разные: CPAN, локальные зеркала-оверлеи CPAN с приватными модулями, просто свои или скачанные из инета модули отсутствующие в CPAN-совместимой системе.
И всю эту радость надо администрировать: устанавливать, обновлять, … В Gentoo для упрощения администрирования глобальных Perl-модулей есть утилитка g-cpan, вот о ней я и хочу немного рассказать.

Рабочая среда


Для начала немного конкретизируем, в каких условиях обычно приходится работать:
  1. Речь не идёт об установке своих модулей на хостинги, мы работаем с целым сервером/серверами.
  2. На этом сервере работает несколько наших Perl-проектов.
  3. Все кто активно пишут на Perl — пишут и свои модули. Причём не все свои модули можно и нужно выкладывать на CPAN. Все, кто работает над несколькими проектами — используют одни и те же свои модули в разных проектах. Поэтому эта ситуация рано или поздно приводит к необходимости поднять локальное зеркало/оверлей CPAN для своих модулей, чтобы иметь возможность устанавливать и обновлять их «штатными» средствами — это не решает проблему в целом, но жить становится легче. Так что необходима поддержка локального зеркала/оверлея CPAN.
  4. Всегда возникает вопрос: ставить необходимые проекту модули (и свои и CPAN) глобально (напоминаю, у нас свой сервер), или локально. Однозначного ответа здесь быть не может, есть доводы и «за» и «против» обоих подходов. Я для себя определился так: поскольку администрировать (устанавливать, обновлять, отслеживать устаревшие/дырявые версии, удалять) надо все установленные модули, и поскольку невозможно собрать все модули в одном месте (от глобальных модулей избавиться невозможно, т.к. они используются другими общесистемными приложениями, и совсем без локальных модулей обойтись тоже в большинстве проектов не получается), то нужно хотя бы минимизировать количество таких мест и общее количество установленных модулей. Что нас приводит к тому, что большинство модулей должно быть установлено глобально, а локально надо устанавливать модули только в том случае, если версии глобальных модулей не совместимы с конкретным проектом. Да, основной недостаток этого подхода в том, что обновление глобальных модулей может сломать некоторые проекты — но во-первых по моему опыту это происходит довольно редко, а во-вторых используйте тестовый сервер и проверяйте на нём все обновления перед тем, как устанавливать их на рабочие сервера — это ведь надо делать в любом случае, правда? :) В крайнем случае, на роль тестового сервера подойдёт рабочая станция главного разработчика, но минимальное тестирование обновлений необходимо.
Мы уже немного облегчили себе жизнь ограничившись установкой модулей из CPAN-совместимого локального зеркала/оверлея (см. cpansite) и устанавливая большинство модулей в единственном экземпляре глобально. Следующая проблема: глобальных мест установки модулей целых три — core, site и vendor. И из-за этого нередко возникают конфликты. Раньше в Gentoo приоритет был vendor-site-core. Из-за этого устанавливаемые вручную через cpan/cpanplus/etc. в site модули зачастую не были доступны, т.к. в vendor стояла устаревшая версия. И утилиты вроде cpan могли бесконечно выполнять обновление модулей, не понимая, что это ничего не даёт. Решалось это удалением всех модулей из vendor, и прописыванием их вручную в /etc/portage/profile/package.provided, чтобы портаж не установил их снова (после чего надо было внимательно следить во время обновлений системы, чтобы портаж не установил пакеты из dev-perl/* или perl-core/*, и при необходимости доустанавливать эти пакеты через cpan и прописывать в package.provided). Сейчас приоритет site-vendor-core, но это тоже не до конца решает проблему: некоторые (а может и все, я не проверял) core модули, когда обновляются утилитой вроде cpan, устанавливаются не в site, а в core, поверх тех версий, которые установились вместе с самим perl. Это создаёт целых два неожиданных эффекта: во-первых, если в vendor установлена другая версия этих core-модулей (из пакетов perl-core/*), то не смотря на «обновление» модуля утилитой cpan по-прежнему будет использоваться более старая версия из vendor; а во-вторых переустановка пакета dev-lang/perl (например, вызванная revdep-rebuild) откатывает все обновления core-модулей установленные вручную через cpan.

g-cpan


Учитывая вышеописанные проблемы, а так же неудобства «штатных» средств вроде cpan/cpanplus, мы плавно подходим к тому, чтобы было бы здорово все глобальные модули устанавливать, обновлять и удалять средствами портаж, как и все остальные пакеты в системе. Если мы при этом полностью откажемся от ручной установки модулей в site, то все проблемы приоритетов/перекрывания модулей/переустановки perl так же будут решены. К сожалению, в портаж есть пакеты только для небольшой части CPAN модулей, и уж конечно там нет пакетов для наших собственных приватных модулей. Для решения этой проблемы была разработана утилита g-cpan — она может генерировать пакеты для любых CPAN модулей (включая приватные модули из CPAN-совместимого зеркала/оверлея) и либо сохранять их в локальном оверлее портаж, либо просто устанавливать «на лету». Когда я несколько лет назад попытался ей воспользоваться, она работала не очень-то здорово, и была практически бесполезна. Но сейчас она уже вполне работоспособна — по крайней мере я сумел установить ~300 модулей, и только для одного из них мне пришлось вручную править ebuild.

В качестве побочного, но приятного эффекта от использования g-cpan мы получаем возможность делать на одном сервере стандартные Gentoo-шные бинарные пакеты для Perl модулей, и очень быстро устанавливать их на остальных серверах (обычная установка модулей, особенно с тестированием, занимает часы, причём некоторые модули ещё и требуют интерактивного взаимодействия при установке).

Установка и настройка
  1. Ставим:
    emerge g-cpan
  2. Нужно задать оверлей, где g-cpan будет создавать ebuild-ы:
    echo "GCPAN_OVERLAY=/usr/local/portage" >> /etc/make.conf
  3. Задаём свой список зеркал CPAN (включающий первым пунктом наше локальное зеркало/оверлей). Проще всего скопировать уже настроенный список из cpan:
    cpanmirrors=$(cpan -J | perl -0777 -ne 'eval;print"@{$CPAN::Config->{urllist}}"')
    echo "cpan    $cpanmirrors" >> /etc/portage/mirrors
  4. Надо ли тестировать модули при установке — каждый решает сам, но я предпочитаю тесты прогонять, особенно для своих модулей. Чтобы форсировать тестирование perl-модулей при установке, нужно добавить возможность задавать свои хуки на целые категории пакетов. Один из возможных подходов — создать каталог /etc/portage/bashrc.d/ и вот такой файлик /etc/portage/bashrc:
    # Useful variables and functions:
    #
    # EBUILD_PHASE=
    #    clean
    #    setup
    #    unpack
    #    compile
    #    test
    #    install
    #    preinst
    #    prerm
    #    postrm
    #    cleanrm
    #    postinst
    #    clean
    #
    # CATEGORY = app-admin
    # PF    = eselect-opengl-1.0.6-r1
    # P     = eselect-opengl-1.0.6
    # PN    = eselect-opengl
    # PVR   = 1.0.6-r1
    # PV    = 1.0.6
    # PR    = r1                        (will be "r0" for packages without -r)
    #
    # einfo ()
    # elog ()
    # ewarn ()
    # eerror ()
    
    if [ "x${EBUILD_PHASE}" != "x" ]; then
        if    [ -x "/etc/portage/bashrc.d/${CATEGORY}.${EBUILD_PHASE}" ]; then
            source "/etc/portage/bashrc.d/${CATEGORY}.${EBUILD_PHASE}"
        fi
        if    [ -x "/etc/portage/bashrc.d/${CATEGORY}/${PF}.${EBUILD_PHASE}" ]; then
            source "/etc/portage/bashrc.d/${CATEGORY}/${PF}.${EBUILD_PHASE}"
        elif  [ -x "/etc/portage/bashrc.d/${CATEGORY}/${P}.${EBUILD_PHASE}"  ]; then
            source "/etc/portage/bashrc.d/${CATEGORY}/${P}.${EBUILD_PHASE}"
        elif  [ -x "/etc/portage/bashrc.d/${CATEGORY}/${PN}.${EBUILD_PHASE}" ]; then
            source "/etc/portage/bashrc.d/${CATEGORY}/${PN}.${EBUILD_PHASE}"
        fi
    fi
    после чего можно включить тестирование всех пакетов в категориях perl-core/*, dev-perl/* и perl-gcpan/*:
    cat <<'EOF' >/etc/portage/bashrc.d/perl_module_test
    FEATURES="$FEATURES test"
    SRC_TEST="do"
    EOF
    cat <<'EOF' >/etc/portage/bashrc.d/perl_module_notest
    SRC_TEST=
    EOF
    chmod +x /etc/portage/bashrc.d/perl_module_*
    
    ln -s perl_module_test /etc/portage/bashrc.d/perl-core.test
    ln -s perl_module_test /etc/portage/bashrc.d/dev-perl.test
    ln -s perl_module_test /etc/portage/bashrc.d/perl-gcpan.test
    
    и, при необходимости, отключить тестирование некоторых модулей или как-то на него повлиять:
    mkdir /etc/portage/bashrc.d/dev-perl
    ln -s ../perl_module_notest /etc/portage/bashrc.d/dev-perl/Crypt-SSLeay.test
    ln -s ../perl_module_notest /etc/portage/bashrc.d/dev-perl/Shell-EnvImporter.test
    ln -s ../perl_module_notest /etc/portage/bashrc.d/dev-perl/Term-ReadLine-Perl.test
    
    cat <<'EOF' >/etc/portage/bashrc.d/dev-perl/Inline.test
    MAKEOPTS=-j1
    EOF
    chmod +x /etc/portage/bashrc.d/dev-perl/Inline.test
    

  5. Далее, обычно на CPAN самые последние версии модулей — самые стабильные. К нашим приватным модулям это тем более относится. Так что имеет смысл форсировать установку самых свежих версий:
    cat <<'EOF' >>/etc/portage/package.keywords
    perl-core/*
    dev-perl/*
    virtual/perl-*
    # avoid depending on ~x86 sys-libs/readline
    dev-perl/Term-ReadLine-Gnu -~x86
    # avoid depending on ~x86 sci-mathematics/pari
    dev-perl/math-pari -~x86
    EOF
    

  6. Теперь нам нужно обновить индекс CPAN для g-cpan. Теоретически для этого у g-cpan есть опция --cpan_reload, но практически в текущей версии (0.16.2) она не работает. А поскольку g-cpan использует индекс утилиты cpan, то можно обойти эту проблему запустив cpan и выполнив «cpan>reload index».
  7. Подготовьте список модулей, используемых всеми Perl-скриптами на сервере. Я обошёлся многоэтажными sh-конвейерами, но, наверное, вам стоит написать для этого Perl-скрипт, и выложить его в комментариях к этой статье. :)
  8. Генерируем для них ebuild-ы (при этом автоматически будут созданы ebuild-ы и для модулей-зависимостей):
    g-cpan -g Module::Name1 Module::Name2 …
    
    Есть небольшая вероятность, что для некоторых модулей вам придётся ручками подправить /usr/local/portage/perl-gcpan/Module-Name/Module-Name-version.ebuild. (Мне пока попался только один такой: Crypt::MatrixSSL.)
  9. Удаляем каталог /usr/lib/perl5/site_perl/*. ОСТОРОЖНО, в этот момент сломается большинство Perl-скриптов на этом сервере. К сожалению, если site модули не удалить перед установкой vendor модулей (у которых более низкий приоритет), то во время установки и тестирования vendor модулей могут возникнуть проблемы вызванные тем, что будут использоваться site версии модулей уже установленных в vendor. Впрочем, проблемы обычно решаемые, так что если вы не можете себе позволить на некоторое время «сломать систему», то сначала устанавливайте vendor модули, но не забудьте после этого удалить каталог с site модулями.
  10. Удаляем все perl-модули из /etc/portage/profile/package.provided.
  11. Ставим сначала модули нужные портаж (если использовался package.provided и они сейчас не установлены):
    emerge -uDNa @world
    
    а потом нужные вам (имеет смысл первыми ставить Test::* модули, потом Math::* — это ускорит установку остальных). Повторяем попытку установки несколько раз, т.к. не во всех модулях корректно прописаны зависимости:
    until emerge -uDN --keep-going y \
              Test-Deep Test-Differences Test-Distribution … \
              Math-BigInt-GMP math-pari … \
              … 
    do
        read -p "Press <Enter> to try again"
    done
    

Обновление

Полного аналога «cpan>upgrade» утилита g-cpan не предоставляет, запуск
g-cpan -u
обновит только модули, пакеты для которых создавал g-cpan (т.е. из категории perl-gcpan/*, а пакеты из perl-core/* и dev-perl/* не обновятся). Но может это и не так плохо — в конце концов для обновления этих модулей, как правило, достаточно скопировать и переименовать ebuild из perl-core/* или dev-perl/* в /usr/local/portage. Зато появляется возможность более тонкого контроля над установленными версиями модулей и удобного отката на предыдущие версии.

Резюме


На мой взгляд g-cpan действительно упрощает управление глобально установленными Perl-модулями. И не смотря на громадный раздел «Установка» из 11-ти пунктов, на самом деле ничего действительно сложного там нет (ну, кроме определения какие же модули вам нужно установить :)).

Успехов в наведении порядка на серверах!
Tags:
Hubs:
+30
Comments 28
Comments Comments 28

Articles