Jenkins Pipeline から AWS SNS に publish する

Jenkins でのビルド失敗時に AWS SNS で通知を送りたかったので、そのために試行錯誤したメモ。

AWS SNS について

Simple Notification Service の略で、通知を扱うための AWS のマネージドサービス。AWS SNS にメッセージを Publish することで、設定に応じてメールやチャットツールなどに通知を送ることができる。

AWS SNS の設定方法についてはググればたくさん出てくるので割愛する。

今回は、AWS SNS の Topic は既に作成済みという前提で話をする。

Amazon SNS Build Notifier について

当初 Amazon SNS Build Notifier というプラグインを使おうとしていたが、これは動かなかった。

リポジトリを見ると 2016 年以降メンテナンスされていないようだし、そもそも Pipeline に対応していないのかもしれない。

Pipeline: AWS Steps を使う

Pipeline: AWS Steps というプラグインの中に snsPublish という機能を見つけて「これはいけそうだぞ」と思って試してみたところ、できた。

Jenkins 公式がメンテしているし、現時点ではこれを使うのがベストっぽい。

AWS の認証情報を設定する

今回は Jenkins サーバのローカルの Credentials を利用する。

Jenkins のビルドは jenkins ユーザが実行していたので、jenkins ユーザのホームディレクトリ以下の .aws/credentials に認証情報が保存されるようにした。

$ sudo su - jenkins
$ aws configure

今回は試していないが、Jenkins サーバが Amazon EC2 で動いている場合は SNS:Publish の権限がある IAM Role を EC2 インスタンスに割り当てれば、認証情報をサーバのローカルに保存しなくても通知を送れると思う。

その他にも、withAWS という step を挟むことで柔軟に認証情報を設定できる模様。

Pipeline 定義に通知設定を書く

以下のように書くと「ビルドが失敗した場合」に通知することができる。

Jenkinsfile
pipeline {
    stages {
        // 必ずコケるステージ
        stage('main') {
            steps {
                sh 'false'
            }
        }
    }
    post {
        failure {
            snsPublish(
                topicArn: 'arn:aws:sns:ap-northeast-1:000000000000:xxxxxxxx',
                subject: 'Build failed!',
                message: env.BUILD_URL,
            )
        }
    }
}

ビルドを実行すると、無事通知が届くと思う。

参考

続きを読む

AWS S3勉強まとめ

ブロックストレージ
EBS, インスタンスストア
→EC2にマウントして活用
→Block番号で管理

オブジェクトストレージ
S3, Glacier
→安価かつ高い耐久性を持つオンラインストレージ
→オブジェクト、それに付随するメタデータ、そのオブジェクトにアクセスするためのユニークなIDで構成

ファイルストレージ
EFS
→EC2から同時マウントできる共有ストレージサービス
→ファイルシステム

・S3特徴
→容量無制限、安価なストレージ(1GB3円)、データ容量に依存しない性能(RAIDやサーバー台数を考える必要なし)

・S3用途
①コンテンツ配信、保管サーバ
②ログ&データハブストレージ
③バックアップやDR

バケット
オブジェクトの保存場所。デフォルト100個/1アカウントまで作成可能。名前はグローバルでユニークな必要あり。
オブジェクト
データ本体。URLが付与される
キー
オブジェクトの格納URL
メタデータ
オブジェクトに付随する属性情報。システム定義メタデータ、ユーザ定義メタデータあり
リージョン
バケットを配置するAWSのロケーション
アクセスコントロールリスト(ACL)
バケットやオブジェクトのアクセス管理

・ストレージクラス
スタンダード
標準低頻度アクセスストレージ:スタンダードに比べて安価だが、データの読出し容量に対して課金
Glacier:最も低コスト。データの取り出しにコストと時間
低冗長化ストレージ:Glacierから取り出したデータの置き場所として利用

結果整合性(Eventual Consistency Readモデル)
「更新はそのうち全体に反映される」
読み取り一貫性
– あるトランザクションがデータを変更中のとき、ほかのトランザクションからは変更される前のデータを参照します。
– ほかのトランザクションからは変更前の確定されたデータを参照します。
– あるユーザーAが値をUPDATEしたとき、ユーザーBがそのデータを参照すると、戻ってくる値はUPDATE前の値となります。
– あるトランザクションで変更した確定前のデータをほかのトランザクションから参照することはできません。

・パソコンのファイルシステムやデータベースと同じようにロックやトランザクション処理は行われない
参考URL:https://dev.classmethod.jp/cloud/amazon-s3-eventually-consistent-and-consistent-read/

・アクセス管理
①ユーザポリシー
→IAMuserに対して権限設定
②バケットポリシー
→バケットごとに権限設定。クロスアカウントで使用する際など
③ACL
→バケット、オブジェクトごとに指定可能(オブジェクトACLが優先)

署名付きURL
AWS SDKで作成。S3のプライベートなオブジェクトに対して一定時間アクセスを許可

・Webサイトホスティング機能
静的なWebサイトをS3のみでホスティング可能
– バケット単位で指定
– 独自ドメインの設定→ドメイン名をバケット名として指定
– リダイレクト機能→任意のドメインにリダイレクト設定が可能
CloudFrontとの経由で配信することを推奨。バケットポリシーでHTTP/HTTPSリクエストのみを許可可能

VPCエンドポイント
プライベートサブネットからNATゲートウェイなどを経由せずに直接S3とセキュアに通信可能
同一リージョンのみ

S3 support for IPv6
追加費用なし
静的ウェブホスティングは使用不可

・暗号化
– サーバーサイド暗号化(サーバリソースを利用して格納データの暗号化)
– クライアントサイド暗号化(クライアント側で暗号化したデータをS3にアップロード)

クロスリージョンレプリケーション
異なるリージョン間のS3バケットオブジェクトのレプリケーションを実施
→オブジェクトに対する動作を非同期でレプリケーション
→対象元バケットはバージョニングの機能を有効にする必要あり
※リージョン間データ転送費用が発生

バージョン管理機能
誤操作による削除対策に有効
バケットに対して設定
任意のオブジェクトを参照可能
バージョニングのオブジェクト分も課金。保存期間も指定可能

ライプサイクル管理
バケット内のオブジェクトに対して、ストレージクラスの変更や、削除処理の自動化
データ登録→Standard保存(一定期間過ぎたら削除)→Standard-IA移動(一定期間過ぎたら削除)→Glacierにアーカイブ(一定期間過ぎたら削除)

・アーカイブ
S3上のデータを削除でGlacier側のデータも削除
S3には8KBのオブジェクト名とメタデータのみ保管

・復元
オブジェクトごと
一時的にS3の低冗長化ストレージに指定日数複製(Glacierと低冗長化ストレージ両方課金)
復元にかかる時間の選択肢は3つ
①Expedited:緊急のアクセス
②Standard:3-5時間。標準的
③Bulk:大量のデータ。5-12時間
それぞれによってコストが異なる

・オブジェクト移動
Standard⇔Standard-IA→Glacier
→Glacier

S3分析
Standard-IAとGlacierどちらにいつ移動すればいいだろうかという疑問に答える可視化ツール
→ライフサイクルポリシーの設定値の参考になる

S3インベントリ
S3のオブジェクトのリストを一気にcsvファイルで取得
スケジュールかも可能

・イベント通知
SNS:メール送信
SQS:キューメッセージの登録
Lambda:ファンクションの実行

・CloudWatchによる監視
ストレージメトリクス:バケット単位。1日単位でのレポート。追加費用なし
リクエストメトリクス:オブジェクト単位。通常のCloudWatch料金

CloudTrailによるAPI(操作ログ。Get, Delete, Putなど)管理
S3への操作ログを収集
監査対象とは別のS3バケットの用意推奨

Logging
バケットに対するアクセスログの出力設定可能

Tag管理
バケット/オブジェクトに対してタグの指定可能

・パフォーマンスの最適化
大きなサイズのファイルをアップロード、ダウンロード
RANGE GETを活用。マルチパートアップロード機能
大量のGETリクエストが発生する場合はCloudFrontを併用することを推奨

Transfer Acceleration(高速ファイル転送サービス)
AWSのエッジネットワークから最適化されたAWSのネットワークを経由する。
S3のデータ転送コストとは別に加算
※通常の転送より高速でない場合は、課金されない

コンテンツ配信サーバ
データをS3に配置、CloudFrontでキャッシュさせる
CloudFrontで静的コンテンツ配信。CloudFrontの料金はかからない
Webサーバーで動的コンテンツは処理

ログ&データハブストレージ
オンプレ:Direct Connectでログデータ収集
外部データソース;Kinesisで収集
AWS;S3に保管。Glacierにアーカイブ
分析:Redshift, EMR, Atenaなど

バックアップ、DR
クロスリージョンでデータの複製を保持
リージョン内でもDR設定

参考URL:https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-2017-amazon-s3

続きを読む

AWS WAF マネージドルールのログをS3にアップロードするlambdaファンクション

作成の経緯・AWS WAF のログは3時間で消えてしまうので自動保存させたい・過去同じ問題をAWS cloudwatch+SNS+Lambdaで自動保存させる環境構築された偉人がいらして、設定方法やlambdaファンクションのソースコードまでアップされていた。参考URL:http://sssslide.com/spe. 続きを読む

CloudFormationテンプレート(JSON) – AWS Config 設定

1. 概要

2. 各リソースメモ

3. JSONテンプレート

AWSConfig.json

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Description" : "AWS Config",
    "Resources" : {
        "S3Bucket" : {
            "Type" : "AWS::S3::Bucket",
            "Properties" : {
                "BucketName" : { "Fn::Join" : [ "", [ "awsconfig-", { "Ref" : "AWS::AccountId" } ] ] }
            }
        },
        "S3BucketPolicy" : {
            "Type" : "AWS::S3::BucketPolicy",
            "Properties" : {
                "Bucket" : { "Ref": "S3Bucket"},
                "PolicyDocument" : {
                    "Version" : "2012-10-17",
                    "Statement" : [
                        {
                            "Sid" : "AWSConfigBucketPermissionsCheck",
                            "Effect" : "Allow",
                            "Principal" : {
                                "Service" : [
                                    "config.amazonaws.com"
                                ]
                            },
                            "Action" : "s3:GetBucketAcl",
                            "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::awsconfig-", { "Ref" : "AWS::AccountId" } ] ] }
                        },
                        {
                            "Sid" : " AWSConfigBucketDelivery",
                            "Effect" : "Allow",
                            "Principal" : {
                                "Service" : [
                                    "config.amazonaws.com"
                                ]
                            },
                            "Action" : "s3:PutObject",
                            "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::awsconfig-", { "Ref" : "AWS::AccountId" }, "/AWSLogs/", { "Ref" : "AWS::AccountId" }, "/Config/*" ] ] },
                            "Condition" : {
                                "StringEquals" : {
                                    "s3:x-amz-acl" : "bucket-owner-full-control"
                                }
                            }
                        }
                    ]
                }
            }
        },
        "SNSTopic" : {
            "Type" : "AWS::SNS::Topic",
            "Properties" : {
                "DisplayName" : "AWS Config Notification Topic",
                "TopicName" : "awsconfig"
            }
        },
        "SNSSubscription" : {
            "Type" : "AWS::SNS::Subscription",
            "Properties" : {
                "Endpoint" : "test@example.com",
                "Protocol" : "email",
                "TopicArn" : { "Ref" : "SNSTopic"}
            }
        },
        "IAMRole" : {
            "Type" : "AWS::IAM::Role",
            "Properties" : {
                "AssumeRolePolicyDocument" : {
                    "Version" : "2012-10-17",
                    "Statement" : [
                        {
                            "Sid" : "",
                            "Effect" : "Allow",
                            "Principal" : {
                                "Service" : "config.amazonaws.com"
                            },
                            "Action" : "sts:AssumeRole"
                        }
                    ]
                },
                "ManagedPolicyArns" : [ "arn:aws:iam::aws:policy/service-role/AWSConfigRole" ],
                "Path" : "/",
                "Policies" : [
                    {
                        "PolicyName" : "awsconfig",
                        "PolicyDocument" : {
                            "Version" : "2012-10-17",
                            "Statement" : [
                                {
                                    "Effect" : "Allow",
                                    "Action" : [
                                        "s3:PutObject*"
                                    ],
                                    "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::awsconfig-", { "Ref" : "AWS::AccountId" }, "/AWSLogs/", { "Ref" : "AWS::AccountId" }, "/*" ] ] },
                                    "Condition" : {
                                        "StringLike" : {
                                            "s3:x-amz-acl" : "bucket-owner-full-control"
                                        }
                                    }
                                },
                                {
                                    "Effect" : "Allow",
                                    "Action" : [ "s3:GetBucketAcl" ],
                                    "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::awsconfig-", { "Ref" : "AWS::AccountId" } ] ] }
                                },
                                {
                                    "Effect" : "Allow",
                                    "Action" : "sns:Publish",
                                    "Resource" : "arn:aws:sns:ap-northeast-1:777813037810:awsconfig",
                                    "Resource" : { "Fn::Join" : [ "", [ "arn:aws:sns:ap-northeast-1:", { "Ref" : "AWS::AccountId" }, ":", { "Fn::GetAtt" : [ "SNSTopic", "TopicName" ] } ] ] }
                                }
                            ]
                        }
                    }
                ],
                "RoleName" : "awsconfig"
            }
        },
        "ConfigConfigurationRecorder" : {
            "Type" : "AWS::Config::ConfigurationRecorder",
            "DependsOn" : [ "S3Bucket", "SNSTopic" ],
            "Properties" : {
                "Name" : { "Fn::Join" : [ "", [ "awsconfig-", { "Ref" : "AWS::AccountId" } ] ] },
                "RecordingGroup" : {
                    "AllSupported" : true,
                    "ResourceTypes" : [],
                    "IncludeGlobalResourceTypes" : true
                },
                "RoleARN" : { "Fn::GetAtt" : [ "IAMRole", "Arn" ] }
            }
        },
        "ConfigDeliveryChannel" : {
            "Type" : "AWS::Config::DeliveryChannel",
            "DependsOn" : [ "S3Bucket", "SNSTopic" ],
            "Properties" : {
                "Name" : { "Fn::Join" : [ "", [ "awsconfig-", { "Ref" : "AWS::AccountId" } ] ] },
                "S3BucketName" : { "Ref" : "S3Bucket" },
                "SnsTopicARN" : { "Ref" : "SNSTopic" }
          }
        }
    },
    "Outputs" : {
        "S3Bucket" : {
            "Description" : "S3 Bucket Name",
            "Value" : { "Ref" : S3Bucket }
        },
        "SNSTopic" : {
            "Description" : "SNS Topic Name",
            "Value" : { "Fn::GetAtt" : [ "SNSTopic", "TopicName" ] }
        },
        "IAMRole" : {
            "Description" : "IAM Role Name",
            "Value" : { "Ref" : IAMRole }
        },
        "ConfigConfigurationRecorder" : {
            "Description" : "Config ConfigurationRecorder Name",
            "Value" : { "Ref" : ConfigConfigurationRecorder }
        },
        "ConfigDeliveryChannel" : {
            "Description" : "Config DeliveryChannel Name",
            "Value" : { "Ref" : ConfigDeliveryChannel }
        }
    }
}

続きを読む

AWS CloudWatchについての記事まとめ

個人メモ用記事

概要

【AWS】CloudWatch
CloudWatch 標準メトリクス(監視項目) 一覧
Black Belt Online Seminar Amazon CloudWatch
AWS Black Belt Techシリーズ Amazon CloudWatch & Auto Scaling
AWS Black Belt Techシリーズ AWS CloudTrail & CloudWatch Logs
AWS Blackbelt 2015シリーズ Amazon CloudWatch & Amazon CloudWatch Logs

ELB

CloudWatchのELB監視項目
あらたにELBを作成する時の、CloudWatch設定メモ
ELBの挙動とCloudWatchメトリクスの読み方を徹底的に理解する
CloudWatchのあるはずのメトリックスが存在しない理由
ELBのHealthyHostCountは全AvailabilityZoneの合計値をモニタリングできない?

EC2

死活監視

AWS CloudWatchでEC2の死活監視

メトリクス監視

新しいCloudWatch AgentでEC2インスタンスのメモリ使用率を監視する
[AWS] CloudWatch でロードアベレージとかメモリ使用量とか監視
ECSクラスタのCPUとメモリをCloudWatchメトリックスで取得してみた
EC2のメモリ使用量とdisk使用量をcollectdでサクッとCloudWatchに登録
AWS CloudWatch で、Amazon Linux のパフォーマンスとログの監視をしてみる
はじめてのCloudWatch(AWS) 〜カスタムメトリクスを作って無料枠でいろいろ監視する〜
AWS CloudWatch シェルだけでPutMetricDataを実行する
AWS CloudWatchでEC2を監視する (プロセス死活監視、ディスク使用率、iノード使用率を監視してアラートメールを送信する)

ログ取得

CloudWatch Logsを使ってログを集める!
CloudWatch Logsのインストール・設定
EC2インスタンスのログをCloudWatchで見る
AWS EC2でメモリ利用率をCloud Watchで監視する
nginxのエラーログをcloudwatch logsに送信する

RDS

Amazon RDS のモニタリング
CloudWatchのRDS監視項目

S3

Amazon CloudWatch を使用したメトリクスのモニタリング
S3のアクセス状況を可視化してみる

Auto Scaling

[翻訳]チュートリアル:CloudWatchアラームによるコンテナインスタンスのスケーリング

アラート設定

CloudWatchでエラーログの内容を通知させたい
CloudWatchアラームでSlackへ通知を行う。
AWS SNS(Amazon Simple Notification Service)の通知設定をしてみる

続きを読む

【2018年】AWS全サービスまとめ その2(開発者用ツール、管理ツール、メディアサービス) | Developers.IO

AWSのリソースおよびAWSで実行しているアプリケーションのモニタリングサービス。メトリクス(CPU使用率やネットワークI/Oなど)やログを収集し(CloudWatch Logs)、SNSやAutoScalingと連携するアラームを作成できる。またAWSリソースの変更イベントを監視し、Lambdaなどのターゲットに対して、リアルタイムに通知が … 続きを読む

AWS EC2 T2 Unlimitedの有効化

T2インスタンスに対してT2 Unlimited機能を有効にしてみた。

T2 Unlimited機能について

T2インスタンスはCPUクレジットを消費してCPU能力を上げるバーストという機能が使用できる。

このバーストはCPUクレジットを消費し尽くした場合は使えなくなってしまうが、消費し尽くした場合でもバーストし続けることができる機能をT2 Unlimitedと呼ぶ。

CPUクレジットの消費量は1vCPU毎1分間に1クレジットで、CPUクレジットの回復量と回復上限はインスタンスタイプごとに違う。

具体的には下記ページの表にまとまっている。

http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/t2-instances.html

バースト / T2 Unlimited詳細

CPUクレジットを消費し尽くした後T2 Unlimitedによりバーストが発生してもすぐには課金されない。

この場合CPUクレジットの回復上限の値までCPUクレジットを使って、初めて課金 (vCPU時間あたり0.05ドル)が発生する。

これらはそれぞれCPUSurplusCreditBalanceとCPUSurplusCreditsChargedというメトリクスで追跡できる。

つまり、バースト発生 → クレジットを使い切る → CPUSurplusCreditBalanceも上限 (CPUクレジットの回復上限の値) まで使い切る → 課金が発生という流れになる。

なおCPUSurplusCreditBalanceを使った場合では、CPUクレジットの回復にはまずCPUSurplusCreditBalanceの返済が必要で、返済が終わった段階からCPUクレジットの回復が始まる。

監視 (アラート)

何らかの問題によりバーストが発生し続けて、T2 Unlimitedの課金が気が付かれずに発生し続けることを避けるため、監視とアラートを設定する。

CloudWatchで設定を行い、アラートは課金が始まった時、つまりCPUSurplusCreditsChargedが0より大きくなった場合にアラートを送信するよう設定する。

設定

下記設定でT2 Unlimitedの設定と監視(アラート)の設定を行った。

  • 対象: T2系インスタンス
  • アラート条件: CPUSurplusCreditsChargedが0より大きくなった時 (5分間隔1回連続)
  • アラート送信先: (既存SNSトピックを使用)

#!/bin/bash
set -euo pipefail

RET=$(aws ec2 describe-instances --query 
      'Reservations[].Instances[].{HostName:Tags[?Key==`Name`].Value|[0],InstanceId:InstanceId}' 
      --filters "Name=instance-type,Values=t2.*" --output text | awk '{ print $1","$2}' | sort | grep -v stg )

for LINES in $RET;
do
        INSTANCE_NAME=$(echo $LINES | awk -F, '{ print $1 }')
        INSTANCE_ID=$(echo $LINES | awk -F, '{ print $2 }')
        echo $INSTANCE_NAME

        aws ec2 modify-instance-credit-specification 
                --instance-credit-specification "[{"InstanceId": "${INSTANCE_ID}","CpuCredits": "unlimited"}]"

        aws cloudwatch put-metric-alarm 
                --alarm-name "$INSTANCE_NAME でのT2 Unlimited課金発生" 
                --namespace AWS/EC2 
                --metric-name CPUSurplusCreditsCharged 
                --dimensions "Name=InstanceId,Value=$INSTANCE_ID" 
                --period 300 
                --statistic Average 
                --threshold 0 
                --comparison-operator GreaterThanThreshold 
                --evaluation-periods 1 
                --alarm-actions (既存SNSトピックを使用) 
                --ok-actions (既存SNSトピックを使用)
        echo "-----------"
done

参考
https://aws.amazon.com/jp/blogs/news/new-t2-unlimited-going-beyond-the-burst-with-high-performance/

続きを読む

Lambdaファンクションのエラーを通知したい(ファンクション名も込みで)

要件

  • Lambdaファンクションのエラーが発生したら関係者へ通知したい
  • どのファンクションがエラーになったのかを通知内容に含めたい
  • Lambdaファンクションは頻繁に追加したり削除したりするので、ファンクションごとに設定を追加/変更するのは避けたい
  • 既存のLambdaファンクションの設定はなるべくいじりたくない

対応案の検討

  1. CloudWatchのメトリクス(Lambda>全ての関数>エラー(Errors)) にアラームをセットしてSNS経由で通知

    • メリット:設定が最初の一回ですむ。ファンクションの追加や削除の影響を受けない
    • デメリット:どのファンクションがエラーになったかがわからない
  2. CloudWatchのメトリクス(Lambda>関数の名称別>[ファンクション名]>エラー(Errors))にアラームをセットしてSNS経由で通知
    • メリット:ファンクション名がわかる
    • デメリット:ファンクションごとに設定が必要。CloudWatch Alarmが増えるとコストもかかる(誤差だけど…)
  3. Lambdaのデッドレターキュー(DLQ)からSNSで通知
    • メリット:ファンクション名がわかる
    • デメリット
      • すでに別の用途でDLQを使っている場合の対応が難しい。
      • ファンクションごとに設定が必要。
      • Lambdaファンクションの実行ロールにDLQへのアクセス権が必要。
      • リトライが全て失敗した場合のみ検出可能。
      • DLQを使えるのはイベント駆動のファンクションのみ。

いずれも要件にピタリとフィットしてくれない。案1が最も簡単だが肝心のファンクション名が通知されないため採用できない。
案2は考え方はシンプルだがすでに大量のLambdaファンクションを抱えている状況では対応が面倒。ファンクション追加ごとにアラームの設定も必要になるため、設定作業漏れのリスクも。(サーバーレスアプリのデプロイ自動化に組み込めるなら別だが…。)
案3もなかなか魅力的。やりたいことはこちらで紹介されている→【新機能】AWS LambdaがDead Letter Queueをサポートしました だがデメリットを許容できない場合は採用できない。
つまり、「他にもっといい案は…?」となる。

もっといい(と思われる)案

CloudWatchのメトリクスデータ群から一発でエラーの発生しているファンクション名を取得できればいいのだが、サクっとはできそうにない。
仕方がないので以下の処理を実行するLambdaファンクションを作成する。

  1. Lambdaファンクションのリストを取得
  2. Lambdaファンクションごとに、CloudWatchのメトリクスをサーチして直近のエラー発生有無を確認
  3. 全ファンクションのサーチが終わったら結果をSNSへPublish

「CloudWatchのメトリクス(Lambda>全ての関数>エラー(Errors))」のメトリクスにセットしたアラームによって何らかのファンクションのエラー発生を検知し、これをトリガーにしてこのファンクションを起動させる。

1. Lambdaファンクションのリストを取得

まずは特定のリージョンに所属するLambdaのファンクション名をリストで取得する。

sample.py
#Lambdaのファンクション名のリストを返す。
def get_function_names(region_name):
    lmd = boto3.client('lambda',region_name=region_name)
    response = lmd.list_functions()
    function_names = [d['FunctionName'] for d in response['Functions']]
    return function_names

2. Lambdaファンクションごとに、CloudWatchのメトリクスをサーチして直近のエラー発生有無を確認

sample.py
    cloudwatch = boto3.client('cloudwatch',region_name = region)

    #FunctionNameごとに直近時間帯のメトリクスデータを取得
    errorpoints = []
    for function_name in function_names:
        datapoints = cloudwatch.get_metric_statistics(
            Namespace  = namespace,
            MetricName = metric,
            StartTime  = timefrom,
            EndTime    = timeto,
            Period     = 300,
            Dimensions = [function_name],
            Statistics = ['Sum']
        )
        #SUM > 0の場合はエラーが発生している。
        #エラー発生のメトリクスデータを抽出し、errorpointsリストに追加する。
        errorpoint = list(filter(lambda x: x['Sum'] > 0, datapoints['Datapoints']))
        if len(errorpoint) > 0:
            errorpoints.append({"FunctionName": function_name['Value'], "Datapoints": errorpoint})

    #Timestampの逆順(=新しい順)でソート
    sortedList = sorted(errorpoints, key=lambda x:x['Datapoints'][0]['Timestamp'],reverse=True)

function_names は前述の get_function_names 関数の戻り値で、Lambdaファンクション名のリスト。namespace,metric,timefrom,timetoはいずれもCloudWatchメトリクスを抽出するためのフィルタ条件だが、このLambdaファンクションがトリガーとして受信するSNSのメッセージ内容から導出できる。

結果としてsortedListには直近時間帯に発生したLambdaファンクション名とDatapointsが含まれる。Datapointsには

Datapoints.json
[
    {
        "FunctionName": "Test-LambdaFunction-Name",
        "Datapoints": [
            {
                "Timestamp": datetime.datetime(2017, 12, 20, 12, 56, tzinfo=tzlocal()),
                "Sum": 1.0,
                "Unit": "Count"
            }
        ]
    }
]

というふうに、ファンクション名のほかに時刻やメトリクスデータ(エラー発生件数)が含まれる。

3. 全ファンクションのサーチが終わったら結果をSNSへPublish

ここは好みで実装すれば良いと思うが、SNSのトピックARN、タイトル、メッセージ本文をセットしてSNS Publishを実行する。

sample.py
    #件名:直近でエラーになったFunctionNameを件名に含める
    title = alarm_name + " : " + new_state + " : " + sortedList[0]['FunctionName']

    #本文:直近時間帯でエラーになったすべてのFunctionNameとエラー件数を本文に含める
    message = description+ "nn"
    for e in sortedList:
        #タイムスタンプは日本時間に変換する
        date = e['Datapoints'][0]['Timestamp'] + datetime.timedelta(hours=9)
        message = message + 'n' + str(date)[:19] + " : " + str(int(e['Datapoints'][0]['Sum'])) + e['Datapoints'][0]['Unit'] + ' : ' + e['FunctionName']

    #SNS Publish
    try:
        response = sns.publish(
            TopicArn = topicarn,
            Message  = message,
            Subject  = title
        )

    except Exception as e:
        print(e)
        raise e

最終的な構成

CloudWatch Alarm -> SNS -> Lambda -> SNS
CloudWatch Alarmは「CloudWatchのメトリクス(Lambda>全ての関数>エラー(Errors))」のメトリクスに紐付け、何らかのLambdaファンクションでエラーが発生したら発動する。一つ目のSNSはLambdaファンクションを呼び出すためのもの。二つ目のSNSはCloudWatchメトリクスから直近時間帯にエラーになったLambdaファンクションのメトリクスを抽出し、いい感じに整形した文面を関係者へ通知するためのもの。

スクリプト

(後日掲載の予定・・・)

参考

続きを読む

Glueの使い方的な⑥(監視モニタリング)

監視モニタリング概要

現状整っているとは言い難いので、他のサービスも含めた監視について考えてみる

全体の流れ

  • CloudWatchメトリクス
  • CloudWatchイベント
  • CloudWatchログ
  • APIで状態取得
  • 何を監視すべきか

CloudWatchメトリクス

ない

今後は追加されてくるかと思います

CloudWatchイベント

バージニアには以下のイベントがあります。
現状、”Job State change”と”Crawler state change”の2つ

スクリーンショット 0030-01-04 18.12.26.png

Job State changeはステートの全ての変化のみ

スクリーンショット 0030-01-04 18.12.39.png

Crawler state changeは任意のステートの変化
Failed
Started
Succeeded

スクリーンショット 0030-01-04 18.12.54.png

これらのイベントをフックに、Lambda動かしたり、SNSで通知したり、StepFunction動かしたり、SQSにキューしたりできます。

ただ、東京とアイルランドにはこのGlueのイベントがありません↓

スクリーンショット 0030-01-04 18.28.00.png

CloudWatchログ

GlueはCloudWatchに出力します。ログに関してはCloudWatchのログ監視が使えます
出力ログは主にSparkのログを”error”と”output”で2種類です

一般的なCloudWatchのログに対してのアラーム設定手順になります

CloudWatchのログの画面に行く。
“aws-glue/jobs/error”のロググループにチェックを入れ、”メトリクスフィルタ”ボタンをクリック

スクリーンショット 0030-01-06 10.18.42.png

ログメトリクスフィルタの定義の画面です
フィルタする文字列パターンに文字列を入れ”パターンのテスト”をクリックすればフィルタの確認が出来ます。結果は一番下に出力されます。
テストするログデータも変更できます。
画面はテストなので”INFO”でフィルタしています。サンプルから1件フィルタできてることがわかります。
エラーログなのにINFOが出てます。。
確認ができたら右下の”メトリクスの割り当て”をクリックします。

スクリーンショット 0030-01-06 10.20.48.png

フィルタの名前、メトリクス名などを任意の名前で入力して、右下の”フィルタの作成”をクリックします

スクリーンショット 0030-01-06 10.20.55.png

フィルタが作成されました

スクリーンショット 0030-01-06 10.21.43.png

最初のCloudWatchログの画面に行くと、”aws-glue/jobs/error”ロググループの右側に”1フィルタ”となっています
ここをクリックします

スクリーンショット 0030-01-06 10.22.19.png

右上の”アラームの作成”をクリックします

スクリーンショット 0030-01-06 10.22.44.png

いつものアラーム設定画面となります

スクリーンショット 0030-01-06 10.23.03.png

APIで状態取得

Glueの使い方的な③(CLIでジョブ作成)“(以後③と書きます)でも触れたようにAPIにアクセスして状態の取得ができます。他のAWSリソースももちろん同様のことができます。

get系のAPIが使えます

クローラーの状態取得

例えばクローラーのgetで取得できる情報は

$ aws glue get-crawler --name se2_in0
{
    "Crawler": {
        "CrawlElapsedTime": 0, 
        "Name": "se2_in0", 
        "CreationTime": 1514874041.0, 
        "LastUpdated": 1514874041.0, 
        "Targets": {
            "JdbcTargets": [], 
            "S3Targets": [
                {
                    "Path": "s3://test-glue00/se2/in0", 
                    "Exclusions": []
                }
            ]
        }, 
        "LastCrawl": {
            "Status": "SUCCEEDED", 
            "LogStream": "se2_in0", 
            "MessagePrefix": "903fa0e1-2874-4b50-a686-660d2da54004", 
            "StartTime": 1515146760.0, 
            "LogGroup": "/aws-glue/crawlers"
        }, 
        "State": "READY", 
        "Version": 1, 
        "Role": "test-glue", 
        "DatabaseName": "se2", 
        "SchemaChangePolicy": {
            "DeleteBehavior": "DEPRECATE_IN_DATABASE", 
            "UpdateBehavior": "UPDATE_IN_DATABASE"
        }, 
        "TablePrefix": "se2_", 
        "Classifiers": []
    }
}

ここから状態だけを取ってきたければ以下のような感じでステータスを見て成否判定することは出来ます。

$ aws glue get-crawler --name se2_in0 | jq -r .Crawler.LastCrawl.Status
SUCCEEDED

ジョブの状態取得

ジョブも同じ要領ですが、ステータスを得るためにget-job-runにRunIDを渡して上げる必要があります

RunIDはstart-jobでリターン値で得られます

$ aws glue start-job-run --job-name se2_job0
{
    "JobRunId": "jr_711b8b157e3b36a1dc1a48c87c5e8b00c509150cc4f9d7d7106009e57f2cac9b"
}

また、get-job-runsでジョブの履歴から取ることもできます

$ aws glue get-job-runs --job-name se2_jobx 
{
    "JobRuns": [
        {
            "LastModifiedOn": 1514440793.923, 
            "StartedOn": 1514440639.623, 
            "PredecessorRuns": [], 
            "Attempt": 0, 
            "JobRunState": "SUCCEEDED", 
            "JobName": "se2_jobx", 
            "Arguments": {
                "--job-bookmark-option": "job-bookmark-disable"
            }, 
            "AllocatedCapacity": 10, 
            "CompletedOn": 1514440793.923, 
            "Id": "jr_1b3c00146b02e36e4682f352a084a2fd37931967346b44a7c39ad182347957d3"
        }, 
        {
            "LastModifiedOn": 1514440548.4, 
            "StartedOn": 1514440394.07, 
            "PredecessorRuns": [], 
            "Attempt": 0, 
            "JobRunState": "FAILED", 
            "ErrorMessage": "An error occurred while calling o54.getCatalogSource. No such table: se4.se04_in", 
            "JobName": "se2_jobx", 
            "Arguments": {
                "--job-bookmark-option": "job-bookmark-disable"
            }, 
            "AllocatedCapacity": 10, 
            "CompletedOn": 1514440548.4, 
            "Id": "jr_aca8a8c587b0986d040aba7eadc7b216e83db409f842cb9d2912c400b181c907"
        }
    ]
}

取得できたRunIDを使ってステータスを取ります

$ aws glue get-job-run --job-name se2_job0 --run-id jr_dff0ac334e5c5bf3043acc5158f9c3bc1f9c8eae048e053536581278ec34a063 
{
    "JobRun": {
        "LastModifiedOn": 1514875561.077, 
        "StartedOn": 1514875046.406, 
        "PredecessorRuns": [], 
        "Attempt": 0, 
        "JobRunState": "SUCCEEDED", 
        "JobName": "se2_job0", 
        "Arguments": {
            "--job-bookmark-option": "job-bookmark-disable"
        }, 
        "AllocatedCapacity": 10, 
        "CompletedOn": 1514875561.077, 
        "Id": "jr_dff0ac334e5c5bf3043acc5158f9c3bc1f9c8eae048e053536581278ec34a063"
    }
}
$ aws glue get-job-run --job-name se2_job0 --run-id jr_dff0ac334e5c5bf3043acc5158f9c3bc1f9c8eae048e053536581278ec34a063 | jq .JobRun.JobRunState -r
SUCCEEDED

ポーリング型とはなりますが、定期的に状態を確認するというやり方もあります

何を監視すべきか

Glueはバッチ処理で多くの場合ジョブフローを形成する中の一部として使われると思います

何を見るべきかはいろんな意見や視点がありますし業務のサービスレベルでも違うので一概に言えないですが、お勧めとして

まずはジョブの成否を見ます。

“CloudWatchイベント”でも述べたように現状Glueジョブのイベントタイプは”全てのステータスチェンジ”の1つしかないので、ジョブが失敗したらアラートを飛ばすということがCloudWatchだけだと出来ません。本記事の”APIで状態取得”で述べたような方法を使って、ジョブ実行した後非同期でステータスを確認する。またはCloudWatchイベントでJobStateChangeのイベントでLambdaを起動してGlueのAPIを叩いてステータス確認するとよりタイムラグない監視になると思います。

多くの場合ジョブフローを形成し、1つのジョブステップを処理の最小単位とするので、その成否の確認はロールバックし易さや業務影響などの考慮が入っています。その単位でジョブの失敗に気づき、そしてより詳細な調査が必要な場合にログを確認していくのが1つのパターンと思います
このジョブフローをサーバーレスで行うサービスにAWSのStepFunctionというサービスがあります。Glueとも相性がいいのでまた今度書いてゆきます

エラーは吐かないが実行時間が長い

これも気づきたいポイントになると思います。
ジョブのスタート時間は取れるので、そこから経過した時間を算出し、通常1時間のジョブが2時間を超えたらアラートを上げる
などもいいと思います。

$ aws glue get-job-run --job-name se2_job0 --run-id jr_dff0ac334e5c5bf3043acc5158f9c3bc1f9c8eae048e053536581278ec34a063 | jq .JobRun.StartedOn -r
1514875046.406

ログ監視

上記の監視運用を繰り返していくと、ジョブが失敗してログ調査をしてこのログは検知したいといった知見が溜まっていきます。
その場合は本記事の”CloudWatchログ”でも述べたやり方でログ監視してください。ジョブが失敗することには変わらないですがログのアラートも飛んでくることで既知の問題であることが即座にわかりトラブルシュートが格段に早まります

To Be Continue

  • StepFunctionでGlueのジョブフローを作るを書く

こちらも是非

CloudWatchログ監視
http://www.d2c-smile.com/201609137826#a1

続きを読む