Конспект моего первого опыта с Kubernetes: как поднять Minikube, задеплоить nginx и победить ошибки, которые чаще всего встречаются у новичков. (c) Новичок с опытом.

Я изучаю Kubernetes как часть практики по контейнеризации и автоматизации развертывания. Чтобы системно выстроить понимание, я веду рабочий конспект в формате статьи: фиксирую используемые команды, практические наблюдения и способы решения возникающих проблем. Моя цель -  уверенно понимать, как устроен кластер изнутри, и уметь работать с ним в реальных условиях. Эта статья будет полезна тем, кто также начинает путь в Kubernetes и сталкивается с тем, что документация даёт базу, но не всегда описывает полную последовательность действий и типичные ошибки, возникающие в процессе.

Для практики я использую локальный кластер на Minikube - он позволяет экспериментировать с компонентами Kubernetes без аренды серверов или облачных инфраструктуры. Действие происходит на ОС Windows 11, версия 25H2.

Ваш ПК
 └── Minikube → Внутри него работает Kubernetes-кластер
       ├── Нода (виртуальная)
       ├── kube-apiserver
       ├── scheduler
       ├── etcd
       └── Поды, сервисы, деплойменты

Во время изучения я столкнулся с тем, что даже базовые шаги - такие как запуск локального кластера, работа с профилями и настройка сервисов - иногда вызывают вопросы. Документация дает основу, но не всегда описывает цепочку действий целиком, включая типичные ошибки и способы их устранения.

В статье я разберу:

  • запуск Minikube с разными параметрами конфигурации;

  • работу с профилями;

  • управление подами, сервисами и развертываниями;

  • подключение Kubernetes Dashboard;

  • настройку и устранение проблем metrics-server, включая ошибку ErrImagePull.

Запуск локального кластера Minikube

Обычно команды Kubernetes выполняются через kubectl:

kubectl get pods -A

Но при работе с Minikube, команды имею встроенную обёртку - minikube, а затем добавляется kubectl -- <опции>. Это позволяет управлять именно тем кластером, который запущен в Minikube.

minikube kubectl -- get pods -A

Запуск минимального кластера осуществляется следующей командой:

minikube start --driver=docker

Проверить состояние можно следующими командами:

minikube kubectl -- get nodes
minikube kubectl -- get pods -A
Рисунок 1. Системные поды находятся в kube-system - это нормальная структура любого кластера Kubernetes
Рисунок 1. Системные поды находятся в kube-system - это нормальная структура любого кластера Kubernetes

Для запуска кластера Minikube с другими конфигурациями, например с параметром memory, который ограничивает использование памяти кластером необходимо ввести следующие аргументы:

minikube start --driver=docker --memory=256mb # где
# --driver=docker - использование Docker контейнера вместо виртуальной машины
# --memory=256mb - ограничение оперативной памяти до 256 МБ

или при ограничении CPU:

minikube start --driver=docker --extra-config=kubeadm.ignore-preflight-errors=NumCPU --force --cpus=1
# --extra-config=kubeadm.ignore-preflight-errors=NumCPU - игнорируем ошибки проверки количества CPU
# --force - принудительный запуск, игнорируя предупреждения и потенциальные проблемы
# --cpus=1 - выделяет только 1 ядро процессора

или оптимальная конфигурация для стабильной работы:

minikube start --driver=docker --memory=1977mb --cpus=2 --disk-size=20g--kubernetes-version=v1.25.0
# --memory=1977mb - выделяем ~2 ГБ оперативной памяти
# --cpus=2 - выделяем 2 ядра процессора 
# --disk-size=20g – создаем диск размером 20 ГБ для хранения образов и данных
# --kubernetes-version=v1.32.2 – устанавливаем конкретную версию Kubernetes

Для получения подробной справки по аргументам, в зависимости от команды добавляется аргумент –help (minikube --help), например:

minikube start –help
minikube delete –help

Команда для удаления кластера:

minikube delete

Команда для проверки статуса:

minikube status --help
Рисунок 2. Процесс удаления  кластера
Рисунок 2. Процесс удаления кластера

Работа с профилями Minikube

Minikube позволяет создавать несколько независимых кластеров и переключаться между ними.

По умолчанию, когда запускаем командой $minikube start, Minikube создает один локальный кластер с настройками по умолчанию.

Но бывает, что тебе нужно несколько разных кластеров на одном компьютере, например:

  • один для экспериментов;

  • второй для тестирования другого приложения;

  • третий - чтобы повторить и воспроизвести чужую ошибку.

    И вот здесь появляются профили, - profiles. Каждый профиль - это полностью отдельный Kubernetes-кластер:

  • свой набор подов;

  • свои сервисы;

  • свои ingress’ы;

  • свои настройки ресурсов;

  • свой IP и сеть.

Профили не конфликтуют между собой.

Запустим кластер с именем профиля Num1 и Num2 командами:

minikube start --driver=docker -p Num1
minikube start --driver=docker -p Num2

Что бы получить список профилей необходимо ввести:

minikube profile list
Рисунок 3. Отображение списка профилей
Рисунок 3. Отображение списка профилей
Рисунок 4. Проверка контейнеров в Docker Desktop
Рисунок 4. Проверка контейнеров в Docker Desktop

Теперь у нас есть два кластера: Num1 и Num2.

Чтобы переключить контекст на первый кластер, необходимо выполнить следующую команду:

minikube profile Num1

После успешного выполнения команды, должна появиться следующая строка, - minikube profile was successfully set to Num1.

Теперь все команды Kubectl будут выполняться на первом кластере с именем Num1.

Выведем все поды во всех namespace Kubernetes кластера командой:

minikube kubectl -- get pods -A
# -- - разделитель, указывающий что дальше идут аргументы для kubectl
# get pods - команда получения информации о pod
# -A или --all-namespaces - показать ресурсы во ВСЕХ namespace
minikube kubectl -- cluster-info
# -- - разделитель аргументов
# cluster-info - команда показа информации о кластере
Рисунок 5. Пример вывода в консоль
Рисунок 5. Пример вывода в консоль

Можно проверить журналы кластера с помощью команды minikube logs или перенаправить журналы в файл: minikube logs > logs.txt

Рисунок 6. Отображение журнала кластера
Рисунок 6. Отображение журнала кластера

Примеры  других команд:

# Посмотреть текущий активный профиль
minikube profile
# Получить поды во всех пространствах имен
minikube kubectl -- get pods -A
# Получить узлы
minikube kubectl -- get nodes
# получить поды в пространстве имен по умолчанию
minikube kubectl -- get pods
# получить поды в пространстве имен по умолчанию с широким выводом
minikube kubectl -- get pods -o wide
# получить поды во всех пространствах имен с широким выводом
minikube kubectl -- get pods -o wide --all-namespaces

С одной стороны, команда одна и таже, но аргументы разные, потому что default - namespace для пользовательских приложений (пока пустой), а kube-system - namespace для системных компонентов Kubernetes.

Например, запустим простой тестовый под командой:

minikube kubectl -- run test-pod --image=nginx

И проверим командой:

minikube kubectl -- get pods -o wide
Рисунок 7. Пример вывода в консоль
Рисунок 7. Пример вывода в консоль

Примеры дальнейших команд:

# получить поды во всех пространствах имен с широким выводом и наблюдать
minikube kubectl -- get pods -o wide --all-namespaces --watch
# получить поды во всех пространствах имен с широким выводом и отсортировать по имени
minikube kubectl -- get pods -o wide --all-namespaces --sort-by=.metadata.name
# получить поды во всех пространствах имен с широким выводом и отсортировать по имени 
с фильтром по статусу
minikube kubectl -- get pods -o wide --all-namespaces --sort-by=.metadata.name --field-selector=status.phase=Running

В результате мы увидим следующие названия столбцов: пространство имен (NAMESPACE), где kube-system - системные компоненты Kubernetes, а default – это пользовательские приложения (test-pod) которое было создано, имя пода (NAME), готовность контейнеров (READY), статус пода (STATUS), количество перезапусков (RESTARTS), время существования (AGE), IP-адрес пода (IP), нода где запущен под (NODE), нода для эвакуации (NOMINATED NODE), проверки готовности (READINESS GATES).

Рисунок 8. Пример вывода в консоль
Рисунок 8. Пример вывода в консоль

Управление кластером через Kubernetes Dashboard в Minikube

У Kubernetes есть встроенная панель управления (dashboard), которую можно использовать в Minikube. Для этого необходимо ввести следующую команду:

minikube dashboard

После запуска Minikube автоматически запустит dashboard-под, создаст прокси и откроет панель в браузере. В выводе вы увидите строку вида:

Рисунок 9. Пример вывода в консоль
Рисунок 9. Пример вывода в консоль

Где Opening http://127.0.0.1:62960 – адрес и порт для доступа к панели.

Рисунок 10. Пример функционала dashboard
Рисунок 10. Пример функционала dashboard

На панели управления можно просматривать все ресурсы (сервисы, поды, деплойменты, события, состояние кластера в реальном времени), которые есть в кластере.

Dashboard очень полезен на этапе обучения - он позволяет визуально увидеть, как Kubernetes создаёт поды и управляет их жизненным циклом.

Создание Deployment

Deployment - это объект Kubernetes, который отвечает за запуск и управление подами (контейнерами) приложения. Когда мы создаём deployment, мы говорим Kubernetes примерно так:

«Запускай мне вот такой контейнер (например, nginx), в количестве N экземпляров, и если что-то упадет - перезапускай автоматически».

Возможность

Что делает Deployment

Автоматический перезапуск

Если под сломался/упал — Kubernetes создаст новый

Масштабирование

Легко увеличить или уменьшить количество подов

Обновления без простоя (Rolling Updates)

Kubernetes заменяет поды по очереди, чтобы приложение не упало

Хранение желаемого состояния

Kubernetes сам следит, чтобы подов было ровно столько, сколько указано

Создадим простой deployment на основе nginx:

minikube kubectl -- create deployment hello-web --image=nginx

Для удаления Deployment, используется следующая команда:

minikube kubectl -- delete deployment hello-web

После развертывания узла hello-web мы можем проверить список развертываний и подов с помощью следующих команд:

minikube kubectl -- get deployments
minikube kubectl -- get pods
Рисунок 11. Пример вывода в консоль
Рисунок 11. Пример вывода в консоль

Можно посмотреть пошагово историю действий Kubernetes, что запускалось, почему перезапускалось, были ли ошибки, с помощью команды:

minikube kubectl -- get events

Если вы создавали что-то и это произошло с ошибкой, то это отобразиться в истории развертываний. Самый простой способ-это удалить все события командой:

minikube kubectl -- delete events –all

или вывести

# Только самые свежие события
minikube kubectl -- get events --field-selector lastSeen=>1m
# Только события для работающих подов
minikube kubectl -- get events --field-selector involvedObject.kind=Pod
Рисунок 12. Пример вывода в консоль
Рисунок 12. Пример вывода в консоль

Создание Service для доступа к приложению

Service - это постоянная точка доступа к подам. Он даёт постоянный IP / DNS-имя и распределяет входящие запросы между несколькими подами. Даже если поды перезапускаются и меняют адреса - Service остаётся неизменным.

Возможность

Что делает Service

Стабильный адрес

Даёт постоянный IP / DNS имя, который не меняется

Балансировка нагрузки

Если подов несколько - распределяет запросы между ними

Доступ извне

Может «открыть» приложение наружу (NodePort, LoadBalancer)

Например:

Без сервиса

  • Под hello-web-abc получил IP 10.244.0.9;

  • Он умер → появился hello-web-xyz с IP 10.244.0.14;

  • Ваши запросы идут в никуда.

С сервисом

  • Вы обращаетесь всегда на один адрес: 10.101.113.145;

  • Service сам найдёт активные поды и отправит запрос в них.

Чтобы получить доступ к подам из интернета, нам нужно раскрыть (expose) развертывание с помощью следующей команды:

minikube kubectl -- expose deployment hello-web --type=LoadBalancer --port=80

Что бы удалить старый сервис необходимо ввести следующую команду:

minikube kubectl -- delete service hello-web

А эта команда создаст сервис, использующий порт 80:

minikube service hello-web

Отобразим список сервисов в кластере командой:

minikube kubectl -get services
Рисунок 13. Пример вывода в консоль
Рисунок 13. Пример вывода в консоль

CLUSTER-IP: 10.101.113.145 - внутренний IP сервиса в кластере.

PORT(S): 80:31904/TCP - порт 80 внутри → порт 31904 снаружи.

Minikube предлагает два способа доступа:

1.     Внешний доступ (Через node IP + NodePort): http://192.168.49.2:31904

  • 192.168.49.2- IP вашей ноды Minikube;

  • 31904- случайный внешний порт.

2.     Локальный проброс портов: http://127.0.0.1:1043

  • 127.0.0.1 - ваш локальный компьютер;

  • 1043- порт проброшенный на вашу машину.

Под слушает на порту 80 внутри кластера → Сервис принимает запросы на порту 31904 снаружи → Minikube автоматически пробрасывает порт 1043 на вашу локальную машину.

Если в вашем случае внешний доступ не будет работать, то необходимо проверить селекторы сервиса и deployment командами:

# Описать объект сервиса
minikube kubectl -- describe service hello-web
# Описать объект deployment
minikube kubectl -- describe deployment hello-web
# Проверим метки подов
minikube kubectl -- get pods --show-labels
# Проверим endpoints сервиса
minikube kubectl -- get endpoints hello-web
# Проверим доступность по ClusterIP
# Войдем в Minikube и проверим доступность по ClusterIP
minikube ssh
curl http://10.101.113.145  # Здесь должен быть ваш CLUSTER-IP из get services

Исходя из выводов в консоли, следует что:

1.     Селекторы и метки совпадают

  • Service Selector:    app=hello-web;

  • Pod Labels:          app=hello-web,pod-template-hash=555fb6d695.

2.     Endpoints существуют и правильные

  • Endpoints: 10.244.0.9:80.

3.     ClusterIP работает ИЗНУТРИ кластера

Единственное что осталось это доступ С ХОСТА (Windows) на Кластер.

Причины могут быть следующие:

  1. Docker создает изолированную сеть, где Windows не может напрямую достучаться.

  2. Отсутствие minikube tunnel.

  3. Firewall/антивирус.

Решение:

1.     Самый надежный и простой это Port-forward

  • В одной вкладке терминала запустим: minikube kubectl -- port-forward deployment/hello-web 8080:80

  • Открываем в браузере http://localhost:8080

2.     Minikube tunnel (правильный способ для LoadBalancer) не будет работать на Windows

  • Удалим текущий сервис командой minikube kubectl -- delete service hello-web

  • Созддим сервис типа NodePort minikube kubectl -- expose deployment hello-web --type=NodePort --port=80

  • Получим доступ командой minikube service hello-web

Удаление ресурсов K8s

Чтобы очистить узел, удалите сервис, а затем развертывание:

minikube kubectl -- delete service hello-web
minikube kubectl -- delete deployment hello-web

Дополнения Minikube

Minikube имеет готовые наборы сервисов и компонентов (addons), которые позволяют устанавливать дополнительное программное обеспечение на ваш кластер.

Вы можете просмотреть список этих дополнений с по мощью следующей команды:

minikube addons list

Чтобы включить/отключить дополнение, вводим такие команды:

minikube addons enable <ADDON_NAME>
minikube addons disable <ADDON_NAME>

Для запуска metrics-server необходимо ввести такую комнду:

minikube addons enable metrics-server

Проверим под командой:

kubectl get pods -n kube-system
Рисунок 14. Пример вывода в консоль
Рисунок 14. Пример вывода в консоль

На фото видно, что что metrics-server действительно создался, но не запустился из-за ошибки загрузки образа. ErrImagePull свидетельствует об отсутствие возможности скачать образ.

Причины могут быть разные:

1.     Нет доступа в интернет из Minikube.

  • Проверим интернет в Minikube командой minikube ssh и ping -c 3 8.8.8.8

Рисунок 15. Пример вывода в консоль
Рисунок 15. Пример вывода в консоль

Если ping не идёт - Minikube не имеет доступа к сети. Вариант решения перезапустить командами:

minikube delete -p Num1
minikube start -p Num1 --driver=docker

2.     Проверить точный образ, который пытается тянуться

  • kubectl describe pod metrics-server-85b7d694d7-s9dsr -n kube-system | grep Image

Пример вывода: Image:         registry.k8s.io/metrics-server/metrics-server:v0.8.0@sha256:89258156d0e9af60403eafd44da9676fd66f600c7934d468ccc17e42b199aee2

  • Проверим в Docker, доступен ли он: docker pull registry.k8s.io/metrics-server/metrics-server:v0.8.0

  • bitnami/metrics-server:0.7.2-debian-12-r27

Решение:

№1 Переустановить metrics-server с указанием другого образа

minikube addons disable metrics-server

Повторим команду с другим образом:

minikube addons enable metrics-server --images= registry-1.docker.io/bitnamicharts/metrics-server --version 7.4.12

Подождем минуту и проверим командой:

kubectl get pods -n kube-system

Ошибка не изменилась, metrics-server по-прежнему не может скачать образ из registry.k8s.io, хотя интернет в Minikube есть. Это классическая ситуация - DNS или провайдер блокирует Google Container Registry / registry.k8s.io.

№2 Скачать образ в отдельном терминале

docker pull registry.k8s.io/metrics-server/metrics-server:v0.8.0

Если снова получаем ошибку - manifest unknown или connect: connection refused, значит доступ к registry.k8s.io заблокирован.

№3 Установить metrics-server напрямую из GitHub (через YAML)

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Если Kubernetes не может скачать образ, то отредактируем YAML командой:

kubectl edit deployment metrics-server -n kube-system

 заменим строку:

registry.k8s.io/metrics-server/metrics-server:v0.8.0

на строку:

image: k8s.gcr.io/metrics-server/metrics-server:v0.6.4 #  или любой другой реально доступный образ (можно взять с Docker Hub mirror).

В выводе должна быть информация – created.

Проверим статус Pod'а командой:

kubectl get pods -n kube-system | grep metrics

при вызове команды:

kubectl describe pod -n kube-system | grep Image

должно быть примерно такой вывод:

Image: registry.k8s.io/coredns/coredns:v1.12.1
Image: registry.k8s.io/etcd:3.6.4-0
Image: registry.k8s.io/kube-apiserver:v1.34.0
Image: registry.k8s.io/kube-controller-manager:v1.34.0
Image: registry.k8s.io/kube-proxy:v1.34.0
Image: registry.k8s.io/kube-scheduler:v1.34.0
Image: registry.k8s.io/metrics-server/metrics-server:v0.8.0
Image: gcr.io/k8s-minikube/storage-provisioner:v5

Это свидетельствует о том, что Kubernetes не может скачать образ registry.k8s.io/metrics-server/metrics-server:v0.8.0. То есть всё работает, кроме загрузки контейнера.

Найдем деплоймент metrics-server командой:

kubectl -n kube-system get deployment metrics-server

и выполним команду:

kubectl -n kube-system edit deployment metrics-server

В открывшемся редакторе (vim/nano) найдем строку:

image: registry.k8s.io/metrics-server/metrics-server:v0.8.0

и заменим на одно из следующих:

image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.8.0
# или 
image: k8s.gcr.io/metrics-server/metrics-server:v0.6.4

Сохраним и выйдем из редактора.

Удалим старый под, чтобы пересоздался:

kubectl delete pod -n kube-system -l k8s-app=metrics-server

Проверим статус:

kubectl get pods -n kube-system | grep metrics

Появится статус Running, но отображается как 0/1 даже спустя время

Проверим, что metrics.k8s.io API активен командой:

kubectl get apiservice | grep metrics

В выводе отобразится как False (MissingEndpoints), означает, что Kubernetes зарегистрировал API metrics.k8s.io, но не может подключиться к сервису metrics-server внутри кластера. Это не критично - просто сервис не отвечает (обычно из-за настроек TLS или порта).

 Это происходит, потому что metrics-server по умолчанию слушает на порту 4443, а API-сервис ожидает доступ к /apis/metrics.k8s.io через порт 443. Если не указать правильный порт в Service или Deployment, появляются “MissingEndpoints”.

Для исправления, необходимо проверить Service metrics-server командой:

kubectl get svc -n kube-system metrics-server -o yaml

Смотрим на блок ports: - должно быть targetPort: 4443, port: 443.

Если targetPort другой - вызовем команду и исправим на targetPort: 4443, port: 443.

kubectl -n kube-system edit svc metrics-server
Рисунок 16. Пример вывода
Рисунок 16. Пример вывода

Теперь, проверим Deployment на нужные аргументы командой:

kubectl -n kube-system get deployment metrics-server -o yaml | grep args -A 5

в результате должно быть следующее:

args:
  - --cert-dir=/tmp
  - --secure-port=4443
  - --kubelet-insecure-tls
  - --kubelet-preferred-address-types=InternalIP,Hostname,ExternalIP

Если этого там нету, то необходимо добавить командой:

kubectl -n kube-system edit deployment metrics-server

Так как это файл yaml, то только пробелы, без TAB.

Перезапустим поды командой:

kubectl delete pod -n kube-system -l k8s-app=metrics-server

Проверим работоспособность командой:

kubectl get apiservice | grep metrics

в случае повторной ошибки (MissingEndpoints), создаем в этой же директории файл metrics-server-fix.yaml с содержимым:

apiVersion: v1
kind: Service
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    k8s-app: metrics-server
spec:
  selector:
    k8s-app: metrics-server
  ports:
    - port: 443
      protocol: TCP
      targetPort: 4443
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    k8s-app: metrics-server
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
        - name: metrics-server
          image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.8.0
          imagePullPolicy: IfNotPresent
          args:
            - --cert-dir=/tmp
            - --secure-port=4443
            - --kubelet-insecure-tls
            - --kubelet-preferred-address-types=InternalIP,Hostname,ExternalIP
          ports:
            - name: main-port
              containerPort: 4443
              protocol: TCP

Применим его командой:

kubectl apply -f metrics-server-fix.yaml

Удалим существующий под командой:

kubectl delete pod -n kube-system -l k8s-app=metrics-server

Ждём и проверяем командой:

kubectl get apiservice | grep metrics

В результате вызова мы видим True, теперь metrics-server полностью запущен и корректно зарегистрирован в Kubernetes API.

Kubernetes наконец «видит» endpoint и получает от него данные.

Если в выводе True, то можно посмотреть метрики с помощью следующих команд:

kubectl top nodes
kubectl top pods -A
# или
minikube kubectl -- top nodes
minikube kubectl -- top pods
Рисунок 17. Пример вывода в консоль
Рисунок 17. Пример вывода в консоль

Исходя из вышеописанного, наглядно можно было увидеть, что ErrImagePull почти всегда связаны с сетевым доступом к registry.

metrics-server требует правильных портов и флагов безопасности, особенно в Minikube/Docker Desktop.

В локальных кластерах часто нужно включать --kubelet-insecure-tls.

Установка вручную через официальный YAML показывает результат сопоставимый, с включениями дополнений minikube addons enable.

Эта статья прекрасно иллюстрирует основы управления локальным кластером Kubernetes, показывает возможности Minikube, работать с профилями, диагностировать состояние подов и сервисов, а также подключать инструменты мониторинга.

Сейчас я продолжаю изучение Kubernetes и хочу применить эти навыки в реальных задачах: развертывание приложений, CI/CD, контейнеризация сервисов, кластерная инфраструктура.
Если вы работаете с Kubernetes и готовы дать небольшой учебный проект или стажировку - буду рад пообщаться.

Комментарии (2)


  1. Advixum
    03.12.2025 05:29

    Для обучения много лучше взять k3d. Он воссоздаёт среду намного ближе к боевой.


    1. Aimnew Автор
      03.12.2025 05:29

      Спасибо! Изучу это добро, но как это всё усвоить когда k8s кажется чем то безграничным в плане обучения?