Setup Kubernetes Cluster Using Kubeadm: Difference between revisions

From wiki.baghirzade.pro
Jump to navigation Jump to search
No edit summary
No edit summary
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Kubeadm Setup Prerequisites ==
На каждую VM:
Following are the prerequisites for '''Kubeadm Kubernetes cluster setup'''.


# Minimum two '''Ubuntu nodes''' [One master and one worker node]. You can have more worker nodes as per your requirement.
* CPU: '''2+ vCPU''' (control-plane лучше 2–4)
# The master node should have a minimum of '''2 vCPU and 2GB RAM'''.
* RAM: '''min 2GB''', лучше '''4GB''' (control-plane 4–8GB)
# For the worker nodes, a minimum of 1vCPU and 2 GB RAM is recommended.
* Disk: '''20GB+'''
# '''10.X.X.X/X''' network range with static IPs for master and worker nodes. We will be using the '''192.x.x.x''' series as the pod network range that will be used by the Calico network plugin. Make sure the Node IP range and pod IP range don't overlap.
* Сеть: все VM '''в одной L2 сети''', чтобы они пинговались по <code>172.16.221.0/24</code>.
Важно для клонов VM:


'''На cp1:'''<pre>
* у каждой ноды должны быть '''уникальные hostname, MAC и product_uuid''' (kubeadm ругается если одинаковые). Проверка:
sudo hostnamectl set-hostname cp1
<syntaxhighlight lang="bash">
</pre>'''На worker1:'''<pre>
hostname
sudo hostnamectl set-hostname w1
ip link | grep link/
</pre>'''На worker2:'''<pre>sudo hostnamectl set-hostname w2</pre>'''На всех нодах''' добавь записи в <code>/etc/hosts</code>:<pre>
sudo cat /sys/class/dmi/id/product_uuid
sudo nano /etc/hosts
</pre>В конец файла допиши, подставив свои IP:<pre>
10.0.0.10  cp1
10.0.0.11  w1
10.0.0.12  w2
</pre>Это позволяет нодам обращаться друг к другу по имени, а не по IP.


'''1. Общая подготовка системы (все ноды)'''
</syntaxhighlight>(уникальность прямо указана в kubeadm prerequisites


'''1.1. Обновляем пакеты'''<pre>
== 1) Присвой имена и пропиши hosts (на ВСЕХ нодах) ==
sudo apt update
На каждой ноде задай hostname:
sudo apt upgrade -y
</pre>Просто приводим систему в свежий вид, чтобы избежать багов из-за старых пакетов.


'''1.2. Отключаем swap (обязательно для kubeadm)'''
'''cp1:'''<syntaxhighlight lang="bash">
sudo hostnamectl set-hostname cp1
</syntaxhighlight>'''worker1:'''<syntaxhighlight lang="bash">
sudo hostnamectl set-hostname worker1
</syntaxhighlight>'''worker2:'''<syntaxhighlight lang="bash">
sudo hostnamectl set-hostname worker2
</syntaxhighlight>'''worker3:'''<syntaxhighlight lang="bash">
sudo hostnamectl set-hostname worker3
</syntaxhighlight>На '''всех''' нодах добавь в <code>/etc/hosts</code>:<syntaxhighlight lang="bash">
sudo tee -a /etc/hosts >/dev/null <<'EOF'
172.16.221.10 cp1
172.16.221.11 worker1
172.16.221.12 worker2
172.16.221.13 worker3
EOF
</syntaxhighlight>2) Базовая подготовка ОС (на ВСЕХ нодах)


На всех нодах:<pre>
2.1 Отключи swap (обязательно)<syntaxhighlight lang="bash">
sudo swapoff -a
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab
</pre>Проверка:<pre>
</syntaxhighlight>2.2 Включи нужные модули + sysctl<syntaxhighlight lang="bash">
free -h
sudo modprobe overlay
</pre>'''1.3. Модули ядра и sysctl (для сетки)'''
sudo modprobe br_netfilter


На '''каждой''' ноде:<pre>
sudo tee /etc/modules-load.d/k8s.conf >/dev/null <<'EOF'
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
overlay
br_netfilter
br_netfilter
EOF
EOF
 
</syntaxhighlight>Sysctl:<syntaxhighlight lang="bash">
sudo modprobe overlay
sudo tee /etc/sysctl.d/k8s.conf >/dev/null <<'EOF'
sudo modprobe br_netfilter
net.ipv4.ip_forward = 1
</pre>Теперь sysctl:<pre>
net.bridge.bridge-nf-call-iptables = 1
cat <<EOF | sudo tee /etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                = 1
EOF
EOF


sudo sysctl --system
sudo sysctl --system
</pre>Это включает корректную обработку трафика через Linux bridge и форвардинг — обязательно для CNI (Calico).
</syntaxhighlight>Проверка (ip_forward должен быть 1) <syntaxhighlight lang="bash">
sysctl net.ipv4.ip_forward
</syntaxhighlight>3) Установка containerd (на ВСЕХ нодах)


'''1.4. (Для лаба) Отключить UFW'''
3.1 Поставь containerd<syntaxhighlight lang="bash">
sudo apt-get update
sudo apt-get install -y containerd
</syntaxhighlight>3.2 Сгенерируй конфиг и включи SystemdCgroup<syntaxhighlight lang="bash">
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
</syntaxhighlight>Открой файл:<syntaxhighlight lang="bash">
sudo nano /etc/containerd/config.toml
</syntaxhighlight>Найди секцию runc options и поставь:
 
* для containerd '''1.x''':
<syntaxhighlight lang="bash">
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
</syntaxhighlight>


Если UFW включён и это чистый лаб:<pre>
* для containerd 2'''.x''':
sudo ufw disable
<syntaxhighlight lang="bash">
</pre>В проде, конечно, лучше открыть нужные порты, но для первой сборки проще без firewall.
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
  SystemdCgroup = true
</syntaxhighlight>(это требование и примеры прямо в доках Kubernetes


'''2. Устанавливаем containerd (все ноды)'''
Также убедись, что '''CRI не отключен''': в <code>disabled_plugins</code> не должно быть <code>"cri"</code>


На '''каждой''' ноде:<pre>
Перезапусти:<syntaxhighlight lang="bash">
sudo apt install -y containerd
</pre>Генерируем дефолтный конфиг:<pre>
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
</pre>Рестарт и включение:<pre>
sudo systemctl restart containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
sudo systemctl enable containerd
sudo systemctl status containerd
</syntaxhighlight>Проверка сокета:<syntaxhighlight lang="bash">
</pre><code>SystemdCgroup = true</code> → kubelet и containerd используют один и тот же cgroup-драйвер (systemd). Это теперь “best practice”.
ls -l /run/containerd/containerd.sock
 
</syntaxhighlight>4) Установка kubeadm/kubelet/kubectl (на ВСЕХ нодах)
'''3. Устанавливаем kubeadm/kubelet/kubectl 1.34 (все ноды)'''
 
По официальным докам для '''v1.34''':
 
'''3.1. Пакеты для репо'''


На '''каждой''' ноде:<pre>
Ставим '''Kubernetes v1.35 (актуальная ветка в доке)''' из '''pkgs.k8s.io'''<syntaxhighlight lang="bash">
sudo apt-get update
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
</pre>'''3.2. GPG-ключ и репозиторий pkgs.k8s.io (v1.34)'''
</syntaxhighlight>Ключ:<syntaxhighlight lang="bash">
 
Ubuntu 22.04 уже имеет <code>/etc/apt/keyrings</code>, но если вдруг нет:<pre>
sudo mkdir -p -m 755 /etc/apt/keyrings
sudo mkdir -p -m 755 /etc/apt/keyrings
</pre>Ключ репозитория:<pre>
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key \
</syntaxhighlight>Репо:<syntaxhighlight lang="bash">
  | 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:/v1.35/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
</pre>Добавляем репо '''именно для Kubernetes 1.34''':<pre>
</syntaxhighlight>Установка и “hold”:<syntaxhighlight lang="bash">
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /" \
  | sudo tee /etc/apt/sources.list.d/kubernetes.list
</pre>'''3.3. Установка kubeadm/kubelet/kubectl'''<pre>
sudo apt-get update
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
</pre><blockquote>Репозиторий <code>v1.34</code> гарантирует, что ставится именно ветка 1.34.x. <code>apt-mark hold</code> не даст случайно обновиться до другой минорной версии при <code>apt upgrade</code></blockquote>(Не обязательно, но можно сразу включить kubelet, он будет крутиться и ждать <code>kubeadm init / join</code>):<pre>
</syntaxhighlight>Включи kubelet:<syntaxhighlight lang="bash">
sudo systemctl enable --now kubelet
sudo systemctl enable --now kubelet
</pre>'''5. Инициализация control-plane (только cp1)'''
</syntaxhighlight>(эти команды и repo-URL — из официальной страницы установки kubeadm


Теперь на <code>cp1</code> создаём кластер.
5) Инициализация control-plane (ТОЛЬКО на cp1 = 172.16.221.10)


'''5.1. kubeadm init с pod-CIDR для Calico'''
5.1 kubeadm init


Calico по умолчанию использует <code>192.168.0.0/16</code>. Для удобства берём его же:
Я использую pod CIDR '''192.168.0.0/16''' (дефолт под Calico). Calico это любит, а документация Calico для kubeadm прямо его упоминает


На <code>cp1</code>:<pre>
На '''cp1''':<syntaxhighlight lang="bash">
sudo kubeadm init \
sudo kubeadm init \
   --apiserver-advertise-address=10.0.0.10 \
   --apiserver-advertise-address=172.16.221.10 \
   --pod-network-cidr=192.168.0.0/16
   --pod-network-cidr=192.168.0.0/16
</pre>
</syntaxhighlight>После успеха kubeadm покажет '''kubeadm join ...''' '''сохрани его''' (для worker-нод).
 
* <code>--apiserver-advertise-address</code> — IP cp1, по которому все будут ходить на API.
* <code>--pod-network-cidr</code> диапазон IP для pod’ов, который будет использовать Calico.
 
В конце <code>kubeadm</code> выдаст:


* сообщение об успешной инициализации;
5.2 Настрой kubectl для твоего пользователя
* '''команду <code>kubeadm join ...</code>''' — обязательно скопируй её куда-нибудь (будем использовать на worker’ах).


'''5.2. Настраиваем kubectl для юзера <code>sadmin</code> (cp1)'''
На cp1 (под обычным юзером, не root):<syntaxhighlight lang="bash">
 
Сейчас kubeconfig лежит в <code>/etc/kubernetes/admin.conf</code> и принадлежит root’у.
 
На <code>cp1</code> под <code>sadmin</code>:<pre>
mkdir -p $HOME/.kube
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
</pre>Проверка:<pre>
</syntaxhighlight>Проверка:<syntaxhighlight lang="bash">
kubectl get nodes
kubectl get nodes
</pre>Пока сеть не поднята, нода может быть <code>NotReady</code> — это нормально.
</syntaxhighlight>На этом этапе cp1 будет <code>NotReady</code> — это нормально, пока не поставим CNI (Calico).


'''6. Устанавливаем Calico 3.31 (только cp1)'''
6) Установка Calico (на cp1)


Берём свежий Calico 3.31.1 из официальной инструкции для on-prem.
Чтобы '''не ловить проблемы с operator-crds/annotations''', я даю '''manifest-вариант''' (самый прямой и обычно “без сюрпризов” для учебных кластеров).


'''6.1. Tigera Operator + CRD'''
На '''cp1''':<syntaxhighlight lang="bash">
 
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/calico.yaml
На <code>cp1</code>:<pre>
kubectl apply -f calico.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.1/manifests/operator-crds.yaml
</syntaxhighlight>(официальная дока Calico для on-prem/kubeadm: скачать <code>calico.yaml</code> и применить
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.1/manifests/tigera-operator.yaml
</pre><blockquote>Это ставит оператор Calico, который потом сам раскатает нужные компоненты по нодам.</blockquote>'''6.2. Конфигурация Calico (custom-resources)'''
 
По дефолту используем iptables-датаплейн (без eBPF, чтоб пока не усложнять).
 
На <code>cp1</code>:<pre>
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.31.1/manifests/custom-resources.yaml
kubectl create -f custom-resources.yaml
</pre>(Если хочешь eBPF в будущем — там же есть <code>custom-resources-bpf.yaml</code>.)
 
'''6.3. Проверяем, что Calico поднялся'''
 
На <code>cp1</code>:<pre>
watch kubectl get tigerastatus
</pre>Ждём, пока всё будет <code>AVAILABLE=True</code>:<pre>
NAME              AVAILABLE  PROGRESSING  DEGRADED  SINCE
calico            True        False        False      ...
ippools          True        False        False      ...
...
</pre>И смотрим pod’ы:<pre>
kubectl get pods -n calico-system
kubectl get pods -n kube-system
kubectl get nodes
</pre>cp1 должен перейти в статус <code>Ready</code>.
 
'''7. Делаем control-plane “чистым” (без workload-подов)'''
 
По умолчанию в 1.34 control-plane '''может''' принимать обычные поды (taint не ставится автоматически).
 
Тебе этого '''не нужно''', поэтому сразу ставим taint:
 
На <code>cp1</code>:<pre>
kubectl taint nodes cp1 node-role.kubernetes.io/control-plane=:NoSchedule
</pre>Проверка:<pre>
kubectl describe node cp1 | grep -i Taint
</pre>Должно быть что-то типа:<pre>
Taints: node-role.kubernetes.io/control-plane:NoSchedule
</pre><blockquote>Теперь scheduler НЕ будет ставить обычные pod’ы на cp1. Там останутся только системные компоненты (kube-apiserver, etcd, kube-controller-manager, scheduler, Calico DaemonSet и т.п.).</blockquote>'''8. Подключаем worker1 и worker2'''
 
'''8.1. Получаем join-команду (если потерял)'''
 
Если не сохранил вывод <code>kubeadm init</code>, на <code>cp1</code>:<pre>
sudo kubeadm token create --print-join-command
</pre>Пример:<pre>
kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxxxxxxx...
</pre>'''8.2. Выполняем join на worker1 / worker2'''
 
На <code>worker1</code>:<pre>
sudo kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxxxxxxx...
</pre>На <code>worker2</code>:<pre>
sudo kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxxxxxxx...
</pre><blockquote>Здесь используется токен и хеш CA, чтобы worker’ы безопасно присоединились к кластеру.</blockquote>После этого на <code>cp1</code>:<pre>
kubectl get nodes
</pre>Ожидаемый результат:<pre>
NAME      STATUS  ROLES          AGE  VERSION
cp1      Ready    control-plane  20m  v1.34.x
worker1  Ready    <none>          5m    v1.34.x
worker2  Ready    <none>          3m    v1.34.x
</pre>Если worker’ы какое-то время <code>NotReady</code> — подожди, пока на них подтянутся и стартанут kube-proxy + Calico.


'''9. Проверка: поды только на worker-нодах'''
Подожди, пока поднимется:<syntaxhighlight lang="bash">
kubectl get pods -n kube-system -w
</syntaxhighlight>Проверка, что ноды стали Ready:<syntaxhighlight lang="bash">
kubectl get nodes -o wide
</syntaxhighlight>7) Подключение worker-нод (на worker1/2/3)


Создадим тестовый deployment:
На '''каждой''' worker-ноде выполни команду, которую показал <code>kubeadm init</code> на cp1, вида:<syntaxhighlight lang="bash">
sudo kubeadm join 172.16.221.10:6443 --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<HASH>
</syntaxhighlight>После подключения всех:<syntaxhighlight lang="bash">
kubectl get nodes -o wide
</syntaxhighlight>8) Быстрые проверки “всё ок”


На <code>cp1</code>:<pre>
8.1 Системные поды<syntaxhighlight lang="bash">
kubectl create deployment nginx --image=nginx --replicas=3
kubectl -n kube-system get pods -o wide
kubectl expose deployment nginx --port=80 --type=NodePort
</syntaxhighlight>8.2 CoreDNS должен быть Running<syntaxhighlight lang="bash">
</pre>Смотрим:<pre>
kubectl -n kube-system get deploy coredns
kubectl get pods -o wide
</syntaxhighlight>9) Частые проблемы и как чинить (коротко)
</pre>Все pod’ы <code>nginx</code> должны сидеть на <code>worker1</code> и <code>worker2</code>, '''но не на cp1'''.


Если вдруг увидишь под на cp1, значит taint не применился → ещё раз:<pre>
1) Node NotReady после Calico
kubectl taint nodes cp1 node-role.kubernetes.io/control-plane=:NoSchedule --overwrite
kubectl rollout restart deploy nginx
</pre>✅ '''1. Как удалить эти pod’ы (Deployment + Service)'''


Ты создал:
смотри:<syntaxhighlight lang="bash">
kubectl -n kube-system get pods -o wide
kubectl -n kube-system describe pod <calico-node-...>
</syntaxhighlight>проверь sysctl/модули (<code>br_netfilter</code>, <code>ip_forward=1</code>)
2) kubelet ругается на cgroup


* Deployment: '''nginx'''
* 99%: не включил <code>SystemdCgroup = true</code> в containerd или CRI отключен Kubernetes
* Service: '''nginx'''
* после правки:
<syntaxhighlight lang="bash">
sudo systemctl restart containerd kubelet
</syntaxhighlight>3) Token протух


Чтобы удалить:<pre>
На cp1 сгенерь новый join:<syntaxhighlight lang="bash">
kubectl delete service nginx
kubeadm token create --print-join-command
</pre>Удалить Deployment:<pre>
</syntaxhighlight>
kubectl delete deployment nginx
</pre>Проверяем:<pre>
kubectl get pods
kubectl get svc
</pre>📌 '''Если хочешь удалить ВСЁ, что относится к nginx, одной командой:'''<pre>
kubectl delete deploy,svc nginx
</pre>

Latest revision as of 13:51, 7 January 2026

На каждую VM:

  • CPU: 2+ vCPU (control-plane лучше 2–4)
  • RAM: min 2GB, лучше 4GB (control-plane 4–8GB)
  • Disk: 20GB+
  • Сеть: все VM в одной L2 сети, чтобы они пинговались по 172.16.221.0/24.

Важно для клонов VM:

  • у каждой ноды должны быть уникальные hostname, MAC и product_uuid (kubeadm ругается если одинаковые). Проверка:
hostname
ip link | grep link/
sudo cat /sys/class/dmi/id/product_uuid

(уникальность прямо указана в kubeadm prerequisites

1) Присвой имена и пропиши hosts (на ВСЕХ нодах)

На каждой ноде задай hostname:

cp1:

sudo hostnamectl set-hostname cp1

worker1:

sudo hostnamectl set-hostname worker1

worker2:

sudo hostnamectl set-hostname worker2

worker3:

sudo hostnamectl set-hostname worker3

На всех нодах добавь в /etc/hosts:

sudo tee -a /etc/hosts >/dev/null <<'EOF'
172.16.221.10 cp1
172.16.221.11 worker1
172.16.221.12 worker2
172.16.221.13 worker3
EOF

2) Базовая подготовка ОС (на ВСЕХ нодах) 2.1 Отключи swap (обязательно)

sudo swapoff -a
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab

2.2 Включи нужные модули + sysctl

sudo modprobe overlay
sudo modprobe br_netfilter

sudo tee /etc/modules-load.d/k8s.conf >/dev/null <<'EOF'
overlay
br_netfilter
EOF

Sysctl:

sudo tee /etc/sysctl.d/k8s.conf >/dev/null <<'EOF'
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

sudo sysctl --system

Проверка (ip_forward должен быть 1)

sysctl net.ipv4.ip_forward

3) Установка containerd (на ВСЕХ нодах) 3.1 Поставь containerd

sudo apt-get update
sudo apt-get install -y containerd

3.2 Сгенерируй конфиг и включи SystemdCgroup

sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml >/dev/null

Открой файл:

sudo nano /etc/containerd/config.toml

Найди секцию runc options и поставь:

  • для containerd 1.x:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
  • для containerd 2.x:
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
  SystemdCgroup = true

(это требование и примеры прямо в доках Kubernetes

Также убедись, что CRI не отключен: в disabled_plugins не должно быть "cri"

Перезапусти:

sudo systemctl restart containerd
sudo systemctl enable containerd

Проверка сокета:

ls -l /run/containerd/containerd.sock

4) Установка kubeadm/kubelet/kubectl (на ВСЕХ нодах) Ставим Kubernetes v1.35 (актуальная ветка в доке) из pkgs.k8s.io

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

Ключ:

sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/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:/v1.35/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

Установка и “hold”:

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Включи kubelet:

sudo systemctl enable --now kubelet

(эти команды и repo-URL — из официальной страницы установки kubeadm

5) Инициализация control-plane (ТОЛЬКО на cp1 = 172.16.221.10)

5.1 kubeadm init

Я использую pod CIDR 192.168.0.0/16 (дефолт под Calico). Calico это любит, а документация Calico для kubeadm прямо его упоминает

На cp1:

sudo kubeadm init \
  --apiserver-advertise-address=172.16.221.10 \
  --pod-network-cidr=192.168.0.0/16

После успеха kubeadm покажет kubeadm join ...сохрани его (для worker-нод).

5.2 Настрой kubectl для твоего пользователя

На cp1 (под обычным юзером, не root):

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Проверка:

kubectl get nodes

На этом этапе cp1 будет NotReady — это нормально, пока не поставим CNI (Calico).

6) Установка Calico (на cp1)

Чтобы не ловить проблемы с operator-crds/annotations, я даю manifest-вариант (самый прямой и обычно “без сюрпризов” для учебных кластеров).

На cp1:

curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/calico.yaml
kubectl apply -f calico.yaml

(официальная дока Calico для on-prem/kubeadm: скачать calico.yaml и применить Подожди, пока поднимется:

kubectl get pods -n kube-system -w

Проверка, что ноды стали Ready:

kubectl get nodes -o wide

7) Подключение worker-нод (на worker1/2/3) На каждой worker-ноде выполни команду, которую показал kubeadm init на cp1, вида:

sudo kubeadm join 172.16.221.10:6443 --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<HASH>

После подключения всех:

kubectl get nodes -o wide

8) Быстрые проверки “всё ок” 8.1 Системные поды

kubectl -n kube-system get pods -o wide

8.2 CoreDNS должен быть Running

kubectl -n kube-system get deploy coredns

9) Частые проблемы и как чинить (коротко)

1) Node NotReady после Calico

смотри:

kubectl -n kube-system get pods -o wide
kubectl -n kube-system describe pod <calico-node-...>

проверь sysctl/модули (br_netfilter, ip_forward=1)

2) kubelet ругается на cgroup

  • 99%: не включил SystemdCgroup = true в containerd или CRI отключен Kubernetes
  • после правки:
sudo systemctl restart containerd kubelet

3) Token протух На cp1 сгенерь новый join:

kubeadm token create --print-join-command