인프라/가상화

Proxmox VE Cloud-Init VM 템플릿 만드는법: 계정·SSH 키·보안 설정 자동화

Proxmox VE에서 VM을 반복해서 만들 때 시간이 가장 많이 드는 부분은 VM 껍데기를 생성하는 작업이 아닙니다. 첫 로그인 계정을 만들고, SSH 키를 넣고, 자동화 계정을 분리하고, 보안 설정을 다시 확인하는 과정이 계속 남기 때문입니다. 이 글은 Ubuntu Cloud Image를 Proxmox 템플릿으로 준비하고, Cloud-Init 설정을 한 파일에서 관리해 새 VM이 첫 부팅부터 접속 가능한 상태가 되도록 만드는 과정을 정리합니다.

완성할 템플릿의 기준

이 글에서 말하는 템플릿 완성은 단순히 운영체제가 부팅되는 상태가 아닙니다. 템플릿을 복제한 뒤 사람이 콘솔에 들어가 계정을 다시 만들지 않아도 되는 상태를 목표로 합니다.

  • 사람이 접속할 일반 계정과 자동화용 ansible 계정이 분리되어 있음
  • 두 계정 모두 비밀번호가 아닌 SSH 공개키로 접속함
  • ansible 계정은 자동화에 필요한 sudo 권한을 가짐
  • SSH 비밀번호 로그인과 root SSH 로그인이 차단되어 있음
  • Cloud-Init과 QEMU Guest Agent가 첫 부팅에서 정상 완료되는지 확인할 수 있음
템플릿 준비, 복제, 첫 부팅 단계에서 사용자 계정과 자동화 계정이 적용되는 흐름
공통 계정과 보안 설정은 템플릿에서 관리하고, VM별 이름과 IP는 복제 단계에서 지정합니다.

계정과 설정을 어떻게 나눌까

Proxmox의 ciusersshkeys는 기본 사용자 한 명을 빠르게 설정할 때 편리합니다. 그러나 일반 사용자와 자동화 계정처럼 여러 계정을 함께 관리하려면 Proxmox가 생성한 user-data와 별도 vendor-data의 병합 결과에 의존하게 됩니다. Cloud-Init의 리스트는 항상 자동으로 합쳐지는 것이 아니므로, users가 서로 덮어쓰이지 않는지 이미지 버전마다 확인해야 합니다.

여기서는 계정과 보안 설정을 하나의 사용자 정의 user-data.yaml에 모으고, Proxmox에는 cicustom user=...로 연결합니다. 이렇게 하면 계정 정의의 원본이 한 파일로 고정되어 병합 순서를 추측할 필요가 없습니다. 대신 사용자 정의 user-data를 쓰는 동안에는 Proxmox 화면의 ciuser·SSH Key 값이 이 파일을 대신하지 않는다는 점을 기억해야 합니다.

설정 관리 위치 이유
일반 사용자·ansible 계정·SSH 키 user-data.yaml 여러 계정을 한 파일에서 일관되게 관리
SSH 비밀번호/root 로그인 차단 user-data.yaml 모든 클론에 같은 보안 기준 적용
VM 이름·IP·게이트웨이 클론 후 qm set VM마다 달라지는 값이므로 복제 단계에서 지정
CPU·메모리·디스크·브리지 Proxmox 템플릿 공통 하드웨어 기준을 상속

Cloud Image로 기본 VM 만들기

아래는 Ubuntu Cloud Image를 사용하는 예시입니다. 스토리지 ID, 브리지 이름, 이미지 경로는 자신의 환경에 맞게 바꿔야 합니다. 명령을 실행하기 전에 pvesm statusip link show로 실제 이름을 확인합니다.

VMID=9000
STORAGE=local-lvm
BRIDGE=vmbr0
IMAGE=jammy-server-cloudimg-amd64.img

qm create "$VMID" 
  --name ubuntu-cloud-template 
  --memory 2048 
  --cores 2 
  --net0 "virtio,bridge=$BRIDGE" 
  --scsihw virtio-scsi-pci

qm importdisk "$VMID" "$IMAGE" "$STORAGE"
qm set "$VMID" --scsi0 "$STORAGE:vm-$VMID-disk-0"
qm set "$VMID" --ide2 "$STORAGE:cloudinit"
qm set "$VMID" --boot order=scsi0
qm set "$VMID" --serial0 socket --vga serial0
qm set "$VMID" --agent enabled=1,fstrim_cloned_disks=1

qm importdisk 뒤에 표시되는 실제 볼륨 이름이 예시와 다를 수 있습니다. 바로 다음 명령을 복사하기보다 qm config 9000과 작업 로그에서 생성된 볼륨 이름을 확인한 뒤 scsi0에 연결하는 편이 안전합니다.

Cloud-Init 설정 파일 작성

먼저 스니펫을 저장할 스토리지에 snippets 콘텐츠 타입이 활성화되어 있는지 확인합니다.

pvesm config local
ls -ld /var/lib/vz/snippets

활성화가 필요하다면 Proxmox 화면의 Datacenter → Storage → local → Edit → Content에서 Snippets를 추가합니다. CLI로 변경할 때는 기존 content 목록을 없애지 말고 snippets만 추가해야 합니다.

다음 파일을 /var/lib/vz/snippets/user-data.yaml에 저장합니다. 공개키는 실제 공개키로 바꾸고 개인키는 절대 넣지 않습니다.

#cloud-config
users:
  - default
  - name: homelab-user
    groups: [sudo]
    shell: /bin/bash
    lock_passwd: true
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... 일반 사용자 공개키
  - name: ansible
    groups: [sudo]
    shell: /bin/bash
    lock_passwd: true
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... 자동화 전용 공개키

ssh_pwauth: false
disable_root: true

packages:
  - qemu-guest-agent

runcmd:
  - [systemctl, enable, --now, qemu-guest-agent]

chpasswd.list 방식은 최신 Cloud-Init에서 더 이상 권장되지 않습니다. 콘솔 복구용 root 비밀번호가 꼭 필요하다면 평문이 아니라 해시를 사용하고, chpasswd.users 형식으로 별도 관리해야 합니다. 다만 일반적인 운영에서는 sudo 가능한 관리 계정과 Proxmox 콘솔을 먼저 복구 경로로 설계하는 편이 단순합니다.

Proxmox 템플릿에 적용

qm set 9000 --cicustom "user=local:snippets/user-data.yaml"
qm config 9000

VM별 네트워크는 아직 넣지 않습니다. 실제 VM을 복제할 때 qm set <VMID> --ipconfig0 ...으로 지정하면 같은 템플릿을 DHCP와 고정 IP 환경에서 모두 재사용할 수 있습니다.

템플릿 변환 전 검증

사용자 정의 설정 파일이 연결됐다는 사실과 설정이 실제로 실행된다는 사실은 다릅니다. 템플릿으로 바꾸기 전에 테스트 VM 상태로 한 번 부팅해 확인합니다.

# Proxmox가 VM에 제공할 user-data 확인
qm cloudinit dump 9000 user
...
hostname: ubuntu-2404-cloudinit
manage_etc_hosts: true
fqdn: ubuntu-2404-cloudinit
user: vmuser
ssh_authorized_keys:
- ssh-ed25519 ...
chpasswd:
expire: False
users:
- default
package_upgrade: true

# 테스트 부팅
qm start 9000

# Guest Agent 응답 확인
qm agent 9000 ping

게스트에 접속한 뒤 다음 항목을 확인합니다.

cloud-init status --wait
getent passwd homelab-user ansible
sudo -n true
systemctl is-active qemu-guest-agent
sshd -T | grep -E 'passwordauthentication|permitrootlogin'

모든 확인이 끝났다면 VM을 정상 종료하고 템플릿으로 변환합니다.

qm shutdown 9000 --timeout 120
qm template 9000

보안상 꼭 확인할 부분

  • 공개키와 개인키를 구분합니다. Cloud-Init에는 공개키만 넣습니다.
  • ssh_pwauth: false를 유지합니다. root뿐 아니라 일반 계정의 SSH 비밀번호 공격 표면도 줄입니다.
  • 자동화 계정의 키를 사람 계정과 분리합니다. 키 교체와 접근 기록을 계정별로 관리할 수 있습니다.
  • 스니펫 파일 권한을 확인합니다. 비밀번호 해시나 내부 계정 정보가 있다면 불필요한 사용자에게 읽기 권한을 주지 않습니다.
  • 이미지 업데이트 전략을 정합니다. 매 VM 첫 부팅에서 전체 업그레이드를 실행하면 준비 시간이 길어지므로, 템플릿 이미지를 주기적으로 새로 만드는 방식과 비교해 선택합니다.

정리

재사용 가능한 Proxmox 템플릿의 핵심은 VM을 빨리 복사하는 것이 아니라 복사 후 반복 작업을 없애는 것입니다. 계정·SSH 키·보안 설정을 하나의 Cloud-Init 파일에서 관리하고, 템플릿 변환 전에 실제 부팅과 로그인까지 검증하면 이후 VM은 이름과 IP만 지정해 배포할 수 있습니다.

템플릿에 Guest Agent를 넣은 이유와 동작 범위는 QEMU Guest Agent 설치와 점검에서, 이 템플릿을 실제 VM으로 배포하는 전체 과정은 VM 프로비저닝 자동화에서 이어서 확인할 수 있습니다.