diff --git a/docs/kube.yml b/docs/kube.yml
index bee4b5db..29c9e648 100644
--- a/docs/kube.yml
+++ b/docs/kube.yml
@@ -88,24 +88,10 @@ chapters:
- concepts-k8s.md
- kubectlget.md
- setup-k8s.md
-
-- - firstservice.md
- - ourapponswarm.md
- - updatingservices.md
- - healthchecks.md
-- - operatingswarm.md
- - netshoot.md
- - ipsec.md
- - swarmtools.md
-- - security.md
- - secrets.md
- - leastprivilege.md
- - apiscope.md
- - logging.md
- - metrics.md
- - stateful.md
- - extratips.md
- - end.md
+- - kubectlrun.md
+ - kubectlexpose.md
+ - ourapponkube.md
+ - kubectlscale.md
- |
class: title
@@ -116,8 +102,3 @@ chapters:
Jérôme ([@jpetazzo](https://twitter.com/jpetazzo)) — [@docker](https://twitter.com/docker)
]]
-
-
diff --git a/docs/kubectlexpose.md b/docs/kubectlexpose.md
new file mode 100644
index 00000000..30dd00dc
--- /dev/null
+++ b/docs/kubectlexpose.md
@@ -0,0 +1,138 @@
+# Exposing containers
+
+- `kubectl expose` creates a *service* for existing pods
+
+- A *service* is a stable address for a pod (or a bunch of pods)
+
+- If we want to connect to our pod(s), we need to create a *service*
+
+- Once a service is created, `kube-dns` will allow us to resolve it by name
+
+ (i.e. after creating service `hello`, the name `hello` will resolve to something)
+
+- There are different types of services, detailed on the following slides:
+
+ `ClusterIP`, `NodePort`, `LoadBalancer`, `ExternalName`
+
+---
+
+## Basic service types
+
+- `ClusterIP` (default type)
+
+ - a virtual IP address is allocated for the service (in an internal, private range)
+ - this IP address is reachable only from within the cluster (nodes and pods)
+ - our code can connect to the service using the original port number
+
+- `NodePort`
+
+ - a port is allocated for the service (by default, in the 30000-32768 range)
+ - that port is made available *on all our nodes* and anybody can connect to it
+ - our code must be changed to connect to that new port number
+
+These service types are always available.
+
+Under the hood: `kube-proxy` is using a userland proxy and a bunch of `iptables` rules.
+
+---
+
+## More service types
+
+- `LoadBalancer`
+
+ - an external load balancer is allocated for the service
+ - the load balancer is configured accordingly
+
(e.g.: a `NodePort` service is created, and the load balancer sends traffic to that port)
+
+- `ExternalName`
+
+ - the DNS entry managed by `kube-dns` will just be a `CNAME` to a provided record
+ - no port, no IP address, no nothing else is allocated
+
+The `LoadBalancer` type is currently only available on AWS, Azure, and GCE.
+
+---
+
+## Running containers with open ports
+
+- Since `ping` doesn't have anything to connect to, we'll have to run something else
+
+.exercise[
+
+- Start a bunch of ElasticSearch containers:
+ ```bash
+ kubectl run elastic --image=elasticsearch:2 --replicas=7
+ ```
+
+- Watch them being started:
+ ```bash
+ kubectl get pods -w
+ ```
+
+]
+
+The `-w` option "watches" events happening on the specified resources.
+
+Note: please DO NOT call the service `search`. It would collide with the TLD.
+
+---
+
+## Exposing our deployment
+
+- We'll create a default `ClusterIP` service
+
+.exercise[
+
+- Expose the ElasticSearch HTTP API port:
+ ```bash
+ kubectl expose deploy/elastic --port 9200
+ ```
+
+- Look up which IP address was allocated:
+ ```bash
+ kubectl get svc
+ ```
+
+]
+
+---
+
+## Services are layer 4 constructs
+
+- You can assign IP addresses to services, but they are still *layer 4*
+
+ (i.e. a service is not an IP address; it's an IP address + protocol + port)
+
+- This is caused by the current implementation of `kube-proxy`
+
+ (it relies on mechanisms that don't support layer 3)
+
+- As a result: you *have to* indicate the port number for your service
+
+- Running services with arbitrary port (or port ranges) requires hacks
+
+ (e.g. host networking mode)
+
+---
+
+## Testing our service
+
+- We will now send a few HTTP requests to our ElasticSearch pods
+
+.exercise[
+
+- Let's obtain the IP address that was allocated for our service, *programatically:*
+ ```bash
+ IP=$(kubectl get svc elastic -o go-template --template '{{ .spec.clusterIP }}')
+ ```
+
+- Send a few requests:
+ ```bash
+ curl http://$IP:9200/
+ ```
+
+]
+
+--
+
+Our requests are load balanced across multiple pods.
diff --git a/docs/kubectlrun.md b/docs/kubectlrun.md
new file mode 100644
index 00000000..3cb50c3f
--- /dev/null
+++ b/docs/kubectlrun.md
@@ -0,0 +1,197 @@
+# Running our first containers on Kubernetes
+
+- First things first: we cannot run a container
+
+--
+
+- We are going to run a pod, and in that pod there will be a single container
+
+--
+
+- In that container in the pod, we are going to run a simple `ping` command
+
+- Then we are going to start additional copies of the pod
+
+---
+
+## Starting a simple pod with `kubectl run`
+
+- We need to specify at least a *name* and the image we want to use
+
+.exercise[
+
+- Let's ping `goo.gl`:
+ ```bash
+ kubectl run pingpong --image alpine ping goo.gl
+ ```
+
+]
+
+--
+
+OK, what did just happen?
+
+---
+
+## Behind the scenes of `kubectl run`
+
+- Let's look at the resources that were created by `kubectl run`
+
+.exercise[
+
+- List most resource types:
+ ```bash
+ kubectl get all
+ ```
+
+]
+
+--
+
+We should see the following things:
+- `deploy/pingpong` (the *deployment* that we just created)
+- `rs/pingpong-xxxx` (a *replica set* created by the deployment)
+- `po/pingpong-yyyy` (a *pod* created by the replica set)
+
+---
+
+## Deployments, replica sets, and replication controllers
+
+- A *deployment* is a high-level construct
+
+ - allows scaling, rolling updates, rollbacks
+
+ - multiple deployments can be used together to implement a
+ [canary deployment](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments)
+
+ - delegates pods management to *replica sets*
+
+- A *replica set* is a low-level construct
+
+ - makes sure that a given number of identical pods are running
+
+ - allows scaling
+
+ - rarely used directly
+
+- A *replication controller* is the (deprecated) predecessor of a replica set
+
+---
+
+## Our `pingpong` deployment
+
+- `kubectl run` created a *deployment*, `deploy/pingpong`
+
+- That deployment created a *replica set*, `rs/pingpong-xxxx`
+
+- That replica set created a *pod*, `po/pingpong-yyyy`
+
+- We'll see later how these folks play together for:
+
+ - scaling
+
+ - high availability
+
+ - rolling updates
+
+---
+
+## Viewing container output
+
+- Let's use the `kubectl logs` command
+
+- We will pass either a *pod name*, or a *type/name*
+
+ (E.g. if we specify a deployment or replica set, it will get the first pod in it)
+
+- Unless specified otherwise, it will only show logs of the first container in the pod
+
+ (Good thing there's only one in ours!)
+
+.exercise[
+
+- View the result of our `ping` command:
+ ```bash
+ kubectl logs deploy/pingpong
+ ```
+
+]
+
+---
+
+## Streaming logs in real time
+
+- Just like `docker logs`, `kubectl logs` supports convenient options:
+
+ - `-f`/`--follow` to stream logs in real time (à la `tail -f`)
+
+ - `--tail` to indicate how many lines you want to see (from the end)
+
+ - `--since` to get logs only after a given timestamp
+
+.exercise[
+
+- View the latest logs of our `ping` command:
+ ```bash
+ kubectl logs deploy/pingpong --tail 1 --follow
+ ```
+
+]
+
+---
+
+## Scaling our application
+
+- We can create additional copies of our container (I mean, our pod) with `kubectl scale`
+
+.exercise[
+
+- Scale our `pingpong` deployment:
+ ```bash
+ kubectl scale deploy/pingpong --replicas 8
+ ```
+
+]
+
+Note: what if we tried to scale `rs/pingpong-xxxx`?
+
+We could! But the *deployment* would notice it right away, and scale back to the initial level.
+
+---
+
+## Viewing logs of multiple pods
+
+- When we specify a deployment name, only one single pod's logs are shown
+
+- We can view the logs of multiple pods by specifying a *selector*
+
+- A selector is a logic expression using *labels*
+
+- Conveniently, when you `kubectl run somename`, the associated objects have a `run=somename` label
+
+.exercise[
+
+- View the last line of log from all pods with the `run=pingpong` label:
+ ```bash
+ kubectl logs -l run=pingpong --tail 1
+ ```
+
+]
+
+Unfortunately, `--follow` cannot (yet) be used to stream the logs from multiple containers.
+
+---
+
+class: title
+
+.small[
+Meanwhile, at the Google NOC ...
+
+.small[
+Why the hell
+
+are we getting 1000 packets per second
+
+of ICMP ECHO traffic from EC2 ?!?
+]
+]
diff --git a/docs/kubectlscale.md b/docs/kubectlscale.md
new file mode 100644
index 00000000..b8789ca3
--- /dev/null
+++ b/docs/kubectlscale.md
@@ -0,0 +1,2 @@
+# Scaling a deployment
+
diff --git a/docs/ourapponkube.md b/docs/ourapponkube.md
new file mode 100644
index 00000000..c30648af
--- /dev/null
+++ b/docs/ourapponkube.md
@@ -0,0 +1,348 @@
+class: title
+
+Our app on Kube
+
+---
+
+## What's on the menu?
+
+In this part, we will:
+
+- **build** images for our app,
+
+- **ship** these images with a registry,
+
+- **run** deployments using these images,
+
+- expose these deployments so they can communicate with each other,
+
+- expose the web UI so we can access it from outside.
+
+---
+
+## The plan
+
+- Build on our control node (`node1`)
+
+- Tag images so that they are named `$REGISTRY/servicename`
+
+- Upload them to a registry
+
+- Create deployments using the images
+
+- Expose (with a ClusterIP) the services that need to communicate
+
+- Expose (with a NodePort) the WebUI
+
+---
+
+## Which registry do we want to use?
+
+- We could use the Docker Hub
+
+- Or a service offered by our cloud provider (GCR, ECR...)
+
+- Or we could just self-host that registry
+
+*We'll self-host the registry because it's the most generic solution for this workshop.*
+
+---
+
+## Using the open source registry
+
+- We need to run a `registry:2` container
+
(make sure you specify tag `:2` to run the new version!)
+
+- It will store images and layers to the local filesystem
+
(but you can add a config file to use S3, Swift, etc.)
+
+- Docker *requires* TLS when communicating with the registry
+
+ - unless for registries on `127.0.0.0/8` (i.e. `localhost`)
+
+ - or with the Engine flag `--insecure-registry`
+
+- Our strategy: publish the registry container on a NodePort,
+
so that it's available through `127.0.0.1:xxxxx` on each node
+
+---
+
+# Deploying a self-hosted registry
+
+- We will deploy a registry container, and expose it with a NodePort
+
+.exercise[
+
+- Create the registry service:
+ ```bash
+ kubectl deploy registry --image=registry:2
+ ```
+
+- Expose it on a NodePort:
+ ```bash
+ kubectl expose deploy/registry --port=5000 --type=NodePort
+ ```
+
+]
+
+---
+
+## Connecting to our registry
+
+- We need to find out which port has been allocated
+
+.exercise[
+
+- View the service details:
+ ```bash
+ kubectl describe svc/registry
+ ```
+
+- Get the port number programmatically:
+ ```bash
+ NODEPORT=$(kubectl get svc/registry -o json | jq .spec.ports[0].nodePort)
+ REGISTRY=127.0.0.1:$NODEPORT
+ ```
+
+]
+
+---
+
+## Testing our registry
+
+- A convenient Docker registry API route to remember is `/v2/_catalog`
+
+.exercise[
+
+- View the repositories currently held in our registry:
+ ```bash
+ curl $REGISTRY/v2/_catalog
+ ```
+
+]
+
+--
+
+We should see:
+```json
+{"repositories":[]}
+```
+
+---
+
+## Testing our local registry
+
+- We can retag a small image, and push it to the registry
+
+.exercise[
+
+- Make sure we have the busybox image, and retag it:
+ ```bash
+ docker pull busybox
+ docker tag busybox $REGISTRY/busybox
+ ```
+
+- Push it:
+ ```bash
+ docker push $REGISTRY/busybox
+ ```
+
+]
+
+---
+
+## Checking again what's on our local registry
+
+- Let's use the same endpoint as before
+
+.exercise[
+
+- Ensure that our busybox image is now in the local registry:
+ ```bash
+ curl $REGISTRY/v2/_catalog
+ ```
+
+]
+
+The curl command should now output:
+```json
+{"repositories":["busybox"]}
+```
+
+---
+
+## Building and pushing our images
+
+- We are going to use a convenient feature of Docker Compose
+
+.exercise[
+
+- Go to the `stacks` directory:
+ ```bash
+ cd ~/orchestration-workshop/stacks
+ ```
+
+- Build and push the images:
+ ```bash
+ docker-compose -f dockercoins.yml build
+ docker-compose -f dockercoins.yml push
+ ```
+
+]
+
+Let's have a look at the `dockercoins.yml` file while this is building and pushing.
+
+---
+
+```yaml
+version: "3"
+
+services:
+ rng:
+ build: dockercoins/rng
+ image: ${REGISTRY-127.0.0.1:5000}/rng:${TAG-latest}
+ deploy:
+ mode: global
+ ...
+ redis:
+ image: redis
+ ...
+ worker:
+ build: dockercoins/worker
+ image: ${REGISTRY-127.0.0.1:5000}/worker:${TAG-latest}
+ ...
+ deploy:
+ replicas: 10
+```
+
+.warning[Just in case you were wondering ... Docker "services" are not Kubernetes "services".]
+
+---
+
+## Deploying all the things
+
+- We can now deploy our code (as well as a redis instance)
+
+.exercise[
+
+- Deploy `redis`:
+ ```bash
+ kubectl deploy redis --image=redis
+ ```
+
+- Deploy everything else:
+ ```bash
+ for SERVICE in hasher rng webui worker; do
+ kubectl deploy $SERVICE --image=$REGISTRY/$SERVICE
+ done
+ ```
+
+]
+
+---
+
+## Is this working?
+
+- After waiting for the deployment to complete, let's look at the logs!
+
+ (Hint: use `kubectl get deploy -w` to watch deployment events)
+
+.exercise[
+
+- Look at some logs:
+ ```bash
+ kubectl logs deploy/rng
+ kubectl logs deploy/worker
+ ```
+
+]
+
+--
+
+🤔 `rng` is fine ... But not `worker`.
+
+--
+
+💡 Oh right! We forgot to `expose`.
+
+---
+
+# Exposing services internally
+
+- Three deployments need to be reachable by others: `hasher`, `redis`, `rng`
+
+- `worker` doesn't need to be exposed
+
+- `webui` will be dealth with later
+
+.exercise[
+
+- Expose each service, specifying the right port:
+ ```bash
+ kubectl expose redis --port 6379
+ kubectl expose rng --port 80
+ kubectl expose hasher --port 80
+ ```
+
+]
+
+---
+
+## Is this working yet?
+
+- The `worker` has an infinite loop, that retries 10 seconds after an error
+
+.exercise[
+
+- Stream the worker's logs:
+ ```bash
+ kubectl logs deploy/worker --follow
+ ```
+
+ (Give it about 10 seconds to recover)
+
+]
+
+--
+
+We should now see the `worker`, well, working happily.
+
+---
+
+# Exposing services for external access
+
+- Now we would like to access the Web UI
+
+- We will expose it with a `NodePort`
+
+ (just like we did for the registry)
+
+.exercise[
+
+- Create a `NodePort` service for the Web UI:
+ ```bash
+ kubectl expose deploy/webui --type=NodePort --port=80
+ ```
+
+- Check the port that was allocated:
+ ```bash
+ kubectl get svc
+ ```
+
+]
+
+---
+
+## Accessing the web UI
+
+- We can now connect to *any node*, on the allocated node port, to view the web UI
+
+.exercise[
+
+- Open the web UI in your browser (http://node-ip-address:3xxxx/)
+
+]
+
+--
+
+*Alright, we're back to where we started, when we were running on a single node!*
\ No newline at end of file
diff --git a/docs/setup-k8s.md b/docs/setup-k8s.md
new file mode 100644
index 00000000..86178f82
--- /dev/null
+++ b/docs/setup-k8s.md
@@ -0,0 +1,67 @@
+# Setting up Kubernetes
+
+- How did we setup these Kubernetes clusters that we're using?
+
+--
+
+- We used `kubeadm` on "fresh" EC2 instances with Ubuntu 16.04 LTS
+
+ 1. Install Docker
+
+ 2. Install Kubernetes packages
+
+ 3. Run `kubeadm init` on the master node
+
+ 4. Setup Weave (the overlay network)
+
+ (that step is just one `kubectl apply` command; discussed later)
+
+ 5. Run `kubeadm join` on the other nodes (with the token produced by `kubeadm init`)
+
+ 6. Copy the configuration file generated by `kubeadm init`
+
+---
+
+## `kubeadm` drawbacks
+
+- Doesn't setup Docker or any other container engine
+
+- Doesn't setup the overlay network
+
+- Scripting is complex
+
+ (because extracting the token requires advanced `kubectl` commands)
+
+- Doesn't setup multi-master (no high availability)
+
+--
+
+- It's still twice as much steps as setting up a Swarm cluster 😕
+
+---
+
+## Other deployment options
+
+- If you are on Google Cloud:
+ [GKE](https://cloud.google.com/container-engine/)
+
+ Empirically the best Kubernetes deployment out there
+
+- If you are on AWS:
+ [kops](https://github.com/kubernetes/kops)
+
+ ... But with AWS re:invent just around the corner, expect some changes
+
+- On a local machine:
+ [minikube](https://kubernetes.io/docs/getting-started-guides/minikube/)
+
+ FIXME
+
+- If you want something customizable:
+ [kubicorn](https://github.com/kris-nova/kubicorn)
+
+ Probably the closest to a multi-cloud/hybrid solution so far, but in development
+
+- Also, many commercial options!
+
+ FIXME