diff --git a/packer/.gitignore b/packer/.gitignore new file mode 100644 index 0000000..d0d0181 --- /dev/null +++ b/packer/.gitignore @@ -0,0 +1,3 @@ +output/ +server.pkrvars.hcl +packer_cache/ diff --git a/packer/build.sh b/packer/build.sh new file mode 100755 index 0000000..6652080 --- /dev/null +++ b/packer/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +if [ ! -f server.pkrvars.hcl ]; then + echo "Error: server.pkrvars.hcl not found." + echo "Copy server.pkrvars.hcl.example to server.pkrvars.hcl and fill in your values." + exit 1 +fi + +echo "=== Initializing Packer plugins ===" +packer init ubuntu-server.pkr.hcl + +echo "" +echo "=== Building image ===" +echo "This will take 15-30 minutes depending on your machine." +echo "" + +packer build -var-file=server.pkrvars.hcl ubuntu-server.pkr.hcl + +echo "" +echo "===========================================" +echo " Image built successfully!" +echo "===========================================" +echo " Output: $SCRIPT_DIR/output/packer-ubuntu-server" +echo "" +echo " To test locally:" +echo " qemu-system-x86_64 -m 4096 -hda output/packer-ubuntu-server -enable-kvm" +echo "" +echo " To convert for other formats:" +echo " qemu-img convert -f qcow2 -O raw output/packer-ubuntu-server output/server.raw" +echo " qemu-img convert -f qcow2 -O vmdk output/packer-ubuntu-server output/server.vmdk" +echo "===========================================" diff --git a/packer/http/meta-data b/packer/http/meta-data new file mode 100644 index 0000000..e69de29 diff --git a/packer/http/user-data b/packer/http/user-data new file mode 100644 index 0000000..582a198 --- /dev/null +++ b/packer/http/user-data @@ -0,0 +1,31 @@ +#cloud-config +autoinstall: + version: 1 + locale: en_US.UTF-8 + keyboard: + layout: us + network: + version: 2 + ethernets: + id0: + match: + driver: virtio_net + dhcp4: true + storage: + layout: + name: lvm + sizing-policy: all + identity: + hostname: server + username: ubuntu + password: "$6$XjqGOA0giVyHdSZ0$9YqNQlsfuU5xo8uLqpnD49mS3KqyMSp.6imNnIQE18obgTyE0g8LOcL6hvg0sJQXgHv6S7HSIBpKY0.keMXKU." + ssh: + install-server: true + allow-pw: true + packages: + - openssh-server + - curl + - wget + late-commands: + - "echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu" + - "chmod 440 /target/etc/sudoers.d/ubuntu" diff --git a/packer/server.pkrvars.hcl.example b/packer/server.pkrvars.hcl.example new file mode 100644 index 0000000..df5a873 --- /dev/null +++ b/packer/server.pkrvars.hcl.example @@ -0,0 +1,7 @@ +base_domain = "example.com" +ssh_pubkey = "ssh-ed25519 AAAA... user@host" +juicefs_s3_endpoint = "https://s3.amazonaws.com" +juicefs_s3_bucket = "my-nextcloud-bucket" +juicefs_s3_access_key = "AKIAIOSFODNN7EXAMPLE" +juicefs_s3_secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" +juicefs_cache_size = "50G" diff --git a/packer/ubuntu-server.pkr.hcl b/packer/ubuntu-server.pkr.hcl new file mode 100644 index 0000000..7c43d8a --- /dev/null +++ b/packer/ubuntu-server.pkr.hcl @@ -0,0 +1,173 @@ +packer { + required_plugins { + qemu = { + version = "~> 1" + source = "github.com/hashicorp/qemu" + } + ansible = { + version = ">= 1.1.2" + source = "github.com/hashicorp/ansible" + } + } +} + +# --- VM settings --- + +variable "cpu" { + type = string + default = "2" +} + +variable "ram" { + type = string + default = "4096" +} + +variable "disk_size" { + type = string + default = "50000" +} + +variable "headless" { + type = bool + default = true +} + +# --- Ubuntu ISO --- + +variable "iso_url" { + type = string + default = "https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso" +} + +variable "iso_checksum" { + type = string + default = "file:https://releases.ubuntu.com/24.04/SHA256SUMS" +} + +# --- OS user (created by autoinstall) --- + +variable "ssh_username" { + type = string + default = "ubuntu" +} + +variable "ssh_password" { + type = string + default = "ubuntu" + sensitive = true +} + +# --- Server config (passed to Ansible) --- + +variable "base_domain" { + type = string +} + +variable "ssh_pubkey" { + type = string + default = "" +} + +variable "juicefs_s3_endpoint" { + type = string +} + +variable "juicefs_s3_bucket" { + type = string +} + +variable "juicefs_s3_access_key" { + type = string + sensitive = true +} + +variable "juicefs_s3_secret_key" { + type = string + sensitive = true +} + +variable "juicefs_cache_size" { + type = string + default = "50G" +} + +source "qemu" "ubuntu-server" { + accelerator = "kvm" + boot_command = [ + "c", + "linux /casper/vmlinuz --- autoinstall ds=\"nocloud;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/\"", + "initrd /casper/initrd", + "boot" + ] + boot_wait = "10s" + disk_cache = "none" + disk_compression = true + disk_discard = "unmap" + disk_interface = "virtio" + disk_size = var.disk_size + format = "qcow2" + headless = var.headless + http_directory = "http" + iso_checksum = var.iso_checksum + iso_url = var.iso_url + net_device = "virtio-net" + output_directory = "output" + qemu_binary = "/usr/bin/qemu-system-x86_64" + qemuargs = [ + ["-m", "${var.ram}M"], + ["-smp", var.cpu], + ["-cpu", "host"] + ] + shutdown_command = "echo '${var.ssh_password}' | sudo -S shutdown -P now" + ssh_password = var.ssh_password + ssh_username = var.ssh_username + ssh_handshake_attempts = 500 + ssh_timeout = "45m" + ssh_wait_timeout = "45m" +} + +build { + sources = ["source.qemu.ubuntu-server"] + + provisioner "shell" { + execute_command = "echo '${var.ssh_password}' | sudo -S bash -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "apt-get update", + "apt-get install -y ansible-core python3-pip" + ] + } + + provisioner "file" { + source = "../playbook.yml" + destination = "/tmp/playbook.yml" + } + + provisioner "file" { + source = "../resources" + destination = "/tmp/resources" + } + + provisioner "shell" { + execute_command = "echo '${var.ssh_password}' | sudo -S bash -c '{{ .Vars }} {{ .Path }}'" + environment_vars = [ + "ANSIBLE_FORCE_COLOR=1" + ] + inline = [ + "ansible-playbook -i localhost, -c local /tmp/playbook.yml -e 'base_domain=${var.base_domain} ssh_pubkey=\"${var.ssh_pubkey}\" juicefs_s3_endpoint=${var.juicefs_s3_endpoint} juicefs_s3_bucket=${var.juicefs_s3_bucket} juicefs_s3_access_key=${var.juicefs_s3_access_key} juicefs_s3_secret_key=${var.juicefs_s3_secret_key} juicefs_cache_size=${var.juicefs_cache_size}'" + ] + } + + provisioner "shell" { + execute_command = "echo '${var.ssh_password}' | sudo -S bash -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "rm -rf /tmp/playbook.yml /tmp/resources", + "apt-get clean", + "rm -rf /var/lib/apt/lists/*", + "cloud-init clean --logs", + "truncate -s 0 /etc/machine-id", + "rm -f /var/lib/dbus/machine-id", + "fstrim -av || true" + ] + } +}