Compare commits

...

68 Commits

Author SHA1 Message Date
Jerome Petazzoni
7bd50cdcf3 fix-redirects.sh: adding forced redirect 2020-04-07 16:56:57 -05:00
Jérôme Petazzoni
e0b27ae943 Merge branch 'master' into qconsf2017intro 2017-11-16 08:44:44 -08:00
Jerome Petazzoni
d2d1771fd3 Better emoji support 2017-11-15 23:41:14 +01:00
Jérôme Petazzoni
5518c8c6ec Update chat link 2017-11-13 22:42:28 -08:00
Jérôme Petazzoni
fab3b3be89 Merge branch 'master' into qconsf2017intro 2017-11-13 22:41:38 -08:00
Jérôme Petazzoni
6c5e3eb3f3 Update versions 2017-11-13 22:39:21 -08:00
Jérôme Petazzoni
d99dbd5878 Add first support to auto-open URLs 2017-11-12 22:47:07 -08:00
Jérôme Petazzoni
6d4894458a Add note about interstitials 2017-11-12 18:06:43 -08:00
Jérôme Petazzoni
bc367a1297 Simplify reference to PWD in intro slides 2017-11-12 17:45:15 -08:00
Jérôme Petazzoni
31d1074ee0 autotest: process single keypresses 2017-11-12 17:38:01 -08:00
Jérôme Petazzoni
cd18a87b8c Add self-paced kube manifest + small fixes in kube content 2017-11-12 17:37:40 -08:00
Jérôme Petazzoni
2bb2edc4f6 Use a rewrite rule instead of a redirect 2017-11-10 23:09:19 -08:00
Jérôme Petazzoni
8279a3bce9 Add QCON slides 2017-11-10 23:01:17 -08:00
Jérôme Petazzoni
5613a37c8f Merge branch 'master' into qconsf2017intro 2017-11-10 22:58:13 -08:00
Jérôme Petazzoni
93524898cc Fix build script exit status 2017-11-10 22:57:06 -08:00
Jérôme Petazzoni
6bba1684ec Setup QCON2017 intro workshop branch 2017-11-10 22:52:36 -08:00
Jérôme Petazzoni
e9319060f6 Add pause images between chapters 2017-11-10 22:48:20 -08:00
Jérôme Petazzoni
4322478a4a A bunch of changes in there
- added self-paced manifest for intro workshop
- refactored intro content to work for all workshops
- fixed footnote CSS to work in all contexts
- intro+logistics content is good to go
2017-11-10 19:24:38 -08:00
Jérôme Petazzoni
00262c767e Move autotest.py to the slides/ directory 2017-11-10 13:31:00 -08:00
Jérôme Petazzoni
c8030e1500 Move autotest.py to the slides/ directory 2017-11-10 13:30:43 -08:00
Jérôme Petazzoni
3717b90444 Fix one long title 2017-11-10 13:08:19 -08:00
Jérôme Petazzoni
d003bdb765 Update --detach explanations 2017-11-10 13:06:52 -08:00
Jérôme Petazzoni
8b246ac334 Fix overflowing titles and slides in kube material 2017-11-10 12:55:13 -08:00
Jérôme Petazzoni
9df543a6bb Fix long titles and long slides in Swarm content 2017-11-09 19:58:55 -08:00
Jérôme Petazzoni
77befc1092 Improve appearance of code snippets 2017-11-09 19:58:35 -08:00
Jérôme Petazzoni
4417771315 Allow to automatically check slides + append check result at the end 2017-11-09 19:58:22 -08:00
Jérôme Petazzoni
04fa6ec1d8 Fix overflow in intro material 2017-11-09 16:43:56 -08:00
Jérôme Petazzoni
5e8bdcb1f6 Improve intro section 2017-11-09 14:27:30 -08:00
Jérôme Petazzoni
07d99763d3 Fix back-to-toc links 2017-11-09 13:41:29 -08:00
Jérôme Petazzoni
87a2051b24 Move one stray image 2017-11-09 13:36:21 -08:00
Jérôme Petazzoni
0eddc876f1 Add typist simulator 2017-11-08 23:24:43 -08:00
Jérôme Petazzoni
1a67a1397b Misc autotest improvements 2017-11-08 23:10:34 -08:00
Jérôme Petazzoni
dc6bf9d0cf Improve autotest by setting up tmux automatically 2017-11-08 23:10:25 -08:00
Jérôme Petazzoni
84f8bf007e Add spacer row 2017-11-08 21:28:22 -08:00
Jérôme Petazzoni
6a2a66c165 Improve local devel workflow; add notes about immutable infra 2017-11-08 18:49:40 -08:00
Jérôme Petazzoni
f491d559e3 Setting up an automated build 2017-11-08 14:32:04 -08:00
Jérôme Petazzoni
5d9bdc303c Fix title capitalization 2017-11-08 13:39:34 -08:00
Jérôme Petazzoni
f7f0ecddd4 Pushing to the hub 2017-11-08 13:31:22 -08:00
Jérôme Petazzoni
f0f03e2440 Rename other images 2017-11-08 11:04:51 -08:00
Jérôme Petazzoni
1f78264e9f Add helper script to rename assets 2017-11-08 10:41:56 -08:00
Jérôme Petazzoni
2aad5319f9 Rename title images for consistency 2017-11-08 10:41:46 -08:00
Jérôme Petazzoni
479b858398 Update use-cases and testimonials 2017-11-08 09:57:00 -08:00
Jérôme Petazzoni
34b8bfd1b2 Improve reporting for slide layout checker 2017-11-08 09:56:38 -08:00
Jérôme Petazzoni
408036b09c Implement checker for slides too high/too wide 2017-11-08 00:08:00 -08:00
Jérôme Petazzoni
a45ec1cb84 Reorg intro material a bit 2017-11-07 22:41:32 -08:00
Jérôme Petazzoni
8decf1852f Refactor build-tag-push
We now have two clean classes: btp-manual and btp-auto

Exclude one or the other depending on whether you want to
show the full process of building and pushing images, or
if you want to shortcut and use stack files directly.
2017-11-07 19:43:42 -08:00
Jérôme Petazzoni
350fac21f6 Add PyCon intro to docker 2017-11-07 18:41:48 -08:00
Jérôme Petazzoni
cedd386eee Add LISA16 recording 2017-11-07 18:29:08 -08:00
Jérôme Petazzoni
5751765d66 MOAR materials; smaller footer 2017-11-07 18:09:38 -08:00
Jérôme Petazzoni
7293c071bd CSS is hard, yo 2017-11-07 17:46:56 -08:00
Jérôme Petazzoni
98cf8f4f04 Rename manifests 2017-11-07 14:48:20 -08:00
Jérôme Petazzoni
c61ccd1c27 Uniformize selfpaced/halfday/fullday 2017-11-07 11:30:36 -08:00
Jérôme Petazzoni
6b3d0efa56 split out the 'declarative/imperative' explanation 2017-11-07 11:23:53 -08:00
Jérôme Petazzoni
164578f1c8 Images should be good now 2017-11-06 23:26:39 -08:00
Jérôme Petazzoni
938fe956cf Better closing links 2017-11-06 22:48:21 -08:00
Jérôme Petazzoni
b44d402c1d Tweak warning image 2017-11-06 22:43:50 -08:00
Jérôme Petazzoni
fbaa511813 add colophon 2017-11-06 22:17:54 -08:00
Jérôme Petazzoni
f000594c62 Refactored CSS 2017-11-06 22:15:12 -08:00
Jérôme Petazzoni
71ba94063e Add explanations when using Docker Machine or Toolbox
Thanks @jgarrouste for reminding me of this issue :-)
2017-11-06 17:05:34 -08:00
Jérôme Petazzoni
b025a8c966 add application metrics 2017-11-06 16:47:32 -08:00
Jérôme Petazzoni
c36aab132b Add prev/next navigation links + fix TOC backlinks 2017-11-06 16:39:02 -08:00
Jérôme Petazzoni
1e7a47ed37 Factor conclusion 2017-11-06 11:40:30 -08:00
Jérôme Petazzoni
87f544774e Factor pre-requirements 2017-11-06 11:26:16 -08:00
Jérôme Petazzoni
db9ee5f03a Factor title, toc, logistics, thankyou slides 2017-11-05 12:41:30 -08:00
Jérôme Petazzoni
d0f5d69157 Add some extra debug info in first slide 2017-11-05 11:52:27 -08:00
Jérôme Petazzoni
742c7a78bc Debug bar now shows manifest file 2017-11-05 10:22:44 -08:00
Jérôme Petazzoni
918d3a6c23 orchestration-workshop -> container.training (in slides) 2017-11-05 10:16:01 -08:00
Jérôme Petazzoni
1f9b304eec Update homepage 2017-11-05 10:08:19 -08:00
129 changed files with 2762 additions and 2492 deletions

2
.gitignore vendored
View File

@@ -7,4 +7,4 @@ prepare-vms/ips.pdf
prepare-vms/settings.yaml
prepare-vms/tags
slides/*.yml.html
autotest/nextstep
slides/nextstep

View File

@@ -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/)

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
/ /intro-fullday.yml.html 200!

17
slides/appendcheck.py Executable file
View 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)

View File

@@ -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:

View File

@@ -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)

View 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*

View File

@@ -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.]

View File

@@ -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
![You get five VMs](images/you-get-five-vms.jpg)
<!--
```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
View 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)!

View File

@@ -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`
![Service discovery](images/service-discovery.png)
```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 logo](images/dockercoins.png)
(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
View File

@@ -0,0 +1,26 @@
class: title, self-paced
Thank you!
---
class: title, in-person
That's all folks! <br/> Questions?
![end](images/end.jpg)
---
# 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
View 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
View File

@@ -0,0 +1,4 @@
@@TOC@@

View File

@@ -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:
![Bell Curve](images/bell-curve.jpg)
---
## "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
View File

@@ -0,0 +1,2 @@
#!/bin/sh
grep --color=auto -P -n "[^\x00-\x80]" */*.md

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

View File

Before

Width:  |  Height:  |  Size: 350 KiB

After

Width:  |  Height:  |  Size: 350 KiB

View File

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 155 KiB

View File

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 230 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View File

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

Before

Width:  |  Height:  |  Size: 927 KiB

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 595 KiB

After

Width:  |  Height:  |  Size: 595 KiB

View File

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -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
View 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
View 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

View 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

View File

@@ -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

View File

@@ -1,9 +1,6 @@
class: title
# Advanced Dockerfiles
![construction](images/construction.jpg)
![construction](images/title-advanced-dockerfiles.jpg)
---

View File

@@ -3,7 +3,7 @@ class: title
# Ambassadors
![](images/ambassador.jpg)
![Two serious-looking persons shaking hands](images/title-ambassador.jpg)
---
@@ -40,7 +40,7 @@ ambassador containers.
---
![ambassador](images/diagram.png)
![ambassador](images/ambassador-diagram.png)
---

View File

@@ -1,9 +1,9 @@
class: title
# Background Containers
# Background containers
![Background Containers](images/background-containers.jpg)
![Background containers](images/title-background-containers.jpg)
---

View File

@@ -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

View File

@@ -3,7 +3,7 @@ class: title
# Building Docker images with a Dockerfile
![construction](images/construction.jpg)
![Construction site with containers](images/title-building-docker-images-with-a-dockerfile.jpg)
---
@@ -188,7 +188,7 @@ root@91f3c974c9a1:/# figlet hello
```
Yay! 🎉
Yay! .emoji[🎉]
---

View File

@@ -1,7 +1,7 @@
class: title
# CMD and ENTRYPOINT
# `CMD` and `ENTRYPOINT`
![Container entry doors](images/entrypoint.jpg)
@@ -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`.
---

View File

@@ -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
```
]
---

View File

@@ -1,9 +1,9 @@
class: title
# Connecting Containers With Links
# Connecting containers with links
![graph](images/graph.gif)
![graph](images/title-connecting-containers-with-links.gif)
---

View File

@@ -3,7 +3,7 @@ class: title
# The Container Network Model
![A denser graph network](images/complex-network.jpg)
![A denser graph network](images/title-the-container-network-model.jpg)
---
@@ -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[
![Trainingwheels error](images/trainingwheels-error.png)
]
* 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[
![Trainingwheels OK](images/trainingwheels-ok.png)
]
* 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!
---

View File

@@ -1,9 +1,9 @@
class: title
# Container Networking Basics
# Container networking basics
![A dense graph network](images/network.jpg)
![A dense graph network](images/title-container-networking-basics.jpg)
---
@@ -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.
![Screenshot](images/web.png)
![Screenshot](images/welcome-to-nginx.png)
---
@@ -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:

View File

@@ -3,7 +3,7 @@ class: title
# Copying files during the build
![Monks copying books](images/copy.jpg)
![Monks copying books](images/title-copying-files-during-build.jpg)
---

View File

@@ -1,32 +0,0 @@
class: title
# Course Conclusion
![end](images/end.jpg)
---
## 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/]

View File

@@ -20,7 +20,7 @@ class: pic
## The VPS age (until 2007-2008)
![lightcont](images/lightcont.png)
![lightcont](images/containers-as-lightweight-vms.png)
---

View File

@@ -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)

View File

@@ -58,7 +58,7 @@ class: pic
## The deployment problem
![problem](images/problem.png)
![problem](images/shipping-software-problem.png)
---
@@ -66,7 +66,7 @@ class: pic
## The matrix from hell
![matrix](images/matrix.png)
![matrix](images/shipping-matrix-from-hell.png)
---
@@ -74,7 +74,7 @@ class: pic
## The parallel with the shipping indsutry
![history](images/shippingindustry.png)
![history](images/shipping-industry-problem.png)
---
@@ -82,7 +82,7 @@ class: pic
## Intermodal shipping containers
![shipping](images/shipping.png)
![shipping](images/shipping-industry-solution.png)
---
@@ -90,7 +90,7 @@ class: pic
## A new shipping ecosystem
![shipeco](images/shipeco.png)
![shipeco](images/shipping-indsutry-results.png)
---
@@ -98,7 +98,7 @@ class: pic
## A shipping container system for applications
![shipapp](images/appcont.png)
![shipapp](images/shipping-software-solution.png)
---
@@ -106,17 +106,29 @@ class: pic
## Eliminate the matrix from hell
![elimatrix](images/elimatrix.png)
![elimatrix](images/shipping-matrix-solved.png)
---
## 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.

View File

@@ -1,9 +1,9 @@
class: title
# Our First Containers
# Our first containers
![Plastic Containers](images/firstcontainers.jpg)
![Colorful plastic tubs](images/title-our-first-containers.jpg)
---
@@ -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

View File

@@ -1,9 +1,9 @@
class: title
# Understanding Docker Images
# Understanding Docker images
![image](images/image.png)
![image](images/title-understanding-docker-images.png)
---

View File

@@ -1,10 +1,8 @@
class: title
# Install Docker
# Installing Docker
![install](images/install.jpg)
![install](images/title-installing-docker.jpg)
---

View File

@@ -1,9 +1,9 @@
class: title
# Local Development Workflow with Docker
# Local development workflow with Docker
![construction](images/construction.jpg)
![Construction site](images/title-local-development-workflow-with-docker.jpg)
---
@@ -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!*)
![web application 1](images/webapp-in-blue.png)
---
## 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.
![web application 1](images/webapp1.png)
* 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.
![web application 2](images/webapp2.png)
![web application 2](images/webapp-in-red.png)
---
## 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!)
---

View File

@@ -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:

View File

@@ -3,7 +3,7 @@ class: title
# Naming and inspecting containers
![Markings on container door](images/containermarkings.jpg)
![Markings on container door](images/title-naming-and-inspecting-containers.jpg)
---
@@ -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)
...
```

View 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.)

View 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.

View File

@@ -2,7 +2,7 @@ class: title
# Our training environment
![SSH terminal](images/ssh.jpg)
![SSH terminal](images/title-our-training-environment.jpg)
---

View File

@@ -1,9 +1,9 @@
class: title
# Working with Volumes
# Working with volumes
![volume](images/volume.jpg)
![volume](images/title-working-with-volumes.jpg)
---
@@ -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
View 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
View 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

View File

@@ -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
--
![CONTAINERS, I TELL YOU](images/aj-containers.jpeg)
--
- 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)
]]

View File

@@ -208,89 +208,6 @@ Yes!
class: pic
![Node, pod, container](images/thanks-weave.png)
![Node, pod, container](images/k8s-arch3-thanks-weave.png)
(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

View File

@@ -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!

View 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

View File

@@ -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.]

View File

@@ -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

View File

@@ -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 ?!?”]

View File

@@ -40,7 +40,7 @@
---
## Kubernetes network model: the bad and the ugly
## Kubernetes network model: the less good
- Everything can reach everything

View File

@@ -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:

View File

@@ -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[

View File

@@ -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:
![Bell Curve](images/bell-curve.jpg)
---
## 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
View 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@@

View File

@@ -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[![Image separating from the next chapter]({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
View 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

View File

@@ -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
View 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
View 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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More