commit | author | age
|
3b58aa
|
1 |
|
OB |
2 |
# Table of Contents |
|
3 |
|
|
4 |
1. [I need a cookie recipe database!](#org651bf34) |
|
5 |
2. [Preparing the cookierecipes CRD](#org58b8bb6) |
|
6 |
3. [Storing some sample recipes](#orgbb687b0) |
|
7 |
4. [Creating RBAC resources](#org2d91ada) |
|
8 |
5. [Storing some sample recipes (hopefully this time!!)](#org69aa53f) |
|
9 |
6. [Now can we do anything with our recipes?](#orgbd83100) |
|
10 |
7. [I'm an operator with my pocket calculator](#org3bb5d31) |
|
11 |
8. [What do we need?](#orgcebea86) |
|
12 |
9. [Let's review the Containerfile](#org6c2e7f3) |
|
13 |
10. [Have a look at the Controller](#org3165f3c) |
|
14 |
11. [Now let's also have a look at the deployment](#org6ec9a40) |
|
15 |
12. [The ServiceAccount](#orgcba3d6d) |
|
16 |
13. [Building the stuff together](#orga6fa80a) |
|
17 |
14. [Deploying the Operator](#org22de8a4) |
|
18 |
15. [Test the Operator](#org1cbedf9) |
|
19 |
16. [Test for updated recipes](#org40457b2) |
|
20 |
|
|
21 |
|
|
22 |
<a id="org651bf34"></a> |
|
23 |
|
|
24 |
# I need a cookie recipe database! |
|
25 |
|
|
26 |
…and because it makes total sense, we are going to abuse the K8s API for it. |
|
27 |
|
|
28 |
- thankfully we can extend K8s with Custom Resource Definitions (CRDs) |
|
29 |
- but how does it work? |
|
30 |
- `CustomResourceDefinitions` are themselves a `Resource`, based on a `ResourceDefinition` |
|
31 |
|
|
32 |
$ oc api-resources | egrep "(NAME|CustomResourceDefinition)" |
|
33 |
NAME SHORTNAMES APIVERSION NAMESPACED KIND |
|
34 |
customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition |
|
35 |
$ oc explain crds |
|
36 |
KIND: CustomResourceDefinition |
|
37 |
VERSION: apiextensions.k8s.io/v1 |
|
38 |
|
|
39 |
DESCRIPTION: |
|
40 |
CustomResourceDefinition represents a resource that should be exposed on |
|
41 |
the API server. Its name MUST be in the format <.spec.name>.<.spec.group>. |
|
42 |
[...] |
|
43 |
|
|
44 |
|
|
45 |
<a id="org58b8bb6"></a> |
|
46 |
|
|
47 |
# Preparing the cookierecipes CRD |
|
48 |
|
|
49 |
Let's create the CRD from <https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-crd.yaml> |
|
50 |
|
|
51 |
$ oc new-project kitchen |
|
52 |
Now using project "kitchen" on server "https://api.crc.testing:6443". |
|
53 |
$ oc create -f cookie-crd.yaml |
|
54 |
Error from server (Forbidden): error when creating "cookie-crd.yaml": |
|
55 |
customresourcedefinitions.apiextensions.k8s.io is forbidden: User "developer" |
|
56 |
cannot create resource "customresourcedefinitions" in API group "apiextensions.k8s.io" |
|
57 |
at the cluster scope |
|
58 |
|
|
59 |
$ oc login -u kubeadmin |
|
60 |
$ oc create -f cookie-crd.yaml |
|
61 |
customresourcedefinition.apiextensions.k8s.io/cookierecipes.de.eenfach.olbohlen created |
|
62 |
$ oc login -u developer |
|
63 |
|
|
64 |
Now the cluster knows about the CRD and we could store recipes! |
|
65 |
|
|
66 |
|
|
67 |
<a id="orgbb687b0"></a> |
|
68 |
|
|
69 |
# Storing some sample recipes |
|
70 |
|
|
71 |
We try to store sample cookie recipes…but: |
|
72 |
|
|
73 |
$ oc create -f sample-cookie.yaml |
|
74 |
Error from server (Forbidden): error when creating "sample-cookie.yaml": |
|
75 |
cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create |
|
76 |
resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen" |
|
77 |
Error from server (Forbidden): error when creating "sample-cookie.yaml": |
|
78 |
cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create |
|
79 |
resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen" |
|
80 |
|
|
81 |
We need to set up some RBAC resources first: |
|
82 |
|
|
83 |
- a `ClusterRole` that allows viewing recipes |
|
84 |
- a `ClusterRole` that allows editing recipes |
|
85 |
- and a `ClusterRoleBinding` that allows that for authenticated users |
|
86 |
|
|
87 |
|
|
88 |
<a id="org2d91ada"></a> |
|
89 |
|
|
90 |
# Creating RBAC resources |
|
91 |
|
|
92 |
Apply the RBAC definitions from: <https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-rbac.yaml> |
|
93 |
|
|
94 |
$ oc login -u kubeadmin |
|
95 |
$ oc create -f cookie-rbac.yaml |
|
96 |
clusterrole.rbac.authorization.k8s.io/cookierecipe-edit created |
|
97 |
clusterrole.rbac.authorization.k8s.io/cookierecipe-view created |
|
98 |
clusterrolebinding.rbac.authorization.k8s.io/cookierecipe-edit created |
|
99 |
|
|
100 |
The `ClusterRoleBinding` "cookierecipe-edit" allows `system:authenticated:oauth` |
|
101 |
group members to edit `cookierecipes`. |
|
102 |
`system:authenticated:oauth` contains all users that logged in via the OAuth |
|
103 |
service (via an `IdentityProvider`). |
|
104 |
|
|
105 |
|
|
106 |
<a id="org69aa53f"></a> |
|
107 |
|
|
108 |
# Storing some sample recipes (hopefully this time!!) |
|
109 |
|
|
110 |
Now we should be able to create the sample recipes: |
|
111 |
|
|
112 |
$ oc login -u developer |
|
113 |
$ oc create -f sample-cookie.yaml |
|
114 |
cookierecipe.de.eenfach.olbohlen/vintage-chocolate-chip created |
|
115 |
cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread created |
|
116 |
$ oc get cookierecipe |
|
117 |
NAME AGE |
|
118 |
double-dipped-shortbread 17s |
|
119 |
vintage-chocolate-chip 17s |
|
120 |
|
|
121 |
There is no functionality here - we just stored the recipes in the etcd via the K8s API. |
|
122 |
|
|
123 |
|
|
124 |
<a id="orgbd83100"></a> |
|
125 |
|
|
126 |
# Now can we do anything with our recipes? |
|
127 |
|
|
128 |
Of course we can **oc get -o yaml** for example on them and filter: |
|
129 |
|
|
130 |
$ oc get cookierecipe vintage-chocolate-chip -o yaml | yq -y .spec.ingredients[0] |
|
131 |
amount: 150 |
|
132 |
name: salted butter |
|
133 |
remarks: softened |
|
134 |
unit: grams |
|
135 |
|
|
136 |
This is handy, as we can extract exactly the data which we need at a time. |
|
137 |
|
|
138 |
But…it's a lot of manual work… |
|
139 |
|
|
140 |
|
|
141 |
<a id="org3bb5d31"></a> |
|
142 |
|
|
143 |
# I'm an operator with my pocket calculator |
|
144 |
|
|
145 |
Operators were introduced as "Kubernetes Native Applications" and that actually |
|
146 |
means nothing. Operators are in the end just `Pods`. |
|
147 |
|
|
148 |
These Pods run one or more containers, but one container should run a `Controller` |
|
149 |
that can interprete your `CustomResources`. |
|
150 |
|
|
151 |
So let's write a CookieRecipe Operator. In shell-script… :) |
|
152 |
|
|
153 |
Of course this Operator is not compatible with the `OperatorLifecycyleManager` (`OLM`), |
|
154 |
so we have to install it manually. |
|
155 |
|
|
156 |
|
|
157 |
<a id="orgcebea86"></a> |
|
158 |
|
|
159 |
# What do we need? |
|
160 |
|
|
161 |
We need: |
|
162 |
|
|
163 |
- a ContainerImage |
|
164 |
- and therefore probably a **Containerfile** |
|
165 |
- Controller code |
|
166 |
|
|
167 |
Then we are going to build the Operator ContainerImage and push it to a Registry. |
|
168 |
|
|
169 |
|
|
170 |
<a id="org6c2e7f3"></a> |
|
171 |
|
|
172 |
# Let's review the Containerfile |
|
173 |
|
|
174 |
The Containerfile is here: |
|
175 |
|
|
176 |
<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/Containerfile> |
|
177 |
|
|
178 |
the base image is a "kshbase" image, which itself is based upon ubi9 containing also a ksh93 |
|
179 |
and an oc client. |
|
180 |
|
|
181 |
|
|
182 |
<a id="org3165f3c"></a> |
|
183 |
|
|
184 |
# Have a look at the Controller |
|
185 |
|
|
186 |
The controller is written in KornShell 93 (ksh93), which is mostly bash compatible :) |
|
187 |
|
|
188 |
The code is here: |
|
189 |
|
|
190 |
<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/recipe-processor.ksh> |
|
191 |
|
|
192 |
|
|
193 |
<a id="org6ec9a40"></a> |
|
194 |
|
|
195 |
# Now let's also have a look at the deployment |
|
196 |
|
|
197 |
Note: this deployment does not use an `ImageStream`, so it would work also on native k8s |
|
198 |
|
|
199 |
<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-operator-deployment.yaml> |
|
200 |
|
|
201 |
This deployment requires a `ServiceAccount` called "cookieprocessor", this `ServiceAccount` provides |
|
202 |
a Token to authenticate against the API (which we use in the controller script). |
|
203 |
|
|
204 |
|
|
205 |
<a id="orgcba3d6d"></a> |
|
206 |
|
|
207 |
# The ServiceAccount |
|
208 |
|
|
209 |
We need a `ServiceAccount`, but that alone will not help. The `ServiceAccount` is NOT member |
|
210 |
of `system:authenticated:oauth`, so it can't read `cookierecipes` based on the `ClusterRoleBinding` we created earlier. |
|
211 |
For that reason we also create a `RoleBinding` (namespaced!) that allows reading recipes: |
|
212 |
|
|
213 |
<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookieprocessor-sa.yaml> |
|
214 |
|
|
215 |
|
|
216 |
<a id="orga6fa80a"></a> |
|
217 |
|
|
218 |
# Building the stuff together |
|
219 |
|
|
220 |
$ oc create -f cookieprocessor-sa.yaml |
|
221 |
serviceaccount/cookieprocessor created |
|
222 |
rolebinding.rbac.authorization.k8s.io/cookierecipe-view created |
|
223 |
|
|
224 |
The registry docker.eenfach.de requires login credentials, so we need to set up a secret and link it. |
|
225 |
First login to the registry with **podman login**, then pick the resulting auth.json: |
|
226 |
|
|
227 |
$ podman login -u olbohlen docker.eenfach.de |
|
228 |
Password: |
|
229 |
Login Succeeded! |
|
230 |
$ oc create secret generic docker-eenfach-de \ |
|
231 |
> --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json \ |
|
232 |
> --type kubernetes.io/dockerconfigjson |
|
233 |
secret/docker-eenfach-de created |
|
234 |
$ oc secrets link cookieprocessor docker-eenfach-de --for pull |
|
235 |
|
|
236 |
|
|
237 |
<a id="org22de8a4"></a> |
|
238 |
|
|
239 |
# Deploying the Operator |
|
240 |
|
|
241 |
Now that we have everything in place, we will just deploy the Operator Pod: |
|
242 |
|
|
243 |
$ oc create -f cookie-operator-deployment.yaml |
|
244 |
deployment.apps/recipe-processor created |
|
245 |
$ oc get pod |
|
246 |
NAME READY STATUS RESTARTS AGE |
|
247 |
recipe-processor-7f9969697b-qt9lv 1/1 Running 0 17s |
|
248 |
$ oc logs -f recipe-processor-7f9969697b-qt9lv |
|
249 |
|
|
250 |
|
|
251 |
New recipe found: double-dipped-shortbread |
|
252 |
-------------------------------------------------------------------------- |
|
253 |
|
|
254 |
Pre: we heat up the oven to 180 degrees Celsius |
|
255 |
|
|
256 |
Fetching ingredients from recipe: |
|
257 |
---------------------------------- |
|
258 |
Fetching 200grams of salted butter (softened) |
|
259 |
[...] |
|
260 |
|
|
261 |
The Operator will process both sample recipes. |
|
262 |
|
|
263 |
|
|
264 |
<a id="org1cbedf9"></a> |
|
265 |
|
|
266 |
# Test the Operator |
|
267 |
|
|
268 |
We should test if the Operator notices new recipes, so let's create a third |
|
269 |
recipe from |
|
270 |
<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/oaty-hazelnut-cookies.yaml> |
|
271 |
|
|
272 |
$ oc create -f oaty-hazelnut-cookies.yaml |
|
273 |
cookierecipe.de.eenfach.olbohlen/oaty-hazelnut created |
|
274 |
|
|
275 |
After a few seconds, we should see in the Operator log: |
|
276 |
|
|
277 |
New recipe found: oaty-hazelnut |
|
278 |
-------------------------------------------------------------------------- |
|
279 |
|
|
280 |
Pre: we heat up the oven to 180 degrees Celsius |
|
281 |
[...] |
|
282 |
|
|
283 |
|
|
284 |
<a id="org40457b2"></a> |
|
285 |
|
|
286 |
# Test for updated recipes |
|
287 |
|
|
288 |
But what if we update a resource? |
|
289 |
Keep the **oc logs -f** on the Operator Pod open, and in another terminal let's patch a recipe. |
|
290 |
|
|
291 |
$ oc patch cookierecipes double-dipped-shortbread --type merge \ |
|
292 |
> -p '{"spec":{"temperature":172}}' |
|
293 |
cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread patched |
|
294 |
|
|
295 |
And again in the log you should see |
|
296 |
|
|
297 |
New recipe found: double-dipped-shortbread |
|
298 |
-------------------------------------------------------------------------- |
|
299 |
|
|
300 |
Pre: we heat up the oven to 172 degrees Celsius |
|
301 |
|
|
302 |
Fetching ingredients from recipe: |
|
303 |
---------------------------------- |
|
304 |
[...] |
|
305 |
|