PXE (Preboot Execution Environment)

PXE(Preboot Execution Environment)는 네트워크를 통해 운영체제를 부팅하는 업계 표준 기술입니다. 이 문서에서는 PXE의 역사와 스펙 구조부터 DHCP/BOOTP 프로토콜 상세, TFTP/HTTP 파일 전송, PXE ROM과 UNDI 드라이버, NBP(Network Bootstrap Program) 체인로딩, iPXE 고급 스크립팅, UEFI PXE/HTTP Boot, PXELINUX/GRUB 네트워크 부트로더, iSCSI/NFS 루트 파일시스템, 대규모 프로비저닝(Cobbler/MAAS/Foreman/FOG), Secure PXE Boot, 리눅스 커널 네트워크 부팅 파라미터, 디스크리스(diskless) 부팅 구성, 그리고 트러블슈팅까지 네트워크 부팅의 모든 것을 심층 분석합니다.

전제 조건: 부팅 과정UEFI 문서를 먼저 읽으세요. PXE는 펌웨어 단계에서 시작되므로, BIOS/UEFI 부팅 흐름을 이해해야 합니다. 네트워크 기초가 필요하다면 네트워킹 개요도 참고하세요.
일상 비유: PXE는 "전화 한 통으로 배달시키는 부팅"입니다. 컴퓨터(손님)가 네트워크(전화)로 "나 부팅할 파일 줘!"라고 요청하면, DHCP 서버(교환원)가 "여기로 가세요"라고 안내하고, TFTP 서버(배달원)가 부팅 파일을 전달합니다. 로컬 디스크 없이도 운영체제를 실행할 수 있습니다.

핵심 요약

  • PXE — Intel이 1999년에 제정한 네트워크 부팅 표준. 펌웨어(BIOS/UEFI)에 내장된 PXE ROM이 DHCP+TFTP로 부트 파일을 받아 실행합니다.
  • DHCP Option 66/67 — 실무에서 많이 쓰는 TFTP 서버/부트 파일 전달 수단입니다. 다만 장애 분석 시에는 siaddr, file, Option 66/67, ProxyDHCP 응답을 함께 봐야 합니다.
  • NBP — Network Bootstrap Program. PXELINUX, GRUB, iPXE 등 네트워크를 통해 전달되는 1차 부트로더입니다.
  • iPXE — 오픈소스 PXE 구현. HTTP/HTTPS, iSCSI, Wi-Fi, 스크립팅 등 레거시 PXE의 한계를 넘는 기능을 제공합니다.
  • UEFI HTTP Boot — UEFI가 정의한 현대적 네트워크 부팅 경로입니다. TFTP 대신 HTTP/HTTPS URI로 EFI 이미지를 전달합니다.

단계별 이해

  1. 펌웨어가 NIC를 깨운다
    BIOS는 PXE Option ROM과 UNDI를, UEFI는 SNP/MNP와 PXE Base Code 또는 HTTP Boot 스택을 사용해 네트워크를 초기화합니다.
  2. DHCP로 "어디서 무엇을 받을지" 묻는다
    클라이언트는 IP 주소만 받는 것이 아니라, 부트 파일 이름, 다음 서버, 아키텍처 코드, 벤더 클래스까지 함께 협상합니다.
  3. 첫 번째 로더(NBP)를 가져온다
    레거시 PXE는 주로 TFTP로 pxelinux.0 같은 NBP를 가져오고, UEFI HTTP Boot는 HTTP URI로 shimx64.efigrubx64.efi를 받습니다.
  4. NBP가 커널과 initrd를 로드한다
    PXELINUX, GRUB, iPXE가 커널 파라미터를 조합하고, 필요하면 HTTP, iSCSI, NFS 같은 더 고급 프로토콜로 2차 리소스를 가져옵니다.
  5. 진짜 난이도는 rootfs부터 시작된다
    네트워크 부팅 성공은 1차 로더 전달로 끝나지 않습니다. initramfs 안의 NIC 드라이버, NFS/iSCSI 재연결, Secure Boot 체인, 자동 설치 스크립트가 최종 성공을 좌우합니다.

실무 선택 가이드

현장에서 "PXE"라는 말을 넓게 쓰는 경우가 많지만, 실제로는 Legacy PXE, UEFI PXE, UEFI HTTP Boot, iPXE 체인로딩이 서로 다른 설계 선택지입니다. 무엇을 고를지는 펌웨어 모드, Secure Boot 요구사항, 이미지 크기, 운영 자동화 수준, 루트 파일시스템 방식에 따라 달라집니다.

검토 기준 (2026-03-07): 이 문서는 Intel PXE 2.1, RFC 2131/2132/4578/5970/7440, UEFI 2.11, IANA Processor Architecture Types 레지스트리(2026-02-02 갱신), iPXE 공식 문서를 기준으로 사실관계를 다시 점검해 보강했습니다.
환경우선 선택이유주의점
구형 서버, BIOS 전용PXELINUX 또는 undionly.kpxe가장 폭넓은 호환성. 기존 PXE ROM이 바로 이해 가능TFTP 성능이 낮고, Secure Boot 개념이 없음
BIOS지만 대형 이미지, 동적 메뉴, iSCSI 필요iPXE 체인로딩첫 단계만 TFTP, 이후는 HTTP/HTTPS/iSCSI로 전환 가능DHCP에서 iPXE 무한 루프를 반드시 차단해야 함
UEFI + 표준 네트워크 부팅만 필요UEFI PXE펌웨어 기본 기능만으로 구현 가능여전히 TFTP 중심이라 대규모 동시 부팅에는 비효율적
UEFI + 큰 EFI 이미지 + 빠른 전송UEFI HTTP BootBoot URI를 직접 전달하므로 웹 서버 인프라 활용이 쉬움펌웨어별 HTTP/HTTPS/TLS 구현 편차가 큼
UEFI + Secure Boot 필수shim + GRUB 또는 서명된 iPXE서명 검증 체인과 배포판 기본 신뢰 체계를 재사용 가능서명되지 않은 ipxe.efi는 기본적으로 거부될 수 있음
호스트별 분기, API 연동, 메타데이터 기반 부팅iPXE 스크립트MAC/UUID/Serial/Platform 기반 동적 정책 구현에 가장 유연DHCP보다 HTTP API 계층의 가용성과 인증이 더 중요해짐
진짜 디스크리스 운영PXE/iPXE + NFS root 또는 iSCSI SAN boot로컬 디스크 없이 운영체제 유지 가능부팅보다 initramfs, 드라이버 내장, rootfs 재연결이 더 어렵다
BIOS/UEFI 혼재 환경Option 93 분기 + iPXE 혼합하나의 DHCP 정책에서 아키텍처별 NBP를 정교하게 나눌 수 있음IANA 아키텍처 코드와 HTTP Boot 코드를 정확히 관리해야 함
용어 주의: 실무에서는 "PXE 서버"가 TFTP 서버, HTTP Boot 서버, iPXE 스크립트 서버, 자동 설치 서버를 통칭하는 경우가 많습니다. 그러나 표준 문서 기준으로는 Intel PXE 2.1UEFI HTTP Boot는 별개의 메커니즘이며, 장애 분석 시 이 둘을 구분해야 문제 지점을 빠르게 찾을 수 있습니다.
펌웨어와 요구사항에 따른 네트워크 부팅 선택 시작: 이 장비는 어떤 펌웨어인가? BIOS / UEFI / Secure Boot / 이미지 크기 / 운영 자동화 요구사항 Legacy BIOS NIC Option ROM + UNDI DHCP + TFTP 중심 UEFI SNP/MNP + PXE/HTTP Boot .efi 이미지 실행 BIOS에서 단순 설치만 필요 PXELINUX / EXTLINUX 계열 장점: 최고 호환성 약점: TFTP 병목, 보안 약함 고속 전송/동적 정책 필요 iPXE 체인로딩 BIOS: undionly.kpxe / UEFI: ipxe.efi HTTP/HTTPS/iSCSI/API 연동에 최적 UEFI PXE 표준 TFTP 경로 가장 단순 속도는 느림 HTTP Boot URI 직접 전달 웹 인프라 활용 펌웨어 편차 큼 모든 경로의 최종 난이도는 initramfs, rootfs 재연결, Secure Boot 서명, 자동 설치 체인에 있다

PXE 역사와 스펙 진화

역사적 타임라인

네트워크 부팅의 계보: PXE 이전에도 여러 네트워크 부팅 기술이 있었습니다.
연도기술설명
1981BOOTP (RFC 951)최초의 네트워크 부팅 프로토콜. UDP 67/68 포트 사용. 정적 매핑 기반
1985RARP (RFC 903)Reverse ARP. MAC→IP 역변환. 간단하지만 라우터 통과 불가
1993DHCP (RFC 1531)BOOTP 확장. 동적 IP 할당, 옵션 확장 가능
1997DHCP Options (RFC 2132)Option 66(TFTP server), 67(bootfile) 등 부팅 관련 옵션 표준화
1999PXE 2.1 (Intel)Intel이 제정한 업계 표준. UNDI API, PXE Base Code, ProxyDHCP 정의
2006gPXEEtherboot 후속. HTTP, iSCSI, DNS 지원 추가
2010iPXEgPXE 포크. HTTPS, Wi-Fi, 스크립팅, SAN boot 등 현대적 기능
2015UEFI HTTP BootUEFI 2.5 스펙에 HTTP(S) 부팅 공식 포함
2020+UEFI HTTPS Boot + RedfishHTTPS 기반 부팅과 BMC/Redfish 자동화가 결합된 운영 패턴 확산

PXE 2.1 스펙 구조

Intel PXE 스펙 2.1은 다음 컴포넌트로 구성됩니다:

컴포넌트역할상세
PXE Base Code핵심 프로토콜 엔진DHCP/TFTP/UDP/IP 스택. BIOS에서는 16비트 리얼모드 API
UNDIUniversal Network Driver InterfaceNIC 하드웨어 추상화. PXE API가 NIC를 제어하는 표준 인터페이스
PXE Boot Server부팅 서버 프로토콜Boot Server Discovery Protocol. 여러 부팅 서버 중 선택
ProxyDHCPDHCP 보조 서버기존 DHCP 변경 없이 PXE 옵션만 추가 전달
BISBoot Integrity Services부팅 파일 무결성 검증 (실무에서는 거의 미사용)
PXE 2.1 스펙 계층 구조 NBP (Network Bootstrap Program) PXELINUX / GRUB / iPXE / Windows Boot Manager PXE Base Code API PXENV_TFTP_READ, PXENV_UDP_WRITE ... Boot Server Discovery ProxyDHCP / Boot Menu / BIS DHCP/BOOTP IP 할당 + 부팅 정보 TFTP 파일 전송 (UDP 69) UDP/IP 네트워크 스택 ARP/RARP 주소 해석 UNDI (Universal Network Driver Interface) PXENV_UNDI_OPEN / PXENV_UNDI_TRANSMIT / PXENV_UNDI_ISR — NIC 하드웨어 추상화 NIC Hardware (Option ROM / UEFI Driver) Intel I210/I350, Realtek RTL8111, Broadcom BCM57xx, Mellanox ConnectX ...

PXE 부팅 전체 흐름

4단계 부팅 과정

PXE 부팅은 크게 4단계로 진행됩니다: (1) DHCP 디스커버리(2) NBP 다운로드(3) 커널/initrd 로드(4) OS 부팅

PXE 부팅 전체 시퀀스 (BIOS Legacy) PXE Client DHCP Server TFTP Server NFS/HTTP Phase 1: DHCP Discovery (DORA) DHCPDISCOVER (broadcast) Option 60: Vendor Class, Option 93: arch DHCPOFFER IP + next-server + filename DHCPREQUEST DHCPACK Option 66: tftp-server, Option 67: pxelinux.0 Phase 2: NBP (Network Bootstrap Program) 다운로드 TFTP Read Request: pxelinux.0 TFTP Data: NBP 바이너리 전송 (512B blocks) PXE가 NBP를 0x7C00(16bit) 또는 지정 주소에 로드 → 실행 Phase 3: 커널 + initrd 다운로드 TFTP: pxelinux.cfg/default (설정 파일) 설정 파일 반환 (KERNEL, APPEND, INITRD 지시어) TFTP: vmlinuz (커널 이미지) TFTP: initrd.img (초기 RAM 디스크) NBP가 커널+initrd를 메모리에 배치 → 커널 엔트리 점프 Phase 4: OS 부팅 (커널 → rootfs) 커널 시작: start_kernel() → 네트워크 드라이버 초기화 DHCP (커널 ip= 파라미터) IP 재할당 NFS mount / HTTP fetch / iSCSI login (root= 파라미터) Root 파일시스템 마운트 pivot_root / switch_root → /sbin/init 실행 네트워크 부팅 완료!

상세 단계별 분석

Step 1: 전원 투입 → PXE ROM 실행

  1. BIOS POST 완료 후 부팅 장치 순회
  2. NIC의 Option ROM(PXE ROM) 발견 → 시그니처 0x55AA 확인
  3. PXE ROM이 메모리에 로드되어 INT 19h 부팅 벡터 설정
  4. PXE Base Code 초기화: UNDI 드라이버 로드 → NIC 활성화

Step 2: DHCP 4-Way Handshake (DORA)

Client                          Server
  |                               |
  |--- DHCPDISCOVER (broadcast) --|  src: 0.0.0.0:68 → 255.255.255.255:67
  |    Option 60: PXEClient:Arch  |  Option 93: Client System Architecture
  |    Option 94: Client NDI      |  Option 97: Client Machine Identifier (UUID)
  |                               |
  |------ DHCPOFFER --------------|  서버 → 클라이언트 (unicast 또는 broadcast)
  |    yiaddr: 192.168.1.100      |  할당할 IP 주소
  |    siaddr: 192.168.1.1        |  TFTP 서버 주소 (next-server)
  |    file: "pxelinux.0"         |  부트 파일명
  |                               |
  |--- DHCPREQUEST ---------------|  선택한 서버에 IP 요청 확인
  |                               |
  |------ DHCPACK ----------------|  IP 할당 확정 + 모든 옵션 전달
  |                               |

Step 3: NBP 다운로드 및 실행

  1. PXE Base Code가 TFTP 클라이언트를 실행
  2. siaddr(next-server)에서 file(filename) 다운로드
  3. NBP를 메모리 0x7C00(리얼모드) 또는 지정 주소에 로드
  4. PXE API 테이블 포인터를 레지스터에 설정한 후 NBP로 점프

Step 4: NBP가 커널 로드

  1. NBP(PXELINUX 등)가 PXE API를 통해 설정 파일 요청
  2. 설정 파일에 지정된 커널(vmlinuz)과 initrd 다운로드
  3. 커널 커맨드 라인 설정 (예: root=/dev/nfs nfsroot=...)
  4. Protected Mode 전환 후 커널 엔트리 포인트로 점프

DHCP/BOOTP 프로토콜 심화

DHCP 패킷 구조

DHCP는 BOOTP(RFC 951)를 기반으로 확장된 프로토콜(RFC 2131)입니다. PXE 부팅에서 핵심적인 패킷 구조:

/* DHCP/BOOTP 패킷 구조 (RFC 2131) */
struct dhcp_packet {
    uint8_t  op;          /* 1=BOOTREQUEST, 2=BOOTREPLY */
    uint8_t  htype;       /* 하드웨어 타입: 1=Ethernet */
    uint8_t  hlen;        /* 하드웨어 주소 길이: 6 (MAC) */
    uint8_t  hops;        /* 릴레이 에이전트 홉 수 */
    uint32_t xid;         /* 트랜잭션 ID */
    uint16_t secs;        /* 클라이언트 시작 후 경과 시간 */
    uint16_t flags;       /* bit0=Broadcast flag */
    uint32_t ciaddr;      /* Client IP (이미 할당된 경우) */
    uint32_t yiaddr;      /* Your IP (서버가 할당하는 IP) */
    uint32_t siaddr;      /* Server IP (next-server = TFTP 서버) */
    uint32_t giaddr;      /* Gateway IP (릴레이 에이전트) */
    uint8_t  chaddr[16];  /* Client hardware address (MAC) */
    char     sname[64];   /* Server host name (옵션) */
    char     file[128];   /* Boot file name (NBP 경로) */
    uint8_t  options[];   /* DHCP 옵션 (magic cookie 0x63825363 시작) */
}; /* 최소 크기: 300 bytes, 일반적으로 576 bytes */

PXE 관련 핵심 DHCP 옵션

옵션 번호이름용도예시 값
60Vendor Class IdentifierPXE 클라이언트 식별"PXEClient:Arch:00000:UNDI:002001"
66TFTP Server NameTFTP 서버 호스트명/IP"192.168.1.1"
67Bootfile NameNBP 파일 경로"pxelinux.0" 또는 "grubx64.efi"
93Client System Architecture클라이언트 아키텍처0x0000(x86 BIOS), 0x0007(x64 UEFI), 0x000B(ARM64 UEFI)
94Client Network InterfaceUNDI 버전 정보UNDI:003:010 (v3.10)
97Client Machine IdentifierUUID/GUIDSMBIOS에서 가져온 시스템 UUID
128-135PXE Vendor Options벤더별 확장멀티캐스트 부팅, 자격 증명 등
175iPXE EncapsulatediPXE 전용 옵션iPXE 스크립트 URL 등
209PXELINUX Config설정 파일 경로"pxelinux.cfg/default"
210PXELINUX PathprefixTFTP 경로 접두사"/tftpboot/linux/"

DHCP 필드 우선순위와 해석

실무에서 가장 많이 헷갈리는 부분은 "부트 파일 정보가 정확히 어디에 들어가느냐"입니다. RFC 2132 관점에서 Option 66/67은 sname/file 필드를 다른 용도로 오버로드했을 때의 보조 수단이지만, 실제 PXE/UEFI 펌웨어는 BOOTP 헤더와 DHCP 옵션을 함께 해석합니다.

위치일반적 의미실무 포인트
siaddrnext-server, 부트 서버 주소레거시 PXE에서 가장 먼저 확인할 필드 중 하나입니다.
file 헤더 필드NBP 이름 또는 Boot URIUEFI HTTP Boot에서는 여기 자체에 http://... URI가 들어갈 수 있습니다.
Option 66TFTP Server Namesname를 다른 목적으로 쓸 때 보조적으로 전달됩니다. 일부 서버는 관행적으로 항상 같이 보냅니다.
Option 67Bootfile Namefile 필드를 대체하거나 보완합니다. HTTP Boot에서는 URI를 담는 구현도 흔합니다.
Option 60Vendor ClassPXEClientHTTPClient를 구분해 BIOS/UEFI/HTTP Boot 정책을 나눕니다.
ProxyDHCP 응답PXE 전용 부가 정보동일한 DORA 흐름처럼 보이지만, 실제로는 DHCP 서버 응답과 합쳐서 해석됩니다.
패킷 분석 규칙: "Option 67이 없으니 부팅 파일이 없다"라고 단정하면 안 됩니다. siaddr, file, Option 66/67, ProxyDHCP(4011) 응답을 모두 캡처해서 비교해야 정확합니다.

아키텍처 자동 감지와 부트파일 분기

DHCP Option 93을 사용하여 클라이언트 아키텍처를 감지하고, 적절한 부트 파일을 제공합니다. 아래 값은 현장에서 자주 보게 되는 대표값이며, 최종 권위는 RFC 4578의 초기 표보다 IANA Processor Architecture Types 레지스트리에 있습니다.

Option 93 값아키텍처권장 부트파일
0x0000 (0)x86 BIOS (IA-32)pxelinux.0 또는 undionly.kpxe
0x0006 (6)x86 UEFI (32-bit)syslinux.efi32 또는 grubia32.efi
0x0007 (7)x64 UEFI (64-bit)grubx64.efi 또는 shimx64.efi
0x0009 (9)EFI Byte Code (EBC)ipxe.efi 또는 EBC 지원 EFI 애플리케이션
0x000A (10)ARM 32-bit UEFIgrubarm.efi
0x000B (11)ARM64 UEFI (AArch64)grubaa64.efi
0x000F (15)x86 UEFI HTTP Boothttp://server/boot/bootia32.efi
0x0010 (16)x64 UEFI HTTP Boothttp://server/boot/shimx64.efi
0x0013 (19)ARM64 UEFI HTTP Boothttp://server/boot/shimaa64.efi
0x0019 (25)RISC-V 32-bit UEFIgrubriscv32.efi
0x001B (27)RISC-V 64-bit UEFIgrubriscv64.efi
분기 규칙: HTTP Boot는 아키텍처 값만으로 충분하지 않습니다. Option 93=0x0010 같은 HTTP Boot 코드와 함께 Vendor Class가 HTTPClient인지도 확인해야, TFTP용 .efi와 HTTP URI를 잘못 섞는 사고를 막을 수 있습니다.

ISC DHCP 서버 PXE 설정 예제

# /etc/dhcp/dhcpd.conf - PXE 부팅을 위한 ISC DHCP 설정

# 전역 옵션
option arch-type code 93 = unsigned integer 16;

subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    option domain-name-servers 8.8.8.8;
    next-server 192.168.1.10;    # TFTP 서버 주소

    # 아키텍처별 부트파일 분기
    if option arch-type = 00:07 {
        # x64 UEFI 클라이언트
        filename "grubx64.efi";
    } elsif option arch-type = 00:0B {
        # ARM64 UEFI 클라이언트
        filename "grubaa64.efi";
    } elsif option arch-type = 00:00 {
        # Legacy BIOS 클라이언트
        filename "pxelinux.0";
    }

    # 특정 MAC 주소에 고정 IP 할당
    host server01 {
        hardware ethernet 00:11:22:33:44:55;
        fixed-address 192.168.1.50;
        filename "pxelinux.0";
    }
}

dnsmasq PXE 설정 예제

# /etc/dnsmasq.conf - dnsmasq PXE 부팅 설정 (DHCP + TFTP 통합)

# DHCP 범위
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h

# 내장 TFTP 서버 활성화
enable-tftp
tftp-root=/var/lib/tftpboot

# PXE 아키텍처별 부트파일 분기
# tag:x86PC  = BIOS (arch 0)
# tag:EFI_x64 = UEFI x64 (arch 7)
# tag:EFI_AA64 = UEFI ARM64 (arch 11)
dhcp-match=set:x86PC,option:client-arch,0
dhcp-match=set:EFI_x64,option:client-arch,7
dhcp-match=set:EFI_AA64,option:client-arch,11

dhcp-boot=tag:x86PC,pxelinux.0
dhcp-boot=tag:EFI_x64,grubx64.efi
dhcp-boot=tag:EFI_AA64,grubaa64.efi

# ProxyDHCP 모드 (기존 DHCP 서버가 있을 때)
# dhcp-range=192.168.1.0,proxy
# pxe-service=x86PC,"PXE Boot",pxelinux
# pxe-service=X86-64_EFI,"PXE Boot UEFI",grubx64.efi

# iPXE 체인로딩: 일반 PXE는 iPXE를 로드, iPXE는 스크립트를 로드
dhcp-match=set:ipxe,175  # iPXE sends option 175
dhcp-boot=tag:!ipxe,undionly.kpxe
dhcp-boot=tag:ipxe,http://192.168.1.10/boot.ipxe

# DHCP 로깅
log-dhcp

ProxyDHCP 메커니즘

ProxyDHCP란? 기존 DHCP 서버(IP 할당)를 수정하지 않고, 별도 ProxyDHCP 서버가 PXE 부팅 옵션(filename, next-server)만 추가로 전달하는 메커니즘입니다. 대기업 네트워크에서 DHCP 관리자와 PXE 관리자가 다를 때 유용합니다.
ProxyDHCP 동작 흐름 PXE Client 기존 DHCP Server (IP 할당만) ProxyDHCP Server (PXE 옵션만, port 4011) 1. DHCPDISCOVER (broadcast, option 60: PXEClient) 2. DHCPOFFER (IP 할당, PXE 옵션 없음) 3. ProxyDHCP OFFER (IP=0.0.0.0, next-server + filename 포함) 4. DHCPREQUEST → DHCPACK 5. PXE Boot Server Request (port 4011, unicast) 6. Boot Server ACK (최종 filename + next-server) → 이후 TFTP로 NBP 다운로드 진행
ProxyDHCP 핵심 포인트:
- 기존 DHCP 서버의 IP 할당을 방해하지 않음
- ProxyDHCP는 yiaddr=0.0.0.0으로 응답 (IP를 할당하지 않음)
- PXE 클라이언트는 두 응답을 모두 수신하여 병합
- ProxyDHCP는 port 67 (broadcast) 또는 port 4011 (unicast) 사용
- dnsmasq의 dhcp-range=...,proxy 옵션으로 간단히 구성 가능

DHCP 옵션 바이너리 인코딩

DHCP 옵션은 TLV(Type-Length-Value) 형식으로 인코딩됩니다. PXE 관련 옵션의 실제 바이트 구조를 이해하면 패킷 분석과 트러블슈팅에 도움됩니다.

DHCP 옵션 TLV 바이너리 인코딩 Magic Cookie (4 bytes): 0x63 0x82 0x53 0x63 ← DHCP 옵션 시작 마커 Option 53 (DHCP Message Type): Type: 53 (0x35) Len: 1 (0x01) Value: 1 DISCOVER Option 60 (Vendor Class Identifier): Type: 60 (0x3C) Len: 32 (0x20) "PXEClient:Arch:00000:UNDI:002001" Option 93 (Client System Architecture): Type: 93 (0x5D) Len: 2 (0x02) 0x00 0x07 x64 UEFI ← 서버가 이 값을 보고 grubx64.efi 제공 Option 97 (Client Machine Identifier - UUID): Type: 97 (0x61) Len: 17 (0x11) Type:0 b8 94 59 08 d6 a6 41 a9 61 1d 74 a6 ab 80 b8 3d (SMBIOS UUID) End Option: 0xFF (255) ← 옵션 목록 종료 (Length 필드 없음) Type Length Value ※ Type 0(Pad)과 255(End)는 Length 필드 없음

BOOTP vs DHCP vs PXE 패킷 비교

세 프로토콜은 동일한 패킷 구조를 공유하지만, 사용하는 필드와 옵션이 다릅니다:

필드/기능BOOTP (RFC 951)DHCP (RFC 2131)PXE (Intel 2.1)
패킷 구조300 bytes 고정576+ bytes (옵션 확장)DHCP + 벤더 확장
op 필드1(req)/2(reply)동일동일
siaddr부팅 서버 IPnext-serverTFTP 서버 (Option 66 우선)
file (128B)부트 파일명부트 파일명NBP 경로 (Option 67 우선)
IP 할당정적 매핑만동적 풀 + 정적DHCP 방식 사용
옵션 영역64 bytes (vendor)가변 (magic cookie 이후)DHCP + PXE 전용 옵션
Magic Cookie없음0x63825363동일
메시지 타입없음Option 53동일
아키텍처 감지불가불가 (표준)Option 93 (arch type)
UUID 전달불가불가 (표준)Option 97 (UUID)
ProxyDHCP불가불가port 4011 유니캐스트
재시도단순 타임아웃지수 백오프DHCP 방식 + PXE 타임아웃
/* BOOTP와 DHCP의 옵션 영역 차이 */

/* BOOTP: 64바이트 vendor-specific 영역 (RFC 951) */
struct bootp_packet {
    /* ... 공통 필드 ... */
    uint8_t  vend[64];    /* vendor-specific area */
};
/* 매우 제한적: 서브넷 마스크, 게이트웨이 정도만 전달 가능 */

/* DHCP: 가변 길이 옵션 영역 (RFC 2131) */
struct dhcp_packet {
    /* ... 공통 필드 (BOOTP와 동일) ... */
    uint8_t  options[];   /* 가변 길이, magic cookie 0x63825363로 시작 */
};
/* Magic Cookie 다음에 TLV 형식 옵션이 이어짐:
 * [Type:1B][Length:1B][Value:N bytes] ...
 * 마지막에 0xFF(End) 옵션
 *
 * PXE 확장:
 * - Option 60: "PXEClient:Arch:NNNNN:UNDI:MMMMMM"
 * - Option 93: 2바이트 아키텍처 코드 (0=BIOS, 7=x64UEFI, 11=ARM64)
 * - Option 94: UNDI 버전 (major.minor)
 * - Option 97: 1바이트 타입 + 16바이트 UUID (SMBIOS)
 * - Option 175: iPXE 캡슐화 옵션
 */

TFTP 프로토콜 심화

TFTP 개요

TFTP(Trivial File Transfer Protocol, RFC 1350)는 PXE의 기본 파일 전송 프로토콜입니다. UDP 기반으로 매우 단순하지만, 그만큼 한계가 있습니다.

특성TFTPFTPHTTP
전송 계층UDP (포트 69)TCP (포트 20/21)TCP (포트 80/443)
인증없음사용자/비밀번호다양 (Basic, Token, ...)
블록 크기512B (기본), 최대 65464B가변가변
최대 파일 크기~32MB (512B블록), ~4GB (확장)무제한무제한
디렉토리 목록불가가능가능
보안없음TLS (FTPS)TLS (HTTPS)
PXE 지원모든 PXE ROM미지원iPXE, UEFI HTTP Boot

TFTP 패킷 타입

/* TFTP 패킷 타입 (RFC 1350) */
#define TFTP_RRQ   1   /* Read Request  - 파일 읽기 요청 */
#define TFTP_WRQ   2   /* Write Request - 파일 쓰기 요청 (PXE에서 미사용) */
#define TFTP_DATA  3   /* Data          - 데이터 블록 전송 */
#define TFTP_ACK   4   /* Acknowledgment - 데이터 수신 확인 */
#define TFTP_ERROR 5   /* Error         - 오류 메시지 */
#define TFTP_OACK  6   /* Option ACK    - 옵션 협상 응답 (RFC 2347) */

/* RRQ/WRQ 패킷 구조 */
struct tftp_rrq {
    uint16_t opcode;     /* 1 (RRQ) */
    char     filename[]; /* NUL 종료 문자열, 예: "pxelinux.0\0" */
    char     mode[];     /* "octet\0" (바이너리 모드) */
    /* 이하 옵션들 (RFC 2347): "blksize\0" "1468\0" "tsize\0" "0\0" */
};

/* DATA 패킷 구조 */
struct tftp_data {
    uint16_t opcode;     /* 3 (DATA) */
    uint16_t block;      /* 블록 번호 (1부터 시작) */
    uint8_t  data[];     /* 데이터 (최대 blksize 바이트) */
};
/* 마지막 블록: data 길이 < blksize → 전송 완료 */

TFTP 확장 옵션 (RFC 2347-2349)

옵션RFC설명기본값권장값
blksizeRFC 2348블록 크기 (8-65464)5121468 (MTU 최적화)
tsizeRFC 2349전송 크기 (바이트)미지원요청: 0 → 응답: 실제 크기
timeoutRFC 2349재전송 타임아웃 (초)미정의1-5
windowsizeRFC 7440윈도우 크기 (블록 수)14-64 (성능 향상)
성능 주의: 기본 TFTP(512B 블록, 윈도우 1)는 매우 느립니다. 100MB 커널 이미지를 전송하면 약 200,000번의 ACK 왕복이 필요합니다. blksize=1468windowsize=64를 설정하면 전송 효율이 큰 폭으로 개선됩니다. 대규모 환경에서는 HTTP 기반 부팅(iPXE/UEFI HTTP Boot)을 권장합니다.
TFTP 파일 전송 흐름 (블록 크기 협상 포함) Client :69 TFTP Server :69 RRQ "pxelinux.0" octet blksize=1468 tsize=0 OACK blksize=1468 tsize=42780 ACK block#0 (OACK 확인) DATA block#1 (1468 bytes) ACK block#1 ... (반복) ... DATA block#30 (312 bytes < 1468 = 마지막!) ACK block#30 → 전송 완료 총 42,780 bytes = 29 × 1468 + 312 bytes (30 블록)

TFTP 서버 구성

# tftpd-hpa 설치 및 설정 (Debian/Ubuntu)
sudo apt install tftpd-hpa

# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create --verbose --blocksize 1468"

# 디렉토리 구조
/var/lib/tftpboot/
├── pxelinux.0              # BIOS NBP (SYSLINUX 패키지)
├── lpxelinux.0             # BIOS NBP + HTTP 지원
├── ldlinux.c32             # SYSLINUX 필수 모듈
├── libutil.c32             # SYSLINUX 유틸리티
├── menu.c32                # 텍스트 메뉴 모듈
├── vesamenu.c32            # 그래픽 메뉴 모듈
├── libcom32.c32            # COM32 라이브러리
├── grubx64.efi             # UEFI x64 NBP
├── grubaa64.efi            # UEFI ARM64 NBP
├── shimx64.efi             # Secure Boot shim
├── undionly.kpxe           # iPXE chainload 이미지
├── ipxe.efi                # iPXE UEFI 이미지
├── pxelinux.cfg/           # PXELINUX 설정 디렉토리
│   ├── default             # 기본 설정 파일
│   ├── C0A80164            # IP 기반 (192.168.1.100 = C0A80164)
│   └── 01-00-11-22-33-44-55  # MAC 기반 설정
├── grub/                   # GRUB 네트워크 설정
│   └── grub.cfg
├── vmlinuz                 # Linux 커널
├── initrd.img              # initramfs
└── images/                 # OS 설치 이미지
    ├── ubuntu/
    ├── rhel/
    └── debian/

# TFTP 서비스 시작
sudo systemctl enable --now tftpd-hpa

# 방화벽 설정
sudo firewall-cmd --add-service=tftp --permanent
sudo firewall-cmd --reload

TFTP 윈도우 크기별 전송 비교

RFC 7440의 windowsize 옵션은 TFTP 성능에 극적인 영향을 줍니다. 윈도우 크기가 1(기본)이면 매 블록마다 ACK를 기다려야 하지만, 윈도우 크기를 늘리면 여러 블록을 연속 전송할 수 있습니다.

TFTP 윈도우 크기별 전송 효율 비교 windowsize=1 (기본) Client Server DATA #1 ACK #1 DATA #2 ACK #2 DATA #3 ACK #3 DATA #4 ACK #4 ... (반복) ... RTT × N회 대기 100MB ÷ 1468B = 68,119 왕복! windowsize=4 Client Server DATA #1 DATA #2 DATA #3 DATA #4 ACK #4 DATA #5 DATA #6 DATA #7 DATA #8 ACK #8 ... (반복) ... RTT × (N÷4)회 대기 100MB ÷ 1468B ÷ 4 = 17,030 왕복 (4× 빠름)
# TFTP 서버별 windowsize 설정

# tftpd-hpa: windowsize 직접 지원 안함 (blksize만 가능)
# → 대안: atftpd 또는 dnsmasq 사용

# atftpd: windowsize 및 멀티캐스트 지원
sudo apt install atftpd
# /etc/default/atftpd
OPTIONS="--daemon --port 69 --tftpd-timeout 300 \
  --retry-timeout 5 --maxthread 100 \
  --bind-address 0.0.0.0 \
  --logfile /var/log/atftpd.log \
  --windowsize 64 --blksize 1468 \
  /var/lib/tftpboot"

# dnsmasq: TFTP windowsize 지원 (v2.87+)
# /etc/dnsmasq.d/tftp.conf
enable-tftp
tftp-root=/var/lib/tftpboot
# blksize는 지원하지만 windowsize는 클라이언트 협상에 의존

# iPXE 클라이언트의 TFTP 옵션 협상
# iPXE는 자동으로 blksize=1468, windowsize를 협상
# PXE ROM은 보통 blksize=512, windowsize=1만 지원

# 성능 측정 방법
# 서버에서 전송 속도 모니터링
sudo tcpdump -i eth0 -c 100 port 69 -w /tmp/tftp.pcap
# Wireshark에서 Statistics → I/O Graphs로 처리량 확인

UNDI 드라이버와 PXE ROM

UNDI 개요

UNDI(Universal Network Driver Interface)는 PXE 스펙에서 정의한 NIC 하드웨어 추상화 계층입니다. NBP가 특정 NIC 드라이버 없이도 네트워크 I/O를 수행할 수 있게 합니다.

UNDI API 함수설명
PXENV_UNDI_STARTUPUNDI 드라이버 초기화
PXENV_UNDI_CLEANUPUNDI 드라이버 정리
PXENV_UNDI_INITIALIZENIC 하드웨어 초기화
PXENV_UNDI_OPENNIC 열기 (패킷 수신 시작)
PXENV_UNDI_CLOSENIC 닫기
PXENV_UNDI_TRANSMIT패킷 전송
PXENV_UNDI_SET_PACKET_FILTER수신 필터 설정
PXENV_UNDI_GET_INFORMATIONNIC 정보 조회 (MAC, IRQ, ...)
PXENV_UNDI_GET_STATISTICS전송/수신 통계
PXENV_UNDI_ISR인터럽트 서비스 루틴
PXENV_UNDI_GET_NIC_TYPENIC 타입 (PCI vendor/device ID)
PXENV_UNDI_GET_IFACE_INFO인터페이스 정보 (속도, 이름)

PXE Option ROM 구조

/* PCI Option ROM 헤더 (BIOS) */
struct option_rom_header {
    uint16_t signature;     /* 0x55AA - ROM 시그니처 */
    uint8_t  rom_size;      /* ROM 크기 (512바이트 단위) */
    uint8_t  init_entry[4]; /* 초기화 엔트리 포인트 (JMP 명령) */
    uint8_t  reserved[17];  /* 예약 */
    uint16_t pci_data_ptr;  /* PCI Data Structure 오프셋 */
    uint16_t pnp_header_ptr;/* PnP Expansion Header 오프셋 */
};

/* PXE ROM 초기화 흐름 (16-bit Real Mode):
 *
 * 1. BIOS가 PCI 버스를 스캔하여 NIC의 Option ROM BAR 발견
 * 2. ROM을 메모리 C000h-DFFFFh 영역에 매핑
 * 3. signature 0x55AA 확인 후 init_entry 호출
 * 4. PXE ROM이 UNDI 드라이버 설치
 * 5. INT 1Ah (PCI BIOS) 연결, INT 18h/19h 부팅 벡터 후킹
 * 6. BIOS 부팅 순서에서 네트워크 부팅 선택 시:
 *    → PXE Base Code 시작 → DHCP → TFTP → NBP 실행
 */

PXE API 호출 메커니즘

; NBP에서 PXE API 호출 예제 (16-bit Real Mode Assembly)
; PXE API는 !PXE 또는 PXENV+ 구조체를 통해 호출

; !PXE 구조체 찾기 (ES:BX에 전달됨)
; BX = !PXE 구조체의 오프셋

; PXENV_TFTP_READ_FILE 호출 예제
mov bx, PXENV_TFTP_READ_FILE   ; 함수 번호: 0x0023
mov di, tftp_read_params        ; 파라미터 구조체 오프셋
push ds
push di
push bx
call far [pxe_entry_point]      ; !PXE EntryPointSP
add sp, 6
; AX = 반환값 (0 = 성공)

; 파라미터 구조체
tftp_read_params:
    .status:     dw 0            ; 반환 상태
    .filename:   times 128 db 0  ; 파일명
    .buffersize: dd 0            ; 버퍼 크기
    .bufferaddr: dd 0            ; 버퍼 세그먼트:오프셋
    .serverip:   dd 0            ; 서버 IP
    .gatewayip:  dd 0            ; 게이트웨이 IP
    .mcastip:    dd 0            ; 멀티캐스트 IP
    .tftpport:   dw 69           ; TFTP 포트
    .tftpblksize: dw 512         ; 블록 크기
UNDI 드라이버 스택과 NBP 상호작용 NBP (PXELINUX / iPXE) PXE Base Code API TFTP_READ / UDP_WRITE / ... PXE 부가 API GET_CACHED_INFO / DISCOVERY UNDI Driver (Universal Network Driver Interface) OPEN / CLOSE / TRANSMIT / ISR / GET_NIC_TYPE H/W UNDI (Option ROM) NIC 벤더 제공 펌웨어 드라이버 S/W UNDI 소프트웨어 전용 드라이버 (iPXE) NIC Hardware (Intel I210, Realtek RTL8169, Broadcom BCM57xx, ...)

PXE 리얼 모드 메모리 맵

BIOS PXE 부팅 시 16-bit 리얼 모드의 메모리 레이아웃은 NBP 개발과 트러블슈팅에 핵심적인 정보입니다. PXE ROM, UNDI 드라이버, NBP 각각이 특정 메모리 영역을 사용합니다.

PXE 리얼 모드 (16-bit) 메모리 맵 ... 확장 메모리 (1MB 이상) ... 0x100000 BIOS ROM (F0000-FFFFF) 0xF0000 PXE Option ROM 영역 (C0000-DFFFF) PXE ROM + UNDI 드라이버 시그니처: 0x55AA, PCI Data Structure 0xC0000 VGA 프레임 버퍼 (A0000-BFFFF) 0xA0000 UNDI 코드/데이터 세그먼트 PXE Base Code 런타임 (가변 위치) ~0x90000 !PXE / PXENV+ 구조체 PXE API 진입점, UNDI 데이터 세그먼트 포인터 NBP 로드 영역 pxelinux.0 / undionly.kpxe 등 최대 ~512KB (리얼 모드 제한) ~0x7C00 스택, 부트 섹터 영역 0x0600 IVT + BDA (0000-04FF) 0x0000 NBP 엔트리 포인트: ES:BX → !PXE 구조체 포인터 SS:SP → 유효한 스택 설정 CS:IP → 0000:7C00 (실행 시작) !PXE 구조체 (40 bytes): Signature: "!PXE" EntryPointSP: far call 진입점 UNDIDataSeg/UNDICodeSeg UEFI PXE의 차이점: UEFI 모드에서는 리얼 모드 메모리 맵이 적용되지 않습니다. EFI 부트 서비스가 메모리를 관리하고, NBP는 PE32+ EFI Application으로 로드됩니다. EFI_PXE_BASE_CODE_PROTOCOL과 EFI_SIMPLE_NETWORK_PROTOCOL을 통해 네트워크 접근합니다.
/* !PXE 구조체 (PXE 2.1 Spec) - NBP가 PXE API에 접근하는 핵심 */
struct pxe_t {
    uint8_t  Signature[4];       /* "!PXE" */
    uint8_t  StructLength;       /* 구조체 크기 */
    uint8_t  StructCksum;        /* 체크섬 (전체 합=0) */
    uint8_t  StructRev;          /* 구조체 리비전 */
    uint8_t  reserved1;
    SEGOFF16 UNDIROMID;          /* UNDI ROM ID 구조체 포인터 */
    SEGOFF16 BaseROMID;          /* BC ROM ID 구조체 포인터 */
    SEGOFF16 EntryPointSP;       /* API 진입점 (스택 파라미터) */
    SEGOFF16 EntryPointESP;      /* API 진입점 (확장 스택) */
    SEGOFF16 StatusCallout;      /* 콜아웃 함수 포인터 */
    uint8_t  reserved2;
    uint8_t  SegDescCnt;         /* 세그먼트 디스크립터 수 */
    SEGSEL   FirstSelector;      /* UNDI 코드/데이터 세그먼트 */
    /* ... 세그먼트 디스크립터들 ... */
};

/* PXENV+ 구조체 (하위 호환용, PXE 2.0 이전) */
struct pxenv_plus {
    uint8_t  Signature[6];       /* "PXENV+" */
    uint16_t Version;            /* PXE 버전 (예: 0x0201) */
    uint8_t  Length;
    uint8_t  Checksum;
    SEGOFF16 RMEntry;            /* 리얼 모드 API 진입점 */
    uint32_t PMOffset;           /* 프로텍티드 모드 오프셋 */
    uint16_t PMSelector;         /* 프로텍티드 모드 셀렉터 */
    uint16_t StackSeg;           /* 스택 세그먼트 */
    uint16_t StackSize;          /* 스택 크기 */
    /* ... */
};

/* NBP에서 !PXE 구조체 탐색 순서:
 * 1. ES:BX 레지스터 확인 (BIOS가 전달)
 * 2. "!PXE" 시그니처 검증
 * 3. 실패 시 "PXENV+" 탐색 (0x9FC00~0xA0000)
 * 4. 체크섬 검증 (전체 바이트 합 = 0)
 */

PXELINUX 부트로더

PXELINUX 개요

PXELINUX는 SYSLINUX 프로젝트의 일부로, PXE 환경에서 가장 널리 사용되는 NBP입니다.

변종파일특징
pxelinux.0~40KB기본 BIOS PXE 부트로더. TFTP만 지원
lpxelinux.0~90KBHTTP, FTP, TFTP 지원. DNS 해석 가능
syslinux.efi~200KBUEFI 모드 PXE 부트로더

설정 파일 탐색 순서

PXELINUX는 다음 순서로 설정 파일을 탐색합니다 (첫 번째 발견 파일 사용):

클라이언트 정보:
  UUID:  b8945908-d6a6-41a9-611d-74a6ab80b83d
  MAC:   01-88-99-aa-bb-cc-dd
  IP:    192.168.1.100 (hex: C0A80164)

PXELINUX 설정 파일 탐색 순서:
  1. pxelinux.cfg/b8945908-d6a6-41a9-611d-74a6ab80b83d   ← UUID
  2. pxelinux.cfg/01-88-99-aa-bb-cc-dd                    ← MAC (01- 접두사)
  3. pxelinux.cfg/C0A80164                                 ← 전체 IP (hex)
  4. pxelinux.cfg/C0A8016                                  ← IP 앞 7자리
  5. pxelinux.cfg/C0A801                                   ← IP 앞 6자리
  6. pxelinux.cfg/C0A80                                    ← IP 앞 5자리
  7. pxelinux.cfg/C0A8                                     ← 서브넷 (Class B)
  8. pxelinux.cfg/C0A                                      ← 앞 3자리
  9. pxelinux.cfg/C0                                       ← 앞 2자리
 10. pxelinux.cfg/C                                        ← 앞 1자리
 11. pxelinux.cfg/default                                  ← 기본 설정

→ 이 메커니즘으로 호스트별/서브넷별/전체 기본 설정을 계층적으로 관리할 수 있습니다.

PXELINUX 설정 파일 예제

# /var/lib/tftpboot/pxelinux.cfg/default

# 전역 설정
DEFAULT vesamenu.c32        # 그래픽 메뉴 사용
PROMPT 0                    # 자동 메뉴 진입
TIMEOUT 300                 # 30초 타임아웃 (1/10초 단위)
ONTIMEOUT local             # 타임아웃 시 로컬 부팅

# 메뉴 테마
MENU TITLE PXE Boot Menu
MENU BACKGROUND pxelinux.cfg/splash.png
MENU COLOR border  30;44  #40ffffff #a0000000 std
MENU COLOR title   1;36;44 #9033ccff #a0000000 std
MENU COLOR sel     7;37;40 #e0ffffff #20ffffff all

# 로컬 디스크 부팅
LABEL local
    MENU LABEL Boot from ^Local Disk
    MENU DEFAULT
    LOCALBOOT 0

# Ubuntu 24.04 LTS 설치
LABEL ubuntu2404
    MENU LABEL ^Ubuntu 24.04 LTS Install
    KERNEL images/ubuntu/vmlinuz
    INITRD images/ubuntu/initrd
    APPEND root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://192.168.1.10/ubuntu-24.04-live-server-amd64.iso autoinstall ds=nocloud-net;s=http://192.168.1.10/autoinstall/

# RHEL 10 계열 설치 (Kickstart)
LABEL rhel10
    MENU LABEL ^RHEL 10 Kickstart Install
    KERNEL images/rhel/vmlinuz
    INITRD images/rhel/initrd.img
    APPEND inst.repo=http://192.168.1.10/rhel10/ inst.ks=http://192.168.1.10/ks/rhel10.cfg ip=dhcp

# Debian 13 설치 (Preseed)
LABEL debian13
    MENU LABEL ^Debian 13 Trixie Netinstall
    KERNEL images/debian/linux
    INITRD images/debian/initrd.gz
    APPEND auto=true priority=critical url=http://192.168.1.10/preseed/debian13.cfg interface=auto vga=788

# 디스크리스 리눅스 (NFS Root)
LABEL diskless
    MENU LABEL ^Diskless Linux (NFS Root)
    KERNEL vmlinuz-diskless
    INITRD initrd-diskless.img
    APPEND root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4,tcp ip=dhcp rw

# Memtest86+
LABEL memtest
    MENU LABEL ^Memtest86+
    KERNEL memtest86+

# iPXE 체인로딩
LABEL ipxe
    MENU LABEL ^iPXE Advanced Boot
    KERNEL ipxe.lkrn
    APPEND dhcp && chain http://192.168.1.10/boot.ipxe

PXELINUX COM32 모듈

모듈용도
menu.c32텍스트 기반 부팅 메뉴
vesamenu.c32VESA 그래픽 부팅 메뉴 (배경 이미지 지원)
chain.c32디스크/파티션 체인로딩
memdiskISO/플로피/하드 디스크 이미지를 RAM에서 부팅
pxechn.c32다른 PXE 서버로 체인로딩
reboot.c32시스템 재부팅
poweroff.c32시스템 종료 (APM/ACPI)
hdt.c32하드웨어 정보 표시
ifcpu64.c32CPU 아키텍처(32/64비트) 감지 분기
linux.c32Linux 커널 직접 로드

GRUB 네트워크 부팅

GRUB PXE 개요

GRUB2는 UEFI 환경에서 가장 널리 사용되는 네트워크 부트로더입니다. PXELINUX보다 기능이 풍부하고 UEFI Secure Boot를 지원합니다.

GRUB 네트워크 설정

# /var/lib/tftpboot/grub/grub.cfg - GRUB 네트워크 부팅 설정

set default=0
set timeout=30

# 변수 설정
set pxe_server="192.168.1.10"
set nfs_server="192.168.1.10"

# 메뉴 테마
loadfont unicode
set gfxmode=auto
insmod all_video
insmod gfxterm
terminal_output gfxterm

menuentry "Boot from Local Disk" {
    exit
}

menuentry "Ubuntu 24.04 LTS Install" {
    linux (tftp)/$pxe_server/images/ubuntu/vmlinuz \
        ip=dhcp \
        url=http://$pxe_server/ubuntu-24.04-live-server-amd64.iso \
        autoinstall ds=nocloud-net\;s=http://$pxe_server/autoinstall/
    initrd (tftp)/$pxe_server/images/ubuntu/initrd
}

menuentry "RHEL 10 Kickstart Install" {
    linuxefi (tftp)/images/rhel/vmlinuz \
        inst.repo=http://$pxe_server/rhel10/ \
        inst.ks=http://$pxe_server/ks/rhel10.cfg \
        ip=dhcp
    initrdefi (tftp)/images/rhel/initrd.img
}

menuentry "Diskless Linux (NFS Root)" {
    linux (tftp)/vmlinuz-diskless \
        root=/dev/nfs \
        nfsroot=$nfs_server:/exports/diskless,vers=4,tcp \
        ip=dhcp rw
    initrd (tftp)/initrd-diskless.img
}

menuentry "iPXE Chainload" {
    chainloader (tftp)/ipxe.efi
}

GRUB EFI 네트워크 이미지 빌드

# GRUB UEFI 네트워크 부트 이미지 생성
# 필요한 모듈을 포함한 standalone EFI 이미지

# x86_64 UEFI
grub-mknetdir --net-directory=/var/lib/tftpboot --subdir=/grub

# 또는 수동으로 standalone 이미지 생성
grub-mkimage -O x86_64-efi \
    -o grubx64.efi \
    -p '(tftp)/grub' \
    -d /usr/lib/grub/x86_64-efi \
    efinet tftp http net normal linux linuxefi \
    all_video boot configfile echo part_gpt part_msdos \
    search search_fs_uuid search_label reboot halt

# ARM64 UEFI
grub-mkimage -O arm64-efi \
    -o grubaa64.efi \
    -p '(tftp)/grub' \
    -d /usr/lib/grub/arm64-efi \
    efinet tftp http net normal linux \
    all_video boot configfile echo

# Secure Boot을 사용할 경우 shim 체인로딩
# shimx64.efi → grubx64.efi 순서로 로드
cp /usr/lib/shim/shimx64.efi.signed /var/lib/tftpboot/shimx64.efi
cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /var/lib/tftpboot/grubx64.efi

iPXE 고급 부팅

iPXE 개요

iPXE는 gPXE의 후속 프로젝트로, 레거시 PXE의 한계를 극복한 오픈소스 네트워크 부트 펌웨어입니다.

기능레거시 PXEiPXE
프로토콜TFTP만HTTP, HTTPS, iSCSI, AoE, FCoE, TFTP, FTP, NFS
DNS미지원지원
스크립팅없음내장 스크립트 언어
Wi-Fi미지원WPA2 지원 (실험적)
VLAN일부 NIC만완전 지원
SAN Boot미지원iSCSI, AoE, FCoE, Infiniband SRP
암호화없음HTTPS/TLS 지원 (공식 문서 기준 TLSv1.0-1.2)
인증없음HTTP Basic/Digest, 클라이언트 인증서
Embedded Script없음ROM에 스크립트 내장 가능
무선 네트워크미지원802.11 지원 (일부 칩셋)

iPXE 체인로딩 전략

iPXE 체인로딩: PXE ROM → iPXE → HTTP Boot NIC PXE ROM DHCP → TFTP (기본 PXE 기능만) undionly.kpxe iPXE 코어 로드 UNDI 드라이버 재사용 iPXE Script HTTP/HTTPS로 다운로드 메뉴, 조건 분기, 루프 Linux Kernel vmlinuz + initrd HTTP로 고속 전송 무한 루프 방지 (핵심!): 1. PXE ROM이 DHCP하면 → 서버가 undionly.kpxe 전달 (iPXE가 아닌 클라이언트) 2. iPXE가 DHCP하면 → user-class="iPXE" 또는 Option 175로 식별 → boot.ipxe 전달 dnsmasq: dhcp-match=set:ipxe,175 / dhcp-boot=tag:!ipxe,undionly.kpxe / dhcp-boot=tag:ipxe,http://server/boot.ipxe

체인로딩 바이너리 선택

펌웨어권장 이미지의미언제 바꾸는가
Legacy BIOSundionly.kpxe펌웨어가 제공한 UNDI 드라이버를 재사용기본 PXE ROM 호환성을 최대화하고 싶을 때
Legacy BIOSipxe.pxeiPXE가 자체 NIC 드라이버 사용펌웨어 UNDI 구현이 불안정하거나 기능이 부족할 때
UEFIipxe.efi일반적인 UEFI 체인로딩용 EFI 애플리케이션가장 보편적인 UEFI iPXE 진입점
UEFIsnponly.efiUEFI SNP 드라이버를 재사용펌웨어 NIC 드라이버는 안정적이지만 iPXE 내장 드라이버 충돌을 피하고 싶을 때
실무 팁: iPXE 공식 DHCP 예제는 BIOS 환경에서 undionly.kpxe, UEFI 환경에서 ipxe.efi를 기본 체인로딩 대상으로 제시합니다. UEFI에서 특정 NIC 드라이버 문제가 있으면 snponly.efi가 더 잘 맞는 경우가 있습니다.

iPXE 스크립트 예제

#!ipxe
# boot.ipxe - iPXE 메인 부팅 스크립트

# 변수 설정
set boot-server http://192.168.1.10
set menu-timeout 30000
set submenu-timeout ${menu-timeout}

# 네트워크 초기화
dhcp || goto netboot_failed
echo IP: ${ip} / Gateway: ${gateway} / DNS: ${dns}

# 아키텍처 감지
iseq ${platform} efi && goto efi_boot ||
goto bios_boot

:efi_boot
set boot-prefix ${boot-server}/efi
goto start

:bios_boot
set boot-prefix ${boot-server}/bios
goto start

:start
menu iPXE Boot Menu - ${ip}
item --gap --             -- OS Installation --
item ubuntu               Ubuntu 24.04 LTS
item rhel                 RHEL 10 계열
item debian               Debian 13 Trixie
item --gap --             -- Utilities --
item diskless             Diskless Linux (NFS)
item iscsi                iSCSI SAN Boot
item memtest              Memtest86+
item --gap --             -- Other --
item shell                iPXE Shell
item reboot               Reboot
item exit                 Exit to BIOS/UEFI
choose --default ubuntu --timeout ${menu-timeout} selected
goto ${selected}

:ubuntu
kernel ${boot-server}/images/ubuntu/vmlinuz
initrd ${boot-server}/images/ubuntu/initrd
imgargs vmlinuz ip=dhcp url=${boot-server}/iso/ubuntu-24.04.iso autoinstall ds=nocloud-net;s=${boot-server}/autoinstall/
boot || goto failed

:rhel
kernel ${boot-server}/images/rhel/vmlinuz
initrd ${boot-server}/images/rhel/initrd.img
imgargs vmlinuz inst.repo=${boot-server}/rhel10/ inst.ks=${boot-server}/ks/rhel10.cfg ip=dhcp
boot || goto failed

:debian
kernel ${boot-server}/images/debian/linux
initrd ${boot-server}/images/debian/initrd.gz
imgargs linux auto=true priority=critical url=${boot-server}/preseed/debian13.cfg interface=auto
boot || goto failed

:diskless
kernel ${boot-server}/vmlinuz-diskless
initrd ${boot-server}/initrd-diskless.img
imgargs vmlinuz-diskless root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4 ip=dhcp rw
boot || goto failed

:iscsi
# iSCSI SAN 부팅
set iscsi-server 192.168.1.20
set iscsi-target iqn.2024-01.com.example:storage.lun0
sanhook iscsi:${iscsi-server}::::${iscsi-target} || goto failed
# iSCSI LUN에서 부팅
sanboot iscsi:${iscsi-server}::::${iscsi-target} || goto failed

:memtest
chain ${boot-server}/memtest86+.bin || goto failed

:shell
echo iPXE Shell. Type 'exit' to return to menu.
shell
goto start

:reboot
reboot

:exit
exit

:failed
echo Boot failed. Retrying in 5 seconds...
sleep 5
goto start

:netboot_failed
echo Network initialization failed!
echo Falling back to local boot...
sleep 3
exit 1

iPXE 고급 스크립팅

#!ipxe
# advanced-boot.ipxe - 고급 기능 데모

# === 환경 변수 활용 ===
echo Platform: ${platform}      # pcbios 또는 efi
echo BuildArch: ${buildarch}    # i386 또는 x86_64 또는 arm64
echo MAC: ${mac}                # xx:xx:xx:xx:xx:xx
echo Serial: ${serial}          # 시리얼 번호
echo UUID: ${uuid}              # SMBIOS UUID
echo Manufacturer: ${manufacturer}
echo Product: ${product}
echo Chip: ${chip}              # NIC 칩셋

# === 조건 분기 ===
# MAC 주소 기반 분기
iseq ${mac} 00:11:22:33:44:55 && goto special_host ||

# 서브넷 기반 분기
isset ${netX/gateway} && goto has_gateway || goto no_gateway

# 제조사 기반 분기
iseq ${manufacturer} "Dell Inc." && set vendor dell ||
iseq ${manufacturer} "HP" && set vendor hp ||
iseq ${manufacturer} "Lenovo" && set vendor lenovo ||
set vendor generic

# === HTTPS + 인증 ===
set cert-server https://pxe.example.com
# 클라이언트 인증서 (빌드 시 내장)
# imgfetch ${cert-server}/cert/${uuid}.pem

# === 동적 메뉴 (서버에서 생성) ===
# 서버가 MAC/UUID/arch에 따라 동적 스크립트 생성
chain ${cert-server}/api/boot?mac=${mac:hexhyp}&arch=${buildarch}&uuid=${uuid}&platform=${platform} ||
echo Dynamic menu failed, falling back...

# === VLAN 태깅 ===
# vcreate --tag 100 net0
# dhcp net0-100

# === 루프와 재시도 ===
set retry-count 0
:retry_loop
inc retry-count
iseq ${retry-count} 5 && goto give_up ||
echo Attempt ${retry-count} of 5...
dhcp && goto dhcp_ok ||
sleep 2
goto retry_loop

:dhcp_ok
echo DHCP succeeded on attempt ${retry-count}

:give_up
echo All retry attempts exhausted.

iPXE 빌드와 커스터마이징

# iPXE 소스 빌드

# 의존성 설치
sudo apt install build-essential liblzma-dev isolinux \
     mtools mkisofs syslinux perl

# 소스 클론
git clone https://github.com/ipxe/ipxe.git
cd ipxe/src

# === 기본 빌드 ===
# BIOS용 PXE chainload 이미지 (UNDI 재사용)
make bin/undionly.kpxe

# BIOS용 standalone 이미지 (내장 드라이버 사용)
make bin/ipxe.pxe

# UEFI용 이미지
make bin-x86_64-efi/ipxe.efi

# ARM64 UEFI용
make bin-arm64-efi/ipxe.efi

# === 내장 스크립트와 함께 빌드 ===
cat > embed.ipxe << 'SCRIPT'
#!ipxe
dhcp
chain http://192.168.1.10/boot.ipxe
SCRIPT
make bin/undionly.kpxe EMBED=embed.ipxe

# === 빌드 옵션 커스터마이징 ===
# config/general.h 에서 기능 활성화/비활성화
# DOWNLOAD_PROTO_HTTPS  - HTTPS 지원
# DOWNLOAD_PROTO_NFS    - NFS 지원
# IMAGE_SCRIPT          - 스크립트 지원
# NET_PROTO_IPV6        - IPv6 지원
# CONSOLE_CMD           - 콘솔 명령
# VLAN_CMD              - VLAN 명령
# NSLOOKUP_CMD          - DNS 조회 명령
# PING_CMD              - 핑 명령

# HTTPS 활성화 빌드
cat > custom_config.h << 'EOF'
#define DOWNLOAD_PROTO_HTTPS
#define IMAGE_TRUST_CMD
#define CERT_CMD
#define CONSOLE_CMD
#define NSLOOKUP_CMD
#define PING_CMD
#define VLAN_CMD
EOF
make bin/undionly.kpxe EMBED=embed.ipxe CONFIG=custom_config

# === USB/ISO 이미지 생성 ===
make bin/ipxe.usb      # USB 부팅 이미지
make bin/ipxe.iso      # CD/DVD ISO 이미지
make bin/ipxe.lkrn     # Linux 커널 형식 (GRUB/PXELINUX에서 로드 가능)

Etherboot → gPXE → iPXE 계보

iPXE의 역사를 이해하면 다양한 레거시 환경에서 만나는 네트워크 부팅 소프트웨어의 관계를 파악할 수 있습니다.

프로젝트시기주요 특징상태
Etherboot1995-2006최초의 오픈소스 네트워크 부팅. NIC별 드라이버 바이너리. Tagged image format종료 (gPXE로 전환)
gPXE2006-2010Etherboot 후속. HTTP, iSCSI, DNS 지원 추가. UNDI 호환 모드. GPL v2종료 (iPXE로 포크)
iPXE2010-현재gPXE 포크. HTTPS, Wi-Fi, 스크립팅, 확장된 SAN boot, UEFI 지원. 공식 암호화 문서 기준 TLSv1.0-1.2 지원. GPL v2활발한 개발 중
왜 gPXE에서 iPXE로 포크되었나? gPXE 프로젝트의 핵심 개발자 Michael Brown이 상표권 분쟁과 프로젝트 거버넌스 문제로 2010년에 iPXE를 포크했습니다. iPXE는 gPXE의 모든 기능을 포함하면서 HTTPS, 스크립팅 확장, UEFI 지원, Wi-Fi 등을 추가했습니다. 다만 HTTPS 사용 시 서버가 TLS 1.3 전용으로 강제되면 일부 iPXE 클라이언트는 접속하지 못할 수 있으므로, 서버 정책에서 TLS 1.2를 함께 허용하는지 확인하는 편이 안전합니다. QEMU/KVM은 2011년부터 내장 PXE ROM으로 iPXE를 사용합니다 (이전에는 gPXE → Etherboot).
iPXE 내부 아키텍처 스크립트 엔진 (iPXE Script) bzImage Loader EFI Image Loader Multiboot Loader PXE NBP Loader PEM/DER Loader HTTP/HTTPS TFTP iSCSI AoE FCoE FTP/NFS SLP TCP (HTTPS/TLS) UDP DHCP/DNS IPv4 / IPv6 / ARP / NDP Net Device Abstraction (VLAN, Bonding, Wi-Fi WPA) SAN Device Abstraction (Block I/O) UNDI (PXE 호환) Intel e1000 Realtek r8169 Virtio-net EFI SNP USB NIC Wi-Fi BIOS (16/32-bit Real→Protected Mode) UEFI (Native 64-bit EFI Application)

Netboot.xyz — iPXE 기반 인터넷 부팅

Netboot.xyz는 iPXE 기반의 인터넷 부팅 서비스입니다. 단일 iPXE 이미지만 TFTP에 배치하면, 인터넷에서 수십 개의 OS 설치 이미지를 동적으로 선택하여 부팅할 수 있습니다.
# Netboot.xyz 설정 (가장 간단한 PXE 구성법)

# 1. Netboot.xyz 이미지 다운로드
wget https://boot.netboot.xyz/ipxe/netboot.xyz.kpxe     # BIOS용
wget https://boot.netboot.xyz/ipxe/netboot.xyz.efi       # UEFI용

# TFTP에 배치
cp netboot.xyz.kpxe /var/lib/tftpboot/
cp netboot.xyz.efi /var/lib/tftpboot/

# 2. dnsmasq 설정
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-boot=tag:bios,netboot.xyz.kpxe
dhcp-boot=tag:efi64,netboot.xyz.efi

# 이것만으로 Ubuntu, Debian, CentOS, Fedora, Arch, FreeBSD,
# Windows PE, Memtest86+, SystemRescue, DBAN 등
# 수십 가지 OS를 네트워크에서 직접 설치 가능!

# 3. 자체 호스팅 Netboot.xyz (Docker)
docker run -d --name netbootxyz \
  -p 3000:3000 \         # 웹 관리 인터페이스
  -p 69:69/udp \          # TFTP
  -p 8080:80 \            # HTTP 부팅 파일
  -v /data/netboot:/config \
  ghcr.io/netbootxyz/netbootxyz

UEFI PXE & HTTP Boot

UEFI PXE vs Legacy PXE

항목Legacy BIOS PXEUEFI PXEUEFI HTTP Boot
실행 환경16-bit Real ModeNative (32/64-bit)Native (32/64-bit)
드라이버UNDI (Option ROM)EFI SNP/MNP 드라이버EFI HTTP 프로토콜
부트 파일.0 (COM 바이너리).efi (PE32+ 바이너리).efi (PE32+ 바이너리)
전송 프로토콜TFTPTFTPHTTP/HTTPS
파일 크기 제한~32MB (blksize 미확장)없음없음
Secure Boot불가지원지원 (HTTPS 시 TLS)
IPv6미지원지원지원
속도느림 (TFTP)느림 (TFTP)빠름 (HTTP/TCP)
UEFI PXE 부팅 흐름 (EFI_PXE_BASE_CODE_PROTOCOL) SEC/PEI/DXE UEFI 펌웨어 초기화 BDS Boot Device Selection Network Boot 선택 SNP/MNP 초기화 Simple Network Protocol NIC UEFI 드라이버 로드 EFI_PXE_BASE_CODE PXE Base Code Protocol 시작 DHCP/TFTP 실행 UEFI 네트워크 프로토콜 스택 EFI_PXE_BASE_CODE_PROTOCOL / EFI_HTTP_PROTOCOL / EFI_MTFTP4_PROTOCOL EFI_TCP4_PROTOCOL / EFI_UDP4_PROTOCOL / EFI_DHCP4_PROTOCOL / EFI_DNS4_PROTOCOL EFI_IP4_PROTOCOL / EFI_IP6_PROTOCOL / EFI_ARP_PROTOCOL / EFI_IP4_CONFIG2_PROTOCOL EFI_MANAGED_NETWORK_PROTOCOL (MNP) / EFI_VLAN_CONFIG_PROTOCOL EFI_SIMPLE_NETWORK_PROTOCOL (SNP) — NIC 하드웨어 추상화 NIC UEFI Driver (Intel UndiDxe, Realtek, Broadcom, Mellanox ...)

UEFI HTTP Boot 설정

HTTP Boot의 장점: Boot URI를 직접 전달하므로 TFTP보다 큰 이미지를 다루기 쉽고, 기존 웹 서버 인프라(nginx, Apache, CDN, 캐시 프록시)를 그대로 활용할 수 있습니다. UEFI 2.5부터 HTTP Boot가 공식 규격에 포함되었고, 최신 UEFI 2.11 문서도 DHCPv4/DHCPv6 기반 HTTP Boot 절차를 계속 규정합니다.
# UEFI HTTP Boot를 위한 DHCP 설정 (ISC DHCPd)
# DHCP Option 60에 "HTTPClient"가 포함된 요청 처리

class "httpclients" {
    match if substring (option vendor-class-identifier, 0, 10) = "HTTPClient";

    # HTTP Boot URI를 BOOTP file 필드로 전달
    if option client-arch = 00:10 {
        # x64 UEFI HTTP Boot
        filename "http://192.168.1.10/boot/shimx64.efi";
    } elsif option client-arch = 00:13 {
        # ARM64 UEFI HTTP Boot
        filename "http://192.168.1.10/boot/shimaa64.efi";
    }
}

# dnsmasq 설정
# HTTP Boot 클라이언트에 HTTP URL 제공
dhcp-match=set:HTTPClient,option:vendor-class,HTTPClient
dhcp-boot=tag:HTTPClient,"http://192.168.1.10/boot/shimx64.efi"

# === nginx HTTP Boot 서버 ===
# /etc/nginx/sites-available/pxe
server {
    listen 80;
    server_name pxe.example.com;

    root /var/www/pxeboot;
    autoindex on;

    # EFI 바이너리 MIME 타입
    types {
        application/efi efi;
    }

    location /boot/ {
        alias /var/lib/tftpboot/;
    }

    location /images/ {
        alias /var/www/pxeboot/images/;
    }

    location /iso/ {
        alias /var/www/pxeboot/iso/;
    }

    # iPXE 동적 스크립트 (PHP/Python 백엔드)
    location /api/boot {
        proxy_pass http://127.0.0.1:8080;
    }
}

# === HTTPS HTTP Boot (TLS) ===
server {
    listen 443 ssl;
    server_name pxe.example.com;

    ssl_certificate /etc/ssl/certs/pxe.crt;
    ssl_certificate_key /etc/ssl/private/pxe.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    root /var/www/pxeboot;
    # ... (동일한 location 설정)
}

HTTP Boot의 DHCP 규칙

UEFI 2.11 기준으로 HTTP Boot 클라이언트는 PXEClient가 아니라 HTTPClient라는 Vendor Class를 사용합니다. DHCPv4에서는 Boot URI를 file 필드나 Option 67로 받을 수 있고, DHCPv6에서는 RFC 5970의 OPTION_BOOTFILE_URL(59)을 사용합니다.

항목DHCPv4DHCPv6
클라이언트 식별Option 60 = HTTPClient:Arch:....Option 16 Vendor Class = HTTPClient:Arch:....
부트 대상 전달file 필드 또는 Option 67에 URI 저장Option 59 BOOTFILE_URL
아키텍처 전달Option 93Option 61 CLIENT_ARCH_TYPE
NIC 인터페이스 정보Option 94Option 62 NII
추가 파라미터벤더 옵션 또는 NBP 내부 처리Option 60 BOOTFILE_PARAM
운영 포인트: HTTP Boot는 "DHCP가 끝나면 자동으로 안전해진다"는 뜻이 아닙니다. DHCP 단계 자체는 여전히 위변조 가능하므로, 무결성은 Secure Boot 서명 체인과 HTTPS 서버 인증, 그리고 관리 네트워크 분리에 의해 보강해야 합니다. 또한 펌웨어마다 지원 TLS 버전, SNI 처리, 인증서 저장소가 다르므로 파일이 아닌 펌웨어 호환성이 실제 장애 원인이 되는 경우가 많습니다.

UEFI PXE Vendor Class 식별

DHCP Option 60 (Vendor Class)의미부트 파일 형식
PXEClient:Arch:00000:UNDI:...BIOS PXE16-bit .0/.com
PXEClient:Arch:00006:UNDI:...UEFI x86 PXEPE32 .efi
PXEClient:Arch:00007:UNDI:...UEFI x64 PXEPE32+ .efi
PXEClient:Arch:00011:UNDI:...UEFI ARM64 PXEPE32+ .efi
HTTPClient:Arch:00015:UNDI:...UEFI x86 HTTP BootPE32 .efi (HTTP/HTTPS URI)
HTTPClient:Arch:00016:UNDI:...UEFI x64 HTTP BootPE32+ .efi (HTTP URL)
HTTPClient:Arch:00017:UNDI:...UEFI EBC HTTP BootEBC EFI 애플리케이션
HTTPClient:Arch:00019:UNDI:...UEFI ARM64 HTTP BootPE32+ .efi (HTTP URL)

리눅스 커널 네트워크 부팅 파라미터

ip= 파라미터

커널이 부팅 시 네트워크를 설정하는 핵심 파라미터입니다. initramfs 안의 네트워크 설정과는 별개로, 커널 자체에 내장된 네트워크 초기화 코드(net/ipv4/ipconfig.c)가 처리합니다.

ip= 파라미터 형식:
  ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>

예시:
  ip=dhcp                                         # 모든 NIC에서 DHCP
  ip=192.168.1.100::192.168.1.1:255.255.255.0:myhost:eth0:off
  ip=::::myhost:eth0:dhcp                          # eth0에서 DHCP, 호스트명 지정
  ip=192.168.1.100::192.168.1.1:255.255.255.0::eth0:off:8.8.8.8:8.8.4.4

autoconf 값:
  dhcp   - DHCP만 사용
  bootp  - BOOTP만 사용
  rarp   - RARP만 사용
  both   - BOOTP와 RARP 모두 시도
  on     - DHCP, BOOTP, RARP 모두 시도
  off    - 정적 설정 (나머지 필드에서 IP 가져옴)
  any    - 사용 가능한 모든 프로토콜 시도

nfsroot= 파라미터

nfsroot= 파라미터 형식:
  nfsroot=[server-ip:]root-dir[,nfs-options]

예시:
  nfsroot=192.168.1.10:/exports/diskless,vers=4,tcp
  nfsroot=/exports/diskless,nfsvers=3,rsize=8192,wsize=8192
  nfsroot=192.168.1.10:/exports/diskless,nolock,tcp

주요 NFS 마운트 옵션:
  vers=4 / nfsvers=3  - NFS 버전 지정
  tcp / udp           - 전송 프로토콜
  rsize=N / wsize=N   - 읽기/쓰기 버퍼 크기
  nolock              - NLM 락 비활성화 (diskless에서 권장)
  ro / rw             - 읽기 전용/읽기 쓰기

네트워크 부팅 관련 전체 커널 파라미터

파라미터설명예시
ip=커널 수준 네트워크 설정ip=dhcp
nfsroot=NFS 루트 파일시스템nfsroot=10.0.0.1:/nfs/root
root=루트 장치/프로토콜root=/dev/nfs, root=iscsi:...
netboot=nfsNFS 네트워크 부팅 명시netboot=nfs
BOOTIF=PXE가 설정한 부팅 인터페이스 MACBOOTIF=01-00-11-22-33-44-55
rd.neednet=1dracut: initrd에서 네트워크 활성화rd.neednet=1
rd.iscsi.initiator=dracut: iSCSI initiator 이름rd.iscsi.initiator=iqn.2024...
rd.iscsi.target.name=dracut: iSCSI target 이름rd.iscsi.target.name=iqn.2024...
rd.iscsi.target.ip=dracut: iSCSI target IPrd.iscsi.target.ip=10.0.0.1
iscsi_target_name=initramfs-tools: iSCSI targetiscsi_target_name=iqn.2024...
rd.live.imagedracut: Live 이미지 부팅rd.live.image
fetch=casper: HTTP/NFS에서 squashfs 가져오기fetch=http://server/fs.squashfs

커널 소스: net/ipv4/ipconfig.c

/* net/ipv4/ipconfig.c - 커널 네트워크 자동 설정 핵심 코드 */

/* ip= 파라미터 파싱 */
static int __init ic_proto_name(char *name)
{
    if (!strcmp(name, "on") || !strcmp(name, "any"))
        return IC_BOOTP | IC_RARP;
    if (!strcmp(name, "off") || !strcmp(name, "none"))
        return 0;
    if (!strcmp(name, "dhcp"))
        return IC_BOOTP | IC_USE_DHCP;
    if (!strcmp(name, "bootp"))
        return IC_BOOTP;
    if (!strcmp(name, "rarp"))
        return IC_RARP;
    if (!strcmp(name, "both"))
        return IC_BOOTP | IC_RARP;
    return IC_BOOTP | IC_RARP;
}

/* DHCP/BOOTP 패킷 수신 처리 */
static void __init ic_bootp_recv(struct sk_buff *skb,
                                  struct net_device *dev)
{
    struct bootp_pkt *b = (struct bootp_pkt *)skb->data;

    /* 트랜잭션 ID 확인 */
    if (b->xid != ic_dev_xid)
        goto drop;

    /* IP 주소 할당 */
    ic_myaddr = b->your_ip;   /* yiaddr → 클라이언트 IP */
    ic_servaddr = b->server_ip; /* siaddr → TFTP 서버 */
    ic_gateway = b->relay_ip;   /* giaddr → 게이트웨이 */

    /* DHCP 옵션 파싱 */
    ic_do_bootp_ext(b->exten, skb->len - sizeof(*b));
    /* ... rootpath, hostname, DNS 등 추출 ... */
}

/* NFS 루트 마운트 (fs/nfs/nfsroot.c) */
static int __init nfs_root_setup(char *line)
{
    /* nfsroot=server:/path,options 파싱 */
    ROOT_DEV = Root_NFS;  /* root=/dev/nfs 효과 */
    /* ... */
}

NFS Root 디스크리스 부팅

디스크리스 부팅 개요

디스크리스(diskless) 부팅은 로컬 디스크 없이 네트워크 파일시스템을 루트로 사용하는 부팅 방식입니다. 씬 클라이언트, 클러스터 노드, 임베디드 시스템에서 널리 사용됩니다.

디스크리스(Diskless) 부팅 아키텍처 디스크리스 클라이언트 [ Client A: 192.168.1.101 ] [ Client B: 192.168.1.102 ] [ Client C: 192.168.1.103 ] 로컬 디스크 없음, PXE 부팅 root=/dev/nfs ip=dhcp L2 Switch 1Gbps / 10Gbps DHCP + TFTP IP 할당 + 커널/initrd NFS Server / (rootfs) 제공 NFS Exports /exports/ base/ (공유 RO) overlay/ (호스트별) clientA/ clientB/ clientC/ 디스크리스 rootfs 전략 1. 전체 복사: 클라이언트별 완전한 rootfs 복사본 (간단하지만 디스크 낭비) 2. 공유 RO + 개별 RW: base(RO) + OverlayFS(RW) 조합 (권장) 3. tmpfs overlay: base(NFS RO) + tmpfs(RW) → 재부팅 시 변경 초기화 (씬 클라이언트) 4. iSCSI LUN: 클라이언트별 블록 디바이스 제공 (로컬 디스크와 동일한 성능) 5. NBD/dm-cache: 네트워크 블록 디바이스 + 로컬 SSD 캐시 (하이브리드)

NFS 서버 설정

# NFS 서버 설정 (Ubuntu/Debian)
sudo apt install nfs-kernel-server

# 디스크리스 루트 파일시스템 준비
sudo mkdir -p /exports/diskless

# debootstrap으로 최소 rootfs 생성 (Debian stable: trixie)
sudo debootstrap --arch=amd64 trixie /exports/diskless \
    http://deb.debian.org/debian

# 또는 기존 시스템 복사
# sudo rsync -axHAX --exclude='/proc/*' --exclude='/sys/*' \
#     --exclude='/dev/*' --exclude='/tmp/*' / /exports/diskless/

# NFS 내보내기 설정
# /etc/exports
/exports/diskless   192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
/exports/base       192.168.1.0/24(ro,sync,no_subtree_check)
/exports/overlay    192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

# NFS 서비스 재시작
sudo exportfs -arv
sudo systemctl restart nfs-kernel-server

# 방화벽 (NFS v4: TCP 2049만 필요)
sudo firewall-cmd --add-service=nfs --permanent
sudo firewall-cmd --reload

디스크리스 클라이언트 rootfs 설정

# /exports/diskless/etc/fstab - 디스크리스 클라이언트용
# NFS root는 커널이 자동 마운트하므로 여기서는 추가 마운트만
proc            /proc   proc    defaults    0   0
sysfs           /sys    sysfs   defaults    0   0
tmpfs           /tmp    tmpfs   defaults    0   0
tmpfs           /run    tmpfs   defaults    0   0
tmpfs           /var/log tmpfs  defaults    0   0

# /exports/diskless/etc/network/interfaces
auto lo
iface lo inet loopback

# DHCP (커널 ip=dhcp로 이미 설정되지만, 사용자 공간에서도 필요)
auto eth0
iface eth0 inet dhcp

# 커널 및 initramfs 생성 (NFS root 지원 포함)
# chroot /exports/diskless
# apt install linux-image-amd64 initramfs-tools

# /exports/diskless/etc/initramfs-tools/initramfs.conf
MODULES=netboot
BOOT=nfs

# initramfs 재생성
# update-initramfs -u

# TFTP에 커널/initrd 복사
sudo cp /exports/diskless/boot/vmlinuz-* /var/lib/tftpboot/vmlinuz-diskless
sudo cp /exports/diskless/boot/initrd.img-* /var/lib/tftpboot/initrd-diskless.img

OverlayFS 디스크리스 구성

# initramfs 안에서 OverlayFS 설정하는 스크립트
# /exports/diskless/etc/initramfs-tools/scripts/init-bottom/overlay

#!/bin/sh
# NFS를 lower로, tmpfs를 upper로 사용하는 OverlayFS
# 재부팅 시 모든 변경 사항 초기화됨

PREREQ=""
prereqs() { echo "$PREREQ"; }
case $1 in prereqs) prereqs; exit 0;; esac

. /scripts/functions

# tmpfs 생성 (writable layer)
mkdir -p /overlay
mount -t tmpfs tmpfs /overlay -o size=2G
mkdir -p /overlay/upper /overlay/work

# OverlayFS 마운트
# ${rootmnt}는 NFS로 마운트된 read-only rootfs
mkdir -p /overlay/merged
mount -t overlay overlay /overlay/merged \
    -o lowerdir=${rootmnt},upperdir=/overlay/upper,workdir=/overlay/work

# rootfs 교체
mkdir -p /overlay/merged/nfs-root
mount --move ${rootmnt} /overlay/merged/nfs-root
mount --move /overlay/merged ${rootmnt}

exit 0

iSCSI SAN 부팅

iSCSI 부팅 개요

iSCSI(Internet Small Computer Systems Interface)는 SCSI 명령을 TCP/IP로 전달하는 프로토콜입니다. PXE/iPXE로 초기 부팅 후 iSCSI LUN을 루트 디바이스로 사용하면, 로컬 디스크와 거의 동일한 블록 레벨 I/O 성능을 얻을 수 있습니다.

iSCSI SAN 부팅 흐름 1. PXE DHCP IP 할당 NBP: iPXE 로드 2. iPXE iSCSI sanhook / sanboot iSCSI target 연결 3. 커널 + initrd iSCSI LUN에서 부팅 또는 TFTP에서 로드 4. initrd iSCSI 재연결 open-iscsi: iscsiadm login root=/dev/sda1 마운트 iSCSI Target Server Target: iqn.2024-01.com.example:storage LUN 0: /dev/zvol/pool/client-A (50GB, ext4) LUN 1: /dev/zvol/pool/client-B (50GB, ext4) LUN 2: /dev/zvol/pool/shared-ro (100GB, squashfs) Auth: CHAP (mutual), Portal: 10.0.0.1:3260

iSCSI Target 서버 설정

# targetcli를 사용한 iSCSI target 설정 (Linux LIO)
sudo apt install targetcli-fb

# LVM 또는 파일 기반 LUN 생성
sudo lvcreate -L 50G -n client-A vg0
# 또는: dd if=/dev/zero of=/var/lib/iscsi/client-A.img bs=1M count=51200

sudo targetcli

# targetcli 내부 명령:
/> backstores/block create client-A /dev/vg0/client-A
/> iscsi/ create iqn.2024-01.com.example:storage
/> iscsi/iqn.2024-01.com.example:storage/tpg1/luns/ create /backstores/block/client-A
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/ create iqn.2024-01.com.example:client-A

# CHAP 인증 설정
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/iqn.2024-01.com.example:client-A/ set auth userid=client-A
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/iqn.2024-01.com.example:client-A/ set auth password=secret123

# 포털 설정 (기본 0.0.0.0:3260)
/> iscsi/iqn.2024-01.com.example:storage/tpg1/portals/ create 10.0.0.1

/> saveconfig
/> exit

iPXE iSCSI 부팅 스크립트

#!ipxe
# iscsi-boot.ipxe

dhcp
set iscsi-server 10.0.0.1
set iscsi-port 3260
set iscsi-target iqn.2024-01.com.example:storage
set iscsi-initiator iqn.2024-01.com.example:${mac:hexhyp}

# iSCSI 연결 및 SAN 부팅
set root-path iscsi:${iscsi-server}::${iscsi-port}:1:${iscsi-target}
sanhook ${root-path} || goto failed
sanboot ${root-path} || goto failed

:failed
echo iSCSI boot failed!
shell

대규모 프로비저닝 시스템

프로비저닝 도구 비교

도구개발사특징규모자동 설치
Cobbler커뮤니티DHCP/DNS/TFTP 통합 관리, 프로파일 시스템중-대Kickstart, Preseed
MAASCanonical"Metal as a Service", 베어메탈 클라우드cloud-init, Curtin
ForemanTheforeman라이프사이클 관리, Puppet/Ansible 통합Kickstart, Preseed
FOG Project커뮤니티이미지 기반 배포, 디스크 클로닝소-중이미지 복원
TinkerbellEquinix클라우드 네이티브, 워크플로 기반Workflow Actions
IronicOpenStackOpenStack 베어메탈 프로비저닝IPA (Ironic Python Agent)
RazorPuppet정책 기반 프로비저닝Kickstart, Preseed
MatchboxCoreOS/Red HatCoreOS/Flatcar 전용, iPXE 통합중-대Ignition
Netboot.xyz커뮤니티인터넷 기반 멀티 OS 부팅 메뉴다양

Kickstart 자동 설치 예제 (RHEL 10 계열)

# /var/www/pxeboot/ks/rhel10.cfg - Kickstart 파일
# RHEL 10 계열 무인 설치

# 설치 소스
url --url="http://192.168.1.10/rhel10/"
repo --name="AppStream" --baseurl="http://192.168.1.10/rhel10/AppStream/"

# 시스템 설정
text
lang en_US.UTF-8
keyboard us
timezone Asia/Seoul --utc
rootpw --iscrypted $6$rounds=656000$salt$hash...
selinux --enforcing
firewall --enabled --ssh
services --enabled=sshd

# 네트워크
network --bootproto=dhcp --device=link --activate --hostname=server01.example.com

# 디스크 파티셔닝
zerombr
clearpart --all --initlabel
autopart --type=lvm --fstype=xfs

# BIOS/UEFI 공용 예시: 위치를 MBR로 고정하지 않음
bootloader --append="crashkernel=auto"

# 패키지
%packages
@^minimal-environment
vim-enhanced
tmux
open-iscsi
nfs-utils
%end

# 설치 후 스크립트
%post --log=/root/ks-post.log
# SSH 키 배포
mkdir -p /root/.ssh
curl -o /root/.ssh/authorized_keys http://192.168.1.10/keys/admin.pub
chmod 600 /root/.ssh/authorized_keys

# 설치 상태 등록
primary_ip=$(hostname -I | awk '{print $1}')
curl -X POST http://192.168.1.10/api/register \
    -d "hostname=$(hostname)&ip=${primary_ip}"
%end

# 설치 완료 후 재부팅
reboot
Kickstart 실무 메모: RHEL 10 공식 문서는 PXE와 UEFI HTTP Boot 모두에서 inst.ks=를 같은 방식으로 커널 인자에 추가하는 구성을 제시합니다. 혼합 BIOS/UEFI 환경 문서라면 BIOS 전용 표현인 --location=mbr를 기본 예제로 두기보다, 범용적인 bootloader --append=... 형태를 먼저 보여 주는 편이 안전합니다. Kickstart 파일은 배포 전에 ksvalidator -v RHEL10로 문법을 먼저 점검하는 편이 좋습니다.

cloud-init / Autoinstall 예제 (Ubuntu)

# /var/www/pxeboot/autoinstall/user-data
# Ubuntu 24.04 LTS Autoinstall 설정

#cloud-config
autoinstall:
  version: 1

  locale: en_US.UTF-8
  keyboard:
    layout: us

  network:
    version: 2
    ethernets:
      id0:
        match:
          driver: e1000*
        dhcp4: true

  storage:
    layout:
      name: lvm

  identity:
    hostname: ubuntu-server
    username: admin
    password: "$6$rounds=656000$..."

  ssh:
    install-server: true
    authorized-keys:
      - ssh-ed25519 AAAA... admin@example.com

  packages:
    - vim
    - tmux
    - nfs-common
    - open-iscsi

  late-commands:
    # 첫 부팅 시 로컬 디스크에서 부팅하도록 설정
    - curtin in-target --target=/target -- efibootmgr -o 0001
    # 설치 후 알림
    - "curl -X POST http://192.168.1.10/api/installed?host=$(hostname)"
Ubuntu Autoinstall 실무 메모: NoCloud 네트워크 방식은 PXE 커널 인자에 autoinstall ds=nocloud-net;s=http://...를 넣고, HTTP 루트에 user-datameta-data를 함께 제공하는 패턴이 가장 일반적입니다. Ubuntu 24.04부터는 cloud-config를 사용할 때 최상위 autoinstall: 구조와 스키마 검증이 더 엄격해졌으므로, 예전의 느슨한 YAML을 그대로 재사용하면 설치 초반에 중단될 수 있습니다.

배포판별 자동설치 실무 메모

배포판현재 문서 기준권장 전달 방식실무에서 자주 틀리는 지점
RHEL 10 계열Kickstart + Anacondainst.repo= + inst.ks=, PXE 또는 UEFI HTTP BootBIOS 전용 --location=mbr 고정, 배포 전 ksvalidator 미실행
Ubuntu 24.04 LTSSubiquity + cloud-init Autoinstallautoinstall ds=nocloud-net;s=... + HTTP user-data/meta-data#cloud-config 헤더 누락, 최상위 autoinstall: 키 누락, 오래된 스키마 재사용
Debian 13 stable (trixie)Debian Installer + Preseedauto=true priority=critical url=...UEFI 환경에서 GPT/EFI 관련 partman 옵션 누락, interface=auto 미지정
대규모 PXE 프로비저닝 워크플로 1. 서버 투입 랙 설치, 케이블 연결 PXE 부팅 설정 2. 자동 발견 DHCP → MAC 등록 IPMI/BMC 감지 3. 프로파일 할당 OS + 역할 선택 (웹 / DB / 컴퓨트) 4. PXE 설치 커널 부팅 → 무인설치 Kickstart/Preseed 5. 구성 관리 Ansible/Puppet 서비스 배포 대규모 배포 최적화 포인트 1. TFTP → HTTP 전환: 대규모 동시 부팅 시 TFTP는 병목. iPXE + HTTP/nginx 조합으로 1000+ 노드 동시 부팅 2. 멀티캐스트 TFTP: 동일 이미지를 여러 노드에 동시 전송 (UDPcast) 3. BitTorrent 배포: 대규모 이미지 배포에 P2P 활용 (Murder, Drax) 4. 로컬 캐싱: 서브넷별 캐싱 프록시(Squid, nginx cache)로 WAN 트래픽 감소 5. IPMI/BMC 연동: ipmitool/Redfish로 원격 전원 제어 + PXE 부팅 순서 자동 설정 6. DHCP 릴레이: 여러 서브넷 지원을 위한 ip helper-address / dhcp-relay 구성

Secure PXE Boot

PXE 보안 위협과 대응

위협설명대응
Rogue DHCP악성 DHCP 서버가 잘못된 부팅 서버로 유도DHCP Snooping, 802.1X, VLAN 분리
TFTP 탈취NBP/커널 파일을 악성으로 교체HTTPS 전송 (iPXE/UEFI HTTP Boot)
MITM 공격네트워크 중간에서 부팅 트래픽 변조TLS/HTTPS, IPSec, 802.1X
악성 NBP서명되지 않은 부트로더 실행UEFI Secure Boot + Shim
부팅 이미지 변조커널/initrd 무결성 훼손IMA/EVM, dm-verity, 서명 검증
인증 없는 접근아무 기기나 PXE 부팅 가능MAC 필터링, 802.1X NAC, CHAP

Secure PXE Boot Chain

UEFI Secure PXE Boot Chain UEFI 펌웨어 PK → KEK → db Microsoft CA 포함 검증 shimx64.efi MS 서명됨 MOK DB 관리 검증 grubx64.efi Distro CA 서명 또는 MOK 서명 검증 vmlinuz 커널 서명 검증 Lockdown LSM 검증 커널 모듈 모듈 서명 검증 CONFIG_MODULE_SIG 전송 보안 계층 UEFI HTTP Boot + HTTPS: 서버 인증과 신뢰 저장소는 펌웨어 구현에 의존 iPXE + HTTPS: 빌드 시 CA 인증서 내장, 서버 인증서 검증

Secure PXE Boot 구성 절차

# 1. MOK (Machine Owner Key) 생성
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key -out MOK.crt \
    -days 3650 -nodes -subj "/CN=PXE Boot Signing Key"

# 2. GRUB EFI 서명
sbsign --key MOK.key --cert MOK.crt --output grubx64.efi.signed grubx64.efi

# 3. 커널 서명
sbsign --key MOK.key --cert MOK.crt --output vmlinuz.signed vmlinuz

# 4. Shim에 MOK 등록 (각 클라이언트에서 1회)
# shimx64.efi가 처음 부팅 시 MokManager로 진입
# → MOK.cer(DER 형식) 등록
openssl x509 -in MOK.crt -outform der -out MOK.cer

# 5. TFTP에 배치
cp shimx64.efi.signed /var/lib/tftpboot/shimx64.efi
cp grubx64.efi.signed /var/lib/tftpboot/grubx64.efi
cp vmlinuz.signed /var/lib/tftpboot/vmlinuz

# 6. iPXE HTTPS 빌드 (CA 인증서 내장)
cd ipxe/src
make bin-x86_64-efi/ipxe.efi EMBED=boot.ipxe \
    TRUST=/etc/ssl/certs/ca-certificates.crt \
    CERT=client.crt PRIVKEY=client.key

# 7. 네트워크 보안
# 802.1X RADIUS 인증 (PXE 전 네트워크 접근 제어)
# DHCP Snooping: 신뢰할 수 있는 포트만 DHCP 응답 허용
# Dynamic ARP Inspection: ARP 스푸핑 방지

IPv6 PXE 부팅

IPv6 PXE 개요

IPv6에서는 BOOTP의 siaddr/file 모델보다 URL 기반 전달이 더 중요해집니다. UEFI는 IPv6 환경에서 netboot6(PXE over DHCPv6 + MTFTP)와 HTTP Boot를 모두 정의하며, DHCPv6 옵션 집합(RFC 5970)을 통해 부트 URI와 아키텍처 정보를 전달합니다.

항목IPv4 PXEIPv6 PXE
주소 할당DHCPv4DHCPv6, RA, 필요 시 SLAAC 병행
부트 위치 전달siaddr, file, Option 66/67Option 59 BOOTFILE_URL
아키텍처 정보Option 93Option 61 CLIENT_ARCH_TYPE
NIC 인터페이스 식별Option 94Option 62 NII
부트파일 예시filename "pxelinux.0"tftp://[2001:db8::1]/grubx64.efi 또는 https://boot.example.com/shimx64.efi
다음 서버 지정next-server 또는 siaddrURI 안에 서버와 경로를 모두 포함
멀티캐스트제한적네이티브 지원
BIOS 지원지원미지원 (UEFI만)

DHCPv6 부트 옵션 세트

RFC 5970 옵션이름역할예시
59BOOTFILE_URL부트 이미지의 전체 URI 전달http://[2001:db8::10]/boot/shimx64.efi
60BOOTFILE_PARAM부트 이미지에 전달할 부가 파라미터root=/dev/nfs, ip=dhcp
61CLIENT_ARCH_TYPE클라이언트 아키텍처 전달x64 UEFI, ARM64 UEFI, HTTP Boot 등
62NIINetwork Interface IdentifierUNDI/SNP 인터페이스 버전, 타입
설계 포인트: IPv6 환경에서는 "TFTP 서버 주소"와 "파일 이름"을 따로 조합하는 사고보다, 완전한 URI를 전달한다고 생각하는 편이 이해와 디버깅에 훨씬 유리합니다. 이 점 때문에 UEFI HTTP Boot는 IPv6 환경에서 특히 자연스럽게 맞아떨어집니다.
# ISC DHCPv6 PXE 설정
# /etc/dhcp/dhcpd6.conf

subnet6 2001:db8::/64 {
    range6 2001:db8::100 2001:db8::200;

    # PXE 부트파일 URL (DHCPv6 Option 59)
    option dhcp6.bootfile-url "tftp://[2001:db8::1]/grubx64.efi";

    # HTTP Boot
    # option dhcp6.bootfile-url "http://[2001:db8::1]/boot/shimx64.efi";
}

# dnsmasq IPv6 PXE
dhcp-range=::100,::200,constructor:eth0,ra-names,slaac,64,12h
dhcp-option=option6:bootfile-url,"tftp://[2001:db8::1]/grubx64.efi"

멀티캐스트 부팅과 성능 최적화

멀티캐스트 TFTP

동일한 이미지를 수백 대의 서버에 동시에 전송해야 할 때, 유니캐스트 TFTP는 심각한 병목이 됩니다. 멀티캐스트를 사용하면 네트워크 대역폭을 절약하면서 대규모 동시 배포가 가능합니다.

방식도구장점단점
유니캐스트 TFTPtftpd-hpa간단, 호환성 최고노드 수 × 대역폭
멀티캐스트 TFTPatftpd (mtftp)1:N 동시 전송PXE ROM 지원 필요
UDPcastudp-sender/receiver대규모 이미지 배포커스텀 리시버 필요
HTTPnginx/ApacheTCP 신뢰성, 빠른 속도iPXE 또는 UEFI HTTP Boot 필요
BitTorrentMurder, opentrackerP2P, 대규모에 최적초기 시드 필요, 복잡성

전송 프로토콜별 성능 비교

테스트 환경: 100MB vmlinuz + 50MB initrd, 1Gbps 네트워크

┌──────────────────────┬──────────────┬─────────────────┬──────────────────┐
│ 프로토콜             │ 단일 클라이언트│ 10대 동시       │ 100대 동시       │
├──────────────────────┼──────────────┼─────────────────┼──────────────────┤
│ TFTP (512B blk)      │ ~120초       │ ~1200초 (직렬)  │ 사실상 불가      │
│ TFTP (1468B blk)     │ ~25초        │ ~250초          │ ~2500초          │
│ TFTP (1468B, win=64) │ ~8초         │ ~80초           │ ~800초           │
│ HTTP (nginx)         │ ~2초         │ ~3초            │ ~15초            │
│ HTTPS (nginx)        │ ~3초         │ ~5초            │ ~20초            │
│ 멀티캐스트 TFTP      │ ~25초        │ ~30초           │ ~40초            │
│ UDPcast              │ ~3초         │ ~5초            │ ~10초            │
└──────────────────────┴──────────────┴─────────────────┴──────────────────┘
* 실제 성능은 네트워크 환경, 서버 성능에 따라 크게 다를 수 있음

nginx PXE 서버 최적화

# /etc/nginx/conf.d/pxe-optimized.conf
server {
    listen 80;
    server_name pxe.example.com;
    root /var/www/pxeboot;

    # 동시 접속 최적화
    keepalive_timeout 30;
    keepalive_requests 100;

    # sendfile + tcp_nopush로 커널 수준 최적화
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # 대용량 파일 전송 최적화
    directio 4m;          # 4MB 이상 파일은 DirectIO
    aio threads;          # 비동기 I/O
    output_buffers 2 1m;  # 출력 버퍼

    # 바이트 범위 요청 (iPXE가 사용)
    max_ranges 1;

    # 캐싱 헤더
    location ~* \.(efi|img|vmlinuz|iso)$ {
        expires 1h;
        add_header Cache-Control "public, no-transform";
    }

    # 접속 제한 (DoS 방지)
    limit_conn_zone $binary_remote_addr zone=pxe:10m;
    limit_conn pxe 10;
}

UDPcast 멀티캐스트 배포

UDPcast는 대규모 동시 이미지 배포에 최적화된 멀티캐스트/브로드캐스트 도구입니다. PXE + iPXE와 결합하여 수백 대의 서버에 동시에 디스크 이미지를 전송할 수 있습니다.

# UDPcast 설치 및 사용

# 서버 설치 (Debian/Ubuntu)
sudo apt install udpcast

# === 1. 디스크 이미지 멀티캐스트 전송 ===

# 송신측 (서버): 디스크 이미지 전송
udp-sender --file /images/ubuntu-golden.img \
    --mcast-addr 239.1.1.1 \
    --portbase 9000 \
    --min-receivers 10 \      # 최소 10대 수신 대기 후 시작
    --max-wait 60 \            # 최대 60초 대기
    --blocksize 1456 \         # 블록 크기 (MTU 최적화)
    --fec 8x8/64 \             # Forward Error Correction
    --autostart 30 \           # 30초 후 자동 시작
    --log /var/log/udpcast.log

# 수신측 (클라이언트): initrd 안에서 실행
udp-receiver --mcast-addr 239.1.1.1 \
    --portbase 9000 \
    --file /dev/sda \          # 직접 디스크에 기록
    --nosync                    # 완료 시 sync 생략 (빠른 재부팅)

# === 2. 파이프를 통한 실시간 압축 전송 ===

# 송신측
lz4 -c /dev/sda | udp-sender --pipe "cat" \
    --mcast-addr 239.1.1.1 --portbase 9000 --min-receivers 5

# 수신측
udp-receiver --pipe "lz4 -d > /dev/sda" \
    --mcast-addr 239.1.1.1 --portbase 9000

# === 3. iPXE 스크립트에서 UDPcast 연동 ===
# initrd에 udp-receiver를 포함시키고, 커널 파라미터로 멀티캐스트 주소 전달
# imgargs vmlinuz ip=dhcp udpcast_addr=239.1.1.1 udpcast_port=9000

atftpd 멀티캐스트 TFTP

# atftpd: 멀티캐스트 TFTP 서버 (RFC 2090)
sudo apt install atftpd

# /etc/default/atftpd 설정
USE_INETD=false
OPTIONS="--daemon --port 69 \
  --tftpd-timeout 300 \
  --retry-timeout 5 \
  --maxthread 200 \
  --bind-address 0.0.0.0 \
  --verbose 5 \
  --logfile /var/log/atftpd.log \
  --mtftp 239.1.1.1,1758 \
  --mtftp-port 1758 \
  /var/lib/tftpboot"

# 멀티캐스트 TFTP 동작 원리:
# 1. 첫 번째 클라이언트가 RRQ 전송
# 2. 서버가 멀티캐스트 그룹으로 DATA 전송 시작
# 3. 이후 클라이언트들은 진행 중인 멀티캐스트 스트림에 합류
# 4. 놓친 블록은 유니캐스트로 재요청
# 주의: PXE ROM이 MTFTP를 지원해야 함 (많은 ROM이 미지원)

sudo systemctl restart atftpd

DHCP 릴레이와 다중 서브넷

DHCP 릴레이 개요

PXE 클라이언트의 DHCPDISCOVER는 브로드캐스트이므로, 라우터를 넘어갈 수 없습니다. 다중 서브넷 환경에서는 DHCP 릴레이 에이전트(ip helper-address)가 필요합니다.

DHCP 릴레이를 통한 다중 서브넷 PXE 부팅 서브넷 A (192.168.1.0/24) PXE Client A PXE Client B 서브넷 B (192.168.2.0/24) PXE Client C PXE Client D L3 Router DHCP Relay Agent ip helper-address DHCP + TFTP Server 10.0.0.1 (관리 서브넷) 릴레이 동작: 1. DISCOVER를 수신 2. giaddr에 수신 인터페이스 IP 설정 3. 유니캐스트로 DHCP 서버에 전달 4. 서버가 giaddr 서브넷 풀에서 IP 할당
# Cisco/Juniper 라우터 DHCP Relay 설정

# Cisco IOS
interface GigabitEthernet0/1
 description Subnet-A
 ip address 192.168.1.1 255.255.255.0
 ip helper-address 10.0.0.1

interface GigabitEthernet0/2
 description Subnet-B
 ip address 192.168.2.1 255.255.255.0
 ip helper-address 10.0.0.1

# Linux DHCP Relay Agent
sudo apt install isc-dhcp-relay

# /etc/default/isc-dhcp-relay
SERVERS="10.0.0.1"
INTERFACES="eth1 eth2"
OPTIONS="-a"  # Agent Information (Option 82) 추가

sudo systemctl enable --now isc-dhcp-relay

# ISC DHCP 서버: 서브넷별 설정
# giaddr로 클라이언트 서브넷 식별
subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    next-server 10.0.0.1;
    filename "pxelinux.0";
}

subnet 192.168.2.0 netmask 255.255.255.0 {
    range 192.168.2.100 192.168.2.200;
    option routers 192.168.2.1;
    next-server 10.0.0.1;
    filename "pxelinux.0";
}

DHCP Option 82 (Relay Agent Information)

DHCP Option 82(RFC 3046)는 릴레이 에이전트가 DHCP 패킷에 추가하는 정보로, 클라이언트가 연결된 물리적 위치(스위치 포트, VLAN)를 DHCP 서버에 알려줍니다. 대규모 PXE 환경에서 자동 프로비저닝에 핵심적입니다.

DHCP Option 82 구조 (RFC 3046):
┌──────────────────────────────────────────────────────┐
│ Option 82 (Relay Agent Information)                  │
│  Type: 82 (0x52), Length: variable                   │
│                                                       │
│  Sub-option 1: Circuit ID (물리적 포트 식별)          │
│    - 스위치명 + 포트 번호                             │
│    - 예: "Gi0/1" (GigabitEthernet 0/1)              │
│    - 예: "Ethernet1/0/24" (스택 스위치)              │
│                                                       │
│  Sub-option 2: Remote ID (원격 장비 식별)            │
│    - 릴레이 에이전트(스위치)의 MAC 또는 식별자        │
│    - 예: "00:1A:2B:3C:4D:5E" (스위치 MAC)           │
│                                                       │
│  Sub-option 5: Link Selection (선택적)               │
│    - DHCP 서버가 사용할 서브넷 지정                   │
│  Sub-option 6: Subscriber ID (선택적)                │
│    - 가입자 식별 (ISP 환경)                          │
└──────────────────────────────────────────────────────┘

활용 시나리오:
- 스위치 포트 기반 자동 프로비저닝: "Gi0/1에 연결된 서버 → Web 프로파일"
- 랙/위치 기반 IP 할당: 포트 정보로 물리적 위치 파악
- 보안 감사: 어떤 포트에서 DHCP 요청이 왔는지 추적
# ISC DHCP에서 Option 82 활용 예제

# Option 82 서브옵션 정의
option agent.circuit-id code 1 = text;
option agent.remote-id code 2 = text;

# Circuit ID 기반 PXE 프로파일 자동 할당
class "rack1-servers" {
    # 랙1의 스위치 포트 Gi0/1~Gi0/24에서 온 요청
    match if substring(option agent.circuit-id, 0, 4) = "Gi0/";
}

# 클래스별 다른 부트 파일/OS 프로파일 할당
pool {
    allow members of "rack1-servers";
    range 192.168.1.100 192.168.1.124;
    next-server 10.0.0.1;
    filename "profiles/rack1/pxelinux.0";
}

# Cisco 스위치에서 Option 82 설정
# ip dhcp snooping
# ip dhcp snooping vlan 100
# ip dhcp snooping information option    ← Option 82 삽입 활성화
# interface GigabitEthernet0/1
#  ip dhcp snooping trust                ← DHCP 서버 쪽 포트

트러블슈팅

일반적인 문제와 해결

증상원인해결
PXE-E51: No DHCP or proxyDHCP offers were receivedDHCP 서버 미응답DHCP 서비스 확인, 방화벽 UDP 67/68 열기, VLAN/포트 확인
PXE-E53: No boot filename receivedDHCP에 filename 미설정filename 또는 Option 67 설정 확인
PXE-E32: TFTP open timeoutTFTP 서버 접근 불가TFTP 서비스 확인, 방화벽 UDP 69 열기, next-server IP 확인
PXE-E36: Error received from TFTP server파일 없음 또는 권한 오류파일 경로/권한 확인: ls -la /var/lib/tftpboot/
PXE-E3B: TFTP Error - File not found부트파일 경로 오류정확한 파일명 확인, 대소문자 주의
PXE-E61: Media test failure, check cable네트워크 물리적 문제케이블, 스위치 포트, 링크 상태 확인
NBP 로드 후 검은 화면아키텍처 불일치BIOS→.0 파일, UEFI→.efi 파일 확인
GRUB: error: no suitable video mode found그래픽 모드 문제terminal_output console 사용
"Booting from ROM" 후 멈춤PXE ROM 비활성화BIOS에서 NIC PXE/Network Boot 활성화
UEFI HTTP Boot 실패HTTP 서버 문제URL 접근 가능 확인, Content-Type 헤더 확인

디버깅 도구와 명령어

# === DHCP 트래픽 캡처 ===
# tcpdump로 DHCP 패킷 모니터링
sudo tcpdump -i eth0 -vvv port 67 or port 68 or port 4011

# Wireshark 필터
# bootp || dhcp || tftp

# === TFTP 테스트 ===
# TFTP 클라이언트로 직접 파일 요청
tftp 192.168.1.10
tftp> get pxelinux.0
tftp> quit

# curl로 TFTP 테스트
curl tftp://192.168.1.10/pxelinux.0 -o /tmp/pxelinux.0

# === DHCP 서버 로그 ===
# ISC DHCP
sudo journalctl -u isc-dhcp-server -f

# dnsmasq
sudo journalctl -u dnsmasq -f
# 또는 dnsmasq --log-dhcp 옵션

# === TFTP 서버 로그 ===
sudo journalctl -u tftpd-hpa -f

# === iPXE 디버깅 ===
# iPXE 쉘에서 직접 디버깅
iPXE> dhcp net0
iPXE> show net0/ip
iPXE> show net0/netmask
iPXE> show net0/gateway
iPXE> show net0/dns
iPXE> nslookup pxe.example.com
iPXE> ping 192.168.1.10
iPXE> imgfetch tftp://192.168.1.10/pxelinux.0
iPXE> chain http://192.168.1.10/boot.ipxe

# === NFS 디버깅 ===
# NFS 마운트 테스트
sudo mount -t nfs 192.168.1.10:/exports/diskless /mnt -o vers=4,tcp
ls /mnt/

# NFS 서버 내보내기 확인
showmount -e 192.168.1.10

# rpcinfo (NFS v3)
rpcinfo -p 192.168.1.10

# === iSCSI 디버깅 ===
# iSCSI target 탐색
iscsiadm -m discovery -t sendtargets -p 10.0.0.1:3260

# iSCSI 로그인
iscsiadm -m node -T iqn.2024-01.com.example:storage -p 10.0.0.1 --login

# 세션 확인
iscsiadm -m session -P 3

# === 커널 네트워크 부팅 디버깅 ===
# 커널 커맨드 라인에 추가:
# ip=dhcp debug loglevel=7

# 시리얼 콘솔로 부팅 로그 확인:
# console=ttyS0,115200n8

PXE 패킷 분석 예제

# tcpdump 출력 예제: 정상적인 PXE 부팅 시퀀스

# 1. DHCPDISCOVER
12:00:00.001 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request
  Client-Ethernet-Address 00:11:22:33:44:55
  Vendor-Class: PXEClient:Arch:00000:UNDI:002001
  Parameter-Request: Subnet-Mask(1), Router(3), DNS(6), Domain(15),
    TFTP-Server(66), Boot-File(67), Vendor-Class(60), Client-Arch(93)

# 2. DHCPOFFER
12:00:00.005 IP 192.168.1.1.67 > 192.168.1.100.68: BOOTP/DHCP, Reply
  Your-IP: 192.168.1.100
  Server-IP: 192.168.1.10      ← TFTP 서버
  Boot-File: pxelinux.0        ← NBP 파일명

# 3. DHCPREQUEST + DHCPACK (생략)

# 4. TFTP RRQ
12:00:00.050 IP 192.168.1.100.2070 > 192.168.1.10.69: TFTP, RRQ "pxelinux.0"
  octet blksize 1468 tsize 0

# 5. TFTP OACK + DATA
12:00:00.051 IP 192.168.1.10.41234 > 192.168.1.100.2070: TFTP, OACK
  blksize=1468 tsize=42780
12:00:00.052 IP 192.168.1.100.2070 > 192.168.1.10.41234: TFTP, ACK block 0
12:00:00.053 IP 192.168.1.10.41234 > 192.168.1.100.2070: TFTP, DATA block 1
  ... (29 더 많은 DATA 블록) ...

# 6. PXELINUX가 설정 파일 요청
12:00:01.100 IP 192.168.1.100.2071 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/01-00-11-22-33-44-55"      ← MAC 기반 설정 시도
12:00:01.101 IP 192.168.1.10.69 > 192.168.1.100.2071: TFTP, ERROR
  code=1 File not found                     ← 없으면 다음 시도

12:00:01.102 IP 192.168.1.100.2072 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/C0A80164"                   ← IP 기반 시도
12:00:01.103 IP 192.168.1.10.69 > 192.168.1.100.2072: TFTP, ERROR
  code=1 File not found

12:00:01.104 IP 192.168.1.100.2073 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/default"                    ← 기본 설정
12:00:01.105 IP 192.168.1.10.41235 > 192.168.1.100.2073: TFTP, DATA block 1
  ... (설정 파일 전송) ...

# 7. 커널 + initrd 다운로드
12:00:02.000 IP 192.168.1.100.2074 > 192.168.1.10.69: TFTP, RRQ "vmlinuz"
  ... (대용량 전송) ...
12:00:10.000 IP 192.168.1.100.2075 > 192.168.1.10.69: TFTP, RRQ "initrd.img"
  ... (대용량 전송) ...

실전 구성 예제

최소 PXE 서버 구성 (5분 설정)

#!/bin/bash
# minimal-pxe-setup.sh - dnsmasq 기반 최소 PXE 서버 설정

set -e

PXE_IFACE="eth0"
PXE_IP="192.168.1.10"
PXE_RANGE="192.168.1.100,192.168.1.200"
TFTP_ROOT="/var/lib/tftpboot"

# 1. 패키지 설치
sudo apt update
sudo apt install -y dnsmasq pxelinux syslinux-common

# 2. TFTP 디렉토리 구성
sudo mkdir -p ${TFTP_ROOT}/pxelinux.cfg

# SYSLINUX 파일 복사
sudo cp /usr/lib/PXELINUX/pxelinux.0 ${TFTP_ROOT}/
sudo cp /usr/lib/syslinux/modules/bios/{ldlinux.c32,menu.c32,libutil.c32,vesamenu.c32,libcom32.c32} ${TFTP_ROOT}/

# 3. dnsmasq 설정
sudo tee /etc/dnsmasq.d/pxe.conf << EOF
# PXE 서버 설정
interface=${PXE_IFACE}
bind-interfaces

# DHCP
dhcp-range=${PXE_RANGE},255.255.255.0,12h
dhcp-option=option:router,${PXE_IP}
dhcp-option=option:dns-server,8.8.8.8

# TFTP
enable-tftp
tftp-root=${TFTP_ROOT}

# PXE
dhcp-boot=pxelinux.0

# 로깅
log-dhcp
log-facility=/var/log/dnsmasq-pxe.log
EOF

# 4. 기본 메뉴 설정
sudo tee ${TFTP_ROOT}/pxelinux.cfg/default << 'EOF'
DEFAULT menu.c32
PROMPT 0
TIMEOUT 300
MENU TITLE PXE Boot Menu

LABEL local
    MENU LABEL Boot from Local Disk
    MENU DEFAULT
    LOCALBOOT 0
EOF

# 5. 서비스 시작
sudo systemctl disable --now systemd-resolved 2>/dev/null || true
sudo systemctl enable --now dnsmasq

echo "PXE 서버 준비 완료!"
echo "커널/initrd를 ${TFTP_ROOT}/에 복사하고 pxelinux.cfg/default를 편집하세요."

전체 스택 구성 (DHCP + TFTP + HTTP + NFS)

#!/bin/bash
# full-pxe-stack.sh - 프로덕션급 PXE 서버 전체 스택

# === 1. dnsmasq (DHCP + TFTP + ProxyDHCP) ===
sudo tee /etc/dnsmasq.d/pxe-full.conf << 'EOF'
# DHCP
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,8.8.8.8,8.8.4.4

# TFTP
enable-tftp
tftp-root=/var/lib/tftpboot

# 아키텍처별 자동 분기
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-match=set:efiaa64,option:client-arch,11
dhcp-match=set:ipxe,175

# iPXE 체인로딩
dhcp-boot=tag:bios,tag:!ipxe,undionly.kpxe
dhcp-boot=tag:efi64,tag:!ipxe,ipxe.efi
dhcp-boot=tag:efiaa64,tag:!ipxe,ipxe-arm64.efi
dhcp-boot=tag:ipxe,http://192.168.1.10/boot.ipxe

log-dhcp
EOF

# === 2. nginx (HTTP 부팅 서버) ===
sudo tee /etc/nginx/sites-available/pxe << 'EOF'
server {
    listen 80;
    server_name pxe.example.com 192.168.1.10;
    root /var/www/pxeboot;
    autoindex on;

    sendfile on;
    tcp_nopush on;

    location /boot/ {
        alias /var/lib/tftpboot/;
    }
}
EOF
sudo ln -sf /etc/nginx/sites-available/pxe /etc/nginx/sites-enabled/
sudo systemctl reload nginx

# === 3. NFS 서버 ===
echo "/exports/diskless 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)" \
    | sudo tee -a /etc/exports
sudo exportfs -arv

# === 4. iPXE 메인 스크립트 ===
sudo tee /var/www/pxeboot/boot.ipxe << 'IPXE'
#!ipxe
set boot-url http://192.168.1.10
menu Network Boot Menu
item ubuntu    Ubuntu 24.04 LTS Install
item diskless  Diskless Linux
item local     Local Boot
choose --default local --timeout 30000 target && goto ${target} || goto local

:ubuntu
kernel ${boot-url}/images/ubuntu/vmlinuz ip=dhcp autoinstall ds=nocloud-net;s=${boot-url}/autoinstall/
initrd ${boot-url}/images/ubuntu/initrd
boot

:diskless
kernel ${boot-url}/vmlinuz-diskless root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4 ip=dhcp rw
initrd ${boot-url}/initrd-diskless.img
boot

:local
exit
IPXE

QEMU/KVM PXE 테스트 환경

QEMU/KVM을 사용하면 물리 서버 없이도 PXE 부팅을 테스트할 수 있습니다. QEMU에는 iPXE ROM이 내장되어 있어 별도 설치 없이 네트워크 부팅이 가능합니다.

# === 1. 기본 QEMU PXE 부팅 (가장 간단) ===
# QEMU의 내장 iPXE ROM으로 PXE 부팅
# -boot n: 네트워크 부팅 우선
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -nic user,tftp=/var/lib/tftpboot,bootfile=pxelinux.0 \
    -nographic  # 시리얼 콘솔 모드

# === 2. 브리지 네트워크 + 실제 PXE 서버 ===
# 실제 네트워크의 DHCP/PXE 서버에 연결
sudo qemu-system-x86_64 \
    -m 4096 \
    -cpu host -enable-kvm \
    -boot order=n \
    -device virtio-net-pci,netdev=br0,mac=52:54:00:12:34:56 \
    -netdev bridge,id=br0,br=virbr0 \
    -drive file=test-disk.qcow2,format=qcow2,if=virtio \
    -vnc :1

# === 3. UEFI PXE 부팅 테스트 ===
# OVMF (Open Virtual Machine Firmware) 사용
sudo apt install ovmf
qemu-system-x86_64 \
    -m 4096 \
    -cpu host -enable-kvm \
    -bios /usr/share/OVMF/OVMF_CODE.fd \
    -boot order=n \
    -device virtio-net-pci,netdev=net0 \
    -netdev bridge,id=net0,br=virbr0 \
    -drive file=test-uefi.qcow2,format=qcow2,if=virtio

# === 4. iPXE ROM 커스텀 빌드로 교체 ===
# 커스텀 iPXE ROM을 QEMU에서 사용
cd ipxe/src
make bin/8086100e.rom EMBED=embed.ipxe  # Intel e1000 ROM
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -device e1000,netdev=net0,romfile=bin/8086100e.rom \
    -netdev bridge,id=net0,br=virbr0

# === 5. 내부 DHCP 없이 테스트 (user 모드) ===
# QEMU user-mode 네트워킹으로 간단 테스트
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -nic user,model=virtio-net-pci,\
tftp=/var/lib/tftpboot,\
bootfile=pxelinux.0,\
dhcpstart=10.0.2.100

# === 6. libvirt/virsh로 PXE VM 생성 ===
virt-install \
    --name pxe-test \
    --ram 4096 \
    --vcpus 2 \
    --disk size=20,format=qcow2 \
    --network bridge=virbr0,model=virtio \
    --pxe \
    --boot network,hd \
    --os-variant ubuntu24.04 \
    --graphics vnc

# === 7. 디버깅: QEMU 모니터에서 네트워크 확인 ===
# Ctrl+Alt+2 로 QEMU 모니터 진입
# (qemu) info network         # 네트워크 장치 목록
# (qemu) info pci             # PCI 장치 (NIC 확인)
# (qemu) hostfwd_add tcp::5555-:22  # SSH 포트 포워딩
QEMU 내장 iPXE 버전: QEMU에 내장된 iPXE ROM은 패키지 빌드 시점의 버전입니다. 최신 기능이 필요하면 romfile= 옵션으로 직접 빌드한 iPXE ROM을 사용하세요. virtio-net-pci 드라이버가 성능이 가장 좋고, iPXE도 virtio 드라이버를 내장하고 있습니다.

Preseed 자동 설치 예제 (Debian 13)

# /var/www/pxeboot/preseed/debian13.cfg
# Debian 13 Trixie 무인 설치 (Preseed)

# 로케일 및 키보드
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us

# 네트워크
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string debian-server
d-i netcfg/get_domain string example.com

# 미러 (로컬 미러 사용 시)
d-i mirror/country string manual
d-i mirror/http/hostname string 192.168.1.10
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string

# 시간대
d-i time/zone string Asia/Seoul
d-i clock-setup/utc boolean true
d-i clock-setup/ntp boolean true

# 파티셔닝 (UEFI + GPT, 전체 디스크 LVM)
d-i partman-efi/non_efi_system boolean true
d-i partman-partitioning/choose_label select gpt
d-i partman-partitioning/default_label string gpt
d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

# 루트 비밀번호 (해시)
d-i passwd/root-password-crypted password $6$rounds=656000$...

# 사용자 계정
d-i passwd/user-fullname string Admin User
d-i passwd/username string admin
d-i passwd/user-password-crypted password $6$rounds=656000$...

# 패키지
d-i pkgsel/include string openssh-server vim tmux nfs-common open-iscsi
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false

# 부트로더 (GRUB)
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default

# 설치 완료 후
d-i finish-install/reboot_in_progress note
d-i preseed/late_command string \
    mkdir -p /target/root/.ssh; \
    wget -O /target/root/.ssh/authorized_keys http://192.168.1.10/keys/admin.pub; \
    chmod 600 /target/root/.ssh/authorized_keys; \
    in-target systemctl enable ssh
Debian Preseed 실무 메모: Debian stable은 2026-03-07 기준 13 (trixie)이며, 네트워크 프리시드는 여전히 preseed/url 또는 그 축약형 url=을 사용합니다. UEFI 서버를 기본 대상으로 둘 때는 partman-efi/non_efi_system과 GPT 라벨 선택을 같이 넣어 두는 편이 BIOS/UEFI 혼합 환경에서 덜 헷갈립니다.

Raspberry Pi 네트워크 부팅

# Raspberry Pi 4는 UEFI가 아닌 자체 네트워크 부팅 메커니즘 사용
# BCM2711 SoC의 부트 ROM이 직접 DHCP + TFTP 수행

# 1. Raspberry Pi의 시리얼 번호 확인
# Pi에서: cat /proc/cpuinfo | grep Serial
# 예: Serial : 00000000abcd1234 → 마지막 8자리: abcd1234

# 2. TFTP 디렉토리 구조 (시리얼 번호 기반)
/var/lib/tftpboot/
├── abcd1234/           # Pi 시리얼 번호
│   ├── start4.elf      # GPU 펌웨어
│   ├── fixup4.dat      # GPU 메모리 수정
│   ├── bcm2711-rpi-4-b.dtb
│   ├── config.txt      # 부팅 설정
│   ├── cmdline.txt     # 커널 커맨드 라인
│   └── kernel8.img     # ARM64 커널
└── overlays/           # DT 오버레이

# 3. config.txt (네트워크 루트)
cat > /var/lib/tftpboot/abcd1234/config.txt << 'EOF'
arm_64bit=1
kernel=kernel8.img
initramfs initrd.img followkernel
EOF

# 4. cmdline.txt
echo "console=serial0,115200 console=tty1 root=/dev/nfs \
nfsroot=192.168.1.10:/exports/pi-root,vers=4,tcp ip=dhcp rw \
rootwait" > /var/lib/tftpboot/abcd1234/cmdline.txt

# 5. dnsmasq 설정 (Pi 전용)
# Pi는 Vendor Class를 보내지 않으므로, MAC 또는 태그로 분기
dhcp-host=dc:a6:32:xx:xx:xx,set:rpi
dhcp-boot=tag:rpi,abcd1234/start4.elf
pxe-service=0,"Raspberry Pi Boot"

Windows WDS/MDT 참고

Windows 배포 서비스 (WDS)

Windows Deployment Services(WDS)는 Microsoft의 PXE 기반 OS 배포 솔루션입니다. 리눅스 PXE 환경과의 호환성을 이해하는 것이 중요합니다.

항목Linux PXEWindows WDS
DHCPISC DHCP / dnsmasqWindows DHCP Server
TFTPtftpd-hpa / dnsmasqWDS TFTP 내장
BIOS NBPpxelinux.0wdsnbp.com → pxeboot.com
UEFI NBPgrubx64.efiwdsmgfw.efi → bootmgfw.efi
이미지 형식vmlinuz + initrdWIM (Windows Imaging)
자동 설치Kickstart/Preseedunattend.xml
멀티캐스트atftpd/UDPcastWDS 멀티캐스트 내장

Linux + Windows 듀얼 PXE 환경

# iPXE 스크립트로 Linux/Windows 선택
#!ipxe

menu Dual Boot PXE Menu
item linux    Linux Installation
item windows  Windows Installation (WDS)
choose --default linux --timeout 20000 target && goto ${target}

:linux
chain http://192.168.1.10/linux-boot.ipxe

:windows
# WDS 서버로 체인로딩
# iPXE가 WDS의 wdsnbp.com을 로드하여 Windows 설치 시작
set next-server 192.168.1.20  # WDS 서버
chain tftp://${next-server}/boot/x64/wdsnbp.com

리눅스 커널 소스 분석

네트워크 부팅 관련 커널 소스 파일

파일역할
net/ipv4/ipconfig.c커널 IP 자동 설정 (DHCP/BOOTP/RARP). ip= 파라미터 처리
fs/nfs/nfsroot.cNFS 루트 마운트. nfsroot= 파라미터 처리
init/do_mounts.c루트 파일시스템 마운트 (root=/dev/nfs 감지)
drivers/firmware/efi/libstub/EFI Stub: UEFI에서 직접 커널 부팅
drivers/net/ethernet/이더넷 드라이버 (부팅 시 네트워크 초기화)
include/linux/nfs_fs.hNFS 클라이언트 헤더
drivers/scsi/iscsi_tcp.ciSCSI TCP 트랜스포트
drivers/infiniband/ulp/iser/iSCSI over RDMA (iSER)

네트워크 부팅 관련 커널 설정

# make menuconfig에서 네트워크 부팅에 필요한 옵션

# Networking → Networking options
CONFIG_IP_PNP=y              # IP: kernel level autoconfiguration
CONFIG_IP_PNP_DHCP=y         # IP: DHCP support
CONFIG_IP_PNP_BOOTP=y        # IP: BOOTP support
CONFIG_IP_PNP_RARP=y         # IP: RARP support

# File systems → Network File Systems
CONFIG_NFS_FS=y              # NFS client support (built-in, NOT module!)
CONFIG_NFS_V3=y              # NFS client support for NFS version 3
CONFIG_NFS_V4=y              # NFS client support for NFS version 4
CONFIG_ROOT_NFS=y            # Root file system on NFS

# Block devices → Network block devices
CONFIG_BLK_DEV_NBD=m         # Network Block Device
CONFIG_ISCSI_TCP=m           # iSCSI Initiator over TCP/IP

# 네트워크 드라이버 (부팅에 사용하는 NIC는 built-in 필수!)
CONFIG_E1000E=y              # Intel(R) PRO/1000 PCI-Express
CONFIG_IGB=y                 # Intel(R) 82575/82576 PCI-Express
CONFIG_IXGBE=y               # Intel(R) 10GbE PCI Express
CONFIG_MLX5_CORE=y           # Mellanox ConnectX
CONFIG_R8169=y               # Realtek RTL8169
CONFIG_BNX2X=y               # Broadcom NetXtreme II 10Gb

# initramfs 사용 시 NFS/iSCSI를 모듈로 컴파일해도 됨
# 단, initramfs에 해당 모듈을 포함해야 함

커널 IP 자동 설정 흐름

/* net/ipv4/ipconfig.c - 커널 네트워크 자동 설정 전체 흐름 */

/*
 * 호출 순서:
 * start_kernel()
 *   → kernel_init()
 *     → kernel_init_freeable()
 *       → do_basic_setup()
 *       → prepare_namespace()
 *         → ip_auto_config()      ← ip= 파라미터 처리
 *         → mount_root()           ← root= 파라미터 처리
 *           → nfs_root_setup()     ← root=/dev/nfs 시
 */

static int __init ip_auto_config(void)
{
    /* 1. 네트워크 인터페이스 탐색 */
    ic_open_devs();  /* 모든 이더넷 디바이스 열기 */

    /* 2. DHCP/BOOTP/RARP 중 활성화된 프로토콜로 IP 설정 */
    if (ic_enable) {
        /* DHCP 패킷 송신 */
        ic_bootp_init();

        /* 응답 대기 (최대 ~60초) */
        ic_dynamic();

        /* IP, 게이트웨이, DNS, NFS root path 등 설정 */
        ic_setup_if();     /* 인터페이스 IP 설정 */
        ic_setup_routes(); /* 라우팅 테이블 설정 */
    }

    /* 3. 결과 출력 */
    pr_info("IP-Config: Complete:\n");
    pr_info("     device=%s, hwaddr=%pM, ipaddr=%pI4, "
            "mask=%pI4, gw=%pI4\n",
            ic_dev->name, ic_dev->dev_addr,
            &ic_myaddr, &ic_netmask, &ic_gateway);
    pr_info("     host=%s, domain=%s, nis-domain=%s\n",
            utsname()->nodename, ic_domain, utsname()->domainname);
    pr_info("     bootserver=%pI4, rootserver=%pI4, rootpath=%s\n",
            &ic_servaddr, &root_server_addr, root_server_path);

    return 0;
}

클라우드 & 베어메탈 PXE 패턴

클라우드 환경의 PXE 부팅

퍼블릭 클라우드의 베어메탈 서비스와 프라이빗 클라우드 인프라에서 PXE는 서버 프로비저닝의 핵심 메커니즘입니다.

플랫폼PXE 방식자동화특징
Equinix MetaliPXE + Custom OSAPI + iPXE 스크립트사용자 iPXE 스크립트 URL 지정 가능. 커스텀 OS 부팅 지원
AWS OutpostsPXE + EC2 이미지SSM + CloudFormation온프레미스 AWS 인프라에서 PXE 부팅
Oracle BMiPXEOCI API베어메탈 인스턴스에 iPXE 스크립트 제공
HetznerDHCP + iPXERobot APIrescue 시스템 + installimage 스크립트
OpenStack IronicPXE/iPXE + IPANova APIIronic Python Agent로 디스크 기록
MAAS (Canonical)PXE + cloud-initREST API + CLI자동 발견 → commissioning → 배포
Tinkerbell (Equinix)iPXE + WorkflowgRPC API클라우드 네이티브, Kubernetes 기반

클라우드 베어메탈 iPXE 커스텀 부팅 예제

# Equinix Metal 스타일: 사용자 제공 iPXE 스크립트로 커스텀 OS 부팅
#!ipxe

# 서버의 메타데이터 조회 (클라우드 API에서 제공)
# Equinix Metal은 metadata.packet.net 제공
# chain https://metadata.packet.net/ipxe?uuid=${uuid}&mac=${mac}

# 또는 직접 스크립트 작성:
set base-url https://my-pxe-server.example.com

# 호스트 메타데이터 기반 분기
chain ${base-url}/api/v1/boot?uuid=${uuid}&serial=${serial}&mac=${mac:hexhyp} || goto fallback

:fallback
# Flatcar Container Linux 부팅 (CoreOS 후속)
set coreos-url https://stable.release.flatcar-linux.net/amd64-usr/current
kernel ${coreos-url}/flatcar_production_pxe.vmlinuz \
    flatcar.first_boot=1 \
    flatcar.config.url=${base-url}/ignition/${uuid}.json \
    ip=dhcp
initrd ${coreos-url}/flatcar_production_pxe_image.cpio.gz
boot
// Ignition 설정 예제 (Flatcar/CoreOS 자동 프로비저닝)
// /var/www/pxeboot/ignition/default.json
{
  "ignition": { "version": "3.4.0" },
  "storage": {
    "filesystems": [{
      "device": "/dev/sda1",
      "format": "ext4",
      "path": "/",
      "wipeFilesystem": true
    }]
  },
  "systemd": {
    "units": [{
      "name": "docker.service",
      "enabled": true
    }]
  },
  "passwd": {
    "users": [{
      "name": "core",
      "sshAuthorizedKeys": ["ssh-ed25519 AAAA... admin@example.com"],
      "groups": ["docker", "sudo"]
    }]
  }
}

OpenStack Ironic PXE 배포 흐름

OpenStack Ironic 베어메탈 프로비저닝 (PXE) 1. Enroll IPMI/Redfish 정보 등록 MAC, BMC IP ironic node-create 2. Inspect IPA PXE 부팅 H/W 정보 수집 CPU/RAM/Disk 3. Clean IPA PXE 부팅 디스크 초기화 BIOS 설정 4. Deploy IPA PXE 부팅 이미지 기록 configdrive 적용 5. Active 로컬 디스크 부팅 서비스 가동 Nova 관리 하에 IPA (Ironic Python Agent) — PXE로 부팅되는 경량 에이전트 - 커널 + initramfs 이미지 (약 200-400MB). 자체 Python 런타임 + ironic_python_agent 패키지 포함 - REST API 서버로 동작: Ironic Conductor가 HTTP로 명령 전달 - 기능: 하드웨어 인벤토리, 디스크 초기화(shred/hdparm), 이미지 기록(dd/qemu-img), RAID 설정, BIOS 설정 - 배포 이미지: QCOW2/RAW → IPA가 다운로드 → /dev/sda에 기록 → 부트로더 설치 - PXE 설정: Ironic이 자동으로 DHCP/TFTP를 관리 (iPXE 또는 PXELINUX/GRUB 사용)
# OpenStack Ironic 베어메탈 노드 등록 및 배포

# 1. 노드 등록
openstack baremetal node create \
    --driver ipmi \
    --driver-info ipmi_address=10.0.0.100 \
    --driver-info ipmi_username=admin \
    --driver-info ipmi_password=secret \
    --driver-info deploy_kernel=file:///var/lib/ironic/ipa-kernel \
    --driver-info deploy_ramdisk=file:///var/lib/ironic/ipa-ramdisk \
    --property cpus=32 --property memory_mb=65536 \
    --property local_gb=500

# 2. 포트(NIC) 등록
openstack baremetal port create \
    --node  \
    00:11:22:33:44:55  # PXE 부팅용 MAC 주소

# 3. 이미지 기반 배포
openstack baremetal node deploy  \
    --instance-info image_source= \
    --instance-info root_gb=50

# Ironic이 자동으로:
# 1) IPMI로 서버 전원 ON
# 2) PXE 부팅 → IPA 로드
# 3) IPA가 이미지 다운로드 → 디스크 기록
# 4) PXE에서 로컬 부팅으로 전환
# 5) 서버 재부팅 → OS 가동

흔한 실수

실수 1: 아키텍처 불일치
BIOS 모드 클라이언트에 EFI 바이너리(.efi)를, UEFI 클라이언트에 BIOS 바이너리(.0)를 제공하면 부팅 실패합니다. DHCP Option 93을 확인하여 올바른 바이너리를 제공하세요.
실수 2: TFTP 경로 문제
TFTP는 tftp-root를 기준으로 경로를 해석합니다. 절대 경로(/var/lib/tftpboot/pxelinux.0)가 아니라 상대 경로(pxelinux.0)를 사용해야 합니다.
실수 3: 방화벽 미개방
PXE에 필요한 포트: UDP 67/68(DHCP), UDP 69(TFTP), UDP 4011(ProxyDHCP), TCP 2049(NFS v4), TCP 3260(iSCSI), TCP 80/443(HTTP/HTTPS). 특히 TFTP는 데이터 전송 시 랜덤 고번호 포트를 사용하므로 conntrack 모듈이 필요합니다: modprobe nf_conntrack_tftp
실수 4: NFS root에서 모듈 누락
NFS 루트 부팅 시 NIC 드라이버와 NFS 클라이언트가 커널에 built-in(=y)이거나, initramfs에 모듈이 포함되어야 합니다. 모듈(=m)만 있고 initramfs에 없으면 네트워크를 활성화할 수 없어 루트를 마운트할 수 없습니다.
실수 5: iPXE 무한 루프
iPXE 체인로딩 설정에서 iPXE와 일반 PXE를 구분하지 않으면 무한 루프가 발생합니다. ISC dhcpd 예제는 보통 user-class = "iPXE"를, dnsmasq 예제는 Option 175를 자주 사용합니다. 어느 방식을 쓰든 "이미 iPXE에 들어온 클라이언트"를 한 번만 식별해 HTTP 스크립트로 넘겨야 합니다.
실수 6: SYSLINUX 모듈 누락
PXELINUX를 사용할 때 ldlinux.c32, libutil.c32, libcom32.c32 등 필수 COM32 모듈을 TFTP 루트에 복사하지 않으면 메뉴가 표시되지 않습니다. SYSLINUX 버전과 모듈 버전이 일치해야 합니다.

표준 및 공식 문서

PXE는 구현 편차가 큰 분야라서 블로그 글보다 표준 문서와 공식 구현 문서를 같이 보는 편이 안전합니다. 아래 문서들은 이 페이지를 보강하면서 직접 대조한 기준 자료입니다.

이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.