CI для фронтенда: Gitlab, Traefik, Docker

В каждом уважающем себя проекте должны участвовать QA инженеры. Ежедневно перед ними будет стоять задача проверки выполнения задач в отдельных ветках. Очень часто процесс перехода на нужную ветку, сборки и тестирования занимает много времени, к тому же, локально не всегда возможно полностью воссоздать максимально идентичное боевому окружение.
Цель данной статьи — показать простую технику настройки стенда на несколько веток. Данная статья написана разработчикам от разработчика, поэтому вряд ли будет представлять существенный интерес для профессиональных DevOps инженеров.

Требования:

  • Gitlab (bare metal/cloud)
  • Выделенный сервер
  • Свободный домен
Шаг первый: настройка Gitlab
  1. Установите Gitlab Runner на Вашем выделенном сервере
  2. Создайте воркера, поддерживающего сборки docker образов
  3. Включите Container Registry
Шаг второй: настройка сервера
  1. Установите Docker
  2. Установите Compose
  3. Создайте пользователя:
    # создаем пользователя для деплоя образов, разрешаем ему работу с докером
    $ sudo adduser deployer
    $ sudo groupadd docker
    $ sudo usermod -aG docker deployer
    # генерим ssh ключи
    $ su deployer
    $ ssh-keygen -t rsa
    (when asked for a passphrase, enter no passphrase)
    # разрешаем подключение к серверу с только что созданным ключом
    $ cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
    
  4. Настройте traefik (сервер, проксирующий запросы к Docker контейнерам на базе их ярлыков(лейблов)):
    $ sudo mkdir -p /opt/traefik
    $ docker network create web
    # файлы настроек docker-compose & traefik упрощены до предела и должны быть достаточно понятными
    

    /opt/traefik/docker-compose.yml

    version: '2'
    services:
      traefik:
        image: traefik:1.4.6
        restart: always
        ports:
          - 80:80
        networks:
          - web
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - ./traefik.toml:/traefik.toml
        container_name: traefik
    networks:
      web:
        external: true

    /opt/traefik/traefik.toml
    Замените DOMAIN.COM на свой домен.

    debug = true
    checkNewVersion = true
    logLevel = "ERROR"
    defaultEntryPoints = ["http"]
    [entryPoints]
      [entryPoints.http]
      address = ":80"
    [retry]
    [docker]
    endpoint = "unix:///var/run/docker.sock"
    domain = "DOMAIN.COM"
    watch = true
    exposedbydefault = false
    
  5. Запустите traefik:
    cd /opt/traefik && docker-compose up -d
  6. Добавьте A запись для DOMAIN.COM вида ‘*’ — IP выделенного сервера.
Шаг третий: подготовка репозитория
  1. Добавьте в корень репозитория Dockerfile:
    FROM node:8.9 as build-deps
    WORKDIR /usr/src/app
    COPY package.json ./
    RUN npm i
    COPY . ./
    RUN npm run build
    FROM nginx:1.12-alpine
    COPY --from=build-deps /usr/src/app/dist /usr/share/nginx/html
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]
    

    Измените

    npm run build

    на вашу команду сборки. Аналогично отредактируйте путь

    /usr/src/app/dist

    изменив dist на вашу директорию сборки.
    Подробнее почитать про структуру и команды докерфайа можно тут.

  2. Добавьте .gitlab-ci.yml файл с секцией сборки образа:
    image: docker:stable
    variables:
      DOCKER_HOST: tcp://docker:2375/
      DOCKER_DRIVER: overlay2
      GITLAB_DOMAIN: gitlab.com
    services:
      - docker:dind
    stages:
      - build
    build_staging:
      stage: build
      script:
        - export GITLAB_DOMAIN=gitlab.com
        - export CONTAINER_IMAGE=$GITLAB_DOMAIN/frontend/main/image
        - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $GITLAB_DOMAIN
        - docker pull $CONTAINER_IMAGE:$CI_COMMIT_REF_SLUG || true
        - export URL="Host:${CI_COMMIT_REF_NAME}.tests.DOMAIN.COM"
        - docker build -t $CONTAINER_IMAGE:$CI_COMMIT_REF_SLUG . --label "traefik.backend=${CI_COMMIT_REF_NAME}" --label "traefik.frontend.rule=${URL}"
        - docker push $CONTAINER_IMAGE:$CI_COMMIT_REF_SLUG
    
  3. Добавьте приватный ключ [email protected] в настройки репозитория(Settings -> CI/CD -> Variables) как SSH_PRIVATE_KEY
  4. Добавьте deploy секции:
    stages:
      - build
      - deploy
    /// ....
    deploy_staging:
      stage: deploy
      image: kroniak/ssh-client:3.6
      script:
        - mkdir ~/.ssh
        - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
        - eval $(ssh-agent -s)
        - ssh-add <(echo "$SSH_PRIVATE_KEY")
        - ssh [email protected] "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $GITLAB_DOMAIN"
        - ssh [email protected] "docker stop frontend_${CI_COMMIT_REF_SLUG}" || true
        - ssh [email protected] "docker rm frontend_${CI_COMMIT_REF_SLUG}" || true
        - ssh [email protected] "docker rmi GITLAB-DOMAIN.COM/frontend/main/image:${CI_COMMIT_REF_SLUG}" || true
        - ssh [email protected] "docker run --name frontend_${CI_COMMIT_REF_SLUG} --network=web -d $GITLAB_DOMAIN/frontend/main/image:${CI_COMMIT_REF_SLUG}"
      environment:
        name: review/$CI_COMMIT_REF_NAME
        url: http://${CI_COMMIT_REF_NAME}.tests.DOMAIN.COM
        on_stop: stop_staging
    stop_staging:
      stage: deploy
      image: kroniak/ssh-client:3.6
      variables:
        GIT_STRATEGY: none
      script:
        - mkdir ~/.ssh
        - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
        - eval $(ssh-agent -s)
        - ssh-add <(echo "$SSH_PRIVATE_KEY")
        - ssh [email protected] "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN GITLAB-DOMAIN.COM"
        - ssh [email protected] "docker stop frontend_${CI_COMMIT_REF_SLUG}" || true
        - ssh [email protected] "docker rm frontend_${CI_COMMIT_REF_SLUG}" || true
        - ssh [email protected] "docker rmi $GITLAB_DOMAIN/frontend/main/image:${CI_COMMIT_REF_SLUG}" || true
      when: manual
      environment:
        name: review/$CI_COMMIT_REF_NAME
        action: stop

Приведенных выше шагов достаточно для получения следующего результата:

  • Каждый новый коммит в ветку иницирует сборку и развертку последнего актуального кода по адресу %branch_name%.tests.DOMAIN.COM
  • В Gitlabе включен механизм окружений, позволяющий в пару касаний создавать/удалять/открывать задеплоенные образы

Следующие шаги:

  • Настроить отдельную секцию для сборок мастера. В случае мастера разумно тэгать образ через COMMIT_SHA
  • Добавить конфиги к nginxу в докере
  • Пробросить параметры сборки бандла через ARG и ENV механизмы докерфайла
  • Настроить использование кэша из образа для сборок