Первые приложения
Кластер поднят, сеть и хранилище понятны — пора запустить что-то полезное. На этой странице три практических упражнения: классический nginx «по учебнику», Kubernetes Dashboard для наглядности и собственное приложение — от Dockerfile до деплоя.
Разворачиваем nginx: Deployment + Service
Классическая связка любого приложения в k8s: Deployment описывает, что и сколько копий запускать (и следит, чтобы они жили), Service даёт им стабильный адрес. Оба описываются одним YAML-файлом.
Шаг 1. Создайте манифест
Файл nginx-demo.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
labels:
app: nginx-demo
spec:
replicas: 2 # две копии: одна упадёт — вторая продолжит
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
spec:
type: NodePort
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
nodePort: 30081
Обратите внимание на блок resources: requests — сколько гарантированно выделить поду, limits — потолок. Привычка всегда указывать их — лучшая прививка против «съедания» всей памяти узла одним приложением (подробности в «Советах»).
Шаг 2. Примените и проверьте
kubectl apply -f nginx-demo.yaml
# что создалось
kubectl get deployments
kubectl get pods -o wide
kubectl get svc nginx-demo
Когда оба пода перейдут в Running, откройте в браузере http://192.168.1.50:30081 (IP любого узла) — должна появиться страница «Welcome to nginx!».
Шаг 3. Поиграйте с отказоустойчивостью
# удалите один из подов
kubectl delete pod -l app=nginx-demo --field-selector=status.phase=Running
# и сразу смотрите: deployment уже создаёт замену
kubectl get pods -w
Это и есть суть Kubernetes: вы описываете желаемое состояние («хочу 2 копии»), а контроллер постоянно приводит реальность к описанию.
Шаг 4. Обновите версию без простоя
# сменить образ на новую версию
kubectl set image deployment/nginx-demo nginx=nginx:1.28
# смотреть rolling update вживую
kubectl rollout status deployment/nginx-demo
# история обновлений
kubectl rollout history deployment/nginx-demo
# откат, если что-то пошло не так
kubectl rollout undo deployment/nginx-demo
Rolling update поднимает новые поды и гасит старые по одному — с точки зрения посетителя сайт даже не моргнул.
Конфиги и секреты: ConfigMap и Secret
Настройки приложения не принято зашивать в образ. Для них есть два объекта:
- ConfigMap — обычные настройки: адреса, флаги, файлы конфигурации;
- Secret — чувствительные данные: пароли, токены, ключи (хранятся в base64, что не шифрование, а кодирование — поэтому доступ к Secret'ам ограничивается RBAC).
Пример: передаём приложению строку приветствия через ConfigMap и пароль через Secret:
# создать из командной строки
kubectl create configmap app-config --from-literal=greeting="Привет из кластера!"
kubectl create secret generic app-secret --from-literal=db-password="SuperSecret"
# посмотреть
kubectl get configmap app-config -o yaml
kubectl get secret app-secret
Подключение в под — как переменные окружения:
env:
- name: GREETING
valueFrom:
configMapKeyRef:
name: app-config
key: greeting
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: db-password
Kubernetes Dashboard
Dashboard — официальный веб-интерфейс кластера: узлы, поды, логи, события, всё наглядно. Для новичка это отличный способ «увидеть» кластер, пока kubectl ещё не стал привычкой.
Шаг 5. Установите Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# проверить
kubectl get pods -n kubernetes-dashboard
Шаг 6. Создайте пользователя с доступом
Dashboard не имеет своей системы пользователей — вход по токену ServiceAccount. Создадим аккаунт администратора (файл dashboard-admin.yaml):
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
kubectl apply -f dashboard-admin.yaml
cluster-admin — полные права на кластер. Для домашнего использования это нормально (вы и есть администратор), но понимайте: любой, кто получит токен, получает ключи от всего. Не выставляйте Dashboard в интернет без необходимости.
Шаг 7. Получите токен
# токен с длительным сроком (например, год = 8760h)
kubectl -n kubernetes-dashboard create token admin-user --duration=8760h
Команда выведет длинную строку — это и есть пароль для входа. Сохраните её в менеджере паролей.
Шаг 8. Откройте Dashboard
Самый простой способ — проброс портов через kubectl с вашего рабочего компьютера:
kubectl port-forward -n kubernetes-dashboard svc/kubernetes-dashboard 8443:443
Откройте https://localhost:8443 (браузер ругнётся на самоподписанный сертификат — это нормально, принимаем), вставьте токен — и вы внутри.
Своё приложение: Dockerfile → образ → манифест
Теперь самое интересное: задеплоим не чужой nginx, а собственное приложение. Для примера — микроскопический веб-сервер на Python, который отвечает «Привет из кластера!».
Шаг 9. Напишите приложение и Dockerfile
Два файла в одном каталоге. Сначала app.py:
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write("Привет из кластера!".encode("utf-8"))
HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
И Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
EXPOSE 8080
CMD ["python", "app.py"]
Шаг 10. Соберите образ
Если Docker установлен на вашем компьютере:
# сборка образа с тегом
docker build -t myapp:1.0 .
# проверка локально
docker run --rm -p 8080:8080 myapp:1.0
# откройте http://localhost:8080
buildkit или docker.io из репозитория, но проще всего поставить Docker Desktop (Windows/macOS) или docker.io (sudo apt install docker.io в Linux) на рабочую машину. Сам кластер для сборки Docker не требует.
Шаг 11. Доставьте образ в кластер
Кластер должен откуда-то взять ваш образ. Три варианта:
Вариант А — через containerd на узле (просто для одного узла): сохраните образ в файл и импортируйте на сервере:
# на машине со сборкой
docker save myapp:1.0 | gzip > myapp.tar.gz
scp myapp.tar.gz ivan@192.168.1.50:~
# на сервере (k3s/containerd): импорт в рантайм k3s
sudo k3s ctr images import myapp.tar.gz
Вариант Б — через публичный реестр: залейте образ в Docker Hub (или GitHub Container Registry):
docker tag myapp:1.0 ваш_логин/myapp:1.0
docker push ваш_логин/myapp:1.0
Вариант В — свой реестр: позже можно поднять registry прямо в кластере — но это уже продвинутая тема.
Шаг 12. Напишите манифест и задеплойте
Файл myapp.yaml (для варианта А используем локальный образ с imagePullPolicy: IfNotPresent — иначе кластер попытается скачать его из интернета):
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
imagePullPolicy: IfNotPresent # образ уже на узле, не ходить в интернет
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: NodePort
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
nodePort: 30082
kubectl apply -f myapp.yaml
kubectl get pods -l app=myapp
curl http://192.168.1.50:30082
# Привет из кластера!
Шаг 13. Посмотрите логи и зайдите внутрь
Две команды, которыми вы будете пользоваться каждый день:
# логи приложения
kubectl logs -l app=myapp
# shell внутри контейнера (для отладки)
kubectl exec -it deploy/myapp -- /bin/sh
Уборка: как удалить приложение
Удаление — такая же важная операция, как установка. Два способа:
# удалить всё, что создано манифестом
kubectl delete -f myapp.yaml
# или по именам объектов
kubectl delete deployment myapp
kubectl delete service myapp
# PVC удаляется отдельно (данные не пропадут сами)
kubectl delete pvc app-data
Если приложение ставилось в отдельный namespace — всё сносится одной командой: kubectl delete namespace myapp-ns. Удобно для экспериментов.