---
# System prepended metadata

title: k8s on bare-metal
tags: [distributed-systems]

---

# k8s on bare-metal

[TOC]
## qemu-guest-agent
Do this if the machine is an virtual machine
```bash=
sudo apt install -y qemu-guest-agent
```

## swapoff
```bash=
sudo swapoff -a
sudo vim /etc/fstab
# comment off /swap.img ...
```

## docker

https://docs.docker.com/engine/install/debian/#install-using-the-repository

https://docs.docker.com/engine/install/linux-postinstall/

```bash=
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# fix
sudo sed -i 's/debian/ubuntu/g' /etc/apt/sources.list.d/docker.list

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

post install
```bash=
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world
```

## cri-dockerd
https://github.com/Mirantis/cri-dockerd

```bash=
curl -LO https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.7/cri-dockerd_0.3.7.3-0.ubuntu-jammy_amd64.deb
sudo apt install ./cri-dockerd_0.3.7.3-0.ubuntu-jammy_amd64.deb
rm ./cri-dockerd_0.3.7.3-0.ubuntu-jammy_amd64.deb
```

## kubeadm + kubelet + kubectl


## Master Node (Control Plane)
https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

### oh-my-bash
https://github.com/ohmybash/oh-my-bash
```bash=
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ohmybash/oh-my-bash/master/tools/install.sh)"
```

### kubeadm init
https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/

```bash=
# sudo kubeadm init --cri-socket /run/cri-dockerd.sock
```

https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/#kubeadm-k8s-io-v1beta3-APIEndpoint
```bash=
KUBEADM_TOKEN=$(kubeadm token generate)
HOST_IP=10.121.240.133

echo "kind: InitConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: $KUBEADM_TOKEN
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
localAPIEndpoint:
  # master ip address
  advertiseAddress: $HOST_IP
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///var/run/cri-dockerd.sock
  imagePullPolicy: IfNotPresent
  # master hostname
  name: $HOSTNAME
  taints: null
---
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
apiServer:
  timeoutForControlPlane: 4m0s
# master ip address:6443
controlPlaneEndpoint: \"$HOST_IP:6443\"
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: registry.k8s.io
kubernetesVersion: 1.28.0
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
scheduler: {}
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd
" > kubeadm-config.yaml

sudo kubeadm init --config kubeadm-config.yaml
```
:::spoiler kdto-0
```bash=
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join 10.121.240.140:6443 --token hwcg4h.0egzhg2iziunw79k \
        --discovery-token-ca-cert-hash sha256:e665256a30b391eaa41c8961c2c23409cc8c9d97473223207dcda2d6a5d2b0c6 \
        --control-plane 

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.121.240.140:6443 --token hwcg4h.0egzhg2iziunw79k \
        --discovery-token-ca-cert-hash sha256:e665256a30b391eaa41c8961c2c23409cc8c9d97473223207dcda2d6a5d2b0c6
```
:::

:::spoiler kdto-single-0
```bash=
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join 10.121.240.133:6443 --token cw5ccx.dc77t52tswgb8367 \
        --discovery-token-ca-cert-hash sha256:d5c3d6a7c425f8f8809b910ea5f31fa377b74f580d30f20c5b577e4b83c4391a \
        --control-plane 

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.121.240.133:6443 --cri-socket unix:///var/run/cri-dockerd.sock --token cw5ccx.dc77t52tswgb8367 \
        --discovery-token-ca-cert-hash sha256:d5c3d6a7c425f8f8809b910ea5f31fa377b74f580d30f20c5b577e4b83c4391a
```
:::

### reset (ONLY WHEN RESETTING)
```bash=
sudo kubeadm reset --cri-socket unix:///var/run/cri-dockerd.sock

# reset iptables
sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F && sudo iptables -X
etcdctl del "" --prefix
sudo rm  -r /etc/cni/net.d/
```

### kube config
```bash=
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
```

### cillium-cli
https://github.com/cilium/cilium-cli/releases/tag/v0.15.0

```bash=
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/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}
```

### cilium

```bash=
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.14.3 --namespace kube-system

cilium version --client
cilium status --wait
cilium connectivity test
```

### fluent-bit

```bash=
git clone https://github.com/fluent/helm-charts
vim helm-charts/charts/fluent-bit/values.yaml
helm install fluent-bit ./helm-charts/charts/fluent-bit

```

[Fluent-bit: a log forwarder](https://docs.fluentbit.io/manual/installation/kubernetes#installing-with-helm-chart)
```bash=
helm repo add fluent https://fluent.github.io/helm-charts
helm upgrade --install fluent-bit fluent/fluent-bit

# export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=fluent-bit,app.kubernetes.io/instance=fluent-bit" -o jsonpath="{.items[0].metadata.name}")
# kubectl --namespace default port-forward $POD_NAME 2020:2020
# curl http://127.0.0.1:2020
```

:::spoiler pv-log-collector.yaml
```bash=
apiVersion: v1
kind: PersistentVolume
metadata:
  name: shared-logs
  labels:
    app: logs-collector
spec:
  accessModes:
    - "ReadWriteOnce"
  storageClassName: manual
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: logs-log-collector-0
    namespace: logging
  capacity:
    storage: 5Gi
  hostPath:
    path: /logs
```
:::
```bash=
kubectl apply -f pv-log-collector.yaml
```

[knative logging](https://knative.dev/docs/serving/observability/logging/collecting-logs/#setting-up-the-collector)
```bash=
# apply the collector
kubectl apply -f https://github.com/knative/docs/raw/main/docs/serving/observability/logging/fluent-bit-collector.yaml
```

```bash=
kubectl edit -n logging configmap/log-collector-config

# add
@INCLUDE output-forward.conf

output-forward.conf: |
  [OUTPUT]
      Name            forward
      Host            log-collector.logging
      Port            24224
      Require_ack_response  True
```

```bash=
kubectl edit configmaps/fluent-bit

# remove all output and add this
[OUTPUT]
  Name forward
  Host log-collector.logging
  Port 24224
  Require_ack_response True
```

```bash=
# Restart fluent-bit by deleting the pod
kubectl delete pods $(kubectl get pods --namespace default -l "app.kubernetes.io/name=fluent-bit,app.kubernetes.io/instance=fluent-bit" -o jsonpath="{.items[0].metadata.name}")

# Wait for the daemonset to start a new pod, log it to check errors
kubectl logs $(kubectl get pods --namespace default -l "app.kubernetes.io/name=fluent-bit,app.kubernetes.io/instance=fluent-bit" -o jsonpath="{.items[0].metadata.name}")
```

Connecting to log collector
```bash=
# Port forward the logging pod
kubectl port-forward --namespace logging service/log-collector 8080:80
```
```bash=
# Also port forward to the machine
ssh -L localhost:8080:localhost:8080 [HOST_IP]
```

Open the website in browser [http://localhost:8080/](http://localhost:8080/)


## Worker Nodes
```bash=
sudo kubeadm join 10.121.240.140:6443 --cri-socket unix:///var/run/cri-dockerd.sock --token <token> \
        --discovery-token-ca-cert-hash sha256:<hash>
# example
# sudo kubeadm join 10.121.240.140:6443 --cri-socket /run/cri-dockerd.sock --token hwcg4h.0egzhg2iziunw79k \
#         --discovery-token-ca-cert-hash sha256:e665256a30b391eaa41c8961c2c23409cc8c9d97473223207dcda2d6a5d2b0c6
```

## Controller

* specify cluster kubeconfig
```bash=
KUBECONFIG=~/.kube/config_kdto-0 kubectl get nodes
```
* Install go

### knative

Install knative
```bash=
# Serving
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-core.yaml

# Eventing
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/eventing-crds.yaml
kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.12.0/eventing-core.yaml
```

metallb
https://metallb.universe.tf/installation/
https://metallb.universe.tf/configuration/
```bash=
# see what changes would be made, returns nonzero returncode if different
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl diff -f - -n kube-system

# actually apply the changes, returns nonzero returncode on errors only
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-system

# install by manifest
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml

echo "apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.100.0/24" > metallb-config.yaml
  
kubectl apply -f metallb-config.yaml
```

kourier
:::spoiler Without metallb and with kind
```bash=
# Without metallb and with kind
curl -LO https://github.com/knative/net-kourier/releases/download/knative-v1.12.1/kourier.yaml
vim kourier.yaml

# nodePort: 31080
# nodePort: 31443
# LoadBalancer -> NodePort

kubectl apply -f kourier.yaml

kubectl patch configmap/config-network \
  --namespace knative-serving \
  --type merge \
  --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'

kubectl patch configmap/config-domain \
  --namespace knative-serving \
  --type merge \
  --patch '{"data":{"127.0.0.1.sslip.io":""}}'

kubectl --namespace kourier-system get service kourier
```
:::
```bash=
# With metallb
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.12.1/kourier.yaml

kubectl --namespace kourier-system get service kourier

kubectl patch configmap/config-network \
  --namespace knative-serving \
  --type merge \
  --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'
```

magic dns (gives a domain to the ip assigned to kourier with metallb)
```bash=
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.12.0/serving-default-domain.yaml
```

knative-cli(kn)
```bash=
git clone https://github.com/knative/client.git knative-cli
cd knative-cli/
hack/build.sh -f
sudo mv kn /usr/local/bin
cd ..
rm -rf knative-cli

kn version
```

hello world service test
:::spoiler To delete the service
```bash=
kn service delete hello
```
:::
```bash=
kn service create hello \
--image gcr.io/knative-samples/helloworld-go \
--port 8080 \
--env TARGET=World
```
