tar.gz çalışıyorken neden uğraşalım?
`curl | tar` ile kurulum ilk seferde gayet iyi çalışır. Sorun ikinci seferde başlar. Kullanıcıya yeni sürüm çıktığını hiçbir şey söylemez, diğer paketleriyle birlikte hiçbir şey güncellemez, temiz bir kaldırma yolu yoktur. Herkes kendi küçük güncelleme ritüelini icat eder.
Paket deposu bunların hepsini işletim sisteminde zaten var olan mekanizmaya devreder. `apt-get upgrade` yeni sürümünüzü diğer her şeyle birlikte alır. Kaldırmak `apt-get remove`'dur. Kaynağın doğruluğu ise paket yöneticisinin kimse düşünmeden otomatik kontrol ettiği bir GPG imzasıdır.
Bedeli bir öğleden sonralık kurulum ve script'e bağlayabileceğiniz küçük bir sürüm başı iş. İşte tamamı.
Depo aslında nedir?
Her iki format da HTTPS üzerinden sunulan düz statik dosyalardan ibarettir. Ne bir servis ne de veritabanı vardır — CDN'in bu işi bu kadar iyi yapmasının sebebi tam olarak budur.
Bir APT deposunda `.deb` dosyalarını tutan bir `pool/` ve metadata'yı tutan bir `dists/<suite>/` ağacı bulunur. `Packages` her paketi boyutu, hash'leri ve yolu ile listeler; `Release` bu `Packages` dosyalarının hash'lerini listeler; imza da `Release`'i kapsar. Güven zincirinin tamamı budur — ve bu ayrıntı ilerideki bölümde önemli olacak.
YUM deposu daha düzdür: `.rpm` dosyaları, `repomd.xml` ve onun işaret ettiği sıkıştırılmış indeksleri barındıran `repodata/` dizininin yanında durur.
Depo yapısı
deb/
dists/stable/
Release InRelease Release.gpg
main/binary-amd64/Packages{,.gz}
main/binary-arm64/Packages{,.gz}
pool/main/a/aracim/aracim_1.0.0_amd64.deb
rpm/
aracim-1.0.0-1.x86_64.rpm
repodata/
repomd.xml repomd.xml.asc
*-primary.xml.gz *-filelists.xml.gz *-other.xml.gz
İmzalama anahtarını üretin
Anahtar, gelişigüzel yeniden yapamayacağınız tek parçadır: kaybederseniz her kullanıcının yeni anahtar içe aktarması gerekir. Bir kez üretin, hem gizli anahtarı hem iptal sertifikasını yedekleyin ve özel yarısını ihtiyacı olmayan hiçbir makinede tutmayın.
Sürüm script'leri imzayı etkileşimsiz atar, bu yüzden depo imzalama anahtarları genelde parolasız olur — tıpkı bir CI imzalama anahtarı gibi, dosya izinleriyle ve kısıtlı bir yerde durarak korunur. Gerçek bir son kullanma tarihi verin (beş yıl yaygındır) ve yenileme tarihini gerçekten göreceğiniz bir yere yazın.
cat > key.batch <<'EOF'
%no-protection
Key-Type: RSA
Key-Length: 4096
Key-Usage: sign
Name-Real: example.com Package Signing
Name-Email: packages@example.com
Expire-Date: 5y
%commit
EOF
gpg --batch --gen-key key.batch && rm key.batch
# public yarısı — kullanıcıların içe aktardığı budur
gpg --armor --export packages@example.com > example-com.gpg
# BUNLARI YEDEKLEYİN, çevrimdışı:
# gpg --export-secret-keys --armor packages@example.com
# ~/.gnupg/openpgp-revocs.d/<fingerprint>.rev
APT deposunu kurun ve imzalayın
`apt-ftparchive` (apt-utils paketinden) her iki metadata dosyasını da üretir. İki ayrıntı önemli.
Komutu deponun kökünden çalıştırın; çünkü `Packages` içine yazdığı `Filename:` yolları komutu nerede çalıştırdığınıza görecelidir. Bunu yanlış yaparsanız apt metadata'yı sorunsuz okur, ama her indirmede 404 alırsınız.
Sonra imzayı iki biçimde yayınlayın: `InRelease` modern satır içi imzalı biçim, `Release.gpg` ise eski istemciler için ayrık imza. İkisini birden üretmenin maliyeti yok ve bir sürü destek sorusunu baştan engelliyor.
cd deb
for arch in amd64 arm64; do
mkdir -p "dists/stable/main/binary-$arch"
apt-ftparchive --arch "$arch" packages pool \
> "dists/stable/main/binary-$arch/Packages"
gzip -9cn "dists/stable/main/binary-$arch/Packages" \
> "dists/stable/main/binary-$arch/Packages.gz"
done
apt-ftparchive \
-o APT::FTPArchive::Release::Origin=example.com \
-o APT::FTPArchive::Release::Suite=stable \
-o APT::FTPArchive::Release::Components=main \
-o APT::FTPArchive::Release::Architectures="amd64 arm64" \
release dists/stable > dists/stable/Release
gpg --batch --yes -abs -o dists/stable/Release.gpg dists/stable/Release
gpg --batch --yes --clearsign -o dists/stable/InRelease dists/stable/Release
RPM tek değil, iki imza ister
Neredeyse herkesin takıldığı adım budur; biz de takıldık. APT ile YUM paketleri temelden farklı biçimde doğrular.
APT bir zincirdir: imza `Release`'i kapsar, o `Packages`'ın hash'lerini tutar, o da her `.deb`'in hash'ini tutar. Metadata'yı imzaladığınızda paketler kendiliğinden kapsanır — tekil `.deb` dosyaları hiç imzalanmaz.
RPM'de ise birbirinden bağımsız iki kontrol vardır. `repo_gpgcheck=1`, ayrık `repomd.xml.asc` üzerinden `repomd.xml`'i doğrular. Ama `gpgcheck=1`, her paketin **içine gömülü** bir imzayı doğrular ve bunu başka hiçbir şey sağlamaz. Yalnızca metadata'yı imzalarsanız dnf şu hatayla durur: `Package … is not signed / Error: GPG check FAILED`.
Bu yüzden her `.rpm` için `rpmsign` çalıştırın — ve bunu `createrepo_c`'den **önce** yapın, çünkü imzalama dosyaları yeniden yazar ve daha önce kaydedilmiş her checksum anında geçersiz olur.
# rpmsign, Debian/Ubuntu'da 'rpm' paketiyle gelir.
# rpm, %__gpg değerini /usr/bin/gpg2 olarak sabitler; Debian/Ubuntu bunu sunmaz:
rpmsign --define "_gpg_name packages@example.com" \
--define "__gpg $(command -v gpg)" \
--addsign rpm/*.rpm
# imzasız bir şey yayınlamaktansa gürültülü hata ver
for r in rpm/*.rpm; do
rpm -qpi "$r" | grep -qi '^Signature *: *(none)' \
&& { echo "IMZASIZ: $r"; exit 1; }
done
# indeksleri ancak şimdi üret, sonra repomd.xml'i imzala
createrepo_c --quiet rpm/
gpg --batch --yes --detach-sign --armor \
-o rpm/repodata/repomd.xml.asc rpm/repodata/repomd.xml
CDN üzerinden sunmak — ve tuzağı
Paket deposu, CDN için neredeyse ideal bir yüktür: statik dosyalar, değiştiğinden çok daha sık indirilir, hem de dünyanın her yerinden. Edge'i origin'inize yöneltin, bant genişliği sorunu ortadan kalksın.
Tuzak şu: depo metadata'sı **kendi içinde tutarlıdır**. Her dosya başka dosyaların hash'lerine atıfta bulunur. Size taze bir `Release` ile bayat bir `Packages` veren bir önbellek, biraz eski bir depo vermiş olmaz — **bozuk** bir depo vermiş olur. Üstelik hata mesajı önbellekten hiç söz etmez.
Tek bir yayında yaşadığımız üç hata şunlar:
• Bayat `InRelease`, taze `Packages` — apt hash uyuşmazlığı bildirir.
• Bayat `.rpm` — dnf `Package … is not signed` der; çünkü imzalama dosyayı yeniden yazmıştır ve önbellekteki kopya eski, imzasız olandır.
• Taze `repomd.xml`, bayat `repomd.xml.asc` — dnf `Bad PGP signature` der. İkisi de değişmiştir, ama yalnızca biri purge edilmiştir.
Buradan çıkan kural: **eksik purge, hiç purge etmemekten kötüdür.** Yayının yeniden yazdığı **her** dosyayı purge edin — giriş noktalarını, `repodata/` altındaki hash isimli indeks dosyalarını ve paketlerin kendisini. Alternatif olarak metadata'ya çok kısa bir TTL verip paketleri uzun süre önbellekte tutun; nasılsa paket adları her sürümde değişiyor.
# giriş noktaları
for p in /deb/dists/stable/InRelease \
/deb/dists/stable/Release \
/deb/dists/stable/Release.gpg \
/deb/dists/stable/main/binary-amd64/Packages \
/deb/dists/stable/main/binary-amd64/Packages.gz \
/rpm/repodata/repomd.xml \
/rpm/repodata/repomd.xml.asc; do
cdnctl purge --account "$ACC" --path "$p" --type exact
done
# hash isimli indeksler ve paketler de değişti
for f in rpm/repodata/* rpm/*.rpm; do
cdnctl purge --account "$ACC" \
--path "/rpm/$(basename "$f")" --type exact
done
Yayınlama: dosyalar CDN'e nasıl gidiyor?
Depo yalnızca statik dosyalardan ibaret olduğu için "yayınlamak", o dizin ağacını CDN'in sunabileceği bir yere koymak demek. İki farklı biçimi var ve hangisini isteyeceğiniz zaten bir web sunucusu işletip işletmediğinize bağlı.
**Zaten bir origin'iniz varsa** — HTTPS sunan herhangi bir makine — CDN'i onun önüne alın ve yayınlamayı dosyaları oraya kopyalayarak yapın. SSH üzerinden `rsync` yeter; edge ilk istekte origin'den çeker, sonrasında önbellekten sunar. cdn.com.tr için biz bunu yapıyoruz, çünkü indirme sunucusu siteyi sunan makinenin ta kendisi.
Deneyimden bir uyarı: üst dizini değil, alt dizinleri senkronlayın. Diğer indirmelerinizi de barındıran bir dizine yöneltilmiş `rsync --delete`, onları seve seve siler.
**Hiç origin işletmek istemiyorsanız**, dosyaları doğrudan CDN depolamasına gönderin. cdn.com.tr ile ağacı yüklersiniz ve kendi CDN alan adınızdan sunulur — kurulacak, yamalanacak, ayakta tutulacak bir web sunucusu yok. `cdnctl cp -r` bütün bir dizini yükler; bir deponun yapısı da tam olarak budur.
Her iki durumda da kullanıcılarınızın `sources.list` ya da `.repo` dosyasına yazacağı temel adres, CDN alan adınız artı yayınladığınız yoldur. Ve her iki durumda da bir önceki bölümdeki purge adımıyla bitirin — asıl unutulan kısım orası.
# A) origin'iniz var: alt dizinleri senkronlayın, üst dizini asla
rsync -a --delete dist/repo/deb/ user@origin:/var/www/downloads/deb/
rsync -a --delete dist/repo/rpm/ user@origin:/var/www/downloads/rpm/
rsync -a dist/repo/example-com.gpg user@origin:/var/www/downloads/
# B) origin yok: doğrudan CDN depolamasına gönderin
cdnctl cp -r dist/repo/deb <account_uuid>:/downloads/deb
cdnctl cp -r dist/repo/rpm <account_uuid>:/downloads/rpm
cdnctl files put --file dist/repo/example-com.gpg \
--target-path /downloads/example-com.gpg
# temel adres = CDN alan adınız + bu yol:
# deb -> https://cdn.example.com/downloads/deb
# rpm -> https://cdn.example.com/downloads/rpm
Temiz bir konteynerde doğrulayın
Doğrulamayı kendi makinenizde yapmayın. O makine anahtarınıza zaten güveniyor, elinde önbelleğe alınmış bir indeks olabilir ve kullanıcılarınızın birazdan çarpacağı sorunları seve seve gizler. Tek dürüst test, tek kullanımlık bir konteynerdir — hem eksik paket imzasını hem de bayat önbellek hatalarını böyle bulduk.
Test ederken imza kontrolünü **açık** tutun. "Gerisi çalışıyor mu bir bakayım" diye `gpgcheck`'i kapatmak, deponun imzasız yayına çıkmasının en bilinen yoludur.
Fedora tarafı için bir ipucu: `--disablerepo="*" --enablerepo="depomuz"` verin. Aksi halde dnf zamanının çoğunu Fedora'nın kendi metadata'sını indirmekle geçirir ve yavaş ya da başarısız bir çalıştırma size kendi deponuz hakkında hiçbir şey söylemez.
# Debian / Ubuntu
docker run --rm debian:12 sh -c '
apt-get update -qq && apt-get install -y -qq curl gnupg ca-certificates
curl -fsSL https://example.com/example-com.gpg |
gpg --dearmor -o /usr/share/keyrings/example-com.gpg
echo "deb [signed-by=/usr/share/keyrings/example-com.gpg] \
https://example.com/deb stable main" \
> /etc/apt/sources.list.d/example.list
apt-get update && apt-get install -y aracim && aracim --version'
# Fedora / RHEL — sadece kendi depomuz, ki test deponuzu ölçsün
docker run --rm fedora:40 sh -c '
printf "[example]\nbaseurl=https://example.com/rpm\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=https://example.com/example-com.gpg\n" \
> /etc/yum.repos.d/example.repo
dnf --disablerepo="*" --enablerepo="example" -y install aracim
rpm -qi aracim | grep Signature'
Son bir şey: pakette self-update'i kapatın
Aracınız kendini güncelleyebiliyorsa, bu özellik artık paket yöneticisiyle çakışır. Self-update, `dpkg` ya da `rpm`'in sahip olduğu bir dosyanın üzerine yazar; onların veritabanı ise artık diskte olmayan bir sürümü göstermeye devam eder — bir sonraki güncellemede sürpriz bir şey olur.
Çözüm küçük: derlemeye hangi yolla dağıtıldığını damgalayın ve paket yöneticisi kurmuşsa self-update kenara çekilsin. Doğrudan indirmeler eskisi gibi kendini güncellemeye devam eder.
// installChannel, paketlenmiş derlemelerde derleme anında değiştirilir.
var installChannel = "direct"
func update() error {
if installChannel != "direct" {
return fmt.Errorf(
"%s ile kurulmuş — güncellemeyi onunla yapın",
installChannel)
}
// ... normal self-update
}
// paketlenmiş derlemeler:
// go build -ldflags "-X main.installChannel=deb"
Sık sorulanlar
Depoyu imzalamak zorunda mıyım?
Teknik olarak hayır — apt ve dnf'e doğrulamayı atlaması söylenebilir. Pratikte evet. Aksi halde kullanıcıların yazılımınızı kurmak için bir güvenlik kontrolünü kapatması gerekir; bu hem istenmesi kötü bir şey hem de öğretilmesi daha kötü bir alışkanlık. İmzalamanın maliyeti bir anahtar ve iki komut.
Metadata'yı imzaladım, dnf neden paket imzasız diyor?
Çünkü RPM birbirinden ayrı iki şeyi kontrol eder. `repo_gpgcheck` `repomd.xml`'i kapsar; `gpgcheck` ise her `.rpm`'in içine gömülü imzayı. Paketlere de `rpmsign --addsign` uygulamanız gerekir — üstelik bunu `createrepo_c`'den önce yapmalısınız, çünkü imzalama dosyaları değiştirir.
Yayından hemen sonra apt neden hash uyuşmazlığı veriyor?
Neredeyse her zaman, eski ve yeni metadata'yı karışık sunan bir önbellek katmanı yüzünden. Depo metadata'sı kendi içinde tutarlıdır, dolayısıyla kısmi bir güncelleme bozuk bir depo demektir. Yayının yeniden yazdığı her dosyayı purge edin ya da metadata'ya kısa bir TTL verin.
Tek depo hem amd64 hem arm64 sunabilir mi?
Evet. APT için aynı suite altında mimari başına bir `Packages` dosyası üretin ve ikisini de `Architectures` içinde listeleyin. YUM için iki paketi aynı dizine koyun — `createrepo_c` her paketin mimarisini kendisi kaydeder.
Depo dosyaları git'te dursun mu?
Genellikle hayır. Paket havuzları her sürümde büyür ve git her sürümü sonsuza dek saklar. Ağacı sürüm çıktılarınızdan üretip origin'e senkronlayın; git'te yalnızca script'ler ve public anahtar kalsın. Derlemeyi tekrarlanabilir yapın ki depo istendiği an sıfırdan yeniden üretilebilsin.