TrayPage Docs

AWSでセルフホストする

自社のAWSアカウントでTrayPageを動かす2つの構成(EC2 1台・Fargate + RDS)の違いと、手順の要点。

自社のAWSアカウントにデータを置いてTrayPageを動かす方法です。構成は2つあります。どちらも、構築用のテンプレート(CloudFormationまたはTerraform)がリポジトリの deploy/aws/ にあります。このページでは判断材料と手順の要点を説明し、パラメータや変数の一覧、詳しいコマンドはそれぞれのREADMEに書いています。

2つの構成の違い

最小構成(EC2 1台)標準構成(Fargate + RDS)
構築方法CloudFormation(deploy/aws/cloudformation)Terraform(deploy/aws/terraform)
向いている用途試したい。小さく使う。止まってよい用途本番運用。止まると困る用途
構成EC2 1台でCaddy・アプリ・Postgresを動かすALBの背後でFargateのタスク(既定2つ)を動かし、DBはRDS
冗長性なし(サーバーが1台)複数のAZに分散。RDSはMulti-AZ(既定)
DBのバックアップEBSの日次スナップショット(既定14世代)と、毎日の pg_dumpRDSの自動バックアップ(既定7日)
運用の手間中(更新はインスタンスに入って行う)中〜大(Terraformの管理が必要)
費用の目安月約$25既定の構成で月約$200、小さい構成で月約$125

最小構成の費用は、東京リージョンの既定の構成での目安です。リージョンやインスタンスタイプで変わります。主な内訳は、EC2(t4g.small)が約$16、EBS 40GiBが約$4、Elastic IP(パブリックIPv4)が約$3.6で、S3とバックアップが少額です。

標準構成の費用は、東京リージョン(ap-northeast-1)、オンデマンド料金での概算です。データ転送やリクエスト数で変わります。内訳は次のとおりです。

  • NAT Gatewayが約$45です。外向きの通信(イメージの取得、メールの送信など)に使います。
  • RDSをMulti-AZにすると、インスタンスとストレージの料金がちょうど2倍になります。
  • 小さい構成は、Fargateを1タスク、RDSを db.t4g.micro のSingle-AZにしたものです。検証用で、冗長性はありません。

正確な金額は AWS Pricing Calculator で確認してください。

迷ったときは、まず最小構成で試し、本番運用に移すときに標準構成を作る、という順序が目安です。

共通の前提

どちらの構成でも、次のものを用意します。

  • ドメイン(ホスト名)を2つ: アプリ用と閲覧用です。閲覧用は任意のHTMLを配信するため、アプリ用とは別のホスト名にする必要があります。可能なら別のドメインにしてください(例: traypage.example.com と traypage-content.example.net)。理由は セキュリティ上の注意 を参照してください。
  • メールの送信手段: ログインはメールのリンクで行います。SES(SMTP)かResendを使えます。SESは、送信元ドメインの検証と、本番アクセスの申請(sandboxの解除)が必要です。解除されるまでは、SESで検証したアドレスにしかログインのメールが届きません。
  • AWSアカウントと権限: 最小構成ではCloudFormation・EC2・IAM・S3、標準構成ではVPC・ECS・RDS・S3・IAM・Secrets Manager・SES・ACM・Route 53を作れる権限が要ります。
  • ACM証明書(標準構成): 2つのホスト名を含む証明書をALBで使います。Route 53のhosted zoneがあれば、DNSによる検証も含めてTerraformが作ります。hosted zoneがなければ、ALBと同じリージョンで証明書を用意し、certificate_arn で渡します。この場合、DNSはALBに向けて自分で設定します。

最小構成の証明書は、DNSのAレコードを向けたあとで、Caddyが2つのホスト名についてLet's Encryptから自動で取得します。

最小構成(EC2 1台)の要点

  1. CloudFormationのスタックを作ります。AppDomain・ViewerDomain とメールの設定を指定します。AWSのコンソールのほか、CLIでも作れます。
  2. スタックが完了したら、出力の ElasticIp に、2つのホスト名のAレコードを向けます。
  3. https://<アプリ用ホスト>/health が応答し、ログインのメールが届くことを確認します。初回の起動には数分かかります。マイグレーションは、アプリが起動時に自動で適用します。

セキュリティグループは80番と443番だけを開けます。SSHは使わず、サーバーに入るときはSSM Session Managerを使います。

バックアップ・更新・削除(最小構成)

  • バックアップ: DBのデータはEBSに置かれ、日次スナップショットが自動で取られます。加えて、毎日 pg_dump がバックアップ用のS3バケットに保存されます。ページ本文はS3のバージョニングで保護されます。
  • 更新: TraypageImage を変えてスタックを更新しても、起動スクリプトは初回しか動かないため反映されません。インスタンスに入って、.env のイメージのタグを書き換え、docker compose pull と up -d を実行します。タグは v1.2.3 のように固定してください。
  • 削除: スタックを消しても、2つのS3バケットは残ります。中身を確認してから手で消してください。データ用のEBSも、削除時にスナップショットが残ります。

前段にCloudFrontやロードバランサを置く場合

認証の操作には、接続元のIPアドレスごとの回数の上限がかかります。EC2の前にCloudFrontなどを足すと、全員が同じIPとして数えられて、まとめて上限に当たります。その場合は、Caddyの trusted_proxies の設定が必要です。詳しくは 設定項目のリファレンス を参照してください。

標準構成(Fargate + RDS)の要点

  1. 前提を用意します(Terraform 1.5以降、AWSの認証情報、ホスト名2つ、DNSと証明書、SESの送信ドメイン)。
  2. 同梱の例(examples/standard)で変数ファイルを作ります。必須の変数は、image_tag・app_hostname・viewer_hostname・email_from です。Route 53を使うなら route53_zone_id も指定します。
  3. terraform init、terraform plan、terraform apply の順に実行します。初回は20〜30分ほどかかります。stateにはDBのパスワードなどが入るため、本番では暗号化したS3のbackendに置いてください。
  4. SESのsandboxを解除し、/health とログイン、MCPでの接続を確認します。

DBのテーブルは、アプリが起動時に自動で作成・更新します。Terraformが作るのは空のDBとユーザーだけで、デプロイにDBの作業はありません。マイグレーションに失敗したタスクは起動せず、ECSが前のタスク定義に戻します。

更新・バックアップ・削除(標準構成)

  • 更新: image_tag を新しいバージョンにして terraform apply します。ECSが、新しいタスクが正常になってから古いタスクを止める形で入れ替えます。更新の前にRDSのスナップショットを取ってください。 マイグレーションは前に戻せないので、古いバージョンに戻すときはスナップショットからDBを戻します。
  • バックアップ: RDSの自動バックアップ(既定7日)と、手動のスナップショットを使います。ページ本文はS3のバージョニングで保護されます。
  • 削除: ALBとRDSには削除保護があります。外してから terraform destroy します。RDSは削除時に最終スナップショットが残ります。S3のバケットは中身があると削除に失敗します。中身ごと消してよい場合だけ、設定を変えて先に適用してください。

S3の認証にキーを置かない

どちらの構成でも、ページ本文を置くS3にはアクセスキーを使いません。EC2のインスタンスロール、またはFargateのタスクロールに、そのバケットだけの権限を付けます。アプリはロールの一時認証情報を取得してS3にアクセスします。環境変数 S3_USE_IAM_ROLE=1 で有効になり、テンプレートが自動で設定します。サーバーにシークレットを置く必要はありません。

標準構成でSESのSMTPを使う場合だけは、SESの仕様上、IAMユーザーのキーから作るSMTPパスワードが必要です。Terraformが、メール送信だけを許したIAMユーザーを作り、パスワードをSecrets Manager経由でアプリに渡します。

関連する設定

  • 接続元のIPを読むヘッダー(TRUSTED_CLIENT_IP_HEADER)、メール、ストレージなどの設定項目は 設定項目のリファレンス を参照してください。最小構成では、TRUSTED_CLIENT_IP_HEADER はテンプレートが設定します。
  • ライセンスキーは ライセンスとEnterprise機能 を参照してください。
  • 構成の比較は セルフホスト を参照してください。
  • AWSのテンプレートを使わず、サーバー1台で動かす場合は Docker / VPS を使えます。PostgresにRDS、ページ本文の置き場所にS3を指定することもできます(外部のPostgresとS3を使う)。