<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Simulator on Joulie</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/</link><description>Recent content in Simulator on Joulie</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/index.xml" rel="self" type="application/rss+xml"/><item><title>Simulated Cluster</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulated-cluster/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulated-cluster/</guid><description>&lt;p&gt;The simulated cluster runs the real Joulie components on emulated bare-metal nodes, on one machine with Docker. The agent discovers and caps emulated hardware through the same files and tools it uses on a real node, the controller manager reads node power from the simulator&amp;rsquo;s OpenTelemetry metrics in Prometheus, and the scheduler extender scores real pods. The scripts in &lt;code&gt;scripts/simcluster&lt;/code&gt; drive it, and &lt;code&gt;tests/simcluster&lt;/code&gt; checks it end to end.&lt;/p&gt;
&lt;p&gt;Use it to see a change to the agent, the controller manager, the extender, the chart or the simulator work across components. A change to discovery or capping alone is checked faster by the hwemu tests in &lt;code&gt;cmd/agent&lt;/code&gt; (&lt;code&gt;go test ./cmd/agent/ -run Hwemu&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Workload Scenarios</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/scenarios/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/scenarios/</guid><description>&lt;p&gt;A workload scenario runs a workload through the &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulated-cluster/"&gt;simulated cluster&lt;/a&gt; and checks what happened: whether the jobs completed, where they were placed, how long they took, what energy they used, and how Joulie&amp;rsquo;s settings compare with each other or with no Joulie at all. The jobs are real Kubernetes Jobs, scheduled by the real scheduler and the Joulie extender onto KWOK Nodes, and the simulator&amp;rsquo;s v1 workload engine drives them on the emulated hardware, so a cap the agent writes slows the jobs on that Node.&lt;/p&gt;</description></item><item><title>Installation</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/installation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/installation/</guid><description>&lt;p&gt;This page installs the simulator into a Kubernetes cluster you already have, or runs it from a workstation. To try Joulie on emulated nodes with nothing but Docker, use the &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulated-cluster/"&gt;simulated cluster&lt;/a&gt; instead: it creates the cluster and installs everything.&lt;/p&gt;
&lt;p&gt;Every command below uses the namespace &lt;code&gt;joulie-sim-demo&lt;/code&gt; and the release &lt;code&gt;joulie-simulator&lt;/code&gt;, the defaults of &lt;code&gt;make simulator-install&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A Kubernetes cluster (real, or &lt;a href="https://kind.sigs.k8s.io/"&gt;kind&lt;/a&gt; for local work), with &lt;code&gt;kubectl&lt;/code&gt; pointed at it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;helm&lt;/code&gt; v3 or later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="install-with-helm"&gt;Install with Helm&lt;/h2&gt;
&lt;p&gt;The simulator chart is published next to the Joulie chart:&lt;/p&gt;</description></item><item><title>Emulate Mode</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/emulate-mode/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/emulate-mode/</guid><description>&lt;p&gt;Emulate mode emulates the bare-metal nodes of a &lt;a href="#the-cluster-spec"&gt;cluster spec&lt;/a&gt; at the boundary the agent sees: the files it reads and writes, and the GPU tools it runs. An unmodified agent then runs its real discovery and capping code against each node, while Kubernetes scheduling stays real. The &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulated-cluster/"&gt;simulated cluster&lt;/a&gt; runs it; to run it yourself, see &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/installation/#emulate-mode-in-a-cluster"&gt;Installation&lt;/a&gt;. Nodes the spec does not list keep the label-based model of &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulator/"&gt;KWOK and standalone modes&lt;/a&gt;, in the same simulator.&lt;/p&gt;</description></item><item><title>KWOK and Standalone Modes</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulator/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/simulator/</guid><description>&lt;p&gt;Outside &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/emulate-mode/"&gt;emulate mode&lt;/a&gt; the simulator models each node from its labels: it keeps per-node power, cap and thermal state in memory and serves it over HTTP, with no files and no GPU tools. It runs in one of two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KWOK mode&lt;/strong&gt;: in a Kubernetes cluster whose API server and scheduler are real and whose worker nodes are &lt;a href="https://kwok.sigs.k8s.io/"&gt;KWOK&lt;/a&gt; fake nodes. The real controller manager plans, the real scheduler extender scores, and the agent in &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/getting-started/daemonset/"&gt;pool mode&lt;/a&gt; reads telemetry from and sends caps to the simulator&amp;rsquo;s &lt;a href="#http-api"&gt;HTTP API&lt;/a&gt;. The benchmark experiments run this way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standalone mode&lt;/strong&gt; (&lt;code&gt;SIM_STANDALONE=true&lt;/code&gt;): no Kubernetes at all. The simulator places the jobs of a trace and runs the policy baselines itself, in simulated time, as fast as the CPU allows.&lt;/li&gt;
&lt;/ul&gt;
&lt;img src='https://joulie-k8s.github.io/Joulie/versions/v0.2.4/images/joulie-arch-simulator.png
' alt="Joulie simulator architecture overview"&gt;
&lt;h2 id="how-kwok-mode-works"&gt;How KWOK mode works&lt;/h2&gt;
&lt;p&gt;Pod placement and pod lifetime stay real in Kubernetes, the one source of truth for where work runs. The simulator only derives each node&amp;rsquo;s hardware state from what runs there:&lt;/p&gt;</description></item><item><title>Workload Generation</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-generation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-generation/</guid><description>&lt;p&gt;&lt;code&gt;workloadgen&lt;/code&gt; (&lt;code&gt;simulator/cmd/workloadgen&lt;/code&gt;) writes synthetic traces of AI cluster workloads for the simulator. This page says what a trace contains and how the generator samples it; &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-simulator/"&gt;Workload Simulator&lt;/a&gt; says how the simulator runs it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go run ./simulator/cmd/workloadgen --out trace.jsonl --jobs &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; --seed &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The generator models AI-oriented Kubernetes clusters: CPU and GPU jobs, memory-bound jobs, and workloads made of several pods, such as distributed training and hyperparameter searches. It does not model network bandwidth, disk throughput or framework communication protocols: their cost is folded into the utilisation and bottleneck signals of each workload (&lt;code&gt;gpuUtilization&lt;/code&gt;, &lt;code&gt;memoryIntensity&lt;/code&gt;, &lt;code&gt;cpuFeedIntensityGpu&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Workload Simulator</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-simulator/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-simulator/</guid><description>&lt;p&gt;This page says how the simulator runs a workload trace: which Kubernetes objects it creates, how each job progresses under caps, and how jobs load the hardware model. &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-generation/"&gt;Workload Generation&lt;/a&gt; describes the traces; &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/power-simulator/"&gt;Power Simulator&lt;/a&gt; the power side of a node.&lt;/p&gt;
&lt;h2 id="two-engines-legacy-and-v1"&gt;Two engines: legacy and v1&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SIM_WORKLOAD_MODEL&lt;/code&gt; selects the engine that runs a trace.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;legacy (outside emulate mode)&lt;/th&gt;
 &lt;th&gt;v1 (emulate mode)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Started by&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;SIM_WORKLOAD_TRACE_PATH&lt;/code&gt;, replayed from startup&lt;/td&gt;
 &lt;td&gt;the run API, or &lt;code&gt;SIM_WORKLOAD_TRACE_PATH&lt;/code&gt; as an implicit run&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Kubernetes objects&lt;/td&gt;
 &lt;td&gt;one bare pod per job&lt;/td&gt;
 &lt;td&gt;one &lt;code&gt;batch/v1&lt;/code&gt; Job per group (&lt;code&gt;workloadId&lt;/code&gt;, &lt;code&gt;podRole&lt;/code&gt;, submit offset), Indexed when larger than one, &lt;code&gt;backoffLimit: 0&lt;/code&gt;, deleted &lt;code&gt;SIM_WORKLOAD_JOB_TTL&lt;/code&gt; after it finishes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Class&lt;/td&gt;
 &lt;td&gt;inferred from the pod&amp;rsquo;s affinity&lt;/td&gt;
 &lt;td&gt;declared by the annotation &lt;code&gt;joulie.io/workload-class&lt;/code&gt; (&lt;code&gt;SIM_WORKLOAD_INTENT&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Placement&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;type=kwok&lt;/code&gt; and the KWOK toleration&lt;/td&gt;
 &lt;td&gt;the trace&amp;rsquo;s placement, plus the selector &lt;code&gt;sim.joulie.io/cluster=&amp;lt;spec&amp;gt;&lt;/code&gt; and the KWOK toleration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Completion&lt;/td&gt;
 &lt;td&gt;the pod is deleted&lt;/td&gt;
 &lt;td&gt;the pod is annotated &lt;code&gt;sim.joulie.io/complete: &amp;quot;true&amp;quot;&lt;/code&gt; and KWOK&amp;rsquo;s &lt;code&gt;pod-complete&lt;/code&gt; Stage moves it to &lt;code&gt;Succeeded&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A job that cannot progress&lt;/td&gt;
 &lt;td&gt;completed by force after &lt;code&gt;SIM_WORKLOAD_STALL_FORCE_COMPLETE&lt;/code&gt; (120 s)&lt;/td&gt;
 &lt;td&gt;reported as stalled (&lt;code&gt;joulie.sim.job.stalled&lt;/code&gt;, &lt;code&gt;joulie.sim.run.stalled&lt;/code&gt;), never completed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Creates per tick&lt;/td&gt;
 &lt;td&gt;20&lt;/td&gt;
 &lt;td&gt;no cap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Speed&lt;/td&gt;
 &lt;td&gt;the node model&amp;rsquo;s frequency scale and GPU cap ratio&lt;/td&gt;
 &lt;td&gt;the throughput of the emulated hardware the job runs on, under the limits the agent wrote&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Energy&lt;/td&gt;
 &lt;td&gt;node and cluster totals at &lt;code&gt;/debug/energy&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;each node&amp;rsquo;s energy ledger delta, attributed per job and class by the jobs&amp;rsquo; requests; the unrequested share of idle package power is class &lt;code&gt;idle&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Results&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;joulie_sim_job_*&lt;/code&gt; counters at &lt;code&gt;/metrics&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;OTLP JSON Lines in the run directory, plus the run API&amp;rsquo;s status&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In a v1 run a job is submitted at the first tick after its create call returns, scheduled at the first tick that sees its pod bound, and done at the tick its work runs out, so its times are exact to one &lt;code&gt;SIM_EMULATE_TICK&lt;/code&gt;. A finish with cleanup deletes the run&amp;rsquo;s Job objects or bare pods by the run&amp;rsquo;s label, one call per namespace (&lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/getting-started/05-configuration-reference/#simulator-workloads-and-scenarios"&gt;Run API&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>Power Simulator</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/power-simulator/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/power-simulator/</guid><description>&lt;p&gt;This page says how the simulator advances a modelled node (any node outside the emulate-mode cluster spec): its control state, its physics step, and the telemetry it serves. The physical model itself, its parameters and their sources, is in &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/hardware/hardware-modeling/"&gt;Hardware Modeling&lt;/a&gt;; how jobs progress on a node is in &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/workload-simulator/"&gt;Workload Simulator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The power simulator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;keeps per-node control state (CPU cap, DVFS throttle, GPU cap),&lt;/li&gt;
&lt;li&gt;applies control actions from &lt;code&gt;/control/{node}&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;advances caps, frequency, power and temperature with their settling lags,&lt;/li&gt;
&lt;li&gt;serves power telemetry on &lt;code&gt;/otlp/telemetry/{node}&lt;/code&gt; and &lt;code&gt;/telemetry/{node}&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;integrates energy over time (&lt;code&gt;/debug/energy&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-physics-step"&gt;The physics step&lt;/h2&gt;
&lt;p&gt;A modelled node advances only in a physics step, in simulated time, never when it is read:&lt;/p&gt;</description></item><item><title>Hardware Calibration</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/calibration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/calibration/</guid><description>&lt;p&gt;&lt;code&gt;hwcalib fit&lt;/code&gt; (&lt;code&gt;simulator/cmd/hwcalib&lt;/code&gt;, package &lt;code&gt;simulator/pkg/calib&lt;/code&gt;) fits the physics of a hardware profile to a sweep measured on one of your nodes, and writes the result as a local profile the simulator loads from &lt;code&gt;SIM_PROFILES_DIR&lt;/code&gt;. The builtin profiles keep vendor figures and placeholders; a calibrated profile replaces them with what your machine draws. &lt;code&gt;hwcalib sweep&lt;/code&gt; records such a sweep on the node itself (see &lt;a href="#recording-on-the-node-with-hwcalib-sweep"&gt;Recording on the node&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="recording-a-sweep"&gt;Recording a sweep&lt;/h2&gt;
&lt;p&gt;A sweep holds the node at a series of setpoints and records, at every sample, the node&amp;rsquo;s readings and the setpoints it held. Each setpoint should last long enough to settle: the fit drops the start of every setpoint (see &lt;a href="#segments-and-steady-windows"&gt;Segments and steady windows&lt;/a&gt;) and needs at least 5 samples after that, so with a 2 s sample period a setpoint of 22 s or more is enough.&lt;/p&gt;</description></item><item><title>Simulator Metrics</title><link>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/metrics/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/metrics/</guid><description>&lt;p&gt;The simulator publishes three kinds of telemetry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prometheus metrics about itself&lt;/strong&gt; at &lt;code&gt;/metrics&lt;/code&gt; (&lt;code&gt;joulie_sim_*&lt;/code&gt;), for Prometheus to scrape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenTelemetry metrics and events about the emulated nodes and the facility&lt;/strong&gt;, in &lt;a href="https://joulie-k8s.github.io/Joulie/versions/v0.2.4/docs/simulator/emulate-mode/"&gt;emulate mode&lt;/a&gt; only. Metrics are pushed as OTLP JSON over HTTP; events are written to &lt;code&gt;events.jsonl&lt;/code&gt; and pushed when a logs endpoint is set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compatibility exporters&lt;/strong&gt;, in emulate mode only: the Prometheus formats of real exporters (Kepler, dcgm-exporter, the ROCm device-metrics-exporter) at &lt;code&gt;/exporters/&amp;lt;name&amp;gt;/metrics&lt;/code&gt;, opt-in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The OpenTelemetry names are spelled once, in &lt;code&gt;pkg/otel/semconv&lt;/code&gt;, and the controller manager reads the same names back from Prometheus. Their Prometheus spelling is computed by &lt;code&gt;pkg/otel/promname&lt;/code&gt;.&lt;/p&gt;</description></item></channel></rss>