Puppet und Kubernetes
Tags: puppet, kubernetes, automation, devops
Published: 2023-08-09 14:46:17

Heute zeigen wir, wie man einen Kubernetes Cluster mit Puppet aufsetzen und verwalten kann.

## Puppet Modul Auswahl

Es gibt einige Puppet Module auf der [Puppet Forge](https://forge.puppet.com), mit denen man Kubernetes verwalten kann.
So findet man unter anderem die Module [Puppetlabs Kubernetes](https://forge.puppet.com/modules/puppetlabs/kubernetes) und [Voxpupuli k8s](https://forge.puppet.com/modules/puppet/k8s).
Alle weiteren Module haben seit geraumer Zeit keine Aktualisierungen mehr erhalten. Daher betrachten wir diese Module als verwaist.

Hinweis: auf dem [CfgMgmtCamp 2023](https://cfgmgmtcamp.eu/ghent2023) hat die Puppet Open Source Community mit den Puppet Mitarbeitern geredet und darum gebeten, das puppetlabs-kubernetes Modul zu archivieren. Dieses Modul benötigt für das initiale Setup eine bereits laufende Kubernetes Umgebung und möchte die Konfiguration mit Hilfe eines besonderen Containers auslesen.

Wir empfehlen dringend, das neue, moderne Voxpupuli k8s Modul zu verwenden.

## Puppet-K8S Modul

Mit dem Puppet K8S Modul kann man Controller Nodes und Worker Nodes einrichten. Beide benötigen dazu die Klasse `k8s`.
Alle Einstellungen können mit Hilfe von Hiera Daten vorgenommen werden. Dabei sind die Parameter über 3 Klassen verteilt:

Allgemeine Daten: `k8s` Klasse
Controller Daten: `k8s::server` Klasse
Worker Daten: `k8s::node` Klasse

Ein einfacher Cluster benötigt die folgenden Parameter:

### Kubernetes controller (apiserver, controller-manager und scheduler)

```yaml
k8s::role: 'server'
k8s::master: 'http://controller-0.example.com:6443'
```

Das Setup der notwendigen etcd Server Instanzen kann auf 2 unterschiedliche Arten vorgenommen werden:

1. Statische Liste (FQDN)
1. Verwendung von PuppetDB

```yaml
# PuppetDB
k8s::puppetdb_discovery: true
# oder Liste
k8s::server::etcd_servers:
  - 'https://node1:2379'
  - 'https://node2:2379'
  - 'https://node3:2379'
```

Das Erzeugen der für Kubernetes notwendigen Zertifikate kann das Modul übernehmen. Man kann auch bestehende Zertifikate hinterlegen.
Beim Aufsetzen einer geclusterten Control Plane, muss man beachten, dass die Zertifikate des ersten Cluster Control Nodes auf die weiteren Control Nodes verteilt wird. Dies betrifft die folgenden Verzeichnisse:
 `/etc/kubernetes/certs` und `/var/lib/etcd/certs`.
 Die Verteilung ist noch nicht Bestandteil des Moduls.

```yaml
k8s::server::etcd::generate_ca: true
k8s::server::generate_ca: true
```

### Kubernetes worker (kubelet)

```yaml
k8s::role: 'node'
k82::master: 'https://controller-0.example.com:6443'
```

## Beispiel für containerd und bridge networking

### Controller

```yaml
k8s::role: 'server'
k8s::master: 'https://controller-0.example.com:6443' # default
k8s::container_manager: 'containerd'

k8s::manage_firewall: true    # default: false
k8s::puppetdb_discovery: true # default: false

k8s::server::node_on_server: false # don't use controller as worker
k8s::server::etcd::generate_ca: true
k8s::server::generate_ca: true

# bind apiserver to a interface the worker and controller can communicate with
k8s::server::apiserver::advertise_address: "%{facts.networking.interfaces.enp0s8.ip}"

# flannel networking is default in the module
# but we want to showcase bridged networking here
k8s::server::resources::manage_flannel: false

k8s::service_cluster_cidr: '10.20.0.0/20' # overlay network for cluster services
k8s::cluster_cidr: '10.20.16.0/20'        # overlay network for the pods in the cluster
```

### Worker

```yaml
k8s::role: 'node'
k8s::master: 'https://controller-0.example.com:6443' # default
k8s::container_manager: 'containerd'

k8s::manage_fireall: true
k8s::puppetdb_discovery: true

# the same as in k8s::server::resources::bootstrap::secret but prefixed with "puppet."
k8s::node::node_token: "puppet.%{lookup('k8s::server::resources::bootstrap::secret')}"

# for debugging
k8s::node::manage_crictl: true
k8s::install::crictl::config:
  'runtime-endpoint': 'unix:///run/containerd/containerd.sock'
  'image-endpoint': 'unix:///run/containerd/containerd.sock'

k8s::service_cluster_cidr: '10.20.0.0/20' # overlay network for cluster services
k8s::cluster_cidr: '10.20.16.0/20'        # overlay network for the pods in the cluster
```

### Shared data

```yaml
lookup_options:
  k8s::server::resources::bootstrap::secret:
    convert_to: Sensitive

# Sensitive[Pattern[/^[a-z0-9]{16}$/]]
k8s::server::resources::bootstrap::secret: 'a23456789bcdefgh'
```

## Beispiel für containerd und cilium

Zuerst wird kube-proxy benötigt um ein initiales Setup zu erzeugen.
Danach wird Cilium installiert, welches kube-proxy ersetzt.
Nach der Cilium Installation kann kube-proxy wieder entfernt werden.

### Controller

```yaml
k8s::role: 'server'
k8s::master: 'https://controller-0.example.com:6443' # default
k8s::container_manager: 'containerd' # default: crio

k8s::manage_firewall: true    # default: false
k8s::puppetdb_discovery: true # default: false

k8s::server::node_on_server: false # don't use controller as worker
k8s::server::etcd::generate_ca: true
k8s::server::generate_ca: true


# bind apiserver to a interface the worker and controller can communicate with
k8s::server::apiserver::advertise_address: "%{facts.networking.interfaces.enp0s8.ip}"

# we want to showcase cilium here
k8s::server::resources::manage_flannel: false
```

### Worker

```yaml
k8s::role: 'node'
k8s::master: 'https://controller-0.example.com:6443'
k8s::container_manager: 'containerd'


k8s::manage_firewall: true
k8s::puppetdb_discovery: true

# the same as in k8s::server::resources::bootstrap::secret but prefixed with "puppet."
k8s::node::node_token: "puppet.%{lookup('k8s::server::resources::bootstrap::secret')}"

# for debugging
k8s::node::manage_crictl: true
k8s::install::crictl::config:
  'runtime-endpoint': 'unix:///run/containerd/containerd.sock'
  'image-endpoint': 'unix:///run/containerd/containerd.sock'

k8s::service_cluster_cidr: '10.20.0.0/20' # overlay network for cluster services
k8s::cluster_cidr: '10.20.16.0/20'        # overlay network for the pods in the cluster
```

### Shared data

```yaml
lookup_options:
  k8s::server::resources::bootstrap::secret:
    convert_to: Sensitive

# Sensitive[Pattern[/^[a-z0-9]{16}$/]]
k8s::server::resources::bootstrap::secret: 'a23456789bcdefgh'
```

Nach dem initialen Setup kann Cilium installiert werden.

### Initialize cilium

⚠️ Alle hier genannten Schritte müssen auf EINEM der Controller ausgeführt werden!

Das Cilium Paket muss heruntergeladen werden. Dies ist noch nicht Bestandteil des Moduls. Die notwendige Konfiguration erfolgt in der Datei cilium-values.yaml.

Installation laut [cilium quick installation](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/)

```bash
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/master/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
```

Die cilium-values.yaml Datei

```yaml
---
k8sServiceHost: controller-0.example.com
k8sServicePort: 6443
autoDirectNodeRoutes: true
rollOutCiliumPods: true
kubeProxyReplacement: strict
tunnel: disabled
ipv4NativeRoutingCIDR: 10.20.16.0/20 # overlay network for the pods in the cluster
priorityClassName: system-cluster-critical
ipam:
  mode: kubernetes
nodePort:
  enabled: true
  directRoutingDevice: ens192
bpf:
  clockProbe: true
  masquerade: true
  tproxy: true
loadBalancer:
  mode: hybrid
  algorithm: maglev
  hashSeed: uWul3Twb7mKCmNSN
hubble:
  relay:
    enabled: true
    rollOutPods: true
  ui:
    enabled: true
    rollOutPods: true
operator:
  rollOutPods: true
  prometheus:
    enabled: true
hostPort:
  enabled: true
ipv4:
  enabled: true
ipv6:
  enabled: true
socketLB:
  enabled: true
prometheus:
  enabled: true
```

Vor der Cilium Installation muss man prüfen, dass alle Worker Nodes verbunden sind. Diese können auch im Status NotReady sein.

```bash
# kubectl get nodes

NAME                   STATUS     ROLES    AGE   VERSION
worker-1.example.com   NotReady   <none>   83s   v1.26.4
worker-2.example.com   NotReady   <none>   83s   v1.26.4
```

Installation von Cilium:

```bash
cilium install --version v1.13.2 --helm-values /path/to/cilium-values.yaml
```

```text
ℹ️  Using Cilium version 1.13.2
🔮 Auto-detected cluster name: default
🔮 Auto-detected datapath mode: tunnel
🔮 Auto-detected kube-proxy has not been installed
ℹ️  Cilium will fully replace all functionalities of kube-proxy
ℹ️  helm template --namespace kube-system cilium cilium/cilium --version 1.13.2 --set autoDirectNodeRoutes=true,bpf.clockProbe=true,bpf.masquerade=true,bpf.tproxy=true,cluster.id=0,cluster.name=default,encryption.nodeEncryption=false,hostPort.enabled=true,hubble.relay.enabled=true,hubble.relay.rollOutPods=true,hubble.ui.enabled=true,hubble.ui.rollOutPods=true,ipam.mode=kubernetes,ipv4.enabled=true,ipv4NativeRoutingCIDR=10.20.16.0/20,ipv6.enabled=true,k8sServiceHost=localhost,k8sServicePort=6443,kubeProxyReplacement=strict,loadBalancer.algorithm=maglev,loadBalancer.hashSeed=uWul3Twb7mKCmNSN,loadBalancer.mode=hybrid,nodePort.directRoutingDevice=enp0s8,nodePort.enabled=true,operator.prometheus.enabled=true,operator.replicas=1,operator.rollOutPods=true,priorityClassName=system-cluster-critical,prometheus.enabled=true,rollOutCiliumPods=true,serviceAccounts.cilium.name=cilium,serviceAccounts.operator.name=cilium-operator,socketLB.enabled=true,tunnel=disabled
ℹ️  Storing helm values file in kube-system/cilium-cli-helm-values Secret
🔑 Created CA in secret cilium-ca
🔑 Generating certificates for Hubble...
🚀 Creating Service accounts...
🚀 Creating Cluster roles...
🚀 Creating ConfigMap for Cilium version 1.13.2...
🚀 Creating Agent DaemonSet...
🚀 Creating Operator Deployment...
⌛ Waiting for Cilium to be installed and ready...
✅ Cilium was successfully installed! Run 'cilium status' to view installation health
```

Nun sollten alle Worker im Ready Status sein:

```bash
kubectl get nodes

NAME                   STATUS   ROLES    AGE   VERSION
worker-1.example.com   Ready    <none>   5m    v1.26.4
worker-2.example.com   Ready    <none>   5m    v1.26.4
```

```bash
cilium status

    /¯¯\
 /¯¯\__/¯¯\    Cilium:          OK
 \__/¯¯\__/    Operator:        OK
 /¯¯\__/¯¯\    Hubble Relay:    disabled
 \__/¯¯\__/    ClusterMesh:     disabled
    \__/

Deployment        cilium-operator    Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet         cilium             Desired: 1, Ready: 1/1, Available: 1/1
Containers:       cilium             Running: 1
                  cilium-operator    Running: 1
Cluster Pods:     1/1 managed by Cilium
Image versions    cilium             quay.io/cilium/cilium:v1.13.2@sha256:85708b11d45647c35b9288e0de0706d24a5ce8a378166cadc700f756cc1a38d6: 1
                  cilium-operator    quay.io/cilium/operator-generic:v1.13.2@sha256:a1982c0a22297aaac3563e428c330e17668305a41865a842dec53d241c5490ab: 1
```

Nach der erfolgreichen Installation von Cilium kann kube-proxy deinstalliert werden:

```yaml
k8s::server::resources::kube_proxy::ensure: absent
```

## Weitere Dokumentation

- [Class reference](https://github.com/voxpupuli/puppet-k8s/blob/master/REFERENCE.md)
- Examples
  - [Simple bridged setup](https://github.com/voxpupuli/puppet-k8s/blob/master/examples/simple_setup/Readme.md)
  - [Cilium setup](https://github.com/voxpupuli/puppet-k8s/blob/master/examples/cilium/Readme.md)