Skip to content

Commit 991dfbf

Browse files
jubradclaude
andauthored
clusterd: set TCP keepalive sysctls on pods (#36351)
## Summary - Container network namespaces get default kernel values, not the host's custom sysctl settings — host-level TCP keepalive tuning was being silently overwritten by container defaults - Adds `net.ipv4.tcp_keepalive_time=300`, `net.ipv4.tcp_keepalive_intvl=30`, `net.ipv4.tcp_keepalive_probes=3` to the clusterd `PodSecurityContext` - Adds `validate_clusterd_pod()` to the orchestratord integration tests, called from all three existing clusterd pod validation sites (`SwapEnabledGlobal`, `StorageClass`, `ClusterdCpu`) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a259272 commit 991dfbf

2 files changed

Lines changed: 44 additions & 2 deletions

File tree

src/orchestrator-kubernetes/src/lib.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use k8s_openapi::api::core::v1::{
3030
PersistentVolumeClaimTemplate, Pod, PodAffinity, PodAffinityTerm, PodAntiAffinity,
3131
PodSecurityContext, PodSpec, PodTemplateSpec, PreferredSchedulingTerm, ResourceRequirements,
3232
SeccompProfile, Secret, SecurityContext, Service as K8sService, ServicePort, ServiceSpec,
33-
Toleration, TopologySpreadConstraint, Volume, VolumeMount, VolumeResourceRequirements,
33+
Sysctl, Toleration, TopologySpreadConstraint, Volume, VolumeMount, VolumeResourceRequirements,
3434
WeightedPodAffinityTerm,
3535
};
3636
use k8s_openapi::apimachinery::pkg::api::resource::Quantity;
@@ -1089,15 +1089,34 @@ impl NamespacedOrchestrator for NamespacedKubernetesOrchestrator {
10891089
None
10901090
};
10911091

1092+
let tcp_keepalive_sysctls = vec![
1093+
Sysctl {
1094+
name: "net.ipv4.tcp_keepalive_time".to_string(),
1095+
value: "300".to_string(),
1096+
},
1097+
Sysctl {
1098+
name: "net.ipv4.tcp_keepalive_intvl".to_string(),
1099+
value: "30".to_string(),
1100+
},
1101+
Sysctl {
1102+
name: "net.ipv4.tcp_keepalive_probes".to_string(),
1103+
value: "3".to_string(),
1104+
},
1105+
];
1106+
10921107
let security_context = if let Some(fs_group) = self.config.service_fs_group {
10931108
Some(PodSecurityContext {
10941109
fs_group: Some(fs_group),
10951110
run_as_user: Some(fs_group),
10961111
run_as_group: Some(fs_group),
1112+
sysctls: Some(tcp_keepalive_sysctls),
10971113
..Default::default()
10981114
})
10991115
} else {
1100-
None
1116+
Some(PodSecurityContext {
1117+
sysctls: Some(tcp_keepalive_sysctls),
1118+
..Default::default()
1119+
})
11011120
};
11021121

11031122
let mut tolerations = vec![

test/orchestratord/mzcompose.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,26 @@ def validate_node_selector(
11481148
assert "materialize.cloud/scratch-fs" not in selector
11491149

11501150

1151+
EXPECTED_CLUSTERD_SYSCTLS = {
1152+
"net.ipv4.tcp_keepalive_time": "300",
1153+
"net.ipv4.tcp_keepalive_intvl": "30",
1154+
"net.ipv4.tcp_keepalive_probes": "3",
1155+
}
1156+
1157+
1158+
def validate_clusterd_pod(pod: dict[str, Any]) -> None:
1159+
"""Validate always-on properties of a clusterd pod."""
1160+
sysctls = {
1161+
s["name"]: s["value"]
1162+
for s in pod["spec"].get("securityContext", {}).get("sysctls", [])
1163+
}
1164+
for name, expected in EXPECTED_CLUSTERD_SYSCTLS.items():
1165+
actual = sysctls.get(name)
1166+
assert (
1167+
actual == expected
1168+
), f"Expected sysctl {name}={expected}, but got {actual}"
1169+
1170+
11511171
def validate_container_resources(
11521172
resources: dict[str, dict[str, str]],
11531173
swap_enabled: bool,
@@ -1196,6 +1216,7 @@ def validate(self, mods: dict[type[Modification], Any]) -> None:
11961216

11971217
def check_pods() -> None:
11981218
clusterd = get_pod_data(labels)["items"][0]
1219+
validate_clusterd_pod(clusterd)
11991220

12001221
resources = clusterd["spec"]["containers"][0]["resources"]
12011222
validate_container_resources(resources, self.value)
@@ -1251,6 +1272,7 @@ def validate(self, mods: dict[type[Modification], Any]) -> None:
12511272

12521273
def check_pods() -> None:
12531274
clusterd = get_pod_data(labels)["items"][0]
1275+
validate_clusterd_pod(clusterd)
12541276

12551277
resources = clusterd["spec"]["containers"][0]["resources"]
12561278
validate_container_resources(resources, mods[SwapEnabledGlobal])
@@ -1366,6 +1388,7 @@ def cpu_to_k8s_quantity(cpu: float | None) -> str | None:
13661388

13671389
def check_pods() -> None:
13681390
clusterd = get_pod_data(labels)["items"][0]
1391+
validate_clusterd_pod(clusterd)
13691392
resources = clusterd["spec"]["containers"][0]["resources"]
13701393
actual_cpu_request = resources.get("requests", {}).get("cpu")
13711394
actual_cpu_limit = resources.get("limits", {}).get("cpu")

0 commit comments

Comments
 (0)