AWS と Rails で実践する Twelve-Factor App
terraform aws rails1. はじめに
近年、クラウドインフラストラクチャの発展と、アプリケーション開発の効率化が求められる中で、Twelve-Factor App の原則に基づいたアプリケーション設計が注目されています。Twelve-Factor App は、アプリケーションを構築する際の12の要素を定義したものであり、これらの原則に従うことで、スケーラブルで保守性の高いアプリケーションを開発することができます。
本記事では、AWS と Rails を使用して Twelve-Factor App の原則を実践する方法について詳しく解説します。また、GitHub を使用したコードベースの管理や CI/CD パイプラインの構築、Terraform を使用したインフラのコード化など、具体的な実装方法や考慮点についても説明します。
1.1 Twelve-Factor App とは
Twelve-Factor App は、Heroku の共同創設者である Adam Wiggins によって提唱された、クラウドネイティブなアプリケーション開発のための12の要素をまとめたものです。以下に、その12の要素を列挙します。
- コードベース
- 依存関係
- 設定
- バックエンドサービス
- ビルド、リリース、実行
- プロセス
- ポートバインディング
- 並行性
- 廃棄容易性
- 開発/本番一致
- ログ
- 管理プロセス
これらの要素に従ってアプリケーションを設計することで、クラウド環境に適した、スケーラブルで保守性の高いアプリケーションを開発することができます。
1.2 AWS と Rails を使用した Twelve-Factor App の実装
AWS は、クラウドインフラストラクチャを提供するサービスであり、Twelve-Factor App の原則を実践するために適したプラットフォームです。また、Rails は、Ruby で書かれた Web アプリケーションフレームワークであり、規約に基づいた開発により、生産性の高いアプリケーション開発が可能です。
2. コードベースと依存関係の管理
2.1 GitHub を使用した単一のコードベース管理
Twelve-Factor App では、アプリケーションのコードベースを単一のリポジトリで管理することが推奨されています。これにより、コードの追跡や変更履歴の管理が容易になります。GitHub を使用することで、コードベースの一元管理と、チーム内でのコラボレーションを効率的に行うことができます。
2.1.1 GitHub Actions を使用した CI/CD パイプラインの構築
GitHub Actions を使用することで、コードのビルド、テスト、デプロイを自動化するCI/CDパイプラインを構築できます。以下に、GitHub Actions を使用したCI/CDパイプラインの例を示すフローチャートを記述します。
2.1.2 ブランチ戦略とプルリクエストを使用した開発フロー
GitHub でのブランチ戦略とプルリクエストを活用することで、コードの変更を安全に管理できます。以下に、GitHub でのブランチ戦略とプルリクエストを使用した開発フローのシーケンス図を Mermaid で記述します。
2.2 Bundler を使用した明示的な依存関係管理
Twelve-Factor App では、アプリケーションの依存関係を明示的に宣言し、管理することが推奨されています。Ruby の場合、Bundler を使用することで、依存関係を管理できます。
2.2.1 Gemfile と Gemfile.lock の役割
Gemfile は、アプリケーションが依存する Ruby のライブラリ(Gem)を宣言するファイルです。Gemfile.lock は、インストールされた Gem のバージョンを固定するためのファイルです。この2つのファイルを使用することで、アプリケーションの依存関係を明示的に管理できます。
2.2.2 Docker を使用した開発環境の共通化
Docker を使用することで、開発環境を共通化し、チーム内で一貫した環境を維持できます。Dockerfile を作成し、アプリケーションの依存関係をコンテナ内にパッケージ化することで、開発環境のセットアップを簡素化できます。
2.2.3 AWS ECS を使用したコンテナデプロイの利点
AWS ECS(Elastic Container Service)を使用することで、Docker コンテナを簡単にデプロイ・管理できます。ECS では、コンテナのスケーリングやロードバランシングなどの機能を利用でき、アプリケーションの信頼性と可用性を向上させることができます。
3. 設定とバックエンドサービスの管理
3.1 環境変数を使用した設定の管理
Twelve-Factor App では、アプリケーションの設定を環境変数として管理することが推奨されています。これにより、設定の変更をデプロイプロセスから分離し、各環境(開発、ステージング、本番)に応じた設定を適用できます。
3.1.1 Terraform を使用した AWS Systems Manager パラメータストアの管理
AWS Systems Manager パラメータストアを使用することで、環境変数をセキュアに保存・管理できます。Terraform を使用してパラメータストアを管理することで、インフラストラクチャのコード化と、環境変数の一元管理を実現できます。
resource "aws_ssm_parameter" "database_url" {
name = "/production/database/url"
type = "SecureString"
value = "postgres://user:password@hostname:port/database"
}
3.1.2 Rails アプリケーションでの環境変数の利用方法
Rails アプリケーションでは、config/application.rb
や config/database.yml
などの設定ファイルで環境変数を利用できます。以下に、config/database.yml
で環境変数を使用する例を示します。
production:
url: <%= ENV['DATABASE_URL'] %>
3.2 サービスの疎結合化とバックエンドサービスの管理
Twelve-Factor App では、バックエンドサービス(データベース、キャッシュ、メッセージキューなど)を疎結合化することが推奨されています。これにより、サービスの交換や設定の変更を容易に行うことができます。
3.2.1 Terraform を使用した AWS RDS, ElastiCache, SQS の管理
Terraform を使用することで、AWS のバックエンドサービスをコードとして管理できます。以下に、Terraform を使用して RDS (PostgreSQL) を管理する例を示します。
resource "aws_db_instance" "postgres" {
engine = "postgres"
engine_version = "12.5"
instance_class = "db.t3.micro"
name = "mydb"
username = "myuser"
password = var.db_password
}
3.2.2 Rails アプリケーションからのバックエンドサービスへのアクセス方法
Rails アプリケーションからバックエンドサービスにアクセスする際は、環境変数を使用して接続情報を設定します。以下に、config/database.yml
で RDS への接続情報を設定する例を示します。
production:
adapter: postgresql
host: <%= ENV['RDS_HOSTNAME'] %>
port: <%= ENV['RDS_PORT'] %>
database: <%= ENV['RDS_DB_NAME'] %>
username: <%= ENV['RDS_USERNAME'] %>
password: <%= ENV['RDS_PASSWORD'] %>
5. ステートレスなプロセスと並行性の管理
5.1 ステートレスなプロセスの重要性
Twelve-Factor App では、アプリケーションのプロセスをステートレスにすることが推奨されています。ステートレスなプロセスは、水平スケーリングを容易にし、フォールトトレランス(システムや機器の一部が故障・停止しても、予備の系統に切り替えるなどして機能を保ち、正常に稼働させ続ける仕組み)を向上させます。
5.1.1 Terraform を使用した AWS Auto Scaling の管理
Terraform を使用することで、AWS Auto Scaling を管理し、アプリケーションのプロセスを自動的にスケーリングできます。以下に、Terraform を使用して ECS のサービスに Auto Scaling を設定する例を示します。
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = 4
min_capacity = 1
resource_id = "service/${aws_ecs_cluster.mycluster.name}/${aws_ecs_service.myservice.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "ecs_policy" {
name = "scale-based-on-cpu"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = 50.0
}
}
5.1.2 Rails アプリケーションでのステートレス設計の考慮点
Rails アプリケーションをステートレスに設計する際は、以下の点に注意が必要です。
- セッション情報をクライアント側(Cookie)または外部ストレージ(Redis など)に保存する
- ファイルアップロードなどの一時的なデータを S3 などの外部ストレージに保存する
- 非同期処理を利用し、リクエストのレスポンスタイムを短縮する
5.2 プロセスモデルを使用した並行性の管理
Twelve-Factor App では、アプリケーションの並行性をプロセスモデルで管理することが推奨されています。各プロセスは独立して実行され、必要に応じて水平にスケーリングできます。
5.2.1 Terraform を使用した AWS ECS, Fargate の管理
Terraform を使用することで、AWS ECS や Fargate を管理し、アプリケーションのプロセスを並行して実行できます。以下に、Terraform を使用して Fargate タスクを定義する例を示します。
resource "aws_ecs_task_definition" "myapp" {
family = "myapp"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = "myapp"
image = "myapp:latest"
cpu = 256
memory = 512
essential = true
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
}
])
}
5.2.2 Rails アプリケーションでの並行性の管理方法
Rails アプリケーションで並行性を管理する際は、以下の方法を検討できます。
- Sidekiq などのバックグラウンドジョブ処理ライブラリを使用する
- Active Job を使用して、ジョブをバックエンドサービス(Sidekiq, AWS SQS など)に委譲する
- Action Cable を使用して、WebSocket 接続を管理する
6. ポートバインディングとログ管理
6.1 サービスを公開するためのポートバインディング
Twelve-Factor App では、アプリケーションがポートバインディングを使用してサービスを公開することが推奨されています。これにより、アプリケーションとインフラストラクチャの関心を分離できます。
6.1.1 Terraform を使用した AWS ELB, ECS の管理
Terraform を使用することで、AWS ELB(Elastic Load Balancer)と ECS を管理し、アプリケーションのポートバインディングを設定できます。以下に、Terraform を使用して ELB と ECS サービスを設定する例を示します。
resource "aws_lb" "myapp" {
name = "myapp-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb.id]
subnets = aws_subnet.public.*.id
}
resource "aws_lb_target_group" "myapp" {
name = "myapp-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip"
}
resource "aws_lb_listener" "myapp" {
load_balancer_arn = aws_lb.myapp.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.myapp.arn
}
}
resource "aws_ecs_service" "myapp" {
name = "myapp"
cluster = aws_ecs_cluster.mycluster.id
task_definition = aws_ecs_task_definition.myapp.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
security_groups = [aws_security_group.ecs_tasks.id]
subnets = aws_subnet.private.*.id
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.myapp.arn
container_name = "myapp"
container_port = 80
}
}
6.1.2 Rails アプリケーションでのポートバインディングの設定方法
Rails アプリケーションでポートバインディングを設定する際は、以下の点に注意が必要です。
config/puma.rb
で、アプリケーションが Listen するポートを指定する- Dockerfile で、
EXPOSE
命令を使用して、コンテナ外部に公開するポートを指定する
6.2 ログをイベントストリームとして扱う
Twelve-Factor App では、ログをイベントストリームとして扱い、ログ集約サービスに送信することが推奨されています。これにより、アプリケーションのログを一元管理し、分析や監視を容易にできます。
6.2.1 Terraform を使用した AWS CloudWatch Logs の管理
Terraform を使用することで、AWS CloudWatch Logs を管理し、ECS タスクのログを収集できます。以下に、Terraform を使用して CloudWatch Logs を設定する例を示します。
resource "aws_cloudwatch_log_group" "myapp" {
name = "/ecs/myapp"
}
resource "aws_ecs_task_definition" "myapp" {
family = "myapp"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = "myapp"
image = "myapp:latest"
cpu = 256
memory = 512
essential = true
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.myapp.name
awslogs-region = "us-east-1"
awslogs-stream-prefix = "ecs"
}
}
}
])
}
6.2.2 Rails アプリケーションでのログ出力方法
Rails アプリケーションでログを出力する際は、以下の方法を検討できます。
config/environments/*.rb
で、ログのフォーマットやログレベルを設定するActiveSupport::Logger
を使用して、カスタムログを出力するconfig/initializers/
で、ログ出力先を設定する(標準出力、ファイル、AWS CloudWatch Logs など)
7. 開発/本番一致と管理プロセス
7.1 開発、ステージング、本番環境の一致
Twelve-Factor App では、開発、ステージング、本番環境を可能な限り一致させることが推奨されています。これにより、環境間の差異によるバグや予期せぬ動作を最小限に抑えられます。
7.1.1 環境間の設定の管理方法
環境間の設定を管理する際は、以下の方法を検討できます。
- Terraform の変数機能を使用して、環境ごとの設定を管理する
- AWS Systems Manager パラメータストアを使用して、環境変数を管理する
- AWS Secrets Manager を使用して、機密情報(APIキー、DBパスワードなど)を管理する
7.2 管理タスクをワンオフのプロセスとして実行
Twelve-Factor App では、管理タスク(データベースのマイグレーション、コンソールでのスクリプト実行など)をワンオフのプロセスとして実行することが推奨されています。これにより、管理タスクのバージョン管理や実行履歴の追跡が容易になります。
7.2.1 Terraform を使用した AWS Systems Manager の管理
Terraform を使用することで、AWS Systems Manager を管理し、ECS タスクとして管理タスクを実行できます。以下に、Terraform を使用して Systems Manager のパラメータを管理する例を示します。
resource "aws_ssm_parameter" "database_url" {
name = "/myapp/database/url"
type = "SecureString"
value = "postgres://user:password@hostname:port/database"
}
7.2.2 Rails アプリケーションでの管理タスクの実行方法
Rails アプリケーションで管理タスクを実行する際は、以下の方法を検討できます。
rails
コマンドを使用して、タスクをワンオフのプロセスとして実行するActiveJob
を使用して、タスクをバックグラウンドジョブとして実行する- AWS ECS のタスク定義を使用して、管理タスクを実行する
8. まとめ
本記事では、AWS と Rails を使用して Twelve-Factor App を実践する方法について説明しました。GitHub と Terraform を活用することで、コードベースの管理、CI/CD パイプラインの構築、インフラのコード化、環境の一致などを実現できます。
Twelve-Factor App を導入することで、以下のようなメリットが期待できます。
- スケーラビリティの向上
- 開発と運用の効率化
- 環境間の一貫性の確保
- 障害対応の迅速化
AWS と Rails を使用した Twelve-Factor App の実装には、以下のようなポイントがあります。
- AWS のマネージドサービスを活用し、インフラストラクチャの管理を簡素化する
- Terraform を使用して、インフラストラクチャをコードとして管理する
- GitHub Actions を使用して、CI/CD パイプラインを自動化する
- ステートレスなアプリケーション設計を心がけ、水平スケーリングを容易にする
- ログやメトリクスを収集し、監視・分析を行う
Twelve-Factor App は、クラウドネイティブなアプリケーション開発のための優れた指針です。本記事で紹介した方法を参考に、AWS と Rails を使用して Twelve-Factor App を実践し、より堅牢で持続可能なアプリケーションを構築していただければ幸いです。