promenade/README.md

238 lines
9.3 KiB
Markdown

# Promenade: Manually Self-hosted Kubernetes via Bootkube
A small howto on how to bring up a self-hosted kubernetes cluster
We'll use [bootkube](https://github.com/kubernetes-incubator/bootkube) to initiate the master-components. First we'll render the assets necessary for bringing up the control plane (apiserver, controller-manger, scheduler, etc). Then we'll start the kubelets which job is it to start the assets but can't do much, because there's no API-server yet. Running `bootkube` once will kick things off then. At a high-level the bootstrapping process looks like this:
![Self-Hosted](./img/self-hosted-moving-parts.png?raw=true "Self-hosted-moving-parts")
Image taken from the [self-hosted proposal](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/self-hosted-kubernetes.md).
This is how the final cluster looks like from a `kubectl` perspective:
![Screenshot](./img/self-hosted.png?raw=true "Screenshot")
Let's start!
## Temporary apiserver: `bootkube`
### Download
```
wget https://github.com/kubernetes-incubator/bootkube/releases/download/v0.3.9/bootkube.tar.gz
tar xvzf bootkube.tar.gz
sudo cp bin/linux/bootkube /usr/bin/
```
### Render the Assets
Exchange `10.7.183.59` with the node you are working on. If you have DNS available group all master node IP addresses behind a CNAME Record and provide this insted.
```
bootkube render --asset-dir=assets --experimental-self-hosted-etcd --etcd-servers=http://10.3.0.15:2379 --api-servers=https://10.7.183.59:443
```
This will generate several things:
- manifests for running apiserver, controller-manager, scheduler, flannel, etcd, dns and kube-proxy
- a `kubeconfig` file for connecting to and authenticating with the apiserver
- TLS assets
## Start the Master Kubelet
### Download `hyperkube`
```
wget http://storage.googleapis.com/kubernetes-release/release/v1.5.3/bin/linux/amd64/hyperkube -O ./hyperkube
sudo mv hyperkube /usr/bin/hyperkube
sudo chmod 755 /usr/bin/hyperkube
```
### Install CNI
```
sudo mkdir -p /opt/cni/bin
wget https://github.com/containernetworking/cni/releases/download/v0.4.0/cni-amd64-v0.4.0.tbz2
sudo tar xjf cni-amd64-v0.4.0.tbz2 -C /opt/cni/bin/
```
### Copy Configuration Files
```
sudo cp assets/auth/kubeconfig /etc/kubernetes/
sudo cp -a assets/manifests /etc/kubernetes/
```
### Start the Kubelet
```
sudo hyperkube kubelet --kubeconfig=/etc/kubernetes/kubeconfig \
--require-kubeconfig \
--cni-conf-dir=/etc/kubernetes/cni/net.d \
--network-plugin=cni \
--lock-file=/var/run/lock/kubelet.lock \
--exit-on-lock-contention \
--pod-manifest-path=/etc/kubernetes/manifests \
--allow-privileged \
--node-labels=master=true \
--minimum-container-ttl-duration=6m0s \
--cluster_dns=10.3.0.10 \
--cluster_domain=cluster.local \
--hostname-override=10.7.183.59
```
The TLS credentials generated by `bootkube render` in assets/tls/ are copied to a secret: assets/manifests/kube-apiserver-secret.yaml.
### Start the Temporary API Server
bootkube will serve as the temporary apiserver so the kubelet from above can start the real apiserver in a pod
```
sudo bootkube start --asset-dir=./assets --experimental-self-hosted-etcd --etcd-server=http://127.0.0.1:12379
```
bootkube should exit itself after successfully bootstrapping the master components. It's only needed for the very first bootstrapping
### Check the Output
```
watch hyperkube kubectl get pods -o wide --all-namespaces
```
## Join Nodes to the Cluster
Copy the information where to find the apiserver and how to authenticate:
```
scp 10.7.183.59:assets/auth/kubeconfig .
sudo mkdir -p /etc/kubernetes
sudo mv kubeconfig /etc/kubernetes/
```
install cni binaries and download hyperkube
```
sudo mkdir -p /opt/cni/bin
wget https://github.com/containernetworking/cni/releases/download/v0.4.0/cni-amd64-v0.4.0.tbz2
sudo tar xjf cni-amd64-v0.4.0.tbz2 -C /opt/cni/bin/
wget http://storage.googleapis.com/kubernetes-release/release/v1.5.3/bin/linux/amd64/hyperkube -O ./hyperkube
sudo mv hyperkube /usr/bin/hyperkube
sudo chmod 755 /usr/bin/hyperkube
```
### Master Nodes
Start the kubelet:
```
sudo hyperkube kubelet --kubeconfig=/etc/kubernetes/kubeconfig \
--require-kubeconfig \
--cni-conf-dir=/etc/kubernetes/cni/net.d \
--network-plugin=cni \
--lock-file=/var/run/lock/kubelet.lock \
--exit-on-lock-contention \
--pod-manifest-path=/etc/kubernetes/manifests \
--allow-privileged \
--node-labels=master=true \
--minimum-container-ttl-duration=6m0s \
--cluster_dns=10.3.0.10 \
--cluster_domain=cluster.local \
--hostname-override=10.7.183.60
```
### Worker Nodes
Note the only difference is the removal of `--node-labels=master=true`:
```
sudo hyperkube kubelet --kubeconfig=/etc/kubernetes/kubeconfig \
--require-kubeconfig \
--cni-conf-dir=/etc/kubernetes/cni/net.d \
--network-plugin=cni \
--lock-file=/var/run/lock/kubelet.lock \
--exit-on-lock-contention \
--pod-manifest-path=/etc/kubernetes/manifests \
--allow-privileged \
--minimum-container-ttl-duration=6m0s \
--cluster_dns=10.3.0.10 \
--cluster_domain=cluster.local\
--hostname-override=10.7.183.60
```
## Scale Etcd
kubectl apply doesn't work for TPR at the moment. See https://github.com/kubernetes/kubernetes/issues/29542. As a workaround, we use cURL to resize the cluster.
```
hyperkube kubectl --namespace=kube-system get cluster.etcd kube-etcd -o json > etcd.json && \
vim etcd.json && \
curl -H 'Content-Type: application/json' -X PUT --data @etcd.json http://127.0.0.1:8080/apis/etcd.coreos.com/v1beta1/namespaces/kube-system/clusters/kube-etcd
```
If that doesn't work, re-run until it does. See https://github.com/kubernetes-incubator/bootkube/issues/346#issuecomment-283526930
## Challenges
### Node setup
Some Broadcom NICs panic'ed with the default Ubuntu kernel
- upgrade kernel to >`4.8` because of brcm nic failure
- move to `--storage-driver=overlay2` instead of `aufs` as docker driver
- disable swap on the node (will be a fatal error in kube-1.6)
## ToDo Items:
### apiserver resiliance
the master apiservers need to have a single address only. Possible solutions:
- use LB from the DC
- use DNS from the DC with programmable API (e.g. powerdns)
- use something like kube-keepalive-vip?
- bootstrap DNS itself (skydns, coredns)
### Etcd Challenges
- backup strategies (https://github.com/coreos/etcd-operator/blob/master/doc/user/spec_examples.md#three-members-cluster-that-restores-from-previous-pv-backup)
- etcd-operator failures (e.g. https://github.com/coreos/etcd-operator/issues/851)
- partial failure (loosing quorum)
- permament failure (state gone completely)
- etcd needs ntp available (or another mechanism so that every node is in sync)
## Notes
### clean up docker
```
sudo su -
docker rm -f $(docker ps -a -q)
exit
```
### Compile Bootkube
```
sudo docker run --rm -it -v $(pwd)/golang/src:/go/src/ -w /go/src golang:1.7 bash
go get -u github.com/kubernetes-incubator/bootkube
cd $GOPATH/src/github.com/kubernetes-incubator/bootkube
make
```
### RBAC
```
./bootkube-rbac render --asset-dir assets-rbac --experimental-self-hosted-etcd --etcd-servers=http://10.3.0.15:2379 --api-servers=https://10.7.183.59:443
sudo rm -rf /etc/kubernetes/*
sudo cp -a assets-rbac/manifests /etc/kubernetes/
sudo cp assets-rbac/auth/kubeconfig /etc/kubernetes/
sudo ./bootkube-rbac start --asset-dir=./assets-rbac --experimental-self-hosted-etcd --etcd-server=http://127.0.0.1:12379
```
### Containerized Kubelet
The benefit here is using a docker container instead of a kubelet binary. Also the hyperkube docker image packages and installs the cni binaries. The downside would be that in either case something needs to start the container upon a reboot of the node. Usually the something is systemd and systemd is better managing binaries than docker containers. Either way, this is how you would run a containerized kubelet:
```
sudo docker run \
--rm \
-it \
--privileged \
-v /dev:/dev \
-v /run:/run \
-v /sys:/sys \
-v /etc/kubernetes:/etc/kubernetes \
-v /usr/share/ca-certificates:/etc/ssl/certs \
-v /var/lib/docker:/var/lib/docker \
-v /var/lib/kubelet:/var/lib/kubelet \
-v /:/rootfs \
quay.io/coreos/hyperkube:v1.5.3_coreos.0 \
./hyperkube \
kubelet \
--network-plugin=cni \
--cni-conf-dir=/etc/kubernetes/cni/net.d \
--cni-bin-dir=/opt/cni/bin \
--pod-manifest-path=/etc/kubernetes/manifests \
--allow-privileged \
--hostname-override=10.7.183.60 \
--cluster-dns=10.3.0.10 \
--cluster-domain=cluster.local \
--kubeconfig=/etc/kubernetes/kubeconfig \
--require-kubeconfig \
--lock-file=/var/run/lock/kubelet.lock \
--containerized
```
Not quite working yet though. The node comes up, registeres successfully with the master and starts daemonsets. Everything comes up except flannel:
```
main.go:127] Failed to create SubnetManager: unable to initialize inclusterconfig: open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory
```
## Resources and References
- https://github.com/kubernetes/community/blob/master/contributors/design-proposals/self-hosted-kubernetes.md
- https://github.com/kubernetes-incubator/bootkube
- https://github.com/coreos/etcd-operator/
- http://blog.kubernetes.io/2017/01/stronger-foundation-for-creating-and-managing-kubernetes-clusters.html
- https://github.com/kubernetes/kubeadm/issues/127