Как интегрировать Docker в CI/CD процесс

Ответ

Интеграция Docker в CI/CD позволяет создавать консистентные и изолированные окружения для сборки, тестирования и развертывания приложений. Это решает проблему «на моей машине работает».

Типичный процесс выглядит так:

  1. Сборка образа (docker build): На основе Dockerfile создается образ приложения со всеми зависимостями.
  2. Тестирование: Запускается контейнер из созданного образа для выполнения тестов (unit, integration). Это гарантирует, что тесты проходят в том же окружении, которое будет развернуто.
  3. Публикация образа (docker push): Если тесты успешны, образ загружается в реестр контейнеров (Docker Hub, GitLab Container Registry, AWS ECR).
  4. Развертывание: Система развертывания (например, Kubernetes) скачивает новую версию образа из реестра и обновляет приложение.

Пример пайплайна в GitHub Actions:

name: Docker CI/CD

on:
  push:
    branches: [ "main" ]

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: your-username/your-app:latest

Для оптимизации размера и безопасности образов часто применяют multi-stage builds в Dockerfile.

Ответ 18+ 🔞

Так, слушай, вот эта вся хуйня с Docker в CI/CD — это вообще огонь, если честно. Раньше же было: «У меня работает, а у тебя нет, потому что у тебя кривые руки». А теперь — нахуй! Берём Dockerfile, и всё, пиздец, консистентность.

Смотри, как это обычно ебётся по шагам, чтоб ты понимал масштаб распиздяйства, которое мы победили:

  1. Собираем образ (docker build). Берём наш священный Dockerfile, где всё прописано, и ваяем из него этакий законсервированный срач — прости, образ — со всеми библиотеками, зависимостями и прочей требухой. Больше никаких «а у меня версия Python 3.11, а у тебя 3.7, поэтому всё ебнулось».
  2. Гоняем тесты. Ага, не просто так! Запускаем контейнер из свежеиспечённого образа и внутри него уже выполняем все эти unit-тесты, интеграционные и прочие страдания. Если там всё зелёное — значит, в продакшене будет работать ровно так же, потому что окружение одно и то же, ёпта! Никаких сюрпризов.
  3. Толкаем в реестр (docker push). Тесты прошли? Отлично, теперь этот образ — наше сокровище. Закидываем его в какой-нибудь Docker Hub, GitLab Registry или AWS ECR. Пусть лежит, ждёт своего звёздного часа.
  4. Разворачиваем. А вот тут уже наша система оркестрации (типа Kubernetes) подъезжает, говорит: «О, новый образ!» — выкачивает его и тихо, мирно обновляет приложение без лишних истерик. Красота, блядь.

Вот тебе, кстати, реальный пример, как это может выглядеть в GitHub Actions, чтоб ты не думал, что я пизжу:

name: Docker CI/CD

on:
  push:
    branches: [ "main" ]

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: your-username/your-app:latest

И ещё одна хитрая хуйня — multi-stage builds. Это чтобы твой итоговый образ не весил как чугунный мост, со всеми компиляторами и промежуточным говном внутри. Собираешь в одном образе, а на выходе — чистенький, лёгкий артефакт. И безопаснее, и быстрее. Вообще, ебать, гениально просто.