Pull to refresh

Bear — автоматизация удаленного развертывания с помощью Groovy/JVM

image Всем привет. Хочу представить новое средство удаленной автоматизации — Bear (Медведь). Bear позволяет выполнять задачи по настройке развертыванию ваших приложений на группе серверов. Этот инструмент стоит в одну с такими утилитами как Capistrano (Ruby), Fabric (Python), Chef (Ruby) и Puppet (Ruby).

Существенное отличие состоит в том, что Bear с самого начала предназначен для платформы JVM. Это означает привычное джависту рабочее окружение — отладчик, автодополнение кода, рефакторинг, статическую типизацию и поддержку нескольких языков программирования. Ну и вербозность джавы, куда уж без нее, родимой.

У Bear есть:

Cтраница на GitHub
Небольшая Wiki
Артифакт в Maven Central

Предыстория


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

Мотивация


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

Изучать средства вроде Capistrano мне, как джависту, не очень хотелось — и хотя язык Ruby мне знаком, Capistrano использует свой DSL, и как отлаживать, да и вообще работать с ним, если что-то пойдет не так — мне, человеку, незнакомому с современными средствами разработки Ruby, неясно.

Поэтому при создании Bear у меня была следующая мотивация, предоставить пользователю:

  • Привычное рабочее окружение: Java IDE пользователя, отладчик для проверки скриптов, юнит-тесты
  • Статические типы для быстрой разработки с помощью IDE
  • Библиотека параллельного выполнения задач
  • Простой, но современный UI. Все-таки стыдно выдавать пользователю консоль, когда сингулярность уже близко
  • Поддержка JVM-языков
  • Подключаемость Maven-библиотека
  • Примеры развертывания с GitHub для популярных технологий (Node.js, Grails, Play! Framework, Tomcat)
  • Конфигурация через лямбды (как в Capistrano), контекст сессии и глобальный контекст лямбды (чтобы, например, можно было переопределить пароль для определенного хоста), проверка ошибок времени компиляции
  • Поддержка multi-instance развертываний из коробки


Установка Медведя


Для запуска демо-проекта вам понадобится:

  • Maven 3+
  • JDK 7+, рекомендуется JDK 8 из-за багфиксов в WebView
  • Удаленный хост — например, виртуальная машина CentOS или Ubuntu.

Bear можно использовать как Maven-зависимость. Среди читателей хабры есть много продвинутых пользователей, знакомых с Maven. Они могут пропустить инструкцию ниже и добавить Bear как зависимость Maven в свой проект. Далее зайти на страницу GitHub и скопировать примеры в свой проект. Каждый проект — это независимый класс, написанный на Groovy.

Установка медведя, в командной строке:

$ mvn com.chaschev:installation-maven-plugin:1.4:install -Dartifact=com.chaschev:bear


Минимальный проект — удаленный ls, листинг директории


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

$ cd my-project
$ bear --create my --user имя-пользователя --password ssh-пароль --host хост

Это создаст папку .bear и в ней — pom.xml и файл проекта MyProject.groovy.

Created project file: .bear\MyProject.groovy
Created Maven pom: .bear\pom.xml

Для запуска удаленного листинга директории наберите:

$ bear my.ls

my — сокращенное имя проекта
ls — название автоматически сгенерированного метода

Вот его текст:

@Method
def ls(){
    run([named("ls task", { _,  task ->
        println _.sys.lsQuick(".")
    } as TaskCallable)])
}

В примере выше происходит запуск Groovy-замыкания (для джавистов, которые не знакомы с Groovy, замыкание — это анонимный класс вроде ActionEvent в Swing) на нашем кластере, пока еще состоящем из одной машины. Комментарии к коду:

_ — это контекст сессии, универсальный объект вроде $ из jQuery
_.sys — объект, отвечающие за системные команды
lsQuick — краткая форма вызова ls, обычно используются fluent interfaces, например, println _.sys.ls('.').run().throwIfError().getPath();

Так выглядит интерфейс Bear после запуска:

image

Запуск тестов окружения и демо-проектов


Чтобы распаковать демо-проекты, наберите в командной строке:

$ bear --unpack-demos

Откройте файл .bear/examples/demo/SmokeProject.groovy и отредактируйте хосты и stages. Для запуска тестов наберите в консоли:

$ bear smoke.runTests -q

Чтобы запустить вместе c UI:

$ bear smoke.runTests --ui

Если тесты пройдут успешно, на выходе должно быть что-то вроде:

command execution time: 44.2ms/command
finished: Stats{time: "8.8s", partiesArrived: 2, partiesOk: 2, partiesPending: 0, partiesFailed: 0}

Далее можно выбрать любой проект из папки .bear/examples/demo и запустить его:

$ bear drywall.setup  --ui
$ bear drywall.deploy --ui

Первая команда выполнит настройку окружения — установит Node.js и все необходимые зависимости. Вторая — выполнит удаленное развертывание проекта. Примечание: демо проекта Grails/Tomcat называется petclinic.

Можно также запустить из вашей IDE как обычный Groovy/Java класс:

public static void main(String[] args){
    new DrywallDemoProject().deploy()
}


Пример проекта


В заключение приведу пример проекта Bear с комментариями в коде:


// Конфигурация проекта по умолчанию через переменные. Также возможны другие способы:
// через переменные окружения, .properties файл, в коде, из командной строки
// Конфигурация может быть переопределена для метода или во время исполнения деплоя.
@Project(shortName =  "drywall-demo", name = "Drywall Demo Deployment")
@Configuration(
    properties = ".bear/demos",                                     // имя файла свойств .properties
    stage = "u-3",                                                               // stage, текущий кластер
    vcs = "https://github.com/jedireza/drywall.git",       // адрес проекта на GitHub
    branch = "master",                                                      // используемая ветка
    useUI = true,                                                                // использовать ли UI по умолчанию
    user = "andrey"                                                           // имя пользователя для ssh
)
public class DrywallDemoProject extends BearProject<DrywallDemoProject> {
    // Используемые плагины
    // Их зависимости автоматически устанавливаются во время project.setup()

    GitCLIPlugin git
    NodeJsPlugin nodeJs
    MongoDbPlugin mongoPlugin
    DeploymentPlugin deployment
    ReleasesPlugin releases
    DumpManagerPlugin dumpManager

    public TaskDef deployProject;

    // таска копирования
    def copyConfiguration = new TaskDef({_, task ->
        final String dir = _.var(releases.pendingRelease).path

        if(!_.sys.exists(dir + "/config.js")){
            _.sys.move("config.example.js").to("config.js").inDir(dir).run().throwIfError();
        }

        OK
    } as TaskCallable);

    @Override
    protected GlobalContext configureMe(GlobalContextFactory factory) throws Exception
    {
        // установка используемых версий
        // можно вынести эту часть в .properties файл, но автору по душе автономность проекта
        nodeJs.version.set("0.10.22");
        nodeJs.projectPath.setEqualTo(bear.vcsBranchLocalPath);

        // конфигурация портов для развертывания, можно через запятую указать несколько instances
        nodeJs.instancePorts.set("3000")

        // есть минималистичный dump manager, здесь устанавливается БД для него
        dumpManager.dbType.set(mongo.toString());

        // данный проект для запуска использует Grunt
        nodeJs.createScriptText.setEqualTo(nodeJs.simpleGruntUpstart);

        bear.stages.set(new Stages(global)
            .addQuick("one", "vm01")
            .addQuick("two", "vm01, vm02")
            .addQuick("three", "vm01, vm02, vm03")
            .addQuick("u-1", "vm04")
            .addQuick("u-2", "vm04, vm05")
            .addQuick("u-3", "vm04, vm05, vm06")
        );

        // Конфигурация деплоймент-задачи.
        // Задачи из плагинов раскиданы по фазам развертывания.
        // Поддерживается откат на предыдущую версию.
        // watch - это отслеживание запуска инстанса по логам.
        defaultDeployment = deployment.newBuilder()
            .CheckoutFiles_2({_, task -> _.run(global.tasks.vcsUpdate); } as TaskCallable)
            .BuildAndCopy_3({_, task -> _.run(nodeJs.build, copyConfiguration); } as TaskCallable)
            .StopService_4({_, task -> _.run(nodeJs.stop); OK; } as TaskCallable)
            .StartService_6({_, task -> _.run(nodeJs.start, nodeJs.watchStart); } as TaskCallable)
            .endDeploy()
            .ifRollback()
            .beforeLinkSwitch({_, task -> _.run(nodeJs.stop); } as TaskCallable)
            .afterLinkSwitch({_, task -> _.run(nodeJs.start, nodeJs.watchStart); } as TaskCallable)
            .endRollback();

        return global;
    }

    static main(args)
    {
        new DrywallDemoProject().start()
    }

    // Дополнительная установка зависимости проекта ImageMagick во время setup-задачи
    public GlobalTaskRunner setup()
    {
        global.tasks.setup.before({_, task -> _.sys.packageManager.installPackage("ImageMagick"); OK } as TaskCallable)

        super.setup()
    }
}


Заключение


Некоторые технические моменты реализации

  • UI сделан с помощью AngularJS+Twitter Bootstrap+Bootstrap-Admin-Template и WebView из JavaFX (движок на основе WebKit)
  • Самая большая проблема разработки AngularJS/JavaFX — отсутствие нормальной отладки. Я использовал Firebug Lite, в дальнейшем попробую писать юнит-тесты для Angular или делать заглушки для Java-вызовов.
  • Интеграция Java и JS в WebView пока очень сырая, иногда бывают Heap Dump-ы, приходится конвертировать объекты в JSON для передачи между JS и Java. Я отправил feature request на трекер Oracle JavaFX с поддержкой JSR-223 в WebView — там написали, что интеграцию возможно улучшат в Java 9
  • Для быстрой установки Bear из мавен-репозитория был написан мавен-плагин, он есть на GitHub, подобие утилит gem, apt или yum на других платформах. Честно говоря, это была большая боль — угадать, как и какие версии библиотек использовать, чтобы новый Maven Aether от Eclipse начал работать.
  • Для синхронизации задач был разработан мини-фреймворк ComputingGrid. Вкратце, это таблица, в которой столбцы — это хосты, строки — это задачи. Ячейки — это Future/Promise с результатом выполнения задачи. Это позволяет выполнить практические произвольную синхронизацию между сессиями.

Больше информации о Медведе есть на Вики проекта.

Вот таким получился Медведь. Я планирую развивать и дополнять этот проект и жду отзывов пользователей. В ближайшее время буду работать над юзабилити, упрощением пользования и багфиксингом. Планы на далекое будущее — поддержка удаленных агентов для улучшения качества связи с серверами (сейчас иногда Ubuntu обрывает связь во время мониторинга логов после запуска приложения), а также поддержка localhost и других языков — Python, Ruby, Scala. Если будет спрос, вынесу AngularJS+JavaFX в отдельную библиотеку.

Если вам интересно увидеть продолжение статей о Медведе, напишите мне об этом, пожалуйста. Всем спасибо за внимание и успехов в ваших проектах!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.
Change theme settings