임베디드 리눅스 빌드 시스템 (OpenWrt · Buildroot · Yocto)
임베디드 리눅스 개발의 핵심인 OpenWrt, Buildroot, Yocto/OpenEmbedded 3대 빌드 시스템을 종합 비교합니다. 각 시스템의 빌드 파이프라인(Pipeline), 패키지 인프라, 펌웨어(Firmware) 이미지 구조, 크로스 툴체인 전략, Device Tree 통합, BSP 개발 방법론, SDK 활용, 디버깅(Debugging) 워크플로, 실전 유스케이스까지 임베디드 빌드 시스템의 모든 것을 다룹니다.
핵심 요약
- 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/릴리스 주기 |
|---|---|---|---|
| OpenWrt | YY.MM (년.월) | 23.05, 24.10 | 약 12~18개월 주기, 포인트 릴리스(23.05.4)로 보안 패치(Patch) |
| Buildroot | YYYY.MM (년.월) | 2024.02, 2024.05 | 분기별 릴리스(2/5/8/11), LTS 없음(롤링 안정판) |
| Yocto | 버전 + 코드명 | 5.0 scarthgap, 4.0 kirkstone | LTS 2년 지원(짝수 메이저), 6개월 주기 |
핵심 용어 정리
| 용어 | 설명 | 사용 시스템 |
|---|---|---|
| Feed | 원격 패키지 저장소. feeds.conf.default에 URL을 정의하여 추가 패키지를 가져옴 | OpenWrt |
| Recipe (.bb) | 패키지 빌드 방법을 기술하는 메타데이터 파일. fetch/compile/install 태스크(Task)를 정의 | Yocto |
| Layer (meta-*) | 관련 recipe와 설정을 논리적으로 그룹화한 디렉토리. BSP, 배포판, 기능별로 분리 | Yocto |
| BSP | Board Support Package. 특정 하드웨어에 필요한 커널 설정, DTS, 부트로더, 드라이버 모음 | 모두 |
| Cross-compile | 호스트 아키텍처(x86_64)에서 타겟 아키텍처(ARM, MIPS 등) 바이너리를 생성하는 빌드 방식 | 모두 |
| Sysroot | 크로스 컴파일러가 타겟 시스템의 헤더와 라이브러리를 찾는 최상위 디렉토리 | 모두 |
| Staging | 빌드 과정에서 타겟용 라이브러리와 헤더가 설치되는 임시 디렉토리. 다른 패키지가 의존성 참조에 사용 | 모두 |
| defconfig | 최소 설정 파일. 기본값과 다른 옵션만 기록하며, make xxx_defconfig로 적용 | Buildroot/커널 |
| BitBake | Yocto의 태스크 실행 엔진. recipe를 파싱하고 의존성 그래프에 따라 태스크를 병렬 실행 | Yocto |
| opkg | OpenWrt의 경량 패키지 매니저. IPK 형식 패키지를 설치/제거/업데이트 | OpenWrt |
| UCI | Unified Configuration Interface. OpenWrt 고유의 설정 관리 시스템 (/etc/config/) | OpenWrt |
| procd | OpenWrt의 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) | 비고 |
|---|---|---|---|---|
| OpenWrt | 10 GB | 20 GB+ | 3~5 GB | feeds 포함 시 증가 |
| Buildroot | 3 GB | 10 GB+ | 1~3 GB | 가장 작음; O= 별도 출력 시 추가 |
| Yocto | 50 GB | 100~200 GB | 5~15 GB | sstate-cache가 대부분; SSD 필수 |
-
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단계: 빌드 시스템이 하는 일
임베디드 빌드 시스템은 다음을 자동화합니다:
- 크로스 툴체인 구축 — GCC, Binutils, C 라이브러리(musl/glibc) 빌드
- 패키지 빌드 — 수백~수천 개 패키지를 올바른 순서로 크로스 컴파일
- 루트 파일시스템 생성 — 타겟에 필요한 바이너리/라이브러리/설정 파일 조합
- 커널 빌드 — 타겟 보드에 맞는 커널 설정과 Device Tree 적용
- 펌웨어 이미지 생성 — 부트로더 + 커널 + 루트FS를 플래시 가능한 이미지로 패키징
-
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단계: 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단계: 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대 빌드 시스템 개요 및 비교
역사와 기원
세 시스템은 서로 다른 배경에서 탄생하여 고유한 철학과 강점을 발전시켰습니다.
- Buildroot (2001~) — uClibc 프로젝트의 빌드 인프라로 시작했습니다. Erik Andersen이 uClibc와 BusyBox를 쉽게 빌드할 수 있는 Makefile 기반 시스템을 만든 것이 시초입니다. 2009년 Peter Korsgaard가 메인테이너를 맡으면서 현대적 형태로 발전했습니다. "Keep it simple"을 핵심 철학으로, Make와 Kconfig만으로 전체 시스템을 구성합니다.
- OpenWrt (2004~) — Linksys가 WRT54G 라우터의 GPL 소스를 공개한 것이 계기입니다. 커뮤니티가 이 소스를 기반으로 완전한 라우터 운영체제를 만들기 시작했습니다. Buildroot에서 포크되었으나, 패키지 매니저(opkg), 웹 인터페이스(LuCI), 네트워크 구성 추상화(UCI) 등을 추가하며 독자적 생태계를 구축했습니다. 2016년 LEDE 프로젝트로 분기 후 2018년 재통합되었습니다.
- Yocto Project (2010~) — Linux Foundation이 OpenEmbedded 프로젝트(2003~)의 경험을 바탕으로 산업 표준 빌드 프레임워크를 목표로 출범했습니다. BitBake 빌드 엔진과 OpenEmbedded-Core(OE-Core) 메타데이터를 핵심으로, Intel, AMD, TI, NXP 등 주요 반도체 벤더가 공식 BSP 레이어를 제공합니다.
역사 타임라인 비교
| 연도 | OpenWrt | Buildroot | Yocto/OE |
|---|---|---|---|
| 2000 | uClibc 빌드 인프라 시작 | ||
| 2003 | 독립 프로젝트화 | OpenEmbedded 프로젝트 시작 | |
| 2004 | WRT54G GPL 소스 기반 탄생 | ||
| 2006 | Kamikaze (첫 공식 릴리스) | BitBake 엔진 안정화 | |
| 2009 | Peter Korsgaard 메인테이너 | ||
| 2010 | Backfire | 분기별 릴리스 시작 | Yocto Project 출범 (Linux Foundation) |
| 2012 | Attitude Adjustment | 1.0 Bernard → 1.2 Danny | |
| 2016 | LEDE 프로젝트 분기 | Cargo/Go 인프라 | 2.1 Krogoth |
| 2018 | LEDE와 OpenWrt 재통합 (18.06) | 2.5 Sumo | |
| 2020 | 19.07 → DSA 스위치 지원 | per-package-directories | 3.1 Dunfell (첫 LTS) |
| 2022 | 22.03 (Firewall4/nftables) | Meson 인프라 강화 | 4.0 Kirkstone (LTS) |
| 2024 | 23.05 LTS, 24.10 | 2024.02 (Rust Cargo 개선) | 5.0 Scarthgap (LTS) |
철학 비교
| 관점 | OpenWrt | Buildroot | Yocto |
|---|---|---|---|
| 설계 목표 | 네트워크 어플라이언스 OS | 최소한의 임베디드 시스템 | 유연한 엔터프라이즈 빌드 |
| 핵심 원칙 | 라우터에 완전한 리눅스를 | 단순하게 유지하라 | 재사용성과 확장성 |
| 빌드 도구 | Make + Kconfig | Make + Kconfig | BitBake + OE metadata |
| 패키지 매니저 | opkg (런타임 설치 가능) | 없음 (정적 이미지) | rpm/deb/ipk (선택) |
| 라이선스 | GPL-2.0 | GPL-2.0+ | MIT (Poky/BitBake) |
| 주요 사용처 | 라우터, AP, 스위치, NAS | IoT, 산업용 제어기, STB | 자동차, 항공, 의료, 반도체 벤더 SDK |
상세 비교표
| 항목 | OpenWrt | Buildroot | Yocto |
|---|---|---|---|
| 첫 빌드 시간 | ~1시간 | ~30분 | 2~4시간 |
| 재빌드 시간 | 수 분 (패키지 단위) | 수 분 (패키지 단위) | 수 초~분 (sstate-cache) |
| 최소 이미지 크기 | ~4 MB | ~2 MB | ~8 MB |
| 런타임 패키지 관리 | opkg (필수) | 없음 | rpm/deb/ipk (선택) |
| 기본 init | procd | BusyBox init/systemd/sysvinit | systemd/sysvinit |
| 기본 C 라이브러리 | musl | 선택 (musl/glibc/uclibc-ng) | glibc (기본) |
| 설정 방식 | Kconfig (menuconfig) | Kconfig (menuconfig) | .conf 파일 + BitBake |
| 패키지 수 | 5,000+ | 2,800+ | 수만 (레이어 합산) |
| SDK 제공 | SDK + Image Builder | SDK (output/host) | Standard/Extensible SDK |
| CI/CD 친화성 | 중간 | 높음 (단순) | 매우 높음 (sstate, hash equiv.) |
| 상업적 지원 | 커뮤니티 중심 | 커뮤니티 중심 | Wind River, Mentor, Siemens 등 |
| 주요 벤더 | 라우터/AP 제조사 | STMicro, Microchip | Intel, TI, NXP, AMD, Qualcomm |
| 호스트 디스크 | ~15 GB | ~5 GB | ~50 GB+ |
| 문서 품질 | Wiki 중심, 양호 | 매뉴얼 우수 | 방대 (Mega Manual) |
라이선스 컴플라이언스 워크플로
임베디드 제품의 GPL/LGPL 소스 코드 공개 의무를 처리하는 방법은 시스템마다 다릅니다:
| 시스템 | 라이선스 수집 명령 | 출력물 | 비고 |
|---|---|---|---|
| OpenWrt | make package/xxx/compile 시 자동 | bin/packages/에 소스 포함 가능 | CONFIG_SRC_TREE_OVERRIDE로 소스 보존 |
| Buildroot | make legal-info | output/legal-info/ — 라이선스, 소스 아카이브, 매니페스트 CSV | 가장 체계적; 호스트/타겟 분리, 재배포 소스 자동 수집 |
| Yocto | INHERIT += "archiver" in local.conf | tmp/deploy/sources/, tmp/deploy/licenses/ | ARCHIVER_MODE 옵션으로 패치 포함 원본/수정 소스 선택 |
커뮤니티 지표 비교
| 지표 | OpenWrt | Buildroot | Yocto |
|---|---|---|---|
| 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 빌드 시스템 아키텍처
OpenWrt의 소스 트리는 라우터/네트워크 장비에 특화된 계층 구조를 가집니다.
핵심은 target/(하드웨어 지원), package/(소프트웨어 패키지), feeds/(외부 패키지 저장소) 3개 디렉토리입니다.
주요 디렉토리 역할
| 디렉토리 | 역할 | 핵심 파일 |
|---|---|---|
target/linux/ | 타겟 플랫폼별 커널 설정, 패치, DTS | config-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단계 계층으로 하드웨어를 분류합니다:
- Target — SoC 패밀리 (예:
ath79= Qualcomm Atheros AR7xxx/AR9xxx) - Subtarget — SoC 변형 (예:
generic,nand,tiny) - 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)입니다.
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 빌드 과정
빌드 단계 상세
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 포맷을 사용합니다.
패키지 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
# }
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
# └── ...
OpenWrt 펌웨어 이미지 구조
OpenWrt 펌웨어의 핵심은 SquashFS + JFFS2 오버레이 아키텍처입니다. 읽기 전용(Read-Only) SquashFS 위에 쓰기 가능한 JFFS2 오버레이를 결합하여, 최소 플래시 사용으로 설정 변경과 패키지 설치를 지원합니다.
이미지 유형
| 이미지 유형 | 파일명 패턴 | 용도 |
|---|---|---|
| sysupgrade.bin | *-sysupgrade.bin | 기존 OpenWrt에서 업그레이드용. 설정 보존 가능 |
| factory.bin | *-factory.bin | 제조사 펌웨어에서 최초 설치용. 벤더 헤더 포함 |
| initramfs | *-initramfs-kernel.bin | RAM 디스크 부팅. 디버깅/복구용 (플래시 미사용) |
| rootfs.img | *-squashfs-rootfs.img.gz | SquashFS 루트 이미지만 (별도 커널 필요) |
이미지 헤더 포맷
| 포맷 | 매직 넘버 | 용도 | 구조 |
|---|---|---|---|
| TRX | HDR0 | Broadcom 라우터 (레거시) | 헤더(28B) + kernel + rootfs [+ 추가 파티션] |
| uImage | 27 05 19 56 | U-Boot 레거시 이미지 | 64B 헤더 + 압축 커널 (로드 주소/엔트리 포인트 포함) |
| FIT (ITB) | FDT 구조 | 현대 장비 (권장) | Device Tree 기반 — 다중 커널/DTB/initrd, 서명 지원 |
| UBI | UBI# | 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만으로 부팅
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 — 빌드 후 스크립트
툴체인 선택
| 옵션 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Internal | Buildroot가 직접 GCC+binutils+C lib 빌드 | 완전한 제어, 최신 버전 | 빌드 시간 증가 (~20분) |
| External prebuilt | Linaro, ARM 등 사전 빌드 툴체인 사용 | 빠른 빌드 시작 | 버전 제한 |
| External custom | crosstool-NG 등으로 직접 빌드한 툴체인 | 완전 맞춤화 | 별도 관리 필요 |
Buildroot 빌드 과정
빌드 명령어 상세
# 기본 빌드
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
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)) | 커스텀 Makefile | BusyBox, Linux 커널 |
| autotools | $(eval $(autotools-package)) | configure/make/make install | coreutils, glib |
| cmake | $(eval $(cmake-package)) | CMake | systemd, json-c |
| meson | $(eval $(meson-package)) | Meson + Ninja | pipewire, gstreamer |
| python | $(eval $(python-package)) | setuptools/pip | python-requests |
| golang | $(eval $(golang-package)) | Go modules | containerd, etcd |
| cargo | $(eval $(cargo-package)) | Rust Cargo | ripgrep |
| 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의 레퍼런스 배포판으로, 다음으로 구성됩니다:
- BitBake — 태스크 실행 엔진 (Python/Shell 레시피 파싱, 의존성 해결, 병렬 실행)
- OpenEmbedded-Core (OE-Core) — 핵심 recipe/class/conf (meta/ 디렉토리)
- meta-poky — Poky 배포판 정책 (distro.conf)
- meta-yocto-bsp — 레퍼런스 BSP (QEMU, BeagleBone 등)
DISTRO_FEATURES vs MACHINE_FEATURES vs IMAGE_FEATURES
| 변수 | 설정 위치 | 역할 | 예시 |
|---|---|---|---|
| DISTRO_FEATURES | distro.conf | 배포판 수준 기능 (소프트웨어 정책) | systemd wifi bluetooth ipv6 pam |
| MACHINE_FEATURES | machine.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 BSP | OE 커뮤니티 |
meta-poky | Poky 배포판 정책 | Yocto Project |
meta-openembedded | 추가 패키지 (networking, python, perl 등) | OE 커뮤니티 |
meta-ti-bsp | Texas Instruments BSP (AM335x, AM62x 등) | TI |
meta-raspberrypi | Raspberry Pi BSP | 커뮤니티 |
meta-intel | Intel BSP (x86, Edison 등) | Intel |
meta-freescale | NXP/Freescale BSP (i.MX, Layerscape) | NXP |
meta-security | 보안 도구 (SELinux, IMA, AppArmor) | 커뮤니티 |
meta-virtualization | 컨테이너(Container), 하이퍼바이저 (Docker, KVM) | 커뮤니티 |
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 명령어
# 환경 초기화 (반드시 매 세션마다)
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 이력으로
# - 패키지 목록 변경
# - 파일 크기/권한 변경
# - 의존성 변경 추적
크로스 컴파일 툴체인 비교
임베디드 빌드 시스템의 핵심은 크로스 컴파일 툴체인입니다. 특히 C 라이브러리 선택은 바이너리 크기, 호환성, 성능에 큰 영향을 미칩니다. 크로스 컴파일의 이론적 배경은 LFS 문서를 참고하세요.
ABI 호환성 매트릭스
| ARM 옵션 | 접미사 | 설명 | 호환성 |
|---|---|---|---|
| Hard-float (VFPv3) | gnueabihf / musleabihf | 하드웨어 FPU 사용, 부동소수점 인자를 FPU 레지스터(Register)로 전달 | Cortex-A7+ 필수, 가장 빠름 |
| Soft-float | gnueabi / musleabi | 소프트웨어 부동소수점 에뮬레이션 | FPU 없는 프로세서, 느림 |
| AArch64 | aarch64-linux-gnu | 64비트 ARM, 항상 하드웨어 FP | ARMv8-A+ |
- 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 커널 모듈 패턴
| 시스템 | 패턴 | 예시 |
|---|---|---|
| OpenWrt | package/kernel/my-module/Makefile + KernelPackage | define KernelPackage/my-module ... FILES:=$(LINUX_DIR)/drivers/my-module.ko endef |
| Buildroot | package/my-module/my-module.mk + kernel-module infra | $(eval $(kernel-module)) $(eval $(generic-package)) |
| Yocto | inherit module in recipe | inherit 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
.cfg fragment를, Buildroot에서는 BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES를,
OpenWrt에서는 config-* 파일에 delta만 기록하는 방식을 권장합니다.
커스텀 보드 지원 (BSP)
새로운 하드웨어 보드를 빌드 시스템에 통합하려면 BSP(Board Support Package)를 작성해야 합니다. BSP는 부트로더, 커널 설정, Device Tree, 보드별 드라이버, 플래시 레이아웃을 포함합니다.
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 위치 | 설정 방법 | 빌드 방법 |
|---|---|---|---|
| OpenWrt | target/linux/xxx/dts/ | 자동 (profile에 따라 선택) | 커널 빌드 시 자동 컴파일 |
| Buildroot | board/*/ 또는 커널 소스 내 | BR2_LINUX_KERNEL_CUSTOM_DTS_PATH | make linux-rebuild |
| Yocto | meta-*/recipes-kernel/linux/files/ | KERNEL_DEVICETREE in machine.conf | bitbake 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/
성능 비교 및 선택 가이드
성능 비교 (동일 하드웨어: 16코어, 64GB RAM, NVMe)
| 항목 | OpenWrt | Buildroot | Yocto |
|---|---|---|---|
| 첫 빌드 시간 | 45~90분 | 15~45분 | 120~240분 |
| 패키지 1개 재빌드 | 1~5분 | 1~5분 | 10초~2분 (sstate) |
| 이미지 재생성 | 2~5분 | 1~3분 | 5~15분 |
| 호스트 디스크 사용 | 10~20 GB | 3~10 GB | 50~150 GB |
| 호스트 RAM 권장 | 4 GB+ | 2 GB+ | 8 GB+ (16 GB 권장) |
| 최소 이미지 크기 | ~4 MB | ~2 MB | ~8 MB |
| 일반 이미지 크기 | 8~16 MB | 4~32 MB | 50~500 MB |
엔지니어링 비용 분석
| 항목 | OpenWrt | Buildroot | Yocto |
|---|---|---|---|
| 초기 학습 시간 | 1~2주 | 2~5일 | 2~4주 |
| 새 패키지 추가 | 30분~2시간 | 15분~1시간 | 1~4시간 |
| 새 보드 BSP | 1~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 라우터/AP | OpenWrt | WiFi 드라이버(ath10k, mt76), LuCI, UCI 네트워크 설정 |
| 네트워크 스위치 | OpenWrt | DSA(Distributed Switch Architecture) 기본 지원 |
| IoT 센서 게이트웨이 | Buildroot | 최소 크기, 빠른 빌드, 단순 커스터마이징 |
| 산업용 HMI/PLC | Buildroot 또는 Yocto | Buildroot(프로토타입), Yocto(양산) |
| 자동차 IVI | Yocto | AGL(Automotive Grade Linux), meta-ivi |
| 의료 기기 | Yocto | 인증, 재현성, 장기 지원 |
| 반도체 벤더 SDK | Yocto | TI, NXP, Intel 공식 BSP 레이어 |
| 교육/학습용 | Buildroot | 가장 쉬운 학습 곡선, 명확한 구조 |
| 셋톱박스/미디어 | Buildroot | Kodi, 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 파티션
펌웨어 업데이트 도구 비교
| 도구 | 특징 | 시스템 호환 |
|---|---|---|
| sysupgrade | OpenWrt 내장 업그레이드, 설정 보존 가능 | OpenWrt |
| SWUpdate | 이중 복사(A/B) 업데이트, 서명 검증, 웹 UI | Buildroot, Yocto |
| RAUC | A/B 슬롯, D-Bus API, Casync 델타 업데이트 | Yocto (meta-rauc) |
| Mender | OTA 클라우드 서비스, A/B 업데이트, 모니터링 | Yocto (meta-mender) |
| OSTree | Git-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에 인증서 경로 설정
관련 문서
| 문서 | 관련 주제 |
|---|---|
| Linux From Scratch (LFS) | 크로스 컴파일 이론, 툴체인 구축 원리, sysroot 개념 |
| 빌드 시스템 | Makefile, CMake, Autotools, Meson 등 빌드 도구 기초 |
| JFFS2 | OpenWrt overlay 파일시스템, NOR 플래시 저널링(Journaling) |
| SquashFS | OpenWrt 읽기 전용 루트 파일시스템, 압축 옵션 |
| Device Tree | DTS 문법, DTB 컴파일, 오버레이, 바인딩 |
| 부팅 과정(Boot Process) | U-Boot, 커널 부팅, initramfs, init 시스템 |
| 개발 환경 설정 | 크로스 컴파일 환경 구축, 호스트 도구 설정 |
| DSA 태깅 | OpenWrt 스위치 칩 지원, VLAN 태깅 |
| 무선 네트워크 | WiFi 드라이버(ath10k, mt76), cfg80211, mac80211 |
| GCC | 크로스 컴파일러 옵션, 타겟 아키텍처 지정 |
| ELF | 크로스 컴파일된 바이너리 형식, 동적 링킹 |
| UFS/eMMC | 임베디드 스토리지, 플래시 메모리 관리(Memory Management) |
| BusyBox | 멀티콜 바이너리 아키텍처, 애플릿 시스템, init, initramfs 활용 |
- OpenWrt 공식 문서 (openwrt.org)
- OpenWrt 개발자 가이드 (openwrt.org)
- OpenWrt 빌드 시스템 사용법 (openwrt.org)
- OpenWrt 빌드 시스템 핵심 개념 (openwrt.org)
- OpenWrt 패키지 개발 가이드 (openwrt.org)
- Buildroot 사용자 매뉴얼 (buildroot.org)
- Buildroot 문서 및 교육 자료 (buildroot.org)
- Yocto Project 문서 포털 (yoctoproject.org)
- Yocto Project 빠른 시작 가이드 (yoctoproject.org)
- Yocto 개발자 매뉴얼 (yoctoproject.org)
- BitBake 사용자 매뉴얼 (yoctoproject.org)
- Yocto 커널 개발 매뉴얼 (yoctoproject.org)
- Yocto BSP 개발자 가이드 (yoctoproject.org)
- OpenEmbedded Layer Index (openembedded.org)
- OpenEmbedded 위키 (openembedded.org)
- 커널 Device Tree 문서 (kernel.org)
- Device Tree 사양서 (devicetree.org)
- U-Boot 공식 문서 (u-boot.org)
- Embedded Linux Wiki (elinux.org)
- 크로스 컴파일 툴체인 개요 (elinux.org)
- crosstool-NG 문서 — 크로스 툴체인 빌더 (crosstool-ng.github.io)
- kas — Yocto 프로젝트 설정 자동화 도구 (github.com)
- SWUpdate — 임베디드 OTA 업데이트 (sbabic.github.io)
- RAUC — 안전한 펌웨어 업데이트 프레임워크 (rauc.io)