Tracing (“трасування”) дозволяє відстежувати запити між компонентами, тобто, наприклад, при використанні AWS і у Kubernetes ми можемо прослідкувати весь шлях запиту від AWS Load Balancer – до Kubernetes Pod – і до DynamoDB або RDS.
Це допомагає нам як відстежувати проблеми з performance – де і які запити виконуються довго – так і мати більше інформації при виникненні проблем, наприклад, коли наш API віддає клієнтам 500 помилки, і нам треба знайти в якому саме компоненті системи виникає проблема.
В AWS для трейсінгу існує є сервіс X-Ray, куди ми можемо відправляти дані за допомогою AWS X-Ray SDK for Python або AWS Distro for OpenTelemetry Python (або інших мов, але тут будемо говорити про Python).
AWS X-Ray до кожного запиту додає унікальний X-Ray ID і дозволяє будувати картину повного “маршруту” запиту.
Окрім X-Ray, в Kubernetes ми можемо трейсити за допомогою таких рішень як Jaeger або Zipkin, і потім будувати картину в Grafana Tempo.
Інше рішення – використовувати X-Ray Daemon, який ми можемо запустити в Kubernetes, і додати в Grafana плагін X-Ray. Див. приклади в Introducing the AWS X-Ray integration with Grafana.
Крім того, AWS Distro for OpenTelemetry теж працює з Trace ID, сумісними з AWS X-Ray – див. AWS Distro for OpenTelemetry and AWS X-Ray та Collecting traces from EKS with ADOT.
Проте сьогодні ми будемо додавати саме X-Ray коллектор, який створить Kubernetes DaemonSet та Kubernetes Service, в який Kubernetes Pods зможуть слати дані, які ми потім зможемо побачити або в AWS Console X-Ray, або в Grafana.
Зміст
AWS IAM
IAM Policy
Для доступу в AWS з подів з X-Ray нам потрібно створити IAM Role, яку ми потім будемо використовувати в ServiceAccount для X-Ray.
Ми все ще користуємось старим варіантом додавання IAM Role через ServiceAccounts, див. Kubernetes: ServiceAccount з AWS IAM Role для Kubernetes Pod, хоча нещодавно AWS анонсували Amazon EKS Pod Identity Agent add-on – див. AWS: EKS Pod Identities – заміна IRSA? Спрощуємо менеджмент IAM доступів.
Отже, створюємо IAM Policy з дозволами для запису в X-Ray:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "xray:PutTraceSegments", "xray:PutTelemetryRecords" ], "Resource": [ "*" ] } ] }
Зберігаємо:
IAM Role
Далі додаємо IAM Role, яку зможе використовувати Kubernetes ServiceAccount.
Знаходимо Identity provider нашого EKS-кластеру:
Переходимо в IAM Role, додаємо нову роль.
В Trusted entity type вибираємо Web Identity, і в Web identity вибираємо Identity provider нашого EKS, в Audience – AWS STS:
Підключаємо створену вище політику:
Зберігаємо:
Запуск X-Ray Daemon в Kubernetes
Використаємо Helm-чарт okgolove/aws-xray.
Створюємо файл x-ray-values.yaml
– див. дефолтні значення у values.yaml
:
serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::492***148:role/XRayAccessRole-test xray: region: us-east-1 loglevel: prod
Додаємо репозиторій:
$ helm repo add okgolove https://okgolove.github.io/helm-charts/
Встановлюємо чарт в кластер, який створить DaemonSet і Service:
$ helm -n ops-monitoring-ns install aws-xray okgolove/aws-xray -f x-ray-values.yaml
Перевіряємо поди:
$ kk get pod -l app.kubernetes.io/name=aws-xray NAME READY STATUS RESTARTS AGE aws-xray-5n2kt 0/1 Pending 0 41s aws-xray-6cwwf 1/1 Running 0 41s aws-xray-7dk67 1/1 Running 0 41s aws-xray-cq7xc 1/1 Running 0 41s aws-xray-cs54v 1/1 Running 0 41s aws-xray-mjxlm 0/1 Pending 0 41s aws-xray-rzcsz 1/1 Running 0 41s aws-xray-x5kb4 1/1 Running 0 41s aws-xray-xm9fk 1/1 Running 0 41s
Та Kubernetes Service:
$ kk get svc -l app.kubernetes.io/name=aws-xray NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE aws-xray ClusterIP None <none> 2000/UDP,2000/TCP 77s
Перевірка та робота з X-Ray
Створення Python Flask HTTP App з X-Ray
Створимо сервіс на Python Flask, який буде відповідати на HTTP-запити і логувати X-ray ID (ChatGPT промт – “Create a simple Python App with AWS X-Ray SDK for Python to run in Kubernetes. Add X-Ray ID output to requests“):
from flask import Flask from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.flask.middleware import XRayMiddleware import logging app = Flask(__name__) # Configure AWS X-Ray xray_recorder.configure(service='SimpleApp') XRayMiddleware(app, xray_recorder) # Set up basic logging logging.basicConfig(level=logging.INFO) @app.route('/') def hello(): # Retrieve the current X-Ray segment segment = xray_recorder.current_segment() # Get the trace ID from the current segment trace_id = segment.trace_id if segment else 'No segment' # Log the trace ID logging.info(f"Responding to request with X-Ray trace ID: {trace_id}") return f"Hello, X-Ray! Trace ID: {trace_id}\n" if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)
Створюємо requirements.txt
:
flask==2.0.1 werkzeug==2.0.0 aws-xray-sdk==2.7.0
Додаємо Dockerfile:
FROM python:3.8-slim COPY requirements.txt . RUN pip install --force-reinstall -r requirements.txt COPY app.py . CMD ["python", "app.py"]
Збираємо Docker-образ – тут використовується репозиторій в AWS ECR:
$ docker build -t 492***148.dkr.ecr.us-east-1.amazonaws.com/x-ray-test .
Логінимось в ECR:
$ aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 492***148.dkr.ecr.us-east-1.amazonaws.com
Пушимо образ:
$ docker push 492***148.dkr.ecr.us-east-1.amazonaws.com/x-ray-test
Запуск Flask App в Kubernetes
Створюємо маніфест з Kubernetes Deployment, Service та Ingress.
Для Ingress включаємо логування в AWS S3 бакет – з нього логи будуть збиратись до Grafana Loki, див. Grafana Loki: збираємо логи AWS LoadBalancer з S3 за допомогою Promtail Lambda.
Для Deployment задаємо змінну оточення AWS_XRAY_DAEMON_ADDRESS
, в якій вказуємо Kubernetes Service нашого X-Ray Daemon:
apiVersion: apps/v1 kind: Deployment metadata: name: flask-app spec: replicas: 2 selector: matchLabels: app: flask-app template: metadata: labels: app: flask-app spec: containers: - name: flask-app image: 492***148.dkr.ecr.us-east-1.amazonaws.com/x-ray-test ports: - containerPort: 5000 env: - name: AWS_XRAY_DAEMON_ADDRESS value: "aws-xray.ops-monitoring-ns.svc.cluster.local:2000" - name: AWS_REGION value: "us-east-1" --- apiVersion: v1 kind: Service metadata: name: flask-app-service spec: selector: app: flask-app ports: - protocol: TCP port: 80 targetPort: 5000 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: flask-app-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=ops-1-28-devops-monitoring-ops-alb-logs spec: ingressClassName: alb rules: - http: paths: - path: / pathType: Prefix backend: service: name: flask-app-service port: number: 80
Деплоїмо, та перевіряємо Ingress/ALB:
$ kk get ingress NAME CLASS HOSTS ADDRESS PORTS AGE flask-app-ingress alb * k8s-default-flaskapp-25042181e0-298318111.us-east-1.elb.amazonaws.com 80 10m
Робимо запит до ендпоінту:
$ curl k8s-default-flaskapp-25042181e0-298318111.us-east-1.elb.amazonaws.com Hello, X-Ray! Trace ID: 1-65e1d287-5fc6f0f34b4fb2120da8bbec
І бачимо X-Ray ID. Чудово.
Його ж бачимо в логах Load Balancer:
І в самому X-Ray:
Правда, я все ж очікував, що Load Balancer теж буде в мапі запиту – але ні.
Grafana X-Ray data source
Додаємо новий Data source:
Налаштовуємо доступ до AWS – тут просто з ACCESS та SECRET ключами (див. документацію X-Ray):
І тепер маємо новий data source в Explore:
Та новий тип візуалізації – Traces:
І десь вже окремим постом мабуть опишу побудову реальної дашборди з використанням X-Ray.