Proxmox VE Cluster GitOps Workflow — จัดการ
Proxmox VE GitOps
Proxmox VE Cluster GitOps Terraform Ansible CI/CD KVM LXC High Availability Ceph Storage Backup PBS Infrastructure as Code Production
| Component | Tool | Purpose | GitOps Role |
|---|---|---|---|
| VM Provisioning | Terraform | สร้าง VM LXC จาก Code | Declarative state |
| Configuration | Ansible | ติดตั้ง Software ตั้งค่า | Idempotent config |
| CI/CD | GitHub Actions | Plan Apply อัตโนมัติ | Automated workflow |
| State | Terraform State | เก็บ State ปัจจุบัน | Source of truth |
| Version Control | Git | Track ทุกการเปลี่ยนแปลง | Audit trail |
| Backup | PBS | Backup VM LXC | DR 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