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

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


Dockerfile

FROM node:20.11.1-bookworm as node

ARG ENDPOINT=prod
ARG NPM_TOKEN
ARG SENTRY_URL=""

WORKDIR /opt/react_client
COPY package.json .npmrc ./
RUN npm install
COPY . .
RUN NPM_TOKEN=$NPM_TOKEN ENDPOINT=$ENDPOINT SENTRY_URL=$SENTRY_URL npm run build

FROM nginx:mainline-alpine-otel as nginx

COPY --from=node /opt/react_client/static/ /opt/react_client/static/
COPY nginx/nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

Что делает Dockerfile

  1. Стейдж node: устанавливает зависимости и собирает приложение
  2. ARG — объявляет build-time аргументы для docker build --build-arg
  3. .npmrc содержит //nexus...:_authToken=${NPM_TOKEN} для доступа к приватному npm реджистри
  4. npm run build собирает статику с нужным ENDPOINT

  5. Стейдж nginx: берёт собранную статику и настраивает nginx


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

В .gitlab-ci.yml:

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

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

- if: '$CI_COMMIT_BRANCH == "stage"'
  variables:
    STAND: stage
    NAMESPACE: planning
    BUILD_ARGS: "--build-arg ENDPOINT=stage --build-arg NPM_NEXUS_TOKEN=${NPM_NEXUS_TOKEN}"
    HELM_SET_ARGS: >-
      --set universal-chart.services.frontend.image.name.stage=${IMAGE_NAME}
      --set universal-chart.global.env=stage

BUILD_ARGS подставляется в команду сборки:

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

Про NPM_TOKEN и чувствительные данные

  • NPM_TOKEN, NPM_NEXUS_TOKEN, SENTRY_URL — чувствительные данные
  • Передаются из переменных на уровне группы
  • В .gitlab-ci.yml используются как ${NPM_NEXUS_TOKEN} без явного указания значений

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

  1. workflow.rules выбирает переменные для ветки

  2. build job (extends .base_docker):

  3. docker login в cr.yandex через SAREX_REGISTRY_KEY
  4. Команда: bash docker build -f ./Dockerfile \ --build-arg ENDPOINT=stage \ --build-arg NPM_NEXUS_TOKEN=${NPM_NEXUS_TOKEN} \ -t ${IMAGE_NAME} .
  5. docker push ${IMAGE_NAME}

  6. build_chart упаковывает и публикует helm chart

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


values.yaml для frontend

universal-chart:
  global:
    env: stage

  services:
    frontend:
      enabled: true

      deployment:
        enabled: true
        name:
          _default: bi-frontend
        replicaCount:
          _default: 1
        port:
          _default: 80
        resources:
          requests:
            cpu:
              _default: "100m"
            memory:
              _default: 128Mi
        probes:
          liveness:
            enabled:
              _default: true
            type:
              _default: httpGet
            httpGet:
              path:
                _default: /
              port:
                _default: 80
            periodSeconds:
              _default: 10
          readiness:
            enabled:
              _default: true
            type:
              _default: httpGet
            httpGet:
              path:
                _default: /
              port:
                _default: 80

      image:
        name:
          _default: cr.yandex/crp3ccidau046kdj8g9q/bi-frontend:latest
          stage: cr.yandex/crp3ccidau046kdj8g9q/bi-frontend:stage
        pullPolicy:
          _default: IfNotPresent

      service:
        enabled: true
        name:
          _default: bi-frontend-service
        port:
          _default: 80
        targetPort:
          _default: 80
        type:
          _default: ClusterIP

      imagePullSecrets:
        enabled:
          _default: false
        name:
          _default: dockerhub

      envs: []

      volumes:
        _default: []

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

HELM_SET_ARGS для frontend

Обратите внимание на путь к образу — для frontend используется services.frontend:

HELM_SET_ARGS: >-
  --set universal-chart.services.frontend.image.name.stage=${IMAGE_NAME}
  --set universal-chart.global.env=stage
  --set universal-chart.services.frontend.commitSha=${CI_COMMIT_SHA}
  --set universal-chart.services.frontend.gitlabUri=${CI_PROJECT_URL}
  --set universal-chart.services.frontend.gitlabJobUrl=${CI_JOB_URL}
  --set universal-chart.services.frontend.owner=${CI_PROJECT_NAMESPACE}

Это отличается от backend, где используется services.main.


Кастомные джобы для frontend

Пример сборки отдельного образа для contour:

build_contour_tag:
  variables:
    CONTAINER_IMAGE_CONTOUR_TAG: cr.yandex/.../frontend-dev:contour_${CI_COMMIT_TAG}
  extends: .base_docker
  script:
    - docker build --build-arg NPM_TOKEN=${NPM_TOKEN} --build-arg ENDPOINT=contour -f Dockerfile.contour -t ${CONTAINER_IMAGE_CONTOUR_TAG} .
    - docker push ${CONTAINER_IMAGE_CONTOUR_TAG}
  rules:
    - if: '$CI_COMMIT_TAG'
  allow_failure: false

Эта джоба: - Определяет свои rules независимо от workflow - Использует отдельный Dockerfile - Запускается только для тегов