بلاگ/ساخت Golden Image با HashiCorp Packer روی سرور اختصاصی: پیش‌پیکربندی VM با QEMU/KVM برای استقرار سریع
به‌روز شده

ساخت Golden Image با HashiCorp Packer روی سرور اختصاصی: پیش‌پیکربندی VM با QEMU/KVM برای استقرار سریع

1405/06/240 بازدید
ساخت Golden Image با HashiCorp Packer روی سرور اختصاصی: پیش‌پیکربندی VM با QEMU/KVM برای استقرار سریع

ساخت Golden Image با HashiCorp Packer روی سرور اختصاصی: پیش‌پیکربندی VM با QEMU/KVM برای استقرار سریع

ساخت Golden Image با HashiCorp Packer برای ماشین‌های مجازی QEMU/KVM نصب دستی سیستم‌عامل برای هر ماشین مجازی، در مقیاس کوچک قابل‌تحمل است؛ اما با افزایش تعداد VMها، تفاوت‌های ناخواسته هم ظاهر می‌شوند. ممکن است نسخه یک بسته، تنظیمات SSH، پارتیشن‌بندی یا حساب‌های کاربری در دو سرور ظاهراً مشابه یکسان نباشد. این اختلاف‌ها عیب‌یابی و به‌روزرسانی زیرساخت را دشوار می‌کنند. Golden Image یک دیسک پایه و آزمایش‌شده است که سیستم‌عامل، بسته‌های ضروری و تنظیمات عمومی از قبل روی آن قرار گرفته‌اند. HashiCorp Packer فرایند تولید چنین ایمیجی را به کد تبدیل می‌کند. در این راهنما، ساخت Golden Image با Packer و بیلدر QEMU را روی یک سرور اختصاصی لینوکس بررسی می‌کنیم؛ سپس ایمیج QCOW2 حاصل را برای ایجاد سریع VMهای مبتنی بر KVM به کار می‌بریم. هدف این نیست که همه تنظیمات هر سرور را داخل ایمیج ثابت کنیم. ایمیج باید شامل اجزای مشترک و کم‌تغییر باشد و اطلاعات وابسته به هر نمونه، مانند نام میزبان، کلید SSH و تنظیمات شبکه، هنگام استقرار با cloud-init تزریق شوند.

Golden Image چه مشکلی را حل می‌کند؟

Golden Image نقطه شروع یکسانی برای ماشین‌های مجازی می‌سازد. به‌جای نصب دوباره Ubuntu، افزودن ابزارهای پایه و اجرای تنظیمات امنیتی برای هر VM، یک بار این مراحل را با Packer اجرا می‌کنیم و خروجی نسخه‌دار می‌گیریم. این رویکرد چند مزیت عملی دارد: - زمان آماده‌سازی VM از مدت نصب سیستم‌عامل به زمان کپی یا Clone کردن یک دیسک کاهش می‌یابد. - تنظیمات پایه میان همه ماشین‌ها یکسان و قابل بازتولید می‌ماند. - تعریف ایمیج را می‌توان همراه کد زیرساخت در Git نگه داشت و بازبینی کرد. - ساخت دوره‌ای ایمیج باعث می‌شود وصله‌های سیستم‌عامل از همان ابتدای عمر VM نصب شده باشند. - در صورت بروز مشکل، می‌توان به نسخه قبلی ایمیج برگشت. البته Golden Image جایگزین کامل ابزارهایی مانند Ansible نیست. Packer برای ساخت آرتیفکت پایه مناسب است، در حالی که ابزار مدیریت پیکربندی می‌تواند تنظیمات مختص نقش هر سرور را پس از ایجاد VM اعمال کند.

پیش‌نیازهای ساخت ایمیج روی سرور اختصاصی

برای استفاده مناسب از QEMU/KVM، پردازنده سرور باید مجازی‌سازی سخت‌افزاری Intel VT-x یا AMD-V را پشتیبانی کند و این قابلیت در BIOS یا UEFI فعال باشد. دستور زیر وجود فلگ‌های پردازنده را بررسی می‌کند:

egrep -c '(vmx|svm)' /proc/cpuinfo

خروجی بزرگ‌تر از صفر به معنی مشاهده قابلیت مجازی‌سازی توسط کرنل است. وجود دستگاه KVM را نیز بررسی کنید:

ls -l /dev/kvm

روی Ubuntu یا Debian، بسته‌های موردنیاز را می‌توان با این دستور نصب کرد:

sudo apt update
sudo apt install -y qemu-kvm qemu-utils libvirt-daemon-system \
  libvirt-clients bridge-utils genisoimage curl unzip

کاربری که Packer را اجرا می‌کند باید به /dev/kvm دسترسی داشته باشد. معمولاً عضویت در گروه‌های kvm و libvirt کافی است:

sudo usermod -aG kvm,libvirt "$USER"

پس از اجرای این دستور، نشست کاربر را ببندید و دوباره وارد شوید تا عضویت گروه‌ها اعمال شود. سپس وضعیت شتاب‌دهنده را بررسی کنید:

sudo kvm-ok

در توزیع‌هایی که kvm-ok نصب نیست، می‌توان از virt-host-validate استفاده کرد:

sudo virt-host-validate

منابع لازم به سیستم‌عامل مهمان و بسته‌هایی که هنگام Build نصب می‌شوند بستگی دارد. برای یک ایمیج سبک Ubuntu، اختصاص موقت ۲ هسته پردازنده، ۲ تا ۴ گیگابایت RAM و دست‌کم ۲۰ گیگابایت فضای آزاد معمولاً نقطه شروع مناسبی است. ظرفیت موردنیاز محیط عملیاتی را جداگانه محاسبه کنید.

نصب HashiCorp Packer

بهتر است Packer از مخزن رسمی HashiCorp نصب شود تا مدیریت نسخه و به‌روزرسانی آن ساده بماند. در Ubuntu و Debian ابتدا کلید و مخزن را اضافه کنید:

curl -fsSL https://apt.releases.hashicorp.com/gpg |
  sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(. /etc/os-release && echo "$VERSION_CODENAME") main" |
  sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install -y packer

نسخه نصب‌شده را ببینید:

packer version

قالب‌های جدید Packer با زبان HCL2 و پسوند pkr.hcl نوشته می‌شوند. بیلدر QEMU نیز از طریق افزونه رسمی hashicorp/qemu در دسترس است. نسخه افزونه را در فایل قالب محدود می‌کنیم تا تغییرات آینده بدون کنترل وارد فرایند Build نشوند.

ساختار پروژه Packer

یک دایرکتوری برای قالب، فایل پاسخ خودکار نصب Ubuntu و اسکریپت‌های Provisioning بسازید:

packer-golden-image/
├── http/
│   └── user-data
├── scripts/
│   └── base.sh
├── ubuntu.pkr.hcl
└── variables.pkr.hcl

فایل http/meta-data نیز باید وجود داشته باشد، حتی اگر خالی باشد. Packer محتویات پوشه http را هنگام نصب از طریق وب‌سرور موقت در اختیار ماشین مهمان می‌گذارد. جریان ساخت Golden Image: سرور اختصاصی از طریق QEMU/KVM پردازش می‌شود، Packer ایمیج پایه را می‌سازد و همان ایمیج برای استقرار چند ماشین مجازی مستقل توزیع می‌شود فرایند کلی از دریافت ISO شروع می‌شود. QEMU یک VM موقت می‌سازد، نصب خودکار سیستم‌عامل انجام می‌شود، Packer از طریق SSH اسکریپت‌های Provisioning را اجرا می‌کند و در پایان دیسک QCOW2 باقی می‌ماند. VM موقت پس از Build حذف می‌شود.

تعریف متغیرها و بیلدر QEMU

فایل variables.pkr.hcl را به این شکل ایجاد کنید:

variable "ubuntu_iso_url" {
  type    = string
  default = "https://releases.ubuntu.com/24.04/ubuntu-24.04.3-live-server-amd64.iso"
}
variable "ubuntu_iso_checksum" {
  type        = string
  description = "SHA256 checksum from the Ubuntu release manifest"
}
variable "ssh_username" {
  type    = string
  default = "packer"
}
variable "ssh_password" {
  type      = string
  sensitive = true
}

مقدار Checksum را از فایل رسمی SHA256SUMS همان انتشار بگیرید. استفاده از مقدار واقعی SHA-256 ضروری است؛ none یا Checksum حدسی باعث می‌شود Packer نتواند دست‌کاری یا دانلود ناقص ISO را تشخیص دهد. اکنون فایل ubuntu.pkr.hcl را بنویسید:

packer {
  required_plugins {
    qemu = {
      version = ">= 1.1.0, < 2.0.0"
      source  = "github.com/hashicorp/qemu"
    }
  }
}
source "qemu" "ubuntu" {
  iso_url          = var.ubuntu_iso_url
  iso_checksum     = "sha256:${var.ubuntu_iso_checksum}"
  output_directory = "output/ubuntu-2404"
  vm_name          = "ubuntu-2404-golden.qcow2"
  accelerator      = "kvm"
  format           = "qcow2"
  disk_size        = "20480M"
  disk_interface   = "virtio"
  net_device       = "virtio-net"
  cpus             = 2
  memory           = 4096
  headless         = true
  http_directory   = "http"
  ssh_username     = var.ssh_username
  ssh_password     = var.ssh_password
  ssh_timeout      = "30m"
  shutdown_command = "echo '${var.ssh_password}' | sudo -S shutdown -P now"
  boot_wait = "5s"
  boot_command = [
    "e<wait>",
    "<down><down><down>",
    "<end>",
    " autoinstall ds=nocloud-net\\;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/",
    "<f10>"
  ]
}
build {
  name    = "ubuntu-kvm-golden"
  sources = ["source.qemu.ubuntu"]
  provisioner "shell" {
    script = "scripts/base.sh"
  }
}

ترتیب کلیدهای boot_command به منوی نسخه ISO وابسته است. اگر نسخه دیگری از Ubuntu انتخاب کنید، ابتدا Build را با نمایش گرافیکی آزمایش کنید و ترتیب ورود به ویرایشگر GRUB را با همان ISO تطبیق دهید. برای این کار می‌توان موقتاً headless=false قرار داد.

نصب خودکار Ubuntu با cloud-init

فایل http/user-data ورودی نصب‌کننده Subiquity است. نمونه زیر یک کاربر موقت برای Packer ایجاد می‌کند:

#cloud-config
autoinstall:
  version: 1
  locale: fa_IR.UTF-8
  keyboard:
    layout: us
  identity:
    hostname: golden-builder
    username: packer
    password: "$6$REPLACE_WITH_A_REAL_SHA512_CRYPT_HASH"
  ssh:
    install-server: true
    allow-pw: true
  storage:
    layout:
      name: direct
  packages:
    - qemu-guest-agent
    - cloud-init
    - curl
    - ca-certificates
  late-commands:
    - curtin in-target --target=/target -- systemctl enable qemu-guest-agent

رمز عبور این فایل باید با متغیر ssh_password یکسان باشد. برای تولید هش SHA-512 می‌توان از دستور زیر استفاده کرد:

openssl passwd -6

فایل http/meta-data را نیز ایجاد کنید و آن را خالی بگذارید. رمز خام را داخل Git ثبت نکنید. در محیط CI بهتر است مقدار حساس از Secret Store به Packer داده شود و فایل پاسخ نصب نیز هنگام اجرای Pipeline از یک قالب امن ساخته شود.

Provisioning و پاک‌سازی ایمیج

اسکریپت scripts/base.sh بسته‌ها و تنظیمات عمومی را اعمال می‌کند:

#!/usr/bin/env bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get dist-upgrade -y
sudo apt-get install -y \
  qemu-guest-agent cloud-init curl jq vim-tiny chrony
sudo systemctl enable qemu-guest-agent
sudo systemctl enable chrony
sudo apt-get autoremove -y
sudo apt-get clean
sudo cloud-init clean --logs --machine-id
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo find /var/log -type f -exec truncate -s 0 {} \;
sudo rm -f /etc/ssh/ssh_host_*
rm -f "$HOME/.bash_history"

حذف کلیدهای Host مربوط به SSH و Machine ID اهمیت دارد؛ در غیر این صورت VMهای Cloneشده ممکن است هویت یکسان داشته باشند. Ubuntu هنگام نخستین Boot شناسه ماشین و کلیدهای SSH جدید تولید می‌کند. اطلاعات مختص هر نمونه را در اسکریپت پایه قرار ندهید. آدرس IP ثابت، نام میزبان نهایی، کلید خصوصی، Token سرویس‌ها و Credential پایگاه داده باید هنگام استقرار تأمین شوند، نه هنگام ساخت ایمیج.

اجرای Build و بررسی خروجی

ابتدا افزونه‌ها را نصب و قالب را قالب‌بندی و اعتبارسنجی کنید:

packer init .
packer fmt -check .
packer validate \
  -var "ubuntu_iso_checksum=CHECKSUM_REAL" \
  -var "ssh_password=TEMPORARY_PASSWORD" .

سپس Build را اجرا کنید:

packer build \
  -var "ubuntu_iso_checksum=CHECKSUM_REAL" \
  -var "ssh_password=TEMPORARY_PASSWORD" .

پس از پایان موفق، فایل زیر ساخته می‌شود:

output/ubuntu-2404/ubuntu-2404-golden.qcow2

پیش از انتشار ایمیج، مشخصات آن را بررسی کنید:

qemu-img info output/ubuntu-2404/ubuntu-2404-golden.qcow2
qemu-img check output/ubuntu-2404/ubuntu-2404-golden.qcow2

برای اطمینان از Boot شدن سیستم، یک VM آزمایشی از روی کپی ایمیج بسازید. هر تست باید روی Overlay یا کپی جداگانه انجام شود تا فایل Golden Image تغییر نکند:

qemu-img create -f qcow2 \
  -F qcow2 \
  -b "$(pwd)/output/ubuntu-2404/ubuntu-2404-golden.qcow2" \
  test-vm.qcow2

در تست پذیرش، Boot موفق، دریافت شبکه، اجرای cloud-init، تولید Machine ID و کلید SSH جدید، فعال بودن qemu-guest-agent و نبود Credential باقی‌مانده را بررسی کنید. نصب شدن بسته‌ها به‌تنهایی برای تأیید ایمیج کافی نیست.

مقایسه نصب دستی، Snapshot و Golden Image

این سه روش کاربرد یکسانی ندارند. Snapshot معمولاً وضعیت یک VM مشخص را در لحظه ثبت می‌کند، اما Golden Image برای ایجاد نمونه‌های مستقل و تکرارپذیر ساخته می‌شود.

روشتکرارپذیریسرعت استقرارقابلیت نسخه‌بندیکاربرد مناسب
نصب دستیکمکممحدودآزمایش موقت یا یک VM منفرد
Snapshotمتوسطزیادوابسته به پلتفرمبازگشت کوتاه‌مدت یک VM
Golden Image با Packerزیادزیادزیادساخت مداوم VMهای استاندارد
Golden Image همراه cloud-initزیادزیادزیاداستقرار استاندارد با تنظیمات مختص هر نمونه

برای محیط عملیاتی، ترکیب Packer و cloud-init معمولاً انعطاف بیشتری دارد. Packer بخش ثابت سیستم را آماده می‌کند و cloud-init تفاوت‌های هر ماشین را در نخستین Boot اعمال می‌کند.

استقرار VM از روی ایمیج

در محیط libvirt می‌توان از Golden Image به‌عنوان Backing File استفاده کرد. این روش سریع و کم‌مصرف است، اما فایل پایه پس از ایجاد Overlayها نباید جابه‌جا یا ویرایش شود:

sudo qemu-img create -f qcow2 \
  -F qcow2 \
  -b /var/lib/libvirt/images/golden/ubuntu-2404-v1.qcow2 \
  /var/lib/libvirt/images/app-01.qcow2

سپس VM را با virt-install تعریف کنید:

sudo virt-install \
  --name app-01 \
  --memory 4096 \
  --vcpus 2 \
  --disk path=/var/lib/libvirt/images/app-01.qcow2,format=qcow2,bus=virtio \
  --network bridge=virbr0,model=virtio \
  --import \
  --os-variant ubuntu24.04 \
  --noautoconsole

برای محیط‌هایی که استقلال کامل دیسک‌ها مهم‌تر از صرفه‌جویی در فضا است، از Full Clone استفاده کنید. پس از کپی فایل، در صورت نیاز ظرفیت دیسک را با qemu-img resize افزایش دهید و فایل‌سیستم داخل مهمان را نیز بزرگ کنید.

نسخه‌بندی و نگه‌داری Golden Image

نام فایل خروجی باید نسخه سیستم‌عامل و نسخه Build را نشان دهد؛ برای مثال:

ubuntu-2404-v2026.09.15.qcow2

در کنار فایل، Checksum و اطلاعات Build را نگه دارید:

sha256sum ubuntu-2404-v2026.09.15.qcow2 \
  > ubuntu-2404-v2026.09.15.qcow2.sha256

بهتر است ایمیج با هر تغییر قالب، انتشار وصله امنیتی مهم یا در یک برنامه زمانی مشخص دوباره ساخته شود. ایمیج موجود را مستقیماً اصلاح نکنید؛ قالب را تغییر دهید، Build تازه بگیرید، تست کنید و سپس نسخه جدید را جایگزین کنید. این مدل Immutable خطای ناشی از تغییرات ثبت‌نشده را کاهش می‌دهد. حداقل یک نسخه سالم قبلی را برای Rollback نگه دارید. حذف نسخه قدیمی باید پس از اطمینان از عملکرد VMهای ساخته‌شده با نسخه جدید انجام شود.

خطاهای رایج هنگام ساخت Golden Image با Packer

اگر Build در مرحله اتصال SSH متوقف شد، ابتدا شبکه VM، نصب شدن OpenSSH، نام کاربری و رمز عبور و مقدار ssh_timeout را بررسی کنید. مشاهده کنسول VM با headless=false معمولاً نشان می‌دهد نصب‌کننده در کدام مرحله مانده است. کند بودن شدید Build اغلب به فعال نبودن KVM مربوط است. مقدار accelerator="kvm" به‌تنهایی کافی نیست؛ کاربر اجراکننده باید به /dev/kvm دسترسی داشته باشد و مجازی‌سازی سخت‌افزاری نیز فعال باشد. باقی ماندن Machine ID، کلیدهای SSH یا تنظیمات شبکه ثابت در ایمیج از خطاهای جدی است. همچنین نباید Golden Image روشن را کپی کرد، مگر اینکه سازوکار Snapshot سازگار و Quiesce کردن فایل‌سیستم در نظر گرفته شده باشد. اگر هر Build بسته‌های متفاوتی دریافت می‌کند، مخازن و نسخه وابستگی‌ها را کنترل کنید. برای نیازهای سخت‌گیرانه‌تر می‌توان از Snapshot Repository داخلی استفاده کرد تا ورودی Build در طول زمان قابل بازتولید بماند.

آماده‌سازی زیرساخت مناسب برای Build

ساخت ایمیج به پردازنده‌ای با مجازی‌سازی سخت‌افزاری، فضای ذخیره‌سازی سریع و دسترسی پایدار به مخازن بسته‌ها نیاز دارد. اجرای Packer روی سخت‌افزار اختصاصی باعث می‌شود Buildهای QEMU/KVM بدون وابستگی به Nested Virtualization انجام شوند و منابع میان فرایند ساخت و بارهای دیگر قابل کنترل باشند. اگر برای ساخت و نگه‌داری Golden Image به میزبان KVM با منابع اختصاصی نیاز دارید، مشخصات سرور اختصاصی آریاسرویس را بررسی کنید. پیش از انتخاب، ظرفیت CPU، RAM و فضای لازم برای Buildهای هم‌زمان و آرشیو نسخه‌های ایمیج را محاسبه کنید.

جمع‌بندی

ساخت Golden Image با Packer، نصب و پیش‌پیکربندی ماشین‌های مجازی را به یک فرایند قابل تکرار تبدیل می‌کند. بیلدر QEMU یک VM موقت روی KVM می‌سازد، نصب خودکار Ubuntu را انجام می‌دهد و پس از اجرای Provisioning، دیسک QCOW2 آماده تحویل می‌دهد. ایمیج نهایی باید عمومی، پاک‌سازی‌شده و آزمایش‌شده باشد. تنظیمات ثابت در Packer قرار می‌گیرند و اطلاعات مختص هر VM با cloud-init یا ابزار مدیریت پیکربندی اعمال می‌شوند. با نسخه‌بندی خروجی، ثبت Checksum و نگه‌داری نسخه قبلی، می‌توان استقرار سریع را بدون قربانی کردن قابلیت پیگیری و Rollback به دست آورد.