Kinesis AgentとFirehoseを利用してEC2からS3にログを転送する

AWS
AWSKinesis

本記事では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 Logs0.033USD
S30.025USD

CloudWatch LogsとFirehoseのデータ取り込み料比較

サービス料金(1GB)
CloudWatch Logs0.76USD
Firehose0.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.

コメント

タイトルとURLをコピーしました