Skip to content

記錄用 Terraform 在 Proxmox 上自動化建置 LXC + K3s 過程中踩到的坑。

1. SSH 認證失敗(remote-exec)

錯誤

SSH authentication failed: ssh: unable to authenticate, attempted methods [none publickey]

原因
connection block 沒有提供 passwordprivate_key,SSH 沒有任何可用的認證方式。

修法
在 connection block 加上 password:

hcl
connection {
  type     = "ssh"
  user     = "your-username"
  host     = "192.168.68.x"
  password = "your-password"
}

2. API Token 401 Authentication Failed

錯誤

error retrieving container: received an HTTP 401 response - Reason: Authentication failed!

原因
api_token 的 UUID 填錯了(複製到舊的 token 值)。

確認方式
在 PVE 上跑:

bash
pveum user token list root@pam

把輸出的 value 欄位貼回 main.tf

hcl
api_token = "root@pam!privileged_token=<正確的UUID>"

3. zsh 的 ! 符號問題

錯誤

zsh: event not found: privileged_token=...

原因
zsh 把 ! 當成 history expansion,導致 curl 指令失敗。

修法
用單引號包住含有 ! 的字串:

bash
curl -sk -H 'Authorization: PVEAPIToken=root@pam!privileged_token=UUID' https://...

4. Proxmox API Token Privilege Separation

原因
Proxmox token 預設開啟 Privilege Separation(privsep=1),token 沒有任何 ACL 權限,打 API 會被拒。

修法(方案A)用 root@pam,關閉 privsep

bash
pveum user token remove root@pam privileged_token
pveum user token add root@pam privileged_token --privsep 0

修法(方案B)用獨立 user,設 ACL

bash
pveum user add terraform@pve
pveum aclmod / -user terraform@pve -role Administrator
pveum user token add terraform@pve terraform-token --privsep 0

5. 403:修改 privileged container feature flags 需要 root@pam

錯誤

Permission check failed (changing feature flags for privileged container is only allowed for root@pam)

原因
unprivileged = false 的容器,feature flags(fuse、keyctl 等)只有 root@pam token 能設定,其他 user 就算有 Administrator 權限也不行。

修法
改用 root@pam token,或是改成 unprivileged container:

hcl
unprivileged = true
features {
  nesting = true
}

6. sudo echo 無法寫入受保護檔案

錯誤

cannot create /etc/pve/lxc/<vmid>.conf: Permission denied

原因
sudo echo 'xxx' >> /etc/pve/lxc/xxx.conf 無效,因為 >> 重定向是由原本的 shell 執行,不是 sudo 的 root shell。

修法
改用 tee -a

bash
echo 'lxc.apparmor.profile: unconfined' | sudo tee -a /etc/pve/lxc/<vmid>.conf

7. pct 指令找不到

錯誤

pct: not found

原因
pct/usr/sbin/pct,非 root 使用者的 PATH 不含 /usr/sbin

修法
用完整路徑:

bash
sudo /usr/sbin/pct stop <vmid>
sudo /usr/sbin/pct start <vmid>

8. Terraform remote-exec 卡住(sudo 等待密碼)

現象
remote-exec 卡在 [sudo] password for user: 超過 2 分鐘。

原因
Terraform 執行遠端指令時無法互動輸入密碼,sudo 等待輸入導致 timeout。

修法
在 PVE 上設定 NOPASSWD:

bash
echo 'your-username ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/your-username

9. kubectl get nodes — connection refused(k3s 還在啟動)

錯誤

dial tcp 127.0.0.1:6443: connect: connection refused

原因
k3s 啟動時會先在 6444 跑 temporary apiserver,待 TLS 憑證產生完畢才切換到 6443。
太早跑 kubectl 就會 connection refused。

修法
等 k3s 完全就緒再下指令(通常 30~60 秒),或用以下指令確認 6443 已在監聽:

bash
ss -tlnp | grep 6443

10. LXC 裡無法載入 kernel module(br_netfilter / overlay)

錯誤(systemctl status k3s)

modprobe br_netfilter (code=exited, status=1/FAILURE)
modprobe overlay     (code=exited, status=1/FAILURE)

原因
LXC 容器沒有權限載入 kernel module,必須在 PVE 宿主機上載入。

修法
在 PVE host 上執行:

bash
modprobe br_netfilter overlay

# 開機自動載入
echo -e "br_netfilter\noverlay" | tee /etc/modules-load.d/k3s.conf

11. kubelet 起不來:/dev/kmsg 不存在

錯誤

open /dev/kmsg: no such file or directory
Error: failed to run Kubelet: failed to create kubelet: open /dev/kmsg: no such file or directory

原因
unprivileged LXC 容器裡 /dev/kmsg 不存在,kubelet 啟動時需要它來建立 oomWatcher,找不到就直接 crash 並重啟。

修法(容器內)

bash
echo 'L /dev/kmsg - - - - /dev/console' > /etc/tmpfiles.d/kmsg.conf
systemd-tmpfiles --create /etc/tmpfiles.d/kmsg.conf
systemctl restart k3s

Terraform 自動化(remote-exec 加在 pct start 之後)

hcl
"sudo /usr/sbin/pct exec ${self.vm_id} -- bash -c \"echo 'L /dev/kmsg - - - - /dev/console' > /etc/tmpfiles.d/kmsg.conf && systemd-tmpfiles --create /etc/tmpfiles.d/kmsg.conf\""

12. kubelet 無法寫入 /proc/sys(unprivileged LXC 權限不足)

錯誤

open /proc/sys/vm/overcommit_memory: permission denied
open /proc/sys/kernel/panic: permission denied
open /proc/sys/kernel/panic_on_oops: permission denied
Error: failed to run Kubelet: failed to start ContainerManager

原因
unprivileged LXC 容器沒有權限寫入 kernel 參數,kubelet 啟動時嘗試調整這些值並 crash。

修法
建立 k3s 設定檔,啟用 KubeletInUserNamespace feature gate 讓 kubelet 跳過這些操作:

bash
mkdir -p /etc/rancher/k3s
cat > /etc/rancher/k3s/config.yaml << 'EOF'
kubelet-arg:
  - "feature-gates=KubeletInUserNamespace=true"
EOF
systemctl restart k3s

確認成功

NAME         STATUS   ROLES                  AGE   VERSION
k3s-master   Ready    control-plane,etcd     70s   v1.35.4+k3s1