Firelens + FluentBit を使って Datadog と S3(Firehose) にログを送る方法です。
今までは CloudWatchLogs, Lambda, StepFuntion を組み合わせて Datadog と S3 にログを送っていたのですが、Datadog にログを送る Lambda がたまにコケるので、Firelens と FluentBit を使ってログを送るようにしました。
Terraform を使って Fargate で Nginx を動かす
まず、Terraform を使って Fargate で Nginx を動かします。
ファイル全文は こちら に上げてあります。
resource "aws_ecs_cluster" "nginx_cluster" {
name = "nginx-cluster"
}
resource "aws_ecs_service" "ecs_service" {
name = "nginx-service"
cluster = aws_ecs_cluster.nginx_cluster.name
launch_type = "FARGATE"
desired_count = "1"
task_definition = aws_ecs_task_definition.task_definition.arn
# ECSタスクへ設定するネットワークの設定
network_configuration {
subnets = [aws_subnet.public_subnet_1a.id, aws_subnet.public_subnet_1c.id, aws_subnet.public_subnet_1d.id]
security_groups = [aws_security_group.security_group.id]
assign_public_ip = true
}
}
resource "aws_ecs_task_definition" "task_definition" {
family = "nginx-task-definition"
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
network_mode = "awsvpc"
task_role_arn = aws_iam_role.ecs_app_role.arn
execution_role_arn = aws_iam_role.ecs_app_role.arn
container_definitions = <<EOL
[
{
"essential":true,
"image":"nginx:latest",
"name":"nginx",
"logConfiguration": {
"logDriver": "awslogs",
"secretOptions": null,
"options": {
"awslogs-group": "${aws_cloudwatch_log_group.nginx_log_group.name}",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"memoryReservation":100,
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
]
}
]
EOL
}
タスクのパプリックIPにアクセスし、Nginx の起動画面が表示されることを確認してください。
FluentBit を使って Nginx のログを Datadog に送る
次に FluentBit を使って Nginx のログを Datadog に送ります。
Datadogでログがパースされるために、Key をデフォルトの log
から message
に変える必要があったため、FluentBitのカスタムイメージを作ります。
ファイル全文は こちら に上げてあります。
カスタムイメージを入れる ECR Repository を作ります。
# ...略
##############################################################
# ECR
##############################################################
resource "aws_ecr_repository" "ecr_repository" {
name = "my-fluent-bit"
}
FluentBit のカスタムイメージを作成し、ECR にプッシュします。
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/my-fluent-bit
$ docker build -t my-fluent-bit .
$ docker tag my-fluent-bit:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/my-fluent-bit:latest
$ docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/my-fluent-bit:latest
FROM amazon/aws-for-fluent-bit:latest
ADD fluent-bit-custom.conf /fluent-bit/etc/fluent-bit-custom.conf
[FILTER]
Name modify
Match *
Rename log message
main.tf
ファイルに手を加えていきます。
Datadog の APIKey をパラメーターストアに保存します。
#... 略
###############################################
# Parameter Store
###############################################
resource "aws_ssm_parameter" "ssm_parameter" {
overwrite = true
name = "DATADOG_API_KEY"
description = "Datadog API Key"
type = "SecureString"
value = local.datadog_apikey
}
Nginx のロググループは削除し、FluentBit のロググループを作成します。
##############################################################
# Log Group
##############################################################
resource "aws_cloudwatch_log_group" "fluentbit_log_group" {
name = "/ecs/fluentbit-log"
}
タスク定義を変更します。
#... 略
resource "aws_ecs_task_definition" "task_definition" {
family = "nginx-task-definition"
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
network_mode = "awsvpc"
task_role_arn = aws_iam_role.ecs_app_role.arn
execution_role_arn = aws_iam_role.ecs_app_role.arn
container_definitions = <<EOL
[
{
"essential":true,
"image":"${aws_ecr_repository.ecr_repository.repository_url}:latest",
"name":"log_router",
"logConfiguration": {
"logDriver": "awslogs",
"secretOptions": null,
"options": {
"awslogs-group": "${aws_cloudwatch_log_group.fluentbit_log_group.name}",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"firelensConfiguration":{
"type":"fluentbit",
"options":{
"enable-ecs-log-metadata":"false",
"config-file-type": "file",
"config-file-value": "/fluent-bit/etc/fluent-bit-custom.conf"
}
},
"memoryReservation":50
},
{
"essential":true,
"image":"nginx:latest",
"name":"nginx",
"logConfiguration":{
"logDriver":"awsfirelens",
"options":{
"Name":"datadog",
"Host":"http-intake.logs.datadoghq.com",
"TLS":"on",
"dd_service":"my-nginx-service",
"dd_source":"my-nginx-source",
"dd_tags":"project:my-nginx",
"format": "none",
"provider":"ecs"
},
"secretOptions": [{
"name": "apikey",
"valueFrom": "${aws_ssm_parameter.ssm_parameter.name}"
}]
},
"memoryReservation":100,
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
]
}
]
EOL
}
タスクのパプリックIPにアクセスし、Nginx にアクセスすると Datadog にログが入ることを確認してください。
FluentBit を使って Nginx のログを Datadog と S3(Firehose) に送る
FluentBit(amazon/aws-for-fluent-bit) は様々なプラグインが含まれていますが、直接 S3 に送ることはできません。
しかし、Firehose のプラグイン(amazon-kinesis-firehose-for-fluent-bit)は含まれているため、
Firehose 経由で S3 にログを送ります。
ファイル全文は こちら に上げてあります。
Firehose 用の IAMロールを作ります。
resources = ["*"]
としていますが、セキュリティ的に良くないので、環境に合わせて修正してください。
##############################################################
# IAM
##############################################################
# ... 略
# for Firehose
resource "aws_iam_role" "firehose_role" {
name = "firehose-role"
assume_role_policy = data.aws_iam_policy_document.firehose_role_policy_document.json
}
data "aws_iam_policy_document" "firehose_role_policy_document" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["firehose.amazonaws.com"]
}
}
}
resource "aws_iam_policy" "firehose_policy" {
name = "firehose-policy"
policy = data.aws_iam_policy_document.firehose_policy_document.json
}
data "aws_iam_policy_document" "firehose_policy_document" {
statement {
effect = "Allow"
actions = [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
"kinesis:DescribeStream",
"kinesis:GetShardIterator",
"kinesis:GetRecords",
"kinesis:ListShards",
"kms:Decrypt"
]
resources = ["*"]
}
}
resource "aws_iam_role_policy_attachment" "firehose_role_policy_attachment" {
role = aws_iam_role.firehose_role.name
policy_arn = aws_iam_policy.firehose_policy.arn
}
Firehose からログが送られる S3バケットを作ります。
##############################################################
# S3
##############################################################
resource "aws_s3_bucket" "log_bucket" {
bucket = "takoikatakotako-log-bucket"
acl = "private"
}
Firehose を作ります。
##############################################################
# Firehose
##############################################################
resource "aws_kinesis_firehose_delivery_stream" "my-firehose" {
name = "my-firehose"
destination = "extended_s3"
extended_s3_configuration {
role_arn = aws_iam_role.firehose_role.arn
bucket_arn = aws_s3_bucket.log_bucket.arn
}
}
ECS を動かすロールの権限に firehose:PutRecordBatch
を追加します。
##############################################################
# IAM
##############################################################
# for ECS
# 略
resource "aws_iam_policy" "ecs_app_policy" {
name = "ecs-policy"
policy = data.aws_iam_policy_document.ecs_policy_document.json
}
data "aws_iam_policy_document" "ecs_policy_document" {
statement {
effect = "Allow"
actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ssm:GetParameters",
"secretsmanager:GetSecretValue",
"firehose:PutRecordBatch",
"kms:Decrypt"
]
resources = ["*"]
}
}
fluent-bit-custom.conf
ファイルを修正します。
[FILTER]
Name modify
Match *
Rename log message
[OUTPUT]
Name firehose
Match *
region ap-northeast-1
delivery_stream my-firehose
--no-cache
オプションを付けて再ビルドし、ECR にプッシュします。
$ docker build -t my-fluent-bit . --no-cache
ECS のタスクを再起動し、S3 にログが入ることを確認します。
おまけ、FluentBit の設定に環境変数を使う
${ENVIRONMENT}
とすることで、環境変数を設定に反映させることができます。
[FILTER]
Name modify
Match *
Rename log message
[OUTPUT]
Name firehose
Match *
region ap-northeast-1
delivery_stream ${DELIVERY_STREAM}
タスク定義に環境変数を追加する
"environment": [
{
"name": "DELIVERY_STREAM",
"value": "my-firehose"
}
]