Proxmox

이 문서는 tirosh-home site에서 Proxmox node를 준비하고, Ubuntu cloud image 기반 VM template을 만든 뒤, OpenTofu로 site VM을 관리하는 흐름을 설명합니다.

1. 전체 흐름

1-1. 구성 단계

Proxmox 작업은 크게 세 단계로 나뉩니다.

Proxmox node 준비
  |
  v
Ubuntu cloud image template 생성
  |
  v
site VM catalog 적용

1-2. 기본 실행 순서

처음 구성할 때는 아래 순서로 진행합니다.

make site/inventory/render SITE=tirosh-home
make proxmox/bootstrap SITE=tirosh-home ASK_PASS=true
make proxmox/template/apply SITE=tirosh-home ASK_PASS=true

make proxmox/vms/plan SITE=tirosh-home
make proxmox/vms/apply SITE=tirosh-home

1-3. 도구별 책임

proxmox/bootstrapproxmox/template/apply는 Ansible로 Proxmox node에 SSH 접속합니다. proxmox/vms/*는 OpenTofu로 Proxmox API를 호출합니다.

2. site 설정과 inventory

Proxmox 작업에는 두 종류의 대상이 함께 등장합니다.

  • Ansible이 SSH로 접속할 Proxmox node
  • OpenTofu가 Proxmox API로 생성하고 관리할 VM

그래서 Proxmox 관련 설정은 한 파일에 몰아두지 않습니다. profile.toml은 Make command가 사용할 실행 기본값을 정하고, inventory.toml은 사람이 관리하는 host catalog가 됩니다. 실제 template 설정과 VM catalog는 각각 proxmox/ 하위 파일과 vms.tfvars에 둡니다.

sites/tirosh-home/
  profile.toml                         Make command 실행 기본값
  inventory.toml                       사람이 편집하는 host catalog
  inventory.yml                        Ansible이 읽는 렌더링 결과
  vms.tfvars                           OpenTofu가 읽는 VM catalog
  proxmox/
    bootstrap.vars.yml                 Ansible bootstrap 변수
    ubuntu-cloud-template.vars.yml     Ansible template 생성 변수
    vars.tfvars                        OpenTofu Proxmox 공통 변수
    vms.secrets.tfvars                 OpenTofu secret 변수

각 파일의 책임을 더 풀면 아래와 같습니다.

파일 누가 읽나 역할
profile.toml Make SITE=tirosh-home일 때 어떤 target, vars file, tfvars file을 기본으로 쓸지 정합니다.
inventory.toml site/inventory/render 사람이 편집하는 source입니다. Proxmox node, GPU host, VM host의 SSH 정보를 둡니다.
inventory.yml Ansible 렌더링 결과입니다. playbook 실행 대상 group과 host alias를 제공합니다.
proxmox/bootstrap.vars.yml Ansible Proxmox node repository, package, 필수 command를 준비합니다.
proxmox/ubuntu-cloud-template.vars.yml Ansible Ubuntu cloud template 생성에 필요한 storage, bridge, template ID를 정합니다.
proxmox/vars.tfvars OpenTofu Proxmox API endpoint, node, 기본 datastore, network 같은 VM 생성 공통값을 둡니다.
vms.tfvars OpenTofu site에서 만들 VM 목록과 VM별 CPU, memory, disk, IP, user를 둡니다.
proxmox/vms.secrets.tfvars OpenTofu Proxmox API token이나 VM password 같은 secret을 둡니다.

2-1. profile 기본값

profile.toml은 인프라 상태를 직접 선언하는 파일이라기보다, Make command가 각 도구를 호출할 때 사용할 기본 인자를 정하는 파일입니다.

예를 들어 사람은 아래처럼 site만 지정합니다.

make proxmox/template/apply SITE=tirosh-home

그러면 Make는 sites/tirosh-home/profile.toml을 읽어서 다음 값들을 채웁니다.

  • 어느 Proxmox node에 SSH로 붙을지
  • 어떤 Ansible vars file을 넘길지
  • OpenTofu가 어떤 tfvars file을 읽을지
  • secret tfvars file의 기본 경로가 어디인지

profile.toml은 “이 site에서 Proxmox 작업을 실행할 때의 기본 실행 context”입니다.

[proxmox]
target_name = "pve1"
vars = "sites/tirosh-home/proxmox/ubuntu-cloud-template.vars.yml"
bootstrap_vars = "sites/tirosh-home/proxmox/bootstrap.vars.yml"
vm_vars = "sites/tirosh-home/proxmox/vars.tfvars"
vm_catalog = "sites/tirosh-home/vms.tfvars"
vm_secrets = "sites/tirosh-home/proxmox/vms.secrets.tfvars"

각 값은 아래처럼 사용됩니다.

key 기본 의미 사용 예
target_name Ansible이 접속할 기본 Proxmox host alias make proxmox/bootstrapTARGET_NAME
vars Ubuntu cloud template 생성 변수 make proxmox/template/apply
bootstrap_vars Proxmox node bootstrap 변수 make proxmox/bootstrap
vm_vars OpenTofu Proxmox 공통 변수 make proxmox/vms/plan
vm_catalog OpenTofu VM catalog make proxmox/vms/plan
vm_secrets OpenTofu secret 변수 make proxmox/vms/plan

아래 명령을 실행하면 Make는 profile.tomlproxmox.target_name, proxmox.bootstrap_vars, inventory.yml을 사용해 pve1에 접속합니다.

make proxmox/bootstrap SITE=tirosh-home

다른 Proxmox node를 지정해야 하면 실행 시 override합니다. 이 override는 일회성이고, profile.toml을 바꾸지는 않습니다.

make proxmox/bootstrap SITE=tirosh-home TARGET_NAME=pve2

반대로 어떤 site에서 항상 pve2를 기본 Proxmox node로 쓰고 싶다면 profile.tomlproxmox.target_name을 바꿉니다.

2-2. inventory 등록

Proxmox node는 Ansible이 SSH로 접속해야 하는 물리 host입니다. 그래서 inventory.tomlbaremetals에 등록합니다.

[baremetals.pve1]
ansible_host = "192.168.219.250"
ansible_user = "root"
ansible_become_user = "root"
workloads = ["proxmox"]

여기서 pve1은 사람이 부르는 host alias입니다. Make의 TARGET_NAME=pve1과 Ansible의 --limit pve1이 이 이름을 사용합니다.

각 필드의 의미는 아래와 같습니다.

key 의미
ansible_host 실제 SSH 접속 주소
ansible_user SSH 로그인 계정
ansible_become_user 권한 상승 후 작업을 실행할 계정
workloads 이 host가 어떤 Ansible group에 들어갈지 정하는 source

workloads = ["proxmox"]는 해당 host를 Ansible proxmox group에 넣기 위한 선언입니다. 사람이 직접 inventory.yml의 group hierarchy를 편집하지 않고, source인 inventory.toml을 수정한 뒤 렌더링합니다.

make site/inventory/render SITE=tirosh-home

렌더링 후 inventory.yml에는 아래 group이 생겨야 합니다.

proxmox:
  hosts:
    pve1: {}

정리하면 inventory.toml은 사람이 편집하는 입력이고, inventory.yml은 Ansible이 읽는 출력입니다. Proxmox node IP나 SSH 계정이 바뀌면 inventory.toml을 수정한 뒤 반드시 다시 렌더링합니다.

3. Proxmox node bootstrap

bootstrap은 Proxmox node 자체의 repository와 host package를 준비합니다.

make proxmox/bootstrap SITE=tirosh-home ASK_PASS=true

3-1. bootstrap 설정

설정은 sites/<site_id>/proxmox/bootstrap.vars.yml에서 관리합니다.

proxmox_disable_enterprise_repository: true
proxmox_enable_no_subscription_repository: true

proxmox_manage_ceph_repository: true
proxmox_ceph_release: squid
proxmox_disable_ceph_enterprise_repository: true
proxmox_enable_ceph_no_subscription_repository: false

proxmox_bootstrap_packages:
  - libguestfs-tools
proxmox_bootstrap_required_commands:
  - qm
  - sha256sum
  - awk
  - virt-customize

기본 bootstrap은 아래 작업을 수행합니다.

  • Proxmox enterprise repository 비활성화
  • Proxmox no-subscription repository 활성화
  • Ceph enterprise repository 비활성화
  • template 생성에 필요한 package 설치
  • qm, virt-customize 같은 필수 command 확인

Proxmox subscription을 사용하는 운영 환경에서는 enterprise repository를 끄지 않도록 설정합니다.

proxmox_disable_enterprise_repository: false
proxmox_enable_no_subscription_repository: false

3-2. 로컬 실행 환경

bootstrap과 template 생성은 Ansible이 필요합니다.

brew install ansible

SSH password를 사용할 경우 로컬에 sshpass도 필요합니다.

# macOS
brew install hudochenkov/sshpass/sshpass

# Debian/Ubuntu
sudo apt install sshpass

SSH key를 사용한다면 inventory.toml 또는 렌더링된 inventory.ymlansible_ssh_private_key_file을 넣어 password prompt 없이 실행할 수 있습니다.

4. Ubuntu cloud template

Ubuntu cloud image 기반 template은 VM clone의 기준점입니다. OpenTofu가 VM을 만들 때는 매번 OS image를 새로 설치하지 않고, Proxmox에 미리 만들어 둔 template을 clone합니다.

Ubuntu cloud image
  |
  v
image customize
  qemu-guest-agent, SSH 설정, clone state 정리
  |
  v
Proxmox VM 생성
  disk import, cloud-init drive, guest agent option
  |
  v
VM template 변환
  |
  v
OpenTofu VM clone

이 단계의 목표는 “clone해도 안전한 VM 원본”을 만드는 것입니다. template 안에는 VM별로 달라져야 하는 값, 예를 들어 machine-id, SSH host key, cloud-init state, IP 주소, user password를 고정으로 남기지 않습니다. VM별 값은 clone 시점에 cloud-init으로 주입합니다.

make proxmox/template/apply SITE=tirosh-home ASK_PASS=true

4-1. template 변수

설정은 sites/<site_id>/proxmox/ubuntu-cloud-template.vars.yml에서 관리합니다.

이 파일은 “어떤 Ubuntu image를 가져와서, Proxmox의 어느 storage와 bridge를 사용해, 어떤 VM ID/name으로 template을 만들지”를 정합니다.

proxmox_storage: local-lvm
proxmox_cloudinit_storage: local-lvm
proxmox_bridge: vmbr0

template_id: 9001
template_name: ubuntu-cloud
template_memory: 2048
template_cores: 2

자주 보는 값은 아래처럼 이해하면 됩니다.

key 의미
proxmox_storage imported disk가 들어갈 Proxmox storage
proxmox_cloudinit_storage cloud-init drive가 들어갈 Proxmox storage
proxmox_bridge template VM의 기본 network bridge
template_id Proxmox template VM ID
template_name Proxmox에 표시될 template 이름
template_memory template VM 기본 memory
template_cores template VM 기본 CPU core 수

여기서 설정한 값은 template 자체의 기본값입니다. 실제 clone VM의 CPU, memory, disk, IP, bridge는 vms.tfvars에서 VM별로 다시 지정할 수 있습니다.

대상 Proxmox node에는 아래 command가 필요합니다. bootstrap을 먼저 실행했다면 보통 준비되어 있습니다.

  • qm
  • sha256sum
  • awk
  • virt-customize

virt-customize는 cloud image 안에 package를 넣거나 파일을 수정하는 데 사용합니다. Debian/Ubuntu 기반 Proxmox node에서는 보통 libguestfs-tools 패키지에 포함됩니다.

4-2. template 생성 작업

infra/ansible/playbooks/proxmox/ubuntu-cloud-template.yml은 아래 작업을 수행합니다.

  • Ubuntu cloud image 다운로드
  • image checksum 검증
  • qemu-guest-agent 설치
  • clone별로 달라져야 하는 image state 정리
  • SSH password authentication 활성화
  • Proxmox VM 생성
  • disk import
  • cloud-init 기본값 설정
  • guest agent와 disk 기본 option 설정
  • VM template 변환

실행 결과는 Proxmox UI에서 template_name으로 보이는 VM template입니다. 이후 OpenTofu VM workflow는 이 template ID를 참조해서 clone VM을 만듭니다.

template 생성은 idempotent하게 동작하도록 설계되어 있습니다. 이미 custom image cache가 있으면 기본적으로 다시 커스터마이징하지 않습니다. template 설정을 바꿨고 기존 cache를 버리고 싶으면 site vars에서 켭니다.

template_rebuild_custom_image: true

기존 Proxmox template 자체를 자동 삭제하거나 교체하지는 않습니다. 운영 중인 VM이 어떤 template에서 만들어졌는지 추적하기 쉽도록, template 교체가 필요하면 기존 template을 확인한 뒤 수동으로 삭제하거나 새 template_id/template_name을 사용합니다.

4-3. image 정리 기준

template 생성 전에 clone별로 달라져야 하는 값을 정리합니다.

  • /etc/machine-id
  • /var/lib/dbus/machine-id
  • SSH host keys
  • cloud-init state and logs
  • persistent udev network rule
  • apt cache and package lists
  • temporary files

machine-id는 비워둔 상태로 template에 넣습니다. clone된 VM의 첫 부팅 시 systemd/cloud-init이 새 값을 생성합니다.

DHCP client-id/DUID가 machine-id 기반이면 clone 간 IP 충돌이 날 수 있습니다. template은 systemd-networkd DUID를 NIC MAC 기반으로 설정합니다.

template_clean_machine_id: true
template_configure_networkd_dhcp_duid: true
template_networkd_dhcp_duid_type: link-layer

SSH host keys도 삭제합니다. clone된 VM은 첫 부팅 과정에서 새 host key를 생성합니다.

이 정리 작업을 하지 않으면 여러 clone VM이 같은 machine-id, 같은 SSH host key, 같은 cloud-init state를 공유할 수 있습니다. 그런 상태는 DHCP lease 충돌, SSH host key 경고, cloud-init 재실행 실패처럼 나중에 보기 어려운 문제로 이어집니다.

특별한 이유로 image 정리를 끄고 싶다면 site vars에서 설정할 수 있습니다.

template_clean_image: false

4-4. disk와 boot option

현재 기본값은 cloud image template에 무난한 값만 켭니다.

  • template_agent_options: enabled=1,fstrim_cloned_disks=1
  • template_scsi_controller: virtio-scsi-pci
  • template_disk_discard: "on"
  • template_disk_ssd: true
  • template_disk_iothread: false
  • template_disk_cache: ""

discard=on은 thin provisioning storage에서 guest의 TRIM 정보를 storage로 전달하는 데 도움이 됩니다.

ssd=1은 guest에 disk를 SSD처럼 보이게 합니다. 현장 storage가 HDD 중심이거나 이 옵션을 원하지 않으면 site vars에서 template_disk_ssd: false로 바꿉니다.

iothread=1은 기본으로 켜지 않습니다. Proxmox에서 SCSI disk에 IO thread를 쓰려면 template_scsi_controller: virtio-scsi-single 조합이 필요하므로, 명확히 필요할 때만 site vars에서 둘 다 켭니다.

정리하면 기본값은 “얇게 provision되는 local-lvm 같은 storage에서 무난하고 예측 가능한 template”에 맞춰져 있습니다. 성능 튜닝은 storage 종류와 workload가 명확해진 뒤 VM별 또는 site별로 조정합니다.

4-5. network와 SSH password

network는 Ubuntu cloud image의 기본 netplan 설정을 유지합니다. 50-cloud-init.yaml을 지우면 환경에 따라 DHCP가 늦거나 실패할 수 있습니다.

template 단계에서는 고정 IP를 박아 넣지 않습니다. template은 여러 VM의 원본이므로 특정 IP를 넣으면 clone들이 같은 주소를 가지게 됩니다. VM별 고정 IP는 vms.tfvars에서 cloud-init network 값으로 지정합니다.

systemd-networkd-wait-online.service는 기본으로 비활성화합니다. 이 서비스는 DHCP나 network-online 판정이 늦을 때 부팅을 오래 붙잡을 수 있습니다. 네트워크 자체를 끄는 설정은 아니며, cloud-init/networkd의 DHCP 설정은 그대로 유지합니다.

template_disable_network_wait_online: true

사내망 운영 편의를 위해 기본 template은 SSH password authentication을 켭니다.

template_enable_ssh_password_auth: true

이 설정은 guest의 cloud-init과 SSH daemon이 password authentication을 받을 수 있게 하는 설정입니다. 실제 password는 template에 저장하지 않고, VM clone 시 cloud-init 계정에 주입합니다.

즉 password 관련 책임은 아래처럼 나뉩니다.

template
  SSH password authentication 허용

VM clone
  cloud-init user/password 주입

OpenTofu state
  password를 넣는 경우 민감정보 접근 권한 관리 필요

password auth를 끄려면 site vars에서 설정합니다.

template_enable_ssh_password_auth: false

clone VM에서 적용 여부를 확인하려면 VM console에서 실행합니다.

sudo sshd -T | grep -E 'passwordauthentication|kbdinteractiveauthentication'
sudo passwd -S <cloud-init-user>
cloud-init status --wait

기대값은 passwordauthentication yes이고, cloud-init user의 password 상태가 locked가 아니어야 합니다.

4-6. template 확인과 재실행

template이 만들어졌는지 Proxmox node에서 확인할 수 있습니다.

qm config <template-id>

template을 다시 만들어야 할 때는 먼저 기존 template이 사용 중인지 확인합니다. 삭제해도 되는 template임을 확인한 뒤 Proxmox에서 수동 cleanup을 수행하고, 같은 site 설정으로 template 생성 playbook을 다시 실행합니다.

make proxmox/template/apply SITE=tirosh-home

template 생성이 끝난 뒤 바로 VM을 만들려면 다음 단계의 OpenTofu workflow로 넘어갑니다.

5. VM catalog와 OpenTofu

VM 생성은 sites/<site_id>/vms.tfvars의 catalog를 기준으로 합니다. Proxmox API endpoint, node, 기본 datastore 같은 공통 변수는 sites/<site_id>/proxmox/vars.tfvars에 둡니다.

sites/tirosh-home/
  vms.tfvars
  proxmox/
    vars.tfvars
    vms.secrets.tfvars

5-1. VM 선언

예시는 Argo CD management cluster용 VM입니다.

vms = {
  argocd = {
    name      = "argocd"
    vm_id     = 1001
    tags      = ["ubuntu", "k3s", "argocd"]
    cpu_cores = 4
    memory_mb = 4096

    disk = {
      datastore_id = "local-lvm"
      size_gb      = 40
    }

    network = {
      bridge = "vmbr1"
    }

    ipv4 = {
      address = "172.31.0.10/24"
      gateway = "172.31.0.1"
    }

    dns_servers = [
      "172.31.0.1",
      "1.1.1.1",
    ]

    user_account = {
      username = "tirosh"
    }
  }
}

VM별 고정 IP는 cloud-init network 설정으로 들어갑니다. VM이 만들어진 뒤 Ansible이 이 VM에 접속해야 한다면 inventory.tomlansible_host도 같은 IP로 맞춥니다.

5-2. plan과 apply

처음 실행하거나 provider lock/workspace가 바뀐 경우 init을 실행합니다.

make proxmox/vms/init SITE=tirosh-home

변경 내용을 확인합니다.

make proxmox/vms/plan SITE=tirosh-home

문제가 없으면 적용합니다.

make proxmox/vms/apply SITE=tirosh-home

5-3. secret과 state

Proxmox API token이나 VM password는 vms.tfvars에 직접 넣지 않습니다. 기본 경로는 아래 파일입니다.

sites/tirosh-home/proxmox/vms.secrets.tfvars

예시는 아래 파일에서 확인합니다.

sites/tirosh-home/proxmox/vms.secrets.tfvars.example

OpenTofu state는 현재 local backend를 사용합니다. state에는 provider가 관리하는 민감한 값이 남을 수 있으므로 git에 커밋하지 않고, 접근 권한을 따로 관리합니다.

6. 문제 확인

작업이 실패하면 어떤 계층에서 실패했는지 먼저 분리합니다.

6-1. Ansible 접속 확인

bootstrap/template 단계가 실패하면 먼저 Proxmox node SSH 접속과 inventory target을 확인합니다.

make proxmox/bootstrap SITE=tirosh-home TARGET_NAME=pve1 ASK_PASS=true

TARGET_NAMEinventory.yml의 host alias와 매칭됩니다. source는 inventory.toml이므로 host를 추가하거나 IP를 바꾼 뒤에는 inventory를 다시 렌더링합니다.

make site/inventory/render SITE=tirosh-home

6-2. template 교체

custom image cache가 이미 있으면 playbook은 기본적으로 다시 커스터마이징하지 않습니다. template 설정을 바꿨고 기존 cache를 버리고 싶으면 site vars에서 켭니다.

template_rebuild_custom_image: true

기존 Proxmox template 자체의 갱신은 자동으로 처리하지 않습니다. 운영 중인 VM이 어떤 template에서 만들어졌는지 추적하기 쉽도록, template 교체가 필요하면 Proxmox에서 기존 template을 확인한 뒤 수동으로 삭제하거나 새 template_id/template_name을 사용합니다.

qm config <template-id>

삭제해도 되는 template임을 확인한 뒤 template 생성 playbook을 다시 실행합니다.

make proxmox/template/apply SITE=tirosh-home

6-3. VM 생성 실패

VM plan/apply가 실패하면 Proxmox API credential, template ID, datastore, bridge, VM ID 충돌을 확인합니다.

make proxmox/vms/plan SITE=tirosh-home

VM 생성 후 SSH 접속이 안 되면 아래 순서로 확인합니다.

  • vms.tfvarsipv4.address
  • Proxmox VM Cloud-Init 탭
  • VM console의 ip addr
  • inventory.tomlansible_host
  • make site/inventory/render SITE=tirosh-home 실행 여부

7. 다음 단계

7-1. 후속 workflow

VM이 준비되면 목적에 맞는 상위 workflow를 진행합니다.