PubliclyAccessible=true 不等於對外曝露:同一台機器,RDS 連不到、kubectl 卻通

起因:一個旗標,讓 AI 一秒喊出了錯的資安結論 那天清掉一顆「砍了 EC2(Elastic Compute Cloud,AWS 的雲端虛擬機器)卻忘了收」的閒置 Elastic IP(彈性 IP — AWS 可保留、可重複指派的固定公網位址)後,我順手請我的 AI 助手盤點帳號還開著哪些服務。掃到一台正式環境的 RDS(Relational Database Service,AWS 託管的關聯式資料庫)PostgreSQL,設定裡寫著 PubliclyAccessible: true。 AI 第一個反應就回我:「正式資料庫直接掛公網,有資安風險。」這句話聽起來很合理——但它是錯的。真相要等到實際讀了 Security Group 規則、再從本機跑一次連線測試才浮現。 這篇文章講的就是這個落差:一個旗標的字面意思,跟它實際造成的曝露,是兩回事。 也順帶示範——在「AI 已經會幫你讀設定、寫指令」的年代,為什麼這種誤判依然會發生。 先說清楚:Security Group 是什麼 Security Group(安全群組,以下簡稱 SG)是 AWS 的虛擬防火牆,掛在資源的網路介面 ENI(Elastic Network Interface,彈性網路介面)上。它有三個關鍵性質: 預設全部拒絕:沒有明確放行的流量一律擋掉。你只能「加白名單」,沒有「黑名單」這種東西。 來源可以是 IP 網段,也可以是另一個 SG:後者意思是「凡是掛了某個 SG 的資源一律放行」,不必管它的 IP 是多少——這在 VPC(Virtual Private Cloud,虛擬私有雲)內部互連時非常好用。 stateful(狀態感知 — 放行了進來的連線,回程流量自動允許):不必另外開一條出站規則。 SG 的逐步設定我在 如何使用 psql 連線 AWS RDS 那篇有完整圖解(「SG 就像 RDS 的防火牆」)。這裡只談它跟「公開存取」旗標之間,那個最容易被誤會的互動。 旗標的陷阱:PubliclyAccessible=true 只給門牌,不給鑰匙 關鍵誤解就在這裡。PubliclyAccessible 這個旗標只決定一件事:RDS 要不要拿到一個公網 IP,以及一個可從網際網路解析的 DNS(網域名稱)。它完全不決定「誰連得進來」。 ...

June 25, 2026 · 3 分鐘 · Peter

EKS 維運的三個隱形陷阱:工具 Pod 消失、事件蒸發、審計空白

起因:工具 Pod 人間蒸發 某天要查資料庫,照慣例執行 kubectl exec -it psql-client,結果 Pod 不見了。 這個 psql-client 是用來連 RDS PostgreSQL 的工具容器,平常拿它跑 SQL 查詢、檢查資料表大小。問題是——沒有人記得刪過它,也沒有任何記錄顯示是誰或什麼原因讓它消失。 這件事本身影響不大,重建一個就好。但它暴露了三個更深層的問題:為什麼 Pod 會無聲消失?為什麼查不到是誰刪的?為什麼整個叢集沒有留下任何操作軌跡? 陷阱一:裸 Pod 沒有人管它的死活 問題本質 當初建立 psql-client 的指令大概是這樣: kubectl run psql-client --image=postgres:15-alpine --restart=Never -- sleep infinity 這行指令建立的是一個裸 Pod(Bare Pod)——直接建立 Pod 物件,不隸屬於任何 Deployment、ReplicaSet 或 StatefulSet。 裸 Pod 和 Deployment 管理的 Pod,差別在於有沒有 Controller 在背後看著它。Deployment 的 ReplicaSet Controller 會持續監控 Pod 數量,少一個就補一個。裸 Pod 沒有 Controller,它的生死完全取決於節點的命運。 flowchart TD subgraph 節點故障發生 A[Node 異常關機] end A --> B[裸 Pod] A --> C[Deployment Pod] B --> D[Pod 永久消失] D --> E[無人知曉] C --> F[ReplicaSet Controller 偵測] F --> G[在其他 Node 重建 Pod] G --> H[服務自動恢復] classDef default fill:#f5f5f5,stroke:#333,color:#333 classDef error fill:#f8d7da,stroke:#dc3545,color:#721c24 classDef success fill:#d4edda,stroke:#28a745,color:#155724 classDef info fill:#d1ecf1,stroke:#17a2b8,color:#0c5460 class A info class D,E error class G,H success 不處理會怎樣 裸 Pod 在以下情境會永久消失: ...

May 16, 2026 · 3 分鐘 · Peter

Kubernetes 節點 DiskPressure 事故:部署失敗到緊急救援的完整記錄

事發:部署成功但服務掛了 一次例行的 Strapi 後端部署,Jenkins build 成功、Docker image 推上 ECR、kubectl set image 也順利執行。但 rollout 等了 300 秒後超時。 以下是整個部署流程,可以看到問題出在最後一步: flowchart LR A[Jenkins Build] -->|✅ 成功| B[推送 Image 到 ECR] B -->|✅ 成功| C[kubectl set image] C -->|✅ 成功| D[rollout status 等待] D -->|❌ 超時 300s| E[自動回滾] classDef success fill:#d4edda,stroke:#28a745,color:#155724 classDef error fill:#f8d7da,stroke:#dc3545,color:#721c24 class A,B,C success class D,E error 具體的錯誤訊息: error: timed out waiting for the condition Jenkins 回報「部署失敗,已自動回滾」。奇怪的是,build 每一步都成功了,問題出在 rollout 階段。 調查:Pod 起不來的真正原因 問題排查的過程如下,從 pod 狀態開始一路追到節點層級: ...

March 26, 2026 · 4 分鐘 · Peter

解決 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