ArgoCDのブルーグリーンデプロイ、GitOpsをローカル環境で試す
はじめに
以前作った、ローカルの k8s で動作するアプリケーションに以下を導入しました。
- ArgoCD Rollouts によるブルーグリーンデプロイ
- ArgoCD にリポジトリを監視してもらい、変更があれば自動でデプロイ(GitOps)
作ったアプリケーションの内容は別の記事で書いてます。
コードはこちら
skaffold、kustomize
skaffold、kustomize を利用しており、こんな感じのディレクトリ構成にしました。
kustomize によりマージされたマニフェストが k8s/manifest/app/app.yml
として吐き出されるようにしています。
├── k8s
│ ├── kustomize
│ │ ├── base
│ │ │ ├── app
│ │ │ │ ├── deployment.yml
│ │ │ │ ├── ingress.yml
│ │ │ │ ├── kustomization.yml
│ │ │ │ ├── nginx-default-configmap.yml
│ │ │ │ ├── nginx-nginx-configmap.yml
│ │ │ │ └── service.yml
│ │ │ └── redis
│ │ │ ├── deployment.yml
│ │ │ ├── kustomization.yml
│ │ │ └── service.yml
│ │ └── overlays
│ │ └── local
│ │ ├── build.sh
│ │ └── kustomization.yml
│ ├── manifest
│ │ └── app
│ │ └── app.yml
│ └── skaffold.yml
skaffold でファイルを監視し、ホットリロードするようにしています。
apiVersion: skaffold/v2beta10
kind: Config
metadata:
name: app
build:
artifacts:
- image: pokoytan/k8s-request-counter-emitter
context: ./
docker:
dockerfile: ./Dockerfile
tagPolicy:
sha256: {}
local:
push: false
useBuildkit: true
profiles:
- name: local
deploy:
kustomize:
paths: ["k8s/kustomize/overlays/local"]
ArgoCD
参考:https://qiita.com/ttr_tkmkb/items/ff4d88f3a91db982b0ab
ArgoCD のインストール、ArgoCD のパスワード設定をします。
ポートフォワードの設定をして、ArgoCD のGUI にアクセスできるようにします。
kubectl port-forward svc/argocd-server -n argocd 8080:443
https://localhost:8080/
にアクセスすると、こんな画面になるので、設定したパスワードでログインします。
ArgoCD が対象にするクラスターをローカルの docker-desktop にします。
argocd cluster add docker-desktop
argocd app create
を実行して、ArgoCD のアプリケーションを作成し、リポジトリを監視させます。
argocd app create k8s-request-counter-emitter \
--repo https://github.com/pokotyan/k8s-request-counter-emitter \
--path k8s/kustomize/overlays/local \
--dest-server https://kubernetes.default.svc \
--dest-namespace default \
--sync-policy automated \
--auto-prune \
--self-heal
上記のコマンドで、 ArgoCD が以下のような監視、自動デプロイをやってくれるようになります。
このリポジトリの
--repo https://github.com/pokotyan/k8s-request-counter-emitter
このパスにあるマニフェストに
--path k8s/kustomize/overlays/local
何か変化があれば、自動でデプロイする
--sync-policy automated
argocd app create
が完了すると、 ArgoCD により ローカル環境へアプリケーションがデプロイされます。
GUI 上でもアプリケーションが追加されており、リポジトリとの同期状態や動作状態がわかるようになります。
いろんな方法がある ArgoCD のアプリケーション作成
上記では、 argocd app create
を実行して、ArgoCD のアプリケーションリソースを作成しましたが、他のやり方もあります。
こんな感じのマニフェストを用意し、 kubectl apply
することでも作成できます。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: k8s-request-counter-emitter
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/pokotyan/k8s-request-counter-emitter
targetRevision: HEAD
path: k8s/kustomize/overlays/local
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
また、ArgoCD の GUI コンソールからぽちぽちすることでも作成できます。
CI/CD
参考:
ArgoCD はリポジトリの変更を検知すると、指定したクラスターに自動でデプロイを行います。なので、リポジトリにコードがマージされたら
- アプリケーションのイメージをビルド
- マニフェストのイメージのタグを動的に書き換える
- 現在の状態をコミットし、リポジトリへ push
という流れを構築する必要があります?(他にもやり方はあるのかもしれない)
そのため、こんな感じの GithubActions を作成しました。
name: ci
on:
push:
branches: main
jobs:
push-image-to-docker-hub:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Image
run: |-
docker build --tag pokotyan/k8s-request-counter-emitter:${GITHUB_SHA} .
- name: Push Image
run: |-
docker push pokotyan/k8s-request-counter-emitter:${GITHUB_SHA}
deploy-local:
runs-on: ubuntu-latest
needs: push-image-to-docker-hub
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Kustomize
run: |-
curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_linux_amd64
chmod +x ./kustomize
- name: Set up Git
run: |
git config --local user.email "tomohiro_soga@yahoo.co.jp"
git config --local user.name "pokotyan"
- name: Set up kubeval
run: |-
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz
tar xf kubeval-linux-amd64.tar.gz
sudo cp kubeval /usr/local/bin
- name: Commit after Build manifest
run: |
bash ./k8s/kustomize/overlays/local/build.sh pokotyan/k8s-request-counter-emitter:${GITHUB_SHA}
git commit -am "Update image to pokotyan/k8s-request-counter-emitter:$GITHUB_SHA"
git pull
git push origin main
main のブランチに何か変更があれば
- アプリケーションのイメージをビルド、 DockerHub へ push
- マニフェストのイメージのタグを動的に書き換える
- 現在の状態をコミットし、リポジトリへ push
の流れを実行します。動的に書き換えているイメージのタグは現在のコミットハッシュを利用するようにしています。
タグを動的に書き換える際に実行している build.sh はこんな感じのスクリプトで kustomize edit set image
を利用して、kustomize の base となるマニフェスト内にある app-image の記述を動的に現在のアプリのイメージに書き換えています。
#!/bin/bash
set -euo pipefail
cd $(dirname $0)
IMAGE_NAME="$1"
kustomize edit set image app-image="${IMAGE_NAME}"
kustomize build . >../../../manifest/app/app.yml | kubeval --strict --ignore-missing-schemas
また、 kustomize で一つにマージされたマニフェストに対して、 kubeval でバリデーションチェックをしています。
kubeval はまだ CRD のリソースには対応していないので、そのまま kubeval をかけると Rollout のところでエラーになります。なので、 --ignore-missing-schemas
のオプションをつけて CRD だった場合はチェックをスルーするようにしています。
余談ですが、 main ブランチの変更をトリガーとする GithubActions の処理内で main ブランチへの push をしたら GithubActions の無限ループになるんじゃないかと思ったんですが、特にそんな動作にはなりませんでした。
ArgoCD Rollouts
参考:
ArgoCD Rollouts のコントローラーと、ArgoCD Rollouts の cli をインストールします。
Deployment の yaml を Rollout の yaml に書き換えます。
Service をこんな感じで、二つ用意します。
active-service が、実際にアプリケーションから参照されるサービスで、
preview-service が、新しくデプロイされる pod が準備完了となるまで控えておくサービス、みたいなイメージです。
apiVersion: v1
kind: Service
metadata:
name: active-service
labels:
app: app
spec:
type: NodePort
selector:
app: app
ports:
- port: 80
targetPort: 80
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: preview-service
spec:
type: NodePort
selector:
app: app
ports:
- port: 80
targetPort: 80
protocol: TCP
これだけで、Argo Rollouts によるブルーグリーンデプロイができるようになります。
ブルーグリーンデプロイがされていく様子を眺める
kubectl argo rollouts get rollout app --watch --cluster docker-desktop
で Rollouts の状態をリアルタイムで眺めることができます。
実際に、どんな感じで表示が移り変わっていくかを確認してみます。
-
8877ec
のイメージを利用する pod が現在active-service
にぶら下がっている
-
main ブランチへ何かしらのコード変更を push をする
(ここでは main ブランチへ直pushしていますが、実運用であれば main ブランチへのプルリクのマージがトリガーになると思います)
-
main ブランチへの push により GithubActions が動く
-
GithubActions の処理でアプリケーションのイメージのビルド、マニフェストのイメージタグの更新、 main ブランチへの commit、push がされる
イメージのタグが現在のコミットハッシュの 35308c に更新される
-
ArgoCD がリポジトリの更新を検知し、ブルーグリーンデプロイが動き始める。
まず、 preview-service に紐づく pod の作成が動き始める。 pod が利用するイメージはタグが35308c
の新しいもの。
-
pod の作成が完了し、
35308c
のイメージを利用する pod の方がactive-service
に紐づく。
-
古い
8877ec
のイメージを利用する pod の削除処理が始まる。
-
8877ec
のイメージを利用する pod は全部消え、35308c
のイメージを利用する pod だけが active-service にぶら下がる形で残る。
ハマったところ
開発している最中は
skaffold dev -p local --port-forward -f k8s/skaffold.yml
で、k8s のアプリケーションをローカルにデプロイしていました。
この状態で、 argocd app create
を実行すると
skaffold によるローカル環境へのデプロイと、 ArgoCD によるローカル環境へのデプロイが両方動き、正しくアプリケーションが動作しない感じになりました。
なので、 argocd app create
を実行する前は skaffold は事前に落としておく必要がありました。
(逆もしかりで skaffold で動かすなら argocd app delete
で ArgoCD のアプリケーションは消しておく)
ArgoCD による GitOps を導入する場合、 ArgoCD が対象とするクラスターは本番環境とかだと思うので、ローカルで ArgoCD を試す時ならではのハマりポイントな気がしました。
最後に
今回、めんどくさくてアプリケーションのコードと k8s のマニフェストを同じリポジトリで管理していますが、 本来なら分けた方がいいです。(ArgoCD のベストプラクティスにも記載がある)
アプリケーションのコードとマニフェストを同じリポジトリで管理してしまうと、ちょっとしたコードの修正のたびにデプロイが動くということになります。デプロイのタイミングやデプロイを実行できるメンバーを管理したいという場合にはマニフェストのリポジトリが分かれていた方がやりやすいです。
Discussion