Amazon EKSでのArgoCDを使ったGitOps CD

こんにちは。スタディサプリ ENGLISH SREグループの木村です。

つい先日、スタディサプリENGLISHの基盤をECSからEKSへの移行をしました。移行の経緯や理由などは先日公開された大島のスタディサプリENGLISHの基盤をECSからEKSに移行しました という記事で紹介しております。
今回は私たちがKubernetesのCDに利用したArgo CDをなぜ選んだのか?どのよう導入したのかという部分を説明していきたいと思います。

Argo CDについて

Argo CDはGitOps1)Gitをアプリケーションとインフラのsingle source of truthとして扱い、Gitにある情報(ここではKubernetesのmanifest)をあるべき姿とし、同期を行いアプリケーションの変更を行う手法。GitOpsを提唱しているweaveworksGitOpsにページに詳細が書いてありますを行うためのツールの一つです。
Argo ProjectというOpen sourceでKubernetes nativeなworkflows,events,CI,CDを扱うために作られた中の1プロジェクトです。元々はApplatixというCloudでののplatform構築のコンサルタントをしていた会社が中心となって作成していましたが、2018年にIntuitがApplatixを買収し、現在はIntuitのエンジニアが中心となって開発しており、2020年4月にCNCFのincubator projectにArgoが入っています。2)TOC welcomes Argo into the CNCF incubator

Why? Argo CD

早い段階でGitOpsを利用したいということは決めていたのですが、GitOpsを導入できるツールには複数のものがあります。有名なものではweaveworks社が作っているFlux3)https://github.com/fluxcd/flux だったり、Kubernetesのprojectでも使われているJenkinsX4)https://github.com/jenkins-x/jxなどがあります。
なぜ私たちがArgo CDを採用したかという点を書いていきたいと思います。

GUI

Argo CDにはdeployを行ったApplicationをGUIで確認することができるGUI機能があります。

現時点ではSREが基本的にArgo CDやKubernetesのmanifestを管理していますが、将来的にはDeveloper自身で管理・運用できるようにしていきたいという気持ちがあり、最初の導入障壁を考えると、どのようにresourceが反映されているか?ということを把握できるGUIは大きな利点だと考えました。

Sync Phase/Sync Wave

DB migarionとapiの依存関係

私達の環境ではDB Migrationにplay frameworkのevolutionsを利用して自動化しています。その関係上で実際にapiをdeployする前にevolutionを実行するタスクを実行する必要があります。
また私たちはアーキテクチャにMicroservicesを採用し、内部apiのcontext間の通信にgRPCを採用しており、deploy時のapi依存によるrequestが落ちる問題を最小限にするため、後方互換が維持されているgRPCのサーバーから先に反映したいという要件もありました。
Argo CDにはSync PhaseとSync Waveという2つの概念があり、Sync PhaseにはPreSync,Sync,PostSyncの3つのPhaseがあり、Sync Waveで各Sync Phaseの間のSync5)Argo CDの用語でmanifestを実際にapplyすることの順序を制御できます。
私たちの場合はPreSyncでConfigMapの作成→DB Migarion、SyncでInternal API(gRPC)→ExternalAPI(REST)を行うようにしています。

Sync順序のイメージ図

manifest管理・運用について

Argoのblogでは5 GitOps Best PracticesというArgo CDを運用するするためのベストプラクティスをまとめた記事があり、その中で「Two Repos: One For App Source Code, Another For Manifests」というApplicationのソースとmanifestのレポジトリを分けて管理するべきというのがあります。
私たちの環境では早い段階からIaCを徹底しており、自然にこのプラクティスに則った形でインフラとサーバーのデプロイの設定ファイルは専用のレポジトリを作成して運用しており、自然と管理しやすい形になっていました。manifestの環境差分の管理にはkustomizeを採用しています。

manifest ディレクトリ

現状ではapiやjob,argoなどの大きな枠で依存しないと思われるディレクトリを三階層で分けその中に各manifestを分けています。(※testディレクトリはmanifestのテストに関するスクリプトをまとめている)
数が多く全ては紹介しきれないのですが、api,argo,clusterの一部のディレクトリを紹介します。

api
appmesh
argo
cluster
gauruns
ingress
jobs
proxy
test
web

api

各api毎にserviceargocd_applicationの2つのディレクトリを作り、その中で各環境用のoverlaysを作成しています。

argocd_application以下にはApplication6)Applicationのdeployするresourceを管理するArgo CDのCRD。詳しくはCore Conceptに書いてあるのyamlを配置
service側にはDeploymentやevolutionのJobのmanifest,appmeshなどのapplicationが動作するために必要なmanifestを配置
運用としては初回のみargocd applicationをapplyして利用することを想定しています。

  • apiディレクトリ構造
├── hoge-api
│   ├── argocd_application
    │   ├── base
    │   │   ├── application.yaml
    │   │   │   └── kustomization.yaml
    │   └── overlays
    │       ├── dev1
    │       │   ├── application.yaml
    │       │   └── kustomization.yaml
    │       │   ├── application.yaml
    │       │   └── kustomization.yaml
--------------中略-----------------
    │       └── staging
    │           ├── application.yaml
    │           └── kustomization.yaml
    └── service
        ├── base
        │   ├── appmesh.yaml
        │   ├── deployment.yaml
        │   ├── evolution.yaml
        │   ├── kustomization.yaml
        │   ├── patches
        │   │   └── generate-name.yaml
        │   └── service.yaml
        └── overlays
            ├── dev1
            │   ├── appmesh.yaml
            │   ├── deployment.yaml
            │   └── kustomization.yaml
--------------中略-----------------
            ├── production
            │   ├── appmesh.yaml
            │   ├── deployment.yaml
            │   ├── evolution.yaml
            │   └── kustomization.yaml
            └── staging
                ├── appmesh.yaml
                ├── deployment.yaml
                └── kustomization.yaml
---fuge-api

argocd

Argo CDに関するresourceをまとめています。
このディレクトリにはArgo CDの関連のresourseを管理しています。
Argo CD本体の管理には以下の工夫をしています。

  • Argo CDをArgoCD applicationで管理する
  • remote build
    • kustomizeの機能でgithub上にあるmanifestを扱うことができる機能。私たちの環境では自分たちが変更したい部分を patchesStrategicMerge で上書き、主要な部分はremote buildから取得することでArgo CD本体のupdateの手間を省いています。
  • 実際のkustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
namespace: argocd
kind: Kustomization
patchesStrategicMerge:
- argocd-rbac-cm.yaml
- argocd-cm.yaml
- argocd-application-controller-deployment.yaml
- argocd-repo-server-deployment.yaml
- argocd-server-deployment.yaml
resources:
- github.com/argoproj/argo-cd/manifests/ha/cluster-install?ref=v1.7.4
- namespace.yaml

argo-notificationsはargo-cd-labs orgで開発されている、Argo CDの通知ツールです。

argocd-app-projectsにはArgoCDのCRDのApp Projectのmanifestを別途配置しています。argocdとは別のディレクトリに配置してある理由は同時にapplyした場合、argocdのCRDが反映されるまえにApp Projectがapplyされるとerrorが発生する場合があるので別のタイミングでapplyできるように別のディレクトリに配置してあります。

  • argocdディレクトリ
├── argo-notifications
│   ├── argocd_application
│   │   ├── base
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays
│   │   ├── development
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   ├── production
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   └── staging
│   │   ├── application.yaml
│   │   └── kustomization.yaml
│   └── manifests
│   └── overlays
│   ├── development
│   │   ├── argo-notifications.yaml
│   │   └── kustomization.yaml
│   ├── production
│   │   ├── argo-notifications.yaml
│   │   └── kustomization.yaml
│   └── staging
│   ├── argo-notifications.yaml
│   └── kustomization.yaml
├── argocd
│   ├── README.md
│   ├── argocd_application
│   │   ├── base
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays
│   │   ├── development
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   ├── production
│   │   │   ├── application.yaml
│   │   │   └── kustomization.yaml
│   │   └── staging
│   │   ├── application.yaml
│   │   └── kustomization.yaml
│   └── manifests
│   └── overlays
│   ├── development
│   │   ├── argocd-application-controller-deployment.yaml
│   │   ├── argocd-cm.yaml
│   │   ├── argocd-rbac-cm.yaml
│   │   ├── argocd-repo-server-deployment.yaml
│   │   ├── argocd-server-deployment.yaml
│   │   ├── kustomization.yaml
│   │   └── namespace.yaml
│   ├── production
│   │   ├── argocd-application-controller-deployment.yaml
│   │   ├── argocd-cm.yaml
│   │   ├── argocd-rbac-cm.yaml
│   │   ├── argocd-server-deployment.yaml
│   │   ├── kustomization.yaml
│   │   └── namespace.yaml
│   └── staging
│   ├── argocd-application-controller-deployment.yaml
│   ├── argocd-cm.yaml
│   ├── argocd-rbac-cm.yaml
│   ├── argocd-server-deployment.yaml
│   ├── kustomization.yaml
│   └── namespace.yaml
└── argocd-app-projects
 ├── argocd_application
 │   ├── base
 │   │   ├── application.yaml
 │   │   └── kustomization.yaml
 │   └── overlays
 │   ├── development
 │   │   ├── application.yaml
 │   │   └── kustomization.yaml
 │   ├── production
 │   │   ├── application.yaml
 │   │   └── kustomization.yaml
 │   └── staging
 │   ├── application.yaml
 │   └── kustomization.yaml
 └── manifests
 └── overlays
 ├── development
 │   ├── argocd-app-projects.yaml
 │   └── kustomization.yaml
 ├── production
 │   ├── argocd-app-projects.yaml
 │   └── kustomization.yaml
 └── staging
 ├── argocd-app-projects.yaml
 └── kustomization.yaml

cluster

共通で必要なingress controllerやdatadog agent、fluent-bitなどのmanifestを管理、一部のものはArgo CDのHelm Chart Repositoriesの機能
7)https://argoproj.github.io/argo-cd/operator-manual/declarative-setup/#helm-chart-repositories">Helm Chart Repositoriesで入れるようにしており、clusterの構築とnodeの管理に関しては、TerraformとSpotを利用しています。8) clusterの構築に関しての詳細はEKSクラスタの効率的なリソース管理にSpotinst Oceanを使おう に詳細があります
cluster構築の後に必要なresourceは全てArgo CDで管理しているので、argocd applicationをclusterにapplyさせることで、必要な設定のほとんどは反映出来るような形です。

  • clusterディレクトリ
├── README.md
├── alb-ingress-controller
│   ├── README.md
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── rbac-role.yaml
├── argocd_application
│   ├── base
│   │   ├── application.yaml
│   │   ├── helm-applications.yaml
│   │   └── kustomization.yaml
│   └── overlays
│       ├── development
│       │   ├── application.yaml
│       │   ├── helm-applications.yaml
│       │   └── kustomization.yaml
│       ├── production
│       │   ├── application.yaml
│       │   ├── helm-applications.yaml
│       │   └── kustomization.yaml
│       └── staging
│           ├── application.yaml
│           ├── helm-applications.yaml
│           └── kustomization.yaml
├── nginx-ingress-controller-ingress
│   ├── README.md
│   ├── ingress.yaml
│   └── kustomization.yaml
└──overlays
    ├── development
    ├── production
    └── staging

Manifestに対するCI

CIテストに関してはCircleCI上で、3種類のテストを行っています。
1. kustomize buildを行った結果に対して、kubevalをかけるテスト
2. kustomizebuildを行った結果に対して、自前でGolangで書いたmanifestに想定した値や設定されているかのテスト
3. kindを使って、実際のclusterを作成できるかのテスト

1.のtestに関しては、私たちのrepositoryにはArgo CDとAppMeshのCRDが入っているので、--ignore-missing-schemas オプションをつけることでCRDの場合のチェックを外すようにしています。またindentずれなどで設定したつもりの値が設定されてないいないなどが起きたため、そのresourceに存在しないkeyが設定した場合失敗としてくれる --strictのオプションをつけています。

2.に関してはCPU、Resourceが設定されているか、想定のパターンのenvが設定されているか、ReadinessProbeが設定いるかなどのkubevalでは検知出来ない部分のテストを実行しています。
当初はconftestの導入も考慮したのですが、実際に書いてみるとregoの記述が複雑になってしまう可能性とチームの中でGolangを書くことができる人が多かった、最初は小さくテストを初めていきたいなどの点から現時点ではGolangを利用し、自分たちでテストを書くという形にしています。

3.に関しては、kind を使って、実際にCI環境上にclusterを構築してテストをしています。全体のmanifestをテストするのはCI環境上では現状では大変なため、現在はArgo CDが立ち上がるかのテストのみを実行しています。
将来的にはcluster update時のテストのため、AWSなどのpublic cloud環境などで実際にclusterを立てるテストなども考えて行きたいと思っています。

Special thanks

自前でテストを書く部分に関しては、cybouzuさんのneco projectがすごく参考になりました。
非常に感謝しています!!!!!!
neco projectのtest部分

デプロイフロー

PRが作成されてmergeされたら、ArgoCDが差分を検知し、clusterへの反映がされるような形です。
私たちの実際のflowとしては、アプリケーションのbuildとPRの作成にはJenkinsを利用しており、Developerには環境deployしたいcomponentを選択してもらい、Jenkinsでbuild,PR作成をするという形になっています。
現状はPR作成時に手作業が発生してしまう点が懸念点なので、master merge時に自動でPR作成をする方法も今後は考慮していきたいなと考えています。

cluster毎の差分

clusterとしてはdevelopment,staging,productionの3clusterを管理しています。
stagingとproductionは本番同等環境として運用しているので、各clusterにapi用のnamespaceを一つずつ、PRをmasterにmergeすることで運用、development環境に関しては、1 namespaceが1環境というような形で、複数の環境を運用する形にしています。

development環境については、現在はdeploy用のbranchを作成し、前述のJenkinsから直接branchにpushをする形で運用しています。
Gitをsingle source of truthという形で運用しているので仕方ない部分はあるのですが、 開発環境のような頻繁にdeployが発生する可能性がある箇所ではGit非依存でのdeployする機能ができて欲しい気持ちがあります。他のArgo CDを運用している人がこの点をどう運用しているかは是非知りたいと思っています。

まとめ

今回は私たちが行ったArgo CDを使ったデプロイフローに関して紹介をさせていただきました。
GitOpsの重要な利点として、環境の正しい状態をGitのyamlを読むことで把握できるようになりました。
また、Argo CDはcustom controllerを利用して、GitOpsを実現しています。開始するにはArgo CDを入れるだけで、Argo CDを止めたい場合はArgo CDのresourceを消してしまえば、やめることができるという利点もあります。
まだまだmanifestの管理の方法やテスト方法など、課題やベストプラクティスを模索している状態ではあるのですが、今回の記事がGitOpsやArgo CDの導入を検討している人たちの助けになると嬉しいです。

とりあえず、ECSからKubernetesへの移行を無事行うことが出来たので、Progressive DeliveryやObservabilityの強化など、Kubernetesになったことでより柔軟に対応しやすくなった課題にも取り組んで行き、より堅牢でAggressiveなサービスにしたいと思っています!

Tips

  • Argo CDの公式利用リスト
    • Argo CDには公式に利用しているユーザー一覧がGitHubにあります。国内だとCyber Agent,PayPay,Cybozeなど複数のユーザーが使っています。今回本番導入をしたことを気に公式利用ユーザー追加へのPRを投げました!
  • 現状のArgocd application数
    • 私たちのclusterで一番環境が多いdevelopment用のclusterでは387個のargocd applicationが動いていますが、現状Syncでの大きな問題は起きていません。
    • ❯ k get applications.argoproj.io -n argocd --output json | jq -j '.items | length'
      387
    • ただ一度application controllerが2台立ててしまい、うまく動かくなってしまったということがあります。
  • Monitoringについて

脚注

脚注
1 Gitをアプリケーションとインフラのsingle source of truthとして扱い、Gitにある情報(ここではKubernetesのmanifest)をあるべき姿とし、同期を行いアプリケーションの変更を行う手法。GitOpsを提唱しているweaveworksGitOpsにページに詳細が書いてあります
2 TOC welcomes Argo into the CNCF incubator
3 https://github.com/fluxcd/flux
4 https://github.com/jenkins-x/jx
5 Argo CDの用語でmanifestを実際にapplyすること
6 Applicationのdeployするresourceを管理するArgo CDのCRD。詳しくはCore Conceptに書いてある
7 https://argoproj.github.io/argo-cd/operator-manual/declarative-setup/#helm-chart-repositories">Helm Chart Repositories
8 clusterの構築に関しての詳細はEKSクラスタの効率的なリソース管理にSpotinst Oceanを使おう に詳細があります