はじめに
小規模なWebサービスを、生成AIを使ってどこまで短時間で作れるか。今回はCodexを使い、占いサイトをゼロから制作して公開することを試した。
題材は、生年月日を入力すると今日の運勢が分かるサイト。占いの内容を作り込むこと以上に、設計・実装・公開・運用までを一通りつなげることを目的にした。
EXP-001で作ったOCIの公開基盤と、EXP-002で扱った計測・検索の設定を、実際のサービスへ広げていく。
公開後の確認まで。
開発環境
作業環境はWindowsとVS Code。Codexを使って実装や設定ファイルを作成し、内容を確認しながら進めた。ソースコードはGitで管理し、GitHubへ送る。
フレームワークにはNext.js、言語にはTypeScript、スタイリングにはTailwind CSSを使った。画面の実装だけでなく、静的ファイルとして出力してサーバーへ配置するところまでを同じ開発の流れに含めた。
占いサイトを作る
Next.js App Routerを使い、モバイルファーストで画面を組んだ。深い紫を基調とし、サイトの雰囲気に合わせたオリジナルキャラクターも作成した。
最初に実装した機能は「今日の運勢」。生年月日と日付をもとに、その日の結果を決定する仕組みにした。同じ生年月日・同じ日付なら結果が同じになるため、ページを開くたびに運勢が変わる構成ではない。
占い結果の決定に外部APIやDBは使わず、静的構成に収めた。Codexは制作に使い、公開後にアクセスのたびに生成AIへ占いを依頼する仕組みにはしていない。
公開構成
Next.jsのstatic exportで出力したファイルを、OCI Compute上のnginx:alpineで配信する。手前にはCaddyを置き、Cloudflareを経由してサブドメインで公開した。
ブラウザ
↓ HTTPS
Cloudflare(Proxy ON)
↓
OCI Compute
Caddy
├─ midnightbench.com → main:80
└─ fortune.midnightbench.com → fortune:80
nginx:alpine
└─ 静的ファイル(out)
ビルド時にはNode.jsを使うが、公開時には生成済みのファイルをnginxから返す。Caddyがホスト名を見て、Midnight Bench本体と占いサイトへリクエストを振り分ける。
Docker化する
Dockerはmulti-stage buildにした。builderステージではNode.jsを使い、npm ciで依存関係をインストールしてから、npm run buildを実行する。
Next.jsはstatic exportを使う設定にし、ビルドで生成されたoutディレクトリを配信用のnginx:alpineへコピーする。ビルド環境と配信環境を分け、配信側には静的ファイルを配置する構成にした。
OCI上では127.0.0.1:8081から表示を確認した。これはOCIホスト上で確認するための入口になる。
その後、fortuneをCaddyと同じDocker networkへ接続した。Caddyからの転送先はホスト側の8081番ポートではなく、同じネットワーク内のfortune:80になる。
サブドメインで振り分ける
Midnight Bench本体はmain:80、占いサイトはfortune:80へ転送する。既存の本体側の設定に、占いサイト用のホストを加える形だ。
今回の振り分けを表すCaddyfileの設定例は次のとおり。本体側のwwwも含めている。
midnightbench.com, www.midnightbench.com {
reverse_proxy main:80
}
fortune.midnightbench.com {
reverse_proxy fortune:80
}
サブドメインごとにreverse_proxyを分けることで、同じOCI上にあるサービスをそれぞれのURLで公開できた。
DNS・HTTPS・計測
Cloudflareにfortune.midnightbench.comのAレコードを追加し、OCIの公開IPアドレスへ向けた。ProxyはONにして、HTTPSでアクセスできることを確認した。
DNSで接続先を指定する作業と、Caddyで転送先を指定する作業は、それぞれ必要になる。名前が引けることだけでなく、目的のサイトが表示されるところまで確認した。
公開後のアクセスを確認するため、Cloudflare Web Analyticsも設定した。まず計測できる状態を用意し、今後の改善でデータを見ていく。
mainへのpushで自動デプロイ
更新のたびに手動でOCIへ接続する代わりに、GitHub ActionsからSSHでデプロイするようにした。起点は占いサイトのリポジトリのmainブランチへのpush。
接続にはappleboy/ssh-actionを使い、GitHub SecretsにOCI_HOST、OCI_USER、OCI_SSH_KEYを登録した。
今回の実行内容に沿ったworkflow例。OCI側の配置先は/opt/fortuneで、ソースを更新してからDockerイメージを再ビルドし、コンテナを更新する。
name: Deploy Fortune
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to OCI
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.OCI_HOST }}
username: ${{ secrets.OCI_USER }}
key: ${{ secrets.OCI_SSH_KEY }}
script: |
cd /opt/fortune
git pull origin main
docker compose up -d --build
流れは「ローカルで変更 → GitHubへpush → ActionsからOCIへSSH → ソース更新 → ビルド・起動」。--buildを付けて、静的ファイルを含むイメージを更新する構成にした。
公開後の検索設定
ページのmetadataとOGPを整え、robots.txtとsitemap.xmlを用意した。サイトの内容やURLを、検索エンジンやリンク共有先へ伝えるための設定だ。
Google Search Consoleではsitemapを送信し、トップページと今日の運勢ページのインデックス登録をリクエストした。
ここで行ったのは送信と登録リクエストまで。実際のインデックス状況や検索からの流入は、公開後にSearch Consoleで確認していく。
やってみて分かったこと
Codexは画面のコード生成だけでなく、DockerやGitHub Actions、各種設定ファイルの作成にも使えた。実装から公開準備まで、作業を続けて進められた点が今回の収穫だった。
一方で、どの構成で公開するかを決めること、必要な権限を用意すること、DNSを設定すること、インフラの状態を確認することは人間が担当した。生成された設定も、配置先や接続先が今回の環境に合っているか確認する必要があった。
人間が確認しながら進める。
完全に放置して完成を待つより、この進め方が現実的だった。今回のように範囲を絞った小規模サービスなら、設計から公開までを短いサイクルで回せると感じた。
今後
まずは今日の運勢を公開した。今後は恋愛・仕事・お金の占いとタロットを追加し、コンテンツを増やしていきたい。
公開して終わりにせず、アクセス解析を見ながら、どのページが読まれているか、どこを改善するかを考える。今回つないだ自動デプロイの流れを使い、小さな更新を重ねていく。