Docker

Что такое Docker

Docker — это платформа для контейнеризации приложений, которая позволяет упаковать приложение со всеми его зависимостями в легковесный, переносимый контейнер. Контейнер может запускаться на любой системе, поддерживающей Docker.

Основные концепции

Image (Образ) — read-only шаблон для создания контейнеров. Содержит файловую систему, runtime, библиотеки, переменные окружения и конфигурацию.

Container (Контейнер) — запущенный экземпляр образа. Изолированная среда выполнения с собственной файловой системой, сетью и процессами.

Dockerfile — текстовый файл с инструкциями для сборки образа. Описывает каждый шаг создания образа как отдельный layer.

Layer (Слой) — промежуточный образ, созданный каждой инструкцией в Dockerfile. Layers кешируются и переиспользуются.

Registry — хранилище Docker образов (Docker Hub, AWS ECR, Google Container Registry).

Преимущества контейнеризации

  • Consistency — одинаковое поведение в dev, test и production
  • Isolation — изоляция приложений друг от друга
  • Portability — запуск на любой платформе с Docker
  • Scalability — легкое масштабирование и orchestration
  • Efficiency — быстрый старт, меньше ресурсов чем VMs

Пояснение: Docker решает проблему "у меня работает" — если приложение работает в контейнере локально, оно будет работать везде.


Создание Dockerfile

Базовая структура

# Базовый образ
FROM openjdk:17-jdk-slim

# Метаданные
LABEL maintainer="developer@company.com"
LABEL version="1.0"
LABEL description="Spring Boot application"

# Рабочая директория
WORKDIR /app

# Копирование файлов
COPY target/myapp.jar app.jar

# Переменные окружения
ENV SPRING_PROFILES_ACTIVE=docker
ENV SERVER_PORT=8080

# Открытие порта
EXPOSE 8080

# Команда запуска
CMD ["java", "-jar", "app.jar"]

Инструкции Dockerfile

FROM — определяет базовый образ:

# Официальный OpenJDK образ
FROM openjdk:17-jdk-slim

# Многоступенчатая сборка
FROM maven:3.8-openjdk-17 AS build
FROM openjdk:17-jre-slim AS runtime

WORKDIR — устанавливает рабочую директорию:

WORKDIR /app
# Все последующие команды выполняются в /app

COPY vs ADD:

# COPY - простое копирование (рекомендуется)
COPY src/ /app/src/
COPY target/app.jar app.jar

# ADD - расширенное копирование (автоматическая распаковка архивов)
ADD https://example.com/file.tar.gz /tmp/
ADD archive.tar.gz /extracted/  # Автоматически распакует

RUN — выполнение команд при сборке:

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

# Создание пользователя
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Maven сборка
RUN mvn clean package -DskipTests

ENV — переменные окружения:

ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV DATABASE_URL="jdbc:postgresql://db:5432/myapp"
ENV SPRING_PROFILES_ACTIVE="docker"

EXPOSE — документирует порты:

EXPOSE 8080 8443  # HTTP и HTTPS порты
# Не открывает порты, только документирует

USER — переключение пользователя:

# Создание non-root пользователя для безопасности
RUN addgroup --system appuser && adduser --system --group appuser
USER appuser

CMD vs ENTRYPOINT:

# CMD - команда по умолчанию (можно переопределить)
CMD ["java", "-jar", "app.jar"]

# ENTRYPOINT - точка входа (нельзя переопределить)
ENTRYPOINT ["java", "-jar", "app.jar"]

# Комбинация ENTRYPOINT + CMD
ENTRYPOINT ["java"]
CMD ["-jar", "app.jar"]  # Можно переопределить параметры

Пояснение: CMD можно полностью заменить при запуске контейнера, ENTRYPOINT — нет. Комбинация позволяет фиксировать executable и делать параметры настраиваемыми.


Multi-stage Build

Зачем нужны многоступенчатые сборки

Проблемы single-stage builds:

  • Большой размер образа (build tools в production образе)
  • Security risks (компиляторы и исходники в production)
  • Сложность maintenance (все в одном образе)

Multi-stage build позволяет:

  • Использовать разные базовые образы для build и runtime
  • Копировать только необходимые артефакты
  • Значительно уменьшить размер финального образа

Maven/Gradle приложения

# === BUILD STAGE ===
FROM maven:3.8-openjdk-17-slim AS build

# Копируем pom.xml для dependency caching
COPY pom.xml .
RUN mvn dependency:go-offline -B

# Копируем исходники и собираем
COPY src src
RUN mvn clean package -DskipTests

# === RUNTIME STAGE ===
FROM openjdk:17-jre-slim AS runtime

# Создаем non-root пользователя
RUN addgroup --system appuser && adduser --system --group appuser

# Рабочая директория
WORKDIR /app

# Копируем только JAR из build stage
COPY --from=build target/*.jar app.jar

# Переключаемся на non-root пользователя
USER appuser

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

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

Node.js приложения

# === BUILD STAGE ===
FROM node:18-alpine AS build

WORKDIR /app

# Package files для dependency caching
COPY package*.json ./
RUN npm ci --only=production

# Исходники и сборка
COPY . .
RUN npm run build

# === RUNTIME STAGE ===
FROM node:18-alpine AS runtime

# Безопасность
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

WORKDIR /app

# Копируем production dependencies
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./package.json

USER nextjs

EXPOSE 3000

CMD ["npm", "start"]

Оптимизированная сборка с кешированием

# === DEPENDENCIES STAGE ===
FROM maven:3.8-openjdk-17-slim AS deps

WORKDIR /app
COPY pom.xml .

# Загружаем зависимости (кешируется если pom.xml не изменился)
RUN mvn dependency:go-offline -B

# === BUILD STAGE ===
FROM deps AS build

# Копируем исходники
COPY src src

# Сборка приложения
RUN mvn clean package -DskipTests -o  # -o = offline mode

# === TEST STAGE (опционально) ===
FROM build AS test
RUN mvn test

# === RUNTIME STAGE ===
FROM gcr.io/distroless/java17-debian11 AS runtime

WORKDIR /app

# Копируем только JAR файл
COPY --from=build /app/target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

Пояснение: Distroless образы содержат только runtime и ваше приложение, без OS utilities. Это максимально безопасно и минимально по размеру.


Оптимизация образов

Layer Caching Strategy

Принципы эффективного кеширования:

  1. Копируйте dependency files перед исходниками
  2. Располагайте наименее изменяемые инструкции вверху
  3. Группируйте связанные команды в одну RUN инструкцию
  4. Используйте .dockerignore для исключения ненужных файлов
# ❌ Плохо - пересборка всего при изменении кода
FROM maven:3.8-openjdk-17-slim
COPY . /app
WORKDIR /app
RUN mvn clean package

# ✅ Хорошо - кеширование зависимостей
FROM maven:3.8-openjdk-17-slim
WORKDIR /app

# 1. Копируем dependency файлы (изменяются редко)
COPY pom.xml .
RUN mvn dependency:go-offline -B

# 2. Копируем исходники (изменяются часто)
COPY src src
RUN mvn clean package -DskipTests -o

.dockerignore файл

# Версионирование
.git
.gitignore
.github

# IDE файлы
.idea
.vscode
*.iml
.settings
.project
.classpath

# Build артефакты
target/
build/
*.jar
*.war

# OS файлы
.DS_Store
Thumbs.db

# Logs
*.log
logs/

# Временные файлы
*.tmp
*.temp
*.cache

# Dependencies (для Node.js)
node_modules/
npm-debug.log

# Documentation
README.md
docs/
*.md

# Docker файлы
Dockerfile
docker-compose.yml
.dockerignore

# Environment файлы (безопасность)
.env
.env.local
secrets/

Пояснение: .dockerignore работает как .gitignore, но для Docker context. Уменьшает размер context и ускоряет сборку.

Выбор базовых образов

Размер образов по типам:

# Full OS images (избегайте в production)
ubuntu:20.04           # ~72MB
centos:8              # ~200MB

# Slim variants (хороший баланс)
openjdk:17-jdk-slim   # ~400MB
node:18-slim          # ~180MB
python:3.9-slim       # ~45MB

# Alpine variants (минимальный размер)
openjdk:17-jdk-alpine # ~350MB
node:18-alpine        # ~110MB
python:3.9-alpine     # ~25MB

# Distroless (максимальная безопасность)
gcr.io/distroless/java17  # ~250MB
gcr.io/distroless/nodejs  # ~150MB

Оптимизация слоев

# ❌ Плохо - много слоев
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

# ✅ Хорошо - один слой
RUN apt-get update && \
    apt-get install -y \
        curl \
        vim && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# ❌ Плохо - оставляет кеш в слое
RUN wget https://example.com/large-file.tar.gz && \
    tar -xzf large-file.tar.gz && \
    rm large-file.tar.gz  # Файл все еще в слое!

# ✅ Хорошо - очистка в том же слое
RUN wget https://example.com/large-file.tar.gz && \
    tar -xzf large-file.tar.gz && \
    rm large-file.tar.gz

Анализ размера образа

# Просмотр слоев образа
docker history myapp:latest

# Анализ размера с помощью dive
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive:latest myapp:latest

# Сравнение размеров
docker images | grep myapp

Docker Compose для локальной разработки

Что такое Docker Compose

Docker Compose — инструмент для определения и запуска multi-container Docker приложений. Использует YAML файл для конфигурации services, networks и volumes.

Преимущества для разработки:

  • Одна команда для запуска всех зависимостей
  • Изоляция окружений разработчиков
  • Consistent environment для команды
  • Easy integration testing

Базовая структура docker-compose.yml

version: '3.8'

services:
  app:
    build: .
    ports:

      - "8080:8080"
    environment:

      - SPRING_PROFILES_ACTIVE=docker
    depends_on:

      - db
      - redis
    networks:

      - app-network

  db:
    image: postgres:14-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:

      - "5432:5432"
    volumes:

      - postgres_data:/var/lib/postgresql/data
    networks:

      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

Полная инфраструктура для микросервисов

version: '3.8'

services:
  # === DATABASES ===
  postgres:
    image: postgres:14-alpine
    environment:
      POSTGRES_DB: userdb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:

      - "5432:5432"
    volumes:

      - postgres_data:/var/lib/postgresql/data
      - ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: orderdb
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:

      - "3306:3306"
    volumes:

      - mysql_data:/var/lib/mysql
      - ./docker/mysql/conf.d:/etc/mysql/conf.d
    command: --default-authentication-plugin=mysql_native_password

  # === MESSAGE BROKERS ===
  rabbitmq:
    image: rabbitmq:3.11-management-alpine
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin
    ports:

      - "5672:5672"   # AMQP port
      - "15672:15672" # Management UI
    volumes:

      - rabbitmq_data:/var/lib/rabbitmq

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:

      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    ports:

      - "9092:9092"

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  # === CACHING ===
  redis:
    image: redis:7-alpine
    ports:

      - "6379:6379"
    volumes:

      - redis_data:/data
    command: redis-server --appendonly yes --requirepass redis123

  # === MONITORING ===
  prometheus:
    image: prom/prometheus:latest
    ports:

      - "9090:9090"
    volumes:

      - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  grafana:
    image: grafana/grafana:latest
    ports:

      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: admin
    volumes:

      - grafana_data:/var/lib/grafana
      - ./docker/grafana/dashboards:/etc/grafana/provisioning/dashboards

  # === SEARCH ===
  elasticsearch:
    image: elasticsearch:8.8.0
    environment:

      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:

      - "9200:9200"
    volumes:

      - elasticsearch_data:/usr/share/elasticsearch/data

  # === TESTING TOOLS ===
  wiremock:
    image: wiremock/wiremock:latest
    ports:

      - "8090:8080"
    volumes:

      - ./docker/wiremock/mappings:/home/wiremock/mappings
      - ./docker/wiremock/__files:/home/wiremock/__files

volumes:
  postgres_data:
  mysql_data:
  rabbitmq_data:
  redis_data:
  prometheus_data:
  grafana_data:
  elasticsearch_data:

networks:
  default:
    name: microservices-network

Profiles для разных окружений

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    profiles: ["app"]
    depends_on:

      - db

  db:
    image: postgres:14-alpine
    # Всегда включен (без profile)
    
  redis:
    image: redis:7-alpine
    profiles: ["cache"]

  monitoring:
    image: prometheus:latest
    profiles: ["monitoring"]

# Запуск разных комбинаций
# docker-compose up                    # Только db
# docker-compose --profile app up      # db + app  
# docker-compose --profile cache up    # db + redis
# docker-compose --profile monitoring up # db + monitoring

Environment-specific конфигурации

# docker-compose.override.yml (автоматически применяется)
version: '3.8'

services:
  app:
    environment:

      - DEBUG=true
      - LOG_LEVEL=debug
    volumes:

      - .:/app  # Live reload для разработки

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: myapp:latest  # Pre-built образ
    restart: unless-stopped
    environment:

      - SPRING_PROFILES_ACTIVE=production

# Запуск с specific файлом
# docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

Управление секретами

Переменные окружения (ENV)

# В Dockerfile
ENV DATABASE_PASSWORD=default_password
ENV API_KEY=placeholder
# В docker-compose.yml
services:
  app:
    environment:

      - DATABASE_PASSWORD=${DB_PASSWORD:-default}
      - API_KEY=${API_KEY}
    env_file:

      - .env
      - .env.local
# .env файл
DB_PASSWORD=secret123
API_KEY=abc123def456
SPRING_PROFILES_ACTIVE=docker

Проблемы ENV подхода:

  • Переменные видны через docker inspect
  • Могут попасть в logs
  • Сложно ротировать
  • Нет encryption at rest

Docker Secrets (Swarm mode)

# docker-compose.yml для Docker Swarm
version: '3.8'

services:
  app:
    image: myapp:latest
    secrets:

      - db_password
      - api_key
    environment:

      - DATABASE_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    external: true  # Управляется через Docker CLI

# Создание secret
# echo "my_secret_password" | docker secret create db_password -
// Чтение secret в приложении
@Component
public class SecretManager {
    
    public String getSecret(String secretName) {
        try {
            Path secretPath = Paths.get("/run/secrets/" + secretName);
            return Files.readString(secretPath).trim();
        } catch (IOException e) {
            throw new RuntimeException("Failed to read secret: " + secretName, e);
        }
    }
}

Volumes для секретов

services:
  app:
    volumes:

      - secrets_volume:/app/secrets:ro  # Read-only mount
    environment:

      - SECRETS_PATH=/app/secrets

volumes:
  secrets_volume:
    driver: local
    driver_opts:
      type: tmpfs  # В памяти, не на диске
      device: tmpfs
      o: size=100m,uid=1000
// Чтение секретов из volume
@ConfigurationProperties(prefix = "app")
public class AppConfig {
    
    @Value("${secrets.path:/app/secrets}")
    private String secretsPath;
    
    @PostConstruct
    public void loadSecrets() {
        try {
            Path dbPasswordFile = Paths.get(secretsPath, "db_password");
            if (Files.exists(dbPasswordFile)) {
                String password = Files.readString(dbPasswordFile).trim();
                System.setProperty("spring.datasource.password", password);
            }
        } catch (IOException e) {
            log.error("Failed to load secrets", e);
        }
    }
}

Интеграция с HashiCorp Vault

# docker-compose.yml с Vault
services:
  vault:
    image: vault:latest
    ports:

      - "8200:8200"
    environment:
      VAULT_DEV_ROOT_TOKEN_ID: myroot
      VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
    cap_add:

      - IPC_LOCK

  app:
    depends_on:

      - vault
    environment:

      - VAULT_ADDR=http://vault:8200
      - VAULT_TOKEN=${VAULT_TOKEN}
// Spring Cloud Vault интеграция
@Configuration
@VaultPropertySource("secret/myapp")
public class VaultConfig {
    
    @Bean
    public VaultTemplate vaultTemplate() {
        VaultEndpoint endpoint = new VaultEndpoint();
        endpoint.setHost("vault");
        endpoint.setPort(8200);
        endpoint.setScheme("http");
        
        TokenAuthentication auth = new TokenAuthentication(vaultToken);
        
        return new VaultTemplate(endpoint, auth);
    }
}

Init containers для секретов

services:
  secret-fetcher:
    image: vault:latest
    command: |
      sh -c "
        vault auth -method=aws
        vault read -field=password secret/db > /shared/db_password
        vault read -field=key secret/api > /shared/api_key
      "
    volumes:

      - shared_secrets:/shared
    environment:

      - VAULT_ADDR=https://vault.company.com

  app:
    depends_on:

      - secret-fetcher
    volumes:

      - shared_secrets:/app/secrets:ro

volumes:
  shared_secrets:
    driver: tmpfs

Пояснение: Init containers загружают секреты до запуска основного приложения. Секреты остаются в shared volume только в памяти.


Безопасность контейнеров

Non-root пользователи

# Создание dedicated пользователя
FROM openjdk:17-jre-slim

# Создаем группу и пользователя
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Создаем директории с правильными permissions
RUN mkdir -p /app/data && chown -R appuser:appuser /app

WORKDIR /app

# Копируем файлы под root
COPY --chown=appuser:appuser target/app.jar app.jar

# Переключаемся на non-root пользователя
USER appuser

CMD ["java", "-jar", "app.jar"]

Security scanning

# .github/workflows/security.yml
name: Container Security Scan

on: [push, pull_request]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@v2
      
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
      
      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v1
        with:
          sarif_file: 'trivy-results.sarif'

Runtime security

# docker-compose.yml с security constraints
services:
  app:
    image: myapp:latest
    security_opt:

      - no-new-privileges:true  # Запрет privilege escalation
    cap_drop:

      - ALL                     # Убираем все capabilities
    cap_add:

      - NET_BIND_SERVICE        # Добавляем только необходимые
    read_only: true             # Read-only root filesystem
    tmpfs:

      - /tmp                    # Writable tmpfs для временных файлов
      - /var/cache
    ulimits:
      memlock: -1
      nofile: 65536

Практические команды

Основные Docker команды

# === ОБРАЗЫ ===
# Сборка образа
docker build -t myapp:latest .
docker build -t myapp:v1.0 --target production .  # Multi-stage

# Просмотр образов
docker images
docker image ls --filter "reference=myapp*"

# Удаление образов
docker rmi myapp:latest
docker image prune  # Удаление неиспользуемых образов

# === КОНТЕЙНЕРЫ ===
# Запуск контейнера
docker run -d --name myapp -p 8080:8080 myapp:latest
docker run -it --rm myapp:latest bash  # Интерактивный режим

# Просмотр контейнеров
docker ps          # Запущенные
docker ps -a       # Все

# Логи контейнера
docker logs myapp
docker logs -f myapp  # Follow режим

# Выполнение команд в контейнере
docker exec -it myapp bash
docker exec myapp cat /app/config.properties

# Остановка и удаление
docker stop myapp
docker rm myapp
docker container prune  # Удаление остановленных контейнеров

# === СИСТЕМНЫЕ КОМАНДЫ ===
# Использование дискового пространства
docker system df

# Очистка всего неиспользуемого
docker system prune -a --volumes

# Информация о Docker
docker info
docker version

Docker Compose команды

# === УПРАВЛЕНИЕ SERVICES ===
# Запуск всех services
docker-compose up
docker-compose up -d  # Detached режим

# Запуск specific services
docker-compose up db redis
docker-compose up --scale app=3  # Масштабирование

# Остановка
docker-compose stop
docker-compose down          # Остановка и удаление
docker-compose down -v       # + удаление volumes

# === СБОРКА ===
# Пересборка образов
docker-compose build
docker-compose up --build    # Сборка и запуск

# Сборка без cache
docker-compose build --no-cache

# === ЛОГИ И МОНИТОРИНГ ===
# Просмотр логов
docker-compose logs
docker-compose logs -f app   # Follow конкретного service

# Статус services
docker-compose ps
docker-compose top           # Процессы в контейнерах

# === ВЫПОЛНЕНИЕ КОМАНД ===
# Выполнение команды в service
docker-compose exec app bash
docker-compose exec db psql -U postgres

# Одноразовый контейнер
docker-compose run --rm app bash

Production deployment

# === CI/CD PIPELINE ===
# Сборка для production
docker build -t myapp:${BUILD_NUMBER} \
  --target production \
  --build-arg VERSION=${BUILD_NUMBER} .

# Тегирование
docker tag myapp:${BUILD_NUMBER} myapp:latest
docker tag myapp:${BUILD_NUMBER} registry.company.com/myapp:${BUILD_NUMBER}

# Push в registry
docker push registry.company.com/myapp:${BUILD_NUMBER}
docker push registry.company.com/myapp:latest

# === DEPLOYMENT ===
# Pull на production сервере
docker pull registry.company.com/myapp:${BUILD_NUMBER}

# Rolling update
docker service update --image registry.company.com/myapp:${BUILD_NUMBER

Kubernetes

Что такое Kubernetes

Kubernetes (K8s) — платформа оркестрации контейнеров, которая автоматизирует развертывание, масштабирование и управление приложениями. Решает ключевые проблемы:

  • Service Discovery — как сервисы находят друг друга
  • Load Balancing — распределение нагрузки между репликами
  • Auto-scaling — автоматическое масштабирование под нагрузку
  • Self-healing — автоматическое восстановление при сбоях
  • Rolling Updates — обновления без простоя

Основная идея: Вы описываете желаемое состояние (3 реплики приложения), а Kubernetes поддерживает его автоматически.


Pod — базовый строительный блок

Pod — минимальная единица развертывания. Содержит один или несколько контейнеров, которые:

  • Делят общую сеть (один IP)
  • Делят общие volumes
  • Всегда запускаются на одной машине
  • Масштабируются как единое целое
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    app: backend
spec:
  containers:

  - name: app
    image: myapp:1.0
    ports:

    - containerPort: 8080
    env:

    - name: ENV
      value: "production"

Когда использовать Pod напрямую: Почти никогда. Pods смертны — при падении Node они исчезают навсегда. Используйте Deployment.


Deployment — управление репликами

Deployment — контроллер, который создает и управляет репликами Pod'ов. Обеспечивает:

  • Desired state — нужное количество реплик
  • Rolling updates — плавные обновления без простоя
  • Rollback — откат к предыдущей версии
  • Self-healing — пересоздание упавших Pod'ов
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3  # Желаемое количество реплик
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:

      - name: app
        image: myapp:1.0
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi" 
            cpu: "500m"

Как работает Rolling Update: Создаются новые Pod'ы с новой версией, затем постепенно удаляются старые. Если что-то идет не так — автоматический откат.

Команды:

kubectl apply -f deployment.yaml
kubectl scale deployment my-app --replicas=5
kubectl set image deployment/my-app app=myapp:2.0
kubectl rollout undo deployment/my-app

Service — сетевой доступ

Service — абстракция для доступа к группе Pod'ов. Решает проблемы:

  • Pod'ы имеют динамические IP, Service дает постоянный
  • Load balancing между репликами
  • Service discovery через DNS

Типы Service:

  • ClusterIP — доступ только внутри кластера
  • NodePort — доступ через порт на каждой машине
  • LoadBalancer — внешний балансировщик (в облаке)
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP
  selector:
    app: backend  # Какие Pod'ы включить
  ports:

  - port: 80        # Порт Service
    targetPort: 8080 # Порт в Pod

Как это работает: Service автоматически находит все Pod'ы с label app: backend и балансирует трафик между ними. Если Pod умирает — исключается из балансировки.

DNS: Внутри кластера сервис доступен по имени my-app-service или полному имени my-app-service.default.svc.cluster.local.


ConfigMap и Secret — конфигурация

ConfigMap — хранит конфигурационные данные (не секретные):

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database.host: "postgres.example.com"
  log.level: "INFO"
  app.properties: |
    server.port=8080
    spring.profiles.active=prod

Secret — хранит секретные данные (пароли, ключи):

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:  # Автоматически кодируется в base64
  database.password: "secret123"
  api.key: "abc-def-ghi"

Использование в Pod:

spec:
  containers:

  - name: app
    image: myapp:1.0
    env:
    # Переменная из ConfigMap
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database.host
    # Переменная из Secret
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: database.password
    # Все переменные сразу
    envFrom:

    - configMapRef:
        name: app-config

Зачем разделять: ConfigMap можно посмотреть всем, Secret — только с правами. Secret автоматически кодируется и может монтироваться как файлы с ограниченными правами.


HorizontalPodAutoscaler — автомасштабирование

HPA автоматически изменяет количество реплик в зависимости от нагрузки:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:

  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # При 70% CPU добавлять реплики

Как работает: HPA каждые 15 секунд проверяет метрики. Если среднее потребление CPU выше 70% — добавляет реплики. Если ниже — убирает (но медленно, чтобы избежать flapping).

Требования:

  • Установленный Metrics Server в кластере
  • Обязательно указанные resources.requests в Deployment

Создание через kubectl:

kubectl autoscale deployment my-app --cpu-percent=70 --min=2 --max=10

Health Checks — проверки здоровья

Kubernetes может автоматически проверять состояние приложений:

Liveness Probe — жив ли контейнер? При неудаче — перезапускает. Readiness Probe — готов ли принимать трафик? При неудаче — исключает из Service.

spec:
  containers:

  - name: app
    image: myapp:1.0
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30  # Ждать 30 сек после запуска
      periodSeconds: 10        # Проверять каждые 10 сек
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

Практические советы:

  • Liveness проверяет внутреннее здоровье (память, deadlock)
  • Readiness проверяет готовность (подключение к БД, кеш)
  • Не делайте проверки слишком агрессивными — можете создать cascade failures

Другие типы проб:

# TCP проверка
livenessProbe:
  tcpSocket:
    port: 8080

# Выполнение команды
livenessProbe:
  exec:
    command:

    - cat
    - /tmp/healthy

Ingress — внешний доступ

Ingress — правила маршрутизации внешнего HTTP/HTTPS трафика к сервисам внутри кластера. Позволяет:

  • Использовать доменные имена вместо IP
  • SSL termination
  • Path-based и host-based routing

Ingress Controller — компонент, который реализует правила (NGINX, Traefik, HAProxy).

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:

  - host: myapp.example.com
    http:
      paths:

      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-service
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

NGINX Ingress популярные аннотации:

annotations:
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
  nginx.ingress.kubernetes.io/rate-limit: "100"
  nginx.ingress.kubernetes.io/proxy-body-size: "10m"
  cert-manager.io/cluster-issuer: "letsencrypt"

Traefik Ingress (использует CRD):

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-app-route
spec:
  entryPoints:

  - web
  routes:

  - match: Host(`myapp.example.com`)
    kind: Rule
    services:

    - name: my-app-service
      port: 80

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

Helm — инструмент для упаковки Kubernetes приложений в Charts. Решает проблемы:

  • Множество YAML файлов сложно управлять
  • Разные конфигурации для dev/stage/prod
  • Версioning и откаты приложений
  • Зависимости между компонентами

Chart структура:

my-app/
├── Chart.yaml        # Метаданные
├── values.yaml       # Конфигурация по умолчанию
└── templates/        # Kubernetes манифесты с переменными
    ├── deployment.yaml
    ├── service.yaml
    └── ingress.yaml

values.yaml — конфигурационные значения:

replicaCount: 1
image:
  repository: myapp
  tag: latest
service:
  type: ClusterIP
  port: 80
ingress:
  enabled: false
  host: myapp.local

templates/deployment.yaml — шаблон с переменными:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:

      - name: app
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        ports:

        - containerPort: 8080

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

# Установка
helm install my-release ./my-app

# Обновление
helm upgrade my-release ./my-app

# Установка с кастомными значениями
helm install my-release ./my-app -f production-values.yaml

# Откат
helm rollback my-release 1

production-values.yaml для разных окружений:

replicaCount: 3
image:
  tag: v1.2.0
ingress:
  enabled: true
  host: myapp.company.com
  tls: true

Переменные окружения

Способы передачи конфигурации в Pod:

  1. Прямые переменные:
env:

- name: ENV
  value: "production"
  1. Из ConfigMap:
env:

- name: DB_HOST
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: database.host
  1. Все ключи из ConfigMap:
envFrom:

- configMapRef:
    name: app-config
  1. Метаданные Pod через Downward API:
env:

- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name
- name: POD_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP

Приоритет: Прямые переменные переопределяют ConfigMap, который переопределяет Dockerfile ENV.


Логирование

Принципы логирования в Kubernetes:

  • Приложения пишут в stdout/stderr
  • Kubernetes собирает логи автоматически
  • Централизованная обработка через DaemonSet

Просмотр логов:

kubectl logs pod-name
kubectl logs -f deployment/my-app  # Follow логи
kubectl logs --previous pod-name   # Логи предыдущего контейнера

Структурированные логи (JSON формат):

# ConfigMap для logback
data:
  logback.xml: |
    <configuration>
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
          <providers>
            <timestamp/>
            <logLevel/>
            <message/>
            <mdc/>
          </providers>
        </encoder>
      </appender>
    </configuration>

Centralized logging с Fluentd:

apiVersion: apps/v1
kind: DaemonSet  # Запускается на каждом Node
metadata:
  name: fluentd
spec:
  template:
    spec:
      containers:

      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset
        volumeMounts:

        - name: varlog
          mountPath: /var/log
        - name: containers
          mountPath: /var/lib/docker/containers
      volumes:

      - name: varlog
        hostPath:
          path: /var/log

Мониторинг

Prometheus + Grafana — стандартный стек мониторинга в Kubernetes.

Автоматические метрики:

  • Kubernetes предоставляет метрики Pod'ов, Node'ов, ресурсов
  • Applications exposing метрики на /metrics endpoint

Spring Boot Actuator:

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    prometheus:
      enabled: true

ServiceMonitor для автоматического scraping:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
spec:
  selector:
    matchLabels:
      app: backend
  endpoints:

  - port: http
    path: /actuator/prometheus

Алерты в PrometheusRule:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: my-app-alerts
spec:
  groups:

  - name: my-app
    rules:

    - alert: HighErrorRate
      expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
      for: 5m
      annotations:
        summary: "High error rate detected"

Grafana Dashboard — визуализация метрик:

  • CPU/Memory usage по Pod'ам
  • Request rate и error rate
  • Response time percentiles
  • Business метрики

Практические команды

Основные операции:

# Применение конфигурации
kubectl apply -f deployment.yaml
kubectl apply -f ./manifests/

# Просмотр ресурсов
kubectl get pods
kubectl get services
kubectl describe pod my-app

# Логи и отладка
kubectl logs -f deployment/my-app
kubectl exec -it pod-name -- bash

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

# Port forwarding для отладки
kubectl port-forward service/my-app 8080:80

Отладка проблем:

# Состояние кластера
kubectl get nodes
kubectl top nodes
kubectl top pods

# События
kubectl get events --sort-by=.metadata.creationTimestamp

# Проблемы с Pod'ами
kubectl describe pod problematic-pod
kubectl logs problematic-pod --previous

Best Practices

Resource Management:

  • Всегда указывайте resources.requests и limits
  • Requests — гарантированные ресурсы
  • Limits — максимальные ресурсы
  • HPA работает на основе requests

Security:

  • Используйте non-root пользователей
  • ReadOnlyRootFilesystem где возможно
  • Network Policies для изоляции
  • RBAC для ограничения доступа

High Availability:

  • Минимум 2 реплики для production
  • PodDisruptionBudget для критичных сервисов
  • Anti-affinity для распределения по Node'ам
  • Health checks обязательны

Мониторинг:

  • Логируйте в structured формате (JSON)
  • Exposing метрики для Prometheus
  • Алерты на критичные проблемы
  • Dashboard'ы для observability

Заключение

Kubernetes решает ключевые проблемы современных приложений:

  • Автоматическое масштабирование под нагрузку
  • Self-healing при сбоях
  • Zero-downtime deployments
  • Service discovery и load balancing
  • Consistent окружения от dev до prod

Начинайте с простого: Pod → Deployment → Service → Ingress. Добавляйте complexity по мере необходимости.

Главный принцип: Описывайте желаемое состояние, Kubernetes поддерживает его автоматически. Это переход от "как развернуть" к "что развернуть".

CI/CD пайплайны

Что такое CI/CD

Continuous Integration (CI) — практика частого слияния изменений кода в общую ветку с автоматической проверкой качества. Решает проблемы:

  • Конфликты при слиянии больших изменений
  • Поздняя отладка проблем
  • Несовместимость между версиями
  • Ручная проверка качества кода

Continuous Deployment (CD) — автоматическое развертывание проверенного кода в production. Обеспечивает:

  • Быстрая доставка фич пользователям
  • Уменьшение рисков через маленькие изменения
  • Откат к предыдущей версии за секунды
  • Consistent deployment process

Pipeline — последовательность автоматизированных шагов от commit'а до production'а. Типичный pipeline:

  1. Source — получение кода из Git
  2. Build — компиляция и сборка
  3. Test — unit, integration, e2e тесты
  4. Security — проверка уязвимостей
  5. Package — создание Docker образа
  6. Deploy — развертывание в окружения

Jenkins — классический CI/CD сервер

Jenkins — open-source автоматизационный сервер с огромной экосистемой плагинов. Основан на концепции Jobs (задач) и Pipelines.

Jenkinsfile — Pipeline as Code

pipeline {
    agent any
    
    tools {
        maven 'Maven-3.8'
        jdk 'OpenJDK-17'
    }
    
    environment {
        DOCKER_REGISTRY = 'registry.company.com'
        APP_NAME = 'myapp'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
        
        stage('Test') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'mvn test'
                    }
                    post {
                        always {
                            publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
                        }
                    }
                }
                stage('Lint') {
                    steps {
                        sh 'mvn checkstyle:check'
                    }
                }
            }
        }
        
        stage('Package') {
            steps {
                sh 'mvn package -DskipTests'
                archiveArtifacts artifacts: 'target/*.jar'
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    def image = docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER}")
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Staging') {
            when {
                branch 'develop'
            }
            steps {
                sh '''
                    kubectl set image deployment/myapp-staging \
                        app=${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER} \
                        --namespace=staging
                '''
            }
        }
        
        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            steps {
                input message: 'Deploy to production?', ok: 'Deploy'
                sh '''
                    kubectl set image deployment/myapp-prod \
                        app=${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER} \
                        --namespace=production
                '''
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            slack channel: '#deployments', 
                  message: "✅ ${env.JOB_NAME} - ${env.BUILD_NUMBER} deployed successfully"
        }
        failure {
            slack channel: '#alerts',
                  message: "❌ ${env.JOB_NAME} - ${env.BUILD_NUMBER} failed"
        }
    }
}

Ключевые концепции Jenkins:

  • Agent — где выполняется pipeline (master, slave nodes, docker)
  • Tools — настроенные инструменты (Maven, JDK, Node.js)
  • Environment — переменные окружения для pipeline
  • Stages — логические группы шагов
  • Parallel — параллельное выполнение шагов
  • When — условное выполнение stages
  • Post — действия после завершения (cleanup, notifications)

Parallel execution значительно ускоряет pipeline — unit tests и lint могут выполняться одновременно.

Jenkins Master-Slave архитектура

Master Node — центральный сервер, который:

  • Управляет web UI и API
  • Хранит конфигурацию jobs
  • Планирует выполнение builds
  • Распределяет нагрузку по slave nodes

Slave Nodes (Agents) — рабочие машины, которые:

  • Выполняют builds
  • Могут иметь специфичную конфигурацию (OS, tools)
  • Подключаются к master через SSH, JNLP или Docker
pipeline {
    agent {
        label 'linux && docker'  // Выполнить на slave с такими labels
    }
    // или
    agent {
        docker {
            image 'maven:3.8-openjdk-17'
            args '-v $HOME/.m2:/root/.m2'  // Maven cache
        }
    }
}

GitLab CI — современный встроенный CI/CD

GitLab CI — встроенная в GitLab система CI/CD с YAML-конфигурацией. Преимущества:

  • Интеграция с Git из коробки
  • Современный Docker-first подход
  • Встроенный Container Registry
  • Kubernetes integration

.gitlab-ci.yml структура

# Глобальные настройки
image: maven:3.8-openjdk-17

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
  DOCKER_REGISTRY: registry.gitlab.com/$CI_PROJECT_PATH

# Кеширование зависимостей
cache:
  paths:

    - .m2/repository/

stages:

  - build
  - test
  - security
  - package
  - deploy

# Jobs
build:
  stage: build
  script:

    - mvn clean compile
  artifacts:
    paths:

      - target/classes/
    expire_in: 1 hour

# Параллельные тесты
unit_tests:
  stage: test
  script:

    - mvn test
  artifacts:
    reports:
      junit: target/surefire-reports/TEST-*.xml
    paths:

      - target/surefire-reports/
  coverage: '/Total.*?([0-9]{1,3})%/'

integration_tests:
  stage: test
  script:

    - mvn verify -Dskip.unit.tests=true
  services:

    - postgres:13
    - redis:6-alpine
  variables:
    POSTGRES_DB: testdb
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test

lint:
  stage: test
  script:

    - mvn checkstyle:check
    - mvn spotbugs:check
  artifacts:
    reports:
      codequality: target/checkstyle-result.xml

# Security scanning
security_scan:
  stage: security
  script:

    - mvn dependency-check:check
  artifacts:
    reports:
      dependency_scanning: target/dependency-check-report.json
  allow_failure: true

# Docker образ
docker_build:
  stage: package
  image: docker:latest
  services:

    - docker:dind
  before_script:

    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:

    - docker build -t $DOCKER_REGISTRY:$CI_COMMIT_SHA .
    - docker push $DOCKER_REGISTRY:$CI_COMMIT_SHA
    - docker tag $DOCKER_REGISTRY:$CI_COMMIT_SHA $DOCKER_REGISTRY:latest
    - docker push $DOCKER_REGISTRY:latest
  only:

    - main
    - develop

# Deployment environments
deploy_staging:
  stage: deploy
  script:

    - kubectl set image deployment/myapp app=$DOCKER_REGISTRY:$CI_COMMIT_SHA
  environment:
    name: staging
    url: https://staging.myapp.com
  only:

    - develop

deploy_production:
  stage: deploy
  script:

    - kubectl set image deployment/myapp app=$DOCKER_REGISTRY:$CI_COMMIT_SHA
  environment:
    name: production
    url: https://myapp.com
  when: manual  # Ручное подтверждение
  only:

    - main

Ключевые концепции GitLab CI:

  • Image — Docker образ для выполнения job'а
  • Services — дополнительные контейнеры (БД, кеш) для job'а
  • Artifacts — файлы, передаваемые между stages
  • Cache — ускорение через сохранение зависимостей
  • Environment — track deployments в разные окружения
  • Only/Except — условия выполнения job'ов

Runners — исполнители pipeline'ов

GitLab Runners

GitLab Runner — агент, который выполняет CI/CD jobs. Типы:

Shared Runners — предоставляются GitLab.com:

  • Бесплатные для open source
  • Ограниченные ресурсы и время
  • Подходят для простых проектов

Group/Project Runners — ваши собственные:

  • Полный контроль над ресурсами
  • Могут подключаться к внутренним сервисам
  • Различные executors (Docker, Shell, Kubernetes)
# Установка GitLab Runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner

# Регистрация runner'а
sudo gitlab-runner register \
  --url https://gitlab.com/ \
  --registration-token PROJECT_TOKEN \
  --description "Docker Runner" \
  --executor docker \
  --docker-image alpine:latest

Docker Executor — каждый job выполняется в отдельном контейнере:

# .gitlab-ci.yml
job:
  image: node:16
  script:

    - npm install
    - npm test

Kubernetes Executor — jobs выполняются в Kubernetes pods:

# config.toml
[[runners]]
  name = "kubernetes-runner"
  executor = "kubernetes"
  [runners.kubernetes]
    image = "alpine:latest"
    namespace = "gitlab-runner"
    cpu_request = "100m"
    memory_request = "128Mi"

Jenkins Agents

Jenkins Agent подключается к master различными способами:

SSH Agent — подключение через SSH:

pipeline {
    agent {
        label 'linux-ssh'
    }
}

Docker Agent — temporary контейнер для job'а:

pipeline {
    agent {
        docker {
            image 'maven:3.8-openjdk-17'
            args '-v /tmp:/tmp'
        }
    }
}

Kubernetes Agent — dynamic pods в Kubernetes:

pipeline {
    agent {
        kubernetes {
            yaml '''
                apiVersion: v1
                kind: Pod
                spec:
                  containers:

                  - name: maven
                    image: maven:3.8-openjdk-17
                    command: [cat]
                    tty: true
            '''
        }
    }
}

Триггеры pipeline'ов

GitLab CI Triggers

Push triggers — автоматический запуск при push:

job:
  script: echo "Run on every push"
  only:

    - main
    - develop
    - /^feature\/.*$/  # Regex для feature веток

Merge Request triggers:

mr_job:
  script: echo "Run on MR"
  only:

    - merge_requests

Scheduled triggers — по расписанию:

nightly_job:
  script: ./run-nightly-tests.sh
  only:

    - schedules

Manual triggers:

deploy_prod:
  script: ./deploy-prod.sh
  when: manual
  only:

    - main

External API triggers:

curl -X POST \
  -F token=TOKEN \
  -F ref=main \
  https://gitlab.com/api/v4/projects/PROJECT_ID/trigger/pipeline

Jenkins Triggers

SCM Polling — проверка изменений в репозитории:

pipeline {
    triggers {
        pollSCM('H/5 * * * *')  // Каждые 5 минут
    }
}

Webhook triggers — мгновенная реакция на push:

pipeline {
    triggers {
        githubPush()
    }
}

Cron triggers — по расписанию:

pipeline {
    triggers {
        cron('0 2 * * 1-5')  // 2 AM в будние дни
    }
}

Upstream/Downstream — запуск после других jobs:

pipeline {
    triggers {
        upstream(upstreamProjects: 'upstream-job', threshold: hudson.model.Result.SUCCESS)
    }
}

Артефакты и кеширование

GitLab Artifacts

Артефакты — файлы, сохраняемые после job'а и доступные в UI или следующих stages:

build:
  script:

    - mvn package
  artifacts:
    paths:

      - target/*.jar           # JAR файлы
      - target/site/           # Документация
    reports:
      junit: target/surefire-reports/TEST-*.xml
      coverage: target/site/jacoco/jacoco.xml
    expire_in: 1 week          # Автоматическое удаление
    when: always               # Сохранять даже при неудаче

test:
  script:

    - java -jar target/app.jar --test
  dependencies:

    - build                    # Скачать артефакты от build

Cache vs Artifacts:

  • Cache — ускорение between pipeline runs (dependencies)
  • Artifacts — передача данных between jobs within pipeline

Maven/Gradle кеширование

Maven cache:

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"

cache:
  key: ${CI_COMMIT_REF_SLUG}  # Разный cache для разных веток
  paths:

    - .m2/repository/

build:
  script:

    - mvn dependency:go-offline  # Загрузка зависимостей
    - mvn compile

Gradle cache:

variables:
  GRADLE_OPTS: "-Dorg.gradle.daemon=false"
  GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:

    - .gradle/wrapper
    - .gradle/caches

build:
  script:

    - ./gradlew --build-cache build

Multi-level caching strategy:

cache:

  - key: global-cache
    paths:

      - .m2/repository/
    policy: pull        # Только чтение
  - key: ${CI_COMMIT_REF_SLUG}
    paths:

      - target/
    policy: pull-push   # Чтение и запись

Jenkins Artifacts

pipeline {
    stages {
        stage('Build') {
            steps {
                sh 'mvn package'
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
                publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
            }
        }
        
        stage('Deploy') {
            steps {
                copyArtifacts projectName: 'upstream-job', buildNumber: '${BUILD_NUMBER}'
                sh 'deploy.sh target/app.jar'
            }
        }
    }
}

Типичный Java pipeline

Complete pipeline с всеми проверками

# GitLab CI для Java приложения
image: maven:3.8-openjdk-17

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
  SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"

cache:
  paths:

    - .m2/repository/
    - .sonar/cache/

stages:

  - validate
  - build
  - test
  - quality
  - security
  - package
  - deploy

# Предварительная проверка
validate:
  stage: validate
  script:

    - mvn validate
    - mvn dependency:analyze
  only:

    - merge_requests
    - main
    - develop

# Компиляция
compile:
  stage: build
  script:

    - mvn clean compile test-compile
  artifacts:
    paths:

      - target/classes/
      - target/test-classes/
    expire_in: 1 hour

# Параллельное выполнение тестов и проверок
unit_tests:
  stage: test
  script:

    - mvn test
  artifacts:
    reports:
      junit: target/surefire-reports/TEST-*.xml
      coverage: target/site/jacoco/jacoco.xml
    paths:

      - target/surefire-reports/
  coverage: '/Total.*?([0-9]{1,3})%/'

integration_tests:
  stage: test
  script:

    - mvn verify -Dskip.unit.tests=true
  services:

    - postgres:13
    - redis:6-alpine
  variables:
    POSTGRES_DB: testdb
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
    SPRING_PROFILES_ACTIVE: integration-test

# Статический анализ кода
checkstyle:
  stage: quality
  script:

    - mvn checkstyle:check
  artifacts:
    reports:
      codequality: target/checkstyle-result.xml

spotbugs:
  stage: quality
  script:

    - mvn spotbugs:check
  artifacts:
    paths:

      - target/spotbugsXml.xml

sonarqube:
  stage: quality
  script:

    - mvn sonar:sonar
      -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG
      -Dsonar.host.url=$SONAR_HOST_URL
      -Dsonar.login=$SONAR_TOKEN
  only:

    - main
    - develop
    - merge_requests

# Security checks
dependency_check:
  stage: security
  script:

    - mvn dependency-check:check
  artifacts:
    reports:
      dependency_scanning: target/dependency-check-report.json
  allow_failure: true

# Сборка финального артефакта
package:
  stage: package
  script:

    - mvn package -DskipTests
  artifacts:
    paths:

      - target/*.jar
    expire_in: 1 day

# Docker image
docker_build:
  stage: package
  image: docker:latest
  services:

    - docker:dind
  script:

    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:

    - main
    - develop

Работа с секретами

Environment Variables

GitLab CI Variables — через UI или API:

deploy:
  script:

    - echo "Deploying to $DEPLOY_URL"
    - curl -H "Authorization: Bearer $API_TOKEN" $DEPLOY_URL
  variables:
    DEPLOY_URL: "https://api.example.com"  # Public variable
    # API_TOKEN устанавливается через GitLab UI как protected

Jenkins Credentials:

pipeline {
    environment {
        API_TOKEN = credentials('api-token-id')
        DB_PASSWORD = credentials('db-password')
    }
    stages {
        stage('Deploy') {
            steps {
                sh 'curl -H "Authorization: Bearer $API_TOKEN" ...'
            }
        }
    }
}

File-based secrets

GitLab File Variables:

deploy:
  script:

    - gcloud auth activate-service-account --key-file $GOOGLE_CREDENTIALS
    - kubectl apply -f deployment.yaml
  # GOOGLE_CREDENTIALS - file variable с JSON ключом

Jenkins Secret Files:

pipeline {
    stages {
        stage('Deploy') {
            steps {
                withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                    sh 'kubectl apply -f deployment.yaml'
                }
            }
        }
    }
}

HashiCorp Vault Integration

GitLab + Vault:

variables:
  VAULT_ADDR: "https://vault.company.com"

before_script:

  - export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=gitlab jwt=$CI_JOB_JWT)
  - export DB_PASSWORD=$(vault kv get -field=password secret/myapp/db)

deploy:
  script:

    - deploy.sh --db-password="$DB_PASSWORD"

Jenkins + Vault Plugin:

pipeline {
    stages {
        stage('Deploy') {
            steps {
                withVault([configuration: [vaultUrl: 'https://vault.company.com',
                                         vaultCredentialId: 'vault-token'],
                          vaultSecrets: [[path: 'secret/myapp', secretValues: [
                              [envVar: 'DB_PASSWORD', vaultKey: 'password'],
                              [envVar: 'API_KEY', vaultKey: 'api_key']]]]]) {
                    sh 'deploy.sh'
                }
            }
        }
    }
}

External Secret Management

AWS Secrets Manager:

before_script:

  - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
  - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
  - export DB_PASSWORD=$(aws secretsmanager get-secret-value --secret-id prod/myapp/db --query SecretString --output text)

deploy:
  script:

    - docker run -e DATABASE_PASSWORD="$DB_PASSWORD" myapp:latest

Azure Key Vault:

before_script:

  - az login --service-principal -u $AZURE_CLIENT_ID -p $AZURE_CLIENT_SECRET -t $AZURE_TENANT_ID
  - export API_KEY=$(az keyvault secret show --name api-key --vault-name myapp-keyvault --query value -o tsv)

Best Practices

Pipeline Optimization

Parallel execution — ускорение через параллельность:

test:
  stage: test
  parallel:
    matrix:

      - SPRING_PROFILES_ACTIVE: [mysql, postgres, h2]
  script:

    - mvn test -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE

Conditional execution — выполнение только при необходимости:

deploy_docs:
  script: ./deploy-docs.sh
  only:
    changes:

      - docs/**/*
      - README.md

Fail fast — быстрое прекращение при критических ошибках:

stages:

  - validate      # Syntax, basic checks
  - build        # Compilation
  - test         # Expensive tests only after build success

Security Best Practices

Least privilege — минимальные необходимые права:

deploy_staging:
  script: kubectl apply -f staging/
  environment: staging
  # Только определенные роли могут deploying в staging

Secret rotation — регулярная смена секретов:

# Использование temporary credentials
before_script:

  - export TEMP_TOKEN=$(vault write -field=token auth/aws/login role=ci-cd)
  - export VAULT_TOKEN=$TEMP_TOKEN

Audit logging — логирование всех deployment'ов:

after_script:

  - echo "Deployed by $GITLAB_USER_LOGIN at $(date)" >> deployment.log
  - curl -X POST $AUDIT_WEBHOOK -d "{'user':'$GITLAB_USER_LOGIN','action':'deploy'}"

Monitoring и Observability

Pipeline metrics — отслеживание производительности:

# Время выполнения pipeline
before_script:

  - export PIPELINE_START=$(date +%s)

after_script:

  - export PIPELINE_END=$(date +%s)
  - export PIPELINE_DURATION=$((PIPELINE_END - PIPELINE_START))
  - curl -X POST $METRICS_ENDPOINT -d "pipeline_duration=$PIPELINE_DURATION"

Deployment tracking:

deploy:
  script:

    - kubectl apply -f deployment.yaml
    - kubectl annotate deployment myapp deployment.kubernetes.io/revision="$CI_COMMIT_SHA"
  after_script:

    - |
      curl -X POST $MONITORING_WEBHOOK \
        -H "Content-Type: application/json" \
        -d '{
          "service": "myapp",
          "version": "'$CI_COMMIT_SHA'",
          "environment": "'$CI_ENVIRONMENT_NAME'",
          "deployed_by": "'$GITLAB_USER_LOGIN'",
          "timestamp": "'$(date -Iseconds)'"
        }'

Заключение

CI/CD решает критические проблемы современной разработки:

  • Автоматизация рутинных процессов
  • Раннее обнаружение проблем
  • Consistent deployment process
  • Быстрая доставка фич пользователям
  • Уменьшение рисков через маленькие изменения

Выбор между Jenkins и GitLab CI:

  • Jenkins — максимальная гибкость, огромная экосистема плагинов, сложность настройки
  • GitLab CI — простота, современный подход, встроенная интеграция с Git

Ключевые принципы:

  1. Fail fast — выявляйте проблемы как можно раньше
  2. Parallel execution — ускоряйте pipeline через параллельность
  3. Cache everything — кешируйте зависимости и промежуточные результаты
  4. Security first — защищайте секреты и ограничивайте доступ
  5. Monitor everything — отслеживайте метрики и производительность pipeline'ов

Начинайте с простого — базовый build → test → deploy pipeline. Добавляйте сложность по мере роста команды и требований.

Деплой и автоматизация

GitOps — автоматизация через Git

GitOps — методология, где состояние инфраструктуры и приложений декларативно описано в Git репозитории, а специальные контроллеры автоматически синхронизируют реальное состояние с желаемым.

Основные принципы GitOps

  1. Declarative — вся конфигурация описана в YAML/JSON манифестах
  2. Versioned — все изменения отслеживаются через Git commits
  3. Immutable — не изменяем running state напрямую, только через Git
  4. Pulled — агенты в кластере сами забирают изменения из Git

Традиционный подход (Push):

CI Pipeline → Build → Test → kubectl apply → Kubernetes

GitOps подход (Pull):

CI Pipeline → Build → Test → Update Git Repo
                               ↓
Git Repo ← ArgoCD/Flux watches ← Kubernetes Cluster

Структура GitOps репозиториев

App Repository — исходный код приложения:

myapp/
├── src/
├── Dockerfile
├── .gitlab-ci.yml
└── k8s/                    # Базовые манифесты
    ├── deployment.yaml
    └── service.yaml

Config Repository — конфигурация deployment'ов:

myapp-config/
├── environments/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   └── values.yaml
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   └── values.yaml
│   └── production/
│       ├── kustomization.yaml
│       └── values.yaml
└── base/                   # Общие ресурсы
    ├── deployment.yaml
    ├── service.yaml
    └── kustomization.yaml

Workflow GitOps:

  1. Разработчик делает commit в App Repository
  2. CI pipeline собирает новый Docker образ с тегом
  3. CI автоматически обновляет image tag в Config Repository
  4. ArgoCD/Flux замечает изменения и деплоит новую версию

Автоматическое обновление образов

CI Pipeline обновляет Config Repository:

# .gitlab-ci.yml в App Repository
update_config:
  stage: deploy
  script:
    # Клонируем config repository
    - git clone https://oauth2:$CONFIG_REPO_TOKEN@gitlab.com/company/myapp-config.git
    - cd myapp-config
    
    # Обновляем image tag
    - sed -i "s|image: myapp:.*|image: myapp:$CI_COMMIT_SHA|" environments/staging/values.yaml
    
    # Коммитим изменения
    - git config user.email "ci@company.com"
    - git config user.name "CI Bot"
    - git add .
    - git commit -m "Update staging image to $CI_COMMIT_SHA"
    - git push
  only:

    - develop

update_prod_config:
  stage: deploy
  script:

    - git clone https://oauth2:$CONFIG_REPO_TOKEN@gitlab.com/company/myapp-config.git
    - cd myapp-config
    - sed -i "s|image: myapp:.*|image: myapp:$CI_COMMIT_TAG|" environments/production/values.yaml
    - git add .
    - git commit -m "Update production image to $CI_COMMIT_TAG"
    - git push
  only:

    - tags
  when: manual  # Ручное подтверждение для production

Deployment Strategies — стратегии развертывания

Rolling Update — постепенное обновление

Как работает: Kubernetes постепенно заменяет старые Pod'ы новыми. В каждый момент времени часть старых и новых Pod'ов работают параллельно.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1     # Максимум 1 Pod может быть недоступен
      maxSurge: 2           # Максимум 2 дополнительных Pod'а
  template:
    spec:
      containers:

      - name: app
        image: myapp:v2.0
        readinessProbe:     # Критично для Rolling Update
          httpGet:
            path: /health
            port: 8080

Процесс Rolling Update:

  1. Создаются 2 новых Pod'а (maxSurge=2) → всего 8 Pod'ов
  2. Когда новые Pod'ы готовы, удаляется 1 старый → 7 Pod'ов
  3. Создается еще 1 новый → 8 Pod'ов
  4. Процесс повторяется до полной замены

Преимущества: Нет простоя, постепенная миграция трафика Недостатки: Смешивание версий, сложный rollback при проблемах

Blue-Green Deployment — мгновенное переключение

Как работает: Создается полная копия приложения с новой версией (Green), затем трафик мгновенно переключается с текущей версии (Blue) на новую.

# Blue environment (текущий production)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-blue
  labels:
    version: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: blue
  template:
    metadata:
      labels:
        app: myapp
        version: blue
    spec:
      containers:

      - name: app
        image: myapp:v1.0

---
# Green environment (новая версия)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
  labels:
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: green
  template:
    metadata:
      labels:
        app: myapp
        version: green
    spec:
      containers:

      - name: app
        image: myapp:v2.0

---
# Service переключается между blue и green
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
    version: blue  # Изменить на green для переключения
  ports:

  - port: 80
    targetPort: 8080

Процесс Blue-Green:

  1. Blue (v1.0) обслуживает production трафик
  2. Деплоим Green (v2.0) параллельно
  3. Тестируем Green через internal endpoint
  4. Переключаем Service с Blue на Green
  5. Мониторим Green, в случае проблем — быстрый rollback на Blue

Преимущества: Мгновенное переключение, легкий rollback, нет смешивания версий Недостатки: Требует двойные ресурсы, сложно с stateful приложениями

Canary Deployment — постепенное переключение трафика

Как работает: Новая версия получает небольшую часть трафика (например, 10%), постепенно увеличивая до 100% при отсутствии проблем.

# Stable version (90% трафика)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-stable
spec:
  replicas: 9  # 90% от общего количества
  selector:
    matchLabels:
      app: myapp
      version: stable
  template:
    metadata:
      labels:
        app: myapp
        version: stable
    spec:
      containers:

      - name: app
        image: myapp:v1.0

---
# Canary version (10% трафика)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-canary
spec:
  replicas: 1  # 10% от общего количества
  selector:
    matchLabels:
      app: myapp
      version: canary
  template:
    metadata:
      labels:
        app: myapp
        version: canary
    spec:
      containers:

      - name: app
        image: myapp:v2.0

---
# Service балансирует между stable и canary
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp  # Включает и stable, и canary
  ports:

  - port: 80
    targetPort: 8080

Advanced Canary с Istio:

# Istio VirtualService для точного контроля трафика
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp-canary
spec:
  http:

  - match:

    - headers:
        canary:
          exact: "true"  # Специальный header для canary
    route:

    - destination:
        host: myapp-service
        subset: canary
  - route:

    - destination:
        host: myapp-service
        subset: stable
      weight: 90
    - destination:
        host: myapp-service
        subset: canary
      weight: 10  # 10% трафика на canary

Процесс Canary:

  1. Деплоим canary версию с 5% трафика
  2. Мониторим метрики (error rate, latency, business metrics)
  3. Если все хорошо → увеличиваем до 20%, 50%, 100%
  4. При проблемах → удаляем canary, весь трафик на stable

Преимущества: Минимальный риск, раннее обнаружение проблем Недостатки: Сложность в настройке, необходимость хорошего мониторинга


Helm — пакетный менеджер для Kubernetes

Helm упрощает управление сложными Kubernetes приложениями через концепцию Charts — пакетов с параметризованными YAML манифестами.

Chart структура для разных окружений

myapp-chart/
├── Chart.yaml
├── values.yaml              # Значения по умолчанию
├── values-dev.yaml          # Development окружение
├── values-staging.yaml      # Staging окружение
├── values-production.yaml   # Production окружение
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    └── _helpers.tpl

values.yaml — базовая конфигурация:

replicaCount: 1
image:
  repository: myapp
  tag: latest
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false

resources:
  requests:
    memory: 256Mi
    cpu: 250m

environment: development

values-production.yaml — production настройки:

replicaCount: 3

image:
  tag: v1.2.0  # Фиксированная версия
  pullPolicy: IfNotPresent

ingress:
  enabled: true
  host: myapp.company.com
  tls:
    enabled: true

resources:
  requests:
    memory: 512Mi
    cpu: 500m
  limits:
    memory: 1Gi
    cpu: 1000m

environment: production

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10

templates/deployment.yaml — параметризованный манифест:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:

      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        env:

        - name: ENVIRONMENT
          value: {{ .Values.environment }}
        resources:
          {{- toYaml .Values.resources | nindent 10 }}

Helm в GitOps workflow

GitOps + Helm структура:

myapp-gitops/
├── applications/
│   ├── dev/
│   │   └── myapp.yaml       # ArgoCD Application
│   ├── staging/
│   │   └── myapp.yaml
│   └── production/
│       └── myapp.yaml
└── charts/
    └── myapp/               # Helm chart
        ├── Chart.yaml
        ├── values.yaml
        └── templates/

ArgoCD Application для Helm:

# applications/production/myapp.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/company/myapp-gitops
    path: charts/myapp
    targetRevision: HEAD
    helm:
      valueFiles:

      - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Kustomize — альтернатива Helm

Kustomize — встроенный в kubectl инструмент для кастомизации Kubernetes манифестов без шаблонов. Использует принцип наследования и наложения изменений.

Kustomize структура

myapp-kustomize/
├── base/                    # Базовые ресурсы
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/                # Окружения
    ├── dev/
    │   ├── kustomization.yaml
    │   └── dev-patch.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   └── staging-patch.yaml
    └── production/
        ├── kustomization.yaml
        ├── production-patch.yaml
        └── hpa.yaml         # Дополнительные ресурсы

base/kustomization.yaml — базовая конфигурация:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:

- deployment.yaml
- service.yaml

commonLabels:
  app: myapp

images:

- name: myapp
  newTag: latest

overlays/production/kustomization.yaml — production кастомизация:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:

- ../../base

resources:

- hpa.yaml           # Дополнительные ресурсы для production

patchesStrategicMerge:

- production-patch.yaml

images:

- name: myapp
  newTag: v1.2.0     # Production версия

replicas:

- name: myapp-deployment
  count: 3           # 3 реплики для production

commonLabels:
  environment: production

overlays/production/production-patch.yaml — патч для production:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  template:
    spec:
      containers:

      - name: myapp
        resources:
          requests:
            memory: 512Mi
            cpu: 500m
          limits:
            memory: 1Gi
            cpu: 1000m
        env:

        - name: ENVIRONMENT
          value: production

Применение Kustomize:

# Локальное применение
kubectl apply -k overlays/production/

# Просмотр результата без применения
kubectl kustomize overlays/production/

Kustomize vs Helm:

  • Kustomize: Простой, декларативный, встроенный в kubectl, нет шаблонизации
  • Helm: Мощная шаблонизация, пакетный менеджер, dependency management, сложнее

ArgoCD — GitOps оператор

ArgoCD — Kubernetes-native continuous delivery tool, который мониторит Git репозитории и автоматически синхронизирует состояние кластера с репозиторием.

Установка ArgoCD

# Установка ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Доступ к UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

ArgoCD Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
  finalizers:

    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  
  source:
    repoURL: https://github.com/company/myapp-config
    path: k8s/production
    targetRevision: HEAD
    
    # Для Helm charts
    helm:
      valueFiles:

      - values-production.yaml
      parameters:

      - name: image.tag
        value: v1.2.0
    
    # Для Kustomize
    kustomize:
      images:

      - myapp:v1.2.0
  
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  
  syncPolicy:
    automated:
      prune: true      # Удалять ресурсы, удаленные из Git
      selfHeal: true   # Автоматически исправлять drift
    syncOptions:

    - CreateNamespace=true
    
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Multi-cluster deployment с ArgoCD

# ApplicationSet для deployment в несколько кластеров
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-multi-cluster
  namespace: argocd
spec:
  generators:

  - clusters:
      selector:
        matchLabels:
          environment: production
  template:
    metadata:
      name: 'myapp-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/company/myapp-config
        path: k8s/production
        targetRevision: HEAD
      destination:
        server: '{{server}}'
        namespace: production
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

ArgoCD преимущества:

  • Декларативный GitOps
  • Web UI для визуализации
  • Multi-cluster support
  • RBAC интеграция
  • Rollback capabilities

Flux CD — альтернативный GitOps оператор

Flux — более легковесная альтернатива ArgoCD с focus на simplicity и Kubernetes-native approach.

Flux v2 структура

# GitRepository — источник манифестов
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: myapp-config
  namespace: flux-system
spec:
  interval: 1m
  ref:
    branch: main
  url: https://github.com/company/myapp-config

---
# Kustomization — что деплоить
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
  name: myapp-production
  namespace: flux-system
spec:
  interval: 10m
  path: "./k8s/production"
  prune: true
  sourceRef:
    kind: GitRepository
    name: myapp-config
  validation: client
  healthChecks:

    - apiVersion: apps/v1
      kind: Deployment
      name: myapp
      namespace: production

Image automation с Flux

# ImageRepository — отслеживание Docker registry
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  image: registry.company.com/myapp
  interval: 1m

---
# ImagePolicy — политика обновления образов
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
  name: myapp-policy
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: myapp
  policy:
    semver:
      range: '>=1.0.0'

---
# ImageUpdateAutomation — автоматическое обновление
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: myapp-auto-update
  namespace: flux-system
spec:
  interval: 30m
  sourceRef:
    kind: GitRepository
    name: myapp-config
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: flux@company.com
        name: Flux
      messageTemplate: |
        Update {{range .Updated.Images}}{{println .}}{{end}}
  update:
    path: "./k8s/production"
    strategy: Setters

Flux vs ArgoCD:

  • Flux: Kubernetes-native CRDs, lightweight, built-in image automation
  • ArgoCD: Rich UI, ApplicationSets, more mature ecosystem

Практические сценарии

Hotfix deployment

Scenario: Критический bug в production, нужен быстрый hotfix.

GitOps процесс:

  1. Создание hotfix ветки от production tag
  2. Исправление bug и создание hotfix tag (v1.2.1)
  3. CI автоматически обновляет production config с новым тегом
  4. ArgoCD автоматически деплоит hotfix
# CI pipeline для hotfix
hotfix_deploy:
  script:

    - git clone $CONFIG_REPO
    - cd myapp-config
    - sed -i "s|tag: v.*|tag: $CI_COMMIT_TAG|" environments/production/values.yaml
    - git commit -am "Hotfix: $CI_COMMIT_TAG"
    - git push
  only:

    - /^v\d+\.\d+\.\d+$/  # Только version tags
  when: manual

Feature flag driven deployment

Scenario: Новая фича должна быть выключена по умолчанию.

# ConfigMap для feature flags
apiVersion: v1
kind: ConfigMap
metadata:
  name: feature-flags
data:
  new-payment-flow: "false"
  experimental-ui: "false"
  enhanced-search: "true"

# Deployment с feature flags
spec:
  template:
    spec:
      containers:

      - name: app
        env:

        - name: FEATURE_NEW_PAYMENT_FLOW
          valueFrom:
            configMapKeyRef:
              name: feature-flags
              key: new-payment-flow

Включение фичи через GitOps:

  1. Изменение ConfigMap в Git: new-payment-flow: "true"
  2. ArgoCD автоматически обновляет ConfigMap
  3. Приложение перечитывает конфигурацию без restart

Multi-environment promotion

Scenario: Код проходит через dev → staging → production.

# ArgoCD ApplicationSet для promotion
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-promotion
spec:
  generators:

  - list:
      elements:

      - environment: dev
        branch: develop
        cluster: dev-cluster
      - environment: staging
        branch: release
        cluster: staging-cluster
      - environment: production
        branch: main
        cluster: production-cluster
  template:
    metadata:
      name: 'myapp-{{environment}}'
    spec:
      source:
        repoURL: https://github.com/company/myapp-config
        path: 'environments/{{environment}}'
        targetRevision: '{{branch}}'
      destination:
        server: '{{cluster}}'
        namespace: '{{environment}}'

Best Practices

Security в GitOps

Repository access control:

# ArgoCD Project с ограничениями
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production-apps
spec:
  description: Production applications
  sourceRepos:

  - https://github.com/company/myapp-config
  destinations:

  - namespace: production
    server: https://production-cluster
  clusterResourceWhitelist:

  - group: apps
    kind: Deployment
  namespaceResourceWhitelist:

  - group: ''
    kind: Service
  - group: ''
    kind: ConfigMap

Separate repositories:

  • App code и infrastructure code в разных репозиториях
  • Разные access rights для разных команд
  • Production config требует code review

Monitoring GitOps

ArgoCD metrics:

# ServiceMonitor для ArgoCD
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-metrics
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-metrics
  endpoints:

  - port: metrics

Key metrics для мониторинга:

  • Sync status приложений
  • Time to deployment (Git commit → running in production)
  • Drift detection и self-healing events
  • Failed deployments и их причины

Rollback strategies

Automatic rollback на проблемы:

# ArgoCD с health checks
spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:

    - Retry=3
  revisionHistoryLimit: 10  # Хранить историю для rollback

Manual rollback через ArgoCD UI или CLI:

# ArgoCD CLI rollback
argocd app rollback myapp --revision 123
argocd app sync myapp

Заключение

Современный deployment — это GitOps + правильная стратегия развертывания:

  • GitOps обеспечивает audit trail, rollback capabilities, и declarative approach
  • Rolling Update — для большинства случаев, простой и надежный
  • Blue-Green — когда нужен instant rollback и нет проблем с ресурсами
  • Canary — для критичных изменений с постепенной проверкой

Выбор инструментов:

  • Helm — для сложных приложений с множеством параметров
  • Kustomize — для простых case'ов без шаблонизации
  • ArgoCD — rich UI, mature ecosystem
  • Flux — Kubernetes-native, automatic image updates

Ключевые принципы:

  1. Everything as Code — вся конфигурация в Git
  2. Immutable deployments — не изменяем running state напрямую
  3. Automated testing — проверка deployments через health checks
  4. Progressive delivery — постепенное раскатывание с мониторингом
  5. Fast rollback — возможность быстро откатиться при проблемах

Начинайте с простого: Rolling Updates + ArgoCD + Helm. Добавляйте Canary deployments по мере роста критичности системы.

Security & DevSecOps

Управление секретами

HashiCorp Vault

Vault — это централизованное хранилище секретов с динамическим управлением доступом. Позволяет генерировать временные секреты, ротировать их автоматически и контролировать доступ через политики.

Основные возможности:

  • Динамическая генерация секретов (БД пароли, AWS ключи)
  • Шифрование данных в покое и при передаче
  • Аудит всех операций с секретами
  • Интеграция с внешними системами аутентификации

Установка и базовая настройка:

# Запуск Vault в dev режиме
vault server -dev

# Авторизация
export VAULT_ADDR='http://127.0.0.1:8200'
vault auth -method=userpass username=admin

Работа с секретами:

# Создание секрета
vault kv put secret/myapp db_password="supersecret123"

# Чтение секрета
vault kv get secret/myapp

# Создание политики доступа
vault policy write myapp-policy - <<EOF
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
EOF

AWS Secrets Manager

AWS Secrets Manager — управляемый сервис для хранения секретов в AWS. Автоматически ротирует пароли для RDS, DocumentDB и других сервисов.

Ключевые особенности:

  • Автоматическая ротация секретов
  • Интеграция с AWS сервисами
  • Шифрование с помощью AWS KMS
  • Контроль доступа через IAM

Создание и использование секретов:

# Создание секрета
aws secretsmanager create-secret \
    --name "prod/myapp/db" \
    --description "Database credentials" \
    --secret-string '{"username":"admin","password":"mysecret"}'

# Получение секрета
aws secretsmanager get-secret-value --secret-id "prod/myapp/db"

Использование в Python:

import boto3
import json

def get_secret(secret_name, region_name="us-east-1"):
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )
    
    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
        return json.loads(get_secret_value_response['SecretString'])
    except Exception as e:
        print(f"Error retrieving secret: {e}")
        return None

# Использование
db_creds = get_secret("prod/myapp/db")

Сканирование уязвимостей

Snyk

Snyk — платформа для обнаружения и исправления уязвимостей в зависимостях, контейнерах и коде. Интегрируется с CI/CD и предоставляет рекомендации по устранению.

Основные функции:

  • Сканирование зависимостей в различных языках
  • Анализ Docker образов
  • Статический анализ кода (SAST)
  • Автоматические PR с исправлениями

Установка и использование:

# Установка CLI
npm install -g snyk

# Авторизация
snyk auth

# Сканирование проекта
snyk test

# Мониторинг проекта
snyk monitor

Интеграция в CI/CD (GitHub Actions):

- name: Run Snyk to check for vulnerabilities
  uses: snyk/actions/node@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    args: --severity-threshold=high

Trivy

Trivy — бесплатный сканер уязвимостей для контейнеров, файловых систем и Git репозиториев. Поддерживает множество форматов вывода и легко интегрируется в CI/CD.

Возможности:

  • Сканирование OS пакетов и языковых зависимостей
  • Обнаружение секретов в коде
  • Проверка конфигураций Kubernetes
  • Сканирование образов в реестрах

Установка и использование:

# Установка через curl
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Сканирование Docker образа
trivy image nginx:latest

# Сканирование файловой системы
trivy fs /path/to/project

# Сканирование с выводом в JSON
trivy image --format json --output results.json nginx:latest

Интеграция в Docker build:

FROM alpine:latest
RUN apk add --no-cache trivy
COPY . /app
RUN trivy fs /app --exit-code 1 --severity HIGH,CRITICAL

OWASP Dependency-Check

OWASP Dependency-Check — инструмент для обнаружения публично известных уязвимостей в зависимостях проекта. Использует базу данных CVE для идентификации уязвимостей.

Особенности:

  • Поддержка множества языков и пакетных менеджеров
  • Интеграция с Maven, Gradle, Jenkins
  • Различные форматы отчетов (HTML, XML, JSON)
  • Возможность задания пороговых значений для прерывания сборки

Использование с Maven:

<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>8.4.0</version>
    <configuration>
        <failBuildOnCVSS>7</failBuildOnCVSS>
        <format>ALL</format>
    </configuration>
</plugin>

CLI использование:

# Скачивание и запуск
wget https://github.com/jeremylong/DependencyCheck/releases/download/v8.4.0/dependency-check-8.4.0-release.zip
unzip dependency-check-8.4.0-release.zip

# Сканирование проекта
./dependency-check/bin/dependency-check.sh \
    --project "MyProject" \
    --scan "./src" \
    --format "HTML" \
    --out "./reports"

Настройка HTTPS и TLS

Основы TLS

TLS (Transport Layer Security) — криптографический протокол для обеспечения безопасной передачи данных. Заменил устаревший SSL.

Ключевые компоненты:

  • Handshake — установление безопасного соединения
  • Сертификат — подтверждение подлинности сервера
  • Симметричное шифрование — для передачи данных
  • Асимметричное шифрование — для обмена ключами

Настройка HTTPS в Nginx

server {
    listen 443 ssl http2;
    server_name example.com;
    
    # Сертификаты
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/private.key;
    
    # Протоколы и шифры
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# Редирект с HTTP на HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

Получение сертификатов с Let's Encrypt

# Установка Certbot
sudo apt-get install certbot python3-certbot-nginx

# Получение сертификата
sudo certbot --nginx -d example.com -d www.example.com

# Автоматическое обновление
sudo crontab -e
0 12 * * * /usr/bin/certbot renew --quiet

Secrets в Kubernetes

Создание и использование секретов

Kubernetes Secrets — объекты для хранения конфиденциальной информации (пароли, токены, ключи). Данные кодируются в base64 и могут быть зашифрованы в etcd.

Типы секретов:

Создание секрета:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=  # admin в base64
  password: MWYyZDFlMmU2N2Rm  # 1f2d1e2e67df в base64

Создание через kubectl:

# Из литералов
kubectl create secret generic mysecret \
    --from-literal=username=admin \
    --from-literal=password=1f2d1e2e67df

# Из файлов
kubectl create secret generic mysecret \
    --from-file=username.txt \
    --from-file=password.txt

# TLS секрет
kubectl create secret tls tls-secret \
    --cert=path/to/cert.pem \
    --key=path/to/key.pem

Использование секретов в Pod

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:

  - name: test-container
    image: nginx
    # Переменные окружения из секрета
    env:

    - name: SECRET_USERNAME
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: username
    - name: SECRET_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: password
    # Монтирование как volume
    volumeMounts:

    - name: secret-volume
      mountPath: /etc/secret-volume
      readOnly: true
  volumes:

  - name: secret-volume
    secret:
      secretName: mysecret

Шифрование секретов в etcd

# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:

  - resources:

      - secrets
    providers:

      - aescbc:
          keys:

            - name: key1
              secret: <base64-encoded-secret>
      - identity: {}

Добавление в kube-apiserver:

# В манифест kube-apiserver
- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

JWT и OAuth2

JWT (JSON Web Token)

JWT — стандарт для создания токенов доступа. Состоит из трех частей: заголовок, полезная нагрузка и подпись. Токены самодостаточны и содержат всю необходимую информацию.

Структура JWT:

header.payload.signature

Пример JWT токена:

// Header
{
  "alg": "HS256",
  "typ": "JWT"
}

// Payload
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622
}

Создание JWT в Node.js:

const jwt = require('jsonwebtoken');

// Создание токена
const token = jwt.sign(
  {
    userId: 123,
    username: 'john',
    role: 'admin'
  },
  'your-secret-key',
  {
    expiresIn: '1h',
    issuer: 'your-app',
    audience: 'your-users'
  }
);

// Проверка токена
try {
  const decoded = jwt.verify(token, 'your-secret-key');
  console.log(decoded);
} catch (err) {
  console.error('Invalid token');
}

OAuth2

OAuth2 — протокол авторизации, позволяющий приложениям получать ограниченный доступ к ресурсам пользователя. Разделяет аутентификацию и авторизацию.

Основные роли:

  • Resource Owner — владелец ресурса (пользователь)
  • Client — приложение, запрашивающее доступ
  • Authorization Server — сервер авторизации
  • Resource Server — сервер ресурсов

Grant Types (типы предоставления доступа):

  • Authorization Code — для веб-приложений
  • Implicit — для SPA (устарел)
  • Client Credentials — для межсервисного взаимодействия
  • Resource Owner Password — для доверенных приложений

Keycloak

Keycloak — открытый сервер идентификации и управления доступом. Поддерживает OAuth2, OpenID Connect, SAML и другие протоколы.

Возможности:

  • Единый вход (SSO)
  • Социальная аутентификация
  • Двухфакторная аутентификация
  • Управление пользователями и ролями
  • Адаптеры для различных платформ

Установка через Docker:

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev

Настройка клиента в Keycloak:

// Конфигурация клиента
const keycloakConfig = {
  realm: 'myrealm',
  'auth-server-url': 'http://localhost:8080',
  'ssl-required': 'external',
  resource: 'myclient',
  'public-client': true,
  'confidential-port': 0
};

// Инициализация Keycloak в JavaScript
const keycloak = new Keycloak(keycloakConfig);

keycloak.init({ onLoad: 'login-required' }).then(authenticated => {
  if (authenticated) {
    console.log('User authenticated');
    // Получение токена
    const token = keycloak.token;
    const refreshToken = keycloak.refreshToken;
  }
});

Интеграция с Spring Boot:

# application.yml
keycloak:
  realm: myrealm
  auth-server-url: http://localhost:8080
  resource: myclient
  public-client: true
  
spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: myclient
            authorization-grant-type: authorization_code
            scope: openid, profile, email
        provider:
          keycloak:
            authorization-uri: http://localhost:8080/realms/myrealm/protocol/openid-connect/auth
            token-uri: http://localhost:8080/realms/myrealm/protocol/openid-connect/token
            user-info-uri: http://localhost:8080/realms/myrealm/protocol/openid-connect/userinfo
            jwk-set-uri: http://localhost:8080/realms/myrealm/protocol/openid-connect/certs

Лучшие практики безопасности

Общие принципы

  1. Принцип минимальных привилегий — предоставляйте минимально необходимые права
  2. Глубокая защита — используйте множественные уровни безопасности
  3. Fail securely — в случае сбоя система должна оставаться безопасной
  4. Регулярные обновления — своевременно применяйте патчи безопасности

Безопасность в CI/CD

# Пример безопасного pipeline
name: Secure CI/CD

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:

    - uses: actions/checkout@v3
    
    # Сканирование секретов
    - name: Scan for secrets
      run: |
        git secrets --scan
        
    # Сканирование зависимостей
    - name: Dependency scan
      run: |
        npm audit --audit-level=moderate
        
    # Сканирование образа
    - name: Container scan
      run: |
        docker build -t myapp .
        trivy image myapp --exit-code 1 --severity HIGH,CRITICAL
        
    # Статический анализ
    - name: Static analysis
      uses: github/super-linter@v4
      env:
        DEFAULT_BRANCH: main
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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

# Пример конфигурации для мониторинга безопасности
apiVersion: v1
kind: ConfigMap
metadata:
  name: security-config
data:
  falco.yaml: |
    rules_file:

      - /etc/falco/falco_rules.yaml
    json_output: true
    log_stderr: true
    log_syslog: true
    
    # Правила для обнаружения аномалий
    - rule: Detect shell in container
      desc: Detect shell execution in container
      condition: >
        spawned_process and container and
        (proc.name in (shell_binaries))
      output: >
        Shell spawned in container
        (user=%user.name container=%container.name 
         shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
      priority: WARNING

Эта шпаргалка охватывает основные аспекты безопасности в DevOps, от управления секретами до настройки аутентификации и авторизации. Каждый инструмент имеет свои особенности и области применения, поэтому важно выбирать подходящие решения в зависимости от конкретных требований проекта.

Kubernetes Health Probes в Spring Boot

Основные концепции

Health Probes — механизм мониторинга состояния подов в Kubernetes. Позволяют кластеру принимать решения о перезапуске, направлении трафика и готовности приложения.

Spring Boot Actuator — модуль для мониторинга и управления приложением, предоставляет готовые эндпоинты для health checks.

Типы пробов

1. Liveness Probe

Назначение: Определяет, жив ли контейнер. При провале — перезапускает под.

Когда использовать: Deadlock, зависание приложения, критические ошибки.

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

2. Readiness Probe

Назначение: Готов ли под принимать трафик. При провале — исключает из Service.

Когда использовать: Подключение к БД, прогрев кэша, инициализация зависимостей.

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 2

3. Startup Probe

Назначение: Проверяет завершение запуска. Блокирует другие пробы до успеха.

Когда использовать: Медленный старт, загрузка данных, legacy приложения.

startupProbe:
  httpGet:
    path: /actuator/health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 30  # 150 сек макс

Spring Boot Actuator настройка

Зависимость

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.yml

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true  # Включает /liveness и /readiness
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

Кастомные Health Indicators

Проверка базы данных

@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    public Health health() {
        try (Connection conn = dataSource.getConnection()) {
            if (conn.isValid(2)) {
                return Health.up()
                    .withDetail("database", "Available")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
        return Health.down().build();
    }
}

Проверка внешнего сервиса

@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Override
    public Health health() {
        try {
            ResponseEntity<String> response = restTemplate
                .getForEntity("http://external-service/health", String.class);
            
            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up().build();
            }
        } catch (Exception e) {
            return Health.down()
                .withException(e)
                .build();
        }
        return Health.down().build();
    }
}

Параметры конфигурации

initialDelaySeconds

Задержка перед первой проверкой. Дает время на инициализацию.

periodSeconds

Интервал между проверками. Баланс между отзывчивостью и нагрузкой.

timeoutSeconds

Таймаут ожидания ответа. Должен быть меньше periodSeconds.

failureThreshold

Количество неудачных попыток перед действием (перезапуск/исключение).

successThreshold

Количество успешных проверок для восстановления (только для readiness).

Deployment пример

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-app
  template:
    metadata:
      labels:
        app: spring-app
    spec:
      containers:

      - name: app
        image: myapp:latest
        ports:

        - containerPort: 8080
        env:

        - name: SPRING_PROFILES_ACTIVE
          value: "k8s"
        
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 2
        
        startupProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          failureThreshold: 30
        
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

Лучшие практики

Разделение ответственности

  • Liveness: Только критические ошибки (deadlock, OOM)
  • Readiness: Зависимости (БД, кэш, внешние сервисы)
  • Startup: Длительная инициализация

Производительность

  • Легковесные проверки (< 1 сек)
  • Избегать сложной логики в health checks
  • Кэширование результатов для медленных зависимостей

Безопасность

management:
  endpoints:
    web:
      base-path: /internal
      exposure:
        include: health
  endpoint:
    health:
      show-details: when-authorized

Мониторинг

  • Логирование изменений состояния
  • Метрики проваленных проверок
  • Алерты на частые перезапуски

Типичные ошибки

  1. Слишком агрессивные таймауты — ложные срабатывания
  2. Проверка внешних зависимостей в liveness — каскадные перезапуски
  3. Одинаковые эндпоинты для всех пробов — неправильная диагностика
  4. Игнорирование startup probe — преждевременные перезапуски медленных приложений

Отладка

Просмотр статуса подов

kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>

Проверка health endpoints

kubectl port-forward <pod-name> 8080:8080
curl http://localhost:8080/actuator/health