本日の第14回「Cloud Foundry 百日行」は、Yabitzです。
社内のサーバなどホスト管理を行えるアプリケーションです。rubyで書かれており、加えてMySQL環境が必要となります。
[Yabitz]基本情報
デプロイ
手順
- 0) 準備
- 1) cf-mysql-releaseを利用する為のコード修正
- 2) アプリの起動
- 3) 動作確認
0.準備
まずはソースコードの取得をしましょう。
$ git clone https://github.com/livedoor/yabitz.git
$ cd yabitz
$ ls
config.ru Gemfile lib public README.md scripts spec tmp view
ここでデプロイに入る前に補足を少ししておきます。
- これまでの記事にも出てきましたがMySQLなどのDBと連携が必要となるアプリではDBの初期設定が必要なものが多く存在します。
今回のアプリもスクリプトでデータベースのスキーマを作成する必要があります。
cf create-service
で作成したMySQLのDBに初期設定を行う場合、「アプリ内から初期設定を実行する」または「DB初期設定用のアプリを利用する」などいくつかの対処がありますが、今回は「アプリ内から初期設定を実行する」方法で対処しています。機会があればその他の方法もご紹介したいと思います。
- このアプリはユーザデータを扱う方法がpluginで提供されています。今回はホストの情報を管理するDBにユーザ情報も管理する為のpluginを利用します。
- 今回紹介する方法でアプリをデプロイする為にはGemfile.lockファイルを
bundle install
で作成する必要があります。その為、下記の準備が必要になります。
- ruby(利用したバージョン 2.1.5)
- bundler(利用したバージョン 1.10.2)
1.cf-mysql-releaseを利用する為のコード修正
まずはアプリを停止状態で cf push
してMySQLとバインドできる状態にします。
$ cf push yabitz --no-start
そしてMySQLを準備します。
なおMySQLはこれまでの記事にも何度か出てきているcf-mysql-releaseを利用します。
$ cf marketplace
service plans description
p-mysql 100mb-dev, 1gb-dev A MySQL service for application development and testing
$ cf create-service p-mysql 100mb-dev yabitzdb
$ cf bind-service yabitz yabitzdb
$ cf env yabitz
System-Provided:
{
"VCAP_SERVICES": {
"p-mysql": [
{
"credentials": {
"hostname": "10.244.1.18",
"jdbcUrl": "jdbc:mysql://10.244.1.18:3306/cf_9f086cff_c59f_4b6f_afa7_925ce372764b?user=k3i8E0ZSj6aBmlLx\u0026password=HMHe7pf0XLqw2j0c",
"name": "cf_9f086cff_c59f_4b6f_afa7_925ce372764b",
"password": "HMHe7pf0XLqw2j0c",
"port": 3306,
"uri": "mysql://k3i8E0ZSj6aBmlLx:HMHe7pf0XLqw2j0c@10.244.1.18:3306/cf_9f086cff_c59f_4b6f_afa7_925ce372764b?reconnect=true",
"username": "k3i8E0ZSj6aBmlLx"
},
"label": "p-mysql",
"name": "yabitzdb",
"plan": "100mb-dev",
"tags": [
"mysql"
]
}
]
}
}
:
準備が終われば手元のコードの修正に入ります。
MySQLとの接続情報は上記の cf env
で得た値をベタ書きしてもいいのですが、環境変数 VCAP_SERVICES(以下 環境変数) から情報を取得できるようにコードを一部書き換えます。
まず環境変数はjsonで提供される為、Gemfileに以下のように追記しjsonを利用できるようにします。
$ vi Gemfile
$ git diff
diff --git a/Gemfile b/Gemfile
index a217b6e..426bc22 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,6 +8,6 @@ gem 'haml'
gem 'sass'
gem 'ruby-ldap'
gem 'rspec'
-
+gem 'json'
gem 'passenger'
gem 'unicorn'
次に手元の環境で bundle install
します。これは後述のruby-buildpackの適用条件を満たす為にGemfile.lockを作成をすることが目的ですのでこちらのリポジトリのコードを使う場合は不要です。
なお、 bundle install
でのエラーは自己解決していただくか、修正済みのコードをご利用ください。
$ bundle install --path vendor/bundle
$ cat Gemfile.lock
GIT
remote: git://github.com/tagomoris/stratum.git
revision: d763f84f3885a81bad263588f3f8e62a58d73b6f
specs:
stratum (0.1.0)
mysql2-cs-bind
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.2.5)
haml (4.0.6)
tilt
json (1.8.3)
kgio (2.9.3)
mysql2 (0.3.18)
mysql2-cs-bind (0.0.6)
mysql2
passenger (5.0.10)
rack
rake (>= 0.8.1)
rack (1.6.2)
rack-protection (1.5.3)
rack
raindrops (0.13.0)
rake (10.4.2)
rspec (3.3.0)
rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-core (3.3.0)
rspec-support (~> 3.3.0)
rspec-expectations (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-mocks (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
ruby-ldap (0.9.17)
sass (3.4.14)
sinatra (1.4.6)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
tilt (2.0.1)
unicorn (4.9.0)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
PLATFORMS
ruby
DEPENDENCIES
haml
json
mysql2
mysql2-cs-bind
passenger
rspec
ruby-ldap
sass
sinatra
stratum!
unicorn
BUNDLED WITH
1.10.2
$ rm -rf vendor/bundle
cf push
後にruby-buildpackにて 再度 bundle install
は実行されるので、不要となるvendor/bundleディレクトリは削除しておいて下さい。
次はMySQLの環境変数を読み込めるように3つのファイルを編集します。
-
アプリとDBの接続plugin(lib/yabitz/plugin/config_instant.rb)
requireに先ほどGemfileに追加したjsonを加え、接続情報を環境変数から取得できるように変更します。
-
利用するユーザデータを指定するplugin(lib/yabitz/plugin/instant_membersource.rb)
cf create-service
で作成したMySQL内のユーザデータのtableを利用する為にpluginを有効化します。
-
DBの初期設定スクリプト(scripts/db_schema.rb)
上記のpluginにはDBの初期設定スクリプトと別に初期ユーザ登録用スクリプト(instant/register_user.rb)が準備されています。
しかし初期ユーザ登録用スクリプトは対話型となっておりCloud Foundry環境で実行するには不向きです。
その為、初期ユーザ登録機能もバッチ型のDBの初期設定スクリプトにまとめています。
$ vi lib/yabitz/plugin/config_instant.rb
$ vi lib/yabitz/plugin/instant_membersource.rb
$ vi scripts/db_schema.rb
$ git diff
:
diff --git a/lib/yabitz/plugin/config_instant.rb b/lib/yabitz/plugin/config_instant.rb
index 15b6462..b67cb61 100644
--- a/lib/yabitz/plugin/config_instant.rb
+++ b/lib/yabitz/plugin/config_instant.rb
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-
+require 'json'
module Yabitz::Plugin
module InstantConfig
def self.plugin_type
@@ -18,14 +18,14 @@ module Yabitz::Plugin
end
DB_PARAMS = [:server, :user, :pass, :name, :port, :sock]
-
+ mysql_dbs = JSON.parse(ENV['VCAP_SERVICES'])
CONFIG_SET = {
:database => {
- :server => 'localhost',
- :user => 'root',
- :pass => nil,
- :name => 'yabitz_instant',
- :port => nil,
+ :server => mysql_dbs["p-mysql"][0]["credentials"]["hostname"],
+ :user => mysql_dbs["p-mysql"][0]["credentials"]["username"],
+ :pass => mysql_dbs["p-mysql"][0]["credentials"]["password"],
+ :name => mysql_dbs["p-mysql"][0]["credentials"]["name"],
+ :port => mysql_dbs["p-mysql"][0]["credentials"]["port"],
:sock => nil,
},
:test_database => {
diff --git a/lib/yabitz/plugin/instant_membersource.rb b/lib/yabitz/plugin/instant_membersource.rb
index 5f8b9a6..6e4ddff 100644
--- a/lib/yabitz/plugin/instant_membersource.rb
+++ b/lib/yabitz/plugin/instant_membersource.rb
@@ -2,6 +2,7 @@
require 'digest/sha1'
require 'mysql2-cs-bind'
+require 'json'
module Yabitz::Plugin
module InstantMemberHandler
@@ -9,13 +10,14 @@ module Yabitz::Plugin
[:auth, :member]
end
def self.plugin_priority
- 0
+ 1
end
- DB_HOSTNAME = "localhost"
- DB_USERNAME = "root"
- DB_PASSWORD = nil
- DATABASE_NAME = "yabitz_member_source"
+ mysql_dbs = JSON.parse(ENV['VCAP_SERVICES'])
+ DB_HOSTNAME = mysql_dbs["p-mysql"][0]["credentials"]["hostname"]
+ DB_USERNAME = mysql_dbs["p-mysql"][0]["credentials"]["username"]
+ DB_PASSWORD = mysql_dbs["p-mysql"][0]["credentials"]["password"]
+ DATABASE_NAME = mysql_dbs["p-mysql"][0]["credentials"]["name"]
TABLE_NAME = "list"
def self.query(sql, *args)
result = []
diff --git a/scripts/db_schema.rb b/scripts/db_schema.rb
index a1fce02..727b511 100644
--- a/scripts/db_schema.rb
+++ b/scripts/db_schema.rb
@@ -2,6 +2,17 @@
# -*- coding: utf-8 -*-
require 'mysql2'
+require 'digest/sha1'
+
+## default user data
+TABLE_NAME = "list"
+USERNAME = "admin"
+PASSWORD = "password"
+FULLNAME = "default_adminuser"
+MAIL = ""
+BADGE = ""
+POSITION = ""
+PASSHASH = Digest::SHA1.hexdigest("#{PASSWORD}")
# before require of schema.rb
# init.rb MUST be already loaded with $YABITZ_RUN_ON_TEST_ENVIRONMENT = true
@@ -351,6 +362,22 @@ removed ENUM('0','1') NOT NULL DEFAULT '0'
) ENGINE=InnoDB charset='utf8'
EOSQL
+ sqls.push <<-EOSQL
+CREATE TABLE #{TABLE_NAME} (
+id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
+name VARCHAR(64) NOT NULL UNIQUE KEY,
+passhash VARCHAR(40) NOT NULL,
+fullname VARCHAR(64) NOT NULL,
+mailaddress VARCHAR(256) ,
+badge VARCHAR(16) ,
+position VARCHAR(16)
+) ENGINE=InnoDB charset='utf8'
+EOSQL
+
+ sqls.push <<-EOSQL
+INSERT INTO #{TABLE_NAME} SET name='#{USERNAME}',passhash='#{PASSHASH}',fullname='#{FULLNAME}',mailaddress='#{MAIL}',badge='#{BADGE}',position='#{POSITION}'
+EOSQL
+
c = conn()
sqls.each do |s|
c.query(s)
2.アプリの起動
今回のアプリには複数のbuildpackを利用しています。
-
heroku-buildpack-apt
指定のパッケージを apt-get install
できる Buildpackになります。
今回は ruby-buildpack にて実行される bundle install
で発生するエラー( ruby-ldap の gem install
)を回避する為に libsasl2-dev のパッケージを追加します。
-
ruby-buildpack
rubyのアプリをデプロイする為のBuildpackです。
このBuildpackは下記のように bundle install
を実行してくれます。
bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
ただし、Gemfile.lockが無い状態で実行すると --deployment
オプションが指定されている為、エラーとなってしまいます。その為、先ほどの手順のようにGemfile.lockを事前に作成しておく必要があります。
そして最後に複数のBuildpackを適用する為に下記のBuildpackを利用します。
- heroku-buildpack-multi
通常一つしか適用できないBuildpackを複数用いる為のBuildpackです。
(補足)heroku-buildpack-multiは非常に便利なBuildpackですが、利用するBuildpackによってはheroku-buildpack-multiで正常に動作をしないものもあるので注意が必要です。
それぞれのBuildpackに必要なファイルを準備していきます。
まずはheroku-buildpack-aptに必要なAptfile。 cf push
を実行するアプリのルートディレクトリに作成します。
ここで作成するAptfileには apt-get install
したいパッケージ名を記載します。
$ echo "libsasl2-dev" > Aptfile
次にheroku-buildpack-multiに必要な.buildpacks。こちらも上記のAptfileと同じディレクトリに作成します。
ここで作成する.buildpacksには利用したいBuildpackを実行したい順番で記載します。今回はruby-buildpackで実行される bundle install
の前に libsasl2-dev パッケージの追加が完了している必要があるので以下の順番になります。
$ echo -e "https://github.com/ddollar/heroku-buildpack-apt.git\nhttps://github.com/cloudfoundry/ruby-buildpack.git" > .buildpacks
$ cat .buildpacks
https://github.com/ddollar/heroku-buildpack-apt.git
https://github.com/cloudfoundry/ruby-buildpack.git
Buildpackの準備はここまでです。
いよいよデプロイに移ります。
cf push
に際し注意いただきたいのは初回と2回目以降で-cオプションに変更があります。
初回はDBの初期設定スクリプトを指定して実行する必要がありますが、2回目以降は設定済みのため不要です。
もしmanifest.ymlを作成される場合はその点を考慮して下さい。今回は作成せずに進めます。
$ cf push yabitz -b https://github.com/ddollar/heroku-buildpack-multi.git -c 'bundle exec ruby scripts/db_schema.rb&&bundle exec rackup config.ru --port=$PORT'
$ cf push yabitz -b https://github.com/ddollar/heroku-buildpack-multi.git -c 'bundle exec rackup config.ru --port=$PORT'
では cf push
を実行。
$ cf push yabitz -b https://github.com/ddollar/heroku-buildpack-multi.git -c 'bundle exec ruby scripts/db_schema.rb&&bundle exec rackup config.ru --port=$PORT'
:
App started
OK
App yabitz was started using this command `bundle exec ruby scripts/db_schema.rb&&bundle exec rackup config.ru --port=$PORT`
Showing health and status for app yabitz in org k-nagai / space work as k-nagai...
OK
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: yabitz.10.244.0.34.xip.io
last uploaded: Fri Jun 19 01:13:12 +0000 2015
stack: lucid64
state since cpu memory disk details
#0 running 2015-06-19 10:14:45 AM 0.0% 65.1M of 256M 0 of 1G
これでアプリのデプロイ完了です。
3.動作確認
URL( yabitz.10.244.0.34.xip.io )にアクセスしてみましょう。
画面の上にある”ログイン”をクリックしてログイン(db_schema.rbに記載したユーザ情報)します。

あとは各情報を登録して管理をするのですが、ホスト登録を達成できるようになるまでに各リストの準備がなかなかに大変です。
たとえ登録ボタンをクリックして「Internal Server Error」が出ても「アプリケーションがうまく動いていない!」なんてことは考えずに「どこかのリストの登録が先に必要なんだ」と思って進めてください。
ブラウザの横に cf logs
を表示しながら作業を進めると、どのテーブルの情報でエラーを起こしているかがわかり効率がいいです。
$ cf logs yabitz

登録出来た時は達成感が少し生まれます。資源管理は大変だ!ということですかね。
もう少しアプリの入力チェックにコードを加えればいきなりInternal Server Errorが返されることもなくなるかもしれませんが、今回はここまでです。お疲れさまでした。
今回使用したソフトウェア