diff --git a/slides/common/about-slides.md b/slides/common/about-slides.md
index 9cd68475..975bc80d 100644
--- a/slides/common/about-slides.md
+++ b/slides/common/about-slides.md
@@ -26,3 +26,24 @@
```open https://github.com/jpetazzo/container.training/tree/master/slides/common/about-slides.md```
]
-->
+
+---
+
+class: extra-details
+
+## Extra details
+
+- This slide has a little magnifying glass in the top left corner
+
+- This magnifiying glass indicates slides that provide extra details
+
+- Feel free to skip them if:
+
+ - you are in a hurry
+
+ - you are new to this and want to avoid cognitive overload
+
+ - you want only the most essential information
+
+- You can review these slides another time if you want, they'll be waiti
+ng for you ☺
diff --git a/slides/common/prereqs.md b/slides/common/prereqs.md
index cc2d17aa..c4225431 100644
--- a/slides/common/prereqs.md
+++ b/slides/common/prereqs.md
@@ -20,26 +20,6 @@
---
-class: extra-details
-
-## Extra details
-
-- This slide has a little magnifying glass in the top left corner
-
-- This magnifiying glass indicates slides that provide extra details
-
-- Feel free to skip them if:
-
- - you are in a hurry
-
- - you are new to this and want to avoid cognitive overload
-
- - you want only the most essential information
-
-- You can review these slides another time if you want, they'll be waiting for you ☺
-
----
-
class: title
*Tell me and I forget.*
diff --git a/slides/images/binpacking-1d-1.gif b/slides/images/binpacking-1d-1.gif
new file mode 100644
index 00000000..a77e0aa1
Binary files /dev/null and b/slides/images/binpacking-1d-1.gif differ
diff --git a/slides/images/binpacking-1d-2.gif b/slides/images/binpacking-1d-2.gif
new file mode 100644
index 00000000..bb0cc2a4
Binary files /dev/null and b/slides/images/binpacking-1d-2.gif differ
diff --git a/slides/images/binpacking-2d.gif b/slides/images/binpacking-2d.gif
new file mode 100644
index 00000000..827152e6
Binary files /dev/null and b/slides/images/binpacking-2d.gif differ
diff --git a/slides/images/binpacking-3d.gif b/slides/images/binpacking-3d.gif
new file mode 100644
index 00000000..06477477
Binary files /dev/null and b/slides/images/binpacking-3d.gif differ
diff --git a/slides/images/conductor.jpg b/slides/images/conductor.jpg
new file mode 100644
index 00000000..2fa74bbe
Binary files /dev/null and b/slides/images/conductor.jpg differ
diff --git a/slides/images/docker-con-15-logo.svg b/slides/images/docker-con-15-logo.svg
new file mode 100644
index 00000000..82df2702
--- /dev/null
+++ b/slides/images/docker-con-15-logo.svg
@@ -0,0 +1,213 @@
+
+
+
+
diff --git a/slides/images/docker-ecosystem-2015.png b/slides/images/docker-ecosystem-2015.png
new file mode 100644
index 00000000..00210c16
Binary files /dev/null and b/slides/images/docker-ecosystem-2015.png differ
diff --git a/slides/images/docker-engine-architecture.svg b/slides/images/docker-engine-architecture.svg
new file mode 100644
index 00000000..afe563ae
--- /dev/null
+++ b/slides/images/docker-engine-architecture.svg
@@ -0,0 +1,2597 @@
+
+
diff --git a/slides/images/dockerd-and-containerd.png b/slides/images/dockerd-and-containerd.png
new file mode 100644
index 00000000..74e34871
Binary files /dev/null and b/slides/images/dockerd-and-containerd.png differ
diff --git a/slides/images/fu-face.jpg b/slides/images/fu-face.jpg
new file mode 100644
index 00000000..f2c62a06
Binary files /dev/null and b/slides/images/fu-face.jpg differ
diff --git a/slides/images/getting-inside.png b/slides/images/getting-inside.png
new file mode 100644
index 00000000..b1ac54d2
Binary files /dev/null and b/slides/images/getting-inside.png differ
diff --git a/slides/images/tangram.gif b/slides/images/tangram.gif
new file mode 100644
index 00000000..d08ccd1a
Binary files /dev/null and b/slides/images/tangram.gif differ
diff --git a/slides/images/tesla.jpg b/slides/images/tesla.jpg
new file mode 100644
index 00000000..dca81281
Binary files /dev/null and b/slides/images/tesla.jpg differ
diff --git a/slides/images/tetris-1.png b/slides/images/tetris-1.png
new file mode 100644
index 00000000..7302c2d1
Binary files /dev/null and b/slides/images/tetris-1.png differ
diff --git a/slides/images/tetris-2.gif b/slides/images/tetris-2.gif
new file mode 100644
index 00000000..505c5c1b
Binary files /dev/null and b/slides/images/tetris-2.gif differ
diff --git a/slides/images/tetris-3.png b/slides/images/tetris-3.png
new file mode 100644
index 00000000..15069e79
Binary files /dev/null and b/slides/images/tetris-3.png differ
diff --git a/slides/images/traffic-graph.png b/slides/images/traffic-graph.png
new file mode 100644
index 00000000..1b154acf
Binary files /dev/null and b/slides/images/traffic-graph.png differ
diff --git a/slides/images/trollface.png b/slides/images/trollface.png
new file mode 100644
index 00000000..cce7c758
Binary files /dev/null and b/slides/images/trollface.png differ
diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml
index e118afe4..80e365e9 100644
--- a/slides/intro-fullday.yml
+++ b/slides/intro-fullday.yml
@@ -16,7 +16,7 @@ chapters:
- common/about-slides.md
- common/toc.md
- - intro/Docker_Overview.md
- #- intro/Docker_History.md
+ - intro/Docker_History.md
- intro/Training_Environment.md
- intro/Installing_Docker.md
- intro/First_Containers.md
@@ -31,6 +31,8 @@ chapters:
- intro/Publishing_To_Docker_Hub.md
- intro/Dockerfile_Tips.md
- - intro/Naming_And_Inspecting.md
+ - intro/Labels.md
+ - intro/Getting_Inside.md
- intro/Container_Networking_Basics.md
- intro/Network_Drivers.md
- intro/Container_Network_Model.md
@@ -39,6 +41,15 @@ chapters:
- - intro/Local_Development_Workflow.md
- intro/Working_With_Volumes.md
- intro/Compose_For_Dev_Stacks.md
+ - intro/Docker_Machine.md
- intro/Advanced_Dockerfiles.md
+ - intro/Application_Configuration.md
+ - intro/Logging.md
+- - intro/Namespaces_Cgroups.md
+ - intro/Copy_On_Write.md
+ #- intro/Containers_From_Scratch.md
+ - intro/Container_Engines.md
+ - intro/Ecosystem.md
+ - intro/Orchestration_Overview.md
- common/thankyou.md
- intro/links.md
diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml
index 38d212e3..883184f5 100644
--- a/slides/intro-selfpaced.yml
+++ b/slides/intro-selfpaced.yml
@@ -16,7 +16,7 @@ chapters:
- common/about-slides.md
- common/toc.md
- - intro/Docker_Overview.md
- #- intro/Docker_History.md
+ - intro/Docker_History.md
- intro/Training_Environment.md
- intro/Installing_Docker.md
- intro/First_Containers.md
@@ -31,6 +31,8 @@ chapters:
- intro/Publishing_To_Docker_Hub.md
- intro/Dockerfile_Tips.md
- - intro/Naming_And_Inspecting.md
+ - intro/Labels.md
+ - intro/Getting_Inside.md
- intro/Container_Networking_Basics.md
- intro/Network_Drivers.md
- intro/Container_Network_Model.md
@@ -39,6 +41,15 @@ chapters:
- - intro/Local_Development_Workflow.md
- intro/Working_With_Volumes.md
- intro/Compose_For_Dev_Stacks.md
+ - intro/Docker_Machine.md
- intro/Advanced_Dockerfiles.md
+ - intro/Application_Configuration.md
+ - intro/Logging.md
+- - intro/Namespaces_Cgroups.md
+ - intro/Copy_On_Write.md
+ #- intro/Containers_From_Scratch.md
+ - intro/Container_Engines.md
+ - intro/Ecosystem.md
+ - intro/Orchestration_Overview.md
- common/thankyou.md
- intro/links.md
diff --git a/slides/intro/Application_Configuration.md b/slides/intro/Application_Configuration.md
new file mode 100644
index 00000000..351ca413
--- /dev/null
+++ b/slides/intro/Application_Configuration.md
@@ -0,0 +1,201 @@
+# Application Configuration
+
+There are many ways to provide configuration to containerized applications.
+
+There is no "best way" — it depends on factors like:
+
+* configuration size,
+
+* mandatory and optional parameters,
+
+* scope of configuration (per container, per app, per customer, per site, etc),
+
+* frequency of changes in the configuration.
+
+---
+
+## Command-line parameters
+
+```bash
+docker run jpetazzo/hamba 80 www1:80 www2:80
+```
+
+* Configuration is provided through command-line parameters.
+
+* In the above example, the `ENTRYPOINT` is a script that will:
+
+ - parse the parameters,
+
+ - generate a configuration file,
+
+ - start the actual service.
+
+---
+
+## Command-line parameters pros and cons
+
+* Appropriate for mandatory parameters (without which the service cannot start).
+
+* Convenient for "toolbelt" services instanciated many times.
+
+ (Because there is no extra step: just run it!)
+
+* Not great for dynamic configurations or bigger configurations.
+
+ (These things are still possible, but more cumbersome.)
+
+---
+
+## Environment variables
+
+```bash
+docker run -e ELASTICSEARCH_URL=http://es42:9201/ kibana
+```
+
+* Configuration is provided through environment variables.
+
+* The environment variable can be used straight by the program,
+ or by a script generating a configuration file.
+
+---
+
+## Environment variables pros and cons
+
+* Appropriate for optional parameters (since the image can provide default values).
+
+* Also convenient for services instanciated many times.
+
+ (It's as easy as command-line parameters.)
+
+* Great for services with lots of parameters, but you only want to specify a few.
+
+ (And use default values for everything else.)
+
+* Ability to introspect possible parameters and their default values.
+
+* Not great for dynamic configurations.
+
+---
+
+## Baked-in configuration
+
+```
+FROM prometheus
+COPY prometheus.conf /etc
+```
+
+* The configuration is added to the image.
+
+* The image may have a default configuration; the new configuration can:
+
+ - replace the default configuration,
+
+ - extend it (if the code can read multiple configuration files).
+
+---
+
+## Baked-in configuration pros and cons
+
+* Allows arbitrary customization and complex configuration files.
+
+* Requires to write a configuration file. (Obviously!)
+
+* Requires to build an image to start the service.
+
+* Requires to rebuild the image to reconfigure the service.
+
+* Requires to rebuild the image to upgrade the service.
+
+* Configured images can be stored in registries.
+
+ (Which is great, but requires a registry.)
+
+---
+
+## Configuration volume
+
+```bash
+docker run -v appconfig:/etc/appconfig myapp
+```
+
+* The configuration is stored in a volume.
+
+* The volume is attached to the container.
+
+* The image may have a default configuration.
+
+ (But this results in a less "obvious" setup, that needs more documentation.)
+
+---
+
+## Configuration volume pros and cons
+
+* Allows arbitrary customization and complex configuration files.
+
+* Requires to create a volume for each different configuration.
+
+* Services with identical configurations can use the same volume.
+
+* Doesn't require to build / rebuild an image when upgrading / reconfiguring.
+
+* Configuration can be generated or edited through another container.
+
+---
+
+## Dynamic configuration volume
+
+* This is a powerful pattern for dynamic, complex configurations.
+
+* The configuration is stored in a volume.
+
+* The configuration is generated / updated by a special container.
+
+* The application container detects when the configuration is changed.
+
+ (And automatically reloads the configuration when necessary.)
+
+* The configuration can be shared between multiple services if needed.
+
+---
+
+## Dynamic configuration volume example
+
+In a first terminal, start a load balancer with an initial configuration:
+
+```bash
+$ docker run --name loadbalancer jpetazzo/hamba \
+ 80 goo.gl:80
+```
+
+In another terminal, reconfigure that load balancer:
+
+```bash
+$ docker run --rm --volumes-from loadbalancer jpetazzo/hamba reconfigure \
+ 80 google.com:80
+```
+
+The configuration could also be updated through e.g. a REST API.
+
+(The REST API being itself served from another container.)
+
+---
+
+## Keeping secrets
+
+.warning[Ideally, you should not put secrets (passwords, tokens...) in:]
+
+* command-line or environment variables (anyone with Docker API access can get them),
+
+* images, especially stored in a registry.
+
+Secrets management is better handled with an orchestrator (like Swarm or Kubernetes).
+
+Orchestrators will allow to pass secrets in a "one-way" manner.
+
+Managing secrets securely without an orchestrator can be contrived.
+
+E.g.:
+
+- read the secret on stdin when the service starts,
+
+- pass the secret using an API endpoint.
\ No newline at end of file
diff --git a/slides/intro/Building_Images_With_Dockerfiles.md b/slides/intro/Building_Images_With_Dockerfiles.md
index 13e7f531..d45da8b4 100644
--- a/slides/intro/Building_Images_With_Dockerfiles.md
+++ b/slides/intro/Building_Images_With_Dockerfiles.md
@@ -93,20 +93,22 @@ The output of `docker build` looks like this:
.small[
```bash
-$ docker build -t figlet .
-Sending build context to Docker daemon 2.048 kB
-Sending build context to Docker daemon
-Step 0 : FROM ubuntu
- ---> e54ca5efa2e9
-Step 1 : RUN apt-get update
- ---> Running in 840cb3533193
- ---> 7257c37726a1
-Removing intermediate container 840cb3533193
-Step 2 : RUN apt-get install figlet
- ---> Running in 2b44df762a2f
- ---> f9e8f1642759
-Removing intermediate container 2b44df762a2f
-Successfully built f9e8f1642759
+docker build -t figlet .
+Sending build context to Docker daemon 2.048kB
+Step 1/3 : FROM ubuntu
+ ---> f975c5035748
+Step 2/3 : RUN apt-get update
+ ---> Running in e01b294dbffd
+(...output of the RUN command...)
+Removing intermediate container e01b294dbffd
+ ---> eb8d9b561b37
+Step 3/3 : RUN apt-get install figlet
+ ---> Running in c29230d70f9b
+(...output of the RUN command...)
+Removing intermediate container c29230d70f9b
+ ---> 0dfd7a253f21
+Successfully built 0dfd7a253f21
+Successfully tagged figlet:latest
```
]
@@ -134,20 +136,20 @@ Sending build context to Docker daemon 2.048 kB
## Executing each step
```bash
-Step 1 : RUN apt-get update
- ---> Running in 840cb3533193
+Step 2/3 : RUN apt-get update
+ ---> Running in e01b294dbffd
(...output of the RUN command...)
- ---> 7257c37726a1
-Removing intermediate container 840cb3533193
+Removing intermediate container e01b294dbffd
+ ---> eb8d9b561b37
```
-* A container (`840cb3533193`) is created from the base image.
-
* The `RUN` command is executed in this container.
-* The container is committed into an image (`7257c37726a1`).
+* A container (`e01b294dbffd`) is created from the base image.
-* The build container (`840cb3533193`) is removed.
+* The build container (`e01b294dbffd`) is removed.
+
+* The container is committed into an image (`eb8d9b561b37`).
* The output of this step will be the base image for the next one.
diff --git a/slides/intro/Cmd_And_Entrypoint.md b/slides/intro/Cmd_And_Entrypoint.md
index be6d04be..5e14615e 100644
--- a/slides/intro/Cmd_And_Entrypoint.md
+++ b/slides/intro/Cmd_And_Entrypoint.md
@@ -64,6 +64,7 @@ Let's build it:
$ docker build -t figlet .
...
Successfully built 042dff3b4a8d
+Successfully tagged figlet:latest
```
And run it:
@@ -165,6 +166,7 @@ Let's build it:
$ docker build -t figlet .
...
Successfully built 36f588918d73
+Successfully tagged figlet:latest
```
And run it:
@@ -223,6 +225,7 @@ Let's build it:
$ docker build -t figlet .
...
Successfully built 6e0b6a048a07
+Successfully tagged figlet:latest
```
Run it without parameters:
diff --git a/slides/intro/Container_Engines.md b/slides/intro/Container_Engines.md
new file mode 100644
index 00000000..74e1d608
--- /dev/null
+++ b/slides/intro/Container_Engines.md
@@ -0,0 +1,177 @@
+# Docker Engine and other container engines
+
+* We are going to cover the architecture of the Docker Engine.
+
+* We will also present other container engines.
+
+---
+
+class: pic
+
+## Docker Engine external architecture
+
+
+
+---
+
+## Docker Engine external architecture
+
+* The Engine is a daemon (service running in the background).
+
+* All interaction is done through a REST API exposed over a socket.
+
+* On Linux, the default socket is a UNIX socket: `/var/run/docker.sock`.
+
+* We can also use a TCP socket, with optional mutual TLS authentication.
+
+* The `docker` CLI communicates with the Engine over the socket.
+
+Note: strictly speaking, the Docker API is not fully REST.
+
+Some operations (e.g. dealing with interactive containers
+and log streaming) don't fit the REST model.
+
+---
+
+class: pic
+
+## Docker Engine internal architecture
+
+
+
+---
+
+## Docker Engine internal architecture
+
+* Up to Docker 1.10: the Docker Engine is one single monolithic binary.
+
+* Starting with Docker 1.11, the Engine is split into multiple parts:
+
+ - `dockerd` (REST API, auth, networking, storage)
+
+ - `containerd` (container lifecycle, controlled over a gRPC API)
+
+ - `containerd-shim` (per-container; does almost nothing but allows to restart the Engine without restarting the containers)
+
+ - `runc` (per-container; does the actual heavy lifting to start the container)
+
+* Some features (like image and snapshot management) are progressively being pushed from `dockerd` to `containerd`.
+
+For more details, check [this short presentation by Phil Estes](https://www.slideshare.net/PhilEstes/diving-through-the-layers-investigating-runc-containerd-and-the-docker-engine-architecture).
+
+---
+
+## Other container engines
+
+The following list is not exhaustive.
+
+Furthermore, we limited the scope to Linux containers.
+
+Containers also exist (sometimes with other names) on Windows, macOS, Solaris, FreeBSD ...
+
+---
+
+## LXC
+
+* The venerable ancestor (first realeased in 2008).
+
+* Docker initially relied on it to execute containers.
+
+* No daemon; no central API.
+
+* Each container is managed by a `lxc-start` process.
+
+* Each `lxc-start` process exposes a custom API over a local UNIX socket, allowing to interact with the container.
+
+* No notion of image (container filesystems have to be managed manually).
+
+* Networking has to be setup manually.
+
+---
+
+## LXD
+
+* Re-uses LXC code (through liblxc).
+
+* Builds on top of LXC to offer a more modern experience.
+
+* Daemon exposing a REST API.
+
+* Can manage images, snapshots, migrations, networking, storage.
+
+* "offers a user experience similar to virtual machines but using Linux containers instead."
+
+---
+
+## rkt
+
+* Compares to `runc`.
+
+* No daemon or API.
+
+* Strong emphasis on security (through privilege separation).
+
+* Networking has to be setup separately (e.g. through CNI plugins).
+
+* Partial image management (pull, but no push).
+
+ (Image build is handled by separate tools.)
+
+---
+
+## CRI-O
+
+* Designed to be used with Kubernetes as a simple, basic runtime.
+
+* Compares to `containerd`.
+
+* Daemon exposing a gRPC interface.
+
+* Controlled using the CRI API (Container Runtime Interface defined by Kubernetes).
+
+* Needs an underlying OCI runtime (e.g. runc).
+
+* Handles storage, images, networking (through CNI plugins).
+
+We're not aware of anyone using it directly (i.e. outside of Kubernetes).
+
+---
+
+## systemd
+
+* "init" system (PID 1) in most modern Linux distributions.
+
+* Offers tools like `systemd-nspawn` and `machinectl` to manage containers.
+
+* `systemd-nspawn` is "In many ways it is similar to chroot(1), but more powerful".
+
+* `machinectl` can interact with VMs and containers managed by systemd.
+
+* Exposes a DBUS API.
+
+* Basic image support (tar archives and raw disk images).
+
+* Network has to be setup manually.
+
+---
+
+## Overall ...
+
+* The Docker Engine is very developer-centric:
+
+ - easy to install
+
+ - easy to use
+
+ - no manual setup
+
+ - first-class image build and transfer
+
+* As a result, it is a fantastic tool in development environments.
+
+* On servers:
+
+ - Docker is a good default choice
+
+ - If you use Kubernetes, the engine doesn't matter
+
diff --git a/slides/intro/Container_Networking_Basics.md b/slides/intro/Container_Networking_Basics.md
index 9cd7fed3..cdab1464 100644
--- a/slides/intro/Container_Networking_Basics.md
+++ b/slides/intro/Container_Networking_Basics.md
@@ -49,14 +49,14 @@ We will use `docker ps`:
```bash
$ docker ps
-CONTAINER ID IMAGE ... PORTS ...
-e40ffb406c9e nginx ... 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp ...
+CONTAINER ID IMAGE ... PORTS ...
+e40ffb406c9e nginx ... 0.0.0.0:32768->80/tcp ...
```
-* The web server is running on ports 80 and 443 inside the container.
+* The web server is running on port 80 inside the container.
-* Those ports are mapped to ports 32769 and 32768 on our Docker host.
+* This port is mapped to port 32768 on our Docker host.
We will explain the whys and hows of this port mapping.
@@ -81,7 +81,7 @@ Make sure to use the right port number if it is different
from the example below:
```bash
-$ curl localhost:32769
+$ curl localhost:32768
@@ -91,6 +91,31 @@ $ curl localhost:32769
---
+## How does Docker know which port to map?
+
+* There is metadata in the image telling "this image has something on port 80".
+
+* We can see that metadata with `docker inspect`:
+
+```bash
+$ docker inspect nginx --format {{.Config.ExposedPorts}}
+map[80/tcp:{}]
+```
+
+* This metadata was set in the Dockerfile, with the `EXPOSE` keyword.
+
+* We can see that with `docker history`:
+
+```bash
+$ docker history nginx
+IMAGE CREATED CREATED BY
+7f70b30f2cc6 11 days ago /bin/sh -c #(nop) CMD ["nginx" "-g" "…
+ 11 days ago /bin/sh -c #(nop) STOPSIGNAL [SIGTERM]
+ 11 days ago /bin/sh -c #(nop) EXPOSE 80/tcp
+```
+
+---
+
## Why are we mapping ports?
* We are out of IPv4 addresses.
@@ -113,7 +138,7 @@ There is a command to help us:
```bash
$ docker port 80
-32769
+32768
```
---
diff --git a/slides/intro/Containers_From_Scratch.md b/slides/intro/Containers_From_Scratch.md
new file mode 100644
index 00000000..1e38e09d
--- /dev/null
+++ b/slides/intro/Containers_From_Scratch.md
@@ -0,0 +1,3 @@
+# Building containers from scratch
+
+(This is a "bonus section" done if time permits.)
\ No newline at end of file
diff --git a/slides/intro/Copy_On_Write.md b/slides/intro/Copy_On_Write.md
new file mode 100644
index 00000000..a5b84b60
--- /dev/null
+++ b/slides/intro/Copy_On_Write.md
@@ -0,0 +1,339 @@
+# Copy-on-write filesystems
+
+Container engines rely on copy-on-write to be able
+to start containers quickly, regardless of their size.
+
+We will explain how that works, and review some of
+the copy-on-write storage systems available on Linux.
+
+---
+
+## What is copy-on-write?
+
+- Copy-on-write is a mechanism allowing to share data.
+
+- The data appears to be a copy, but is only
+ a link (or reference) to the original data.
+
+- The actual copy happens only when someone
+ tries to change the shared data.
+
+- Whoever changes the shared data ends up
+ using their own copy instead of the shared data.
+
+---
+
+## A few metaphors
+
+--
+
+- First metaphor:
+ white board and tracing paper
+
+--
+
+- Second metaphor:
+ magic books with shadowy pages
+
+--
+
+- Third metaphor:
+ just-in-time house building
+
+---
+
+## Copy-on-write is *everywhere*
+
+- Process creation with `fork()`.
+
+- Consistent disk snapshots.
+
+- Efficient VM provisioning.
+
+- And, of course, containers.
+
+---
+
+## Copy-on-write and containers
+
+Copy-on-write is essential to give us "convenient" containers.
+
+- Creating a new container (from an existing image) is "free".
+
+ (Otherwise, we would have to copy the image first.)
+
+- Customizing a container (by tweaking a few files) is cheap.
+
+ (Adding a 1 KB configuration file to a 1 GB container takes 1 KB, not 1 GB.)
+
+- We can take snapshots, i.e. have "checkpoints" or "save points"
+ when building images.
+
+---
+
+## AUFS overview
+
+- The original (legacy) copy-on-write filesystem used by first versions of Docker.
+
+- Combine multiple *branches* in a specific order.
+
+- Each branch is just a normal directory.
+
+- You generally have:
+
+ - at least one read-only branch (at the bottom),
+
+ - exactly one read-write branch (at the top).
+
+ (But other fun combinations are possible too!)
+
+---
+
+## AUFS operations: opening a file
+
+- With `O_RDONLY` - read-only access:
+
+ - look it up in each branch, starting from the top
+
+ - open the first one we find
+
+- With `O_WRONLY` or `O_RDWR` - write access:
+
+ - if the file exists on the top branch: open it
+
+ - if the file exists on another branch: "copy up"
+
+ (i.e. copy the file to the top branch and open the copy)
+
+ - if the file doesn't exist on any branch: create it on the top branch
+
+That "copy-up" operation can take a while if the file is big!
+
+---
+
+## AUFS operations: deleting a file
+
+- A *whiteout* file is created.
+
+- This is similar to the concept of "tombstones" used in some data systems.
+
+```
+ # docker run ubuntu rm /etc/shadow
+
+ # ls -la /var/lib/docker/aufs/diff/$(docker ps --no-trunc -lq)/etc
+ total 8
+ drwxr-xr-x 2 root root 4096 Jan 27 15:36 .
+ drwxr-xr-x 5 root root 4096 Jan 27 15:36 ..
+ -r--r--r-- 2 root root 0 Jan 27 15:36 .wh.shadow
+```
+
+---
+
+## AUFS performance
+
+- AUFS `mount()` is fast, so creation of containers is quick.
+
+- Read/write access has native speeds.
+
+- But initial `open()` is expensive in two scenarios:
+
+ - when writing big files (log files, databases ...),
+
+ - when searching many directories (PATH, classpath, etc.) over many layers.
+
+- Protip: when we built dotCloud, we ended up putting
+ all important data on *volumes*.
+
+- When starting the same container multiple times:
+
+ - the data is loaded only once from disk, and cached only once in memory;
+
+ - but `dentries` will be duplicated.
+
+---
+
+## Device Mapper
+
+Device Mapper is a rich subsystem with many features.
+
+It can be used for: RAID, encrypted devices, snapshots, and more.
+
+In the context of containers (and Docker in particular), "Device Mapper"
+means:
+
+"the Device Mapper system + its *thin provisioning target*"
+
+If you see the abbreviation "thinp" it stands for "thin provisioning".
+
+---
+
+## Device Mapper principles
+
+- Copy-on-write happens on the *block* level
+ (instead of the *file* level).
+
+- Each container and each image get their own block device.
+
+- At any given time, it is possible to take a snapshot:
+
+ - of an existing container (to create a frozen image),
+
+ - of an existing image (to create a container from it).
+
+- If a block has never been written to:
+
+ - it's assumed to be all zeros,
+
+ - it's not allocated on disk.
+
+(That last property is the reason for the name "thin" provisioning.)
+
+---
+
+## Device Mapper operational details
+
+- Two storage areas are needed:
+ one for *data*, another for *metadata*.
+
+- "data" is also called the "pool"; it's just a big pool of blocks.
+
+ (Docker uses the smallest possible block size, 64 KB.)
+
+- "metadata" contains the mappings between virtual offsets (in the
+ snapshots) and physical offsets (in the pool).
+
+- Each time a new block (or a copy-on-write block) is written,
+ a block is allocated from the pool.
+
+- When there are no more blocks in the pool, attempts to write
+ will stall until the pool is increased (or the write operation
+ aborted).
+
+- In other words: when running out of space, containers are
+ frozen, but operations will resume as soon as space is available.
+
+---
+
+## Device Mapper performance
+
+- By default, Docker puts data and metadata on a loop device
+ backed by a sparse file.
+
+- This is great from a usability point of view,
+ since zero configuration is needed.
+
+- But it is terrible from a performance point of view:
+
+ - each time a container writes to a new block,
+ - a block has to be allocated from the pool,
+ - and when it's written to,
+ - a block has to be allocated from the sparse file,
+ - and sparse file performance isn't great anyway.
+
+- If you use Device Mapper, make sure to put data (and metadata)
+ on devices!
+
+---
+
+## BTRFS principles
+
+- BTRFS is a filesystem (like EXT4, XFS, NTFS...) with built-in snapshots.
+
+- The "copy-on-write" happens at the filesystem level.
+
+- BTRFS integrates the snapshot and block pool management features
+ at the filesystem level.
+
+ (Instead of the block level for Device Mapper.)
+
+- In practice, we create a "subvolume" and
+ later take a "snapshot" of that subvolume.
+
+ Imagine: `mkdir` with Super Powers and `cp -a` with Super Powers.
+
+- These operations can be executed with the `btrfs` CLI tool.
+
+---
+
+## BTRFS in practice with Docker
+
+- Docker can use BTRFS and its snapshotting features to store container images.
+
+- The only requirement is that `/var/lib/docker` is on a BTRFS filesystem.
+
+ (Or, the directory specified with the `--data-root` flag when starting the engine.)
+
+---
+
+class: extra-details
+
+## BTRFS quirks
+
+- BTRFS works by dividing its storage in *chunks*.
+
+- A chunk can contain data or metadata.
+
+- You can run out of chunks (and get `No space left on device`)
+ even though `df` shows space available.
+
+ (Because chunks are only partially allocated.)
+
+- Quick fix:
+
+```
+ # btrfs filesys balance start -dusage=1 /var/lib/docker
+```
+
+---
+
+## Overlay2
+
+- Overlay2 is very similar to AUFS.
+
+- However, it has been merged in "upstream" kernel.
+
+- It is therefore available on all modern kernels.
+
+ (AUFS was available on Debian and Ubuntu, but required custom kernels on other distros.)
+
+- It is simpler than AUFS (it can only have two branches, called "layers").
+
+- The container engine abstracts this detail, so this is not a concern.
+
+- Overlay2 storage drivers generally use hard links between layers.
+
+- This improves `stat()` and `open()` performance, at the expense of inode usage.
+
+---
+
+## ZFS
+
+- ZFS is similar to BTRFS (at least from a container user's perspective).
+
+- Pros:
+
+ - high performance
+ - high reliability (with e.g. data checksums)
+ - optional data compression and deduplication
+
+- Cons:
+
+ - high memory usage
+ - not in upstream kernel
+
+- It is available as a kernel module or through FUSE.
+
+---
+
+## Which one is the best?
+
+- According to Michael Crosby (core Docker maintainer), overlay2!
+
+- Overlay2 is available on all modern systems.
+
+- Its memory usage is better than Device Mapper, BTRFS, or ZFS.
+
+- The remarks about *write performance* shouldn't bother you:
+
+ data should always be stored in volumes anyway!
+
diff --git a/slides/intro/Docker_Machine.md b/slides/intro/Docker_Machine.md
new file mode 100644
index 00000000..e22d8705
--- /dev/null
+++ b/slides/intro/Docker_Machine.md
@@ -0,0 +1,81 @@
+# Managing hosts with Docker Machine
+
+- Docker Machine is a tool to provision and manage Docker hosts.
+
+- It automates the creation of a virtual machine:
+
+ - locally, with a tool like VirtualBox or VMware;
+
+ - on a public cloud like AWS EC2, Azure, Digital Ocean, GCP, etc.;
+
+ - on a private cloud like OpenStack.
+
+- It can also configure existing machines through an SSH connection.
+
+- It can manage as many hosts as you want, with as many "drivers" as you want.
+
+---
+
+## Docker Machine workflow
+
+1) Prepare the environment: setup VirtualBox, obtain cloud credentials ...
+
+2) Create hosts with `docker-machine create -d drivername machinename`.
+
+3) Use a specific machine with `eval $(docker-machine env machinename)`.
+
+4) Profit!
+
+---
+
+## Environment variables
+
+- Most of the tools (CLI, libraries...) connecting to the Docker API can use ennvironment variables.
+
+- These variables are:
+
+ - `DOCKER_HOST` (indicates address+port to connect to, or path of UNIX socket)
+
+ - `DOCKER_TLS_VERIFY` (indicates that TLS mutual auth should be used)
+
+ - `DOCKER_CERT_PATH` (path to the keypair and certificate to use for auth)
+
+- `docker-machine env ...` will generate the variables needed to connect to an host.
+
+- `$(eval docker-machine env ...)` sets these variables in the current shell.
+
+---
+
+## Host management features
+
+With `docker-machine`, we can:
+
+- upgrade an host to the latest version of the Docker Engine,
+
+- start/stop/restart hosts,
+
+- get a shell on a remote machine (with SSH),
+
+- copy files to/from remotes machines (with SCP),
+
+- mount a remote host's directory on the local machine (with SSHFS),
+
+- ...
+
+---
+
+## The `generic` driver
+
+When provisioning a new host, `docker-machine` executes these steps:
+
+1) Create the host using a cloud or hypervisor API.
+
+2) Connect to the host over SSH.
+
+3) Install and configure Docker on the host.
+
+With the `generic` driver, we provide the IP address of an existing host
+(instead of e.g. cloud credentials) and we omit the first step.
+
+This allows to provision physical machines, or VMs provided by a 3rd
+party, or use a cloud for which we don't have a provisioning API.
diff --git a/slides/intro/Ecosystem.md b/slides/intro/Ecosystem.md
new file mode 100644
index 00000000..20c892e9
--- /dev/null
+++ b/slides/intro/Ecosystem.md
@@ -0,0 +1,173 @@
+# The container ecosystem
+
+In this chapter, we will talk about a few actors of the container ecosystem.
+
+We have (arbitrarily) decided to focus on two groups:
+
+- the Docker ecosystem,
+
+- the Cloud Native Computing Foundation (CNCF) and its projects.
+
+---
+
+class: pic
+
+## The Docker ecosystem
+
+
+
+---
+
+## Moby vs. Docker
+
+- Docker Inc. (the company) started Docker (the open source project).
+
+- At some point, it became necessary to differentiate between:
+
+ - the open source project (code base, contributors...),
+
+ - the product that we use to run containers (the engine),
+
+ - the platform that we use to manage containerized applications,
+
+ - the brand.
+
+---
+
+class: pic
+
+
+
+---
+
+## Exercise in brand management
+
+Questions:
+
+--
+
+- What is the brand of the car on the previous slide?
+
+--
+
+- What kind of engine does it have?
+
+--
+
+- Would you say that it's a safe or unsafe car?
+
+--
+
+- Harder question: can you drive from the US West to East coasts with it?
+
+--
+
+The answers to these questions are part of the Tesla brand.
+
+---
+
+## What if ...
+
+- The blueprints for Tesla cars were available for free.
+
+- You could legally build your own Tesla.
+
+- You were allowed to customize it entirely.
+
+ (Put a combustion engine, drive it with a game pad ...)
+
+- You could even sell the customized versions.
+
+--
+
+- ... And call your customized version "Tesla".
+
+--
+
+Would we give the same answers to the questions on the previous slide?
+
+---
+
+## From Docker to Moby
+
+- Docker Inc. decided to split the brand.
+
+- Moby is the open source project.
+
+ (= Components and libraries that you can use, reuse, customize, sell ...)
+
+- Docker is the product.
+
+ (= Software that you can use, buy support contracts ...)
+
+- Docker is made with Moby.
+
+- When Docker Inc. improves the Docker products, it improves Moby.
+
+ (And vice versa.)
+
+
+---
+
+## Other examples
+
+- *Read the Docs* is an open source project to generate and host documentation.
+
+- You can host it yourself (on your own servers).
+
+- You can also get hosted on readthedocs.org.
+
+- The maintainers of the open source project often receive
+ support requests from users of the hosted product ...
+
+- ... And the maintainers of the hosted product often
+ receive support requests from users of self-hosted instances.
+
+- Another example:
+
+ *WordPress.com is a blogging platform that is owned and hosted online by
+ Automattic. It is run on WordPress, an open source piece of software used by
+ bloggers. (Wikipedia)*
+
+---
+
+## Docker CE vs Docker EE
+
+- Docker CE = Community Edition.
+
+- Available on most Linux distros, Mac, Windows.
+
+- Optimized for developers and ease of use.
+
+- Docker EE = Enterprise Edition.
+
+- Available only on a subset of Linux distros + Windows servers.
+
+ (Only available when there is a strong partnership to offer enterprise-class support.)
+
+- Optimized for production use.
+
+- Comes with additional components: security scanning, RBAC ...
+
+---
+
+## The CNCF
+
+- Non-profit, part of the Linux Foundation; founded in December 2015.
+
+ *The Cloud Native Computing Foundation builds sustainable ecosystems and fosters
+ a community around a constellation of high-quality projects that orchestrate
+ containers as part of a microservices architecture.*
+
+ *CNCF is an open source software foundation dedicated to making cloud-native computing universal and sustainable.*
+
+- Home of Kubernetes (and many other projects now).
+
+- Funded by corporate memberships.
+
+---
+
+class: pic
+
+
+
diff --git a/slides/intro/Getting_Inside.md b/slides/intro/Getting_Inside.md
new file mode 100644
index 00000000..ce3e436e
--- /dev/null
+++ b/slides/intro/Getting_Inside.md
@@ -0,0 +1,227 @@
+class: title
+
+# Getting inside a container
+
+
+
+---
+
+## Objectives
+
+On a traditional server or VM, we sometimes need to:
+
+* log into the machine (with SSH or on the console),
+
+* analyze the disks (by removing them or rebooting with a rescue system).
+
+In this chapter, we will see how to do that with containers.
+
+---
+
+## Getting a shell
+
+Every once in a while, we want to log into a machine.
+
+In an perfect world, this shouldn't be necessary.
+
+* You need to install or update packages (and their configuration)?
+
+ Use configuration management. (e.g. Ansible, Chef, Puppet, Salt...)
+
+* You need to view logs and metrics?
+
+ Collect and access them through a centralized platform.
+
+In the real world, though ... we often need shell access!
+
+---
+
+## Not getting a shell
+
+Even without a perfect deployment system, we can do many operations without getting a shell.
+
+* Installing packages can (and should) be done in the container image.
+
+* Configuration can be done at the image level, or when the container starts.
+
+* Dynamic configuration can be stored in a volume (shared with another container).
+
+* Logs written to stdout are automatically collected by the Docker Engine.
+
+* Other logs can be written to a shared volume.
+
+* Process information and metrics are visible from the host.
+
+_Let's save logging, volumes ... for later, but let's have a look at process information!_
+
+---
+
+## Viewing container processes from the host
+
+If you run Docker on Linux, container processes are visible on the host.
+
+```bash
+$ ps faux | less
+```
+
+* Scroll around the output of this command.
+
+* You should see the `jpetazzo/clock` container.
+
+* A containerized process is just like any other process on the host.
+
+* We can use tools like `lsof`, `strace`, `gdb` ... To analyze them.
+
+---
+
+class: extra-details
+
+## What's the difference between a container process and a host process?
+
+* Each process (containerized or not) belongs to *namespaces* and *cgroups*.
+
+* The namespaces and cgroups determine what a process can "see" and "do".
+
+* Analogy: each process (containerized or not) runs with a specific UID (user ID).
+
+* UID=0 is root, and has elevated privileges. Other UIDs are normal users.
+
+_We will give more details about namespaces and cgroups later._
+
+---
+
+## Getting a shell in a running container
+
+* Sometimes, we need to get a shell anyway.
+
+* We _could_ run some SSH server in the container ...
+
+* But it is easier to use `docker exec`.
+
+```bash
+$ docker exec -ti ticktock sh
+```
+
+* This creates a new process (running `sh`) _inside_ the container.
+
+* This can also be done "manually" with the tool `nsenter`.
+
+---
+
+## Caveats
+
+* The tool that you want to run needs to exist in the container.
+
+* Some tools (like `ip netns exec`) let you attach to _one_ namespace at a time.
+
+ (This lets you e.g. setup network interfaces, even if you don't have `ifconfig` or `ip` in the container.)
+
+* Most importantly: the container needs to be running.
+
+* What if the container is stopped or crashed?
+
+---
+
+## Getting a shell in a stopped container
+
+* A stopped container is only _storage_ (like a disk drive).
+
+* We cannot SSH into a disk drive or USB stick!
+
+* We need to connect the disk to a running machine.
+
+* How does that translate into the container world?
+
+---
+
+## Analyzing a stopped container
+
+As an exercise, we are going to try to find out what's wrong with `jpetazzo/crashtest`.
+
+```bash
+docker run jpetazzo/crashtest
+```
+
+The container starts, but then stops immediately, without any output.
+
+What would McGyver do?
+
+First, let's check the status of that container.
+
+```bash
+docker ps -l
+```
+
+---
+
+## Viewing filesystem changes
+
+* We can use `docker diff` to see files that were added / changed / removed.
+
+```bash
+docker diff
+```
+
+* The container ID was shown by `docker ps -l`.
+
+* We can also see it with `docker ps -lq`.
+
+* The output of `docker diff` shows some interesting log files!
+
+---
+
+## Accessing files
+
+* We can extract files with `docker cp`.
+
+```bash
+docker cp :/var/log/nginx/error.log .
+```
+
+* Then we can look at that log file.
+
+```bash
+cat error.log
+```
+
+(The directory `/run/nginx` doesn't exist.)
+
+---
+
+## Exploring a crashed container
+
+* We can restart a container with `docker start` ...
+
+* ... But it will probably crash again immediately!
+
+* We cannot specify a different program to run with `docker start`
+
+* But we can create a new image from the crashed container
+
+```bash
+docker commit debugimage
+```
+
+* Then we can run a new container from that image, with a custom entrypoint
+
+```bash
+docker run -ti --entrypoint sh debugimage
+```
+
+---
+
+class: extra-details
+
+## Obtaining a complete dump
+
+* We can also dump the entire filesystem of a container.
+
+* This is done with `docker export`.
+
+* It generates a tar archive.
+
+```bash
+docker export | tar tv
+```
+
+This will give a detailed listing of the content of the container.
diff --git a/slides/intro/Installing_Docker.md b/slides/intro/Installing_Docker.md
index 959ea9d3..57dc92d2 100644
--- a/slides/intro/Installing_Docker.md
+++ b/slides/intro/Installing_Docker.md
@@ -29,7 +29,7 @@ We can arbitrarily distinguish:
* Installing Docker on an existing Linux machine (physical or VM)
-* Installing Docker on MacOS or Windows
+* Installing Docker on macOS or Windows
* Installing Docker on a fleet of cloud VMs
@@ -55,9 +55,31 @@ We can arbitrarily distinguish:
---
-## Installing Docker on MacOS and Windows
+class: extra-details
-* On MacOS, the recommended method is to use Docker4Mac:
+## Docker Inc. packages vs distribution packages
+
+* Docker Inc. releases new versions monthly (edge) and quarterly (stable)
+
+* Releases are immediately available on Docker Inc.'s package repositories
+
+* Linux distros don't always update to the latest Docker version
+
+ (Sometimes, updating would break their guidelines for major/minor upgrades)
+
+* Sometimes, some distros have carried packages with custom patches
+
+* Sometimes, these patches added critical security bugs ☹
+
+* Installing through Docker Inc.'s repositories is a bit of extra work …
+
+ … but it is generally worth it!
+
+---
+
+## Installing Docker on macOS and Windows
+
+* On macOS, the recommended method is to use Docker4Mac:
https://docs.docker.com/docker-for-mac/install/
@@ -71,7 +93,7 @@ We can arbitrarily distinguish:
---
-## Running Docker on MacOS and Windows
+## Running Docker on macOS and Windows
When you execute `docker version` from the terminal:
diff --git a/slides/intro/Labels.md b/slides/intro/Labels.md
new file mode 100644
index 00000000..5d29faf9
--- /dev/null
+++ b/slides/intro/Labels.md
@@ -0,0 +1,82 @@
+# Labels
+
+* Labels allow to attach arbitrary metadata to containers.
+
+* Labels are key/value pairs.
+
+* They are specified at container creation.
+
+* You can query them with `docker inspect`.
+
+* They can also be used as filters with some commands (e.g. `docker ps`).
+
+---
+
+## Using labels
+
+Let's create a few containers with a label `owner`.
+
+```bash
+docker run -d -l owner=alice nginx
+docker run -d -l owner=bob nginx
+docker run -d -l owner nginx
+```
+
+We didn't specify a value for the `owner` label in the last example.
+
+This is equivalent to setting the value to be an empty string.
+
+---
+
+## Querying labels
+
+We can view the labels with `docker inspect`.
+
+```bash
+$ docker inspect $(docker ps -lq) | grep -A3 Labels
+ "Labels": {
+ "maintainer": "NGINX Docker Maintainers ",
+ "owner": ""
+ },
+```
+
+We can use the `--format` flag to list the value of a label.
+
+```bash
+$ docker inspect $(docker ps -q) --format 'OWNER={{.Config.Labels.owner}}'
+```
+
+---
+
+## Using labels to select containers
+
+We can list containers having a specific label.
+
+```bash
+$ docker ps --filter label=owner
+```
+
+Or we can list containers having a specific label with a specific value.
+
+```bash
+$ docker ps --filter label=owner=alice
+```
+
+---
+
+## Use-cases for labels
+
+
+* HTTP vhost of a web app or web service.
+
+ (The label is used to generate the configuration for NGINX, HAProxy, etc.)
+
+* Backup schedule for a stateful service.
+
+ (The label is used by a cron job to determine if/when to backup container data.)
+
+* Service ownership.
+
+ (To determine internal cross-billing, or who to page in case of outage.)
+
+* etc.
diff --git a/slides/intro/Logging.md b/slides/intro/Logging.md
new file mode 100644
index 00000000..c5b463c3
--- /dev/null
+++ b/slides/intro/Logging.md
@@ -0,0 +1,273 @@
+# Logging
+
+In this chapter, we will explain the different ways to send logs from containers.
+
+We will then show one particular method in action, using ELK and Docker's logging drivers.
+
+---
+
+## There are many ways to send logs
+
+- The simplest method is to write on the standard output and error.
+
+- Applications can write their logs to local files.
+
+ (The files are usually periodically rotated and compressed.)
+
+- It is also very common (on UNIX systems) to use syslog.
+
+ (The logs are collected by syslogd or an equivalent like journald.)
+
+- In large applications with many components, it is common to use a logging service.
+
+ (The code uses a library to send messages to the logging service.)
+
+*All these methods are available with containers.*
+
+---
+
+## Writing on stdout/stderr
+
+- The standard output and error of containers is managed by the container engine.
+
+- This means that each line written by the container is received by the engine.
+
+- The engine can then do "whatever" with these log lines.
+
+- With Docker, the default configuration is to write the logs to local files.
+
+- The files can then be queried with e.g. `docker logs` (and the equivalent API request).
+
+- This can be customized, as we will see later.
+
+---
+
+## Writing to local files
+
+- If we write to files, it is possible to access them but cumbersome.
+
+ (We have to use `docker exec` or `docker cp`.)
+
+- Furthermore, if the container is stopped, we cannot use `docker exec`.
+
+- If the container is deleted, the logs disappear.
+
+- What should we do for programs who can only log to local files?
+
+--
+
+- There are multiple solutions.
+
+---
+
+## Using a volume or bind mount
+
+- Instead of writing logs to a normal directory, we can place them on a volume.
+
+- The volume can be accessed by other containers.
+
+- We can run a program like `filebeat` in another container accessing the same volume.
+
+ (`filebeat` reads local log files continuously, like `tail -f`, and sends them
+ to a centralized system like ElasticSearch.)
+
+- We can also use a bind mount, e.g. `-v /var/log/containers/www:/var/log/tomcat`.
+
+- The container will write log files to a directory mapped to a host directory.
+
+- The log files will appear on the host and be consumable directly from the host.
+
+---
+
+## Using logging services
+
+- We can use logging frameworks (like log4j or the Python `logging` package).
+
+- These frameworks require some code and/or configuration in our application code.
+
+- These mechanisms can be used identically inside or outside of containers.
+
+- Sometimes, we can leverage containerized networking to simplify their setup.
+
+- For instance, our code can send log messages to a server named `log`.
+
+- The name `log` will resolve to different addresses in development, production, etc.
+
+---
+
+## Using syslog
+
+- What if our code (or the program we are running in containers) uses syslog?
+
+- One possibility is to run a syslog daemon in the container.
+
+- Then that daemon can be setup to write to local files or forward to the network.
+
+- Under the hood, syslog clients connect to a local UNIX socket, `/dev/log`.
+
+- We can expose a syslog socket to the container (by using a volume or bind-mount).
+
+- Then just create a symlink from `/dev/log` to the syslog socket.
+
+- Voilà!
+
+---
+
+## Using logging drivers
+
+- If we log to stdout and stderr, the container engine receives the log messages.
+
+- The Docker Engine has a modular logging system with many plugins, including:
+
+ - json-file (the default one)
+ - syslog
+ - journald
+ - gelf
+ - fluentd
+ - splunk
+ - etc.
+
+- Each plugin can process and forward the logs to another process or system.
+
+---
+
+## Demo: sending logs to ELK
+
+- We are going to deploy an ELK stack.
+
+- It will accept logs over a GELF socket.
+
+- We will run a few containers with the `gelf` logging driver.
+
+- We will then see our logs in Kibana, the web interface provided by ELK.
+
+*Important foreword: this is not an "official" or "recommended"
+setup; it is just an example. We used ELK in this demo because
+it's a popular setup and we keep being asked about it; but you
+will have equal success with Fluent or other logging stacks!*
+
+---
+
+## What's in an ELK stack?
+
+- ELK is three components:
+
+ - ElasticSearch (to store and index log entries)
+
+ - Logstash (to receive log entries from various
+ sources, process them, and forward them to various
+ destinations)
+
+ - Kibana (to view/search log entries with a nice UI)
+
+- The only component that we will configure is Logstash
+
+- We will accept log entries using the GELF protocol
+
+- Log entries will be stored in ElasticSearch,
+ and displayed on Logstash's stdout for debugging
+
+---
+
+## Running ELK
+
+- We are going to use a Compose file describing the ELK stack.
+
+```bash
+$ cd ~/container.training/stacks
+$ docker-compose -f elk.yml up -d
+```
+
+- Let's have a look at the Compose file while it's deploying.
+
+---
+
+## Our basic ELK deployment
+
+- We are using images from the Docker Hub: `elasticsearch`, `logstash`, `kibana`.
+
+- We don't need to change the configuration of ElasticSearch.
+
+- We need to tell Kibana the address of ElasticSearch:
+
+ - it is set with the `ELASTICSEARCH_URL` environment variable,
+
+ - by default it is `localhost:9200`, we change it to `elastichsearch:9200`.
+
+- We need to configure Logstash:
+
+ - we pass the entire configuration file through command-line arguments,
+
+ - this is a hack so that we don't have to create an image just for the config.
+
+---
+
+## Sending logs to ELK
+
+- The ELK stack accepts log messages through a GELF socket.
+
+- The GELF socket listens on UDP port 12201.
+
+- To send a message, we need to change the logging driver used by Docker.
+
+- This can be done globally (by reconfiguring the Engine) or on a per-container basis.
+
+- Let's override the logging driver for a single container:
+
+```bash
+$ docker run --log-driver=gelf --log-opt=gelf-address=udp://localhost:12201 \
+ alpine echo hello world
+```
+
+---
+
+## Viewing the logs in ELK
+
+- Connect to the Kibana interface.
+
+- It is exposed on port 5601.
+
+- Browse http://X.X.X.X:5601.
+
+---
+
+## "Configuring" Kibana
+
+- Kibana should offer you to "Configure an index pattern":
+ in the "Time-field name" drop down, select "@timestamp", and hit the
+ "Create" button.
+
+- Then:
+
+ - click "Discover" (in the top-left corner),
+ - click "Last 15 minutes" (in the top-right corner),
+ - click "Last 1 hour" (in the list in the middle),
+ - click "Auto-refresh" (top-right corner),
+ - click "5 seconds" (top-left of the list).
+
+- You should see a series of green bars (with one new green bar every minute).
+
+- Our 'hello world' message should be visible there.
+
+---
+
+## Important afterword
+
+**This is not a "production-grade" setup.**
+
+It is just an educational example. Since we have only
+one node , we did set up a single
+ElasticSearch instance and a single Logstash instance.
+
+In a production setup, you need an ElasticSearch cluster
+(both for capacity and availability reasons). You also
+need multiple Logstash instances.
+
+And if you want to withstand
+bursts of logs, you need some kind of message queue:
+Redis if you're cheap, Kafka if you want to make sure
+that you don't drop messages on the floor. Good luck.
+
+If you want to learn more about the GELF driver,
+have a look at [this blog post](
+http://jpetazzo.github.io/2017/01/20/docker-logging-gelf/).
diff --git a/slides/intro/Namespaces_Cgroups.md b/slides/intro/Namespaces_Cgroups.md
new file mode 100644
index 00000000..ffae02ac
--- /dev/null
+++ b/slides/intro/Namespaces_Cgroups.md
@@ -0,0 +1,1012 @@
+# Deep dive into container internals
+
+In this chapter, we will explain some of the fundamental building blocks of containers.
+
+This will give you a solid foundation so you can:
+
+- understand "what's going on" in complex situations,
+
+- anticipate the behavior of containers (performance, security...) in new scenarios,
+
+- implement your own container engine.
+
+The last item should be done for educational purposes only!
+
+---
+
+## There is no container code in the Linux kernel
+
+- If we search "container" in the Linux kernel code, we find:
+
+ - generic code to manipulate data structures (like linked lists, etc.),
+
+ - unrelated concepts like "ACPI containers",
+
+ - *nothing* relevant to "our" containers!
+
+- Containers are composed using multiple independent feautres.
+
+- On Linux, containers rely on "namespaces, cgroups, and some filesystem magic."
+
+ (Other features are also involved for security: capabilities, seccompd, LSMs...)
+
+- We are going to explore namespaces and cgroups.
+
+ (Filesystems are covered in a dedicated chapter about copy-on-write.)
+
+---
+
+## Namespaces overview
+
+- Provide processes with their own view of the system.
+
+- Namespaces limit what you can see (and therefore, what you can use).
+
+- These namespaces are available in modern kernels:
+
+ - pid
+ - net
+ - mnt
+ - uts
+ - ipc
+ - user
+
+ (We are going to detail them individually.)
+
+- Each process belongs to one namespace of each type.
+
+---
+
+## Namespaces are always active
+
+- Namespaces exist even when you don't use containers.
+
+- This is a bit similar to the UID field in UNIX processes:
+
+ - all processes have the UID field, even if no user exists on the system
+
+ - the field always has a value / the value is always defined
+
+ (i.e. any process running on the system has some UID)
+
+ - the value of the UID field is used when checking permissions
+
+ (the UID field determines which resources the process can access)
+
+- You can replace "UID field" with "namespace" above and it still works!
+
+- In other words: even when you don't use containers,
+ there is one namespace of each type, containing all the processes on the system.
+
+---
+
+## Manipulating namespaces
+
+- Namespaces are created with two methods:
+
+ - the `clone()` system call (used when creating new threads and processes),
+
+ - the `unshare()` system call.
+
+- The Linux tool `unshare` allows to do that from a shell.
+
+- A new process can re-use none / all / some of the namespaces of its parent.
+
+- Namespaces are materialized by pseudo-files in `/proc//ns`.
+
+- When the last process of a namespace exits, the namespace is destroyed.
+
+ (Unless the namespace has been preserved by bind-mounting the pseudo-file.)
+
+- It is possible to "enter" a namespace with the `setns()` system call.
+
+- The Linux tool `nsenter` allows to do that from a shell.
+
+---
+
+## Namespaces can be used independently
+
+- As mentioned in the previous slide:
+
+ *A new process can re-use none / all / some of the namespaces of its parent.*
+
+- We are going to use that property in the examples in the next slides.
+
+- We are going to present each type of namespace.
+
+- For each type, we will provide an example using only that namespace.
+
+---
+
+## UTS namespace
+
+- gethostname / sethostname
+
+- Allows to set a custom hostname for a container.
+
+- That's (mostly) it!
+
+- Also allows to set the NIS domain.
+
+ (If you dont' know what a NIS domain is, you don't have to worry about it!)
+
+- If you're wondering: UTS = UNIX time sharing.
+
+- This namespace was named like this because of the `struct utsname`,
+
+ which is commonly used to obtain the machine's hostname, architecture, etc.
+
+ (The more you know!)
+
+---
+
+## Creating our first namespace
+
+Let's use `unshare` to create a new process that will have its own UTS namespace:
+
+```bash
+$ sudo unshare --uts
+```
+
+- We have to use `sudo` for most `unshare` operations.
+
+- We indicate that we want a new uts namespace, and nothing else.
+
+- If we don't specify a program to run, a `$SHELL` is started.
+
+---
+
+## Demonstrating our uts namespace
+
+In our new "container", check the hostname, change it, and check it:
+
+```bash
+ # hostname
+ nodeX
+ # hostname tupperware
+ # hostname
+ tupperware
+```
+
+In another shell, check that the machine's hostname hasn't changed:
+
+```bash
+$ hostname
+nodeX
+```
+
+Exit the "container" with `exit` or `Ctrl-D`.
+
+---
+
+## Net namespace in theory
+
+- Each network namespace has its own private network stack.
+
+- The network stack includes:
+
+ - network interfaces (including `lo`)
+
+ - routing table**s** (as in `ip rule` etc.)
+
+ - iptables chains and rules
+
+ - sockets (as seen by `ss`, `netstat`)
+
+- You can move a network interface from a network namespace to another:
+ ```bash
+ ip link set dev eth0 netns PID
+ ```
+
+---
+
+## Net namespace in practice
+
+- Typical use-case:
+
+ - each container is given its own network namespace
+
+ - for each network namespace (i.e. each container), a `veth` pair is created
+
+ - (reminder: the two `veth` interfaces act as if they were connected with a cross-over cable)
+
+ - one `veth` is move to the container network namespace (and renamed `eth0`)
+
+ - the other `veth` is moved to a bridge on the host (e.g. the `docker0` bridge)
+
+- But also:
+
+ - `--net host` means "do not containerize the network"
+
+ - `--net container` means "share the network stack with another container"
+
+---
+
+## Net namespace in action
+
+Start a new process with its own network namespace:
+
+```bash
+$ sudo unshare --net
+```
+
+See that this new network namespace is unconfigured:
+
+```bash
+ # ping 1.1
+ connect: Network is unreachable
+ # ifconfig
+ # ip link ls
+ 1: lo: mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+```
+
+---
+
+class: extra-details
+
+## Setting up connectivity for a new network namespace
+
+In another shell (on the host), create a `veth` pair:
+
+```bash
+$ sudo ip link add name in_host type veth peer name in_netns
+```
+
+Configure the host side (`in_host`):
+
+```bash
+$ sudo ip link set in_host master docker0 up
+```
+
+---
+
+class: extra-details
+
+## Moving the interface to the network namespace
+
+*In the process created by `unshare`,* check the PID of our "network container":
+
+```bash
+ # echo $$
+ 533
+```
+
+*On the host*, move the other side (`in_netns`) to the network namespace:
+
+```bash
+$ sudo ip link set in_netns netns 533
+```
+
+(Make sure to update "533" with the actual PID obtained above!)
+
+---
+
+class: extra-details
+
+## Basic configuration of the network namespace
+
+Let's set up `lo` (the loopback interface):
+
+```bash
+ # ip link set lo up
+```
+
+Activate the `veth` interface and rename it to `eth0`:
+
+```bash
+ # ip link set in_netns name eth0 up
+```
+
+---
+
+class: extra-details
+
+## Allocating IP address and default route
+
+*On the host*, check the address of the Docker bridge:
+
+```bash
+$ ip addr ls dev docker0
+```
+
+(It could be something like `172.17.0.1`.)
+
+Pick an IP address in the middle of the same subnet, e.g. `172.17.0.99`.
+
+*In the process created by `unshare`,* configure the interface:
+
+```bash
+ # ip addr add 172.17.0.99/24 dev eth0
+ # ip route add default via 172.17.0.1
+```
+
+(Make sure to update the IP addresses if necessary.)
+
+---
+
+class: extra-details
+
+## Validating the setup
+
+Check that we now have connectivity:
+
+```bash
+ # ping 1.1
+```
+
+Note: we were able to take a shortcut, because Docker is running,
+and provides us with a `docker0` bridge and a valid `iptables` setup.
+
+If Docker is not running, you will need to take care of this!
+
+---
+
+## Cleaning up network namespaces
+
+- Terminate the process created by `unshare` (with `exit` or `Ctrl-D`).
+
+- Since this was the only process in the network namespace, it is destroyed.
+
+- All the interfaces in the network namespace are destroyed.
+
+- When a `veth` interface is destroyed, it also destroys the other half of the pair.
+
+- So we don't have anything else to do to clean up!
+
+---
+
+## Mnt namespace
+
+- Processes can have their own root fs (à la chroot).
+
+- Processes can also have "private" mounts. This allows to:
+
+ - isolate `/tmp` (per user, per service...)
+
+ - mask `/proc`, `/sys` (for processes that don't need them)
+
+ - mount remote filesystems or sensitive data,
+ but make it visible only for allowed processes
+
+- Mounts can be totally private, or shared.
+
+- At this point, there is no easy way to pass along a mount
+ from a namespace to another.
+
+---
+
+## Setting up a private `/tmp`
+
+Create a new mount namespace:
+
+```bash
+$ sudo unshare --mount
+```
+
+In that new namespace, mount a brand new `/tmp`:
+
+```bash
+ # mount -t tmpfs none /tmp
+```
+
+Check the content of `/tmp` in the new namespace, and compare to the host.
+
+The mount is automatically cleaned up when you exit the process.
+
+---
+
+## PID namespace
+
+- Processes within a PID namespace only "see" processes
+ in the same PID namespace.
+
+- Each PID namespace has its own numbering (starting at 1).
+
+- When PID 1 goes away, the whole namespace is killed.
+
+ (When PID 1 goes away on a normal UNIX system, the kernel panics!)
+
+- Those namespaces can be nested.
+
+- A process ends up having multiple PIDs (one per namespace in which it is nested).
+
+---
+
+## PID namespace in action
+
+Create a new PID namespace:
+
+```bash
+$ sudo unshare --pid --fork
+```
+
+(We need the `--fork` flag because the PID namespace is special.)
+
+Check the process tree in the new namespace:
+
+```bash
+ # ps faux
+```
+
+--
+
+🤔 Why do we see all the processes?!?
+
+---
+
+## PID namespaces and `/proc`
+
+- Tools like `ps` rely on the `/proc` pseudo-filesystem.
+
+- Our new namespace still has access to the original `/proc`.
+
+- Therefore, it still sees host processes.
+
+- But it cannot affect them.
+
+ (Try to `kill` a process: you will get `No such process`.)
+
+---
+
+## PID namespaces, take 2
+
+- This can be solved by mounting `/proc` in the namespace.
+
+- The `unshare` utility provides a convenience flag, `--mount-proc`.
+
+- This flag will mount `/proc` in the namespace.
+
+- It will also unshare the mount namespace, so that this mount is local.
+
+Try it:
+
+```bash
+ $ sudo unshare --pid --fork --mount-proc
+ # ps faux
+```
+
+---
+
+class: extra-details
+
+## OK, really, why do we need `--fork`?
+
+- The `unshare` tool calls the `unshare` syscall, then `exec`s the new binary.
+
+- A process calling `unshare` to create new namespaces is moved to the new namespaces ...
+
+- ... Except for the PID namespace.
+
+ (Because this would change the current PID of the process from X to 1.)
+
+- The processes created by the new binary are placed into the new PID namespace.
+
+- The first one will be PID 1.
+
+- If PID 1 exits, it is not possible to create additional processes in the namespace.
+
+ (Attempting to do so will result in `ENOMEM`.)
+
+- Without the `--fork` flag, the first command that we execute will be PID 1 ...
+
+- ... And once it exits, we cannot create more processes in the namespace!
+
+Check `man 2 unshare` and `man pid_namespaces` if you want more details.
+
+---
+
+## IPC namespace
+
+--
+
+- Does anybody know about IPC?
+
+--
+
+- Does anybody *care* about IPC?
+
+--
+
+- Allows a process (or group of processes) to have own:
+
+ - IPC semaphores
+ - IPC message queues
+ - IPC shared memory
+
+ ... without risk of conflict with other instances.
+
+- Older versions of PostgreSQL cared about this.
+
+*No demo for that one.*
+
+---
+
+## User namespace
+
+- Allows to map 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
+ - etc.
+
+- UID in containers becomes irrelevant.
+
+ (Just use UID 0 in the container, it gets squashed to a non-privileged user outside.)
+
+- Huge security benefits!
+
+- Allows non-privileged users to perform privileged operations in context.
+
+ Example: a regular user can configure network interfaces in a namespace,
+ if they own that namespace.
+
+- Ultimately enables better privilege separation in container engines.
+
+---
+
+## User namespace challenges
+
+- UID needs to be mapped when passed between processes or kernel subsystems.
+
+- Filesystem permissions and file ownership are more complicated.
+
+ (E.g. when using a common root filesystem for multiple containers
+ running each with their own UID.)
+
+- With the Docker Engine:
+
+ - some feature combinations are not allowed
+
+ (e.g. user namespace + host network namespace sharing)
+
+ - user namespaces need to be enabled/disabled globally
+
+ (when the daemon is started)
+
+ - container images are stored separately
+
+ (so the first time you toggle user namespaces, you need to re-pull images)
+
+*No demo for that one.*
+
+---
+
+## Control groups
+
+- Control groups provide resource *metering* and *limiting*.
+
+- This covers a number of "usual suspects" like:
+
+ - memory
+
+ - CPU
+
+ - block I/O
+
+ - network (with cooperation from iptables/tc)
+
+- And a few exotic ones:
+
+ - huge pages (a special way to allocate memory)
+
+ - RDMA (resources specific to InfiniBand / remote memory transfer)
+
+---
+
+## Crowd control
+
+- Control groups also allow to group processes for special operations:
+
+ - freezer (conceptually similar to a "mass-SIGSTOP/SIGCONT")
+
+ - perf_event (gather performance events on multiple processes)
+
+ - cpuset (limit or pin processes to specific CPUs)
+
+- There is a "pids" cgroup to limit the number of processes in a given group.
+
+- There is also a "devices" cgroup to control access to device nodes.
+
+ (i.e. everything in `/dev`.)
+
+---
+
+## Generalities
+
+- Cgroups form a hierarchy (a tree).
+
+- We can create nodes in that hierarchy.
+
+- We can associate limits to a node.
+
+- We can move a process (or multiple processes) to a node.
+
+- The process (or processes) will then respect these limits.
+
+- We can check the current usage of each node.
+
+- In other words: limits are optional (if we only want accounting).
+
+- When a process is created, it is placed in its parent's groups.
+
+---
+
+## Example
+
+The numbers are PIDs.
+
+The names are the names of our nodes (arbitrarily chosen).
+
+.small[
+```bash
+cpu memory
+├── batch ├── stateless
+│ ├── cryptoscam │ ├── 25
+│ │ └── 52 │ ├── 26
+│ └── ffmpeg │ ├── 27
+│ ├── 109 │ ├── 52
+│ └── 88 │ ├── 109
+└── realtime │ └── 88
+ ├── nginx └── databases
+ │ ├── 25 ├── 1008
+ │ ├── 26 └── 524
+ │ └── 27
+ ├── postgres
+ │ └── 524
+ └── redis
+ └── 1008
+```
+]
+
+---
+
+## Cgroups v1 vs v2
+
+- Cgroups v1 are available on all systems (and widely used).
+
+- Cgroups v2 are a huge refactor.
+
+ (Development started in Linux 3.10, released in 4.5.)
+
+- Cgroups v2 have a number of differences:
+
+ - single hierarchy (instead of one tree per controller),
+
+ - processes can only be on leaf nodes (not inner nodes),
+
+ - and of course many improvements / refactorings.
+
+---
+
+## Memory cgroup: accounting
+
+- Keeps track of pages used by each group:
+
+ - file (read/write/mmap from block devices),
+ - anonymous (stack, heap, anonymous mmap),
+ - active (recently accessed),
+ - inactive (candidate for eviction).
+
+- Each page is "charged" to a group.
+
+- Pages can be shared across multiple groups.
+
+ (Example: multiple processes reading from the same files.)
+
+- To view all the counters kept by this cgroup:
+
+ ```bash
+ $ cat /sys/fs/cgroup/memory/memory.stat
+ ```
+
+---
+
+## Memory cgroup: limits
+
+- Each group can have (optional) hard and soft limits.
+
+- Limits can be set for different kinds of memory:
+
+ - physical memory,
+
+ - kernel memory,
+
+ - total memory (including swap).
+
+---
+
+## Soft limits and hard limits
+
+- Soft limits are not enforced.
+
+ (But they influence reclaim under memory pressure.)
+
+- Hard limits *cannot* be exceeded:
+
+ - if a group of processes exceeds a hard limit,
+
+ - and if the kernel cannot reclaim any memory,
+
+ - then the OOM (out-of-memory) killer is triggered,
+
+ - and processes are killed until memory gets below the limit again.
+
+---
+
+## Avoiding the OOM killer
+
+- For some workloads (databases and stateful systems), killing
+ processes because we run out of memory is not acceptable.
+
+- The "oom-notifier" mechanism helps with that.
+
+- When "oom-notifier" is enabled and a hard limit is exceeded:
+
+ - all processes in the cgroup are frozen,
+
+ - a notification is sent to user space (instead of killing processes),
+
+ - user space can then raise limits, migrate containers, etc.,
+
+ - once the memory usage is below the hard limit, unfreeze the cgroup.
+
+---
+
+## Overhead of the memory cgroup
+
+- Each time a process grabs or releases a page, the kernel update counters.
+
+- This adds some overhead.
+
+- Unfortunately, this cannot be enabled/disabled per process.
+
+- It has to be done system-wide, at boot time.
+
+- Also, when multiple groups use the same page:
+
+ - only the first group gets "charged",
+
+ - but if it stops using it, the "charge" is moved to another group.
+
+---
+
+## Setting up a limit with the memory cgroup
+
+Create a new memory cgroup:
+
+```bash
+$ CG=/sys/fs/cgroup/memory/onehundredmegs
+$ sudo mkdir $CG
+```
+
+Limit it to approximately 100MB of memory usage:
+
+```bash
+$ sudo tee $CG/memory.memsw.limit_in_bytes <<<100000000
+```
+
+Move the current process to that cgroup:
+
+```bash
+$ sudo tee $CG/tasks <<< $$
+```
+
+The current process *and all its future children* are now limited.
+
+---
+
+## Testing the memory limit
+
+Start the Python interpreter:
+
+```bash
+$ python
+Python 3.6.4 (default, Jan 5 2018, 02:35:40)
+[GCC 7.2.1 20171224] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>>
+```
+
+Allocate 80 megabytes:
+
+```python
+>>> s = "!" * 1000000 * 80
+```
+
+Add 20 megabytes more:
+
+```python
+>>> t = "!" * 1000000 * 20
+Killed
+```
+
+---
+
+## CPU cgroup
+
+- Keeps track of CPU time used by a group of processes.
+
+ (This is easier and more accurate than `getrusage` and `/proc`.)
+
+- Keeps track of usage per CPU as well.
+
+ (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.
+
+- We cannot set CPU limits (like, "don't use more than 10% of CPU").
+
+---
+
+## Cpuset cgroup
+
+- Pin groups to specific CPU(s).
+
+- Use-case: reserve CPUs for specific apps.
+
+- Warning: make sure that "default" processes aren't using all CPUs!
+
+- CPU pinning can also avoid performance loss due to cache flushes.
+
+- This is also relevant for NUMA systems.
+
+- Provides extra dials and knobs.
+
+ (Per zone memory pressure, process migration costs...)
+
+---
+
+## Blkio cgroup
+
+- Keeps track of I/Os for each group:
+
+ - per block device
+ - read vs write
+ - sync vs async
+
+- Set throttle (limits) for each group:
+
+ - per block device
+ - read vs write
+ - ops vs bytes
+
+- Set relative weights for each group.
+
+- Note: most writes go through the page cache.
+ (So classic writes will appear to be unthrottled at first.)
+
+---
+
+## Net_cls and net_prio cgroup
+
+- Only works for egress (outgoing) traffic.
+
+- Automatically set traffic class or priority
+ for traffic generated by processes in the group.
+
+- Net_cls will assign traffic to a class.
+
+- Classes have to be matched with tc or iptables, otherwise traffic just flows normally.
+
+- Net_prio will assign traffic to a priority.
+
+- Priorities are used by queuing disciplines.
+
+---
+
+## Devices cgroup
+
+- Controls what the group can do on device nodes
+
+- Permissions include read/write/mknod
+
+- Typical use:
+
+ - allow `/dev/{tty,zero,random,null}` ...
+ - deny everything else
+
+- A few interesting nodes:
+
+ - `/dev/net/tun` (network interface manipulation)
+ - `/dev/fuse` (filesystems in user space)
+ - `/dev/kvm` (VMs in containers, yay inception!)
+ - `/dev/dri` (GPU)
+
+---
+
+## Capabilities, seccomp, LSMs...
+
+- Namespaces and cgroups are not enough to ensure strong security.
+
+- If we want our containers to actually contain, we need more.
+
+- Capabilities, seccomp, LSMs were already used before containers to harden security.
+
+- They can be used together with containers.
+
+- Good container engines will automatically leverage these features.
+
+ (So that you don't have to worry about it.)
+
+---
+
+## Capabilities
+
+- In traditional UNIX, many operations are possible if and only if UID=0 (root).
+
+- Some of these operations are very powerful:
+
+ - changing file ownership, accessing all files ...
+
+- Some of these operations deal with system configuration, but can be abused:
+
+ - setting up network interfaces, mounting filesystems ...
+
+- Some of these operations are not very dangerous but are needed by servers:
+
+ - binding to a port below 1024.
+
+- Capabilities are per-process flags to allow these operations individually.
+
+---
+
+## Some capabilities
+
+- `CAP_CHOWN`: arbitrarily change file ownership and permissions.
+
+- `CAP_DAC_OVERRIDE`: arbitrarily bypass file ownership and permissions.
+
+- `CAP_NET_ADMIN`: configure network interfaces, iptables rules, etc.
+
+- `CAP_NET_BIND_SERVICE`: bind a port below 1024.
+
+See `man capabilities` for the full list and details.
+
+---
+
+## Using capabilities
+
+- Container engines will typically drop all "dangerous" capabilities.
+
+- You can then re-enable capabilities on a per-container basis, as needed.
+
+- With the Docker engine: `docker run --cap-add ...`
+
+- If you write your own code to manage capabilities:
+
+ - make sure that you understand what each capability does,
+
+ - read about *ambient* capabilities as well.
+
+---
+
+## Seccomp
+
+- Seccomp is secure computing.
+
+- Achieve high level of security by restricting drastically available syscalls.
+
+- Original seccomp only allows `read()`, `write()`, `exit()`, `sigreturn()`.
+
+- The seccomp-bpf extension allows to specify custom filters with BPF rules.
+
+- This allows to filter by syscall, and by parameter.
+
+- BPF code can perform arbitrarily complex checks, quickly, and safely.
+
+- Container engines take care of this so you don't have to.
+
+---
+
+## Linux Security Modules
+
+- The most popular ones are SELinux and AppArmor.
+
+- Red Hat distros generally use SELinux.
+
+- Debian distros (in particular, Ubuntu) generally use AppArmor.
+
+- LSMs add a layer of access control to all process operations.
+
+- Container engines take care of this so you don't have to.
diff --git a/slides/intro/Orchestration_Overview.md b/slides/intro/Orchestration_Overview.md
new file mode 100644
index 00000000..098cee74
--- /dev/null
+++ b/slides/intro/Orchestration_Overview.md
@@ -0,0 +1,427 @@
+# Orchestration, an overview
+
+In this chapter, we will:
+
+* Explain what is orchestration and why we would need it.
+
+* Present (from a high-level perspective) some orchestrators.
+
+* Show one orchestrator (Kubernetes) in action.
+
+---
+
+class: pic
+
+## What's orchestration?
+
+
+
+---
+
+## What's orchestration?
+
+According to Wikipedia:
+
+*Orchestration describes the __automated__ arrangement,
+coordination, and management of complex computer systems,
+middleware, and services.*
+
+--
+
+*[...] orchestration is often discussed in the context of
+__service-oriented architecture__, __virtualization__, provisioning,
+Converged Infrastructure and __dynamic datacenter__ topics.*
+
+--
+
+What does that really mean?
+
+---
+
+## Example 1: dynamic cloud instances
+
+--
+
+- Q: do we always use 100% of our servers?
+
+--
+
+- A: obviously not!
+
+.center[]
+
+---
+
+## Example 1: dynamic cloud instances
+
+- Every night, scale down
+
+ (by shutting down extraneous replicated instances)
+
+- Every morning, scale up
+
+ (by deploying new copies)
+
+- "Pay for what you use"
+
+ (i.e. save big $$$ here)
+
+---
+
+## Example 1: dynamic cloud instances
+
+How do we implement this?
+
+- Crontab
+
+- Autoscaling (save even bigger $$$)
+
+That's *relatively* easy.
+
+Now, how are things for our IAAS provider?
+
+---
+
+## Example 2: dynamic datacenter
+
+- Q: what's the #1 cost in a datacenter?
+
+--
+
+- A: electricity!
+
+--
+
+- Q: what uses electricity?
+
+--
+
+- A: servers, obviously
+
+- A: ... and associated cooling
+
+--
+
+- Q: do we always use 100% of our servers?
+
+--
+
+- A: obviously not!
+
+---
+
+## Example 2: dynamic datacenter
+
+- If only we could turn off unused servers during the night...
+
+- Problem: we can only turn off a server if it's totally empty!
+
+ (i.e. all VMs on it are stopped/moved)
+
+- Solution: *migrate* VMs and shutdown empty servers
+
+ (e.g. combine two hypervisors with 40% load into 80%+0%,
+ and shutdown the one at 0%)
+
+---
+
+## Example 2: dynamic datacenter
+
+How do we implement this?
+
+- Shutdown empty hosts (but keep some spare capacity)
+
+- Start hosts again when capacity gets low
+
+- Ability to "live migrate" VMs
+
+ (Xen already did this 10+ years ago)
+
+- Rebalance VMs on a regular basis
+
+ - what if a VM is stopped while we move it?
+ - should we allow provisioning on hosts involved in a migration?
+
+*Scheduling* becomes more complex.
+
+---
+
+## What is scheduling?
+
+According to Wikipedia (again):
+
+*In computing, scheduling is the method by which threads,
+processes or data flows are given access to system resources.*
+
+The scheduler is concerned mainly with:
+
+- throughput (total amount or work done per time unit);
+- turnaround time (between submission and completion);
+- response time (between submission and start);
+- waiting time (between job readiness and execution);
+- fairness (appropriate times according to priorities).
+
+In practice, these goals often conflict.
+
+**"Scheduling" = decide which resources to use.**
+
+---
+
+## Exercise 1
+
+- You have:
+
+ - 5 hypervisors (physical machines)
+
+- Each server has:
+
+ - 16 GB RAM, 8 cores, 1 TB disk
+
+- Each week, your team asks:
+
+ - one VM with X RAM, Y CPU, Z disk
+
+Scheduling = deciding which hypervisor to use for each VM.
+
+Difficulty: easy!
+
+---
+
+
+
+## Exercise 2
+
+- You have:
+
+ - 1000+ hypervisors (and counting!)
+
+- Each server has different resources:
+
+ - 8-500 GB of RAM, 4-64 cores, 1-100 TB disk
+
+- Multiple times a day, a different team asks for:
+
+ - up to 50 VMs with different characteristics
+
+Scheduling = deciding which hypervisor to use for each VM.
+
+Difficulty: ???
+
+---
+
+
+
+## Exercise 2
+
+- You have:
+
+ - 1000+ hypervisors (and counting!)
+
+- Each server has different resources:
+
+ - 8-500 GB of RAM, 4-64 cores, 1-100 TB disk
+
+- Multiple times a day, a different team asks for:
+
+ - up to 50 VMs with different characteristics
+
+Scheduling = deciding which hypervisor to use for each VM.
+
+
+
+---
+
+## Exercise 3
+
+- You have machines (physical and/or virtual)
+
+- You have containers
+
+- You are trying to put the containers on the machines
+
+- Sounds familiar?
+
+---
+
+## Scheduling with one resource
+
+.center[]
+
+Can we do better?
+
+---
+
+## Scheduling with one resource
+
+.center[]
+
+Yup!
+
+---
+
+## Scheduling with two resources
+
+.center[]
+
+---
+
+## Scheduling with three resources
+
+.center[]
+
+---
+
+## You need to be good at this
+
+.center[]
+
+---
+
+## But also, you must be quick!
+
+.center[]
+
+---
+
+## And be web scale!
+
+.center[]
+
+---
+
+## And think outside (?) of the box!
+
+.center[]
+
+---
+
+## Good luck!
+
+.center[]
+
+---
+
+## TL,DR
+
+* Scheduling with multiple resources (dimensions) is hard.
+
+* Don't expect to solve the problem with a Tiny Shell Script.
+
+* There are literally tons of research papers written on this.
+
+---
+
+## But our orchestrator also needs to manage ...
+
+* Network connectivity (or filtering) between containers.
+
+* Load balancing (external and internal).
+
+* Failure recovery (if a node or a whole datacenter fails).
+
+* Rolling out new versions of our applications.
+
+ (Canary deployments, blue/green deployments...)
+
+
+---
+
+## Some orchestrators
+
+We are going to present briefly a few orchestrators.
+
+There is no "absolute best" orchestrator.
+
+It depends on:
+
+- your applications,
+
+- your requirements,
+
+- your pre-existing skills...
+
+---
+
+## Nomad
+
+- Open Source project by Hashicorp.
+
+- Arbitrary scheduler (not just for containers).
+
+- Great if you want to schedule mixed workloads.
+
+ (VMs, containers, processes...)
+
+- Less integration with the rest of the container ecosystem.
+
+---
+
+## Mesos
+
+- Open Source project in the Apache Foundation.
+
+- Arbitrary scheduler (not just for containers).
+
+- Two-level scheduler.
+
+- Top-level scheduler acts as a resource broker.
+
+- Second-level schedulers (aka "frameworks") obtain resources from top-level.
+
+- Frameworks implement various strategies.
+
+ (Marathon = long running processes; Chronos = run at intervals; ...)
+
+- Commercial offering through DC/OS my Mesosphere.
+
+---
+
+## Rancher
+
+- Rancher 1 offered a simple interface for Docker hosts.
+
+- Rancher 2 is a complete management platform for Docker and Kubernetes.
+
+- Technically not an orchestrator, but it's a popular option.
+
+---
+
+## Swarm
+
+- Tightly integrated with the Docker Engine.
+
+- Extremely simple to deploy and setup, even in multi-manager (HA) mode.
+
+- Secure by default.
+
+- Strongly opinionated:
+
+ - smaller set of features,
+
+ - easier to operate.
+
+---
+
+## Kubernetes
+
+- Open Source project initiated by Google.
+
+- Contributions from many other actors.
+
+- *De facto* standard for container orchestration.
+
+- Many deployment options; some of them very complex.
+
+- Reputation: steep learning curve.
+
+- Reality:
+
+ - true, if we try to understand *everything*;
+
+ - false, if we focus on what matters.
+
+---
+
+## Kubernetes in action
+
+FIXME (describe the demo?)
diff --git a/slides/intro/Training_Environment.md b/slides/intro/Training_Environment.md
index 272a2df3..2176cb59 100644
--- a/slides/intro/Training_Environment.md
+++ b/slides/intro/Training_Environment.md
@@ -38,6 +38,42 @@ individual Docker VM.*
---
+## What *is* Docker?
+
+- "Installing Docker" really means "Installing the Docker Engine and CLI".
+
+- The Docker Engine is a daemon (a service running in the background).
+
+- This daemon manages containers, the same way that an hypervisor manages VMs.
+
+- We interact with the Docker Engine by using the Docker CLI.
+
+- The Docker CLI and the Docker Engine communicate through an API.
+
+- There are many other programs, and many client libraries, to use that API.
+
+---
+
+## Why don't we run Docker locally?
+
+- We are going to download container images and distribution packages.
+
+- This could put a bit of stress on the local WiFi and slow us down.
+
+- Instead, we use a remote VM that has a good connectivity
+
+- In some rare cases, installing Docker locally is challenging:
+
+ - no administrator/root access (computer managed by strict corp IT)
+
+ - 32-bit CPU or OS
+
+ - old OS version (e.g. CentOS 6, OSX pre-Yosemite, Windows 7)
+
+- It's better to spend time learning containers than fiddling with the installer!
+
+---
+
## Connecting to your Virtual Machine
You need an SSH client.
@@ -66,21 +102,24 @@ Once logged in, make sure that you can run a basic Docker command:
```bash
$ docker version
Client:
- Version: 17.09.0-ce
- API version: 1.32
- Go version: go1.8.3
- Git commit: afdb6d4
- Built: Tue Sep 26 22:40:09 2017
- OS/Arch: darwin/amd64
+ Version: 18.03.0-ce
+ API version: 1.37
+ Go version: go1.9.4
+ Git commit: 0520e24
+ Built: Wed Mar 21 23:10:06 2018
+ OS/Arch: linux/amd64
+ Experimental: false
+ Orchestrator: swarm
Server:
- Version: 17.09.0-ce
- API version: 1.32 (minimum version 1.12)
- Go version: go1.8.3
- Git commit: afdb6d4
- Built: Tue Sep 26 22:45:38 2017
- OS/Arch: linux/amd64
- Experimental: true
+ Engine:
+ Version: 18.03.0-ce
+ API version: 1.37 (minimum version 1.12)
+ Go version: go1.9.4
+ Git commit: 0520e24
+ Built: Wed Mar 21 23:08:35 2018
+ OS/Arch: linux/amd64
+ Experimental: false
```
]
diff --git a/slides/intro/Working_With_Volumes.md b/slides/intro/Working_With_Volumes.md
index 214c778a..e16b6135 100644
--- a/slides/intro/Working_With_Volumes.md
+++ b/slides/intro/Working_With_Volumes.md
@@ -401,6 +401,47 @@ or providing extra features. For instance:
---
+## Volumes vs. Mounts
+
+* Since Docker 17.06, a new options is available: `--mount`.
+
+* It offers a new, richer syntax to manipulate data in containers.
+
+* It makes an explicit difference between:
+
+ - volumes (identified with a unique name, managed by a storage plugin),
+
+ - bind mounts (identified with a host path, not managed).
+
+* The former `-v` / `--volume` option is still usable.
+
+---
+
+## `--mount` syntax
+
+Binding a host path to a container path:
+
+```bash
+$ docker run \
+ --mount type=bind,source=/path/on/host,target=/path/in/container alpine
+```
+
+Mounting a volume to a container path:
+
+```bash
+$ docker run \
+ --mount source=myvolume,target=/path/in/container alpine
+```
+
+Mounting a tmpfs (in-memory, for temporary files):
+
+```bash
+$ docker run \
+ --mount type=tmpfs,destination=/path/in/container,tmpfs-size=1000000 alpine
+```
+
+---
+
## Section summary
We've learned how to: