解決 Kubernetes 多餘 Pod 問題與 CrashLoopBackOff 的實戰心得

前言:一次神秘的 Pod 複製事件 在一次例行的 Strapi CMS 更新部署到 AWS EKS 時,遇到了一個詭異的現象:明明 Deployment 設定檔中清楚寫著 replicas: 1,但實際運行的 Pod 卻有兩個!更奇怪的是,其中一個 Pod 持續處於 CrashLoopBackOff 狀態,而另一個則正常運行。 無論怎麼刪除多餘的 Pod,它總是會像打不死的蟑螂一樣再次出現。這種「靈異事件」讓我開始懷疑 Kubernetes 是不是有自己的想法… 本文將深入探討: 為什麼會出現多餘的 Pod CrashLoopBackOff 背後的機制 Kubernetes Deployment 和 ReplicaSet 的運作原理 實戰排查步驟與解決方案 Secret 編碼陷阱與預防措施 問題背景:Deployment 說一個,實際卻有兩個 問題現象 預期行為: # my-strapi-prod-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-strapi-prod spec: replicas: 1 # 只要 1 個 Pod selector: matchLabels: app: my-strapi-prod 實際情況: $ kubectl get pods -n default | grep my-strapi-prod NAME READY STATUS RESTARTS AGE my-strapi-prod-7d4b5c8f9d-x2k4p 1/1 Running 0 10m my-strapi-prod-8c9a6d7e5f-w8n2q 0/1 CrashLoopBackOff 5 5m 兩個 Pod 同時存在,卻只有一個正常運行! ...

May 6, 2025 · 9 分鐘 · Peter

EKS Pod 卡在 Pending?從 Too Many Pods 到 ENI / CNI 限制全面解析

前言:一個讓人懷疑人生的 Pending 狀態 最近部署 Strapi CMS 到 AWS EKS 時,遇到一個詭異的情況: $ kubectl get pods -n default NAME READY STATUS RESTARTS AGE mycompany-strapi-prod-695854fbd4-dzw66 0/1 Pending 0 3h42m 一個 Pod 卡在 Pending 狀態超過三小時,CPU 和 Memory 明明還很充足,但就是起不來。 如果你曾經盯著 kubectl get pods 看著那個永遠不會變成 Running 的 Pending 狀態,同時懷疑是不是 Kubernetes 在跟你開玩笑——恭喜你,你不孤單。 在嘗試了 Google 前五個搜尋結果、檢查了三次 YAML 設定、並認真考慮是否該轉行當咖啡師之後,我終於找到了問題的根源… ⚠️ 劇透警告:問題的根源不是 CPU、不是 Memory,而是一個你可能從沒注意過的限制——網卡(ENI)和 IP 數量。 問題診斷:一步步找出真兇 Step 1:查看 Pod 事件 遇到 Pending 狀態,第一步當然是看看 Kubernetes 到底在抱怨什麼: $ kubectl describe pod mycompany-strapi-prod-695854fbd4-dzw66 -n default 輸出內容很長,但最重要的是 Events 區塊: ...

June 15, 2024 · 13 分鐘 · Peter

全端專案 AWS EKS 雲端架構深度解析

前言:從本地開發到雲端生產環境 本文將深入解析一個全端專案在 AWS 上的完整基礎設施架構,展示如何透過 Kubernetes (EKS) 實現高可用性、可擴展性和成本效益的生產環境。這個平台提供線上課程、預約服務、會員管理和金流整合等功能。 本文涵蓋內容: 完整的 AWS EKS 叢集架構 Strapi CMS 和 Vue.js 前端的容器化部署 Jenkins CI/CD 自動化部署流程 Ingress NGINX 負載均衡和 SSL 憑證管理 與 AWS RDS、S3、ECR 的整合 第三方服務整合 (Firebase FCM、台灣金流) 監控與日誌管理 架構概覽 整體架構圖 flowchart TB subgraph Internet["網際網路"] Users["使用者"] Admin["管理員"] MobileUsers["App 使用者<br/>iOS / Android"] end subgraph OnPremise["本地機房 Mac mini"] Jenkins["Jenkins CI/CD"] subgraph MobileBuild["行動應用建置"] Fastlane["📲 Fastlane<br/>自動化打包工具"] FastlaneMatch["🔐 Fastlane Match<br/>iOS 憑證管理"] XcodeBuild["🍎 Xcode Build<br/>iOS App 編譯"] GradleBuild["🤖 Gradle Build<br/>Android App 編譯"] end end subgraph AppStores["應用商店"] TestFlight["✈️ Apple TestFlight<br/>iOS 測試版發布"] AppStore["🍎 App Store<br/>iOS 正式上架"] PlayConsole["▶️ Google Play Console<br/>Android 上架"] end GitRepo["📂 Git Repository<br/>Fastlane Match 憑證庫"] subgraph AWS["AWS Cloud (ap-southeast-1)"] subgraph EKS["EKS Cluster: mycom-taipei-cluster"] subgraph IngressNS["ingress-nginx namespace"] LB["AWS ELB<br/>LoadBalancer"] Ingress["Ingress NGINX<br/>Controller"] end subgraph CertManager["cert-manager namespace"] CertMgr["Cert Manager"] CertInj["CA Injector"] CertWebhook["Webhook"] end subgraph DefaultNS["default namespace"] StrapiPod["Strapi Pod<br/>mycom-strapi-prod<br/>(1.5 CPU / 2Gi RAM)"] WebPod["Vue.js Pod<br/>mycom-web<br/>(500m CPU / 512Mi RAM)"] PSQLClient["PostgreSQL Client<br/>(Debug)"] end subgraph KubeSystem["kube-system namespace"] CoreDNS["CoreDNS<br/>(2 replicas)"] MetricsServer["Metrics Server<br/>(2 replicas)"] AWSNode["AWS VPC CNI"] KubeProxy["Kube Proxy"] end subgraph CloudWatch["amazon-cloudwatch namespace"] FluentBit["Fluent Bit<br/>(日誌收集)"] end end subgraph Storage["儲存服務"] RDS["RDS PostgreSQL<br/>postgres-strapi-taipei<br/>(ap-southeast-1)"] S3["S3 Bucket<br/>mycom-strapi-prod-s3-taipei"] end subgraph Registry["容器映像倉儲"] ECR["AWS ECR<br/>AWS_Account_ID.dkr.ecr"] end subgraph Monitoring["監控服務"] CW["CloudWatch<br/>Logs & Metrics"] end end subgraph ThirdParty["第三方服務"] Firebase["Firebase FCM<br/>(推播通知)"] Newebpay["Newebpay<br/>(台灣金流)"] EZPay["EZPay<br/>(電子發票)"] SendGrid["SendGrid<br/>(郵件服務)"] TWCA["TWCA<br/>(OV SSL 憑證)"] end Jenkins -->|建置映像檔| ECR Jenkins -->|部署更新| EKS Jenkins -->|觸發建置| Fastlane Fastlane --> FastlaneMatch FastlaneMatch -->|同步憑證| GitRepo Fastlane --> XcodeBuild Fastlane --> GradleBuild XcodeBuild -->|自動上傳| TestFlight TestFlight -.->|正式發布| AppStore GradleBuild -->|自動上傳| PlayConsole Users -->|HTTPS| LB Admin -->|HTTPS| LB MobileUsers -->|下載 App| TestFlight MobileUsers -->|下載 App| PlayConsole LB -->|:80,:443| Ingress Ingress -->|CMS Backend| StrapiPod Ingress -->|Web Frontend| WebPod CertMgr -.->|管理 SSL 憑證| Ingress TWCA -.->|OV SSL 憑證| CertMgr StrapiPod -->|讀寫資料| RDS StrapiPod -->|上傳媒體檔案| S3 StrapiPod -->|推播通知| Firebase StrapiPod -->|金流處理| Newebpay StrapiPod -->|電子發票| EZPay StrapiPod -->|寄送郵件| SendGrid WebPod -->|GraphQL API| StrapiPod FluentBit -->|收集日誌| CW MetricsServer -.->|資源監控| CW ECR -->|拉取映像檔| StrapiPod ECR -->|拉取映像檔| WebPod 核心技術棧 基礎設施層: ...

June 5, 2024 · 12 分鐘 · Peter

搜尋目錄下每一個檔案的特定字/檔案名稱

在開發過程中,我們常需要快速找出「哪個檔案用到了這個變數?」或是「那個設定檔到底放在哪裡?」。 本文整理了 Linux/Mac 終端機中最常用的搜尋指令:grep、ag 和 find。 1. 搜尋檔案內容 (grep) grep 是系統內建最強大的文字搜尋工具。 常用指令 # 在目前目錄 (./) 遞歸搜尋 "R2" grep -rwn --color=auto ./ -e 'R2' 參數詳解 -r (recursive):遞歸搜尋,包含所有子目錄。 -w (word):僅匹配「完整單字」,避免匹配到部分字串(例如搜尋 is 不會匹配到 this)。 -n (line number):顯示行號。 -e (pattern):指定搜尋的關鍵字。 --color=auto:將關鍵字高亮顯示。 顯示前後文 (Context) 有時候只看一行不夠,我們需要看前後幾行程式碼: # -C (Context): 顯示前後各 1 行 grep -rwn -C 1 ./ -e 'R2' # -A (After): 顯示後 1 行 # -B (Before): 顯示前 1 行 2. 更快的選擇:The Silver Searcher (ag) grep 雖然強大,但速度較慢且指令較長。ag 是一個專為程式碼搜尋設計的工具,速度極快且預設忽略 .gitignore 的檔案。 (需額外安裝:brew install the_silver_searcher) ...

September 23, 2023 · 1 分鐘 · Peter

iOS如何把專案打包framwork後也讓原本的pod/套件包入framework ?

工作需要把整個app包成一個SDK讓另一個app引用,所以要把原本專案的套件也包進去,不然在打包framework的時候,import套件那一行會報錯. iOS如何把專案打包framwork後也讓原本的pod/套件包入framework ? 工作需要把整個app包成一個SDK讓另一個app引用,所以要把原本專案的套件也包進去,不然在打包framework的時候,import套件那一行會報錯. 假設你的專案叫”B”,打包的framework取名為BPack,那麽在Podfile的部分要加入framework自己的套件引用,原本的專案裝什麼pod,framework如果有用到該套件也要跟著裝,以SnapKit為例,作法如下: target ‘B’ do Comment the next line if you don’t want to use dynamic frameworks use_frameworks! pod ‘SnapKit’, ‘~> 5.6.0’ Pods for B end target ‘BPack’ do Comment the next line if you don’t want to use dynamic frameworks use_frameworks! pod ‘SnapKit’, ‘~> 5.6.0’ Pods for B Pods for BPack end另外,在Build Setting的部分好像也要做一下設定: 如果你包的是動態庫,那麽在framework target的Build Settings: Linking -> Mach-O Type應該要選擇:Dynamic Libray Mach-O這部分不太確定有沒有影響,不過我這樣選是可以Run App Successful. 有錯麻煩請指正,感謝! ...

September 22, 2023 · 1 分鐘 · Peter

終端機現代化:DevOps 工程師的六個 CLI 升級

前言:差點對錯誤的 cluster 下指令 某天下午,我在兩個 EKS(Amazon Elastic Kubernetes Service)cluster 之間切換除錯,staging 和 production 的 kubeconfig context 都掛在同一台筆電上。當我打完 kubectl delete deployment 按下 Enter 前的那半秒,我其實不確定自己在哪個 context。那次運氣好,是 staging。 事後檢討,問題不是粗心,是預設的終端機提示字元什麼都不告訴你。user@host ~ % 這行字 20 年來沒變過,但我們的工作環境早就變了:一個人同時面對多個 cluster、多組 AWS 帳號、十幾個專案目錄。工具沒跟上環境,靠的就只剩自律——而自律總有失靈的一天。 這篇文章整理我把終端機全面現代化的過程:六個主力工具,加上一批不用安裝任何東西的原生 shell 技巧。每個工具都回答三個問題:為什麼要換、不換的代價是什麼、以及會踩到哪些坑。 六個升級,按痛點排序 1. starship — 操作 EKS 前,先看一眼提示字元 starship 是用 Rust 寫的跨 shell 提示字元引擎。裝完之後,每行指令前會自動顯示:目前路徑、git 分支與狀態、語言版本,以及對 DevOps 最關鍵的兩個欄位——kubernetes context 和 AWS profile: ~/work/api-server on main via 🐍 v3.11.4 ☸ arn:aws:eks:ap-northeast-1:123456789012:cluster/prod-cluster (default) ❯ 為什麼這樣做? 防呆的本質是把「需要主動確認的事」變成「被動就會看到的事」。kubectl config current-context 誰都會打,但沒有人會在每一條指令前打一次;提示字元每次都在,你想忽略都難。 ...

August 1, 2023 · 3 分鐘 · Peter