blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2025.09.25 技術記事

Pococha リアーキテクチャ:Argo CD による GitOps デプロイ戦略 [DeNA インフラ SRE]

by negimaq

#infrastructure #sre #pococha #aws #eks #gitops #kubernetes #argocd

はじめに

こんにちは、IT 本部 IT 基盤部第一グループの横田です。 IT 基盤部では、組織横断的に複数のプロダクトのインフラ運用を行っており、インフラ基盤の安定稼働やコスト削減に取り組んでいます。

本記事では、私たちがインフラ運用を担当する Pococha において、プロダクトの安定稼働とパフォーマンスの向上、そして開発体験の向上を実現するために現在行っているリアーキテクチャの事例をご紹介します。 特に、インフラ運用の観点でその中核となる、 Amazon EKS Argo CD を使用して構築した GitOps に基づくデプロイフローについて詳細を説明します。

本記事のポイント

  • 開発チームとインフラチームの責任分界点を明確にするため、Argo CD を使用して pull 型の GitOps を実現
  • Pull Request ベースの承認フローによる安全な本番デプロイの自動化

従来環境の問題点:なぜリアーキテクチャを行うのか

Pococha では、プロダクトとコードベースの規模が拡大するにつれて、大きく 2 つの課題に直面していました。

  1. プロダクトの安定稼働(インフラチームの課題)
  2. 巨大なモノリスアプリケーションのパフォーマンスと開発効率(開発チームの課題)

本記事では、1 つ目の「プロダクトの安定稼働」という課題に対して、どのようにリアーキテクチャを進めたか、その一部をインフラチームの視点から説明します。 2 つ目の「巨大なモノリスアプリケーションのパフォーマンスと開発効率」については、開発チームの記事をぜひご覧ください。

Pococha の従来のシステムは、Ruby on Rails アプリケーションが実行される Amazon EC2 で構成されており、デプロイには Capistrano というツールを使用していました。 これは、1 台のデプロイサーバーから、起動している全てのアプリケーションサーバーにソースコードを配布する、いわゆる push 型のデプロイです。 このデプロイ方法では、以下のような問題を抱えていました。

  • ロールバックに時間がかかる
    • デプロイが原因で問題が発生した場合、安全に切り戻すためには再度デプロイのプロセスを最初から実行する必要があり、復旧までに時間がかかっていました。
  • デプロイのロック競合
    • アプリケーションサーバーがオートスケールによってスケールアウトした際、新しいサーバーにソースコードを自動的にデプロイする仕組みがあります。この自動デプロイと、開発者による手動デプロイのタイミングが重なると、異なるリビジョンの同時デプロイを防ぐためのロックが競合し、どちらかのデプロイが失敗する問題が発生していました。

これらの課題を根本的に解決し、より安全で効率的なデプロイを実現するため、リアーキテクチャの中で GitOps に基づいたデプロイフローの構築を検討することになりました。

技術選定:どのようにシステムの構成要素を選択したか

今回のリアーキテクチャでは、複数の技術要素を組み合わせてデプロイフロー全体を設計しました。 ここでは、主要なコンポーネントを「なぜ採用したのか」という視点で、選定の流れをご紹介します。

Istio

まず前提として、従来のモノリスなアプリケーションから、機能ごとに独立したマイクロサービスへアーキテクチャを移行する要件がありました。 マイクロサービス化を進めるとサービス間の通信が複雑化するため、その通信を仲介し、通信制御や可視化を一手に引き受けるサービスメッシュ 1 の導入が妥当だと判断しました。 サービスメッシュにも複数の選択肢がありますが、 Cloud Native Computing Foundation (CNCF) Graduated Projects に認定されるなど、OSS としての成熟度が高く、他社の導入事例やインターネット上で参考にできる情報が豊富であったことから、 Istio を中心にマイクロサービスのインフラ環境を構築することにしました。

Amazon EKS

次に、Istio をホストする実行基盤を選定する必要がありました。 従来のシステムが Amazon EC2 で稼働していたため、新しいシステムも AWS 上に構築することで段階的な移行がスムーズに進められると考えました。 AWS のコンテナ実行基盤としては Amazon ECS Kubernetes on Amazon EC2 なども候補に挙がりましたが、コンテナオーケストレーションツールのデファクトスタンダードとなっている Kubernetes の知見を長期的な視点で蓄積していくことを重視し、AWS のマネージドな Kubernetes 基盤サービスである Amazon EKS を選択しました。

Helm

Kubernetes では、Istio をはじめとする OSS や各種アプリケーションなど、様々なコンポーネントを YAML 形式のマニフェストで管理します。 この Kubernetes マニフェストの管理を効率化するため、私たちは Helm を採用しました。 Helm は Kubernetes マニフェストのパッケージマネージャーであり、アプリケーションの構成をチャートというパッケージの単位でまとめて再利用しやすくしてくれます。 インターネット上に公開されている OSS 公式の Helm チャートを利用できる点や、独自に Helm チャートを作成する際も高度なテンプレートエンジンを利用できる点が導入のメリットとして挙げられます。

Argo CD

続いて、Helm チャートを Amazon EKS クラスターにデプロイする方法を検討しました。 Helm CLI を使用してコマンドで直接デプロイする方法や Helmfile を使用する方法が候補となりましたが、以下の理由から最終的に Argo CD を利用することにしました。

  • デプロイ状況の可視性
    • 開発チームがデプロイ状況を直感的に把握できる、分かりやすい Web UI を備えていたこと
  • 情報収集のしやすさ
    • インターネット上に多くの情報があり、問題解決のヒントを得やすかったこと
  • OSS プロジェクトとしての成熟度
    • CNCF の Graduated Projects に認定されていたこと
    • Argo Workflows Argo Rollouts など、プロジェクト内の他のツールとの親和性が高く、拡張性に優れていたこと
  • GitOps との親和性
    • Git を「信頼できる唯一の情報源」(Single Source of Truth) とし、その状態をクラスターに自動反映させる GitOps を実現できる CD ツールであったこと

設計思想:チーム間の責任分界点をどう定めるか

今回のリアーキテクチャでは、まずチームの役割分担を明確に定義しました。 開発チームはアプリケーションの設計と開発、デプロイを担当し、インフラチームはそれ以外のインフラリソースの設計と設定、デプロイ、運用を担当します。

例えば、開発チームが管理するアプリケーションのソースコードやインフラチームが管理する Terraform のコードは、Git リポジトリを分けることで簡単に分離境界を定義できます。 一方で、Kubernetes マニフェストは両者がデプロイの際に変更を加える可能性があるため、どこまでがどちらのチームの管轄なのか明示的に分離境界を定めておく必要があります。

ALT

Kubernetes マニフェスト管理の分離境界

Kubernetes 運用における GitOps

Kubernetes では、マニフェストでシステムの理想の状態 (Desired State) を定義し、実際の状態 (Actual State) との差分があった場合は Kubernetes の Controller が理想の状態となるように変更を繰り返します。 このループ処理は Reconciliation Loop と呼ばれ、Kubernetes のコアコンセプトとなっています。

上記の課題を解決するため、私たちは GitOps の思想に基づいてデプロイフローを設計することにしました。 GitOps とは、アプリケーションのソースコードと Infrastructure as Code (IaC) 化したインフラリソースのコードを全て Git リポジトリで管理し、Git を「信頼できる唯一の情報源」(Single Source of Truth) として利用する Continuous Delivery (CD) の手法です。 具体的には、CD ツールが Git リポジトリの変更を検知し、その差分を自動的に実環境へ反映します。 Kubernetes 運用における GitOps のアプローチには、以下のようなメリットがあります。

  • 生産性の向上:Kubernetes マニフェストについて Git の機能を利用して差分管理やデプロイを実施できる
    • デプロイの運用負荷が軽減され、開発チームがアプリケーション開発に集中できる
  • 安定性の向上:Kubernetes マニフェストの変更が全て Git コミットとして残るため、トラブルシューティングがしやすくなる
    • 変更履歴が Git で追跡可能となるため、デバッグやロールバック、監査等の観点で有用
  • 信頼性の向上:コマンド実行など手作業によるオペレーションミスがなくなる
    • オペレーションの自動化により、Git の操作だけでデプロイが可能となる

push 型と pull 型のデプロイ

GitOps には、デプロイの方式によって push 型と pull 型の 2 種類が存在します。 push 型は GitHub Actions などのツールがビルドからデプロイまでを一貫して実行するのに対し、pull 型は Argo CD のような CD ツールが Git リポジトリの変更を検知し、自ら設定を同期します。 つまり、アプリケーションのビルドやテストを自動化する Continuous Integration (CI) と、実際に変更を反映する CD のワークフローを分離していることが pull 型の特徴です。

一般的に、push 型の GitOps には以下のようなデメリットがあります。

  • CI/CD を実施するツールが同じで権限が強くなりすぎる
  • CI/CD の各ワークフローの管理をチームごとに分離することが難しい
  • Git の状態と実環境の状態が乖離するリスクがある

これらの理由から、特に Kubernetes 運用においては pull 型の GitOps が主流となっており、私たちもこの方式を採用することにしました。 pull 型の GitOps を Argo CD で実現するにあたり、私たちは両チームの視点から以下のような追加の要件を定義しました。

インフラチーム視点の要件

  • デプロイに関連するオペレーションについて、開発チームとインフラチームの権限を適切に分離する
    • 不要な権限が付いていることによる事故が起きない
    • 両チームがそれぞれ実施するオペレーション手順がシンプル
    • CI(開発チーム管理)と CD(インフラチーム管理)が完全に分離されている
  • 日常的に行われるアプリケーションのデプロイにおいてインフラチームの工数がかからない

開発チーム視点の要件

  • CI または手動でイメージを push すればデプロイをトリガーできる
    • 本番環境では、手動の承認後にデプロイを実施する
  • 環境ごとにイメージ push の権限とデプロイされるイメージを分ける
    • AWS アカウントごとに Amazon ECR のリポジトリを設置する
    • 複数の Amazon EKS クラスターが設置された開発環境では、特定のプレフィックスをイメージタグに付与して、どのクラスターをデプロイ対象とするか制御できる
  • デプロイ時にデータストアのマイグレーション処理を実行する

新たに構築したデプロイフロー

定義した要件を満たすように、Argo CD を使用した pull 型の GitOps を実現するデプロイの仕組みを設計・構築しました。

ALT

新たに構築したデプロイフロー(本番環境)

初期構築を除き、赤色の番号は開発チーム、紫色はインフラチームが管理・運用を担当します。 各ステップにおける処理内容は以下の通りです。

各ステップの処理
開発チームが管理する CI もしくは手動のコマンド実行により、Amazon ECR にビルドしたイメージを push します。
Argo CD Image Updater が Amazon ECR 上に新しいイメージが push されたことを自動的に検知します。
Argo CD Image Updater が自動的にデプロイ対象となるイメージの情報が記述された Helm の Values ファイル を変更し、それを管理する GitHub リポジトリにブランチを作成します。
GitHub Actions がブランチの作成を検知し、自動的に Pull Request を作成します。
作成された Pull Request を開発チームがレビューし、変更をマージします。
GitHub 上に push された Values ファイルの変更を Argo CD が検知します。
Argo CD が新しいイメージタグを含む Kubernetes マニフェストを生成して自動的に反映します。その後、Kubernetes の仕組み (Reconciliation Loop) によって、 Pod などのリソースが理想の状態 (Desired State) を満たすように更新されます。

このデプロイフローの要となるのが、ステップ ②〜③ を担う Argo CD Image Updater です。 次に、その具体的な設定方法についてコードを交えて詳しく見ていきます。

イメージ push の検知とデプロイ対象の更新

Argo CD Image Updater は、コンテナレジストリを監視し、新しいイメージが push されたことを検知してデプロイに必要な情報を自動的に更新するツールです。 公式で提供されている argocd-image-updater チャート のインストールだけで導入できます。

現在、デプロイ情報の更新方法として、以下の 2 種類をサポートしています。

  • argocd write-back method
    • Argo CD の Application リソース(1 つの Helm チャートに相当)を直接変更し、イメージ情報を Parameters として上書き します。
    • この変更は Git で管理されないため、GitOps の原則からは外れます。
  • git write-back method
    • 更新対象のイメージ情報を含む Git コミットを指定した Git リポジトリに push します。

今回のデプロイフローでは、イメージ情報も Git で管理する方針のため、git write-back method を選択しました。 この方法では、Argo CD Image Updater が新しいイメージの push を検知し、指定された Git リポジトリに argocd-image-updater-<SHA256> という名前のブランチを作成して、イメージ情報を含む Values ファイルの変更を Git コミットとして push します。 これにより、問題が発生した場合も、そのコミットを revert するだけで容易にロールバックできます。

<SHA256> の部分は更新内容から自動計算されるハッシュ値に置き換えられ、ブランチ名の一意性が保証されます。

例えば、Broadcast Engine というアプリケーションコンポーネントに対してこの設定を行う場合、Argo CD の Application リソースに以下のような Annotations を付与します。 具体的には、「broadcast-engine というイメージが保存されている Amazon ECR リポジトリを監視し、最新バージョンが見つかったとき、指定した Git リポジトリの values.yaml を更新する」という振る舞いを定義しています。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: |
      broadcastEngine=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/broadcast-engine
    argocd-image-updater.argoproj.io/broadcastEngine.update-strategy: newest-build
    argocd-image-updater.argoproj.io/broadcastEngine.force-update: "true"
    argocd-image-updater.argoproj.io/broadcastEngine.helm.image-name: broadcastEngine.repository
    argocd-image-updater.argoproj.io/broadcastEngine.helm.image-tag: broadcastEngine.tag
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/write-back-target: helmvalues:/values.yaml
    argocd-image-updater.argoproj.io/git-repository: ${GITHUB_REPOSITORY_URL}
    argocd-image-updater.argoproj.io/git-branch: main:argocd-image-updater-{{.SHA256}}
# ...

ただし、${AWS_ACCOUNT_ID}${AWS_REGION}${GITHUB_REPOSITORY_URL} の部分は実際の値に置き換えて設定します。 これにより、${GITHUB_REPOSITORY_URL} で指定した GitHub リポジトリ直下に設置された values.yaml に対して、イメージ情報の変更が Git コミットとして push されます。

broadcastEngine:
  repository: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/broadcast-engine
  tag: 324b11b87de6ab21f50172da284abb3f0e664f03

続いて、GitHub Actions のワークフローにより argocd-image-updater-<SHA256> という名前のブランチが新たに push されたことをトリガーにして、main ブランチにマージするための Pull Request 作成と Slack 通知を自動的に行います。 これを実現する GitHub Actions のワークフロー定義は以下の通りです。

name: create-pull-request

on:
  push:
    branches:
      - 'argocd-image-updater-*'

jobs:
  create-pull-request:
    steps:
      - name: Create Pull Request
        uses: actions/github-script@v6
        env:
          TZ: Asia/Tokyo
        with:
          script: |
            const { repo, owner } = context.repo;
            const result = await github.rest.pulls.create({
              title: '[Argo CD Image Updater] アプリケーションのデプロイ',
              owner,
              repo,
              head: '${{ github.ref_name }}',
              base: 'main',
              body: 'ECR に push されたイメージを本番環境にデプロイします。'
            });
            github.rest.issues.addLabels({
              owner,
              repo,
              issue_number: result.data.number,
              labels: ['automated']
            });
            core.exportVariable('PULL_REQUEST_HTML_URL', result.data.html_url);
            core.exportVariable('PULL_REQUEST_TITLE', result.data.title);
            core.exportVariable('PULL_REQUEST_NUMBER', result.data.number);
            core.exportVariable('PULL_REQUEST_BODY', result.data.body);
            core.exportVariable('PULL_REQUEST_CREATED_AT', result.data.created_at);
      - name: Notify Pull Request to Slack
        uses: slackapi/slack-github-action@v1.26.0
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
        with:
          payload: |
            {
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": ":github_opened: *<${{ env.PULL_REQUEST_HTML_URL }}|${{ env.PULL_REQUEST_TITLE }}>* #${{ env.PULL_REQUEST_NUMBER }}"
                  }
                },
                {
                  "type": "rich_text",
                  "elements": [
                    {
                      "type": "rich_text_quote",
                      "elements": [
                        {
                          "type": "text",
                          "text": "${{ env.PULL_REQUEST_BODY }}"
                        }
                      ]
                    }
                  ]
                },
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Created at:* ${{ env.PULL_REQUEST_CREATED_AT }}"
                  }
                }
              ]
            }

この Slack 通知を受けて、開発チームが Pull Request のレビューと main ブランチへのマージを実施します。

また、事前に上記と同じ Application リソースにおいて、以下のように Multiple Sources を設定しておきます。 この設定により、Argo CD が main ブランチにマージされた values.yaml の変更を検知し、差分を自動的に反映します。

apiVersion: argoproj.io/v1alpha1
kind: Application
# ...
spec:
  sources:
    # アプリケーションコンポーネントを設置するための Helm チャート
    - helm:
        valueFiles:
          - $releaseValues/values.yaml
      # ...

    # イメージ情報を含む Values ファイル
    - repoURL: ${GITHUB_REPOSITORY_URL}
      targetRevision: main
      ref: releaseValues
# ...

権限分離と役割分担

pull 型 GitOps を採用することで、定常的なデプロイ運用に必要な権限を最小化しつつ、変更履歴は全て Git 上に記録されるようになりました。 主要な対象ごとの権限の範囲は次の通りです。

対象 開発チームの権限 インフラチームの権限
アプリケーションリポジトリ 変更・CI ワークフロー実行 閲覧
デプロイ用 Values リポジトリ
(イメージ情報)
Pull Request レビュー・マージ リポジトリ設定・Argo CD 連携設定
IaC リポジトリ(Terraform / Helm / Argo CD 等) 閲覧 変更・CI ワークフロー実行
Amazon ECR イメージ push 管理
Amazon EKS 直接 apply 権限なし(必要に応じて閲覧) 管理
Argo CD Application の状態・差分確認 管理
Argo CD Image Updater なし 管理

上記の分担により、最小権限での運用と、Git コミットおよび Pull Request による完全な監査可能性を両立できています。

おわりに

本記事では、プロダクトの安定稼働とパフォーマンスの向上、開発体験の向上を目指して行っている Pococha のリアーキテクチャの一環で構築した、Amazon EKS と Argo CD による GitOps に基づくデプロイフローの事例をご紹介しました。

従来の EC2 環境で問題となっていた、ロールバックに時間がかかることやデプロイのロック競合といった課題を解決するため、私たちは pull 型の GitOps をデプロイ戦略の核として採用しました。 これにより、開発チームとインフラチームの Kubernetes マニフェスト管理の分離境界を明確にしつつ、より安全で効率的なデプロイの仕組みを構築することができました。

Kubernetes 環境におけるデプロイフローの設計や、チーム間の役割分担に課題を抱えている方々にとって、本記事の事例が一つの参考になれば幸いです。

This article is not affiliated with or otherwise sponsored by CNCF.


  1. マイクロサービス間の通信を管理・制御するための専用のインフラレイヤーをサービスメッシュと呼びます。各サービスにサイドカーと呼ばれる軽量なプロキシを配置し、サービス間の通信をすべてこのプロキシ経由にすることで、通信の暗号化やリトライ、アクセス制御、詳細な監視(可観測性)といった機能を、アプリケーションのコードを変更することなく実現できます。 ↩︎

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。