Autoscale: infinite scale-up when VMs fail to START (Stopped) — #11244 guard only counts Error state
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 48/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- java
- Lĩnh vực
- cloud, infrastructure
Hướng nghiên cứu
Start with AutoScaleManagerImpl, especially doScaleUp, checkConditionUp, checkConditionDown, and checkAutoScaleVmGroup, then inspect AutoScaleVmGroupVmMapDaoImpl's instance counters. Trace VirtualMachineManagerImpl.start() and the MonitorTask exception path to confirm how failed starts are handled. Done means failed starts no longer cause unbounded VM creation, with Stopped members handled consistently and regression coverage added where the project’s existing autoscale tests belong.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
ISSUE TYPE
- Bug Report
COMPONENT NAME
VM Autoscale Feature
CLOUDSTACK VERSION
4.22.0.0 (observed)
Also present in 4.22.1.0, 4.23.0.0 and main (verified by source inspection)
CONFIGURATION
Advanced zone, KVM, Ceph/RBD-only primary storage. One AutoScale VM group
(min_members=1, max_members=2, interval=30) on an isolated network with a
/24 guest CIDR.
OS / ENVIRONMENT
Linux (Debian 12), KVM hosts.
SUMMARY
The infinite-autoscaling guard added in #11244 (fixing #9318) only counts
instances in State.Error. When scale-up VMs fail to start — as opposed to
failing to be created — they land in State.Stopped, not State.Error. The
guard therefore never trips, and the group scales up on every interval
indefinitely.
In our incident this produced 2,296 VMs from a group whose max_members is 2,
over roughly 21 hours, until the guest subnet was exhausted.
Two independent counters are involved and both exclude Stopped:
AutoScaleVmGroupVmMapDaoImpl.getErroredInstanceCount()— the #11244 guard —
countsState.Erroronly:
public int getErroredInstanceCount(long vmGroupId) {
SearchCriteria<Integer> sc = CountBy.create();
sc.setParameters("vmGroupId", vmGroupId);
sc.setJoinParameters("vmSearch", "states", State.Error); // Stopped not counted
...
}
AutoScaleVmGroupVmMapDaoImpl.countAvailableVmsByGroup()— used by every
scaling decision inAutoScaleManagerImpl(checkConditionUp,
checkConditionDown,checkAutoScaleVmGroup, and the group-state handlers) —
counts onlyStarting,Running,Stopping,Migrating:
sc.setJoinParameters("vmSearch", "states",
State.Starting, State.Running, State.Stopping, State.Migrating); // Stopped not counted
So with N leaked Stopped members, currentVM == 0:
checkAutoScaleVmGroup:if (currentVM < minMembers)->0 < 1-> scale up, every intervalcheckAutoScaleVmGroup:if (currentVM > maxMembers)->0 > 2-> scale-down never firescheckConditionDown:if (currentVM - 1 < minVm)->-1 < 1-> scale-down additionally blockedcheckConditionUp: errored-instance guard ->0 > 10false -> guard never trips
A third defect prevents the failed VM from being cleaned up, which is what allows
the leak to accumulate in the first place. doScaleUp persists the group map row
before attempting the start, and its cleanup is guarded on ServerApiException:
// AutoScaleManagerImpl.doScaleUp
autoScaleVmGroupVmMapDao.persist(groupVmMapVO); // persisted BEFORE the start attempt
try {
startNewVM(vm.getId());
...
} catch (ServerApiException e) {
...
destroyVm(vm.getId()); // never reached, see below
break;
}
startNewVM does convert InsufficientCapacityException into ServerApiException,
but it never sees that exception, because VirtualMachineManagerImpl.start()
has already wrapped it into an unchecked CloudRuntimeException:
try {
advanceStart(vmUuid, params, planToDeploy, planner);
} catch (ConcurrentOperationException | InsufficientCapacityException e) {
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e);
}
CloudRuntimeException matches none of startNewVM's typed catches and is not a
ServerApiException, so it propagates past doScaleUp's handler to
AutoScaleManagerImpl$MonitorTask, and destroyVm() is never called. The
observed log line is exactly this:
WARN [c.c.n.a.A.MonitorTask] Caught the following exception on monitoring AutoScale Vm Group
com.cloud.utils.exception.CloudRuntimeException: Unable to start a VM [...]
Note that PR #9574 ("Prevent infinite retries of autoscaling"), which proposed a
one-line change to AutoScaleVmGroupVmMapDaoImpl, was closed unmerged; the merged
#11244 took the threshold approach instead, which is what leaves this variant
uncovered.
STEPS TO REPRODUCE
-
Create an AutoScale VM group (
min_members=1,max_members=2, short interval). -
Let it stabilise at 1 running VM.
-
Break VM start (not creation) in a way that returns an
InsufficientCapacityExceptionorResourceUnavailableExceptionfrom
advanceStart.The trigger we actually hit was the group network's Virtual Router becoming
unreachable, soVirtualRouterElement.applyDhcpEntriesfailed with:
ResourceUnavailableException: Resource [DataCenter:1] is unreachable: Unable to apply dhcp entry on router.
That was an observed failure rather than a deliberate test, so I have not
confirmed that stopping the VR is a minimal reproducer — any start-path failure
that surfaces asCloudRuntimeExceptionout ofVirtualMachineManagerImpl.start()
should exhibit the same leak. -
Observe: one new VM per interval, each landing in
Stopped, each retaining its
autoscale_vmgroup_vm_maprow, indefinitely.
EXPECTED RESULTS
Scale-up stops after a bounded number of consecutive failed starts, and/or failed
instances are cleaned up, and/or Stopped members count toward max_members.
ACTUAL RESULTS
Unbounded VM creation. In our case, one VM per 30s for ~21 hours:
- 2,043 VMs in
Error(created after the guest subnet was exhausted — these fail
at IP allocation, before a NIC is assigned) - 253 VMs in
Stopped, each holding a NIC and therefore a guest IP - The /24 guest network reached 254/254 NICs and all subsequent VM deployments —
including unrelated, non-autoscale ones — failed with
InsufficientVirtualNetworkCapacityException: Unable to acquire Guest IP address
autoscale.errored.instance.threshold was at its default of 10 throughout, and
getErroredInstanceCount() returned 0 the entire time, because none of the leaked
instances were in Error — they were in Stopped.
SUGGESTED FIX
Any one of these would break the loop; the first two seem most direct:
- Include
State.StoppedingetErroredInstanceCount()(or add a separate
"failed instance" counter covering bothErrorandStopped). - Broaden
doScaleUp's catch fromServerApiExceptionto also handle
CloudRuntimeException, sodestroyVm()runs and the member is not leaked. - Count
Stoppedmembers incountAvailableVmsByGroup()so that leaked members
contribute tomax_membersand become eligible for scale-down.
- Ngôn ngữ chính
- Java
- Star
- 3.1k
- Fork
- 1.4k
- Merge trung bình
- 7 ngày 5 giờ
- Pull request đã merge (30 ngày)
- 28
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của apache/cloudstack
-
create-kubernetes-binaries-iso.sh builds the ISO without setting a volume ID on EL8 based os's Đang mởbug component:kubernetes
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
apache/cloudstack#14180 ·
-
bug component:projects component:UI
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
apache/cloudstack#14070 · 5 bình luận ·
-
component:backup
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
apache/cloudstack#14013 ·
-
KVM agent fails to connect to Ceph RBD storage pool after upgrading Ceph client to Tentacle 20.2.4 Đang mởbug component:ceph
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
apache/cloudstack#13989 · 3 bình luận ·
-
component:UI
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
apache/cloudstack#13944 · 3 bình luận ·
Tất cả issue của apache/cloudstack
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
infinispan/infinispan#18150 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
-
untriaged
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
opensearch-project/k-NN#3597 ·
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100