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!** 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 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.) 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/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" - - diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index 80e365e9..e4b1d523 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -1,11 +1,14 @@ 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)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - self-paced @@ -27,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 @@ -42,13 +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/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 883184f5..0bb89df8 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -1,11 +1,14 @@ 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)" +gitrepo: github.com/jpetazzo/container.training + +slides: http://container.training/ + exclude: - in-person @@ -27,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 @@ -42,13 +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/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/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/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/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`. +... +``` +] 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 diff --git a/slides/intro/Dockerfile_Tips.md b/slides/intro/Dockerfile_Tips.md index 2ea68133..8d9e56f9 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 server is 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! 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/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: diff --git a/slides/intro/Logging.md b/slides/intro/Logging.md index c5b463c3..0fa11d95 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. @@ -192,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: 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. diff --git a/slides/intro/Namespaces_Cgroups.md b/slides/intro/Namespaces_Cgroups.md index c27fae3a..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: @@ -808,7 +838,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,8 +849,67 @@ $ sudo tee $CG/tasks <<< $$ The current process *and all its future children* are now limited. +(Confused about `<<<`? Look at the next slide!) + --- +class: extra-details, deep-dive + +## 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 +``` + +--- + +class: extra-details, deep-dive + ## Testing the memory limit Start the Python interpreter: @@ -860,8 +949,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 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 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`. 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..266caade 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 @@ -29,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 13e9b1fc..3275b0a3 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 @@ -31,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 9989a7c0..ac472541 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 @@ -28,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/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/kubectlproxy.md b/slides/kube/kubectlproxy.md new file mode 100644 index 00000000..bc1f9cdf --- /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` process with Ctrl-C + +] + 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- <