github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/pxc/app/statefulset/proxysql.go (about) 1 package statefulset 2 3 import ( 4 "context" 5 6 appsv1 "k8s.io/api/apps/v1" 7 corev1 "k8s.io/api/core/v1" 8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 "k8s.io/apimachinery/pkg/types" 10 "sigs.k8s.io/controller-runtime/pkg/client" 11 12 api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1" 13 app "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/app" 14 "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/users" 15 "github.com/pkg/errors" 16 ) 17 18 const ( 19 proxyName = "proxysql" 20 proxyDataVolumeName = "proxydata" 21 proxyConfigVolumeName = "config" 22 ) 23 24 type Proxy struct { 25 sfs *appsv1.StatefulSet 26 labels map[string]string 27 service string 28 } 29 30 func NewProxy(cr *api.PerconaXtraDBCluster) *Proxy { 31 sfs := &appsv1.StatefulSet{ 32 TypeMeta: metav1.TypeMeta{ 33 APIVersion: "apps/v1", 34 Kind: "StatefulSet", 35 }, 36 ObjectMeta: metav1.ObjectMeta{ 37 Name: cr.Name + "-" + proxyName, 38 Namespace: cr.Namespace, 39 }, 40 } 41 42 labels := map[string]string{ 43 "app.kubernetes.io/name": "percona-xtradb-cluster", 44 "app.kubernetes.io/instance": cr.Name, 45 "app.kubernetes.io/component": proxyName, 46 "app.kubernetes.io/managed-by": "percona-xtradb-cluster-operator", 47 "app.kubernetes.io/part-of": "percona-xtradb-cluster", 48 } 49 50 return &Proxy{ 51 sfs: sfs, 52 labels: labels, 53 service: cr.Name + "-proxysql-unready", 54 } 55 } 56 57 func (c *Proxy) Name() string { 58 return proxyName 59 } 60 61 func (c *Proxy) AppContainer(spec *api.PodSpec, secrets string, cr *api.PerconaXtraDBCluster, 62 availableVolumes []corev1.Volume, 63 ) (corev1.Container, error) { 64 appc := corev1.Container{ 65 Name: proxyName, 66 Image: spec.Image, 67 ImagePullPolicy: spec.ImagePullPolicy, 68 Ports: []corev1.ContainerPort{ 69 { 70 ContainerPort: 3306, 71 Name: "mysql", 72 }, 73 { 74 ContainerPort: 6032, 75 Name: "proxyadm", 76 }, 77 }, 78 VolumeMounts: []corev1.VolumeMount{ 79 { 80 Name: proxyDataVolumeName, 81 MountPath: "/var/lib/proxysql", 82 }, 83 { 84 Name: "ssl", 85 MountPath: "/etc/proxysql/ssl", 86 }, 87 { 88 Name: "ssl-internal", 89 MountPath: "/etc/proxysql/ssl-internal", 90 }, 91 }, 92 Env: []corev1.EnvVar{ 93 { 94 Name: "PXC_SERVICE", 95 Value: c.labels["app.kubernetes.io/instance"] + "-pxc", 96 }, 97 { 98 Name: "MYSQL_ROOT_PASSWORD", 99 ValueFrom: &corev1.EnvVarSource{ 100 SecretKeyRef: app.SecretKeySelector(secrets, users.Root), 101 }, 102 }, 103 { 104 Name: "PROXY_ADMIN_USER", 105 Value: users.ProxyAdmin, 106 }, 107 { 108 Name: "PROXY_ADMIN_PASSWORD", 109 ValueFrom: &corev1.EnvVarSource{ 110 SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin), 111 }, 112 }, 113 { 114 Name: "MONITOR_PASSWORD", 115 ValueFrom: &corev1.EnvVarSource{ 116 SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor), 117 }, 118 }, 119 }, 120 SecurityContext: spec.ContainerSecurityContext, 121 Resources: spec.Resources, 122 } 123 if cr.CompareVersionWith("1.9.0") >= 0 { 124 fvar := true 125 appc.EnvFrom = []corev1.EnvFromSource{ 126 { 127 SecretRef: &corev1.SecretEnvSource{ 128 LocalObjectReference: corev1.LocalObjectReference{ 129 Name: cr.Spec.ProxySQL.EnvVarsSecretName, 130 }, 131 Optional: &fvar, 132 }, 133 }, 134 } 135 136 } 137 138 if api.ContainsVolume(availableVolumes, proxyConfigVolumeName) { 139 appc.VolumeMounts = append(appc.VolumeMounts, corev1.VolumeMount{ 140 Name: proxyConfigVolumeName, 141 MountPath: "/etc/proxysql/", 142 }) 143 } 144 145 if cr.CompareVersionWith("1.5.0") >= 0 { 146 appc.Env[1] = corev1.EnvVar{ 147 Name: "OPERATOR_PASSWORD", 148 ValueFrom: &corev1.EnvVarSource{ 149 SecretKeyRef: app.SecretKeySelector(secrets, users.Operator), 150 }, 151 } 152 } 153 154 if cr.CompareVersionWith("1.11.0") >= 0 && cr.Spec.ProxySQL != nil && cr.Spec.ProxySQL.HookScript != "" { 155 appc.VolumeMounts = append(appc.VolumeMounts, corev1.VolumeMount{ 156 Name: "hookscript", 157 MountPath: "/opt/percona/hookscript", 158 }) 159 } 160 161 if cr.Spec.ProxySQL != nil && (cr.Spec.ProxySQL.Lifecycle.PostStart != nil || cr.Spec.ProxySQL.Lifecycle.PreStop != nil) { 162 appc.Lifecycle = &cr.Spec.ProxySQL.Lifecycle 163 } 164 165 return appc, nil 166 } 167 168 func (c *Proxy) SidecarContainers(spec *api.PodSpec, secrets string, cr *api.PerconaXtraDBCluster) ([]corev1.Container, error) { 169 pxcMonit := corev1.Container{ 170 Name: "pxc-monit", 171 Image: spec.Image, 172 ImagePullPolicy: spec.ImagePullPolicy, 173 Args: []string{ 174 "/usr/bin/peer-list", 175 "-on-change=/usr/bin/add_pxc_nodes.sh", 176 "-service=$(PXC_SERVICE)", 177 }, 178 Resources: spec.SidecarResources, 179 Env: []corev1.EnvVar{ 180 { 181 Name: "PXC_SERVICE", 182 Value: c.labels["app.kubernetes.io/instance"] + "-pxc", 183 }, 184 { 185 Name: "MYSQL_ROOT_PASSWORD", 186 ValueFrom: &corev1.EnvVarSource{ 187 SecretKeyRef: app.SecretKeySelector(secrets, users.Root), 188 }, 189 }, 190 { 191 Name: "PROXY_ADMIN_USER", 192 Value: users.ProxyAdmin, 193 }, 194 { 195 Name: "PROXY_ADMIN_PASSWORD", 196 ValueFrom: &corev1.EnvVarSource{ 197 SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin), 198 }, 199 }, 200 { 201 Name: "MONITOR_PASSWORD", 202 ValueFrom: &corev1.EnvVarSource{ 203 SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor), 204 }, 205 }, 206 }, 207 } 208 209 proxysqlMonit := corev1.Container{ 210 Name: "proxysql-monit", 211 Image: spec.Image, 212 ImagePullPolicy: spec.ImagePullPolicy, 213 Args: []string{ 214 "/usr/bin/peer-list", 215 "-on-change=/usr/bin/add_proxysql_nodes.sh", 216 "-service=$(PROXYSQL_SERVICE)", 217 }, 218 Resources: spec.SidecarResources, 219 Env: []corev1.EnvVar{ 220 { 221 Name: "PROXYSQL_SERVICE", 222 Value: c.labels["app.kubernetes.io/instance"] + "-proxysql-unready", 223 }, 224 { 225 Name: "MYSQL_ROOT_PASSWORD", 226 ValueFrom: &corev1.EnvVarSource{ 227 SecretKeyRef: app.SecretKeySelector(secrets, users.Root), 228 }, 229 }, 230 { 231 Name: "PROXY_ADMIN_USER", 232 Value: users.ProxyAdmin, 233 }, 234 { 235 Name: "PROXY_ADMIN_PASSWORD", 236 ValueFrom: &corev1.EnvVarSource{ 237 SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin), 238 }, 239 }, 240 { 241 Name: "MONITOR_PASSWORD", 242 ValueFrom: &corev1.EnvVarSource{ 243 SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor), 244 }, 245 }, 246 }, 247 } 248 if cr.Spec.AllowUnsafeConfig && (cr.Spec.TLS == nil || cr.Spec.TLS.IssuerConf == nil) { 249 pxcMonit.Env = append(pxcMonit.Env, corev1.EnvVar{ 250 Name: "SSL_DIR", 251 Value: "/dev/null", 252 }) 253 proxysqlMonit.Env = append(proxysqlMonit.Env, corev1.EnvVar{ 254 Name: "SSL_DIR", 255 Value: "/dev/null", 256 }) 257 } 258 if cr.CompareVersionWith("1.9.0") >= 0 { 259 fvar := true 260 envFrom := corev1.EnvFromSource{ 261 SecretRef: &corev1.SecretEnvSource{ 262 LocalObjectReference: corev1.LocalObjectReference{ 263 Name: cr.Spec.ProxySQL.EnvVarsSecretName, 264 }, 265 Optional: &fvar, 266 }, 267 } 268 pxcMonit.EnvFrom = append(pxcMonit.EnvFrom, envFrom) 269 proxysqlMonit.EnvFrom = append(proxysqlMonit.EnvFrom, envFrom) 270 } 271 if cr.CompareVersionWith("1.5.0") >= 0 { 272 operEnv := corev1.EnvVar{ 273 Name: "OPERATOR_PASSWORD", 274 ValueFrom: &corev1.EnvVarSource{ 275 SecretKeyRef: app.SecretKeySelector(secrets, users.Operator), 276 }, 277 } 278 pxcMonit.Env[1] = operEnv 279 proxysqlMonit.Env[1] = operEnv 280 } 281 282 return []corev1.Container{pxcMonit, proxysqlMonit}, nil 283 } 284 285 func (c *Proxy) LogCollectorContainer(_ *api.LogCollectorSpec, _ string, _ string, _ *api.PerconaXtraDBCluster) ([]corev1.Container, error) { 286 return nil, nil 287 } 288 289 func (c *Proxy) PMMContainer(ctx context.Context, cl client.Client, spec *api.PMMSpec, secret *corev1.Secret, cr *api.PerconaXtraDBCluster) (*corev1.Container, error) { 290 envVarsSecret := &corev1.Secret{} 291 err := cl.Get(ctx, types.NamespacedName{Name: cr.Spec.PXC.EnvVarsSecretName, Namespace: cr.Namespace}, envVarsSecret) 292 if client.IgnoreNotFound(err) != nil { 293 return nil, errors.Wrap(err, "get env vars secret") 294 } 295 296 ct := app.PMMClient(cr, spec, secret, envVarsSecret) 297 298 pmmEnvs := []corev1.EnvVar{ 299 { 300 Name: "DB_TYPE", 301 Value: "proxysql", 302 }, 303 { 304 Name: "MONITOR_USER", 305 Value: users.Monitor, 306 }, 307 { 308 Name: "MONITOR_PASSWORD", 309 ValueFrom: &corev1.EnvVarSource{ 310 SecretKeyRef: app.SecretKeySelector(secret.Name, users.Monitor), 311 }, 312 }, 313 } 314 315 dbEnvs := []corev1.EnvVar{ 316 { 317 Name: "DB_USER", 318 Value: users.Monitor, 319 }, 320 { 321 Name: "DB_PASSWORD", 322 ValueFrom: &corev1.EnvVarSource{ 323 SecretKeyRef: app.SecretKeySelector(secret.Name, users.Monitor), 324 }, 325 }, 326 { 327 Name: "DB_CLUSTER", 328 Value: app.Name, 329 }, 330 { 331 Name: "DB_HOST", 332 Value: "localhost", 333 }, 334 { 335 Name: "DB_PORT", 336 Value: "6032", 337 }, 338 } 339 340 dbArgsEnv := []corev1.EnvVar{ 341 { 342 Name: "DB_ARGS", 343 Value: "--dsn $(MONITOR_USER):$(MONITOR_PASSWORD)@tcp(localhost:6032)/", 344 }, 345 } 346 347 ct.Env = append(ct.Env, pmmEnvs...) 348 if cr.CompareVersionWith("1.2.0") >= 0 { 349 ct.Env = append(ct.Env, dbEnvs...) 350 ct.Resources = spec.Resources 351 } else { 352 ct.Env = append(ct.Env, dbArgsEnv...) 353 } 354 355 if cr.CompareVersionWith("1.9.0") >= 0 { 356 fvar := true 357 ct.EnvFrom = []corev1.EnvFromSource{ 358 { 359 SecretRef: &corev1.SecretEnvSource{ 360 LocalObjectReference: corev1.LocalObjectReference{ 361 Name: cr.Spec.ProxySQL.EnvVarsSecretName, 362 }, 363 Optional: &fvar, 364 }, 365 }, 366 } 367 } 368 369 if cr.CompareVersionWith("1.7.0") >= 0 { 370 PmmProxysqlParams := "" 371 if spec.ProxysqlParams != "" { 372 PmmProxysqlParams = spec.ProxysqlParams 373 } 374 clusterPmmEnvs := []corev1.EnvVar{ 375 { 376 Name: "CLUSTER_NAME", 377 Value: cr.Name, 378 }, 379 { 380 Name: "PMM_ADMIN_CUSTOM_PARAMS", 381 Value: PmmProxysqlParams, 382 }, 383 } 384 ct.Env = append(ct.Env, clusterPmmEnvs...) 385 pmmAgentScriptEnv := app.PMMAgentScript(cr, "proxysql") 386 ct.Env = append(ct.Env, pmmAgentScriptEnv...) 387 } 388 389 if cr.CompareVersionWith("1.10.0") >= 0 { 390 // PMM team added these flags which allows us to avoid 391 // container crash, but just restart pmm-agent till it recovers 392 // the connection. 393 sidecarEnvs := []corev1.EnvVar{ 394 { 395 Name: "PMM_AGENT_SIDECAR", 396 Value: "true", 397 }, 398 { 399 Name: "PMM_AGENT_SIDECAR_SLEEP", 400 Value: "5", 401 }, 402 } 403 ct.Env = append(ct.Env, sidecarEnvs...) 404 } 405 406 if cr.CompareVersionWith("1.14.0") >= 0 { 407 // PMM team moved temp directory to /usr/local/percona/pmm2/tmp 408 // but it doesn't work on OpenShift so we set it back to /tmp 409 sidecarEnvs := []corev1.EnvVar{ 410 { 411 Name: "PMM_AGENT_PATHS_TEMPDIR", 412 Value: "/tmp", 413 }, 414 } 415 ct.Env = append(ct.Env, sidecarEnvs...) 416 } 417 418 return &ct, nil 419 } 420 421 func (c *Proxy) Volumes(podSpec *api.PodSpec, cr *api.PerconaXtraDBCluster, vg api.CustomVolumeGetter) (*api.Volume, error) { 422 ls := c.Labels() 423 424 vol := app.Volumes(podSpec, proxyDataVolumeName) 425 vol.Volumes = append( 426 vol.Volumes, 427 app.GetSecretVolumes("ssl-internal", podSpec.SSLInternalSecretName, true), 428 app.GetSecretVolumes("ssl", podSpec.SSLSecretName, cr.Spec.AllowUnsafeConfig), 429 ) 430 431 configVolume, err := vg(cr.Namespace, proxyConfigVolumeName, ls["app.kubernetes.io/instance"]+"-proxysql", false) 432 if err != nil && !errors.Is(err, api.NoCustomVolumeErr) { 433 return nil, err 434 } 435 if err == nil { 436 vol.Volumes = append(vol.Volumes, configVolume) 437 } 438 if cr.CompareVersionWith("1.11.0") >= 0 && cr.Spec.ProxySQL != nil && cr.Spec.ProxySQL.HookScript != "" { 439 vol.Volumes = append(vol.Volumes, 440 app.GetConfigVolumes("hookscript", ls["app.kubernetes.io/instance"]+"-"+ls["app.kubernetes.io/component"]+"-hookscript")) 441 } 442 if cr.CompareVersionWith("1.13.0") >= 0 { 443 vol.Volumes = append(vol.Volumes, 444 corev1.Volume{ 445 Name: app.BinVolumeName, 446 VolumeSource: corev1.VolumeSource{ 447 EmptyDir: &corev1.EmptyDirVolumeSource{}, 448 }, 449 }, 450 ) 451 } 452 return vol, nil 453 } 454 455 func (c *Proxy) StatefulSet() *appsv1.StatefulSet { 456 return c.sfs 457 } 458 459 func (c *Proxy) Labels() map[string]string { 460 return c.labels 461 } 462 463 func (c *Proxy) Service() string { 464 return c.service 465 } 466 467 func (c *Proxy) UpdateStrategy(cr *api.PerconaXtraDBCluster) appsv1.StatefulSetUpdateStrategy { 468 switch cr.Spec.UpdateStrategy { 469 case appsv1.OnDeleteStatefulSetStrategyType: 470 return appsv1.StatefulSetUpdateStrategy{Type: appsv1.OnDeleteStatefulSetStrategyType} 471 case api.SmartUpdateStatefulSetStrategyType: 472 return appsv1.StatefulSetUpdateStrategy{Type: appsv1.RollingUpdateStatefulSetStrategyType} 473 default: 474 var zero int32 = 0 475 return appsv1.StatefulSetUpdateStrategy{ 476 Type: appsv1.RollingUpdateStatefulSetStrategyType, 477 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 478 Partition: &zero, 479 }, 480 } 481 } 482 }