FireLensを使用してECS FargateでホストしているアプリケーションのログをS3とCloudWatch Logsロググループに出力してみた
ECS Fargateでホストしているアプリケーションログをエラーログとそれ以外のログでS3とCloudWatch Logsに振り分ける設定をFireLensで行ってみました。
構成
今回作成するAWSの構成は以下のようになります。
通常のECSクラスターとログの出力先としてS3を作成します。
また、AWS for Fluent Bitの設定ファイルの置き場所用のS3も作成します。
Fargateの場合、設定ファイルの置き場所としてS3を使用できないのですが、こちらのブログで紹介されているようにAWS for Fluent Bitのイメージタグに"init-"がついているものを使用すればS3内に配置した設定ファイルを参照させることができます。
AWS Fargate でホストされるタスクは、file 設定ファイルタイプのみをサポートします。
リソース作成
今回使用するアプリケーションはFlaskを使用します。
Flaskはuwsgiを使用してNginxを経由して配信するためWebサーバー用のコンテナイメージも作成します。
サンプルのアプリケーションは以下のGitHubリポジトリに置いてあります。
事前準備
事前準備としてアプリケーション用とNginx用のコンテナイメージを作成します。
以下のCloudFormationテンプレートを使用してECRを作成してください。
AWSTemplateFormatVersion: "2010-09-09"
Description: ECR
Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Parameters for env Name
Parameters:
- env
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
env:
Type: String
Default: dev
AllowedValues:
- dev
Resources:
# ------------------------------------------------------------#
# ECR
# ------------------------------------------------------------#
ECRApp:
Type: AWS::ECR::Repository
Properties:
EmptyOnDelete: true
EncryptionConfiguration:
EncryptionType: AES256
RepositoryName: !Sub ecr-app-${env}
ECRWeb:
Type: AWS::ECR::Repository
Properties:
EmptyOnDelete: true
EncryptionConfiguration:
EncryptionType: AES256
RepositoryName: !Sub ecr-web-${env}
ECRの作成後、CloudShellから以下のコマンドを実行してコンテナイメージを作成してください。
git clone https://github.com/Kobayashi-Riku0226/Flask.git
cd Flask/
cd app/
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t ecr-app-dev .
docker tag ecr-app-dev:latest AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-app-dev:latest
docker push AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-app-dev:latest
cd ../web/
docker build -t ecr-web-dev .
docker tag ecr-web-dev:latest AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-web-dev:latest
docker push AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-web-dev:latest
CloudWatch Logsに全てのログを出力
まずは全てのログをCloudWatch Logsに出力する設定を行ってみます。
以下のCloudFormationテンプレートを使用してリソースを作成します。
AWSTemplateFormatVersion: "2010-09-09"
Description: ECS
Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Parameters for env Name
Parameters:
- env
- Label:
default: Parameters for Network
Parameters:
- VPCCIDR
- PublicSubnet01CIDR
- PublicSubnet02CIDR
- PrivateSubnet01CIDR
- PrivateSubnet02CIDR
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
env:
Type: String
Default: dev
AllowedValues:
- dev
VPCCIDR:
Default: 192.168.0.0/16
Type: String
PublicSubnet01CIDR:
Default: 192.168.0.0/24
Type: String
PublicSubnet02CIDR:
Default: 192.168.1.0/24
Type: String
PrivateSubnet01CIDR:
Default: 192.168.2.0/24
Type: String
PrivateSubnet02CIDR:
Default: 192.168.3.0/24
Type: String
Resources:
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3LogBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub s3-log-${AWS::StackName}-${AWS::AccountId}
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
S3ConfBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub s3-conf-${AWS::StackName}-${AWS::AccountId}
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
TaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ecs-tasks.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
RoleName: !Sub iam-${env}-ecs-tast-execution-role
TaskRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ecs-tasks.amazonaws.com
Action:
- sts:AssumeRole
RoleName: !Sub iam-${env}-ecs-tast-role
TaskRolePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Sub iam-${env}-ecs-task-role-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:GetBucketLocation"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "logs:DescribeLogStreams"
- "logs:CreateLogGroup"
- "ssmmessages:CreateControlChannel"
- "ssmmessages:CreateDataChannel"
- "ssmmessages:OpenControlChannel"
- "ssmmessages:OpenDataChannel"
Resource:
- "*"
Roles:
- !Ref TaskRole
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub vpc-${env}
# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------#
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub igw-${env}
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
PublicSubnet01:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PublicSubnet01CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub subnet-${env}-pub1
VpcId: !Ref VPC
PublicSubnet02:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1c
CidrBlock: !Ref PublicSubnet02CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub subnet-${env}-pub2
VpcId: !Ref VPC
PrivateSubnet01:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PrivateSubnet01CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub subnet-${env}-prv1
VpcId: !Ref VPC
PrivateSubnet02:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1c
CidrBlock: !Ref PrivateSubnet02CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub subnet-${env}-prv2
VpcId: !Ref VPC
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: rtb-${env}-pub
PublicRouteTableRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
PublicRtAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet01
PublicRtAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet02
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: rtb-${env}-prv
PrivateRtAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet01
PrivateRtAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet02
# ------------------------------------------------------------#
# NAT Gateway
# ------------------------------------------------------------#
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub eip-${env}-nat
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet01
Tags:
- Key: Name
Value: !Sub ngw-${env}
PrivateRouteTableRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
# ------------------------------------------------------------#
# SecurityGroup
# ------------------------------------------------------------#
ALBSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for alb
GroupName: !Sub securitygroup-${env}-alb
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- FromPort: 80
IpProtocol: tcp
CidrIp: 0.0.0.0/0
ToPort: 80
Tags:
- Key: Name
Value: !Sub securitygroup-${env}-alb
VpcId: !Ref VPC
ECSSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for ecs
GroupName: !Sub securitygroup-${env}-ecs
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- FromPort: 80
IpProtocol: tcp
SourceSecurityGroupId: !Ref ALBSG
ToPort: 80
Tags:
- Key: Name
Value: !Sub securitygroup-${env}-ecs
VpcId: !Ref VPC
VPCEndpointSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for vpc endpoint
GroupName: !Sub securitygroup-${env}-vpc-endpoint
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- FromPort: 443
IpProtocol: tcp
SourceSecurityGroupId: !Ref ECSSG
ToPort: 443
Tags:
- Key: Name
Value: !Sub securitygroup-${env}-vpc-endpoint
VpcId: !Ref VPC
# ------------------------------------------------------------#
# VPC Endpoint
# ------------------------------------------------------------#
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PrivateRouteTable
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
VpcId: !Ref VPC
ECRdkrEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ecr.dkr
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet01
- !Ref PrivateSubnet02
SecurityGroupIds:
- !Ref VPCEndpointSG
ECRapiEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ecr.api
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet01
- !Ref PrivateSubnet02
SecurityGroupIds:
- !Ref VPCEndpointSG
LogsEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.logs
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet01
- !Ref PrivateSubnet02
SecurityGroupIds:
- !Ref VPCEndpointSG
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: false
Name: !Sub alb-${env}-ecs
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSG
Subnets:
- !Ref PublicSubnet01
- !Ref PublicSubnet02
Tags:
- Key: Name
Value: !Sub alb-${env}-ecs
Type: application
TargetGroup1:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckPort: traffic-port
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 5
IpAddressType: ipv4
Matcher:
HttpCode: 200
Name: !Sub tg-${env}-01
Port: 80
Protocol: HTTP
ProtocolVersion: HTTP1
Tags:
- Key: Name
Value: !Sub tg-${env}-01
TargetType: ip
UnhealthyThresholdCount: 2
VpcId: !Ref VPC
ALBHTTPListener1:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup1
Type: forward
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
# ------------------------------------------------------------#
# ECS
# ------------------------------------------------------------#
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
CapacityProviders:
- FARGATE
ClusterName: !Sub ecs-${env}-cluster
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
ECSTaskDef:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/ecr-web-${env}:latest
Essential: true
LogConfiguration:
LogDriver: awsfirelens
Options:
log_group_name: !Sub /ecs/${env}/web-log
log_stream_prefix: web/
region: !Ref "AWS::Region"
auto_create_group: "true"
Name: cloudwatch_logs
Name: !Sub task-web-${env}
PortMappings:
- ContainerPort: 80
HostPort: 80
- Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/ecr-app-${env}:latest
Essential: true
LogConfiguration:
LogDriver: awsfirelens
Options:
log_group_name: !Sub /ecs/${env}/app-log
log_stream_prefix: app/
region: !Ref "AWS::Region"
auto_create_group: "true"
Name: cloudwatch_logs
Name: !Sub task-app-${env}
PortMappings:
- ContainerPort: 3031
HostPort: 3031
- Image: public.ecr.aws/aws-observability/aws-for-fluent-bit:stable
Essential: false
environment:
- name: ENV
value: !Ref env
- name: AWS_REGION
value: !Ref "AWS::Region"
- name: AWS_ACCOUNT_ID
value: !Ref "AWS::AccountId"
FirelensConfiguration:
Type: fluentbit
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: /ecs/ecs-aws-firelens-sidecar-container
mode: non-blocking
awslogs-create-group: "true"
max-buffer-size: 25m
awslogs-region: ap-northeast-1
awslogs-stream-prefix: firelens
Name: !Sub task-firelens-${env}
Cpu: 256
ExecutionRoleArn: !Ref TaskExecutionRole
TaskRoleArn: !Ref TaskRole
Family: !Sub task-${env}
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ECSService:
Type: AWS::ECS::Service
DependsOn:
- ALBHTTPListener1
Properties:
Cluster: !Ref ECSCluster
DesiredCount: 1
EnableExecuteCommand: true
LoadBalancers:
- ContainerName: !Sub task-web-${env}
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup1
NetworkConfiguration:
AwsvpcConfiguration:
SecurityGroups:
- !Ref ECSSG
Subnets:
- !Ref PrivateSubnet01
- !Ref PrivateSubnet02
ServiceName: !Sub service-${env}
TaskDefinition: !Ref ECSTaskDef
PlatformVersion: LATEST
DeploymentConfiguration:
DeploymentCircuitBreaker:
Enable: true
Rollback: true
DeploymentController:
Type: ECS
# ------------------------------------------------------------#
# AutoScaling
# ------------------------------------------------------------#
ServiceAutoScalingRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub iam-${env}-ecs-autoscaling-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: application-autoscaling.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub iam-${env}-ecs-autoscaling-policy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- application-autoscaling:*
- cloudwatch:DescribeAlarms
- cloudwatch:PutMetricAlarm
- ecs:DescribeServices
- ecs:UpdateService
Resource: "*"
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MinCapacity: 1
MaxCapacity: 4
ResourceId: !Sub service/${ECSCluster}/${ECSService.Name}
RoleARN: !GetAtt ServiceAutoScalingRole.Arn
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScaleOutPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: ecs-autoscaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceScalingTarget
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70.0
ScaleInCooldown: 300
ScaleOutCooldown: 300
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
とりあえずCloudWatch Logsに全てのログを出すだけであれば以下のようにタスク定義内のコンテナ定義で設定することが可能です。
指定できるオプションはこちらのドキュメントに記載されています。
LogConfiguration:
LogDriver: awsfirelens
Options:
log_group_name: !Sub /ecs/${env}/web-log
log_stream_prefix: web/
region: !Ref "AWS::Region"
auto_create_group: "true"
Name: cloudwatch_logs
上記のCloudFormationテンプレートが正常にデプロイできると「/ecs/dev/app-log」と「/ecs/dev/web-log」というロググループ内にログが出力されていきます。
試しに以下のようにALB経由でアクセスするとエラーログが「/ecs/dev/app-log」に出力されることが確認できます。
http://ALBのDNS名/err
以下のようにログが確認できます。
タスクのARNやタスク定義のバージョンなども確認ができます。
{
"source": "stdout",
"log": "[2025-09-29 07:40:57,705] ERROR in app: test err access",
"container_id": "c98113c7df794737895671db6784c2c6-3222487192",
"container_name": "task-app-dev",
"ecs_cluster": "ecs-dev-cluster",
"ecs_task_arn": "arn:aws:ecs:ap-northeast-1:AWSアカウントID:task/ecs-dev-cluster/c98113c7df794737895671db6784c2c6",
"ecs_task_definition": "task-dev:33"
}
S3とCloudWatch Logsに振り分ける設定
まずはFluent Bitの設定ファイルを作成します。
今回作成した設定ファイルは以下になります。
[FILTER]
Name rewrite_tag
Match task-app-${ENV}-firelens*
Rule $log ^(?=.*ERROR).*$ task-app-${ENV}-err-log-$container_id false
Rule $log ^(?!.*ERROR).*$ task-app-${ENV}-not-err-log-$container_id false
[OUTPUT]
Name cloudwatch_logs
Match task-web-${ENV}-firelens*
auto_create_group true
log_group_name /ecs/${ENV}/web-log
log_stream_prefix web/
region ${AWS_REGION}
[OUTPUT]
Name cloudwatch_logs
Match task-app-${ENV}-err-log*
auto_create_group true
log_group_name /ecs/${ENV}/app-log
log_stream_prefix app/
region ${AWS_REGION}
[OUTPUT]
Name s3
Match task-app-${ENV}-not-err-log*
bucket s3-log-ecs-${AWS_ACCOUNT_ID}
region ${AWS_REGION}
total_file_size 100M
s3_key_format /app/$TAG/%Y/%m/%d/%H_%M_%S/$UUID.gz
compression gzip
Filterセクションでrewrite_tagを使いアプリケーションのログから文字列にERRORが含まれているかいないかでエラーログ (app-err-log) とそれ以外のログ (app-not-err-log) でタグを付けています。
Matchについては「<コンテナ名>-firelens*」という形式で一致するようなので「task-app-${ENV}-firelens*」という形にしています。
タスク定義から生成されるログアウトプットは、<コンテナ名>-firelens* と <コンテナ名>-firelens** にマッチします。つまり、Fluent Bit を使用していて、コンテナ名が app の場合、マッチパターンは app-firelens* となります。
OUTPUTセクションはNginxのログとアプリケーションのログ用で3つ作成しています。
一つ目がNginxのログ用の設定です。
NginxはシンプルにCloudWatch Logsへ全てのログを出力するようにしています。
ここは、Filterセクションを追加してHTTPステータスコードなどで出力先を振り分けるといったことも可能だと思います。
二つ目がアプリケーションのエラーログをCloudWatch Logsに出力する設定です。
Match部分を「task-app-${ENV}-err-log*」としてFilterセクションで設定したタグに引っかかるようにしています。
三つめがアプリケーションのエラーログ以外をS3に出力する設定です。
Match部分を「task-app-${ENV}-not-err-log*」としてFilterセクションで設定したタグに引っかかるようにしています。
また、出力時にgzip圧縮を行うようにcompressionを設定しています。
他に設定できるパラメータは以下のドキュメントに記載されています。
上記の設定を「firelens.conf」というファイルに保存して「s3-conf-ecs-AWSアカウントID」というバケット名に配置してください。
S3バケットに配置したらCloudFormationテンプレートのタスク定義部分を以下のように変更してください。
FireLensコンテナの環境変数に「firelens.conf」を読み込ませる設定を行っています。
ECSTaskDef:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/ecr-web-${env}:latest
Essential: true
LogConfiguration:
LogDriver: awsfirelens
Name: !Sub task-web-${env}
PortMappings:
- ContainerPort: 80
HostPort: 80
- Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/ecr-app-${env}:latest
Essential: true
LogConfiguration:
LogDriver: awsfirelens
Name: !Sub task-app-${env}
PortMappings:
- ContainerPort: 3031
HostPort: 3031
- Image: public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.34.0
Essential: false
environment:
- name: ENV
value: !Ref env
- name: AWS_REGION
value: !Ref "AWS::Region"
- name: AWS_ACCOUNT_ID
value: !Ref "AWS::AccountId"
- name: aws_fluent_bit_init_s3_1
value: !Sub arn:aws:s3:::s3-conf-ecs-${AWS::AccountId}/firelens.conf
FirelensConfiguration:
Type: fluentbit
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: /ecs/ecs-aws-firelens-sidecar-container
mode: non-blocking
awslogs-create-group: "true"
max-buffer-size: 25m
awslogs-region: ap-northeast-1
awslogs-stream-prefix: firelens
Name: !Sub task-firelens-${env}
Cpu: 256
ExecutionRoleArn: !Ref TaskExecutionRole
TaskRoleArn: !Ref TaskRole
Family: !Sub task-${env}
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
設定ファイルの配置とCloudFormationテンプレートの更新が完了したらECSサービスの更新を行い、新しいタスクを起動させます。
更新時は最新のタスク定義が選択されていることと「新しいデプロイの強制」にチェックを入れてください。
新しいタスクが起動したらALBのDNS名から何度かアクセスを行いCloudWatch Logsにログが出力されることを確認します。
以下の画像の通りエラーログのみCloudWatch Logsに出力できるようになっていることが確認できます。
S3側も「s3-log-ecs-AWSアカウントID」にログが出力されていることが確認できます。
さいごに
FireLensを使用してS3とCloudWatch Logsにログを振り分ける設定を試してみました。
ログの内容で出力先を振り分ける設定は初めてやったので設定ファイルの書き方に戸惑いましたが、慣れればサクッと設定できそうな内容でした。