Зараз ми вміємо збирати логи 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:
Готово.