Proxmox VE : Automated Installation

Proxmox VE 8.2에서 추가된 기능을 살펴보던 중에 “Support for automated and unattended installation“이 있어서 자료를 찾아 보았습니다. 설명서에 추가된 부분은 없고, 위키 문서로 따로 정리되어 있어서, AI의 힘을 빌려 기계번역해 보았습니다.

출처 : https://pve.proxmox.com/wiki/Automated_Installation

소개

자동화된 설치 방법을 사용하면 무인 방식으로 Proxmox 솔루션을 설치할 수 있습니다. 이를 통해 베어메탈에서 설치 프로세스를 완전히 자동화할 수 있습니다. 설치가 완료되고 호스트가 부팅되면 Ansible과 같은 자동화 도구를 사용하여 설치를 추가로 구성할 수 있습니다.

설치 프로그램에 필요한 옵션은 응답 파일(answer file)에 제공되어야 합니다. 이 파일을 사용하면 필터 규칙을 사용하여 어떤 디스크와 네트워크 카드를 사용해야 하는지 결정할 수 있습니다.

자동 설치를 사용하려면 먼저 응답 파일을 가져올 소스를 선택한 다음 해당 선택 사항으로 설치 ISO를 준비해야 합니다.

ISO가 준비되면 초기 부팅 메뉴에 ‘자동 설치’라는 새 부팅 항목이 표시되고 10초의 시간이 지나면 이 항목이 자동으로 선택됩니다.

개요

보조 도구

proxmox-auto-install-assistant 는 자동 설치를 위해 충분히 새롭지만 그렇지 않은 표준 ISO를 준비하는 데 사용할 수 있는 prepare-iso 하위 명령을 제공합니다. 먼저 설치해야 합니다:

apt install proxmox-auto-install-assistant

참고: ISO를 준비하려면 xorriso 바이너리가 필요합니다. Debian 기반 시스템에서는 apt install xorriso를 사용하여 설치할 수 있습니다.

자세한 내용은 하위 명령의 도움말을 참조하세요: proxmox-auto-install-assistant prepare-iso --help

응답 파일 형식

응답 파일은 루트 비밀번호, 네트워크 구성 및 대상 루트 디스크와 같은 시스템의 기본 구성을 제공하는 TOML 형식의 구성 파일입니다.

예를 들어 디스크 및 네트워크 장치가 많은 시스템을 설치할 수 있도록 하기 위해 일련 번호, 공급업체 또는 네트워크 인터페이스의 MAC 주소와 같은 기타 고유 세부 정보와 같은 장치 속성에 필터를 사용하여 일치시킬 수 있습니다.

스키마 및 사용 가능한 필터에 대한 자세한 내용은 응답 파일 형식 섹션을 참조하세요.

응답 파일 소스

자동 설치 프로그램이 응답 파일을 가져올 수 있는 위치는 여러 곳이 있습니다:

  • ISO에서 직접
  • PROXMOX-AIS 파티션 레이블이 있는 별도의 파티션에서 읽기
  • 네트워크에서 HTTP를 통해 가져오기
    • 소스 URL을 미리 결정하거나 DNS 또는 DHCP에서 쿼리할 수 있습니다.

지금은 정확히 하나의 소스를 선택해야 합니다.

설치 ISO 준비

가능한 소스에 대한 자세한 설명과 다양한 가져오기 모드를 사용하여 ISO를 준비하는 방법에 대한 예는 다음을 참조하세요.

ISO에 포함된 응답

answer.toml을 직접 포함하도록 ISO를 준비하여 자동 설치를 롤아웃하기 위한 통합된 매체를 가질 수 있습니다.

proxmox-auto-install-assistant prepare-iso /path/to/source.iso --fetch-from iso --answer-file /path/to/answer.toml

별도의 파티션에 답변

ISO는 answer.toml 파일이 포함된 proxmox-ais 또는 PROXMOX-AIS(Automated Installation Source)로 레이블이 지정된 파일 시스템(예: USB 플래시 드라이브)에 준비할 수 있습니다.

proxmox-auto-install-assistant prepare-iso /path/to/source.iso --fetch-from partition

그런 다음 USB 플래시 드라이브를 준비합니다(예: /dev/sdX1). 그런 다음 다음 명령을 루트 권한으로 변경하여 실행할 수 있습니다:

# format as new vfat file system (DESTRUCTIVE!)
mkfs.vfat /dev/sdX1

# set the label
fatlabel /dev/sdX1 "PROXMOX-AIS"

# mount the USB pen drive 
mkdir /mnt/usb
mount /dev/sdX1 /mnt/usb

# copy over the answer file as "answer.toml"
cp my-prepared-answer.toml /mnt/usb/answer.toml

# unmount the USB pen drive
sync
umount /mnt/usb

HTTP를 통해 응답 가져오기

HTTP를 통해 답변 파일을 가져오려면 설치 관리자가 URL을 알아야 합니다. URL은 다음과 같은 방법을 통해 제공될 수 있습니다:

  • ISO에 정의된 URL
  • DHCP 옵션으로(250)
  • DNS TXT 레코드를 통해
    • DNS TXT 레코드는 proxmox-auto-installer.{search domain}에 위치해야 하며, 여기서 {search domain}은 DHCP 서버에서 제공하는 search domain입니다.

참고: DHCP 또는 DNS 레코드를 통해 지문을 가져오는 것은 동일한 방법으로 URL을 검색하는 경우에만 수행됩니다!

HTTP(S) POST 요청은 실제 컴퓨터를 식별하고 이 정보를 사용하여 사용자 지정 응답 파일을 생성하는 데 도움이 되는 JSON 데이터를 전송합니다.

이 정보가 어떻게 보이는지는 sudo proxmox-auto-install-assistant system-info 명령을 사용하여 확인할 수 있습니다.

인증서 지문 매칭

TLS 인증서의 ‘SHA256’ 인증서 지문을 제공할 수 있습니다. 이 기능은 다음과 같은 상황에서 유용합니다:

  • URL이 FQDN 대신 IP 주소를 사용하는 경우
  • 설치 관리자가 신뢰하지 않는 자체 서명 인증서를 사용하는 경우
  • 서버의 알려진 인증서를 고정하여 보안을 강화한 경우

인스톨러에 ‘SHA256’ 지문을 제공하는 방법에는 세 가지가 있습니다:

  • ISO에 정의된 핑거프린트
  • DHCP 옵션(251)으로
  • DNS TXT 레코드를 통해
    • DNS TXT 레코드는 proxmox-auto-installer-cert-fingerprint.{search domain}에 위치해야 하며, 여기서 {search domain}은 DHCP 서버에서 제공하는 검색 도메인입니다.

참고: DHCP 또는 DNS 레코드를 통해 지문을 가져오는 것은 URL을 검색하는 데 동일한 방법을 사용하는 경우에만 수행됩니다!

예제

마찬가지로 설치 관리자가 응답 파일과 TLS 인증서 지문을 가져올 URL을 지정할 수 있습니다. 예를 들어 둘 다 지정할 수 있습니다:

proxmox-auto-install-assistant prepare-iso /path/to/source.iso --fetch-from http --url "https://10.0.0.100/get_answer/" --cert-fingerprint "04:42:97:27:F6:29:2F:9F:3D:7F:13:11:C8:E2:F5:5F:84:03:95:D9:F5:14:72:7C:9E:90:47:03:D2:96:2B:EC"

응답 파일 형식

응답 파일은 TOML 형식으로 예상됩니다.

다음 예는 DHCP 제공 네트워크 설정을 사용하는 기본 응답 파일로, 디스크 sda 및 sdb의 RAID-1에서 ZFS를 사용합니다:

[global]
keyboard = "de"
country = "at"
fqdn = "pveauto.testinstall"
mailto = "mail@no.invalid"
timezone = "Europe/Vienna"
root_password = "123456"
root_ssh_keys = [
    "ssh-ed25519 AAAA..."
]

[network]
source = "from-dhcp"

[disk-setup]
filesystem = "zfs"
zfs.raid = "raid1"
disk_list = ["sda", "sdb"]

global 섹션

이 섹션에는 다음과 같은 키가 포함되어 있습니다:

  • 키보드 – 다음과 같은 옵션이 있는 키보드 레이아웃입니다:
de de-ch dk en-gb en-us es fi fr fr-be fr-ca fr-ch hu is it jp lt mk nl no pl pt pt-br se si tr 
  • country – 두 글자 변형의 국가 코드입니다. 예: at, us 또는 fr.
  • fqdn – 호스트의 정규화된 도메인 이름입니다. 도메인 부분은 검색 도메인으로 사용됩니다.
  • mailto — 사용자 루트의 기본 이메일 주소입니다.
  • timezone — tzdata 형식의 표준 시간대입니다. 예를 들어, 유럽/비엔나 또는 미국/뉴욕.
  • root_password — 루트 사용자의 비밀번호입니다.
  • root_ssh_keys — 선택 사항입니다. 설치 후 루트 사용자의 authorized_keys 파일에 추가할 SSH 공개 키입니다.
  • reboot_on_error — true로 설정하면 오류가 발생하면 설치 프로그램이 자동으로 재부팅됩니다. 기본 동작은 관리자가 설치 실패 원인을 조사할 수 있도록 기다리는 것입니다.

network 섹션

이 섹션에는 다음과 같은 키가 포함되어 있습니다:

  • source – 정적 네트워크 구성을 가져올 위치입니다. 이 값은 from-dhcp 또는 from-answer일 수 있습니다.
    from-dhcp로 설정하면 다른 네트워크 옵션은 무시되고 설치 관리자는 설치 중에 수신한 활성 NIC 및 DHCP 설정을 사용하여 정적 네트워크 구성을 작성합니다.
  • cidr – CIDR 표기법의 IP 주소입니다. 예: 192.168.1.10/24
  • dns — DNS 서버의 IP 주소입니다.
  • gateway — 기본 게이트웨이의 IP 주소입니다.
  • filter – 네트워크 카드를 선택하기 위해 UDEV 속성에 대해 필터링합니다. 필터를 참조하십시오.

disk-setup 섹션

이 섹션에는 다음 키가 포함되어 있습니다:

  • filesystem — 다음 옵션 중 하나: ext4, xfs, zfs 또는 btrfs.
  • disk_list — 사용할 디스크 목록입니다. 디스크 이름이 확실한 경우에 유용합니다. 예: disk_list = [“sda”, “sdb”]
  • filter — UDEV 속성을 기준으로 필터링하여 설치할 디스크를 선택합니다. 필터를 참조하세요.
    참고: disk_list 또는 filter 중 하나를 사용합니다. 둘 다 정의하는 것은 허용되지 않습니다.
  • filter_match — “any” 또는 “all”일 수 있습니다. 디스크를 선택하려면 모든 필터가 일치해야 하는지 모든 필터가 일치하는 것으로 충분한지 여부를 결정합니다. 기본값은 “any”입니다.
  • zfs — ZFS 관련 속성을 정의합니다. 설명서의 ZFS 고급 옵션을 참조하세요. 속성은 다음과 같습니다:
    • raid – 사용할 RAID 레벨입니다. 옵션은 raid0, raid1, raid10, raidz-1, raidz-2 또는 raidz-3입니다.
    • ashift
    • arc_max
    • checksum
    • compress
    • copies
    • hdsize
  • lvm — ext4 또는 xfs 파일 시스템에서 사용할 수 있는 고급 속성입니다. LVM 고급 옵션을 참조하십시오. 속성은 다음과 같습니다:
    • hdsize
    • swapsize
    • maxroot
    • maxvz
    • minfree
  • btrfs — BTRFS 관련 옵션을 정의합니다.
    • raid — 사용할 레이드 레벨입니다. 옵션은 raid0, raid1, raid10입니다.
    • hdsize

응답 파일 유효성 검사

proxmox-auto-install-assistant 도구를 사용하여 응답 파일의 구문을 검증하고 응답 파일을 가져올 때 HTTP 서버로 전송할 식별 정보를 표시할 수도 있습니다.

예를 들어 응답 파일의 유효성을 검사하는 데 사용할 수 있습니다:

$ proxmox-auto-install-assistant validate-answer answer.toml 
The file was parsed successfully, no syntax errors found!

필터

필터를 사용하면 udevadm에 의해 노출된 장치 속성과 일치시킬 수 있습니다.

프로엑스박스 자동 설치 도우미 유틸리티는 이러한 속성을 표시할 수 있으며 필터를 미리 테스트할 수 있습니다. 이 유틸리티는 설치 관리자 환경(디버그 모드를 통해)과 기존 Proxmox 가상 환경 설치에서 사용할 수 있습니다.

예를 들어 사용 가능한 디스크에 대한 정보를 가져오는 데 사용할 수 있습니다:

$ proxmox-auto-install-assistant device-info -t disk
[…]
    "nvme1n1": {
      "CURRENT_TAGS": ":systemd:",
      "DEVLINKS": "/dev/disk/by-id/nvme-KIOXIA_KCMYXVUG1T60_9DUXXXXXXXX_1 /dev/disk/by-path/pci-0000:e2:00.0-nvme-1 /dev/disk/by-diskseq/12 /dev/disk/by-id/nvme-eui.01000000000000008ce38ee300708529 /dev/disk/by-id/nvme-KIOXIA_KCMYXVUG1T60_9DUXXXXXXXX",
      "DEVNAME": "/dev/nvme1n1",
      "DEVPATH": "/devices/virtual/nvme-subsystem/nvme-subsys1/nvme1n1",
      "DEVTYPE": "disk",
      "DISKSEQ": "12",
      "ID_MODEL": "KIOXIA KCMYXVUG1T60",
      "ID_NSID": "1",
      "ID_PART_TABLE_TYPE": "gpt",
      "ID_PART_TABLE_UUID": "539fabeb-aecd-6643-94a9-f28a68cfa12d",
      "ID_PATH": "pci-0000:e2:00.0-nvme-1",
      "ID_PATH_TAG": "pci-0000_e2_00_0-nvme-1",
      "ID_REVISION": "1UETE103",
      "ID_SERIAL": "KIOXIA_KCMYXVUG1T60_9DUXXXXXXXX_1",
      "ID_SERIAL_SHORT": "9DU0A02D0L33",
      "ID_WWN": "eui.01000000000000008ce38ee300708529",
      "MAJOR": "259",
      "MINOR": "5",
      "SUBSYSTEM": "block",
      "TAGS": ":systemd:",
      "USEC_INITIALIZED": "3169050"
    },
[…]

필터의 키에 따라 필터가 적용될 속성이 결정됩니다. 예를 들어 디스크의 공급업체 및 모델 번호와 일치시키기 위해 답변 파일의 필터는 다음과 같이 보일 수 있습니다:

filter.ID_SERIAL = "KIOXIA_KCMYXVUG1T60*"

참고: 끝에 있는 * 글로브 기호는 정의된 필터 뒤에 있는 모든 것을 일치시키는 데 사용됩니다!

이 필터는 해당 모델 번호를 가진 모든 Kioxia 디스크에 대해 일치합니다. 다음 명령을 실행하여 필터가 어떤 디스크를 찾을지 확인할 수 있습니다:

$ proxmox-auto-install-assistant device-match disk ID_SERIAL='KIOXIA_KCMYXVUG1T60*'
[
  "nvme0n1",
  "nvme1n1",
  "nvme4n1",
  "nvme5n1"
]

filter_match 매개변수는 모든 필터를 적용해야 하는지, 아니면 필터 중 하나라도 일치하면 충분한지 여부를 제어합니다. 이를 통해 다른 속성을 사용하여 설치에 다른 디스크 모델을 사용할 수 있습니다.

예를 들어

filter.ID_SERIAL = "KIOXIA*"
filter.ID_MODEL = "ATP*"

참고: 네트워크 카드의 경우 설치 프로그램에는 네트워크 카드가 하나만 필요하므로 첫 번째 일치 항목만 사용됩니다.

더 복잡한 네트워크 설정은 설치 후에 구성할 수 있습니다. 고유 식별자가 있는 속성을 사용하면 가장 예측 가능한 동작(예: MAC 주소)을 얻을 수 있습니다.

필터 문법

필터에는 다음과 같은 특수 문자를 사용할 수 있습니다:

  • ? — 단일 문자와 일치
  • * — 임의의 수의 문자와 일치, 없음 가능
  • [a], [abc], [0-9] — 괄호 안의 모든 단일 문자와 일치, 범위 가능
  • [!a] — 필터를 무효화, 지정된 문자를 제외한 모든 단일 문자를 일치시킵니다.

유용한 속성

네트워크 카드의 경우 다음 속성이 유용할 수 있습니다:

  • ID_NET_NAME
  • ID_NET_NAME_MAC
  • ID_VENDOR_From_Database
  • ID_MODEL_From_Database

디스크의 경우 다음 속성이 유용할 수 있습니다:

  • DEVNAME
  • ID_SERIAL_SHORT
  • ID_WWN
  • ID_MODEL
  • ID_SERIAL

응답 파일

삼성 디스크의 ZFS 미러

삼성 디스크가 두 개만 있다고 가정합니다. 이 디스크는 ZFS 미러(RAID-1)의 OS에 사용해야 합니다. 또한 디스크 용량 중 150GB만 사용하고 나머지는 추가 사용자 지정을 위해 비워 두려고 합니다.

[global]
keyboard = "de"
country = "at"
fqdn = "pveauto.testinstall"
mailto = "mail@no.invalid"
timezone = "Europe/Vienna"
root_password = "123456"

[network]
source = "from-dhcp"

[disk-setup]
filesystem = "zfs"
zfs.raid = "raid1"
zfs.hdsize = 150
filter.ID_MODEL = "Samsung*"

dev/sda에 스왑 및 데이터 LV가 없는 Ext4

dev/sda에 설치하고 스왑 및 데이터 LV를 0 크기로 정의합니다.

[global]
keyboard = "de"
country = "at"
fqdn = "pveauto.testinstall"
mailto = "mail@no.invalid"
timezone = "Europe/Vienna"
root_password = "123456"

[network]
source = "from-dhcp"

[disk-setup]
filesystem = "ext4"
lvm.swapsize = 0
lvm.maxvz = 0
disk_list = ['sda']

수동 네트워크 구성과 ZFS 미러

[global]
keyboard = "de"
country = "at"
fqdn = "pveauto.testinstall"
mailto = "mail@no.invalid"
timezone = "Europe/Vienna"
root_password = "123456"

[network]
source = "from-answer"
cidr = "10.10.10.10/24"
dns = "10.10.10.1"
gateway = "10.10.10.1"
filter.ID_NET_NAME_MAC = "*e43d1afa379a"

[disk-setup]
filesystem = "zfs"
zfs.raid = "raid1"
filter.ID_MODEL = "Samsung*"

참고: 네트워크 카드에 대한 필터에는 *가 있습니다. 해당 속성에는 일반적으로 접두사 enx가 붙기 때문에 필수입니다.

프로엑스목스-자동 설치 지원 장치 정보 -t 네트워크와 함께 이 속성을 표시할 때 전체 값은 다음과 같이 표시됩니다:

"ID_NET_NAME_MAC": "enxe43d1afa379a",

HTTP를 통해 응답 파일 서비스하기

참고: 이는 단지 예시일 뿐입니다! 연결이 TLS를 통해 안전하게 보호되는지 또는 네트워크가 신뢰할 수 있는지 확인하세요.

서버에 TLS를 제공하는 역방향 프록시를 앞에 설정하는 것이 좋습니다.

netcat을 통해 정적 응답 파일 서비스하기

이 변형은 단일 응답 파일을 제공하기 위해 최소한의 작업을 수행합니다.

netcat-traditional이 설치되어 있는지 확인하세요:

apt update
apt install netcat-traditional

예를 들어 서버가 실행될 디렉터리를 만들고 적절한 권한을 부여합니다:

mkdir -p /srv/proxmox/auto-install-server
chmod 700 /srv/proxmox/auto-install-server

해당 디렉터리에 answer.toml 파일을 넣습니다.

그런 다음 다음과 같이 https://[HOST IP]:8000에 파일을 제공할 수 있습니다:

cd /srv/proxmox/auto-install-server
while true; do cat <(printf "HTTP/1.1 200 OK\n\n") answer.toml | nc -l -q 0 -p 8000; done &

프로세스는 백그라운드에서 실행됩니다.

참고: 역방향 프록시 등을 통해 연결이 TLS로 보호되는지 확인하세요.

프로세스를 종료하려면 해당 작업 ID 또는 PID를 통해 프로세스를 종료할 수 있습니다. 이러한 백그라운드 작업의 경우 jobs -l을 통해 나열할 수 있습니다.

예를 들어 프로세스의 작업 ID가 1이고 PID가 371012인 경우 kill %1 또는 kill 371012를 실행하여 프로세스를 종료할 수 있습니다.

Python을 통해 정적 응답 파일 서비스하기

이 옵션은 Python을 사용하여 단일 응답 파일을 제공하는 방법을 보여줍니다. 솔루션의 시작점이 될 수 있습니다.

python3-aiohttp가 설치되어 있는지 확인하세요:

apt update
apt install python3-aiohttp

예를 들어 서버가 실행될 디렉터리를 만들고 적절한 권한을 부여합니다:

mkdir -p /srv/proxmox/auto-install-server
chmod 700 /srv/proxmox/auto-install-server

해당 디렉터리에 answer.toml 파일을 넣습니다.

그런 다음 다음 스크립트를 서버의 디렉터리에도 server.py로 저장합니다:

import logging

from aiohttp import web

routes = web.RouteTableDef()


@routes.post("/answer")
async def answer(request: web.Request):
    logging.info(f"Received request from peer '{request.remote}'")

    file_contents = app.get("answer_file", None)

    if file_contents is None:
        return web.Response(status=404, text="not found")

    return web.Response(text=file_contents)


if __name__ == "__main__":
    app = web.Application()

    with open("answer.toml") as answer_file:
        file_contents = answer_file.read()

    app["answer_file"] = file_contents

    logging.basicConfig(level=logging.INFO)

    app.add_routes(routes)
    web.run_app(app, host="0.0.0.0", port=8000)

이제 서버의 디렉토리는 다음과 같이 보일 것입니다:

# tree -p /srv/proxmox/auto-install-server
[drwx------]  /srv/proxmox/auto-install-server
├── [-rw-r--r--]  answer.toml
└── [-rw-r--r--]  server.py

1 directory, 2 files

이제 파이썬으로 서버를 시작하여 https://[HOST IP]:8000/answer에서 답변 파일을 제공할 수 있습니다:

cd /srv/proxmox/auto-install-server
python3 server.py

서버는 포그라운드에서 실행되며 콘솔에서 CTRL+C를 눌러 종료할 수 있습니다.

참고: 역방향 프록시와 같은 것을 통해 연결이 TLS로 보호되는지 확인하세요.

Python을 통해 MAC 주소에 따라 응답 파일 제공하기

이것은 요청을 한 호스트의 MAC 주소에 따라 응답 파일을 동적으로 제공할 수 있는 약간 더 고급 예제입니다. 이는 HTTP POST 요청에서 JSON 데이터를 읽음으로써 이루어집니다.

python3-aiohttp 및 python3-tomlkit이 설치되어 있는지 확인하세요:

apt update
apt install python3-aiohttp python3-tomlkit

mkdir -p /srv/proxmox/auto-install-server
chmod 700 /srv/proxmox/auto-install-server

아래 서버를 실행하려면 먼저 다음을 설정해야 합니다:

  • 서버의 디렉터리에 default.toml이라는 파일을 만듭니다.
    이 파일은 일치하는 MAC 주소를 찾을 수 없는 경우 사용되는 대체 응답 파일입니다.
  • 서버의 디렉터리에 answers라는 이름의 디렉터리를 만듭니다.
    이 디렉터리에는 각 MAC 주소와 관련된 답변 파일이 포함됩니다.

그런 다음 답변 디렉터리에 원하는 만큼 답변 파일을 추가할 수 있습니다.

  • 각 파일의 이름은 연결된 MAC 주소의 이름을 따서 지어야 하며 끝은 .toml로 끝나야 합니다.
    예를 들면 다음과 같습니다: BC:24:11:AB:12:21.toml

그런 다음 다음 스크립트를 서버의 디렉터리에도 server.py로 저장합니다:

import logging
import json
import pathlib

try:
    import tomlkit
    from aiohttp import web
except ImportError as e:
    import sys

    message = """Could not import required packages.
Please ensure you've installed all necessary packages first!

On Debian-based distributions, you should be able to install them via:

\tapt update
\tapt install python3-aiohttp python3-tomlkit"""

    print(message, file=sys.stderr)

    raise e

DEFAULT_ANSWER_FILE_PATH = pathlib.Path("./default.toml")
ANSWER_FILE_DIR = pathlib.Path("./answers/")

routes = web.RouteTableDef()


@routes.post("/answer")
async def answer(request: web.Request):
    try:
        request_data = json.loads(await request.text())
    except json.JSONDecodeError as e:
        return web.Response(
            status=500,
            text=f"Internal Server Error: failed to parse request contents: {e}",
        )

    logging.info(
        f"Request data for peer '{request.remote}':\n"
        f"{json.dumps(request_data, indent=1)}"
    )

    try:
        answer = create_answer(request_data)

        logging.info(f"Answer file for peer '{request.remote}':\n{answer}")

        return web.Response(text=answer)
    except Exception as e:
        logging.exception(f"failed to create answer: {e}")
        return web.Response(status=500, text=f"Internal Server Error: {e}")


def create_answer(request_data: dict) -> str:
    with open(DEFAULT_ANSWER_FILE_PATH) as file:
        answer = tomlkit.parse(file.read())

    for nic in request_data.get("network_interfaces", []):
        if "mac" not in nic:
            continue

        answer_mac = lookup_answer_for_mac(nic["mac"])
        if answer_mac is not None:
            answer = answer_mac

    return tomlkit.dumps(answer)


def lookup_answer_for_mac(mac: str) -> tomlkit.TOMLDocument | None:
    mac = mac.lower()

    for filename in ANSWER_FILE_DIR.glob("*.toml"):
        if filename.name.lower().startswith(mac):
            with open(filename) as mac_file:
                return tomlkit.parse(mac_file.read())


def assert_default_answer_file_exists():
    if not DEFAULT_ANSWER_FILE_PATH.exists():
        raise RuntimeError(
            f"Default answer file '{DEFAULT_ANSWER_FILE_PATH}' does not exist"
        )


def assert_default_answer_file_parseable():
    with open(DEFAULT_ANSWER_FILE_PATH) as file:
        try:
            tomlkit.parse(file.read())
        except Exception as e:
            raise RuntimeError(
                "Could not parse default answer file "
                f"'{DEFAULT_ANSWER_FILE_PATH}':\n{e}"
            )


def assert_answer_dir_exists():
    if not ANSWER_FILE_DIR.exists():
        raise RuntimeError(f"Answer file directory '{ANSWER_FILE_DIR}' does not exist")


if __name__ == "__main__":
    assert_default_answer_file_exists()
    assert_answer_dir_exists()
    assert_default_answer_file_parseable()

    app = web.Application()

    logging.basicConfig(level=logging.INFO)

    app.add_routes(routes)
    web.run_app(app, host="0.0.0.0", port=8000)

이제 서버의 디렉토리는 다음과 같이 보일 것입니다:

# tree -p /srv/proxmox/auto-install-server
[drwx------]  /srv/proxmox/auto-install-server
├── [drwxr-xr-x]  answers
│   ├── [-rw-r--r--]  BC:24:11:AB:12:21.toml
│   ├── [-rw-r--r--]  BC:24:11:BE:F2:A2.toml
│   └── [-rw-r--r--]  BC:24:11:DC:CD:21.toml
├── [-rw-r--r--]  default.toml
└── [-rw-r--r--]  server.py

2 directories, 5 files

이제 Python으로 서버를 시작하여 https://[HOST IP]:8000/answer에서 답변 파일을 제공할 수 있습니다:

cd /srv/proxmox/auto-install-server
python3 server.py

서버는 포그라운드에서 실행되며 콘솔에서 CTRL+C를 눌러 종료할 수 있습니다.

참고: 역방향 프록시 등을 통해 연결이 TLS로 안전하게 보호되는지 확인하세요.

타사 도구

Proxmox 자동 설치와 관련된 다음과 같은 타사 도구가 유용할 수 있습니다:

https://github.com/natankeddem/autopve 웹 기반 GUI가 있는 응답 파일 서버

문제 해결

어떤 이유로 설치가 실패하면 셸로 떨어집니다(응답 파일의 reboot_on_error 옵션이 true로 설정되어 있지 않은 경우). 이를 통해 무엇이 잘못되었는지 문제를 해결할 수 있습니다.

관심 있는 로그 파일은 다음과 같습니다:

/tmp/fetch_answer.log – 응답 파일을 검색하는 단계
/tmp/auto_installer – 응답 파일 파싱, 사용할 하드웨어 매칭
/tmp/install-low-level-start-session.log – 실제 설치 프로세스

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like