diff --git a/k8s/persistent-consul.yaml b/k8s/persistent-consul.yaml
new file mode 100644
index 00000000..ff9d5955
--- /dev/null
+++ b/k8s/persistent-consul.yaml
@@ -0,0 +1,95 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: consul
+rules:
+ - apiGroups: [ "" ]
+ resources: [ pods ]
+ verbs: [ get, list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: consul
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: consul
+subjects:
+ - kind: ServiceAccount
+ name: consul
+ namespace: orange
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: consul
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: consul
+spec:
+ ports:
+ - port: 8500
+ name: http
+ selector:
+ app: consul
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: consul
+spec:
+ serviceName: consul
+ replicas: 3
+ selector:
+ matchLabels:
+ app: consul
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+ template:
+ metadata:
+ labels:
+ app: consul
+ spec:
+ serviceAccountName: consul
+ affinity:
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - labelSelector:
+ matchExpressions:
+ - key: app
+ operator: In
+ values:
+ - consul
+ topologyKey: kubernetes.io/hostname
+ terminationGracePeriodSeconds: 10
+ containers:
+ - name: consul
+ image: "consul:1.4.4"
+ volumeMounts:
+ - name: data
+ mountPath: /consul/data
+ args:
+ - "agent"
+ - "-bootstrap-expect=3"
+ - "-retry-join=provider=k8s namespace=orange label_selector=\"app=consul\""
+ - "-client=0.0.0.0"
+ - "-data-dir=/consul/data"
+ - "-server"
+ - "-ui"
+ lifecycle:
+ preStop:
+ exec:
+ command:
+ - /bin/sh
+ - -c
+ - consul leave
diff --git a/k8s/volumes-for-consul.yaml b/k8s/volumes-for-consul.yaml
new file mode 100644
index 00000000..8d75e8ea
--- /dev/null
+++ b/k8s/volumes-for-consul.yaml
@@ -0,0 +1,70 @@
+---
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: consul-node2
+ annotations:
+ node: node2
+spec:
+ capacity:
+ storage: 10Gi
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Delete
+ local:
+ path: /mnt/consul
+ nodeAffinity:
+ required:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/hostname
+ operator: In
+ values:
+ - node2
+---
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: consul-node3
+ annotations:
+ node: node3
+spec:
+ capacity:
+ storage: 10Gi
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Delete
+ local:
+ path: /mnt/consul
+ nodeAffinity:
+ required:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/hostname
+ operator: In
+ values:
+ - node3
+---
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: consul-node4
+ annotations:
+ node: node4
+spec:
+ capacity:
+ storage: 10Gi
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Delete
+ local:
+ path: /mnt/consul
+ nodeAffinity:
+ required:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/hostname
+ operator: In
+ values:
+ - node4
+
diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh
index 6daec799..f668ced0 100644
--- a/prepare-vms/lib/commands.sh
+++ b/prepare-vms/lib/commands.sh
@@ -248,6 +248,14 @@ EOF"
sudo tar -C /usr/local/bin -zx ship
fi"
+ # Install the AWS IAM authenticator
+ pssh "
+ if [ ! -x /usr/local/bin/aws-iam-authenticator ]; then
+ ##VERSION##
+ sudo curl -o /usr/local/bin/aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.12.7/2019-03-27/bin/linux/amd64/aws-iam-authenticator
+ sudo chmod +x /usr/local/bin/aws-iam-authenticator
+ fi"
+
sep "Done"
}
@@ -383,6 +391,15 @@ _cmd_retag() {
aws_tag_instances $OLDTAG $NEWTAG
}
+_cmd ssh "Open an SSH session to the first node of a tag"
+_cmd_ssh() {
+ TAG=$1
+ need_tag
+ IP=$(head -1 tags/$TAG/ips.txt)
+ info "Logging into $IP"
+ ssh docker@$IP
+}
+
_cmd start "Start a group of VMs"
_cmd_start() {
while [ ! -z "$*" ]; do
@@ -481,12 +498,12 @@ _cmd_helmprom() {
if i_am_first_node; then
kubectl -n kube-system get serviceaccount helm ||
kubectl -n kube-system create serviceaccount helm
- helm init --service-account helm
+ sudo -u docker -H helm init --service-account helm
kubectl get clusterrolebinding helm-can-do-everything ||
kubectl create clusterrolebinding helm-can-do-everything \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:helm
- helm upgrade --install prometheus stable/prometheus \
+ sudo -u docker -H helm upgrade --install prometheus stable/prometheus \
--namespace kube-system \
--set server.service.type=NodePort \
--set server.service.nodePort=30090 \
diff --git a/slides/containers/Ambassadors.md b/slides/containers/Ambassadors.md
index facef966..dba3398d 100644
--- a/slides/containers/Ambassadors.md
+++ b/slides/containers/Ambassadors.md
@@ -186,22 +186,48 @@ Different deployments will use different underlying technologies.
---
-## Section summary
+## Some popular service meshes
-We've learned how to:
+... And related projects:
-* Understand the ambassador pattern and what it is used for (service portability).
-
-For more information about the ambassador pattern, including demos on Swarm and ECS:
-
-* AWS re:invent 2015 [DVO317](https://www.youtube.com/watch?v=7CZFpHUPqXw)
-
-* [SwarmWeek video about Swarm+Compose](https://youtube.com/watch?v=qbIvUvwa6As)
-
-Some services meshes and related projects:
-
-* [Istio](https://istio.io/)
-
-* [Linkerd](https://linkerd.io/)
+* [Consul Connect](https://www.consul.io/docs/connect/index.html)
+
+ Transparently secures service-to-service connections with mTLS.
* [Gloo](https://gloo.solo.io/)
+
+ API gateway that can interconnect applications on VMs, containers, and serverless.
+
+* [Istio](https://istio.io/)
+
+ A popular service mesh.
+
+* [Linkerd](https://linkerd.io/)
+
+ Another popular service mesh.
+
+---
+
+## Learning more about service meshes
+
+A few blog posts about service meshes:
+
+* [Containers, microservices, and service meshes](http://jpetazzo.github.io/2019/05/17/containers-microservices-service-meshes/)
+
+ Provides historical context: how did we do before service meshes were invented?
+
+* [Do I Need a Service Mesh?](https://www.nginx.com/blog/do-i-need-a-service-mesh/)
+
+ Explains the purpose of service meshes. Illustrates some NGINX features.
+
+* [Do you need a service mesh?](https://www.oreilly.com/ideas/do-you-need-a-service-mesh)
+
+ Includes high-level overview and definitions.
+
+* [What is Service Mesh and Why Do We Need It?](https://containerjournal.com/2018/12/12/what-is-service-mesh-and-why-do-we-need-it/)
+
+ Includes a step-by-step demo of Linkerd.
+
+And a video:
+
+* [What is a Service Mesh, and Do I Need One When Developing Microservices?](https://www.datawire.io/envoyproxy/service-mesh/)
diff --git a/slides/containers/Container_Network_Model.md b/slides/containers/Container_Network_Model.md
index dec982fe..fd77c3bd 100644
--- a/slides/containers/Container_Network_Model.md
+++ b/slides/containers/Container_Network_Model.md
@@ -528,7 +528,9 @@ Very short instructions:
- `docker network create mynet --driver overlay`
- `docker service create --network mynet myimage`
-See https://jpetazzo.github.io/container.training for all the deets about clustering!
+If you want to learn more about Swarm mode, you can check
+[this video](https://www.youtube.com/watch?v=EuzoEaE6Cqs)
+or [these slides](https://container.training/swarm-selfpaced.yml.html).
---
diff --git a/slides/containers/Exercise_Composefile.md b/slides/containers/Exercise_Composefile.md
new file mode 100644
index 00000000..703b21b2
--- /dev/null
+++ b/slides/containers/Exercise_Composefile.md
@@ -0,0 +1,5 @@
+# Exercise — writing a Compose file
+
+Let's write a Compose file for the wordsmith app!
+
+The code is at: https://github.com/jpetazzo/wordsmith
diff --git a/slides/containers/Exercise_Dockerfile_Advanced.md b/slides/containers/Exercise_Dockerfile_Advanced.md
new file mode 100644
index 00000000..0ca16d75
--- /dev/null
+++ b/slides/containers/Exercise_Dockerfile_Advanced.md
@@ -0,0 +1,9 @@
+# Exercise — writing better Dockerfiles
+
+Let's update our Dockerfiles to leverage multi-stage builds!
+
+The code is at: https://github.com/jpetazzo/wordsmith
+
+Use a different tag for these images, so that we can compare their sizes.
+
+What's the size difference between single-stage and multi-stage builds?
diff --git a/slides/containers/Exercise_Dockerfile_Basic.md b/slides/containers/Exercise_Dockerfile_Basic.md
new file mode 100644
index 00000000..c6cad7b4
--- /dev/null
+++ b/slides/containers/Exercise_Dockerfile_Basic.md
@@ -0,0 +1,5 @@
+# Exercise — writing Dockerfiles
+
+Let's write Dockerfiles for an existing application!
+
+The code is at: https://github.com/jpetazzo/wordsmith
diff --git a/slides/containers/First_Containers.md b/slides/containers/First_Containers.md
index aa47a764..21d8e6f4 100644
--- a/slides/containers/First_Containers.md
+++ b/slides/containers/First_Containers.md
@@ -203,4 +203,90 @@ bash: figlet: command not found
* The basic Ubuntu image was used, and `figlet` is not here.
-* We will see in the next chapters how to bake a custom image with `figlet`.
+---
+
+## Where's my container?
+
+* Can we reuse that container that we took time to customize?
+
+ *We can, but that's not the default workflow with Docker.*
+
+* What's the default workflow, then?
+
+ *Always start with a fresh container.*
+
+ *If we need something installed in our container, build a custom image.*
+
+* That seems complicated!
+
+ *We'll see that it's actually pretty easy!*
+
+* And what's the point?
+
+ *This puts a strong emphasis on automation and repeatability. Let's see why ...*
+
+---
+
+## Pets vs. Cattle
+
+* In the "pets vs. cattle" metaphor, there are two kinds of servers.
+
+* Pets:
+
+ * have distinctive names and unique configurations
+
+ * when they have an outage, we do everything we can to fix them
+
+* Cattle:
+
+ * have generic names (e.g. with numbers) and generic configuration
+
+ * configuration is enforced by configuration management, golden images ...
+
+ * when they have an outage, we can replace them immediately with a new server
+
+* What's the connection with Docker and containers?
+
+---
+
+## Local development environments
+
+* When we use local VMs (with e.g. VirtualBox or VMware), our workflow looks like this:
+
+ * create VM from base template (Ubuntu, CentOS...)
+
+ * install packages, set up environment
+
+ * work on project
+
+ * when done, shutdown VM
+
+ * next time we need to work on project, restart VM as we left it
+
+ * if we need to tweak the environment, we do it live
+
+* Over time, the VM configuration evolves, diverges.
+
+* We don't have a clean, reliable, deterministic way to provision that environment.
+
+---
+
+## Local development with Docker
+
+* With Docker, the workflow looks like this:
+
+ * create container image with our dev environment
+
+ * run container with that image
+
+ * work on project
+
+ * when done, shutdown container
+
+ * next time we need to work on project, start a new container
+
+ * if we need to tweak the environment, we create a new image
+
+* We have a clear definition of our environment, and can share it reliably with others.
+
+* Let's see in the next chapters how to bake a custom image with `figlet`!
diff --git a/slides/containers/Initial_Images.md b/slides/containers/Initial_Images.md
index 8c6a5475..0e601e57 100644
--- a/slides/containers/Initial_Images.md
+++ b/slides/containers/Initial_Images.md
@@ -70,8 +70,9 @@ class: pic
* An image is a read-only filesystem.
-* A container is an encapsulated set of processes running in a
- read-write copy of that filesystem.
+* A container is an encapsulated set of processes,
+
+ running in a read-write copy of that filesystem.
* To optimize container boot time, *copy-on-write* is used
instead of regular copy.
@@ -177,8 +178,11 @@ Let's explain each of them.
## Root namespace
-The root namespace is for official images. They are put there by Docker Inc.,
-but they are generally authored and maintained by third parties.
+The root namespace is for official images.
+
+They are gated by Docker Inc.
+
+They are generally authored and maintained by third parties.
Those images include:
@@ -188,7 +192,7 @@ Those images include:
* Ready-to-use components and services, like redis, postgresql...
-* Over 130 at this point!
+* Over 150 at this point!
---
diff --git a/slides/containers/Orchestration_Overview.md b/slides/containers/Orchestration_Overview.md
index 986d70a3..1ab25a80 100644
--- a/slides/containers/Orchestration_Overview.md
+++ b/slides/containers/Orchestration_Overview.md
@@ -6,8 +6,6 @@ In this chapter, we will:
* Present (from a high-level perspective) some orchestrators.
-* Show one orchestrator (Kubernetes) in action.
-
---
class: pic
diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml
index 98281b5b..b137fb59 100644
--- a/slides/intro-fullday.yml
+++ b/slides/intro-fullday.yml
@@ -30,27 +30,11 @@ chapters:
- containers/Building_Images_With_Dockerfiles.md
- containers/Cmd_And_Entrypoint.md
- - containers/Copying_Files_During_Build.md
- - |
- # Exercise — writing Dockerfiles
-
- Let's write Dockerfiles for an existing application!
-
- The code is at: https://github.com/jpetazzo/wordsmith
-
+ - containers/Exercise_Dockerfile_Basic.md
- containers/Multi_Stage_Builds.md
- containers/Publishing_To_Docker_Hub.md
- containers/Dockerfile_Tips.md
- - |
- # Exercise — writing better Dockerfiles
-
- Let's update our Dockerfiles to leverage multi-stage builds!
-
- The code is at: https://github.com/jpetazzo/wordsmith
-
- Use a different tag for these images, so that we can compare their sizes.
-
- What's the size difference between single-stage and multi-stage builds?
-
+ - containers/Exercise_Dockerfile_Advanced.md
- - containers/Naming_And_Inspecting.md
- containers/Labels.md
- containers/Getting_Inside.md
@@ -64,13 +48,7 @@ chapters:
- containers/Windows_Containers.md
- containers/Working_With_Volumes.md
- containers/Compose_For_Dev_Stacks.md
- - |
- # Exercise — writing a Compose file
-
- Let's write a Compose file for the wordsmith app!
-
- The code is at: https://github.com/jpetazzo/wordsmith
-
+ - containers/Exercise_Composefile.md
- - containers/Docker_Machine.md
- containers/Advanced_Dockerfiles.md
- containers/Application_Configuration.md
diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml
index 6020ed78..5ebc32c5 100644
--- a/slides/intro-selfpaced.yml
+++ b/slides/intro-selfpaced.yml
@@ -30,9 +30,11 @@ chapters:
- containers/Building_Images_With_Dockerfiles.md
- containers/Cmd_And_Entrypoint.md
- containers/Copying_Files_During_Build.md
+ - containers/Exercise_Dockerfile_Basic.md
- - containers/Multi_Stage_Builds.md
- containers/Publishing_To_Docker_Hub.md
- containers/Dockerfile_Tips.md
+ - containers/Exercise_Dockerfile_Advanced.md
- - containers/Naming_And_Inspecting.md
- containers/Labels.md
- containers/Getting_Inside.md
@@ -45,6 +47,7 @@ chapters:
- containers/Windows_Containers.md
- containers/Working_With_Volumes.md
- containers/Compose_For_Dev_Stacks.md
+ - containers/Exercise_Composefile.md
- containers/Docker_Machine.md
- - containers/Advanced_Dockerfiles.md
- containers/Application_Configuration.md
diff --git a/slides/k8s/authn-authz.md b/slides/k8s/authn-authz.md
index 96aa5f92..d4dd70bf 100644
--- a/slides/k8s/authn-authz.md
+++ b/slides/k8s/authn-authz.md
@@ -407,7 +407,7 @@ class: extra-details
- We are going to create a service account
-- We will use an existing cluster role (`view`)
+- We will use a default cluster role (`view`)
- We will bind together this role and this service account
@@ -574,6 +574,51 @@ It's important to note a couple of details in these flags ...
class: extra-details
+## Where does this `view` role come from?
+
+- Kubernetes defines a number of ClusterRoles intended to be bound to users
+
+- `cluster-admin` can do *everything* (think `root` on UNIX)
+
+- `admin` can do *almost everything* (except e.g. changing resource quotas and limits)
+
+- `edit` is similar to `admin`, but cannot view or edit permissions
+
+- `view` has read-only access to most resources, except permissions and secrets
+
+*In many situations, these roles will be all you need.*
+
+*You can also customize them if needed!*
+
+---
+
+class: extra-details
+
+## Customizing the default roles
+
+- If you need to *add* permissions to these default roles (or others),
+
+ you can do it through the [ClusterRole Aggregation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) mechanism
+
+- This happens by creating a ClusterRole with the following labels:
+ ```yaml
+ metadata:
+ labels:
+ rbac.authorization.k8s.io/aggregate-to-admin: "true"
+ rbac.authorization.k8s.io/aggregate-to-edit: "true"
+ rbac.authorization.k8s.io/aggregate-to-view: "true"
+ ```
+
+- This ClusterRole permissions will be added to `admin`/`edit`/`view` respectively
+
+- This is particulary useful when using CustomResourceDefinitions
+
+ (since Kubernetes cannot guess which resources are sensitive and which ones aren't)
+
+---
+
+class: extra-details
+
## Where do our permissions come from?
- When interacting with the Kubernetes API, we are using a client certificate
diff --git a/slides/k8s/cluster-upgrade.md b/slides/k8s/cluster-upgrade.md
index 57bce466..48197842 100644
--- a/slides/k8s/cluster-upgrade.md
+++ b/slides/k8s/cluster-upgrade.md
@@ -166,7 +166,7 @@
- Upgrade kubelet:
```bash
- apt install kubelet=1.14.1-00
+ apt install kubelet=1.14.2-00
```
]
@@ -267,7 +267,7 @@
- Perform the upgrade:
```bash
- sudo kubeadm upgrade apply v1.14.1
+ sudo kubeadm upgrade apply v1.14.2
```
]
@@ -287,8 +287,8 @@
- Download the configuration on each node, and upgrade kubelet:
```bash
for N in 1 2 3; do
- ssh node$N sudo kubeadm upgrade node config --kubelet-version v1.14.1
- ssh node $N sudo apt install kubelet=1.14.1-00
+ ssh node$N sudo kubeadm upgrade node config --kubelet-version v1.14.2
+ ssh node $N sudo apt install kubelet=1.14.2-00
done
```
]
@@ -297,7 +297,7 @@
## Checking what we've done
-- All our nodes should now be updated to version 1.14.1
+- All our nodes should now be updated to version 1.14.2
.exercise[
diff --git a/slides/k8s/create-chart.md b/slides/k8s/create-chart.md
index 35ea3878..4d7731d2 100644
--- a/slides/k8s/create-chart.md
+++ b/slides/k8s/create-chart.md
@@ -69,3 +69,46 @@
`Error: release loitering-otter failed: services "hasher" already exists`
- To avoid naming conflicts, we will deploy the application in another *namespace*
+
+---
+
+## Switching to another namespace
+
+- We can create a new namespace and switch to it
+
+ (Helm will automatically use the namespace specified in our context)
+
+- We can also tell Helm which namespace to use
+
+.exercise[
+
+- Tell Helm to use a specific namespace:
+ ```bash
+ helm install dockercoins --namespace=magenta
+ ```
+
+]
+
+---
+
+## Checking our new copy of DockerCoins
+
+- We can check the worker logs, or the web UI
+
+.exercise[
+
+- Retrieve the NodePort number of the web UI:
+ ```bash
+ kubectl get service webui --namespace=magenta
+ ```
+
+- Open it in a web browser
+
+- Look at the worker logs:
+ ```bash
+ kubectl logs deploy/worker --tail=10 --follow --namespace=magenta
+ ```
+
+]
+
+Note: it might take a minute or two for the worker to start.
diff --git a/slides/k8s/extending-api.md b/slides/k8s/extending-api.md
index c9b126cd..2a266e04 100644
--- a/slides/k8s/extending-api.md
+++ b/slides/k8s/extending-api.md
@@ -61,7 +61,7 @@ There are many possibilities!
- creates a new custom type, `Remote`, exposing a git+ssh server
- - deploy by pushing YAML or Helm Charts to that remote
+ - deploy by pushing YAML or Helm charts to that remote
- Replacing built-in types with CRDs
@@ -87,7 +87,11 @@ There are many possibilities!
(and take action when they are created/updated)
-*Example: [YAML to install the gitkube CRD](https://storage.googleapis.com/gitkube/gitkube-setup-stable.yaml)*
+*
+Examples:
+[YAML to install the gitkube CRD](https://storage.googleapis.com/gitkube/gitkube-setup-stable.yaml),
+[YAML to install a redis operator CRD](https://github.com/amaizfinance/redis-operator/blob/master/deploy/crds/k8s_v1alpha1_redis_crd.yaml)
+*
---
diff --git a/slides/k8s/gitworkflows.md b/slides/k8s/gitworkflows.md
index a009ea69..4bccea72 100644
--- a/slides/k8s/gitworkflows.md
+++ b/slides/k8s/gitworkflows.md
@@ -234,6 +234,6 @@
(see the [documentation](https://github.com/hasura/gitkube/blob/master/docs/remote.md) for more details)
-- Gitkube can also deploy Helm Charts
+- Gitkube can also deploy Helm charts
(instead of raw YAML files)
diff --git a/slides/k8s/kubectlexpose.md b/slides/k8s/kubectlexpose.md
index 124ef922..baa7f3f5 100644
--- a/slides/k8s/kubectlexpose.md
+++ b/slides/k8s/kubectlexpose.md
@@ -276,3 +276,21 @@ error: the server doesn't have a resource type "endpoint"
- There is no `endpoint` object: `type Endpoints struct`
- The type doesn't represent a single endpoint, but a list of endpoints
+
+---
+
+## Exposing services to the outside world
+
+- The default type (ClusterIP) only works for internal traffic
+
+- If we want to accept external traffic, we can use one of these:
+
+ - NodePort (expose a service on a TCP port between 30000-32768)
+
+ - LoadBalancer (provision a cloud load balancer for our service)
+
+ - ExternalIP (use one node's external IP address)
+
+ - Ingress (a special mechanism for HTTP services)
+
+*We'll see NodePorts and Ingresses more in detail later.*
diff --git a/slides/k8s/kubectlget.md b/slides/k8s/kubectlget.md
index d1c034bb..14a0bff4 100644
--- a/slides/k8s/kubectlget.md
+++ b/slides/k8s/kubectlget.md
@@ -104,7 +104,7 @@
## Introspection vs. documentation
-- We can access the same information by reading the [API documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/)
+- We can access the same information by reading the [API documentation](https://kubernetes.io/docs/reference/#api-reference)
- The API documentation is usually easier to read, but:
diff --git a/slides/k8s/kustomize.md b/slides/k8s/kustomize.md
index b45f0491..60641b4a 100644
--- a/slides/k8s/kustomize.md
+++ b/slides/k8s/kustomize.md
@@ -14,15 +14,15 @@
## Differences with Helm
-- Helm Charts use placeholders `{{ like.this }}`
+- Helm charts use placeholders `{{ like.this }}`
- Kustomize "bases" are standard Kubernetes YAML
- It is possible to use an existing set of YAML as a Kustomize base
-- As a result, writing a Helm Chart is more work ...
+- As a result, writing a Helm chart is more work ...
-- ... But Helm Charts are also more powerful; e.g. they can:
+- ... But Helm charts are also more powerful; e.g. they can:
- use flags to conditionally include resources or blocks
@@ -88,11 +88,11 @@
- Change to a new directory:
```bash
- mkdir ~/kubercoins
- cd ~/kubercoins
+ mkdir ~/kustomcoins
+ cd ~/kustomcoins
```
-- Run `ship init` with the kubercoins repository:
+- Run `ship init` with the kustomcoins repository:
```bash
ship init https://github.com/jpetazzo/kubercoins
```
@@ -146,3 +146,49 @@
- We will create a new copy of DockerCoins in another namespace
+---
+
+## Deploy DockerCoins with Kustomize
+
+.exercise[
+
+- Create a new namespace:
+ ```bash
+ kubectl create namespace kustomcoins
+ ```
+
+- Deploy DockerCoins:
+ ```bash
+ kubectl apply -f rendered.yaml --namespace=kustomcoins
+ ```
+
+- Or, with Kubernetes 1.14, you can also do this:
+ ```bash
+ kubectl apply -k overlays/ship --namespace=kustomcoins
+ ```
+
+]
+
+---
+
+## Checking our new copy of DockerCoins
+
+- We can check the worker logs, or the web UI
+
+.exercise[
+
+- Retrieve the NodePort number of the web UI:
+ ```bash
+ kubectl get service webui --namespace=kustomcoins
+ ```
+
+- Open it in a web browser
+
+- Look at the worker logs:
+ ```bash
+ kubectl logs deploy/worker --tail=10 --follow --namespace=kustomcoins
+ ```
+
+]
+
+Note: it might take a minute or two for the worker to start.
diff --git a/slides/k8s/lastwords-admin.md b/slides/k8s/lastwords-admin.md
index 5bd12285..4fbde579 100644
--- a/slides/k8s/lastwords-admin.md
+++ b/slides/k8s/lastwords-admin.md
@@ -120,7 +120,7 @@
- Team "build" ships ready-to-run manifests
- (YAML, Helm Charts, Kustomize ...)
+ (YAML, Helm charts, Kustomize ...)
- Team "run" adjusts some parameters and monitors the application
diff --git a/slides/k8s/local-persistent-volumes.md b/slides/k8s/local-persistent-volumes.md
new file mode 100644
index 00000000..b7718bd0
--- /dev/null
+++ b/slides/k8s/local-persistent-volumes.md
@@ -0,0 +1,244 @@
+# Local Persistent Volumes
+
+- We want to run that Consul cluster *and* actually persist data
+
+- But we don't have a distributed storage system
+
+- We are going to use local volumes instead
+
+ (similar conceptually to `hostPath` volumes)
+
+- We can use local volumes without installing extra plugins
+
+- However, they are tied to a node
+
+- If that node goes down, the volume becomes unavailable
+
+---
+
+## With or without dynamic provisioning
+
+- We will deploy a Consul cluster *with* persistence
+
+- That cluster's StatefulSet will create PVCs
+
+- These PVCs will remain unbound¹, until we will create local volumes manually
+
+ (we will basically do the job of the dynamic provisioner)
+
+- Then, we will see how to automate that with a dynamic provisioner
+
+.footnote[¹Unbound = without an associated Persistent Volume.]
+
+---
+
+## Work in a separate namespace
+
+- To avoid conflicts with existing resources, let's create and use a new namespace
+
+.exercise[
+
+- Create a new namespace:
+ ```bash
+ kubectl create namespace orange
+ ```
+
+- Switch to that namespace:
+ ```bash
+ kns orange
+ ```
+
+]
+
+.warning[Make sure to call that namespace `orange`, because that name is hardcoded in the YAML files.]
+
+---
+
+## Deploying Consul
+
+- We will use a slightly different YAML file
+
+- The only differences between that file and the previous one are:
+
+ - `volumeClaimTemplate` defined in the Stateful Set spec
+
+ - the corresponding `volumeMounts` in the Pod spec
+
+ - the namespace `orange` used for discovery of Pods
+
+.exercise[
+
+- Apply the persistent Consul YAML file:
+ ```bash
+ kubectl apply -f ~/container.training/k8s/persistent-consul.yaml
+ ```
+
+]
+
+---
+
+## Observing the situation
+
+- Let's look at Persistent Volume Claims and Pods
+
+.exercise[
+
+- Check that we now have an unbound Persistent Volume Claim:
+ ```bash
+ kubectl get pvc
+ ```
+
+- We don't have any Persistent Volume:
+ ```bash
+ kubectl get pv
+ ```
+
+- The Pod `consul-0` is not scheduled yet:
+ ```bash
+ kubectl get pods -o wide
+ ```
+
+]
+
+*Hint: leave these commands running with `-w` in different windows.*
+
+---
+
+## Explanations
+
+- In a Stateful Set, the Pods are started one by one
+
+- `consul-1` won't be created until `consul-0` is running
+
+- `consul-0` has a dependency on an unbound Persistent Volume Claim
+
+- The scheduler won't schedule the Pod until the PVC is bound
+
+ (because the PVC might be bound to a volume that is only available on a subset of nodes; for instance EBS are tied to an availability zone)
+
+---
+
+## Creating Persistent Volumes
+
+- Let's create 3 local directories (`/mnt/consul`) on node2, node3, node4
+
+- Then create 3 Persistent Volumes corresponding to these directories
+
+.exercise[
+
+- Create the local directories:
+ ```bash
+ for NODE in node2 node3 node4; do
+ ssh $NODE sudo mkdir -p /mnt/consul
+ done
+ ```
+
+- Create the PV objects:
+ ```bash
+ kubectl apply -f ~/container.training/k8s/volumes-for-consul.yaml
+ ```
+
+]
+
+---
+
+## Check our Consul cluster
+
+- The PVs that we created will be automatically matched with the PVCs
+
+- Once a PVC is bound, its pod can start normally
+
+- Once the pod `consul-0` has started, `consul-1` can be created, etc.
+
+- Eventually, our Consul cluster is up, and backend by "persistent" volumes
+
+.exercise[
+
+- Check that our Consul clusters has 3 members indeed:
+ ```bash
+ kubectl exec consul-0 consul members
+ ```
+
+]
+
+---
+
+## Devil is in the details (1/2)
+
+- The size of the Persistent Volumes is bogus
+
+ (it is used when matching PVs and PVCs together, but there is no actual quota or limit)
+
+---
+
+## Devil is in the details (2/2)
+
+- This specific example worked because we had exactly 1 free PV per node:
+
+ - if we had created multiple PVs per node ...
+
+ - we could have ended with two PVCs bound to PVs on the same node ...
+
+ - which would have required two pods to be on the same node ...
+
+ - which is forbidden by the anti-affinity constraints in the StatefulSet
+
+- To avoid that, we need to associated the PVs with a Storage Class that has:
+ ```yaml
+ volumeBindingMode: WaitForFirstConsumer
+ ```
+ (this means that a PVC will be bound to a PV only after being used by a Pod)
+
+- See [this blog post](https://kubernetes.io/blog/2018/04/13/local-persistent-volumes-beta/) for more details
+
+---
+
+## Bulk provisioning
+
+- It's not practical to manually create directories and PVs for each app
+
+- We *could* pre-provision a number of PVs across our fleet
+
+- We could even automate that with a Daemon Set:
+
+ - creating a number of directories on each node
+
+ - creating the corresponding PV objects
+
+- We also need to recycle volumes
+
+- ... This can quickly get out of hand
+
+---
+
+## Dynamic provisioning
+
+- We could also write our own provisioner, which would:
+
+ - watch the PVCs across all namespaces
+
+ - when a PVC is created, create a corresponding PV on a node
+
+- Or we could use one of the dynamic provisioners for local persistent volumes
+
+ (for instance the [Rancher local path provisioner](https://github.com/rancher/local-path-provisioner))
+
+---
+
+## Strategies for local persistent volumes
+
+- Remember, when a node goes down, the volumes on that node become unavailable
+
+- High availability will require another layer of replication
+
+ (like what we've just seen with Consul; or primary/secondary; etc)
+
+- Pre-provisioning PVs makes sense for machines with local storage
+
+ (e.g. cloud instance storage; or storage directly attached to a physical machine)
+
+- Dynamic provisioning makes sense for large number of applications
+
+ (when we can't or won't dedicate a whole disk to a volume)
+
+- It's possible to mix both (using distinct Storage Classes)
diff --git a/slides/k8s/localkubeconfig.md b/slides/k8s/localkubeconfig.md
index eaaf9e4e..e95977dc 100644
--- a/slides/k8s/localkubeconfig.md
+++ b/slides/k8s/localkubeconfig.md
@@ -6,6 +6,24 @@
---
+## Requirements
+
+.warning[The exercises in this chapter should be done *on your local machine*.]
+
+- `kubectl` is officially available on Linux, macOS, Windows
+
+ (and unofficially anywhere we can build and run Go binaries)
+
+- You may skip these exercises if you are following along from:
+
+ - a tablet or phone
+
+ - a web-based terminal
+
+ - an environment where you can't install and run new binaries
+
+---
+
## Installing `kubectl`
- If you already have `kubectl` on your local machine, you can skip this
@@ -16,11 +34,11 @@
- Download the `kubectl` binary from one of these links:
- [Linux](https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/linux/amd64/kubectl)
+ [Linux](https://storage.googleapis.com/kubernetes-release/release/v1.14.2/bin/linux/amd64/kubectl)
|
- [macOS](https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/darwin/amd64/kubectl)
+ [macOS](https://storage.googleapis.com/kubernetes-release/release/v1.14.2/bin/darwin/amd64/kubectl)
|
- [Windows](https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/windows/amd64/kubectl.exe)
+ [Windows](https://storage.googleapis.com/kubernetes-release/release/v1.14.2/bin/windows/amd64/kubectl.exe)
- On Linux and macOS, make the binary executable with `chmod +x kubectl`
@@ -65,9 +83,16 @@ Platform:"linux/amd64"}
- If you never used `kubectl` on your machine before: nothing to do!
-- If you already used `kubectl` to control a Kubernetes cluster before:
+.exercise[
- - rename `~/.kube/config` to e.g. `~/.kube/config.bak`
+- Make a copy of `~/.kube/config`; if you are using macOS or Linux, you can do:
+ ```bash
+ cp ~/.kube/config ~/.kube/config.before.training
+ ```
+
+- If you are using Windows, you will need to adapt this command
+
+]
---
diff --git a/slides/k8s/namespaces.md b/slides/k8s/namespaces.md
index 3dbd13c9..22459db3 100644
--- a/slides/k8s/namespaces.md
+++ b/slides/k8s/namespaces.md
@@ -1,26 +1,65 @@
# Namespaces
+- We would like to deploy another copy of DockerCoins on our cluster
+
+- We could rename all our deployments and services:
+
+ hasher → hasher2, redis → redis2, rng → rng2, etc.
+
+- That would require updating the code
+
+- There as to be a better way!
+
+--
+
+- As hinted by the title of this section, we will use *namespaces*
+
+---
+
+## Identifying a resource
+
- We cannot have two resources with the same name
- (Or can we...?)
+ (or can we...?)
--
-- We cannot have two resources *of the same type* with the same name
+- We cannot have two resources *of the same kind* with the same name
- (But it's OK to have a `rng` service, a `rng` deployment, and a `rng` daemon set!)
+ (but it's OK to have a `rng` service, a `rng` deployment, and a `rng` daemon set)
--
-- We cannot have two resources of the same type with the same name *in the same namespace*
+- We cannot have two resources of the same kind with the same name *in the same namespace*
- (But it's OK to have e.g. two `rng` services in different namespaces!)
+ (but it's OK to have e.g. two `rng` services in different namespaces)
--
-- In other words: **the tuple *(type, name, namespace)* needs to be unique**
+- Except for resources that exist at the *cluster scope*
- (In the resource YAML, the type is called `Kind`)
+ (these do not belong to a namespace)
+
+---
+
+## Uniquely identifying a resource
+
+- For *namespaced* resources:
+
+ the tuple *(kind, name, namespace)* needs to be unique
+
+- For resources at the *cluster scope*:
+
+ the tuple *(kind, name)* needs to be unique
+
+.exercise[
+
+- List resource types again, and check the NAMESPACED column:
+ ```bash
+ kubectl api-resources
+ ```
+
+]
---
@@ -59,7 +98,7 @@
- The two methods above are identical
-- If we are using a tool like Helm, it will create namespaces automatically
+- Some tools like Helm will create namespaces automatically when needed
---
@@ -168,41 +207,27 @@
---
-## Deploy DockerCoins with Helm
+## Deploying DockerCoins with YAML files
-*Follow these instructions if you previously created a Helm Chart.*
+- The GitHub repository `jpetazzo/kubercoins` contains everything we need!
.exercise[
-- Deploy DockerCoins:
+- Clone the kubercoins repository:
```bash
- helm install dockercoins
+ git clone https://github.com/jpetazzo/kubercoins
+ ```
+
+- Create all the DockerCoins resources:
+ ```bash
+ kubectl create -f kubercoins
```
]
-In the last command line, `dockercoins` is just the local path where
-we created our Helm chart before.
+If the argument behind `-f` is a directory, all the files in that directory are processed.
----
-
-## Deploy DockerCoins with Kustomize
-
-*Follow these instructions if you previously created a Kustomize overlay.*
-
-.exercise[
-
-- Deploy DockerCoins:
- ```bash
- kubectl apply -f rendered.yaml
- ```
-
-- Or, with Kubernetes 1.14, you can also do this:
- ```bash
- kubectl apply -k overlays/ship
- ```
-
-]
+The subdirectories are *not* processed, unless we also add the `-R` flag.
---
@@ -221,46 +246,7 @@ we created our Helm chart before.
]
-If the graph shows up but stays at zero, check the next slide!
-
----
-
-## Troubleshooting
-
-If did the exercices from the chapter about labels and selectors,
-the app that you just created may not work, because the `rng` service
-selector has `enabled=yes` but the pods created by the `rng` daemon set
-do not have that label.
-
-How can we troubleshoot that?
-
-- Query individual services manually
-
- → the `rng` service will time out
-
-- Inspect the services with `kubectl describe service`
-
- → the `rng` service will have an empty list of backends
-
----
-
-## Fixing the broken service
-
-The easiest option is to add the `enabled=yes` label to the relevant pods.
-
-.exercise[
-
-- Add the `enabled` label to the pods of the `rng` daemon set:
- ```bash
- kubectl label pods -l app=rng enabled=yes
- ```
-
-]
-
-The *best* option is to change either the service definition, or the
-daemon set definition, so that their respective selectors match correctly.
-
-*This is left as an exercise for the reader!*
+If the graph shows up but stays at zero, give it a minute or two!
---
diff --git a/slides/k8s/prometheus.md b/slides/k8s/prometheus.md
index b6777a6c..b78b1884 100644
--- a/slides/k8s/prometheus.md
+++ b/slides/k8s/prometheus.md
@@ -12,7 +12,7 @@
- an *alert manager* to notify us according to metrics values or trends
-- We are going to deploy it on our Kubernetes cluster and see how to query it
+- We are going to use it to collect and query some metrics on our Kubernetes cluster
---
@@ -145,7 +145,28 @@ scrape_configs:
(it will even be gentler on the I/O subsystem since it needs to write less)
-[Storage in Prometheus 2.0](https://www.youtube.com/watch?v=C4YV-9CrawA) by [Goutham V](https://twitter.com/putadent) at DC17EU
+- Would you like to know more? Check this video:
+
+ [Storage in Prometheus 2.0](https://www.youtube.com/watch?v=C4YV-9CrawA) by [Goutham V](https://twitter.com/putadent) at DC17EU
+
+---
+
+## Checking if Prometheus is installed
+
+- Before trying to install Prometheus, let's check if it's already there
+
+.exercise[
+
+- Look for services with a label `app=prometheus` across all namespaces:
+ ```bash
+ kubectl get services --selector=app=prometheus --all-namespaces
+ ```
+
+]
+
+If we see a `NodePort` service called `prometheus-server`, we're good!
+
+(We can then skip to "Connecting to the Prometheus web UI".)
---
@@ -169,11 +190,11 @@ We need to:
---
-## Helm Charts to the rescue
+## Helm charts to the rescue
-- To make our lives easier, we are going to use a Helm Chart
+- To make our lives easier, we are going to use a Helm chart
-- The Helm Chart will take care of all the steps explained above
+- The Helm chart will take care of all the steps explained above
(including some extra features that we don't need, but won't hurt)
@@ -210,20 +231,41 @@ We need to:
- Install Prometheus on our cluster:
```bash
- helm install stable/prometheus \
- --set server.service.type=NodePort \
- --set server.persistentVolume.enabled=false
+ helm upgrade prometheus stable/prometheus \
+ --install \
+ --namespace kube-system \
+ --set server.service.type=NodePort \
+ --set server.service.nodePort=30090 \
+ --set server.persistentVolume.enabled=false \
+ --set alertmanager.enabled=false
```
]
-The provided flags:
+Curious about all these flags? They're explained in the next slide.
-- expose the server web UI (and API) on a NodePort
+---
-- use an ephemeral volume for metrics storage
-
- (instead of requesting a Persistent Volume through a Persistent Volume Claim)
+class: extra-details
+
+## Explaining all the Helm flags
+
+- `helm upgrade prometheus` → upgrade release "prometheus" to the latest version ...
+
+ (a "release" is a unique name given to an app deployed with Helm)
+
+- `stable/prometheus` → ... of the chart `prometheus` in repo `stable`
+
+- `--install` → if the app doesn't exist, create it
+
+- `--namespace kube-system` → put it in that specific namespace
+
+- And set the following *values* when rendering the chart's templates:
+
+ - `server.service.type=NodePort` → expose the Prometheus server with a NodePort
+ - `server.service.nodePort=30090` → set the specific NodePort number to use
+ - `server.persistentVolume.enabled=false` → do not use a PersistentVolumeClaim
+ - `alertmanager.enabled=false` → disable the alert manager entirely
---
@@ -235,7 +277,7 @@ The provided flags:
- Figure out the NodePort that was allocated to the Prometheus server:
```bash
- kubectl get svc | grep prometheus-server
+ kubectl get svc --all-namespaces | grep prometheus-server
```
- With your browser, connect to that port
@@ -292,7 +334,7 @@ This query will show us CPU usage across all containers:
container_cpu_usage_seconds_total
```
-- The suffix of the metrics name tells us:
+- The suffix of the metrics name tells us:
- the unit (seconds of CPU)
@@ -486,3 +528,21 @@ class: extra-details
- see [this comment](https://github.com/prometheus/prometheus/issues/2204#issuecomment-261515520) for an overview
- or [this blog post](https://5pi.de/2017/11/09/use-prometheus-vector-matching-to-get-kubernetes-utilization-across-any-pod-label/) for a complete description of the process
+
+---
+
+## In practice
+
+- Grafana is a beautiful (and useful) frontend to display all kinds of graphs
+
+- Not everyone needs to know Prometheus, PromQL, Grafana, etc.
+
+- But in a team, it is valuable to have at least one person who know them
+
+- That person can set up queries and dashboards for the rest of the team
+
+- It's a little bit likeknowing how to optimize SQL queries, Dockerfiles ...
+
+ Don't panic if you don't know these tools!
+
+ ... But make sure at least one person in your team is on it 💯
diff --git a/slides/k8s/statefulsets.md b/slides/k8s/statefulsets.md
index 3dc45286..9692eef0 100644
--- a/slides/k8s/statefulsets.md
+++ b/slides/k8s/statefulsets.md
@@ -34,13 +34,13 @@
- Each pod can discover the IP address of the others easily
-- The pods can have persistent volumes attached to them
+- The pods can persist data on attached volumes
🤔 Wait a minute ... Can't we already attach volumes to pods and deployments?
---
-## Volumes and Persistent Volumes
+## Revisiting volumes
- [Volumes](https://kubernetes.io/docs/concepts/storage/volumes/) are used for many purposes:
@@ -50,13 +50,13 @@
- accessing storage systems
-- The last type of volumes is known as a "Persistent Volume"
+- Let's see examples of the latter usage
---
-## Persistent Volumes types
+## Volumes types
-- There are many [types of Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes) available:
+- There are many [types of volumes](https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes) available:
- public cloud storage (GCEPersistentDisk, AWSElasticBlockStore, AzureDisk...)
@@ -74,7 +74,7 @@
---
-## Using a Persistent Volume
+## Using a cloud volume
Here is a pod definition using an AWS EBS volume (that has to be created first):
@@ -99,7 +99,32 @@ spec:
---
-## Shortcomings of Persistent Volumes
+## Using an NFS volume
+
+Here is another example using a volume on an NFS server:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: pod-using-my-nfs-volume
+spec:
+ containers:
+ - image: ...
+ name: container-using-my-nfs-volume
+ volumeMounts:
+ - mountPath: /my-nfs
+ name: my-nfs-volume
+ volumes:
+ - name: my-nfs-volume
+ nfs:
+ server: 192.168.0.55
+ path: "/exports/assets"
+```
+
+---
+
+## Shortcomings of volumes
- Their lifecycle (creation, deletion...) is managed outside of the Kubernetes API
@@ -125,17 +150,47 @@ spec:
- This type is a *Persistent Volume Claim*
+- A Persistent Volume Claim (PVC) is a resource type
+
+ (visible with `kubectl get persistentvolumeclaims` or `kubectl get pvc`)
+
+- A PVC is not a volume; it is a *request for a volume*
+
+---
+
+## Persistent Volume Claims in practice
+
- Using a Persistent Volume Claim is a two-step process:
- creating the claim
- using the claim in a pod (as if it were any other kind of volume)
-- Between these two steps, something will happen behind the scenes:
+- A PVC starts by being Unbound (without an associated volume)
- - Kubernetes will associate an existing volume with the claim
+- Once it is associated with a Persistent Volume, it becomes Bound
- - ... or dynamically create a volume if possible and necessary
+- A Pod referring an unbound PVC will not start
+
+ (but as soon as the PVC is bound, the Pod can start)
+
+---
+
+## Binding PV and PVC
+
+- A Kubernetes controller continuously watches PV and PVC objects
+
+- When it notices an unbound PVC, it tries to find a satisfactory PV
+
+ ("satisfactory" in terms of size and other characteristics; see next slide)
+
+- If no PV fits the PVC, a PV can be created dynamically
+
+ (this requires to configure a *dynamic provisioner*, more on that later)
+
+- Otherwise, the PVC remains unbound indefinitely
+
+ (until we manually create a PV or setup dynamic provisioning)
---
@@ -147,7 +202,9 @@ spec:
- the access mode (e.g. "read-write by a single pod")
-- It can also give extra details, like:
+- Optionally, it can also specify a Storage Class
+
+- The Storage Class indicates:
- which storage system to use (e.g. Portworx, EBS...)
@@ -155,8 +212,6 @@ spec:
e.g.: "replicate the data 3 times, and use SSD media"
-- The extra details are provided by specifying a Storage Class
-
---
## What's a Storage Class?
@@ -167,15 +222,15 @@ spec:
- It indicates which *provisioner* to use
+ (which controller will create the actual volume)
+
- And arbitrary parameters for that provisioner
(replication levels, type of disk ... anything relevant!)
-- It is necessary to define a Storage Class to use [dynamic provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/)
+- Storage Classes are required if we want to use [dynamic provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/)
-- Conversely, it is not necessary to define one if you will create volumes manually
-
- (we will see dynamic provisioning in action later)
+ (but we can also create volumes manually, and ignore Storage Classes)
---
@@ -200,7 +255,7 @@ spec:
## Using a Persistent Volume Claim
-Here is the same definition as earlier, but using a PVC:
+Here is a Pod definition like the ones shown earlier, but using a PVC:
```yaml
apiVersion: v1
@@ -212,7 +267,7 @@ spec:
- image: ...
name: container-using-a-claim
volumeMounts:
- - mountPath: /my-ebs
+ - mountPath: /my-vol
name: my-volume
volumes:
- name: my-volume
diff --git a/slides/k8s/versions-k8s.md b/slides/k8s/versions-k8s.md
index e093d20a..ab76efed 100644
--- a/slides/k8s/versions-k8s.md
+++ b/slides/k8s/versions-k8s.md
@@ -1,7 +1,7 @@
## Versions installed
-- Kubernetes 1.14.1
-- Docker Engine 18.09.5
+- Kubernetes 1.14.2
+- Docker Engine 18.09.6
- Docker Compose 1.21.1
diff --git a/slides/k8s/volumes.md b/slides/k8s/volumes.md
index 2773d185..dc7c355a 100644
--- a/slides/k8s/volumes.md
+++ b/slides/k8s/volumes.md
@@ -18,6 +18,8 @@
---
+class: extra-details
+
## Kubernetes volumes vs. Docker volumes
- Kubernetes and Docker volumes are very similar
@@ -35,13 +37,35 @@
- Kubernetes volumes are also used to expose configuration and secrets
- Docker has specific concepts for configuration and secrets
-
+
(but under the hood, the technical implementation is similar)
- If you're not familiar with Docker volumes, you can safely ignore this slide!
---
+## Volumes ≠ Persistent Volumes
+
+- Volumes and Persistent Volumes are related, but very different!
+
+- *Volumes*:
+
+ - appear in Pod specifications (see next slide)
+
+ - do not exist as API resources (**cannot** do `kubectl get volumes`)
+
+- *Persistent Volumes*:
+
+ - are API resources (**can** do `kubectl get persistentvolumes`)
+
+ - correspond to concrete volumes (e.g. on a SAN, EBS, etc.)
+
+ - cannot be associated to a Pod directly; but through a Persistent Volume Claim
+
+ - won't be discussed further in this section
+
+---
+
## A simple volume example
```yaml
diff --git a/slides/k8s/whatsnext.md b/slides/k8s/whatsnext.md
index 0b7d39be..48ec0550 100644
--- a/slides/k8s/whatsnext.md
+++ b/slides/k8s/whatsnext.md
@@ -132,6 +132,8 @@ And *then* it is time to look at orchestration!
|
[Persistent Volumes](kube-selfpaced.yml.html#toc-highly-available-persistent-volumes)
+- Excellent [blog post](http://www.databasesoup.com/2018/07/should-i-run-postgres-on-kubernetes.html) tackling the question: “Should I run Postgres on Kubernetes?”
+
---
## HTTP traffic handling
diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml
index fccb4b4a..33b85dfa 100644
--- a/slides/kube-fullday.yml
+++ b/slides/kube-fullday.yml
@@ -20,54 +20,57 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- k8s/versions-k8s.md
- shared/sampleapp.md
-# - shared/composescale.md
-# - shared/hastyconclusions.md
+ #- shared/composescale.md
+ #- shared/hastyconclusions.md
- shared/composedown.md
- k8s/concepts-k8s.md
- shared/declarative.md
- k8s/declarative.md
-- - k8s/kubenet.md
- - k8s/kubectlget.md
+ - k8s/kubenet.md
+- - k8s/kubectlget.md
- k8s/setup-k8s.md
- k8s/kubectlrun.md
- k8s/deploymentslideshow.md
- k8s/kubectlexpose.md
- - k8s/shippingimages.md
-# - k8s/buildshiprun-selfhosted.md
+ #- k8s/buildshiprun-selfhosted.md
- k8s/buildshiprun-dockerhub.md
- k8s/ourapponkube.md
-# - k8s/kubectlproxy.md
-# - k8s/localkubeconfig.md
-# - k8s/accessinternal.md
+ #- k8s/kubectlproxy.md
+ #- k8s/localkubeconfig.md
+ #- k8s/accessinternal.md
- k8s/dashboard.md
-# - k8s/kubectlscale.md
+ #- k8s/kubectlscale.md
- k8s/scalingdockercoins.md
- shared/hastyconclusions.md
- k8s/daemonset.md
- - k8s/rollout.md
-# - k8s/healthchecks.md
+ - k8s/namespaces.md
+ #- k8s/kustomize.md
+ #- k8s/helm.md
+ #- k8s/create-chart.md
+ #- k8s/healthchecks.md
- k8s/logs-cli.md
- k8s/logs-centralized.md
-#- - k8s/helm.md
-# - k8s/create-chart.md
-# - k8s/kustomize.md
-# - k8s/namespaces.md
-# - k8s/netpol.md
-# - k8s/authn-authz.md
-# - k8s/podsecuritypolicy.md
-#- - k8s/ingress.md
-# - k8s/gitworkflows.md
+ #- k8s/netpol.md
+ #- k8s/authn-authz.md
+ #- k8s/podsecuritypolicy.md
+ #- k8s/ingress.md
+ #- k8s/gitworkflows.md
- k8s/prometheus.md
-#- - k8s/volumes.md
-# - k8s/build-with-docker.md
-# - k8s/build-with-kaniko.md
-# - k8s/configuration.md
-#- - k8s/owners-and-dependents.md
-# - k8s/extending-api.md
-# - k8s/statefulsets.md
-# - k8s/portworx.md
+ #- k8s/volumes.md
+ #- k8s/build-with-docker.md
+ #- k8s/build-with-kaniko.md
+ #- k8s/configuration.md
+ #- k8s/owners-and-dependents.md
+ #- k8s/extending-api.md
+ #- k8s/statefulsets.md
+ #- k8s/local-persistent-volumes.md
+ #- k8s/portworx.md
+ #- k8s/staticpods.md
- - k8s/whatsnext.md
- k8s/links.md
- shared/thankyou.md
diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml
index 8ff6b2ea..53bc677b 100644
--- a/slides/kube-halfday.yml
+++ b/slides/kube-halfday.yml
@@ -22,6 +22,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- k8s/versions-k8s.md
- shared/sampleapp.md
# Bridget doesn't go into as much depth with compose
@@ -53,10 +54,10 @@ chapters:
- - k8s/logs-cli.md
# Bridget hasn't added EFK yet
#- k8s/logs-centralized.md
+ - k8s/namespaces.md
- k8s/helm.md
- k8s/create-chart.md
#- k8s/kustomize.md
- - k8s/namespaces.md
#- k8s/netpol.md
- k8s/whatsnext.md
# - k8s/links.md
diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml
index b82db29b..bb58f76c 100644
--- a/slides/kube-selfpaced.yml
+++ b/slides/kube-selfpaced.yml
@@ -20,6 +20,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- k8s/versions-k8s.md
- shared/sampleapp.md
- shared/composescale.md
@@ -46,14 +47,14 @@ chapters:
# - k8s/scalingdockercoins.md
# - shared/hastyconclusions.md
- k8s/daemonset.md
-- - k8s/rollout.md
+ - k8s/rollout.md
+- - k8s/namespaces.md
+ - k8s/kustomize.md
+ - k8s/helm.md
+ - k8s/create-chart.md
- k8s/healthchecks.md
- k8s/logs-cli.md
- k8s/logs-centralized.md
-- - k8s/helm.md
- #- k8s/create-chart.md
- - k8s/kustomize.md
- - k8s/namespaces.md
- k8s/netpol.md
- k8s/authn-authz.md
- k8s/podsecuritypolicy.md
@@ -67,6 +68,7 @@ chapters:
- - k8s/owners-and-dependents.md
- k8s/extending-api.md
- k8s/statefulsets.md
+ - k8s/local-persistent-volumes.md
- k8s/portworx.md
- k8s/staticpods.md
- - k8s/whatsnext.md
diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml
index 82952469..95a0e001 100644
--- a/slides/kube-twodays.yml
+++ b/slides/kube-twodays.yml
@@ -20,6 +20,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- k8s/versions-k8s.md
- shared/sampleapp.md
#- shared/composescale.md
@@ -28,8 +29,8 @@ chapters:
- k8s/concepts-k8s.md
- shared/declarative.md
- k8s/declarative.md
-- - k8s/kubenet.md
- - k8s/kubectlget.md
+ - k8s/kubenet.md
+- - k8s/kubectlget.md
- k8s/setup-k8s.md
- k8s/kubectlrun.md
- k8s/deploymentslideshow.md
@@ -47,14 +48,14 @@ chapters:
- shared/hastyconclusions.md
- - k8s/daemonset.md
- k8s/rollout.md
- - k8s/healthchecks.md
+ - k8s/namespaces.md
+ - k8s/kustomize.md
+ #- k8s/helm.md
+ #- k8s/create-chart.md
+- - k8s/healthchecks.md
- k8s/logs-cli.md
- k8s/logs-centralized.md
-- - k8s/helm.md
- #- k8s/create-chart.md
- - k8s/kustomize.md
- - k8s/namespaces.md
- - k8s/netpol.md
+ #- k8s/netpol.md
- k8s/authn-authz.md
- k8s/podsecuritypolicy.md
- - k8s/ingress.md
@@ -65,10 +66,11 @@ chapters:
#- k8s/build-with-kaniko.md
- k8s/configuration.md
#- k8s/owners-and-dependents.md
- - k8s/extending-api.md
+ #- k8s/extending-api.md
- - k8s/statefulsets.md
+ - k8s/local-persistent-volumes.md
- k8s/portworx.md
- - k8s/staticpods.md
+ #- k8s/staticpods.md
- - k8s/whatsnext.md
- k8s/links.md
- shared/thankyou.md
diff --git a/slides/shared/connecting.md b/slides/shared/connecting.md
new file mode 100644
index 00000000..f21d46b7
--- /dev/null
+++ b/slides/shared/connecting.md
@@ -0,0 +1,137 @@
+class: in-person
+
+## Connecting to our lab environment
+
+.exercise[
+
+- Log into the first VM (`node1`) with your SSH client
+
+
+
+- Check that you can SSH (without password) to `node2`:
+ ```bash
+ ssh node2
+ ```
+- Type `exit` or `^D` to come back to `node1`
+
+
+
+]
+
+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://training.play-with-kubernetes.com/)
+
+ 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://@@GITREPO@@/tree/master/prepare-vms))
+
+ Bigger setup effort; ideal for group training
+
+---
+
+class: self-paced
+
+## Get your own Docker nodes
+
+- If you already have some Docker nodes: great!
+
+- If not: let's get some thanks to Play-With-Docker
+
+.exercise[
+
+- Go to http://www.play-with-docker.com/
+
+- Log in
+
+- Create your first node
+
+
+
+]
+
+You will need a Docker ID to use Play-With-Docker.
+
+(Creating a Docker ID is free.)
+
+---
+
+## 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`
+
+- During normal operations, we do not need access to the other nodes
+
+- If we had to troubleshoot issues, we would use a combination of:
+
+ - SSH (to access system logs, daemon status...)
+
+ - Docker API (to check running containers and container engine status)
+
+---
+
+## Terminals
+
+Once in a while, the instructions will say:
+
"Open a new terminal."
+
+There are multiple ways to do this:
+
+- create a new window or tab on your machine, and SSH into the VM;
+
+- use screen or tmux on the VM and open a new window from there.
+
+You are welcome to use the method that you feel the most comfortable with.
+
+---
+
+## Tmux cheatsheet
+
+[Tmux](https://en.wikipedia.org/wiki/Tmux) is a terminal multiplexer like `screen`.
+
+*You don't have to use it or even know about it to follow along.
+
+But some of us like to use it to switch between terminals.
+
+It has been preinstalled on your workshop nodes.*
+
+- Ctrl-b c → creates a new window
+- Ctrl-b n → go to next window
+- Ctrl-b p → go to previous window
+- Ctrl-b " → split window top/bottom
+- Ctrl-b % → split window left/right
+- Ctrl-b Alt-1 → rearrange windows in columns
+- Ctrl-b Alt-2 → rearrange windows in rows
+- Ctrl-b arrows → navigate to other windows
+- Ctrl-b d → detach session
+- tmux attach → reattach to session
diff --git a/slides/shared/prereqs.md b/slides/shared/prereqs.md
index 7840b527..52684b34 100644
--- a/slides/shared/prereqs.md
+++ b/slides/shared/prereqs.md
@@ -169,143 +169,3 @@ class: in-person, extra-details
- It requires UDP ports to be open
(By default, it uses a UDP port between 60000 and 61000)
-
----
-
-class: in-person
-
-## Connecting to our lab environment
-
-.exercise[
-
-- Log into the first VM (`node1`) with your SSH client
-
-
-
-- Check that you can SSH (without password) to `node2`:
- ```bash
- ssh node2
- ```
-- Type `exit` or `^D` to come back to `node1`
-
-
-
-]
-
-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://training.play-with-kubernetes.com/)
-
- 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://@@GITREPO@@/tree/master/prepare-vms))
-
- Bigger setup effort; ideal for group training
-
----
-
-class: self-paced
-
-## Get your own Docker nodes
-
-- If you already have some Docker nodes: great!
-
-- If not: let's get some thanks to Play-With-Docker
-
-.exercise[
-
-- Go to http://www.play-with-docker.com/
-
-- Log in
-
-- Create your first node
-
-
-
-]
-
-You will need a Docker ID to use Play-With-Docker.
-
-(Creating a Docker ID is free.)
-
----
-
-## 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`
-
-- During normal operations, we do not need access to the other nodes
-
-- If we had to troubleshoot issues, we would use a combination of:
-
- - SSH (to access system logs, daemon status...)
-
- - Docker API (to check running containers and container engine status)
-
----
-
-## Terminals
-
-Once in a while, the instructions will say:
-
"Open a new terminal."
-
-There are multiple ways to do this:
-
-- create a new window or tab on your machine, and SSH into the VM;
-
-- use screen or tmux on the VM and open a new window from there.
-
-You are welcome to use the method that you feel the most comfortable with.
-
----
-
-## Tmux cheatsheet
-
-[Tmux](https://en.wikipedia.org/wiki/Tmux) is a terminal multiplexer like `screen`.
-
-*You don't have to use it or even know about it to follow along.
-
-But some of us like to use it to switch between terminals.
-
-It has been preinstalled on your workshop nodes.*
-
-- Ctrl-b c → creates a new window
-- Ctrl-b n → go to next window
-- Ctrl-b p → go to previous window
-- Ctrl-b " → split window top/bottom
-- Ctrl-b % → split window left/right
-- Ctrl-b Alt-1 → rearrange windows in columns
-- Ctrl-b Alt-2 → rearrange windows in rows
-- Ctrl-b arrows → navigate to other windows
-- Ctrl-b d → detach session
-- tmux attach → reattach to session
diff --git a/slides/swarm-fullday.yml b/slides/swarm-fullday.yml
index ab3b38a9..8f30665d 100644
--- a/slides/swarm-fullday.yml
+++ b/slides/swarm-fullday.yml
@@ -24,6 +24,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- swarm/versions.md
- shared/sampleapp.md
- shared/composescale.md
diff --git a/slides/swarm-halfday.yml b/slides/swarm-halfday.yml
index d69c5c2c..a80a0733 100644
--- a/slides/swarm-halfday.yml
+++ b/slides/swarm-halfday.yml
@@ -24,6 +24,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- swarm/versions.md
- shared/sampleapp.md
- shared/composescale.md
diff --git a/slides/swarm-selfpaced.yml b/slides/swarm-selfpaced.yml
index 73290511..2510eed5 100644
--- a/slides/swarm-selfpaced.yml
+++ b/slides/swarm-selfpaced.yml
@@ -19,6 +19,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- swarm/versions.md
- |
name: part-1
diff --git a/slides/swarm-video.yml b/slides/swarm-video.yml
index ba62175a..0d361a40 100644
--- a/slides/swarm-video.yml
+++ b/slides/swarm-video.yml
@@ -19,6 +19,7 @@ chapters:
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
+ - shared/connecting.md
- swarm/versions.md
- |
name: part-1