Upgrading AWS EKS to AL2023: Troubleshooting Custom Networking and nodeadm
AWS EKS 升級 AL2023 實戰:踩坑 Custom Networking 與 nodeadm 的啟動機制
隨著 Amazon Linux 2 (AL2) 逐漸進入維護期,將 EKS Node Group 升級到 Amazon Linux 2023 (AL2023) 已成為許多 SRE 的待辦事項。AL2023 引入了全新的初始化工具 nodeadm,雖然讓配置更標準化(使用 YAML),但也改變了我們習慣的 User Data 撰寫邏輯。
這篇文章將分享我們在升級過程中遇到的特定場景:當你需要「高度客製化」的網路配置(Custom Networking)時,該如何在 AL2023 中正確啟動節點。
背景:為什麼我們的場景比較複雜?
我們的 EKS 環境使用了 VPC CNI Custom Networking,目的是讓 Pod 使用與 Node 不同的 Subnet 網段。
在標準的 AWS 設計中,如果你的 ENIConfig 名稱與 Availability Zone (AZ) 名稱一致(例如 ap-northeast-1a),VPC CNI 可以自動對應。但我們的環境為了隔離不同業務(例如 Billing 系統),使用了帶有前綴的命名方式:
- AZ:
ap-northeast-1a - ENIConfig:
billing-ap-northeast-1a
因為名稱無法自動對應,我們必須在 Node 啟動時,強制打上特定的 Label 告訴 VPC CNI 該用哪個設定檔:
1 | eks.amazonaws.com/custom-eni-config=billing-ap-northeast-1a |
這在 AL2 時期很簡單,只要在 bootstrap.sh 裡算好 AZ 變數並塞進 --kubelet-extra-args 即可。但在 AL2023,這變成了一個挑戰。
踩到的坑:Node 無法加入 Cluster
AL2023 官方建議使用 MIME Multi-part 格式傳遞 NodeConfig YAML。但問題來了:Terraform 在 Plan/Apply 階段無法預知 Node 會跑在哪個 AZ,我們必須在 Runtime (Shell Script) 透過 IMDS (Instance Metadata Service) 動態獲取 AZ,才能組出正確的 custom-eni-config Label。
於是,我們將 User Data 改寫為 Shell Script,手動生成設定檔。然而,Terraform Apply 後,Node Group 卡在 Creating 狀態長達 20 分鐘,最後超時失敗。
排查 Log
我們 SSH 進入那台「只活在 EC2 Console 但進不了 K8s」的節點,查看 /var/log/user-data.log (我們自定義的輸出) 和 journalctl。
執行結果如下:
1 | [ec2-user@ip-10-xxx-xxx-xxx ~]$ sudo cat /var/log/user-data.log |
關鍵錯誤訊息:
Encountered error in config provider {"error": "could not find NodeConfig within UserData"}Command failed {"error": "no config in chain"}
原因分析
這段 Log 揭示了 nodeadm 的預設行為。當我們執行 /usr/bin/nodeadm init 而不帶任何參數時,它預設會去 IMDS (Instance Metadata Service) 抓取 User Data。
- 標準場景:User Data 是 YAML 格式的 NodeConfig ➝
nodeadm讀取成功 ➝ 啟動 Kubelet。 - 我們的場景:User Data 是 Bash Script ➝
nodeadm抓下來發現是 Script 不是 YAML ➝ 解析失敗 ➝ 報錯no config in chain。
雖然我們的 Script 已經在本地生成了正確的 YAML 檔 (/etc/eks/nodeadm-config.yaml),但 nodeadm 並不知道要去讀它。
解決方案:指定 –config-source
我們查閱了 nodeadm 的說明文件(或是直接看 Source Code/Help),發現它支援指定配置來源。
1 | $ nodeadm init --help |
原來關鍵在於 file:// scheme。我們需要明確告訴 nodeadm:「不要去網路抓,請讀取我剛寫入本機的檔案」。
修正後的 Terraform User Data
以下是最終成功的 User Data 腳本(簡化版):
1 | user_data = base64encode(<<-EOF |
重點提示:
- URI 格式:必須是
file:///absolute/path。 - MIME:依然建議包在 MIME Multipart 中,這是 AWS 推薦的標準做法。
同場加映:VPC CNI 的環境變數陷阱
在解決 Node 啟動問題後,我們發現 Node 雖然 Ready 了,但如果 VPC CNI (aws-node) 的設定不正確,Pod 依然無法產生。
在 AL2023 + Custom Networking 的架構下,請務必檢查 aws-node DaemonSet 的以下變數:
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true
- 一定要開,否則 CNI 會忽略我們辛苦打上的
custom-eni-configLabel,嘗試用主網段發 IP,導致 IP 不足或連線失敗。
ENABLE_PREFIX_DELEGATION=true
- 如果你有設定
maxPods: 110這類高密度部署,務必開啟此項,否則節點會因為 Private IP 耗盡而無法排程 Pod。
驗證結果
修正 User Data 與 CNI 變數後,Node 順利加入 Cluster。我們透過以下方式驗證 Custom Networking 是否生效:
1. 檢查 Node Label
確保 Label 正確指向了帶有前綴的 ENIConfig:
1 | kubectl get node <node-name> --show-labels | grep custom-eni-config |
2. 檢查 Pod IP 與 Node IP 的網段差異
這是最直接的證據。我們的設定中,Node 使用 10.xx.233.x (主網段),而 Pod 應該使用 10.xx.139.x (Custom Subnet)。
1 | $ kubectl get pod -o wide |
- Node IP:
10.23.xxx.xxx - Pod IP:
10.23.xxx.xxx
透過 aws ec2 describe-subnets 確認 10.23.xxx.xxx 確實屬於我們指定的 Custom Subnet CIDR (10.23.xxx.xxx/20),驗證成功!
總結
升級 AL2023 時,如果你的架構涉及動態參數(如依賴 AZ 的 Custom Networking),不要害怕放棄純 YAML User Data。使用 Shell Script 搭配 nodeadm init --config-source file://... 是官方支援且穩健的解法。
同時,別忘了檢查 aws-node 的環境變數,確保 CNI 能正確理解你的網路拓撲。