From e4bc37f9598f0c8f1bf06a3955613c1826407199 Mon Sep 17 00:00:00 2001 From: zhyass <34016424+zhyass@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:21:51 +0800 Subject: [PATCH] *: add pod disruption budget --- api/v1alpha1/cluster_types.go | 6 +++ .../crds/mysql.radondb.com_clusters.yaml | 5 ++ .../templates/cluster_rbac.yaml | 12 +++++ cluster/cluster.go | 2 +- cluster/syncer/pdb.go | 49 +++++++++++++++++++ cluster/syncer/rolebinding.go | 2 +- .../crd/bases/mysql.radondb.com_clusters.yaml | 5 ++ config/rbac/role.yaml | 12 +++++ controllers/cluster_controller.go | 4 ++ utils/constants.go | 2 + 10 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 cluster/syncer/pdb.go diff --git a/api/v1alpha1/cluster_types.go b/api/v1alpha1/cluster_types.go index 0f8fda45..4610d6c3 100644 --- a/api/v1alpha1/cluster_types.go +++ b/api/v1alpha1/cluster_types.go @@ -35,6 +35,12 @@ type ClusterSpec struct { // +kubebuilder:default:=3 Replicas *int32 `json:"replicas,omitempty"` + // The number of pods from that set that must still be available after the + // eviction, even in the absence of the evicted pod + // +optional + // +kubebuilder:default:="50%" + MinAvailable string `json:"minAvailable,omitempty"` + // MysqlOpts is the options of MySQL container. // +optional // +kubebuilder:default:={rootPassword: "", rootHost: "127.0.0.1", user: "qc_usr", password: "Qing@123", database: "qingcloud", initTokuDB: true, resources: {limits: {cpu: "500m", memory: "1Gi"}, requests: {cpu: "100m", memory: "256Mi"}}} diff --git a/charts/mysql-operator/crds/mysql.radondb.com_clusters.yaml b/charts/mysql-operator/crds/mysql.radondb.com_clusters.yaml index 135eed8c..11861f18 100644 --- a/charts/mysql-operator/crds/mysql.radondb.com_clusters.yaml +++ b/charts/mysql-operator/crds/mysql.radondb.com_clusters.yaml @@ -106,6 +106,11 @@ spec: type: object type: object type: object + minAvailable: + default: 50% + description: The number of pods from that set that must still be available + after the eviction, even in the absence of the evicted pod + type: string mysqlOpts: default: database: qingcloud diff --git a/charts/mysql-operator/templates/cluster_rbac.yaml b/charts/mysql-operator/templates/cluster_rbac.yaml index 539b077b..f34aaddc 100644 --- a/charts/mysql-operator/templates/cluster_rbac.yaml +++ b/charts/mysql-operator/templates/cluster_rbac.yaml @@ -99,6 +99,18 @@ rules: - get - patch - update +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/cluster/cluster.go b/cluster/cluster.go index 43228902..13ccb090 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -248,7 +248,7 @@ func (c *Cluster) EnsureVolumeClaimTemplates(schema *runtime.Scheme) ([]corev1.P // GetNameForResource returns the name of a resource from above func (c *Cluster) GetNameForResource(name utils.ResourceName) string { switch name { - case utils.StatefulSet, utils.ConfigMap, utils.HeadlessSVC: + case utils.StatefulSet, utils.ConfigMap, utils.HeadlessSVC, utils.PodDisruptionBudget: return fmt.Sprintf("%s-mysql", c.Name) case utils.LeaderService: return fmt.Sprintf("%s-leader", c.Name) diff --git a/cluster/syncer/pdb.go b/cluster/syncer/pdb.go new file mode 100644 index 00000000..155637af --- /dev/null +++ b/cluster/syncer/pdb.go @@ -0,0 +1,49 @@ +/* +Copyright 2021 RadonDB. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package syncer + +import ( + "github.com/presslabs/controller-util/syncer" + policyv1beta1 "k8s.io/api/policy/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/radondb/radondb-mysql-kubernetes/cluster" + "github.com/radondb/radondb-mysql-kubernetes/utils" +) + +// NewPDBSyncer returns podDisruptionBudget syncer. +func NewPDBSyncer(cli client.Client, c *cluster.Cluster) syncer.Interface { + pdb := &policyv1beta1.PodDisruptionBudget{ + ObjectMeta: metav1.ObjectMeta{ + Name: c.GetNameForResource(utils.PodDisruptionBudget), + Namespace: c.Namespace, + }, + } + + return syncer.NewObjectSyncer("PDB", c.Unwrap(), pdb, cli, func() error { + if pdb.Spec.MinAvailable != nil { + // this mean that pdb is created and should return because spec is imutable + return nil + } + ma := intstr.FromString(c.Spec.MinAvailable) + pdb.Spec.MinAvailable = &ma + pdb.Spec.Selector = metav1.SetAsLabelSelector(c.GetSelectorLabels()) + return nil + }) +} diff --git a/cluster/syncer/rolebinding.go b/cluster/syncer/rolebinding.go index 2a02b447..f56cab25 100644 --- a/cluster/syncer/rolebinding.go +++ b/cluster/syncer/rolebinding.go @@ -39,7 +39,7 @@ func NewRoleBindingSyncer(cli client.Client, c *cluster.Cluster) syncer.Interfac Labels: c.GetLabels(), }, } - return syncer.NewObjectSyncer("Role", c.Unwrap(), roleBinding, cli, func() error { + return syncer.NewObjectSyncer("RoleBinding", c.Unwrap(), roleBinding, cli, func() error { roleBinding.RoleRef = rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "Role", diff --git a/config/crd/bases/mysql.radondb.com_clusters.yaml b/config/crd/bases/mysql.radondb.com_clusters.yaml index 135eed8c..11861f18 100644 --- a/config/crd/bases/mysql.radondb.com_clusters.yaml +++ b/config/crd/bases/mysql.radondb.com_clusters.yaml @@ -106,6 +106,11 @@ spec: type: object type: object type: object + minAvailable: + default: 50% + description: The number of pods from that set that must still be available + after the eviction, even in the absence of the evicted pod + type: string mysqlOpts: default: database: qingcloud diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 41e1919f..da2fa859 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -90,6 +90,18 @@ rules: - get - patch - update +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go index 730a1ef4..ed378200 100644 --- a/controllers/cluster_controller.go +++ b/controllers/cluster_controller.go @@ -23,6 +23,7 @@ import ( "github.com/presslabs/controller-util/syncer" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + policyv1beta1 "k8s.io/api/policy/v1beta1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -52,6 +53,7 @@ type ClusterReconciler struct { // +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=get;list;watch;create;update // +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -113,6 +115,7 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct clustersyncer.NewLeaderSVCSyncer(r.Client, instance), clustersyncer.NewFollowerSVCSyncer(r.Client, instance), clustersyncer.NewStatefulSetSyncer(r.Client, instance, cmRev, sctRev), + clustersyncer.NewPDBSyncer(r.Client, instance), } // run the syncers @@ -136,5 +139,6 @@ func (r *ClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&rbacv1.RoleBinding{}). Owns(&corev1.ServiceAccount{}). Owns(&corev1.Secret{}). + Owns(&policyv1beta1.PodDisruptionBudget{}). Complete(r) } diff --git a/utils/constants.go b/utils/constants.go index f0580e8a..30c5e51f 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -110,4 +110,6 @@ const ( RoleBinding ResourceName = "rolebinding" // ServiceAccount is the alias of the serviceaccount resource. ServiceAccount ResourceName = "service-account" + // PodDisruptionBudget is the name of pod disruption budget for the statefulset. + PodDisruptionBudget ResourceName = "pdb" )