1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初心者・IaC入門】第2回:Terraform × ローリングアップデート 〜少しずつ、着実に〜

1
Posted at

※本記事はAIが書いて、人間がファクトチェックしています。
IaC連載企画の第2回です。


こんにちは!「絶対止めない」デプロイ戦略ハンズオン、第2回です。

前回(第1回)は、稼働中のサーバーを1台まるごと「壊して・作り直す(インプレースデプロイ)」という力技を試した結果、見事に数分間のダウンタイム(瞬断)が発生してしまいましたね。
今回はその反省を活かし、**「ユーザーに全く気づかれずに、裏側でコッソリとバージョンアップを完了させる」**魔法のようなデプロイを体験します。


1. 【プロローグ】 「もう絶対止めないぞ!」若手エンジニアの決意

若手エンジニア(あなた):
「前回のデプロイで数分間サイトが落ちて、SNSで『〇〇のサイト繋がらないんだけどw』って書かれちゃいました……。もうあんな思いは嫌です!」

先輩エンジニア:
「じゃあ今回は、サーバーを2台に増やしてみようか。1台をアップデートしている間、もう1台でユーザーのアクセスを受け止めれば、サイトは落ちないよね?」

若手エンジニア:
「なるほど!ロードバランサー(ALB)を前に置いて、後ろのサーバーを順番に入れ替えていくんですね。それなら誰も気づかないうちに青(v1)から緑(v2)へ移行できる!」


2. 【図解インプット】 ローリングアップデートの仕組み

今回実践するのは**「ローリングアップデート(Rolling Update)」**という戦略です。

【開始時】
[ ALB ] ──┬──> [ EC2 (青 v1) ]
          └──> [ EC2 (青 v1) ]

【アップデート中(1台ずつ入れ替え)】
[ ALB ] ──┬──> [ EC2 (緑 v2) ] ← 新しいのを作って繋ぐ
          └──> [ EC2 (青 v1) ] ← 古いのは後で消す
          
【完了時】
[ ALB ] ──┬──> [ EC2 (緑 v2) ]
          └──> [ EC2 (緑 v2) ]

AWSでこれを実現するには、Application Load Balancer (ALB)Auto Scaling Group (ASG) を組み合わせます。
ASGは「常に指定した台数のサーバーを維持する」機能を持っています。Terraformの instance_refresh という機能を使うと、この入れ替え作業を全自動で、かつ無停止でやってくれるのです。


3. 【ハンズオン】 止まらないデプロイを体感せよ!(所要時間:約40分)

Step 1: ALB + ASG構成のTerraformコード準備

今回はコードが少し長くなります。適当な作業用フォルダに main.tf を作成し、以下のコードを貼り付けてください。
(※学習用のため、デフォルトVPCを利用するシンプルな構成にしています)

provider "aws" {
  region = "ap-northeast-1"
}

# デフォルトVPCとサブネットの情報を自動取得
data "aws_vpc" "default" { default = true }
data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}

# 最新のAmazon Linux 2023のAMIを動的に取得
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-x86_64"]
  }
}

# 1. セキュリティグループ(ALB用とEC2用を兼用)
resource "aws_security_group" "web_sg" {
  name        = "rolling-update-sg"
  description = "Allow HTTP"
  vpc_id      = data.aws_vpc.default.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# 2. ALB(ロードバランサー)とターゲットグループ
resource "aws_lb" "web_alb" {
  name               = "handson-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.web_sg.id]
  subnets            = data.aws_subnets.default.ids
}

resource "aws_lb_target_group" "web_tg" {
  name     = "handson-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = data.aws_vpc.default.id
}

resource "aws_lb_listener" "web_listener" {
  load_balancer_arn = aws_lb.web_alb.arn
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.web_tg.arn
  }
}

# 3. 起動テンプレート(EC2の設計図)
resource "aws_launch_template" "web_lt" {
  name          = "handson-web-lt"
  image_id      = data.aws_ami.amazon_linux.id # 最新のAMIをセット
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  # ★ここがアプリのバージョン(今回はv1の青)
  user_data = base64encode(<<-EOF
    #!/bin/bash
    dnf install -y nginx
    systemctl start nginx
    systemctl enable nginx
    echo '<body style="background-color: blue;"><h1 style="color: white;">Version 1 (Blue)</h1></body>' > /usr/share/nginx/html/index.html
  EOF
  )

  lifecycle {
    ignore_changes = [image_id]  
  }
  
}

# 4. Auto Scaling Group (ASG)
resource "aws_autoscaling_group" "web_asg" {
  name                = "handson-asg"
  desired_capacity    = 2 # 常に2台稼働させる
  max_size            = 4
  min_size            = 2
  vpc_zone_identifier = data.aws_subnets.default.ids
  target_group_arns   = [aws_lb_target_group.web_tg.arn]

  # ★超重要:ALBのヘルスチェックを基準にする設定
  # これがないと、Nginxが起動する前に「新しいEC2ができたから古いものを消す」と誤認され、瞬断します。
  health_check_type         = "ELB"
  health_check_grace_period = 120

  launch_template {
    id      = aws_launch_template.web_lt.id
    version = aws_launch_template.web_lt.latest_version
  }

  # ★ここがローリングアップデートの魔法!
  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 50 # 最低1台は生き残らせる
    }
  }
}

# 最後にALBのURLを表示
output "alb_dns_name" {
  value = aws_lb.web_alb.dns_name
}

Step 2: v1(青アプリ)のデプロイ

ターミナルで以下を実行します。今回はロードバランサーを作るため、約3〜4分かかります。コーヒーでも飲みながら待ちましょう。

terraform init
terraform apply

完了すると、ターミナルの一番下に alb_dns_name = "handson-alb-xxxx.ap-northeast-1.elb.amazonaws.com" のようなURLが出力されます。これをブラウザで開いて、青い画面が出れば準備完了です!

Step 3: 【真骨頂】ダウンタイムなしのv2(緑アプリ)デプロイ

さあ、無停止デプロイの実験です。
まず、別のターミナルウィンドウを開き、**1秒に1回アクセスし続けるコマンド(curlループ)**を実行します。

※ の部分は、さっき出力された自分のURLに変えてください

【Mac / Linux (bash, zsh等) の場合】

while true; do curl -s http://<ALBのURL> | grep -o "Version .*"; sleep 1; done

【Windows (PowerShell) の場合】

while ($true) { curl.exe -s http://<ALBURL> | Select-String "Version"; Start-Sleep -Seconds 1 }

画面に Version 1 (Blue)</h1></body> が1秒ごとに出力され続けますね。

実際の画面:
image.png

この状態のまま、元のターミナルに戻り、main.tfuser_data を「緑色(v2)」に書き換えます。

  # ★v2の緑に変更!
  user_data = base64encode(<<-EOF
    #!/bin/bash
    dnf install -y nginx
    systemctl start nginx
    systemctl enable nginx
    echo '<body style="background-color: green;"><h1 style="color: white;">Version 2 (Green)</h1></body>' > /usr/share/nginx/html/index.html
  EOF
  )

保存したら、いざデプロイ!

terraform apply

【curlループの画面に注目してください】
デプロイが進むにつれて、出力が劇的に変化します。(※切り替わるまで数分かかります)

Version 1 (Blue)</h1></body>
Version 1 (Blue)</h1></body>
Version 2 (Green)</h1></body>  ← おっ!?
Version 1 (Blue)</h1></body>   ← 混ざってきた!
Version 2 (Green)</h1></body>
Version 2 (Green)</h1></body>
Version 2 (Green)</h1></body>  ← 完全に切り替わった!

実際の画面:
image.png

一度もエラー(接続拒否など)が出ることなく、徐々に青から緑へ移行したのがお分かりいただけたでしょうか?
これがローリングアップデートの力です。ユーザーから見れば「サイトを使っていたら、いつの間にか新しいバージョンになっていた」という体験になります。


4. 【トラシュー体験】 恐怖!「状態」を忘れたTerraform

大成功に喜んだのも束の間。ここで、IaC初心者が高確率で踏む 「絶望の罠」 を体験しておきましょう。

Terraformは、AWS上のどこに何を作ったかを terraform.tfstate というファイルに記録しています。
もし、このファイルをうっかり消してしまったら……?

わざとファイル名を変更して、消えた状態を疑似体験してみましょう。

# tfstateファイルを隠す(消したのと同じ状態にする)
mv terraform.tfstate terraform.tfstate.hidden

この状態で、terraform plan を実行してみてください。

terraform plan

【結果】
Terraformは「あれ?何もないぞ? よし、ALBもASGも全部ゼロから新しく作ろう!」と提案してきます。
しかし、AWS上にはすでに同じ名前のALBが存在するため、もし apply すると 「名前が重複しています(AlreadyExists)」という真っ赤なエラー を吐いてパニックになります。

実際の画面:

image.png

現場でこれが起きると冷や汗が止まりません。本来であれば、コマンドラインから terraform import aws_security_group.web_sg sg-01234... のように叩いて、AWS上に実在するリソースのIDとTerraformコードを1つずつ手作業で紐付け直す、非常に泥臭い復旧作業(import) が必要になります。

今回は学習用なので、隠したファイルを元に戻して復旧させましょう。

mv terraform.tfstate.hidden terraform.tfstate

これでもう一度 terraform plan を打つと、「No changes」と表示され、無事に元の平和な状態に戻ります。
「tfstateファイルは命より重い(だから実務ではS3などの安全な場所に保存する)」 という教訓を胸に刻んでください。

実際の画面:

image.png


5. 【今回のリザルト】 ローリングアップデート

お片付けを忘れずに!

terraform destroy
評価項目 結果 現場でのリアルな声
コスト(AWS費用) ★★☆ (普通) 一時的に新旧のサーバーが同時に立ち上がるため、少しだけ費用が増える。
デプロイの速さ ★☆☆ (遅い) 1台(または数台)ずつ徐々に入れ替えるため、台数が多いと数十分かかることも。
安全性(無停止か) ★★☆ (高い) ダウンタイムなし! ただし、新旧のバージョンが混在する時間帯があるため、DBの設計変更などが絡むと少し工夫が必要。

💡 まとめ:
ローリングアップデートは、コストと安全性のバランスが取れた非常に優秀な戦略です。ただし「デプロイ完了までに時間がかかる」という弱点があります。


【次回予告】

「無停止なのは最高だけど、デプロイに5分も10分も待ってられないよ! もしバグがあったら、戻すのにも同じ時間がかかるんでしょ!?」

鋭いですね。その弱点を克服するのが、次回(第3回)の 「ブルーグリーンデプロイ」 です。
CloudFormationとECS(コンテナ)を使って、「一瞬で切り替え、一瞬で戻す」 という最強のスピード感を体験しましょう。お楽しみに!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?