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 @@ + + + + + 2014-04-15 00:37Z + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + +![](images/docker-engine-architecture.svg) + +--- + +## 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 + +![](images/dockerd-and-containerd.png) + +--- + +## 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 + +![The Docker ecosystem in 2015](images/docker-ecosystem-2015.png) + +--- + +## 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 + +![Picture of a Tesla](images/tesla.jpg) + +--- + +## 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 + +![Cloud Native Landscape](https://raw.githubusercontent.com/cncf/landscape/master/landscape/CloudNativeLandscape_latest.png) + 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 + +![Person standing inside a container](images/getting-inside.png) + +--- + +## 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? + +![Joana Carneiro (orchestra conductor)](images/conductor.jpg) + +--- + +## 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[![Daily variations of traffic](images/traffic-graph.png)] + +--- + +## 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. + +![Troll face](images/trollface.png) + +--- + +## 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[![Not-so-good bin packing](images/binpacking-1d-1.gif)] + +Can we do better? + +--- + +## Scheduling with one resource + +.center[![Better bin packing](images/binpacking-1d-2.gif)] + +Yup! + +--- + +## Scheduling with two resources + +.center[![2D bin packing](images/binpacking-2d.gif)] + +--- + +## Scheduling with three resources + +.center[![3D bin packing](images/binpacking-3d.gif)] + +--- + +## You need to be good at this + +.center[![Tangram](images/tangram.gif)] + +--- + +## But also, you must be quick! + +.center[![Tetris](images/tetris-1.png)] + +--- + +## And be web scale! + +.center[![Big tetris](images/tetris-2.gif)] + +--- + +## And think outside (?) of the box! + +.center[![3D tetris](images/tetris-3.png)] + +--- + +## Good luck! + +.center[![FUUUUUU face](images/fu-face.jpg)] + +--- + +## 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: