この記事はRuby Advent Calendar 2016の5日目です。
昨日はiguchi1124さんの「Railsアプリケーションでより良いリソース設計を実装するためのテクニック」 でした。
明日は、tomokane@githubさんの記事になります。
はじめに
最近、個人的なプロダクトを久しぶりにRailsで書いています。
自分の場合、Rubyはテキスト処理とかスクレイピングとかそういったスクリプトで使う用途が多いので、
Railsをまともに触るのは実に2以来。そのせいで5を触るにあたって大分忘れてたことや時代に取り残されたところがありました。
あと、Herokuも本格的に使ったことは無かったのですが、今回Dockerに完全対応したということで使ってみたので、
Heroku Container Registryを使ったDockerによるRails環境について備忘録がてら、今年はまとめてみようと思います。
今回はWindowsで開発してますがDockerをベースとすることで、
Rubyの開発をWindowsで実施しても苦しまなかったのが良いことですね。
ターゲットアプリ
今回はDBへのCRUDをするだけのシンプルなTODOアプリを作ります。ええ、Scaffoldするだけです>-<
ActiveJobとHerokuの組み合わせが結構便利だったので面白そうだったんですが、長くなりそうだったので割愛。
そっちは、また今度書きます。
"command"に記載されているスクリプトを実行する形です。"status"には"未実行"|"実行中"|"完了"の三つを付けます。
環境
- Windows 10
- Rails 5
- Heroku(Heroku Container Registry)
- Docker-Toolbox
- PostgreSQL
DockerでRails開発環境を作る
まずは、DockerでローカルにRails環境を作ります。
Dockerは入ってること前提にしますので、まだ入れてない人はDocker-Toolboxとかで入れてください。
Heroku Container Registryは別に特殊なコンテナをベースにする必要はないので、通常通りのDockerコンテナを使うことが出来きます。
今回はrubyイメージをベースにkoduki/railsを作って使っていますので、問題なければそのまま使ってもらえればと。
最初はGemfileを作ります。あとから上書こうと思うので、いったんこんな感じで。
source 'https://rubygems.org'
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
続いてプロジェクトの作成をします。
$ docker run -it -v `pwd`:/usr/src/app -w /usr/src/app koduki/rails rails new .
.
.
Overwrite /usr/src/app/Gemfile? (enter "h" for help) [Ynaqdh] Y
途中、Gemfileの上書きを確認されますが、これはYesにしておきます。
ボリュームをカレントディレクトリに指定し、そこをコンテナ上のワーキングディレクトリに指定しているので、
これでカレントディレクトリにRailsのプロジェクトが作られることになります。
それでは起動してみます。
$ docker run -it -p 3000:3000 -v `pwd`:/usr/src/app -w /usr/src/app koduki/rails bundle exec rails server -b 0.0.0.0
=> Booting Puma
=> Rails 5.0.0.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.6.2 (ruby 2.3.0-p0), codename: Sleepy Sunday Serenity
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
ポイントは-b 0.0.0.0
で、これが無いとDocker-Toolbox等で動かしている時は、アクセス先IPがlocalhostじゃないとつながりません。
http://192.168.99.100:3000/
などlocalhost以外のURLでアクセスしたい場合は、着けるのを忘れないようにしてください。
最後に、今回はPostgreSQLを使うのでGemfileに下記のようにpg
gemを追加しておきます。
gem 'sqlite3'
gem 'pg'
これで、開発環境の準備は整いました。
実行用Webイメージの作成
開発だけなら上記の設定だけでできますが、引き続き実行用のDockerイメージを作成します。
アプリケーション名はexample-todo
としました。
下記のようなDockerfileをアプリケーションルート配下に作成します。
FROM koduki/rails
ENV APP_ROOT /usr/src/app
COPY . $APP_ROOT
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
続いてビルド。
$ docker build -t koduki/example-todo .
では、今度は実行用のイメージを動かしてみましょう。
$ docker run -it -p 3000:3000 koduki/example-todo
=> Booting Puma
=> Rails 5.0.0.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.6.2 (ruby 2.3.0-p0), codename: Sleepy Sunday Serenity
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
ちゃんと、うごきましたね!
Herokuにデプロイする
では、ローカル環境で動いたところで、さっそくHerokuにデプロイしましょう。
Heroku Container Registryの使い方は公式のContainer Registry and Runtimeに詳しく乗っています。
ただ、私にはちょっと分かり難いところもあったので、もう少し補足を加えながらの説明をしていきます。
サインアップ
とにもかくにもサインアップ。
まだの人は、ここ https://api.heroku.com/signup からユーザー登録をしてください。
Heroku Toolbeltのインストール
こちらも導入してない人はHerokuのCommand Lineをインストールします。
昔みたいにGemじゃないので、Windowsで開発する場合には嬉しいですね。
Heroku Container Registryプラグインのインストール
お待たせしました。
ここからがHeroku Container Registryのための設定になります。
$ heroku plugins:install heroku-container-registry
こちらのコマンドを実行して、Heroku Container Registryプラグインのインストールをします。
Heroku アプリケーションの作成
プロジェクトのルートでheroku create
コマンドを実行します。
デフォルトではアプリ名はランダムに作られますが、後から変更することもできます。
$ heroku container:login # 初回のみ
$ heroku create
Creating app... done, hidden-escarpment-87242
https://hidden-escarpment-87242.herokuapp.com/ | https://git.heroku.com/hidden-escarpment-87242.git
デプロイ
いよいよHerokuへのDockerコンテナのデプロイです。
Dockerfileがある場所でheroku container:push web
を実行すると、
先ほどheroku create
で作ったアプリケーションにデプロイされます。
$ heroku container:push web
Sending build context to Docker daemon 163.3 kB
Step 1 : FROM koduki/rails
---> c28f05cfaafb
Step 2 : EXPOSE 3000
---> Using cache
.
.
The push refers to a repository [registry.heroku.com/hidden-escarpment-87242/web]
.
.
これでデプロイが完了しました。
https://${your_app_id}.herokuapp.com/
で、アクセスすることが出来ます。
Heroku上のアプリをproductionモードにする
この時点ではHeroku上のアプリはdevelopmentモードになっています。
それでは色々と困るので、productionモードにするための方法を説明します。
Herokuはご存知の通りThe Twelve Factorsの元祖であり、III. 設定の原則に則のって設定は環境変数に記載されています。
おそらく、Dockerではなく通常のCedarであればデフォルトで設定されてると思われますが、Dockerの場合は自分で設定する必要があります。
まず、現在のHeroku上に指定されている環境変数を確認します。
$ heroku config
=== hidden-escarpment-87242 Config Vars
空であることが分かりますね。
続いて、Railsを本番環境で動かすための設定を下記に追加します。
$ heroku config:add RAILS_ENV=production
$ heroku config:add RACK_ENV=production
$ heroku config:add RAILS_SERVE_STATIC_FILES=enabled
$ heroku config:add RAILS_LOG_TO_STDOUT=enabled
$ heroku config:add LANG=en_US.UTF-8
再びconfigコマンド。
$ heroku config
=== hidden-escarpment-87242 Config Vars
LANG: en_US.UTF-8
RACK_ENV: production
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: enabled
RAILS_SERVE_STATIC_FILES: enabled
追加されましたね!
最後にセッションキー等のシードに使われるSECRET_KEY_BASE
を追加します。
これは通常コマンドで生成したものを指定するので下記のようにしました。
$ heroku config:add SECRET_KEY_BASE=$(docker run -v `pwd`:/usr/src/app -w /usr/src/app -it koduki/example-todo bundle exec rake secret)
なお、この値は秘密キーなので他の人に間違って共有しないように注意しましょう。
データベースの準備とCRUD画面の作成
とりあえず、動くようになったところでデータベースの作成とCRUD画面の登録をしましょう。
PostgreSQLの導入と設定
従来は開発環境ではデータベースはsqliteを使うことも多いと思いますが、今回は本番同様にPostgreSQLを使います。
postgresもDockerがあるので導入が簡単ですからね。sqliteとの差分を意識しながら作るよりこっちの方がずっと簡単だと思います。
というわけで、下記のようなdocker-compose.ymlを書きます。
version: '2'
services:
db:
image: postgres
web:
image: koduki/example-todo
ports:
- 3000:3000
volumes:
- "$PWD:/usr/src/app"
depends_on:
- db
これでホスト名postgresでexample-todoからpostgresサーバにアクセスできるようになりました。
続いて、config/database.ymlを修正します。
HerokuではDATABASE_URLという環境変数に接続先情報を格納し、PGの修正をすることなく接続先のDBになっていているので、下記のように開発環境もプロダクション環境も同様に設定します。
development:
url: <%= ENV['DATABASE_URL'] %>
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
url: <%= ENV['DATABASE_URL'] %>
production:
url: <%= ENV['DATABASE_URL'] %>
これで、接続先は環境変数になりました。ではもう一度docker-compose.ymlを開いて設定ファイルを開いて環境変数に関しての追記をします。
version: '2'
services:
db:
image: postgres
web:
image: koduki/example-todo
ports:
- 3000:3000
volumes:
- "$PWD:/usr/src/app"
environment:
- DATABASE_URL=postgres://postgres:mysecretpassword@db:5432/postgres
depends_on:
- db
あとは、下記のコマンドでdocker-composeを立ち上げればOKです
$ docker-compose up
ScaffoldでモデルとCRUDページを作る
DBの設定が終わったところで、実際のモデルとCRUDページを作成していきます。
RailsならまずはScaffoldですね。DBがpostgresなので同時に起動させる必要があるため、本来は下記のようにdocker-compose run
で実行します。
$ docker-compose run web bundle exec rails generate scaffold todo title:string description:string
しかし、Windowsでは下記のような古から続くエラーが発生します。MacやLinuxを見習ってほしい...
ERROR: Interactive mode is not yet supported on Windows.
Please pass the -d flag when using `docker-compose run`.
あまり良い解決策はないので、シンプルにdockerコマンドを直接使うか、docker-composeで立ち上げたコンテナにdocker exec -it
するかって形になります。
今回はpostgresとの環境変数の連携とかが色々あるので、あきらめてdocker exec
する方式にしてきます。
$ docker-compose up -d
Starting devdockerrails_database_1
Starting devdockerrails_web_1
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------
devdockerrails_database_1 /docker-entrypoint.sh postgres Up 5432/tcp
devdockerrails_web_1 rails server -b 0.0.0.0 Up 0.0.0.0:3000->3000/tcp
$ docker exec -it devdockerrails_web_1 /bin/bash
root@2adf5610dc52:/usr/src/app#
root@2adf5610dc52:/usr/src/app# bundle exec rails generate scaffold todo title:string description:string
Running via Spring preloader in process 55
.
.
invoke scss
create app/assets/stylesheets/scaffolds.scss
続いてテーブル作成。
root@2adf5610dc52:/usr/src/app# bundle exec rake db:migrate
== 20161205025803 CreateJobs: migrating =======================================
-- create_table(:jobs)
-> 0.0035s
== 20161205025803 CreateJobs: migrated (0.0041s) ==============================
これでCRUDページが出来ました。
それではさっそくアクセスしてみましょう。
open http://192.168.99.100:3000/todos
無事動きました!
Heroku上でデータベースを動かす
では、ローカルでDBが動いたところで、さっそくHerokuに上げてみましょう。
$ docker build -t koduki/example-todo .
$ heroku container:push web
open https://${your_app_id}.herokuapp.com/todos
おそらく、エラーになったかと思います。これはHeroku上でデータベースの設定をしていないからです。
というわけで、Herokuでの設定をしていきます。
HerokuにPostgreSQLの追加
HerokuではAdd-onsとして簡単にデータベースとかKVSを追加することが出来ます。
- Herokuの管理画面から「Resouces」
- 「Add-ons」の検索バーで「heroku-postgresql」と入力
- Provision
これだけです。ね、簡単でしょ?
この時点で環境変数にも登録されています。heroku configを見てみましょう。
$ heroku config
=== limitless-tor-12820 Config Vars
DATABASE_URL: postgres://xxx...
.
.
上記のように環境変数に登録されていることが分かりますね?
テーブルの作成
HerokuとPostgresの関連付けは出来たのでテーブルを行います。
下記のように実行することで、Heroku上のDBにテーブルを作成することが出来ます。
$ heroku run rake db:migrate
$ heroku restart
今度はちゃんとエラー無くアクセスできましたね?
open http://192.168.99.100:3000/todos
まとめ
Heroku Container Registryを使うことで素のDockerを利用しつつ、各種Add-onsの恩恵を受けれる環境を作ることが出来ました。
また、Rails開発するならやはりMacやLinux!ってのが今までの流れだったと思いますが、Dockerを今回のように使えばローカル環境にはRubyのインストールすら不要なので、Windowsでもはまりどころは随分減るのかな、と思います。
今回はページの都合で書かなかったのですが、RedisとSidekiqとActiveJobの組み合わせが結構良かったのでこれは別途記事を書こうと思います。asset周りも少し罠がありましたし。
それでは、来年もHappy Hacking!