Конспект моего первого опыта с 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 nodesminikube 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 - показать ресурсы во ВСЕХ namespaceminikube 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=docker2. Проверить точный образ, который пытается тянуться
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 и готовы дать небольшой учебный проект или стажировку — буду рад пообщаться.
