왜 SWUpdate?
현장 업데이트는 불가피합니다. SWUpdate 는 펌웨어, 루트 파일 시스템, 애플리케이션 레이어를 업데이트할 수 있는 강력한 모듈식 솔루션을 제공하며, 모두 롤백 기능이 내장되어 있습니다.
오픈 소스이며 문서화가 잘 되어 있고 A/B 파티션 레이아웃과 원활하게 통합됩니다.
아키텍처 개요
SWUpdate 은 몇 가지 주요 구성 요소로 이루어져 있습니다:
- 업데이트 데몬(swupdate) - 장치에서 실행되어 업데이트를 적용합니다.
- 업데이트 핸들러 - 업데이트할 대상(rootfs, 파일, 스크립트 등)을 정의합니다.
- 클라이언트 인터페이스 - 웹 인터페이스, REST API 또는 로컬 CLI
- sw-description 파일 - 업데이트 패키지의 구조와 로직을 정의합니다.
업데이트 흐름 예시
- 다음이 포함된 업데이트 패키지(.swu)를 빌드합니다:
- 새 루트 파일시스템 이미지
- 새 루트 파일시스템 이미지 sw-description 파일
- 선택적 스크립트(사용자 지정 또는 확인용)
2.swupdate 정의된 장치에 업데이트를 씁니다.
- 업데이트 후 스크립트가 성공 여부를 확인하거나 필요한 경우 롤백을 트리거합니다.
예
이 예제에서는 공식 Raspberry Pi OS 트릭시 이미지를 두 개의 파일로 분할했습니다:
- 2025-10-01-raspios-trixie-arm64.boot.vfat
- 2025-10-01-raspios-trixie-arm64.root.ext4
이 파일은 sw-description 파일에서 참조하여 .swu 패키지를 생성합니다:
software =
{
version = "0.1.0";
description = "Firmware update for XXXXX Project";
hardware-compatibility: [ "1.0", "1.2", "1.3"];
images: (
{
filename = "2025-10-01-raspios-trixie-arm64.boot.vfat";
device = "/dev/mmcblk0p1";
compressed = "zlib";
installed-directly = true;
},
{
filename = "2025-10-01-raspios-trixie-arm64.root.ext4";
device = "/dev/mmcblk0p2";
compressed = "zlib";
installed-directly = true;
}
);
scripts: (
{
type: "lua",
filename: "repair-disk-uuid.lua"
}
),
}다음 Lua 스크립트는 파티션 UUID를 조정합니다. cmdline.txt 와 fstab 를 플래시합니다:
#!/usr/bin/lua
-- helper: run shell command and capture output
function run(cmd)
local f = io.popen(cmd)
local out = f:read("*a")
f:close()
return (out:gsub("%s+$", ""))
end
-- detect PARTUUIDs
local root_part = "/dev/mmcblk0p2"
local boot_part = "/dev/mmcblk0p1"
local root_uuid = run("blkid -s PARTUUID -o value " .. root_part)
local boot_uuid = run("blkid -s PARTUUID -o value " .. boot_part)
print("Rootfs PARTUUID: " .. root_uuid)
print("Boot PARTUUID: " .. boot_uuid)
-- mount points
os.execute("mkdir -p /mnt/root /mnt/boot")
os.execute("mount " .. root_part .. " /mnt/root")
os.execute("mount " .. boot_part .. " /mnt/boot")
-- update cmdline.txt
local cmdline_path = "/mnt/boot/cmdline.txt"
local file = io.open(cmdline_path, "r")
local text = file:read("*a")
file:close()
text = text:gsub("root=PARTUUID=[^ ]+", "root=PARTUUID=" .. root_uuid)
file = io.open(cmdline_path, "w")
file:write(text)
file:close()
-- update /etc/fstab
local fstab_path = "/mnt/root/etc/fstab"
local fstab = io.open(fstab_path, "r")
local content = fstab:read("*a")
fstab:close()
-- replace root line
content = content:gsub("PARTUUID=[^%s]+%s+/%s", "PARTUUID=" .. root_uuid .. " /")
-- replace boot line (/boot or /boot/firmware)
content = content:gsub("PARTUUID=[^%s]+%s+/boot", "PARTUUID=" .. boot_uuid .. " /boot")
fstab = io.open(fstab_path, "w")
fstab:write(content)
fstab:close()
os.execute("sync")
os.execute("umount /mnt/boot")
os.execute("umount /mnt/root")
print("All PARTUUIDs updated successfully.")사용 swugenerator 를 사용하여 업데이트:(https://github.com/sbabic/swugenerator).
업데이트 적용
생성된 .swu 파일을 테스트하려면 다음과 같이 하세요:
- CM5를 복구 시스템으로 부팅합니다.
- 마운트 지점을 생성합니다:
sudo mkdir -p /mnt/update- 업데이트 파일이 포함된 NFS 공유를 마운트합니다:
sudo mount -t nfs :/path/to/share /mnt/update- 업데이트를 적용합니다:
sudo swupdate -i /mnt/update/update.swu통합 가능성
로컬 UI 또는 백엔드 API에서 업데이트 트리거하기
- 보안 강화를 위한 업데이트 서명 및 확인
- 사용 SWUpdate’s 테스트 또는 디버깅을 위한 웹 인터페이스 사용
- systemd 서비스와 결합하여 복구 및 롤백 자동화
이 스택에 적합한 이유
와 함께 rpi-image-gen 및 rpi-sb-provisioner, SWUpdate 를 사용하면 그림이 완성됩니다:
- 빌드 → rpi-image-gen (이미지 생성)
- 배포 → rpi-sb-provisioner (디바이스 프로비저닝)
- 유지 관리 → → SWUpdate (OTA 업데이트 및 수명 주기 관리)
그 결과 유연하고 개방적이며 유지 관리가 가능한 임베디드 플랫폼이 탄생합니다. Linux 유연하고 개방적이며 유지 관리가 가능한 임베디드 플랫폼이 탄생했습니다. Yocto.
출처
- rpi-image-gen: https://github.com/raspberrypi/rpi-image-gen
- rpi-sb-provisioner: https://github.com/raspberrypi/rpi-sb-provisioner
- SWUpdate: https://github.com/sbabic/swupdate
- swugenerator: https://github.com/sbabic/swugenerator