Инструменты пользователя

Инструменты сайта


service:gitlab

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
service:gitlab [31.05.2024 10:01] – [Безопасность] viacheslavservice:gitlab [13.05.2025 13:11] (текущий) – [CI_REGISTRY error during connect status 255: Permission denied, please try again] viacheslav
Строка 1: Строка 1:
 +====== 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). По умолчанию, если не заданы условия, задачи выполняются параллельно. Если какая-то задача выполнилась с ошибкой, то весь пайплайн будет провален.
 +
 +Вид простейшего пайплайна:
 +<code yaml>
 +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
 +
 +</code>
 +Релиз - это скомпилированный и упакованный исходный код + файл 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 и т. п.\\
 +''- > '' превращает переносы строк в пробелы. Удобно для простого переноса длинной строки.
 +<code yaml>
 +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-.*$/
 +</code>
 +
 +
 +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). В рамках одного этапа задачи выполняются одновременно. Этап не начинается, пока не закончится предыдущий. Зависимость задач от выполнения этапов:
 +<code yaml>
 +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"
 +</code>
 +{{:service:pasted:20220819-062835.png}}
 +
 +Можно вызвать скрипт, но сначала его нужно сделать исполняемым:
 +<code yaml>
 +    script:
 +        - chmod +x ./scriptfile.sh
 +        - ./scriptfile.sh
 +</code>
 +Если скрипт что-то создал в каталоге на раннере, то можно это не удалять, т. к. пайплайн запускается каждый раз по новой и не использует старого окружения.
 +
 +==== only/except ====
 +Определяют, при каких условиях задача выполняется.
 +<code yaml>
 +# выполнять только в ветке main
 +job:
 +  only:
 +    - main
 +</code>
 +
 +==== Workflow rules ====
 +''workflow'' определяет глобальные настройки пайплайна. Положим, чтобы не добавлять в каждую задачу ''only:'', можно задать это глобально:
 +<code yaml>
 +workflow:
 +  rules:
 +    # если ветка не main и вызывается не с помощью merge/pull request, то
 +    - if: $CI_COMMIT_BRANCH != "main" && $CI_PIPELINE_SOURCE != "merge_request_event"
 +      when: never # никогда не выполнять
 +    - when: always # в остальных случаях - выполнять
 +</code>
 +''$CI_COMMIT_BRANCH'' и ''$CI_PIPELINE_SOURCE'' - [[https://docs.gitlab.com/ee/ci/variables/predefined_variables.html|встроенные переменные]].
 +
 +==== Переменные ====
 +Помимо встроенных переменных, есть произвольные.\\
 +Настройка: Settings (внутри проекта) -> CI/CD -> Variables.
 +
 +Тип переменной может быть file. Например, можно занести туда конфигурационный файл для какого-либо сервиса. При вызове переменной в ней содержится полный путь к этому файлу, например,
 +<code bash>
 +# так выводится путь
 +echo "$CONF_FILE"
 +/builds/user/projectname.tmp/CONF_FILE
 +# а так - содержимое
 +cat $CONF_FILE
 +</code>
 +
 +Можно задать переменные и внутри пайплайна
 +<code yaml>
 +variables:
 +  image_repo: docker.io/id/app
 +  image_tag: v2.0
 +</code>
 +Если блок ''variables:'' находится внутри задачи, то они действуют только внутри этой задачи. Чтобы переменные действовали во всех задачах пайплайна, блок ''variables:'' нужно перенести на верхний уровень (вровень с самими задачами).
 +
 +==== Использование в Dockerfile образов из локального реестра образов ====
 +В пайплайне нужно добавить ''%%--build-arg CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE}%%'' в команду сборки.
 +
 +<code yaml>
 +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
 +
 +</code>
 +
 +Dockerfile:
 +<code>
 +ARG CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE}
 +
 +FROM ${CI_REGISTRY_IMAGE}/node:16.18-alpine AS builder
 +</code>
 +
 +==== Кэш ====
 +Хранение файлов, которые могут быть использованы в соседней задаче на раннере повторно вместо того, чтобы каждый раз качать их из интернета или с сервера, например, npm/ruby/python/go-пакеты.
 +
 +Артефакты помещаются на сервер, кэш хранится на раннере. Если раннеров много, нужно настраивать распределённый кэш (distributed caching), хранящийся в S3. Хоть это всё равно удалённый сервер, это более эффективно, чем качать из интернета, и качается один zip-файл вместо множества мелких.
 +
 +Настраивается соответствующим разделом в задаче. Если не задать ключ (имя кэша), то он будет называться default. Все задачи, где совпадает ключ, будут использовать один и тот же кэш.
 +<code yaml>
 +run_unit_tests:
 +...
 +  cache:
 +    key: "npmdeps_$CI_COMMIT_REF_NAME"
 +    paths:
 +      - app/node_modules
 +    policy: pull-push
 +</code>
 +"Pull-push" - значение по умолчанию, его можно не указывать, т. е., кэш используется и потом обновляется при необходимости. Если есть какая-то параллельно выполняющаяся задача, использующая тот же кэш, то политику нужно отрегулировать, чтобы обе задачи не пытались писать одни и те же файлы в кэш. В одной из задач ставится ''policy: pull'', чтобы она только использовала кэш без его обновления. Иногда, в больших пайплайнах, бывает выделенная задача для создания кэша, там тогда нужно ставить политику в ''push''.
 +
 +Команду ''npm install'' всё равно нужно оставить, чтобы отсутствие кэша или какие-то проблемы с ним не влияли на общую работу пайплайна.
 +
 +Если кэш создаётся с помощью Docker executor, то нужно настраивать volume на раннере, чтобы кэш не уничтожился вместе с контейнером. Путь в ''cache_dir'' и в ''volumes'' должен быть одним и тем же.
 +<file yaml /etc/gitlab-runner/config.toml>
 +[[runners]]
 +...
 +  executor = "docker"
 +  cache_dir = "/cache"
 +  ...
 +  [runners.docker]
 +    volumes = ["/cache"]
 +</file>
 +
 +Разница между первым и вторым запуском пайплайна. Первый раз кэш формировался, второй - использовался.\\
 +{{: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:
 +<code yaml>
 +sast:
 +  stage: test
 +  tags:
 +    - remote
 +    - docker
 +
 +include:
 +  template: Jobs/SAST.gitlab-ci.yml
 +</code>
 +Можно включить до 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 # вставить содержимое сертификата с сервера''
 +  - В пайплайне примерно следующее
 +<code yaml>
 +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
 +        "
 +</code>
 +
 +Вышеприведённый вариант с Докером неудобен, т. к. контейнер не заменяется, и при повторном запуске пайплайна будет ошибка, т. к. порт занят. Эту проблему решает docker-compose.
 +<code yaml>
 +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
 +</code>
 +Так как сервер, где запускается контейнер, ничего не знает об окружении, надо пробрасывать значения переменных через ''export'' и копировать ''docker-compose.yaml'' по SSH. CСоответственно, в docker-compose.yaml можно сослаться на переменные
 +<code yaml>
 +version: "3.3"
 +services:
 +  app:
 +    image: $IMAGE_NAME:$IMAGE_TAG
 +    ports:
 +      - 3000:3000
 +</code>
 +
 +SSH keys, документация: https://docs.gitlab.com/ee/ci/ssh_keys/
 +==== Автоверсии ====
 +Канон - major.minor.patch. Здесь в примере npm и версия находится в файле app/version.json, откуда она будет браться:
 +<code javascript>
 +{
 +  "name": "bootcamp-node-project",
 +  "version": "1.0",
 +  ..
 +},
 +</code>
 +Недостающая часть добавляется из ID пайплайна. ''jd'' надо поставить на воркере, если его там нет. Полученный файл передаётся через артефакт.
 +<code yaml>
 +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
 +</code>
 +<WRAP round important 100%>
 +Артефакты автоматически доступны в последующих этапах, но не между задачами внутри одного этапа. Чтобы сделать артефакт доступным для соседней задачи, нужно настроить dependencies/needs.\\
 +Needs ждёт выполнения задач, указанных там, а dependencies берёт артефакты из указанных задач. Если указаны и needs, и dependencies одновременно, то needs должен содержать задачи, перечисленные в dependencies, иначе работать не будет. В то же время, если указать только needs, артефакты будут скачаны из задач, перечисленных там, поэтому нет необходимости указывать dependencies, если они совпадают с needs.
 +<code yaml>
 +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
 +</code>
 +</WRAP>
 +
 +Чтобы заблокировать скачивание артефактов, например, на этапе последующего тестирования, нужно указать пустой массив:
 +<code yaml>
 +test_dev:
 +  stage: deploy
 +  dependencies: []
 +  script:
 +    - echo "testing dev"
 +</code>
 +
 +В специфическом случае с npm можно провернуть вариант с .env вместо выгрузки артефактов. Это файл "ключ-значение". В блоке артефактов указывается reports -> dotenv.
 +<code yaml>
 +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
 +</code>
 +После этого последующие соседние задачи (c соответствующими dependencies/needs) будут уже знать про эти переменные, ничего указывать не надо.
 +
 +==== Развёртывание на несколько окружений ====
 +^dev -> ^staging -> ^prod ^
 +|Functional tests\\ Integration tests\\ SAST tests |Performance tests\\ DAST tests | |
 +Для примера развёртывание будет на одну и ту же машину с использованием разных портов. Для начала нужно задать переменную для порта в докер-композе, чтобы управлять ей из пайплайна.
 +<code yaml>
 +ports:
 +  - ${APP_PORT}:3000
 +</code>
 +Задать переменную в пайплайне в задаче 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:'' как задаваемые извне.
 +<code yaml>
 +.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
 +</code>
 +А потом можно ссылаться на этот шаблон, задавая недостающие параметры и значения переменных. Имена переменных лучше делать разными, например, так работать не будет: ''SSH_PRIVATE_KEY: $SSH_PRIVATE_KEY'', если ''$SSH_PRIVATE_KEY'' имеет тип File. В этом случае при присвоении вместо пути к файлу в переменную будет пихаться его содержимое.
 +<code yaml>
 +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
 +</code>
 +===== Install =====
 +:!: По состоянию на 2023 год репозитории для установки закрыты для России, качать и ставить надо пакетом.\\
 +https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=amd64&dist=jammy (для Ubuntu 22.04)
 +<code bash>
 +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
 +</code>
 +
 +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\\
 +Установить:
 +<code bash>
 +curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb"
 +dpkg -i gitlab-runner_amd64.deb
 +</code>
 +Регистрация: ''gitlab-runner register''
 +<code bash>
 +# Регистрировать можно несколько раз, если нужно несколько видов раннера на одной и той же машине.
 +# 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
 +</code>
 +:!: Если задачи застревают (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
 +  - Сертификат самоподписанный
 +<code bash>
 +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
 +</code>
 +https://docs.gitlab.com/ee/administration/packages/container_registry.html#configure-container-registry-under-an-existing-gitlab-domain
 +
 +На раннере:
 +<code bash>
 +# Добавить сертификат git.ca.crt в доверенные
 +nano /etc/ssl/certs/git.ca.crt # вставить содержимое сертификата с сервера
 +update-ca-certificates
 +# После этого можно регистрировать раннер
 +</code>
 +Если не добавить в доверенные на уровне самой системы, надо будет париться с регистрацией раннера на сервере и с докером:
 +<code bash>
 +### 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"
 +</code>
 +Справка: ''<YOUR_GITLAB_URL>/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)
 +<code yaml>
 +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
 +</code>
 +:!: Параметр ''name:'' в опциях сети предписывает не прибавлять к её названию имя проекта, т. е. будет именно ''micro_service'', а не ''project_micro_service''.
 +
 +А в пайплайне в шаблоне задачи деплоя прописать создание этой сети и указание, чтобы задача не обламывалась, если сеть уже есть:
 +<code yaml>
 +docker network create micro_service || true &&
 +docker-compose down &&
 +docker-compose up -d
 +</code>
 +
 +++++ Полный листинг .gitlab-ci.yml (monorepo) |
 +<file yaml .gitlab-ci.yml>
 +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/**/*"
 +</file>
 +++++
 +
 +==== Polyrepo ====
 +Подготовка:
 +  - Создаётся **группа,** куда добавляются все нужные проекты. Удобство группы в том, что можно задавать переменные, раннеры и т. п. для всей группы, а не для каждого проекта.
 +  - Раннер для группы регистрируется отдельно в настройках группы. Потом нужно в группе настроить переменную для закрытого ключа SSH сервера деплоя.
 +
 +Дальше в каждую репу копируются docker-compose.yml и .gitlab-ci.yml и редактируются соответственно. Шаблоны задач можно переделать в реальные задачи, т. к. сервис в репе только один, отредактировав их и убрав задачи, вызывающие эти шаблоны, удалить из них блоки переменных, ожидаемых извне. Убрать детекты срабатывания в подкаталогах и заходы в подкаталоги из задач.
 +
 +++++ Полный листинг .gitlab-ci.yml (polyrepo) сервиса frontend |
 +<file yaml .gitlab-ci.yml>
 +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
 +
 +</file>
 +++++
 +
 +Проблема множества репозиториев в том, что одна и та же конфигурация повторяется из раза в раз, а если работают разные команды разрабов, то эти конфигурации начинают разниться в подходах, появляются разные тесты и т. п. и управлять этим барахлом становится тяжело. Для решения этой проблемы есть шаблоны задач, но не в том же пайплайне, а вынесенные в отдельный проект, на которые можно ссылаться как на шаблон SAST, к примеру.
 +
 +==== Шаблоны задач ====
 +Надо вынести повторяющиеся задачи в отдельные .yml-файлы, например
 +<file yaml .build-template.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
 +</file>
 +В файле шаблона всё должно быть параметризовано по-максимуму. В основном пайплайне нужно сослаться на файлы шаблона, а в самой задае добавить только те разделы, которых нет в шаблоне.\\
 +Местоположение раздела ''include:'' в пайплайне не имеет значения.\\
 +:!: Если нужно что-то добавить в раздел, который уже есть в шаблоне (например, before_script), то придётся писать его полностью, т. к. при наличии раздела в основном пайплайне раздел шаблона полностью заменяется.
 +<file yaml .gitlab-ci.yml>
 +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
 +</file>
 +
 +Файлы шаблонов можно вынести в отдельный репозиторий, например, ci-templates. В этом случае ссылаться на эти шаблоны нужно так:
 +<file yaml .gitlab-ci.yml>
 +include:
 +  - project: mymicroservice-cicd/ci-templates
 +    ref: main
 +    file:
 +      - build.yml
 +      - deploy.yml
 +</file>
 +''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
 +
 +<code ruby>
 +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'
 +</code>
 +
 +Проверка отправки писем из консоли
 +<code bash>
 +gitlab-rails console -e production
 +Notify.test_email('user@example.com', 'Hello World', 'This is a test message').deliver_now
 +</code>
 +===== Решение проблем =====
 +==== 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) ====
 +<code ruby>
 +В конфигурацию /etc/gitlab/gitlab.rb добавить
 +gitlab_rails['smtp_openssl_verify_mode'] = 'none'
 +# Затем
 +gitlab-ctl reconfigure
 +gitlab-rake cache:clear RAILS_ENV=production
 +</code>
 +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). Решение:
 +<code bash>
 +usermod -aG docker gitlab-runner
 +service docker restart
 +</code>
 +https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3492
 +
 +==== CI_REGISTRY error during connect status 255: Permission denied, please try again ====
 +Раннер не может подключиться к реестру контейнеров:
 +<code bash>
 +$ 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).
 +</code>
 +Решение: на раннере удалить всё в каталоге ''/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 ====
 +
 +<code bash>
 +# 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
 +</code>
 +
 +<code yaml>
 +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
 +</code>

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki