Skip to content

Repository files navigation

Kube-Trace

A distributed tracing setup for microservices. You run three small services that call each other, capture latency and errors with OpenTelemetry, store everything in PostgreSQL, and browse it through a React dashboard. Works locally with Docker Compose or on a Kubernetes cluster with the manifests in k8s/.

Built as a portfolio and learning project. Not production hardened, but the full path from instrumented service to UI is there.


What you get

Three demo services simulate a simple order flow. When you hit the order endpoint, the request chains through payment and inventory. Each hop creates spans with timing and status. The API rolls that into latency percentiles, error counts, and a basic service graph. The UI shows traces as a waterfall, lists failed spans, and lets you filter by service.


Architecture

System overview

flowchart LR
    subgraph clients [Clients]
        User[Browser / curl]
    end

    subgraph services [Demo Microservices]
        Order[order-service\n:3001]
        Payment[payment-service\n:3002]
        Inventory[inventory-service\n:3003]
    end

    subgraph platform [Observability]
        Collector[OTel Collector\n:4317]
        API[trace-api\n:4000]
        DB[(PostgreSQL\n:5432)]
        UI[trace-ui\n:5173]
    end

    User -->|POST /orders| Order
    Order -->|POST /process| Payment
    Payment -->|POST /check| Inventory

    Order -->|spans| API
    Payment -->|spans| API
    Inventory -->|spans| API

    Order -.->|K8s mode| Collector
    Payment -.->|K8s mode| Collector
    Inventory -.->|K8s mode| Collector
    Collector -.->|OTLP JSON| API

    API --> DB
    User -->|HTTP| UI
    UI -->|REST| API
Loading

Locally, demo services send spans straight to trace-api over HTTP. On Kubernetes they go through the OpenTelemetry Collector first, which batches and forwards to the API.

Request and trace flow

sequenceDiagram
    participant Client
    participant Order as order-service
    participant Payment as payment-service
    participant Inventory as inventory-service
    participant API as trace-api
    participant DB as PostgreSQL
    participant UI as trace-ui

    Client->>Order: POST /orders
    Order->>Payment: POST /process
    Payment->>Inventory: POST /check
    Inventory-->>Payment: stock OK
    Payment-->>Order: payment OK
    Order-->>Client: order confirmed

    Note over Order,Inventory: Each service exports spans as the request moves

    Order->>API: span batch
    Payment->>API: span batch
    Inventory->>API: span batch
    API->>DB: store spans

    UI->>API: GET /v1/traces
    API->>DB: query
    DB-->>API: results
    API-->>UI: trace list + metrics
Loading

Kubernetes layout

Everything runs in the kube-trace namespace.

flowchart TB
    subgraph ns [Namespace kube-trace]
        Ingress[Ingress\nkube-trace.local]

        subgraph apps [Applications]
            UI_Pod[trace-ui]
            API_Pod[trace-api]
            Order_Pod[order-service]
            Pay_Pod[payment-service]
            Inv_Pod[inventory-service]
        end

        subgraph infra [Infrastructure]
            PG[(postgres)]
            OTel[otel-collector]
        end

        Ingress --> UI_Pod
        Ingress --> API_Pod

        Order_Pod --> Pay_Pod
        Pay_Pod --> Inv_Pod

        Order_Pod --> OTel
        Pay_Pod --> OTel
        Inv_Pod --> OTel
        OTel --> API_Pod

        API_Pod --> PG
        UI_Pod --> API_Pod
    end
Loading

Tech stack

Layer Tools
Backend API TypeScript, Fastify, PostgreSQL
Frontend React, Vite, TanStack Query, Recharts, Tailwind
Tracing OpenTelemetry SDK, OTel Collector
Containers Docker, multi-stage Dockerfiles
Orchestration Kubernetes manifests (plain YAML, no Helm)

Repository layout

KubePro/
├── apps/
│   ├── trace-api/           Ingestion and query API
│   ├── trace-ui/            React dashboard
│   └── demo-services/       order, payment, inventory
├── packages/
│   ├── shared/              Shared TypeScript types
│   └── otel-config/         OTel setup and trace-api exporter
├── k8s/                     Kubernetes manifests
├── scripts/                 Deploy, seed traffic, integration test
├── docker-compose.yml       Postgres + optional collector stack
└── otel-collector-config.yaml

Running it locally

You need Node 20+, Docker, and npm.

Install and build shared packages

npm install
npm run build -w @kube-trace/shared
npm run build -w @kube-trace/otel-config

Start Postgres

docker compose up -d postgres

Start the API (keep this terminal open)

npm run dev:api

Start the three demo services (one terminal each)

npm run dev:inventory
npm run dev:payment
npm run dev:order

Start the UI

npm run dev:ui

Open http://localhost:5173.

Generate some traffic

PowerShell:

.\scripts\seed-traffic.ps1 -Count 20

Bash:

./scripts/seed-traffic.sh 20

Or send a single order by hand:

curl -X POST http://localhost:3001/orders \
  -H "Content-Type: application/json" \
  -d '{"item":"book","amount":29.99}'

Run the integration test

npm run test:integration

The OTel Collector is optional for local dev. Services talk to trace-api directly. If you want the collector in the loop:

docker compose up -d otel-collector

Then set OTEL_USE_COLLECTOR=true on the demo services before starting them.


Deploying to Kubernetes

You need kubectl, Docker, and ideally kind for a local cluster.

kind create cluster --name kube-trace
./scripts/deploy-local.sh

Port-forward the UI and order service:

kubectl -n kube-trace port-forward svc/trace-ui 8080:80
kubectl -n kube-trace port-forward svc/order-service 3001:3001

UI at http://localhost:8080. Send orders to port 3001 the same way as local dev.

The deploy script builds images, loads them into kind if available, and applies everything under k8s/.


API reference

Method Path What it does
POST /v1/spans Ingest a batch of spans as JSON
POST /v1/traces Ingest OTLP JSON from the collector
GET /v1/traces Search traces by service, status, time range
GET /v1/traces/:traceId Full trace with all spans
GET /v1/services Per-service latency (p50, p95, p99) and error rate
GET /v1/services/:name/errors Errors for one service
GET /v1/errors Recent errors across all services
GET /v1/dependencies Service-to-service call counts
GET /health Liveness check
GET /ready Readiness check (includes DB)

Dashboard

The UI has five sections.

Services shows request volume, error rate, and p50/p95/p99 latency per service with a bar chart comparison.

Traces is a searchable list. Filter by service or errors only, then click a trace ID to open the detail view.

Trace detail renders a waterfall timeline. Each row is a span with service name, operation, duration bar, and status.

Errors lists failed spans with the error message and a link back to the full trace.

Service graph draws the order → payment → inventory chain with call counts pulled from parent-child span relationships.


How tracing is wired

Each demo service initializes OpenTelemetry on boot with auto-instrumentation for HTTP and Fastify. Manual spans wrap the business logic (create-order, process-payment, check-inventory). Random delays and a small error rate keep the demo data interesting.

For local development, spans export as JSON to trace-api through a custom exporter in packages/otel-config. In Kubernetes, OTEL_USE_COLLECTOR=true sends spans to the collector on gRPC port 4317, which forwards OTLP JSON to the API.

The API writes spans into a single spans table. Queries aggregate latency percentiles and build the dependency graph from parent-child links between spans in the same trace.

Each service records its own spans. Cross-service correlation on a single trace ID depends on W3C trace context propagation through the HTTP calls. The demo covers the basics; tighter end-to-end waterfall stitching across all three services is an area you can extend.


Scripts

Script Purpose
scripts/seed-traffic.sh Fire random orders at the order service (Bash)
scripts/seed-traffic.ps1 Same thing for PowerShell
scripts/deploy-local.sh Build images and apply K8s manifests
scripts/integration-test.mjs POST an order and verify spans land in the API

License

MIT

About

Trace request latency and errors across microservices running on kubernetes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages