Kubernetes hard way
Любой человек, активно работающий с k8s, рано или поздно приходит к пониманию необходимости пройти дорогу самостоятельного разворачивания кластера.
Скорее всего в этот момент быстрый поиск приведет вас к инструментам на подобии kubeadm, kubespray, rke. Как правило, развернув кластер с их помощью природная любознательность утоляется и мы можем вернуться к своим рутинным задачам, но иногда нам хочется узнать как же глубока кроличья нора.
Вот именно для людей, чей интерес невозможно утолить распатроненными средствами автоматизации, посвящена эта статья.
Под катом вас ждет хардкорный гайд, который поможет вам собрать свой кластер k8s используя только linux и пресловутые 5 бинарей.
Предупреждение
Данное руководство нельзя воспринимать как руководство подготовки промышленного кластера. Относитесь к нему как к лабораторной работе.
Что нам понадобится
Чеклист
- Две виртуальные машины с Ubuntu 22.04
- Настроенный ntp на этих машинах
- У машин должен быть доступ к сети интернет
- Настроенный hostname и fqdn
Системные требования для машин
Параметр | Минимальная конфигурация |
---|---|
CPU (vCPU) | 4 |
RAM (GB) | 8 |
Storage (GB) | 30 |
Подготовка машин
Nota bene
В данной заметки мы будем использовать 2 виртуальные машины
10.0.138.179 с хостнеймом k8shw-1 (воркер)
10.0.138.180 с хостнеймом k8shw-0 (контролплейн)
Обновление и установка пакетов
На каждой машине выполняем
После чего вытягиваем kubectl на машины
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Проверяем что у нас есть настроенный ntp
Добавляем машины в хосты
На каждой машине необходимо добавить в файл /etc/hosts информацию о наших ВМ
Служебные утилиты
Для работы kubelet нам потребуются зависимости, установим их из официального репозитория.
Для начала добавим этот репозиторий:
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/${KUBE_LATEST}/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${KUBE_LATEST}/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo mkdir -p /etc/containerd
containerd config default | sed 's/SystemdCgroup = false/SystemdCgroup = true/' | sudo tee /etc/containerd/config.toml
Подготавливаем переменные окружения
Предупреждение
Обратите внимание, эти переменные окружения активно используются практически всеми командами ниже. Всегда проверяйте что переменные корректно экспортированы на сервере где вы работаете.
export MASTER_1=$(dig +short k8shw-0)
export WORKER_1=$(dig +short k8shw-1)
export SERVICE_CIDR=172.21.0.0/16
export POD_CIDR=172.20.0.0/16
export API_SERVICE=$(echo $SERVICE_CIDR | awk 'BEGIN {FS="."} ; { printf("%s.%s.%s.1", $1, $2, $3) }')
export KUBE_LATEST=$(curl -L -s https://dl.k8s.io/release/stable.txt | awk 'BEGIN { FS="." } { printf "%s.%s", $1, $2 }')
export KUBE_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
export CLUSTER_DNS=$(echo $SERVICE_CIDR | awk 'BEGIN {FS="."} ; { printf("%s.%s.%s.10", $1, $2, $3) }')
export ETCD_VERSION="v3.5.10"
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
Работа с сертификатами
В этой главе мы создадим pki инфраструктуру для нашего будущего кластера, создадим сертификаты для всех нужных компонентов и административный сертификат.
В рамках этой статьи мы будем использовать openssl, однако вы можете использовать например утилиту cfssl
Nota bene
Действия этой главы мы выполняем на k8shw-0 который будет нашим controlplane
CA
Для того чтобы иметь возможность выпускать сертификаты, нам необходимо сгенерировать так называемый Certificate Authority (CA), для этого на машине k8shw-0 выполним следующие команды:
openssl genrsa -out ca.key 2048
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA/O=Kubernetes" -out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -CAcreateserial -out ca.crt -days 1000
Для работы нам будут нужны ca.crt и ca.key
Предупреждение
Утечка ca.key является критической проблемой безопасности, будьте осторожны.
Далее мы можем приступить к генерации остальных сертификатов
Административный сертификат
Этот сертификат будет использоваться для задач администрирования кластера, процесс генерации выполняется с помощью следующих команд:
openssl genrsa -out admin.key 2048
openssl req -new -key admin.key -subj "/CN=admin/O=system:masters" -out admin.csr
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out admin.crt -days 1000
Сертификаты компонентов кластера
Для некоторых компонентов нужны сертификаты содержащие в себе большое количество полей, для этих компонентов мы будем создавать специальный конфигурационный файл, например как тут
Controller Manager
openssl genrsa -out kube-controller-manager.key 2048
openssl req -new -key kube-controller-manager.key -subj "/CN=system:kube-controller-manager/O=system:kube-controller-manager" -out kube-controller-manager.csr
openssl x509 -req -in kube-controller-manager.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-controller-manager.crt -days 1000
Kube Proxy
openssl genrsa -out kube-proxy.key 2048
openssl req -new -key kube-proxy.key -subj "/CN=system:kube-proxy/O=system:node-proxier" -out kube-proxy.csr
openssl x509 -req -in kube-proxy.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-proxy.crt -days 1000
Scheduler Client
openssl genrsa -out kube-scheduler.key 2048
openssl req -new -key kube-scheduler.key -subj "/CN=system:kube-scheduler/O=system:kube-scheduler" -out kube-scheduler.csr
openssl x509 -req -in kube-scheduler.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-scheduler.crt -days 1000
Kubernetes API Server
cat > kubeapi.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req]
basicConstraints = critical, CA:FALSE
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
IP.1 = ${API_SERVICE}
IP.2 = ${MASTER_1}
IP.5 = 127.0.0.1
EOF
openssl genrsa -out kube-apiserver.key 2048
openssl req -new -key kube-apiserver.key -subj "/CN=kube-apiserver/O=Kubernetes" -out kube-apiserver.csr -config kubeapi.cnf
openssl x509 -req -in kube-apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-apiserver.crt -extensions v3_req -extfile kubeapi.cnf -days 1000
Kubelet Certificate
cat > openssl-kubelet.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req]
basicConstraints = critical, CA:FALSE
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF
openssl genrsa -out apiserver-kubelet-client.key 2048
openssl req -new -key apiserver-kubelet-client.key -subj "/CN=kube-apiserver-kubelet-client/O=system:masters" -out apiserver-kubelet-client.csr -config openssl-kubelet.cnf
openssl x509 -req -in apiserver-kubelet-client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out apiserver-kubelet-client.crt -extensions v3_req -extfile openssl-kubelet.cnf -days 1000
ETCD Server
cat > openssl-etcd.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
IP.1 = ${MASTER_1}
IP.3 = 127.0.0.1
EOF
openssl genrsa -out etcd-server.key 2048
openssl req -new -key etcd-server.key -subj "/CN=etcd-server/O=Kubernetes" -out etcd-server.csr -config openssl-etcd.cnf
openssl x509 -req -in etcd-server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out etcd-server.crt -extensions v3_req -extfile openssl-etcd.cnf -days 1000
Service Account
openssl genrsa -out service-account.key 2048
openssl req -new -key service-account.key -subj "/CN=service-accounts/O=Kubernetes" -out service-account.csr
openssl x509 -req -in service-account.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out service-account.crt -days 1000
Kubeconfig
В целом операции довольно типичны, мы не будем подробно на них подробно останавливаться. Основной концепт связан с тем что с помощью kubectl мы создаём новые kubeconfig
admin
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=ca.crt \
--embed-certs=true \
--server=https://${MASTER_1}:6443 \
--kubeconfig=admin.kubeconfig
kubectl config set-credentials admin \
--client-certificate=admin.crt \
--client-key=admin.key \
--embed-certs=true \
--kubeconfig=admin.kubeconfig
kubectl config set-context default \
--cluster=kubernetes-the-hard-way \
--user=admin \
--kubeconfig=admin.kubeconfig
kubectl config use-context default --kubeconfig=admin.kubeconfig
kube-proxy
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=/var/lib/kubernetes/pki/ca.crt \
--server=https://${MASTER_1}:6443 \
--kubeconfig=kube-proxy.kubeconfig
kubectl config set-credentials system:kube-proxy \
--client-certificate=/var/lib/kubernetes/pki/kube-proxy.crt \
--client-key=/var/lib/kubernetes/pki/kube-proxy.key \
--kubeconfig=kube-proxy.kubeconfig
kubectl config set-context default \
--cluster=kubernetes-the-hard-way \
--user=system:kube-proxy \
--kubeconfig=kube-proxy.kubeconfig
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
kube-controller-manager
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=/var/lib/kubernetes/pki/ca.crt \
--server=https://127.0.0.1:6443 \
--kubeconfig=kube-controller-manager.kubeconfig
kubectl config set-credentials system:kube-controller-manager \
--client-certificate=/var/lib/kubernetes/pki/kube-controller-manager.crt \
--client-key=/var/lib/kubernetes/pki/kube-controller-manager.key \
--kubeconfig=kube-controller-manager.kubeconfig
kubectl config set-context default \
--cluster=kubernetes-the-hard-way \
--user=system:kube-controller-manager \
--kubeconfig=kube-controller-manager.kubeconfig
kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
kube-scheduler
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=/var/lib/kubernetes/pki/ca.crt \
--server=https://127.0.0.1:6443 \
--kubeconfig=kube-scheduler.kubeconfig
kubectl config set-credentials system:kube-scheduler \
--client-certificate=/var/lib/kubernetes/pki/kube-scheduler.crt \
--client-key=/var/lib/kubernetes/pki/kube-scheduler.key \
--kubeconfig=kube-scheduler.kubeconfig
kubectl config set-context default \
--cluster=kubernetes-the-hard-way \
--user=system:kube-scheduler \
--kubeconfig=kube-scheduler.kubeconfig
kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
Запуск компонентов Controlplane
ETCD
Шифрование
Для того чтобы etcd шифровал секреты, необходимо подготовить специальный конфигурационный файл:
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_KEY}
- identity: {}
EOF
Установка ETCD
Качаем бинарник etcd:
wget -q --show-progress --https-only --timestamping \
"https://github.com/coreos/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz"
sudo mkdir -p /etc/etcd /var/lib/etcd /var/lib/kubernetes/pki
sudo cp etcd-server.key etcd-server.crt /etc/etcd/
sudo cp ca.crt /var/lib/kubernetes/pki/
sudo chown root:root /etc/etcd/*
sudo chmod 600 /etc/etcd/*
sudo chown root:root /var/lib/kubernetes/pki/*
sudo chmod 600 /var/lib/kubernetes/pki/*
sudo ln -s /var/lib/kubernetes/pki/ca.crt /etc/etcd/ca.crt
ETCD_NAME=$(hostname -s)
cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos
[Service]
ExecStart=/usr/local/bin/etcd \\
--name ${ETCD_NAME} \\
--cert-file=/etc/etcd/etcd-server.crt \\
--key-file=/etc/etcd/etcd-server.key \\
--peer-cert-file=/etc/etcd/etcd-server.crt \\
--peer-key-file=/etc/etcd/etcd-server.key \\
--trusted-ca-file=/etc/etcd/ca.crt \\
--peer-trusted-ca-file=/etc/etcd/ca.crt \\
--peer-client-cert-auth \\
--client-cert-auth \\
--initial-advertise-peer-urls https://${MASTER_1}:2380 \\
--listen-peer-urls https://${MASTER_1}:2380 \\
--listen-client-urls https://${MASTER_1}:2379,https://127.0.0.1:2379 \\
--advertise-client-urls https://${MASTER_1}:2379 \\
--initial-cluster-token etcd-cluster-0 \\
--initial-cluster ${ETCD_NAME}=https://${MASTER_1}:2380 \\
--initial-cluster-state new \\
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
Nota bene
Для проверки работоспособности etcd можно посмотреть логи, для этого выполните следующую команду:
Control plane
Для начала нам необходимо получить бинарники компонентов:
wget -q --show-progress --https-only --timestamping \
"https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/amd64/kube-apiserver" \
"https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/amd64/kube-controller-manager" \
"https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/amd64/kube-scheduler" \
sudo chmod +x kube-apiserver kube-controller-manager kube-scheduler
sudo mv kube-apiserver kube-controller-manager kube-scheduler /usr/local/bin/
sudo cp encryption-config.yaml /var/lib/kubernetes/
sudo mkdir -p /var/lib/kubernetes/pki
sudo cp ca.crt ca.key /var/lib/kubernetes/pki
for c in kube-apiserver service-account apiserver-kubelet-client etcd-server kube-scheduler kube-controller-manager
do
sudo mv "$c.crt" "$c.key" /var/lib/kubernetes/pki/
done
Kube-apiserver
Создаём systemd unit:
cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${MASTER_1} \\
--allow-privileged=true \\
--apiserver-count=1 \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/audit.log \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--client-ca-file=/var/lib/kubernetes/pki/ca.crt \\
--enable-admission-plugins=NodeRestriction,ServiceAccount \\
--enable-bootstrap-token-auth=true \\
--etcd-cafile=/var/lib/kubernetes/pki/ca.crt \\
--etcd-certfile=/var/lib/kubernetes/pki/etcd-server.crt \\
--etcd-keyfile=/var/lib/kubernetes/pki/etcd-server.key \\
--etcd-servers=https://${MASTER_1}:2379 \\
--event-ttl=1h \\
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
--kubelet-certificate-authority=/var/lib/kubernetes/pki/ca.crt \\
--kubelet-client-certificate=/var/lib/kubernetes/pki/apiserver-kubelet-client.crt \\
--kubelet-client-key=/var/lib/kubernetes/pki/apiserver-kubelet-client.key \\
--runtime-config=api/all=true \\
--service-account-key-file=/var/lib/kubernetes/pki/service-account.crt \\
--service-account-signing-key-file=/var/lib/kubernetes/pki/service-account.key \\
--service-account-issuer=https://${MASTER_1}:6443 \\
--service-cluster-ip-range=${SERVICE_CIDR} \\
--service-node-port-range=30000-32767 \\
--tls-cert-file=/var/lib/kubernetes/pki/kube-apiserver.crt \\
--tls-private-key-file=/var/lib/kubernetes/pki/kube-apiserver.key \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
Controller Manager
Копируем kubeconfig для controller manager:
Создаём systemd unit:cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
--allocate-node-cidrs=true \\
--authentication-kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
--authorization-kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
--bind-address=127.0.0.1 \\
--client-ca-file=/var/lib/kubernetes/pki/ca.crt \\
--cluster-cidr=${POD_CIDR} \\
--cluster-name=kubernetes \\
--cluster-signing-cert-file=/var/lib/kubernetes/pki/ca.crt \\
--cluster-signing-key-file=/var/lib/kubernetes/pki/ca.key \\
--controllers=*,bootstrapsigner,tokencleaner \\
--kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
--leader-elect=true \\
--node-cidr-mask-size=24 \\
--requestheader-client-ca-file=/var/lib/kubernetes/pki/ca.crt \\
--root-ca-file=/var/lib/kubernetes/pki/ca.crt \\
--service-account-private-key-file=/var/lib/kubernetes/pki/service-account.key \\
--service-cluster-ip-range=${SERVICE_CIDR} \\
--use-service-account-credentials=true \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
Scheduler
Копируем kubeconfig для scheduler:
Создаём systemd unit:cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
--kubeconfig=/var/lib/kubernetes/kube-scheduler.kubeconfig \\
--leader-elect=true \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
Запуск компонентов
Перед запуском выставим верные права на все kubeconfig
Далее настраиваем автостарт и запускаем все необходимые компоненты:sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
Nota bene
Для проверки работоспособности control plane можно посмотреть логи этих компонентов, дополнительно можно запросить статус у самого cp:
Добавление узла в кластер
Предупреждение
Действия в этом разделе выполняются как на master узле так и на worker, будьте внимательны
После того как мы закончили с master узлом, нам нужно добавить в кластер машинки на которых будут крутиться наши нагрузки.
Создание конфигурации
Внимание
Дальнейшие действия производятся на master узле
Выпускаем сертификат для регистрации kubelet, для этого сначала создадим конфигурацию:
cat > openssl-k8shw-1.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = k8shw-1
IP.1 = ${WORKER_1}
EOF
После этого выпускаем сертификат и ключ
openssl genrsa -out k8shw-1.key 2048
openssl req -new -key k8shw-1.key -subj "/CN=system:node:k8shw-1/O=system:nodes" -out k8shw-1.csr -config openssl-k8shw-1.cnf
openssl x509 -req -in k8shw-1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out k8shw-1.crt -extensions v3_req -extfile openssl-k8shw-1.cnf -days 1000
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=/var/lib/kubernetes/pki/ca.crt \
--server=https://${MASTER_1}:6443 \
--kubeconfig=k8shw-1.kubeconfig
kubectl config set-credentials system:node:k8shw-1 \
--client-certificate=/var/lib/kubernetes/pki/k8shw-1.crt \
--client-key=/var/lib/kubernetes/pki/k8shw-1.key \
--kubeconfig=k8shw-1.kubeconfig
kubectl config set-context default \
--cluster=kubernetes-the-hard-way \
--user=system:node:k8shw-1 \
--kubeconfig=k8shw-1.kubeconfig
kubectl config use-context default --kubeconfig=k8shw-1.kubeconfig
Переносим все необходимые файлы с мастера на worker
Регистрация рабочего узла
Внимание
Дальнейшие действия производятся на worker узле
Загружаем необходимые компоненты:
wget -q --show-progress --https-only --timestamping \
https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/amd64/kube-proxy \
https://dl.k8s.io/release/${KUBE_VERSION}/bin/linux/amd64/kubelet
sudo mkdir -p \
/var/lib/kubelet \
/var/lib/kube-proxy \
/var/lib/kubernetes/pki \
/var/run/kubernetes
sudo mv ${HOSTNAME}.key ${HOSTNAME}.crt /var/lib/kubernetes/pki/
sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubelet.kubeconfig
sudo mv ca.crt /var/lib/kubernetes/pki/
sudo mv kube-proxy.crt kube-proxy.key /var/lib/kubernetes/pki/
sudo chown root:root /var/lib/kubernetes/pki/*
sudo chown root:root /var/lib/kubelet/*
cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: /var/lib/kubernetes/pki/ca.crt
authorization:
mode: Webhook
containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock
clusterDomain: cluster.local
clusterDNS:
- ${CLUSTER_DNS}
cgroupDriver: systemd
resolvConf: /run/systemd/resolve/resolv.conf
runtimeRequestTimeout: "15m"
tlsCertFile: /var/lib/kubernetes/pki/${HOSTNAME}.crt
tlsPrivateKeyFile: /var/lib/kubernetes/pki/${HOSTNAME}.key
registerNode: true
EOF
cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
kubeconfig: /var/lib/kube-proxy/kube-proxy.kubeconfig
mode: ipvs
clusterCIDR: ${POD_CIDR}
EOF
cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-proxy \\
--config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
На этом настройки завершены, можно настроить автостарт и запустить компоненты:
sudo systemctl daemon-reload
sudo systemctl enable kubelet kube-proxy
sudo systemctl start kubelet kube-proxy
Nota bene
Для проверки работоспособности proxy\kubelet можно посмотреть логи этих компонентов:
Регистрация узла control plane
Nota bene
Это опциональный шаг, его можно пропустить.
By design, если не зарегистрировать узел в кластере, он не будет отображаться в
Соответственно если мы хотим взаимодействовать с узлами control plane из кластера, нам необходимо пройтись по вот этой инструкции
Та инструкция разделена на 2 этапа:
- Действия которые необходимо выполнить на master
- Действия которые необходимо выполнить на worker
В нашем случае worker и master это один и тот-же узел, соответственно все действия из этого пункта мы выполняем на master узле.
Наполнение кластера
К этому моменту наш кластер запущен, но все еще не готов к рабочим нагрузкам, нам нужно еще как минимум два важных компонента, это cni flannel и coredns
Внимание
Дальнейшие действия производятся на master узле
CNI Flannel
Flannel — это простой и легкий способ настройки сетевой структуры третьего уровня, предназначенной для Kubernetes. (с) Flannel
Flannel один из самых простых представителей сетевого стека для k8s, он не поддерживает сетевые политики, но в рамках этой статьи они нам и не нужны.
Для установки нам понадобится манифест , в котором необходимо исправить параметр Network.
Готовый манифест который можно сразу применить в кластере:
flannel.yaml
apiVersion: v1
kind: Namespace
metadata:
labels:
k8s-app: flannel
pod-security.kubernetes.io/enforce: privileged
name: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: flannel
name: flannel
namespace: kube-flannel
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: flannel
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
- apiGroups:
- networking.k8s.io
resources:
- clustercidrs
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: flannel
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-flannel
---
apiVersion: v1
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "172.20.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
kind: ConfigMap
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
name: kube-flannel-cfg
namespace: kube-flannel
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
name: kube-flannel-ds
namespace: kube-flannel
spec:
selector:
matchLabels:
app: flannel
k8s-app: flannel
template:
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
containers:
- args:
- --ip-masq
- --kube-subnet-mgr
command:
- /opt/bin/flanneld
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: "5000"
image: docker.io/flannel/flannel:v0.23.0
name: kube-flannel
resources:
requests:
cpu: 100m
memory: 50Mi
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
privileged: false
volumeMounts:
- mountPath: /run/flannel
name: run
- mountPath: /etc/kube-flannel/
name: flannel-cfg
- mountPath: /run/xtables.lock
name: xtables-lock
hostNetwork: true
initContainers:
- args:
- -f
- /flannel
- /opt/cni/bin/flannel
command:
- cp
image: docker.io/flannel/flannel-cni-plugin:v1.2.0
name: install-cni-plugin
volumeMounts:
- mountPath: /opt/cni/bin
name: cni-plugin
- args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
command:
- cp
image: docker.io/flannel/flannel:v0.23.0
name: install-cni
volumeMounts:
- mountPath: /etc/cni/net.d
name: cni
- mountPath: /etc/kube-flannel/
name: flannel-cfg
priorityClassName: system-node-critical
serviceAccountName: flannel
tolerations:
- effect: NoSchedule
operator: Exists
volumes:
- hostPath:
path: /run/flannel
name: run
- hostPath:
path: /opt/cni/bin
name: cni-plugin
- hostPath:
path: /etc/cni/net.d
name: cni
- configMap:
name: kube-flannel-cfg
name: flannel-cfg
- hostPath:
path: /run/xtables.lock
type: FileOrCreate
name: xtables-lock
DNS
После установки кластера у нас отсутствует DNS сервер, что в свою очередь не позволяет работать внутренним резолверам. Например не получиться организовать взаимодействие между подами по имени сервиса. Для того чтобы исправить эту ситуацию нам понадобится CoreDNS.
Готовый манифест который можно сразу применить в кластере:
coredns.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:coredns
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/name: "CoreDNS"
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
priorityClassName: system-cluster-critical
serviceAccountName: coredns
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
nodeSelector:
kubernetes.io/os: linux
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values: ["kube-dns"]
topologyKey: kubernetes.io/hostname
containers:
- name: coredns
image: coredns/coredns:1.9.4
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 172.21.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
- name: metrics
port: 9153
protocol: TCP
Проверка кластера
Мы выполним пару проверок: - Проверка статуса нод - Проверка DNS сервера - Проверка готовности кластера запускать нагрузки
Внимание
Дальнейшие действия производятся на master узле
Статус узлов
Для проверки статуса узлов, достаточно выполнить следующую команду:
Если узлы в поле STATUS имеют строку Ready, значит все узлы готовы к работе.DNS
Для проверки днс сервера, сначало нам нужно создать POD в котором мы будем работать:
kubectl --kubeconfig admin.kubeconfig run busybox -n default --image=busybox:1.28 --restart Never --command -- sleep 150
Запуск рабочей нагрузки
Этим пунктом мы проверим все критически важные компоненты в k8s для начала запустим pod с nginx
Далее, откроем доступ к этому деплойменту из вне, для этого создадим service с типом NodePort Во время создания такого сервиса, будет использован рандомный реальный порт с worker узла, чтобы узнать какой именно - выполним комманду: Теперь у нас есть все чтобы проверить наш деплоймент, открываем браузер и переходим по адресу в моём же случае итоговая ссылка выглядит такЕсли в браузере мы видим стандартную страничку nginx - все работает отлично, поздравляю!
Post Scriptum
После прохождения этого материалы мы смогли развернуть кластер куба без использования средств автоматизации, я надеюсь что теперь вы чуть лучше понимаете как устроен куб и как его компоненты взаимодействуют друг с другом.
Хотелось бы отдельно отметить работу Kelsey Hightower, без неё этой статьи бы не было.