Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

TerraformでECS FargateなコンテナにFireLensを適用する

はじめに

FargateのログはデフォルトだとCloudWatch Logsに収集される。
このままでも良いのだけど、加工したり転送したりと色々やりたいときに手間にならない方法としてFireLensが使えるらしいので、試してみる。

以前書いた記事をベースに、以下の記事と同様のことをTerraformで実装する。

【Developers.IO】[アップデート] ECS/Fargateでログ出力先をカスタマイズできる「FireLens」機能がリリースされました

事前準備

上記の記事の構成でFireLensを使うのに必要なのは以下。
まずはこれを用意していく。

  • ログの書き込み先となるFirehoseの準備
  • ECSのタスクロール(Notタスク実行ロール)へのFirehoseへの書き込み権限のアタッチ
  • Firehoseに対する書き込み先のS3の書き込み権限のアタッチ

これまでの記事に書いているように、localなリソースは名前の定義程度なので、テキトーに良い感じな名前を振ってもらえれば良い。

まずは、以下のような感じでログの書き込み先を作成する。
s3_configurationcloudwatch_logging_optionsは有ってもなくても良い。
エラー時の原因調査にログが必要であればつけておこう(これがないと、本当に何の手掛かりもなくログが出力されないことになる)
エラー時に出力するlog_group_nameは、別のリソースで作っておく。

loggroup.tf
################################################################################
# CloudWatch Logs                                                              #
################################################################################
resource "aws_cloudwatch_log_group" "firelenstest"{
  name = "${local.loggroup_name}"
}
kinesis.tf
################################################################################
# Kinesis Firehose                                                             #
################################################################################
resource "aws_kinesis_firehose_delivery_stream" "firelenstest" {
  name        = "${local.stream_name}"
  destination = "s3"

  s3_configuration {
    role_arn   = "${data.aws_iam_role.firehose.arn}"
    bucket_arn = "${data.aws_s3_bucket.firelenstest.arn}"

    cloudwatch_logging_options {
      enabled         = "true"
      log_group_name  = "${data.aws_cloudwatch_log_group.firelenstest.name}"
      log_stream_name = "kinesis_error"
    }
  }
}

次に、ECSのタスクロールにFirehoseへの書き込み権限をアタッチする。
タスク実行ロールではなくてタスクロールの信頼ポリシーの設定なのに、Principalsがecs-tasks.amazonaws.comなのは不思議だが、どうやらこれでOKらしい。

iam.tf
################################################################################
# IAM Role for ECS Task                                                        #
################################################################################
resource "aws_iam_role" "ecs_task" {
  name               = "${local.ecs_task_role_name}"
  assume_role_policy = "${data.aws_iam_policy_document.ecs_task_trust.json}"
}

data "aws_iam_policy_document" "ecs_task_trust" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "ecs-tasks.amazonaws.com",
      ]
    }
  }
}

resource "aws_iam_role_policy_attachment" "ecs_task" {
  role       = "${aws_iam_role.ecs_task.name}"
  policy_arn = "${aws_iam_policy.ecs_task_custom.arn}"
}

resource "aws_iam_policy" "ecs_task_custom" {
  name        = "ecs-task-role-policy"
  description = "ECS Task Role Policy for Firehose"
  policy      = "${data.aws_iam_policy_document.ecs_task_custom.json}"
}

data "aws_iam_policy_document" "ecs_task_custom" {
  version   = "2012-10-17"

  statement {
    sid = "FirehosePutRecord"

    effect = "Allow"
    actions = [
      "firehose:PutRecordBatch",
    ]

    resources = [
      "*",
    ]
  }
}

最後に、Firehoseに対する書き込み先のS3の書き込み権限のアタッチをすれば準備完了だ。
書き込み先のS3もここでリソースを準備してしまおう。
こんかい、エラー時にCloudWatch Logsにも出力するつもりなので、logs:PutLogEventsのアクセス権限をFirehoseにアタッチしている。CloudWatch Logsに出さないという強気戦略であれば、不要だろう。
↑の準備とiam.tfで名前が重複しているので、コンカチするか分けるかはお任せする。やりやすい方で良いと思う。

s3.tf
################################################################################
# S3 Bucket                                                                    #
################################################################################
resource "aws_s3_bucket" "firelenstest" {
  bucket = "${local.bucket_name}"
  acl    = "private"
}
iam.tf
################################################################################
# IAM Role for Kinesis Firehose                                                #
################################################################################
resource "aws_iam_role" "firehose" {
  name               = "${local.firehose_role_name}"
  assume_role_policy = "${data.aws_iam_policy_document.firehose_trust.json}"
}

data "aws_iam_policy_document" "firehose_trust" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "firehose.amazonaws.com",
      ]
    }
  }
}

resource "aws_iam_role_policy_attachment" "firehose" {
  role       = "${aws_iam_role.firehose.name}"
  policy_arn = "${aws_iam_policy.firehose_custom.arn}"
}

resource "aws_iam_policy" "firehose_custom" {
  name        = "firehose-role-policy"
  description = "Firehose Role Policy"
  policy      = "${data.aws_iam_policy_document.firehose_custom.json}"
}

data "aws_iam_policy_document" "firehose_custom" {
  version   = "2012-10-17"

  statement {
    sid = "S3Access"

    effect = "Allow"
    actions = [
      "s3:AbortMultipartUpload",
      "s3:GetBucketLocation",
      "s3:GetObject",
      "s3:ListBucket",
      "s3:ListBucketMultipartUploads",
      "s3:PutObject"
    ]

    resources = [
      "arn:aws:s3:::${aws_s3_bucket.firelenstest.bucket}",
      "arn:aws:s3:::${aws_s3_bucket.firelenstest.bucket}/*",
    ]
  }
  statement {
    sid = "CloudWatchLogsAccess"

    effect = "Allow"
    actions = [
      "logs:PutLogEvents"
    ]

    resources = [
      "${aws_cloudwatch_log_group.firelenstest.arn}"
    ]
  }
}

ECSサービスとタスク定義の修正

ECSサービスとタスク定義は以下の通りに修正する。

ポイントは以下の通り。

  • aws_ecs_task_definitionにFireLensのコンテナ(Fluentbitのコンテナ)を仕込んでいるので、通常のHTTPサーバとは構成が異なる可能性あり
  • コンテナイメージのパスは公式のAWS Fargate 用ユーザーガイドに書かれている値を設定する。他所のリポジトリから取得することでイメージが未来永劫同じであることの担保がされなくなってしまうため、それを嫌うのであれば、自分のECRリポジトリにコピーを行い、安定板として自分で管理するのが良い。が、その後のメンテ性とは裏返しになるので、注意が必要な部分ではある。

ポイントになるのは以下の部分。
デフォルトではawslogsで定義する部分を以下のように変更する。
optionsで指定できるプロパティは、AWS Fargate用ユーザーガイドによると、

Fluentd または Fluent Bit 出力設定の生成に使用されます。

らしい。が、Fluent Bitのドキュメントには言及がない。おそらくこの辺が使えるのだろうが、イマイチよく分からないな……。

      "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": {
          "Name": "firehose",
          "region": "${data.aws_region.current.name}",
          "delivery_stream": "${aws_kinesis_firehose_delivery_stream.firelenstest.name}"
        }
      }

また、以下の部分で、Fluent Bitをサイドカーコンテナとして起動している。
imageのパスは、↑のユーザーガイドに書かれているものを設定する。リージョンごとに違うので注意。Fluent Bitそのものが出力するログは、awslogsでCloudWatch Logsに転送する(自分のストリームにはログは出せないというか、出してしまうと障害解析ができない)。

    {
      "name" : "log_router",
      "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest",
      "firelensConfiguration": {
        "type": "fluentbit"
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "${data.aws_cloudwatch_log_group.firelenstest.name}",
          "awslogs-region": "${data.aws_region.current.name}",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }

ECSを定義するHCLの全文は以下。

ecs.tf
ecs.tf
resource "aws_ecs_cluster" "firelenstest" {
  name = "${local.ecs_cluster_name}"
}

resource "aws_ecs_task_definition" "firelenstest" {
  family                   = "${local.ecs_family_name}"
  task_role_arn            = "${data.aws_iam_role.ecs_task.arn}"
  execution_role_arn       = "${data.aws_iam_role.task_execution.arn}"
  network_mode             = "awsvpc"
  cpu                      = "256"
  memory                   = "1024"
  requires_compatibilities = [
    "FARGATE",
  ]
  container_definitions    = <<EOF
  [
    {
      "name" : "${local.ecs_container_name}",
      "image": "${data.aws_ecr_repository.firelenstest.repository_url}:${local.container_image_tag}",
      "cpu": 0,
      "memoryReservation": 512,
      "portMappings": [
        {
          "containerPort": 9000,
          "hostPort": 9000,
          "protocol": "tcp"
        }
      ],
      "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": {
          "Name": "firehose",
          "region": "${data.aws_region.current.name}",
          "delivery_stream": "${aws_kinesis_firehose_delivery_stream.firelenstest.name}"
        }
      }
    },
    {
      "name" : "log_router",
      "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest",
      "firelensConfiguration": {
        "type": "fluentbit"
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "${data.aws_cloudwatch_log_group.firelenstest.name}",
          "awslogs-region": "${data.aws_region.current.name}",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
  EOF
}

resource "aws_ecs_service" "firelenstest" {
  name = "${local.ecs_service_name}"
  cluster = "${aws_ecs_cluster.firelenstest.id}"
  launch_type = "FARGATE"
  task_definition = "${aws_ecs_task_definition.firelenstest.arn}"
  desired_count = 1

  load_balancer {
    target_group_arn = "${data.aws_alb_target_group.firelenstest.arn}"
    container_name   = "${var.prefix}-Container"
    container_port   = 9000
  }

  deployment_controller {
    type = "CODE_DEPLOY"
  }

  network_configuration {
    subnets = flatten(["${data.aws_subnet_ids.my_vpc.ids}",])

    security_groups = [
      "${data.aws_security_group.firelenstest.id}",
    ]

    assign_public_ip = "true"
  }
}

出力の確認

これを動かすと、S3に
キャプチャ3.png
こんな感じで出力される。

①…時間単位でディレクトリが作られる
②…kinesis.tfでs3_configurationのデフォルト設定を適用すると、5MiBまたは5分単位で出力される。

ログ分割に関する時間やバッファリングのチューニングは↑この設定で行える。

ログ出力そのものの設定は、Fluent Bit側の設定になる。詳細はユーザーガイドの『カスタム設定ファイルの指定』を参照。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away