
はじめに
こんにちは、ZOZOMO部SREブロックの中村です。普段はZOZOMOのSREを担当しています。
本記事では、ECS on FargateにPipeCDを導入してGitOpsベースのデプロイ基盤を構築した取り組みをご紹介します。デプロイ経路の複数存在による管理の煩雑さと、段階的デプロイができない課題をPipeCDでどのように解決したかを解説します。
本記事がECS on Fargateを運用していてGitOps化に興味がある方や、PipeCDの導入を検討している方の参考になれば幸いです。
目次
導入前の課題
PipeCD導入前、私たちのチームでは以下の課題を抱えていました。
- デプロイ経路の複数存在による管理の煩雑さ
- 段階的デプロイができない
デプロイ経路の複数存在による管理の煩雑さ
アプリケーションのタスク定義をデプロイする経路が複数存在していました。アプリ、インフラそれぞれのデプロイの仕方によってデプロイ方法が異なり、どの経路でデプロイされたかの追跡が困難な状況でした。これにより以下の問題が発生していました。
- デプロイ履歴の一元管理ができない
- 障害発生時の原因特定に時間がかかる
- 予期しないロールバックを引き起こす可能性
段階的デプロイができない
従来の環境では、新しいバージョンのアプリケーションを一度に全てのタスクにデプロイしていました。問題のあるリリースでは全ユーザーに影響するリスクがありました。
- カナリアリリースの仕組みがない
- 問題発生時の影響範囲が大きい
- ロールバックに時間がかかる
解決アプローチ
これらの課題を解決するために、PipeCDの導入を決定しました。
CDツールの選定にあたり、Argo CD、CodePipeline(CodeBuild + CodeDeploy)、PipeCDを比較検討しました。
| 観点 | Argo CD | CodePipeline | PipeCD |
|---|---|---|---|
| Kubernetes対応 | サポート | 非対応 | サポート |
| ECS 対応 | 非対応 | サポート | サポート |
| GitOps | 対応 | 非対応 | 対応 |
| カナリアリリース | 対応 | 対応 | 対応 |
Argo CDはEKSを含むKubernetes環境では広く採用されていますが、ECSへのネイティブ対応がありません。CodePipelineはECS on Fargateに対応していますが、GitOpsのようにGitの情報を正とは必ずしも言えないと思います。一方、PipeCDはECSを含むマルチプラットフォームに対応しており、単一のツールでGitOpsとカナリアリリースを実現できる点が決め手となりました。
PipeCDとは
PipeCDは、マルチクラウド・マルチプラットフォームに対応した継続的デリバリー(CD)ツールです。GitOpsの原則に基づき、Gitリポジトリをデプロイにおいて信頼できる唯一の情報源(Single Source of Truth)として扱います。
主な特徴としては以下の点が挙げられます。
- GitOpsベース: プルリクエストによるデプロイ操作で、変更履歴が明確に管理される
- マルチプラットフォーム対応: Kubernetes、ECS、Lambda、Cloud Run、Terraformなど幅広く対応
- 段階的デプロイ: カナリアリリース、ブルーグリーンデプロイメントを標準サポート
- セキュリティ: デプロイ用の認証情報がクラスター外に出ない設計
- 可視化: デプロイ状況をWeb UIでリアルタイムに確認可能
またPipeCDは、大きく分けてControl PlaneとPipedの2つのコンポーネントで構成されます。
| コンポーネント | 役割 |
|---|---|
| Control Plane | Web UI、APIサーバー、デプロイ状況の管理 |
| Piped | 実際のデプロイを実行するエージェント。GitリポジトリとECSを監視し、差分を検知してデプロイを実行 |
Pipedはデプロイ対象の環境(今回はECS)と同じネットワーク内に配置します。
詳細は以下のPipeCD公式ドキュメントをご参照ください。
導入構成
今回構築した環境の全体像は以下のとおりです。

- Control Plane: 専用のECSで稼働
- Piped: デプロイ対象と同じVPC内のECSクラスターで稼働
- マニフェスト管理: 専用のGitHubリポジトリで管理
PipeCDへのログインには、Entra IDを用いたSAML認証を採用しました。弊社ではSSO基盤としてEntra IDを広く利用しており、一元管理されたアクセス管理を実現しています。
実装
Control Planeの構築
Control Planeは以下のコンポーネントで構成され、今回はこのような構成で作成しました。
| コンポーネント | 役割 | 今回の構成 |
|---|---|---|
| Server | Web UI、APIサーバー | ECS on Fargate |
| Ops | 管理用サーバー | ECS on Fargate |
| Gateway | gRPC/HTTPルーティング | Envoy(サイドカー) |
| Cache | セッション・ログのキャッシュ | Redis(サイドカー) |
| Datastore | デプロイ情報の永続化 | Aurora MySQL |
| Filestore | ログ等のファイル保存 | S3 |
PipeCDの利用を検証した際にそこまで膨大なセッションやログのキャッシュを持たないことから、ElastiCacheを利用するのではなく、サイドカーにRedisを立ててコスト面を抑える構成にしました。ECS上でのControl Plane構築例はPipeCD公式のデモリポジトリを参考に実装しました。
事前準備
Control Planeを構築する前に、以下のリソースを作成しました。
- Aurora MySQL: Datastore用のデータベース
- S3バケット: Filestore用のバケット
- ALB: gRPC/HTTP通信用のロードバランサー(Target GroupはgRPC用とHTTP用の2つ)
- Secrets Manager: 設定ファイルや暗号化キーの保存
ECSタスク構成
Control PlaneのECSタスクを、以下のコンテナで構成しました。
┌─────────────────────────────────────────────────────┐ │ ECS Task (Control Plane) │ │ ┌─────────────┐ ┌─────────────┐ ┌───────────────┐ │ │ │ Redis │ │ Envoy │ │ PipeCD Server │ │ │ │ (cache) │ │ (gateway) │ │ │ │ │ │ port:6379 │ │ port:9090 │ │ port:9080-9083│ │ │ └─────────────┘ └─────────────┘ └───────────────┘ │ └─────────────────────────────────────────────────────┘
- Redis: ログやセッション情報のキャッシュ
- Envoy: gRPC/HTTPリクエストのルーティング(ALBからのトラフィックを受信)
- PipeCD Server: Web UIとAPIを提供
設定ファイル
Control Planeの設定はcontrol-plane-config.yamlで定義します。
apiVersion: "pipecd.dev/v1beta1" kind: ControlPlane spec: datastore: type: MYSQL config: url: ${DB_USER}:${DB_PASS}@tcp(pipecd-mysql:3306) database: controlplane filestore: type: S3 config: bucket: ${ENV}-${RESOURCE_PREFIX}-pipecd-filestore region: ap-northeast-1 stateKey: ${statekey} sharedSSOConfigs: - name: cognito-azure provider: OIDC oidc: clientId: ${clientId} clientSecret: ${clientSecret} issuer: https://cognito-idp.ap-northeast-1.amazonaws.com/${userPoolId} redirectUri: https://pipecd.${DOMAIN}/auth/callback scopes: - openid - profile
設定ファイルはBase64エンコードしてSecrets Managerに登録し、ECSタスク定義から参照します。詳細は公式ドキュメント(Configuration reference)をご参照ください。
また、Envoy(Gateway)の設定はenvoy-config.yamlで定義します。EnvoyはgRPC/HTTPリクエストをPipeCD Serverの各サービスにルーティングする役割を担います。
admin: address: socket_address: address: 0.0.0.0 port_value: 9095 static_resources: listeners: - name: ingress address: socket_address: address: 0.0.0.0 port_value: 9090 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: AUTO stat_prefix: ingress_http http_filters: - name: envoy.filters.http.grpc_web - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router route_config: name: local_route virtual_hosts: - name: envoy domains: - '*' routes: # Piped Service(Pipedとの通信) - match: prefix: /grpc.service.pipedservice.PipedService/ grpc: {} route: cluster: grpc-piped-service # Web Service(Web UIからの通信) - match: prefix: /grpc.service.webservice.WebService/ grpc: {} route: cluster: grpc-web-service # API Service(外部APIからの通信) - match: prefix: /grpc.service.apiservice.APIService/ grpc: {} route: cluster: grpc-api-service # その他のHTTPリクエスト(静的ファイル等) - match: prefix: / route: cluster: server-http clusters: - name: grpc-piped-service http2_protocol_options: {} connect_timeout: 0.25s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: grpc-piped-service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: localhost port_value: 9080 - name: grpc-web-service http2_protocol_options: {} connect_timeout: 0.25s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: grpc-web-service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: localhost port_value: 9081 - name: grpc-api-service http2_protocol_options: {} connect_timeout: 0.25s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: grpc-api-service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: localhost port_value: 9083 - name: server-http connect_timeout: 0.25s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: server-http endpoints: - lb_endpoints: - endpoint: address: socket_address: address: localhost port_value: 9082
Envoyの設定では、リクエストのパスに応じてPipeCD Serverの各サービスにルーティングしています。
ECSタスク定義(CloudFormation)
Control PlaneのECSタスク定義は以下のように構成しました。Redis、Envoy(Gateway)、PipeCD Serverの3コンテナをサイドカー構成で動作させています。
Secretsで宣言しているCONTROL_PLANE_CONFIGとENVOY_CONFIGは、それぞれの設定ファイルをBase64エンコードした値です。これらはControl PlaneとEnvoyの起動時に利用されます。必要なリソースは別途宣言する必要があります。
ECSTaskDefinition: Type: 'AWS::ECS::TaskDefinition' Properties: Family: !Sub '${Env}-${ResourcePrefix}-pipecd-control-plane' NetworkMode: 'awsvpc' RequiresCompatibilities: - 'FARGATE' Cpu: !Ref ECSTaskCPU Memory: !Ref ECSTaskMemory ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn TaskRoleArn: !GetAtt ECSTaskRole.Arn ContainerDefinitions: # Redisコンテナ(キャッシュ用) - Name: 'redis' Image: 'redis:7.4-alpine' Essential: false PortMappings: - ContainerPort: 6379 Protocol: tcp Command: - 'redis-server' - '--appendonly' - 'yes' - '--maxmemory' - '128mb' - '--maxmemory-policy' - 'allkeys-lru' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogsLogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: 'redis' # Envoyコンテナ(Gateway) - Name: 'pipecd-gateway' Image: !Ref PipeCDGatewayImageURL # envoyproxy/envoy Essential: false PortMappings: - HostPort: 9090 ContainerPort: 9090 Protocol: tcp Command: - '/bin/sh -c ''echo $ENVOY_CONFIG | base64 -d >> envoy-config.yaml; envoy -c envoy-config.yaml;''' EntryPoint: - sh - '-c' Secrets: - Name: 'ENVOY_CONFIG' ValueFrom: !Sub '${SecretsManagerEnvoyConfigArn}:config::' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogsLogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: 'pipecd-gateway' DependsOn: - ContainerName: 'redis' Condition: 'START' # PipeCD Serverコンテナ - Name: 'pipecd-server' Image: !Ref PipeCDServerImageURL # ghcr.io/pipe-cd/pipecd Essential: true Command: - !Sub '/bin/sh -c ''echo $CONTROL_PLANE_CONFIG | base64 -d >> control-plane-config.yaml; echo $ENCRYPTION_KEY >> encryption-key; pipecd server --insecure-cookie=true --cache-address=localhost:6379 --config-file=control-plane-config.yaml --encryption-key-file=encryption-key;''' EntryPoint: - sh - '-c' Secrets: - Name: 'CONTROL_PLANE_CONFIG' ValueFrom: !Sub '${SecretsManagerControlPlaneConfigArn}:config::' - Name: 'ENCRYPTION_KEY' ValueFrom: !Sub '${SecretsManagerEncryptionKeyArn}:key::' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogsLogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: 'pipecd-server' DependsOn: - ContainerName: 'redis' Condition: 'START'
Control Planeの初期設定
Control PlaneのECSタスクがデプロイされた後、初期設定を実施します。この手順でPipedの認証情報を取得するため、Piped構築の前に実施します。
OPSサーバーへの接続
PipeCD OPSサーバーに接続し、プロジェクトの作成やユーザー管理を実施します。OPSサーバーは外部公開していないため、SSM Session Managerを使用してポートフォワードで接続します。
ポートフォワードが確立されたら、ブラウザでlocalhostを叩き、OPSサーバーにアクセスして管理画面を開きます。

プロジェクトの作成
管理画面から「Add Project」でプロジェクトを作成します。
| 項目 | 設定値 |
|---|---|
| ID | プロジェクト識別子(例:your-project-dev) |
| Description | 任意の説明 |
| Shared SSO | cognito-azure(control-plane-config.yamlで定義したSSO設定名) |

プロジェクト作成後に表示される Static Admin Username と Static Admin Password は、初回ログインに必要なため必ずメモしておきます。
ユーザーグループの作成
- Control PlaneのURLにアクセス(例:
https://pipecd.your-domain.com) - 作成したプロジェクト名を入力
- Static Adminの認証情報でログイン
- 「Settings」→「User Groups」からユーザーグループを作成


| グループ名 | ロール | 権限 |
|---|---|---|
| Admin | Admin | プロジェクト全体の管理権限 |
| Editor | Editor | デプロイ・設定変更(ユーザー管理除く) |
| Viewer | Viewer | 読み取り専用 |
権限の詳細は公式ドキュメント(Authentication and authorization)をご参照ください。
SSOログインの確認
- ログアウトし、「Login with OIDC」をクリック
- Entra IDの認証画面でログイン
Pipedの登録
Control PlaneにPipedを登録し、Piped構築に必要な認証情報を取得します。
- 「Settings」→「Piped」→「Add Piped」をクリック
- Piped情報を入力(Name、Description)
- 「Save」をクリック
- 表示される以下の情報をメモ
- Piped Id: Piped設定ファイルの
pipedIDに使用 - Base64 Encoded Piped Key: Piped設定ファイルの
pipedKeyDataに使用
- Piped Id: Piped設定ファイルの


これらの値は次の「Pipedの構築」で使用します。
Pipedの構築
PipedはControl Planeと通信し、実際のデプロイを実行するエージェントです。デプロイ対象のECSクラスターと同じVPC内にECS on Fargateで構築しました。
Pipedの設定はpiped-config.yamlで定義します。
apiVersion: pipecd.dev/v1beta1 kind: Piped spec: projectID: ${PROJECT_ID} pipedID: ${PIPED_ID} pipedKeyData: ${PIPED_KEY_DATA} apiAddress: pipecd.${DOMAIN}:443 git: sshKeyData: ${SSH_KEY_DATA} repositories: - repoId: ${REPO} remote: git@github.com:hoge/${REPO}.git branch: main platformProviders: - name: ECS type: ECS config: region: ap-northeast-1 notifications: routes: - name: xxxxx_ci_notice events: - DEPLOYMENT_TRIGGERED - DEPLOYMENT_SUCCEEDED - DEPLOYMENT_FAILED - DEPLOYMENT_ROLLING_BACK receiver: xxxxx_ci_notice receivers: - name: xxxxx_ci_notice slack: hookURL: ${SLACK_WEBHOOK_URL}
pipedIDとpipedKeyDataは、Control PlaneのWeb UIからPipedを登録した際に発行されます。詳細は公式ドキュメント(Configuration reference)をご参照ください。
ECSタスク定義(CloudFormation抜粋)
PipedのECSタスク定義は以下のように構成しました。Secretsで宣言しているCONFIG_DATAは、piped-config.yamlをBase64エンコードした状態で取得し、Pipedの起動時に利用されます。
ECSTaskDefinitionPiped: Type: 'AWS::ECS::TaskDefinition' Properties: Family: !Sub '${Env}-${ResourcePrefix}-pipecd-piped' NetworkMode: 'awsvpc' RequiresCompatibilities: - 'FARGATE' Cpu: !Ref PipeCDPipedTaskCpuType Memory: !Ref PipeCDPipedTaskMemoryType ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn TaskRoleArn: !GetAtt ECSTaskRole.Arn ContainerDefinitions: - Name: 'piped' Image: !Sub '${ECRRepositoryUri}:${PipeCDPipedImageTag}' Essential: true EntryPoint: - sh - -c Command: - /bin/sh -c "piped piped --config-data=$(echo $CONFIG_DATA)" Secrets: - Name: 'CONFIG_DATA' ValueFrom: !Sub '${SecretsManagerPipedConfigArn}:config::' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogsLogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: 'piped'
IAMロール
PipedがECSサービスをデプロイするために、タスクロールに以下の権限を付与しています。
ECSTaskRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Sub '${Env}-${ResourcePrefix}-pipecd-piped-ecs-task-role' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Principal: Service: 'ecs-tasks.amazonaws.com' Action: 'sts:AssumeRole' Policies: - PolicyName: 'PipedECSServiceAccess' PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - ecs:CreateService - ecs:CreateTaskSet - ecs:DeleteTaskSet - ecs:DeregisterTaskDefinition - ecs:DescribeServices - ecs:DescribeTaskDefinition - ecs:DescribeTaskSets - ecs:DescribeTasks - ecs:ListClusters - ecs:ListServices - ecs:ListTaskDefinitions - ecs:ListTasks - ecs:RegisterTaskDefinition - ecs:RunTask - ecs:TagResource - ecs:UntagResource - ecs:ListTagsForResource - ecs:UpdateService - ecs:UpdateServicePrimaryTaskSet - elasticloadbalancing:DescribeListeners - elasticloadbalancing:DescribeRules - elasticloadbalancing:DescribeTargetGroups - elasticloadbalancing:ModifyListener - elasticloadbalancing:ModifyRule Resource: "*" - Effect: 'Allow' Action: - iam:PassRole Resource: - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${Env}-${ResourcePrefix}-ecs-task-execution-role' - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${Env}-${ResourcePrefix}-task-role'
PipedステータスのONLINE確認
PipedのECSタスクが起動したら、Control PlaneでPipedのステータスを確認します。
- Control PlaneのWeb UIにログイン
- 「Settings」→「Piped」へ移動
- 登録したPipedのステータスを確認

ステータスが ONLINE(緑色のインジケーター)になっていれば、Control Planeとの接続が正常に確立されています。
ステータスがOFFLINEの場合は、以下を確認してください。
- Piped設定ファイルの
pipedIDとpipedKeyDataが正しいか apiAddressがControl PlaneのURLと一致しているか- ECSタスクのログでエラーが出ていないか
マニフェストファイルの構成
PipeCDでECSアプリケーションを管理するために、以下のファイルを用意しました。
| ファイル | 役割 |
|---|---|
| app.pipecd.yaml | PipeCDアプリケーション定義(デプロイ戦略、参照ファイルの指定等) |
| servicedef.yaml | ECSサービス定義 |
| taskdef.yaml | ECSタスク定義 |
app.pipecd.yaml
PipeCDのアプリケーション設定ファイルです。デプロイ対象のサービス定義・タスク定義ファイルやターゲットグループを指定します。
apiVersion: pipecd.dev/v1beta1 kind: ECSApp spec: name: hogehoge labels: env: dev team: zozo-xxxx app: hogehoge notification: mentions: - event: DEPLOYMENT_TRIGGERED - event: DEPLOYMENT_SUCCEEDED - event: DEPLOYMENT_FAILED - event: DEPLOYMENT_ROLLING_BACK input: serviceDefinitionFile: servicedef.yaml taskDefinitionFile: taskdef.yaml targetGroups: primary: targetGroupArn: arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxx:targetgroup/primary/xxxxxxxxxxxxx containerName: hogehoge containerPort: 8080 canary: targetGroupArn: arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxx:targetgroup/canary/xxxxxxxxxxxxx containerName: hogehoge containerPort: 8080 pipeline: stages: - name: ECS_CANARY_ROLLOUT with: scale: 20 - name: ECS_TRAFFIC_ROUTING with: canary: 10 - name: WAIT_APPROVAL - name: ECS_PRIMARY_ROLLOUT - name: ECS_TRAFFIC_ROUTING with: primary: 100 - name: ECS_CANARY_CLEAN
設定項目の詳細は公式ドキュメント(Configuring ECS application)をご参照ください。
servicedef.yaml
ECSサービスの定義ファイルです。
version: v1 spec: cluster: arn:aws:ecs:ap-northeast-1:xxxx:cluster/sample-cluster serviceName: hogehoge desiredCount: 6 launchType: FARGATE schedulingStrategy: REPLICA networkConfiguration: awsvpcConfiguration: subnets: - subnet-xxxx - subnet-yyyy - subnet-zzzz securityGroups: - sg-xxxx assignPublicIp: DISABLED propagateTags: SERVICE deploymentController: type: EXTERNAL
taskdef.yaml
ECSタスクの定義ファイルです。コンテナイメージやリソース設定を記述します。
family: hogehoge networkMode: awsvpc requiresCompatibilities: - FARGATE cpu: "256" memory: "512" executionRoleArn: arn:aws:iam::xxxx:role/ecsTaskExecutionRole taskRoleArn: arn:aws:iam::xxxx:role/hogehoge-task-role containerDefinitions: - name: app image: xxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hogehoge:latest essential: true portMappings: - containerPort: 8080 protocol: tcp logConfiguration: logDriver: awslogs options: awslogs-group: /ecs/hogehoge awslogs-region: ap-northeast-1 awslogs-stream-prefix: ecs
アプリケーションの登録
マニフェストファイルをGitリポジトリにプッシュしたら、Control PlaneでアプリケーションをPipedに紐づけます。
- Control PlaneのWeb UIにログイン
- 「Applications」→「Add」をクリック
- アプリケーション情報を入力
| 項目 | 設定値 |
|---|---|
| Name | アプリケーション名(例:hogehoge) |
| Kind | ECS |
| Piped | 登録済みのPipedを選択 |
| Repository | piped-config.yamlで定義したリポジトリを選択 |
| Path | マニフェストファイルが配置されているディレクトリパス |
| Config Filename | app.pipecd.yaml |

- 「Save」をクリック
登録が完了すると、PipedがGitリポジトリを監視し始めます。
デプロイの実行
アプリケーションの登録後、以下の流れでデプロイが実行されます。
初回デプロイ(Quick Sync)
アプリケーション登録直後は、現在のマニフェストの状態でデプロイが実行されます。「Applications」画面でデプロイの進行状況を確認できます。
通常のデプロイフロー
- マニフェストファイル(taskdef.yaml等)を変更
- GitリポジトリにPush
- Pipedが変更を検知し、自動でデプロイを開始
- app.pipecd.yamlで定義したパイプラインに従って段階的にデプロイ
補足: カナリアリリースの流れ
app.pipecd.yamlでカナリアリリースのパイプラインを定義していた場合、以下の流れでカナリアリリースが実行されます。
- ECS_CANARY_ROLLOUT: Canary用のTaskSetを作成
- ECS_TRAFFIC_ROUTING: Canaryにトラフィックを流す
- WAIT_APPROVAL: 承認待ち(Web UIで「Approve」をクリック)
- ECS_PRIMARY_ROLLOUT: Primary用のTaskSetを新バージョンに更新
- ECS_TRAFFIC_ROUTING: Primaryに100%のトラフィックを戻す
- ECS_CANARY_CLEAN: Canary用のTaskSetを削除

承認待ちの間にCanary環境で問題が見つかった場合は、Web UIから「Cancel」でロールバックできます。
Webhook連携による自動テスト実行
PipeCDの通知機能を活用して、デプロイ成功後に自動でE2Eテストを実行する仕組みを構築しました。
Pipedの設定(piped-config.yaml)で、特定のアプリケーション・環境のデプロイ成功時にWebhookを送信できます。
# piped-config.yaml notifications: routes: - name: acceptance_test_webhook events: - DEPLOYMENT_SUCCEEDED labels: app: test-app env: dev receiver: webhook_receiver receivers: - name: webhook_receiver webhook: url: ${WEBHOOK_URL}
私たちの環境では、WebhookをZapierに送信し、ZapierからGitHub Actionsのワークフローをトリガーしています。GitHub Actionsのワークフローでは、DevのAWS環境で稼働するtest-appへE2Eテストを実行しています。これにより、dev環境のtest-appデプロイ成功後にE2Eテストが自動実行される仕組みを実現しました。詳細は公式ドキュメント(Configuring Notifications)をご参照ください。
導入効果
PipeCDの導入により、以下の効果が得られました。
- デプロイ経路の一本化
- 段階的リリースによるリスク低減
デプロイ経路の一本化
GitOpsの原則に基づき、全てのデプロイがGitリポジトリを経由するようになりました。
- デプロイ履歴がGitのコミット履歴として残る
- 誰が・いつ・何をデプロイしたかが明確になった
- PipeCDのWeb UIでデプロイ状況をリアルタイムに確認可能
段階的リリースによるリスク低減
カナリアリリースの導入により、リリースに伴うリスクを大幅に低減できました。
- 新バージョンの問題を早期に検知可能
- 問題発生時の影響範囲を限定できる
- 自動ロールバックにより迅速な復旧が可能
まとめ
本記事では、ECS on FargateにPipeCDを導入してGitOpsベースの段階的リリースを実現した取り組みを紹介しました。ECS on FargateでGitOps化を検討している方や、カナリアリリースを導入したい方は、ぜひPipeCDを検討してみてください。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。