Skip to content
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][RayService] Set default ports #3262

Merged

Conversation

machichima
Copy link
Contributor

@machichima machichima commented Apr 1, 2025

Changes

  • Remove ports section in ray-service.*.yaml
  • Remove unnecessary loop in BuildServeService when assigning service port
  • Add test for setting default port in ray service, covering
    • No ports set by user, set all default ports for them
    • Some ports set but metrics does not, the default metrics port is set

Why are these changes needed?

Currently, many sample YAML files define the ports section, while in code the default ports will be set if no ports defined.

// Assign default ports
if len(ports) == 0 {
ports = getDefaultPorts()
}

Those ports section in ray-service.*.yaml file can be removed.

Related issue number

Closes #3246

Checks

  • I've made sure the tests are passing.
  • Testing Strategy
    • Unit tests
    • Manual tests
    • This PR is not tested :(

@MortalHappiness MortalHappiness self-assigned this Apr 4, 2025
for name, defaultPort := range defaultPorts {
// ensure client port value overwrite
if name == utils.ClientPortName {
assert.Equalf(t, 12345, int(svcPorts[name]), "Expected `%v` as `%v` port value but got `%v`", 12345, name, svcPorts[name])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.Equalf(t, 12345, int(svcPorts[name]), "Expected `%v` as `%v` port value but got `%v`", 12345, name, svcPorts[name])
assert.Equal(t, 12345, int(svcPorts[name]))

The message is not very useful. Simple assertion is enough.

Comment on lines -84 to -92
ports:
- containerPort: 6379
name: gcs-server
- containerPort: 8265 # Ray dashboard
name: dashboard
- containerPort: 10001
name: client
- containerPort: 8000
name: serve
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also remove this section in other ray-service.*.yaml files.

Comment on lines 388 to 392
// Only assign defaultPort if the port is not already in use
if !usedPorts[defaultPort] {
ports[name] = defaultPort
usedPorts[defaultPort] = true // Mark this port as used
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return error instead of do nothing if ports conflict with defaultPort.

Copy link
Contributor Author

@machichima machichima Apr 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I was thinking here is that if user set the port with different name, the user's setting will be used.
For instance, when running tests, I encountered the issue that the default is:

- containerPort: 6379
  name: gcs-server

While test set:

- containerPort: 6379
  name: gcs

In this case, user's setting with name gcs will be applied.

Do we need error for this? Or probably a warning is enough? Update on below comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update:
This problem is actually caused by different naming for gcs-server (some named it as gcs)

For example:

ContainerPort: 6379,
Name: "gcs",

Default gcs-server name should be:

GcsServerPortName = "gcs-server"

I would like to confirm if it is ok to change all port name "gcs" into "gcs-server"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. We should use the same value across the codebase.

Comment on lines 161 to 180
svcPorts := make(map[string]int32)
usedPorts := make(map[int32]bool)
for _, port := range ports {
if *port.AppProtocol != utils.DefaultServiceAppProtocol {
t.Fatalf("Expected `%v` but got `%v`", expectedResult, *port.AppProtocol)
}
svcPorts[port.Name] = port.Port
usedPorts[port.Port] = true
}

for name, defaultPort := range defaultPorts {
// ensure client port value overwrite
if name == utils.ClientPortName {
assert.Equalf(t, 12345, int(svcPorts[name]), "Expected `%v` as `%v` port value but got `%v`", 12345, name, svcPorts[name])
} else if !usedPorts[defaultPort] {
// Ensure the default port value is applied
t.Fatalf("Port `%v` not set", defaultPort)
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In tests, we should simply assert the final result, and the expected outcome should be easy to construct. It's hard to read a test that loops over constructed values and makes assertions.

We may want to split the test into several test cases if the expected value is hard to construct. For example, one with duplicated ports and another without.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the advice! I moved into a new test function so that it is easier to read

@kevin85421
Copy link
Member

cc @rueian for review

ports[utils.MetricsPortName] = utils.DefaultMetricsPort
// 1. If a value is provided in the YAML file, overwrite the default value
// 2. If no port value is provided, use the default name and port
// 3. If a port value is provided in the YAML file, retain the original name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will k8s throw an error when there are duplicated port values? If not, can we just preserve them all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I encountered the following error when there's duplicate port value 6379 (for gcs-server).

Warning  FailedToCreateService  47s (x16 over 3m31s)  raycluster-controller  Failed creating service test-ns-hgtnx/raycluster-gcsft-head-svc, Service "raycluster-gcsft-head-svc" is invalid: spec.ports[3]: Duplicate value: core.ServicePort{Name:"", Protocol:"TCP", AppProtocol:(*string)(nil), Port:6379, TargetPort:intstr.IntOrString{Type:0, IntVal:0, StrVal:""}, NodePort:0}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

Comment on lines 214 to 216
svcPort := corev1.ServicePort{Name: utils.ServingPortName, Port: portsInt[utils.ServingPortName]}
// Only include serve port
ports := []corev1.ServicePort{svcPort}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we still include the svcPort if utils.ServingPortName is not in the portsInt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will always be there as we will assign default value. And now if user trying to use name other than utils.ServingPortName for serving port, the error will be raised.

This also raise error if the default port is set with name different to
default name
-e
Signed-off-by: machichima <[email protected]>
-e
Signed-off-by: machichima <[email protected]>
@machichima
Copy link
Contributor Author

machichima commented Apr 5, 2025

Hi @MortalHappiness ,
It seems like while running sampleyaml test, the BuildServiceForHeadPod will also be called to setup the service for ray head pod. I think we should also remove the ports section under ray-head container in files like ray-cluster.*.yaml .

Otherwise, the "gcs" port name need to be renamed as "gcs-server" (default port name) so that the error will not be raised.

@MortalHappiness
Copy link
Member

Hi @MortalHappiness , It seems like while running sampleyaml test, the BuildServiceForHeadPod will also be called to setup the service for ray head pod. I think we should also remove the ports section under ray-head container in files like ray-cluster.*.yaml .

Otherwise, the "gcs" port name need to be renamed as "gcs-server" (default port name) so that the error will not be raised.

Yes. They'll be removed in #3245

Comment on lines 216 to 218
for name, port := range testCase.expectResult {
assert.Equal(t, port, svcPorts[name])
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for name, port := range testCase.expectResult {
assert.Equal(t, port, svcPorts[name])
}
assert.Equal(t, testCase.expectResult, svcPorts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thank you!

ports = append(ports, svcPort)
break
}
ports := []corev1.ServicePort{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ports seems to have at most one element? If this is correct, maybe we can use var servePort corev1.ServicePort instead.

Copy link
Contributor Author

@machichima machichima Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the code below we need to assign ports to serveService.Spec.Ports, so I think it's better to keep it as slice here. I will set its capacity into 1 then.

serveService.Spec.Ports = ports

@@ -40,10 +40,3 @@ spec:
requests:
cpu: 500m
memory: 2Gi
ports:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should we update this RayCluster YAML?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, I modified this accidentally. Let me change it back.

"dashboard": "8265",
"gcs": "6379",
"metrics": "8080",
"client": "10001",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily in this PR, but would you mind using constant variables instead of hard-coded strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I will add a new PR for this as there's multiple places to modify

@@ -125,7 +125,7 @@ func headPodTemplateApplyConfiguration() *corev1ac.PodTemplateSpecApplyConfigura
WithName("ray-head").
WithImage(GetRayImage()).
WithPorts(
corev1ac.ContainerPort().WithName("gcs").WithContainerPort(6379),
corev1ac.ContainerPort().WithName("gcs-server").WithContainerPort(6379),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily in this PR, but would you mind using constant variables instead of hard-coded strings?

@@ -98,7 +98,7 @@ func headPodTemplateApplyConfiguration() *corev1ac.PodTemplateSpecApplyConfigura
WithName("ray-head").
WithImage(GetRayImage()).
WithPorts(
corev1ac.ContainerPort().WithName("gcs").WithContainerPort(6379),
corev1ac.ContainerPort().WithName("gcs-server").WithContainerPort(6379),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily in this PR, but would you mind using constant variables instead of hard-coded strings?

// BuildServiceForHeadPod should generate a headless service for a Head Pod by default.
if svc.Spec.ClusterIP != corev1.ClusterIPNone {
t.Fatalf("Expected `%v` but got `%v`", corev1.ClusterIPNone, svc.Spec.ClusterIP)
}
}

// Test if default ports is applied and can be overwritten by config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the relationship between this test and this PR? The tests seem irrelevant and don't cover all cases. Perhaps we can remove this from the PR and open another one to add tests with sufficient coverage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test ensures the default ports are set if user does not specify ports in yaml. As we are trying to remove all ports section in rayservice sample yaml file, I think this test is needed.

Copy link
Member

@kevin85421 kevin85421 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left small comments. Otherwise, LGTM.

name string
expectResult map[string]int32
ports []corev1.ContainerPort
expectError bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, expectError is false in all tests, so we can remove it for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks!

// BuildServiceForHeadPod should generate a headless service for a Head Pod by default.
if svc.Spec.ClusterIP != corev1.ClusterIPNone {
t.Fatalf("Expected `%v` but got `%v`", corev1.ClusterIPNone, svc.Spec.ClusterIP)
}
}

// Test if default ports is applied and can be overwritten by config
func TestBuildServiceForHeadPodDefaultPorts(t *testing.T) {
type testCase struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add comments here to summarize the behavior of setting ports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I added:

"Test that default ports are applied when none are specified. The metrics port is always added if not explicitly set."

@kevin85421 kevin85421 merged commit 1283a62 into ray-project:master Apr 12, 2025
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature][RayService] Set default ports for users
4 participants