0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EFS を利用してカスタム FireLens を実装し、アプリケーション ログを CloudWatch Logs に出力する

Last updated at Posted at 2024-10-04

背景

ECS Fargate で稼働するアプリケーションのログファイル(非標準出力)の内容をCloudWatch Logsに出力する要望があります。

しかし、AWS Fargate でホストされるタスクは、file 設定ファイルタイプのみをサポートします。1

メンテナンス性、利用易さ、コスト、またシステムCI/CDを複雑化したくない等の理由で、FireLensとその設定ファイルはカスタムイメージを利用したくないです。

FireLens設定ファイルは EFS に格納し、その EFS を ECS にマウントする案を考えました。

  • システムは、Dev環境、STG環境、本番環境で構成されている
  • 環境ごとにAWSアカウントが異なって、アカウント間にTransit Gatewayで繋がっている
  • EFS は各環境で作成する
  • 運用管理サーバ(踏み台)は、Dev環境で構築されている

EFS構築

  1. EFS File system を作成する

    • VPC は、運用サーバ(EC2)また ECS が稼働する VPC を選択する
  2. AccessPoint を作成する

    • File system は、上記 1 で作成した File system を選択する
    • Root directory path は / を設定する
  3. 各環境で上記1,2の手順を実施する

EC2にEFSをマウントする

注意: クロスアカウントシナリオでは通常の NFS コマンドを使用できないため、botocore と Amazon EFS クライアントが必要になります。

  1. セキュリティグループ
     EC2 用のセキュリティグループに、NAS用2049ポートを許可する。

    image.png

  2. Amazon EFS クライアントをインストールする

    $ sudo yum install -y amazon-efs-utils
    
  3. クロスアカウント EFS ファイルシステムにアクセスしてマウントするには、以下のようなポリシーステートメントを IAM ポリシーに追加します。

    • 運用サーバ IAM Profile 用 Policy

      aws-ec2-profile-policy
      {
          "Sid": "EfsPermissions",
          "Effect": "Allow",
          "Action": [
              "elasticfilesystem:ClientMount",
              "elasticfilesystem:ClientWrite",
              "elasticfilesystem:ClientRootAccess"
          ],
          "Resource": [
              "arn:aws:elasticfilesystem:region:accountA-id:file-system/file-system-id",
              "arn:aws:elasticfilesystem:region:accountB-id:file-system/file-system-id",
              "arn:aws:elasticfilesystem:region:accountB-id:file-system/file-system-id"
          ]
      }
      
    • STG環境、本番環境 EFS ポリシーに下記内容を追記

      {
          "Sid": "access-point-statement-example03",
          "Effect": "Allow",
          "Principal": {
              "AWS": "arn:aws:iam::{dev_account_id}:root"
          },
          "Action": "*",
          "Resource": "arn:aws:elasticfilesystem:ap-northeast-1:{efs_account_id}:file-system/{file_system_id}"
      }
      
  4. Amazon EFS コンソールにログインして、[File Systems] (ファイルシステム) を選択します。[EFS-File-System-ID] を選択してから、[Network] (ネットワーク) にあるアベイラビリティーゾーンの IP アドレスをメモします。

    image.png

  5. 取得した IP アドレスを使用して、NFS クライアントの /etc/hosts ファイルに hosts エントリを追加します。DNS 名の形式は、mount-target-IP-Address file-system-ID.efs.region.amazonaws.com です。

    以下のコマンド例を参照してください。

    $ echo "10.0.8.117 fs-087e048b084872a24.efs.ap-northeast-1.amazonaws.com" | sudo tee -a /etc/hosts
    
  6. マウントヘルパーを使用して EFS ファイルシステムをマウントします。

    • ファイルシステムDNS名を使用してマウントするには:

      $ sudo mount -t efs -o iam,tls file-system-dns-name efs-mount-point/
      
      $ sudo mount -t efs -o iam,tls fs-087e048b084872a24.efs.ap-northeast-1.amazonaws.com efs/
      

    - または -

    • マウントターゲット IP アドレスを使用してマウントするには:

      $ sudo mount -t efs -o iam,tls,mounttargetip=mount-target-ip file-system-id efs-mount-point/
      
      $ sudo mount -t efs -o iam,tls,mounttargetip=10.0.8.117 fs-087e048b084872a24 efs/
      
  7. 再起動しても自動マウントできるため、/etc/fstab を修正する。2

ECSにEFSをマウントする

  1. ECS 用のセキュリティグループに、NAS用2049ポートを許可する。

  2. ECS Task実行ロールに EFS関する権限、例えばAmazonElasticFileSystemClientFullAccess ロールをアタッチする

  3. 各環境の ECS タスク定義ファイルに、各環境のEFS情報を書き換えた内容を追記する。

    "volumes": [
        {
            "name": "firelens-config",
            "efsVolumeConfiguration": {
                "fileSystemId": "{file-system-id}",
                "rootDirectory": "/",
                "transitEncryption": "ENABLED",
                "authorizationConfig": {
                    "accessPointId": "{access-point-id}",
                    "iam": "ENABLED"
                }
            }
        }
    ]
    

タスク定義にFireLensを設定する

  1. ECS タスク定義に、FireLens用サイドカーを追加

    firelens-side-car
    {
        "name": "log_router",
        "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
        "cpu": 0,
        "memory": 256,
        "memoryReservation": 128,
        "portMappings": [],
        "essential": true,
        "environment": [],
        "mountPoints": [],
        "volumesFrom": [],
        "user": "0",
        "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
                "awslogs-group": "/ecs/Cluster/Service",
                "awslogs-create-group": "true",
                "awslogs-region": "ap-northeast-1",
                "awslogs-stream-prefix": "ecs"
            }
        },
        "systemControls": [],
    }
    
  2. 各環境 FireLens 用の Container 定義に、下記マウント定義を追記する

    "mountPoints": [
        {
            "sourceVolume": "firelens-config",
            "containerPath": "/configs/{env}"
        }
    ],
    
  3. Multiline PARSER 用 parser_springboot.conf ファイルを作成

    以下の設定は、ログのタイムスタンプを見て、次のタイムスタンプまでログを合併する。タイムスタンプが付いていないログは、前のログに統合する。

    [MULTILINE_PARSER]
    name          springboot-multiline
    type          regex
    flush_timeout 1000
    #
    # Regex rules for multiline parsing
    # ---------------------------------
    #
    # configuration hints:
    #
    #  - first state always has the name: start_state
    #  - every field in the rule must be inside double quotes
    #
    # rules |   state name  | regex pattern                  | next state
    # ------|---------------|--------------------------------------------
    rule      "start_state"   "/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}/"      "cont"
    rule      "cont"          "/^(?!\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})/"  "cont"
    
  4. extra.conf ファイルを作成する。

    Parsers_File のパスは、上記ステップ2sourceVolumeパスとなります。

    注意: [OUTPUT]セクションのプラグインNameについて、cloudwatchと最新のcloudwatch_logsがありますが、現時点では cloudwatch_logs は、log_group_name また log_stream_name での $(variable) テンプレートが利用できないため、今回 cloudwatch を利用する。

    extra.conf
    # グローバルな設定
    [SERVICE]
        Flush 1
        Grace 30
        Parsers_File /firelens-configs/parser_springboot.conf
    
    [FILTER]
        Name multiline
        Match *
        multiline.key_content log
        multiline.parser springboot-multiline
    
    [FILTER]
        Name grep
        Match *
        regex log ^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}
        
    [OUTPUT]
        Name cloudwatch
        Match app-log
        region ap-northeast-1
        log_group_name /ecs/Cluster/Service
        log_stream_name app/$(ecs_task_id)
        auto_create_group true
        retry_limit 2
    
  5. EC2 踏み台から、修正した extra.confファイルを各環境用のフォルダにコピーする。

    • /configs/dev/extra.conf
    • /configs/stg/extra.conf
    • /configs/prd/extra.conf
  6. 各環境 FireLens 用のサイドカー定義に、FireLens 設定ファイルを指定する。

    "firelensConfiguration": {
        "type": "fluentbit",
        "options": {
            "config-file-type": "file",
            "config-file-value": "/configs/{env}/extra.conf",
            "enable-ecs-log-metadata": "false"
        }
    }
    
  7. ECS タスク定義のアプリケーション用のコンテナ定義部分の logConfiguration 内容を修正する。

    "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": {
            "Name": "cloudwatch",
            "log_group_name": "/ecs/Cluster/Service",
            "log_stream_name": "app/$(ecs_task_id)",
            "auto_create_group": "true",
            "log_key": "log",
            "region": "ap-northeast-1",
            "retry_limit": "2"
        }
    },
    
  8. 修正後のタスク定義は、下記のようになります。

    ECS タスク定義(taskdef.json)
    taskdef.json
    {
        "containerDefinitions": [
            {
                "name": "app",
                "image": "<IMAGE_URI>",
                "cpu": 0,
                "memory": 512,
                "memoryReservation": 256,
                "portMappings": [
                    {
                        "name": "app-port",
                        "containerPort": 8080,
                        "hostPort": 8080,
                        "protocol": "tcp",
                        "appProtocol": "http"
                    }
                ],
                "essential": true,
                "environment": [],
                "mountPoints": [],
                "volumesFrom": [],
                "logConfiguration": {
                    "logDriver": "awsfirelens",
                    "options": {
                        "Name": "cloudwatch",
                        "log_group_name": "/ecs/Cluster/Service",
                        "log_stream_name": "app/$(ecs_task_id)",
                        "auto_create_group": "true",
                        "log_key": "log",
                        "region": "ap-northeast-1",
                        "retry_limit": "2"
                    }
                },
                "systemControls": []
            },
            {
                "name": "log_router",
                "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
                "cpu": 0,
                "memory": 256,
                "memoryReservation": 128,
                "portMappings": [],
                "essential": true,
                "environment": [],
                "mountPoints": [
                    {
                        "sourceVolume": "firelens-configs",
                        "containerPath": "/firelens-configs"
                    }
                ],
                "volumesFrom": [],
                "user": "0",
                "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                        "awslogs-group": "/ecs/Cluster/Service",
                        "awslogs-create-group": "true",
                        "awslogs-region": "ap-northeast-1",
                        "awslogs-stream-prefix": "ecs"
                    }
                },
                "systemControls": [],
                "firelensConfiguration": {
                    "type": "fluentbit",
                    "options": {
                        "config-file-type": "file",
                        "config-file-value": "/firelens-configs/extra.conf",
                        "enable-ecs-log-metadata": "false"
                    }
                }
            }
        ],
        "family": "ecs-taskdef-app",
        "taskRoleArn": "arn:aws:iam::{account_id}:role/role-ecs-task-exec",
        "executionRoleArn": "arn:aws:iam::{account_id}:role/role-ecs-task-exec",
        "networkMode": "awsvpc",
        "placementConstraints": [],
        "compatibilities": [
            "EC2",
            "FARGATE"
        ],
        "requiresCompatibilities": [
            "FARGATE"
        ],
        "cpu": "256",
        "memory": "512",
        "ephemeralStorage": {
            "sizeInGiB": 100
        },
        "volumes": [
            {
                "name": "firelens-configs",
                "efsVolumeConfiguration": {
                    "fileSystemId": "{file-system-id}",
                    "rootDirectory": "/",
                    "transitEncryption": "ENABLED",
                    "authorizationConfig": {
                        "accessPointId": "{access-point-id}",
                        "iam": "ENABLED"
                    }
                }
            }
        ],
        "runtimePlatform": {
            "cpuArchitecture": "X86_64",
            "operatingSystemFamily": "LINUX"
        }
    }
    

脚注

  1. カスタム設定ファイルを使用する

  2. /etc/fstab に複数の Amazon EFS ファイルシステムをマウントできない

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?