mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-07-19 21:09:27 +00:00
⚡️ New content for M5
Instead of showing kubenet and kuberouter with Kubernetes 1.19, we now start with Kubernetes 1.28 (or whatever is the latest version) along with containerd and CNI.
This commit is contained in:
@@ -11,6 +11,7 @@ STEPS="
|
||||
clusterize
|
||||
tools
|
||||
kubepkgs
|
||||
kubebins
|
||||
createuser
|
||||
webssh
|
||||
tailhist
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Building our own cluster
|
||||
# Building our own cluster (easy)
|
||||
|
||||
- Let's build our own cluster!
|
||||
|
||||
@@ -33,10 +33,7 @@
|
||||
|
||||
## Our environment
|
||||
|
||||
- We will use the machine indicated as `dmuc1`
|
||||
|
||||
(this stands for "Dessine Moi Un Cluster" or "Draw Me A Sheep",
|
||||
<br/>in homage to Saint-Exupery's "The Little Prince")
|
||||
- We will use the machine indicated as `monokube1`
|
||||
|
||||
- This machine:
|
||||
|
||||
@@ -48,13 +45,33 @@
|
||||
|
||||
---
|
||||
|
||||
## The fine print
|
||||
|
||||
- We're going to use a *very old* version of Kubernetes
|
||||
|
||||
(specifically, 1.19)
|
||||
|
||||
- Why?
|
||||
|
||||
- It's much easier to set up than recent versions
|
||||
|
||||
- it's compatible with Docker (no need to set up CNI)
|
||||
|
||||
- it doesn't require a ServiceAccount keypair
|
||||
|
||||
- it can be exposed over plain HTTP (insecure but easier)
|
||||
|
||||
- We'll do that, and later, move to recent versions of Kubernetes!
|
||||
|
||||
---
|
||||
|
||||
## Checking our environment
|
||||
|
||||
- Let's make sure we have everything we need first
|
||||
|
||||
.lab[
|
||||
|
||||
- Log into the `dmuc1` machine
|
||||
- Log into the `monokube1` machine
|
||||
|
||||
- Get root:
|
||||
```bash
|
||||
@@ -547,7 +564,7 @@ Success!
|
||||
|
||||
Our node should show up.
|
||||
|
||||
Its name will be its hostname (it should be `dmuc1`).
|
||||
Its name will be its hostname (it should be `monokube1`).
|
||||
|
||||
---
|
||||
|
||||
374
slides/k8s/dmuc-hard.md
Normal file
374
slides/k8s/dmuc-hard.md
Normal file
@@ -0,0 +1,374 @@
|
||||
# Building our own cluster (hard)
|
||||
|
||||
- This section assumes that you already went through
|
||||
|
||||
*“Building our own cluster (medium)”*
|
||||
|
||||
- In that previous section, we built a cluster with a single node
|
||||
|
||||
- In this new section, we're going to add more nodes to the cluster
|
||||
|
||||
- Note: we will need the lab environment of that previous section
|
||||
|
||||
- If you haven't done it yet, you should go through that section first
|
||||
|
||||
---
|
||||
|
||||
## Our environment
|
||||
|
||||
- On `polykube1`, we should have our Kubernetes control plane
|
||||
|
||||
- We're also assuming that we have the kubeconfig file created earlier
|
||||
|
||||
(in `~/.kube/config`)
|
||||
|
||||
- We're going to work on `polykube2` and add it to the cluster
|
||||
|
||||
- This machine has exactly the same setup as `polykube1`
|
||||
|
||||
(Ubuntu LTS with CNI, etcd, and Kubernetes binaries installed)
|
||||
|
||||
- Note that we won't need the etcd binaries here
|
||||
|
||||
(the control plane will run solely on `polykube1`)
|
||||
|
||||
---
|
||||
|
||||
## Checklist
|
||||
|
||||
We need to:
|
||||
|
||||
- generate the kubeconfig file for `polykube2`
|
||||
|
||||
- install a container engine
|
||||
|
||||
- generate a CNI configuration file
|
||||
|
||||
- start kubelet
|
||||
|
||||
---
|
||||
|
||||
## Generating the kubeconfig file
|
||||
|
||||
- Ideally, we should generate a key pair and certificate for `polykube2`...
|
||||
|
||||
- ...and generate a kubeconfig file using these
|
||||
|
||||
- At the moment, for simplicity, we'll use the same key pair and certificate as earlier
|
||||
|
||||
- We have a couple of options:
|
||||
|
||||
- copy the required files (kubeconfig, key pair, certificate)
|
||||
|
||||
- "flatten" the kubeconfig file (embed the key and certificate within)
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## To flatten or not to flatten?
|
||||
|
||||
- "Flattening" the kubeconfig file can seem easier
|
||||
|
||||
(because it means we'll only have one file to move around)
|
||||
|
||||
- But it's easier to rotate the key or renew the certificate when they're in separate files
|
||||
|
||||
---
|
||||
|
||||
## Flatten and copy the kubeconfig file
|
||||
|
||||
- We'll flatten the file and copy it over
|
||||
|
||||
.lab[
|
||||
|
||||
- On `polykube1`, flatten the kubeconfig file:
|
||||
```bash
|
||||
kubectl config view --flatten > kubeconfig
|
||||
```
|
||||
|
||||
- Then copy it to `polykube2`:
|
||||
```bash
|
||||
scp kubeconfig polykube2:
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Generate CNI configuration
|
||||
|
||||
Back on `polykube2`, put the following in `/etc/cni/net.d/kube.conf`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "kube",
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isDefaultGateway": true,
|
||||
"ipMasq": true,
|
||||
"hairpinMode": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": `"10.1.2.0/24"`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note how we changed the subnet!
|
||||
|
||||
---
|
||||
|
||||
## Install container engine and start `kubelet`
|
||||
|
||||
.lab[
|
||||
|
||||
- Install `containerd`:
|
||||
```bash
|
||||
sudo apt-get install containerd -y
|
||||
```
|
||||
|
||||
- Start `containerd`:
|
||||
```bash
|
||||
sudo systemctl start containerd
|
||||
```
|
||||
|
||||
- Start `kubelet`:
|
||||
```bash
|
||||
sudo kubelet --kubeconfig kubeconfig
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
We're getting errors looking like:
|
||||
```
|
||||
"Post \"https://localhost:6443/api/v1/nodes\": ... connect: connection refused"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating the kubeconfig file
|
||||
|
||||
- Our kubeconfig file still references `localhost:6443`
|
||||
|
||||
- This was fine on `polykube1`
|
||||
|
||||
(where `kubelet` was connecting to the control plane running locally)
|
||||
|
||||
- On `polykube2`, we need to change that and put the address of the API server
|
||||
|
||||
(i.e. the address of `polykube1`)
|
||||
|
||||
.lab[
|
||||
|
||||
- Update the `kubeconfig` file:
|
||||
```bash
|
||||
sed -i s/localhost:6443/polykube1:6443/ kubeconfig
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Starting `kubelet`
|
||||
|
||||
- `kubelet` should now start correctly (hopefully!)
|
||||
|
||||
.lab[
|
||||
|
||||
- On `polykube2`, start `kubelet`:
|
||||
```bash
|
||||
sudo kubelet --kubeconfig kubeconfig
|
||||
```
|
||||
|
||||
- On `polykube1`, check that `polykube2` shows up and is `Ready`:
|
||||
```bash
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Testing connectivity
|
||||
|
||||
- From `polykube1`, can we connect to Pods running on `polykube2`? 🤔
|
||||
|
||||
.lab[
|
||||
|
||||
- Scale the test Deployment:
|
||||
```bash
|
||||
kubectl scale deployment blue --replicas=5
|
||||
```
|
||||
|
||||
- Get the IP addresses of the Pods:
|
||||
```bash
|
||||
kubectl get pods -o wide
|
||||
```
|
||||
|
||||
- Pick a Pod on `polykube2` and try to connect to it:
|
||||
```bash
|
||||
curl `10.1.2.2`
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
--
|
||||
|
||||
At that point, it doesn't work.
|
||||
|
||||
---
|
||||
|
||||
## Refresher on the *pod network*
|
||||
|
||||
- The *pod network* (or *pod-to-pod network*) has a few responsibilities:
|
||||
|
||||
- allocating and managing Pod IP addresses
|
||||
|
||||
- connecting Pods and Nodes
|
||||
|
||||
- connecting Pods together on a given node
|
||||
|
||||
- *connecting Pods together across nodes*
|
||||
|
||||
- That last part is the one that's not functioning in our cluster
|
||||
|
||||
- It typically requires some combination of routing, tunneling, bridging...
|
||||
|
||||
---
|
||||
|
||||
## Connecting networks together
|
||||
|
||||
- We can add manual routes between our nodes
|
||||
|
||||
- This requires adding `N x (N-1)` routes
|
||||
|
||||
(on each node, add a route to every other node)
|
||||
|
||||
- This will work on home labs where nodes are directly connected
|
||||
|
||||
(e.g. on an Ethernet switch, or same WiFi network, or a bridge between local VMs)
|
||||
|
||||
- ...Or on clouds where IP address filtering has been disabled
|
||||
|
||||
(by default, most cloud providers will discard packets going to unknown IP addresses)
|
||||
|
||||
- If IP address filtering is enabled, you'll have to use e.g. tunneling or overlay networks
|
||||
|
||||
---
|
||||
|
||||
## Important warning
|
||||
|
||||
- The technique that we are about to use doesn't work everywhere
|
||||
|
||||
- It only works if:
|
||||
|
||||
- all the nodes are directly connected to each other (at layer 2)
|
||||
|
||||
- the underlying network allows the IP addresses of our pods
|
||||
|
||||
- If we are on physical machines connected by a switch: OK
|
||||
|
||||
- If we are on virtual machines in a public cloud: NOT OK
|
||||
|
||||
- on AWS, we need to disable "source and destination checks" on our instances
|
||||
|
||||
- on OpenStack, we need to disable "port security" on our network ports
|
||||
|
||||
---
|
||||
|
||||
## Routing basics
|
||||
|
||||
- We need to tell *each* node:
|
||||
|
||||
"The subnet 10.1.N.0/24 is located on node N" (for all values of N)
|
||||
|
||||
- This is how we add a route on Linux:
|
||||
```bash
|
||||
ip route add 10.1.N.0/24 via W.X.Y.Z
|
||||
```
|
||||
|
||||
(where `W.X.Y.Z` is the internal IP address of node N)
|
||||
|
||||
- We can see the internal IP addresses of our nodes with:
|
||||
```bash
|
||||
kubectl get nodes -o wide
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding our route
|
||||
|
||||
- Let's add a route from `polykube1` to `polykube2`
|
||||
|
||||
.lab[
|
||||
|
||||
- Check the internal address of `polykube2`:
|
||||
```bash
|
||||
kubectl get node polykube2 -o wide
|
||||
```
|
||||
|
||||
- Now, on `polykube1`, add the route to the Pods running on `polykube2`:
|
||||
```bash
|
||||
sudo ip route add 10.1.2.0/24 via `A.B.C.D`
|
||||
```
|
||||
|
||||
- Finally, check that we can now connect to a Pod running on `polykube2`:
|
||||
```bash
|
||||
curl 10.1.2.2
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## What's next?
|
||||
|
||||
- The network configuration feels very manual:
|
||||
|
||||
- we had to generate the CNI configuration file (in `/etc/cni/net.d`)
|
||||
|
||||
- we had to manually update the nodes' routing tables
|
||||
|
||||
- Can we automate that?
|
||||
|
||||
**YES!**
|
||||
|
||||
- We could install something like [kube-router](https://www.kube-router.io/)
|
||||
|
||||
(which specifically takes care of the CNI configuration file and populates routing tables)
|
||||
|
||||
- Or we could also go with e.g. [Cilium](https://cilium.io/)
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## If you want to try Cilium...
|
||||
|
||||
- Add the `--root-ca-file` flag to the controller manager:
|
||||
|
||||
- use the certificate automatically generated by the API server
|
||||
<br/>
|
||||
(it should be in `/var/run/kubernetes/apiserver.crt`)
|
||||
|
||||
- or generate a key pair and certificate for the API server and point to
|
||||
that certificate
|
||||
|
||||
- without that, you'll get certificate validation errors
|
||||
<br/>
|
||||
(because in our Pods, the `ca.crt` file used to validate the API server will be empty)
|
||||
|
||||
- Check the Cilium [without kube-proxy][ciliumwithoutkubeproxy] instructions
|
||||
|
||||
(make sure to pass the API server IP address and port!)
|
||||
|
||||
- Other pod-to-pod network implementations might also require additional steps
|
||||
|
||||
[ciliumwithoutkubeproxy]: https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/#kubeproxy-free
|
||||
|
||||
???
|
||||
|
||||
:EN:- Connecting nodes and pods
|
||||
:FR:- Interconnecter les nœuds et les pods
|
||||
891
slides/k8s/dmuc-medium.md
Normal file
891
slides/k8s/dmuc-medium.md
Normal file
@@ -0,0 +1,891 @@
|
||||
# Building our own cluster (medium)
|
||||
|
||||
- This section assumes that you already went through
|
||||
|
||||
*“Building our own cluster (easy)”*
|
||||
|
||||
- In that section, we saw how to run each control plane component manually...
|
||||
|
||||
...but with an older version of Kubernetes (1.19)
|
||||
|
||||
- In this section, we're going to do something similar...
|
||||
|
||||
...but with recent versions of Kubernetes!
|
||||
|
||||
- Note: we won't need the lab environment of that previous section
|
||||
|
||||
(we're going to build a new cluster from scratch)
|
||||
|
||||
---
|
||||
|
||||
## What remains the same
|
||||
|
||||
- We'll use machines with Kubernetes binaries pre-downloaded
|
||||
|
||||
- We'll run individual components by hand
|
||||
|
||||
(etcd, API server, controller manager, scheduler, kubelet)
|
||||
|
||||
- We'll run on a single node
|
||||
|
||||
(but we'll be laying the groundwork to add more nodes)
|
||||
|
||||
- We'll get the cluster to the point where we can run and expose pods
|
||||
|
||||
---
|
||||
|
||||
## What's different
|
||||
|
||||
- We'll need to generate TLS keys and certificates
|
||||
|
||||
(because it's mandatory with recent versions of Kubernetes)
|
||||
|
||||
- Things will be *a little bit more* secure
|
||||
|
||||
(but still not 100% secure, far from it!)
|
||||
|
||||
- We'll use containerd instead of Docker
|
||||
|
||||
(you could probably try with CRI-O or another CRI engine, too)
|
||||
|
||||
- We'll need to set up CNI for networking
|
||||
|
||||
- *And we won't do everything as root this time (but we might use `sudo` a lot)*
|
||||
|
||||
---
|
||||
|
||||
## Our environment
|
||||
|
||||
- We will use the machine indicated as `polykube1`
|
||||
|
||||
- This machine:
|
||||
|
||||
- runs Ubuntu LTS
|
||||
|
||||
- has Kubernetes, etcd, and CNI binaries installed
|
||||
|
||||
- but nothing is running
|
||||
|
||||
---
|
||||
|
||||
## Checking our environment
|
||||
|
||||
- Let's make sure we have everything we need first
|
||||
|
||||
.lab[
|
||||
|
||||
- Log into the `polykube1` machine
|
||||
|
||||
- Check available versions:
|
||||
```bash
|
||||
etcd -version
|
||||
kube-apiserver --version
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## The plan
|
||||
|
||||
We'll follow the same methodology as for the "easy" section
|
||||
|
||||
1. Start API server
|
||||
|
||||
2. Interact with it (create Deployment and Service)
|
||||
|
||||
3. See what's broken
|
||||
|
||||
4. Fix it and go back to step 2 until it works!
|
||||
|
||||
---
|
||||
|
||||
## Dealing with multiple processes
|
||||
|
||||
- Again, we are going to start many processes
|
||||
|
||||
- Depending on what you're comfortable with, you can:
|
||||
|
||||
- open multiple windows and multiple SSH connections
|
||||
|
||||
- use a terminal multiplexer like screen or tmux
|
||||
|
||||
- put processes in the background with `&`
|
||||
<br/>(warning: log output might get confusing to read!)
|
||||
|
||||
---
|
||||
|
||||
## Starting API server
|
||||
|
||||
.lab[
|
||||
|
||||
- Try to start the API server:
|
||||
```bash
|
||||
kube-apiserver
|
||||
# It will complain about permission to /var/run/kubernetes
|
||||
|
||||
sudo kube-apiserver
|
||||
# Now it will complain about a bunch of missing flags, including:
|
||||
# --etcd-servers
|
||||
# --service-account-issuer
|
||||
# --service-account-signing-key-file
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
Just like before, we'll need to start etcd.
|
||||
|
||||
But we'll also need some TLS keys!
|
||||
|
||||
---
|
||||
|
||||
## Generating TLS keys
|
||||
|
||||
- There are many ways to generate TLS keys (and certificates)
|
||||
|
||||
- A very popular and modern tool to do that is [cfssl]
|
||||
|
||||
- We're going to use the old-fashioned [openssl] CLI
|
||||
|
||||
- Feel free to use cfssl or any other tool if you prefer!
|
||||
|
||||
[cfssl]: https://github.com/cloudflare/cfssl#using-the-command-line-tool
|
||||
[openssl]: https://www.openssl.org/docs/man3.0/man1/
|
||||
|
||||
---
|
||||
|
||||
## How many keys do we need?
|
||||
|
||||
At the very least, we need the following two keys:
|
||||
|
||||
- ServiceAccount key pair
|
||||
|
||||
- API client key pair, aka "CA key"
|
||||
|
||||
(technically, we will need a *certificate* for that key pair)
|
||||
|
||||
But if we wanted to tighten the cluster security, we'd need many more...
|
||||
|
||||
---
|
||||
|
||||
## The other keys
|
||||
|
||||
These keys are not strictly necessary at this point:
|
||||
|
||||
- etcd key pair
|
||||
|
||||
*without that key, communication with etcd will be insecure*
|
||||
|
||||
- API server endpoint key pair
|
||||
|
||||
*the API server will generate this one automatically if we don't*
|
||||
|
||||
- kubelet key pair (used by API server to connect to kubelets)
|
||||
|
||||
*without that key, commands like kubectl logs/exec will be insecure*
|
||||
|
||||
---
|
||||
|
||||
## Would you like some auth with that?
|
||||
|
||||
If we want to enable authentication and authorization, we also need various API client key pairs signed by the "CA key" mentioned earlier. That would include (non-exhaustive list):
|
||||
|
||||
- controller manager key pair
|
||||
|
||||
- scheduler key pair
|
||||
|
||||
- in most cases: kube-proxy (or equivalent) key pair
|
||||
|
||||
- in most cases: key pairs for the nodes joining the cluster
|
||||
|
||||
(these might be generated through TLS bootstrap tokens)
|
||||
|
||||
- key pairs for users that will interact with the clusters
|
||||
|
||||
(unless another authentication mechanism like OIDC is used)
|
||||
|
||||
---
|
||||
|
||||
## Generating our keys and certificates
|
||||
|
||||
.lab[
|
||||
|
||||
- Generate the ServiceAccount key pair:
|
||||
```bash
|
||||
openssl genrsa -out sa.key 2048
|
||||
```
|
||||
|
||||
- Generate the CA key pair:
|
||||
```bash
|
||||
openssl genrsa -out ca.key 2048
|
||||
```
|
||||
|
||||
- Generate a self-signed certificate for the CA key:
|
||||
```bash
|
||||
openssl x509 -new -key ca.key -out ca.cert -subj /CN=kubernetes/
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Starting etcd
|
||||
|
||||
- This one is easy!
|
||||
|
||||
.lab[
|
||||
|
||||
- Start etcd:
|
||||
```bash
|
||||
etcd
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
Note: if you want a bit of extra challenge, you can try
|
||||
to generate the etcd key pair and use it.
|
||||
|
||||
(You will need to pass it to etcd and to the API server.)
|
||||
|
||||
---
|
||||
|
||||
## Starting API server
|
||||
|
||||
- We need to use the keys and certificate that we just generated
|
||||
|
||||
.lab[
|
||||
|
||||
- Start the API server:
|
||||
```bash
|
||||
sudo kube-apiserver \
|
||||
--etcd-servers=http://localhost:2379 \
|
||||
--service-account-signing-key-file=sa.key \
|
||||
--service-account-issuer=https://kubernetes \
|
||||
--service-account-key-file=sa.key \
|
||||
--client-ca-file=ca.cert
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
The API server should now start.
|
||||
|
||||
But can we really use it? 🤔
|
||||
|
||||
---
|
||||
|
||||
## Trying `kubectl`
|
||||
|
||||
- Let's try some simple `kubectl` command
|
||||
|
||||
.lab[
|
||||
|
||||
- Try to list Namespaces:
|
||||
```bash
|
||||
kubectl get namespaces
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
We're getting an error message like this one:
|
||||
|
||||
```
|
||||
The connection to the server localhost:8080 was refused -
|
||||
did you specify the right host or port?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What's going on?
|
||||
|
||||
- Recent versions of Kubernetes don't support unauthenticated API access
|
||||
|
||||
- The API server doesn't support listening on plain HTTP anymore
|
||||
|
||||
- `kubectl` still tries to connect to `localhost:8080` by default
|
||||
|
||||
- But there is nothing listening there
|
||||
|
||||
- Our API server listens on port 6443, using TLS
|
||||
|
||||
---
|
||||
|
||||
## Trying to access the API server
|
||||
|
||||
- Let's use `curl` first to confirm that everything works correctly
|
||||
|
||||
(and then we will move to `kubectl`)
|
||||
|
||||
.lab[
|
||||
|
||||
- Try to connect with `curl`:
|
||||
```bash
|
||||
curl https://localhost:6443
|
||||
# This will fail because the API server certificate is unknown.
|
||||
```
|
||||
|
||||
- Try again, skipping certificate verification:
|
||||
```bash
|
||||
curl --insecure https://localhost:6443
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
We should now see an `Unauthorized` Kubernetes API error message.
|
||||
</br>
|
||||
We need to authenticate with our key and certificate.
|
||||
|
||||
---
|
||||
|
||||
## Authenticating with the API server
|
||||
|
||||
- For the time being, we can use the CA key and cert directly
|
||||
|
||||
- In a real world scenario, we would *never* do that!
|
||||
|
||||
(because we don't want the CA key to be out there in the wild)
|
||||
|
||||
.lab[
|
||||
|
||||
- Try again, skipping cert verification, and using the CA key and cert:
|
||||
```bash
|
||||
curl --insecure --key ca.key --cert ca.cert https://localhost:6443
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
We should see a list of API routes.
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Doing it right
|
||||
|
||||
In the future, instead of using the CA key and certificate,
|
||||
we should generate a new key, and a certificate for that key,
|
||||
signed by the CA key.
|
||||
|
||||
Then we can use that new key and certificate to authenticate.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
### Generate a key pair
|
||||
openssl genrsa -out user.key
|
||||
|
||||
### Extract the public key
|
||||
openssl pkey -in user.key -out user.pub -pubout
|
||||
|
||||
### Generate a certificate signed by the CA key
|
||||
openssl x509 -new -key ca.key -force_pubkey user.pub -out user.cert \
|
||||
-subj /CN=kubernetes-user/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing a kubeconfig file
|
||||
|
||||
- We now want to use `kubectl` instead of `curl`
|
||||
|
||||
- We'll need to write a kubeconfig file for `kubectl`
|
||||
|
||||
- There are many way to do that; here, we're going to use `kubectl config`
|
||||
|
||||
- We'll need to:
|
||||
|
||||
- set the "cluster" (API server endpoint)
|
||||
|
||||
- set the "credentials" (the key and certficate)
|
||||
|
||||
- set the "context" (referencing the cluster and credentials)
|
||||
|
||||
- use that context (make it the default that `kubectl` will use)
|
||||
|
||||
---
|
||||
|
||||
## Set the cluster
|
||||
|
||||
The "cluster" section holds the API server endpoint.
|
||||
|
||||
.lab[
|
||||
|
||||
- Set the API server endpoint:
|
||||
```bash
|
||||
kubectl config set-cluster polykube --server=https://localhost:6443
|
||||
```
|
||||
|
||||
- Don't verify the API server certificate:
|
||||
```bash
|
||||
kubectl config set-cluster polykube --insecure-skip-tls-verify
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Set the credentials
|
||||
|
||||
The "credentials" section can hold a TLS key and certificate, or a token, or configuration information for a plugin (for instance, when using AWS EKS or GCP GKE, they use a plugin).
|
||||
|
||||
.lab[
|
||||
|
||||
- Set the client key and certificate:
|
||||
```bash
|
||||
kubectl config set-credentials polykube \
|
||||
--client-key ca.key \
|
||||
--client-certificate ca.cert
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Set and use the context
|
||||
|
||||
The "context" section references the "cluster" and "credentials" that we defined earlier.
|
||||
|
||||
(It can also optionally reference a Namespace.)
|
||||
|
||||
.lab[
|
||||
|
||||
- Set the "context":
|
||||
```bash
|
||||
kubectl config set-context polykube --cluster polykube --user polykube
|
||||
```
|
||||
|
||||
- Set that context to be the default context:
|
||||
```bash
|
||||
kubectl config use-context polykube
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Review the kubeconfig filfe
|
||||
|
||||
The kubeconfig file should look like this:
|
||||
|
||||
.small[
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:6443
|
||||
name: polykube
|
||||
contexts:
|
||||
- context:
|
||||
cluster: polykube
|
||||
user: polykube
|
||||
name: polykube
|
||||
current-context: polykube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: polykube
|
||||
user:
|
||||
client-certificate: /root/ca.cert
|
||||
client-key: /root/ca.key
|
||||
```
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Trying the kubeconfig file
|
||||
|
||||
- We should now be able to access our cluster's API!
|
||||
|
||||
.lab[
|
||||
|
||||
- Try to list Namespaces:
|
||||
```bash
|
||||
kubectl get namespaces
|
||||
```
|
||||
]
|
||||
|
||||
This should show the classic `default`, `kube-system`, etc.
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Do we need `--client-ca-file` ?
|
||||
|
||||
Technically, we didn't need to specify the `--client-ca-file` flag!
|
||||
|
||||
But without that flag, no client can be authenticated.
|
||||
|
||||
Which means that we wouldn't be able to issue any API request!
|
||||
|
||||
---
|
||||
|
||||
## Running pods
|
||||
|
||||
- We can now try to create a Deployment
|
||||
|
||||
.lab[
|
||||
|
||||
- Create a Deployment:
|
||||
```bash
|
||||
kubectl create deployment blue --image=jpetazzo/color
|
||||
```
|
||||
|
||||
- Check the results:
|
||||
```bash
|
||||
kubectl get deployments,replicasets,pods
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
Our Deployment exists, but not the Replica Set or Pod.
|
||||
|
||||
We need to run the controller manager.
|
||||
|
||||
---
|
||||
|
||||
## Running the controller manager
|
||||
|
||||
- Previously, we used the `--master` flag to pass the API server address
|
||||
|
||||
- Now, we need to authenticate properly
|
||||
|
||||
- The simplest way at this point is probably to use the same kubeconfig file!
|
||||
|
||||
.lab[
|
||||
|
||||
- Start the controller manager:
|
||||
```bash
|
||||
kube-controller-manager --kubeconfig .kube/config
|
||||
```
|
||||
|
||||
- Check the results:
|
||||
```bash
|
||||
kubectl get deployments,replicasets,pods
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## What's next?
|
||||
|
||||
- Normally, the last commands showed us a Pod in `Pending` state
|
||||
|
||||
- We need two things to continue:
|
||||
|
||||
- the scheduler (to assign the Pod to a Node)
|
||||
|
||||
- a Node!
|
||||
|
||||
- We're going to run `kubelet` to register the Node with the cluster
|
||||
|
||||
---
|
||||
|
||||
## Running `kubelet`
|
||||
|
||||
- Let's try to run `kubelet` and see what happens!
|
||||
|
||||
.lab[
|
||||
|
||||
- Start `kubelet`:
|
||||
```bash
|
||||
sudo kubelet
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
We should see an error about connecting to `containerd.sock`.
|
||||
|
||||
We need to run a container engine!
|
||||
|
||||
(For instance, `containerd`.)
|
||||
|
||||
---
|
||||
|
||||
## Running `containerd`
|
||||
|
||||
- We need to install and start `containerd`
|
||||
|
||||
- You could try another engine if you wanted
|
||||
|
||||
(but there might be complications!)
|
||||
|
||||
.lab[
|
||||
|
||||
- Install `containerd`:
|
||||
```bash
|
||||
sudo apt-get install containerd
|
||||
```
|
||||
|
||||
- Start `containerd`:
|
||||
```bash
|
||||
sudo containerd
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Configuring `containerd`
|
||||
|
||||
Depending on how we install `containerd`, it might need a bit of extra configuration.
|
||||
|
||||
Watch for the following symptoms:
|
||||
|
||||
- `containerd` refuses to start
|
||||
|
||||
(rare, unless there is an *invalid* configuration)
|
||||
|
||||
- `containerd` starts but `kubelet` can't connect
|
||||
|
||||
(could be the case if the configuration disables the CRI socket)
|
||||
|
||||
- `containerd` starts and things work but Pods keep being killed
|
||||
|
||||
(may happen if there is a mismatch in the cgroups driver)
|
||||
|
||||
---
|
||||
|
||||
## Starting `kubelet` for good
|
||||
|
||||
- Now that `containerd` is running, `kubelet` should start!
|
||||
|
||||
.lab[
|
||||
|
||||
- Try to start `kubelet`:
|
||||
```bash
|
||||
sudo kubelet
|
||||
```
|
||||
|
||||
- In another terminal, check if our Node is now visible:
|
||||
```bash
|
||||
sudo kubectl get nodes
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
`kubelet` should now start, but our Node doesn't show up in `kubectl get nodes`!
|
||||
|
||||
This is because without a kubeconfig file, `kubelet` runs in standalone mode:
|
||||
<br/>
|
||||
it will not connect to a Kubernetes API server, and will only start *static pods*.
|
||||
|
||||
---
|
||||
|
||||
## Passing the kubeconfig file
|
||||
|
||||
- Let's start `kubelet` again, with our kubeconfig file
|
||||
|
||||
.lab[
|
||||
|
||||
- Stop `kubelet` (e.g. with `Ctrl-C`)
|
||||
|
||||
- Restart it with the kubeconfig file:
|
||||
```bash
|
||||
sudo kubelet --kubeconfig .kube/config
|
||||
```
|
||||
|
||||
- Check our list of Nodes:
|
||||
```bash
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
This time, our Node should show up!
|
||||
|
||||
---
|
||||
|
||||
## Node readiness
|
||||
|
||||
- However, our Node shows up as `NotReady`
|
||||
|
||||
- If we wait a few minutes, the `kubelet` logs will tell us why:
|
||||
|
||||
*we're missing a CNI configuration!*
|
||||
|
||||
- As a result, the containers can't be connected to the network
|
||||
|
||||
- `kubelet` detects that and doesn't become `Ready` until this is fixed
|
||||
|
||||
---
|
||||
|
||||
## CNI configuration
|
||||
|
||||
- We need to provide a CNI configuration
|
||||
|
||||
- This is a file in `/etc/cni/net.d`
|
||||
|
||||
(the name of the file doesn't matter; the first file in lexicographic order will be used)
|
||||
|
||||
- Usually, when installing a "CNI plugin¹", this file gets installed automatically
|
||||
|
||||
- Here, we are going to write that file manually
|
||||
|
||||
.footnote[¹Technically, a *pod network*; typically running as a DaemonSet, which will install the file with a `hostPath` volume.]
|
||||
|
||||
---
|
||||
|
||||
## Our CNI configuration
|
||||
|
||||
Create the following file in e.g. `/etc/cni/net.d/kube.conf`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "kube",
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isDefaultGateway": true,
|
||||
"ipMasq": true,
|
||||
"hairpinMode": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.1.0/24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's all we need - `kubelet` will detect and validate the file automatically!
|
||||
|
||||
---
|
||||
|
||||
## Checking our Node again
|
||||
|
||||
- After a short time (typically about 10 seconds) the Node should be `Ready`
|
||||
|
||||
.lab[
|
||||
|
||||
- Wait until the Node is `Ready`:
|
||||
```bash
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
If the Node doesn't show up as `Ready`, check the `kubelet` logs.
|
||||
|
||||
---
|
||||
|
||||
## What's next?
|
||||
|
||||
- At this point, we have a `Pending` Pod and a `Ready` Node
|
||||
|
||||
- All we need is the scheduler to bind the former to the latter
|
||||
|
||||
.lab[
|
||||
|
||||
- Run the scheduler:
|
||||
```bash
|
||||
kube-scheduler --kubeconfig .kube/config
|
||||
```
|
||||
|
||||
- Check that the Pod gets assigned to the Node and becomes `Running`:
|
||||
```bash
|
||||
kubectl get pods
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Check network access
|
||||
|
||||
- Let's check that we can connect to our Pod, and that the Pod can connect outside
|
||||
|
||||
.lab[
|
||||
|
||||
- Get the Pod's IP address:
|
||||
```bash
|
||||
kubectl get pods -o wide
|
||||
```
|
||||
|
||||
- Connect to the Pod (make sure to update the IP address):
|
||||
```bash
|
||||
curl `10.1.1.2`
|
||||
```
|
||||
|
||||
- Check that the Pod has external connectivity too:
|
||||
```bash
|
||||
kubectl exec `blue-xxxxxxxxxx-yyyyy` -- ping -c3 1.1
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Expose our Deployment
|
||||
|
||||
- We can now try to expose the Deployment and connect to the ClusterIP
|
||||
|
||||
.lab[
|
||||
|
||||
- Expose the Deployment:
|
||||
```bash
|
||||
kubectl expose deployment blue --port=80
|
||||
```
|
||||
|
||||
- Retrieve the ClusterIP:
|
||||
```bash
|
||||
kubectl get services
|
||||
```
|
||||
|
||||
- Try to connect to the ClusterIP:
|
||||
```bash
|
||||
curl `10.0.0.42`
|
||||
```
|
||||
]
|
||||
|
||||
At this point, it won't work - we need to run `kube-proxy`!
|
||||
|
||||
---
|
||||
|
||||
## Running `kube-proxy`
|
||||
|
||||
- We need to run `kube-proxy`
|
||||
|
||||
(also passing it our kubeconfig file)
|
||||
|
||||
.lab[
|
||||
|
||||
- Run `kube-proxy`:
|
||||
```bash
|
||||
sudo kube-proxy --kubeconfig .kube/config
|
||||
```
|
||||
|
||||
- Try again to connect to the ClusterIP:
|
||||
```bash
|
||||
curl `10.0.0.42`
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
This time, it should work.
|
||||
|
||||
---
|
||||
|
||||
## What's next?
|
||||
|
||||
- Scale up the Deployment, and check that load balancing works properly
|
||||
|
||||
- Enable RBAC, and generate individual certificates for each controller
|
||||
|
||||
(check the [certificate paths][certpath] section in the Kubernetes documentatio
|
||||
for a detailed list of all the certificates and keys that are used by the
|
||||
control plane, and which flags are used by which components to configure them!)
|
||||
|
||||
- Add more nodes to the cluster
|
||||
|
||||
*Feel free to try these if you want to get additional hands-on experience!*
|
||||
|
||||
[certpath]: https://kubernetes.io/docs/setup/best-practices/certificates/#certificate-paths
|
||||
|
||||
???
|
||||
|
||||
:EN:- Setting up control plane certificates
|
||||
:EN:- Implementing a basic CNI configuration
|
||||
:FR:- Mettre en place les certificats du plan de contrôle
|
||||
:FR:- Réaliser un configuration CNI basique
|
||||
@@ -32,12 +32,14 @@ content:
|
||||
- k8s/architecture.md
|
||||
#- k8s/internal-apis.md
|
||||
- k8s/deploymentslideshow.md
|
||||
- k8s/dmuc.md
|
||||
- k8s/dmuc-easy.md
|
||||
-
|
||||
- k8s/multinode.md
|
||||
- k8s/cni.md
|
||||
- k8s/dmuc-medium.md
|
||||
- k8s/dmuc-hard.md
|
||||
#- k8s/multinode.md
|
||||
#- k8s/cni.md
|
||||
- k8s/cni-internals.md
|
||||
- k8s/interco.md
|
||||
#- k8s/interco.md
|
||||
-
|
||||
- k8s/apilb.md
|
||||
#- k8s/setup-overview.md
|
||||
|
||||
@@ -32,11 +32,13 @@ content:
|
||||
- k8s/architecture.md
|
||||
- k8s/internal-apis.md
|
||||
- k8s/deploymentslideshow.md
|
||||
- k8s/dmuc.md
|
||||
- - k8s/multinode.md
|
||||
- k8s/cni.md
|
||||
- k8s/dmuc-easy.md
|
||||
- - k8s/dmuc-medium.md
|
||||
- k8s/dmuc-hard.md
|
||||
#- k8s/multinode.md
|
||||
#- k8s/cni.md
|
||||
- k8s/cni-internals.md
|
||||
- k8s/interco.md
|
||||
#- k8s/interco.md
|
||||
- - k8s/apilb.md
|
||||
- k8s/setup-overview.md
|
||||
#- k8s/setup-devel.md
|
||||
|
||||
@@ -30,13 +30,15 @@ content:
|
||||
- k8s/architecture.md
|
||||
- k8s/internal-apis.md
|
||||
- k8s/deploymentslideshow.md
|
||||
- k8s/dmuc.md
|
||||
- k8s/dmuc-easy.md
|
||||
- #2
|
||||
- k8s/multinode.md
|
||||
- k8s/cni.md
|
||||
- k8s/interco.md
|
||||
- #3
|
||||
- k8s/dmuc-medium.md
|
||||
- k8s/dmuc-hard.md
|
||||
#- k8s/multinode.md
|
||||
#- k8s/cni.md
|
||||
#- k8s/interco.md
|
||||
- k8s/cni-internals.md
|
||||
- #3
|
||||
- k8s/apilb.md
|
||||
- k8s/control-plane-auth.md
|
||||
- |
|
||||
|
||||
@@ -151,9 +151,11 @@ content:
|
||||
- k8s/owners-and-dependents.md
|
||||
- k8s/events.md
|
||||
-
|
||||
- k8s/dmuc.md
|
||||
- k8s/multinode.md
|
||||
- k8s/cni.md
|
||||
- k8s/dmuc-easy.md
|
||||
- k8s/dmuc-medium.md
|
||||
- k8s/dmuc-hard.md
|
||||
#- k8s/multinode.md
|
||||
#- k8s/cni.md
|
||||
- k8s/cni-internals.md
|
||||
- k8s/apilb.md
|
||||
- k8s/staticpods.md
|
||||
|
||||
Reference in New Issue
Block a user