ラベル Ruby の投稿を表示しています。 すべての投稿を表示
ラベル Ruby の投稿を表示しています。 すべての投稿を表示
2014年5月13日火曜日

[Ruby] Chefで”application_java”クックブックを使ってTomcatにwarファイルをデプロイする際にはまったこと

2013-10-21 20.28.59

猫と一緒にガジェットライフ♪ムチャ(@mutoj_rdm821)です。

前回に続き、ChefでJavaアプリケーションのデプロイを行うクックブック”application_java”を使っていたときに遭遇した問題とその解決方法です。

前回記事はこちら↓

[Ruby] Chefで”database”クックブックを使ってCentOSにPostgreSQLを構成する際にはまったこと | 羽根帽子の太公望



クックブック”application_java”とは

Tomcatへのwarファイルのデプロイを自動化するものです。

どうも一般的にはChefのお仕事はミドルウェアの構成までで、その上で動くアプリケーションの構成については別のツール(Capistrano:カピストラーノなど)を使うようですが、今回は事情あってChefで全て行うというお達しでした。

クックブックは公式のコミュニティで配布されています。

そこからリンクされているgithubの方へ行くと、サンプルレシピが書いてあります。

application 'my-app' do
  path         '/usr/local/my-app'
  repository   '/nas/distro/my-app.war'
  revision     '...'
  scm_provider Chef::Provider::File::Deploy
  java_webapp do
    database_master_role 'database_master'
    database do
      driver     'org.gjt.mm.mysql.Driver'
      database   'name'
      port       5678
      username   'user'
      password   'password'
      max_active 1
      max_idle   2
      max_wait   3
    end
  end
  tomcat
end

別途”java”、”tomcat”、”application”クックブックも必要になります。これらもコミュニティで配布されています。



謎のエラーが発生

サンプル通りにchef-soloを実行すると、こんなエラーが出ました。

Error executing action `deploy` on resource 'deploy_revision[solr]'

コードを探ってもよくわからず・・・。



解決方法

色々探し回った結果、解決方法を見つけました。

配布元のリポジトリから別の人がforkしたリポジトリの、さらにtestというブランチに対応済みのコードがアップされていました。

こちらのクックブック一式を使うと正常にデプロイができました。

クックブックによっては、公式のコミュニティで配布されているものでも長いこと放置されていたりするので注意が必要です。かといって、bashリソースとかでシェルスクリプトをゴリゴリ書いてしまうと汎用性が失われるので、難しい所です。


今度技術評論社からChefの本が出るようなので、利用している人はチェックしてみてはいかがでしょうか。

それではみなさま良きガジェットライフを(´∀`)ノ



2014年5月5日月曜日

[Ruby] Chefで”database”クックブックを使ってCentOSにPostgreSQLを構成する際にはまったこと

2013-10-21 20.28.59

猫と一緒にガジェットライフ♪ムチャ(@mutoj_rdm821)です。

Chefというのは、サーバー(主にLinux)の設定やミドルウェアのインストール及び構成などをRubyのコードで記述して自動化するプラットフォームです。今回はピンポイントではまった事を書くだけなので、もう少し詳しく知りたい方は以下の記事をご覧下さい。完璧にまとまっています。

chef-solo - Chefを読んで実行するための全知識 – Qiita

今回ご紹介するのは、databaseクックブックを使ってPostreSQLを構成しようとしたときにはまった事とその解決方法です。しかしながら、確認したところこの問題は2014年5月5日時点で既に修正されている(後述)ので、今だと発生しないと思います。とはいえ、せっかくなので記事にしておきます。



databaseクックブックとは

既にインストールされているPostgreSQL、MySQL、SQLServerに対して、ユーザーの追加・削除・権限付与、データベースの作成・削除、SQLの投入を行う事ができます。

それぞれのセットアップについては個々にクックブックが配布されています。



CentOS上でPostgreSQLを構成しようとするとエラー

レシピを書いて実行すると、以下のエラーが発生しました。

Chef::Exceptions::Package: packagelibpq-dev had an error: Chef::Exceptions::Package: No version s
pecified, and no candidate version available for libpq-dev

libpq-devというのは、Debian用のパッケージです。なぜかCentOSなのにこれを入れようとして、存在しないので落ちてしまっていました。

詳しく追っていくと、内部で別の”postgresql”というクックブックを呼び出していて、その中のrecipes/ruby.rbでエラーが起きているようでした。以下はchef(chef-solo)の出力をdebugにした時の内容です。

================================================================================
Error executing action `install` on resource 'package[libpq-dev]'
================================================================================
SystemExit
----------
exit

Cookbook Trace:
---------------
/tmp/chef-repo/site-cookbooks/postgresql/recipes/ruby.rb:54:in `rescue in from_file'
/tmp/chef-repo/site-cookbooks/postgresql/recipes/ruby.rb:24:in `from_file'
/tmp/chef-repo/cookbooks/database/recipes/postgresql.rb:20:in `from_file'
/tmp/chef-repo/xxxxxxxxxxxxxxxxxxxx/recipes/database.rb:14:in `from_file'

Resource Declaration:
---------------------
# In /tmp/chef-repo/site-cookbooks/postgresql/recipes/ruby.rb
~略~
Compiled Resource:
------------------
# Declared in /tmp/chef-repo/site-cookbooks/postgresql/recipes/ruby.rb:52:in `rescue in from_file'
package("libpq-dev") do
  action [:nothing]
  retries 0
  retry_delay 2
  package_name "libpq-dev"
  cookbook_name :postgresql
  recipe_name "ruby"
end

================================================================================
Recipe Compile Error in /tmp/chef-repo/xxxxxxxxxxxxxx/recipes/database.rb
================================================================================

SystemExit
----------
package[libpq-dev] (postgresql::ruby line 52) had an error: SystemExit: exit

Cookbook Trace:
---------------
47: resources("package[#{pg_pack}]").run_action(:install)
48: end
49: require 'rbconfig'
50: 
51:
52: package "libpq-dev" do
53: action :nothing
54>> end.run_action(:install)
55:
56: begin
57: chef_gem "pg"
58: rescue Gem::Installer::ExtensionBuildError => e
59: # Are we an omnibus install?
60: raise if RbConfig.ruby.scan(%r{(chef|opscode)}).empty?
61: # Still here, must be omnibus. Lets make this thing install!
62: Chef::Log.warn 'Failed to properly build pg gem. Forcing properly linking and retrying (omnibus fix)'
63: gem_dir = e.message.scan(%r{will remain installed in ([^ ]+)}).flatten.first
Relevant File Content:
----------------------
/tmp/chef-repo/site-cookbooks/postgresql/recipes/ruby.rb:
~略~

最後のCookbook Trace:の所を見ると、確かにlibpq-devを入れようとして落ちてます。Chefでは実行されているOSを判別することもできるのですが、ここでは判定が行われていませんでした。

※この問題自体は1年以上前に修正がコミットされていたようですが、mastarにマージされたのが2週間ほど前だったようです(;´∀`)



どう対処したか

エラーの部分だけを見ても分かりませんが、この前にpostgresqlを操作するためのRubyのライブラリ(gem)の”pg”をrequireしようとして、失敗した場合(インストールされていない場合)に該当コードに到達することが分かりました。

なので、自分で作ったレシピの方で先にpgをインストールしてしまえばOKでした。

しかし、このpgというgemはネイティブコードを含んでいて、インストール時にコンパイルが必要となります。そのためにオプションを与えてやらないといけません。具体的には”pg-config”というコマンドへのパスです。

書いたレシピが以下の内容です。

pg_config_path = "/usr/pgsql-#{node['postgresql']['version']}/bin/pg_config"
require 'rbconfig'
gem_package "pg" do
  gem_binary "#{RbConfig::CONFIG['bindir']}/gem"
  options("-- --with-pg-config=#{pg_config_path}")
  action :install
end

パスの探し方は、別の何かのクックブックを参考にしてこのようにしました。

gem_packageリソースのオプションで、gem_binaryとoptionsを指定しています。

「先にpg入れておけばいいじゃん」と思うかもしれませんが、chefが実行に使うRubyの環境は専用に保持していて、普通にログインしてgemコマンドでインストールしても効果はありません。まあそのchef用環境(CentOSだと/var/opt/chef/以下だったか)に対してインストールすればよいのですが、それだとchefで構成する意味が無くなってしまいます。

Chefの公式コミュニティで配布されているクックブックはこうした小さなバグがあって、しかも長いこと放置されているものもあったりで大変でした。

次回はappilcation_javaというクックブックで遭遇した問題について書こうと思います。

それではみなさまよきガジェットライフを(´∀`)ノ



2014年4月17日木曜日

[Ruby]ちょっとはまった小ネタ

2013-10-21 20.28.59

猫と一緒にガジェットライフ♪ムチャ(@mutoj_rdm821)です。

今回は、Rubyで開発をしていてはまった小ネタをご紹介します。お役に立てば幸いです。



erbを使ってerb形式のファイルを出力する

Rubyに標準添付されているテンプレートライブラリとしてERBという物があります。詳しい紹介は以下のページにあります。

Rubyist Magazine - 標準添付ライブラリ紹介 【第 10 回】 ERB

簡単に説明すると、<%~%>で囲まれた部分にRubyスクリプトを記述したテンプレートに対して、ちょっとしたおまじないを書いてやると動的に文字列を埋め込んだりした結果が得られます。

require 'erb'
str = "hoge"
erb = ERB.new("value = <%= str %>")
puts erb.result(binding)

出力結果

value = hoge

あまり使用する局面はないかもしれませんが、ERBを使ってerbファイルを出力したいという状況がありました。出力結果に<%~%>といったerbの埋め込み構文を残さないといけません。

どうすれば良いかというと、%を2つ並べて書きます。

year = 2014
owner = "Junichi Muto"

# テンプレートは以下のような感じ
"<%%# Copyright <%= year %> <%= owner %> %>
"

こうすると、結果は以下のようになります。

<%# Copyright 2014 Jnichi Muto %>


文字列に文字を追加していく

文字列を結合するには+でできますが、追加していく場合は以下のようにも書けます。但し最初の変数には初期値が必要です(nilだとダメ)

content = "abc"
content << "def"


ランダムな文字列を生成する

例えば数値と英字小文字大文字による長さ5のランダムな文字列が欲しい場合は以下で得られます。

((0..9).to_a + ("a".."z").to_a + ("A".."Z").to_a).sample(5).join


シンボルで"-"を使いたい

RubyにはSymboleというクラスがあり、文字列の前に:(コロン)を付けて表します。シンボルで表した文字列は唯一のオブジェクトなり、必ず同じものを指します。ここがJavaとちょっと違う所。

ハッシュを定義するときには、通常「キー => 値」の形式で書きますが、ハッシュのキーにシンボルを使う場合は「シンボル: 値」という書き方ができます(Ruby1.9から)。

シンボルで"-"を使いたいとき、そのままだとエラーになってしまいますが、ダブルクォートでくくってやれば大丈夫です。しかし、このとき「シンボル: 値」の記述は使えません。

# シンボル
:test
# ハイフンを入れたシンボル
:"test-hyphen"
# ハッシュの定義
{ :a => "A", :b => "B", :c => "C" }
{ a:"A", b:"B", c:"C" } #シンボルの場合

# 以下はエラー
#{ "test-hyphen": "value" }

# 従来の記述法ならOK
{ :"test-hyphen" => "value" }


JSON#parseでキーをシンボルにしたい

JSON#parseはJSON文字列からHashを生成するメソッドです。普通に変換するとHashのキーは文字列となりますが、RubyでHash生成する場合はキーをシンボルにすることが多いと思います。

これらをマージすると文字列とシンボルは別の扱いになってしまうので結果がおかしな事になったりします。(さらにJSON#generateで戻すと、シンボルも文字列の扱いになってしまい、同じ文字列だと片方が上書きされたりします。)

JSON#parseでキーをシンボルにしたい場合、第2引数でsymbolize_names: trueを渡してやります。

result = JSON.parse(json_str, symbolize_names: true)



Rails・マルチスレッド環境で「RuntimeError: Circular dependency detected while autoloading constant xxxx」が発生する

がらっと変わりますが(;´∀`)

Railsにはオートロードという機能があります。以下のようにソースコードがあるディレクトリを予め登録しておくと、個々のソースコードでrequireをしなくても、初めて発見したクラスをディレクトリから探して自動的に読み込んでくれます。

require 'active_support/dependencies.rb'
ActiveSupport::Dependencies.autoload_paths << 'src'

しかし、マルチスレッド環境で問題が起こることがありました。

通常、この機能で一度読み込んだコードはキャッシュされるので再び読むことはありません。しかし、2つのスレッドが同時に知らないクラスを発見した場合、タイミングによっては読み込み済みかのチェックを通過してしまい、片方のスレッドは読み込みに成功するのですが、もう片方のスレッドでは「既に読み込んでいる!」となって上記エラーが発生します。

複数のクラスから使われるヘルパーのようなクラスは、オートロードから除外して予めロードしてしまうと解決します。対象のソースコードは別ディレクトリに移してautoload_pathsから外し、以下のようにしてロードしてしまいます。

ActiveSupport::Dependencies.require_or_load(file)

タイミングによっては起こらないので、原因を探るのが大変でした(;´∀`)


オートロードを使っている時にGuardでテストしていると、ソースコードの保存を検知してくれない

rspecを使ってテストする際にGuardというツールでテストを行っていました。これはコードを保存するとそれを検知して該当するspecファイルのテストコードを実行してくれるのですが、あるとき(バージョンが変わったせいか)この検知が働かなくなりました。

Guardfileで以下のようにするとうまくいきました。上記のオートロードがいけなかったようです。

Spork.each_run do
  ActiveSupport::Dependencies.clear
end


まとめ

初心者ながらいろいろ苦労して解決した問題です。同じように困っている方のお役に立てれば幸いです。

それではみなさま良きガジェットライフを(´∀`)ノ



▼スポンサーリンク

▼ブログを気に入っていただけたらRSS登録をお願いします!
▼ブログランキング参加中!応援よろしくお願いします。

スポンサーリンク