From 3b58aa1ef847d9594b48fe1584d47e7aeb1da64d Mon Sep 17 00:00:00 2001 From: Olaf Bohlen <olbohlen@eenfach.de> Date: Tue, 21 Mar 2023 15:06:28 +0100 Subject: [PATCH] add also Markdown varian --- README.md | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 305 insertions(+), 0 deletions(-) diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9485e9 --- /dev/null +++ b/README.md @@ -0,0 +1,305 @@ + +# Table of Contents + +1. [I need a cookie recipe database!](#org651bf34) +2. [Preparing the cookierecipes CRD](#org58b8bb6) +3. [Storing some sample recipes](#orgbb687b0) +4. [Creating RBAC resources](#org2d91ada) +5. [Storing some sample recipes (hopefully this time!!)](#org69aa53f) +6. [Now can we do anything with our recipes?](#orgbd83100) +7. [I'm an operator with my pocket calculator](#org3bb5d31) +8. [What do we need?](#orgcebea86) +9. [Let's review the Containerfile](#org6c2e7f3) +10. [Have a look at the Controller](#org3165f3c) +11. [Now let's also have a look at the deployment](#org6ec9a40) +12. [The ServiceAccount](#orgcba3d6d) +13. [Building the stuff together](#orga6fa80a) +14. [Deploying the Operator](#org22de8a4) +15. [Test the Operator](#org1cbedf9) +16. [Test for updated recipes](#org40457b2) + + +<a id="org651bf34"></a> + +# I need a cookie recipe database! + +…and because it makes total sense, we are going to abuse the K8s API for it. + +- thankfully we can extend K8s with Custom Resource Definitions (CRDs) +- but how does it work? +- `CustomResourceDefinitions` are themselves a `Resource`, based on a `ResourceDefinition` + + $ oc api-resources | egrep "(NAME|CustomResourceDefinition)" + NAME SHORTNAMES APIVERSION NAMESPACED KIND + customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition + $ oc explain crds + KIND: CustomResourceDefinition + VERSION: apiextensions.k8s.io/v1 + + DESCRIPTION: + CustomResourceDefinition represents a resource that should be exposed on + the API server. Its name MUST be in the format <.spec.name>.<.spec.group>. + [...] + + +<a id="org58b8bb6"></a> + +# Preparing the cookierecipes CRD + +Let's create the CRD from <https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-crd.yaml> + + $ oc new-project kitchen + Now using project "kitchen" on server "https://api.crc.testing:6443". + $ oc create -f cookie-crd.yaml + Error from server (Forbidden): error when creating "cookie-crd.yaml": + customresourcedefinitions.apiextensions.k8s.io is forbidden: User "developer" + cannot create resource "customresourcedefinitions" in API group "apiextensions.k8s.io" + at the cluster scope + + $ oc login -u kubeadmin + $ oc create -f cookie-crd.yaml + customresourcedefinition.apiextensions.k8s.io/cookierecipes.de.eenfach.olbohlen created + $ oc login -u developer + +Now the cluster knows about the CRD and we could store recipes! + + +<a id="orgbb687b0"></a> + +# Storing some sample recipes + +We try to store sample cookie recipes…but: + + $ oc create -f sample-cookie.yaml + Error from server (Forbidden): error when creating "sample-cookie.yaml": + cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create + resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen" + Error from server (Forbidden): error when creating "sample-cookie.yaml": + cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create + resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen" + +We need to set up some RBAC resources first: + +- a `ClusterRole` that allows viewing recipes +- a `ClusterRole` that allows editing recipes +- and a `ClusterRoleBinding` that allows that for authenticated users + + +<a id="org2d91ada"></a> + +# Creating RBAC resources + +Apply the RBAC definitions from: <https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-rbac.yaml> + + $ oc login -u kubeadmin + $ oc create -f cookie-rbac.yaml + clusterrole.rbac.authorization.k8s.io/cookierecipe-edit created + clusterrole.rbac.authorization.k8s.io/cookierecipe-view created + clusterrolebinding.rbac.authorization.k8s.io/cookierecipe-edit created + +The `ClusterRoleBinding` "cookierecipe-edit" allows `system:authenticated:oauth` +group members to edit `cookierecipes`. +`system:authenticated:oauth` contains all users that logged in via the OAuth +service (via an `IdentityProvider`). + + +<a id="org69aa53f"></a> + +# Storing some sample recipes (hopefully this time!!) + +Now we should be able to create the sample recipes: + + $ oc login -u developer + $ oc create -f sample-cookie.yaml + cookierecipe.de.eenfach.olbohlen/vintage-chocolate-chip created + cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread created + $ oc get cookierecipe + NAME AGE + double-dipped-shortbread 17s + vintage-chocolate-chip 17s + +There is no functionality here - we just stored the recipes in the etcd via the K8s API. + + +<a id="orgbd83100"></a> + +# Now can we do anything with our recipes? + +Of course we can **oc get -o yaml** for example on them and filter: + + $ oc get cookierecipe vintage-chocolate-chip -o yaml | yq -y .spec.ingredients[0] + amount: 150 + name: salted butter + remarks: softened + unit: grams + +This is handy, as we can extract exactly the data which we need at a time. + +But…it's a lot of manual work… + + +<a id="org3bb5d31"></a> + +# I'm an operator with my pocket calculator + +Operators were introduced as "Kubernetes Native Applications" and that actually +means nothing. Operators are in the end just `Pods`. + +These Pods run one or more containers, but one container should run a `Controller` +that can interprete your `CustomResources`. + +So let's write a CookieRecipe Operator. In shell-script… :) + +Of course this Operator is not compatible with the `OperatorLifecycyleManager` (`OLM`), +so we have to install it manually. + + +<a id="orgcebea86"></a> + +# What do we need? + +We need: + +- a ContainerImage +- and therefore probably a **Containerfile** +- Controller code + +Then we are going to build the Operator ContainerImage and push it to a Registry. + + +<a id="org6c2e7f3"></a> + +# Let's review the Containerfile + +The Containerfile is here: + +<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/Containerfile> + +the base image is a "kshbase" image, which itself is based upon ubi9 containing also a ksh93 +and an oc client. + + +<a id="org3165f3c"></a> + +# Have a look at the Controller + +The controller is written in KornShell 93 (ksh93), which is mostly bash compatible :) + +The code is here: + +<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/recipe-processor.ksh> + + +<a id="org6ec9a40"></a> + +# Now let's also have a look at the deployment + +Note: this deployment does not use an `ImageStream`, so it would work also on native k8s + +<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-operator-deployment.yaml> + +This deployment requires a `ServiceAccount` called "cookieprocessor", this `ServiceAccount` provides +a Token to authenticate against the API (which we use in the controller script). + + +<a id="orgcba3d6d"></a> + +# The ServiceAccount + +We need a `ServiceAccount`, but that alone will not help. The `ServiceAccount` is NOT member +of `system:authenticated:oauth`, so it can't read `cookierecipes` based on the `ClusterRoleBinding` we created earlier. +For that reason we also create a `RoleBinding` (namespaced!) that allows reading recipes: + +<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookieprocessor-sa.yaml> + + +<a id="orga6fa80a"></a> + +# Building the stuff together + + $ oc create -f cookieprocessor-sa.yaml + serviceaccount/cookieprocessor created + rolebinding.rbac.authorization.k8s.io/cookierecipe-view created + +The registry docker.eenfach.de requires login credentials, so we need to set up a secret and link it. +First login to the registry with **podman login**, then pick the resulting auth.json: + + $ podman login -u olbohlen docker.eenfach.de + Password: + Login Succeeded! + $ oc create secret generic docker-eenfach-de \ + > --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json \ + > --type kubernetes.io/dockerconfigjson + secret/docker-eenfach-de created + $ oc secrets link cookieprocessor docker-eenfach-de --for pull + + +<a id="org22de8a4"></a> + +# Deploying the Operator + +Now that we have everything in place, we will just deploy the Operator Pod: + + $ oc create -f cookie-operator-deployment.yaml + deployment.apps/recipe-processor created + $ oc get pod + NAME READY STATUS RESTARTS AGE + recipe-processor-7f9969697b-qt9lv 1/1 Running 0 17s + $ oc logs -f recipe-processor-7f9969697b-qt9lv + + + New recipe found: double-dipped-shortbread + -------------------------------------------------------------------------- + + Pre: we heat up the oven to 180 degrees Celsius + + Fetching ingredients from recipe: + ---------------------------------- + Fetching 200grams of salted butter (softened) + [...] + +The Operator will process both sample recipes. + + +<a id="org1cbedf9"></a> + +# Test the Operator + +We should test if the Operator notices new recipes, so let's create a third +recipe from +<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/oaty-hazelnut-cookies.yaml> + + $ oc create -f oaty-hazelnut-cookies.yaml + cookierecipe.de.eenfach.olbohlen/oaty-hazelnut created + +After a few seconds, we should see in the Operator log: + + New recipe found: oaty-hazelnut + -------------------------------------------------------------------------- + + Pre: we heat up the oven to 180 degrees Celsius + [...] + + +<a id="org40457b2"></a> + +# Test for updated recipes + +But what if we update a resource? +Keep the **oc logs -f** on the Operator Pod open, and in another terminal let's patch a recipe. + + $ oc patch cookierecipes double-dipped-shortbread --type merge \ + > -p '{"spec":{"temperature":172}}' + cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread patched + +And again in the log you should see + + New recipe found: double-dipped-shortbread + -------------------------------------------------------------------------- + + Pre: we heat up the oven to 172 degrees Celsius + + Fetching ingredients from recipe: + ---------------------------------- + [...] + -- Gitblit v1.9.3