LoginSignup
10
6

More than 3 years have passed since last update.

Fargate + Firelens + FluentBit + Firehose で S3(Firehose) と Datadog にログを送る

Last updated at Posted at 2020-04-15

Firelens + FluentBit を使って Datadog と S3(Firehose) にログを送る方法です。
今までは CloudWatchLogs, Lambda, StepFuntion を組み合わせて Datadog と S3 にログを送っていたのですが、Datadog にログを送る Lambda がたまにコケるので、Firelens と FluentBit を使ってログを送るようにしました。

Terraform を使って Fargate で Nginx を動かす

まず、Terraform を使って Fargate で Nginx を動かします。
ファイル全文は こちら に上げてあります。

main.tf
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 を作ります。

main.tf
# ...略

##############################################################
# 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
Dockerfile
FROM amazon/aws-for-fluent-bit:latest
ADD fluent-bit-custom.conf /fluent-bit/etc/fluent-bit-custom.conf
fluent-bit-custom.conf
[FILTER]
    Name modify
    Match *
    Rename log message

main.tf ファイルに手を加えていきます。

Datadog の APIKey をパラメーターストアに保存します。

main.tf
#... 略

###############################################
# 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 のロググループを作成します。

main.tf
##############################################################
# Log Group
##############################################################
resource "aws_cloudwatch_log_group" "fluentbit_log_group" {
  name = "/ecs/fluentbit-log"
}

タスク定義を変更します。

main.tf
#... 略

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 = ["*"] としていますが、セキュリティ的に良くないので、環境に合わせて修正してください。

main.tf
##############################################################
# 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バケットを作ります。

main.tf
##############################################################
# S3
##############################################################
resource "aws_s3_bucket" "log_bucket" {
  bucket = "takoikatakotako-log-bucket"
  acl    = "private"
}

Firehose を作ります。

main.tf
##############################################################
# 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 を追加します。

main.tf
##############################################################
# 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 ファイルを修正します。

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} とすることで、環境変数を設定に反映させることができます。

fluent-bit-custom.conf
[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"
  }
]
10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6