W pracy admina po jakimś czasie chyba każdy zbiera sobie kolekcję skryptów i programów które używa do debugowania błędów, czy do automatyzacji czynności. Od tooli typu nmap, socat czy tcpdump które są niesamowicie użyteczne ale nie są instalowane w domyślnej instalacji, po własne skrypty automatyzujące niektóre czynności, czy służące np. do wyciągania statów z monitoringu.

I o ile gdy się ma jeden serwer panowanie na tym nie stanowi wielkiego problemu (zainstalować to co potrzeba, a skrypty wrzucić do /usr/local/bin) to już przy dwóch zaczyna się “aaa mam nową wersję tego skryptu na innym serwerze” a przy większej ilości zwykle kończy się na instalowaniu tooli “kiedy są potrzebne” czyli za każdym razem tracimy czas, a w przypadku własnych skryptów kończy się to tym że na serwerach jest 6 różnych wersji tego samego skryptu.

Rozwiązanie jest proste, albo trzymać wszystkie skrypty na NFSie ( ale to raczej obejście problemu ), albo zapaczkować je i dodać repo z paczkami do /etc/apt/sources.list. Na szczęście samo tworzenie prostych paczek (bez rzeczy wymaganych  żeby paczka była zaakceptowana do “oficjalnego” repo Debiana/Ubuntu) jest dosyć proste. Ale po kolei.

Paczka debianowa “w środku” wygląda mniej-więcej tak:

xani@hydra:~/src/my/pkg-repo/packages/xani-tools (master)ᛯ find .
.
./DEBIAN
./DEBIAN/control
./DEBIAN/conffiles
./DEBIAN/postinst
./DEBIAN/prerm
./usr
./usr/bin
./usr/bin/haproxy_stat.pl

Mamy tu katalog DEBIAN gdzie mamy pliki informacyjne paczki oraz skrypty uruchamiane przed/po instalacji/deinstalacji paczki, a pozostałe katalogi to są rzeczy która nasza paczka zainstaluje na dysku (i usunie przy instalacji). Z ważniejszych:

  • control – opis/nazwa/wersja paczki oraz zależności (czego wymaga, z czym konfliktuje)
  • conffiles – które pliki paczki są plikami konfiguracyjnymi. Plik oznaczony jako “config” w tej paczce, gdy zostanie zmieniony przez usera spowoduje przy nasŧępnej instalacji zapytanie czy zostawić modyfikacje usera czy zainstalować nową wersję configa, a przy usunięciu bez opcji purge config zostanie w systemie
  • post/pre inst/rm – wykonywane  przed (pre) i po (post) instalacji/usunięciu paczki. Można użyć np. do sprzątania po aplikacji (pliki cache itp.) przy deinstalacji, lub do np. startu aplikacji od razu po instalacji, czy dodawaniu skryptów startowych

Wymagany jest tylko plik control, reszta jest opcjonalna. A wygląda on tak:

Package: xani-tools
Version: 0.0.1
Section: base
Priority: optional
Architecture: all
Maintainer: XANi <some@email>
Depends: etckeeper (>= 0.40), syslog-ng, emacs23-nox, emacs-goodies-el, git-core, iotop, iftop, links, mc, multitail, mtr-tiny, nmap, psmisc, screen, tcpdump, curl, socat
Recommends: swaks, atop, ntpdate
Suggests: hping3
Conflicts: rsyslog
Description: Some random tools
 tools i use for everyday work

Po kolei:

  • Package/version/section/priority/architecture/maintainer – wiadomo o co chodzi
  • Depends: rzeczy bez których paczka się nie zainstaluje
  • Recommends: te pakiety zostaną zaznaczone jeżeli nie będą konfliktować z niczym, ale można je odznaczyć i paczka się zainstaluje (nie są wymagane)
  • Suggests – oznacza że domyśnie nie zainstaluje danej paczki ale będzie ona na liście sugestii w menadżerze paczek
  • Conflicts – nie zainstaluje się gdy ta paczka jest w systemie. W powyższym przykładzie syslog-ng jest w depends a rsyslog w conflicts, więc spowoduje to zmianę rsysloga na syslog-ng. Można robić to samo gdy chce się np. domyślnie instalować postfixa zamiast exima
  • Description – wiadomo o co chodzi. Można dodać więcej linijek poniżej ale muszą się zaczynać od spacji (jako kontynuacja poprzedniej linijki)

To teraz tworzenie paczki. Instalujemy dpkg-dev fakeroot (NIE FAKEROOT-NG robi zbugowane paczki z ownerem plików jako user a nie root) i:

$ fakeroot dpkg-deb -b xani-tools
dpkg-deb: budowanie pakietu "xani-tools" w "xani-tools.deb".

albo gdy chcemy mieć numer wersji w paczce:

$ fakeroot dpkg-deb -b xani-tools ../repo
dpkg-deb: budowanie pakietu "xani-tools" w "../repo/xani-tools_0.0.1_all.deb".

I jeżeli chodzi o budowanie najprostrzej paczki to tyle ;]. Ale to nam nie wystarcza, chcemy przecież repo dla naszych serwerów, idealnie z paczkami w gicie, podzielone na część “stabilną” i “dev”. Zacznijmy więc od repo gitowego:

xani@hydra:~/src/my/pkg-repoᛯ  find .
.
./packages
./packages/xani-tools
./packages/xani-tools/DEBIAN
./packages/xani-tools/DEBIAN/postinst
./packages/xani-tools/DEBIAN/prerm
./packages/xani-tools/DEBIAN/conffiles
./packages/xani-tools/DEBIAN/control
./packages/xani-tools/usr
./packages/xani-tools/usr/bin
./packages/xani-tools/usr/bin/haproxy_stat.pl
xani@hydra:~/src/my/pkg-repoᛯ git init
Initialized empty Git repository in /home/xani/src/my/pkg-repo/.git/
xani@hydra:~/src/my/pkg-repo (master)ᛯ git add . ; git commit -a -m 'Initial commit'
[master (root-commit) 7a0bd78] Initial commit
 3 files changed, 48 insertions(+), 0 deletions(-)
 create mode 100644 packages/xani-tools.deb
 create mode 100644 packages/xani-tools/DEBIAN/conffiles
 create mode 100644 packages/xani-tools/DEBIAN/control
 create mode 100755 packages/xani-tools/DEBIAN/postinst
 create mode 100755 packages/xani-tools/DEBIAN/prerm
 create mode 100755 packages/xani-tools/usr/bin/haproxy_stat.pl

I teraz część najważniejsza, mianowicie budowanie paczek. Chcemy mieć możliwość cofnięcia się do starszej wersji paczki, oraz żeby paczki w branchu gitowym dev (i każdym innym) były w oddzielnym repo:

#!/bin/sh
PKGDIR="packages"
TMPDIR="/tmp/repo"
REPODIR="/home/xani/src/my/repo"
STARTPWD=$(pwd)
git clone . $TMPDIR

if [ $1 ] ; then
    BRANCH=$1
else
    BRANCH="master"
fi
#make sure repodir exists
mkdir -p $REPODIR/$BRANCH/binary

cd $TMPDIR
git checkout $BRANCH
cd $PKGDIR
for a in $(find . -mindepth 1 -maxdepth 1 -type d) ;do
    fakeroot dpkg-deb -b $a $REPODIR/$BRANCH/binary
done
# cleaning
rm -rf $TMPDIR

#making Packages.gz
cd $REPODIR/$BRANCH
dpkg-scanpackages -m binary /dev/null | gzip -9c > Packages.gz

czyli gdy odpalimy ten skrypt w katalogu głównym repa stworzy on nam repo w podanym katalogu z brancha master, chyba że podamy mu nazwę innego brancha. Teraz wystarczy dodać do repo docelowego serwera coś w stylu:

deb ssh://repo@reposerwer/repo/packages/master ./

i dodać klucze publiczne. Można także po prostu udostępnić przez http jak “normalne” repo, wszystko zależy czy w naszych paczkach jest coś poufnego.
Dalszy rozwoj to budowanie paczek w posthookach repo albo używając hooków githuba (aktualnie mam tak w pracy, minutę-dwie po commicie przychodzi mi email z wynikami budowania paczek i ew. błędami).

Na początku to trochę roboty ale szybko się zwraca, zwłaszcza gdy już zrobi się parę paczek typu “zainstaluj mi wszystko co jest wymagane do postawienia LAMP” albo “zainstaluj mi toole które używam” :)