Skip to content

Commit a5b8cb1

Browse files
authored
remove memory limit (#3604) (#3606)
(cherry picked from commit 9217828) (cherry picked from commit 93d7517)
1 parent 55c9441 commit a5b8cb1

5 files changed

Lines changed: 113 additions & 43 deletions

File tree

azurelinuxagent/common/event.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class WALAEventOperation:
137137
RequestedStateDisabled = "RequestedStateDisabled"
138138
RequestedVersionMismatch = "RequestedVersionMismatch"
139139
ResetFirewall = "ResetFirewall"
140+
ResetMemory = "ResetMemory"
140141
Restart = "Restart"
141142
SetCGroupsLimits = "SetCGroupsLimits"
142143
SignatureValidation = "SignatureValidation" # Event for general logs related to package signature or manifest validation that don't fall under a specific operation.

azurelinuxagent/ga/cgroupconfigurator.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def initialize(self):
152152
# Before agent setup, cleanup the old agent setup (drop-in files) since new agent uses different approach(systemctl) to setup cgroups.
153153
log_cgroup_info("Cleaning up old agent setup (drop-in files), if any")
154154
self._cleanup_old_agent_setup()
155+
if self.using_cgroup_v2():
156+
self._reset_resource_quota(systemd.get_agent_unit_name(), ResourceName.MEMORY, ignore_enforce_check=True)
155157

156158
# Notes about slice setup:
157159
# For machines where daemon version did not already create azure.slice, the
@@ -183,8 +185,7 @@ def initialize(self):
183185
if isinstance(controller, _CpuController) and self._cgroups_api.can_enforce_cpu():
184186
self._set_resource_quota(agent_unit_name, {ResourceName.CPU:conf.get_agent_cpu_quota()})
185187
controller.track_throttle_time(True) # CPU controller track the throttle time only when CPU quota is set
186-
elif isinstance(controller, _MemoryController) and self._cgroups_api.can_enforce_memory():
187-
self._set_resource_quota(agent_unit_name, {ResourceName.MEMORY:conf.get_agent_memory_quota()})
188+
elif isinstance(controller, _MemoryController):
188189
self._agent_memory_metrics = controller
189190
CGroupsTelemetry.track_cgroup_controller(controller)
190191

@@ -194,9 +195,9 @@ def initialize(self):
194195
log_cgroup_info('Agent cgroups enabled: {0}'.format(self._agent_cgroups_enabled))
195196
self._initialized = True
196197

197-
if self._cgroups_api is not None and not self._cgroups_api.can_enforce_cpu():
198-
# If agent cgroups are not enabled or quotas not enabled, reset the quota for the agent unit
199-
log_cgroup_info("Reset CPU quota if agent cgroups were not enabled for enforcement")
198+
# If agent cgroups are not enabled or CPU quotas cannot be enforced, reset the quota for the agent unit
199+
if self._cgroups_api is not None and (not self._agent_cgroups_enabled or not self._cgroups_api.can_enforce_cpu()):
200+
log_cgroup_info("Reset CPU quota since agent cgroups are not enabled for enforcement")
200201
self._reset_resource_quota(systemd.get_agent_unit_name(), ResourceName.CPU, ignore_enforce_check=True)
201202

202203
def _check_cgroups_supported(self):

azurelinuxagent/ga/update.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,36 @@ def run(self, debug=False):
332332
"""
333333

334334
try:
335+
reset_memory_telemetry = []
336+
msg1 = "[{0}] Starting ext_handler process".format(datetime.now(UTC))
337+
logger.info(msg1)
338+
reset_memory_telemetry.append(msg1)
339+
try:
340+
if systemd.is_systemd():
341+
unit_name = systemd.get_agent_unit_name()
342+
current_limit = systemd.get_unit_property(unit_name, "MemoryHigh").strip().lower() # check if the property exists
343+
if current_limit != "infinity":
344+
systemd.set_unit_run_time_property(unit_name, "MemoryHigh", "")
345+
msg2 = "[{0}] Reset agent cgroup MemoryHigh property to infinity finished.".format(datetime.now(UTC))
346+
logger.info(msg2)
347+
reset_memory_telemetry.append(msg2)
348+
new_limit = systemd.get_unit_property(unit_name, "MemoryHigh")
349+
msg3 = "[{0}] Current MemoryHigh is {1}.".format(datetime.now(UTC), new_limit)
350+
logger.info(msg3)
351+
reset_memory_telemetry.append(msg3)
352+
else:
353+
msg4 = "[{0}] Agent cgroup MemoryHigh property is already set to infinity, no need to reset it.".format(datetime.now(UTC))
354+
logger.info(msg4)
355+
reset_memory_telemetry.append(msg4)
356+
else:
357+
msg6 = "[{0}] Systemd is not present, skipping reset of agent cgroup MemoryHigh property.".format(datetime.now(UTC))
358+
logger.info(msg6)
359+
reset_memory_telemetry.append(msg6)
360+
except Exception as e:
361+
msg5 = "[{0}] Failed to reset agent cgroup MemoryHigh property: {1}".format(datetime.now(UTC), ustr(e))
362+
logger.info(msg5)
363+
reset_memory_telemetry.append(msg5)
364+
335365
logger.info("{0} (Goal State Agent version {1})", AGENT_LONG_NAME, AGENT_VERSION)
336366
logger.info("OS: {0} {1}", DISTRO_NAME, DISTRO_VERSION)
337367
logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO)
@@ -377,6 +407,9 @@ def run(self, debug=False):
377407
# Initialize the common parameters for telemetry events
378408
initialize_event_logger_vminfo_common_parameters_and_protocol(protocol)
379409

410+
# reporting reset memory telemetry
411+
add_event(AGENT_NAME, op=WALAEventOperation.ResetMemory, message="\n".join(reset_memory_telemetry))
412+
380413
# Send telemetry if protocol endpoint is not the known WireServer endpoint.
381414
endpoint = protocol.get_endpoint()
382415
if endpoint is not None and endpoint != KNOWN_WIRESERVER_IP:

tests/ga/test_cgroupconfigurator.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,45 @@ def test_initialize_should_start_tracking_other_controllers_when_one_is_not_pres
226226
any(cg for cg in tracked if tracked[cg].name == AGENT_NAME_TELEMETRY and 'cpu' in cg),
227227
"The Agent's cpu is being tracked. Tracked: {0}".format(tracked))
228228

229-
def test_agent_enforcement_enabled_in_v2(self):
229+
def test_agent_should_set_cpu_quota_and_not_set_memory_quota_in_v2(self):
230230
with self._get_cgroup_configurator_v2() as configurator:
231231
cmd1 = 'systemctl set-property walinuxagent.service CPUQuota=50% --runtime'
232-
cmd2 = 'systemctl set-property walinuxagent.service MemoryHigh=314572800 --runtime'
233232
self.assertIn(cmd1, configurator.mocks.commands_call_list, "The command to set CPU quota was not called")
234-
self.assertIn(cmd2, configurator.mocks.commands_call_list, "The command to set Memory quota was not called")
233+
# The agent no longer enforces a memory limit via systemd; it must never set MemoryHigh to a value.
234+
cmd2 = "systemctl set-property walinuxagent.service MemoryHigh=314572800 --runtime"
235+
self.assertNotIn(cmd2, configurator.mocks.commands_call_list, "The command to set Memory quota was called")
236+
237+
def test_agent_should_reset_memory_quota_in_v2_when_previously_set(self):
238+
# Simulate a previously-set MemoryHigh on the agent unit; the agent should reset it on initialize.
239+
command_mocks = [MockCommand(r"^systemctl show (.+) --property MemoryHigh$",
240+
'''MemoryHigh=314572800
241+
''')]
242+
with self._get_cgroup_configurator_v2(mock_commands=command_mocks) as configurator:
243+
cmd = 'systemctl set-property walinuxagent.service MemoryHigh= --runtime'
244+
self.assertIn(cmd, configurator.mocks.commands_call_list,
245+
"The command to reset the Memory quota was not called")
246+
247+
def test_agent_should_not_reset_memory_quota_in_v2_when_already_infinity(self):
248+
# The default v2 mock returns MemoryHigh=infinity; the reset must be a no-op (no systemctl call).
249+
with self._get_cgroup_configurator_v2() as configurator:
250+
for cmd in configurator.mocks.commands_call_list:
251+
self.assertNotIn(
252+
"systemctl set-property walinuxagent.service MemoryHigh= --runtime", cmd,
253+
"MemoryHigh reset should not be issued when current value is already infinity. Command: {0}".format(cmd))
254+
255+
def test_agent_should_reset_cpu_quota_when_previously_set_and_agent_not_enabled_now(self):
256+
command_mocks = [
257+
MockCommand(r"^systemctl show walinuxagent\.service --property ControlGroup$",
258+
'''ControlGroup=/azure.slice/walinuxagent.service
259+
'''),
260+
MockCommand(r"^systemctl show (.+) --property CPUQuotaPerSecUSec$",
261+
'''CPUQuotaPerSecUSec=5ms
262+
''')
263+
]
264+
with self._get_cgroup_configurator_v2(mock_commands=command_mocks) as configurator:
265+
cmd = 'systemctl set-property walinuxagent.service CPUQuota= --runtime'
266+
self.assertIn(cmd, configurator.mocks.commands_call_list,
267+
"The command to reset the cpu quota was not called")
235268

236269
def test_accounting_properties_not_set_explicitly_in_cgroupv2(self):
237270
with self._get_cgroup_configurator_v2() as configurator:

tests_e2e/tests/scripts/agent_memory_quota-check_agent_memory_quota.py

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def prepare_agent():
4545
log.info("Executing script update-waagent-conf to enable agent cgroups config flag")
4646
result = shellutil.run_command(["update-waagent-conf", "Debug.CgroupCheckPeriod=30", "Debug.CgroupLogMetrics=y",
4747
"Debug.CgroupDisableOnProcessCheckFailure=n",
48-
"Debug.CgroupDisableOnQuotaCheckFailure=n",
49-
"Debug.AgentMemoryQuota=104857600"])
48+
"Debug.CgroupDisableOnQuotaCheckFailure=n"
49+
])
5050
log.info("Successfully enabled agent cgroups config flag: {0}".format(result))
5151

5252
found: bool = retry_if_false(
@@ -55,24 +55,24 @@ def prepare_agent():
5555
fail("Agent cgroups not enabled")
5656

5757

58-
def verify_agent_has_memory_quota_set():
58+
def verify_agent_has_no_memory_quota_set():
5959
"""
60-
This method verifies that the agent's cgroup has memory quota set
60+
This method verifies that the agent's cgroup does NOT have a memory quota set.
61+
The agent no longer enforces a memory limit via systemd; MemoryHigh should be 'infinity'.
6162
"""
62-
log.info("** Verifying agent cgroup has memory quota set")
63+
log.info("** Verifying agent cgroup has no memory quota set (MemoryHigh=infinity)")
6364

64-
def check_memory_quota() -> bool:
65+
def check_no_memory_quota() -> bool:
6566
quota = get_agent_memory_quota()
66-
if quota is None or quota == "infinity":
67-
return False
68-
return True
67+
# 'infinity' means no memory limit is enforced.
68+
return quota.strip().lower() == "infinity"
6969

70-
found: bool = retry_if_false(check_memory_quota)
70+
found: bool = retry_if_false(check_no_memory_quota)
7171
if found:
7272
log.info("Agent Memory Quota: %s", get_agent_memory_quota())
73-
log.info("Successfully verified agent cgroup has memory quota set")
73+
log.info("Successfully verified agent cgroup has no memory quota set")
7474
else:
75-
fail("The agent's cgroup doesn't seem to have memory quota set. Agent Memory Quota: {0}".format(get_agent_memory_quota()))
75+
fail("The agent's cgroup should not have a memory quota set, but MemoryHigh={0}".format(get_agent_memory_quota()))
7676

7777

7878
def verify_agent_reported_memory_metrics():
@@ -105,41 +105,43 @@ def check_agent_log_for_metrics() -> bool:
105105

106106
def verify_memory_throttling_check_on_agent_cgroups():
107107
"""
108-
This method verifies that the agent detects memory throttling on its cgroup
108+
This method verifies that the agent reports memory throttling metrics on its cgroup,
109+
and that all reported values are zero. Since the agent no longer enforces a memory limit
110+
(MemoryHigh=infinity), there should be no memory throttling events.
109111
"""
110-
log.info("** Verifying agent detected memory throttling on its cgroup")
112+
log.info("** Verifying agent reports zero memory throttling on its cgroup")
111113

114+
# Only consider records logged from now on; older entries (e.g. from before this test
115+
# configured the agent) must not influence the assertion.
112116
throttled_events = []
113-
pressure_time = []
114117

115118
def check_agent_log_for_metrics() -> bool:
119+
# Rebuild on every attempt so the assertion reflects only the latest scan window
120+
# and does not accumulate values across retries.
121+
del throttled_events[:]
116122
for record in AgentLog().read():
117123
match = re.search(r"Memory/Total Memory Throttled Events \s*\[walinuxagent.service\]\s*=\s*([0-9.]+)", record.message)
118124
if match is not None:
119125
throttled_events.append(float(match.group(1)))
120-
else:
121-
match = re.search(r"Memory/Memory Pressure \(s\)\s*\[walinuxagent.service\]\s*=\s*([0-9.]+)", record.message)
122-
if match is not None:
123-
pressure_time.append(float(match.group(1)))
124-
if len(pressure_time) < 1 or len(throttled_events) < 1:
125-
return False
126-
return True
126+
return len(throttled_events) >= 1
127127

128-
distro = shellutil.run_command("get_distro.py").rstrip().lower()
128+
found: bool = retry_if_false(check_agent_log_for_metrics, delay=60)
129+
if not found:
130+
fail(
131+
"The agent doesn't seem to be collecting Memory Throttling metrics. Agent found Memory Throttle Events: {0}".format(
132+
throttled_events))
129133

130-
if "rhel" in distro:
131-
log.info("Skipping memory throttling check verification on RHEL distros due to known issues with memory pressure file not present.")
132-
return
134+
log.info("Memory Throttle Events: %s", throttled_events)
133135

134-
found: bool = retry_if_false(check_agent_log_for_metrics, delay=60)
135-
if found:
136-
log.info("Memory Throttle Events: %s", throttled_events)
137-
log.info("Memory Pressure Time: %s", pressure_time)
138-
log.info("Successfully verified agent reported memory throttling metrics")
139-
else:
136+
# No memory quota is enforced, so every reported value must be zero.
137+
non_zero_throttled = [v for v in throttled_events if v != 0.0]
138+
if non_zero_throttled:
140139
fail(
141-
"The agent doesn't seem to be collecting Memory Throttling metrics. Agent found Memory Throttle Events: {0} and Pressure: {1}".format(
142-
throttled_events, pressure_time))
140+
"Expected all memory throttling values to be zero (no memory limit is enforced), "
141+
"but found non-zero Memory Throttle Events: {0}".format(
142+
non_zero_throttled))
143+
144+
log.info("Successfully verified agent reported zero memory throttling metrics")
143145

144146

145147
def cleanup_test_setup():
@@ -161,7 +163,7 @@ def cleanup_test_setup():
161163
def main():
162164
skip_if_distro_not_supports_memory_quota()
163165
prepare_agent()
164-
verify_agent_has_memory_quota_set()
166+
verify_agent_has_no_memory_quota_set()
165167
verify_agent_reported_memory_metrics()
166168
verify_memory_throttling_check_on_agent_cgroups()
167169
cleanup_test_setup()

0 commit comments

Comments
 (0)