2.4 Kubernetes для начинающих

2.4 Kubernetes для начинающих #

📚 Градация сложности изучения Kubernetes:

  • 🟢 Новичок (0-6 месяцев): Pod, Service, основные команды kubectl
  • 🟡 Базовый (6-12 месяцев): Deployment, ConfigMap, Secrets, Ingress
  • 🟠 Средний (1-2 года): Helm, operators, networking, storage
  • 🔴 Продвинутый (2+ года): Custom resources, cluster administration, security

🟢 Что такое Kubernetes (простыми словами) #

Kubernetes — это как “умный управляющий” для контейнеров. Представьте, что у вас есть 100 контейнеров с приложениями, и вам нужно:

  • Следить, чтобы они все работали
  • Перезапускать упавшие
  • Распределять нагрузку между ними
  • Обновлять их без остановки сервиса

Kubernetes делает всё это автоматически!

Аналогия с дирижером оркестра:

  • Docker — это музыканты (контейнеры)
  • Kubernetes — это дирижер, который координирует их работу

🎯 Зачем нужен Kubernetes #

Проблемы без оркестрации #

Проблемы ручного управления контейнерами:

Обычный подход без Kubernetes:

  • Отдельный запуск каждого контейнера app1, app2, app3 в фоновом режиме
  • Отсутствие автоматического управления

Критические вопросы без оркестрации:

  • Отказоустойчивость: Кто перезапустит упавший контейнер?
  • Масштабирование: Как распределить нагрузку между контейнерами?
  • Миграция: Как перенести контейнеры на другой сервер при сбое?
  • Обновления: Как обновить приложение без прерывания сервиса?

Решение с Kubernetes #

Декларативное описание желаемого состояния приложения:

Конфигурация Deployment:

  • API версия: apps/v1 (стандарт для приложений)
  • Тип ресурса: Deployment (управление Pod’ами)
  • Имя: “myapp” (уникальный идентификатор)

Параметры развертывания:

  • Количество реплик: 3 экземпляра (высокая доступность)
  • Селектор: метка “app: myapp” для связи с Pod’ами
  • Образ контейнера: myapp:v1.0
  • Порт: 8080 (внутренний порт контейнера)

Автоматические возможности Kubernetes:

  • ✅ Автоматический запуск 3 экземпляров приложения
  • ✅ Мгновенный перезапуск сломанных контейнеров
  • ✅ Интеллектуальное распределение по серверам кластера
  • ✅ Встроенная балансировка нагрузки между экземплярами

🏗️ Архитектура Kubernetes #

Control Plane (Управляющий слой) #

Компоненты управляющего узла (Master Node):

  • API Server: Центральная точка входа для всех операций с кластером
  • etcd: Распределенная база данных для хранения всего состояния кластера
  • Controller: Множество контроллеров для управления различными ресурсами
  • Scheduler: Планировщик, определяющий на каком узле запустить Pod
  • Cloud Controller: Интеграция с облачными провайдерами (AWS, GCP, Azure)

Worker Nodes (Рабочие узлы) #

Компоненты рабочих узлов кластера:

  • kubelet: Основной агент узла, отвечающий за запуск и мониторинг Pod’ов
  • kube-proxy: Сетевой прокси для маршрутизации трафика к Service’ам
  • Container Runtime: Движок контейнеров (Docker, containerd, CRI-O)
  • Pods: Фактически запущенные приложения в контейнерах

Полная схема #

Схема взаимодействия компонентов Kubernetes кластера:

Архитектура кластера:

Мастер-узел (управляющий):

  • API Server: центральная точка входа
  • etcd: распределенное хранилище данных
  • Controller: контроллеры ресурсов

Рабочие узлы (выполняющие):

  • kubelet: агент для связи с мастером
  • kube-proxy: сетевой маршрутизатор
  • Container Runtime: движок контейнеров
  • Pods: работающие приложения

Принцип работы: API Server на мастер-узле обменивается командами с kubelet’ами на рабочих узлах, координируя работу всего кластера

📦 Основные объекты Kubernetes #

1. Pod - минимальная единица развертывания #

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: nginx:latest
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"

2. Deployment - управляет Pod’ами #

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:v1.0
        ports:
        - containerPort: 8080
        livenessProbe:          # Проверка жизнеспособности
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:         # Проверка готовности
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

3. Service - сетевой доступ к Pod’ам #

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP    # ClusterIP, NodePort, LoadBalancer, ExternalName

4. Ingress - внешний доступ HTTP/HTTPS #

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-service
            port:
              number: 80
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls

🛠️ Установка и настройка #

Локальная разработка - Minikube #

# Установка minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Запуск локального кластера
minikube start --driver=docker --memory=4096 --cpus=2

# Проверка статуса
minikube status

# Доступ к dashboard
minikube dashboard

Установка kubectl (обновлено для 2025) #

# Метод 1: Через официальный репозиторий (рекомендуется)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/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.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubectl

# Метод 2: Прямая загрузка актуальной версии
curl -LO "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Метод 3: Через snap (Ubuntu)
sudo snap install kubectl --classic

# Проверить установку
kubectl version --client

# Включить автодополнение для bash
echo 'source <(kubectl completion bash)' >>~/.bashrc
source ~/.bashrc

Kind (Kubernetes in Docker) #

# Установка kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# Создание кластера
kind create cluster --name my-cluster

# Использование кластера
kubectl cluster-info --context kind-my-cluster

📋 Основные команды kubectl #

Информация о кластере #

# Информация о кластере
kubectl cluster-info
kubectl get nodes
kubectl describe node node-name

# Все ресурсы в кластере
kubectl get all --all-namespaces

# Версии API
kubectl api-versions
kubectl api-resources

Работа с Pod’ами #

# Просмотр pods
kubectl get pods                    # в текущем namespace
kubectl get pods -n kube-system     # в конкретном namespace
kubectl get pods -o wide            # с дополнительной информацией
kubectl get pods --watch            # отслеживать изменения

# Детальная информация
kubectl describe pod pod-name
kubectl logs pod-name               # логи
kubectl logs -f pod-name            # следить за логами
kubectl logs pod-name -c container-name # логи конкретного контейнера

# Выполнение команд
kubectl exec -it pod-name -- bash   # подключиться к pod
kubectl exec pod-name -- ls /app    # выполнить команду

Применение конфигураций #

# Создание ресурсов
kubectl apply -f deployment.yaml    # из файла
kubectl apply -f ./configs/         # из директории
kubectl apply -k ./kustomize/       # с Kustomize

# Удаление ресурсов
kubectl delete -f deployment.yaml
kubectl delete pod pod-name
kubectl delete deployment deployment-name

# Обновление ресурсов
kubectl replace -f deployment.yaml
kubectl patch deployment myapp -p '{"spec":{"replicas":5}}'

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

# Масштабирование
kubectl scale deployment myapp --replicas=5

# Rolling updates
kubectl set image deployment/myapp myapp=myapp:v2.0
kubectl rollout status deployment/myapp
kubectl rollout history deployment/myapp
kubectl rollout undo deployment/myapp

# Restart deployment
kubectl rollout restart deployment/myapp

🎯 Практический пример: Веб-приложение #

Структура проекта #

k8s-webapp/
├── app/
│   ├── Dockerfile
│   ├── app.py
│   └── requirements.txt
└── k8s/
    ├── namespace.yaml
    ├── deployment.yaml
    ├── service.yaml
    └── ingress.yaml

app.py #

from flask import Flask, jsonify
import os
import socket

app = Flask(__name__)

@app.route('/')
def hello():
    return jsonify({
        'message': 'Hello from Kubernetes!',
        'hostname': socket.gethostname(),
        'version': os.getenv('APP_VERSION', 'v1.0')
    })

@app.route('/health')
def health():
    return jsonify({'status': 'healthy'})

@app.route('/ready')
def ready():
    return jsonify({'status': 'ready'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

k8s/namespace.yaml #

apiVersion: v1
kind: Namespace
metadata:
  name: webapp
  labels:
    name: webapp

k8s/deployment.yaml #

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  namespace: webapp
  labels:
    app: webapp
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: webapp:v1.0
        ports:
        - containerPort: 8080
        env:
        - name: APP_VERSION
          value: "v1.0"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3

k8s/service.yaml #

apiVersion: v1
kind: Service
metadata:
  name: webapp-service
  namespace: webapp
spec:
  selector:
    app: webapp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

k8s/ingress.yaml #

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  namespace: webapp
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - webapp.example.com
    secretName: webapp-tls
  rules:
  - host: webapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp-service
            port:
              number: 80

Развертывание #

# 1. Собрать Docker образ
cd app/
docker build -t webapp:v1.0 .

# 2. Для minikube загрузить в локальный registry
minikube image load webapp:v1.0

# 3. Применить конфигурации
kubectl apply -f k8s/

# 4. Проверить развертывание
kubectl get all -n webapp

# 5. Проверить доступность (для minikube)
minikube service webapp-service -n webapp

🔧 ConfigMaps и Secrets #

ConfigMap для конфигурации #

apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
  namespace: webapp
data:
  database_url: "postgresql://localhost:5432/mydb"
  log_level: "info"
  feature_flag: "true"
  config.properties: |
    server.port=8080
    server.host=0.0.0.0
    database.connection.timeout=30s

Secret для чувствительных данных #

apiVersion: v1
kind: Secret
metadata:
  name: webapp-secrets
  namespace: webapp
type: Opaque
data:
  database_password: cGFzc3dvcmQxMjM=  # base64 encoded
  api_key: YWJjZGVmZ2hpams=              # base64 encoded

Использование в Deployment #

spec:
  containers:
  - name: webapp
    image: webapp:v1.0
    env:
    # Из ConfigMap
    - name: DATABASE_URL
      valueFrom:
        configMapKeyRef:
          name: webapp-config
          key: database_url
    # Из Secret
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: webapp-secrets
          key: database_password
    # Монтирование как файлы
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: webapp-config
  - name: secret-volume
    secret:
      secretName: webapp-secrets

📊 Мониторинг и логирование #

Prometheus + Grafana #

# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
    scrape_configs:
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

Fluent Bit для логов #

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      name: fluent-bit
  template:
    metadata:
      labels:
        name: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:latest
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config

🔒 Безопасность Kubernetes #

RBAC (Role-Based Access Control) #

# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: webapp-sa
  namespace: webapp

---
# Role  
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: webapp-role
  namespace: webapp
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]

---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: webapp-binding
  namespace: webapp
subjects:
- kind: ServiceAccount
  name: webapp-sa
  namespace: webapp
roleRef:
  kind: Role
  name: webapp-role
  apiGroup: rbac.authorization.k8s.io

Network Policies #

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: webapp-netpol
  namespace: webapp
spec:
  podSelector:
    matchLabels:
      app: webapp
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: database
    ports:
    - protocol: TCP
      port: 5432

Pod Security Standards #

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      capabilities:
        drop:
        - ALL
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

🎯 Практические задания #

Задание 1: Настройка локального кластера Kubernetes (30 минут) #

# 1. Установка и настройка minikube
echo "🚀 Установка minikube..."
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
rm minikube-linux-amd64

# 2. Установка kubectl
echo "⚙️ Установка kubectl..."
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl

# 3. Запуск локального кластера
echo "🔄 Запуск кластера Kubernetes..."
minikube start --driver=docker --memory=4096 --cpus=2 --disk-size=20g

# 4. Проверка статуса
echo "✅ Проверка статуса кластера:"
minikube status
kubectl cluster-info
kubectl get nodes

# 5. Включение необходимых дополнений
echo "🔧 Включение дополнений..."
minikube addons enable dashboard
minikube addons enable metrics-server
minikube addons enable ingress

# 6. Настройка автодополнения kubectl
echo "📝 Настройка автодополнения..."
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -F __start_kubectl k' >> ~/.bashrc

# 7. Создание первого Pod
echo "🐳 Создание первого Pod..."
kubectl run test-pod --image=nginx:alpine --port=80

# 8. Проверка Pod
echo "🔍 Проверка Pod:"
kubectl get pods
kubectl describe pod test-pod

# 9. Создание Service для доступа к Pod
kubectl expose pod test-pod --type=NodePort --port=80

# 10. Получение URL для доступа
echo "🌐 URL для доступа к приложению:"
minikube service test-pod --url

# 11. Тестирование доступности
TEST_URL=$(minikube service test-pod --url)
curl -s $TEST_URL | head -n 5

# 12. Очистка ресурсов
echo "🧹 Очистка тестовых ресурсов..."
kubectl delete pod test-pod
kubectl delete service test-pod

echo "✅ Локальный кластер Kubernetes готов к использованию!"

Проверка: Кластер должен быть запущен, kubectl должен подключаться к кластеру, и тестовый Pod должен быть доступен.

Задание 2: Создание полноценного веб-приложения (45 минут) #

# 1. Создайте проект для Kubernetes приложения
mkdir ~/k8s-webapp && cd ~/k8s-webapp

# 2. Создайте простое веб-приложение
mkdir app
cat > app/app.py << 'EOF'
from flask import Flask, jsonify, request
import os
import socket
import datetime
import time
import random

app = Flask(__name__)
start_time = time.time()

@app.route('/')
def home():
    return jsonify({
        'message': 'Hello from Kubernetes!',
        'hostname': socket.gethostname(),
        'version': os.getenv('APP_VERSION', 'v1.0'),
        'environment': os.getenv('ENVIRONMENT', 'development'),
        'uptime_seconds': int(time.time() - start_time),
        'timestamp': datetime.datetime.now().isoformat()
    })

@app.route('/health')
def health():
    # Симуляция проблем со здоровьем (5% вероятность)
    if random.random() < 0.05:
        return jsonify({'status': 'unhealthy', 'error': 'Random failure'}), 500
    return jsonify({'status': 'healthy', 'uptime': int(time.time() - start_time)})

@app.route('/ready')
def ready():
    # Симуляция времени готовности (10 секунд после запуска)
    if time.time() - start_time < 10:
        return jsonify({'status': 'not ready', 'message': 'Still starting up'}), 503
    return jsonify({'status': 'ready'})

@app.route('/info')
def info():
    return jsonify({
        'hostname': socket.gethostname(),
        'version': os.getenv('APP_VERSION', 'v1.0'),
        'environment': os.getenv('ENVIRONMENT', 'development'),
        'config': {
            'database_url': os.getenv('DATABASE_URL', 'not configured'),
            'cache_enabled': os.getenv('CACHE_ENABLED', 'false'),
            'log_level': os.getenv('LOG_LEVEL', 'info')
        },
        'uptime_seconds': int(time.time() - start_time),
        'start_time': datetime.datetime.fromtimestamp(start_time).isoformat()
    })

@app.route('/load')
def load():
    # Эндпоинт для создания нагрузки
    duration = int(request.args.get('duration', 1))
    cpu_load = float(request.args.get('cpu', 0.5))
    
    start = time.time()
    while time.time() - start < duration:
        # Создаем CPU нагрузку
        if random.random() < cpu_load:
            sum(i*i for i in range(10000))
    
    return jsonify({
        'message': f'Generated load for {duration} seconds',
        'cpu_load': cpu_load,
        'hostname': socket.gethostname()
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=False)
EOF

# 3. Создайте requirements.txt
cat > app/requirements.txt << 'EOF'
Flask==2.3.3
gunicorn==21.2.0
EOF

# 4. Создайте Dockerfile
cat > app/Dockerfile << 'EOF'
FROM python:3.11-alpine

# Создание пользователя для безопасности
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

WORKDIR /app

# Установка зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Копирование приложения
COPY --chown=appuser:appgroup app.py .

USER appuser

EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "--timeout", "30", "app:app"]
EOF

# 5. Создайте директорию для Kubernetes манифестов
mkdir k8s

# 6. Создайте Namespace
cat > k8s/01-namespace.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
  name: webapp
  labels:
    name: webapp
    environment: development
EOF

# 7. Создайте ConfigMap
cat > k8s/02-configmap.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
  namespace: webapp
data:
  DATABASE_URL: "postgresql://db:5432/webapp"
  CACHE_ENABLED: "true"
  LOG_LEVEL: "info"
  ENVIRONMENT: "kubernetes"
  # Файл конфигурации
  app.properties: |
    server.port=8080
    server.host=0.0.0.0
    database.connection.pool.size=10
    cache.ttl=300
    logging.level=info
EOF

# 8. Создайте Secret
cat > k8s/03-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: webapp-secrets
  namespace: webapp
type: Opaque
data:
  # Пароли и ключи в base64
  database_password: c2VjcmV0UGFzc3dvcmQ=  # secretPassword
  api_key: YWJjZGVmZ2hpams=                 # abcdefghijk
  jwt_secret: bXlTdXBlclNlY3JldEtleQ==      # mySuperSecretKey
EOF

# 9. Создайте Deployment
cat > k8s/04-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  namespace: webapp
  labels:
    app: webapp
    version: v1.0
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
        version: v1.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: webapp
        image: webapp:v1.0
        imagePullPolicy: Never  # Для локальных образов
        ports:
        - containerPort: 8080
          name: http
        env:
        - name: APP_VERSION
          value: "v1.0"
        # Переменные из ConfigMap
        - name: DATABASE_URL
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: DATABASE_URL
        - name: CACHE_ENABLED
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: CACHE_ENABLED
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: LOG_LEVEL
        - name: ENVIRONMENT
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: ENVIRONMENT
        # Секреты
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: webapp-secrets
              key: database_password
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: webapp-secrets
              key: api_key
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "300m"
        # Проверки здоровья
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
          successThreshold: 1
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
          successThreshold: 1
        # Монтирование конфигурационных файлов
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
      volumes:
      - name: config-volume
        configMap:
          name: webapp-config
          items:
          - key: app.properties
            path: app.properties
      - name: secret-volume
        secret:
          secretName: webapp-secrets
          defaultMode: 0400
      # Настройки безопасности
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        fsGroup: 1001
EOF

# 10. Создайте Service
cat > k8s/05-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
  namespace: webapp
  labels:
    app: webapp
spec:
  selector:
    app: webapp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP
  sessionAffinity: None
---
# NodePort Service для внешнего доступа
apiVersion: v1
kind: Service
metadata:
  name: webapp-nodeport
  namespace: webapp
  labels:
    app: webapp
spec:
  selector:
    app: webapp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
    nodePort: 30080
  type: NodePort
EOF

# 11. Создайте HorizontalPodAutoscaler
cat > k8s/06-hpa.yaml << 'EOF'
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: webapp-hpa
  namespace: webapp
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: webapp-deployment
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
EOF

# 12. Создайте Ingress
cat > k8s/07-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  namespace: webapp
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
  - host: webapp.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp-service
            port:
              number: 80
EOF

# 13. Соберите Docker образ
echo "🐳 Сборка Docker образа..."
cd app
docker build -t webapp:v1.0 .
cd ..

# 14. Загрузите образ в minikube
echo "⬆️ Загрузка образа в minikube..."
minikube image load webapp:v1.0

# 15. Примените все манифесты
echo "🚀 Развертывание приложения..."
kubectl apply -f k8s/

# 16. Ожидайте готовности
echo "⏳ Ожидание готовности всех Pod..."
kubectl wait --for=condition=ready pod -l app=webapp -n webapp --timeout=300s

# 17. Проверьте статус развертывания
echo "📊 Статус развертывания:"
kubectl get all -n webapp
echo ""
kubectl get configmap,secret -n webapp
echo ""
kubectl get hpa -n webapp

# 18. Тестирование приложения
echo "🧪 Тестирование приложения:"

# Получение URL через NodePort
NODE_PORT_URL="http://$(minikube ip):30080"
echo "NodePort URL: $NODE_PORT_URL"

# Тестирование различных эндпоинтов
echo "Тестирование главной страницы:"
curl -s $NODE_PORT_URL | python3 -m json.tool

echo -e "\nТестирование health check:"
curl -s $NODE_PORT_URL/health | python3 -m json.tool

echo -e "\nТестирование готовности:"
curl -s $NODE_PORT_URL/ready | python3 -m json.tool

echo -e "\nТестирование информации о приложении:"
curl -s $NODE_PORT_URL/info | python3 -m json.tool

# 19. Демонстрация балансировки нагрузки
echo -e "\n🔄 Демонстрация балансировки нагрузки:"
for i in {1..5}; do
    echo "Запрос $i:"
    curl -s $NODE_PORT_URL | jq -r '.hostname'
done

# 20. Создайте скрипт для мониторинга
cat > monitor-app.sh << 'EOF'
#!/bin/bash

echo "🔍 Мониторинг Kubernetes приложения"
echo "=================================="

while true; do
    echo "$(date): Pods status:"
    kubectl get pods -n webapp -o wide
    
    echo -e "\nHPA status:"
    kubectl get hpa -n webapp
    
    echo -e "\nСобытия (последние 5):"
    kubectl get events -n webapp --sort-by='.lastTimestamp' | tail -5
    
    echo -e "\n" && sleep 30
done
EOF

chmod +x monitor-app.sh

echo "✅ Веб-приложение успешно развернуто!"
echo "🌐 Доступ к приложению: $NODE_PORT_URL"
echo "📊 Для мониторинга запустите: ./monitor-app.sh"
echo "🎯 Для создания нагрузки: curl '$NODE_PORT_URL/load?duration=5&cpu=0.8'"

Проверка: Все Pod должны быть в статусе Running, приложение должно отвечать на всех эндпоинтах, HPA должен быть настроен.

Задание 3: Управление конфигурацией и секретами (25 минут) #

# 1. Создайте проект для демонстрации конфигураций
mkdir ~/k8s-config-demo && cd ~/k8s-config-demo

# 2. Создайте ConfigMap с различными типами данных
cat > configmap-demo.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: multi-config
  namespace: default
data:
  # Простые переменные
  database_host: "postgres.example.com"
  database_port: "5432"
  cache_size: "100"
  debug_mode: "false"
  
  # JSON конфигурация
  app_config.json: |
    {
      "server": {
        "port": 8080,
        "host": "0.0.0.0",
        "timeout": 30
      },
      "database": {
        "max_connections": 100,
        "connection_timeout": 30,
        "retry_attempts": 3
      },
      "features": {
        "authentication": true,
        "caching": true,
        "metrics": true
      }
    }
  
  # YAML конфигурация
  app_config.yaml: |
    server:
      port: 8080
      host: 0.0.0.0
      timeout: 30
    database:
      max_connections: 100
      connection_timeout: 30
      retry_attempts: 3
    features:
      authentication: true
      caching: true
      metrics: true
  
  # Properties файл
  app.properties: |
    server.port=8080
    server.host=0.0.0.0
    database.url=jdbc:postgresql://postgres:5432/mydb
    cache.enabled=true
    cache.ttl=3600
    logging.level=INFO
  
  # Nginx конфигурация
  nginx.conf: |
    server {
        listen 80;
        server_name localhost;
        
        location / {
            proxy_pass http://backend:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
        
        location /health {
            access_log off;
            return 200 "healthy\n";
        }
    }
EOF

# 3. Создайте Secret с различными типами секретов
cat > secret-demo.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: multi-secret
  namespace: default
type: Opaque
data:
  # Пароли (base64 encoded)
  database_password: cG9zdGdyZXNfcGFzc3dvcmQ=      # postgres_password
  redis_password: cmVkaXNfcGFzc3dvcmQ=              # redis_password
  admin_password: YWRtaW5fcGFzc3dvcmQ=              # admin_password
  
  # API ключи
  github_token: Z2hwX2FiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6MTIzNDU=  # ghp_abcdefghijklmnopqrstuvwxyz12345
  aws_access_key: QUtJQUlPU0ZPRE5ON0VYQU1QTEU=                    # AKIAIOSFODNN7EXAMPLE
  aws_secret_key: d0phbHJYVXRuRkVNSS9LN01ERU5HL2JQWEZ1Q1lFWEFNUExFa2V5  # wJalrXUtnFEMI/K7MDENG/bPwFzuCYEXAMPLEkey
  
  # TLS сертификаты (примеры)
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t                   # -----BEGIN CERTIFICATE-----
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t                   # -----BEGIN PRIVATE KEY-----
---
# Docker registry secret
apiVersion: v1
kind: Secret
metadata:
  name: docker-registry-secret
  namespace: default
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6InVzZXIiLCJwYXNzd29yZCI6InBhc3N3b3JkIiwiYXV0aCI6ImRYTmxjanB3WVhOemQyOXlaQT09In19fQ==
---
# TLS secret
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
  namespace: default
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t
EOF

# 4. Создайте Pod для демонстрации использования конфигураций
cat > config-consumer-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: config-consumer
  namespace: default
spec:
  containers:
  - name: app
    image: busybox:latest
    command: ['sh', '-c', 'while true; do sleep 30; done']
    
    # Переменные окружения из ConfigMap
    env:
    - name: DATABASE_HOST
      valueFrom:
        configMapKeyRef:
          name: multi-config
          key: database_host
    - name: DATABASE_PORT
      valueFrom:
        configMapKeyRef:
          name: multi-config
          key: database_port
    - name: CACHE_SIZE
      valueFrom:
        configMapKeyRef:
          name: multi-config
          key: cache_size
    
    # Переменные из Secret
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: multi-secret
          key: database_password
    - name: GITHUB_TOKEN
      valueFrom:
        secretKeyRef:
          name: multi-secret
          key: github_token
    
    # Монтирование файлов конфигурации
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
      readOnly: true
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
    - name: nginx-config
      mountPath: /etc/nginx
      readOnly: true
      
  volumes:
  # Полный ConfigMap как том
  - name: config-volume
    configMap:
      name: multi-config
  
  # Secret как том с ограниченными правами
  - name: secret-volume
    secret:
      secretName: multi-secret
      defaultMode: 0400
  
  # Отдельные файлы из ConfigMap
  - name: nginx-config
    configMap:
      name: multi-config
      items:
      - key: nginx.conf
        path: nginx.conf
        mode: 0644
  
  restartPolicy: Never
EOF

# 5. Примените все конфигурации
echo "📦 Создание ConfigMap и Secret..."
kubectl apply -f configmap-demo.yaml
kubectl apply -f secret-demo.yaml

echo "🚀 Запуск Pod для демонстрации..."
kubectl apply -f config-consumer-pod.yaml

# 6. Дождитесь запуска Pod
kubectl wait --for=condition=ready pod config-consumer --timeout=60s

# 7. Демонстрация переменных окружения
echo "🔍 Переменные окружения в Pod:"
kubectl exec config-consumer -- env | grep -E "(DATABASE|CACHE|GITHUB)" | sort

# 8. Демонстрация монтированных файлов
echo -e "\n📁 Конфигурационные файлы в /etc/config:"
kubectl exec config-consumer -- ls -la /etc/config/

echo -e "\n📄 Содержимое app_config.json:"
kubectl exec config-consumer -- cat /etc/config/app_config.json

echo -e "\n📄 Содержимое nginx.conf:"
kubectl exec config-consumer -- cat /etc/nginx/nginx.conf

echo -e "\n🔒 Секретные файлы в /etc/secrets:"
kubectl exec config-consumer -- ls -la /etc/secrets/

# Примечание: не показываем содержимое секретов в продакшене!
echo -e "\n⚠️ Содержимое секретов (только для демо):"
kubectl exec config-consumer -- sh -c 'echo "Database password: $(cat /etc/secrets/database_password)"'

# 9. Создайте Deployment с обновлением конфигурации
cat > config-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-demo-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: config-demo
  template:
    metadata:
      labels:
        app: config-demo
      # Аннотация для автоматического перезапуска при изменении ConfigMap
      annotations:
        configmap/hash: ""  # Будет обновляться автоматически
    spec:
      containers:
      - name: app
        image: nginx:alpine
        ports:
        - containerPort: 80
        
        env:
        - name: DATABASE_HOST
          valueFrom:
            configMapKeyRef:
              name: multi-config
              key: database_host
        - name: DEBUG_MODE
          valueFrom:
            configMapKeyRef:
              name: multi-config
              key: debug_mode
        
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf
        - name: app-config
          mountPath: /etc/app/config.json
          subPath: app_config.json
      
      volumes:
      - name: nginx-config
        configMap:
          name: multi-config
          items:
          - key: nginx.conf
            path: nginx.conf
      - name: app-config
        configMap:
          name: multi-config
          items:
          - key: app_config.json
            path: app_config.json
EOF

echo -e "\n🚀 Создание Deployment с конфигурацией..."
kubectl apply -f config-deployment.yaml

# 10. Обновление ConfigMap и демонстрация rolling update
echo -e "\n🔄 Обновление конфигурации..."
kubectl patch configmap multi-config --patch '{"data":{"debug_mode":"true","cache_size":"200"}}'

# 11. Принудительный restart Deployment для применения изменений
kubectl rollout restart deployment config-demo-app

echo -e "\n⏳ Ожидание завершения rolling update..."
kubectl rollout status deployment config-demo-app

# 12. Проверка обновленной конфигурации
echo -e "\n✅ Проверка обновленных переменных:"
POD_NAME=$(kubectl get pods -l app=config-demo -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD_NAME -- env | grep -E "(DEBUG_MODE|CACHE_SIZE)"

# 13. Создайте скрипт для демонстрации управления секретами
cat > manage-secrets.sh << 'EOF'
#!/bin/bash

echo "🔐 Демонстрация управления секретами в Kubernetes"
echo "================================================"

# Создание секрета из командной строки
echo "1. Создание секрета из командной строки:"
kubectl create secret generic cli-secret \
  --from-literal=username=admin \
  --from-literal=password=secret123 \
  --dry-run=client -o yaml

echo -e "\n2. Создание секрета из файлов:"
echo "secret-value-1" > /tmp/secret1.txt
echo "secret-value-2" > /tmp/secret2.txt
kubectl create secret generic file-secret \
  --from-file=/tmp/secret1.txt \
  --from-file=/tmp/secret2.txt \
  --dry-run=client -o yaml

echo -e "\n3. Создание Docker registry секрета:"
kubectl create secret docker-registry my-registry-secret \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=myemail@example.com \
  --dry-run=client -o yaml

echo -e "\n4. Создание TLS секрета:"
# В реальности вы бы использовали настоящие сертификаты
kubectl create secret tls my-tls-secret \
  --cert=/path/to/cert/file \
  --key=/path/to/key/file \
  --dry-run=client -o yaml || echo "Нужны реальные сертификаты"

echo -e "\n5. Просмотр секретов (без значений):"
kubectl get secrets
kubectl describe secret multi-secret

echo -e "\n6. Декодирование секрета (только для демонстрации!):"
kubectl get secret multi-secret -o jsonpath='{.data.database_password}' | base64 -d
echo ""

# Очистка временных файлов
rm -f /tmp/secret*.txt
EOF

chmod +x manage-secrets.sh

echo -e "\n📚 Демонстрация управления конфигурацией завершена!"
echo "🔧 Для изучения управления секретами запустите: ./manage-secrets.sh"
echo "🧹 Для очистки ресурсов:"
echo "  kubectl delete pod config-consumer"
echo "  kubectl delete deployment config-demo-app"
echo "  kubectl delete configmap multi-config"
echo "  kubectl delete secret multi-secret docker-registry-secret tls-secret"

Проверка: Pod должен получать переменные из ConfigMap и Secret, файлы должны быть правильно смонтированы, обновление конфигурации должно приводить к rolling update.

Задание 4: Автомасштабирование и нагрузочное тестирование (35 минут) #

# 1. Создайте проект для демонстрации автомасштабирования
mkdir ~/k8s-scaling-demo && cd ~/k8s-scaling-demo

# 2. Создайте приложение с возможностью создания нагрузки
cat > stress-app.py << 'EOF'
from flask import Flask, jsonify, request
import multiprocessing
import time
import os
import threading

app = Flask(__name__)

# Глобальная переменная для отслеживания нагрузки
current_load = 0
load_lock = threading.Lock()

@app.route('/')
def home():
    return jsonify({
        'message': 'Stress Test Application',
        'hostname': os.uname().nodename,
        'current_load': current_load,
        'cpu_count': multiprocessing.cpu_count()
    })

@app.route('/health')
def health():
    return jsonify({'status': 'healthy'})

@app.route('/stress')
def stress():
    global current_load
    
    # Параметры нагрузки
    duration = int(request.args.get('duration', 10))
    intensity = float(request.args.get('intensity', 0.5))
    
    def cpu_stress():
        global current_load
        with load_lock:
            current_load += 1
        
        start_time = time.time()
        while time.time() - start_time < duration:
            # Создаем CPU нагрузку
            if time.time() % 1 < intensity:
                sum(i * i for i in range(10000))
        
        with load_lock:
            current_load -= 1
    
    # Запускаем нагрузку в отдельном потоке
    thread = threading.Thread(target=cpu_stress)
    thread.start()
    
    return jsonify({
        'message': f'Started stress test for {duration} seconds',
        'intensity': intensity,
        'hostname': os.uname().nodename,
        'active_stress_threads': current_load
    })

@app.route('/memory-stress')
def memory_stress():
    """Создает нагрузку на память"""
    size_mb = int(request.args.get('size', 50))
    duration = int(request.args.get('duration', 10))
    
    def memory_load():
        # Выделяем память
        data = []
        for i in range(size_mb):
            # Создаем массив размером ~1MB
            chunk = bytearray(1024 * 1024)
            data.append(chunk)
        
        # Держим память занятой
        time.sleep(duration)
        
        # Освобождаем память
        del data
    
    thread = threading.Thread(target=memory_load)
    thread.start()
    
    return jsonify({
        'message': f'Started memory stress test: {size_mb}MB for {duration} seconds',
        'hostname': os.uname().nodename
    })

@app.route('/metrics')
def metrics():
    """Простые метрики для мониторинга"""
    import psutil
    
    return jsonify({
        'cpu_percent': psutil.cpu_percent(),
        'memory_percent': psutil.virtual_memory().percent,
        'hostname': os.uname().nodename,
        'active_stress_threads': current_load
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=False)
EOF

# 3. Создайте requirements.txt
cat > requirements.txt << 'EOF'
Flask==2.3.3
psutil==5.9.5
gunicorn==21.2.0
EOF

# 4. Создайте Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.11-slim

# Установка системных утилит
RUN apt-get update && apt-get install -y \
    stress-ng \
    htop \
    && rm -rf /var/lib/apt/lists/*

# Создание пользователя
RUN addgroup --gid 1001 appgroup && \
    adduser --uid 1001 --gid 1001 --disabled-password --gecos "" appuser

WORKDIR /app

# Установка Python зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Копирование приложения
COPY --chown=appuser:appgroup stress-app.py .

USER appuser

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "--timeout", "60", "stress-app:app"]
EOF

# 5. Создайте Deployment с ресурсными ограничениями
cat > stress-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stress-app
  namespace: default
  labels:
    app: stress-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: stress-app
  template:
    metadata:
      labels:
        app: stress-app
    spec:
      containers:
      - name: stress-app
        image: stress-app:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
        
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "400m"
        
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: stress-app-service
  labels:
    app: stress-app
spec:
  selector:
    app: stress-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
  type: ClusterIP
---
# NodePort для внешнего доступа
apiVersion: v1
kind: Service
metadata:
  name: stress-app-nodeport
  labels:
    app: stress-app
spec:
  selector:
    app: stress-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    nodePort: 30090
  type: NodePort
EOF

# 6. Создайте HorizontalPodAutoscaler
cat > hpa.yaml << 'EOF'
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: stress-app-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: stress-app
  minReplicas: 2
  maxReplicas: 8
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
      - type: Pods
        value: 2
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 180
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
EOF

# 7. Создайте VPA (Vertical Pod Autoscaler) для демонстрации
cat > vpa.yaml << 'EOF'
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: stress-app-vpa
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: stress-app
  updatePolicy:
    updateMode: "Off"  # Только рекомендации, без автоматического обновления
  resourcePolicy:
    containerPolicies:
    - containerName: stress-app
      minAllowed:
        cpu: 50m
        memory: 64Mi
      maxAllowed:
        cpu: 500m
        memory: 512Mi
      controlledResources: ["cpu", "memory"]
EOF

# 8. Соберите и разверните приложение
echo "🐳 Сборка Docker образа..."
docker build -t stress-app:latest .

echo "⬆️ Загрузка образа в minikube..."
minikube image load stress-app:latest

echo "🚀 Развертывание приложения..."
kubectl apply -f stress-deployment.yaml

echo "⏳ Ожидание готовности Pod..."
kubectl wait --for=condition=ready pod -l app=stress-app --timeout=120s

echo "📊 Применение HPA..."
kubectl apply -f hpa.yaml

# Применение VPA только если установлен VPA controller
kubectl apply -f vpa.yaml 2>/dev/null || echo "VPA не установлен, пропускаем"

# 9. Создайте скрипт для мониторинга автомасштабирования
cat > monitor-scaling.sh << 'EOF'
#!/bin/bash

echo "📊 Мониторинг автомасштабирования Kubernetes"
echo "============================================"

while true; do
    clear
    echo "$(date)"
    echo "============================================"
    
    echo "🏃 HPA Status:"
    kubectl get hpa stress-app-hpa
    
    echo -e "\n📦 Pod Status:"
    kubectl get pods -l app=stress-app -o wide
    
    echo -e "\n📈 Resource Usage:"
    kubectl top pods -l app=stress-app 2>/dev/null || echo "Metrics server недоступен"
    
    echo -e "\n🎯 Node Resources:"
    kubectl top nodes 2>/dev/null || echo "Metrics server недоступен"
    
    echo -e "\n📋 Recent Events:"
    kubectl get events --field-selector involvedObject.name=stress-app-hpa --sort-by='.lastTimestamp' | tail -3
    
    echo -e "\nPress Ctrl+C to stop monitoring"
    sleep 10
done
EOF

chmod +x monitor-scaling.sh

# 10. Создайте скрипт для генерации нагрузки
cat > generate-load.sh << 'EOF'
#!/bin/bash

NODEPORT_URL="http://$(minikube ip):30090"

echo "🔥 Генератор нагрузки для тестирования автомасштабирования"
echo "URL приложения: $NODEPORT_URL"
echo "========================================================="

echo "1. Проверка доступности приложения:"
curl -s $NODEPORT_URL | python3 -m json.tool

echo -e "\n2. Создание базовой нагрузки (10 параллельных запросов):"
for i in {1..10}; do
    curl -s "$NODEPORT_URL/stress?duration=30&intensity=0.3" &
done
wait

echo -e "\n3. Создание высокой CPU нагрузки:"
for i in {1..5}; do
    curl -s "$NODEPORT_URL/stress?duration=60&intensity=0.8" &
done

echo -e "\n4. Создание нагрузки на память:"
for i in {1..3}; do
    curl -s "$NODEPORT_URL/memory-stress?size=100&duration=60" &
done

echo -e "\n5. Непрерывная нагрузка (для демонстрации масштабирования):"
while true; do
    curl -s "$NODEPORT_URL/stress?duration=20&intensity=0.7" > /dev/null &
    curl -s "$NODEPORT_URL/memory-stress?size=80&duration=20" > /dev/null &
    sleep 5
done
EOF

chmod +x generate-load.sh

# 11. Создайте скрипт для тестирования различных сценариев
cat > test-scenarios.sh << 'EOF'
#!/bin/bash

NODEPORT_URL="http://$(minikube ip):30090"

echo "🧪 Сценарии тестирования автомасштабирования"
echo "============================================"

function wait_for_scaling() {
    echo "⏳ Ожидание масштабирования (60 секунд)..."
    sleep 60
    kubectl get hpa stress-app-hpa
    kubectl get pods -l app=stress-app
}

echo "Сценарий 1: Постепенное увеличение нагрузки"
echo "-------------------------------------------"
for intensity in 0.2 0.4 0.6 0.8; do
    echo "Интенсивность: $intensity"
    for i in {1..5}; do
        curl -s "$NODEPORT_URL/stress?duration=30&intensity=$intensity" > /dev/null &
    done
    wait_for_scaling
done

echo -e "\nСценарий 2: Пиковая нагрузка"
echo "----------------------------"
for i in {1..20}; do
    curl -s "$NODEPORT_URL/stress?duration=90&intensity=0.9" > /dev/null &
done
wait_for_scaling

echo -e "\nСценарий 3: Смешанная нагрузка (CPU + Memory)"
echo "---------------------------------------------"
for i in {1..10}; do
    curl -s "$NODEPORT_URL/stress?duration=60&intensity=0.6" > /dev/null &
    curl -s "$NODEPORT_URL/memory-stress?size=120&duration=60" > /dev/null &
done
wait_for_scaling

echo -e "\nСценарий 4: Снижение нагрузки (scale down)"
echo "------------------------------------------"
echo "Прекращение генерации нагрузки..."
pkill -f curl
echo "Ожидание уменьшения количества Pod (3 минуты)..."
sleep 180
kubectl get hpa stress-app-hpa
kubectl get pods -l app=stress-app

echo -e "\n✅ Тестирование завершено!"
EOF

chmod +x test-scenarios.sh

# 12. Запустите мониторинг и начальное тестирование
echo "📊 Текущий статус HPA:"
kubectl get hpa

echo -e "\n📦 Текущие Pod:"
kubectl get pods -l app=stress-app

echo -e "\n🌐 URL приложения: http://$(minikube ip):30090"

echo -e "\n📚 Доступные скрипты:"
echo "  ./monitor-scaling.sh    - Мониторинг автомасштабирования"
echo "  ./generate-load.sh      - Генерация нагрузки"
echo "  ./test-scenarios.sh     - Тестирование различных сценариев"

echo -e "\n🧪 Базовое тестирование:"
NODEPORT_URL="http://$(minikube ip):30090"
echo "Проверка приложения:"
curl -s $NODEPORT_URL | python3 -m json.tool

echo -e "\nСоздание легкой нагрузки для демонстрации:"
curl -s "$NODEPORT_URL/stress?duration=30&intensity=0.5" | python3 -m json.tool

echo -e "\n⚡ Для наблюдения за автомасштабированием запустите в отдельных терминалах:"
echo "  Terminal 1: ./monitor-scaling.sh"
echo "  Terminal 2: ./generate-load.sh"

Проверка: HPA должен корректно масштабировать Pod при увеличении нагрузки, метрики должны отображаться, и количество Pod должно увеличиваться/уменьшаться в зависимости от нагрузки.

Задание 5: Производственный деплой с мониторингом (40 минут) #

# 1. Создайте проект для производственного деплоя
mkdir ~/k8s-production && cd ~/k8s-production

# 2. Создайте namespace для production
cat > 01-namespace.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    name: production
    environment: production
    tier: production
---
# Resource quota для production namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"
    services: "10"
    persistentvolumeclaims: "5"
---
# Limit range для Pod
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "512Mi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    type: Container
EOF

# 3. Создайте RBAC конфигурацию
cat > 02-rbac.yaml << 'EOF'
# Service Account для production приложений
apiVersion: v1
kind: ServiceAccount
metadata:
  name: production-app-sa
  namespace: production
---
# Role для production приложений
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: production-app-role
  namespace: production
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "secrets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: production-app-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: production-app-sa
  namespace: production
roleRef:
  kind: Role
  name: production-app-role
  apiGroup: rbac.authorization.k8s.io
EOF

# 4. Создайте Network Policy
cat > 03-network-policy.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: production-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: production-app
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Разрешить трафик от ingress controller
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8080
  # Разрешить трафик для мониторинга
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 8080
  egress:
  # Разрешить DNS
  - to: []
    ports:
    - protocol: UDP
      port: 53
  # Разрешить HTTPS (для внешних API)
  - to: []
    ports:
    - protocol: TCP
      port: 443
  # Разрешить доступ к базе данных
  - to:
    - namespaceSelector:
        matchLabels:
          name: database
    ports:
    - protocol: TCP
      port: 5432
EOF

# 5. Создайте Pod Security Policy
cat > 04-pod-security.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: security-example
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: nginx:alpine
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: var-cache
      mountPath: /var/cache/nginx
    - name: var-run
      mountPath: /var/run
  volumes:
  - name: tmp
    emptyDir: {}
  - name: var-cache
    emptyDir: {}
  - name: var-run
    emptyDir: {}
  restartPolicy: Never
EOF

# 6. Создайте приложение с мониторингом
cat > production-app.py << 'EOF'
from flask import Flask, jsonify, request, Response
import os
import socket
import time
import psutil
import json
import logging
from datetime import datetime
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('/tmp/app.log')
    ]
)
logger = logging.getLogger(__name__)

app = Flask(__name__)

# Prometheus метрики
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint', 'status'])
REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')
ACTIVE_CONNECTIONS = Gauge('active_connections', 'Number of active connections')
APP_INFO = Gauge('app_info', 'Application info', ['version', 'hostname'])

# Инициализация метрик
APP_INFO.labels(version=os.getenv('APP_VERSION', 'unknown'), hostname=socket.gethostname()).set(1)

@app.before_request
def before_request():
    request.start_time = time.time()
    ACTIVE_CONNECTIONS.inc()

@app.after_request
def after_request(response):
    REQUEST_COUNT.labels(
        method=request.method,
        endpoint=request.endpoint or 'unknown',
        status=response.status_code
    ).inc()
    REQUEST_DURATION.observe(time.time() - request.start_time)
    ACTIVE_CONNECTIONS.dec()
    return response

@app.route('/')
def home():
    logger.info("Home endpoint accessed")
    return jsonify({
        'message': 'Production Application',
        'hostname': socket.gethostname(),
        'version': os.getenv('APP_VERSION', 'v1.0'),
        'environment': os.getenv('ENVIRONMENT', 'production'),
        'timestamp': datetime.now().isoformat(),
        'status': 'healthy'
    })

@app.route('/health')
def health():
    """Health check endpoint"""
    try:
        # Проверки здоровья приложения
        cpu_percent = psutil.cpu_percent()
        memory_percent = psutil.virtual_memory().percent
        disk_percent = psutil.disk_usage('/').percent
        
        # Определение статуса
        if cpu_percent > 90 or memory_percent > 90 or disk_percent > 90:
            status = 'degraded'
        else:
            status = 'healthy'
        
        return jsonify({
            'status': status,
            'checks': {
                'cpu_percent': cpu_percent,
                'memory_percent': memory_percent,
                'disk_percent': disk_percent
            },
            'hostname': socket.gethostname(),
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        logger.error(f"Health check failed: {e}")
        return jsonify({
            'status': 'unhealthy',
            'error': str(e),
            'hostname': socket.gethostname()
        }), 500

@app.route('/ready')
def ready():
    """Readiness check endpoint"""
    # Проверка готовности (например, подключение к БД)
    return jsonify({
        'status': 'ready',
        'hostname': socket.gethostname(),
        'timestamp': datetime.now().isoformat()
    })

@app.route('/metrics')
def metrics():
    """Prometheus metrics endpoint"""
    return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST)

@app.route('/info')
def info():
    """Detailed application information"""
    return jsonify({
        'application': {
            'name': 'production-app',
            'version': os.getenv('APP_VERSION', 'v1.0'),
            'environment': os.getenv('ENVIRONMENT', 'production'),
            'hostname': socket.gethostname()
        },
        'system': {
            'cpu_count': psutil.cpu_count(),
            'memory_total_gb': round(psutil.virtual_memory().total / (1024**3), 2),
            'disk_total_gb': round(psutil.disk_usage('/').total / (1024**3), 2)
        },
        'configuration': {
            'database_url': os.getenv('DATABASE_URL', 'not configured'),
            'cache_enabled': os.getenv('CACHE_ENABLED', 'false'),
            'log_level': os.getenv('LOG_LEVEL', 'info'),
            'max_connections': os.getenv('MAX_CONNECTIONS', '100')
        },
        'runtime': {
            'start_time': datetime.fromtimestamp(psutil.Process().create_time()).isoformat(),
            'uptime_seconds': int(time.time() - psutil.Process().create_time()),
            'python_version': os.sys.version
        }
    })

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found', 'hostname': socket.gethostname()}), 404

@app.errorhandler(500)
def internal_error(error):
    logger.error(f"Internal server error: {error}")
    return jsonify({'error': 'Internal server error', 'hostname': socket.gethostname()}), 500

if __name__ == '__main__':
    logger.info("Starting production application")
    app.run(host='0.0.0.0', port=8080, debug=False)
EOF

# 7. Создайте requirements.txt
cat > requirements.txt << 'EOF'
Flask==2.3.3
gunicorn==21.2.0
psutil==5.9.5
prometheus-client==0.17.1
EOF

# 8. Создайте production Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.11-slim

# Метаданные
LABEL maintainer="devops@company.com"
LABEL version="2.0.0"
LABEL description="Production Flask Application with Monitoring"

# Установка системных зависимостей
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    procps \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean

# Создание пользователя
RUN groupadd -r -g 1000 appgroup && \
    useradd -r -u 1000 -g appgroup -m -s /bin/false appuser

# Создание директорий
RUN mkdir -p /app /tmp/app-logs && \
    chown -R appuser:appgroup /app /tmp/app-logs

WORKDIR /app

# Установка Python зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# Копирование приложения
COPY --chown=appuser:appgroup production-app.py .

# Переключение на непривилегированного пользователя
USER appuser

# Переменные окружения
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV FLASK_ENV=production
ENV APP_VERSION=v2.0.0

EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=15s --start-period=30s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

# Запуск с Gunicorn
CMD ["gunicorn", \
     "--bind", "0.0.0.0:8080", \
     "--workers", "3", \
     "--timeout", "60", \
     "--max-requests", "1000", \
     "--max-requests-jitter", "100", \
     "--preload", \
     "--access-logfile", "-", \
     "--error-logfile", "-", \
     "production-app:app"]
EOF

# 9. Создайте ConfigMap для production
cat > 05-configmap.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: production-config
  namespace: production
data:
  ENVIRONMENT: "production"
  DATABASE_URL: "postgresql://prod-db:5432/production"
  CACHE_ENABLED: "true"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "200"
  
  # Конфигурационные файлы
  gunicorn.conf.py: |
    import multiprocessing
    
    bind = "0.0.0.0:8080"
    workers = multiprocessing.cpu_count() * 2 + 1
    worker_class = "sync"
    worker_connections = 1000
    max_requests = 1000
    max_requests_jitter = 100
    preload_app = True
    timeout = 60
    keepalive = 5
    
    # Logging
    accesslog = "-"
    errorlog = "-"
    loglevel = "info"
    access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
    
    def when_ready(server):
        server.log.info("Server is ready. Spawning workers")
    
    def worker_int(worker):
        worker.log.info("worker received INT or QUIT signal")
    
    def on_exit(server):
        server.log.info("Server is shutting down")
EOF

# 10. Создайте Secret для production
cat > 06-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: production-secrets
  namespace: production
type: Opaque
data:
  # Production пароли (base64 encoded)
  database_password: cHJvZHVjdGlvbl9wYXNzd29yZA==  # production_password
  api_key: cHJvZF9hcGlfa2V5XzEyMzQ1Ng==            # prod_api_key_123456
  jwt_secret: cHJvZF9qd3Rfc2VjcmV0X2tleQ==          # prod_jwt_secret_key
  redis_password: cHJvZF9yZWRpc19wYXNzd29yZA==      # prod_redis_password
EOF

# 11. Создайте production Deployment
cat > 07-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production-app
  namespace: production
  labels:
    app: production-app
    version: v2.0.0
    tier: backend
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0  # Zero downtime deployment
  selector:
    matchLabels:
      app: production-app
  template:
    metadata:
      labels:
        app: production-app
        version: v2.0.0
        tier: backend
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/metrics"
        config.hash: "{{ checksum }}"  # Для автоматического обновления при изменении конфигурации
    spec:
      serviceAccountName: production-app-sa
      
      # Настройки безопасности
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      
      containers:
      - name: production-app
        image: production-app:v2.0.0
        imagePullPolicy: Never
        
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        
        # Переменные окружения из ConfigMap
        envFrom:
        - configMapRef:
            name: production-config
        
        env:
        - name: APP_VERSION
          value: "v2.0.0"
        # Секреты
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: production-secrets
              key: database_password
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: production-secrets
              key: api_key
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: production-secrets
              key: jwt_secret
        
        # Ресурсы для production
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        
        # Production health checks
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 10
          failureThreshold: 3
          successThreshold: 1
          
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
          successThreshold: 1
        
        # Startup probe для медленно стартующих приложений
        startupProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 10
        
        # Security context
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          capabilities:
            drop:
            - ALL
        
        # Volume mounts
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: app-logs
          mountPath: /tmp/app-logs
        - name: config-volume
          mountPath: /etc/app-config
          readOnly: true
      
      volumes:
      - name: tmp
        emptyDir: {}
      - name: app-logs
        emptyDir: {}
      - name: config-volume
        configMap:
          name: production-config
          items:
          - key: gunicorn.conf.py
            path: gunicorn.conf.py
      
      # Распределение Pod по узлам
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - production-app
              topologyKey: kubernetes.io/hostname
      
      # Tolerations для production nodes
      tolerations:
      - key: "production"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"
      
      # Graceful shutdown
      terminationGracePeriodSeconds: 60
EOF

# 12. Создайте Services
cat > 08-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: production-app-service
  namespace: production
  labels:
    app: production-app
spec:
  selector:
    app: production-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
  type: ClusterIP
  sessionAffinity: None
---
# Headless service для direct Pod access
apiVersion: v1
kind: Service
metadata:
  name: production-app-headless
  namespace: production
  labels:
    app: production-app
spec:
  selector:
    app: production-app
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  clusterIP: None
---
# NodePort для внешнего доступа
apiVersion: v1
kind: Service
metadata:
  name: production-app-nodeport
  namespace: production
  labels:
    app: production-app
spec:
  selector:
    app: production-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    nodePort: 30100
  type: NodePort
EOF

# 13. Создайте HPA для production
cat > 09-hpa.yaml << 'EOF'
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: production-app-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: production-app
  minReplicas: 3
  maxReplicas: 15
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 75
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
      - type: Pods
        value: 2
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 25
        periodSeconds: 60
EOF

# 14. Создайте PodDisruptionBudget
cat > 10-pdb.yaml << 'EOF'
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: production-app-pdb
  namespace: production
spec:
  selector:
    matchLabels:
      app: production-app
  minAvailable: 2  # Минимум 2 Pod должны быть доступны во время обновлений
EOF

# 15. Соберите и разверните приложение
echo "🐳 Сборка production Docker образа..."
docker build -t production-app:v2.0.0 .

echo "⬆️ Загрузка образа в minikube..."
minikube image load production-app:v2.0.0

echo "🚀 Развертывание production приложения..."
kubectl apply -f 01-namespace.yaml
kubectl apply -f 02-rbac.yaml
kubectl apply -f 03-network-policy.yaml
kubectl apply -f 05-configmap.yaml
kubectl apply -f 06-secret.yaml
kubectl apply -f 07-deployment.yaml
kubectl apply -f 08-service.yaml
kubectl apply -f 09-hpa.yaml
kubectl apply -f 10-pdb.yaml

echo "⏳ Ожидание готовности всех Pod..."
kubectl wait --for=condition=ready pod -l app=production-app -n production --timeout=300s

# 16. Создайте скрипт для мониторинга production
cat > production-monitor.sh << 'EOF'
#!/bin/bash

echo "📊 Production Kubernetes Application Monitor"
echo "==========================================="

while true; do
    clear
    echo "$(date)"
    echo "==========================================="
    
    echo "🏗️ Deployment Status:"
    kubectl get deployment -n production
    
    echo -e "\n📦 Pod Status:"
    kubectl get pods -n production -o wide
    
    echo -e "\n🔄 HPA Status:"
    kubectl get hpa -n production
    
    echo -e "\n📊 Resource Usage:"
    kubectl top pods -n production 2>/dev/null || echo "Metrics server unavailable"
    
    echo -e "\n🚦 Service Status:"
    kubectl get services -n production
    
    echo -e "\n📋 Recent Events:"
    kubectl get events -n production --sort-by='.lastTimestamp' | tail -5
    
    echo -e "\n🔒 Security Status:"
    kubectl get pdb,networkpolicy -n production
    
    echo -e "\nPress Ctrl+C to stop monitoring"
    sleep 15
done
EOF

chmod +x production-monitor.sh

# 17. Создайте скрипт для health checks
cat > health-checks.sh << 'EOF'
#!/bin/bash

NODEPORT_URL="http://$(minikube ip):30100"

echo "🏥 Production Application Health Checks"
echo "======================================"

echo "Application URL: $NODEPORT_URL"
echo ""

function check_endpoint() {
    local endpoint=$1
    local expected_status=$2
    
    echo -n "Checking $endpoint... "
    response=$(curl -s -w "%{http_code}" $NODEPORT_URL$endpoint)
    status_code="${response: -3}"
    body="${response%???}"
    
    if [ "$status_code" -eq "$expected_status" ]; then
        echo "✅ OK ($status_code)"
        if [ "$endpoint" != "/metrics" ]; then
            echo "$body" | python3 -m json.tool 2>/dev/null || echo "$body"
        fi
    else
        echo "❌ FAILED (expected $expected_status, got $status_code)"
        echo "Response: $body"
    fi
    echo ""
}

echo "1. Health Checks:"
check_endpoint "/" 200
check_endpoint "/health" 200
check_endpoint "/ready" 200
check_endpoint "/info" 200

echo "2. Metrics Check:"
check_endpoint "/metrics" 200

echo "3. Error Handling:"
check_endpoint "/non-existent" 404

echo "4. Load Balancing Test:"
echo "Testing load balancing across pods..."
for i in {1..5}; do
    hostname=$(curl -s $NODEPORT_URL | python3 -c "import sys, json; print(json.load(sys.stdin)['hostname'])" 2>/dev/null)
    echo "Request $i: Pod $hostname"
done

echo -e "\n5. Resource Monitoring:"
echo "Pod resource usage:"
kubectl top pods -n production 2>/dev/null || echo "Metrics server unavailable"

echo -e "\n6. Application Logs (last 10 lines):"
kubectl logs -n production -l app=production-app --tail=10 --prefix=true

echo -e "\n✅ Health checks completed!"
EOF

chmod +x health-checks.sh

# 18. Выполните итоговые проверки
echo "📊 Production deployment status:"
kubectl get all -n production

echo -e "\n🌐 Application URL: http://$(minikube ip):30100"

echo -e "\n🧪 Running initial health checks..."
sleep 10
./health-checks.sh

echo -e "\n📚 Available monitoring scripts:"
echo "  ./production-monitor.sh  - Continuous monitoring"
echo "  ./health-checks.sh       - Health and functionality checks"

echo -e "\n✅ Production deployment completed!"
echo "🎯 Key features implemented:"
echo "   - Resource quotas and limits"
echo "   - RBAC security"
echo "   - Network policies"
echo "   - Pod security contexts"
echo "   - Health checks and monitoring"
echo "   - Horizontal autoscaling"
echo "   - Pod disruption budgets"
echo "   - Zero-downtime deployments"

Проверка: Production приложение должно быть развернуто с полной конфигурацией безопасности, все health checks должны проходить, мониторинг должен показывать корректные метрики, и автомасштабирование должно работать.

📊 Проверочный тест Kubernetes #

  1. Какая команда показывает все Pod во всех namespace?
  2. Как масштабировать Deployment до 5 реплик?
  3. Какой тип Service обеспечивает внешний доступ через LoadBalancer?
  4. Как посмотреть логи всех Pod с лейблом app=myapp?
  5. Какая команда применяет все YAML файлы из директории?

Ответы:

  1. kubectl get pods --all-namespaces
  2. kubectl scale deployment myapp --replicas=5
  3. LoadBalancer
  4. kubectl logs -l app=myapp
  5. kubectl apply -f ./directory/

🛠️ Полезные инструменты #

k9s - TUI для Kubernetes #

# Установка
curl -sS https://webinstall.dev/k9s | bash

# Использование
k9s

Helm - пакетный менеджер #

# Установка Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Добавить репозиторий
helm repo add stable https://charts.helm.sh/stable
helm repo update

# Установить приложение
helm install my-release stable/mysql

# Список установленных релизов
helm list

Kustomize - управление конфигурациями #

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml
- service.yaml

namePrefix: prod-
namespace: production

images:
- name: myapp
  newTag: v2.0

replicas:
- name: webapp-deployment
  count: 5

🎯 Заключение #

Kubernetes — это мощная платформа оркестрации, но и сложная. Ключевые принципы:

Изучайте постепенно - начните с Pod, Service, Deployment
Практикуйтесь локально - используйте minikube или kind
Понимайте архитектуру - как компоненты взаимодействуют
Используйте декларативный подход - YAML манифесты
Мониторьте и логируйте - observability критически важна
Думайте о безопасности - RBAC, Network Policies, Pod Security

Помните: Kubernetes - это марафон, а не спринт. Не пытайтесь изучить всё сразу. Начните с основ и постепенно углубляйтесь в детали.


Следующий раздел: 2.5 Облачные платформы