00 OSTree neden?
Geleneksel paket tabanlı güncelleme sistemleri (dpkg, rpm, opkg) gömülü cihazlarda güvenilirlik sorunları yaratır. OSTree, dosya sistemi düzeyinde atomik commit modeliyle bu sorunları ortadan kaldırır.
Geleneksel güncellemelerin sorunları
Bir gömülü cihazda apt upgrade çalışırken güç kesilirse, sistem yarı güncellenmiş bir durumda kalır. Bazı paketler yeni sürümde, bazıları eskisinde, bazı konfigürasyon dosyaları tutarsız durumdadır. Bu durumdan kurtulmak bazen fabrika resetine giden bir yol olabilir — özellikle saha cihazlarında kabul edilemez bir senaryodur.
Geleneksel dpkg/rpm güncelleme riski:
Güncelleme başladı
|
v
Paket 1 güncellendi (yeni)
Paket 2 güncellendi (yeni)
Paket 3 GÜNCELLENİYOR ... <-- GÜÇ KESİLDİ
|
v
Sistem tutarsız: paket 3 bozuk, bağımlılıklar çözümsüz
Kurtarma: fabrika reset gerekebilir
OSTree'nin atomik commit modeli
OSTree, tüm güncellemeyi tek bir atomik işlem olarak ele alır. Güncelleme tamamlanmadan bir sonraki önyükleme ile etkinleşmez. Güç kesilirse cihaz eski sürümden açılmaya devam eder:
OSTree güncelleme akışı:
Yeni deployment indirildi (mevcut sürüm çalışmaya devam eder)
|
v
Bootloader config güncellendi (sonraki açılışta yeni sürümü dene)
|
v
Cihaz yeniden başlatıldı
|-- Başarılı? --> Yeni sürüm aktif, eski sürüm yedekte
`-- Başarısız?--> Bootloader eski sürüme geri döner (rollback)
OSTree ve rakip yaklaşımlar
| Özellik | OSTree | RAUC | SWUpdate | Mender |
|---|---|---|---|---|
| Depolama modeli | Content-addressable FS | A/B bölüm | A/B bölüm | A/B bölüm |
| Delta güncelleme | Evet (static delta) | Hayır | Evet (bsdiff) | Evet |
| Disk kullanımı | Hardlink paylaşımı — verimli | 2x boyut | 2x boyut | 2x boyut |
| Uygulama güncellemesi | Flatpak ile birlikte | Yok | Yok | Yok |
| Yocto desteği | meta-updater | meta-rauc | meta-swupdate | meta-mender |
01 OSTree mimarisi
OSTree, Git'e benzer şekilde içerik adreslenebilir (content-addressable) bir nesne deposu kullanır. Dosyalar SHA256 özetleriyle tanımlanır ve hardlink'ler aracılığıyla paylaşılır.
Nesne deposu yapısı
/ostree/repo/
├── config # Depo yapılandırması
├── objects/
│ ├── 00/ # SHA256'nın ilk 2 hex karakteri
│ │ └── abcdef... # Nesne dosyası (file/dirtree/dirmeta/commit)
│ ├── 1a/
│ └── ...
├── refs/
│ ├── heads/ # Yerel dallar
│ └── remotes/ # Uzak depo refs
└── tmp/ # Geçici işlemler
Nesne tipleri
Hardlink paylaşımı ve disk tasarrufu
İki deployment arasında değişmeyen dosyalar hardlink aracılığıyla paylaşılır. Tipik bir kernel güncellemesinde değişen dosyalar yalnızca birkaç MB olabilirken, userspace'in tamamı (yüzlerce MB) yeniden kopyalanmaz. Bu yaklaşım hem disk alanı hem de indirme bant genişliği açısından büyük tasarruf sağlar.
# İki deployment arasındaki farkı gör
ostree diff COMMIT_ESK COMMIT_YENI
# Disk kullanımını kontrol et
du -sh /ostree/deploy/*/
# deploy/myos/deploy/abc123.0/ 180M
# deploy/myos/deploy/def456.0/ 12M (yalnızca değişen dosyalar)
# Toplam gerçek kullanım: ~180M (hardlink'ler tekrar sayılmaz)
Deployment dizin yapısı
/ostree/
├── boot/
│ └── ostree/
│ └── myos-SHA256/
│ ├── vmlinuz
│ └── initramfs.img
├── deploy/
│ └── myos/
│ ├── deploy/
│ │ ├── COMMIT_HASH.0/ # Aktif deployment (rootfs)
│ │ └── COMMIT_HASH.1/ # Önceki deployment (rollback için)
│ └── var/ # Mutable veri (/var symlink'i)
└── repo/ # OSTree nesne deposu
02 Depo oluşturma ve yayımlama
OSTree deposu sunucu tarafında oluşturulur ve HTTP üzerinden istemcilere sunulur. ostree init ile başlatılır, ostree commit ile içerik eklenir.
Sunucu kurulumu
# OSTree araçlarını kur (Debian/Ubuntu)
sudo apt-get install ostree
# Yeni bir depo oluştur (archive modu: HTTP ile sunuma uygun)
mkdir -p /srv/ostree-repo
ostree --repo=/srv/ostree-repo init --mode=archive
# Depo yapılandırmasını kontrol et
cat /srv/ostree-repo/config
# [core]
# repo_version=1
# mode=archive
Rootfs'i commit etme
# Bir rootfs dizinini OSTree'ye commit et
# Örnek: Yocto çıktısı olan rootfs/ dizini
ostree --repo=/srv/ostree-repo commit \
--branch=myos/aarch64/stable \
--subject="v2.1.0 kararlı sürüm" \
--body="Kernel 6.6.21, busybox 1.36" \
--tree=dir=rootfs/
# Commit özetini doğrula
ostree --repo=/srv/ostree-repo refs
# myos/aarch64/stable
# Son commit'i incele
ostree --repo=/srv/ostree-repo log myos/aarch64/stable
Commit filtreleri
# Belirli dizinleri commit'ten hariç tut
ostree --repo=/srv/ostree-repo commit \
--branch=myos/aarch64/stable \
--skip-if-unchanged \
--exclude-regex="\.pyc$" \
--exclude-regex="^/var/" \
--tree=dir=rootfs/
# Önceki sürümle farkı gör
ostree --repo=/srv/ostree-repo diff \
myos/aarch64/stable^ \
myos/aarch64/stable
Nginx ile HTTP sunucusu
# /etc/nginx/sites-available/ostree-repo
server {
listen 80;
server_name ota.yerel.ag;
root /srv/ostree-repo;
location / {
autoindex on;
add_header Cache-Control "no-cache";
}
}
# summary dosyasını güncelle (istemci keşfi için)
ostree --repo=/srv/ostree-repo summary --update
GPG imzalama
# GPG anahtarı oluştur (bir kez)
gpg --gen-key
# Commit'i imzala
ostree --repo=/srv/ostree-repo commit \
--branch=myos/aarch64/stable \
--gpg-sign=ANAHTAR_KIMLIK \
--gpg-homedir=/root/.gnupg \
--tree=dir=rootfs/
# Depo genel anahtarını dışa aktar
gpg --export ANAHTAR_KIMLIK > /srv/ostree-repo/ostree-trusted.gpg
03 İstemci tarafı güncelleme
Cihaz tarafında ostree pull ile depodan yeni sürüm indirilir, ostree deploy ile etkinleştirilir. Staged deployment sayesinde güncelleme bir sonraki yeniden başlatmada devreye girer.
İstemci deposu başlatma
# Uzak depo ekle
ostree remote add --no-gpg-verify \
myota http://ota.yerel.ag \
--set=branches=myos/aarch64/stable
# Üretim ortamı — GPG doğrulama ile
ostree remote add \
--gpg-import=/etc/ostree/trusted.gpg \
myota http://ota.yerel.ag
# Uzak depoları listele
ostree remote list
Güncelleme indirme ve uygulama
# Yeni commit'leri kontrol et (indirmeden)
ostree remote summary myota
# Güncellemeyi indir
ostree pull myota myos/aarch64/stable
# İndirilen commit'i gör
ostree log myota:myos/aarch64/stable
# Deployment oluştur
ostree admin deploy --os=myos myota:myos/aarch64/stable
# Mevcut deployment durumunu gör
ostree admin status
# * myos 7a3f9d2e... (aktif)
# myos a1b2c3d4... (önceki)
Staged deployment
Staged deployment ile güncelleme arka planda indirilir ve hazırlanır; etkinleşme açık onay veya planlanmış yeniden başlatma ile gerçekleşir. Üretim cihazlarında kontrolsüz yeniden başlatmayı önler.
# Güncellemeyi staged modda hazırla (yeniden başlatma YOK)
ostree admin deploy --stage \
--os=myos myota:myos/aarch64/stable
# Bakım penceresinde manuel yeniden başlat
systemctl reboot
# Ya da finalize servisini tetikle
systemctl start ostree-finalize-staged.service
Rollback
# Mevcut deploymentleri gör
ostree admin status
# Önceki deployment'a geç
ostree admin undeploy 0
# Bir sonraki açılışta önceki sürüm devreye girer
systemctl reboot
04 A/B boot entegrasyonu
OSTree, A/B (aktif/yedek) bölüm stratejisini bootloader ile koordineli şekilde yönetir. U-Boot veya GRUB ile deployment seçimi ve başarısızlık durumunda otomatik rollback sağlanır.
Deployment switching mantığı
Yeni sürüm deploy edildi
|
v
Bootloader config güncellendi:
U-Boot: bootcount=0, upgrade_available=1
|
v
Cihaz yeniden başlatıldı
|
+--[Başarılı boot]--> post-boot-healthcheck çalışır
| upgrade_available=0 yazılır (kalıcı)
|
`--[Başarısız boot]--> bootcount artışı (U-Boot izler)
bootcount >= bootlimit
Eski deployment seçilir (rollback)
U-Boot entegrasyonu
# /boot/uEnv.txt — U-Boot ortam değişkenleri
bootcount=0
bootlimit=3
upgrade_available=0
ostree_root=/ostree/deploy/myos/deploy/COMMIT_HASH.0
# U-Boot script parçası (boot.scr içinde):
# if test UPGRADE_AVAILABLE = "1"; then
# setexpr bootcount BOOTCOUNT + 1
# saveenv
# if test BOOTCOUNT > BOOTLIMIT; then
# run rollback_cmd
# fi
# fi
# Linux tarafında ortam değişkenlerini güncelle
fw_setenv upgrade_available 0
fw_setenv bootcount 0
GRUB entegrasyonu
# /boot/grub/grub.cfg — OSTree tarafından yönetilir
set default="0"
set timeout=3
menuentry "myos v2.1.0 (güncel)" {
linux /ostree/boot.1/myos/vmlinuz \
root=/dev/mmcblk0p2 rw \
ostree=/ostree/deploy/myos/deploy/7a3f9d2e.0
initrd /ostree/boot.1/myos/initramfs.img
}
menuentry "myos v2.0.1 (rollback)" {
linux /ostree/boot.0/myos/vmlinuz \
root=/dev/mmcblk0p2 rw \
ostree=/ostree/deploy/myos/deploy/a1b2c3d4.0
initrd /ostree/boot.0/myos/initramfs.img
}
Önerilen bölüm düzeni
| Bölüm | İçerik | Boyut | Notlar |
|---|---|---|---|
| /dev/mmcblk0p1 | Boot (FAT32) | 256 MB | Kernel, initramfs, GRUB/U-Boot |
| /dev/mmcblk0p2 | Rootfs (ext4) | 4+ GB | OSTree deposu + deploymentler |
| /dev/mmcblk0p3 | /var (ext4) | 1+ GB | Mutable veriler — güncellemeden etkilenmez |
| /dev/mmcblk0p4 | Veri (ext4/f2fs) | Kalan | Uygulama verileri |
05 Yocto ile OSTree
meta-updater Yocto katmanı, OSTree desteğini BitBake recipe'lerine entegre eder. IMAGE_CLASSES değişkenine eklenerek OSTree uyumlu imaj üretimi otomatikleştirilir.
meta-updater katmanını ekleme
# bblayers.conf içine ekle
BBLAYERS += " \
TOPDIR/../meta-updater \
TOPDIR/../meta-updater-raspberrypi \
"
# meta-updater klonla
git clone https://github.com/uptane/meta-updater.git \
../meta-updater -b hardknott
local.conf yapılandırması
# local.conf
IMAGE_CLASSES += "ostree"
# OSTree branch adı
OSTREE_BRANCHNAME = "myos/aarch64/stable"
# Bootloader seçimi: "u-boot" veya "grub"
OSTREE_BOOTLOADER = "u-boot"
# HTTP sunucusu base URL
OSTREE_REPO_SYNC_URL = "http://ota.yerel.ag/"
# OSTree repo modu
OSTREE_REPO_MODE = "archive"
Build ve çıktılar
# Normal imaj build
bitbake myos-image-minimal
# Çıktılar (deploy/ dizininde):
# myos-image-minimal-MACHINE.ostree.tar.gz -- OSTree depo arşivi
# myos-image-minimal-MACHINE.rootfs.ext4 -- Disk imajı
# myos-image-minimal-MACHINE.wic -- Tam SD kart imajı
sstate-cache optimizasyonu
# Değişmemiş bileşenler sstate-cache'den yüklenir
# OSTree commit aşamasında yalnızca değişen dosyalar işlenir
SSTATE_DIR = "/srv/sstate-cache"
SSTATE_MIRRORS = "file://.* http://sstate.yerel.ag/PATH"
# İstatistikleri izle
bitbake -e myos-image-minimal | grep SSTATE_RESULT
bbappend ile cihaza özel yapılandırma
# meta-myos/recipes-sota/ostree/ostree_VERSION.bbappend
do_install_append() {
install -d DEST_DIR/etc/ostree/remotes.d/
cat > DEST_DIR/etc/ostree/remotes.d/myota.conf <<CONF
[remote "myota"]
url=http://ota.yerel.ag/
branches=myos/aarch64/stable;
gpg-verify=true
CONF
}
06 Flatpak ve OSTree ilişkisi
Flatpak, uygulama paketleme ve dağıtımı için OSTree altyapısını kullanır. İşletim sistemi güncellemesi (OSTree) ile uygulama güncellemesi (Flatpak) birbirinden bağımsız ve paralel çalışabilir.
İki deponun yan yana çalışması
OSTree (İşletim Sistemi) Flatpak (Uygulamalar)
/ostree/repo/ /var/lib/flatpak/repo/
├── refs/heads/ ├── refs/heads/
│ └── myos/aarch64/stable │ ├── app/org.Gnome.App/...
│ │ └── runtime/org.Gnome.Platform/...
└── objects/ └── objects/
(OS dosyaları) (uygulama + runtime dosyaları)
Her iki depo da aynı OSTree protokolünü kullanır;
birbirinden bağımsız commit ve güncelleme döngüleri vardır.
Gömülü sistemlerde Flatpak
# Flatpak kurulumu (Yocto — meta-flatpak)
IMAGE_INSTALL += "flatpak"
# Remote ekle
flatpak remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
# Uygulama yükle
flatpak install flathub org.freedesktop.Platform//23.08
flatpak install flathub com.sirket.Uygulama
# Güncelleme kontrolü
flatpak update --check
OS güncellemesi vs Uygulama güncellemesi
| Özellik | OSTree (OS) | Flatpak (Uygulama) |
|---|---|---|
| Kapsam | Kernel, libc, sistem araçları | Masaüstü/gömülü uygulamalar |
| Rollback | Tam OS rollback (deployment) | Uygulama versiyonu geri alma |
| Sandbox | Yok (sistem bileşeni) | Bubblewrap sandbox |
| Güncelleme sıklığı | Düşük (ayda 1–2) | Yüksek (günlük mümkün) |
| Yeniden başlatma | Zorunlu | Genellikle gereksiz |
Runtime ref'lerini sorgulama
# Mevcut Flatpak runtime'larını listele
flatpak list --runtime
# OSTree nesne deposunu sorgula
ostree --repo=/var/lib/flatpak/repo refs | grep runtime
# Delta güncelleme istatistikleri
flatpak update --no-deploy --verbose 2>&1 | grep "Delta"
07 Delta güncellemeleri
OSTree static delta'ları, iki commit arasındaki farkı tek bir indirilebilir dosyaya paketler. Bsdiff algoritması ile bayt düzeyinde delta hesaplanır ve bant genişliği kullanımı dramatik biçimde azaltılır.
Static delta oluşturma
# Sunucu tarafında: eski commit'ten yeni commit'e delta üret
ostree --repo=/srv/ostree-repo static-delta generate \
--from=a1b2c3d4e5f6... \
--to=7a3f9d2e1b4c... \
--min-fallback-size=0
# Delta boyutunu kontrol et
ostree --repo=/srv/ostree-repo static-delta list
# a1b2c3d4e5f6->7a3f9d2e1b4c 8.2M
# summary güncelle
ostree --repo=/srv/ostree-repo summary --update
İstemci tarafında delta kullanımı
# İstemci — delta otomatik algılanır ve kullanılır
ostree pull myota myos/aarch64/stable
# Yerel delta listesini kontrol et
ostree --repo=/ostree/repo static-delta list
Delta boyutu karşılaştırması
| Yöntem | İndirme boyutu | Açıklama |
|---|---|---|
| Tam imaj (eski yöntem) | 180 MB | Her güncellemede tam rootfs |
| OSTree pull (delta yok) | ~20 MB | Yalnızca değişen nesneler |
| OSTree static delta | ~8 MB | bsdiff ile sıkıştırılmış fark |
Delta üretimini otomatikleştirme
#!/bin/bash
# Yeni commit sonrası otomatik delta üret
REPO=/srv/ostree-repo
BRANCH=myos/aarch64/stable
# Son iki commit'i al
COMMIT_NEW=$(ostree --repo=$REPO rev-parse $BRANCH)
COMMIT_OLD=$(ostree --repo=$REPO rev-parse $BRANCH^)
echo "Delta: $COMMIT_OLD -> $COMMIT_NEW"
ostree --repo=$REPO static-delta generate \
--from=$COMMIT_OLD \
--to=$COMMIT_NEW \
--min-fallback-size=0
ostree --repo=$REPO summary --update
echo "Delta hazir, summary guncellendi."
Delta bileşeni istatistikleri
08 Gömülü senaryo: Raspberry Pi OTA demo
Raspberry Pi 4 üzerinde OSTree tabanlı OTA sistemi kurulumu ve update-agent servisi ile tam bir gömülü güncelleme döngüsü örneği.
Ortam gereksinimleri
update-agent betiği
#!/bin/bash
# /usr/bin/update-agent.sh
REMOTE=myota
BRANCH=myos/aarch64/stable
LOG=/var/log/update-agent.log
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a $LOG; }
check_and_update() {
log "Güncellemeler kontrol ediliyor..."
CURRENT=$(ostree admin status | awk '/^\*/{print $3}')
log "Mevcut: $CURRENT"
ostree pull --commit-metadata-only $REMOTE $BRANCH 2>>$LOG
REMOTE_COMMIT=$(ostree rev-parse $REMOTE:$BRANCH)
log "Uzak: $REMOTE_COMMIT"
if [ "$CURRENT" = "$REMOTE_COMMIT" ]; then
log "Sistem güncel."
return 0
fi
log "Yeni sürüm bulundu: $REMOTE_COMMIT"
# Tam pull (delta varsa otomatik kullanılır)
ostree pull $REMOTE $BRANCH >>$LOG 2>&1
# Staged deploy
ostree admin deploy --stage --os=myos $REMOTE:$BRANCH >>$LOG 2>&1
if [ $? -eq 0 ]; then
log "Güncelleme hazır. Bir sonraki yeniden başlatmada aktif olacak."
else
log "HATA: deploy başarısız!"
fi
}
check_and_update
systemd timer ile otomatik kontrol
# /etc/systemd/system/update-agent.service
[Unit]
Description=OTA Update Agent
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/update-agent.sh
# /etc/systemd/system/update-agent.timer
[Unit]
Description=OTA güncellemelerini saatlik kontrol et
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
# Etkinleştir ve test et
systemctl enable --now update-agent.timer
systemctl start update-agent.service
journalctl -u update-agent.service -f
Post-boot health check
#!/bin/bash
# /usr/bin/post-boot-healthcheck.sh
HEALTH_OK=1
for svc in networking ssh myapp.service; do
if ! systemctl is-active --quiet $svc; then
echo "HATA: $svc çalışmıyor!" >&2
HEALTH_OK=0
fi
done
if [ $HEALTH_OK -eq 1 ]; then
fw_setenv upgrade_available 0
fw_setenv bootcount 0
echo "Sağlık kontrolü başarılı, güncelleme onaylandı."
else
echo "Sağlık kontrolü BAŞARISIZ — rollback bekleniyor."
exit 1
fi
Tam OTA döngüsü
[Sunucu] Yeni rootfs hazırlandı
|
v
ostree commit --branch=myos/aarch64/stable
|
v
static-delta generate (delta paketi ~8 MB)
|
v
ostree summary --update
|
v
[Cihaz] update-agent.timer tetiklendi
|
v
ostree pull myota (delta ile indirildi)
|
v
ostree admin deploy --stage
|
v
[Bakım penceresi] systemctl reboot
|
v
U-Boot: yeni deployment seçildi
|
+--[Başarılı]--> post-boot-healthcheck OK
| fw_setenv upgrade_available 0
| Güncelleme ONAYLANDI
|
`--[Başarısız]-> bootcount >= bootlimit
U-Boot eski deployment seçer
ROLLBACK TAMAMLANDI