OPS

Lambda+AWS Systems Managerでやる障害自動復旧

2018.10.19

本記事のポイント

Lambda+AWS Systems Managerを使って障害検知後の自動復旧を実装してみます。

発想次第で色んなオペレーションを自動化できる仕組みになるので参考にしてみていただければと思います。

はじめに

Lambdaとは

サーバの構築や管理が不要でコードを実行できるサービスになります。

サーバレスなアーキテクチャで良く使われるサービスの一つです。

AWS Systems Managerとは

SDKやAWS CLIを使ってサーバに対してアクションを実行することができるサービスです。

今回はLinuxのshellを実行する為に使用します。

シナリオと仕組み

シナリオ

以下のシナリオ時に自動復旧が行えるように実装します。

1. 正常時はApacheが起動しているWEBサーバ(Linux)

2. Apacheのプロセスダウンが発生

3. 障害検知

4. 検知後自動復旧

仕組み

今回は弊社独自開発の監視ツールpuzzleを使用し障害検知を行なってます。

APIが叩ける監視ツールや仕組みであればツールは問いません。

今回の仕組みではpuzzleで障害検知後APIを叩きLambdaをキック、LambdaからSystems ManagerをキックしてEC2内で復旧コマンドが実行され復旧する仕組みになります。

事前準備

EC2と監視

あらかじめ下記環境を用意しておきました。

・Apacheがインストール済みのEC2インスタンス(Linux)

・AmazonEC2RoleforSSMポリシーのロールを作成しEC2へアタッチ

・監視ツールインストール

・Systems Managerのエージェントインストール

※参考:https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-manual-agent-install.html

puzzleでApacheプロセスを監視しプロセスがダウンすると検知するように設定しております。

今回は弊社の監視ツールを使いましたがAPIを叩けるのであればDatadogやZabbix等の監視ツールでも実装可能です。

IAMロールの用意

Systems Managerのコマンド実行を許可するロール

Systems Managerのコマンド実行を許可するロールです。
下記ポリシーでIAMロールを作成し対象のEC2へアタッチします。

デフォルトで用意されている管理ポリシーになりますのでそちらを選択しても問題ありません。

・AmazonEC2RoleforSSM

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeAssociation",
                "ssm:GetDeployablePatchSnapshotForInstance",
                "ssm:GetDocument",
                "ssm:GetManifest",
                "ssm:GetParameters",
                "ssm:ListAssociations",
                "ssm:ListInstanceAssociations",
                "ssm:PutInventory",
                "ssm:PutComplianceItems",
                "ssm:PutConfigurePackageResult",
                "ssm:UpdateAssociationStatus",
                "ssm:UpdateInstanceAssociationStatus",
                "ssm:UpdateInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2messages:AcknowledgeMessage",
                "ec2messages:DeleteMessage",
                "ec2messages:FailMessage",
                "ec2messages:GetEndpoint",
                "ec2messages:GetMessages",
                "ec2messages:SendReply"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstanceStatus"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ds:CreateComputer",
                "ds:DescribeDirectories"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetEncryptionConfiguration",
                "s3:AbortMultipartUpload",
                "s3:ListMultipartUploadParts",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": "*"
        }
    ]
}

 

Lambda実行用ロール

Lambda用には下記ポリシーを付与したロールを作成します。
いずれもデフォルトで用意されている管理ポリシーがありますのでそちらを選んでいただいて問題ございません。

・AmazonSSMFullAccess

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData",
                "ds:CreateComputer",
                "ds:DescribeDirectories",
                "ec2:DescribeInstanceStatus",
                "logs:*",
                "ssm:*",
                "ec2messages:*"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "ssm.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:DeleteServiceLinkedRole",
                "iam:GetServiceLinkedRoleDeletionStatus"
            ],
            "Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*"
        }
    ]
}

 

・CloudWatchLogsFullAccess

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "autoscaling:Describe*",
                "cloudwatch:*",
                "logs:*",
                "sns:*",
                "iam:GetPolicy",
                "iam:GetPolicyVersion",
                "iam:GetRole"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/events.amazonaws.com/AWSServiceRoleForCloudWatchEvents*",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "events.amazonaws.com"
                }
            }
        }
    ]
}

 

Lambda設定

Function作成

Systems ManagerでEC2内のApacheを起動するLambdaFunctionを作成します。

ランタイム:Python 2.7で動作します。
実行ロール:先ほど作ったLambda実行用ロール

 

import boto3
import logging
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
ssm = boto3.client('ssm')

instance = ["〓対象のインスタンスID〓"]

def lambda_handler(event, context):
    try:
        ssm.send_command( #Systems Managerの処理です
            InstanceIds = instance,
            DocumentName = "AWS-RunShellScript",
            Parameters = {
                "commands": [
                    "systemctl start httpd" #EC2で実行させたいコマンドです
                ],
                "executionTimeout": ["3600"]
            },
        )
 
    except Exception as e:
        logger.error(e)
        raise e

 

この時点でテストを実行するとEC2のApacheが起動するはずです。
起動しない場合はEC2にアタッチしているロールやLambdaのロールに問題がある場合がほとんどなので再度確認してみましょう。

上手くいかない場合はCloudWatchLogsにもログが出力されますのでログを確認してみましょう。

API Gateway設定

API作成

警報をトリガーにAPIを叩きLambdaを実行する仕組みを作成します。
監視ツールpuzzleから叩けるAPIをAPI Gatewayを使い実装します。

 

1. API Gatewayダッシュボードから新規作成でAPI名などを入力しAPIを作成してください。

 

2. アクションボタンをクリックしメソッドの作成を選択してください。

 

3. GETメソッドを選択します。

 

4. Lambda関数を選択し作成したLambdaFunctionの名前を入力します。

 

5. アクションをクリックしAPIのデプロイを選択します。

 

6. 新しいステージを選択しステージ名を入力します。

※APIのURLパスになります。

 

7. ステージを選択するとAPIのURLが確認できます、アクセスし一連の処理が実行されるか確認しましょう。

補足

今回割愛しますがリソースポリシーを設定することで特定IPからのみAPIを叩けるようにしたりVPC内部からのみ許可する設定などを行うことができます。

特に設定を行わない場合はAPIはインターネットへ公開されますのでご注意ください。

まとめ

実際にテストを行い監視ツールpuzzleでApacheプロセスダウン検知後にAPIを叩き自動復旧できることが確認できました。

 

API経由でなくてもCloudWatchのイベントやアラームをトリガーにLmabdaが実行可能ですので発想次第で色んな自動化が実現できる仕組みかと思います。

 

是非参考にしてみていただければ幸いです。