====== Gitlab ====== Документация: https://docs.gitlab.com/ee/ci/\\ Get started with GitLab CI/CD: https://docs.gitlab.com/ee/ci/\\ Learn GitLab with tutorials: https://docs.gitlab.com/ee/tutorials/\\ GitLab with Git Essentials - Hands-On Lab: [[https://handbook.gitlab.com/handbook/customer-success/professional-services-engineering/education-services/gitbasicshandsonlab3/|Use GitLab To Merge Code]]\\ Удобен тем, что содержит всё необходимое, не нужно использовать кучу различных сервисов: * Собственно, сам git-репозиторий * wiki * Issue board с привязкой к событиями внутри Гитлаба, этакая канбан-доска. Для каждого этапа можно создать список задач, а также метки для удобной фильтрации. Для каждого тикета можно назначить ответственного и этап. * Container registry - хранилище образов контейнеров * Package registry - хранилище ПО * CI/CD ([[https://habr.com/ru/company/ruvds/blog/522334/|Разница между Jenkins и Gitlab CI]]) Ещё один комбайн типа Гитлаба - [[https://www.jetbrains.com/ru-ru/space/|Jetbrains Space]]. * Pipeline - описывается в ''.gitlab-ci.yml'', кладётся в корень репозитория. * Job artifacts — результаты выполнения задач. * Cache dependencies — кэш зависимостей можно сохранять для ускорения сборки. * CI/CD variables — могут использоваться в ''.gitlab-ci.yml'', но лучше не задавать переменные в самом файле, а определять их в настройках CI/CD. ===== Workflow ===== ^GitLab Component ^Function ^Also Known As... ^ |Project |The core building block where work is organized, managed, tracked and delivered to help the team to collaborate and plan work in the form of issues. |Repository | |Group |A collection of projects and/or other groups. They are like folders. |Project | |Issue |An issue is part of a project. It is the fundamental planning object where the team documents the use case in the description, discusses the approach, estimates the size/effort (issue weight), tracks actual time/effort, assigns work, and tracks progress. |Story, Narrative, Ticket | |Epic |A collection of related issues across different groups and projects to help organize by theme |Initiatives, Themes | |Merge Request |The linkage between the issue and the actual code changes. Captures the design, implementation details (code changes), discussions (code reviews), approvals, testing (CI Pipeline), and security scans. |Pull Request | |Label |Used to tag and track work for a project or group and associate issues with different initiatives |Tag | |Board |A visual listing of projects and issues useful for teams to manage their backlog of work, prioritize items, and move issues to the team or specific stage in the project. |Kanban | |Milestone |A sprint or deliverable(s), helping you organize code, issues, and merge requests into a cohesive group |Release | |Roadmap |A visual representation of the various epics for the group | | **Поток** {{:service:pasted:20240529-111219.png?800}} - Issues - всё начинается с этого. Обсуждение и комментирование нововведения и его реализации. Проблема связана только с конкретным проектом, но если в группе несколько проектов, то можно видеть проблемы всех проектов на уровне группы. - Merge Request - создаётся после создания проблемы. Мерж-реквесты позволяют визуализировать и совместно работать над предлагаемыми изменениями в исходном коде, которые существуют в виде коммитов в данной ветке Git. Иногда называется pull request. - Когда мерж-реквест принят, его можно закоммитить, что запустит пайплайн. - Выполнение пайплайна - сборка, тесты и деплой на тестовое окружение. Если пайплайн завершился неуспешно, можно прочесть логи для исправления проблемы. - Review Apps - проверка приложения. Живой экземпляр новой версии приложения для ознакомления дизайнерам/менеджерам и т. п. - Peer Review and Discussion - проверка и обсуждение с коллегами на предмет отсутствия каких-то конфликтов и т. п. - Approve changes - одобрение изменений тем, у кого есть на это права. - Merge; Issue Closed; CD Pipeline runs - после одобрения изменений проблема закрывается и приложение выкатывается в прод. - Мониторинг - контроль приложения на предмет того, что изменения имеют желаемый эффект. В Гитлабе, если что, изменения можно откатить обратно. **Code Review Workflow** {{:service:pasted:20240529-115042.png}} **Дополнительные инструменты** * Snippets - хранение небольших кусков кода или текста и общий доступ к ним. * Wiki - встроена в каждый проект Гитлаба. Используется, если не хочется хранить документацию к проекту в самом репозитории, но она должна быть близко. Её можно править как через веб-интерфейс, так и локально через git. Wiki является отдельным репозиторием. * Web IDE - удобный встроенный редактор кода, можно работать прямо в браузере. [[https://about.gitlab.com/blog/2022/12/15/get-ready-for-new-gitlab-web-ide/|Обзор новых функций]]. ===== Реестры ===== * [[https://docs.gitlab.com/ee/user/packages/container_registry/index.html|Container registry]] - хранение образов контейнеров * [[https://docs.gitlab.com/ee/user/packages/package_registry/index.html|Package registry]] - хранение пакетов разных пакетных менеджеров и зависимостей. Дополнительно: [[https://docs.gitlab.com/ee/user/packages/terraform_module_registry/index.html|Terraform Module Registry]], [[https://docs.gitlab.com/ee/user/packages/dependency_proxy/index.html|Dependency Proxy]] (local proxy for frequently-used upstream images and packages. The Dependency Proxy caches both the manifest and blobs for a given image, so when you request it again, Docker Hub does not have to be contacted.) ===== Релизы ===== Это снапшот проекта для конечных пользователей, включая инсталляционные пакеты и примечания к релизу. Релизы могут создаваться в любой ветке. Создание релиза также создаёт [[https://git-scm.com/book/en/v2/Git-Basics-Tagging|git-тэг]] для пометки точки релиза в исходном коде. Релиз включает: * Снапшот исходного кода репозитория. * [[https://docs.gitlab.com/ee/user/packages/generic_packages/index.html|Пакеты (generic packages)]], созданные из артефактов. * Метаданные, ассоциированные с релиз-версией кода. * Примечания к релизу. Когда релиз создаётся, то * Гитлаб автоматически архивирует исходный код и ассоциирует его с релизом. * Гитлаб автоматически создаёт json-файл ([[https://docs.gitlab.com/ee/user/project/releases/release_evidence.html|release evidence]]), где перечислено всё, что входит в релиз, что нужно для сравнения релизов и их аудита. Можно использовать API для генерации release evidence, их может быть несколько. После релиза можно * Добавить примечания к релизу. * Добавить сообщение к гит-тэгу, ассоциированному с релизом. * [[https://docs.gitlab.com/ee/user/project/releases/#associate-milestones-with-a-release|Ассоциировать вехи]] (milestones) с релизом. * [[https://docs.gitlab.com/ee/user/project/releases/release_fields.html#release-assets|Добавить прочие ресурсы (assets)]] типа инструкций по запуску и пакетов. [[https://docs.gitlab.com/ee/user/project/releases/#create-a-release|Creating a release]]\\ [[https://docs.gitlab.com/ee/user/project/releases/release_cicd_examples.html|Release CI/CD examples]]\\ [[https://docs.gitlab.com/ee/user/project/releases/release_cli.html|Release CLI tool]] В Гитлабе есть несколько функций для удобства развертывания и теста релизов. * [[https://docs.gitlab.com/ee/ci/review_apps/|Review apps]] - предпросмотр изменений, сделанных в ветке, с помощью развёртывания динамического окружения для мердж-реквеста. [[https://www.youtube.com/watch?v=HblSiPFamDI|Youtube]] * [[https://docs.gitlab.com/ee/operations/feature_flags.html|Feature flags]] - переключатели, позволяющие включить и выключать функции для тех или иных групп пользователей. * [[https://docs.gitlab.com/ee/user/project/pages/index.html|Gitlab Pages]] - встроенный хостинг статических веб-страниц прямо из репозитория. Таким образом можно хостить портфолио, документацию, презентации и т. п. [[https://www.youtube.com/watch?v=TWqh9MtT4Bg|Youtube]] ===== Pipeline ===== Пайплайн - это файл ''.gitlab-ci.yml'' в корне git-репозитория. Состоит из последовательных этапов (stages), в каждом этапе есть задачи (jobs). По умолчанию, если не заданы условия, задачи выполняются параллельно. Если какая-то задача выполнилась с ошибкой, то весь пайплайн будет провален. Вид простейшего пайплайна: variables: ART_TOKEN: "37tui2v3pd8238PGd83g2" stages: - build - release - notify build_a: stage: build build_b: stage: build # https://docs.gitlab.com/ee/ci/yaml/index.html#only--except only: # срабатывать только, если: - master # ветка master - tags # может быть инициировано тэгом changes: - backend/* # при изменении файлов в каталоге backend - frontend/* # при изменении файлов в каталоге frontend except: # не срабатывать, если: refs: # refs: - значение по умолчанию, можно не писать. - schedules # этап вызван по расписанию - triggers # или по триггеру (через вызов API) variables: - $CI_COMMIT_MESSAGE =~ /skip tests/ # или сообщение коммита содержит текст release_a: stage: release needs: # зависимость задач друг от друга, в веб-интерфейсе GitLab есть их визуальное представление - build_a release_b: stage: release slack_notify_build_b: stage: notify only: changes: - backend/* - frontend/* script: - | curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\":building_construction: *$CI_PROJECT_NAME* ($CI_COMMIT_REF_NAME) backend - <$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/$CI_COMMIT_REF_NAME/download?job=build-backend-code-job&private_token=$ART_TOKEN|:package:>\"}" \ https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXXX/w8ydugdiug2p938 needs: - build_b Релиз - это скомпилированный и упакованный исходный код + файл json, содержащий информацию о выпуске. Дополнительно можно добавить описание и прочее, например, ссылку на объект в хранилище артефактов. Можно использовать встроенное хранилище (Package Registry), а можно другое, например, Nexus. Использование спецсимволов в строке скрипта: https://docs.gitlab.com/ee/ci/yaml/script.html (как экранировать символы, брать строку в кавычки и т. д.)\\ Переменные: https://docs.gitlab.com/ee/ci/variables/ Средство для проверки синтаксиса внутри Гитлаба - CI Lint. Токен можно сгенерировать как для пользователя в целом (Edit profile -> Access tokens), так и для отдельного репозитория (Settings -> Access tokens). ==== Heredoc и перенос строк ==== ''- |-'' сохраняет переносы строк. Удобно для heredoc, if/else и т. п.\\ ''- > '' превращает переносы строк в пробелы. Удобно для простого переноса длинной строки. release: image: node:12-stretch-slim stage: release before_script: - apt-get update && apt-get install -y curl git jq script: - |- PAYLOAD=$(cat << JSON { "branch": "master", "commit_message": "some commit message", "actions": [ { "action": "create", "file_path": "foo/bar", "content": "some content" } ] } JSON ) - > curl -X POST https://requestbin.io/1f84by61 --header 'Content-Type: application/json; charset=utf-8' --data-binary "$PAYLOAD" when: manual only: - /^release-.*$/ https://forum.gitlab.com/t/is-it-possible-to-use-a-here-document-from-within-a-multiline-command-in-gitlab-ci-yml/39329/2 ===== Задачи (jobs) ===== Могут являться частью этапа (stage). В рамках одного этапа задачи выполняются одновременно. Этап не начинается, пока не закончится предыдущий. Зависимость задач от выполнения этапов: stages: - test - build - push - deploy lint-test: stage: test before_script: - echo "prepare lint test" script: - echo "lint test" after_script: - echo "cleaing up lint test data" unit-test: stage: test before_script: - echo "prepare unit test" script: - echo "unit test" after_script: - echo "cleaing up unit test data" build-windows: stage: build script: # задача закончится ошибкой, и задача push-windows и deploy будут пропущены - wrong-echo "build windows image" - echo "tag windows image" build-linux: stage: build script: - echo "build linux image" - echo "tag linux image" push-windows: stage: push needs: - build-windows script: - echo "log in to repository" - echo "push windows image" push-linux: stage: push needs: - build-linux script: - echo "log in to repository" - echo "push linux image" deploy: stage: deploy script: - echo "deploy" {{:service:pasted:20220819-062835.png}} Можно вызвать скрипт, но сначала его нужно сделать исполняемым: script: - chmod +x ./scriptfile.sh - ./scriptfile.sh Если скрипт что-то создал в каталоге, то можно это не удалять, т. к. пайплайн запускается каждый раз по новой и не использует старого окружения. ==== only/except ==== Определяют, при каких условиях задача выполняется. # выполнять только в ветке main job: only: - main ==== Workflow rules ==== ''workflow'' определяет глобальные настройки пайплайна. Положим, чтобы не добавлять в каждую задачу ''only:'', можно задать это глобально: workflow: rules: # если ветка не main и вызывается не с помощью merge/pull request, то - if: $CI_COMMIT_BRANCH != "main" && $CI_PIPELINE_SOURCE != "merge_request_event" when: never # никогда не выполнять - when: always # в остальных случаях - выполнять ''$CI_COMMIT_BRANCH'' и ''$CI_PIPELINE_SOURCE'' - [[https://docs.gitlab.com/ee/ci/variables/predefined_variables.html|встроенные переменные]]. ==== Переменные ==== Помимо встроенных переменных, есть произвольные.\\ Настройка: Settings (внутри проекта) -> CI/CD -> Variables. Тип переменной может быть file. Например, можно занести туда конфигурационный файл для какого-либо сервиса. При вызове переменной в ней содержится полный путь к этому файлу, например, # так выводится путь echo "$CONF_FILE" /builds/user/projectname.tmp/CONF_FILE # а так - содержимое cat $CONF_FILE Можно задать переменные и внутри пайплайна variables: image_repo: docker.io/id/app image_tag: v2.0 Если блок ''variables:'' находится внутри задачи, то они действуют только внутри этой задачи. Чтобы переменные действовали во всех задачах пайплайна, блок ''variables:'' нужно перенести на верхний уровень (вровень с самими задачами). ==== Использование в Dockerfile образов из локального реестра образов ==== В пайплайне нужно добавить ''--build-arg CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE}'' в команду сборки. build: stage: build image: docker:20.10.12-dind-rootless before_script: - until docker info; do sleep 1; done - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - cd backend - > docker build --build-arg VERSION=$VERSION --build-arg CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE} --tag $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHA Dockerfile: ARG CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE} FROM ${CI_REGISTRY_IMAGE}/node:16.18-alpine AS builder ==== Кэш ==== Хранение файлов, которые могут быть использованы в соседней задаче на раннере повторно вместо того, чтобы каждый раз качать их из интернета или с сервера, например, npm/ruby/python/go-пакеты. Артефакты помещаются на сервер, кэш хранится на раннере. Если раннеров много, нужно настраивать распределённый кэш (distributed caching), хранящийся в S3. Хоть это всё равно удалённый сервер, это более эффективно, чем качать из интернета, и качается один zip-файл вместо множества мелких. Настраивается соответствующим разделом в задаче. Если не задать ключ (имя кэша), то он будет называться default. Все задачи, где совпадает ключ, будут использовать один и тот же кэш. run_unit_tests: ... cache: key: "npmdeps_$CI_COMMIT_REF_NAME" paths: - app/node_modules policy: pull-push "Pull-push" - значение по умолчанию, его можно не указывать, т. е., кэш используется и потом обновляется при необходимости. Если есть какая-то параллельно выполняющаяся задача, использующая тот же кэш, то политику нужно отрегулировать, чтобы обе задачи не пытались писать одни и те же файлы в кэш. В одной из задач ставится ''policy: pull'', чтобы она только использовала кэш без его обновления. Иногда, в больших пайплайнах, бывает выделенная задача для создания кэша, там тогда нужно ставить политику в ''push''. Команду ''npm install'' всё равно нужно оставить, чтобы отсутствие кэша или какие-то проблемы с ним не влияли на общую работу пайплайна. Если кэш создаётся с помощью Docker executor, то нужно настраивать volume на раннере, чтобы кэш не уничтожился вместе с контейнером. Путь в ''cache_dir'' и в ''volumes'' должен быть одним и тем же. [[runners]] ... executor = "docker" cache_dir = "/cache" ... [runners.docker] volumes = ["/cache"] Разница между первым и вторым запуском пайплайна. Первый раз кэш формировался, второй - использовался.\\ {{:service:pasted:20230530-081539.png}} Почистить кэш можно кнопкой "Clear runner caches" в CI/CD -> Pipelines. "Почистить" в этом случае значит просто не использовать кэшв следующих запусках пайплайна, физически кэш не удаляется. [[https://docs.gitlab.com/ee/ci/caching/#where-the-caches-are-stored|Где хранится кэш]]? [[https://docs.gitlab.com/ee/ci/caching/|Справка по кэшу]] ==== Шаблоны задач ==== Идут в комплекте с Гитлабом и могут быть включены в пайплайн в любом проекте, например, SAST (тест кода на безопасность). Шаблоны: https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates Включить SAST: sast: stage: test tags: - remote - docker include: template: Jobs/SAST.gitlab-ci.yml Можно включить до 100 шаблонов. Местоположение раздела ''include:'' в пайплайне неважно. Если в проекте ещё нет пайплайна, то в разделе CI/CD -> Pipelines можно выбрать шаблон для быстрого его создания. ===== Deploy ===== - На сервере, куда будет ставиться приложение, сгенерить ключи SSH: ''%%ssh-keygen -t ed25519 -C "dev-server"%%'' - Засунуть закрытый ключ в переменную File на Гитлабе (в конце должна быть пустая строка!) - Открытый ключ добавить на dev-server в ''~/.ssh/authorized_keys'' пользователя, под которым будет деплой. - Добавить сертификат git.ca.crt в доверенные, так же как на раннере: ''nano /etc/ssl/certs/git.ca.crt # вставить содержимое сертификата с сервера'' - В пайплайне примерно следующее deploy_to_dev: stage: deploy tags: - remote - shell before_script: - chmod 400 $SSH_PRIVATE_KEY script: - ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ssu@$DEV_SERVER_HOST " docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker run -d -p 3000:3000 $IMAGE_NAME:$IMAGE_TAG " Вышеприведённый вариант с Докером неудобен, т. к. контейнер не заменяется, и при повторном запуске пайпдайна будет ошибка, т. к. порт занят. Эту проблему решает docker-compose. deploy_to_dev: stage: deploy tags: - remote - shell before_script: - chmod 400 $SSH_PRIVATE_KEY - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./docker-compose.yaml ssu@$DEV_SERVER_HOST:~ script: - ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ssu@$DEV_SERVER_HOST " export IMAGE_NAME=$IMAGE_NAME && export IMAGE_TAG=$IMAGE_TAG && docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker-compose down && docker-compose up -d " environment: name: development url: $DEV_ENDPOINT Так как сервер, где запускается контейнер, ничего не знает об окружении, надо пробрасывать значения переменных через ''export'' и копировать ''docker-compose.yaml'' по SSH. CСоответственно, в docker-compose.yaml можно сослаться на переменные version: "3.3" services: app: image: $IMAGE_NAME:$IMAGE_TAG ports: - 3000:3000 SSH keys, документация: https://docs.gitlab.com/ee/ci/ssh_keys/ ==== Автоверсии ==== Канон - major.minor.patch. Здесь в примере npm и версия находится в файле app/version.json, откуда она будет браться: { "name": "bootcamp-node-project", "version": "1.0", .. }, Недостающая часть добавляется из ID пайплайна. ''jd'' надо поставить на воркере, если его там нет. Полученный файл передаётся через артефакт. build_image: ... before_script: - export PACKAGE_JSON_VERSION=$(cat app/package.json | jq -r .version) - export VERSION=$PACKAGE_JSON_VERSION.$CI_PIPELINE_IID - echo $VERSION > version-file.txt artifacts: paths: - version-file.txt Артефакты автоматически доступны в последующих этапах, но не между задачами внутри одного этапа. Чтобы сделать артефакт доступным для соседней задачи, нужно настроить dependencies/needs.\\ Needs ждёт выполнения задач, указанных там, а dependencies берёт артефакты из указанных задач. Если указаны и needs, и dependencies одновременно, то needs должен содержать задачи, перечисленные в dependencies, иначе работать не будет. В то же время, если указать только needs, артефакты будут скачаны из задач, перечисленных там, поэтому нет необходимости указывать dependencies, если они совпадают с needs. push_image: ... needs: - build_image before_script: - export VERSION=$(cat version-file.txt) - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker push $IMAGE_NAME:$VERSION Чтобы заблокировать скачивание артефактов, например, на этапе последующего тестирования, нужно указать пустой массив: test_dev: stage: deploy dependencies: [] script: - echo "testing dev" В специфическом случае с npm можно провернуть вариант с .env вместо выгрузки артефактов. Это файл "ключ-значение". В блоке артефактов указывается reports -> dotenv. build_image: ... before_script: - export PACKAGE_JSON_VERSION=$(cat app/package.json | jq -r .version) - export VERSION=$PACKAGE_JSON_VERSION.$CI_PIPELINE_IID - echo "VERSION=$VERSION" > build.env - echo "DEVELOPER=Vasya" >> build.env artifacts: reports: dotenv: build.env После этого последующие соседние задачи (c соответствующими dependencies/needs) будут уже знать про эти переменные, ничего указывать не надо. ==== Развёртывание на несколько окружений ==== ^dev -> ^staging -> ^prod ^ |Functional tests\\ Integration tests\\ SAST tests |Performance tests\\ DAST tests | | Для примера развёртывание будет на одну и ту же машину с использованием разных портов. Для начала нужно задать переменную для порта в докер-композе, чтобы управлять ей из пайплайна. ports: - ${APP_PORT}:3000 Задать переменную в пайплайне в задаче deploy_to_dev: ''export APP_PORT=3000'', а также ''export COMPOSE_PROJECT_NAME=dev'', чтобы задать имя проекта, иначе ''docker-compose down'' в другом деплое будет останавливать один и тот же контейнер (файл Докер-композ используется ведь тот же самый, и имя по умолчанию генерируется одинаковое). Ну а затем копируется задача deploy_to_dev в deploy_to_staging и там меняется всё, что нужно. Создаются этапы deploy_dev и deploy_staging, меняются/дописываются переменные. Перед deploy_to_staging должна стоять задача каких-нибудь functional tests, чтобы, если тесты не прошли, развётрывание на тестовое окружение уже не шло. Чтобы не плодить этапов, эти тесты могут входить в этап deploy_dev с ''needs: - deploy_to_dev''. Чтобы не копировать практически одинаковые задачи деплоя много раз, нужно сделать //шаблон задачи.// Положим, взяли задачу deploy_to_dev, поменяли название (точка спереди - чтобы эта "задача" не выполнялась), удалён ''stage:'' и все параметры, которые меняются от одного окружения к другому ($SSH_PRIVATE_KEY), указываются в блоке ''variables:'' как задаваемые извне. .deploy: tags: - remote - shell variables: SSH_PRIVATE_KEY: "" SERVER_HOST: "" DEPLOY_ENV: "" APP_PORT: "" ENDPOINT: "" before_script: - chmod 400 $SSH_PRIVATE_KEY - export VERSION=$(cat version-file.txt) - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./docker-compose.yaml ubuntu@$SERVER_HOST:~ script: - ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ubuntu@$SERVER_HOST " export IMAGE_NAME=$IMAGE_NAME && export IMAGE_TAG=$VERSION && export APP_PORT=$APP_PORT && export COMPOSE_PROJECT_NAME=$DEPLOY_ENV && docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker-compose down && docker-compose up -d " environment: name: $DEPLOY_ENV url: $ENDPOINT А потом можно ссылаться на этот шаблон, задавая недостающие параметры и значения переменных. Имена переменных лучше делать разными, например, так работать не будет: ''SSH_PRIVATE_KEY: $SSH_PRIVATE_KEY'', если ''$SSH_PRIVATE_KEY'' имеет тип File. В этом случае при присвоении вместо пути к файлу в переменную будет пихаться его содержимое. deploy_to_dev: extends: .deploy stage: deploy_dev variables: SSH_KEY: $DEV_SSH_PRIVATE_KEY SERVER_HOST: $DEV_SERVER_HOST DEPLOY_ENV: development APP_PORT: 3000 ENDPOINT: $DEV_ENDPOINT deploy_to_staging: extends: .deploy stage: deploy_staging variables: SSH_KEY: $STAGING_SSH_PRIVATE_KEY SERVER_HOST: $STAGING_SERVER_HOST DEPLOY_ENV: staging APP_PORT: 4000 ENDPOINT: $STAGING_ENDPOINT ===== Install ===== :!: По состоянию на 2023 год репозитории для установки закрыты для России, качать и ставить надо пакетом.\\ https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=amd64&dist=jammy (для Ubuntu 22.04) wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/ubuntu/jammy/gitlab-ce_16.0.5-ce.0_amd64.deb/download.deb dpkg -i gitlab-ce_16.0.5-ce.0_amd64.deb DockerHub: https://hub.docker.com/u/gitlab\\ Install on Docker: https://docs.gitlab.com/ee/install/docker.html\\ Private CI/CD using Docker: https://oramind.com/private-cicd-using-gitlab-docker/ https://packages.gitlab.com/gitlab/gitlab-ce\\ https://docs.gitlab.com/ee/update/package/index.html#upgrade-using-a-manually-downloaded-package ==== Runner ==== Скачать: https://gitlab.com/gitlab-org/gitlab-runner/-/releases\\ Установить: curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb" dpkg -i gitlab-runner_amd64.deb Регистрация: ''gitlab-runner register'' # Регистрировать можно несколько раз, если нужно несколько видов раннера на одной и той же машине. # URL и token берутся из Settings -> CI/CD -> Runners URL='http://git.example.com/' REG_TOKEN='GD1358941-xY4DE8k7Mr3dffgLLun' gitlab-runner register \ --url $URL \ --registration-token $REG_TOKEN \ --executor shell \ --tag-list "shell" \ --description t-docker2 gitlab-runner register \ --url $URL \ --registration-token $REG_TOKEN \ --executor docker \ --tag-list "docker" \ --docker-image "alpine:3.18" \ --description t-docker2 # Перезапустить сервис, если раннер не готов сразу systemctl restart gitlab-runner # Для докера нужно добавить пользователя gitlab-runner в группу docker sudo usermod -aG docker gitlab-runner reboot :!: Если задачи застревают (stack), надо поставить галку Run untagged jobs в Admin -> Settings -> CI/CD -> Runners -> Свойства раннера. Но лучше прописывать тэги раннера в задачах пайплайна. https://docs.gitlab.com/runner/install/linux-manually.html\\ https://docs.gitlab.com/runner/register/ ==== Container registry ==== * Package registry - для пакетов поддерживаемых менеджеров пакетов (Maven, npm, NuGet,PyPI, Rube gems, etc) * Container registry - для хранения контейнеров * Terraform modules (Infrastructure registry) - пакеты IaC Здесь: - Gitlab был установлен из пакета (Omnibus GitLab installations) - Тот же домен, порт 5050 - Сертификат самоподписанный CRT_DIR=/etc/gitlab/ssl CN=git.example.com PORT=5050 mkdir -p -m 700 $CRT_DIR cd $CRT_DIR # Сгенерить сертификаты openssl genrsa -out $CN.ca.key 2048 openssl req -new -x509 -days 36500 -key $CN.ca.key -subj "/C=CN/ST=GD/L=SZ/O=$CN/CN=$CN Root CA" -out $CN.ca.crt openssl req -newkey rsa:2048 -nodes -keyout $CN.key -subj "/C=CN/ST=GD/L=SZ/O=$CN/CN=$CN" -out $CN.csr openssl x509 -req -extfile <(printf "subjectAltName=DNS:$CN") -days 36500 -in $CN.csr -CA $CN.ca.crt -CAkey $CN.ca.key -CAcreateserial -out $CN.crt chmod 600 $CRT_DIR/$CN.* ################################### # В конфиге /etc/gitlab/gitlab.rb ################################### # Включить SSL для самого Гитлаба sed -i "/[^_]external_url /c external_url 'https://$CN' /nginx\['ssl_certificate'\] /c nginx['ssl_certificate'] = \"/etc/gitlab/ssl/$CN.crt\" /nginx\['ssl_certificate_key'\] /c nginx['ssl_certificate_key'] = \"/etc/gitlab/ssl/$CN.key\" " /etc/gitlab/gitlab.rb # Включить реестр контейнеров sed -i "/registry_external_url /c registry_external_url 'https://$CN:$PORT' /registry_nginx\['enable'\] /c registry_nginx['enable'] = true /registry_nginx\['listen_port'\] /c registry_nginx['listen_port'] = $PORT " /etc/gitlab/gitlab.rb # Перечитать настройки sudo gitlab-ctl reconfigure https://docs.gitlab.com/ee/administration/packages/container_registry.html#configure-container-registry-under-an-existing-gitlab-domain На раннере: # Добавить сертификат git.ca.crt в доверенные nano /etc/ssl/certs/git.ca.crt # вставить содержимое сертификата с сервера update-ca-certificates # После этого можно регистрировать раннер Если не добавить в доверенные на уровне самой системы, надо будет париться с регистрацией раннера на сервере и с докером: ### Let Docker accept your self-signed certificate # Per default, Docker will not accept your self-signed certificate. You need to create a folder with your CA in order to make Docker aware that # your certificate is valid. For that reason, you create a folder of your trusted Docker registry and copy your CA into the folder. # If you will not copy the CA in the folder. You will receive the following error: # Registry fails with x509 certificate signed by unknown authority sudo mkdir -p /etc/docker/certs.d/git.example.com:5050 sudo cp /etc/gitlab/ssl/git.example.com.ca.crt /etc/docker/certs.d/git.example.com:5050/ ### Let GitLab runners accept your self-signed certificate # The same error will occur when you want to register your GitLab runner: # Post “https://git.example.com/api/v4/runners”: x509: certificate signed by unknown authority # Please use the following command in order to register your GitLab runner successfully: sudo gitlab-runner register --tls-ca-file="/etc/gitlab/ssl/git.example.com.ca.crt" Справка: ''/help/administration/packages/container_registry.md''\\ [[https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28841#note_1004765355|x509: certificate relies on legacy Common Name field, use SANs instead]]\\ [[https://ubuntu.com/server/docs/security-trust-store|Installing a root CA certificate in the trust store]]\\ [[https://beye.blog/gitlab-server-with-a-self-signed-certificate-and-embedded-docker-registry/|GitLab server with a self-signed certificate and embedded docker registry]] ===== Подход к работе с микросервисами ===== Если микросервисов в приложении больше чем один, возникает 2 подхода к хранению кода - Один репозиторий (monorepo) - для каждого микросервиса используется свой каталог. Минусы - опасность написания связанного кода между микросервисами, большой объём кода, сложность управления пайплайнами, одним коммитом можно поломать всё сразу. Подходит для маленьких проектов и одной команды. - Несколько (polyrepo) - каждый микросервис находится в своём репозитории. В Гитлабе связанные проекты можно объединить в группу. ==== Сеть ==== Чтобы контейнеры могли взаимодействовать, их нужно поместить в одну сеть. Для этого в докер-композ-файле указывается определённая сеть, которая должна уже присутствовать (параметр external) version: "3.3" services: app: image: ${DC_IMAGE_NAME}:${DC_IMAGE_TAG} ports: - ${DC_APP_PORT}:${DC_APP_PORT} networks: - micro_service networks: micro_service: external: name: micro_service :!: Параметр ''name:'' в опциях сети предписывает не прибавлять к её названию имя проекта, т. е. будет именно ''micro_service'', а не ''project_micro_service''. А в пайплайне в шаблоне задачи деплоя прописать создание этой сети и указание, чтобы задача не обламывалась, если сеть уже есть: docker network create micro_service || true && docker-compose down && docker-compose up -d ++++ Полный листинг .gitlab-ci.yml (monorepo) | workflow: rules: - if: $CI_COMMIT_BRANCH != "main" && $CI_PIPELINE_SOURCE != "merge_request_event" when: never - when: always variables: DEPLOYMENT_SERVER_HOST: "server" APP_ENDPOINT: http://server:3000 stages: - build - deploy .build: stage: build tags: - remote - shell variables: MICRO_SERVICE: "" SERVICE_VERSION: "" before_script: - cd $MICRO_SERVICE - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE_NAME:$IMAGE_TAG . - docker push $IMAGE_NAME:$IMAGE_TAG build_frontend: extends: .build variables: MICRO_SERVICE: frontend SERVICE_VERSION: "1.3" only: changes: - "frontend/**/*" build_products: extends: .build variables: MICRO_SERVICE: products SERVICE_VERSION: "1.8" only: changes: - "products/**/*" build_shopping_cart: extends: .build variables: MICRO_SERVICE: shopping-cart SERVICE_VERSION: "2.1" only: changes: - "shopping-cart/**/*" .deploy: stage: deploy tags: - remote - shell variables: MICRO_SERVICE: "" SERVICE_VERSION: "" APP_PORT: "" before_script: - chmod 400 $SSH_PRIVATE_KEY - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION script: - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./docker-compose.yaml ubuntu@$DEPLOYMENT_SERVER_HOST:~ - ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ubuntu@$DEPLOYMENT_SERVER_HOST " docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && export COMPOSE_PROJECT_NAME=$MICRO_SERVICE && export DC_IMAGE_NAME=$IMAGE_NAME && export DC_IMAGE_TAG=$IMAGE_TAG && export DC_APP_PORT=$APP_PORT && docker network create micro_service || true && docker-compose down && docker-compose up -d" environment: name: development url: $APP_ENDPOINT deploy_frontend: extends: .deploy variables: MICRO_SERVICE: frontend SERVICE_VERSION: "1.3" APP_PORT: 3000 only: changes: - "frontend/**/*" deploy_products: extends: .deploy variables: MICRO_SERVICE: products SERVICE_VERSION: "1.8" APP_PORT: 3001 only: changes: - "products/**/*" deploy_shopping_cart: extends: .deploy variables: MICRO_SERVICE: shopping-cart SERVICE_VERSION: "2.1" APP_PORT: 3002 only: changes: - "shopping-cart/**/*" ++++ ==== Polyrepo ==== Подготовка: - Создаётся **группа,** куда добавляются все нужные проекты. Удобство группы в том, что можно задавать переменные, раннеры и т. п. для всей группы, а не для каждого проекта. - Раннер для группы регистрируется отдельно в настройках группы. Потом нужно в группе настроить переменную для закрытого ключа SSH сервера деплоя. Дальше в каждую репу копируются docker-compose.yml и .gitlab-ci.yml и редактируются соответственно. Шаблоны задач можно переделать в реальные задачи, т. к. сервис в репе только один, отредактировав их и убрав задачи, вызывающие эти шаблоны, удалить из них блоки переменных, ожидаемых извне. Убрать детекты срабатывания в подкаталогах и заходы в подкаталоги из задач. ++++ Полный листинг .gitlab-ci.yml (polyrepo) сервиса frontend | workflow: rules: - if: $CI_COMMIT_BRANCH != "main" && $CI_PIPELINE_SOURCE != "merge_request_event" when: never - when: always variables: DEPLOYMENT_SERVER_HOST: "server" APP_ENDPOINT: http://server:3000 MICRO_SERVICE: frontend SERVICE_VERSION: "1.3" APP_PORT: 3000 stages: - build - deploy build: stage: build tags: - group - shell before_script: - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE_NAME:$IMAGE_TAG . - docker push $IMAGE_NAME:$IMAGE_TAG deploy: stage: deploy tags: - group - shell before_script: - chmod 400 $SSH_PRIVATE_KEY - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION script: - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./docker-compose.yaml ubuntu@$DEPLOYMENT_SERVER_HOST:~ - ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ubuntu@$DEPLOYMENT_SERVER_HOST " docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && export COMPOSE_PROJECT_NAME=$MICRO_SERVICE && export DC_IMAGE_NAME=$IMAGE_NAME && export DC_IMAGE_TAG=$IMAGE_TAG && export DC_APP_PORT=$APP_PORT && docker network create micro_service || true && docker-compose down && docker-compose up -d" environment: name: development url: $APP_ENDPOINT ++++ Проблема множества репозиториев в том, что одна и та же конфигурация повторяется из раза в раз, а если работают разные команды разрабов, то эти конфигурации начинают разниться в подходах, появляются разные тесты и т. п. и управлять этим барахлом становится тяжело. Для решения этой проблемы есть шаблоны задач, но не в том же пайплайне, а вынесенные в отдельный проект, на которые можно ссылаться как на шаблон SAST, к примеру. ==== Шаблоны задач ==== Надо вынести повторяющиеся задачи в отдельные .yml-файлы, например # Можно также вписать блок с переменными, которые нужно передать извне для большей наглядности, но это необязательно variables: MICRO_SERVICE: "" SERVICE_VERSION: "" build: stage: build before_script: - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE_NAME:$IMAGE_TAG . - docker push $IMAGE_NAME:$IMAGE_TAG В файле шаблона всё должно быть параметризовано по-максимуму. В основном пайплайне нужно сослаться на файлы шаблона, а в самой задае добавить только те разделы, которых нет в шаблоне.\\ Местоположение раздела ''include:'' в пайплайне не имеет значения.\\ :!: Если нужно что-то добавить в раздел, который уже есть в шаблоне (например, before_script), то придётся писать его полностью, т. к. при наличии раздела в основном пайплайне раздел шаблона полностью заменяется. include: - local: '.build-template.yml' - local: '.deploy-template.yml' ... build: tags: - group - shell before_script: - echo "Добавленные действия, а дальше то, что уже есть в шаблоне, иначе не заработает" - export IMAGE_NAME=$CI_REGISTRY_IMAGE/microservice/$MICRO_SERVICE - export IMAGE_TAG=$SERVICE_VERSION Файлы шаблонов можно вынести в отдельный репозиторий, например, ci-templates. В этом случае ссылаться на эти шаблоны нужно так: include: - project: mymicroservice-cicd/ci-templates ref: main file: - build.yml - deploy.yml ''ref: main'' указывает на ветку.\\ Т. к. репозиторий в той же группе, указывается ''группа/репозиторий''. Если бы он был вне группы, надо было бы указывать ''имя пользователя/репозиторий''. ===== Mail ===== If you would rather send application email via an SMTP server instead of via Sendmail or Postfix, add the following configuration information to ''/etc/gitlab/gitlab.rb'' and run ''gitlab-ctl reconfigure''.\\ https://docs.gitlab.com/omnibus/settings/smtp.html gitlab_rails['smtp_enable'] = true gitlab_rails['smtp_address'] = "mail.example.com" gitlab_rails['smtp_port'] = 25 gitlab_rails['smtp_domain'] = "example.com" gitlab_rails['smtp_enable_starttls_auto'] = true gitlab_rails['smtp_openssl_verify_mode'] = 'none' ### Email Settings gitlab_rails['gitlab_email_from'] = 'git@example.com' gitlab_rails['gitlab_email_display_name'] = 'Git' Проверка отправки писем из консоли gitlab-rails console -e production Notify.test_email('user@example.com', 'Hello World', 'This is a test message').deliver_now ===== Решение проблем ===== ==== 502 - Whoops, GitLab is taking too much time to respond is normal during GitLab startup and goes away after couple minutes ==== После перезапуска возникает ошибка 502. Нужно подождать, т. к. Gitlab запускается небыстро. См. статус: ''gitlab-ctl'' ==== OpenSSL::SSL::SSLError (hostname does not match the server certificate) ==== В конфигурацию /etc/gitlab/gitlab.rb добавить gitlab_rails['smtp_openssl_verify_mode'] = 'none' # Затем gitlab-ctl reconfigure gitlab-rake cache:clear RAILS_ENV=production https://gitlab.com/gitlab-org/gitlab-foss/-/issues/446 ==== Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock ==== Возникает при сборке образа на раннере (shell executor). Решение: usermod -aG docker gitlab-runner service docker restart https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3492 ==== CI_REGISTRY error during connect status 255: Permission denied, please try again ==== Раннер не может подключиться к реестру контейнеров: $ echo "$CI_REGISTRY_PASSWORD" |docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY error during connect: Post "http://docker.example.com/v1.24/auth": command [ssh -l cicd -- 10.1.0.138 docker system dial-stdio] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=Permission denied, please try again. Permission denied, please try again. cicd@10.1.0.138: Permission denied (publickey,password). Решение: не раннере удалить всё в каталоге ''/home/gitlab-runner''. ===== Безопасность ===== Shifting Security Left - GitLab DevSecOps Overview: https://www.youtube.com/watch?v=XnYstHObqlA\\ Гитлаб предлагает сканеры безопасности: * [[https://docs.gitlab.com/ee/user/application_security/sast/|Static Application Security Testing]] (SAST) - обнаружение проблем в самом исходном коде, например, пользовательский ввод позволяет выполнить инъекцию команды. [[https://www.youtube.com/watch?v=8sOjvlkl8QY|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/secret_detection/|Secret Detection]] - обнаружение вбитых паролей в исходном коде. [[https://www.youtube.com/watch?v=W2tjcQreDwQ|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/dast/|Dynamic Application Security Testing]] (DAST) - сканирование на уязвимости через вызов приложения или API. [[https://www.youtube.com/watch?v=ywme200L7LU|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/iac_scanning/|Infrastructure-as-Code (IaC) Scanning]] - сканирование IaC-файлов (Ansible, Terraform) на уязвимости. * [[https://docs.gitlab.com/ee/user/application_security/dependency_scanning/|Dependency Scanning]] - отчёт по уязвимостям зависимостей проекта по их версиям. [[https://www.youtube.com/watch?v=39RvTMLDszc|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/container_scanning/|Container Scanning]] - сканирование докер-образов на уязвимости. [[https://www.youtube.com/watch?v=C0jn2eN5MAs|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/api_fuzzing/|Fuzz Testing]] - шлёт на вход функций всякую ерунду на предмет неожиданных глюков. [[https://www.youtube.com/watch?v=oUHsfvLGhDk|Демо]] Отчёты / управление: * [[https://docs.gitlab.com/ee/user/application_security/security_dashboard/|Security Reports]] - сводный отчёт по уязвимостям. [[https://www.youtube.com/watch?v=QHQHN4luNpc|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/vulnerability_report/|Vulnerability Management]] [[https://www.youtube.com/watch?v=8SJHz6BCgXM|Демо]] * [[https://docs.gitlab.com/ee/user/application_security/policies/|Policies]] [[https://www.youtube.com/watch?v=IZbCIKXz-wM|Демо]] ===== Прочее ===== ==== Тестовый проект Gitlab from zero to hero ==== https://gitlab.com/nanuchi/mynodeapp-cicd-project Monorepo: https://gitlab.com/nanuchi/mymicroservice-cicd ==== Example CI/CD Pipelines ==== [[https://gitlab.com/gitlab-examples/ruby-autodeploy|Ruby Auto Deploy]]\\ [[https://gitlab.com/gitlab-examples/multi-project-pipelines/simple-maven-app|Simple Maven App]]\\ [[https://www.youtube.com/watch?v=0Tc0YYBxqi4&ab_channel=GitLab|AutoDevOps]]\\ [[https://gitlab.com/gitlab-examples|More Project Examples]]\\ ===== Заметки о реальных проектах ===== ==== Сервис личного кабинета в Docker Swarm ==== # SSH / Context ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY $DEPLOY_USER@$DEPLOY_HOST docker context create remote --docker "host=ssh://cicd@host" docker context use remote # Вход в репозиторий docker login -u mail@example.com -p P@ssw0rd git.example.com:5050 # Пулл для локального тестирования docker pull git.example.com:5050/personal-account/backend-laravel/lk/backend_app:1.0 docker pull git.example.com:5050/personal-account/backend-laravel/lk/backend_nginx:1.0 # Сети надо создавать overlay и swarm, иначе не будет работать внутренний DNS docker network create --driver=overlay --scope=swarm lk_rabbitmq docker network create --driver=overlay --scope=swarm lk_backend # Запуск docker stack deploy -c ./docker-compose.yml lk #docker stack rm lk docker exec $(docker ps -qf name=lk_app) mkdir -p /var/www/html/storage/framework/sessions mkdir -p /var/www/html/storage/framework/sessions chown -R 82:82 /var/www/html/storage version: "3.9" services: app: &app image: git.example.com:5050/personal-account/backend-laravel/lk/backend_app:1.0 healthcheck: test: ["CMD", "netstat", "-an", "|fgrep", ":9000"] volumes: - /docker/lk/www/storage:/var/www/html/storage networks: - lk_backend - lk_rabbitmq nginx: image: git.example.com:5050/personal-account/backend-laravel/lk/backend_nginx:1.0 environment: NGINX_ROOT: /var/www/html/public NGINX_FASTCGI_PASS: app healthcheck: test: ["CMD", "curl", "-f", "http://localhost:80"] ports: - 8080:80 networks: - lk_backend db: image: mysql:8.0 environment: MYSQL_DATABASE: personal-area MYSQL_ROOT_PASSWORD: password MYSQL_PASSWORD: password MYSQL_USER: personal-area-user SERVICE_TAGS: dev SERVICE_NAME: mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p$$MYSQL_ROOT_PASSWORD"] volumes: - /docker/lk/db:/docker-entrypoint-initdb.d networks: - lk_backend rabbitmq: image: rabbitmq:management-alpine hostname: rabbitmq environment: RABBITMQ_DEFAULT_USER: admin RABBITMQ_DEFAULT_PASS: admin healthcheck: test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] ports: - 5672:5672 - 15672:15672 volumes: - /docker/lk/rabbitmq:/var/lib/rabbitmq networks: - lk_rabbitmq queue-lk-job-broadcast: &queue <<: *app command: php artisan queue:work --queue=lk.job.broadcast healthcheck: disable: true queue-lk-job-catalog_order: <<: *queue command: php artisan queue:work --queue=lk.job.catalog_order queue-lk-job-notification: <<: *queue command: php artisan queue:work --queue=lk.job.notification queue-lk-job-schedule: <<: *queue command: php artisan queue:work --queue=lk.job.schedule # И ещё куча таких же, слушающих очереди websockets: <<: *queue command: php artisan websockets:serve networks: lk_backend: external: true name: lk_backend lk_rabbitmq: external: true name: lk_rabbitmq