Set up the ico host
The ico host runs two containers: ClickHouse, which stores everything, and the collector, which receives telemetry from the GPU machines and writes it to ClickHouse. It needs Docker with Compose and about ten minutes.
Get the code
git clone --branch v0.1.1 https://github.com/Gudjon01/inference-cost-optimizer.git
cd inference-cost-optimizer/deploy
Set the secrets
cp .env.example .env
In .env, set ICO_ADMIN_TOKEN and ICO_CLICKHOUSE_PASSWORD to long random strings, such as the output of openssl rand -hex 24. Keep the admin token: every gateway needs the same one for ico replay to drain replicas. The ClickHouse password is the password of the ico user.
Write the config
cp config.example.yaml config.yaml
The collector reads four things from it: your replicas, and how it reaches each engine's /metrics; your GPU exporters; what your GPUs cost; and where ClickHouse is. For two GPU machines on a private network, with one vLLM replica each:
replicas:
- name: gpu-1-r1 # unique; the gateway and vLLM's traces use the same name
url: http://10.0.0.11:8000 # how the ico host reaches this engine
host: gpu-1 # matches the exporter's host below
gpu_model: H100-SXM # matches a key of gpu_price_per_hour
gpu_count: 1 # GPUs this replica uses (tensor parallel size)
- name: gpu-2-r1
url: http://10.0.0.12:8000
host: gpu-2
gpu_model: H100-SXM
gpu_count: 1
# USD per GPU-hour, what you pay. Idle hours are billed too.
gpu_price_per_hour:
H100-SXM: 2.99
# For the replay's memory check: what each served model weighs.
models:
Qwen/Qwen3-8B:
params: 8_190_735_360
weight_bytes_per_param: 2 # BF16
kv_bytes_per_token: 147_456 # 2 x layers(36) x kv_heads(8) x head_dim(128) x 2 bytes
collector:
grpc_listen: 0.0.0.0:4317
scrape_interval_s: 5
gpu_exporters:
- kind: dcgm # nvidia_smi for nvidia_gpu_exporter on GeForce cards
url: http://10.0.0.11:9400/metrics
host: gpu-1
- kind: dcgm
url: http://10.0.0.12:9400/metrics
host: gpu-2
clickhouse:
host: clickhouse # the ClickHouse container, inside Compose
port: 8123
username: ico
database: ico
Every key is described in the configuration reference.
Let the GPU machines reach the collector
Compose publishes the collector (4317) and ClickHouse (8123) on 127.0.0.1 only. Publish them on the ico host's private address instead, in an override file Compose reads next to its own:
services:
collector:
ports: !override
- "10.0.0.5:4317:4317" # the ico host's private address
clickhouse:
ports: !override
- "10.0.0.5:8123:8123" # for ico replay, which runs on the GPU machines
ico user's password.Start ClickHouse and the collector
docker compose up -d clickhouse collector
Compose pulls the collector's image, ghcr.io/gudjon01/ico-collector, at the release you cloned; --build builds it from the checkout instead. The collector applies ClickHouse's schema on its first start. Until the GPU machines are set up, its log says that every scrape failed; that is expected:
docker compose logs -f collector