Хабрахабр

Docs as Code. Часть 1: автоматизируем обновление

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

Я покажу, как можно применять эту философию в реалиях classified-сервиса, а точнее, начну с первого этапа её внедрения: автоматизации обновления данных в документации.

Набор инструментов

Главная цель: создать условия для совместной работы всей команды над итоговым результатом — полноценной базой знаний и инструкций по использованию отдельных сервисов продукта. Принцип «документация как код» подразумевает использование при написании документации того же инструментария, что и при создании кода: языков разметки текста, систем контроля версий, code review и авто-тестов. Далее я расскажу о конкретных инструментах, выбранных нами для решения этой задачи.

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

Он позволяет создавать статические сайты, для которых можно делать собственные или использовать уже готовые темы оформления. Файлы конвертируются из .rst в .html посредством генератора документации Sphinx. Вторая содержит подтемы, позволяющие задавать разные цветовые схемы для ключевых элементов интерфейса. В нашем проекте используются две готовые темы — stanford-theme и bootstrap-theme.

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

Обновить в ней данные можно только через pull-request, что позволяет проверять все новые разделы документации перед тем, как они будут опубликованы в общем доступе. Исходные файлы проекта хранятся в Bitbucket-репозитории, причём сайт генерируется только из файлов, содержащихся в ветке master.

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

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

  1. Добавление нового узла в Jenkins
  2. Описание Jenkinsfile
  3. Интеграция Jenkins и Bitbucket

Добавление нового узла в Jenkins

Для сборки и обновления документации на сайте необходимо зарегистрировать в качестве агента Jenkins заранее подготовленную для этого машину.

Подготовка машины

В нашем случае будет использоваться JDK 8, для установки которой достаточно выполнить команду: Согласно требованиям Jenkins, на всех компонентах, входящих в систему, включая мастер-машину и все зарегистрированные агентские узлы, должна быть установлена JDK или JRE.

sudo apt-get -y install java-1.8.0-openjdk git

Для этого на агенте необходимо создать пользователя, под которым будут выполняться все операции, и в домашней папке которого будут храниться все генерируемые Jenkins-файлы. Мастер-машина будет подключаться к агенту для выполнения на нём назначенных задач. В Linux-системах достаточно выполнить команду:

sudo adduser jenkins \--shell /bin/bash
su jenkins

Сгенерируем ключи на агенте, после чего для пользователя jenkins добавим публичный ключ в файл authorized_keys. Для установки соединения между мастер-машиной и агентом необходимо настроить SSH и добавить необходимые ключи авторизации.

7. Собирать сайт с документацией будем в Docker-контейнере, использующем готовый образ python:3. Чтобы завершить процесс установки, необходимо переподключиться к агенту. Для установки Docker на агенте следуйте инструкциям официальной документации. Проверим корректность установки, выполнив команду, которая загружает тестовый образ:

docker run hello-world

Чтобы не приходилось запускать команды Docker от имени суперпользователя (sudo), достаточно добавить в созданную на этапе установки группу docker пользователя, от имени которого будут выполняться команды.

sudo usermod -aG docker $USER

Конфигурация нового узла в Jenkins

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

ВАЖНО: Идентификатор, который задаётся в разделе Настроить Jenkins -> Управление средами сборки -> Имя узла -> Настроить в параметре Метки, в дальнейшем используется в Jenkinsfile для указания агента, на котором будут выполнены все операции.

Описание Jenkinsfile

В корне репозитория проекта хранится Jenkinsfile, который содержит инструкции по:

  • подготовке среды сборки и установке зависимостей;
  • сборке сайта с помощью Sphinx;
  • обновлению информации на хосте.

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

Указание агента

Для этого необходимо использовать директиву agent: В начале Jenkinsfile укажем метку агента в Jenkins, на котором будут выполняться все операции.

agent { label 'название-метки'
}

Подготовка среды

Также для обновления информации на хосте необходимо заранее указать пути, где хранятся данные сайта с документацией. Для выполнения команды сборки сайта sphinx-build необходимо задать переменные среды, в которых будут храниться актуальные пути к данным. Присвоить эти значения переменным позволяет директива environment:

environment { SPHINX_DIR = '.' //папка, в которой установлен Sphinx BUILD_DIR = 'project_home_built' //папка с собранным сайтом SOURCE_DIR = 'project_home_source' //папка с исходными .rst и .md файлами DEPLOY_HOST = 'username@127.1.1.0:/var/www/html/' //пользоатель@IP_адрес_хоста:папка_с_сайтом
}

Основные действия

Простой пример трёхэтапного CI-pipeline: Основные инструкции, которые будут выполняться в Jenkinsfile, содержатся внутри директивы stages, которая состоит из разных шагов, описываемых директивами stage.

pipeline } stage('Test') { steps { echo 'Testing..' } } stage('Deploy') { steps { echo 'Deploying....' } } }
}

Запуск контейнера Docker и установка зависимостей

7. Сначала запустим Docker-контейнер с готовым образом python:3. Затем последовательно сделаем следующее: Для этого воспользуемся командой docker run с флагами --rm и -i.

  • установим python virtualenv;
  • создадим и активируем новую виртуальную среду;
  • установим в ней все необходимые зависимости, перечисленные в файле
    requirements.txt, который хранится в корне репозитория проекта.

stage('Install Dependencies') { steps { sh ''' docker run --rm -i python:3.7 python3 -m pip install --user --upgrade pip python3 -m pip install --user virtualenv python3 -m virtualenv pyenv . pyenv/bin/activate pip install -r \${SPHINX\_DIR}/requirements.txt ''' } }

Сборка сайта с документацией

Для этого необходимо выполнить команду sphinx-build со следующими флагами: Теперь соберём сайт.

-q: записывать в лог только предупреждения и ошибки;
-w: записывать лог в указанный после флага файл;
-b: имя сборщика сайта;
-d: указать директорию для хранения кешированных файлов — doctree pickles.

В случае ошибки на одном из этапов в консоли Jenkins появится лог выполнения sphinx-build. Перед запуском сборки, с помощью команды rm -rf удалим предыдущую сборку сайта и логи.

stage('Build') { steps { // clear out old files sh 'rm -rf ${BUILD_DIR}' sh 'rm -f ${SPHINX_DIR}/sphinx-build.log' sh ''' ${WORKSPACE}/pyenv/bin/sphinx-build -q -w ${SPHINX_DIR}/sphinx-build.log \ -b html \ -d ${BUILD_DIR}/doctrees ${SOURCE\_DIR} ${BUILD\_DIR} ''' } post { failure { sh 'cat ${SPHINX_DIR}sphinx-build.log' } }
}

Обновление сайта на хосте

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

Для её корректной работы необходимо настроить подключение по SSH между Docker-контейнером, в котором собирался сайт с документацией, и хостом. В качестве инструмента синхронизации используем утилиту rsync.

Чтобы можно было с помощью Jenkinsfile настраивать подключение по SSH, в Jenkins нужно установить следующие плагины:

  1. SSH Agent Plugin — позволяет использовать в скриптах шаг sshagent для предоставления учётных данных вида имя_пользователя/ключ.
  2. SSH Credentials Plugin — позволяет сохранять в настройках Jenkins учётные данные вида имя_пользователя/ключ.

После установки плагинов необходимо указать актуальные учётные данные для подключения к хосту, заполнив форму в разделе Credentials:

  • ID: идентификатор, который будет использоваться в Jenkinsfile на шаге sshagent для указания конкретных учётных данных (docs-deployer);
  • Username: имя пользователя, под которым будут выполняться операции обновления данных сайта (пользователь должен иметь доступ на запись в папку /var/html хоста);
  • Private Key: приватный ключ для доступа к хосту;
  • Passphrase: пароль для ключа, если он задавался на этапе генерирования.

Результат выполнения команды rsync записывается в лог, который будет выводиться в консоли Jenkins в случае ошибок синхронизации. Ниже представлен код скрипта, который подключается по SSH и обновляет информацию на хосте, используя заданные на этапе подготовки среды системные переменные с путями к необходимым данным.

stage('Deploy') { steps { sshagent(credentials: ['docs-deployer']) { sh ''' #!/bin/bash rm -f ${SPHINX_DIR}/rsync.log RSYNCOPT=(-aze 'ssh -o StrictHostKeyChecking=no') rsync "${RSYNCOPT[@]}" \ --delete \ ${BUILD_DIR_CI} ${DEPLOY_HOST}/ ''' } } post { failure { sh 'cat ${SPHINX_DIR}/rsync.log' } }
}

Интеграция Jenkins и Bitbucket

В официальной документации есть подробная инструкция по установке плагина, а также приведены настройки, которые должны быть заданы для обеих систем. Существует много способов организовать взаимодействие Jenkins и Bitbucket, но в нашем проекте мы решили использовать плагин Parameterized Builds for Jenkins. Для работы с этим плагином необходимо создать пользователя Jenkins и сгенерировать для него специальный токен, который позволит этому пользователю авторизоваться в системе.

Создание пользователя и API-токена

Для создания нового пользователя в Jenkins необходимо перейти в раздел Настройки Jenkins -> Управление пользователями -> Создать пользователя, и в форме заполнить все необходимые учётные данные.

Для этого: Механизм аутентификации, позволяющий сторонним скриптам или приложениям использовать API Jenkins без фактической передачи пароля пользователя, представляет собой специальный API-токен, который можно сгенерировать для каждого пользователя Jenkins.

  • авторизуйтесь в консоли управления, используя реквизиты пользователя, созданного ранее;
  • перейдите в раздел Настроить Jenkins -> Управление пользователями;
  • нажмите на значок шестерёнки справа от имени пользователя, под которым авторизовались в системе;
  • в списке параметров найдите API Token и нажмите на кнопку Add new Token;
  • в появившемся поле укажите идентификатор API-токена и нажмите кнопку Generate;
  • следуя подсказке на экране, скопируйте и сохраните сгенерированный API-токен.

Теперь в настройках сервера Bitbucket можно указывать пользователя по умолчанию для подключения к Jenkins.

Заключение

Если раньше процесс состоял из нескольких шагов:

  • загрузить обновление в репозиторий;
  • дождаться подтверждения корректности;
  • собрать сайт с документацией;
  • обновить информацию на хосте;

Если после проверки не требуется вносить изменения в исходные файлы, то актуальная версия документации обновляется сразу же после подтверждения корректности данных. то теперь достаточно нажать одну кнопку Merge в Bitbucket.

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

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

Спасибо дочитавшим за внимание, скоро мы продолжим делиться подробностями создания документации в нашем проекте!

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть