IaC 起手式:用 Terraform 在 PVE 上自動建出 K3s 容器
建立 K3s 節點前,第一步是把整個流程用程式碼重現。這篇整理核心思維轉變、工具分工,以及在 PVE 上實作自動化前必須掌握的 CLI 與 API Token。
核心思維:基礎設施即程式碼(IaC)
Pets vs. Cattle
| 寵物(Pets) | 牛群(Cattle) | |
|---|---|---|
| 對待方式 | 精心照顧,害怕壞掉 | 壞了直接銷毀重建 |
| 管理方式 | 手動 SSH 進去修 | 程式碼定義,一鍵重新部署 |
| 代表情境 | 手動調整的實體伺服器 | 由 Terraform 產生的 CT/VM |
IaC 的目標就是零手動容忍:連「建立機器」這件事都交給程式碼,達到任何節點壞掉都能自動重建的最終狀態。
工具分工:Terraform + Ansible
兩個工具負責不同層次的工作,缺一不可。
Terraform(建築師)— Provisioning
負責跟 PVE 溝通,自動「生出」實體資源:
- 建立 CT,分配 CPU / RAM
- 設定靜態 IP、網路介面
- 定義節點規格(Master 2G RAM、Worker 8G RAM)
Ansible(室內設計師)— Configuration Management
在 CT 建起來後,自動連進去「裝潢」:
- 修改 SSH 設定(開啟 root 登入)
- 安裝基礎套件(curl、wget 等)
- 執行 K3s 安裝指令
bash
# Master 安裝
curl -sfL https://get.k3s.io | sh -
# Worker 加入叢集(需先從 Master 取得 token)
curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<node-token> sh -前置作業
1. 連線進入主控台
先確認主控台是否允許 SSH 連線,若有的話,可以使用終端機連進去:
bash
ssh user_name@server_ip2. 建立 PVE API Token
Terraform 不會使用網頁登入,所以需要一把專屬的 API Token 來操作 PVE。
bash
# 1. 建立專用帳號
pveum user add terraform@pve --comment "Terraform automation user"
# 2. 建立自訂角色並給予必要權限
pveum role add TerraformRole -privs \
"VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU \
VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType \
VM.Config.Memory VM.Config.Network VM.Config.Options \
VM.Audit VM.PowerMgmt \
Datastore.AllocateSpace Datastore.Audit \
SDN.Use Sys.Audit \
Pool.Allocate Sys.Console Sys.Modify VM.Migrate"
# 3. 將角色綁定到帳號(/ 代表對整個 PVE 根目錄授權)
pveum aclmod / -user terraform@pve -role TerraformRole
# 4. 產生 API Token(--privsep=0 讓 Token 繼承帳號全部權限)
pveum user token add terraform@pve terraform-token --privsep=0
# 5. 確認是否建立成功
pvesh get /access/users/terraform@pve/token --output-format json
pvesh user list | less執行第 4 步後,終端機會輸出 Token 值,只會顯示一次,範例如下:
┌──────────────┬──────────────────────────────────────┐
│ key │ value │
╞══════════════╪══════════════════════════════════════╡
│ full-tokenid │ terraform@pve!terraform-token │
├──────────────┼──────────────────────────────────────┤
│ value │ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │
└──────────────┴──────────────────────────────────────┘撰寫 Terraform 腳本
有了 API Token 之後,就可以開始撰寫 Terraform 腳本,讓它自動在 PVE 上建出 K3s 所需的容器。
專案結構
terraform/
├── main.tf # 主要資源定義
└── terraform.tfvars # 敏感變數(不納入版控)main.tf
hcl
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.50.0"
}
}
}
provider "proxmox" {
endpoint = "<PVE IP,例如 https://192.168.x.x:8006>"
api_token = "terraform@pve!terraform-token=<token>"
insecure = true # 若使用自簽憑證需開啟
}
# --- Master 節點 ---
resource "proxmox_virtual_environment_container" "k3s_master" {
node_name = "pve00"
vm_id = 204
started = true
initialization {
hostname = "k3s-master"
user_account {
password = "<password>"
}
ip_config {
ipv4 {
address = "192.168.<網段>.14/24"
gateway = "192.168.<網段>.1"
}
}
dns {
servers = ["8.8.8.8", "1.1.1.1"]
}
}
network_interface {
name = "eth0"
bridge = "vmbr0"
}
operating_system {
template_file_id = "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst"
type = "debian"
}
cpu { cores = 2 }
memory { dedicated = 2048 }
disk { datastore_id = "local-lvm"; size = 5 }
unprivileged = true
features { nesting = true }
}
# --- Worker 節點 ---
resource "proxmox_virtual_environment_container" "k3s_worker" {
node_name = "pve00"
vm_id = 205
started = true
initialization {
hostname = "k3s-worker"
user_account {
password = "<password>"
}
ip_config {
ipv4 {
address = "192.168.<網段>.15/24"
gateway = "192.168.<網段>.1"
}
}
dns {
servers = ["8.8.8.8", "1.1.1.1"]
}
}
network_interface {
name = "eth0"
bridge = "vmbr0"
}
operating_system {
template_file_id = "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst"
type = "debian"
}
cpu { cores = 2 }
memory { dedicated = 2048 }
disk { datastore_id = "local-lvm"; size = 5 }
unprivileged = true
features { nesting = true }
}腳本寫好後,執行以下指令讓 Terraform 建出這兩個容器:
bash
terraform init # 下載 provider
terraform plan # 預覽會建什麼
terraform apply # 實際執行容器建好後,下一篇繼續:在容器內安裝 K3s
