Series: Lập trình Raspberry Pi & Ứng dụng thực tế Phần 3 — Python “build app thật” Bài 22: OTA “nhẹ” cho Raspberry Pi app — Git pull + version + rollback nhanh (production-friendly)
1) Mục tiêu bài học
Sau bài này bạn sẽ:
- Deploy cập nhật app trên Pi theo kiểu OTA nhẹ (không cần container).
- Cập nhật bằng git pull + restart service.
- Có endpoint /version để kiểm tra version đang chạy.
- Có cơ chế rollback nhanh nếu update lỗi.
- An toàn hơn: chỉ cho phép update từ LAN + có token.
Đây là kiểu OTA rất hợp cho “Pi gateway/app” khi bạn vận hành vài chục/ vài trăm Pi.
2) Quy ước version
Trong .env (hoặc systemd EnvironmentFile) thêm:
APP_VERSION=1.0.0
APP_GIT_SHA=dev
Bạn sẽ update APP_GIT_SHA tự động sau mỗi deploy.
3) Thêm endpoint /version (FastAPI)
Mở src/api.py, thêm:
import os
from fastapi import Header, HTTPException
Thêm endpoint:
@app.get("/version")
def version():
return {
"app": os.getenv("APP_NAME", "iotlabs-py-agent"),
"version": os.getenv("APP_VERSION", "unknown"),
"git_sha": os.getenv("APP_GIT_SHA", "unknown"),
}
Test:
curl -s http://127.0.0.1:8000/version
4) Tạo script deploy (OTA nhẹ)
Tạo file scripts/deploy.sh:
cd ~/apps/iotlabs-py-agent
nano scripts/deploy.sh
Dán:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/home/developer/apps/iotlabs-py-agent"
SERVICE_NAME="iotlabs-api"
cd "$APP_DIR"
echo "[1/6] Backup current revision"
TS="$(date +%Y%m%d-%H%M%S)"
mkdir -p backups
git rev-parse HEAD > "backups/rev_$TS.txt"
echo "[2/6] Fetch & reset to origin/main"
git fetch --all
git reset --hard origin/main
echo "[3/6] Update venv deps (if requirements changed)"
# Nếu bạn dùng requirements.txt:
if [ -f requirements.txt ]; then
"$APP_DIR/.venv/bin/pip" install -r requirements.txt
fi
echo "[4/6] Write git sha to .env (APP_GIT_SHA)"
SHA="$(git rev-parse --short HEAD)"
if grep -q "^APP_GIT_SHA=" .env; then
sed -i "s/^APP_GIT_SHA=.*/APP_GIT_SHA=$SHA/" .env
else
echo "APP_GIT_SHA=$SHA" >> .env
fi
echo "[5/6] Restart service"
sudo systemctl restart "$SERVICE_NAME"
echo "[6/6] Health check"
sleep 1
curl -fsS "http://127.0.0.1:8000/health" >/dev/null
echo "OK deployed sha=$SHA"
Cho phép chạy:
chmod +x scripts/deploy.sh
Chạy thử:
./scripts/deploy.sh
5) Rollback nhanh (khi update lỗi)
Tạo scripts/rollback.sh:
nano scripts/rollback.sh
Dán:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/home/developer/apps/iotlabs-py-agent"
SERVICE_NAME="iotlabs-api"
cd "$APP_DIR"
echo "Available backups:"
ls -1 backups | tail -n 10
echo -n "Enter backup file name (e.g., rev_20260218-210001.txt): "
read -r f
REV="$(cat "backups/$f")"
echo "Rollback to $REV"
git reset --hard "$REV"
SHA="$(git rev-parse --short HEAD)"
if grep -q "^APP_GIT_SHA=" .env; then
sed -i "s/^APP_GIT_SHA=.*/APP_GIT_SHA=$SHA/" .env
else
echo "APP_GIT_SHA=$SHA" >> .env
fi
sudo systemctl restart "$SERVICE_NAME"
sleep 1
curl -fsS "http://127.0.0.1:8000/health" >/dev/null
echo "OK rollback sha=$SHA"
Cho phép chạy:
chmod +x scripts/rollback.sh
6) Tạo “OTA API” (tuỳ chọn) — gọi update từ xa
Bạn có thể tạo endpoint /admin/deploy để trigger script deploy.sh.
⚠️ Lưu ý an toàn:
- Chỉ mở trong LAN
- Bắt buộc token
- Chạy dưới quyền hạn chế (không khuyến khích chạy sudo trực tiếp từ API)
Cách an toàn hơn (khuyến nghị)
- Không gọi deploy từ API
- Thay vào đó: deploy bằng SSH hoặc CI runner nội bộ.
Nếu vẫn muốn endpoint, bạn có thể làm “request → ghi flag file → scheduler job chạy deploy” (tránh chạy lệnh nguy hiểm trong HTTP handler). Bài sau mình sẽ làm kiểu đó.
7) Gợi ý: systemd EnvironmentFile
Trong /etc/systemd/system/iotlabs-api.service thêm:
EnvironmentFile=/home/developer/apps/iotlabs-py-agent/.env
Sau khi sửa:
sudo systemctl daemon-reload
sudo systemctl restart iotlabs-api
8) Checklist vận hành OTA
- /version trả đúng sha ✅
- Deploy xong chạy health check ✅
- Có backup revision để rollback ✅
- Update dependency có kiểm soát (requirements.txt) ✅
- Chỉ deploy từ người/CI tin cậy ✅


