本記事ではEC2からS3にKinesis Firehoseを利用してログを転送する方法を記載します。大量のログをS3で管理したい場合に便利な構成の1つだと思われます。
EC2→Firehose→S3構成とする利点
大容量の場合はCloudWatch Logsよりも低コスト
ログ保存先としてCloudWatch Logsを利用する場合、データの保存だけでなく、取り込み自体にも課金されます。保存と取り込みについて、それぞれS3とFirehoseの価格と比較した場合、いずれもCloudWatch Logsの方が割高となります。
ただし、CloudWatch Logsの場合はデータ取り込みに無料枠が存在するため、少量のデータであれば、CloudWatch Logsの方が安くなる可能性もあります。
以下、執筆時点での東京リージョンの料金比較です。
CloudWatch LogsとS3のデータ保存料比較
サービス | 料金(1GB) |
---|---|
CloudWatch Logs | 0.033USD |
S3 | 0.025USD |
CloudWatch LogsとFirehoseのデータ取り込み料比較
サービス | 料金(1GB) |
---|---|
CloudWatch Logs | 0.76USD |
Firehose | 0.036USD |
参考
途中でLambdaによるデータ加工処理を簡単に入れられる
S3にログを送るだけなら、td-agent
を利用しても要件を満たせると思います(未経験ですが)。しかし、Firehoseを使う利点の1つとして、途中にLambdaによるデータ加工処理を簡易的に実装できるということも挙げられると存じます。コンソールから選択するだけですからね。
td-agent
でもデータ加工できる?……とりあえずKinesis Agentを使ってみたかったというのが正直なところです。
S3バケットを準備する
まずは、ログを格納するS3バケットを準備します。特別な設定は指定していませんが、バケットにライフサイクルを設定することで、ログの保存期間を指定することができます。
Kinesis Firehoseを準備する
続いて、Kinesis Firehoseを準備します。なお、Firehoseは取り込み量に応じた従量課金となりますので、作成が完了した段階で料金が発生するわけではありません。
Step 1: Name and source
Delivery stream name
でFirehoseの名前を設定します。Source
に関しては、今回はKinesis Data Stream
ではないため(Kinesis Agent)、Direct PUT or other sources
を選択します。
Step 2: Process records
収集したデータの加工処理を指定できます。Lambdaによるデータの加工や、フォーマット変換(Parquet等)を行うことができますが、今回は生データを直接S3にアップロードする設定としています。
Step 3: Choose a destination
Select a destination
今回はS3にデータを保存するのが目的であるため、Destination
ではAmazon S3
を選択します。
S3 destination
また、S3に保存する際のプレフィクスを指定することもできます。デフォルトだとYYYY/MM/DD/HH
といったプレフィクスがFirehoseにより利用されますが、今回はAthenaによる検索も意識して、以下のようにパーティション分割した形を指定しています。
S3 prefix
log/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}/
S3 error prefix
error/!{firehose:error-output-type}/
Step 4: Configure settings
S3 buffer conditions
Firehoseは送付されてきたデータがBuffer size
に達するか、あるいは、Buffer interval
の時間が経過した後、S3にデータを転送します。小さな値にすることでS3への転送タイムラグを減らすことができますが、その分、ファイル数が多くなることに注意が必要です。今回はデフォルト値を利用しています。
S3 compression and encryption
S3 compression
で圧縮形式を指定することで、S3格納時の容量を減らすことができます。テキストファイルは圧縮率が高い傾向にあり、かつ、S3への転送後に利用を意識しているAthenaは圧縮形式にも対応しているため、ここでは圧縮しています。
Permissions
Firehoseが利用するIAMロールを指定します。事前に作成している場合はそれを選択可能です。特に作成していない場合、このタイミングで新規作成することができます。新規作成では権限がAWSにより自動で指定されるため、今回はそれをそのまま利用しています。
EC2にKinesis Agentをインストールし設定する
Kinesis Agentのインストール
今回はAmazon Linux 2にインストールしました。Amazon Linuxならyumでaws-kinesis-agent
のパッケージを指定することでインストールできますが、現時点ではAmazon Linux 2には対応していないようです。
sudo yum install -y https://s3.amazonaws.com/streaming-data-agent/aws-kinesis-agent-latest.amzn1.noarch.rpm
なお、最新バージョン(※2021年12月31日追記時点)をインストールする際は、下記のコマンドを利用します。
sudo yum install -y https://s3.amazonaws.com/streaming-data-agent/aws-kinesis-agent-latest.amzn2.noarch.rpm
なお、Kinesis AgentはJavaベースのエージェントです。執筆時点ではJVMの1.7以上が必要になります。もしインストールされていない場合、Javaをインストールします。
sudo yum install -y java-1.8.0-openjdk
Kinesis Agentの設定
設定ファイルは/etc/aws-kinesis/agent.json
です。以下のように変更しています。
/etc/aws-kinesis/agent.json記載例
{
"cloudwatch.emitMetrics": true,
"kinesis.endpoint": "",
"firehose.endpoint": "firehose.ap-northeast-1.amazonaws.com",
"awsAccessKeyId": "XXXXX",
"awsSecretAccessKey": "XXXXX",
"flows": [
{
"filePattern": "/tmp/app.log*",
"deliveryStream": "XXXXX"
}
]
}
firehose.endpoint
今回は東京リージョンを利用するため、firehose.ap-northeast-1.amazonaws.com
をエンドポイントとして明示的に指定しています。デフォルト(未指定)だとfirehose.us-east-1.amazonaws.com
が利用されます。
flows
今回はFirehoseを利用するので、デフォルトで記載されていたkinesisStream
の項目を削除しています。
filePattern
今回は動作確認用に/tmp/app.log*
を指定しています。なお、ここに記載するファイルは、OSユーザaws-kinesis-agent-user
が読み取り可能である必要があります。
deliveryStream
配信ストリームの名前として、事前作成したFirehoseの名前を指定しています。
awsAccessKeyId, awsSecretAccessKey
今回は一時的な検証ということもあり、アクセスキーとシークレットキーを利用する方式としました。しかしながら、定石通り、EC2にIAMロールをアタッチして認証を行う方がセキュリティ的には好ましいものと存じます。IAMロールを利用する場合、アクセスキーとシークレットキーの項目を設定ファイルに記載する必要はございません。
Kinesis Agentの起動
以下のコマンドでエージェントを起動します。
sudo systemctl start aws-kinesis-agent
エージェントログ/var/log/aws-kinesis-agent/aws-kinesis-agent.log
を確認し、正常にログが転送されたことを確認します。正常に転送された場合、以下のようにX records sent successfully to destinations
という出力があります。
2019-12-29 07:45:40.528+0000 (Agent.MetricsEmitter RUNNING) com.amazon.kinesis.streaming.agent.Agent [INFO] Agent: Progress: 6 records parsed (228 bytes), and 6 records sent successfully to destinations. Uptime: 90045ms
特に問題なく転送できたことが確認できたら、必要に応じてサービスの自動起動設定をします。
sudo systemctl enable aws-kinesis-agent
Kinesis Agentのアップデートについて
アップデート方法については、Kinesisエージェントのアップデートについてをご参照ください。
詰まった点
Kinesis Agentが起動しない
JVMをインストールしていなかったため、エージェントが起動しませんでした。JVMをインストールすることで解消しました。
エラー例
kinesis-agent[3880]: ERROR: JVM version >= 1.7 is required. Found following JVM:
ログの転送処理に入らない
ログがaws-kinesis-agent-user
により読み取り可能なよう権限を変更したら解消しました。システムログ等の場合、特に権限に注意が必要かと思われます。
エラー例
2019-12-29 07:11:24.091+0000 (Agent STARTING) com.amazon.kinesis.streaming.agent.Agent [INFO] Agent: Starting tailer for file fh:kinesis-agent-firehose:/home/ec2-user/application.log
2019-12-29 07:11:24.106+0000 (Agent STARTING) com.amazon.kinesis.streaming.agent.Agent [ERROR] Unhandled exception during startup.
java.lang.IllegalStateException: Expected the service to be RUNNING, but the service has FAILED
at com.google.common.util.concurrent.AbstractService.checkCurrentState(AbstractService.java:285)
at com.google.common.util.concurrent.AbstractService.awaitRunning(AbstractService.java:229)
at com.google.common.util.concurrent.AbstractExecutionThreadService.awaitRunning(AbstractExecutionThreadService.java:197)
at com.amazon.kinesis.streaming.agent.Agent.startUp(Agent.java:258)
at com.google.common.util.concurrent.AbstractIdleService$2$1.run(AbstractIdleService.java:54)
at com.google.common.util.concurrent.Callables$3.run(Callables.java:95)
at com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:457)
at com.google.common.util.concurrent.MoreExecutors$2.execute(MoreExecutors.java:849)
at com.google.common.util.concurrent.AbstractIdleService$2.doStart(AbstractIdleService.java:50)
at com.google.common.util.concurrent.AbstractService.startAsync(AbstractService.java:174)
at com.google.common.util.concurrent.AbstractIdleService.startAsync(AbstractIdleService.java:135)
at com.amazon.kinesis.streaming.agent.Agent.main(Agent.java:109)
Caused by: java.nio.file.AccessDeniedException: /home/ec2-user
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newDirectoryStream(UnixFileSystemProvider.java:427)
at java.nio.file.Files.newDirectoryStream(Files.java:457)
at com.amazon.kinesis.streaming.agent.tailing.SourceFile.listFiles(SourceFile.java:70)
at com.amazon.kinesis.streaming.agent.tailing.SourceFileTracker.initialize(SourceFileTracker.java:156)
at com.amazon.kinesis.streaming.agent.tailing.FileTailer.initialize(FileTailer.java:151)
at com.amazon.kinesis.streaming.agent.tailing.FileTailer.startUp(FileTailer.java:216)
at com.google.common.util.concurrent.AbstractExecutionThreadService$1$2.run(AbstractExecutionThreadService.java:55)
at com.google.common.util.concurrent.Callables$3.run(Callables.java:95)
at java.lang.Thread.run(Thread.java:748)
2019-12-29 07:11:24.116+0000 (FileTailer[fh:kinesis-agent-firehose:/home/ec2-user/application.log]) com.amazon.kinesis.streaming.agent.Agent [ERROR] FATAL: Thread FileTailer[fh:kinesis-agent-firehose:/home/ec2-user/application.log] threw an unrecoverable error. Aborting application
java.lang.RuntimeException: java.nio.file.AccessDeniedException: /home/ec2-user
at com.google.common.base.Throwables.propagate(Throwables.java:160)
at com.google.common.util.concurrent.AbstractExecutionThreadService$1$2.run(AbstractExecutionThreadService.java:77)
at com.google.common.util.concurrent.Callables$3.run(Callables.java:95)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.nio.file.AccessDeniedException: /home/ec2-user
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newDirectoryStream(UnixFileSystemProvider.java:427)
at java.nio.file.Files.newDirectoryStream(Files.java:457)
at com.amazon.kinesis.streaming.agent.tailing.SourceFile.listFiles(SourceFile.java:70)
at com.amazon.kinesis.streaming.agent.tailing.SourceFileTracker.initialize(SourceFileTracker.java:156)
at com.amazon.kinesis.streaming.agent.tailing.FileTailer.initialize(FileTailer.java:151)
at com.amazon.kinesis.streaming.agent.tailing.FileTailer.startUp(FileTailer.java:216)
at com.google.common.util.concurrent.AbstractExecutionThreadService$1$2.run(AbstractExecutionThreadService.java:55)
... 2 more
ログの転送時にエラーが発生する
Firehoseに書き込むための認証情報が受け渡せていないため発生していました。今回は/etc/aws-kinesis/agent.json
にアクセスキーとシークレットキーを記載することで解消しましたが、定石に則るのであれば、EC2にロールを付与する方式が、安全かつ推奨される方法かと存じます。
エラー例
2019-12-29 07:35:25.313+0000 (sender-0) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable
send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:25.317+0000 (sender-1) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable
send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:25.567+0000 (sender-2) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:25.818+0000 (sender-3) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:26.321+0000 (sender-4) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:27.070+0000 (sender-5) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:28.572+0000 (sender-6) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:31.823+0000 (sender-7) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
2019-12-29 07:35:39.331+0000 (sender-8) com.amazon.kinesis.streaming.agent.tailing.AsyncPublisher [ERROR] AsyncPublisher[fh:kinesis-agent-firehose:/tmp/app.log*]:RecordBuffer(id=2,records=6,bytes=228) Retriable send error (com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain). Will retry.
コメント