こんにちは、技術戦略部 SREグループのカンタンです!
Kubernetesクラスタを運用するとマニフェストの適用方法が気になる方が多いかと思います。 kubectlを使ってYAMLファイルをそのまま適用したり、Kustomizeファイルを適用したり、Helmチャートを適用したり、Argo CD や Flux のようなCDツールを利用したりするなどやり方が色々あります。
約一年前からSREグループで管理しているマニフェストの適用方法をArgo CDに切り替えました。適用スピードが加速し、クラスタの状況が把握しやすくなり、セキュリティレベルが向上し、運用の効率が圧倒的に上がりました。とても良くできているツールのため、Argo CDの魅力とSREグループの使い方を紹介させていただいてご参考になれば幸いです!
SREグループが管理しているEKSとGKEクラスタのマニフェストを一つのmonorepoで一元管理しています。Kubernetesマニフェストをmonorepoで管理するのが主流と言えますが、SREグループの管理方法に主に3つの特徴があります:
そのため、以下のようなフォルダ構造を採用しています。
.
├── namespaces
│ ├── namespace-a
│ │ ├── Chart.yaml # 共通チャートとコミュニティチャートを依存関係に設定
│ │ ├── common-values-a.yaml # クラスタ共通の設定値
│ │ └── common-values-a.secret.yaml # クラスタ共通の秘密設定値
│ └── namespace-b
│ ├── common-values-b.yaml
│ └── common-values-b.secret.yaml
└── clusters
├── dev-cluster
│ ├── namespace-a
│ │ ├── dev-values-a.yaml # devクラスタ専用の設定値
│ │ └── dev-values-a.secret.yaml # devクラスタ専用の秘密設定値
│ └── namespace-b
│ │ ├── dev-values-b.yaml
│ │ └── dev-values-b.secret.yaml
└── prod-cluster
├── namespace-a
│ ├── prod-values-a.yaml # prodクラスタ専用の設定値
│ └── prod-values-a.secret.yaml # prodクラスタ専用の秘密設定値
└── namespace-b
├── prod-values-b.yaml
└── prod-values-b.secret.yaml
Argo CDに移行する前、特定のクラスタとネームスペースのマニフェストを適用するには helm をラップした独自のCLIを以下のようにローカルから実行していました。
customcli --cluster dev-cluster --namespace namespace-a
裏では、CLIがクラスタ共通設定とクラスタ専用設定を統合したり、 helm dep update でHelmチャートの依存関係を更新したり、 helm diff で適用される設定の差分を出したりして最終的に helm upgrade で設定をクラスタに適用していました。
そのやり方に問題がいくつかありました:
MoTの成長に伴ってサービスの数も増えて運用が徐々に苦しくなっていたため、運用効率を上げるために約一年前にArgo CDを導入することになりました。上記の問題が解決され、期待していなかったメリットも得られて非常に満足しています。これからArgo CDについてとSREグループの使い方を紹介させていただきたいと思います。
Argo CDはKubernetesマニフェストをGitOpsで管理するためのオープンソースCDツールです。Gitリポジトリに管理されているマニフェストとKubernetesクラスタの状態を常に同期することで、Gitだけでクラスタの状態を確認、変更、リバートできます!
基本動作として、クラスタの状態を常に監視つつgitリポジトリを定期的に取得しています。差分が検知された場合、設定次第同期を自動的に実行してくれたり通知してくれたりします。また、gitのwebhookを設定することでマージした途端にArgo CDがgitリポジトリの最新情報を取得し同期を走らせてくれるためマニフェストを数秒で適用できます!
CI経由での適用、いわゆる「プッシュ型」から、適用を非同期で行う「プル型」になりますのでCIに強い権限を付与する必要がなくてセキュリティ観点的にも嬉しいです。様々なCIが影響を受けた2021年4月のCodecovセキュリティインシデント、Travis CIも影響を受けた2022年4月のGitHubセキュリティインシデント や 2023年1月のCircleCIセキュリティインシデントなど最近CI関連のセキュリティインシデントが増えていてKubernetes本番環境の権限をCIに付与することが難しくなっています。Argo CDのようなプル型の仕組みを使うことでCIに権限を付与する必要がなくなってセキュリティレベル向上に繋がります!
Flux、SpinnakerなどArgo CD以外にGitOps系のCDツールがいくつかありますが、以下の理由でArgo CDを選定しました:
Argo CDはKubernetesのコントローラーとして実装されています。一つのアプリケーションを表す App と複数アプリケーションをまとめるための単位 AppProject カスタムリソースが定義されています。 AppProject と App リソースをクラスタに適用することでArgo CDが参照すべきGitリポジトリと管理すべきKubernetesクラスタ、ネームスペース、リソースなどを指定できます。一つのArgo CDインスタンスで複数クラスタを管理できますが、管理のしやすさのためSREグループではクラスタごとにArgo CDをインストールし、そのクラスタだけを管理するようにしています。
AppProjectのサンプル
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: my-project
namespace: argocd
spec:
clusterResourceWhitelist: # プロジェクト内のアプリケーションが管理できるKubernetesリソース
- group: '*'
kind: '*'
description: My Project
destinations: # プロジェクト内のアプリケーションが管理できるクラスタとネームスペース
- namespace: 'my-namespace'
server: https://kubernetes.default.svc # ローカルクラスタ
sourceRepos:
- https://github.com/MyOrg/my-repo
Appのサンプル
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
destination:
namespace: my-namespace
server: https://kubernetes.default.svc
project: my-project # 上に定義されているプロジェクト
source:
repoURL: https://github.com/MyOrg/my-repo # 利用すべきgitリポジトリ
path: manifests/my-app # マニフェストの保存パス
targetRevision: HEAD # gitリポジトリのどのバージョンを同期すべきか
Argo CDがマイクロサービスアーキテクチャを採用していて以下のサービスが動いています:
他に関連プロジェクトがたくさんありますので様々なユースケースに対応できます!
SREグループではEKSとGKEクラスタをいくつか運用していて、背景で説明した通りにマニフェストリポジトリで特殊なフォルダ構造を採用したり、Helmの共通チャートでマニフェストを生成したり、秘密情報の暗号化仕組みを利用したりしている関係でArgo CDの導入にあたって以下のようなアーキテクチャを採用しました。
適用タイミングと適用者を考慮し、マニフェストを以下の3種類に分けて考えました:
Argo CDではKubernetesリソースの制御を AppProject 単位で行うため、以下の設計に至りました。
そうすることで、クラスタの基本機能とセキュリティに影響するマニフェストの適用をSREまでとし、アプリケーションごとの設定の適用権限をエンジニアにも付与できます。
尚、事情があって現時点ではArgo CDで管理するマニフェストはあくまでも上記の cluster と namespaces AppProjectと該当するマニフェストのみにしていて、アプリケーションごとのマニフェストはまだ別の仕組みで管理しています。最終的に全てのマニフェストをArgo CD管理にしていきたいと思います。
以下のような適用フローを採用しました
Slack通知のサンプル:この例では monitoring ネームスペースが変更されました。通知からArgo CDのGUIとgitリビジョンに直接飛べます。
GUIを開くと、ネームスペースの状況がすぐわかります。今回は kube-state-metrics Deploymentが同期されていないことがわかります。
差分も簡単に確認できます。
「Sync」ボタンで同期を実行すると、設定が適用されネームスペースが同期されます。
PRを見ると、同期開始と同期成功イベントがコメントされているためエンジニアも同期状況を把握できます。
Argo CD導入前と違ってローカル環境の状態と関係なくなったのと適用スピードも加速したため適用体験がかなり良くなりました。上記のようなフローにすることで、レビューを必須にする流れと監査ログを残す形になったため体験以外にも嬉しいことがありました。
Argo CDはYAML、kustomize、helmなどマニフェストフォーマットをいくつかサポートしているため様々なユースケースに対応できています。更に、Argo CDのConfig Management Plugins機能を使えばカスタム処理を実行できます。最終的にYAMLマニフェストを生成さえすれば好きな処理を実行できるため柔軟性が高くて素晴らしい機能です。背景に話したようにSREグループでは特殊なフォルダ構造を採用したり秘密情報の暗号化仕組みを利用したりしているためカスタムプラグインを活かして対応しています。
カスタムプラグインを作るには2つのやり方があります:sidecarコンテナとして動かすかConfigMapに登録することでArgo CDコンテナ上で動かすか。ConfigMapのやり方が今後廃止されsidecarを利用しないといけなくなりますが、ConfigMapの方がわかりやすいのでその方法で説明させていただきます。
Argo CDのConfigMapに以下のような設定を追加することでカスタムスクリプトを用意できます。
data:
configManagementPlugins: |
- name: my-plugin
init: # 任意初期化処理
command: ["sample command"]
args: ["sample args"]
generate: # YAMLマニフェスト生成コマンド
command: ["sample command"]
args: ["sample args"]
その後、 App に以下の設定を追加することでプラグインを利用できます
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
...
source:
repoURL: https://github.com/MyOrg/my-repo
path: manifests/my-app
targetRevision: HEAD
plugin:
name: my-plugin # 「my-plugin」カスタムプラグインを利用
参考まで、SREグループで利用しているカスタムプラグインは以下のようなものです(擬似コード)。最後に helm template でYAMLマニフェストを生成しています。
configManagementPlugins: |-
- name: my-plugin
lockRepo: true
generate:
command: ["bash", "-c"]
args:
- |
set -e
# 前提
# - クラスタ共通設定とクラスタ専用設定を別々のフォルダに保存している
# - helmチャートキャッシュ用のフォルダを設けている
# - 秘密情報管理にsopsを利用している (https://lab.mo-t.com/blog/secrets-sopsに参考)
# 設定統合
prepare_build_folder # クラスタ共通設定とクラスタ専用設定を統合
# 依存関係取得
synchronize_cache # 依存チャートをキャッシュから取得
fetch_missing_dependencies # キャッシュにないチャートの取得 (helm dep updateベース)
synchronize_cache # 取得したチャートをキャッシュに入れる
# 秘密情報復号
decrypt_files # 暗号化ファイルを復号 (sopsを利用)
trap handle_exit EXIT # 異常終了時も正常終了時も復号したファイルを削除
# マニフェスト生成
values="-f common.yaml -f common.secret.yaml -f dev.yaml -f dev.secret.yaml"
helm -n $ARGOCD_APP_NAMESPACE template --release-name $ARGOCD_APP_NAME . $values
一言で言うと、Argo CDを導入してから運用の効率が圧倒的に向上しました! 🎉
ローカル環境の状態と関係なくなったため、適用までのリードタイムが大幅に減りました。適用フローがわかりやすくなり、監査ログが取れるようになり、エンジニアとのコミュニケーションコストも減りました。チャートのキャッシュ仕組みを入れたことで同期自体が数十秒から数秒までに減少しました!
また、クラスタやネームスペースを横断した変更をマージした際、影響を受ける全てのクラスタとネームスペースに対してSlack通知が来るようになったため漏れなく適用できるようになりました。GUIを開くことだけでクラスタの状況を一発で分かるようになり非常に便利です。
Argo CD導入前の課題を振り返ると、
狙い通りに様々な改善を達成できました!元々考慮していなかったとても便利な「ドリフト検知」機能も利用できるようになったためその機能を紹介したいと思います。また、Argo CDを1年ぐらい運用した感想と残課題を共有させていただければと思います。
Argo CDを導入してから1年弱経っていますが、一番気に入っている機能が元々考慮もしていなかった「ドリフト検知」機能になります。Argo CDがKubernetesクラスタの状態を常に監視してくれているため、gitリポジトリと差分があった際に即時で通知してくれています。そのため、以下のような想定外な変更に気づけて早めに対応できます:
Argo CDはとても安定していて、クラッシュしたことがないです。gitリポジトリにマージした際にCPUがスパイクしていますがそれ以外の時間帯に関してはArgo CDの全てのマイクロサービスを合わせてもCPUコアを1つも使っていなくて、メモリは2GB程度です。
PersistentVolumeを使っていなくて、Redisもあくまでもキャッシュとしてしか利用されていないのでArgo CDが実質ステートレスです。そのため、運用もかなり楽です。
SREグループでは3ヶ月に一回ぐらいのペースでIstioやArgo CDなどKubernetesクラスタにインストールしているコンポーネントをアップデートしています。Argo CDを何回かアップデートしたことがありますがArgo CDの開発がそれなりに進んでいて毎回Helmチャート設定の微調整が発生することがわかりました。欠点はそれぐらいです!
導入してから運用が楽になりましたが、課題がいくつか残っています。
Argo CDで管理しているマニフェストはSREグループが開発・運用しているアプリケーションとSREが管理すべきセキュリティに影響するマニフェストのみになっています(Secret、Ingressなど)。サービス自体のマニフェストをまだ移行していなくて別の仕組みになっています。最終的にArgo CDに完全に移行していきたいと思います。
設定すれば簡単に有効にできますが以下の懸念があります。
Argo CDを導入することでマニフェストをGitOpsで管理できるようになりました。適用フローが明確になり、適用のハードルが下がり、リードタイムが減少し、全体の効率が圧倒的に向上しました! 🎉
運用観点でも利用観点でもArgo CDがとても楽です。カスタムプラグイン機能のおかげで様々な利用方法が可能になり、セキュリティとコンプライアンスによる制約に柔軟に対応できます。試していない機能がまだたくさんありますので、今後Argo CDをもっと活かしていきたいと思います!
Kubernetesのマニフェスト管理に迷っている方へ、是非Argo CDを検討してみてください!この記事がご参考になれば幸いです!
興味のある方は 採用ページ も見ていただけると嬉しいです。
Twitter @goinc_techtalk のフォローもよろしくお願いします!