Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX OTA GÜNCELLEME 2026

OSTree
Atomik İşletim Sistemi Güncellemeleri

OSTree'nin git benzeri dosya sistemi depolaması ile gömülü cihazlarda atomik, geri alınabilir OTA güncellemeleri ve A/B boot entegrasyonu.

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

ÖzellikOSTreeRAUCSWUpdateMender
Depolama modeliContent-addressable FSA/B bölümA/B bölümA/B bölüm
Delta güncellemeEvet (static delta)HayırEvet (bsdiff)Evet
Disk kullanımıHardlink paylaşımı — verimli2x boyut2x boyut2x boyut
Uygulama güncellemesiFlatpak ile birlikteYokYokYok
Yocto desteğimeta-updatermeta-raucmeta-swupdatemeta-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

file nesnesiTek dosyanın içeriği — SHA256 özeti ile içerik eşleşmesi garanti edilir
dirtree nesnesiBir dizinin dosya ve alt dizinlerini listeler — her biri SHA256 referansıdır
dirmeta nesnesiDizin meta verisi: uid, gid, mode, xattrs
commit nesnesiKök dirtree referansı + zaman damgası + üst commit + commit mesajı

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İçerikBoyutNotlar
/dev/mmcblk0p1Boot (FAT32)256 MBKernel, initramfs, GRUB/U-Boot
/dev/mmcblk0p2Rootfs (ext4)4+ GBOSTree deposu + deploymentler
/dev/mmcblk0p3/var (ext4)1+ GBMutable veriler — güncellemeden etkilenmez
/dev/mmcblk0p4Veri (ext4/f2fs)KalanUygulama 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

ÖzellikOSTree (OS)Flatpak (Uygulama)
KapsamKernel, libc, sistem araçlarıMasaüstü/gömülü uygulamalar
RollbackTam OS rollback (deployment)Uygulama versiyonu geri alma
SandboxYok (sistem bileşeni)Bubblewrap sandbox
Güncelleme sıklığıDüşük (ayda 1–2)Yüksek (günlük mümkün)
Yeniden başlatmaZorunluGenellikle 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 boyutuAçıklama
Tam imaj (eski yöntem)180 MBHer güncellemede tam rootfs
OSTree pull (delta yok)~20 MBYalnızca değişen nesneler
OSTree static delta~8 MBbsdiff 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

Kernel güncellemeTipik delta: 3–15 MB (vmlinuz ikili farkı bsdiff ile)
Libc güncellemesiTipik delta: ~500 KB (tam kütüphane ~2 MB yerine)
Konfigürasyon değişikliğiTipik delta: 1–10 KB (yalnızca metin farkları)
Büyük binary güncellemebsdiff ile genel kural: %60–80 boyut azaltımı

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

CihazRaspberry Pi 4 — 32 GB SD kart, Debian Bullseye veya Yocto imajı
SunucuHTTP sunucusu erişilebilir (aynı LAN veya internet)
BootloaderU-Boot — meta-raspberrypi + meta-updater ile hazırlanmış
Ethernet veya WiFi — OTA indirme için bant genişliği

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