[Из песочницы] Запускаем полноценный кластер на Kubernetes с нуля на Ubuntu 16.04

habr.png

Уже довольно много написано статей, по установке и запуску Kubernetes, однако, не всё так гладко (я потратил несколько суток на запуск своего кластера).

Данная статья призвана дать исчерпывающую информацию не только по установке k8s, но и объяснить каждый шаг: зачем и почему мы делаем именно так, как написано (это очень важно для успешного запуска).

Что нужно знать


Серверы:
Кластер подразумевает, что у Вас более одного физического сервера, между которыми и будут распределятся ресурсы. Серверы называются нодами (nodes).

Диски:
k8s требует распределенного файлового хранилища. Это необходимо для того, чтобы k8s мог «перемещать» контейнеры docker на другие ноды в случае необходимости, без потери данных (файлов).

Начинать создание кластера нужно именно с создания своего распределенного файлового хранилища. Я выбрал Ceph. А еще рекомендую почитать эту замечательную статью.

Минимальное разумное количество серверов для Ceph — 3 (можно построить и на одном, но в этом мало смысла из-за высокой вероятности потерять данные).

Сеть:
Нам понадобится Flannel — он позволяет организовать программно определяемую сеть (Software Defined Network, SDN). Именно SDN позволяет всем нашим контейнерам общаться с друг другом внутри кластера (установка Flannel производится вместе с k8s и описана ниже).

Подготовка серверов


В нашем примере мы используем 3 физических сервера. Установите Ubuntu 16.04 на все сервера. Не создавайте swap партиции (требование k8s).

Предусмотрите в каждом сервере как минимум один диск (или партицию) для Ceph.

Не включайте поддержку SELinux (в Ubuntu 16.04 он выключен по-умолчанию).

Мы назвали сервера так: kub01 kub02 kub03. Партиция sda2 на каждом сервере создана для Ceph (форматировать не обязательно).

Установка и настройка Ceph


Установку Ceph я опишу довольно кратко. В сети много примеров и на самом сайте Ceph довольно хорошая документация.

Все операции производим из под привелигированого пользователя root.

Создадим временную директорию:

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


Установим Ceph:

apt install ceph-deploy ceph-common


Необходимо создать ключ и разложить его по всем серверам. Это нужно для утилиты ceph-deploy:

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


Создаем дисковый кластер и инициализируем его:

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


Проверяем наш дисковый кластер:

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


Можно взять на вооружение следующие полезные команды:

ceph -s
ceph df
ceph osd tree


Теперь, когда мы убедились, что Ceph работает, мы создадим отдельный пул (pool) для k8s:

ceph osd pool create kube 100 100


(можно посмотреть все существующие пулы командой: ceph df)

Теперь создадим отдельного пользователя для нашего пула kube и сохраним ключи:

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


(ключи понадобятся для доступа k8s к хранилищу)

Устанавливаем Kubernetes


Добавим репозиторий k8s в нашу систему:

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


Инициализируем и запускаем k8s

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


(именно такая сеть 10.244.0.0/16 необходима для работы flannel — не изменяйте ее)

Для работы с k8s удобно использовать отдельного непривилегированного пользователя. Создадим его и скопируем в него конфигурационный файл k8s:

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


Для работы с k8s используется утилита: kubectl. Используем ее только из под нашего пользователя kube. Для перехода под пользователя выполним:

su - kube


Разрешаем запуск контейнеров на мастере:

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


Настраиваем права:

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


Устанавливаем flannel (сетевую подсистему):

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


Как проверить что всё работает
Выполните команду:
kubectl -n kube-system get pods

Вывод должен быть примерно следующим:
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


Устанавливаем и настраиваем веб-интерфейс


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


Создаем пользователя для доступа в веб интерфейс:

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


Запустим kube-proxy, сделать это можно так:

kubectl proxy &


И пробросим порт 8001 с вашей рабочей машины до сервера kub01:

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


Теперь мы можем зайти в веб интерфейс со своей рабочей машины по адресу:

http://127.0.0.1:8001/ui
(откроется веб интерфейс, где нужно указать токен)

Получить токен для доступа в веб интерфейс можно так:

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


Настраиваем Kubernetes и Ceph


В текущем контроллере kube-controller-manager-amd64: v1.9.2 отсутствует бинарник rbd, необходимый для работы с Ceph, поэтому создадим свой собственный kube-controller-manager:

Что такое RBD, зачем он нужен и почему отсутствует?
RBD (Rados Block Device) — блочное устройство, которое как раз и используется k8s для создания и монтирования партиций Docker контейнеров. В данном случае, это бинарник, входящий в состав пакета: ceph-common.

k8s не включает этот пакет в свой контроллер видимо потому, что он зависит от дистрибутива операционной системы, которую вы используете. Поэтому, при сборке своего контроллера обязательно указывайте именно Ваш дистрибутив, чтобы RBD был актуальным.


Для создания нашего kube-controller-manager сделайте следующее:
(все команды выполняем из под пользователя root)

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/


(обязательно указывайте актуальные версии k8s и дистрибутива ОС)

Проверяем, что наш контроллер успешно создан:

docker images | grep my-kube-controller-manager


Проверяем, что в нашем образе есть rbd:

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


Должны увидеть что-то вида: rbd: /usr/bin/rbd /usr/share/man/man8/rbd.8.gz

Заменяем стандартный контроллер нашим, для этого правим файл:
/etc/kubernetes/manifests/kube-controller-manager.yaml

Заменяем строку:
image: gcr.io/google_containers/kube-controller-manager-amd64: v1.9.2
на:
image: my-kube-controller-manager: v1.9.2
imagePullPolicy: IfNotPresent

(обязательно добавляем директиву imagePullPolicy, чтобы k8s не пытался скачивать этот образ из интернета)

Переходим под пользователя kube и дожидаемся пока наш контроллер запустится (ничего делать не нужно).

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


Должны увидеть, что используется наш образ:
Image: my-kube-controller-manager: v1.9.2

Теперь, когда запустился наш контроллер с поддержкой RBD, мы можем приступить к настройке связки k8s и Ceph.

Настройка связки дисковой подсистемы (k8s + Ceph)


Добавляем ключи в k8s, для доступа к 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


Создаем 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


Как проверить работу дисковой подсистемы?
Проверяем наличие StorageClass:
kube@kub01:~$ kubectl get storageclass
NAME                      PROVISIONER         AGE
ceph-rbd (default)        kubernetes.io/rbd   4d

Создадим тестовый pod с диском:
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

Проверим, что создался Pod (нужно дождаться создания и запуска):
kube@kub01:~$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
test-pod-with-pvc   1/1       Running   0          15m

Проверим, что создался Claim (запрос на предоставление диска):
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

Проверим, что создался и сам диск:
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        

И наконец, проверим, что диск примонтирован в системе:
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)


Добавление новых (дополнительных) нод в кластер k8s


На новом сервере выполните следующие команды:

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


Присоединяем ноду к мастеру:
Нам нужен ключ. Его можно получить на мастере, выполнив команду:

kubeadm token list


Либо, создать его:

kubeadm token create --print-join-command

Пример команды для присоединения (выполняем на новой ноде):

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


Установка и настройка Helm


Для быстрой и простой установки приложений в k8s был придуман Helm.

Список доступных приложений можно найти здесь.

Устанавливаем и инициализируем Helm:

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


P.S.: на написание этой статьи ушло 6 часов. Не судите строго, если где-то есть опечатки. Задавайте вопросы, с радостью отвечу и помогу.

© Habrahabr.ru