Kooper is a simple Go library to create Kubernetes operators and controllers.
Kooper is a set of utilities packed as a library or framework to easily create Kubernetes controllers and operators.
There is a little of discussion of what a controller and what an operator is, see here for more information.
In Kooper the concepts of controller an operator are very simple, a controller controls the state of a resource in Kubernetes, and an operator is a controller that initializes custom resources (CRD) and controls the state of this custom resource.
- Easy and decoupled library.
- Well structured and a clear API.
- Remove all duplicated code from every controller and operator.
- Uses the tooling already created by Kubernetes.
- Remove complexity from operators and controllers so the focus is on domain logic.
- Easy to mock and extend functionality (Go interfaces!).
- Only support CRD, no TPR support (Kubernetes >=1.7).
- Controller metrics (with a Grafana dashboard based on Prometheus metrics backend).
- Leader election.
- Tracing with Opentracing.
It can be seen how easy is to develop a controller or an operator in kooper looking at the documentation.
This is a simple pod log controller example (full running example here):
// Initialize resources like logger and kubernetes client
//...
// Create our retriever so the controller knows how to get/listen for pod events.
retr := &retrieve.Resource{
Object: &corev1.Pod{},
ListerWatcher: &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return k8scli.CoreV1().Pods("").List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return k8scli.CoreV1().Pods("").Watch(options)
},
},
}
// Our domain logic that will print every add/sync/update and delete event.
hand := &handler.HandlerFunc{
AddFunc: func(_ context.Context, obj runtime.Object) error {
pod := obj.(*corev1.Pod)
log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name)
return nil
},
DeleteFunc: func(_ context.Context, s string) error {
log.Infof("Pod deleted: %s", s)
return nil
},
}
// Create the controller that will refresh every 30 seconds.
ctrl := controller.NewSequential(30*time.Second, hand, retr, nil, log)
stopC := make(chan struct{})
if err := ctrl.Run(stopC); err != nil {
log.Errorf("error running controller: %s", err)
os.Exit(1)
}
os.Exit(0)
The above shows that it is very easy to get a controller working in less than 100 lines of code. How it works can be demonstrated by running the controller from this repository.
go run ./examples/onefile-echo-pod-controller/main.go
or directly using docker (Note you are binding kubernetes configuration on the container so kooper can connect to kubernetes cluster).
docker run \
--rm -it \
-v ${HOME}/.kube:/root/.kube:ro \
golang:1.10 \
/bin/bash -c "go get github.com/spotahome/kooper/... && cd /go/src/github.com/spotahome/kooper && go run ./examples/onefile-echo-pod-controller/main.go"
The state of art in the operators/controllers moves fast, a lot of new operators are being published every day. Most of them have the same "infrastructure" code refering Kubernetes operators/controllers and bootstrapping a new operator can be slow or repetitive.
At this moment there is no standard, although there are some projects like rook operator kit or Giantswarm operator kit that are trying to create it.
Spotahome studied these projects before developing Kooper and they didn't fit the requirements:
- Clear and maintanable code.
- Easy to test and mock.
- Well tested library.
- Easy and clear programming API.
- Good abstraction and structure to focus on domain logic (the meat of the controller).
- Reduce complexity in all the operators and controllers that use the library.
- Not only operators, controllers as first class citizen also.
Any dependency manager can get Kooper or directly with go get the latest version:
go get -u github.com/spotahome/kooper
Managing a project that uses different kubernetes libs as dependencies can be tricky at first because of the different versions of all these libraries(apimachinery, client-go, api...). Here you have an example of how would you use kooper as a dependency in a project and setting the kubernetes libraries to the version that you want along with kooper (using dep).
Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | |
---|---|---|---|---|---|---|
kooper 0.1 | ✓ | ? | ? | ? | ? | ? |
kooper 0.2 | ✓ | ? | ? | ? | ? | ? |
kooper 0.3 | ?+ | ✓ | ? | ? | ? | ? |
kooper 0.4 | ?+ | ✓ | ? | ? | ? | ? |
kooper 0.5 | ? | ?+ | ✓ | ? | ? | ? |
kooper 0.6 | ? | ? | ?+ | ✓ | ? | ? |
kooper HEAD | ? | ? | ? | ?+ | ✓? | ? |
Based on this matrix Kooper needs different versions of Kubernetes dependencies.
An example would be. If the cluster that will use kooper operators/controllers is a 1.9.x Kubernetes cluster, the version of kooper would be 0.2.x
and the kubernetes libraries in 1.9.x
compatibility style. For example the project that uses kooper as a dependency, its dep file would be something like this:
...
[[override]]
name = "k8s.io/api"
version = "kubernetes-1.9.6"
[[override]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.9.6"
[[override]]
name = "k8s.io/client-go"
version = "kubernetes-1.9.6"
[[constraint]]
name = "github.com/spotahome/kooper"
version = "0.2.0"
...
Kooper comes with different topics as documentation.
The starting point would be to check the concepts and then continue with the controller and operator tutorials.