CloudWatchのログ蓄積とモニタリングを使ってみる(その3 完結編)

2014年9月1日月曜日 Kouta Mikawa

皆さんこんにちは。今日も元気に監視してますか?Mikawaです。
2回続けたCloudWatchのログ蓄積の話題も今回の3回目で最後です。
もう飽きたと言わずに最後までお付き合いください。

前回はAPIを利用してS3のログCloudWatchにログを送る方法について書きましたが
今回は、もう少し違う方法について書いてみます。

まあ、エージェントもどきを自分で作るのも手間だよねということですね。
そのうち、CloudWatchにS3のログを読み込む機能が実装されそうな予感がものすごく
しますが、その時はまた便利になったと素直に喜びましょう。

さて、S3に溜まったAWSからのログをAPIを使わずにどう送るかということですが
構成としてはこんな感じです。



適当なファイルをエージェントに監視させて、そのファイルに対して定期的にS3から取得した
ログの内容を出力して行けば、あとは勝手にやってくれるという構成です。

それでは詳しく見ていきましょう。
ちなみに、今回はせっかくなので、やはり最近東京リージョンに来たCloudTrailで試して
みました。
(CloudTrailのログを漏れなく扱うにはSNSの通知を使う方が良いのですが今回はあえてやっていません)

その1で書いたエージェントのCentOS対応も、正式に行われたので(8/18時点)おさらい含めて
最初から書きます。

■監視対象となるログファイルの準備

mkdir /var/log/cloudTrail
touch /var/log/cloudTrail/trail.log
touch /var/log/cloudTrail/.lastmarker
  場所やファイル名はなんでも良いです。
  .lastmarkerは今回のプログラムの為に用意します。

■logrotateの設定
   放置して、ログが大量に溜まっても大変なので、logrotateを設定しておきます。
   テンポラリ的なファイルなので、dailyでローテートして消してしまって良いかと。

■エージェントのインストール
  バージョン指定ではなく最新を持ってきます。
  中をみるとCentOSにも対応された事が確認できます。

wget https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py

python ./awslogs-agent-setup.py --region us-east-1

  設定は流れにそってやっていくだけですが、日付フォーマットは
CloudTrailに合わせたカスタム値を指定します。

%Y-%m-%dT%H:%M:%S

  参考までに、設定完了後のawslogs.confはこんな感じですね。

[/var/log/cloudTrail/trail.log]
datetime_format = %Y-%m-%dT%H:%M:%S
file = /var/log/cloudTrail/trail.log
buffer_duration = 5000
log_stream_name = vallaboratory
initial_position = start_of_file
log_group_name = cloudTrail
time_zone = UTC
  CloudTrailのログはUTCのtimestampですが、エージェントを動かすサーバのlocaleがJSTなどの
  場合、転送時にJSTに変更されます。(timestampの"Z"をUTC offset +0000と判定できないため)
  これはawslogs.confの最後にtime_zone=UTCを追加しておくことで回避できます。

■S3からログを取得するプログラムの用意
  ざっと書くとこんな感じです。


#coding: utf-8
require 'rubygems'
require 'aws-sdk-core'
require 'json'
require 'zlib'

#ログ設定
LOG_DIR = '/var/log/cloudTrail'
LOG_FILE = 'trail.log'
MARKER_FILE = '.lastmarker'

#S3設定
s3 = Aws::S3::Client.new(
  access_key_id: "your access_key",
  secret_access_key: "your secret_access_key",
  region: "your region"
)

#日付設定(UTCの必要がある)
time = Time.now.getutc
year = time.year
month = "%02d" % time.month
day = "%02d" % time.day

#最後のファイル位置を読み込み
file = File.open("#{LOG_DIR}/#{MARKER_FILE}","r")
marker = file.read
file.close

#bucket内のリストを取得
list = s3.list_objects(
  bucket: "your bucket",
  max_keys: 300,
  marker: marker,
  prefix: "AWSLogs/xxxxxxxxx/CloudTrail/your region/#{year}/#{month}/#{day}", 
)

#追加があればログとして処理
unless list.data.contents.size == 0
  lastkey = ""
  list.data.contents.each do | item |
    #ファイルを取得
    lastkey = item.key
    obj = s3.get_object(
      bucket: "your bucket",
      key: lastkey,
    )

    json = ""
    Zlib::GzipReader.wrap(obj.body) do |gz|
      json << gz.read
    end
    trail = JSON.load(json)

    #必要な項目を抜き出してファイルに追加
    file = File.open("#{LOG_DIR}/#{LOG_FILE}","a")
    trail['Records'].each do |rec|
      file.write("#{rec['sourceIPAddress']} #{rec['eventTime']} #{rec['userIdentity']['type']} #{rec['userIdentity']['userName']} #{rec['awsRegion']} #{rec['eventSource
']} #{rec['eventName']} #{rec['userAgent']}\n")
    end
    file.close
  end

  #最後のファイル位置を書き込み
  file = File.open("#{LOG_DIR}/#{MARKER_FILE}","w")
  file.write(lastkey)
  file.close
end

結構雑に書いてあるので、使いたい方は適当に直してください。
(あと、このコードだと日付の変わり目のログをスキップする可能性があります)
ポイントはS3の日付ディレクトリをUTCで処理する辺りとファイルのリストをmarkerで
見ていくあたりでしょうか。(そのために.lastmasrkerなるファイルを用意したのです)

ちょっと、複雑な感じもしますが、このやり方だと、面倒な所は公式のエージェントに
お任せですし、1つのstreamに纏めることもできます。
また、プログラム上で、不要なログ(ヘルスチェック等監視のためのアクセス)を
省いておくことで、本当に監視しなければならない部分のみ見るということも可能です。

cloudWatchに送られたログを確認すると、こんな感じです。


IAMUserがbeanstalkに対して操作しているのが解りますね。
あとは、メトリクスとアラートを設定すれば、特定の誰かを追跡 問題のある操作で警告する
こともできますね。
CloudTrail用に書いた部分を直せばELBのログなどにも適用できるかと思います。

いかがでしょうか。
エージェントを利用するだけでも、工夫次第で結構色々できますよね。
まだまだ色々できそうですが、とりあえず今回で3回に渡り続いたCloudWatchのお話は終了です。

では、また次回、新しいネタでお会いしましょう!


関連記事
CloudWatchのログ蓄積とモニタリングを使ってみる(その2)
CloudWatchのログ蓄積とモニタリングを使ってみる(その1)