github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/resources/deployments/elasticsearch.go (about) 1 // Copyright (C) 2020, 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package deployments 5 6 import ( 7 "fmt" 8 9 vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1" 10 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/config" 11 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants" 12 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources" 13 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources/nodes" 14 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/util/memory" 15 16 "go.uber.org/zap" 17 appsv1 "k8s.io/api/apps/v1" 18 corev1 "k8s.io/api/core/v1" 19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 ) 21 22 // ElasticsearchBasic function type 23 type ElasticsearchBasic struct { 24 } 25 26 // IsOpenSearchDataDeployment checks template label to see if a given deployment is an OpenSearch data deployment 27 func IsOpenSearchDataDeployment(vmoName string, deployment *appsv1.Deployment) bool { 28 return deployment.Spec.Template.Labels[constants.ServiceAppLabel] == vmoName+"-"+config.ElasticsearchData.Name 29 } 30 31 // Returns a common base deployment structure for all Elasticsearch components 32 func (es ElasticsearchBasic) createCommonDeployment(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, node vmcontrollerv1.ElasticsearchNode, componentDetails config.ComponentDetails, index int) *appsv1.Deployment { 33 34 deploymentElement := createDeploymentElementByPvcIndex(vmo, node.Storage, &node.Resources, componentDetails, index, node.Name) 35 esContainer := &deploymentElement.Spec.Template.Spec.Containers[0] 36 esContainer.Env = append(esContainer.Env, 37 corev1.EnvVar{ 38 Name: "NAMESPACE", 39 ValueFrom: &corev1.EnvVarSource{ 40 FieldRef: &corev1.ObjectFieldSelector{ 41 FieldPath: "metadata.namespace", 42 }, 43 }, 44 }, 45 corev1.EnvVar{ 46 Name: "node.name", 47 ValueFrom: &corev1.EnvVarSource{ 48 FieldRef: &corev1.ObjectFieldSelector{ 49 FieldPath: "metadata.name", 50 }, 51 }, 52 }, 53 corev1.EnvVar{Name: "cluster.name", Value: vmo.Name}, 54 corev1.EnvVar{Name: "logger.org.opensearch", Value: "info"}, 55 ) 56 57 esContainer.Ports = []corev1.ContainerPort{ 58 {Name: "http", ContainerPort: int32(constants.OSHTTPPort)}, 59 {Name: "transport", ContainerPort: int32(constants.OSTransportPort)}, 60 } 61 62 // Common Elasticsearch readiness and liveness settings 63 if esContainer.LivenessProbe != nil { 64 esContainer.LivenessProbe.InitialDelaySeconds = 60 65 esContainer.LivenessProbe.TimeoutSeconds = 3 66 esContainer.LivenessProbe.PeriodSeconds = 20 67 esContainer.LivenessProbe.FailureThreshold = 5 68 } 69 if esContainer.ReadinessProbe != nil { 70 esContainer.ReadinessProbe.InitialDelaySeconds = 60 71 esContainer.ReadinessProbe.TimeoutSeconds = 3 72 esContainer.ReadinessProbe.PeriodSeconds = 10 73 esContainer.ReadinessProbe.FailureThreshold = 10 74 } 75 76 // Add init containers 77 deploymentElement.Spec.Template.Spec.InitContainers = append(deploymentElement.Spec.Template.Spec.InitContainers, *resources.GetElasticsearchInitContainer()) 78 79 // Add node labels 80 deploymentElement.Spec.Selector.MatchLabels[constants.NodeGroupLabel] = node.Name 81 deploymentElement.Spec.Template.Labels[constants.NodeGroupLabel] = node.Name 82 nodes.SetNodeRoleLabels(&node, deploymentElement.Labels) 83 nodes.SetNodeRoleLabels(&node, deploymentElement.Spec.Template.Labels) 84 85 var elasticsearchUID int64 = 1000 86 esContainer.SecurityContext.RunAsUser = &elasticsearchUID 87 return deploymentElement 88 } 89 90 // Creates all Elasticsearch Client deployment elements 91 func (es ElasticsearchBasic) createElasticsearchIngestDeploymentElements(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []*appsv1.Deployment { 92 var deployments []*appsv1.Deployment 93 nodeList := nodes.IngestNodes(vmo) 94 for i, node := range nodeList { 95 if node.Replicas < 1 { 96 continue 97 } 98 // Default JVM heap settings if none provided 99 javaOpts, err := memory.PodMemToJvmHeapArgs(node.Resources.RequestMemory, constants.DefaultESIngestMemArgs) 100 if err != nil { 101 javaOpts = constants.DefaultESIngestMemArgs 102 zap.S().Errorf("Failed to derive heap sizes from IngestNodes pod, using default %s: %v", javaOpts, err) 103 } 104 if node.JavaOpts != "" { 105 javaOpts = node.JavaOpts 106 } 107 108 ingestDeployment := es.createCommonDeployment(vmo, node, config.ElasticsearchIngest, -1) 109 ingestDeployment.Spec.Replicas = resources.NewVal(node.Replicas) 110 111 // Anti-affinity on other client zones 112 ingestDeployment.Spec.Template.Spec.Affinity = resources.CreateZoneAntiAffinityElement(vmo.Name, config.ElasticsearchIngest.Name) 113 ingestDeployment.Spec.Template.Spec.Containers[0].Env = append(ingestDeployment.Spec.Template.Spec.Containers[0].Env, 114 corev1.EnvVar{Name: "discovery.seed_hosts", Value: resources.GetMetaName(vmo.Name, config.ElasticsearchMaster.Name)}, 115 corev1.EnvVar{Name: "NETWORK_HOST", Value: "0.0.0.0"}, 116 corev1.EnvVar{Name: "node.roles", Value: nodes.GetRolesString(&nodeList[i])}, 117 corev1.EnvVar{Name: "OPENSEARCH_JAVA_OPTS", Value: javaOpts}, 118 ) 119 // add the required istio annotations to allow inter-es component communication 120 if ingestDeployment.Spec.Template.Annotations == nil { 121 ingestDeployment.Spec.Template.Annotations = make(map[string]string) 122 } 123 // Adding command to install OS plugins at pod bootup 124 ingestDeployment.Spec.Template.Spec.Containers[0].Command = []string{ 125 "sh", 126 "-c", 127 fmt.Sprintf(resources.OpenSearchIngestCmdTmpl, resources.GetOSPluginsInstallTmpl(resources.GetOpenSearchPluginList(vmo), resources.OSPluginsInstallCmd)), 128 } 129 ingestDeployment.Spec.Template.Annotations["traffic.sidecar.istio.io/excludeInboundPorts"] = fmt.Sprintf("%d", constants.OSTransportPort) 130 ingestDeployment.Spec.Template.Annotations["traffic.sidecar.istio.io/excludeOutboundPorts"] = fmt.Sprintf("%d", constants.OSTransportPort) 131 deployments = append(deployments, ingestDeployment) 132 } 133 return deployments 134 } 135 136 // Creates all Elasticsearch DataNodes deployment elements 137 func (es ElasticsearchBasic) createElasticsearchDataDeploymentElements(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, pvcToAdMap map[string]string) []*appsv1.Deployment { 138 var deployments []*appsv1.Deployment 139 nodeList := nodes.DataNodes(vmo) 140 for idx, node := range nodeList { 141 if node.Replicas < 1 { 142 continue 143 } 144 // Default JVM heap settings if none provided 145 javaOpts, err := memory.PodMemToJvmHeapArgs(node.Resources.RequestMemory, constants.DefaultESDataMemArgs) 146 if err != nil { 147 javaOpts = constants.DefaultESDataMemArgs 148 zap.S().Errorf("Failed to derive heap sizes from DataNodes pod, using default %s: %v", javaOpts, err) 149 } 150 if node.JavaOpts != "" { 151 javaOpts = node.JavaOpts 152 } 153 for i := 0; i < int(node.Replicas); i++ { 154 dataDeployment := es.createCommonDeployment(vmo, node, config.ElasticsearchData, i) 155 156 dataDeployment.Spec.Replicas = resources.NewVal(1) 157 availabilityDomain := getAvailabilityDomainForPvcIndex(node.Storage, pvcToAdMap, i) 158 if availabilityDomain == "" { 159 // With shard allocation awareness, we must provide something for the AD, even in the case of the simple 160 // VMO with no persistence volumes 161 availabilityDomain = "None" 162 } 163 164 // Anti-affinity on other data pod *nodes* (try out best to spread across many nodes) 165 dataDeployment.Spec.Template.Spec.Affinity = &corev1.Affinity{ 166 PodAntiAffinity: &corev1.PodAntiAffinity{ 167 PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ 168 { 169 Weight: 100, 170 PodAffinityTerm: corev1.PodAffinityTerm{ 171 LabelSelector: &metav1.LabelSelector{ 172 MatchLabels: resources.GetSpecID(vmo.Name, config.ElasticsearchData.Name), 173 }, 174 TopologyKey: "kubernetes.io/hostname", 175 }, 176 }, 177 }, 178 }, 179 } 180 // When the deployment does not have a pod security context with an FSGroup attribute, any mounted volumes are 181 // initially owned by root/root. Previous versions of the ES image were run as "root", and chown'd the mounted 182 // directory to "elasticsearch", but we don't want to run as "root". The current ES image creates a group 183 // "elasticsearch" (GID 1000), and a user "elasticsearch" (UID 1000) in that group. When we provide FSGroup = 184 // 1000 below, the volume is owned by root/elasticsearch, with permissions "rwxrwsr-x". This allows the ES 185 // image to run as UID 1000, and have sufficient permissions to write to the mounted volume. 186 elasticsearchGid := int64(1000) 187 dataDeployment.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{ 188 FSGroup: &elasticsearchGid, 189 } 190 191 dataDeployment.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType 192 dataDeployment.Spec.Strategy.RollingUpdate = nil 193 dataDeployment.Spec.Template.Spec.Containers[0].Env = append(dataDeployment.Spec.Template.Spec.Containers[0].Env, 194 corev1.EnvVar{Name: "discovery.seed_hosts", Value: resources.GetMetaName(vmo.Name, config.ElasticsearchMaster.Name)}, 195 corev1.EnvVar{Name: "node.attr.availability_domain", Value: availabilityDomain}, 196 corev1.EnvVar{Name: "node.roles", Value: nodes.GetRolesString(&nodeList[idx])}, 197 corev1.EnvVar{Name: "OPENSEARCH_JAVA_OPTS", Value: javaOpts}, 198 corev1.EnvVar{Name: constants.ObjectStoreAccessKeyVarName, 199 ValueFrom: &corev1.EnvVarSource{ 200 SecretKeyRef: &corev1.SecretKeySelector{ 201 LocalObjectReference: corev1.LocalObjectReference{ 202 Name: constants.VerrazzanoBackupScrtName, 203 }, 204 Key: constants.ObjectStoreAccessKey, 205 Optional: func(opt bool) *bool { 206 return &opt 207 }(true), 208 }, 209 }, 210 }, 211 corev1.EnvVar{Name: constants.ObjectStoreCustomerKeyVarName, 212 ValueFrom: &corev1.EnvVarSource{ 213 SecretKeyRef: &corev1.SecretKeySelector{ 214 LocalObjectReference: corev1.LocalObjectReference{ 215 Name: constants.VerrazzanoBackupScrtName, 216 }, 217 Key: constants.ObjectStoreCustomerKey, 218 Optional: func(opt bool) *bool { 219 return &opt 220 }(true), 221 }, 222 }, 223 }, 224 ) 225 226 // Adding command for add keystore values and OS plugins installation at pod bootup 227 dataDeployment.Spec.Template.Spec.Containers[0].Command = []string{ 228 "sh", 229 "-c", 230 resources.CreateOpenSearchContainerCMD(javaOpts, resources.GetOpenSearchPluginList(vmo)), 231 } 232 233 // add the required istio annotations to allow inter-es component communication 234 if dataDeployment.Spec.Template.Annotations == nil { 235 dataDeployment.Spec.Template.Annotations = make(map[string]string) 236 } 237 dataDeployment.Spec.Template.Annotations["traffic.sidecar.istio.io/excludeInboundPorts"] = fmt.Sprintf("%d", constants.OSTransportPort) 238 dataDeployment.Spec.Template.Annotations["traffic.sidecar.istio.io/excludeOutboundPorts"] = fmt.Sprintf("%d", constants.OSTransportPort) 239 deployments = append(deployments, dataDeployment) 240 } 241 } 242 return deployments 243 } 244 245 // Creates *all* Elasticsearch deployment elements 246 func (es ElasticsearchBasic) createElasticsearchDeploymentElements(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, pvcToAdMap map[string]string) []*appsv1.Deployment { 247 var deployList []*appsv1.Deployment 248 deployList = append(deployList, es.createElasticsearchIngestDeploymentElements(vmo)...) 249 deployList = append(deployList, es.createElasticsearchDataDeploymentElements(vmo, pvcToAdMap)...) 250 return deployList 251 }