mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-09-06 03:07:15 +00:00
Merge branch 'master' into 2020-02-enix
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -61,6 +61,6 @@ TAG=$PREFIX-$SETTINGS
|
||||
--count $((3*$STUDENTS))
|
||||
|
||||
./workshopctl deploy $TAG
|
||||
./workshopctl kube $TAG 1.14.6
|
||||
./workshopctl kube $TAG 1.16.6
|
||||
./workshopctl cards $TAG
|
||||
|
||||
|
||||
+166
-61
@@ -81,7 +81,7 @@
|
||||
|
||||
## What version are we running anyway?
|
||||
|
||||
- When I say, "I'm running Kubernetes 1.11", is that the version of:
|
||||
- When I say, "I'm running Kubernetes 1.16", is that the version of:
|
||||
|
||||
- kubectl
|
||||
|
||||
@@ -139,6 +139,73 @@
|
||||
|
||||
---
|
||||
|
||||
## Important questions
|
||||
|
||||
- Should we upgrade the control plane before or after the kubelets?
|
||||
|
||||
- Within the control plane, should we upgrade the API server first or last?
|
||||
|
||||
- How often should we upgrade?
|
||||
|
||||
- How long are versions maintained?
|
||||
|
||||
- All the answers are in [the documentation about version skew policy](https://kubernetes.io/docs/setup/release/version-skew-policy/)!
|
||||
|
||||
- Let's review the key elements together ...
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes uses semantic versioning
|
||||
|
||||
- Kubernetes versions look like MAJOR.MINOR.PATCH; e.g. in 1.17.2:
|
||||
|
||||
- MAJOR = 1
|
||||
- MINOR = 17
|
||||
- PATCH = 2
|
||||
|
||||
- It's always possible to mix and match different PATCH releases
|
||||
|
||||
(e.g. 1.16.1 and 1.16.6 are compatible)
|
||||
|
||||
- It is recommended to run the latest PATCH release
|
||||
|
||||
(but it's mandatory only when there is a security advisory)
|
||||
|
||||
---
|
||||
|
||||
## Version skew
|
||||
|
||||
- API server must be more recent than its clients (kubelet and control plane)
|
||||
|
||||
- ... Which means it must always be upgraded first
|
||||
|
||||
- All components support a difference of one¹ MINOR version
|
||||
|
||||
- This allows live upgrades (since we can mix e.g. 1.15 and 1.16)
|
||||
|
||||
- It also means that going from 1.14 to 1.16 requires going through 1.15
|
||||
|
||||
.footnote[¹Except kubelet, which can be up to two MINOR behind API server,
|
||||
and kubectl, which can be one MINOR ahead or behind API server.]
|
||||
|
||||
---
|
||||
|
||||
## Release cycle
|
||||
|
||||
- There is a new PATCH relese whenever necessary
|
||||
|
||||
(every few weeks, or "ASAP" when there is a security vulnerability)
|
||||
|
||||
- There is a new MINOR release every 3 months (approximately)
|
||||
|
||||
- At any given time, three MINOR releases are maintained
|
||||
|
||||
- ... Which means that MINOR releases are maintained approximately 9 months
|
||||
|
||||
- We should expect to upgrade at least every 3 months (on average)
|
||||
|
||||
---
|
||||
|
||||
## In practice
|
||||
|
||||
- We are going to update a few cluster components
|
||||
@@ -151,47 +218,6 @@
|
||||
|
||||
---
|
||||
|
||||
## Updating kubelet
|
||||
|
||||
- These nodes have been installed using the official Kubernetes packages
|
||||
|
||||
- We can therefore use `apt` or `apt-get`
|
||||
|
||||
.exercise[
|
||||
|
||||
- Log into node `test3`
|
||||
|
||||
- View available versions for package `kubelet`:
|
||||
```bash
|
||||
apt show kubelet -a | grep ^Version
|
||||
```
|
||||
|
||||
- Upgrade kubelet:
|
||||
```bash
|
||||
sudo apt install kubelet=1.15.3-00
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Checking what we've done
|
||||
|
||||
.exercise[
|
||||
|
||||
- Log into node `test1`
|
||||
|
||||
- Check node versions:
|
||||
```bash
|
||||
kubectl get nodes -o wide
|
||||
```
|
||||
|
||||
- Create a deployment and scale it to make sure that the node still works
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Updating the API server
|
||||
|
||||
- This cluster has been deployed with kubeadm
|
||||
@@ -228,7 +254,7 @@
|
||||
sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml
|
||||
```
|
||||
|
||||
- Look for the `image:` line, and update it to e.g. `v1.15.0`
|
||||
- Look for the `image:` line, and update it to e.g. `v1.17.0`
|
||||
|
||||
]
|
||||
|
||||
@@ -249,9 +275,27 @@
|
||||
|
||||
---
|
||||
|
||||
## Was that a good idea?
|
||||
|
||||
--
|
||||
|
||||
**No!**
|
||||
|
||||
--
|
||||
|
||||
- Remember the guideline we gave earlier:
|
||||
|
||||
*To update a component, use whatever was used to install it.*
|
||||
|
||||
- This control plane was deployed with kubeadm
|
||||
|
||||
- We should use kubeadm to upgrade it!
|
||||
|
||||
---
|
||||
|
||||
## Updating the whole control plane
|
||||
|
||||
- As an example, we'll use kubeadm to upgrade the entire control plane
|
||||
- Let's make it right, and use kubeadm to upgrade the entire control plane
|
||||
|
||||
(note: this is possible only because the cluster was installed with kubeadm)
|
||||
|
||||
@@ -264,11 +308,11 @@
|
||||
|
||||
]
|
||||
|
||||
Note 1: kubeadm thinks that our cluster is running 1.15.0.
|
||||
Note 1: kubeadm thinks that our cluster is running 1.17.0.
|
||||
<br/>It is confused by our manual upgrade of the API server!
|
||||
|
||||
Note 2: kubeadm itself is still version 1.14.6.
|
||||
<br/>It doesn't know how to upgrade do 1.15.X.
|
||||
Note 2: kubeadm itself is still version 1.16.6.
|
||||
<br/>It doesn't know how to upgrade do 1.17.X.
|
||||
|
||||
---
|
||||
|
||||
@@ -290,8 +334,8 @@ Note 2: kubeadm itself is still version 1.14.6.
|
||||
|
||||
]
|
||||
|
||||
Note: kubeadm still thinks that our cluster is running 1.15.0.
|
||||
<br/>But at least it knows about version 1.15.X now.
|
||||
Note: kubeadm still thinks that our cluster is running 1.17.0.
|
||||
<br/>But at least it knows about version 1.17.X now.
|
||||
|
||||
---
|
||||
|
||||
@@ -307,28 +351,89 @@ Note: kubeadm still thinks that our cluster is running 1.15.0.
|
||||
|
||||
- Perform the upgrade:
|
||||
```bash
|
||||
sudo kubeadm upgrade apply v1.15.3
|
||||
sudo kubeadm upgrade apply v1.17.2
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Updating kubelets
|
||||
## Updating kubelet
|
||||
|
||||
- After updating the control plane, we need to update each kubelet
|
||||
- These nodes have been installed using the official Kubernetes packages
|
||||
|
||||
- This requires to run a special command on each node, to download the config
|
||||
- We can therefore use `apt` or `apt-get`
|
||||
|
||||
(this config is generated by kubeadm)
|
||||
.exercise[
|
||||
|
||||
- Log into node `test3`
|
||||
|
||||
- View available versions for package `kubelet`:
|
||||
```bash
|
||||
apt show kubelet -a | grep ^Version
|
||||
```
|
||||
|
||||
- Upgrade kubelet:
|
||||
```bash
|
||||
sudo apt install kubelet=1.17.2-00
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Checking what we've done
|
||||
|
||||
.exercise[
|
||||
|
||||
- Log into node `test1`
|
||||
|
||||
- Check node versions:
|
||||
```bash
|
||||
kubectl get nodes -o wide
|
||||
```
|
||||
|
||||
- Create a deployment and scale it to make sure that the node still works
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Was that a good idea?
|
||||
|
||||
--
|
||||
|
||||
**Almost!**
|
||||
|
||||
--
|
||||
|
||||
- Yes, kubelet was installed with distribution packages
|
||||
|
||||
- However, kubeadm took care of configuring kubelet
|
||||
|
||||
(when doing `kubeadm join ...`)
|
||||
|
||||
- We were supposed to run a special command *before* upgrading kubelet!
|
||||
|
||||
- That command should be executed on each node
|
||||
|
||||
- It will download the kubelet configuration generated by kubeadm
|
||||
|
||||
---
|
||||
|
||||
## Upgrading kubelet the right way
|
||||
|
||||
- The command that we need to run was shown by kubeadm
|
||||
|
||||
(after upgrading the control plane)
|
||||
|
||||
.exercise[
|
||||
|
||||
- Download the configuration on each node, and upgrade kubelet:
|
||||
```bash
|
||||
for N in 1 2 3; do
|
||||
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.15.3
|
||||
ssh test$N sudo apt install kubelet=1.15.3-00
|
||||
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.17.2
|
||||
ssh test$N sudo apt install kubelet=1.17.2-00
|
||||
done
|
||||
```
|
||||
]
|
||||
@@ -337,7 +442,7 @@ Note: kubeadm still thinks that our cluster is running 1.15.0.
|
||||
|
||||
## Checking what we've done
|
||||
|
||||
- All our nodes should now be updated to version 1.15.3
|
||||
- All our nodes should now be updated to version 1.17.2
|
||||
|
||||
.exercise[
|
||||
|
||||
@@ -354,12 +459,12 @@ class: extra-details
|
||||
|
||||
## Skipping versions
|
||||
|
||||
- This example worked because we went from 1.14 to 1.15
|
||||
- This example worked because we went from 1.16 to 1.17
|
||||
|
||||
- If you are upgrading from e.g. 1.13, you will generally have to go through 1.14 first
|
||||
- If you are upgrading from e.g. 1.14, you will have to go through 1.15 first
|
||||
|
||||
- This means upgrading kubeadm to 1.14.X, then using it to upgrade the cluster
|
||||
- This means upgrading kubeadm to 1.15.X, then using it to upgrade the cluster
|
||||
|
||||
- Then upgrading kubeadm to 1.15.X, etc.
|
||||
- Then upgrading kubeadm to 1.16.X, etc.
|
||||
|
||||
- **Make sure to read the release notes before upgrading!**
|
||||
|
||||
@@ -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
|
||||
|
||||
--
|
||||
|
||||
+15
-15
@@ -52,7 +52,7 @@
|
||||
|
||||
<!-- ##VERSION## -->
|
||||
|
||||
- Unfortunately, as of Kubernetes 1.15, the CLI cannot create daemon sets
|
||||
- Unfortunately, as of Kubernetes 1.17, the CLI cannot create daemon sets
|
||||
|
||||
--
|
||||
|
||||
@@ -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
|
||||
|
||||
<!--
|
||||
```wait Please edit the object below```
|
||||
@@ -566,7 +566,7 @@ If we did everything correctly, the web UI shouldn't show any change.
|
||||
|
||||
- We want to disable the pod that was created by the deployment
|
||||
|
||||
- All we have to do, is remove the `enabled` label from that pod
|
||||
- All we have to do, is remove the `active` label from that pod
|
||||
|
||||
- To identify that pod, we can use its name
|
||||
|
||||
@@ -600,7 +600,7 @@ If we did everything correctly, the web UI shouldn't show any change.
|
||||
|
||||
- In another window, remove the label from the pod:
|
||||
```bash
|
||||
kubectl label pod -l app=rng,pod-template-hash enabled-
|
||||
kubectl label pod -l app=rng,pod-template-hash active-
|
||||
```
|
||||
(The stream of HTTP logs should stop immediately)
|
||||
|
||||
@@ -623,7 +623,7 @@ class: extra-details
|
||||
|
||||
- If we scale up our cluster by adding new nodes, the daemon set will create more pods
|
||||
|
||||
- These pods won't have the `enabled=yes` label
|
||||
- These pods won't have the `active=yes` label
|
||||
|
||||
- If we want these pods to have that label, we need to edit the daemon set spec
|
||||
|
||||
|
||||
+145
-19
@@ -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/)
|
||||
|
||||
@@ -314,7 +314,7 @@ class: extra-details
|
||||
|
||||
- List all the resources created by this release:
|
||||
```bash
|
||||
kuectl get all --selector=release=java4ever
|
||||
kubectl get all --selector=release=java4ever
|
||||
```
|
||||
|
||||
]
|
||||
@@ -416,4 +416,4 @@ All unspecified values will take the default values defined in the chart.
|
||||
curl localhost:$PORT/sample/
|
||||
```
|
||||
|
||||
]
|
||||
]
|
||||
|
||||
+23
-9
@@ -120,19 +120,13 @@
|
||||
|
||||
- We want our ingress load balancer to be available on port 80
|
||||
|
||||
- We could do that with a `LoadBalancer` service
|
||||
- The best way to do that would be with a `LoadBalancer` service
|
||||
|
||||
... but it requires support from the underlying infrastructure
|
||||
|
||||
- We could use pods specifying `hostPort: 80`
|
||||
- Instead, we are going to use the `hostNetwork` mode on the Traefik pods
|
||||
|
||||
... but with most CNI plugins, this [doesn't work or requires additional setup](https://github.com/kubernetes/kubernetes/issues/23920)
|
||||
|
||||
- We could use a `NodePort` service
|
||||
|
||||
... but that requires [changing the `--service-node-port-range` flag in the API server](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
|
||||
|
||||
- Last resort: the `hostNetwork` mode
|
||||
- Let's see what this `hostNetwork` mode is about ...
|
||||
|
||||
---
|
||||
|
||||
@@ -170,6 +164,26 @@
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Other techniques to expose port 80
|
||||
|
||||
- We could use pods specifying `hostPort: 80`
|
||||
|
||||
... but with most CNI plugins, this [doesn't work or requires additional setup](https://github.com/kubernetes/kubernetes/issues/23920)
|
||||
|
||||
- We could use a `NodePort` service
|
||||
|
||||
... but that requires [changing the `--service-node-port-range` flag in the API server](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
|
||||
|
||||
- We could create a service with an external IP
|
||||
|
||||
... this would work, but would require a few extra steps
|
||||
|
||||
(figuring out the IP address and adding it to the service)
|
||||
|
||||
---
|
||||
|
||||
## Running Traefik
|
||||
|
||||
- The [Traefik documentation](https://docs.traefik.io/user-guide/kubernetes/#deploy-trfik-using-a-deployment-or-daemonset) tells us to pick between Deployment and Daemon Set
|
||||
|
||||
+126
-35
@@ -1,20 +1,76 @@
|
||||
# Exposing containers
|
||||
|
||||
- `kubectl expose` creates a *service* for existing pods
|
||||
- We can connect to our pods using their IP address
|
||||
|
||||
- A *service* is a stable address for a pod (or a bunch of pods)
|
||||
- Then we need to figure out a lot of thigns:
|
||||
|
||||
- If we want to connect to our pod(s), we need to create a *service*
|
||||
- how do we look up the IP address of the pod(s)?
|
||||
|
||||
- Once a service is created, CoreDNS will allow us to resolve it by name
|
||||
- how do we connect from outside the cluster?
|
||||
|
||||
(i.e. after creating service `hello`, the name `hello` will resolve to something)
|
||||
- how do we load balance traffic?
|
||||
|
||||
- There are different types of services, detailed on the following slides:
|
||||
- what if a pod fails?
|
||||
|
||||
- Kubernetes has a resource type named *Service*
|
||||
|
||||
- Services address all these questions!
|
||||
|
||||
---
|
||||
|
||||
## Services in a nutshell
|
||||
|
||||
- Services give us a *stable endpoint* to connect to a pod or a group of pods
|
||||
|
||||
- An easy way to create a service is to use `kubectl expose`
|
||||
|
||||
- If we have a deployment named `my-little-deploy`, we can run:
|
||||
|
||||
`kubectl expose deployment my-little-deploy --port=80`
|
||||
|
||||
... and this will create a service with the same name (`my-little-deploy`)
|
||||
|
||||
- Services are automatically added to an internal DNS zone
|
||||
|
||||
(in the example above, our code can now connect to http://my-little-deploy/)
|
||||
|
||||
---
|
||||
|
||||
## Advantages of services
|
||||
|
||||
- We don't need to look up the IP address of the pod(s)
|
||||
|
||||
(we resolve the IP address of the service using DNS)
|
||||
|
||||
- There are multiple service types; some of them allow external traffic
|
||||
|
||||
(e.g. `LoadBalancer` and `NodePort`)
|
||||
|
||||
- Services provide load balancing
|
||||
|
||||
(for both internal and external traffic)
|
||||
|
||||
- Service addresses are independent from pods' addresses
|
||||
|
||||
(when a pod fails, the service seamlessly sends traffic to its replacement)
|
||||
|
||||
---
|
||||
|
||||
## Many kinds and flavors of service
|
||||
|
||||
- There are different types of services:
|
||||
|
||||
`ClusterIP`, `NodePort`, `LoadBalancer`, `ExternalName`
|
||||
|
||||
- HTTP services can also use `Ingress` resources (more on that later)
|
||||
- There are also *headless services*
|
||||
|
||||
- Services can also have optional *external IPs*
|
||||
|
||||
- There is also another resource type called *Ingress*
|
||||
|
||||
(specifically for HTTP services)
|
||||
|
||||
- Wow, that's a lot! Let's start with the basics ...
|
||||
|
||||
---
|
||||
|
||||
@@ -73,24 +129,6 @@
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## `ExternalName`
|
||||
|
||||
- No load balancer (internal or external) is created
|
||||
|
||||
- Only a DNS entry gets added to the DNS managed by Kubernetes
|
||||
|
||||
- That DNS entry will just be a `CNAME` to a provided record
|
||||
|
||||
Example:
|
||||
```bash
|
||||
kubectl create service externalname k8s --external-name kubernetes.io
|
||||
```
|
||||
*Creates a CNAME `k8s` pointing to `kubernetes.io`*
|
||||
|
||||
---
|
||||
|
||||
## Running containers with open ports
|
||||
|
||||
- Since `ping` doesn't have anything to connect to, we'll have to run something else
|
||||
@@ -175,9 +213,7 @@ kubectl create service externalname k8s --external-name kubernetes.io
|
||||
|
||||
- As a result: you *have to* indicate the port number for your service
|
||||
|
||||
- Running services with arbitrary port (or port ranges) requires hacks
|
||||
|
||||
(e.g. host networking mode)
|
||||
(with some exceptions, like `ExternalName` or headless services, covered later)
|
||||
|
||||
---
|
||||
|
||||
@@ -218,7 +254,48 @@ Try it a few times! Our requests are load balanced across multiple pods.
|
||||
|
||||
class: extra-details
|
||||
|
||||
## If we don't need a load balancer
|
||||
## `ExternalName`
|
||||
|
||||
- Services of type `ExternalName` are quite different
|
||||
|
||||
- No load balancer (internal or external) is created
|
||||
|
||||
- Only a DNS entry gets added to the DNS managed by Kubernetes
|
||||
|
||||
- That DNS entry will just be a `CNAME` to a provided record
|
||||
|
||||
Example:
|
||||
```bash
|
||||
kubectl create service externalname k8s --external-name kubernetes.io
|
||||
```
|
||||
*Creates a CNAME `k8s` pointing to `kubernetes.io`*
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## External IPs
|
||||
|
||||
- We can add an External IP to a service, e.g.:
|
||||
```bash
|
||||
kubectl expose deploy my-little-deploy --port=80 --external-ip=1.2.3.4
|
||||
```
|
||||
|
||||
- `1.2.3.4` should be the address of one of our nodes
|
||||
|
||||
(it could also be a virtual address, service address, or VIP, shared by multiple nodes)
|
||||
|
||||
- Connections to `1.2.3.4:80` will be sent to our service
|
||||
|
||||
- External IPs will also show up on services of type `LoadBalancer`
|
||||
|
||||
(they will be added automatically by the process provisioning the load balancer)
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Headless services
|
||||
|
||||
- Sometimes, we want to access our scaled services directly:
|
||||
|
||||
@@ -238,7 +315,7 @@ class: extra-details
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Headless services
|
||||
## Creating a headless services
|
||||
|
||||
- A headless service is obtained by setting the `clusterIP` field to `None`
|
||||
|
||||
@@ -324,18 +401,32 @@ error: the server doesn't have a resource type "endpoint"
|
||||
|
||||
class: extra-details
|
||||
|
||||
## `ExternalIP`
|
||||
## The DNS zone
|
||||
|
||||
- When creating a servivce, we can also specify an `ExternalIP`
|
||||
- In the `kube-system` namespace, there should be a service named `kube-dns`
|
||||
|
||||
(this is not a type, but an extra attribute to the service)
|
||||
- This is the internal DNS server that can resolve service names
|
||||
|
||||
- It will make the service availableon this IP address
|
||||
- The default domain name for the service we created is `default.svc.cluster.local`
|
||||
|
||||
(if the IP address belongs to a node of the cluster)
|
||||
.exercise[
|
||||
|
||||
- Get the IP address of the internal DNS server:
|
||||
```bash
|
||||
IP=$(kubectl -n kube-system get svc kube-dns -o jsonpath={.spec.clusterIP})
|
||||
```
|
||||
|
||||
- Resolve the cluster IP for the `httpenv` service:
|
||||
```bash
|
||||
host httpenv.default.svc.cluster.local $IP
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## `Ingress`
|
||||
|
||||
- Ingresses are another type (kind) of resource
|
||||
|
||||
@@ -102,8 +102,6 @@
|
||||
|
||||
]
|
||||
|
||||
- Some tools like Helm will create namespaces automatically when needed
|
||||
|
||||
---
|
||||
|
||||
## Using namespaces
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Versions installed
|
||||
|
||||
- Kubernetes 1.17.1
|
||||
- Kubernetes 1.17.2
|
||||
- Docker Engine 19.03.5
|
||||
- Docker Compose 1.24.1
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class: extra-details
|
||||
|
||||
- *Volumes*:
|
||||
|
||||
- appear in Pod specifications (see next slide)
|
||||
- appear in Pod specifications (we'll see that in a few slides)
|
||||
|
||||
- do not exist as API resources (**cannot** do `kubectl get volumes`)
|
||||
|
||||
@@ -232,7 +232,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/
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user