From e2f3034a96d2a74b245a53bf5728812649bf9a7e Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 30 Jan 2020 01:11:09 -0600 Subject: [PATCH 1/6] Fix container picture generator --- slides/markmaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides/markmaker.py b/slides/markmaker.py index aad08012..07e78062 100755 --- a/slides/markmaker.py +++ b/slides/markmaker.py @@ -28,7 +28,7 @@ class Interstitials(object): def next(self): index = self.index % len(self.images) - index += 1 + self.index += 1 return self.images[index] From ed5009c769c581cbf81eee710c816a0a05377828 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 30 Jan 2020 14:44:44 -0600 Subject: [PATCH 2/6] Tweaks after Caen --- k8s/nginx-3-with-git.yaml | 2 +- slides/k8s/concepts-k8s.md | 23 ++++++ slides/k8s/daemonset.md | 28 +++---- slides/k8s/ingress.md | 32 +++++-- slides/k8s/kubectlexpose.md | 161 ++++++++++++++++++++++++++++-------- slides/k8s/namespaces.md | 2 - slides/k8s/volumes.md | 4 +- 7 files changed, 189 insertions(+), 63 deletions(-) diff --git a/k8s/nginx-3-with-git.yaml b/k8s/nginx-3-with-git.yaml index c0ed9c96..a3ee90b0 100644 --- a/k8s/nginx-3-with-git.yaml +++ b/k8s/nginx-3-with-git.yaml @@ -13,7 +13,7 @@ spec: mountPath: /usr/share/nginx/html/ - name: git image: alpine - command: [ "sh", "-c", "apk add --no-cache git && git clone https://github.com/octocat/Spoon-Knife /www" ] + command: [ "sh", "-c", "apk add git && git clone https://github.com/octocat/Spoon-Knife /www" ] volumeMounts: - name: www mountPath: /www/ diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md index 2d07da54..59594145 100644 --- a/slides/k8s/concepts-k8s.md +++ b/slides/k8s/concepts-k8s.md @@ -10,6 +10,29 @@ --- +## What can we do with Kubernetes? + +- Let's imagine that we have a 3-tier e-commerce app: + + - web frontend + + - API backend + + - database (that we will keep out of Kubernetes for now) + +- We have built images for our frontend and backend components + + (e.g. with Dockerfiles and `docker build`) + +- We are running them successfully with a local environment + + (e.g. with Docker Compose) + +- Let's see how we would deploy our app on Kubernetes! + +--- + + ## Basic things we can ask Kubernetes to do -- diff --git a/slides/k8s/daemonset.md b/slides/k8s/daemonset.md index d6668748..fd798bb4 100644 --- a/slides/k8s/daemonset.md +++ b/slides/k8s/daemonset.md @@ -427,7 +427,7 @@ class: extra-details - We need to change the selector of the `rng` service! -- Let's add another label to that selector (e.g. `enabled=yes`) +- Let's add another label to that selector (e.g. `active=yes`) --- @@ -445,11 +445,11 @@ class: extra-details ## The plan -1. Add the label `enabled=yes` to all our `rng` pods +1. Add the label `active=yes` to all our `rng` pods -2. Update the selector for the `rng` service to also include `enabled=yes` +2. Update the selector for the `rng` service to also include `active=yes` -3. Toggle traffic to a pod by manually adding/removing the `enabled` label +3. Toggle traffic to a pod by manually adding/removing the `active` label 4. Profit! @@ -464,7 +464,7 @@ be any interruption.* ## Adding labels to pods -- We want to add the label `enabled=yes` to all pods that have `app=rng` +- We want to add the label `active=yes` to all pods that have `app=rng` - We could edit each pod one by one with `kubectl edit` ... @@ -474,9 +474,9 @@ be any interruption.* .exercise[ -- Add `enabled=yes` to all pods that have `app=rng`: +- Add `active=yes` to all pods that have `app=rng`: ```bash - kubectl label pods -l app=rng enabled=yes + kubectl label pods -l app=rng active=yes ``` ] @@ -495,7 +495,7 @@ be any interruption.* .exercise[ -- Update the service to add `enabled: yes` to its selector: +- Update the service to add `active: yes` to its selector: ```bash kubectl edit service rng ``` @@ -504,7 +504,7 @@ be any interruption.* ```wait Please edit the object below``` ```keys /app: rng``` ```key ^J``` -```keys noenabled: yes``` +```keys noactive: yes``` ```key ^[``` ] ```keys :wq``` ```key ^J``` @@ -530,7 +530,7 @@ be any interruption.* - If we want the string `"42"` or the string `"yes"`, we have to quote them -- So we have to use `enabled: "yes"` +- So we have to use `active: "yes"` .footnote[For a good laugh: if we had used "ja", "oui", "si" ... as the value, it would have worked!] @@ -542,7 +542,7 @@ be any interruption.* - Update the YAML manifest of the service -- Add `enabled: "yes"` to its selector +- Add `active: "yes"` to its selector -- Unfortunately, as of Kubernetes 1.15, the CLI cannot create daemon sets +- Unfortunately, as of Kubernetes 1.17, the CLI cannot create daemon sets -- diff --git a/slides/k8s/versions-k8s.md b/slides/k8s/versions-k8s.md index 2daf88f8..5a9cab4c 100644 --- a/slides/k8s/versions-k8s.md +++ b/slides/k8s/versions-k8s.md @@ -1,6 +1,6 @@ ## Versions installed -- Kubernetes 1.17.1 +- Kubernetes 1.17.2 - Docker Engine 19.03.5 - Docker Compose 1.24.1 From 583933c4921f94671edf12a78eadb50b3315a3b6 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 2 Feb 2020 08:16:28 -0600 Subject: [PATCH 5/6] Add kubectl delete -f and --prune --- slides/k8s/yamldeploy.md | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/slides/k8s/yamldeploy.md b/slides/k8s/yamldeploy.md index f9bf9250..12d2aec0 100644 --- a/slides/k8s/yamldeploy.md +++ b/slides/k8s/yamldeploy.md @@ -91,3 +91,52 @@ because the resources that we created lack the necessary annotation. We can safely ignore them.) +--- + +## Deleting resources + +- We can also use a YAML file to *delete* resources + +- `kubectl delete -f ...` will delete all the resources mentioned in a YAML file + + (useful to clean up everything that was created by `kubectl apply -f ...`) + +- The definitions of the resources don't matter + + (just their `kind`, `apiVersion`, and `name`) + +--- + +## Pruning¹ resources + +- We can also tell `kubectl` to remove old resources + +- This is done with `kubectl apply -f ... --prune` + +- It will remove resources that don't exist in the YAML file(s) + +- But only if they were created with `kubectl apply` in the first place + + (technically, if they have an annotation `kubectl.kubernetes.io/last-applied-configuration`) + +.footnote[¹If English is not your first language: *to prune* means to remove dead or overgrown branches in a tree, to help it to grow.] + +--- + +## YAML as source of truth + +- Imagine the following workflow: + + - do not use `kubectl run`, `kubectl create deployment`, `kubectl expose` ... + + - define everything with YAML + + - `kubectl apply -f ... --prune --all` that YAML + + - keep that YAML under version control + + - enforce all changes to go through that YAML (e.g. with pull requests) + +- Our version control system now has a full history of what we deploy + +- Compares to "Infrastructure-as-Code", but for app deployments From b988c28b41f6954b218a8d978b9c071c38160e89 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 2 Feb 2020 09:24:17 -0600 Subject: [PATCH 6/6] Extend section on API extension Mention the aggregation layer. Add an example of CRD. --- k8s/coffee-1.yaml | 15 ++++ k8s/coffee-2.yaml | 32 +++++++ k8s/coffees.yaml | 29 +++++++ slides/k8s/extending-api.md | 164 +++++++++++++++++++++++++++++++----- 4 files changed, 221 insertions(+), 19 deletions(-) create mode 100644 k8s/coffee-1.yaml create mode 100644 k8s/coffee-2.yaml create mode 100644 k8s/coffees.yaml diff --git a/k8s/coffee-1.yaml b/k8s/coffee-1.yaml new file mode 100644 index 00000000..68e5522e --- /dev/null +++ b/k8s/coffee-1.yaml @@ -0,0 +1,15 @@ +apiVersion: apiextensions.k8s.io/v1alpha1 +kind: CustomResourceDefinition +metadata: + name: coffees.container.training +spec: + group: container.training + version: v1alpha1 + scope: Namespaced + names: + plural: coffees + singular: coffee + kind: Coffee + shortNames: + - cof + diff --git a/k8s/coffee-2.yaml b/k8s/coffee-2.yaml new file mode 100644 index 00000000..cb415751 --- /dev/null +++ b/k8s/coffee-2.yaml @@ -0,0 +1,32 @@ +apiVersion: apiextensions.k8s.io/v1alpha1 +kind: CustomResourceDefinition +metadata: + name: coffees.container.training +spec: + group: container.training + version: v1alpha1 + scope: Namespaced + names: + plural: coffees + singular: coffee + kind: Coffee + shortNames: + - cof + additionalPrinterColumns: + - JSONPath: .spec.taste + description: Subjective taste of that kind of coffee bean + name: Taste + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + validation: + openAPIV3Schema: + properties: + spec: + required: + - taste + properties: + taste: + description: Subjective taste of that kind of coffee bean + type: string diff --git a/k8s/coffees.yaml b/k8s/coffees.yaml new file mode 100644 index 00000000..8adf4f35 --- /dev/null +++ b/k8s/coffees.yaml @@ -0,0 +1,29 @@ +--- +kind: Coffee +apiVersion: container.training/v1alpha1 +metadata: + name: arabica +spec: + taste: strong +--- +kind: Coffee +apiVersion: container.training/v1alpha1 +metadata: + name: robusta +spec: + taste: stronger +--- +kind: Coffee +apiVersion: container.training/v1alpha1 +metadata: + name: liberica +spec: + taste: smoky +--- +kind: Coffee +apiVersion: container.training/v1alpha1 +metadata: + name: excelsa +spec: + taste: fruity + diff --git a/slides/k8s/extending-api.md b/slides/k8s/extending-api.md index dfec6ab5..f470b160 100644 --- a/slides/k8s/extending-api.md +++ b/slides/k8s/extending-api.md @@ -8,6 +8,8 @@ We are going to cover: - Admission Webhooks +- The Aggregation Layer + --- ## Revisiting the API server @@ -46,6 +48,90 @@ We are going to cover: --- +## A very simple CRD + +The YAML below describes a very simple CRD representing different kinds of coffee: + +```yaml +apiVersion: apiextensions.k8s.io/v1alpha1 +kind: CustomResourceDefinition +metadata: + name: coffees.container.training +spec: + group: container.training + version: v1alpha1 + scope: Namespaced + names: + plural: coffees + singular: coffee + kind: Coffee + shortNames: + - cof +``` + +--- + +## Creating a CRD + +- Let's create the Custom Resource Definition for our Coffee resource + +.exercise[ + +- Load the CRD: + ```bash + kubectl apply -f ~/container.training/k8s/coffee-1.yaml + ``` + +- Confirm that it shows up: + ```bash + kubectl get crds + ``` + +] + +--- + +## Creating custom resources + +The YAML below defines a resource using the CRD that we just created: + +```yaml +kind: Coffee +apiVersion: container.training/v1alpha1 +metadata: + name: arabica +spec: + taste: strong +``` + +.exercise[ + +- Create a few types of coffee beans: + ```bash + kubectl apply -f ~/container.training/k8s/coffees.yaml + ``` + +] + +--- + +## Viewing custom resources + +- By default, `kubectl get` only shows name and age of custom resources + +.exercise[ + +- View the coffee beans that we just created: + ```bash + kubectl get coffees + ``` + +] + +- We can improve that, but it's outside the scope of this section! + +--- + ## What can we do with CRDs? There are many possibilities! @@ -81,7 +167,7 @@ There are many possibilities! - Generally, when creating a CRD, we also want to run a *controller* - (otherwise nothing will happen when we create resources of that type) + (otherwise nothing will happen when we create resources of that type) - The controller will typically *watch* our custom resources @@ -95,6 +181,22 @@ Examples: --- +## (Ab)using the API server + +- If we need to store something "safely" (as in: in etcd), we can use CRDs + +- This gives us primitives to read/write/list objects (and optionally validate them) + +- The Kubernetes API server can run on its own + + (without the scheduler, controller manager, and kubelets) + +- By loading CRDs, we can have it manage totally different objects + + (unrelated to containers, clusters, etc.) + +--- + ## Service catalog - *Service catalog* is another extension mechanism @@ -109,7 +211,7 @@ Examples: - ClusterServiceClass - ClusterServicePlan - ServiceInstance - - ServiceBinding + - ServiceBinding - It uses the Open service broker API @@ -117,17 +219,13 @@ Examples: ## Admission controllers -- When a Pod is created, it is associated with a ServiceAccount +- Admission controllers are another way to extend the Kubernetes API - (even if we did not specify one explicitly) +- Instead of creating new types, admission controllers can transform or vet API requests -- That ServiceAccount was added on the fly by an *admission controller* +- The diagram on the next slide shows the path of an API request - (specifically, a *mutating admission controller*) - -- Admission controllers sit on the API request path - - (see the cool diagram on next slide, courtesy of Banzai Cloud) + (courtesy of Banzai Cloud) --- @@ -137,7 +235,7 @@ class: pic --- -## Admission controllers +## Types of admission controllers - *Validating* admission controllers can accept/reject the API call @@ -151,7 +249,27 @@ class: pic (see [documentation](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#what-does-each-admission-controller-do) for a list) -- But we can also define our own! +- We can also dynamically define and register our own + +--- + +class: extra-details + +## Some built-in admission controllers + +- ServiceAccount: + + automatically adds a ServiceAccount to Pods that don't explicitly specify one + +- LimitRanger: + + applies resource constraints specified by LimitRange objects when Pods are created + +- NamespaceAutoProvision: + + automatically creates namespaces when an object is created in a non-existent namespace + +*Note: #1 and #2 are enabled by default; #3 is not.* --- @@ -191,19 +309,25 @@ class: pic --- -## (Ab)using the API server +## The aggregation layer -- If we need to store something "safely" (as in: in etcd), we can use CRDs +- We can delegate entire parts of the Kubernetes API to external servers -- This gives us primitives to read/write/list objects (and optionally validate them) +- This is done by creating APIService resources -- The Kubernetes API server can run on its own + (check them with `kubectl get apiservices`!) - (without the scheduler, controller manager, and kubelets) +- The APIService resource maps a type (kind) and version to an external service -- By loading CRDs, we can have it manage totally different objects +- All requests concerning that type are sent (proxied) to the external service - (unrelated to containers, clusters, etc.) +- This allows to have resources like CRDs, but that aren't stored in etcd + +- Example: `metrics-server` + + (storing live metrics in etcd would be extremely inefficient) + +- Requires significantly more work than CRDs! --- @@ -218,3 +342,5 @@ class: pic - [Built-in Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) - [Dynamic Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) + +- [Aggregation Layer](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/)