Зараз ми вміємо збирати логи API Gateway та CloudWatch Logs, див. Loki: збір логів з CloudWatch Logs з використанням Lambda Promtail.
Але в процесі міграції в Kubernetes у нас з’явились Application Load Balancers, які вміють писати логи тільки в S3, і нам треба навчитись збирати логи і звідти.
Формат логу AWS ALB див. у Access logs for your Application Load Balancer, налаштування логування в S3 – в Enable access logs for your Application Load Balancer.
Зміст
Технічна реалізація
Начебто і нічого складного, але по-перше є деякі нюанси, особливо з IAM та VPC, по-друге – я ніде не знаходив такої документації, тож довелося писати її самому.
В принципі, тут все майже однаково зі збором логів з CloudWatch Logs:
- використовуємо Promtail Lambda
- на S3 бакеті налаштовуємо тригер на відправку івента в Lambda, коли з’явлється або оновлюється об’єкт в корзині
- Promtail сходить в корзину, і забере звідти лог
Схематично можна відобразити так:
Тепер давайте все зробимо руками, а потім вже будемо думати як його прикрутити до автоматизації з Terraform.
Створення S3 для логів
Для S3 бакету нам потрібна політика, яка буде дозволяти:
- писати в корзину логи з Application Load Balancer
- забирати з корзини логи в Lambda
Для цього нам будуть потрібні:
- ELB Account ID для Load Balancer – див. Step 2: Attach a policy to your S3 bucket
- в нашому випадку AWS Region == us-east-1, тож
elb-account-idбуде 127311923021
- в нашому випадку AWS Region == us-east-1, тож
- IAM Role ARN – ролі, яка підключена до нашої Lambda-фунції з Promtail
Створюємо файл s3-alb-logs-policy.json з двома Allow:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::127311923021:root"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::eks-alb-logs-test/AWSLogs/492***148/*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::492***148:role/grafana-atlas-monitoring-dev-1-28-loki-logger-backend"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::eks-alb-logs-test/*"
}
]
}
Створюємо корзину:
$ aws --profile work s3api create-bucket --bucket eks-alb-logs-test --region us-east-1
І підключаємо політику:
$ aws --profile work s3api put-bucket-policy --bucket eks-alb-logs-test --policy file://s3-alb-logs-policy.json
Далі треба налаштувати відправку повідомлень до нашої Lambda.
S3 Event notifications
Переходимо в Properties > Event notifications:
Задаємо ім’я та префікс – каталог AWSLogs і в ньому каталог з ім’ям аккаунту:
Нижче вибираємо типи подій – всі ObjectCreated:
І задаємо Destination – ARN функції, до якої будемо слати повідомлення:
Promtail Lambda permissions
Перевіримо дозволи в Lambda. Це знадобиться, коли будемо робити автоматизацію, та й для дебагу.
Переходимо в Configuration > Permissions:
І внизу, в Resource-based policy statements, маємо побачити нові дозволи – додалось автоматом, коли ми створили Event notification в S3:
Створення Ingress/ALB з логуванням в S3
Створюємо Kubernetes Ingress, і в annotations alb.ingress.kubernetes.io/load-balancer-attributes включаємо йому логуваня в тестову корзину:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx-demo-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo-service
spec:
selector:
app: nginx-demo
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
alb.ingress.kubernetes.io/load-balancer-attributes: access_logs.s3.enabled=true,access_logs.s3.bucket=eks-alb-logs-test
spec:
ingressClassName: alb
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo-service
port:
number: 80
Створюємо ресурси:
$ kk apply -f ingress-svc-deploy.yaml deployment.apps/nginx-demo-deployment created service/nginx-demo-service created ingress.networking.k8s.io/example-ingress created
Перевіряємо:
$ kk get ingress NAME CLASS HOSTS ADDRESS PORTS AGE example-ingress alb example.com k8s-default-examplei-b48cab7a95-1519459738.us-east-1.elb.amazonaws.com 80 22s
Перевіряємо файли в корзині:
$ aws s3 ls s3://eks-alb-logs-test/AWSLogs/492***148/ 2023-11-15 14:51:43 106 ELBAccessLogTestFile
Робимо кілька запитів до АЛБ:
$ curl k8s-default-examplei-b48cab7a95-1519459738.us-east-1.elb.amazonaws.com
І за 2-3 хвилини – маємо новий файл в корзині:
$ aws s3 ls s3://eks-alb-logs-test/AWSLogs/492***148/elasticloadbalancing/us-east-1/2023/11/15/ ... 2023-11-15 16:15:11 344 492***148_elasticloadbalancing_us-east-1_app.k8s-default-examplei-b48cab7a95.bc6b35b432aa3492_20231115T1415Z_54.211.225.139_2ir4tbe3.log.gz
І ще за хвилину маємо івент в Lambda:
Та її логи – можна пошукати по імені файла, bc6b35b432aa3492_20231115T1415Z_54.211.225.139_2ir4tbe3:
І логи в Loki – тут можна пошукати по X-Ray ID, який додається до всіх запитів до ALB в хедері X-Amzn-Trace-Id:
Помилка Lambda: Task timed out, та VPC Endpoints
Поки налаштовува це, зіткнувся з помилкою “Task timed out“.
В логах це виглядало так:
msg=”fetching s3 file: AWSLogs/492***148/elasticloadbalancing/us-east-1/2023/11/15/492***148_elasticloadbalancing_us-east-1_app.k8s-default-examplei-b48cab7a95.21d12877724a6c9f_20231115T1205Z_52.44.168.196_jin2v33x.log.gz”
Task timed out after 3.00 seconds
Спочатку подумав, що Promtail за 3 секунди не встигає отримати лог з S3, і збільшив таймаут функції:
Але виявилось, що причина в іншому: Lambda Promtail запущена в VPC, і до S3 ходить через VPC Ednpoint (див. Terraform: створення EKS, частина 1 – VPC, Subnets та Endpoints), але в SecurityGtoup, яка підключена до цієї функції, Outbound доступ був дозволений тільки до адрес в приватних сабнетах цієї VPC, бо тоді налаштовувалась тільки передача логів в Grafana Loki:
А нам потрібно надати доступ до VPC енпоінту S3, і якби це був тип Interface Ednpoint – то в SecurityGroup лямбди можна було б задати SecurityGroup ID цього Interface Endpoint-у:
Але у Gateway такої опції нема.
Проте ми можемо використати Prefix List ID – знаходимо його:
І додаємо в Outbound Rules:
Готово.














