サーバー構成
サーバー構成
Section titled “サーバー構成”rs-textbook-guide の本番・staging 環境は、1台の VPS 上で Docker コンテナとして動かしています。 デプロイとコンテナ管理には Kamal を使用します。
flowchart TB general_user[一般利用者] -->|HTTPS| proxy[kamal-proxy] operator[管理者・運用者] -->|HTTPS| proxy proxy -->|guide.suubutsuseminar.com / public| app_prod[Rails app production] proxy -->|guide-stg.suubutsuseminar.com / restricted| app_stg[Rails app staging] proxy -->|repo.suubutsuseminar.com / restricted| registry[Docker Registry] proxy -->|repo.suubutsuseminar.com/auth / restricted| auth_proxy[docker-auth-proxy] proxy -->|grafana.suubutsuseminar.com / restricted| grafana[Grafana] app_prod --> db_prod[(PostgreSQL production)] app_stg --> db_stg[(PostgreSQL staging)] app_prod --> storage_prod[(Active Storage volume)] app_stg --> storage_stg[(Active Storage volume)] auth_proxy --> docker_auth[docker-auth] registry --> registry_data[(registry volume)] fluent[Fluent Bit] --> loki[Loki] grafana --> loki journald[(journald)] --> fluent
ここでの利用者は、Railsアプリ内の権限ではなく、サーバー上で公開しているエンドポイントにアクセスする人を指します。
| 利用者 | 主なアクセス先 | アクセス制限 |
|---|---|---|
| 一般利用者 | guide.suubutsuseminar.com | 制限なし |
| 管理者・運用者 | guide-stg.suubutsuseminar.com | Basic認証 |
| 管理者・運用者 | repo.suubutsuseminar.com | Docker registry token認証 |
| 管理者・運用者 | grafana.suubutsuseminar.com | Grafanaログイン |
本番アプリだけを一般利用者向けに公開します。 staging, private registry, Grafana は運用者向けのエンドポイントとして制限をかけます。
使用しているもの
Section titled “使用しているもの”| 分類 | 使用しているもの | 用途 |
|---|---|---|
| サーバー | ConoHa VPS / Debian 11 | アプリケーション, DB, registry, 監視基盤を動かすホスト |
| デプロイ | Kamal | Docker image の build, push, pull, container 起動, reverse proxy 設定 |
| リバースプロキシ | kamal-proxy | HTTPS終端, host routing, health check |
| コンテナ実行基盤 | Docker | Rails app, PostgreSQL, registry, 監視系サービスの実行 |
| アプリケーション | Rails app container | 本番・staging のアプリケーション |
| DB | PostgreSQL 18 | production / staging の primary DB と cache DB |
| ファイル永続化 | Docker volume | /app/storage と PostgreSQL data directory の永続化 |
| private registry | Docker Registry v2 / docker-auth / nginx | Kamal が使う image registry と認証 |
| ログ | Docker journald logging driver | Rails container のログを journald に出力 |
| ログ収集 | Fluent Bit | journald からログを収集して Loki に送信 |
| ログ保存 | Loki | アプリ・ホストログの集約 |
| 可視化 | Grafana | Loki のログ閲覧 |
| 開発ドキュメント | Astro Starlight | docs サイト |
| docsホスティング | Cloudflare Pages | 開発ドキュメントの公開 |
ホストとドメイン
Section titled “ホストとドメイン”| ドメイン | 役割 | 公開先 |
|---|---|---|
guide.suubutsuseminar.com | 本番アプリ | rs-textbook-guide |
guide-stg.suubutsuseminar.com | stagingアプリ | rs-textbook-guide-staging |
repo.suubutsuseminar.com | private Docker registry | registry / docker-auth-proxy |
grafana.suubutsuseminar.com | Grafana | grafana |
Kamal の proxy.ssl: true により、各ドメインは kamal-proxy で HTTPS 終端します。
Rails app 側には forward_headers: true でリバースプロキシ経由のヘッダーを渡します。
| 項目 | 値 |
|---|---|
| Kamal service | rs-textbook-guide |
| image | daichi-629/rs-textbook-guide |
| URL | https://guide.suubutsuseminar.com |
| Basic認証 | 無効 |
| DB host | rs-textbook-guide-postgres |
| DB port | 5432 |
| primary DB | rs_textbook_guide_production |
| cache DB | rs_textbook_guide_production_cache |
| storage volume | rs_textbook_guide_storage:/app/storage |
| DB volume | data:/var/lib/postgresql |
staging環境
Section titled “staging環境”| 項目 | 値 |
|---|---|
| Kamal service | rs-textbook-guide-staging |
| image | daichi-629/rs-textbook-guide-staging |
| URL | https://guide-stg.suubutsuseminar.com |
| Basic認証 | 有効 |
| DB host | rs-textbook-guide-staging-postgres-staging |
| DB port | 5433 |
| primary DB | rs_textbook_guide_staging |
| cache DB | rs_textbook_guide_staging_cache |
| storage volume | rs_textbook_guide_storage-staging:/app/storage |
| DB volume | data-staging:/var/lib/postgresql |
staging では BASIC_AUTH_ENABLED=true を設定し、BASIC_AUTH_USERNAME と BASIC_AUTH_PASSWORD を secret として渡します。
データ永続化
Section titled “データ永続化”永続化が必要なデータは Docker volume に保存します。
| データ | volume | 用途 |
|---|---|---|
| 本番DB | data | PostgreSQL production data |
| staging DB | data-staging | PostgreSQL staging data |
| 本番アップロードファイル | rs_textbook_guide_storage | Active Storage local disk |
| stagingアップロードファイル | rs_textbook_guide_storage-staging | Active Storage local disk |
| registry image | docker-registry | private registry の image data |
| Loki data | loki-data | ログ保存 |
| Fluent Bit state | fluentbit-state | journald cursor / buffer |
| Grafana data | grafana-data | Grafana 設定・状態 |
ネットワークとプロキシ
Section titled “ネットワークとプロキシ”外部からの HTTP/HTTPS は kamal-proxy が受けます。
アプリ・registry・Grafana は直接外部公開せず、Kamal proxy の host routing で公開します。
health check は以下を使用します。
| 対象 | path |
|---|---|
| Rails app | /up |
| registry | / |
| docker-auth-proxy | /up |
| Grafana | /api/health |
Rails の /up は rails/health#show に割り当てています。
private registry
Section titled “private registry”Kamal の共通設定では registry として repo.suubutsuseminar.com を使用します。
registry は config/deploy.registry.yml で管理します。
構成は以下です。
registry: Docker Registry v2。image data はdocker-registryvolume に保存docker-auth: token 認証サーバー。/etc/registry-authの証明書と鍵を使用docker-auth-proxy: nginx。repo.suubutsuseminar.com/authをdocker-authに転送
Kamal が image を push / pull するため、config/deploy.yml では registry user として admin を使い、password は KAMAL_REGISTRY_PASSWORD secret から渡します。
監視系は config/deploy.observability.yml で管理します。
| service | image | 役割 |
|---|---|---|
loki | grafana/loki:3.4.1 | ログ集約 |
fluent-bit | fluent/fluent-bit:4.2.2 | journald からログ収集 |
grafana | grafana/grafana:12.3.1 | ログ可視化 |
Rails container は Docker の journald logging driver を使い、tag: rails-web を付けます。
Fluent Bit は以下を収集します。
SYSLOG_IDENTIFIER=rails-webの Rails container ログ- VPS の
ssh.serviceログ SYSLOG_IDENTIFIER=sudoの sudo ログ
Fluent Bit は Loki の rs-textbook-guide-observability-loki:3100 にログを送信します。
Grafana の datasource は Loki を default に設定しています。
secrets と環境変数
Section titled “secrets と環境変数”Kamal には clear env と secret env を分けて渡します。
共通 secret:
RAILS_MASTER_KEYPOSTGRES_PASSWORDKAMAL_REGISTRY_PASSWORD
staging のみ使う secret:
BASIC_AUTH_USERNAMEBASIC_AUTH_PASSWORD
監視で使う secret:
GF_SECURITY_ADMIN_PASSWORD
server IP は Rails credentials の kamal.server_ip から取得し、各 script で KAMAL_SERVER_IP_ADDRESS として Kamal に渡します。
そのため config/deploy*.yml の host は ENV["KAMAL_SERVER_IP_ADDRESS"] を参照します。
デプロイフロー
Section titled “デプロイフロー”staging は main branch への push または手動実行で GitHub Actions からデプロイされます。
production は GitHub Actions の手動実行でデプロイします。
sequenceDiagram participant Dev as Developer participant GH as GitHub Actions participant Kamal as Kamal participant Reg as repo.suubutsuseminar.com participant VPS as VPS Dev->>GH: push main / workflow_dispatch GH->>GH: checkout, Ruby setup, SSH setup, Docker Buildx setup GH->>Kamal: deploy script Kamal->>Reg: build and push image Kamal->>VPS: SSH deploy VPS->>Reg: pull image VPS->>VPS: start app container and accessories VPS->>VPS: run db:prepare on Rails server boot
実行スクリプトは以下です。
| 対象 | 初回 | 2回目以降 |
|---|---|---|
| staging | ./setup_staging.sh | ./deploy_staging.sh |
| production | ./setup_production.sh | ./deploy_production.sh |
docs はアプリ本体とは別に Cloudflare Pages へ deploy します。
docs/** に変更が入り main に push されると、docs directory で pnpm run build を実行し、wrangler pages deploy dist で公開します。
設定ファイルの対応表
Section titled “設定ファイルの対応表”| ファイル | 内容 |
|---|---|
config/deploy.yml | Kamal 共通設定 |
config/deploy.staging.yml | staging app / DB / Basic認証 |
config/deploy.production.yml | production app / DB |
config/deploy.registry.yml | private Docker registry |
config/deploy.observability.yml | Loki / Fluent Bit / Grafana |
config/observability/* | Loki, Fluent Bit, Grafana設定 |
config/registry/* | registry, docker-auth設定 |
.github/workflows/deploy-staging.yml | staging deploy workflow |
.github/workflows/deploy-production.yml | production deploy workflow |
.github/workflows/deploy-docs.yml | docs deploy workflow |
- production / staging は同じ VPS 上で動きますが、service名, DB名, DB port, volume を分けています。
- Active Storage は local disk なので、
/app/storageの volume が消えるとアップロードファイルも失われます。 config/cable.ymlの production は Redis adapter を参照していますが、現在の Kamal accessory には Redis service が定義されていません。Action Cable を本番で使う場合は Redis の追加が必要です。config/environments/production.rbのforce_sslとassume_sslはコメントアウトされています。HTTPS 終端は Kamal proxy 側で行います。