Fujitsu Advent Calendarの3日目です。2日目と同じテーマであるFluxによるGitOpsについて書きたいと思います。昨日のブログでは、ハンズオン部分が間に合わなかったので3日目も空いていることですし、記事を 概要編 と ハンズオン編 で分けてみました。
ハンズオンで実現するGitOps環境
実現する環境は以下の要素で構成されます:
- Github上のリポジトリ
- GitHub ActionによるCIパイプライン
- Github上のコンテナレジストリ(OCIレジストリ)
- k8sクラスタ上のFlux
Github上のリポジトリへのコミットによってCIパイプラインがトリガーされ、OCI ArtifactsをOCIレジストリに格納するジョブが実行されます。k8sクラスタ上のFluxがOCI Artifactsのハッシュが更新されたことを検出して、OCI Artifactsに含まれるマニフェストを取得し、クラスタに適用します。
最終的には以下のような構成を作り上げていきます。
それでは1つずつ見ていきます。
k8sクラスタの作成
まずはローカルにシングルノードクラスタを立ち上げます。
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.25.3) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Thanks for using kind! 😊
GitOpsのSourceとなる空のGitリポジトリを作成する
Github上に空のリポジトリを作成します。ここにFluxの設定に関するマニフェストをこの後格納していきます。
この段階では上記のような構成になります。
FluxのVSCode拡張機能を使ってクラスタ上にFlux CRDsをセットアップする
先ほど作成したGitリポジトリをクローンして、VSCodeで開きます。
$ git clone https://github.com/nekia/qiita-podinfo
$ cd qiita-podinfo
$ code .
VSCodeに拡張機能GitOps Tools for Flux
を追加します。ここで1点注意が必要です。デフォルトでインストールされるバージョンではなく、プレリリース版(執筆時点ではv0.22.1668822354
)をインストールしないと、私の環境だとうまく動作しませんでした。
- 拡張機能のページに満たす必要のある依存関係がきさいしてあるので別途対応が必要です(kubectl/flux/gitコマンドのインストール)
拡張機能をインストールするとサイドメニューにGitOpsのアイコンが現れるので、クリックしてGitOps Viewに移動します。ここで正しくセットアップできていると以下のように、先に生成したk8sクラスタ kind-kind
が検出されています。ここから SOURCES
もしくはWORKLOADS
ウィンドウ内の Enable GitOps
をクリックし、クラスタにFluxの各種CRDをインストールします。
これによりクラスタ内にGitOpsに必要な各種コントローラがインストールされます。
GitOps Toolkitに対してターゲットとなるソースリポジトリとデプロイメント方法を設定
ここではPodinfoという既存のサンプルアプリ(https://github.com/stefanprodan/podinfo) をターゲットにしてみます。クラスタへのGitOps Toolkitのインストールで追加されたGitRepository
というリソースを生成しまが、その際にこのターゲットを指定します。アプリのデプロイメント方法に関しては、指定したリポジトリ内にあるKustomizations
のマニフェストファイルへのパスを指定します(./kustomize
)。
上記の設定で + Create
をクリックするとターゲットのGitリポジトリとのReconcileが開始され、しばらくするとサービスがクラスタ上で動作していることが確認できます。
$ kubectl port-forward svc/podinfo 9898:9898
Forwarding from 127.0.0.1:9898 -> 9898
Forwarding from [::1]:9898 -> 9898
この時点での構成は以下のようになっています。ご覧頂くと分かる通り、アプリを構成するマニフェストはローカルのみに存在してGitOpsと呼べる状態になっていません(Podinfo自体はGitリポジトリから参照していますが、これはあくまでRead OnlyなUpstreamのGitリポジトリをサンプルアプリとして利用しているためGitOpsには該当しません)。
次のステップでは、現在ローカルのみに存在しているマニフェストをGitリポジトリにPushし、それらをOCI ArtifactsとしてOCIレジストリに登録するCIパイプラインを作成して、クラスタからOCI経由で同期するように変更していきます。
なお、ここまでで作成した Sources と Workloads は一旦削除します。
マニフェストファイルとOCIレジストリとの同期設定をGitリポジトリにPushする
まずは先ほどと同じ設定で、SourceとKustomizationをVSCode拡張機能から作成し、今度は YAML
を選択してマニフェストファイルをエクスポートします。
# cluster/demo/app.yaml
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: nekia-qiita-podinfo-main
namespace: flux-system
spec:
gitImplementation: go-git
interval: 1m0s
ref:
branch: master
url: https://github.com/stefanprodan/podinfo
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 1m0s
path: ./kustomize
prune: true
sourceRef:
kind: GitRepository
name: nekia-qiita-podinfo-main
targetNamespace: default
これを作成したGitリポジトリの cluster/demo/app.yaml
にPushします。このマニフェストは、この後作成するCIパイプラインにて、OCI ArtifactsとしてGithub Container Registryに格納されるファイルになります。
次に、OCIレジストリからマニフェストを含むOCI Artifactsを取得する設定と、そのマニフェストを使ってデプロイメントする設定を作成していきます。
これも YAML
を選択してマニフェストファイルをエクスポートし、Gitリポジトリのcluster/demo/sync.yaml
にPushします。
OCIレジストリをターゲットにした同期をローカルからBootstrapする
この段階でGitOpsの同期を開始します。設定がまだ足りておらずエラーが出るので1つずつ対処していきます。Bootstrap方法は以下の通り、先ほどPushしたcluster/demo/sync.yaml
をローカルでクラスタに適用します。OCIレジストリ上のOCI Artifacts(この後のステップで作成。app.yamlとsync.yamlを含む)との同期をBootstrapします。一度Bootstrapしてしまえば、以降はGitリポジトリへの変更のたびに更新されるOCI ArtifactsをソースとしたGitOps構成ができあがります。
$ kubectl apply -f cluster/demo/sync.yaml
ocirepository.source.toolkit.fluxcd.io/nekia-qiita-oci-podinfo-main created
kustomization.kustomize.toolkit.fluxcd.io/oci-podinfo created
この時点での構成は以下のようになっています。
Github Container Registryへアクセスするためのシークレット作成
同期を開始すると新たに OCIRepository
が見えるようになりますが、作成に失敗していることが分かります。
設定で指定したシークレットghcr-secret
を作成してやる必要がありますので以下のコマンドで作成します。GITHUB_USERとGITHUB_TOKENは別途取得、設定が必要です。
$ flux create secret oci ghcr-secret --url=ghcr.io --username=$GITHUB_USER --password=$GITHUB_TOKEN
► oci secret 'ghcr-secret' created in 'flux-system' namespace
$ kubectl get secrets -n flux-system
NAME TYPE DATA AGE
ghcr-secret kubernetes.io/dockerconfigjson 1 7m43s
シークレットを設定するとOCIRepository
のエラーが AuthenticationFailed
からOCIArtifactPullFailed
に変わります。まだGithub Container Registry上にsync.yaml
で指定したOCI Artifacts nekia/manifests/podinfo
が存在しないため、Pullに失敗しています。次のステップでGitHub上にGitHub Actionを使ったCIパイプラインを追加してOCI Artifactsをレジストリに追加していきます。
CIパイプラインでOCI Artifactsをレジストリに格納する
Github Actionについては、公式ドキュメントでサンプルが紹介されており、その設定を参考にします。
name: push-artifact-staging
on:
push:
branches:
- 'main'
permissions:
packages: write # needed for ghcr.io access
env:
OCI_REPO: "oci://ghcr.io/nekia/manifests/podinfo"
MANIFEST_PATH: "./cluster/demo"
jobs:
kubernetes:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Flux CLI
uses: fluxcd/flux2/action@main
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# - name: Generate manifests
# run: |
# kustomize build ./manifests/staging > ./deploy/app.yaml
- name: Push manifests
run: |
flux push artifact $OCI_REPO:$(git rev-parse --short HEAD) \
--path=$MANIFEST_PATH \
--source="$(git config --get remote.origin.url)" \
--revision="$(git branch --show-current)/$(git rev-parse HEAD)"
- name: Deploy manifests to staging
run: |
flux tag artifact $OCI_REPO:$(git rev-parse --short HEAD) --tag latest
このファイルをリポジトリの .github/workflows
配下にPushするとCIパイプラインが作成され、早速PushをトリガーにしてCIジョブがスタートし、以下の通りGithub Container RegistryにOCI Artifactsが格納されます。
なおCIジョブによってGithub Container Registry上に作成されたOCI Artifactsは以下のコマンドで中身を確認可能です。
$ flux pull artifact oci://ghcr.io/nekia/manifests/podinfo:latest --output ./tmp/
► pulling artifact from ghcr.io/nekia/manifests/podinfo:latest
✔ source https://github.com/nekia/qiita-podinfo
✔ revision main/c43f6a46e67730359a6f2d4db9522f94d20308ff
✔ digest ghcr.io/nekia/manifests/podinfo@sha256:0084c529dad5632a7972a0ac18ac447da37db9593ae8a86ba606bb1e12e2d088
✔ artifact content extracted to ./tmp/
$ find ./tmp/ -type f
./tmp/cluster/demo/app.yaml
./tmp/cluster/demo/sync.yaml
この状態でOCIRepositoryの同期状態を再確認すると、同期に成功しているはずです。合わせてOCI Artifactsから取得されたマニフェストapp.yaml
によってGitRepository
およびKustomization: podinfo
も作成されているはずです。
前述した手順と同じようにServiceをPort Forwardすると、http://localhost:9898/ からPodInfoアプリにアクセスできるはずです。この段階で、本記事の冒頭に載せていた構成が完成しました。
PodInfoアプリのバージョンを変更する
cluster/demo/app.yaml
内で指定しているアプリのバージョンを 6.2.3
から 6.2.2
に変更してみます。
Gitレポジトリの変更を検出して新たなバージョンのアプリケーションが自動でデプロイされることが以下の通り確認できると思います。
Fluxで管理されるリソースの由来をトレースする
例えば今回動かしているPodInfoアプリがどのバージョンで、どのマニフェストに基づいてデプロイメントされているのか、を確認したい場合には以下の手順でトレースが可能です。flux CLIの trace
コマンドで指定リソースの由来を追跡することが可能です。
$ flux trace -n default deployment podinfo
Object: Deployment/podinfo
Namespace: default
Status: Managed by Flux
---
Kustomization: podinfo
Namespace: flux-system
Target: default
Path: ./kustomize
Revision: 6.2.2/1cf228c67b8f8102db1a6966f81a95c09954b3e8
Status: Last reconciled at 2022-12-04 22:07:43 +0900 JST
Message: Applied revision: 6.2.2/1cf228c67b8f8102db1a6966f81a95c09954b3e8
---
GitRepository: podinfo
Namespace: flux-system
URL: https://github.com/stefanprodan/podinfo
Tag: 6.2.2
Revision: 6.2.2/1cf228c67b8f8102db1a6966f81a95c09954b3e8
Status: Last reconciled at 2022-12-04 22:02:09 +0900 JST
Message: stored artifact for revision '6.2.2/1cf228c67b8f8102db1a6966f81a95c09954b3e8'
https://github.com/stefanprodan/podinfo
の6.2.2/1cf228c67b8f8102db1a6966f81a95c09954b3e8
から取得したGitRepository
に由来することが分かります。更にGitRepository
リソースpodinfo
のラベルをチェックするとkustomize
リソースoci-podinfo
に由来することが読み取れます。
$ kubectl describe gitrepositories.source.toolkit.fluxcd.io -n flux-system podinfo | head -n 10
Name: podinfo
Namespace: flux-system
Labels: kustomize.toolkit.fluxcd.io/name=oci-podinfo
kustomize.toolkit.fluxcd.io/namespace=flux-system
Annotations: <none>
API Version: source.toolkit.fluxcd.io/v1beta2
Kind: GitRepository
Metadata:
Creation Timestamp: 2022-12-04T01:46:08Z
Finalizers:
次にkustomize
リソースoci-podinfo
の詳細を確認すると、OCIRepository
リソースnekia-qiita-oci-podinfo-main
が由来であることが更に分かります。
$ kubectl get kustomizations.kustomize.toolkit.fluxcd.io -n flux-system oci-podinfo -o jsonpath='{.spec}' | jq
{
"force": false,
"interval": "30s",
"path": "./",
"prune": true,
"sourceRef": {
"kind": "OCIRepository",
"name": "nekia-qiita-oci-podinfo-main"
},
"targetNamespace": "flux-system"
}
最後にOCIRepository
リソースnekia-qiita-oci-podinfo-main
の詳細を確認すると、oci://ghcr.io/nekia/manifests/podinfo:latest
のOCI Artifactsを参照していることが分かり、そのOCI Artifactsはhttps://github.com/nekia/qiita-podinfo
リポジトリのmain/c43f6a46e67730359a6f2d4db9522f94d20308ff
から作成されていることが分かります。長かった。。。
{
"interval": "30s",
"provider": "generic",
"ref": {
"tag": "latest"
},
"secretRef": {
"name": "ghcr-secret"
},
"timeout": "60s",
"url": "oci://ghcr.io/nekia/manifests/podinfo"
}
{
"checksum": "6941be0504e583be9b631d3a06ae3d7ae28bc05c9661b0aa0c6cd9d3e8d8b5d4",
"lastUpdateTime": "2022-12-04T13:02:08Z",
"metadata": {
"org.opencontainers.image.created": "2022-12-04T13:01:33Z",
"org.opencontainers.image.revision": "main/c43f6a46e67730359a6f2d4db9522f94d20308ff",
"org.opencontainers.image.source": "https://github.com/nekia/qiita-podinfo"
},
"path": "ocirepository/flux-system/nekia-qiita-oci-podinfo-main/0084c529dad5632a7972a0ac18ac447da37db9593ae8a86ba606bb1e12e2d088.tar.gz",
"revision": "latest/0084c529dad5632a7972a0ac18ac447da37db9593ae8a86ba606bb1e12e2d088",
"size": 532,
"url": "http://source-controller.flux-system.svc.cluster.local./ocirepository/flux-system/nekia-qiita-oci-podinfo-main/0084c529dad5632a7972a0ac18ac447da37db9593ae8a86ba606bb1e12e2d088.tar.gz"
}
- 本来は
flux trace
コマンドでOCI Artifactまで辿れるはずなのですが今回作成した環境だとうまくいかず、コミュニティに質問中です。
最後に
アプリのGitレポジトリとマニフェストのGitレポジトリが出てきて分かりにくい部分があったかと思います。私はおうちK8sクラスタ環境でFluxを使っていますが、今回のOCI Artifactsサポートで解決するようなチャレンジを抱えるほど使い込んでいないため、より理解しやすいデモを共有できませんでしたが、少しでもFluxにでもGitOpsにでも興味持って試してみたいと思っていただけたら幸いです。
参考URL
概要編にも載せましたが再掲しておきます。
- 今回のデモリポジトリ
- OCI cheatsheet | Flux
- OCI Repositories | Flux
- go-containerregistry/crane.md at main · google/go-containerregistry
- OCI Artifacts Explained. Are they real? Kind of! | by Dan Lorenc | Medium
- OCI Artifacts and a View of the Future – Steve Lasker
- flux2/rfcs/0003-kubernetes-oci at main · fluxcd/flux2
- Flux Security & Scalability using VS Code GitOps Extension - YouTube
- Flux’s Security & Scalability with OCI & Helm (Part 2) with Kingdon Barrett - YouTube