0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EC2 にアプリケーション層のヘルスチェック「Application status checks」が来たので実際に試してみた

0
Posted at

はじめに

EC2の「ステータスチェック」といえば、これまではインスタンス自体やホスト側の障害を見るものでした。

Webサーバーのプロセスが固まっている、Dockerデーモンが落ちているといった「アプリケーションの中身」の異常は、EC2側のチェックでは検知できず、自前でヘルスチェックの仕組みを作る必要がありました。

今回、この隙間を埋める新しいステータスチェックが追加されたので、実際にインスタンスを立てて正常時・異常時の両方の挙動を確認してみました。

Application status checksとは

アプリケーションが動いているポートとパス(例: /health)に対して定期的にHTTPリクエストを送り、期待したステータスコードが返ってくるかどうかを60秒間隔で確認する機能です。

チェック用のリクエストは、自分のVPC内に自動で作られる「マネージドENI(AWSが管理するネットワークインターフェース)」から送られるため、インターネットを経由しません。

チェックは「連続◯回失敗したらimpaired」「連続◯回成功したらok」というしきい値(デフォルトはどちらも2回)で判定され、この結果はAuto Scalingグループのヘルスチェックとしてもそのまま使えます。

既存のインスタンス/システムステータスチェックと並行して動くので、今まで自前で作っていたアプリケーションレベルの死活監視をAWS側に寄せられるのが一番のメリットかと思います。

やってみた

手順1: Terraformでテスト用インスタンスを用意する

nginxをアプリケーション代わりに動かすインスタンスと、ポート80をVPC内からだけ許可するセキュリティグループを作ります。

main.tfの中身
mkdir -p appstatus-tf && cd appstatus-tf

cat > main.tf <<'EOF'
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

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

data "aws_vpc" "default" {
  default = true
}

data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}

data "aws_ssm_parameter" "al2023_arm64" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
}

resource "aws_security_group" "appstatus_verify" {
  name        = "appstatus-verify-sg"
  description = "temp sg for application status check verification"
  vpc_id      = data.aws_vpc.default.id
}

resource "aws_vpc_security_group_egress_rule" "all" {
  security_group_id = aws_security_group.appstatus_verify.id
  cidr_ipv4          = "0.0.0.0/0"
  ip_protocol        = "-1"
}

resource "aws_instance" "appstatus_verify" {
  ami                    = data.aws_ssm_parameter.al2023_arm64.value
  instance_type          = "t4g.micro"
  subnet_id              = sort(data.aws_subnets.default.ids)[0]
  vpc_security_group_ids = [aws_security_group.appstatus_verify.id]
  user_data              = <<-USERDATA
    #!/bin/bash
    dnf install -y nginx
    systemctl enable --now nginx
  USERDATA

  tags = {
    Name = "appstatus-verify"
  }
}

output "instance_id" {
  value = aws_instance.appstatus_verify.id
}

output "security_group_id" {
  value = aws_security_group.appstatus_verify.id
}
EOF

ヘルスチェック用のインバウンドルールだけは、あとの手順4で「Terraformの変更として」消したいので別ファイルに分けておきます。

cat > ingress.tf <<'EOF'
resource "aws_vpc_security_group_ingress_rule" "http" {
  security_group_id = aws_security_group.appstatus_verify.id
  cidr_ipv4          = data.aws_vpc.default.cidr_block
  from_port          = 80
  to_port            = 80
  ip_protocol        = "tcp"
}
EOF

terraform init -input=false
terraform apply -auto-approve

INSTANCE_ID=$(terraform output -raw instance_id)
SG_ID=$(terraform output -raw security_group_id)
echo "INSTANCE_ID=$INSTANCE_ID / SG_ID=$SG_ID"

AMIをAWS公式ドキュメントにあるami = "resolve:ssm:/aws/service/..."構文で直接指定すると、state上は実際に解決されたAMI IDが保存されるため、次のterraform applyのたびに設定値との差分でインスタンスが再作成されてしまいます。
そのため、data "aws_ssm_parameter"経由で値を取ることで安定します。

また、aws_subnetsidsは実はList型なのですが、裏側のAWS API(DescribeSubnets)自体がレスポンス順序を保証していないため、tolist(...)[0]のままだと再適用時にsubnet_idが変わってインスタンスが再作成されることがありました。sort()で固定しています。

手順2: Application status checkを作成する

Get Started手順に沿って、HTTPのポート80・パス/(デフォルトの/にnginxが200を返すのでそのままでOK)・ステータスコード200を条件にチェックを作ります。

CHECK_ID=$(aws ec2 create-application-status-check \
  --protocol http --port 80 --status-code-matcher 200 \
  --initialization-grace-period-seconds 30 \
  --query 'ApplicationStatusCheck.ApplicationStatusCheckId' --output text)

echo "CHECK_ID=$CHECK_ID"

スクリーンショット 2026-08-24 1.34.19.png

手順3: インスタンスに関連付けて結果を確認する

aws ec2 associate-application-status-check \
  --application-status-check-id $CHECK_ID --instance-ids $INSTANCE_ID

関連付け直後はinitializingで、しばらくするとokに変わります。okになるまでその場でポーリングします。

while true; do
  STATUS=$(aws ec2 describe-application-status \
    --query "ApplicationStatuses.Instances[?InstanceId=='$INSTANCE_ID'].ApplicationStatus.Status" \
    --output text)
  echo "$(date +%T) $STATUS"
  [ "$STATUS" = "ok" ] && break
  sleep 15
done

aws ec2 describe-application-status \
  --query "ApplicationStatuses.Instances[?InstanceId=='$INSTANCE_ID']"
[
    {
        "InstanceId": "i-0b2b14188f940ab8e",
        "ApplicationStatus": {
            "Status": "ok",
            "Details": [
                {
                    "ApplicationStatusCheckId": "asc-f216389f",
                    "Status": "passed",
                    "Reason": { "Code": "ResponseCodeMatched", "StatusCode": 200, "Protocol": "HTTP" }
                }
            ]
        }
    }
]

スクリーンショット 2026-08-24 1.38.38.png

手順4: 障害を疑似的に起こして検知できるか確認する

ingress.tfを削除してterraform applyし、Terraformの変更としてインバウンドルールを取り除きます。
FailureThresholdのデフォルト2回どおり、impairedになるまで約2分かかります。

rm ingress.tf
terraform apply -auto-approve

while true; do
  STATUS=$(aws ec2 describe-application-status \
    --query "ApplicationStatuses.Instances[?InstanceId=='$INSTANCE_ID'].ApplicationStatus.Status" \
    --output text)
  echo "$(date +%T) $STATUS"
  [ "$STATUS" = "impaired" ] && break
  sleep 15
done

aws ec2 describe-application-status \
  --query "ApplicationStatuses.Instances[?InstanceId=='$INSTANCE_ID']"
[
    {
        "InstanceId": "i-0b2b14188f940ab8e",
        "ApplicationStatus": {
            "Status": "impaired",
            "Details": [
                { "Status": "failed", "Reason": { "Code": "ConnectionTimeout", "StatusCode": 0 } }
            ]
        }
    }
]

スクリーンショット 2026-08-24 1.52.45.png

CLIの結果からは impaired となっていますが、AWS マネージメントコンソールの「アプリケーションステータス」は チェックに合格しました のままとなっているのは何なんでしょうね。「ステータスチェック」は 3/4 のチェックに合格しました となっていますので、確実にアプリケーションステータスは失敗しているはずなのですが、ちょっと今のところ謎です。

手順5: 後片付け

# まずチェックをインスタンスから外す
aws ec2 disassociate-application-status-check --application-status-check-id $CHECK_ID --instance-ids $INSTANCE_ID
# その後チェック自体を削除
aws ec2 delete-application-status-check --application-status-check-id $CHECK_ID

# マネージドENIが外れるまで数分〜十数分かかることがあるので、外れ次第destroyする
until terraform destroy -auto-approve; do
  echo "$(date +%T) マネージドENIの解放待ち..."
  sleep 30
done
echo "cleanup done"

料金

マネージドENI 1個・1アベイラビリティゾーンあたり時間0.01ドルに加え、チェックのメトリクスには通常のCloudWatch料金がかかります。
チェックそのものやHTTPリクエスト回数に対する追加課金はありません。

参考リンク

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?