็†ฑ้–€ๅˆ†้กž
 ่ผ‰ๅ…ฅไธญ…
็›ฎ้Œ„

๐Ÿงฑ Proxmox VE ่™›ๆ“ฌๆฉŸ่‡ชๅ‹•ๅŒ–็ฎก็†:CLI、API ่ˆ‡ Ansible ๆ•ดๅˆๆ‡‰็”จ

    ๐Ÿงฑ Proxmox VE ่™›ๆ“ฌๆฉŸ่‡ชๅ‹•ๅŒ–็ฎก็†:CLI、API ่ˆ‡ Ansible ๆ•ดๅˆๆ‡‰็”จ

    Proxmox VE ๆ“ๆœ‰ๅฎŒๆ•ด็š„ CLI ๆŒ‡ไปค้›†REST API ่ˆ‡ Ansible ๆ”ฏๆด,่ƒฝไปฅ็จ‹ๅผๅŒ–ๆ–นๅผๅปบ็ซ‹ VM/LXC、ๆ‰นๆฌก่ชฟๆ•ด่ณ‡ๆบ、้›ฒ็ซฏๅˆๅง‹ๅŒ–(Cloud-Init)่ˆ‡็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†。ๆœฌๆ–‡ๆไพ›ๅฏ็›ดๆŽฅ่ค‡่ฃฝ็š„่…ณๆœฌ่ˆ‡ Playbook,ๅ”ๅŠฉไฝ ๆŠŠๆ—ฅๅธธๆ“ไฝœ่‡ชๅ‹•ๅŒ–。

    ไธ€、CLI(qm / pct / pvesh):ๆœ€ๅฟซไธŠๆ‰‹็š„่‡ชๅ‹•ๅŒ–ๅ…ฅๅฃ

    CLI ้ฉๅˆๅœจๅ–ฎ้ปžๆˆ– Pipeline ่ฃกๅฟซ้€Ÿๆ‰นๆฌกไฝœๆฅญ;ๆญ้… shell ่ฟดๅœˆๅณๅฏ้‡็”ข VM/LXC。

    1) ๅปบ็ซ‹ KVM ่™›ๆ“ฌๆฉŸ(qm + Cloud-Init)

    # ๅƒๆ•ธ
    VMID=101
    ISO=local:iso/debian-12.6.0-amd64-netinst.iso
    STOR=local-lvm
    
    # ๅปบ็ซ‹ VM ่ˆ‡ๅŸบๆœฌ็กฌ้ซ”
    qm create $VMID --name "vm-$VMID" --memory 4096 --cores 2 --cpu host \
      --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci
    
    # ๅŒฏๅ…ฅ(ๆˆ–็›ดๆŽฅๆŽ›)็ฃ็ขŸ
    qm set   $VMID --ide2 $ISO,media=cdrom
    qm set   $VMID --scsi0 $STOR:32    # 32G ่™›ๆ“ฌ็ขŸ
    
    # ๅ•Ÿ็”จ Cloud-Init(่‡ชๅ‹•ๅŒ–ๅธณ่™Ÿ/็ถฒ่ทฏ/SSH Key)
    qm set   $VMID --ide0 $STOR:cloudinit
    qm set   $VMID --ciuser admin --cipassword 'P@ssw0rd!'
    qm set   $VMID --sshkey ~/.ssh/id_rsa.pub
    qm set   $VMID --ipconfig0 ip=dhcp
    
    # ้–‹ๆฉŸ
    qm start $VMID
    

    2) ๅปบ็ซ‹ LXC ๅฎนๅ™จ(pct)

    # ไธ‹่ผ‰็ฏ„ไพ‹ๆจกๆฟ(ๅฏ็”จ Web UI ๆˆ– pveam)
    pveam available | grep debian-12
    pveam download local debian-12-standard_12.2-1_amd64.tar.zst
    
    # ๅปบ็ซ‹ๅฎนๅ™จ
    CTID=201
    pct create $CTID local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
      --hostname lxc-$CTID --cores 2 --memory 2048 --swap 512 \
      --rootfs local-lvm:8 --net0 name=eth0,bridge=vmbr0,ip=dhcp --unprivileged 1
    pct start $CTID
    

    3) pvesh:ไปฅๆœฌๆฉŸๆŒ‡ไปคๅ‘ผๅซ REST API

    # ๅˆ—ๅ‡บๅข้›†่ณ‡ๆบ(็ญ‰ๅŒ GET /api2/json/cluster/resources)
    pvesh get /cluster/resources
    
    # ๅปบ็ซ‹ๆŽ’็จ‹ๅ‚™ไปฝ(็ญ‰ๅŒ POST /api2/json/nodes/{node}/vzdump)
    pvesh create /nodes/node1/vzdump --all 1 --mode snapshot --compress zstd --storage local
    

    ไบŒ、REST API:่ˆ‡ CI/CD、ๅ…ง้ƒจ็ณป็ตฑๅฐๆŽฅ

    Proxmox API ๅ…ฅๅฃ็‚บ https://<host>:8006/api2/json。ๅปบ่ญฐไฝฟ็”จ API Token(้ฟๅ…ๆ˜Žๆ–‡ๅฏ†็ขผ)。

    1) ๅ–ๅพ— Token(Web ไป‹้ข)

    • Datacenter → Permissions → API Tokens → ็‚บ user@pam ๅปบ็ซ‹ tokenid,ๅ‹พ้ธ Privilege Separation,ๆŒ‡ๆดพ็›ธๅฐๆ‡‰่ง’่‰ฒ(ไพ‹ๅฆ‚ PVEVMAdmin)。

    2) ไฝฟ็”จ Token ๅ‘ผๅซ API(cURL ็ฏ„ไพ‹)

    # ๅˆ—ๅ‡บ็ฏ€้ปž
    curl -k -H "Authorization: PVEAPIToken=user@pam!tokenid=SECRET" \
      https://pve-1:8006/api2/json/nodes
    
    # ๅปบ็ซ‹ VM(ๅƒ…็คบๆ„,ๅธธ็”จๅƒๆ•ธๅ–ๆฑบๆ–ผๅ„ฒๅญ˜/็ถฒ่ทฏ)
    curl -k -X POST -H "Authorization: PVEAPIToken=user@pam!tokenid=SECRET" \
      -d "vmid=150&name=auto-150&memory=4096&cores=2&net0=virtio,bridge=vmbr0" \
      https://pve-1:8006/api2/json/nodes/pve-1/qemu
    

    3) ไปฅ Cookie + CSRF(ๅ‚ณ็ตฑๆ–นๅผ)

    # ็™ปๅ…ฅๅ–ๅพ— Ticket ่ˆ‡ CSRF
    curl -k -d "username=root@pam&password=YOURPASS" https://pve-1:8006/api2/json/access/ticket
    
    # ไน‹ๅพŒ POST ้œ€ๅธถไธŠ:
    #  -H "CSRFPreventionToken: <from-login>"
    #  --cookie "PVEAuthCookie=<from-login>"
    

    ไธ‰、Ansible:ๅฎฃๅ‘Šๅผๅคง้‡ไฝˆๅปบ่ˆ‡ๆ”ถๆ–‚

    ้ฉๅˆๆŠŠ「ๅปบ็ซ‹ VM → ่จญๅฎš่ฆๆ ผ → ๅ•Ÿๅ‹• → ๅฎ‰่ฃๅพŒ่จญๅฎš」ๆต็จ‹ๅŒ–,ๆ–นไพฟ้‡่ค‡ๅŸท่กŒ่ˆ‡่ฟฝ่นค。

    1) ๅปบ่ญฐ็›ฎ้Œ„

    inventories/
      production/
        hosts.ini
        group_vars/proxmox.yml
    playbooks/
      create_vms.yml
    roles/
      postconfig/...
    

    2) inventory(hosts.ini)

    [proxmox]
    pve-1 ansible_host=10.10.10.11
    pve-2 ansible_host=10.10.10.12
    
    [proxmox:vars]
    ansible_user=root
    ansible_ssh_common_args='-o StrictHostKeyChecking=no'
    

    3) API ้€ฃ็ทšๅƒๆ•ธ(group_vars/proxmox.yml)

    pve_api_host: "https://10.10.10.11:8006/api2/json"
    pve_api_user: "user@pam"
    pve_api_token_id: "tokenid"
    pve_api_token_secret: "SECRET"
    pve_validate_certs: false
    

    4) Playbook(ไฝฟ็”จ community.general.proxmox_kvm)

    ---
    - name: Create KVMs on Proxmox
      hosts: proxmox
      gather_facts: false
      collections:
        - community.general
    
      vars:
        vm_plan:
          - { vmid: 310, name: "web-01", memory: 4096, cores: 2 }
          - { vmid: 311, name: "web-02", memory: 4096, cores: 2 }
    
      tasks:
        - name: Create/ensure VMs
          community.general.proxmox_kvm:
            api_user: "{{ pve_api_user }}"
            api_token_id: "{{ pve_api_token_id }}"
            api_token_secret: "{{ pve_api_token_secret }}"
            api_host: "{{ pve_api_host }}"
            validate_certs: "{{ pve_validate_certs }}"
            node: "pve-1"
            vmid: "{{ item.vmid }}"
            name: "{{ item.name }}"
            memory: "{{ item.memory }}"
            cores: "{{ item.cores }}"
            net:
              - model=virtio,bridge=vmbr0
            scsihw: virtio-scsi-pci
            scsi:
              - storage=local-lvm,size=32
            state: present
          loop: "{{ vm_plan }}"
    
        - name: Start VMs
          community.general.proxmox_kvm:
            api_user: "{{ pve_api_user }}"
            api_token_id: "{{ pve_api_token_id }}"
            api_token_secret: "{{ pve_api_token_secret }}"
            api_host: "{{ pve_api_host }}"
            validate_certs: "{{ pve_validate_certs }}"
            node: "pve-1"
            vmid: "{{ item.vmid }}"
            state: started
          loop: "{{ vm_plan }}"
    
    ๐Ÿ’ก ่‹ฅๆฒ’ๆœ‰ proxmox_kvm ๆจก็ต„ๆ€Ž้บผ่พฆ?(็”จ uri ็›ดๆ‰“ REST API)
    - name: Create VM via REST
      uri:
        url: "{{ pve_api_host }}/nodes/pve-1/qemu"
        method: POST
        headers:
          Authorization: "PVEAPIToken={{ pve_api_user }}!{{ pve_api_token_id }}={{ pve_api_token_secret }}"
        body_format: form-urlencoded
        body:
          vmid: "320"
          name: "api-320"
          memory: "4096"
          cores: "2"
          net0: "virtio,bridge=vmbr0"
        validate_certs: false
        status_code: 200, 202
    

    ๅ››、ๆ‰นๆฌกๅŒ–:ไธ€้ตๅคง้‡ไฝˆ็ฝฒๅฐ็ฏ„ไพ‹

    #!/usr/bin/env bash
    # create_many.sh
    for i in {401..405}; do
      qm create $i --name "batch-$i" --memory 2048 --cores 2 \
        --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci
      qm set $i --scsi0 local-lvm:16 --ide2 local:iso/debian-12.iso,media=cdrom
      qm start $i
    done
    

    ไบ”、ๅธธ่ฆ‹่‡ชๅ‹•ๅŒ–ๆƒ…ๅขƒ

    • ๐Ÿš€ ๅพžๆจกๆฟๅฟซ้€Ÿ่ค‡่ฃฝ:ๅ…ˆๆŠŠ Golden Image ่จญ็‚บๆจกๆฟ qm template <VMID>,ๅ† qm clone ๆญ้… Cloud-Init ๆ‰นๆฌก็”Ÿๆˆ。
    • ๐Ÿ” ๆปพๅ‹•ๆ›ดๆ–ฐ:็”จ Ansible ๅฐ‡ VM ๅˆ†็ต„,้€ๅฐ้ท็งป(ๆˆ–ๅœๆญข)、ๆ›ดๆ–ฐ、้ฉ—่ญ‰,ๅ†ๅˆ‡ๅ›ž HA。
    • ๐Ÿงฐ ่ณ‡ๆบๅฝˆๆ€ง่ชฟๆ•ด:ไปฅ API/Ansible ไพๅฐ–ๅณฐ่‡ชๅ‹•่ชฟๆ•ด vCPU/Memory,ไฝŽ่ฐทๅ†็ธฎๅ›ž。
    • ๐Ÿ›ก️ ๅ‚™ไปฝ่ˆ‡ๅฅๅบทๆชขๆŸฅ:ไปฅ pvesh ่งธ็™ผ vzdump,ๆ—ฅ่ชŒไธฒๆŽฅ Rsyslog/GoAccess,็•ฐๅธธๆ™‚็™ผ Slack/Webhook。

    ๅ…ญ、ๆŽ’้Œฏ่ˆ‡ๆœ€ไฝณๅฏฆๅ‹™

    • API 403:็ขบ่ช Token ่ง’่‰ฒ(Role)ๆ˜ฏๅฆๆถต่“‹ VM.Allocate / Sys.Modify ็ญ‰ๆฌŠ้™。
    • ๆจก็ต„็ผบๅฐ‘:Ansible ๅŠ ่ฃ ansible-galaxy collection install community.general
    • Cloud-Init ็„กๆ•ˆ:ๆชขๆŸฅ --ide0 <storage>:cloudinit ๆ˜ฏๅฆๅทฒ่จญๅฎš、VM BIOS/Boot ้ †ๅบ。
    • REST 202:ไปฃ่กจๅทฒๅ—็†้žๅŒๆญฅๅทฅไฝœ,ๅพŒ็บŒๅฏ่ผช่ฉข /nodes/<node>/tasks/<upid>/status

    ๐Ÿ“˜ ็ต่ชž

    ๆŠŠ CLI、REST API ่ˆ‡ Ansible ไธฒ่ตทไพ†,ไฝ ๅฐฑๆ“ๆœ‰「ๅฏ้‡็พ、ๅฏ่ฟฝ่นค、ๅฏๅ›žๆปพ」็š„ VM/LXC ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†。ๅปบ่ญฐ้…ๅˆๅ‰ๆ–‡็š„็ถฒ่ทฏ/VLAN、ๅ„ฒๅญ˜、ๅ‚™ไปฝ、HA ๅข้›†ๆ–‡็ซ ,ๅฝขๆˆๅฎŒๆ•ด็š„ Proxmox ่‡ชๅ‹•ๅŒ–ไฝœๆฅญ้ˆ。


    ๐Ÿ”— ๅปถไผธ้–ฑ่ฎ€

    — WWFandy・็ณป็ตฑ่ˆ‡็ถฒ่ทฏ็ญ†่จ˜

    ๐Ÿ”— ๅˆ†ไบซ้€™็ฏ‡ LINE Facebook X

    ๆฒ’ๆœ‰็•™่จ€:

    ๅผต่ฒผ็•™่จ€

    ๅญ—็ดš