CloudFormationでfluentdサーバを一発構築する
よく訓練されたアップル信者、都元です。
最近ブログにAWSネタが少ないなぁ。|o゚д゚)
— よこたさとし (@sato_shi) October 5, 2013
ハイ! AWSチームなのにSpring Frameworkネタばっか書いてんじゃねえよ、ということで、都元の愛するCloudFromationを使ってfluentdサーバを立ててみました。
CloudFormationの流儀
CloudFormationのテンプレートは汎用的に越した事はありません。その前提で。
EC2において、「何かをインストールしたサーバ」をCloudFormationで構築したい場合、ぱっと思いつくのは「構築済みのAMIを用意する」ことでしょうか。しかし、独自AMIのメンテナンスというのは意外と大変なので、出来れば「素のAmazon Linuxから、自動的にサーバが構築される」といった仕組みを用意したいものです。素のAmazon Linuxは公開されたAMIですので、アカウントに依存しないポータビリティが実現できる、というメリットも大きいです。
そんな望みに応えるCloudFormationの機能として、以前CloudFormationのヘルパースクリプトcfn-initによるインスタンスの初期化というものをご紹介しました。今回、この仕組みを使って、VPC環境内にfluentdサーバを立ててみましょう。
fluentdのテンプレート
テンプレート全体はgistに置いておきましたので、必要に応じて参照してください。ここでは重要な部分を抜粋して解説します。
ログの格納先S3バケット
fluentdに送られて来たログデータは、fluent-plugin-s3を使ってS3バケットに書き出すことにしましょう。その辺りの詳細は、Amazon LinuxにFluentdをインストールしてS3とMongoDB連携するというエントリで紹介されています。
CloudFormationで、まずログを出力するためのS3バケットを定義しましょう。あまりにも簡単な記述ですが、"DeletionPolicy" : "Retain"となっているのがキモです。というのも、CloudFormationは、スタックを削除する際に、基本的に関連リソースを全て削除してしまいます。つまり、ログが豊富に溜まったS3バケットも、デフォルトでは削除されてしまうのです。ログ用のバケットはそのようなことにならないように、スタックの撤収時であっても削除しない、というのがこの設定です。
"LogBucket" : { "Type" : "AWS::S3::Bucket", "DeletionPolicy" : "Retain" },
2つのconfigSet
続いて、FluentdInstanceのcfn-init設定を見てください。configSetsという見慣れないプロパティがあります。
通常、AWS::CloudFormation::Initの直下には1つのconfigを配置し、その下にfiles, commands, packages, services等 *1の設定を記述していきます。
これらの設定は、通常packages, files, servicesの順で処理されます *2。しかし今回の場合、filesで/etc/yum.repos.d/td.repoファイルを配置してからpackagesによるtd-agentのインストールを行い、その後でまた、filesで/etc/td-agent/td-agent.confを書き換える、ということがしたいのです。
というわけで、今回はconfig1で/etc/yum.repos.d/td.repoファイルを配置し、config2でtd-agentのインストールと設定等を行う、という二段構えのconfigをconfigSetとして用意しました。
"Metadata" : { "AWS::CloudFormation::Init" : { "configSets" : { "default" : [ "config1" , "config2" ] }, "config1" : { "files" : { ... } }, "config2" : { "packages" : { "yum" : { "td-agent" : [] } }, ... } } }
td-agent.confの設定
td-agent.confでは、port 24224でTCPの口を開けておくと共に、自分自身の/var/log/messagesをsourceとして設定しておきます。
また、fluentdが受け付けたログデータは全て、S3に投げるようになっています。{ "Ref" : "LogBucket" }という記述によって、上で作成したS3バケット名を動的に設定するようになっています。
"/etc/td-agent/td-agent.conf" : { "content" : { "Fn::Join" : ["", [ "<source>\n", " type forward\n", " port 24224\n", "</source>\n", "\n", "<source>\n", " type config_expander\n", " <config>\n", " type tail\n", " format syslog\n", " path /var/log/messages\n", " tag ${hostname}/syslog.messages\n", " </config>\n", "</source>\n", "\n", "<match *.**>\n", " type forest\n", " subtype s3\n", "\n", " <template>\n", " s3_bucket ", { "Ref" : "LogBucket" },"\n", " s3_endpoint ", { "Fn::FindInMap": [ "AWSAPIEndpoint", { "Ref": "AWS::Region" }, "S3" ]}, "\n", "\n", " path ${tag}/\n", " buffer_path /var/log/td-agent/buffer/${tag}\n", "\n", " time_slice_format %Y/%m/%d/ec2-%Y-%m-%d-%H\n", " flush_interval 1m\n", " </template>\n", "</match>\n" ]]}, "mode" : "000644", "owner" : "root", "group" : "root" }
また、s3_endpointにS3のエンドポイントホスト名が必要ですので、下記のようなMappingsを用意しておき、各リージョンに対応します。
"Mappings": { ... "AWSAPIEndpoint": { "us-east-1": { "S3": "s3.amazonaws.com" }, "us-west-2": { "S3": "s3-us-west-2.amazonaws.com" }, "us-west-1": { "S3": "s3-us-west-1.amazonaws.com" }, "eu-west-1": { "S3": "s3-eu-west-1.amazonaws.com" }, "ap-southeast-1": { "S3": "s3-ap-southeast-1.amazonaws.com" }, "ap-southeast-2": { "S3": "s3-ap-southeast-2.amazonaws.com" }, "ap-northeast-1": { "S3": "s3-ap-northeast-1.amazonaws.com" }, "sa-east-1": { "S3": "s3-sa-east-1.amazonaws.com" } }, ... }
td-agentのインストールとfluentdの設定が終わったところで、/var/log/messagesの権限設定と、各種fluent-pluginのインストールをしておきましょう。
"commands" : { "0-chmod" : { "command" : "chgrp td-agent /var/log/messages && chmod g+r /var/log/messages" }, "1-fluent-update" : { "command" : "/usr/lib64/fluent/ruby/bin/fluent-gem update" }, "2-fluent-plugin-s3" : { "command" : "/usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-s3" }, "3-fluent-plugin-forest" : { "command" : "/usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-forest" }, "4-fluent-plugin-config-expander" : { "command" : "/usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-config-expander" }
最後に、td-agentを起動します。要するにchkconfig td-agent onとservice td-agent startを行っているのが以下の記述です。
"services" : { "sysvinit" : { "td-agent" : { "enabled" : "true", "ensureRunning" : "true" } } }
動作確認
というわけで、このテンプレートでスタックを立ち上げると、自分自身の/var/log/messagesをS3バケットに上げ始めますので、しばらくすると下記のようにログがアップロードされて来ます。
ついでですので、/var/log/messagesだけではなく、fluent-catを使ってTCPのソースからもログも上げてみましょう。
$ echo '{"foo":"bar"}' | /usr/lib64/fluent/ruby/bin/fluent-cat debug.test
こちらも、しばらくすると下記のようにファイルがアップロードされて来ます。
2013-10-09T09:35:22+00:00 debug.test {"foo":"bar"}
ちゃんと来てますね!