Requirements
I’m writing the requirements here, but will go through all the steps below.
- https://kubernetes.io/docs/setup/independent/install-kubeadm/
- Operating System: CentOS 7
- 2 GB or more of RAM per machine (any less will leave little room for your apps)
- 2 CPUs or more
- Full network connectivity between all machines in the cluster (public or private network is fine)
- Unique hostname, MAC address, and product_uuid for every node. See here for more details.
- Certain ports are open on your machines. See here for more details.
- Swap disabled. You MUST disable swap in order for the kubelet to work properly.
My Setup
I created a quick VPC called itsmetommy and three VMs within GCP.
- VPC: itsmetommy 10.0.0.0/9
- Range: 10.0.0.0 – 10.127.255.255
- IPs: 10.0.0.1 – 10.127.255.254
- Hosts: 8388606
- k8s Cluster
- 10.244.0.0/16
- Range: 10.244.0.0 – 10.244.255.255
- IPs: 10.244.0.1 – 10.244.255.254
- Hosts: 65534
- OS: CentOS 7
- Machine Type: n1-standard-2 (2 vCPUs, 7.5 GB memory)
- Hostnames
- k8s-itsmetommy-master
- k8s-itsmetommy-worker-1
- k8s-itsmetommy-worker-2
Install
Run on every node.
$ sudo bash
# yum -y update
Verify the MAC address and product_uuid are unique for every node.
# ifconfig -a
# cat /sys/class/dmi/id/product_uuid
Disable swap.
# swapoff -a
Disable the firewall.
# systemctl disable firewalld && systemctl stop firewalld
I created a firewall rule that allowed port 22, along with an internal firewall rule with all the recommended ports.
Configure iptables to receive bridged network traffic.
# cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
Load system configuration files.
# sysctl --system
Update SELinux.
# setenforce 0
# sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
Install Docker.
# yum -y install docker
# systemctl enable docker && systemctl start docker
Add Kubernetes YUM repository.
# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
Install kubelet, kubeadm and kubectl.
# yum install -y kubelet kubeadm kubectl
# systemctl enable kubelet && systemctl start kubelet
Create cluster
Run on master.
$ sudo kubeadm init --pod-network-cidr 10.244.0.0/16
Example
$ sudo kubeadm init --pod-network-cidr 10.244.0.0/16
[init] Using Kubernetes version: v1.13.1
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-itsmetommy-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.0.0.2]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-itsmetommy-master localhost] and IPs [10.0.0.2 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-itsmetommy-master localhost] and IPs [10.0.0.2 127.0.0.1 ::1]
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 21.502512 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.13" in namespace kube-system with the configuration for the kubelets in the cluster
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "k8s-itsmetommy-master" as an annotation
[mark-control-plane] Marking the node k8s-itsmetommy-master as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node k8s-itsmetommy-master as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: i6rmg7.9b4i2eyl06ru6mqp
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes master 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
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 machines by running the following on each node
as root:
kubeadm join 10.0.0.2:6443 --token i6rmg7.9b4i2eyl06ru6mqp --discovery-token-ca-cert-hash sha256:a3ddcd2aaa87e7ea9de096dcb24e028e68f3e309ca634df0f278c059efd88527
IMPORTANT: Make a record of the kubeadm join command that kubeadm init outputs. You need this command to join nodes to your cluster.
Run on master 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
Deploying Container Networking Interface (CNI)
- https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network
- https://kubernetes.io/docs/concepts/cluster-administration/addons/
The Container Network Interface (CNI) defines how the different nodes and their workloads should communicate. There are multiple network providers available, some are listed here.
I went with Flannel.
Run on master — run apply.
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml
Run on master — verify all pods are in running state.
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-86c58d9df4-5r97f 1/1 Running 0 6m58s
kube-system coredns-86c58d9df4-lxrzr 1/1 Running 0 6m58s
kube-system etcd-k8s-itsmetommy-master 1/1 Running 0 11m
kube-system kube-apiserver-k8s-itsmetommy-master 1/1 Running 0 11m
kube-system kube-controller-manager-k8s-itsmetommy-master 1/1 Running 0 11m
kube-system kube-flannel-ds-amd64-hpbrt 1/1 Running 0 24s
kube-system kube-proxy-8hdh5 1/1 Running 0 12m
kube-system kube-scheduler-k8s-itsmetommy-master 1/1 Running 0 11m
Configure worker nodes
Run on both worker-1 and worker-2.
$ sudo kubeadm join 10.0.0.2:6443 --token i6rmg7.9b4i2eyl06ru6mqp --discovery-token-ca-cert-hash sha256:a3ddcd2aaa87e7ea9de096dcb24e028e68f3e309ca634df0f278c059efd88527
Example
$ sudo kubeadm join 10.0.0.2:6443 --token i6rmg7.9b4i2eyl06ru6mqp --discovery-token-ca-cert-hash sha256:a3ddcd2aaa87e7ea9de096dcb24e028e68f3e309ca634df0f278c059efd88527
[preflight] Running pre-flight checks
[discovery] Trying to connect to API Server "10.0.0.2:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://10.0.0.2:6443"
[discovery] Requesting info from "https://10.0.0.2:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "10.0.0.2:6443"
[discovery] Successfully established connection with API Server "10.0.0.2:6443"
[join] Reading configuration from the cluster...
[join] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.13" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[tlsbootstrap] Waiting for the kubelet to perform the TLS Bootstrap...
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "k8s-itsmetommy-worker-1" as an annotation
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the master to see this node join the cluster.
Run on master — get nodes.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-itsmetommy-master Ready master 15m v1.13.1
k8s-itsmetommy-worker-1 Ready <none> 29s v1.13.1
k8s-itsmetommy-worker-2 NotReady <none> 5s v1.13.1
Run on master — view tokens.
$ sudo kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
i6rmg7.9b4i2eyl06ru6mqp 22h 2018-12-29T20:27:36Z authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token
Run on master — label the worker nodes (optional).
$ kubectl label node k8s-itsmetommy-worker-1 node-role.kubernetes.io/node=
node/k8s-itsmetommy-worker-1 labeled
$ kubectl label node k8s-itsmetommy-worker-2 node-role.kubernetes.io/node=
node/k8s-itsmetommy-worker-2 labeled
Run on master — get nodes.
Notice the ROLES column and how the workers are now labeled node.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-itsmetommy-master Ready master 173m v1.13.1
k8s-itsmetommy-worker-1 Ready node 158m v1.13.1
k8s-itsmetommy-worker-2 Ready node 157m v1.13.1
Remove cluster
$ kubeadm reset