← Назад к вопросам

Расскажи как происходил деплой на твоем проекте

1.0 Junior🔥 201 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Деплой на моем проекте

В последних проектах я работал с микросервисной архитектурой, использовал Docker, Kubernetes и CI/CD пайплайны. Расскажу про наиболее сложный и интересный случай.

Архитектура деплоя

Мы использовали Kubernetes на AWS (EKS) с GitLab CI/CD:

Данные (Git) → GitLab CI → Docker Build → ECR → Kubectl apply → EKS

Этап 1: Коммит и тестирование

При push в main-ветку запускалась сборка в GitLab CI:

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

test:
  stage: test
  image: openjdk:17
  script:
    - ./mvnw clean test
    - ./mvnw jacoco:report
  coverage: '/Coverage: \d+\.\d+%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: target/site/jacoco/jacoco.xml

Если тесты падали — пайплайн останавливался, деплой не происходил.

Этап 2: Сборка Docker образа

build_image:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

Dockerfile

# Multi-stage build для оптимизации
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline

COPY src src
RUN mvn clean package -DskipTests

# Runtime образ
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar

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

Этап 3: Деплой в Kubernetes

deploy_staging:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - kubectl config use-context $KUBE_CONTEXT_STAGING
    - kubectl set image deployment/api-service 
        api-service=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - kubectl rollout status deployment/api-service -n staging
  environment:
    name: staging
    kubernetes:
      namespace: staging
  only:
    - develop

deploy_production:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - kubectl config use-context $KUBE_CONTEXT_PROD
    - kubectl set image deployment/api-service 
        api-service=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - kubectl rollout status deployment/api-service -n production
    - kubectl rollout history deployment/api-service -n production
  environment:
    name: production
    kubernetes:
      namespace: production
  only:
    - main
  when: manual  # Ручное одобрение для продакшена

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service
  namespace: production
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: api-service
  template:
    metadata:
      labels:
        app: api-service
    spec:
      containers:
      - name: api-service
        image: registry.gitlab.com/company/api-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

Этап 4: Rolling Update и мониторинг

Rolling Update означал постепенное обновление подов:

  1. Создаётся новый под с новым образом
  2. Ждём, пока он пройдёт readiness check
  3. Трафик переводится на новый под
  4. Старый под gracefully shutdown (ждём максимум 30 сек)
  5. Повторяем для остальных подов
# Мониторим деплой
kubectl rollout status deployment/api-service -n production

# Откатываемся если что-то пошло не так
kubectl rollout undo deployment/api-service -n production

# История версий
kubectl rollout history deployment/api-service -n production

Проблемы, которые мы решали

1. Разбитые миграции БД

// Проблема: миграция несовместима с текущей версией приложения
// Решение: всегда откатываем БД миграции перед деплоем новой версии

// 1. Откатываем миграции
flyway migrate

// 2. Деплоим приложение
kubectl set image ...

// 3. Запускаем новые миграции
kubectl run migration-job --image=api-service -- /bin/sh -c "java -jar app.jar --migrate"

2. Утечки памяти при деплое

// Использовали graceful shutdown в Spring Boot
@Configuration
public class GracefulShutdownConfig {
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> 
            webServerFactoryCustomizer() {
        return factory -> factory.setShutdownWaitTime(Duration.ofSeconds(30));
    }
}

3. Холодный старт микросервиса

Оптимизировали через:

  • GraalVM Native Image (вместо JVM)
  • Уменьшение количества beans в Spring
  • Lazy initialization

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

// Использовали Prometheus + Grafana
@RestController
@RequestMapping("/api/users")
public class UserController {
    private final MeterRegistry meterRegistry;
    
    @GetMapping("/{id}")
    public UserDto getUser(@PathVariable Long id) {
        Timer.Sample sample = Timer.start(meterRegistry);
        try {
            UserDto user = userService.findById(id);
            meterRegistry.counter("users.fetched").increment();
            return user;
        } catch (UserNotFoundException e) {
            meterRegistry.counter("users.not_found").increment();
            throw e;
        } finally {
            sample.stop(Timer.builder("users.response.time")
                .publishPercentiles(0.5, 0.95, 0.99)
                .register(meterRegistry));
        }
    }
}

Время деплоя

Обычно деплой занимал:

  • Тесты: 5-7 минут
  • Сборка образа: 3-4 минуты
  • Rolling update: 2-3 минуты
  • Итого: ~12-15 минут от коммита до продакшена
Расскажи как происходил деплой на твоем проекте | PrepBro