CaddyとCloudflarePagesで複数サイトを1レポジトリでデプロイ
都知事杯ハッカソン2023でファイナルステージに進んだ、BuTTERというツールを運営しています。BuTTERでは、別ドメインの3つのサイトを1つのレポジトリで管理することで効率的な開発・運用を行っており、これら3つのサイトは一括でCloudflare Pagesにてホストし、各ドメインへのアクセスをCaddy Proxyにより振り分けています。この記事では、その運用方法を共有させていただきます。
運用しているサイト
BuTTERでは、以下のような3つのサイトを運用しています。
URL | 用途 |
---|---|
https://butter.takoyaki3.com | BuTTER全体の紹介 |
https://tag-maker.butter.takoyaki3.com | Butterタグを作成するためのページ |
https://timetable.butter.takoyaki3.com | BuTTERを使った時刻表サイト |
アーキテクチャと設計方針
これらは1つのGitレポジトリでコードを管理しており、CloudflareのPagesに最新のproductブランチがデプロイされる仕組みを構築しています。
このレポジトリは関係するこれら3サイト部分のみ取り出すと以下のような構成となっており、それぞれのサイトは次のようなURLでCloudflarePageでホストされているサイトに直接アクセスすることもできます。
URL | CloudflarePagesへ直接アクセスする場合のURL | 用途 |
---|---|---|
https://butter.takoyaki3.com | https://butter.pages.dev/ | BuTTER全体の紹介 |
https://tag-maker.butter.takoyaki3.com | https://butter.pages.dev/tag-maker | Butterタグを作成するためのページ |
https://timetable.butter.takoyaki3.com | https://butter.pages.dev/timetable | BuTTERを使った時刻表サイト |
レポジトリにおけるフォルダ構成
このレポジトリには、サイトごとに独立したフォルダがあります。
├── about
│ ├── babel.config.js
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── jsconfig.json
│ ├── package.json
│ ├── package-lock.json
│ ├── public
│ ├── README.md
│ ├── src
│ └── vue.config.js
├── tag-maker
│ ├── babel.config.js
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── jsconfig.json
│ ├── package.json
│ ├── package-lock.json
│ ├── public
│ ├── README.md
│ ├── src
│ └── vue.config.js
└── timetable-app
├── babel.config.js
├── docker-compose.yml
├── Dockerfile
├── jsconfig.json
├── package.json
├── package-lock.json
├── public
├── README.md
├── src
└── vue.config.js
Caddyによるリバースプロキシの設定
これらは、*.butter.takoyaki3.comへのアクセスをすべてDNS設定によりCaddyがインストールされたVPSに割り当てており、Caddyのリバースプロキシ機能によりCloudflarePagesに転送しています。Caddyfileの各設定は以下のようになっています。ここでは、header_upを使ってリバースプロキシ先のヘッダを書き換え、rewrite指令でURLの書き換えも行っています。なお、 https://butter.takoyaki3.com/v0.0.0/root.json というROOTファイルのみ頻繁に自動更新されるため、Pagesとは別のホスト先に転送する設定を施しています。
timetable.butter.takoyaki3.com {
rewrite * /timetable{uri}
reverse_proxy https://butter.pages.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
log {
output file /log/timetable.butter.takoyaki3.com.log {
roll_size 10MiB
roll_keep 128
roll_keep_days 30
roll_suffix .20060102-15
output_format pretty_json
}
}
}
tag-maker.butter.takoyaki3.com {
rewrite * /tag-maker{uri}
reverse_proxy https://butter.pages.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
log {
output file /log/tag-maker.butter.takoyaki3.com.log {
roll_size 10MiB
roll_keep 128
roll_keep_days 30
roll_suffix .20060102-15
output_format pretty_json
}
}
}
butter.takoyaki3.com {
handle /v0.0.0/root.json {
reverse_proxy https://pub-ad1f4a48b8ef46779b720e734b0c2e1d.r2.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
}
reverse_proxy https://butter.pages.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
log {
output file /log/butter.takoyaki3.com.log {
roll_size 10MiB
roll_keep 128
roll_keep_days 30
roll_suffix .20060102-15
output_format pretty_json
}
}
}
ビルド設定
このようなサイト構成でデプロイするために、ビルド時に、about
,tag-maker
,timetable-app
をそれぞれビルドする際に、ビルドしたディレクトリがそれぞれdist
,dist/tag-maker
,dist/timetable-app
になるように、ビルドしたファイルの保存先などを設定するvue.config.js
にそれぞれ設定しています。この設定により、ビルドされたファイルはそれぞれ適切な場所に保存されます。
aboutにおけるvue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
outputDir: '../dist/',
transpileDependencies: [
'vuetify'
]
})
tag-makerにおけるvue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
outputDir: '../dist/tag-maker',
transpileDependencies: [
'vuetify'
]
})
timetable-appにおけるvue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
outputDir: '../dist/timetable',
transpileDependencies: [
'vuetify'
]
})
自動ビルド設定
これらの設定を施した上で、package.json
内でnpm run build
した際にビルド用にbuild.js
が実行されるように指定し、build.js
では先ほどのabout
,tag-maker
,timetable-app
の3つのディレクトリを1つのコマンドでビルドするように設定しています。
package.jsonの関連する部分
{
"scripts": {
"build": "node build.js"
},
"dependencies": {
"butter-lib": "^1.0.10"
}
}
build.js
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
const dirs = ['./about', './timetable-app', './tag-maker']; // ビルドするディレクトリのリスト
const buildDir = async (dir) => {
try {
console.log(`依存パッケージのインストール開始: ${dir}`);
await execAsync('npm install', { cwd: dir });
console.log(`依存パッケージのインストール成功: ${dir}`);
console.log(`ビルド開始: ${dir}`);
await execAsync('npm run build', { cwd: dir });
console.log(`ビルド成功: ${dir}`);
} catch (err) {
console.error(`エラー: ${dir}`, err);
}
};
Promise.all(dirs.map(dir => buildDir(dir)))
.then(() => console.log('全てのビルドが完了しました'))
.catch(err => console.error('ビルド中にエラーが発生しました', err));
Cloudflare Pagesの設定
最後に、Cloudflare Pagesでdistディレクトリを公開するように設定しています。これにより、1つのレポジトリで複数のサイトを効率よく運用しています。
まとめ
このようにして、BuTTERは1つのレポジトリで複数のサイトを効率よく運用しています。このアーキテクチャを採用することで、開発のスピードと効率が大幅に向上しています。
Discussion