service:gitlab
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
service:gitlab [30.07.2024 19:21] – внешнее изменение 127.0.0.1 | service:gitlab [13.05.2025 13:11] (текущий) – [CI_REGISTRY error during connect status 255: Permission denied, please try again] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== Gitlab ====== | ||
+ | Документация: | ||
+ | Get started with GitLab CI/CD: https:// | ||
+ | Learn GitLab with tutorials: https:// | ||
+ | GitLab with Git Essentials - Hands-On Lab: [[https:// | ||
+ | Удобен тем, что содержит всё необходимое, | ||
+ | * Собственно, | ||
+ | * wiki | ||
+ | * Issue board с привязкой к событиями внутри Гитлаба, | ||
+ | * Container registry - хранилище образов контейнеров | ||
+ | * Package registry - хранилище ПО | ||
+ | * CI/CD ([[https:// | ||
+ | |||
+ | Ещё один комбайн типа Гитлаба - [[https:// | ||
+ | |||
+ | * Pipeline - описывается в '' | ||
+ | * Job artifacts — результаты выполнения задач. | ||
+ | * Cache dependencies — кэш зависимостей можно сохранять для ускорения сборки. | ||
+ | * CI/CD variables — могут использоваться в '' | ||
+ | |||
+ | ===== 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, | ||
+ | |Epic |A collection of related issues across different groups and projects to help organize by theme |Initiatives, | ||
+ | |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), | ||
+ | |Roadmap |A visual representation of the various epics for the group | | | ||
+ | |||
+ | |||
+ | **Поток** | ||
+ | |||
+ | {{: | ||
+ | |||
+ | - Issues - всё начинается с этого. Обсуждение и комментирование нововведения и его реализации. Проблема связана только с конкретным проектом, | ||
+ | - Merge Request - создаётся после создания проблемы. Мерж-реквесты позволяют визуализировать и совместно работать над предлагаемыми изменениями в исходном коде, которые существуют в виде коммитов в данной ветке Git. Иногда называется pull request. | ||
+ | - Когда мерж-реквест принят, | ||
+ | - Выполнение пайплайна - сборка, | ||
+ | - Review Apps - проверка приложения. Живой экземпляр новой версии приложения для ознакомления дизайнерам/ | ||
+ | - Peer Review and Discussion - проверка и обсуждение с коллегами на предмет отсутствия каких-то конфликтов и т. п. | ||
+ | - Approve changes - одобрение изменений тем, у кого есть на это права. | ||
+ | - Merge; Issue Closed; CD Pipeline runs - после одобрения изменений проблема закрывается и приложение выкатывается в прод. | ||
+ | - Мониторинг - контроль приложения на предмет того, что изменения имеют желаемый эффект. В Гитлабе, | ||
+ | |||
+ | **Code Review Workflow** | ||
+ | |||
+ | {{: | ||
+ | |||
+ | **Дополнительные инструменты** | ||
+ | |||
+ | * Snippets - хранение небольших кусков кода или текста и общий доступ к ним. | ||
+ | * Wiki - встроена в каждый проект Гитлаба. Используется, | ||
+ | * Web IDE - удобный встроенный редактор кода, можно работать прямо в браузере. [[https:// | ||
+ | |||
+ | ===== Реестры ===== | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | Дополнительно: | ||
+ | |||
+ | ===== Релизы ===== | ||
+ | Это снапшот проекта для конечных пользователей, | ||
+ | |||
+ | Релиз включает: | ||
+ | * Снапшот исходного кода репозитория. | ||
+ | * [[https:// | ||
+ | * Метаданные, | ||
+ | * Примечания к релизу. | ||
+ | |||
+ | Когда релиз создаётся, | ||
+ | * Гитлаб автоматически архивирует исходный код и ассоциирует его с релизом. | ||
+ | * Гитлаб автоматически создаёт json-файл ([[https:// | ||
+ | |||
+ | После релиза можно | ||
+ | * Добавить примечания к релизу. | ||
+ | * Добавить сообщение к гит-тэгу, | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | |||
+ | В Гитлабе есть несколько функций для удобства развертывания и теста релизов. | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | ===== Pipeline ===== | ||
+ | Пайплайн - это файл '' | ||
+ | |||
+ | Вид простейшего пайплайна: | ||
+ | <code yaml> | ||
+ | variables: | ||
+ | ART_TOKEN: " | ||
+ | |||
+ | stages: | ||
+ | - build | ||
+ | - release | ||
+ | - notify | ||
+ | |||
+ | build_a: | ||
+ | stage: build | ||
+ | |||
+ | build_b: | ||
+ | stage: build | ||
+ | # https:// | ||
+ | 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 ' | ||
+ | --data " | ||
+ | https:// | ||
+ | needs: | ||
+ | - build_b | ||
+ | |||
+ | </ | ||
+ | Релиз - это скомпилированный и упакованный исходный код + файл json, содержащий информацию о выпуске. Дополнительно можно добавить описание и прочее, | ||
+ | |||
+ | Использование спецсимволов в строке скрипта: | ||
+ | Переменные: | ||
+ | |||
+ | Средство для проверки синтаксиса внутри Гитлаба - CI Lint. | ||
+ | |||
+ | Токен можно сгенерировать как для пользователя в целом (Edit profile -> Access tokens), так и для отдельного репозитория (Settings -> Access tokens). | ||
+ | |||
+ | ==== Heredoc и перенос строк ==== | ||
+ | '' | ||
+ | '' | ||
+ | <code yaml> | ||
+ | release: | ||
+ | image: node: | ||
+ | stage: release | ||
+ | before_script: | ||
+ | - apt-get update && apt-get install -y curl git jq | ||
+ | script: | ||
+ | - |- | ||
+ | PAYLOAD=$(cat << JSON | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | } | ||
+ | JSON | ||
+ | ) | ||
+ | - > | ||
+ | curl -X POST https:// | ||
+ | --header ' | ||
+ | --data-binary " | ||
+ | when: manual | ||
+ | only: | ||
+ | - / | ||
+ | </ | ||
+ | |||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Задачи (jobs) ===== | ||
+ | Могут являться частью этапа (stage). В рамках одного этапа задачи выполняются одновременно. Этап не начинается, | ||
+ | <code yaml> | ||
+ | stages: | ||
+ | - test | ||
+ | - build | ||
+ | - push | ||
+ | - deploy | ||
+ | |||
+ | lint-test: | ||
+ | stage: test | ||
+ | before_script: | ||
+ | - echo " | ||
+ | script: | ||
+ | - echo "lint test" | ||
+ | after_script: | ||
+ | - echo " | ||
+ | |||
+ | unit-test: | ||
+ | stage: test | ||
+ | before_script: | ||
+ | - echo " | ||
+ | script: | ||
+ | - echo "unit test" | ||
+ | after_script: | ||
+ | - echo " | ||
+ | |||
+ | build-windows: | ||
+ | stage: build | ||
+ | script: | ||
+ | # задача закончится ошибкой, | ||
+ | - 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 " | ||
+ | </ | ||
+ | {{: | ||
+ | |||
+ | Можно вызвать скрипт, | ||
+ | <code yaml> | ||
+ | script: | ||
+ | - chmod +x ./ | ||
+ | - ./ | ||
+ | </ | ||
+ | Если скрипт что-то создал в каталоге на раннере, | ||
+ | |||
+ | ==== only/except ==== | ||
+ | Определяют, | ||
+ | <code yaml> | ||
+ | # выполнять только в ветке main | ||
+ | job: | ||
+ | only: | ||
+ | - main | ||
+ | </ | ||
+ | |||
+ | ==== Workflow rules ==== | ||
+ | '' | ||
+ | <code yaml> | ||
+ | workflow: | ||
+ | rules: | ||
+ | # если ветка не main и вызывается не с помощью merge/pull request, то | ||
+ | - if: $CI_COMMIT_BRANCH != " | ||
+ | when: never # никогда не выполнять | ||
+ | - when: always # в остальных случаях - выполнять | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | ==== Переменные ==== | ||
+ | Помимо встроенных переменных, | ||
+ | Настройка: | ||
+ | |||
+ | Тип переменной может быть file. Например, | ||
+ | <code bash> | ||
+ | # так выводится путь | ||
+ | echo " | ||
+ | / | ||
+ | # а так - содержимое | ||
+ | cat $CONF_FILE | ||
+ | </ | ||
+ | |||
+ | Можно задать переменные и внутри пайплайна | ||
+ | <code yaml> | ||
+ | variables: | ||
+ | image_repo: docker.io/ | ||
+ | image_tag: v2.0 | ||
+ | </ | ||
+ | Если блок '' | ||
+ | |||
+ | ==== Использование в Dockerfile образов из локального реестра образов ==== | ||
+ | В пайплайне нужно добавить '' | ||
+ | |||
+ | <code yaml> | ||
+ | build: | ||
+ | stage: build | ||
+ | image: docker: | ||
+ | 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/ | ||
+ | . | ||
+ | - docker push $CI_REGISTRY_IMAGE/ | ||
+ | |||
+ | </ | ||
+ | |||
+ | Dockerfile: | ||
+ | < | ||
+ | ARG CI_REGISTRY_IMAGE=${CI_REGISTRY_IMAGE} | ||
+ | |||
+ | FROM ${CI_REGISTRY_IMAGE}/ | ||
+ | </ | ||
+ | |||
+ | ==== Кэш ==== | ||
+ | Хранение файлов, | ||
+ | |||
+ | Артефакты помещаются на сервер, | ||
+ | |||
+ | Настраивается соответствующим разделом в задаче. Если не задать ключ (имя кэша), то он будет называться default. Все задачи, | ||
+ | <code yaml> | ||
+ | run_unit_tests: | ||
+ | ... | ||
+ | cache: | ||
+ | key: " | ||
+ | paths: | ||
+ | - app/ | ||
+ | policy: pull-push | ||
+ | </ | ||
+ | " | ||
+ | |||
+ | Команду '' | ||
+ | |||
+ | Если кэш создаётся с помощью Docker executor, то нужно настраивать volume на раннере, | ||
+ | <file yaml / | ||
+ | [[runners]] | ||
+ | ... | ||
+ | executor = " | ||
+ | cache_dir = "/ | ||
+ | ... | ||
+ | [runners.docker] | ||
+ | volumes = ["/ | ||
+ | </ | ||
+ | |||
+ | Разница между первым и вторым запуском пайплайна. Первый раз кэш формировался, | ||
+ | {{: | ||
+ | |||
+ | Почистить кэш можно кнопкой "Clear runner caches" | ||
+ | |||
+ | [[https:// | ||
+ | ==== Шаблоны задач ==== | ||
+ | Идут в комплекте с Гитлабом и могут быть включены в пайплайн в любом проекте, | ||
+ | |||
+ | Шаблоны: | ||
+ | |||
+ | Включить SAST: | ||
+ | <code yaml> | ||
+ | sast: | ||
+ | stage: test | ||
+ | tags: | ||
+ | - remote | ||
+ | - docker | ||
+ | |||
+ | include: | ||
+ | template: Jobs/ | ||
+ | </ | ||
+ | Можно включить до 100 шаблонов. Местоположение раздела '' | ||
+ | |||
+ | Если в проекте ещё нет пайплайна, | ||
+ | ===== Deploy ===== | ||
+ | - На сервере, | ||
+ | - Засунуть закрытый ключ в переменную File на Гитлабе (в конце должна быть пустая строка!) | ||
+ | - Открытый ключ добавить на dev-server в '' | ||
+ | - Добавить сертификат 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: | ||
+ | " | ||
+ | </ | ||
+ | |||
+ | Вышеприведённый вариант с Докером неудобен, | ||
+ | <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 ./ | ||
+ | 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 yaml> | ||
+ | version: " | ||
+ | services: | ||
+ | app: | ||
+ | image: $IMAGE_NAME: | ||
+ | ports: | ||
+ | - 3000:3000 | ||
+ | </ | ||
+ | |||
+ | SSH keys, документация: | ||
+ | ==== Автоверсии ==== | ||
+ | Канон - major.minor.patch. Здесь в примере npm и версия находится в файле app/ | ||
+ | <code javascript> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | .. | ||
+ | }, | ||
+ | </ | ||
+ | Недостающая часть добавляется из ID пайплайна. '' | ||
+ | <code yaml> | ||
+ | build_image: | ||
+ | ... | ||
+ | before_script: | ||
+ | - export PACKAGE_JSON_VERSION=$(cat app/ | ||
+ | - export VERSION=$PACKAGE_JSON_VERSION.$CI_PIPELINE_IID | ||
+ | - echo $VERSION > version-file.txt | ||
+ | artifacts: | ||
+ | paths: | ||
+ | - version-file.txt | ||
+ | </ | ||
+ | <WRAP round important 100%> | ||
+ | Артефакты автоматически доступны в последующих этапах, | ||
+ | Needs ждёт выполнения задач, указанных там, а dependencies берёт артефакты из указанных задач. Если указаны и needs, и dependencies одновременно, | ||
+ | <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: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | Чтобы заблокировать скачивание артефактов, | ||
+ | <code yaml> | ||
+ | test_dev: | ||
+ | stage: deploy | ||
+ | dependencies: | ||
+ | script: | ||
+ | - echo " | ||
+ | </ | ||
+ | |||
+ | В специфическом случае с npm можно провернуть вариант с .env вместо выгрузки артефактов. Это файл " | ||
+ | <code yaml> | ||
+ | build_image: | ||
+ | ... | ||
+ | before_script: | ||
+ | - export PACKAGE_JSON_VERSION=$(cat app/ | ||
+ | - export VERSION=$PACKAGE_JSON_VERSION.$CI_PIPELINE_IID | ||
+ | - echo " | ||
+ | - echo " | ||
+ | artifacts: | ||
+ | reports: | ||
+ | dotenv: build.env | ||
+ | </ | ||
+ | После этого последующие соседние задачи (c соответствующими dependencies/ | ||
+ | |||
+ | ==== Развёртывание на несколько окружений ==== | ||
+ | ^dev -> ^staging -> ^prod ^ | ||
+ | |Functional tests\\ Integration tests\\ SAST tests |Performance tests\\ DAST tests | | | ||
+ | Для примера развёртывание будет на одну и ту же машину с использованием разных портов. Для начала нужно задать переменную для порта в докер-композе, | ||
+ | <code yaml> | ||
+ | ports: | ||
+ | - ${APP_PORT}: | ||
+ | </ | ||
+ | Задать переменную в пайплайне в задаче deploy_to_dev: | ||
+ | |||
+ | Ну а затем копируется задача deploy_to_dev в deploy_to_staging и там меняется всё, что нужно. Создаются этапы deploy_dev и deploy_staging, | ||
+ | |||
+ | Чтобы не копировать практически одинаковые задачи деплоя много раз, нужно сделать // | ||
+ | |||
+ | Положим, | ||
+ | <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 ./ | ||
+ | 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 yaml> | ||
+ | deploy_to_dev: | ||
+ | extends: .deploy | ||
+ | stage: deploy_dev | ||
+ | variables: | ||
+ | SSH_KEY: $DEV_SSH_PRIVATE_KEY | ||
+ | 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: | ||
+ | DEPLOY_ENV: staging | ||
+ | APP_PORT: 4000 | ||
+ | ENDPOINT: $STAGING_ENDPOINT | ||
+ | </ | ||
+ | ===== Install ===== | ||
+ | :!: По состоянию на 2023 год репозитории для установки закрыты для России, | ||
+ | https:// | ||
+ | <code bash> | ||
+ | wget --content-disposition https:// | ||
+ | dpkg -i gitlab-ce_16.0.5-ce.0_amd64.deb | ||
+ | </ | ||
+ | |||
+ | DockerHub: https:// | ||
+ | Install on Docker: https:// | ||
+ | Private CI/CD using Docker: https:// | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Runner ==== | ||
+ | Скачать: | ||
+ | Установить: | ||
+ | <code bash> | ||
+ | curl -LJO " | ||
+ | dpkg -i gitlab-runner_amd64.deb | ||
+ | </ | ||
+ | Регистрация: | ||
+ | <code bash> | ||
+ | # Регистрировать можно несколько раз, если нужно несколько видов раннера на одной и той же машине. | ||
+ | # URL и token берутся из Settings -> CI/CD -> Runners | ||
+ | URL=' | ||
+ | REG_TOKEN=' | ||
+ | |||
+ | gitlab-runner register \ | ||
+ | --url $URL \ | ||
+ | --registration-token $REG_TOKEN \ | ||
+ | --executor shell \ | ||
+ | --tag-list " | ||
+ | --description t-docker2 | ||
+ | gitlab-runner register \ | ||
+ | --url $URL \ | ||
+ | --registration-token $REG_TOKEN \ | ||
+ | --executor docker \ | ||
+ | --tag-list " | ||
+ | --docker-image " | ||
+ | --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:// | ||
+ | https:// | ||
+ | |||
+ | |||
+ | ==== 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=/ | ||
+ | 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 "/ | ||
+ | openssl req -newkey rsa:2048 -nodes -keyout $CN.key -subj "/ | ||
+ | openssl x509 -req -extfile <(printf " | ||
+ | |||
+ | chmod 600 $CRT_DIR/ | ||
+ | |||
+ | ################################### | ||
+ | # В конфиге / | ||
+ | ################################### | ||
+ | # Включить SSL для самого Гитлаба | ||
+ | sed -i "/ | ||
+ | / | ||
+ | / | ||
+ | " / | ||
+ | # Включить реестр контейнеров | ||
+ | sed -i "/ | ||
+ | / | ||
+ | / | ||
+ | " / | ||
+ | |||
+ | # Перечитать настройки | ||
+ | sudo gitlab-ctl reconfigure | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | На раннере: | ||
+ | <code bash> | ||
+ | # Добавить сертификат git.ca.crt в доверенные | ||
+ | nano / | ||
+ | update-ca-certificates | ||
+ | # После этого можно регистрировать раннер | ||
+ | </ | ||
+ | Если не добавить в доверенные на уровне самой системы, | ||
+ | <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 / | ||
+ | sudo cp / | ||
+ | |||
+ | ### Let GitLab runners accept your self-signed certificate | ||
+ | # The same error will occur when you want to register your GitLab runner: | ||
+ | # Post “https:// | ||
+ | # Please use the following command in order to register your GitLab runner successfully: | ||
+ | sudo gitlab-runner register --tls-ca-file="/ | ||
+ | </ | ||
+ | Справка: | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | |||
+ | ===== Подход к работе с микросервисами ===== | ||
+ | Если микросервисов в приложении больше чем один, возникает 2 подхода к хранению кода | ||
+ | - Один репозиторий (monorepo) - для каждого микросервиса используется свой каталог. Минусы - опасность написания связанного кода между микросервисами, | ||
+ | - Несколько (polyrepo) - каждый микросервис находится в своём репозитории. В Гитлабе связанные проекты можно объединить в группу. | ||
+ | |||
+ | ==== Сеть ==== | ||
+ | Чтобы контейнеры могли взаимодействовать, | ||
+ | <code yaml> | ||
+ | version: " | ||
+ | services: | ||
+ | app: | ||
+ | image: ${DC_IMAGE_NAME}: | ||
+ | ports: | ||
+ | - ${DC_APP_PORT}: | ||
+ | networks: | ||
+ | - micro_service | ||
+ | |||
+ | networks: | ||
+ | micro_service: | ||
+ | external: | ||
+ | name: micro_service | ||
+ | </ | ||
+ | :!: Параметр '' | ||
+ | |||
+ | А в пайплайне в шаблоне задачи деплоя прописать создание этой сети и указание, | ||
+ | <code yaml> | ||
+ | docker network create micro_service || true && | ||
+ | docker-compose down && | ||
+ | docker-compose up -d | ||
+ | </ | ||
+ | |||
+ | ++++ Полный листинг .gitlab-ci.yml (monorepo) | | ||
+ | <file yaml .gitlab-ci.yml> | ||
+ | workflow: | ||
+ | rules: | ||
+ | - if: $CI_COMMIT_BRANCH != " | ||
+ | when: never | ||
+ | - when: always | ||
+ | |||
+ | variables: | ||
+ | DEPLOYMENT_SERVER_HOST: | ||
+ | APP_ENDPOINT: | ||
+ | |||
+ | 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/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | script: | ||
+ | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY | ||
+ | - docker build -t $IMAGE_NAME: | ||
+ | - docker push $IMAGE_NAME: | ||
+ | |||
+ | |||
+ | build_frontend: | ||
+ | extends: .build | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | |||
+ | build_products: | ||
+ | extends: .build | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | |||
+ | build_shopping_cart: | ||
+ | extends: .build | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | | ||
+ | |||
+ | .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/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | script: | ||
+ | - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./ | ||
+ | - 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: | ||
+ | SERVICE_VERSION: | ||
+ | APP_PORT: 3000 | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | |||
+ | deploy_products: | ||
+ | extends: .deploy | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | APP_PORT: 3001 | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | |||
+ | deploy_shopping_cart: | ||
+ | extends: .deploy | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | APP_PORT: 3002 | ||
+ | only: | ||
+ | changes: | ||
+ | - " | ||
+ | </ | ||
+ | ++++ | ||
+ | |||
+ | ==== Polyrepo ==== | ||
+ | Подготовка: | ||
+ | - Создаётся **группа, | ||
+ | - Раннер для группы регистрируется отдельно в настройках группы. Потом нужно в группе настроить переменную для закрытого ключа SSH сервера деплоя. | ||
+ | |||
+ | Дальше в каждую репу копируются docker-compose.yml и .gitlab-ci.yml и редактируются соответственно. Шаблоны задач можно переделать в реальные задачи, | ||
+ | |||
+ | ++++ Полный листинг .gitlab-ci.yml (polyrepo) сервиса frontend | | ||
+ | <file yaml .gitlab-ci.yml> | ||
+ | workflow: | ||
+ | rules: | ||
+ | - if: $CI_COMMIT_BRANCH != " | ||
+ | when: never | ||
+ | - when: always | ||
+ | |||
+ | variables: | ||
+ | DEPLOYMENT_SERVER_HOST: | ||
+ | APP_ENDPOINT: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | APP_PORT: 3000 | ||
+ | |||
+ | stages: | ||
+ | - build | ||
+ | - deploy | ||
+ | |||
+ | build: | ||
+ | stage: build | ||
+ | tags: | ||
+ | - group | ||
+ | - shell | ||
+ | before_script: | ||
+ | - export IMAGE_NAME=$CI_REGISTRY_IMAGE/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | script: | ||
+ | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY | ||
+ | - docker build -t $IMAGE_NAME: | ||
+ | - docker push $IMAGE_NAME: | ||
+ | |||
+ | |||
+ | deploy: | ||
+ | stage: deploy | ||
+ | tags: | ||
+ | - group | ||
+ | - shell | ||
+ | before_script: | ||
+ | - chmod 400 $SSH_PRIVATE_KEY | ||
+ | - export IMAGE_NAME=$CI_REGISTRY_IMAGE/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | script: | ||
+ | - scp -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY ./ | ||
+ | - 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 | ||
+ | |||
+ | </ | ||
+ | ++++ | ||
+ | |||
+ | Проблема множества репозиториев в том, что одна и та же конфигурация повторяется из раза в раз, а если работают разные команды разрабов, | ||
+ | |||
+ | ==== Шаблоны задач ==== | ||
+ | Надо вынести повторяющиеся задачи в отдельные .yml-файлы, | ||
+ | <file yaml .build-template.yml> | ||
+ | # Можно также вписать блок с переменными, | ||
+ | variables: | ||
+ | MICRO_SERVICE: | ||
+ | SERVICE_VERSION: | ||
+ | |||
+ | build: | ||
+ | stage: build | ||
+ | before_script: | ||
+ | - export IMAGE_NAME=$CI_REGISTRY_IMAGE/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | script: | ||
+ | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY | ||
+ | - docker build -t $IMAGE_NAME: | ||
+ | - docker push $IMAGE_NAME: | ||
+ | </ | ||
+ | В файле шаблона всё должно быть параметризовано по-максимуму. В основном пайплайне нужно сослаться на файлы шаблона, | ||
+ | Местоположение раздела '' | ||
+ | :!: Если нужно что-то добавить в раздел, | ||
+ | <file yaml .gitlab-ci.yml> | ||
+ | include: | ||
+ | - local: ' | ||
+ | - local: ' | ||
+ | ... | ||
+ | |||
+ | build: | ||
+ | tags: | ||
+ | - group | ||
+ | - shell | ||
+ | before_script: | ||
+ | - echo " | ||
+ | - export IMAGE_NAME=$CI_REGISTRY_IMAGE/ | ||
+ | - export IMAGE_TAG=$SERVICE_VERSION | ||
+ | </ | ||
+ | |||
+ | Файлы шаблонов можно вынести в отдельный репозиторий, | ||
+ | <file yaml .gitlab-ci.yml> | ||
+ | include: | ||
+ | - project: mymicroservice-cicd/ | ||
+ | ref: main | ||
+ | file: | ||
+ | - build.yml | ||
+ | - deploy.yml | ||
+ | </ | ||
+ | '' | ||
+ | Т. к. репозиторий в той же группе, | ||
+ | ===== Mail ===== | ||
+ | If you would rather send application email via an SMTP server instead of via Sendmail or Postfix, add the following configuration information to ''/ | ||
+ | https:// | ||
+ | |||
+ | <code ruby> | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | |||
+ | ### Email Settings | ||
+ | gitlab_rails[' | ||
+ | gitlab_rails[' | ||
+ | </ | ||
+ | |||
+ | Проверка отправки писем из консоли | ||
+ | <code bash> | ||
+ | gitlab-rails console -e production | ||
+ | Notify.test_email(' | ||
+ | </ | ||
+ | ===== Решение проблем ===== | ||
+ | ==== 502 - Whoops, GitLab is taking too much time to respond is normal during GitLab startup and goes away after couple minutes ==== | ||
+ | После перезапуска возникает ошибка 502. Нужно подождать, | ||
+ | См. статус: | ||
+ | |||
+ | ==== OpenSSL:: | ||
+ | <code ruby> | ||
+ | В конфигурацию / | ||
+ | gitlab_rails[' | ||
+ | # Затем | ||
+ | gitlab-ctl reconfigure | ||
+ | gitlab-rake cache:clear RAILS_ENV=production | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Got permission denied while trying to connect to the Docker daemon socket at unix:/// | ||
+ | Возникает при сборке образа на раннере (shell executor). Решение: | ||
+ | <code bash> | ||
+ | usermod -aG docker gitlab-runner | ||
+ | service docker restart | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== CI_REGISTRY error during connect status 255: Permission denied, please try again ==== | ||
+ | Раннер не может подключиться к реестру контейнеров: | ||
+ | <code bash> | ||
+ | $ echo " | ||
+ | error during connect: Post " | ||
+ | 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: | ||
+ | </ | ||
+ | Решение: | ||
+ | ===== Безопасность ===== | ||
+ | Shifting Security Left - GitLab DevSecOps Overview: https:// | ||
+ | |||
+ | Гитлаб предлагает сканеры безопасности: | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | Отчёты / управление: | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | |||
+ | ===== Прочее ===== | ||
+ | |||
+ | |||
+ | ==== Тестовый проект Gitlab from zero to hero ==== | ||
+ | https:// | ||
+ | |||
+ | Monorepo: https:// | ||
+ | |||
+ | ==== Example CI/CD Pipelines ==== | ||
+ | |||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | ===== Заметки о реальных проектах ===== | ||
+ | |||
+ | ==== Сервис личного кабинета в Docker Swarm ==== | ||
+ | |||
+ | <code bash> | ||
+ | # SSH / Context | ||
+ | ssh -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY $DEPLOY_USER@$DEPLOY_HOST | ||
+ | |||
+ | docker context create remote --docker " | ||
+ | docker context use remote | ||
+ | |||
+ | # Вход в репозиторий | ||
+ | docker login -u mail@example.com -p P@ssw0rd git.example.com: | ||
+ | # Пулл для локального тестирования | ||
+ | docker pull git.example.com: | ||
+ | docker pull git.example.com: | ||
+ | |||
+ | # Сети надо создавать 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 stack rm lk | ||
+ | |||
+ | docker exec $(docker ps -qf name=lk_app) mkdir -p / | ||
+ | |||
+ | mkdir -p / | ||
+ | chown -R 82:82 / | ||
+ | </ | ||
+ | |||
+ | <code yaml> | ||
+ | version: " | ||
+ | services: | ||
+ | app: &app | ||
+ | image: git.example.com: | ||
+ | healthcheck: | ||
+ | test: [" | ||
+ | volumes: | ||
+ | - / | ||
+ | networks: | ||
+ | - lk_backend | ||
+ | - lk_rabbitmq | ||
+ | |||
+ | nginx: | ||
+ | image: git.example.com: | ||
+ | environment: | ||
+ | NGINX_ROOT: / | ||
+ | NGINX_FASTCGI_PASS: | ||
+ | healthcheck: | ||
+ | test: [" | ||
+ | ports: | ||
+ | - 8080:80 | ||
+ | networks: | ||
+ | - lk_backend | ||
+ | |||
+ | db: | ||
+ | image: mysql:8.0 | ||
+ | environment: | ||
+ | MYSQL_DATABASE: | ||
+ | MYSQL_ROOT_PASSWORD: | ||
+ | MYSQL_PASSWORD: | ||
+ | MYSQL_USER: personal-area-user | ||
+ | SERVICE_TAGS: | ||
+ | SERVICE_NAME: | ||
+ | healthcheck: | ||
+ | test: [" | ||
+ | volumes: | ||
+ | - / | ||
+ | networks: | ||
+ | - lk_backend | ||
+ | |||
+ | rabbitmq: | ||
+ | image: rabbitmq: | ||
+ | hostname: rabbitmq | ||
+ | environment: | ||
+ | RABBITMQ_DEFAULT_USER: | ||
+ | RABBITMQ_DEFAULT_PASS: | ||
+ | healthcheck: | ||
+ | test: [" | ||
+ | ports: | ||
+ | - 5672:5672 | ||
+ | - 15672:15672 | ||
+ | volumes: | ||
+ | - / | ||
+ | networks: | ||
+ | - lk_rabbitmq | ||
+ | |||
+ | queue-lk-job-broadcast: | ||
+ | <<: *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: | ||
+ | |||
+ | |||
+ | networks: | ||
+ | lk_backend: | ||
+ | external: true | ||
+ | name: lk_backend | ||
+ | lk_rabbitmq: | ||
+ | external: true | ||
+ | name: lk_rabbitmq | ||
+ | </ |