임베디드 리눅스 빌드 시스템 (OpenWrt · Buildroot · Yocto)

임베디드 리눅스 개발의 핵심인 OpenWrt, Buildroot, Yocto/OpenEmbedded 3대 빌드 시스템을 종합 비교합니다. 각 시스템의 빌드 파이프라인(Pipeline), 패키지 인프라, 펌웨어(Firmware) 이미지 구조, 크로스 툴체인 전략, Device Tree 통합, BSP 개발 방법론, SDK 활용, 디버깅(Debugging) 워크플로, 실전 유스케이스까지 임베디드 빌드 시스템의 모든 것을 다룹니다.

전제 조건: LFS (크로스 컴파일(Cross Compilation))빌드 시스템, Device Tree 문서를 먼저 읽으세요. 임베디드 빌드 시스템은 크로스 컴파일러 구성, Makefile/Kconfig, 그리고 Device Tree 이해가 필수적입니다.
일상 비유: 임베디드 빌드 시스템은 가구 조립 키트와 비슷합니다. OpenWrt는 라우터 전문 조립 키트(부품이 미리 선별됨), Buildroot는 미니멀리즘 DIY 키트(필요한 것만 빠르게), Yocto는 산업용 자동화 생산 라인(복잡하지만 대량 생산에 최적)입니다.

핵심 요약

  • OpenWrt — 라우터/네트워크 장비에 특화된 임베디드 리눅스 배포판이자 빌드 시스템. opkg 패키지 매니저, LuCI 웹 인터페이스, SquashFS+JFFS2 오버레이(Overlay) 파일시스템(Filesystem)을 기본 제공합니다. 2004년 Linksys WRT54G에서 시작했습니다.
  • Buildroot — 간결함을 최우선으로 하는 임베디드 리눅스 빌드 프레임워크. Kconfig 기반 설정, Make 기반 빌드, 최소 4MB 이미지 생성이 가능합니다. 2001년 uClibc 프로젝트에서 시작했습니다.
  • Yocto/OpenEmbedded — Linux Foundation이 후원하는 엔터프라이즈급 빌드 프레임워크. BitBake 빌드 엔진, 레이어 아키텍처, sstate-cache 등 대규모 프로젝트에 적합합니다. 2010년에 시작했습니다.
  • 크로스 컴파일 — 호스트(x86_64 PC)에서 타겟(ARM, MIPS 등) 바이너리를 생성하는 기법. 임베디드 빌드 시스템의 핵심 기능입니다.
  • BSP (Board Support Package) — 특정 하드웨어 보드를 지원하기 위한 부트로더(Bootloader), 커널, Device Tree, 드라이버 구성 묶음입니다.

버전 네이밍 규칙

시스템네이밍 규칙예시LTS/릴리스 주기
OpenWrtYY.MM (년.월)23.05, 24.10약 12~18개월 주기, 포인트 릴리스(23.05.4)로 보안 패치(Patch)
BuildrootYYYY.MM (년.월)2024.02, 2024.05분기별 릴리스(2/5/8/11), LTS 없음(롤링 안정판)
Yocto버전 + 코드명5.0 scarthgap, 4.0 kirkstoneLTS 2년 지원(짝수 메이저), 6개월 주기

핵심 용어 정리

용어설명사용 시스템
Feed원격 패키지 저장소. feeds.conf.default에 URL을 정의하여 추가 패키지를 가져옴OpenWrt
Recipe (.bb)패키지 빌드 방법을 기술하는 메타데이터 파일. fetch/compile/install 태스크(Task)를 정의Yocto
Layer (meta-*)관련 recipe와 설정을 논리적으로 그룹화한 디렉토리. BSP, 배포판, 기능별로 분리Yocto
BSPBoard Support Package. 특정 하드웨어에 필요한 커널 설정, DTS, 부트로더, 드라이버 모음모두
Cross-compile호스트 아키텍처(x86_64)에서 타겟 아키텍처(ARM, MIPS 등) 바이너리를 생성하는 빌드 방식모두
Sysroot크로스 컴파일러가 타겟 시스템의 헤더와 라이브러리를 찾는 최상위 디렉토리모두
Staging빌드 과정에서 타겟용 라이브러리와 헤더가 설치되는 임시 디렉토리. 다른 패키지가 의존성 참조에 사용모두
defconfig최소 설정 파일. 기본값과 다른 옵션만 기록하며, make xxx_defconfig로 적용Buildroot/커널
BitBakeYocto의 태스크 실행 엔진. recipe를 파싱하고 의존성 그래프에 따라 태스크를 병렬 실행Yocto
opkgOpenWrt의 경량 패키지 매니저. IPK 형식 패키지를 설치/제거/업데이트OpenWrt
UCIUnified Configuration Interface. OpenWrt 고유의 설정 관리 시스템 (/etc/config/)OpenWrt
procdOpenWrt의 init/서비스 매니저. systemd 대비 경량이며 JSON 기반 서비스 정의OpenWrt

사용하지 말아야 할 경우 (anti-pattern)

선택 실수 방지:
  • OpenWrt를 쓰지 말아야 할 때: 라우터가 아닌 범용 임베디드 장비, GUI가 필요한 제품, systemd 기반 서비스가 많은 경우, 16MB 이상 플래시를 가진 고성능 장비
  • Buildroot를 쓰지 말아야 할 때: 현장에서 패키지를 설치/업데이트해야 하는 경우, 10명 이상 팀이 동시에 다른 컴포넌트를 개발하는 경우, 벤더가 Yocto BSP만 제공하는 경우
  • Yocto를 쓰지 말아야 할 때: 프로토타입을 빠르게 만들어야 할 때(학습 곡선 2~4주), 빌드 서버 디스크가 50GB 미만인 경우, 1~2명의 소규모 팀, 빠른 빌드 반복이 필수인 초기 개발 단계

단계별 이해

임베디드 리눅스 빌드를 처음 접하는 개발자를 위한 5단계 가이드입니다:

호스트 시스템 준비 (필수 패키지)

호스트 의존성: 빌드 시작 전 호스트 시스템에 필요한 패키지를 반드시 설치하세요. 누락 시 빌드 중간에 실패합니다.
# Ubuntu/Debian 공통 패키지 (3 시스템 모두 필요)
sudo apt-get install -y build-essential gcc g++ binutils patch gawk \
  bzip2 unzip wget curl git file python3 python3-distutils rsync \
  bc cpio libncurses-dev zlib1g-dev libssl-dev

# OpenWrt 추가 패키지
sudo apt-get install -y libelf-dev ecj fastjar java-propose-classpath \
  subversion mercurial swig gettext time xsltproc

# Buildroot 추가 패키지
sudo apt-get install -y libncurses5 cmake perl-modules

# Yocto 추가 패키지 (가장 많음)
sudo apt-get install -y gawk diffstat texinfo chrpath socat \
  lz4 zstd xterm debianutils iputils-ping python3-git \
  python3-jinja2 python3-pexpect python3-pip python3-subunit \
  mesa-common-dev locales

디스크 공간 요구사항

시스템최소 디스크권장 디스크다운로드 캐시(Cache)비고
OpenWrt10 GB20 GB+3~5 GBfeeds 포함 시 증가
Buildroot3 GB10 GB+1~3 GB가장 작음; O= 별도 출력 시 추가
Yocto50 GB100~200 GB5~15 GBsstate-cache가 대부분; SSD 필수
  1. 1단계: 왜 크로스 컴파일인가?

    임베디드 타겟(라우터, IoT 디바이스)은 CPU 성능과 메모리가 제한적이므로 직접 컴파일이 비현실적입니다. 강력한 x86_64 호스트 PC에서 ARM/MIPS용 바이너리를 생성하면 빌드 시간을 수십 배 단축할 수 있습니다. 이를 위해 크로스 툴체인(크로스 컴파일러 + 링커(Linker) + C 라이브러리)이 필요합니다.

    # 예: ARM용 크로스 컴파일러로 Hello World 빌드
    arm-linux-gnueabihf-gcc -o hello hello.c
    file hello
    # hello: ELF 32-bit LSB executable, ARM, ...
  2. 2단계: 빌드 시스템이 하는 일

    임베디드 빌드 시스템은 다음을 자동화합니다:

    • 크로스 툴체인 구축 — GCC, Binutils, C 라이브러리(musl/glibc) 빌드
    • 패키지 빌드 — 수백~수천 개 패키지를 올바른 순서로 크로스 컴파일
    • 루트 파일시스템 생성 — 타겟에 필요한 바이너리/라이브러리/설정 파일 조합
    • 커널 빌드 — 타겟 보드에 맞는 커널 설정과 Device Tree 적용
    • 펌웨어 이미지 생성 — 부트로더 + 커널 + 루트FS를 플래시 가능한 이미지로 패키징
  3. 3단계: OpenWrt 빠른 시작
    # OpenWrt 소스 클론 (23.05 안정 브랜치)
    git clone -b openwrt-23.05 https://github.com/openwrt/openwrt.git
    cd openwrt
    
    # 피드(추가 패키지 저장소) 업데이트 및 설치
    ./scripts/feeds update -a
    ./scripts/feeds install -a
    
    # 타겟/패키지 선택 (ncurses 메뉴)
    make menuconfig
    # → Target System: (예) MediaTek Ralink MIPS
    # → Target Profile: (예) Generic MT7621
    # → 필요한 패키지 선택
    
    # 빌드 (CPU 코어 수만큼 병렬, 상세 로그)
    make -j$(nproc) V=s
  4. 4단계: Buildroot 빠른 시작
    # Buildroot 소스 클론
    git clone https://github.com/buildroot/buildroot.git
    cd buildroot
    
    # 사전 정의된 보드 설정 적용 (예: QEMU ARM)
    make qemu_arm_vexpress_defconfig
    
    # 패키지/커널/부트로더 커스터마이징
    make menuconfig
    
    # 빌드 (output/ 디렉토리에 결과물 생성)
    make -j$(nproc)
  5. 5단계: Yocto 빠른 시작
    # Poky (Yocto 레퍼런스 배포판) 클론 — scarthgap (5.0 LTS)
    git clone -b scarthgap https://git.yoctoproject.org/poky
    cd poky
    
    # 빌드 환경 초기화 (build/ 디렉토리 생성)
    source oe-init-build-env
    
    # local.conf에서 MACHINE 설정
    # MACHINE = "qemuarm64"
    
    # 최소 이미지 빌드 (첫 빌드는 2-4시간 소요)
    bitbake core-image-minimal

3대 빌드 시스템 개요 및 비교

역사와 기원

세 시스템은 서로 다른 배경에서 탄생하여 고유한 철학과 강점을 발전시켰습니다.

역사 타임라인 비교

연도OpenWrtBuildrootYocto/OE
2000uClibc 빌드 인프라 시작
2003독립 프로젝트화OpenEmbedded 프로젝트 시작
2004WRT54G GPL 소스 기반 탄생
2006Kamikaze (첫 공식 릴리스)BitBake 엔진 안정화
2009Peter Korsgaard 메인테이너
2010Backfire분기별 릴리스 시작Yocto Project 출범 (Linux Foundation)
2012Attitude Adjustment1.0 Bernard → 1.2 Danny
2016LEDE 프로젝트 분기Cargo/Go 인프라2.1 Krogoth
2018LEDE와 OpenWrt 재통합 (18.06)2.5 Sumo
202019.07 → DSA 스위치 지원per-package-directories3.1 Dunfell (첫 LTS)
202222.03 (Firewall4/nftables)Meson 인프라 강화4.0 Kirkstone (LTS)
202423.05 LTS, 24.102024.02 (Rust Cargo 개선)5.0 Scarthgap (LTS)

철학 비교

관점OpenWrtBuildrootYocto
설계 목표네트워크 어플라이언스 OS최소한의 임베디드 시스템유연한 엔터프라이즈 빌드
핵심 원칙라우터에 완전한 리눅스를단순하게 유지하라재사용성과 확장성
빌드 도구Make + KconfigMake + KconfigBitBake + OE metadata
패키지 매니저opkg (런타임 설치 가능)없음 (정적 이미지)rpm/deb/ipk (선택)
라이선스GPL-2.0GPL-2.0+MIT (Poky/BitBake)
주요 사용처라우터, AP, 스위치, NASIoT, 산업용 제어기, STB자동차, 항공, 의료, 반도체 벤더 SDK
3대 임베디드 빌드 시스템 비교 개요 OpenWrt 2004~ | GPL-2.0 라우터 · 네트워크 어플라이언스 빌드 도구: Make + Kconfig 패키지: opkg (IPK) C 라이브러리: musl (기본) 설정: menuconfig 이미지: SquashFS + JFFS2 웹 UI: LuCI 네트워크: UCI 추상화 Init: procd (경량) IPC: ubus (JSON-RPC) 빌드 시간: ~1시간 이미지: 4~16 MB 학습 곡선: 중간 호스트 디스크: ~15 GB 릴리스: ~12개월 주기 커뮤니티: 활발 (GitHub) 패키지 5,000+ · 타겟 1,500+ Buildroot 2001~ | GPL-2.0+ 미니멀 임베디드 시스템 빌드 도구: Make + Kconfig 패키지: 없음 (정적 이미지) C 라이브러리: musl/glibc/uclibc 설정: defconfig + menuconfig 이미지: ext4/squashfs/cpio 외부 트리: BR2_EXTERNAL 시각화: graph-depends Init: BusyBox/systemd/sysv 법적: make legal-info 빌드 시간: ~30분 이미지: 2~8 MB 학습 곡선: 낮음 호스트 디스크: ~5 GB 릴리스: 분기별 (2/5/8/11) 커뮤니티: 안정적 (메일링 리스트) 패키지 2,800+ · defconfig 200+ Yocto 2010~ | MIT 엔터프라이즈 · 산업용 빌드 도구: BitBake + OE 패키지: rpm/deb/ipk (선택) C 라이브러리: glibc/musl 설정: .conf + .bb recipes 이미지: 다양 (wic/ext4/...) 레이어: meta-* 아키텍처 캐시: sstate-cache Init: systemd/sysvinit SDK: Standard/Extensible 빌드 시간: 2~4시간 이미지: 설정에 따라 다양 학습 곡선: 높음 호스트 디스크: ~50 GB+ 릴리스: 6개월 (LTS 2년) 커뮤니티: 기업 주도 + 커뮤니티 레이어 400+ · 벤더 공식 BSP
그림 1. 3대 임베디드 빌드 시스템 비교 개요 — OpenWrt(파랑), Buildroot(초록), Yocto(보라)

상세 비교표

항목OpenWrtBuildrootYocto
첫 빌드 시간~1시간~30분2~4시간
재빌드 시간수 분 (패키지 단위)수 분 (패키지 단위)수 초~분 (sstate-cache)
최소 이미지 크기~4 MB~2 MB~8 MB
런타임 패키지 관리opkg (필수)없음rpm/deb/ipk (선택)
기본 initprocdBusyBox init/systemd/sysvinitsystemd/sysvinit
기본 C 라이브러리musl선택 (musl/glibc/uclibc-ng)glibc (기본)
설정 방식Kconfig (menuconfig)Kconfig (menuconfig).conf 파일 + BitBake
패키지 수5,000+2,800+수만 (레이어 합산)
SDK 제공SDK + Image BuilderSDK (output/host)Standard/Extensible SDK
CI/CD 친화성중간높음 (단순)매우 높음 (sstate, hash equiv.)
상업적 지원커뮤니티 중심커뮤니티 중심Wind River, Mentor, Siemens 등
주요 벤더라우터/AP 제조사STMicro, MicrochipIntel, TI, NXP, AMD, Qualcomm
호스트 디스크~15 GB~5 GB~50 GB+
문서 품질Wiki 중심, 양호매뉴얼 우수방대 (Mega Manual)

라이선스 컴플라이언스 워크플로

임베디드 제품의 GPL/LGPL 소스 코드 공개 의무를 처리하는 방법은 시스템마다 다릅니다:

시스템라이선스 수집 명령출력물비고
OpenWrtmake package/xxx/compile 시 자동bin/packages/에 소스 포함 가능CONFIG_SRC_TREE_OVERRIDE로 소스 보존
Buildrootmake legal-infooutput/legal-info/ — 라이선스, 소스 아카이브, 매니페스트 CSV가장 체계적; 호스트/타겟 분리, 재배포 소스 자동 수집
YoctoINHERIT += "archiver" in local.conftmp/deploy/sources/, tmp/deploy/licenses/ARCHIVER_MODE 옵션으로 패치 포함 원본/수정 소스 선택

커뮤니티 지표 비교

지표OpenWrtBuildrootYocto
GitHub Stars~20K~3K~3K (Poky)
연간 커밋 수~8,000~3,000~5,000 (OE-Core)
활성 기여자~300명/년~200명/년~400명/년
메일링 리스트중간 활성높은 활성매우 높은 활성
IRC/Discord#openwrt (Libera)#buildroot (OFTC)#yocto (Libera)
릴리스 주기~12개월3개월6개월
버전 기준: 이 문서는 OpenWrt 23.05, Buildroot 2024.02, Yocto 5.0 (scarthgap) LTS를 기준으로 합니다. 최신 버전에서 세부 사항이 달라질 수 있으므로 공식 문서를 함께 참고하세요.

OpenWrt 빌드 시스템 아키텍처

OpenWrt의 소스 트리는 라우터/네트워크 장비에 특화된 계층 구조를 가집니다. 핵심은 target/(하드웨어 지원), package/(소프트웨어 패키지), feeds/(외부 패키지 저장소) 3개 디렉토리입니다.

OpenWrt 소스 트리 아키텍처 openwrt/ target/ package/ feeds/ toolchain/ tools/ include/ scripts/ linux/ ath79/ ramips/ mediatek/ x86/ target/linux/ath79/ generic/ — subtarget 공용 설정 config-6.6 — 커널 .config patches-6.6/ — 커널 패치 dts/ — Device Tree 소스 feeds.conf.default src-git packages ... src-git luci ... src-git routing ... src-git telephony ... 빌드 생성 디렉토리 build_dir/ — 소스 압축 해제 및 빌드 staging_dir/ — 크로스 툴체인+sysroot dl/ — 다운로드 캐시 bin/ — 최종 펌웨어 이미지 tmp/ — 임시 파일, .config toolchain/ binutils/ — 어셈블러, 링커 gcc/ — 크로스 컴파일러 musl/ — C 라이브러리 (기본) 하드웨어 타겟 패키지 피드 툴체인/빌드
그림 2. OpenWrt 소스 트리 아키텍처 — target/(하드웨어), package/(소프트웨어), feeds/(외부 저장소)가 핵심

주요 디렉토리 역할

디렉토리역할핵심 파일
target/linux/타겟 플랫폼별 커널 설정, 패치, DTSconfig-6.6, patches-6.6/, dts/
target/sdk/SDK 이미지 생성 규칙Makefile
package/핵심 패키지 Makefile (base-files, kernel, network 등)package/*/Makefile
feeds/외부 패키지 소스 (feeds install 후 심볼릭 링크)feeds.conf.default
toolchain/크로스 컴파일러 빌드 (GCC, Binutils, musl/glibc)toolchain/gcc/, toolchain/musl/
tools/호스트 빌드 도구 (m4, autoconf, cmake, sstrip 등)각 도구별 Makefile
include/빌드 시스템 공통 Makefile 인프라package.mk, image.mk, kernel.mk
scripts/빌드 유틸리티 스크립트feeds, config/, download.pl

target/subtarget/profile 계층 구조

OpenWrt는 3단계 계층으로 하드웨어를 분류합니다:

  1. Target — SoC 패밀리 (예: ath79 = Qualcomm Atheros AR7xxx/AR9xxx)
  2. Subtarget — SoC 변형 (예: generic, nand, tiny)
  3. Profile — 개별 보드/제품 (예: tplink_tl-wr1043nd-v1)
# target/linux/ath79/ 디렉토리 구조 예시
target/linux/ath79/
├── Makefile                  # 타겟 정의, BOARDNAME, FEATURES
├── config-6.6               # 커널 .config (subtarget 공용)
├── generic/                  # subtarget: generic
│   ├── config-default        # subtarget 기본 설정
│   └── target.mk             # DEVICE_PACKAGES 등
├── nand/                     # subtarget: nand (NAND 플래시 장비)
├── dts/                      # Device Tree 소스 (.dts/.dtsi)
│   ├── ar9344_tplink_tl-wr1043nd-v2.dts
│   └── qca9558_tplink_archer-c7-v2.dts
├── files/                    # 커널 소스에 추가할 파일
├── patches-6.6/              # 커널 패치 (순서번호_설명.patch)
│   ├── 0001-ath79-add-xxx.patch
│   └── 0002-ath79-fix-xxx.patch
└── image/                    # 이미지 생성 규칙
    └── Makefile              # DEVICE_xxx 매크로로 프로파일 정의

UCI/procd/netifd/ubus 서브시스템

OpenWrt가 일반 임베디드 리눅스와 구별되는 핵심은 자체 서브시스템 스택입니다. UCI(Unified Configuration Interface)는 /etc/config/ 디렉토리의 구조화된 설정 파일을 관리하며, procd는 systemd 대신 사용하는 경량 init/서비스 매니저, netifd는 네트워크 인터페이스를 추상화하는 데몬, ubus는 시스템 내 IPC(프로세스(Process) 간 통신)를 담당하는 JSON-RPC 버스(Bus)입니다.

OpenWrt UCI/procd/netifd 아키텍처 사용자 인터페이스 계층 LuCI Web UI CLI (uci 명령) rpcd (JSON-RPC) 커스텀 스크립트 UCI 설정 계층 (/etc/config/) network wireless firewall dhcp system ubus (마이크로 버스 — JSON-RPC IPC) procd (init/서비스 관리) PID 1 | 서비스 감시 | 핫플러그 프로세스 재시작 | jail(샌드박스) netifd (네트워크 데몬) 인터페이스/프로토콜 관리 bridge, VLAN, PPPoE, DHCP 기타 데몬 dnsmasq, fw4(nftables) hostapd, uhttpd, logd Linux Kernel 프로세스 관리 Netlink / Netfilter nl80211 (WiFi) DSA (Switch) procd vs systemd: procd는 ~200KB(systemd ~10MB), JSON 서비스 정의, 의존성 해결 단순화, 4MB 플래시에서도 동작
그림 13. OpenWrt UCI/procd/netifd 아키텍처 — LuCI/CLI → UCI 설정 → ubus IPC → procd/netifd → 커널

UCI 시스템 상세

# UCI 설정 파일 구조 (/etc/config/network 예시)
config interface 'loopback'
    option device 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'

config interface 'lan'
    option device 'br-lan'
    option proto 'static'
    option ipaddr '192.168.1.1'
    option netmask '255.255.255.0'

config interface 'wan'
    option device 'eth0.2'
    option proto 'dhcp'

# UCI CLI 명령
uci show network                     # 전체 설정 보기
uci get network.lan.ipaddr           # 특정 값 읽기
uci set network.lan.ipaddr='10.0.0.1' # 값 변경
uci commit network                   # 변경사항 저장
/etc/init.d/network restart          # 서비스 재시작으로 적용

# procd 서비스 관리
/etc/init.d/dnsmasq start|stop|restart|enable|disable
service list                         # 실행 중인 서비스 목록 (JSON)

procd vs systemd 비교

항목procd (OpenWrt)systemd (일반 리눅스)
바이너리 크기~200 KB~10 MB
서비스 정의Shell 스크립트 + JSON.service unit 파일
의존성단순 (시작 순서 번호)복잡 (Wants, Requires, After)
프로세스 감시respawn 지원Restart=on-failure 등 다양
샌드박스(Sandbox)procd jail (seccomp, namespace)다양한 보안 옵션
핫플러그(Hotplug)내장 (hotplug.d/)udev + systemd
최소 RAM~1 MB~30 MB

staging_dir 구조

staging_dir/은 OpenWrt 빌드 시스템의 핵심 중간 산출물 디렉토리입니다:

staging_dir/
├── host/                     # 호스트 빌드 도구 (cmake, m4, pkg-config 등)
├── hostpkg/                  # 호스트 패키지
├── toolchain-mips_24kc_gcc-13.3.0_musl/   # 크로스 툴체인
│   ├── bin/                  # mips-openwrt-linux-musl-gcc 등
│   ├── include/              # 헤더 파일
│   └── lib/                  # 크로스 컴파일된 라이브러리
└── target-mips_24kc_musl/    # 타겟 sysroot
    ├── usr/include/          # 타겟 헤더
    ├── usr/lib/              # 타겟 라이브러리 (.so, .a)
    └── pkgconfig/            # pkg-config .pc 파일

OpenWrt 빌드 과정

OpenWrt 빌드 파이프라인 feeds update -a 외부 저장소 동기화 feeds install -a 심볼릭 링크 생성 make menuconfig 타겟/패키지 선택 make -j$(nproc) V=s 전체 빌드 실행 1. Host Tools 2. Toolchain 3. Kernel 4. Packages 5. Image m4, autoconf, cmake sstrip, mkimage → staging_dir/host/ binutils, gcc, musl kernel headers → staging_dir/toolchain-*/ config-* 기반 .config patches-* 적용 → build_dir/target-*/linux-*/ 의존성 순서대로 크로스 컴파일 → IPK 패키지 생성 rootfs 조합 SquashFS 생성 → bin/targets/ bin/targets/ath79/generic/ *-sysupgrade.bin | *-factory.bin | *-initramfs.bin packages/ (IPK 저장소) | sha256sums
그림 3. OpenWrt 빌드 파이프라인 — feeds → menuconfig → host tools → toolchain → kernel → packages → firmware image

빌드 단계 상세

Step 1: 피드 업데이트 및 설치

# feeds.conf.default 내용 확인
cat feeds.conf.default
# src-git packages https://git.openwrt.org/feed/packages.git;openwrt-23.05
# src-git luci https://git.openwrt.org/project/luci.git;openwrt-23.05
# src-git routing https://git.openwrt.org/feed/routing.git;openwrt-23.05
# src-git telephony https://git.openwrt.org/feed/telephony.git;openwrt-23.05

# 모든 피드 업데이트 (git clone/pull)
./scripts/feeds update -a

# 모든 피드 패키지를 빌드 트리에 심볼릭 링크
./scripts/feeds install -a

# 특정 패키지만 설치
./scripts/feeds install luci-app-openvpn

Step 2: 설정 (menuconfig)

# ncurses 기반 메뉴 설정
make menuconfig

# 설정 항목:
# Target System → (예: MediaTek Ralink MIPS)
# Subtarget → (예: MT7621 based boards)
# Target Profile → (예: Xiaomi Mi Router 4A Gigabit)
# [*] Base system → base-files, busybox, ...
# [*] LuCI → Collections → luci
# [M] Network → VPN → openvpn-openssl    # M=모듈(IPK 생성)

# 커널 설정 확인/수정
make kernel_menuconfig

Step 3: 빌드 실행

# 전체 빌드 (상세 로그, CPU 코어 수 병렬)
make -j$(nproc) V=s

# 다운로드만 먼저 실행 (빌드 서버에서 유용)
make download -j8

# 특정 패키지만 재빌드
make package/network/services/dnsmasq/compile V=s

# 특정 패키지 정리 후 재빌드
make package/network/services/dnsmasq/{clean,compile} V=s

# 펌웨어 이미지만 재생성 (패키지 변경 없이)
make target/install V=s

# ccache 활성화 (재빌드 가속)
echo "CONFIG_CCACHE=y" >> .config
make defconfig

커널 모듈(Kernel Module) 컴파일

OpenWrt에서 커널 모듈은 kmod-* 패키지로 관리됩니다. 커널 모듈 패키지는 커널 버전과 정확히 일치해야 합니다:

# 커널 모듈 패키지 빌드 (kmod-usb-net 예시)
make package/kernel/linux/compile V=s

# 커널 모듈 패키지 정의 위치
package/kernel/linux/modules/
├── block.mk         # kmod-loop, kmod-nbd 등
├── crypto.mk        # kmod-crypto-aes 등
├── netdevices.mk    # kmod-usb-net, kmod-tun 등
├── netsupport.mk    # kmod-ipsec, kmod-wireguard 등
└── usb.mk           # kmod-usb-core, kmod-usb-storage 등

# 모듈 이름 패턴: kmod-<기능명>
# 예: kmod-usb-storage, kmod-wireguard, kmod-nft-nat

다운로드 미러 및 재현 가능 빌드

# 다운로드 미러 설정 (.config)
CONFIG_DOWNLOAD_FOLDER="/shared/dl"           # 공유 다운로드 캐시
CONFIG_LOCALMIRROR="https://mirror.example.com/openwrt/dl"  # 로컬 미러

# 또는 환경변수로 지정
export DL_DIR=/shared/dl

# 재현 가능 빌드를 위한 체크리스트
# 1. .config 파일 버전 관리
# 2. feeds.conf.default에 커밋 해시 고정
#    src-git packages https://git.openwrt.org/feed/packages.git^abc1234
# 3. dl/ 디렉토리 보존 (다운로드 소스 아카이브)
# 4. 호스트 도구 버전 통일 (Docker 권장)

빌드 오류 디버깅

# 단일 스레드로 상세 빌드 (오류 위치 확인)
make -j1 V=s 2>&1 | tee build.log

# 특정 패키지만 빌드 (의존성 문제 격리)
make package/xxx/compile V=s

# 빌드 디렉토리에서 직접 확인
ls build_dir/target-mips_24kc_musl/xxx-*/

# .config 변경 반영
make defconfig    # 의존성 자동 해결
make oldconfig    # 대화형 질문
빌드 가속 팁:
  • CONFIG_CCACHE=y — 컴파일러 캐시로 재빌드 시간 대폭 단축
  • CONFIG_DOWNLOAD_FOLDER — 공유 다운로드 캐시 디렉토리 지정
  • make download -j8 — 소스 다운로드를 먼저 병렬 실행
  • tmpfs에 build_dir/ 배치 — I/O 병목(Bottleneck) 해소 (16GB+ RAM 필요)
  • CONFIG_AUTOREMOVE=y — 빌드 완료된 패키지의 빌드 디렉토리 자동 삭제로 디스크 절약

OpenWrt 패키지 시스템

OpenWrt의 패키지 시스템은 다른 임베디드 빌드 시스템과 차별화되는 핵심 기능입니다. 런타임 패키지 설치/제거가 가능한 opkg 패키지 매니저와 IPK 포맷을 사용합니다.

OpenWrt 패키지 Makefile 구조 package/xxx/Makefile PKG_NAME:=hello PKG_VERSION:=1.0 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_HASH:=sha256... define Package/hello SECTION:=utils CATEGORY:=Utilities DEPENDS:=+libc +libpthread define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) ... endef define Package/hello/install $(INSTALL_BIN) ... $(1)/usr/bin/hello endef $(eval $(call BuildPackage,hello)) 소스 다운로드 dl/ → PKG_SOURCE 크로스 컴파일 build_dir/target-*/ 스테이징 설치 staging_dir/target-*/ IPK 패키지 생성 bin/packages/*/ hello_1.0-1_mips.ipk control.tar.gz - control (메타데이터) - preinst/postinst data.tar.gz - usr/bin/hello
그림 4. OpenWrt 패키지 Makefile 구조 — PKG_ 변수 → Build/Compile → Package/install → IPK 출력

패키지 Makefile 예제: Hello World

# package/hello/Makefile
include $(TOPDIR)/rules.mk

PKG_NAME:=hello
PKG_VERSION:=1.0
PKG_RELEASE:=1

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://example.com/
PKG_HASH:=skip

include $(INCLUDE_DIR)/package.mk

define Package/hello
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=Hello World Example
  DEPENDS:=+libc
  MAINTAINER:=Developer <dev@example.com>
endef

define Package/hello/description
  A simple Hello World package for OpenWrt.
endef

define Build/Compile
  $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) \
    -o $(PKG_BUILD_DIR)/hello $(PKG_BUILD_DIR)/hello.c
endef

define Package/hello/install
  $(INSTALL_DIR) $(1)/usr/bin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/hello $(1)/usr/bin/hello
endef

$(eval $(call BuildPackage,hello))

커널 모듈 패키지 패턴 (kmod-*)

OpenWrt의 커널 모듈은 독립 패키지로 분리되어 런타임 설치가 가능합니다:

# package/kernel/linux/modules/netdevices.mk (발췌)
define KernelPackage/usb-net
  SUBMENU:=$(USB_MENU)
  TITLE:=Kernel modules for USB-to-Ethernet
  DEPENDS:=+kmod-mii +kmod-usb-core
  KCONFIG:=CONFIG_USB_USBNET
  FILES:=$(LINUX_DIR)/drivers/net/usb/usbnet.ko
  AUTOLOAD:=$(call AutoLoad,61,usbnet)
endef

# 사용자가 menuconfig에서 kmod-usb-net을 선택하면
# → 커널 CONFIG_USB_USBNET=m 자동 설정
# → usbnet.ko 빌드 → IPK 생성
# → 타겟에서 opkg install kmod-usb-net 가능

LuCI 앱 패키지 패턴

# package/luci-app-myvpn/Makefile
include $(TOPDIR)/rules.mk

LUCI_TITLE:=LuCI support for MyVPN
LUCI_DEPENDS:=+myvpn +luci-base

include $(TOPDIR)/feeds/luci/luci.mk

# 필요한 것은 이것뿐 — luci.mk가 나머지를 자동 처리
$(eval $(call BuildPackage,luci-app-myvpn))

UCI 설정 통합 패턴

패키지가 UCI 설정을 사용하는 경우, 설치 시 기본 설정 파일을 제공합니다:

# Package/hello/install에 UCI 설정 포함
define Package/hello/install
  $(INSTALL_DIR) $(1)/usr/bin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/hello $(1)/usr/bin/hello
  $(INSTALL_DIR) $(1)/etc/config
  $(INSTALL_CONF) ./files/hello.conf $(1)/etc/config/hello
  $(INSTALL_DIR) $(1)/etc/init.d
  $(INSTALL_BIN) ./files/hello.init $(1)/etc/init.d/hello
endef

# files/hello.conf (UCI 설정 파일)
# config hello 'main'
#     option enabled '1'
#     option port '8080'

# files/hello.init (procd 서비스 스크립트)
# #!/bin/sh /etc/rc.common
# START=99
# USE_PROCD=1
# start_service() {
#     procd_open_instance
#     procd_set_param command /usr/bin/hello
#     procd_set_param respawn
#     procd_close_instance
# }
OpenWrt 네트워크 구성 추상화 (UCI → netifd → kernel) /etc/config/network (UCI 설정) interface 'lan' device 'br-lan', proto 'static' interface 'wan' device 'eth0.2', proto 'dhcp' device 'br-lan' type 'bridge', ports 'eth0.1' 'wlan0' uci commit → /etc/init.d/network restart netifd (Network Interface Daemon) UCI 설정 파싱 → 프로토콜 핸들러 실행 → 커널 인터페이스 생성 Bridge 생성 ip link add br-lan type bridge ip link set eth0.1 master br-lan VLAN 생성 ip link add eth0.1 link eth0 type vlan id 1 ip link add eth0.2 link eth0 type vlan id 2 IP 설정 ip addr add 192.168.1.1/24 dev br-lan udhcpc -i eth0.2 (WAN DHCP) 최종 네트워크 토폴로지 WAN(eth0.2/DHCP) ↔ NAT/Firewall ↔ LAN(br-lan: eth0.1+wlan0, 192.168.1.0/24)
그림 15. OpenWrt 네트워크 구성 추상화 — UCI 설정 → netifd → bridge/VLAN/IP 커널 설정

opkg 패키지 매니저

# 패키지 목록 업데이트
opkg update

# 패키지 설치
opkg install luci-app-openvpn

# 패키지 제거
opkg remove luci-app-openvpn

# 설치된 패키지 목록
opkg list-installed

# 패키지 정보 조회
opkg info dnsmasq

# 패키지가 제공하는 파일 확인
opkg files dnsmasq

# 의존성 확인
opkg depends luci

가상 패키지와 패키지 변형

OpenWrt는 같은 기능의 대안 패키지를 가상 패키지(virtual package)로 관리합니다:

# 예: OpenSSL vs wolfSSL — 둘 다 "libssl" 가상 패키지를 제공
# package/libs/openssl/Makefile
define Package/libopenssl
  PROVIDES:=libssl
endef

# package/libs/wolfssl/Makefile
define Package/libwolfssl
  PROVIDES:=libssl
endef

# 의존하는 패키지는 +libssl로 선언 → menuconfig에서 구현체 선택

opkg 오버레이와 플래시 공간 관리

# 플래시 사용량 확인
df -h /overlay
# Filesystem    Size  Used  Avail  Use%  Mounted on
# /dev/mtdblock6  3.5M  1.2M  2.3M   34%  /overlay

# 패키지 설치 전 공간 확인
opkg install --size luci-app-statistics
# Installing luci-app-statistics (40-r23456-abc1234) to root...
# Installing collectd (5.12.0-r23456) to root... (120 KB 필요)

# USB/SD로 opkg overlay 확장 (extroot)
# 1. USB 포맷: mkfs.ext4 /dev/sda1
# 2. /etc/config/fstab에 마운트 설정:
#    config mount
#        option target '/overlay'
#        option device '/dev/sda1'
#        option enabled '1'
# 3. 기존 overlay 복사: tar -C /overlay -cvf - . | tar -C /mnt/sda1 -xf -
# 4. reboot

피드(Feed) 저장소 구조

# /etc/opkg/distfeeds.conf (타겟 장비에서)
src/gz openwrt_core https://downloads.openwrt.org/releases/23.05.4/targets/ath79/generic/packages
src/gz openwrt_base https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/base
src/gz openwrt_luci https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/luci
src/gz openwrt_packages https://downloads.openwrt.org/releases/23.05.4/packages/mips_24kc/packages

# 저장소 디렉토리 구조
# packages/
# ├── Packages.gz          # 패키지 인덱스 (압축)
# ├── Packages.sig         # 서명
# ├── hello_1.0-1_mips_24kc.ipk
# └── ...
IPK vs DEB vs RPM: OpenWrt의 IPK 형식은 Debian의 DEB와 유사한 ar 아카이브 구조입니다. 경량화에 초점을 맞추어 메타데이터가 최소화되어 있으며, opkg 패키지 매니저도 dpkg에 비해 메모리/디스크 사용량이 매우 적습니다. 4MB 플래시 장비에서도 동작합니다.

OpenWrt 펌웨어 이미지 구조

OpenWrt 펌웨어의 핵심은 SquashFS + JFFS2 오버레이 아키텍처입니다. 읽기 전용(Read-Only) SquashFS 위에 쓰기 가능한 JFFS2 오버레이를 결합하여, 최소 플래시 사용으로 설정 변경과 패키지 설치를 지원합니다.

OpenWrt 플래시 파티션 레이아웃 NOR Flash 레이아웃 (일반 라우터) U-Boot 128~256 KB Env 64K Kernel (uImage/FIT) 1.5~4 MB RootFS (SquashFS, 읽기 전용) 2~10 MB Overlay (JFFS2) 나머지 공간, 쓰기 가능 OverlayFS 동작 원리 Lower Layer: SquashFS (읽기 전용) /rom — 원본 루트 파일시스템 Upper Layer: JFFS2 (쓰기 가능) /overlay — 변경/추가된 파일 / (OverlayFS 마운트) NAND Flash 레이아웃 (UBI) U-Boot + Env raw 파티션 UBI Vol: kernel UBI 볼륨 (wear leveling) UBI Vol: rootfs (SquashFS) 읽기 전용, UBI 볼륨 UBI Vol: rootfs_data (UBIFS) 쓰기 가능, overlay
그림 5. OpenWrt 플래시 파티션 레이아웃 — NOR(SquashFS+JFFS2 overlay)와 NAND(UBI 볼륨) 비교

이미지 유형

이미지 유형파일명 패턴용도
sysupgrade.bin*-sysupgrade.bin기존 OpenWrt에서 업그레이드용. 설정 보존 가능
factory.bin*-factory.bin제조사 펌웨어에서 최초 설치용. 벤더 헤더 포함
initramfs*-initramfs-kernel.binRAM 디스크 부팅. 디버깅/복구용 (플래시 미사용)
rootfs.img*-squashfs-rootfs.img.gzSquashFS 루트 이미지만 (별도 커널 필요)

이미지 헤더 포맷

포맷매직 넘버용도구조
TRXHDR0Broadcom 라우터 (레거시)헤더(28B) + kernel + rootfs [+ 추가 파티션]
uImage27 05 19 56U-Boot 레거시 이미지64B 헤더 + 압축 커널 (로드 주소/엔트리 포인트 포함)
FIT (ITB)FDT 구조현대 장비 (권장)Device Tree 기반 — 다중 커널/DTB/initrd, 서명 지원
UBIUBI#NAND 장비UBI 볼륨 이미지 — wear leveling, bad block 관리 포함

FIT (Flattened Image Tree) 포맷 상세

최신 OpenWrt 타겟은 FIT 이미지를 사용합니다. FIT는 Device Tree 구조를 이용하여 커널, DTB, initrd를 하나의 서명 가능한 이미지로 결합합니다:

/* FIT 이미지 구조 (.its 파일) */
/dts-v1/;
/ {
    description = "OpenWrt FIT Image";
    images {
        kernel-1 {
            data = /incbin/("zImage");
            type = "kernel";
            arch = "arm";
            compression = "none";
            hash-1 { algo = "sha256"; };
        };
        fdt-1 {
            data = /incbin/("my-board.dtb");
            type = "flat_dt";
            arch = "arm";
            hash-1 { algo = "sha256"; };
        };
    };
    configurations {
        default = "config-1";
        config-1 {
            kernel = "kernel-1";
            fdt = "fdt-1";
            signature-1 {
                algo = "sha256,rsa2048";
                key-name-hint = "dev";
            };
        };
    };
};

eMMC 장비 레이아웃

최신 고성능 라우터(MediaTek MT7986, Qualcomm IPQ807x 등)는 eMMC를 사용합니다:

# eMMC 파티션 레이아웃 (GPT)
# /dev/mmcblk0p1  boot0     — BL2 (ARM Trusted Firmware stage 1)
# /dev/mmcblk0p2  boot1     — FIP (BL31 + U-Boot)
# /dev/mmcblk0p3  env       — U-Boot 환경변수
# /dev/mmcblk0p4  factory   — 칼리브레이션 데이터 (WiFi, MAC)
# /dev/mmcblk0p5  kernel    — FIT 이미지 (커널 + DTB)
# /dev/mmcblk0p6  rootfs    — SquashFS 루트 파일시스템
# /dev/mmcblk0p7  rootfs_data — ext4/f2fs overlay (쓰기 가능)

# 이미지 크기 최적화 팁
# 1. 불필요한 커널 모듈 제거 (kmod-* 최소화)
# 2. LuCI 대신 CLI만 사용 시 ~1MB 절약
# 3. SquashFS XZ 압축으로 30-50% 절약 (기본 활성화)
# 4. strip 레벨: CONFIG_USE_SSTRIP=y (초공격적 strip)

sysupgrade 메커니즘

# 펌웨어 업그레이드 (설정 보존)
sysupgrade -v /tmp/firmware-sysupgrade.bin

# 설정 초기화하며 업그레이드
sysupgrade -n /tmp/firmware-sysupgrade.bin

# 업그레이드 전 설정 백업
sysupgrade -b /tmp/backup.tar.gz

# 복구: firstboot (overlay 초기화)
firstboot -y && reboot

# Failsafe 모드: 부팅 시 버튼 누르기 → 192.168.1.1:22 접속
# overlay를 마운트하지 않아 /rom만으로 부팅
관련 문서: SquashFS 상세는 SquashFS 문서, JFFS2 상세는 JFFS2 문서를 참고하세요.

Buildroot 아키텍처

Buildroot는 "Make를 사용하여 임베디드 리눅스 시스템을 간단하게 생성한다"는 철학을 따릅니다. 전체가 Makefile과 Kconfig로 구성되어 있으며, 추가 빌드 도구(BitBake 같은)가 필요 없습니다.

소스 트리 구조

buildroot/
├── Makefile                  # 최상위 Makefile
├── Config.in                 # 최상위 Kconfig
├── package/                  # 패키지 정의 (2800+ 패키지)
│   ├── busybox/              # BusyBox
│   │   ├── busybox.mk        # 빌드 레시피
│   │   ├── Config.in          # Kconfig 옵션
│   │   └── busybox.config     # BusyBox 설정
│   ├── openssl/
│   │   ├── openssl.mk
│   │   └── Config.in
│   └── pkg-generic.mk        # 공통 패키지 인프라
├── board/                    # 보드별 설정/스크립트
│   ├── qemu/                 # QEMU 보드
│   │   └── arm-vexpress/
│   │       ├── linux.config   # 커널 설정
│   │       ├── rootfs-overlay/ # 루트FS 오버레이
│   │       └── post-build.sh  # 빌드 후 스크립트
│   └── raspberrypi/          # Raspberry Pi
├── configs/                  # 사전 정의된 defconfig
│   ├── qemu_arm_vexpress_defconfig
│   ├── raspberrypi4_64_defconfig
│   └── ...                   # 200+ defconfig
├── dl/                       # 다운로드 캐시
├── output/                   # 빌드 출력 (아래에서 상세)
├── boot/                     # 부트로더 패키지 (U-Boot, GRUB, Barebox)
├── linux/                    # 커널 빌드 규칙
├── toolchain/                # 툴체인 빌드/외부 정의
├── system/                   # 시스템 설정 (hostname, init, overlay 등)
├── fs/                       # 파일시스템 이미지 생성 (ext4, squashfs, cpio 등)
├── support/                  # 빌드 지원 스크립트
└── docs/                     # 문서

시스템 스켈레톤 (system/)

Buildroot의 system/ 디렉토리는 루트 파일시스템의 기본 구조를 정의합니다:

# system/skeleton/ — 기본 루트FS 구조
system/skeleton/
├── etc/
│   ├── fstab              # 파일시스템 마운트 테이블
│   ├── group              # 기본 그룹 (root, daemon, ...)
│   ├── hostname           # BR2_TARGET_GENERIC_HOSTNAME
│   ├── hosts              # 로컬 호스트 매핑
│   ├── inittab            # BusyBox init 설정
│   ├── issue              # 로그인 배너
│   ├── passwd             # 기본 사용자 (root)
│   ├── profile            # 셸 프로필
│   ├── protocols          # 프로토콜 번호
│   ├── resolv.conf        # DNS 설정
│   ├── services           # 서비스 포트 매핑
│   └── shadow             # 패스워드 해시 (BR2_TARGET_GENERIC_ROOT_PASSWD)
└── usr/

# 시스템 설정 옵션 (menuconfig → System configuration)
BR2_TARGET_GENERIC_HOSTNAME="my-device"     # 호스트 이름
BR2_TARGET_GENERIC_ISSUE="Welcome to MyDevice"  # 로그인 배너
BR2_TARGET_GENERIC_ROOT_PASSWD="secret"     # root 패스워드
BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"       # 시리얼 콘솔 포트
BR2_TARGET_GENERIC_GETTY_BAUDRATE="115200"  # 시리얼 속도

# init 시스템 선택
BR2_INIT_BUSYBOX=y           # BusyBox init (기본, 가장 작음)
# BR2_INIT_SYSV=y            # sysvinit
# BR2_INIT_SYSTEMD=y         # systemd (glibc 필요)
# BR2_INIT_NONE=y            # init 없음 (커스텀)

Kconfig 시스템

Buildroot의 모든 설정은 BR2_* 변수로 관리됩니다:

# 설정 방법들
make menuconfig             # ncurses 메뉴
make nconfig                # 대안 ncurses
make xconfig                # Qt GUI
make gconfig                # GTK GUI

# defconfig 워크플로
make qemu_arm_vexpress_defconfig   # 사전 설정 적용
make menuconfig                     # 추가 커스터마이징
make savedefconfig                  # 변경 저장 → configs/

# 주요 설정 카테고리 (BR2_*)
# BR2_TOOLCHAIN_*           — 툴체인 옵션
# BR2_LINUX_KERNEL_*        — 커널 설정
# BR2_PACKAGE_*             — 패키지 선택
# BR2_TARGET_ROOTFS_*       — 루트FS 이미지 형식
# BR2_ROOTFS_OVERLAY        — 루트FS 오버레이 경로
# BR2_ROOTFS_POST_BUILD_SCRIPT — 빌드 후 스크립트

툴체인 선택

옵션설명장점단점
InternalBuildroot가 직접 GCC+binutils+C lib 빌드완전한 제어, 최신 버전빌드 시간 증가 (~20분)
External prebuiltLinaro, ARM 등 사전 빌드 툴체인 사용빠른 빌드 시작버전 제한
External customcrosstool-NG 등으로 직접 빌드한 툴체인완전 맞춤화별도 관리 필요

Buildroot 빌드 과정

Buildroot 빌드 파이프라인 make xxx_defconfig .config 생성 make menuconfig 커스터마이징 (선택) make -j$(nproc) BR2_JLEVEL=$(nproc) — 패키지별 병렬 빌드 1. Toolchain GCC + binutils + musl/glibc 2. Packages 의존성 순서 크로스 컴파일 3. RootFS target/ → 파일시스템 조합 4. Image ext4/squashfs/cpio/wic BR2_TOOLCHAIN_* BR2_PACKAGE_* BR2_ROOTFS_* BR2_TARGET_ROOTFS_* output/images/ — 최종 펌웨어 이미지
그림 6. Buildroot 빌드 파이프라인 — defconfig → toolchain → packages → rootfs → image
Buildroot output/ 디렉토리 트리 output/ build/ busybox-1.36.1/ linux-6.6.x/ openssl-3.x/ uboot-2024.x/ 패키지별 빌드 디렉토리 host/ bin/ (cross-gcc 등) lib/ (호스트 라이브러리) share/ (호스트 데이터) 크로스 툴체인 + 호스트 도구 images/ zImage (커널) rootfs.ext4 sdcard.img 최종 결과물 이미지 staging/ usr/include/ usr/lib/ (.so, .a) → host/에 심볼릭 링크 개발용 sysroot target/ bin/ usr/ etc/ lib/ var/ tmp/ (strip된 바이너리) 루트FS 스켈레톤 mksquashfs / mkfs.ext4 / genimage 주요 명령어 make xxx-rebuild 패키지 재빌드 (configure 건너뛰기) make xxx-reconfigure 패키지 재설정 + 재빌드 make graph-depends 의존성 그래프 PDF 생성
그림 7. Buildroot output/ 디렉토리 — build/(빌드), host/(크로스 툴), images/(최종), staging/(sysroot), target/(루트FS)

빌드 명령어 상세

# 기본 빌드
make -j$(nproc)

# 특정 패키지 조작
make busybox-menuconfig    # BusyBox 설정
make linux-menuconfig       # 커널 설정
make uboot-menuconfig       # U-Boot 설정

# 패키지 재빌드
make busybox-rebuild        # 소스 변경 후 재빌드
make busybox-reconfigure    # configure부터 재실행
make busybox-dirclean       # 완전 삭제 후 처음부터

# 시각화
make graph-depends          # 의존성 그래프 (output/graphs/)
make graph-build            # 빌드 시간 그래프
make graph-size             # 이미지 크기 분석

# 법적 정보 수집
make legal-info             # 라이선스, 소스 코드 아카이브 생성

# SDK 생성 (외부 개발용)
make sdk                    # output/images/에 SDK 타르볼

per-package-directories 모드

Buildroot 2020.02부터 도입된 BR2_PER_PACKAGE_DIRECTORIES는 top-level 병렬 빌드를 가능하게 합니다:

# 활성화: menuconfig → Build options → Use per-package directories
BR2_PER_PACKAGE_DIRECTORIES=y

# 기존 방식: 모든 패키지가 하나의 staging/target 공유
#   → 패키지 간 빌드 순서 직렬화 필요
# per-package 방식: 각 패키지가 독립적인 staging/target 보유
#   → 의존성 없는 패키지 동시 빌드 가능
#   → 재빌드 시 영향 범위 최소화

# 디렉토리 구조 변화
output/
├── per-package/
│   ├── busybox/
│   │   ├── host/        # busybox 전용 호스트 도구
│   │   ├── staging/     # busybox 전용 sysroot
│   │   └── target/      # busybox 전용 타겟
│   ├── openssl/
│   │   ├── host/
│   │   ├── staging/
│   │   └── target/
│   └── ...
└── target/              # 최종 통합 루트FS

ccache 통합

# ccache 활성화: menuconfig → Build options → Enable compiler cache
BR2_CCACHE=y
BR2_CCACHE_DIR="/shared/buildroot-ccache"  # 공유 캐시 디렉토리

# ccache 통계 확인
make ccache-stats

# 효과: 전체 리빌드 시 50-80% 시간 단축 (캐시 히트 시)

graph-size 이미지 크기 분석

# 이미지 크기 분석 실행
make graph-size

# 결과물: output/graphs/
# ├── graph-size.pdf          # 패키지별 크기 파이 차트
# ├── package-size-stats.csv  # CSV 데이터
# └── file-size-stats.csv     # 파일별 크기

# 크기 최적화 워크플로
# 1. make graph-size 실행
# 2. 큰 패키지 확인 (보통 glibc > openssl > busybox > python)
# 3. 불필요 패키지 제거 또는 musl로 전환
# 4. BR2_STRIP_strip=y (기본) → 바이너리 strip
out-of-tree 빌드: Buildroot는 O= 변수로 여러 설정을 동시에 빌드할 수 있습니다: make O=/path/to/output1 qemu_arm_vexpress_defconfig, make O=/path/to/output2 raspberrypi4_64_defconfig. 각 output 디렉토리가 독립적인 빌드 환경을 가집니다.

Buildroot 패키지 인프라

Buildroot는 다양한 빌드 시스템에 맞춘 패키지 인프라(infrastructure)를 제공합니다. 개발자는 적절한 인프라를 선택하여 최소한의 코드로 패키지를 정의할 수 있습니다.

패키지 인프라 유형

인프라Makefile 매크로(Macro)대상 빌드 시스템예시 패키지
generic$(eval $(generic-package))커스텀 MakefileBusyBox, Linux 커널
autotools$(eval $(autotools-package))configure/make/make installcoreutils, glib
cmake$(eval $(cmake-package))CMakesystemd, json-c
meson$(eval $(meson-package))Meson + Ninjapipewire, gstreamer
python$(eval $(python-package))setuptools/pippython-requests
golang$(eval $(golang-package))Go modulescontainerd, etcd
cargo$(eval $(cargo-package))Rust Cargoripgrep
kconfig$(eval $(kconfig-package))Kconfig 기반BusyBox, Linux

Generic 패키지 예제

# package/hello/hello.mk
HELLO_VERSION = 1.0
HELLO_SOURCE = hello-$(HELLO_VERSION).tar.gz
HELLO_SITE = https://example.com/downloads
HELLO_LICENSE = MIT
HELLO_LICENSE_FILES = LICENSE
HELLO_DEPENDENCIES = host-pkgconf openssl

define HELLO_BUILD_CMDS
    $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D)
endef

define HELLO_INSTALL_TARGET_CMDS
    $(INSTALL) -D -m 0755 $(@D)/hello $(TARGET_DIR)/usr/bin/hello
endef

$(eval $(generic-package))

CMake 패키지 예제

# package/myapp/myapp.mk
MYAPP_VERSION = 2.0
MYAPP_SITE = $(call github,myorg,myapp,v$(MYAPP_VERSION))
MYAPP_LICENSE = Apache-2.0
MYAPP_INSTALL_STAGING = YES
MYAPP_DEPENDENCIES = openssl zlib

MYAPP_CONF_OPTS = \
    -DBUILD_TESTS=OFF \
    -DWITH_SSL=ON

$(eval $(cmake-package))

가상 패키지 (Virtual Packages)

Buildroot의 가상 패키지는 동일한 기능을 제공하는 대안 패키지를 추상화합니다:

# package/jpeg/Config.in — 가상 패키지 정의
config BR2_PACKAGE_HAS_JPEG
    bool  # "provides" 마커

config BR2_PACKAGE_PROVIDES_JPEG
    string
    default "libjpeg"   if BR2_PACKAGE_LIBJPEG
    default "jpeg-turbo" if BR2_PACKAGE_JPEG_TURBO

# 의존하는 패키지:
# MYAPP_DEPENDENCIES = jpeg   ← 가상 패키지명으로 의존
# → menuconfig에서 libjpeg 또는 jpeg-turbo 중 선택

호스트 패키지 (host-xxx)

# 호스트 패키지는 빌드 호스트에서 실행되는 도구를 빌드
# 패키지명에 host- 접두사 사용

HOST_MYAPP_DEPENDENCIES = host-pkgconf host-openssl

define HOST_MYAPP_BUILD_CMDS
    $(MAKE) -C $(@D)
endef

define HOST_MYAPP_INSTALL_CMDS
    $(INSTALL) -D $(@D)/mytool $(HOST_DIR)/bin/mytool
endef

$(eval $(host-generic-package))  # host- 접두사 인프라

사용자/권한 관리 (FOO_USERS, FOO_PERMISSIONS)

# package/myapp/myapp.mk — 사용자/그룹/권한 설정
MYAPP_USERS = myapp -1 myapp -1 * /var/lib/myapp - - My Application User

# 형식: username uid group gid password home shell groups comment
# -1은 자동 할당

MYAPP_PERMISSIONS = /etc/myapp.conf f 0600 root myapp - - - - -
MYAPP_PERMISSIONS += /var/lib/myapp d 0750 myapp myapp - - - - -

# systemd 서비스 설치 패턴
define MYAPP_INSTALL_INIT_SYSTEMD
    $(INSTALL) -D -m 644 $(MYAPP_PKGDIR)/myapp.service \
        $(TARGET_DIR)/usr/lib/systemd/system/myapp.service
endef

# sysvinit 서비스 설치 패턴
define MYAPP_INSTALL_INIT_SYSV
    $(INSTALL) -D -m 755 $(MYAPP_PKGDIR)/S99myapp \
        $(TARGET_DIR)/etc/init.d/S99myapp
endef

Config.in 구조

# package/hello/Config.in
config BR2_PACKAGE_HELLO
    bool "hello"
    depends on BR2_TOOLCHAIN_HAS_THREADS
    select BR2_PACKAGE_OPENSSL
    help
      A simple Hello World application.

      https://example.com/hello

BR2_EXTERNAL: 외부 트리

회사/프로젝트 전용 패키지와 설정을 Buildroot 소스 트리 외부에 관리할 수 있습니다:

# 외부 트리 구조
my-external/
├── Config.in                   # 외부 패키지 Kconfig
├── external.mk                 # 외부 패키지 포함
├── external.desc               # 이름, 설명
├── configs/                    # 커스텀 defconfig
│   └── my_board_defconfig
├── package/                    # 커스텀 패키지
│   └── my-app/
│       ├── my-app.mk
│       └── Config.in
└── board/                      # 보드 설정
    └── my-company/
        └── my-board/
            ├── linux.config
            ├── rootfs-overlay/
            ├── post-build.sh
            └── genimage.cfg

# 사용법
make BR2_EXTERNAL=/path/to/my-external menuconfig
# 또는
make BR2_EXTERNAL=/path/to/my-external my_board_defconfig

Yocto/OpenEmbedded 아키텍처

Yocto Project는 가장 유연하고 확장성 있는 임베디드 빌드 프레임워크입니다. 핵심은 레이어 아키텍처로, 독립적인 메타데이터 레이어를 쌓아 올려 맞춤형 배포판을 구성합니다.

Poky 구성요소

Poky는 Yocto의 레퍼런스 배포판으로, 다음으로 구성됩니다:

Yocto 레이어/레시피 아키텍처 스택 meta/ (OpenEmbedded-Core) 핵심 recipe, class, 기본 이미지 meta-poky (배포판 정책) DISTRO = "poky", poky.conf BSP 레이어 (meta-ti, meta-raspberrypi ...) MACHINE, 커널 recipe, DTS, 부트로더 기능 레이어 (meta-security, meta-oe ...) 추가 패키지, 보안 정책, 네트워크 등 커스텀 레이어 (meta-my-product) 프로젝트 recipe, bbappend, 이미지 정의 우선순위 (높음 →) 주요 파일 유형 .bb Recipe — 패키지 빌드 정의 .bbappend Recipe 수정/확장 .bbclass Class — 공통 빌드 로직 .conf 설정 — MACHINE, DISTRO, local .inc Include — 공유 recipe 코드 핵심 설정 파일 build/conf/local.conf build/conf/bblayers.conf meta-*/conf/machine/*.conf BitBake 엔진 레시피 파싱 → 의존성 해결 → 태스크 병렬 실행
그림 8. Yocto 레이어/레시피 아키텍처 — BitBake 엔진이 계층화된 레이어의 recipe/class/conf를 처리
Yocto class (.bbclass) 상속 계층 Yocto bbclass 상속 계층 (inherit 관계) base.bbclass do_fetch, do_unpack, do_patch 기본 구현 autotools.bbclass configure/make/install cmake.bbclass CMake 빌드 meson.bbclass Meson+Ninja 빌드 image.bbclass 이미지 생성 kernel.bbclass 커널 빌드 glib_2.78.bb inherit meson pkgconfig openssl_3.2.bb inherit autotools json-c_0.17.bb inherit cmake core-image-minimal.bb inherit image linux-yocto_6.6.bb inherit kernel 점선 화살표: recipe가 class를 inherit 실선 화살표: class가 base.bbclass를 inherit class는 do_configure, do_compile 등의 기본 구현을 제공하며, recipe에서 override 가능
그림 14. Yocto class 상속 계층 — base.bbclass를 뿌리로 autotools/cmake/meson/image/kernel class가 파생

DISTRO_FEATURES vs MACHINE_FEATURES vs IMAGE_FEATURES

변수설정 위치역할예시
DISTRO_FEATURESdistro.conf배포판 수준 기능 (소프트웨어 정책)systemd wifi bluetooth ipv6 pam
MACHINE_FEATURESmachine.conf하드웨어 기능 (보드가 제공하는 것)serial apm usbhost wifi gpu
IMAGE_FEATURES이미지 recipe/local.conf이미지에 포함할 기능 세트debug-tweaks ssh-server-openssh tools-debug
# DISTRO_FEATURES 활용 — 배포판 정책
# systemd를 init으로 사용:
DISTRO_FEATURES:append = " systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED:append = " sysvinit"
VIRTUAL-RUNTIME_init_manager = "systemd"

# MACHINE_FEATURES 활용 — 하드웨어 능력
# meta-raspberrypi/conf/machine/raspberrypi4-64.conf:
MACHINE_FEATURES = "apm usbhost wifi bluetooth vfat ext2 screen touchscreen"

# IMAGE_FEATURES 활용 — 이미지 구성
IMAGE_FEATURES += "debug-tweaks"       # root 비밀번호 없이 로그인 (개발용)
IMAGE_FEATURES += "ssh-server-openssh" # SSH 서버 포함
IMAGE_FEATURES += "tools-debug"        # gdb, strace 등 디버그 도구

multiconfig 지원

Yocto의 multiconfig는 하나의 빌드 환경에서 여러 머신/배포판을 동시에 빌드합니다:

# conf/multiconfig/board-a.conf
MACHINE = "board-a"
DISTRO = "my-distro"

# conf/multiconfig/board-b.conf
MACHINE = "board-b"
DISTRO = "my-distro"

# local.conf
BBMULTICONFIG = "board-a board-b"

# 빌드
bitbake mc:board-a:core-image-minimal mc:board-b:core-image-minimal

kas 설정 도구

kas는 Yocto 프로젝트의 레이어/설정을 YAML로 선언적 관리하는 도구입니다:

# kas-project.yml
header:
  version: 14
machine: raspberrypi4-64
distro: poky

repos:
  poky:
    url: "https://git.yoctoproject.org/poky"
    branch: scarthgap
    layers:
      meta:
      meta-poky:
  meta-raspberrypi:
    url: "https://git.yoctoproject.org/meta-raspberrypi"
    branch: scarthgap

local_conf_header:
  custom: |
    IMAGE_INSTALL:append = " openssh python3"
    PACKAGE_CLASSES = "package_rpm"

target: core-image-full-cmdline
# kas 사용
pip install kas
kas build kas-project.yml       # 전체 빌드
kas shell kas-project.yml       # 빌드 환경 셸 진입
kas checkout kas-project.yml    # 레이어 클론만

레이어 아키텍처

레이어설명제공처
meta/ (OE-Core)기본 recipe, class, qemu BSPOE 커뮤니티
meta-pokyPoky 배포판 정책Yocto Project
meta-openembedded추가 패키지 (networking, python, perl 등)OE 커뮤니티
meta-ti-bspTexas Instruments BSP (AM335x, AM62x 등)TI
meta-raspberrypiRaspberry Pi BSP커뮤니티
meta-intelIntel BSP (x86, Edison 등)Intel
meta-freescaleNXP/Freescale BSP (i.MX, Layerscape)NXP
meta-security보안 도구 (SELinux, IMA, AppArmor)커뮤니티
meta-virtualization컨테이너(Container), 하이퍼바이저 (Docker, KVM)커뮤니티
OpenEmbedded Layer Index: layers.openembedded.org에서 400개 이상의 공개 레이어를 검색할 수 있습니다. 레이어의 호환 Yocto 버전, 의존성, recipe 목록을 확인할 수 있습니다.

Recipe (.bb) 구조

# recipes-example/hello/hello_1.0.bb
SUMMARY = "Hello World Application"
DESCRIPTION = "A simple Hello World for Yocto"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=..."

SRC_URI = "https://example.com/hello-${PV}.tar.gz"
SRC_URI[sha256sum] = "abc123..."

DEPENDS = "openssl"
RDEPENDS:${PN} = "libssl"

S = "${WORKDIR}/hello-${PV}"

inherit autotools pkgconfig

EXTRA_OECONF = "--with-ssl"

do_install:append() {
    install -d ${D}${sysconfdir}
    install -m 0644 ${WORKDIR}/hello.conf ${D}${sysconfdir}/
}

주요 설정 변수

# build/conf/local.conf
MACHINE = "raspberrypi4-64"       # 타겟 머신
DISTRO = "poky"                    # 배포판
PACKAGE_CLASSES = "package_rpm"    # 패키지 형식 (rpm/deb/ipk)

IMAGE_INSTALL:append = " openssh vim strace gdb"

DISTRO_FEATURES:append = " systemd wifi bluetooth"
DISTRO_FEATURES:remove = "sysvinit"

BB_NUMBER_THREADS = "8"
PARALLEL_MAKE = "-j 8"
SSTATE_DIR = "/opt/yocto/sstate"
DL_DIR = "/opt/yocto/downloads"

# build/conf/bblayers.conf
BBLAYERS = " \
  /path/to/poky/meta \
  /path/to/poky/meta-poky \
  /path/to/meta-openembedded/meta-oe \
  /path/to/meta-raspberrypi \
  /path/to/meta-my-product \
  "

Yocto 빌드 과정

BitBake 태스크 체인 BitBake 태스크 체인 (per-recipe) do_fetch do_unpack do_patch do_configure do_compile do_install do_package do_package_write do_rootfs do_image sstate-cache (Shared State) 각 태스크 완료 시 결과물을 해시 기반 캐시에 저장 → 재빌드 시 캐시 히트하면 태스크 건너뜀 (수 시간 → 수 초) 빌드 디렉토리 구조 tmp/work/<TUNE>-poky-linux/<PN>/<PV>/ tmp/deploy/images/<MACHINE>/ per-recipe 빌드 디렉토리 최종 이미지 + 패키지
그림 9. BitBake 태스크 체인 — do_fetch에서 do_image까지, sstate-cache가 각 단계를 캐시

핵심 BitBake 명령어

# 환경 초기화 (반드시 매 세션마다)
source oe-init-build-env [build-dir]

# 이미지 빌드
bitbake core-image-minimal
bitbake core-image-sato
bitbake core-image-full-cmdline

# 특정 recipe 빌드
bitbake busybox
bitbake -c compile busybox
bitbake -c devshell busybox
bitbake -c cleansstate busybox

# 레이어 관리
bitbake-layers show-layers
bitbake-layers add-layer ../meta-xxx
bitbake-layers show-recipes

# 의존성 분석
bitbake -g core-image-minimal
bitbake -e busybox | grep ^SRC_URI

# SDK 생성
bitbake -c populate_sdk core-image-minimal
bitbake -c populate_sdk_ext core-image-minimal

Hash Equivalence Server

Yocto 3.1+의 Hash Equivalence Server는 동일한 출력을 생성하는 다른 입력 해시(Hash)를 매핑(Mapping)하여 불필요한 재빌드를 추가 방지합니다:

# local.conf에서 활성화
BB_HASHSERVE = "auto"
BB_SIGNATURE_HANDLER = "OEEquivHash"

# 원리: A 패키지의 입력이 변경되었지만 출력 바이너리가 동일한 경우
# → B 패키지(A에 의존)를 재빌드하지 않음
# → 예: 주석만 변경된 소스 파일, 환경변수 순서 변경 등

재현 가능 빌드 (Reproducible Builds)

# Yocto 3.4+: 재현 가능 빌드 기본 활성화
# local.conf:
BUILD_REPRODUCIBLE_BINARIES = "1"

# 빌드 히스토리 추적 (변경사항 모니터링)
INHERIT += "buildhistory"
BUILDHISTORY_COMMIT = "1"

# 결과: buildhistory/ 디렉토리에 git 이력으로
# - 패키지 목록 변경
# - 파일 크기/권한 변경
# - 의존성 변경 추적
빌드 시간 비교: 같은 하드웨어(16코어, 64GB RAM, NVMe)에서 core-image-minimal 첫 빌드는 약 2~4시간이 소요되지만, sstate-cache가 있는 재빌드는 수 분 이내로 완료됩니다. CI/CD 환경에서 sstate-cache를 NFS/S3에 공유하면 빌드 시간을 극적으로 단축할 수 있습니다.

크로스 컴파일 툴체인 비교

임베디드 빌드 시스템의 핵심은 크로스 컴파일 툴체인입니다. 특히 C 라이브러리 선택은 바이너리 크기, 호환성, 성능에 큰 영향을 미칩니다. 크로스 컴파일의 이론적 배경은 LFS 문서를 참고하세요.

크로스 컴파일 C 라이브러리 비교 C 라이브러리 비교: musl vs glibc vs uClibc-ng musl 2011~ | MIT | Rich Felker 정적 링크 크기: ~700 KB POSIX 준수: 높음 스레드: NPTL (1:1) DNS: 내장 (단순) locale: UTF-8 전용 보안: stack canary 기본 사용: OpenWrt (기본), Alpine Linux, Buildroot, Yocto (선택) 크기 30% glibc 1987~ | LGPL-2.1 | GNU 정적 링크 크기: ~2.5 MB POSIX 준수: 완전 스레드: NPTL (고성능) DNS: nsswitch, nscd locale: 완전 지원 호환: glibc 확장 API 사용: Yocto (기본), Ubuntu, Fedora, Debian, Buildroot (선택) 크기 100% uClibc-ng 2000~/2015~ | LGPL-2.1 정적 링크 크기: ~600 KB POSIX 준수: 부분적 스레드: NPTL/LinuxThreads DNS: 내장 (제한적) locale: 선택적 MMU-less: 지원 (FLAT) 사용: Buildroot (선택), MMU 없는 Cortex-M, 레거시 시스템 크기 25%
그림 10. C 라이브러리 비교 — musl(경량, OpenWrt 기본), glibc(완전, Yocto 기본), uClibc-ng(레거시/MMU-less)

ABI 호환성 매트릭스

ARM 옵션접미사설명호환성
Hard-float (VFPv3)gnueabihf / musleabihf하드웨어 FPU 사용, 부동소수점 인자를 FPU 레지스터(Register)로 전달Cortex-A7+ 필수, 가장 빠름
Soft-floatgnueabi / musleabi소프트웨어 부동소수점 에뮬레이션FPU 없는 프로세서, 느림
AArch64aarch64-linux-gnu64비트 ARM, 항상 하드웨어 FPARMv8-A+
잘못된 sysroot 사용 증상: 크로스 컴파일 시 "sysroot mismatch" 오류가 나타나면:
  • cannot find -lxxx — sysroot에 해당 라이브러리 미설치. PKG_CONFIG_SYSROOT_DIR 확인
  • GLIBC_2.xx not found — 타겟 glibc 버전이 빌드 시 사용한 것보다 낮음
  • Illegal instruction — 타겟 CPU가 지원하지 않는 명령어 생성 (예: Cortex-A7에서 Cortex-A72 코드)
  • dynamic linker not found — 정적/동적 링킹(Dynamic Linking) 불일치, 또는 sysroot의 ld.so 경로 불일치

정적 vs 동적 링킹 트레이드오프

항목정적 링킹(Static Linking)동적 링킹
바이너리 크기큼 (라이브러리 포함)작음 (공유 라이브러리(Shared Library) 참조)
총 시스템 크기적은 프로그램: 작음 / 많은 프로그램: 큼라이브러리 공유로 절약
보안 업데이트모든 바이너리 재빌드 필요라이브러리만 교체
배포 편의성단일 파일, 의존성 없음.so 파일 함께 배포 필요
사용 사례initramfs, rescue 도구, 단일 앱 장비일반 임베디드 시스템

시스템별 툴체인 전략

OpenWrt 툴체인

# OpenWrt는 항상 내부 빌드 (musl 기본, glibc 선택 가능)
make menuconfig
# → Advanced configuration → Toolchain Options
#   → C Library implementation (musl / glibc)

# 결과물
staging_dir/toolchain-mips_24kc_gcc-13.3.0_musl/bin/
├── mips-openwrt-linux-musl-gcc
├── mips-openwrt-linux-musl-g++
├── mips-openwrt-linux-musl-ld
└── mips-openwrt-linux-musl-strip

Buildroot 툴체인

# 내부 빌드 (Internal)
make menuconfig
# → Toolchain → Toolchain type: Internal toolchain
#   → C library: musl / glibc / uClibc-ng

# 외부 프리빌트 (Linaro ARM)
# → Toolchain type: External toolchain
# → Toolchain: Linaro ARM 2024.xx

# 결과물 (내부 빌드 시)
output/host/bin/
├── arm-buildroot-linux-musleabihf-gcc
├── arm-buildroot-linux-musleabihf-g++
└── ...

Yocto 툴체인/SDK

# Standard SDK 생성
bitbake -c populate_sdk core-image-minimal

# SDK 설치
./poky-glibc-x86_64-...-toolchain-4.0.sh -d /opt/yocto-sdk

# SDK 환경 설정
source /opt/yocto-sdk/environment-setup-cortexa72-poky-linux

# 크로스 컴파일
$CC -o hello hello.c   # $CC = aarch64-poky-linux-gcc + sysroot flags

# Extensible SDK (eSDK) — devtool 포함
bitbake -c populate_sdk_ext core-image-minimal

Sysroot 구조

# 공통 sysroot 구조 (모든 시스템)
sysroot/
├── usr/
│   ├── include/              # 타겟 시스템 헤더
│   │   ├── linux/            # 커널 헤더
│   │   ├── stdio.h           # C 라이브러리 헤더
│   │   └── openssl/          # 패키지 헤더
│   └── lib/                  # 타겟 라이브러리
│       ├── libc.so           # C 라이브러리
│       ├── libssl.so         # OpenSSL
│       └── pkgconfig/        # pkg-config 메타데이터
└── lib/
    └── ld-musl-mips.so.1     # 동적 링커

커널 빌드 커스터마이징

각 빌드 시스템은 고유한 방식으로 커널 빌드를 관리합니다. 커널 설정, 패치 적용, Device Tree 생성, 버전 관리 방법이 모두 다릅니다.

OpenWrt 커널 커스터마이징

# 커널 설정 파일 위치
target/linux/ath79/config-6.6          # 타겟별 기본 설정
target/linux/generic/config-6.6        # 공통 설정

# 커널 패치
target/linux/ath79/patches-6.6/
├── 0001-ath79-add-new-board.patch
└── 0002-ath79-fix-pcie.patch

# 커널 소스에 추가할 파일 (drivers, DTS 등)
target/linux/ath79/files/
├── arch/mips/dts/my-board.dts
└── drivers/net/phy/my-phy.c

# 커널 menuconfig
make kernel_menuconfig

# 변경사항을 config-6.6에 반영
make kernel_oldconfig

# 특정 커널 모듈만 빌드
make target/linux/compile V=s

Buildroot 커널 커스터마이징

# menuconfig에서 커널 설정
make menuconfig
# → Kernel
#   → Kernel version: 6.6.x (또는 custom tarball)
#   → Kernel configuration: Using a custom config file
#   → Configuration file path: board/my-company/my-board/linux.config
#   → Custom kernel patches: board/my-company/my-board/patches/

# 커널 menuconfig 직접 실행
make linux-menuconfig

# 커널 설정 저장 (defconfig 형태)
make linux-update-defconfig

# 커널만 재빌드
make linux-rebuild

Yocto 커널 커스터마이징

# 커널 recipe 위치 확인
bitbake -e virtual/kernel | grep ^FILE=

# 커널 menuconfig
bitbake -c menuconfig virtual/kernel

# 커널 설정 fragment 방식 (권장)
# meta-my-layer/recipes-kernel/linux/linux-yocto_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "file://my-config.cfg"
SRC_URI += "file://0001-my-driver.patch"

# files/my-config.cfg (config fragment)
CONFIG_MY_DRIVER=m
CONFIG_USB_GADGET=y

# KERNEL_FEATURES (Yocto 고유 기능)
# 커널 기능을 논리적으로 그룹화한 .scc 파일
KERNEL_FEATURES:append = " features/netfilter/netfilter.scc"
KERNEL_FEATURES:append = " cfg/virtio.scc"

# DTB 지정
KERNEL_DEVICETREE = "broadcom/bcm2711-rpi-4-b.dtb"

# 커널 devshell (소스 디렉토리에서 직접 작업)
bitbake -c devshell virtual/kernel

out-of-tree 커널 모듈 패턴

시스템패턴예시
OpenWrtpackage/kernel/my-module/Makefile + KernelPackagedefine KernelPackage/my-module ... FILES:=$(LINUX_DIR)/drivers/my-module.ko endef
Buildrootpackage/my-module/my-module.mk + kernel-module infra$(eval $(kernel-module)) $(eval $(generic-package))
Yoctoinherit module in recipeinherit module — do_compile은 자동으로 make -C ${STAGING_KERNEL_DIR} M=${S}

커널 버전 고정 전략

# OpenWrt: 타겟별 커널 버전은 include/kernel-version.mk에서 관리
# → 사용자 변경 비권장 (타겟-커널 호환성 보장)

# Buildroot: menuconfig에서 지정
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.6.30"
# 또는 커스텀 tarball/git:
BR2_LINUX_KERNEL_CUSTOM_TARBALL=y
BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="https://example.com/linux-6.6.30.tar.xz"

# Yocto: PREFERRED_VERSION으로 고정
PREFERRED_VERSION_linux-yocto = "6.6%"   # 6.6.x 시리즈
# 또는 정확한 커밋 고정:
SRCREV = "abc1234..."  # in linux-yocto bbappend
Config Fragment vs Full defconfig: Config fragment 방식은 변경한 옵션만 기록하므로 커널 버전 업그레이드 시 충돌이 적습니다. Yocto에서는 .cfg fragment를, Buildroot에서는 BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES를, OpenWrt에서는 config-* 파일에 delta만 기록하는 방식을 권장합니다.

커스텀 보드 지원 (BSP)

새로운 하드웨어 보드를 빌드 시스템에 통합하려면 BSP(Board Support Package)를 작성해야 합니다. BSP는 부트로더, 커널 설정, Device Tree, 보드별 드라이버, 플래시 레이아웃을 포함합니다.

BSP 구성요소 다이어그램 BSP Board Support Package 부트로더 설정 U-Boot defconfig, env, DTS 커널 설정/패치 defconfig, fragments, patches Device Tree 소스 .dts/.dtsi, 오버레이 보드별 루트FS overlay, 설정 파일, init 스크립트 플래시 레이아웃 파티션 테이블, 이미지 생성 규칙 보드별 드라이버 out-of-tree 커널 모듈, 펌웨어 blob OpenWrt: target/linux/xxx/ Buildroot: board/company/product/ Yocto: meta-xxx/
그림 11. BSP 구성요소 — 부트로더, 커널, DTS, 루트FS, 플래시 레이아웃, 드라이버를 포함

BSP 검증 체크리스트

BSP 통합 전 검증:
  • 시리얼 콘솔 출력 확인 (U-Boot → 커널 → 로그인 프롬프트)
  • 커널 부팅 시 Device Tree 올바르게 로드 확인: cat /proc/device-tree/model
  • 주요 하드웨어 초기화: 이더넷, WiFi, USB, GPIO, I2C, SPI
  • 플래시 파티션 레이아웃 확인: cat /proc/mtd 또는 lsblk
  • 부트로더 환경변수 정상 작동: fw_printenv
  • Watchdog 타이머(Timer) 설정 확인 (타이머 미설정 시 재부팅 루프)
  • 전원 관리(Power Management)/서스펜드 테스트 (해당하는 경우)

시스템별 U-Boot 커스터마이징

# OpenWrt: U-Boot은 target/linux/*/image/ 에서 관리
# 대부분 장비는 제조사 U-Boot 사용 (교체 비권장)

# Buildroot: boot/uboot/uboot.mk
make menuconfig
# → Bootloaders → U-Boot
#   → U-Boot board name: my_board
#   → U-Boot custom config file: board/my-company/my-board/uboot.config
make uboot-menuconfig          # U-Boot 설정
make uboot-rebuild              # U-Boot만 재빌드

# Yocto: u-boot recipe bbappend
# meta-my-bsp/recipes-bsp/u-boot/u-boot_%.bbappend
UBOOT_MACHINE = "my_board_defconfig"
SRC_URI += "file://0001-add-my-board.patch"
SRC_URI += "file://my-board-env.txt"

Buildroot BSP 추가

# board/my-company/my-board/
board/my-company/my-board/
├── linux.config                 # 커널 defconfig
├── uboot.config                 # U-Boot defconfig (선택)
├── rootfs-overlay/              # 루트FS에 복사될 파일
│   └── etc/
│       ├── network/interfaces
│       └── init.d/S99myapp
├── post-build.sh                # 빌드 후 스크립트
├── post-image.sh                # 이미지 생성 후 스크립트
├── genimage.cfg                 # SD 카드 이미지 레이아웃
└── readme.txt

# configs/my_board_defconfig
BR2_arm=y
BR2_cortex_a7=y
BR2_TOOLCHAIN_EXTERNAL=y
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/my-company/my-board/linux.config"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="board/my-company/my-board/my-board.dts"
BR2_TARGET_ROOTFS_EXT2=y
BR2_ROOTFS_OVERLAY="board/my-company/my-board/rootfs-overlay"
BR2_ROOTFS_POST_BUILD_SCRIPT="board/my-company/my-board/post-build.sh"
BR2_ROOTFS_POST_IMAGE_SCRIPT="board/my-company/my-board/post-image.sh"

Yocto BSP 레이어 생성

# BSP 레이어 구조
meta-my-bsp/
├── conf/
│   ├── layer.conf
│   └── machine/
│       └── my-board.conf
├── recipes-bsp/
│   ├── u-boot/
│   │   └── u-boot_%.bbappend
│   └── my-firmware/
│       └── my-firmware_1.0.bb
├── recipes-kernel/
│   └── linux/
│       ├── linux-yocto_%.bbappend
│       └── files/
│           ├── my-board.cfg
│           └── 0001-add-driver.patch
└── wic/
    └── my-board.wks

# conf/machine/my-board.conf
MACHINE_FEATURES = "serial apm ext2 usbhost wifi"
KERNEL_IMAGETYPE = "zImage"
KERNEL_DEVICETREE = "my-board.dtb"
SERIAL_CONSOLES = "115200;ttyS0"
PREFERRED_PROVIDER_virtual/kernel = "linux-yocto"
IMAGE_FSTYPES = "wic ext4"
WKS_FILE = "my-board.wks"
UBOOT_MACHINE = "my_board_defconfig"

Device Tree 통합

Device Tree는 하드웨어 구성을 커널에 전달하는 데이터 구조로, 임베디드 리눅스 빌드 시스템에서 핵심적인 역할을 합니다. 각 시스템은 DTS(Device Tree Source) 파일의 위치와 빌드 방식이 다릅니다. 상세한 Device Tree 문법과 구조는 Device Tree 문서를 참고하세요.

시스템별 DTS 관리

시스템DTS 위치설정 방법빌드 방법
OpenWrttarget/linux/xxx/dts/자동 (profile에 따라 선택)커널 빌드 시 자동 컴파일
Buildrootboard/*/ 또는 커널 소스 내BR2_LINUX_KERNEL_CUSTOM_DTS_PATHmake linux-rebuild
Yoctometa-*/recipes-kernel/linux/files/KERNEL_DEVICETREE in machine.confbitbake virtual/kernel

DT Overlay 런타임 로딩

# DT Overlay를 런타임에 적용 (ConfigFS 방식)
# 1. 오버레이 컴파일
dtc -I dts -O dtb -o /tmp/my-overlay.dtbo my-overlay.dts

# 2. ConfigFS를 통해 적용
mkdir -p /sys/kernel/config/device-tree/overlays/my-overlay
cat /tmp/my-overlay.dtbo > /sys/kernel/config/device-tree/overlays/my-overlay/dtbo

# 3. 제거
rmdir /sys/kernel/config/device-tree/overlays/my-overlay

# fdtoverlay 도구로 오프라인 병합 (이미지 빌드 시)
fdtoverlay -i base.dtb -o merged.dtb overlay1.dtbo overlay2.dtbo

# U-Boot에서 오버레이 적용
fdt addr ${fdt_addr}
fdt resize 8192
fdt apply ${overlay_addr}

DTS 디버깅

# 컴파일된 DTB 역컴파일
dtc -I dtb -O dts -o decompiled.dts my-board.dtb

# 런타임 DT 확인
cat /sys/firmware/devicetree/base/compatible
ls /sys/firmware/devicetree/base/

# Yocto에서 커스텀 DTS 추가
# recipes-kernel/linux/linux-yocto_%.bbappend
SRC_URI += "file://my-board.dts;subdir=git/arch/arm64/boot/dts/my-vendor"
KERNEL_DEVICETREE += "my-vendor/my-board.dtb"

디버깅 및 개발 워크플로

OpenWrt 개발 도구

SDK와 Image Builder

# SDK: 패키지를 독립적으로 크로스 컴파일
tar xf openwrt-sdk-23.05-ath79-generic_gcc-13.3.0_musl.Linux-x86_64.tar.xz
cd openwrt-sdk-*/
./scripts/feeds update -a && ./scripts/feeds install -a
cp -r my-package package/
make package/my-package/compile V=s

# Image Builder: 사전 빌드된 패키지로 이미지만 재조합
tar xf openwrt-imagebuilder-23.05-ath79-generic.Linux-x86_64.tar.xz
cd openwrt-imagebuilder-*/
make image PROFILE=tplink_tl-wr1043nd-v2 \
  PACKAGES="luci luci-app-openvpn -ppp -ppp-mod-pppoe" \
  FILES=files/

원격 디버깅 (GDB)

# 타겟에서 gdbserver 실행
opkg install gdbserver
gdbserver :9000 /usr/bin/my-app

# 호스트에서 GDB 연결
./staging_dir/toolchain-*/bin/mips-openwrt-linux-musl-gdb
(gdb) target remote 192.168.1.1:9000
(gdb) set sysroot ./staging_dir/target-mips_24kc_musl/
(gdb) break main
(gdb) continue

Buildroot 개발 도구

# 디버그 빌드 활성화 + NFS root
make menuconfig
# → Build options → build packages with debugging symbols

# NFS root로 빠른 반복 개발
# U-Boot: setenv bootargs "root=/dev/nfs nfsroot=192.168.1.100:/path/to/output/target ip=dhcp"

# QEMU 통합 테스트
make qemu_arm_vexpress_defconfig && make
./output/images/start-qemu.sh

Yocto 개발 도구

# devtool: 가장 강력한 개발 도구
devtool modify busybox
# → workspace/sources/busybox/에 소스 체크아웃
devtool build busybox
devtool deploy-target busybox root@192.168.1.100
devtool finish busybox meta-my-layer

# devshell: 빌드 환경에서 직접 작업
bitbake -c devshell busybox

# QEMU 테스트
runqemu qemuarm64 nographic

Valgrind/AddressSanitizer 활용

# Buildroot에서 Valgrind 사용
make menuconfig
# → Target packages → Debugging → valgrind
make
# 타겟에서:
valgrind --leak-check=full /usr/bin/my-app

# AddressSanitizer (GCC -fsanitize=address)
# Buildroot: MYAPP_CONF_OPTS에 추가
MYAPP_CFLAGS = -fsanitize=address -fno-omit-frame-pointer
# 주의: ASan은 ~2x 메모리 오버헤드, 임베디드에서는 RAM 충분한 경우만

# Yocto에서 ASan 활성화
# local.conf:
EXTRA_OECMAKE:append:pn-myapp = " -DSANITIZE_ADDRESS=ON"

커널 크래시 덤프(Dump) 수집

# ramoops: RAM 기반 크래시 로그 (재부팅 후 확인)
# Device Tree에 예약 메모리 추가:
# reserved-memory { ramoops@... { compatible = "ramoops"; ... }; };
# 부팅 후 확인:
cat /sys/fs/pstore/console-ramoops-0   # 이전 부팅의 커널 로그

# OpenWrt에서 로그 확인
logread                # 시스템 로그
dmesg | tail -50       # 커널 링 버퍼

# coredump 수집 (Buildroot/Yocto)
# /proc/sys/kernel/core_pattern에 경로 설정
echo "/tmp/core.%e.%p" > /proc/sys/kernel/core_pattern
ulimit -c unlimited
# 크래시 발생 시 /tmp/core.myapp.1234 생성
# 호스트에서 분석:
arm-buildroot-linux-gnueabihf-gdb output/build/myapp-1.0/myapp /tmp/core.myapp.1234

시리얼 콘솔과 공통 디버깅

# 시리얼 콘솔 연결
picocom -b 115200 /dev/ttyUSB0

# strace로 시스템 콜 추적
strace -f -e trace=network ./my-app

# 네트워크 성능 측정
iperf3 -s           # 서버 모드 (타겟)
iperf3 -c 192.168.1.1 -t 30   # 클라이언트 (호스트)

# 디스크 I/O 성능 측정
dd if=/dev/zero of=/tmp/test bs=1M count=100 oflag=direct

CI/CD 통합 패턴

# Buildroot CI (가장 단순) — .gitlab-ci.yml
build:
  script:
    - make my_board_defconfig
    - make -j$(nproc)
  artifacts:
    paths:
      - output/images/

# OpenWrt CI
build:
  script:
    - ./scripts/feeds update -a && ./scripts/feeds install -a
    - cp my-config .config && make defconfig
    - make download -j8 && make -j$(nproc) V=s

# Yocto CI (sstate-cache 활용이 핵심)
build:
  script:
    - source oe-init-build-env
    - bitbake core-image-minimal
  cache:
    paths:
      - sstate-cache/
      - downloads/

성능 비교 및 선택 가이드

임베디드 빌드 시스템 선택 의사결정 플로차트 임베디드 리눅스 빌드 시스템 선택 라우터/네트워크 장비? Yes OpenWrt opkg, LuCI, UCI, SquashFS No 최소한의 임베디드 시스템? Yes Buildroot 빠른 빌드, 간단한 설정 No 장기 유지보수 기업/산업용? Yes Yocto 레이어, sstate, 벤더 BSP No 런타임 패키지 매니저 필요? Yes OpenWrt 또는 Yocto No Buildroot
그림 12. 임베디드 빌드 시스템 선택 의사결정 플로차트

성능 비교 (동일 하드웨어: 16코어, 64GB RAM, NVMe)

항목OpenWrtBuildrootYocto
첫 빌드 시간45~90분15~45분120~240분
패키지 1개 재빌드1~5분1~5분10초~2분 (sstate)
이미지 재생성2~5분1~3분5~15분
호스트 디스크 사용10~20 GB3~10 GB50~150 GB
호스트 RAM 권장4 GB+2 GB+8 GB+ (16 GB 권장)
최소 이미지 크기~4 MB~2 MB~8 MB
일반 이미지 크기8~16 MB4~32 MB50~500 MB

엔지니어링 비용 분석

항목OpenWrtBuildrootYocto
초기 학습 시간1~2주2~5일2~4주
새 패키지 추가30분~2시간15분~1시간1~4시간
새 보드 BSP1~3일1~2일2~5일
메이저 업그레이드1~3일1~2일3~10일
CI/CD 구축1일반나절2~3일

시스템 간 마이그레이션 경로

마이그레이션 팁:
  • Buildroot → Yocto: 가장 흔한 경로. 프로토타입(Buildroot)에서 양산(Yocto)으로. Buildroot의 패키지 목록(.config)을 Yocto IMAGE_INSTALL로 매핑. 커널 config는 그대로 재사용 가능.
  • OpenWrt → Buildroot: 네트워크 장비에서 범용 장비로. UCI/procd/LuCI 대체 필요. 커널 패치는 Buildroot에서도 사용 가능.
  • Buildroot → OpenWrt: 비권장. OpenWrt의 타겟 지원이 없으면 포팅 비용이 높음.
  • Yocto → Buildroot: 규모 축소 시. recipe를 .mk로 변환하는 수작업 필요.

선택 매트릭스

유스케이스추천이유
WiFi 라우터/APOpenWrtWiFi 드라이버(ath10k, mt76), LuCI, UCI 네트워크 설정
네트워크 스위치OpenWrtDSA(Distributed Switch Architecture) 기본 지원
IoT 센서 게이트웨이Buildroot최소 크기, 빠른 빌드, 단순 커스터마이징
산업용 HMI/PLCBuildroot 또는 YoctoBuildroot(프로토타입), Yocto(양산)
자동차 IVIYoctoAGL(Automotive Grade Linux), meta-ivi
의료 기기Yocto인증, 재현성, 장기 지원
반도체 벤더 SDKYoctoTI, NXP, Intel 공식 BSP 레이어
교육/학습용Buildroot가장 쉬운 학습 곡선, 명확한 구조
셋톱박스/미디어BuildrootKodi, FFmpeg 패키지 지원, 단순 구조
대규모 팀 프로젝트Yocto레이어 분리, 재사용성, 벤더 협업

실전 예제

유스케이스 1: WiFi 라우터 (OpenWrt)

# MediaTek MT7621 기반 라우터 빌드 (OpenWrt 23.05)
git clone -b openwrt-23.05 https://github.com/openwrt/openwrt.git
cd openwrt

./scripts/feeds update -a && ./scripts/feeds install -a

make menuconfig
# Target System: MediaTek Ralink MIPS
# Subtarget: MT7621 based boards
# Target Profile: Xiaomi Mi Router 4A Gigabit Edition
# LuCI → Collections → luci
# Network → VPN → wireguard-tools (선택)

make download -j8 && make -j$(nproc) V=s

ls bin/targets/ramips/mt7621/
# openwrt-23.05-ramips-mt7621-xiaomi_mi-router-4a-gigabit-squashfs-sysupgrade.bin

유스케이스 2: IoT 센서 게이트웨이 (Buildroot)

# Raspberry Pi Zero W 기반 센서 게이트웨이
git clone https://github.com/buildroot/buildroot.git
cd buildroot

make raspberrypi0w_defconfig
make menuconfig
# Target packages → Interpreter languages → python3
# Target packages → Libraries → Networking → libmosquitto (MQTT)
# Filesystem images → ext4 root filesystem

make -j$(nproc)
sudo dd if=output/images/sdcard.img of=/dev/sdX bs=4M

유스케이스 3: 산업용 게이트웨이 (Yocto)

# TI AM62x 기반 산업 게이트웨이 (Yocto scarthgap)
git clone -b scarthgap https://git.yoctoproject.org/poky
git clone -b scarthgap https://git.yoctoproject.org/meta-ti
git clone -b scarthgap https://git.openembedded.org/meta-openembedded

cd poky && source oe-init-build-env build-am62x

bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-ti/meta-ti-bsp

cat >> conf/local.conf <<'EOF'
MACHINE = "am62xx-evm"
DISTRO_FEATURES:append = " systemd wifi"
IMAGE_INSTALL:append = " openssh python3 mosquitto can-utils"
EOF

bitbake core-image-full-cmdline

CI 빌드용 Dockerfile 예시

# Dockerfile.buildroot-ci
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    build-essential gcc g++ binutils patch gawk bzip2 \
    unzip wget curl git file python3 python3-distutils \
    rsync bc cpio libncurses-dev zlib1g-dev libssl-dev \
    cmake perl-modules ca-certificates \
    && rm -rf /var/lib/apt/lists/*

RUN useradd -m builder
USER builder
WORKDIR /home/builder

# 사용법:
# docker build -t buildroot-ci -f Dockerfile.buildroot-ci .
# docker run -v $(pwd)/buildroot:/home/builder/buildroot buildroot-ci \
#   sh -c "cd buildroot && make my_board_defconfig && make -j$(nproc)"

펌웨어 업데이트 전략: A/B 파티션

A/B 파티션 업데이트 흐름 A/B 이중 파티션 업데이트 (SWUpdate/RAUC/Mender) Slot A (현재 활성 — 부팅 중) Kernel A RootFS A (v1.0) 실행 중 — 서비스 정상 동작 부트로더 플래그: active=A Slot B (비활성 — 업데이트 대상) Kernel B RootFS B (v1.0→v2.0) 업데이트 이미지 기록 중... 기존 v1.0 → 새 v2.0 덮어쓰기 1. 업데이트 다운로드 2. 서명 검증 3. Slot B에 기록 4. 부트 플래그 전환 5. Reboot → Slot B로 부팅 부팅 성공 Slot B를 active로 확정 (mark good) 부팅 실패 (Watchdog) 자동 롤백 → Slot A로 복귀 boot_count 초과 시 fallback 자동 롤백
그림 16. A/B 파티션 업데이트 흐름 — 다운로드 → 서명 검증(Signature Verification) → 비활성 슬롯 기록 → 재부팅 → 성공/롤백(Rollback)

펌웨어 업데이트 도구 비교

도구특징시스템 호환
sysupgradeOpenWrt 내장 업그레이드, 설정 보존 가능OpenWrt
SWUpdate이중 복사(A/B) 업데이트, 서명 검증, 웹 UIBuildroot, Yocto
RAUCA/B 슬롯, D-Bus API, Casync 델타 업데이트Yocto (meta-rauc)
MenderOTA 클라우드 서비스, A/B 업데이트, 모니터링Yocto (meta-mender)
OSTreeGit-like 파일시스템 스냅샷, 원자적(Atomic) 업데이트Yocto (meta-updater)
# SWUpdate A/B 업데이트 예시
swupdate -i my-firmware-update.swu

# RAUC 예시
rauc install my-update-bundle.raucb
rauc status   # 슬롯 상태 확인

# Secure Boot 체인 예시 (Yocto + RAUC)
# 1. 인증서 생성: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem
# 2. 번들 서명: rauc bundle --cert=cert.pem --key=key.pem --signing-keyring=ca.pem
# 3. 디바이스에서 검증: /etc/rauc/system.conf에 인증서 경로 설정
보안 주의: 펌웨어 업데이트 시 반드시 서명 검증을 적용하세요. SWUpdate는 CMS 서명, RAUC는 X.509 인증서, Mender는 서버 인증을 지원합니다. 서명 없는 OTA는 중간자 공격(MITM)에 취약합니다.
문서관련 주제
Linux From Scratch (LFS)크로스 컴파일 이론, 툴체인 구축 원리, sysroot 개념
빌드 시스템Makefile, CMake, Autotools, Meson 등 빌드 도구 기초
JFFS2OpenWrt overlay 파일시스템, NOR 플래시 저널링(Journaling)
SquashFSOpenWrt 읽기 전용 루트 파일시스템, 압축 옵션
Device TreeDTS 문법, DTB 컴파일, 오버레이, 바인딩
부팅 과정(Boot Process)U-Boot, 커널 부팅, initramfs, init 시스템
개발 환경 설정크로스 컴파일 환경 구축, 호스트 도구 설정
DSA 태깅OpenWrt 스위치 칩 지원, VLAN 태깅
무선 네트워크WiFi 드라이버(ath10k, mt76), cfg80211, mac80211
GCC크로스 컴파일러 옵션, 타겟 아키텍처 지정
ELF크로스 컴파일된 바이너리 형식, 동적 링킹
UFS/eMMC임베디드 스토리지, 플래시 메모리 관리(Memory Management)
BusyBox멀티콜 바이너리 아키텍처, 애플릿 시스템, init, initramfs 활용