Playframework2.5のアプリケーションをdockerでAWS EC2にデプロイしてCloudWatch Logsでログを集約した話です。デプロイの自動化はここでは触れません。すごいまとめていうとdockerのawslogs logging driverが楽で便利だから使おうぜーってだけの話です。
dockerの1.9以上のバージョンでAmazon CloudWatch Logs logging driverを使いました。
環境は下記の通りです。
環境
- Playframework 2.5
- Amazon Linux
- Java 1.8 (EC2ではopenjdk1.8を利用)
- docker 1.12.6
手順
- 本番用のlogbackの設定
- playframeworkでdockerのビルド
- CloudWatch Logsの設定
- EC2インスタンスでアプリケーション起動
1. 本番用のlogbackの設定
dockerのロギングドライバーはdockerコンテナ内の標準出力・エラー出力の内容をハンドルしてくれるので、playframeworkのアプリケーションログをファイルに出力しないですべてdocker内のコンソールに出力するようにします。
logback-production.xmlを用意して下記のようにコンソールログだけにします。SQLのログもオフにしています。
<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
<configuration>
<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
</encoder>
</appender>
<appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="STDOUT" />
</appender>
<logger name="play" level="INFO" />
<logger name="application" level="INFO" />
<!-- Off these ones as they are annoying, and anyway we manage configuration ourselves -->
<logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
<root level="INFO">
<appender-ref ref="ASYNCSTDOUT" />
</root>
</configuration>
2. playframeworkでdockerのビルド
build.sbtにdockerビルド用の設定を下記のように追記します。Dockefileの内容を記述できます。ここではOracke Java8のdockerイメージを利用しています。
//
// docker build
//
enablePlugins(DockerPlugin)
import com.typesafe.sbt.packager.docker.{ExecCmd, Cmd}
dockerExposedPorts in Docker := Seq(9000)
dockerCommands := Seq(
Cmd("FROM","nimmis/java:14.04-oracle-8-jdk"),
Cmd("MAINTAINER","MychaelStyle"),
Cmd("EXPOSE","9000"),
Cmd("RUN","apt-get -y upgrade && apt-get -y update"),
Cmd("RUN","apt-get -y install language-pack-ja ntp sysv-rc-conf"),
Cmd("RUN","update-locale LANG=ja_JP.UTF-8"),
Cmd("ADD","stage /"),
Cmd("WORKDIR","/opt/docker"),
Cmd("RUN","[\"chown\", \"-R\", \"daemon\", \".\"]"),
Cmd("RUN","[\"chmod\", \"+x\", \"bin/MyApp\"]"),
Cmd("USER","daemon"),
Cmd("ENTRYPOINT","[\"bin/MyApp\", "
+" \"-Dplay.cripto.secret=\\\"your secret!!\\\"\","
+" \"-Dlogger.resource=logback-prod.xml\","
+" \"-Dconfig.resource=production.conf\","
+" \"-J-Xms512m\", \"-J-Xmx4096m\", \"-J-server\""
+"]"),
ExecCmd("CMD")
)
試しにビルドしてみます。
./bin/activator docker:stage
ビルドすると[プロジェクトフォルダ]/target/docker
フォルダ内に必要なファイルとDockerfileができています。フォルダに移動してdockerイメージを作成します。
cd target/docker
docker build -t MyAppImage .
イメージがビルドできたらコンテナを起動して見ます。起動に問題なければとりあえずOK
docker run -t -d -p 9000:9000 --name=MyApp MyAppImage
エラーなく起動すればOK! エラーがある場合やhttp://localhost:9000/でアクセスできない場合はdocker execなどでコンテナにログインして
3. CloudWatch Logsの設定
3.1. IAMロールでCloudWatch Logsを許可
公式のドキュメントを見ながらごにょごにょすればできます。
http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html
まずはインスタンスで使う予定のロールにCloudWatchLogsのフルアクセスを許可するようにポリシーを設定します。ポリシー名はCloudWatchLogsFullAccess
です。インスタンス用に新規にロールを作成する場合にもこのポリシーを含めるのを忘れないようにしましょう。
ポリシーの表示をしてみるとこんなコードになっています。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
アタッチしたインスタンスをAmazon Linuxで起動します。使ったのは2017年2月現在でAmazon Linux AMI 2016.09.1 (HVM), SSD Volume Type - ami-56d4ad31
のAMIです。最初の状態だと色々と入っていないので色々と入れました。
sudo yum groupinstall -y "Development tools"
sudo yum install -y docker java-1.8.0 awscli awslogs
最初に入るjavaが1.7なので1.8をいれてalternativesで1.8に切り替えます。
sudo alternatives --config java
There are 2 programs which provide 'java'.
Selection Command
-----------------------------------------------
* 1 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
+ 2 /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
Enter to keep the current selection[+], or type selection number: 2
awslogsエージェントのために設定をします。
vim /etc/awslogs/awscli.conf
リージョンを東京に変更して試しにエージェントで/var/log/messagesを送信する設定をしてみます。設定ファイルの項目は公式のガイドにあります。
http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/AgentReference.html
[plugins]
cwlogs = cwlogs
[default]
region = ap-northeast-1
[logstream1]
log_group_name = MyLogsTest
log_stream_name = {instance_id}
datetime_format = %Y-%m-%dT%H:%M:%S%z
time_zone = UTC
file = /var/log/messages
file_fingerprint_lines = 1
multi_line_start_pattern = {datetime_format}
initial_position = start_of_file
encoding = utf_8
buffer_duration = 10000
batch_count = 10000
batch_size = 1048576
エージェントを起動して見ます。
sudo service awslogs start
sudo chkconfig awslogs on
AWS CloudWatchのログの画面からロググループが作成されて閲覧できるようになっています!
さて、awslogs.confの設定とエージェントの起動手順はただのテストで、インスタンスにAWS CloudWatch Logsが使えるIAMロールが設定されて入ればdockerのコマンドだけで事足ります。ただし、dockerのあawslogs-logging-driverはロググループは自動で作ってくれないのでAWSマネジメントコンソール->CloudWatch->ログ の画面から「アクション」 -> 「ロググループの作成」でアプリケーション用のロググループを作成しておきます。ここでは画面は省略、[MyAppLogGroup]を作成したこととして進めます。
4. EC2インスタンスでアプリケーション起動
dockerhubとか使ってやったほうが楽なのでしょうがデプロイのテストのためインスタンスにログインしてgithubからpullしてビルドして稼働させるという手順でやってみます。
cd
mkdir webapps
cd webapps
github clone ...MyApp
cd MyApp
./bin/activator docker:stage
activatorでビルドできたらdockerイメージをビルドして起動します。
cd target/docker
docker build -t MyAppImage .
docker run -it -d --name MyApp -p 80:9000 \
--log-driver=awslogs \
--log-opt awslogs-region=ap-northeast-1 \
--log-opt awslogs-group=MyAppLogGroup \
--log-opt awslogs-stream=MyAppWebApplication \
MyApp
以上でインスタンスにポート80でアクセスできるようになり、AWSの管理コンソールのCloudWatch -> ログの画面でログが閲覧できるようになりました!