Bash Booster — SCM инструмент на чистом баше

Для управления серверами профессиональные администраторы давно используют такие SCM системы, как Chef, Ansible, SaltStack и т.п. Данные инструменты помогают централизованно администрировать большой парк серверов. Для управления же одним сервером трудозатраты на установку и настройку такого инструмента часто превышают выигрыш от его использования. В данном случае нередко применяется подход «да ну его, я быстрее скрипт на баше напишу». Подход достаточно популярный, а потому я хотел бы познакомить вас с легковесным SCM инструментом, который не требует ничего, кроме старого доброго баша, и может вполне успешно применяться для настройки одного сервера.

Итак, Bash Booster — библиотека, помогающая писать идемпотентные баш-скрипты для настройки серверов и развертки приложений. Была написана под впечатлением от Chef и для использования совместно с Vagrant, хотя область применения вовсе этим не ограничивается. Не требует ничего, кроме баша, стандартных утилит и, в некоторых случаях, питона (который так же установлен на любой Linux системе из коробки). Т.е. вполне пригодна для запуска на абсолютно голой машине без дополнительной подготовки.

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

Итак, предположим у нас есть сервер с Linux Ubuntu 14.04, на который нужно установить nginx и настроить его.

Создадим пустую директорию, а в ней Vagantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty32"
  config.vm.network :forwarded_port, host: 8080, guest: 80
end


И выполним команду:

$ vagrant up


Vagrant создаст и запустит виртуальную машину с чистой системой (возможно, придется подождать пока он скачает образ). Кроме того, он примонтирует текущую директорию хост-системы в точку /vagrant, т.е. мы сможем иметь доступ к файлам из нашего примера внутри виртуальной машины. Можете проверить это:

$ vagrant ssh
$ ls /vagrant
Vagrantfile
$ exit


Далее, скачаем архив Bash Booster и распакуем его в bashbooster-0.3beta (0.3beta — текущая версия на момент написания статьи). А так же подправим Vagrantfile, указав скрипт настройки:

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty32"
  config.vm.provision :shell, inline: "/vagrant/provision.sh"
  config.vm.network :forwarded_port, host: 8080, guest: 80
end


На этом подготовка закончена и можно приступить к самому интересному. Создадим скрипт provision.sh:

#!/usr/bin/env bash

# Удаляем переменную CDPATH, чтобы избежать нежелательных эффектов
# при использовании функции cd
unset CDPATH
# Переключаем текущую директорию в то место, где лежит скрипт
cd "$( dirname "${BASH_SOURCE[0]}" )"

##
# Инициализируем Bash Booster
##
BB_LOG_USE_COLOR=true   # Раскрашиваем логи для удобства
source bashbooster-0.3beta/bashbooster.sh

# Устанавливаем nginx
bb-apt-install nginx


Теперь помечаем скрипт как исполняемый:

$ chmod a+x provision.sh


И запускаем настройку нашего сервера:

$ vagrant provision


В логах должный появится строчки:

bb-apt [INFO] Updating apt cache
bb-apt [INFO] Installing package 'nginx'


Что это значит? Фактически наш скрипт выполнил:

$ apt-get update
$ apt-get install nginx


Можно перейти в браузере на http://localhost:8080 что бы увидеть стандартное приветствие «Welcome to nginx!» Теперь, если выполнить vagrant provision еще раз, скрипт отработает практически мгновенно, потому что функция bb-apt-install ничего не делает, если запрошенный пакет уже установлен.

Давайте теперь создадим директорию www с файлом index.html:

<h1>Bash Booster Rocks!</h1>


И настроим nginx, что бы он отдавал файлы из этой директории. Для этого создадим конфигурацию nginx-default-site в директории conf:

server {
    root /vagrant/www;
    index index.html;
}


И добавим синхронизацию конфигурации в скрипт provision.sh:

bb-event-on "nginx-updated" "on-nginx-updated"
on-nginx-updated() {
    service nginx restart
}

bb-sync-file \
    /etc/nginx/sites-available/default \
    conf/nginx-default-site \
    nginx-updated


А теперь выполним команду настройки:

$ vagrant provision


В логах появится:

 * Restarting nginx nginx
    ...done.


Перейдя на http://localhost:8080, можно увидеть вместо стандартного приветствия nginx неприлично большую надпись «Bash Booster Rocks!» из ранее созданного файла.

Как это работает? Функция bb-event-on подписывает функцию on-nginx-updated на событие nginx-updated. Функция bb-file-sync синхронизирует локальную копию конфигурации nginx с его текущей версией. Если были изменения, то эта функция порождает событие nginx-updated, по которому его обработчик перезапустит nginx. Попробуйте выполнить vagrant provision еще раз, и он отработает без перезагрузки nginx. Если же внести изменения в nginx-default-site, то nginx будет перезагружен. Таким образом мы получили компактный идемпотентный скрипт, который делает ровно то, что нужно и не более.

Конечно, здесь описаны далеко не все возможности Bash Booster, но для первого знакомства вполне достаточно. Полная документация находится на сайте www.bashbooster.net.
Share post

Similar posts

Comments 30

    –1
    питона (который так же установлен на любой Linux системе из коробки)


    Далеко не в любой, и это делает всю затею сомнительной. Странно, что в зависимостях не Ruby, а именно Python, учитывая Вагрантоориентированность.
      +1
      Python используется только в модуле read для парсинга INI, JSON, Yaml и Java Properties файлов. Другие модули его не используют, так что зависимость опциональная.

      Кроме того, если уж Python не установлен, то он всегда есть в стандартных репозиториях. Чего не скажешь о Ruby, который из коробки не установлен вообще, а в репозиториях часто лежит древняя версия, под которой половина новых пакетов уже не работает. Ну а, если очень хочется использовать Ruby, вы всегда можете прислать патч для модуля ext.
        –1
        Я-то как раз ни Ruby, ни Python, ни bash, если уж на то пошло, для такой сложной задачи как управление конфигурациями использовать совершенно не хочу и не использую. Просто обратил внимание на одно сомнительное предположение с вашей стороны и сомнительное архитектурное решение со стороны авторов bash booster.
          –1
          О гуру, подскажи другие языки с таким же удобным синтаксисом, подходящим для написания деплой-скриптов.
            0
            Уже подсказывал. В процессе написания ещё пара «подсказок».
              0
              Выглядит как-то не очень удобно.
                0
                Когда в чём-то не разбираешься, оно часто кажется неудобным.
        0
        Дык, Python входит в LSB и должен быть в любом современном дистрибутиве в минимальной конфигурации. Я это принципиально для администрировании.
          0
          А кому, простите, должен?

          $ sudo debootstrap --arch=amd64 testing $(pwd)/vm-tst1 http://ftp.jp.debian.org/debian/
          [...]
          $ sudo find vm-tst1 | grep -i python
          vm-tst1/usr/share/gcc-4.9/python
          vm-tst1/usr/share/gcc-4.9/python/libstdcxx
          vm-tst1/usr/share/gcc-4.9/python/libstdcxx/v6
          vm-tst1/usr/share/gcc-4.9/python/libstdcxx/v6/__init__.py
          vm-tst1/usr/share/gcc-4.9/python/libstdcxx/v6/printers.py
          vm-tst1/usr/share/gcc-4.9/python/libstdcxx/__init__.py
          vm-tst1/usr/share/nano/python.nanorc
          vm-tst1/usr/lib/python2.7
          vm-tst1/usr/lib/python2.7/dist-packages
          vm-tst1/usr/lib/python2.7/dist-packages/debconf.py
          vm-tst1/usr/lib/python3
          vm-tst1/usr/lib/python3/dist-packages
          vm-tst1/usr/lib/python3/dist-packages/debconf.py
          
          
            0
            lxc например создает убунту конейнеры без него, приходится ансиблом в рамках развертывания вм в raw режиме его ставить, иначе тот же ансибл не работает
          0
          Неплохо бы в таких статьях показывать, чем выгодно отличается от того же упомянутого Chef?
          То, что я прочитал в этой статье, как бы говлорит: ещё один велосипед, ни комьюнити, ни интеграции нормальной с Vagrant: создавать свой скрипт для провижина и тд.
            0
            От Chef выгодно отличается тем, что «я быстрей на баше напишу» становится еще быстрей.

            Вообще мое субъективное мнение о Chef следующее: это крутая вещь, когда нужно централизованно развернуть парк серверов (8 и более) на том же Amazon. Но для управления парой-тройкой машин, его использование больше напрягает, чем помогает. Для этих целей этот велосипед и был изобретен. Я и мои коллеги успешно на нем ездим уже полгода и пока довольны.
              0
              Никогда не мог понять, что сложного в нем? Приведите пример чем он вас напрягает? Действительно интересно.
                0
                Опыт годичной давности и сугубо субъективный.

                Во-первых, документация написана маркетологами в стиле «Chef умеет вот так, купите платный саппорт и мы сделаем вам хорошо». Я конечно разобрался, но осадок остался.

                Во-вторых, для установки Chef-solo нужен Ruby. В то время в репозиторях Ubuntu 12.04 был Ruby 1.8.4. Chef на нем заводится отказался без внятных сообщений об ошибках. На установленном через RVM, Ruby 2.1 (или 2.0 не помню точно версию) он заработал, но периодически падал опять же без внятных сообщений. Опытным путем удалось его завести на Ruby 1.9.х. Этого конечно можно было бы избежать, если бы не первый пункт.

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

                Если использовать Chef на Amazon OpsWorks, где все инструменты уже интегрированы и нужно просто подсунуть нужные кукбуки, то таких проблем не возникает. И лично у меня осталось хорошее впечатление от него. Но для одиночных образов в Vagrant у меня нет желания его использовать.

                Еще следует оговориться, что я не администратор, а программист. И мой девопсовский опыт был получен в тот момент, когда я ждал нового проекта, а в соседней команде не хватало инженера по развертыванию. В моей повседневной работе Chef и другие «большие» SCM системы — явный перебор. Все что мне нужно — это настроить виртуалку в Vagrant и положить настройки в репозиторий с кодом, чтобы от остальной команды никогда не слышать: «Не знаю, у меня все работает». Если вас Chef полностью устраивает, значит Bash Booster просто не для ваших задач.
                  0
                  1. У Chef нормальная документация и всегда такой была: docs.chef.io/. Но если не знать где искать, на их сайте она немного неочевидно ищется.

                  2. Chef тащит весь свой руби с собой (да, это пара сотен мегабайт). Но если вы не пишете на руби и не используете RVM, проблем это никаких не создает (кроме занятого места на диске). Не используйте Chef с RVM и системным руби — это не поддерживается и если вы пытаетесь так делать, вы должны знать как все возможные проблемы исправить.

                  4. В Vagrant он подсовывается прямо из вагрантфайла: docs.vagrantup.com/v2/provisioning/chef_solo.html

                  Но вообще, если нужно что-то действительно легковесное, я бы лучше посоветовал Ansible. Он действительно простой и из зависимостей у него только Python.
                    0
                    1. У Chef нормальная документация и всегда такой была: docs.chef.io/. Но если не знать где искать, на их сайте она немного неочевидно ищется.


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

                      Ну Омнибус сделали, конечно, за это им большое спасибо, но это не доки, про которые мы здесь говорим :-)
                        0
                        Только что проглядел их сайт — за год многое изменилось.

                        Первое, что бросается в глаза, появилась возможность скачать готовую сборку под нужную платформу. Например, есть deb-пакеты под Ubuntu. Год назад их не было. Я, конечно, допускаю возможность, что плохо искал, но я их сайт в течении двух недель излазил вдоль и поперек. Если они и были, то прятали их очень хорошо. А про веселую инсталляцию руками я уже выше писал.

                        Второе, появился внятный пошаговый туториал для новичков. Этого тоже год назад не было. Было что-то вроде известной картинки «Как нарисовать филина».

                        выпилили wiki, который устарел и сбивал с толку

                        Я это и имел ввиду, когда сказал про плохую документацию. Если она устарела и на каждой странице написано «купите саппорт» — это разочаровывает.
                          0
                          Omnibus (те самые пакеты под все платформы) ЕМНИП появился с выходом Chef 11.
                          Это начало 2013г.
                          До этого было, конечно, лучше чем у Puppet, но все равно страшновато выглядело, в этом согласен :-)

            +1
            Велосипед 2.0. По-мойму тем, кому лень понять всю простоту папета/шефа самое оно. Конечно простые вещи на таком велосипеде можно сделать, однако что-то сложное уже не уверен.

            И да — даже на одной машинке можно сделать что-то сложное. Число машинок не должно пугать настоящего DevOps'а.

            Хотя реализация мне нравится. Как только у вас появится репозиторий с готовыми рецептами — зовите на чай ))
              0
              Конечно простые вещи на таком велосипеде можно сделать, однако что-то сложное уже не уверен.

              А не могли бы вы привести пример чего-то сложного? Возможно, мне удастся убедить вас в обратном.
                0
                Океюшки, как грят бабушки, как грит Задорнов.

                Вот что-то типа такого: github.com/garex/puppet-module-nginx

                Чтоб там снаружи подключил, какой-нить дёрнул один точка с параметрами и оно всё далее само?

                Т.е. не god-mode скрипт который на любой штуке возможен, а что-то модульное. А-ля тот же докер. И чтобы внутри были всякие события, шаблоны и всё такое.
                  0
                  Это на целый пост тянет. Если интересно, напишу.
                    0
                    Давайте попробуем.
              +1
              1. Объясните в чем ценность примера, в котором для выполнения двух команд вы ставите отдельную софтину и пишете скрипт-костыль, который по своей логике ни чем не отличается от того-же скрипта но без bashbooster?

              2. В первом абзаце на сайте написано — что нужен только bash. А мелким шрифтом в описании модуля read — используйте python. Это даже не смешно.
              Объясню суть комментария — у нас есть некоторое ПО, которые мы ставим на всякие разные Unix системы(Linux\AIX\HP-UX\Solaris и аналогичное ...). Компилировать под них python\ruby\%another_interpreted_lang% — достаточно ресурсозатратно и не интересно. Bash для нужд выполнения простых консольных команд в готовой последовательность идеально подходит. Но у него есть свои проблемы — например парсинг файлов приходится делать только с учетом платформонезависимых ключей к awk \ grep и т.п. Для себя мы какое-то решение нашли, но иногда хочется взять что-то работающее, вместо допила домашнего костыля.

              Если кто-то чем-то подобным занималя (не [только] на linux) — расскажите как решали проблемы с платформозависимыми командами.
                +1
                По первому вопросу. Вероятно, вы не уловили суть той части, где обновляется конфигурация nginx — перезагрузка веб сервера осуществляется по событию. Т.е. создается функция-обработчик события nginx-updated и эта функция вызывается только в том случае, если событие возникло. В чем здесь ценность? Скрипт делает ровно то что нужно: если конфигурация не обновлялась — нет смысла перезагружать сервис.

                Представьте более сложный пример. У нас есть nginx, который отдает статику и проксирует запросы к серверу приложений uWSGI. Под uWSGI крутится несколько веб-сервисов на Python. Веб-сервисы используют MySQL. Настройки nginx, uWSGI, исходный код и конфиги веб-сервисов, а так же миграции модели базы данных лежат в одном репозитории с кодом.

                Задача: сделать автоматический деплой всех обновлений, которые появляются в репозитории.

                Какие действия возможны? Если изменились конфиги nginx — перезагрузить nginx. Если изменились конфиги uWSGI — перезагрузить uWSGI. Если изменились конфиги веб-сервисов — перезагрузить uWSGI. Если изменились исходники веб-сервисов — переустановить их в виртуальное окружение и перезагрузить uWSGI. Если изменилась модель — выключить uWSGI (нужен монопольный доступ к базе), выполнить миграцию, включить uWSGI.

                Если попытаться написать этот скрипт без использования событий ­— получится лапша из операторов if, в которой уже через неделю сам автор без бутылки не разберется.

                Это пример из жизни. Я не стал его использовать в статье, что бы не загружать читателя ненужными подробностями.

                По второму вопросу. Я не вижу ничего криминального в использовании Python для чтения конфигов. Для каких целей это задумывалось? Есть приложение, которое использует базу данных. Логин/пароль от нее лежат в конфиге приложения. Нужно рядом положить скрипт, который периодически (через CRON) делает резервную копию базы. Что-то вроде:

                mysqldump -u username -p password databasename > dump.sql

                Проблема: где взять username, password и databasename? Можно захардкодить, но тогда придется дублировать настройки, что не очень хорошо. А можно распарсить конфиг скриптом и взять оттуда нужные значения.

                Я в своей работе чаще всего использую Ubuntu, Debian или CentOS. На всех из них Python есть. Насколько я знаю, во FreeBSD его нет, но он есть в портах. Из того, что мне приходит в голову, сложности могут возникнуть только с выполнением в Linux на роутере или холодильнике, но в данном случае у вас просто не будет возможности парсить конфиги. Все остальное будет работать без проблем.
                  0
                  >> Задача: сделать автоматический деплой всех обновлений, которые появляются в репозитории.

                  Тут уж извините — ребята писавшие про Chef\Puppet\Ansible — возможно чуть более правы. Но холивар на эту тему разводить не буду — я этими средствами не пользуюсь в силу того, что нам оно не подошло.
                    0
                    Ммм, автоматический деплой на роутер или холодильник…
                    +1
                    Перечитал еще раз второй пункт в вашем комментарии. Парсить JSON или INI при помощи awk — это ад. Изначально в Bash Booster применялся такой подход для Java Properties файлов. Работало не везде. Потому было решено использовать для этих целей Python, т.к. он работает всегда предсказуемо. Ну а раз впилили Python, заодно впилили поддержку INI, JSON и Yaml (если стоит PyYaml).

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


                    Почему бы вам не поделиться своим домашним костылем? Думаю можно было бы добавить в модуль read в качестве резервного варианта, если вдруг Python не установлен.
                      0
                      JSON — парсится через JSON.sh, например. Есть еще разные реализации, но этот наиболее вменяемый.

                      У нас достаточно специфичный набор самоделок. Как только оно будет отполировано до соответствующего состояния — думаю что поделимся. Тем более что суть решаемых, которые можно решать эти bash booster достаточно похожа.
                        +1
                        Есть еще augeas, но у него чуть великоват порог входа и он временами слишком строгий.
                          0
                          Парсить JSON или INI при помощи awk — это ад.


                          Парсить JSON при помощи awk — это 250 строк.

                      Only users with full accounts can post comments. Log in, please.