Initial commit: MAAS-deployable Proxmox VE images with cluster automation
Builds a Proxmox VE image that MAAS can deploy to bare metal, plus first-boot
automation that configures the node and joins it to a Proxmox cluster with no
manual steps.
The image starts from the official Debian cloud image and installs proxmox-ve
on top of it, rather than capturing a raw disk from the Proxmox ISO. That keeps
MAAS in control of partitioning, networking, SSH keys and cloud-init, and makes
moving between Proxmox releases a variable change instead of a rewrite.
Contents:
* Makefile driving the whole flow: build, verify, preseed, upload
* customize-proxmox.sh, run inside the Packer build VM, which layers Proxmox
onto the Debian cloud image and resets the pmxcfs node identity so one image
can produce many nodes
* pve-maas-init, a first-boot state machine covering /etc/hosts, node-unique
identifiers, the root password, vmbr0 conversion, cluster create/join and
the local-lvm thin pool; each stage is resumable across reboots
* curtin-hooks, which stops curtin installing a kernel over APT and pins
interface names by MAC so they match what MAAS recorded at commissioning
* a MAAS curtin preseed template and cloud-init examples
* deploy-cluster.sh, which builds a whole cluster through the MAAS API
* verify-image.sh, 22 static checks on the produced tarball
Cluster identity lives entirely in deploy-time cloud-init user-data, so a single
image and preseed can build any number of independent clusters.
Verified end to end against MAAS 3.7.2: proxmox-ve 9.2.0 / pve-manager 9.2.11 /
kernel 7.0.14-15-pve, deployed to two machines that formed a quorate cluster with
local-lvm on both, with no manual intervention.
The README documents four failure modes found along the way that all fail
silently: curtin rejecting "kernel: null", pvenetcommit overwriting the network
configuration at boot, interface renaming leaving the link down, and a systemd
ordering cycle that made systemd delete the service's start job.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/build/
|
||||
*.tar.gz
|
||||
*.dd.gz
|
||||
OVMF_CODE.fd
|
||||
OVMF_VARS.fd
|
||||
.secrets/
|
||||
251
Makefile
Normal file
251
Makefile
Normal file
@@ -0,0 +1,251 @@
|
||||
# maas-proxmox - Proxmox VE icin MAAS'a yuklenebilir Packer imaji
|
||||
#
|
||||
# Hizli baslangic (build host uzerinde, root olarak):
|
||||
# sudo ./scripts/install-deps.sh
|
||||
# sudo make image
|
||||
# make preseed
|
||||
# make upload MAAS_PROFILE=admin
|
||||
#
|
||||
SHELL := /bin/bash
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# ---------------------------------------------------------------- surum ayarlari
|
||||
# Proxmox VE ana surumu. Yeni bir PVE surumu ciktiginda genelde sadece
|
||||
# PVE_VERSION + DEBIAN_SERIES/DEBIAN_VERSION degistirmek yeterlidir.
|
||||
PVE_VERSION ?= 9
|
||||
DEBIAN_SERIES ?= trixie
|
||||
DEBIAN_VERSION ?= 13
|
||||
|
||||
# Proxmox APT deposu: pve-no-subscription | pve-enterprise | pve-test
|
||||
PVE_REPO ?= pve-no-subscription
|
||||
PVE_REPO_URI ?= http://download.proxmox.com/debian/pve
|
||||
PVE_KEYRING_URL ?= https://enterprise.proxmox.com/debian/proxmox-archive-keyring-$(DEBIAN_SERIES).gpg
|
||||
|
||||
# proxmox-ve disinda imaja girecek ek paketler
|
||||
# NOT: 'vlan' ve 'vzdump' pve-manager ile catisir (Conflicts) - eklemeyin.
|
||||
# VLAN destegini ifupdown2 kendisi saglar.
|
||||
PVE_EXTRA_PACKAGES ?= ifupdown2 open-iscsi chrony postfix lvm2 thin-provisioning-tools \
|
||||
ethtool bridge-utils ipmitool nvme-cli lsscsi sudo
|
||||
|
||||
# ---------------------------------------------------------------- imaj ayarlari
|
||||
ARCH ?= amd64
|
||||
SUBARCH ?= generic
|
||||
BOOT ?= uefi
|
||||
TIMEOUT ?= 3h
|
||||
PACKER_LOG ?= 0
|
||||
|
||||
# Build VM kaynaklari. Upstream sablon 4G/2CPU/2GB ile gelir; Debian cloud image
|
||||
# (~3G) + Proxmox VE (~3G) 4G'ye sigmaz, bu yuzden sablonu yamiyoruz.
|
||||
# Sonuc tgz yalnizca kullanilan dosyalari icerdiginden buyuk disk imaji sismez.
|
||||
DISK_SIZE ?= 16G
|
||||
BUILD_CPUS ?= 4
|
||||
BUILD_MEM ?= 4096
|
||||
|
||||
# --- derleme hizi -------------------------------------------------------------
|
||||
# stable | daily
|
||||
# stable: cloud.debian.org/.../trixie/latest (sabit URL -> packer onbellegi
|
||||
# calisir, tekrarlanan derlemelerde ~350MB indirme yok, tekrarlanabilir)
|
||||
# daily : upstream sablonun varsayilani (her gun degisir, onbellek isabet etmez)
|
||||
DEBIAN_IMAGE_CHANNEL ?= stable
|
||||
|
||||
# Tarball sikistirma seviyesi. Upstream --best (9) kullaniyor; 6 belirgin daha
|
||||
# hizli ve imaj yalnizca ~%2-3 buyuyor. pigz varsa cok cekirdekli calisir.
|
||||
GZIP_LEVEL ?= 6
|
||||
|
||||
# Yerel APT onbellegi (or. apt-cacher-ng): http://10.0.2.2:3142
|
||||
# Packer'in user-mode aginda build VM host'u 10.0.2.2 olarak gorur.
|
||||
# Bos birakilirsa proxy kullanilmaz. Bkz: make deps-cache
|
||||
APT_PROXY ?=
|
||||
|
||||
IMAGE_NAME ?= proxmox-ve-$(PVE_VERSION)
|
||||
IMAGE_TITLE ?= Proxmox VE $(PVE_VERSION) (Debian $(DEBIAN_VERSION))
|
||||
|
||||
# ---------------------------------------------------------------- yollar
|
||||
WORKDIR ?= $(CURDIR)/build
|
||||
PM_REPO ?= https://github.com/canonical/packer-maas.git
|
||||
# Test edilmis upstream commit'e sabitlenmistir. 'main' birakmak, upstream'de
|
||||
# yapilan bir degisikligin derlemeyi haber vermeden bozmasi anlamina gelir.
|
||||
# Ileri tasimak icin: PM_REF=main ile derleyip test edin, sonra yeni SHA'yi buraya yazin.
|
||||
PM_REF ?= c23d5dd985f52b2eaccc893e886213a424a243a5
|
||||
PM := $(WORKDIR)/packer-maas
|
||||
TPL := $(PM)/debian
|
||||
|
||||
OVERLAY_TGZ := $(WORKDIR)/pve-maas-overlay.tar.gz
|
||||
CUSTOMIZE := $(WORKDIR)/customize-proxmox.sh
|
||||
OUTPUT ?= $(WORKDIR)/$(IMAGE_NAME).tar.gz
|
||||
PRESEED := $(WORKDIR)/curtin_userdata_custom_$(ARCH)_$(SUBARCH)_$(IMAGE_NAME)
|
||||
|
||||
# ---------------------------------------------------------------- MAAS ayarlari
|
||||
MAAS_PROFILE ?= admin
|
||||
MAAS_ARCH ?= $(ARCH)/$(SUBARCH)
|
||||
MAAS_IMAGE_NAME ?= custom/$(IMAGE_NAME)
|
||||
# MAAS snap kurulumu icin: /var/snap/maas/current/preseeds
|
||||
MAAS_PRESEED_DIR ?= /var/snap/maas/current/preseeds
|
||||
|
||||
OVMF_DIR ?= /usr/share/OVMF
|
||||
OVMF_SFX ?= $(shell test -f $(OVMF_DIR)/OVMF_CODE.fd && echo "" || echo "_4M")
|
||||
|
||||
# ---------------------------------------------------------------- hedefler
|
||||
.PHONY: help deps deps-cache check-upstream checkout overlay customize image verify preseed install-preseed upload clean distclean lint
|
||||
|
||||
help:
|
||||
@echo "maas-proxmox - Proxmox VE $(PVE_VERSION) MAAS imaji"
|
||||
@echo
|
||||
@echo " make deps Build host bagimliliklarini kur (root gerekir)"
|
||||
@echo " make deps-cache Yerel APT onbellegi kur (derlemeyi hizlandirir)"
|
||||
@echo " make image Imaji derle -> $(OUTPUT) (root gerekir)"
|
||||
@echo " make verify Uretilen imajin icerigini dogrula"
|
||||
@echo " make check-upstream Depodaki surumleri elimizdeki imajla karsilastir"
|
||||
@echo " make preseed MAAS curtin preseed dosyasini uret"
|
||||
@echo " make install-preseed Preseed'i $(MAAS_PRESEED_DIR) altina kopyala (root)"
|
||||
@echo " make upload Imaji MAAS'a yukle (MAAS_PROFILE=$(MAAS_PROFILE))"
|
||||
@echo " make clean Ara dosyalari sil"
|
||||
@echo " make distclean build/ dizinini tamamen sil"
|
||||
@echo
|
||||
@echo "Onemli degiskenler:"
|
||||
@echo " PVE_VERSION=$(PVE_VERSION) DEBIAN_SERIES=$(DEBIAN_SERIES) PVE_REPO=$(PVE_REPO)"
|
||||
@echo " IMAGE_NAME=$(IMAGE_NAME) ARCH=$(ARCH) BOOT=$(BOOT)"
|
||||
@echo " OUTPUT=$(OUTPUT)"
|
||||
|
||||
deps:
|
||||
./scripts/install-deps.sh
|
||||
|
||||
# --- packer-maas deposunu getir -----------------------------------------------
|
||||
$(PM)/.git:
|
||||
@mkdir -p $(WORKDIR)
|
||||
git clone $(PM_REPO) $(PM)
|
||||
|
||||
# PM_REF bir dal, etiket ya da commit SHA olabilir.
|
||||
checkout: $(PM)/.git
|
||||
@cd $(PM) && git fetch -q --all --tags && git checkout -q -f $(PM_REF) \
|
||||
&& (git symbolic-ref -q HEAD >/dev/null && git pull -q --ff-only || true)
|
||||
@echo "packer-maas: $$(cd $(PM) && git rev-parse --short HEAD) ($(PM_REF))"
|
||||
|
||||
# --- imaja gomulecek overlay ---------------------------------------------------
|
||||
overlay: $(OVERLAY_TGZ)
|
||||
|
||||
$(OVERLAY_TGZ): $(shell find overlay -type f 2>/dev/null)
|
||||
@mkdir -p $(WORKDIR)
|
||||
COPYFILE_DISABLE=1 tar czf $@ -C overlay --no-xattrs --exclude='.keep' .
|
||||
@echo "overlay: $@ ($$(du -h $@ | cut -f1))"
|
||||
|
||||
# --- packer'in VM icinde calistiracagi customize script -------------------------
|
||||
customize: $(CUSTOMIZE)
|
||||
|
||||
$(CUSTOMIZE): scripts/customize-proxmox.sh.in $(OVERLAY_TGZ)
|
||||
@mkdir -p $(WORKDIR)
|
||||
@sed -e 's|@@PVE_SUITE@@|$(DEBIAN_SERIES)|g' \
|
||||
-e 's|@@PVE_REPO@@|$(PVE_REPO)|g' \
|
||||
-e 's|@@PVE_REPO_URI@@|$(PVE_REPO_URI)|g' \
|
||||
-e 's|@@PVE_KEYRING_URL@@|$(PVE_KEYRING_URL)|g' \
|
||||
-e 's|@@PVE_VERSION@@|$(PVE_VERSION)|g' \
|
||||
-e 's|@@PVE_EXTRA_PACKAGES@@|$(PVE_EXTRA_PACKAGES)|g' \
|
||||
-e 's|@@PM_REF@@|$(PM_REF)|g' \
|
||||
$< > $@
|
||||
@echo '__PVE_MAAS_OVERLAY__' >> $@
|
||||
@base64 < $(OVERLAY_TGZ) >> $@
|
||||
@chmod +x $@
|
||||
@echo "customize script: $@"
|
||||
|
||||
# --- imaj derleme ---------------------------------------------------------------
|
||||
image: checkout $(CUSTOMIZE)
|
||||
@if [ "$$(id -u)" -ne 0 ]; then echo "HATA: 'make image' root gerektirir (sudo make image)"; exit 1; fi
|
||||
@command -v packer >/dev/null || { echo "HATA: packer yok, once 'make deps'"; exit 1; }
|
||||
@echo "==> Sablon yamalaniyor: disk=$(DISK_SIZE) cpus=$(BUILD_CPUS) mem=$(BUILD_MEM)"
|
||||
sed -i -E 's|^([[:space:]]*disk_size[[:space:]]*=[[:space:]]*).*|\1"$(DISK_SIZE)"|' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
sed -i -E 's|^([[:space:]]*cpus[[:space:]]*=[[:space:]]*).*|\1$(BUILD_CPUS)|' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
sed -i -E 's|^([[:space:]]*memory[[:space:]]*=[[:space:]]*).*|\1$(BUILD_MEM)|' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
ifeq ($(strip $(DEBIAN_IMAGE_CHANNEL)),stable)
|
||||
@echo "==> Kararli Debian cloud image kullanilacak (packer onbellegi isabet eder)"
|
||||
sed -i -E 's|/daily/latest/|/latest/|g; s|-daily\.qcow2|.qcow2|g' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
@grep -nE 'iso_url|iso_checksum' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
endif
|
||||
sed -i -E 's|--best --force|-$(GZIP_LEVEL) --force|' $(PM)/scripts/fuse-tar-root
|
||||
@grep -nE 'disk_size|^ cpus|^ memory' $(TPL)/debian-cloudimg.pkr.hcl
|
||||
cp -v $(OVMF_DIR)/OVMF_CODE$(OVMF_SFX).fd $(TPL)/OVMF_CODE.fd
|
||||
cp -v $(OVMF_DIR)/OVMF_VARS$(OVMF_SFX).fd $(TPL)/OVMF_VARS.fd
|
||||
rm -rf $(TPL)/output-cloudimg $(TPL)/seeds-cloudimg.iso
|
||||
cd $(TPL) && PACKER_LOG=$(PACKER_LOG) packer init .
|
||||
cd $(TPL) && PACKER_LOG=$(PACKER_LOG) packer build \
|
||||
-var debian_series=$(DEBIAN_SERIES) \
|
||||
-var debian_version=$(DEBIAN_VERSION) \
|
||||
-var architecture=$(ARCH) \
|
||||
-var boot_mode=$(BOOT) \
|
||||
-var ovmf_suffix=$(OVMF_SFX) \
|
||||
-var host_is_arm=false \
|
||||
-var timeout=$(TIMEOUT) \
|
||||
-var customize_script=$(CUSTOMIZE) \
|
||||
-var filename=$(OUTPUT) \
|
||||
-var http_proxy=$(APT_PROXY) \
|
||||
.
|
||||
@ls -lh $(OUTPUT)
|
||||
|
||||
# --- MAAS preseed ---------------------------------------------------------------
|
||||
preseed: $(PRESEED)
|
||||
|
||||
$(PRESEED): maas/curtin_userdata_custom.in
|
||||
@mkdir -p $(WORKDIR)
|
||||
@sed -e 's|@@IMAGE_NAME@@|$(IMAGE_NAME)|g' \
|
||||
-e 's|@@ARCH@@|$(ARCH)|g' \
|
||||
$< > $@
|
||||
@echo "preseed: $@"
|
||||
@echo " -> MAAS region controller uzerinde $(MAAS_PRESEED_DIR)/ altina kopyalayin"
|
||||
|
||||
install-preseed: $(PRESEED)
|
||||
@if [ "$$(id -u)" -ne 0 ]; then echo "HATA: root gerekir"; exit 1; fi
|
||||
install -D -m 0644 $(PRESEED) $(MAAS_PRESEED_DIR)/$(notdir $(PRESEED))
|
||||
@echo "kuruldu: $(MAAS_PRESEED_DIR)/$(notdir $(PRESEED))"
|
||||
|
||||
# --- MAAS'a yukleme -------------------------------------------------------------
|
||||
upload:
|
||||
@test -f $(OUTPUT) || { echo "HATA: $(OUTPUT) yok, once 'make image'"; exit 1; }
|
||||
maas $(MAAS_PROFILE) boot-resources create \
|
||||
name='$(MAAS_IMAGE_NAME)' \
|
||||
title='$(IMAGE_TITLE)' \
|
||||
architecture='$(MAAS_ARCH)' \
|
||||
filetype='tgz' \
|
||||
content@=$(OUTPUT)
|
||||
|
||||
verify:
|
||||
@test -f $(OUTPUT) || { echo "HATA: $(OUTPUT) yok, once 'make image'"; exit 1; }
|
||||
./scripts/verify-image.sh $(OUTPUT)
|
||||
|
||||
# Depoda hangi surumler var, elimizdeki imaj hangi surumde?
|
||||
check-upstream:
|
||||
@echo "==> Depoda mevcut ($(PVE_REPO_URI) $(DEBIAN_SERIES) $(PVE_REPO)):"
|
||||
@curl -fsS $(PVE_REPO_URI)/dists/$(DEBIAN_SERIES)/$(PVE_REPO)/binary-$(ARCH)/Packages.gz 2>/dev/null \
|
||||
| gunzip \
|
||||
| awk '/^Package: (proxmox-ve|pve-manager|proxmox-default-kernel)$$/{p=$$2; next} \
|
||||
/^Version: /{if(p!=""){print p" "$$2; p=""}}' \
|
||||
| sort -V | awk '{v[$$1]=$$2} END{for(k in v) printf " %-24s %s\n", k, v[k]}' \
|
||||
|| echo " (depoya erisilemedi)"
|
||||
@echo "==> Yerel imajda ($(OUTPUT)):"
|
||||
@if [ -f $(OUTPUT) ]; then \
|
||||
tar xzf $(OUTPUT) -O ./etc/pve-maas/image-info 2>/dev/null | sed 's/^/ /' \
|
||||
|| echo " (image-info okunamadi - imaj bu ozellikten once mi derlendi?)"; \
|
||||
else echo " (imaj yok - once 'make image')"; fi
|
||||
|
||||
# Yerel APT onbellegi - tekrarlanan derlemelerde ~700MB indirmeyi ortadan kaldirir.
|
||||
# Kurduktan sonra: sudo make image APT_PROXY=http://10.0.2.2:3142
|
||||
deps-cache:
|
||||
@if [ "$$(id -u)" -ne 0 ]; then echo "HATA: root gerekir (sudo make deps-cache)"; exit 1; fi
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y apt-cacher-ng
|
||||
systemctl enable --now apt-cacher-ng
|
||||
@echo
|
||||
@echo "Hazir. Derlemede kullanmak icin:"
|
||||
@echo " sudo make image APT_PROXY=http://10.0.2.2:3142"
|
||||
@echo "(10.0.2.2 = packer user-mode aginda build host'un adresi)"
|
||||
|
||||
lint:
|
||||
@bash -n scripts/customize-proxmox.sh.in && echo "customize-proxmox.sh.in: OK"
|
||||
@bash -n overlay/usr/local/sbin/pve-maas-init && echo "pve-maas-init: OK"
|
||||
@command -v shellcheck >/dev/null && shellcheck -S warning \
|
||||
overlay/usr/local/sbin/pve-maas-init scripts/install-deps.sh || true
|
||||
|
||||
clean:
|
||||
rm -f $(OVERLAY_TGZ) $(CUSTOMIZE) $(PRESEED)
|
||||
rm -rf $(TPL)/output-cloudimg $(TPL)/seeds-cloudimg.iso \
|
||||
$(TPL)/OVMF_CODE.fd $(TPL)/OVMF_VARS.fd
|
||||
|
||||
distclean:
|
||||
rm -rf $(WORKDIR)
|
||||
824
README.md
Normal file
824
README.md
Normal file
@@ -0,0 +1,824 @@
|
||||
# maas-proxmox
|
||||
|
||||
Build **Proxmox VE** images that [MAAS](https://maas.io) can deploy to bare metal, with
|
||||
first-boot automation that configures the node and joins it to a Proxmox cluster —
|
||||
without anyone logging in.
|
||||
|
||||
Target: **Proxmox VE 9.x** on Debian 13 "Trixie". Older and newer releases are a
|
||||
variable change away (see [Moving to a new Proxmox release](#moving-to-a-new-proxmox-release)).
|
||||
|
||||
*Türkçe dokümantasyon: [README.tr.md](README.tr.md)*
|
||||
|
||||
---
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [What this does](#what-this-does)
|
||||
- [Why Debian + the Proxmox repository, and not the Proxmox ISO](#why-debian--the-proxmox-repository-and-not-the-proxmox-iso)
|
||||
- [How it works](#how-it-works)
|
||||
- [Requirements](#requirements)
|
||||
- [Quick start](#quick-start)
|
||||
- [Build configuration](#build-configuration)
|
||||
- [First-boot behaviour](#first-boot-behaviour)
|
||||
- [Configuration reference](#configuration-reference)
|
||||
- [Deploying a cluster](#deploying-a-cluster)
|
||||
- [Networking](#networking)
|
||||
- [Storage](#storage)
|
||||
- [Four traps this image works around](#four-traps-this-image-works-around)
|
||||
- [Moving to a new Proxmox release](#moving-to-a-new-proxmox-release)
|
||||
- [Build performance](#build-performance)
|
||||
- [Repository layout](#repository-layout)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Verified status](#verified-status)
|
||||
- [Credits and licensing](#credits-and-licensing)
|
||||
|
||||
---
|
||||
|
||||
## What this does
|
||||
|
||||
MAAS provisions bare-metal machines from images. It ships images for Ubuntu and a few
|
||||
other distributions, but not for Proxmox VE. This repository produces one.
|
||||
|
||||
`make image` builds a tarball that you upload to MAAS as a custom image. After that,
|
||||
deploying a machine in MAAS gives you a fully configured Proxmox VE node:
|
||||
|
||||
| Step | Who does it |
|
||||
|---|---|
|
||||
| Partition the disk, write the image, configure networking, inject SSH keys | MAAS / curtin |
|
||||
| Set hostname, regenerate node-unique identifiers | cloud-init + this image |
|
||||
| Convert the management interface into a `vmbr0` bridge | this image |
|
||||
| Set the `root@pam` password | this image |
|
||||
| Create an LVM-thin pool and register it as `local-lvm` | this image |
|
||||
| Create a Proxmox cluster, or join an existing one | this image |
|
||||
|
||||
Everything after the MAAS handoff is driven by a single systemd service,
|
||||
`pve-maas-init`, configured through cloud-init user-data supplied at deploy time.
|
||||
The image itself carries no cluster identity, no hostname and no credentials, so the
|
||||
same image can build any number of independent clusters.
|
||||
|
||||
---
|
||||
|
||||
## Why Debian + the Proxmox repository, and not the Proxmox ISO
|
||||
|
||||
There are two plausible ways to get Proxmox VE into MAAS, and the choice shapes
|
||||
everything else.
|
||||
|
||||
**Option A — install the Proxmox ISO under automation, capture the raw disk (`.dd.gz`).**
|
||||
You get Proxmox's native layout: ZFS-on-root if you want it, `local-lvm` preconfigured.
|
||||
But MAAS writes a `dd` image to the disk verbatim. Its storage layouts, its partitioning
|
||||
UI and much of the curtin flow stop applying. Proxmox does not ship cloud-init on the
|
||||
host, so MAAS's metadata — hostname, network configuration, SSH keys, user-data —
|
||||
has to be bolted on afterwards. Growing the root filesystem becomes your problem.
|
||||
|
||||
**Option B — start from the Debian cloud image and install `proxmox-ve` on top of it.**
|
||||
This is a
|
||||
[configuration Proxmox supports and documents](https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_13_Trixie).
|
||||
The result is an ordinary Debian root filesystem, so MAAS's normal `tgz` custom-image
|
||||
path applies unchanged: MAAS partitions the disk according to whatever storage layout
|
||||
you configured, curtin installs the bootloader, and cloud-init consumes MAAS metadata
|
||||
the way it does for Ubuntu.
|
||||
|
||||
**This repository takes option B.** The cost is that `local-lvm` is not preconfigured —
|
||||
the image creates the thin pool on first boot from whatever free space MAAS left in the
|
||||
volume group — and ZFS-on-root is not available. In exchange, MAAS stays in control of
|
||||
the parts MAAS is good at, and moving between Proxmox releases is a variable change
|
||||
rather than a rewrite.
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
### Build pipeline
|
||||
|
||||
```
|
||||
Debian 13 cloud image (qcow2, official)
|
||||
│
|
||||
├─ canonical/packer-maas, "debian" template (QEMU + KVM)
|
||||
│ ├─ cloud-init / netplan / curtin compatibility [upstream]
|
||||
│ ├─ disk, cpu and memory patch (4G -> 16G) [this repo]
|
||||
│ └─ customize-proxmox.sh [this repo]
|
||||
│ ├─ add the Proxmox APT repository and keyring
|
||||
│ ├─ install proxmox-default-kernel and proxmox-ve
|
||||
│ ├─ remove the Debian kernel and os-prober
|
||||
│ ├─ wipe the pmxcfs node identity
|
||||
│ ├─ disable networking.service, clear interfaces{,.new}
|
||||
│ └─ install the overlay:
|
||||
│ /usr/local/sbin/pve-maas-init
|
||||
│ /etc/pve-maas/pve-maas.conf
|
||||
│ /etc/systemd/system/pve-maas-init.service
|
||||
│ /curtin/curtin-hooks
|
||||
│
|
||||
└─ proxmox-ve-9.tar.gz ──► maas boot-resources create
|
||||
```
|
||||
|
||||
The upstream [`canonical/packer-maas`](https://github.com/canonical/packer-maas)
|
||||
repository is cloned at build time and pinned to a tested commit. It is *not* vendored;
|
||||
this repository applies a small, explicit patch to its Packer template and supplies the
|
||||
customization script through the template's `customize_script` variable.
|
||||
|
||||
### Deployment flow
|
||||
|
||||
```
|
||||
MAAS: Deploy (osystem=custom, distro_series=proxmox-ve-9, user_data=…)
|
||||
│
|
||||
├─ PXE boot into the ephemeral environment
|
||||
├─ curtin: partition, extract the image
|
||||
├─ curtin: /curtin/curtin-hooks ── skip kernel install
|
||||
│ └─ pin interface names by MAC
|
||||
├─ reboot into Proxmox VE
|
||||
│
|
||||
└─ first boot
|
||||
├─ cloud-init: hostname, network (netplan), SSH keys,
|
||||
│ write /etc/pve-maas/conf.d/*.conf from user-data
|
||||
└─ pve-maas-init.service
|
||||
hosts → identity → rootpw → network → cluster → storage
|
||||
```
|
||||
|
||||
`pve-maas-init` is a state machine. Each stage records completion in
|
||||
`/var/lib/pve-maas/<stage>.done`, so a stage that reboots the node (the network stage
|
||||
does, by default) resumes at the next stage on the following boot, and a stage that
|
||||
fails is retried on the next boot rather than leaving the node half-configured.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
**Build host** — where `make image` runs:
|
||||
|
||||
- Ubuntu 22.04 or newer (24.04 LTS is what this was developed and tested on), x86_64
|
||||
- **Access to `/dev/kvm`.** If the build host is itself a virtual machine, nested
|
||||
virtualization must be enabled and the CPU type must pass the virtualization flags
|
||||
through (on Proxmox: `--cpu host`). Without KVM the build falls back to full
|
||||
emulation and is not practical.
|
||||
- 4+ vCPU, 8+ GB RAM, 25+ GB free disk
|
||||
- `sudo` (the build runs as root — it uses `qemu-nbd`, FUSE mounts and `tar --xattrs`)
|
||||
|
||||
**Deployment side:**
|
||||
|
||||
- MAAS 3.2 or newer (custom image support); tested on 3.7.2
|
||||
- curtin 21.0 or newer
|
||||
- The curtin preseed from this repository installed on the MAAS region controller
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1. Install build dependencies (packer, qemu, ovmf, nbdkit, fuse2fs, …)
|
||||
sudo ./scripts/install-deps.sh
|
||||
|
||||
# 2. Build the image (~11 minutes)
|
||||
sudo make image
|
||||
# -> build/proxmox-ve-9.tar.gz
|
||||
|
||||
# 3. Check the result
|
||||
make verify
|
||||
|
||||
# 4. Generate the MAAS curtin preseed
|
||||
make preseed
|
||||
# -> build/curtin_userdata_custom_amd64_generic_proxmox-ve-9
|
||||
|
||||
# 5. Install the preseed on the MAAS region controller
|
||||
sudo make install-preseed
|
||||
# default: /var/snap/maas/current/preseeds/
|
||||
# for a deb-packaged MAAS: MAAS_PRESEED_DIR=/etc/maas/preseeds
|
||||
|
||||
# 6. Upload the image to MAAS
|
||||
make upload MAAS_PROFILE=admin
|
||||
```
|
||||
|
||||
Steps 5 and 6 run against the MAAS region controller, so either run them there or copy
|
||||
the two artifacts across.
|
||||
|
||||
**The preseed is not optional.** Without it, curtin tries to install a kernel over APT
|
||||
during deployment and the deployment fails. See
|
||||
[Four traps this image works around](#four-traps-this-image-works-around).
|
||||
|
||||
Then deploy a machine:
|
||||
|
||||
```bash
|
||||
maas $PROFILE machine deploy $SYSTEM_ID \
|
||||
osystem=custom distro_series=proxmox-ve-9 \
|
||||
user_data="$(base64 -w0 maas/examples/01-first-node.yaml)"
|
||||
```
|
||||
|
||||
…or pick **Custom → Proxmox VE 9** in the MAAS web UI. For a whole cluster, use
|
||||
[`scripts/deploy-cluster.sh`](scripts/deploy-cluster.sh).
|
||||
|
||||
---
|
||||
|
||||
## Build configuration
|
||||
|
||||
All of these are `make` variables — `sudo make image DISK_SIZE=24G`, and so on.
|
||||
|
||||
### Version selection
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_VERSION` | `9` | Proxmox VE major version; used in the image name |
|
||||
| `DEBIAN_SERIES` | `trixie` | Debian codename that Proxmox release is built on |
|
||||
| `DEBIAN_VERSION` | `13` | Debian major version number |
|
||||
| `PVE_REPO` | `pve-no-subscription` | `pve-no-subscription`, `pve-enterprise` or `pve-test` |
|
||||
| `PVE_REPO_URI` | `http://download.proxmox.com/debian/pve` | APT repository URI |
|
||||
| `PVE_KEYRING_URL` | derived from `DEBIAN_SERIES` | Proxmox archive keyring |
|
||||
| `PVE_EXTRA_PACKAGES` | `ifupdown2 open-iscsi chrony …` | Extra packages to bake in |
|
||||
|
||||
> `vlan` and `vzdump` **conflict** with `pve-manager` — do not add them.
|
||||
> ifupdown2 provides VLAN support natively.
|
||||
|
||||
### Image and build
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `IMAGE_NAME` | `proxmox-ve-9` | MAAS name (`custom/<name>`) and preseed filename |
|
||||
| `ARCH` / `SUBARCH` | `amd64` / `generic` | Target architecture |
|
||||
| `BOOT` | `uefi` | Boot mode baked into the image |
|
||||
| `DISK_SIZE` | `16G` | Build VM disk. Upstream's 4G cannot fit Debian + Proxmox |
|
||||
| `BUILD_CPUS` / `BUILD_MEM` | `4` / `4096` | Build VM resources |
|
||||
| `TIMEOUT` | `3h` | Packer build timeout |
|
||||
| `PM_REF` | pinned SHA | `canonical/packer-maas` revision |
|
||||
|
||||
### Speed
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `DEBIAN_IMAGE_CHANNEL` | `stable` | `stable` uses a fixed URL so Packer's cache works; `daily` is upstream's default and changes every day |
|
||||
| `GZIP_LEVEL` | `6` | Tarball compression. Upstream uses 9 |
|
||||
| `APT_PROXY` | *(empty)* | Local APT cache, e.g. `http://10.0.2.2:3142` — see `make deps-cache` |
|
||||
|
||||
### MAAS
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `MAAS_PROFILE` | `admin` | `maas` CLI profile name |
|
||||
| `MAAS_PRESEED_DIR` | `/var/snap/maas/current/preseeds` | Preseed directory |
|
||||
|
||||
### Targets
|
||||
|
||||
```
|
||||
make deps install build dependencies (root)
|
||||
make deps-cache install a local APT cache to speed up rebuilds (root)
|
||||
make image build the image (root)
|
||||
make verify check the built image's contents
|
||||
make check-upstream compare repository versions against the built image
|
||||
make preseed generate the MAAS curtin preseed
|
||||
make install-preseed install the preseed on this host (root)
|
||||
make upload upload the image to MAAS
|
||||
make lint syntax-check the scripts
|
||||
make clean remove intermediates
|
||||
make distclean remove build/ entirely
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First-boot behaviour
|
||||
|
||||
`pve-maas-init.service` is enabled in the image and runs the following stages in order,
|
||||
each exactly once:
|
||||
|
||||
| Stage | What it does |
|
||||
|---|---|
|
||||
| `hosts` | Writes `<management-ip> <fqdn> <hostname>` into `/etc/hosts` — `pvecm` requires the node name to resolve to a real address. Disables cloud-init's `manage_etc_hosts` so it is not reverted on later boots, fixes postfix's `myhostname`, and restarts the Proxmox services so they pick up the correct identity. |
|
||||
| `identity` | Regenerates node-unique identifiers that must not be shared between nodes cloned from one image — currently the iSCSI initiator name. |
|
||||
| `rootpw` | Sets the `root@pam` password. Without it the Proxmox web UI cannot be used and the node cannot be a join target for other nodes. |
|
||||
| `network` | Converts the interface MAAS configured into a `vmbr0` bridge, disables the netplan/systemd-networkd configuration, and enables ifupdown2. |
|
||||
| `cluster` | Creates a cluster or joins an existing one. |
|
||||
| `storage` | Creates an LVM-thin pool in the free space of the volume group and registers it as `local-lvm`. |
|
||||
|
||||
Progress and failures:
|
||||
|
||||
```bash
|
||||
systemctl status pve-maas-init
|
||||
journalctl -u pve-maas-init -b
|
||||
ls -l /var/lib/pve-maas/ # <stage>.done files; "complete" when finished
|
||||
cat /etc/pve-maas/image-info # which image this node was built from
|
||||
```
|
||||
|
||||
If a stage fails, the service exits non-zero and the remaining stages are retried on the
|
||||
next boot. To force a stage to run again, delete its `.done` file along with `complete`
|
||||
and restart the service.
|
||||
|
||||
---
|
||||
|
||||
## Configuration reference
|
||||
|
||||
Defaults live in `/etc/pve-maas/pve-maas.conf` (do not edit it). Per-node settings go
|
||||
into `/etc/pve-maas/conf.d/*.conf`, which cloud-init writes from the user-data you pass
|
||||
at deploy time. Files in `conf.d` override the defaults.
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
PVE_ROOT_PASSWORD_HASH='$6$...'
|
||||
PVE_CLUSTER_MODE=join
|
||||
PVE_CLUSTER_PEER=192.0.2.11
|
||||
PVE_CLUSTER_PEER_PASSWORD='...'
|
||||
```
|
||||
|
||||
### General
|
||||
|
||||
| Option | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_ENABLED` | `true` | Set to `false` to disable all first-boot automation |
|
||||
| `PVE_FQDN` | *(empty)* | Override the detected FQDN |
|
||||
|
||||
### Credentials
|
||||
|
||||
| Option | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_ROOT_PASSWORD_HASH` | *(empty)* | `root@pam` password hash — generate with `openssl passwd -6` |
|
||||
| `PVE_ROOT_PASSWORD` | *(empty)* | Plaintext alternative; prefer the hash |
|
||||
|
||||
### Networking
|
||||
|
||||
| Option | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_NET_MANAGE` | `true` | Set to `false` to configure `/etc/network/interfaces` yourself |
|
||||
| `PVE_NET_BRIDGE` | `vmbr0` | Bridge name |
|
||||
| `PVE_NET_UPLINK` | *(auto)* | Bridge port; defaults to the interface holding the default route |
|
||||
| `PVE_NET_MODE` | `auto` | `auto`, `static` or `dhcp` |
|
||||
| `PVE_NET_APPLY` | `reboot` | `reboot`, `reload` (`ifreload -a`) or `none` |
|
||||
| `PVE_NET_VLAN_AWARE` | `false` | Make the bridge VLAN-aware (`bridge-vids 2-4094`) |
|
||||
| `PVE_NET_EXTRA` | *(empty)* | Raw text appended to `/etc/network/interfaces` |
|
||||
|
||||
### Cluster
|
||||
|
||||
| Option | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_CLUSTER_MODE` | `none` | `none`, `create` or `join` |
|
||||
| `PVE_CLUSTER_NAME` | *(empty)* | Cluster name, for `create` |
|
||||
| `PVE_CLUSTER_PEER` | *(empty)* | Address of an existing member, for `join` |
|
||||
| `PVE_CLUSTER_PEER_PASSWORD` | *(empty)* | That node's `root@pam` password |
|
||||
| `PVE_CLUSTER_PEER_PASSWORD_FILE` | *(empty)* | Read the password from a file instead |
|
||||
| `PVE_CLUSTER_FINGERPRINT` | *(empty)* | The peer's certificate SHA-256 fingerprint |
|
||||
| `PVE_CLUSTER_FINGERPRINT_DISCOVER` | `true` | Read the fingerprint from the peer if not supplied (trust on first use) |
|
||||
| `PVE_CLUSTER_LINK0` / `LINK1` | *(empty)* | This node's corosync link addresses |
|
||||
| `PVE_CLUSTER_NODEID` / `VOTES` | *(empty)* | Passed through to Proxmox |
|
||||
| `PVE_CLUSTER_WAIT` | `900` | Seconds to wait for the peer's API to answer |
|
||||
| `PVE_CLUSTER_RETRIES` | `5` | Join attempts, 30 s apart |
|
||||
| `PVE_CLUSTER_WIPE_SECRETS` | `true` | Scrub passwords from `conf.d` after joining |
|
||||
|
||||
### Storage
|
||||
|
||||
| Option | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `PVE_THINPOOL` | `auto` | `auto` (the VG with the most free space), `off`, or a VG name |
|
||||
| `PVE_THINPOOL_NAME` | `data` | Thin pool logical volume name |
|
||||
| `PVE_THINPOOL_STORAGE` | `local-lvm` | Proxmox storage id |
|
||||
| `PVE_THINPOOL_MIN_GB` | `16` | Skip the stage below this much free space |
|
||||
| `PVE_THINPOOL_DISK` | *(empty)* | Build a new VG from this whole disk instead |
|
||||
| `PVE_THINPOOL_VG` | `pve` | VG name used with `PVE_THINPOOL_DISK` |
|
||||
|
||||
Worked examples: [`maas/examples/`](maas/examples/).
|
||||
|
||||
---
|
||||
|
||||
## Deploying a cluster
|
||||
|
||||
`pvecm add` prompts for a password interactively, which makes it unusable from a script.
|
||||
This image uses the API endpoint that does the same job non-interactively:
|
||||
|
||||
```bash
|
||||
pvesh create /cluster/config/join --hostname <peer> --password <pw> --fingerprint <fp>
|
||||
```
|
||||
|
||||
### By hand
|
||||
|
||||
Deploy the first node with `PVE_CLUSTER_MODE=create`
|
||||
([example](maas/examples/01-first-node.yaml)), then read its certificate fingerprint:
|
||||
|
||||
```bash
|
||||
openssl s_client -connect <first-node>:8006 </dev/null 2>/dev/null \
|
||||
| openssl x509 -noout -fingerprint -sha256 | cut -d= -f2
|
||||
```
|
||||
|
||||
and deploy the remaining nodes with `PVE_CLUSTER_MODE=join` plus that fingerprint
|
||||
([example](maas/examples/02-join-node.yaml)).
|
||||
|
||||
A joining node waits for the peer's port 8006 to answer (`PVE_CLUSTER_WAIT`, 15 minutes
|
||||
by default) and retries a failed join five times, so you can start several deployments at
|
||||
once without ordering them carefully.
|
||||
|
||||
### With the helper script
|
||||
|
||||
[`scripts/deploy-cluster.sh`](scripts/deploy-cluster.sh) automates the whole flow: it
|
||||
deploys the first node, waits for it, reads the fingerprint, and deploys the rest.
|
||||
|
||||
Run it **on the MAAS region controller** — it needs the `maas` CLI profile and network
|
||||
access to the nodes' port 8006.
|
||||
|
||||
```bash
|
||||
# Show what it would deploy, without deploying anything
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3 --dry-run
|
||||
|
||||
# Deploy
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3
|
||||
```
|
||||
|
||||
The first host in `--nodes` creates the cluster; the rest join it.
|
||||
|
||||
### Several independent clusters
|
||||
|
||||
The image carries no cluster identity — it comes entirely from deploy-time user-data.
|
||||
The same image and the same preseed can build any number of unrelated clusters, with no
|
||||
rebuild:
|
||||
|
||||
```bash
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3
|
||||
./deploy-cluster.sh --name pve-dr --nodes dr1,dr2,dr3
|
||||
```
|
||||
|
||||
Each cluster gets its own root password (generated and printed if you do not supply one).
|
||||
MAAS tags are a convenient way to keep the groups apart:
|
||||
|
||||
```bash
|
||||
maas $PROFILE tag create name=pve-prod
|
||||
maas $PROFILE tag update-nodes pve-prod add=$SYSTEM_ID
|
||||
maas $PROFILE machines read tags=pve-prod | jq -r '.[].hostname'
|
||||
```
|
||||
|
||||
### Security notes
|
||||
|
||||
- The peer's `root@pam` password **must be plaintext** — the join API does not accept a
|
||||
hash. It is stored in MAAS user-data, where anyone with MAAS access can read it. Use a
|
||||
short-lived password and change it after the cluster is up.
|
||||
- Without `PVE_CLUSTER_FINGERPRINT` the fingerprint is read from the peer on first
|
||||
contact, which is trust-on-first-use and open to interception. Supply it explicitly in
|
||||
production; `deploy-cluster.sh` always does.
|
||||
- Prefer `PVE_ROOT_PASSWORD_HASH` over `PVE_ROOT_PASSWORD` for the node's own password.
|
||||
- With no `PVE_ROOT_PASSWORD*` at all, root stays locked: no `root@pam` web login, and
|
||||
the node cannot be a join target.
|
||||
- A two-node cluster loses quorum when either node goes down. Use three nodes or a
|
||||
QDevice in production — that is a Proxmox property, not a limitation of this image.
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
### Who owns the network, and when
|
||||
|
||||
The image ships with `networking.service` (ifupdown2) **disabled** and an
|
||||
`/etc/network/interfaces` containing nothing but loopback. On the first boot MAAS owns
|
||||
the network entirely, through the netplan configuration curtin wrote and
|
||||
systemd-networkd. Only once `pve-maas-init` reaches its network stage does it write the
|
||||
`vmbr0` configuration, disable netplan and networkd, and enable ifupdown2.
|
||||
|
||||
That order matters. If ifupdown2 were enabled in the image it would start with the stale
|
||||
interface definition baked in at build time and take the real interface down before the
|
||||
automation ever ran.
|
||||
|
||||
### The bridge
|
||||
|
||||
The network stage reads the live configuration — the interface holding the default
|
||||
route, its address, gateway and DNS — and writes a Proxmox-style bridge:
|
||||
|
||||
```
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
iface enp6s18 inet manual
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address 192.0.2.20/24
|
||||
gateway 192.0.2.1
|
||||
bridge-ports enp6s18
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
|
||||
source /etc/network/interfaces.d/*
|
||||
```
|
||||
|
||||
`PVE_NET_APPLY=reboot` (the default) reboots the node once to apply this, after
|
||||
cloud-init has already told MAAS the deployment succeeded. `reload` uses ifupdown2's
|
||||
`ifreload -a` and avoids the reboot at the cost of a brief interruption.
|
||||
|
||||
### Complex topologies
|
||||
|
||||
For bonds, VLANs or multiple bridges, set `PVE_NET_MANAGE=false` and write
|
||||
`/etc/network/interfaces` yourself through cloud-init — see
|
||||
[`maas/examples/04-advanced-network.yaml`](maas/examples/04-advanced-network.yaml).
|
||||
|
||||
---
|
||||
|
||||
## Storage
|
||||
|
||||
`local` (directory storage on `/var/lib/vz`) works out of the box. `local-lvm` is
|
||||
created on first boot from free space in the volume group, which means **MAAS has to
|
||||
leave some**.
|
||||
|
||||
Choose the LVM storage layout in MAAS and make the root logical volume smaller than the
|
||||
disk:
|
||||
|
||||
```bash
|
||||
maas $PROFILE machine set-storage-layout $SYSTEM_ID \
|
||||
storage_layout=lvm lv_size=12884901888 # 12 GiB root
|
||||
```
|
||||
|
||||
The rest of the volume group is then turned into a thin pool named `data` and registered
|
||||
as `local-lvm`. With less than `PVE_THINPOOL_MIN_GB` free the stage is skipped silently
|
||||
and the node runs with directory storage only.
|
||||
|
||||
To use a whole separate disk instead:
|
||||
|
||||
```
|
||||
PVE_THINPOOL_DISK=/dev/sdb
|
||||
PVE_THINPOOL_VG=pve
|
||||
```
|
||||
|
||||
`storage.cfg` is cluster-wide, so a node joining an existing cluster does not overwrite a
|
||||
`local-lvm` that is already defined — it adds itself to that storage's node list instead.
|
||||
|
||||
---
|
||||
|
||||
## Four traps this image works around
|
||||
|
||||
Each of these fails **silently**: no error, no failed unit, just a node that does not
|
||||
work. They are documented here because anyone building a similar image will hit them.
|
||||
|
||||
### 1. `kernel: null` in the curtin preseed
|
||||
|
||||
The image already contains the Proxmox kernel, so curtin should not install one. Recent
|
||||
curtin supports `kernel: null` for exactly this. The curtin that ships with MAAS may not:
|
||||
|
||||
```
|
||||
install_kernel -> config.merge_config(mapping, kernel_cfg.get('mapping', {}))
|
||||
AttributeError: 'NoneType' object has no attribute 'get'
|
||||
```
|
||||
|
||||
Instead, the image ships `/curtin/curtin-hooks`. When that file exists, curtin runs it in
|
||||
place of its built-in hooks; the hook neutralises the kernel-installation step and calls
|
||||
the built-in hooks itself. This works regardless of curtin version and removes the
|
||||
deployment's dependency on reaching `download.proxmox.com`.
|
||||
|
||||
### 2. `pvenetcommit.service`
|
||||
|
||||
Proxmox stages network changes in `/etc/network/interfaces.new`, and `pvenetcommit`
|
||||
**moves that file over `interfaces` on every boot**, before `sysinit.target`:
|
||||
|
||||
```
|
||||
ExecStart=sh -c 'if [ -f ${FN}.new ]; then mv ${FN}.new ${FN}; fi'
|
||||
```
|
||||
|
||||
A `.new` file created during the build and left in the image silently overwrites the
|
||||
node's configuration on its first boot. It is removed at build time, and again after the
|
||||
network stage writes the real configuration.
|
||||
|
||||
### 3. Interface names differ between commissioning and deployment
|
||||
|
||||
MAAS commissions machines in an Ubuntu ephemeral environment and records the interface
|
||||
name it sees there — for example `enp6s18`. The deployed Debian 13 system may use a
|
||||
different udev naming scheme and call the same card `ens18`. cloud-init then tries to
|
||||
rename it, fails, and **leaves the interface down**:
|
||||
|
||||
```
|
||||
Failed to rename devices: [busy] Error renaming mac=… from ens18 to enp6s18
|
||||
```
|
||||
|
||||
The node loses its network moments after cloud-init fetches its metadata — late enough
|
||||
that MAAS still reports a successful deployment.
|
||||
|
||||
`curtin-hooks` writes the MAC-to-name mapping from MAAS's own network configuration into
|
||||
`/etc/systemd/network/10-maas-<name>.link`, so udev names the card correctly from the
|
||||
start and no rename is attempted.
|
||||
|
||||
### 4. systemd ordering — both a cycle and a deadlock
|
||||
|
||||
Ordering `pve-maas-init.service` `After=cloud-final.service` while it is
|
||||
`WantedBy=multi-user.target` creates a dependency cycle. systemd resolves it by
|
||||
**deleting our job**:
|
||||
|
||||
```
|
||||
multi-user.target: Found ordering cycle on pve-maas-init.service/start
|
||||
multi-user.target: Found dependency on cloud-final.service/start
|
||||
multi-user.target: Found dependency on multi-user.target/start
|
||||
Job pve-maas-init.service/start deleted to break ordering cycle
|
||||
```
|
||||
|
||||
The service never runs, and nothing reports an error.
|
||||
|
||||
Waiting for cloud-init inside the script instead removes the cycle but introduces a
|
||||
runtime deadlock if the unit is `Type=oneshot`, because such a unit blocks
|
||||
`multi-user.target`, which `cloud-final.service` is ordered after:
|
||||
|
||||
```
|
||||
cloud-final.service waiting
|
||||
pve-maas-init.service running
|
||||
multi-user.target waiting
|
||||
```
|
||||
|
||||
The unit is therefore `Type=simple`, with no cloud-init ordering at all, and the script
|
||||
calls `cloud-init status --wait` itself. Stage tracking uses `/var/lib/pve-maas/*.done`
|
||||
rather than systemd state, so nothing is lost by dropping `RemainAfterExit`.
|
||||
|
||||
---
|
||||
|
||||
## Moving to a new Proxmox release
|
||||
|
||||
Check what is available without building anything:
|
||||
|
||||
```bash
|
||||
make check-upstream
|
||||
```
|
||||
|
||||
It prints the current `proxmox-ve` / `pve-manager` versions in the configured repository
|
||||
next to the metadata of the image you have (`/etc/pve-maas/image-info`).
|
||||
|
||||
### Within the same Debian base (9.2 → 9.3 → …)
|
||||
|
||||
Nothing to change. No package versions are pinned; every build takes the current
|
||||
`proxmox-ve`:
|
||||
|
||||
```bash
|
||||
sudo make image && make verify && make upload
|
||||
```
|
||||
|
||||
The image name and preseed filename stay the same, so the MAAS record is updated in
|
||||
place.
|
||||
|
||||
### A major release that changes the Debian base (e.g. PVE 10 on Debian 14)
|
||||
|
||||
**This is not automatic**, and changing the variables in this repository is not enough.
|
||||
The upstream packer-maas template contains hard-coded conditions on the Debian version:
|
||||
|
||||
```
|
||||
debian/scripts/networking.sh: if [ ${DEBIAN_VERSION} == '12' ] || [ ${DEBIAN_VERSION} == '13' ]
|
||||
debian/scripts/setup-boot.sh: if [ ${DEBIAN_VERSION} == '13' ]
|
||||
```
|
||||
|
||||
An unknown version falls through to the `else` branch, which installs a cloud-init
|
||||
package from 2020 — the image breaks silently. A major jump therefore waits on upstream
|
||||
support. In order:
|
||||
|
||||
1. Does `canonical/packer-maas` handle the new Debian? (look at the `DEBIAN_VERSION`
|
||||
conditions in `debian/scripts/`)
|
||||
2. Has Proxmox published the keyring?
|
||||
`https://enterprise.proxmox.com/debian/proxmox-archive-keyring-<suite>.gpg`
|
||||
3. Are the packages there?
|
||||
`curl -s http://download.proxmox.com/debian/pve/dists/<suite>/Release | grep Components`
|
||||
4. Then:
|
||||
|
||||
```bash
|
||||
sudo make image PVE_VERSION=10 DEBIAN_SERIES=forky DEBIAN_VERSION=14 PM_REF=main
|
||||
make preseed PVE_VERSION=10
|
||||
```
|
||||
|
||||
The preseed filename changes too (`…_proxmox-ve-10`) — install it on the region
|
||||
controller.
|
||||
|
||||
### Upstream pinning
|
||||
|
||||
`PM_REF` points at a tested `canonical/packer-maas` commit. Leaving it at `main` means an
|
||||
upstream change can break your build without warning. To move it forward: build with
|
||||
`PM_REF=main`, **deploy the result and verify it**, then record the new SHA in the
|
||||
Makefile.
|
||||
|
||||
---
|
||||
|
||||
## Build performance
|
||||
|
||||
A build takes roughly 11 minutes on a 4-vCPU builder, most of it installing packages
|
||||
inside the build VM. These optimisations are on by default:
|
||||
|
||||
- **`eatmydata`** — drops dpkg's per-package `fsync` calls. Safe here: the build VM's
|
||||
disk is thrown away.
|
||||
- **Deferred initramfs** — `update-initramfs` is diverted during installation and run
|
||||
exactly once at the end, instead of being triggered by the kernel, firmware and dkms
|
||||
packages in turn.
|
||||
- **Stable cloud image** — a fixed URL means Packer's cache actually hits; upstream's
|
||||
`daily` image changes every day, costing a ~350 MB download per build and making
|
||||
builds non-reproducible.
|
||||
- **`GZIP_LEVEL=6`** — upstream uses `--best` (9). With `pigz` this is noticeably faster
|
||||
for a few percent more size.
|
||||
|
||||
For repeated builds, a local APT cache removes about 700 MB of downloads:
|
||||
|
||||
```bash
|
||||
sudo make deps-cache # installs apt-cacher-ng
|
||||
sudo make image APT_PROXY=http://10.0.2.2:3142
|
||||
```
|
||||
|
||||
`10.0.2.2` is the build host as seen from Packer's user-mode network. When a proxy is
|
||||
configured, Debian repositories are rewritten from `https` to `http` so the cache can
|
||||
serve them; package signatures are still verified.
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
Makefile build / preseed / upload targets
|
||||
scripts/install-deps.sh build host dependencies
|
||||
scripts/customize-proxmox.sh.in template for the script that runs inside the build VM
|
||||
scripts/deploy-cluster.sh deploy a whole cluster through MAAS
|
||||
scripts/verify-image.sh check a built image's contents
|
||||
overlay/ files baked into the image
|
||||
usr/local/sbin/pve-maas-init first-boot state machine
|
||||
etc/pve-maas/pve-maas.conf defaults, with every option documented
|
||||
etc/systemd/system/… pve-maas-init.service
|
||||
curtin/curtin-hooks skips kernel install, pins interface names
|
||||
maas/curtin_userdata_custom.in MAAS curtin preseed template
|
||||
maas/examples/*.yaml cloud-init user-data examples
|
||||
build/ generated artifacts (git-ignored)
|
||||
```
|
||||
|
||||
The overlay is embedded into the generated customization script as a base64 payload
|
||||
rather than served over Packer's HTTP server, which keeps the build self-contained.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Deployment ends in "Failed deployment".**
|
||||
Read the installation log:
|
||||
|
||||
```bash
|
||||
maas $PROFILE node-script-result download $SYSTEM_ID current-installation \
|
||||
filetype=txt filters=/tmp/install.log output=all | tail -60
|
||||
```
|
||||
|
||||
The most common cause is a missing or misnamed preseed. The filename must match the
|
||||
uploaded image exactly: `curtin_userdata_custom_<arch>_<subarch>_<image-name>`. Use what
|
||||
`make preseed` produces.
|
||||
|
||||
**MAAS says "Deployed" but the node is unreachable.**
|
||||
Look at the console (`qm terminal <vmid>` on Proxmox, or your BMC). A login prompt means
|
||||
the system booted and the problem is networking. Inspect the disk from rescue mode:
|
||||
|
||||
```bash
|
||||
maas $PROFILE machine rescue-mode $SYSTEM_ID
|
||||
# then, over SSH to the ephemeral environment:
|
||||
mount /dev/vgroot/lvroot /mnt/t
|
||||
cat /mnt/t/etc/network/interfaces # was vmbr0 written?
|
||||
ls -l /mnt/t/var/lib/pve-maas/ # which stages completed?
|
||||
cat /mnt/t/var/log/cloud-init-output.log # did user-data arrive?
|
||||
journalctl -D /mnt/t/var/log/journal -u pve-maas-init
|
||||
```
|
||||
|
||||
An empty `/var/lib/pve-maas/` means the service never ran — check for the systemd
|
||||
ordering cycle described above.
|
||||
|
||||
**Deployed but the web UI rejects the login.**
|
||||
Was a root password supplied? `journalctl -u pve-maas-init -b | grep rootpw`. Check the
|
||||
bridge with `ip -br addr`.
|
||||
|
||||
**A node did not join the cluster.**
|
||||
`journalctl -u pve-maas-init -b`. Common causes: the peer is unreachable on 8006, the
|
||||
root password is wrong, the fingerprint does not match, or the peer's own `/etc/hosts`
|
||||
is wrong so `pvecm` fails there. To retry without redeploying:
|
||||
|
||||
```bash
|
||||
rm -f /var/lib/pve-maas/cluster.done /var/lib/pve-maas/complete
|
||||
systemctl start pve-maas-init
|
||||
```
|
||||
|
||||
**The node reports the build hostname.**
|
||||
It should not — the pmxcfs database is removed from the image and regenerated from the
|
||||
current hostname on first boot. If it happens, `/var/lib/pve-cluster/config.db` survived
|
||||
the build; check the "resetting node identity" step in the build log.
|
||||
|
||||
**The build reports that `proxmox-ve` cannot be found.**
|
||||
`DEBIAN_SERIES` and `PVE_REPO` disagree. Verify:
|
||||
`curl -s http://download.proxmox.com/debian/pve/dists/<suite>/Release | grep Components`
|
||||
|
||||
**Do not delete or replace a boot resource while a machine is using it.**
|
||||
MAAS loses the machine's boot files and it can end up stuck in "Failed to exit rescue
|
||||
mode", with GRUB reporting `invalid magic number`.
|
||||
|
||||
---
|
||||
|
||||
## Verified status
|
||||
|
||||
This repository was tested end to end against live hardware-backed infrastructure:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Image | proxmox-ve 9.2.0 / pve-manager 9.2.11 / kernel 7.0.14-15-pve |
|
||||
| MAAS | 3.7.2 (snap) on Ubuntu 24.04, region + rack |
|
||||
| Deployment | `custom/proxmox-ve-9`, amd64/generic, LVM layout with a 12 GiB root |
|
||||
| Result | Two-node cluster, `Quorate: Yes`, `local-lvm` thin pool on both nodes |
|
||||
| Automation | Bridge conversion, root password, node identity, cluster create and join — all without logging in |
|
||||
|
||||
Not yet exercised: `PVE_CLUSTER_LINK0` (a separate corosync network), the
|
||||
`pve-enterprise` repository, arm64, and `deploy-cluster.sh` beyond `--dry-run`.
|
||||
|
||||
---
|
||||
|
||||
## Credits and licensing
|
||||
|
||||
Built on [`canonical/packer-maas`](https://github.com/canonical/packer-maas) (AGPL-3.0),
|
||||
which is cloned at build time rather than vendored here.
|
||||
|
||||
References:
|
||||
|
||||
- [MAAS — Build custom images](https://canonical.com/maas/docs/how-to-build-custom-images)
|
||||
- [Proxmox VE — Install on Debian 13](https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_13_Trixie)
|
||||
- [Proxmox VE — Cluster Manager](https://pve.proxmox.com/wiki/Cluster_Manager)
|
||||
- [`pvecm(1)`](https://pve.proxmox.com/pve-docs/pvecm.1.html)
|
||||
|
||||
No license has been chosen for this repository yet; add one before expecting others to
|
||||
reuse it.
|
||||
521
README.tr.md
Normal file
521
README.tr.md
Normal file
@@ -0,0 +1,521 @@
|
||||
> **Not:** Kanonik dokumantasyon [README.md](README.md) (Ingilizce). Bu Turkce
|
||||
> surum geride kalmis olabilir.
|
||||
|
||||
# maas-proxmox
|
||||
|
||||
MAAS ile bare-metal sunuculara deploy edilebilen **Proxmox VE** imajlari uretir.
|
||||
Deploy edilen dugum ilk acilista kendini yapilandirir: `vmbr0` koprusunu kurar,
|
||||
`local-lvm` thin havuzunu olusturur ve istenirse bir Proxmox kumesine otomatik katilir.
|
||||
|
||||
Varsayilan hedef: **Proxmox VE 9.x** (Debian 13 "trixie" tabanli).
|
||||
|
||||
---
|
||||
|
||||
## Nasil calisiyor?
|
||||
|
||||
```
|
||||
Debian 13 cloud image (qcow2)
|
||||
│
|
||||
├─ packer-maas / debian sablonu (QEMU + KVM)
|
||||
│ ├─ cloud-init, netplan, curtin uyumlulugu [upstream]
|
||||
│ ├─ disk/cpu/ram yamasi (4G -> 16G) [bu proje]
|
||||
│ └─ customize-proxmox.sh [bu proje]
|
||||
│ ├─ Proxmox APT deposu + anahtarlik
|
||||
│ ├─ proxmox-default-kernel + proxmox-ve
|
||||
│ ├─ Debian cekirdegi / os-prober kaldirilir
|
||||
│ ├─ pmxcfs dugum kimligi sifirlanir
|
||||
│ └─ overlay: pve-maas-init + systemd unit + curtin-hooks
|
||||
│
|
||||
└─ proxmox-ve-9.tar.gz ──► maas boot-resources create
|
||||
│
|
||||
▼
|
||||
MAAS deploy (curtin)
|
||||
│
|
||||
▼
|
||||
pve-maas-init.service (ilk acilis)
|
||||
hosts → identity → rootpw →
|
||||
network → cluster → storage
|
||||
```
|
||||
|
||||
Neden Proxmox ISO'su degil de Debian uzerine kurulum? MAAS imaji disk bolumlemesini,
|
||||
ag yapilandirmasini, kullanici/SSH anahtarlarini ve cloud-init'i **curtin** ile kendisi
|
||||
yonetir. Debian tabanli imaj bu akisa dogal olarak uyar; ISO'dan yakalanan ham disk
|
||||
imaji (`dd.gz`) ise MAAS'in disk duzenini tamamen devre disi birakir.
|
||||
|
||||
---
|
||||
|
||||
## Dogrulanmis durum
|
||||
|
||||
Bu depo canli bir ortamda ucdan uca test edildi:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Imaj | proxmox-ve 9.2.0 / pve-manager 9.2.11 / kernel 7.0.14-15-pve |
|
||||
| MAAS | 3.7.2 (snap), Ubuntu 24.04 region+rack |
|
||||
| Deploy | `custom/proxmox-ve-9`, amd64/generic, LVM duzeni (kok 12GiB) |
|
||||
| Sonuc | 2 dugumlu kume, `Quorate: Yes`, her iki dugumde `local-lvm` thin havuzu |
|
||||
| Otomasyon | vmbr0 donusumu, root parolasi, dugum adi, kume olusturma + katilma - hepsi elle mudahalesiz |
|
||||
|
||||
## Gereksinimler
|
||||
|
||||
**Build host** (imajin derlendigi makine):
|
||||
|
||||
* Ubuntu 22.04+ (24.04 LTS onerilir), x86_64
|
||||
* **Nested virtualization / KVM erisimi** — VM ise CPU tipi `host` olmali
|
||||
* 4+ vCPU, 8+ GB RAM, 25+ GB bos disk
|
||||
* `sudo` yetkisi
|
||||
|
||||
**Deploy tarafi:**
|
||||
|
||||
* MAAS 3.2+ (custom image destegi)
|
||||
* Curtin 21.0+
|
||||
* Region controller uzerinde bu depodaki curtin preseed dosyasi (asagida)
|
||||
|
||||
---
|
||||
|
||||
## Hizli baslangic
|
||||
|
||||
```bash
|
||||
# 1) Build host bagimliliklari (packer, qemu, nbdkit, ovmf, ...)
|
||||
sudo ./scripts/install-deps.sh
|
||||
|
||||
# 2) Imaji derle (~20-40 dk, ag hizina bagli)
|
||||
sudo make image
|
||||
# -> build/proxmox-ve-9.tar.gz
|
||||
|
||||
# 3) MAAS curtin preseed dosyasini uret
|
||||
make preseed
|
||||
# -> build/curtin_userdata_custom_amd64_generic_proxmox-ve-9
|
||||
|
||||
# 4) Preseed'i MAAS region controller'a kopyala
|
||||
sudo make install-preseed
|
||||
# (varsayilan: /var/snap/maas/current/preseeds/ ; deb kurulumunda
|
||||
# MAAS_PRESEED_DIR=/etc/maas/preseeds kullanin)
|
||||
|
||||
# 5) Imaji MAAS'a yukle
|
||||
make upload MAAS_PROFILE=admin
|
||||
```
|
||||
|
||||
Ardindan MAAS arayuzunde makineyi deploy ederken OS olarak
|
||||
**Custom → Proxmox VE 9** secin, ya da CLI ile:
|
||||
|
||||
```bash
|
||||
maas $PROFILE machine deploy $SYSTEM_ID \
|
||||
osystem=custom distro_series=proxmox-ve-9 \
|
||||
user_data="$(base64 -w0 maas/examples/01-first-node.yaml)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Makefile degiskenleri
|
||||
|
||||
| Degisken | Varsayilan | Aciklama |
|
||||
|---|---|---|
|
||||
| `PVE_VERSION` | `9` | Proxmox VE ana surumu (imaj adinda kullanilir) |
|
||||
| `DEBIAN_SERIES` | `trixie` | PVE'nin dayandigi Debian kod adi |
|
||||
| `DEBIAN_VERSION` | `13` | Debian ana surum numarasi |
|
||||
| `PVE_REPO` | `pve-no-subscription` | `pve-no-subscription` / `pve-enterprise` / `pve-test` |
|
||||
| `PVE_EXTRA_PACKAGES` | `ifupdown2 open-iscsi chrony ...` | Imaja eklenecek ek paketler |
|
||||
| `IMAGE_NAME` | `proxmox-ve-9` | MAAS'taki `custom/<ad>` ve preseed dosya adi |
|
||||
| `ARCH` / `BOOT` | `amd64` / `uefi` | Mimari ve onyukleme modu |
|
||||
| `DISK_SIZE` | `16G` | Build VM disk boyutu (upstream sablondaki 4G yetersiz) |
|
||||
| `BUILD_CPUS` / `BUILD_MEM` | `4` / `4096` | Build VM kaynaklari |
|
||||
| `DEBIAN_IMAGE_CHANNEL` | `stable` | `stable` sabit URL kullanir (packer onbellegi isabet eder); `daily` upstream varsayilani |
|
||||
| `GZIP_LEVEL` | `6` | Tarball sikistirma seviyesi (upstream 9; 6 belirgin hizli, imaj ~%2-3 buyuk) |
|
||||
| `APT_PROXY` | *(bos)* | Yerel APT onbellegi, or. `http://10.0.2.2:3142` - bkz. `make deps-cache` |
|
||||
| `PM_REF` | `main` | Kullanilacak `canonical/packer-maas` surumu (dal, etiket ya da commit SHA) |
|
||||
| `MAAS_PROFILE` | `admin` | `maas` CLI profil adi |
|
||||
| `MAAS_PRESEED_DIR` | `/var/snap/maas/current/preseeds` | Preseed dizini |
|
||||
| `TIMEOUT` | `3h` | Packer build zaman asimi |
|
||||
|
||||
### Derlemeyi hizlandirma
|
||||
|
||||
Sure agirlikli olarak VM icindeki paket kurulumunda geciyor. Uygulanan
|
||||
optimizasyonlar (varsayilan olarak acik):
|
||||
|
||||
* **eatmydata** - dpkg'nin her paket icin yaptigi `fsync` cagrilari devre disi.
|
||||
Imaj derlemede guvenli, cunku build VM'inin diski zaten atilabilir.
|
||||
* **initramfs erteleme** - `update-initramfs` kurulum boyunca diverte edilir ve
|
||||
en sonda yalnizca bir kez calistirilir (cekirdek + firmware + dkms
|
||||
tetikleyicileri normalde defalarca calistiriyor).
|
||||
* **Kararli cloud image** - sabit URL sayesinde packer'in onbellegi isabet eder;
|
||||
`daily` her gun degistigi icin her derlemede ~350MB yeniden inerdi.
|
||||
* **`GZIP_LEVEL=6`** - upstream `--best` (9) kullaniyor; 6 ile `pigz` cok
|
||||
cekirdekli calisip belirgin hizlaniyor.
|
||||
|
||||
Tekrarlanan derlemeler icin en buyuk ek kazanc yerel APT onbellegi:
|
||||
|
||||
```bash
|
||||
sudo make deps-cache # apt-cacher-ng kurar
|
||||
sudo make image APT_PROXY=http://10.0.2.2:3142 # ~700MB indirme onbellekten
|
||||
```
|
||||
|
||||
`10.0.2.2`, packer'in user-mode aginda build host'un adresidir. Proxy verildiginde
|
||||
Debian depolari `https` yerine `http` kullanacak sekilde yeniden yazilir (onbellek
|
||||
CONNECT tunelini saklayamaz); paket imzalari yine dogrulandigi icin guvenlik
|
||||
kaybi yoktur.
|
||||
|
||||
### Yeni bir Proxmox surumune gecmek
|
||||
|
||||
Once ne oldugunu gorun - derleme yapmadan, saniyeler icinde:
|
||||
|
||||
```bash
|
||||
make check-upstream
|
||||
```
|
||||
|
||||
Depodaki guncel `proxmox-ve` / `pve-manager` surumlerini, elinizdeki imajin
|
||||
kunyesiyle (`/etc/pve-maas/image-info`) yan yana gosterir.
|
||||
|
||||
**Durum 1 - ayni Debian tabani icinde surum yukseltmesi (9.2 -> 9.3 -> ...)**
|
||||
|
||||
Hicbir sey degistirmeniz gerekmez. Imajda surum sabitlemesi yok; her derleme
|
||||
depodaki en guncel `proxmox-ve` paketini alir:
|
||||
|
||||
```bash
|
||||
sudo make image && make verify && make upload
|
||||
```
|
||||
|
||||
Imaj adi (`proxmox-ve-9`) ve preseed dosyasi ayni kaldigi icin MAAS'taki kayit
|
||||
yerinde guncellenir, preseed'e dokunmaniza gerek kalmaz.
|
||||
|
||||
**Durum 2 - Debian tabanini degistiren ana surum (or. PVE 10 / Debian 14)**
|
||||
|
||||
Bu **otomatik degildir**; bu depodaki degiskenleri degistirmek tek basina yetmez.
|
||||
Sebep: temel aldigimiz `canonical/packer-maas` sablonunun icinde Debian surumune
|
||||
ozel sabit kosullar var -
|
||||
|
||||
```
|
||||
debian/scripts/networking.sh: if [ ${DEBIAN_VERSION} == '12' ] || [ ${DEBIAN_VERSION} == '13' ]
|
||||
debian/scripts/setup-boot.sh: if [ ${DEBIAN_VERSION} == '13' ]
|
||||
```
|
||||
|
||||
Bilinmeyen bir surumde bu kosullar `else` dalina duser ve 2020 tarihli bir
|
||||
cloud-init paketi kurulur; imaj sessizce bozulur. Bu yuzden ana surum gecisi
|
||||
**upstream'in yeni Debian'i desteklemesini bekler**. Sirasi:
|
||||
|
||||
1. `canonical/packer-maas` yeni Debian'i destekliyor mu?
|
||||
(`debian/scripts/` icindeki `DEBIAN_VERSION` kosullarina bakin)
|
||||
2. Proxmox anahtarligi yayinlanmis mi?
|
||||
`https://enterprise.proxmox.com/debian/proxmox-archive-keyring-<suite>.gpg`
|
||||
3. Depoda paketler var mi?
|
||||
`curl -s http://download.proxmox.com/debian/pve/dists/<suite>/Release | grep Components`
|
||||
4. Sonra:
|
||||
|
||||
```bash
|
||||
sudo make image PVE_VERSION=10 DEBIAN_SERIES=forky DEBIAN_VERSION=14 PM_REF=main
|
||||
make preseed PVE_VERSION=10
|
||||
```
|
||||
|
||||
Yeni preseed dosyasinin adi da degisir (`..._proxmox-ve-10`) - MAAS'a kurmayi
|
||||
unutmayin.
|
||||
|
||||
### Upstream surum sabitlemesi
|
||||
|
||||
`PM_REF`, test edilmis bir `canonical/packer-maas` commit'ine sabitlenmistir.
|
||||
`main` birakmak, upstream'de yapilan bir degisikligin derlemenizi haber vermeden
|
||||
bozmasi anlamina gelir. Ileri tasirken `PM_REF=main` ile derleyip **deploy ederek**
|
||||
test edin, sonra yeni SHA'yi Makefile'a yazin.
|
||||
|
||||
---
|
||||
|
||||
## Dugumun ilk acilis davranisi
|
||||
|
||||
Imajda `pve-maas-init.service` etkin gelir. Servis su asamalari sirayla, her birini
|
||||
yalnizca bir kez calistirir (`/var/lib/pve-maas/<asama>.done`):
|
||||
|
||||
| Asama | Ne yapar |
|
||||
|---|---|
|
||||
| `hosts` | `/etc/hosts` icinde `<yonetim-ip> <fqdn> <hostname>` satirini kurar (pvecm bunu sart kosar), postfix `myhostname` degerini duzeltir |
|
||||
| `identity` | Dugume ozel kimlikleri yeniden uretir (iSCSI IQN) |
|
||||
| `rootpw` | `root@pam` parolasini ayarlar |
|
||||
| `network` | MAAS'in yapilandirdigi arayuzu `vmbr0` koprusune donusturur, cloud-init/netplan ag yapilandirmasini devre disi birakir |
|
||||
| `cluster` | Kume olusturur (`create`) veya mevcut kumeye katilir (`join`) |
|
||||
| `storage` | VG'deki bos alanda LVM-thin havuzu acar ve `local-lvm` olarak tanimlar |
|
||||
|
||||
Bir asama basarisiz olursa servis hata verir ve **sonraki acilista kaldigi yerden
|
||||
devam eder**. Durum:
|
||||
|
||||
```bash
|
||||
systemctl status pve-maas-init
|
||||
journalctl -u pve-maas-init -b
|
||||
ls -l /var/lib/pve-maas/
|
||||
```
|
||||
|
||||
Yapilandirma `/etc/pve-maas/pve-maas.conf` (varsayilanlar) ve
|
||||
`/etc/pve-maas/conf.d/*.conf` (cloud-init ile yazilan, ezici degerler) dosyalarindan
|
||||
okunur. Tum secenekler icin [`overlay/etc/pve-maas/pve-maas.conf`](overlay/etc/pve-maas/pve-maas.conf).
|
||||
|
||||
### Proxmox + MAAS birlesiminin tuzaklari
|
||||
|
||||
Bu imaj asagidaki uc sorunu bilerek cozer. Kendi turevinizi yazacaksaniz
|
||||
bunlari bozmayin:
|
||||
|
||||
**1. systemd siralama dongusu.** `pve-maas-init.service` icin
|
||||
`After=cloud-final.service` + `WantedBy=multi-user.target` yazmak bu imajda
|
||||
donguye yol aciyor ve systemd dongoyu kirmak icin *bizim* servisimizi siliyor:
|
||||
|
||||
```
|
||||
multi-user.target: Found ordering cycle on pve-maas-init.service/start
|
||||
Job pve-maas-init.service/start deleted to break ordering cycle
|
||||
```
|
||||
|
||||
Servis sessizce hic calismaz. Cozum: unit'te cloud-init siralamasi yok;
|
||||
script kendi icinde `cloud-init status --wait` ile bekliyor.
|
||||
|
||||
**2. `pvenetcommit.service`.** Proxmox ag degisikliklerini once
|
||||
`/etc/network/interfaces.new` dosyasina yazar; `pvenetcommit` her acilista bu
|
||||
dosyayi `interfaces` uzerine **tasir**. Build sirasinda olusan bir `.new`
|
||||
dosyasi imajda kalirsa dugumun ilk acilisinda yapilandirmayi ezer. Hem imajda
|
||||
hem de ag donusumunden sonra siliniyor.
|
||||
|
||||
**3. Arayuz adi uyusmazligi.** MAAS commissioning'i Ubuntu ephemeral ortaminda
|
||||
yapar ve karti orada gordugu adla kaydeder (or. `enp6s18`). Deploy edilen
|
||||
Debian 13 / Proxmox ise udev'in farkli isimlendirme semasi yuzunden ayni karta
|
||||
`ens18` diyebilir. Bu durumda cloud-init acilista yeniden adlandirmayi dener,
|
||||
`[busy]` hatasi alir ve **arayuzu kapali birakir** - dugum agini tamamen
|
||||
kaybeder:
|
||||
|
||||
```
|
||||
Failed to rename devices: [busy] Error renaming mac=... from ens18 to enp6s18
|
||||
```
|
||||
|
||||
`curtin-hooks`, MAAS'in ag yapilandirmasindaki MAC -> ad eslemesini
|
||||
`/etc/systemd/network/10-maas-<ad>.link` olarak yazar; udev karti en bastan
|
||||
dogru adla olusturur ve yeniden adlandirma gerekmez.
|
||||
|
||||
### Ilk acilista ag sahipligi
|
||||
|
||||
Imaj `networking.service`'i (ifupdown2) **devre disi** ve `/etc/network/interfaces`
|
||||
dosyasini yalnizca `lo` icerecek sekilde gonderir. Ilk acilista agi tamamen MAAS
|
||||
yonetir (curtin'in yazdigi netplan + systemd-networkd). `pve-maas-init` ag asamasi
|
||||
`vmbr0` yapilandirmasini yazdiktan sonra netplan/networkd'yi devre disi birakip
|
||||
`networking.service`'i kendisi etkinlestirir.
|
||||
|
||||
Bu sirala onemli: ifupdown2 etkin gelirse, imajdan gelen eski arayuz tanimiyla
|
||||
acilista gercek arayuzu kapatir ve dugum daha otomasyon calismadan agini kaybeder.
|
||||
|
||||
### Ag donusumu
|
||||
|
||||
`PVE_NET_APPLY=reboot` (varsayilan) ile dugum, cloud-init MAAS'a "deploy tamamlandi"
|
||||
sinyalini gonderdikten **sonra** bir kez yeniden baslar ve `vmbr0` ile acilir.
|
||||
Yeniden baslatma istemiyorsaniz `PVE_NET_APPLY=reload` ifupdown2'nin `ifreload -a`
|
||||
komutunu kullanir.
|
||||
|
||||
Bond/VLAN gibi karmasik topolojilerde `PVE_NET_MANAGE=false` yapip
|
||||
`/etc/network/interfaces` dosyasini cloud-init ile kendiniz yazin —
|
||||
bkz. [`maas/examples/04-advanced-network.yaml`](maas/examples/04-advanced-network.yaml).
|
||||
|
||||
### Depolama
|
||||
|
||||
MAAS'in disk duzeni bir LVM VG birakiyorsa (MAAS'ta **LVM storage layout** secip
|
||||
kok LV'yi kucultun), artan alanda `data` adinda bir thin havuz acilir ve `local-lvm`
|
||||
olarak tanimlanir. Ayri bir disk kullanmak icin:
|
||||
|
||||
```
|
||||
PVE_THINPOOL_DISK=/dev/sdb
|
||||
PVE_THINPOOL_VG=pve
|
||||
```
|
||||
|
||||
Bos alan `PVE_THINPOOL_MIN_GB` (varsayilan 16 GiB) altindaysa asama sessizce atlanir;
|
||||
dugum yalnizca dizin tabanli `local` deposuyla calisir.
|
||||
|
||||
---
|
||||
|
||||
## Kume otomasyonu
|
||||
|
||||
`pvecm add` parolayi etkilesimli sorar, bu yuzden ayni isi yapan API ucu kullanilir:
|
||||
|
||||
```
|
||||
pvesh create /cluster/config/join --hostname <peer> --password <pw> --fingerprint <fp>
|
||||
```
|
||||
|
||||
**Ilk dugum** ([`01-first-node.yaml`](maas/examples/01-first-node.yaml)):
|
||||
|
||||
```yaml
|
||||
PVE_CLUSTER_MODE=create
|
||||
PVE_CLUSTER_NAME=pve-cluster-01
|
||||
```
|
||||
|
||||
**Katilan dugumler** ([`02-join-node.yaml`](maas/examples/02-join-node.yaml)):
|
||||
|
||||
```yaml
|
||||
PVE_CLUSTER_MODE=join
|
||||
PVE_CLUSTER_PEER=192.0.2.11
|
||||
PVE_CLUSTER_PEER_PASSWORD='...'
|
||||
PVE_CLUSTER_FINGERPRINT='AA:BB:...' # onerilir
|
||||
```
|
||||
|
||||
Katilan dugum, hedef dugumun 8006 portu acilana kadar bekler
|
||||
(`PVE_CLUSTER_WAIT`, varsayilan 900 s) ve basarisiz denemeleri tekrarlar
|
||||
(`PVE_CLUSTER_RETRIES`). Katilim bittikten sonra `conf.d` icindeki parolalar
|
||||
temizlenir (`PVE_CLUSTER_WIPE_SECRETS=true`).
|
||||
|
||||
### Birden fazla bagimsiz kume
|
||||
|
||||
Imaj tamamen jeneriktir - kume kimligi **yalnizca** deploy anindaki cloud-init
|
||||
user-data'sindan gelir. Ayni imaj ve ayni preseed ile istediginiz kadar ayri
|
||||
kume kurabilirsiniz; **yeniden derleme gerekmez**.
|
||||
|
||||
Elle yapmak zahmetli oldugu icin ([`scripts/deploy-cluster.sh`](scripts/deploy-cluster.sh))
|
||||
tum akisi otomatiklestirir: ilk dugumu `create` ile deploy eder, ayaga kalkmasini
|
||||
bekler, sertifika parmak izini okur ve kalan dugumleri o parmak iziyle `join`
|
||||
modunda deploy eder.
|
||||
|
||||
**MAAS region controller uzerinde** calistirin (`maas` CLI profili ve dugumlerin
|
||||
8006 portuna erisim gerekir):
|
||||
|
||||
```bash
|
||||
# Once ne uretecegini gorun - hicbir sey deploy etmez
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3 --dry-run
|
||||
|
||||
# Gercek kurulum
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3
|
||||
|
||||
# Ikinci, tamamen bagimsiz kume - ayni imaj, ayni preseed
|
||||
./deploy-cluster.sh --name pve-dr --nodes dr1,dr2,dr3
|
||||
```
|
||||
|
||||
Listedeki **ilk dugum kumeyi olusturur**, digerleri ona katilir. Her kume kendi
|
||||
root parolasini alir (vermezseniz uretilir ve ekrana yazilir).
|
||||
|
||||
Ayri bir corosync agi kullaniyorsaniz:
|
||||
|
||||
```bash
|
||||
./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3 --link0-prefix 10.10.20.
|
||||
```
|
||||
|
||||
Her dugumun o onekle baslayan adresi MAAS'tan bulunup `PVE_CLUSTER_LINK0` olarak
|
||||
yazilir. (Bu yol henuz canli test edilmedi.)
|
||||
|
||||
Cok dugumlu kumelerde `--serial` ile katilanlar teker teker deploy edilir.
|
||||
Varsayilan paralel moddur; es zamanli katilim denemeleri olursa `pve-maas-init`
|
||||
zaten 5 kez, 30 saniye arayla tekrar dener.
|
||||
|
||||
**Kumeleri MAAS'ta ayirmak icin** etiket kullanmak ise yarar:
|
||||
|
||||
```bash
|
||||
maas $PROFILE tag create name=pve-prod
|
||||
maas $PROFILE tag update-nodes pve-prod add=$SYSTEM_ID
|
||||
maas $PROFILE machines read tags=pve-prod | jq -r '.[].hostname'
|
||||
```
|
||||
|
||||
### Guvenlik notlari
|
||||
|
||||
* Hedef dugumun **root@pam parolasi duz metin olmak zorundadir** — API dogrulamasi
|
||||
hash kabul etmez. Bu deger cloud-init user-data icinde MAAS'ta saklanir; MAAS
|
||||
user-data'ya erisimi olan herkes gorebilir. Kisa omurlu bir parola kullanip
|
||||
kurulumdan sonra degistirmeyi dusunun.
|
||||
* `PVE_CLUSTER_FINGERPRINT` vermezseniz parmak izi hedef dugumden okunur (TOFU) —
|
||||
ilk baglantida MITM'e aciktir. Uretimde parmak izini onceden verin.
|
||||
* Dugumun kendi `root` parolasi icin `PVE_ROOT_PASSWORD_HASH` kullanin
|
||||
(`openssl passwd -6`), duz metin `PVE_ROOT_PASSWORD` yerine.
|
||||
* `PVE_ROOT_PASSWORD*` hic verilmezse root parolasi kilitli kalir: web arayuzune
|
||||
`root@pam` ile girilemez ve bu dugum baska bir dugumun katilim hedefi olamaz.
|
||||
|
||||
---
|
||||
|
||||
## Dizin yapisi
|
||||
|
||||
```
|
||||
Makefile derleme / preseed / yukleme hedefleri
|
||||
scripts/install-deps.sh build host bagimliliklari
|
||||
scripts/customize-proxmox.sh.in build VM icinde calisan Proxmox kurulum sablonu
|
||||
scripts/deploy-cluster.sh MAAS uzerinden komple bir kume kurar (cok kumeli senaryolar)
|
||||
scripts/verify-image.sh uretilen imajin icerik dogrulamasi
|
||||
overlay/ imaja kopyalanan dosyalar
|
||||
usr/local/sbin/pve-maas-init ilk acilis durum makinesi
|
||||
etc/pve-maas/pve-maas.conf varsayilan yapilandirma (tum secenekler)
|
||||
etc/systemd/system/... pve-maas-init.service
|
||||
curtin/curtin-hooks curtin'in apt ile cekirdek kurmasini engeller
|
||||
maas/curtin_userdata_custom.in MAAS curtin preseed sablonu
|
||||
maas/examples/*.yaml cloud-init user-data ornekleri
|
||||
build/ uretilen dosyalar (git'e girmez)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### packer-maas uzerindeki degisiklikler
|
||||
|
||||
`make image`, `build/packer-maas` altina klonlanan upstream sablonda tek bir yama
|
||||
yapar: `debian-cloudimg.pkr.hcl` icindeki `disk_size` (4G -> `DISK_SIZE`), `cpus` ve
|
||||
`memory` degerleri. Upstream 4G ile gelir; Debian cloud image (~3G) uzerine Proxmox VE
|
||||
kurulumu bu alana sigmaz. Uretilen `.tar.gz` yalnizca kullanilan dosyalari icerdigi
|
||||
icin buyuk sanal disk son imaji buyutmez.
|
||||
|
||||
Yama her `make image` calistiginda `git checkout -f` sonrasi yeniden uygulanir;
|
||||
upstream'i `PM_REF` ile bir commit SHA'ya sabitleyerek surprizleri onleyebilirsiniz.
|
||||
|
||||
---
|
||||
|
||||
## Sorun giderme
|
||||
|
||||
**Deploy "Failed deployment" ile bitiyor**
|
||||
Preseed dosyasi dogru adla region controller'da mi? Ad, yuklenen imajin adiyla
|
||||
birebir eslesmelidir: `curtin_userdata_custom_<arch>_<subarch>_<image-name>`.
|
||||
`make preseed` ciktisindaki dosya adini kullanin.
|
||||
|
||||
**Deploy tamam ama web arayuzune girilemiyor**
|
||||
`root@pam` parolasi ayarlandi mi? `journalctl -u pve-maas-init -b | grep rootpw`.
|
||||
Ayrica `vmbr0` ayaga kalkmis mi: `ip -br addr`.
|
||||
|
||||
**Dugum kumeye katilmadi**
|
||||
`journalctl -u pve-maas-init -b` ciktisina bakin. Sik nedenler: hedef dugum 8006'da
|
||||
erisilemiyor, root parolasi yanlis, parmak izi eslesmiyor, ya da hedef dugumun
|
||||
`/etc/hosts` kaydi eksik oldugu icin `pvecm` orada hata veriyor.
|
||||
Servisi elle tekrar calistirmak icin:
|
||||
|
||||
```bash
|
||||
rm -f /var/lib/pve-maas/cluster.done /var/lib/pve-maas/complete
|
||||
systemctl start pve-maas-init
|
||||
```
|
||||
|
||||
**Deploy "Deployed" diyor ama dugume hic erisilemiyor**
|
||||
Dugumun konsoluna bakin (Proxmox'ta `qm terminal <vmid>`). Login istemi geliyorsa
|
||||
sistem acilmis, sorun agda demektir. Kurtarma modunda diski inceleyin:
|
||||
|
||||
```bash
|
||||
maas $PROFILE machine rescue-mode $SYSTEM_ID
|
||||
# ephemeral ortama SSH ile baglanip:
|
||||
mount /dev/vgroot/lvroot /mnt/t
|
||||
cat /mnt/t/etc/network/interfaces # vmbr0 yazilmis mi?
|
||||
ls -l /mnt/t/var/lib/pve-maas/ # hangi asamalar tamamlandi?
|
||||
cat /mnt/t/var/log/cloud-init-output.log # cloud-init user-data'yi aldi mi?
|
||||
journalctl -D /mnt/t/var/log/journal -u pve-maas-init
|
||||
```
|
||||
|
||||
`/var/lib/pve-maas/` bosken ag da kapaliysa, `networking.service` ile MAAS'in
|
||||
netplan yapilandirmasi cakisiyor demektir; imajda bu servis devre disi olmali.
|
||||
|
||||
**Dugum adi imajdaki hostname olarak gorunuyor**
|
||||
Olmamasi gerekir — pmxcfs veritabani imajdan silinir ve ilk acilista guncel
|
||||
hostname ile yeniden uretilir. Boyle bir durumda `/var/lib/pve-cluster/config.db`
|
||||
imajda kalmis demektir; `make image` ciktisinda "Proxmox dugum kimligi sifirlaniyor"
|
||||
adimini kontrol edin.
|
||||
|
||||
**Deploy "Installing OS" sonrasi "Configuring OS" asamasinda patliyor**
|
||||
Kurulum kaydina bakin:
|
||||
|
||||
```bash
|
||||
maas $PROFILE node-script-result download $SYSTEM_ID current-installation \
|
||||
filetype=txt filters=/tmp/install.log output=all | tail -60
|
||||
```
|
||||
|
||||
`install_kernel -> AttributeError: 'NoneType' object has no attribute 'get'`
|
||||
goruyorsaniz preseed'de `kernel: null` kullanilmis demektir. MAAS ile gelen
|
||||
curtin surumlerinin bir kismi bunu desteklemez. Bu depodaki preseed bunu
|
||||
kullanmaz; cekirdek kurulumu imajdaki `/curtin/curtin-hooks` ile devre disi
|
||||
birakilir. Preseed'i elle duzenlediyseniz geri alin.
|
||||
|
||||
**Build "proxmox-ve paketi bulunamadi" diyor**
|
||||
`PVE_SUITE`/`DEBIAN_SERIES` ile `PVE_REPO` uyumsuz. Depoyu dogrulayin:
|
||||
`curl -s http://download.proxmox.com/debian/pve/dists/<suite>/Release | grep Components`
|
||||
|
||||
---
|
||||
|
||||
## Kaynaklar
|
||||
|
||||
* [canonical/packer-maas](https://github.com/canonical/packer-maas) — temel Packer sablonlari (AGPL-3.0)
|
||||
* [MAAS: Build custom images](https://canonical.com/maas/docs/how-to-build-custom-images)
|
||||
* [Proxmox VE: Install on Debian](https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_13_Trixie)
|
||||
* [Proxmox VE: Cluster Manager](https://pve.proxmox.com/wiki/Cluster_Manager)
|
||||
* [pvecm(1)](https://pve.proxmox.com/pve-docs/pvecm.1.html)
|
||||
57
maas/curtin_userdata_custom.in
Normal file
57
maas/curtin_userdata_custom.in
Normal file
@@ -0,0 +1,57 @@
|
||||
#cloud-config
|
||||
#
|
||||
# MAAS curtin preseed - Proxmox VE ozel imaji icin
|
||||
#
|
||||
# Dosya adi: curtin_userdata_custom_@@ARCH@@_generic_@@IMAGE_NAME@@
|
||||
# Kopyalanacak yer (MAAS snap): /var/snap/maas/current/preseeds/
|
||||
# (MAAS deb paketi): /etc/maas/preseeds/
|
||||
#
|
||||
# Bu ad yalnizca 'custom/@@IMAGE_NAME@@' adiyla, '@@ARCH@@/generic' mimarisiyle
|
||||
# yuklenen imaja uygulanir; diger ozel imajlariniz etkilenmez.
|
||||
|
||||
# Cekirdek kurulumu imajdaki /curtin/curtin-hooks tarafindan devre disi
|
||||
# birakilir (Proxmox cekirdegi imajda hazir gelir). Asagidaki blok yalnizca
|
||||
# kanca herhangi bir nedenle calismazsa devreye giren yedek yoldur.
|
||||
#
|
||||
# DIKKAT: burada 'kernel: null' KULLANMAYIN. MAAS ile gelen curtin
|
||||
# surumlerinin bir kismi bunu desteklemiyor ve kurulum su hatayla basarisiz
|
||||
# oluyor: install_kernel -> AttributeError: 'NoneType' object has no
|
||||
# attribute 'get'
|
||||
kernel:
|
||||
package: proxmox-default-kernel
|
||||
fallback-package: proxmox-default-kernel
|
||||
mapping: {}
|
||||
|
||||
apt:
|
||||
preserve_sources_list: true
|
||||
|
||||
debconf_selections:
|
||||
maas: |
|
||||
{{for line in str(curtin_preseed).splitlines()}}
|
||||
{{line}}
|
||||
{{endfor}}
|
||||
|
||||
late_commands:
|
||||
maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null']
|
||||
|
||||
# Hedef sistemi /mnt altina bagla
|
||||
late_01: mount --bind $TARGET_MOUNT_POINT /mnt
|
||||
|
||||
# MAAS datasource tanimini hedef sisteme tasi
|
||||
late_02: grep -A2 datasource /etc/cloud/cloud.cfg.d/91_kernel_cmdline_url.cfg | sed 's/curtin//' | tee /mnt/etc/cloud/cloud.cfg.d/91_maas_datasource.cfg
|
||||
|
||||
# Debian cloud-init sablonundaki Ubuntu referanslarini duzelt
|
||||
late_03: sed -i 's@ubuntu.com/ubuntu@debian.org/debian@g;s@archive@deb@g;s@ubuntu@debian@g;s@Ubuntu@Debian@g;s@security.debian.org/debian@security.debian.org@g' /mnt/etc/cloud/cloud.cfg
|
||||
|
||||
# zz-update-grub kancasindaki 'set -e' tuzagini kaldir
|
||||
late_04: sed -i '/^set -e/{n;N;d}' /mnt/etc/kernel/postinst.d/zz-update-grub
|
||||
|
||||
# packer-maas'in netplan.io kontrolunu atlatmak icin koydugu sarmalayicilari sil
|
||||
late_05: rm -f /usr/local/bin/dpkg-query /usr/local/bin/netplan
|
||||
late_06: rm -f /mnt/usr/local/bin/dpkg-query /mnt/usr/local/bin/netplan
|
||||
|
||||
# Guvenlik agi: ilk acilis servisi kesin etkin olsun
|
||||
late_07: chroot /mnt systemctl enable pve-maas-init.service || true
|
||||
|
||||
# Onceki calistirmadan kalan durum dosyalari varsa temizle
|
||||
late_08: rm -f /mnt/var/lib/pve-maas/*.done /mnt/var/lib/pve-maas/complete
|
||||
25
maas/examples/01-first-node.yaml
Normal file
25
maas/examples/01-first-node.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
#cloud-config
|
||||
# Kumenin ILK dugumu: yeni bir kume olusturur.
|
||||
#
|
||||
# MAAS ile deploy:
|
||||
# maas $PROFILE machine deploy $SYSTEM_ID \
|
||||
# osystem=custom distro_series=proxmox-ve-9 \
|
||||
# user_data="$(base64 -w0 01-first-node.yaml)"
|
||||
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
# root@pam parolasi - web arayuzu ve diger dugumlerin katilimi icin sart.
|
||||
# Hash uretmek icin: openssl passwd -6
|
||||
PVE_ROOT_PASSWORD_HASH='$6$DEGISTIRIN$REPLACE.WITH.YOUR.OWN.HASH'
|
||||
|
||||
PVE_CLUSTER_MODE=create
|
||||
PVE_CLUSTER_NAME=pve-cluster-01
|
||||
|
||||
# Corosync icin ayri bir ag kullaniyorsaniz:
|
||||
# PVE_CLUSTER_LINK0=198.51.100.11
|
||||
|
||||
PVE_NET_APPLY=reboot
|
||||
PVE_THINPOOL=auto
|
||||
31
maas/examples/02-join-node.yaml
Normal file
31
maas/examples/02-join-node.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
#cloud-config
|
||||
# Mevcut bir kumeye katilan dugum.
|
||||
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
PVE_ROOT_PASSWORD_HASH='$6$DEGISTIRIN$REPLACE.WITH.YOUR.OWN.HASH'
|
||||
|
||||
PVE_CLUSTER_MODE=join
|
||||
|
||||
# Kumede zaten bulunan bir dugumun IP'si
|
||||
PVE_CLUSTER_PEER=192.0.2.11
|
||||
|
||||
# O dugumun root@pam parolasi (duz metin olmak zorunda - API dogrulamasi icin)
|
||||
PVE_CLUSTER_PEER_PASSWORD='degistirin'
|
||||
|
||||
# Onerilen: parmak izini onceden verin (TOFU riskini kaldirir)
|
||||
# openssl s_client -connect 192.0.2.11:8006 </dev/null 2>/dev/null | \
|
||||
# openssl x509 -noout -fingerprint -sha256 | cut -d= -f2
|
||||
# PVE_CLUSTER_FINGERPRINT='AA:BB:CC:...'
|
||||
PVE_CLUSTER_FINGERPRINT_DISCOVER=true
|
||||
|
||||
# PVE_CLUSTER_LINK0=198.51.100.12
|
||||
|
||||
# Katilimdan sonra bu dosyadaki parolalar silinir
|
||||
PVE_CLUSTER_WIPE_SECRETS=true
|
||||
|
||||
PVE_NET_APPLY=reboot
|
||||
PVE_THINPOOL=auto
|
||||
17
maas/examples/03-standalone.yaml
Normal file
17
maas/examples/03-standalone.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
#cloud-config
|
||||
# Tek basina (kumesiz) Proxmox VE dugumu.
|
||||
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
PVE_ROOT_PASSWORD_HASH='$6$DEGISTIRIN$REPLACE.WITH.YOUR.OWN.HASH'
|
||||
PVE_CLUSTER_MODE=none
|
||||
|
||||
# VLAN farkindalikli kopru isteniyorsa:
|
||||
# PVE_NET_VLAN_AWARE=true
|
||||
|
||||
# Ikinci diski VM depolamasi olarak kullan:
|
||||
# PVE_THINPOOL_DISK=/dev/sdb
|
||||
# PVE_THINPOOL_VG=pve
|
||||
49
maas/examples/04-advanced-network.yaml
Normal file
49
maas/examples/04-advanced-network.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
#cloud-config
|
||||
# Karmasik ag: bond + VLAN. Otomatik vmbr0 donusumunu kapatip
|
||||
# /etc/network/interfaces dosyasini kendimiz yaziyoruz.
|
||||
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
PVE_ROOT_PASSWORD_HASH='$6$DEGISTIRIN$REPLACE.WITH.YOUR.OWN.HASH'
|
||||
PVE_NET_MANAGE=false
|
||||
PVE_CLUSTER_MODE=none
|
||||
|
||||
- path: /etc/network/interfaces
|
||||
permissions: "0644"
|
||||
owner: root:root
|
||||
content: |
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
iface eno1 inet manual
|
||||
iface eno2 inet manual
|
||||
|
||||
auto bond0
|
||||
iface bond0 inet manual
|
||||
bond-slaves eno1 eno2
|
||||
bond-mode 802.3ad
|
||||
bond-xmit-hash-policy layer3+4
|
||||
bond-miimon 100
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address 192.0.2.20/24
|
||||
gateway 192.0.2.1
|
||||
bridge-ports bond0
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
bridge-vlan-aware yes
|
||||
bridge-vids 2-4094
|
||||
|
||||
source /etc/network/interfaces.d/*
|
||||
|
||||
- path: /etc/cloud/cloud.cfg.d/99-pve-maas-disable-network.cfg
|
||||
permissions: "0644"
|
||||
content: |
|
||||
network: {config: disabled}
|
||||
|
||||
runcmd:
|
||||
- [ systemctl, enable, networking.service ]
|
||||
114
overlay/curtin/curtin-hooks
Executable file
114
overlay/curtin/curtin-hooks
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# curtin-hooks - MAAS/curtin kurulum kancasi (Proxmox VE imaji icin)
|
||||
#
|
||||
# Curtin, hedef sistemde /curtin/curtin-hooks varsa dahili curthooks yerine
|
||||
# bu script'i calistirir (curtin.util.run_hook_if_exists).
|
||||
#
|
||||
# Amac: curtin'in APT uzerinden cekirdek kurmaya calismasini engellemek.
|
||||
# Proxmox cekirdegi (proxmox-default-kernel) imajin icinde hazir gelir;
|
||||
# curtin'in onu yeniden kurmasi gereksizdir ve dugumun deploy sirasinda
|
||||
# download.proxmox.com'a erisebilmesini zorunlu kilar (izole aglarda calismaz).
|
||||
#
|
||||
# Not: Bunu preseed'de "kernel: null" ile yapmak mumkun degil - MAAS ile
|
||||
# gelen curtin surumlerinin bir kismi bu degeri desteklemiyor ve
|
||||
# install_kernel icinde AttributeError ile patliyor. Kancayi kullanmak
|
||||
# curtin surumunden bagimsiz olarak calisir.
|
||||
#
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from curtin.commands import curthooks
|
||||
from curtin.config import load_command_config
|
||||
from curtin.util import load_command_environment
|
||||
|
||||
|
||||
def disable_kernel_install():
|
||||
"""curtin'in cekirdek kurulum adimini etkisiz hale getir.
|
||||
|
||||
Fonksiyon adi curtin surumune gore degisiyor (install_kernel ->
|
||||
curthook_install_kernel), ikisini de yakaliyoruz.
|
||||
"""
|
||||
disabled = []
|
||||
for name in ("install_kernel", "curthook_install_kernel"):
|
||||
if hasattr(curthooks, name):
|
||||
setattr(curthooks, name, lambda *args, **kwargs: None)
|
||||
disabled.append(name)
|
||||
print("curtin-hooks: cekirdek kurulumu devre disi: %s" % (disabled or "hicbiri bulunamadi"))
|
||||
|
||||
|
||||
def pin_interface_names(config, target):
|
||||
"""MAAS'in bildigi arayuz adlarini hedef sistemde MAC uzerinden sabitle.
|
||||
|
||||
MAAS commissioning'i Ubuntu ephemeral ortaminda yapiyor ve arayuzu orada
|
||||
gordugu adla (or. enp6s18) kaydediyor. Deploy edilen Debian 13 / Proxmox
|
||||
ise udev'in farkli isimlendirme semasi yuzunden ayni karti ens18 diye
|
||||
adlandirabiliyor. Bu durumda cloud-init acilista arayuzu yeniden
|
||||
adlandirmaya calisiyor, su hatayi aliyor:
|
||||
|
||||
Failed to rename devices: [busy] Error renaming
|
||||
mac=... from ens18 to enp6s18
|
||||
|
||||
...ve arayuzu KAPALI birakiyor; dugum agini tamamen kaybediyor.
|
||||
|
||||
Cozum: MAC -> ad eslemesini systemd .link dosyasi olarak yaziyoruz, boylece
|
||||
udev karti en bastan MAAS'in bekledigi adla olusturuyor ve yeniden
|
||||
adlandirmaya hic gerek kalmiyor.
|
||||
"""
|
||||
net = config.get("network") or {}
|
||||
pairs = []
|
||||
|
||||
# netplan / v2 bicimi
|
||||
for name, cfg in (net.get("ethernets") or {}).items():
|
||||
cfg = cfg or {}
|
||||
mac = (cfg.get("match") or {}).get("macaddress")
|
||||
if mac:
|
||||
pairs.append((cfg.get("set-name") or name, mac))
|
||||
|
||||
# curtin v1 bicimi
|
||||
for entry in net.get("config") or []:
|
||||
if not isinstance(entry, dict) or entry.get("type") != "physical":
|
||||
continue
|
||||
if entry.get("name") and entry.get("mac_address"):
|
||||
pairs.append((entry["name"], entry["mac_address"]))
|
||||
|
||||
if not pairs:
|
||||
print("curtin-hooks: arayuz adi sabitlenmedi (ag yapilandirmasi bos)")
|
||||
return
|
||||
|
||||
link_dir = os.path.join(target, "etc/systemd/network")
|
||||
os.makedirs(link_dir, exist_ok=True)
|
||||
for name, mac in pairs:
|
||||
path = os.path.join(link_dir, "10-maas-%s.link" % name)
|
||||
with open(path, "w") as fh:
|
||||
fh.write(
|
||||
"# MAAS tarafindan beklenen arayuz adi - pve-maas imaji\n"
|
||||
"# curtin-hooks tarafindan olusturuldu.\n"
|
||||
"[Match]\n"
|
||||
"MACAddress=%s\n"
|
||||
"\n"
|
||||
"[Link]\n"
|
||||
"Name=%s\n" % (mac.lower(), name)
|
||||
)
|
||||
print("curtin-hooks: arayuz adi sabitlendi %s -> %s" % (mac, name))
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Kancayi hedef sistemden kaldir - iz birakma."""
|
||||
curtin_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
shutil.rmtree(curtin_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
state = load_command_environment()
|
||||
config = load_command_config(None, state)
|
||||
target = state["target"]
|
||||
|
||||
disable_kernel_install()
|
||||
curthooks.builtin_curthooks(config, target, state)
|
||||
pin_interface_names(config, target)
|
||||
cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
overlay/etc/pve-maas/conf.d/.keep
Normal file
0
overlay/etc/pve-maas/conf.d/.keep
Normal file
98
overlay/etc/pve-maas/pve-maas.conf
Normal file
98
overlay/etc/pve-maas/pve-maas.conf
Normal file
@@ -0,0 +1,98 @@
|
||||
# /etc/pve-maas/pve-maas.conf
|
||||
#
|
||||
# pve-maas-init varsayilan yapilandirmasi. BU DOSYAYI DUZENLEMEYIN.
|
||||
# Dugume ozel ayarlari MAAS deploy sirasinda cloud-init ile
|
||||
# /etc/pve-maas/conf.d/*.conf altina yazin; oradaki degerler bunlari ezer.
|
||||
#
|
||||
# Ornek (cloud-init user-data):
|
||||
# write_files:
|
||||
# - path: /etc/pve-maas/conf.d/50-cluster.conf
|
||||
# permissions: "0600"
|
||||
# content: |
|
||||
# PVE_ROOT_PASSWORD='...'
|
||||
# PVE_CLUSTER_MODE=join
|
||||
# PVE_CLUSTER_PEER=192.0.2.11
|
||||
# PVE_CLUSTER_PEER_PASSWORD='...'
|
||||
|
||||
# ---------------------------------------------------------------- genel
|
||||
# Tum ilk acilis otomasyonunu kapatmak icin false yapin.
|
||||
PVE_ENABLED=true
|
||||
|
||||
# Bos birakilirsa hostname -f / resolv.conf'tan turetilir.
|
||||
PVE_FQDN=
|
||||
|
||||
# ---------------------------------------------------------------- ag
|
||||
# MAAS'in yapilandirdigi arayuz vmbr0 koprusune donusturulsun mu?
|
||||
# Bond/VLAN gibi karmasik topolojilerde false yapip /etc/network/interfaces
|
||||
# dosyasini cloud-init ile kendiniz yazin.
|
||||
PVE_NET_MANAGE=true
|
||||
|
||||
PVE_NET_BRIDGE=vmbr0
|
||||
|
||||
# Bos ise varsayilan rotayi tasiyan arayuz kullanilir.
|
||||
PVE_NET_UPLINK=
|
||||
|
||||
# auto | static | dhcp (auto: MAAS'in yazdigi yapilandirmadan tespit edilir)
|
||||
PVE_NET_MODE=auto
|
||||
|
||||
# reboot | reload | none
|
||||
# reboot : yapilandirmayi yazip bir kez yeniden baslatir (en guvenilir)
|
||||
# reload : ifreload -a ile canli uygular (yeniden baslatma yok)
|
||||
# none : sadece yazar, uygulamaz
|
||||
PVE_NET_APPLY=reboot
|
||||
|
||||
# Koprude VLAN farkindaligi (bridge-vlan-aware yes, vids 2-4094)
|
||||
PVE_NET_VLAN_AWARE=false
|
||||
|
||||
# /etc/network/interfaces sonuna eklenecek ham metin (ek kopruler vb.)
|
||||
PVE_NET_EXTRA=
|
||||
|
||||
# ---------------------------------------------------------------- root parolasi
|
||||
# root@pam web arayuzu girisi ve baska dugumlerin bu dugume katilmasi icin gerekli.
|
||||
# Duz metin yerine hash tercih edin: openssl passwd -6
|
||||
PVE_ROOT_PASSWORD=
|
||||
PVE_ROOT_PASSWORD_HASH=
|
||||
|
||||
# ---------------------------------------------------------------- kume
|
||||
# none | create | join
|
||||
PVE_CLUSTER_MODE=none
|
||||
|
||||
# create icin
|
||||
PVE_CLUSTER_NAME=
|
||||
|
||||
# join icin: mevcut bir kume dugumunun IP/hostname'i ve root@pam parolasi
|
||||
PVE_CLUSTER_PEER=
|
||||
PVE_CLUSTER_PEER_PASSWORD=
|
||||
PVE_CLUSTER_PEER_PASSWORD_FILE=
|
||||
|
||||
# Sertifika parmak izi. Bos ve DISCOVER=true ise dugumden okunur (TOFU).
|
||||
# Guvenlik icin degeri onceden vermeniz onerilir:
|
||||
# openssl s_client -connect NODE:8006 </dev/null 2>/dev/null | \
|
||||
# openssl x509 -noout -fingerprint -sha256 | cut -d= -f2
|
||||
PVE_CLUSTER_FINGERPRINT=
|
||||
PVE_CLUSTER_FINGERPRINT_DISCOVER=true
|
||||
|
||||
# Corosync baglantilari (ayri bir kume agi kullaniyorsaniz)
|
||||
PVE_CLUSTER_LINK0=
|
||||
PVE_CLUSTER_LINK1=
|
||||
PVE_CLUSTER_NODEID=
|
||||
PVE_CLUSTER_VOTES=
|
||||
|
||||
# Kume dugumunun erisilebilir olmasi icin beklenecek sure (saniye) ve deneme sayisi
|
||||
PVE_CLUSTER_WAIT=900
|
||||
PVE_CLUSTER_RETRIES=5
|
||||
|
||||
# Katilimdan sonra conf.d icindeki parolalari sil
|
||||
PVE_CLUSTER_WIPE_SECRETS=true
|
||||
|
||||
# ---------------------------------------------------------------- depolama
|
||||
# auto | off | <vg-adi>
|
||||
# auto: en cok bos alani olan VG secilir
|
||||
PVE_THINPOOL=auto
|
||||
PVE_THINPOOL_NAME=data
|
||||
PVE_THINPOOL_STORAGE=local-lvm
|
||||
PVE_THINPOOL_MIN_GB=16
|
||||
|
||||
# Ayri bir disk verilirse o diskten yeni bir VG olusturulur.
|
||||
PVE_THINPOOL_DISK=
|
||||
PVE_THINPOOL_VG=pve
|
||||
44
overlay/etc/systemd/system/pve-maas-init.service
Normal file
44
overlay/etc/systemd/system/pve-maas-init.service
Normal file
@@ -0,0 +1,44 @@
|
||||
[Unit]
|
||||
Description=Proxmox VE post-deployment configuration for MAAS
|
||||
Documentation=file:/etc/pve-maas/pve-maas.conf
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
ConditionPathExists=!/var/lib/pve-maas/complete
|
||||
|
||||
# DIKKAT - burada cloud-init servislerine "After=" YAZMAYIN.
|
||||
#
|
||||
# After=cloud-final.service + WantedBy=multi-user.target birlesimi bu imajda
|
||||
# siralama dongusu olusturuyor ve systemd dongoyu kirmak icin BU servisin
|
||||
# baslatma isini siliyor:
|
||||
#
|
||||
# multi-user.target: Found ordering cycle on pve-maas-init.service/start
|
||||
# multi-user.target: Found dependency on cloud-final.service/start
|
||||
# multi-user.target: Found dependency on multi-user.target/start
|
||||
# Job pve-maas-init.service/start deleted to break ordering cycle
|
||||
#
|
||||
# Bunun yerine script kendi icinde "cloud-init status --wait" ile bekliyor;
|
||||
# boylece hem conf.d dosyalari yazilmis oluyor hem de MAAS deploy sinyalini
|
||||
# almis oluyor, systemd bagimlilik grafigine dokunmadan.
|
||||
|
||||
[Service]
|
||||
# Type=simple (oneshot DEGIL) - bu onemli.
|
||||
#
|
||||
# Type=oneshot ile servis bitene kadar multi-user.target ulasilmis sayilmiyor.
|
||||
# cloud-final.service ise "After=multi-user.target" ile siralanmis. Script
|
||||
# icinde cloud-init'i bekledigimiz icin bu bir kilitlenme uretiyordu:
|
||||
#
|
||||
# cloud-final.service waiting (multi-user.target bekliyor)
|
||||
# pve-maas-init.service running (cloud-init bekliyor)
|
||||
# multi-user.target waiting (pve-maas-init bekliyor)
|
||||
#
|
||||
# Type=simple ile servis hemen "basladi" sayilir, multi-user.target acilir,
|
||||
# cloud-final calisir ve beklememiz doner. Asama takibi zaten
|
||||
# /var/lib/pve-maas/*.done dosyalariyla yapiliyor.
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/pve-maas-init
|
||||
TimeoutStartSec=0
|
||||
StandardOutput=journal+console
|
||||
StandardError=journal+console
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
636
overlay/usr/local/sbin/pve-maas-init
Executable file
636
overlay/usr/local/sbin/pve-maas-init
Executable file
@@ -0,0 +1,636 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# pve-maas-init - MAAS ile deploy edilen Proxmox VE dugumunu ilk acilista yapilandirir.
|
||||
#
|
||||
# Asamalar (her biri /var/lib/pve-maas/<asama>.done ile bir kez calisir):
|
||||
# hosts - hostname/FQDN'i yonetim IP'sine bagla (pvecm bunu ister)
|
||||
# identity - dugume ozel kimlikleri yeniden uret (iSCSI IQN vb.)
|
||||
# rootpw - root@pam parolasini ayarla (web arayuzu ve kume katilimi icin)
|
||||
# network - MAAS'in verdigi arayuzu vmbr0 koprusune donustur
|
||||
# cluster - kume olustur veya mevcut kumeye katil
|
||||
# storage - bos alanda LVM-thin havuzu olustur (local-lvm)
|
||||
#
|
||||
# Yapilandirma: /etc/pve-maas/pve-maas.conf ve /etc/pve-maas/conf.d/*.conf
|
||||
# (conf.d icerigi MAAS deploy sirasinda cloud-init write_files ile yazilir)
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
CONF_DIR=/etc/pve-maas
|
||||
STATE_DIR=/var/lib/pve-maas
|
||||
LOG_TAG=pve-maas-init
|
||||
|
||||
mkdir -p "$STATE_DIR" "$CONF_DIR/conf.d"
|
||||
|
||||
log() { echo "[$LOG_TAG] $*"; logger -t "$LOG_TAG" -- "$*" 2>/dev/null || true; }
|
||||
warn() { echo "[$LOG_TAG] UYARI: $*" >&2; logger -t "$LOG_TAG" -p user.warning -- "UYARI: $*" 2>/dev/null || true; }
|
||||
die() { echo "[$LOG_TAG] HATA: $*" >&2; logger -t "$LOG_TAG" -p user.err -- "HATA: $*" 2>/dev/null || true; exit 1; }
|
||||
|
||||
done_flag() { echo "$STATE_DIR/$1.done"; }
|
||||
is_done() { [ -e "$(done_flag "$1")" ]; }
|
||||
mark_done() { date -Is > "$(done_flag "$1")"; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yapilandirmayi yukle
|
||||
# ---------------------------------------------------------------------------
|
||||
load_config() {
|
||||
# shellcheck disable=SC1091
|
||||
[ -r "$CONF_DIR/pve-maas.conf" ] && . "$CONF_DIR/pve-maas.conf"
|
||||
local f
|
||||
for f in "$CONF_DIR"/conf.d/*.conf; do
|
||||
[ -r "$f" ] || continue
|
||||
log "yapilandirma yukleniyor: $f"
|
||||
# shellcheck disable=SC1090
|
||||
. "$f"
|
||||
done
|
||||
|
||||
# Varsayilanlar
|
||||
PVE_ENABLED="${PVE_ENABLED:-true}"
|
||||
PVE_FQDN="${PVE_FQDN:-}"
|
||||
|
||||
PVE_NET_MANAGE="${PVE_NET_MANAGE:-true}"
|
||||
PVE_NET_BRIDGE="${PVE_NET_BRIDGE:-vmbr0}"
|
||||
PVE_NET_UPLINK="${PVE_NET_UPLINK:-}"
|
||||
PVE_NET_MODE="${PVE_NET_MODE:-auto}" # auto | static | dhcp
|
||||
PVE_NET_APPLY="${PVE_NET_APPLY:-reboot}" # reboot | reload | none
|
||||
PVE_NET_VLAN_AWARE="${PVE_NET_VLAN_AWARE:-false}"
|
||||
PVE_NET_EXTRA="${PVE_NET_EXTRA:-}"
|
||||
|
||||
PVE_ROOT_PASSWORD="${PVE_ROOT_PASSWORD:-}"
|
||||
PVE_ROOT_PASSWORD_HASH="${PVE_ROOT_PASSWORD_HASH:-}"
|
||||
|
||||
PVE_CLUSTER_MODE="${PVE_CLUSTER_MODE:-none}" # none | create | join
|
||||
PVE_CLUSTER_NAME="${PVE_CLUSTER_NAME:-}"
|
||||
PVE_CLUSTER_PEER="${PVE_CLUSTER_PEER:-}"
|
||||
PVE_CLUSTER_PEER_PASSWORD="${PVE_CLUSTER_PEER_PASSWORD:-}"
|
||||
PVE_CLUSTER_PEER_PASSWORD_FILE="${PVE_CLUSTER_PEER_PASSWORD_FILE:-}"
|
||||
PVE_CLUSTER_FINGERPRINT="${PVE_CLUSTER_FINGERPRINT:-}"
|
||||
PVE_CLUSTER_FINGERPRINT_DISCOVER="${PVE_CLUSTER_FINGERPRINT_DISCOVER:-true}"
|
||||
PVE_CLUSTER_LINK0="${PVE_CLUSTER_LINK0:-}"
|
||||
PVE_CLUSTER_LINK1="${PVE_CLUSTER_LINK1:-}"
|
||||
PVE_CLUSTER_NODEID="${PVE_CLUSTER_NODEID:-}"
|
||||
PVE_CLUSTER_VOTES="${PVE_CLUSTER_VOTES:-}"
|
||||
PVE_CLUSTER_WAIT="${PVE_CLUSTER_WAIT:-900}"
|
||||
PVE_CLUSTER_RETRIES="${PVE_CLUSTER_RETRIES:-5}"
|
||||
PVE_CLUSTER_WIPE_SECRETS="${PVE_CLUSTER_WIPE_SECRETS:-true}"
|
||||
|
||||
PVE_THINPOOL="${PVE_THINPOOL:-auto}" # auto | off | <vg-adi>
|
||||
PVE_THINPOOL_NAME="${PVE_THINPOOL_NAME:-data}"
|
||||
PVE_THINPOOL_STORAGE="${PVE_THINPOOL_STORAGE:-local-lvm}"
|
||||
PVE_THINPOOL_MIN_GB="${PVE_THINPOOL_MIN_GB:-16}"
|
||||
PVE_THINPOOL_DISK="${PVE_THINPOOL_DISK:-}"
|
||||
PVE_THINPOOL_VG="${PVE_THINPOOL_VG:-pve}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yardimcilar
|
||||
# ---------------------------------------------------------------------------
|
||||
primary_iface() {
|
||||
if [ -n "$PVE_NET_UPLINK" ]; then echo "$PVE_NET_UPLINK"; return; fi
|
||||
ip -4 -o route show default 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}'
|
||||
}
|
||||
|
||||
primary_ip() {
|
||||
ip -4 -o route get 1.1.1.1 2>/dev/null \
|
||||
| awk '{for(i=1;i<=NF;i++) if($i=="src") {print $(i+1); exit}}'
|
||||
}
|
||||
|
||||
node_fqdn() {
|
||||
local host fqdn dom
|
||||
host="$(hostname -s)"
|
||||
if [ -n "$PVE_FQDN" ]; then echo "$PVE_FQDN"; return; fi
|
||||
fqdn="$(hostname -f 2>/dev/null || true)"
|
||||
if [ -n "$fqdn" ] && [ "$fqdn" != "$host" ]; then echo "$fqdn"; return; fi
|
||||
dom="$(awk '/^(search|domain)[[:space:]]/{print $2; exit}' /etc/resolv.conf 2>/dev/null || true)"
|
||||
if [ -n "$dom" ]; then echo "${host}.${dom}"; else echo "$host"; fi
|
||||
}
|
||||
|
||||
wait_for_cloud_init() {
|
||||
command -v cloud-init >/dev/null 2>&1 || return 0
|
||||
log "cloud-init'in bitmesi bekleniyor"
|
||||
# Unit'te After=cloud-final.service kullanamiyoruz (siralama dongusu),
|
||||
# bu yuzden beklemeyi burada yapiyoruz. Hata donmesi normal olabilir.
|
||||
timeout 900 cloud-init status --wait >/dev/null 2>&1
|
||||
log "cloud-init durumu: $(cloud-init status 2>/dev/null | head -1)"
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_network() {
|
||||
local i ip
|
||||
for ((i = 0; i < 120; i += 5)); do
|
||||
ip="$(primary_ip)"
|
||||
[ -n "$ip" ] && { log "yonetim IP'si: ${ip}"; return 0; }
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_pmxcfs() {
|
||||
local i
|
||||
for i in $(seq 1 60); do
|
||||
[ -e /etc/pve/local ] && return 0
|
||||
sleep 2
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: hosts
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_hosts() {
|
||||
is_done hosts && return 0
|
||||
local host fqdn ip
|
||||
host="$(hostname -s)"
|
||||
fqdn="$(node_fqdn)"
|
||||
ip="$(primary_ip)"
|
||||
[ -n "$ip" ] || { warn "yonetim IP'si bulunamadi, /etc/hosts atlaniyor"; return 0; }
|
||||
|
||||
log "/etc/hosts guncelleniyor: ${ip} ${fqdn} ${host}"
|
||||
cp -a /etc/hosts "$STATE_DIR/hosts.orig" 2>/dev/null || true
|
||||
|
||||
local keep
|
||||
keep="$(grep -vE "^[[:space:]]*(127\.0\.0\.1|127\.0\.1\.1|::1|ff02::[12])[[:space:]]" /etc/hosts 2>/dev/null \
|
||||
| grep -vE "[[:space:]]${host}([[:space:]]|\$)" || true)"
|
||||
|
||||
{
|
||||
echo "127.0.0.1 localhost.localdomain localhost"
|
||||
echo "${ip} ${fqdn} ${host}"
|
||||
[ -n "$keep" ] && echo "$keep"
|
||||
echo
|
||||
echo "# IPv6"
|
||||
echo "::1 localhost ip6-localhost ip6-loopback"
|
||||
echo "ff02::1 ip6-allnodes"
|
||||
echo "ff02::2 ip6-allrouters"
|
||||
} > /etc/hosts
|
||||
|
||||
if command -v postconf >/dev/null 2>&1 && [ -f /etc/postfix/main.cf ]; then
|
||||
postconf -e "myhostname = ${fqdn}" >/dev/null 2>&1 || true
|
||||
systemctl try-restart postfix >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# cloud-init manage_etc_hosts=true ise her acilista /etc/hosts'u
|
||||
# 127.0.1.1 satiriyla geri yazar ve duzeltmemizi bozar.
|
||||
cat > /etc/cloud/cloud.cfg.d/99-pve-maas-hosts.cfg <<'EOF'
|
||||
# /etc/hosts artik pve-maas-init tarafindan yonetiliyor.
|
||||
manage_etc_hosts: false
|
||||
EOF
|
||||
|
||||
hostname -f >/dev/null 2>&1 || warn "hostname -f hala cozulmuyor"
|
||||
|
||||
# pmxcfs dugum kimligini ve sertifikalari dogru hostname/IP ile yenile.
|
||||
if systemctl list-unit-files pve-cluster.service >/dev/null 2>&1; then
|
||||
log "pve servisleri yeniden baslatiliyor (dogru hostname ile)"
|
||||
systemctl restart pve-cluster >/dev/null 2>&1 || true
|
||||
systemctl restart pvedaemon pveproxy pvestatd >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
mark_done hosts
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: identity - dugume ozel kimlikler
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_identity() {
|
||||
is_done identity && return 0
|
||||
if [ ! -s /etc/iscsi/initiatorname.iscsi ] && command -v iscsi-iname >/dev/null 2>&1; then
|
||||
log "iSCSI initiator adi uretiliyor"
|
||||
mkdir -p /etc/iscsi
|
||||
echo "InitiatorName=$(iscsi-iname)" > /etc/iscsi/initiatorname.iscsi
|
||||
chmod 0600 /etc/iscsi/initiatorname.iscsi
|
||||
systemctl try-restart iscsid open-iscsi >/dev/null 2>&1 || true
|
||||
fi
|
||||
mark_done identity
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: rootpw
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_rootpw() {
|
||||
is_done rootpw && return 0
|
||||
if [ -n "$PVE_ROOT_PASSWORD_HASH" ]; then
|
||||
log "root parolasi (hash) ayarlaniyor"
|
||||
usermod -p "$PVE_ROOT_PASSWORD_HASH" root
|
||||
elif [ -n "$PVE_ROOT_PASSWORD" ]; then
|
||||
log "root parolasi ayarlaniyor"
|
||||
echo "root:${PVE_ROOT_PASSWORD}" | chpasswd
|
||||
else
|
||||
warn "PVE_ROOT_PASSWORD tanimli degil; root@pam ile web arayuzune girilemez"
|
||||
warn "ve bu dugum baska bir dugumun kumeye katilmasi icin hedef olamaz."
|
||||
mark_done rootpw
|
||||
return 0
|
||||
fi
|
||||
mark_done rootpw
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: network - MAAS arayuzunu vmbr0'a cevir
|
||||
# ---------------------------------------------------------------------------
|
||||
detect_net_mode() {
|
||||
[ "$PVE_NET_MODE" != "auto" ] && { echo "$PVE_NET_MODE"; return; }
|
||||
if grep -rqsE '^\s*(dhcp4|dhcp6)\s*:\s*(true|yes)' /etc/netplan/ 2>/dev/null; then
|
||||
echo dhcp; return
|
||||
fi
|
||||
if grep -rqsE '^\s*iface\s+\S+\s+inet6?\s+dhcp' /etc/network/interfaces.d/ 2>/dev/null; then
|
||||
echo dhcp; return
|
||||
fi
|
||||
echo static
|
||||
}
|
||||
|
||||
neutralize_foreign_netconf() {
|
||||
log "MAAS/cloud-init ag yapilandirmasi devre disi birakiliyor"
|
||||
printf 'network: {config: disabled}\n' > /etc/cloud/cloud.cfg.d/99-pve-maas-disable-network.cfg
|
||||
|
||||
local d
|
||||
d=/etc/network/interfaces.d/disabled-by-pve-maas
|
||||
mkdir -p "$d"
|
||||
find /etc/network/interfaces.d -maxdepth 1 -type f -print0 2>/dev/null \
|
||||
| xargs -0 -r -I{} mv {} "$d/"
|
||||
|
||||
if compgen -G "/etc/netplan/*.yaml" >/dev/null || compgen -G "/etc/netplan/*.yml" >/dev/null; then
|
||||
mkdir -p /etc/netplan/disabled-by-pve-maas
|
||||
mv /etc/netplan/*.y*ml /etc/netplan/disabled-by-pve-maas/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if systemctl is-enabled systemd-networkd >/dev/null 2>&1; then
|
||||
systemctl disable --now systemd-networkd systemd-networkd.socket >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Proxmox statik /etc/resolv.conf bekler.
|
||||
if [ -L /etc/resolv.conf ]; then
|
||||
local ns search
|
||||
ns="$(resolvectl dns 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9a-fA-F:.]+$' | sort -u || true)"
|
||||
[ -z "$ns" ] && ns="$(awk '/^nameserver/{print $2}' /etc/resolv.conf | sort -u || true)"
|
||||
search="$(awk '/^(search|domain)[[:space:]]/{$1=""; print substr($0,2); exit}' /etc/resolv.conf || true)"
|
||||
if [ -n "$ns" ]; then
|
||||
rm -f /etc/resolv.conf
|
||||
{
|
||||
[ -n "$search" ] && echo "search ${search}"
|
||||
echo "$ns" | while read -r s; do [ -n "$s" ] && echo "nameserver $s"; done
|
||||
} > /etc/resolv.conf
|
||||
systemctl disable --now systemd-resolved >/dev/null 2>&1 || true
|
||||
else
|
||||
warn "nameserver bulunamadi, /etc/resolv.conf oldugu gibi birakiliyor"
|
||||
fi
|
||||
fi
|
||||
|
||||
systemctl enable networking.service >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
stage_network() {
|
||||
is_done network && return 0
|
||||
[ "$PVE_NET_MANAGE" = "true" ] || { log "ag yonetimi kapali (PVE_NET_MANAGE=false)"; mark_done network; return 0; }
|
||||
|
||||
local br uplink
|
||||
br="$PVE_NET_BRIDGE"
|
||||
uplink="$(primary_iface)"
|
||||
|
||||
if ip link show "$br" >/dev/null 2>&1 && [ "$uplink" = "$br" ]; then
|
||||
log "$br zaten mevcut ve varsayilan rotayi tasiyor, ag donusumu atlaniyor"
|
||||
mark_done network
|
||||
return 0
|
||||
fi
|
||||
[ -n "$uplink" ] || { warn "uplink arayuzu bulunamadi, ag donusumu atlaniyor"; mark_done network; return 0; }
|
||||
|
||||
local mode cidr gw cidr6 gw6
|
||||
mode="$(detect_net_mode)"
|
||||
cidr="$(ip -4 -o addr show dev "$uplink" scope global 2>/dev/null | awk '{print $4; exit}')"
|
||||
gw="$(ip -4 -o route show default dev "$uplink" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="via") {print $(i+1); exit}}')"
|
||||
cidr6="$(ip -6 -o addr show dev "$uplink" scope global -deprecated 2>/dev/null | awk '{print $4; exit}')"
|
||||
gw6="$(ip -6 -o route show default dev "$uplink" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="via") {print $(i+1); exit}}')"
|
||||
|
||||
if [ "$mode" = "static" ] && [ -z "$cidr" ]; then
|
||||
warn "$uplink uzerinde IPv4 adresi yok, DHCP'ye dusuluyor"
|
||||
mode=dhcp
|
||||
fi
|
||||
|
||||
log "ag donusumu: ${uplink} -> ${br} (mod=${mode} adres=${cidr:-dhcp} gw=${gw:-yok})"
|
||||
cp -a /etc/network/interfaces "$STATE_DIR/interfaces.orig" 2>/dev/null || true
|
||||
neutralize_foreign_netconf
|
||||
|
||||
{
|
||||
echo "# /etc/network/interfaces - pve-maas-init tarafindan olusturuldu"
|
||||
echo "# Kaynak yedegi: $STATE_DIR/interfaces.orig"
|
||||
echo
|
||||
echo "auto lo"
|
||||
echo "iface lo inet loopback"
|
||||
echo
|
||||
echo "iface ${uplink} inet manual"
|
||||
echo
|
||||
echo "auto ${br}"
|
||||
if [ "$mode" = "dhcp" ]; then
|
||||
echo "iface ${br} inet dhcp"
|
||||
else
|
||||
echo "iface ${br} inet static"
|
||||
echo " address ${cidr}"
|
||||
[ -n "$gw" ] && echo " gateway ${gw}"
|
||||
fi
|
||||
echo " bridge-ports ${uplink}"
|
||||
echo " bridge-stp off"
|
||||
echo " bridge-fd 0"
|
||||
[ "$PVE_NET_VLAN_AWARE" = "true" ] && {
|
||||
echo " bridge-vlan-aware yes"
|
||||
echo " bridge-vids 2-4094"
|
||||
}
|
||||
if [ -n "$cidr6" ]; then
|
||||
echo
|
||||
echo "iface ${br} inet6 static"
|
||||
echo " address ${cidr6}"
|
||||
[ -n "$gw6" ] && echo " gateway ${gw6}"
|
||||
fi
|
||||
if [ -n "$PVE_NET_EXTRA" ]; then
|
||||
echo
|
||||
echo "# PVE_NET_EXTRA"
|
||||
printf '%s\n' "$PVE_NET_EXTRA"
|
||||
fi
|
||||
echo
|
||||
echo "source /etc/network/interfaces.d/*"
|
||||
} > /etc/network/interfaces
|
||||
|
||||
# Proxmox'un pvenetcommit.service'i acilista /etc/network/interfaces.new
|
||||
# dosyasini interfaces uzerine tasiyor. Kalmis bir .new dosyasi yeni
|
||||
# yazdigimiz yapilandirmayi yeniden baslatmada ezer.
|
||||
rm -f /etc/network/interfaces.new
|
||||
|
||||
mark_done network
|
||||
|
||||
case "$PVE_NET_APPLY" in
|
||||
reload)
|
||||
log "ifreload -a ile uygulaniyor"
|
||||
ip -4 addr flush dev "$uplink" 2>/dev/null || true
|
||||
ip -6 addr flush dev "$uplink" scope global 2>/dev/null || true
|
||||
if ! ifreload -a; then
|
||||
warn "ifreload basarisiz, yedek rota ekleniyor"
|
||||
[ -n "$gw" ] && ip route replace default via "$gw" dev "$br" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
reboot)
|
||||
log "ag yapilandirmasi icin yeniden baslatiliyor; kalan asamalar sonraki acilista surecek"
|
||||
# Bu servis hala calisirken 'systemctl reboot' cagirmak systemd islem
|
||||
# kuyrugunda "transaction is destructive" hatasina yol acabilir; bu
|
||||
# yuzden reboot'u ayri bir gecici unit uzerinden tetikliyoruz.
|
||||
systemd-run --no-block --unit=pve-maas-reboot --on-active=5 \
|
||||
/bin/systemctl reboot \
|
||||
|| shutdown -r +1 "pve-maas-init: ag yapilandirmasi uygulanacak"
|
||||
exit 0
|
||||
;;
|
||||
none)
|
||||
log "PVE_NET_APPLY=none: yapilandirma yazildi, uygulanmadi"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: cluster
|
||||
# ---------------------------------------------------------------------------
|
||||
peer_fingerprint() {
|
||||
local peer="$1"
|
||||
openssl s_client -connect "${peer}:8006" -servername "${peer}" </dev/null 2>/dev/null \
|
||||
| openssl x509 -noout -fingerprint -sha256 2>/dev/null \
|
||||
| cut -d= -f2
|
||||
}
|
||||
|
||||
wait_for_peer() {
|
||||
local peer="$1" timeout="$2" i
|
||||
log "kume dugumu bekleniyor: ${peer}:8006 (en fazla ${timeout}s)"
|
||||
for ((i = 0; i < timeout; i += 5)); do
|
||||
if timeout 4 bash -c "exec 3<>/dev/tcp/${peer}/8006" 2>/dev/null; then
|
||||
log "${peer}:8006 erisilebilir"
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
cluster_password() {
|
||||
if [ -n "$PVE_CLUSTER_PEER_PASSWORD_FILE" ] && [ -r "$PVE_CLUSTER_PEER_PASSWORD_FILE" ]; then
|
||||
head -1 "$PVE_CLUSTER_PEER_PASSWORD_FILE"
|
||||
else
|
||||
printf '%s' "$PVE_CLUSTER_PEER_PASSWORD"
|
||||
fi
|
||||
}
|
||||
|
||||
wipe_cluster_secrets() {
|
||||
[ "$PVE_CLUSTER_WIPE_SECRETS" = "true" ] || return 0
|
||||
local f
|
||||
for f in "$CONF_DIR"/conf.d/*.conf; do
|
||||
[ -r "$f" ] || continue
|
||||
grep -q 'PVE_CLUSTER_PEER_PASSWORD\|PVE_ROOT_PASSWORD' "$f" || continue
|
||||
log "kimlik bilgileri temizleniyor: $f"
|
||||
sed -i -E "s/^([[:space:]]*(PVE_CLUSTER_PEER_PASSWORD|PVE_ROOT_PASSWORD)(_HASH)?=).*/\1'<silindi>'/" "$f"
|
||||
done
|
||||
if [ -n "$PVE_CLUSTER_PEER_PASSWORD_FILE" ] && [ -f "$PVE_CLUSTER_PEER_PASSWORD_FILE" ]; then
|
||||
shred -u "$PVE_CLUSTER_PEER_PASSWORD_FILE" 2>/dev/null \
|
||||
|| rm -f "$PVE_CLUSTER_PEER_PASSWORD_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_quorum() {
|
||||
local i
|
||||
for ((i = 0; i < 180; i += 5)); do
|
||||
if pvecm status 2>/dev/null | grep -qE '^Quorate:[[:space:]]+Yes'; then
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
stage_cluster() {
|
||||
is_done cluster && return 0
|
||||
[ "$PVE_CLUSTER_MODE" = "none" ] && { log "kume islemi yok (PVE_CLUSTER_MODE=none)"; mark_done cluster; return 0; }
|
||||
|
||||
wait_for_pmxcfs || { warn "/etc/pve baglanmadi, kume asamasi erteleniyor"; return 1; }
|
||||
|
||||
if [ -f /etc/pve/corosync.conf ]; then
|
||||
log "dugum zaten bir kumenin parcasi, kume asamasi atlaniyor"
|
||||
mark_done cluster
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -a opts=()
|
||||
[ -n "$PVE_CLUSTER_LINK0" ] && opts+=(--link0 "$PVE_CLUSTER_LINK0")
|
||||
[ -n "$PVE_CLUSTER_LINK1" ] && opts+=(--link1 "$PVE_CLUSTER_LINK1")
|
||||
[ -n "$PVE_CLUSTER_NODEID" ] && opts+=(--nodeid "$PVE_CLUSTER_NODEID")
|
||||
[ -n "$PVE_CLUSTER_VOTES" ] && opts+=(--votes "$PVE_CLUSTER_VOTES")
|
||||
|
||||
case "$PVE_CLUSTER_MODE" in
|
||||
create)
|
||||
[ -n "$PVE_CLUSTER_NAME" ] || { warn "PVE_CLUSTER_NAME bos, kume olusturulamiyor"; return 1; }
|
||||
log "kume olusturuluyor: ${PVE_CLUSTER_NAME}"
|
||||
if pvecm create "$PVE_CLUSTER_NAME" "${opts[@]}"; then
|
||||
wait_for_quorum && log "kume olusturuldu ve quorate"
|
||||
mark_done cluster
|
||||
else
|
||||
warn "pvecm create basarisiz"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
join)
|
||||
[ -n "$PVE_CLUSTER_PEER" ] || { warn "PVE_CLUSTER_PEER bos, katilim yapilamiyor"; return 1; }
|
||||
local pw fp
|
||||
pw="$(cluster_password)"
|
||||
[ -n "$pw" ] || { warn "kume dugumunun root parolasi verilmedi (PVE_CLUSTER_PEER_PASSWORD)"; return 1; }
|
||||
|
||||
wait_for_peer "$PVE_CLUSTER_PEER" "$PVE_CLUSTER_WAIT" \
|
||||
|| { warn "kume dugumu ${PVE_CLUSTER_PEER} zaman asimina ugradi"; return 1; }
|
||||
|
||||
fp="$PVE_CLUSTER_FINGERPRINT"
|
||||
if [ -z "$fp" ] && [ "$PVE_CLUSTER_FINGERPRINT_DISCOVER" = "true" ]; then
|
||||
fp="$(peer_fingerprint "$PVE_CLUSTER_PEER")"
|
||||
warn "parmak izi otomatik alindi (TOFU, dogrulanmadi): ${fp}"
|
||||
fi
|
||||
[ -n "$fp" ] || { warn "kume dugumunun sertifika parmak izi alinamadi"; return 1; }
|
||||
|
||||
local i rc=1
|
||||
for ((i = 1; i <= PVE_CLUSTER_RETRIES; i++)); do
|
||||
log "kumeye katiliniyor (deneme ${i}/${PVE_CLUSTER_RETRIES}): ${PVE_CLUSTER_PEER}"
|
||||
# pvecm add etkilesimli parola sorar; API ucu ile etkilesimsiz calisiyoruz.
|
||||
if pvesh create /cluster/config/join \
|
||||
--hostname "$PVE_CLUSTER_PEER" \
|
||||
--password "$pw" \
|
||||
--fingerprint "$fp" \
|
||||
"${opts[@]}"; then
|
||||
rc=0
|
||||
break
|
||||
fi
|
||||
warn "katilim basarisiz, 30s sonra tekrar denenecek"
|
||||
sleep 30
|
||||
done
|
||||
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
warn "kumeye katilim ${PVE_CLUSTER_RETRIES} denemede basarisiz"
|
||||
return 1
|
||||
fi
|
||||
|
||||
systemctl restart pve-cluster pvedaemon pveproxy pvestatd >/dev/null 2>&1 || true
|
||||
if wait_for_quorum; then
|
||||
log "kumeye katilim tamamlandi ve quorate"
|
||||
else
|
||||
warn "katilim yapildi ama quorum saglanamadi, kume durumunu kontrol edin"
|
||||
fi
|
||||
mark_done cluster
|
||||
wipe_cluster_secrets
|
||||
;;
|
||||
*)
|
||||
warn "gecersiz PVE_CLUSTER_MODE=${PVE_CLUSTER_MODE}"
|
||||
mark_done cluster
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: storage - LVM-thin havuzu
|
||||
# ---------------------------------------------------------------------------
|
||||
pick_vg() {
|
||||
if [ "$PVE_THINPOOL" != "auto" ] && [ "$PVE_THINPOOL" != "off" ]; then
|
||||
echo "$PVE_THINPOOL"; return
|
||||
fi
|
||||
vgs --noheadings --nosuffix --units b -o vg_name,vg_free 2>/dev/null \
|
||||
| awk '{gsub(/^ +/,""); print $2, $1}' | sort -rn | head -1 | awk '{print $2}'
|
||||
}
|
||||
|
||||
stage_storage() {
|
||||
is_done storage && return 0
|
||||
[ "$PVE_THINPOOL" = "off" ] && { log "thin havuz kapali"; mark_done storage; return 0; }
|
||||
|
||||
wait_for_pmxcfs || { warn "/etc/pve baglanmadi, storage asamasi erteleniyor"; return 1; }
|
||||
command -v lvs >/dev/null 2>&1 || { warn "lvm2 yok"; mark_done storage; return 0; }
|
||||
|
||||
local vg="$PVE_THINPOOL_VG"
|
||||
|
||||
if [ -n "$PVE_THINPOOL_DISK" ]; then
|
||||
if [ ! -b "$PVE_THINPOOL_DISK" ]; then
|
||||
warn "PVE_THINPOOL_DISK=${PVE_THINPOOL_DISK} blok aygiti degil"
|
||||
mark_done storage; return 0
|
||||
fi
|
||||
if ! pvs "$PVE_THINPOOL_DISK" >/dev/null 2>&1; then
|
||||
log "PV/VG olusturuluyor: ${PVE_THINPOOL_DISK} -> ${vg}"
|
||||
pvcreate -ff -y "$PVE_THINPOOL_DISK" || { warn "pvcreate basarisiz"; mark_done storage; return 0; }
|
||||
vgcreate "$vg" "$PVE_THINPOOL_DISK" || { warn "vgcreate basarisiz"; mark_done storage; return 0; }
|
||||
fi
|
||||
else
|
||||
vg="$(pick_vg)"
|
||||
fi
|
||||
|
||||
[ -n "$vg" ] || { log "uygun VG bulunamadi, thin havuz atlaniyor"; mark_done storage; return 0; }
|
||||
|
||||
local pool="$PVE_THINPOOL_NAME"
|
||||
if lvs "${vg}/${pool}" >/dev/null 2>&1; then
|
||||
log "${vg}/${pool} zaten var"
|
||||
else
|
||||
local free_b free_g
|
||||
free_b="$(vgs --noheadings --nosuffix --units b -o vg_free "$vg" 2>/dev/null | tr -d ' ')"
|
||||
free_g=$(( ${free_b:-0} / 1024 / 1024 / 1024 ))
|
||||
if [ "$free_g" -lt "$PVE_THINPOOL_MIN_GB" ]; then
|
||||
log "${vg} icinde yalnizca ${free_g}GiB bos alan var (gereken ${PVE_THINPOOL_MIN_GB}GiB), thin havuz atlaniyor"
|
||||
mark_done storage
|
||||
return 0
|
||||
fi
|
||||
log "thin havuz olusturuluyor: ${vg}/${pool} (~${free_g}GiB)"
|
||||
if ! lvcreate --type thin-pool -l 95%FREE -n "$pool" "$vg"; then
|
||||
warn "lvcreate basarisiz"
|
||||
mark_done storage
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local storage="$PVE_THINPOOL_STORAGE" node
|
||||
node="$(hostname -s)"
|
||||
if pvesm status --storage "$storage" >/dev/null 2>&1; then
|
||||
log "storage '${storage}' zaten tanimli"
|
||||
local nodes
|
||||
nodes="$(awk -v s="$storage" '$1=="lvmthin:" && $2==s {f=1; next} /^[a-z]+:/{f=0} f && $1=="nodes"{print $2}' /etc/pve/storage.cfg 2>/dev/null)"
|
||||
if [ -n "$nodes" ] && ! echo ",$nodes," | grep -q ",${node},"; then
|
||||
log "'${storage}' dugum listesine ${node} ekleniyor"
|
||||
pvesm set "$storage" --nodes "${nodes},${node}" || warn "pvesm set basarisiz"
|
||||
fi
|
||||
else
|
||||
log "storage tanimlaniyor: ${storage} (lvmthin ${vg}/${pool})"
|
||||
local -a sopts=(--vgname "$vg" --thinpool "$pool" --content images,rootdir)
|
||||
[ -f /etc/pve/corosync.conf ] && sopts+=(--nodes "$node")
|
||||
pvesm add lvmthin "$storage" "${sopts[@]}" || warn "pvesm add basarisiz"
|
||||
fi
|
||||
|
||||
mark_done storage
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
load_config
|
||||
|
||||
if [ "$PVE_ENABLED" != "true" ]; then
|
||||
log "PVE_ENABLED=false, hicbir sey yapilmiyor"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "baslatiliyor (hostname=$(hostname -s))"
|
||||
|
||||
wait_for_cloud_init
|
||||
load_config # cloud-init conf.d dosyalarini yeni yazmis olabilir
|
||||
|
||||
if ! wait_for_network; then
|
||||
warn "yonetim IP'si bulunamadi; sonraki acilista tekrar denenecek"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local failed=0
|
||||
stage_hosts || failed=1
|
||||
stage_identity || failed=1
|
||||
stage_rootpw || failed=1
|
||||
stage_network || failed=1
|
||||
stage_cluster || failed=1
|
||||
stage_storage || failed=1
|
||||
|
||||
if [ "$failed" -eq 0 ] \
|
||||
&& is_done hosts && is_done identity && is_done rootpw \
|
||||
&& is_done network && is_done cluster && is_done storage; then
|
||||
date -Is > "$STATE_DIR/complete"
|
||||
log "tum asamalar tamamlandi"
|
||||
else
|
||||
warn "bazi asamalar tamamlanmadi; servis sonraki acilista tekrar denenecek"
|
||||
warn "ayrintilar: journalctl -u pve-maas-init"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
285
scripts/customize-proxmox.sh.in
Normal file
285
scripts/customize-proxmox.sh.in
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# customize-proxmox.sh - packer-maas'in debian sablonu icinde, build VM'inde calisir.
|
||||
#
|
||||
# Debian cloud image uzerine Proxmox VE kurar ve MAAS ile deploy edilebilecek
|
||||
# hale getirir. Bu dosya bir sablondur; Makefile @@...@@ yer tutucularini
|
||||
# doldurur ve sonuna base64 kodlu overlay arsivini ekler.
|
||||
#
|
||||
# NOT: Bu script packer tarafindan "expect_disconnect = true" ile calistirilir.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
PVE_SUITE="@@PVE_SUITE@@"
|
||||
PVE_REPO="@@PVE_REPO@@"
|
||||
PVE_REPO_URI="@@PVE_REPO_URI@@"
|
||||
PVE_KEYRING_URL="@@PVE_KEYRING_URL@@"
|
||||
PVE_VERSION="@@PVE_VERSION@@"
|
||||
PVE_EXTRA_PACKAGES="@@PVE_EXTRA_PACKAGES@@"
|
||||
PACKER_MAAS_REF="@@PM_REF@@"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
APT="apt-get -y -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef"
|
||||
|
||||
# eatmydata kurulduktan sonra $APT bunun uzerinden calisir; dpkg'nin her paket
|
||||
# icin yaptigi fsync'ler devre disi kalir. Imaj derlemede guvenli (VM diski
|
||||
# zaten atilabilir) ve paket kurulumunu belirgin sekilde hizlandirir.
|
||||
use_eatmydata() {
|
||||
command -v eatmydata >/dev/null 2>&1 || return 0
|
||||
APT="eatmydata ${APT}"
|
||||
log "eatmydata etkin (dpkg fsync'leri devre disi)"
|
||||
}
|
||||
|
||||
log() { echo "==> [pve-image] $*"; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Gomulu overlay arsivini ac
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Overlay dosyalari aciliyor"
|
||||
sed -n '/^__PVE_MAAS_OVERLAY__$/,$p' "$0" | tail -n +2 | base64 -d \
|
||||
| tar xzf - -C / --no-same-owner --no-same-permissions
|
||||
chown -R root:root /usr/local/sbin/pve-maas-init /etc/pve-maas \
|
||||
/etc/systemd/system/pve-maas-init.service /curtin
|
||||
chmod 0755 /usr/local/sbin/pve-maas-init
|
||||
chmod 0755 /curtin /curtin/curtin-hooks
|
||||
chmod 0644 /etc/systemd/system/pve-maas-init.service
|
||||
chmod 0644 /etc/pve-maas/pve-maas.conf
|
||||
mkdir -p /etc/pve-maas/conf.d /var/lib/pve-maas
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Build sirasinda hostname cozulebilir olmali (pve-cluster bunu ister)
|
||||
# ---------------------------------------------------------------------------
|
||||
BUILD_HOST="$(hostname -s)"
|
||||
BUILD_IP="$(ip -4 -o route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' | head -1)"
|
||||
: "${BUILD_IP:=127.0.1.1}"
|
||||
log "Build hostname=${BUILD_HOST} ip=${BUILD_IP}"
|
||||
cp -a /etc/hosts /etc/hosts.pve-image-backup
|
||||
sed -i "/[[:space:]]${BUILD_HOST}\([[:space:]]\|$\)/d" /etc/hosts
|
||||
echo "${BUILD_IP} ${BUILD_HOST}.local ${BUILD_HOST}" >> /etc/hosts
|
||||
hostname -f || echo "UYARI: hostname -f cozulemedi" >&2
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. packer-maas'in kurdugu MAAS uyumlu cloud-init'i koru
|
||||
#
|
||||
# packer-maas, Debian'in cloud-init'i MAAS datasource'unu tam desteklemedigi
|
||||
# icin Ubuntu build'ini kuruyor. full-upgrade bunu geri almasin.
|
||||
# ---------------------------------------------------------------------------
|
||||
log "cloud-init paketi hold'a aliniyor"
|
||||
apt-mark hold cloud-init || true
|
||||
|
||||
# apt-cacher-ng gibi bir HTTP onbellegi verilmisse Debian depolarini https'ten
|
||||
# http'ye cevir - aksi halde onbellek isabet etmez (CONNECT tunelini cache'leyemez).
|
||||
# Paket imzalari yine dogrulandigi icin guvenlik kaybi yok.
|
||||
if [ -n "${http_proxy:-}" ]; then
|
||||
log "APT onbellegi kullaniliyor (${http_proxy}); Debian depolari http'ye cevriliyor"
|
||||
sed -i 's|https://deb.debian.org|http://deb.debian.org|g; s|https://security.debian.org|http://security.debian.org|g' \
|
||||
/etc/apt/sources.list /etc/apt/sources.list.d/*.sources /etc/apt/sources.list.d/*.list 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log "eatmydata kuruluyor"
|
||||
$APT install eatmydata || true
|
||||
use_eatmydata
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Proxmox APT deposu
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Proxmox anahtarligi indiriliyor: ${PVE_KEYRING_URL}"
|
||||
curl -fsSL --retry 5 --retry-delay 3 "${PVE_KEYRING_URL}" \
|
||||
-o /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
chmod 0644 /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
|
||||
log "APT deposu ekleniyor: ${PVE_REPO_URI} ${PVE_SUITE} ${PVE_REPO}"
|
||||
cat > /etc/apt/sources.list.d/proxmox.sources <<EOF
|
||||
Types: deb
|
||||
URIs: ${PVE_REPO_URI}
|
||||
Suites: ${PVE_SUITE}
|
||||
Components: ${PVE_REPO}
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF
|
||||
|
||||
# Depo dogru mu, paketler gorunuyor mu?
|
||||
apt-get update
|
||||
apt-cache policy proxmox-ve | sed -n '1,6p'
|
||||
if ! apt-cache show proxmox-ve >/dev/null 2>&1; then
|
||||
echo "HATA: proxmox-ve paketi bulunamadi. PVE_SUITE=${PVE_SUITE} PVE_REPO=${PVE_REPO} dogru mu?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Sistemi guncelle
|
||||
# ---------------------------------------------------------------------------
|
||||
log "full-upgrade"
|
||||
$APT full-upgrade
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Etkilesimsiz kurulum icin debconf yanitlari
|
||||
# ---------------------------------------------------------------------------
|
||||
debconf-set-selections <<EOF
|
||||
postfix postfix/main_mailer_type select Local only
|
||||
postfix postfix/mailname string ${BUILD_HOST}.local
|
||||
samba-common samba-common/dhcp boolean false
|
||||
EOF
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Proxmox cekirdegi + proxmox-ve
|
||||
#
|
||||
# Resmi rehber cekirdek kurulumundan sonra reboot onerir; bu reboot yalnizca
|
||||
# ZFS/DKMS gibi calisan cekirdege bagimli islemler icin gereklidir. proxmox-ve
|
||||
# paketleri kurulum sirasinda calisan cekirdege ihtiyac duymaz, bu yuzden imaj
|
||||
# derlerken reboot atlaniyor. Dugum zaten ilk acilista PVE cekirdegi ile acilir.
|
||||
# ---------------------------------------------------------------------------
|
||||
# initramfs, cekirdek/firmware/dkms tetikleyicileriyle kurulum sirasinda
|
||||
# defalarca yeniden uretiliyor. Kurulum boyunca devre disi birakip en sonda
|
||||
# bir kez uretmek birkac dakika kazandiriyor.
|
||||
log "update-initramfs gecici olarak devre disi"
|
||||
dpkg-divert --local --rename --add /usr/sbin/update-initramfs >/dev/null
|
||||
ln -sf /bin/true /usr/sbin/update-initramfs
|
||||
|
||||
log "proxmox-default-kernel kuruluyor"
|
||||
$APT install proxmox-default-kernel
|
||||
|
||||
log "proxmox-ve kuruluyor"
|
||||
$APT install proxmox-ve
|
||||
|
||||
if [ -n "${PVE_EXTRA_PACKAGES}" ]; then
|
||||
log "Ek paketler kuruluyor: ${PVE_EXTRA_PACKAGES}"
|
||||
# shellcheck disable=SC2086
|
||||
$APT install ${PVE_EXTRA_PACKAGES}
|
||||
fi
|
||||
|
||||
log "Kurulan surumler:"
|
||||
dpkg-query -W -f='${Package} ${Version}\n' proxmox-ve pve-manager proxmox-default-kernel ifupdown2 || true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 8. Debian cekirdegini ve os-prober'i kaldir
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Debian cekirdegi ve os-prober kaldiriliyor"
|
||||
$APT purge os-prober || true
|
||||
# Yalnizca Debian'in kendi cekirdeklerini hedefle; Proxmox paketlerine dokunma.
|
||||
DEB_KERNELS="$(dpkg-query -W -f='${Package}\n' 'linux-image*' 2>/dev/null \
|
||||
| grep -E '^linux-image' | grep -vE 'pve|proxmox' || true)"
|
||||
if [ -n "${DEB_KERNELS}" ]; then
|
||||
log "Kaldirilacak: ${DEB_KERNELS}"
|
||||
# shellcheck disable=SC2086
|
||||
$APT purge ${DEB_KERNELS} || true
|
||||
fi
|
||||
$APT autoremove --purge || true
|
||||
|
||||
log "update-initramfs geri aliniyor ve bir kez calistiriliyor"
|
||||
rm -f /usr/sbin/update-initramfs
|
||||
dpkg-divert --local --rename --remove /usr/sbin/update-initramfs >/dev/null
|
||||
update-initramfs -u -k all
|
||||
|
||||
update-grub
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Abonelik gerektiren depolari devre disi birak (varsa)
|
||||
# ---------------------------------------------------------------------------
|
||||
for f in /etc/apt/sources.list.d/*enterprise*; do
|
||||
[ -e "$f" ] || continue
|
||||
log "Devre disi birakiliyor: $f"
|
||||
mv "$f" "$f.disabled"
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 10. Dugum kimligini sifirla
|
||||
#
|
||||
# pmxcfs (/etc/pve) dugum adini config.db icinde saklar. Imaj build hostname'i
|
||||
# ile olusur; MAAS baska bir hostname ile deploy edecegi icin veritabanini
|
||||
# tamamen siliyoruz. pve-cluster ilk acilista guncel hostname ile yeni bir
|
||||
# config.db uretir ve ExecStartPost'taki 'pvecm updatecerts' sertifikalari
|
||||
# yeniden olusturur.
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Proxmox dugum kimligi sifirlaniyor"
|
||||
systemctl stop pve-guests pve-ha-lrm pve-ha-crm pvescheduler pvestatd pveproxy \
|
||||
pvedaemon pve-firewall pvefw-logger spiceproxy corosync pve-cluster 2>/dev/null || true
|
||||
sleep 2
|
||||
umount /etc/pve 2>/dev/null || true
|
||||
rm -f /var/lib/pve-cluster/config.db /var/lib/pve-cluster/config.db-wal \
|
||||
/var/lib/pve-cluster/config.db-shm /var/lib/pve-cluster/.pmxcfs.lockfile
|
||||
rm -rf /etc/corosync/* /var/lib/corosync/*
|
||||
rm -rf /var/lib/rrdcached/db/*
|
||||
rm -f /etc/pve/* 2>/dev/null || true
|
||||
# iSCSI initiator adi dugume ozeldir; ilk acilista yeniden uretilir.
|
||||
rm -f /etc/iscsi/initiatorname.iscsi
|
||||
# Kume icin uretilen SSH bilgileri imaja sizmasin.
|
||||
rm -f /root/.ssh/known_hosts /etc/ssh/ssh_known_hosts
|
||||
find /root/.ssh -type l -delete 2>/dev/null || true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 11. Ilk acilis servisini etkinlestir
|
||||
# ---------------------------------------------------------------------------
|
||||
log "pve-maas-init.service etkinlestiriliyor"
|
||||
systemctl daemon-reload
|
||||
systemctl enable pve-maas-init.service
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 12. Temizlik
|
||||
# ---------------------------------------------------------------------------
|
||||
# Kalici journald: dugum ilk acilista bir kez yeniden baslayabildigi icin
|
||||
# (PVE_NET_APPLY=reboot) ucucu journal ile pve-maas-init kayitlari kayboluyor.
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ilk acilista agi MAAS'a birak
|
||||
#
|
||||
# ifupdown2 kurulumu /etc/network/interfaces dosyasina build VM'inin arayuz
|
||||
# adini yaziyor (or. "iface ens4 inet manual") ve networking.service etkin
|
||||
# geliyor. Deploy edilen dugumde MAAS agi netplan/systemd-networkd ile
|
||||
# yapilandirir; ayni anda ifupdown2 devreye girince gercek arayuzu kapatiyor
|
||||
# ve dugum daha pve-maas-init calismadan agini kaybediyor.
|
||||
#
|
||||
# Cozum: imajda temiz/bos bir interfaces dosyasi birak ve networking.service'i
|
||||
# devre disi birak. pve-maas-init ag asamasinda vmbr0'i yazip bu servisi
|
||||
# kendisi etkinlestirir.
|
||||
# ---------------------------------------------------------------------------
|
||||
log "networking.service devre disi birakiliyor (ilk acilista ag MAAS'ta)"
|
||||
cat > /etc/network/interfaces <<'EOF'
|
||||
# Bu dosya pve-maas-init tarafindan, dugum ilk acilista vmbr0 koprusune
|
||||
# gecirilirken yeniden yazilir. O ana kadar ag MAAS/cloud-init (netplan)
|
||||
# tarafindan yonetilir; bu yuzden burada yalnizca loopback tanimlidir.
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
source /etc/network/interfaces.d/*
|
||||
EOF
|
||||
rm -f /etc/network/interfaces.d/* 2>/dev/null || true
|
||||
# Proxmox, ag degisikliklerini once /etc/network/interfaces.new dosyasina
|
||||
# yaziyor; pvenetcommit.service acilista bunu interfaces uzerine TASIYOR.
|
||||
# Build sirasinda olusan bir .new dosyasi imajda kalirsa dugumun ilk
|
||||
# acilisinda temiz yapilandirmamizi ezer.
|
||||
rm -f /etc/network/interfaces.new
|
||||
systemctl disable networking.service 2>/dev/null || true
|
||||
|
||||
# Imajin kunyesi - dagitilmis bir dugumde "bu hangi imajdan geldi?" sorusunu
|
||||
# cevaplar; 'make check-upstream' de bunu okur.
|
||||
log "Imaj kunyesi yaziliyor: /etc/pve-maas/image-info"
|
||||
{
|
||||
echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "pve_version=@@PVE_VERSION@@"
|
||||
echo "debian_suite=${PVE_SUITE}"
|
||||
echo "pve_repo=${PVE_REPO}"
|
||||
echo "packer_maas_ref=${PACKER_MAAS_REF}"
|
||||
dpkg-query -W -f='${Package}=${Version}\n' proxmox-ve pve-manager proxmox-default-kernel 2>/dev/null
|
||||
echo "kernel=$(ls -1 /boot/vmlinuz-*-pve 2>/dev/null | sed 's|.*/vmlinuz-||' | head -1)"
|
||||
} > /etc/pve-maas/image-info
|
||||
cat /etc/pve-maas/image-info
|
||||
|
||||
log "Kalici journald etkinlestiriliyor"
|
||||
mkdir -p /var/log/journal
|
||||
systemd-tmpfiles --create --prefix /var/log/journal 2>/dev/null || true
|
||||
|
||||
log "Temizlik"
|
||||
mv /etc/hosts.pve-image-backup /etc/hosts
|
||||
rm -f /etc/postfix/main.cf.proto 2>/dev/null || true
|
||||
[ -f /etc/postfix/main.cf ] && postconf -e "myhostname = localhost" >/dev/null 2>&1 || true
|
||||
$APT clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
find /var/log/journal -mindepth 1 -delete 2>/dev/null || true
|
||||
rm -rf /var/log/*.gz /var/log/*.1
|
||||
: > /var/log/wtmp || true
|
||||
: > /var/log/btmp || true
|
||||
cloud-init clean --logs || true
|
||||
|
||||
log "Imaj hazir: Proxmox VE ${PVE_VERSION} / Debian ${PVE_SUITE}"
|
||||
exit 0
|
||||
|
||||
# Bu satirdan sonrasi Makefile tarafindan eklenen base64 overlay arsividir.
|
||||
283
scripts/deploy-cluster.sh
Executable file
283
scripts/deploy-cluster.sh
Executable file
@@ -0,0 +1,283 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# deploy-cluster.sh - MAAS uzerinden komple bir Proxmox VE kumesi kurar.
|
||||
#
|
||||
# Ilk dugumu 'create' modunda deploy eder, ayaga kalkmasini bekler, sertifika
|
||||
# parmak izini okur ve kalan dugumleri o parmak iziyle 'join' modunda deploy eder.
|
||||
#
|
||||
# NEREDE CALISTIRILIR: MAAS region controller uzerinde (ya da 'maas' CLI profili
|
||||
# tanimli ve dugumlerin 8006 portuna erisebilen bir makinede).
|
||||
#
|
||||
# Ornek:
|
||||
# ./deploy-cluster.sh --name pve-prod --nodes pve1,pve2,pve3
|
||||
# ./deploy-cluster.sh --name pve-dr --nodes dr1,dr2 --profile admin --dry-run
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
PROFILE=admin
|
||||
CLUSTER_NAME=""
|
||||
NODES=""
|
||||
DISTRO_SERIES=proxmox-ve-9
|
||||
ROOT_PASSWORD=""
|
||||
ROOT_PASSWORD_HASH=""
|
||||
THINPOOL=auto
|
||||
THINPOOL_MIN_GB=16
|
||||
NET_APPLY=reboot
|
||||
LINK0_PREFIX=""
|
||||
DEPLOY_TIMEOUT=2400
|
||||
PEER_TIMEOUT=1200
|
||||
SERIAL=false
|
||||
DRY_RUN=false
|
||||
|
||||
usage() {
|
||||
# Bastaki yorum blogunu, ilk kod satirina kadar bas.
|
||||
awk 'NR>1 && /^#/{sub(/^# ?/,""); print; next} NR>1 && !/^#/{exit}' "$0"
|
||||
cat <<EOF
|
||||
|
||||
Secenekler:
|
||||
--name <ad> Kume adi (zorunlu)
|
||||
--nodes <a,b,c> MAAS hostname listesi; ILKI kumeyi olusturur (zorunlu)
|
||||
--profile <ad> maas CLI profili (varsayilan: ${PROFILE})
|
||||
--distro-series <ad> MAAS ozel imaj adi (varsayilan: ${DISTRO_SERIES})
|
||||
--root-password <p> root@pam parolasi. Verilmezse uretilir ve ekrana yazilir.
|
||||
--thinpool <deger> auto | off | <vg-adi> (varsayilan: ${THINPOOL})
|
||||
--thinpool-min-gb <n> Thin havuz icin gereken en az bos alan (varsayilan: ${THINPOOL_MIN_GB})
|
||||
--net-apply <mod> reboot | reload | none (varsayilan: ${NET_APPLY})
|
||||
--link0-prefix <cidr> Corosync link0 icin ayri ag oneki, or. 10.10.20.
|
||||
Dugumun o agdaki adresi otomatik bulunur.
|
||||
--serial Katilan dugumleri teker teker deploy et (buyuk kumelerde)
|
||||
--dry-run Hicbir sey deploy etme, uretilecek user-data'yi goster
|
||||
-h, --help Bu yardim
|
||||
|
||||
Notlar:
|
||||
* Kume dugumunun root parolasi API dogrulamasi icin DUZ METIN olarak
|
||||
user-data'ya girer ve MAAS'ta saklanir. Kisa omurlu bir parola kullanip
|
||||
kurulumdan sonra degistirin.
|
||||
* Ayni imaj ve preseed ile birden fazla bagimsiz kume kurabilirsiniz;
|
||||
kume kimligi yalnizca bu user-data'dan gelir.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--name) CLUSTER_NAME="$2"; shift 2 ;;
|
||||
--nodes) NODES="$2"; shift 2 ;;
|
||||
--profile) PROFILE="$2"; shift 2 ;;
|
||||
--distro-series) DISTRO_SERIES="$2"; shift 2 ;;
|
||||
--root-password) ROOT_PASSWORD="$2"; shift 2 ;;
|
||||
--thinpool) THINPOOL="$2"; shift 2 ;;
|
||||
--thinpool-min-gb) THINPOOL_MIN_GB="$2"; shift 2 ;;
|
||||
--net-apply) NET_APPLY="$2"; shift 2 ;;
|
||||
--link0-prefix) LINK0_PREFIX="$2"; shift 2 ;;
|
||||
--serial) SERIAL=true; shift ;;
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Bilinmeyen secenek: $1" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$CLUSTER_NAME" ] || { echo "HATA: --name zorunlu" >&2; exit 2; }
|
||||
[ -n "$NODES" ] || { echo "HATA: --nodes zorunlu" >&2; exit 2; }
|
||||
|
||||
for c in maas jq openssl base64; do
|
||||
command -v "$c" >/dev/null || { echo "HATA: '$c' bulunamadi" >&2; exit 1; }
|
||||
done
|
||||
|
||||
IFS=',' read -r -a NODE_LIST <<< "$NODES"
|
||||
FIRST="${NODE_LIST[0]}"
|
||||
JOINERS=("${NODE_LIST[@]:1}")
|
||||
|
||||
log() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
|
||||
info() { printf ' %s\n' "$*"; }
|
||||
die() { printf '\n\033[1;31mHATA: %s\033[0m\n' "$*" >&2; exit 1; }
|
||||
|
||||
# ---------------------------------------------------------------- kimlik bilgileri
|
||||
if [ -z "$ROOT_PASSWORD" ]; then
|
||||
ROOT_PASSWORD="$(openssl rand -base64 15 | tr -d '/+=' | head -c 16)"
|
||||
GENERATED=true
|
||||
else
|
||||
GENERATED=false
|
||||
fi
|
||||
ROOT_PASSWORD_HASH="$(openssl passwd -6 "$ROOT_PASSWORD")"
|
||||
|
||||
# ---------------------------------------------------------------- MAAS yardimcilari
|
||||
maas_get() { maas "$PROFILE" machines read hostname="$1" 2>/dev/null | jq -r ".[0].$2 // empty"; }
|
||||
|
||||
require_ready() {
|
||||
local h st
|
||||
for h in "${NODE_LIST[@]}"; do
|
||||
st="$(maas_get "$h" status_name)"
|
||||
[ -n "$st" ] || die "MAAS'ta '$h' adinda makine yok"
|
||||
[ "$st" = "Ready" ] || die "'$h' durumu '$st' - deploy icin 'Ready' olmali (once release edin)"
|
||||
info "$h: Ready"
|
||||
done
|
||||
}
|
||||
|
||||
wait_status() {
|
||||
local h="$1" want="$2" timeout="$3" waited=0 st
|
||||
while [ "$waited" -lt "$timeout" ]; do
|
||||
st="$(maas_get "$h" status_name)"
|
||||
case "$st" in
|
||||
"$want") return 0 ;;
|
||||
Failed*|Broken*) die "'$h' durumu '$st' - MAAS olaylarina bakin" ;;
|
||||
esac
|
||||
sleep 20; waited=$((waited + 20))
|
||||
done
|
||||
die "'$h' ${timeout}s icinde '$want' durumuna gelmedi (son durum: ${st:-bilinmiyor})"
|
||||
}
|
||||
|
||||
node_ip() { maas "$PROFILE" machines read hostname="$1" 2>/dev/null | jq -r '.[0].ip_addresses[0] // empty'; }
|
||||
|
||||
wait_port() {
|
||||
local ip="$1" port="$2" timeout="$3" waited=0
|
||||
while [ "$waited" -lt "$timeout" ]; do
|
||||
if timeout 4 bash -c "exec 3<>/dev/tcp/${ip}/${port}" 2>/dev/null; then return 0; fi
|
||||
sleep 10; waited=$((waited + 10))
|
||||
done
|
||||
die "${ip}:${port} ${timeout}s icinde acilmadi"
|
||||
}
|
||||
|
||||
fingerprint_of() {
|
||||
openssl s_client -connect "$1:8006" -servername "$1" </dev/null 2>/dev/null \
|
||||
| openssl x509 -noout -fingerprint -sha256 2>/dev/null | cut -d= -f2
|
||||
}
|
||||
|
||||
# Dugumun link0 oneki ile eslesen adresini MAAS'tan bul.
|
||||
link0_of() {
|
||||
[ -n "$LINK0_PREFIX" ] || return 0
|
||||
maas "$PROFILE" machines read hostname="$1" 2>/dev/null \
|
||||
| jq -r --arg p "$LINK0_PREFIX" '.[0].ip_addresses[]? | select(startswith($p))' | head -1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- user-data uretimi
|
||||
render_userdata() {
|
||||
local mode="$1" host="$2" peer="${3:-}" fp="${4:-}" link0
|
||||
link0="$(link0_of "$host")"
|
||||
|
||||
cat <<EOF
|
||||
#cloud-config
|
||||
# ${host} - kume '${CLUSTER_NAME}' (${mode})
|
||||
# deploy-cluster.sh tarafindan uretildi
|
||||
write_files:
|
||||
- path: /etc/pve-maas/conf.d/50-pve.conf
|
||||
permissions: "0600"
|
||||
owner: root:root
|
||||
content: |
|
||||
PVE_ROOT_PASSWORD_HASH='${ROOT_PASSWORD_HASH}'
|
||||
PVE_NET_APPLY=${NET_APPLY}
|
||||
PVE_THINPOOL=${THINPOOL}
|
||||
PVE_THINPOOL_MIN_GB=${THINPOOL_MIN_GB}
|
||||
EOF
|
||||
|
||||
if [ "$mode" = "create" ]; then
|
||||
cat <<EOF
|
||||
PVE_CLUSTER_MODE=create
|
||||
PVE_CLUSTER_NAME=${CLUSTER_NAME}
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
PVE_CLUSTER_MODE=join
|
||||
PVE_CLUSTER_PEER=${peer}
|
||||
PVE_CLUSTER_PEER_PASSWORD='${ROOT_PASSWORD}'
|
||||
PVE_CLUSTER_FINGERPRINT='${fp}'
|
||||
EOF
|
||||
fi
|
||||
[ -n "$link0" ] && echo " PVE_CLUSTER_LINK0=${link0}"
|
||||
return 0
|
||||
}
|
||||
|
||||
deploy_node() {
|
||||
local host="$1" ud="$2" sid
|
||||
sid="$(maas_get "$host" system_id)"
|
||||
maas "$PROFILE" machine deploy "$sid" \
|
||||
osystem=custom "distro_series=${DISTRO_SERIES}" \
|
||||
"user_data=$(printf '%s' "$ud" | base64 -w0)" >/dev/null
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- akis
|
||||
log "Kume: ${CLUSTER_NAME}"
|
||||
info "ilk dugum (create) : ${FIRST}"
|
||||
info "katilanlar (join) : ${JOINERS[*]:-yok}"
|
||||
info "imaj : custom/${DISTRO_SERIES}"
|
||||
[ -n "$LINK0_PREFIX" ] && info "corosync link0 oneki: ${LINK0_PREFIX}"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log "DRY RUN - hicbir sey deploy edilmiyor"
|
||||
echo "--- ${FIRST} (create) ---"
|
||||
render_userdata create "$FIRST"
|
||||
for h in "${JOINERS[@]:-}"; do
|
||||
[ -n "$h" ] || continue
|
||||
echo "--- ${h} (join) ---"
|
||||
render_userdata join "$h" "<ilk-dugumun-IP-si>" "<parmak-izi>"
|
||||
done
|
||||
echo
|
||||
echo "root@pam parolasi: ${ROOT_PASSWORD}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Makineler kontrol ediliyor"
|
||||
require_ready
|
||||
|
||||
log "${FIRST} deploy ediliyor (kume olusturuluyor)"
|
||||
deploy_node "$FIRST" "$(render_userdata create "$FIRST")"
|
||||
wait_status "$FIRST" Deployed "$DEPLOY_TIMEOUT"
|
||||
info "MAAS deploy tamamlandi"
|
||||
|
||||
FIRST_IP="$(node_ip "$FIRST")"
|
||||
[ -n "$FIRST_IP" ] || die "'$FIRST' icin IP bulunamadi"
|
||||
info "IP: ${FIRST_IP}"
|
||||
|
||||
info "Proxmox arayuzunun acilmasi bekleniyor (ag donusumu icin bir kez yeniden baslar)"
|
||||
wait_port "$FIRST_IP" 8006 "$PEER_TIMEOUT"
|
||||
|
||||
FP="$(fingerprint_of "$FIRST_IP")"
|
||||
[ -n "$FP" ] || die "'$FIRST' sertifika parmak izi okunamadi"
|
||||
info "parmak izi: ${FP}"
|
||||
|
||||
if [ "${#JOINERS[@]}" -eq 0 ] || [ -z "${JOINERS[0]:-}" ]; then
|
||||
log "Katilacak baska dugum yok"
|
||||
else
|
||||
log "Katilan dugumler deploy ediliyor"
|
||||
for h in "${JOINERS[@]}"; do
|
||||
info "deploy: $h"
|
||||
deploy_node "$h" "$(render_userdata join "$h" "$FIRST_IP" "$FP")"
|
||||
if [ "$SERIAL" = true ]; then
|
||||
wait_status "$h" Deployed "$DEPLOY_TIMEOUT"
|
||||
info "$h: MAAS deploy tamamlandi, kumeye katilmasi bekleniyor"
|
||||
sleep 60
|
||||
fi
|
||||
done
|
||||
[ "$SERIAL" = false ] && {
|
||||
for h in "${JOINERS[@]}"; do wait_status "$h" Deployed "$DEPLOY_TIMEOUT"; info "$h: deploy tamamlandi"; done
|
||||
}
|
||||
fi
|
||||
|
||||
log "Kume durumu bekleniyor"
|
||||
EXPECTED="${#NODE_LIST[@]}"
|
||||
# Tum dugumlerin 8006'da yanit vermesini bekle - kume uyeligini asagidaki
|
||||
# talimatla dugum uzerinden dogrulayin (bu script dugumlere SSH yapmaz).
|
||||
for h in "${NODE_LIST[@]}"; do
|
||||
ip="$(node_ip "$h")"
|
||||
if [ -n "$ip" ] && wait_port "$ip" 8006 300 2>/dev/null; then
|
||||
info "$h ($ip): Proxmox arayuzu acik"
|
||||
else
|
||||
info "$h: 8006 acilmadi - 'journalctl -u pve-maas-init -b' ile bakin"
|
||||
fi
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Kume '${CLUSTER_NAME}' kuruldu.
|
||||
|
||||
Ilk dugum : ${FIRST} (${FIRST_IP})
|
||||
Web arayuzu : https://${FIRST_IP}:8006/
|
||||
Kullanici : root@pam
|
||||
Parola : ${ROOT_PASSWORD}$([ "$GENERATED" = true ] && echo " <- uretildi, kaydedin")
|
||||
|
||||
Dogrulamak icin bir dugumde:
|
||||
pvecm status # 'Nodes: ${EXPECTED}' ve 'Quorate: Yes' bekleniyor
|
||||
pvecm nodes
|
||||
|
||||
Dugumler kumeye katilmadiysa ilgili dugumde:
|
||||
journalctl -u pve-maas-init -b
|
||||
EOF
|
||||
55
scripts/install-deps.sh
Executable file
55
scripts/install-deps.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# install-deps.sh - Ubuntu 22.04+ build host'una packer-maas bagimliliklarini kurar.
|
||||
#
|
||||
# Kullanim: sudo ./scripts/install-deps.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Bu script root olarak calistirilmali: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. /etc/os-release
|
||||
if [ "${ID:-}" != "ubuntu" ] && [ "${ID_LIKE:-}" != "debian" ]; then
|
||||
echo "UYARI: Bu script Ubuntu/Debian icin yazildi (bulunan: ${PRETTY_NAME:-bilinmiyor})." >&2
|
||||
fi
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
echo "==> Temel paketler kuruluyor"
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl gpg git make parted pigz jq \
|
||||
qemu-system-x86 qemu-utils ovmf cloud-image-utils \
|
||||
libnbd-bin nbdkit fuse2fs cpu-checker
|
||||
|
||||
echo "==> HashiCorp APT deposu ekleniyor (packer)"
|
||||
install -d -m 0755 /etc/apt/keyrings
|
||||
curl -fsSL https://apt.releases.hashicorp.com/gpg \
|
||||
| gpg --dearmor --yes -o /etc/apt/keyrings/hashicorp-archive-keyring.gpg
|
||||
chmod 0644 /etc/apt/keyrings/hashicorp-archive-keyring.gpg
|
||||
cat > /etc/apt/sources.list.d/hashicorp.list <<REPO
|
||||
deb [signed-by=/etc/apt/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com ${UBUNTU_CODENAME:-${VERSION_CODENAME}} main
|
||||
REPO
|
||||
|
||||
apt-get update
|
||||
apt-get install -y packer
|
||||
|
||||
echo "==> KVM kontrolu"
|
||||
if ! kvm-ok; then
|
||||
echo "HATA: KVM kullanilamiyor. VM'de nested virtualization acik mi? (Proxmox: cpu=host)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build root olarak kosuyor ama kullaniciyi da kvm grubuna alalim.
|
||||
TARGET_USER="${SUDO_USER:-}"
|
||||
if [ -n "$TARGET_USER" ] && [ "$TARGET_USER" != "root" ]; then
|
||||
adduser "$TARGET_USER" kvm >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==> Hazir. Surumler:"
|
||||
packer version
|
||||
qemu-system-x86_64 --version | head -1
|
||||
99
scripts/verify-image.sh
Executable file
99
scripts/verify-image.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# verify-image.sh - Uretilen MAAS tgz imajinin beklenen icerige sahip oldugunu dogrular.
|
||||
#
|
||||
# Kullanim: ./scripts/verify-image.sh build/proxmox-ve-9.tar.gz
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
IMG="${1:-}"
|
||||
[ -n "$IMG" ] && [ -f "$IMG" ] || { echo "Kullanim: $0 <imaj.tar.gz>" >&2; exit 2; }
|
||||
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
echo "==> Imaj: $IMG ($(du -h "$IMG" | cut -f1))"
|
||||
echo "==> Icerik listesi cikariliyor..."
|
||||
tar tzf "$IMG" > "$TMP/list" || { echo "HATA: arsiv okunamadi"; exit 1; }
|
||||
echo " $(wc -l < "$TMP/list") giris"
|
||||
|
||||
pass=0; fail=0
|
||||
have() { grep -qx "\./$1" "$TMP/list" || grep -q "^\./$1$" "$TMP/list"; }
|
||||
present() { grep -q "^\./$1" "$TMP/list"; }
|
||||
|
||||
check() {
|
||||
local desc="$1" cond="$2"
|
||||
if eval "$cond"; then
|
||||
printf ' [ OK ] %s\n' "$desc"; pass=$((pass+1))
|
||||
else
|
||||
printf ' [FAIL] %s\n' "$desc"; fail=$((fail+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo
|
||||
echo "==> Dosya varligi kontrolleri"
|
||||
check "pve-maas-init mevcut" 'present "usr/local/sbin/pve-maas-init"'
|
||||
check "pve-maas.conf mevcut" 'present "etc/pve-maas/pve-maas.conf"'
|
||||
check "systemd unit mevcut" 'present "etc/systemd/system/pve-maas-init.service"'
|
||||
check "unit multi-user.target icin etkin" 'present "etc/systemd/system/multi-user.target.wants/pve-maas-init.service"'
|
||||
check "Proxmox APT deposu mevcut" 'present "etc/apt/sources.list.d/proxmox.sources"'
|
||||
check "Proxmox anahtarligi mevcut" 'present "usr/share/keyrings/proxmox-archive-keyring.gpg"'
|
||||
check "pveproxy ikilisi mevcut" 'present "usr/bin/pveproxy"'
|
||||
check "pvecm ikilisi mevcut" 'present "usr/bin/pvecm"'
|
||||
check "pvesh ikilisi mevcut" 'present "usr/bin/pvesh"'
|
||||
check "ifupdown2 mevcut" 'present "usr/share/ifupdown2"'
|
||||
check "cloud-init mevcut" 'present "usr/bin/cloud-init"'
|
||||
check "curtin-hooks mevcut" 'present "curtin/curtin-hooks"'
|
||||
|
||||
echo
|
||||
echo "==> Olmamasi gerekenler"
|
||||
check "pmxcfs config.db yok (dugum kimligi temiz)" '! present "var/lib/pve-cluster/config.db"'
|
||||
check "corosync yapilandirmasi yok" '! present "etc/corosync/corosync.conf"'
|
||||
check "iSCSI initiator adi yok (dugumde uretilir)" '! present "etc/iscsi/initiatorname.iscsi"'
|
||||
check "SSH host anahtarlari yok" '! grep -qE "^\./etc/ssh/ssh_host_.*_key$" "$TMP/list"'
|
||||
check "networking.service etkin DEGIL" '! present "etc/systemd/system/multi-user.target.wants/networking.service"'
|
||||
check "interfaces.new yok (pvenetcommit ezmesin)" '! present "etc/network/interfaces.new"'
|
||||
check "Debian cekirdegi yok" '! grep -qE "^\./boot/vmlinuz-.*[^e]-(cloud-)?amd64$" "$TMP/list"'
|
||||
|
||||
echo
|
||||
echo "==> Proxmox cekirdegi"
|
||||
if grep -qE '^\./boot/vmlinuz-.*-pve$' "$TMP/list"; then
|
||||
printf ' [ OK ] PVE cekirdegi: %s\n' "$(grep -oE 'vmlinuz-[^ ]*-pve' "$TMP/list" | head -1)"
|
||||
pass=$((pass+1))
|
||||
else
|
||||
printf ' [FAIL] /boot altinda *-pve cekirdegi bulunamadi\n'; fail=$((fail+1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==> Ayiklanan dosya icerikleri"
|
||||
tar xzf "$IMG" -C "$TMP" \
|
||||
./etc/apt/sources.list.d/proxmox.sources \
|
||||
./usr/local/sbin/pve-maas-init \
|
||||
./etc/pve-maas/pve-maas.conf 2>/dev/null
|
||||
|
||||
tar xzf "$IMG" -C "$TMP" ./etc/network/interfaces 2>/dev/null
|
||||
if [ -f "$TMP/etc/network/interfaces" ]; then
|
||||
echo "--- /etc/network/interfaces ---"
|
||||
sed 's/^/ /' "$TMP/etc/network/interfaces"
|
||||
if grep -qE '^\s*(auto|iface)\s+(?!lo)' "$TMP/etc/network/interfaces" 2>/dev/null \
|
||||
|| grep -qE '^[[:space:]]*iface[[:space:]]+[^l ]' "$TMP/etc/network/interfaces"; then
|
||||
printf ' [FAIL] interfaces dosyasinda build VM artigi arayuz var\n'; fail=$((fail+1))
|
||||
else
|
||||
printf ' [ OK ] interfaces yalnizca loopback iceriyor\n'; pass=$((pass+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "$TMP/etc/apt/sources.list.d/proxmox.sources" ]; then
|
||||
echo "--- proxmox.sources ---"
|
||||
sed 's/^/ /' "$TMP/etc/apt/sources.list.d/proxmox.sources"
|
||||
fi
|
||||
|
||||
if [ -x "$TMP/usr/local/sbin/pve-maas-init" ]; then
|
||||
printf ' [ OK ] pve-maas-init calistirilabilir\n'; pass=$((pass+1))
|
||||
else
|
||||
printf ' [FAIL] pve-maas-init calistirilabilir degil\n'; fail=$((fail+1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "==> Sonuc: ${pass} basarili, ${fail} basarisiz"
|
||||
[ "$fail" -eq 0 ]
|
||||
Reference in New Issue
Block a user