
Всем привет! Мы продолжаем серию статей про DevOps и ищем наиболее эффективные способы управлять конфигурацией, делясь с вами опытом. В прошлых статьях мы рассматривали, как выстроить управление конфигурацией Ansible с помощью Jenkins и Serverspec, а теперь по вашим просьбам рассмотрим, как организовать управление конфигурацией с помощью GitLab-CI.
Ansible-lint — это утилита для проверки корректности синтаксиса плейбука и стиля кода, которую можно интегрировать в CI-сервис. В нашем случае мы внедряем её в gitlab-ci для проверки плейбуков на этапе принятия Merge-Request и выставления статуса проверок.
GitLab (GitLab Community Edition) — это opensource-проект, менеджер git-репозиториев, изначально разрабатывающийся как альтернатива платной корпоративной версии Github.
Установка GitLab CE описана в этой статье.
Устанавливаем gitlab-ci-multirunner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash yum install gitlab-ci-multi-runner
Регистрируем runner
gitlab-ci-multi-runner register
Теперь нужно ответить на вопросы:
Running in system-mode. 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: your_token Please enter the gitlab-ci description for this runner: [domain.example.com]: Please enter the gitlab-ci tags for this runner (comma separated): ansible Registering runner... succeeded runner= Please enter the executor: docker-ssh+machine, docker, docker-ssh, parallels, shell, ssh, virtualbox, docker+machine: shell Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Токен и URL берем со страницы настроек проекта.


Особенности настройки multi-runner.
Один раннер может обрабатывать несколько проектов. Для того, чтоб один раннер обрабатывал всё подряд, нужно просто зарегистрировать новый раннер, не указывая теги. Токен для shared-раннера берем в Admin Area:

Можно пачку раннеров разнести по разным серверам. И ещё: в gitlab, как и в Jenkins, есть такое понятие, как теги. Проект с тегом ansible будет обрабатывать раннер, помеченный тегом ansible, а для других проектов, без метки или с другой меткой, этот раннер работать не будет.
Также в админке можно настроить соответствие раннера и проекта.
domain.example.com/admin/runners

Установка Ansible-lint
Ansible-lint устанавливается через python-pip или из репозитория EPEL.
Первый способ:
Сначала устанавливаем python-pip, затем через него ставим ansible-lint:
sudo yum groupinstall @Development Tools sudo yum install python-pip sudo pip install --upgrade pip sudo pip install ansible-lint
Второй способ
Всё просто — ставим epel-release и ставим ansible-lint через пакетный менеджер:
sudo yum install epel-release sudo yum install ansible-lint
Настройка пайплайна.
Создаем в корне репозитория файл .gitlab-ci.yml. Важно соблюдать количество пробелов, иначе будет ошибка — yaml такое не прощает, ага.
stages: [два пробела]- test test_job: [два пробела]stage: test [два пробела]script: [четыре пробела]- ansible-lint *.yml [два пробела]tags: [четыре пробела]- ansible
stages — обязательно описываем стадии сборки.
stages: - stagename
test_job — произвольное название джоба.
stage: test —обязательно описываем стадию test, указанную в секции stages.
jobname: stage: stagename
script — проводим тест в корне репозитория
tags — метка для раннера.
Название стадии stage: test может быть любым, например: converge, pre-test, post-test, deploy.
Название джоба test_job также может быть любым.
В GitLab есть встроенный линтер для пайплайнов. Проверить корректность синтаксиса пайплайна можно по URL domain.example.com/ci/lint
Вставляем пайплайн, жмём Validate.

При ошибке линтер будет ругаться и укажет на ошибку.

Должно быть stages, а не stage.
Таким образом ansible-lint будет автоматически при каждом коммите проверять все плейбуки с расширением .yml из корня репозитория.
У нас в репозитории две роли:
roles ├── monit └── openssh
И два плейбука, накатывающих эти роли:
├── monit.yml ├── openssh.yml ├── README.md └── roles
openssh.yml
--- - hosts: all user: ansible become: yes roles: - openssh
monit.yml
--- - hosts: all user: ansible become: yes roles: - monit
Следовательно, проверяя плейбук, присваивающий роль, мы проверяем все ее содержимое:
roles/openssh/tasks/ ├── configure_iptables.yml ├── configure_monitoring.yml ├── configure_ssh.yml └── main.ym
roles/monit/tasks/ ├── configure_monit.yml ├── configure_monit_checks.yml ├── install_monit.yml └── main.yml
Теперь, при коммите ansible-lint будет запущен автоматически и проверит все перечисленные в плейбуках роли.
Если попробовать что-нибудь закоммитить и перейти в веб-интерфейс (вкладка pipelines), то можно увидеть джоб в статусе failed.

Для того, чтоб отключить проверки lint'ом при пуше в репозиторий, достаточно вычистить в файле .gittab-ci.yml все стейджи, касающиеся запуска проверок ansible-lint.
Параметры проверки плейбуков также настраиваются:
╰─>$ ansible-lint --help Usage: ansible-lint playbook.yml Options: --version show program's version number and exit -h, --help show this help message and exit -L list all the rules -q quieter, although not silent output -p parseable output in the format of pep8 -r RULESDIR specify one or more rules directories using one or more -r arguments. Any -r flags override the default rules in /usr/local/lib/python2.7/dist- packages/ansiblelint/rules, unless -R is also used. -R Use default rules in /usr/local/lib/python2.7/dist- packages/ansiblelint/rules in addition to any extra rules directories specified with -r. There is no need to specify this if no -r flags are used -t TAGS only check rules whose id/tags match these values -T list all the tags -v Increase verbosity level -x SKIP_LIST only check rules whose id/tags do not match these values --nocolor disable colored output --exclude=EXCLUDE_PATHS path to directories or files to skip. This option is repeatable.
╰─>$ ansible-lint -L ANSIBLE0002: Trailing whitespace There should not be any trailing whitespace ANSIBLE0004: Git checkouts must contain explicit version All version control checkouts must point to an explicit commit or tag, not just "latest" ANSIBLE0005: Mercurial checkouts must contain explicit revision All version control checkouts must point to an explicit commit or tag, not just "latest" ANSIBLE0006: Using command rather than module Executing a command when there is an Ansible module is generally a bad idea ANSIBLE0007: Using command rather than an argument to e.g. file Executing a command when there is are arguments to modules is generally a bad idea ANSIBLE0008: Deprecated sudo Instead of sudo/sudo_user, use become/become_user. ANSIBLE0009: Octal file permissions must contain leading zero Numeric file permissions without leading zero can behavein unexpected ways. See http://docs.ansible.com/ansible/file_module.html ANSIBLE0010: Package installs should not use latest Package installs should use state=present with or without a version ANSIBLE0011: All tasks should be named All tasks should have a distinct name for readability and for --start-at-task to work ANSIBLE0012: Commands should not change things if nothing needs doing Commands should either read information (and thus set changed_when) or not do something if it has already been done (using creates/removes) or only do it if another check has a particular result (when) ANSIBLE0013: Use shell only when shell functionality is required Shell should only be used when piping, redirecting or chaining commands (and Ansible would be preferred for some of those!) ANSIBLE0014: Environment variables don't work as part of command Environment variables should be passed to shell or command through environment argument ANSIBLE0015: Using bare variables is deprecated Using bare variables is deprecated. Update yourplaybooks so that the environment value uses the full variablesyntax ("{{your_variable}}").
Некоторые таски можно пропускать при проверке ansible-lint не очень любит модули command и shell, так как в идеологии ansible считается, что штатных core-модулей хватает для всех задач. На самом деле, это не всегда так.
К примеру, у нас в роли встречается таск, использующий модуль command:
- name: Installing monit command: yum -y install monit tags: monit
И если пролинтим плейбук с этой ролью, то ansible-lint сругнётся на то, что мы используем модуль command
╰─>$ ansible-lint monit.yml [ANSIBLE0002] Trailing whitespace monit.yml:7 - monit [ANSIBLE0012] Commands should not change things if nothing needs doing /tmp/ansible-lint/roles/monit/tasks/install_monit.yml:8 Task/Handler: Installing monit [ANSIBLE0006] yum used in place of yum module /tmp/ansible-lint/roles/monit/tasks/install_monit.yml:8 Task/Handler: Installing monit
Чтобы этого избежать, можно пометить таск тегом skip_ansible_lint:
- name: Installing monit command: yum -y install monit tags: monit,skip_ansible_lint
Теперь при прогоне lint'а по плейбуку он не будет ругаться на используемый модуль command:
╰─>$ ansible-lint monit.yml [ANSIBLE0002] Trailing whitespace monit.yml:7 - monit
Особенности Merge Request.
Забегая вперёд, пару слов о функционале проверок в MR. По-умолчанию, merge-request принимается только при успешной проверке. Отключить это можно в настройках проекта, в разделе Merge Requests:

Ansible-lint также можно запустить на локалхосте, не делая коммиты и не дожидаясь проверки CI-сервисом. Если у на десктопе Linux или OS X — просто установите его к себе.
Примеры коммитов с ошибкой, как это выглядит в gitlab.
1. Открываем файл во встроенном редакторе GitLab:

2. Вносим изменения. Например, yaml очень чувствителен к пробелам, попробуем добавить лишний пробел в начале какой-нибудь строки:

Жмём Commit Changes, возвращаемся к изменённому файлу. Справа вверху появится пиктограмма со статусом проверки:

Сейчас она в статусе Pending, так как проверка ещё не завершена. Если ткнуть на пиктограмму — перейдем к статусу проверки нашего свежесделанного коммита:

Он сейчас в статусе Failed, так как мы умышленно допустили ошибку в синтаксисе. Если ткнуть на пиктограмму Failed, то мы сможем увидеть результат работы ansible-lint:

Можно прилепить симпатичный баджик со статусом сборки в README.md
[](http://domain.example.com/projectname/commits/master)
Взять его можно в настройках проекта, в разделе CI/CD Pipelines

Копипастим маркдаун и добавляем в README.md в корне проекта, коммитим, теперь статус проверки отображается на главной странице проекта:

Зеленый / Passed — если проверка прошла успешна.
Красный / Failed — если проверка завершилась с ошибкой.
Во вкладке Pipelines можно посмотреть статусы всех коммитов:

Таким образом, мы выстроили процесс контроля корректности синтаксиса при управлении конфигурацией. Спасибо за внимание и всем удачной автоматизации!
Автор: DevOps-администратор Centos-admin — Виктор Батуев.

