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

Enable setting the resource request/limits via annotations for queue-proxy side-car container #4151

Merged
5 changes: 5 additions & 0 deletions pkg/apis/serving/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ const (
// UpdaterAnnotation is the annotation key to describe the user that
// last updated the resource.
UpdaterAnnotation = GroupName + "/lastModifier"

queueName = "queue.sidecar."

// QueueSideCarResourcePercentageAnnotation is the percentage of user container resources to be used for queue-proxy
QueueSideCarResourcePercentageAnnotation = queueName + GroupName + "/resourcePercentage"
)
36 changes: 36 additions & 0 deletions pkg/apis/serving/v1alpha1/revision_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v1alpha1
import (
"context"
"fmt"
"strconv"
"strings"

"github.com/knative/pkg/apis"
Expand Down Expand Up @@ -81,6 +82,9 @@ func (rt *RevisionTemplateSpec) Validate(ctx context.Context) *apis.FieldError {
}
}

// Validate the annotations of RevisionTemplate for sidecar resource request and limits
errs = errs.Also(validateAnnotations(rt.Annotations))

return errs
}

Expand Down Expand Up @@ -149,6 +153,38 @@ func (rs *RevisionSpec) Validate(ctx context.Context) *apis.FieldError {
return errs
}

func validateAnnotations(annotations map[string]string) *apis.FieldError {

return validatePercentageAnnotationKey(annotations, serving.QueueSideCarResourcePercentageAnnotation)
}

func validatePercentageAnnotationKey(annotations map[string]string, resourcePercentageAnnotationKey string) *apis.FieldError {
if len(annotations) == 0 {
return nil
}

v, ok := annotations[resourcePercentageAnnotationKey]
if !ok {
return nil
}
value, err := strconv.ParseFloat(v, 32)
if err != nil {
return &apis.FieldError{
Message: fmt.Sprintf("Invalid value %s for annotation %s: %v", v, resourcePercentageAnnotationKey, err),
Paths: []string{resourcePercentageAnnotationKey},
}
}

if value <= 0 || value > 1 {
return &apis.FieldError{
Message: fmt.Sprintf("%s=%v should be in the range (0,1)", resourcePercentageAnnotationKey, value),
Paths: []string{resourcePercentageAnnotationKey},
}
}

return nil
}

func validateTimeoutSeconds(timeoutSeconds int64) *apis.FieldError {
if timeoutSeconds != 0 {
if timeoutSeconds > int64(networking.DefaultTimeout.Seconds()) || timeoutSeconds < 0 {
Expand Down
37 changes: 37 additions & 0 deletions pkg/apis/serving/v1alpha1/revision_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/knative/pkg/ptr"
"github.com/knative/serving/pkg/apis/autoscaling"
net "github.com/knative/serving/pkg/apis/networking"
"github.com/knative/serving/pkg/apis/serving"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -299,6 +300,42 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
},
},
want: nil,
}, {
name: "Queue sidecar resource percentage annotation more than 1",
rts: &RevisionTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
serving.QueueSideCarResourcePercentageAnnotation: "3",
},
},
Spec: RevisionSpec{
DeprecatedContainer: &corev1.Container{
Image: "helloworld",
},
},
},
want: &apis.FieldError{
Message: "queue.serving.knative.dev/resourcePercentage=3 should be in the range (0,1)",
Paths: []string{serving.QueueSideCarResourcePercentageAnnotation},
},
}, {
name: "Invalid queue sidecar resource percentage annotation",
rts: &RevisionTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
serving.QueueSideCarResourcePercentageAnnotation: "50mx",
},
},
Spec: RevisionSpec{
DeprecatedContainer: &corev1.Container{
Image: "helloworld",
},
},
},
want: &apis.FieldError{
Message: "Invalid value 50mx for annotation queue.sidecar.serving.knative.dev/resourcePercentage: strconv.ParseFloat: parsing \"50mx\": invalid syntax",
Paths: []string{serving.QueueSideCarResourcePercentageAnnotation},
},
}}

for _, test := range tests {
Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/revision/resources/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ var (

defaultQueueContainer = &corev1.Container{
Name: QueueContainerName,
Resources: queueResources,
Resources: getResources(make(map[string]string), &corev1.Container{}),
Ports: append(queueNonServingPorts, queueHTTPPort),
ReadinessProbe: queueReadinessProbe,
Env: []corev1.EnvVar{{
Expand Down
89 changes: 82 additions & 7 deletions pkg/reconciler/revision/resources/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ limitations under the License.
package resources

import (
"fmt"
"math"
"strconv"

"k8s.io/apimachinery/pkg/api/resource"

"k8s.io/apimachinery/pkg/util/intstr"

"github.com/knative/pkg/logging"
Expand All @@ -37,12 +41,6 @@ import (
const requestQueueHTTPPortName = "queue-port"

var (
queueResources = corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceName("cpu"): queueContainerCPU,
},
}

queueHTTPPort = corev1.ContainerPort{
Name: requestQueueHTTPPortName,
ContainerPort: int32(networking.BackendHTTPPort),
Expand Down Expand Up @@ -82,6 +80,83 @@ var (
}
)

func getResources(annotations map[string]string, userContainer *corev1.Container) corev1.ResourceRequirements {

resources := corev1.ResourceRequirements{}
resourceRequests := corev1.ResourceList{corev1.ResourceCPU: queueContainerCPU}
resourceLimits := corev1.ResourceList{}
ok := false
var requestCPU, limitCPU, requestMemory, limitMemory resource.Quantity
var resourcePercentage float32

if ok, resourcePercentage = getResourcePercentageFromAnnotations(annotations, serving.QueueSideCarResourcePercentageAnnotation); ok {

if ok, requestCPU = getUserContainerResourceRequirements(userContainer.Resources.Requests.Cpu(), resourcePercentage, queueContainerRequestCPU); ok {
resourceRequests[corev1.ResourceCPU] = requestCPU
}

if ok, limitCPU = getUserContainerResourceRequirements(userContainer.Resources.Limits.Cpu(), resourcePercentage, queueContainerLimitCPU); ok {
resourceLimits[corev1.ResourceCPU] = limitCPU
}

if ok, requestMemory = getUserContainerResourceRequirements(userContainer.Resources.Requests.Memory(), resourcePercentage, queueContainerRequestMemory); ok {
resourceRequests[corev1.ResourceMemory] = requestMemory
}

if ok, limitMemory = getUserContainerResourceRequirements(userContainer.Resources.Limits.Memory(), resourcePercentage, queueContainerLimitMemory); ok {
resourceLimits[corev1.ResourceMemory] = limitMemory
}

}

resources.Requests = resourceRequests

if len(resourceLimits) != 0 {
resources.Limits = resourceLimits
}

return resources
}

func getUserContainerResourceRequirements(resourceQuantity *resource.Quantity, percentage float32, boundary ResourceBoundary) (bool, resource.Quantity) {
if !resourceQuantity.IsZero() {
absoluteValue := resourceQuantity.Value()
absoluteMilliValue := absoluteValue
if absoluteValue < (math.MaxInt64 / 1000) {
absoluteMilliValue = resourceQuantity.MilliValue()
}

fmt.Printf("Actual %v\n", resourceQuantity)
fmt.Printf("Actual value %v\n", resourceQuantity.Value())
fmt.Printf("Actual milli value %v\n", resourceQuantity.MilliValue())

newValue := int64(float64(absoluteMilliValue) * float64(percentage))
fmt.Printf("Percentage Value %v\n", newValue)

newquantity := *resource.NewMilliQuantity(newValue, resource.BinarySI)
if newquantity.Cmp(boundary.min) == -1 {
newquantity = boundary.min
} else if newquantity.Cmp(boundary.max) == 1 {
newquantity = boundary.max
}
fmt.Printf("%v\n", newquantity)
return true, newquantity
}
return false, resource.Quantity{}
}

func getResourcePercentageFromAnnotations(m map[string]string, k string) (bool, float32) {
v, ok := m[k]
if !ok {
return false, 0
}
value, err := strconv.ParseFloat(v, 32)
if err != nil {
return false, 0
}
return true, float32(value)
}

// makeQueueContainer creates the container spec for the queue sidecar.
func makeQueueContainer(rev *v1alpha1.Revision, loggingConfig *logging.Config, observabilityConfig *metrics.ObservabilityConfig,
autoscalerConfig *autoscaler.Config, deploymentConfig *deployment.Config) *corev1.Container {
Expand Down Expand Up @@ -115,7 +190,7 @@ func makeQueueContainer(rev *v1alpha1.Revision, loggingConfig *logging.Config, o
return &corev1.Container{
Name: QueueContainerName,
Image: deploymentConfig.QueueSidecarImage,
Resources: queueResources,
Resources: getResources(rev.GetAnnotations(), rev.Spec.GetContainer()),
Ports: ports,
ReadinessProbe: queueReadinessProbe,
Env: []corev1.EnvVar{{
Expand Down
Loading