Skip to content

サーバー構成

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.comBasic認証
管理者・運用者repo.suubutsuseminar.comDocker registry token認証
管理者・運用者grafana.suubutsuseminar.comGrafanaログイン

本番アプリだけを一般利用者向けに公開します。 staging, private registry, Grafana は運用者向けのエンドポイントとして制限をかけます。

分類使用しているもの用途
サーバーConoHa VPS / Debian 11アプリケーション, DB, registry, 監視基盤を動かすホスト
デプロイKamalDocker image の build, push, pull, container 起動, reverse proxy 設定
リバースプロキシkamal-proxyHTTPS終端, host routing, health check
コンテナ実行基盤DockerRails app, PostgreSQL, registry, 監視系サービスの実行
アプリケーションRails app container本番・staging のアプリケーション
DBPostgreSQL 18production / staging の primary DB と cache DB
ファイル永続化Docker volume/app/storage と PostgreSQL data directory の永続化
private registryDocker Registry v2 / docker-auth / nginxKamal が使う image registry と認証
ログDocker journald logging driverRails container のログを journald に出力
ログ収集Fluent Bitjournald からログを収集して Loki に送信
ログ保存Lokiアプリ・ホストログの集約
可視化GrafanaLoki のログ閲覧
開発ドキュメントAstro Starlightdocs サイト
docsホスティングCloudflare Pages開発ドキュメントの公開
ドメイン役割公開先
guide.suubutsuseminar.com本番アプリrs-textbook-guide
guide-stg.suubutsuseminar.comstagingアプリrs-textbook-guide-staging
repo.suubutsuseminar.comprivate Docker registryregistry / docker-auth-proxy
grafana.suubutsuseminar.comGrafanagrafana

Kamal の proxy.ssl: true により、各ドメインは kamal-proxy で HTTPS 終端します。 Rails app 側には forward_headers: true でリバースプロキシ経由のヘッダーを渡します。

項目
Kamal servicers-textbook-guide
imagedaichi-629/rs-textbook-guide
URLhttps://guide.suubutsuseminar.com
Basic認証無効
DB hostrs-textbook-guide-postgres
DB port5432
primary DBrs_textbook_guide_production
cache DBrs_textbook_guide_production_cache
storage volumers_textbook_guide_storage:/app/storage
DB volumedata:/var/lib/postgresql
項目
Kamal servicers-textbook-guide-staging
imagedaichi-629/rs-textbook-guide-staging
URLhttps://guide-stg.suubutsuseminar.com
Basic認証有効
DB hostrs-textbook-guide-staging-postgres-staging
DB port5433
primary DBrs_textbook_guide_staging
cache DBrs_textbook_guide_staging_cache
storage volumers_textbook_guide_storage-staging:/app/storage
DB volumedata-staging:/var/lib/postgresql

staging では BASIC_AUTH_ENABLED=true を設定し、BASIC_AUTH_USERNAMEBASIC_AUTH_PASSWORD を secret として渡します。

永続化が必要なデータは Docker volume に保存します。

データvolume用途
本番DBdataPostgreSQL production data
staging DBdata-stagingPostgreSQL staging data
本番アップロードファイルrs_textbook_guide_storageActive Storage local disk
stagingアップロードファイルrs_textbook_guide_storage-stagingActive Storage local disk
registry imagedocker-registryprivate registry の image data
Loki dataloki-dataログ保存
Fluent Bit statefluentbit-statejournald cursor / buffer
Grafana datagrafana-dataGrafana 設定・状態

外部からの 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 の /uprails/health#show に割り当てています。

Kamal の共通設定では registry として repo.suubutsuseminar.com を使用します。 registry は config/deploy.registry.yml で管理します。

構成は以下です。

  • registry: Docker Registry v2。image data は docker-registry volume に保存
  • docker-auth: token 認証サーバー。/etc/registry-auth の証明書と鍵を使用
  • docker-auth-proxy: nginx。repo.suubutsuseminar.com/authdocker-auth に転送

Kamal が image を push / pull するため、config/deploy.yml では registry user として admin を使い、password は KAMAL_REGISTRY_PASSWORD secret から渡します。

監視系は config/deploy.observability.yml で管理します。

serviceimage役割
lokigrafana/loki:3.4.1ログ集約
fluent-bitfluent/fluent-bit:4.2.2journald からログ収集
grafanagrafana/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 に設定しています。

Kamal には clear env と secret env を分けて渡します。

共通 secret:

  • RAILS_MASTER_KEY
  • POSTGRES_PASSWORD
  • KAMAL_REGISTRY_PASSWORD

staging のみ使う secret:

  • BASIC_AUTH_USERNAME
  • BASIC_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"] を参照します。

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 で公開します。

ファイル内容
config/deploy.ymlKamal 共通設定
config/deploy.staging.ymlstaging app / DB / Basic認証
config/deploy.production.ymlproduction app / DB
config/deploy.registry.ymlprivate Docker registry
config/deploy.observability.ymlLoki / Fluent Bit / Grafana
config/observability/*Loki, Fluent Bit, Grafana設定
config/registry/*registry, docker-auth設定
.github/workflows/deploy-staging.ymlstaging deploy workflow
.github/workflows/deploy-production.ymlproduction deploy workflow
.github/workflows/deploy-docs.ymldocs 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.rbforce_sslassume_ssl はコメントアウトされています。HTTPS 終端は Kamal proxy 側で行います。