Конспект моего первого опыта с 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

Для запуска кластера 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

Работа с профилями 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


Теперь у нас есть два кластера: 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 - команда показа информации о кластере

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

Примеры других команд:
# Посмотреть текущий активный профиль
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

Примеры дальнейших команд:
# получить поды во всех пространствах имен с широким выводом и наблюдать
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).

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

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

На панели управления можно просматривать все ресурсы (сервисы, поды, деплойменты, события, состояние кластера в реальном времени), которые есть в кластере.
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

Можно посмотреть пошагово историю действий 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

Создание 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

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 работает ИЗНУТРИ кластера
curl http://10.101.113.145 → Возвращает HTML nginx
Единственное что осталось это доступ С ХОСТА (Windows) на Кластер.
Причины могут быть следующие:
Docker создает изолированную сеть, где Windows не может напрямую достучаться.
Отсутствие minikube tunnel.
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

На фото видно, что что metrics-server действительно создался, но не запустился из-за ошибки загрузки образа. ErrImagePull свидетельствует об отсутствие возможности скачать образ.
Причины могут быть разные:
1. Нет доступа в интернет из Minikube.
Проверим интернет в Minikube командой
minikube sshиping -c 3 8.8.8.8

Если 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.0bitnami/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

Теперь, проверим 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

Исходя из вышеописанного, наглядно можно было увидеть, что ErrImagePull почти всегда связаны с сетевым доступом к registry.
metrics-server требует правильных портов и флагов безопасности, особенно в Minikube/Docker Desktop.
В локальных кластерах часто нужно включать --kubelet-insecure-tls.
Установка вручную через официальный YAML показывает результат сопоставимый, с включениями дополнений minikube addons enable.
Эта статья прекрасно иллюстрирует основы управления локальным кластером Kubernetes, показывает возможности Minikube, работать с профилями, диагностировать состояние подов и сервисов, а также подключать инструменты мониторинга.
Сейчас я продолжаю изучение Kubernetes и хочу применить эти навыки в реальных задачах: развертывание приложений, CI/CD, контейнеризация сервисов, кластерная инфраструктура.
Если вы работаете с Kubernetes и готовы дать небольшой учебный проект или стажировку - буду рад пообщаться.
Advixum
Для обучения много лучше взять k3d. Он воссоздаёт среду намного ближе к боевой.
Aimnew Автор
Спасибо! Изучу это добро, но как это всё усвоить когда k8s кажется чем то безграничным в плане обучения?