はじめに
チャットツール「Zulip」をAWS上で構築しました。
「スポットフリートと起動テンプレート」 を活用して極限までコストを抑えつつ、設定をコード化(IaC)することで、インスタンスが入れ替わっても自動復旧できる基盤を目指しました。
本記事は、その構築過程と直面した技術的な課題、その解決策のログです。
1. システム構成
コンピュートリソースを「使い捨て」と割り切り、データと設定を外部化するアーキテクチャです。
- Compute: Spot Fleet (起動テンプレート利用)
- OS/Runtime: Ubuntu / Docker & Docker Compose
- Storage (File): Amazon S3 (画像・アップロードファイル)
- Storage (DB): EC2 Local Volume (※現時点ではインスタンス依存)
- Network: VPC (Public Subnets / Multi-AZ設計)
- Mail: Amazon SES (SMTPインターフェース)
- Security: IAM User Access Keys (環境変数による注入)
2. 実装のポイントと技術的ハマりどころ
① 起動テンプレート × スポットフリートによる自動化
単発のEC2インスタンスではなく、**「起動テンプレート」にあらかじめ設定(AMI, インスタンスタイプ, UserData等)を定義し、「スポットフリート」**でリクエストする構成としました。
これにより、スポットインスタンスが中断(回収)されても、自動的に事前定義された設定で次のサーバーが立ち上がる仕組みを構築しました。
② S3へのファイル保存とDocker設定 (IaC)
Zulipの画像保存先をS3にする際、サーバー内の設定ファイル (settings.py) を手動編集するのではなく、docker-compose.yml の環境変数で制御する方法を採用しました。
ハマりポイント:自動判定が効かない
単純なバケット指定だけではS3モードが有効になりませんでした。Zulipの仕様上、環境変数での注入タイミングでは内部の自動判定ロジックが期待通りに動かないため、以下の変数を明示的に指定する必要がありました。
environment:
# ローカル保存を無効化 ("None"という文字列を渡す)
SETTING_LOCAL_UPLOADS_DIR: "None"
# S3モードを強制ONにするスイッチ
SETTING_S3_AUTH_UPLOADS: "True"
# URL生成パターンを手動定義 (※これが重要。ないとリンク切れになる)
SETTING_S3_KEY_PATTERN: "%s/%s"
# バケット情報
SETTING_S3_BUCKET: "my-zulip-bucket"
SETTING_S3_AUTH_UPLOADS_BUCKET: "my-zulip-bucket"
SETTING_S3_AVATAR_BUCKET: "my-zulip-bucket"
③ DockerコンテナからのS3認証(アクセスキー注入)
EC2にIAMロールを付与する方法も検討しましたが、Dockerコンテナ内からのメタデータアクセス(IMDSv2)にはネットワーク階層(ホップ数)の壁があり、設定が複雑化するため今回は回避しました。
確実性を優先し、IAMユーザーのアクセスキーを docker-compose.yml の環境変数(SECRETS_s3_key 等)として注入する方式 を採用しました。
これにより、Zulip内部の設定ファイル (zulip-secrets.conf) に自動的にキーがマッピングされ、即座にS3接続を確立できました。
④ Amazon SES によるメール配信
Zulipからの通知メール(招待やメンション)を確実に届けるため、Amazon SES をSMTPサーバーとして利用しました。
-
設定:
email-smtp.ap-northeast-1.amazonaws.com - 認証: SES用のSMTP認証情報をSecretsとして注入
-
対策:
feedback-smtpなどのDNSレコードを設定し、到達率(Deliverability)を考慮
⑤ ネットワークとデータ永続化の課題
スポットインスタンスの在庫枯渇リスクに備え、VPCは東京リージョンの3つのAZ (1a, 1c, 1d) にサブネットを展開しました。
注意点:EBSはAZを跨げない
現在はDBがインスタンス内部(ローカルボリューム)にあるため、どのAZでも自由に起動できます。
しかし、将来的に**「EBSボリュームをマウントしてDBを永続化」**する場合、「EBSは作成されたAZの中でしか使えない」という制約が課題になります。
その際は、以下のトレードオフ判断が必要になります。
- 起動AZを固定する: 可用性は下がるが、EBSを使い回せる(コスト低)。
- Amazon EFSを利用する: どのAZからもマウントできるが、EBSよりコストが高い。
まとめ
起動テンプレートとスポットフリートを組み合わせることで、**「サーバーはいつ消えてもいい箱」**として扱う運用を実現しました。
DockerとAWSのマネージドサービス(S3, SES)をコードベース (docker-compose.yml) で正しく連携させることで、低コストかつ実用的なチャット基盤が構築できました。