Всем привет.
У нас не так много задач, которым необходим полноценный CI. Некоторое время мы использовали в качестве CI-сервиса Jenkins. Там всё довольно очевидно, он прост и гибок в настройке, имеет кучу плагинов, но пару раз мы столкнулись с OOM-убийцами агентов на слабых машинах и решили рассмотреть в качестве CI-сервиса Gitlab CI, потому что мы любим эксперименты и тем более в комментариях к нашей прошлой статье задавали такой вопрос.


Установка GitLab-CE



Тут всё довольно тривиально, т. к. есть Omnibus package.

Устанавливаем и запускаем необходимые пакеты:

sudo yum install curl openssh-server openssh-clients postfix cronie
sudo service postfix start
sudo chkconfig postfix on


Устанавливаем Gitlab-CE:

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo yum install gitlab-ce


Настраиваем и запускаем Gitlab-CE:

sudo gitlab-ctl reconfigure


Установка раннера



GitLab Runner — это агент, который собственно и занимается выполнением инструкций из специального файла .gitlab-ci.yml.
В отличие от Jenkins раннеры гитлаба написаны на Go, поэтому они очень маленькие и быстрые. И умеют запускать задачи совершенно различными способами: локально, в докер-контейнерах, в различных облаках или через ssh-коннект к какому либо серверу. Подробности, как всегда, в документации

Кто-то в комментариях к прошлой статье просил рассмотреть Gitlab для тестирования ansible-плейбуков, его и возьмём.
Для обеспечения идентичности среды тестирования и продакшен будем использовать Docker.

Для работы раннера в Docker — сначала необходимо установить docker:
curl -sSL https://get.docker.com/ | sh


Установка раннера

# Для Debian/Ubuntu
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash

# для RHEL/CentOS
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash


# Для Debian/Ubuntu
sudo apt-get install gitlab-ci-multi-runner

# Для RHEL/CentOS
sudo yum install gitlab-ci-multi-runner


Настройка и подключение раннера к CI-сервису:


sudo gitlab-ci-multi-runner register

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci )
http://domain.example.com/ci
Please enter the gitlab-ci token for this runner
bQ0nvkVJACDUrvQ9ttqx
Please enter the gitlab-ci description for this runner
my-runner
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, ssh?
docker
Please enter the Docker image (eg. ruby:2.1):
centos:7
INFO[0037] Runner registered successfully. Feel free to start it, but if it's
running already the config should be automatically reloaded!


Указываем URL нашего Gitlab, и прописываем токен для авторизации.
Также необходимо задать название раннера, способ запуска джоба, в случае с docker — указываем образ который будет запускать раннер.
Конфигурация для раннера указана по урлу example.com/groupname/projectname/runners
Здесь можно редактировать название раннера и метки, с которыми будет собираться проект на этом раннере. Например, нам нужно на разных стадиях протестировать проект сначала в shell, затем запаковать его в docker-контейнер и выкатить куда-нибудь по ssh. Об этом немного позже.

Оттуда необходимо взять URL мастера и токен для авторизации раннера на «мастере».



Запуск

sudo gitlab-ci-multi-runner start


После этого он должен появиться в списке раннеров проекта:



Установка Container Registry



Не так давно в Gitlab интегрировали Container Registry.
GitLab Container Registry — это защищённый приватный репозиторий для хранения Docker-образов (Docker images).
Ищем в /etc/gitlab/gitlab.rb секцию Container registry settings

registry_external_url 'https://domain.example.com'

# Settings used by GitLab application
gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "domain.example.com"
gitlab_rails['registry_port'] = "5000"
gitlab_rails['registry_api_url'] = "http://localhost:5000"
gitlab_rails['registry_key_path'] = "/var/opt/gitlab/gitlab-rails/certificate.key"
gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
# gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"

# Settings used by Registry application
registry['enable'] = true
registry['username'] = "registry"
registry['group'] = "registry"
# registry['uid'] = nil
# registry['gid'] = nil
registry['dir'] = "/var/opt/gitlab/registry"
registry['log_directory'] = "/var/log/gitlab/registry"
registry['log_level'] = "info"
registry['rootcertbundle'] = "/var/opt/gitlab/registry/gitlab-registry.crt"
registry['storage_delete_enabled'] = true


Из необходимого:
нужно выставить gitlab_rails['registry_enabled'] = true и registry['enable'] = true
В registry_external_url указываем доменное имя сервера, на котором будет находится репозиторий.

Также нужно найти следующие настройки:

registry_nginx['ssl_certificate'] = "/path/to/certificate.pem"
registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key"


И указать правильные пути к сертификатам.

Если будут использоваться самоподписные сертификаты, то на стороне docker-daemon, с которого будет проходить вся работа с образами нужно выставить опцию --insecure-registry, в противном случае при попытке залогинится — мы получим ошибку (раннеров тоже касается).

В Debian: /etc/systemd/system/multi-user.target.wants/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket

[Service]
Type=notify
ExecStart=/usr/bin/docker daemon -H fd:// --insecure-registry domain.example.com
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target


(Да, порт указывать не нужно. Существует API для registry — он висит на localhost:5000 и фронт, через который происходит авторизация и дальнейшая работа с образами. Я же долго искал способ перевесить API с локалхоста :) )

применяем изменения
systemctl daemon-reload

systemctl restart docker


И пробуем зайти под нашей учётной записью gitlab
docker login domain.example.com
Username: root
Password: 
Email: admin@example.com
WARNING: login credentials saved in /root/.docker/config.json
Login Succeeded



Ну что, теперь самое время что-нибудь с этим сделать, построить первый пайплайн и посмотреть, как проект будет собираться.

Настройка CI



Приступаем к тестированию ansible-плейбуков:

Я не буду вдаваться в глубины и рассказывать о serverspec и test-kitchen, о них уже было написано в моём прошлом посте.
Первым делом — собираем простой образ с окружением для тестов. Обойдёмся Dry run и ansible-lint.

vim Dockerfile
FROM centos:7 

RUN yum -y update && yum -y install epel-release     && yum -y install ansible python-pip

RUN pip install ansible-lint

# Default command 
CMD ["/bin/bash"]


Окей, собраем образ

docker build -t centos:7 .


и пушим в Registry

docker tag centos:7 domain.example.com/<groupname>/<projectname>
docker push domain.example.com/<groupname>/<projectname>


Теперь самое время описать пайплайн

В корне нашего проекта создаём файл .gitlab-ci.yml (опять YAML, ага) и описываем шаги сборки проекта.

image: domain.example.com/root/my-repo


stages:
  - test
  - deploy

test_job:
  stage: test
  script:
    - ansible-lint playbook.yml
    - ansible-playbook --check playbook.yml

  tags:
    - ansible

deploy_job:
  stage: deploy
  script:
    - ansible-playbook playbook.yml

  tags:
    - ansible


Здесь мы указываем стадии сборки проекта. На стадии тестирования — мы проходимся lint'ом на предмет синтаксических ошибок и запускаем Ansible в Dry mode, то есть не применяя изменения на хосте, а просто их показывая. Если вдруг что-то ломается во время проигрывания плейбука — мы об этом узнаем до того, как конфигурация на хосте изменится.
Соответсвенно, если мы сейчас попробуем в плейбук добавить где-нибудь пробелов — то конфигурация не применится, lint сообщит об ошибке и упадёт на этапе тестирования, о чём можно узнать после коммита прямо в веб-интерфейсе gitlab.





У .gitlab-ci.yml очень много различных опций на все случаи стадии жизни проекта. К сожалению, за один вечер со всем ознакомиться не удалось.

Как видите, Gitlab ничем не хуже других CI-сервисов, имеет позитивный и удобный интерфейс, но есть особенности в плане написания сценария тестирования и деплоя.

Спасибо за внимание. Удачной автоматизации!
Поделиться с друзьями
-->

Комментарии (14)


  1. QtRoS
    30.08.2016 07:32

    Правильно ли я понял, что CI в таком варианте, по сути, настраивается для одного репозитория? Зависимости от нескольких репозиториев можно сделать?
    Как дела обстоят с шаблонами? Допустим, у меня несколько контуров тестирования, девеоперский и продакшн, и все нужно собирать похожим образом с похожим наборов параметров. Удастся ли тут легко организовать такую схему?
    Возможно, глупый вопрос, но все же — Python скрипты можно пускать в шагах сборки?


    1. nik_OS
      30.08.2016 08:55
      +2

      1. Настраивается не только для одного репозитория, но и если необходимо для каждой отдельной ветки.
      2. Шаблонов вроде пока еще не завезли, но разные джобы на разные окружения завести довольно просто, как и описано в статье
      3. В джобе есть такой раздел script: и в нем вы можете прописать любую последовательность команд, вы бы делали в терминале, если бы собирали ручками. Соответственно для запуска каких либо скриптов, скорее всего достаточно подключить docker-image с необходимыми зависимостями.

      А вообще кроме этой статьи еще советую прочитать на ту же тему блог гитлаба https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/


      1. axdr
        30.08.2016 10:41

        Так же есть отличная статья по ознакомлению с GitLab CI и понятию CI в целом.


    1. NelSon29
      30.08.2016 10:41
      +1

      На своей работе используем gitlab, в частности gitlab-CI.

      > Допустим, у меня несколько контуров тестирования, девеоперский и продакшн, и все нужно собирать похожим образом с похожим наборов параметров. Удастся ли тут легко организовать такую схему?
      Да, это делается достаточно легко. Помимо этого, возможно создать зависимости, например, если промежуточные тесты не проходят, CI-сервер даже не будет приступать к продакшн тестам.

      > Python скрипты можно пускать в шагах сборки?
      Конечно. На компьютере, который играет роль сервера, можно развернуть runner, который будет выполнять написанные сценарии либо в некой виртуальной машине, контейнере Docker, либо же в обычной командной строке (как cmd, так и bash).

      > CI в таком варианте, по сути, настраивается для одного репозитория?
      Верно, в каждом репозитории лежит свой файл сценария сборки и тестирования.

      > Зависимости от нескольких репозиториев можно сделать?
      Смотря что вы имеете в виду под зависимостями. Зависимость от успешной сборки другого, нигде не указанного репозитория? Зависимость от подмодуля?

      P.S. Писал в личный блог пост по применению этой системы для сборки C++ проектов для Visual Studio и ARM DS-5. Если сообществу будет интересно, приведу в более приемлемый для хабра вид и опубликую.

      Как найти
      Пока можно прогуглить по фразе «Автоматизированная сборка C++ проектов в Gitlab CI»


    1. Serhii_M
      30.08.2016 10:41

      Все build steps указываються в .gitlab-ci.yml, который лежит в корне репозитория и да — для одного репозитория.
      В .gitlab-ci.yml можно указывать enviroments, которые можно настроить в настройках проекта ( pipelines ), соответсвенно конфигурация может меняться, в зависимости от того, для какого енва ты билдишь и куда деплоишь.
      Можно запускать все что угодно — главное, что бы на машине, на которой раниться gitlab-runner были все необходимые компоненты.

      Мы используем собранный под себя имейдж докера с нодой и некоторыми другими инструментами для билда. И все инструкции описанные в .gitlab-ci.yml выполняются в этом контейнере.


  1. Anthrax_Beta
    30.08.2016 10:41
    -1

    Отличная, на мой взгляд статья. Хорошая альтернатива Jenkins-у.


  1. tuupic
    30.08.2016 10:43
    +1

    Как-то, на уровне подсознания, инструкции установки вида «curl некий_магический_скрипт | bash» вызывают недоверие.


    1. Anthrax_Beta
      30.08.2016 10:56

      Никто не мешает скачать скрипт и посмотреть, что он делает


  1. RaveNoX
    30.08.2016 10:58

    Мы у себя рассматривали gitlab-ci как альтернативу jenkins для сборки .net проекта. На текущий момент как ci он нормально работает, но есть пару моментов:
    — нельзя задать какое количество сборок, для которых нужно хранить артефакты, только время их хранения.
    — сам файл конфигурации хранится в репозитории как результат сложно сделать схему, когда deploy должен быть предварительно одобрен (или в ручную запущен) ограниченным количеством ответственных лиц. Это можно попробовать сделать при помощи protected branch, но никто не мешает разработчику изменить файл в своём бранче и залить что угодно на прод.
    В результате как cd его пока использовать сложно.

    Если есть у кого-нибудь опыт использования в реалиях, которые я описал, прошу поделиться своим опытом.


    1. Prototik
      30.08.2016 13:41
      +1

      когда deploy должен быть предварительно одобрен (или в ручную запущен) ограниченным количеством ответственных лиц

      Как вариант — в одной из последних версий gitlab'a появились блокировки на файл. Какой-нибудь team-lead или deploy-manager лочит на себя этот файл — и все остальные лишаются возможности редактировать этот файл.


    1. vitalybaev
      30.08.2016 18:45

      Учитывая, как развивался GitLab в последнее время, легко можно запросить такую фичу.


    1. zartdinov
      30.08.2016 19:09

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


    1. rekby
      31.08.2016 00:44

      Посмотрите http://docs.gitlab.com/ce/ci/yaml/README.html#stages
      Manual actions

      возможно это то, что вам нужно.

      Ограничения по изменениям — можно просто административно. Если этого недостаточно — завести отдельный репозиторий из которого будет делаться публикация (и для этого репозитория прописать шаг запуска публикации).
      Для этого же спец. репозитория заданы переменные, необходимые для публикации (ключи доступа на сервер например), без которых публикация невозможна.
      В этом случае разработчик не сможет путем локальных изменений что-то опубликовать, т.к. доступа к спец. репозиторию не имеет.


  1. farcaller
    30.08.2016 19:00

    Если будут использоваться самоподписные сертификаты, то на стороне docker-daemon, с которого будет проходить вся работа с образами нужно выставить опцию --insecure-registry, в противном случае при попытке залогинится — мы получим ошибку (раннеров тоже касается).

    Как насчет добавить самоподписанный сертификат в список корневых?