Продолжаю эксперименты с CI/CD. После первого опыта деплоя занялся разработкой CI/CD для Mailbroker. В этом сервисе задействованы сразу несколько контейнеров, а значит простым «docker run» не обойтись — слишком длинная команда получится.

Проблематика

Я знаю, что docker-compose — это элегантный способ сделать сразу все:

  • запустить контейнеры
  • передать переменные окружения
  • создать сети и связать контейнеры между собой

Сейчас docker-compose.yml я использую для локальной разработки. А также на «lamp-сборке», которая работает на сервере уже несколько лет. Но использовать в полноценном CI/CD такой файл нельзя:

  • монтирование каталогов проекта в контейнеры — удобно для разработки, но на продакшене такой способ не приемлем
  • используется «build», что также тянет зависимость в виде файлов проекта

Хотелось бы иметь такой файл, который, с одной стороны, описывает все связи, а с другой — не тянет ненужные зависимости.

И, наконец, я изобрел такой способ, а также провел эксперимент.

docker-compose.deploy.yml

Вот так выглядит оригинальный файл из отладочного проекта «hello»:

version: "3.8"

services:

  nginx:
    build:
      dockerfile: ./docker/nginx/Dockerfile
      context: .
    volumes:
      - ./docker/nginx/conf.d/:/etc/nginx/conf.d/
      - ./public/:/var/www/
    ports:
      - ${HTTP_PORT}:80
    restart: always

Если просто скопировать этот файл в отдельный каталог и попробовать запустить, ничего не получится: нет необходимых файлов, и не определена переменная HTTP_PORT.

А что если для CI/CD сделать отдельный файл? И он был сделан:

version: "3.8"

services:

  nginx:
    image: hello_nginx:${CI_COMMIT_SHA}
    ports:
      - ${HTTP_PORT}:80
    environment:
      - HTTP_PORT=${HTTP_PORT}
    restart: always

В нем сборка образа заменена запуском конкретной версии на основе CI_COMMIT_SHA, значит есть возможность отката назад путем простого перезапуска прошлого деплоя. А переменная HTTP_PORT берется из GitLab variables, которые я проставил в настройках проекта.

Теперь — как это работает. Сокращенное содержимое файла .gitlab-ci.yml:

stages:
  - build
  - deploy

build docker image:
  stage: build
  tags:
    - docker-image-builder
  script:
    - echo "Commit SHA ${CI_COMMIT_SHA}" >> public/index.html
    - docker build . -f docker/nginx/Dockerfile -t hello_nginx:${CI_COMMIT_SHA} -t hello_nginx:latest

run container from docker-compose.deploy.yml:
  stage: deploy
  tags:
    - deploy
  script:
    - cp docker-compose.deploy.yml /tmp/
    - cd /tmp/
    - docker-compose -f docker-compose.deploy.yml up -d nginx

На первом этапе происходит сборка и тегирование образа hello_nginx:${CI_COMMIT_SHA}. На втором этапе файл копируется во временный (!) каталог, и происходит запуск с подстановкой переменных окружения. Это работает.

Но что произойдет, если перезагрузиться, ведь при этом очищается каталог /tmp? И я перезагрузил сервер. На удивление, все продолжило работать. Если бы я разбирался в деталях, как работает docker-compose, вопроса бы не было. Но я не знаю, и поэтому пришлось ставить эксперимент.

А что насчет переменных окружения? Они также были сохранены в контейнере:

docker exec f7f60beaedff env
# вывод
...
HTTP_PORT=8005
...

Финальный вариант

Чтобы резюмировать все стадии деплоя с использованием docker-compose, я составил такой шорт-лист:

  • прописать переменные в настройках проекта GitLab
  • пипелина: собирать образы с тегами (docker build . -f docker/nginx/Dockerfile -t hello_nginx:${CI_COMMIT_SHA})
  • создать отдельный docker-compose.deploy.yml, в котором: использовать теги образов (image: hello_nginx:${CI_COMMIT_SHA}), передавать ports и environment
  • пипелина: запускать сборку так: docker-compose -f docker-compose.deploy.yml up -d nginx

От debitos

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *