From 9cdccd40c7902d5e19492245bfbff4b16b6e18e7 Mon Sep 17 00:00:00 2001 From: jasonknudsen Date: Wed, 23 May 2018 08:17:46 -0700 Subject: [PATCH 01/24] Update README.md - typo Typo in instructions - should be pull_images not pull-images --- prepare-vms/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prepare-vms/README.md b/prepare-vms/README.md index 7de6fb00..9b3d1ef2 100644 --- a/prepare-vms/README.md +++ b/prepare-vms/README.md @@ -103,7 +103,7 @@ wrap Run this program in a container - Run `./workshopctl deploy TAG settings/somefile.yaml` to run `lib/postprep.py` via parallel-ssh - If it errors or times out, you should be able to rerun - Requires good connection to run all the parallel SSH connections, up to 100 parallel (ProTip: create dedicated management instance in same AWS region where you run all these utils from) -- Run `./workshopctl pull-images TAG` to pre-pull a bunch of Docker images to the instances +- Run `./workshopctl pull_images TAG` to pre-pull a bunch of Docker images to the instances - Run `./workshopctl cards TAG settings/somefile.yaml` generates PDF/HTML files to print and cut and hand out to students - *Have a great workshop* - Run `./workshopctl stop TAG` to terminate instances. @@ -210,7 +210,7 @@ The `postprep.py` file will be copied via parallel-ssh to all of the VMs and exe #### Pre-pull images - $ ./workshopctl pull-images TAG + $ ./workshopctl pull_images TAG #### Generate cards From 5bb37dff49a726c64b333c689bbc11960aaf444d Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 18 May 2018 09:38:30 -0500 Subject: [PATCH 02/24] Parametrize git repo and slides URLs We have two extra variables in the slides: @@GITREPO@@ (current value: github.com/jpetazzo/container.training) @@SLIDES@@ (current value: http://container.training/) These variables are set with gitrepo and slides in the YAML files. (Just like the chat variable.) Supercedes #256 --- slides/common/about-slides.md | 6 +++--- slides/common/prereqs.md | 6 +++--- slides/common/sampleapp.md | 10 +++++----- slides/common/title.md | 4 ++-- slides/intro-fullday.yml | 4 ++++ slides/intro-selfpaced.yml | 4 ++++ slides/intro/intro.md | 2 +- slides/kube-fullday.yml | 4 ++++ slides/kube-halfday.yml | 4 ++++ slides/kube-selfpaced.yml | 4 ++++ slides/kube/intro.md | 2 +- slides/kube/setup-k8s.md | 2 +- slides/markmaker.py | 2 ++ slides/swarm-fullday.yml | 4 ++++ slides/swarm-halfday.yml | 4 ++++ slides/swarm-selfpaced.yml | 4 ++++ slides/swarm-video.yml | 4 ++++ slides/swarm/encryptionatrest.md | 2 +- slides/swarm/intro.md | 2 +- slides/swarm/logging.md | 4 ++-- slides/swarm/machine.md | 2 +- slides/swarm/operatingswarm.md | 2 +- 22 files changed, 60 insertions(+), 22 deletions(-) diff --git a/slides/common/about-slides.md b/slides/common/about-slides.md index 5856fb34..d5ee02a8 100644 --- a/slides/common/about-slides.md +++ b/slides/common/about-slides.md @@ -2,7 +2,7 @@ - All the content is available in a public GitHub repository: - https://github.com/jpetazzo/container.training + https://@@GITREPO@@ - You can get updated "builds" of the slides there: @@ -10,7 +10,7 @@ @@ -23,7 +23,7 @@ diff --git a/slides/common/prereqs.md b/slides/common/prereqs.md index 9eec2b3c..5bf1bc57 100644 --- a/slides/common/prereqs.md +++ b/slides/common/prereqs.md @@ -48,11 +48,11 @@ Misattributed to Benjamin Franklin - This is the stuff you're supposed to do! -- Go to [container.training](http://container.training/) to view these slides +- Go to @@SLIDES@@ to view these slides - Join the chat room: @@CHAT@@ - + ] @@ -222,7 +222,7 @@ If anything goes wrong — ask for help! Small setup effort; small cost; flexible environments - Create a bunch of clusters for you and your friends - ([instructions](https://github.com/jpetazzo/container.training/tree/master/prepare-vms)) + ([instructions](https://@@GITREPO@@/tree/master/prepare-vms)) Bigger setup effort; ideal for group training diff --git a/slides/common/sampleapp.md b/slides/common/sampleapp.md index a6cc03af..0e76edef 100644 --- a/slides/common/sampleapp.md +++ b/slides/common/sampleapp.md @@ -16,7 +16,7 @@ fi - Clone the repository on `node1`: ```bash - git clone git://github.com/jpetazzo/container.training + git clone git://@@GITREPO@@ ``` ] @@ -56,16 +56,16 @@ and displays aggregated logs. ## More detail on our sample application - Visit the GitHub repository with all the materials of this workshop: -
https://github.com/jpetazzo/container.training +
https://@@GITREPO@@ - The application is in the [dockercoins]( - https://github.com/jpetazzo/container.training/tree/master/dockercoins) + https://@@GITREPO@@/tree/master/dockercoins) subdirectory - Let's look at the general layout of the source code: there is a Compose file [docker-compose.yml]( - https://github.com/jpetazzo/container.training/blob/master/dockercoins/docker-compose.yml) ... + https://@@GITREPO@@/blob/master/dockercoins/docker-compose.yml) ... ... and 4 other services, each in its own directory: @@ -124,7 +124,7 @@ def hash_bytes(data): ``` (Full source code available [here]( -https://github.com/jpetazzo/container.training/blob/8279a3bce9398f7c1a53bdd95187c53eda4e6435/dockercoins/worker/worker.py#L17 +https://@@GITREPO@@/blob/8279a3bce9398f7c1a53bdd95187c53eda4e6435/dockercoins/worker/worker.py#L17 )) --- diff --git a/slides/common/title.md b/slides/common/title.md index 1d7755d4..007e01a7 100644 --- a/slides/common/title.md +++ b/slides/common/title.md @@ -17,5 +17,5 @@ class: title, in-person *Don't stream videos or download big files during the workshop.*
*Thank you!* -**Slides: http://container.training/** -] \ No newline at end of file +**Slides: @@SLIDES@@** +] diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index 80e365e9..8aebbc93 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -6,6 +6,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 883184f5..5637cdc8 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -6,6 +6,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - in-person diff --git a/slides/intro/intro.md b/slides/intro/intro.md index 16a4e0c8..b6c1ee76 100644 --- a/slides/intro/intro.md +++ b/slides/intro/intro.md @@ -2,7 +2,7 @@ - This was initially written to support in-person, instructor-led workshops and tutorials -- These materials are maintained by [Jérôme Petazzoni](https://twitter.com/jpetazzo) and [multiple contributors](https://github.com/jpetazzo/container.training/graphs/contributors) +- These materials are maintained by [Jérôme Petazzoni](https://twitter.com/jpetazzo) and [multiple contributors](https://@@GITREPO@@/graphs/contributors) - You can also follow along on your own, at your own pace diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 2ba0d8d9..92d76fab 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -6,6 +6,10 @@ title: | #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" chat: "In person!" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 13e9b1fc..1741762f 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -5,6 +5,10 @@ title: | #chat: "[Gitter](https://gitter.im/jpetazzo/training-20180413-paris)" chat: "In person!" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 9989a7c0..88b23c5e 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -5,6 +5,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - in-person diff --git a/slides/kube/intro.md b/slides/kube/intro.md index a0090371..451177dc 100644 --- a/slides/kube/intro.md +++ b/slides/kube/intro.md @@ -3,7 +3,7 @@ - This was initially written by [Jérôme Petazzoni](https://twitter.com/jpetazzo) to support in-person, instructor-led workshops and tutorials -- Credit is also due to [multiple contributors](https://github.com/jpetazzo/container.training/graphs/contributors) — thank you! +- Credit is also due to [multiple contributors](https://@@GITREPO@@/graphs/contributors) — thank you! - You can also follow along on your own, at your own pace diff --git a/slides/kube/setup-k8s.md b/slides/kube/setup-k8s.md index c397a393..b53e94a0 100644 --- a/slides/kube/setup-k8s.md +++ b/slides/kube/setup-k8s.md @@ -20,7 +20,7 @@ 6. Copy the configuration file generated by `kubeadm init` -- Check the [prepare VMs README](https://github.com/jpetazzo/container.training/blob/master/prepare-vms/README.md) for more details +- Check the [prepare VMs README](https://@@GITREPO@@/blob/master/prepare-vms/README.md) for more details --- diff --git a/slides/markmaker.py b/slides/markmaker.py index a29ba38a..a01fbe02 100755 --- a/slides/markmaker.py +++ b/slides/markmaker.py @@ -114,6 +114,8 @@ def generatefromyaml(manifest, filename): html = html.replace("@@MARKDOWN@@", markdown) html = html.replace("@@EXCLUDE@@", exclude) html = html.replace("@@CHAT@@", manifest["chat"]) + html = html.replace("@@GITREPO@@", manifest["gitrepo"]) + html = html.replace("@@SLIDES@@", manifest["slides"]) html = html.replace("@@TITLE@@", manifest["title"].replace("\n", " ")) return html diff --git a/slides/swarm-fullday.yml b/slides/swarm-fullday.yml index 2d273bd0..4ba793b6 100644 --- a/slides/swarm-fullday.yml +++ b/slides/swarm-fullday.yml @@ -5,6 +5,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced - snap diff --git a/slides/swarm-halfday.yml b/slides/swarm-halfday.yml index c1c23575..39acc72b 100644 --- a/slides/swarm-halfday.yml +++ b/slides/swarm-halfday.yml @@ -5,6 +5,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced - snap diff --git a/slides/swarm-selfpaced.yml b/slides/swarm-selfpaced.yml index f78de79c..89b6146f 100644 --- a/slides/swarm-selfpaced.yml +++ b/slides/swarm-selfpaced.yml @@ -4,6 +4,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - in-person - btp-auto diff --git a/slides/swarm-video.yml b/slides/swarm-video.yml index 47deb73c..3274d60a 100644 --- a/slides/swarm-video.yml +++ b/slides/swarm-video.yml @@ -4,6 +4,10 @@ title: | chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - in-person - btp-auto diff --git a/slides/swarm/encryptionatrest.md b/slides/swarm/encryptionatrest.md index 7caf20c6..115b228c 100644 --- a/slides/swarm/encryptionatrest.md +++ b/slides/swarm/encryptionatrest.md @@ -49,7 +49,7 @@ This will display the unlock key. Copy-paste it somewhere safe. ] Note: if you are doing the workshop on your own, using nodes -that you [provisioned yourself](https://github.com/jpetazzo/container.training/tree/master/prepare-machine) or with [Play-With-Docker](http://play-with-docker.com/), you might have to use a different method to restart the Engine. +that you [provisioned yourself](https://@@GITREPO@@/tree/master/prepare-machine) or with [Play-With-Docker](http://play-with-docker.com/), you might have to use a different method to restart the Engine. --- diff --git a/slides/swarm/intro.md b/slides/swarm/intro.md index 6db88c16..db236139 100644 --- a/slides/swarm/intro.md +++ b/slides/swarm/intro.md @@ -3,7 +3,7 @@ - This was initially written by [Jérôme Petazzoni](https://twitter.com/jpetazzo) to support in-person, instructor-led workshops and tutorials -- Over time, [multiple contributors](https://github.com/jpetazzo/container.training/graphs/contributors) also helped to improve these materials — thank you! +- Over time, [multiple contributors](https://@@GITREPO@@/graphs/contributors) also helped to improve these materials — thank you! - You can also follow along on your own, at your own pace diff --git a/slides/swarm/logging.md b/slides/swarm/logging.md index d21894a4..ab8ee265 100644 --- a/slides/swarm/logging.md +++ b/slides/swarm/logging.md @@ -113,7 +113,7 @@ class: elk-manual - We could author a custom image bundling this configuration -- We can also pass the [configuration](https://github.com/jpetazzo/container.training/blob/master/elk/logstash.conf) on the command line +- We can also pass the [configuration](https://@@GITREPO@@/blob/master/elk/logstash.conf) on the command line .exercise[ @@ -195,7 +195,7 @@ class: elk-auto Note: the *build* and *push* steps are not strictly necessary, but they don't hurt! Let's have a look at the [Compose file]( -https://github.com/jpetazzo/container.training/blob/master/stacks/elk.yml). +https://@@GITREPO@@/blob/master/stacks/elk.yml). --- diff --git a/slides/swarm/machine.md b/slides/swarm/machine.md index 0a9d6a11..3820bcfb 100644 --- a/slides/swarm/machine.md +++ b/slides/swarm/machine.md @@ -169,7 +169,7 @@ class: in-person This should tell us that we are talking to `node3`. Note: it can be useful to use a [custom shell prompt]( -https://github.com/jpetazzo/container.training/blob/master/prepare-vms/scripts/postprep.rc#L68) +https://@@GITREPO@@/blob/master/prepare-vms/scripts/postprep.rc#L68) reflecting the `DOCKER_HOST` variable. --- diff --git a/slides/swarm/operatingswarm.md b/slides/swarm/operatingswarm.md index 9d7c5a68..12deb4eb 100644 --- a/slides/swarm/operatingswarm.md +++ b/slides/swarm/operatingswarm.md @@ -33,7 +33,7 @@ TOKEN=$(docker swarm join-token -q manager) for N in $(seq 2 5); do DOCKER_HOST=tcp://node$N:2375 docker swarm join --token $TOKEN node1:2377 done -git clone git://github.com/jpetazzo/container.training +git clone git://@@GITREPO@@ cd container.training/stacks docker stack deploy --compose-file registry.yml registry docker-compose -f dockercoins.yml build From ad554f89fcfa0512f883df6224a34e3972334bdc Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Wed, 23 May 2018 10:18:02 -0500 Subject: [PATCH 03/24] New events (and old event to past) --- slides/index.html | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/slides/index.html b/slides/index.html index 3065f484..09973c2d 100644 --- a/slides/index.html +++ b/slides/index.html @@ -71,11 +71,6 @@ thing for now (stay tuned...) --> - - May 17th, 2018: Revolution Conf Virginia Beach - Docker 101 -   - - June 12th, 2018: Velocity San Jose - Kubernetes 101 @@ -83,14 +78,33 @@ thing for now (stay tuned...) + + June 27th, 2018: devopsdays Amsterdam - Kubernetes 101 +   + + + + July 17th, 2018: OSCON - Kubernetes 101   + + Oct 1st, 2018: Velocity New York - Kubernetes 101 +   + + + + Past workshops + + May 17th, 2018: Revolution Conf Virginia Beach - Docker 101 + + + May 10th, 2018: NDC Minnesota - Kubernetes 101 From 92f9ab9001083a9a88495ac2223baf71ab1538a8 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 24 May 2018 05:46:28 -0500 Subject: [PATCH 04/24] Add a section leading to multi-stage builds --- slides/intro/Multi_Stage_Builds.md | 193 ++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/slides/intro/Multi_Stage_Builds.md b/slides/intro/Multi_Stage_Builds.md index 5fe60eb5..7dfa03e6 100644 --- a/slides/intro/Multi_Stage_Builds.md +++ b/slides/intro/Multi_Stage_Builds.md @@ -1,4 +1,4 @@ -# Multi-stage builds +# Reducing image size * In the previous example, our final image contained: @@ -14,7 +14,196 @@ --- -## Multi-stage builds principles +## Can't we remove superfluous files with `RUN`? + +What happens if we do one of the following commands? + +- `RUN rm -rf ...` + +- `RUN apt-get remove ...` + +- `RUN make clean ...` + +-- + +This adds a layer which removes a bunch of files. + +But the previous layers (which added the files) still exist. + +--- + +## Removing files with an extra layer + +When downloading an image, all the layers must be downloaded. + +| Dockerfile instruction | Layer size | Image size | +| ---------------------- | ---------- | ---------- | +| `FROM ubuntu` | Size of base image | Size of base image | +| `...` | ... | Sum of this layer
+ all previous ones | +| `RUN apt-get install somepackage` | Size of files added
(e.g. a few MB) | Sum of this layer
+ all previous ones | +| `...` | ... | Sum of this layer
+ all previous ones | +| `RUN apt-get remove somepackage` | Almost zero
(just metadata) | Same as previous one | + +Therefore, `RUN rm` does not reduce the size of the image or free up disk space. + +--- + +## Removing unnecessary files + +Various techniques are available to obtain smaller images: + +- collapsing layers, + +- adding binaries that are built outside of the Dockerfile, + +- squashing the final image, + +- multi-stage builds. + +Let's review them quickly. + +--- + +## Collapsing layers + +You will frequently see Dockerfiles like this: + +```dockerfile +FROM ubuntu +RUN apt-get update && apt-get install xxx && ... && apt-get remove xxx && ... +``` + +Or the (more readable) variant: + +```dockerfile +FROM ubuntu +RUN apt-get update \ + && apt-get install xxx \ + && ... \ + && apt-get remove xxx \ + && ... +``` + +This `RUN` command gives us a single layer. + +The files that are added, then removed in the same layer, do not grow the layer size. + +--- + +## Collapsing layers: pros and cons + +Pros: + +- works on all versions of Docker + +- doesn't require extra tools + +Cons: + +- not very readable + +- some unnecessary files might still remain if the cleanup is not torough + +- that layer is expensive (slow to build) + +--- + +## Building binaries outside of the Dockerfile + +This results in a Dockerfile looking like this: + +```dockerfile +FROM ubuntu +COPY xxx /usr/local/bin +``` + +Of course, this implies that the file `xxx` exists in the build context. + +That file has to exist before you can run `docker build`. + +For instance, it can: + +- exist in the code repository, +- be created by another tool (script, Makefile...), +- be created by another container image and extracted from the image. + +See for instance the [busybox official image](https://github.com/docker-library/busybox/blob/fe634680e32659aaf0ee0594805f74f332619a90/musl/Dockerfile) or this [older busybox image](https://github.com/jpetazzo/docker-busybox). + +--- + +## Building binaries outside: pros and cons + +Pros: + +- final image can be very small + +Cons: + +- requires an extra build tool + +- we're back in dependency hell and "works on my machine" + +Cons, if binary is added to code repository: + +- breaks portability across different platforms + +- grows repository size a lot if the binary is updated frequently + +--- + +## Squashing the final image + +The idea is to transform the final image into a single-layer image. + +This can be done in (at least) two ways. + +- Activate experimental features and squash the final image: + ```bash + docker image build --squash ... + ``` + +- Export/import the final image. + ```bash + docker build -t temp-image . + docker run --entrypoint true --name temp-container temp-image + docker export temp-container | docker import - final-image + docker rm temp-container + docker rmi temp-image + ``` + +--- + +## Squashing the image: pros and cons + +Pros: + +- single-layer images are smaller and faster to download + +- removed files no longer take up storage and network resources + +Cons: + +- we still need to actively remove unnecessary files + +- squash operation can take a lot of time (on big images) + +- squash operation does not benefit from cache +
+ (even if we change just a tiny file, the whole image needs to be re-squashed) + +--- + +## Multi-stage builds + +Multi-stage builds allow us to have multiple *stages*. + +Each stage is a separate image, and can copy files from previous stages. + +We're going to see how they work in more detail. + +--- + +# Multi-stage builds * At any point in our `Dockerfile`, we can add a new `FROM` line. From 9614f8761a006b6f74ec03d912af97346d104940 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 24 May 2018 06:03:28 -0500 Subject: [PATCH 05/24] Add link to Serge Hallyn blog post --- slides/intro/Docker_History.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/slides/intro/Docker_History.md b/slides/intro/Docker_History.md index 299042c6..9406e379 100644 --- a/slides/intro/Docker_History.md +++ b/slides/intro/Docker_History.md @@ -10,10 +10,12 @@ * [Solaris Containers (2004)](https://en.wikipedia.org/wiki/Solaris_Containers) -* [FreeBSD jails (1999)](https://www.freebsd.org/cgi/man.cgi?query=jail&sektion=8&manpath=FreeBSD+4.0-RELEASE) +* [FreeBSD jails (1999-2000)](https://www.freebsd.org/cgi/man.cgi?query=jail&sektion=8&manpath=FreeBSD+4.0-RELEASE) Containers have been around for a *very long time* indeed. +(See [this excellent blog post by Serge Hallyn](https://s3hh.wordpress.com/2018/03/22/history-of-containers/) for more historic details.) + --- class: pic From 06f70e8246d5402e8a256a7cffbeaad3b9944f1b Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 24 May 2018 07:06:21 -0500 Subject: [PATCH 06/24] Add 'tree' in the VMs This is a convenient tool to get an idea of what a directory hierarchy looks like. --- prepare-vms/lib/postprep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare-vms/lib/postprep.py b/prepare-vms/lib/postprep.py index d5970d54..1c4ed4d9 100755 --- a/prepare-vms/lib/postprep.py +++ b/prepare-vms/lib/postprep.py @@ -108,7 +108,7 @@ system("sudo chmod +x /usr/local/bin/docker-machine") system("docker-machine version") system("sudo apt-get remove -y --purge dnsmasq-base") -system("sudo apt-get -qy install python-setuptools pssh apache2-utils httping htop unzip mosh") +system("sudo apt-get -qy install python-setuptools pssh apache2-utils httping htop unzip mosh tree") ### Wait for Docker to be up. ### (If we don't do this, Docker will not be responsive during the next step.) From b0841562ea5cf80e872e365c7439eab0e43fc485 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 25 May 2018 09:31:44 -0500 Subject: [PATCH 07/24] Add a bunch of Dockerfile examples --- slides/intro/Dockerfile_Tips.md | 263 ++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) diff --git a/slides/intro/Dockerfile_Tips.md b/slides/intro/Dockerfile_Tips.md index 2ea68133..3fc63116 100644 --- a/slides/intro/Dockerfile_Tips.md +++ b/slides/intro/Dockerfile_Tips.md @@ -96,3 +96,266 @@ CMD, EXPOSE ... * The build fails as soon as an instruction fails * If `RUN ` fails, the build doesn't produce an image * If it succeeds, it produces a clean image (without test libraries and data) + +--- + +# Dockerfile examples + +There are a number of tips, tricks, and techniques that we can use in Dockerfiles. + +But sometimes, we have to use different (and even opposed) practices depending on: + +- the complexity of our project, + +- the programming language or framework that we are using, + +- the stage of our project (early MVP vs. super-stable production), + +- whether we're building a final image or a base for further images, + +- etc. + +We are going to show a few examples using very different techniques. + +--- + +## When to optimize an image + +When authoring official images, it is a good idea to reduce as much as possible: + +- the number of layers, + +- the size of the final image. + +This is often done at the expense of build time and convenience for the image maintainer; +but when an image is downloaded millions of time, saving even a few seconds of pull time +can be worth it. + +.small[ +```dockerfile +RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev && rm -rf /var/lib/apt/lists/* \ + && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ + && docker-php-ext-install gd +... +RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${WORDPRESS_UPSTREAM_VERSION}.tar.gz \ + && echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c - \ + && tar -xzf wordpress.tar.gz -C /usr/src/ \ + && rm wordpress.tar.gz \ + && chown -R www-data:www-data /usr/src/wordpress +``` +] + +(Source: [Wordpress official image](https://github.com/docker-library/wordpress/blob/618490d4bdff6c5774b84b717979bfe3d6ba8ad1/apache/Dockerfile)) + +--- + +## When to *not* optimize an image + +Sometimes, it is better to prioritize *maintainer convenience*. + +In particular, if: + +- the image changes a lot, + +- the image has very few users (e.g. only 1, the maintainer!), + +- the image is built and run on the same machine, + +- the image is built and run on machines with a very fast link ... + +In these cases, just keep things simple! + +(Next slide: a Dockerfile that can be used to preview a Jekyll / github pages site.) + +--- + +```dockerfile +FROM debian:sid + +RUN apt-get update -q +RUN apt-get install -yq build-essential make +RUN apt-get install -yq zlib1g-dev +RUN apt-get install -yq ruby ruby-dev +RUN apt-get install -yq python-pygments +RUN apt-get install -yq nodejs +RUN apt-get install -yq cmake +RUN gem install --no-rdoc --no-ri github-pages + +COPY . /blog +WORKDIR /blog + +VOLUME /blog/_site + +EXPOSE 4000 +CMD ["jekyll", "serve", "--host", "0.0.0.0", "--incremental"] +``` + +--- + +## Multi-dimensional versioning systems + +Images can have a tag, indicating the version of the image. + +But sometimes, there are multiple important components, and we need to indicate the versions +for all of them. + +This can be done with environment variables: + +```dockerfile +ENV PIP=9.0.3 \ + ZC_BUILDOUT=2.11.2 \ + SETUPTOOLS=38.7.0 \ + PLONE_MAJOR=5.1 \ + PLONE_VERSION=5.1.0 \ + PLONE_MD5=76dc6cfc1c749d763c32fff3a9870d8d +``` + +(Source: [Plone official image](https://github.com/plone/plone.docker/blob/master/5.1/5.1.0/alpine/Dockerfile)) + +--- + +## Entrypoints and wrappers + +It is very common to define a custom entrypoint. + +That entrypoint will generally be a script, performing any combination of: + +- pre-flights checks (if a required dependency is not available, display + a nice error message early instead of an obscure one in a deep log file), + +- generation or validation of configuration files, + +- dropping privileges (with e.g. `su` or `gosu`, sometimes combined with `chown`), + +- and more. + +--- + +## A typical entrypoint script + +```dockerfile + #!/bin/sh + set -e + + # first arg is `-f` or `--some-option` + # or first arg is `something.conf` + if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then + set -- redis-server "$@" + fi + + # allow the container to be started with `--user` + if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then + chown -R redis . + exec su-exec redis "$0" "$@" + fi + + exec "$@" +``` + +(Source: [Redis official image](https://github.com/docker-library/redis/blob/d24f2be82673ccef6957210cc985e392ebdc65e4/4.0/alpine/docker-entrypoint.sh)) + +--- + +## Factoring information + +To facilitate maintenance (and avoid human errors), avoid to repeat information like: + +- version numbers, + +- remote asset URLs (e.g. source tarballs) ... + +Instead, use environment variables. + +.small[ +```dockerfile +ENV NODE_VERSION 10.2.1 +... +RUN ... + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \ + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ + && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ + && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ + && tar -xf "node-v$NODE_VERSION.tar.xz" \ + && cd "node-v$NODE_VERSION" \ +... +``` +] + +(Source: [Nodejs official image](https://github.com/nodejs/docker-node/blob/master/10/alpine/Dockerfile)) + +--- + +## Overrides + +In theory, development and production images should be the same. + +In practice, we often need to enable specific behaviors in development (e.g. debug statements). + +One way to reconcile both needs is to use Compose to enable these behaviors. + +Let's look at the [trainingwheels](https://github.com/jpetazzo/trainingwheels) demo app for an example. + +--- + +## Production image + +This Dockerfile builds an image leveraging gunicorn: + +```dockerfile +FROM python +RUN pip install flask +RUN pip install gunicorn +RUN pip install redis +COPY . /src +WORKDIR /src +CMD gunicorn --bind 0.0.0.0:5000 --workers 10 counter:app +EXPOSE 5000 +``` + +(Source: [traininghweels Dockerfile](https://github.com/jpetazzo/trainingwheels/blob/master/www/Dockerfile)) + +--- + +## Development Compose file + +This Compose file uses the same image, but with a few overrides for development: + +- the Flask development serve ris used (overriding `CMD`), + +- the `DEBUG` environment variable is set, + +- a volume is used to provide a faster local development workflow. + +.small[ +```yaml +services: + www: + build: www + ports: + - 8000:5000 + user: nobody + environment: + DEBUG: 1 + command: python counter.py + volumes: + - ./www:/src +``` +] + +(Source: [trainingwheels Compose file](https://github.com/jpetazzo/trainingwheels/blob/master/docker-compose.yml)) + +--- + +## How to know which best practices are better? + +- The main goal of containers is to make our lives easier. + +- In this chapter, we showed many ways to write Dockerfiles. + +- These Dockerfiles use sometimes diametrally opposed techniques. + +- Yet, they were the "right" ones *for a specific situation.* + +- It's OK (and even encouraged) to start simple and evolve as needed. + +- Feel free to review this chapter later (after writing a few Dockerfiles) for inspiration! From cd5b06b9c7c159acba10c8f892ede4354746a931 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 28 May 2018 10:08:11 -0500 Subject: [PATCH 08/24] Show how to connect/disconnect dynamically --- slides/intro/Container_Network_Model.md | 175 +++++++++++++++++++++++- 1 file changed, 168 insertions(+), 7 deletions(-) diff --git a/slides/intro/Container_Network_Model.md b/slides/intro/Container_Network_Model.md index fa92abeb..9e13700b 100644 --- a/slides/intro/Container_Network_Model.md +++ b/slides/intro/Container_Network_Model.md @@ -498,12 +498,12 @@ b2887adeb5578a01fd9c55c435cad56bbbe802350711d2743691f95743680b09 * If containers span multiple hosts, we need an *overlay* network to connect them together. -* Docker ships with a default network plugin, `overlay`, implementing an overlay network leveraging +* Docker ships with a default network plugin, `overlay`, implementing an overlay network leveraging VXLAN, *enabled with Swarm Mode*. * Other plugins (Weave, Calico...) can provide overlay networks as well. -* Once you have an overlay network, *all the features that we've used in this chapter work identically +* Once you have an overlay network, *all the features that we've used in this chapter work identically across multiple hosts.* --- @@ -542,13 +542,174 @@ General idea: --- -## Section summary +## Connecting and disconnecting dynamically -We've learned how to: +* So far, we have specified which network to use when starting the container. -* Create private networks for groups of containers. +* The Docker Engine also allows to connect and disconnect while the container runs. -* Assign IP addresses to containers. +* This feature is exposed through the Docker API, and through two Docker CLI commands: -* Use container naming to implement service discovery. + * `docker network connect ` + * `docker network disconnect ` + +--- + +## Dynamically connecting to a network + +* We have a container named `es` connected to a network named `dev`. + +* Let's start a simple alpine container on the default network: + + ```bash + $ docker run -ti alpine sh + / # + ``` + +* In this container, try to ping the `es` container: + + ```bash + / # ping es + ping: bad address 'es' + ``` + + This doesn't work, but we will change that by connecting the container. + +--- + +## Finding the container ID and connecting it + +* Figure out the ID of our alpine container; here are two methods: + + * looking at `/etc/hostname` in the container, + + * running `docker ps -lq` on the host. + +* Run the following command on the host: + + ```bash + $ docker network connect dev `` + ``` + +--- + +## Checking what we did + +* Try again to `ping es` from the container. + +* It should now work correctly: + + ```bash + / # ping es + PING es (172.20.0.3): 56 data bytes + 64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.376 ms + 64 bytes from 172.20.0.3: seq=1 ttl=64 time=0.130 ms + ^C + ``` + +* Interrupt it with Ctrl-C. + +--- + +## Looking at the network setup in the container + +We can look at the list of network interfaces with `ifconfig`, `ip a`, or `ip l`: + +.small[ +```bash +/ # ip a +1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever +18: eth0@if19: mtu 1500 qdisc noqueue state UP + link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff + inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 + valid_lft forever preferred_lft forever +20: eth1@if21: mtu 1500 qdisc noqueue state UP + link/ether 02:42:ac:14:00:04 brd ff:ff:ff:ff:ff:ff + inet 172.20.0.4/16 brd 172.20.255.255 scope global eth1 + valid_lft forever preferred_lft forever +/ # +``` +] + +Each network connection is materialized with a virtual network interface. + +As we can see, we can be connected to multiple networks at the same time. + +--- + +## Disconnecting from a network + +* Let's try the symmetrical command to disconnect the container: + ```bash + $ docker network disconnect dev + ``` + +* From now on, if we try to ping `es`, it will not resolve: + ```bash + / # ping es + ping: bad address 'es' + ``` + +* Trying to ping the IP address directly won't work either: + ```bash + / # ping 172.20.0.3 + ... (nothing happens until we interrupt it with Ctrl-C) + ``` + +--- + +class: extra-details + +## Network aliases are scoped per network + +* Each network has its own set of network aliases. + +* We saw this earlier: `es` resolves to different addresses in `dev` and `prod`. + +* If we are connected to multiple networks, the resolver looks up names in each of them + (as of Docker Engine 18.03, it is the connection order) and stops as soon as the name + is found. + +* Therefore, if we are connected to both `dev` and `prod`, resolving `es` will **not** + give us the addresses of all the `es` services; but only the ones in `dev` or `prod`. + +* However, we can lookup `es.dev` or `es.prod` if we need to. + +--- + +class: extra-details + +## Finding out about our networks and names + +* We can do reverse DNS lookups on containers' IP addresses. + +* If the IP address belongs to a network (other than the default bridge), the result will be: + + ``` + name-or-first-alias-or-container-id.network-name + ``` + +* Example: + +.small[ +```bash +$ docker run -ti --net prod --net-alias hello alpine +/ # apk add --no-cache drill +... +OK: 5 MiB in 13 packages +/ # ifconfig +eth0 Link encap:Ethernet HWaddr 02:42:AC:15:00:03 + inet addr:`172.21.0.3` Bcast:172.21.255.255 Mask:255.255.0.0 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 +... +/ # drill -t ptr `3.0.21.172`.in-addr.arpa +... +;; ANSWER SECTION: +3.0.21.172.in-addr.arpa. 600 IN PTR `hello.prod`. +... +``` +] From 3e822bad827087f2000e69a59b8c81e6f3e865f7 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 28 May 2018 10:28:52 -0500 Subject: [PATCH 09/24] Add a slide about JSON file and log rotation --- slides/intro/Logging.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/slides/intro/Logging.md b/slides/intro/Logging.md index c5b463c3..6e88b8ca 100644 --- a/slides/intro/Logging.md +++ b/slides/intro/Logging.md @@ -131,6 +131,27 @@ We will then show one particular method in action, using ELK and Docker's loggin --- +## A word of warning about `json-file` + +- By default, log file size is unlimited. + +- This means that a very verbose container *will* use up all your disk space. + + (Or a less verbose container, but running for a very long time.) + +- Log rotation can be enabled by setting a `max-size` option. + +- Older log files can be removed by setting a `max-file` option. + +- Just like other logging options, these can be set per container, or globally. + +Example: +```bash +$ docker run --log-opt max-size=10m --log-opt max-file=3 elasticsearch +``` + +--- + ## Demo: sending logs to ELK - We are going to deploy an ELK stack. From 4213aba76e264a560adb793a6da2ba214a4cd0e5 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 29 May 2018 05:47:27 -0500 Subject: [PATCH 10/24] Show how to access internal services with kubectl proxy --- slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube/kubectlproxy.md | 117 ++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 slides/kube/kubectlproxy.md diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 92d76fab..266caade 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -33,6 +33,7 @@ chapters: - kube/kubectlrun.md - - kube/kubectlexpose.md - kube/ourapponkube.md + - kube/kubectlproxy.md - kube/dashboard.md - - kube/kubectlscale.md - kube/daemonset.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 1741762f..3275b0a3 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -35,6 +35,7 @@ chapters: - - kube/kubectlrun.md - kube/kubectlexpose.md - kube/ourapponkube.md + #- kube/kubectlproxy.md - - kube/dashboard.md - kube/kubectlscale.md - kube/daemonset.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 88b23c5e..ac472541 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -32,6 +32,7 @@ chapters: - kube/kubectlrun.md - - kube/kubectlexpose.md - kube/ourapponkube.md + - kube/kubectlproxy.md - kube/dashboard.md - - kube/kubectlscale.md - kube/daemonset.md diff --git a/slides/kube/kubectlproxy.md b/slides/kube/kubectlproxy.md new file mode 100644 index 00000000..76529a94 --- /dev/null +++ b/slides/kube/kubectlproxy.md @@ -0,0 +1,117 @@ +# Accessing internal services with `kubectl proxy` + +- `kubectl proxy` runs a proxy in the foreground + +- This proxy lets us access the Kubernetes API without authentication + + (`kubectl proxy` adds our credentials on the fly to the requests) + +- This proxy lets us access the Kubernetes API over plain HTTP + +- This is a great tool to learn and experiment with the Kubernetes API + +- The Kubernetes API also gives us a proxy to HTTP and HTTPS services + +- Therefore, we can use `kubectl proxy` to access internal services + + (Without using a `NodePort` or similar service) + +--- + +## Secure by default + +- By default, the proxy listens on port 8001 + + (But this can be changed, or we can tell `kubectl proxy` to pick a port) + +- By default, the proxy binds to `127.0.0.1` + + (Making it unreachable from other machines, for security reasons) + +- By default, the proxy only accepts connections from: + + `^localhost$,^127\.0\.0\.1$,^\[::1\]$` + +- This is great when running `kubectl proxy` locally + +- Not-so-great when running it on a remote machine + +--- + +## Running `kubectl proxy` on a remote machine + +- We are going to bind to `INADDR_ANY` instead of `127.0.0.1` + +- We are going to accept connections from any address + +.exercise[ + +- Run an open proxy to the Kubernetes API: + ```bash + kubectl proxy --port=8888 --address=0.0.0.0 --accept-hosts=.* + ``` + +] + +.warning[Anyone can now do whatever they want with our Kubernetes cluster! +
+(Don't do this on a real cluster!)] + +--- + +## Viewing available API routes + +- The default route (i.e. `/`) shows a list of available API endpoints + +.exercise[ + +- Point your browser to the IP address of the node running `kubectl proxy`, port 8888 + +] + +The result should look like this: +```json +{ + "paths": [ + "/api", + "/api/v1", + "/apis", + "/apis/", + "/apis/admissionregistration.k8s.io", + … +``` + +--- + +## Connecting to a service through the proxy + +- The API can proxy HTTP and HTTPS requests by accessing a special route: + ``` + /api/v1/namespaces/`name_of_namespace`/services/`name_of_service`/proxy + ``` + +- Since we now have access to the API, we can use this special route + +.exercise[ + +- Access the `hasher` service through the special proxy route: + ```open + http://`X.X.X.X`:8888/api/v1/namespaces/default/services/hasher/proxy + ``` + +] + +You should see the banner of the hasher service: `HASHER running on ...` + +--- + +## Stopping the proxy + +- Remember: as it is running right now, `kubectl proxy` gives open access to our cluster + +.exercise[ + +- Stop the `kubectl proxy` + +] + From 636a2d5c87285638949e7b5524bf76c56fd50989 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 29 May 2018 05:53:12 -0500 Subject: [PATCH 11/24] Show an easier way to create namespaces We were using 'kubectl apply' with a YAML snppet. It's valid, but it's quite convoluted. Instead, let's use 'kubectl create namespace'. We can still mention the other method of course. --- slides/kube/namespaces.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/slides/kube/namespaces.md b/slides/kube/namespaces.md index 8d42536a..9993a0d9 100644 --- a/slides/kube/namespaces.md +++ b/slides/kube/namespaces.md @@ -40,7 +40,12 @@ ## Creating namespaces -- We can create namespaces with a very minimal YAML, e.g.: +- Creating a namespace is done with the `kubectl create namespace` command: + ```bash + kubectl create namespace blue + ``` + +- We can also get fancy and use a very minimal YAML snippet, e.g.: ```bash kubectl apply -f- < Date: Tue, 29 May 2018 10:03:11 -0500 Subject: [PATCH 12/24] New script to count slides count-slides.py will count the number of slides per section, and compute size of each chapter as well. It is not perfect (for instance, it assumes that excluded_classes=in_person) but it should help to assess the size of the content before delivering long workshops. --- slides/count-slides.py | 57 ++++++++++++++++++++++++++++++++ slides/generate-chapter-sizes.sh | 10 ------ 2 files changed, 57 insertions(+), 10 deletions(-) create mode 100755 slides/count-slides.py delete mode 100755 slides/generate-chapter-sizes.sh diff --git a/slides/count-slides.py b/slides/count-slides.py new file mode 100755 index 00000000..20fd6d90 --- /dev/null +++ b/slides/count-slides.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +import re +import sys + +PREFIX = "name: toc-" +EXCLUDED = ["in-person"] + +class State(object): + def __init__(self): + self.current_slide = 1 + self.section_title = None + self.section_start = 0 + self.section_slides = 0 + self.chapters = {} + self.sections = {} + def show(self): + if self.section_title.startswith("chapter-"): + return + print("{0.section_title}\t{0.section_start}\t{0.section_slides}".format(self)) + self.sections[self.section_title] = self.section_slides + +state = State() + +title = None +for line in open(sys.argv[1]): + line = line.rstrip() + if line.startswith(PREFIX): + if state.section_title is None: + print("{}\t{}\t{}".format("title", "index", "size")) + else: + state.show() + state.section_title = line[len(PREFIX):].strip() + state.section_start = state.current_slide + state.section_slides = 0 + if line == "---": + state.current_slide += 1 + state.section_slides += 1 + if line == "--": + state.current_slide += 1 + toc_links = re.findall("\(#toc-(.*)\)", line) + if toc_links and state.section_title.startswith("chapter-"): + if state.section_title not in state.chapters: + state.chapters[state.section_title] = [] + state.chapters[state.section_title].append(toc_links[0]) + # This is really hackish + if line.startswith("class:"): + for klass in EXCLUDED: + if klass in line: + state.section_slides -= 1 + state.current_slide -= 1 + +state.show() + +for chapter in sorted(state.chapters): + chapter_size = sum(state.sections[s] for s in state.chapters[chapter]) + print("{}\t{}\t{}".format("total size for", chapter, chapter_size)) + diff --git a/slides/generate-chapter-sizes.sh b/slides/generate-chapter-sizes.sh deleted file mode 100755 index 00eeae6d..00000000 --- a/slides/generate-chapter-sizes.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -INPUT=$1 - -{ - echo "# Front matter" - cat "$INPUT" -} | - grep -e "^# " -e ^---$ | uniq -c | - sed "s/^ *//" | sed s/---// | - paste -d "\t" - - From 24017ad83f52d4adc6ed3a8e187a0879fea7552f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 29 May 2018 11:06:31 -0500 Subject: [PATCH 13/24] Clarify usage of <<< --- slides/intro/Namespaces_Cgroups.md | 55 +++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/slides/intro/Namespaces_Cgroups.md b/slides/intro/Namespaces_Cgroups.md index c27fae3a..f5819678 100644 --- a/slides/intro/Namespaces_Cgroups.md +++ b/slides/intro/Namespaces_Cgroups.md @@ -808,7 +808,7 @@ $ sudo mkdir $CG Limit it to approximately 100MB of memory usage: ```bash -$ sudo tee $CG/memory.memsw.limit_in_bytes <<<100000000 +$ sudo tee $CG/memory.memsw.limit_in_bytes <<< 100000000 ``` Move the current process to that cgroup: @@ -819,6 +819,59 @@ $ sudo tee $CG/tasks <<< $$ The current process *and all its future children* are now limited. +(Confused about `<<<`? Look at the next slide!) + +--- + +## What's `<<<`? + +- This is a "here string". (It is a non-POSIX shell extension.) + +- The following commands are equivalent: + + ```bash + foo <<< hello + ``` + + ```bash + echo hello | foo + ``` + + ```bash + foo < $CG/tasks" +``` + +The following commands, however, would be invalid: + +```bash +sudo echo $$ > $CG/tasks +``` + +```bash +sudo -i # (or su) +echo $$ > $CG/tasks +``` + --- ## Testing the memory limit From 8915bfb4430991894ef60c8f6b1cdfde4e0f400c Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 31 May 2018 12:55:09 -0500 Subject: [PATCH 14/24] Update README section indicating 'teacher for hire' --- README.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b75ba444..973a57b1 100644 --- a/README.md +++ b/README.md @@ -292,15 +292,31 @@ If there is a bug and you can't even reproduce it: sorry. It is probably an Heisenbug. We can't act on it until it's reproducible, alas. -If you have attended this workshop and have feedback, -or if you want somebody to deliver that workshop at your -conference or for your company: you can contact one of us! + +# “Please teach us!” + +If you have attended one of these workshops, and want +your team or organization to attend a similar one, you +can look at the list of upcoming events on +http://container.training/. + +You are also welcome to reuse these materials to run +your own workshop, for your team or even at a meetup +or conference. In that case, you might enjoy watching +[Bridget Kromhout's talk at KubeCon 2018 Europe]( +https://www.youtube.com/watch?v=mYsp_cGY2O0), explaining +precisely how to run such a workshop yourself. + +Finally, you can also contact the following persons, +who are experienced speakers, are familiar with the +material, and are available to deliver these workshops +at your conference or for your company: - jerome dot petazzoni at gmail dot com - bret at bretfisher dot com -If you are willing and able to deliver such workshops, -feel free to submit a PR to add your name to that list! +(If you are willing and able to deliver such workshops, +feel free to submit a PR to add your name to that list!) **Thank you!** From 5e78e00bc9bbad0aed46bb1f1f2393307d3d30b7 Mon Sep 17 00:00:00 2001 From: Alexis Daboville Date: Sat, 2 Jun 2018 15:09:38 +0100 Subject: [PATCH 15/24] Small typos (#272) * Small typo * elastichsearch -> elasticsearch * realeased -> released --- slides/intro/Container_Engines.md | 2 +- slides/intro/Dockerfile_Tips.md | 2 +- slides/intro/Logging.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/slides/intro/Container_Engines.md b/slides/intro/Container_Engines.md index 74e1d608..0deff1bc 100644 --- a/slides/intro/Container_Engines.md +++ b/slides/intro/Container_Engines.md @@ -73,7 +73,7 @@ Containers also exist (sometimes with other names) on Windows, macOS, Solaris, F ## LXC -* The venerable ancestor (first realeased in 2008). +* The venerable ancestor (first released in 2008). * Docker initially relied on it to execute containers. diff --git a/slides/intro/Dockerfile_Tips.md b/slides/intro/Dockerfile_Tips.md index 3fc63116..6d06fd79 100644 --- a/slides/intro/Dockerfile_Tips.md +++ b/slides/intro/Dockerfile_Tips.md @@ -320,7 +320,7 @@ EXPOSE 5000 This Compose file uses the same image, but with a few overrides for development: -- the Flask development serve ris used (overriding `CMD`), +- the Flask development server is used (overriding `CMD`), - the `DEBUG` environment variable is set, diff --git a/slides/intro/Logging.md b/slides/intro/Logging.md index 6e88b8ca..0fa11d95 100644 --- a/slides/intro/Logging.md +++ b/slides/intro/Logging.md @@ -213,7 +213,7 @@ $ docker-compose -f elk.yml up -d - it is set with the `ELASTICSEARCH_URL` environment variable, - - by default it is `localhost:9200`, we change it to `elastichsearch:9200`. + - by default it is `localhost:9200`, we change it to `elasticsearch:9200`. - We need to configure Logstash: From 3d7ed3a3f78e9c0cd0682c16d80ff5470afe3ce2 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 3 Jun 2018 05:10:48 -0500 Subject: [PATCH 16/24] Clarify how to stop kubectl proxy --- slides/kube/kubectlproxy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides/kube/kubectlproxy.md b/slides/kube/kubectlproxy.md index 76529a94..bc1f9cdf 100644 --- a/slides/kube/kubectlproxy.md +++ b/slides/kube/kubectlproxy.md @@ -111,7 +111,7 @@ You should see the banner of the hasher service: `HASHER running on ...` .exercise[ -- Stop the `kubectl proxy` +- Stop the `kubectl proxy` process with Ctrl-C ] From c5572020b9aec36c5b43baacb2a021843683920f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Petazzoni?= Date: Sun, 3 Jun 2018 05:28:16 -0500 Subject: [PATCH 17/24] Add a few slides about resource limits (#273) The section about namespaces and cgroups is very thorough, but we also need something showing how to practically limit container resource usage without diving into a very deep technical chapter. --- slides/intro-fullday.yml | 3 +- slides/intro-selfpaced.yml | 3 +- slides/intro/Resource_Limits.md | 229 ++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 slides/intro/Resource_Limits.md diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index 8aebbc93..4a10d87e 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -49,7 +49,8 @@ chapters: - intro/Advanced_Dockerfiles.md - intro/Application_Configuration.md - intro/Logging.md -- - intro/Namespaces_Cgroups.md +- - intro/Resource_Limits.md + - intro/Namespaces_Cgroups.md - intro/Copy_On_Write.md #- intro/Containers_From_Scratch.md - intro/Container_Engines.md diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 5637cdc8..06ac2353 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -49,7 +49,8 @@ chapters: - intro/Advanced_Dockerfiles.md - intro/Application_Configuration.md - intro/Logging.md -- - intro/Namespaces_Cgroups.md +- - intro/Resource_Limits.md + - intro/Namespaces_Cgroups.md - intro/Copy_On_Write.md #- intro/Containers_From_Scratch.md - intro/Container_Engines.md diff --git a/slides/intro/Resource_Limits.md b/slides/intro/Resource_Limits.md new file mode 100644 index 00000000..c74d2ebe --- /dev/null +++ b/slides/intro/Resource_Limits.md @@ -0,0 +1,229 @@ +# Limiting resources + +- So far, we have used containers as convenient units of deployment. + +- What happens when a container tries to use more resources than available? + + (RAM, CPU, disk usage, disk and network I/O...) + +- What happens when multiple containers compete for the same resource? + +- Can we limit resources available to a container? + + (Spoiler alert: yes!) + +--- + +## Container processes are normal processes + +- Containers are closer to "fancy processes" than to "lightweight VMs". + +- A process running in a container is, in fact, a process running on the host. + +- Let's look at the output of `ps` on a container host running 3 containers : + + ``` + 0 2662 0.2 0.3 /usr/bin/dockerd -H fd:// + 0 2766 0.1 0.1 \_ docker-containerd --config /var/run/docker/containe + 0 23479 0.0 0.0 \_ docker-containerd-shim -namespace moby -workdir + 0 23497 0.0 0.0 | \_ `nginx`: master process nginx -g daemon off; + 101 23543 0.0 0.0 | \_ `nginx`: worker process + 0 23565 0.0 0.0 \_ docker-containerd-shim -namespace moby -workdir + 102 23584 9.4 11.3 | \_ `/docker-java-home/jre/bin/java` -Xms2g -Xmx2 + 0 23707 0.0 0.0 \_ docker-containerd-shim -namespace moby -workdir + 0 23725 0.0 0.0 \_ `/bin/sh` + ``` + +- The highlighted processes are containerized processes. +
+ (That host is running nginx, elasticsearch, and alpine.) + +--- + +## By default: nothing changes + +- What happens when a process uses too much memory on a Linux system? + +-- + +- Simplified answer: + + - swap is used (if available); + + - if there is not enough swap space, eventually, the out-of-memory killer is invoked; + + - the OOM killer uses heuristics to kill processes; + + - sometimes, it kills an unrelated process. + +-- + +- What happens when a container uses too much memory? + +- The same thing! + + (i.e., a process eventually gets killed, possibly in another container.) + +--- + +## Limiting container resources + +- The Linux kernel offers rich mechanisms to limit container resources. + +- For memory usage, the mechanism is part of the *cgroup* subsystem. + +- This subsystem allows to limit the memory for a process or a group of processes. + +- A container engine leverages these mechanisms to limit memory for a container. + +- The out-of-memory killer has a new behavior: + + - it runs when a container exceeds its allowed memory usage, + + - in that case, it only kills processes in that container. + +--- + +## Limiting memory in practice + +- The Docker Engine offers multiple flags to limit memory usage. + +- The two most useful ones are `--memory` and `--memory-swap`. + +- `--memory` limits the amount of physical RAM used by a container. + +- `--memory-swap` limits the total amount (RAM+swap) used by a container. + +- The memory limit can be expressed in bytes, or with a unit suffix. + + (e.g.: `--memory 100m` = 100 megabytes.) + +- We will see two strategies: limiting RAM usage, or limiting both + +--- + +## Limiting RAM usage + +Example: + +```bash +docker run -ti --memory 100m python +``` + +If the container tries to use more than 100 MB of RAM, *and* swap is available: + +- the container will not be killed, + +- memory above 100 MB will be swapped out, + +- in most cases, the app in the container will be slowed down (a lot). + +If we run out of swap, the global OOM killer still intervenes. + +--- + +## Limiting both RAM and swap usage + +Example: + +```bash +docker run -ti --memory 100m --memory-swap 100m python +``` + +If the container tries to use more than 100 MB of memory, it is killed. + +On the other hand, the application will never be slowed down because of swap. + +--- + +## When to pick which strategy? + +- Stateful services (like databases) will lose or corrupt data when killed + +- Allow them to use swap space, but monitor swap usage + +- Stateless services can usually be killed with little impact + +- Limit their mem+swap usage, but monitor if they get killed + +- Ultimately, this is no different from "do I want swap, and how much?" + +--- + +## Limiting CPU usage + +- There are no less than 3 ways to limit CPU usage: + + - setting a relative priority with `--cpu-shares`, + + - setting a CPU% limit with `--cpus`, + + - pinning a container to specific CPUs with `--cpuset-cpus`. + +- They can be used separately or together. + +--- + +## Setting relative priority + +- Each container has a relative priority used by the Linux scheduler. + +- By default, this priority is 1024. + +- As long as CPU usage is not maxed out, this has no effect. + +- When CPU usage is maxed out, each container receives CPU cycles in proportion of its relative priority. + +- In other words: a container with `--cpu-shares 2048` will receive twice as much than the default. + +--- + +## Setting a CPU% limit + +- This setting will make sure that a container doesn't use more than a given % of CPU. + +- The value is expressed in CPUs; therefore: + + `--cpus 0.1` means 10% of one CPU, + + `--cpus 1.0` means 100% of one whole CPU, + + `--cpus 10.0` means 10 entire CPUs. + +--- + +## Pinning containers to CPUs + +- On multi-core machines, it is possible to restrict the execution on a set of CPUs. + +- Examples: + + `--cpuset-cpus 0` forces the container to run on CPU 0; + + `--cpuset-cpus 3,5,7` restricts the container to CPUs 3, 5, 7; + + `--cpuset-cpus 0-3,8-11` restricts the container to CPUs 0, 1, 2, 3, 8, 9, 10, 11. + +- This will not reserve the corresponding CPUs! + + (They might still be used by other containers, or uncontainerized processes.) + +--- + +## Limiting disk usage + +- Most storage drivers do not support limiting the disk usage of containers. + + (With the exception of devicemapper, but the limit cannot be set easily.) + +- This means that a single container could exhaust disk space for everyone. + +- In practice, however, this is not a concern, because: + + - data files (for stateful services) should reside on volumes, + + - assets (e.g. images, user-generated content...) should reside on object stores or on volume, + + - logs are written on standard output and gathered by the container engine. + +- Container disk usage can be audited with `docker ps -s` and `docker diff`. From da053ecde2ad9f92e4e7f7859931b0caab287665 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 3 Jun 2018 15:27:27 -0500 Subject: [PATCH 18/24] Update fundamentals TOC --- slides/intro-fullday.yml | 15 +++++++-------- slides/intro-selfpaced.yml | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index 4a10d87e..e4b1d523 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -1,7 +1,6 @@ title: | Introduction - to Docker and - Containers + to Containers chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" @@ -31,13 +30,13 @@ chapters: - intro/Building_Images_With_Dockerfiles.md - intro/Cmd_And_Entrypoint.md - intro/Copying_Files_During_Build.md - - intro/Multi_Stage_Builds.md +- - intro/Multi_Stage_Builds.md - 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/Container_Networking_Basics.md - intro/Network_Drivers.md - intro/Container_Network_Model.md #- intro/Connecting_Containers_With_Links.md @@ -46,14 +45,14 @@ chapters: - intro/Working_With_Volumes.md - intro/Compose_For_Dev_Stacks.md - intro/Docker_Machine.md - - intro/Advanced_Dockerfiles.md +- - intro/Advanced_Dockerfiles.md - intro/Application_Configuration.md - intro/Logging.md -- - intro/Resource_Limits.md - - intro/Namespaces_Cgroups.md + - intro/Resource_Limits.md +- - intro/Namespaces_Cgroups.md - intro/Copy_On_Write.md #- intro/Containers_From_Scratch.md - - intro/Container_Engines.md +- - intro/Container_Engines.md - intro/Ecosystem.md - intro/Orchestration_Overview.md - common/thankyou.md diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 06ac2353..0bb89df8 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -1,7 +1,6 @@ title: | Introduction - to Docker and - Containers + to Containers chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)" #chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)" @@ -31,13 +30,13 @@ chapters: - intro/Building_Images_With_Dockerfiles.md - intro/Cmd_And_Entrypoint.md - intro/Copying_Files_During_Build.md - - intro/Multi_Stage_Builds.md +- - intro/Multi_Stage_Builds.md - 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/Container_Networking_Basics.md - intro/Network_Drivers.md - intro/Container_Network_Model.md #- intro/Connecting_Containers_With_Links.md @@ -46,14 +45,14 @@ chapters: - intro/Working_With_Volumes.md - intro/Compose_For_Dev_Stacks.md - intro/Docker_Machine.md - - intro/Advanced_Dockerfiles.md +- - intro/Advanced_Dockerfiles.md - intro/Application_Configuration.md - intro/Logging.md -- - intro/Resource_Limits.md - - intro/Namespaces_Cgroups.md + - intro/Resource_Limits.md +- - intro/Namespaces_Cgroups.md - intro/Copy_On_Write.md #- intro/Containers_From_Scratch.md - - intro/Container_Engines.md +- - intro/Container_Engines.md - intro/Ecosystem.md - intro/Orchestration_Overview.md - common/thankyou.md From 14eb19a42b6c2d2fe29abe5c48b3813f6a18cf4a Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 05:43:28 -0500 Subject: [PATCH 19/24] Typo fixes --- slides/intro/Dockerfile_Tips.md | 6 +++--- slides/intro/Installing_Docker.md | 2 +- slides/intro/Namespaces_Cgroups.md | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/slides/intro/Dockerfile_Tips.md b/slides/intro/Dockerfile_Tips.md index 6d06fd79..8d9e56f9 100644 --- a/slides/intro/Dockerfile_Tips.md +++ b/slides/intro/Dockerfile_Tips.md @@ -237,13 +237,13 @@ That entrypoint will generally be a script, performing any combination of: #!/bin/sh set -e - # first arg is `-f` or `--some-option` - # or first arg is `something.conf` + # first arg is '-f' or '--some-option' + # or first arg is 'something.conf' if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then set -- redis-server "$@" fi - # allow the container to be started with `--user` + # allow the container to be started with '--user' if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then chown -R redis . exec su-exec redis "$0" "$@" diff --git a/slides/intro/Installing_Docker.md b/slides/intro/Installing_Docker.md index d1f98552..41cfa90b 100644 --- a/slides/intro/Installing_Docker.md +++ b/slides/intro/Installing_Docker.md @@ -85,7 +85,7 @@ class: extra-details https://docs.docker.com/docker-for-mac/install/ -* On Windows 10 Pro, Enterprise, and Eduction, you can use Docker for Windows: +* On Windows 10 Pro, Enterprise, and Education, you can use Docker for Windows: https://docs.docker.com/docker-for-windows/install/ diff --git a/slides/intro/Namespaces_Cgroups.md b/slides/intro/Namespaces_Cgroups.md index f5819678..981ef5ae 100644 --- a/slides/intro/Namespaces_Cgroups.md +++ b/slides/intro/Namespaces_Cgroups.md @@ -913,8 +913,6 @@ Killed - 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 From 30ca940eeb9e2c317d4b624f0a6b487fd2565991 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 05:49:24 -0500 Subject: [PATCH 20/24] Opt-out a bunch of slides in the deep dive section --- slides/intro/Namespaces_Cgroups.md | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/slides/intro/Namespaces_Cgroups.md b/slides/intro/Namespaces_Cgroups.md index 981ef5ae..ff85a9ac 100644 --- a/slides/intro/Namespaces_Cgroups.md +++ b/slides/intro/Namespaces_Cgroups.md @@ -76,6 +76,8 @@ The last item should be done for educational purposes only! --- +class: extra-details, deep-dive + ## Manipulating namespaces - Namespaces are created with two methods: @@ -94,6 +96,8 @@ The last item should be done for educational purposes only! --- +class: extra-details, deep-dive + ## Namespaces lifecycle - When the last process of a namespace exits, the namespace is destroyed. @@ -114,6 +118,8 @@ The last item should be done for educational purposes only! --- +class: extra-details, deep-dive + ## Namespaces can be used independently - As mentioned in the previous slides: @@ -150,6 +156,8 @@ The last item should be done for educational purposes only! --- +class: extra-details, deep-dive + ## Creating our first namespace Let's use `unshare` to create a new process that will have its own UTS namespace: @@ -166,6 +174,8 @@ $ sudo unshare --uts --- +class: extra-details, deep-dive + ## Demonstrating our uts namespace In our new "container", check the hostname, change it, and check it: @@ -398,6 +408,8 @@ class: extra-details --- +class: extra-details, deep-dive + ## Setting up a private `/tmp` Create a new mount namespace: @@ -435,6 +447,8 @@ The mount is automatically cleaned up when you exit the process. --- +class: extra-details, deep-dive + ## PID namespace in action Create a new PID namespace: @@ -453,10 +467,14 @@ Check the process tree in the new namespace: -- +class: extra-details, deep-dive + 🤔 Why do we see all the processes?!? --- +class: extra-details, deep-dive + ## PID namespaces and `/proc` - Tools like `ps` rely on the `/proc` pseudo-filesystem. @@ -471,6 +489,8 @@ Check the process tree in the new namespace: --- +class: extra-details, deep-dive + ## PID namespaces, take 2 - This can be solved by mounting `/proc` in the namespace. @@ -570,6 +590,8 @@ Check `man 2 unshare` and `man pid_namespaces` if you want more details. --- +class: extra-details, deep-dive + ## User namespace challenges - UID needs to be mapped when passed between processes or kernel subsystems. @@ -686,6 +708,8 @@ cpu memory --- +class: extra-details, deep-dive + ## Cgroups v1 vs v2 - Cgroups v1 are available on all systems (and widely used). @@ -759,6 +783,8 @@ cpu memory --- +class: extra-details, deep-dive + ## Avoiding the OOM killer - For some workloads (databases and stateful systems), killing @@ -778,6 +804,8 @@ cpu memory --- +class: extra-details, deep-dive + ## Overhead of the memory cgroup - Each time a process grabs or releases a page, the kernel update counters. @@ -796,6 +824,8 @@ cpu memory --- +class: extra-details, deep-dive + ## Setting up a limit with the memory cgroup Create a new memory cgroup: @@ -823,6 +853,8 @@ The current process *and all its future children* are now limited. --- +class: extra-details, deep-dive + ## What's `<<<`? - This is a "here string". (It is a non-POSIX shell extension.) @@ -847,6 +879,8 @@ The current process *and all its future children* are now limited. --- +class: extra-details, deep-dive + ## Writing to cgroups pseudo-files requires root Instead of: @@ -874,6 +908,8 @@ echo $$ > $CG/tasks --- +class: extra-details, deep-dive + ## Testing the memory limit Start the Python interpreter: From 6e35162788ec3aa793f7625c2145534259072633 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 05:50:21 -0500 Subject: [PATCH 21/24] Remove 'kubernetes in action' demo --- slides/intro/Orchestration_Overview.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/slides/intro/Orchestration_Overview.md b/slides/intro/Orchestration_Overview.md index b07b3418..5be67d90 100644 --- a/slides/intro/Orchestration_Overview.md +++ b/slides/intro/Orchestration_Overview.md @@ -420,8 +420,3 @@ It depends on: - false, if we focus on what matters. ---- - -## Kubernetes in action - -.center[![Demo stamp](images/demo.jpg)] \ No newline at end of file From cc6f36b50f5bf9f027de7de2aef426a61892b0c9 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 05:54:02 -0500 Subject: [PATCH 22/24] Wording (non-native speakers probably don't know boo-boo) --- slides/kube/rollout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides/kube/rollout.md b/slides/kube/rollout.md index a6120b9f..0637d0fe 100644 --- a/slides/kube/rollout.md +++ b/slides/kube/rollout.md @@ -133,7 +133,7 @@ That rollout should be pretty quick. What shows in the web UI? --- -## Rolling out a boo-boo +## Rolling out something invalid - What happens if we make a mistake? From a21e8b0849aae43510f625c6aa87012ae3d8e70f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 06:11:00 -0500 Subject: [PATCH 23/24] Image and title size fixes --- slides/intro/Ambassadors.md | 2 ++ slides/intro/Compose_For_Dev_Stacks.md | 2 +- slides/intro/Local_Development_Workflow.md | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/slides/intro/Ambassadors.md b/slides/intro/Ambassadors.md index 136d7453..261b866d 100644 --- a/slides/intro/Ambassadors.md +++ b/slides/intro/Ambassadors.md @@ -40,6 +40,8 @@ ambassador containers. --- +class: pic + ![ambassador](images/ambassador-diagram.png) --- diff --git a/slides/intro/Compose_For_Dev_Stacks.md b/slides/intro/Compose_For_Dev_Stacks.md index 39065520..2e0a6a3e 100644 --- a/slides/intro/Compose_For_Dev_Stacks.md +++ b/slides/intro/Compose_For_Dev_Stacks.md @@ -49,7 +49,7 @@ Before diving in, let's see a small example of Compose in action. --- -## Compose in action +class: pic ![composeup](images/composeup.gif) diff --git a/slides/intro/Local_Development_Workflow.md b/slides/intro/Local_Development_Workflow.md index 11f53e22..648dfe18 100644 --- a/slides/intro/Local_Development_Workflow.md +++ b/slides/intro/Local_Development_Workflow.md @@ -17,7 +17,7 @@ At the end of this section, you will be able to: --- -## Containerized local development environments +## Local development in a container We want to solve the following issues: From e294a4726cc7fd24a71950d31abd7264587fc3f4 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 4 Jun 2018 08:47:30 -0500 Subject: [PATCH 24/24] Update version numbers --- slides/kube/versions-k8s.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slides/kube/versions-k8s.md b/slides/kube/versions-k8s.md index 198d30ec..6a63ca82 100644 --- a/slides/kube/versions-k8s.md +++ b/slides/kube/versions-k8s.md @@ -1,8 +1,8 @@ ## Versions installed -- Kubernetes 1.10.1 +- Kubernetes 1.10.3 - Docker Engine 18.03.0-ce -- Docker Compose 1.20.1 +- Docker Compose 1.21.1 .exercise[