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

Issue 130: Ability to specify container resources #129

Merged
merged 19 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions example/cr-detailed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ spec:
pullPolicy: IfNotPresent

replicas: 3
resources:

Choose a reason for hiding this comment

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

Is there any test that validates that these values are properly passed to the process?

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, we will work on adding a test to validate that resources are passed down to pod templates correctly.

Copy link
Member

Choose a reason for hiding this comment

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

Test added. Could you please review it again? Thanks!

requests:
memory: "3Gi"
cpu: "1000m"
limits:
memory: "5Gi"
cpu: "2000m"

storage:
ledgerVolumeClaimTemplate:
Expand Down Expand Up @@ -51,7 +58,22 @@ spec:

pravega:
controllerReplicas: 1
controllerResources:
requests:
memory: "1Gi"
cpu: "1000m"
limits:
memory: "3Gi"
cpu: "2000m"

segmentStoreReplicas: 3
segmentStoreResources:
requests:
memory: "3Gi"
cpu: "1000m"
limits:
memory: "5Gi"
cpu: "2000m"

# Turn on Pravega Debug Logging
debugLogging: false
Expand Down
19 changes: 19 additions & 0 deletions pkg/apis/pravega/v1alpha1/bookkeeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ type BookkeeperSpec struct {
// ServiceAccountName configures the service account used on BookKeeper instances
ServiceAccountName string `json:"serviceAccountName,omitempty"`

// BookieResources specifies the request and limit of resources that bookie can have.
// BookieResources includes CPU and memory resources
Resources *v1.ResourceRequirements `json:"resources,omitempty"`

// Options is the Bookkeeper configuration that is to override the bk_server.conf
// in bookkeeper. Some examples can be found here
// https://github.com/apache/bookkeeper/blob/master/docker/README.md
Expand Down Expand Up @@ -102,9 +106,24 @@ func (s *BookkeeperSpec) withDefaults() (changed bool) {
s.AutoRecovery = &boolTrue
}

if s.Resources == nil {
changed = true
s.Resources = &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("500m"),

Choose a reason for hiding this comment

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

Why these values are lower than the ones in cr-detailed.yaml? Shall we stick to a consistent defaults?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The default values are meant to represent the bare minimum requirements for Pravega to run. These values are intended to be use in a first contact with Pravega, functional testing, or development work. Therefore, we want to keep them low so that Pravega can be deployed to small Kubernetes clusters. Whereas the values in cr-detailed.yaml are just an example of how the default resources would be overridden to tune Pravega resources to a higher performance cluster.

v1.ResourceMemory: resource.MustParse("1Gi"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("2Gi"),
},
}
}

if s.Options == nil {
s.Options = map[string]string{}
}

return changed
}

Expand Down
37 changes: 37 additions & 0 deletions pkg/apis/pravega/v1alpha1/pravega.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ type PravegaSpec struct {
// SegmentStoreServiceAccountName configures the service account used on segment store instances.
// If not specified, Kubernetes will automatically assign the default service account in the namespace
SegmentStoreServiceAccountName string `json:"segmentStoreServiceAccountName,omitempty"`

// ControllerResources specifies the request and limit of resources that controller can have.
// ControllerResources includes CPU and memory resources
ControllerResources *v1.ResourceRequirements `json:"controllerResources,omitempty"`

// SegmentStoreResources specifies the request and limit of resources that segmentStore can have.
// SegmentStoreResources includes CPU and memory resources
SegmentStoreResources *v1.ResourceRequirements `json:"segmentStoreResources,omitempty"`
}

func (s *PravegaSpec) withDefaults() (changed bool) {
Expand Down Expand Up @@ -129,10 +137,39 @@ func (s *PravegaSpec) withDefaults() (changed bool) {
changed = true
s.Tier2 = &Tier2Spec{}
}

if s.Tier2.withDefaults() {
changed = true
}

if s.ControllerResources == nil {
changed = true
s.ControllerResources = &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("250m"),

Choose a reason for hiding this comment

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

Same comment here with the defaults compared to cr-detailed.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.

Responded in #129 (comment)

v1.ResourceMemory: resource.MustParse("512Mi"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("500m"),
v1.ResourceMemory: resource.MustParse("1Gi"),
},
}
}

if s.SegmentStoreResources == nil {
changed = true
s.SegmentStoreResources = &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("500m"),
v1.ResourceMemory: resource.MustParse("1Gi"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("2Gi"),
},
}
}

return changed
}

Expand Down
20 changes: 20 additions & 0 deletions pkg/apis/pravega/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 23 additions & 12 deletions pkg/controller/pravega/bookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ package pravega

import (
"fmt"
"strings"

"github.com/pravega/pravega-operator/pkg/apis/pravega/v1alpha1"
"github.com/pravega/pravega-operator/pkg/util"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -122,16 +122,7 @@ func makeBookiePodSpec(clusterName string, bookkeeperSpec *v1alpha1.BookkeeperSp
MountPath: "/bk/index",
},
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1000m"),
corev1.ResourceMemory: resource.MustParse("3Gi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2000m"),
corev1.ResourceMemory: resource.MustParse("5Gi"),
},
},
Resources: *bookkeeperSpec.Resources,
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
Exec: &corev1.ExecAction{
Expand Down Expand Up @@ -193,11 +184,31 @@ func makeBookieVolumeClaimTemplates(spec *v1alpha1.BookkeeperSpec) []corev1.Pers
}

func MakeBookieConfigMap(pravegaCluster *v1alpha1.PravegaCluster) *corev1.ConfigMap {
javaOpts := []string{
"-Xms1g",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+UseCGroupMemoryLimitForHeap",
"-XX:MaxRAMFraction=1",
"-XX:MaxDirectMemorySize=1g",
"-XX:+UseG1GC",
"-XX:MaxGCPauseMillis=10",
"-XX:+ParallelRefProcEnabled",
"-XX:+AggressiveOpts",
"-XX:+DoEscapeAnalysis",
"-XX:ParallelGCThreads=32",
"-XX:ConcGCThreads=32",
"-XX:G1NewSizePercent=50",
"-XX:+DisableExplicitGC",
"-XX:-ResizePLAB",
}

configData := map[string]string{
"BK_BOOKIE_EXTRA_OPTS": "-Xms1g -Xmx4g -XX:MaxDirectMemorySize=1g -XX:+UseG1GC -XX:MaxGCPauseMillis=10 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+AggressiveOpts -XX:+DoEscapeAnalysis -XX:ParallelGCThreads=32 -XX:ConcGCThreads=32 -XX:G1NewSizePercent=50 -XX:+DisableExplicitGC -XX:-ResizePLAB",
"BK_BOOKIE_EXTRA_OPTS": strings.Join(javaOpts, " "),
"ZK_URL": pravegaCluster.Spec.ZookeeperUri,
// Set useHostNameAsBookieID to false until BookKeeper Docker
// image is updated to 4.7
// This value can be explicitly overridden when using the operator
// with images based on BookKeeper 4.7 or newer
"BK_useHostNameAsBookieID": "false",
"PRAVEGA_CLUSTER_NAME": pravegaCluster.ObjectMeta.Name,
"WAIT_FOR": pravegaCluster.Spec.ZookeeperUri,
Expand Down
21 changes: 5 additions & 16 deletions pkg/controller/pravega/pravega_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -79,16 +78,7 @@ func makeControllerPodSpec(name string, pravegaSpec *api.PravegaSpec) *corev1.Po
},
},
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1000m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2000m"),
corev1.ResourceMemory: resource.MustParse("3Gi"),
},
},
Resources: *pravegaSpec.ControllerResources,
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
Exec: &corev1.ExecAction{
Expand Down Expand Up @@ -127,7 +117,10 @@ func makeControllerPodSpec(name string, pravegaSpec *api.PravegaSpec) *corev1.Po

func MakeControllerConfigMap(p *api.PravegaCluster) *corev1.ConfigMap {
var javaOpts = []string{
"-Xms1g -Xmx2g -Dpravegaservice.clusterName=" + p.Name,
"-Xms1g",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+UseCGroupMemoryLimitForHeap",
"-Dpravegaservice.clusterName=" + p.Name,
}

for name, value := range p.Spec.Pravega.Options {
Expand All @@ -147,10 +140,6 @@ func MakeControllerConfigMap(p *api.PravegaCluster) *corev1.ConfigMap {
"WAIT_FOR": p.Spec.ZookeeperUri,
}

for name, value := range p.Spec.Pravega.Options {
configData[name] = value
}

configMap := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
Expand Down
48 changes: 21 additions & 27 deletions pkg/controller/pravega/pravega_segmentstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -99,16 +98,7 @@ func makeSegmentstorePodSpec(pravegaCluster *api.PravegaCluster) corev1.PodSpec
MountPath: cacheVolumeMountPoint,
},
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1000m"),
corev1.ResourceMemory: resource.MustParse("3Gi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2000m"),
corev1.ResourceMemory: resource.MustParse("5Gi"),
},
},
Resources: *pravegaSpec.SegmentStoreResources,
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
Exec: &corev1.ExecAction{
Expand Down Expand Up @@ -151,44 +141,48 @@ func makeSegmentstorePodSpec(pravegaCluster *api.PravegaCluster) corev1.PodSpec
return podSpec
}

func MakeSegmentstoreConfigMap(pravegaCluster *api.PravegaCluster) *corev1.ConfigMap {
func MakeSegmentstoreConfigMap(p *api.PravegaCluster) *corev1.ConfigMap {
javaOpts := []string{
"-Xms1g -Xmx4g -XX:MaxDirectMemorySize=1g -Dpravegaservice.clusterName=" + pravegaCluster.Name,
"-Xms1g",
"-XX:+UnlockExperimentalVMOptions",

Choose a reason for hiding this comment

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

Are these options configurable as part of another PR, right?

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, JVM options are not externally configurable.

Copy link

@RaulGracia RaulGracia Mar 21, 2019

Choose a reason for hiding this comment

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

Ok, this may not be great, because we will rely on the default -Xmx value set by the JVM implementation. The default may be set as follows:

Often its default value is 1/4th of your physical memory or 1GB (whichever is smaller).

So, we may end up providing 4GBs of memory to a pod, but 1GB at most will be devoted to the JVM (despite the main purpose of that pod is to run the JVM for the Pravega process at hand). What is your opinion about this? Have you used -XX:+PrintFlagsFinal to see what is the actual -Xmx value that is being used? Would it make sense to set the -Xmx value in function to the provisioned memory resources for the pod?

"-XX:+UseCGroupMemoryLimitForHeap",
"-XX:MaxRAMFraction=1",
"-Dpravegaservice.clusterName=" + p.Name,
}

for name, value := range pravegaCluster.Spec.Pravega.Options {
for name, value := range p.Spec.Pravega.Options {
javaOpts = append(javaOpts, fmt.Sprintf("-D%v=%v", name, value))
}

configData := map[string]string{
"AUTHORIZATION_ENABLED": "false",
"CLUSTER_NAME": pravegaCluster.Name,
"ZK_URL": pravegaCluster.Spec.ZookeeperUri,
"CLUSTER_NAME": p.Name,
"ZK_URL": p.Spec.ZookeeperUri,
"JAVA_OPTS": strings.Join(javaOpts, " "),
"CONTROLLER_URL": util.PravegaControllerServiceURL(*pravegaCluster),
"CONTROLLER_URL": util.PravegaControllerServiceURL(*p),
}

// Wait for at least 3 Bookies to come up
var waitFor []string
for i := int32(0); i < util.Min(3, pravegaCluster.Spec.Bookkeeper.Replicas); i++ {
for i := int32(0); i < util.Min(3, p.Spec.Bookkeeper.Replicas); i++ {
waitFor = append(waitFor,
fmt.Sprintf("%s-%d.%s.%s:3181",
util.StatefulSetNameForBookie(pravegaCluster.Name),
util.StatefulSetNameForBookie(p.Name),
i,
util.HeadlessServiceNameForBookie(pravegaCluster.Name),
pravegaCluster.Namespace))
util.HeadlessServiceNameForBookie(p.Name),
p.Namespace))
}
configData["WAIT_FOR"] = strings.Join(waitFor, ",")

if pravegaCluster.Spec.ExternalAccess.Enabled {
if p.Spec.ExternalAccess.Enabled {
configData["K8_EXTERNAL_ACCESS"] = "true"
}

if pravegaCluster.Spec.Pravega.DebugLogging {
if p.Spec.Pravega.DebugLogging {
configData["log.level"] = "DEBUG"
}

for k, v := range getTier2StorageOptions(pravegaCluster.Spec.Pravega) {
for k, v := range getTier2StorageOptions(p.Spec.Pravega) {
configData[k] = v
}

Expand All @@ -198,9 +192,9 @@ func MakeSegmentstoreConfigMap(pravegaCluster *api.PravegaCluster) *corev1.Confi
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: util.ConfigMapNameForSegmentstore(pravegaCluster.Name),
Namespace: pravegaCluster.Namespace,
Labels: util.LabelsForSegmentStore(pravegaCluster),
Name: util.ConfigMapNameForSegmentstore(p.Name),
Namespace: p.Namespace,
Labels: util.LabelsForSegmentStore(p),
},
Data: configData,
}
Expand Down