iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Build on AWS

AWS架構師的自我修養:30天雲端系統思維實戰指南系列 第 32

Day 16-2 | Dev / Staging / Prod 多環境治理與架構策略: AWS 多環境配置管理與部署策略(二): ECS 、 EKS 與 K8s 管理

  • 分享至 

  • xImage
  •  

3. Amazon ECS 叢集管理

我們已經蓋好了標準化的廚房 (IaC),也設計好了標準化的「半成品料理包」 (Docker Image)。現在,我們需要一個智能、高效的「總廚系統」來管理整個後場的運作。這個系統需要知道:

  • 一道菜(我們的應用程式)的標準烹飪流程是什麼?
  • 尖峰時段(高流量)要同時開幾個爐子來做這道菜?
  • 如果某個爐子壞了(伺服器故障),要如何自動換一個新的爐子繼續烹飪?

這個「總廚系統」,就是 Amazon ECS (Elastic Container Service)

3.1 ECS 服務定義

要理解 ECS,我們要先認識它的 4 個核心組件: ECS Cluster (叢集) - 「廚房本身」Task Definition (任務定義) - 「標準作業程序 (SOP) 卡」Task (任務) - 「大火快炒的爐位」Service (服務) - 「不知疲倦的總廚」

  1. ECS Cluster (叢集) - 「廚房本身」
  • 核心概念:一個邏輯上的分組,用來容納我們的所有容器化應用。我們可以把它想像成 Dev Kitchen Worktop, Staging Kitchen Worktop, Prod Kitchen Worktop,就像是跟其他人說我的廚房工作區域有幾坪一樣,尚未涉及到實際裡面的布置,它本身不花錢,只是一個管理的範圍邊界。

  • 重點: 叢集需要有「算力」來運作,就像廚房需要有爐子。算力來源有兩種模式:

    • EC2 模式: 我們自己買來一批「爐子」(EC2 雲端伺服器),把它們註冊到我們的廚房裡。我們對爐子的品牌、型號有完全的控制權可以自由調校,但也要自己負責維護和管理。
    • Fargate 模式 (Serverless - 業界主流趨勢): 我們不買爐子,要做菜時,直接跟 AWS 說:「我需要一個火力 500W、佔地 1 平方公尺的爐位」,AWS 會自動從他們龐大的資源池裡,變出一個符合我們要求的爐位給我們用,用完即焚。
    • 對於絕大多數新的應用,優先選擇 Fargate。它讓我們從繁瑣的伺服器管理中解放出來,更專注於我們的應用程式本身。
  1. Task Definition (任務定義) - 「標準作業程序 (SOP) 卡」
  • 概念: 這是 ECS 最核心的藍圖。它是一份 JSON 格式的說明書,詳細定義了「如何烹飪一道菜」。

  • 重點:就像是一張給廚師看的傻瓜式 SOP 卡,上面精確地寫著:

    • image: 要用哪個版本的「半成品料理包」 (Docker Image URL)。
    • cpu & memory: 需要分配多少「火力」和「空間」給這個任務。
    • environment: 需要注入哪些「調味料」 (環境變數,例如 DATABASE_URL)。
    • secrets: 需要從「保險櫃」(AWS Secrets Manager) 裡取用哪些「秘方」 (API Key, 資料庫密碼)。
    • logConfiguration: 烹飪過程的紀錄(日誌)要送到哪裡 (例如送到 CloudWatch Logs)。
    • taskRoleArn: 給予這位廚師什麼樣的「權限卡」(IAM Role),讓它可以去跟其他 AWS 服務互動(例如讀取 S3 上的檔案)。
  1. Task (任務) - 「大火快炒的爐位」
  • 概念: 任務定義的「執行中實例 (Running Instance)」。當 ECS 根據我們的 SOP 卡實際啟動一個容器後,那個正在運行的容器就是一個 Task, 我們可以透過設定多少個 Task 來決定同時間有多少灶口在大鍋炒
  1. Service (服務) - 「不知疲倦的總廚」
  • 概念: Service 是 ECS 的大腦和管家。它的職責是維持我們想要的狀態。
  • 重點:確保廚房的一切都按照我們所想要的執行秩序運行下去
    • desiredCount: { N } :「我希望永遠有 { N } 份『法式紅酒燉牛肉』在火上烹飪,不多也不少。」
    • 透過 Health Check ,總廚會不斷巡視所有爐子是不是正常的。如果他發現其中一份因為爐子故障了(HC fail),他會立刻把這份丟掉,並馬上開一個新的爐子上,確保開火中的爐口的數量永遠是 3。
    • 負載平衡 (Load Balancing): 當有爐點燃時(新的 Task 啟動),總廚會自動通知前台的服務生(Application Load Balancer),告訴他現在多了一個出餐口,可以把客人的訂單(流量)也送過來。

實作範例

{
  "family": "app-service",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "your-registry/app:${ENVIRONMENT}",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "${ENVIRONMENT}"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:${ENVIRONMENT}/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/${ENVIRONMENT}/app",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

ECS 多環境下的 ECS 實踐

簡單說明了 ECS 的意涵之後,那我們的 Dev Kitchen Worktop, Staging Kitchen Worktop, Prod Kitchen Worktop 策略在 ECS 層級如何體現?

  • 叢集隔離: 遵循多帳號策略,我們會在 Account-Dev 裡建立 dev-cluster,在 Account-Prod 裡建立 prod-cluster。這是最徹底的隔離。

  • SOP 卡 (Task Definition) 的管理: 我們依然使用 IaC (Terraform) 來管理任務定義。我們會有一份共用的 task-definition.tf 模板,然後為不同環境傳入不同的參數。

    • dev.tfvars: cpu = 256 (1/4 vCPU), memory = 512 (MB), API_KEY_SECRET_ARN = "arn:aws:secretsmanager:ap-northeast-1:DEV_ACCOUNT_ID..."

    • prod.tfvars: cpu = 1024 (1 vCPU), memory = 2048 (MB), API_KEY_SECRET_ARN = "arn:aws:secretsmanager:ap-northeast-1:PROD_ACCOUNT_ID..."

    • 注意囉,我們 image 的參數會指向 同一個 Docker Image ,但注入的 CPU記憶體Secrets 依舊來自不同的環境配置,維持了我們的 一致性不變性

  • 總廚指令 (Service) 的管理: 同樣用 IaC 定義。

    • dev 環境的 Service,desiredCount 可能只是 1 ,而且可能不設定自動擴縮,以節省成本。
    • prod 環境的 Service,desiredCount 可能是 3 起跳,並且會配置接下來要講的自動擴縮策略。

3.2 ECS 自動擴縮配置

我們的餐廳不能只靠固定 3 位廚師。有時候是有規律的,像是週二下午可能一位就夠了,但週五晚上可能需要 10 位才能應付源源不絕的訂單這種固定的規律模型,有時又會突然人潮爆量到需要 20 位廚師來 handle。

這時候手動去增減廚師數量太慢且不切實際,所以我們需要設置一個 「客流-爐口監控連動系統」( ECS Service Auto Scaling ) ,讓我們能根據「客流量」自動增減人手從而最佳化我們的成本考量。

  • 核心思想: 監控一個指標,並根據這個指標的變化,自動調整 Service 的 desiredCount。
  • 最常用的指標 (Metrics):
    • CPU 利用率 (CPU Utilization): 「如果廚師們的平均忙碌程度超過 70%,就增加人手。」這是最常見的指標。
    • 記憶體利用率 (Memory Utilization): 「如果廚房的平均空間佔用超過 75%,就擴大廚房(增加 Task)。」
    • ALB 請求數 (Request Count Per Target): 這是最能反映真實用戶壓力的指標。「如果平均每個出餐口每分鐘要處理的訂單超過 500 張,就立刻增加出餐口。」
  • 設定閾值: 我希望這個ALBRequestCountPerTarget指標維持在 500 左右。
  • 設定邊界:
    • min_capacity = 2:無論如何,廚房裡至少要有 2 位廚師待命。
    • max_capacity = 20:最多就加到 20 位,再多可能我的成本就爆了,或者後端資料庫也撐不住了。

運作流程大致如下:

  • 擴展 (Scale Out): 當週五晚上 7 點流量湧入,平均請求數飆升到 800。Auto Scaling 偵測到後,立刻自動提高 Service 的 desiredCount (例如從 2->3, 3->5...),ECS 就會啟動新的 Task。新的 Task 自動註冊到 ALB,分攤流量,直到平均請求數降回 500 左右。
  • 縮減 (Scale In): 到了晚上 10 點,客人陸續離開,平均請求數掉到 100。Auto Scaling 發現後,會逐步減少 desiredCount,ECS 就會優雅地終止掉多餘的 Task,將資源釋放,為我們節省成本。
  • 排程(Period): 假如我們已經知道每週五晚上 7 點有 固定流量湧入 且 在 晚上 11 點 客人散的差不多了的話,那我們可以固定化的設置在 19:00-23:00 這個區建置固定的 Task { N }數來面對流量,這又能更進一步地減少成本花費 - 監聽也是要錢的。
# ecs-auto-scaling.yml
Resources:
  ServiceScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: !Ref MaxCapacity
      MinCapacity: !Ref MinCapacity
      ResourceId: !Sub "service/${ClusterName}/${ServiceName}"
      RoleARN: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs

  ServiceScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: !Sub "${ServiceName}-scaling-policy"
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        TargetValue: 70.0

4. Amazon EKS 與 Kubernetes 管理

人力有時而窮,味蕾也會出現疲乏。有時候要吸引更多的人而開更多的 米其林餐廳 不一定是更好的選擇。或許是口味考量,或許是預算的考量,當我們的服務進行業務邏輯的擴張時,一直在同一家餐廳裡不斷增長新的菜色會讓管理成本與複雜度逐漸增加,同時,也有可能開了一新店後,大家都指點某一個菜品(例如米其林主廚做的起司漢堡與吃完後自動爆炸的餐廳),其他服務根本沒用上 - 這不是廠商的疏失,這無異是成本上的浪費。

如果我們接下來的目標是開一個根據不同客群擁有數十個不同品牌(微服務)、未來還可能把分店開到 Google 或 Microsoft 商場(多雲)的餐飲集團,那麼我們就需要一個業界通用的、標準化的「集團營運系統」。這個系統,就是 Kubernetes (常簡稱為 K8s)。

Amazon EKS (Elastic Kubernetes Service),就是 AWS 提供的、幫我們把這個複雜營運系統中最核心、最燒腦的「集團總部管理室」(控制平面)給託管起來的服務。=

4.1 EKS 叢集配置 — 搭建我們的美食廣場

如果說 ECS Cluster 是一間獨立餐廳的後廚,那麼 EKS Cluster 就是整個美食廣場的場地和基礎設施,其中最重要的就是 「美食廣場管理辦公室」(控制平面 - Control Plane)「美食攤位的鋪位」(數據平面 - Data Plane / Worker Nodes)

  • 控制平面 (Control Plane) - 「美食廣場管理辦公室」

    • 概念: 這是 Kubernetes 的大腦。它負責接收指令、調度所有攤位(應用程式)的開設位置、監控所有攤位的營運狀況、儲存整個廣場的佈局圖等。這部分極度複雜且至關重要,必須保證 7x24 小時高可用。
    • EKS 的價值: 我們不用自己去蓋這個管理辦公室,也不用去聘請保全、IT 人員來維護它。AWS EKS 會提供一個高可用、安全、自動化升級的管理辦公室給我們。這是我們付錢給 EKS 最主要的原因。我們只需要專心經營我們的美食攤位。
  • 數據平面 (Data Plane) / Worker Nodes - 「美食攤位的鋪位」

    • 概念: 這是實際「烹飪」的地方,也就是我們的應用程式容器實際運行的伺服器。
    • 在 EKS 中,主要有兩種形式: - EC2 Managed Node Groups: 和 ECS 的 EC2 模式類似。我們向廣場管理處租下一排「標準鋪位」(EC2 instances)。我們可以選擇鋪位的規格(例如 m5.large ),但鋪位的電力、網路、消防等都由 AWS 協助管理,例如自動化的節點升級和替換。這是最常見的模式。 - Fargate Profiles: 和 ECS Fargate 模式一樣。我們連鋪位都不用租。我們直接跟管理處說:「我要開一個臨時的章魚燒攤位,需要 1vCPU 和 2GB 記憶體的空間。」EKS 會自動在廣場的某個角落給我們變出一個符合要求的空間,用完就收走。非常適合無狀態應用或需要應對突發流量的場景。

多環境策略: 原則不變。我們會在 Account-Dev 裡建立一個 dev-eks-cluster ,在 Account-Prod 裡建立 prod-eks-cluster。IaC (Terraform) 依然是我們用來標準化定義這些叢集配置的工具。

# eks-cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: app-cluster-${ENVIRONMENT}
  region: us-west-2
  version: "1.28"

nodeGroups:
  - name: worker-nodes
    instanceType: ${INSTANCE_TYPE}
    minSize: ${MIN_SIZE}
    maxSize: ${MAX_SIZE}
    desiredCapacity: ${DESIRED_SIZE}
    privateNetworking: true
    labels:
      environment: ${ENVIRONMENT}
    tags:
      Environment: ${ENVIRONMENT}
      Project: "multi-env-demo"

addons:
  - name: vpc-cni
  - name: coredns
  - name: kube-proxy
  - name: aws-load-balancer-controller

4.2 Kubernetes 應用部署

Kubernetes 的世界裡,描述「如何經營一個攤位」有一套標準的詞彙和文件(YAML 檔)。這比 ECS 的 Task Definition 更為細緻和強大。

  1. Pod - 「一個獨立的烹飪工作站」
  • 概念: Kubernetes 中最小的部署單元。它可以包含一個或多個緊密關聯的容器。
  • 比喻: Pod 不僅僅是那口鍋 (Container),它還包括了鍋子旁邊的一小塊工作台、獨立的水槽和電源插座 (獨立的網路 IP 位址),以及一個專用的小冰箱 (儲存空間 Volume)。Pod 內的容器可以共享這個小環境。
  • 關鍵特性: Pod 是有生命週期的 (Ephemeral),它們隨時可能被銷毀和重建。我們絕不能直接依賴某一個 Pod 的存在。
  1. Deployment - 「連鎖品牌加盟手冊」
  • 概念: Deployment 是一個更高層級的控制器,它的核心職責是確保指定數量的、一模一樣的 Pod 副本正在運行。
  • 比喻: 我們是「A+ 炸雞排」的品牌方。我們的 Deployment YAML 就是我們的加盟手冊,上面寫著:
    • replicas: 3:我要求在美食廣場裡,必須時刻保持 3 個「A+ 炸雞排」的烹飪工作站 (Pod) 在運作。
    • template: 手冊裡詳細描述了標準工作站的規格(用哪個 Docker Image、需要多少 CPU/Memory 等)。
    • 自我修復: 如果 Deployment 發現只有 2 個工作站在運作,它會立刻自動建立第 3 個。
    • 滾動更新 (Rolling Update): 當我們要推出新口味(更新 Docker Image)時,Deployment 會非常聰明地:先開一個新的工作站 -> 等新站準備就緒 -> 才關掉一個舊的站。這樣逐步替換,確保我們的攤位服務不中斷。
  1. Service - 「攤位的叫號器與內部專線」
  • 概念: Pods 是會不斷生滅的,它們的 IP 位址也一直在變。Service 提供了一個穩定不變的內部網路端點,來代表一組功能相同的 Pods。
  • 比喻: 我們的 3 個炸雞排工作站 (Pods) 可能在廣場的不同位置,而且還可能隨時換位。客人不可能追著跑。於是,我們在美食廣場的服務台註冊了一個固定的攤位號碼「B-52」 (Service)。
  • 運作方式:當美食廣場裡的其他攤位(例如「好喝飲料店」Pod)想訂購炸雞排時,它不需要知道任何一個炸雞排 Pod 的實際位置,它只需要呼叫「B-52」這個內部專線,Kubernetes 的網路系統就會自動把請求轉接到一個當前健康的炸雞排 Pod,這很好地確保了服務之間通訊的穩定性,是微服務架構的基石。
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
  namespace: ${ENVIRONMENT}
  labels:
    app: demo-app
    environment: ${ENVIRONMENT}
spec:
  replicas: ${REPLICA_COUNT}
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
        environment: ${ENVIRONMENT}
    spec:
      containers:
        - name: app
          image: your-registry/app:${IMAGE_TAG}
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: ${ENVIRONMENT}
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: database-secret
                  key: url
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "200m"
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: app-service
  namespace: ${ENVIRONMENT}
spec:
  selector:
    app: demo-app
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP

4.3 Ingress 與 Load Balancer 配置

現在,攤位都開好了,內部員工也可以互相點餐了。但最重要的問題:外面的顧客要怎麼走進來,並找到我們的攤位?

一個簡單粗暴的方法是給每個攤位都開一個直通外面的大門 (Service type LoadBalancer)。這會為每個服務都建立一個獨立的 AWS 負載平衡器,在有數十個服務時,成本會非常驚人。

更優雅、更經濟的做法是使用 IngressIngress 是管理外部流量進入叢集的 API 物件,它提供基於 HTTP/HTTPS 的路由規則,這就像是整個美食廣場只有一個總入口,而在入口處有一塊巨大的電子導覽看板,上面寫著:

  • 想去「A+ 炸雞排」( host: food.com, path: /chicken ) -> 請走 B 區 52 號 (導向 chicken-service )
  • 想去「C- 披薩屋」( host: food.com, path: /pizza ) -> 請走 C 區 03 號 (導向 pizza-service )

構成我們的大門( Ingress )體系的兩個關鍵組件:

  1. Ingress Controller - 「入口的警衛與導覽員」 : 它是一個實際在叢集中運行的程式 (本身也是 Pod)。它負責監聽我們定義的 Ingress 規則,並實際去設定那個位於總入口的 AWS Application Load Balancer (ALB)。在 EKS 環境中,最常用的就是 AWS Load Balancer Controller。
  2. Ingress Resource - 「我們寫的那份導覽圖規則」 : 這是一個 YAML 檔案,我們向 Kubernetes 提交這個檔案,告訴 Ingress Controller 我們想要的路由規則。

Ingress 的巨大優勢就是我們可以用一個 ALB,來作為數十甚至數百個後端服務的流量入口,透過不同的域名或 URL 路徑,將流量精準分發給對應的 Service。這極大地節省了成本,並簡化了外部流量的管理,基德再也不會找上門了

# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: ${ENVIRONMENT}
  annotations:
    kubernetes.io/ingress.class: "alb"
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: ${CERTIFICATE_ARN}
    alb.ingress.kubernetes.io/ssl-redirect: "443"
spec:
  rules:
    - host: ${ENVIRONMENT}.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service
                port:
                  number: 80

ECS vs. EKS

我們今天(?)從宏觀的叢集搭建,到微觀的應用部署,再到流量的入口管理,完整地走了一遍 Kubernetes 的核心路徑。這套體系雖然複雜,但它正是支撐起當今大規模微服務應用的骨架。

最後的最後我們來簡單比較一下兩者的差別:

ECS 是條理分明的精緻餐廳:

AWS 幫我們定義好了很多規則,我們只需要專注於菜色(我們的應用),整合度高,學習曲線平緩。

EKS/Kubernetes 是生機勃勃的國際美食廣場:

它提供了一套功能極其強大、靈活、且廠商中立的「營運標準」。我們需要學習它的一整套術語和概念(Pod, Deployment, Service, Ingress...),但回報是無與倫比的彈性、擴展性和活躍的生態圈。


上一篇
Day 16-1 | Dev / Staging / Prod 多環境治理與架構策略: AWS 多環境配置管理與部署策略(一):多環境架構設計原則與Docker 容器化策略
系列文
AWS架構師的自我修養:30天雲端系統思維實戰指南32
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言