diff --git a/prepare-labs/settings/admin-polykube.env b/prepare-labs/settings/admin-polykube.env
index 8fca0763..611105ee 100644
--- a/prepare-labs/settings/admin-polykube.env
+++ b/prepare-labs/settings/admin-polykube.env
@@ -11,6 +11,7 @@ STEPS="
clusterize
tools
kubepkgs
+ kubebins
createuser
webssh
tailhist
diff --git a/slides/k8s/dmuc.md b/slides/k8s/dmuc-easy.md
similarity index 95%
rename from slides/k8s/dmuc.md
rename to slides/k8s/dmuc-easy.md
index 7d5edbf7..001ad51d 100644
--- a/slides/k8s/dmuc.md
+++ b/slides/k8s/dmuc-easy.md
@@ -1,4 +1,4 @@
-# Building our own cluster
+# Building our own cluster (easy)
- Let's build our own cluster!
@@ -33,10 +33,7 @@
## Our environment
-- We will use the machine indicated as `dmuc1`
-
- (this stands for "Dessine Moi Un Cluster" or "Draw Me A Sheep",
-
in homage to Saint-Exupery's "The Little Prince")
+- We will use the machine indicated as `monokube1`
- This machine:
@@ -48,13 +45,33 @@
---
+## The fine print
+
+- We're going to use a *very old* version of Kubernetes
+
+ (specifically, 1.19)
+
+- Why?
+
+- It's much easier to set up than recent versions
+
+ - it's compatible with Docker (no need to set up CNI)
+
+ - it doesn't require a ServiceAccount keypair
+
+ - it can be exposed over plain HTTP (insecure but easier)
+
+- We'll do that, and later, move to recent versions of Kubernetes!
+
+---
+
## Checking our environment
- Let's make sure we have everything we need first
.lab[
-- Log into the `dmuc1` machine
+- Log into the `monokube1` machine
- Get root:
```bash
@@ -547,7 +564,7 @@ Success!
Our node should show up.
-Its name will be its hostname (it should be `dmuc1`).
+Its name will be its hostname (it should be `monokube1`).
---
diff --git a/slides/k8s/dmuc-hard.md b/slides/k8s/dmuc-hard.md
new file mode 100644
index 00000000..572b8fd9
--- /dev/null
+++ b/slides/k8s/dmuc-hard.md
@@ -0,0 +1,374 @@
+# Building our own cluster (hard)
+
+- This section assumes that you already went through
+
+ *“Building our own cluster (medium)”*
+
+- In that previous section, we built a cluster with a single node
+
+- In this new section, we're going to add more nodes to the cluster
+
+- Note: we will need the lab environment of that previous section
+
+- If you haven't done it yet, you should go through that section first
+
+---
+
+## Our environment
+
+- On `polykube1`, we should have our Kubernetes control plane
+
+- We're also assuming that we have the kubeconfig file created earlier
+
+ (in `~/.kube/config`)
+
+- We're going to work on `polykube2` and add it to the cluster
+
+- This machine has exactly the same setup as `polykube1`
+
+ (Ubuntu LTS with CNI, etcd, and Kubernetes binaries installed)
+
+- Note that we won't need the etcd binaries here
+
+ (the control plane will run solely on `polykube1`)
+
+---
+
+## Checklist
+
+We need to:
+
+- generate the kubeconfig file for `polykube2`
+
+- install a container engine
+
+- generate a CNI configuration file
+
+- start kubelet
+
+---
+
+## Generating the kubeconfig file
+
+- Ideally, we should generate a key pair and certificate for `polykube2`...
+
+- ...and generate a kubeconfig file using these
+
+- At the moment, for simplicity, we'll use the same key pair and certificate as earlier
+
+- We have a couple of options:
+
+ - copy the required files (kubeconfig, key pair, certificate)
+
+ - "flatten" the kubeconfig file (embed the key and certificate within)
+
+---
+
+class: extra-details
+
+## To flatten or not to flatten?
+
+- "Flattening" the kubeconfig file can seem easier
+
+ (because it means we'll only have one file to move around)
+
+- But it's easier to rotate the key or renew the certificate when they're in separate files
+
+---
+
+## Flatten and copy the kubeconfig file
+
+- We'll flatten the file and copy it over
+
+.lab[
+
+- On `polykube1`, flatten the kubeconfig file:
+ ```bash
+ kubectl config view --flatten > kubeconfig
+ ```
+
+- Then copy it to `polykube2`:
+ ```bash
+ scp kubeconfig polykube2:
+ ```
+
+]
+
+---
+
+## Generate CNI configuration
+
+Back on `polykube2`, put the following in `/etc/cni/net.d/kube.conf`:
+
+```json
+{
+ "cniVersion": "0.3.1",
+ "name": "kube",
+ "type": "bridge",
+ "bridge": "cni0",
+ "isDefaultGateway": true,
+ "ipMasq": true,
+ "hairpinMode": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": `"10.1.2.0/24"`
+ }
+}
+```
+
+Note how we changed the subnet!
+
+---
+
+## Install container engine and start `kubelet`
+
+.lab[
+
+- Install `containerd`:
+ ```bash
+ sudo apt-get install containerd -y
+ ```
+
+- Start `containerd`:
+ ```bash
+ sudo systemctl start containerd
+ ```
+
+- Start `kubelet`:
+ ```bash
+ sudo kubelet --kubeconfig kubeconfig
+ ```
+
+]
+
+We're getting errors looking like:
+```
+"Post \"https://localhost:6443/api/v1/nodes\": ... connect: connection refused"
+```
+
+---
+
+## Updating the kubeconfig file
+
+- Our kubeconfig file still references `localhost:6443`
+
+- This was fine on `polykube1`
+
+ (where `kubelet` was connecting to the control plane running locally)
+
+- On `polykube2`, we need to change that and put the address of the API server
+
+ (i.e. the address of `polykube1`)
+
+.lab[
+
+- Update the `kubeconfig` file:
+ ```bash
+ sed -i s/localhost:6443/polykube1:6443/ kubeconfig
+ ```
+
+]
+
+---
+
+## Starting `kubelet`
+
+- `kubelet` should now start correctly (hopefully!)
+
+.lab[
+
+- On `polykube2`, start `kubelet`:
+ ```bash
+ sudo kubelet --kubeconfig kubeconfig
+ ```
+
+- On `polykube1`, check that `polykube2` shows up and is `Ready`:
+ ```bash
+ kubectl get nodes
+ ```
+
+]
+
+---
+
+## Testing connectivity
+
+- From `polykube1`, can we connect to Pods running on `polykube2`? 🤔
+
+.lab[
+
+- Scale the test Deployment:
+ ```bash
+ kubectl scale deployment blue --replicas=5
+ ```
+
+- Get the IP addresses of the Pods:
+ ```bash
+ kubectl get pods -o wide
+ ```
+
+- Pick a Pod on `polykube2` and try to connect to it:
+ ```bash
+ curl `10.1.2.2`
+ ```
+
+]
+
+--
+
+At that point, it doesn't work.
+
+---
+
+## Refresher on the *pod network*
+
+- The *pod network* (or *pod-to-pod network*) has a few responsibilities:
+
+ - allocating and managing Pod IP addresses
+
+ - connecting Pods and Nodes
+
+ - connecting Pods together on a given node
+
+ - *connecting Pods together across nodes*
+
+- That last part is the one that's not functioning in our cluster
+
+- It typically requires some combination of routing, tunneling, bridging...
+
+---
+
+## Connecting networks together
+
+- We can add manual routes between our nodes
+
+- This requires adding `N x (N-1)` routes
+
+ (on each node, add a route to every other node)
+
+- This will work on home labs where nodes are directly connected
+
+ (e.g. on an Ethernet switch, or same WiFi network, or a bridge between local VMs)
+
+- ...Or on clouds where IP address filtering has been disabled
+
+ (by default, most cloud providers will discard packets going to unknown IP addresses)
+
+- If IP address filtering is enabled, you'll have to use e.g. tunneling or overlay networks
+
+---
+
+## Important warning
+
+- The technique that we are about to use doesn't work everywhere
+
+- It only works if:
+
+ - all the nodes are directly connected to each other (at layer 2)
+
+ - the underlying network allows the IP addresses of our pods
+
+- If we are on physical machines connected by a switch: OK
+
+- If we are on virtual machines in a public cloud: NOT OK
+
+ - on AWS, we need to disable "source and destination checks" on our instances
+
+ - on OpenStack, we need to disable "port security" on our network ports
+
+---
+
+## Routing basics
+
+- We need to tell *each* node:
+
+ "The subnet 10.1.N.0/24 is located on node N" (for all values of N)
+
+- This is how we add a route on Linux:
+ ```bash
+ ip route add 10.1.N.0/24 via W.X.Y.Z
+ ```
+
+ (where `W.X.Y.Z` is the internal IP address of node N)
+
+- We can see the internal IP addresses of our nodes with:
+ ```bash
+ kubectl get nodes -o wide
+ ```
+
+---
+
+## Adding our route
+
+- Let's add a route from `polykube1` to `polykube2`
+
+.lab[
+
+- Check the internal address of `polykube2`:
+ ```bash
+ kubectl get node polykube2 -o wide
+ ```
+
+- Now, on `polykube1`, add the route to the Pods running on `polykube2`:
+ ```bash
+ sudo ip route add 10.1.2.0/24 via `A.B.C.D`
+ ```
+
+- Finally, check that we can now connect to a Pod running on `polykube2`:
+ ```bash
+ curl 10.1.2.2
+ ```
+
+]
+
+---
+
+## What's next?
+
+- The network configuration feels very manual:
+
+ - we had to generate the CNI configuration file (in `/etc/cni/net.d`)
+
+ - we had to manually update the nodes' routing tables
+
+- Can we automate that?
+
+ **YES!**
+
+- We could install something like [kube-router](https://www.kube-router.io/)
+
+ (which specifically takes care of the CNI configuration file and populates routing tables)
+
+- Or we could also go with e.g. [Cilium](https://cilium.io/)
+
+---
+
+class: extra-details
+
+## If you want to try Cilium...
+
+- Add the `--root-ca-file` flag to the controller manager:
+
+ - use the certificate automatically generated by the API server
+
+ (it should be in `/var/run/kubernetes/apiserver.crt`)
+
+ - or generate a key pair and certificate for the API server and point to
+ that certificate
+
+ - without that, you'll get certificate validation errors
+
+ (because in our Pods, the `ca.crt` file used to validate the API server will be empty)
+
+- Check the Cilium [without kube-proxy][ciliumwithoutkubeproxy] instructions
+
+ (make sure to pass the API server IP address and port!)
+
+- Other pod-to-pod network implementations might also require additional steps
+
+[ciliumwithoutkubeproxy]: https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/#kubeproxy-free
+
+???
+
+:EN:- Connecting nodes and pods
+:FR:- Interconnecter les nœuds et les pods
diff --git a/slides/k8s/dmuc-medium.md b/slides/k8s/dmuc-medium.md
new file mode 100644
index 00000000..70541513
--- /dev/null
+++ b/slides/k8s/dmuc-medium.md
@@ -0,0 +1,891 @@
+# Building our own cluster (medium)
+
+- This section assumes that you already went through
+
+ *“Building our own cluster (easy)”*
+
+- In that section, we saw how to run each control plane component manually...
+
+ ...but with an older version of Kubernetes (1.19)
+
+- In this section, we're going to do something similar...
+
+ ...but with recent versions of Kubernetes!
+
+- Note: we won't need the lab environment of that previous section
+
+ (we're going to build a new cluster from scratch)
+
+---
+
+## What remains the same
+
+- We'll use machines with Kubernetes binaries pre-downloaded
+
+- We'll run individual components by hand
+
+ (etcd, API server, controller manager, scheduler, kubelet)
+
+- We'll run on a single node
+
+ (but we'll be laying the groundwork to add more nodes)
+
+- We'll get the cluster to the point where we can run and expose pods
+
+---
+
+## What's different
+
+- We'll need to generate TLS keys and certificates
+
+ (because it's mandatory with recent versions of Kubernetes)
+
+- Things will be *a little bit more* secure
+
+ (but still not 100% secure, far from it!)
+
+- We'll use containerd instead of Docker
+
+ (you could probably try with CRI-O or another CRI engine, too)
+
+- We'll need to set up CNI for networking
+
+- *And we won't do everything as root this time (but we might use `sudo` a lot)*
+
+---
+
+## Our environment
+
+- We will use the machine indicated as `polykube1`
+
+- This machine:
+
+ - runs Ubuntu LTS
+
+ - has Kubernetes, etcd, and CNI binaries installed
+
+ - but nothing is running
+
+---
+
+## Checking our environment
+
+- Let's make sure we have everything we need first
+
+.lab[
+
+- Log into the `polykube1` machine
+
+- Check available versions:
+ ```bash
+ etcd -version
+ kube-apiserver --version
+ ```
+
+]
+
+---
+
+## The plan
+
+We'll follow the same methodology as for the "easy" section
+
+1. Start API server
+
+2. Interact with it (create Deployment and Service)
+
+3. See what's broken
+
+4. Fix it and go back to step 2 until it works!
+
+---
+
+## Dealing with multiple processes
+
+- Again, we are going to start many processes
+
+- Depending on what you're comfortable with, you can:
+
+ - open multiple windows and multiple SSH connections
+
+ - use a terminal multiplexer like screen or tmux
+
+ - put processes in the background with `&`
+
(warning: log output might get confusing to read!)
+
+---
+
+## Starting API server
+
+.lab[
+
+- Try to start the API server:
+ ```bash
+ kube-apiserver
+ # It will complain about permission to /var/run/kubernetes
+
+ sudo kube-apiserver
+ # Now it will complain about a bunch of missing flags, including:
+ # --etcd-servers
+ # --service-account-issuer
+ # --service-account-signing-key-file
+ ```
+
+]
+
+Just like before, we'll need to start etcd.
+
+But we'll also need some TLS keys!
+
+---
+
+## Generating TLS keys
+
+- There are many ways to generate TLS keys (and certificates)
+
+- A very popular and modern tool to do that is [cfssl]
+
+- We're going to use the old-fashioned [openssl] CLI
+
+- Feel free to use cfssl or any other tool if you prefer!
+
+[cfssl]: https://github.com/cloudflare/cfssl#using-the-command-line-tool
+[openssl]: https://www.openssl.org/docs/man3.0/man1/
+
+---
+
+## How many keys do we need?
+
+At the very least, we need the following two keys:
+
+- ServiceAccount key pair
+
+- API client key pair, aka "CA key"
+
+ (technically, we will need a *certificate* for that key pair)
+
+But if we wanted to tighten the cluster security, we'd need many more...
+
+---
+
+## The other keys
+
+These keys are not strictly necessary at this point:
+
+- etcd key pair
+
+ *without that key, communication with etcd will be insecure*
+
+- API server endpoint key pair
+
+ *the API server will generate this one automatically if we don't*
+
+- kubelet key pair (used by API server to connect to kubelets)
+
+ *without that key, commands like kubectl logs/exec will be insecure*
+
+---
+
+## Would you like some auth with that?
+
+If we want to enable authentication and authorization, we also need various API client key pairs signed by the "CA key" mentioned earlier. That would include (non-exhaustive list):
+
+- controller manager key pair
+
+- scheduler key pair
+
+- in most cases: kube-proxy (or equivalent) key pair
+
+- in most cases: key pairs for the nodes joining the cluster
+
+ (these might be generated through TLS bootstrap tokens)
+
+- key pairs for users that will interact with the clusters
+
+ (unless another authentication mechanism like OIDC is used)
+
+---
+
+## Generating our keys and certificates
+
+.lab[
+
+- Generate the ServiceAccount key pair:
+ ```bash
+ openssl genrsa -out sa.key 2048
+ ```
+
+- Generate the CA key pair:
+ ```bash
+ openssl genrsa -out ca.key 2048
+ ```
+
+- Generate a self-signed certificate for the CA key:
+ ```bash
+ openssl x509 -new -key ca.key -out ca.cert -subj /CN=kubernetes/
+ ```
+
+]
+
+---
+
+## Starting etcd
+
+- This one is easy!
+
+.lab[
+
+- Start etcd:
+ ```bash
+ etcd
+ ```
+
+]
+
+Note: if you want a bit of extra challenge, you can try
+to generate the etcd key pair and use it.
+
+(You will need to pass it to etcd and to the API server.)
+
+---
+
+## Starting API server
+
+- We need to use the keys and certificate that we just generated
+
+.lab[
+
+- Start the API server:
+ ```bash
+ sudo kube-apiserver \
+ --etcd-servers=http://localhost:2379 \
+ --service-account-signing-key-file=sa.key \
+ --service-account-issuer=https://kubernetes \
+ --service-account-key-file=sa.key \
+ --client-ca-file=ca.cert
+ ```
+
+]
+
+The API server should now start.
+
+But can we really use it? 🤔
+
+---
+
+## Trying `kubectl`
+
+- Let's try some simple `kubectl` command
+
+.lab[
+
+- Try to list Namespaces:
+ ```bash
+ kubectl get namespaces
+ ```
+
+]
+
+We're getting an error message like this one:
+
+```
+The connection to the server localhost:8080 was refused -
+did you specify the right host or port?
+```
+
+---
+
+## What's going on?
+
+- Recent versions of Kubernetes don't support unauthenticated API access
+
+- The API server doesn't support listening on plain HTTP anymore
+
+- `kubectl` still tries to connect to `localhost:8080` by default
+
+- But there is nothing listening there
+
+- Our API server listens on port 6443, using TLS
+
+---
+
+## Trying to access the API server
+
+- Let's use `curl` first to confirm that everything works correctly
+
+ (and then we will move to `kubectl`)
+
+.lab[
+
+- Try to connect with `curl`:
+ ```bash
+ curl https://localhost:6443
+ # This will fail because the API server certificate is unknown.
+ ```
+
+- Try again, skipping certificate verification:
+ ```bash
+ curl --insecure https://localhost:6443
+ ```
+
+]
+
+We should now see an `Unauthorized` Kubernetes API error message.
+
+We need to authenticate with our key and certificate.
+
+---
+
+## Authenticating with the API server
+
+- For the time being, we can use the CA key and cert directly
+
+- In a real world scenario, we would *never* do that!
+
+ (because we don't want the CA key to be out there in the wild)
+
+.lab[
+
+- Try again, skipping cert verification, and using the CA key and cert:
+ ```bash
+ curl --insecure --key ca.key --cert ca.cert https://localhost:6443
+ ```
+
+]
+
+We should see a list of API routes.
+
+---
+
+class: extra-details
+
+## Doing it right
+
+In the future, instead of using the CA key and certificate,
+we should generate a new key, and a certificate for that key,
+signed by the CA key.
+
+Then we can use that new key and certificate to authenticate.
+
+Example:
+
+```
+### Generate a key pair
+openssl genrsa -out user.key
+
+### Extract the public key
+openssl pkey -in user.key -out user.pub -pubout
+
+### Generate a certificate signed by the CA key
+openssl x509 -new -key ca.key -force_pubkey user.pub -out user.cert \
+ -subj /CN=kubernetes-user/
+```
+
+---
+
+## Writing a kubeconfig file
+
+- We now want to use `kubectl` instead of `curl`
+
+- We'll need to write a kubeconfig file for `kubectl`
+
+- There are many way to do that; here, we're going to use `kubectl config`
+
+- We'll need to:
+
+ - set the "cluster" (API server endpoint)
+
+ - set the "credentials" (the key and certficate)
+
+ - set the "context" (referencing the cluster and credentials)
+
+ - use that context (make it the default that `kubectl` will use)
+
+---
+
+## Set the cluster
+
+The "cluster" section holds the API server endpoint.
+
+.lab[
+
+- Set the API server endpoint:
+ ```bash
+ kubectl config set-cluster polykube --server=https://localhost:6443
+ ```
+
+- Don't verify the API server certificate:
+ ```bash
+ kubectl config set-cluster polykube --insecure-skip-tls-verify
+ ```
+
+]
+
+---
+
+## Set the credentials
+
+The "credentials" section can hold a TLS key and certificate, or a token, or configuration information for a plugin (for instance, when using AWS EKS or GCP GKE, they use a plugin).
+
+.lab[
+
+- Set the client key and certificate:
+ ```bash
+ kubectl config set-credentials polykube \
+ --client-key ca.key \
+ --client-certificate ca.cert
+ ```
+
+]
+
+---
+
+## Set and use the context
+
+The "context" section references the "cluster" and "credentials" that we defined earlier.
+
+(It can also optionally reference a Namespace.)
+
+.lab[
+
+- Set the "context":
+ ```bash
+ kubectl config set-context polykube --cluster polykube --user polykube
+ ```
+
+- Set that context to be the default context:
+ ```bash
+ kubectl config use-context polykube
+ ```
+
+]
+
+---
+
+## Review the kubeconfig filfe
+
+The kubeconfig file should look like this:
+
+.small[
+```yaml
+apiVersion: v1
+clusters:
+- cluster:
+ insecure-skip-tls-verify: true
+ server: https://localhost:6443
+ name: polykube
+contexts:
+- context:
+ cluster: polykube
+ user: polykube
+ name: polykube
+current-context: polykube
+kind: Config
+preferences: {}
+users:
+- name: polykube
+ user:
+ client-certificate: /root/ca.cert
+ client-key: /root/ca.key
+```
+]
+
+---
+
+## Trying the kubeconfig file
+
+- We should now be able to access our cluster's API!
+
+.lab[
+
+- Try to list Namespaces:
+ ```bash
+ kubectl get namespaces
+ ```
+]
+
+This should show the classic `default`, `kube-system`, etc.
+
+---
+
+class: extra-details
+
+## Do we need `--client-ca-file` ?
+
+Technically, we didn't need to specify the `--client-ca-file` flag!
+
+But without that flag, no client can be authenticated.
+
+Which means that we wouldn't be able to issue any API request!
+
+---
+
+## Running pods
+
+- We can now try to create a Deployment
+
+.lab[
+
+- Create a Deployment:
+ ```bash
+ kubectl create deployment blue --image=jpetazzo/color
+ ```
+
+- Check the results:
+ ```bash
+ kubectl get deployments,replicasets,pods
+ ```
+
+]
+
+Our Deployment exists, but not the Replica Set or Pod.
+
+We need to run the controller manager.
+
+---
+
+## Running the controller manager
+
+- Previously, we used the `--master` flag to pass the API server address
+
+- Now, we need to authenticate properly
+
+- The simplest way at this point is probably to use the same kubeconfig file!
+
+.lab[
+
+- Start the controller manager:
+ ```bash
+ kube-controller-manager --kubeconfig .kube/config
+ ```
+
+- Check the results:
+ ```bash
+ kubectl get deployments,replicasets,pods
+ ```
+
+]
+
+---
+
+## What's next?
+
+- Normally, the last commands showed us a Pod in `Pending` state
+
+- We need two things to continue:
+
+ - the scheduler (to assign the Pod to a Node)
+
+ - a Node!
+
+- We're going to run `kubelet` to register the Node with the cluster
+
+---
+
+## Running `kubelet`
+
+- Let's try to run `kubelet` and see what happens!
+
+.lab[
+
+- Start `kubelet`:
+ ```bash
+ sudo kubelet
+ ```
+
+]
+
+We should see an error about connecting to `containerd.sock`.
+
+We need to run a container engine!
+
+(For instance, `containerd`.)
+
+---
+
+## Running `containerd`
+
+- We need to install and start `containerd`
+
+- You could try another engine if you wanted
+
+ (but there might be complications!)
+
+.lab[
+
+- Install `containerd`:
+ ```bash
+ sudo apt-get install containerd
+ ```
+
+- Start `containerd`:
+ ```bash
+ sudo containerd
+ ```
+
+]
+
+---
+
+class: extra-details
+
+## Configuring `containerd`
+
+Depending on how we install `containerd`, it might need a bit of extra configuration.
+
+Watch for the following symptoms:
+
+- `containerd` refuses to start
+
+ (rare, unless there is an *invalid* configuration)
+
+- `containerd` starts but `kubelet` can't connect
+
+ (could be the case if the configuration disables the CRI socket)
+
+- `containerd` starts and things work but Pods keep being killed
+
+ (may happen if there is a mismatch in the cgroups driver)
+
+---
+
+## Starting `kubelet` for good
+
+- Now that `containerd` is running, `kubelet` should start!
+
+.lab[
+
+- Try to start `kubelet`:
+ ```bash
+ sudo kubelet
+ ```
+
+- In another terminal, check if our Node is now visible:
+ ```bash
+ sudo kubectl get nodes
+ ```
+
+]
+
+`kubelet` should now start, but our Node doesn't show up in `kubectl get nodes`!
+
+This is because without a kubeconfig file, `kubelet` runs in standalone mode:
+
+it will not connect to a Kubernetes API server, and will only start *static pods*.
+
+---
+
+## Passing the kubeconfig file
+
+- Let's start `kubelet` again, with our kubeconfig file
+
+.lab[
+
+- Stop `kubelet` (e.g. with `Ctrl-C`)
+
+- Restart it with the kubeconfig file:
+ ```bash
+ sudo kubelet --kubeconfig .kube/config
+ ```
+
+- Check our list of Nodes:
+ ```bash
+ kubectl get nodes
+ ```
+
+]
+
+This time, our Node should show up!
+
+---
+
+## Node readiness
+
+- However, our Node shows up as `NotReady`
+
+- If we wait a few minutes, the `kubelet` logs will tell us why:
+
+ *we're missing a CNI configuration!*
+
+- As a result, the containers can't be connected to the network
+
+- `kubelet` detects that and doesn't become `Ready` until this is fixed
+
+---
+
+## CNI configuration
+
+- We need to provide a CNI configuration
+
+- This is a file in `/etc/cni/net.d`
+
+ (the name of the file doesn't matter; the first file in lexicographic order will be used)
+
+- Usually, when installing a "CNI plugin¹", this file gets installed automatically
+
+- Here, we are going to write that file manually
+
+.footnote[¹Technically, a *pod network*; typically running as a DaemonSet, which will install the file with a `hostPath` volume.]
+
+---
+
+## Our CNI configuration
+
+Create the following file in e.g. `/etc/cni/net.d/kube.conf`:
+
+```json
+{
+ "cniVersion": "0.3.1",
+ "name": "kube",
+ "type": "bridge",
+ "bridge": "cni0",
+ "isDefaultGateway": true,
+ "ipMasq": true,
+ "hairpinMode": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.1.1.0/24"
+ }
+}
+```
+
+That's all we need - `kubelet` will detect and validate the file automatically!
+
+---
+
+## Checking our Node again
+
+- After a short time (typically about 10 seconds) the Node should be `Ready`
+
+.lab[
+
+- Wait until the Node is `Ready`:
+ ```bash
+ kubectl get nodes
+ ```
+
+]
+
+If the Node doesn't show up as `Ready`, check the `kubelet` logs.
+
+---
+
+## What's next?
+
+- At this point, we have a `Pending` Pod and a `Ready` Node
+
+- All we need is the scheduler to bind the former to the latter
+
+.lab[
+
+- Run the scheduler:
+ ```bash
+ kube-scheduler --kubeconfig .kube/config
+ ```
+
+- Check that the Pod gets assigned to the Node and becomes `Running`:
+ ```bash
+ kubectl get pods
+ ```
+
+]
+
+---
+
+## Check network access
+
+- Let's check that we can connect to our Pod, and that the Pod can connect outside
+
+.lab[
+
+- Get the Pod's IP address:
+ ```bash
+ kubectl get pods -o wide
+ ```
+
+- Connect to the Pod (make sure to update the IP address):
+ ```bash
+ curl `10.1.1.2`
+ ```
+
+- Check that the Pod has external connectivity too:
+ ```bash
+ kubectl exec `blue-xxxxxxxxxx-yyyyy` -- ping -c3 1.1
+ ```
+
+]
+
+---
+
+## Expose our Deployment
+
+- We can now try to expose the Deployment and connect to the ClusterIP
+
+.lab[
+
+- Expose the Deployment:
+ ```bash
+ kubectl expose deployment blue --port=80
+ ```
+
+- Retrieve the ClusterIP:
+ ```bash
+ kubectl get services
+ ```
+
+- Try to connect to the ClusterIP:
+ ```bash
+ curl `10.0.0.42`
+ ```
+]
+
+At this point, it won't work - we need to run `kube-proxy`!
+
+---
+
+## Running `kube-proxy`
+
+- We need to run `kube-proxy`
+
+ (also passing it our kubeconfig file)
+
+.lab[
+
+- Run `kube-proxy`:
+ ```bash
+ sudo kube-proxy --kubeconfig .kube/config
+ ```
+
+- Try again to connect to the ClusterIP:
+ ```bash
+ curl `10.0.0.42`
+ ```
+
+]
+
+This time, it should work.
+
+---
+
+## What's next?
+
+- Scale up the Deployment, and check that load balancing works properly
+
+- Enable RBAC, and generate individual certificates for each controller
+
+ (check the [certificate paths][certpath] section in the Kubernetes documentatio
+ for a detailed list of all the certificates and keys that are used by the
+ control plane, and which flags are used by which components to configure them!)
+
+- Add more nodes to the cluster
+
+*Feel free to try these if you want to get additional hands-on experience!*
+
+[certpath]: https://kubernetes.io/docs/setup/best-practices/certificates/#certificate-paths
+
+???
+
+:EN:- Setting up control plane certificates
+:EN:- Implementing a basic CNI configuration
+:FR:- Mettre en place les certificats du plan de contrôle
+:FR:- Réaliser un configuration CNI basique
diff --git a/slides/kadm-fullday.yml b/slides/kadm-fullday.yml
index 9e6adf0b..b4f3c193 100644
--- a/slides/kadm-fullday.yml
+++ b/slides/kadm-fullday.yml
@@ -32,12 +32,14 @@ content:
- k8s/architecture.md
#- k8s/internal-apis.md
- k8s/deploymentslideshow.md
- - k8s/dmuc.md
+ - k8s/dmuc-easy.md
-
- - k8s/multinode.md
- - k8s/cni.md
+ - k8s/dmuc-medium.md
+ - k8s/dmuc-hard.md
+ #- k8s/multinode.md
+ #- k8s/cni.md
- k8s/cni-internals.md
- - k8s/interco.md
+ #- k8s/interco.md
-
- k8s/apilb.md
#- k8s/setup-overview.md
diff --git a/slides/kadm-twodays.yml b/slides/kadm-twodays.yml
index 6640624f..334c93bd 100644
--- a/slides/kadm-twodays.yml
+++ b/slides/kadm-twodays.yml
@@ -32,11 +32,13 @@ content:
- k8s/architecture.md
- k8s/internal-apis.md
- k8s/deploymentslideshow.md
- - k8s/dmuc.md
-- - k8s/multinode.md
- - k8s/cni.md
+ - k8s/dmuc-easy.md
+- - k8s/dmuc-medium.md
+ - k8s/dmuc-hard.md
+ #- k8s/multinode.md
+ #- k8s/cni.md
- k8s/cni-internals.md
- - k8s/interco.md
+ #- k8s/interco.md
- - k8s/apilb.md
- k8s/setup-overview.md
#- k8s/setup-devel.md
diff --git a/slides/kube-adv.yml b/slides/kube-adv.yml
index 68cb59f9..627d71bc 100644
--- a/slides/kube-adv.yml
+++ b/slides/kube-adv.yml
@@ -30,13 +30,15 @@ content:
- k8s/architecture.md
- k8s/internal-apis.md
- k8s/deploymentslideshow.md
- - k8s/dmuc.md
+ - k8s/dmuc-easy.md
- #2
- - k8s/multinode.md
- - k8s/cni.md
- - k8s/interco.md
-- #3
+ - k8s/dmuc-medium.md
+ - k8s/dmuc-hard.md
+ #- k8s/multinode.md
+ #- k8s/cni.md
+ #- k8s/interco.md
- k8s/cni-internals.md
+- #3
- k8s/apilb.md
- k8s/control-plane-auth.md
- |
diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml
index 3e72697e..91d4f4c9 100644
--- a/slides/kube-selfpaced.yml
+++ b/slides/kube-selfpaced.yml
@@ -151,9 +151,11 @@ content:
- k8s/owners-and-dependents.md
- k8s/events.md
-
- - k8s/dmuc.md
- - k8s/multinode.md
- - k8s/cni.md
+ - k8s/dmuc-easy.md
+ - k8s/dmuc-medium.md
+ - k8s/dmuc-hard.md
+ #- k8s/multinode.md
+ #- k8s/cni.md
- k8s/cni-internals.md
- k8s/apilb.md
- k8s/staticpods.md