Compare commits

..

2 Commits

Author SHA1 Message Date
Jerome Petazzoni
c0411d6b8f Tweak content, add extra stuff 2020-09-11 14:37:41 +02:00
Jerome Petazzoni
f7b38325d4 fwdays k8s content 2020-09-11 13:20:56 +02:00
31 changed files with 109 additions and 1138 deletions

View File

@@ -1,33 +0,0 @@
kind: Service
apiVersion: v1
metadata:
name: certbot
spec:
ports:
- port: 80
protocol: TCP
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: certbot
spec:
rules:
- http:
paths:
- path: /.well-known/acme-challenge/
backend:
serviceName: certbot
servicePort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
name: certbot
subsets:
- addresses:
- ip: A.B.C.D
ports:
- port: 8000
protocol: TCP

View File

@@ -1,77 +0,0 @@
# Basic Consul cluster using Cloud Auto-Join.
# Caveats:
# - no actual persistence
# - scaling down to 1 will break the cluster
# - pods may be colocated
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: consul
rules:
- apiGroups: [""]
resources:
- pods
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: consul
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: consul
subjects:
- kind: ServiceAccount
name: consul
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: consul
---
apiVersion: v1
kind: Service
metadata:
name: consul
spec:
ports:
- port: 8500
name: http
selector:
app: consul
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: consul
spec:
serviceName: consul
replicas: 3
selector:
matchLabels:
app: consul
template:
metadata:
labels:
app: consul
spec:
serviceAccountName: consul
containers:
- name: consul
image: "consul:1.8"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- "agent"
- "-bootstrap-expect=3"
- "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NAMESPACE)\""
- "-client=0.0.0.0"
- "-data-dir=/consul/data"
- "-server"
- "-ui"

View File

@@ -1,104 +0,0 @@
# Even better Consul cluster.
# That one uses a volumeClaimTemplate to achieve true persistence.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: consul
rules:
- apiGroups: [""]
resources:
- pods
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: consul
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: consul
subjects:
- kind: ServiceAccount
name: consul
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: consul
---
apiVersion: v1
kind: Service
metadata:
name: consul
spec:
ports:
- port: 8500
name: http
selector:
app: consul
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: consul
spec:
serviceName: consul
replicas: 3
selector:
matchLabels:
app: consul
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
template:
metadata:
labels:
app: consul
spec:
serviceAccountName: consul
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- persistentconsul
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 10
containers:
- name: consul
image: "consul:1.8"
volumeMounts:
- name: data
mountPath: /consul/data
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- "agent"
- "-bootstrap-expect=3"
- "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NAMESPACE)\""
- "-client=0.0.0.0"
- "-data-dir=/consul/data"
- "-server"
- "-ui"
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- consul leave

View File

@@ -1,9 +1,5 @@
# Better Consul cluster.
# There is still no actual persistence, but:
# - podAntiaffinity prevents pod colocation
# - clusters works when scaling down to 1 (thanks to lifecycle hook)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
kind: ClusterRole
metadata:
name: consul
rules:
@@ -15,16 +11,17 @@ rules:
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
kind: ClusterRoleBinding
metadata:
name: consul
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
kind: ClusterRole
name: consul
subjects:
- kind: ServiceAccount
name: consul
namespace: default
---
apiVersion: v1
kind: ServiceAccount
@@ -71,16 +68,11 @@ spec:
terminationGracePeriodSeconds: 10
containers:
- name: consul
image: "consul:1.8"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: "consul:1.6"
args:
- "agent"
- "-bootstrap-expect=3"
- "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NAMESPACE)\""
- "-retry-join=provider=k8s label_selector=\"app=consul\""
- "-client=0.0.0.0"
- "-data-dir=/consul/data"
- "-server"

View File

@@ -3,10 +3,6 @@ kind: Ingress
metadata:
name: whatever
spec:
#tls:
#- secretName: whatever.A.B.C.D.nip.io
# hosts:
# - whatever.A.B.C.D.nip.io
rules:
- host: whatever.A.B.C.D.nip.io
http:

View File

@@ -50,10 +50,8 @@ spec:
- --api.insecure
- --log.level=INFO
- --metrics.prometheus
- --providers.kubernetescrd
- --providers.kubernetesingress
- --entrypoints.http.Address=:80
- --entrypoints.https.Address=:443
- --entrypoints.https.http.tls.certResolver=default
---
kind: Service
apiVersion: v1

View File

@@ -1 +1 @@
traefik-v2.yaml
traefik-v1.yaml

View File

@@ -43,16 +43,6 @@ _cmd_cards() {
info "$0 www"
}
_cmd clean "Remove information about stopped clusters"
_cmd_clean() {
for TAG in tags/*; do
if grep -q ^stopped$ "$TAG/status"; then
info "Removing $TAG..."
rm -rf "$TAG"
fi
done
}
_cmd deploy "Install Docker on a bunch of running VMs"
_cmd_deploy() {
TAG=$1
@@ -348,22 +338,10 @@ _cmd_ips() {
done < tags/$TAG/ips.txt
}
_cmd list "List all VMs on a given infrastructure (or all infras if no arg given)"
_cmd list "List available groups for a given infrastructure"
_cmd_list() {
case "$1" in
"")
for INFRA in infra/*; do
$0 list $INFRA
done
;;
*/example.*)
;;
*)
need_infra $1
sep "Listing instances for $1"
infra_list
;;
esac
need_infra $1
infra_list
}
_cmd listall "List VMs running on all configured infrastructures"

View File

@@ -3,8 +3,7 @@ if ! command -v aws >/dev/null; then
fi
infra_list() {
aws ec2 describe-instances --output json |
jq -r '.Reservations[].Instances[] | [.InstanceId, .ClientToken, .State.Name, .InstanceType ] | @tsv'
aws_display_tags
}
infra_quotas() {

View File

@@ -5,13 +5,6 @@ if ! [ -f ~/.config/hcloud/cli.toml ]; then
warn "~/.config/hcloud/cli.toml not found."
fi
infra_list() {
[ "$(hcloud server list -o json)" = "null" ] && return
hcloud server list -o json |
jq -r '.[] | [.id, .name , .status, .server_type.name] | @tsv'
}
infra_start() {
COUNT=$1

View File

@@ -1,8 +1,3 @@
infra_list() {
openstack server list -f json |
jq -r '.[] | [.ID, .Name , .Status, .Flavor] | @tsv'
}
infra_start() {
COUNT=$1
@@ -49,5 +44,5 @@ oscli_get_instances_json() {
oscli_get_ips_by_tag() {
TAG=$1
oscli_get_instances_json $TAG |
jq -r .[].Networks | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' || true
jq -r .[].Networks | cut -d= -f2 | cut -d, -f1 | grep . || true
}

View File

@@ -5,11 +5,6 @@ if ! [ -f ~/.config/scw/config.yaml ]; then
warn "~/.config/scw/config.yaml not found."
fi
infra_list() {
scw instance server list -o json |
jq -r '.[] | [.id, .name, .state, .commercial_type] | @tsv'
}
infra_start() {
COUNT=$1

View File

@@ -114,7 +114,7 @@ system("sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /e
system("sudo service ssh restart")
system("sudo apt-get -q update")
system("sudo apt-get -qy install git jid jq")
system("sudo apt-get -qy install git jq")
system("sudo apt-get -qy install emacs-nox joe")
#######################

View File

@@ -29,7 +29,6 @@ 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"]

View File

@@ -1,67 +0,0 @@
title: |
Docker Intensif
chat: "[Gitter](https://gitter.im/jpetazzo/training-202010-online)"
gitrepo: github.com/jpetazzo/container.training
slides: https://2020-10-enix.container.training/
#slidenumberprefix: "#SomeHashTag &mdash; "
exclude:
- self-paced
content:
- shared/title.md
- logistics.md
- containers/intro.md
- shared/about-slides.md
- shared/chat-room-im.md
#- shared/chat-room-zoom-meeting.md
#- shared/chat-room-zoom-webinar.md
- shared/toc.md
-
#- containers/Docker_Overview.md
#- containers/Docker_History.md
- containers/Training_Environment.md
#- containers/Installing_Docker.md
- containers/First_Containers.md
- containers/Background_Containers.md
#- containers/Start_And_Attach.md
- containers/Naming_And_Inspecting.md
#- containers/Labels.md
- containers/Getting_Inside.md
- containers/Initial_Images.md
-
- containers/Building_Images_Interactively.md
- containers/Building_Images_With_Dockerfiles.md
- containers/Cmd_And_Entrypoint.md
- containers/Copying_Files_During_Build.md
- containers/Exercise_Dockerfile_Basic.md
-
- containers/Container_Networking_Basics.md
#- containers/Network_Drivers.md
- containers/Local_Development_Workflow.md
- containers/Container_Network_Model.md
- containers/Compose_For_Dev_Stacks.md
- containers/Exercise_Composefile.md
-
- containers/Multi_Stage_Builds.md
#- containers/Publishing_To_Docker_Hub.md
- containers/Dockerfile_Tips.md
- containers/Exercise_Dockerfile_Advanced.md
#- containers/Docker_Machine.md
#- containers/Advanced_Dockerfiles.md
#- containers/Init_Systems.md
#- containers/Application_Configuration.md
#- containers/Logging.md
#- containers/Namespaces_Cgroups.md
#- containers/Copy_On_Write.md
#- containers/Containers_From_Scratch.md
#- containers/Container_Engines.md
#- containers/Pods_Anatomy.md
#- containers/Ecosystem.md
#- containers/Orchestration_Overview.md
- shared/thankyou.md
- containers/links.md

View File

@@ -1,36 +0,0 @@
title: |
Packaging d'applications
pour Kubernetes
chat: "[Gitter](https://gitter.im/jpetazzo/training-202010-online)"
gitrepo: github.com/jpetazzo/container.training
slides: https://2020-10-enix.container.training/
#slidenumberprefix: "#SomeHashTag &mdash; "
exclude:
- self-paced
content:
- shared/title.md
#- logistics.md
- k8s/intro.md
- shared/about-slides.md
#- shared/chat-room-im.md
#- shared/chat-room-zoom.md
- shared/toc.md
-
- shared/prereqs.md
- shared/webssh.md
- shared/connecting.md
- k8s/kustomize.md
- k8s/helm-intro.md
- k8s/helm-chart-format.md
- k8s/helm-create-basic-chart.md
- k8s/helm-create-better-chart.md
- k8s/helm-secrets.md
#- k8s/exercise-helm.md
- shared/thankyou.md
- k8s/links.md

View File

@@ -1,47 +0,0 @@
title: |
Kubernetes Avancé
chat: "[Gitter](https://gitter.im/jpetazzo/training-202010-online)"
gitrepo: github.com/jpetazzo/container.training
slides: https://2020-10-enix.container.training/
#slidenumberprefix: "#SomeHashTag &mdash; "
exclude:
- self-paced
content:
- shared/title.md
- logistics.md
- k8s/intro.md
- shared/about-slides.md
- shared/chat-room-im.md
#- shared/chat-room-zoom.md
- shared/toc.md
-
- shared/prereqs.md
- shared/webssh.md
- shared/connecting.md
- k8s/netpol.md
- k8s/authn-authz.md
-
- k8s/extending-api.md
- k8s/operators.md
-
- k8s/resource-limits.md
- k8s/metrics-server.md
- k8s/cluster-sizing.md
- k8s/horizontal-pod-autoscaler.md
- k8s/prometheus.md
-
- k8s/statefulsets.md
- k8s/local-persistent-volumes.md
- k8s/portworx.md
- shared/thankyou.md
-
- |
# (Bonus material)
- k8s/podsecuritypolicy.md
- k8s/operators-design.md

View File

@@ -1,48 +0,0 @@
title: |
Opérer Kubernetes
chat: "[Gitter](https://gitter.im/jpetazzo/training-202010-online)"
gitrepo: github.com/jpetazzo/container.training
slides: https://2020-10-enix.container.training/
#slidenumberprefix: "#SomeHashTag &mdash; "
exclude:
- self-paced
content:
- shared/title.md
- logistics.md
- k8s/intro.md
- shared/about-slides.md
- shared/chat-room-im.md
#- shared/chat-room-zoom-meeting.md
#- shared/chat-room-zoom-webinar.md
- shared/toc.md
# DAY 1
-
- k8s/prereqs-admin.md
- k8s/architecture.md
- k8s/deploymentslideshow.md
- k8s/dmuc.md
-
- k8s/multinode.md
- k8s/cni.md
- k8s/interco.md
-
- k8s/apilb.md
- k8s/setup-overview.md
- k8s/setup-devel.md
- k8s/setup-managed.md
- k8s/setup-selfhosted.md
- k8s/staticpods.md
- k8s/cluster-upgrade.md
- k8s/cluster-backup.md
#- k8s/cloud-controller-manager.md
-
- k8s/podsecuritypolicy.md
- k8s/csr-api.md
- k8s/openid-connect.md
- shared/thankyou.md

View File

@@ -1,6 +1,6 @@
# Uncomment and/or edit one of the the following lines if necessary.
#/ /kube-halfday.yml.html 200!
#/ /kube-fullday.yml.html 200!
/ /kube-fullday.yml.html 200!
#/ /kube-twodays.yml.html 200!
# And this allows to do "git clone https://container.training".
@@ -19,4 +19,6 @@
/next https://skillsmatter.com/courses/700-advanced-kubernetes-concepts-workshop-jerome-petazzoni
/hi5 https://enix.io/fr/services/formation/online/
/ /highfive.html 200!
/chat https://gitter.im/jpetazzo/workshop-20200912-online
/vms https://docs.google.com/spreadsheets/d/1LkxnxGpMWWWTdpDhz-mF1noYrfc_BlI8yi71EyxwmM4

View File

@@ -95,24 +95,6 @@ $ ssh <login>@<ip-address>
---
class: in-person
## `tailhist`
The shell history of the instructor is available online in real time.
Note the IP address of the instructor's virtual machine (A.B.C.D).
Open http://A.B.C.D:1088 in your browser and you should see the history.
The history is updated in real time (using a WebSocket connection).
It should be green when the WebSocket is connected.
If it turns red, reloading the page should fix it.
---
## Checking your Virtual Machine
Once logged in, make sure that you can run a basic Docker command:

View File

@@ -1,75 +0,0 @@
<?xml version="1.0"?>
<html>
<head>
<style>
td {
background: #ccc;
padding: 1em;
}
</style>
</head>
<body>
<table>
<tr>
<td>Lundi 5 octobre 2020</td>
<td>
<a href="1.yml.html">Docker Intensif</a>
</td>
</tr>
<tr>
<td>Mardi 6 octobre 2020</td>
<td>
<a href="1.yml.html">Docker Intensif</a>
</td>
</tr>
<tr>
<td>Mercredi 7 octobre 2020</td>
<td>
<a href="2.yml.html">Fondamentaux Kubernetes</a>
</td>
</tr>
<tr>
<td>Jeudi 8 octobre 2020</td>
<td>
<a href="2.yml.html">Fondamentaux Kubernetes</a>
</td>
</tr>
<tr>
<td>Vendredi 9 octobre 2020</td>
<td>
<a href="2.yml.html">Fondamentaux Kubernetes</a>
</td>
</tr>
<tr>
<td>Lundi 12 octobre 2020</td>
<td>
<a href="3.yml.html">Packaging d'applications pour Kubernetes</a>
</td>
</tr>
<tr>
<td>Mardi 13 octobre 2020</td>
<td>
<a href="4.yml.html">Kubernetes Avancé</a>
</td>
</tr>
<tr>
<td>Mercredi 14 octobre 2020</td>
<td>
<a href="4.yml.html">Kubernetes Avancé</a>
</td>
</tr>
<tr>
<td>Lundi 19 octobre 2020</td>
<td>
<a href="5.yml.html">Opérer Kubernetes</a>
</td>
</tr>
<tr>
<td>Mardi 20 octobre 2020</td>
<td>
<a href="5.yml.html">Opérer Kubernetes</a>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -58,7 +58,6 @@
speaker: jpetazzo
title: Intensive Docker Online Workshop
attend: https://fwdays.com/en/event/intensive-docker-workshop
slides: https://2020-08-fwdays.container.training/
- date: [2020-09-12, 2020-09-13]
country: www
@@ -67,7 +66,6 @@
speaker: jpetazzo
title: Kubernetes Intensive Online Workshop
attend: https://fwdays.com/en/event/kubernetes-intensive-workshop
slides: https://2020-09-fwdays.container.training/
- date: [2020-07-07, 2020-07-09]
country: www

View File

@@ -1,403 +0,0 @@
# Ingress and TLS certificates
- Most ingress controllers support TLS connections
(in a way that is standard across controllers)
- The TLS key and certificate are stored in a Secret
- The Secret is then referenced in the Ingress resource:
```yaml
spec:
tls:
- secretName: XXX
hosts:
- YYY
rules:
- ZZZ
```
---
## Obtaining a certificate
- In the next section, we will need a TLS key and certificate
- These usually come in [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) format:
```
-----BEGIN CERTIFICATE-----
MIIDATCCAemg...
...
-----END CERTIFICATE-----
```
- We will see how to generate a self-signed certificate
(easy, fast, but won't be recognized by web browsers)
- We will also see how to obtain a certificate from [Let's Encrypt](https://letsencrypt.org/)
(requires the cluster to be reachable through a domain name)
---
class: extra-details
## In production ...
- A very popular option is to use the [cert-manager](https://cert-manager.io/docs/) operator
- It's a flexible, modular approach to automated certificate management
- For simplicity, in this section, we will use [certbot](https://certbot.eff.org/)
- The method shown here works well for one-time certs, but lacks:
- automation
- renewal
---
## Which domain to use
- If you're doing this in a training:
*the instructor will tell you what to use*
- If you're doing this on your own Kubernetes cluster:
*you should use a domain that points to your cluster*
- More precisely:
*you should use a domain that points to your ingress controller*
- If you don't have a domain name, you can use [nip.io](https://nip.io/)
(if your ingress controller is on 1.2.3.4, you can use `whatever.1.2.3.4.nip.io`)
---
## Setting `$DOMAIN`
- We will use `$DOMAIN` in the following section
- Let's set it now
.exercise[
- Set the `DOMAIN` environment variable:
```bash
export DOMAIN=...
```
]
---
## Method 1, self-signed certificate
- Thanks to `openssl`, generating a self-signed cert is just one command away!
.exercise[
- Generate a key and certificate:
```bash
openssl req \
-newkey rsa -nodes -keyout privkey.pem \
-x509 -days 30 -subj /CN=$DOMAIN/ -out cert.pem
```
]
This will create two files, `privkey.pem` and `cert.pem`.
---
## Method 2, Let's Encrypt with certbot
- `certbot` is an [ACME](https://tools.ietf.org/html/rfc8555) client
(Automatic Certificate Management Environment)
- We can use it to obtain certificates from Let's Encrypt
- It needs to listen to port 80
(to complete the [HTTP-01 challenge](https://letsencrypt.org/docs/challenge-types/))
- If port 80 is already taken by our ingress controller, see method 3
---
class: extra-details
## HTTP-01 challenge
- `certbot` contacts Let's Encrypt, asking for a cert for `$DOMAIN`
- Let's Encrypt gives a token to `certbot`
- Let's Encrypt then tries to access the following URL:
`http://$DOMAIN/.well-known/acme-challenge/<token>`
- That URL needs to be routed to `certbot`
- Once Let's Encrypt gets the response from `certbot`, it issues the certificate
---
## Running certbot
- There is a very convenient container image, `certbot/certbot`
- Let's use a volume to get easy access to the generated key and certificate
.exercise[
- Obtain a certificate from Let's Encrypt:
```bash
EMAIL=your.address@example.com
docker run --rm -p 80:80 -v $PWD/letsencrypt:/etc/letsencrypt \
certbot/certbot certonly \
-m $EMAIL \
--standalone --agree-tos -n \
--domain $DOMAIN \
--test-cert
```
]
This will get us a "staging" certificate.
Remove `--test-cert` to obtain a *real* certificate.
---
## Copying the key and certificate
- If everything went fine:
- the key and certificate files are in `letsencrypt/live/$DOMAIN`
- they are owned by `root`
.exercise[
- Grant ourselves permissions on these files:
```bash
sudo chown -R $USER letsencrypt
```
- Copy the certificate and key to the current directory:
```bash
cp letsencrypt/live/test/{cert,privkey}.pem .
```
]
---
## Method 3, certbot with Ingress
- Sometimes, we can't simply listen to port 80:
- we might already have an ingress controller there
- our nodes might be on an internal network
- But we can define an Ingress to route the HTTP-01 challenge to `certbot`!
- Our Ingress needs to route all requests to `/.well-known/acme-challenge` to `certbot`
- There are at least two ways to do that:
- run `certbot` in a Pod (and extract the cert+key when it's done)
- run `certbot` in a container on a node (and manually route traffic to it)
- We're going to use the second option
(mostly because it will give us an excuse to tinker with Endpoints resources!)
---
## The plan
- We need the following resources:
- an Endpoints¹ listing a hard-coded IP address and port
<br/>(where our `certbot` container will be listening)
- a Service corresponding to that Endpoints
- an Ingress sending requests to `/.well-known/acme-challenge/*` to that Service
<br/>(we don't even need to include a domain name in it)
- Then we need to start `certbot` so that it's listening on the right address+port
.footnote[¹Endpoints is always plural, because even a single resource is a list of endpoints.]
---
## Creating resources
- We prepared a YAML file to create the three resources
- However, the Endpoints needs to be adapted to put the current node's address
.exercise[
- Edit `~/containers.training/k8s/certbot.yaml`
(replace `A.B.C.D` with the current node's address)
- Create the resources:
```bash
kubectl apply -f ~/containers.training/k8s/certbot.yaml
```
]
---
## Obtaining the certificate
- Now we can run `certbot`, listening on the port listed in the Endpoints
(i.e. 8000)
.exercise[
- Run `certbot`:
```bash
EMAIL=your.address@example.com
docker run --rm -p 8000:80 -v $PWD/letsencrypt:/etc/letsencrypt \
certbot/certbot certonly \
-m $EMAIL \
--standalone --agree-tos -n \
--domain $DOMAIN \
--test-cert
```
]
This is using the staging environment.
Remove `--test-cert` to get a production certificate.
---
## Copying the certificate
- Just like in the previous method, the certificate is in `letsencrypt/live/$DOMAIN`
(and owned by root)
.exercise[
- Grand ourselves permissions on these files:
```bash
sudo chown -R $USER letsencrypt
```
- Copy the certificate and key to the current directory:
```bash
cp letsencrypt/live/$DOMAIN/{cert,privkey}.pem .
```
]
---
## Creating the Secret
- We now have two files:
- `privkey.pem` (the private key)
- `cert.pem` (the certificate)
- We can create a Secret to hold them
.exercise[
- Create the Secret:
```bash
kubectl create secret tls $DOMAIN --cert=cert.pem --key=privkey.pem
```
]
---
## Ingress with TLS
- To enable TLS for an Ingress, we need to add a `tls` section to the Ingress:
```yaml
spec:
tls:
- secretName: DOMAIN
hosts:
- DOMAIN
rules: ...
```
- The list of hosts will be used by the ingress controller
(to know which certificate to use with [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication))
- Of course, the name of the secret can be different
(here, for clarity and convenience, we set it to match the domain)
---
class: extra-details
## About the ingress controller
- Many ingress controllers can use different "stores" for keys and certificates
- Our ingress controller needs to be configured to use secrets
(as opposed to, e.g., obtain certificates directly with Let's Encrypt)
---
## Using the certificate
.exercise[
- Edit the Ingress manifest, `~/container.training/k8s/ingress.yaml`
- Uncomment the `tls` section
- Update the `secretName` and `hosts` list
- Create or update the Ingress:
```bash
kubectl apply -f ~/container.training/k8s/ingress.yaml
```
- Check that the URL now works over `https`
(it might take a minute to be picked up by the ingress controller)
]
---
## Discussion
*To repeat something mentioned earlier ...*
- The methods presented here are for *educational purpose only*
- In most production scenarios, the certificates will be obtained automatically
- A very popular option is to use the [cert-manager](https://cert-manager.io/docs/) operator
???
:EN:- Ingress and TLS
:FR:- Certificats TLS et *ingress*

View File

@@ -58,7 +58,7 @@
## Deploying Consul
- Let's use a new manifest for our Consul cluster
- We will use a slightly different YAML file
- The only differences between that file and the previous one are:
@@ -66,11 +66,15 @@
- the corresponding `volumeMounts` in the Pod spec
- the label `consul` has been changed to `persistentconsul`
<br/>
(to avoid conflicts with the other Stateful Set)
.exercise[
- Apply the persistent Consul YAML file:
```bash
kubectl apply -f ~/container.training/k8s/consul-3.yaml
kubectl apply -f ~/container.training/k8s/persistent-consul.yaml
```
]
@@ -93,7 +97,7 @@
kubectl get pv
```
- The Pod `consul-0` is not scheduled yet:
- The Pod `persistentconsul-0` is not scheduled yet:
```bash
kubectl get pods -o wide
```
@@ -108,9 +112,9 @@
- In a Stateful Set, the Pods are started one by one
- `consul-1` won't be created until `consul-0` is running
- `persistentconsul-1` won't be created until `persistentconsul-0` is running
- `consul-0` has a dependency on an unbound Persistent Volume Claim
- `persistentconsul-0` has a dependency on an unbound Persistent Volume Claim
- The scheduler won't schedule the Pod until the PVC is bound
@@ -148,7 +152,7 @@
- Once a PVC is bound, its pod can start normally
- Once the pod `consul-0` has started, `consul-1` can be created, etc.
- Once the pod `persistentconsul-0` has started, `persistentconsul-1` can be created, etc.
- Eventually, our Consul cluster is up, and backend by "persistent" volumes
@@ -156,7 +160,7 @@
- Check that our Consul clusters has 3 members indeed:
```bash
kubectl exec consul-0 -- consul members
kubectl exec persistentconsul-0 -- consul members
```
]

View File

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

View File

@@ -431,13 +431,13 @@ troubleshoot easily, without having to poke holes in our firewall.
- 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: <!-- ##VERSION## -->
- The API documentation has a lot of detail about the format of various objects:
- [NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicy-v1-networking-k8s-io)
- [NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.12/#networkpolicy-v1-networking-k8s-io)
- [NetworkPolicySpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicyspec-v1-networking-k8s-io)
- [NetworkPolicySpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.12/#networkpolicyspec-v1-networking-k8s-io)
- [NetworkPolicyIngressRule](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicyingressrule-v1-networking-k8s-io)
- [NetworkPolicyIngressRule](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.12/#networkpolicyingressrule-v1-networking-k8s-io)
- etc.

View File

@@ -30,7 +30,7 @@
- They are stopped in reverse order (from *R-1* to 0)
- Each pod knows its identity (i.e. which number it is in the set)
- Each pod know its identity (i.e. which number it is in the set)
- Each pod can discover the IP address of the others easily
@@ -218,9 +218,7 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \
- Replace X.X.X.X and Y.Y.Y.Y with the addresses of other nodes
- A node can add its own address (it will work fine)
- ... Which means that we can use the same command-line on all nodes (convenient!)
- The same command-line can be used on all nodes (convenient!)
---
@@ -260,13 +258,19 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \
## Putting it all together
- The file `k8s/consul-1.yaml` defines the required resources
- The file `k8s/consul.yaml` defines the required resources
(service account, role, role binding, service, stateful set)
(service account, cluster role, cluster role binding, service, stateful set)
- Inspired by this [excellent tutorial](https://github.com/kelseyhightower/consul-on-kubernetes) by Kelsey Hightower
- It has a few extra touches:
(many features from the original tutorial were removed for simplicity)
- a `podAntiAffinity` prevents two pods from running on the same node
- a `preStop` hook makes the pod leave the cluster when shutdown gracefully
This was inspired by this [excellent tutorial](https://github.com/kelseyhightower/consul-on-kubernetes) by Kelsey Hightower.
Some features from the original tutorial (TLS authentication between
nodes and encryption of gossip traffic) were removed for simplicity.
---
@@ -278,7 +282,7 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \
- Create the stateful set and associated service:
```bash
kubectl apply -f ~/container.training/k8s/consul-1.yaml
kubectl apply -f ~/container.training/k8s/consul.yaml
```
- Check the logs as the pods come up one after another:
@@ -302,88 +306,6 @@ consul agent -data-dir=/consul/data -client=0.0.0.0 -server -ui \
## Caveats
- The scheduler may place two Consul pods on the same node
- if that node fails, we lose two Consul pods at the same time
- this will cause the cluster to fail
- Scaling down the cluster will cause it to fail
- when a Consul member leaves the cluster, it needs to inform the others
- otherwise, the last remaining node doesn't have quorum and stops functioning
- This Consul cluster doesn't use real persistence yet
- data is stored in the containers' ephemeral filesystem
- if a pod fails, its replacement starts from a blank slate
---
## Improving pod placement
- We need to tell the scheduler:
*do not put two of these pods on the same node!*
- This is done with an `affinity` section like the following one:
```yaml
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- consul
topologyKey: kubernetes.io/hostname
```
---
## Using a lifecycle hook
- When a Consul member leaves the cluster, it needs to execute:
```bash
consul leave
```
- This is done with a `lifecycle` section like the following one:
```yaml
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- consul leave
```
---
## Running a better Consul cluster
- Let's try to add the scheduling constraint and lifecycle hook
- We can do that in the same namespace or another one (as we like)
- If we do that in the same namespace, we will see a rolling update
(pods will be replaced one by one)
.exercise[
- Deploy a better Consul cluster:
```bash
kubectl apply -f ~/container.training/k8s/consul-2.yaml
```
]
---
## Still no persistence, though
- We aren't using actual persistence yet
(no `volumeClaimTemplate`, Persistent Volume, etc.)

View File

@@ -1,7 +1,7 @@
## Versions installed
- Kubernetes 1.92.2
- Docker Engine 19.03.13
- Kubernetes 1.18.0
- Docker Engine 19.03.8
- Docker Compose 1.25.4
<!-- ##VERSION## -->

View File

@@ -1,13 +1,22 @@
title: |
Fondamentaux Kubernetes
Kubernetes
Intensive
Workshop
chat: "[Gitter](https://gitter.im/jpetazzo/training-202010-online)"
chat: "[Gitter](https://gitter.im/jpetazzo/workshop-20200912-online)"
gitrepo: github.com/jpetazzo/container.training
slides: https://2020-10-enix.container.training/
slides: https://2020-09-fwdays.container.training/
#slidenumberprefix: "#SomeHashTag &mdash; "
slidenumberprefix:
<a href="https://fwdays.com/">fwdays</a>
&mdash;
<a href="https://container.training/">container.training</a>
&mdash;
<a href="https://twitter.com/jpetazzo">@jpetazzo</a>
&mdash;
&nbsp;
exclude:
- self-paced
@@ -34,6 +43,9 @@ content:
- k8s/kubectlget.md
- k8s/kubectl-run.md
-
- k8s/labels-annotations.md
- k8s/kubectl-logs.md
- k8s/logs-cli.md
- shared/declarative.md
- k8s/declarative.md
- k8s/deploymentslideshow.md
@@ -45,54 +57,66 @@ content:
- k8s/ourapponkube.md
#- k8s/exercise-wordsmith.md
-
- k8s/labels-annotations.md
- k8s/kubectl-logs.md
- k8s/logs-cli.md
- k8s/yamldeploy.md
#- k8s/setup-managed.md
#- k8s/setup-selfhosted.md
#- k8s/dashboard.md
#- k8s/kubectlscale.md
- k8s/scalingdockercoins.md
- shared/hastyconclusions.md
- k8s/daemonset.md
-
#- k8s/dryrun.md
#- k8s/exercise-yaml.md
#- k8s/localkubeconfig.md
#- k8s/accessinternal.md
#- k8s/kubectlproxy.md
- k8s/rollout.md
- k8s/healthchecks.md
#- k8s/healthchecks-more.md
- k8s/setup-overview.md
- k8s/setup-devel.md
- k8s/setup-managed.md
#- k8s/setup-selfhosted.md
#- k8s/record.md
-
- k8s/namespaces.md
- k8s/localkubeconfig.md
- k8s/accessinternal.md
- k8s/kubectlproxy.md
- k8s/dashboard.md
- k8s/ingress.md
-
#- k8s/kustomize.md
#- k8s/helm-intro.md
#- k8s/helm-chart-format.md
#- k8s/helm-create-basic-chart.md
#- k8s/helm-create-better-chart.md
#- k8s/helm-secrets.md
#- k8s/exercise-helm.md
#- k8s/create-chart.md
#- k8s/create-more-charts.md
#- k8s/csr-api.md
#- k8s/openid-connect.md
#- k8s/podsecuritypolicy.md
- k8s/volumes.md
#- k8s/exercise-configmap.md
#- k8s/build-with-docker.md
#- k8s/build-with-kaniko.md
- k8s/configuration.md
- k8s/batch-jobs.md
#- k8s/logs-centralized.md
#- k8s/prometheus.md
#- k8s/statefulsets.md
#- k8s/local-persistent-volumes.md
#- k8s/portworx.md
#- k8s/extending-api.md
#- k8s/operators.md
#- k8s/operators-design.md
#- k8s/staticpods.md
#- k8s/owners-and-dependents.md
#- k8s/gitworkflows.md
- k8s/setup-overview.md
- k8s/setup-devel.md
#- k8s/whatsnext.md
#- k8s/lastwords.md
- shared/thankyou.md
- k8s/links.md
- shared/thankyou.md
-
- |
# (Bonus)
- k8s/record.md
- k8s/dryrun.md
- k8s/ingress-tls.md
# (Extra content)
- k8s/healthchecks.md
- k8s/batch-jobs.md
- k8s/netpol.md
- k8s/authn-authz.md
-
- |
# (More extra content)
- k8s/statefulsets.md
- k8s/local-persistent-volumes.md
- k8s/portworx.md

View File

@@ -2,14 +2,18 @@
- Hello! We are:
- .emoji[👷🏻‍♀️] AJ ([@s0ulshake](https://twitter.com/s0ulshake), [EphemeraSearch](https://www.ephemerasearch.com/))
- .emoji[👷🏻‍♀️] AJ ([@s0ulshake](https://twitter.com/s0ulshake), [EphemeraSearch](https://ephemerasearch.com/))
- .emoji[🐳] Jérôme ([@jpetazzo](https://twitter.com/jpetazzo), Enix SAS)
- The training will run from 9:30 to 13:00
- The workshop will run from 10:00 to 15:00 (EEST timezone, GMT+3)
- There will be a break at (approximately) 11:00
- We will have short breaks at 11:10 and 13:50 (approximately!)
- And a longer lunch break at 12:20 (about 30 minutes)
- Feel free to interrupt for questions at any time
- *Especially when you see full screen container pictures!*
- Live feedback, questions, help: @@CHAT@@

View File

@@ -34,26 +34,6 @@ If anything goes wrong — ask for help!
---
class: in-person
## `tailhist`
- The shell history of the instructor is available online in real time
- Note the IP address of the instructor's virtual machine (A.B.C.D)
- Open http://A.B.C.D:1088 in your browser and you should see the history
- The history is updated in real time
(using a WebSocket connection)
- It should be green when the WebSocket is connected
(if it turns red, reloading the page should fix it)
---
## Doing or re-doing the workshop on your own?
- Use something like