diff --git a/k8s/traefik-v2.yaml b/k8s/traefik-v2.yaml
index 9fb6aeeb..e6d113db 100644
--- a/k8s/traefik-v2.yaml
+++ b/k8s/traefik-v2.yaml
@@ -1,36 +1,44 @@
---
apiVersion: v1
+kind: Namespace
+metadata:
+ name: traefik
+---
+apiVersion: v1
kind: ServiceAccount
metadata:
- name: traefik-ingress-controller
- namespace: kube-system
+ name: traefik
+ namespace: traefik
---
kind: DaemonSet
apiVersion: apps/v1
metadata:
- name: traefik-ingress-controller
- namespace: kube-system
+ name: traefik
+ namespace: traefik
labels:
- k8s-app: traefik-ingress-lb
+ app: traefik
spec:
selector:
matchLabels:
- k8s-app: traefik-ingress-lb
+ app: traefik
template:
metadata:
labels:
- k8s-app: traefik-ingress-lb
- name: traefik-ingress-lb
+ app: traefik
+ name: traefik
spec:
tolerations:
- effect: NoSchedule
operator: Exists
- hostNetwork: true
- serviceAccountName: traefik-ingress-controller
+ # If, for some reason, our CNI plugin doesn't support hostPort,
+ # we can enable hostNetwork instead. That should work everywhere
+ # but it doesn't provide the same isolation.
+ #hostNetwork: true
+ serviceAccountName: traefik
terminationGracePeriodSeconds: 60
containers:
- - image: traefik:v2.5
- name: traefik-ingress-lb
+ - image: traefik:v2.10
+ name: traefik
ports:
- name: http
containerPort: 80
@@ -61,7 +69,7 @@ spec:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: traefik-ingress-controller
+ name: traefik
rules:
- apiGroups:
- ""
@@ -73,14 +81,6 @@ rules:
- get
- list
- watch
- - apiGroups:
- - extensions
- resources:
- - ingresses
- verbs:
- - get
- - list
- - watch
- apiGroups:
- networking.k8s.io
resources:
@@ -94,15 +94,15 @@ rules:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: traefik-ingress-controller
+ name: traefik
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
- name: traefik-ingress-controller
+ name: traefik
subjects:
- kind: ServiceAccount
- name: traefik-ingress-controller
- namespace: kube-system
+ name: traefik
+ namespace: traefik
---
kind: IngressClass
apiVersion: networking.k8s.io/v1
diff --git a/slides/k8s/ingress.md b/slides/k8s/ingress.md
index 8e24f83b..b4270ea6 100644
--- a/slides/k8s/ingress.md
+++ b/slides/k8s/ingress.md
@@ -28,7 +28,7 @@ A few use-cases:
- Cost optimization
- (because individual `LoadBalancer` services typically cost money)
+ (using `LoadBalancer` services for everything would be expensive)
- Automatic handling of TLS certificates
@@ -109,7 +109,7 @@ A few use-cases:
- Step 1: deploy an *ingress controller*
- (one-time setup)
+ (one-time setup; typically done by cluster admin)
- Step 2: create *Ingress resources*
@@ -117,7 +117,7 @@ A few use-cases:
- the controller watches ingress resources and sets up a LB
-- Step 3: set up DNS
+- Step 3: set up DNS (optional)
- associate DNS entries with the load balancer address
@@ -175,15 +175,50 @@ class: extra-details
- maybe motivated by the fact that Traefik releases are named after cheeses
-- For DNS, we will use [nip.io](http://nip.io/)
+- We will create ingress resources for various HTTP services
+
+- For DNS, we can use [nip.io](http://nip.io/)
- `*.1.2.3.4.nip.io` resolves to `1.2.3.4`
-- We will create ingress resources for various HTTP services
+---
+
+## Classic ingress controller setup
+
+- Ingress controller runs with a Deployment
+
+ (with at least 2 replicas for redundancy)
+
+- It is exposed with a `LoadBalancer` Service
+
+- Typical for cloud-based clusters
+
+- Also common when running or on-premises with [MetalLB] or [kube-vip]
+
+[MetalLB]: https://metallb.org/
+[kube-vip]: https://kube-vip.io/
---
-## Accepting connections on port 80 (and 443)
+## Alternate ingress controller setup
+
+- Ingress controller runs with a DaemonSet
+
+ (on bigger clusters, this can be coupled with a `nodeSelector`)
+
+- It is exposed with `externalIPs`, `hostPort`, or `hostNetwork`
+
+- Typical for on-premises clusters
+
+ (where at least a set of nodes have a stable IP and high availability)
+
+---
+
+class: extra-details
+
+## Why not a `NodePort` Service?
+
+- Node ports are typically in the 30000-32767 range
- Web site users don't want to specify port numbers
@@ -193,94 +228,40 @@ class: extra-details
(and 443 if we want to handle HTTPS)
-- Let's see how we can achieve that!
+---
+
+class: extra-details
+
+## Local clusters
+
+- When running a local cluster, some extra steps might be necessary
+
+- When using Docker-based clusters on Linux:
+
+ *connect directly to the node's IP address (172.X.Y.Z)*
+
+- When using Docker-based clusters with Docker Desktop:
+
+ *set up port mapping (then connect to localhost:XYZ)*
+
+- Generic scenario:
+
+ *run `kubectl port-forward 8888:80` to the ingress controller*
+
+ *(and then connect to `http://localhost:8888`)*
---
-## Various ways to expose something on port 80
+## Trying it out with Traefik
-- Service with `type: LoadBalancer`
+- We are going to run Traefik with a DaemonSet
- *costs a little bit of money; not always available*
+ (there will be one instance of Traefik on every node of the cluster)
-- Service with one (or multiple) `ExternalIP`
-
- *requires public nodes; limited by number of nodes*
-
-- Service with `hostPort` or `hostNetwork`
-
- *same limitations as `ExternalIP`; even harder to manage*
-
----
-
-## Deploying pods listening on port 80
-
-- We are going to run Traefik in Pods with `hostNetwork: true`
-
- (so that our load balancer can use the "real" port 80 of our nodes)
-
-- Traefik Pods will be created by a DaemonSet
-
- (so that we get one instance of Traefik on every node of the cluster)
+- The Pods will use `hostPort: 80`
- This means that we will be able to connect to any node of the cluster on port 80
-.warning[This is not typical of a production setup!]
-
----
-
-## Doing it in production
-
-- When running "on cloud", the easiest option is a `LoadBalancer` service
-
-- When running "on prem", it depends:
-
- - [MetalLB] is a good option if a pool of public IP addresses is available
-
- - otherwise, using `externalIPs` on a few nodes (2-3 for redundancy)
-
-- Many variations/optimizations are possible depending on our exact scenario!
-
-[MetalLB]: https://metallb.org/
-
----
-
-class: extra-details
-
-## Without `hostNetwork`
-
-- Normally, each pod gets its own *network namespace*
-
- (sometimes called sandbox or network sandbox)
-
-- An IP address is assigned to the pod
-
-- This IP address is routed/connected to the cluster network
-
-- All containers of that pod are sharing that network namespace
-
- (and therefore using the same IP address)
-
----
-
-class: extra-details
-
-## With `hostNetwork: true`
-
-- No network namespace gets created
-
-- The pod is using the network namespace of the host
-
-- It "sees" (and can use) the interfaces (and IP addresses) of the host
-
-- The pod can receive outside traffic directly, on any port
-
-- Downside: with most network plugins, network policies won't work for that pod
-
- - most network policies work at the IP address level
-
- - filtering that pod = filtering traffic from the node
-
---
## Running Traefik
@@ -293,7 +274,7 @@ class: extra-details
- use a Daemon Set so that each node can accept connections
- - enable `hostNetwork`
+ - enable `hostPort: 80`
- add a *toleration* so that Traefik also runs on all nodes
@@ -426,11 +407,13 @@ This one is a special case that means "ignore all taints and run anyway."
## Running Traefik on our cluster
-- We provide a YAML file (`k8s/traefik.yaml`) which is essentially the sum of:
+- We provide a YAML file (@@LINK[k8s/traefik.yaml]) which contains:
- - [Traefik's Daemon Set resources](https://github.com/containous/traefik/blob/v1.7/examples/k8s/traefik-ds.yaml) (patched with `hostNetwork` and tolerations)
+ - a `traefik` Namespace
- - [Traefik's RBAC rules](https://github.com/containous/traefik/blob/v1.7/examples/k8s/traefik-rbac.yaml) allowing it to watch necessary API objects
+ - a `traefik` DaemonSet in that Namespace
+
+ - RBAC rules allowing Traefik to watch the necessary API objects
.lab[
@@ -462,20 +445,6 @@ This is normal: we haven't provided any ingress rule yet.
---
-## Setting up DNS
-
-- To make our lives easier, we will use [nip.io](http://nip.io)
-
-- Check out `http://red.A.B.C.D.nip.io`
-
- (replacing A.B.C.D with the IP address of `node1`)
-
-- We should get the same `404 page not found` error
-
- (meaning that our DNS is "set up properly", so to speak!)
-
----
-
## Traefik web UI
- Traefik provides a web dashboard
@@ -492,7 +461,7 @@ This is normal: we haven't provided any ingress rule yet.
---
-## Setting up host-based routing ingress rules
+## Setting up routing ingress rules
- We are going to use the `jpetazzo/color` image
@@ -504,7 +473,7 @@ This is normal: we haven't provided any ingress rule yet.
- Then we will create 3 ingress rules (one for each service)
-- We will route `.A.B.C.D.nip.io` to the corresponding deployment
+- We will route requests to `/red`, `/green`, `/blue`
---
@@ -534,100 +503,164 @@ This is normal: we haven't provided any ingress rule yet.
- Since Kubernetes 1.19, we can use `kubectl create ingress`
+ (if you're running an older version of Kubernetes, **you must upgrade**)
+
+.lab[
+
+- Create the three ingress resources:
```bash
- kubectl create ingress red \
- --rule=red.`A.B.C.D`.nip.io/*=red:80
+ kubectl create ingress red --rule=/red=red:80
+ kubectl create ingress green --rule=/green=green:80
+ kubectl create ingress blue --rule=/blue=blue:80
```
-- We can specify multiple rules per resource
+]
+---
+
+## Testing
+
+- We should now be able to access `localhost/red`, `localhost/green`, etc.
+
+.lab[
+
+- Check that these routes work correctly:
+ ```bash
+ curl http://localhost/red
+ curl http://localhost/green
+ curl http://localhost/blue
+ ```
+
+]
+
+---
+
+## Accessing other URIs
+
+- What happens if we try to access e.g. `/blue/hello`?
+
+.lab[
+
+- Retrieve the `ClusterIP` of Service `blue`:
+ ```bash
+ BLUE=$(kubectl get svc blue -o jsonpath={.spec.clusterIP})
+ ```
+
+- Check that the `blue` app serves `/hello`:
+ ```bash
+ curl $BLUE/hello
+ ```
+
+- See what happens if we try to access it through the Ingress:
+ ```bash
+ curl http://localhost/blue/hello
+ ```
+
+]
+
+---
+
+## Exact or prefix matches
+
+- By default, ingress rules are *exact* matches
+
+ (the request is routed only if the URI is exactly `/blue`)
+
+- We can also ask a *prefix* match by adding a `*` to the rule
+
+.lab[
+
+- Create a prefix match rule for the `blue` service:
+ ```bash
+ kubectl create ingress bluestar --rule=/blue*:blue:80
+ ```
+
+- Check that it works:
+ ```bash
+ curl http://localhost/blue/hello
+ ```
+
+]
+
+---
+
+## Multiple rules per Ingress resource
+
+- It is also possible to have multiple rules in a single resource
+
+.lab[
+
+- Create an Ingress resource with multiple rules:
```bash
kubectl create ingress rgb \
- --rule=red.`A.B.C.D`.nip.io/*=red:80 \
- --rule=green.`A.B.C.D`.nip.io/*=green:80 \
- --rule=blue.`A.B.C.D`.nip.io/*=blue:80
+ --rule=/red*=red:80 \
+ --rule=/green*=green:80 \
+ --rule=/blue*=blue:80
```
----
+- Check that everything still works after deleting individual rules
-## Pay attention to the `*`!
-
-- The `*` is important:
-
- ```
- --rule=red.A.B.C.D.nip.io/`*`=red:80
- ```
-
-- It means "all URIs below that path"
-
-- Without the `*`, it means "only that exact path"
-
- (if we omit it, requests for e.g. `red.A.B.C.D.nip.io/hello` will 404)
+]
---
-## Before Kubernetes 1.19
+## Using domain-based routing
-- Before Kubernetes 1.19:
+- In the previous examples, we didn't use domain names
- - `kubectl create ingress` wasn't available
+ (we routed solely based on the URI of the request)
- - `apiVersion: networking.k8s.io/v1` wasn't supported
+- We are now going to show how to use domain-based routing
-- It was necessary to use YAML, and `apiVersion: networking.k8s.io/v1beta1`
+- We are going to assume that we have a domain name
- (see example on next slide)
+ (for instance: `cloudnative.tld`)
+
+- That domain name should be set up so that a few subdomains point to the ingress
+
+ (for instance, `blue.cloudnative.tld`, `green.cloudnative.tld`...)
+
+- For simplicity or flexibility, we can also use a wildcard record
---
-## YAML for old ingress resources
+## Setting up DNS
-Here is a minimal host-based ingress resource:
+- To make our lives easier, we will use [nip.io](http://nip.io)
-```yaml
-apiVersion: networking.k8s.io/v1beta1
-kind: Ingress
-metadata:
- name: red
-spec:
- rules:
- - host: red.`A.B.C.D`.nip.io
- http:
- paths:
- - path: /
- backend:
- serviceName: red
- servicePort: 80
+- Check out `http://red.A.B.C.D.nip.io`
-```
+ (replacing A.B.C.D with the IP address of `node1`)
+
+- We should get the same `404 page not found` error
+
+ (meaning that our DNS is "set up properly", so to speak!)
---
-## YAML for new ingress resources
+## Setting up name-based Ingress
-- Starting with Kubernetes 1.19, `networking.k8s.io/v1` is available
-
-- And we can use `kubectl create ingress` 🎉
-
-- We can see "modern" YAML with `-o yaml --dry-run=client`:
+.lab[
+- Set the `$IPADDR` variable to our ingress controller address:
```bash
- kubectl create ingress red -o yaml --dry-run=client \
- --rule=red.`A.B.C.D`.nip.io/*=red:80
-
+ IPADDR=`A.B.C.D`
```
----
+- Create our Ingress resource:
+ ```bash
+ kubectl create ingress rgb-with-domain \
+ --rule=red.$IPADDR.nip.io/*=red:80 \
+ --rule=green.$IPADDR.nip.io/*=green:80 \
+ --rule=blue.$IPADDR.nip.io/*=blue:80
+ ```
-## Creating ingress resources
+- Test it out:
+ ```bash
+ curl http://red.$IPADDR.nip.io/hello
+ ```
-- Create the ingress resources with `kubectl create ingress`
-
- (or use the YAML manifests if using Kubernetes 1.18 or older)
-
-- Make sure to update the hostnames!
-
-- Check that you can connect to the exposed web apps
+]
---
@@ -677,7 +710,7 @@ class: extra-details
GatewayClass, Gateway, HTTPRoute, TCPRoute...
-- It is still in alpha stage
+- It is now in beta (since v0.5.0, released in 2022)
???