From b1adca025d74e45978ece22a982d372424f6a257 Mon Sep 17 00:00:00 2001 From: Anton Weiss Date: Wed, 24 Feb 2021 12:26:44 +0200 Subject: [PATCH 01/73] Add openebs tutorial --- k8s/openebs-pod.yaml | 19 +++++ slides/k8s/openebs.md | 165 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 k8s/openebs-pod.yaml create mode 100644 slides/k8s/openebs.md diff --git a/k8s/openebs-pod.yaml b/k8s/openebs-pod.yaml new file mode 100644 index 00000000..1a5cc6de --- /dev/null +++ b/k8s/openebs-pod.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: openebs-local-hostpath-pod +spec: + volumes: + - name: storage + persistentVolumeClaim: + claimName: local-hostpath-pvc + containers: + - name: better + image: alpine + command: + - sh + - -c + - 'while true; do echo "`date` [`hostname`] Kubernetes is better with PVs." >> /mnt/storage/greet.txt; sleep $(($RANDOM % 5 + 20)); done' + volumeMounts: + - mountPath: /mnt/storage + name: storage diff --git a/slides/k8s/openebs.md b/slides/k8s/openebs.md new file mode 100644 index 00000000..7193e2e4 --- /dev/null +++ b/slides/k8s/openebs.md @@ -0,0 +1,165 @@ +# OpenEBS + + - OpenEBS is a popular open-source storage solution for Kubernetes + + - Think "Container Attached Storage" + + - Supports a wide range of storage engines: + - Jiva: for lighter workloads with basic cloning/snapshotting + + - cStor: based on iSCSI + + - Mayastor: light-weight abstraction layer with nVME and vhost-user support + + - OpenEBS Local PV - for lowest latency local volumes + +--- +## Installing OpenEBS with Helm + +OpenEBS control plane runs as a set of containers on Kubernetes worker nodes. +It can be installed with helm: + +.exercise[ + + - Install OpenEBS +```bash + kubectl create ns openebs + helm repo add openebs https://openebs.github.io/charts + helm repo update + helm install openebs openebs/openebs --namespace openebs +``` +] + +--- + +## Installing OpenEBS with Helm + +Let's check the running OpenEBS components: + +.exercise[ + +```bash + kubectl get pod -n openebs +``` +] + +Let's check the new StorageClasses: + +.exercise[ + +```bash + kubectl get sc +``` +] + +--- + +## Default Storage Classes + +For a simple testing of OpenEBS, you can use the below default storage classes: + + - **openebs-jiva-default** for provisioning Jiva Volume (this uses default pool which means the data replicas are created in the /mnt/openebs_disk directory of the Jiva replica pod) + + - **openebs-hostpath** for provisioning Local PV on hostpath. + + - **openebs-device** for provisioning Local PV on device. + +For using real disks, you have to create *cStorPools* or *Jiva* pools or *OpenEBS Local PV* based on the requirement and then create corresponding StorageClasses or use default StorageClasses to use them. + +--- + +## Selecting an OpenEBS Storage Engine + +Storage engine is chosen by specifying the annotation `openebs.io/cas-type` in the StorageClass specification. StorageClass defines the provisioner details. Separate provisioners are specified for each CAS engine. + +Example for Local PV host path: + +```yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: localpv-hostpath-sc + annotations: + openebs.io/cas-type: local + cas.openebs.io/config: | + - name: BasePath + value: "/var/openebs/local" + - name: StorageType + value: "hostpath" +provisioner: openebs.io/local +``` + +--- + +## Exploring the host path StorageClass + +.exercise[ + - Let's look at the OpenEBS Local PV host path StorageClass + ```bash + kubectl get sc openebs-hostpath -oyaml + ``` +] + +--- + +## Create a host path PVC + +Let's create a Persistent Volume Claim +.exercise[ +```bash +kubectl apply -f - < Date: Sun, 14 Mar 2021 19:21:43 +0100 Subject: [PATCH 02/73] =?UTF-8?q?=E2=9D=93=EF=B8=8F=20Add=20some=20quizzes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/0.yml | 2 ++ slides/k8s/accessinternal.md | 14 ++++++++++++++ slides/k8s/cert-manager.md | 2 ++ slides/k8s/helm-intro.md | 14 ++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 slides/0.yml diff --git a/slides/0.yml b/slides/0.yml new file mode 100644 index 00000000..36bd3a0e --- /dev/null +++ b/slides/0.yml @@ -0,0 +1,2 @@ +content: +- k8s/netpol.md diff --git a/slides/k8s/accessinternal.md b/slides/k8s/accessinternal.md index a647c38a..d70c882d 100644 --- a/slides/k8s/accessinternal.md +++ b/slides/k8s/accessinternal.md @@ -134,3 +134,17 @@ installed and set up `kubectl` to communicate with your cluster. :EN:- Securely accessing internal services :FR:- Accès sécurisé aux services internes + +:T: Accessing internal services from our local machine + +:Q: What's the advantage of "kubectl port-forward" compared to a NodePort? +:A: It can forward arbitrary protocols +:A: It doesn't require Kubernetes API credentials +:A: It offers deterministic load balancing (instead of random) +:A: ✔️It doesn't expose the service to the public + +:Q: What's the security concept behind "kubectl port-forward"? +:A: ✔️We authenticate with the Kubernetes API, and it forwards connections on our behalf +:A: It detects our source IP address, and only allows connections coming from it +:A: It uses end-to-end mTLS (mutual TLS) to authenticate our connections +:A: There is no security (as long as it's running, anyone can connect from anywhere) diff --git a/slides/k8s/cert-manager.md b/slides/k8s/cert-manager.md index dee9193f..b95949b3 100644 --- a/slides/k8s/cert-manager.md +++ b/slides/k8s/cert-manager.md @@ -242,3 +242,5 @@ class: extra-details :EN:- Obtaining certificates with cert-manager :FR:- Obtenir des certificats avec cert-manager + +:T: Obtaining TLS certificates with cert-manager diff --git a/slides/k8s/helm-intro.md b/slides/k8s/helm-intro.md index 6edd0b62..66385ab8 100644 --- a/slides/k8s/helm-intro.md +++ b/slides/k8s/helm-intro.md @@ -462,3 +462,17 @@ All unspecified values will take the default values defined in the chart. :FR:- Fonctionnement général de Helm :FR:- Installer des composants via Helm :FR:- Helm 2, Helm 3, et le *Helm Hub* + +:T: Getting started with Helm and its concepts + +:Q: Which comparison is the most adequate? +:A: Helm is a firewall, charts are access lists +:A: ✔️Helm is a package manager, charts are packages +:A: Helm is an artefact repository, charts are artefacts +:A: Helm is a CI/CD platform, charts are CI/CD pipelines + +:Q: What's required to distribute a Helm chart? +:A: A Helm commercial license +:A: A Docker registry +:A: An account on the Helm Hub +:A: ✔️An HTTP server From a60f9292321822454d80f84bfcad9821bc9cc17b Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 14 Mar 2021 19:22:31 +0100 Subject: [PATCH 03/73] =?UTF-8?q?=E2=98=81=EF=B8=8F=20Add=20support=20for?= =?UTF-8?q?=20Linode=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/lib/commands.sh | 26 ++++++++++++--- prepare-vms/lib/infra/linode.sh | 58 +++++++++++++++++++++++++++++++++ prepare-vms/lib/pssh.sh | 10 +++--- 3 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 prepare-vms/lib/infra/linode.sh diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh index 91e5077e..822a9539 100644 --- a/prepare-vms/lib/commands.sh +++ b/prepare-vms/lib/commands.sh @@ -69,11 +69,14 @@ _cmd_deploy() { echo deploying > tags/$TAG/status sep "Deploying tag $TAG" - # Wait for cloudinit to be done + # If this VM image is using cloud-init, + # wait for cloud-init to be done pssh " - while [ ! -f /var/lib/cloud/instance/boot-finished ]; do - sleep 1 - done" + if [ -d /var/lib/cloud ]; then + while [ ! -f /var/lib/cloud/instance/boot-finished ]; do + sleep 1 + done + fi" # Special case for scaleway since it doesn't come with sudo if [ "$INFRACLASS" = "scaleway" ]; then @@ -102,6 +105,12 @@ _cmd_deploy() { sudo apt-get update && sudo apt-get install -y python-yaml" + # If there is no "python" binary, symlink to python3 + #pssh " + #if ! which python; then + # ln -s $(which python3) /usr/local/bin/python + #fi" + # Copy postprep.py to the remote machines, and execute it, feeding it the list of IP addresses pssh -I tee /tmp/postprep.py >/tmp/pp.out 2>>/tmp/pp.err" /tmp/token && diff --git a/prepare-vms/lib/infra/linode.sh b/prepare-vms/lib/infra/linode.sh new file mode 100644 index 00000000..8a7630d3 --- /dev/null +++ b/prepare-vms/lib/infra/linode.sh @@ -0,0 +1,58 @@ +if ! command -v linode-cli >/dev/null; then + warn "Linode CLI (linode-cli) not found." +fi +if ! [ -f ~/.config/linode-cli ]; then + warn "~/.config/linode-cli not found." +fi + +# To view available regions: "linode-cli regions list" +LINODE_REGION=${LINODE_REGION-us-west} + +# To view available types: "linode-cli linodes types" +LINODE_TYPE=${LINODE_TYPE-g6-standard-2} + +infra_list() { + linode-cli linodes list --json | + jq -r '.[] | [.id, .label, .status, .type] | @tsv' +} + +infra_start() { + COUNT=$1 + + for I in $(seq 1 $COUNT); do + NAME=$(printf "%s-%03d" $TAG $I) + sep "Starting instance $I/$COUNT" + info " Zone: $LINODE_REGION" + info " Name: $NAME" + info " Instance type: $LINODE_TYPE" + ROOT_PASS="$(base64 /dev/urandom | cut -c1-20 | head -n 1)" + linode-cli linodes create \ + --type=${LINODE_TYPE} --region=${LINODE_REGION} \ + --image=linode/ubuntu18.04 \ + --authorized_keys="${LINODE_SSHKEY}" \ + --root_pass="${ROOT_PASS}" \ + --tags=${TAG} --label=${NAME} + done + sep + + linode_get_ips_by_tag $TAG > tags/$TAG/ips.txt +} + +infra_stop() { + info "Counting instances..." + linode_get_ids_by_tag $TAG | wc -l + info "Deleting instances..." + linode_get_ids_by_tag $TAG | + xargs -n1 -P10 \ + linode-cli linodes delete +} + +linode_get_ids_by_tag() { + TAG=$1 + linode-cli linodes list --tags $TAG --json | jq -r ".[].id" +} + +linode_get_ips_by_tag() { + TAG=$1 + linode-cli linodes list --tags $TAG --json | jq -r ".[].ipv4[0]" +} diff --git a/prepare-vms/lib/pssh.sh b/prepare-vms/lib/pssh.sh index ca3bc639..fb855696 100644 --- a/prepare-vms/lib/pssh.sh +++ b/prepare-vms/lib/pssh.sh @@ -18,11 +18,11 @@ pssh() { echo "[parallel-ssh] $@" export PSSH=$(which pssh || which parallel-ssh) - if [ "$INFRACLASS" = hetzner ]; then - LOGIN=root - else - LOGIN=ubuntu - fi + case "$INFRACLASS" in + hetzner) LOGIN=root ;; + linode) LOGIN=root ;; + *) LOGIN=ubuntu ;; + esac $PSSH -h $HOSTFILE -l $LOGIN \ --par 100 \ From 366c656d825e6705cfba17b1943de09b678d7619 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 17 Mar 2021 23:55:26 +0100 Subject: [PATCH 04/73] =?UTF-8?q?=F0=9F=93=83=20Minor=20slides=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/kubenet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides/k8s/kubenet.md b/slides/k8s/kubenet.md index 86bf2d44..abbc18ca 100644 --- a/slides/k8s/kubenet.md +++ b/slides/k8s/kubenet.md @@ -52,7 +52,7 @@ - There are literally dozens of implementations out there - (15 are listed in the Kubernetes documentation) + (https://github.com/containernetworking/cni/ lists more than 25 plugins) - Pods have level 3 (IP) connectivity, but *services* are level 4 (TCP or UDP) From 894dafeecb2a0696608ffa7445b9e29f48586f14 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 18 Mar 2021 14:57:46 +0100 Subject: [PATCH 05/73] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Improve=20error=20re?= =?UTF-8?q?porting=20for=20missing=20content=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/markmaker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/slides/markmaker.py b/slides/markmaker.py index 5173cd93..7a5a0772 100755 --- a/slides/markmaker.py +++ b/slides/markmaker.py @@ -213,6 +213,7 @@ def processcontent(content, filename): return (content, titles) if os.path.isfile(content): return processcontent(open(content).read(), content) + logging.warning("Content spans only one line (it's probably a file name) but no file found: {}".format(content)) if isinstance(content, list): subparts = [processcontent(c, filename) for c in content] markdown = "\n---\n".join(c[0] for c in subparts) From b4576e39d013a8ca31242c15db4705999bac4a73 Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:05:42 +0100 Subject: [PATCH 06/73] Add a quick/dirty script to associate a role with the default service account in the default namespace granting r/o access to an s3 bucket --- bin/sa-can-read-s3.sh | 128 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100755 bin/sa-can-read-s3.sh diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh new file mode 100755 index 00000000..74499788 --- /dev/null +++ b/bin/sa-can-read-s3.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# I would like to demonstrate access to AWS resource (e.g. S3 bucket) from a pod. Idea: +# create a bucket, put two objects in it (one public, one private), then … I suppose I +# need to create a role with access to the private object, associate the role to a service +# account in k8s, find an image with the aws CLI (or some s3 client) in it … ? + +set -euo pipefail + +emit_describe_cluster_policy() { + # Not used right now, but this permission is required in order to run `aws eks update-kubeconfig`: + echo '{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "eks:DescribeCluster" + ], + "Resource": "'"arn:aws:eks:${REGION}:${ACCOUNT_ID}:cluster/${CLUSTER_NAME}"'", + "Effect": "Allow" + } + ] +}' +} + +create_describe_cluster_policy() { + local role_name="can-describe-cluster" + aws iam create-policy \ + --policy-name "${role_name}" \ + --description "Policy allowing to describe ${CLUSTER_NAME}" \ + --policy-document "$(emit_describe_cluster_policy)" + + aws iam attach-user-policy --role-name "${role_name}" --policy-arn "${S3_POLICY_ARN}" +} + +emit_service_account_role_trust_policy() { + local oidc_provider_arn key_prefix + oidc_provider_arn="$(aws iam list-open-id-connect-providers | jq -r '.OpenIDConnectProviderList[0].Arn')" + key_prefix="$(echo "${oidc_provider_arn}" | cut -f2- -d '/')" + + echo '{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "'"${oidc_provider_arn}"'" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "'"${key_prefix}:sub"'": "system:serviceaccount:default:default" + } + } + } + ] +}' +} + +associate_oidc_provider() { + local issuer_url + issuer_url="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.identity.oidc.issuer" --output text)" + if ! aws iam list-open-id-connect-providers | grep "${issuer_url}"; then + eksctl utils associate-iam-oidc-provider --cluster "${CLUSTER_NAME}" --approve + else + echo "OIDC provider already associated" + fi +} + +create_role() { + if ! _="$(aws iam get-role --role-name "${ROLE_NAME}")"; then + aws iam create-role --role-name "${ROLE_NAME}" --description "Role for service account" --assume-role-policy-document "$(emit_service_account_role_trust_policy)" + else + echo "Role ${ROLE_NAME} already exists" + fi +} + +annotate_serviceaccount() { + kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" +} + +checkit() { + kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true herro -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - +} + +update_kubeconfig() { + aws eks update-kubeconfig --name "${CLUSTER_NAME}" +} + +teardown() { + aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" + aws iam delete-role "${ROLE_NAME}" + aws iam delete-policy --policy-arn +} + +create_and_populate_bucket() { + if ! _="$(aws s3api get-bucket-acl --bucket "${BUCKET_NAME}")"; then + aws s3api create-bucket --region "${REGION}" --bucket "${BUCKET_NAME}" --create-bucket-configuration "LocationConstraint=${REGION}" + else + echo "Bucket ${BUCKET_NAME} already exists." + fi + f="$(mktemp)" + echo "THE UNICORN IS IN THE GARDEN!!" >"${f}" + aws s3api put-object --bucket "${BUCKET_NAME}" --key top-sekret.txt --body "${f}" +} + +ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" +S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess +CLUSTER_NAME=floral-mongoose-1616851817 +ROLE_NAME=service-account-role +REGION=eu-north-1 +BUCKET_NAME=wooga-booga-pants +export KUBECONFIG=myconfig + +main() { + if [ -n "${1:-}" ]; then + echo "An argument was provided, running that: $1" + "${1}" + else + echo "ACCOUNT_ID: $ACCOUNT_ID" + associate_oidc_provider + create_role + aws iam attach-role-policy --role-name "${ROLE_NAME}" --policy-arn "${S3_POLICY_ARN}" + annotate_serviceaccount + checkit + fi +} + +main "$@" From ab959220ba3c333576ad29bb225da96021792ed6 Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:12:30 +0100 Subject: [PATCH 07/73] Remove partial teardown command --- bin/sa-can-read-s3.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index 74499788..a6d001de 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -25,7 +25,7 @@ emit_describe_cluster_policy() { create_describe_cluster_policy() { local role_name="can-describe-cluster" aws iam create-policy \ - --policy-name "${role_name}" \ + --policy-name can-describe-cluster \ --description "Policy allowing to describe ${CLUSTER_NAME}" \ --policy-document "$(emit_describe_cluster_policy)" @@ -87,9 +87,9 @@ update_kubeconfig() { } teardown() { + # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" aws iam delete-role "${ROLE_NAME}" - aws iam delete-policy --policy-arn } create_and_populate_bucket() { From 02278b37489ab77dd45f771a9781babbb804a53b Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:13:34 +0100 Subject: [PATCH 08/73] Add --overwrite when annotating service account --- bin/sa-can-read-s3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index a6d001de..c6b02b66 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -75,7 +75,7 @@ create_role() { } annotate_serviceaccount() { - kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" + kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" --overwrite } checkit() { From df0ffc4d7505348ff784b68849287369fa613fab Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:15:24 +0100 Subject: [PATCH 09/73] Rename test pod --- bin/sa-can-read-s3.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index c6b02b66..d7dc9666 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -79,7 +79,8 @@ annotate_serviceaccount() { } checkit() { - kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true herro -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - + echo "Will try to read s3://"${BUCKET_NAME}"/top-sekret.txt" + kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true can-we-read-s3 -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - } update_kubeconfig() { From dff505ac765182a8b1910dcb4aed293835d465c6 Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sun, 28 Mar 2021 10:53:48 +0200 Subject: [PATCH 10/73] Fix incorrect bits in create_describe_cluster_policy --- bin/sa-can-read-s3.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index d7dc9666..3f5b981d 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -23,13 +23,13 @@ emit_describe_cluster_policy() { } create_describe_cluster_policy() { - local role_name="can-describe-cluster" aws iam create-policy \ - --policy-name can-describe-cluster \ + --policy-name ${DESCRIBE_CLUSTER_POLICY_NAME} \ --description "Policy allowing to describe ${CLUSTER_NAME}" \ --policy-document "$(emit_describe_cluster_policy)" - aws iam attach-user-policy --role-name "${role_name}" --policy-arn "${S3_POLICY_ARN}" + # to attach: + # aws iam attach-user-policy --user-name "${user_name}" --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" } emit_service_account_role_trust_policy() { @@ -91,6 +91,9 @@ teardown() { # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" aws iam delete-role "${ROLE_NAME}" + # for username in users; do ... + # aws iam detach-user-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" --user-name "${username}" + aws iam delete-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" } create_and_populate_bucket() { @@ -107,6 +110,7 @@ create_and_populate_bucket() { ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess CLUSTER_NAME=floral-mongoose-1616851817 +DESCRIBE_CLUSTER_POLICY_NAME=can-describe-cluster ROLE_NAME=service-account-role REGION=eu-north-1 BUCKET_NAME=wooga-booga-pants From 907adf8075aa1fce6b68f1693e560da09f87543f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 11:11:18 +0200 Subject: [PATCH 11/73] =?UTF-8?q?=F0=9F=9A=AB=20Remove=200.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/0.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 slides/0.yml diff --git a/slides/0.yml b/slides/0.yml deleted file mode 100644 index 36bd3a0e..00000000 --- a/slides/0.yml +++ /dev/null @@ -1,2 +0,0 @@ -content: -- k8s/netpol.md From c3d6e5e660a2725601f787f3048313fb7dd16e2c Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 11:12:50 +0200 Subject: [PATCH 12/73] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Add=20EKS=20prep=20s?= =?UTF-8?q?cripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-eks/10_create_cluster.sh | 10 +++++ prepare-eks/20_create_users.sh | 24 +++++++++++ prepare-eks/30_create_or_update_policy.sh | 22 ++++++++++ prepare-eks/40_attach_policy.sh | 8 ++++ prepare-eks/50_aws_auth.sh | 15 +++++++ prepare-eks/60_setup_rbac_and_ns.sh | 35 ++++++++++++++++ prepare-eks/99_cleanup_old_policy.sh | 7 ++++ prepare-eks/users.txt | 50 +++++++++++++++++++++++ 8 files changed, 171 insertions(+) create mode 100755 prepare-eks/10_create_cluster.sh create mode 100755 prepare-eks/20_create_users.sh create mode 100755 prepare-eks/30_create_or_update_policy.sh create mode 100755 prepare-eks/40_attach_policy.sh create mode 100755 prepare-eks/50_aws_auth.sh create mode 100755 prepare-eks/60_setup_rbac_and_ns.sh create mode 100755 prepare-eks/99_cleanup_old_policy.sh create mode 100644 prepare-eks/users.txt diff --git a/prepare-eks/10_create_cluster.sh b/prepare-eks/10_create_cluster.sh new file mode 100755 index 00000000..53cb81df --- /dev/null +++ b/prepare-eks/10_create_cluster.sh @@ -0,0 +1,10 @@ +#!/bin/sh +eksctl create cluster \ + --node-type=t3.large \ + --nodes-max=10 \ + --alb-ingress-access \ + --asg-access \ + --ssh-access \ + --with-oidc \ + # + diff --git a/prepare-eks/20_create_users.sh b/prepare-eks/20_create_users.sh new file mode 100755 index 00000000..5a2f5d80 --- /dev/null +++ b/prepare-eks/20_create_users.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +echo "Getting list of existing users ..." +aws iam list-users --output json | jq -r .Users[].UserName > users.tmp + +for U in $(cat users.txt); do + if ! grep -qw $U users.tmp; then + echo "Creating user $U..." + aws iam create-user --user-name=$U \ + --tags=Key=container.training,Value=1 + fi + if ! grep -qw $U users.keys; then + echo "Listing keys for user $U..." + KEYS=$(aws iam list-access-keys --user=$U | jq -r .AccessKeyMetadata[].AccessKeyId) + for KEY in $KEYS; do + echo "Deleting key $KEY for user $U..." + aws iam delete-access-key --user=$U --access-key-id=$KEY + done + echo "Creating access key for user $U..." + aws iam create-access-key --user=$U --output json \ + | jq -r '.AccessKey | [ .UserName, .AccessKeyId, .SecretAccessKey ] | @tsv' \ + >> users.keys + fi +done diff --git a/prepare-eks/30_create_or_update_policy.sh b/prepare-eks/30_create_or_update_policy.sh new file mode 100755 index 00000000..f5fc6ecd --- /dev/null +++ b/prepare-eks/30_create_or_update_policy.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +JSON='{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "eks:DescribeCluster" + ], + "Resource": "arn:aws:eks:*", + "Effect": "Allow" + } + ] +}' + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +#aws iam create-policy --policy-name user.container.training --policy-document "$JSON" +aws iam create-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --policy-document "$JSON" --set-as-default + +# Uncomment this to check which users have the policy +#aws iam list-entities-for-policy --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training diff --git a/prepare-eks/40_attach_policy.sh b/prepare-eks/40_attach_policy.sh new file mode 100755 index 00000000..569557b2 --- /dev/null +++ b/prepare-eks/40_attach_policy.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +for U in $(cat users.txt); do + echo "Attaching policy to user $U ..." + aws iam attach-user-policy --user-name $U --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training +done diff --git a/prepare-eks/50_aws_auth.sh b/prepare-eks/50_aws_auth.sh new file mode 100755 index 00000000..ba140a39 --- /dev/null +++ b/prepare-eks/50_aws_auth.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +rm -f users.map +for U in ada.lovelace also.lol; do +echo "\ +- userarn: arn:aws:iam::$ACCOUNT:user/$U + username: $U + groups: [ container.training ]\ +" >> users.map +done + +kubectl create --namespace=kube-system configmap aws-auth --dry-run=client --from-file=mapUsers=users.map -o yaml | kubectl apply -f- + diff --git a/prepare-eks/60_setup_rbac_and_ns.sh b/prepare-eks/60_setup_rbac_and_ns.sh new file mode 100755 index 00000000..9b87a169 --- /dev/null +++ b/prepare-eks/60_setup_rbac_and_ns.sh @@ -0,0 +1,35 @@ +#!/bin/sh +kubectl create rolebinding --namespace default container.training --group=container.training --clusterrole=view +kubectl create clusterrole view-nodes --verb=get,list,watch --resource=node +kubectl create clusterrolebinding view-nodes --group=container.training --clusterrole=view-nodes +kubectl create clusterrole view-namespaces --verb=get,list,watch --resource=namespace +kubectl create clusterrolebinding view-namespaces --group=container.training --clusterrole=view-namespaces + +kubectl create namespace container-training +kubectl create rolebinding --namespace container-training edit --group=container.training --clusterrole=edit + +for U in $(cat users.txt); do + NS=$(echo $U | tr . -) + cat < Date: Sun, 28 Mar 2021 12:59:54 +0200 Subject: [PATCH 13/73] =?UTF-8?q?=F0=9F=94=80=20Move=20@soulshake's=20scri?= =?UTF-8?q?pts=20and=20commands=20to=20prepare-eks=20directory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/sa-can-read-s3.sh | 133 ------------------------------------ prepare-eks/70_oidc.sh | 46 +++++++++++++ prepare-eks/80_s3_bucket.sh | 43 ++++++++++++ 3 files changed, 89 insertions(+), 133 deletions(-) delete mode 100755 bin/sa-can-read-s3.sh create mode 100755 prepare-eks/70_oidc.sh create mode 100755 prepare-eks/80_s3_bucket.sh diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh deleted file mode 100755 index 3f5b981d..00000000 --- a/bin/sa-can-read-s3.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env bash -# I would like to demonstrate access to AWS resource (e.g. S3 bucket) from a pod. Idea: -# create a bucket, put two objects in it (one public, one private), then … I suppose I -# need to create a role with access to the private object, associate the role to a service -# account in k8s, find an image with the aws CLI (or some s3 client) in it … ? - -set -euo pipefail - -emit_describe_cluster_policy() { - # Not used right now, but this permission is required in order to run `aws eks update-kubeconfig`: - echo '{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "eks:DescribeCluster" - ], - "Resource": "'"arn:aws:eks:${REGION}:${ACCOUNT_ID}:cluster/${CLUSTER_NAME}"'", - "Effect": "Allow" - } - ] -}' -} - -create_describe_cluster_policy() { - aws iam create-policy \ - --policy-name ${DESCRIBE_CLUSTER_POLICY_NAME} \ - --description "Policy allowing to describe ${CLUSTER_NAME}" \ - --policy-document "$(emit_describe_cluster_policy)" - - # to attach: - # aws iam attach-user-policy --user-name "${user_name}" --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" -} - -emit_service_account_role_trust_policy() { - local oidc_provider_arn key_prefix - oidc_provider_arn="$(aws iam list-open-id-connect-providers | jq -r '.OpenIDConnectProviderList[0].Arn')" - key_prefix="$(echo "${oidc_provider_arn}" | cut -f2- -d '/')" - - echo '{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "'"${oidc_provider_arn}"'" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "'"${key_prefix}:sub"'": "system:serviceaccount:default:default" - } - } - } - ] -}' -} - -associate_oidc_provider() { - local issuer_url - issuer_url="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.identity.oidc.issuer" --output text)" - if ! aws iam list-open-id-connect-providers | grep "${issuer_url}"; then - eksctl utils associate-iam-oidc-provider --cluster "${CLUSTER_NAME}" --approve - else - echo "OIDC provider already associated" - fi -} - -create_role() { - if ! _="$(aws iam get-role --role-name "${ROLE_NAME}")"; then - aws iam create-role --role-name "${ROLE_NAME}" --description "Role for service account" --assume-role-policy-document "$(emit_service_account_role_trust_policy)" - else - echo "Role ${ROLE_NAME} already exists" - fi -} - -annotate_serviceaccount() { - kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" --overwrite -} - -checkit() { - echo "Will try to read s3://"${BUCKET_NAME}"/top-sekret.txt" - kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true can-we-read-s3 -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - -} - -update_kubeconfig() { - aws eks update-kubeconfig --name "${CLUSTER_NAME}" -} - -teardown() { - # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy - aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" - aws iam delete-role "${ROLE_NAME}" - # for username in users; do ... - # aws iam detach-user-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" --user-name "${username}" - aws iam delete-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" -} - -create_and_populate_bucket() { - if ! _="$(aws s3api get-bucket-acl --bucket "${BUCKET_NAME}")"; then - aws s3api create-bucket --region "${REGION}" --bucket "${BUCKET_NAME}" --create-bucket-configuration "LocationConstraint=${REGION}" - else - echo "Bucket ${BUCKET_NAME} already exists." - fi - f="$(mktemp)" - echo "THE UNICORN IS IN THE GARDEN!!" >"${f}" - aws s3api put-object --bucket "${BUCKET_NAME}" --key top-sekret.txt --body "${f}" -} - -ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" -S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess -CLUSTER_NAME=floral-mongoose-1616851817 -DESCRIBE_CLUSTER_POLICY_NAME=can-describe-cluster -ROLE_NAME=service-account-role -REGION=eu-north-1 -BUCKET_NAME=wooga-booga-pants -export KUBECONFIG=myconfig - -main() { - if [ -n "${1:-}" ]; then - echo "An argument was provided, running that: $1" - "${1}" - else - echo "ACCOUNT_ID: $ACCOUNT_ID" - associate_oidc_provider - create_role - aws iam attach-role-policy --role-name "${ROLE_NAME}" --policy-arn "${S3_POLICY_ARN}" - annotate_serviceaccount - checkit - fi -} - -main "$@" diff --git a/prepare-eks/70_oidc.sh b/prepare-eks/70_oidc.sh new file mode 100755 index 00000000..f50eac5b --- /dev/null +++ b/prepare-eks/70_oidc.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# Note: if cluster was created without OIDC provider attached, +# you need to run the following command. It is idempotent. +#eksctl utils associate-iam-oidc-provider --cluster cluster-name-12341234 --approve + +if [ "$1" ]; then + CLUSTER="$1" +else + echo "Please indicate cluster to use. Available clusters:" + aws eks list-clusters --output table + exit 1 +fi + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) +OIDC=$(aws eks describe-cluster --name $CLUSTER --query cluster.identity.oidc.issuer --output text | cut -d/ -f3-) +ROLE_NAME=s3-reader-container-training +TRUST_POLICY=$(envsubst < Date: Sun, 28 Mar 2021 15:36:25 +0200 Subject: [PATCH 14/73] =?UTF-8?q?=F0=9F=93=83=20Document=20the=20EKS=20she?= =?UTF-8?q?ll=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-eks/10_create_cluster.sh | 3 ++ prepare-eks/20_create_users.sh | 8 +++++ prepare-eks/30_create_or_update_policy.sh | 39 ++++++++++++++++++--- prepare-eks/40_attach_policy.sh | 8 ++++- prepare-eks/50_aws_auth.sh | 15 ++++++-- prepare-eks/60_setup_rbac_and_ns.sh | 42 +++++++++++++++++++---- prepare-eks/70_oidc.sh | 38 +++++++++++++++++--- prepare-eks/80_s3_bucket.sh | 11 ++++++ prepare-eks/99_cleanup_old_policy.sh | 7 ---- 9 files changed, 145 insertions(+), 26 deletions(-) delete mode 100755 prepare-eks/99_cleanup_old_policy.sh diff --git a/prepare-eks/10_create_cluster.sh b/prepare-eks/10_create_cluster.sh index 53cb81df..e7dc26de 100755 --- a/prepare-eks/10_create_cluster.sh +++ b/prepare-eks/10_create_cluster.sh @@ -1,4 +1,7 @@ #!/bin/sh +# Create an EKS cluster. +# This is not idempotent (each time you run it, it creates a new cluster). + eksctl create cluster \ --node-type=t3.large \ --nodes-max=10 \ diff --git a/prepare-eks/20_create_users.sh b/prepare-eks/20_create_users.sh index 5a2f5d80..71a470bc 100755 --- a/prepare-eks/20_create_users.sh +++ b/prepare-eks/20_create_users.sh @@ -1,4 +1,12 @@ #!/bin/sh +# For each user listed in "users.txt", create an IAM user. +# Also create AWS API access keys, and store them in "users.keys". +# This is idempotent (you can run it multiple times, it will only +# create the missing users). However, it will not remove users. +# Note that you can remove users from "users.keys" (or even wipe +# that file out entirely) and then this script will delete their +# keys and generate new keys for them (and add the new keys to +# "users.keys".) echo "Getting list of existing users ..." aws iam list-users --output json | jq -r .Users[].UserName > users.tmp diff --git a/prepare-eks/30_create_or_update_policy.sh b/prepare-eks/30_create_or_update_policy.sh index f5fc6ecd..9c7d11bc 100755 --- a/prepare-eks/30_create_or_update_policy.sh +++ b/prepare-eks/30_create_or_update_policy.sh @@ -1,6 +1,16 @@ #!/bin/sh +# Create an IAM policy to authorize users to do "aws eks update-kubeconfig". +# This is idempotent, which allows to update the policy document below if +# you want the users to do other things as well. +# Note that each time you run this script, it will actually create a new +# version of the policy, set that version as the default version, and +# remove all non-default versions. (Because you can only have up to +# 5 versions of a given policy, so you need to clean them up.) +# After running that script, you will want to attach the policy to our +# users (check the other scripts in that directory). -JSON='{ +POLICY_NAME=user.container.training +POLICY_DOC='{ "Version": "2012-10-17", "Statement": [ { @@ -15,8 +25,27 @@ JSON='{ ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) -#aws iam create-policy --policy-name user.container.training --policy-document "$JSON" -aws iam create-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --policy-document "$JSON" --set-as-default +aws iam create-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --policy-document "$POLICY_DOC" \ + --set-as-default -# Uncomment this to check which users have the policy -#aws iam list-entities-for-policy --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training +# For reference, the command below creates a policy without versioning: +#aws iam create-policy \ +#--policy-name user.container.training \ +#--policy-document "$JSON" + +for VERSION in $( + aws iam list-policy-versions \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --query 'Versions[?!IsDefaultVersion].VersionId' \ + --output text) +do + aws iam delete-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --version-id "$VERSION" +done + +# For reference, the command below shows all users using the policy: +#aws iam list-entities-for-policy \ +#--policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME diff --git a/prepare-eks/40_attach_policy.sh b/prepare-eks/40_attach_policy.sh index 569557b2..42483ecd 100755 --- a/prepare-eks/40_attach_policy.sh +++ b/prepare-eks/40_attach_policy.sh @@ -1,8 +1,14 @@ #!/bin/sh +# Attach our user policy to all the users defined in "users.txt". +# This should be idempotent, because attaching the same policy +# to the same user multiple times doesn't do anything. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) +POLICY_NAME=user.container.training for U in $(cat users.txt); do echo "Attaching policy to user $U ..." - aws iam attach-user-policy --user-name $U --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training + aws iam attach-user-policy \ + --user-name $U \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME done diff --git a/prepare-eks/50_aws_auth.sh b/prepare-eks/50_aws_auth.sh index ba140a39..f586a30e 100755 --- a/prepare-eks/50_aws_auth.sh +++ b/prepare-eks/50_aws_auth.sh @@ -1,9 +1,17 @@ #!/bin/sh +# Update the aws-auth ConfigMap to map our IAM users to Kubernetes users. +# Each user defined in "users.txt" will be mapped to a Kubernetes user +# with the same name, and put in the "container.training" group, too. +# This is idempotent. +# WARNING: this will wipe out the mapUsers component of the aws-auth +# ConfigMap, removing all users that aren't in "users.txt". +# It won't touch mapRoles, so it shouldn't break the role mappings +# put in place by EKS. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) rm -f users.map -for U in ada.lovelace also.lol; do +for U in $(cat users.txt); do echo "\ - userarn: arn:aws:iam::$ACCOUNT:user/$U username: $U @@ -11,5 +19,6 @@ echo "\ " >> users.map done -kubectl create --namespace=kube-system configmap aws-auth --dry-run=client --from-file=mapUsers=users.map -o yaml | kubectl apply -f- - +kubectl create --namespace=kube-system configmap aws-auth \ + --dry-run=client --from-file=mapUsers=users.map -o yaml \ + | kubectl apply -f- diff --git a/prepare-eks/60_setup_rbac_and_ns.sh b/prepare-eks/60_setup_rbac_and_ns.sh index 9b87a169..3d52f2c9 100755 --- a/prepare-eks/60_setup_rbac_and_ns.sh +++ b/prepare-eks/60_setup_rbac_and_ns.sh @@ -1,13 +1,43 @@ #!/bin/sh -kubectl create rolebinding --namespace default container.training --group=container.training --clusterrole=view -kubectl create clusterrole view-nodes --verb=get,list,watch --resource=node -kubectl create clusterrolebinding view-nodes --group=container.training --clusterrole=view-nodes -kubectl create clusterrole view-namespaces --verb=get,list,watch --resource=namespace -kubectl create clusterrolebinding view-namespaces --group=container.training --clusterrole=view-namespaces +# Create a shared Kubernetes Namespace ("container-training") as well as +# individual namespaces for every user in "users.txt", and set up a bunch +# of permissions. +# Specifically: +# - each user gets "view" permissions in the "default" Namespace +# - each user gets "edit" permissions in the "container-training" Namespace +# - each user gets permissions to list Nodes and Namespaces +# - each user gets "admin" permissions in their personal Namespace +# Note that since Kubernetes Namespaces can't have dots in their names, +# if a user has dots, dots will be mapped to dashes. +# So user "ada.lovelace" will get namespace "ada-lovelace". +# This is kind of idempotent (but will raise a bunch of errors for objects +# that already exist). +# TODO: if this needs to evolve, replace all the "create" operations by +# "apply" operations. But this is good enough for now. + +kubectl create rolebinding --namespace default container.training \ + --group=container.training --clusterrole=view + +kubectl create clusterrole view-nodes \ + --verb=get,list,watch --resource=node +kubectl create clusterrolebinding view-nodes \ + --group=container.training --clusterrole=view-nodes + +kubectl create clusterrole view-namespaces \ + --verb=get,list,watch --resource=namespace +kubectl create clusterrolebinding view-namespaces \ + --group=container.training --clusterrole=view-namespaces kubectl create namespace container-training -kubectl create rolebinding --namespace container-training edit --group=container.training --clusterrole=edit +kubectl create rolebinding --namespace container-training edit \ + --group=container.training --clusterrole=edit +# Note: API calls to EKS tend to be fairly slow. To optimize things a bit, +# instead of running "kubectl" N times, we generate a bunch of YAML and +# apply it. It will still generate a lot of API calls but it's much faster +# than calling "kubectl" N times. It might be possible to make this even +# faster by generating a "kind: List" (I don't know if this would issue +# a single API calls or multiple ones; TBD!) for U in $(cat users.txt); do NS=$(echo $U | tr . -) cat < /tmp/policy.json +aws iam update-assume-role-policy \ + --role-name $ROLE_NAME \ + --policy-document file:///tmp/policy.json diff --git a/prepare-eks/80_s3_bucket.sh b/prepare-eks/80_s3_bucket.sh index a67c9de6..66539250 100755 --- a/prepare-eks/80_s3_bucket.sh +++ b/prepare-eks/80_s3_bucket.sh @@ -1,4 +1,15 @@ #!/bin/sh +# Create an S3 bucket with two objects in it: +# - public.txt (world-readable) +# - private.txt (private) +# Also create an IAM policy granting read-only access to the bucket +# (and therefore, to the private object). +# Finally, attach the policy to an IAM role (for instance, the role +# created by another script in this directory). +# This isn't idempotent, but it can be made idempotent by replacing the +# "aws iam create-policy" call with "aws iam create-policy-version" and +# a bit of extra elbow grease. (See other scripts in this directory for +# an example). ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) BUCKET=container.training diff --git a/prepare-eks/99_cleanup_old_policy.sh b/prepare-eks/99_cleanup_old_policy.sh deleted file mode 100755 index cb562f65..00000000 --- a/prepare-eks/99_cleanup_old_policy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) - -for VERSION in $(aws iam list-policy-versions --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training | jq -r '.Versions[].VersionId'); do - aws iam delete-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --version-id "$VERSION" -done From cc5da860b96c2f8ca9650d5b10b8e734e273fd7f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 18:28:38 +0200 Subject: [PATCH 15/73] =?UTF-8?q?=F0=9F=92=AC=20Add=20Slack=20chat=20room?= =?UTF-8?q?=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/intro-fullday.yml | 1 + slides/intro-selfpaced.yml | 1 + slides/intro-twodays.yml | 1 + slides/kadm-fullday.yml | 1 + slides/kadm-twodays.yml | 1 + slides/kube-adv.yml | 1 + slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + slides/shared/chat-room-slack.md | 12 ++++++++++++ slides/swarm-fullday.yml | 1 + slides/swarm-halfday.yml | 1 + slides/swarm-selfpaced.yml | 1 + 14 files changed, 25 insertions(+) create mode 100644 slides/shared/chat-room-slack.md diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index c70eb8d4..0473f9d0 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 96c2b837..021f9c4d 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/intro-twodays.yml b/slides/intro-twodays.yml index e0a15e33..2f0dd6e6 100644 --- a/slides/intro-twodays.yml +++ b/slides/intro-twodays.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kadm-fullday.yml b/slides/kadm-fullday.yml index 4fb62c4a..e40d6d27 100644 --- a/slides/kadm-fullday.yml +++ b/slides/kadm-fullday.yml @@ -22,6 +22,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kadm-twodays.yml b/slides/kadm-twodays.yml index 679ca54f..dea0f87d 100644 --- a/slides/kadm-twodays.yml +++ b/slides/kadm-twodays.yml @@ -22,6 +22,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-adv.yml b/slides/kube-adv.yml index 87cbf454..297c7cf0 100644 --- a/slides/kube-adv.yml +++ b/slides/kube-adv.yml @@ -20,6 +20,7 @@ content: - k8s/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index a3c9ad5d..0f2877ae 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 42da801c..2caae94c 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -23,6 +23,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index b615c6e5..542998ef 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 3b1f27b9..37e622f1 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/shared/chat-room-slack.md b/slides/shared/chat-room-slack.md new file mode 100644 index 00000000..14a7e5b3 --- /dev/null +++ b/slides/shared/chat-room-slack.md @@ -0,0 +1,12 @@ +## Chat room + +- A Slack room has been set up for the duration of the training + +- We'll use it to ask questions, get help, share feedback ... + + (let's keep an eye on it during the training!) + +- Reminder, the room is @@CHAT@@ + +- Say hi in the chat room! + diff --git a/slides/swarm-fullday.yml b/slides/swarm-fullday.yml index 76800ca6..ec3af144 100644 --- a/slides/swarm-fullday.yml +++ b/slides/swarm-fullday.yml @@ -25,6 +25,7 @@ content: - swarm/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/swarm-halfday.yml b/slides/swarm-halfday.yml index a1c1a086..b4cd036b 100644 --- a/slides/swarm-halfday.yml +++ b/slides/swarm-halfday.yml @@ -25,6 +25,7 @@ content: - swarm/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/swarm-selfpaced.yml b/slides/swarm-selfpaced.yml index 43ad7b44..1fbe718d 100644 --- a/slides/swarm-selfpaced.yml +++ b/slides/swarm-selfpaced.yml @@ -20,6 +20,7 @@ content: - swarm/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md From 5380b2d52ab80871556f8ecaaaaeecc9e0db27ef Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 20:08:58 +0200 Subject: [PATCH 16/73] =?UTF-8?q?=F0=9F=9A=AA=20Instructions=20to=20access?= =?UTF-8?q?=20EKS=20cluster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/access-eks-cluster.md | 104 +++++++++++++++++++++++++++++++ slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 5 files changed, 108 insertions(+) create mode 100644 slides/k8s/access-eks-cluster.md diff --git a/slides/k8s/access-eks-cluster.md b/slides/k8s/access-eks-cluster.md new file mode 100644 index 00000000..6bd0d845 --- /dev/null +++ b/slides/k8s/access-eks-cluster.md @@ -0,0 +1,104 @@ +## Accessing our EKS cluster + +- We also have a shared EKS cluster + +- With individual IAM users + +- Let's connect to this cluster! + +--- + +## What we need + +- `kubectl` (obviously!) + +- `aws` CLI (recent-ish version) + + (or `aws` CLI + `aws-iam-authenticator` plugin) + +- AWS API access key and secret access key + +- AWS region + +- EKS cluster name + +--- + +## Setting up AWS credentials + +- There are many ways to do this + +- We're going to use environment variables + +- You're welcome to use whatever you like (e.g. AWS profiles) + +.exercise[ + +- Set the AWS region, API access key, and secret key: + ```bash + export AWS_DEFAULT_REGION=`us-east-2` + export AWS_ACCESS_KEY_ID=`AKI...` + export AWS_SECRET_ACCESS_KEY=`xyz123...` + ``` + +- Check that the AWS API recognizes us: + ```bash + aws sts get-caller-identity + ``` + +] + +--- + +## Updating our kubeconfig file + +- Now we can use the AWS CLI to: + + - obtain the Kubernetes API address + + - register it in our kubeconfig file + +.exercise[ + +- Update our kubeconfig file: + ```bash + aws eks update-kubeconfig --name `fancy-clustername-1234` + ``` + +- Run some harmless command: + ```bash + kubectl version + ``` + +] + +--- + +## Our resources + +- We have the following permissions: + + - `view` in the `default` namespace + + - `edit` in the `container-training` namespace + + - `admin` in our personal namespace + +- Our personal namespace is our IAM user name + + (but with dots replaced with dashes) + +- For instance, user `ada.lovelace` has namespace `ada-lovelace` + +--- + +## Deploying things + +- Let's deploy DockerCoins in our personal namespace! + +- Expose the Web UI with a `LoadBalancer` service + +??? + +:EN:- Working with an EKS cluster +:FR:- Travailler avec un cluster EKS diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 0f2877ae..c43169f4 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -68,6 +68,7 @@ content: #- k8s/authoring-yaml.md #- k8s/exercise-yaml.md #- k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md #- k8s/accessinternal.md #- k8s/kubectlproxy.md - k8s/rollout.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 2caae94c..3bd0e430 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -56,6 +56,7 @@ content: - k8s/buildshiprun-dockerhub.md - k8s/ourapponkube.md #- k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md #- k8s/accessinternal.md #- k8s/kubectlproxy.md - - k8s/dashboard.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 542998ef..aa533663 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -76,6 +76,7 @@ content: - - k8s/namespaces.md - k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md - k8s/accessinternal.md - k8s/kubectlproxy.md - diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 37e622f1..bb4272f9 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -69,6 +69,7 @@ content: #- k8s/exercise-yaml.md - - k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md - k8s/accessinternal.md #- k8s/kubectlproxy.md - k8s/rollout.md From fcfcb127b41a170e649c8b1926ae5a3ac4e8feac Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 30 Mar 2021 18:09:24 +0200 Subject: [PATCH 17/73] =?UTF-8?q?=F0=9F=93=83=20Update=20section=20summari?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/containers/Compose_For_Dev_Stacks.md | 2 +- slides/containers/Container_Network_Model.md | 12 ++++++++++++ slides/containers/Container_Networking_Basics.md | 9 ++------- slides/containers/Copying_Files_During_Build.md | 2 +- slides/containers/Dockerfile_Tips.md | 9 ++++++++- slides/containers/Network_Drivers.md | 9 +++++++++ 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/slides/containers/Compose_For_Dev_Stacks.md b/slides/containers/Compose_For_Dev_Stacks.md index 4f660881..d978c6f5 100644 --- a/slides/containers/Compose_For_Dev_Stacks.md +++ b/slides/containers/Compose_For_Dev_Stacks.md @@ -329,4 +329,4 @@ This is ideal to debug regressions, do side-by-side comparisons, etc. :EN:- Connecting services together with a *Compose file* :FR:- Utiliser Compose pour décrire son environnement -:FR:- Écrire un *Compose file* pour connecter les services entre eux \ No newline at end of file +:FR:- Écrire un *Compose file* pour connecter les services entre eux diff --git a/slides/containers/Container_Network_Model.md b/slides/containers/Container_Network_Model.md index e39349b8..bbe4c45a 100644 --- a/slides/containers/Container_Network_Model.md +++ b/slides/containers/Container_Network_Model.md @@ -742,3 +742,15 @@ class: extra-details * This may be used to access an internal package repository. (But try to use a multi-stage build instead, if possible!) + +??? + +:EN:Container networking essentials +:EN:- The Container Network Model +:EN:- Container isolation +:EN:- Service discovery + +:FR:Mettre ses conteneurs en réseau +:FR:- Le "Container Network Model" +:FR:- Isolation des conteneurs +:FR:- *Service discovery* diff --git a/slides/containers/Container_Networking_Basics.md b/slides/containers/Container_Networking_Basics.md index 4ce0fc49..23dc8eb9 100644 --- a/slides/containers/Container_Networking_Basics.md +++ b/slides/containers/Container_Networking_Basics.md @@ -229,10 +229,5 @@ containers together without exposing their ports. ??? -:EN:Connecting containers -:EN:- Container networking basics -:EN:- Exposing a container - -:FR:Connecter les conteneurs -:FR:- Description du modèle réseau des conteneurs -:FR:- Exposer un conteneur +:EN:- Exposing single containers +:FR:- Exposer un conteneur isolé diff --git a/slides/containers/Copying_Files_During_Build.md b/slides/containers/Copying_Files_During_Build.md index 2d58d287..a57386df 100644 --- a/slides/containers/Copying_Files_During_Build.md +++ b/slides/containers/Copying_Files_During_Build.md @@ -101,5 +101,5 @@ Success! ??? -:EN:- The build cache +:EN:- Leveraging the build cache for faster builds :FR:- Tirer parti du cache afin d'optimiser la vitesse de *build* diff --git a/slides/containers/Dockerfile_Tips.md b/slides/containers/Dockerfile_Tips.md index 485df970..6b402906 100644 --- a/slides/containers/Dockerfile_Tips.md +++ b/slides/containers/Dockerfile_Tips.md @@ -434,5 +434,12 @@ services: ??? +:EN:Optimizing images :EN:- Dockerfile tips, tricks, and best practices -:FR:- Bonnes pratiques pour la construction des images +:EN:- Reducing build time +:EN:- Reducing image size + +:FR:Optimiser ses images +:FR:- Bonnes pratiques, trucs et astuces +:FR:- Réduire le temps de build +:FR:- Réduire la taille des images diff --git a/slides/containers/Network_Drivers.md b/slides/containers/Network_Drivers.md index 3d487370..216579b5 100644 --- a/slides/containers/Network_Drivers.md +++ b/slides/containers/Network_Drivers.md @@ -82,3 +82,12 @@ Use cases: * Those containers can communicate over their `lo` interface.
(i.e. one can bind to 127.0.0.1 and the others can connect to it.) +??? + +:EN:Advanced container networking +:EN:- Transparent network access with the "host" driver +:EN:- Sharing is caring with the "container" driver + +:FR:Paramétrage réseau avancé +:FR:- Accès transparent au réseau avec le mode "host" +:FR:- Partage de la pile réseau avece le mode "container" From f03aedd02467f2d54bcffedb5d2226500bcaa6eb Mon Sep 17 00:00:00 2001 From: Julien Girardin Date: Thu, 18 Mar 2021 14:48:40 +0100 Subject: [PATCH 18/73] =?UTF-8?q?=F0=9F=8F=A0Helm=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/helm.yml | 40 ++++++++ slides/k8s/helm-dependencies.md | 164 ++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 slides/helm.yml create mode 100644 slides/k8s/helm-dependencies.md diff --git a/slides/helm.yml b/slides/helm.yml new file mode 100644 index 00000000..617b7e20 --- /dev/null +++ b/slides/helm.yml @@ -0,0 +1,40 @@ +title: | + Packaging d'applications + et CI/CD pour Kubernetes + +chat: "[Gitter](https://gitter.im/jpetazzo/training-202102-online)" + +gitrepo: github.com/jpetazzo/container.training + +slides: https://2021-02-enix.container.training/ + +#slidenumberprefix: "#SomeHashTag — " + +exclude: +- self-paced + +content: +- shared/title.md +#- logistics.md +- k8s/intro.md +- shared/about-slides.md +- shared/prereqs.md +- shared/webssh.md +- shared/connecting.md +#- shared/chat-room-im.md +#- shared/chat-room-zoom.md +- shared/toc.md +- + - k8s/helm-intro.md + - k8s/helm-chart-format.md + - k8s/helm-create-basic-chart.md + - k8s/helm-create-better-chart.md + - k8s/helm-dependencies.md + - k8s/helm-advanced.md + - k8s/helm-secrets.md + - k8s/cert-manager.md + - k8s/gitlab.md + - | + # (Extra content) + - k8s/prometheus.md + diff --git a/slides/k8s/helm-dependencies.md b/slides/k8s/helm-dependencies.md new file mode 100644 index 00000000..e5a64d75 --- /dev/null +++ b/slides/k8s/helm-dependencies.md @@ -0,0 +1,164 @@ +# Creating even better charts + +- We will going to use dependencies to write less code + +- reuse a community chart for redis + +- We will see how it improve the developer life as well as the one of the user + +- We will see that sometimes dependencies could also remove a lot of code + +--- + +## Assertions about dockercoins + +- Every image is custom except the one of redis + +- It's is quite logic that deployment are custom except of the one of redis + +- So if redis is not custom can we use templates of someone else ? + +- Yes, through dependencies + +--- + +## Add redis dependency + +- In `Chart.yaml` fill the `dependencies` section: + ```yaml + dependencies: + - name: redis + version: 11.0.5 + repository: https://charts.bitnami.com/bitnami + condition: redis.enabled + ``` +- `condition` will trigger at install or upgrade to template or not the dependency + +- Now, we can tell helm to fetch the dependency by running `helm dependency update` + + (abbv `helm dep up`) + +- Another file has been created: `Chart.lock`. What should we do with this file ? + + - Short answer: add it to the source tree. +--- + +## Chart.lock + +- The real command to fetch dependency is `helm depedency build` + + (abbv `helm dep build`) + +- It looks into the `Chart.lock` to fetch the exact version + +- So what's the matter with the version in `Chart.yaml`. + + - This is indicative version for the `helm dep update` *dependency resolution* process + - You can specify loose version requirements + ```yaml + dependencies: + - name: redis + version: >=11 <12 + repository: https://charts.bitnami.com/bitnami + ``` +- We don't need to `helm dep build` after `helm dep up` : it's included + +--- + +## Dependency live matters ? + +- Every dependency lives in the `charts/` dependency + +- Downloaded dependencies will stay in compress binary format (`.tgz`) + +- Should we commit dependency ? + +- Pro: + + - more resilient to internet/mirror failures/decomissioning + +- Cons: + + - Tracking binary files in source tree may require additionnal tools (like git-lfs) to be done correctly + +--- + +## Dependency tuning + +- If we install our chart it will not work as the name of the redis service is not `redis` + +- Debug: + + - Service name is `{{ template "redis.fullname" . }}-master` + + - `redis.fullname` looks like + ``` + {{- define "redis.fullname" -}} + {{- if .Values.fullnameOverride -}} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} + {{- else -}} + [...] + {{- end }} + {{- end }} + ``` +--- +## Passing variable + +- If we pass `fullnameOverride=redis`, we will make the template part to output `redis` + +- We can pass variables from top-level to children, editing parent's `values.yaml`: + ```yaml + redis: # Name of the dependency + fullnameOverride: redis # Value passed to redis + cluster: # Other values passed to redis + enabled: false + ``` +- We can even pass template `{{ include "template.name" }}`, but warning: + + - Need to be evaluated with the `tpl` function, on the child side + - Evaluated in the context of the child, with no access to parent variables + +- User can also set variables with `--set=` or with `--values=` + +--- +## Embedding dependency + +- To remove `-master`, we will need a bit more the values to pass. + +- We need to edit the chart templates. + +- First of all we need to decompress the chart + +- Adjust `Chart.yaml` and reference to it + ```yaml + dependencies: + - name: redis + version: '*' # No need to constraint version, from local files + ``` + +- Run `helm dep update` + +- We can edit the template and test installation ! +--- + +class: extra-details + +## Chart v1 + +- Chart `apiVersion: v1` is the only version supported by helm v2 + +- Chart v1 is also supported by helm v3 + +- To be used, if we're looking for compatibility + +- Instead of `Chart.yaml` it use a separated file `requirements.yaml` + +- We should commit the created `requirements.lock` instead of `Charts.lock` + +--- + +??? + +:EN: - helm dependencies +:FR: - dépendences helm + From 45213a8f2e3314c37ee560ee77784ebcbaab6175 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 24 Mar 2021 20:33:16 +0100 Subject: [PATCH 19/73] =?UTF-8?q?=F0=9F=91=80=20Review=20dependency=20chap?= =?UTF-8?q?ter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/helm-dependencies.md | 321 +++++++++++++++++++++++--------- 1 file changed, 237 insertions(+), 84 deletions(-) diff --git a/slides/k8s/helm-dependencies.md b/slides/k8s/helm-dependencies.md index e5a64d75..74cf0be8 100644 --- a/slides/k8s/helm-dependencies.md +++ b/slides/k8s/helm-dependencies.md @@ -1,30 +1,59 @@ -# Creating even better charts +# Charts using other charts -- We will going to use dependencies to write less code +- Helm charts can have *dependencies* on other charts -- reuse a community chart for redis +- These dependencies will help us to share or reuse* components -- We will see how it improve the developer life as well as the one of the user + (so that we write and maintain less manifests, less templates, less code!) -- We will see that sometimes dependencies could also remove a lot of code +- As an example, we will use a community chart for Redis + +- This will help people who write charts, and people who use them + +- ... And potentially remove a lot of code! ✌️ --- -## Assertions about dockercoins +## Redis in DockerCoins -- Every image is custom except the one of redis +- In the DockerCoins demo app, we have 5 components: -- It's is quite logic that deployment are custom except of the one of redis + - 2 internal webservices + - 1 worker + - 1 public web UI + - 1 Redis data store -- So if redis is not custom can we use templates of someone else ? +- Every component is running some custom code, except Redis -- Yes, through dependencies +- Every component is using a custom image, except Redis + + (which is using the official `redis` image) + +- Could we use a standard chart for Redis? + +- Yes! Dependencies to the rescue! --- -## Add redis dependency +## Adding our dependency -- In `Chart.yaml` fill the `dependencies` section: +- First, we will add the dependency to the `Chart.yaml` file + +- Then, we will ask Helm to download that dependency + +- We will also *lock* the dependency + + (lock it to a specific version, to ensure reproducibility) + +--- + +## Declaring the dependency + +- First, let's edit `Chart.yaml` + +.exercise[ + +- In `Chart.yaml`, fill the `dependencies` section: ```yaml dependencies: - name: redis @@ -32,104 +61,215 @@ repository: https://charts.bitnami.com/bitnami condition: redis.enabled ``` -- `condition` will trigger at install or upgrade to template or not the dependency -- Now, we can tell helm to fetch the dependency by running `helm dependency update` +] - (abbv `helm dep up`) +Where do that `repository` and `version` come from? -- Another file has been created: `Chart.lock`. What should we do with this file ? - - - Short answer: add it to the source tree. ---- - -## Chart.lock - -- The real command to fetch dependency is `helm depedency build` - - (abbv `helm dep build`) - -- It looks into the `Chart.lock` to fetch the exact version - -- So what's the matter with the version in `Chart.yaml`. - - - This is indicative version for the `helm dep update` *dependency resolution* process - - You can specify loose version requirements - ```yaml - dependencies: - - name: redis - version: >=11 <12 - repository: https://charts.bitnami.com/bitnami - ``` -- We don't need to `helm dep build` after `helm dep up` : it's included +We're assuming here that we did our reserach, +or that our resident Helm expert advised us to +use Bitnami's Redis chart. --- -## Dependency live matters ? +## Conditions -- Every dependency lives in the `charts/` dependency +- The `condition` field gives us a way to enable/disable the dependency: + ```yaml + conditions: redis.enabled + ``` -- Downloaded dependencies will stay in compress binary format (`.tgz`) +- Here, we can disable Redis with the Helm flag `--set redis.enabled=false` -- Should we commit dependency ? + (or set that value in a `values.yaml` file) -- Pro: +- Of course, this is mostly useful for *optional* dependencies + + (otherwise, the app ends up being broken since it'll miss a component) + +--- + +## Lock & Load! + +- After adding the dependency, we ask Helm to pin an download it + +.exercise[ + +- Ask Helm: + ```bash + helm dependency update + ``` + + (Or `helm dep up`) + +] + +- This wil create `Chart.lock` and fetch the dependency + +--- + +## What's `Chart.lock`? + +- This is a common pattern with dependencies + + (see also: `Gemfile.lock`, `package.json.lock`, and many others) + +- This lets us define loose dependencies in `Chart.yaml` + + (e.g. "version 11.whatever, but below 12") + +- But have the exact version used in `Chart.lock` + +- This ensures reproducible deployments + +- `Chart.lock` can (should!) be added to our source tree + +- `Chart.lock` can (should!) regularly be updated + +--- + +## Loose dependencies + +- Here is an example of loose version requirement: + ```yaml + dependencies: + - name: redis + version: ">=11 <12" + repository: https://charts.bitnami.com/bitnami + ``` + +- This makes sure that we have the most recent version in the 11.x train + +- ... But without upgrading to version 12.x + + (because it might be incompatible) + +--- + +## `build` vs `update` + +- Helm actually offers two commands to manage dependencies: + + `helm dependency build` = fetch dependencies listed in `Chart.lock` + + `helm dependency update` = update `Chart.lock` (and run `build`) + +- When the dependency gets updated, we can/should: + + - `helm dep up` (update `Chart.lock` and fetch new chart) + + - test! + + - if everything is fine, `git add Chart.lock` and commit + +--- + +## Where are my dependencies? + +- Dependencies are downloaded to the `charts/` subdirectory + +- When they're downloaded, they stay in compressed format (`.tgz`) + +- Should we commit them to our code repository? + +- Pros: - more resilient to internet/mirror failures/decomissioning - Cons: - - Tracking binary files in source tree may require additionnal tools (like git-lfs) to be done correctly + - can add a lot of weight to the repo if charts are big or change often + + - this can be solved by extra tools like git-lfs --- ## Dependency tuning -- If we install our chart it will not work as the name of the redis service is not `redis` +- DockerCoins expects the `redis` Service to be named `redis` -- Debug: +- Our Redis chart uses a different Service name by default - - Service name is `{{ template "redis.fullname" . }}-master` +- Service name is `{{ template "redis.fullname" . }}-master` + +- `redis.fullname` looks like this: + ``` + {{- define "redis.fullname" -}} + {{- if .Values.fullnameOverride -}} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} + {{- else -}} + [...] + {{- end }} + {{- end }} + ``` + +- How do we fix this? - - `redis.fullname` looks like - ``` - {{- define "redis.fullname" -}} - {{- if .Values.fullnameOverride -}} - {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} - {{- else -}} - [...] - {{- end }} - {{- end }} - ``` --- -## Passing variable -- If we pass `fullnameOverride=redis`, we will make the template part to output `redis` +## Setting dependency variables -- We can pass variables from top-level to children, editing parent's `values.yaml`: - ```yaml - redis: # Name of the dependency - fullnameOverride: redis # Value passed to redis - cluster: # Other values passed to redis - enabled: false - ``` -- We can even pass template `{{ include "template.name" }}`, but warning: +- If we set `fullnameOverride` to `redis`: - - Need to be evaluated with the `tpl` function, on the child side - - Evaluated in the context of the child, with no access to parent variables + - the `{{ template ... }}` block will output `redis` + + - the Service name will be `redis-master` + +- A parent chart can set values for its dependencies + +- For example, in the parent's `values.yaml`: + + ```yaml + redis: # Name of the dependency + fullnameOverride: redis # Value passed to redis + cluster: # Other values passed to redis + enabled: false + ``` - User can also set variables with `--set=` or with `--values=` --- -## Embedding dependency -- To remove `-master`, we will need a bit more the values to pass. +## Passing templates -- We need to edit the chart templates. +- We can even pass template `{{ include "template.name" }}`, but warning: -- First of all we need to decompress the chart + - need to be evaluated with the `tpl` function, on the child side -- Adjust `Chart.yaml` and reference to it + - evaluated in the context of the child, with no access to parent variables + + + +--- + +## Getting rid of the `-master` + +- Even if we set that `fullnameOverride`, the Service name will be `redis-master` + +- To remove the `-master` suffix, we need to edit the chart itself + +- To edit the Redis chart, we need to *embed* it in our own chart + +- We need to: + + - decompress the chart + + - adjust `Chart.yaml` accordingly + +--- + +## Embedding a dependency + +.exercise[ + +- Decompress the chart: + ```yaml + cd charts + tar zxf redis-*.tgz + cd .. + ``` + +- Edit `Chart.yaml` and update the `dependencies` section: ```yaml dependencies: - name: redis @@ -138,27 +278,40 @@ - Run `helm dep update` -- We can edit the template and test installation ! +] + +--- + +## Updating the dependency + +- Now we can edit the Service name + + (it should be in `charts/redis/templates/redis-master-svc.yaml`) + +- Then try to deploy the whole chart! + --- class: extra-details -## Chart v1 +## Compatibility with Helm 2 -- Chart `apiVersion: v1` is the only version supported by helm v2 +- Chart `apiVersion: v1` is the only version supported by Helm 2 -- Chart v1 is also supported by helm v3 +- Chart v1 is also supported by Helm 3 -- To be used, if we're looking for compatibility +- Use v1 if you want to be compatible with Helm 2 -- Instead of `Chart.yaml` it use a separated file `requirements.yaml` +- Instead of `Chart.yaml`, dependencies are defined in `requirements.yaml` -- We should commit the created `requirements.lock` instead of `Charts.lock` + (and we should commit `requirements.lock` instead of `Chart.lock`) --- ??? -:EN: - helm dependencies -:FR: - dépendences helm +:EN:- Depending on other charts +:EN:- Charts within charts +:FR:- Dépendances entre charts +:FR:- Un chart peut en cacher un autre From 8f75a4cd7f613ba549570822f1e5ff93781fd398 Mon Sep 17 00:00:00 2001 From: Julien Girardin Date: Fri, 26 Mar 2021 16:40:23 +0100 Subject: [PATCH 20/73] =?UTF-8?q?=F0=9F=91=AE=20Add=20values=20schema=20va?= =?UTF-8?q?lidation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/helm.yml | 2 +- slides/k8s/helm-values-schema-validation.md | 108 ++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 slides/k8s/helm-values-schema-validation.md diff --git a/slides/helm.yml b/slides/helm.yml index 617b7e20..e0265735 100644 --- a/slides/helm.yml +++ b/slides/helm.yml @@ -30,7 +30,7 @@ content: - k8s/helm-create-basic-chart.md - k8s/helm-create-better-chart.md - k8s/helm-dependencies.md - - k8s/helm-advanced.md + - k8s/helm-values-schema-validation.md - k8s/helm-secrets.md - k8s/cert-manager.md - k8s/gitlab.md diff --git a/slides/k8s/helm-values-schema-validation.md b/slides/k8s/helm-values-schema-validation.md new file mode 100644 index 00000000..f29beb41 --- /dev/null +++ b/slides/k8s/helm-values-schema-validation.md @@ -0,0 +1,108 @@ +--- +## Helm mistake + +- What if I put wrong values and deploy the helm chart ? + +.exercice[ + + - Install a helm release with wrong values: + + `helm install broken-release --set=foo=bar --set=ImAgeTAg=toto` +] + +- What happened ? + + - The `broken-release` is installed 😨 + +- Is-it really broken ? + + - Not really: helm just ignored the values it doesn't know about 😓 + +- Is there something we can do to avoid mistakes ? + + +--- + +## Helm values schema validation + +- With *values schema validation*, we can write a spec representing the possible + values accepted by the chart. + +- Moving away from the spec make helm bailing out the installation/upgrade + +- It uses [jsonschema](https://json-schema.org/) language which is a + + `"a vocabulary that allows you to annotate and validate JSON documents."` + +- But can be adapted like here to validate any markup language that consist in + `map|dict|associativearray` and `list|array|sequence|tuple` like *yaml* + +--- +.exercise[ + +- Let's create a `values.schema.json`: + ```json + { + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "image": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "pattern": "^[a-z0-9-_]+$" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } } } } } + ``` +] + +--- +## Testing Helm validation + +- Now we can test to install Wrong release: + +.exercise[ + +- Run: + + `helm install broken --set image=image.pullPolicy=ShallNotPass` + +- Run: + + `helm install should-break --set=foo=bar --set=ImAgeTAg=toto` +] + +- First fails, but second one still pass even if we put a `values.schema.json` ! + +- Why ? + +--- +## Bailing out on unkown properties + +- We told helm more about valid properties but not what to do with non-existing ones + +- We can fix that with `"additionalProperties": false`: + +.exercise[ + +- Edit `values.schema.json` to add `"additionalProperties": false` + ```json + { + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "image": { [...] } + }, + "additionalProperties": false + } + ``` + +- And test again: `helm install should-break --set=foo=bar --set=ImAgeTAg=toto` +] +--- + + From 2160aa7f4063097e9253f54cb6af709fdde4f47c Mon Sep 17 00:00:00 2001 From: Julien Girardin Date: Fri, 2 Apr 2021 15:47:20 +0200 Subject: [PATCH 21/73] Split chapter for better toc --- slides/helm.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/slides/helm.yml b/slides/helm.yml index e0265735..0ac4bc7e 100644 --- a/slides/helm.yml +++ b/slides/helm.yml @@ -2,11 +2,11 @@ title: | Packaging d'applications et CI/CD pour Kubernetes -chat: "[Gitter](https://gitter.im/jpetazzo/training-202102-online)" +#chat: "[Gitter](https://gitter.im/jpetazzo/training-202102-online)" gitrepo: github.com/jpetazzo/container.training -slides: https://2021-02-enix.container.training/ +slides: https://2021-04-dijon.container.training/ #slidenumberprefix: "#SomeHashTag — " @@ -25,13 +25,16 @@ content: #- shared/chat-room-zoom.md - shared/toc.md - + - shared/sampleapp.md - k8s/helm-intro.md - k8s/helm-chart-format.md - k8s/helm-create-basic-chart.md +- - k8s/helm-create-better-chart.md - k8s/helm-dependencies.md - k8s/helm-values-schema-validation.md - k8s/helm-secrets.md +- - k8s/cert-manager.md - k8s/gitlab.md - | From bb1b225026bdf680410dfb0e65fb34ab0d583d98 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 5 Apr 2021 19:02:30 +0200 Subject: [PATCH 22/73] =?UTF-8?q?=F0=9F=91=80=20Review=20and=20suggestions?= =?UTF-8?q?=20for=20new=20Helm=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/helm-dependencies.md | 27 ++- slides/k8s/helm-values-schema-validation.md | 187 ++++++++++++++------ 2 files changed, 160 insertions(+), 54 deletions(-) diff --git a/slides/k8s/helm-dependencies.md b/slides/k8s/helm-dependencies.md index 74cf0be8..20b1b9bd 100644 --- a/slides/k8s/helm-dependencies.md +++ b/slides/k8s/helm-dependencies.md @@ -2,7 +2,7 @@ - Helm charts can have *dependencies* on other charts -- These dependencies will help us to share or reuse* components +- These dependencies will help us to share or reuse components (so that we write and maintain less manifests, less templates, less code!) @@ -230,6 +230,8 @@ use Bitnami's Redis chart. --- +class: extra-details + ## Passing templates - We can even pass template `{{ include "template.name" }}`, but warning: @@ -238,7 +240,7 @@ use Bitnami's Redis chart. - evaluated in the context of the child, with no access to parent variables - + --- @@ -292,6 +294,27 @@ use Bitnami's Redis chart. --- +## Embedding a dependency multiple times + +- What if we need multiple copies of the same subchart? + + (for instance, if we need two completely different Redis servers) + +- We can declare a dependency multiple times, and specify an `alias`: + ```yaml + dependencies: + - name: redis + version: '*' + alias: querycache + - name: redis + version: '*' + alias: celeryqueue + ``` + +- `.Chart.Name` will be set to the `alias` + +--- + class: extra-details ## Compatibility with Helm 2 diff --git a/slides/k8s/helm-values-schema-validation.md b/slides/k8s/helm-values-schema-validation.md index f29beb41..7c987377 100644 --- a/slides/k8s/helm-values-schema-validation.md +++ b/slides/k8s/helm-values-schema-validation.md @@ -1,91 +1,151 @@ +## Helm and invalid values + +- A lot of Helm charts let us specify an image tag like this: + ```bash + helm install ... --set image.tag=v1.0 + ``` + +- What happens if we make a small mistake, like this: + ```bash + helm install ... --set imagetag=v1.0 + ``` + +- Or even, like this: + ```bash + helm install ... --set image=v1.0 + ``` + +🤔 + --- -## Helm mistake -- What if I put wrong values and deploy the helm chart ? +## Making mistakes -.exercice[ +- In the first case: - - Install a helm release with wrong values: + - we set `imagetag=v1.0` instead of `image.tag=v1.0` - `helm install broken-release --set=foo=bar --set=ImAgeTAg=toto` -] + - Helm will ignore that value (if it's not used anywhere in templates) -- What happened ? + - the chart is deployed with the default value instead - - The `broken-release` is installed 😨 +- In the second case: -- Is-it really broken ? + - we set `image=v1.0` instead of `image.tag=v1.0` - - Not really: helm just ignored the values it doesn't know about 😓 + - `image` will be a string instead of an object -- Is there something we can do to avoid mistakes ? + - Helm will *probably* fail when trying to evaluate `image.tag` +--- + +## Preventing mistakes + +- To prevent the first mistake, we need to tell Helm: + + *"let me know if any additional (unknonw) value was set!"* + +- To prevent the second mistake, we need to tell Helm: + + *"`image` should be an object, and `image.tag` should be a string!"* + +- We can do this with *values schema validation* --- ## Helm values schema validation -- With *values schema validation*, we can write a spec representing the possible - values accepted by the chart. +- We can write a spec representing the possible values accepted by the chart -- Moving away from the spec make helm bailing out the installation/upgrade +- Helm will check the validity of the values before trying to install/upgrade -- It uses [jsonschema](https://json-schema.org/) language which is a +- If it finds problems, it will stop immediately - `"a vocabulary that allows you to annotate and validate JSON documents."` +- The spec uses [JSON Schema](https://json-schema.org/): -- But can be adapted like here to validate any markup language that consist in - `map|dict|associativearray` and `list|array|sequence|tuple` like *yaml* + *JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.* + +- JSON Schema is designed for JSON, but can easily work with YAML too + + (or any language with `map|dict|associativearray` and `list|array|sequence|tuple`) --- -.exercise[ -- Let's create a `values.schema.json`: - ```json - { - "$schema": "http://json-schema.org/schema#", +## In practice + +- We need to put the JSON Schema spec in a file called `values.schema.json` + + (at the root of our chart; right next to `values.yaml` etc.) + +- The file is optional + +- We don't need to register or declare it in `Chart.yaml` or anywhere + +- Let's write a schema that will verify that ... + + - `image.repository` is an official image (string without slashes or dots) + + - `image.pullPolicy` can only be `Always`, `Never`, `IfNotPresent` + +--- + +## `values.schema.json` + +```json +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "image": { "type": "object", "properties": { - "image": { - "type": "object", - "properties": { - "repository": { - "type": "string", - "pattern": "^[a-z0-9-_]+$" - }, - "pullPolicy": { - "type": "string", - "pattern": "^(Always|Never|IfNotPresent)$" - } } } } } - ``` -] + "repository": { + "type": "string", + "pattern": "^[a-z0-9-_]+$" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } + } + } + } +} +``` --- -## Testing Helm validation -- Now we can test to install Wrong release: +## Testing our schema + +- Let's try to install a couple releases with that schema! .exercise[ -- Run: +- Try an invalid `pullPolicy`: + ```bash + helm install broken --set image.pullPolicy=ShallNotPass + ``` - `helm install broken --set image=image.pullPolicy=ShallNotPass` +- Try an invalid value: + ```bash + helm install should-break --set ImAgeTAg=toto + ``` -- Run: - - `helm install should-break --set=foo=bar --set=ImAgeTAg=toto` ] -- First fails, but second one still pass even if we put a `values.schema.json` ! +- The first one fails, but the second one still passes ... -- Why ? +- Why? --- + ## Bailing out on unkown properties -- We told helm more about valid properties but not what to do with non-existing ones +- We told Helm what properties (values) were valid -- We can fix that with `"additionalProperties": false`: +- We didn't say what to do about additional (unknown) properties! + +- We can fix that with `"additionalProperties": false` .exercise[ @@ -94,15 +154,38 @@ { "$schema": "http://json-schema.org/schema#", "type": "object", + "additionalProperties": false, "properties": { - "image": { [...] } - }, - "additionalProperties": false - } + ... ``` -- And test again: `helm install should-break --set=foo=bar --set=ImAgeTAg=toto` ] + --- +## Testing with unknown properties +.exercise[ + +- Try to pass an extra property: + ```bash + helm install should-break --set ImAgeTAg=toto + ``` + +- Try to pass an extra nested property: + ```bash + helm install does-it-work --set image.hello=world + ``` + +] + +The first command should break. + +The second will not. + +`"additionalProperties": false` needs to be specified at each level. + +??? + +:EN:- Helm schema validation +:FR:- Validation de schema Helm From b0dc1c7c3fcabd4141230d59054a0cbb7e251e5d Mon Sep 17 00:00:00 2001 From: Julien Girardin Date: Wed, 7 Apr 2021 11:32:30 +0200 Subject: [PATCH 23/73] Fix blank slide, and title of Helm Invalid values --- slides/k8s/helm-dependencies.md | 2 -- slides/k8s/helm-values-schema-validation.md | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/slides/k8s/helm-dependencies.md b/slides/k8s/helm-dependencies.md index 20b1b9bd..9d9d575d 100644 --- a/slides/k8s/helm-dependencies.md +++ b/slides/k8s/helm-dependencies.md @@ -329,8 +329,6 @@ class: extra-details (and we should commit `requirements.lock` instead of `Chart.lock`) ---- - ??? :EN:- Depending on other charts diff --git a/slides/k8s/helm-values-schema-validation.md b/slides/k8s/helm-values-schema-validation.md index 7c987377..c8f39a2e 100644 --- a/slides/k8s/helm-values-schema-validation.md +++ b/slides/k8s/helm-values-schema-validation.md @@ -1,4 +1,4 @@ -## Helm and invalid values +# Helm and invalid values - A lot of Helm charts let us specify an image tag like this: ```bash From 04bc8a9f60e10b68e02b2c694273055fb9f811d8 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 7 Apr 2021 15:59:16 +0200 Subject: [PATCH 24/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Add=20a=20bunch=20of?= =?UTF-8?q?=20control=20plane=20diagrams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advanced-control-plane-split-events.svg | 3921 ++++++++++++++++ .../control-planes/advanced-control-plane.svg | 3596 +++++++++++++++ .../control-planes/managed-kubernetes.svg | 1294 ++++++ .../single-control-and-workers.svg | 1611 +++++++ .../images/control-planes/single-node-dev.svg | 914 ++++ .../control-planes/stacked-control-plane.svg | 3940 +++++++++++++++++ slides/k8s/concepts-k8s.md | 30 + slides/workshop.css | 9 + 8 files changed, 15315 insertions(+) create mode 100644 slides/images/control-planes/advanced-control-plane-split-events.svg create mode 100644 slides/images/control-planes/advanced-control-plane.svg create mode 100644 slides/images/control-planes/managed-kubernetes.svg create mode 100644 slides/images/control-planes/single-control-and-workers.svg create mode 100644 slides/images/control-planes/single-node-dev.svg create mode 100644 slides/images/control-planes/stacked-control-plane.svg diff --git a/slides/images/control-planes/advanced-control-plane-split-events.svg b/slides/images/control-planes/advanced-control-plane-split-events.svg new file mode 100644 index 00000000..ae30df7d --- /dev/null +++ b/slides/images/control-planes/advanced-control-plane-split-events.svg @@ -0,0 +1,3921 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADVANCED CONTROL PLANE + WORKERS(SPLIT EVENTS) + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + passive + + + + active + + + + + + + + + + + + + + + + + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + app pod + + + + some combination of VMs,containers, pods ... + APIload balancer + + + + API server + + API server + + API server + + API server + + API server + + API server + + scheduler + + scheduler + + + + controller manager + + controller manager + + + + + + + + etcd + + + + + + + + + + etcd + + + + etcd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + + + + etcd (events) + + + + + + + + + + etcd (events) + + + + etcd (events) + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + + diff --git a/slides/images/control-planes/advanced-control-plane.svg b/slides/images/control-planes/advanced-control-plane.svg new file mode 100644 index 00000000..84ff5350 --- /dev/null +++ b/slides/images/control-planes/advanced-control-plane.svg @@ -0,0 +1,3596 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADVANCED CONTROL PLANE + WORKERS + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + passive + + + + active + + + + + + + + + + + + + + + + + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + app pod + + + + some combination of VMs,containers, pods ... + APIload balancer + + + + API server + + API server + + API server + + API server + + API server + + API server + + scheduler + + scheduler + + + + controller manager + + controller manager + + + + + + + + etcd + + + + + + + + + + etcd + + + + etcd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + diff --git a/slides/images/control-planes/managed-kubernetes.svg b/slides/images/control-planes/managed-kubernetes.svg new file mode 100644 index 00000000..30a02615 --- /dev/null +++ b/slides/images/control-planes/managed-kubernetes.svg @@ -0,0 +1,1294 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MANAGED KUBERNETES + + + + controllermanager + + + + scheduler + + control plane(operated by provider) + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + + + + + + + + + + + + + pod + + + + kubelet + + + + kubelet + + + + + + pod + + + + + + pod + + + + + + pod + + + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + + ? + API server + + + + + + cloudcontrollermanager + etcd + + + diff --git a/slides/images/control-planes/single-control-and-workers.svg b/slides/images/control-planes/single-control-and-workers.svg new file mode 100644 index 00000000..3bec2e53 --- /dev/null +++ b/slides/images/control-planes/single-control-and-workers.svg @@ -0,0 +1,1611 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SINGLE-NODE CONTROL PLANE + WORKERS(DEPLOYED WITH KUBEADM) + + + + + + + + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + kubelet + + container engine + + + + + + + + + API server + + + + kubelet + + container engine + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + API server + + + diff --git a/slides/images/control-planes/single-node-dev.svg b/slides/images/control-planes/single-node-dev.svg new file mode 100644 index 00000000..828de926 --- /dev/null +++ b/slides/images/control-planes/single-node-dev.svg @@ -0,0 +1,914 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SINGLE-NODE CLUSTER (FOR DEVELOPMENT) + + + API server + + + + controllermanager + + + + scheduler + + VM or container + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + etcd + + + + + + + + + + kubelet + + + + + + pod + + + + + containerengine + + + + + + + + + + pod + + + + + + pod + + + diff --git a/slides/images/control-planes/stacked-control-plane.svg b/slides/images/control-planes/stacked-control-plane.svg new file mode 100644 index 00000000..432209b2 --- /dev/null +++ b/slides/images/control-planes/stacked-control-plane.svg @@ -0,0 +1,3940 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + STACKED CONTROL PLANE + WORKERS(DEPLOYED WITH KUBEADM) + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + passive + + + + + + + + + other pods... + + + + + + + + + active + + + + + + + + + + + + + + + + + + + + + + APIload balancer + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + app pod + + + + + diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md index f8d3a0db..4fd7a3fe 100644 --- a/slides/k8s/concepts-k8s.md +++ b/slides/k8s/concepts-k8s.md @@ -220,6 +220,36 @@ class: extra-details --- +class: pic +![](images/control-planes/single-node-dev.svg) + +--- + +class: pic +![](images/control-planes/managed-kubernetes.svg) + +--- + +class: pic +![](images/control-planes/single-control-and-workers.svg) + +--- + +class: pic +![](images/control-planes/stacked-control-plane.svg) + +--- + +class: pic +![](images/control-planes/advanced-control-plane.svg) + +--- + +class: pic +![](images/control-planes/advanced-control-plane-split-events.svg) + +--- + class: extra-details ## How many nodes should a cluster have? diff --git a/slides/workshop.css b/slides/workshop.css index f3047404..f64fdec0 100644 --- a/slides/workshop.css +++ b/slides/workshop.css @@ -109,8 +109,17 @@ div.pic p { div.pic img { display: block; margin: auto; + /* + "pic" class slides should have a single, full screen picture. + We used to have these attributes below but they prevented + pictures from taking up the whole slide. Replacing them with + 100%/100% seems to put the pictures full screen, but I've left + these old attributes here just in case. max-width: 1210px; max-height: 550px; + */ + max-width: 100%; + max-height: 100%; } div.pic h1, div.pic h2, div.title h1, div.title h2 { text-align: center; From be6d982e2c504688959ef40bc9ac74fff877cd1a Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 7 Apr 2021 16:52:36 +0200 Subject: [PATCH 25/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Add=20non-dedicated?= =?UTF-8?q?=20control=20plane?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks @zempashi for the suggestion 👍🏻 --- .../non-dedicated-stacked-nodes.svg | 3132 +++++++++++++++++ slides/k8s/concepts-k8s.md | 5 + 2 files changed, 3137 insertions(+) create mode 100644 slides/images/control-planes/non-dedicated-stacked-nodes.svg diff --git a/slides/images/control-planes/non-dedicated-stacked-nodes.svg b/slides/images/control-planes/non-dedicated-stacked-nodes.svg new file mode 100644 index 00000000..f0bd1033 --- /dev/null +++ b/slides/images/control-planes/non-dedicated-stacked-nodes.svg @@ -0,0 +1,3132 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CONTROL PLANE AND APPS RUNNING ON THE SAME NODES(SHOWN HERE WITH STACKED CONTROL PLANE) + + + + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + + + + + + + + + + + + + APIload balancer + + + + + + + + + + + passive + + + + + + + + + active + + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + ... + + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md index 4fd7a3fe..adfc0336 100644 --- a/slides/k8s/concepts-k8s.md +++ b/slides/k8s/concepts-k8s.md @@ -240,6 +240,11 @@ class: pic --- +class: pic +![](images/control-planes/non-dedicated-stacked-nodes.svg) + +--- + class: pic ![](images/control-planes/advanced-control-plane.svg) From e3e4d0420221fc75dbfbc32249577a72716a77e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jan 2021 23:13:24 +0000 Subject: [PATCH 26/73] Bump socket.io from 2.0.4 to 2.4.0 in /slides/autopilot Bumps [socket.io](https://github.com/socketio/socket.io) from 2.0.4 to 2.4.0. - [Release notes](https://github.com/socketio/socket.io/releases) - [Changelog](https://github.com/socketio/socket.io/blob/2.4.0/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io/compare/2.0.4...2.4.0) Signed-off-by: dependabot[bot] --- slides/autopilot/package-lock.json | 298 ++++++++++++++++------------- slides/autopilot/package.json | 2 +- 2 files changed, 163 insertions(+), 137 deletions(-) diff --git a/slides/autopilot/package-lock.json b/slides/autopilot/package-lock.json index a709ddbe..1d9fade4 100644 --- a/slides/autopilot/package-lock.json +++ b/slides/autopilot/package-lock.json @@ -24,14 +24,9 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=" - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" }, "backo2": { "version": "1.0.2", @@ -39,27 +34,19 @@ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" }, "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=" + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, "body-parser": { "version": "1.18.2", @@ -83,20 +70,15 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" - }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "component-inherit": { "version": "0.0.3", @@ -152,58 +134,76 @@ "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" }, "engine.io": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz", - "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", + "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.6.9", - "engine.io-parser": "2.1.1", - "uws": "0.14.5", - "ws": "3.3.3" + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.4.2" }, "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "engine.io-client": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", - "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", + "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", "requires": { - "component-emitter": "1.2.1", + "component-emitter": "~1.3.0", "component-inherit": "0.0.3", - "debug": "2.6.9", - "engine.io-parser": "2.1.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", "has-cors": "1.1.0", "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "3.3.3", - "xmlhttprequest-ssl": "1.5.4", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } } }, "engine.io-parser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz", - "integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", "requires": { "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary2": "1.0.2" + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" } }, "escape-html": { @@ -278,9 +278,9 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "has-binary2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", - "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", "requires": { "isarray": "2.0.1" } @@ -376,11 +376,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" - }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -390,20 +385,14 @@ } }, "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "requires": { - "better-assert": "1.0.2" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" }, "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "1.0.2" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" }, "parseurl": { "version": "1.3.2", @@ -487,51 +476,104 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "socket.io": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", - "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.0.tgz", + "integrity": "sha512-9UPJ1UTvKayuQfVv2IQ3k7tCQC/fboDyIK62i99dAQIyHKaBsNdTpwHLgKJ6guRWxRtC9H+138UwpaGuQO9uWQ==", "requires": { - "debug": "2.6.9", - "engine.io": "3.1.4", - "socket.io-adapter": "1.1.1", - "socket.io-client": "2.0.4", - "socket.io-parser": "3.1.2" + "debug": "~4.1.0", + "engine.io": "~3.5.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.4.0", + "socket.io-parser": "~3.4.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } } }, "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" }, "socket.io-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", - "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", + "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", "requires": { "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.6.9", - "engine.io-client": "3.1.4", - "has-cors": "1.1.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "3.1.2", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "socket.io-parser": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz", + "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==", + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } + } } }, "socket.io-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", - "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", + "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", "requires": { "component-emitter": "1.2.1", - "debug": "2.6.9", - "has-binary2": "1.0.2", + "debug": "~4.1.0", "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } } }, "statuses": { @@ -553,11 +595,6 @@ "mime-types": "2.1.17" } }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -568,31 +605,20 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "uws": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", - "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", - "optional": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" - } + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, "xmlhttprequest-ssl": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz", - "integrity": "sha1-BPVgkVcks4kIhxXMDteBPpZ3v1c=" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" }, "yeast": { "version": "0.1.2", diff --git a/slides/autopilot/package.json b/slides/autopilot/package.json index b7832ea2..34df3d90 100644 --- a/slides/autopilot/package.json +++ b/slides/autopilot/package.json @@ -3,6 +3,6 @@ "version": "0.0.1", "dependencies": { "express": "^4.16.2", - "socket.io": "^2.0.4" + "socket.io": "^2.4.0" } } From 88d4e5ff54e3543ae10628d4ebc80e00036b5bf5 Mon Sep 17 00:00:00 2001 From: Anton Weiss Date: Sun, 31 Jan 2021 12:18:09 +0200 Subject: [PATCH 27/73] Update volumeSnapshot link and status --- slides/k8s/cluster-backup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slides/k8s/cluster-backup.md b/slides/k8s/cluster-backup.md index 113bd134..db8edd92 100644 --- a/slides/k8s/cluster-backup.md +++ b/slides/k8s/cluster-backup.md @@ -338,9 +338,9 @@ docker run --rm --net host -v $PWD:/vol \ (e.g. [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/create-snapshots/) can [create snapshots through annotations](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/create-snapshots/snaps-annotations/#taking-periodic-snapshots-on-a-running-pod)) -- Option 3: [snapshots through Kubernetes API](https://kubernetes.io/blog/2018/10/09/introducing-volume-snapshot-alpha-for-kubernetes/) +- Option 3: [snapshots through Kubernetes API](https://kubernetes.io/docs/concepts/storage/volume-snapshots/) - (now in alpha for a few storage providers: GCE, OpenSDS, Ceph, Portworx) + (Generally available since Kuberentes 1.20 for a number of [CSI](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/) volume plugins : GCE, OpenSDS, Ceph, Portworx, etc) --- From 6bc08c0a7e335bdf685aa3149989b791dd845d85 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 7 Feb 2021 21:41:08 +0100 Subject: [PATCH 28/73] Add k9s section --- slides/k8s/k9s.md | 141 ++++++++++++++++++++++++++++++++++++++ slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 5 files changed, 145 insertions(+) create mode 100644 slides/k8s/k9s.md diff --git a/slides/k8s/k9s.md b/slides/k8s/k9s.md new file mode 100644 index 00000000..9c5174e4 --- /dev/null +++ b/slides/k8s/k9s.md @@ -0,0 +1,141 @@ +# k9s + +- Somewhere in between CLI and GUI (or web UI), we can find the magic land of TUI + + - [Text-based user interfaces](https://en.wikipedia.org/wiki/Text-based_user_interface) + + - often using libraries like [curses](https://en.wikipedia.org/wiki/Curses_%28programming_library%29) and its successors + +- Some folks love them, some folks hate them, some are indifferent ... + +- But it's nice to have different options! + +- Let's see one particular TUI for Kubernetes: [k9s](https://k9scli.io/) + +--- + +## Installing k9s + +- If you are using a training cluster or the [shpod](https://github.com/jpetazzo/shpod) image, k9s is pre-installed + +- Otherwise, it can be installed easily: + + - with [various package managers](https://k9scli.io/topics/install/) + + - or by fetching a [binary release](https://github.com/derailed/k9s/releases) + +- We don't need to set up or configure anything + + (it will use the same configuration as `kubectl` and other well-behaved clients) + +- Just run `k9s` to fire it up! + +--- + +## What kind to we want to see? + +- Press `:` to change the type of resource to view + +- Then type, for instance, `ns` or `namespace` or `nam[TAB]`, then `[ENTER]` + +- Use the arrows to move down to e.g. `kube-system`, and press `[ENTER]` + +- Or, type `/kub` or `/sys` to filter the output, and press `[ENTER]` twice + + (once to exit the filter, once to enter the namespace) + +- We now see the pods in `kube-system`! + +--- + +## Interacting with pods + +- `l` to view logs + +- `d` to describe + +- `s` to get a shell (won't work if `sh` isn't available in the container image) + +- `e` to edit + +- `shift-f` to define port forwarding + +- `ctrl-k` to kill + +- `[ESC]` to get out or get back + +--- + +## Quick navigation between namespaces + +- On top of the screen, we should see shortcuts like this: + ``` + <0> all + <1> kube-system + <2> default + ``` + +- Pressing the corresponding number switches to that namespace + + (or shows resources across all namespaces with `0`) + +- Locate a namespace with a copy of DockerCoins, and go there! + +--- + +## Interacting with Deployments + +- View Deployments (type `:` `deploy` `[ENTER]`) + +- Select e.g. `worker` + +- Scale it with `s` + +- View its aggregated logs with `l` + +--- + +## Exit + +- Exit at any time with `Ctrl-C` + +- k9s will "remember" where you were + + (and go back there next time you run it) + +--- + +## Pros + +- Very convenient to navigate through resources + + (hopping from a deployment, to its pod, to another namespace, etc.) + +- Very convenient to quickly view logs of e.g. init containers + +- Very convenient to get a (quasi) realtime view of resources + + (if we use `watch kubectl get` a lot, we will probably like k9s) + +--- + +## Cons + +- Doesn't promote automation / scripting + + (if you repeat the same things over and over, there is a scripting opportunity) + +- Not all features are available + + (e.g. executing arbitrary commands in containers) + +--- + +## Conclusion + +Try it out, and see if it makes you more productive! + +??? + +:EN:- The k9s TUI +:FR:- L'interface texte k9s diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 72c9f631..87ba65d0 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -58,6 +58,7 @@ content: #- k8s/setup-managed.md #- k8s/setup-selfhosted.md #- k8s/dashboard.md + #- k8s/k9s.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 0aaea969..6f4ed3c6 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -58,6 +58,7 @@ content: #- k8s/accessinternal.md #- k8s/kubectlproxy.md - - k8s/dashboard.md + - k8s/k9s.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 0fe9be4b..90b3d5e3 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -59,6 +59,7 @@ content: - k8s/setup-managed.md - k8s/setup-selfhosted.md - k8s/dashboard.md + - k8s/k9s.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 638bcc26..e52f7eaf 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -58,6 +58,7 @@ content: #- k8s/setup-managed.md #- k8s/setup-selfhosted.md - k8s/dashboard.md + - k8s/k9s.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md From 426957bdca201b80062fb6678b688d83c86a60ee Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 7 Feb 2021 21:44:38 +0100 Subject: [PATCH 29/73] Add Tilt section --- dockercoins/Tiltfile | 49 +++++++ slides/k8s/tilt.md | 302 ++++++++++++++++++++++++++++++++++++++ slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 3 +- slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 dockercoins/Tiltfile create mode 100644 slides/k8s/tilt.md diff --git a/dockercoins/Tiltfile b/dockercoins/Tiltfile new file mode 100644 index 00000000..d06b8120 --- /dev/null +++ b/dockercoins/Tiltfile @@ -0,0 +1,49 @@ +k8s_yaml(blob(''' +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: registry + name: registry +spec: + selector: + matchLabels: + app: registry + template: + metadata: + labels: + app: registry + spec: + containers: + - image: registry + name: registry +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: registry + name: registry +spec: + ports: + - port: 5000 + protocol: TCP + targetPort: 5000 + nodePort: 30555 + selector: + app: registry + type: NodePort +''')) +default_registry('localhost:30555') +docker_build('dockercoins/hasher', 'hasher') +docker_build('dockercoins/rng', 'rng') +docker_build('dockercoins/webui', 'webui') +docker_build('dockercoins/worker', 'worker') +k8s_yaml('../k8s/dockercoins.yaml') + +# Uncomment the following line to let tilt run with the default kubeadm cluster-admin context. +#allow_k8s_contexts('kubernetes-admin@kubernetes') + +# While we're here: if you're controlling a remote cluster, uncomment that line. +# It will create a port forward so that you can access the remote registry. +#k8s_resource(workload='registry', port_forwards='30555:5000') diff --git a/slides/k8s/tilt.md b/slides/k8s/tilt.md new file mode 100644 index 00000000..678f005a --- /dev/null +++ b/slides/k8s/tilt.md @@ -0,0 +1,302 @@ +# Tilt + +- What does a development workflow look like? + + - make changes + + - test / see these changes + + - repeat! + +- What does it look like, with containers? + + 🤔 + +--- + +## Basic Docker workflow + +- Preparation + + - write Dockerfiles + +- Iteration + + - edit code + - `docker build` + - `docker run` + - test + - `docker stop` + +Straightforward when we have a single container. + +--- + +## Docker workflow with volumes + +- Preparation + + - write Dockerfiles + - `docker build` + `docker run` + +- Iteration + + - edit code + - test + +Note: only works with interpreted languages. +
+(Compiled languages require extra work.) + +--- + +## Docker workflow with Compose + +- Preparation + + - write Dockerfiles + Compose file + - `docker-compose up` + +- Iteration + + - edit code + - test + - `docker-compose up` (as needed) + +Simplifies complex scenarios (multiple containers). +
+Facilitates updating images. + +--- + +## Basic Kubernetes workflow + +- Preparation + + - write Dockerfiles + - write Kubernetes YAML + - set up container registry + +- Iteration + + - edit code + - build images + - push images + - update Kubernetes resources + +Seems simple enough, right? + +--- + +## Basic Kubernetes workflow + +- Preparation + + - write Dockerfiles + - write Kubernetes YAML + - **set up container registry** + +- Iteration + + - edit code + - build images + - **push images** + - update Kubernetes resources + +Ah, right ... + +--- + +## We need a registry + +- Remember "build, ship, and run" + +- Registries are involved in the "ship" phase + +- With Docker, we were building and running on the same node + +- We didn't need a registry! + +- With Kubernetes, though ... + +--- + +## Special case of single node clusters + +- If our Kubernetes has only one node ... + +- ... We can build directly on that node ... + +- ... We don't need to push images ... + +- ... We don't need to run a registry! + +- Examples: Docker Desktop, Minikube ... + +--- + +## When we have more than one node + +- Which registry should we use? + + (Docker Hub, Quay, cloud-based, self-hosted ...) + +- Should we use a single registry, or one per cluster or environment? + +- Which tags and credentials should we use? + + (in particular when using a shared registry!) + +- How do we provision that registry and its users? + +- How do we adjust our Kubernetes YAML manifests? + + (e.g. to inject image names and tags) + +--- + +## More questions + +- The whole cycle (build+push+update) is expensive + +- If we have many services, how do we update only the ones we need? + +- Can we take shortcuts? + + (e.g. synchronized files without going through a whole build+push+update cycle) + +--- + +## Tilt + +- Tilt is a tool to address all these questions + +- There are other similar tools (e.g. Skaffold) + +- We arbitrarily decided to focus on that one + +--- + +## Tilt in practice + +- The `dockercoins` directory in our repository has a `Tiltfile` + +- Go to that directory and try `tilt up` + +- Tilt should refuse to start, but it will explain why + +- Edit the `Tiltfile` accordingly and try again + +- Open the Tilt web UI + + (if running Tilt on a remote machine, you will need `tilt up --host 0.0.0.0`) + +- Watch as the Dockercoins app is built, pushed, started + +--- + +## What's in our Tiltfile? + +- Kubernetes manifests for a local registry + +- Kubernetes manifests for DockerCoins + +- Instructions indicating how to build DockerCoins' images + +- A tiny bit of sugar + + (telling Tilt which registry to use) + +--- + + +## How does it work? + +- Tilt keeps track of dependencies between files and resources + + (a bit like a `make` that would run continuously) + +- It automatically alters some resources + + (for instance, it updates the images used in our Kubernetes manifests) + +- That's it! + +(And of course, it provides a great web UI, lots of libraries, etc.) + +--- + +## What happens when we edit a file (1/2) + +- Let's change e.g. `worker/worker.py` + +- Thanks to this line, + ```python + docker_build('dockercoins/worker', 'worker') + ``` + ... Tilt watches the `worker` directory and uses it to build `dockercoins/worker` + +- Thanks to this line, + ```python + default_registry('localhost:30555') + ``` + ... Tilt actually renames `dockercoins/worker` to `localhost:30555/dockercoins_worker` + +- Tilt will tag the image with something like `tilt-xxxxxxxxxx` + +--- + +## What happens when we edit a file (2/2) + +- Thanks to this line, + ```python + k8s_yaml('../k8s/dockercoins.yaml') + ``` + ... Tilt is aware of our Kubernetes resources + +- The `worker` Deployment uses `dockercoins/worker`, so it must be updated + +- `dockercoins/worker` becomes `localhost:30555/dockercoins_worker:tilt-xxx` + +- The `worker` Deployment gets updated on the Kubernetes cluster + +- All these operations (and their log output) are visible in the Tilt UI + +--- + +## Configuration file format + +- The Tiltfile is written in [Starlark](https://github.com/bazelbuild/starlark) + + (essentially a subset of Python) + +- Tilt monitors the Tiltfile too + + (so it reloads it immediately when we change it) + +--- + +## Tilt "killer features" + +- Dependency engine + + (build or run only what's necessary) + +- Ability to watch resources + + (execute actions immediately, without explicitly running a command) + +- Rich library of function and helpers + + (build container images, manipulate YAML manifests...) + +- Convenient UI (web; TUI also available) + + (provides immediate feedback and logs) + +- Extensibility! + +??? + +:EN:- Development workflow with Tilt +:FR:- Développer avec Tilt diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 87ba65d0..17b93bcd 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -59,6 +59,7 @@ content: #- k8s/setup-selfhosted.md #- k8s/dashboard.md #- k8s/k9s.md + #- k8s/tilt.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 6f4ed3c6..42da801c 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -58,7 +58,8 @@ content: #- k8s/accessinternal.md #- k8s/kubectlproxy.md - - k8s/dashboard.md - - k8s/k9s.md + #- k8s/k9s.md + #- k8s/tilt.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 90b3d5e3..d725650b 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -60,6 +60,7 @@ content: - k8s/setup-selfhosted.md - k8s/dashboard.md - k8s/k9s.md + - k8s/tilt.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index e52f7eaf..7b8b4de7 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -59,6 +59,7 @@ content: #- k8s/setup-selfhosted.md - k8s/dashboard.md - k8s/k9s.md + #- k8s/tilt.md #- k8s/kubectlscale.md - k8s/scalingdockercoins.md - shared/hastyconclusions.md From 18853b24978f2db9688746a2d382a323a061616c Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 15 Feb 2021 22:19:45 +0100 Subject: [PATCH 30/73] Add diagrams showing the different k8s network layers --- slides/images/k8s-net-0-overview.svg | 1060 +++++++++++++++++++++++ slides/images/k8s-net-1-pod-to-pod.svg | 519 +++++++++++ slides/images/k8s-net-2-pod-to-svc.svg | 587 +++++++++++++ slides/images/k8s-net-3-netpol.svg | 493 +++++++++++ slides/images/k8s-net-4-overview.svg | 1108 ++++++++++++++++++++++++ slides/k8s/kubenet.md | 30 + 6 files changed, 3797 insertions(+) create mode 100644 slides/images/k8s-net-0-overview.svg create mode 100644 slides/images/k8s-net-1-pod-to-pod.svg create mode 100644 slides/images/k8s-net-2-pod-to-svc.svg create mode 100644 slides/images/k8s-net-3-netpol.svg create mode 100644 slides/images/k8s-net-4-overview.svg diff --git a/slides/images/k8s-net-0-overview.svg b/slides/images/k8s-net-0-overview.svg new file mode 100644 index 00000000..1227c09d --- /dev/null +++ b/slides/images/k8s-net-0-overview.svg @@ -0,0 +1,1060 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/images/k8s-net-1-pod-to-pod.svg b/slides/images/k8s-net-1-pod-to-pod.svg new file mode 100644 index 00000000..5358b23d --- /dev/null +++ b/slides/images/k8s-net-1-pod-to-pod.svg @@ -0,0 +1,519 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/images/k8s-net-2-pod-to-svc.svg b/slides/images/k8s-net-2-pod-to-svc.svg new file mode 100644 index 00000000..2a2dc80d --- /dev/null +++ b/slides/images/k8s-net-2-pod-to-svc.svg @@ -0,0 +1,587 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/images/k8s-net-3-netpol.svg b/slides/images/k8s-net-3-netpol.svg new file mode 100644 index 00000000..af655baf --- /dev/null +++ b/slides/images/k8s-net-3-netpol.svg @@ -0,0 +1,493 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/images/k8s-net-4-overview.svg b/slides/images/k8s-net-4-overview.svg new file mode 100644 index 00000000..d1ed10a5 --- /dev/null +++ b/slides/images/k8s-net-4-overview.svg @@ -0,0 +1,1108 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/k8s/kubenet.md b/slides/k8s/kubenet.md index 73c6af0f..86bf2d44 100644 --- a/slides/k8s/kubenet.md +++ b/slides/k8s/kubenet.md @@ -128,6 +128,36 @@ class: extra-details --- +class: pic + +![Overview of the three Kubernetes network layers](images/k8s-net-0-overview.svg) + +--- + +class: pic + +![Pod-to-pod network](images/k8s-net-1-pod-to-pod.svg) + +--- + +class: pic + +![Pod-to-service network](images/k8s-net-2-pod-to-svc.svg) + +--- + +class: pic + +![Network policies](images/k8s-net-3-netpol.svg) + +--- + +class: pic + +![View with all the layers again](images/k8s-net-4-overview.svg) + +--- + class: extra-details ## Even more moving parts From 2295e4f3de5e8cc66ec5bc13803ead986f6979fc Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 18 Feb 2021 09:18:23 +0100 Subject: [PATCH 31/73] =?UTF-8?q?=F0=9F=90=9E=20Fix=20missing=20closing=20?= =?UTF-8?q?triple-backquote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/setup-devel.md | 1 + 1 file changed, 1 insertion(+) diff --git a/slides/k8s/setup-devel.md b/slides/k8s/setup-devel.md index b15e77c1..5cecbd22 100644 --- a/slides/k8s/setup-devel.md +++ b/slides/k8s/setup-devel.md @@ -63,6 +63,7 @@ ```bash k3d cluster create groscluster \ --image rancher/k3s:v1.18.9-k3s1 --servers 3 --agents 5 + ``` (3 nodes for the control plane + 5 worker nodes) From 6c11de207a6f8ebcb5698b00ccdb7a4225f08c77 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sat, 20 Feb 2021 11:51:45 +0100 Subject: [PATCH 32/73] =?UTF-8?q?=F0=9F=94=8E=20Extra=20details=20about=20?= =?UTF-8?q?CPU=20limits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/resource-limits.md | 106 ++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/slides/k8s/resource-limits.md b/slides/k8s/resource-limits.md index ae1b73dd..75be8b6e 100644 --- a/slides/k8s/resource-limits.md +++ b/slides/k8s/resource-limits.md @@ -40,6 +40,112 @@ --- +class: extra-details + +## CPU limits implementation details + +- A container with a CPU limit will be "rationed" by the kernel + +- Every `cfs_period_us`, it will receive a CPU quota, like an "allowance" + + (that interval defaults to 100ms) + +- Once it has used its quota, it will be stalled until the next period + +- This can easily result in throttling for bursty workloads + + (see details on next slide) + +--- + +class: extra-details + +## A bursty example + +- Web service receives one request per minute + +- Each request takes 1 second of CPU + +- Average load: 0.16% + +- Let's say we set a CPU limit of 10% + +- This means CPU quotas of 10ms every 100ms + +- Obtaining the quota for 1 second of CPU will take 10 seconds + +- Observed latency will be 10 seconds (... actually 9.9s) instead of 1 second + + (real-life scenarios will of course be less extreme, but they do happen!) + +--- + +class: extra-details + +## Multi-core scheduling details + +- Each core gets a small share of the container's CPU quota + + (this avoids locking and contention on the "global" quota for the container) + +- By default, the kernel distributes that quota to CPUs in 5ms increments + + (tunable with `kernel.sched_cfs_bandwidth_slice_us`) + +- If a containerized process (or thread) uses up its local CPU quota: + + *it gets more from the "global" container quota (if there's some left)* + +- If it "yields" (e.g. sleeps for I/O) before using its local CPU quota: + + *the quota is **soon** returned to the "global" container quota, **minus** 1ms* + +--- + +class: extra-details + +## Low quotas on machines with many cores + +- The local CPU quota is not immediately returned to the global quota + + - this reduces locking and contention on the global quota + + - but this can cause starvation when many threads/processes become runnable + +- That 1ms that "stays" on the local CPU quota is often useful + + - if the thread/process becomes runnable, it can be scheduled immediately + + - again, this reduces locking and contention on the global quota + + - but if the thread/process doesn't become runnable, it is wasted! + + - this can become a huge problem on machines with many cores + +--- + +class: extra-details + +## CPU limits in a nutshell + +- Beware if you run small bursty workloads on machines with many cores! + + ("highly-threaded, user-interactive, non-cpu bound applications") + +- Check the `nr_throttled` and `throttled_time` metrics in `cpu.stat` + +- Possible solutions/workarounds: + + - be generous with the limits + + - make sure your kernel has the [appropriate patch](https://lkml.org/lkml/2019/5/17/581) + + - use [static CPU manager policy](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#static-policy) + +For more details, check [this blog post](https://erickhun.com/posts/kubernetes-faster-services-no-cpu-limits/) or these ones ([part 1](https://engineering.indeedblog.com/blog/2019/12/unthrottled-fixing-cpu-limits-in-the-cloud/), [part 2](https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/)). + +--- + ## Exceeding memory limits - Memory needs to be swapped out before being reclaimed From 6ec8849da143f051fc6dcbee7c114caae9b2c6f8 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 21 Feb 2021 15:12:00 +0100 Subject: [PATCH 33/73] =?UTF-8?q?=F0=9F=A7=AA=20Add=20GitLab=20chapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/gitlab.md | 447 ++++++++++++++++++++++++++++++++++++++ slides/kube-fullday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 4 files changed, 450 insertions(+) create mode 100644 slides/k8s/gitlab.md diff --git a/slides/k8s/gitlab.md b/slides/k8s/gitlab.md new file mode 100644 index 00000000..8f198b52 --- /dev/null +++ b/slides/k8s/gitlab.md @@ -0,0 +1,447 @@ +# CI/CD with GitLab + +- In this section, we will see how to set up a CI/CD pipeline with GitLab + + (using a "self-hosted" GitLab; i.e. running on our Kubernetes cluster) + +- The big picture: + + - each time we push code to GitLab, it will be deployed in a staging environment + + - each time we push the `production` tag, it will be deployed in production + +--- + +## Disclaimers + +- We'll use GitLab here as an exemple, but there are many other options + + (e.g. some combination of Argo, Harbor, Tekton ...) + +- There are also hosted options + + (e.g. GitHub Actions and many others) + +- We'll use a specific pipeline and workflow, but it's purely arbitrary + + (treat it as a source of inspiration, not a model to be copied!) + +--- + +## Workflow overview + +- Push code to GitLab's git server + +- GitLab notices the `.gitlab-ci.yml` file, which defines our pipeline + +- Our pipeline can have multiple *stages* executed sequentially + + (e.g. lint, build, test, deploy ...) + +- Each stage can have multiple *jobs* executed in parallel + + (e.g. build images in parallel) + +- Each job will be executed in an independent *runner* pod + +--- + +## Pipeline overview + +- Our repository holds source code, Dockerfiles, and a Helm chart + +- *Lint* stage will check the Helm chart validity + +- *Build* stage will build container images + + (and push them to GitLab's integrated registry) + +- *Deploy* stage will deploy the Helm chart, using these images + +- Pushes to `production` will deploy to "the" production namespace + +- Pushes to other tags/branches will deploy to a namespace created on the fly + +- We will discuss shortcomings and alternatives and the end of this chapter! + +--- + +## Lots of requirements + +- We need *a lot* of components to pull this off: + + - a domain name + + - a storage class + + - a TLS-capable ingress controller + + - the cert-manager operator + + - GitLab itself + + - the GitLab pipeline + +- Wow, why?!? + +--- + +## I find your lack of TLS disturbing + +- We need a container registry (obviously!) + +- Docker (and other container engines) *require* TLS on the registry + + (with valid certificates) + +- A few options: + + - use a "real" TLS certificate (e.g. obtained with Let's Encrypt) + + - use a self-signed TLS certificate + + - communicate with the registry over localhost (TLS isn't required then) + +--- + +class: extra-details + +## Why not self-signed certs? + +- When using self-signed certs, we need to either: + + - add the cert (or CA) to trusted certs + + - disable cert validation + +- This needs to be done on *every client* connecting to the registry: + + - CI/CD pipeline (building and pushing images) + + - container engine (deploying the images) + + - other tools (e.g. container security scanner) + +- It's doable, but it's a lot of hacks (especially when adding more tools!) + +--- + +class: extra-details + +## Why not localhost? + +- TLS is usually not required when the registry is on localhost + +- We could expose the registry e.g. on a `NodePort` + +- ... And then tweak the CI/CD pipeline to use that instead + +- This is great when obtaining valid certs is difficult: + + - air-gapped or internal environments (that can't use Let's Encrypt) + + - no domain name available + +- Downside: the registry isn't easily or safely available from outside + + (the `NodePort` essentially defeats TLS) + +--- + +class: extra-details + +## Can we use `nip.io`? + +- We will use Let's Encrypt + +- Let's Encrypt has a quota of certificates per domain + + (in 2020, that was [50 certificates per week per domain](https://letsencrypt.org/docs/rate-limits/)) + +- So if we all use `nip.io`, we will probably run into that limit + +- But you can try and see if it works! + +--- + +## Ingress + +- We will assume that we have a domain name pointing to our cluster + + (i.e. with a wildcard record pointing to at least one node of the cluster) + +- We will get traffic in the cluster by leveraging `ExternalIPs` services + + (but it would be easy to use `LoadBalancer` services instead) + +- We will use Traefik as the ingress controller + + (but any other one should work too) + +- We will use cert-manager to obtain certificates with Let's Encrypt + +--- + +## Other details + +- We will deploy GitLab with its official Helm chart + +- It will still require a bunch of parameters and customization + +- We also need a Storage Class + + (unless our cluster already has one, of course) + +- We suggest the [Rancher local path provisioner](https://github.com/rancher/local-path-provisioner) + +--- + +## Setting everything up + +1. `git clone https://github.com/jpetazzo/kubecoin` + +2. `export EMAIL=xxx@example.com DOMAIN=awesome-kube-ci.io` + + (we need a real email address and a domain pointing to the cluster!) + +3. `. setup-gitlab-on-k8s.rc` + + (this doesn't do anything, but defines a number of helper functions) + +4. Execute each helper function, one after another + + (try `do_[TAB]` to see these functions) + +--- + +## Local Storage + +`do_1_localstorage` + +Applies the YAML directly from Rancher's repository. + +Annotate the Storage Class so that it becomes the default one. + +--- + +## Traefik + +`do_2_traefik_with_externalips` + +Install the official Traefik Helm chart. + +Instead of a `LoadBalancer` service, use a `ClusterIP` with `ExternalIPs`. + +Automatically infer the `ExternalIPs` from `kubectl get nodes`. + +Enable TLS. + +--- + +## cert-manager + +`do_3_certmanager` + +Install cert-manager using their official YAML. + +Easy-peasy. + +--- + +## Certificate issuers + +`do_4_issuers` + +Create a couple of `ClusterIssuer` resources for cert-manager. + +(One for the staging Let's Encrypt environment, one for production.) + +Note: this requires to specify a valid `$EMAIL` address! + +Note: if this fails, wait a bit and try again (cert-manager needs to be up). + +--- + +## GitLab + +`do_5_gitlab` + +Deploy GitLab using their official Helm chart. + +We pass a lot of parameters to this chart: +- the domain name to use +- disable GitLab's own ingress and cert-manager +- annotate the ingress resources so that cert-manager kicks in +- bind the shell service (git over SSH) to port 222 to avoid conflict +- use ExternalIPs for that shell service + +Note: on modest cloud instances, it can take 10 minutes for GitLab to come up. + +We can check the status with `kubectl get pods --namespace=gitlab` + +--- + +## Log into GitLab and configure it + +`do_6_showlogin` + +This will get the GitLab root password (stored in a Secret). + +Then we need to: +- log into GitLab +- add our SSH key (top-right user menu → settings, then SSH keys on the left) +- create a project (using the + menu next to the search bar on top) +- go to project configuration (on the left, settings → CI/CD) +- add a `KUBECONFIG` file variable with the content of our `.kube/config` file +- go to settings → access tokens to create a read-only registry token +- add variables `REGISTRY_USER` and `REGISTRY_PASSWORD` with that token +- push our repo (`git remote add gitlab ...` then `git push gitlab ...`) + +--- + +## Monitoring progress and troubleshooting + +- Click on "CI/CD" in the left bar to view pipelines + +- If you see a permission issue mentioning `system:serviceaccount:gitlab:...`: + + *make sure you did set `KUBECONFIG` correctly!* + +- GitLab will create namespaces named `gl--` + +- At the end of the deployment, the web UI will be available on some unique URL + + (`http://---gitlab.`) + +--- + +## Production + +- `git tag -f production && git push -f --tags` + +- Our CI/CD pipeline will deploy on the production URL + + (`http://--gitlab.`) + +- It will do it *only* if that same git commit was pushed to staging first + + (look in the pipeline configuration file to see how it's done!) + +--- + +## Let's talk about build + +- There are many ways to build container images on Kubernetes + +- ~~And they all suck~~ Many of them have inconveniencing issues + +- Let's do a quick review! + +--- + +## Docker-based approaches + +- Bind-mount the Docker socket + + - very easy, but requires Docker Engine + - build resource usage "evades" Kubernetes scheduler + - insecure + +- Docker-in-Docker in a pod + + - requires privileged pod + - insecure + - approaches like rootless or sysbox might help in the future + +- External build host + + - more secure + - requires resources outside of the Kubernetes cluster + +--- + +## Non-privileged builders + +- Kaniko + + - each build runs in its own containers or pod + - no caching by default + - registry-based caching is possible + +- BuildKit / `docker buildx` + + - can leverage Docker Engine or long-running Kubernetes worker pod + - supports distributed, multi-arch build farms + - basic caching out of the box + - can also leverage registry-based caching + +--- + +## Other approaches + +- Ditch the Dockerfile! + +- bazel + +- jib + +- ko + +- etc. + +--- + +## Discussion + +- Our CI/CD workflow is just *one* of the many possibilities + +- It would be nice to add some actual unit or e2e tests + +- Map the production namespace to a "real" domain name + +- Automatically remove older staging environments + + (see e.g. [kube-janitor](https://codeberg.org/hjacobs/kube-janitor)) + +- Deploy production to a separate cluster + +- Better segregate permissions + + (don't give `cluster-admin` to the GitLab pipeline) + +--- + +## Pros + +- GitLab is an amazing, open source, all-in-one platform + +- Available as hosted, community, or enterprise editions + +- Rich ecosystem, very customizable + +- Can run on Kubernetes, or somewhere else + +--- + +## Cons + +- It can be difficult to use components separately + + (e.g. use a different registry, or a different job runner) + +- More than one way to configure it + + (it's not an opinionated platform) + +- Not "Kubernetes-native" + + (for instance, jobs are not Kubernetes jobs) + +- Job latency could be improved + +*Note: most of these drawbacks are the flip side of the "pros" on the previous slide!* + +??? + +:EN:- CI/CD with GitLab +:FR:- CI/CD avec GitLab diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 17b93bcd..1f7a3c16 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -84,6 +84,7 @@ content: #- k8s/helm-create-better-chart.md #- k8s/helm-secrets.md #- k8s/exercise-helm.md + #- k8s/gitlab.md #- k8s/create-chart.md #- k8s/create-more-charts.md #- k8s/netpol.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index d725650b..661e8b27 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -88,6 +88,7 @@ content: - k8s/helm-create-better-chart.md - k8s/helm-secrets.md #- k8s/exercise-helm.md + - k8s/gitlab.md - - k8s/netpol.md - k8s/authn-authz.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 7b8b4de7..92f055c0 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -85,6 +85,7 @@ content: - k8s/helm-create-better-chart.md - k8s/helm-secrets.md #- k8s/exercise-helm.md + - k8s/gitlab.md - - k8s/netpol.md - k8s/authn-authz.md From f72cf16c82cae6639aceb49d5203fafe79c7beab Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 21 Feb 2021 16:29:49 +0100 Subject: [PATCH 34/73] =?UTF-8?q?=F0=9F=90=9E=20Fix=20Helm=20command=20in?= =?UTF-8?q?=20Prom=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/lib/commands.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh index cd965e11..91e5077e 100644 --- a/prepare-vms/lib/commands.sh +++ b/prepare-vms/lib/commands.sh @@ -721,7 +721,7 @@ _cmd_helmprom() { need_tag pssh " if i_am_first_node; then - sudo -u docker -H helm helm repo add prometheus-community https://prometheus-community.github.io/helm-charts/ + sudo -u docker -H helm repo add prometheus-community https://prometheus-community.github.io/helm-charts/ sudo -u docker -H helm install prometheus prometheus-community/prometheus \ --namespace kube-system \ --set server.service.type=NodePort \ From 23f7e8cff95f459a5453c753838df8ba8933774d Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 22 Feb 2021 21:35:02 +0100 Subject: [PATCH 35/73] =?UTF-8?q?=E2=86=94=EF=B8=8F=20Update=20DNS=20map?= =?UTF-8?q?=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/map-dns.py | 94 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/prepare-vms/map-dns.py b/prepare-vms/map-dns.py index 95fee748..1d25eee9 100755 --- a/prepare-vms/map-dns.py +++ b/prepare-vms/map-dns.py @@ -2,11 +2,11 @@ """ There are two ways to use this script: -1. Pass a tag name as a single argument. -It will then take the clusters corresponding to that tag, and assign one -domain name per cluster. Currently it gets the domains from a hard-coded -path. There should be more domains than clusters. -Example: ./map-dns.py 2020-08-15-jp +1. Pass a file name and a tag name as a single argument. +It will load a list of domains from the given file (one per line), +and assign them to the clusters corresponding to that tag. +There should be more domains than clusters. +Example: ./map-dns.py domains.txt 2020-08-15-jp 2. Pass a domain as the 1st argument, and IP addresses then. It will configure the domain with the listed IP addresses. @@ -19,55 +19,53 @@ import requests import sys import yaml -# configurable stuff -domains_file = "../../plentydomains/domains.txt" +# This can be tweaked if necessary. config_file = os.path.join( - os.environ["HOME"], ".config/gandi/config.yaml") -tag = None + os.environ["HOME"], ".config/gandi/config.yaml") apiurl = "https://dns.api.gandi.net/api/v5/domains" - -if len(sys.argv) == 2: - tag = sys.argv[1] - domains = open(domains_file).read().split() - domains = [ d for d in domains if not d.startswith('#') ] - ips = open(f"tags/{tag}/ips.txt").read().split() - settings_file = f"tags/{tag}/settings.yaml" - clustersize = yaml.safe_load(open(settings_file))["clustersize"] -else: - domains = [sys.argv[1]] - ips = sys.argv[2:] - clustersize = len(ips) - -# inferred stuff apikey = yaml.safe_load(open(config_file))["apirest"]["key"] -# now do the fucking work -while domains and ips: - domain = domains[0] - domains = domains[1:] - cluster = ips[:clustersize] - ips = ips[clustersize:] - print(f"{domain} => {cluster}") - zone = "" - node = 0 - for ip in cluster: - node += 1 - zone += f"@ 300 IN A {ip}\n" - zone += f"* 300 IN A {ip}\n" - zone += f"node{node} 300 IN A {ip}\n" - r = requests.put( - f"{apiurl}/{domain}/records", - headers={"x-api-key": apikey}, - data=zone) - print(r.text) +# Figure out if we're called for a bunch of domains, or just one. +first_arg = sys.argv[1] +if os.path.isfile(first_arg): + domains = open(first_arg).read().split() + domains = [ d for d in domains if not d.startswith('#') ] + tag = sys.argv[2] + ips = open(f"tags/{tag}/ips.txt").read().split() + settings_file = f"tags/{tag}/settings.yaml" + clustersize = yaml.safe_load(open(settings_file))["clustersize"] +else: + domains = [first_arg] + ips = sys.argv[2:] + clustersize = len(ips) - #r = requests.get( - # f"{apiurl}/{domain}/records", - # headers={"x-api-key": apikey}, - # ) +# Now, do the work. +while domains and ips: + domain = domains[0] + domains = domains[1:] + cluster = ips[:clustersize] + ips = ips[clustersize:] + print(f"{domain} => {cluster}") + zone = "" + node = 0 + for ip in cluster: + node += 1 + zone += f"@ 300 IN A {ip}\n" + zone += f"* 300 IN A {ip}\n" + zone += f"node{node} 300 IN A {ip}\n" + r = requests.put( + f"{apiurl}/{domain}/records", + headers={"x-api-key": apikey}, + data=zone) + print(r.text) + + #r = requests.get( + # f"{apiurl}/{domain}/records", + # headers={"x-api-key": apikey}, + # ) if domains: - print(f"Good, we have {len(domains)} domains left.") + print(f"Good, we have {len(domains)} domains left.") if ips: - print(f"Crap, we have {len(ips)} IP addresses left.") + print(f"Crap, we have {len(ips)} IP addresses left.") From ae17c2479cc62604c6ea33b66289bb13e8756ed6 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 22 Feb 2021 22:30:19 +0100 Subject: [PATCH 36/73] =?UTF-8?q?=F0=9F=93=8A=20Update=20Helm=20stable=20c?= =?UTF-8?q?hart=20and=20add=20deprecation=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/helm-intro.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/slides/k8s/helm-intro.md b/slides/k8s/helm-intro.md index 2db93989..6edd0b62 100644 --- a/slides/k8s/helm-intro.md +++ b/slides/k8s/helm-intro.md @@ -244,7 +244,7 @@ fine for personal and development clusters.) - Add the `stable` repo: ```bash - helm repo add stable https://kubernetes-charts.storage.googleapis.com/ + helm repo add stable https://charts.helm.sh/stable ``` ] @@ -255,6 +255,22 @@ It's OK to add a repo that already exists (it will merely update it). --- +class: extra-details + +## Deprecation warning + +- That "stable" is being deprecated, in favor of a more decentralized approach + + (each community / company / group / project hosting their own repository) + +- We're going to use it here for educational purposes + +- But if you're looking for production-grade charts, look elsewhere! + + (namely, on the Helm Hub) + +--- + ## Search available charts - We can search available charts with `helm search` From 2e6230a9a01c733128a2b5a7c813f6076dfbb67b Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 23 Feb 2021 21:44:57 +0100 Subject: [PATCH 37/73] =?UTF-8?q?=F0=9F=94=91=20Explain=20how=20to=20use?= =?UTF-8?q?=20imagePullSecrets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/secrets.md | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/slides/k8s/secrets.md b/slides/k8s/secrets.md index 53112061..772b1395 100644 --- a/slides/k8s/secrets.md +++ b/slides/k8s/secrets.md @@ -68,6 +68,128 @@ “Ah yes, this secret is a ...” +--- + +## Accessing private repositories + +- Let's see how to access an image on private registry! + +- These images are protected by a username + password + + (on some registries, it's token + password, but it's the same thing) + +- To access a private image, we need to: + + - create a secret + + - reference that secret in a Pod template + + - or reference that secret in a ServiceAccount used by a Pod + +--- + +## In practice + +- Let's try to access an image on a private registry! + + - image = docker-registry.enix.io/jpetazzo/private:latest + - user = reader + - password = VmQvqdtXFwXfyy4Jb5DR + +.exercise[ + +- Create a Deployment using that image: + ```bash + kubectl create deployment priv \ + --image=docker-registry.enix.io/jpetazzo/private + ``` + +- Check that the Pod won't start: + ```bash + kubectl get pods --selector=app=priv + ``` + +] + +--- + +## Creating a secret + +- Let's create a secret with the information provided earlier + +.exercise[ + +- Create the registry secret: + ```bash + kubectl create secret docker-registry enix \ + --docker-server=docker-registry.enix.io \ + --docker-username=reader \ + --docker-password=VmQvqdtXFwXfyy4Jb5DR + ``` + +] + +Why do we have to specify the registry address? + +If we use multiple sets of credentials for different registries, it prevents leaking the credentials of one registry to *another* registry. + +--- + +## Using the secret + +- The first way to use a secret is to add it to `imagePullSecrets` + + (in the `spec` section of a Pod template) + +.exercise[ + +- Patch the `priv` Deployment that we created earlier: + ```bash + kubectl patch deploy priv --patch=' + spec: + template: + spec: + imagePullSecrets: + - name: enix + ' + ``` + +] + +--- + +## Checking the results + +.exercise[ + +- Confirm that our Pod can now start correctly: + ```bash + kubectl get pods --selector=app=priv + ``` + +] + +--- + +## Another way to use the secret + +- We can add the secret to the ServiceAccount + +- This is convenient to automatically use credentials for *all* pods + + (as long as they're using a specific ServiceAccount, of course) + +.exercise[ + +- Add the secret to the ServiceAccount: + ```bash + kubectl patch serviceaccount default --patch=' + imagePullSecrets: + - name: enix + ' + ``` + +] --- From 2300d0719b1591b56de69ab36afe6d97366bb124 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 24 Feb 2021 14:20:09 +0100 Subject: [PATCH 38/73] =?UTF-8?q?=E2=9C=82=EF=B8=8F=20Remove=20ctr.run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/exercise-helm.md | 18 +++++------------- slides/k8s/shippingimages.md | 18 +++++------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/slides/k8s/exercise-helm.md b/slides/k8s/exercise-helm.md index b41e429f..c4f790e1 100644 --- a/slides/k8s/exercise-helm.md +++ b/slides/k8s/exercise-helm.md @@ -10,7 +10,7 @@ Level 2: make it so that the number of replicas can be set with `--set replicas= Level 3: change the colors of the lego bricks. -(For level 3, fork the repository and use ctr.run to build images.) +(For level 3, you'll have to build/push your own images.) See next slide if you need hints! @@ -44,20 +44,12 @@ Also add `replicas: 5` to `values.yaml` to provide a default value. ## Changing the color -- Fork the repository +- Create an account on e.g. Docker Hub (e.g. `janedoe`) -- Make sure that your fork has valid Dockerfiles - - (or identify a branch that has valid Dockerfiles) - -- Use the following images: - - ctr.run/yourgithubusername/wordsmith/db:branchname - - (replace db with web and words for the other components) +- Create an image repository (e.g. `janedoe/web`) - Change the images and/or CSS in `web/static` -- Commit, push, trigger a rolling update +- Build and push - (`imagePullPolicy` should be `Always`, which is the default) +- Trigger a rolling update using the image you just pushed diff --git a/slides/k8s/shippingimages.md b/slides/k8s/shippingimages.md index 37abef9d..5c35d016 100644 --- a/slides/k8s/shippingimages.md +++ b/slides/k8s/shippingimages.md @@ -94,28 +94,20 @@ ## Building on the fly -- Some services can build images on the fly from a repository +- Conceptually, it is possible to build images on the fly from a repository - Example: [ctr.run](https://ctr.run/) -.exercise[ + (deprecated in August 2020, after being aquired by Datadog) -- Use ctr.run to automatically build a container image and run it: +- It did allow something like this: ```bash docker run ctr.run/github.com/jpetazzo/container.training/dockercoins/hasher ``` - +- No alternative yet -] - -There might be a long pause before the first layer is pulled, -because the API behind `docker pull` doesn't allow to stream build logs, and there is no feedback during the build. - -It is possible to view the build logs by setting up an account on [ctr.run](https://ctr.run/). + (free startup idea, anyone?) ??? From 8597ca1956d93e4211dea25bd693a03ae998cd5e Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 24 Feb 2021 18:22:47 +0100 Subject: [PATCH 39/73] =?UTF-8?q?=F0=9F=94=A7=20Fix=20args=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/configuration.md | 40 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/slides/k8s/configuration.md b/slides/k8s/configuration.md index c37238d2..1effd876 100644 --- a/slides/k8s/configuration.md +++ b/slides/k8s/configuration.md @@ -60,21 +60,41 @@ ## Command-line arguments -- Pass options to `args` array in the container specification +- Indicate what should run in the container -- Example ([source](https://github.com/coreos/pods/blob/master/kubernetes.yaml#L29)): +- Pass `command` and/or `args` in the container options in a Pod's template + +- Both `command` and `args` are arrays + +- Example ([source](https://github.com/jpetazzo/container.training/blob/main/k8s/consul-1.yaml#L70)): ```yaml - args: - - "--data-dir=/var/lib/etcd" - - "--advertise-client-urls=http://127.0.0.1:2379" - - "--listen-client-urls=http://127.0.0.1:2379" - - "--listen-peer-urls=http://127.0.0.1:2380" - - "--name=etcd" + args: + - "agent" + - "-bootstrap-expect=3" + - "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NS)\"" + - "-client=0.0.0.0" + - "-data-dir=/consul/data" + - "-server" + - "-ui" ``` -- The options can be passed directly to the program that we run ... +--- - ... or to a wrapper script that will use them to e.g. generate a config file +## `args` or `command`? + +- Use `command` to override the `ENTRYPOINT` defined in the image + +- Use `args` to keep the `ENTRYPOINT` defined in the image + + (the parameters specified in `args` are added to the `ENTRYPOINT`) + +- In doubt, use `command` + +- It is also possible to use *both* `command` and `args` + + (they will be strung together, just like `ENTRYPOINT` and `CMD`) + +- See the [docs](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes) to see how they interact together --- From bc33f1f5df114257d0874d6e183b8f8e739bb80f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 24 Feb 2021 21:41:30 +0100 Subject: [PATCH 40/73] =?UTF-8?q?=F0=9F=92=BB=EF=B8=8F=20Update=20Scaleway?= =?UTF-8?q?=20deployment=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/infra/scaleway | 2 ++ prepare-vms/lib/infra/scaleway.sh | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/prepare-vms/infra/scaleway b/prepare-vms/infra/scaleway index 53f232e7..a39d7633 100644 --- a/prepare-vms/infra/scaleway +++ b/prepare-vms/infra/scaleway @@ -1 +1,3 @@ INFRACLASS=scaleway +#SCW_INSTANCE_TYPE=DEV1-L +#SCW_ZONE=fr-par-2 diff --git a/prepare-vms/lib/infra/scaleway.sh b/prepare-vms/lib/infra/scaleway.sh index 5311b7ec..71227118 100644 --- a/prepare-vms/lib/infra/scaleway.sh +++ b/prepare-vms/lib/infra/scaleway.sh @@ -5,6 +5,9 @@ if ! [ -f ~/.config/scw/config.yaml ]; then warn "~/.config/scw/config.yaml not found." fi +SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M} +SCW_ZONE=${SCW_ZONE-fr-par-1} + infra_list() { scw instance server list -o json | jq -r '.[] | [.id, .name, .state, .commercial_type] | @tsv' @@ -13,9 +16,6 @@ infra_list() { infra_start() { COUNT=$1 - SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M} - SCW_ZONE=${SCW_ZONE-fr-par-1} - for I in $(seq 1 $COUNT); do NAME=$(printf "%s-%03d" $TAG $I) sep "Starting instance $I/$COUNT" @@ -36,16 +36,16 @@ infra_stop() { scw_get_ids_by_tag $TAG | wc -l info "Deleting instances..." scw_get_ids_by_tag $TAG | - xargs -n1 -P10 -I@@ \ - scw instance server delete force-shutdown=true server-id=@@ + xargs -n1 -P10 \ + scw instance server delete zone=${SCW_ZONE} force-shutdown=true with-ip=true } scw_get_ids_by_tag() { TAG=$1 - scw instance server list name=$TAG -o json | jq -r .[].id + scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].id } scw_get_ips_by_tag() { TAG=$1 - scw instance server list name=$TAG -o json | jq -r .[].public_ip.address + scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].public_ip.address } From ce8dc2cdff04691d0a4df2e67a7ec8b3749dbf41 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 24 Feb 2021 22:35:25 +0100 Subject: [PATCH 41/73] =?UTF-8?q?=F0=9F=94=A7=20Minor=20tweaks=20and=20imp?= =?UTF-8?q?rovements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/aggregation-layer.md | 26 ++++++++++++++++--------- slides/k8s/hpa-v2.md | 34 ++++++++++++++++----------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/slides/k8s/aggregation-layer.md b/slides/k8s/aggregation-layer.md index 5efd7b1a..82df51c6 100644 --- a/slides/k8s/aggregation-layer.md +++ b/slides/k8s/aggregation-layer.md @@ -58,27 +58,24 @@ *probably aggregation layer* - --- ## How are resources organized? - Let's have a look at the Kubernetes API hierarchical structure -- Useful: `.metadata.selfLink` contains the URI of a resource +- We'll ask `kubectl` to show us the exacts requests that it's making .exercise[ -- Check the `apiVersion` and URI of a "core" resource, e.g. a Node: +- Check the URI for a cluster-scope, "core" resource, e.g. a Node: ```bash - kubectl get nodes -o json | jq .items[0].apiVersion - kubectl get nodes -o json | jq .items[0].metadata.selfLink + kubectl -v6 get node node1 ``` -- Get the `apiVersion` and URI for a "non-core" resource, e.g. a ClusterRole: +- Check the URI for a cluster-scope, "non-core" resource, e.g. a ClusterRole: ```bash - kubectl get clusterrole view -o json | jq .apiVersion - kubectl get clusterrole view -o json | jq .metadata.selfLink + kubectl -v6 get clusterrole view ``` ] @@ -123,6 +120,17 @@ class: extra-details ## Namespaced resources +- What about namespaced resources? + +.exercise[ + +- Check the URI for a namespaced, "core" resource, e.g. a Service: + ```bash + kubectl -v6 get service kubernetes --namespace default + ``` + +] + - Here are what namespaced resources URIs look like: ``` @@ -168,7 +176,7 @@ class: extra-details kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy PODNAME=$( kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy \ - -o json | jq .items[0].metadata.name) + -o json | jq -r .items[0].metadata.name) ``` - Execute a command in a pod, showing the API requests: diff --git a/slides/k8s/hpa-v2.md b/slides/k8s/hpa-v2.md index 32155a17..35dec98d 100644 --- a/slides/k8s/hpa-v2.md +++ b/slides/k8s/hpa-v2.md @@ -72,7 +72,7 @@ - Deploy DockerCoins, and scale up the `worker` Deployment: ```bash - kubectl apply -f ~/container.training/k8/dockercoins.yaml + kubectl apply -f ~/container.training/k8s/dockercoins.yaml kubectl scale deployment worker --replicas=10 ``` @@ -118,7 +118,7 @@ - Deploy `httplat`: ```bash - kubectl create deployment httplat -- httplat http://rng/ + kubectl create deployment httplat --image=jpetazzo/httplat -- httplat http://rng/ ``` - Expose it: @@ -512,20 +512,20 @@ no custom metrics API (custom.metrics.k8s.io) registered Here is the rule that we need to add to the configuration: ```yaml -- seriesQuery: | - httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""} - resources: - overrides: - kubernetes_namespace: - resource: namespace - kubernetes_name: - resource: service - name: - matches: "httplat_latency_seconds_sum" - as: "httplat_latency_seconds" - metricsQuery: | - rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m]) - /rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m]) + - seriesQuery: | + httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""} + resources: + overrides: + kubernetes_namespace: + resource: namespace + kubernetes_name: + resource: service + name: + matches: "httplat_latency_seconds_sum" + as: "httplat_latency_seconds" + metricsQuery: | + rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m]) + /rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m]) ``` (I built it following the [walkthrough](https://github.com/DirectXMan12/k8s-prometheus-adapter/blob/master/docs/config-walkthrough.md @@ -636,7 +636,7 @@ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 Check that our `httplat` metrics are available: ```bash kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1\ -/namespaces/coins/services/httplat/httplat_latency_seconds +/namespaces/customscaling/services/httplat/httplat_latency_seconds ``` Also check the logs of the `prometheus-adapter` and the `kube-controller-manager`. From fe84dec863b94480af95c4e2cea123735d0552c4 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sat, 27 Feb 2021 19:13:50 +0100 Subject: [PATCH 42/73] =?UTF-8?q?=F0=9F=94=91=20Add=20details=20about=20et?= =?UTF-8?q?cd=20security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/control-plane-auth.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/slides/k8s/control-plane-auth.md b/slides/k8s/control-plane-auth.md index aee2f651..ec95e612 100644 --- a/slides/k8s/control-plane-auth.md +++ b/slides/k8s/control-plane-auth.md @@ -92,6 +92,29 @@ --- +## etcd authorization + +- etcd supports RBAC, but Kubernetes doesn't use it by default + + (note: etcd RBAC is completely different from Kubernetes RBAC!) + +- By default, etcd access is "all or nothing" + + (if you have a valid certificate, you get in) + +- Be very careful if you use the same root CA for etcd and other things + + (if etcd trusts the root CA, then anyone with a valid cert gets full etcd access) + +- For more details, check the following resources: + + - [etcd documentation on authentication](https://etcd.io/docs/current/op-guide/authentication/) + + - [PKI The Wrong Way](https://www.youtube.com/watch?v=gcOLDEzsVHI) at KubeCon NA 2020 + +--- + + ## API server clients - The API server has a sophisticated authentication and authorization system From 249d446ef2a863ecd6e427cfb53dfa484b4343c5 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sat, 27 Feb 2021 19:48:38 +0100 Subject: [PATCH 43/73] =?UTF-8?q?=F0=9F=94=91=20Add=20Cilium=20and=20Tufin?= =?UTF-8?q?=20web=20tools=20to=20generate=20and=20view=20network=20policie?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/netpol.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/slides/k8s/netpol.md b/slides/k8s/netpol.md index f92148cf..0ee5de8c 100644 --- a/slides/k8s/netpol.md +++ b/slides/k8s/netpol.md @@ -427,26 +427,34 @@ troubleshoot easily, without having to poke holes in our firewall. --- -## Further resources +## Tools and resources -- As always, the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/network-policies/) is a good starting point +- [Cilium Network Policy Editor](https://editor.cilium.io/) -- The API documentation has a lot of detail about the format of various objects: +- [Tufin Network Policy Viewer](https://orca.tufin.io/netpol/) - - [NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicy-v1-networking-k8s-io) - - - [NetworkPolicySpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicyspec-v1-networking-k8s-io) - - - [NetworkPolicyIngressRule](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicyingressrule-v1-networking-k8s-io) - - - etc. - -- And two resources by [Ahmet Alp Balkan](https://ahmet.im/): +- Two resources by [Ahmet Alp Balkan](https://ahmet.im/): - a [very good talk about network policies](https://www.youtube.com/watch?list=PLj6h78yzYM2P-3-xqvmWaZbbI1sW-ulZb&v=3gGpMmYeEO8) at KubeCon North America 2017 - a repository of [ready-to-use recipes](https://github.com/ahmetb/kubernetes-network-policy-recipes) for network policies +--- + +## Documentation + +- As always, the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/network-policies/) is a good starting point + +- The API documentation has a lot of detail about the format of various objects: + + - [NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#networkpolicy-v1-networking-k8s-io) + + - [NetworkPolicySpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#networkpolicyspec-v1-networking-k8s-io) + + - [NetworkPolicyIngressRule](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#networkpolicyingressrule-v1-networking-k8s-io) + + - etc. + ??? :EN:- Isolating workloads with Network Policies From 8d20fa4654c1732ad9bb8ea5ef6c1a37165b09e8 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sat, 27 Feb 2021 19:52:07 +0100 Subject: [PATCH 44/73] =?UTF-8?q?=F0=9F=90=9E=20Fix=20missing=20resource?= =?UTF-8?q?=20name=20in=20Kyverno=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/kyverno.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/slides/k8s/kyverno.md b/slides/k8s/kyverno.md index 83403caa..06cad7b1 100644 --- a/slides/k8s/kyverno.md +++ b/slides/k8s/kyverno.md @@ -321,9 +321,9 @@ class: extra-details - Try to apply a few color labels: ```bash - kubectl label test-color-2 color=purple - kubectl label test-color-2 color=red - kubectl label test-color-2 color=blue --overwrite + kubectl label pod test-color-2 color=purple + kubectl label pod test-color-2 color=red + kubectl label pod test-color-2 color=blue --overwrite ``` ] @@ -432,9 +432,9 @@ class: extra-details - Try to apply a few color labels: ```bash - kubectl label test-color-3 color=purple - kubectl label test-color-3 color=red - kubectl label test-color-3 color- + kubectl label pod test-color-3 color=purple + kubectl label pod test-color-3 color=red + kubectl label pod test-color-3 color- ``` ] From f306749f683ec80e9af3f23bbfca93112d04d8b9 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 10 Mar 2021 19:45:23 +0100 Subject: [PATCH 45/73] =?UTF-8?q?=F0=9F=96=A8=EF=B8=8F=20Improve=20output?= =?UTF-8?q?=20in=20case=20no=20arg=20is=20provided?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/map-dns.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prepare-vms/map-dns.py b/prepare-vms/map-dns.py index 1d25eee9..58f54e1c 100755 --- a/prepare-vms/map-dns.py +++ b/prepare-vms/map-dns.py @@ -26,16 +26,16 @@ apiurl = "https://dns.api.gandi.net/api/v5/domains" apikey = yaml.safe_load(open(config_file))["apirest"]["key"] # Figure out if we're called for a bunch of domains, or just one. -first_arg = sys.argv[1] -if os.path.isfile(first_arg): - domains = open(first_arg).read().split() +domain_or_domain_file = sys.argv[1] +if os.path.isfile(domain_or_domain_file): + domains = open(domain_or_domain_file).read().split() domains = [ d for d in domains if not d.startswith('#') ] tag = sys.argv[2] ips = open(f"tags/{tag}/ips.txt").read().split() settings_file = f"tags/{tag}/settings.yaml" clustersize = yaml.safe_load(open(settings_file))["clustersize"] else: - domains = [first_arg] + domains = [domain_or_domain_file] ips = sys.argv[2:] clustersize = len(ips) From cb760dbe942dd14c1eb379bddb507a4dfdb2f3ef Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 11 Mar 2021 12:55:53 +0100 Subject: [PATCH 46/73] =?UTF-8?q?=E2=9C=8D=EF=B8=8F=20Add=20details=20abou?= =?UTF-8?q?t=20how=20to=20author=20YAML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/{dryrun.md => authoring-yaml.md} | 76 ++++++++++++++++++--- slides/kube-fullday.yml | 2 +- slides/kube-selfpaced.yml | 2 +- slides/kube-twodays.yml | 2 +- 4 files changed, 68 insertions(+), 14 deletions(-) rename slides/k8s/{dryrun.md => authoring-yaml.md} (72%) diff --git a/slides/k8s/dryrun.md b/slides/k8s/authoring-yaml.md similarity index 72% rename from slides/k8s/dryrun.md rename to slides/k8s/authoring-yaml.md index bdeb019d..dbb99f9a 100644 --- a/slides/k8s/dryrun.md +++ b/slides/k8s/authoring-yaml.md @@ -1,6 +1,6 @@ # Authoring YAML -- There are various ways to generate YAML with Kubernetes, e.g.: +- We have already generated YAML implicitly, with e.g.: - `kubectl run` @@ -32,26 +32,63 @@ --- -## We don't have to start from scratch +## Various ways to write YAML -- Create a resource (e.g. Deployment) +- Completely from scratch with our favorite editor -- Dump its YAML with `kubectl get -o yaml ...` + (yeah, right) -- Edit the YAML +- Dump an existing resource with `kubectl get -o yaml ...` -- Use `kubectl apply -f ...` with the YAML file to: + (it is recommended to clean up the result) - - update the resource (if it's the same kind) +- Ask `kubectl` to generate the YAML - - create a new resource (if it's a different kind) + (with a `kubectl create --dry-run -o yaml`) -- Or: Use The Docs, Luke +- Use The Docs, Luke (the documentation almost always has YAML examples) --- +## Generating YAML from scratch + +- Start with a namespace: + ```yaml + kind: Namespace + apiVersion: v1 + metadata: + name: hello + ``` + +- We can use `kubectl explain` to see resource definitions: + ```bash + kubectl explain -r pod.spec + ``` + +- Not the easiest option! + +--- + +## Dump the YAML for an existing resource + +- `kubectl get -o yaml` works! + +- A lot of fields in `metadata` are not necessary + + (`managedFields`, `resourceVersion`, `uid`, `creationTimestamp` ...) + +- Most objects will have a `status` field that is not necessary + +- Default or empty values can also be removed for clarity + +- This can be done manually or with the `kubectl-neat` plugin + + `kubectl get -o yaml ... | kubectl neat` + +--- + ## Generating YAML without creating resources - We can use the `--dry-run` option @@ -63,14 +100,18 @@ kubectl create deployment web --image nginx --dry-run ``` +- Optionally clean it up with `kubectl neat`, too + ] -- We can clean up that YAML even more if we want +Note: in recent versions of Kubernetes, we should use `--dry-run=client` - (for instance, we can remove the `creationTimestamp` and empty dicts) +(Or `--dry-run=server`; more on that later!) --- +class: extra-details + ## Using `--dry-run` with `kubectl apply` - The `--dry-run` option can also be used with `kubectl apply` @@ -87,6 +128,8 @@ --- +class: extra-details + ## The limits of `kubectl apply --dry-run` .exercise[ @@ -112,6 +155,8 @@ The resulting YAML doesn't represent a valid DaemonSet. --- +class: extra-details + ## 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/) @@ -135,6 +180,8 @@ Instead, it has the fields expected in a DaemonSet. --- +class: extra-details + ## Advantages of server-side dry run - The YAML is verified much more extensively @@ -149,6 +196,8 @@ Instead, it has the fields expected in a DaemonSet. --- +class: extra-details + ## `kubectl diff` - Kubernetes 1.13 also introduced `kubectl diff` @@ -209,3 +258,8 @@ Note: we don't need to specify `--validate=false` here. - check that it still works! - That YAML will be useful later when using e.g. Kustomize or Helm + +??? + +:EN:- Techniques to write YAML manifests +:FR:- Comment écrire des *manifests* YAML diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 1f7a3c16..a3c9ad5d 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -64,7 +64,7 @@ content: - k8s/scalingdockercoins.md - shared/hastyconclusions.md - k8s/daemonset.md - #- k8s/dryrun.md + #- k8s/authoring-yaml.md #- k8s/exercise-yaml.md #- k8s/localkubeconfig.md #- k8s/accessinternal.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 661e8b27..b615c6e5 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -65,7 +65,7 @@ content: - k8s/scalingdockercoins.md - shared/hastyconclusions.md - k8s/daemonset.md - - k8s/dryrun.md + - k8s/authoring-yaml.md #- k8s/exercise-yaml.md - - k8s/rollout.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 92f055c0..3b1f27b9 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -64,7 +64,7 @@ content: - k8s/scalingdockercoins.md - shared/hastyconclusions.md - k8s/daemonset.md - - k8s/dryrun.md + - k8s/authoring-yaml.md #- k8s/exercise-yaml.md - - k8s/localkubeconfig.md From 4be82f4f57411b21c578b0ec4ef550f53e8fd4e3 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 14 Mar 2021 19:21:43 +0100 Subject: [PATCH 47/73] =?UTF-8?q?=E2=9D=93=EF=B8=8F=20Add=20some=20quizzes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/0.yml | 2 ++ slides/k8s/accessinternal.md | 14 ++++++++++++++ slides/k8s/cert-manager.md | 2 ++ slides/k8s/helm-intro.md | 14 ++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 slides/0.yml diff --git a/slides/0.yml b/slides/0.yml new file mode 100644 index 00000000..36bd3a0e --- /dev/null +++ b/slides/0.yml @@ -0,0 +1,2 @@ +content: +- k8s/netpol.md diff --git a/slides/k8s/accessinternal.md b/slides/k8s/accessinternal.md index a647c38a..d70c882d 100644 --- a/slides/k8s/accessinternal.md +++ b/slides/k8s/accessinternal.md @@ -134,3 +134,17 @@ installed and set up `kubectl` to communicate with your cluster. :EN:- Securely accessing internal services :FR:- Accès sécurisé aux services internes + +:T: Accessing internal services from our local machine + +:Q: What's the advantage of "kubectl port-forward" compared to a NodePort? +:A: It can forward arbitrary protocols +:A: It doesn't require Kubernetes API credentials +:A: It offers deterministic load balancing (instead of random) +:A: ✔️It doesn't expose the service to the public + +:Q: What's the security concept behind "kubectl port-forward"? +:A: ✔️We authenticate with the Kubernetes API, and it forwards connections on our behalf +:A: It detects our source IP address, and only allows connections coming from it +:A: It uses end-to-end mTLS (mutual TLS) to authenticate our connections +:A: There is no security (as long as it's running, anyone can connect from anywhere) diff --git a/slides/k8s/cert-manager.md b/slides/k8s/cert-manager.md index dee9193f..b95949b3 100644 --- a/slides/k8s/cert-manager.md +++ b/slides/k8s/cert-manager.md @@ -242,3 +242,5 @@ class: extra-details :EN:- Obtaining certificates with cert-manager :FR:- Obtenir des certificats avec cert-manager + +:T: Obtaining TLS certificates with cert-manager diff --git a/slides/k8s/helm-intro.md b/slides/k8s/helm-intro.md index 6edd0b62..66385ab8 100644 --- a/slides/k8s/helm-intro.md +++ b/slides/k8s/helm-intro.md @@ -462,3 +462,17 @@ All unspecified values will take the default values defined in the chart. :FR:- Fonctionnement général de Helm :FR:- Installer des composants via Helm :FR:- Helm 2, Helm 3, et le *Helm Hub* + +:T: Getting started with Helm and its concepts + +:Q: Which comparison is the most adequate? +:A: Helm is a firewall, charts are access lists +:A: ✔️Helm is a package manager, charts are packages +:A: Helm is an artefact repository, charts are artefacts +:A: Helm is a CI/CD platform, charts are CI/CD pipelines + +:Q: What's required to distribute a Helm chart? +:A: A Helm commercial license +:A: A Docker registry +:A: An account on the Helm Hub +:A: ✔️An HTTP server From f72847bc81d1452201f12c16f88e744767f0cc43 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 14 Mar 2021 19:22:31 +0100 Subject: [PATCH 48/73] =?UTF-8?q?=E2=98=81=EF=B8=8F=20Add=20support=20for?= =?UTF-8?q?=20Linode=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/lib/commands.sh | 26 ++++++++++++--- prepare-vms/lib/infra/linode.sh | 58 +++++++++++++++++++++++++++++++++ prepare-vms/lib/pssh.sh | 10 +++--- 3 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 prepare-vms/lib/infra/linode.sh diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh index 91e5077e..822a9539 100644 --- a/prepare-vms/lib/commands.sh +++ b/prepare-vms/lib/commands.sh @@ -69,11 +69,14 @@ _cmd_deploy() { echo deploying > tags/$TAG/status sep "Deploying tag $TAG" - # Wait for cloudinit to be done + # If this VM image is using cloud-init, + # wait for cloud-init to be done pssh " - while [ ! -f /var/lib/cloud/instance/boot-finished ]; do - sleep 1 - done" + if [ -d /var/lib/cloud ]; then + while [ ! -f /var/lib/cloud/instance/boot-finished ]; do + sleep 1 + done + fi" # Special case for scaleway since it doesn't come with sudo if [ "$INFRACLASS" = "scaleway" ]; then @@ -102,6 +105,12 @@ _cmd_deploy() { sudo apt-get update && sudo apt-get install -y python-yaml" + # If there is no "python" binary, symlink to python3 + #pssh " + #if ! which python; then + # ln -s $(which python3) /usr/local/bin/python + #fi" + # Copy postprep.py to the remote machines, and execute it, feeding it the list of IP addresses pssh -I tee /tmp/postprep.py >/tmp/pp.out 2>>/tmp/pp.err" /tmp/token && diff --git a/prepare-vms/lib/infra/linode.sh b/prepare-vms/lib/infra/linode.sh new file mode 100644 index 00000000..8a7630d3 --- /dev/null +++ b/prepare-vms/lib/infra/linode.sh @@ -0,0 +1,58 @@ +if ! command -v linode-cli >/dev/null; then + warn "Linode CLI (linode-cli) not found." +fi +if ! [ -f ~/.config/linode-cli ]; then + warn "~/.config/linode-cli not found." +fi + +# To view available regions: "linode-cli regions list" +LINODE_REGION=${LINODE_REGION-us-west} + +# To view available types: "linode-cli linodes types" +LINODE_TYPE=${LINODE_TYPE-g6-standard-2} + +infra_list() { + linode-cli linodes list --json | + jq -r '.[] | [.id, .label, .status, .type] | @tsv' +} + +infra_start() { + COUNT=$1 + + for I in $(seq 1 $COUNT); do + NAME=$(printf "%s-%03d" $TAG $I) + sep "Starting instance $I/$COUNT" + info " Zone: $LINODE_REGION" + info " Name: $NAME" + info " Instance type: $LINODE_TYPE" + ROOT_PASS="$(base64 /dev/urandom | cut -c1-20 | head -n 1)" + linode-cli linodes create \ + --type=${LINODE_TYPE} --region=${LINODE_REGION} \ + --image=linode/ubuntu18.04 \ + --authorized_keys="${LINODE_SSHKEY}" \ + --root_pass="${ROOT_PASS}" \ + --tags=${TAG} --label=${NAME} + done + sep + + linode_get_ips_by_tag $TAG > tags/$TAG/ips.txt +} + +infra_stop() { + info "Counting instances..." + linode_get_ids_by_tag $TAG | wc -l + info "Deleting instances..." + linode_get_ids_by_tag $TAG | + xargs -n1 -P10 \ + linode-cli linodes delete +} + +linode_get_ids_by_tag() { + TAG=$1 + linode-cli linodes list --tags $TAG --json | jq -r ".[].id" +} + +linode_get_ips_by_tag() { + TAG=$1 + linode-cli linodes list --tags $TAG --json | jq -r ".[].ipv4[0]" +} diff --git a/prepare-vms/lib/pssh.sh b/prepare-vms/lib/pssh.sh index ca3bc639..fb855696 100644 --- a/prepare-vms/lib/pssh.sh +++ b/prepare-vms/lib/pssh.sh @@ -18,11 +18,11 @@ pssh() { echo "[parallel-ssh] $@" export PSSH=$(which pssh || which parallel-ssh) - if [ "$INFRACLASS" = hetzner ]; then - LOGIN=root - else - LOGIN=ubuntu - fi + case "$INFRACLASS" in + hetzner) LOGIN=root ;; + linode) LOGIN=root ;; + *) LOGIN=ubuntu ;; + esac $PSSH -h $HOSTFILE -l $LOGIN \ --par 100 \ From 055c8a72679dd7ad6ff2e1d58464fc6536555a69 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 17 Mar 2021 23:55:26 +0100 Subject: [PATCH 49/73] =?UTF-8?q?=F0=9F=93=83=20Minor=20slides=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/kubenet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides/k8s/kubenet.md b/slides/k8s/kubenet.md index 86bf2d44..abbc18ca 100644 --- a/slides/k8s/kubenet.md +++ b/slides/k8s/kubenet.md @@ -52,7 +52,7 @@ - There are literally dozens of implementations out there - (15 are listed in the Kubernetes documentation) + (https://github.com/containernetworking/cni/ lists more than 25 plugins) - Pods have level 3 (IP) connectivity, but *services* are level 4 (TCP or UDP) From 63fccb495fe365e4684b38ca39559bc3d3f51b45 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Thu, 18 Mar 2021 14:57:46 +0100 Subject: [PATCH 50/73] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Improve=20error=20re?= =?UTF-8?q?porting=20for=20missing=20content=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/markmaker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/slides/markmaker.py b/slides/markmaker.py index 5173cd93..7a5a0772 100755 --- a/slides/markmaker.py +++ b/slides/markmaker.py @@ -213,6 +213,7 @@ def processcontent(content, filename): return (content, titles) if os.path.isfile(content): return processcontent(open(content).read(), content) + logging.warning("Content spans only one line (it's probably a file name) but no file found: {}".format(content)) if isinstance(content, list): subparts = [processcontent(c, filename) for c in content] markdown = "\n---\n".join(c[0] for c in subparts) From edaef92b35ed624b5b0c052f8bdd80c580b29347 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 11:11:18 +0200 Subject: [PATCH 51/73] =?UTF-8?q?=F0=9F=9A=AB=20Remove=200.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/0.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 slides/0.yml diff --git a/slides/0.yml b/slides/0.yml deleted file mode 100644 index 36bd3a0e..00000000 --- a/slides/0.yml +++ /dev/null @@ -1,2 +0,0 @@ -content: -- k8s/netpol.md From 5acb05dfffa9dbbfde23c3e3cee9de293258ff17 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 11:12:50 +0200 Subject: [PATCH 52/73] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Add=20EKS=20prep=20s?= =?UTF-8?q?cripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-eks/10_create_cluster.sh | 10 +++++ prepare-eks/20_create_users.sh | 24 +++++++++++ prepare-eks/30_create_or_update_policy.sh | 22 ++++++++++ prepare-eks/40_attach_policy.sh | 8 ++++ prepare-eks/50_aws_auth.sh | 15 +++++++ prepare-eks/60_setup_rbac_and_ns.sh | 35 ++++++++++++++++ prepare-eks/99_cleanup_old_policy.sh | 7 ++++ prepare-eks/users.txt | 50 +++++++++++++++++++++++ 8 files changed, 171 insertions(+) create mode 100755 prepare-eks/10_create_cluster.sh create mode 100755 prepare-eks/20_create_users.sh create mode 100755 prepare-eks/30_create_or_update_policy.sh create mode 100755 prepare-eks/40_attach_policy.sh create mode 100755 prepare-eks/50_aws_auth.sh create mode 100755 prepare-eks/60_setup_rbac_and_ns.sh create mode 100755 prepare-eks/99_cleanup_old_policy.sh create mode 100644 prepare-eks/users.txt diff --git a/prepare-eks/10_create_cluster.sh b/prepare-eks/10_create_cluster.sh new file mode 100755 index 00000000..53cb81df --- /dev/null +++ b/prepare-eks/10_create_cluster.sh @@ -0,0 +1,10 @@ +#!/bin/sh +eksctl create cluster \ + --node-type=t3.large \ + --nodes-max=10 \ + --alb-ingress-access \ + --asg-access \ + --ssh-access \ + --with-oidc \ + # + diff --git a/prepare-eks/20_create_users.sh b/prepare-eks/20_create_users.sh new file mode 100755 index 00000000..5a2f5d80 --- /dev/null +++ b/prepare-eks/20_create_users.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +echo "Getting list of existing users ..." +aws iam list-users --output json | jq -r .Users[].UserName > users.tmp + +for U in $(cat users.txt); do + if ! grep -qw $U users.tmp; then + echo "Creating user $U..." + aws iam create-user --user-name=$U \ + --tags=Key=container.training,Value=1 + fi + if ! grep -qw $U users.keys; then + echo "Listing keys for user $U..." + KEYS=$(aws iam list-access-keys --user=$U | jq -r .AccessKeyMetadata[].AccessKeyId) + for KEY in $KEYS; do + echo "Deleting key $KEY for user $U..." + aws iam delete-access-key --user=$U --access-key-id=$KEY + done + echo "Creating access key for user $U..." + aws iam create-access-key --user=$U --output json \ + | jq -r '.AccessKey | [ .UserName, .AccessKeyId, .SecretAccessKey ] | @tsv' \ + >> users.keys + fi +done diff --git a/prepare-eks/30_create_or_update_policy.sh b/prepare-eks/30_create_or_update_policy.sh new file mode 100755 index 00000000..f5fc6ecd --- /dev/null +++ b/prepare-eks/30_create_or_update_policy.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +JSON='{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "eks:DescribeCluster" + ], + "Resource": "arn:aws:eks:*", + "Effect": "Allow" + } + ] +}' + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +#aws iam create-policy --policy-name user.container.training --policy-document "$JSON" +aws iam create-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --policy-document "$JSON" --set-as-default + +# Uncomment this to check which users have the policy +#aws iam list-entities-for-policy --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training diff --git a/prepare-eks/40_attach_policy.sh b/prepare-eks/40_attach_policy.sh new file mode 100755 index 00000000..569557b2 --- /dev/null +++ b/prepare-eks/40_attach_policy.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +for U in $(cat users.txt); do + echo "Attaching policy to user $U ..." + aws iam attach-user-policy --user-name $U --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training +done diff --git a/prepare-eks/50_aws_auth.sh b/prepare-eks/50_aws_auth.sh new file mode 100755 index 00000000..ba140a39 --- /dev/null +++ b/prepare-eks/50_aws_auth.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) + +rm -f users.map +for U in ada.lovelace also.lol; do +echo "\ +- userarn: arn:aws:iam::$ACCOUNT:user/$U + username: $U + groups: [ container.training ]\ +" >> users.map +done + +kubectl create --namespace=kube-system configmap aws-auth --dry-run=client --from-file=mapUsers=users.map -o yaml | kubectl apply -f- + diff --git a/prepare-eks/60_setup_rbac_and_ns.sh b/prepare-eks/60_setup_rbac_and_ns.sh new file mode 100755 index 00000000..9b87a169 --- /dev/null +++ b/prepare-eks/60_setup_rbac_and_ns.sh @@ -0,0 +1,35 @@ +#!/bin/sh +kubectl create rolebinding --namespace default container.training --group=container.training --clusterrole=view +kubectl create clusterrole view-nodes --verb=get,list,watch --resource=node +kubectl create clusterrolebinding view-nodes --group=container.training --clusterrole=view-nodes +kubectl create clusterrole view-namespaces --verb=get,list,watch --resource=namespace +kubectl create clusterrolebinding view-namespaces --group=container.training --clusterrole=view-namespaces + +kubectl create namespace container-training +kubectl create rolebinding --namespace container-training edit --group=container.training --clusterrole=edit + +for U in $(cat users.txt); do + NS=$(echo $U | tr . -) + cat < Date: Sat, 27 Mar 2021 19:05:42 +0100 Subject: [PATCH 53/73] Add a quick/dirty script to associate a role with the default service account in the default namespace granting r/o access to an s3 bucket --- bin/sa-can-read-s3.sh | 128 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100755 bin/sa-can-read-s3.sh diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh new file mode 100755 index 00000000..74499788 --- /dev/null +++ b/bin/sa-can-read-s3.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# I would like to demonstrate access to AWS resource (e.g. S3 bucket) from a pod. Idea: +# create a bucket, put two objects in it (one public, one private), then … I suppose I +# need to create a role with access to the private object, associate the role to a service +# account in k8s, find an image with the aws CLI (or some s3 client) in it … ? + +set -euo pipefail + +emit_describe_cluster_policy() { + # Not used right now, but this permission is required in order to run `aws eks update-kubeconfig`: + echo '{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "eks:DescribeCluster" + ], + "Resource": "'"arn:aws:eks:${REGION}:${ACCOUNT_ID}:cluster/${CLUSTER_NAME}"'", + "Effect": "Allow" + } + ] +}' +} + +create_describe_cluster_policy() { + local role_name="can-describe-cluster" + aws iam create-policy \ + --policy-name "${role_name}" \ + --description "Policy allowing to describe ${CLUSTER_NAME}" \ + --policy-document "$(emit_describe_cluster_policy)" + + aws iam attach-user-policy --role-name "${role_name}" --policy-arn "${S3_POLICY_ARN}" +} + +emit_service_account_role_trust_policy() { + local oidc_provider_arn key_prefix + oidc_provider_arn="$(aws iam list-open-id-connect-providers | jq -r '.OpenIDConnectProviderList[0].Arn')" + key_prefix="$(echo "${oidc_provider_arn}" | cut -f2- -d '/')" + + echo '{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "'"${oidc_provider_arn}"'" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "'"${key_prefix}:sub"'": "system:serviceaccount:default:default" + } + } + } + ] +}' +} + +associate_oidc_provider() { + local issuer_url + issuer_url="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.identity.oidc.issuer" --output text)" + if ! aws iam list-open-id-connect-providers | grep "${issuer_url}"; then + eksctl utils associate-iam-oidc-provider --cluster "${CLUSTER_NAME}" --approve + else + echo "OIDC provider already associated" + fi +} + +create_role() { + if ! _="$(aws iam get-role --role-name "${ROLE_NAME}")"; then + aws iam create-role --role-name "${ROLE_NAME}" --description "Role for service account" --assume-role-policy-document "$(emit_service_account_role_trust_policy)" + else + echo "Role ${ROLE_NAME} already exists" + fi +} + +annotate_serviceaccount() { + kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" +} + +checkit() { + kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true herro -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - +} + +update_kubeconfig() { + aws eks update-kubeconfig --name "${CLUSTER_NAME}" +} + +teardown() { + aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" + aws iam delete-role "${ROLE_NAME}" + aws iam delete-policy --policy-arn +} + +create_and_populate_bucket() { + if ! _="$(aws s3api get-bucket-acl --bucket "${BUCKET_NAME}")"; then + aws s3api create-bucket --region "${REGION}" --bucket "${BUCKET_NAME}" --create-bucket-configuration "LocationConstraint=${REGION}" + else + echo "Bucket ${BUCKET_NAME} already exists." + fi + f="$(mktemp)" + echo "THE UNICORN IS IN THE GARDEN!!" >"${f}" + aws s3api put-object --bucket "${BUCKET_NAME}" --key top-sekret.txt --body "${f}" +} + +ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" +S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess +CLUSTER_NAME=floral-mongoose-1616851817 +ROLE_NAME=service-account-role +REGION=eu-north-1 +BUCKET_NAME=wooga-booga-pants +export KUBECONFIG=myconfig + +main() { + if [ -n "${1:-}" ]; then + echo "An argument was provided, running that: $1" + "${1}" + else + echo "ACCOUNT_ID: $ACCOUNT_ID" + associate_oidc_provider + create_role + aws iam attach-role-policy --role-name "${ROLE_NAME}" --policy-arn "${S3_POLICY_ARN}" + annotate_serviceaccount + checkit + fi +} + +main "$@" From 6bdc687cc73f55c80dd67a94b1daaa90aee803af Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:12:30 +0100 Subject: [PATCH 54/73] Remove partial teardown command --- bin/sa-can-read-s3.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index 74499788..a6d001de 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -25,7 +25,7 @@ emit_describe_cluster_policy() { create_describe_cluster_policy() { local role_name="can-describe-cluster" aws iam create-policy \ - --policy-name "${role_name}" \ + --policy-name can-describe-cluster \ --description "Policy allowing to describe ${CLUSTER_NAME}" \ --policy-document "$(emit_describe_cluster_policy)" @@ -87,9 +87,9 @@ update_kubeconfig() { } teardown() { + # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" aws iam delete-role "${ROLE_NAME}" - aws iam delete-policy --policy-arn } create_and_populate_bucket() { From 305674fa3cb318c400cea911ee0aa7eb9b65b219 Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:13:34 +0100 Subject: [PATCH 55/73] Add --overwrite when annotating service account --- bin/sa-can-read-s3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index a6d001de..c6b02b66 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -75,7 +75,7 @@ create_role() { } annotate_serviceaccount() { - kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" + kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" --overwrite } checkit() { From 911d78aede5da47e4f6c4f26441911554cf115da Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sat, 27 Mar 2021 19:15:24 +0100 Subject: [PATCH 56/73] Rename test pod --- bin/sa-can-read-s3.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index c6b02b66..d7dc9666 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -79,7 +79,8 @@ annotate_serviceaccount() { } checkit() { - kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true herro -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - + echo "Will try to read s3://"${BUCKET_NAME}"/top-sekret.txt" + kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true can-we-read-s3 -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - } update_kubeconfig() { From 068c81bdcdef38bbe669fe01b7c2e4a76f39ef85 Mon Sep 17 00:00:00 2001 From: AJ Bowen Date: Sun, 28 Mar 2021 10:53:48 +0200 Subject: [PATCH 57/73] Fix incorrect bits in create_describe_cluster_policy --- bin/sa-can-read-s3.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh index d7dc9666..3f5b981d 100755 --- a/bin/sa-can-read-s3.sh +++ b/bin/sa-can-read-s3.sh @@ -23,13 +23,13 @@ emit_describe_cluster_policy() { } create_describe_cluster_policy() { - local role_name="can-describe-cluster" aws iam create-policy \ - --policy-name can-describe-cluster \ + --policy-name ${DESCRIBE_CLUSTER_POLICY_NAME} \ --description "Policy allowing to describe ${CLUSTER_NAME}" \ --policy-document "$(emit_describe_cluster_policy)" - aws iam attach-user-policy --role-name "${role_name}" --policy-arn "${S3_POLICY_ARN}" + # to attach: + # aws iam attach-user-policy --user-name "${user_name}" --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" } emit_service_account_role_trust_policy() { @@ -91,6 +91,9 @@ teardown() { # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" aws iam delete-role "${ROLE_NAME}" + # for username in users; do ... + # aws iam detach-user-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" --user-name "${username}" + aws iam delete-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" } create_and_populate_bucket() { @@ -107,6 +110,7 @@ create_and_populate_bucket() { ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess CLUSTER_NAME=floral-mongoose-1616851817 +DESCRIBE_CLUSTER_POLICY_NAME=can-describe-cluster ROLE_NAME=service-account-role REGION=eu-north-1 BUCKET_NAME=wooga-booga-pants From df1db67e530abe4e05aff5ccdb93f0e02510b907 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 12:59:54 +0200 Subject: [PATCH 58/73] =?UTF-8?q?=F0=9F=94=80=20Move=20@soulshake's=20scri?= =?UTF-8?q?pts=20and=20commands=20to=20prepare-eks=20directory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/sa-can-read-s3.sh | 133 ------------------------------------ prepare-eks/70_oidc.sh | 46 +++++++++++++ prepare-eks/80_s3_bucket.sh | 43 ++++++++++++ 3 files changed, 89 insertions(+), 133 deletions(-) delete mode 100755 bin/sa-can-read-s3.sh create mode 100755 prepare-eks/70_oidc.sh create mode 100755 prepare-eks/80_s3_bucket.sh diff --git a/bin/sa-can-read-s3.sh b/bin/sa-can-read-s3.sh deleted file mode 100755 index 3f5b981d..00000000 --- a/bin/sa-can-read-s3.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env bash -# I would like to demonstrate access to AWS resource (e.g. S3 bucket) from a pod. Idea: -# create a bucket, put two objects in it (one public, one private), then … I suppose I -# need to create a role with access to the private object, associate the role to a service -# account in k8s, find an image with the aws CLI (or some s3 client) in it … ? - -set -euo pipefail - -emit_describe_cluster_policy() { - # Not used right now, but this permission is required in order to run `aws eks update-kubeconfig`: - echo '{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "eks:DescribeCluster" - ], - "Resource": "'"arn:aws:eks:${REGION}:${ACCOUNT_ID}:cluster/${CLUSTER_NAME}"'", - "Effect": "Allow" - } - ] -}' -} - -create_describe_cluster_policy() { - aws iam create-policy \ - --policy-name ${DESCRIBE_CLUSTER_POLICY_NAME} \ - --description "Policy allowing to describe ${CLUSTER_NAME}" \ - --policy-document "$(emit_describe_cluster_policy)" - - # to attach: - # aws iam attach-user-policy --user-name "${user_name}" --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" -} - -emit_service_account_role_trust_policy() { - local oidc_provider_arn key_prefix - oidc_provider_arn="$(aws iam list-open-id-connect-providers | jq -r '.OpenIDConnectProviderList[0].Arn')" - key_prefix="$(echo "${oidc_provider_arn}" | cut -f2- -d '/')" - - echo '{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "'"${oidc_provider_arn}"'" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "'"${key_prefix}:sub"'": "system:serviceaccount:default:default" - } - } - } - ] -}' -} - -associate_oidc_provider() { - local issuer_url - issuer_url="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.identity.oidc.issuer" --output text)" - if ! aws iam list-open-id-connect-providers | grep "${issuer_url}"; then - eksctl utils associate-iam-oidc-provider --cluster "${CLUSTER_NAME}" --approve - else - echo "OIDC provider already associated" - fi -} - -create_role() { - if ! _="$(aws iam get-role --role-name "${ROLE_NAME}")"; then - aws iam create-role --role-name "${ROLE_NAME}" --description "Role for service account" --assume-role-policy-document "$(emit_service_account_role_trust_policy)" - else - echo "Role ${ROLE_NAME} already exists" - fi -} - -annotate_serviceaccount() { - kubectl annotate serviceaccounts default -n default "role-arn=arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" --overwrite -} - -checkit() { - echo "Will try to read s3://"${BUCKET_NAME}"/top-sekret.txt" - kubectl run --image amazon/aws-cli --attach --restart=Never --rm --wait=true can-we-read-s3 -- s3 cp s3://"${BUCKET_NAME}"/top-sekret.txt - -} - -update_kubeconfig() { - aws eks update-kubeconfig --name "${CLUSTER_NAME}" -} - -teardown() { - # see also 'can-describe-cluster' policy, if created via create_describe_cluster_policy - aws iam detach-role-policy --policy-arn "${S3_POLICY_ARN}" --role-name "${ROLE_NAME}" - aws iam delete-role "${ROLE_NAME}" - # for username in users; do ... - # aws iam detach-user-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" --user-name "${username}" - aws iam delete-policy --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/${DESCRIBE_CLUSTER_POLICY_NAME}" -} - -create_and_populate_bucket() { - if ! _="$(aws s3api get-bucket-acl --bucket "${BUCKET_NAME}")"; then - aws s3api create-bucket --region "${REGION}" --bucket "${BUCKET_NAME}" --create-bucket-configuration "LocationConstraint=${REGION}" - else - echo "Bucket ${BUCKET_NAME} already exists." - fi - f="$(mktemp)" - echo "THE UNICORN IS IN THE GARDEN!!" >"${f}" - aws s3api put-object --bucket "${BUCKET_NAME}" --key top-sekret.txt --body "${f}" -} - -ACCOUNT_ID="$(aws sts get-caller-identity | jq -r .Account)" -S3_POLICY_ARN=arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess -CLUSTER_NAME=floral-mongoose-1616851817 -DESCRIBE_CLUSTER_POLICY_NAME=can-describe-cluster -ROLE_NAME=service-account-role -REGION=eu-north-1 -BUCKET_NAME=wooga-booga-pants -export KUBECONFIG=myconfig - -main() { - if [ -n "${1:-}" ]; then - echo "An argument was provided, running that: $1" - "${1}" - else - echo "ACCOUNT_ID: $ACCOUNT_ID" - associate_oidc_provider - create_role - aws iam attach-role-policy --role-name "${ROLE_NAME}" --policy-arn "${S3_POLICY_ARN}" - annotate_serviceaccount - checkit - fi -} - -main "$@" diff --git a/prepare-eks/70_oidc.sh b/prepare-eks/70_oidc.sh new file mode 100755 index 00000000..f50eac5b --- /dev/null +++ b/prepare-eks/70_oidc.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# Note: if cluster was created without OIDC provider attached, +# you need to run the following command. It is idempotent. +#eksctl utils associate-iam-oidc-provider --cluster cluster-name-12341234 --approve + +if [ "$1" ]; then + CLUSTER="$1" +else + echo "Please indicate cluster to use. Available clusters:" + aws eks list-clusters --output table + exit 1 +fi + +ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) +OIDC=$(aws eks describe-cluster --name $CLUSTER --query cluster.identity.oidc.issuer --output text | cut -d/ -f3-) +ROLE_NAME=s3-reader-container-training +TRUST_POLICY=$(envsubst < Date: Sun, 28 Mar 2021 15:36:25 +0200 Subject: [PATCH 59/73] =?UTF-8?q?=F0=9F=93=83=20Document=20the=20EKS=20she?= =?UTF-8?q?ll=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-eks/10_create_cluster.sh | 3 ++ prepare-eks/20_create_users.sh | 8 +++++ prepare-eks/30_create_or_update_policy.sh | 39 ++++++++++++++++++--- prepare-eks/40_attach_policy.sh | 8 ++++- prepare-eks/50_aws_auth.sh | 15 ++++++-- prepare-eks/60_setup_rbac_and_ns.sh | 42 +++++++++++++++++++---- prepare-eks/70_oidc.sh | 38 +++++++++++++++++--- prepare-eks/80_s3_bucket.sh | 11 ++++++ prepare-eks/99_cleanup_old_policy.sh | 7 ---- 9 files changed, 145 insertions(+), 26 deletions(-) delete mode 100755 prepare-eks/99_cleanup_old_policy.sh diff --git a/prepare-eks/10_create_cluster.sh b/prepare-eks/10_create_cluster.sh index 53cb81df..e7dc26de 100755 --- a/prepare-eks/10_create_cluster.sh +++ b/prepare-eks/10_create_cluster.sh @@ -1,4 +1,7 @@ #!/bin/sh +# Create an EKS cluster. +# This is not idempotent (each time you run it, it creates a new cluster). + eksctl create cluster \ --node-type=t3.large \ --nodes-max=10 \ diff --git a/prepare-eks/20_create_users.sh b/prepare-eks/20_create_users.sh index 5a2f5d80..71a470bc 100755 --- a/prepare-eks/20_create_users.sh +++ b/prepare-eks/20_create_users.sh @@ -1,4 +1,12 @@ #!/bin/sh +# For each user listed in "users.txt", create an IAM user. +# Also create AWS API access keys, and store them in "users.keys". +# This is idempotent (you can run it multiple times, it will only +# create the missing users). However, it will not remove users. +# Note that you can remove users from "users.keys" (or even wipe +# that file out entirely) and then this script will delete their +# keys and generate new keys for them (and add the new keys to +# "users.keys".) echo "Getting list of existing users ..." aws iam list-users --output json | jq -r .Users[].UserName > users.tmp diff --git a/prepare-eks/30_create_or_update_policy.sh b/prepare-eks/30_create_or_update_policy.sh index f5fc6ecd..9c7d11bc 100755 --- a/prepare-eks/30_create_or_update_policy.sh +++ b/prepare-eks/30_create_or_update_policy.sh @@ -1,6 +1,16 @@ #!/bin/sh +# Create an IAM policy to authorize users to do "aws eks update-kubeconfig". +# This is idempotent, which allows to update the policy document below if +# you want the users to do other things as well. +# Note that each time you run this script, it will actually create a new +# version of the policy, set that version as the default version, and +# remove all non-default versions. (Because you can only have up to +# 5 versions of a given policy, so you need to clean them up.) +# After running that script, you will want to attach the policy to our +# users (check the other scripts in that directory). -JSON='{ +POLICY_NAME=user.container.training +POLICY_DOC='{ "Version": "2012-10-17", "Statement": [ { @@ -15,8 +25,27 @@ JSON='{ ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) -#aws iam create-policy --policy-name user.container.training --policy-document "$JSON" -aws iam create-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --policy-document "$JSON" --set-as-default +aws iam create-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --policy-document "$POLICY_DOC" \ + --set-as-default -# Uncomment this to check which users have the policy -#aws iam list-entities-for-policy --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training +# For reference, the command below creates a policy without versioning: +#aws iam create-policy \ +#--policy-name user.container.training \ +#--policy-document "$JSON" + +for VERSION in $( + aws iam list-policy-versions \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --query 'Versions[?!IsDefaultVersion].VersionId' \ + --output text) +do + aws iam delete-policy-version \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME \ + --version-id "$VERSION" +done + +# For reference, the command below shows all users using the policy: +#aws iam list-entities-for-policy \ +#--policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME diff --git a/prepare-eks/40_attach_policy.sh b/prepare-eks/40_attach_policy.sh index 569557b2..42483ecd 100755 --- a/prepare-eks/40_attach_policy.sh +++ b/prepare-eks/40_attach_policy.sh @@ -1,8 +1,14 @@ #!/bin/sh +# Attach our user policy to all the users defined in "users.txt". +# This should be idempotent, because attaching the same policy +# to the same user multiple times doesn't do anything. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) +POLICY_NAME=user.container.training for U in $(cat users.txt); do echo "Attaching policy to user $U ..." - aws iam attach-user-policy --user-name $U --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training + aws iam attach-user-policy \ + --user-name $U \ + --policy-arn arn:aws:iam::$ACCOUNT:policy/$POLICY_NAME done diff --git a/prepare-eks/50_aws_auth.sh b/prepare-eks/50_aws_auth.sh index ba140a39..f586a30e 100755 --- a/prepare-eks/50_aws_auth.sh +++ b/prepare-eks/50_aws_auth.sh @@ -1,9 +1,17 @@ #!/bin/sh +# Update the aws-auth ConfigMap to map our IAM users to Kubernetes users. +# Each user defined in "users.txt" will be mapped to a Kubernetes user +# with the same name, and put in the "container.training" group, too. +# This is idempotent. +# WARNING: this will wipe out the mapUsers component of the aws-auth +# ConfigMap, removing all users that aren't in "users.txt". +# It won't touch mapRoles, so it shouldn't break the role mappings +# put in place by EKS. ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) rm -f users.map -for U in ada.lovelace also.lol; do +for U in $(cat users.txt); do echo "\ - userarn: arn:aws:iam::$ACCOUNT:user/$U username: $U @@ -11,5 +19,6 @@ echo "\ " >> users.map done -kubectl create --namespace=kube-system configmap aws-auth --dry-run=client --from-file=mapUsers=users.map -o yaml | kubectl apply -f- - +kubectl create --namespace=kube-system configmap aws-auth \ + --dry-run=client --from-file=mapUsers=users.map -o yaml \ + | kubectl apply -f- diff --git a/prepare-eks/60_setup_rbac_and_ns.sh b/prepare-eks/60_setup_rbac_and_ns.sh index 9b87a169..3d52f2c9 100755 --- a/prepare-eks/60_setup_rbac_and_ns.sh +++ b/prepare-eks/60_setup_rbac_and_ns.sh @@ -1,13 +1,43 @@ #!/bin/sh -kubectl create rolebinding --namespace default container.training --group=container.training --clusterrole=view -kubectl create clusterrole view-nodes --verb=get,list,watch --resource=node -kubectl create clusterrolebinding view-nodes --group=container.training --clusterrole=view-nodes -kubectl create clusterrole view-namespaces --verb=get,list,watch --resource=namespace -kubectl create clusterrolebinding view-namespaces --group=container.training --clusterrole=view-namespaces +# Create a shared Kubernetes Namespace ("container-training") as well as +# individual namespaces for every user in "users.txt", and set up a bunch +# of permissions. +# Specifically: +# - each user gets "view" permissions in the "default" Namespace +# - each user gets "edit" permissions in the "container-training" Namespace +# - each user gets permissions to list Nodes and Namespaces +# - each user gets "admin" permissions in their personal Namespace +# Note that since Kubernetes Namespaces can't have dots in their names, +# if a user has dots, dots will be mapped to dashes. +# So user "ada.lovelace" will get namespace "ada-lovelace". +# This is kind of idempotent (but will raise a bunch of errors for objects +# that already exist). +# TODO: if this needs to evolve, replace all the "create" operations by +# "apply" operations. But this is good enough for now. + +kubectl create rolebinding --namespace default container.training \ + --group=container.training --clusterrole=view + +kubectl create clusterrole view-nodes \ + --verb=get,list,watch --resource=node +kubectl create clusterrolebinding view-nodes \ + --group=container.training --clusterrole=view-nodes + +kubectl create clusterrole view-namespaces \ + --verb=get,list,watch --resource=namespace +kubectl create clusterrolebinding view-namespaces \ + --group=container.training --clusterrole=view-namespaces kubectl create namespace container-training -kubectl create rolebinding --namespace container-training edit --group=container.training --clusterrole=edit +kubectl create rolebinding --namespace container-training edit \ + --group=container.training --clusterrole=edit +# Note: API calls to EKS tend to be fairly slow. To optimize things a bit, +# instead of running "kubectl" N times, we generate a bunch of YAML and +# apply it. It will still generate a lot of API calls but it's much faster +# than calling "kubectl" N times. It might be possible to make this even +# faster by generating a "kind: List" (I don't know if this would issue +# a single API calls or multiple ones; TBD!) for U in $(cat users.txt); do NS=$(echo $U | tr . -) cat < /tmp/policy.json +aws iam update-assume-role-policy \ + --role-name $ROLE_NAME \ + --policy-document file:///tmp/policy.json diff --git a/prepare-eks/80_s3_bucket.sh b/prepare-eks/80_s3_bucket.sh index a67c9de6..66539250 100755 --- a/prepare-eks/80_s3_bucket.sh +++ b/prepare-eks/80_s3_bucket.sh @@ -1,4 +1,15 @@ #!/bin/sh +# Create an S3 bucket with two objects in it: +# - public.txt (world-readable) +# - private.txt (private) +# Also create an IAM policy granting read-only access to the bucket +# (and therefore, to the private object). +# Finally, attach the policy to an IAM role (for instance, the role +# created by another script in this directory). +# This isn't idempotent, but it can be made idempotent by replacing the +# "aws iam create-policy" call with "aws iam create-policy-version" and +# a bit of extra elbow grease. (See other scripts in this directory for +# an example). ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) BUCKET=container.training diff --git a/prepare-eks/99_cleanup_old_policy.sh b/prepare-eks/99_cleanup_old_policy.sh deleted file mode 100755 index cb562f65..00000000 --- a/prepare-eks/99_cleanup_old_policy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -ACCOUNT=$(aws sts get-caller-identity | jq -r .Account) - -for VERSION in $(aws iam list-policy-versions --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training | jq -r '.Versions[].VersionId'); do - aws iam delete-policy-version --policy-arn arn:aws:iam::$ACCOUNT:policy/user.container.training --version-id "$VERSION" -done From 629b4d1037214dc1bd8a2b0f2b412650880aa63b Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 18:28:38 +0200 Subject: [PATCH 60/73] =?UTF-8?q?=F0=9F=92=AC=20Add=20Slack=20chat=20room?= =?UTF-8?q?=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/intro-fullday.yml | 1 + slides/intro-selfpaced.yml | 1 + slides/intro-twodays.yml | 1 + slides/kadm-fullday.yml | 1 + slides/kadm-twodays.yml | 1 + slides/kube-adv.yml | 1 + slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + slides/shared/chat-room-slack.md | 12 ++++++++++++ slides/swarm-fullday.yml | 1 + slides/swarm-halfday.yml | 1 + slides/swarm-selfpaced.yml | 1 + 14 files changed, 25 insertions(+) create mode 100644 slides/shared/chat-room-slack.md diff --git a/slides/intro-fullday.yml b/slides/intro-fullday.yml index c70eb8d4..0473f9d0 100644 --- a/slides/intro-fullday.yml +++ b/slides/intro-fullday.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/intro-selfpaced.yml b/slides/intro-selfpaced.yml index 96c2b837..021f9c4d 100644 --- a/slides/intro-selfpaced.yml +++ b/slides/intro-selfpaced.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/intro-twodays.yml b/slides/intro-twodays.yml index e0a15e33..2f0dd6e6 100644 --- a/slides/intro-twodays.yml +++ b/slides/intro-twodays.yml @@ -20,6 +20,7 @@ content: - containers/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kadm-fullday.yml b/slides/kadm-fullday.yml index 4fb62c4a..e40d6d27 100644 --- a/slides/kadm-fullday.yml +++ b/slides/kadm-fullday.yml @@ -22,6 +22,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kadm-twodays.yml b/slides/kadm-twodays.yml index 679ca54f..dea0f87d 100644 --- a/slides/kadm-twodays.yml +++ b/slides/kadm-twodays.yml @@ -22,6 +22,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-adv.yml b/slides/kube-adv.yml index 87cbf454..297c7cf0 100644 --- a/slides/kube-adv.yml +++ b/slides/kube-adv.yml @@ -20,6 +20,7 @@ content: - k8s/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index a3c9ad5d..0f2877ae 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 42da801c..2caae94c 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -23,6 +23,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index b615c6e5..542998ef 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 3b1f27b9..37e622f1 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -21,6 +21,7 @@ content: - k8s/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/shared/chat-room-slack.md b/slides/shared/chat-room-slack.md new file mode 100644 index 00000000..14a7e5b3 --- /dev/null +++ b/slides/shared/chat-room-slack.md @@ -0,0 +1,12 @@ +## Chat room + +- A Slack room has been set up for the duration of the training + +- We'll use it to ask questions, get help, share feedback ... + + (let's keep an eye on it during the training!) + +- Reminder, the room is @@CHAT@@ + +- Say hi in the chat room! + diff --git a/slides/swarm-fullday.yml b/slides/swarm-fullday.yml index 76800ca6..ec3af144 100644 --- a/slides/swarm-fullday.yml +++ b/slides/swarm-fullday.yml @@ -25,6 +25,7 @@ content: - swarm/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/swarm-halfday.yml b/slides/swarm-halfday.yml index a1c1a086..b4cd036b 100644 --- a/slides/swarm-halfday.yml +++ b/slides/swarm-halfday.yml @@ -25,6 +25,7 @@ content: - swarm/intro.md - shared/about-slides.md - shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md diff --git a/slides/swarm-selfpaced.yml b/slides/swarm-selfpaced.yml index 43ad7b44..1fbe718d 100644 --- a/slides/swarm-selfpaced.yml +++ b/slides/swarm-selfpaced.yml @@ -20,6 +20,7 @@ content: - swarm/intro.md - shared/about-slides.md #- shared/chat-room-im.md +#- shared/chat-room-slack.md #- shared/chat-room-zoom-meeting.md #- shared/chat-room-zoom-webinar.md - shared/toc.md From 858afc846c42e315c707fd4edb16b34e92644b30 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sun, 28 Mar 2021 20:08:58 +0200 Subject: [PATCH 61/73] =?UTF-8?q?=F0=9F=9A=AA=20Instructions=20to=20access?= =?UTF-8?q?=20EKS=20cluster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/access-eks-cluster.md | 104 +++++++++++++++++++++++++++++++ slides/kube-fullday.yml | 1 + slides/kube-halfday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 5 files changed, 108 insertions(+) create mode 100644 slides/k8s/access-eks-cluster.md diff --git a/slides/k8s/access-eks-cluster.md b/slides/k8s/access-eks-cluster.md new file mode 100644 index 00000000..6bd0d845 --- /dev/null +++ b/slides/k8s/access-eks-cluster.md @@ -0,0 +1,104 @@ +## Accessing our EKS cluster + +- We also have a shared EKS cluster + +- With individual IAM users + +- Let's connect to this cluster! + +--- + +## What we need + +- `kubectl` (obviously!) + +- `aws` CLI (recent-ish version) + + (or `aws` CLI + `aws-iam-authenticator` plugin) + +- AWS API access key and secret access key + +- AWS region + +- EKS cluster name + +--- + +## Setting up AWS credentials + +- There are many ways to do this + +- We're going to use environment variables + +- You're welcome to use whatever you like (e.g. AWS profiles) + +.exercise[ + +- Set the AWS region, API access key, and secret key: + ```bash + export AWS_DEFAULT_REGION=`us-east-2` + export AWS_ACCESS_KEY_ID=`AKI...` + export AWS_SECRET_ACCESS_KEY=`xyz123...` + ``` + +- Check that the AWS API recognizes us: + ```bash + aws sts get-caller-identity + ``` + +] + +--- + +## Updating our kubeconfig file + +- Now we can use the AWS CLI to: + + - obtain the Kubernetes API address + + - register it in our kubeconfig file + +.exercise[ + +- Update our kubeconfig file: + ```bash + aws eks update-kubeconfig --name `fancy-clustername-1234` + ``` + +- Run some harmless command: + ```bash + kubectl version + ``` + +] + +--- + +## Our resources + +- We have the following permissions: + + - `view` in the `default` namespace + + - `edit` in the `container-training` namespace + + - `admin` in our personal namespace + +- Our personal namespace is our IAM user name + + (but with dots replaced with dashes) + +- For instance, user `ada.lovelace` has namespace `ada-lovelace` + +--- + +## Deploying things + +- Let's deploy DockerCoins in our personal namespace! + +- Expose the Web UI with a `LoadBalancer` service + +??? + +:EN:- Working with an EKS cluster +:FR:- Travailler avec un cluster EKS diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 0f2877ae..c43169f4 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -68,6 +68,7 @@ content: #- k8s/authoring-yaml.md #- k8s/exercise-yaml.md #- k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md #- k8s/accessinternal.md #- k8s/kubectlproxy.md - k8s/rollout.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 2caae94c..3bd0e430 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -56,6 +56,7 @@ content: - k8s/buildshiprun-dockerhub.md - k8s/ourapponkube.md #- k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md #- k8s/accessinternal.md #- k8s/kubectlproxy.md - - k8s/dashboard.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index 542998ef..aa533663 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -76,6 +76,7 @@ content: - - k8s/namespaces.md - k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md - k8s/accessinternal.md - k8s/kubectlproxy.md - diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 37e622f1..bb4272f9 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -69,6 +69,7 @@ content: #- k8s/exercise-yaml.md - - k8s/localkubeconfig.md + #- k8s/access-eks-cluster.md - k8s/accessinternal.md #- k8s/kubectlproxy.md - k8s/rollout.md From cfcf874bac382375f936fc2c47240f8bc0a6ee9c Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Tue, 30 Mar 2021 18:09:24 +0200 Subject: [PATCH 62/73] =?UTF-8?q?=F0=9F=93=83=20Update=20section=20summari?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/containers/Compose_For_Dev_Stacks.md | 2 +- slides/containers/Container_Network_Model.md | 12 ++++++++++++ slides/containers/Container_Networking_Basics.md | 9 ++------- slides/containers/Copying_Files_During_Build.md | 2 +- slides/containers/Dockerfile_Tips.md | 9 ++++++++- slides/containers/Network_Drivers.md | 9 +++++++++ 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/slides/containers/Compose_For_Dev_Stacks.md b/slides/containers/Compose_For_Dev_Stacks.md index 4f660881..d978c6f5 100644 --- a/slides/containers/Compose_For_Dev_Stacks.md +++ b/slides/containers/Compose_For_Dev_Stacks.md @@ -329,4 +329,4 @@ This is ideal to debug regressions, do side-by-side comparisons, etc. :EN:- Connecting services together with a *Compose file* :FR:- Utiliser Compose pour décrire son environnement -:FR:- Écrire un *Compose file* pour connecter les services entre eux \ No newline at end of file +:FR:- Écrire un *Compose file* pour connecter les services entre eux diff --git a/slides/containers/Container_Network_Model.md b/slides/containers/Container_Network_Model.md index e39349b8..bbe4c45a 100644 --- a/slides/containers/Container_Network_Model.md +++ b/slides/containers/Container_Network_Model.md @@ -742,3 +742,15 @@ class: extra-details * This may be used to access an internal package repository. (But try to use a multi-stage build instead, if possible!) + +??? + +:EN:Container networking essentials +:EN:- The Container Network Model +:EN:- Container isolation +:EN:- Service discovery + +:FR:Mettre ses conteneurs en réseau +:FR:- Le "Container Network Model" +:FR:- Isolation des conteneurs +:FR:- *Service discovery* diff --git a/slides/containers/Container_Networking_Basics.md b/slides/containers/Container_Networking_Basics.md index 4ce0fc49..23dc8eb9 100644 --- a/slides/containers/Container_Networking_Basics.md +++ b/slides/containers/Container_Networking_Basics.md @@ -229,10 +229,5 @@ containers together without exposing their ports. ??? -:EN:Connecting containers -:EN:- Container networking basics -:EN:- Exposing a container - -:FR:Connecter les conteneurs -:FR:- Description du modèle réseau des conteneurs -:FR:- Exposer un conteneur +:EN:- Exposing single containers +:FR:- Exposer un conteneur isolé diff --git a/slides/containers/Copying_Files_During_Build.md b/slides/containers/Copying_Files_During_Build.md index 2d58d287..a57386df 100644 --- a/slides/containers/Copying_Files_During_Build.md +++ b/slides/containers/Copying_Files_During_Build.md @@ -101,5 +101,5 @@ Success! ??? -:EN:- The build cache +:EN:- Leveraging the build cache for faster builds :FR:- Tirer parti du cache afin d'optimiser la vitesse de *build* diff --git a/slides/containers/Dockerfile_Tips.md b/slides/containers/Dockerfile_Tips.md index 485df970..6b402906 100644 --- a/slides/containers/Dockerfile_Tips.md +++ b/slides/containers/Dockerfile_Tips.md @@ -434,5 +434,12 @@ services: ??? +:EN:Optimizing images :EN:- Dockerfile tips, tricks, and best practices -:FR:- Bonnes pratiques pour la construction des images +:EN:- Reducing build time +:EN:- Reducing image size + +:FR:Optimiser ses images +:FR:- Bonnes pratiques, trucs et astuces +:FR:- Réduire le temps de build +:FR:- Réduire la taille des images diff --git a/slides/containers/Network_Drivers.md b/slides/containers/Network_Drivers.md index 3d487370..216579b5 100644 --- a/slides/containers/Network_Drivers.md +++ b/slides/containers/Network_Drivers.md @@ -82,3 +82,12 @@ Use cases: * Those containers can communicate over their `lo` interface.
(i.e. one can bind to 127.0.0.1 and the others can connect to it.) +??? + +:EN:Advanced container networking +:EN:- Transparent network access with the "host" driver +:EN:- Sharing is caring with the "container" driver + +:FR:Paramétrage réseau avancé +:FR:- Accès transparent au réseau avec le mode "host" +:FR:- Partage de la pile réseau avece le mode "container" From 67806fc59291102cc9b9fda54718db66a8e704d7 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 7 Apr 2021 15:59:16 +0200 Subject: [PATCH 63/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Add=20a=20bunch=20of?= =?UTF-8?q?=20control=20plane=20diagrams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advanced-control-plane-split-events.svg | 3921 ++++++++++++++++ .../control-planes/advanced-control-plane.svg | 3596 +++++++++++++++ .../control-planes/managed-kubernetes.svg | 1294 ++++++ .../single-control-and-workers.svg | 1611 +++++++ .../images/control-planes/single-node-dev.svg | 914 ++++ .../control-planes/stacked-control-plane.svg | 3940 +++++++++++++++++ slides/k8s/concepts-k8s.md | 30 + slides/workshop.css | 9 + 8 files changed, 15315 insertions(+) create mode 100644 slides/images/control-planes/advanced-control-plane-split-events.svg create mode 100644 slides/images/control-planes/advanced-control-plane.svg create mode 100644 slides/images/control-planes/managed-kubernetes.svg create mode 100644 slides/images/control-planes/single-control-and-workers.svg create mode 100644 slides/images/control-planes/single-node-dev.svg create mode 100644 slides/images/control-planes/stacked-control-plane.svg diff --git a/slides/images/control-planes/advanced-control-plane-split-events.svg b/slides/images/control-planes/advanced-control-plane-split-events.svg new file mode 100644 index 00000000..ae30df7d --- /dev/null +++ b/slides/images/control-planes/advanced-control-plane-split-events.svg @@ -0,0 +1,3921 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADVANCED CONTROL PLANE + WORKERS(SPLIT EVENTS) + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + passive + + + + active + + + + + + + + + + + + + + + + + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + app pod + + + + some combination of VMs,containers, pods ... + APIload balancer + + + + API server + + API server + + API server + + API server + + API server + + API server + + scheduler + + scheduler + + + + controller manager + + controller manager + + + + + + + + etcd + + + + + + + + + + etcd + + + + etcd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + + + + etcd (events) + + + + + + + + + + etcd (events) + + + + etcd (events) + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + + diff --git a/slides/images/control-planes/advanced-control-plane.svg b/slides/images/control-planes/advanced-control-plane.svg new file mode 100644 index 00000000..84ff5350 --- /dev/null +++ b/slides/images/control-planes/advanced-control-plane.svg @@ -0,0 +1,3596 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADVANCED CONTROL PLANE + WORKERS + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + passive + + + + active + + + + + + + + + + + + + + + + + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + app pod + + + + some combination of VMs,containers, pods ... + APIload balancer + + + + API server + + API server + + API server + + API server + + API server + + API server + + scheduler + + scheduler + + + + controller manager + + controller manager + + + + + + + + etcd + + + + + + + + + + etcd + + + + etcd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + load balancer,DNS RR, etcdserver list ... + diff --git a/slides/images/control-planes/managed-kubernetes.svg b/slides/images/control-planes/managed-kubernetes.svg new file mode 100644 index 00000000..30a02615 --- /dev/null +++ b/slides/images/control-planes/managed-kubernetes.svg @@ -0,0 +1,1294 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MANAGED KUBERNETES + + + + controllermanager + + + + scheduler + + control plane(operated by provider) + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + + + + + + + + + + + + + pod + + + + kubelet + + + + kubelet + + + + + + pod + + + + + + pod + + + + + + pod + + + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + + ? + API server + + + + + + cloudcontrollermanager + etcd + + + diff --git a/slides/images/control-planes/single-control-and-workers.svg b/slides/images/control-planes/single-control-and-workers.svg new file mode 100644 index 00000000..3bec2e53 --- /dev/null +++ b/slides/images/control-planes/single-control-and-workers.svg @@ -0,0 +1,1611 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SINGLE-NODE CONTROL PLANE + WORKERS(DEPLOYED WITH KUBEADM) + + + + + + + + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + kubelet + + container engine + + + + + + + + + API server + + + + kubelet + + container engine + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + API server + + + diff --git a/slides/images/control-planes/single-node-dev.svg b/slides/images/control-planes/single-node-dev.svg new file mode 100644 index 00000000..828de926 --- /dev/null +++ b/slides/images/control-planes/single-node-dev.svg @@ -0,0 +1,914 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SINGLE-NODE CLUSTER (FOR DEVELOPMENT) + + + API server + + + + controllermanager + + + + scheduler + + VM or container + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + etcd + + + + + + + + + + kubelet + + + + + + pod + + + + + containerengine + + + + + + + + + + pod + + + + + + pod + + + diff --git a/slides/images/control-planes/stacked-control-plane.svg b/slides/images/control-planes/stacked-control-plane.svg new file mode 100644 index 00000000..432209b2 --- /dev/null +++ b/slides/images/control-planes/stacked-control-plane.svg @@ -0,0 +1,3940 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + STACKED CONTROL PLANE + WORKERS(DEPLOYED WITH KUBEADM) + + + + + + + + + + + + kubelet + + container engine + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + other pods... + + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + passive + + + + + + + + + other pods... + + + + + + + + + active + + + + + + + + + + + + + + + + + + + + + + APIload balancer + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + kubelet + + container engine + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + app pod + + + + + diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md index f8d3a0db..4fd7a3fe 100644 --- a/slides/k8s/concepts-k8s.md +++ b/slides/k8s/concepts-k8s.md @@ -220,6 +220,36 @@ class: extra-details --- +class: pic +![](images/control-planes/single-node-dev.svg) + +--- + +class: pic +![](images/control-planes/managed-kubernetes.svg) + +--- + +class: pic +![](images/control-planes/single-control-and-workers.svg) + +--- + +class: pic +![](images/control-planes/stacked-control-plane.svg) + +--- + +class: pic +![](images/control-planes/advanced-control-plane.svg) + +--- + +class: pic +![](images/control-planes/advanced-control-plane-split-events.svg) + +--- + class: extra-details ## How many nodes should a cluster have? diff --git a/slides/workshop.css b/slides/workshop.css index f3047404..f64fdec0 100644 --- a/slides/workshop.css +++ b/slides/workshop.css @@ -109,8 +109,17 @@ div.pic p { div.pic img { display: block; margin: auto; + /* + "pic" class slides should have a single, full screen picture. + We used to have these attributes below but they prevented + pictures from taking up the whole slide. Replacing them with + 100%/100% seems to put the pictures full screen, but I've left + these old attributes here just in case. max-width: 1210px; max-height: 550px; + */ + max-width: 100%; + max-height: 100%; } div.pic h1, div.pic h2, div.title h1, div.title h2 { text-align: center; From a484425c818b42d74426d4864de17fd5f48b8e63 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Wed, 7 Apr 2021 16:52:36 +0200 Subject: [PATCH 64/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Add=20non-dedicated?= =?UTF-8?q?=20control=20plane?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks @zempashi for the suggestion 👍🏻 --- .../non-dedicated-stacked-nodes.svg | 3132 +++++++++++++++++ slides/k8s/concepts-k8s.md | 5 + 2 files changed, 3137 insertions(+) create mode 100644 slides/images/control-planes/non-dedicated-stacked-nodes.svg diff --git a/slides/images/control-planes/non-dedicated-stacked-nodes.svg b/slides/images/control-planes/non-dedicated-stacked-nodes.svg new file mode 100644 index 00000000..f0bd1033 --- /dev/null +++ b/slides/images/control-planes/non-dedicated-stacked-nodes.svg @@ -0,0 +1,3132 @@ + + + + + + image/svg+xml + + how-does-k8s-work + + + + + how-does-k8s-work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CONTROL PLANE AND APPS RUNNING ON THE SAME NODES(SHOWN HERE WITH STACKED CONTROL PLANE) + + + + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + + + + + + + + + + + + + APIload balancer + + + + + + + + + + + passive + + + + + + + + + active + + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + + + + + + + API server + + + + kubelet + + container engine + + + + + + + + + + + + + + etcd + + + + + + + + + controller manager + + + + + + + + + scheduler + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + ... + + + + + + + + + + + 👩🏼‍💻👨🏾‍💻🤖 + + $ kubectl ... + + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + + + + + + + + app pod + + diff --git a/slides/k8s/concepts-k8s.md b/slides/k8s/concepts-k8s.md index 4fd7a3fe..adfc0336 100644 --- a/slides/k8s/concepts-k8s.md +++ b/slides/k8s/concepts-k8s.md @@ -240,6 +240,11 @@ class: pic --- +class: pic +![](images/control-planes/non-dedicated-stacked-nodes.svg) + +--- + class: pic ![](images/control-planes/advanced-control-plane.svg) From f3eb9ce12f4b157ff404ee2dc3d867423c2d4dc2 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 12:51:38 +0200 Subject: [PATCH 65/73] =?UTF-8?q?=F0=9F=91=80=20Review=20+=20improve=20Ope?= =?UTF-8?q?nEBS=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- k8s/openebs-pod.yaml | 7 +- slides/k8s/openebs.md | 514 ++++++++++++++++++++++++++++++++------ slides/kube-fullday.yml | 1 + slides/kube-selfpaced.yml | 1 + slides/kube-twodays.yml | 1 + 5 files changed, 444 insertions(+), 80 deletions(-) diff --git a/k8s/openebs-pod.yaml b/k8s/openebs-pod.yaml index 1a5cc6de..9a38a86f 100644 --- a/k8s/openebs-pod.yaml +++ b/k8s/openebs-pod.yaml @@ -13,7 +13,12 @@ spec: command: - sh - -c - - 'while true; do echo "`date` [`hostname`] Kubernetes is better with PVs." >> /mnt/storage/greet.txt; sleep $(($RANDOM % 5 + 20)); done' + - | + while true; do + echo "$(date) [$(hostname)] Kubernetes is better with PVs." >> /mnt/storage/greet.txt + sleep $(($RANDOM % 5 + 20)) + done volumeMounts: - mountPath: /mnt/storage name: storage + diff --git a/slides/k8s/openebs.md b/slides/k8s/openebs.md index 7193e2e4..942b398e 100644 --- a/slides/k8s/openebs.md +++ b/slides/k8s/openebs.md @@ -1,111 +1,172 @@ # OpenEBS - - OpenEBS is a popular open-source storage solution for Kubernetes + - [OpenEBS] is a popular open-source storage solution for Kubernetes - - Think "Container Attached Storage" + - Uses the concept of "Container Attached Storage" + + (1 volume = 1 dedicated controller pod + a set of replica pods) - Supports a wide range of storage engines: + + - LocalPV: local volumes (hostpath or device), no replication + - Jiva: for lighter workloads with basic cloning/snapshotting - - cStor: based on iSCSI + - cStor: more powerful engine that also supports resizing, RAID, disk pools ... - - Mayastor: light-weight abstraction layer with nVME and vhost-user support + - [Mayastor]: newer, even more powerful engine with NVMe and vhost-user support - - OpenEBS Local PV - for lowest latency local volumes +[OpenEBS]: https://openebs.io/ + +[Mayastor]: https://github.com/openebs/MayaStor#mayastor --- -## Installing OpenEBS with Helm -OpenEBS control plane runs as a set of containers on Kubernetes worker nodes. -It can be installed with helm: +class: extra-details -.exercise[ +## What are all these storage engines? - - Install OpenEBS -```bash - kubectl create ns openebs - helm repo add openebs https://openebs.github.io/charts - helm repo update - helm install openebs openebs/openebs --namespace openebs -``` -] +- LocalPV is great if we want good performance, no replication, easy setup + + (it is similar to the Rancher local path provisioner) + +- Jiva is great if we want replication and easy setup + + (data is stored in containers' filesystems) + +- cStor is more powerful and flexible, but requires more extensive setup + +- Mayastor is designed to achieve extreme performance levels + + (with the right hardware and disks) + +- The OpenEBS documentation has a [good comparison of engines] to help us pick + +[good comparison of engines]: https://docs.openebs.io/docs/next/casengines.html#cstor-vs-jiva-vs-localpv-features-comparison --- ## Installing OpenEBS with Helm -Let's check the running OpenEBS components: +- The OpenEBS control plane can be installed with Helm + +- It will run as a set of containers on Kubernetes worker nodes .exercise[ -```bash - kubectl get pod -n openebs -``` -] - -Let's check the new StorageClasses: - -.exercise[ - -```bash - kubectl get sc -``` + - Install OpenEBS: + ```bash + helm upgrade --install openebs openebs \ + --repo https://openebs.github.io/charts \ + --namespace openebs --create-namespace + ``` ] --- -## Default Storage Classes +## Checking what was installed -For a simple testing of OpenEBS, you can use the below default storage classes: +- Wait a little bit ... - - **openebs-jiva-default** for provisioning Jiva Volume (this uses default pool which means the data replicas are created in the /mnt/openebs_disk directory of the Jiva replica pod) +.exercise[ - - **openebs-hostpath** for provisioning Local PV on hostpath. +- Look at the pods in the `openebs` namespace: + ```bash + kubectl get pods --namespace openebs + ``` - - **openebs-device** for provisioning Local PV on device. +- And the StorageClasses that were created: + ```bash + kubectl get sc + ``` -For using real disks, you have to create *cStorPools* or *Jiva* pools or *OpenEBS Local PV* based on the requirement and then create corresponding StorageClasses or use default StorageClasses to use them. +] --- -## Selecting an OpenEBS Storage Engine +## The default StorageClasses -Storage engine is chosen by specifying the annotation `openebs.io/cas-type` in the StorageClass specification. StorageClass defines the provisioner details. Separate provisioners are specified for each CAS engine. +- OpenEBS typically creates three default StorageClasses -Example for Local PV host path: +- `openebs-jiva-default` provisions 3 replicated Jiva pods per volume + + - data is stored in `/openebs` in the replica pods + - `/openebs` is a localpath volume mapped to `/var/openebs/pvc-...` on the node + +- `openebs-hostpath` uses LocalPV with local directories + + - volumes are hostpath volumes created in `/var/openebs/local` on each node + +- `openebs-device` uses LocalPV with local block devices + + - requires available disks and/or a bit of extra configuration + - the default configuration filters out loop, LVM, MD devices + +--- + +## When do we need custom StorageClasses? + +- To store LocalPV hostpath volumes on a different path on the host + +- To change the number of replicated Jiva pods + +- To use a different Jiva pool + + (i.e. a different path on the host to store the Jiva volumes) + +- To create a cStor pool + +- ... + +--- + +class: extra-details + +## Defining a custom StorageClass + +Example for a LocalPV hostpath class using an extra mount on `/mnt/vol001`: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - name: localpv-hostpath-sc + name: localpv-hostpath-mntvol001 annotations: openebs.io/cas-type: local cas.openebs.io/config: | - name: BasePath - value: "/var/openebs/local" + value: "/mnt/vol001" - name: StorageType value: "hostpath" provisioner: openebs.io/local ``` +- `provisioner` needs to be set accordingly +- Storage engine is chosen by specifying the annotation `openebs.io/cas-type` +- Storage engine configuration is set with the annotation `cas.openebs.io/config` + --- -## Exploring the host path StorageClass +## Checking the default hostpath StorageClass + +- Let's inspect the StorageClass that OpenEBS created for us .exercise[ - - Let's look at the OpenEBS Local PV host path StorageClass - ```bash - kubectl get sc openebs-hostpath -oyaml - ``` + +- Let's look at the OpenEBS LocalPV hostpath StorageClass: + ```bash + kubectl get storageclass openebs-hostpath -o yaml + ``` ] --- -## Create a host path PVC +## Create a host path PVC + +- Let's create a Persistent Volume Claim using an explicit StorageClass -Let's create a Persistent Volume Claim .exercise[ + ```bash kubectl apply -f - < Date: Fri, 9 Apr 2021 13:46:52 +0200 Subject: [PATCH 66/73] =?UTF-8?q?=E2=9C=82=EF=B8=8F=20Remove=20unused=20an?= =?UTF-8?q?notations=20(they're=20confusing)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- k8s/volumes-for-consul.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/k8s/volumes-for-consul.yaml b/k8s/volumes-for-consul.yaml index 8d75e8ea..1cab0afa 100644 --- a/k8s/volumes-for-consul.yaml +++ b/k8s/volumes-for-consul.yaml @@ -3,8 +3,6 @@ apiVersion: v1 kind: PersistentVolume metadata: name: consul-node2 - annotations: - node: node2 spec: capacity: storage: 10Gi @@ -26,8 +24,6 @@ apiVersion: v1 kind: PersistentVolume metadata: name: consul-node3 - annotations: - node: node3 spec: capacity: storage: 10Gi @@ -49,8 +45,6 @@ apiVersion: v1 kind: PersistentVolume metadata: name: consul-node4 - annotations: - node: node4 spec: capacity: storage: 10Gi From a5d857edd453fcfbb344da92119d6f29c2cc1cfc Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 15:26:27 +0200 Subject: [PATCH 67/73] =?UTF-8?q?=E2=9C=82=EF=B8=8F=20Simplify=20Consul=20?= =?UTF-8?q?YAML=20a=20tiny=20bit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- k8s/consul-2.yaml | 12 +++--------- k8s/consul-3.yaml | 12 +++--------- slides/k8s/statefulsets.md | 12 +++--------- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/k8s/consul-2.yaml b/k8s/consul-2.yaml index e683aacd..f042770d 100644 --- a/k8s/consul-2.yaml +++ b/k8s/consul-2.yaml @@ -62,11 +62,8 @@ spec: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - consul + matchLabels: + app: consul topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 10 containers: @@ -88,7 +85,4 @@ spec: lifecycle: preStop: exec: - command: - - /bin/sh - - -c - - consul leave + command: [ "sh", "-c", "consul leave" ] diff --git a/k8s/consul-3.yaml b/k8s/consul-3.yaml index af62fe0e..f442ea30 100644 --- a/k8s/consul-3.yaml +++ b/k8s/consul-3.yaml @@ -69,11 +69,8 @@ spec: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - persistentconsul + matchLabels: + app: consul topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 10 containers: @@ -98,7 +95,4 @@ spec: lifecycle: preStop: exec: - command: - - /bin/sh - - -c - - consul leave + command: [ "sh", "-c", "consul leave" ] diff --git a/slides/k8s/statefulsets.md b/slides/k8s/statefulsets.md index 97d8ca62..e0d20f91 100644 --- a/slides/k8s/statefulsets.md +++ b/slides/k8s/statefulsets.md @@ -331,11 +331,8 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \ podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - consul + matchLabels: + app: consul topologyKey: kubernetes.io/hostname ``` @@ -353,10 +350,7 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \ lifecycle: preStop: exec: - command: - - /bin/sh - - -c - - consul leave + command: [ "sh", "-c", "consul leave" ] ``` --- From 6ab11ca91c04fc318cd897cb766f781400e8df79 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 15:48:10 +0200 Subject: [PATCH 68/73] =?UTF-8?q?=F0=9F=94=90=20Add=20cert-manager=20+=20I?= =?UTF-8?q?ngress=20annotation=20information?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/cert-manager.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/slides/k8s/cert-manager.md b/slides/k8s/cert-manager.md index b95949b3..61c65b10 100644 --- a/slides/k8s/cert-manager.md +++ b/slides/k8s/cert-manager.md @@ -223,6 +223,24 @@ spec: class: extra-details +## Automatic TLS Ingress with annotations + +- It is also possible to annotate Ingress resources for cert-manager + +- If we annotate an Ingress resource with `cert-manager.io/cluster-issuer=xxx`: + + - cert-manager will detect that annotation + + - it will obtain a certificate using the specified ClusterIssuer (`xxx`) + + - it will store the key and certificate in the specified Secret + +- Note: the Ingress still needs the `tls` section with `secretName` and `hosts` + +--- + +class: extra-details + ## Let's Encrypt and nip.io - Let's Encrypt has [rate limits](https://letsencrypt.org/docs/rate-limits/) per domain From f25bf60d46d18100de6edf89517683baebf0c4b7 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 17:12:55 +0200 Subject: [PATCH 69/73] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Replace=20the=20Tomc?= =?UTF-8?q?at=20example=20with=20the=20OWASP=20Juice=20Shop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/helm-chart-format.md | 40 +++++--- slides/k8s/helm-intro.md | 165 ++++++++++++++++++-------------- slides/k8s/helm-secrets.md | 43 ++++++--- 3 files changed, 149 insertions(+), 99 deletions(-) diff --git a/slides/k8s/helm-chart-format.md b/slides/k8s/helm-chart-format.md index c0a5ada0..a3352f3d 100644 --- a/slides/k8s/helm-chart-format.md +++ b/slides/k8s/helm-chart-format.md @@ -40,7 +40,22 @@ - a `Chart.yaml` file, containing metadata (name, version, description ...) -- Let's look at a simple chart, `stable/tomcat` +- Let's look at a simple chart for a basic demo app + +--- + +## Adding the repo + +- If you haven't done it before, you need to add the repo for that chart + +.exercise[ + +- Add the repo that holds the chart for the OWASP Juice Shop: + ```bash + helm repo add juice https://charts.securecodebox.io + ``` + +] --- @@ -50,17 +65,17 @@ .exercise[ -- Download the tarball for `stable/tomcat`: +- Download the tarball for `juice/juice-shop`: ```bash - helm pull stable/tomcat + helm pull juice/juice-shop ``` - (This will create a file named `tomcat-X.Y.Z.tgz`.) + (This will create a file named `juice-shop-X.Y.Z.tgz`.) -- Or, download + untar `stable/tomcat`: +- Or, download + untar `juice/juice-shop`: ```bash - helm pull stable/tomcat --untar + helm pull juice/juice-shop --untar ``` - (This will create a directory named `tomcat`.) + (This will create a directory named `juice-shop`.) ] @@ -68,13 +83,13 @@ ## Looking at the chart's content -- Let's look at the files and directories in the `tomcat` chart +- Let's look at the files and directories in the `juice-shop` chart .exercise[ - Display the tree structure of the chart we just downloaded: ```bash - tree tomcat + tree juice-shop ``` ] @@ -93,12 +108,11 @@ We see the components mentioned above: `Chart.yaml`, `templates/`, `values.yaml` (using the standard Go template library) - .exercise[ -- Look at the template file for the tomcat Service resource: +- Look at the template file for the Service resource: ```bash - cat tomcat/templates/appsrv-svc.yaml + cat juice-shop/templates/service.yaml ``` ] @@ -190,7 +204,7 @@ We see the components mentioned above: `Chart.yaml`, `templates/`, `values.yaml` - At the top-level of the chart, it's a good idea to have a README -- It will be viewable with e.g. `helm show readme stable/tomcat` +- It will be viewable with e.g. `helm show readme juice/juice-shop` - In the `templates/` directory, we can also have a `NOTES.txt` file diff --git a/slides/k8s/helm-intro.md b/slides/k8s/helm-intro.md index 66385ab8..c6e02fbb 100644 --- a/slides/k8s/helm-intro.md +++ b/slides/k8s/helm-intro.md @@ -229,71 +229,95 @@ fine for personal and development clusters.) --- -## Managing repositories - -- Let's check what repositories we have, and add the `stable` repo - - (the `stable` repo contains a set of official-ish charts) - -.exercise[ - -- List our repos: - ```bash - helm repo list - ``` - -- Add the `stable` repo: - ```bash - helm repo add stable https://charts.helm.sh/stable - ``` - -] - -Adding a repo can take a few seconds (it downloads the list of charts from the repo). - -It's OK to add a repo that already exists (it will merely update it). - ---- - class: extra-details -## Deprecation warning +## How to find charts, the old way -- That "stable" is being deprecated, in favor of a more decentralized approach +- Helm 2 came with one pre-configured repo, the "stable" repo - (each community / company / group / project hosting their own repository) + (located at https://charts.helm.sh/stable) -- We're going to use it here for educational purposes +- Helm 3 doesn't have any pre-configured repo -- But if you're looking for production-grade charts, look elsewhere! +- The "stable" repo mentioned above is now being deprecated - (namely, on the Helm Hub) +- The new approach is to have fully decentralized repos + +- Repos can be indexed in the Artifact Hub + + (which supersedes the Helm Hub) --- -## Search available charts +## How to find charts, the new way -- We can search available charts with `helm search` +- Go to the [Artifact Hub](https://artifacthub.io/packages/search?kind=0) (https://artifacthub.io) -- We need to specify where to search (only our repos, or Helm Hub) +- Or use `helm search hub ...` from the CLI -- Let's search for all charts mentioning tomcat! +- Let's try to find a Helm chart for something called "OWASP Juice Shop"! + + (it is a famous demo app used in security challenges) + +--- + +## Finding charts from the CLI + +- We can use `helm search hub ` .exercise[ -- Search for tomcat in the repo that we added earlier: +- Look for the OWASP Juice Shop app: ```bash - helm search repo tomcat + helm search hub owasp juice ``` -- Search for tomcat on the Helm Hub: +- Since the URLs are truncated, try with the YAML output: ```bash - helm search hub tomcat + helm search hub owasp juice -o yaml ``` ] -[Helm Hub](https://hub.helm.sh/) indexes many repos, using the [Monocular](https://github.com/helm/monocular) server. +Then go to → https://artifacthub.io/packages/helm/seccurecodebox/juice-shop + +--- + +## Finding charts on the web + +- We can also use the Artifact Hub search feature + +.exercise[ + +- Go to https://artifacthub.io/ + +- In the search box on top, enter "owasp juice" + +- Click on the "juice-shop" result (not "multi-juicer" or "juicy-ctf") + +] + +--- + +## Installing the chart + +- Click on the "Install" button, it will show instructions + +.exercise[ + +- First, add the repository for that chart: + ```bash + helm repo add juice https://charts.securecodebox.io + ``` + +- Then, install the chart: + ```bash + helm install my-juice-shop juice/juice-shop + ``` + +] + +Note: it is also possible to install directly a chart, with `--repo https://...` --- @@ -301,22 +325,22 @@ class: extra-details - "Installing a chart" means creating a *release* -- We need to name that release +- In the previous exemple, the release was named "my-juice-shop" - (or use the `--generate-name` to get Helm to generate one for us) +- We can also use `--generate-name` to ask Helm to generate a name for us .exercise[ -- Install the tomcat chart that we found earlier: - ```bash - helm install java4ever stable/tomcat - ``` - - List the releases: ```bash helm list ``` +- Check that we have a `my-juice-shop-...` Pod up and running: + ```bash + kubectl get pods + ``` + ] --- @@ -329,13 +353,13 @@ class: extra-details - The `helm search` command only takes a search string argument - (e.g. `helm search tomcat`) + (e.g. `helm search juice-shop`) - With Helm 2, the name is optional: - `helm install stable/tomcat` will automatically generate a name + `helm install juice/juice-shop` will automatically generate a name - `helm install --name java4ever stable/tomcat` will specify a name + `helm install --name my-juice-shop juice/juice-shop` will specify a name --- @@ -349,12 +373,12 @@ class: extra-details - List all the resources created by this release: ```bash - kubectl get all --selector=release=java4ever + kubectl get all --selector=app.kubernetes.io/instance=my-juice-shop ``` ] -Note: this `release` label wasn't added automatically by Helm. +Note: this label wasn't added automatically by Helm.
It is defined in that chart. In other words, not all charts will provide this label. @@ -362,11 +386,11 @@ It is defined in that chart. In other words, not all charts will provide this la ## Configuring a release -- By default, `stable/tomcat` creates a service of type `LoadBalancer` +- By default, `juice/juice-shop` creates a service of type `ClusterIP` - We would like to change that to a `NodePort` -- We could use `kubectl edit service java4ever-tomcat`, but ... +- We could use `kubectl edit service my-juice-shop`, but ... ... our changes would get overwritten next time we update that chart! @@ -386,14 +410,14 @@ It is defined in that chart. In other words, not all charts will provide this la .exercise[ -- Look at the README for tomcat: +- Look at the README for the app: ```bash - helm show readme stable/tomcat + helm show readme juice/juice-shop ``` - Look at the values and their defaults: ```bash - helm show values stable/tomcat + helm show values juice/juice-shop ``` ] @@ -410,18 +434,19 @@ The `readme` may or may not have (accurate) explanations for the values. - Values can be set when installing a chart, or when upgrading it -- We are going to update `java4ever` to change the type of the service +- We are going to update `my-juice-shop` to change the type of the service .exercise[ -- Update `java4ever`: +- Update `my-juice-shop`: ```bash - helm upgrade java4ever stable/tomcat --set service.type=NodePort + helm upgrade my-juice-shop juice/my-juice-shop \ + --set service.type=NodePort ``` ] -Note that we have to specify the chart that we use (`stable/tomcat`), +Note that we have to specify the chart that we use (`juice/my-juice-shop`), even if we just want to update some values. We can set multiple values. If we want to set many values, we can use `-f`/`--values` and pass a YAML file with all the values. @@ -430,25 +455,21 @@ All unspecified values will take the default values defined in the chart. --- -## Connecting to tomcat +## Connecting to the Juice Shop -- Let's check the tomcat server that we just installed - -- Note: its readiness probe has a 60s delay - - (so it will take 60s after the initial deployment before the service works) +- Let's check the app that we just installed .exercise[ - Check the node port allocated to the service: ```bash - kubectl get service java4ever-tomcat - PORT=$(kubectl get service java4ever-tomcat -o jsonpath={..nodePort}) + kubectl get service my-juice-shop + PORT=$(kubectl get service my-juice-shop -o jsonpath={..nodePort}) ``` -- Connect to it, checking the demo app on `/sample/`: +- Connect to it: ```bash - curl localhost:$PORT/sample/ + curl localhost:$PORT/ ``` ] diff --git a/slides/k8s/helm-secrets.md b/slides/k8s/helm-secrets.md index 032ad0e4..8d613b54 100644 --- a/slides/k8s/helm-secrets.md +++ b/slides/k8s/helm-secrets.md @@ -12,22 +12,37 @@ --- +## Adding the repo + +- If you haven't done it before, you need to add the repo for that chart + +.exercise[ + +- Add the repo that holds the chart for the OWASP Juice Shop: + ```bash + helm repo add juice https://charts.securecodebox.io + ``` + +] + +--- + ## We need a release - We need to install something with Helm -- Let's use the `stable/tomcat` chart as an example +- Let's use the `juice/juice-shop` chart as an example .exercise[ -- Install a release called `tomcat` with the chart `stable/tomcat`: +- Install a release called `orange` with the chart `juice/juice-shop`: ```bash - helm upgrade tomcat stable/tomcat --install + helm upgrade orange juice/juice-shop --install ``` - Let's upgrade that release, and change a value: ```bash - helm upgrade tomcat stable/tomcat --set ingress.enabled=true + helm upgrade orange juice/juice-shop --set ingress.enabled=true ``` ] @@ -42,7 +57,7 @@ - View the history for that release: ```bash - helm history tomcat + helm history orange ``` ] @@ -82,11 +97,11 @@ We should see a number of secrets with TYPE `helm.sh/release.v1`. .exercise[ -- Examine the secret corresponding to the second release of `tomcat`: +- Examine the secret corresponding to the second release of `orange`: ```bash - kubectl describe secret sh.helm.release.v1.tomcat.v2 + kubectl describe secret sh.helm.release.v1.orange.v2 ``` - (`v1` is the secret format; `v2` means revision 2 of the `tomcat` release) + (`v1` is the secret format; `v2` means revision 2 of the `orange` release) ] @@ -102,7 +117,7 @@ There is a key named `release`. - Dump the secret: ```bash - kubectl get secret sh.helm.release.v1.tomcat.v2 \ + kubectl get secret sh.helm.release.v1.orange.v2 \ -o go-template='{{ .data.release }}' ``` @@ -120,7 +135,7 @@ Secrets are encoded in base64. We need to decode that! - Decode the secret: ```bash - kubectl get secret sh.helm.release.v1.tomcat.v2 \ + kubectl get secret sh.helm.release.v1.orange.v2 \ -o go-template='{{ .data.release | base64decode }}' ``` @@ -144,7 +159,7 @@ Let's try one more round of decoding! - Decode it twice: ```bash - kubectl get secret sh.helm.release.v1.tomcat.v2 \ + kubectl get secret sh.helm.release.v1.orange.v2 \ -o go-template='{{ .data.release | base64decode | base64decode }}' ``` @@ -164,7 +179,7 @@ Let's try one more round of decoding! - Pipe the decoded release through `file -`: ```bash - kubectl get secret sh.helm.release.v1.tomcat.v2 \ + kubectl get secret sh.helm.release.v1.orange.v2 \ -o go-template='{{ .data.release | base64decode | base64decode }}' \ | file - ``` @@ -185,7 +200,7 @@ Gzipped data! It can be decoded with `gunzip -c`. - Rerun the previous command, but with `| gunzip -c > release-info` : ```bash - kubectl get secret sh.helm.release.v1.tomcat.v2 \ + kubectl get secret sh.helm.release.v1.orange.v2 \ -o go-template='{{ .data.release | base64decode | base64decode }}' \ | gunzip -c > release-info ``` @@ -211,7 +226,7 @@ If we inspect that JSON (e.g. with `jq keys release-info`), we see: - `config` (contains the values that we've set) - `info` (date of deployment, status messages) - `manifest` (YAML generated from the templates) -- `name` (name of the release, so `tomcat`) +- `name` (name of the release, so `orange`) - `namespace` (namespace where we deployed the release) - `version` (revision number within that release; starts at 1) From f3c36462982f598642d07aa824cf31f708b2c09c Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 17:16:12 +0200 Subject: [PATCH 70/73] =?UTF-8?q?=F0=9F=94=A5=20Deprecate=20--count=20in?= =?UTF-8?q?=20favor=20of=20--students?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prepare-vms/lib/commands.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare-vms/lib/commands.sh b/prepare-vms/lib/commands.sh index 822a9539..cd1150b2 100644 --- a/prepare-vms/lib/commands.sh +++ b/prepare-vms/lib/commands.sh @@ -598,7 +598,7 @@ _cmd_start() { case "$1" in --infra) INFRA=$2; shift 2;; --settings) SETTINGS=$2; shift 2;; - --count) COUNT=$2; shift 2;; + --count) die "Flag --count is deprecated; please use --students instead." ;; --tag) TAG=$2; shift 2;; --students) STUDENTS=$2; shift 2;; *) die "Unrecognized parameter: $1." From d217e52ab568c98a002b8a9821deadc0378c9d54 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Fri, 9 Apr 2021 17:34:49 +0200 Subject: [PATCH 71/73] =?UTF-8?q?=F0=9F=94=90=20Add=20rbac-lookup=20plugin?= =?UTF-8?q?=20info=20in=20RBAC=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/k8s/authn-authz.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/slides/k8s/authn-authz.md b/slides/k8s/authn-authz.md index b6ae85ca..a1c79131 100644 --- a/slides/k8s/authn-authz.md +++ b/slides/k8s/authn-authz.md @@ -733,17 +733,19 @@ class: extra-details ## Figuring out who can do what -- For auditing purposes, sometimes we want to know who can perform an action +- For auditing purposes, sometimes we want to know who can perform which actions -- There are a few tools to help us with that +- There are a few tools to help us with that, available as `kubectl` plugins: - - [kubectl-who-can](https://github.com/aquasecurity/kubectl-who-can) by Aqua Security + - `kubectl who-can` / [kubectl-who-can](https://github.com/aquasecurity/kubectl-who-can) by Aqua Security - - [Review Access (aka Rakkess)](https://github.com/corneliusweig/rakkess) + - `kubectl access-matrix` / [Rakkess (Review Access)](https://github.com/corneliusweig/rakkess) by Cornelius Weig -- Both are available as standalone programs, or as plugins for `kubectl` + - `kubectl rbac-lookup` / [RBAC Lookup](https://github.com/FairwindsOps/rbac-lookup) by FairwindsOps - (`kubectl` plugins can be installed and managed with `krew`) +- `kubectl` plugins can be installed and managed with `krew` + +- They can also be installed and executed as standalone programs ??? From e606cd2b21d8818ecc92074e90123311a35dc5a5 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 12 Apr 2021 18:28:46 +0200 Subject: [PATCH 72/73] =?UTF-8?q?=E2=9C=82=EF=B8=8F=20Don't=20include=20he?= =?UTF-8?q?lm.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slides/helm.yml | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 slides/helm.yml diff --git a/slides/helm.yml b/slides/helm.yml deleted file mode 100644 index 0ac4bc7e..00000000 --- a/slides/helm.yml +++ /dev/null @@ -1,43 +0,0 @@ -title: | - Packaging d'applications - et CI/CD pour Kubernetes - -#chat: "[Gitter](https://gitter.im/jpetazzo/training-202102-online)" - -gitrepo: github.com/jpetazzo/container.training - -slides: https://2021-04-dijon.container.training/ - -#slidenumberprefix: "#SomeHashTag — " - -exclude: -- self-paced - -content: -- shared/title.md -#- logistics.md -- k8s/intro.md -- shared/about-slides.md -- shared/prereqs.md -- shared/webssh.md -- shared/connecting.md -#- shared/chat-room-im.md -#- shared/chat-room-zoom.md -- shared/toc.md -- - - shared/sampleapp.md - - k8s/helm-intro.md - - k8s/helm-chart-format.md - - k8s/helm-create-basic-chart.md -- - - k8s/helm-create-better-chart.md - - k8s/helm-dependencies.md - - k8s/helm-values-schema-validation.md - - k8s/helm-secrets.md -- - - k8s/cert-manager.md - - k8s/gitlab.md - - | - # (Extra content) - - k8s/prometheus.md - From e75e4d7f2c9af4ee01e5c12ff4abe79c6885ea85 Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Mon, 12 Apr 2021 18:33:30 +0200 Subject: [PATCH 73/73] =?UTF-8?q?=F0=9F=97=82=EF=B8=8F=20Update=20table=20?= =?UTF-8?q?of=20contents=20to=20add=20new=20Helm=20chapters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #580 --- slides/kube-adv.yml | 2 ++ slides/kube-fullday.yml | 2 ++ slides/kube-halfday.yml | 2 ++ slides/kube-selfpaced.yml | 2 ++ slides/kube-twodays.yml | 2 ++ 5 files changed, 10 insertions(+) diff --git a/slides/kube-adv.yml b/slides/kube-adv.yml index 297c7cf0..951531c2 100644 --- a/slides/kube-adv.yml +++ b/slides/kube-adv.yml @@ -50,6 +50,8 @@ content: - | # (Extra content) - k8s/helm-create-better-chart.md + - k8s/helm-dependencies.md + - k8s/helm-values-schema-validation.md - k8s/helm-secrets.md - #5 - k8s/extending-api.md diff --git a/slides/kube-fullday.yml b/slides/kube-fullday.yml index 047587d6..050bddc8 100644 --- a/slides/kube-fullday.yml +++ b/slides/kube-fullday.yml @@ -84,6 +84,8 @@ content: #- k8s/helm-chart-format.md #- k8s/helm-create-basic-chart.md #- k8s/helm-create-better-chart.md + #- k8s/helm-dependencies.md + #- k8s/helm-values-schema-validation.md #- k8s/helm-secrets.md #- k8s/exercise-helm.md #- k8s/gitlab.md diff --git a/slides/kube-halfday.yml b/slides/kube-halfday.yml index 3bd0e430..f3045598 100644 --- a/slides/kube-halfday.yml +++ b/slides/kube-halfday.yml @@ -76,6 +76,8 @@ content: #- k8s/helm-chart-format.md - k8s/helm-create-basic-chart.md #- k8s/helm-create-better-chart.md + #- k8s/helm-dependencies.md + #- k8s/helm-values-schema-validation.md #- k8s/helm-secrets.md #- k8s/kustomize.md #- k8s/netpol.md diff --git a/slides/kube-selfpaced.yml b/slides/kube-selfpaced.yml index f2d3fde5..4328175c 100644 --- a/slides/kube-selfpaced.yml +++ b/slides/kube-selfpaced.yml @@ -88,6 +88,8 @@ content: - k8s/helm-chart-format.md - k8s/helm-create-basic-chart.md - k8s/helm-create-better-chart.md + - k8s/helm-dependencies.md + - k8s/helm-values-schema-validation.md - k8s/helm-secrets.md #- k8s/exercise-helm.md - k8s/gitlab.md diff --git a/slides/kube-twodays.yml b/slides/kube-twodays.yml index 0e384ccd..71805b23 100644 --- a/slides/kube-twodays.yml +++ b/slides/kube-twodays.yml @@ -85,6 +85,8 @@ content: - k8s/helm-chart-format.md - k8s/helm-create-basic-chart.md - k8s/helm-create-better-chart.md + - k8s/helm-dependencies.md + - k8s/helm-values-schema-validation.md - k8s/helm-secrets.md #- k8s/exercise-helm.md - k8s/gitlab.md