Astroを自前CI/CDでデプロイする――Forgejo、BuildKit、Harbor、Argo CDの実例

Astroを自前CI/CDでデプロイする――Forgejo、BuildKit、Harbor、Argo CDの実例のカバー画像

先に結論

この記事では、実際に運用しているこのブログのAstroアプリを例に、ソースコードのcommitが本番Podへ届くまでを追います。

現在の経路は、次のようになっています。

[手元のAstroプロジェクト]

  ├─ git push
  │      ↓
  │  [Forgejo]
  │      └─ Astroのソースコードを管理

  └─ buildctl
         ↓ kubectl port-forward
     [BuildKit]

         ├─ Astroをビルド
         ├─ コンテナイメージを作成
         └─ semverタグを付けてpush

                [Harbor]

                    │ 新しいタグを検知

        [Argo CD Image Updater]

                    │ KustomizeのnewTagを変更
                    │ Forgejoへcommit

                [Forgejo]


               [Argo CD]
                    │ automated sync

          [ブログの本番Pod]

たとえば、ブログの修正を含むイメージを0.3.21としてHarborへpushすると、Argo CD Image Updaterがそのタグを検知します。

Image Updaterは本番Podを直接変更するのではなく、デプロイ用リポジトリにあるKustomizeのnewTag0.3.21へ更新し、その変更をForgejoへcommitします。Argo CDはそのcommitを検知し、本番Podを新しいAstroイメージへ入れ替えます。

人間が行う作業は、大きく2つです。

1つ目は、AstroのコードをForgejoへpushすること。

2つ目は、新しいsemverタグを付けてイメージをビルドし、Harborへpushすることです。

新しいタグの検知、Gitへの書き戻し、本番への反映は自動化しています。

ただし、commitしただけでビルドが始まるわけではありません。ビルドの開始は手動であるため、現状は完全なCI/CDではなく、**「手動ビルド+自動デプロイ」**です。

道のり1:AstroのコードをForgejoへpushする

このブログのフロントエンドはAstroで作っています。

Astroのソースコードと、Kubernetesへデプロイするためのmanifestは、別のGitリポジトリとして管理しています。いずれも正本はForgejoにあり、GitHubは外部ミラーとして利用しています。

コードを修正したら、まずAstro側のリポジトリへcommitし、Forgejoへpushします。

git add .
git commit -m "Update blog application"
git push origin main

この時点では、まだ本番環境には何も起きません。

Argo CDが監視しているのはデプロイ用リポジトリであり、Astroのソースコードをpushしただけでは、コンテナイメージも本番Podも更新されないからです。

次に、Astroのソースコードから新しいコンテナイメージを作ります。

道のり2:BuildKitでAstroのイメージを作る

イメージのビルドには、クラスタ内で動かしているBuildKitを使っています。

手元のマシンではDockerデーモンを動かさず、buildctlからクラスタ内のBuildKitへビルドを依頼します。

Astroのプロジェクトには、依存パッケージのインストール、Astroのビルド、配信用ランタイムの作成を行うDockerfileを置いています。BuildKitはそのDockerfileとソースコードを受け取り、ブログを実行するためのコンテナイメージを作ります。

ただし、BuildKitはKubernetesのClusterIP Serviceとして公開しているため、クラスタ外の作業マシンから直接接続することはできません。

そこで、作業時だけkubectl port-forwardを使います。

kubectl port-forward service/buildkit 1234:1234

別のターミナルから、ローカルへ転送したポートを指定してビルドします。

buildctl \
  --addr tcp://127.0.0.1:1234 \
  build \
  --frontend dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=image,name=<Harbor上のイメー>:0.3.21,push=true

実際のレジストリ名、プロジェクト名、イメージ名は環境ごとに異なるため、ここでは省略しています。

ビルドが成功すると、Astroの静的ファイルやサーバー処理を含む新しいコンテナイメージが作られ、そのままHarborへpushされます。

ここで実際に遭遇したのが、イメージのビルドは成功するのに、Harborへのpushだけが401で失敗する問題でした。

道のり3:Harborに届いたAstroイメージを検知する

Harborへ0.3.21のような新しいタグが登録されると、Argo CD Image Updaterが定期的にレジストリを確認し、現在のデプロイ設定より新しいバージョンがあることを検知します。

対象にするのは、次のような正式なsemver形式のタグだけです。

0.3.19
0.3.20
0.3.21

latestdev、一時的な検証用タグは、自動更新の対象にしません。

Image Updaterは、検知したイメージを直接Deploymentへ設定するのではなく、デプロイ用リポジトリにあるKustomize設定を変更します。

変更前が次の状態だったとします。

images:
  - name: blog-application
    newTag: 0.3.20

新しい0.3.21がHarborへpushされると、Image Updaterは次のように変更し、Forgejoへcommitします。

images:
  - name: blog-application
    newTag: 0.3.21

Argo CDはこのcommitを検知し、通常のGitOpsと同じ手順で同期します。

DeploymentのPod templateが変更され、新しいAstroイメージを使ったPodが起動します。readiness checkを通過すると、古いPodと入れ替わり、このブログの更新が本番へ反映されます。

つまり、Harborへ新しいタグをpushした後の流れは、次のようになります。

Harborへ0.3.21をpush

Image Updaterが検知

KustomizeのnewTagを更新

Forgejoへcommit

Argo CDが同期

Astroの新しいPodが起動

この仕組みにより、「現在、このブログがどのイメージで動いているか」は、Kubernetesクラスタの状態だけでなく、Gitの履歴からも確認できます。

この事例で分かったこと

Astroアプリを1つデプロイするだけなら、GitHub Actionsと外部レジストリを使った方が簡単です。

それでも自前で組んだことで、イメージが本番へ届くまでには、単にツールを並べるだけでは足りないことが分かりました。

BuildKitへどのように接続するのか。

Harborの認証情報はどこから渡されるのか。

Image Updaterはどのタグを新しいと判断するのか。

同じタグが上書きされないことを、どのように保証するのか。

実際にこのブログのAstroイメージを流してみると、問題になるのは各製品の機能ではなく、製品と製品の間にあるこうした「継ぎ目」でした。

現在は、Astroのコードを修正し、新しいバージョン番号でイメージをHarborへpushすれば、その後はGit履歴を残しながら本番Podまで届きます。

完全自動ではありませんが、自分が操作する範囲と、自動化に任せる範囲が明確なデプロイ経路になっています。

Astroを自前CI/CDでデプロイする――Forgejo、BuildKit、Harbor、Argo CDの実例
http://notes.midnightstops.com/posts/42/
作者
kairo
公開日
2026-07-18

関連記事