firelens is 何?
fluentd/fluent-bitに連携することが可能なログドライバーです。
なぜfirelensか
firelensが出るまでFargateはawslogsドライバくらいしか選択肢がありませんでした。
s3に生ログの保存、解析+アラート用にdatadog logsに送るためには
fargate -> cloudwatch -> firehose -> lambda -> datadog logs, s3
といった構成を取る必要があり、そこには2つ問題がありました。
- cloudwatch使わないのに無駄な費用が発生
- lambdaがたまにこけていた(これは我々の実装の問題だと思います)
fargate(firelens) -> firehose -> datadog logs, s3
とfirelensを使うとこの構成になり、上記解決 + cloudwatch, lambda費用もかからなくなります。
fluent-bitのイメージについて
今回サイドカーで立てるfluent-bitのイメージは
https://hub.docker.com/r/sioncojp/fluent-bit-datadog-firehose を使います。
なぜこのイメージを使うかというと、Fargateはs3にあるconfigファイルを利用できないので、firehose, datadogに送るconfigを設定したカスタムイメージを作成する必要がありました。
ここで注意するのは、 /fluent-bit/etc/fluent-bit.conf
と /fluentd/etc/fluent.conf
のファイルパスは予約されてるので、/fluent-bit/etc/extra.conf
などの名前で保管しましょう。
ref: s3にあるconfigが使えない + ファイルパスの予約
ECS task definition
タイミーではecs-deployというGoの自社ツールでecsのtask設定 + service updateをしてますが、そちらで自動的にfluent-bitのContainerDefinition設定がされるようになってます。
fluent-bitの管理はSREなので、開発者には動かしたいコンテナの設定だけしてもらうよう意識してます。
設定されるjsonについて、firelens部分だけ紹介するとこのような感じになります
"logConfiguration": {
"logDriver": "awsfirelens",
"secretOptions": null,
"options": null
},
"environment": [
{
"name": "AWS_REGION",
"value": "ap-northeast-1"
},
{
"name": "DD_SERVICE",
"value": "koyama"
},
{
"name": "DD_SOURCE",
"value": "fargate"
},
{
"name": "DD_TAGS",
"value": "env:sandbox"
},
{
"name": "DELIVERY_STREAM",
"value": "koyama_koyama"
}
],
"secrets": [
{
"valueFrom": "SSMのDD_API_KEYパス",
"name": "DD_API_KEY"
}
],
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"config-file-type": "file",
"config-file-value": "/fluent-bit/etc/extra.conf"
}
},
全task definitionはこちら。
{
"ipcMode": null,
"executionRoleArn": "arn:aws:iam::xxxxxxxxxxx:role/fargate-koyama",
"containerDefinitions": [
{
"dnsSearchDomains": null,
"logConfiguration": {
"logDriver": "awsfirelens",
"secretOptions": null,
"options": null
},
"entryPoint": [],
"portMappings": [
{
"hostPort": 8000,
"protocol": "tcp",
"containerPort": 8000
}
],
"command": [],
"linuxParameters": {
"capabilities": null,
"sharedMemorySize": null,
"tmpfs": null,
"devices": null,
"maxSwap": null,
"swappiness": null,
"initProcessEnabled": true
},
"cpu": 0,
"environment": [],
"resourceRequirements": null,
"ulimits": [
{
"name": "nofile",
"softLimit": 65536,
"hardLimit": 65536
}
],
"dnsServers": null,
"mountPoints": [],
"workingDirectory": null,
"secrets": null,
"dockerSecurityOptions": null,
"memory": null,
"memoryReservation": null,
"volumesFrom": [],
"stopTimeout": null,
"image": "sioncojp/sample-go-app:latest",
"startTimeout": null,
"firelensConfiguration": null,
"dependsOn": null,
"disableNetworking": null,
"interactive": null,
"healthCheck": null,
"essential": true,
"links": null,
"hostname": null,
"extraHosts": null,
"pseudoTerminal": null,
"user": null,
"readonlyRootFilesystem": null,
"dockerLabels": null,
"systemControls": null,
"privileged": null,
"name": "app"
},
{
"dnsSearchDomains": null,
"logConfiguration": {
"logDriver": null,
"entryPoint": null,
"portMappings": [],
"command": null,
"linuxParameters": {
"capabilities": null,
"sharedMemorySize": null,
"tmpfs": null,
"devices": null,
"maxSwap": null,
"swappiness": null,
"initProcessEnabled": true
},
"cpu": 0,
"environment": [
{
"name": "AWS_REGION",
"value": "ap-northeast-1"
},
{
"name": "DD_SERVICE",
"value": "koyama"
},
{
"name": "DD_SOURCE",
"value": "firelens"
},
{
"name": "DD_TAGS",
"value": "env:sandbox"
},
{
"name": "DELIVERY_STREAM",
"value": "koyama_koyama"
}
],
"resourceRequirements": null,
"ulimits": [
{
"name": "nofile",
"softLimit": 65536,
"hardLimit": 65536
}
],
"dnsServers": null,
"mountPoints": [],
"workingDirectory": null,
"secrets": [
{
"valueFrom": "SSMのDD_API_KEYパス",
"name": "DD_API_KEY"
}
],
"dockerSecurityOptions": null,
"memory": null,
"memoryReservation": null,
"volumesFrom": [],
"stopTimeout": null,
"image": "sioncojp/fluent-bit-datadog-firehose:latest",
"startTimeout": null,
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"config-file-type": "file",
"config-file-value": "/fluent-bit/etc/extra.conf"
}
},
"dependsOn": null,
"disableNetworking": null,
"interactive": null,
"healthCheck": null,
"essential": true,
"links": null,
"hostname": null,
"extraHosts": null,
"pseudoTerminal": null,
"user": "0",
"readonlyRootFilesystem": null,
"dockerLabels": null,
"systemControls": null,
"privileged": null,
"name": "fluentbit"
}
],
"placementConstraints": [],
"memory": "512",
"taskRoleArn": "arn:aws:iam::xxxxxxxxx:role/fargate-koyama",
"compatibilities": [
"EC2",
"FARGATE"
],
"taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:task-definition/koyama:26",
"family": "koyama",
"requiresAttributes": [
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "ecs.capability.execution-role-awslogs"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "ecs.capability.firelens.fluentbit"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "ecs.capability.firelens.options.config.file"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.task-iam-role"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.docker-remote-api.1.25"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "ecs.capability.secrets.ssm.environment-variables"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
},
{
"targetId": null,
"targetType": null,
"value": null,
"name": "ecs.capability.task-eni"
}
],
"pidMode": null,
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"cpu": "256",
"revision": 26,
"status": "ACTIVE",
"inferenceAccelerators": null,
"proxyConfiguration": null,
"volumes": []
}
datadogに飛ばす
firelens -> サイドカーのfluentbit -> datadog
- source: fargate/cron/runtask
- service: コンテナのサービス名
- env: prod/stg/sandbox...
s3に飛ばす
firelens -> fluentbit-> firehose -> s3
https://github.com/cosmo0920/fluent-bit-go-s3 も検証しましたが、1リクエスト1ファイルだったので管理が辛く断念しました。
またfirehoseは各サービスで1つ作るのがベストです。そうしないと同じs3ディレクトリに全ファイルが集約されてしまいます。
s3://生ログ保存バケット/共通ディレクトリ/YYYY/MM/DD/ファイル名
s3://生ログ保存バケット/クラスタ名/サービス名/YYYY/MM/DD/ファイル名
1つずつ作り クラスタ名/サービス名
にfirehose prefixを設定することでディレクティブに保管できます。データ分析の際に使いやすくなるよう意識しました。
fluent-bit自体のロギング
awslogsドライバーでcloudwatchに吐くことができますが、特に必要な情報が今の所なさそうだったので出してません。
もし必要になった場合、cloudwatch logsで全fluent-bitログを保管するlog groupを作り、そこのグループにlogstreamにサービス名を定義して保管するのがベストだと思います。
fluent-bit自体のモニタリング
今の所はしてません。問題が起きたらそれに合わせてすると思います。
またessential:trueなので、落ちた場合、taskにある全コンテナ落ちますので、datadogでサービス単位のコンテナ変動をモニタリングはしております。