🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

我平常從事後端工程師的工作,經常在專案中使用 Terraform 和 ecspresso,然而這兩者的角色分配乍看之下似乎不太明確。本文將透過建立使用 Terraform 和 ecspresso 的範例專案來說明它們的生命週期差異。

讀完本文你將了解

  • Terraform 和 ecspresso 的角色差異
  • 為何 ecspresso 更適合用於 ECS 的變更管理
  • ecspresso 和 GitHub Actions 的範例程式碼

目標讀者

  • 後端工程師,對基礎架構有一定了解的人
  • 想了解 Terraform 和 ecspresso 差異的人

image

ecspresso 想要解決的問題

僅以 Terraform 管理 ECS 的變更所面臨的困難

「ecspresso 是專門用於管理 ECS 的工具。」
聽到這句話時,我心中不禁產生疑問:「Terraform 不是也可以管理 ECS 嗎?那為什麼還需要 ecspresso?」首先,我們來整理 AWS 資源的生命週期,確認 ecspresso 的適用範圍。

首先,Terraform 負責整體 AWS 的配置管理。大多數 AWS 資源在設定後變更的頻率較低。而在 ECS 上啟動的映像,卻是由應用程式工程師開發並經常進行部署。在具備 CI/CD 流程的優秀團隊中,可能會有每天多次高頻率的應用程式部署。

image

在這種情況下,如果使用 Terraform 來更新 ECS,會出現以下問題:

  • 只想變更 ECS 的任務定義,卻必須進行全 AWS 的差異檢查,有時甚至會同時變更非 ECS 的資源。
  • 如果通過 CI/CD 或其他手段更改 ECS 的設定,Terraform 會判定為存在差異,從而在執行 terraform apply 時,ECS 的狀態可能會被回滾至之前的狀態。
  • 當發生故障時,切回任務定義和映像會變得相當困難。

尤其在商業服務中,為了避免事故,往往希望將頻繁變更的 ECS 管理與其他變更頻率低的 AWS 資源管理區分開來。

ECS 的變更不以 Terraform 管理

因此,基於基礎架構中生命週期較短的資源,如 ECS,應避免使用 Terraform 管理,而是改用其他管理工具。ecspresso 正是擔負此任務的工具。

  • 表:AWS 資源管理的角色劃分
項目 Terraform ecspresso 變更頻率
ECS 任務定義
ECS 服務
ECS 叢集
其他 AWS 資源

明確劃分應用團隊與基礎架構團隊的界線

在開發流程中,ECS 以外的基礎架構管理應托管於基礎架構專用的 Git 倉庫,而應用程式的部署和 ECS 資源管理則應托管於應用程式專用的 Git 倉庫。

如此一來,應用團隊與基礎架構團隊的責任邊界就變得明確,應用團隊可以自行管理應用程式的部署,而無需向基礎架構團隊請求變更。此外,這樣也能減少在應用部署時與基礎架構團隊的溝通成本。

  • 倉庫劃分
    • 基礎架構倉庫:管理 AWS 所有資源 (Terraform)
    • 應用程式倉庫:應用程式代碼 + ecspresso 的管理

image

從 Terraform 管理轉換為 ecspresso 管理

刪除 Terraform 管理下的程式碼

以下程式碼是管理 ECS 的 tf 文件範例。最初是使用 Terraform 全面管理 ECS,但 services 下的部分以及 container_definitions 已被註解。因為這些不再屬於 terraform.tfstate 的管理範圍,所以即使重新部署容器,也不會在 tfstate 中出現差異。

module "ecs" {
  source = "terraform-aws-modules/ecs/aws"

  # 注意:Terraform 僅管理 ECS 叢集
  cluster_name = "${var.product_name}-${var.env}"

  cluster_configuration = {
  }

  default_capacity_provider_strategy = {
    FARGATE_SPOT = {
      weight = 1
    }
  }

  # 注意:刪除 Terraform 的 ECS 服務定義,接下來使用 ecspresso 來管理 ECS 服務
  services = {
    # fastapi-service = {
    #   cpu           = 256
    #   memory        = 512
    #   desired_count = 1

    # 注意:刪除 Terraform 的任務定義,轉移至 ecspresso
    #   container_definitions = {
    #     fastapi = {
    #       image     = data.aws_ecr_image.this.image_uri
    #       essential = true
    # ... (以下略)
    #   }
  }
}

※ 參考:最初使用的 Terraform 模組

將註解部分移至 ecspresso 的定義文件

在之前的 modules/ecs/main.tf 中被註解的部分,將會移至 ecspresso 的 ecs-service-def.jsonnetecs-task-def.jsonnet 進行管理。這兩者分別控制 ECS 服務和 ECS 任務定義。執行 ecspresso deploy 命令後,只有 ECS 服務和任務將會被更新

  • ECS 服務定義文件
{
  "deploymentConfiguration": {
    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": false
    },
    "maximumPercent": 200,
    "minimumHealthyPercent": 50
  },
  "deploymentController": {
    "type": "ECS"
  },
  "desiredCount": 1,
  "enableECSManagedTags": true,
  "enableExecuteCommand": true,
  "healthCheckGracePeriodSeconds": 30,
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "assignPublicIp": "ENABLED",
      "securityGroups": [
        "{{ tfstate `module.ecs.aws_security_group.ecs_sg.id` }}"
      ],
      "subnets": [
        "{{ tfstate `module.vpc.module.vpc.aws_subnet.public[0].id` }}",
        "{{ tfstate `module.vpc.module.vpc.aws_subnet.public[1].id` }}"
      ]
    }
  },
  "pendingCount": 0,
  "platformFamily": "Linux",
  "platformVersion": "LATEST",
  "propagateTags": "NONE",
  "runningCount": 0,
  "schedulingStrategy": "REPLICA"
}
  • ECS 任務定義文件
{
  "containerDefinitions": [
    {
      "cpu": 0,
      "essential": true,
      "image": "{{ tfstate `module.ecs.data.aws_ecr_repository.this.repository_url`}}:latest",
      "interactive": false,
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "{{tfstate `module.ecs.module.ecs.module.cluster.aws_cloudwatch_log_group.this[0].name`}}",
          "awslogs-region": "{{ tfstate `module.ecs.module.ecs.module.cluster.aws_cloudwatch_log_group.this[0].region` }}",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "name": "fastapi",
      "portMappings": [
        {
          "containerPort": 8000,
          "hostPort": 8000,
          "protocol": "tcp"
        }
      ],
      "privileged": false,
      "pseudoTerminal": false,
      "readonlyRootFilesystem": false,
      "startTimeout": 30,
      "stopTimeout": 120,
      "healthCheck": {
        "command": [
          "CMD-SHELL",
          "curl -f http://localhost:8000/ || exit 1"
        ],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 10
      }
    }
  ],
  "cpu": "256",
  "executionRoleArn": "{{ tfstate `module.ecs.aws_iam_role.ecs_task_execution_role.arn` }}",
  "family": "{{ tfstate `module.ecs.module.ecs.module.cluster.aws_ecs_cluster.this[0].tags.ServiceName` }}",
  "ipcMode": "",
  "memory": "512",
  "networkMode": "awsvpc",
  "pidMode": "",
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  },
  "taskRoleArn": "{{tfstate `module.ecs.aws_iam_role.ecs_task_role.arn`}}"
}

若想了解更詳細的使用方式,建議參考 ecspresso 作者所著的書籍。(¥500)

在 CI/CD 中整合 ecspresso

在應用程式倉庫中,設定當主分支有程式碼合併時,自動執行 GitHub Actions。GitHub Actions 的工作流程應如以下所示。

  1. 透過 GHA 建立 Docker 映像
    映像標籤需同時附上 latest 標籤和提交哈希
  2. 將 Docker 映像推送至 ECR
  3. 使用 ecspresso 進行部署

image

name: ECS 部署
on:
  push:
    branches:
      - main
  workflow_dispatch: # 供 GitHub UI 手動執行

jobs:
  build_and_push:
    name: 建立 Docker 並將 Docker 映像推送至 ECR
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: 檢出程式碼
        uses: actions/checkout@v4

      - name: 設定 AWS 憑證
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_AllowGitHubActions }}
          aws-region: ap-northeast-1

      - name: 登入 Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: 建立、標記並推送映像至 Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: n700a/fastapi-mini
          IMAGE_TAG: latest # ${{ GitHub.sha }}
        run: |
          docker build -f backend/Dockerfile -t $ECR_REPOSITORY:$IMAGE_TAG backend
          docker tag $ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

  deploy:
    name: 部署至 ECS
    needs: build_and_push
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: 檢出程式碼
        uses: actions/checkout@v4

      - name: 設定 AWS 憑證
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_AllowGitHubActions }}
          aws-region: ap-northeast-1

      # 在此執行 ecspresso deploy
      - name: 部署至 ECS 服務
        uses: kayac/ecspresso@v2
        with:
          version: v2.6.0
          args: deploy --config ./infra/dev/ecspresso/ecspresso.yml

設定此工作流程後,應用團隊將不再需處理 ECS 的部署任務,得以專注於應用程式源コード的創作。

總結

  • 與應用開發相關的 ECS 部分,其變更頻率高於其他 AWS 資源
  • 專注於應用 ECS 部署的工具為 ecspresso☕
  • 在 ecspresso 中,像是 RDB 的遷移工具一樣,部署 / 回滾都變得簡單

本文提到在 AWS 資源中,應用領域與基礎設施領域的部署頻率不同,並指出全用 Terraform 管理的問題。藉由同時使用 ecspresso 和 Terraform,可以明確劃分 AWS 資源中的應用管理與基礎架構管理區域。

本文未詳細說明 ecspresso 的具體環境建構方法。如有興趣,建議親自試試看。
希望本文能讓讀者更了解 ecspresso 的意義及概念。

參考文章

<iframe id="qiita-embed-content360a5c3b9c84f9a9cb8c18b8b321f484"></iframe>
<iframe id="qiita-embed-content
33c1a8d3cce3cb52534f600f38604321"></iframe>


原文出處:https://qiita.com/N700A/items/99a802307e0184d48e32


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝19   💬9   ❤️5
718
🥈
我愛JS
📝4   💬13   ❤️7
239
🥉
AppleLily
📝1   💬3   ❤️1
69
#4
御魂
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付