Приручаем GitLab: прикольные фишки и инциденты, которые упростят вашу жизнь

—

от автора

Глава 1: То, что и так видят все, но не каждый готов признать

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

В текущих реалиях все IT-продукты разрабатываются с использованием какого-либо ПО, способного управлять репозиториями программного кода для Git. В нашем случае, хотелось бы рассказать про один из самых популярных продуктов Gitlab. «Gitlab — наше всё» должно быть слоганом каждой компании, которая его использует, иначе могут произойти события, которые приведут к печальным последствиям. На Habr можно найти множество различной информации, связанной с кейсами, туториалами или просто интересными историями про GitLab. Но сколько бы ни было написано, найти место где было бы собрано всё и сразу — не получилось. Придется исправлять. 

Начнём?

Глава 2: Latest говорит не только о том, что релиз последний, но и о том, что всем вашим релизам может прийти конец

Новый день, солнце, душ, кофе и разработчик садится разбирать эпик. Первое, что он там видит — это деплой в среду тестирования нового функционала. Смело вешая тэг на master ветку, его релиз уверенно падает на стадии build-а образа контейнера. Как такое могло произойти и почему так вышло? Неожиданный результат, опубликованный в рамках канала slack, создаёт отклик в душе других сотрудников компании.

Коллеги, не работают все процессы CI/CD, все релизы и тесты падают с различными ошибками.

Как бы это странно ни звучало, но все Devops-инженеры, изучая проблему, приходят в лёгкое удивление, ведь причина возникновения множества ошибок непонятна. То, что вчера ещё работало — сегодня уже остановило весь процесс разработки. 

Лучше всегда всё начинать сначала, этот подход помог и здесь. Ранее при первоначальных настройках различных pipelines во все проектах для стадий build, test и deploy использовался всем известный подход DIND (Docker-in-Docker) и тут нас не может не радовать, что образ для всех job имеет тэг latest.

build_rc:     stage: build     image: docker:dind

Вечерним релизом (накануне проблемы) разработчики продукта docker произвели обновление версии docker в образе dind до 24.0 и все описанные старым способом процессы сборки, тестирования и деплоя начали падать с ошибками docker. Быстрое понимание проблемы помогло найти и решение, изменив текущий tag образа в каждой джобе на 23.0-dind, но и тут возникла весьма интересная проблема, которая говорит об отсутствии детального понимания принципов настройки Gitlab и его раннеров. Образ джобы не был описан глобально, а в индивидуальном варианте для каждого pipeline. «Зачем?» — остается загадкой. В процессе настройки runner-ов каждый имеет свой подход:

  • Закрепление runners за определенными группами, подгруппами, репозиториями.

  • Настройка runners для деплоя джоб имеющих соответствующий ему tag.

  • Подключение runners глобально на уровне панели администратора, предоставляя доступ к нему всем проектам вашего Gitlab.

Если же вы используете именно последний подход, то самым простым и правильным вариантом решения для вас будет настройка образа джобы именно на уровне этого раннера в config.toml:

concurrent = 10 check_interval = 0 shutdown_timeout = 0  [session_server]   session_timeout = 1800  [[runners]]   name = "runner-01.git.test.ru"   url = "https://git.test.ru/"   id = 44   token = "super_secure_token"   token_obtained_at = 2023-07-13T10:24:48Z   token_expires_at = 0001-01-01T00:00:00Z   executor = "docker"   [runners.cache]     MaxUploadedArchiveSize = 0     [runners.cache.s3]     [runners.cache.gcs]     [runners.cache.azure]   [runners.docker]     tls_verify = false     image = "docker:23.0-dind"     privileged = true     disable_entrypoint_overwrite = false     oom_kill_disable = false     disable_cache = false     volumes = ["/cache"]     shm_size = 0

Что получается в итоге? 

При отсутствии описания во всех ваших .gitlab-ci.yml образа джобы, по умолчанию будет вызываться необходимый нам образ docker:23.0-dind. В результате подобного инцидента, который произошёл фактически из-за очень маленькой ошибки разработки, трудозатраты DevOps-инженеров на причину понимания проблемы, глобальные правки во всех манифестах .gitlab-ci.yml, которые не имели бы никакого смысла при исходно правильной настройке.

Глава 3: Сохранись, а то мало ли

Тот же день, пекло, обед, сэндвич, латте, yaml манифесты и IaC (Infrastructure-as-Code).

IaC — это очень популярный и полезный подход, так как если мы говорим про Kubernetes, то в первую очередь мы подразумеваем декларативный подход. Соответственно, когда мы планируем использовать Terraform с использованием облачного провайдера для описания нашей инфраструктуры, то наш путь исконно верный. Ещё один интересный кейс, с которым мы столкнулись в схожей проблеме, был завязан вновь на неправильном подходе или настройке Gitlab. 

Описав развертывание большого количества виртуальных машин в облаке и параллельно настроив сеть для них, межсетевое взаимодействие, security groups, релиз был успешно запушен в репозиторий, где посредством заранее описанного процесса CI/CD выполнялись стадии validate, plan, apply и сохранение terraform.state в Infrastructure/Terraform.

После успешного отрабатывания всех 3-х стадий, можно фактически убедиться в веб-панели облака о наличии развернутых ресурсов, но каким было удивление, когда было обнаружено, что terraform.state был записан не полностью? Получается, все повторные запуски нашего pipeline при изменении ресурса имели безукоризненное падение, а при анализе вывода успешно отработанных джоб результат посмотреть не удавалось из-за ограниченности вывода количества информации в output.

Как быть и как этого избежать?

Вариантов решения данного вопроса множество, в первую очередь надо не забывать о существующих ограничениях в Gitlab на размер вашего terraform.tfstate:

Во-вторых, не забываем поднимать лимит по количеству строк для gitlab-runners, добавляя output_limit в основной блок:

concurrent = 10 check_interval = 0 shutdown_timeout = 0  [session_server]   session_timeout = 1800  [[runners]]   name = "runner-01.git.test.ru"   url = "https://git.test.ru/"   id = 44   token = "super_secure_token"   token_obtained_at = 2023-07-13T10:24:48Z   token_expires_at = 0001-01-01T00:00:00Z   executor = "docker"   output_limit = 50000000   [runners.cache]     MaxUploadedArchiveSize = 0     [runners.cache.s3]     [runners.cache.gcs]     [runners.cache.azure]   [runners.docker]     tls_verify = false     image = "docker:23.0-dind"     privileged = true     disable_entrypoint_overwrite = false     oom_kill_disable = false     disable_cache = false     volumes = ["/cache"]     shm_size = 0

В-третьих, очень важным моментом является разделение создаваемых ресурсов Terraform на различные модули. Как пример, вынос создания Network и Security groups в отдельные модули и дальнейшее переиспользование готовых сущностей через data sources для упрощения понимания аспектов управления инфраструктурой и уменьшение исходного tfstate.

Глава 4: Процессы, подходы и слёзы

Самая интересная и самая любимая часть каждого Devops-инженера это, само собой, реализация подхода деплоя и здесь, когда спрашиваешь у коллеги: «А как сделали вы?», возникает целая дискуссия с дебатами и беспрерывными выяснениями, а кто же прав.

Поехали? 🙂 

Каждый любит строить проект и процессы деплоя с нуля, но если вы находитесь на любой из стадий, то обратите сразу внимание на ряд принципиально важных моментов, которые упростят вам жизнь.

  1. Распределяйте все микросервисы по раздельным репозиториям в рамках определенной группы или подгруппы — это упростит предоставления доступов для каждого проекта, а также поможет в реализации использования Gitlab CI/CD variables. Тут же хочется поделиться, что периодически встречается архитектура, когда в рамках одного репозитория лежит 20 каталогов с различными микросервисами, где используются разные подходы и исходные образы для деплоя. «Зачем и почему?» — вопросы хорошие. 

  2. Определяйте переменные для Gitlab CI/CD пайплайнов глобально в панели администратора или на уровне групп, подгрупп. Согласитесь, дублировать одну и ту же переменную в рамках каждого репозитория — себя не уважать. Особенно будет интересно править их все после изменения переменной или протухания токена.

  3. Отойдите от концепции использования кастомных .gitlab-ci.yml в рамках каждого процесса деплоя. Пишите шаблонизированные .gitlab-ci.yml, подвязываясь под внутренние переменные самого Gitlab, для реализации этого подхода вполне прекрасно подходят include, extends, reference. В дальнейшим их можно с лёгкостью переиспользовать для деплоя других микросервисов:

    Глобальный конфиг build:

.build_template: &build  image: nexus.test.ru/generic-images/dind:dind-latest  variables:    DOCKER_HOST: tcp://docker:2375/    DOCKER_DRIVER: overlay2    DOCKER_TLS_CERTDIR: ""  services:    - docker:dind  script:    - docker login -u $CI_REGISTRY_USER_NEXUS -p $CI_REGISTRY_PASSWORD_NEXUS $CI_REGISTRY_NEXUS    - docker build --no-cache -t ${CI_REGISTRY_NEXUS}/k8s-${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:${CI_COMMIT_SHA} .    - docker push ${CI_REGISTRY_NEXUS}/k8s-${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:${CI_COMMIT_SHA}

Глобальный конфиг deploy:

.deploy_template: &deploy   image:      name: nexus.test.ru/generic-images/k8s-deployer:latest   variables:     HELM_KUBECONTEXT: ${KUBE_CONTEXT}   script:     - helm repo add nixys https://registry.nixys.ru/chartrepo/public     - helm repo update     - helm upgrade       --install ${CI_PROJECT_NAME} nixys/universal-chart       --namespace ${NAMESPACE}       --insecure-skip-tls-verify       --set "defaultImage=${CI_REGISTRY_NEXUS}/k8s-${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}"       --set "defaultImageTag=${CI_COMMIT_SHA}"       --set "secretEnvsString=${VAULT_IMAGES_ENV}"       --create-namespace       --values ${FILE}       --version 2.4.0       --debug

Частный конфиг в виде джоб в pipeline:

include:  - project: ‘test-devops/ci-cd/sample-ci-cd'    ref: main    file: '.build.yml'  - project: 'test-devops/ci-cd/sample-ci-cd'     ref: main     file: '.deploy.yml'  stages:  - build  - deploy  ### #  PROD ###  build:  extends: .build_template  stage: build  tags:    - dind  variables:    KUBE_CONTEXT: ${CI_PROJECT_NAMESPACE}/k8s-agents:kubernetes-ya-default-cluster  only:    refs:      - main  deploy-prod:   extends: .deploy_template   when: manual   stage: deploy   tags:     - k8s-runner-prod   needs:     - job: build   variables:     KUBE_CONTEXT: ${CI_PROJECT_NAMESPACE}/k8s-agents:kubernetes-ya-default-cluster     NAMESPACE: "prod"     FILE: ".helm/prod.yml"   environment:     name: production   only:     refs:       - main  ### #  DEV ###  deploy-dev:   extends: .deploy_template   when: manual   stage: deploy   tags:     - k8s-runner-dev   needs:     - job: build   variables:     KUBE_CONTEXT: ${CI_PROJECT_NAMESPACE}/k8s-agents:kubernetes-ya-develop-cluster     NAMESPACE: "dev"     FILE: ".helm/dev.yml"   environment:     name: dev   only:     refs:       - develop
  1. Если вы используете Gitlab CI/CD variables и у вас есть переменные, идентичные по названию, но разные по содержанию — всегда используйте Environments.

build dev-kg:     stage: build     services:         - docker:dind     environment:         name: dev-kg

Самое интересное в этой маленькой, но приятной фиче, что у вас есть возможность использовать регулярные выражения при указании переменных в Gitlab, что делает весь этот механизм ещё более простым и комфортным.

  1. Выберите свой подход в процессе Continuous Deployment/Delivery приложений и микросервисов, если вы работаете с kubernetes. На данный момент есть несколько вариантов деплоя.

6.1. Деплой в kubernetes, OLD средствами.

Старое — не значит неработающее. Создаём Namespace, ServiceAccount, Secret, rbac policy и генерируем для него kubeconfig, с помощью которого можно будет безопасно и уверенно совершать деплой в определённый Namespace.

Удобен ли данный вариант? — Вряд ли

Но он помогает детально понимать все аспекты деплоя со стороны безопасности в рамках кластера k8s и опять же в данном способе нет никаких завязок на стороннее ПО. При этом можно с легкостью автоматизировать весь этот процесс, написав Terraform модуль на основе kubernetes provider и gitlab provider. Но если безопасность у вас на данный момент не на первом месте и в целом вы человек прогрессивный, то никто не мешает обратиться к современным подходам.

Перейдём к динозаврам…

6.2. Деплой в kubernetes через Gitlab интеграцию (Kubernetes cluster) — да, да, да.

Для тех кто не знал, расскажу просто и легко. После предоставления ca.crt вашего кластера kubernetes Gitlab-у, вы получаете из группы или подгруппы настроенный деплой из коробки.

Если конкретизировать, то используя ca.crt, Gitlab integration генерирует kubeconfig для деплоя в namespace, добавляя его в виде tmp файла в рамках запущенной джобы вашего pipeline.

Получается все шаги описанные в пункте 1 можно не делать? — да

Помимо упрощения процесса деплоя эта интеграции предоставляет большое количество дополнительных функций. Как пример, при добавлении к deployment annotations можно было получить доступ до shell контейнера прямо из web интерфейса Gitlab.

Но не бывает хороших новостей без плохих, в версии Gitlab 17.0.0 данный функционал возможно уберут и заменят на другой подход реализации процесса интеграции, если ранее процесс деплоя настраивался из Gitlab до K8S через kubeconfig, то теперь процесс работает по принципу взаимодействия:

Gitlab  with API Token- > gitlab-agent (предоставляем kubeconfig с правами на деплой)-> kubernetes

6.3. Деплой в kubernetes через Gitlab интеграцию (Kubernetes cluster).

Новый, актуальный, свежий и как уже было написано ранее — безопасный вариант. В настройке он также весьма прост. Делаем инсталляцию helm chart-а готовыми командами, предоставляемыми Gitlab-ом, и создаём конфигурацию в рамках репозитория, в которой мы подключали наш gitlab-agent:

В нашем репозитории необходимо создать схожую структуру с одним yaml конфигом, в рамках которого будут описаны те проекты, которым должны предоставлять возможности деплоя через наш gitlab-agent. Файл лежит по следующему пути и выглядит примерно следующим образом:

.gitlab/agents/kubernetes-ya-default-cluster/config.yaml
ci_access:  projects:  - id: test/test # Project/Repo name to authorize  - id: test/backend  - id: test/websocket  - id: test/static  observability:    logging:        level: debug

Здесь важно  понимать, что вам нужно точечно на уровне репозиториев или глобально на уровне группы предоставить доступ для деплоя через gitlab-agent. Важно заметить, что gitlab-agent не может как и ранее выйти за пределы группы Gitlab. Получается в случае, если у вас 2 группы с микросервисами backend и frontend, то вам придётся для одного кластера создавать и деплоить 2 разных агента. Выглядит как минус и лишняя работа, но это маленький шаг в сторону безопасности. Однако нельзя забывать и про существенный минус, с которым можно столкнуться.

Все инженеры любят (и правильно делают), когда обновляют Gitlab — важным моментом здесь является то, что люди, использующие интеграцию для деплоя, неожиданно для себя и для своей команды узнали об уходе в deprecated старого механизма деплоя. Перед обновлением на 17.0.0 версию Gitlab надо будет кардинально подготовиться и переключить все существующие деплои, потеряв часть приятного интерактивного функционала и минусом является даже не это, а то, что вероятнее всего в будущем ситуация повторится и нужно предельно внимательно следить за релизами и это, по сути, является императивной нормой любого продукта.

  1. Обновления — от них не застрахован никто.

Весьма частая и регулярная задача, с которой сталкиваются администраторы и DevOps-инженеры — это обновление Gitlab. Первая ошибка новичка, само собой, обновиться сразу с версии 13 до версии 16.2.1 в 1 шаг. На этот случай Gitlab уже давно разработали весьма удобную площадку, описывающую поэтапность обновления с релизами c вашей версии до последней актуальной. Но самая банальность проблемы заключается в не изучении изменения функционала релиза.

P.S. К ссылочке выше

Используйте VPN, чтобы открыть сайт.

К примеру, все ваши процессы build и deploy контейнера завязаны на использовании вышеописанных глобальных CI/CD переменных:

before_script:   - export RELEASE_VERSION=$(echo $CI_BUILD_REF_NAME | grep -o '[[:digit:]]\{1,5\}\.[[:digit:]]\{1,5\}\.[[:digit:]]\{1,5\}' || echo development)  build_rc:   stage: build   services:     - docker:dind   script:     - docker login -u $CI_REGISTRY_USER_NEXUS -p $CI_REGISTRY_PASSWORD_NEXUS $CI_REGISTRY_NEXUS     - docker build --no-cache -t ${CI_REGISTRY_NEXUS}/k8s-${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:${RELEASE_VERSION} .     - docker push ${CI_REGISTRY_NEXUS}/k8s-${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:${RELEASE_VERSION}

В последнем релизе происходит удаление этой переменной или изменения её названия. Результат: все текущие и ранее актуальные pipeline переходят в нерабочий статус, что сразу же грозит простоем разработки и разбором нового инцидента.

Глава 5: Почему не получилось пройти мимо?

Вечер, горячий чай, тёплый ужин и осознание…

Имея под рукой Gitlab, у вас появляется большое количество возможностей и большое количество потенциальных проблем. Регулярные релизы со стороны разработки продукта покрывают оба описанных аспекта ранее, но многое зависит от инженера, реализующего подход как в деплое, так и в поддержке.

Прочитав всё описанное выше, можно сказать, что многие вещи уже рассказаны в разных статьях и мануалах, но каждый раз, подключаясь на проект, видишь одни и те же ошибки или проблемы, которые не могут не огорчать. Порой ошибаются все и никто не безгрешен, но вовремя понять потенциальную проблему, нависшую над текущими рабочими процессами, и исправить её сразу — лучше, чем игнорировать и получать с утра пораньше сообщения о глобальном инциденте всех конвейеров.

Главное — это понять, что правильные решения, видение инфраструктуры и процессов через 6 месяцев, поможет ускорить решение задач как со стороны инженеров, так и со стороны бизнеса. Ведь чем быстрее мы деплоим, тем быстрее растём.
Получается, крепких пайплайнов и быстрых релизов!

Если есть вопросы — пишите в комментариях. И, конечно, подписывайтесь на наш блог Хабр, TG-канал DevOps FM, интернет-издание VC и YouTube — мы всегда рады новым друзьям!


ссылка на оригинал статьи https://habr.com/ru/articles/758068/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *