Kubernetes: tracing запитів з AWS X-Ray та Grafana data source

Автор |  02/03/2024
 

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.