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

OTA Güncellemeler
SWUpdate & Mender

sahadaki cihazı güvenle güncelle — atomik A/B partition güncellemesi, imzalı artifact, rollback ve delta transfer.

00 OTA neden zor

Sahadaki gömülü cihazları güncellemek, masaüstü yazılım güncellemesinden çok daha risklidir; güç kesintisi, bozuk paket veya bağlantı kopması cihazı kalıcı olarak kullanılamaz hale getirebilir.

Güç kesintisi riski

Gömülü sistemlerde güncelleme sırasında güç kesilebilir. Eğer update, rootfs partition'ına yarım yazılmışsa cihaz boot edemez. Fiziksel erişim yoksa brick olmuş bir cihazı düzeltmek ya çok maliyetlidir ya da imkânsızdır. Bu yüzden atomicity — ya tümü ya hiçbiri garantisi — OTA'nın birinci şartıdır.

Rollback zorunluluğu

Yeni firmware'in bir donanım revizyonuyla uyumsuz olduğu, regresyon içerdiği veya network stack'inin bozuk geldiği durumlar yaşanır. Bu senaryolarda sistem otomatik olarak eski sürüme geri dönebilmelidir. İnsan müdahalesi gerektiren rollback, production ortamında kabul edilemez.

OTA araçları karşılaştırması

AraçDilA/BİmzalamaServerUygun senaryo
SWUpdateCEvetRSA/CMSHawkBit, özelKüçük-orta, esnek handler
MenderC++/GoEvetRSASaaS/self-hostedKurumsal, yönetilen fleet
RAUCCEvetCMSHawk-Bit, hawkBitsystemd ekosistemi
OSTreeCEvetGPGFlatpak sunucusuDesktop-embedded hybrid
swupdate (custom)Bash/PythonManuelYokYokPrototip, geliştirme

Seçim kriterleri

Cihaz sayısı<100 cihaz: SWUpdate + basit script server yeterli. >1000 cihaz: Mender veya RAUC + HawkBit gibi yönetim platformu gerekli.
Yocto / BuildrootYocto: meta-mender ve meta-swupdate katmanları mevcut. Buildroot: SWUpdate paketi hazır, Mender daha az olgun.
Delta güncellemeBandwidth kısıtlıysa: Mender binary delta veya SWUpdate + zchunk. Delta olmadan tipik rootfs 50-200 MB transfer gerektirir.
Güvenlik gereksinimleriİmzalı artifact zorunlu: Mender RSA imzalama, SWUpdate CMS (OpenSSL), RAUC X.509 bundle imzası.

Bu bölümde

  • OTA'nın temel zorluğu: atomicity, rollback, güvenlik
  • SWUpdate, Mender, RAUC, OSTree karşılaştırması
  • Seçim kriterleri: cihaz sayısı, build sistemi, delta ihtiyacı

01 A/B partition şeması

A/B (dual-bank) partition şeması, güncellemenin pasif slota yazılmasını, onaylanana kadar aktif slotan boot edilmesini sağlayan en güvenilir OTA mimarisidir.

Partition düzeni

  eMMC
  ┌───────────────────────────────────────────────────────┐
  │  p1: boot     64 MB   FAT32    kernel + dtb + initramfs     │
  │  p2: rootfsA 256 MB   squashfs Slot A  ← aktif (v1.0)      │
  │  p3: rootfsB 256 MB   squashfs Slot B  ← güncelleme hedefi │
  │  p4: data    Kalan    ext4     Kalıcı veri + config          │
  └───────────────────────────────────────────────────────┘

Güncelleme akışı

  [1] OTA daemon yeni rootfs.squashfs indirir → /data/tmp/
  [2] Slot B'ye (p3) yazar: dd if=rootfs-v1.1.squashfs of=/dev/mmcblk0p3
  [3] U-Boot env: boot_slot=B, upgrade_available=1, bootcount=0
  [4] Reboot
  [5] U-Boot Slot B'den boot eder
  [6] Health check servisi (30s): servisler OK?
      → Evet: fw_setenv upgrade_available 0  (B kalıcı)
      → Hayır: reboot
  [7] bootcount > 3: U-Boot Slot A'ya döner (rollback)

U-Boot environment değişkenleri

bash — fw_printenv / fw_setenv
# Mevcut ortamı görüntüle
fw_printenv
# boot_slot=A
# upgrade_available=0
# bootcount=0
# bootlimit=3

# Güncelleme öncesi: Slot B'yi hedefle
fw_setenv boot_slot B
fw_setenv upgrade_available 1
fw_setenv bootcount 0

# Güncelleme onayı (health check başarılı)
fw_setenv upgrade_available 0

# Mevcut aktif slotu sor
fw_printenv boot_slot | cut -d= -f2

U-Boot bootscript

boot.cmd (mkimage ile dönüştürülür)
if test "${upgrade_available}" = "1"; then
    setexpr bootcount ${bootcount} + 1
    saveenv
    if test ${bootcount} > ${bootlimit}; then
        echo "Rollback! bootcount=${bootcount}"
        setenv boot_slot A
        setenv upgrade_available 0
        setenv bootcount 0
        saveenv
    fi
fi

if test "${boot_slot}" = "B"; then
    setenv mmcpart 3
else
    setenv mmcpart 2
fi

setenv bootargs console=ttymxc0,115200 \
    root=/dev/mmcblk0p${mmcpart} rootfstype=squashfs ro \
    systemd.volatile=state quiet

sqfsload mmc 0:${mmcpart} ${loadaddr}    /boot/Image
sqfsload mmc 0:${mmcpart} ${fdt_addr_r} /boot/myboard.dtb
booti ${loadaddr} - ${fdt_addr_r}

Bu bölümde

  • A/B partition şeması: boot + rootfsA + rootfsB + data
  • Güncelleme akışı: indir → pasif slota yaz → reboot → health check → onayla
  • fw_setenv/fw_printenv: U-Boot env değişkenleri
  • U-Boot bootscript: bootcount + rollback mantığı

02 SWUpdate temelleri

SWUpdate, gömülü Linux için C ile yazılmış esnek bir güncelleme framework'üdür; CPIO tabanlı .swu formatı, pluggable handler'lar ve web arayüzü içerir.

SWUpdate mimarisi

  .swu dosyası (CPIO arşivi)
  ├── sw-description          (libconfig formatında güncelleme tarifi)
  ├── sw-description.sig      (opsiyonel imza)
  ├── rootfs.squashfs         (rootfs image)
  ├── kernel.itb              (kernel FIT image)
  └── postinstall.sh          (opsiyonel script)

  SWUpdate süreci:
  [parse sw-description] → [doğrula] → [handler'ları çağır] → [bootloader env güncelle]

Build ve konfigürasyon

bash
# Kaynak koddan derleme
git clone https://github.com/sbabic/swupdate.git
cd swupdate

# Handler seçimi (menuconfig)
make menuconfig
# Handlers → Raw image handler (rawfile)
# Handlers → Shell script handler
# Bootloader support → U-Boot support
# Network → Embedded web server (mongoose)

make -j$(nproc)

# Cross-compile
make CROSS_COMPILE=arm-linux-gnueabihf- \
     ARCH=arm \
     -j$(nproc)

Temel çalışma modları

Local (stdin)swupdate -i update.swu — dosyadan okuma.
Local (pipe)cat update.swu | swupdate — pipe'dan okuma.
Suricatta (HawkBit)swupdate -u "-t default -u http://hawkbit:8080" — HawkBit server polling.
Web serverswupdate -w "-document_root /var/www/swupdate" — Tarayıcıdan .swu yükleme.
HTTP downloadswupdate -d "-url http://server/update.swu" — URL'den indir ve uygula.

Handler türleri

HandlerAçıklamaKullanım
rawBlock device'a ham yazmarootfs, kernel partition
rawfileDosya sistemi üzerine dosya yaztek dosya güncelleme
shellscriptBash script çalıştırpre/post install işlemler
ubivolUBI volume'a yazRaw NAND flash
archivetar.gz'yi dosya sistemine aç/data güncelleme
bootloaderU-Boot environment güncelleSlot geçişi

Bu bölümde

  • .swu formatı: CPIO arşivi içinde sw-description + image dosyaları
  • Çalışma modları: local, suricatta (HawkBit), web server, HTTP download
  • Handler türleri: raw, rawfile, shellscript, ubivol, bootloader
  • Build: make menuconfig ile handler seçimi

03 sw-description yazımı

sw-description, .swu paketinin tarifidir; libconfig formatında yazılır ve hangi dosyanın nereye nasıl yükleneceğini belirtir.

Temel yapı

sw-description
software =
{
    version = "1.1.0";
    description = "MyBoard firmware v1.1.0";
    hardware-compatibility = ["1.0", "1.1", "2.0"];

    images: (
        {
            filename = "rootfs.squashfs";
            type     = "raw";
            device   = "/dev/mmcblk0p3";  /* Slot B */
            sha256   = "abc123...def456";
        },
        {
            filename = "kernel.itb";
            type     = "raw";
            device   = "/dev/mmcblk0p1";
            sha256   = "fed321...cba654";
            offset   = "0x100000";  /* 1 MB offset */
        }
    );

    scripts: (
        {
            filename = "postinstall.sh";
            type     = "shellscript";
        }
    );

    bootenv: (
        {
            name  = "boot_slot";
            value = "B";
        },
        {
            name  = "upgrade_available";
            value = "1";
        },
        {
            name  = "bootcount";
            value = "0";
        }
    );
};

Multi-image güncelleme

sw-description — kernel + rootfs + dtb
software =
{
    version = "2.0.0";
    hardware-compatibility = ["2.0"];

    images: (
        {
            filename = "rootfs-v2.squashfs";
            type     = "raw";
            device   = "/dev/mmcblk0p3";
            sha256   = "11223344...";
            compressed = false; /* squashfs zaten sıkıştırılmış */
        },
        {
            filename = "Image";
            type     = "rawfile";
            path     = "/boot/Image";
            sha256   = "aabbccdd...";
        },
        {
            filename = "myboard.dtb";
            type     = "rawfile";
            path     = "/boot/myboard.dtb";
            sha256   = "11aabb22...";
        }
    );

    scripts: (
        {
            filename   = "pre-install.sh";
            type       = "shellscript";
            properties: { "install-if-different" = ["true"]; };
        }
    );
};

Şifreli image

sw-description — AES-256-CBC şifreli image
images: (
    {
        filename  = "rootfs.squashfs.enc";
        type      = "raw";
        device    = "/dev/mmcblk0p3";
        encrypted = true;
        sha256    = "...";
    }
);
bash — .swu paketi oluşturma
# SHA256 hesapla
SHA_ROOTFS=$(sha256sum rootfs.squashfs | cut -d ' ' -f1)
SHA_KERNEL=$(sha256sum Image         | cut -d ' ' -f1)

# sw-description içine hash'leri yerleştir
sed -i "s/ROOTFS_SHA256/$SHA_ROOTFS/g" sw-description
sed -i "s/KERNEL_SHA256/$SHA_KERNEL/g" sw-description

# CPIO arşivi oluştur (sw-description MUTLAKA ilk sırada)
echo sw-description sw-description.sig rootfs.squashfs Image myboard.dtb postinstall.sh \
    | tr ' ' '\n' \
    | cpio -ovH newc > firmware-v1.1.0.swu
NOT

sw-description CPIO arşivinde ilk dosya olmalıdır. SWUpdate, arşivi stream modunda işler ve ilk önce sw-description'ı parse etmesi gerekir. Sıralama yanlışsa swupdate: sw-description not found hatası alırsınız.

Bu bölümde

  • sw-description libconfig formatı: software, images, scripts, bootenv
  • hardware-compatibility: donanım revizyonu kontrolü
  • sha256 doğrulaması: her image için hash zorunlu
  • Şifreli image: encrypted = true
  • CPIO paket oluşturma: sw-description ilk sırada olmalı

04 SWUpdate ile A/B güncelleme

SWUpdate'in bootloader handler'ı ve Lua/shell script desteğiyle tam A/B güncelleme pipeline'ı kurulabilir.

Aktif slotu tespit etme

preinstall.sh
#!/bin/sh
# SWUpdate, bu script'i images kopyalanmadan önce çalıştırır
# Aktif slotu oku, hedef slotu /tmp/target_slot'a yaz

ACTIVE=$(fw_printenv boot_slot 2>/dev/null | cut -d= -f2)
ACTIVE=${ACTIVE:-A}

if [ "$ACTIVE" = "A" ]; then
    TARGET_SLOT="B"
    TARGET_DEV="/dev/mmcblk0p3"
else
    TARGET_SLOT="A"
    TARGET_DEV="/dev/mmcblk0p2"
fi

echo "$TARGET_SLOT" > /tmp/swupdate_target_slot
echo "$TARGET_DEV"  > /tmp/swupdate_target_dev

echo "[preinstall] Aktif: $ACTIVE, Hedef: $TARGET_SLOT ($TARGET_DEV)"

Dinamik device: Lua script ile

sw-description — Lua handler
software =
{
    version = "1.2.0";
    hardware-compatibility = ["1.0"];

    hardware: {
        revision = "myboard-1.0";
    };

    images: (
        {
            filename   = "rootfs.squashfs";
            type       = "raw";
            lua_handler = "get_target_device";  /* Lua ile device seç */
            sha256     = "...";
        }
    );

    bootenv: (
        { name = "upgrade_available"; value = "1"; },
        { name = "bootcount";         value = "0"; }
    );
};

İmzalı .swu oluşturma

bash — RSA imzalama
# Private key ve self-signed cert oluştur
openssl req -x509 -newkey rsa:4096 -keyout private.pem \
    -out public.pem -days 3650 -nodes \
    -subj "/CN=SWUpdate Signing Key"

# sw-description'ı imzala (CMS / PKCS#7)
openssl cms -sign -in sw-description \
    -out sw-description.sig \
    -signer public.pem \
    -inkey private.pem \
    -outform DER \
    -nosmimecap -binary

# İmzalı .swu paketle
echo sw-description sw-description.sig rootfs.squashfs \
    | tr ' ' '\n' | cpio -ovH newc > firmware-signed.swu

# SWUpdate ile imzalı paketi uygula
swupdate -k public.pem -i firmware-signed.swu

Web arayüzü ile güncelleme

bash
# Web server ile başlat (mongoose embedded)
swupdate -w "-document_root /var/www/swupdate -port 8080"

# Tarayıcıdan: http://device-ip:8080
# .swu dosyasını sürükle-bırak ile yükle

# Veya curl ile gönder
curl -F "swupdate=@firmware.swu" \
     http://192.168.1.100:8080/upload
NOT

SWUpdate, güncelleme sırasında sistem sinyallerini yakalar. SIGINT veya SIGTERM alırsa güncellemeyi tamamlamadan çıkmaz — mevcut image yazma işlemi tamamlanır. Bu, kısmi yazmanın önüne geçmez; atomik güncelleme garantisi yalnızca A/B slotlarla sağlanır.

Bu bölümde

  • preinstall.sh: aktif slot tespiti ve hedef device belirleme
  • RSA/CMS imzalama: openssl cms -sign ile sw-description imzası
  • swupdate -k public.pem -i signed.swu ile imzalı güncelleme
  • Web arayüzü: mongoose ile tarayıcıdan güncelleme
  • curl -F swupdate=@firmware.swu ile programatik güncelleme

05 Mender temelleri

Mender, merkezi yönetim sunucusu, signed artifact formatı ve tam A/B güncelleme desteğiyle kurumsal ölçekli IoT filolarında tercih edilen OTA platformudur.

Mender mimarisi

  Mender Server (SaaS / self-hosted)
  ├── Device Management API
  ├── Deployment Service
  └── Artifact Repository
            │ HTTPS polling (30s-24h)
            ▼
  Mender Client (C++ daemon, cihazda)
  ├── Inventory
  ├── Update Manager
  └── State Machine
            │
            ▼
  Rootfs (A/B swap via mender-grub / U-Boot)

Kurulum

bash
# Mender client kurulumu (binary)
wget https://downloads.mender.io/mender-client/latest/linux/arm/mender
chmod +x mender
mv mender /usr/bin/

# mender-artifact CLI kurulumu (host makinede)
wget https://downloads.mender.io/mender-artifact/latest/linux/mender-artifact
chmod +x mender-artifact
mv mender-artifact /usr/local/bin/

Mender konfigürasyonu

/etc/mender/mender.conf
{
    "ServerURL": "https://hosted.mender.io",
    "TenantToken": "eyJhbGciOi...",
    "DeviceTypeFile": "/var/lib/mender/device_type",
    "UpdatePollIntervalSeconds": 1800,
    "InventoryPollIntervalSeconds": 28800,
    "RetryPollIntervalSeconds": 300,
    "RootfsPartA": "/dev/mmcblk0p2",
    "RootfsPartB": "/dev/mmcblk0p3",
    "BootUtilitiesGetNextActivePart": "mender-grubenv-get-next-active",
    "BootUtilitiesSetActivePart": "mender-grubenv-set-active"
}

Device type dosyası

/var/lib/mender/device_type
device_type=raspberrypi4

Mender state machine

  Idle → Checking → Downloading → Installing → Rebooting
                                                    │
                            ┌────────────────────────┘
                            ▼
                     ArtifactVerifyReboot
                            │
                  OK ───────┴─────── FAIL
                  │                    │
              ArtifactCommit      ArtifactRollback
                  │                    │
               Idle              Rebooting (old slot)

Bu bölümde

  • Mender mimarisi: server + client + artifact repository
  • /etc/mender/mender.conf: server URL, tenant token, partition config
  • Device type: artifact uyumluluğu için cihaz kimliği
  • State machine: Idle → Check → Download → Install → Reboot → Commit/Rollback

06 Mender artifact oluşturma

mender-artifact CLI aracı ile rootfs image'ını signed .mender dosyasına dönüştürebilir, artifact bağımlılıkları ve delta güncelleme tanımlayabilirsiniz.

Temel artifact oluşturma

bash
# Basit rootfs artifact
mender-artifact write rootfs-image \
    --file          rootfs.ext4 \
    --device-type  raspberrypi4 \
    --artifact-name firmware-v1.2.0 \
    --output       firmware-v1.2.0.mender

# Birden fazla device type
mender-artifact write rootfs-image \
    --file          rootfs.ext4 \
    --device-type  raspberrypi4 \
    --device-type  raspberrypi4-64 \
    --artifact-name firmware-v1.2.0 \
    --output       firmware-v1.2.0.mender

İmzalı artifact

bash — RSA imzalama
# RSA private key oluştur
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
    -out mender-private.pem

# Public key export
openssl rsa -in mender-private.pem -pubout -out mender-public.pem

# İmzalı artifact oluştur
mender-artifact write rootfs-image \
    --file          rootfs.ext4 \
    --device-type  raspberrypi4 \
    --artifact-name firmware-v1.2.0 \
    --signing-key  mender-private.pem \
    --output       firmware-v1.2.0-signed.mender

# Artifact bilgilerini görüntüle
mender-artifact read firmware-v1.2.0-signed.mender

Update module ile özel artifact

bash — file artifact
# Tek dosya güncellemesi için file artifact
mender-artifact write module-image \
    --type         myapp-config \
    --file         /tmp/myapp.conf \
    --device-type  raspberrypi4 \
    --artifact-name config-v2.0 \
    --output       config-update.mender

# Artifact bağımlılığı: bu artifact yüklenmeden önce rootfs v1.2.0 gerekli
mender-artifact write module-image \
    --type         myapp-config \
    --file         /tmp/myapp.conf \
    --device-type  raspberrypi4 \
    --artifact-name config-v2.0 \
    --depends      rootfs-image.version:firmware-v1.2.0 \
    --output       config-update.mender

Delta güncelleme (mender-binary-delta)

bash
# Delta artifact oluştur (v1.1 → v1.2)
mender-binary-delta \
    --source  rootfs-v1.1.ext4 \
    --dest    rootfs-v1.2.ext4 \
    --output  delta-v1.1-to-v1.2.mender \
    --device-type raspberrypi4 \
    --artifact-name delta-v1.1-to-v1.2

# Delta artifact genellikle %70-90 daha küçük olur
# 200 MB rootfs için 15-30 MB delta tipik

Bu bölümde

  • mender-artifact write rootfs-image: temel artifact oluşturma
  • --signing-key mender-private.pem: RSA imzalama
  • module-image: özel artifact tipi ve bağımlılık tanımı
  • mender-binary-delta: delta artifact (yüzde 70-90 boyut azalması)

07 Mender server entegrasyonu

Mender hosted veya self-hosted sunucusu, artifact yönetimi, cihaz kimlik doğrulaması ve phased rollout için REST API ve web UI sunar.

Hosted Mender vs self-hosted

SeçenekAvantajDezavantaj
hosted.mender.ioHızlı başlangıç, yönetim yokÜcretli (100+ cihaz), dışa bağımlılık
Self-hosted (Docker)Tam kontrol, maliyet optimizasyonuAltyapı yönetimi gerekli
Self-hosted (Kubernetes)Yüksek ölçeklenebilirlikDevOps uzmanlığı gerekli

Self-hosted kurulumu

bash
# Mender server (Docker Compose)
git clone https://github.com/mendersoftware/mender-server-enterprise.git
cd mender-server-enterprise

# Demo ortam için (production için özelleştir)
./run --demo

# mender-cli kurulumu (host makinede)
wget https://downloads.mender.io/mender-cli/latest/linux/mender-cli
chmod +x mender-cli
mv mender-cli /usr/local/bin/

# Server'a giriş
mender-cli login \
    --server https://mender.mycompany.com \
    --username admin@example.com

Artifact yükleme ve deployment

bash — mender-cli
# Artifact yükle
mender-cli artifacts upload firmware-v1.2.0-signed.mender

# Yüklü artifact'ları listele
mender-cli artifacts list

# Belirli cihazlara deployment oluştur
mender-cli deployments create \
    --name         "Production v1.2.0" \
    --artifact     firmware-v1.2.0-signed \
    --devices      all-production

# Deployment durumu izle
mender-cli deployments list

REST API ile CI/CD entegrasyonu

ci-deploy.sh (GitLab CI / GitHub Actions)
#!/bin/bash
# CI pipeline: artifact yükle + deployment tetikle

MENDER_SERVER="${MENDER_SERVER_URL}"
MENDER_TOKEN="${MENDER_ACCESS_TOKEN}"
ARTIFACT_NAME="firmware-${CI_COMMIT_TAG}"
ARTIFACT_FILE="output/${ARTIFACT_NAME}.mender"

# 1. Artifact yükle
curl -s -X POST \
    -H "Authorization: Bearer ${MENDER_TOKEN}" \
    -F "artifact=@${ARTIFACT_FILE}" \
    "${MENDER_SERVER}/api/management/v1/deployments/artifacts" || exit 1

# 2. Phased deployment oluştur (önce %10)
curl -s -X POST \
    -H "Authorization: Bearer ${MENDER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d "{
        \"name\": \"${ARTIFACT_NAME}-phased\",
        \"artifact_name\": \"${ARTIFACT_NAME}\",
        \"filter\": {\"id\": \"all\"},
        \"phases\": [
            {\"batch_size\": 10, \"delay_seconds\": 7200},
            {\"batch_size\": 50, \"delay_seconds\": 7200},
            {\"batch_size\": 100}
        ]
    }" \
    "${MENDER_SERVER}/api/management/v2/deployments/deployments"

Bu bölümde

  • hosted.mender.io vs self-hosted Docker karşılaştırması
  • mender-cli artifacts upload ve deployment oluşturma
  • REST API ile CI/CD entegrasyonu: artifact push + deployment tetikleme
  • Phased rollout: yüzde 10 → 50 → 100 aşamalı deployment

08 RAUC (Robust Auto-Update Controller)

RAUC, systemd proje ailesinden gelen D-Bus arayüzlü güncelleme kontrolcüsüdür; GRUB, U-Boot ve Barebox bootloader'larıyla sorunsuz entegre olur.

RAUC mimarisi

  .raucb bundle (SquashFS + manifest + imza)
          │
          ▼
  rauc install bundle.raucb
          │
  [verify bundle signature]
          │
  [parse manifest.raucm]
          │
  [write slots via handlers]
          │
  [update bootloader state via D-Bus]
          │
  [reboot → bootloader marks slot active]

Sistem konfigürasyonu

/etc/rauc/system.conf
[system]
compatible=myboard-v1
bootloader=uboot
bundle-formats=verity

[keyring]
path=/etc/rauc/keyring.pem

[slot.rootfs.0]
device=/dev/mmcblk0p2
type=raw
bootname=A

[slot.rootfs.1]
device=/dev/mmcblk0p3
type=raw
bootname=B

[slot.boot.0]
device=/dev/mmcblk0p1
type=vfat
parent=rootfs.0

[slot.data.0]
device=/dev/mmcblk0p4
type=ext4
parent=rootfs.0
allow-mounted=true

RAUC bundle manifest

manifest.raucm
[update]
compatible=myboard-v1
version=2.0.0
description=MyBoard production firmware 2.0.0

[bundle]
format=verity

[image.rootfs]
filename=rootfs.squashfs
sha256=abcdef1234567890...
size=52428800

[image.boot]
filename=boot.vfat
sha256=0987654321fedcba...
size=67108864

Bundle oluşturma ve yükleme

bash
# Bundle oluştur
rauc bundle \
    --cert signing.pem \
    --key  signing.key \
    ./bundle-dir/ \
    firmware-v2.0.raucb

# Bundle bilgisi
rauc info firmware-v2.0.raucb

# Yükle
rauc install firmware-v2.0.raucb

# Durum
rauc status

D-Bus API ile programatik güncelleme

bash — busctl ile RAUC D-Bus
# RAUC D-Bus interface
busctl introspect de.pengutronix.rauc / \
    de.pengutronix.rauc.Installer

# Güncelleme başlat
busctl call de.pengutronix.rauc / \
    de.pengutronix.rauc.Installer \
    Install "s" /path/to/firmware-v2.0.raucb

# Durum sorgula
busctl call de.pengutronix.rauc / \
    de.pengutronix.rauc.Installer \
    GetSlotStatus

# Progress sinyalini dinle
busctl monitor de.pengutronix.rauc

RAUC, SWUpdate ve Mender karşılaştırması

ÖzellikSWUpdateMenderRAUC
D-Bus APIHayırHayırEvet
Bundle formatCPIO .swutar .menderSquashFS .raucb
Server entegrasyonuHawkBitMender serverHawkBit, özel
Yocto layermeta-swupdatemeta-mendermeta-rauc
Delta güncellemezchunk (deneysel)binary-deltaYok (roadmap)

Bu bölümde

  • RAUC: systemd ailesi, D-Bus interface, SquashFS bundle
  • /etc/rauc/system.conf: slot tanımları ve bootloader konfigürasyonu
  • manifest.raucm: bundle içerik tarifi
  • busctl call de.pengutronix.rauc: D-Bus üzerinden programatik güncelleme
  • SWUpdate / Mender / RAUC karşılaştırma tablosu

09 Pratik: Raspberry Pi OTA pipeline

Raspberry Pi 4, Yocto, SWUpdate ve A/B eMMC ile gerçek bir OTA pipeline kuruyoruz; CI'dan cihaza tam otomasyonlu güncelleme akışı.

Hedef mimari

  GitLab CI
  ├── Yocto build (core-image-minimal + meta-swupdate)
  ├── .swu artifact imzala
  └── S3 bucket'a yükle
            │ HTTPS
            ▼
  Cihaz (Raspberry Pi 4)
  ├── swupdate-suricatta → S3 polling (her 6 saatte)
  ├── İndir + doğrula + yaz (Slot B)
  ├── fw_setenv boot_slot=B upgrade_available=1
  └── reboot → health check → onayla

Yocto konfigürasyonu

conf/local.conf
MACHINE = "raspberrypi4-64"
DISTRO = "poky"

# SWUpdate katmanı
# meta-swupdate: https://github.com/sbabic/meta-swupdate

IMAGE_INSTALL:append = " \
    swupdate \
    swupdate-www \
    u-boot-fw-utils \
    health-check \
"

# SWUpdate suricatta (HawkBit veya özel) için
SWUPDATE_SURICATTA_CONFIG = "file:///etc/swupdate/suricatta.cfg"

# A/B için iki rootfs image
IMAGE_FSTYPES = "squashfs-xz ext4.gz"

GitLab CI pipeline

.gitlab-ci.yml
stages:
  - build
  - sign
  - deploy

build-image:
  stage: build
  script:
    - cd yocto
    - source poky/oe-init-build-env build
    - bitbake rpi4-ota-image
    - cp tmp/deploy/images/raspberrypi4-64/*.squashfs-xz ../artifacts/
  artifacts:
    paths:
      - artifacts/

sign-swu:
  stage: sign
  script:
    - SHA=$(sha256sum artifacts/rootfs.squashfs-xz | cut -d' ' -f1)
    - sed "s/ROOTFS_SHA256/$SHA/g" sw-description.tmpl > sw-description
    - openssl cms -sign -in sw-description -out sw-description.sig
          -signer $SIGNING_CERT -inkey $SIGNING_KEY
          -outform DER -nosmimecap -binary
    - echo "sw-description sw-description.sig artifacts/rootfs.squashfs-xz"
          | tr ' ' '\n' | cpio -ovH newc > firmware-${CI_COMMIT_TAG}.swu
    - aws s3 cp firmware-${CI_COMMIT_TAG}.swu
          s3://my-ota-bucket/releases/
  only:
    - tags

Cihaz polling script

/usr/bin/ota-poll.sh
#!/bin/sh
# S3'ten güncel firmware kontrolü ve indirme

BUCKET_URL="https://my-ota-bucket.s3.amazonaws.com/releases"
DEVICE_TYPE="raspberrypi4"
CURRENT_VER=$(cat /etc/firmware-version 2>/dev/null || echo "0.0.0")

# Güncel versiyon dosyasını kontrol et
LATEST=$(curl -sf "${BUCKET_URL}/latest-${DEVICE_TYPE}.txt")
[ -z "$LATEST" ] && exit 0
[ "$LATEST" = "$CURRENT_VER" ] && exit 0

echo "Güncelleme mevcut: $CURRENT_VER → $LATEST"

# İndir
curl -f -o /data/tmp/firmware.swu \
    "${BUCKET_URL}/firmware-${LATEST}.swu" || exit 1

# Uygula
swupdate -k /etc/swupdate/public.pem \
         -i /data/tmp/firmware.swu
RC=$?

rm -f /data/tmp/firmware.swu

if [ $RC -eq 0 ]; then
    echo "Güncelleme hazır, yeniden başlatılıyor..."
    reboot
else
    echo "Güncelleme başarısız: $RC"
fi

Health check servisi

/usr/bin/health-check.sh
#!/bin/sh
UPGRADE=$(fw_printenv upgrade_available 2>/dev/null | cut -d= -f2)
[ "$UPGRADE" != "1" ] && exit 0

echo "Post-update health check başlıyor..."

# 1. Kritik servisler
for svc in networking myapp ssh; do
    if ! systemctl is-active --quiet "$svc"; then
        echo "FAIL: $svc çalışmıyor"
        fw_setenv upgrade_available 0
        fw_setenv boot_slot A
        reboot; exit 1
    fi
done

# 2. Ağ bağlantısı
if ! ping -c 1 -W 5 8.8.8.8 >/dev/null 2>&1; then
    echo "FAIL: Ağ yok"
    fw_setenv upgrade_available 0
    fw_setenv boot_slot A
    reboot; exit 1
fi

# Tüm kontroller geçti
fw_setenv upgrade_available 0
fw_setenv bootcount 0
echo "Güncelleme onaylandı."
/etc/systemd/system/health-check.service
[Unit]
Description=OTA Health Check
After=network-online.target myapp.service
Wants=network-online.target

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 30
ExecStart=/usr/bin/health-check.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
NOT

Rollback senaryosunda bootcount limit aşıldığında U-Boot otomatik olarak eski slota döner. Ancak health-check.service'in başarısız olduğu durumları da izlemek gerekir. systemd'nin OnFailure= directive'i ile başarısızlık durumunda ek bir bildirim veya rollback tetikleyicisi kurulabilir.

Bu bölümde

  • Hedef mimari: GitLab CI → Yocto build → S3 → SWUpdate polling
  • GitLab CI: imzalı .swu oluşturma ve S3 upload
  • Cihaz polling: S3'ten versiyon kontrolü ve indirme
  • Health check: kritik servis + ağ kontrolü + otomatik rollback
  • systemd service: ExecStartPre=/bin/sleep 30 ile geciktirilmiş kontrol