めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

OpenShiftの内部構造についての覚書 (2)

アプリケーションデプロイの仕組み

いくつかの具体例で、クラウド上のPaaS環境にアプリケーションをデプロイした際の動作を説明します。

Rubyカートリッジ

まずは、単純に、ruby-1.9のギアを用意します。

# rhc app create -a rubyapp -t ruby-1.9
Password: ********

Creating application 'rubyapp'
==============================

  Cartridge: ruby-1.9
  Gear Size: default
  Namespace: enakaidom
  Scaling:   no
(以下略)

この時、クライアント側にデフォルト構成のアプリケーションのコードが「git clone」でコピーされます。

# cd rubyapp/
# ll
合計 24
-rw-r--r--. 1 root root 3500  1月  5 16:14 2013 README
-rwxr-xr-x. 1 root root 5588  1月  5 16:14 2013 config.ru
drwxr-xr-x. 2 root root 4096  1月  5 16:14 2013 public
-rw-r--r--. 1 root root  664  1月  5 16:14 2013 thread-dumper.rb
drwxr-xr-x. 2 root root 4096  1月  5 16:14 2013 tmp

Rubyのカートリッジでは、ApacheのPassengerモジュールにより、Rack仕様のWebアプリケーションが実行されます。(それぞれのカートリッジの仕様は、OpenShift Originの各カートリッジのソースコードを見ると分かります。)Rackの説明は省略しますが、簡単に言うと、設定ファイルconfig.ruによって、呼び出し先のアプリケーションを切り替えることができます。

デフォルトでは、config.ruの中に、直接、HTMLが埋め込まれています。

config.ru

require 'rack/lobster'
require './thread-dumper'

map '/health' do
  health = proc do |env|
    [200, { "Content-Type" => "text/html" }, ["1"]]
  end
  run health
end

map '/lobster' do
  run Rack::Lobster.new
end

map '/' do
  welcome = proc do |env|
    [200, { "Content-Type" => "text/html" }, [<<WELCOME_CONTENTS
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>Welcome to OpenShift</title>
  <style>
  html {
(以下略)

適当にコンテンツを編集したら、git commit/git pushを実行します。

# sed -i -e 's/Welcome to OpenShift/Hello, OpenShift!/' config.ru
# git commit -a -m "New Title"
# git push
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 300 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: restart_on_add=false
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Done
remote: restart_on_add=false
remote: ~/git/rubyapp.git ~/git/rubyapp.git
remote: ~/git/rubyapp.git
remote: Running .openshift/action_hooks/pre_build
remote: Running .openshift/action_hooks/build
remote: Running .openshift/action_hooks/deploy
remote: hot_deploy_added=false
remote: Done
remote: Running .openshift/action_hooks/post_deploy
To ssh://369d22c1c9c94b5683c7f9c1e5652187@rubyapp-enakaidom.rhcloud.com/~/git/rubyapp.git/
   bab6f7c..423cf72  master -> master

「git push」により、コンテンツがギアに送り込まれた後、ビルド処理が実施されます。ビルド処理の内容は、カートリッジによって異なります。JBossなどの場合は、mavenによるコンパイル処理が走ります。編集したコンテンツは、自動的にWebで公開されています。

実際にビルド処理を行う部分のソースは、こちらになります。ポイントは次の部分で、「Gemfile」が存在する場合は、ギア内部で、Bundlerによる依存モジュールのインストール処理が行われるようになっています。

  if [ -f ${OPENSHIFT_REPO_DIR}/Gemfile ]
  then
...
          pushd ${OPENSHIFT_REPO_DIR} > /dev/null
          /usr/bin/scl enable ruby193 "bundle install --deployment"
          popd > /dev/null
          export GIT_DIR=$SAVED_GIT_DIR
...
          pushd ${OPENSHIFT_REPO_DIR} > /dev/null
          /usr/bin/scl enable ruby193 "bundle exec rake assets:precompile" 2>/dev/null
          popd > /dev/null

これを利用することで、Ruby on Railsなどのフレームワークを後から追加することが可能になります。具体例は次のセクションで。。。

Rubyカートリッジ + Ruby on Rails

まず、クライアント側にRailsとBundlerを導入しておきます。

# yum groupinstall "Development Tools"
# yum install ruby-devel sqlite-devel postgresql-devel
# gem install rails bundler

続いて、Ruby環境のギアを用意して、PostgreSQLカートリッジを追加します。

# rhc app create -a railsapp -t ruby-1.9
Password: ********

...

# rhc cartridge add postgresql-8.4 -a railsapp
Password: ********

Adding 'postgresql-8.4' to application 'railsapp'
Success
postgresql-8.4
==============
  Properties
  ==========
    Database Name  = railsapp
    Connection URL = postgresql://127.7.164.129:5432/
    Password       = GjQxS4CsQcFT
    Username       = admin

PostgreSQLのカートリッジを追加すると、DBの接続URLやパスワードなどが表示されます。これらは、ギア内部では、環境変数を通してアクセスできるので、特に覚えておく必要はありません。(とはいえ、トラブル対応用に記録しておくことはお勧めします。)

この例では、「railsapp」ディレクトリ以下にデフォルトのコンテンツが用意されていますが、これに被せる形で、Railsアプリケーションを作成します。

# ll
合計 12
drwxr-xr-x. 16 root root 4096  1月  5 16:04 2013 railsapp

# rails new railsapp
       exist  
      create  README.rdoc
      create  Rakefile
    conflict  config.ru
Overwrite /root/work/railsapp/config.ru? (enter "h" for help) [Ynaqdh] Y
...

必要なライブラリを追加しておきます。同じライブラリをギア上にも導入するため、必ず、Bundlerを利用して追加してください。

# cd railsapp
# cat <<EOF >>Gemfile
gem 'pg'
gem 'execjs'
gem 'therubyracer', '~> 0.10.2'
EOF
# bundle install

config/database.ymlにPostgreSQL接続情報を記載します。ここで、先に触れた、DB接続情報の環境変数を利用しています。

config/database.yml

production:
  adapter: postgresql
  encoding: utf8
  database: <%=ENV['OPENSHIFT_APP_NAME']%>
  pool: 5
  host: <%=ENV['OPENSHIFT_POSTGRESQL_DB_HOST']%>
  port: <%=ENV['OPENSHIFT_POSTGRESQL_DB_PORT']%>
  username: <%=ENV['OPENSHIFT_POSTGRESQL_DB_USERNAME']%>
  password: <%=ENV['OPENSHIFT_POSTGRESQL_DB_PASSWORD']%>

最後にもう1つだけ、仕込みがあります。新しいコードをgit pushした際に、ギア側で、DBのMigrate処理を実行させるためのものです。

# cat <<'EOF' >>.openshift/action_hooks/deploy 
pushd ${OPENSHIFT_REPO_DIR} > /dev/null
bundle exec rake db:migrate RAILS_ENV="production"
popd > /dev/null
EOF

「.openshift/action_hooks」以下に用意されているスクリプトは、ギア側のさまざまな処理にフックして、任意の処理を実施するために利用します。ここでは、デプロイ処理後にMigrate処理を実施させています。

以上で準備は完了です。あとは、Railsアプリを作成して、git commit/git pushすればOKです。ここでは、ありがちなBlogアプリを用意してみます。

# rm public/index.html 
# rails generate controller home index
# rails generate scaffold Post name:string title:string content:text
# echo '<%= link_to "My Blog", posts_path %>' >> app/views/home/index.html.erb 

# git add .
# git commit -a -m "Initial Application"
# git push

「git push」の後、ギア側で、bundle installが走るので、追加ライブラリのインストール完了までに結構な時間がかかります。気長にお待ちください。無事にデプロイが完了すると、Webからアプリケーションが利用可能になります。

晒すほどのものではありませんが、一応、スクリーンショットを載せておきます。

次回は、Jenkinsカートリッジの使い方を書いておく予定です。。。。