Перейти к содержанию

Установка 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

[preflight] Running pre-flight checks

суть:

kubeadm
Можно ли вообще превратить этот Linux
в Kubernetes Node?
да

3. Скачиваются container images

[preflight] Running pre-flight checks

зачем

Control plane компоненты будут работать в контейнерах

поэтому kubeadm обеспечивает наличие образов для:

kube-apiserver
kube-controller-manager
kube-scheduler
etcd
CoreDNS
kube-proxy
...

посмотреть список необходимых образов можно:

kubeadm config images list

заранее скачать:

sudo kubeadm config images pull

схема:

Мы установили containerd
теперь Kubernetes нужны
container images

4. [certs] - создается PKI Kubernetes

[certs] Using certificateDir folder "/etc/kubernetes/pki"

зачем

kubeadm создает PKI кластера

Kubernetes-компоненты не должны просто доверять любому, кто пришёл по сети.

Они аутентифицируют друг друга с помощью сертификатов.


5. Создается главный CA (Certificate Authority)

Generating "ca" certificate and key

появляются примерно:

/etc/kubernetes/pki/ca.crt
/etc/kubernetes/pki/ca.key

схематично:

               Kubernetes CA
          ┌──────────┼──────────┐
          ↓          ↓          ↓
      apiserver   clients    kubelet...
CA является корнем доверия для значительной части Kubernetes PKI.

ca.key — очень чувствительный приватный ключ.


6. сертификат API Server

Generating "apiserver" certificate and key

появляются:

apiserver.crt
apiserver.key

API Server работает через HTTPS:

kubectl
   │ HTTPS
192.168.64.11:6443
kube-apiserver

далее идет:

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 валиден, в частности, для:

192.168.64.11

вашего физического Node IP

и:

10.96.0.1

виртуального service IP Kubernetes API внутри кластера.


7. API Server получет клиентский сертификат для kubelet

Generating "apiserver-kubelet-client"

API Server иногда сам должен обращаться к kubelet


8. Front proxy

Generating "front-proxy-ca"
Generating "front-proxy-client"

это PKI для aggregation layer Kubernetes API.


9. Отдельный CA для etcd

Generating "etcd/ca"

это отдельная PKI-ветка.

файлы находятся:

/etc/kubernetes/pki/etcd/

10. серверный сертификат etcd

Generating "etcd/server"

etcd работает тоже по TLS.

причем kubeadm пишет SAN:

DNS:
k8s-cp1
localhost

IP:
192.168.64.11
127.0.0.1
::1

то есть etcd получает свою серверную identity.


11. etcd/peer

Generating "etcd/peer"

это сертификат для общения:

etcd ←→ etcd

В HA control plane может быть:

etcd1 ←→ etcd2 ←→ etcd3

12. healthcheck etcd

Generating "etcd/healthcheck-client"

нужен клиент, который может безопасно проверить:

etcd жив или нет?

13. API Server -> etcd

Generating "apiserver-etcd-client"

API Server должен работать с etcd:

kubectl
API Server
   ↓ TLS
etcd

но kubectl напрямую в etcd не ходит

и schedulet напрямую за данными туда ходить не должен.

Правильная архитектурная модель:

    API Server
      etcd

API Server - центральный интерфейс к состоянию Kubernetes.


14. Service Account keys

Generating "sa" key and public key

это связано с ServiceAccount и токенами.


15. [kubeconfig]

[kubeconfig] Using kubeconfig folder "/etc/kubernetes"

теперь kubeadm создает конфигурации клиентов API Server

admin.conf
super-admin.conf
kubelet.conf
controller-manager.conf
scheduler.conf

можно посмотреть:

sudo ls -lah /etc/kubernetes/*.conf

16. admin.conf

Writing "admin.conf"

это администраторский kubeconfig

в нем содержится:

КУДА подключаться
       +
КАКОМУ CA доверять
       +
КЕМ представляться

упрощенно:

/etc/kubernetes/admin.conf

server:
https://192.168.64.11:6443

CA:
...

client identity:
...

поэтому после установки мы делали:

cp /etc/kubernetes/admin.conf ~/.kube/config
после чего заработал:

kubectl get nodes

17. Остальные kubeconfig

создаются:

kubelet.conf
controller-manager.conf
scheduler.conf

так как эти компоненты тоже являются клиентами Kubernetes API.

scheduler
    │ scheduler.conf
API Server

у scheduler есть конфигурация подключения и credentials.


18. Создается etcd static pod

[etcd] Creating static Pod manifest for local etcd
in "/etc/kubernetes/manifests"

kubeadm создает:

/etc/kubernetes/manifests/etcd.yaml

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

[kubelet-start] Writing ...

создаются файлы под:

/var/lib/kubelet/

например:

kubeadm-flags.env
config.yaml
instance-config.yaml

21. Запускается kubelet

[kubelet-start] Starting the kubelet

kubelet смотрит

/etc/kubernetes/manifests/

видит 4 манифеста и через CRI обеспечивает запуск соответствующих контейнеров

итого:

                    systemd
              ┌────────┴────────┐
              ▼                 ▼
           kubelet          containerd
              │                 ▲
              │     CRI         │
              └─────────────────┘
        читает static manifests
        /etc/kubernetes/manifests
          ┌────────────┼─────────────┐
          ▼            ▼             ▼
        etcd       apiserver     scheduler
                                  controller

22. kubeadm ждет запуска

[wait-control-plane]
Waiting for the kubelet to boot up the control plane
as static Pods

23. Проверяется kubelet

Checking:
http://127.0.0.1:10248/healthz

ответ:

kubelet is healthy

иными словами:

kubeadm → 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

и все отвечают

healthly

25. Конфигурация загружается уже в Kubernetes API

[upload-config]
Storing configuration in ConfigMap "kubeadm-config"

до этого 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

[mark-control-plane]

добавляютя labels:

node-role.kubernetes.io/control-plane

поэтому:

kubectl get nodes

показывает :

ROLES
control-plane

27. Добавляется taint

важная строка:

node-role.kubernetes.io/control-plane:NoSchedule

это можно перевести как

Обычные пользовательские Pods сюда не планировать.

схематично:

CP
├── API Server
├── etcd
├── scheduler
└── controllers

        X nginx

28. Создается bootstrap token

[bootstrap-token]
Using token:
p4ijkw....

Новый 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

[addons] Applied essential addon: CoreDNS

Теперь kubeadm создаёт CoreDNS.

CoreDNS нужен для DNS внутри Kubernetes.

Например Pod сможет обращаться:

postgres.default.svc.cluster.local

вместо запоминания IP.

но у нас:

CoreDNS Pending

Потому что Pod networking ещё нет.


31. kube-proxy

[addons] Applied essential addon: kube-proxy

Он участвует в реализации сетевой модели Kubernetes Services на Node.


32. Готов Control Plane

Your Kubernetes control-plane has initialized successfully!

Но это не означает что кластер готов

Control Plane
─────────────

etcd                 ✓
API Server           ✓
scheduler            ✓
controller-manager   ✓
kubelet              ✓
containerd           ✓

CoreDNS              Pending
CNI                   ✗

Node                 NotReady

33. join

kubeadm join 192.168.64.11:6443 \
  --token ... \
  --discovery-token-ca-cert-hash sha256:...

означает:

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-сеть

--pod-network-cidr=10.244.0.0/16

Теперь нужен CNI-плагин, который эту сеть реализует. Для нашего стенда можем продолжить с Calico.

документация 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
CoreDNS Pod
нужна Pod-сеть
её нет
Pending

Node
NotReady
После установки CNI:
                 CNI
        ┌─────────┴─────────┐
        │                   │
   Pod networking      Node networking
CoreDNS получает сеть
CoreDNS Running
Node Ready

Добавление worker node в существующий кластер k8s

На control plane вводим:

sudo kubeadm token create --print-join-command

На добавляемой worker node выполняем:

sudo kubeadm join 192.168.64.10:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:1234567890abcdef...

Затем на control plane проверяем состояние нод кластера:

kubectl get nodes
NAME       STATUS   ROLES
k8s-cp1    Ready    control-plane
k8s-w1     Ready    <none>
k8s-w2     Ready    <none>

Что происходит в 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

sudo kubeadm token list