18 KiB
Executable File
title | authors | reviewers | approvers | creation-date | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|
CronFederatedHPA |
|
|
|
2023-06-06 |
CronFederatedHPA
Summary
For some certain predictable load change scenarios, for example Black Friday sale and Singles' Day sale, there will be sharp load peaks. With standard K8s HPA, it needs time to scale up the replicas, which may not be fast enough to handle sharp load peaks, resulting in service unavailability.
To solve this problem, there are already some approaches in single cluster, called CronHPA
, which can scale up/down the workloads at specific time. Cluster administrators can scale up the workloads in advance with CronHPA
, to meet the requirements of the sharp load peaks. So this proposal proposes CronFederatedHPA
to meet the the same requirements in multi-cluster scenario.
Motivation
The scaling speed is not fast enough to handle sharp load peaks, causing service unavailability, which is unacceptable. So the cluster administrators want to scale up/down the workloads in advance to meet the sharp load peaks in multi-cluster scenario, ensure the service is always available. This is critical in some special days, such as Black Friday sale and Singles' Day sale.
Goals
- Define the
CronFederatedHPA
API, to scale the workloads in specific time. - Support both
FederatedHPA
and workloads(Deployment
,StatefulSet
or other resources that have a subresource namedscale
). - Propose the implementation ideas for involved components, including
karmada-controller-manager
.
Non-Goals
Proposal
User Stories(Optional)
Story 1
As a cluster administrator, I know that there will be a sudden peak traffic every days at 9 o'clock AM. So I want to scale up the related service in advance(like 30 minutes) to meet the sharp load peaks, ensure the service is available when the time comes. Also, after the sharp load peaks have gone(like 2 hours latter), I want to scale down the related service to save cloud costs.
Notes/Constraints/Caveats (Optional)
Risks and Mitigations
CronFederatedHPA
could scale FederatedHPA, but also scale the workloads directly.
If the workloads are scaled by FederatedHPA
, but you configure CronFederatedHPA
to scale the workloads at the same time, it will have conflicts as follows:
So, please ensure that the workloads will not be scale by FederatedHPA
and CronFederatedHPA
at the same time.
Design Details
Architecture

From the above architecture diagram, CronFederatedHPA should do following things:
CronFederatedHPA
allow users to scale the workloads at specific time.CronFederatedHPA
allow users to set theFederatedHPA
at specific time.
API Definition
In order to satisfy the above requirements, we defined the API as following:
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=cronfhpa,categories={karmada-io}
// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.kind`,name=`REFERENCE-KIND`,type=string
// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.name`,name=`REFERENCE-NAME`,type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name=`AGE`,type=date
// CronFederatedHPA represents a collection of repeating schedule to scale
// replica number of a specific workload. It can scale any resource implementing
// the scale subresource as well as FederatedHPA.
type CronFederatedHPA struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the specification of the CronFederatedHPA.
// +required
Spec CronFederatedHPASpec `json:"spec"`
// Status is the current status of the CronFederatedHPA.
// +optional
Status CronFederatedHPAStatus `json:"status"`
}
// CronFederatedHPASpec is the specification of the CronFederatedHPA.
type CronFederatedHPASpec struct {
// ScaleTargetRef points to the target resource to scale.
// Target resource could be any resource that implementing the scale
// subresource like Deployment, or FederatedHPA.
// +required
ScaleTargetRef autoscalingv2.CrossVersionObjectReference `json:"scaleTargetRef"`
// Rules contains a collection of schedules that declares when and how
// the referencing target resource should be scaled.
// +required
Rules []CronFederatedHPARule `json:"rules"`
}
// CronFederatedHPARule declares a schedule as well as scale actions.
type CronFederatedHPARule struct {
// Name of the rule.
// Each rule in a CronFederatedHPA must have a unique name.
//
// Note: the name will be used as an identifier to record its execution
// history. Changing the name will be considered as deleting the old rule
// and adding a new rule, that means the original execution history will be
// discarded.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=32
// +required
Name string `json:"name"`
// Schedule is the cron expression that represents a periodical time.
// The syntax follows https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#schedule-syntax.
// +required
Schedule string `json:"schedule"`
// TargetReplicas is the target replicas to be scaled for resources
// referencing by ScaleTargetRef of this CronFederatedHPA.
// Only needed when referencing resource is not FederatedHPA.
// +optional
TargetReplicas *int32 `json:"targetReplicas,omitempty"`
// TargetMinReplicas is the target MinReplicas to be set for FederatedHPA.
// Only needed when referencing resource is FederatedHPA.
// TargetMinReplicas and TargetMaxReplicas can be specified together or
// either one can be specified alone.
// nil means the MinReplicas(.spec.minReplicas) of the referencing FederatedHPA
// will not be updated.
// +optional
TargetMinReplicas *int32 `json:"targetMinReplicas,omitempty"`
// TargetMaxReplicas is the target MaxReplicas to be set for FederatedHPA.
// Only needed when referencing resource is FederatedHPA.
// TargetMinReplicas and TargetMaxReplicas can be specified together or
// either one can be specified alone.
// nil means the MaxReplicas(.spec.maxReplicas) of the referencing FederatedHPA
// will not be updated.
// +optional
TargetMaxReplicas *int32 `json:"targetMaxReplicas,omitempty"`
// Suspend tells the controller to suspend subsequent executions.
// Defaults to false.
//
// +kubebuilder:default=false
// +optional
Suspend *bool `json:"suspend,omitempty"`
// TimeZone for the giving schedule.
// If not specified, this will default to the time zone of the
// karmada-controller-manager process.
// Invalid TimeZone will be rejected when applying by karmada-webhook.
// see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for the
// all timezones.
// +optional
TimeZone *string `json:"timeZone,omitempty"`
// SuccessfulHistoryLimit represents the count of successful execution items
// for each rule.
// The value must be a positive integer. It defaults to 3.
//
// +kubebuilder:default=3
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=32
// +optional
SuccessfulHistoryLimit *int32 `json:"successfulHistoryLimit,omitempty"`
// FailedHistoryLimit represents the count of failed execution items for
// each rule.
// The value must be a positive integer. It defaults to 3.
//
// +kubebuilder:default=3
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=32
FailedHistoryLimit *int32 `json:"failedHistoryLimit,omitempty"`
}
// CronFederatedHPAStatus represents the current status of a CronFederatedHPA.
type CronFederatedHPAStatus struct {
// ExecutionHistories record the execution histories of CronFederatedHPARule.
// +optional
ExecutionHistories []ExecutionHistory `json:"executionHistories,omitempty"`
}
// ExecutionHistory records the execution history of specific CronFederatedHPARule.
type ExecutionHistory struct {
// RuleName is the name of the CronFederatedHPARule.
// +required
RuleName string `json:"ruleName"`
// NextExecutionTime is the next time to execute.
// Nil means the rule has been suspended.
// +optional
NextExecutionTime *metav1.Time `json:"nextExecutionTime,omitempty"`
// SuccessfulExecutions records successful executions.
// +optional
SuccessfulExecutions []SuccessfulExecution `json:"successfulExecutions,omitempty"`
// FailedExecutions records failed executions.
// +optional
FailedExecutions []FailedExecution `json:"failedExecutions,omitempty"`
}
// SuccessfulExecution records a successful execution.
type SuccessfulExecution struct {
// ScheduleTime is the expected execution time declared in CronFederatedHPARule.
// +required
ScheduleTime *metav1.Time `json:"scheduleTime"`
// ExecutionTime is the actual execution time of CronFederatedHPARule.
// Tasks may not always be executed at ScheduleTime. ExecutionTime is used
// to evaluate the efficiency of the controller's execution.
// +required
ExecutionTime *metav1.Time `json:"executionTime"`
// AppliedReplicas is the replicas have been applied.
// It is required if .spec.rules[*].targetReplicas is not empty.
// +optional
AppliedReplicas *int32 `json:"appliedReplicas,omitempty"`
// AppliedMaxReplicas is the MaxReplicas have been applied.
// It is required if .spec.rules[*].targetMaxReplicas is not empty.
// +optional
AppliedMaxReplicas *int32 `json:"appliedMaxReplicas,omitempty"`
// AppliedMinReplicas is the MinReplicas have been applied.
// It is required if .spec.rules[*].targetMinReplicas is not empty.
// +optional
AppliedMinReplicas *int32 `json:"appliedMinReplicas,omitempty"`
}
// FailedExecution records a failed execution.
type FailedExecution struct {
// ScheduleTime is the expected execution time declared in CronFederatedHPARule.
// +required
ScheduleTime *metav1.Time `json:"scheduleTime"`
// ExecutionTime is the actual execution time of CronFederatedHPARule.
// Tasks may not always be executed at ScheduleTime. ExecutionTime is used
// to evaluate the efficiency of the controller's execution.
// +required
ExecutionTime *metav1.Time `json:"executionTime"`
// Message is the human-readable message indicating details about the failure.
// +required
Message string `json:"message"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CronFederatedHPAList contains a list of CronFederatedHPA.
type CronFederatedHPAList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CronFederatedHPA `json:"items"`
}
To better understand what is status.executionHistories[*].NextExecutionTime
, here are some examples:
- The schedule time is
3 * * * *
, I apply this rule when 9:04 AM, sostatus.executionHistories[*].NextExecutionTime
is: 10:03 AM, after executing this rule, the next time will be updated to 11:03 AM. - The schedule time is
3 * * * *
, I apply this rule when 9:01 AM, sostatus.executionHistories[*].NextExecutionTime
is: 09:03 AM, after executing this rule, the next time will be updated to 10:03 AM.
Components change
karmada-controller-manager
In order to make CronFederatedHPA
work, CronFederatedHPA controller should be implemented in karmada-controller-manager
, it will check the time and apply the rules of CronFederatedHPA
at the configured time. The timestamp is configured via spec.rules[*].schedule
, which should restrict the following format:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │ OR sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# * * * * *
For example:
Description | Equivalent to |
---|---|
Run once a year at midnight of 1 January | 0 0 1 1 * |
Run once a month at midnight of the first day of the month | 0 0 1 * * |
Run once a week at midnight on Sunday morning | 0 0 * * 0 |
Run once a day at midnight | 0 0 * * * |
Run once an hour at the beginning of the hour | 0 * * * * |
karmada-webhook
In order to make sure the applied configuration is corrent, some validations are necessary for CronFederatedHPA
, these logic should be implemented in karmada-webhook
:
- If
spec.scaleTargetRef.apiVersion
isautoscaling.karmada.io/v1alpha1
,spec.scaleTargetRef.kind
can only beFederatedHPA
,spec.rules[*].targetMinReplicas
andspec.rules[*].targetMaxReplicas
cannot be empty at the same time. - If
spec.scaleTargetRef.apiVersion
is notautoscaling.karmada.io/v1alpha1
,spec.rules[*].targetReplicas
cannot be empty. spec.rules[*].schedule
should be a valid cron format.maxDelaySeconds
should be smaller than the period interval.
Story Solution
Story 1
Requirements:
- Scale up workloads to 1000 every day at 08:30 AM.
- Scale down workloads to 1 every day at 11:00 AM.
apiVersion: autoscaling.karmada.io/v1alpha1
kind: CronFederatedHPA
metadata:
name: cron-federated-hpa
spec:
scaleTargetRef:
apiVersion: app/v1
kind: Deployment
name: shop
rules:
- ruleName: "Scale-Up"
schedule: "30 08 * * *"
targetReplicas: 1000
- ruleName: "Scale-Down"
schedule: "0 11 * * *"
targetReplicas: 1
Also, if we want a better approach, we can choose to set FederatedHPA
's MinReplicas
:
apiVersion: autoscaling.karmada.io/v1alpha1
kind: CronFederatedHPA
metadata:
name: cron-federated-hpa
spec:
scaleTargetRef:
apiVersion: autoscaling.karmada.io/v1alpha1
kind: FederatedHPA
name: shop-fhpa
rules:
- ruleName: "Scale-Up"
schedule: "30 08 * * *"
targetMinReplicas: 1000
- ruleName: "Scale-Down"
schedule: "0 11 * * *"
targetMinReplicas: 1
By using this approach, workloads will not be abruptly scaled down at 11:00 AM, which could potentially result in service interruptions.
More
As a cluster administrator, there are international business that targets the United States and China. There will be sharp load peaks when 8:00 AM everyday in the Asia/Shanghai time zone and 8:00 AM everyday in the America/Los_Angeles time zone. The workloads are deployed in the clusters in china, which are in the Asia/Shanghai time zone. So I want to configure the workloads conveniently to be scaled up. Specifically, I can configure the specific time zone for different cron scaling rules.
Requirements:
- Scale up workloads to 1000 every day at 07:30 AM everyday in the Asia/Shanghai time zone.
- Scale up workloads to 1000 every day at 07:30 AM everyday in the America/Los_Angeles time zone.
apiVersion: autoscaling.karmada.io/v1alpha1
kind: CronFederatedHPA
metadata:
name: cron-federated-hpa
spec:
scaleTargetRef:
apiVersion: autoscaling.karmada.io/v1alpha1
kind: FederatedHPA
name: shop-fhpa
rules:
- ruleName: "scale-up-asia-shanghai"
schedule: "30 07 * * *"
targetMinReplicas: 1000
timeZone: "Asia/Shanghai"
- ruleName: "scale-up-america-los-angeles"
schedule: "30 07 * * *"
targetMinReplicas: 1000
timeZone: "America/Los_Angeles"
With this configuration, the workloads will be scaled up to 1000 replicas at least every day at 07:30 AM in the Asia/Shanghai and America/Los_Angeles time zone.
High Availability
To maintain high availability for CronFederatedHPA
, the Karmada control plane should be deployed in a highly available manner, such as across multiple zones. If the Karmada control plane fully goes down, workloads can no longer be scaled.
Development Plan
This feature is should be implement in three stages:
- Implement
CronFederatedHPA
controller inkarmada-controller-manager
to scale the workloads or setFederatedHPA
. - Implement the validations in
karmada-webhook
.
Test Plan
- All current testing should be passed, no break change would be involved by this feature.
- Add new E2E test cases to cover the new feature.
- Scale workload directly with different time.
- Set
FederatedHPA
'sMinReplicas/MaxReplicas
with different time. - Construct different time and check the validations.