Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
2d09e03
k8s pressure merge to main test branch (#1050)
pdamianov-dev Feb 9, 2026
0414c6c
change to only use memory and pid
pdamianov-dev Feb 10, 2026
7e6a497
remove params, use vars for pass in
pdamianov-dev Feb 11, 2026
7065e7f
return to params
pdamianov-dev Feb 12, 2026
da585ed
Update k8s-resource-pressure.yml for Azure Pipelines
pdamianov-dev Feb 12, 2026
1381d2b
increase memory usage over allocatable to kill kubelet
pdamianov-dev Feb 13, 2026
83d1188
testing new sed feature for os sku
pdamianov-dev Feb 17, 2026
6875f97
uncomment competitive test
pdamianov-dev Feb 17, 2026
917fb6d
using metadata in params for run id
pdamianov-dev Feb 17, 2026
0ddf4e1
Update k8s-resource-pressure.yml for Azure Pipelines
pdamianov-dev Feb 17, 2026
e2e7bb1
reduce memory scale factor
pdamianov-dev Feb 18, 2026
93902cc
add more metrics scraping
pdamianov-dev Feb 19, 2026
2072820
revert to 1.0 to ensure requests match allocatable
pdamianov-dev Feb 19, 2026
1024c49
hold node for cl2 report dir
pdamianov-dev Feb 19, 2026
91b77fe
publish config and results for better checking of output
pdamianov-dev Feb 20, 2026
28b84b6
add node measurements to cl2 config
pdamianov-dev Feb 20, 2026
1646cc3
add node measure file
pdamianov-dev Feb 20, 2026
a2b3eeb
finalize suppoort for resource usage, add node_exporter for promethue…
pdamianov-dev Feb 23, 2026
cd843f3
telling prometheus to scrape node-exporter
pdamianov-dev Feb 23, 2026
c1cfb23
adding tolerations for helm chart
pdamianov-dev Feb 23, 2026
df24803
remove count on kube_node_status metric
pdamianov-dev Feb 23, 2026
5a69d71
fix helm nodeselector issue
pdamianov-dev Feb 23, 2026
760364d
fix issues with helm parsing
pdamianov-dev Feb 23, 2026
133553c
use set-string
pdamianov-dev Feb 23, 2026
f1cf976
cmt out node-exporter helm for now, add node label
pdamianov-dev Feb 23, 2026
28051bd
fix label sep
pdamianov-dev Feb 23, 2026
317a89e
stop using node-exporter
pdamianov-dev Feb 24, 2026
ea7b0e7
remove node label
pdamianov-dev Feb 24, 2026
4808c50
try out new daemon
pdamianov-dev Feb 24, 2026
153c526
fix issues
pdamianov-dev Feb 24, 2026
83db13e
fix yaml parsing issue
pdamianov-dev Feb 24, 2026
9632005
echo pods before copy
pdamianov-dev Feb 24, 2026
af0e8f8
fix with new container image
pdamianov-dev Feb 24, 2026
53bbca6
fix pod capture
pdamianov-dev Feb 24, 2026
cae0887
new try
pdamianov-dev Feb 24, 2026
e1ad571
correct folder
pdamianov-dev Feb 24, 2026
d89a2c5
fixed daemonset so that it gets deployed properly
pdamianov-dev Feb 24, 2026
9bd951a
reduce time diff
pdamianov-dev Feb 24, 2026
9670e86
adding ratios and switching to GiB
pdamianov-dev Feb 24, 2026
8eac1db
bump memory factor
pdamianov-dev Feb 24, 2026
094a641
add python parsing of psi
pdamianov-dev Feb 24, 2026
c5ffe29
tmp path then mv
pdamianov-dev Feb 25, 2026
0e1bb80
test out new cl2 report dir
pdamianov-dev Feb 25, 2026
2cc8037
fix paths
pdamianov-dev Feb 25, 2026
b5d1fa5
permissions
pdamianov-dev Feb 25, 2026
5f50339
fix paths again
pdamianov-dev Feb 25, 2026
cf3e6e1
test path
pdamianov-dev Feb 25, 2026
b2004cf
node specific metrics and change in cl2 report dir
pdamianov-dev Feb 25, 2026
013a114
test k8s change
pdamianov-dev Feb 25, 2026
bf590c9
multiple fixes
pdamianov-dev Feb 25, 2026
2a69575
adding node to measure
pdamianov-dev Feb 25, 2026
a5b4c4b
fix bash syntax error
pdamianov-dev Feb 25, 2026
2beea5e
double quotes in cl2 config
pdamianov-dev Feb 25, 2026
c5e7ec5
test several changes
pdamianov-dev Feb 25, 2026
2d4468f
fix py parse
pdamianov-dev Feb 25, 2026
5420c70
float conversion
pdamianov-dev Feb 25, 2026
4cc4bb0
metrics adjustments
pdamianov-dev Feb 26, 2026
1ffdc2a
quick fix to inaccurate metrics
pdamianov-dev Feb 27, 2026
453926b
switching to 0 over None type
pdamianov-dev Feb 27, 2026
5bbfcfa
vm sku selection
pdamianov-dev Mar 1, 2026
c0fe78b
quick test of vm sku
pdamianov-dev Mar 2, 2026
54fae63
check valid resources
pdamianov-dev Mar 2, 2026
010153e
modify path for proper sed substitution
pdamianov-dev Mar 2, 2026
44cf2ae
rearrange TF overrides to go after checkout ts repo
pdamianov-dev Mar 2, 2026
a43aaff
move from matrix to params
pdamianov-dev Mar 2, 2026
bf2b7d3
uncomment test execution and publish
pdamianov-dev Mar 3, 2026
b41e69b
testing matrix input for sed script
pdamianov-dev Mar 5, 2026
a92798a
matrix expansion and tests
pdamianov-dev Mar 5, 2026
396562a
fix file name for generics
pdamianov-dev Mar 5, 2026
19eafbf
fixing pipeline artifact naming
pdamianov-dev Mar 5, 2026
04599c6
making jobs unique
pdamianov-dev Mar 5, 2026
cb71f61
reduce max parallel and add more baseline
pdamianov-dev Mar 5, 2026
9918cc3
more baseline and more sep
pdamianov-dev Mar 5, 2026
afd9abb
time to stress
pdamianov-dev Mar 6, 2026
26243bf
revert to fix issues and hang memory usage for 5 sec
pdamianov-dev Mar 6, 2026
e1950ec
add double ki for extra stress
pdamianov-dev Mar 6, 2026
058840e
dropping waitforcontrooledpods
pdamianov-dev Mar 6, 2026
642625c
changing baseline naming and increase pod ct
pdamianov-dev Mar 6, 2026
a94d1f0
bug fix on run_id
pdamianov-dev Mar 6, 2026
7614243
remove pod latency threshold
pdamianov-dev Mar 6, 2026
6f6df9e
doubling usage of pods
pdamianov-dev Mar 9, 2026
e87f517
adding v3 and d2
pdamianov-dev Mar 11, 2026
5dd54f4
removing redundant params
pdamianov-dev Mar 11, 2026
2dc6226
add new oom metrics
pdamianov-dev Mar 16, 2026
baaf66e
fixing issues with oom
pdamianov-dev Mar 16, 2026
bba471d
gather metrics correctly
pdamianov-dev Mar 17, 2026
4e73a20
add threshold tos ee if failures dont block metrics
pdamianov-dev Mar 17, 2026
3774244
super stress
pdamianov-dev Mar 18, 2026
ce0233e
update daemon to try and capture oom_kill
pdamianov-dev Mar 18, 2026
607d6a8
one value for oom_kill
pdamianov-dev Mar 18, 2026
30398ea
separating into by cgroup to check how load impacts system vs pods
pdamianov-dev Mar 19, 2026
7f029d3
updated psi parse
pdamianov-dev Mar 19, 2026
8e91d07
more metrics
pdamianov-dev Mar 19, 2026
b1a458e
switch to node reader
pdamianov-dev Mar 20, 2026
495c0ba
fix node-reader bug
pdamianov-dev Mar 20, 2026
b1b7099
normalize pgscan
pdamianov-dev Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions jobs/competitive-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ jobs:
timeoutInMinutes: ${{ parameters.timeout_in_minutes }}
condition: or(eq(variables['Build.Reason'], 'Manual'), and(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.SourceBranchName'], 'main')))
steps:
- script: |
sed -i "/name[[:space:]]*=[[:space:]]*\"os-sku\"/,+2s/value[[:space:]]*=[[:space:]]*\"[^\"]*/value = \"$OS_SKU/g" $(Pipeline.Workspace)/s/scenarios/perf-eval/k8s-node-stress/terraform-inputs/azure.tfvars
sed -i "/name[[:space:]]*=[[:space:]]*\"userpool1\"/,+2s/vm_size[[:space:]]*=[[:space:]]*\"[^\"]*/vm_size = \"$VM_SIZE/g" $(Pipeline.Workspace)/s/scenarios/perf-eval/k8s-node-stress/terraform-inputs/azure.tfvars
cat $(pwd)/scenarios/perf-eval/k8s-node-stress/terraform-inputs/azure.tfvars
displayName: "Set TF Overrides"
- template: /steps/setup-tests.yml
parameters:
cloud: ${{ parameters.cloud }}
Expand Down
115 changes: 79 additions & 36 deletions modules/python/clusterloader2/cri/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ name: resource-consumer
{{$deploymentSize := DefaultParam .CL2_DEPLOYMENT_SIZE 10}}
{{$memory := DefaultParam .CL2_RESOURCE_CONSUME_MEMORY "100"}}
{{$memoryKi := DefaultParam .CL2_RESOURCE_CONSUME_MEMORY_KI "100"}}
{{$testMemoryKi := DefaultParam .CL2_TEST_RESOURCE_CONSUME_MEMORY_KI $memoryKi}}
{{$cpu := DefaultParam .CL2_RESOURCE_CONSUME_CPU 100}}
{{$repeats := DefaultParam .CL2_REPEATS 1}}

{{$steps := DefaultParam .CL2_STEPS 1}}
{{$nodePerStep := DefaultParam .CL2_NODE_PER_STEP 1}}
{{$totalNodes := MultiplyInt $nodePerStep $steps}}
{{$nodeToMeasure := DefaultParam .CL2_NODE_TO_MEASURE ""}}
{{$replicas := MultiplyInt $deploymentSize $totalNodes}}
{{$scaleReplicas := MultiplyInt $deploymentSize $nodePerStep}}
{{$scaleEnabled := DefaultParam .CL2_SCALE_ENABLED false}}
Expand Down Expand Up @@ -39,26 +41,31 @@ tuningSets:
steps:
- name: Start measurements
measurements:
- Identifier: TestMetrics
Method: TestMetrics
Params:
action: start
systemPodMetricsEnabled: true
clusterOOMsTrackerEnabled: true
clusterOOMsIgnoredProcesses: ""
enableRestartCountCheck: true
labelSelector: group = resource-consumer
- Identifier: PodStartupLatency
Method: PodStartupLatency
Params:
action: start
labelSelector: group = resource-consumer
threshold: {{$podStartupLatencyThreshold}}
threshold: "60s"
- Identifier: ResourceUsageSummary
Method: ResourceUsageSummary
Params:
action: start
labelSelector: group = resource-consumer
- Identifier: WaitForRunningLatencyDeployments
Method: WaitForControlledPodsRunning
- Identifier: SchedulingThroughput
Method: SchedulingThroughput
Params:
action: start
checkIfPodsAreUpdated: true
apiVersion: apps/v1
kind: Deployment
labelSelector: group = resource-consumer
operationTimeout: {{$operationTimeout}}

{{range $i := Loop $repeats}}

Expand All @@ -76,6 +83,12 @@ steps:
action: start
{{end}}

- module:
path: /node-measurements.yaml
params:
action: start
node: {{$nodeToMeasure}}

{{range $j := Loop $steps}}
- name: Create deployment {{$j}}
phases:
Expand All @@ -101,7 +114,7 @@ steps:
{{if eq $osType "windows"}}
Memory: {{$memory}}
{{else}}
Memory: {{$memoryKi}}K
Memory: {{$testMemoryKi}}K
{{end}}
CPU: --millicores={{$cpu}}
MemoryRequest: {{$memoryKi}}
Expand All @@ -112,36 +125,56 @@ steps:
OSType: {{$osType}}
HostNetwork: {{$hostNetwork}}

- name: Waiting for latency pods to be running
measurements:
- Identifier: WaitForRunningLatencyDeployments
Method: WaitForControlledPodsRunning
Params:
action: gather

- name: Wait for resource consumption
measurements:
- Identifier: Sleep
Method: Sleep
Params:
duration: 1m
{{end}}

- name: Wait for nodes to be ready
measurements:
- Identifier: ConfirmNodeCount
Method: WaitForNodes
Params:
action: start
{{if $scaleEnabled}}
minDesiredNodeCount: {{MultiplyInt (AddInt (MultiplyInt $nodePerStep (AddInt $j 1)) 1) 0.8}}
maxDesiredNodeCount: {{AddInt $totalNodes 1}}
{{range $j := Loop $steps}}
- name: Create deployment {{$j}}
phases:
- namespaceRange:
min: 1
max: 1
replicasPerNamespace: 1
tuningSet: Uniform1qps
objectBundle:
- basename: resource-consumer-{{$j}}
objectTemplatePath: deployment_template.yaml
templateFillMap:
{{if $scaleEnabled}}
{{if eq $j 0}}
Replicas: {{AddInt $scaleReplicas $deploymentSize}}
{{else}}
minDesiredNodeCount: {{MultiplyInt $totalNodes 0.8}}
maxDesiredNodeCount: {{$totalNodes}}
Replicas: {{$scaleReplicas}}
{{end}}
labelSelector: cri-resource-consume = true
timeout: 1m
refreshInterval: 5s
{{else}}
Replicas: {{$replicas}}
{{end}}
Group: resource-consumer
{{if eq $osType "windows"}}
Memory: {{$memory}}
{{else}}
Memory: {{$testMemoryKi}}K
{{end}}
CPU: --millicores={{$cpu}}
MemoryRequest: {{$memoryKi}}
CPURequest: {{$cpu}}m
LoadType: {{$loadType}}
Provider: {{$provider}}
RegistryEndpoint: {{$registryEndpoint}}
OSType: {{$osType}}
HostNetwork: {{$hostNetwork}}

- name: Wait for resource consumption
measurements:
- Identifier: Sleep
Method: Sleep
Params:
duration: 30s
{{end}}

{{if $scrapeKubelets}}
Expand All @@ -158,6 +191,12 @@ steps:
action: gather
{{end}}

- module:
path: /node-measurements.yaml
params:
action: gather
node: {{$nodeToMeasure}}

{{range $j := Loop $steps}}
- name: Deleting deployments {{$j}}
phases:
Expand All @@ -169,18 +208,18 @@ steps:
objectBundle:
- basename: resource-consumer-{{$j}}
objectTemplatePath: deployment_template.yaml

- name: Waiting for latency pods to be deleted
measurements:
- Identifier: WaitForRunningLatencyDeployments
Method: WaitForControlledPodsRunning
Params:
action: gather
{{end}}
{{end}}

- name: Collect measurements
measurements:
- Identifier: TestMetrics
Method: TestMetrics
Params:
action: gather
systemPodMetricsEnabled: true
clusterOOMsTrackerEnabled: true
enableRestartCountCheck: true
- Identifier: ResourceUsageSummary
Method: ResourceUsageSummary
Params:
Expand All @@ -189,3 +228,7 @@ steps:
Method: PodStartupLatency
Params:
action: gather
- Identifier: SchedulingThroughput
Method: SchedulingThroughput
Params:
action: gather
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ spec:
- stress
args:
- --vm
- "1"
- "3"
- --vm-bytes
- {{$Memory}}
- --vm-hang
- "0"
- "30"
- --timeout
- "3600"
{{end}}
Expand Down
45 changes: 45 additions & 0 deletions modules/python/clusterloader2/cri/config/node-measurements.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{{$action := .action}} # start, gather
{{$node := .node}}

steps:
- name: {{$action}} Node Resource Measurements
measurements:
- Identifier: ResourceMetrics
Method: GenericPrometheusQuery
Params:
action: {{$action}}
metricName: NodeResourceMetrics
metricVersion: v1
unit: mixed
queries:
# Node Level Summary
- name: NodeInfo
query: kube_node_info{node = "{{$node}}"}
- name: NodeMemoryAllocatableGiB
query: sum(kube_node_status_allocatable{resource="memory", node = "{{$node}}"}) / 1073741824
- name: NodeMemoryCapacityGiB
query: sum(kube_node_status_capacity{resource="memory", node = "{{$node}}"}) / 1073741824
- name: NodesReady
query: changes(kube_node_status_condition{condition="Ready",status="true", node = "{{$node}}"}[5m])
- name: NodePressureStatus
query: changes(kube_node_status_condition{condition="MemoryPressure", status="true", node = "{{$node}}"}[5m])
# Node Memory Usage Stats (from kubelet/cAdvisor - container metrics aggregated by node)
- name: NodeMemoryUsageTotalGiB
query: sum(container_memory_working_set_bytes{instance="{{$node}}"}) / 1073741824
- name: NodeMemoryRequestsTotalGiB
query: sum(kube_pod_container_resource_requests{resource="memory", node = "{{$node}}"}) / 1073741824
- name: NodeMemoryCommitmentGiB
query: (sum(kube_pod_container_resource_limits{resource="memory", node = "{{$node}}"}) - sum(kube_node_status_allocatable{resource="memory", node = "{{$node}}"})) / 1073741824
- name: NodeMemoryRequestToAllocatableRatio
query: sum(kube_pod_container_resource_requests{resource="memory", node = "{{$node}}"}) / sum(kube_node_status_allocatable{resource="memory", node = "{{$node}}"})
- name: NodeMemoryUsageToAllocatableRatio
query: sum(container_memory_working_set_bytes{instance="{{$node}}"}) / sum(kube_node_status_allocatable{resource="memory", node = "{{$node}}"})
- name: NodeMemoryUsageToLimitsRatio
query: sum(container_memory_working_set_bytes{instance="{{$node}}"}) / sum(kube_pod_container_resource_limits{resource="memory", node = "{{$node}}"})
# Container Level Summary
- name: ContainerRuntimes
query: count(container_runtime_version{node = "{{$node}}"})
- name: ContainerMemoryFailures
query: increase(container_memory_failcnt{node = "{{$node}}"}[5m])
- name: ContainersNearMemoryLimit
query: count((container_memory_working_set_bytes{instance="{{$node}}"} / container_spec_memory_limit_bytes{instance="{{$node}}"} > 0.8))
35 changes: 30 additions & 5 deletions modules/python/clusterloader2/cri/cri.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
setup_logging()
logger = get_logger(__name__)

MEMORY_SCALE_FACTOR = 0.95 # 95% of the total allocatable memory to account for error margin

# TODO: Refactor to use a config dataclass to reduce number of arguments
# Reference: modules/python/clusterloader2/job_controller/job_controller.py
def override_config_clusterloader2(
node_count, node_per_step, max_pods, repeats, operation_timeout,
node_count, node_to_measure, node_per_step, max_pods, repeats, operation_timeout,
load_type, scale_enabled, pod_startup_latency_threshold, provider,
registry_endpoint, os_type, scrape_kubelets, scrape_containerd, containerd_scrape_interval, host_network, override_file):
registry_endpoint, os_type, scrape_kubelets, scrape_containerd, containerd_scrape_interval, host_network, override_file, use_custom_kubelet = False):
MEMORY_SCALE_FACTOR = 1.0
client = KubernetesClient(os.path.expanduser("~/.kube/config"))
nodes = client.get_nodes(label_selector="cri-resource-consume=true")
if len(nodes) == 0:
Expand Down Expand Up @@ -51,7 +50,7 @@ def override_config_clusterloader2(
# Calculate request cpu and memory for each pod
daemonset_count = client.get_daemonsets_pods_count("kube-system", node.metadata.name)
logger.info(f"Node {node.metadata.name} has {daemonset_count} daemonset pods")
pod_count = max_pods - daemonset_count
pod_count = max_pods
cpu_request = cpu_value // pod_count
memory_request_in_ki = math.ceil(memory_value * MEMORY_SCALE_FACTOR // pod_count)
memory_request_in_k = int(memory_request_in_ki // 1.024)
Expand All @@ -75,11 +74,13 @@ def override_config_clusterloader2(
file.write(f"CL2_DEPLOYMENT_SIZE: {pod_count}\n")
file.write(f"CL2_RESOURCE_CONSUME_MEMORY: {memory_request}\n")
file.write(f"CL2_RESOURCE_CONSUME_MEMORY_KI: {memory_request_in_ki}Ki\n")
file.write(f"CL2_TEST_RESOURCE_CONSUME_MEMORY_KI: {5*memory_request_in_ki}Ki\n")
file.write(f"CL2_RESOURCE_CONSUME_CPU: {cpu_request}\n")
file.write(f"CL2_REPEATS: {repeats}\n")
file.write(f"CL2_NODE_COUNT: {node_count}\n")
file.write(f"CL2_NODE_PER_STEP: {node_per_step}\n")
file.write(f"CL2_STEPS: {steps}\n")
file.write(f"CL2_NODE_TO_MEASURE: {node_to_measure}\n")
file.write(f"CL2_OPERATION_TIMEOUT: {operation_timeout}\n")
file.write(f"CL2_LOAD_TYPE: {load_type}\n")
file.write(f"CL2_SCALE_ENABLED: {str(scale_enabled).lower()}\n")
Expand Down Expand Up @@ -139,6 +140,17 @@ def verify_measurement():
except k8s_client.ApiException as e:
logger.error(f"Error fetching metrics: {e}")

def parse_node_reader_report(file_path, template):
with open(file_path, 'r', encoding='utf-8') as file:
logger.info(f"Processing node_reader metrics report")

template["group"] = "self"
template["measurement"] = "NodeReader"

node_reader_data = json.loads(file.read())
template["data"] = node_reader_data
return template

def collect_clusterloader2(
node_count,
max_pods,
Expand Down Expand Up @@ -190,6 +202,9 @@ def collect_clusterloader2(
for f in os.listdir(cl2_report_dir):
file_path = os.path.join(cl2_report_dir, f)
with open(file_path, 'r', encoding='utf-8') as file:
if file_path.endswith("node_reader.json"):
content += json.dumps(parse_node_reader_report(file_path, template)) + "\n"
continue
measurement, group_name = get_measurement(file_path)
if not measurement:
continue
Expand All @@ -216,6 +231,12 @@ def collect_clusterloader2(
template["percentile"] = "dataItems"
template["data"] = item
content += json.dumps(template) + "\n"
else:
result = template.copy()
result["group"] = group_name
result["measurement"] = measurement
result["data"] = data
content += json.dumps(result) + "\n"

os.makedirs(os.path.dirname(result_file), exist_ok=True)
with open(result_file, 'w', encoding='utf-8') as file:
Expand All @@ -228,6 +249,9 @@ def main():
# Sub-command for override_config_clusterloader2
parser_override = subparsers.add_parser("override", help="Override CL2 config file")
parser_override.add_argument("--node_count", type=int, help="Number of nodes")
parser_override.add_argument(
"--node_to_measure", type=str, default="", help="Name of the node to gather detailed measurements from"
)
parser_override.add_argument(
"--node_per_step", type=int, help="Number of nodes to scale per step"
)
Expand Down Expand Up @@ -382,6 +406,7 @@ def main():
if args.command == "override":
override_config_clusterloader2(
args.node_count,
args.node_to_measure,
args.node_per_step,
args.max_pods,
args.repeats,
Expand Down
Loading