Как создаются и подтягиваются переменные, содержащие версии приложений, в CI/CD?

«Как создаются и подтягиваются переменные, содержащие версии приложений, в CI/CD?» — вопрос из категории CI/CD, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В DevOps-практиках версии приложений управляются автоматически и декларативно. Вот основные подходы:

1. Источник истины для версии:

  • Git-теги (Semantic Versioning - SemVer): Самый распространенный способ. Версия определяется тегами в репозитории (v1.2.3, 2.0.0-rc.1).
  • Файлы метаданных: Например, package.json для Node.js, pyproject.toml/setup.py для Python, pom.xml для Java. Версия хранится внутри проекта.
  • Номер сборки из CI/CD: Уникальный инкрементный номер, генерируемый системой CI/CD (например, $CI_PIPELINE_IID в GitLab).

2. Как версия "подтягивается" в процесс CI/CD:

Сценарий A: Версия из Git-тега (рекомендуемый для релизов)

# Скрипт в .gitlab-ci.yml или GitHub Actions workflow
# Извлекаем версию из последнего тега
VERSION=$(git describe --tags --abbrev=0)
# Или для коммита с тегом:
VERSION=${CI_COMMIT_TAG}

# Передаем версию как переменную окружения в следующие шаги
export APP_VERSION=${VERSION}

# Используем при сборке Docker-образа
docker build --build-arg VERSION=${APP_VERSION} -t my-registry/app:${APP_VERSION} .

# Или для создания артефакта
echo "Building version: ${APP_VERSION}"

Сценарий B: Версия из файла проекта + хэш коммита (для dev-сборок)

# Для Python проекта (из pyproject.toml)
VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['project']['version'])")
COMMIT_SHA=$(git rev-parse --short HEAD)
FULL_VERSION="${VERSION}-dev+${COMMIT_SHA}"

# Используем для тегирования образа
docker tag my-app:latest my-registry/app:${FULL_VERSION}

Сценарий C: Использование специализированных инструментов (например, для Go)

# Можно использовать `goreleaser` или встроенные флаги `ldflags`
go build -ldflags "-X main.version=$(git describe --tags)" -o app .

3. Инъекция версии в артефакты и runtime:

  • В Docker-образ: Через ARG и ENV.
    ARG VERSION="unknown"
    ENV APP_VERSION=$VERSION
    # Далее версия доступна внутри контейнера как $APP_VERSION
  • В бинарники/приложения: Через переменные сборки (как в примере с Go выше) или запись в файл version.json.
  • В Helm-чарты: Версия передается через --set или в values.yaml и подставляется в шаблоны, а также используется как метка (label) для ресурсов Kubernetes.
    helm upgrade my-app ./chart --set image.tag=${CI_COMMIT_TAG}

4. Best Practices:

  • Автоматизация: Версия должна определяться автоматически в CI/CD, а не вручную.
  • Идемпотентность: Сборка с одним и тем же тегом (например, v1.0.0) должна производить бинарно идентичный артефакт.
  • Прозрачность: Версия должна быть легко определяема у запущенного приложения (через эндпоинт /version, метки в Kubernetes, переменную окружения).
  • Соответствие SemVer: Использование семантического версионирования облегчает понимание характера изменений (мажорная/минорная/патч-версия).