Switch Mcrouter using kopf

Change-Id: Ic9904e8677eacea41ade96502f2dd3c5e37c8a95
This commit is contained in:
okozachenko 2020-04-16 13:54:35 -07:00
parent 66b6315d09
commit 4c543987ed
14 changed files with 287 additions and 617 deletions

View File

@ -1,63 +0,0 @@
// Copyright 2020 VEXXHOST, Inc.
//
// 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 v1alpha1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// McrouterPoolSpec defines the desired state of an Mcrouter pool
type McrouterPoolSpec struct {
Servers []string `json:"servers"`
}
// McrouterSpec defines the desired state of Mcrouter
type McrouterSpec struct {
Pools map[string]McrouterPoolSpec `json:"pools"`
Route string `json:"route"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
}
// McrouterStatus defines the observed state of Mcrouter
type McrouterStatus struct {
// +kubebuilder:validation:Default=Pending
Phase string `json:"phase"`
}
// +kubebuilder:object:root=true
// Mcrouter is the Schema for the mcrouters API
type Mcrouter struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec McrouterSpec `json:"spec,omitempty"`
Status McrouterStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// McrouterList contains a list of Mcrouter
type McrouterList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Mcrouter `json:"items"`
}
func init() {
SchemeBuilder.Register(&Mcrouter{}, &McrouterList{})
}

View File

@ -23,136 +23,6 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Mcrouter) DeepCopyInto(out *Mcrouter) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mcrouter.
func (in *Mcrouter) DeepCopy() *Mcrouter {
if in == nil {
return nil
}
out := new(Mcrouter)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Mcrouter) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *McrouterList) DeepCopyInto(out *McrouterList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Mcrouter, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterList.
func (in *McrouterList) DeepCopy() *McrouterList {
if in == nil {
return nil
}
out := new(McrouterList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *McrouterList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *McrouterPoolSpec) DeepCopyInto(out *McrouterPoolSpec) {
*out = *in
if in.Servers != nil {
in, out := &in.Servers, &out.Servers
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterPoolSpec.
func (in *McrouterPoolSpec) DeepCopy() *McrouterPoolSpec {
if in == nil {
return nil
}
out := new(McrouterPoolSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *McrouterSpec) DeepCopyInto(out *McrouterSpec) {
*out = *in
if in.Pools != nil {
in, out := &in.Pools, &out.Pools
*out = make(map[string]McrouterPoolSpec, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]v1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterSpec.
func (in *McrouterSpec) DeepCopy() *McrouterSpec {
if in == nil {
return nil
}
out := new(McrouterSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *McrouterStatus) DeepCopyInto(out *McrouterStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterStatus.
func (in *McrouterStatus) DeepCopy() *McrouterStatus {
if in == nil {
return nil
}
out := new(McrouterStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rabbitmq) DeepCopyInto(out *Rabbitmq) {
*out = *in

View File

@ -1,87 +0,0 @@
// Copyright 2020 VEXXHOST, Inc.
//
// 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 builders
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// McrouterBuilder defines the interface to build a Mcrouter
type McrouterBuilder struct {
obj *infrastructurev1alpha1.Mcrouter
poolSpecs map[string]*McrouterPoolSpecBuilder
owner metav1.Object
scheme *runtime.Scheme
}
// Mcrouter returns a new mcrouter builder
func Mcrouter(existing *infrastructurev1alpha1.Mcrouter, owner metav1.Object, scheme *runtime.Scheme) *McrouterBuilder {
if existing.Spec.Pools == nil {
existing.Spec.Pools = map[string]infrastructurev1alpha1.McrouterPoolSpec{}
}
return &McrouterBuilder{
obj: existing,
poolSpecs: map[string]*McrouterPoolSpecBuilder{},
owner: owner,
scheme: scheme,
}
}
// Labels specifies labels for the Mcrouter
func (z *McrouterBuilder) Labels(labels map[string]string) *McrouterBuilder {
z.obj.ObjectMeta.Labels = labels
return z
}
// NodeSelector defines a NodeSelector for Mcrouter
func (z *McrouterBuilder) NodeSelector(selector map[string]string) *McrouterBuilder {
z.obj.Spec.NodeSelector = selector
return z
}
// Tolerations defines tolerations for Mcrouter
func (z *McrouterBuilder) Tolerations(tolerations []v1.Toleration) *McrouterBuilder {
z.obj.Spec.Tolerations = tolerations
return z
}
// Route defines route for Mcrouter
func (z *McrouterBuilder) Route(route string) *McrouterBuilder {
z.obj.Spec.Route = route
return z
}
// Pool defines one set pool for Mcrouter
func (z *McrouterBuilder) Pool(poolName string, poolSpec *McrouterPoolSpecBuilder) *McrouterBuilder {
z.poolSpecs[poolName] = poolSpec
return z
}
// Build returns a complete Mcrouter object
func (z *McrouterBuilder) Build() error {
for key, value := range z.poolSpecs {
pool, err := value.Build()
if err != nil {
return err
}
z.obj.Spec.Pools[key] = pool
}
return controllerutil.SetControllerReference(z.owner, z.obj, z.scheme)
}

View File

@ -1,45 +0,0 @@
// Copyright 2020 VEXXHOST, Inc.
//
// 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 builders
import (
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
)
// McrouterPoolSpecBuilder defines the interface to build a McrouterPoolSpec
type McrouterPoolSpecBuilder struct {
obj *infrastructurev1alpha1.McrouterPoolSpec
}
// McrouterPoolSpec returns a new mcrouterPoolSpec builder
func McrouterPoolSpec() *McrouterPoolSpecBuilder {
poolSpec := &infrastructurev1alpha1.McrouterPoolSpec{
Servers: []string{},
}
return &McrouterPoolSpecBuilder{
obj: poolSpec,
}
}
// Servers specifies servers for the McrouterPoolSpec
func (ps *McrouterPoolSpecBuilder) Servers(servers []string) *McrouterPoolSpecBuilder {
ps.obj.Servers = servers
return ps
}
// Build returns a complete McrouterPoolSpec object
func (ps *McrouterPoolSpecBuilder) Build() (infrastructurev1alpha1.McrouterPoolSpec, error) {
return *ps.obj, nil
}

View File

@ -24,6 +24,8 @@ spec:
args:
- run
- -m
- openstack_operator.mcrouter
- -m
- openstack_operator.memcached
resources:
limits:

View File

@ -1,279 +0,0 @@
// Copyright 2020 VEXXHOST, Inc.
//
// 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 controllers
import (
"context"
"encoding/json"
"fmt"
"github.com/go-logr/logr"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
"opendev.org/vexxhost/openstack-operator/builders"
"opendev.org/vexxhost/openstack-operator/utils/baseutils"
"opendev.org/vexxhost/openstack-operator/utils/k8sutils"
)
// McrouterReconciler reconciles a Mcrouter object
type McrouterReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=prometheusrules,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps;services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// Reconcile does the reconcilication of Mcrouter instances
func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("mcrouter", req.NamespacedName)
var mcrouter infrastructurev1alpha1.Mcrouter
if err := r.Get(ctx, req.NamespacedName, &mcrouter); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Labels
typeLabels := baseutils.MergeMapsWithoutOverwrite(map[string]string{
"app.kubernetes.io/name": "mcrouter",
"app.kubernetes.io/managed-by": "openstack-operator",
}, mcrouter.Labels)
labels := map[string]string{
"app.kubernetes.io/name": "mcrouter",
"app.kubernetes.io/managed-by": "openstack-operator",
"app.kubernetes.io/instance": req.Name,
}
// ConfigMap
if res, err := r.ReconcileConfigMap(ctx, req, &mcrouter, log); err != nil || res != (ctrl.Result{}) {
return res, err
}
// Deployment
if res, err := r.ReconcileDeployment(ctx, req, &mcrouter, log, labels); err != nil || res != (ctrl.Result{}) {
return res, err
}
// PodMonitor
if res, err := r.ReconcilePodMonitor(ctx, req, &mcrouter, log, typeLabels); err != nil || res != (ctrl.Result{}) {
return res, err
}
// Alertrule
if res, err := r.ReconcilePrometheusRule(ctx, req, &mcrouter, log, typeLabels); err != nil || res != (ctrl.Result{}) {
return res, err
}
// Service
if res, err := r.ReconcileService(ctx, req, &mcrouter, log, labels); err != nil || res != (ctrl.Result{}) {
return res, err
}
return ctrl.Result{}, nil
}
// SetupWithManager initializes the controller with primary manager
func (r *McrouterReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&infrastructurev1alpha1.Mcrouter{}).
Owns(&corev1.ConfigMap{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Owns(&monitoringv1.PodMonitor{}).
Owns(&monitoringv1.PrometheusRule{}).
Complete(r)
}
// ReconcileService reconciles the service
func (r *McrouterReconciler) ReconcileService(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) {
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, service, func() error {
return builders.Service(service, mcrouter, r.Scheme).
Port("mcrouter", 11211).
Selector(labels).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "Service").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}
// ReconcilePodMonitor reconciles the podMonitor
func (r *McrouterReconciler) ReconcilePodMonitor(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) {
podMonitor := &monitoringv1.PodMonitor{
TypeMeta: metav1.TypeMeta{
APIVersion: "monitoring.coreos.com/v1",
Kind: "PodMonitor",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "mcrouter-podmonitor",
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, podMonitor, func() error {
return builders.PodMonitor(podMonitor, mcrouter, r.Scheme).
Labels(labels).
Selector(map[string]string{
"app.kubernetes.io/name": "mcrouter",
}).
PodMetricsEndpoints(
builders.PodMetricsEndpoint().
Port("metrics").
Path("/metrics").
Interval("15s"),
).Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "mcrouter-podmonitor").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}
// ReconcilePrometheusRule reconciles the prometheusRule
func (r *McrouterReconciler) ReconcilePrometheusRule(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) {
alertRule := &monitoringv1.PrometheusRule{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "mcrouter-alertrule",
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, alertRule, func() error {
return builders.PrometheusRule(alertRule, mcrouter, r.Scheme).
Labels(labels).
RuleGroups(builders.RuleGroup().
Name("mcrouter-rule").
Rules(
builders.Rule().
Alert("McrouterDown").
Priority(2).
Expr("mcrouter_up != 1"),
builders.Rule().
Alert("McrouterAllBackendsDown").
Priority(2).
Expr("mcrouter_servers{state='up'} / mcrouter_servers == 0"),
builders.Rule().
Alert("McrouterBackendDown").
Priority(3).
Expr("mcrouter_servers{state='down'} != 0"),
).
Interval("1m")).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "mcrouter-alertrule").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}
// ReconcileDeployment reconciles the deployment
func (r *McrouterReconciler) ReconcileDeployment(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) {
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, deployment, func() error {
return builders.Deployment(deployment, mcrouter, r.Scheme).
Labels(labels).
Replicas(2).
PodTemplateSpec(
builders.PodTemplateSpec().
PodSpec(
builders.PodSpec().
NodeSelector(mcrouter.Spec.NodeSelector).
Tolerations(mcrouter.Spec.Tolerations).
Containers(
builders.Container("mcrouter", "vexxhost/mcrouter:latest").
Args("-p", "11211", "-f", "/data/config.json").
Port("mcrouter", 11211).PortProbe("mcrouter", 10, 30).
Resources(500, 128, 500, 2).
Volume("config", "/data").
SecurityContext(
builders.SecurityContext().
RunAsUser(999).
RunAsGroup(999),
),
builders.Container("exporter", "vexxhost/mcrouter-exporter:latest").
Args("-mcrouter.address", "localhost:11211").
Port("metrics", 9442).HTTPProbe("metrics", "/metrics", 10, 30).
Resources(500, 128, 500, 2).
SecurityContext(
builders.SecurityContext().
RunAsUser(1001),
),
).
Volumes(
builders.Volume("config").FromConfigMap(fmt.Sprintf("mcrouter-%s", req.Name)),
),
),
).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}
// ReconcileConfigMap reconciles the configMap
func (r *McrouterReconciler) ReconcileConfigMap(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger) (ctrl.Result, error) {
configMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, configMap, func() error {
b, err := json.Marshal(mcrouter.Spec)
if err != nil {
return err
}
return builders.ConfigMap(configMap, mcrouter, r.Scheme).
Data("config.json", string(b)).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "ConfigMap").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}

13
main.go
View File

@ -74,7 +74,6 @@ func main() {
designateClientBuilder.SetAuthFailed()
// Setup controllers with manager
setupMcrouterReconciler(mgr)
setupRabbitmqReconciler(mgr)
setupZoneReconciler(mgr, designateClientBuilder)
setupDesignateReconciler(mgr, designateClientBuilder)
@ -113,18 +112,6 @@ func setupDesignateReconciler(mgr ctrl.Manager, designateClientBuilder *openstac
}
}
// setupMcrouterReconciler setups the Mcrouter controller with manager
func setupMcrouterReconciler(mgr ctrl.Manager) {
if err := (&controllers.McrouterReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Mcrouter")
os.Exit(1)
}
}
// setupRabbitmqReconciler setups the Rabbitmq controller with manager
func setupRabbitmqReconciler(mgr ctrl.Manager) {
if err := (&controllers.RabbitmqReconciler{

View File

@ -0,0 +1,66 @@
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
"""Mcrouter Operator
This module maintains the operator for Mcrouter, it takes care of creating
the appropriate deployments, Mcrouter, pod monitors and Prometheus rules.
"""
import json
import kopf
from openstack_operator import utils
class McrouterSpecEncoder(json.JSONEncoder):
"""McrouterSpecEncoder makes kopf dictview class JSON serializable"""
def default(self, o): # pylint: disable=E0202
return o.__dict__["_src"]["spec"]
@kopf.on.resume('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters')
@kopf.on.create('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters')
def create_or_resume(name, spec, **_):
"""Create and re-sync any Mcrouter instances
This function is called when a new resource is created but also when we
start the service up for the first time.
"""
data = McrouterSpecEncoder().encode(spec)
utils.create_or_update('mcrouter/configmap.yml.j2',
name=name, data=data)
utils.create_or_update('mcrouter/deployment.yml.j2',
name=name, spec=spec)
utils.create_or_update('mcrouter/service.yml.j2',
name=name, spec=spec)
utils.create_or_update('mcrouter/podmonitor.yml.j2',
name=name, spec=spec)
utils.create_or_update('mcrouter/prometheusrule.yml.j2',
name=name, spec=spec)
@kopf.on.update('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters')
def update(name, spec, **_):
"""Update a Mcrouter
This function updates the deployment for Mcrouter if there are any
changes that happen within it.
"""
data = McrouterSpecEncoder().encode(spec)
utils.create_or_update('mcrouter/configmap.yml.j2',
name=name, data=data)
utils.create_or_update('mcrouter/deployment.yml.j2',
name=name, spec=spec)

View File

@ -22,9 +22,11 @@ It also inclues a ``dict`` with mappings which allows doing reverse-lookups
from combinations of apiVersion and kind to the exact model.
"""
from pykube.objects import ConfigMap
from pykube.objects import Deployment
from pykube.objects import NamespacedAPIObject
from pykube.objects import Pod
from pykube.objects import Service
class Mcrouter(NamespacedAPIObject):
@ -53,7 +55,9 @@ class PrometheusRule(NamespacedAPIObject):
MAPPING = {
"v1": {
"ConfigMap": ConfigMap,
"Pod": Pod,
"Service": Service,
},
"apps/v1": {
"Deployment": Deployment,

View File

@ -0,0 +1,21 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
apiVersion: v1
kind: ConfigMap
metadata:
name: mcrouter-{{ name }}
data:
config.json: '{{ data }}'

View File

@ -0,0 +1,98 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcrouter-{{ name }}
labels:
{{ labels("mcrouter", name) | indent(4) }}
spec:
replicas: 2
selector:
matchLabels:
{{ labels("mcrouter", name) | indent(6) }}
template:
metadata:
labels:
{{ labels("mcrouter", name) | indent(8) }}
spec:
containers:
- name: mcrouter
image: vexxhost/mcrouter:latest
args: ["-p", "11211", "-f", "/data/config.json"]
imagePullPolicy: Always
ports:
- name: mcrouter
containerPort: 11211
livenessProbe:
tcpSocket:
port: mcrouter
readinessProbe:
tcpSocket:
port: mcrouter
resources:
limits:
cpu: 50m
ephemeral-storage: 50M
memory: 128M
requests:
cpu: 10m
ephemeral-storage: 50M
memory: 64M
securityContext:
runAsUser: 1001
volumeMounts:
- mountPath: /data
name: config
- name: exporter
image: vexxhost/mcrouter_exporter:latest
args: ["-mcrouter.address", "localhost:11211"]
imagePullPolicy: Always
ports:
- name: metrics
containerPort: 9442
livenessProbe:
httpGet:
path: /metrics
port: metrics
readinessProbe:
httpGet:
path: /metrics
port: metrics
resources:
limits:
cpu: 100m
ephemeral-storage: 10M
memory: 64Mi
requests:
cpu: 50m
ephemeral-storage: 10M
memory: 32Mi
securityContext:
runAsUser: 1001
volumes:
- configMap:
defaultMode: 420
name: mcrouter-{{ name }}
name: config
{% if 'nodeSelector' in spec %}
nodeSelector:
{{ spec.nodeSelector | to_yaml | indent(8) }}
{% endif %}
{% if 'tolerations' in spec %}
tolerations:
{{ spec.tolerations | to_yaml | indent(8) }}
{% endif %}

View File

@ -0,0 +1,29 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: mcrouter-podmonitor
labels:
{{ labels("mcrouter", name) | indent(4) }}
spec:
podMetricsEndpoints:
- interval: 15s
path: /metrics
port: metrics
selector:
matchLabels:
{{ labels("mcrouter", name) | indent(6) }}

View File

@ -0,0 +1,39 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: mcrouter-alertrule
labels:
{{ labels("mcrouter", name) | indent(4) }}
spec:
groups:
- name: down
rules:
- alert: McrouterDown
expr: "mcrouter_up == 0"
annotations:
priority: P2
- name: backend-down
rules:
- alert: McrouterAllBackendsDown
expr: "mcrouter_servers{state='up'} / mcrouter_servers == 0"
annotations:
priority: P2
- alert: McrouterBackendDown
expr: "mcrouter_servers{state='down'} != 0"
annotations:
priority: P3

View File

@ -0,0 +1,28 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# 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.
apiVersion: v1
kind: Service
metadata:
name: mcrouter-{{ name }}
spec:
serviceType: ClusterIP
ports:
- name: mcrouter
port: 11211
protocol: TCP
targetPort: mcrouter
selector:
{{ labels("mcrouter", name) | indent(4) }}