Перейти к содержанию

Пример сборки backend-сервиса


Dockerfile

FROM python:3.13-slim-trixie AS builder

RUN apt update -y; apt install -y libpq-dev gcc; rm -rf /var/lib/apt/lists/*;
RUN pip install poetry

ENV POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_IN_PROJECT=1 \
    POETRY_VIRTUALENVS_CREATE=1 \
    POETRY_CACHE_DIR=/tmp/poetry_cache

WORKDIR /opt
COPY pyproject.toml poetry.lock ./
RUN poetry install --without dev --no-root && rm -rf $POETRY_CACHE_DIR

FROM python:3.13-slim-trixie AS runtime

ENV VIRTUAL_ENV=/opt/.venv PATH="/opt/.venv/bin:$PATH"
RUN apt update -y; apt install -y libpq-dev; rm -rf /var/lib/apt/lists/*;

COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
WORKDIR /opt
COPY src src
COPY docker/entrypoint.sh entrypoint.sh
RUN chmod +x entrypoint.sh

ENTRYPOINT ["/opt/entrypoint.sh"]

Dockerfile обычно лежит в ./docker/Dockerfile (указывается в DOCKERFILE_PATH).


Как переменные попадают в билд

В workflow.rules для ветки задаётся:

variables:
  SERVICE_NAME: bi-backend
  DOCKERFILE_PATH: ./docker/Dockerfile
  BUILD_ARGS: ""
  CI_TRIGGER_SOURCE: app

И для конкретного окружения:

- if: '$CI_COMMIT_BRANCH == "stage"'
  variables:
    STAND: stage
    NAMESPACE: planning
    RELEASE_NAME: ${SERVICE_NAME}
    HELM_SET_ARGS: >-
      --set universal-chart.services.main.image.name.stage=${IMAGE_NAME}
      --set universal-chart.global.env=stage

BUILD_ARGS — строка, вставляемая в docker build:

docker build -f ${DOCKERFILE_PATH} ${BUILD_ARGS} -t ${IMAGE_NAME} .

Если нужно передать аргументы в Dockerfile:

BUILD_ARGS: "--build-arg CI_COMMIT_SHORT_SHA=${CI_COMMIT_SHORT_SHA}"

Формирование IMAGE_NAME

IMAGE_NAME = ${CR}/${RELEASE_NAME}:${STAND}_${CI_COMMIT_SHORT_SHA}

Пример: cr.yandex/crp.../bi-backend:stage_abc123def

Этот тег пушится в registry и подставляется в Helm через HELM_SET_ARGS.


Полный flow сборки образа

  1. workflow.rules подставляет переменные для ветки (STAND, NAMESPACE, HELM_SET_ARGS)

  2. Опциональные джобы lint/typecheck — если ошибка, pipeline останавливается. Поведение можно переопределить с помощью опции allow_failure.

  3. build (extends .base_docker):

  4. .base_docker делает docker login и выставляет переменную DOCKER_BUILDKIT=1
  5. Выполняется: bash docker build -f ./docker/Dockerfile ${BUILD_ARGS} -t ${IMAGE_NAME} . docker push ${IMAGE_NAME}

  6. build_chart пакует и пушит Helm chart в HELM_REGISTRY

  7. deploy job: bash helm upgrade -i bi-backend .helm --atomic -n=${NAMESPACE} \ -f .helm/values.yaml ${HELM_SET_ARGS}

где HELM_SET_ARGS содержит: --set universal-chart.services.main.image.name.stage=${IMAGE_NAME}


Примеры кастомных джоб

lint:
  stage: lint
  image: ghcr.io/astral-sh/uv:0.7.3-python3.13-bookworm-slim
  script:
    - uv sync --locked
    - uv run ruff check src
    - uv run ruff format src --check
  rules:
    - if: '$CI_COMMIT_BRANCH'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

typecheck:
  stage: lint
  image: ghcr.io/astral-sh/uv:0.7.3-python3.13-bookworm-slim
  script:
    - uv sync --locked
    - uv run mypy src
  rules:
    - if: '$CI_COMMIT_BRANCH'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  allow_failure: true

Эти джобы работают отдельно от workflow — у них свои rules и среда выполнения.


values.yaml для backend

universal-chart:
  global:
    env: stage

  services:
    main:
      enabled: true

      deployment:
        enabled: true
        name:
          _default: bi-backend
        replicaCount:
          _default: 1
          production: 3
        port:
          _default: 80
        resources:
          requests:
            memory:
              _default: "1024Mi"
            cpu:
              _default: "500m"

      image:
        name:
          _default: cr.yandex/crp3ccidau046kdj8g9q/bi-backend
        pullPolicy:
          _default: IfNotPresent

      service:
        enabled: true
        type:
          _default: ClusterIP
        port:
          _default: 8080
        targetPort:
          _default: 8000

      envs:
        - name: SETTINGS_DEBUG
          value:
            _default: "0"
        - name: AUTH_HOST
          value:
            _default: "https://stage.sarex.io"
            preprod: "https://preprod.sarex.io"
            production: "https://lk.sarex.io"

      secretEnvs:
        - name: DB_PASSWORD
          secretName:
            _default: "ya-pg-secret"
          secretKey: "password"

      volumes:
        _default:
          - name: ya-ca-cert
            mountPath:
              _default: /opt/config
            readOnly:
              _default: true
            configMap:
              name:
                _default: ya-ca-cert
              items:
                - key: ca.crt
                  path:
                    _default: ca.crt

      commitSha: ""
      gitlabUri: ""
      gitlabJobUrl: ""
      owner: "team-abc"

  ingress:
    enabled: false

Важно: секреты и ConfigMap создаются внешними средствами, чарт только ссылается на них.