티스토리 뷰
Grafana, Loki를 ECS 환경에서 사용하여 모니터링, 로깅, 경보 시스템 구축하기 - (2) Grafana, Loki, API Server 를 ECS에 띄우기
김도리개발자 2023. 12. 8. 17:05프로세스
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 클러스터 생성
3️⃣ Grafana 태스크 정의 생성
- Service Connect 를 사용하여 Loki 의 dns 로 내부 통신 (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)
태스크 정의 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)
태스크 정의 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 서비스 생성
- Grafana 서비스
- Loki 서비스
- API Server 서비스
이렇게 API Server 에서 쌓이는 로그를 수집하여 Loki, S3 버킷에 저장되고,
Loki 로 쿼리를 쏴서 로그를 Grafana 대시보드에서 볼 수 있다.
트러블 슈팅
- ECS Task IAM role AWSLambdaVPCAccessExecutionRole
- cloudwatch:createLogGroup
- aws logs: The specified log group does not exist
- [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로 변경해주니까 해결
![](https://blog.kakaocdn.net/dn/N1e71/btsBDRzmPT7/fQLEUKvKUqPxCRH5S3be8K/img.png)
![](https://blog.kakaocdn.net/dn/ondFD/btsBDRzl6vv/LHaLSpVkkDYJw2dkNl0og0/img.png)
레퍼런스
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
- Grafana
- Hook
- Service
- AWS
- subnet
- redux
- ci/cd
- github action
- ALB
- VPC
- Docker
- 로깅
- Grafana Alert
- html
- 모니터링
- javascript
- ecr
- js
- Workflow
- 서버
- 리액트
- LOKI
- SG
- 인프라
- RDS
- ECS
- springboot
- react
- CSS
- EC2
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |