diff --git a/k8s/dockercoins.yaml b/k8s/dockercoins.yaml new file mode 100644 index 00000000..4e754715 --- /dev/null +++ b/k8s/dockercoins.yaml @@ -0,0 +1,160 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: hasher + name: hasher +spec: + replicas: 1 + selector: + matchLabels: + app: hasher + template: + metadata: + labels: + app: hasher + spec: + containers: + - image: dockercoins/hasher:v0.1 + name: hasher +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: hasher + name: hasher +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + app: hasher + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: redis + name: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - image: redis + name: redis +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: redis + name: redis +spec: + ports: + - port: 6379 + protocol: TCP + targetPort: 6379 + selector: + app: redis + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: rng + name: rng +spec: + replicas: 1 + selector: + matchLabels: + app: rng + template: + metadata: + labels: + app: rng + spec: + containers: + - image: dockercoins/rng:v0.1 + name: rng +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: rng + name: rng +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + app: rng + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: webui + name: webui +spec: + replicas: 1 + selector: + matchLabels: + app: webui + template: + metadata: + labels: + app: webui + spec: + containers: + - image: dockercoins/webui:v0.1 + name: webui +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: webui + name: webui +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + app: webui + type: NodePort +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: worker + name: worker +spec: + replicas: 1 + selector: + matchLabels: + app: worker + template: + metadata: + labels: + app: worker + spec: + containers: + - image: dockercoins/worker:v0.1 + name: worker diff --git a/k8s/efk.yaml b/k8s/efk.yaml index 48d6c1ce..f2c9ea84 100644 --- a/k8s/efk.yaml +++ b/k8s/efk.yaml @@ -32,13 +32,16 @@ subjects: name: fluentd namespace: default --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd labels: app: fluentd spec: + selector: + matchLabels: + app: fluentd template: metadata: labels: @@ -86,7 +89,7 @@ spec: hostPath: path: /var/lib/docker/containers --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: labels: @@ -128,7 +131,7 @@ spec: app: elasticsearch type: ClusterIP --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: labels: diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml index e322bc10..a40f3dff 100644 --- a/k8s/ingress.yaml +++ b/k8s/ingress.yaml @@ -1,14 +1,13 @@ apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: - name: cheddar + name: kibana spec: rules: - - host: px.3.123.33.38.nip.io + - host: kibana.185.145.251.54.nip.io http: paths: - path: / backend: - serviceName: px-lighthouse - servicePort: 80 - + serviceName: kibana + servicePort: 5601 diff --git a/k8s/insecure-dashboard.yaml b/k8s/insecure-dashboard.yaml index 44ec1d65..43ea51fa 100644 --- a/k8s/insecure-dashboard.yaml +++ b/k8s/insecure-dashboard.yaml @@ -162,7 +162,7 @@ spec: selector: k8s-app: kubernetes-dashboard --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: labels: diff --git a/k8s/just-a-pod.yaml b/k8s/just-a-pod.yaml index 346525de..a6d1ff4f 100644 --- a/k8s/just-a-pod.yaml +++ b/k8s/just-a-pod.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -Kind: Pod +kind: Pod metadata: name: hello namespace: default diff --git a/k8s/local-path-storage.yaml b/k8s/local-path-storage.yaml index 7374a02a..b163792a 100644 --- a/k8s/local-path-storage.yaml +++ b/k8s/local-path-storage.yaml @@ -45,7 +45,7 @@ subjects: name: local-path-provisioner-service-account namespace: local-path-storage --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: local-path-provisioner diff --git a/k8s/metrics-server.yaml b/k8s/metrics-server.yaml index 5864e25d..ddb2998d 100644 --- a/k8s/metrics-server.yaml +++ b/k8s/metrics-server.yaml @@ -58,7 +58,7 @@ metadata: name: metrics-server namespace: kube-system --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: metrics-server diff --git a/k8s/traefik.yaml b/k8s/traefik.yaml index 72b4173b..959ea198 100644 --- a/k8s/traefik.yaml +++ b/k8s/traefik.yaml @@ -6,13 +6,16 @@ metadata: namespace: kube-system --- kind: DaemonSet -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 metadata: name: traefik-ingress-controller namespace: kube-system labels: k8s-app: traefik-ingress-lb spec: + selector: + matchLabels: + k8s-app: traefik-ingress-lb template: metadata: labels: @@ -26,7 +29,7 @@ spec: serviceAccountName: traefik-ingress-controller terminationGracePeriodSeconds: 60 containers: - - image: traefik + - image: traefik:1.7 name: traefik-ingress-lb ports: - name: http diff --git a/prepare-local/README.md b/prepare-local/README.md index 0fb0057f..96ad0ba3 100644 --- a/prepare-local/README.md +++ b/prepare-local/README.md @@ -7,9 +7,9 @@ workshop. ## 1. Prerequisites - Virtualbox, Vagrant and Ansible + - Virtualbox: https://www.virtualbox.org/wiki/Downloads - Vagrant: https://www.vagrantup.com/downloads.html @@ -25,7 +25,7 @@ Virtualbox, Vagrant and Ansible $ git clone --recursive https://github.com/ansible/ansible.git $ cd ansible - $ git checkout stable-2.0.0.1 + $ git checkout stable-{{ getStableVersionFromAnsibleProject }} $ git submodule update - source the setup script to make Ansible available on this terminal session: @@ -38,6 +38,7 @@ Virtualbox, Vagrant and Ansible ## 2. Preparing the environment +Change into directory that has your Vagrantfile Run the following commands: @@ -66,6 +67,14 @@ will reflect inside the instance. - Depending on the Vagrant version, `sudo apt-get install bsdtar` may be needed +- If you get an error like "no Vagrant file found" or you have a file but "cannot open base box" when running `vagrant up`, +chances are good you not in the correct directory. +Make sure you are in sub directory named "prepare-local". It has all the config files required by ansible, vagrant and virtualbox + +- If you are using Python 3.7, running the ansible-playbook provisioning, see an error like "SyntaxError: invalid syntax" and it mentions +the word "async", you need to upgrade your Ansible version to 2.6 or higher to resolve the keyword conflict. +https://github.com/ansible/ansible/issues/42105 + - If you get strange Ansible errors about dependencies, try to check your pip version with `pip --version`. The current version is 8.1.1. If your pip is older than this, upgrade it with `sudo pip install --upgrade pip`, restart diff --git a/prepare-vms/README.md b/prepare-vms/README.md index f325946b..4dbaff56 100644 --- a/prepare-vms/README.md +++ b/prepare-vms/README.md @@ -10,15 +10,21 @@ These tools can help you to create VMs on: - [Docker](https://docs.docker.com/engine/installation/) - [Docker Compose](https://docs.docker.com/compose/install/) -- [Parallel SSH](https://code.google.com/archive/p/parallel-ssh/) (on a Mac: `brew install pssh`) - the configuration scripts require this +- [Parallel SSH](https://code.google.com/archive/p/parallel-ssh/) (on a Mac: `brew install pssh`) Depending on the infrastructure that you want to use, you also need to install the Azure CLI, the AWS CLI, or terraform (for OpenStack deployment). And if you want to generate printable cards: -- [pyyaml](https://pypi.python.org/pypi/PyYAML) (on a Mac: `brew install pyyaml`) -- [jinja2](https://pypi.python.org/pypi/Jinja2) (on a Mac: `brew install jinja2`) +- [pyyaml](https://pypi.python.org/pypi/PyYAML) +- [jinja2](https://pypi.python.org/pypi/Jinja2) + +You can install them with pip (perhaps with `pip install --user`, or even use `virtualenv` if that's your thing). + +These require Python 3. If you are on a Mac, see below for specific instructions on setting up +Python 3 to be the default Python on a Mac. In particular, if you installed `mosh`, Homebrew +may have changed your default Python to Python 2. ## General Workflow @@ -87,26 +93,37 @@ You're all set! ``` workshopctl - the orchestration workshop swiss army knife Commands: -ami Show the AMI that will be used for deployment -amis List Ubuntu AMIs in the current region -build Build the Docker image to run this program in a container -cards Generate ready-to-print cards for a group of VMs -deploy Install Docker on a bunch of running VMs -ec2quotas Check our EC2 quotas (max instances) -help Show available commands -ids List the instance IDs belonging to a given tag or token -ips List the IP addresses of the VMs for a given tag or token -kube Setup kubernetes clusters with kubeadm (must be run AFTER deploy) -kubetest Check that all notes are reporting as Ready -list List available groups in the current region -opensg Open the default security group to ALL ingress traffic -pull_images Pre-pull a bunch of Docker images -retag Apply a new tag to a group of VMs -start Start a group of VMs -status List instance status for a given group -stop Stop (terminate, shutdown, kill, remove, destroy...) instances -test Run tests (pre-flight checks) on a group of VMs -wrap Run this program in a container +build Build the Docker image to run this program in a container +cards Generate ready-to-print cards for a group of VMs +deploy Install Docker on a bunch of running VMs +disableaddrchecks Disable source/destination IP address checks +disabledocker Stop Docker Engine and don't restart it automatically +helmprom Install Helm and Prometheus +help Show available commands +ids (FIXME) List the instance IDs belonging to a given tag or token +kubebins Install Kubernetes and CNI binaries but don't start anything +kubereset Wipe out Kubernetes configuration on all nodes +kube Setup kubernetes clusters with kubeadm (must be run AFTER deploy) +kubetest Check that all nodes are reporting as Ready +listall List VMs running on all configured infrastructures +list List available groups for a given infrastructure +netfix Disable GRO and run a pinger job on the VMs +opensg Open the default security group to ALL ingress traffic +ping Ping VMs in a given tag, to check that they have network access +pssh Run an arbitrary command on all nodes +pull_images Pre-pull a bunch of Docker images +quotas Check our infrastructure quotas (max instances) +remap_nodeports Remap NodePort range to 10000-10999 +retag (FIXME) Apply a new tag to a group of VMs +ssh Open an SSH session to the first node of a tag +start Start a group of VMs +stop Stop (terminate, shutdown, kill, remove, destroy...) instances +tags List groups of VMs known locally +test Run tests (pre-flight checks) on a group of VMs +weavetest Check that weave seems properly setup +webssh Install a WEB SSH server on the machines (port 1080) +wrap Run this program in a container +www Run a web server to access card HTML and PDF ``` ### Summary of What `./workshopctl` Does For You @@ -245,3 +262,32 @@ If you don't have `wkhtmltopdf` installed, you will get a warning that it is a m - Don't write to bash history in system() in postprep - compose, etc version inconsistent (int vs str) + +## Making sure Python3 is the default (Mac only) + +Check the `/usr/local/bin/python` symlink. It should be pointing to +`/usr/local/Cellar/python/3`-something. If it isn't, follow these +instructions. + +1) Verify that Python 3 is installed. + +``` +ls -la /usr/local/Cellar/Python +``` + +You should see one or more versions of Python 3. If you don't, +install it with `brew install python`. + +2) Verify that `python` points to Python3. + +``` +ls -la /usr/local/bin/python +``` + +If this points to `/usr/local/Cellar/python@2`, then we'll need to change it. + +``` +rm /usr/local/bin/python +ln -s /usr/local/Cellar/Python/xxxx /usr/local/bin/python +# where xxxx is the most recent Python 3 version you saw above +``` diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh index 5fb3ed76..ade36b6e 100644 --- a/prepare-vms/lib/commands.sh +++ b/prepare-vms/lib/commands.sh @@ -127,11 +127,11 @@ _cmd_kubebins() { set -e cd /usr/local/bin if ! [ -x etcd ]; then - curl -L https://github.com/etcd-io/etcd/releases/download/v3.3.10/etcd-v3.3.10-linux-amd64.tar.gz \ + curl -L https://github.com/etcd-io/etcd/releases/download/v3.3.15/etcd-v3.3.15-linux-amd64.tar.gz \ | sudo tar --strip-components=1 --wildcards -zx '*/etcd' '*/etcdctl' fi if ! [ -x hyperkube ]; then - curl -L https://dl.k8s.io/v1.14.1/kubernetes-server-linux-amd64.tar.gz \ + curl -L https://dl.k8s.io/v1.16.2/kubernetes-server-linux-amd64.tar.gz \ | sudo tar --strip-components=3 -zx kubernetes/server/bin/hyperkube fi if ! [ -x kubelet ]; then @@ -143,7 +143,7 @@ _cmd_kubebins() { sudo mkdir -p /opt/cni/bin cd /opt/cni/bin if ! [ -x bridge ]; then - curl -L https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-amd64-v0.7.5.tgz \ + curl -L https://github.com/containernetworking/plugins/releases/download/v0.7.6/cni-plugins-amd64-v0.7.6.tgz \ | sudo tar -zx fi " @@ -157,10 +157,10 @@ _cmd_kube() { # Optional version, e.g. 1.13.5 KUBEVERSION=$2 if [ "$KUBEVERSION" ]; then - EXTRA_KUBELET="=$KUBEVERSION-00" + EXTRA_APTGET="=$KUBEVERSION-00" EXTRA_KUBEADM="--kubernetes-version=v$KUBEVERSION" else - EXTRA_KUBELET="" + EXTRA_APTGET="" EXTRA_KUBEADM="" fi @@ -172,7 +172,7 @@ _cmd_kube() { sudo tee /etc/apt/sources.list.d/kubernetes.list" pssh --timeout 200 " sudo apt-get update -q && - sudo apt-get install -qy kubelet$EXTRA_KUBELET kubeadm kubectl && + sudo apt-get install -qy kubelet$EXTRA_APTGET kubeadm$EXTRA_APTGET kubectl$EXTRA_APTGET && kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl" # Initialize kube master @@ -386,6 +386,20 @@ _cmd_pull_images() { pull_tag } +_cmd remap_nodeports "Remap NodePort range to 10000-10999" +_cmd_remap_nodeports() { + TAG=$1 + need_tag + + FIND_LINE=" - --service-cluster-ip-range=10.96.0.0\/12" + ADD_LINE=" - --service-node-port-range=10000-10999" + MANIFEST_FILE=/etc/kubernetes/manifests/kube-apiserver.yaml + pssh " + if i_am_first_node && ! grep -q '$ADD_LINE' $MANIFEST_FILE; then + sudo sed -i 's/\($FIND_LINE\)\$/\1\n$ADD_LINE/' $MANIFEST_FILE + fi" +} + _cmd quotas "Check our infrastructure quotas (max instances)" _cmd_quotas() { need_infra $1 @@ -582,7 +596,7 @@ _cmd_www() { echo "http://$IPADDR:8000/$F" done info "Press Ctrl-C to stop server." - python -m http.server + python3 -m http.server } greet() { diff --git a/prepare-vms/lib/ips-txt-to-html.py b/prepare-vms/lib/ips-txt-to-html.py index 5bc1873c..6f0891cc 100755 --- a/prepare-vms/lib/ips-txt-to-html.py +++ b/prepare-vms/lib/ips-txt-to-html.py @@ -4,17 +4,12 @@ import sys import yaml import jinja2 -def prettify(l): - l = [ip.strip() for ip in l] - ret = [ "node{}: {}".format(i+1, s) for (i, s) in zip(range(len(l)), l) ] - return ret # Read settings from user-provided settings file -SETTINGS = yaml.load(open(sys.argv[1])) - -clustersize = SETTINGS["clustersize"] +context = yaml.safe_load(open(sys.argv[1])) ips = list(open("ips.txt")) +clustersize = context["clustersize"] print("---------------------------------------------") print(" Number of IPs: {}".format(len(ips))) @@ -30,7 +25,9 @@ while ips: ips = ips[clustersize:] clusters.append(cluster) -template_file_name = SETTINGS["cards_template"] +context["clusters"] = clusters + +template_file_name = context["cards_template"] template_file_path = os.path.join( os.path.dirname(__file__), "..", @@ -39,18 +36,19 @@ template_file_path = os.path.join( ) template = jinja2.Template(open(template_file_path).read()) with open("ips.html", "w") as f: - f.write(template.render(clusters=clusters, **SETTINGS)) + f.write(template.render(**context)) print("Generated ips.html") + try: import pdfkit with open("ips.html") as f: pdfkit.from_file(f, "ips.pdf", options={ - "page-size": SETTINGS["paper_size"], - "margin-top": SETTINGS["paper_margin"], - "margin-bottom": SETTINGS["paper_margin"], - "margin-left": SETTINGS["paper_margin"], - "margin-right": SETTINGS["paper_margin"], + "page-size": context["paper_size"], + "margin-top": context["paper_margin"], + "margin-bottom": context["paper_margin"], + "margin-left": context["paper_margin"], + "margin-right": context["paper_margin"], }) print("Generated ips.pdf") except ImportError: diff --git a/prepare-vms/lib/postprep.py b/prepare-vms/lib/postprep.py index f4cd042a..3ee70ed4 100755 --- a/prepare-vms/lib/postprep.py +++ b/prepare-vms/lib/postprep.py @@ -73,8 +73,29 @@ set expandtab set number set shiftwidth=2 set softtabstop=2 +set nowrap SQRL""") +# Custom .tmux.conf +system( + """sudo -u docker tee /home/docker/.tmux.conf <

{{ intro }}

+ {% if image_src %} + {% endif %} {% if clusternumber != None %} @@ -187,8 +200,10 @@ img.kube {

+ {% if url %} {{ slides_are_at }}

{{ url }}
+ {% endif %}

{% if loop.index%pagesize==0 or loop.last %} diff --git a/slides/index.yaml b/slides/index.yaml index 4c69e4b6..788d3b1f 100644 --- a/slides/index.yaml +++ b/slides/index.yaml @@ -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 diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index 4529d97b..5df7e643 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -9,6 +9,8 @@ gitrepo: github.com/jpetazzo/container.training slides: http://container.training/ +#slidenumberprefix: "#SomeHashTag — " + exclude: - self-paced diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 5ebc32c5..14c494b3 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -9,6 +9,8 @@ gitrepo: github.com/jpetazzo/container.training slides: http://container.training/ +#slidenumberprefix: "#SomeHashTag — " + exclude: - in-person diff --git a/slides/k8s/authn-authz.md b/slides/k8s/authn-authz.md index a2c0c431..57f2c965 100644 --- a/slides/k8s/authn-authz.md +++ b/slides/k8s/authn-authz.md @@ -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`) diff --git a/slides/k8s/buildshiprun-dockerhub.md b/slides/k8s/buildshiprun-dockerhub.md index ea949cfc..3d414c49 100644 --- a/slides/k8s/buildshiprun-dockerhub.md +++ b/slides/k8s/buildshiprun-dockerhub.md @@ -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`) diff --git a/slides/k8s/cluster-upgrade.md b/slides/k8s/cluster-upgrade.md index 0b477ce9..2051d78d 100644 --- a/slides/k8s/cluster-upgrade.md +++ b/slides/k8s/cluster-upgrade.md @@ -10,6 +10,8 @@ - Components can be upgraded one at a time without problems + + --- ## 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. -
It thinks the cluster is running 1.14.0!) +] - +Note 1: kubeadm thinks that our cluster is running 1.15.0. +
It is confused by our manual upgrade of the API server! + +Note 2: kubeadm itself is still version 1.14.6. +
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. +
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!** diff --git a/slides/k8s/configuration.md b/slides/k8s/configuration.md index 47b23ca7..0283e888 100644 --- a/slides/k8s/configuration.md +++ b/slides/k8s/configuration.md @@ -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) --- diff --git a/slides/k8s/dashboard.md b/slides/k8s/dashboard.md index bae2d732..8d284f90 100644 --- a/slides/k8s/dashboard.md +++ b/slides/k8s/dashboard.md @@ -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 `, 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) - diff --git a/slides/k8s/dmuc.md b/slides/k8s/dmuc.md index 17f60543..94658c6b 100644 --- a/slides/k8s/dmuc.md +++ b/slides/k8s/dmuc.md @@ -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 ``` ] diff --git a/slides/k8s/dryrun.md b/slides/k8s/dryrun.md new file mode 100644 index 00000000..f319c8b6 --- /dev/null +++ b/slides/k8s/dryrun.md @@ -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 diff --git a/slides/k8s/healthchecks-more.md b/slides/k8s/healthchecks-more.md index b3093c93..a812966c 100644 --- a/slides/k8s/healthchecks-more.md +++ b/slides/k8s/healthchecks-more.md @@ -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; -
- 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) diff --git a/slides/k8s/healthchecks.md b/slides/k8s/healthchecks.md index a72fedbf..8055bfd8 100644 --- a/slides/k8s/healthchecks.md +++ b/slides/k8s/healthchecks.md @@ -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 diff --git a/slides/k8s/kubectlrun.md b/slides/k8s/kubectlrun.md index 2c8a9c2b..9b7ae2ec 100644 --- a/slides/k8s/kubectlrun.md +++ b/slides/k8s/kubectlrun.md @@ -153,10 +153,7 @@ pod/pingpong-7c8bbcd9bc-6c9qz 1/1 Running 0 10m kubectl logs deploy/pingpong --tail 1 --follow ``` - +- 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 ``` - - -- 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 diff --git a/slides/k8s/localkubeconfig.md b/slides/k8s/localkubeconfig.md index ce58b889..efcae788 100644 --- a/slides/k8s/localkubeconfig.md +++ b/slides/k8s/localkubeconfig.md @@ -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` diff --git a/slides/k8s/logs-cli.md b/slides/k8s/logs-cli.md index 5a9a6c1a..5dda900b 100644 --- a/slides/k8s/logs-cli.md +++ b/slides/k8s/logs-cli.md @@ -66,6 +66,8 @@ Exactly what we need! sudo chmod +x /usr/local/bin/stern ``` +- On OS X, just `brew install stern` + --- diff --git a/slides/k8s/multinode.md b/slides/k8s/multinode.md index af6b6194..829208db 100644 --- a/slides/k8s/multinode.md +++ b/slides/k8s/multinode.md @@ -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: diff --git a/slides/k8s/ourapponkube.md b/slides/k8s/ourapponkube.md index 50eed1ea..abe48620 100644 --- a/slides/k8s/ourapponkube.md +++ b/slides/k8s/ourapponkube.md @@ -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! diff --git a/slides/k8s/prometheus.md b/slides/k8s/prometheus.md index 9032657c..049bea18 100644 --- a/slides/k8s/prometheus.md +++ b/slides/k8s/prometheus.md @@ -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? +
+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 diff --git a/slides/k8s/resource-limits.md b/slides/k8s/resource-limits.md index 2a03c000..25e86406 100644 --- a/slides/k8s/resource-limits.md +++ b/slides/k8s/resource-limits.md @@ -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 +
(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) diff --git a/slides/k8s/rollout.md b/slides/k8s/rollout.md index 5783c020..41aba22d 100644 --- a/slides/k8s/rollout.md +++ b/slides/k8s/rollout.md @@ -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. -
-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/ - - - -] - --- - -- 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 diff --git a/slides/k8s/setup-k8s.md b/slides/k8s/setup-k8s.md index 6b704ef0..91740afa 100644 --- a/slides/k8s/setup-k8s.md +++ b/slides/k8s/setup-k8s.md @@ -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), diff --git a/slides/k8s/setup-managed.md b/slides/k8s/setup-managed.md index 065f4e4f..e13fd18a 100644 --- a/slides/k8s/setup-managed.md +++ b/slides/k8s/setup-managed.md @@ -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` diff --git a/slides/k8s/staticpods.md b/slides/k8s/staticpods.md index 31ad23ea..63974122 100644 --- a/slides/k8s/staticpods.md +++ b/slides/k8s/staticpods.md @@ -224,7 +224,7 @@ In the manifest, the pod was named `hello`. ```yaml apiVersion: v1 -Kind: Pod +kind: Pod metadata: name: hello namespace: default diff --git a/slides/k8s/versions-k8s.md b/slides/k8s/versions-k8s.md index e939876f..8f28c203 100644 --- a/slides/k8s/versions-k8s.md +++ b/slides/k8s/versions-k8s.md @@ -1,6 +1,6 @@ ## Versions installed -- Kubernetes 1.15.2 +- Kubernetes 1.15.3 - Docker Engine 19.03.1 - Docker Compose 1.24.1 diff --git a/slides/k8s/yamldeploy.md b/slides/k8s/yamldeploy.md new file mode 100644 index 00000000..f9bf9250 --- /dev/null +++ b/slides/k8s/yamldeploy.md @@ -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 +
(and display error message) + +- `kubectl apply -f whatever.yaml` + + - creates resources if they don't exist + + - if resources already exist, update them +
(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.) + diff --git a/slides/markmaker.py b/slides/markmaker.py index dd50291f..37cbf6bc 100755 --- a/slides/markmaker.py +++ b/slides/markmaker.py @@ -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 diff --git a/slides/shared/connecting.md b/slides/shared/connecting.md index 2e83d996..eb40ec6d 100644 --- a/slides/shared/connecting.md +++ b/slides/shared/connecting.md @@ -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) -- Check that you can SSH (without password) to `node2`: - ```bash - ssh node2 - ``` -- Type `exit` or `^D` to come back to `node1` - - - ] +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 diff --git a/slides/shared/webssh.md b/slides/shared/webssh.md new file mode 100644 index 00000000..45f345c0 --- /dev/null +++ b/slides/shared/webssh.md @@ -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") diff --git a/slides/workshop.html b/slides/workshop.html index ffb378ce..96f56b04 100644 --- a/slides/workshop.html +++ b/slides/workshop.html @@ -28,6 +28,7 @@ var slideshow = remark.create({ ratio: '16:9', highlightSpans: true, + slideNumberFormat: '@@SLIDENUMBERPREFIX@@%current%/%total%', excludedClasses: [@@EXCLUDE@@] });
cluster: