読者です 読者をやめる 読者になる 読者になる

機械学習, Rails, Androidが好きです - プログラマdogwood008のライフハック

最近のマイブームは機械学習, Ruby on Rails, Android、中でも機械学習を使った金融商品の自動取引に興味があります。

RailsプロジェクトをHerokuへデプロイし、無料ホスティングしてもらう

Ruby on Rails Heroku

f:id:dogwood008:20160717185046p:plain
herokuロゴ画像はHerokuより。

Getting Started with Rails 5.x on Herokuを参考に進めていきます。

準備

herokuは PostgreSQL を推奨しているので、それに合わせる。

group :production do
  gem 'pg'
end

heroku toolbeltをインストール。

$ wget -O- https://toolbelt.heroku.com/install.sh | sh
$ echo 'PATH="/usr/local/heroku/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile

ログインします。予めアカウントはWebから作っておく。

$ heroku login
heroku-cli: Installing CLI... 21.83MB/21.83MB
Enter your Heroku credentials.
Email: email@example.com
Password (typing will be hidden):
Logged in as email@example.com

アプリケーションを作成~デプロイ

$ heroku create
Heroku CLI submits usage information back to Heroku. If you would like to disable this, set `skip_analytics: true` in /home/kit/.heroku/config.json
Creating app... done, ⬢ XXXXX-YYYYY-12345
https://XXXXX-YYYYY-12345.herokuapp.com/ | https://git.heroku.com/whispering-YYYYY-12345.git

origin の他に、リモートリポジトリ heroku が追加されます。

$ git remote show heroku
* remote heroku
  Fetch URL: https://git.heroku.com/XXXXX-YYYYY-12345.git
  Push  URL: https://git.heroku.com/XXXXX-YYYYY-12345.git
  HEAD branch: (unknown)

早速、 Heroku へプッシュしてみます。

$ git push heroku master
Counting objects: 9, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 828 bytes | 0 bytes/s, done.
Total 6 (delta 4), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.3.0
remote: ###### WARNING:
remote:        You have the `.bundle/config` file checked into your repository
remote:        It contains local state like the location of the installed bundle
remote:        as well as configured git local gems, and other settings that should
remote:        not be shared between multiple checkouts of a single repo. Please
remote:        remove the `.bundle/` folder from your repo and add it to your `.gitignore` file.
remote:        https://devcenter.heroku.com/articles/bundler-configuration
remote:
remote: -----> Installing dependencies using bundler 1.11.2
remote:        Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
remote:        Fetching gem metadata from https://rubygems.org/..........
remote:        Fetching version metadata from https://rubygems.org/...
remote:        Fetching dependency metadata from https://rubygems.org/..
remote:        Installing rake 11.2.2
remote:        Installing concurrent-ruby 1.0.2
remote:        Installing i18n 0.7.0
remote:        Installing thread_safe 0.3.5
remote:        Installing minitest 5.9.0
remote:        Installing builder 3.2.2
remote:        Installing erubis 2.7.0
remote:        Installing mini_portile2 2.1.0
remote:        Installing pkg-config 1.1.7
remote:        Installing nio4r 1.2.1 with native extensions
remote:        Installing rack 2.0.1
remote:        Installing websocket-extensions 0.1.2
remote:        Installing arel 7.0.0
remote:        Installing mime-types-data 3.2016.0521
remote:        Installing method_source 0.8.2
remote:        Using bundler 1.11.2
remote:        Installing puma 3.4.0 with native extensions
remote:        Installing thor 0.19.1
remote:        Installing tzinfo 1.2.2
remote:        Installing nokogiri 1.6.8 with native extensions
remote:        Installing websocket-driver 0.6.4 with native extensions
remote:        Installing rack-test 0.6.3
remote:        Installing sprockets 3.6.3
remote:        Installing mime-types 3.1
remote:        Installing activesupport 5.0.0
remote:        Installing mail 2.6.4
remote:        Installing globalid 0.3.6
remote:        Installing activemodel 5.0.0
remote:        Installing activejob 5.0.0
remote:        Installing activerecord 5.0.0
remote:        Installing rails-dom-testing 2.0.1
remote:        Installing loofah 2.0.3
remote:        Installing rails-html-sanitizer 1.0.3
remote:        Installing actionview 5.0.0
remote:        Installing actionpack 5.0.0
remote:        Installing actionmailer 5.0.0
remote:        Installing actioncable 5.0.0
remote:        Installing railties 5.0.0
remote:        Installing sprockets-rails 3.1.1
remote:        Installing rails 5.0.0
remote:        Installing pg 0.18.4 with native extensions                                                                           
remote:        Bundle complete! 9 Gemfile dependencies, 41 gems now installed. 
remote:        Gems in the groups development and test were not installed.
remote:        Bundled gems are installed into ./vendor/bundle.
remote:        Bundle completed (19.95s)
remote:        Cleaning up the bundler cache.
remote:
remote: ###### WARNING:
remote:        You have the `.bundle/config` file checked into your repository
remote:        It contains local state like the location of the installed bundle
remote:        as well as configured git local gems, and other settings that should
remote:        not be shared between multiple checkouts of a single repo. Please
remote:        remove the `.bundle/` folder from your repo and add it to your `.gitignore` file.
remote:        https://devcenter.heroku.com/articles/bundler-configuration
remote:
remote: ###### WARNING:
remote:        No Procfile detected, using the default web server.
remote:        We recommend explicitly declaring how to boot your server process via a Procfile.
remote:        https://devcenter.heroku.com/articles/ruby-default-web-server
remote:
remote: -----> Discovering process types
remote:        Procfile declares types     -> (none)
remote:        Default types for buildpack -> console, rake, web, worker
remote:
remote: -----> Compressing...
remote:        Done: 23.1M
remote: -----> Launching...
remote:        Released v6
remote:        https://XXXXX-YYYYY-12345.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/XXXXX-YYYYY-12345.git
 * [new branch]      master -> master

途中で、 sqlite3.h is missing みたいなメッセージが出たり、 「詳しくは https://devcenter.heroku.com/articles/sqlite3 を見てね」的なメッセージが出たなら、 Gemfilegroup :development の中に gem 'sqlite' を押し込めましょう。

group :development do
  # Use sqlite3 as the database for Active Record
  gem 'sqlite3'
end

必要に応じて、DBのマイグレーションを行います。

$ heroku run rake db:migrate
Running rake db:migrate on ⬢ XXXXX-YYYYY-12345... up, run.2785                                                               
D, [2016-07-12T16:25:40.173919 #3] DEBUG -- :    (11.2ms)  CREATE TABLE "schema_migrations" ("version" character varying PRIMARY KEY)
D, [2016-07-12T16:25:40.187941 #3] DEBUG -- :    (9.3ms)  CREATE TABLE "ar_internal_metadata" ("key" character varying PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
D, [2016-07-12T16:25:40.190308 #3] DEBUG -- :    (1.0ms)  SELECT pg_try_advisory_lock(1753797496500759360);
D, [2016-07-12T16:25:40.200901 #3] DEBUG -- :   ActiveRecord::SchemaMigration Load (1.1ms)  SELECT "schema_migrations".* FROM "schema_migrations"
I, [2016-07-12T16:25:40.206248 #3]  INFO -- : Migrating to CreatePosts (20160710102422)
D, [2016-07-12T16:25:40.208344 #3] DEBUG -- :    (0.7ms)  BEGIN
== 20160710102422 CreatePosts: migrating ======================================
-- create_table(:posts)
D, [2016-07-12T16:25:40.216973 #3] DEBUG -- :    (7.4ms)  CREATE TABLE "posts" ("id" serial primary key, "title" character varying, "url" character varying, "comment" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
   -> 0.0085s
== 20160710102422 CreatePosts: migrated (0.0087s) =============================

D, [2016-07-12T16:25:40.224358 #3] DEBUG -- :   SQL (1.1ms)  INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version"  [["version", "20160710102422"]]
D, [2016-07-12T16:25:40.227280 #3] DEBUG -- :    (2.6ms)  COMMIT
I, [2016-07-12T16:25:40.227414 #3]  INFO -- : Migrating to AddDateToPost (20160710152551)
D, [2016-07-12T16:25:40.228671 #3] DEBUG -- :    (0.7ms)  BEGIN
== 20160710152551 AddDateToPost: migrating ====================================
-- add_column(:posts, :date, :datetime)
D, [2016-07-12T16:25:40.230074 #3] DEBUG -- :    (1.0ms)  ALTER TABLE "posts" ADD "date" timestamp
   -> 0.0013s
== 20160710152551 AddDateToPost: migrated (0.0014s) ===========================

D, [2016-07-12T16:25:40.231584 #3] DEBUG -- :   SQL (0.8ms)  INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version"  [["version", "20160710152551"]]
D, [2016-07-12T16:25:40.233287 #3] DEBUG -- :    (1.5ms)  COMMIT
D, [2016-07-12T16:25:40.240319 #3] DEBUG -- :   ActiveRecord::InternalMetadata Load (0.9ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", :environment], ["LIMIT", 1]]
D, [2016-07-12T16:25:40.248994 #3] DEBUG -- :    (0.7ms)  BEGIN
D, [2016-07-12T16:25:40.251805 #3] DEBUG -- :   SQL (1.0ms)  INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key"  [["key", "environment"], ["value", "production"], ["created_at", 2016-07-12 16:25:40 +0000], ["updated_at", 2016-07-12 16:25:40 +0000]]
D, [2016-07-12T16:25:40.253633 #3] DEBUG -- :    (1.5ms)  COMMIT
D, [2016-07-12T16:25:40.254643 #3] DEBUG -- :    (0.8ms)  SELECT pg_advisory_unlock(1753797496500759360)

これでおしまい。うまくいっていれば、ログ中に出てきたhttp://example.herokuapp.com/でアクセスできる。

デバッグ

プロセス状態

heroku ps で現在の状態を見ることができる。

$ heroku ps
Free dyno hours quota remaining this month: 1000h 0m (100%)
For more information on dyno sleeping and how to upgrade, see:
https://devcenter.heroku.com/articles/dyno-sleeping

=== web (Free): bin/rails server -p $PORT -e $RAILS_ENV (1)
web.1: up 2016/07/13 01:25:02 +0900 (~ 2m ago)

ログ

ログを見るなら heroku logs--tail オプションを付けると、リアルタイムで見られる。

$ heroku logs
$ heroku logs --tail

環境変数

# 環境変数の一覧
$ heroku config
=== XXXXX-YYYYY-12345 Config Vars
DATABASE_URL:             postgres://hogehogehoge:foobarbaz@ec2-NNN-NNN-NNN-NNN.compute-M.amazonaws.com:PPPP/fugapiyo
LANG:                     en_US.UTF-8
RACK_ENV:                 production
RAILS_ENV:                production
RAILS_LOG_TO_STDOUT:      enabled
RAILS_SERVE_STATIC_FILES: enabled
SECRET_KEY_BASE:          foobarbazhogefugapiyo
# 環境変数のうち、指定したものだけを表示
$ heroku config:get LANG                                                              
en_US.UTF-8
# 環境変数をセット
$ heroku config:set SECRET_KEY_BASE=`rake secret`                                     
Setting SECRET_KEY_BASE and restarting ⬢ XXXXX-YYYYY-12345... done, v8                                                       
SECRET_KEY_BASE: hogefugapiyofoobarbaz

コンソール

$ heroku run rails console
Running rails console on ⬢ XXXXX-YYYYY-12345... up, run.4519
Loading production environment (Rails 5.0.0)
irb(main):001:0> 

サーバの再起動

$ heroku restart
Restarting dynos on ⬢ XXXXX-YYYYY-12345... done

Heroku 無料枠の範囲

1つのサーバプロセスを立ち上げると、 1 dyno を使用するということになります。 1 dyno を1時間使用すると、 1 dyno hour を消費したことになります。

各アカウントに対し 550 dyno hours を、有効なクレジットカードを heroku に登録していれば、さらに 450 dyno hours(計 1000 dyno hours) を一月に付与されます。

31日間の可動に必要な dyno hours は単純計算で 744 dyno hours (= 24 hours * 31 days) ですが、 heroku run consoleheroku run rake db:migrate などのコマンドを実行する One-off Dyno と呼ばれる Dyno の実行も、利用量に含まれるので注意。

以前は「18時間稼働&6時間スリープ」のような制限がありましたが、今は上記の制限に代わりました。

参考