Причины и последствия

С надеждой

dev.family — аутсорсинговый разработчик, специализирующийся на экокомм и пищевых технологиях, занимающийся мобильным и кроссплатформенным веб-сайтом.

При этом у нас около 10 проектов в разработке, к которым добавляются старые, которые мы поддерживаем. Когда мы проектировали весь этот описанный в статье «ролловер», мы позаботились о том, чтобы после каждого коммита на ветке master она автоматически расширялась. Так что, если хотите, можете самостоятельно развернуть другие ветки, развернуть на продакшн. В нашей новой картине мира нам не нужно было мучиться с разными версиями ПО, https можно было выдавать автоматически, а старые и ненужные ветки со временем удалялись. И все это для того, чтобы мы могли сосредоточиться на разработке и не тратить время на развертывание каждого проекта на тестовой площадке.

Как и многие, давным-давно мы все разворачивали вручную. Заходим на сервер, git pull, запускаем команды миграции. Потом вспомнили, что при миграции забыли запустить команду, что-то сломалось и ушли.

А в процессе можно было и растянуть при обновлении сайта, потому что, например, код уже можно было обновить, а миграцию в базе — нет. И не дай Бог, если у нас будет dev, stage, prod! Подойдите к каждому, переверните его руками. Но как-то захотелось продлевать несколько веток параллельно и тоже пришлось делать это вручную… Кошмар, вспоминать страшно, а ностальгия приятная.

Со временем это стало вызывать кучу проблем:

  • разные версии php, node.js;

  • некоторые приложения требовали установки утилит прямо в систему;

  • разница между местной средой и производством. То, что работало во время разработки, могло сломаться после развертывания в рабочей среде;

  • было сложно запускать старые проекты, у которых было много зависимостей

В любом случае, мы решили, что пора все это поместить в докер…

Что мы наделали?

В 2018 году мы обратились к Docker, потому что увидели в нем ключ к решению этих проблем. Мы решили вводить его постепенно, и только в новых проектах. Поэтому процесс затянулся. И когда практики Kubernetes и ci/cd начали набирать популярность, их решили использовать для тестовой среды.

ЧИТАТЬ   Книга: Через тернии к звездам. Прогноз от 15.06.2023 | LiteFinance

Причины использования Kubernetes в наших проектах:

  • автоматическая выдача сертификатов;

  • масштабирование;

  • высокая доступность;

  • простота управления контейнером.

Kubernetes предоставил готовое решение для этих задач и позволил нам решить соответствующие проблемы быстро и эффективно.

Для ci/cd мы использовали GitLab Runner, так как мы храним проекты в собственном экземпляре GitLab. Kubernetes был разработан с использованием microk8s.

Развертывание происходило с помощью штанги. Это решение просуществовало долгое время, но породило много проблем:

  • Для надежной работы кластера Kubernetes требуется минимум 3 узла, у нас был только один.

  • Быстрое развитие Kubernetes и трудности, связанные с его обновлением.

  • Огромные ямлы, которые сложно читать, создавать и поддерживать.

  • Требуется много времени, чтобы учиться и решать всевозможные проблемы. У нас нет специальной команды devops.

В процессе мы обнаружили, что Kubernetes не подходит для всех наших задач и всех наших потребностей. Его поддержка требовала много дополнительных усилий и времени, а также значительного объема ИТ-ресурсов. Мы сняли его с производства и продолжили поиск оптимальных решений для каждого нашего проекта.

Однако опыт использования Kubernetes оказался очень полезным, и мы всегда помним, что при увеличении нагрузки на приложение мы можем обратиться к этому инструменту.

Чем мы закончили?

Устав от постоянных проблем и сложности Kubernetes, они начали искать альтернативу. Рассматривались Swarm и другие решения, но ничего лучше Docker Compose не нашлось. Они остановились там.

За что? Да потому, что каждый разработчик в компании знает, как работать с Docker Compose. С ним сложно выстрелить себе в ногу, его легко обслуживать и развертывать. А его недостатки практически не заметны на наших проектах.

Минусы считаю:

  • Ограничение ресурсов: Docker Compose не позволяет ограничивать потребление ресурсов в пределах одного контейнера.

  • Нет обновлений без простоев. При обновлении приложения в Docker Compose оно становится недоступным на несколько секунд.

Потом начали искать решение для развертывания в среде разработки. Главное, что нам нужно было, чтобы каждая ветка была доступна по своему адресу и получила сертификат для https.

Готового решения найти не удалось, поэтому мы реализовали собственный деплойер.

Что мы нашли для развертывания?

Наша диаграмма развертывания выглядит следующим образом:

ЧИТАТЬ   Образ будущего. Как изменится жизнь и город через 30 лет?

Развертывание клиента и развертывание сервера — это два двоичных файла, написанных на golang. Клиент упакован в докер и помещен в наш реестр GitLab.

Все, что делает клиент, это берет все файлы в каталоге и отправляет их на сервер развертывания через http. Вместе с файлами он также отправляет переменные GitLab. В ci/cd это выглядит так:

review:
  image: gitlab/company/ci-deployer/client:latest
  stage: review
  script:
    - deploy

//прочий код
function deploy() {
    mv ci/dev /deploys;

    /golang/main up; 
}

Сервер развертывания работает как демон и принимает файлы через http. Базовая файловая структура выглядит так:

config.json

Содержит конфигурацию. Пример:

{
  "not_delete_old" : false, //проекты, в которые не пушили 28 дней удаляются
  "cron": {
    "enable": true, //настройка крон команд
    "commands": [
      {
        "schedule": "* * * * *",
        "task": "cron" //будет выполнена таска из Taskfile.yml​
       }
    ]
  }
}

Taskfile.yml

Содержит задачи, которые будут выполняться во время развертывания. Утилита установлена ​​в системе.

файл_задачи.dev

version: '3'

tasks:
  up:
    cmds:
      - docker-compose pull
      - docker-compose up -d --remove-orphans
      - docker-compose exec -T back php artisan migrate --force
      - docker-compose exec -T back php artisan search:index

  down:
    cmds:
      - docker-compose down

  cron:
    cmds:
      - docker-compose exec -T back php artisan schedule:run

  tinker:
    cmds:
      - docker-compose exec  back php artisan tinker

.env

Переменные среды для Docker Compose.

COMPOSE_PROJECT_NAME={{ .BaseName }}
VERSION={{ .Version }}
REGISTRY={{ .RegImage }}

docker-compose.yaml

version: "3.8"

services:
  front:
    networks:
      - traefik
    restart: always
    image: ${REGISTRY}/front:${VERSION}
    env_file: .env.fronted
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.{{ .BaseName }}.rule=Host(`vendor.{{ .HOST }}`)"
      - "traefik.http.routers.{{ .BaseName }}.entrypoints=websecure"
      - "traefik.http.routers.{{ .BaseName }}.tls.certresolver=myresolver"
      - "traefik.http.routers.{{ .BaseName }}.service={{ .BaseName }}"
      - "traefik.http.services.{{ .BaseName }}.loadbalancer.server.port=3000"
networks:
  traefik:
    name: app_traefik
    external: true

После принятия файлов сервер начинает свою работу. На основе переменных GitLab, которые включают имя ветки, имя проекта и т. д., создаются переменные развертывания. BaseName создается таким образом.

return fmt.Sprintf("%s_%s_%s",
  receiver.EnvGit["CI_PROJECT_NAMESPACE"], //группа проекта
  receiver.EnvGit["CI_PROJECT_NAME"], //название проекта
  receiver.EnvGit["CI_COMMIT_BRANCH"], //название ветки
)

Все файлы рассматриваются как шаблоны golang. Поэтому, например, в docker-compose.yaml вместо {{.BaseName}} будет заменено уникальное имя, созданное для развертывания. {{.HOST}} также создается на основе имени ветки.

Затем для филиала создается база данных, если она еще не создана. Если ветка отличается от основной, то база данных создается не с нуля, а клонируется основная база данных. Это удобно, потому что миграции веток не влияют на main. Но основные данные в ветке могут быть полезны.

ЧИТАТЬ   Четверть россиян хотели бы стать фермерами

После этого готовые файлы помещаются в директорию, по пути

/группа проекта/название проекта/название ветки​

И команда выполняется в системе задач, которая описана в файле Taskfile.yml. В нем уже описаны команды для конкретного проекта.

После этого деплой становится доступным в сети traefik, который автоматически запускает к нему прокси-трафик и выдает сертификат let’s encrypt.

Что мы имеем в итоге?

  1. Такой подход значительно упростил разработку: стало проще использовать различные новые утилиты, которые легко добавить как дополнительный сервис в docker-compose.yaml. И теперь каждый разработчик понимает, как работает тестовая среда.

  2. Эту систему легко настроить специально под наши нужды. Например, нам нужно было добавить возможность запуска cron. Решение очень простое. Мы создаем конфигурацию через config.json, разбираем ее в структуру golang и запускаем cron внутри сервера, который можно менять динамически.

  3. Реализована еще одна идея: деактивировать неактивные проекты, которые не пушились более 28 дней. Для этого мы создали файл с данными о последнем развертывании.

"XXX_NAME": {
    "user": "xxxx",
    "slug": "xxxx",
    "db": "xxx",
    "branch": "main",
    "db_default": "xxxxx",
    "last_up": "2023-04-07T09:06:15.017236822Z",
    "version": "cb2dd08b02fd29d57599d2ac14c4c26200e3c827",
    "dir": "/projects/xxx/backend/main",
  },

Также cron внутри server deployer проверяет этот файл раз в сутки, если видит там неактивный проект, переходит в dir и запускает команду down. Ну, он удаляет базу данных, если это не главная ветка. А для информативности сервер после выполнения работы отдает логи клиенту, который отображает их в GitLab.

Также cron внутри server deployer проверяет этот файл раз в сутки, если видит там неактивный проект, переходит в dir и запускает команду down. Ну, он удаляет базу данных, если это не главная ветка. А для информативности сервер после выполнения работы отдает логи клиенту, который отображает их в GitLab.

Так выглядит, когда все хорошо

ххх - название проекта

ххх — название проекта

Ну или так, когда что-то пошло не так 🙂


Было бы здорово, если бы вы поделились своим опытом в комментариях.

Source

От admin