Установка k8s на Control Plane
Жизненный цикл k8s кластера
1. Linux на control plane
2. containerd
3. kubelet/kubeadm/kubectl
4. kubeadm init
5. Control Plane запущен
6. CNI
7. Node становится Ready
8. Готовим worker
9. kubeadm join
10. Проверяем двухнодовый кластер
11. Запускаем первый nginx
12. Deployment → ReplicaSet → Pods
13. Service
14. поломки/ремонт/обновления
Список команд для установки Control Plane
Разверните чтобы посмотреть
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
df -h /
sudo swapoff -a
free -h
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab
cat /etc/fstab
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
lsmod | grep -E 'overlay|br_netfilter'
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.bridge.bridge-nf-call-ip6tables
sysctl net.ipv4.ip_forward
clear
lsmod | grep -E 'overlay|br_netfilter'
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.ipv4.ip_forward
hostname
hostname -I
sudo apt update
sudo apt install -y containerd
containerd --version
systemctl status containerd --no-pager
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
grep SystemdCgroup /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
systemctl is-active containerd
systemctl is-enabled containerd
sudo apt update
sudo apt 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.34/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.34/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
kubeadm version
kubectl version --client
kubelet --version
systemctl status kubelet --no-pager
clear
kubeadm version
kubectl version --client
kubelet --version
ip route
sudo kubeadm init --apiserver-advertise-address=10.211.55.26 --pod-network-cidr=192.168.0.0/16
ping -c 3 8.8.8.8
curl -I https://registry.k8s.io
sudo kubeadm config images pull --kubernetes-version v1.34.11
sudo kubeadm init --apiserver-advertise-address=10.211.55.26 --pod-network-cidr=192.168.0.0/16
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
kubectl get nodes -o wide
kubectl get pods -n kube-system
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/calico.yaml
kubectl get pods -n kube-system -w
kubectl get nodes
Создание кластера через kubadm
Разверните чтобы посмотреть
sudo kubeadm init \
--apiserver-advertise-address=192.168.64.11 \
--pod-network-cidr=10.244.0.0/16
I0910 08:06:13.155023 21548 version.go:260] remote version is much newer: v1.37.0; falling back to: stable-1.34
[init] Using Kubernetes version: v1.34.11
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-cp1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.64.11]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-cp1 localhost] and IPs [192.168.64.11 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-cp1 localhost] and IPs [192.168.64.11 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "super-admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/instance-config.yaml"
[patches] Applied patch of type "application/strategic-merge-patch+json" to target "kubeletconfiguration"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests"
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 458.417µs
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://192.168.64.11:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-controller-manager is healthy after 1.986125ms
[control-plane-check] kube-scheduler is healthy after 2.391875ms
[control-plane-check] kube-apiserver is healthy after 1.501441792s
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-cp1 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-cp1 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: p4ijkw.gsdb165pwo782lpg
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Configured RBAC rules to allow the API server kubelet client certificate to access the kubelet API
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.64.11:6443 --token p4ijkw.gsdb165pwo782lpg \
--discovery-token-ca-cert-hash sha256:230deb897ea7d43ab2b06497ea00437800a183cdb40845d90d45e7e02be5d887
Разбор команды kubeadm init (на control plane)
192.168.64.11
│
└── IP самой Linux VM k8s-cp1
по нему будет доступен API Server
10.244.0.0/16
│
└── адресное пространство будущей Pod-сети
Важно
Сам kubeadm Pod-сеть не строит. Поэтому CNI нам ещё предстоит установить.
1. kubeadm выбирает версию
remote version is much newer: v1.37.0
falling back to: stable-1.34
[init] Using Kubernetes version: v1.34.11
2. Preflisght check
суть:
3. Скачиваются container images
зачем
Control plane компоненты будут работать в контейнерах
поэтому kubeadm обеспечивает наличие образов для:
посмотреть список необходимых образов можно:
заранее скачать:
схема:
4. [certs] - создается PKI Kubernetes
зачем
kubeadm создает PKI кластера
Kubernetes-компоненты не должны просто доверять любому, кто пришёл по сети.
Они аутентифицируют друг друга с помощью сертификатов.
5. Создается главный CA (Certificate Authority)
появляются примерно:
схематично:
CA является корнем доверия для значительной части Kubernetes PKI.ca.key — очень чувствительный приватный ключ.
6. сертификат API Server
появляются:
API Server работает через HTTPS:
далее идет:
apiserver serving cert is signed for DNS names
[k8s-cp1
kubernetes
kubernetes.default
kubernetes.default.svc
kubernetes.default.svc.cluster.local]
and IPs
[10.96.0.1 192.168.64.11]
Это SAN сертификата.
Поэтому сертификат API Server валиден, в частности, для:
вашего физического Node IP
и:
виртуального service IP Kubernetes API внутри кластера.
7. API Server получет клиентский сертификат для kubelet
API Server иногда сам должен обращаться к kubelet
8. Front proxy
это PKI для aggregation layer Kubernetes API.
9. Отдельный CA для etcd
это отдельная PKI-ветка.
файлы находятся:
10. серверный сертификат etcd
etcd работает тоже по TLS.
причем kubeadm пишет SAN:
то есть etcd получает свою серверную identity.
11. etcd/peer
это сертификат для общения:
В HA control plane может быть:
12. healthcheck etcd
нужен клиент, который может безопасно проверить:
13. API Server -> etcd
API Server должен работать с etcd:
но kubectl напрямую в etcd не ходит
и schedulet напрямую за данными туда ходить не должен.
Правильная архитектурная модель:
API Server - центральный интерфейс к состоянию Kubernetes.
14. Service Account keys
это связано с ServiceAccount и токенами.
15. [kubeconfig]
теперь kubeadm создает конфигурации клиентов API Server
можно посмотреть:
16. admin.conf
это администраторский kubeconfig
в нем содержится:
упрощенно:
поэтому после установки мы делали:
после чего заработал:17. Остальные kubeconfig
создаются:
так как эти компоненты тоже являются клиентами Kubernetes API.
у scheduler есть конфигурация подключения и credentials.
18. Создается etcd static pod
kubeadm создает:
19. Создаются остальные control-plane manifests
Creating static Pod manifest for "kube-apiserver"
Creating static Pod manifest for "kube-controller-manager"
Creating static Pod manifest for "kube-scheduler"
в итоге:
/etc/kubernetes/manifests/
etcd.yaml
kube-apiserver.yaml
kube-controller-manager.yaml
kube-scheduler.yaml
20. Настраивается kubelet
создаются файлы под:
например:
21. Запускается kubelet
kubelet смотрит
видит 4 манифеста и через CRI обеспечивает запуск соответствующих контейнеров
итого:
systemd
│
┌────────┴────────┐
▼ ▼
kubelet containerd
│ ▲
│ CRI │
└─────────────────┘
│
читает static manifests
│
▼
/etc/kubernetes/manifests
│
┌────────────┼─────────────┐
▼ ▼ ▼
etcd apiserver scheduler
controller
22. kubeadm ждет запуска
23. Проверяется kubelet
ответ:
иными словами:
24. Проверяется весь control plane
kube-apiserver
https://192.168.64.11:6443/livez
controller-manager
https://127.0.0.1:10257/healthz
scheduler
https://127.0.0.1:10259/livez
и все отвечают
25. Конфигурация загружается уже в Kubernetes API
до этого kubeadm в основном создавал файлы в Linux
теперь API Server работает, поэтому kubeadm уже может содавать Kubernetes API objects.
ДО:
/etc/kubernetes/...
/var/lib/kubelet/...
Linux filesystem
ПОСЛЕ ЗАПУСКА API:
ConfigMap
Node
Namespace
RBAC
...
Kubernetes objects
26. Node помечаются как control plane
добавляютя labels:
поэтому:
показывает :
27. Добавляется taint
важная строка:
это можно перевести как
схематично:
28. Создается bootstrap token
Новый worker использует его для первоначального bootstrap.
Но токен — не вечный пароль worker.
После присоединения Node получает долгосрочные certificate credentials.
29. Настраиваем RBAC для boostrap
Configuring bootstrap tokens
RBAC Roles
allow Node Bootstrap tokens...
post CSRs...
approve CSRs...
certificate rotation...
другими словами:
Новый worker
│
│ bootstrap token
▼
API Server
│
│ можно ли ему доверять?
▼
CSR
Certificate Signing Request
│
▼
подписание/одобрение
│
▼
worker получает нормальную
certificate identity
То есть token нужен прежде всего для первоначального знакомства.
30. CoreDNS
Теперь kubeadm создаёт CoreDNS.
CoreDNS нужен для DNS внутри Kubernetes.
Например Pod сможет обращаться:
вместо запоминания IP.
но у нас:
Потому что Pod networking ещё нет.
31. kube-proxy
Он участвует в реализации сетевой модели Kubernetes Services на Node.
32. Готов Control Plane
Но это не означает что кластер готов
Control Plane
─────────────
etcd ✓
API Server ✓
scheduler ✓
controller-manager ✓
kubelet ✓
containerd ✓
CoreDNS Pending
CNI ✗
Node NotReady
33. join
означает:
192.168.64.11:6443
│
└── где находится API Server
--token
│
└── временная bootstrap-аутентификация
--discovery-token-ca-cert-hash
│
└── проверка:
"Я точно подключаюсь
к правильному кластеру?"
Вся операция kubeadm init в виде схемы
kubeadm init
│
├─ 1. Проверяет Linux
│
├─ 2. Подготавливает images
│
├─ 3. Создаёт PKI
│ └─ /etc/kubernetes/pki/
│
├─ 4. Создаёт kubeconfig
│ └─ /etc/kubernetes/*.conf
│
├─ 5. Создаёт static Pod manifests
│ └─ /etc/kubernetes/manifests/
│
├─ 6. Конфигурирует kubelet
│ └─ /var/lib/kubelet/
│
├─ 7. kubelet читает manifests
│
├─ 8. containerd запускает контейнеры
│
├─ 9. Поднимаются
│ ├─ etcd
│ ├─ kube-apiserver
│ ├─ scheduler
│ └─ controller-manager
│
├─ 10. kubeadm проверяет health
│
├─ 11. Создаёт Kubernetes-объекты
│ ├─ Node
│ ├─ ConfigMaps
│ ├─ RBAC
│ └─ bootstrap
│
├─ 12. Создаёт CoreDNS + kube-proxy
│
└─ 13. Выдаёт worker join command
После установки получаем следующую схему:
Ubuntu
│
├── systemd
│ │
│ ├── containerd
│ │
│ └── kubelet
│ │
│ │ читает
│ ▼
│ /etc/kubernetes/manifests/
│ │
│ ┌─────┼─────────┐
│ ▼ ▼ ▼
│ etcd API scheduler
│ server +
│ controller
│
└──────────────────────────────
34. CNI
При init мы указали pod-сеть
Теперь нужен CNI-плагин, который эту сеть реализует. Для нашего стенда можем продолжить с Calico.
зафиксируем что есть:
192.168.64.0/24 ← сеть физических Node (UTM)
192.168.64.11 ← k8s-cp1
192.168.64.xx ← будущий worker
10.244.0.0/16 ← сеть Kubernetes Pods
10.244.x.x ← будущие Pod IP
То есть CNI не заменяет обычную сеть Linux. Он строит Pod networking поверх сети между Node.
До CNI
После установки CNI:
CNI
│
┌─────────┴─────────┐
│ │
Pod networking Node networking
│
▼
CoreDNS получает сеть
│
▼
CoreDNS Running
│
▼
Node Ready
Добавление worker node в существующий кластер k8s
На control plane вводим:
На добавляемой worker node выполняем:
sudo kubeadm join 192.168.64.10:6443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234567890abcdef...
Затем на control plane проверяем состояние нод кластера:
Что происходит в join
kubeadm join 192.168.64.10:6443
└────────┬────────┘
API Server CP
--token abcdef....
└────┬───────┘
временный bootstrap token
--discovery-token-ca-cert-hash sha256:...
└────────┬────────┘
проверка CA кластера
другими словами:
«Вот API Server, к которому надо присоединиться. Вот временный токен для bootstrap. Вот fingerprint CA, чтобы убедиться, что это действительно мой Kubernetes-кластер».
Посмотреть существующие токены на control plane