-
Notifications
You must be signed in to change notification settings - Fork 402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Disable zero downtime upgrade for a RayService using RayServiceSpec #2468
base: master
Are you sure you want to change the base?
Conversation
e436040
to
3870fb7
Compare
3870fb7
to
702d2ba
Compare
702d2ba
to
21fdf3f
Compare
type UpgradeStrategy string | ||
|
||
const ( | ||
BlueGreenUpgrade UpgradeStrategy = "BlueGreenUpgrade" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking of alternatives out loud:
- RollingUpdate / Recreate
- CreateThenDelete / DeleteThenCreate
- CreateFirst / DeleteFirst
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or perhaps we can follow upgrade strategy types similar to StatefulSet: RollingUpdate / OnDelete https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see "RollingUpdate" being misleading because we only ever update 1 new RayCluster at a time, so it's not really a rolling update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use the "OnDelete" as I think it fits the "delete first" and "delete then create". For the zero downtime maybe we can name it something along the lines of "PrepareUpdateThenSwap" to describe that the updated cluster is getting ready?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnDelete
implies the users has to manually delete the cluster first. This is a new behavior which is maybe okay? If we are still automatically deleting the cluster, then it should be just Delete
, DeleteFirst
, Replace
something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I see, so like upgrade OnDelete. If that's the case, my vote would be for "Replace" or "DeleteFirst".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, I think having a separate strategy for OnDelete and DeleteFirst would be valuable. So in total we could support 3 upgrade strategies
- spec.upgradeStrategy=RollingUpdate -- same behavior as ENABLE_ZERO_DOWNTIME=true
- spec.upgradeStrategy=DeleteFirst -- same behavior as ENABLE_ZERO_DOWNTIME=false
- spec.upgradeStrategy=OnDelete -- new behavior, only upgrade if user manually deletes the RayCluster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnDelete implies the users has to manually delete the cluster first. This is a new behavior which is maybe okay?
I think the behavior of OnDelete
is fine, but it may require additional code changes, such as updating the CR status. This is beyond the scope of this PR. Maybe we can open an issue to track the progress, and we can revisit whether we need this behavior after the refactoring of RayService.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ENABLE_ZERO_DOWNTIME=false
case, how about name it to None
or Disabled
or NoUpgrade
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current behavior of ENABLE_ZERO_DOWNTIME=false
is to ignore all spec changes and do nothing.
b1c2096
to
179207c
Compare
ray-operator/controllers/ray/rayservice_controller_unit_test.go
Outdated
Show resolved
Hide resolved
ray-operator/controllers/ray/rayservice_controller_unit_test.go
Outdated
Show resolved
Hide resolved
179207c
to
8f69b78
Compare
if zeroDowntimeEnvVar != "" { | ||
enableZeroDowntime = strings.ToLower(zeroDowntimeEnvVar) != "false" | ||
} else { | ||
enableZeroDowntime = rayServiceSpecUpgradeStrategy == rayv1.NewCluster |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may cause issues when upgrading the CRD. Can you test the case that:
- Install KubeRay v1.2.2
- Create a RayService
- Upgrade KubeRay to this PR (without upgrading CRD)
- Try to trigger zero-downtime upgrade.
I expect that the zero-downtime upgrade will not be triggered. The reason is that the KubeRay v1.2.2's CRD doesn't have the field rayServiceInstance.Spec.RayServiceUpgradeStrategy
, so the zero value of string
will be used, so rayServiceSpecUpgradeStrategy == rayv1.NewCluster
will be false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If my above statement is correct (I'm not 100% sure), can you:
- Add a check in
validateRayServiceSpec
to make sure the value ofrayServiceInstance.Spec.RayServiceUpgradeStrategy
is valid (for now,NewCluster
,None
, and the zero value of string are valid).
func validateRayServiceSpec(rayService *rayv1.RayService) error { |
- Handle the case if
RayServiceUpgradeStrategy
is an empty string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, would you mind adding some comments to summarize the mechanism to control zero-downtime upgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your statement is correct, with the current logic, it did not trigger zero-downtime.
I changed the logic within rayservice controller to default zero-downtime to true
let me know if this is sufficient.
c2567a1
to
818e12b
Compare
818e12b
to
ec428ab
Compare
@@ -57,6 +66,9 @@ type RayServiceSpec struct { | |||
DeploymentUnhealthySecondThreshold *int32 `json:"deploymentUnhealthySecondThreshold,omitempty"` | |||
// ServeService is the Kubernetes service for head node and worker nodes who have healthy http proxy to serve traffics. | |||
ServeService *corev1.Service `json:"serveService,omitempty"` | |||
// RayServiceUpgradeStrategy represents the strategy used when upgrading the RayService. Currently supports `NewCluster` and `None` | |||
// +kubebuilder:default:=NewCluster | |||
RayServiceUpgradeStrategy RayServiceUpgradeStrategy `json:"rayServiceUpgradeStrategy,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I did it 😅
ec428ab
to
4a531cb
Compare
@@ -57,6 +66,9 @@ type RayServiceSpec struct { | |||
DeploymentUnhealthySecondThreshold *int32 `json:"deploymentUnhealthySecondThreshold,omitempty"` | |||
// ServeService is the Kubernetes service for head node and worker nodes who have healthy http proxy to serve traffics. | |||
ServeService *corev1.Service `json:"serveService,omitempty"` | |||
// UpgradeStrategy represents the strategy used when upgrading the RayService. Currently supports `NewCluster` and `None` | |||
// +kubebuilder:default:=NewCluster | |||
UpgradeStrategy RayServiceUpgradeStrategy `json:"upgradeStrategy,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what will happen if specify a value for UpgradeStrategy
other than NewCluster
or None
? If OpenAPI doesn't complain, we should add a check in validateRayServiceSpec
to make sure the value is valid.
https://github.com/ray-project/kuberay/pull/2468/files#r1823902608
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added validation to validateRayServiceSpec
…le/enable zero-downtime update.
4a531cb
to
29a5a74
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM @kevin85421 can you take another look?
@@ -248,6 +248,12 @@ func validateRayServiceSpec(rayService *rayv1.RayService) error { | |||
if headSvc := rayService.Spec.RayClusterSpec.HeadGroupSpec.HeadService; headSvc != nil && headSvc.Name != "" { | |||
return fmt.Errorf("spec.rayClusterConfig.headGroupSpec.headService.metadata.name should not be set") | |||
} | |||
|
|||
if currStrat := rayService.Spec.UpgradeStrategy; currStrat != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if currStrat := rayService.Spec.UpgradeStrategy; currStrat != "" { | |
if upgradeStrategy := rayService.Spec.UpgradeStrategy; upgradeStrategy != "" { |
|
||
if currStrat := rayService.Spec.UpgradeStrategy; currStrat != "" { | ||
if currStrat != rayv1.NewCluster && currStrat != rayv1.None { | ||
return fmt.Errorf("spec.UpgradeStrategy is invalid") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you print the invalid value and the valid options?
enableZeroDowntime := true | ||
if s := os.Getenv(ENABLE_ZERO_DOWNTIME); strings.ToLower(s) == "false" { | ||
enableZeroDowntime = false | ||
if zeroDowntimeEnvVar != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will happen if we set ENABLE_ZERO_DOWNTIME: true
and Spec.UpgradeStrategy: None
?
As discussed, ENABLE_ZERO_DOWNTIME
is an operator-level configuration, while Spec.UpgradeStrategy
is a CR-level configuration. The CR-level configuration should take priority over ENABLE_ZERO_DOWNTIME
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also write down some comments to summarize the mechanism to control zero-downtime upgrade as I mentioned https://github.com/ray-project/kuberay/pull/2468/files#r1823903220?
@@ -815,6 +823,14 @@ func TestReconcileRayCluster(t *testing.T) { | |||
updateKubeRayVersion: true, | |||
kubeRayVersion: "new-version", | |||
}, | |||
// Test 7: Zero downtime upgrade is enabled, but is enabled through the RayServiceSpec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add more tests? We have 4 combinations enableZeroDowntime
(true, false) and rayServiceUpgradeStrategy
(NewCluster, None).
Why are these changes needed?
To allow disabling zero downtime upgrade for a specific ray service
Related issue number
Part of #2397
Checks