====== 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