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

С какими проблемами сталкивался при Delivery в CI/CD

2.3 Middle🔥 201 комментариев
#Другое

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

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

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

Проблемы при Delivery в CI/CD

В процессе настройки и работы с CI/CD pipeline мне приходилось сталкиваться с различными проблемами на этапе delivery (доставки артефактов в production или staging).

1. Нестабильность развёртывания

Проблема: Один и тот же коммит развёртывается по-разному в разных окружениях.

# Плохо - переменные окружения разные
stages:
  delivery:
    script:
      - docker run -e DB_HOST=$DB_HOST app:latest
      # DB_HOST может быть разным в dev, staging, prod

Решение: Использование версионированных артефактов и конфигов

# Хорошо - явное указание версий
stages:
  build:
    script:
      - docker build -t app:${CI_COMMIT_SHA} .
      - docker push registry.example.com/app:${CI_COMMIT_SHA}
  
  delivery:
    script:
      - kubectl set image deployment/app app=registry.example.com/app:${CI_COMMIT_SHA}
      # Чёткие версии для каждого окружения
      - kubectl apply -f config/prod-secrets.yaml

2. Database migration проблемы

Проблема: Миграции БД не откатываются при ошибке deploy

// Проблемное развёртывание
public class DeploymentConfig {
    public static void main(String[] args) {
        // Миграции БД выполняются
        Flyway.configure()
            .dataSource(dbUrl, dbUser, dbPassword)
            .load()
            .migrate();  // Если здесь ошибка, отката нет!
        
        // Потом запускаем приложение
        SpringApplication.run(Application.class, args);
    }
}

Решение: Blue-Green deployment с возможностью отката

delivery:
  before_script:
    # Резервная копия текущего состояния
    - kubectl get deployment app -o yaml > /tmp/app-backup.yaml
    - ./scripts/backup-database.sh
  
  script:
    # Развёртываем новую версию (Green)
    - kubectl apply -f deployment-new.yaml
    - sleep 30  # Даём время на старт
    - kubectl run test-pod --image=curlimages/curl -- curl http://app-new:8080/health
  
  on_failure:
    # При ошибке откатываемся (возвращаемся к Blue)
    - kubectl apply -f /tmp/app-backup.yaml
    - ./scripts/restore-database.sh

3. Артефакты не удаляются, дисковое пространство кончается

Проблема: Pipeline создаёт Docker образы и артефакты, но не удаляет старые

# Проблема: диск переполнится
build:
  script:
    - docker build -t app:${CI_COMMIT_SHA} .
    - docker push registry.example.com/app:${CI_COMMIT_SHA}
    # Но образ остаётся на диске

Решение: Очистка старых артефактов

build:
  script:
    - docker build -t app:${CI_COMMIT_SHA} .
    - docker push registry.example.com/app:${CI_COMMIT_SHA}
    - docker image prune -f --filter "until=72h"  # Удалить образы старше 3 дней
    - docker system prune -f --volumes  # Очистить unused volumes

artifacts:
  paths:
    - target/app.jar
  expire_in: 7 days  # Автоматически удаляется через 7 дней

4.롤льбэк при критической ошибке

Проблема: Развертывание началось, но новое приложение крашится, а откатиться невозможно

// Новая версия с багом
@RestController
public class ApiController {
    @PostMapping("/users")
    public void createUser(@RequestBody User user) {
        userService.save(user);  // NullPointerException при user == null!
        // Приложение крашится, но версия уже в prod
    }
}

Решение: Health check перед финализацией deployment

#!/bin/bash
delivery_script() {
    # 1. Развёртываем новую версию
    kubectl apply -f deployment.yaml
    
    # 2. Ждём старта подов
    kubectl wait --for=condition=available --timeout=300s \
        deployment/app
    
    # 3. Проверяем здоровье приложения
    for i in {1..10}; do
        if curl -f http://localhost:8080/actuator/health; then
            echo "Health check passed"
            break
        fi
        if [ $i -eq 10 ]; then
            echo "Health check failed, rolling back"
            kubectl rollout undo deployment/app
            exit 1
        fi
        sleep 5
    done
    
    # 4. Запускаем smoke тесты
    ./run-smoke-tests.sh || {
        kubectl rollout undo deployment/app
        exit 1
    }
    
    echo "Deployment successful"
}

5. Зависимости от порядка развёртывания микросервисов

Проблема: Сервис A зависит от сервиса B, но B развёртывается после A

// Service A пытается подключиться к Service B
@Service
public class ServiceA {
    @Autowired
    private RestTemplate restTemplate;
    
    public String callServiceB() {
        // Service B может быть недоступен!
        return restTemplate.getForObject("http://service-b:8080/data", String.class);
    }
}

Решение: Явная последовательность в CI/CD

stages:
  - build
  - test
  - deploy-critical-services   # Сначала базовые сервисы
  - deploy-dependent-services  # Потом зависимые
  - smoke-tests

deploy-base-services:
  stage: deploy-critical-services
  script:
    - kubectl apply -f services/database.yaml
    - kubectl wait --for=condition=ready pod -l app=database --timeout=600s
    - kubectl apply -f services/service-b.yaml
    - kubectl wait --for=condition=ready pod -l app=service-b --timeout=600s

deploy-dependent-services:
  stage: deploy-dependent-services
  dependencies:
    - deploy-base-services
  script:
    - kubectl apply -f services/service-a.yaml

6. Недостаточно прав в production

Проблема: Pipeline пытается развернуть, но не хватает прав доступа

# Проблема: используется одна учётная запись для всех окружений
deploy:
  script:
    - kubectl apply -f deployment.yaml
    # Может не быть доступа к prod кластеру

Решение: Разные credentials для разных окружений

deploy-staging:
  stage: deploy
  environment:
    name: staging
  before_script:
    - kubectl config use-context staging-cluster
    - kubectl config set-credentials deployer --token=$STAGING_TOKEN
  script:
    - kubectl apply -f deployment.yaml
  only:
    - develop

deploy-prod:
  stage: deploy
  environment:
    name: production
  before_script:
    - kubectl config use-context prod-cluster
    - kubectl config set-credentials deployer --token=$PROD_TOKEN
  script:
    - kubectl apply -f deployment.yaml
  only:
    - main
  when: manual  # Требует ручного подтверждения

7. Конфликты портов при локальном тестировании

Проблема: Pipeline запускает Docker контейнеры, но порты уже занят

# Ошибка: Docker: bind: address already in use
docker run -p 8080:8080 app:latest

Решение: Использование динамических портов

# Получаем свободный порт
get_free_port() {
    python3 -c "import socket; s = socket.socket(); s.bind(('', 0)); print(s.getsockname()[1])"
}

PORT=$(get_free_port)
docker run -p $PORT:8080 app:latest

# Или использование docker-compose с версионированием
docker-compose -p "app-${CI_PIPELINE_ID}" up

8. Timeout при развёртывании

Проблема: Kubernetes pod слишком долго стартует, pipeline отменяется

# По умолчанию timeout может быть недостаточным
script:
  - kubectl apply -f deployment.yaml
  - kubectl wait --for=condition=ready pod -l app=app --timeout=60s
  # Может не успеть за 60 секунд

Решение: Правильное конфигурирование проб и timeout

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  template:
    spec:
      containers:
      - name: app
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30  # Даём время на старт
          periodSeconds: 10
          failureThreshold: 3
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10

9. Утечки памяти в Java приложениях

Проблема: Старые версии приложения остаются в памяти после deployment

// Неправильный shutdown hook
public class Application {
    static ExecutorService executor = Executors.newFixedThreadPool(10);
    
    // executor не закрывается при shutdown!
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        System.out.println("Shutdown");
        // executor.shutdown();  // Забыли это!
    }));
}

Решение: Правильная конфигурация graceful shutdown

@Configuration
public class ShutdownConfig {
    @Bean
    public ExecutorServiceExitHandler executorServiceExitHandler(
            @Qualifier("taskExecutor") ExecutorService executor) {
        return new ExecutorServiceExitHandler(executor);
    }
}

@Component
public class ExecutorServiceExitHandler implements DisposableBean {
    private final ExecutorService executor;
    
    public ExecutorServiceExitHandler(ExecutorService executor) {
        this.executor = executor;
    }
    
    @Override
    public void destroy() throws Exception {
        executor.shutdown();
        if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
            executor.shutdownNow();
        }
    }
}

10. Версионирование и совместимость

Проблема: API версия не совпадает между сервисами при deployment

// Service A ждёт version: 2, но Service B уже в version: 3
@RestController
public class ServiceAClient {
    @GetMapping("/data")
    public Data getData() {
        // API изменился, поле удалено
        return restTemplate.getForObject("http://service-b/api/v2/data", Data.class);
    }
}

Решение: Версионирование с обратной совместимостью

// API версия явно указана
@RestController
@RequestMapping("/api/v2")
public class ServiceBController {
    @GetMapping("/data")
    public Map<String, Object> getData() {
        Map<String, Object> response = new HashMap<>();
        response.put("version", "2");
        response.put("data", "value");
        response.put("legacyField", "для совместимости");
        return response;
    }
}

Чеклист для надёжного Delivery

✅ Версионирование артефактов (Docker images с SHA коммита) ✅ Health checks перед финализацией ✅ Возможность отката (Blue-Green deployment) ✅ Правильная последовательность развёртывания сервисов ✅ Timeout с запасом ✅ Graceful shutdown приложений ✅ Очистка старых артефактов ✅ Мониторинг и алертинг ✅ Smoke tests после развёртывания ✅ Документация процесса deployment

Эти проблемы и решения помогли мне построить надёжные CI/CD pipeline, которые минимизируют downtime и обеспечивают быстрое восстановление при сбоях.

С какими проблемами сталкивался при Delivery в CI/CD | PrepBro