インフラを担当している渡瀬です。
歯が痛くて仕事にならないので虫歯だと思って歯医者に行ったら、TCH (Tooth Contacting Habit、「上下歯列接触癖」)と診断を受けました。
歯と歯が接触する時間は一般的に一日に17分だそうですが、TCHの人は歯と歯が接触している時間が長すぎるという癖だそうです。
下を向くと歯と歯が接触するため、デスクワークやスマホ操作が長い人に多いそうです。
背景
最近、EC2インスタンスがシステムステータスのチェックにフェイルするケースに遭遇しました。
当該サーバはautoscaling groupに所属していたため、自動的にインスタンスの追加と削除が実行されましたが、autoscaling groupに所属せずに動作しているEC2インスタンスもあるため、自動復旧する設定を投入しました。
VMWare vSphereやGCPであれば、ハードウェア障害は、ライブマイグレーション(GCP)で自動対応されますが、AWSにはライブマイグレーションの機能がないため、自力で対応する必要があります。
EC2のインスタンスチェックの種類
AWSのドキュメントを見てみました: インスタンスのステータスチェック
システムステータスチェック
基本的にハードウェア障害
システムステータスチェックの失敗の原因となる問題の例を次に示します。 - ネットワーク接続の喪失 - システム電源の喪失 - 物理ホストのソフトウェアの問題 - ネットワーク到達可能性に影響する、物理ホスト上のハードウェアの問題
引用元: インスタンスのステータスチェック
このケースではこちらでできることは基本的にはない認識です。
インスタンスステータスチェック
ユーザー側で対処する必要のある障害
ユーザーが関与して修復する必要のある問題が検出されます。 インスタンスステータスチェックが失敗した場合は通常、自分自身で (たとえば、インスタンスを再起動する、インスタンス設定を変更するなどによって) 問題に対処する必要があります。 インスタンスステータスチェックの失敗の原因となる問題の例を次に示します。 - 失敗したシステムステータスチェック - 正しくないネットワークまたは起動設定 - メモリの枯渇 - 破損したファイルシステム - 互換性のないカーネル
引用元: インスタンスのステータスチェック
インスタンスステータスチェックの失敗時は、必ずしも自動再起動することが望ましいわけではないのですが、今回のケースでは、自動再起動を設定することを選択しました。
実際の設定
CloudFormation で設定しました
システムステータス対応
AutoRecoveryの設定をするテンプレートです
AWSTemplateFormatVersion: '2010-09-09' Parameters: InstanceId: Type: AWS::EC2::Instance::Id Description: InstanceId Resources: AutoRecoverySystemAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "system-failed-autorecovery-${InstanceId}" Namespace: AWS/EC2 MetricName: StatusCheckFailed_System Statistic: Minimum Period: 60 EvaluationPeriods: 2 ComparisonOperator: GreaterThanThreshold Threshold: 0 AlarmActions: - !Sub "arn:aws:automate:${AWS::Region}:ec2:recover" Dimensions: - Name: InstanceId Value: !Ref InstanceId
インスタンスステータス対応
自動再起動するテンプレートです
AWSTemplateFormatVersion: '2010-09-09' Parameters: InstanceId: Type: AWS::EC2::Instance::Id Description: InstanceId Resources: AutoRecoveryInstanceAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "instance-failed-autoreboot-${InstanceId}" Namespace: AWS/EC2 MetricName: StatusCheckFailed_Instance Statistic: Minimum Period: 60 EvaluationPeriods: 3 ComparisonOperator: GreaterThanThreshold Threshold: 0 AlarmActions: - !Sub "arn:aws:swf:${AWS::Region}:${AWS::AccountId}:action/actions/AWS_EC2.InstanceId.Reboot/1.0" Dimensions: - Name: InstanceId Value: !Ref InstanceId
設定されていないインスタンスを抽出する
コマンドは zsh / bash で動作確認しました
システムステータスのAutoRecovery
システムステータスのAutoRecovery設定済みのインスタンスの一覧
region=ap-northeast-1 aws cloudwatch describe-alarms --region $region \ | jq -r ' .MetricAlarms[] |select(.MetricName == "StatusCheckFailed_System") |select(.AlarmActions[] | . == "arn:aws:automate:'$region':ec2:recover") |.Dimensions[] |select(.Name == "InstanceId").Value'
AutoScalingに属しない status:running のインスタンスのうち、AutoRecovery が適用されていないインスタンスの一覧の抽出コマンド
region=ap-northeast-1 aws ec2 describe-instances --region $region \ --filters 'Name=instance-state-name,Values=running' \ --query 'Reservations[*].Instances[*].InstanceId' \ --output text \ | grep -vxFf <(echo i-xxxxxxxxxxxxxxxxx; \ aws autoscaling describe-auto-scaling-instances --region $region \ --query 'AutoScalingInstances[*].InstanceId' | jq -r '.[]') \ | grep -vxFf <(echo i-xxxxxxxxxxxxxxxxx; \ aws cloudwatch describe-alarms --region $region \ | jq -r ' .MetricAlarms[] |select(.MetricName == "StatusCheckFailed_System") |select(.AlarmActions[] | . == "arn:aws:automate:'$region':ec2:recover") |.Dimensions[] |select(.Name == "InstanceId").Value' \ )
(途中の i-xxxxxxxxxxxxxxxxx は、grep によるフィルタ時に、フィルタ条件が空になり、全件マッチするのを防ぐための条件です)
インスタンスステータスの自動再起動
インスタンスステータスフェイル時に再起動する設定がされているインスタンスの一覧
region=ap-northeast-1 account_id=$(aws sts get-caller-identity --query Account --output text) aws cloudwatch describe-alarms --region $region | \ jq -r ' .MetricAlarms[] |select(.MetricName == "StatusCheckFailed_Instance") |select(.AlarmActions[] | . == "arn:aws:swf:'$region':'$account_id':action/actions/AWS_EC2.InstanceId.Reboot/1.0") |.Dimensions[] |select(.Name == "InstanceId").Value'
AutoScalingに属しない status:running のインスタンスのうち、再起動が設定されていないインスタンスの一覧の抽出コマンド
region=ap-northeast-1 account_id=$(aws sts get-caller-identity --query Account --output text) aws ec2 describe-instances --region $region \ --filters 'Name=instance-state-name,Values=running' \ --query 'Reservations[*].Instances[*].InstanceId' \ --output text \ | grep -vxFf <(echo i-xxxxxxxxxxxxxxxxx; \ aws autoscaling describe-auto-scaling-instances --region $region \ --query 'AutoScalingInstances[*].InstanceId' | jq -r '.[]') \ | grep -vxFf <(echo i-xxxxxxxxxxxxxxxxx; \ aws cloudwatch describe-alarms --region $region \ | jq -r ' .MetricAlarms[] |select(.MetricName == "StatusCheckFailed_Instance") |select(.AlarmActions[] | . == "arn:aws:swf:'$region':'$account_id':action/actions/AWS_EC2.InstanceId.Reboot/1.0") |.Dimensions[] |select(.Name == "InstanceId").Value')
(途中の i-xxxxxxxxxxxxxxxxx は、grep によるフィルタ時に、フィルタ条件が空になり、全件マッチするのを防ぐための条件です)
終わりに
仕組みに任せれることは任せてしまえると楽ですね