diff --git a/prepare-vms/lib/ips-txt-to-html.py b/prepare-vms/lib/ips-txt-to-html.py
index fd576afc..5bc1873c 100755
--- a/prepare-vms/lib/ips-txt-to-html.py
+++ b/prepare-vms/lib/ips-txt-to-html.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import os
import sys
import yaml
diff --git a/prepare-vms/settings/admin-dmuc.yaml b/prepare-vms/settings/admin-dmuc.yaml
index 70ac6ec6..a7d776a8 100644
--- a/prepare-vms/settings/admin-dmuc.yaml
+++ b/prepare-vms/settings/admin-dmuc.yaml
@@ -5,7 +5,7 @@ clustersize: 1
clusterprefix: dmuc
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: admin.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: A4
diff --git a/prepare-vms/settings/admin-kubenet.yaml b/prepare-vms/settings/admin-kubenet.yaml
index 8256fc65..52046831 100644
--- a/prepare-vms/settings/admin-kubenet.yaml
+++ b/prepare-vms/settings/admin-kubenet.yaml
@@ -5,7 +5,7 @@ clustersize: 3
clusterprefix: kubenet
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: admin.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: A4
diff --git a/prepare-vms/settings/admin-kuberouter.yaml b/prepare-vms/settings/admin-kuberouter.yaml
index 3f903065..f894c5f4 100644
--- a/prepare-vms/settings/admin-kuberouter.yaml
+++ b/prepare-vms/settings/admin-kuberouter.yaml
@@ -5,7 +5,7 @@ clustersize: 3
clusterprefix: kuberouter
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: admin.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: A4
diff --git a/prepare-vms/settings/admin-test.yaml b/prepare-vms/settings/admin-test.yaml
index ac9679be..57d1339b 100644
--- a/prepare-vms/settings/admin-test.yaml
+++ b/prepare-vms/settings/admin-test.yaml
@@ -5,7 +5,7 @@ clustersize: 3
clusterprefix: test
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: admin.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: A4
diff --git a/prepare-vms/settings/enix.yaml b/prepare-vms/settings/enix.yaml
deleted file mode 100644
index 075efb34..00000000
--- a/prepare-vms/settings/enix.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# Number of VMs per cluster
-clustersize: 1
-
-# The hostname of each node will be clusterprefix + a number
-clusterprefix: node
-
-# Jinja2 template to use to generate ready-to-cut cards
-cards_template: enix.html
-
-# Use "Letter" in the US, and "A4" everywhere else
-paper_size: A4
-
-# Feel free to reduce this if your printer can handle it
-paper_margin: 0.2in
-
-# Note: paper_size and paper_margin only apply to PDF generated with pdfkit.
-# If you print (or generate a PDF) using ips.html, they will be ignored.
-# (The equivalent parameters must be set from the browser's print dialog.)
-
-# This can be "test" or "stable"
-engine_version: stable
-
-# These correspond to the version numbers visible on their respective GitHub release pages
-compose_version: 1.21.1
-machine_version: 0.14.0
-
-# Password used to connect with the "docker user"
-docker_user_password: training
-
diff --git a/prepare-vms/settings/jerome.yaml b/prepare-vms/settings/jerome.yaml
index 62e8a08f..78014b55 100644
--- a/prepare-vms/settings/jerome.yaml
+++ b/prepare-vms/settings/jerome.yaml
@@ -5,7 +5,7 @@ clustersize: 4
clusterprefix: node
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: jerome.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: Letter
diff --git a/prepare-vms/settings/kube101.yaml b/prepare-vms/settings/kube101.yaml
index 8f742e54..4f89305c 100644
--- a/prepare-vms/settings/kube101.yaml
+++ b/prepare-vms/settings/kube101.yaml
@@ -7,7 +7,7 @@ clustersize: 3
clusterprefix: node
# Jinja2 template to use to generate ready-to-cut cards
-cards_template: kube101.html
+cards_template: cards.html
# Use "Letter" in the US, and "A4" everywhere else
paper_size: Letter
diff --git a/prepare-vms/setup-admin-clusters.sh b/prepare-vms/setup-admin-clusters.sh
index b93f0da3..a4719d7a 100755
--- a/prepare-vms/setup-admin-clusters.sh
+++ b/prepare-vms/setup-admin-clusters.sh
@@ -1,15 +1,20 @@
#!/bin/sh
set -e
-INFRA=infra/aws-eu-west-3
+export AWS_INSTANCE_TYPE=t3a.small
+
+INFRA=infra/aws-us-west-2
STUDENTS=2
-TAG=admin-dmuc
+PREFIX=$(date +%Y-%m-%d-%H-%M)
+
+SETTINGS=admin-dmuc
+TAG=$PREFIX-$SETTINGS
./workshopctl start \
--tag $TAG \
--infra $INFRA \
- --settings settings/$TAG.yaml \
+ --settings settings/$SETTINGS.yaml \
--count $STUDENTS
./workshopctl deploy $TAG
@@ -17,11 +22,12 @@ TAG=admin-dmuc
./workshopctl kubebins $TAG
./workshopctl cards $TAG
-TAG=admin-kubenet
+SETTINGS=admin-kubenet
+TAG=$PREFIX-$SETTINGS
./workshopctl start \
--tag $TAG \
--infra $INFRA \
- --settings settings/$TAG.yaml \
+ --settings settings/$SETTINGS.yaml \
--count $((3*$STUDENTS))
./workshopctl deploy $TAG
@@ -29,11 +35,12 @@ TAG=admin-kubenet
./workshopctl disableaddrchecks $TAG
./workshopctl cards $TAG
-TAG=admin-kuberouter
+SETTINGS=admin-kuberouter
+TAG=$PREFIX-$SETTINGS
./workshopctl start \
--tag $TAG \
--infra $INFRA \
- --settings settings/$TAG.yaml \
+ --settings settings/$SETTINGS.yaml \
--count $((3*$STUDENTS))
./workshopctl deploy $TAG
@@ -41,11 +48,12 @@ TAG=admin-kuberouter
./workshopctl disableaddrchecks $TAG
./workshopctl cards $TAG
-TAG=admin-test
+SETTINGS=admin-test
+TAG=$PREFIX-$SETTINGS
./workshopctl start \
--tag $TAG \
--infra $INFRA \
- --settings settings/$TAG.yaml \
+ --settings settings/$SETTINGS.yaml \
--count $((3*$STUDENTS))
./workshopctl deploy $TAG
diff --git a/prepare-vms/templates/admin.html b/prepare-vms/templates/admin.html
deleted file mode 100644
index 9684a55c..00000000
--- a/prepare-vms/templates/admin.html
+++ /dev/null
@@ -1,124 +0,0 @@
-{# Feel free to customize or override anything in there! #}
-{%- set url = "http://FIXME.container.training" -%}
-{%- set pagesize = 9 -%}
-{%- if clustersize == 1 -%}
- {%- set workshop_name = "Docker workshop" -%}
- {%- set cluster_or_machine = "machine virtuelle" -%}
- {%- set this_or_each = "cette" -%}
- {%- set plural = "" -%}
- {%- set image_src = "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png" -%}
-{%- else -%}
- {%- set workshop_name = "Kubernetes workshop" -%}
- {%- set cluster_or_machine = "cluster" -%}
- {%- set this_or_each = "chaque" -%}
- {%- set plural = "s" -%}
- {%- set image_src_swarm = "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png" -%}
- {%- set image_src_kube = "https://avatars1.githubusercontent.com/u/13629408" -%}
- {%- set image_src = image_src_kube -%}
-{%- endif -%}
-
-
-
-
-{% for cluster in clusters %}
- {% if loop.index0>0 and loop.index0%pagesize==0 %}
-
- {% endif %}
-
-
-
- Voici les informations permettant de se connecter à un
- des environnements utilisés pour cette formation.
- Vous pouvez vous connecter à {{ this_or_each }} machine
- virtuelle avec n'importe quel client SSH.
-
-
-
-
-
- | cluster: |
- | {{ clusterprefix }} |
- | identifiant: |
- | docker |
- | mot de passe: |
- | {{ docker_user_password }} |
-
-
-
-
- Adresse{{ plural }} IP :
-
-
- {% for node in cluster %}
- | {{ clusterprefix }}{{ loop.index }}: | {{ node }} |
- {% endfor %}
-
-
-
Le support de formation est à l'adresse suivante :
-
{{ url }}
-
-
-{% endfor %}
-
-
diff --git a/prepare-vms/templates/cards.html b/prepare-vms/templates/cards.html
index dc977b65..1f58b8f6 100644
--- a/prepare-vms/templates/cards.html
+++ b/prepare-vms/templates/cards.html
@@ -1,29 +1,88 @@
{# Feel free to customize or override anything in there! #}
-{%- set url = "http://container.training/" -%}
-{%- set pagesize = 12 -%}
-{%- if clustersize == 1 -%}
- {%- set workshop_name = "Docker workshop" -%}
- {%- set cluster_or_machine = "machine" -%}
- {%- set this_or_each = "this" -%}
- {%- set machine_is_or_machines_are = "machine is" -%}
- {%- set image_src = "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png" -%}
-{%- else -%}
- {%- set workshop_name = "orchestration workshop" -%}
- {%- set cluster_or_machine = "cluster" -%}
- {%- set this_or_each = "each" -%}
- {%- set machine_is_or_machines_are = "machines are" -%}
- {%- set image_src_swarm = "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png" -%}
- {%- set image_src_kube = "https://avatars1.githubusercontent.com/u/13629408" -%}
- {%- set image_src = image_src_swarm -%}
+
+{%- set url = "http://FIXME.container.training/" -%}
+{%- set pagesize = 9 -%}
+{%- set lang = "en" -%}
+{%- set event = "training session" -%}
+{%- set backside = False -%}
+{%- set image = "kube" -%}
+{%- set clusternumber = 100 -%}
+
+{%- set image_src = {
+ "docker": "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png",
+ "swarm": "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png",
+ "kube": "https://avatars1.githubusercontent.com/u/13629408",
+ "enix": "https://enix.io/static/img/logos/logo-domain-cropped.png",
+ }[image] -%}
+{%- if lang == "en" and clustersize == 1 -%}
+ {%- set intro -%}
+ Here is the connection information to your very own
+ machine for this {{ event }}.
+ You can connect to this VM with any SSH client.
+ {%- endset -%}
+ {%- set listhead -%}
+ Your machine is:
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "en" and clustersize != 1 -%}
+ {%- set intro -%}
+ Here is the connection information to your very own
+ cluster for this {{ event }}.
+ You can connect to each VM with any SSH client.
+ {%- endset -%}
+ {%- set listhead -%}
+ Your machines are:
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "fr" and clustersize == 1 -%}
+ {%- set intro -%}
+ Voici les informations permettant de se connecter à votre
+ machine pour cette formation.
+ Vous pouvez vous connecter à cette machine virtuelle
+ avec n'importe quel client SSH.
+ {%- endset -%}
+ {%- set listhead -%}
+ Adresse IP:
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "en" and clusterprefix != "node" -%}
+ {%- set intro -%}
+ Here is the connection information for the
+ {{ clusterprefix }} environment.
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "fr" and clustersize != 1 -%}
+ {%- set intro -%}
+ Voici les informations permettant de se connecter à votre
+ cluster pour cette formation.
+ Vous pouvez vous connecter à chaque machine virtuelle
+ avec n'importe quel client SSH.
+ {%- endset -%}
+ {%- set listhead -%}
+ Adresses IP:
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "en" -%}
+ {%- set slides_are_at -%}
+ You can find the slides at:
+ {%- endset -%}
+{%- endif -%}
+{%- if lang == "fr" -%}
+ {%- set slides_are_at -%}
+ Le support de formation est à l'adresse suivante :
+ {%- endset -%}
{%- endif -%}
{% for cluster in clusters %}
- {% if loop.index0>0 and loop.index0%pagesize==0 %}
-
- {% endif %}
-
-
- Here is the connection information to your very own
- {{ cluster_or_machine }} for this {{ workshop_name }}.
- You can connect to {{ this_or_each }} VM with any SSH client.
-
+
{{ intro }}
+ {% if clusternumber != None %}
+ | cluster: |
+ | {{ clusternumber + loop.index }} |
+ {% endif %}
| login: |
| docker |
| password: |
@@ -90,17 +175,44 @@ img {
- Your {{ machine_is_or_machines_are }}:
+ {{ listhead }}
{% for node in cluster %}
- | node{{ loop.index }}: | {{ node }} |
+
+ | {{ clusterprefix }}{{ loop.index }}: |
+ {{ node }} |
+
{% endfor %}
- You can find the slides at:
+
+
+ {{ slides_are_at }}
{{ url }}
+ {% if loop.index%pagesize==0 or loop.last %}
+
+ {% if backside %}
+ {% for x in range(pagesize) %}
+
+
+
You got this at the workshop
+ "Getting Started With Kubernetes and Container Orchestration"
+ during QCON London (March 2019).
+
If you liked that workshop,
+ I can train your team or organization
+ on Docker, container, and Kubernetes,
+ with curriculums of 1 to 5 days.
+
+
Interested? Contact me at:
+
jerome.petazzoni@gmail.com
+
Thank you!
+
+ {% endfor %}
+
+ {% endif %}
+ {% endif %}
{% endfor %}
diff --git a/prepare-vms/templates/enix.html b/prepare-vms/templates/enix.html
deleted file mode 100644
index e84c0d7c..00000000
--- a/prepare-vms/templates/enix.html
+++ /dev/null
@@ -1,121 +0,0 @@
-{# Feel free to customize or override anything in there! #}
-{%- set url = "http://FIXME.container.training" -%}
-{%- set pagesize = 9 -%}
-{%- if clustersize == 1 -%}
- {%- set workshop_name = "Docker workshop" -%}
- {%- set cluster_or_machine = "machine virtuelle" -%}
- {%- set this_or_each = "cette" -%}
- {%- set plural = "" -%}
- {%- set image_src = "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png" -%}
-{%- else -%}
- {%- set workshop_name = "Kubernetes workshop" -%}
- {%- set cluster_or_machine = "cluster" -%}
- {%- set this_or_each = "chaque" -%}
- {%- set plural = "s" -%}
- {%- set image_src_swarm = "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png" -%}
- {%- set image_src_kube = "https://avatars1.githubusercontent.com/u/13629408" -%}
- {%- set image_src = image_src_kube -%}
-{%- endif -%}
-
-
-
-
-{% for cluster in clusters %}
- {% if loop.index0>0 and loop.index0%pagesize==0 %}
-
- {% endif %}
-
-
-
- Voici les informations permettant de se connecter à votre
- {{ cluster_or_machine }} pour cette formation.
- Vous pouvez vous connecter à {{ this_or_each }} machine virtuelle
- avec n'importe quel client SSH.
-
-
-
-
- | identifiant: |
- | docker |
- | mot de passe: |
- | {{ docker_user_password }} |
-
-
-
-
- Adresse{{ plural }} IP :
-
-
- {% for node in cluster %}
- | node{{ loop.index }}: | {{ node }} |
- {% endfor %}
-
-
-
Le support de formation est à l'adresse suivante :
-
{{ url }}
-
-
-{% endfor %}
-
-
diff --git a/prepare-vms/templates/jerome.html b/prepare-vms/templates/jerome.html
deleted file mode 100644
index 9f0263b8..00000000
--- a/prepare-vms/templates/jerome.html
+++ /dev/null
@@ -1,134 +0,0 @@
-{# Feel free to customize or override anything in there! #}
-{%- set url = "http://qconuk2019.container.training/" -%}
-{%- set pagesize = 9 -%}
-{%- if clustersize == 1 -%}
- {%- set workshop_name = "Docker workshop" -%}
- {%- set cluster_or_machine = "machine" -%}
- {%- set this_or_each = "this" -%}
- {%- set machine_is_or_machines_are = "machine is" -%}
- {%- set image_src = "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png" -%}
-{%- else -%}
- {%- set workshop_name = "Kubernetes workshop" -%}
- {%- set cluster_or_machine = "cluster" -%}
- {%- set this_or_each = "each" -%}
- {%- set machine_is_or_machines_are = "machines are" -%}
- {%- set image_src_swarm = "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png" -%}
- {%- set image_src_kube = "https://avatars1.githubusercontent.com/u/13629408" -%}
- {%- set image_src = image_src_kube -%}
-{%- endif -%}
-
-
-
-
-{% for cluster in clusters %}
-
-
-
- Here is the connection information to your very own
- {{ cluster_or_machine }} for this {{ workshop_name }}.
- You can connect to {{ this_or_each }} VM with any SSH client.
-
-
-
-
- | login: |
- | docker |
- | password: |
- | {{ docker_user_password }} |
-
-
-
-
- Your {{ machine_is_or_machines_are }}:
-
- {% for node in cluster %}
- | node{{ loop.index }}: | {{ node }} |
- {% endfor %}
-
-
-
You can find the slides at:
-
{{ url }}
-
-
- {% if loop.index%pagesize==0 or loop.last %}
-
- {% for x in range(pagesize) %}
-
-
-
You got this at the workshop
- "Getting Started With Kubernetes and Container Orchestration"
- during QCON London (March 2019).
-
If you liked that workshop,
- I can train your team or organization
- on Docker, container, and Kubernetes,
- with curriculums of 1 to 5 days.
-
-
Interested? Contact me at:
-
jerome.petazzoni@gmail.com
-
Thank you!
-
- {% endfor %}
-
- {% endif %}
-{% endfor %}
-
-
diff --git a/prepare-vms/templates/kube101.html b/prepare-vms/templates/kube101.html
deleted file mode 100644
index 1a937818..00000000
--- a/prepare-vms/templates/kube101.html
+++ /dev/null
@@ -1,106 +0,0 @@
-{# Feel free to customize or override anything in there! #}
-{%- set url = "http://container.training/" -%}
-{%- set pagesize = 12 -%}
-{%- if clustersize == 1 -%}
- {%- set workshop_name = "Docker workshop" -%}
- {%- set cluster_or_machine = "machine" -%}
- {%- set this_or_each = "this" -%}
- {%- set machine_is_or_machines_are = "machine is" -%}
- {%- set image_src = "https://s3-us-west-2.amazonaws.com/www.breadware.com/integrations/docker.png" -%}
-{%- else -%}
- {%- set workshop_name = "Kubernetes workshop" -%}
- {%- set cluster_or_machine = "cluster" -%}
- {%- set this_or_each = "each" -%}
- {%- set machine_is_or_machines_are = "machines are" -%}
- {%- set image_src_swarm = "https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png" -%}
- {%- set image_src_kube = "https://avatars1.githubusercontent.com/u/13629408" -%}
- {%- set image_src = image_src_kube -%}
-{%- endif -%}
-
-
-
-
-{% for cluster in clusters %}
- {% if loop.index0>0 and loop.index0%pagesize==0 %}
-
- {% endif %}
-
-
-
- Here is the connection information to your very own
- {{ cluster_or_machine }} for this {{ workshop_name }}.
- You can connect to {{ this_or_each }} VM with any SSH client.
-
-
-
-
- | login: |
- | docker |
- | password: |
- | {{ docker_user_password }} |
-
-
-
-
- Your {{ machine_is_or_machines_are }}:
-
- {% for node in cluster %}
- | node{{ loop.index }}: | {{ node }} |
- {% endfor %}
-
-
-
You can find the slides at:
-
{{ url }}
-
-
-{% endfor %}
-
-
diff --git a/slides/containers/Container_Network_Model.md b/slides/containers/Container_Network_Model.md
index fd77c3bd..87cf044f 100644
--- a/slides/containers/Container_Network_Model.md
+++ b/slides/containers/Container_Network_Model.md
@@ -474,7 +474,7 @@ When creating a network, extra options can be provided.
* `--ip-range` (in CIDR notation) indicates the subnet to allocate from.
-* `--aux-address` allows to specify a list of reserved addresses (which won't be allocated to containers).
+* `--aux-address` allows specifying a list of reserved addresses (which won't be allocated to containers).
---
@@ -556,7 +556,7 @@ General idea:
* So far, we have specified which network to use when starting the container.
-* The Docker Engine also allows to connect and disconnect while the container runs.
+* The Docker Engine also allows connecting and disconnecting while the container is running.
* This feature is exposed through the Docker API, and through two Docker CLI commands:
diff --git a/slides/containers/Namespaces_Cgroups.md b/slides/containers/Namespaces_Cgroups.md
index 46d1ac06..09b90561 100644
--- a/slides/containers/Namespaces_Cgroups.md
+++ b/slides/containers/Namespaces_Cgroups.md
@@ -86,13 +86,13 @@ class: extra-details, deep-dive
- the `unshare()` system call.
-- The Linux tool `unshare` allows to do that from a shell.
+- The Linux tool `unshare` allows doing that from a shell.
- A new process can re-use none / all / some of the namespaces of its parent.
- It is possible to "enter" a namespace with the `setns()` system call.
-- The Linux tool `nsenter` allows to do that from a shell.
+- The Linux tool `nsenter` allows doing that from a shell.
---
@@ -138,11 +138,11 @@ class: extra-details, deep-dive
- gethostname / sethostname
-- Allows to set a custom hostname for a container.
+- Allows setting a custom hostname for a container.
- That's (mostly) it!
-- Also allows to set the NIS domain.
+- Also allows setting the NIS domain.
(If you don't know what a NIS domain is, you don't have to worry about it!)
@@ -392,13 +392,13 @@ class: extra-details
- Processes can have their own root fs (à la chroot).
-- Processes can also have "private" mounts. This allows to:
+- Processes can also have "private" mounts. This allows:
- - isolate `/tmp` (per user, per service...)
+ - isolating `/tmp` (per user, per service...)
- - mask `/proc`, `/sys` (for processes that don't need them)
+ - masking `/proc`, `/sys` (for processes that don't need them)
- - mount remote filesystems or sensitive data,
+ - mounting remote filesystems or sensitive data,
but make it visible only for allowed processes
- Mounts can be totally private, or shared.
@@ -570,7 +570,7 @@ Check `man 2 unshare` and `man pid_namespaces` if you want more details.
## User namespace
-- Allows to map UID/GID; e.g.:
+- Allows mapping UID/GID; e.g.:
- UID 0→1999 in container C1 is mapped to UID 10000→11999 on host
- UID 0→1999 in container C2 is mapped to UID 12000→13999 on host
@@ -947,7 +947,7 @@ Killed
(i.e., "this group of process used X seconds of CPU0 and Y seconds of CPU1".)
-- Allows to set relative weights used by the scheduler.
+- Allows setting relative weights used by the scheduler.
---
@@ -1101,9 +1101,9 @@ See `man capabilities` for the full list and details.
- Original seccomp only allows `read()`, `write()`, `exit()`, `sigreturn()`.
-- The seccomp-bpf extension allows to specify custom filters with BPF rules.
+- The seccomp-bpf extension allows specifying custom filters with BPF rules.
-- This allows to filter by syscall, and by parameter.
+- This allows filtering by syscall, and by parameter.
- BPF code can perform arbitrarily complex checks, quickly, and safely.
diff --git a/slides/containers/Resource_Limits.md b/slides/containers/Resource_Limits.md
index c74d2ebe..bb04e9de 100644
--- a/slides/containers/Resource_Limits.md
+++ b/slides/containers/Resource_Limits.md
@@ -72,7 +72,7 @@
- For memory usage, the mechanism is part of the *cgroup* subsystem.
-- This subsystem allows to limit the memory for a process or a group of processes.
+- This subsystem allows limiting the memory for a process or a group of processes.
- A container engine leverages these mechanisms to limit memory for a container.
diff --git a/slides/index.yaml b/slides/index.yaml
index e7721610..668fdbe2 100644
--- a/slides/index.yaml
+++ b/slides/index.yaml
@@ -31,6 +31,7 @@
title: Kubernetes for administrators and operators
speaker: jpetazzo
attend: https://conferences.oreilly.com/velocity/vl-ca/public/schedule/detail/75313
+ slides: https://kadm-2019-06.container.training/
- date: 2019-05-01
country: us
diff --git a/slides/k8s/architecture.md b/slides/k8s/architecture.md
index e3bf8c86..705548ac 100644
--- a/slides/k8s/architecture.md
+++ b/slides/k8s/architecture.md
@@ -356,9 +356,9 @@ We demonstrated *update* and *watch* semantics.
- we create a Deployment object
- - the Deployment controller notices it, creates a ReplicaSet
+ - the Deployment controller notices it, and creates a ReplicaSet
- - the ReplicaSet controller notices it, creates a Pod
+ - the ReplicaSet controller notices the ReplicaSet, and creates a Pod
---
diff --git a/slides/k8s/authn-authz.md b/slides/k8s/authn-authz.md
index 30c77d3c..a2c0c431 100644
--- a/slides/k8s/authn-authz.md
+++ b/slides/k8s/authn-authz.md
@@ -22,7 +22,7 @@
- When the API server receives a request, it tries to authenticate it
- (it examines headers, certificates ... anything available)
+ (it examines headers, certificates... anything available)
- Many authentication methods are available and can be used simultaneously
@@ -34,7 +34,7 @@
- the user ID
- a list of groups
-- The API server doesn't interpret these; it'll be the job of *authorizers*
+- The API server doesn't interpret these; that'll be the job of *authorizers*
---
@@ -50,7 +50,7 @@
- [HTTP basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication)
- (carrying user and password in a HTTP header)
+ (carrying user and password in an HTTP header)
- Authentication proxy
@@ -88,7 +88,7 @@
(i.e. they are not stored in etcd or anywhere else)
-- Users can be created (and given membership to groups) independently of the API
+- Users can be created (and added to groups) independently of the API
- The Kubernetes API can be set up to use your custom CA to validate client certs
@@ -193,7 +193,7 @@ class: extra-details
(the kind that you can view with `kubectl get secrets`)
-- Service accounts are generally used to grant permissions to applications, services ...
+- Service accounts are generally used to grant permissions to applications, services...
(as opposed to humans)
@@ -217,7 +217,7 @@ class: extra-details
.exercise[
-- The resource name is `serviceaccount` or `sa` in short:
+- The resource name is `serviceaccount` or `sa` for short:
```bash
kubectl get sa
```
@@ -309,7 +309,7 @@ class: extra-details
- The API "sees" us as a different user
-- But neither user has any right, so we can't do nothin'
+- But neither user has any rights, so we can't do nothin'
- Let's change that!
@@ -339,9 +339,9 @@ class: extra-details
- A rule is a combination of:
- - [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb) like create, get, list, update, delete ...
+ - [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb) like create, get, list, update, delete...
- - resources (as in "API resource", like pods, nodes, services ...)
+ - resources (as in "API resource," like pods, nodes, services...)
- resource names (to specify e.g. one specific pod instead of all pods)
@@ -375,13 +375,13 @@ class: extra-details
- We can also define API resources ClusterRole and ClusterRoleBinding
-- These are a superset, allowing to:
+- These are a superset, allowing us to:
- specify actions on cluster-wide objects (like nodes)
- operate across all namespaces
-- We can create Role and RoleBinding resources within a namespaces
+- We can create Role and RoleBinding resources within a namespace
- ClusterRole and ClusterRoleBinding resources are global
@@ -389,13 +389,13 @@ class: extra-details
## Pods and service accounts
-- A pod can be associated to a service account
+- A pod can be associated with a service account
- - by default, it is associated to the `default` service account
+ - by default, it is associated with the `default` service account
- - as we've seen earlier, this service account has no permission anyway
+ - as we saw earlier, this service account has no permissions anyway
-- The associated token is exposed into the pod's filesystem
+- The associated token is exposed to the pod's filesystem
(in `/var/run/secrets/kubernetes.io/serviceaccount/token`)
@@ -460,7 +460,7 @@ class: extra-details
]
-It's important to note a couple of details in these flags ...
+It's important to note a couple of details in these flags...
---
@@ -493,13 +493,13 @@ It's important to note a couple of details in these flags ...
- again, the command would have worked fine (no error)
- - ... but our API requests would have been denied later
+ - ...but our API requests would have been denied later
- What's about the `default:` prefix?
- that's the namespace of the service account
- - yes, it could be inferred from context, but ... `kubectl` requires it
+ - yes, it could be inferred from context, but... `kubectl` requires it
---
@@ -590,7 +590,7 @@ class: extra-details
*In many situations, these roles will be all you need.*
-*You can also customize them if needed!*
+*You can also customize them!*
---
@@ -652,7 +652,7 @@ class: extra-details
kubectl describe clusterrolebinding cluster-admin
```
-- This binding associates `system:masters` to the cluster role `cluster-admin`
+- This binding associates `system:masters` with the cluster role `cluster-admin`
- And the `cluster-admin` is, basically, `root`:
```bash
@@ -667,7 +667,7 @@ class: extra-details
- For auditing purposes, sometimes we want to know who can perform an action
-- Here is a proof-of-concept tool by Aqua Security, doing exactly that:
+- There is a proof-of-concept tool by Aqua Security which does exactly that:
https://github.com/aquasecurity/kubectl-who-can
diff --git a/slides/k8s/cni.md b/slides/k8s/cni.md
index 306c4302..3970f119 100644
--- a/slides/k8s/cni.md
+++ b/slides/k8s/cni.md
@@ -26,7 +26,7 @@
The reference plugins are available [here].
-Look into each plugin's directory for its documentation.
+Look in each plugin's directory for its documentation.
[here]: https://github.com/containernetworking/plugins/tree/master/plugins
@@ -98,7 +98,7 @@ class: extra-details
- CNI_NETNS: path to network namespace file
- - CNI_IFNAME: how the network interface should be named
+ - CNI_IFNAME: what the network interface should be named
- The network configuration must be provided to the plugin on stdin
@@ -188,12 +188,16 @@ class: extra-details
- ... But this time, the controller manager will allocate `podCIDR` subnets
-- We will start kube-router with a DaemonSet
+ (so that we don't have to manually assign subnets to individual nodes)
-- This DaemonSet will start one instance of kube-router on each node
+- We will create a DaemonSet for kube-router
+
+- We will join nodes to the cluster
+
+- The DaemonSet will automatically start a kube-router pod on each node
---
-
+
## Logging into the new cluster
.exercise[
@@ -221,7 +225,7 @@ class: extra-details
- It is similar to the one we used with the `kubenet` cluster
- The API server is started with `--allow-privileged`
-
+
(because we will start kube-router in privileged pods)
- The controller manager is started with extra flags too:
@@ -254,7 +258,7 @@ class: extra-details
---
-## The kube-router DaemonSet
+## The kube-router DaemonSet
- In the same directory, there is a `kuberouter.yaml` file
@@ -300,12 +304,10 @@ Note: the DaemonSet won't create any pods (yet) since there are no nodes (yet).
- Generate the kubeconfig file (replacing `X.X.X.X` with the address of `kuberouter1`):
```bash
- kubectl --kubeconfig ~/kubeconfig config \
- set-cluster kubenet --server http://`X.X.X.X`:8080
- kubectl --kubeconfig ~/kubeconfig config \
- set-context kubenet --cluster kubenet
- kubectl --kubeconfig ~/kubeconfig config\
- use-context kubenet
+ kubectl config set-cluster cni --server http://`X.X.X.X`:8080
+ kubectl config set-context cni --cluster cni
+ kubectl config use-context cni
+ cp ~/.kube/config ~/kubeconfig
```
]
@@ -487,8 +489,8 @@ What does that mean?
- First, get the container ID, with `docker ps` or like this:
```bash
- CID=$(docker ps
- --filter label=io.kubernetes.pod.namespace=kube-system
+ CID=$(docker ps -q \
+ --filter label=io.kubernetes.pod.namespace=kube-system \
--filter label=io.kubernetes.container.name=kube-router)
```
@@ -599,13 +601,13 @@ done
## Updating kube-router configuration
-- We need to add two command-line flags to the kube-router process
+- We need to pass two command-line flags to the kube-router process
.exercise[
- Edit the `kuberouter.yaml` file
-- Add the following flags to the kube-router arguments,:
+- Add the following flags to the kube-router arguments:
```
- "--peer-router-ips=`X.X.X.X`"
- "--peer-router-asns=64512"
diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md
index 32e241bb..0e27a464 100644
--- a/slides/k8s/concepts-k8s.md
+++ b/slides/k8s/concepts-k8s.md
@@ -177,7 +177,7 @@ class: extra-details
- In that case, there is no "master node"
-*For this reason, it is more accurate to say "control plane" rather than "master".*
+*For this reason, it is more accurate to say "control plane" rather than "master."*
---
diff --git a/slides/k8s/configuration.md b/slides/k8s/configuration.md
index d1e57a6a..47b23ca7 100644
--- a/slides/k8s/configuration.md
+++ b/slides/k8s/configuration.md
@@ -22,7 +22,7 @@
- There are many ways to pass configuration to code running in a container:
- - baking it in a custom image
+ - baking it into a custom image
- command-line arguments
@@ -125,7 +125,7 @@
- We can also use a mechanism called the *downward API*
-- The downward API allows to expose pod or container information
+- The downward API allows exposing pod or container information
- either through special files (we won't show that for now)
@@ -436,7 +436,7 @@ We should see connections served by Google, and others served by IBM.
- We are going to store the port number in a configmap
-- Then we will expose that configmap to a container environment variable
+- Then we will expose that configmap as a container environment variable
---
diff --git a/slides/k8s/csr-api.md b/slides/k8s/csr-api.md
index aec66fdc..2673c4a1 100644
--- a/slides/k8s/csr-api.md
+++ b/slides/k8s/csr-api.md
@@ -46,7 +46,7 @@
(and vice versa)
-- If I use someone's public key to encrypt / decrypt their messages,
+- If I use someone's public key to encrypt/decrypt their messages,
I can be certain that I am talking to them / they are talking to me
@@ -58,7 +58,7 @@
This is what I do if I want to obtain a certificate.
-1. Create public and private key.
+1. Create public and private keys.
2. Create a Certificate Signing Request (CSR).
@@ -84,7 +84,7 @@ The CA (or anyone else) never needs to know my private key.
(= upload a CSR to the Kubernetes API)
-- Then, using the Kubernetes API, we can approve / deny the request
+- Then, using the Kubernetes API, we can approve/deny the request
- If we approve the request, the Kubernetes API generates a certificate
@@ -122,7 +122,7 @@ The CA (or anyone else) never needs to know my private key.
- Users can then retrieve their certificate from their CSR object
-- ... And use that certificate for subsequent interactions
+- ...And use that certificate for subsequent interactions
---
@@ -387,7 +387,7 @@ The command above generates:
## What's missing?
-We shown, step by step, a method to issue short-lived certificates for users.
+We have just shown, step by step, a method to issue short-lived certificates for users.
To be usable in real environments, we would need to add:
@@ -417,7 +417,7 @@ To be usable in real environments, we would need to add:
- This provides enhanced security:
- - the long-term credentials can use long passphrases, 2FA, HSM ...
+ - the long-term credentials can use long passphrases, 2FA, HSM...
- the short-term credentials are more convenient to use
diff --git a/slides/k8s/dmuc.md b/slides/k8s/dmuc.md
index a00d940c..d70972aa 100644
--- a/slides/k8s/dmuc.md
+++ b/slides/k8s/dmuc.md
@@ -584,7 +584,7 @@ Our pod is still `Pending`. 🤔
Which is normal: it needs to be *scheduled*.
-(i.e., something needs to decide on which node it should go.)
+(i.e., something needs to decide which node it should go on.)
---
@@ -658,7 +658,7 @@ class: extra-details
- This is actually how the scheduler works!
-- It watches pods, takes scheduling decisions, creates Binding objects
+- It watches pods, makes scheduling decisions, and creates Binding objects
---
@@ -686,7 +686,7 @@ We should see the `Welcome to nginx!` page.
## Exposing our Deployment
-- We can now create a Service associated to this Deployment
+- We can now create a Service associated with this Deployment
.exercise[
@@ -711,11 +711,11 @@ This won't work. We need kube-proxy to enable internal communication.
## Starting kube-proxy
-- kube-proxy also needs to connect to API server
+- kube-proxy also needs to connect to the API server
- It can work with the `--master` flag
- (even though that will be deprecated in the future)
+ (although that will be deprecated in the future)
.exercise[
@@ -832,6 +832,6 @@ class: extra-details
- By default, the API server expects to be running directly on the nodes
- (it could be as a bare process, or in a container/pod using host network)
+ (it could be as a bare process, or in a container/pod using the host network)
- ... And it expects to be listening on port 6443 with TLS
diff --git a/slides/k8s/healthchecks.md b/slides/k8s/healthchecks.md
index 2563da68..a72fedbf 100644
--- a/slides/k8s/healthchecks.md
+++ b/slides/k8s/healthchecks.md
@@ -108,7 +108,7 @@
(as opposed to merely started)
-- Containers in a broken state gets killed and restarted
+- Containers in a broken state get killed and restarted
(instead of serving errors or timeouts)
diff --git a/slides/k8s/helm.md b/slides/k8s/helm.md
index 004abb58..5c895daa 100644
--- a/slides/k8s/helm.md
+++ b/slides/k8s/helm.md
@@ -158,7 +158,7 @@ Where do these `--set` options come from?
]
-The chart's metadata includes an URL to the project's home page.
+The chart's metadata includes a URL to the project's home page.
(Sometimes it conveniently points to the documentation for the chart.)
diff --git a/slides/k8s/horizontal-pod-autoscaler.md b/slides/k8s/horizontal-pod-autoscaler.md
index 38e363f6..6e660900 100644
--- a/slides/k8s/horizontal-pod-autoscaler.md
+++ b/slides/k8s/horizontal-pod-autoscaler.md
@@ -34,7 +34,7 @@
`TargetNumOfPods = ceil(sum(CurrentPodsCPUUtilization) / Target)`
-- It scales up/down the related object to this target number of pods
+- It scales the related object up/down to this target number of pods
---
diff --git a/slides/k8s/ingress.md b/slides/k8s/ingress.md
index 3188de96..9ccfdd8c 100644
--- a/slides/k8s/ingress.md
+++ b/slides/k8s/ingress.md
@@ -88,7 +88,7 @@
- the control loop watches over ingress resources, and configures the LB accordingly
-- Step 2: setup DNS
+- Step 2: set up DNS
- associate DNS entries with the load balancer address
@@ -126,7 +126,7 @@
- We could use pods specifying `hostPort: 80`
- ... but with most CNI plugins, this [doesn't work or require additional setup](https://github.com/kubernetes/kubernetes/issues/23920)
+ ... 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
@@ -142,7 +142,7 @@
(sometimes called sandbox or network sandbox)
-- An IP address is associated to the pod
+- An IP address is assigned to the pod
- This IP address is routed/connected to the cluster network
@@ -239,7 +239,7 @@ class: extra-details
- an error condition on the node
- (for instance: "disk full", do not start new pods here!)
+ (for instance: "disk full," do not start new pods here!)
- The `effect` can be:
@@ -501,11 +501,11 @@ spec:
(as long as it has access to the cluster subnet)
-- This allows to use external (hardware, physical machines...) load balancers
+- This allows the use of external (hardware, physical machines...) load balancers
- Annotations can encode special features
- (rate-limiting, A/B testing, session stickiness, etc.)
+ (rate-limiting, A/B testing, session stickiness, etc.)
---
diff --git a/slides/k8s/kubectlget.md b/slides/k8s/kubectlget.md
index 66a1fac1..44566b26 100644
--- a/slides/k8s/kubectlget.md
+++ b/slides/k8s/kubectlget.md
@@ -132,7 +132,7 @@ class: extra-details
- short (e.g. `no`, `svc`, `deploy`)
-- Some resources do not have a short names
+- Some resources do not have a short name
- `Endpoints` only have a plural form
@@ -466,4 +466,4 @@ class: extra-details
- For more details, see [KEP-0009] or the [node controller documentation]
[KEP-0009]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/0009-node-heartbeat.md
-[node controller documentation]: https://kubernetes.io/docs/concepts/architecture/nodes/#node-controller
\ No newline at end of file
+[node controller documentation]: https://kubernetes.io/docs/concepts/architecture/nodes/#node-controller
diff --git a/slides/k8s/kubectlproxy.md b/slides/k8s/kubectlproxy.md
index 749f8c0e..fa7f7505 100644
--- a/slides/k8s/kubectlproxy.md
+++ b/slides/k8s/kubectlproxy.md
@@ -77,9 +77,9 @@ If we wanted to talk to the API, we would need to:
- This is a great tool to learn and experiment with the Kubernetes API
-- ... And for serious usages as well (suitable for one-shot scripts)
+- ... And for serious uses as well (suitable for one-shot scripts)
-- For unattended use, it is better to create a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
+- For unattended use, it's better to create a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
---
diff --git a/slides/k8s/kubenet.md b/slides/k8s/kubenet.md
index 0bb7c624..bc470475 100644
--- a/slides/k8s/kubenet.md
+++ b/slides/k8s/kubenet.md
@@ -54,7 +54,7 @@
(15 are listed in the Kubernetes documentation)
-- Pods have level 3 (IP) connectivity, but *services* are level 4
+- Pods have level 3 (IP) connectivity, but *services* are level 4 (TCP or UDP)
(Services map to a single UDP or TCP port; no port ranges or arbitrary IP packets)
diff --git a/slides/k8s/kubercoins.md b/slides/k8s/kubercoins.md
new file mode 100644
index 00000000..3220f5fa
--- /dev/null
+++ b/slides/k8s/kubercoins.md
@@ -0,0 +1,244 @@
+# Deploying a sample application
+
+- We will connect to our new Kubernetes cluster
+
+- We will deploy a sample application, "DockerCoins"
+
+- That app features multiple micro-services and a web UI
+
+---
+
+## Connecting to our Kubernetes cluster
+
+- Our cluster has multiple nodes named `node1`, `node2`, etc.
+
+- We will do everything from `node1`
+
+- We have SSH access to the other nodes, but won't need it
+
+ (but we can use it for debugging, troubleshooting, etc.)
+
+.exercise[
+
+- Log into `node1`
+
+- Check that all nodes are `Ready`:
+ ```bash
+ kubectl get nodes
+ ```
+
+]
+
+---
+
+## Cloning some repos
+
+- We will need two repositories:
+
+ - the first one has the "DockerCoins" demo app
+
+ - the second one has these slides, some scripts, more manifests ...
+
+.exercise[
+
+- Clone the kubercoins repository on `node1`:
+ ```bash
+ git clone https://github.com/jpetazzo/kubercoins
+ ```
+
+
+- Clone the container.training repository as well:
+ ```bash
+ git clone https://@@GITREPO@@
+ ```
+
+]
+
+---
+
+## Running the application
+
+Without further ado, let's start this application!
+
+.exercise[
+
+- Apply all the manifests from the kubercoins repository:
+ ```bash
+ kubectl apply -f kubercoins/
+ ```
+
+]
+
+---
+
+## What's this application?
+
+--
+
+- It is a DockerCoin miner! .emoji[💰🐳📦🚢]
+
+--
+
+- No, you can't buy coffee with DockerCoins
+
+--
+
+- How DockerCoins works:
+
+ - generate a few random bytes
+
+ - hash these bytes
+
+ - increment a counter (to keep track of speed)
+
+ - repeat forever!
+
+--
+
+- DockerCoins is *not* a cryptocurrency
+
+ (the only common points are "randomness", "hashing", and "coins" in the name)
+
+---
+
+## DockerCoins in the microservices era
+
+- DockerCoins is made of 5 services:
+
+ - `rng` = web service generating random bytes
+
+ - `hasher` = web service computing hash of POSTed data
+
+ - `worker` = background process calling `rng` and `hasher`
+
+ - `webui` = web interface to watch progress
+
+ - `redis` = data store (holds a counter updated by `worker`)
+
+- These 5 services are visible in the application's Compose file,
+ [docker-compose.yml](
+ https://@@GITREPO@@/blob/master/dockercoins/docker-compose.yml)
+
+---
+
+## How DockerCoins works
+
+- `worker` invokes web service `rng` to generate random bytes
+
+- `worker` invokes web service `hasher` to hash these bytes
+
+- `worker` does this in an infinite loop
+
+- every second, `worker` updates `redis` to indicate how many loops were done
+
+- `webui` queries `redis`, and computes and exposes "hashing speed" in our browser
+
+*(See diagram on next slide!)*
+
+---
+
+class: pic
+
+
+
+---
+
+## Service discovery in container-land
+
+How does each service find out the address of the other ones?
+
+--
+
+- We do not hard-code IP addresses in the code
+
+- We do not hard-code FQDNs in the code, either
+
+- We just connect to a service name, and container-magic does the rest
+
+ (And by container-magic, we mean "a crafty, dynamic, embedded DNS server")
+
+---
+
+## Example in `worker/worker.py`
+
+```python
+redis = Redis("`redis`")
+
+
+def get_random_bytes():
+ r = requests.get("http://`rng`/32")
+ return r.content
+
+
+def hash_bytes(data):
+ r = requests.post("http://`hasher`/",
+ data=data,
+ headers={"Content-Type": "application/octet-stream"})
+```
+
+(Full source code available [here](
+https://@@GITREPO@@/blob/8279a3bce9398f7c1a53bdd95187c53eda4e6435/dockercoins/worker/worker.py#L17
+))
+
+---
+
+## Show me the code!
+
+- You can check the GitHub repository with all the materials of this workshop:
+
https://@@GITREPO@@
+
+- The application is in the [dockercoins](
+ https://@@GITREPO@@/tree/master/dockercoins)
+ subdirectory
+
+- The Compose file ([docker-compose.yml](
+ https://@@GITREPO@@/blob/master/dockercoins/docker-compose.yml))
+ lists all 5 services
+
+- `redis` is using an official image from the Docker Hub
+
+- `hasher`, `rng`, `worker`, `webui` are each built from a Dockerfile
+
+- Each service's Dockerfile and source code is in its own directory
+
+ (`hasher` is in the [hasher](https://@@GITREPO@@/blob/master/dockercoins/hasher/) directory,
+ `rng` is in the [rng](https://@@GITREPO@@/blob/master/dockercoins/rng/)
+ directory, etc.)
+
+---
+
+## Our application at work
+
+- We can check the logs of our application's pods
+
+.exercise[
+
+- Check the logs of the various components:
+ ```bash
+ kubectl logs deploy/worker
+ kubectl logs deploy/hasher
+ ```
+
+]
+
+---
+
+## Connecting to the web UI
+
+- "Logs are exciting and fun!" (No-one, ever)
+
+- The `webui` container exposes a web dashboard; let's view it
+
+.exercise[
+
+- Check the NodePort allocated to the web UI:
+ ```bash
+ kubectl get svc webui
+ ```
+
+- Open that in a web browser
+
+]
+
+A drawing area should show up, and after a few seconds, a blue
+graph will appear.
diff --git a/slides/k8s/kustomize.md b/slides/k8s/kustomize.md
index 60641b4a..664f82f3 100644
--- a/slides/k8s/kustomize.md
+++ b/slides/k8s/kustomize.md
@@ -70,7 +70,7 @@
- We need to run `ship init` in a new directory
-- `ship init` requires an URL to a remote repository containing Kubernetes YAML
+- `ship init` requires a URL to a remote repository containing Kubernetes YAML
- It will clone that repository and start a web UI
diff --git a/slides/k8s/localkubeconfig.md b/slides/k8s/localkubeconfig.md
index e95977dc..ff3c84b0 100644
--- a/slides/k8s/localkubeconfig.md
+++ b/slides/k8s/localkubeconfig.md
@@ -75,9 +75,9 @@ Platform:"linux/amd64"}
---
-## Moving away the existing `~/.kube/config`
+## Preserving the existing `~/.kube/config`
-- If you already have a `~/.kube/config` file, move it away
+- If you already have a `~/.kube/config` file, rename it
(we are going to overwrite it in the following slides!)
@@ -192,4 +192,4 @@ class: extra-details
]
-We can now utilize the cluster exactly as we did before, ignoring that it's remote.
+We can now utilize the cluster exactly as we did before, except that it's remote.
diff --git a/slides/k8s/logs-centralized.md b/slides/k8s/logs-centralized.md
index 6cdddda8..07af0ce3 100644
--- a/slides/k8s/logs-centralized.md
+++ b/slides/k8s/logs-centralized.md
@@ -73,12 +73,12 @@ and a few roles and role bindings (to give fluentd the required permissions).
- Fluentd runs on each node (thanks to a daemon set)
-- It binds-mounts `/var/log/containers` from the host (to access these files)
+- It bind-mounts `/var/log/containers` from the host (to access these files)
- It continuously scans this directory for new files; reads them; parses them
- Each log line becomes a JSON object, fully annotated with extra information:
-
container id, pod name, Kubernetes labels ...
+
container id, pod name, Kubernetes labels...
- These JSON objects are stored in ElasticSearch
diff --git a/slides/k8s/logs-cli.md b/slides/k8s/logs-cli.md
index aa7ffdc2..fe12f466 100644
--- a/slides/k8s/logs-cli.md
+++ b/slides/k8s/logs-cli.md
@@ -1,6 +1,6 @@
# Accessing logs from the CLI
-- The `kubectl logs` commands has limitations:
+- The `kubectl logs` command has limitations:
- it cannot stream logs from multiple pods at a time
@@ -12,7 +12,7 @@
## Doing it manually
-- We *could* (if we were so inclined), write a program or script that would:
+- We *could* (if we were so inclined) write a program or script that would:
- take a selector as an argument
@@ -72,11 +72,11 @@ Exactly what we need!
## Using Stern
-- There are two ways to specify the pods for which we want to see the logs:
+- There are two ways to specify the pods whose logs we want to see:
- `-l` followed by a selector expression (like with many `kubectl` commands)
- - with a "pod query", i.e. a regex used to match pod names
+ - with a "pod query," i.e. a regex used to match pod names
- These two ways can be combined if necessary
diff --git a/slides/k8s/multinode.md b/slides/k8s/multinode.md
index 09c9801a..0efb7dc5 100644
--- a/slides/k8s/multinode.md
+++ b/slides/k8s/multinode.md
@@ -104,12 +104,10 @@ class: extra-details
- Generate the `kubeconfig` file:
```bash
- kubectl --kubeconfig ~/kubeconfig config \
- set-cluster kubenet --server http://`X.X.X.X`:8080
- kubectl --kubeconfig ~/kubeconfig config \
- set-context kubenet --cluster kubenet
- kubectl --kubeconfig ~/kubeconfig config\
- use-context kubenet
+ kubectl config set-cluster kubenet --server http://`X.X.X.X`:8080
+ kubectl config set-context kubenet --cluster kubenet
+ kubectl config use-context kubenet
+ cp ~/.kube/config ~/kubeconfig
```
]
diff --git a/slides/k8s/namespaces.md b/slides/k8s/namespaces.md
index 13500990..51bbf774 100644
--- a/slides/k8s/namespaces.md
+++ b/slides/k8s/namespaces.md
@@ -26,7 +26,7 @@
- We cannot have two resources *of the same kind* with the same name
- (but it's OK to have a `rng` service, a `rng` deployment, and a `rng` daemon set)
+ (but it's OK to have an `rng` service, an `rng` deployment, and an `rng` daemon set)
--
diff --git a/slides/k8s/podsecuritypolicy.md b/slides/k8s/podsecuritypolicy.md
index 90f542d1..fea1d100 100644
--- a/slides/k8s/podsecuritypolicy.md
+++ b/slides/k8s/podsecuritypolicy.md
@@ -8,12 +8,18 @@
- Then we will explain how to avoid this with PodSecurityPolicies
-- We will illustrate this by creating a non-privileged user limited to a namespace
+- We will enable PodSecurityPolicies on our cluster
+
+- We will create a couple of policies (restricted and permissive)
+
+- Finally we will see how to use them to improve security on our cluster
---
## Setting up a namespace
+- For simplicity, let's work in a separate namespace
+
- Let's create a new namespace called "green"
.exercise[
@@ -32,168 +38,9 @@
---
-## Using limited credentials
-
-- When a namespace is created, a `default` ServiceAccount is added
-
-- By default, this ServiceAccount doesn't have any access rights
-
-- We will use this ServiceAccount as our non-privileged user
-
-- We will obtain this ServiceAccount's token and add it to a context
-
-- Then we will give basic access rights to this ServiceAccount
-
----
-
-## Obtaining the ServiceAccount's token
-
-- The token is stored in a Secret
-
-- The Secret is listed in the ServiceAccount
-
-.exercise[
-
-- Obtain the name of the Secret from the ServiceAccount::
- ```bash
- SECRET=$(kubectl get sa default -o jsonpath={.secrets[0].name})
- ```
-
-- Extract the token from the Secret object:
- ```bash
- TOKEN=$(kubectl get secrets $SECRET -o jsonpath={.data.token}
- | base64 -d)
- ```
-
-]
-
----
-
-class: extra-details
-
-## Inspecting a Kubernetes token
-
-- Kubernetes tokens are JSON Web Tokens
-
- (as defined by [RFC 7519](https://tools.ietf.org/html/rfc7519))
-
-- We can view their content (and even verify them) easily
-
-.exercise[
-
-- Display the token that we obtained:
- ```bash
- echo $TOKEN
- ```
-
-- Copy paste the token in the verification form on https://jwt.io
-
-]
-
----
-
-## Authenticating using the ServiceAccount token
-
-- Let's create a new *context* accessing our cluster with that token
-
-.exercise[
-
-- First, add the token credentials to our kubeconfig file:
- ```bash
- kubectl config set-credentials green --token=$TOKEN
- ```
-
-- Then, create a new context using these credentials:
- ```bash
- kubectl config set-context green --user=green --cluster=kubernetes
- ```
-
-- Check the results:
- ```bash
- kubectl config get-contexts
- ```
-
-]
-
----
-
-## Using the new context
-
-- Normally, this context doesn't let us access *anything* (yet)
-
-.exercise[
-
-- Change to the new context with one of these two commands:
- ```bash
- kctx green
- kubectl config use-context green
- ```
-
-- Also change to the green namespace in that context:
- ```bash
- kns green
- ```
-
-- Confirm that we don't have access to anything:
- ```bash
- kubectl get all
- ```
-
-]
-
----
-
-## Giving basic access rights
-
-- Let's bind the ClusterRole `edit` to our ServiceAccount
-
-- To allow access only to the namespace, we use a RoleBinding
-
- (instead of a ClusterRoleBinding, which would give global access)
-
-.exercise[
-
-- Switch back to `cluster-admin`:
- ```bash
- kctx -
- ```
-
-- Create the Role Binding:
- ```bash
- kubectl create rolebinding green --clusterrole=edit --serviceaccount=green:default
- ```
-
-]
-
----
-
-## Verifying access rights
-
-- Let's switch back to the `green` context and check that we have rights
-
-.exercise[
-
-- Switch back to `green`:
- ```bash
- kctx green
- ```
-
-- Check our permissions:
- ```bash
- kubectl get all
- ```
-
-]
-
-We should see an empty list.
-
-(Better than a series of permission errors!)
-
----
-
## Creating a basic Deployment
-- Just to demonstrate that everything works correctly, deploy NGINX
+- Just to check that everything works correctly, deploy NGINX
.exercise[
@@ -474,12 +321,65 @@ We can get hints at what's happening by looking at the ReplicaSet and Events.
---
+## Check that we can create Pods again
+
+- We haven't bound the policy to any user yet
+
+- But `cluster-admin` can implicitly `use` all policies
+
+.exercise[
+
+- Check that we can now create a Pod directly:
+ ```bash
+ kubectl run testpsp3 --image=nginx --restart=Never
+ ```
+
+- Create a Deployment as well:
+ ```bash
+ kubectl run testpsp4 --image=nginx
+ ```
+
+- Confirm that the Deployment is *not* creating any Pods:
+ ```bash
+ kubectl get all
+ ```
+
+]
+
+---
+
+## What's going on?
+
+- We can create Pods directly (thanks to our root-like permissions)
+
+- The Pods corresponding to a Deployment are created by the ReplicaSet controller
+
+- The ReplicaSet controller does *not* have root-like permissions
+
+- We need to either:
+
+ - grant permissions to the ReplicaSet controller
+
+ *or*
+
+ - grant permissions to our Pods' ServiceAccount
+
+- The first option would allow *anyone* to create pods
+
+- The second option will allow us to scope the permissions better
+
+---
+
## Binding the restricted policy
- Let's bind the role `psp:restricted` to ServiceAccount `green:default`
(aka the default ServiceAccount in the green Namespace)
+- This will allow Pod creation in the green Namespace
+
+ (because these Pods will be using that ServiceAccount automatically)
+
.exercise[
- Create the following RoleBinding:
@@ -495,18 +395,17 @@ We can get hints at what's happening by looking at the ReplicaSet and Events.
## Trying it out
-- Let's switch to the `green` context, and try to create resources
+- The Deployments that we created earlier will *eventually* recover
+
+ (the ReplicaSet controller will retry to create Pods once in a while)
+
+- If we create a new Deployment now, it should work immediately
.exercise[
-- Switch to the `green` context:
- ```bash
- kctx green
- ```
-
- Create a simple Deployment:
```bash
- kubectl create deployment web --image=nginx
+ kubectl create deployment testpsp5 --image=nginx
```
- Look at the Pods that have been created:
diff --git a/slides/k8s/prometheus.md b/slides/k8s/prometheus.md
index b78b1884..68ef6ca1 100644
--- a/slides/k8s/prometheus.md
+++ b/slides/k8s/prometheus.md
@@ -340,7 +340,7 @@ container_cpu_usage_seconds_total
- that it's the total used since the container creation
-- Since it's a "total", it is an increasing quantity
+- Since it's a "total," it is an increasing quantity
(we need to compute the derivative if we want e.g. CPU % over time)
@@ -495,7 +495,7 @@ class: extra-details
## Querying labels
-- What if we want to get metrics for containers belong to pod tagged `worker`?
+- What if we want to get metrics for containers belonging to a pod tagged `worker`?
- The cAdvisor exporter does not give us Kubernetes labels
diff --git a/slides/k8s/resource-limits.md b/slides/k8s/resource-limits.md
index e641fe78..047f29af 100644
--- a/slides/k8s/resource-limits.md
+++ b/slides/k8s/resource-limits.md
@@ -392,7 +392,7 @@ These quotas will apply to the namespace where the ResourceQuota is created.
count/roles.rbac.authorization.k8s.io: 10
```
-(The `count/` syntax allows to limit arbitrary objects, including CRDs.)
+(The `count/` syntax allows limiting arbitrary objects, including CRDs.)
---
diff --git a/slides/k8s/rollout.md b/slides/k8s/rollout.md
index 31331985..958ad5e2 100644
--- a/slides/k8s/rollout.md
+++ b/slides/k8s/rollout.md
@@ -5,9 +5,9 @@
- new pods are created
- old pods are terminated
-
+
- ... all at the same time
-
+
- if something goes wrong, ¯\\\_(ツ)\_/¯
---
@@ -212,7 +212,7 @@ class: extra-details
## Checking the dashboard during the bad rollout
-If you haven't deployed the Kubernetes dashboard earlier, just skip this slide.
+If you didn't deploy the Kubernetes dashboard earlier, just skip this slide.
.exercise[
@@ -255,7 +255,7 @@ Note the `3xxxx` port.
```
-->
-- Cancel the deployment and wait for the dust to settle down:
+- Cancel the deployment and wait for the dust to settle:
```bash
kubectl rollout undo deploy worker
kubectl rollout status deploy worker
diff --git a/slides/k8s/setup-managed.md b/slides/k8s/setup-managed.md
index ccc1b084..919bd2da 100644
--- a/slides/k8s/setup-managed.md
+++ b/slides/k8s/setup-managed.md
@@ -20,7 +20,7 @@ with a cloud provider
## EKS (the hard way)
-- [Read the doc](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html)
+- [Read the doc](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html)
- Create service roles, VPCs, and a bunch of other oddities
@@ -69,6 +69,8 @@ with a cloud provider
eksctl get clusters
```
+.footnote[Note: the AWS documentation has been updated and now includes [eksctl instructions](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html).]
+
---
## GKE (initial setup)
diff --git a/slides/k8s/volumes.md b/slides/k8s/volumes.md
index dc7c355a..58b6849a 100644
--- a/slides/k8s/volumes.md
+++ b/slides/k8s/volumes.md
@@ -28,11 +28,11 @@ class: extra-details
but it refers to Docker 1.7, which was released in 2015!)
-- Docker volumes allow to share data between containers running on the same host
+- Docker volumes allow us to share data between containers running on the same host
- Kubernetes volumes allow us to share data between containers in the same pod
-- Both Docker and Kubernetes volumes allow us access to storage systems
+- Both Docker and Kubernetes volumes enable access to storage systems
- Kubernetes volumes are also used to expose configuration and secrets
@@ -60,7 +60,7 @@ class: extra-details
- correspond to concrete volumes (e.g. on a SAN, EBS, etc.)
- - cannot be associated to a Pod directly; but through a Persistent Volume Claim
+ - cannot be associated with a Pod directly; but through a Persistent Volume Claim
- won't be discussed further in this section
diff --git a/slides/sfsf.yml b/slides/sfsf.yml
index 5a517d31..081503ab 100644
--- a/slides/sfsf.yml
+++ b/slides/sfsf.yml
@@ -40,7 +40,8 @@ chapters:
- k8s/operators.md
- k8s/operators-design.md
- k8s/owners-and-dependents.md
-- - k8s/logs-cli.md
+- - k8s/kubercoins.md
+ - k8s/logs-cli.md
- k8s/logs-centralized.md
- k8s/healthchecks.md
- k8s/healthchecks-more.md
diff --git a/slides/shared/connecting.md b/slides/shared/connecting.md
index 11f9568a..2e83d996 100644
--- a/slides/shared/connecting.md
+++ b/slides/shared/connecting.md
@@ -84,14 +84,14 @@ You will need a Docker ID to use Play-With-Docker.
- Unless instructed, **all commands must be run from the first VM, `node1`**
-- We will only checkout/copy the code on `node1`
+- We will only check out/copy the code on `node1`
- During normal operations, we do not need access to the other nodes
- If we had to troubleshoot issues, we would use a combination of:
- SSH (to access system logs, daemon status...)
-
+
- Docker API (to check running containers and container engine status)
---
diff --git a/slides/shared/prereqs.md b/slides/shared/prereqs.md
index 52684b34..9daca01a 100644
--- a/slides/shared/prereqs.md
+++ b/slides/shared/prereqs.md
@@ -90,7 +90,7 @@ class: in-person
## Why don't we run containers locally?
-- Installing that stuff can be hard on some machines
+- Installing this stuff can be hard on some machines
(32 bits CPU or OS... Laptops without administrator access... etc.)
diff --git a/slides/shared/sampleapp.md b/slides/shared/sampleapp.md
index cd9db553..0636d2de 100644
--- a/slides/shared/sampleapp.md
+++ b/slides/shared/sampleapp.md
@@ -80,7 +80,7 @@ and displays aggregated logs.
- DockerCoins is *not* a cryptocurrency
- (the only common points are "randomness", "hashing", and "coins" in the name)
+ (the only common points are "randomness," "hashing," and "coins" in the name)
---
@@ -134,7 +134,7 @@ How does each service find out the address of the other ones?
- We do not hard-code IP addresses in the code
-- We do not hard-code FQDN in the code, either
+- We do not hard-code FQDNs in the code, either
- We just connect to a service name, and container-magic does the rest
@@ -173,7 +173,7 @@ class: extra-details
- Compose file version 2+ makes each container reachable through its service name
-- Compose file version 1 did require "links" sections
+- Compose file version 1 required "links" sections to accomplish this
- Network aliases are automatically namespaced