AWS/Azure

AWS ALBのログを管理するためのCloudWatch Logs設定方法

当記事では、ログ収集ソフトLogStare Collector(LSC)のCloudWatch Logs収集機能を用いてApplication Load Balancer (ALB)のログを取得する方法について記載します。

ALBはCloudWatch Logsに直接ログを出力することが出来ず、S3にしか出力できません。そのため、S3に出力されたログを、Lambdaを用いてCloudWatch Logsに集約し、LSCで収集します。
また、LSCをログ分析基盤であるLogStare Reporter(LSR)、LogStare Quint(LSQ)と連携することで、レポートを作成することができ、簡単にログを分析することが出来ます。

本記事ではALBは構築済みと想定し、手順を記載いたします。

  • 当記事では複数のAWSサービスを利用します。それぞれ利用料金が発生いたしますので、予めご了承ください。
  • 2024年3月に行った検証をもとに記載しており、詳細な手順等についてはAWSのドキュメント等についてもあわせてご参照ください。
  • 記載内容により発生したいかなる損害、想定外のAWS利用料金の発生等について、一切の責任を負いかねます。予めご了承ください。
  • 記載手順を実行後の完成後の状態は以下の通りです。

S3の構築

まず、ALBのログを出力するためのS3を構築します。

コンソールから「S3」サービスを選択し、「バケットを作成」を押下します。

 

「AWSリージョン」はALBと同じリージョンに作成します。今回は「アジアパシフィック(東京)ap-northeast-1」を選択しました。

バケット名は任意の名前を入力します。

その他はデフォルトで結構です。設定が完了したらページ下部にある「バケットを作成」を押下します。

 

次に、ALBのログを出力できるようにするためにS3 バケットにポリシーをアタッチします。
作成したバケット名をクリックし、「アクセス許可」>「バケットポリシー」>「編集」を押下します。

その後は、「ステップ 2: S3 バケットにポリシーをアタッチする」を参考に、ポリシーを作成してください。

ALBのログ出力設定

次に、ALBの設定を変更し、ログがS3に出力されるようにします。

対象のALBを押下します。

 

「属性」の「編集」を押下します。

 

「モニタリング」の「アクセスログ」をオンにし、「S3 URI」に作成したS3をフォーマットに沿って指定します。設定が完了したら「変更を保存」を押下します。

ここまで完了するとS3にALBのログが出力されるようになります。(アクセスしてからログが出力されるまでにはある程度時間がかかります。)

CloudWatch Logsの構築

次に、ログを出力するCloudWatch Logsを構築します。

初めにロググループを作成します。
コンソールから「CloudWatch」サービスを選択し、「ログ」>「ロググループ」から「ロググループを作成」を押下します。

 

「ロググループ名」に任意の名前を入力し、特に設定は変更せずに「作成」を押下します。

 

次にログストリームを作成します。作成したロググループを押下し、「ログストリームの作成」を押下します。

 

任意のログストリーム名を入力し、「Create」を押下します。

Lambda関数の構築

次に、S3にあるログファイルをCloudWatch Logsに出力するLambda関数を作成します。

コンソールより「Lambda」サービスを選択し、メニューより「関数」を押下します。
右上の「関数の作成」を押下します。

 

「関数名」に任意の名前を入力し、ランタイムは「Python 3.9」を選択します。その他はデフォルトで結構です。
設定が完了したら「関数の作成」を押下します。

 

次にトリガーの設定を行います。

「トリガーを追加」を押下します。

「ソース」にS3を選択し、「Bucket」は作成したS3を指定します。
「Event types」はPUTを設定します。
「Recursive invocation」にチェックを入れ、「追加」を押下します。

トリガーが作成され、S3にログファイルが出力されたタイミングでLambda関数が実行されるようになりました。

 

次に、S3にあるログファイルをCloudWatch Logsに出力するコードを入力します。

「コード」を押下し、以下のコードを入力します。logGroupNameとlogStreamNameは環境に応じて書き換えてください。

※コードは以下のサイトを参考にしました。

import json
import urllib.parse
import boto3
import time
import gzip
import os

print('Loading function')

s3 = boto3.client('s3')
logs_client = boto3.client('logs')

## 変数
logGroupName = ※環境によって書き換えてください
logStreamName = ※環境によって書き換えてください

def lambda_handler(event, context):
    # Get the object from the event and show its content type
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    try:
        file_name = os.path.basename(key)  
        file_path = os.path.join('/tmp', file_name)  
        
        s3.download_file(bucket, key, file_path)
        
        
        with gzip.open(file_path, 'rt') as f:
            file_content=f.read()
        
        list = file_content.splitlines()

        for item in list:
            #keyを'/'で区切り、key_splittedに代入。
            key_splitted=key.split('/')
            
            #AWSLogsという文字列を探し、その添え字を代入
            AWS_Logs_index=key_splitted.index('AWSLogs')
            
            #key_splittedのAWSLogsの添え字に7を足したものを'_'で区切り、key_splitted_7に代入
            key_splitted_7=key_splitted[AWS_Logs_index+7].split('_')
            
            #prefixがない場合はNoneにする。AWS_Logs_indexが0ではない場合はprefixが存在する。
            prefix='None'
            if AWS_Logs_index >0:
                prefix=''
                for i in range(AWS_Logs_index):
                    prefix=prefix+'/'+key_splitted[i]
            
            AWS_Logs=key_splitted[AWS_Logs_index]
            aws_account_id=key_splitted[AWS_Logs_index+1]
            region=key_splitted[AWS_Logs_index+3]
            date=key_splitted[AWS_Logs_index+4]+'/'+key_splitted[AWS_Logs_index+5]+'/'+key_splitted[AWS_Logs_index+6]
            #load_balancer_id=key_splitted_7[3].split('.')[1]+'.'+key_splitted_7[3].split('.')[2]
            load_balancer_id=key_splitted_7[3].lstrip('app.')
            end_time=key_splitted_7[4]
            ip_address=key_splitted_7[5]
            
            #key_splitted_7の添え字が6のものを'.'で区切り、それの添え字が0のものを代入
            random_string=key_splitted_7[6].split('.')[0]
            
            item_inserted=' '.join([bucket,prefix,AWS_Logs,aws_account_id,region,date,load_balancer_id,end_time,ip_address,random_string,item])
            put_logs(logs_client,logGroupName,logStreamName,item_inserted)
        
        #delete file in tmp
        if os.path.isfile(file_path):
            os.remove(file_path)
       
        return 'function complete'
       
    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

def put_logs(client, group_name, stream_name_prefix, message):
    try:
        exist_log_stream = True
        log_event = {
            'timestamp': int(time.time()) * 1000,
            'message': message
        }
        sequence_token = None
        
        try:
            if exist_log_stream == False:
                #変数create_log_stream_responseは使用しないが、公式ドキュメントに記載されている書き方に合わせるため、定義。
                create_log_stream_response = client.create_log_stream(
                    logGroupName = group_name,
                    logStreamName = stream_name_prefix)
                exist_log_stream = True
            if sequence_token is None:
                #変数put_log_events_responseは使用しないが、公式ドキュメントに記載されている書き方に合わせるため、定義。
                put_log_events_response = client.put_log_events(
                    logGroupName = group_name,
                    logStreamName = stream_name_prefix,
                    logEvents = [log_event])
            else:
                put_log_events_response = client.put_log_events(
                    logGroupName = group_name,
                    logStreamName = stream_name_prefix,
                    logEvents = [log_event],
                    sequenceToken = sequence_token)
            
        except client.exceptions.ResourceNotFoundException as e:
            exist_log_stream = False
        except client.exceptions.DataAlreadyAcceptedException as e:
            sequence_token = e.response.get('expectedSequenceToken')
        except client.exceptions.InvalidSequenceTokenException as e:
            sequence_token = e.response.get('expectedSequenceToken')
        except Exception as e:
            print(e)
    except Exception as e:
        print(e)

入力が完了したら「Deploy」を押下します。

 

次に、Lambda関数に権限を付与します。

設定>アクセス権限から「ロール名」を押下します。

 

IAMの設定画面に遷移しますので、「設定を追加」の「インラインポリシーを作成」を押下します。

 

ここで、S3に「GetObject」、CloudWatch Logsに「PutLogEvents」の権限を付与します。
ARNは環境にあったものを設定してください。
ポリシー名に任意の名前を入力し、「ポリシーの作成」を押下します。

ここまで完了すると、CloudWatch Logsにログが出力されるようになります。

※タイムアウトについて

S3に出力されるログファイルによっては処理時間が長くなってしまい、タイムアウトが発生して処理が中断される場合があります。
タイムアウトが発生している場合には、CloudWatch Logsのlambda関数自身のログの中に「Task timed out」というエラーメッセージが出力されます。
その際は、対象のlambda関数の「設定」>「一般設定」からタイムアウト時間を適宜延長してください。

LogStare Collectorの構築

次に、CloudWatch Logsのログを収集できるようにLSCを構築します。

詳しい設定方法は「LogStare CollectorでのAWS WAFログの取得方法とログレポート」の「LogStare Collector側の設定」をご参照ください。

ログレポート

LSCをLogStare Reporter(LSR) または LogStare Quint(LSQ) に紐づけると、ログレポートを生成することが出来ます。以下が作成例です。

アクセスページ別リクエスト集計

アクセスされたページをまとめたレポートです。
アクセスされたホスト名、URI、ポート番号を把握することが出来ます。

アクセスページ別ALB-Target応答集計

アクセスされたページの応答をまとめたレポートです。
アクセスされたホスト名、URI、ポート番号だけでなく、ALBとホストのステータスコードを確認することが出来ます。

アクセスページ別リクエスト処理時間平均集計

アクセスされたページの処理時間をまとめたレポートです。
アクセスされたホスト、URI、ポート番号だけでなく、リクエストやレスポンスの処理時間も確認することが出来ます。

おわりに

今回は、LSCでのALBログの取得に向けたS3、CloudWatch Logs、Lambdaの構築方法と、LSR、LSQのレポート作成例をご紹介しました。
ALBのログをLSCに出力するためにはやや手間がかかりますが、LSR、LSQでレポート化することで、容易にログを分析出来るようになります。
実際にLSQの操作を行うことが出来るデモサイトがございますので、ぜひご覧ください。
ALBのログ管理にLSC、LSR、LSQをご活用いただけたら幸いです。

記載されている会社名、システム名、製品名は一般に各社の登録商標または商標です。

当社製品以外のサードパーティ製品の設定内容につきましては、弊社サポート対象外となります。

LSC v2.3.7 build 240312リリースノート前のページ

CloudFrontなどのS3にしか出力できないログをCloudWatch Logsに集約してLSCで収集する方法次のページ

ピックアップ記事

  1. ログフォワーダー「okurun.jar」について
  2. IoT機器「Raspberry pi」とLogStare Collectorで温…
  3. Zabbixヒストリデータのレポート生成について
  4. 自社製品をAMIにしてAWSマーケットプレイスへ出品

関連記事

  1. AWS/Azure

    NetworkFirewall関連メトリクス一覧

    当記事では、CloudWatch監視にて対応しているNetworkFi…

  2. AWS/Azure

    ApplicationELB関連メトリクス一覧

    当記事では、CloudWatch監視にて対応しているApplicati…

  3. NW機器

    AWS Transit Gateway で集約したトラフィックをFortiGate で精査する

    当記事では、AWS Transit Gateway を用いて通信を集約…

  4. AWS/Azure

    LogStare CollectorでのVPCフローログの取得方法とログレポート

    当記事では、LogStare CollectorでのVPCフローログの…

  5. AWS/Azure

    PrivateLinkEndpoints関連メトリクス一覧

    当記事では、CloudWatch監視にて対応しているPrivateLi…

  6. Windows/Linux

    CloudWatch Logs収集に必要な権限について

    当記事では、CloudWatch Logs収集に必要な権限について記載…

月額200円でM356の監査ログの運用レベルUP LogStare M365

AWSのログ分析・モニタリングに 次世代のマネージド・セキュリティ・プラットフォーム LogStare

  1. 実践記事

    DNSキャッシュポイズニングやってみた
  2. デフォルト画像イメージ

    FortiGate

    FortiGateのSD-WAN設定について
  3. ログ分析・監視テクニック

    nProbeであらゆる通信をログに記録し可視化する
  4. SNMPを触ってみた

    ログ分析・監視テクニック

    SNMPとは?新入社員が生まれてはじめて触ってみた!
  5. NW機器

    PaloAltoのIPsec IKEv1 Phase1におけるトラブルシューティ…
PAGE TOP