Compare commits
68 Commits
dc17eu
...
qconsf2017
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bd50cdcf3 | ||
|
|
e0b27ae943 | ||
|
|
d2d1771fd3 | ||
|
|
5518c8c6ec | ||
|
|
fab3b3be89 | ||
|
|
6c5e3eb3f3 | ||
|
|
d99dbd5878 | ||
|
|
6d4894458a | ||
|
|
bc367a1297 | ||
|
|
31d1074ee0 | ||
|
|
cd18a87b8c | ||
|
|
2bb2edc4f6 | ||
|
|
8279a3bce9 | ||
|
|
5613a37c8f | ||
|
|
93524898cc | ||
|
|
6bba1684ec | ||
|
|
e9319060f6 | ||
|
|
4322478a4a | ||
|
|
00262c767e | ||
|
|
c8030e1500 | ||
|
|
3717b90444 | ||
|
|
d003bdb765 | ||
|
|
8b246ac334 | ||
|
|
9df543a6bb | ||
|
|
77befc1092 | ||
|
|
4417771315 | ||
|
|
04fa6ec1d8 | ||
|
|
5e8bdcb1f6 | ||
|
|
07d99763d3 | ||
|
|
87a2051b24 | ||
|
|
0eddc876f1 | ||
|
|
1a67a1397b | ||
|
|
dc6bf9d0cf | ||
|
|
84f8bf007e | ||
|
|
6a2a66c165 | ||
|
|
f491d559e3 | ||
|
|
5d9bdc303c | ||
|
|
f7f0ecddd4 | ||
|
|
f0f03e2440 | ||
|
|
1f78264e9f | ||
|
|
2aad5319f9 | ||
|
|
479b858398 | ||
|
|
34b8bfd1b2 | ||
|
|
408036b09c | ||
|
|
a45ec1cb84 | ||
|
|
8decf1852f | ||
|
|
350fac21f6 | ||
|
|
cedd386eee | ||
|
|
5751765d66 | ||
|
|
7293c071bd | ||
|
|
98cf8f4f04 | ||
|
|
c61ccd1c27 | ||
|
|
6b3d0efa56 | ||
|
|
164578f1c8 | ||
|
|
938fe956cf | ||
|
|
b44d402c1d | ||
|
|
fbaa511813 | ||
|
|
f000594c62 | ||
|
|
71ba94063e | ||
|
|
b025a8c966 | ||
|
|
c36aab132b | ||
|
|
1e7a47ed37 | ||
|
|
87f544774e | ||
|
|
db9ee5f03a | ||
|
|
d0f5d69157 | ||
|
|
742c7a78bc | ||
|
|
918d3a6c23 | ||
|
|
1f9b304eec |
2
.gitignore
vendored
@@ -7,4 +7,4 @@ prepare-vms/ips.pdf
|
||||
prepare-vms/settings.yaml
|
||||
prepare-vms/tags
|
||||
slides/*.yml.html
|
||||
autotest/nextstep
|
||||
slides/nextstep
|
||||
|
||||
13
README.md
@@ -39,14 +39,16 @@ your own tutorials.
|
||||
All these materials have been gathered in a single repository
|
||||
because they have a few things in common:
|
||||
|
||||
- a [build system](slides/) generating HTML slides from
|
||||
Markdown source files;
|
||||
- some [common slides](slides/common/) that are re-used
|
||||
(and updated) identically between different decks;
|
||||
- a [build system](slides/) generating HTML slides from
|
||||
Markdown source files;
|
||||
- a [semi-automated test harness](slides/autotest.py) to check
|
||||
that the exercises and examples provided work properly;
|
||||
- a [PhantomJS script](slides/slidechecker.js) to check
|
||||
that the slides look good and don't have formatting issues;
|
||||
- [deployment scripts](prepare-vms/) to start training
|
||||
VMs in bulk;
|
||||
- a [semi-automated test harness](autotest/) to check
|
||||
that the exercises and examples provided work properly;
|
||||
- a fancy pipeline powered by
|
||||
[Netlify](https://www.netlify.com/) and continuously
|
||||
deploying `master` to http://container.training/.
|
||||
@@ -74,9 +76,6 @@ a few other contributors. It is actively maintained.
|
||||
|
||||
## Repository structure
|
||||
|
||||
- [autotest](autotest/)
|
||||
- Semi-automated testing system to check that all the exercises
|
||||
in the slides work properly.
|
||||
- [bin](bin/)
|
||||
- A few helper scripts that you can safely ignore for now.
|
||||
- [dockercoins](dockercoins/)
|
||||
|
||||
@@ -20,5 +20,5 @@ paper_margin: 0.2in
|
||||
engine_version: test
|
||||
|
||||
# These correspond to the version numbers visible on their respective GitHub release pages
|
||||
compose_version: 1.16.1
|
||||
machine_version: 0.12.0
|
||||
compose_version: 1.17.1
|
||||
machine_version: 0.13.0
|
||||
|
||||
@@ -20,5 +20,5 @@ paper_margin: 0.2in
|
||||
engine_version: test
|
||||
|
||||
# These correspond to the version numbers visible on their respective GitHub release pages
|
||||
compose_version: 1.16.1
|
||||
machine_version: 0.12.0
|
||||
compose_version: 1.17.1
|
||||
machine_version: 0.13.0
|
||||
|
||||
1
slides/_redirects
Normal file
@@ -0,0 +1 @@
|
||||
/ /intro-fullday.yml.html 200!
|
||||
17
slides/appendcheck.py
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
logging.info("Checking file {}...".format(filename))
|
||||
text = subprocess.check_output(["./slidechecker.js", filename])
|
||||
html = open(filename).read()
|
||||
html = html.replace("</textarea>", "\n---\n```\n{}\n```\n</textarea>".format(text))
|
||||
|
||||
open(filename, "w").write(html)
|
||||
@@ -1,16 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
import uuid
|
||||
import click
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
|
||||
|
||||
interactive = True
|
||||
verify_status = False
|
||||
simulate_type = True
|
||||
|
||||
TIMEOUT = 60 # 1 minute
|
||||
|
||||
@@ -73,9 +78,9 @@ def ansi(code):
|
||||
return lambda s: "\x1b[{}m{}\x1b[0m".format(code, s)
|
||||
|
||||
|
||||
def wait_for_string(s):
|
||||
def wait_for_string(s, timeout=TIMEOUT):
|
||||
logging.debug("Waiting for string: {}".format(s))
|
||||
deadline = time.time() + TIMEOUT
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
output = capture_pane()
|
||||
if s in output:
|
||||
@@ -91,13 +96,17 @@ def wait_for_prompt():
|
||||
output = capture_pane()
|
||||
# If we are not at the bottom of the screen, there will be a bunch of extra \n's
|
||||
output = output.rstrip('\n')
|
||||
if output[-2:] == "\n$":
|
||||
if output.endswith("\n$"):
|
||||
return
|
||||
if output.endswith("\n/ #"):
|
||||
return
|
||||
time.sleep(1)
|
||||
raise Exception("Timed out while waiting for prompt!")
|
||||
|
||||
|
||||
def check_exit_status():
|
||||
if not verify_status:
|
||||
return
|
||||
token = uuid.uuid4().hex
|
||||
data = "echo {} $?\n".format(token)
|
||||
logging.debug("Sending {!r} to get exit status.".format(data))
|
||||
@@ -117,6 +126,23 @@ def check_exit_status():
|
||||
# Otherwise just return peacefully.
|
||||
|
||||
|
||||
def setup_tmux_and_ssh():
|
||||
if subprocess.call(["tmux", "has-session"]):
|
||||
logging.info("Couldn't connect to tmux. A new tmux session will be created.")
|
||||
subprocess.check_call(["tmux", "new-session", "-d"])
|
||||
wait_for_string("$")
|
||||
send_keys("cd ../prepare-vms\n")
|
||||
send_keys("ssh docker@$(head -n1 ips.txt)\n")
|
||||
wait_for_string("password:")
|
||||
send_keys("training\n")
|
||||
wait_for_prompt()
|
||||
else:
|
||||
logging.info("Found tmux session. Trying to acquire shell prompt.")
|
||||
wait_for_prompt()
|
||||
logging.info("Successfully connected to test cluster in tmux session.")
|
||||
|
||||
|
||||
|
||||
slides = []
|
||||
content = open(sys.argv[1]).read()
|
||||
for slide in re.split("\n---?\n", content):
|
||||
@@ -137,12 +163,22 @@ for slide in slides:
|
||||
|
||||
|
||||
def send_keys(data):
|
||||
subprocess.check_call(["tmux", "send-keys", data])
|
||||
if simulate_type and data[0] != '^':
|
||||
for key in data:
|
||||
if key == ";":
|
||||
key = "\\;"
|
||||
subprocess.check_call(["tmux", "send-keys", key])
|
||||
time.sleep(0.1*random.random())
|
||||
else:
|
||||
subprocess.check_call(["tmux", "send-keys", data])
|
||||
|
||||
def capture_pane():
|
||||
return subprocess.check_output(["tmux", "capture-pane", "-p"])
|
||||
|
||||
|
||||
setup_tmux_and_ssh()
|
||||
|
||||
|
||||
try:
|
||||
i = int(open("nextstep").read())
|
||||
logging.info("Loaded next step ({}) from file.".format(i))
|
||||
@@ -150,8 +186,6 @@ except Exception as e:
|
||||
logging.warning("Could not read nextstep file ({}), initializing to 0.".format(e))
|
||||
i = 0
|
||||
|
||||
interactive = True
|
||||
|
||||
while i < len(actions):
|
||||
with open("nextstep", "w") as f:
|
||||
f.write(str(i))
|
||||
@@ -165,10 +199,14 @@ while i < len(actions):
|
||||
print(hrule())
|
||||
if interactive:
|
||||
print("[{}/{}] Shall we execute that snippet above?".format(i, len(actions)))
|
||||
print("(ENTER to execute, 'c' to continue until next error, N to jump to step #N)")
|
||||
command = raw_input("> ")
|
||||
print("y/⏎/→ Execute snippet")
|
||||
print("s Skip snippet")
|
||||
print("g Go to a specific snippet")
|
||||
print("q Quit")
|
||||
print("c Continue non-interactively until next error")
|
||||
command = click.getchar()
|
||||
else:
|
||||
command = ""
|
||||
command = "y"
|
||||
|
||||
# For now, remove the `highlighted` sections
|
||||
# (Make sure to use $() in shell snippets!)
|
||||
@@ -176,12 +214,16 @@ while i < len(actions):
|
||||
logging.info("Stripping ` from snippet.")
|
||||
data = data.replace('`', '')
|
||||
|
||||
if command == "c":
|
||||
if command == "s":
|
||||
i += 1
|
||||
elif command == "g":
|
||||
i = click.prompt("Enter snippet number", type=int)
|
||||
elif command == "q":
|
||||
break
|
||||
elif command == "c":
|
||||
# continue until next timeout
|
||||
interactive = False
|
||||
elif command.isdigit():
|
||||
i = int(command)
|
||||
elif command == "":
|
||||
elif command in ("y", "\r", " ", "\x1b[C"):
|
||||
logging.info("Running with method {}: {}".format(method, data))
|
||||
if method == "keys":
|
||||
send_keys(data)
|
||||
@@ -199,6 +241,8 @@ while i < len(actions):
|
||||
_, _, next_method, next_data = actions[i+1]
|
||||
if next_method == "wait":
|
||||
wait_for_string(next_data)
|
||||
elif next_method == "longwait":
|
||||
wait_for_string(next_data, 10*TIMEOUT)
|
||||
else:
|
||||
wait_for_prompt()
|
||||
# Verify return code FIXME should be optional
|
||||
@@ -216,13 +260,19 @@ while i < len(actions):
|
||||
# FIXME: we should factor out the "bash" method
|
||||
wait_for_prompt()
|
||||
check_exit_status()
|
||||
elif method == "open":
|
||||
# Cheap way to get node1's IP address
|
||||
screen = capture_pane()
|
||||
ipaddr = re.findall("^\[(.*)\]", screen, re.MULTILINE)[-1]
|
||||
url = data.replace("/node1", "/{}".format(ipaddr))
|
||||
# This should probably be adapted to run on different OS
|
||||
subprocess.check_call(["open", url])
|
||||
else:
|
||||
logging.warning("Unknown method {}: {!r}".format(method, data))
|
||||
i += 1
|
||||
|
||||
else:
|
||||
i += 1
|
||||
logging.warning("Unknown command {}, skipping to next step.".format(command))
|
||||
logging.warning("Unknown command {}.".format(command))
|
||||
|
||||
# Reset slide counter
|
||||
with open("nextstep", "w") as f:
|
||||
@@ -2,11 +2,16 @@
|
||||
case "$1" in
|
||||
once)
|
||||
for YAML in *.yml; do
|
||||
./markmaker.py < $YAML > $YAML.html || {
|
||||
./markmaker.py $YAML > $YAML.html || {
|
||||
rm $YAML.html
|
||||
break
|
||||
}
|
||||
done
|
||||
if [ -n "$SLIDECHECKER" ]; then
|
||||
for YAML in *.yml; do
|
||||
./appendcheck.py $YAML.html
|
||||
done
|
||||
fi
|
||||
;;
|
||||
|
||||
forever)
|
||||
|
||||
63
slides/common/declarative.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Declarative vs imperative
|
||||
|
||||
- Our container orchestrator puts a very strong emphasis on being *declarative*
|
||||
|
||||
- Declarative:
|
||||
|
||||
*I would like a cup of tea.*
|
||||
|
||||
- Imperative:
|
||||
|
||||
*Boil some water. Pour it in a teapot. Add tea leaves. Steep for a while. Serve in cup.*
|
||||
|
||||
--
|
||||
|
||||
- Declarative seems simpler at first ...
|
||||
|
||||
--
|
||||
|
||||
- ... As long as you know how to brew tea
|
||||
|
||||
---
|
||||
|
||||
## Declarative vs imperative
|
||||
|
||||
- What declarative would really be:
|
||||
|
||||
*I want a cup of tea, obtained by pouring an infusion¹ of tea leaves in a cup.*
|
||||
|
||||
--
|
||||
|
||||
*¹An infusion is obtained by letting the object steep a few minutes in hot² water.*
|
||||
|
||||
--
|
||||
|
||||
*²Hot liquid is obtained by pouring it in an appropriate container³ and setting it on a stove.*
|
||||
|
||||
--
|
||||
|
||||
*³Ah, finally, containers! Something we know about. Let's get to work, shall we?*
|
||||
|
||||
--
|
||||
|
||||
.footnote[Did you know there was an [ISO standard](https://en.wikipedia.org/wiki/ISO_3103)
|
||||
specifying how to brew tea?]
|
||||
|
||||
---
|
||||
|
||||
## Declarative vs imperative
|
||||
|
||||
- Imperative systems:
|
||||
|
||||
- simpler
|
||||
|
||||
- if a task is interrupted, we have to restart from scratch
|
||||
|
||||
- Declarative systems:
|
||||
|
||||
- if a task is interrupted (or if we show up to the party half-way through),
|
||||
we can figure out what's missing and do only what's necessary
|
||||
|
||||
- we need to be able to *observe* the system
|
||||
|
||||
- ... and compute a "diff" between *what we have* and *what we want*
|
||||
@@ -27,15 +27,32 @@ class: self-paced
|
||||
- Likewise, it will take more than merely *reading* these slides
|
||||
to make you an expert
|
||||
|
||||
- These slides include *tons* of exercises
|
||||
- These slides include *tons* of exercises and examples
|
||||
|
||||
- They assume that you have access to a cluster of Docker nodes
|
||||
- They assume that you have access to some Docker nodes
|
||||
|
||||
- If you are attending a workshop or tutorial:
|
||||
<br/>you will be given specific instructions to access your cluster
|
||||
|
||||
- If you are doing this on your own:
|
||||
<br/>you can use
|
||||
[Play-With-Docker](http://www.play-with-docker.com/) and
|
||||
read [these instructions](https://github.com/jpetazzo/orchestration-workshop#using-play-with-docker) for extra
|
||||
details
|
||||
<br/>the first chapter will give you various options like
|
||||
[Play-With-Docker](http://www.play-with-docker.com/)
|
||||
to get your own cluster
|
||||
|
||||
---
|
||||
|
||||
## About these slides
|
||||
|
||||
- All the content is available in a public GitHub repository:
|
||||
|
||||
https://github.com/jpetazzo/container.training
|
||||
|
||||
- You can get updated "builds" of the slides there:
|
||||
|
||||
http://container.training/
|
||||
|
||||
- Typos? Mistakes? Questions? Feel free to hover over the bottom of the slide ...
|
||||
|
||||
--
|
||||
|
||||
.footnote[.emoji[👇] Try it! The source file will be shown and you can view it on GitHub and fork and edit it.]
|
||||
@@ -1,37 +1,22 @@
|
||||
# Pre-requirements
|
||||
|
||||
- Computer with internet connection and a web browser
|
||||
- Be comfortable with the UNIX command line
|
||||
|
||||
- For instructor-led workshops: an SSH client to connect to remote machines
|
||||
- navigating directories
|
||||
|
||||
- on Linux, OS X, FreeBSD... you are probably all set
|
||||
- editing files
|
||||
|
||||
- on Windows, get [putty](http://www.putty.org/),
|
||||
Microsoft [Win32 OpenSSH](https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH),
|
||||
[Git BASH](https://git-for-windows.github.io/), or
|
||||
[MobaXterm](http://mobaxterm.mobatek.net/)
|
||||
- a little bit of bash-fu (environment variables, loops)
|
||||
|
||||
- A tiny little bit of Docker knowledge
|
||||
- Some Docker knowledge
|
||||
|
||||
(that's totally OK if you're not a Docker expert!)
|
||||
- `docker run`, `docker ps`, `docker build`
|
||||
|
||||
---
|
||||
- ideally, you know how to write a Dockerfile and build it
|
||||
<br/>
|
||||
(even if it's a `FROM` line and a couple of `RUN` commands)
|
||||
|
||||
class: in-person, extra-details
|
||||
|
||||
## Nice-to-haves
|
||||
|
||||
- [Mosh](https://mosh.org/) instead of SSH, if your internet connection tends to lose packets
|
||||
<br/>(available with `(apt|yum|brew) install mosh`; then connect with `mosh user@host`)
|
||||
|
||||
- [GitHub](https://github.com/join) account
|
||||
<br/>(if you want to fork the repo)
|
||||
|
||||
- [Slack](https://community.docker.com/registrations/groups/4316) account
|
||||
<br/>(to join the conversation after the workshop)
|
||||
|
||||
- [Docker Hub](https://hub.docker.com) account
|
||||
<br/>(it's one way to distribute images on your cluster)
|
||||
- It's totally OK if you are not a Docker expert!
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +38,7 @@ class: extra-details
|
||||
|
||||
- The whole workshop is hands-on
|
||||
|
||||
- We will see Docker and Kubernetes in action
|
||||
- We are going to build, ship, and run containers!
|
||||
|
||||
- You are invited to reproduce all the demos
|
||||
|
||||
@@ -62,25 +47,25 @@ class: extra-details
|
||||
.exercise[
|
||||
|
||||
- This is the stuff you're supposed to do!
|
||||
- Go to [container.training](http://container.training/) to view these slides
|
||||
- Join the chat room on @@CHAT@@
|
||||
|
||||
<!-- ```open http://container.training/``` -->
|
||||
- Go to [container.training](http://container.training/) to view these slides
|
||||
|
||||
- Join the chat room on @@CHAT@@
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
class: pic, in-person
|
||||
class: in-person
|
||||
|
||||
## Where are we going to run our containers?
|
||||
|
||||
---
|
||||
|
||||
class: in-person, pic
|
||||
|
||||

|
||||
|
||||
<!--
|
||||
```bash
|
||||
kubectl get all -o name | grep -v services/kubernetes | xargs -n1 kubectl delete
|
||||
```
|
||||
-->
|
||||
|
||||
---
|
||||
|
||||
class: in-person
|
||||
@@ -88,22 +73,82 @@ class: in-person
|
||||
## You get five VMs
|
||||
|
||||
- Each person gets 5 private VMs (not shared with anybody else)
|
||||
- Kubernetes has been deployed and pre-configured on these machines
|
||||
- They'll remain up until the day after the tutorial
|
||||
|
||||
- They'll remain up for the duration of the workshop
|
||||
|
||||
- You should have a little card with login+password+IP addresses
|
||||
|
||||
- You can automatically SSH from one VM to another
|
||||
|
||||
- The nodes have aliases: `node1`, `node2`, etc.
|
||||
|
||||
---
|
||||
|
||||
class: in-person
|
||||
|
||||
## Why don't we run containers locally?
|
||||
|
||||
- Installing that stuff can be hard on some machines
|
||||
|
||||
(32 bits CPU or OS... Laptops without administrator access... etc.)
|
||||
|
||||
- *"The whole team downloaded all these container images from the WiFi!
|
||||
<br/>... and it went great!"* (Literally no-one ever)
|
||||
|
||||
- All you need is a computer (or even a phone or tablet!), with:
|
||||
|
||||
- an internet connection
|
||||
|
||||
- a web browser
|
||||
|
||||
- an SSH client
|
||||
|
||||
---
|
||||
|
||||
class: in-person
|
||||
|
||||
## SSH clients
|
||||
|
||||
- On Linux, OS X, FreeBSD... you are probably all set
|
||||
|
||||
- On Windows, get one of these:
|
||||
|
||||
- [putty](http://www.putty.org/)
|
||||
- Microsoft [Win32 OpenSSH](https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH)
|
||||
- [Git BASH](https://git-for-windows.github.io/)
|
||||
- [MobaXterm](http://mobaxterm.mobatek.net/)
|
||||
|
||||
- On Android, [JuiceSSH](https://juicessh.com/)
|
||||
([Play Store](https://play.google.com/store/apps/details?id=com.sonelli.juicessh))
|
||||
works pretty well
|
||||
|
||||
- Nice-to-have: [Mosh](https://mosh.org/) instead of SSH, if your internet connection tends to lose packets
|
||||
<br/>(available with `(apt|yum|brew) install mosh`; then connect with `mosh user@host`)
|
||||
|
||||
---
|
||||
|
||||
class: in-person
|
||||
|
||||
## Connecting to our lab environment
|
||||
|
||||
.exercise[
|
||||
|
||||
- Log into the first VM (`node1`) with SSH or MOSH
|
||||
|
||||
<!--
|
||||
```bash
|
||||
for N in $(seq 1 5); do
|
||||
ssh -o StrictHostKeyChecking=no node$N true
|
||||
done
|
||||
```
|
||||
|
||||
```bash
|
||||
if which kubectl; then
|
||||
kubectl get all -o name | grep -v services/kubernetes | xargs -n1 kubectl delete
|
||||
fi
|
||||
```
|
||||
-->
|
||||
|
||||
- Log into the first VM (`node1`) with SSH or MOSH
|
||||
- Check that you can SSH (without password) to `node2`:
|
||||
```bash
|
||||
ssh node2
|
||||
@@ -114,10 +159,34 @@ done
|
||||
|
||||
]
|
||||
|
||||
If anything goes wrong — ask for help!
|
||||
|
||||
---
|
||||
|
||||
## Doing or re-doing the workshop on your own?
|
||||
|
||||
- Use something like
|
||||
[Play-With-Docker](http://play-with-docker.com/) or
|
||||
[Play-With-Kubernetes](https://medium.com/@marcosnils/introducing-pwk-play-with-k8s-159fcfeb787b)
|
||||
|
||||
Zero setup effort; but environment are short-lived and
|
||||
might have limited resources
|
||||
|
||||
- Create your own cluster (local or cloud VMs)
|
||||
|
||||
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))
|
||||
|
||||
Bigger setup effort; ideal for group training
|
||||
|
||||
---
|
||||
|
||||
## We will (mostly) interact with node1 only
|
||||
|
||||
*These remarks apply only when using multiple nodes, of course.*
|
||||
|
||||
- Unless instructed, **all commands must be run from the first VM, `node1`**
|
||||
|
||||
- We will only checkout/copy the code on `node1`
|
||||
19
slides/common/pwd.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Using Play-With-Docker
|
||||
|
||||
- Open a new browser tab to [www.play-with-docker.com](http://www.play-with-docker.com/)
|
||||
|
||||
- Confirm that you're not a robot
|
||||
|
||||
- Click on "ADD NEW INSTANCE": congratulations, you have your first Docker node!
|
||||
|
||||
- When you will need more nodes, just click on "ADD NEW INSTANCE" again
|
||||
|
||||
- Note the countdown in the corner; when it expires, your instances are destroyed
|
||||
|
||||
- If you give your URL to somebody else, they can access your nodes too
|
||||
<br/>
|
||||
(You can use that for pair programming, or to get help from a mentor)
|
||||
|
||||
- Loving it? Not loving it? Tell it to the wonderful authors,
|
||||
[@marcosnils](https://twitter.com/marcosnils) &
|
||||
[@xetorthio](https://twitter.com/xetorthio)!
|
||||
@@ -1,16 +1,16 @@
|
||||
# Our sample application
|
||||
|
||||
- Visit the GitHub repository with all the materials of this workshop:
|
||||
<br/>https://github.com/jpetazzo/orchestration-workshop
|
||||
<br/>https://github.com/jpetazzo/container.training
|
||||
|
||||
- The application is in the [dockercoins](
|
||||
https://github.com/jpetazzo/orchestration-workshop/tree/master/dockercoins)
|
||||
https://github.com/jpetazzo/container.training/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/orchestration-workshop/blob/master/dockercoins/docker-compose.yml) ...
|
||||
https://github.com/jpetazzo/container.training/blob/master/dockercoins/docker-compose.yml) ...
|
||||
|
||||
... and 4 other services, each in its own directory:
|
||||
|
||||
@@ -59,25 +59,32 @@ class: extra-details
|
||||
|
||||
## Example in `worker/worker.py`
|
||||
|
||||

|
||||
```python
|
||||
redis = Redis("`redis`")
|
||||
|
||||
|
||||
def get_random_bytes():
|
||||
r = requests.get("http://`rng`/32")
|
||||
return r.content
|
||||
|
||||
|
||||
def hash_bytes(data):
|
||||
r = requests.post("http://`hasher`/",
|
||||
data=data,
|
||||
headers={"Content-Type": "application/octet-stream"})
|
||||
```
|
||||
|
||||
(Full source code available [here](
|
||||
https://github.com/jpetazzo/container.training/blob/8279a3bce9398f7c1a53bdd95187c53eda4e6435/dockercoins/worker/worker.py#L17
|
||||
))
|
||||
|
||||
---
|
||||
|
||||
## What's this application?
|
||||
|
||||
---
|
||||
--
|
||||
|
||||
class: pic
|
||||
|
||||

|
||||
|
||||
(DockerCoins 2016 logo courtesy of [@XtlCnslt](https://twitter.com/xtlcnslt) and [@ndeloof](https://twitter.com/ndeloof). Thanks!)
|
||||
|
||||
---
|
||||
|
||||
## What's this application?
|
||||
|
||||
- It is a DockerCoin miner! 💰🐳📦🚢
|
||||
- It is a DockerCoin miner! .emoji[💰🐳📦🚢]
|
||||
|
||||
--
|
||||
|
||||
@@ -109,15 +116,15 @@ class: pic
|
||||
|
||||
<!--
|
||||
```bash
|
||||
if [ -d orchestration-workshop ]; then
|
||||
mv orchestration-workshop orchestration-workshop.$$
|
||||
if [ -d container.training ]; then
|
||||
mv container.training container.training.$$
|
||||
fi
|
||||
```
|
||||
-->
|
||||
|
||||
- Clone the repository on `node1`:
|
||||
```bash
|
||||
git clone git://github.com/jpetazzo/orchestration-workshop
|
||||
git clone git://github.com/jpetazzo/container.training
|
||||
```
|
||||
|
||||
]
|
||||
@@ -134,7 +141,7 @@ Without further ado, let's start our application.
|
||||
|
||||
- Go to the `dockercoins` directory, in the cloned repo:
|
||||
```bash
|
||||
cd ~/orchestration-workshop/dockercoins
|
||||
cd ~/container.training/dockercoins
|
||||
```
|
||||
|
||||
- Use Compose to build and run all containers:
|
||||
@@ -143,7 +150,7 @@ Without further ado, let's start our application.
|
||||
```
|
||||
|
||||
<!--
|
||||
```wait units of work done```
|
||||
```longwait units of work done```
|
||||
```keys ^C```
|
||||
-->
|
||||
|
||||
@@ -263,11 +270,31 @@ class: extra-details
|
||||
|
||||
]
|
||||
|
||||
You should see a speed of approximately 4 hashes/second.
|
||||
A drawing area should show up, and after a few seconds, a blue
|
||||
graph will appear.
|
||||
|
||||
More precisely: 4 hashes/second, with regular dips down to zero.
|
||||
<br/>This is because Jérôme is incapable of writing good frontend code.
|
||||
<br/>Don't ask. Seriously, don't ask. This is embarrassing.
|
||||
---
|
||||
|
||||
class: self-paced, extra-details
|
||||
|
||||
## If the graph doesn't load
|
||||
|
||||
If you just see a `Page not found` error, it might be because your
|
||||
Docker Engine is running on a different machine. This can be the case if:
|
||||
|
||||
- you are using the Docker Toolbox
|
||||
|
||||
- you are using a VM (local or remote) created with Docker Machine
|
||||
|
||||
- you are controlling a remote Docker Engine
|
||||
|
||||
When you run DockerCoins in development mode, the web UI static files
|
||||
are mapped to the container using a volume. Alas, volumes can only
|
||||
work on a local environment, or when using Docker4Mac or Docker4Windows.
|
||||
|
||||
How to fix this?
|
||||
|
||||
Edit `dockercoins.yml` and comment out the `volumes` section, and try again.
|
||||
|
||||
---
|
||||
|
||||
@@ -275,19 +302,43 @@ class: extra-details
|
||||
|
||||
## Why does the speed seem irregular?
|
||||
|
||||
- It *looks like* the speed is approximately 4 hashes/second
|
||||
|
||||
- Or more precisely: 4 hashes/second, with regular dips down to zero
|
||||
|
||||
- Why?
|
||||
|
||||
--
|
||||
|
||||
class: extra-details
|
||||
|
||||
- The app actually has a constant, steady speed: 3.33 hashes/second
|
||||
<br/>
|
||||
(which corresponds to 1 hash every 0.3 seconds, for *reasons*)
|
||||
|
||||
- Yes, and?
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## The reason why this graph is *not awesome*
|
||||
|
||||
- The worker doesn't update the counter after every loop, but up to once per second
|
||||
|
||||
- The speed is computed by the browser, checking the counter about once per second
|
||||
|
||||
- Between two consecutive updates, the counter will increase either by 4, or by 0
|
||||
|
||||
- The perceived speed will therefore be 4 - 4 - 4 - 0 - 4 - 4 - etc.
|
||||
- The perceived speed will therefore be 4 - 4 - 4 - 0 - 4 - 4 - 0 etc.
|
||||
|
||||
*We told you to not ask!!!*
|
||||
- What can we conclude from this?
|
||||
|
||||
--
|
||||
|
||||
class: extra-details
|
||||
|
||||
- Jérôme is clearly incapable of writing good frontend code
|
||||
|
||||
---
|
||||
|
||||
|
||||
26
slides/common/thankyou.md
Normal file
@@ -0,0 +1,26 @@
|
||||
class: title, self-paced
|
||||
|
||||
Thank you!
|
||||
|
||||
---
|
||||
|
||||
class: title, in-person
|
||||
|
||||
That's all folks! <br/> Questions?
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Links and resources
|
||||
|
||||
- [Docker Community Slack](https://community.docker.com/registrations/groups/4316)
|
||||
- [Docker Community Forums](https://forums.docker.com/)
|
||||
- [Docker Hub](https://hub.docker.com)
|
||||
- [Docker Blog](http://blog.docker.com/)
|
||||
- [Docker documentation](http://docs.docker.com/)
|
||||
- [Docker on StackOverflow](https://stackoverflow.com/questions/tagged/docker)
|
||||
- [Docker on Twitter](http://twitter.com/docker)
|
||||
- [Play With Docker Hands-On Labs](http://training.play-with-docker.com/)
|
||||
|
||||
.footnote[These slides (and future updates) are on → http://container.training/]
|
||||
21
slides/common/title.md
Normal file
@@ -0,0 +1,21 @@
|
||||
class: title, self-paced
|
||||
|
||||
@@TITLE@@
|
||||
|
||||
.nav[*Self-paced version*]
|
||||
|
||||
---
|
||||
|
||||
class: title, in-person
|
||||
|
||||
@@TITLE@@<br/></br>
|
||||
|
||||
.footnote[
|
||||
**Be kind to the WiFi!**<br/>
|
||||
<!-- *Use the 5G network.* -->
|
||||
*Don't use your hotspot.*<br/>
|
||||
*Don't stream videos or download big files during the workshop.*<br/>
|
||||
*Thank you!*
|
||||
|
||||
**Slides: http://container.training/**
|
||||
]
|
||||
4
slides/common/toc.md
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
@@TOC@@
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7ET1GY4Q)"
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
- snap
|
||||
- auto-btp
|
||||
- benchmarking
|
||||
- elk-manual
|
||||
- prom-manual
|
||||
|
||||
title: "Swarm: from Zero to Hero (DC17EU)"
|
||||
chapters:
|
||||
- |
|
||||
class: title
|
||||
|
||||
.small[
|
||||
|
||||
Swarm: from Zero to Hero
|
||||
|
||||
.small[.small[
|
||||
|
||||
**Be kind to the WiFi!**
|
||||
|
||||
*Use the 5G network*
|
||||
<br/>
|
||||
*Don't use your hotspot*
|
||||
<br/>
|
||||
*Don't stream videos from YouTube, Netflix, etc.
|
||||
<br/>(if you're bored, watch local content instead)*
|
||||
|
||||
Also: share the power outlets
|
||||
<br/>
|
||||
*(with limited power comes limited responsibility?)*
|
||||
<br/>
|
||||
*(or something?)*
|
||||
|
||||
Thank you!
|
||||
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Intros
|
||||
|
||||
<!--
|
||||
- Hello! We are
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake))
|
||||
&
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo))
|
||||
-->
|
||||
|
||||
- Hello! We are Jérôme, Lee, Nicholas, and Scott
|
||||
|
||||
<!--
|
||||
I am
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo))
|
||||
-->
|
||||
|
||||
--
|
||||
|
||||
- This is our collective Docker knowledge:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## "From zero to hero"
|
||||
|
||||
--
|
||||
|
||||
- It rhymes, but it's a pretty bad title, to be honest
|
||||
|
||||
--
|
||||
|
||||
- None of you is a "zero"
|
||||
|
||||
--
|
||||
|
||||
- None of us is a "hero"
|
||||
|
||||
--
|
||||
|
||||
- None of us should even try to be a hero
|
||||
|
||||
--
|
||||
|
||||
*The hero syndrome is a phenomenon affecting people who seek heroism or recognition,
|
||||
usually by creating a desperate situation which they can resolve.
|
||||
This can include unlawful acts, such as arson.
|
||||
The phenomenon has been noted to affect civil servants,
|
||||
such as firefighters, nurses, police officers, and security guards.*
|
||||
|
||||
(Wikipedia page on [hero syndrome](https://en.wikipedia.org/wiki/Hero_syndrome))
|
||||
|
||||
---
|
||||
|
||||
## Agenda
|
||||
|
||||
.small[
|
||||
- 09:00-09:10 Hello!
|
||||
- 09:10-10:30 Part 1
|
||||
- 10:30-11:00 coffee break
|
||||
- 11:00-12:30 Part 2
|
||||
- 12:30-13:30 lunch break
|
||||
- 13:30-15:00 Part 3
|
||||
- 15:00-15:30 coffee break
|
||||
- 15:30-17:00 Part 4
|
||||
- 17:00-18:00 Afterhours and Q&A
|
||||
]
|
||||
|
||||
<!--
|
||||
- The tutorial will run from 9:00am to 12:20pm
|
||||
|
||||
- This will be fast-paced, but DON'T PANIC!
|
||||
|
||||
- There will be a coffee break at 10:30am
|
||||
<br/>
|
||||
(please remind me if I forget about it!)
|
||||
-->
|
||||
|
||||
- All the content is publicly available (slides, code samples, scripts)
|
||||
|
||||
Upstream URL: https://github.com/jpetazzo/orchestration-workshop
|
||||
|
||||
- Feel free to interrupt for questions at any time
|
||||
|
||||
- Live feedback, questions, help on [Gitter](chat)
|
||||
|
||||
http://container.training/chat
|
||||
|
||||
- swarm/intro.md
|
||||
- |
|
||||
@@TOC@@
|
||||
- - swarm/prereqs.md
|
||||
- swarm/versions.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
All right!
|
||||
<br/>
|
||||
We're all set.
|
||||
<br/>
|
||||
Let's do this.
|
||||
- common/sampleapp.md
|
||||
- swarm/swarmkit.md
|
||||
- swarm/creatingswarm.md
|
||||
- swarm/morenodes.md
|
||||
- - swarm/firstservice.md
|
||||
- swarm/ourapponswarm.md
|
||||
- swarm/updatingservices.md
|
||||
- swarm/healthchecks.md
|
||||
- - swarm/operatingswarm.md
|
||||
- swarm/netshoot.md
|
||||
- swarm/ipsec.md
|
||||
- swarm/swarmtools.md
|
||||
- swarm/security.md
|
||||
- swarm/secrets.md
|
||||
- swarm/encryptionatrest.md
|
||||
- swarm/leastprivilege.md
|
||||
- swarm/apiscope.md
|
||||
- - swarm/logging.md
|
||||
- swarm/metrics.md
|
||||
- swarm/stateful.md
|
||||
- swarm/extratips.md
|
||||
- swarm/end.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
That's all folks! <br/> Questions?
|
||||
|
||||
.small[.small[
|
||||
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo)) — [@docker](https://twitter.com/docker)
|
||||
|
||||
]]
|
||||
|
||||
<!--
|
||||
Tiffany ([@tiffanyfayj](https://twitter.com/tiffanyfayj))
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake))
|
||||
-->
|
||||
2
slides/find-non-ascii.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
grep --color=auto -P -n "[^\x00-\x80]" */*.md
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 64 KiB |
BIN
slides/images/container-background.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 927 KiB After Width: | Height: | Size: 927 KiB |
BIN
slides/images/title-building-docker-images-with-a-dockerfile.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 595 KiB After Width: | Height: | Size: 595 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
@@ -1,29 +1,152 @@
|
||||
<ul style="font-size:2em;">
|
||||
<li>
|
||||
LISA17 M7: Getting Started with Docker and Containers
|
||||
<a href="intro.yml.html">(slides)</a>
|
||||
<a href="https://gitter.im/jpetazzo/workshop-20171030-sanfrancisco">(chat)</a>
|
||||
</li>
|
||||
<li>
|
||||
LISA17 T9: Build, Ship, and Run Microservices on a Docker Swarm Cluster
|
||||
<a href="lisa.yml.html">(slides)</a>
|
||||
<a href="https://gitter.im/jpetazzo/workshop-20171031-sanfrancisco">(chat)</a>
|
||||
</li>
|
||||
<li>
|
||||
Deploying and scaling microservices with Docker and Kubernetes
|
||||
<a href="kube.yml.html">(slides)</a>
|
||||
<a href="https://gitter.im/jpetazzo/workshop-20171026-prague">(chat)</a>
|
||||
</li>
|
||||
<li>
|
||||
DockerCon Workshop: from Zero to Hero (full day, B3 M1-2)
|
||||
<a href="dockercon.yml.html">(slides)</a>
|
||||
</li>
|
||||
<li>
|
||||
DockerCon Workshop: Orchestration for Advanced Users (afternoon, B4 M5-6)
|
||||
<a href="https://www.bretfisher.com/dockercon17eu/">(slides)</a>
|
||||
</li>
|
||||
<li>
|
||||
Self-paced Workshop: Orchestrating Microservices with Docker and Swarm
|
||||
<a href="selfpaced.yml.html">(slides)</a>
|
||||
</li>
|
||||
</ul>
|
||||
<html>
|
||||
<head>
|
||||
<title>Container Training</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-image: url("images/container-background.jpg");
|
||||
max-width: 1024px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
table {
|
||||
font-size: 20px;
|
||||
font-family: sans-serif;
|
||||
background: white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
font-size: 300%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.title {
|
||||
font-size: 150%;
|
||||
font-weight: bold;
|
||||
}
|
||||
td {
|
||||
padding: 1px;
|
||||
height: 1em;
|
||||
}
|
||||
td.spacer {
|
||||
height: unset;
|
||||
}
|
||||
td.footer {
|
||||
padding-top: 80px;
|
||||
height: 100px;
|
||||
}
|
||||
td.title {
|
||||
border-bottom: thick solid black;
|
||||
padding-bottom: 2px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
background: yellow;
|
||||
}
|
||||
a.attend:after {
|
||||
content: "📅 attend";
|
||||
}
|
||||
a.slides:after {
|
||||
content: "📚 slides";
|
||||
}
|
||||
a.chat:after {
|
||||
content: "💬 chat";
|
||||
}
|
||||
a.video:after {
|
||||
content: "📺 video";
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<table>
|
||||
<tr><td class="header" colspan="4">Container Training</td></tr>
|
||||
|
||||
<tr><td class="title" colspan="4">Coming soon at a conference near you</td></tr>
|
||||
|
||||
<!--
|
||||
<td><a class="chat" href="https://gitter.im/jpetazzo/workshop-20171026-prague" /></td>
|
||||
-->
|
||||
|
||||
<tr>
|
||||
<td>QCON SF: Introduction to Docker and Containers</td>
|
||||
<td><a class="slides" href="http://qconsf2017intro.container.training/" /></td>
|
||||
<td><a class="attend" href="https://qconsf.com/sf2017/workshop/introduction-docker-and-containers" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>QCON SF: Orchestrating Microservices with Docker Swarm</td>
|
||||
<td><a class="slides" href="http://qconsf2017swarm.container.training/" /></td>
|
||||
<td><a class="attend" href="https://qconsf.com/sf2017/workshop/orchestrating-microservices-docker-swarm" /></td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="title" colspan="4">Past workshops</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>LISA17 M7: Getting Started with Docker and Containers</td>
|
||||
<td><a class="slides" href="http://lisa17m7.container.training/" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>LISA17 T9: Build, Ship, and Run Microservices on a Docker Swarm Cluster</td>
|
||||
<td><a class="slides" href="http://lisa17t9.container.training/" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Deploying and scaling microservices with Docker and Kubernetes</td>
|
||||
<td><a class="slides" href="http://osseu17.container.training/" /></td>
|
||||
<td><a class="video" href="https://www.youtube.com/playlist?list=PLBAFXs0YjviLrsyydCzxWrIP_1-wkcSHS" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>DockerCon Workshop: from Zero to Hero (full day, B3 M1-2)</td>
|
||||
<td><a class="slides" href="http://dc17eu.container.training/" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>DockerCon Workshop: Orchestration for Advanced Users (afternoon, B4 M5-6)</td>
|
||||
<td><a class="slides" href="https://www.bretfisher.com/dockercon17eu/" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>LISA16 T1: Deploying and Scaling Applications with Docker Swarm</td>
|
||||
<td><a class="slides" href="http://lisa16t1.container.training/" /></td>
|
||||
<td><a class="video" href="https://www.youtube.com/playlist?list=PLBAFXs0YjviIDDhr8vIwCN1wkyNGXjbbc" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>PyCon2016: Introduction to Docker and containers</td>
|
||||
<td><a class="slides" href="https://us.pycon.org/2016/site_media/media/tutorial_handouts/DockerSlides.pdf" /></td>
|
||||
<td><a class="video" href="https://www.youtube.com/watch?v=ZVaRK10HBjo" /></td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="title" colspan="4">Self-paced tutorials</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>Introduction to Docker and Containers</td>
|
||||
<td><a class="slides" href="intro-fullday.yml.html" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Container Orchestration with Docker and Swarm</td>
|
||||
<td><a class="slides" href="swarm-selfpaced.yml.html" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Deploying and Scaling Microservices with Docker and Kubernetes</td>
|
||||
<td><a class="slides" href="kube-halfday.yml.html" /></td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="spacer"></td></tr>
|
||||
|
||||
<tr>
|
||||
<td class="footer">
|
||||
Maintained by Jérôme Petazzoni (<a href="https://twitter.com/jpetazzo">@jpetazzo</a>)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
10
slides/interstitials.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
https://static.pexels.com/photos/163726/belgium-antwerp-shipping-container-163726.jpeg
|
||||
https://cdn.pixabay.com/photo/2017/03/12/06/18/container-2136505_1280.jpg
|
||||
http://www.publicdomainpictures.net/pictures/100000/velka/blue-containers.jpg
|
||||
https://media.defense.gov/2013/Nov/12/2000897311/-1/-1/0/131108-F-PD986-087.JPG
|
||||
https://upload.wikimedia.org/wikipedia/commons/4/4d/Locomotive_4700_with_a_container_train_at_Concordancia_de_Poceirao.jpg
|
||||
https://upload.wikimedia.org/wikipedia/commons/7/7e/ShippingContainerSFBay.jpg
|
||||
https://upload.wikimedia.org/wikipedia/commons/c/c7/Copper_%26_Kings_Distillery_Shipping_Containers.jpg
|
||||
https://cdn.pixabay.com/photo/2017/08/01/21/54/container-2568197_1280.jpg
|
||||
http://s0.geograph.org.uk/geophotos/05/04/71/5047160_cc034d65.jpg
|
||||
https://c1.staticflickr.com/2/1513/25530579783_57d6dd3d9c_b.jpg
|
||||
41
slides/intro-fullday.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
title: |
|
||||
Introduction
|
||||
to Docker and
|
||||
Containers
|
||||
|
||||
chat: "[Gitter](https://gitter.im/jpetazzo/workshop-20171116-sanfrancisco)"
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
- logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - intro/Docker_Overview.md
|
||||
#- intro/Docker_History.md
|
||||
- intro/Training_Environment.md
|
||||
- intro/Installing_Docker.md
|
||||
- intro/First_Containers.md
|
||||
- intro/Background_Containers.md
|
||||
- intro/Start_And_Attach.md
|
||||
- - intro/Initial_Images.md
|
||||
- intro/Building_Images_Interactively.md
|
||||
- intro/Building_Images_With_Dockerfiles.md
|
||||
- intro/Cmd_And_Entrypoint.md
|
||||
- intro/Copying_Files_During_Build.md
|
||||
- intro/Multi_Stage_Builds.md
|
||||
- intro/Publishing_To_Docker_Hub.md
|
||||
- intro/Dockerfile_Tips.md
|
||||
- - intro/Naming_And_Inspecting.md
|
||||
- intro/Container_Networking_Basics.md
|
||||
- intro/Network_Drivers.md
|
||||
- intro/Container_Network_Model.md
|
||||
#- intro/Connecting_Containers_With_Links.md
|
||||
- intro/Ambassadors.md
|
||||
- - intro/Local_Development_Workflow.md
|
||||
- intro/Working_With_Volumes.md
|
||||
- intro/Compose_For_Dev_Stacks.md
|
||||
- intro/Advanced_Dockerfiles.md
|
||||
- common/thankyou.md
|
||||
42
slides/intro-selfpaced.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
title: |
|
||||
Introduction
|
||||
to Docker and
|
||||
Containers
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
|
||||
|
||||
exclude:
|
||||
- in-person
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
# - common/logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - intro/Docker_Overview.md
|
||||
#- intro/Docker_History.md
|
||||
- intro/Training_Environment.md
|
||||
- intro/Installing_Docker.md
|
||||
- intro/First_Containers.md
|
||||
- intro/Background_Containers.md
|
||||
- intro/Start_And_Attach.md
|
||||
- - intro/Initial_Images.md
|
||||
- intro/Building_Images_Interactively.md
|
||||
- intro/Building_Images_With_Dockerfiles.md
|
||||
- intro/Cmd_And_Entrypoint.md
|
||||
- intro/Copying_Files_During_Build.md
|
||||
- intro/Multi_Stage_Builds.md
|
||||
- intro/Publishing_To_Docker_Hub.md
|
||||
- intro/Dockerfile_Tips.md
|
||||
- - intro/Naming_And_Inspecting.md
|
||||
- intro/Container_Networking_Basics.md
|
||||
- intro/Network_Drivers.md
|
||||
- intro/Container_Network_Model.md
|
||||
#- intro/Connecting_Containers_With_Links.md
|
||||
- intro/Ambassadors.md
|
||||
- - intro/Local_Development_Workflow.md
|
||||
- intro/Working_With_Volumes.md
|
||||
- intro/Compose_For_Dev_Stacks.md
|
||||
- intro/Advanced_Dockerfiles.md
|
||||
- common/thankyou.md
|
||||
@@ -1,91 +0,0 @@
|
||||
title: "LISA17 M7: Getting Started with Docker and Containers"
|
||||
|
||||
chat: "[Slack](https://usenix-lisa.slack.com/messages/C0E6N1NJW)"
|
||||
|
||||
chapters:
|
||||
- |
|
||||
class: title
|
||||
|
||||
.small[
|
||||
|
||||
LISA17 M7
|
||||
|
||||
Getting Started <br/> with Docker and Containers
|
||||
|
||||
.small[.small[
|
||||
|
||||
**Be kind to the WiFi!**
|
||||
|
||||
*Use the 5G network*
|
||||
<br/>
|
||||
*Don't use your hotspot*
|
||||
<br/>
|
||||
*Don't stream videos from YouTube, Netflix, etc.
|
||||
<br/>(if you're bored, watch local content instead)*
|
||||
|
||||
<!--
|
||||
Also: share the power outlets
|
||||
<br/>
|
||||
*(with limited power comes limited responsibility?)*
|
||||
<br/>
|
||||
*(or something?)*
|
||||
-->
|
||||
|
||||
Thank you!
|
||||
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Logistics
|
||||
|
||||
- Hello! We are
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo), Docker Inc.)
|
||||
&
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake), Travis CI)
|
||||
|
||||
- The tutorial will run from 1:30pm to 5:00pm
|
||||
|
||||
- This will be fast-paced, but DON'T PANIC!
|
||||
|
||||
- There will be a coffee break at 3:00pm
|
||||
<br/>
|
||||
(please remind me if I forget about it!)
|
||||
|
||||
- All the content is publicly available
|
||||
|
||||
One URL to remember: http://container.training
|
||||
|
||||
- Feel free to interrupt for questions at any time
|
||||
|
||||
- Live feedback, questions, help on @@CHAT@@
|
||||
|
||||
- |
|
||||
@@TOC@@
|
||||
- - intro/Docker_Overview.md
|
||||
#- intro/Docker_History.md
|
||||
- intro/Training_Environment.md
|
||||
- intro/Install_Docker.md
|
||||
- intro/First_Containers.md
|
||||
- intro/Background_Containers.md
|
||||
- intro/Start_And_Attach.md
|
||||
- - intro/Initial_Images.md
|
||||
- intro/Building_Images_Interactively.md
|
||||
- intro/Building_Images_With_Dockerfiles.md
|
||||
- intro/Cmd_And_Entrypoint.md
|
||||
- intro/Copying_Files_During_Build.md
|
||||
- intro/Multi_Stage_Builds.md
|
||||
- intro/Dockerfile_Tips.md
|
||||
#- intro/Advanced_Dockerfiles.md
|
||||
- intro/Docker_Hub_Tease.md
|
||||
- - intro/Naming_And_Inspecting.md
|
||||
- intro/Container_Networking_Basics.md
|
||||
- intro/Container_Network_Model.md
|
||||
#- intro/Connecting_Containers_With_Links.md
|
||||
- intro/Ambassadors.md
|
||||
- - intro/Local_Development_Workflow.md
|
||||
- intro/Working_With_Volumes.md
|
||||
- intro/Compose_For_Dev_Stacks.md
|
||||
- intro/Course_Conclusion.md
|
||||
@@ -1,9 +1,6 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Advanced Dockerfiles
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class: title
|
||||
|
||||
# Ambassadors
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -40,7 +40,7 @@ ambassador containers.
|
||||
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Background Containers
|
||||
# Background containers
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Building Images Interactively
|
||||
# Building images interactively
|
||||
|
||||
In this section, we will create our first container image.
|
||||
|
||||
@@ -16,27 +16,21 @@ We will:
|
||||
|
||||
---
|
||||
|
||||
## Building Images Interactively
|
||||
## The plan
|
||||
|
||||
As we have seen, the images on the Docker Hub are sometimes very basic.
|
||||
1. Create a container (with `docker run`) using our base distro of choice.
|
||||
|
||||
How do we want to construct our own images?
|
||||
2. Run a bunch of commands to install and set up our software in the container.
|
||||
|
||||
As an example, we will build an image that has `figlet`.
|
||||
3. (Optionally) review changes in the container with `docker diff`.
|
||||
|
||||
First, we will do it manually with `docker commit`.
|
||||
4. Turn the container into a new image with `docker commit`.
|
||||
|
||||
Then, in an upcoming chapter, we will use a `Dockerfile` and `docker build`.
|
||||
5. (Optionally) add tags to the image with `docker tag`.
|
||||
|
||||
---
|
||||
|
||||
## Building from a base
|
||||
|
||||
Our base will be the `ubuntu` image.
|
||||
|
||||
---
|
||||
|
||||
## Create a new container and make some changes
|
||||
## Setting up our container
|
||||
|
||||
Start an Ubuntu container:
|
||||
|
||||
@@ -107,7 +101,7 @@ As explained before:
|
||||
|
||||
---
|
||||
|
||||
## Commit and run your image
|
||||
## Commit our changes into a new image
|
||||
|
||||
The `docker commit` command will create a new layer with those changes,
|
||||
and a new image using this new layer.
|
||||
@@ -119,7 +113,13 @@ $ docker commit <yourContainerId>
|
||||
|
||||
The output of the `docker commit` command will be the ID for your newly created image.
|
||||
|
||||
We can run this image:
|
||||
We can use it as an argument to `docker run`.
|
||||
|
||||
---
|
||||
|
||||
## Testing our new image
|
||||
|
||||
Let's run this image:
|
||||
|
||||
```bash
|
||||
$ docker run -it <newImageId>
|
||||
@@ -131,6 +131,8 @@ root@fcfb62f0bfde:/# figlet hello
|
||||
|_| |_|\___|_|_|\___/
|
||||
```
|
||||
|
||||
It works! .emoji[🎉]
|
||||
|
||||
---
|
||||
|
||||
## Tagging images
|
||||
|
||||
@@ -3,7 +3,7 @@ class: title
|
||||
|
||||
# Building Docker images with a Dockerfile
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -188,7 +188,7 @@ root@91f3c974c9a1:/# figlet hello
|
||||
```
|
||||
|
||||
|
||||
Yay! 🎉
|
||||
Yay! .emoji[🎉]
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
class: title
|
||||
|
||||
# CMD and ENTRYPOINT
|
||||
# `CMD` and `ENTRYPOINT`
|
||||
|
||||

|
||||
|
||||
@@ -141,7 +141,7 @@ Why did we use JSON syntax for our `ENTRYPOINT`?
|
||||
|
||||
* When CMD or ENTRYPOINT use string syntax, they get wrapped in `sh -c`.
|
||||
|
||||
* To avoid this wrapping, you must use JSON syntax.
|
||||
* To avoid this wrapping, we can use JSON syntax.
|
||||
|
||||
What if we used `ENTRYPOINT` with string syntax?
|
||||
|
||||
@@ -178,8 +178,6 @@ $ docker run figlet salut
|
||||
\/ \_/|_/|__/ \_/|_/|_/
|
||||
```
|
||||
|
||||
Great success!
|
||||
|
||||
---
|
||||
|
||||
## Using `CMD` and `ENTRYPOINT` together
|
||||
@@ -227,9 +225,8 @@ $ docker build -t figlet .
|
||||
Successfully built 6e0b6a048a07
|
||||
```
|
||||
|
||||
And run it:
|
||||
Run it without parameters:
|
||||
|
||||
.small[
|
||||
```bash
|
||||
$ docker run figlet
|
||||
_ _ _ _
|
||||
@@ -237,7 +234,15 @@ $ docker run figlet
|
||||
| | _ | | | | __ __ ,_ | | __|
|
||||
|/ \ |/ |/ |/ / \_ | | |_/ \_/ | |/ / |
|
||||
| |_/|__/|__/|__/\__/ \/ \/ \__/ |_/|__/\_/|_/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overriding the image default parameters
|
||||
|
||||
Now let's pass extra arguments to the image.
|
||||
|
||||
```bash
|
||||
$ docker run figlet hola mundo
|
||||
_ _
|
||||
| | | | |
|
||||
@@ -245,7 +250,8 @@ $ docker run figlet hola mundo
|
||||
|/ \ / \_|/ / | / |/ |/ | | | / |/ | / | / \_
|
||||
| |_/\__/ |__/\_/|_/ | | |_/ \_/|_/ | |_/\_/|_/\__/
|
||||
```
|
||||
]
|
||||
|
||||
We overrode `CMD` but still used `ENTRYPOINT`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
# Compose For Development Stacks
|
||||
# Compose for development stacks
|
||||
|
||||
Dockerfiles are great to build container images.
|
||||
|
||||
@@ -113,6 +112,7 @@ them.
|
||||
|
||||
Here is the file used in the demo:
|
||||
|
||||
.small[
|
||||
```yaml
|
||||
version: "2"
|
||||
|
||||
@@ -131,6 +131,7 @@ services:
|
||||
redis:
|
||||
image: redis
|
||||
```
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Connecting Containers With Links
|
||||
# Connecting containers with links
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class: title
|
||||
|
||||
# The Container Network Model
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -126,13 +126,16 @@ $ docker run -d --name es --net dev elasticsearch:2
|
||||
|
||||
Now, create another container on this network.
|
||||
|
||||
.small[
|
||||
```bash
|
||||
$ docker run -ti --net dev alpine sh
|
||||
root@0ecccdfa45ef:/#
|
||||
```
|
||||
]
|
||||
|
||||
From this new container, we can resolve and ping the other one, using its assigned name:
|
||||
|
||||
.small[
|
||||
```bash
|
||||
/ # ping es
|
||||
PING es (172.18.0.2) 56(84) bytes of data.
|
||||
@@ -145,6 +148,7 @@ PING es (172.18.0.2) 56(84) bytes of data.
|
||||
rtt min/avg/max/mdev = 0.114/0.149/0.221/0.052 ms
|
||||
root@0ecccdfa45ef:/#
|
||||
```
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
@@ -155,6 +159,7 @@ class: extra-details
|
||||
In Docker Engine 1.9, name resolution is implemented with `/etc/hosts`, and
|
||||
updating it each time containers are added/removed.
|
||||
|
||||
.small[
|
||||
```bash
|
||||
[root@0ecccdfa45ef /]# cat /etc/hosts
|
||||
172.18.0.3 0ecccdfa45ef
|
||||
@@ -167,6 +172,7 @@ ff02::2 ip6-allrouters
|
||||
172.18.0.2 es
|
||||
172.18.0.2 es.dev
|
||||
```
|
||||
]
|
||||
|
||||
In Docker Engine 1.10, this has been replaced by a dynamic resolver.
|
||||
|
||||
@@ -174,7 +180,7 @@ In Docker Engine 1.10, this has been replaced by a dynamic resolver.
|
||||
|
||||
---
|
||||
|
||||
## Connecting multiple containers together
|
||||
# Service discovery with containers
|
||||
|
||||
* Let's try to run an application that requires two containers.
|
||||
|
||||
@@ -210,9 +216,7 @@ $ docker ps -l
|
||||
|
||||
* If we connect to the application now, we will see an error page:
|
||||
|
||||
.small[
|
||||

|
||||
]
|
||||
|
||||
* This is because the Redis service is not running.
|
||||
* This container tries to resolve the name `redis`.
|
||||
@@ -241,9 +245,7 @@ $ docker run --net dev --name redis -d redis
|
||||
|
||||
* If we connect to the application now, we should see that the app is working correctly:
|
||||
|
||||
.small[
|
||||

|
||||
]
|
||||
|
||||
* When the app tries to resolve `redis`, instead of getting a DNS error, it gets the IP address of our Redis container.
|
||||
|
||||
@@ -362,6 +364,7 @@ Each ElasticSearch instance has a name (generated when it is started). This name
|
||||
|
||||
Try the following command a few times:
|
||||
|
||||
.small[
|
||||
```bash
|
||||
$ docker run --rm --net dev centos curl -s es:9200
|
||||
{
|
||||
@@ -369,9 +372,11 @@ $ docker run --rm --net dev centos curl -s es:9200
|
||||
...
|
||||
}
|
||||
```
|
||||
]
|
||||
|
||||
Then try it a few times by replacing `--net dev` with `--net prod`:
|
||||
|
||||
.small[
|
||||
```bash
|
||||
$ docker run --rm --net prod centos curl -s es:9200
|
||||
{
|
||||
@@ -379,6 +384,7 @@ $ docker run --rm --net prod centos curl -s es:9200
|
||||
...
|
||||
}
|
||||
```
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
@@ -486,7 +492,7 @@ Very short instructions:
|
||||
- `docker network create mynet --driver overlay`
|
||||
- `docker service create --network mynet myimage`
|
||||
|
||||
See http://jpetazzo.github.io/orchestration-workshop for all the deets about clustering!
|
||||
See http://jpetazzo.github.io/container.training for all the deets about clustering!
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Container Networking Basics
|
||||
# Container networking basics
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -69,7 +69,7 @@ But first, let's make sure that everything works properly.
|
||||
Point your browser to the IP address of your Docker host, on the port
|
||||
shown by `docker ps` for container port 80.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -189,87 +189,6 @@ $ ping <ipAddress>
|
||||
|
||||
---
|
||||
|
||||
## The different network drivers
|
||||
|
||||
A container can use one of the following drivers:
|
||||
|
||||
* `bridge` (default)
|
||||
* `none`
|
||||
* `host`
|
||||
* `container`
|
||||
|
||||
The driver is selected with `docker run --net ...`.
|
||||
|
||||
The different drivers are explained with more details on the following slides.
|
||||
|
||||
---
|
||||
|
||||
## The default bridge
|
||||
|
||||
* By default, the container gets a virtual `eth0` interface.
|
||||
<br/>(In addition to its own private `lo` loopback interface.)
|
||||
|
||||
* That interface is provided by a `veth` pair.
|
||||
|
||||
* It is connected to the Docker bridge.
|
||||
<br/>(Named `docker0` by default; configurable with `--bridge`.)
|
||||
|
||||
* Addresses are allocated on a private, internal subnet.
|
||||
<br/>(Docker uses 172.17.0.0/16 by default; configurable with `--bip`.)
|
||||
|
||||
* Outbound traffic goes through an iptables MASQUERADE rule.
|
||||
|
||||
* Inbound traffic goes through an iptables DNAT rule.
|
||||
|
||||
* The container can have its own routes, iptables rules, etc.
|
||||
|
||||
---
|
||||
|
||||
## The null driver
|
||||
|
||||
* Container is started with `docker run --net none ...`
|
||||
|
||||
* It only gets the `lo` loopback interface. No `eth0`.
|
||||
|
||||
* It can't send or receive network traffic.
|
||||
|
||||
* Useful for isolated/untrusted workloads.
|
||||
|
||||
---
|
||||
|
||||
## The host driver
|
||||
|
||||
* Container is started with `docker run --net host ...`
|
||||
|
||||
* It sees (and can access) the network interfaces of the host.
|
||||
|
||||
* It can bind any address, any port (for ill and for good).
|
||||
|
||||
* Network traffic doesn't have to go through NAT, bridge, or veth.
|
||||
|
||||
* Performance = native!
|
||||
|
||||
Use cases:
|
||||
|
||||
* Performance sensitive applications (VOIP, gaming, streaming...)
|
||||
|
||||
* Peer discovery (e.g. Erlang port mapper, Raft, Serf...)
|
||||
|
||||
---
|
||||
|
||||
## The container driver
|
||||
|
||||
* Container is started with `docker run --net container:id ...`
|
||||
|
||||
* It re-uses the network stack of another container.
|
||||
|
||||
* It shares with this other container the same interfaces, IP address(es), routes, iptables rules, etc.
|
||||
|
||||
* Those containers can communicate over their `lo` interface.
|
||||
<br/>(i.e. one can bind to 127.0.0.1 and the others can connect to it.)
|
||||
|
||||
---
|
||||
|
||||
## Section summary
|
||||
|
||||
We've learned how to:
|
||||
|
||||
@@ -3,7 +3,7 @@ class: title
|
||||
|
||||
# Copying files during the build
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
class: title
|
||||
|
||||
# Course Conclusion
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Questions & Next Steps
|
||||
|
||||
A bunch of useful links:
|
||||
|
||||
* Docker homepage - http://www.docker.com/
|
||||
* Docker Hub - https://hub.docker.com
|
||||
* Docker blog - http://blog.docker.com/
|
||||
* Docker documentation - http://docs.docker.com/
|
||||
* Docker code on GitHub - https://github.com/docker/docker
|
||||
* Docker mailing list - [https://groups.google.com/forum/#!forum/docker-user
|
||||
* Docker on IRC: irc.freenode.net and channels `#docker` and `#docker-dev`
|
||||
* Docker on Twitter - http://twitter.com/docker
|
||||
* Get Docker help on Stack Overflow - http://stackoverflow.com/search?q=docker
|
||||
* Play With Docker Hands-On Labs - http://training.play-with-docker.com/
|
||||
|
||||
These slides are at: http://container.training/
|
||||
|
||||
---
|
||||
|
||||
class: title
|
||||
|
||||
Thank You!
|
||||
|
||||
.small[http://container.training/]
|
||||
@@ -20,7 +20,7 @@ class: pic
|
||||
|
||||
## The VPS age (until 2007-2008)
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Publishing images to the Docker Hub
|
||||
|
||||
We have built our first images.
|
||||
|
||||
If we were so inclined, we could share those images through the Docker Hub.
|
||||
|
||||
We won't do it since we don't want to force everyone to create a Docker Hub account (although it's free, yay!) but the steps would be:
|
||||
|
||||
* have an account on the Docker Hub
|
||||
|
||||
* tag our image accordingly (i.e. `username/imagename`)
|
||||
|
||||
* `docker push username/imagename`
|
||||
|
||||
Anybody can now `docker run username/imagename` from any Docker host.
|
||||
|
||||
Images can be set to be private as well.
|
||||
|
||||
---
|
||||
|
||||
## The goodness of automated builds
|
||||
|
||||
* You can link a Docker Hub repository with a GitHub or BitBucket repository
|
||||
|
||||
* Each push to GitHub or BitBucket will trigger a build on Docker Hub
|
||||
|
||||
* If the build succeeds, the new image is available on Docker Hub
|
||||
|
||||
* You can map tags and branches between source and container images
|
||||
|
||||
* If you work with public repositories, this is free
|
||||
|
||||
* Corollary: this gives you a very simple way to get free, basic CI
|
||||
|
||||
(With the technique presented earlier)
|
||||
@@ -58,7 +58,7 @@ class: pic
|
||||
|
||||
## The deployment problem
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -66,7 +66,7 @@ class: pic
|
||||
|
||||
## The matrix from hell
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -74,7 +74,7 @@ class: pic
|
||||
|
||||
## The parallel with the shipping indsutry
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -82,7 +82,7 @@ class: pic
|
||||
|
||||
## Intermodal shipping containers
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -90,7 +90,7 @@ class: pic
|
||||
|
||||
## A new shipping ecosystem
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -98,7 +98,7 @@ class: pic
|
||||
|
||||
## A shipping container system for applications
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -106,17 +106,29 @@ class: pic
|
||||
|
||||
## Eliminate the matrix from hell
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## Results
|
||||
|
||||
* Dev-to-prod reduced from 9 months to 15 minutes (ING)
|
||||
* [Dev-to-prod reduced from 9 months to 15 minutes (ING)](
|
||||
https://www.docker.com/sites/default/files/CS_ING_01.25.2015_1.pdf)
|
||||
|
||||
* Continuous integration job time reduced by more than 60% (BBC)
|
||||
* [Continuous integration job time reduced by more than 60% (BBC)](
|
||||
https://www.docker.com/sites/default/files/CS_BBCNews_01.25.2015_1.pdf)
|
||||
|
||||
* Dev-to-prod reduced from weeks to minutes (GILT)
|
||||
* [Deploy 100 times a day instead of once a week (GILT)](
|
||||
https://www.docker.com/sites/default/files/CS_Gilt%20Groupe_03.18.2015_0.pdf)
|
||||
|
||||
* [70% infrastructure consolidation (MetLife)](
|
||||
https://www.docker.com/customers/metlife-transforms-customer-experience-legacy-and-microservices-mashup)
|
||||
|
||||
* [60% infrastructure consolidation (Intesa Sanpaolo)](
|
||||
https://blog.docker.com/2017/11/intesa-sanpaolo-builds-resilient-foundation-banking-docker-enterprise-edition/)
|
||||
|
||||
* [14x application density; 60% of legacy datacenter migrated in 4 months (GE Appliances)](
|
||||
https://www.docker.com/customers/ge-uses-docker-enable-self-service-their-developers)
|
||||
|
||||
* etc.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Our First Containers
|
||||
# Our first containers
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -51,10 +51,13 @@ root@04c0bb0a6c07:/#
|
||||
```
|
||||
|
||||
* This is a brand new container.
|
||||
|
||||
* It runs a bare-bones, no-frills `ubuntu` system.
|
||||
|
||||
* `-it` is shorthand for `-i -t`.
|
||||
|
||||
* `-i` tells Docker to connect us to the container's stdin.
|
||||
|
||||
* `-t` tells Docker that we want a pseudo-terminal.
|
||||
|
||||
---
|
||||
@@ -72,22 +75,6 @@ Alright, we need to install it.
|
||||
|
||||
---
|
||||
|
||||
## An observation
|
||||
|
||||
Let's check how many packages are installed here.
|
||||
|
||||
```bash
|
||||
root@04c0bb0a6c07:/# dpkg -l | wc -l
|
||||
189
|
||||
```
|
||||
|
||||
* `dpkg -l` lists the packages installed in our container
|
||||
* `wc -l` counts them
|
||||
* If you have a Debian or Ubuntu machine, you can run the same command
|
||||
and compare the results.
|
||||
|
||||
---
|
||||
|
||||
## Install a package in our container
|
||||
|
||||
We want `figlet`, so let's install it:
|
||||
@@ -104,6 +91,12 @@ Reading package lists... Done
|
||||
|
||||
One minute later, `figlet` is installed!
|
||||
|
||||
---
|
||||
|
||||
## Try to run our freshly installed program
|
||||
|
||||
The `figlet` program takes a message as parameter.
|
||||
|
||||
```bash
|
||||
root@04c0bb0a6c07:/# figlet hello
|
||||
_ _ _
|
||||
@@ -113,11 +106,30 @@ root@04c0bb0a6c07:/# figlet hello
|
||||
|_| |_|\___|_|_|\___/
|
||||
```
|
||||
|
||||
Beautiful! .emoji[😍]
|
||||
|
||||
---
|
||||
|
||||
## Exiting our container
|
||||
## Counting packages in the container
|
||||
|
||||
Just exit the shell, like you would usually do.
|
||||
Let's check how many packages are installed there.
|
||||
|
||||
```bash
|
||||
root@04c0bb0a6c07:/# dpkg -l | wc -l
|
||||
190
|
||||
```
|
||||
|
||||
* `dpkg -l` lists the packages installed in our container
|
||||
|
||||
* `wc -l` counts them
|
||||
|
||||
How many packages do we have on our host?
|
||||
|
||||
---
|
||||
|
||||
## Counting packages on the host
|
||||
|
||||
Exit the container by logging out of the shell, like you would usually do.
|
||||
|
||||
(E.g. with `^D` or `exit`)
|
||||
|
||||
@@ -125,10 +137,36 @@ Just exit the shell, like you would usually do.
|
||||
root@04c0bb0a6c07:/# exit
|
||||
```
|
||||
|
||||
Now, try to:
|
||||
|
||||
* run `dpkg -l | wc -l`. How many packages are installed?
|
||||
|
||||
* run `figlet`. Does that work?
|
||||
|
||||
---
|
||||
|
||||
## Host and containers are independent things
|
||||
|
||||
* We ran an `ubuntu` container on an `ubuntu` host.
|
||||
|
||||
* But they have different, independent packages.
|
||||
|
||||
* Installing something on the host doesn't expose it to the container.
|
||||
|
||||
* And vice-versa.
|
||||
|
||||
* We can run *any container* on *any host*.
|
||||
|
||||
---
|
||||
|
||||
## Where's our container?
|
||||
|
||||
* Our container is now in a *stopped* state.
|
||||
|
||||
* It still exists on disk, but all compute resources have been freed up.
|
||||
|
||||
* We will see later how to get back to that container.
|
||||
|
||||
---
|
||||
|
||||
## Starting another container
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Understanding Docker Images
|
||||
# Understanding Docker images
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Install Docker
|
||||
# Installing Docker
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Local Development Workflow with Docker
|
||||
# Local development workflow with Docker
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -17,7 +17,7 @@ At the end of this section, you will be able to:
|
||||
|
||||
---
|
||||
|
||||
## Using a Docker container for local development
|
||||
## Containerized local development environments
|
||||
|
||||
We want to solve the following issues:
|
||||
|
||||
@@ -31,28 +31,25 @@ By using Docker containers, we will get a consistent development environment.
|
||||
|
||||
---
|
||||
|
||||
## Our "namer" application
|
||||
## Working on the "namer" application
|
||||
|
||||
* The code is available on https://github.com/jpetazzo/namer.
|
||||
* We have to work on some application whose code is at:
|
||||
|
||||
* The image jpetazzo/namer is automatically built by the Docker Hub.
|
||||
https://github.com/jpetazzo/namer.
|
||||
|
||||
Let's run it with:
|
||||
* What is it? We don't know yet!
|
||||
|
||||
```bash
|
||||
$ docker run -dP jpetazzo/namer
|
||||
```
|
||||
|
||||
Check the port number with `docker ps` and open the application.
|
||||
|
||||
---
|
||||
|
||||
## Let's look at the code
|
||||
|
||||
Let's download our application's source code.
|
||||
* Let's download the code.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/jpetazzo/namer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Looking at the code
|
||||
|
||||
```bash
|
||||
$ cd namer
|
||||
$ ls -1
|
||||
company_name_generator.rb
|
||||
@@ -62,11 +59,13 @@ Dockerfile
|
||||
Gemfile
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
Aha, a `Gemfile`! This is Ruby. Probably. We know this. Maybe?
|
||||
|
||||
---
|
||||
|
||||
## Where's my code?
|
||||
|
||||
According to the Dockerfile, the code is copied into `/src` :
|
||||
## Looking at the `Dockerfile`
|
||||
|
||||
```dockerfile
|
||||
FROM ruby
|
||||
@@ -80,9 +79,85 @@ CMD ["rackup", "--host", "0.0.0.0"]
|
||||
EXPOSE 9292
|
||||
```
|
||||
|
||||
We want to make changes *inside the container* without rebuilding it each time.
|
||||
* This application is using a base `ruby` image.
|
||||
* The code is copied in `/src`.
|
||||
* Dependencies are installed with `bundler`.
|
||||
* The application is started with `rackup`.
|
||||
* It is listening on port 9292.
|
||||
|
||||
For that, we will use a *volume*.
|
||||
---
|
||||
|
||||
## Building and running the "namer" application
|
||||
|
||||
* Let's build the application with the `Dockerfile`!
|
||||
|
||||
--
|
||||
|
||||
```bash
|
||||
$ docker build -t namer .
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
* Then run it. *We need to expose its ports.*
|
||||
|
||||
--
|
||||
|
||||
```bash
|
||||
$ docker run -dP namer
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
* Check on which port the container is listening.
|
||||
|
||||
--
|
||||
|
||||
```bash
|
||||
$ docker ps -l
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connecting to our application
|
||||
|
||||
* Point our browser to our Docker node, on the port allocated to the container.
|
||||
|
||||
--
|
||||
|
||||
* Hit "reload" a few times.
|
||||
|
||||
--
|
||||
|
||||
* This is an enterprise-class, carrier-grade, ISO-compliant company name generator!
|
||||
|
||||
(With 50% more bullshit than the average competition!)
|
||||
|
||||
(Wait, was that 50% more, or 50% less? *Anyway!*)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Making changes to the code
|
||||
|
||||
Option 1:
|
||||
|
||||
* Edit the code locally
|
||||
* Rebuild the image
|
||||
* Re-run the container
|
||||
|
||||
Option 2:
|
||||
|
||||
* Enter the container (with `docker exec`)
|
||||
* Install an editor
|
||||
* Make changes from within the container
|
||||
|
||||
Option 3:
|
||||
|
||||
* Use a *volume* to mount local files into the container
|
||||
* Make changes locally
|
||||
* Changes are reflected into the container
|
||||
|
||||
---
|
||||
|
||||
@@ -91,16 +166,16 @@ For that, we will use a *volume*.
|
||||
We will tell Docker to map the current directory to `/src` in the container.
|
||||
|
||||
```bash
|
||||
$ docker run -d -v $(pwd):/src -p 80:9292 jpetazzo/namer
|
||||
$ docker run -d -v $(pwd):/src -P namer
|
||||
```
|
||||
|
||||
* `-d`: the container should run in detached mode (in the background).
|
||||
|
||||
* `-v`: the following host directory should be mounted inside the container.
|
||||
|
||||
* `-p`: connections to port 80 on the host should be routed to port 9292 in the container.
|
||||
* `-P`: publish all the ports exposed by this image.
|
||||
|
||||
* `jpetazzo/namer` is the name of the image we will run.
|
||||
* `namer` is the name of the image we will run.
|
||||
|
||||
* We don't specify a command to run because is is already set in the Dockerfile.
|
||||
|
||||
@@ -108,14 +183,15 @@ $ docker run -d -v $(pwd):/src -p 80:9292 jpetazzo/namer
|
||||
|
||||
## Mounting volumes inside containers
|
||||
|
||||
The `-v` flag mounts a directory from your host into your Docker
|
||||
container. The flag structure is:
|
||||
The `-v` flag mounts a directory from your host into your Docker container.
|
||||
|
||||
The flag structure is:
|
||||
|
||||
```bash
|
||||
[host-path]:[container-path]:[rw|ro]
|
||||
```
|
||||
|
||||
* If [host-path] or [container-path] doesn't exist it is created.
|
||||
* If `[host-path]` or `[container-path]` doesn't exist it is created.
|
||||
|
||||
* You can control the write status of the volume with the `ro` and
|
||||
`rw` options.
|
||||
@@ -128,27 +204,15 @@ There will be a full chapter about volumes!
|
||||
|
||||
## Testing the development container
|
||||
|
||||
Now let us see if our new container is running.
|
||||
* Check the port used by our new container.
|
||||
|
||||
```bash
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
045885b68bc5 trai... rackup 3 seconds ago Up ... 0.0.0.0:80->9292/tcp ...
|
||||
$ docker ps -l
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
045885b68bc5 namer rackup 3 seconds ago Up ... 0.0.0.0:32770->9292/tcp ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Viewing our application
|
||||
|
||||
Now let's browse to our web application on:
|
||||
|
||||
```bash
|
||||
http://<yourHostIP>:80
|
||||
```
|
||||
|
||||
We can see our company naming application.
|
||||
|
||||

|
||||
* Open the application in your web browser.
|
||||
|
||||
---
|
||||
|
||||
@@ -174,53 +238,121 @@ color: red;
|
||||
|
||||
---
|
||||
|
||||
## Refreshing our application
|
||||
## Viewing our changes
|
||||
|
||||
Now let's refresh our browser:
|
||||
* Reload the application in our browser.
|
||||
|
||||
```bash
|
||||
http://<yourHostIP>:80
|
||||
```
|
||||
--
|
||||
|
||||
We can see the updated color of our company naming application.
|
||||
* The color should have changed.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## Improving the workflow with Compose
|
||||
## Understanding volumes
|
||||
|
||||
* You can also start the container with the following command:
|
||||
* Volumes are *not* copying or synchronizing files between the host and the container.
|
||||
|
||||
```bash
|
||||
$ docker-compose up -d
|
||||
```
|
||||
* Volumes are *bind mounts*: a kernel mechanism associating a path to another.
|
||||
|
||||
* This works thanks to the Compose file, `docker-compose.yml`:
|
||||
* Bind mounts are *kind of* similar to symbolic links, but at a very different level.
|
||||
|
||||
```yaml
|
||||
www:
|
||||
build: .
|
||||
volumes:
|
||||
- .:/src
|
||||
ports:
|
||||
- 80:9292
|
||||
```
|
||||
* Changes made on the host or on the container will be visible on the other side.
|
||||
|
||||
(Since under the hood, it's the same file on both anyway.)
|
||||
|
||||
---
|
||||
|
||||
## Why Compose?
|
||||
## Trash your servers and burn your code
|
||||
|
||||
* Specifying all those "docker run" parameters is tedious.
|
||||
*(This is the title of a
|
||||
[2013 blog post](http://chadfowler.com/2013/06/23/immutable-deployments.html)
|
||||
by Chad Fowler, where he explains the concept of immutable infrastructure.)*
|
||||
|
||||
* And error-prone.
|
||||
--
|
||||
|
||||
* We can "encode" those parameters in a "Compose file."
|
||||
* Let's mess up majorly with our container.
|
||||
|
||||
* When you see a `docker-compose.yml` file, you know that you can use `docker-compose up`.
|
||||
(Remove files or whatever.)
|
||||
|
||||
* Now, how can we fix this?
|
||||
|
||||
--
|
||||
|
||||
* Our old container (with the blue version of the code) is still running.
|
||||
|
||||
* See on which port it is exposed:
|
||||
```bash
|
||||
docker ps
|
||||
```
|
||||
|
||||
* Point our browser to it to confirm that it still works fine.
|
||||
|
||||
---
|
||||
|
||||
## Immutable infrastructure in a nutshell
|
||||
|
||||
* Instead of *updating* a server, we deploy a new one.
|
||||
|
||||
* This might be challenging with classical servers, but it's trivial with containers.
|
||||
|
||||
* In fact, with Docker, the most logical workflow is to build a new image and run it.
|
||||
|
||||
* If something goes wrong with the new image, we can always restart the old one.
|
||||
|
||||
* We can even keep both versions running side by side.
|
||||
|
||||
If this pattern sounds interesting, you might want to read about *blue/green deployment*
|
||||
and *canary deployments*.
|
||||
|
||||
---
|
||||
|
||||
## Improving the workflow
|
||||
|
||||
The workflow that we showed is nice, but it requires us to:
|
||||
|
||||
* keep track of all the `docker run` flags required to run the container,
|
||||
|
||||
* inspect the `Dockerfile` to know which path(s) to mount,
|
||||
|
||||
* write scripts to hide that complexity.
|
||||
|
||||
There has to be a better way!
|
||||
|
||||
---
|
||||
|
||||
## Docker Compose to the rescue
|
||||
|
||||
* Docker Compose allows us to "encode" `docker run` parameters in a YAML file.
|
||||
|
||||
* Here is the `docker-compose.yml` file that we can use for our "namer" app:
|
||||
|
||||
```yaml
|
||||
www:
|
||||
build: .
|
||||
volumes:
|
||||
- .:/src
|
||||
ports:
|
||||
- 80:9292
|
||||
```
|
||||
|
||||
* Try it:
|
||||
```bash
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Working with Docker Compose
|
||||
|
||||
* When you see a `docker-compose.yml` file, you can use `docker-compose up`.
|
||||
|
||||
* It can build images and run them with the required parameters.
|
||||
|
||||
* Compose can also deal with complex, multi-container apps.
|
||||
<br/>(More on this later.)
|
||||
|
||||
(More on this later!)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
---
|
||||
|
||||
## Implementing multi-stage builds for our C program
|
||||
## Multi-stage builds for our C program
|
||||
|
||||
We will change our Dockerfile to:
|
||||
|
||||
@@ -65,7 +65,7 @@ The resulting Dockerfile is on the next slide.
|
||||
|
||||
---
|
||||
|
||||
## Revised Dockerfile implementing multi-stage build
|
||||
## Multi-stage build `Dockerfile`
|
||||
|
||||
Here is the final Dockerfile:
|
||||
|
||||
@@ -89,7 +89,7 @@ docker run hellomultistage
|
||||
|
||||
---
|
||||
|
||||
## Comparing single-stage and multi-stage image sizes
|
||||
## Comparing single/multi-stage build image sizes
|
||||
|
||||
List our images with `docker images`, and check the size of:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class: title
|
||||
|
||||
# Naming and inspecting containers
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -85,16 +85,8 @@ The `docker inspect` command will output a very detailed JSON map.
|
||||
```bash
|
||||
$ docker inspect <containerID>
|
||||
[{
|
||||
"AppArmorProfile": "",
|
||||
"Args": [],
|
||||
"Config": {
|
||||
"AttachStderr": true,
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": true,
|
||||
"Cmd": [
|
||||
"bash"
|
||||
],
|
||||
"CpuShares": 0,
|
||||
...
|
||||
(many pages of JSON here)
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
84
slides/intro/Network_Drivers.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Container network drivers
|
||||
|
||||
The Docker Engine supports many different network drivers.
|
||||
|
||||
The built-in drivers include:
|
||||
|
||||
* `bridge` (default)
|
||||
|
||||
* `none`
|
||||
|
||||
* `host`
|
||||
|
||||
* `container`
|
||||
|
||||
The driver is selected with `docker run --net ...`.
|
||||
|
||||
The different drivers are explained with more details on the following slides.
|
||||
|
||||
---
|
||||
|
||||
## The default bridge
|
||||
|
||||
* By default, the container gets a virtual `eth0` interface.
|
||||
<br/>(In addition to its own private `lo` loopback interface.)
|
||||
|
||||
* That interface is provided by a `veth` pair.
|
||||
|
||||
* It is connected to the Docker bridge.
|
||||
<br/>(Named `docker0` by default; configurable with `--bridge`.)
|
||||
|
||||
* Addresses are allocated on a private, internal subnet.
|
||||
<br/>(Docker uses 172.17.0.0/16 by default; configurable with `--bip`.)
|
||||
|
||||
* Outbound traffic goes through an iptables MASQUERADE rule.
|
||||
|
||||
* Inbound traffic goes through an iptables DNAT rule.
|
||||
|
||||
* The container can have its own routes, iptables rules, etc.
|
||||
|
||||
---
|
||||
|
||||
## The null driver
|
||||
|
||||
* Container is started with `docker run --net none ...`
|
||||
|
||||
* It only gets the `lo` loopback interface. No `eth0`.
|
||||
|
||||
* It can't send or receive network traffic.
|
||||
|
||||
* Useful for isolated/untrusted workloads.
|
||||
|
||||
---
|
||||
|
||||
## The host driver
|
||||
|
||||
* Container is started with `docker run --net host ...`
|
||||
|
||||
* It sees (and can access) the network interfaces of the host.
|
||||
|
||||
* It can bind any address, any port (for ill and for good).
|
||||
|
||||
* Network traffic doesn't have to go through NAT, bridge, or veth.
|
||||
|
||||
* Performance = native!
|
||||
|
||||
Use cases:
|
||||
|
||||
* Performance sensitive applications (VOIP, gaming, streaming...)
|
||||
|
||||
* Peer discovery (e.g. Erlang port mapper, Raft, Serf...)
|
||||
|
||||
---
|
||||
|
||||
## The container driver
|
||||
|
||||
* Container is started with `docker run --net container:id ...`
|
||||
|
||||
* It re-uses the network stack of another container.
|
||||
|
||||
* It shares with this other container the same interfaces, IP address(es), routes, iptables rules, etc.
|
||||
|
||||
* Those containers can communicate over their `lo` interface.
|
||||
<br/>(i.e. one can bind to 127.0.0.1 and the others can connect to it.)
|
||||
|
||||
102
slides/intro/Publishing_To_Docker_Hub.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Publishing images to the Docker Hub
|
||||
|
||||
We have built our first images.
|
||||
|
||||
We can now publish it to the Docker Hub!
|
||||
|
||||
*You don't have to do the exercises in this section,
|
||||
because they require an account on the Docker Hub, and we
|
||||
don't want to force anyone to create one.*
|
||||
|
||||
*Note, however, that creating an account on the Docker Hub
|
||||
is free (and doesn't require a credit card), and hosting
|
||||
public images is free as well.*
|
||||
|
||||
---
|
||||
|
||||
## Logging into our Docker Hub account
|
||||
|
||||
* This can be done from the Docker CLI:
|
||||
```bash
|
||||
docker login
|
||||
```
|
||||
|
||||
.warning[When running Docker4Mac, Docker4Windows, or
|
||||
Docker on a Linux workstation, it can (and will when
|
||||
possible) integrate with your system's keyring to
|
||||
store your credentials securely. However, on most Linux
|
||||
servers, it will store your credentials in `~/.docker/config`.]
|
||||
|
||||
---
|
||||
|
||||
## Image tags and registry addresses
|
||||
|
||||
* Docker images tags are like Git tags and branches.
|
||||
|
||||
* They are like *bookmarks* pointing at a specific image ID.
|
||||
|
||||
* Tagging an image doesn't *rename* an image: it adds another tag.
|
||||
|
||||
* When pushing an image to a registry, the registry address is in the tag.
|
||||
|
||||
Example: `registry.example.net:5000/image`
|
||||
|
||||
* What about Docker Hub images?
|
||||
|
||||
--
|
||||
|
||||
* `jpetazzo/clock` is, in fact, `index.docker.io/jpetazzo/clock`
|
||||
|
||||
* `ubuntu` is, in fact, `library/ubuntu`, i.e. `index.docker.io/library/ubuntu`
|
||||
|
||||
---
|
||||
|
||||
## Tagging an image to push it on the Hub
|
||||
|
||||
* Let's tag our `figlet` image (or any other to our liking):
|
||||
```bash
|
||||
docker tag figlet jpetazzo/figlet
|
||||
```
|
||||
|
||||
* And push it to the Hub:
|
||||
```bash
|
||||
docker push jpetazzo/figlet
|
||||
```
|
||||
|
||||
* That's it!
|
||||
|
||||
--
|
||||
|
||||
* Anybody can now `docker run jpetazzo/figlet` anywhere.
|
||||
|
||||
---
|
||||
|
||||
## The goodness of automated builds
|
||||
|
||||
* You can link a Docker Hub repository with a GitHub or BitBucket repository
|
||||
|
||||
* Each push to GitHub or BitBucket will trigger a build on Docker Hub
|
||||
|
||||
* If the build succeeds, the new image is available on Docker Hub
|
||||
|
||||
* You can map tags and branches between source and container images
|
||||
|
||||
* If you work with public repositories, this is free
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Setting up an automated build
|
||||
|
||||
* We need a Dockerized repository!
|
||||
* Let's go to https://github.com/jpetazzo/trainingwheels and fork it.
|
||||
* Go to the Docker Hub (https://hub.docker.com/).
|
||||
* Select "Create" in the top-right bar, and select "Create Automated Build."
|
||||
* Connect your Docker Hub account to your GitHub account.
|
||||
* Select your user and the repository that we just forked.
|
||||
* Create.
|
||||
* Then go to "Build Settings."
|
||||
* Put `/www` in "Dockerfile Location" (or whichever directory the Dockerfile is in).
|
||||
* Click "Trigger" to build the repository immediately (without waiting for a git push).
|
||||
* Subsequent builds will happen automatically, thanks to GitHub hooks.
|
||||
@@ -2,7 +2,7 @@ class: title
|
||||
|
||||
# Our training environment
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
class: title
|
||||
|
||||
# Working with Volumes
|
||||
# Working with volumes
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -19,7 +19,7 @@ At the end of this section, you will be able to:
|
||||
|
||||
---
|
||||
|
||||
## Working with Volumes
|
||||
## Working with volumes
|
||||
|
||||
Docker volumes can be used to achieve many things, including:
|
||||
|
||||
@@ -95,7 +95,7 @@ We will see an example in the following slides.
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Sharing web application logs with another container
|
||||
## Sharing app server logs with another container
|
||||
|
||||
Let's start a Tomcat container:
|
||||
|
||||
@@ -311,9 +311,9 @@ QUIT
|
||||
|
||||
---
|
||||
|
||||
## What happens when you remove containers with volumes?
|
||||
## Volumes lifecycle
|
||||
|
||||
* Volumes are kept around.
|
||||
* When you remove a container, its volumes are kept around.
|
||||
|
||||
* You can list them with `docker volume ls`.
|
||||
|
||||
@@ -371,9 +371,9 @@ $ docker inspect <yourContainerID>
|
||||
|
||||
---
|
||||
|
||||
## Sharing a single file between the host and a container
|
||||
## Sharing a single file
|
||||
|
||||
The same `-v` flag can be used to share a single file.
|
||||
The same `-v` flag can be used to share a single file (instead of a directory).
|
||||
|
||||
One of the most interesting examples is to share the Docker control socket.
|
||||
|
||||
@@ -381,8 +381,11 @@ One of the most interesting examples is to share the Docker control socket.
|
||||
$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker sh
|
||||
```
|
||||
|
||||
Warning: when using such mounts, the container gains root-like access to the host.
|
||||
It can potentially do bad things.
|
||||
From that container, you can now run `docker` commands communicating with
|
||||
the Docker Engine running on the host. Try `docker ps`!
|
||||
|
||||
.warning[Since that container has access to the Docker socket, it
|
||||
has root-like access to the host.]
|
||||
|
||||
---
|
||||
|
||||
|
||||
33
slides/kube-halfday.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
title: |
|
||||
Deploying and Scaling Microservices
|
||||
with Docker and Kubernetes
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
- logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - common/prereqs.md
|
||||
- kube/versions-k8s.md
|
||||
- common/sampleapp.md
|
||||
- - kube/concepts-k8s.md
|
||||
- common/declarative.md
|
||||
- kube/declarative.md
|
||||
- kube/kubenet.md
|
||||
- kube/kubectlget.md
|
||||
- kube/setup-k8s.md
|
||||
- kube/kubectlrun.md
|
||||
- - kube/kubectlexpose.md
|
||||
- kube/ourapponkube.md
|
||||
- kube/dashboard.md
|
||||
- - kube/kubectlscale.md
|
||||
- kube/daemonset.md
|
||||
- kube/rollout.md
|
||||
- kube/whatsnext.md
|
||||
- common/thankyou.md
|
||||
33
slides/kube-selfpaced.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
title: |
|
||||
Deploying and Scaling Microservices
|
||||
with Docker and Kubernetes
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
|
||||
|
||||
exclude:
|
||||
- in-person
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
#- logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - common/prereqs.md
|
||||
- kube/versions-k8s.md
|
||||
- common/sampleapp.md
|
||||
- - kube/concepts-k8s.md
|
||||
- common/declarative.md
|
||||
- kube/declarative.md
|
||||
- kube/kubenet.md
|
||||
- kube/kubectlget.md
|
||||
- kube/setup-k8s.md
|
||||
- kube/kubectlrun.md
|
||||
- - kube/kubectlexpose.md
|
||||
- kube/ourapponkube.md
|
||||
- kube/dashboard.md
|
||||
- - kube/kubectlscale.md
|
||||
- kube/daemonset.md
|
||||
- kube/rollout.md
|
||||
- kube/whatsnext.md
|
||||
- common/thankyou.md
|
||||
106
slides/kube.yml
@@ -1,106 +0,0 @@
|
||||
exclude:
|
||||
- self-paced
|
||||
- snap
|
||||
|
||||
chat: "[Gitter](https://gitter.im/jpetazzo/workshop-20171026-prague)"
|
||||
|
||||
title: "Deploying and Scaling Microservices with Docker and Kubernetes"
|
||||
|
||||
chapters:
|
||||
- |
|
||||
class: title
|
||||
|
||||
.small[
|
||||
|
||||
Deploying and Scaling Microservices <br/> with Docker and Kubernetes
|
||||
|
||||
.small[.small[
|
||||
|
||||
**Be kind to the WiFi!**
|
||||
|
||||
<!--
|
||||
*Use the 5G network*
|
||||
<br/>
|
||||
-->
|
||||
*Don't use your hotspot*
|
||||
<br/>
|
||||
*Don't stream videos from YouTube, Netflix, etc.
|
||||
<br/>(if you're bored, watch local content instead)*
|
||||
|
||||
Thank you!
|
||||
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Intros
|
||||
|
||||
- Hello! We are
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo), Docker Inc.)
|
||||
&
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake), Travis CI)
|
||||
|
||||
--
|
||||
|
||||
- This is our first time doing this
|
||||
|
||||
--
|
||||
|
||||
- But ... containers and us go back a long way
|
||||
|
||||
--
|
||||
|
||||

|
||||
|
||||
--
|
||||
|
||||
- In the immortal words of [Chelsea Manning](https://twitter.com/xychelsea): #WeGotThis!
|
||||
|
||||
---
|
||||
|
||||
## Logistics
|
||||
|
||||
- The tutorial will run from 9:00am to 12:15pm
|
||||
|
||||
- There will be a coffee break at 10:30am
|
||||
<br/>
|
||||
(please remind me if I forget about it!)
|
||||
|
||||
- This will be fast-paced, but DON'T PANIC!
|
||||
<br/>
|
||||
(all the content is publicly available)
|
||||
|
||||
- Feel free to interrupt for questions at any time
|
||||
|
||||
- Live feedback, questions, help on @@CHAT@@
|
||||
|
||||
- kube/intro-ks.md
|
||||
- |
|
||||
@@TOC@@
|
||||
- - kube/prereqs-k8s.md
|
||||
- kube/versions-k8s.md
|
||||
- common/sampleapp.md
|
||||
- - kube/concepts-k8s.md
|
||||
- kube/kubenet.md
|
||||
- kube/kubectlget.md
|
||||
- kube/setup-k8s.md
|
||||
- kube/kubectlrun.md
|
||||
- - kube/kubectlexpose.md
|
||||
- kube/ourapponkube.md
|
||||
- kube/dashboard.md
|
||||
- - kube/kubectlscale.md
|
||||
- kube/daemonset.md
|
||||
- kube/rollout.md
|
||||
- kube/whatsnext.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
That's all folks! <br/> Questions?
|
||||
|
||||
.small[.small[
|
||||
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo)) — [@docker](https://twitter.com/docker)
|
||||
|
||||
]]
|
||||
@@ -208,89 +208,6 @@ Yes!
|
||||
|
||||
class: pic
|
||||
|
||||

|
||||

|
||||
|
||||
(Diagram courtesy of Weave Works, used with permission.)
|
||||
|
||||
---
|
||||
|
||||
# Declarative vs imperative
|
||||
|
||||
- Kubernetes puts a very strong emphasis on being *declarative*
|
||||
|
||||
- Declarative:
|
||||
|
||||
*I would like a cup of tea.*
|
||||
|
||||
- Imperative:
|
||||
|
||||
*Boil some water. Pour it in a teapot. Add tea leaves. Steep for a while. Serve in cup.*
|
||||
|
||||
--
|
||||
|
||||
- Declarative seems simpler at first ...
|
||||
|
||||
--
|
||||
|
||||
- ... As long as you know how to brew tea
|
||||
|
||||
---
|
||||
|
||||
## Declarative vs imperative
|
||||
|
||||
- What declarative would really be:
|
||||
|
||||
*I want a cup of tea, obtained by pouring an infusion¹ of tea leaves in a cup.*
|
||||
|
||||
--
|
||||
|
||||
*¹An infusion is obtained by letting the object steep a few minutes in hot² water.*
|
||||
|
||||
--
|
||||
|
||||
*²Hot liquid is obtained by pouring it in an appropriate container³ and setting it on a stove.*
|
||||
|
||||
--
|
||||
|
||||
*³Ah, finally, containers! Something we know about. Let's get to work, shall we?*
|
||||
|
||||
--
|
||||
|
||||
.footnote[Did you know there was an [ISO standard](https://en.wikipedia.org/wiki/ISO_3103)
|
||||
specifying how to brew tea?]
|
||||
|
||||
---
|
||||
|
||||
## Declarative vs imperative
|
||||
|
||||
- Imperative systems:
|
||||
|
||||
- simpler
|
||||
|
||||
- if a task is interrupted, we have to restart from scratch
|
||||
|
||||
- Declarative systems:
|
||||
|
||||
- if a task is interrupted (or if we show up to the party half-way through),
|
||||
we can figure out what's missing and do only what's necessary
|
||||
|
||||
- we need to be able to *observe* the system
|
||||
|
||||
- ... and compute a "diff" between *what we have* and *what we want*
|
||||
|
||||
---
|
||||
|
||||
## Declarative vs imperative in Kubernetes
|
||||
|
||||
- Virtually everything we create in Kubernetes is created from a *spec*
|
||||
|
||||
- Watch for the `spec` fields in the YAML files later!
|
||||
|
||||
- The *spec* describes *how we want the thing to be*
|
||||
|
||||
- Kubernetes will *reconcile* the current state with the spec
|
||||
<br/>(technically, this is done by a number of *controllers*)
|
||||
|
||||
- When we want to change some resource, we update the *spec*
|
||||
|
||||
- Kubernetes will then *converge* that resource
|
||||
|
||||
@@ -336,7 +336,7 @@ Of course, option 2 offers more learning opportunities. Right?
|
||||
|
||||
---
|
||||
|
||||
## We've put resources in your resources all the way down
|
||||
## We've put resources in your resources
|
||||
|
||||
- Reminder: a daemon set is a resource that creates more resources!
|
||||
|
||||
|
||||
14
slides/kube/declarative.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## Declarative vs imperative in Kubernetes
|
||||
|
||||
- Virtually everything we create in Kubernetes is created from a *spec*
|
||||
|
||||
- Watch for the `spec` fields in the YAML files later!
|
||||
|
||||
- The *spec* describes *how we want the thing to be*
|
||||
|
||||
- Kubernetes will *reconcile* the current state with the spec
|
||||
<br/>(technically, this is done by a number of *controllers*)
|
||||
|
||||
- When we want to change some resource, we update the *spec*
|
||||
|
||||
- Kubernetes will then *converge* that resource
|
||||
@@ -1,15 +0,0 @@
|
||||
## About these slides
|
||||
|
||||
- Your one-stop shop to awesomeness:
|
||||
|
||||
http://container.training/
|
||||
|
||||
- The content that you're viewing right now is in a public GitHub repository:
|
||||
|
||||
https://github.com/jpetazzo/orchestration-workshop
|
||||
|
||||
- Typos? Mistakes? Questions? Feel free to hover over the bottom of the slide ...
|
||||
|
||||
--
|
||||
|
||||
.footnote[👇 Try it! The source file will be shown and you can view it on GitHub and fork and edit it.]
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
---
|
||||
|
||||
## From human-readable to machine-readable output
|
||||
## Obtaining machine-readable output
|
||||
|
||||
- `kubectl get` can output JSON, YAML, or be directly formatted
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ We should see the following things:
|
||||
|
||||
---
|
||||
|
||||
## Deployments, replica sets, and replication controllers
|
||||
## What are these different things?
|
||||
|
||||
- A *deployment* is a high-level construct
|
||||
|
||||
@@ -236,14 +236,13 @@ Unfortunately, `--follow` cannot (yet) be used to stream the logs from multiple
|
||||
|
||||
class: title
|
||||
|
||||
.small[
|
||||
Meanwhile, at the Google NOC ...
|
||||
|
||||
.small[
|
||||
Why the hell
|
||||
Meanwhile,
|
||||
<br/>
|
||||
are we getting 1000 packets per second
|
||||
at the Google NOC ...
|
||||
<br/>
|
||||
of ICMP ECHO traffic from EC2 ?!?
|
||||
]
|
||||
]
|
||||
<br/>
|
||||
.small[“Why the hell]
|
||||
<br/>
|
||||
.small[are we getting 1000 packets per second]
|
||||
<br/>
|
||||
.small[of ICMP ECHO traffic from EC2 ?!?”]
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes network model: the bad and the ugly
|
||||
## Kubernetes network model: the less good
|
||||
|
||||
- Everything can reach everything
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ The curl command should now output:
|
||||
|
||||
- Go to the `stacks` directory:
|
||||
```bash
|
||||
cd ~/orchestration-workshop/stacks
|
||||
cd ~/container.training/stacks
|
||||
```
|
||||
|
||||
- Build and push the images:
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
- Go to the `stack` directory:
|
||||
```bash
|
||||
cd ~/orchestration-workshop/stacks
|
||||
cd ~/container.training/stacks
|
||||
```
|
||||
|
||||
- Edit `dockercoins/worker/worker.py`, update the `sleep` line to sleep 1 second
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
---
|
||||
|
||||
## Rolling out the new version of the `worker` service
|
||||
## Rolling out the new `worker` service
|
||||
|
||||
.exercise[
|
||||
|
||||
|
||||
136
slides/lisa.yml
@@ -1,136 +0,0 @@
|
||||
title: "LISA17 T9: Build, Ship, and Run Microservices on a Docker Swarm Cluster"
|
||||
|
||||
chat: "[Gitter](https://gitter.im/jpetazzo/workshop-20171031-sanfrancisco)"
|
||||
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
- snap
|
||||
- auto-btp
|
||||
- benchmarking
|
||||
- elk-manual
|
||||
- prom-manual
|
||||
|
||||
chapters:
|
||||
- |
|
||||
class: title
|
||||
|
||||
.small[
|
||||
|
||||
LISA17 T9
|
||||
|
||||
Build, Ship, and Run Microservices on a Docker Swarm Cluster
|
||||
|
||||
.small[.small[
|
||||
|
||||
**Be kind to the WiFi!**
|
||||
|
||||
*Use the 5G network*
|
||||
<br/>
|
||||
*Don't use your hotspot*
|
||||
<br/>
|
||||
*Don't stream videos from YouTube, Netflix, etc.
|
||||
<br/>(if you're bored, watch local content instead)*
|
||||
|
||||
<!--
|
||||
Also: share the power outlets
|
||||
<br/>
|
||||
*(with limited power comes limited responsibility?)*
|
||||
<br/>
|
||||
*(or something?)*
|
||||
-->
|
||||
|
||||
Thank you!
|
||||
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Intros
|
||||
|
||||
- Hello! We are
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake), Travis CI)
|
||||
&
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo), Docker Inc.)
|
||||
|
||||
--
|
||||
|
||||
- This is our collective Docker knowledge:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Logistics
|
||||
|
||||
- The tutorial will run from 1:30pm to 5:00pm
|
||||
|
||||
- This will be fast-paced, but DON'T PANIC!
|
||||
|
||||
- There will be a coffee break at 3:00pm
|
||||
<br/>
|
||||
(please remind us if we forget about it!)
|
||||
|
||||
- Feel free to interrupt for questions at any time
|
||||
|
||||
- All the content is publicly available (slides, code samples, scripts)
|
||||
|
||||
One URL to remember: http://container.training
|
||||
|
||||
- Live feedback, questions, help on @@CHAT@@
|
||||
|
||||
- swarm/intro.md
|
||||
- |
|
||||
@@TOC@@
|
||||
- - swarm/prereqs.md
|
||||
- swarm/versions.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
All right!
|
||||
<br/>
|
||||
We're all set.
|
||||
<br/>
|
||||
Let's do this.
|
||||
- common/sampleapp.md
|
||||
- swarm/swarmkit.md
|
||||
- swarm/creatingswarm.md
|
||||
- swarm/morenodes.md
|
||||
- - swarm/firstservice.md
|
||||
- swarm/ourapponswarm.md
|
||||
- swarm/updatingservices.md
|
||||
#- swarm/rollingupdates.md
|
||||
#- swarm/healthchecks.md
|
||||
- - swarm/operatingswarm.md
|
||||
#- swarm/netshoot.md
|
||||
#- swarm/ipsec.md
|
||||
#- swarm/swarmtools.md
|
||||
- swarm/security.md
|
||||
#- swarm/secrets.md
|
||||
#- swarm/encryptionatrest.md
|
||||
- swarm/leastprivilege.md
|
||||
- swarm/apiscope.md
|
||||
- swarm/logging.md
|
||||
- swarm/metrics.md
|
||||
#- swarm/stateful.md
|
||||
#- swarm/extratips.md
|
||||
- swarm/end.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
That's all folks! <br/> Questions?
|
||||
|
||||
.small[.small[
|
||||
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake)) — [@TravisCI](https://twitter.com/travisci)
|
||||
|
||||
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo)) — [@Docker](https://twitter.com/docker)
|
||||
|
||||
]]
|
||||
|
||||
<!--
|
||||
Tiffany ([@tiffanyfayj](https://twitter.com/tiffanyfayj))
|
||||
AJ ([@s0ulshake](https://twitter.com/s0ulshake))
|
||||
-->
|
||||
19
slides/logistics.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Intros
|
||||
|
||||
- Hello! We are:
|
||||
|
||||
- .emoji[👷🏻♀️] AJ ([@s0ulshake](https://twitter.com/s0ulshake), Travis CI)
|
||||
|
||||
- .emoji[🐳] Jérôme ([@jpetazzo](https://twitter.com/jpetazzo), Docker Inc.)
|
||||
|
||||
- The workshop will run from 9am to 4pm
|
||||
|
||||
- There will be a lunch break at noon
|
||||
|
||||
(And coffee breaks!)
|
||||
|
||||
- Feel free to interrupt for questions at any time
|
||||
|
||||
- *Especially when you see full screen container pictures!*
|
||||
|
||||
- Live feedback, questions, help on @@CHAT@@
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
# transforms a YAML manifest into a HTML workshop file
|
||||
|
||||
import glob
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ def anchor(title):
|
||||
return "toc-" + title
|
||||
|
||||
|
||||
def interstitials_generator():
|
||||
images = [url.strip() for url in open("interstitials.txt") if url.strip()]
|
||||
while True:
|
||||
for image in images:
|
||||
yield image
|
||||
interstitials = interstitials_generator()
|
||||
|
||||
|
||||
def insertslide(markdown, title):
|
||||
title_position = markdown.find("\n# {}\n".format(title))
|
||||
slide_position = markdown.rfind("\n---\n", 0, title_position+1)
|
||||
@@ -33,18 +41,37 @@ def insertslide(markdown, title):
|
||||
|
||||
before = markdown[:slide_position]
|
||||
|
||||
toclink = "toc-chapter-{}".format(title2path[title][0])
|
||||
_titles_ = [""] + all_titles + [""]
|
||||
currentindex = _titles_.index(title)
|
||||
previouslink = anchor(_titles_[currentindex-1])
|
||||
nextlink = anchor(_titles_[currentindex+1])
|
||||
interstitial = interstitials.next()
|
||||
|
||||
extra_slide = """
|
||||
---
|
||||
|
||||
class: pic
|
||||
|
||||
.interstitial[]
|
||||
|
||||
---
|
||||
|
||||
name: {anchor}
|
||||
class: title
|
||||
|
||||
{title}
|
||||
|
||||
.nav[[Back to table of contents](#{toclink})]
|
||||
.nav[
|
||||
[Previous section](#{previouslink})
|
||||
|
|
||||
[Back to table of contents](#{toclink})
|
||||
|
|
||||
[Next section](#{nextlink})
|
||||
]
|
||||
|
||||
.debug[(automatically generated title slide)]
|
||||
""".format(anchor=anchor(title), title=title, toclink=title2chapter[title])
|
||||
""".format(anchor=anchor(title), interstitial=interstitial, title=title, toclink=toclink, previouslink=previouslink, nextlink=nextlink)
|
||||
after = markdown[slide_position:]
|
||||
return before + extra_slide + after
|
||||
|
||||
@@ -58,10 +85,10 @@ def flatten(titles):
|
||||
yield title
|
||||
|
||||
|
||||
def generatefromyaml(manifest):
|
||||
def generatefromyaml(manifest, filename):
|
||||
manifest = yaml.load(manifest)
|
||||
|
||||
markdown, titles = processchapter(manifest["chapters"], "(inline)")
|
||||
markdown, titles = processchapter(manifest["chapters"], filename)
|
||||
logging.debug("Found {} titles.".format(len(titles)))
|
||||
toc = gentoc(titles)
|
||||
markdown = markdown.replace("@@TOC@@", toc)
|
||||
@@ -74,38 +101,51 @@ def generatefromyaml(manifest):
|
||||
logging.warning("'exclude' is empty.")
|
||||
exclude = ",".join('"{}"'.format(c) for c in exclude)
|
||||
|
||||
# Insert build info. This is super hackish.
|
||||
|
||||
markdown = markdown.replace(
|
||||
".debug[",
|
||||
".debug[\n```\n{}\n```\n\nThese slides have been built from commit: {}\n\n".format(dirtyfiles, commit),
|
||||
1)
|
||||
|
||||
markdown = markdown.replace("@@TITLE@@", manifest["title"].replace("\n", "<br/>"))
|
||||
|
||||
html = open("workshop.html").read()
|
||||
html = html.replace("@@MARKDOWN@@", markdown)
|
||||
html = html.replace("@@EXCLUDE@@", exclude)
|
||||
html = html.replace("@@CHAT@@", manifest["chat"])
|
||||
html = html.replace("@@TITLE@@", manifest["title"])
|
||||
html = html.replace("@@TITLE@@", manifest["title"].replace("\n", " "))
|
||||
return html
|
||||
|
||||
|
||||
title2chapter = {}
|
||||
# Maps a section title (the string just after "^# ") to its position
|
||||
# in the table of content (as a (chapter,part,subpart,...) tuple).
|
||||
title2path = {}
|
||||
path2title = {}
|
||||
all_titles = []
|
||||
|
||||
|
||||
def gentoc(titles, depth=0, chapter=0):
|
||||
if not titles:
|
||||
# "tree" is a list of titles, potentially nested.
|
||||
def gentoc(tree, path=()):
|
||||
if not tree:
|
||||
return ""
|
||||
if isinstance(titles, str):
|
||||
title2chapter[titles] = "toc-chapter-1"
|
||||
logging.debug("Chapter {} Title {}".format(chapter, titles))
|
||||
return " "*(depth-2) + "- [{}](#{})\n".format(titles, anchor(titles))
|
||||
if isinstance(titles, list):
|
||||
if depth==0:
|
||||
sep = "\n\n.debug[(auto-generated TOC)]\n---\n\n"
|
||||
head = ""
|
||||
tail = ""
|
||||
elif depth==1:
|
||||
sep = "\n"
|
||||
head = "name: toc-chapter-{}\n\n## Chapter {}\n\n".format(chapter, chapter)
|
||||
tail = ""
|
||||
if isinstance(tree, str):
|
||||
title = tree
|
||||
title2path[title] = path
|
||||
path2title[path] = title
|
||||
all_titles.append(title)
|
||||
logging.debug("Path {} Title {}".format(path, title))
|
||||
return "- [{}](#{})".format(title, anchor(title))
|
||||
if isinstance(tree, list):
|
||||
if len(path) == 0:
|
||||
return "\n---\n".join(gentoc(subtree, path+(i+1,)) for (i,subtree) in enumerate(tree))
|
||||
elif len(path) == 1:
|
||||
chapterslide = "name: toc-chapter-{n}\n\n## Chapter {n}\n\n".format(n=path[0])
|
||||
for (i,subtree) in enumerate(tree):
|
||||
chapterslide += gentoc(subtree, path+(i+1,)) + "\n\n"
|
||||
chapterslide += ".debug[(auto-generated TOC)]"
|
||||
return chapterslide
|
||||
else:
|
||||
sep = "\n"
|
||||
head = ""
|
||||
tail = ""
|
||||
return head + sep.join(gentoc(t, depth+1, c+1) for (c,t) in enumerate(titles)) + tail
|
||||
return "\n\n".join(gentoc(subtree, path+(i+1,)) for (i,subtree) in enumerate(tree))
|
||||
|
||||
|
||||
# Arguments:
|
||||
@@ -146,8 +186,8 @@ try:
|
||||
if "BRANCH" in os.environ:
|
||||
branch = os.environ["BRANCH"]
|
||||
else:
|
||||
branch = subprocess.check_output(["git", "status", "--short", "--branch"])
|
||||
branch = branch[3:].split("...")[0]
|
||||
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
||||
branch = branch.strip()
|
||||
base = subprocess.check_output(["git", "rev-parse", "--show-prefix"])
|
||||
base = base.strip().strip("/")
|
||||
urltemplate = ("{repo}/tree/{branch}/{base}/{filename}"
|
||||
@@ -155,6 +195,16 @@ try:
|
||||
except:
|
||||
logging.exception("Could not generate repository URL; generating local URLs instead.")
|
||||
urltemplate = "file://{pwd}/{filename}".format(pwd=os.environ["PWD"], filename="{}")
|
||||
try:
|
||||
commit = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
|
||||
except:
|
||||
logging.exception("Could not figure out HEAD commit.")
|
||||
commit = "??????"
|
||||
try:
|
||||
dirtyfiles = subprocess.check_output(["git", "status", "--porcelain"])
|
||||
except:
|
||||
logging.exception("Could not figure out repository cleanliness.")
|
||||
dirtyfiles = "?? git status --porcelain failed"
|
||||
|
||||
def makelink(filename):
|
||||
if os.path.isfile(filename):
|
||||
@@ -163,6 +213,15 @@ def makelink(filename):
|
||||
else:
|
||||
return filename
|
||||
|
||||
|
||||
sys.stdout.write(generatefromyaml(sys.stdin))
|
||||
logging.info("Done")
|
||||
if len(sys.argv) != 2:
|
||||
logging.error("This program takes one and only one argument: the YAML file to process.")
|
||||
else:
|
||||
filename = sys.argv[1]
|
||||
if filename == "-":
|
||||
filename = "<stdin>"
|
||||
manifest = sys.stdin
|
||||
else:
|
||||
manifest = open(filename)
|
||||
logging.info("Processing {}...".format(filename))
|
||||
sys.stdout.write(generatefromyaml(manifest, filename))
|
||||
logging.info("Processed {}.".format(filename))
|
||||
|
||||
11
slides/rename.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
if ! [ -f "$1" ]; then
|
||||
echo "File $1 not found, aborting."
|
||||
exit 1
|
||||
fi
|
||||
if [ -f "$2" ]; then
|
||||
echo "File $2 already exists, aborting."
|
||||
exit 1
|
||||
fi
|
||||
git mv "$1" "$2"
|
||||
sed -i "" "s,$1,$2," */*.md
|
||||
@@ -9,13 +9,63 @@ page.onResourceError = function(resourceError) {
|
||||
console.log('ResourceError: ' + resourceError.url);
|
||||
}
|
||||
|
||||
console.log('Loading: ' + url);
|
||||
page.onConsoleMessage = function(msg) {
|
||||
//console.log('Console: ' +msg);
|
||||
}
|
||||
|
||||
console.log('DEBUG Loading: ' + url);
|
||||
page.open(url, function(status) {
|
||||
console.log('Loaded: ' + url + '(' + status + ')');
|
||||
var slides = page.evaluate(function() {
|
||||
return document.getElementsByClassName('remark-slide-container');
|
||||
console.log('DEBUG Loaded: ' + url + '(' + status + ')');
|
||||
|
||||
/* analyze will be an object with:
|
||||
*
|
||||
* titles
|
||||
* A dict with all the titles that are too high
|
||||
* (i.e. because they have been broken across multiple
|
||||
* lines because they are too long)
|
||||
*
|
||||
* slides
|
||||
* A dict with the slides that are too high
|
||||
*
|
||||
* n_slides
|
||||
* Number of slides found
|
||||
*/
|
||||
var analyze = page.evaluate(function() {
|
||||
var ret = {}, i, n = slideshow.getSlideCount();
|
||||
ret = [];
|
||||
for (i=1; i<=n; i++) {
|
||||
console.log('DEBUG Current slide: ' + i + '/' + n);
|
||||
var visible_slide = document.getElementsByClassName('remark-visible')[0];
|
||||
var debug = visible_slide.getElementsByClassName('debug');
|
||||
if (debug.length==0) {
|
||||
debug = '?';
|
||||
}
|
||||
else {
|
||||
debug = debug[0].textContent;
|
||||
}
|
||||
var slide_desc = 'Slide ' + i + '/' + n + ' (' + debug + ')';
|
||||
['h1', 'h2'].forEach(function(tag) {
|
||||
var titles = visible_slide.getElementsByTagName(tag);
|
||||
console.log('DEBUG Found ' + titles.length + ' titles with tag ' + tag);
|
||||
titles.forEach(function(t) {
|
||||
if (t.clientHeight>60) {
|
||||
ret.push(slide_desc + ' has a long title: ' + t.textContent);
|
||||
}
|
||||
});
|
||||
});
|
||||
var scaler = visible_slide.getElementsByClassName('remark-slide-scaler')[0];
|
||||
var slide = scaler.getElementsByClassName('remark-slide')[0];
|
||||
if (slide.clientHeight > scaler.clientHeight) {
|
||||
ret.push(slide_desc + ' is too long');
|
||||
}
|
||||
slideshow.gotoNextSlide();
|
||||
}
|
||||
ret.push('Deck has ' + n + ' slides');
|
||||
return ret;
|
||||
});
|
||||
console.log('Number of slides: ' + slides.length);
|
||||
console.log('Done: ' + url + '(' + status + ')');
|
||||
analyze.forEach(function(msg) {
|
||||
console.log(msg);
|
||||
});
|
||||
console.log('DEBUG Done: ' + url + '(' + status + ')');
|
||||
phantom.exit();
|
||||
});
|
||||
|
||||
53
slides/swarm-fullday.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
title: |
|
||||
Container Orchestration
|
||||
with Docker and Swarm
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
- snap
|
||||
- btp-auto
|
||||
- benchmarking
|
||||
- elk-manual
|
||||
- prom-manual
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
- logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - common/prereqs.md
|
||||
- swarm/versions.md
|
||||
- common/sampleapp.md
|
||||
- swarm/swarmkit.md
|
||||
- common/declarative.md
|
||||
- swarm/swarmmode.md
|
||||
- swarm/creatingswarm.md
|
||||
#- swarm/machine.md
|
||||
- swarm/morenodes.md
|
||||
- - swarm/firstservice.md
|
||||
- swarm/ourapponswarm.md
|
||||
- swarm/hostingregistry.md
|
||||
- swarm/testingregistry.md
|
||||
- swarm/btp-manual.md
|
||||
- swarm/swarmready.md
|
||||
- swarm/compose2swarm.md
|
||||
- swarm/updatingservices.md
|
||||
#- swarm/rollingupdates.md
|
||||
- swarm/healthchecks.md
|
||||
- - swarm/operatingswarm.md
|
||||
- swarm/netshoot.md
|
||||
- swarm/ipsec.md
|
||||
- swarm/swarmtools.md
|
||||
- swarm/security.md
|
||||
- swarm/secrets.md
|
||||
- swarm/encryptionatrest.md
|
||||
- swarm/leastprivilege.md
|
||||
- swarm/apiscope.md
|
||||
- - swarm/logging.md
|
||||
- swarm/metrics.md
|
||||
- swarm/stateful.md
|
||||
- swarm/extratips.md
|
||||
- common/thankyou.md
|
||||
53
slides/swarm-halfday.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
title: |
|
||||
Container Orchestration
|
||||
with Docker and Swarm
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
|
||||
|
||||
exclude:
|
||||
- self-paced
|
||||
- snap
|
||||
- btp-manual
|
||||
- benchmarking
|
||||
- elk-manual
|
||||
- prom-manual
|
||||
|
||||
chapters:
|
||||
- common/title.md
|
||||
- logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - common/prereqs.md
|
||||
- swarm/versions.md
|
||||
- common/sampleapp.md
|
||||
- swarm/swarmkit.md
|
||||
- common/declarative.md
|
||||
- swarm/swarmmode.md
|
||||
- swarm/creatingswarm.md
|
||||
#- swarm/machine.md
|
||||
- swarm/morenodes.md
|
||||
- - swarm/firstservice.md
|
||||
- swarm/ourapponswarm.md
|
||||
#- swarm/hostingregistry.md
|
||||
#- swarm/testingregistry.md
|
||||
#- swarm/btp-manual.md
|
||||
#- swarm/swarmready.md
|
||||
- swarm/compose2swarm.md
|
||||
- swarm/updatingservices.md
|
||||
#- swarm/rollingupdates.md
|
||||
#- swarm/healthchecks.md
|
||||
- - swarm/operatingswarm.md
|
||||
#- swarm/netshoot.md
|
||||
#- swarm/ipsec.md
|
||||
#- swarm/swarmtools.md
|
||||
- swarm/security.md
|
||||
#- swarm/secrets.md
|
||||
#- swarm/encryptionatrest.md
|
||||
- swarm/leastprivilege.md
|
||||
- swarm/apiscope.md
|
||||
- swarm/logging.md
|
||||
- swarm/metrics.md
|
||||
#- swarm/stateful.md
|
||||
#- swarm/extratips.md
|
||||
- common/thankyou.md
|
||||
@@ -1,27 +1,20 @@
|
||||
title: |
|
||||
Container Orchestration
|
||||
with Docker and Swarm
|
||||
|
||||
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
|
||||
|
||||
exclude:
|
||||
- in-person
|
||||
|
||||
chat: FIXME
|
||||
|
||||
title: Docker Orchestration Workshop
|
||||
- btp-auto
|
||||
|
||||
chapters:
|
||||
- |
|
||||
class: title
|
||||
Docker <br/> Orchestration <br/> Workshop
|
||||
- swarm/intro.md
|
||||
- |
|
||||
@@TOC@@
|
||||
- - swarm/prereqs.md
|
||||
- common/title.md
|
||||
#- common/logistics.md
|
||||
- common/intro.md
|
||||
- common/toc.md
|
||||
- - common/prereqs.md
|
||||
- swarm/versions.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
All right!
|
||||
<br/>
|
||||
We're all set.
|
||||
<br/>
|
||||
Let's do this.
|
||||
- |
|
||||
name: part-1
|
||||
|
||||
@@ -29,16 +22,25 @@ chapters:
|
||||
|
||||
Part 1
|
||||
- common/sampleapp.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
Scaling out
|
||||
- swarm/swarmkit.md
|
||||
- common/declarative.md
|
||||
- swarm/swarmmode.md
|
||||
- swarm/creatingswarm.md
|
||||
- swarm/machine.md
|
||||
- swarm/morenodes.md
|
||||
- - swarm/firstservice.md
|
||||
- swarm/ourapponswarm.md
|
||||
- swarm/hostingregistry.md
|
||||
- swarm/testingregistry.md
|
||||
- swarm/btp-manual.md
|
||||
- swarm/swarmready.md
|
||||
- swarm/compose2swarm.md
|
||||
- |
|
||||
name: part-2
|
||||
|
||||
class: title, self-paced
|
||||
|
||||
Part 2
|
||||
- - swarm/operatingswarm.md
|
||||
- swarm/netshoot.md
|
||||
- swarm/swarmnbt.md
|
||||
@@ -50,16 +52,11 @@ chapters:
|
||||
- swarm/swarmtools.md
|
||||
- - swarm/security.md
|
||||
- swarm/secrets.md
|
||||
- swarm/leastprivilege.md
|
||||
- kube/namespaces.md
|
||||
- swarm/apiscope.md
|
||||
- swarm/encryptionatrest.md
|
||||
- swarm/leastprivilege.md
|
||||
- swarm/apiscope.md
|
||||
- swarm/logging.md
|
||||
- swarm/metrics.md
|
||||
- swarm/stateful.md
|
||||
- swarm/extratips.md
|
||||
- swarm/end.md
|
||||
- |
|
||||
class: title
|
||||
|
||||
Thank you!
|
||||
- common/thankyou.md
|
||||