티스토리 뷰

프로세스

1️⃣ 이미지 준비

  • API Server
    • Dockerfile 을 통해 API Server 이미지를 생성하여 ECR 에 올려서 준비하기
    • (추후 CI/CD 를 통해 자동화됨)
  • Grafana
    • 프로비저닝이 필요하다면 아키텍처에 맞는 grafana / loki 이미지를 pull 받아 프로비저닝된 이미지로 생성하여 ECR 에 올려서 준비하기
    • 이 프로젝트에서는 기본 이미지를 사용한 후 나중에 Terraform 으로 프로비저닝 해줄 예정이기에  ECR 에 올리는거 생략하고 기본 Grafana 이미지를 준비한다.
  • Loki
    • 프로비저닝이 필요하여 아래의 config 파일과 Dockerfile 로 이미지 생성하여 ECR 에 올려서 준비하기
더보기
auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki

ingester:
  lifecycler:
    address: 127.0.0.1
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
  chunk_idle_period: 1h
  max_chunk_age: 1h

schema_config:
  configs:
    - from: 2021-05-12
      store: boltdb-shipper
      object_store: s3
      schema: v11
      index:
        prefix: index_
        period: 24h

storage_config:
  boltdb_shipper:
    shared_store: s3
    active_index_directory: /loki/boltdb-shipper-active
    cache_location: /loki/boltdb-shipper-cache
  aws:
    s3: s3://<access-key>:<private-key>@ap-northeast-2/(S3 버킷 이름)
FROM grafana/loki:2.8.3

COPY ./loki.yml /etc/loki/config.yaml

CMD ["-config.file=/etc/loki/config.yaml"]

2️⃣ Amazon ECS 클러스터 생성

EC2 인스턴스 ASG 옵션의 ECS 클러스터를 생성한다.

3️⃣ Grafana 태스크 정의 생성

  • Service Connect 를 사용하여 Loki 의 dns 로 내부 통신 (client)

Service Connect Client 예시 사진

더보기

태스크 정의 JSON 코드

{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-2:(aws 계정 ID):task-definition/(태스크 정의 이름):(개정 번호)",
    "containerDefinitions": [
        {
            "name": "grafana-container",
            "image": "grafana/grafana:latest",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "grafana3000pm",
                    "containerPort": 3000,
                    "hostPort": 3000,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "GF_AUTH_ANONYMOUS_ORG_ROLE",
                    "value": "Admin"
                },
                {
                    "name": "GF_AUTH_ANONYMOUS_ENABLED",
                    "value": "true"
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "startTimeout": 30,
            "stopTimeout": 120,
            "user": "0",
            "privileged": true,
            "readonlyRootFilesystem": false,
            "interactive": false,
            "pseudoTerminal": false,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/aws/ecs/(서비스 이름)/grafana-container",
                    "awslogs-region": "ap-northeast-2",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "family": "(태스크 정의 이름)",
    "taskRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "executionRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "networkMode": "bridge",
    "revision": (개정 번호),
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "com.amazonaws.ecs.capability.privileged-container"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
        },
        {
            "name": "com.amazonaws.ecs.capability.task-iam-role"
        },
        {
            "name": "ecs.capability.container-ordering"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2"
    ],
    "requiresCompatibilities": [
        "EC2"
    ],
    "cpu": "512",
    "memory": "512",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2023-12-05T06:11:00.637Z",
    "registeredBy": "arn:aws:iam::(aws 계정 ID):user/dory",
    "tags": []
}

4️⃣ Loki 태스크 정의 생성

  • Service Connect를 사용하여 Loki의 dns로 내부 통신 (client & server)

Service Connect Client & Server 예시 사진

더보기

태스크 정의 JSON 코드

{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-2:(aws 계정 ID):task-definition/(태스크 정의 이름):(개정 번호)",
    "containerDefinitions": [
        {
            "name": "loki-container",
            "image": "(Loki 이미지 ECR URL)",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "loki3100pms",
                    "containerPort": 3100,
                    "hostPort": 3100,
                    "protocol": "tcp",
                    "appProtocol": "http"
                },
                {
                    "name": "loki7946pm",
                    "containerPort": 7946,
                    "hostPort": 0,
                    "protocol": "tcp",
                    "appProtocol": "http"
                },
                {
                    "name": "loki9095pm",
                    "containerPort": 9095,
                    "hostPort": 0,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "S3_BUCKET_NAME",
                    "value": ""
                },
                {
                    "name": "LOKI_S3_SECRET_KEY",
                    "value": ""
                },
                {
                    "name": "LOKI_S3_ACCESS_KEY",
                    "value": ""
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "startTimeout": 30,
            "stopTimeout": 120,
            "user": "0",
            "privileged": true,
            "readonlyRootFilesystem": false,
            "interactive": false,
            "pseudoTerminal": false,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/aws/ecs/(서비스 이름)/loki-container",
                    "awslogs-region": "ap-northeast-2",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "family": "(태스크 정의 이름)",
    "taskRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "executionRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "networkMode": "bridge",
    "revision": (개정 번호),
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.ecr-auth"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "com.amazonaws.ecs.capability.privileged-container"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
        },
        {
            "name": "com.amazonaws.ecs.capability.task-iam-role"
        },
        {
            "name": "ecs.capability.container-ordering"
        },
        {
            "name": "ecs.capability.execution-role-ecr-pull"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2"
    ],
    "requiresCompatibilities": [
        "EC2"
    ],
    "cpu": "512",
    "memory": "512",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2023-12-05T06:08:42.423Z",
    "registeredBy": "arn:aws:iam::(aws 계정 ID):user/dory",
    "tags": []
}

5️⃣ API Server 에 FireLens, Fluentbit 으로 로그 수집하고, Grafana Loki 와 연결되어있는 태스크 정의 생성

  • API Application 컨테이너
  • fluentbit 컨테이너
  • Service Connect 컨테이너

&

  • Service Connect 를 사용하여 Loki 의 dns 로 내부 통신 (client)

Service Connect Client 예시 사진

더보기

태스크 정의 JSON 코드

{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-2:(aws 계정 ID):task-definition/(태스크 정의 이름):(개정 번호)",
    "containerDefinitions": [
        {
            "name": "backend-container",
            "image": "(API Server 이미지 ECR URL)",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "server8080pm",
                    "containerPort": 8080,
                    "hostPort": 0,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "ADMIN_JWT_KEY",
                    "value": ""
                },
                {
                    "name": "DB_USERNAME",
                    "value": ""
                },
                {
                    "name": "DB_URL",
                    "value": ""
                },
                {
                    "name": "REQUESTER_JWT_KEY",
                    "value": ""
                },
                {
                    "name": "DB_NAME",
                    "value": ""
                },
                {
                    "name": "WORKER_JWT_KEY",
                    "value": ""
                },
                {
                    "name": "DB_PASSWORD",
                    "value": ""
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "startTimeout": 30,
            "stopTimeout": 120,
            "user": "0",
            "privileged": true,
            "readonlyRootFilesystem": false,
            "interactive": false,
            "pseudoTerminal": false,
            "logConfiguration": {
                "logDriver": "awsfirelens",
                "options": {
                    "Name": "loki",
                    "host": "loki",
                    "label_keys": "$container_name, $ecs_task_definition, $source, $ecs_cluster, $log",
                    "labels": "job=backend",
                    "line_format": "key_value",
                    "port": "3100"
                }
            }
        },
        {
            "name": "fluentbit-container",
            "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:latest",
            "cpu": 0,
            "portMappings": [],
            "essential": true,
            "environment": [],
            "mountPoints": [],
            "volumesFrom": [],
            "startTimeout": 30,
            "stopTimeout": 120,
            "user": "0",
            "privileged": true,
            "readonlyRootFilesystem": false,
            "interactive": false,
            "pseudoTerminal": false,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-create-group": "true",
                    "awslogs-group": "/firelens-container/",
                    "awslogs-region": "ap-northeast-2",
                    "awslogs-stream-prefix": "firelens"
                }
            },
            "firelensConfiguration": {
                "type": "fluentbit",
                "options": {
                    "enable-ecs-log-metadata": "true"
                }
            }
        }
    ],
    "family": "(태스크 정의 이름)",
    "taskRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "executionRoleArn": "arn:aws:iam::(aws 계정 ID):role/(서비스 IAM role 이름)",
    "networkMode": "bridge",
    "revision": (개정 번호),
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.ecr-auth"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "ecs.capability.firelens.fluentbit"
        },
        {
            "name": "com.amazonaws.ecs.capability.privileged-container"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
        },
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens"
        },
        {
            "name": "com.amazonaws.ecs.capability.task-iam-role"
        },
        {
            "name": "ecs.capability.container-ordering"
        },
        {
            "name": "ecs.capability.execution-role-ecr-pull"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2"
    ],
    "requiresCompatibilities": [
        "EC2"
    ],
    "cpu": "1024",
    "memory": "512",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2023-12-06T11:50:25.738Z",
    "registeredBy": "arn:aws:sts::(aws 계정 ID):assumed-role/dev-gridge-cpl-iam-role/1701863425432",
    "tags": []
}

6️⃣ ECS 서비스 생성

각 태스크 정의를 통해 ECS Service 를 생성한다.

  • Grafana 서비스
  • Loki 서비스
  • API Server 서비스

이렇게 API Server 에서 쌓이는 로그를 수집하여 Loki, S3 버킷에 저장되고,

Loki 로 쿼리를 쏴서 로그를 Grafana 대시보드에서 볼 수 있다.

트러블 슈팅

더보기
  • ECS Task IAM role AWSLambdaVPCAccessExecutionRole
  • [error] [config] section 'grafana-loki' tried to instance a plugin name that don't exists
    • fluentbit의 이미지를 public.ecr.aws/aws-observability/aws-for-fluent-bit:latest로 변경해주니까 해결

레퍼런스

Loki tutorial: How to send logs from Amazon's ECS to Loki | Grafana Labs

 

Loki tutorial: How to send logs from Amazon's ECS to Loki | Grafana Labs

Thank you! Your message has been received!

grafana.com

Loki tutorial: How to set up Promtail on AWS EC2 to find and analyze your logs | Grafana Labs

 

Loki tutorial: How to set up Promtail on AWS EC2 to find and analyze your logs | Grafana Labs

Thank you! Your message has been received!

grafana.com

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함