オンプレミスで稼働しているサーバのログをS3に収集して、解析を行う環境を構築する。
/collectlog2aws
/template.yml
/src
/index.py
/collectlog2aws
/__init__.py
/analyze.py
/genurl.py
・・・ cloudformationテンプレート
・・・ Lambdaソース格納用
・・・ Lambda本体(メインハンドラ)
・・・ 一応パッケージ化しておく
・・・ CSV変換 及び ログ解析処理
・・・ 署名付きURL生成処理
S3バケット 及び Lambdaを作成/デプロイする為の CloudFormationテンプレートを作成する
※生ログ格納用S3バケットは、ログがPUTされたらCSV変換Lambdaが起動するように設定しておく
※署名付きURL生成用の処理はオンプレミス側のサーバから要求があった時に起動できるようにAPI Gateway 経由で起動する
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
#!/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
#!/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