継続的デリバリ - ArgoCD

| 17 min read
Author: noboru-kudo

前回は、GitOpsの産みの親であるWeaveworks社が開発したFluxを導入して、継続的デリバリを実現しました。

今回はもう1つのGitOpsツールとして人気を集めるArgoCDを導入します。
Flux同様に、ArgoCDもGitOpsを体現したツールですが、最も大きな違いとして、ArgoCDはリッチなWeb UIを組み込みで持っています[1]
このUIにより、Gitとクラスタ環境の同期状況が視覚的に把握可能です。また、このUIにはリリース履歴、ログ参照や差分チェック等、多彩な機能が搭載されており、kubectlを理解していなくてもクラスタ運用できる点が、ArgoCDの大きなメリットでしょう。
弊社の社内システムでも、ArgoCDを利用して継続的デリバリ環境を構築しています。

最終的には、以下のような構成となります。

主要部分のみですが、ArgoCDの各コンポーネントは、継続的に以下を実施します。
① GitHubリソースよりマニフェスト生成(キャッシュ)。
② 差分検知とKubernetes環境への反映。
③ UI/CLI等へのAPI提供。

このようにして、ArgoCDでも定期的にGit(期待する状態)とクラスタ(現実の状態)を同期して、GitOpsを実現します。

Contents

事前準備#

前回Fluxで環境構築済みの場合は、そのまま利用できますので、新たに構築不要です。
未実施の場合は事前に、EKS/アプリケーションの実行環境を作成してください。

EKSクラスタ環境#

以下を参考に、EKSクラスタ環境を用意してください。

アプリケーション#

対象アプリケーションは、アプリケーション開発編で実施したタスク管理ツールを利用します。
アプリケーションのデプロイは不要ですが、前提となるDynamoDB等のAWS環境のセットアップや、Kustomizeのマニフェストファイルは事前に用意しておく必要があります。

なお、各アプリケーションのコンテナイメージは、1.0.0タグでECRにプッシュしておいてください。

ArgoCDのセットアップ#

ArgoCDインストール#

ArgoCDには、以下のように様々なインストール方法が用意されています。

今回は、Helmを使ってArgoCDをインストールします。
<your-custom-domain>の部分は、自分で取得したドメインに置き換えてください。

helm upgrade argocd argo/argo-cd \
--install --version 3.33.3 \
--namespace argocd --create-namespace \
--set server.extraArgs[0]="--insecure" \
--set config.url=argocd.<your-custom-domain> \
--wait

argocdNamespace内に、現時点で最新の3.33.3のHelmチャートをインストールしました。
インストールが正常に終了したら、ArgoCDのコンポーネントを確認しましょう。

kubectl get pod -n argocd
NAME                                            READY   STATUS    RESTARTS   AGE
argocd-application-controller-774475c7c-vjgnm   1/1     Running   0          3m57s
argocd-dex-server-6497f5d97-468ff               1/1     Running   0          3m57s
argocd-redis-694d566c49-jjw4h                   1/1     Running   0          3m57s
argocd-repo-server-848ffdcb7f-qt7z9             1/1     Running   0          3m57s
argocd-server-84d6b447b4-j8kfr                  1/1     Running   0          3m57s

ArgoCDの各種コンポーネントが実行中になっていればインストール完了です。

マニフェスト用のGitリポジトリ作成#

自分のGitHubアカウントまたは組織(Organization)に、事前にマニフェスト用のGitリポジトリを作成してください。
ここには、各種Kubernetesのマニフェストを配置します。
なお、Fluxチュートリアルで既に作成済みであれば、新たに作成は不要です。

ここではtask-tool-cluster-configというプライベートリポジトリを作成していることを前提とします。
別のリポジトリ名で作成した場合は、以降は該当箇所を置き換えてください。

作成したリポジトリは、ローカル環境にクローンしておきましょう。

git clone https://github.com/<github-user>/task-tool-cluster-config.git
cd task-tool-cluster-config
export CONFIG_ROOT=$(pwd)

以降は、このCONFIG_ROOT配下で作業することを前提としています。

Web UI向けのIngress作成#

前述の通り、ArgoCDにはリッチなUIが備わっています。
ですが、デフォルトでは、このUIにアクセスする手段は用意されていません。Ingressを配置し、ブラウザからアクセスできるようにしましょう。
既に事前準備でNGINX Ingress Controllerをセットアップ済みですので、これを使用してArgoCDにルーティングを追加します。
また、HTTPS経由でアクセスするため、こちらも事前準備でセットアップ済みのCert Managerを使用して、Let's Encryptの証明書も作成しましょう。

以下のマニフェストファイル(ここではargocd/argocd-ingress.yamlとしました)を用意してください。
なお、<your-email-address>/<your-custom-domain>は、自分のものに置き換えてください。

---
# TLS証明書のLet's Encrypt Issuer
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-issuer
namespace: argocd
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your-email-address>
privateKeySecretRef:
name: acme-client-letsencrypt
solvers:
- http01:
ingress:
class: nginx
---
# UI向けのHTTPSルール
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-http-ingress
namespace: argocd
annotations:
external-dns.alpha.kubernetes.io/hostname: argocd.<your-custom-domain>
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
cert-manager.io/issuer: letsencrypt-issuer
spec:
ingressClassName: nginx
tls:
- hosts:
- argocd.<your-custom-domain>
secretName: http-argocd-ingress-cert
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
name: http
host: argocd.<your-custom-domain>
---
# CLI向けのgRPCルール(任意)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-grpc-ingress
namespace: argocd
annotations:
external-dns.alpha.kubernetes.io/hostname: grpc-argocd.<your-custom-domain>
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
cert-manager.io/issuer: letsencrypt-issuer
spec:
ingressClassName: nginx
tls:
- hosts:
- grpc-argocd.<your-custom-domain>
secretName: grpc-argocd-ingress-cert
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
name: https
host: grpc-argocd.<your-custom-domain>

若干長いですが、内容は単純です。
まず1つ目で、TLS証明書の自動セットアップのために、Let's EncryptのIssuerリソースを用意しています。

その後の2つのIngressリソースが、UIからArgoCDへのルーティングを担います。
argocd-server-http-ingressでは、HTTPS経由でのリクエストをルーティングしています。これは主にUIからのリクエストハンドラーです。
もう1つのargocd-server-grpc-ingressは、ArgoCDのCLI向けです。ArgoCDのCLIではgRPCプロトコルでArgoCDとやりとりします。
ArgoCDのCLIについては今回は利用しませんので、こちはセットアップしなくても構いません(任意)。
CLIに興味のある方は、以下を参照してください。こちらを利用すると、FluxのようにCLIのみでArgoCDの操作が可能になります。

なお、NGINX以外のIngress Controllerを使用する場合は、公式ドキュメントを参照してセットでしてください。

これを反映しましょう。

kubectl apply -f argocd/argocd-ingress.yaml

しばらくすると、External DNSによるDNSレコードの追加や、TLS証明書の取得が終わり、ArgoCDのUIへのアクセスができるようになります。
ブラウザよりhttps://argocd.<your-custom-domain>/にアクセスしてみましょう。

このようにログインページが表示されていれば問題ありません。
TLS証明書の発行には時間がかかりますので、表示されない場合はもう少し待ってみましょう。待っても表示されない場合は、こちらを参考にCert Manager側の状態を確認してください。

それでは、ArgoCD組み込みの管理者ユーザーadminでログインしましょう。
パスワードは、以下で取得できます。

kubectl get secret argocd-initial-admin-secret -n argocd \
-o jsonpath="{.data.password}" | base64 -d; echo

ログインできれば確認完了です。現時点では何も登録されていない状態です。

なお、ユーザー管理については、ArgoCD単体でも可能ですが、ほとんどの組織では別途SSO基盤が用意されていることでしょう。
ArgoCDでは、Google、Keycloak等、OpenID Connect(OIDC)プロトコルに対応しているプロバイダであれば、ユーザー認証を移譲できます。
また、ArgoCDに組み込まれているDexを使うことで、OIDC非対応(SAML、LDAP等)のプロバイダーとの連携も可能です。
詳細はこちらを参照してください。

また、ArgoCDでは認可機能が備わっています。実運用ではadminは無効にしておき、別途ユーザーグループでポリシー管理をすることが望ましいでしょう。
ArgoCDの認可機能については、こちらを参照してください。

アプリケーションマニフェスト追加#

それでは、先程生成したリポジトリにアプリケーションの構成を追加していきましょう。

なお、ここはFluxのチュートリアル実施済みの場合はスキップしても構いませんが、イメージは初期バージョンの1.0.0に戻してください。

リポジトリルート(CONFIG_ROOT)直下にディレクトリappを作成し、以前Kustomize形式で作成したファイル[2]をコピーします。

mkdir ${CONFIG_ROOT}/app
# PROJECT_ROOTはアプリケーションソースコードを配置したリポジトリルート
cp -r ${PROJECT_ROOT}/app/k8s/v3/base ${CONFIG_ROOT}/app
cp -r ${PROJECT_ROOT}/app/k8s/v3/overlays ${CONFIG_ROOT}/app

以下の構成となります。

.
├── app
│   ├── base
│   │   ├── ingress
│   │   │   └── ingress.yaml
│   │   ├── kustomization.yaml
│   │   ├── task-reporter
│   │   │   └── cronjob.yaml
│   │   └── task-service
│   │       ├── deployment.yaml
│   │       └── service.yaml
│   └── overlays
│       ├── local -> 今回は未使用
│       └── prod
│           ├── kustomization.yaml
│           ├── lets-encrypt-issuer.template.yaml
│           ├── lets-encrypt-issuer.yaml
│           ├── patches -> パッチファイル
│           └── task-web -> UIマニフェスト
└── argocd
    └── argocd-ingress.yaml -> 適用済み

app/overlays/prod/kustomization.yamlimagesに指定したタグが、初期バージョンの1.0.0となっていることを確認してください。

Information

現時点でArgoCD(2.2.4)に含まれるKustomizeのバージョンが4.2.0と、Kubernetes1.21のCronJob(batch/v1)に未対応でした。
このため、CronJobマニフェストのapiVersionをbatch/v1beta1にする必要がありました。
なお、ここでは実施しませんが、カスタムバージョンのKustomizeセットアップも可能です。
Kustomizeのバージョンアップ(v4.3.0~)で対応する場合は、こちらを参照してください。

ArgoCDからGitHubへの認証設定#

GitリポジトリにArgoCDがアクセスできるよう、認証情報をクラスタに設定しましょう。

まず、GitHubにログインして、アクセストークンを発行してください。許可するスコープはrepoのみで構いません。
なお、Fluxのチュートリアルで発行作成済みでしたら、同じものを利用して構いません。

ArgoCDの認証設定は、Web UIからも作成可能ですが、ここではSecretリソース経由で実施します。

以下のコマンドでSecretリソースを作成し、専用のラベルをつけます。

# 以下は発行したアクセストークンに置き換えてください
export GITHUB_TOKEN=<github-token>
kubectl create secret generic task-tool-cluster-config-cred -n argocd \
--from-literal url=https://github.com/${GITHUB_USER}/task-tool-cluster-config \
--from-literal username=<github-user> \
--from-literal password=${GITHUB_TOKEN} \
--from-literal name=task-tool-cluster-config
kubectl label secret task-tool-cluster-config-cred argocd.argoproj.io/secret-type=repository -n argocd

作成したら、ArgoCDのUI左側のメニューよりSettings -> Repositoriesを選択してみましょう。

Repositoryとして、認証情報が設定されています。
CONNECTION STATUSを見ると、GitHubへの接続に成功していることも確認できます。

アプリケーションのGit同期設定#

ここまでで、ArgoCDを利用する準備が整いました。
Flux同様にArgoCDでも、アプリケーションの同期設定は、カスタムリソースを作成する必要があります。
これには以下の方法がサポートされています。

今回は、リソース作成/更新は「Declarative Setup」、その確認に「Web UI」を使用します。

プロジェクト(AppProject)#

アプリケーションの論理的なグループです。
デフォルトで用意されているもの(default)の使用も可能ですが、専用のプロジェクトで作成しておくと、ソースとなるGitリポジトリやターゲットクラスタを制約したり、ロールで権限を絞ることができます。通常はチーム単位で作成しておくと良いでしょう。

argocd/task-tool-app-project.yamlを作成し、以下を記述してください。
<github-user>は自分のものに置き換えてください。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: task-tool
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: task-tool application for k8s tutorial
sourceRepos:
- "https://github.com/<github-user>/*"
destinations:
- namespace: prod
server: https://kubernetes.default.svc

ソースは自分のGitHubアカウント配下のリポジトリに限定し、prodNamespaceのみにデプロイすることを許可しています。

他にも多数の設定が可能です。詳細はこちらを参照しくてださい。

これを適用します。

kubectl apply -f argocd/task-tool-app-project.yaml

ArgoCDのUIで、Settings -> Projectsを選択し、以下のように表示されれば、作成完了です。

アプリケーション#

ここで、デプロイ対象のアプリケーションの同期設定をします。
argocd/task-tool-app-project.yamlを作成し、以下を記述してください。
ここでも、<github-user>は自分のものに置き換えてください。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prod-task-tool
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: task-tool # 先程作成したプロジェクトを指定
source:
repoURL: https://github.com/<github-user>/task-tool-cluster-config
targetRevision: main
path: app/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false

sourcedestinationにプロジェクト設定で許可したもの以外を設定すると、エラーとなります。
また、sourcepathにはKustomizeのoverlaysを指定しています。ArgoCDではこの配下のファイルからマニフェスト生成のツールを推論します。

syncPolicyとしてautomatedを指定しています。これを設定することで、Gitからクラスタ環境への同期が自動になります。

Applicationリソースの詳細はこちらを参照しくてださい。

こちらもクラスタ環境に適用しましょう。

kubectl apply -f argocd/task-tool-application.yaml

少し待ってから、ArgoCDのUIのトップページを見ると、アプリケーションが作成され、GitHubとの同期が始まっている様子が分かります。

対象アプリケーションをクリックして、実際のアプリケーションの内容を見てみましょう(右上のアイコンより表示切替できます)。

リソースツリー

ノード状態

ネットワーク構成

以下の状態が分かります。

  • デプロイリソース間の関係性
  • 各リソースの同期状態や健康状態
  • 現在のクラスタ状態に対応するGitコミット
  • 現在のクラスタとGitの差分有無
  • ノードの状態(メモリ・CPU使用量やデプロイされているPod)
  • ネットワーク通信経路

また、各リソースをクリックするとマニフェストリソースの詳細やログ・イベントを参照できます。
さらに、Gitの状態(Desired Manifest)と実際の状態(Live Manifest)のコードレベルでの差分表示もできます(後述)。

これだけ見ても、ArgoCDのUIがどれほどリッチなのかは一目瞭然でしょう。kubectlを使う必要性はほとんどありません。

なお、ここでは直接kubectl applyを使ってArgoCDのリソース(AppProject/Application)を反映しました。
さらに一步進めて、Fluxのカスタムリソースのように、これもGitと同期するようにすること(app of apps pattern)も可能です。
ここでは実施しませんが、こちらに挑戦する場合は、公式ドキュメントのCluster Bootstrappingを参照してください。

動作確認#

ここからはGit上のマニフェストを修正して、ArgoCDの挙動を確認してみましょう。

ArgoCDの動作を確認するために、一旦自動反映を無効にしておきます。
argocd/task-tool-application.yamlを以下のように変更してください。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prod-task-tool
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: task-tool
source:
repoURL: https://github.com/kudoh/task-tool-cluster-config
targetRevision: main
path: app/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: prod
# syncPolicy:
# automated:
# prune: true
# selfHeal: true
# allowEmpty: false

syncPolicyをコメントアウトするだけです。これでGitとの自動同期が停止されます。
こちらで更新しておきます。

kubectl apply -f argocd/task-tool-application.yaml

レプリカ数変更#

タスク管理API(task-service)のトラフィックが急増したと想定し、レプリカ数を上げてみましょう。
app/overlays/prod/deployment.patch.yamlを変更します。

apiVersion: apps/v1
kind: Deployment
metadata:
name: task-service
spec:
replicas: 5
# (以下省略)

replicasでレプリカ数を3から5に変更しています。後はコミットしてプッシュするだけです。

git add app && git commit -m "Increase task-service replica size"
git push

ArgoCDのUIを確認してみましょう。

ArgoCDが、クラスタ環境とGitが異なることを検知し、Sync StatusをOutOfSyncに変更していることが分かります。
App Diffボタンをクリックすると以下のようになります。

Git(期待する状態)と現在のクラスタ状態の差分が表示されます。ここではレプリカ数(replicas)が異なっているということが分かります。

ではこれを同期しましょう。Syncボタンをクリックし、表示されるスライドバーよりSynchronizeボタンを押します。

Sync StatusがSyncedとなり、追加となった2つのPodが起動されていく様子が分かります。
また、その後にPodの起動が完了し、App HealthがHealthyとなっていくことも確認できます。
このように、ArgoCDではGitとの同期状態だけでなく、デプロイリソース自体の状態も監視し、クラスタが正常に動作しているかをチェックしています。

アプリケーションのバージョンアップ#

ここではアプリケーションを新しいタグに更新し、これを自動反映できるかを確認しましょう。

現状ArgoCDでは、Fluxのようにビルトインでイメージ更新を検知して、クラスタ環境に反映する機能はありません。
これには別途プロダクト(Argo CD Image Updater)の導入が必要です。

今回は、手動でイメージタグを更新し、先程と同様にArgoCDで同期しましょう。自動アップデートを試したい場合は上記を試してみてください。

新しいイメージタグ作成#

Fluxのチュートリアルで実施済みの場合は、スキップして構いません。

ArgoCD上の動作確認が目的のため、アプリケーション自体は変更せずに、1.0.0タグのイメージを1.1.0でも作成します。
以下を実行し、ECRに1.1.0のイメージをプッシュします。

# バージョン1.1.0作成
docker tag <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/mamezou-tech/task-service:1.0.0 \
<aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/mamezou-tech/task-service:1.1.0

# ECRプッシュ
aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
docker push <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/mamezou-tech/task-service:1.1.0

動作確認#

それでは、新しいイメージタグを反映するようにしましょう。
app/overlays/prod/kustomization.yamltask-serviceのイメージを、1.0.0から1.1.0に変更しましょう。

images:
- name: task-service
newName: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/mamezou-tech/task-service
newTag: 1.1.0

変更後はGitにコミット&プッシュしておきましょう。

git add app && git commit -m "Bumped to 1.1.0"
git push

先程同様にArgoCDのUIはOutOfSyncになります。差分を確認してみましょう。

アプリケーションのバージョンアップが差分として出ていることが分かります。
後は新しいバージョンでクラスタを同期しましょう。手順は先程と同じです。

アプリケーションが、ローリングアップデートで更新されていく様子が見て取れます。

また、HISTORY AND ROLLBACKをクリックすると、リリース内容を履歴確認できます。

このようにインフラの変更(今回はレプリカ数)も、アプリケーションの変更も手順は変わりません。
今回は手動同期としましたが、自動同期を再度有効化すれば、全自動でGit上の状態と同期され、そのフィードバックをUIで確認できます。

クリーンアップ#

ArgoCDで作成したリソースについては、以下で削除してください。

# Applicationとともに、デプロイしたアプリも削除される
kubectl delete -f argocd/task-tool-application.yaml
kubectl delete -f argocd/task-tool-app-project.yaml
# Ingress
kubectl delete -f argocd/argocd-ingress.yaml

ArgoCD自体はHelmコマンドで削除します。

helm uninstall argocd -n argocd

それ以外のリリース削除については、以下を参照してください。

まとめ#

ここでは、もう1つのGitOpsツールとして人気のArgoCDを導入して、継続的デリバリ環境を構築しました。
CLIベースで地味なFluxと違い、ArgoCDのリッチなUIに惹かれた方も多いのではないでしょうか?

とはいえ、FluxがArgoCDより劣っていることはありません。
Fluxは、v2リリースで洗練されたカスタムリソースとそのコントローラが導入され、ArgoCDよりも構造が理解しやすくなったと感じています。

どちらを選択するにせよ、Gitを中心としたサービスのデリバリ環境を整えることは、安定した運用の実現に欠かせません。
開発だけにとどまらず、その後の継続的な改善フィードバックを回すことは健全なプロジェクトにとって必須要素です。
これらのツールの導入自体の敷居は低いと感じますので、CI環境とセットで考えていくと良いでしょう。

なお、Flux同様に、ここでもArgoCDの通知機能については触れませんでした。
現状はArgoCDでチャット等への通知機能を使う場合は、 ArgoCD Notificationsを別途導入する必要があります[3]
興味のある方はこちらも試してみてください。


参考資料

ArgoCDドキュメント: https://argo-cd.readthedocs.io/en/stable/


  1. Fluxでも開発中のものはありますが、正式版ではありません。https://github.com/fluxcd/webui ↩︎

  2. 未作成の場合は、こちらを利用可能です。lets-encrypt-issuer.template.yamlのemail設定とファイルリネーム(template削除)と、kustomization.yamlimages/カスタムドメインを自身の環境に合わせて変更してください。 ↩︎

  3. ロードマップ上、ArgoCDのv2.4では、ArgoCD Notificationsについても本体に組み込まれる予定です。 ↩︎