SiamCafe · Blog
Proxmox VE Cluster GitOps Workflow — จัดการ
บทความ

Proxmox VE Cluster GitOps Workflow — จัดการ

เผยแพร่ 28 พฤษภาคม 2569

Proxmox VE GitOps

Proxmox VE Cluster GitOps Terraform Ansible CI/CD KVM LXC High Availability Ceph Storage Backup PBS Infrastructure as Code Production

ComponentToolPurposeGitOps Role
VM ProvisioningTerraformสร้าง VM LXC จาก CodeDeclarative state
ConfigurationAnsibleติดตั้ง Software ตั้งค่าIdempotent config
CI/CDGitHub ActionsPlan Apply อัตโนมัติAutomated workflow
StateTerraform Stateเก็บ State ปัจจุบันSource of truth
Version ControlGitTrack ทุกการเปลี่ยนแปลงAudit trail
BackupPBSBackup VM LXCDR strategy

Terraform Proxmox

=== Terraform Proxmox Provider ===

terraform {

required_providers {

proxmox = {

source = "telmate/proxmox"

version = "3.0.1-rc1"

}

}

backend "s3" {

bucket = "terraform-state"

key = "proxmox/terraform.tfstate"

region = "ap-southeast-1"

}

}

provider "proxmox" {

pm_api_url = "https://pve1.local:8006/api2/json"

pm_user = "terraform@pam"

pm_password = var.proxmox_password

pm_tls_insecure = true

}

resource "proxmox_vm_qemu" "web_server" {

count = 3

name = "web-"

target_node = "pve"

clone = "ubuntu-22.04-template"

cores = 4

memory = 8192

balloon = 4096

scsihw = "virtio-scsi-single"

disk {

storage = "ceph-pool"

size = "50G"

type = "scsi"

ssd = 1

}

network {

model = "virtio"

bridge = "vmbr0"

tag = 100

}

ipconfig0 = "ip=10.0.1./24, gw=10.0.1.1"

sshkeys = file("~/.ssh/id_rsa.pub")

lifecycle {

ignore_changes = [network]

}

}

resource "proxmox_lxc" "db_server" {

hostname = "db-1"

target_node = "pve1"

ostemplate = "local:vztmpl/ubuntu-22.04-standard.tar.zst"

cores = 4

memory = 4096

swap = 1024

unprivileged = true

rootfs {

storage = "ceph-pool"

size = "30G"

}

network {

name = "eth0"

bridge = "vmbr0"

ip = "10.0.1.50/24"

gw = "10.0.1.1"

}

}

from dataclasses import dataclass

@dataclass

class VMSpec:

name: str

role: str

cores: int

ram_gb: int

disk_gb: int

node: str

ha: bool

vms = [

VMSpec("web-1", "Web Server", 4, 8, 50, "pve1", True),

VMSpec("web-2", "Web Server", 4, 8, 50, "pve2", True),

VMSpec("web-3", "Web Server", 4, 8, 50, "pve3", True),

VMSpec("db-1", "PostgreSQL Primary", 8, 32, 200, "pve1", True),

VMSpec("db-2", "PostgreSQL Replica", 8, 32, 200, "pve2", True),

VMSpec("monitor-1", "Prometheus + Grafana", 4, 8, 100, "pve3", False),

VMSpec("backup-1", "PBS Server", 4, 16, 500, "pve3", False),

]

print("=== Infrastructure VMs ===")

for v in vms:

print(f" [{v.name}] Role: {v.role} | Node: {v.node}")

print(f" CPU: {v.cores} cores | RAM: {v.ram_gb}GB | Disk: {v.disk_gb}GB | HA: {v.ha}")

Ansible Configuration

=== Ansible Playbooks ===

inventory/hosts.yml

all:

children:

web_servers:

hosts:

web-1: { ansible_host: 10.0.1.10 }

web-2: { ansible_host: 10.0.1.11 }

web-3: { ansible_host: 10.0.1.12 }

db_servers:

hosts:

db-1: { ansible_host: 10.0.1.50 }

db-2: { ansible_host: 10.0.1.51 }

playbooks/web-server.yml

  • hosts: web_servers

become: yes

roles:

  • common
  • nginx
  • node-exporter

tasks:

  • name: Install packages

apt:

name: [nginx, certbot, python3-certbot-nginx]

state: present

update_cache: yes

  • name: Deploy nginx config

template:

src: nginx.conf.j2

dest: /etc/nginx/sites-available/default

notify: restart nginx

  • name: Enable firewall rules

ufw:

rule: allow

port: "{{ item }}"

loop: ["80", "443", "9100"]

Proxmox Cluster Commands

pvecm create my-cluster # Create cluster

pvecm add 10.0.1.2 # Add node

pvecm status # Check cluster status

pvecm expected 1 # Set expected votes (emergency)

ha-manager set vm:100 --state started # Enable HA for VM

ha-manager groupadd ha-group --nodes pve1, pve2, pve3

@dataclass

class ClusterCommand:

command: str

purpose: str

when: str

commands = [

ClusterCommand("pvecm create cluster-name", "สร้าง Cluster ใหม่", "ครั้งแรก Node แรก"),

ClusterCommand("pvecm add 10.0.1.1", "เพิ่ม Node เข้า Cluster", "Node ใหม่ทุกตัว"),

ClusterCommand("pvecm status", "ดู Cluster Status", "ตรวจสอบปกติ"),

ClusterCommand("pvecm nodes", "ดู Nodes ทั้งหมด", "ตรวจสอบ"),

ClusterCommand("ha-manager set vm:100 --state started", "เปิด HA สำหรับ VM", "VM สำคัญ"),

ClusterCommand("ceph status", "ดู Ceph Storage Status", "ตรวจสอบ Storage"),

ClusterCommand("qm migrate 100 pve2", "ย้าย VM ไป Node อื่น", "Maintenance"),

ClusterCommand("vzdump 100 --storage pbs", "Backup VM ไป PBS", "Backup"),

]

print("\n=== Proxmox Commands ===")

for c in commands:

print(f" [{c.command}]")

print(f" Purpose: {c.purpose} | When: {c.when}")

CI/CD Pipeline

=== GitHub Actions GitOps Pipeline ===

.github/workflows/proxmox-gitops.yml

name: Proxmox GitOps

on:

pull_request:

paths: ['terraform/**', 'ansible/**']

push:

branches: [main]

paths: ['terraform/**', 'ansible/**']

jobs:

terraform-plan:

if: github.event_name == 'pull_request'

runs-on: self-hosted

steps:

  • uses: actions/checkout@v4
  • uses: hashicorp/setup-terraform@v3
  • run: terraform init

working-directory: terraform/

  • run: terraform plan -out=plan.tfplan

working-directory: terraform/

  • uses: actions/github-script@v7

with:

script: |

const plan = require('fs').readFileSync('terraform/plan.txt', 'utf8');

github.rest.issues.createComment({

owner: context.repo.owner,

repo: context.repo.repo,

issue_number: context.issue.number,

body: '```\n' + plan + '\n```'

});

terraform-apply:

if: github.ref == 'refs/heads/main' && github.event_name == 'push'

runs-on: self-hosted

steps:

  • uses: actions/checkout@v4
  • run: terraform init && terraform apply -auto-approve

working-directory: terraform/

ansible-configure:

needs: terraform-apply

runs-on: self-hosted

steps:

  • uses: actions/checkout@v4
  • run: ansible-playbook -i inventory/hosts.yml playbooks/site.yml

@dataclass

class GitOpsStep:

step: str

trigger: str

action: str

safety: str

steps = [

GitOpsStep("1. Developer creates PR", "Code push", "terraform plan runs automatically",

"Plan output posted as PR comment"),

GitOpsStep("2. Team reviews PR", "PR created", "Review Terraform plan + Ansible changes",

"Approval required from 2 reviewers"),

GitOpsStep("3. Merge to main", "PR approved", "terraform apply runs automatically",

"State locked during apply"),

GitOpsStep("4. Ansible configure", "After Terraform", "ansible-playbook runs on new VMs",

"Idempotent — safe to re-run"),

GitOpsStep("5. Health check", "After Ansible", "Verify VMs running + services healthy",

"Auto-rollback if health check fails"),

]

print("GitOps Workflow:")

for s in steps:

print(f" [{s.step}]")

print(f" Trigger: {s.trigger} | Action: {s.action}")

print(f" Safety: {s.safety}")

เคล็ดลับ

  • Template: สร้าง VM Template สำหรับ Clone เร็วกว่าติดตั้งใหม่
  • Ceph: ใช้ Ceph Storage สำหรับ Shared Storage HA ย้าย VM ได้
  • State: เก็บ Terraform State ใน Remote Backend เช่น S3
  • PR Review: ทุกการเปลี่ยนแปลงต้องผ่าน PR Review ก่อน Apply
  • Backup: ทดสอบ Restore Backup ทุกเดือน ตรวจว่าใช้ได้จริง

Proxmox VE คืออะไร

Open Source Virtualization KVM LXC Web UI Cluster 32 Nodes HA Ceph Storage Backup ZFS REST API ฟรี Enterprise Subscription