00 balenaOS mimarisi — immutable OS, Supervisor, Docker Compose
balenaOS, edge cihazlar için özel olarak tasarlanmış, immutable (değişmez) bir Linux dağıtımıdır. Uygulama katmanı Docker container'larında çalışır; OS katmanı OTA ile atomik olarak güncellenir.
Katmanlı mimari
┌──────────────────────────────────────────────────────┐
│ Kullanıcı Uygulamaları (Docker Compose servisleri) │
│ sensor-collector mqtt-broker influxdb grafana │
├──────────────────────────────────────────────────────┤
│ balena Supervisor (Go daemon) │
│ · OTA uygulama güncellemesi │
│ · Device state raporlama │
│ · Sağlık kontrolü ve otomatik restart │
├──────────────────────────────────────────────────────┤
│ balenaEngine (Docker API uyumlu container runtime) │
├──────────────────────────────────────────────────────┤
│ balenaOS (Yocto tabanlı, immutable rootfs) │
│ · Read-only root partition │
│ · A/B partition tabanlı OS güncelleme │
│ · Minimal attack surface │
└──────────────────────────────────────────────────────┘
balena ekosistemi bileşenleri
| Bileşen | Görev |
|---|---|
| balenaCloud | Fleet yönetim platformu — web dashboard, API, OTA orchestration |
| balenaOS | Yocto tabanlı immutable Linux OS |
| balena Supervisor | Cihaz üstü daemon; OTA, sağlık, state sync |
| balenaEngine | Docker uyumlu container runtime |
| balena CLI | Geliştirici araç seti: push, logs, ssh, local dev |
| openbalena | Self-hosted balenaCloud alternatifi |
Bu bölümde
- balenaOS = immutable Yocto OS + A/B partition + balena Supervisor
- Uygulama Docker Compose ile tanımlanır; Supervisor OTA ile günceller
- RPi4, CM4, balenaFin dahil 100+ donanım resmi destek alır
01 balenaCloud hesap ve fleet oluşturma
balenaCloud'da fleet (cihaz grubu) oluşturma, OS image indirme ve cihazı kaydetme adımları.
Fleet oluşturma adımları
# balena CLI kurulumu (npm)
npm install -g balena-cli
# veya binary indirme
curl -fsSL https://github.com/balena-io/balena-cli/releases/latest/download/balena-cli-v18.0.0-linux-x64-standalone.zip \
| unzip - -d ~/.local/bin/
# balenaCloud hesabına giriş
balena login
# ? How would you like to login?
# > Web authorization (recommended)
# Fleet listele
balena fleets
# Fleet oluştur
balena fleet create sensor-platform \
--type raspberrypi4-64
# Cihazları listele
balena devices --fleet sensor-platform
# Cihaz detayları
balena device <device-uuid>
Bu bölümde
- Fleet oluştur → OS indir → balenaEtcher ile yaz → cihaz otomatik kaydolur
- Development mod: SSH açık; Production mod: SSH kapalı, güvenli
- balena CLI ile fleet ve cihaz yönetimi terminal üzerinden yapılır
02 balena CLI — push, logs, ssh
balena CLI'nin en çok kullanılan üç komutu: balena push (uygulama deploy), balena logs (container log izleme) ve balena ssh (uzak terminal).
balena push — uygulama deploy
# Proje dizinine git
cd ~/projects/sensor-platform
# Tüm fleet'e push et
balena push sensor-platform
# balena push adımları:
# 1. docker-compose.yml dosyasını okur
# 2. Her servis için Dockerfile build eder (balena builder buluta yükler)
# 3. Multi-arch imaj oluşturur (ARM64 için aarch64 native)
# 4. Release oluşturur → fleet'teki tüm cihazlara deploy başlar
# Push çıktısı:
# [Build] [main] Step 1/8 : FROM python:3.11-slim
# [Build] [main] Successfully built abc123
# [Info] Release: 1234567 (commit: abc123def)
# [Success] Deploy successful
# Belirli cihaza push et
balena push <device-uuid> --source .
# Draft release oluştur (production'a gitmeden test)
balena push sensor-platform --draft
balena logs
# Tüm servislerin logunu izle
balena logs <device-uuid>
# Belirli servis logu
balena logs <device-uuid> --service sensor-collector
# Son 100 satır
balena logs <device-uuid> --tail 100
# Sistem logları (Supervisor dahil)
balena logs <device-uuid> --system
balena ssh
# Host OS'e SSH (development mod veya tunnel)
balena ssh <device-uuid>
# Belirli servis container'ına gir
balena ssh <device-uuid> --service sensor-collector
# Container içinde komut çalıştır
balena ssh <device-uuid> -s sensor-collector -- python3 -c "import sys; print(sys.version)"
# Lokal ağdaki cihaza direk SSH (local mode)
balena ssh <local-ip>
# root@<uuid>:~#
Bu bölümde
- balena push: bulutta build → release oluştur → tüm fleet'e OTA deploy
- balena logs: servis bazında log takibi, --system Supervisor loglarını kapsar
- balena ssh: host OS veya belirli container'a uzak terminal erişimi
03 docker-compose.yml ile multi-container app
balenaOS uygulamaları Docker Compose v3 formatında tanımlanır. Her servis ayrı bir container olarak çalışır; Supervisor bu compose dosyasını yorumlar.
Örnek multi-container compose
version: "2.4"
volumes:
influxdb-data:
grafana-data:
mosquitto-data:
services:
mqtt-broker:
build: ./mqtt-broker
restart: always
ports:
- "1883:1883"
- "9001:9001"
volumes:
- mosquitto-data:/mosquitto/data
- ./mqtt-broker/config:/mosquitto/config:ro
labels:
io.balena.features.supervisor-api: "1"
sensor-collector:
build: ./sensor-collector
restart: always
privileged: true
devices:
- "/dev/i2c-1:/dev/i2c-1"
- "/dev/gpiomem:/dev/gpiomem"
environment:
MQTT_HOST: mqtt-broker
MQTT_PORT: "1883"
POLL_INTERVAL: "10"
depends_on:
- mqtt-broker
labels:
io.balena.features.balena-socket: "1"
influxdb:
image: influxdb:2.7-alpine
restart: always
ports:
- "8086:8086"
volumes:
- influxdb-data:/var/lib/influxdb2
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: admin
grafana:
image: grafana/grafana:10.2.0
restart: always
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
depends_on:
- influxdb
Bu bölümde
- Docker Compose v2.4 formatı kullanılır (v3 özelliklerinin bir kısmı desteklenmez)
- io.balena.features.* label'ları ile Supervisor özellikleri etkinleştirilir
- GPIO/I2C için devices veya privileged; üretimde devices tercih edilmeli
04 Environment variable ve secret yönetimi
balenaCloud'da ortam değişkenleri fleet seviyesinde veya tekil cihaz seviyesinde tanımlanabilir. Hassas değerler için "balena Secret" özelliği kullanılır.
Fleet ve cihaz seviyesi değişkenler
# Fleet seviyesi değişken ekle (tüm cihazlara uygulanır)
balena env add MQTT_BROKER_URL \
mqtt://mqtt-broker:1883 \
--fleet sensor-platform
# Birden fazla servis için (belirli servise)
balena env add POLL_INTERVAL 30 \
--fleet sensor-platform \
--service sensor-collector
# Cihaz seviyesi değişken (yalnızca bu cihaz için)
balena env add DEVICE_LOCATION \
"factory-floor-A" \
--device <device-uuid>
# Tüm değişkenleri listele
balena envs --fleet sensor-platform
# Değişken güncelle
balena env rename <var-id> NEW_VALUE
# Değişken sil
balena env rm <var-id>
balena Secret (gizli değerler)
# Secret oluştur — dashboard'da değer şifreli saklanır
balena env add INFLUXDB_PASSWORD \
"s3cr3tP@ssw0rd" \
--fleet sensor-platform \
--secret
# API token secret'ı
balena env add GRAFANA_ADMIN_PASSWORD \
"gr4f4n4P4ss" \
--fleet sensor-platform \
--secret
# Secret değerler CLI çıktısında maskelenir
# balena envs → INFLUXDB_PASSWORD: ***
compose'da env var kullanımı
influxdb:
image: influxdb:2.7-alpine
environment:
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUXDB_PASSWORD}
DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_ORG:-myorg}
DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUXDB_BUCKET:-sensors}
Bu bölümde
- Fleet seviyesi değişkenler tüm cihazlara, cihaz seviyesi değişkenler tekil cihaza uygulanır
- --secret flag'i ile değer dashboard'da maskelenir, log'a düşmez
- compose'da ${VAR:-default} sözdizimi ile varsayılan değer tanımlanır
05 OTA güncelleme akışı — delta update
balenaCloud'un delta update sistemi, yalnızca değişen dosya bloklarını iletir. 100 MB imaj için sadece 2-5 MB veri aktarılabilir; düşük bant genişliği ve ölçülü veri planı olan edge cihazlar için kritiktir.
OTA güncelleme akışı
Geliştirici
balena push sensor-platform
│
▼
balena Builder (bulut)
· Dockerfile build (aarch64 native)
· Multi-layer imaj oluştur
· Delta hesapla (mevcut sürümle fark)
│
▼
balenaCloud Release (commit hash)
│
▼ (SuperVisor polling veya push event)
Cihazlar (10x RPi4)
· Delta parçaları indir (~MB, tam imaj değil ~100MB)
· containerd imaj katmanlarını güncelle
· Graceful restart: eski container dur → yeni başlar
· Başarısızsa: otomatik rollback
# Güncel release listele
balena releases sensor-platform
# Belirli release'i pinle (otomatik güncelleme durdur)
balena fleet pin sensor-platform <release-commit>
# Tekil cihaza release pinle
balena device pin <device-uuid> <release-commit>
# Pin kaldır (tekrar otomatik güncellemeye geç)
balena fleet track-latest sensor-platform
# Draft release'i final yap (staged rollout)
balena release finalize <release-commit>
# Cihaz güncelleme durumunu izle
balena devices --fleet sensor-platform \
--fields id,uuid,status,is_online,os_version,supervisor_version
Supervisor API ile programatik güncelleme
# Container içinden (io.balena.features.supervisor-api aktif)
# Uygulamayı güncelle
curl -X POST \
"http://$(BALENA_SUPERVISOR_ADDRESS)/v1/update" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"force": true}'
# Cihazı yeniden başlat
curl -X POST \
"http://$(BALENA_SUPERVISOR_ADDRESS)/v1/reboot" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY"
Bu bölümde
- Delta update: değişen bloklar iletilir; bant genişliği %90+ tasarruf
- Release pin ile belirli sürüm dondurulur; staged rollout desteklenir
- Supervisor API ile container içinden güncelleme ve reboot tetiklenir
06 Device health monitoring — dashboard ve metrikler
balenaCloud dashboard'u, cihaz sağlığını anlık izleme, CPU/RAM/disk metrikleri ve bağlantı durumu görünürlüğü sağlar.
Dashboard metrikleri
Supervisor API ile metrik okuma
# Cihaz durumu (container içinden)
curl "http://$BALENA_SUPERVISOR_ADDRESS/v2/device/name" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY"
# {"status":"success","deviceName":"floral-dew"}
# Çalışan servisler
curl "http://$BALENA_SUPERVISOR_ADDRESS/v2/containerId" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY"
# Uygulama state
curl "http://$BALENA_SUPERVISOR_ADDRESS/v2/applications/state" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY"
# Host OS metrik
curl "http://$BALENA_SUPERVISOR_ADDRESS/v1/device" \
-H "Authorization: Bearer $BALENA_SUPERVISOR_API_KEY"
# {"api_port":48484,"ip_address":"192.168.1.50","mac_address":"dc:a6:32:xx:xx:xx",
# "memory_usage":412,"memory_total":3904,"storage_usage":2048,"storage_total":29000,
# "cpu_usage":12,"cpu_temp":47}
Supervisor metriklerini kendi Prometheus/Grafana stack'inize aktarmak için BALENA_SUPERVISOR_ADDRESS ve BALENA_SUPERVISOR_API_KEY ortam değişkenlerini kullanan bir exporter servisi yazabilirsiniz. Bu metrikleri Fleet'teki tüm cihazlardan merkezde toplamak için Prometheus remote_write kullanın.
Bu bölümde
- Dashboard: CPU, RAM, disk, ağ durumu, OS/Supervisor versiyonu
- Supervisor API /v1/device ile programatik metrik okuma
- Metrikler kendi Prometheus/Grafana stack'ine aktarılabilir
07 balena local mode — yerel geliştirme
Local mode, balenaCloud'a push etmeden aynı ağdaki cihaza doğrudan kod deploy etmeyi sağlar. Geliştirme döngüsünü saniyeye indirir.
Local mode aktifleştirme
# Dashboard'dan veya CLI ile local mode aç
balena device local-mode enable <device-uuid>
# Aynı ağdaki cihazları tara
balena scan
# - 192.168.1.50 floral-dew.local
# - 192.168.1.51 crimson-leaf.local
# Lokal cihaza push et (bulut build olmadan)
balena push floral-dew.local
# veya IP ile
balena push 192.168.1.50
# Lokal SSH
balena ssh floral-dew.local
# Servis container'ına gir
balena ssh floral-dew.local -s sensor-collector
Live update (sıcak kod güncellemesi)
# Live update: dosya değişikliği algılanır, container yeniden başlatılmadan güncellenir
balena push floral-dew.local --live
# Çıktı:
# [Live] Watching for file changes...
# [Live] File changed: src/sensor.py
# [Live] Syncing to sensor-collector...
# [Live] Sync complete in 1.2s
# .balenaignore ile gereksiz dosyaları hariç tut
cat .balenaignore
# __pycache__/
# *.pyc
# .git/
# node_modules/
# tests/
Bu bölümde
- Local mode: buluta push gerekmez, aynı ağdaki cihaza doğrudan deploy
- balena scan ile ağdaki balenaOS cihazları keşfedilir
- --live flag'i ile dosya değişikliği anında cihaza sync edilir
08 Pratik — MQTT + InfluxDB + Grafana 10 cihaza deploy
10 adet Raspberry Pi 4'ten oluşan bir sensör izleme fleet'ine balenaCloud üzerinden MQTT + InfluxDB + Grafana stack'i deploy ediyoruz.
Proje yapısı
sensor-platform/
├── docker-compose.yml
├── mqtt-broker/
│ ├── Dockerfile
│ └── config/mosquitto.conf
├── sensor-collector/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── src/collector.py
├── influxdb/
│ └── setup.sh
└── grafana/
└── provisioning/
├── datasources/influxdb.yaml
└── dashboards/sensors.json
sensor-collector Dockerfile
FROM balenalib/raspberrypi4-64-python:3.11-bookworm-run
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ ./src/
CMD ["python3", "src/collector.py"]
import os, time, json, smbus2
import paho.mqtt.client as mqtt
MQTT_HOST = os.environ.get("MQTT_HOST", "mqtt-broker")
MQTT_PORT = int(os.environ.get("MQTT_PORT", 1883))
DEVICE_UUID = os.environ.get("BALENA_DEVICE_UUID", "unknown")
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", 10))
client = mqtt.Client(client_id=f"collector-{DEVICE_UUID}")
client.connect(MQTT_HOST, MQTT_PORT)
client.loop_start()
def read_sensor():
# BME280 I2C sensör okuma (gerçek uygulamada)
import random
return {
"device_uuid": DEVICE_UUID,
"temperature": 23.0 + random.uniform(-1, 1),
"humidity": 55.0 + random.uniform(-5, 5),
"pressure": 1013.0 + random.uniform(-2, 2),
"timestamp": time.time()
}
while True:
data = read_sensor()
topic = f"sensors/{DEVICE_UUID}/environment"
client.publish(topic, json.dumps(data), qos=1)
print(f"Published: {data}")
time.sleep(POLL_INTERVAL)
Fleet deploy ve izleme
# Fleet değişkenlerini ayarla
balena env add INFLUXDB_ORG myorg --fleet sensor-platform
balena env add INFLUXDB_BUCKET sensors --fleet sensor-platform
balena env add INFLUXDB_PASSWORD "s3cr3t" --fleet sensor-platform --secret
balena env add GRAFANA_ADMIN_PASSWORD "gr4f4n4" --fleet sensor-platform --secret
# Tüm fleet'e push et
balena push sensor-platform
# 10 cihazın güncelleme durumunu izle
watch -n 5 "balena devices --fleet sensor-platform | grep -E 'UUID|STATUS'"
# MQTT mesajlarını test cihazından izle
balena ssh <device-uuid> -s mqtt-broker -- \
mosquitto_sub -h localhost -t "sensors/#" -v
# InfluxDB'de veri kontrolü
balena ssh <device-uuid> -s influxdb -- \
influx query 'from(bucket:"sensors") |> range(start: -1h) |> limit(n:5)'
10 cihaz için balenaCloud ücretsiz planı yeterlidir. Daha büyük fleet'lerde (100+ cihaz) openbalena self-hosted kurulumu maliyet optimizasyonu sağlar. openbalena, tam balenaCloud stack'ini kendi altyapınızda çalıştırmanıza olanak verir.
Bu bölümde
- MQTT → InfluxDB → Grafana stack balenaOS üzerinde Compose ile çalışır
- BALENA_DEVICE_UUID env değişkeni her cihazda unique topic oluşturmak için kullanıldı
- balena push ile tüm 10 cihaza delta OTA güncelleme tek komutla yapılır