Launch a full cluster on Kubernetes from scratch on Ubuntu 16.04

Quite a lot of articles have already been written on installing and running Kubernetes , however, not everything is so smooth (I spent several days launching my cluster).

This article is intended to provide comprehensive information not only on installing k8s, but also to explain each step: why and why we do exactly as it is written (this is very important for a successful launch).

What you need to know


Servers: The
cluster implies that you have more than one physical server, between which resources will be distributed. Servers are called nodes.

Drives:
Normal k8s hard drives are not supported. Work with disks is done using distributed file storage facilities. This is necessary so that k8s can "move" docker containers to other nodes if necessary, without losing data (files).

To start creating a cluster, you need to create your own distributed file storage. If you are sure that you will never need disks, then you can skip this step.
I chose Ceph . I also recommend reading this wonderful article .

The minimum reasonable number of servers for Ceph is 3 (you can build on one, but this makes little sense because of the high probability of losing data).

Network:
We need Flannel - it allows you to organize a software-defined network (Software Defined Network, SDN). It is SDN that allows all of our containers to communicate with each other within the cluster (Flannel installation is performed together with k8s and is described below).

Server Preparation


In our example, we use 3 physical servers. Install Ubuntu 16.04 on all servers. Do not create swap partitions (k8s requirement).

Provide at least one disk (or partition) for Ceph in each server.

Do not enable SELinux support (in Ubuntu 16.04 it is turned off by default).

We named the server like this: kub01 kub02 kub03. The sda2 partition on each server is created for Ceph (formatting is optional).

Install and configure Ceph


I will describe Ceph installation quite briefly. There are many examples online and the Ceph site itself has pretty good documentation.

All operations are performed from under the privileged root user.

Create a temporary directory:

mkdir ~/ceph-admin
cd ~/ceph-admin

Install Ceph:

apt install ceph-deploy ceph-common

You must create a key and decompose it across all servers. This is needed for the ceph-deploy utility:

ssh-keygen
ssh-copy-id kub01
ssh-copy-id kub02
ssh-copy-id kub03


(you may need to fix the ssh config to allow login as root).

Check that kub01 is not registered in your / etc / hosts as 127.0.0.1 (if registered, delete this line).

Create a disk cluster and initialize it:

ceph-deploy new kub01 kub02 kub03
ceph-deploy install kub01 kub02 kub03
ceph-deploy mon create-initial
ceph-deploy osd prepare kub01:sda2 kub02:sda2 kub03:sda2
ceph-deploy osd activate kub01:sda2 kub02:sda2 kub03:sda2

Checking our disk cluster:

ceph -s
    cluster 363a4cd8-4cb3-4955-96b2-73da72b63cf5
    health HEALTH_OK

The following useful commands can be adopted:

ceph -s
ceph df
ceph osd tree

Now that we have verified that Ceph is working, we will create a separate pool for k8s:

ceph osd pool create kube 100 100

(you can see all existing pools with the command: ceph df)

Now we will create a separate user for our kube pool and save the keys:

ceph auth get-or-create client.kube mon 'allow r' osd 'allow rwx pool=kube'
ceph auth get-key client.admin > /etc/ceph/client.admin
ceph auth get-key client.kube > /etc/ceph/client.kube

(keys will be needed to access k8s to the repository)

Install Kubernetes


Add the k8s repository to our system:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat </etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main  
EOF

Now install the main packages:

apt update
apt install -y docker.io kubelet kubeadm kubernetes-cni

Initialize and run k8s

kubeadm init --pod-network-cidr=10.244.0.0/16

(just such a network 10.244.0.0/16 is necessary for flannel to work - do not change it)

Save the command printed by the script to join the nodes to the cluster.

It is convenient to use a separate unprivileged user to work with k8s. Create it and copy the k8s configuration file into it:

useradd -s /bin/bash -m kube
mkdir ~kube/.kube
cp /etc/kubernetes/admin.conf ~kube/.kube/config
chown kube: ~kube/.kube/config

To work with k8s, use the utility: kubectl . We use it only from under our user kube . To go under the user, do:

su - kube

Allow the launch of containers on the wizard:

kubectl taint nodes --all node-role.kubernetes.io/master-

Configure the rights:

kubectl create clusterrolebinding add-on-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default

Install flannel (network subsystem):

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

How to check that everything works
Run the command:

kubectl -n kube-system get pods

The output should be something like this:

NAME                                      READY     STATUS    RESTARTS   AGE
etcd-kub01.domain.com                      1/1       Running   1          4d
kube-apiserver-kub01.domain.com            1/1       Running   1          4d
kube-controller-manager-kub01.domain.com   1/1       Running   0          4d
kube-dns-7c6d8859cb-dmqrn                  3/3       Running   0          1d
kube-flannel-ds-j948h                      1/1       Running   0          1d
kube-proxy-rmbqq                           1/1       Running   0          1d
kube-scheduler-kub01.domain.com            1/1       Running   1          4d


Install and configure the web interface


kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

Create a user to access the web interface:
cat << EOF > account.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
EOF
kubectl -n kube-system create -f account.yaml

Run kube-proxy, you can do it like this:

kubectl proxy &

And forward port 8001 from your working machine to the kub01 server:

ssh -L 8001:127.0.0.1:8001 -N kub01 &

Now we can go to the web interface from our working machine at:

http://127.0.0.1:8001/ui
(the web interface opens, where you need to specify the token)

You can get the token for access to the web interface like this:

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

Customize Kubernetes and Ceph


The current controller kube-controller-manager-amd64: v1.9.2 is not binary rbd , needed to work with Ceph , so create your own kube-controller-manager:

What is RBD, why is it needed and why is it missing?
RBD (Rados Block Device) is a block device that k8s uses to create and mount partitions of Docker containers. In this case, this is the binary included in the package: ceph-common .

k8s does not include this package in its controller, apparently because it depends on the distribution of the operating system that you are using. Therefore, when assembling your controller, be sure to specify your distribution to keep RBD up to date.

To create our kube-controller-manager do the following:
(we execute all commands from under the root user)

mkdir docker
cat << EOF > docker/Dockerfile
FROM ubuntu:16.04
ARG KUBERNETES_VERSION=v1.9.2
ENV DEBIAN_FRONTEND=noninteractive \
   container=docker \
   KUBERNETES_DOWNLOAD_ROOT=https://storage.googleapis.com/kubernetes-release/release/${KUBERNETES_VERSION}/bin/linux/amd64 \
   KUBERNETES_COMPONENT=kube-controller-manager
RUN set -x \
   && apt-get update \
   && apt-get install -y \
       ceph-common \
       curl \
   && curl -L ${KUBERNETES_DOWNLOAD_ROOT}/${KUBERNETES_COMPONENT} -o /usr/bin/${KUBERNETES_COMPONENT} \
   && chmod +x /usr/bin/${KUBERNETES_COMPONENT} \
   && apt-get purge -y --auto-remove \
       curl \
   && rm -rf /var/lib/apt/lists/*
EOF
docker build -t "my-kube-controller-manager:v1.9.2" docker/

(be sure to indicate the current version of k8s and the OS distribution) We

check that our controller has been successfully created:

docker images | grep my-kube-controller-manager

Check that there is rbd in our image:

docker run my-kube-controller-manager:v1.9.2 whereis rbd

You should see something like: rbd: / usr / bin / rbd /usr/share/man/man8/rbd.8.gz

Replace the standard controller with ours, for this we edit the file:
/ etc / kubernetes / manifests / kube-controller- manager.yaml

Replace the line:
image: gcr.io/google_containers/kube-controller-manager-amd64:v1.9.2
with:
image: my-kube-controller-manager: v1.9.2
imagePullPolicy: IfNotPresent

(be sure to add the imagePullPolicy directive so that k8s did not try to download this image from the Internet)

We go under the user kube and wait until our controller starts up (nothing needs to be done).

kubectl -n kube-system describe pods | grep kube-controller

You should see that our image is being used:
Image: my-kube-controller-manager: v1.9.2

Now that our RBD -enabled controller has started, we can begin to configure the connection between k8s and Ceph.

Configuring a bunch of disk subsystems (k8s + Ceph)


Add keys to k8s to access Ceph:

kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" --from-file=/etc/ceph/client.admin --namespace=kube-system
kubectl create secret generic ceph-secret-kube --type="kubernetes.io/rbd" --from-file=/etc/ceph/client.kube --namespace=default

Create StorageClass (default):

cat << EOF > ceph_storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ceph-rbd
  annotations: {"storageclass.kubernetes.io/is-default-class":"true"}
provisioner: kubernetes.io/rbd
parameters:
  monitors: kub01:6789,kub02:6789,kub03:6789
  pool: kube
  adminId: admin
  adminSecretName: ceph-secret
  adminSecretNamespace: "kube-system"
  userId: kube
  userSecretName: ceph-secret-kube
  fsType: ext4
  imageFormat: "2"
  imageFeatures: "layering"
EOF
kubectl create -f ceph_storage.yaml

How to check the operation of the disk subsystem?
Checking the presence of StorageClass:

kube@kub01:~$ kubectl get storageclass
NAME                      PROVISIONER         AGE
ceph-rbd (default)        kubernetes.io/rbd   4d

Create a test pod with a disk:

cat << EOF > test_pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: test-pod-with-pvc
spec:
  volumes:
    - name: test-pvc-storage
      persistentVolumeClaim:
        claimName: claim1
  containers:
    - name: test-container
      image: kubernetes/pause
      volumeMounts:
       - name: test-pvc-storage
         mountPath: /var/lib/www/html
EOF
kubectl create -f test_pod.yaml

Check that the Pod is created (you need to wait for the creation and launch):

kube@kub01:~$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
test-pod-with-pvc   1/1       Running   0          15m

Check that Claim has been created (a request for a drive):

kube@kub01:~$ kubectl get pvc
NAME      STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
claim1    Bound     pvc-076df6ee-0ce9-11e8-8b93-901b0e8fc39b   1Gi        RWO            ceph-rbd       12m

Check that the disk itself is created:

kube@kub01:~$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                        STORAGECLASS   REASON    AGE
pvc-076df6ee-0ce9-11e8-8b93-901b0e8fc39b   1Gi        RWO            Delete           Bound     default/claim1               ceph-rbd        

And finally, check that the disk is mounted on the system:

root@kub01:~$ mount | grep pvc-076df6ee-0ce9-11e8-8b93-901b0e8fc39b
/dev/rbd0 on /var/lib/kubelet/pods/076fff13-0ce9-11e8-8b93-901b0e8fc39b/volumes/kubernetes.io~rbd/pvc-076df6ee-0ce9-11e8-8b93-901b0e8fc39b type ext4 (rw,relatime,stripe=1024,data=ordered)


Adding new (additional) nodes to the k8s cluster


On the new server, run the following commands:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat </etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main  
EOF
apt update
apt install -y  docker.io kubelet kubeadm kubernetes-cni ceph-common python

Connect the node to the master:
We need a key. You can get it on the wizard by running the command:

kubeadm token list

Or, create it:
kubeadm token create --print-join-command


An example of a command to join (run on a new node):

kubeadm join --token cb9141.6a912d1dd7f66ff5 8.8.8.8:6443 --discovery-token-ca-cert-hash sha256:f0ec6d8f9699169089c89112e0e6b5905b4e1b42db22815186240777970dc6fd

Install and configure Helm


Helm was invented for quick and easy installation of applications in k8s.

A list of available applications can be found here .

Install and initialize Helm:

curl https://storage.googleapis.com/kubernetes-helm/helm-v2.8.0-linux-amd64.tar.gz | tar -xz
./linux-amd64/helm init

PS: it took 6 hours to write this article. Do not judge strictly if there are typos somewhere. Ask questions, I will be happy to answer and help.

Also popular now: