[Из песочницы] Запускаем полноценный кластер на Kubernetes с нуля на Ubuntu 16.04
Уже довольно много написано статей, по установке и запуску 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:
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
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 часов. Не судите строго, если где-то есть опечатки. Задавайте вопросы, с радостью отвечу и помогу.