마루옵스서버 · 네트워크 · AI 기술 노트

소개문의

Rocky·Ubuntu 클라우드 이미지 AHV 배포: QCOW2 비밀번호 변경과 cloud-init

노트북으로 서버 랙을 점검하는 NERSC 기술자

참고 사진 · 노트북으로 NERSC 서버 랙을 점검하는 기술자 · 사진: Derrick Coetzee / 원본 / CC0 1.0 · 목록용 크기 조정

Rocky Linux와 Ubuntu 클라우드 이미지를 쓰면 설치 ISO로 운영체제를 설치하는 과정을 줄일 수 있습니다. 공식 디스크 이미지를 받아 Nutanix AHV에 등록하고, VM 첫 부팅에 cloud-init으로 계정과 SSH 키를 전달하면 됩니다. Linux 작업 PC에서 QCOW2 파일을 열어 비밀번호나 설정을 미리 바꾸는 방법도 있어요.

이 글은 x86_64 Rocky Linux 9 GenericCloud와 Ubuntu 24.04 LTS를 예로 듭니다. 공식 이미지 경로와 문서는 2026년 10월 24일 기준으로 확인했으며, 아래 절차는 실제 Nutanix 클러스터에서 배포를 재현한 기록이 아닌 구성 예제입니다. AOS·AHV·Prism 버전과 게스트 OS 지원 여부는 대상 환경에서 따로 확인합니다.

이 글의 목차

배포 전에 정할 것

구분이 글의 기준
대상 하이퍼바이저Nutanix AHV. Nutanix 장비에서 ESXi를 사용하는 경우와 구분
작업 PCUbuntu 24.04 또는 Rocky Linux 9, 이미지 저장 공간과 sudo 권한
첫 네트워크DHCP 또는 Nutanix IPAM으로 주소를 받을 수 있는 VLAN
예시 VM 자원2 vCPU·메모리 4 GiB·디스크 40 GiB. 제품 최소 사양이 아닌 실습 예시
초기 설정VM별 계정·SSH 키는 cloud-init, 공통 파일·복구용 비밀번호는 오프라인 수정

cloud-init은 첫 부팅에 사용자·호스트 이름·패키지 등을 설정하는 도구입니다. VM마다 다른 값을 전달하기 편하므로 반복 배포에는 이 방식을 먼저 선택합니다. 오프라인 수정은 VM을 부팅하지 않고 이미지 내부 파일을 바꾸는 작업이에요. 두 방식을 함께 쓰면 같은 항목을 서로 다른 값으로 설정하지 않도록 조정합니다.

1. 공식 클라우드 이미지 다운로드

Rocky Linux 9 GenericCloud

Rocky Linux 공식 이미지 디렉터리에서 GenericCloud-Base의 x86_64.qcow2를 선택합니다. 설치용 ISO, EC2용 이미지, Azure용 VHD와 구분하세요. latest의 내용은 갱신되므로 다운로드한 원본과 체크섬 파일을 함께 보관합니다.

mkdir -p ~/ahv-cloud-images/rocky9 ~/ahv-cloud-images/ubuntu2404
cd ~/ahv-cloud-images/rocky9

rocky_base='https://dl.rockylinux.org/pub/rocky/9/images/x86_64'
rocky_image='Rocky-9-GenericCloud-Base.latest.x86_64.qcow2'
curl -fLO "$rocky_base/$rocky_image"
curl -fLO "$rocky_base/$rocky_image.CHECKSUM"

sha256sum "$rocky_image"
cat "$rocky_image.CHECKSUM"

계산한 SHA-256 값이 체크섬 파일의 SHA256 (...) = ... 값과 같을 때만 진행합니다. Rocky 체크섬은 이 예제에서 BSD 형식으로 제공되므로 Ubuntu 예제의 sha256sum -c 명령을 그대로 적용하지 않습니다.

Ubuntu 24.04 LTS

Ubuntu 공식 릴리스 이미지에서 ubuntu-24.04-server-cloudimg-amd64.img를 받습니다. amd64는 Intel·AMD x86_64용 아키텍처 이름입니다. 이 Ubuntu 클라우드 이미지의 .img는 QCOW2 형식이며, 확장자만으로 RAW 이미지라고 판단하지 않습니다. Canonical의 QCOW 이미지 안내에서도 이 구분을 확인할 수 있습니다.

cd ~/ahv-cloud-images/ubuntu2404

ubuntu_base='https://cloud-images.ubuntu.com/releases/noble/release'
ubuntu_image='ubuntu-24.04-server-cloudimg-amd64.img'
curl -fLO "$ubuntu_base/$ubuntu_image"
curl -fLO "$ubuntu_base/SHA256SUMS"

sha256sum --check --ignore-missing SHA256SUMS

대상 파일이 OK로 표시되고 명령이 성공했는지 확인합니다. 체크섬은 전송 중 파일이 손상됐는지 확인하는 수단입니다. 배포자의 서명까지 검증하는 환경에서는 공식 디렉터리의 서명 파일과 별도로 신뢰한 배포자 공개키를 사용합니다. 다운로드 도중 릴리스가 갱신됐다면 같은 빌드의 이미지와 체크섬을 다시 받습니다.

2. Linux에서 이미지 형식 확인과 작업용 복사

다음 명령은 이미지를 보관한 Linux 작업 PC에서 실행합니다. 배포할 게스트 안에 설치하는 도구가 아니에요. Ubuntu 작업 PC에서는 다음 패키지를 설치합니다.

sudo apt update
sudo apt install -y qemu-utils libguestfs-tools

Rocky Linux 9 작업 PC에서는 다음 패키지를 설치합니다. virt-customize와 virt-cat은 guestfs-tools에 포함됩니다.

sudo dnf install -y qemu-img guestfs-tools

qemu-img로 실제 파일 형식과 가상 디스크 용량을 확인합니다. 원본을 보관하고 독립적인 작업용 복사본을 만든 뒤, 이후 수정은 복사본에만 진행합니다.

cd ~/ahv-cloud-images

qemu-img info rocky9/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
qemu-img info ubuntu2404/ubuntu-24.04-server-cloudimg-amd64.img

cp --reflink=auto --sparse=always \
  rocky9/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 rocky9-ahv.qcow2
cp --reflink=auto --sparse=always \
  ubuntu2404/ubuntu-24.04-server-cloudimg-amd64.img ubuntu2404-ahv.qcow2

qemu-img check -f qcow2 rocky9-ahv.qcow2
qemu-img check -f qcow2 ubuntu2404-ahv.qcow2

file format: qcow2와 virtual size를 확인합니다. 파일의 실제 사용량과 가상 용량은 달라요. AHV 디스크를 40 GiB로 만들려면 원본의 가상 용량이 40 GiB 이하여야 합니다. backing file이 표시되면 다른 이미지에 의존하므로, 업로드할 파일이 단독으로 동작하는 상태인지 확인합니다.

실행 중인 VM이나 다른 도구가 사용 중인 디스크는 수정하면 안 됩니다. virt-customize는 이미지 파일 자체를 변경합니다. 아래 작업은 사용 중이지 않은 로컬 복사본을 대상으로 하며, AHV 호스트나 CVM에 이미지 편집 도구를 설치하지 않습니다. libguestfs의 동시 수정 주의사항도 확인합니다.

3. QCOW2 이미지의 비밀번호 직접 변경

root의 콘솔 접속용 비밀번호 설정

복구용 root 비밀번호를 먼저 설정하는 예제입니다. Bash에서 실행하며, Ubuntu를 대상으로 할 때는 rocky9-ahv.qcow2를 ubuntu2404-ahv.qcow2로 바꿉니다. 비밀번호를 명령행 인수에 직접 넣지 않고, 접근 권한을 제한한 임시 파일에서 읽습니다.

cd ~/ahv-cloud-images

(
  set -e
  umask 077
  image_password_file=$(mktemp)
  trap 'rm -f -- "$image_password_file"' EXIT

  read -r -s -p 'New root password: ' image_password
  printf '\n'
  test -n "$image_password"
  printf '%s\n' "$image_password" > "$image_password_file"
  unset image_password

  sudo virt-customize --format qcow2 -a rocky9-ahv.qcow2 \
    --root-password "file:$image_password_file" \
    --selinux-relabel
)

libguestfs의 비밀번호 지정 방식에서 file:은 파일의 첫 줄을 새 비밀번호로 읽습니다. Rocky에서는 변경 파일의 SELinux 레이블도 정리합니다. 임시 파일 삭제가 저장 매체에서의 완전 삭제를 보장하지는 않으므로, 작업 PC의 임시 저장 위치도 관리 기준에 맞춥니다.

root 비밀번호 설정과 root SSH 로그인 허용은 별개입니다. 이 명령은 PermitRootLogin이나 SSH 인증 정책을 풀지 않습니다. 설정한 값은 Prism 콘솔의 복구용 자격 증명으로 사용하고, 평소 SSH 접속에는 일반 사용자와 공개키를 사용합니다. 첫 부팅의 cloud-init에서 root 비밀번호 변경이나 잠금을 지정했다면 그 설정도 적용됩니다.

rocky·ubuntu 사용자 변경 전 확인

클라우드 이미지의 기본 사용자는 cloud-init이 실행될 때 처음 만들어질 수 있습니다. --password 옵션은 사용자를 생성하지 않으므로 먼저 이미지의 /etc/passwd를 확인합니다. 비밀번호 해시가 들어 있는 /etc/shadow를 로그에 출력할 필요는 없습니다.

sudo virt-cat --format=qcow2 -a rocky9-ahv.qcow2 /etc/passwd
sudo virt-cat --format=qcow2 -a rocky9-ahv.qcow2 /etc/cloud/cloud.cfg

사용자 이름은 /etc/passwd와 system_info.default_user를 확인해 결정합니다. 대상 계정이 이미 존재하면 앞의 예제에서 --root-password "file:$image_password_file"를 --password "rocky:file:$image_password_file"로 바꿀 수 있습니다. Ubuntu는 실제로 확인한 사용자 이름을 넣습니다. 계정이 없다면 cloud-init으로 만들거나 뒤의 전용 사용자 추가 예제를 사용합니다. 파일 확인 방법은 virt-cat 문서에 설명되어 있습니다.

4. cloud-init으로 SSH 접속 계정 만들기

일반적인 Nutanix 배포에서는 VM마다 다음 user-data를 전달하면 편해요. ops 사용자를 만들고 SSH 공개키를 등록하는 구성입니다. REPLACE_WITH_YOUR_FULL_SSH_PUBLIC_KEY는 접속 PC의 ~/.ssh/id_ed25519.pub처럼 공개키 파일에 있는 한 줄 전체로 바꿉니다. 개인키는 전달하지 않습니다.

#cloud-config
hostname: ahv-linux-01
manage_etc_hosts: true
disable_root: true
ssh_pwauth: false
users:
  - default
  - name: ops
    shell: /bin/bash
    sudo:
      - ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - REPLACE_WITH_YOUR_FULL_SSH_PUBLIC_KEY
timezone: Asia/Seoul

default는 이미지의 기본 사용자 생성을 유지하는 항목입니다. ops는 공개키로 접속하며, 이 예제에서는 비밀번호 없는 sudo 권한을 가집니다. 운영 정책에 맞게 sudo 권한을 줄이고 호스트 이름과 시간대는 VM별로 조정합니다. 사용자의 생성 방식은 cloud-init 사용자 설정 예제에서 확인할 수 있습니다.

콘솔 비밀번호도 함께 전달하는 경우

일반 사용자의 비밀번호도 필요하면 작업 PC에서 openssl passwd -6을 실행해 대화형 입력으로 SHA-512 crypt 해시를 만듭니다. 다음 예제는 앞의 user-data를 대신해서 사용합니다. REPLACE_WITH_SHA512_CRYPT_HASH에 출력된 $6$로 시작하는 값을 넣고 따옴표를 유지합니다.

#cloud-config
hostname: ahv-linux-01
manage_etc_hosts: true
disable_root: true
ssh_pwauth: false
users:
  - default
  - name: ops
    shell: /bin/bash
    sudo:
      - ALL=(ALL) ALL
    lock_passwd: false
    hashed_passwd: 'REPLACE_WITH_SHA512_CRYPT_HASH'
    ssh_authorized_keys:
      - REPLACE_WITH_YOUR_FULL_SSH_PUBLIC_KEY
timezone: Asia/Seoul

이 구성은 콘솔과 sudo에서 비밀번호를 사용하고 SSH는 키 인증으로 유지합니다. SSH 비밀번호 인증까지 필요하면 ssh_pwauth: true로 바꾸고, 최종 sshd 설정과 인증 정책도 확인합니다. hashed_passwd는 기존 사용자에게도 적용되므로 오프라인에서 설정한 비밀번호를 다시 바꿀 수 있습니다. 해시가 포함된 user-data도 보호합니다. 사용자·비밀번호 모듈 사양을 참고하세요.

수정한 YAML을 user-data.yaml로 저장하고 cloud-init이 설치된 Linux에서 구조를 검증합니다. 검증에 통과해도 공개키와 호스트 이름의 실제 값은 따로 확인해야 합니다.

cloud-init schema --config-file user-data.yaml

5. Nutanix AHV에 이미지 등록과 VM 생성

Images에 디스크 이미지로 등록

Prism Central의 Infrastructure에서 Compute → Images를 열고 이미지 생성을 시작합니다. Prism Element는 버전에 따라 Settings → Image Configuration에서 등록합니다. 사용 중인 버전의 표시명을 따르며, QCOW2는 ISO가 아닌 디스크 이미지로 등록합니다. Nutanix 이미지 관리 가이드는 QCOW2를 지원 형식으로 안내합니다.

  1. 이름은 rocky9-cloud-base처럼 지정하고 배포 원본과 빌드 정보를 함께 기록합니다.
  2. Type은 DISK에 해당하는 유형을 선택하고 수정한 rocky9-ahv.qcow2 또는 ubuntu2404-ahv.qcow2를 업로드합니다.
  3. URL로 가져올 때는 이미지 서비스가 접근할 수 있는 HTTP(S) 주소를 지정합니다. 작업 PC의 로컬 경로는 다운로드 주소로 사용할 수 없습니다.
  4. 대상 클러스터의 이미지 배치와 가져오기 작업이 끝난 뒤 VM을 만듭니다.

업로드 화면에서 Ubuntu 원본의 .img 확장자를 허용하지 않더라도, 이 글의 작업용 복사본은 .qcow2 확장자를 사용합니다. 내부 형식이 이미 QCOW2이므로 확장자 때문에 RAW 변환을 할 필요는 없습니다. 등록 후 로컬 파일을 수정해도 이미 가져온 이미지나 생성된 VM 디스크에는 반영되지 않습니다.

이미지에서 OS 디스크 복제

  1. 대상 클러스터에 VM을 만들고 CPU와 메모리를 지정합니다.
  2. OS 디스크는 Clone from Image 등 이미지 복제 방식으로 추가합니다. 빈 디스크만 추가해서는 운영체제가 부팅되지 않습니다.
  3. AHV에서 지원하는 VirtIO SCSI 디스크와 VirtIO NIC를 사용하고 OS 디스크를 부팅 대상으로 지정합니다. 디스크 용량은 이미지의 가상 용량 이상으로 설정합니다.
  4. NIC를 목적 VLAN·서브넷에 연결하고 DHCP 또는 IPAM 할당을 사용할 수 있는지 확인합니다.
  5. 이미지의 부트로더와 AHV 지원에 맞게 BIOS·UEFI를 선택합니다. UEFI 전용 이미지는 UEFI로 생성하며 Secure Boot 지원은 별도로 확인합니다.
  6. Guest Customization → Cloud-init → Custom Script에 해당하는 영역에 user-data를 붙여 넣거나 업로드합니다. 화면에는 일반 YAML을 입력하며 API용 Base64 값으로 바꾸지 않습니다.
  7. 설정을 저장한 뒤 처음으로 전원을 켭니다.

cloud-init 선택과 스크립트 입력 방식은 Prism Central Infrastructure Guide의 VM 생성 절차에 설명되어 있습니다. 메뉴 위치와 디스크 버스 선택지는 릴리스마다 달라질 수 있으므로 설치한 버전의 문서를 함께 확인합니다.

처음에는 DHCP로 접속하는 구성에 집중합니다. user-data 맨 위에 network:를 추가하는 것만으로 고정 IP가 설정되지는 않습니다. cloud-init의 네트워크 구성은 별도의 입력 경로를 사용합니다. 고정 IP가 필요하면 Prism의 관련 기능과 datasource로 전달하는 방식을 확인하거나, 첫 접속 후 게스트 OS에서 설정합니다. cloud-init 네트워크 설정 사양에서 입력 경로의 차이를 확인할 수 있습니다.

6. 첫 로그인과 동작 확인

Prism이나 DHCP 서버에서 할당된 IP를 확인하고 접속 PC에서 ops로 로그인합니다. 아래의 192.0.2.50은 문서용 주소이므로 실제 VM의 IP로 바꿉니다. SSH 호스트 키의 지문은 콘솔 등 별도 경로에서 확인합니다.

ssh -i ~/.ssh/id_ed25519 ops@192.0.2.50

다음 명령은 부팅한 게스트 안에서 실행합니다. cloud-init이 끝날 때까지 기다린 뒤 오류, 주소, 경로, 이름 해석과 디스크 용량을 확인합니다.

cloud-init status --wait
sudo cloud-init status --long
ip -br address
ip route
getent hosts download.rockylinux.org
lsblk -f
df -hT /
sudo tail -n 80 /var/log/cloud-init.log
sudo tail -n 80 /var/log/cloud-init-output.log

로그에 WARNING이나 오류가 있다면 내용을 확인한 뒤 진행합니다. 상태와 검증 명령은 cloud-init CLI 문서를 참고하세요. DHCP 주소를 받았더라도 DNS, 기본 경로와 외부 통신이 모두 정상이라는 뜻은 아닙니다.

AHV에서 디스크를 늘려도 파티션과 파일시스템이 자동으로 같은 크기가 되는 것은 아닙니다. lsblk와 df를 비교한 뒤 LVM 사용 여부와 XFS·ext4 구성에 맞게 확장합니다. 명령을 정해 놓고 실행하기보다 일반 파티션의 루트 확장과 LVM·XFS·ext4 루트 확장에서 현재 구조에 맞는 절차를 선택합니다.

7. 비밀번호 외의 오프라인 수정

SSH 공개키를 가진 전용 사용자 추가

cloud-init을 사용하기 어려운 배포 과정에서는 이미지에 전용 계정을 만들고 공개키를 넣을 수도 있습니다. 다음은 labadmin이 없으면 생성한 뒤 공개키와 sudo 설정을 추가하는 예제입니다. --ssh-inject보다 계정 생성이 먼저 실행되어야 합니다. 이 작업도 이미지가 사용 중이지 않은 상태에서 진행합니다.

cd ~/ahv-cloud-images
test -s "$HOME/.ssh/id_ed25519.pub"

sudo virt-customize --format qcow2 -a rocky9-ahv.qcow2 \
  --run-command 'id -u labadmin >/dev/null 2>&1 || useradd -m -s /bin/bash labadmin' \
  --ssh-inject "labadmin:file:$HOME/.ssh/id_ed25519.pub" \
  --write '/etc/sudoers.d/90-labadmin:labadmin ALL=(ALL) NOPASSWD:ALL' \
  --chmod '0440:/etc/sudoers.d/90-labadmin' \
  --run-command 'visudo -cf /etc/sudoers.d/90-labadmin' \
  --selinux-relabel

이 예제는 이미지에 sudo가 설치되어 있다고 가정합니다. 공개키 파일만 지정하며, 여러 사람이 사용할 이미지에 개인 키 등록과 넓은 권한을 남길지는 배포 범위에 맞춰 판단합니다. cloud-init에서도 같은 사용자를 설정한다면 키, 비밀번호 정책과 sudo 설정을 맞춥니다. libguestfs SSH 키 지정 방법도 확인합니다.

호스트 이름·시간대·파일·패키지 변경

호스트 이름과 시간대를 바꾸고 파일을 배치할 수도 있습니다. 아래의 motd.txt는 작업 PC에서 미리 준비하는 로그인 안내문입니다. --upload는 대상 파일을 덮어쓰므로 기존 내용이 필요하면 먼저 읽어 보거나 보관합니다.

sudo virt-customize --format qcow2 -a rocky9-ahv.qcow2 \
  --hostname ahv-rocky-01 \
  --timezone Asia/Seoul \
  --upload motd.txt:/etc/motd \
  --selinux-relabel

패키지는 다음처럼 추가할 수 있습니다. 이미지 내부의 패키지 관리자를 사용하므로 저장소에 접근할 수 있는 네트워크와 DNS가 필요합니다. 폐쇄망에서는 내부 저장소를 먼저 연결하거나 첫 부팅 후 패키지를 설치합니다.

sudo virt-customize --format qcow2 -a ubuntu2404-ahv.qcow2 \
  --install qemu-guest-agent

qemu-guest-agent는 Nutanix Guest Tools(NGT)와 다른 도구입니다. 설치 필요성과 사용할 수 있는 기능은 대상 환경에서 확인합니다. 파일 하나를 대화형으로 편집하려면 sudo virt-edit --format=qcow2 -a rocky9-ahv.qcow2 /etc/hosts도 사용할 수 있어요. 옵션은 virt-customize, 대화형 편집은 virt-edit 문서에서 확인합니다.

8. 접속·부팅 문제 확인

증상확인할 곳다음 조치
No bootable device이미지 복제 여부, 부팅 디스크, BIOS·UEFI, 가져오기 작업빈 디스크로 부팅하는지 확인하고 이미지에 맞는 펌웨어 선택
IP 할당 실패VLAN·NIC 연결·DHCP·IPAM·게스트의 링크와 경로콘솔에서 ip -br address 확인
콘솔 화면이 비어 있음실행 상태, 그래픽·시리얼 콘솔 출력 위치SSH 접속과 이미지의 콘솔 설정도 함께 확인
Permission denied (publickey)실제 사용자 이름, 공개키, 권한, cloud-init 로그root 비밀번호 변경과 SSH 허용을 혼동했는지 확인
바꾼 비밀번호 사용 불가cloud-init 덮어쓰기, 잠금, 만료, 유효한 sshd 설정passwd -S와 SSH 인증 정책을 나눠 확인
루트 용량 미확장가상 디스크·파티션·LVM·파일시스템의 각 용량현재 구조를 확인하고 필요한 계층 확장

콘솔에서 관리자 권한을 사용할 수 있다면 다음 명령으로 ops 상태와 실제 적용된 sshd 설정을 확인합니다. passwd -S는 비밀번호 해시를 출력하지 않습니다. sshd -T는 설정 파일 한 줄보다 최종 구성을 확인하기에 좋지만, Match 조건이 있다면 해당 접속 조건에 맞춘 평가도 필요합니다. OpenSSH 설정 문서를 참고합니다.

sudo passwd -S ops
sudo /usr/sbin/sshd -T | grep -E \
  '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|pubkeyauthentication|usepam) '
sudo journalctl -b -u ssh -u sshd --no-pager -n 80

root SSH를 허용하거나 SELinux를 끄기 전에 어느 단계가 실패하는지 구분합니다. virt-customize 자체가 시작되지 않으면 작업 PC에서 libguestfs-test-tool로 환경을 확인합니다. 가상화 가속을 사용할 수 없는 PC에서는 처리가 느릴 수 있으며, 이것만으로 이미지 문제라고 판단하지 않습니다.

공통 이미지로 다시 사용할 때

공식 이미지를 오프라인으로 수정해 배포하는 경우와 이미 부팅한 VM을 템플릿으로 만드는 경우는 구분해야 합니다. 부팅한 VM에는 cloud-init 처리 기록, machine-id, SSH 호스트 키, DHCP 상태와 개별 자격 증명이 남을 수 있습니다. 공유 템플릿에는 고정 비밀번호를 남기지 않고 VM별 키와 초기 자격 증명을 전달하는 구성으로 정리합니다.

cloud-init 기반 템플릿은 준비한 원본 VM에서 sudo cloud-init clean --logs --machine-id를 실행하고 종료한 뒤 채취하는 절차를 검토합니다. 이 명령은 다음 부팅에 cloud-init을 다시 실행하게 하므로 운영 중인 VM의 문제 해결 명령으로 무조건 사용하지 않습니다. SSH 호스트 키와 사용자 키 정리는 별도로 확인하며, 필요하면 virt-sysprep의 작업 대상을 명시합니다. 기본 일괄 정리로 주입한 키나 필요한 설정이 지워지지 않게 합니다.

먼저 VM 한 대에서 초기화와 SSH 접속을 확인하고, 재부팅 후 네트워크와 스토리지가 유지되는지 살펴본 뒤 배포를 늘리면 편해요. VM별 초기 설정을 더 구성하려면 cloud-init 실전 예제를 함께 참고하세요.