前提
下記のガイドに従い、Elastic Beans + Docker で何かしらのアプリケーションはDeployできている前提
http://aws.typepad.com/aws_japan/2014/04/aws-elastic-beanstalk-for-docker.html
Elastic Beanstalk Docker に Deploy するファイル構成
下記のファイルを一つのZipにまとめて、Elastic Beanstalk, Dockerにて Deployします
.
|-- .ebextensions
| |-- cwl-myjob.config
| |-- cwl-setup.config
| |-- cwl-webrequest-metrics.config
| `-- eb-logs.config
|-- Dockerfile
|-- Dockerrun.aws.json
`-- testlog.sh
- Dockerfile :Docker container 定義file(今回は一般的なサンプル+下記testlog.shを動かす設定をしている)
- testlog.sh :log を定期的に書き出すサンプルスクリプト
- Dockerrun.aws.json : ElasticBeanstalk 固有の設定ファイル(今回はDocker containerのログDirectoryとHostMacineのDirectoryをMountする)
- .ebextensions 以下: Cloudwatch logs 関連の設定ファイル
ElasticBeanstalk + CloudwatchLog用サンプル config ファイルのDL
まず下記の記事を参考に
docs.aws.amazon.com/elasticbeanstalk/latest/dg/AWSHowTo.cloudwatchlogs.html
以下から EB + Docker 用のnginx 設定ファイルを取得
https://s3.amazonaws.com/elasticbeanstalk-samples-us-east-1/cloudwatchlogs/aws_eb_cloudwatchlogs-nginx-us-east-1.zip
取得,展開したファイルを.ebextensions というディレクトリに格納
この.ebextensionsというDirectoryをDockerfileと並列において、zipでかためて
EB 上のDashboadで"Upload & Deploy" すればサンプルは確認可能。
サンプルはcwl-webrequest-metrics.config というファイルで
nginx 用のrequest の4XX, 5XX 応答の数をカウントする
cwl-setup.config, eb-logs.config は 特に編集の必要がなく、そのまま使用する
カスタムログ用 config 作成
上記 nginx request用configを参考に、任意のログ上の値をカウントするサンプルを作成する。
*.config という名前にして、上記.ebextensionsというDirectoryに格納しておくと
ElasticBeanstalk deploy実行時に読み込んでくれる。
HotTopic
- Resourceとして Cloudwatch logs で連携させたい (LogGroup,MetricFilter, Alarm) を定義する。
- CWLogsAgentConfigSetup で監視するファイルの設定をする
- CloudWatchLogsではTimestampとそのときのログ本文という形で保持されるので、datetime_formatを設定する
- MetricFilter のFilterPattern,MetricValue でログの書式と抜き出したい値を定義する
CWLogsAgentConfigSetup
http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/AgentReference.html
上記に定義されているものの中で重要なものは下記
file
監視するファイルを以下のように設定する。サンプルのconfigでは最上部に定義いしているMappingsで切り替えられるようにしている。(Mappingsで定数にあたるものの宣言を上部に持ってくるのはCloudformationでもよく使われるパターンの一つ。)
Mappings:
CWLogs:
MyJobLogGroup:
LogFile: "/var/log_docker/test.log"
...
file = `{"Fn::FindInMap":["CWLogs", "MyJobLogGroup", "LogFile"]}`
datetime_format
これも上記のリンクに詳細説明はあるが、下記の例をみればだいたい理解できる。
これを指定しておくと、以下のMetricFilterで特に指定しなくても、一行の中でdatetimeが入っているところを自動で検出してくれて、それをログの発生時刻としてCloudWatch上であつかってくれる点が、優秀
。
Syslog: '%b %d %H:%M:%S', e.g. Jan 23 20:59:29
Log4j: '%d %b %Y %H:%M:%S', e.g. 24 Jan 2014 05:00:00
ISO8601: '%Y-%m-%dT%H:%M:%S%z', e.g. 2014-02-20T05:20:20+0000
今回のサンプル: TimestampFormat: "%Y/%m/%d %H:%M:%S.%f" ( 2014/08/13 19:12:03.839)
AWS::Logs::MetricFilter
Logの出力フォーマットを定義してその中から、値として記録したいValueを定義する。
CloudWatch上のUIからも設定可能だが、複数環境で複数作成する場合などは再現性があるのでconfigファイルとしてsrc code としてコントロールできることが利点となる。
FilterPattern
下記を参考にログの中で抜き出したい箇所を定義する。
このログ定義に一致したときだけMetricValueとしてカウントされる。
http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/FilterAndPatternSyntax.html
-
ログは空白区切りになっていることが前提
-
抜き出したい箇所だけ、変数を定義して幾つはいるかわからないところは...と記述することが可能
[..., status_code, bytes]
[ip, user, ..., status_code, bytes]
[ip, user, ...] -
文字列は = , != 演算子と * が使用可能
-
数値は >, <, >=, <=, =, != 比較演算子が使用可能
今回は Kinesis から読み込んだメッセージ数、書き込んだメッセージ数をCloudWatch上で表示したいと思い、下記のようなログを出力したという前提でLogFilterを設定した。
SERVICE # INFO 2014/08/13 19:12:03.839 0.926.552 TestService TestSender 0:0(0x00000000) +MSG TestLog-01 kineGet 250 kineWrite 0
FilterPatterns:
TestLogFilter: "[..., logtype=TestLog-01, logtag1, logval1, logtag2, logval2]"
MetricValue
上記で定義したFilterPatternの中で、使いたい変数を書くことが可能
例:
MetricValue : $logval2
4xx, 5xx 応答の数などをカウントしたい場合はそのログがあったことで+1すればよいので下記のように定義する
MetricValue : 1
Sample config : cwl-myjob.config
# Sample Myjob access log pattern:
# SERVICE # INFO 2014/08/13 19:12:03.839 0.926.552 TestService TestSender 0:0(0x00000000) +MSG TestLog-01 kineGet 250 kineWrite 0
# $category # $level $date $time ..., logtype=TestLog-01, logtag1, logval1, logtag2, logval2]"
Mappings:
CWLogs:
MyJobLogGroup:
LogFile: "/var/log_docker/test.log"
TimestampFormat: "%Y/%m/%d %H:%M:%S.%f"
FilterPatterns:
TestLogFilter: "[..., logtype=TestLog-01, logtag1, logval1, logtag2, logval2]"
Outputs:
MyJobCWLogGroup:
Description: "The name of the Cloudwatch Logs Log Group created for myjob. You can specify this by setting the value for the environment variable: MyJobCWLogGroup. Please note: if you update this value, then you will need to go and clear out the old cloudwatch logs group and delete it through Cloudwatch Logs."
Value: { "Ref" : "AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0MyJobLogGroup"}
Resources :
AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0MyJobLogGroup: ## Must have prefix: AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0
Type: "AWS::Logs::LogGroup"
DependsOn: AWSEBBeanstalkMetadata
DeletionPolicy: Retain ## this is required
Properties:
LogGroupName:
"Fn::GetOptionSetting":
Namespace: "aws:elasticbeanstalk:application:environment"
OptionName: MyJobCWLogGroup
DefaultValue: {"Fn::Join":["-", [{ "Ref":"AWSEBEnvironmentName" }, "MyJobLogs"]]}
RetentionInDays: 14
## Register the files/log groups for monitoring
AWSEBAutoScalingGroup:
Metadata:
"AWS::CloudFormation::Init":
CWLogsAgentConfigSetup:
files:
## any .conf file put into /tmp/cwlogs/conf.d will be added to the cwlogs config (see cwl-agent.config)
"/tmp/cwlogs/conf.d/myjob-log.conf":
content : |
[myjob-log_log]
file = `{"Fn::FindInMap":["CWLogs", "MyJobLogGroup", "LogFile"]}`
log_group_name = `{ "Ref" : "AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0MyJobLogGroup" }`
log_stream_name = {instance_id}
datetime_format = `{"Fn::FindInMap":["CWLogs", "MyJobLogGroup", "TimestampFormat"]}`
mode : "000400"
owner : root
group : root
#######################################
## Cloudwatch Logs Metric Filters
AWSEBCWLMyJobLogTestVal1MetricFilter :
Type : "AWS::Logs::MetricFilter"
Properties :
LogGroupName: { "Ref" : "AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0MyJobLogGroup" }
FilterPattern : {"Fn::FindInMap":["CWLogs", "FilterPatterns", "TestLogFilter"]}
MetricTransformations :
- MetricValue : $logval1
MetricNamespace: {"Fn::Join":["/", ["ElasticBeanstalk", {"Ref":"AWSEBEnvironmentName"}]]}
MetricName : CWLTestVal1KinessisRead
AWSEBCWLMyJobLogTestVal2MetricFilter :
Type : "AWS::Logs::MetricFilter"
Properties :
LogGroupName: { "Ref" : "AWSEBCloudWatchLogs8832c8d3f1a54c238a40e36f31ef55a0MyJobLogGroup" }
FilterPattern : {"Fn::FindInMap":["CWLogs", "FilterPatterns", "TestLogFilter"]}
MetricTransformations :
- MetricValue : $logval2
MetricNamespace: {"Fn::Join":["/", ["ElasticBeanstalk", {"Ref":"AWSEBEnvironmentName"}]]}
MetricName : CWLTestVal2KinessisWrite
######################################################
## Alarms
AWSEBCWLTestVal1Alarm :
Type : "AWS::CloudWatch::Alarm"
DependsOn : AWSEBCWLMyJobLogTestVal1MetricFilter
Properties :
AlarmDescription: "Kinesis read count is too many (count too high)."
MetricName: CWLTestVal1KinessisRead
Namespace: {"Fn::Join":["/", ["ElasticBeanstalk", {"Ref":"AWSEBEnvironmentName"}]]}
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 200
ComparisonOperator: GreaterThanThreshold
AlarmActions:
- "Fn::If":
- SNSTopicExists
- "Fn::FindInMap":
- AWSEBOptions
- options
- EBSNSTopicArn
- { "Ref" : "AWS::NoValue" }
サンプル用 Dockerfile, Script
AWSで紹介されている"2048" Gameのサンプルファイルに定期的にログを出力するスクリプトを追加した。
Dockerfile
気を付けるところ
- ADDでログファイルを該当Directoryにコピー配置するだけだと、下記Dockerrun.aws.jsonで指定したDirectoryにコピーされない。なので、CMDでスクリプトを実行するようにした。
- スクリプトが終了してしまうと、ElasticBeansTalk上でエラーとして扱われてしまうので、終了しないようにwhile loop で処理している
FROM dockerfile/nginx
RUN apt-get update
RUN apt-get install -y zip curl
RUN curl -o /usr/share/nginx/html/master.zip -L https://codeload.github.com/gabrielecirulli/2048/zip/master
RUN cd /usr/share/nginx/html/ && unzip master.zip && mv 2048-master/* . && rm -rf 2048-master master.zip
EXPOSE 80
RUN mkdir /var/log_out
ADD ./testlog.sh /var/
RUN chmod 755 /var/testlog.sh
CMD /var/testlog.sh
testlog.sh
定期的にログを出力するスクリプト(60分以上動かすとおかしくなります)
#!/bin/sh
a=0
while true
do
a=`expr $a + 1`
if test ${a} -lt 10
then
echo "SERVICE # INFO 2014/08/13 20:0${a}:03.839 0.926.552 TestService TestSender 0:0(0x00000000) +MSG TestLog-01 kineGet 1${a}0 kineWrite 0" >> /var/log_out/test.log
else
echo "SERVICE # INFO 2014/08/13 20:${a}:03.839 0.926.552 TestService TestSender 0:0(0x00000000) +MSG TestLog-01 kineGet 10 kineWrite 1${a}" >> /var/log_out/test.log
fi
sleep 10
done
サンプル用 Dockerrun.aws.json
Docker Container 上のログ出力ディレクトリと、
Host (ElasticBenastalk で起動されたEC2 instance)上のDirectoryのマウント設定を定義
下記で定義したDirectoryを上記 ClooudWatch Logs のconfig CWLogsAgentConfigSetupで指定する
{
"AWSEBDockerrunVersion": "1",
"Volumes": [
{
"HostDirectory": "/var/log_docker",
"ContainerDirectory": "/var/log_out"
}
]
}