Aptly - self-hosted debian repozitáře

Rádi bychom představili pěkný nástroj pro správu debianích repozitářů a popsali způsob, jakým jej my používáme.

Motivace

Při správě serverů nejspíš každý z nás v jeden moment dojde do situace, kdy potřebuje distribuovat vlastní nástroje, které mu usnadní život.

Už jsme viděli spousty kreativních řešení. My jsme se jsme si položili jednoduchou otázku: Proč distribuci nedělat jako jí dělá distribuce samotná?

Dostupná řešení

V době, kdy jsme dělali rešerši řešení, byla dostupná pro nás převážně komplikovaná řešení. Většinou se jednalo o upload souboru do konkrétního adresáře a následně spuštění scriptu (buď cronem nebo přes ssh).

Narazili jsme ale na Aptly (Github), které bylo v prvotní fázi vývoje a jeho jednoduché API se nám tak líbilo, že bylo celkem rychle rozhodnuto. Nyní se již jedná o stabilní komponentu Debianu.

Jako alternativu lze zvážit využití integrace v Gitlab package registry (v současné době jen experimentálně). Zatím ale zůstaneme věrní aptly. Zastáváme názor, že jeden nástroj by měl dělat jen jednu věc a snažíme se věci držet co nejvíce jednoduché.

Hiearchie repozitářů

Debian sice krásně podporuje hostování repozitářů pro více distribucí na jedné adrese a Aptly tuto podporu má v sobě, my jsme ale měli nějaké porodní bolesti, které nám toto nasazení komplikovalo.

Rozhodli jsme se tedy dělat věci jednoduše. A tak pro každou distribuci vytváříme nový server, kde každý repozitář je subdoména. Výsledná adresa tedy vypadá následovně: PROJEKT.DISTRIBUCE.deb.DOMENA (např. demo.bookworm.deb.DOMENA).

Díky tomuto oddělení jsme získali některé dobré vlastnosti:

  • Vidíme jednoduše (tj. bez nutnosti filtrace) v accesslogu, které servery repozitář využívají.

  • Vidíme balíčky jen pro danou distribuci a projekt a můžeme udělat jednoduše jejich revizi.

  • Jednotlivé projekty jsou vzájemně oddělené a můžeme k nim jednoduše řídit přístupové oprávnění.

  • Čištění repozitáře při ukončení podpory distribuce je velmi jednoduché - smažeme celý server.

Přípravu repozitářů máme maximálně automatizovanou, takže pro vytvoření nového repozitáře stačí do puppet přidat:

demo:
    auth_users: [ 'demo:$apr1$qm06z9ya$BcszwIX1RDz9rwV7uZXmZ1' ]
    api_auth_users: [ 'demo_upload:$apr1$t0fe48k9$vFp/zPdlaC4fKQWZ00xvh.' ]

To nám vytvoří nový repozitář pro projekt demo s ověřením pomocí http authentizace, která je rozdělená na veřejnou část (servery) a API (build pipeline).

Nasazení přes více prostředí a synchronizace

Aptly je mocný nástroj a umí mimojiné i mirrorování repozitářů.

Původně nám přišlo jako dobrý nápad, aby se aplikace nasazovaly konzistentně - čili udělal se mirror celého repozitáře a nasadily se všechny změny. Později se ale ukázalo, že je-li v repozitáři trochu více balíků, tak tento přístup vyžaduje trochu organizace a plánování při nasazování.

My ale chceme dostávat do produkce jednotlivé komponenty co nejrychleji. Nahradili jsme tedy mirrorování repozitářů oddělenými repozitáři a balíky v konkrétních verzích kopírujeme mezi repozitáři.

Ukázka nasazení

Upozornění: Následující je ukázka bez jakéhokoliv zabezpečení.

Aptly je už nějakou dobu součástí Debianu, pro instalaci tedy stačí tedy jen:

apt install aptly gnupg

Příprava GPG pro podpis repozitáře

Vytvoříme soubor ~/gpg.conf s obsahem:

Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: Server zone
Name-Comment: Generated by puppet
Name-Email: info@serverzone.cz
Expire-Date: 0
%no-protection
%commit

Následně si vytvoříme gpg klíče:

mkdir -p /var/www/public/
/usr/bin/gpg --batch --gen-key ~/gpg.conf
/usr/bin/gpg --export -a "ServerZone" > /var/www/public/apt.asc

Vytvoříme aptly repozitář

Soubor: ~/.aptly.conf

{
  "rootDir": "/var/www/",
  "downloadConcurrency": 4,
  "downloadSpeedLimit": 0,
  "downloadRetries": 0,
  "downloader": "default",
  "databaseOpenAttempts": -1,
  "architectures": [],
  "dependencyFollowSuggests": false,
  "dependencyFollowRecommends": false,
  "dependencyFollowAllVariants": false,
  "dependencyFollowSource": false,
  "dependencyVerboseResolve": false,
  "gpgDisableSign": false,
  "gpgDisableVerify": false,
  "gpgProvider": "gpg",
  "downloadSourcePackages": false,
  "skipLegacyPool": true,
  "ppaDistributorID": "ubuntu",
  "ppaCodename": "",
  "skipContentsPublishing": false,
  "skipBz2Publishing": false,
  "FileSystemPublishEndpoints": {},
  "S3PublishEndpoints": {},
  "SwiftPublishEndpoints": {},
  "AzurePublishEndpoints": {},
  "AsyncAPI": false,
  "enableMetricsEndpoint": false
}

Proti výchozímu jsou tam jen 2 změny:

  • "rootDir": "/var/www/",
  • "architectures": ["amd64"],

Vytvoříme základní strukturu repozitáře spuštěním:

aptly repo create -distribution stable demo

Zároveň jej rovnou vypublikujeme, abychom jej mohli používat:

aptly publish repo demo

Instalace nginxu pro distribuci souborů

apt install nginx

Upravíme výchozí virtualhost - /etc/nginx/sites-available/default:

server {
  listen       *:80;
  listen       [::]:80;

  server_name  demo;

  location / {
    root      /var/www/public;
  }

  location /api/ {
    proxy_pass            http://localhost:8080;
  }
}

Rovnou jsme si udělali přípravu proxy pro přístup k API, které budeme spouštět před uploadem souborů.

Přidání repozitáře na počítači (testujeme lokálně)

Do souboru /etc/apt/sources.list.d/demo.list vložíme:

deb [signed-by=/var/www/public/apt.asc] http://localhost stable main

A spustíme apt update:

$ apt update
Hit:1 http://localhost stable InRelease
Hit:2 http://security.debian.org/debian-security bookworm-security InRelease
Hit:3 http://deb.debian.org/debian bookworm InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.

Nyní máme repozitář v provozním stavu a pustíme se do přípravy API pro upload nových kusů.

Aptly API

Spustíme aptly API server:

/usr/bin/aptly api serve -listen=":8080"

Upload balíčku a publikace repozitáře

Nahrajeme první balíček:

curl -f -s -S -X POST -F file=@demo_1.deb http://localhost/api/files/upload

Řekneme Aptly, aby si přidal do databáze nové soubory:

curl -f -s -S -X POST http://localhost/api/repos/demo/file/upload

A vypublikujeme repozitář:

curl -f -s -S -X PUT -H 'Content-Type: application/json' --data '{}' http://localhost/api/publish/:./stable

Závěr

Aptly nám každý den pomáhá spravovat velké množství serverů, rychle distribuovat nové aplikace a my na něj nedáme dopustit. V roce 2020 to s jeho vývojem sice nevypadalo dobře, ale nyní se aptly chytl nový člověk a postupně jej posouvá dál, za což jsme rádi.