Merge from master

This commit is contained in:
Jerome Petazzoni
2019-11-10 09:36:25 -06:00
49 changed files with 1176 additions and 307 deletions

View File

@@ -5,6 +5,7 @@
speaker: jpetazzo
title: Deploying and scaling applications with Kubernetes
attend: https://conferences.oreilly.com/velocity/vl-eu/public/schedule/detail/79109
slides: https://velocity-2019-11.container.training/
- date: 2019-11-13
country: fr
@@ -15,6 +16,38 @@
lang: fr
attend: http://2019.devops-dday.com/Workshop.html
- date: 2019-10-30
country: us
city: Portland, OR
event: LISA
speaker: jpetazzo
title: Deep Dive into Kubernetes Internals for Builders and Operators
attend: https://www.usenix.org/conference/lisa19/presentation/petazzoni-tutorial
- date: [2019-10-22, 2019-10-24]
country: us
city: Charlotte, NC
event: Ardan Labs
speaker: jpetazzo
title: Kubernetes Training
attend: https://www.eventbrite.com/e/containers-docker-and-kubernetes-training-for-devs-and-ops-charlotte-nc-november-2019-tickets-73296659281
- date: 2019-10-22
country: us
city: Charlotte, NC
event: Ardan Labs
speaker: jpetazzo
title: Docker & Containers Training
attend: https://www.eventbrite.com/e/containers-docker-and-kubernetes-training-for-devs-and-ops-charlotte-nc-november-2019-tickets-73296659281
- date: 2019-10-22
country: de
city: Berlin
event: GOTO
speaker: bretfisher
title: Kubernetes or Swarm? Build Both, Deploy Apps, Learn The Differences
attend: https://gotober.com/2019/workshops/194
- date: [2019-09-24, 2019-09-25]
country: fr
city: Paris
@@ -23,6 +56,7 @@
title: Déployer ses applications avec Kubernetes (in French)
lang: fr
attend: https://enix.io/fr/services/formation/deployer-ses-applications-avec-kubernetes/
slides: https://kube-2019-09.container.training/
- date: 2019-08-27
country: tr

View File

@@ -9,6 +9,8 @@ gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
#slidenumberprefix: "#SomeHashTag — "
exclude:
- self-paced

View File

@@ -9,6 +9,8 @@ gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
#slidenumberprefix: "#SomeHashTag — "
exclude:
- in-person

View File

@@ -667,17 +667,12 @@ class: extra-details
- For auditing purposes, sometimes we want to know who can perform an action
- There is a proof-of-concept tool by Aqua Security which does exactly that:
- There are a few tools to help us with that
https://github.com/aquasecurity/kubectl-who-can
- [kubectl-who-can](https://github.com/aquasecurity/kubectl-who-can) by Aqua Security
- This is one way to install it:
```bash
docker run --rm -v /usr/local/bin:/go/bin golang \
go get -v github.com/aquasecurity/kubectl-who-can
```
- [Review Access (aka Rakkess)](https://github.com/corneliusweig/rakkess)
- This is one way to use it:
```bash
kubectl-who-can create pods
```
- Both are available as standalone programs, or as plugins for `kubectl`
(`kubectl` plugins can be installed and managed with `krew`)

View File

@@ -15,26 +15,3 @@
- `dockercoins/webui:v0.1`
- `dockercoins/worker:v0.1`
---
## Setting `$REGISTRY` and `$TAG`
- In the upcoming exercises and labs, we use a couple of environment variables:
- `$REGISTRY` as a prefix to all image names
- `$TAG` as the image version tag
- For example, the worker image is `$REGISTRY/worker:$TAG`
- If you copy-paste the commands in these exercises:
**make sure that you set `$REGISTRY` and `$TAG` first!**
- For example:
```
export REGISTRY=dockercoins TAG=v0.1
```
(this will expand `$REGISTRY/worker:$TAG` to `dockercoins/worker:v0.1`)

View File

@@ -10,6 +10,8 @@
- Components can be upgraded one at a time without problems
<!-- ##VERSION## -->
---
## Checking what we're running
@@ -166,7 +168,7 @@
- Upgrade kubelet:
```bash
apt install kubelet=1.14.2-00
sudo apt install kubelet=1.15.3-00
```
]
@@ -226,7 +228,7 @@
sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml
```
- Look for the `image:` line, and update it to e.g. `v1.14.0`
- Look for the `image:` line, and update it to e.g. `v1.15.0`
]
@@ -260,14 +262,52 @@
sudo kubeadm upgrade plan
```
(Note: kubeadm is confused by our manual upgrade of the API server.
<br/>It thinks the cluster is running 1.14.0!)
]
<!-- ##VERSION## -->
Note 1: kubeadm thinks that our cluster is running 1.15.0.
<br/>It is confused by our manual upgrade of the API server!
Note 2: kubeadm itself is still version 1.14.6.
<br/>It doesn't know how to upgrade do 1.15.X.
---
## Upgrading kubeadm
- First things first: we need to upgrade kubeadm
.exercise[
- Upgrade kubeadm:
```
sudo apt install kubeadm
```
- Check what kubeadm tells us:
```
sudo kubeadm upgrade plan
```
]
Note: kubeadm still thinks that our cluster is running 1.15.0.
<br/>But at least it knows about version 1.15.X now.
---
## Upgrading the cluster with kubeadm
- Ideally, we should revert our `image:` change
(so that kubeadm executes the right migration steps)
- Or we can try the upgrade anyway
.exercise[
- Perform the upgrade:
```bash
sudo kubeadm upgrade apply v1.14.2
sudo kubeadm upgrade apply v1.15.3
```
]
@@ -287,8 +327,8 @@
- Download the configuration on each node, and upgrade kubelet:
```bash
for N in 1 2 3; do
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.14.2
ssh test$N sudo apt install kubelet=1.14.2-00
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.15.3
ssh test$N sudo apt install kubelet=1.15.3-00
done
```
]
@@ -297,7 +337,7 @@
## Checking what we've done
- All our nodes should now be updated to version 1.14.2
- All our nodes should now be updated to version 1.15.3
.exercise[
@@ -307,3 +347,19 @@
```
]
---
class: extra-details
## Skipping versions
- This example worked because we went from 1.14 to 1.15
- If you are upgrading from e.g. 1.13, you will generally have to go through 1.14 first
- This means upgrading kubeadm to 1.14.X, then using it to upgrade the cluster
- Then upgrading kubeadm to 1.15.X, etc.
- **Make sure to read the release notes before upgrading!**

View File

@@ -193,7 +193,12 @@
- Best practice: set a memory limit, and pass it to the runtime
(see [this blog post](https://very-serio.us/2017/12/05/running-jvms-in-kubernetes/) for a detailed example)
- Note: recent versions of the JVM can do this automatically
(see [JDK-8146115](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115))
and
[this blog post](https://very-serio.us/2017/12/05/running-jvms-in-kubernetes/)
for detailed examples)
---

View File

@@ -105,6 +105,22 @@ The dashboard will then ask you which authentication you want to use.
---
## Other dashboards
- [Kube Web View](https://codeberg.org/hjacobs/kube-web-view)
- read-only dashboard
- optimized for "troubleshooting and incident response"
- see [vision and goals](https://kube-web-view.readthedocs.io/en/latest/vision.html#vision) for details
- [Kube Ops View](https://github.com/hjacobs/kube-ops-view)
- "provides a common operational picture for multiple Kubernetes clusters"
---
# Security implications of `kubectl apply`
- When we do `kubectl apply -f <URL>`, we create arbitrary resources
@@ -156,4 +172,3 @@ The dashboard will then ask you which authentication you want to use.
- It introduces new failure modes
(for instance, if you try to apply YAML from a link that's no longer valid)

View File

@@ -481,13 +481,13 @@ docker run alpine echo hello world
.exercise[
- Create the file `kubeconfig.kubelet` with `kubectl`:
- Create the file `~/.kube/config` with `kubectl`:
```bash
kubectl --kubeconfig kubeconfig.kubelet config \
kubectl config \
set-cluster localhost --server http://localhost:8080
kubectl --kubeconfig kubeconfig.kubelet config \
kubectl config \
set-context localhost --cluster localhost
kubectl --kubeconfig kubeconfig.kubelet config \
kubectl config \
use-context localhost
```
@@ -495,19 +495,7 @@ docker run alpine echo hello world
---
## All Kubernetes clients can use `kubeconfig`
- The `kubeconfig.kubelet` file has the same format as e.g. `~/.kubeconfig`
- All Kubernetes clients can use a similar file
- The `kubectl config` commands can be used to manipulate these files
- This highlights that kubelet is a "normal" client of the API server
---
## Our `kubeconfig.kubelet` file
## Our `~/.kube/config` file
The file that we generated looks like the one below.
@@ -533,9 +521,9 @@ clusters:
.exercise[
- Start kubelet with that `kubeconfig.kubelet` file:
- Start kubelet with that kubeconfig file:
```bash
kubelet --kubeconfig kubeconfig.kubelet
kubelet --kubeconfig ~/.kube/config
```
]

209
slides/k8s/dryrun.md Normal file
View File

@@ -0,0 +1,209 @@
# Authoring YAML
- There are various ways to generate YAML with Kubernetes, e.g.:
- `kubectl run`
- `kubectl create deployment` (and a few other `kubectl create` variants)
- `kubectl expose`
- When and why do we need to write our own YAML?
- How do we write YAML from scratch?
---
## The limits of generated YAML
- Many advanced (and even not-so-advanced) features require to write YAML:
- pods with multiple containers
- resource limits
- healthchecks
- DaemonSets, StatefulSets
- and more!
- How do we access these features?
---
## We don't have to start from scratch
- Create a resource (e.g. Deployment)
- Dump its YAML with `kubectl get -o yaml ...`
- Edit the YAML
- Use `kubectl apply -f ...` with the YAML file to:
- update the resource (if it's the same kind)
- create a new resource (if it's a different kind)
- Or: Use The Docs, Luke
(the documentation almost always has YAML examples)
---
## Generating YAML without creating resources
- We can use the `--dry-run` option
.exercise[
- Generate the YAML for a Deployment without creating it:
```bash
kubectl create deployment web --image nginx --dry-run
```
]
- We can clean up that YAML even more if we want
(for instance, we can remove the `creationTimestamp` and empty dicts)
---
## Using `--dry-run` with `kubectl apply`
- The `--dry-run` option can also be used with `kubectl apply`
- However, it can be misleading (it doesn't do a "real" dry run)
- Let's see what happens in the following scenario:
- generate the YAML for a Deployment
- tweak the YAML to transform it into a DaemonSet
- apply that YAML to see what would actually be created
---
## The limits of `kubectl apply --dry-run`
.exercise[
- Generate the YAML for a deployment:
```bash
kubectl create deployment web --image=nginx -o yaml > web.yaml
```
- Change the `kind` in the YAML to make it a `DaemonSet`:
```bash
sed -i s/Deployment/DaemonSet/ web.yaml
```
- Ask `kubectl` what would be applied:
```bash
kubectl apply -f web.yaml --dry-run --validate=false -o yaml
```
]
The resulting YAML doesn't represent a valid DaemonSet.
---
## Server-side dry run
- Since Kubernetes 1.13, we can use [server-side dry run and diffs](https://kubernetes.io/blog/2019/01/14/apiserver-dry-run-and-kubectl-diff/)
- Server-side dry run will do all the work, but *not* persist to etcd
(all validation and mutation hooks will be executed)
.exercise[
- Try the same YAML file as earlier, with server-side dry run:
```bash
kubectl apply -f web.yaml --server-dry-run --validate=false -o yaml
```
]
The resulting YAML doesn't have the `replicas` field anymore.
Instead, it has the fields expected in a DaemonSet.
---
## Advantages of server-side dry run
- The YAML is verified much more extensively
- The only step that is skipped is "write to etcd"
- YAML that passes server-side dry run *should* apply successfully
(unless the cluster state changes by the time the YAML is actually applied)
- Validating or mutating hooks that have side effects can also be an issue
---
## `kubectl diff`
- Kubernetes 1.13 also introduced `kubectl diff`
- `kubectl diff` does a server-side dry run, *and* shows differences
.exercise[
- Try `kubectl diff` on the YAML that we tweaked earlier:
```bash
kubectl diff -f web.yaml
```
]
Note: we don't need to specify `--validate=false` here.
---
## Advantage of YAML
- Using YAML (instead of `kubectl run`/`create`/etc.) allows to be *declarative*
- The YAML describes the desired state of our cluster and applications
- YAML can be stored, versioned, archived (e.g. in git repositories)
- To change resources, change the YAML files
(instead of using `kubectl edit`/`scale`/`label`/etc.)
- Changes can be reviewed before being applied
(with code reviews, pull requests ...)
- This workflow is sometimes called "GitOps"
(there are tools like Weave Flux or GitKube to facilitate it)
---
## YAML in practice
- Get started with `kubectl run`/`create`/`expose`/etc.
- Dump the YAML with `kubectl get -o yaml`
- Tweak that YAML and `kubectl apply` it back
- Store that YAML for reference (for further deployments)
- Feel free to clean up the YAML:
- remove fields you don't know
- check that it still works!
- That YAML will be useful later when using e.g. Kustomize or Helm

View File

@@ -1,41 +1,3 @@
## Questions to ask before adding healthchecks
- Do we want liveness, readiness, both?
(sometimes, we can use the same check, but with different failure thresholds)
- Do we have existing HTTP endpoints that we can use?
- Do we need to add new endpoints, or perhaps use something else?
- Are our healthchecks likely to use resources and/or slow down the app?
- Do they depend on additional services?
(this can be particularly tricky, see next slide)
---
## Healthchecks and dependencies
- A good healthcheck should always indicate the health of the service itself
- It should not be affected by the state of the service's dependencies
- Example: a web server requiring a database connection to operate
(make sure that the healthcheck can report "OK" even if the database is down;
<br/>
because it won't help us to restart the web server if the issue is with the DB!)
- Example: a microservice calling other microservices
- Example: a worker process
(these will generally require minor code changes to report health)
---
## Adding healthchecks to an app
- Let's add healthchecks to DockerCoins!
@@ -370,24 +332,4 @@ class: extra-details
(and have gcr.io/pause take care of the reaping)
---
## Healthchecks for worker
- Readiness isn't useful
(because worker isn't a backend for a service)
- Liveness may help us restart a broken worker, but how can we check it?
- Embedding an HTTP server is an option
(but it has a high potential for unwanted side effects and false positives)
- Using a "lease" file can be relatively easy:
- touch a file during each iteration of the main loop
- check the timestamp of that file from an exec probe
- Writing logs (and checking them from the probe) also works
- Discussion of this in [Video - 10 Ways to Shoot Yourself in the Foot with Kubernetes, #9 Will Surprise You](https://www.youtube.com/watch?v=QKI-JRs2RIE)

View File

@@ -42,9 +42,11 @@
- internal corruption (causing all requests to error)
- If the liveness probe fails *N* consecutive times, the container is killed
- Anything where our incident response would be "just restart/reboot it"
- *N* is the `failureThreshold` (3 by default)
.warning[**Do not** use liveness probes for problems that can't be fixed by a restart]
- Otherwise we just restart our pods for no reason, creating useless load
---
@@ -52,7 +54,7 @@
- Indicates if the container is ready to serve traffic
- If a container becomes "unready" (let's say busy!) it might be ready again soon
- If a container becomes "unready" it might be ready again soon
- If the readiness probe fails:
@@ -66,19 +68,79 @@
## When to use a readiness probe
- To indicate temporary failures
- To indicate failure due to an external cause
- the application can only service *N* parallel connections
- database is down or unreachable
- the runtime is busy doing garbage collection or initial data load
- mandatory auth or other backend service unavailable
- The container is marked as "not ready" after `failureThreshold` failed attempts
- To indicate temporary failure or unavailability
(3 by default)
- application can only service *N* parallel connections
- It is marked again as "ready" after `successThreshold` successful attempts
- runtime is busy doing garbage collection or initial data load
(1 by default)
- For processes that take a long time to start
(more on that later)
---
## Dependencies
- If a web server depends on a database to function, and the database is down:
- the web server's liveness probe should succeed
- the web server's readiness probe should fail
- Same thing for any hard dependency (without which the container can't work)
.warning[**Do not** fail liveness probes for problems that are external to the container]
---
## Timing and thresholds
- Probes are executed at intervals of `periodSeconds` (default: 10)
- The timeout for a probe is set with `timeoutSeconds` (default: 1)
.warning[If a probe takes longer than that, it is considered as a FAIL]
- A probe is considered successful after `successThreshold` successes (default: 1)
- A probe is considered failing after `failureThreshold` failures (default: 3)
- A probe can have an `initialDelaySeconds` parameter (default: 0)
- Kubernetes will wait that amount of time before running the probe for the first time
(this is important to avoid killing services that take a long time to start)
---
class: extra-details
## Startup probe
- Kubernetes 1.16 introduces a third type of probe: `startupProbe`
(it is in `alpha` in Kubernetes 1.16)
- It can be used to indicate "container not ready *yet*"
- process is still starting
- loading external data, priming caches
- Before Kubernetes 1.16, we had to use the `initialDelaySeconds` parameter
(available for both liveness and readiness probes)
- `initialDelaySeconds` is a rigid delay (always wait X before running probes)
- `startupProbe` works better when a container start time can vary a lot
---
@@ -112,10 +174,12 @@
(instead of serving errors or timeouts)
- Overloaded backends get removed from load balancer rotation
- Unavailable backends get removed from load balancer rotation
(thus improving response times across the board)
- If a probe is not defined, it's as if there was an "always successful" probe
---
## Example: HTTP probe
@@ -165,14 +229,56 @@ If the Redis process becomes unresponsive, it will be killed.
---
## Details about liveness and readiness probes
## Questions to ask before adding healthchecks
- Probes are executed at intervals of `periodSeconds` (default: 10)
- Do we want liveness, readiness, both?
- The timeout for a probe is set with `timeoutSeconds` (default: 1)
(sometimes, we can use the same check, but with different failure thresholds)
- A probe is considered successful after `successThreshold` successes (default: 1)
- Do we have existing HTTP endpoints that we can use?
- A probe is considered failing after `failureThreshold` failures (default: 3)
- Do we need to add new endpoints, or perhaps use something else?
- If a probe is not defined, it's as if there was an "always successful" probe
- Are our healthchecks likely to use resources and/or slow down the app?
- Do they depend on additional services?
(this can be particularly tricky, see next slide)
---
## Healthchecks and dependencies
- Liveness checks should not be influenced by the state of external services
- All checks should reply quickly (by default, less than 1 second)
- Otherwise, they are considered to fail
- This might require to check the health of dependencies asynchronously
(e.g. if a database or API might be healthy but still take more than
1 second to reply, we should check the status asynchronously and report
a cached status)
---
## Healthchecks for workers
(In that context, worker = process that doesn't accept connections)
- Readiness isn't useful
(because workers aren't backends for a service)
- Liveness may help us restart a broken worker, but how can we check it?
- Embedding an HTTP server is a (potentially expensive) option
- Using a "lease" file can be relatively easy:
- touch a file during each iteration of the main loop
- check the timestamp of that file from an exec probe
- Writing logs (and checking them from the probe) also works

View File

@@ -153,10 +153,7 @@ pod/pingpong-7c8bbcd9bc-6c9qz 1/1 Running 0 10m
kubectl logs deploy/pingpong --tail 1 --follow
```
<!--
```wait seq=3```
```keys ^C```
-->
- Leave that command running, so that we can keep an eye on these logs
]
@@ -186,6 +183,44 @@ We could! But the *deployment* would notice it right away, and scale back to the
---
## Log streaming
- Let's look again at the output of `kubectl logs`
(the one we started before scaling up)
- `kubectl logs` shows us one line per second
- We could expect 3 lines per second
(since we should now have 3 pods running `ping`)
- Let's try to figure out what's happening!
---
## Streaming logs of multiple pods
- What happens if we restart `kubectl logs`?
.exercise[
- Interrupt `kubectl logs` (with Ctrl-C)
- Restart it:
```bash
kubectl logs deploy/pingpong --tail 1 --follow
```
]
`kubectl logs` will warn us that multiple pods were found, and that it's showing us only one of them.
Let's leave `kubectl logs` running while we keep exploring.
---
## Resilience
- The *deployment* `pingpong` watches its *replica set*
@@ -196,20 +231,12 @@ We could! But the *deployment* would notice it right away, and scale back to the
.exercise[
- In a separate window, list pods, and keep watching them:
- In a separate window, watch the list of pods:
```bash
kubectl get pods -w
watch kubectl get pods
```
<!--
```wait Running```
```keys ^C```
```hide kubectl wait deploy pingpong --for condition=available```
```keys kubectl delete pod ping```
```copypaste pong-..........-.....```
-->
- Destroy a pod:
- Destroy the pod currently shown by `kubectl logs`:
```
kubectl delete pod pingpong-xxxxxxxxxx-yyyyy
```
@@ -217,6 +244,23 @@ We could! But the *deployment* would notice it right away, and scale back to the
---
## What happened?
- `kubectl delete pod` terminates the pod gracefully
(sending it the TERM signal and waiting for it to shutdown)
- As soon as the pod is in "Terminating" state, the Replica Set replaces it
- But we can still see the output of the "Terminating" pod in `kubectl logs`
- Until 30 seconds later, when the grace period expires
- The pod is then killed, and `kubectl logs` exits
---
## What if we wanted something different?
- What if we wanted to start a "one-shot" container that *doesn't* get restarted?
@@ -234,6 +278,72 @@ We could! But the *deployment* would notice it right away, and scale back to the
---
## Scheduling periodic background work
- A Cron Job is a job that will be executed at specific intervals
(the name comes from the traditional cronjobs executed by the UNIX crond)
- It requires a *schedule*, represented as five space-separated fields:
- minute [0,59]
- hour [0,23]
- day of the month [1,31]
- month of the year [1,12]
- day of the week ([0,6] with 0=Sunday)
- `*` means "all valid values"; `/N` means "every N"
- Example: `*/3 * * * *` means "every three minutes"
---
## Creating a Cron Job
- Let's create a simple job to be executed every three minutes
- Cron Jobs need to terminate, otherwise they'd run forever
.exercise[
- Create the Cron Job:
```bash
kubectl run --schedule="*/3 * * * *" --restart=OnFailure --image=alpine sleep 10
```
- Check the resource that was created:
```bash
kubectl get cronjobs
```
]
---
## Cron Jobs in action
- At the specified schedule, the Cron Job will create a Job
- The Job will create a Pod
- The Job will make sure that the Pod completes
(re-creating another one if it fails, for instance if its node fails)
.exercise[
- Check the Jobs that are created:
```bash
kubectl get jobs
```
]
(It will take a few minutes before the first job is scheduled.)
---
## What about that deprecation warning?
- As we can see from the previous slide, `kubectl run` can do many things

View File

@@ -34,11 +34,11 @@
- Download the `kubectl` binary from one of these links:
[Linux](https://storage.googleapis.com/kubernetes-release/release/v1.15.2/bin/linux/amd64/kubectl)
[Linux](https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl)
|
[macOS](https://storage.googleapis.com/kubernetes-release/release/v1.15.2/bin/darwin/amd64/kubectl)
[macOS](https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/darwin/amd64/kubectl)
|
[Windows](https://storage.googleapis.com/kubernetes-release/release/v1.15.2/bin/windows/amd64/kubectl.exe)
[Windows](https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/windows/amd64/kubectl.exe)
- On Linux and macOS, make the binary executable with `chmod +x kubectl`

View File

@@ -66,6 +66,8 @@ Exactly what we need!
sudo chmod +x /usr/local/bin/stern
```
- On OS X, just `brew install stern`
<!-- ##VERSION## -->
---

View File

@@ -218,6 +218,18 @@ class: extra-details
## What's going on?
- Without the `--network-plugin` flag, kubelet defaults to "no-op" networking
- It lets the container engine use a default network
(in that case, we end up with the default Docker bridge)
- Our pods are running on independent, disconnected, host-local networks
---
## What do we need to do?
- On a normal cluster, kubelet is configured to set up pod networking with CNI plugins
- This requires:
@@ -228,14 +240,6 @@ class: extra-details
- running kubelet with `--network-plugin=cni`
- Without the `--network-plugin` flag, kubelet defaults to "no-op" networking
- It lets the container engine use a default network
(in that case, we end up with the default Docker bridge)
- Our pods are running on independent, disconnected, host-local networks
---
## Using network plugins
@@ -394,7 +398,7 @@ class: extra-details
- Start kube-proxy:
```bash
sudo kube-proxy --kubeconfig ~/kubeconfig
sudo kube-proxy --kubeconfig ~/.kube/config
```
- Expose our Deployment:

View File

@@ -11,16 +11,36 @@
- Deploy everything else:
```bash
set -u
for SERVICE in hasher rng webui worker; do
kubectl create deployment $SERVICE --image=$REGISTRY/$SERVICE:$TAG
done
kubectl create deployment hasher --image=dockercoins/hasher:v0.1
kubectl create deployment rng --image=dockercoins/rng:v0.1
kubectl create deployment webui --image=dockercoins/webui:v0.1
kubectl create deployment worker --image=dockercoins/worker:v0.1
```
]
---
class: extra-details
## Deploying other images
- If we wanted to deploy images from another registry ...
- ... Or with a different tag ...
- ... We could use the following snippet:
```bash
REGISTRY=dockercoins
TAG=v0.1
for SERVICE in hasher rng webui worker; do
kubectl create deployment $SERVICE --image=$REGISTRY/$SERVICE:$TAG
done
```
---
## Is this working?
- After waiting for the deployment to complete, let's look at the logs!

View File

@@ -60,10 +60,12 @@
(by default: every minute; can be more/less frequent)
- If you're worried about parsing overhead: exporters can also use protobuf
- The list of URLs to scrape (the *scrape targets*) is defined in configuration
.footnote[Worried about the overhead of parsing a text format?
<br/>
Check this [comparison](https://github.com/RichiH/OpenMetrics/blob/master/markdown/protobuf_vs_text.md) of the text format with the (now deprecated) protobuf format!]
---
## Defining scrape targets

View File

@@ -515,3 +515,24 @@ services.nodeports 0 0
(with `kubectl describe resourcequota ...`)
- Rinse and repeat regularly
---
## Additional resources
- [A Practical Guide to Setting Kubernetes Requests and Limits](http://blog.kubecost.com/blog/requests-and-limits/)
- explains what requests and limits are
- provides guidelines to set requests and limits
- gives PromQL expressions to compute good values
<br/>(our app needs to be running for a while)
- [Kube Resource Report](https://github.com/hjacobs/kube-resource-report/)
- generates web reports on resource usage
- [static demo](https://hjacobs.github.io/kube-resource-report/sample-report/output/index.html)
|
[live demo](https://kube-resource-report.demo.j-serv.de/applications.html)

View File

@@ -61,32 +61,6 @@
---
## Building a new version of the `worker` service
.warning[
Only run these commands if you have built and pushed DockerCoins to a local registry.
<br/>
If you are using images from the Docker Hub (`dockercoins/worker:v0.1`), skip this.
]
.exercise[
- Go to the `stacks` directory (`~/container.training/stacks`)
- Edit `dockercoins/worker/worker.py`; update the first `sleep` line to sleep 1 second
- Build a new tag and push it to the registry:
```bash
#export REGISTRY=localhost:3xxxx
export TAG=v0.2
docker-compose -f dockercoins.yml build
docker-compose -f dockercoins.yml push
```
]
---
## Rolling out the new `worker` service
.exercise[
@@ -105,7 +79,7 @@ If you are using images from the Docker Hub (`dockercoins/worker:v0.1`), skip th
- Update `worker` either with `kubectl edit`, or by running:
```bash
kubectl set image deploy worker worker=$REGISTRY/worker:$TAG
kubectl set image deploy worker worker=dockercoins/worker:v0.2
```
]
@@ -146,8 +120,7 @@ That rollout should be pretty quick. What shows in the web UI?
- Update `worker` by specifying a non-existent image:
```bash
export TAG=v0.3
kubectl set image deploy worker worker=$REGISTRY/worker:$TAG
kubectl set image deploy worker worker=dockercoins/worker:v0.3
```
- Check what's going on:
@@ -216,27 +189,14 @@ If you didn't deploy the Kubernetes dashboard earlier, just skip this slide.
.exercise[
- Check which port the dashboard is on:
```bash
kubectl -n kube-system get svc socat
```
- Connect to the dashboard that we deployed earlier
- Check that we have failures in Deployments, Pods, and Replica Sets
- Can we see the reason for the failure?
]
Note the `3xxxx` port.
.exercise[
- Connect to http://oneofournodes:3xxxx/
<!-- ```open https://node1:3xxxx/``` -->
]
--
- We have failures in Deployments, Pods, and Replica Sets
---
## Recovering from a bad rollout
@@ -285,7 +245,7 @@ spec:
spec:
containers:
- name: worker
image: $REGISTRY/worker:v0.1
image: dockercoins/worker:v0.1
strategy:
rollingUpdate:
maxUnavailable: 0
@@ -316,7 +276,7 @@ class: extra-details
spec:
containers:
- name: worker
image: $REGISTRY/worker:v0.1
image: dockercoins/worker:v0.1
strategy:
rollingUpdate:
maxUnavailable: 0

View File

@@ -61,7 +61,8 @@
- [minikube](https://kubernetes.io/docs/setup/minikube/),
[kubespawn](https://github.com/kinvolk/kube-spawn),
[Docker Desktop](https://docs.docker.com/docker-for-mac/kubernetes/):
[Docker Desktop](https://docs.docker.com/docker-for-mac/kubernetes/),
[kind](https://kind.sigs.k8s.io):
for local development
- [kubicorn](https://github.com/kubicorn/kubicorn),

View File

@@ -18,7 +18,7 @@ with a cloud provider
---
## EKS (the hard way)
## EKS (the old way)
- [Read the doc](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html)
@@ -36,7 +36,7 @@ with a cloud provider
---
## EKS (the easy way)
## EKS (the new way)
- Install `eksctl`

View File

@@ -224,7 +224,7 @@ In the manifest, the pod was named `hello`.
```yaml
apiVersion: v1
Kind: Pod
kind: Pod
metadata:
name: hello
namespace: default

View File

@@ -1,6 +1,6 @@
## Versions installed
- Kubernetes 1.15.2
- Kubernetes 1.15.3
- Docker Engine 19.03.1
- Docker Compose 1.24.1

93
slides/k8s/yamldeploy.md Normal file
View File

@@ -0,0 +1,93 @@
# Deploying with YAML
- So far, we created resources with the following commands:
- `kubectl run`
- `kubectl create deployment`
- `kubectl expose`
- We can also create resources directly with YAML manifests
---
## `kubectl apply` vs `create`
- `kubectl create -f whatever.yaml`
- creates resources if they don't exist
- if resources already exist, don't alter them
<br/>(and display error message)
- `kubectl apply -f whatever.yaml`
- creates resources if they don't exist
- if resources already exist, update them
<br/>(to match the definition provided by the YAML file)
- stores the manifest as an *annotation* in the resource
---
## Creating multiple resources
- The manifest can contain multiple resources separated by `---`
```yaml
kind: ...
apiVersion: ...
metadata: ...
name: ...
...
---
kind: ...
apiVersion: ...
metadata: ...
name: ...
...
```
---
## Creating multiple resources
- The manifest can also contain a list of resources
```yaml
apiVersion: v1
kind: List
items:
- kind: ...
apiVersion: ...
...
- kind: ...
apiVersion: ...
...
```
---
## Deploying dockercoins with YAML
- We provide a YAML manifest with all the resources for Dockercoins
(Deployments and Services)
- We can use it if we need to deploy or redeploy Dockercoins
.exercise[
- Deploy or redeploy Dockercoins:
```bash
kubectl apply -f ~/container.training/k8s/dockercoins.yaml
```
]
(If we deployed Dockercoins earlier, we will see warning messages,
because the resources that we created lack the necessary annotation.
We can safely ignore them.)

View File

@@ -80,7 +80,7 @@ def flatten(titles):
def generatefromyaml(manifest, filename):
manifest = yaml.load(manifest)
manifest = yaml.safe_load(manifest)
markdown, titles = processchapter(manifest["chapters"], filename)
logging.debug("Found {} titles.".format(len(titles)))
@@ -111,6 +111,7 @@ def generatefromyaml(manifest, filename):
html = html.replace("@@GITREPO@@", manifest["gitrepo"])
html = html.replace("@@SLIDES@@", manifest["slides"])
html = html.replace("@@TITLE@@", manifest["title"].replace("\n", " "))
html = html.replace("@@SLIDENUMBERPREFIX@@", manifest.get("slidenumberprefix", ""))
return html

View File

@@ -4,7 +4,12 @@ class: in-person
.exercise[
- Log into the first VM (`node1`) with your SSH client
- Log into the first VM (`node1`) with your SSH client:
```bash
ssh `user`@`A.B.C.D`
```
(Replace `user` and `A.B.C.D` with the user and IP address provided to you)
<!--
```bash
@@ -18,16 +23,13 @@ done
```
-->
- Check that you can SSH (without password) to `node2`:
```bash
ssh node2
```
- Type `exit` or `^D` to come back to `node1`
<!-- ```bash exit``` -->
]
You should see a prompt looking like this:
```
[A.B.C.D] (...) user@node1 ~
$
```
If anything goes wrong — ask for help!
---
@@ -52,6 +54,20 @@ If anything goes wrong — ask for help!
---
## For a consistent Kubernetes experience ...
- If you are using your own Kubernetes cluster, you can use [shpod](https://github.com/jpetazzo/shpod)
- `shpod` provides a shell running in a pod on your own cluster
- It comes with many tools pre-installed (helm, stern...)
- These tools are used in many exercises in these slides
- `shpod` also gives you completion and a fancy prompt
---
class: self-paced
## Get your own Docker nodes

29
slides/shared/webssh.md Normal file
View File

@@ -0,0 +1,29 @@
## WebSSH
- The virtual machines are also accessible via WebSSH
- This can be useful if:
- you can't install an SSH client on your machine
- SSH connections are blocked (by firewall or local policy)
- To use WebSSH, connect to the IP address of the remote VM on port 1080
(each machine runs a WebSSH server)
- Then provide the login and password indicated on your card
---
## Good to know
- WebSSH uses WebSocket
- If you're having connections issues, try to disable your HTTP proxy
(many HTTP proxies can't handle WebSocket properly)
- Most keyboard shortcuts should work, except Ctrl-W
(as it is hardwired by the browser to "close this tab")

View File

@@ -28,6 +28,7 @@
var slideshow = remark.create({
ratio: '16:9',
highlightSpans: true,
slideNumberFormat: '@@SLIDENUMBERPREFIX@@%current%/%total%',
excludedClasses: [@@EXCLUDE@@]
});
</script>