Статьи

Взлом Mruby на Heroku

Если вы не жили под рубиновым камнем, вы, вероятно, слышали о mruby , последней экспериментальной реализации Ruby от Matz . Могу поспорить, что вы не знали, что вы можете запустить mruby на Heroku прямо сейчас. На самом деле, вы можете запускать на Heroku что угодно, если она может скомпилировать его в двоичный файл на Linux-боксе.

Если вы новичок в mruby или в компиляции двоичных файлов, взгляните на мою последнюю статью. Попробуйте mruby Today . Я рассказываю о том, как запустить mruby и запустить его на вашей локальной машине. Если вы уже освоили скорость, следуйте инструкциям, используя вулкан для упаковки mruby в двоичном формате, оберните его в пользовательский сборочный пакет и запустите приложение для использования mruby в облаке Heroku.

Что такое Buildpack?

Когда вы развертываете Python , Ruby или что-либо еще в Heroku, мы обнаруживаем язык и затем запускаем соответствующий пакет сборки. Проще говоря, buildpacks — это то, что позволяет нам запускать любой язык или фреймворк на Heroku. Целью системы является минимизация блокировки и максимальная прозрачность.

Пакеты buildd — вот где вся магия происходит в процессе развертывания. В пакете сборки ruby Heroku обнаруживает и настраивает вашу версию ruby , устанавливает гемы и запускает rake assets:precompile . Все buildpack-пакеты имеют открытый исходный код, и это означает, что вы можете создать один или собрать свой собственный с нуля.

Если вы хотите узнать больше, есть отличный пост в блоге о buildpacks Buildpacks: Heroku for Everything .

Прежде чем мы сможем поместить новое программное обеспечение, такое как mruby, в пакет сборки, нам нужно скомпилировать его, чтобы оно могло работать на Heroku с использованием сервера сборки.

Ваш первый Heroku Build Server

Если у вас есть Mruby, скомпилированный локально, вы можете просто отправить его в Heroku, верно? Не так быстро, машины Heroku, вероятно, отличаются от ваших личных настроек. Чтобы программное обеспечение работало, нам нужно скомпилировать mruby на компьютере Heroku, но как нам это сделать?

Чтобы помочь с этим процессом, Heroku выпустила инструмент с открытым исходным кодом под названием vulcan, который вы можете использовать для упаковки своих двоичных файлов в Heroku . Для начала нам нужно установить инструмент:

 $ gem install vulcan 

Примечание: вам может понадобиться установленный пакет heroku, также вы можете запустить $ gem install heroku чтобы получить его. Вулкан должен быть обновлен в ближайшее время, чтобы не полагаться на него.

Теперь вам нужно выделить место на компьютере Heroku, где вы сможете скомпилировать свой код. Мы назовем его сервером сборки и на самом деле это приложение, которое работает на стандартном Heroku dyno:

 $ vulcan create vulcan-schneems 

Примечание: вам нужно выбрать другое имя, чем vulcan-schneems .

Теперь, когда вы подготовили сервер сборки, давайте клонируем свежую копию исходного кода mruby.

Клон мруби

Перейдите на страницу Mruby Github . Мы можем получить git url для клонирования исходного кода на наш компьютер.

 $ git clone git://github.com/mruby/mruby.git $ cd mruby 

Примечание: mruby в альфа-версии, и эти инструкции могут отличаться. Для получения актуальной информации посетите веб-сайт https://github.com/mruby/mruby/blob/master/INSTALL.

Убедитесь, что каталог bin/ пуст, прежде чем пытаться что-то скомпилировать в Heroku, в противном случае сборка может завершиться неудачно, но все равно выглядит, как будто она работала, потому что на вашем компьютере были собраны старые файлы.

Компилировать с Vulcan

Теперь, когда вы находитесь в каталоге mruby, vulcan загрузит содержимое каталога на сервер сборки Heroku, когда вы попытаетесь его собрать. По умолчанию vulcan пытается запустить ./configure && make install в текущем каталоге, в котором вы находитесь. Если вам нужно было указать другой набор команд, вы можете сделать это с флагом --command= . Поскольку нам не нужен ./configure давайте просто запустим его с make .

Подсказка: вы можете получить весь вывод, передав --verbose для диагностики ошибок. Я очень рекомендую этот вариант.

Последний фрагмент конфигурации, который нам понадобится, — --prefix — это путь к серверу, который будет клонирован после завершения сборки вашего программного обеспечения. Традиционно выходные данные сборки могут быть указаны при запуске ./config с флагом --prefix . Например, если вы хотите, чтобы вывод шел в /tmp/mydirectory вы можете запустить

 $ ./configure --prefix /tmp/mydirectory $ make $ make install 

Поскольку у mruby нет команды ./configure, это не сработает, вместо этого мы должны указать вулкану, в каком каталоге находятся наши исполняемые файлы. Мы можем просто сказать, взять текущий каталог, указанный в . (период), но эта загрузка будет довольно большой. Вместо этого позволяет только получить выходные данные сборки. При запуске сборки vulcan помещает загруженные файлы (из текущего каталога) во input папку, поэтому результат нашей сборки будет в ./input/bin . Итак, давайте захватим эти файлы с помощью команды --prefix=./input/bin . Мы можем собрать все это вместе, чтобы сформировать команду vulcan build:

 $ vulcan build --command=make --prefix=./input/bin --verbose # ... >> Downloading build artifacts to: /tmp/mruby.tgz 

(доступно по адресу http://vulcan-schneems.herokuapp.com/output/4e584d28-6451-4ff3-a72f-1d0d509d639d)

СОВЕТ: Для получения дополнительной документации по вулканскому CLI, проверьте источник вулкана на GitHub .

Как только команда будет завершена, файл будет доступен по указанному URL-адресу, когда вулкан перезапустит сервер. Ваш сервер сборки работает на dyno, на котором установлены временные жесткие диски. Хотя они могут быть прочитаны и записаны, очищаются при перезагрузке сервера. Это должно помочь предотвратить заполнение дисков запущенными процессами и обеспечить хороший масштабируемый дизайн приложения. Перезапуск сервера гарантированно происходит не реже одного раза в 24 часа. Копия файла также должна быть скопирована в ваш каталог /tmp .

Разархивируйте файл, чтобы убедиться, что в нем есть все, что мы хотим:

 $ cd /tmp $ tar xvf mruby.tgz $ ls mirb mrbc mruby # ... 

Это должно распаковать содержимое, и теперь у вас должны быть бинарные файлы mirb , mruby и mrbc . Если вас интересует mrbc файл mrbc его можно использовать для компиляции программ mruby в байт-код. Если вы попытаетесь выполнить эти любые файлы на вашем компьютере, вы, скорее всего, получите ошибку, так как они не были встроены в ваш компьютер. Мы можем дважды проверить, что они построены, просто используя другое приложение Heroku.

Проверьте скомпилированные файлы

Для этого раздела вам понадобится приложение Heroku для проверки правильности скомпилированных файлов. С установленным Heroku Toolbelt выполните:

 $ heroku create Creating fast-sierra-6912... done, stack is cedar http://fast-sierra-6912.herokuapp.com/ | [email protected]:fast-sierra-6912.git 

Примечание. Имя вашего приложения будет отличаться от моего. При необходимости замените его для следующих команд.

Теперь вы захотите запускать команды внутри вашего приложения. Вы можете сделать это с помощью команды run или открыть сеанс bash с помощью heroku run bash например:

 $ heroku run bash -a fast-sierra-6912 

Примечание. Замените fast-sierra-6912 именем вашего приложения.

Команда heroku run bash раскручивает лишнюю динамограмму и дает вам ssh-сессию. Это отличается от ssh-в обычного сервера, поскольку он полностью изолирован от любых других dyno или веб-запросов. Вы можете удалить весь диск приложения, в то время как в heroku run bash и это не повлияет на другие dynos.

Теперь, когда мы находимся в сеансе Heroku Bash, нам нужно получить двоичный файл на машине. Мы можем сделать это с помощью команды curl и опции -O (заглавная O, как в Оскаре). Используйте эту команду вместе с двоичным URL, который мы получили от vulcan ранее, чтобы загрузить двоичные данные.

 # on heroku bash $ curl -O http://vulcan-schneems.herokuapp.com/output/4e584d28-6451-4ff3-a72f-1d0d509d639d 

Примечание: вам нужно использовать свой собственный URL здесь.

Затем распакуйте файл

 # on heroku bash $ tar xvf 4e584d28-6451-4ff3-a72f-1d0d509d639d x mirb x mrbc x mruby 

Теперь у вас есть файлы, скопированные в Heroku, и выполните их так же, как вы делали это на своей локальной машине в моей последней статье. Попробуйте mruby Today :

 # on heroku bash ~ $ ./mirb mirb - Embeddable Interactive Ruby Shell This is a very early version, please test and report errors. Thanks 🙂 > 

Это сработало! Мы смогли собрать двоичный файл, который мы можем использовать для запуска на Heroku. Мы можем использовать эту же технику для компиляции любой библиотеки, которую вы можете себе представить. Но мы еще не закончили. Теперь, когда у нас есть двоичный файл, давайте поместим его в сборочный пакет, чтобы можно было повторно использовать этот двоичный файл в любом приложении, которое мы хотим. Давайте начнем.

Форк пустой пакет

Несмотря на то, что мы могли бы начать с пакета сборки, в котором больше возможностей, гораздо проще увидеть, что происходит, начиная с пустого пакета сборки, созданного инженером Heroku Райаном Смитом. Разверните «пустой» пакет сборки, затем измените имя на что-то более подходящее, например «mruby-buildpack», чтобы сделать это, вы можете перейти к admin, выбрать «переименовать» и затем ввести «mruby-buildpack». Как только вы это сделаете, сделайте клонирование из вашего личного репозитория github на вашу локальную машину:

 $ git clone git://github.com/schneems/mruby-buildpack.git $ cd mruby-buildpack 

Примечание: ваш URL будет другим (не schneems / mruby-buildpack)

Как только вы сделаете репо локально, откройте его и посмотрите на него. Здесь действительно не так много 4 файлов и папок. Если вы посмотрите в каталог bin вы увидите 3 файла: detect , compile и release . Это три этапа процесса сборки, и эти файлы будут вызываться во время выполнения во время развертывания. Мы узнаем об этом позже.

Создайте тестовое приложение

Давайте проверим наш очень простой нулевой сборочный пакет работ. Мы создадим новое «приложение» Heroku и развернем его с помощью нашего собственного нулевого пакета сборки:

Сначала создайте новый каталог (убедитесь, что вы не находитесь внутри своего каталога buildpack).

 $ mkdir null $ cd null $ touch README.md 

Затем инициализируйте этот каталог как git-репо

 $ git init $ git add . $ git commit -m "initial commit" 

Теперь мы хотим создать приложение на Heroku и сказать ему, чтобы оно создавалось с помощью нашего разветвленного пакета сборки. Мы можем сделать это, указав URL-адрес buildpack при создании приложения или изменив BUILDPACK_URL в конфигурации. Запустите эту команду, но не забудьте указать ее на свой URL-адрес buildpack, а не на мой:

 $ heroku create --buildpack http://github.com/schneems/mruby-buildpack.git Creating still-mountain-4026... done, stack is cedar BUILDPACK_URL=http://github.com/schneems/mruby-buildpack.git http://still-mountain-4026.herokuapp.com/ | [email protected]:still-mountain-4026.git Git remote heroku added 

Помните: используйте schneems/mruby-buildpack github, а не schneems/mruby-buildpack

После того, как вы это сделаете, вы можете проверить, что переменная конфигурации buildpack установлена:

 $ heroku config === still-mountain-4026 Config Vars BUILDPACK_URL: http://github.com/schneems/mruby-buildpack.git 

Большой! Вы можете изменить этот URL в любое время в будущем, например, если вы хотите использовать определенную ветку buildpack. Вы можете указать это, используя # (хеш-метку) и название ветви. Если у вас есть ветка с именем total_re_write вы можете указать ее с помощью URL http://github.com/schneems/mruby-buildpack.git#total_re_write и heroku config:add команду; но пока не будем об этом беспокоиться.

Разверните ваш проект и убедитесь, что все работает:

 $ git push heroku master Counting objects: 3, done. Writing objects: 100% (3/3), 212 bytes, done. Total 3 (delta 0), reused 0 (delta 0) -----> Heroku receiving push -----> Fetching custom git buildpack... done -----> Null app detected -----> Nothing to do. -----> Discovering process types Procfile declares types -> (none) -----> Compiled slug size: 4K -----> Launching... done, v4 http://still-mountain-4026.herokuapp.com deployed to Heroku To [email protected]:still-mountain-4026.git * [new branch] HEAD -> master 

Обратите внимание на вывод Nothing to do. приходит прямо из команды компиляции в вашем репо.

Прежде чем мы добавим mruby в наш сборочный пакет, давайте посмотрим, что делает каждый из этапов сборки.

детектировать

Когда Heroku вызывает команду обнаружения в пакете сборки, она запускается для любого исходного кода, который вы нажимаете. Здесь приложение, такое как приложение ruby, может проверить наличие Gemfile или config.ru чтобы дважды проверить, что оно действительно может запустить данный проект. Если buildpack-пакет может собираться на основе файлов, он возвращает Heroku значение 0 которое в * nix интерпретируется как «все работает как ожидалось». Если buildpack не нашел нужные ему файлы, он должен вернуть ненулевой статус, обычно 1 . Если это произойдет, Heroku отменит процесс развертывания.

Так как мы вручную указываем URL-адрес buildpack, мы можем всегда возвращать 0.

Compile

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

Самое важное, что следует отметить на этапе компиляции, это то, что переменные среды конфигурации недоступны на этапе компиляции. Это сделано специально, поскольку хорошо спроектированное приложение должно компилироваться независимо от конфигурации. В лаборатории Heroku есть функция включения переменных среды в процессе компиляции, хотя я рекомендую ее не использовать. С помощью этой функции я видел больше, чем просто мою долю невоспроизводимых проблем «он работал при постановке, почему он сломан на производстве». Компиляция вашего приложения без конфигурации считается лучшей практикой.

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

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

Так когда же конфиг будет добавлен в проект? Мы не будем его обнаруживать или компилировать, так что, вероятно, …

Выпуск

В этом выпуске скомпилированное приложение связывается с переменными среды. Вы не должны вносить какие-либо изменения в диск здесь, только изменения в переменных среды. Heroku ожидает возврата в формате YAML с 3 дополнительными ключами, если есть какие-либо дополнительные config_vars умолчанию, config_vars который предоставляет набор переменных конфигурации среды по умолчанию, и default_process_types которые сообщат Heroku, какую команду запускать по умолчанию (т. config_vars web ).

Вы можете получить больше информации об этих трех шагах через документацию buildpack .

Отправим его

Теперь, когда вы знаете, как Heroku запускает buildpack-пакеты, давайте откроем ваш разветвленный buildpack-пакет, добавим в него mruby и затем что-нибудь сделаем.

Откройте mruby-buildpack/bin/detect и измените "Null" на "mruby" . Это была легкая часть. Возьмите бинарный файл, который вы скачали, на свой локальный компьютер и поместите его в общедоступный каталог, например, S3. Это должно быть где-то, что Heroku может получить к нему доступ. Вы можете найти мою копию на https://s3.amazonaws.com/schneems-heroku/mruby.tgz . Если у вас нет учетной записи S3, публичная публикация пока подойдет, даже Dropbox.

Откройте mruby-buildpack/bin/compile . Поскольку этот файл является файлом bash, мы можем писать команды командной строки непосредственно в него. Если вы предпочитаете писать свои скрипты в ruby, мы могли бы добавить дополнительные файлы в пакет сборки и вызвать их на этапе компиляции, но для этого упражнения мы будем придерживаться сценариев оболочки. Сначала мы хотим убедиться, что мы находимся в правильном каталоге сборки, это передается в качестве первого аргумента в наш скрипт bash, чтобы мы могли получить его, используя $1

  # change to the the BUILD_DIR ($1) cd $1 

Затем мы хотим скачать файл из общедоступного места, используя curl -O следующим образом:

  # download the mruby binary (-O) silently (-s) curl https://s3.amazonaws.com/schneems-heroku/mruby.tgz -s -O 

* Примечание: не забудьте указать ваш URL вместо моего. Также флаг -s в curl отключает загрузку, если вы хотите, чтобы это произошло, удалите его.

Теперь нам нужно создать каталог для хранения нашего двоичного vendor/mruby_bin давайте поместим его в vendor/mruby_bin . Мы можем запустить mkdir для этого:

 # make a directory to untar (like unzip) the binary mkdir -p vendor/mruby_bin 

Теперь мы хотим распаковать файлы в соответствующий каталог с помощью команды tar xvf , где -C используется для указания выходного каталога файла:

 # untar the binary to the directory we want tar -C vendor/mruby_bin -xvf mruby.tgz 

Наконец, очистите все неиспользуемые файлы

 # clean up the unused files rm mruby.tgz 

В качестве приятного прикосновения вы можете заменить эту строку:

 echo "-----> Nothing to do." 

С этим:

 echo "-----> Installing mruby like a boss" 

Финальный файл компиляции выглядит так для меня

  #!/usr/bin/env bash # bin/compile <build-dir> <cache-dir> echo "-----> Installing mruby like a boss" # change to the the BUILD_DIR ($1) cd $1 # download the mruby binary (-O) silently (-s) curl https://s3.amazonaws.com/schneems-heroku/mruby.tgz -s -O # make a directory to untar (like unzip) the binary mkdir -p vendor/mruby_bin # untar the binary to the directory we want tar -C vendor/mruby_bin -xvf mruby.tgz # clean up the unused files rm mruby.tgz 

Это завершает раздел компиляции, но мы еще не закончили. Если вы помните, когда у нас mruby работал локально, мы изменили наш путь, чтобы мы могли вызывать команду mruby из любого места. Чтобы включить эту функцию в приложении Heroku, нам нужно изменить путь, чтобы включить vendor/mruby_bin .

Прежде чем мы вызовем попытку push, мы хотим добавить файлы из vendor/mruby_bin в наш путь, чтобы мы могли получить к ним доступ из любого каталога. Для этого нам нужно установить наш PATH, который является частью переменных конфигурации среды. Так как они недоступны для нас на этапе компиляции, мы должны будем добавить их во время выпуска. Вам нужно будет добавить путь к существующей PATH чтобы мы ничего не нарушали.

В вашем bin/release вы можете вывести переменные конфигурации по умолчанию в формате YAML , например:

  #!/usr/bin/env bash # add our mruby_bin folder to the path on first deploy echo "---" echo "addons:" echo "config_vars:" echo " PATH: $PATH:/app/vendor/mruby_bin" echo "default_process_types:" 

Обратите внимание, что мы добавили полный путь /app/vendor/mruby_bin который является абсолютным путем к файлу (он начинается с / ). Когда ваше приложение будет развернуто, оно будет жить в /app . Поскольку мы добавили наши файлы в vendor/mruby_bin нам нужно добавить полное местоположение к пути. Символ двоеточия : является разделителем. ПУТЬ может иметь несколько путей, и они ищутся по порядку.

Одно важное предостережение об изменении переменных среды в пакете сборки, они считаются «значениями по умолчанию» и не будут игнорировать существующие переменные конфигурации. Если вы испортили настройку config_var для одного приложения, вам нужно будет создать новое приложение ( heroku create #... ), чтобы проверить все изменения.

Теперь, когда у нас есть все скомпилированные и выпущенные версии, нам нужно зафиксировать наши изменения в нашем buildpack-пакете и отправить его обратно в github, чтобы Heroku мог получить к нему доступ.

 $ git add . $ git commit -m "adding mruby like a boss" $ git push origin master 

Дважды убедитесь, что изменения отображаются в вашем репозитории github, и дважды проверьте, что URL-адрес buildpack в вашем приложении указывает на то же репозиторий github. Когда вы закончите, мы все готовы к развертыванию!

mruby Buildpack Deploy

Вернитесь к своему пустому приложению (не к папке buildpack, к приложению с пустым файлом readme, который мы создали). Сделайте новый коммит

 $ git commit -m "empty" --allow-empty 

Затем разверните, и вы должны увидеть ваше приложение в действии:

 $ git push heroku master ----> Heroku receiving push -----> Fetching custom git buildpack... done -----> mruby app detected -----> Installing mruby like a boss -----> Discovering process types Procfile declares types -> (none) -----> Compiled slug size: 2.9MB -----> Launching... done, v4 http://morning-taiga-3831.herokuapp.com deployed to Heroku 

Давайте быстро убедимся, что наш путь правильный:

 $ heroku config PATH: /usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/app/vendor/mruby_bin 

Вы должны увидеть /app/vendor/mruby_bin там. Выглядит хорошо для меня, давайте попробуем и посмотрим, все ли работает. Конечно, ни один учебник по языку не будет полным без привета, давайте запустим его в нашем приложении Heroku:

 $ heroku run "mruby -e "puts 'hello world' " " Running `mruby -e "puts 'hello world' " ` attached to terminal... up, run.1 hello world 

Congrats! Вы только что создали полнофункциональный buildpack-пакет, который работает на таком языке, что у него пока нет номеров версий. Вы должны воспользоваться этой возможностью, чтобы отпраздновать и рассказать всем своим программистам, насколько вы хороши. Также не забудьте указать ссылку на эту статью, пока вы злорадствуете (конечно).

Теперь, когда mruby доступен в системе, вы можете использовать его через $ heroku run bash или напрямую через команду run например $ heroku run mirb .

Зачем запускать Мруби на Героку?

Помимо очевидного «потому что вы можете», mruby — это новая реализация моих любимых языков. Это означает, что у него разные сильные и слабые стороны. Это предназначено, чтобы бежать на маленьких целях и быть легким. Я провел ряд тестов, сравнивая mruby и стандартный MRI ruby ​​(cruby). Если будет довольно много вычислений с плавающей запятой, то mruby будет пнуть задницу Крэби, но по многим другим операциям он будет сильно отставать. Опять же, дело не в том, чтобы быть быстрым, а в том, чтобы быть легким.

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

Важно помнить, что Mruby не является заменой для Cruby и все еще находится в альфа-версии. В настоящее время он не имеет стандартной библиотеки и не имеет возможности использовать require , поэтому не ожидайте запуска приложений rails на нем в ближайшее время.

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

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

Плавник

Вау, так что это было довольно много работы. Можете ли вы представить написание buildpack-пакета с нуля для любого приложения Rack / Rails? К счастью, мы позаботимся об этом за вас. Если вам нужно было создать собственный сборочный пакет, может быть немного проще начать с одного из существующих (и не нулевых) сборочных пакетов.

Если вам нужен бинарный файл в системе, но вы не хотите поддерживать ветвь buildpack-пакета или экземпляра, если вы хотите поместить бинарные файлы mruby в ваше git-репо и изменить конфигурационный PATH так, чтобы он указывал на этот каталог.

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