Ziel des Artikels ist es, die notwendigen Schritte zum Aufsetzen eines einfachen Kubernetes Systems zu dokumentieren.
Die Anforderungen im Detail:
- Lauffähig auf einem einzelnen Netcup vServer (keine High-Availability)
- Integration in Gitlab AutoDevops möglich
- IPv4/IPv6 Dual-Stack fähig
- Mit Kubernetes Network Policies abgesichert
- Nutzung der lokalen Platte als Persistent Storage
- Mögliche Erweiterbarkeit auf mehrere Nodes
Basisimage
- Ubuntu 18.04 LTS Docker Image von Netcup, kleine Partition oder
- Ubuntu 20.04 LTS Minimal Image von Netcup, kleine Partition
- oder entsprechend neuer
Absicherungen basierend auf
System absichern
Mit dem vorkonfigurierten Root-User per SSH:
1
2
3
4
5
6
7
8
9
10
11
12
|
adduser jo
#
adduser jo sudo
# nur bei 20.04
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
apt install docker-ce docker-ce-cli containerd.io
#
# SSH verlegen um Platz für Gitlab SSH Port zu schaffen:
sed -i -e 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
systemctl restart ssh
|
Mit dem neuen Benutzer jo
per SSH:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
mkdir .ssh
ssh-keygen
echo "ssh-rsa AAAAB...3w== ssh-jowi-privat-aes" >> ~/.ssh/authorized_keys
sudo passwd -l root
sudo ufw allow OpenSSH
sudo ufw allow 2222
sudo ufw allow out http
sudo ufw allow out https
sudo ufw allow out 22
# allow dns queries
sudo ufw allow out 53/udp
# allow ntp systemd time syncing
sudo ufw allow out 123
sudo ufw default deny outgoing
sudo ufw default deny incoming
sudo ufw enable
|
Partitionierung
1
2
3
4
5
6
7
8
9
10
11
12
|
sudo fdisk /dev/sda
# create partition 4 and 5 with 75G and rest
sudo mke2fs /dev/sda4
sudo mke2fs /dev/sda5
echo "/dev/sda4 /var ext4 defaults 0 2" | sudo tee -a /etc/fstab
echo "/dev/sda5 /srv ext4 defaults 0 2" | sudo tee -a /etc/fstab
sudo mkdir /mnt/tmp
sudo mount /dev/sda4 /mnt/tmp
sudo mv /var/* /mnt/tmp
sudo mount /dev/sda4 /var
sudo mount /dev/sda5 /srv
sudo umount /mnt/tmp
|
Automatische Updates aktivieren
1
2
3
4
5
6
7
|
sudo sed -i 's/\/\/Unattended-Upgrade::Automatic-Reboot/Unattended-Upgrade::Automatic-Reboot/g' /etc/apt/apt.conf.d/50unattended-upgrades
cat <<EOF | sudo tee -a /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
EOF
|
Aufsetzen des Clusters
Basierend auf der offiziellen Dokumentation. Zusätzlich IPv6 Forwarding aktivieren (Zeile 4).
Vorbereitungen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl --system
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
|
1
2
3
4
5
6
7
8
9
10
11
|
sudo systemctl enable docker
sudo systemctl restart docker
sudo ufw allow 6443
sudo ufw allow out to 172.18.0.0/24
sudo ufw allow out to 172.18.1.0/24
sudo ufw allow out to fc00::/64
sudo ufw allow out to fc01::/110
sudo ufw allow in from 172.18.0.0/24
sudo ufw allow in from 172.18.1.0/24
sudo ufw allow in from fc00::/64
sudo ufw allow in from fc01::/110
|
Paketinstallation
Zum Zeitpunkt des Artikels wird Version 1.22.4 installiert.
1
2
3
4
5
6
7
8
9
10
|
sudo apt-get update && sudo apt-get upgrade
sudo apt-get install -y apt-transport-https curl mc ipvsadm
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo snap install helm --classic
helm repo add stable https://charts.helm.sh/stable
helm repo update
|
Bash Tab-Vervollständigung analog zu https://kubernetes.io/de/docs/tasks/tools/install-kubectl/
1
|
echo 'source <(kubectl completion bash)' >> ~/.bashrc
|
Kubeadm Setup
1
|
sudo kubeadm init --config https://gitlab.com/jowi24/kubernetes-setup/-/raw/main/kubeadm/config.yaml
|
1
2
3
4
5
6
7
8
|
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl taint nodes --all node-role.kubernetes.io/master-
# edit /etc/kubernetes/manifests/kube-apiserver.yaml
# add ServerSideApply=false to --feature-gate parameter
|
Calico Networking Setup
Basierend auf https://docs.projectcalico.org/manifests/calico.yaml
1
|
kubectl apply -f https://gitlab.com/jowi24/kubernetes-setup/-/raw/main/kubeadm/calico.yaml
|
Hostpath Provider
1
|
kubectl apply -f https://gitlab.com/jowi24/kubernetes-setup/-/raw/main/persistence/hostpath-provisioner.yaml
|
NGINX Ingress
Auf Basis von https://github.com/helm/charts/tree/master/stable/nginx-ingress
1
2
3
|
sudo ufw allow in 22
sudo ufw allow in http
sudo ufw allow in https
|
Siehe Skripte unter https://gitlab.com/jowi24/kubernetes-setup/-/tree/main/ingress
1
2
|
cd ingress
./install.sh
|
Proxy für SMTP/IMAP
Helm-Konfiguration erweitern:
1
2
3
4
5
6
7
|
"22": "gitlab/gitlab-gitlab-shell:22",
"25": "mail/exim-auto-deploy:25::PROXY",
"53": "jowi-dyndns/production-auto-deploy:53",
"143": "mail/dovecot-auto-deploy:143::PROXY",
"465": "mail/exim-auto-deploy:465::PROXY",
"587": "mail/exim-auto-deploy:587::PROXY",
"993": "mail/dovecot-auto-deploy:993::PROXY"
|
Firewall freischalten:
1
2
3
4
5
6
7
|
sudo ufw allow 22
sudo ufw allow 25
sudo ufw allow 53
sudo ufw allow 143
sudo ufw allow 465
sudo ufw allow 587
sudo ufw allow 993
|
DynDNS Dienst
Auf Basis von https://gist.github.com/zoilomora/f7d264cefbb589f3f1b1fc2cea2c844c
SystemD resolved deaktivieren:
1
2
3
4
|
sudo systemctl disable systemd-resolved.service
sudo systemctl stop systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf
|
Certmanager
Automatisches Ausstellen von Zertifikaten mittels LetsEncrypt
Auf Basis von https://cert-manager.io/docs/tutorials/acme/ingress/
Achtung: ein abweichender Namespace (z.B. kube-cert-manager
) führt leider zu Problemen, hier müssten die CRDs modifiziert werden. (TODO: prüfen ob das mit helm chart noch stimmt)
Siehe Skripte unter https://gitlab.com/jowi24/kubernetes-setup/-/tree/main/certmanager
1
2
3
|
cd certmanager
./create-issuer.sh
./install.sh
|
Verwendung: Ein Ingress muss folgende Annotation aufweisen um vom Reverse-Proxy geroutet zu werden und ein TLS-Zertifikat mit dem defaultIssuer
zu erhalten:
1
2
3
4
5
|
metadata:
name: kuard
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
|
Kubernetes Dashboard
Auf Basis von:
Siehe Skripte unter https://gitlab.com/jowi24/kubernetes-setup/-/tree/main/dashboard
1
2
|
cd dashboard
./install.sh
|
Gitlab
Installation
Verwendendung des Standard-Helm-Charts unter Berücksichtigung folgender Modifikationen https://docs.gitlab.com/charts/advanced/external-nginx/index.html and https://docs.gitlab.com/charts/installation/tls.html
1
2
3
|
helm repo add gitlab https://charts.gitlab.io/
kubectl create namespace gitlab
kubectl create secret generic gitlab-smtp-creds --from-literal=password='xxx' -n gitlab
|
Gitlab Integration
Den Cluster in Gitlab hinzufügen (nicht durch Gitlab managen lassen):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
cat <<EOF > gitlab-admin-service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-admin
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: gitlab-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: gitlab-admin
namespace: kube-system
EOF
kubectl apply -f gitlab-admin-service-account.yaml
|
Folgende Informationen in der Einrichtung angeben:
1
2
3
4
5
6
7
8
|
# API URL
kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'
# CA Certificate
kubectl get secret $(kubectl get secrets | grep token | cut -d " " -f 1) \
-o jsonpath="{['data']['ca\.crt']}" | base64 --decode
# Token
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret |\
grep gitlab-admin | awk '{print $1}')
|
Vorhandene Docker Projekte für Kubernetes anpassen
1
2
3
4
|
cd repo
cat <<EOF > .gitlab/auto-deploy-values.yaml
deploymentApiVersion: apps/v1
EOF
|
- Auto-Devops konfigurieren mit eingenem Namespace und Domain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# derived from https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
include:
- template: Auto-DevOps.gitlab-ci.yml
variables:
CODE_QUALITY_DISABLED: "1"
POSTGRES_ENABLED: "false"
TEST_DISABLED: "1"
PERFORMANCE_DISABLED: "1"
ADDITIONAL_HOSTS: "app2.joachim-wilke.de"
production:
environment:
name: production
url: https://app.joachim-wilke.de
kubernetes:
namespace: jowi-app
|
- Bei Bedarf eigenes Chart auf Basis des offiziellen Charts ableiten (Nutzung von Git Subtree)
1
2
3
4
5
6
|
git remote add -f autodeploy https://gitlab.com/gitlab-org/charts/auto-deploy-app.git
git subtree add --prefix chart autodeploy master --squash
# On each update:
git fetch autodeploy master
git subtree pull --prefix chart autodeploy master --squash
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# allow no egress traffic, ingress only from ingress proxy
networkPolicy:
enabled: true
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector: {}
- podSelector:
matchLabels:
app: "nginx-ingress"
|
Troubleshooting
Zertifikate abgelaufen
Symptom: Fehlermeldung folgender Art
1
2
|
$ kubectl get pods -A
Unable to connect to the server: x509: certificate has expired or is not yet valid: current time 2022-03-25T18:14:53+01:00 is after 2022-03-11T06:28:24Z
|
Die Fehlermeldung kann auch bei Server-Neustart im Log von Kube-Apiserver auftauchen und dann dazu führen, dass der Cluster nicht mehr startet.
Lösung: Zertifikate erneuern und Dienste neu starten
1
2
3
|
sudo kubeadm certs renew all
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo reboot
|
Cheat-Sheet
Firewall
- Get Status:
sudo ufw status verbose
- Applikationsprofile aufrufen:
sudo ufw app list
Kubeadm und Kubectl
- Reset Kubeadm:
kubeadm reset && iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X && ipvsadm -C
- Get Node Status:
kubectl get nodes -o wide
- Get Status:
watch kubectl get all -A
Upgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
apt-mark hold kubeadm kubelet kubectl
apt-get update
apt-get upgrade
# change to new major version
sudo nano /etc/apt/sources.list.d/kubernetes.list
# show available versions
apt-cache policy kubeadm
# upgrade to next minor version
apt-get install -y --allow-change-held-packages kubeadm=1.29.12-1.1
# show possible upgrade paths
kubeadm upgrade plan
# apply upgrade
kubeadm upgrade apply 1.29.12
# --> resolvConf Eintrag in /var/lib/kubelet/config.yaml prüfen (/etc)
# Upgrade verfolgen
kubectl get pods -A -w
kubectl drain v220211146370169713 --ignore-daemonsets --delete-emptydir-data
apt-get install -y --allow-change-held-packages kubelet=1.29.12-1.1 kubectl=1.29.12-1.1
systemctl daemon-reload
systemctl restart kubelet
kubectl uncordon v220211146370169713
# Upgrade verfolgen
kubectl get pods -A -w
|
Offene Punkte
- Bringt der
iptables
proxy mode, im Vergleich zu ipvs
Vorteile? Aktuell wird iptables
von calico i.V.m. DualStack noch nicht unterstützt.
- Anzahl Replicas des Coredns Pods auf 1 beschränken (per default sind es 2)
- Ist es sinnvoll (und bei Netcup technisch machbar), global routbare IPv6 Adressen in den Pods zu verwenden? Siehe auch https://forum.netcup.de/administration-eines-server-vserver/vserver-server-kvm-server/9577-ipv6-addressvergabe-an-docker-container/
- Was hat es mit den Meldungen im Syslog auf sich:
systemd[1551]: Failed to canonicalize path permission denied message
?
- Nginx Ingress Default Backend anpassen (eigene Fehlerseite)
-
- Alle Checks des Kube-Bench bestehen
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/master/job-master.yaml