From 201545003b2fee32276866fdc2a1ed1544da1c92 Mon Sep 17 00:00:00 2001 From: Hongchao Deng Date: Fri, 16 Feb 2018 16:23:20 -0800 Subject: [PATCH 1/2] README: add customize logic section --- README.md | 215 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 193 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e60613a746..37dce3f526 100644 --- a/README.md +++ b/README.md @@ -10,64 +10,60 @@ Operator SDK comes with a number of code generators that are designed to facilitate development lifecycle. It helps create the project scaffolding, preprocess custom resource API to generate Kubernetes related code, generate deployment scripts -- just everything that is necessary to build an operator. -Navigate to `$GOPATH/src/github.com/example.com/`. +Navigate to `$GOPATH/src/github.com/example-inc/`. To start a project, we use the `new` generator to provide the foundation of a fresh operator project. Run the following command: ``` -operator-sdk new play-operator +operator-sdk new memcached-operator cache.example.com/v1alpha1 Memcached ``` -This will create the `play-operator` project with scaffolding with dependency code ready. It generates Kubernetes custom resource API of APIGroup `play.example.com` and Kind `PlayService` by default. APIGroups and Kinds can be overridden and added by flags. +This will generate a project repo `memcached-operator`, a custom resource with APIGroup `cache.example.com/v1apha1` and Kind `Memcached`, and an example operator that watches all deployments in the same namespace and logs deployment names. -Navigate to the project root folder: +Navigate to the project folder: ``` -cd play-operator +cd memcached-operator ``` More details about the structure of the project can be found in [this doc][scaffold_doc]. ## Up and running -At this point we are ready to build and deploy a functional operator. First build the binary and container: +At this step we actually have a functional operator already. To see it, first build the binary and container: ``` operator-sdk build $image docker push $image ``` -Kubernetes deployment manifests will be generated in `deploy/play-operator/operator.yaml`. Deploy play-operator: +Kubernetes deployment manifests will be generated in `deploy/memcached-operator.yaml`. The `$image` parameter value will be used and set in the manifest. + +Deploy memcached-operator: ``` -kubectl create -f deploy/play-operator/operator.yaml +kubectl create -f deploy/memcached-operator.yaml ``` -The play-operator would be up and running: +The memcached-operator would be up and running: ``` # kubectl get deploy -NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE -play-operator 1 1 1 1 1m +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE +memcached-operator 1 1 1 1 1m ``` -A `PlayService` CR with the following spec can be created to demonstrate the use of operator: +Check memcached-operator pod’s log: ``` -apiVersion: "play.coreos.com/v1" -kind: "PlayService" -metadata: - name: "example" -spec: - replica: 1 +# kubectl get pod | grep memcached-operator | cut -d' ' -f1 | xargs kubectl logs +Found Deployment `memcached-operator` ``` -Once the CR is created, a new pod `example-box` will be created: +Clean up resources: ``` -# kubectl get pod -NAME READY STATUS RESTARTS AGE -example-box 2/2 Running 0 1m +kubectl delete -f deploy/memcached-operator.yaml ``` This is a basic test that verifies everything works correctly. Next we are going to write the business logic and do something more interesting. @@ -75,5 +71,180 @@ This is a basic test that verifies everything works correctly. Next we are going ## Customizing operator logic +An operator is used to extend the kube-API and codify application domain knowledge. Operator SDK is designed to provide non-Kubernetes developers an easy way to write the business logic. + +In the following we are adding a custom resource `Memcached`, and customizing the operator logic that creating a new Memcached CR will create a Memcached Deployment and (optional) Service. + +In `pkg/apis/cache/v1alpha1/types.go`, Add to `MemcachedSpec` a new field `WithService`: + +```Go +type MemcachedSpec struct { + WithService bool `json:"withService"` +} +``` + +Re-render the generated code for custom resource: + +``` +operator-sdk generate k8s +``` + +In `main.go`, change to watch Memcached CR: + +```Go +func main() { + sdk.Watch(“memcacheds”, namespace, &api.Memcached{}, restcli) + sdk.Handle(stub.NewHandler()) + sdk.Run(context.TODO()) +} +``` + +In `pkg/stub/handler.go`, change `Handle()` logic: + +```Go +import ( + appsv1beta1 "k8s.io/api/apps/v1beta1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (h *Handler) Handle(ctx Context, ev Event) []Action { + var actions []Action + switch obj := ev.Object.(type) { + case *Memcached: + ls := map[string]string{ + "app": "memcached", + "name": obj.Name, + } + + d := &appsv1beta1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: obj.Name, + }, + Spec: appsv1beta1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: ls, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: ls, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{{ + Image: "memcached:1.4.36-alpine", + Name: "memcached", + Command: []string{"memcached", "-m=64", "-o", "modern", "-v"}, + Ports: []v1.ContainerPort{{ + ContainerPort: 11211, + Name: "memcached", + }}, + }}, + }, + }, + }, + } + actions = append(actions, Action{ + Object: d, + Func: KubeApplyFunc, + }) + + if !obj.Spec.WithService { + break + } + + svc := &v1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: obj.Name, + }, + Spec: v1.ServiceSpec{ + Selector: ls, + Ports: []v1.ServicePort{{ + Port: 11211, + Name: "memcached", + }}, + }, + } + actions = append(actions, Action{ + Object: svc, + Func: KubeApplyFunc, + }) + } + return actions +} +``` + +Rebuild the container: + +``` +operator-sdk build $image +docker push $image +``` + +Deploy operator: +``` +kubectl create -f deploy/memcached-operator.yaml +``` + +Create a `Memcached` CR with the following spec: + +``` +apiVersion: "cache.example.com/v1alpha1" +kind: "Memcached" +metadata: + name: "example" +spec: + withService: true +``` + +There will be a new Memcached Deployment: +``` +# kubectl get deploy +example +``` + +There will be a new Memcached Service: +``` +# kubectl get service +example +``` + +We can test the Memcached service by opening a telnet session and running commands via [Memcached protocols][mc_protocol]: + +1. Open a telnet session in another container in order to talk to the service: + ``` + kubectl run -it --rm busybox --image=busybox --restart=Never -- telnet example 11211 + ``` +2. In the telnet prompt, enter the following command to set a key: + ``` + set foo 0 0 5 + bar + ``` +3. Enter the following command to get the key: + ``` + get foo + ``` + It should output: + ``` + VALUE foo 0 5 + bar + ``` + +Now we have successfully to customize the event handling logic to deploy Memcached service for us. + +Clean up resources: + +``` +kubectl delete memcached example +kubectl delete -f deploy/memcached-operator.yaml +``` [scaffold_doc]:./doc/project_layout.md +[mc_protocol]:https://github.com/memcached/memcached/blob/master/doc/protocol.txt From 8c9c0262d1e86afd66b8e3e77f0e2a7c1d544981 Mon Sep 17 00:00:00 2001 From: Hongchao Deng Date: Tue, 20 Feb 2018 11:00:59 -0800 Subject: [PATCH 2/2] comments --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 37dce3f526..59cfb99c23 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,9 @@ This is a basic test that verifies everything works correctly. Next we are going An operator is used to extend the kube-API and codify application domain knowledge. Operator SDK is designed to provide non-Kubernetes developers an easy way to write the business logic. -In the following we are adding a custom resource `Memcached`, and customizing the operator logic that creating a new Memcached CR will create a Memcached Deployment and (optional) Service. +In the following steps we are adding a custom resource `Memcached`, and customizing the operator logic that creating a new Memcached CR will create a Memcached Deployment and (optional) Service. -In `pkg/apis/cache/v1alpha1/types.go`, Add to `MemcachedSpec` a new field `WithService`: +In `pkg/apis/cache/v1alpha1/types.go`, add to `MemcachedSpec` a new field `WithService`: ```Go type MemcachedSpec struct { @@ -237,7 +237,7 @@ We can test the Memcached service by opening a telnet session and running comman bar ``` -Now we have successfully to customize the event handling logic to deploy Memcached service for us. +Now we have successfully customized the event handling logic to deploy Memcached service for us. Clean up resources: