目次

概要

オンプレミスで稼働しているサーバのログをS3に収集して、解析を行う環境を構築する。

簡単な処理の流れ

イメージ

TODO: TODO

AWS側の処理作成

ファイル/フォルダ構成

/collectlog2aws
 /template.yml
 /src
  /index.py
  /collectlog2aws
   /__init__.py
   /analyze.py
   /genurl.py

 
・・・ cloudformationテンプレート
・・・ Lambdaソース格納用
・・・ Lambda本体(メインハンドラ)
・・・ 一応パッケージ化しておく

・・・ CSV変換 及び ログ解析処理
・・・ 署名付きURL生成処理

署名付きURLの生成のLambda作成

CSVに変換するLambda作成

CloudFormationテンプレートの作成

S3バケット 及び Lambdaを作成/デプロイする為の CloudFormationテンプレートを作成する
※生ログ格納用S3バケットは、ログがPUTされたらCSV変換Lambdaが起動するように設定しておく
※署名付きURL生成用の処理はオンプレミス側のサーバから要求があった時に起動できるようにAPI Gateway 経由で起動する

template.yml

WSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "Stack for generate pre signed url and analyze log when put to s3."

Resources:

  # ログファイルアップロード用の署名付きURL生成するLambda
  GenerateSignedUrlFunc:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: GenerateSignedUrl
      Description: "サーバログ用のs3バケットの署名付きURLを生成する"
      Handler: index.generate_url
      Runtime: python3.6
      CodeUri: ./src
      MemorySize: 128 
      Timeout: 60
      Events:
        GenUrlApi:
          Type: Api 
          Properties:
            Path: /
            Method: get 
      Role: !GetAtt GenerateSignedUrlFuncRole.Arn

  # ログファイルアップロード用の署名付きURLを生成するLambda用のロール
  GenerateSignedUrlFuncRole:
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: GenerateSignedUrlFuncRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "GenerateSignedUrlPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - s3:PutObject           # 生ログ格納用のs3バケットにログをputする為の権限を付与しておく
                Resource: "*"              # TODO: 対象のバケットを絞る(循環参照への対応が必要)

  # アップロードされた生ログを解析するLambda
  AnalyzeLogFunc:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: AnalyzeLog
      Description: "アップロードされたログをフォーマット 及び 解析する"
      Runtime: python3.6
      Handler: index.analyze_log
      CodeUri: ./src
      MemorySize: 128 
      Timeout: 60
      Role: !GetAtt AnalyzeLogFuncRole.Arn

  # アップロードされた生ログを解析するLambda用のロール
  AnalyzeLogFuncRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: AnalyzeLogFuncRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "AnalyzeLogFuncPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - s3:GetObject           # 生ログ格納用のs3バケットからログをgetする為の権限を付与しておく
                  - s3:PutObject           # 解析済ログ格納用のs3バケットにログをputする為の権限を付与しておく
                Resource: "*"              # TODO: 対象のバケットを絞る(循環参照への対応が必要)

  # サーバログ解析用Lambdaへのアクセス許可
  AnalyzeLogFuncPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt AnalyzeLogFunc.Arn
      Principal: "s3.amazonaws.com"
      SourceArn: !Join
                  - ""
                  - - "arn:aws:s3:::"
                    - !Sub 'serverlogs-${AWS::AccountId}'

  # サーバ生ログ格納用バケット
  ServerlogBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain    # スタック削除時にバケットを削除しない
    DependsOn:
      - AnalyzeLogFuncPermission
    Properties:
      BucketName: !Sub 'serverlogs-${AWS::AccountId}'
      # 生ログがputされた時に自動的にログ解析用Lambdaを実行する
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: "s3:ObjectCreated:Put"
            Function: !GetAtt AnalyzeLogFunc.Arn
            Filter:
              S3Key:
                Rules:
                  - Name: suffix
                    Value: log

  # 解析済みログ格納用バケット
  AnalyzedlogBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain    # スタック削除時にバケットを削除しない
    Properties:
      BucketName: !Sub 'analyzedlogs-${AWS::AccountId}'

Outputs:
  # 署名付きURL生成用のAPIエンドポイントをエクスポートしておく
  GenerateSignedUrl:
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
    Export:
      Name: GenerateSignedUrl

awsbuild.sh

※ CloudFormation実行用のシェル
#!/bin/bash

CURRENT_DIR=`dirname $0`
MODE=$1

STAGE=dev
REGION=
FORCE=
while [ "$1" != "" ]; do
  if [ "$1" == "--region" ]; then
    shift
    REGION=$1
  fi
  if [ "$1" == "--env" ]; then
    shift
    STAGE=$1
  fi
  if [ "$1" == "--force" ]; then
    FORCE=y
  fi
  shift
done

# テンプレートファイル
TEMPLATE=template.yml

# スタック名 ( gitプロジェクトの場合はgitリポジトリ名、それ以外の場合はフォルダ名をスタック名とする )
cd $CURRENT_DIR
STACK_NAME=`basename \`pwd\``
if [ -e ".git" ]; then
    STACK_NAME=`cat .git/config | grep url | head -1 | awk -F"/" '{print $NF}'`
fi
STACK_NAME=`echo $STACK_NAME | sed 's/_/-/g' | sed 's/Repo$/Stack/'`
STACK_NAME=${STACK_NAME}-${STAGE}

# アカウントIDの取得
ACCOUNT_ID=`aws sts get-caller-identity | grep Account | awk '{print $2}' | sed -e "s/[^0-9]//g"`

# リージョン指定
if [ "${REGION}" == "" ]; then
  REGION=`aws configure list | grep "region" | awk '{print $2}'`
fi
REGION_PARAM="--region ${REGION}"

# スタック作成時のイベント確認
if [ "${MODE}" == "events" ]; then
    echo "Display events of Stack: ${STACK_NAME}"
    echo `date "+%Y-%m-%d %H:%M:%S"`" - START"
    aws cloudformation describe-stack-events --region $REGION --stack-name $STACK_NAME
    echo `date "+%Y-%m-%d %H:%M:%S"`" - END"
    exit 0
fi

# 削除
if [ "${MODE}" == "delete" ]; then
    if [ "$FORCE" != "y" ] && [ "$STAGE" == "prod" ]; then
      echo ""
      read -p "Delete Stack in production environment? (y/n): " yn
      if [ "$yn" != "y" ]; then
        echo "\nDelete Stack Canceled."
        echo ""
        exit 0
      fi
    fi
    echo "Delete Stack: ${STACK_NAME}"
    echo `date "+%Y-%m-%d %H:%M:%S"`" - START"
    aws cloudformation delete-stack --region $REGION --stack-name $STACK_NAME
    aws cloudformation wait stack-delete-complete --region $REGION --stack-name $STACK_NAME
    echo `date "+%Y-%m-%d %H:%M:%S"`" - END"
    exit 0
fi

# 登録/更新
if [ "${MODE}" == "deploy" ]; then
    if [ "$FORCE" != "y" ] && [ "$STAGE" == "prod" ]; then
      echo ""
      read -p "Create/Update Stack in production environment? (y/n): " yn
      if [ "$yn" != "y" ]; then
        echo "Create/Update Stack Canceled."
        echo ""
        exit 0
      fi
    fi
    echo "Create/Update Stack: ${STACK_NAME}"
    echo `date "+%Y-%m-%d %H:%M:%S"`" - START"

    # S3バケットがない場合は作る(バケット名は世界で唯一である必要がある為、末尾にアカウントID等を付与しておく)
    #BUCKET_NAME=stack-${STACK_NAME}-${ACCOUNT_ID}
    BUCKET_NAME=cf-templates-${REGION}-${ACCOUNT_ID}
    BUCKET_COUNT=`aws s3api list-buckets --region $REGION | grep -e "\"${BUCKET_NAME}\"" | wc -l | awk '{print $1}'`
    if [ "${BUCKET_COUNT}" == "0" ]; then
        echo Create s3 bucket: ${BUCKET_NAME}
        if [ "${REGION}" == "us-east-1" ]; then
          # es-east-1 の場合は LocationConstraint の指定なしで作成
          aws s3api create-bucket --region $REGION --bucket $BUCKET_NAME
        else
          aws s3api create-bucket --region $REGION --create-bucket-configuration "{\"LocationConstraint\": \"${REGION}\"}" --bucket $BUCKET_NAME
        fi
    fi  

    # 検証&パッケージング&デプロイ(成功時は作成したAPIのURIを表示する)
    #aws cloudformation validate-template --template-body file://${TEMPLATE} \
    aws cloudformation package --region $REGION --template-file $TEMPLATE --s3-bucket $BUCKET_NAME --output-template-file packaged-template.yml \
      && aws cloudformation deploy --region $REGION --template-file packaged-template.yml --stack-name $STACK_NAME --parameter-overrides StageName="$STAGE" --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
      && echo "" \
      && echo "### Exported Value ###" \
      && aws cloudformation describe-stacks --region $REGION --stack-name $STACK_NAME \
           | awk 'BEGIN{key=""}{ if ($1 == "\"OutputKey\":") key=$2; if ($1 == "\"OutputValue\":") print key" : "$2 }' \
           | sed 's/[",]//g' \
      && echo "######################/" \
      && echo ""
    echo `date "+%Y-%m-%d %H:%M:%S"`" - END"
    exit 0
fi

echo "Usage)"
echo ""
echo "  ${0} (deploy|delete|events) [--region regionName] [--env envName] [--force]"
echo ""
echo "Example)"
echo ""
echo "  # create or update stack named '${STACK_NAME}'"
echo "  ${0} deploy --env prod"
echo "  ${0} deploy --env prod --region us-east-1"
echo ""
echo "  # delete stack named '${STACK_NAME}'"
echo "  ${0} delete --env prod"
echo "  ${0} delete --env prod --region us-east-1"
echo ""
echo "  # display events details of create or update or delete stack of '${STACK_NAME}'"
echo "  ${0} events --env prod"
echo "  ${0} events --env prod --region us-east-1"
echo ""
echo "Details)"
echo ""
echo "  StackName ... The stack name will be the git repository name or folder name"
echo ""

※ 関連 AWS CloudFormationメモ

デプロイ

./awsbuild.sh deploy

オンプレミスのサーバ側のバッチ処理

log2aws.sh

#!/bin/bash

gen_url=上記のデプロイ時に表示された GenerateSignedUrlの値
server=`hostname`
log_dir=ログディレクトリのPATH
date=`date --date '+1day ago' +%Y-%m-%d`

# 署名付きURL取得
signed_url=`curl -s -L ${gen_url}?server=${server}\&date=${date}`

# アップロード
curl -s -L -D - -X PUT --upload-file ${log_dir}/${date}.log $signed_url  >/dev/null 2>&1

動作確認


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS