一台のサーバを動かすのははじめの一歩としては良いですが、現実的には一台のサーバは単一障害点となり得ます。
単一障害点とは、簡単に言えばサービスが停止し利用不可になる弱点のこと。 これを防ぐためには複数のサーバーで負荷分散をするのが必須となりますがAWSのASG(オートスケーリンググループ)サービスを使うことで EC2のクラスタ管理を行なってくれます。 ECS Fargateなどのコンテナをしようしてスケーリングする手法もありますが、今回はEC2のスケーリングASGを使用します。
ASGをTerraformから使用する場合は、aws_autoscaling_group
リソースを使用します。
これは単一のEC2インスタンスを表すリソースaws_instance
と似ていますが、min_size
とmax_size
のパラメーターに値を入れることでその値の範囲でオートスケーリングしてくれます。
また、それらスケーリンググループの起動設定リソースとして、aws_launch_configuration
リソースをしようします。
resource "aws_launch_configuration" "example" { image_id = "ami-0fb653ca2d3203ac1" instance_type = "t2.micro" security_groups = [aws_security_group.instance.id] user_data = <<-EOF #!/bin/bash echo "Hello, World" > index.html nohup busybox httpd -f -p ${var.server_port} & EOF # Required when using a launch configuration with an auto scaling group. lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "example" { launch_configuration = aws_launch_configuration.example.name vpc_zone_identifier = data.aws_subnets.default.ids target_group_arns = [aws_lb_target_group.asg.arn] health_check_type = "ELB" min_size = 2 max_size = 10 tag { key = "Name" value = "terraform-asg-example" propagate_at_launch = true } }
- 上記の例では、EC2インスタンスは2~10の間でスケーリングします。
launch_configuration = aws_launch_configuration.example.name
という設定を使用することで、aws_autoscaling_group
はaws_launch_configuration
を利用することがわかります。- また、
vpc_zone_identifier = data.aws_subnets.default.ids
では、デフォルトのVPCを採用したサブネットidを指定しております。 ここでは、AWSがデフォルトで用意してくれているサービスに接続するためにデータソースという構文をしようしてサービスに接続します。
data "aws_vpc" "default" { default = true } data "aws_subnets" "default" { filter { name = "vpc-id" values = [data.aws_vpc.default.id] } }
ロードバランサーで負荷分散
ここまでで複数のサーバーを立ち上げる準備が整いましたが、エンドユーザーが全てのIPアドレスを把握するというのは大変なことです。 これらのサーバーにトラフィックを分散するロードバランサをデプロイし、エンドユーザーにはこのIPへアクセスさせるのが良いでしょう。
AWSではElastic Load Balancingというサービスがロードバランサーの役割を担ってくれます。 ELBには主に2種類あります。
- Application Load Balancer (ALB)
- Network Load Balancer (NLB)
他にもClassic Load Balancerが存在しますが、比較的機能が少ない&古いため省きます。
今回のケースではWebトラフィックを使用するためHTTPSを扱うALBを採用します。
Listener, Target Group
ロードバランサーは一つのAWSリソースから構成されているわけではありません。 リスナ、ターゲットグループ、リスナールールから構成されます。
- リスナ
- ターゲットグループ
- リスナルール
- リスナーに対するアクセスを受け取り、特定のパスやホスト名に一致したリクエストを指定したターゲットグループに送ります。
ALB本体
まずはALB本体を作成する必要があります。
resource "aws_lb" "example" { name = var.alb_name load_balancer_type = "application" subnets = data.aws_subnets.default.ids security_groups = [aws_security_group.alb.id] }
load_balancer_type
の引数にapplication
を指定することで、ALBの作成が可能です。
上記の例では、EC2とロードバランサが同じサブネットグループに所属していますが、本来はEC2はプライベートサブネット、ALBはパブリックサブネットに配置し、 ユーザーが直接EC2にアクセスできない用にした方がいいでしょう。
リスナ
次に、このALBに登録するリスナを作成します。
resource "aws_lb_listener" "http" { load_balancer_arn = aws_lb.example.arn port = 80 protocol = "HTTP" # By default, return a simple 404 page default_action { type = "fixed-response" fixed_response { content_type = "text/plain" message_body = "404: page not found" status_code = 404 } } }
このリスナは80番ポートのHTTPでリッスンし、条件に一致しない場合は404 page not found
を返すようにしています。
ターゲットグループ
resource "aws_lb_target_group" "asg" { name = var.alb_name port = var.server_port protocol = "HTTP" vpc_id = data.aws_vpc.default.id health_check { path = "/" protocol = "HTTP" matcher = "200" interval = 15 timeout = 3 healthy_threshold = 2 unhealthy_threshold = 2 } }
このターゲットグループは、インスタンスに対して定期的にHTTPリクエストを送ることでインスタンスがhealthyであることを確認します。 インスタンスがダウンしている時はunhealthyと判断され、他のインスタンスにトラフィックが分散される仕組みです。
このあとは、aws_autoscaling_group
の属性target_group_arns
に上記のターゲットグループを指定することで、どのEC2をターゲットグループにするかを伝えることができます。
このように、ユーザーから見てロードバランサーよりも奥の層では、オートスケーリンググループとターゲットグループが密な結合になっているため、この部分でEC2を指定できるという状況です
resource "aws_autoscaling_group" "example" { launch_configuration = aws_launch_configuration.example.name vpc_zone_identifier = data.aws_subnets.default.ids target_group_arns = [aws_lb_target_group.asg.arn] health_check_type = "ELB" min_size = 2 max_size = 10 tag { key = "Name" value = "terraform-asg-example" propagate_at_launch = true } }
りすなルール
resource "aws_lb_listener_rule" "asg" { listener_arn = aws_lb_listener.http.arn priority = 100 condition { path_pattern { values = ["*"] } } action { type = "forward" target_group_arn = aws_lb_target_group.asg.arn } }
最後にリスナルールを作成し、ここまで見てきたものを一つにまとめましょう。 リスナールールの役割は、リスナーから受け取った情報を、特定のターゲットグループに渡すことでした。
上記のコードは、パスが一致するリクエストをASGが含まれるターゲットグループに送るリスナルールを追加します。
page:https://minegishirei.hatenablog.com/entry/2024/06/22/073328