GitXplorerGitXplorer
p

kubebuilder-workshop

public
15 stars
4 forks
1 issues

Commits

List of commits on branch master.
Unverified
b10d6882240ecc68ef68ca0a69bd35bb1a6cb613

kubebuilder 2.0

ppwittrock committed 6 years ago
Unverified
a10d0366b73ed7fd876e279bf491ab4f2a9738fa

Post Slides

ppwittrock committed 6 years ago
Unverified
31d8362778e486b7b5db2609212d76ce421db0d9

Improvements

ppwittrock committed 6 years ago
Unverified
b71d4c1cf8817c0c3c316d4f8ecd9c5335cd2b98

feedback from demo

ppwittrock committed 6 years ago
Unverified
03ef88a6a741fc0957cadf68051eca6573ec8fbb

update break glass

ppwittrock committed 6 years ago
Unverified
80cd016ecde925106501581bdb58792406ae3e6e

update solution for workshop

ppwittrock committed 6 years ago

README

The README file for this repository.

This is not an officially supported Google product

Kubebuilder Workshop

The Kubebuilder Workshop provides a hands-on experience creating Kubernetes APIs using kubebuilder.

Futher documentation on kubebuilder can be found here

Create a Kubernetes API for creating MongoDB instances similar to what is shown in this blog post

It should be possible to manage MongoDB instances by running kubectl apply -f on the yaml file declaring a MongoDB instance.

apiVersion: databases.k8s.io/v1alpha1
kind: MongoDB
metadata:
  name: mongo-instance
spec:
  replicas: 3
  storage: 100Gi

Kubernetes API Deep Dive Slides

Posted Here

Prerequisites

Setup the project

$ go mod init github.com/pwittrock/kubebuilder-workshop
$ kubebuilder init --domain example.com --license apache2 --owner "The Kubernetes authors"

Create the MongoDB Resource and Controller Stubs

$ kubebuilder create api --group databases --version v1alpha1 --kind MongoDB
  • enter y to have it create the stub for the Resource
  • enter y to have it create the stub for the Controller

Implement the Resource

Update the MongoDBSpec

Modify the MongoDB API Schema (e.g. MongoDBSpec) in api/v1alpha1/mongodb_types.go.

// MongoDBSpec defines the desired state of MongoDB
type MongoDBSpec struct {
	// replicas is the number of MongoDB replicas
    // +kubebuilder:validation:Minimum=1
	// +optional
	Replicas *int32 `json:"replicas,omitempty"`

    // storage is the volume size for each instance
	// +optional
	Storage *string `json:"storage,omitempty"`
}

Update the MongoDBStatus

// MongoDBStatus defines the observed state of MongoDB
type MongoDBStatus struct {
	// statefulSetStatus contains the status of the StatefulSet managed by MongoDB
	StatefulSetStatus appsv1.StatefulSetStatus `json:"statefulSetStatus,omitempty"`

	// serviceStatus contains the status of the Service managed by MongoDB
	ServiceStatus corev1.ServiceStatus `json:"serviceStatus,omitempty"`
}

Update the print column markers

These tell kubectl how to print the object.

// +kubebuilder:printcolumn:name="storage",type="string",JSONPath=".spec.storage",format="byte"
// +kubebuilder:printcolumn:name="replicas",type="integer",JSONPath=".spec.replicas",format="int32"
// +kubebuilder:printcolumn:name="ready replicas",type="integer",JSONPath=".status.statefulSetStatus.readyReplicas",format="int32"
// +kubebuilder:printcolumn:name="current replicas",type="integer",JSONPath=".status.statefulSetStatus.currentReplicas",format="int32"

Add the status subresource

This allows status to be updated independently from the spec.

// +kubebuilder:subresource:status

Add the scale subresource

This allows kubectl scale work.

// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.statefulSetStatus.replicas

Update main.go

Add StatefulSets and Services to the Scheme

This is necessary to read / write the objects from the client

func init() {

	databasesv1alpha1.AddToScheme(scheme)
	appsv1.AddToScheme(scheme)
	corev1.AddToScheme(scheme)
	// +kubebuilder:scaffold:scheme
}

Pass the Scheme and Recorder to the Controller

This will be needed later for setting owners references

main.go

	err = (&controllers.MongoDBReconciler{
		Client:   mgr.GetClient(),
		Log:      ctrl.Log.WithName("controllers").WithName("MongoDB"),
		Recorder: mgr.GetEventRecorderFor("mongodb"),
		Scheme:   mgr.GetScheme(),
	}).SetupWithManager(mgr)
	if err != nil {
		setupLog.Error(err, "unable to create controller", "controller", "MongoDB")
		os.Exit(1)
	}

mongodb_controller.go

type MongoDBReconciler struct {
	client.Client
	Log      logr.Logger
	Recorder record.EventRecorder
	Scheme   *runtime.Scheme
}

Implement the Controller

Add StatefulSet and Service RBAC markers

Add an RBAC rule so the Controller can read / write StatuefulSets and Services

// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete

Watch StatefulSets and Services

Update the SetupWithManager function to configure MondoDB to own StatefulSets and Services.

func (r *MongoDBReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&v1alpha1.MongoDB{}).
		Owns(&appsv1.StatefulSet{}). // Generates StatefulSets
		Owns(&corev1.Service{}).     // Generates Services
		Complete(r)
}

Read MongoDB from Reconcile

	// Fetch the MongoDB instance
	mongo := &v1alpha1.MongoDB{}
	if err := r.Get(ctx, req.NamespacedName, mongo); err != nil {
		log.Error(err, "unable to fetch MongoDB")
		if apierrs.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		return ctrl.Result{}, err
	}

Write the Service object

	// Generate Service
	service := &corev1.Service{
		ObjectMeta: ctrl.ObjectMeta{
			Name:      req.Name + "-mongodb-service",
			Namespace: req.Namespace,
		},
	}
	_, err := ctrl.CreateOrUpdate(ctx, r.Client, service, func() error {
		util.SetServiceFields(service, mongo)
		return controllerutil.SetControllerReference(mongo, service, r.Scheme)
	})
	if err != nil {
		return ctrl.Result{}, err
	}

Write the StatefulSet object

	// Generate StatefulSet
	ss := &appsv1.StatefulSet{
		ObjectMeta: ctrl.ObjectMeta{
			Name:      req.Name + "-mongodb-statefulset",
			Namespace: req.Namespace,
		},
	}
	_, err = ctrl.CreateOrUpdate(ctx, r.Client, ss, func() error {
		util.SetStatefulSetFields(ss, service, mongo, mongo.Spec.Replicas, mongo.Spec.Storage)
		return controllerutil.SetControllerReference(mongo, ss, r.Scheme)

	})
	if err != nil {
		return ctrl.Result{}, err
	}

Update the MongoDB status

	ssNN := req.NamespacedName
	ssNN.Name = ss.Name
	if err := r.Get(ctx, ssNN, ss); err != nil {
		log.Error(err, "unable to fetch StatefulSet", "namespaceName", ssNN)
		return ctrl.Result{}, err
	}
	mongo.Status.StatefulSetStatus = ss.Status

	serviceNN := req.NamespacedName
	serviceNN.Name = service.Name
	if err := r.Get(ctx, serviceNN, service); err != nil {
		log.Error(err, "unable to fetch Service", "namespaceName", serviceNN)
		return ctrl.Result{}, err
	}
	mongo.Status.ServiceStatus = service.Status

	err = r.Update(ctx, mongo)
	if err != nil {
		return ctrl.Result{}, err
	}

Run the API

Install CRDs into a cluster

$ make manifests
$ kubectl apply -k config/crd

Run the Controller locally

$ make run

Edit the sample MongoDB file

Edit config/samples/databases_v1alpha1_mongodb.yaml

apiVersion: databases.k8s.io/v1alpha1
kind: MongoDB
metadata:
  name: mongo-instance
spec:
  replicas: 1
  storage: 100Gi

Create the sample

$ kubectl apply -f config/samples/databases_v1alpha1_mongodb.yaml

Test the Application

Inspect cluster state

$ kubectl get mongodbs,statefulsets,services,pods
$ kubectl logs mongodb-sample-mongodb-statefulset-0 mongo

Connect to the running MongoDB instance from within the cluster using a Pod

$ kubectl run mongo-test -t -i --rm --image mongo bash
$ mongo <cluster ip address of mongodb service>:27017

Test scale

$ rm -rf ~/.kube/cache/ # clear the discovery cache
$ kubectl scale mongodbs/mongodb-sample --replicas=3
$ kubectl get mongodbs 

Test Garbage Collection

  • delete the mongodb instance
    • kubectl delete -f config/samples/databases_v1alpha1_mongodb.yaml
  • look for garbage collected resources (they should be gone)
    • kubectl get monogodbs
    • kubectl get statefulsets
    • kubectl get services
    • kubectl get pods
  • recreate the MongoDB instance
    • kubectl apply -f config/samples/databases_v1alpha1_mongodb.yaml