RubyKaigi 2018 LT で発表させて頂きます。

RubyKaigi 2018 LT に出していた CFP が accept されましたので、 Red Chainer と Numo::NArray のパフォーマンス改善の取り組み (下記の二つ)について、 どんな事をしたか、どれくらい改善されたのかを python Chainer と numpy の結果と比較しながら、お話させて頂く予定です。

LT は初日 (5/31) になります。

Red Chainer の紹介は @hatappi さんが同じ日にメインセッションの Deep Learning Programming on Ruby - RubyKaigi 2018 でお話しされますので、そちらをご覧ください。

受理された CFP はこんな感じです。ご参考までに。

## Improve Red Chainer and Numo::NArray performance

## Abstract

I would like to talk my progress of daily contribution to Ruby world.
I'm planing to talk the performance improvement to Red Chainer and Numo::NArray(used by Red Chainer) gem.

I hope that machine learning will be comfortable to use with Ruby.

## Details

I would like to talk my progress of daily contribution to Ruby world.
Last year, I talked about "How to get on the shoulder of the giant for machine learning" at RejectKaigi 2017.
I'm planing to talk the performance improvement to Red Chainer and Numo::NArray(used by Red Chainer) gem as following with 5 minutes at RubyKaigi 2018 LT.

## Red Chainer 

- https://github.com/red-data-tools/red-chainer/pull/48

When my pull request will be merged, red-chainer gem performance will improve as follows.

* examples/mnist.rb 1 epoc  -> 73 % faster

## Numo::NArray

- https://github.com/ruby-numo/numo-narray/pull/94

With my pull request, the performance of Numo::NArray gem has been improved as follows.

* x.inplace + y or - y or * y  -> 10-15 % faster
* x.inplace + 1 or - 1 or  * 1  -> 2.1 times faster
* x.inplace / y or / 1 -> 3.2-3.8 times faster

Thanks.

PythonのコードをPython ASTベースでRubyに変換を行う py2rb.py 0.1.0 をリリースしました。

去年の8月に RejectKaigi 2017 にて発表した、「PythonのコードをPython ASTベースでRubyに変換を行う py2rb.py」が、初版公開できるレベルになったので、python のパッケージとして PyPIで公開しました。

pypi.python.org

github.com


py2rb.py は、Python機械学習関連のライブラリ、特に Chainer を Ruby に移植するためにPythonからRubyへのトランスコンパイラを作成している位置づけになります。

py2rb.py 開発の経緯の詳細は、下記 blog をご覧ください。

naitoh.hatenablog.com

特徴
  • Python ASTベースで1行単位にRubyへ翻訳
    1. Python => Ruby (メソッド,クラス,変数) : decorator や yield 等は未サポート
    2. import 文を解釈し、ローカルのモジュールファイルを参照&一括変換
    3. Python標準ライブラリ=>Ruby標準ライブラリ : (Chainer で使われている 73 モジュール のうち 10モジュールを仮実装)
  • ドメインが同じライブラリ間でPython=>Rubyに置き換え
    1. numpy => Numo::NArray (Chainer で使われている 116 メソッド のうち 31メソッドを仮実装)
    2. unittest => test::unit (Chainer で使われている 33 メソッド のうち 4メソッドを仮実装)
実行方法
$ py2rb
Usage: py2rb.py [options] filename.py
    or py2rb.py [-w [-f]] [-(r|b)] [-v] filename.py
    or py2rb.py -p foo/bar/ -m [-w [-f]] [-(r|b)] [-v] foo/bar/filename.py
    or py2rb -l lib_store_directory/ [-f]

Usage のように変換対象の Python のファイル名を指定して実行すると、変換結果を標準出力に表示します。
(以前は利用しているローカルのモジュール名を個別に指定する必要がありましたが、0.1.0では import 文を解釈しローカルのモジュールファイルを自動的に参照します。)
"-w" オプションをつけると、filename.py の変換結果を filename.rb に拡張子を変えた形式で保存します。
すでに filename.rb が存在すると処理を Skip するので、上書き保存をしたい場合は "-f" オプションをあわせて指定します。
"-m" オプションをつけると参照したローカルのモジュールファイルもあわせて一括変換します。

実際に変換すると下記のようになります。

Python サンプルコード:
$ git clone git://github.com/naitoh/py2rb.git
$ cat py2rb/tests/strings/zipstring.py

s1 = "hello"
s2 = "world"
s3 = "abcd"

s4 = list(zip(s1,s2,s3))

for item in s4:
    print("----")
    for val in item:
        print(val)

Python実行例:

$ python py2rb/tests/strings/zipstring.py
----
h
w
a
----
e
o
b
----
l
r
c
----
l
l
d

変換例:
$ py2rb -r py2rb/tests/strings/zipstring.py

# frozen_string_literal: true

require 'module'

using EnumerableEx
using PythonZipEx
using PythonPrintEx
using PythonIsBoolEx
using PythonIndexEx
using PythonFindEx
using PythonSplitEx
using PythonStripEx
using PythonStringCountEx
using PythonRemoveEx
using PythonMethodEx

s1 = "hello"
s2 = "world"
s3 = "abcd"
s4 = zip_p(s1, s2, s3).to_a
for item in s4
  print("----")
  for val in item
    print(val)
  end
end

なお、ここで Python の zip メソッドを Rubyでは独自に定義した zip_p メソッドに置き換えています。
PythonRuby で互換性のないメソッドなどの場合、互換用ライブラリを使用することで互換性を確保しています。
"using PythonZipEx" をコメントアウトすれば互換用ライブラリのメソッドは個別に無効になるので、処理の置き換えなどを実施後に該当処理が不要になれば using 行を削除するだけで済みます。

先ほどは、標準出力に表示されましたが、"-w" をつけることで、ファイル保存されます。

$ py2rb -r py2rb/tests/strings/zipstring.py -w
Try  : py2rb/tests/strings/zipstring.py -> py2rb/tests/strings/zipstring.rb : [OK]

"-l" で互換用ライブラリを指定ディレクトリに保存します。今回はカレントディレクトリに保存します。

$ py2rb -l .
OK :  ./module.rb file was stored.

Ruby の -I オプションでカレントディレクトリをライブラリパスを追加し、実行時に module.rb を呼び出し可能にして実行します。

Ruby実行例:

$ ruby -I . py2rb/tests/strings/zipstring.rb
----
h
w
a
----
e
o
b
----
l
r
c
----
l
l
d

Python の実行結果と同じになりました。

大規模なソースコードの変換の例として、chainer の変換例は次のようになります。

$ git clone git://github.com/chainer/chainer.git
$ cat chainer/chainer/variable.py

import collections
import copy
import heapq
import traceback
import warnings
import weakref

import numpy

import chainer
from chainer.backends import cuda
from chainer import initializers
from chainer.initializers import constant
from chainer.utils import argument

def _check_grad_type(func, x, gx):
    if x.data is None or gx is None:
        # ``x.data is None`` implies that the data array is not retained
        return
    if not isinstance(gx, type(x.data)):
        msg = ('Type of data and grad mismatch\n%s != %s' %
               (type(x.data), type(gx)))
        typ = TypeError
    elif gx.dtype != x.data.dtype:
        msg = ('Dtype of data and grad mismatch\n%s != %s' %
               (x.data.dtype, gx.dtype))
        typ = TypeError
    elif gx.shape != x.data.shape:
        msg = ('Shape of data and grad mismatch\n%s != %s' %
               (x.data.shape, gx.shape))
        typ = ValueError
    else:
        return

    detail = ''
    if func:
        detail = 'Function `{0}` ({1}) has a bug.\n'.format(
            type(func)._impl_name, func.label)
        stack = func.stack
        if stack:
            detail += 'Stacktrace of the function is below:\n'
            for line in traceback.format_list(func.stack):
                detail += line
        detail += '''
Please report this error to the issue tracker with the stack trace,
the information of your environment, and your script:
https://github.com/chainer/chainer/issues/new.
'''.format(type(func).__name__, func.label)

    raise typ(detail + msg)
(略)

$ py2rb -p chainer chainer/chainer/variable.py

module Chainer
  module Variable
    require 'weakref'
    require_relative 'backends/cuda'
    include Chainer::Backends
    require_relative 'chainer'
    include Chainer
    require_relative 'initializers/constant'
    include Chainer::Initializers
    require_relative 'utils/argument'
    include Chainer::Utils
    def _check_grad_type(func, x, gx)
      if is_bool((x.data === nil)||(gx === nil))
        return
      end
      if is_bool(!(gx.is_a? (x.data).class))
        msg = "Type of data and grad mismatch
%s != %s" % [(x.data).class, (gx).class]
        typ = ArgumentError
      else
        if gx.dtype != x.data.dtype
          msg = "Dtype of data and grad mismatch
%s != %s" % [x.data.dtype, gx.dtype]
          typ = ArgumentError
        else
          if gx.shape != x.data.shape
            msg = "Shape of data and grad mismatch
%s != %s" % [x.data.shape, gx.shape]
            typ = TypeError
          else
            return
          end
        end
      end
      detail = ""
      if is_bool(func)
        detail = "Function `{0}` ({1}) has a bug.
".format((func).class._impl_name, func.label)
        stack = func.stack
        if is_bool(stack)
          detail += "Stacktrace of the function is below:
"
          for line in (func.stack).to_a
            detail += line
          end
        end
        detail += "
Please report this error to the issue tracker with the stack trace,
the information of your environment, and your script:
https://github.com/chainer/chainer/issues/new.
".format((func).class.__name__, func.label)
      end
      raise typ, (detail)+(msg)
    end
(略)

ここまでの規模になると、ソースコード修正が必要になりますが、大部分の機械的な置き換えが行われた後なので悩む箇所や手間が削減されます。

実際にPython からの移植を行う場合は、"-m" オプションをつけると "-p" のパス配下のファイルで指定されたpython ファイル (下記の場合は chainer/chainer/variable.py) から参照されているファイルを一括変換するので便利です。

$ py2rb -p chainer chainer/chainer/variable.py -m -w
Try  : chainer/chainer/functions/math/fmod.py -> chainer/chainer/functions/math/fmod.rb : [OK]
Try  : chainer/chainer/functions/array/expand_dims.py -> chainer/chainer/functions/array/expand_dims.rb : [OK]
Try  : chainer/chainer/functions/pooling/average_pooling_nd.py -> chainer/chainer/functions/pooling/average_pooling_nd.rb : [OK]
Warning : syntax not supported (<_ast.ExtSlice object at 0x102df11d0>)
Warning : ExtSlice not supported (path[]) in Assign(ast.Subscript)
Warning : syntax not supported (<_ast.ExtSlice object at 0x102df11d0>)
Warning : syntax not supported (<_ast.ExtSlice object at 0x102dd35f8>)
Warning : syntax not supported (<_ast.ExtSlice object at 0x102ddef28>)
()

変換処理で未サポートなどにより変換ができない部分は Warning が表示され、該当箇所以外の変換を続行します。

現在、Red Data Tool プロジェクトで行われている Chainer の Ruby移植版 Red Chainer の開発に Join し、ドッグフーディングも兼ねて私の作成した部分については py2rb.py を使いながら (py2rb.py の機能不足点も強化しながら)開発を進めています。

github.com

今後はRed Chainer の成長にあわせて py2rb.py の機能カバレッジや完成度も向上していければと考えています。

興味ある方は使ってみてください。

RejectKaigi 2017で『Rubyで機械学習を行うための「巨人の肩に乗る」別の方法』という発表をしました

RubyKaigi 2017 に CFP を出したところ残念ながら Reject されてしまい、この内容を発表する場が無いかなと考えていたところ、
RejectKaigi 2017 に発表の機会を頂きましたので、お話させて頂きました。

スライドは下記に公開してますので、ご覧ください。

www.slideshare.net

お話した内容は

Ruby機械学習をしたいと思った場合、現状では不可能なため、下記の三つの内容について Ruby コミュニティで取り組みが行われており、

  1. 既存のgemをなんとかすること
  2. Rubyのための仕組みをつくっていくこと
  3. 巨人 (Python) の肩に乗る方法

この中の現実的な解が「巨人 (Python) の肩に乗る方法」を実現しつつある PyCall になります。

PyCall の詳細は過去の記事をご覧ください。

www.s-itoc.jp

getnews.jp

Ruby機械学習を行うための「巨人の肩に乗る」別の方法』

とは

  • Ruby から PyCall を経由してPython の機能をを呼び出す」

という方法とは異なり、

  • Pythonのライブラリを Ruby に移植」

することで、Python のノウハウ、知見を Ruby で活用したい
そのためにはどうすれば良いか

というお話になります。

今回、移植の対象としてChainerを選択しました、理由は

  • tensorflow.rb はすでに移植作業が行われているため、対象外。PyCallでまだ動作していないChainerがいいのではないか
  • (GPU対応を保留にすれば)主な依存性が numpy に絞られる Chainer について、numpy 部分の機能を Numo::NArray に置き換える形で移植を行えば移植のハードルが比較的低い

になります。

移植の現実性

について検討したところ、Chainer 2.0 : 合計 86.3 Ks (本体 49.8 Ks/テストコード 36.4 Kstep) とかなりの規模になり、自分が過去に行なったRBPDF(TCPDFからの PHP => Ruby移植。Redmine のPDF出力で使用されている gem です。)が約15 Ksで移植作業に約2年かかったことから、86.3 Ks/(15 Ks/2年)=11年必要となり、仮に完成したとしても時代遅れになります。

そのため、自動で Python のコードを Ruby に変換するツールを探したところ、5つのトランスレータが見つかりました。

  • https://bitbucket.org/snej/py2rb/src/tip/py2.rb
    • 正規表現でパースしてRubyに置換する。精度甘めのため、小規模ソースコード量ならよいが、大規模だと修正が多すぎるため断念
  • Grumpy(Go running Python)
    • Goに変換して実行。実行(非移植)が目的のためメソッドがインライン展開されるため、機械に優しく人に優しくなコードになるため断念
  • py2js.py
    • Python AST(Abstract Syntax Tree: 抽象構文木)ベースでJavaScriptに正確な変換をする、テストコードの仕組みも充実しておりかなりよい
  • py2cpp
    • Python ASTベースでC++に変換、結構変換してくれる
  • HOLY
    • Python ASTベースでRubyに変換し、今回の目的に合致したのですが、POCが目的であり実装範囲が狭くそのままでは使えない。

ここで、py2js.py をベースに Python から Ruby に変換するツールをまず開発すればいいのではという結論になりました。

py2rb.py

というわけで、 py2rb.py という Python ASTベースで Ruby に変換するツールを作っています。

github.com

  • Python ASTベースで1行単位にRubyへ翻訳
    1. Python => Ruby (メソッド,クラス,変数) : 大分動作
    2. Python標準ライブラリ=>Ruby標準ライブラリ : (Chainer で使われている 73 モジュール のうち 8モジュールを仮実装)
  • ドメインが同じライブラリ間でPython=>Rubyに置き換え
    1. numpy => Numo::NArray (Chainer で使われている 116 メソッド のうち 8メソッドを仮実装)
    2. unittest => test::unit (Chainer で使われている 33 メソッド のうち 4メソッドを仮実装)

このように単純なソースコード変換だけでなく、下回りのライブラリのドメインが同じ場合、それに置き換えることで品質をある程度確保した移植を行うことを考えています。

主な特徴として、下記を備えています。

  • Python 3.5 => Ruby 2.4 変換
  • クラス継承
  • モジュール呼び出し(import => require)
  • PythonRuby のメソッドのキーワード引数差異吸収

py2rb.py を用いた移植作業の流れ下記を想定しており、このうち 2, 3の作業を支援できることを想定しています。

  1. 移植対象を決定 (移植可能か見極め)
  2. プロダクションコードを1行単位で移植
  3. テストコードを1行単位で移植 (テストコードが無いツールの移植はお勧めしません)
  4. テストをパスするようプロダクションコード修正 (機能が動くことを担保するために必要な作業)
  5. 必要に応じてリファクタリング (コードのメンテナンス性向上や、性能を確保するために必要な作業)

py2rb.py 開発方針として下記を考えています。

  • あくまで移植の支援ツール と割り切る (PythonRuby で言語思想の違いからどうしても非互換部分が避けられないため)
  • 移植対象が使用していない機能は保留
  • ライブラリ部分の変換定義をYAMLで指定 (変換ツール内部に手を入れなくても、ライブラリ部の変換定義を追加可能にするため)
  • Rubyの黒魔術でPythonとの互換性を向上 (互換性確保のため py-builtins.rb を用意)

これにより、私以外が py2rb.py を用いて Python のライブラリ・ツールをRuby に移植したいと考えた場合でも、ライブラリ変換定義のYAML定義を追加する作業を行い、上記 1, 4, 5 の作業行うことで移植作業が可能になると考えています。

実行方法

以下のように変換対象の filename と、filename から import 指定されているモジュール名を指定します。 (モジュールとして指定したソースコードは意図せずファイルを上書きする事を避けるため、変換は行いません。モジュールを個別に filename に指定して変換することを想定しています。)
※ ここでモジュール名の指定漏れがある場合、変換精度が落ちます。

$ ./py2rb.py
Usage: py2rb.py [options] filename [module filename [module filename [..]]]

実際に変換すると下記のようになります。

$ cat chainer/chainer/variable.py

def _check_grad_type(func, x, gx):
    def make_message(message):
        if func:
            detail = 'Function `{0}` ({1}) has a bug.\n'.format(
                type(func).__name__, func.label)
            stack = func.stack
            if stack:
                detail += 'Stacktrace of the function is below:\n'
                for line in traceback.format_list(func._stack):
                    detail += line
            detail += ''' (略)'''.format(type(func).__name__, func.label)
        else:
            detail = ''
        detail += message
        return detail

$./py2rb.py --include-require chainer/chainer/variable.py chainer/chainer/utils/argument.py

def _check_grad_type(func, x, gx)
    make_message = lambda do |message|
        if is_bool(func)
            detail = "Function `{0}` ({1}) has a bug.\n".format(type(func).__name__, func.label)
            stack = func.stack
            if is_bool(stack)
                detail += "Stacktrace of the function is below:\n"
                for line in (func._stack).to_a
                    detail += line
                end
            end
            detail += "(略)".format(type(func).__name__, func.label)
        else
            detail = ""
        end
        detail += message
        return detail
    end
  • pythonクロージャー (def 内 def) をlambdaに変換することで、 def make_message の外部スコープのfuncを参照可能にしています。
  • is_bool()を用意し、Pythonとの差 (Rubyでは 0, '', [], {} は True、nil(None), false は Falseだが、Python は全て False)を吸収します。

※ まだ変換定義の実装が不足しているため、未変換で Python のコードがそのままの部分が残っている例になります。

また、テストケースとしてサンプル変換を一括実行する形式でテストを行なっています。

./run_tests.py -a

およそ 200個のテストケースを用意して変換動作を確認しています。

テストケース期待値は、

  • python のコードがrubyのコードに変換される (変換後のrubyのコードを期待値として持つ)事

ではなく

  • pythonのコードの実行結果と変換したrubyのコードの実行結果が同一になること

で、意図した変換になることを確認しています。

PythonRubyの差の影響

Python の関数は Ruby と異なりObject なのですが、変換が難しい例として、全く同じに見える foo() が実は中身によって解釈が変わる場合があります。

  1. foo() : fooが関数の場合、メソッド実行
  2. foo() : fooがクラスの場合、Rubyのnew()
  3. foo() : fooがインスタンスの場合、Rubyのfoo.call()

これは正規表現では判断は無理でASTなら判断可能です。ただし、変数に代入されるともはやASTでも判断は無理です。
なので、変換可能な範囲で変換する形に割り切っています。

他に、py2rb.py で割り切った点として

  • 多重継承は未サポート: これは根本的に無理なので、移植対象選択時、多重継承の有無を事前確認することを想定。
  • クラス内クラス
  • 変数のスコープ範囲の違い
  • デコレータ : 可能な範囲のみ。

があります。そもそも、

  • Python はストリーム : メソッド定義時にコンテキストを保持
  • Ruby はオブジェクト(型) : クラス内ではコンテキスト非保持

という差があるのでなかなか難しいものがあります。

py2rb.py で気をつけた点として

  • 動く事: 動かないとどこが問題なのか探す(デバッグ)する必要があり、不要なロスの時間は避けたい。(互換性を保つための余分な定義は不要と判断後に削除すればよい。)
  • (移植が目的なので)ソースコードをなるべく綺麗な形で変換する事。
  • 非互換を吸収する為、ソースコードが汚くなりそうな場合はPython&Ruby互換用ライブラリとして実装。(大半が互換の範囲なので、不要と判断したら using を外せばよい。)
Ruby機械学習を行うためのまとめ
  1. 既存のgemをなんとかすること
  2. Rubyのための仕組みをつくっていくこと
    • PythonからRubyへ変換ツール(py2rb.py)を用意
  3. 巨人(Python)の肩に乗ること
    • Pythonのツールを移植する事でPythonでの知見を活用する
今後の予定
  • ドックフーディングとしてChainerの移植にTry.
  • 標準ライブラリ/unittest/numpy のYAML変換定義の実装完了(Chainerの対象範囲)
  • RubyDeep Learning(本当にやりたい事)

という形で締めくくったのですが、質疑応答で Red Data Toolのプロジェクトで、すでにChainerの移植作業が始まっているという情報を頂きました。

私の手法は Python のコードの1行単位の移植になるため、Rubyらしいコードではなく Python ぽいコードになるため、最初から Ruby らしく書いた方がいいのではないかという指摘を頂きました。

また、手で作業を行なった場合、11年と見積もった作業も、一人ではなく複数人でやれば現実的な時間になるのではというお話も頂きました。
今の作業を保留して、そちらに Join すべきか、今の作業そのままやるべきか、まだ決めかねていますが、同じ目的の人がいるんだなと心強くなりました。

第6回shinagawa.redmine勉強会参加者アンケート結果と『Redmine超入門』を用いた Redmineインストール(Bitnami)と手動インストールの場合のはまりどころ #47redmine

先週末(2/15)は大雪でお足下の悪い中、約7割の方に第6回shinagawa.redmine勉強会に参加頂きありがとうございました。

参加者アンケート結果

勉強会の冒頭に発表した参加者アンケートの結果(48名分)です。 (※先日の中間アンケート結果の続きになります。)

アイスブレイクの位置づけで勉強会に馴染んで頂ける事を意図していましたが、好評だったので次回も継続したいと思います。

f:id:ju-na:20140221212344p:plain

未使用、最近使い始めた方がそれなりにおられました。

f:id:ju-na:20140221212352p:plain

中間結果より、Redmine 2.4 へアップグレードしている方が増えましたね。
ちなみにすでに Redmine 2.5のブランチが切られたので、来月には Redmine 2.5.0 が出ると思われます。
Redmine 3.0 ではついに Rails 4対応です。つまりRuby 1.8.x は未サポートになる予定です。また、プラグインの対応待ちの状況が、再び発生しそうです。
うーん、アンケートに使っている Ruby 版数入れておけばよかったと今更ながら思います。

f:id:ju-na:20140221212358p:plain

プラグインは、導入数がなだらかになりました。

f:id:ju-na:20140221212402p:plain

Subversion 使用者が中間結果より増えました。 Mercurial がなくなり CVS な人が増えました。ちょっとびっくり。

お勧め or よく利用しているプラグイン

となりました。ニコカレ強いですね。

アンケート結果は以上になりますが、懇親会で、shinagawa.redmine勉強会への参加回数もアンケート項目に入れて欲しいとフィードバックを頂きました。
やはりご意見頂くと嬉しいですね。

Redmine超入門』を用いた Redmineインストール(Bitnami)と手動インストールの場合のはまりどころ

『Redmine超入門』に記載のある、Redmine 環境構築の二つの例、「 BitNami を使ったインストール例」と「手動インストール」の両方の場合について実演し、本の記載通りに行う事で問題無くインストールできる点と、そこを外れた事をしようとした場合や、環境要因でのはまりどころの紹介になります。

デモをするために調べて気づいたのが BitNami は、非インターネット環境でも問題なしなのでデモ中にVM環境がインターネットに繋がっていなくても、全然問題無ありませんでした。

デモ環境

BitNami を用いたインストール

BitNami 2.4(Ruby 1.9.3 ※専用パスにインストールされるのでCentOS標準のRuby 1.8.7に影響を与えない。)
下記を実行すればOK
./bitnami-redmine-2.4.3-0-linux-x64-installer.run

こちらは日本語メニューが文字化けするので英語メニューで進める点にさえ気をつければ、5分程度で難なく終了。

手動インストールすると?

  1. OS設定 : SELinux 無効、Firewall 設定
  2. Ruby 2.0 コンパイル準備 :
  3. Ruby 2.0 コンパイル : CentOS 5環境などでは厳しい
  4. Bundler インストール :
  5. GEMインストール(bundle install) : 要ネット接続
  6. 接続セッションファイル作成 :
  7. DB パッケージ(MySQL)インストール :
  8. DB 設定 :
  9. DB 初期化(rake db migrate):
  10. Redmine デフォルトデータ登録 :

のような流れのデモ(の予定でした。)

ポイント

BitNami 非インターネット環境でも問題なし
基本的にはまらない。(Ruby1.9.3 & Apache同梱)
逆にいじるのに知識が必要。
他のWebアプリをインストール済みだと(port変更等)共存させるのに知識が必要
手動インストール インターネット要接続(非接続だと厳しい)
ネット上のノウハウが使える。
細かなセッティングが可能

全体の流れはこのような形で実施しましたが、VirtualBox上の仮想マシンのネットワーク設定がうまく調整できていなかっため、DB パッケージ(MySQL)インストールあたりまでのデモになります。
基本的にデモを通じた説明なので動画を参照頂くと雰囲気が伝わると思います。

CentOS 6での Ruby 2.0.0 rpm 作成方法

CentOS 6.5(x86_64)で、ruby 2.0.0 rpm を作成したので、そのメモ。

前回のリベンジ になります。

今回は、ALMiniumでも使用している ruby-2.0.0-rpm の spec ファイル を使用。

RPMビルド環境準備

$ su
# yum -y install gcc
# yum -y install rpm-build
# yum -y install gdbm-devel
# yum -y install libffi-devel
# yum -y install tk-devel
# yum -y install byacc

Ruby2.0.0 をインストールする下準備

ruby のビルドは、ビルド環境にある範囲でライブラリなどをビルドするので
パッケージが不足していると、ライブラリが欠けた ruby rpm パッケージが出来てしまうのであらかじめ以下を全部インストールしておく。

# yum -y install gcc-c++
# yum -y install zlib-devel
# yum -y install httpd-devel
# yum -y install openssl-devel
# yum -y install curl-devel
# yum -y install ncurses-devel
# yum -y install gdbm-devel
# yum -y install readline-devel
# yum -y install sqlite-devel
# yum -y install ImageMagick ImageMagick-devel

libyaml-devel もインストールしないといけないけど、リポジトリにパッケージが無い。
EPEL には存在するので、Yum リポジトリに EPEL を追加する。

# wget http://ftp.iij.ad.jp/pub/linux/fedora/epel/RPM-GPG-KEY-EPEL-6
# rpm --import RPM-GPG-KEY-EPEL-6
# rm -f RPM-GPG-KEY-EPEL-6
# vi /etc/yum.repos.d/epel.repo

epel.repo には次の内容を記述する。

[epel]
name=EPEL RPM Repository for Red Hat Enterprise Linux
baseurl=http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/$basearch/
gpgcheck=1
enabled=0

あとは libyaml-devel をインストール

# yum --enablerepo=epel install libyaml-devel

一般ユーザーで rpm をビルド開始

# exit
$ mkdir -p ~/rpmbuild/{SOURCES,SPECS}
$ cd ~/rpmbuild/SOURCES/
$ wget http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz
$ cd ~/rpmbuild/SPECS/
$ wget https://raw.github.com/hansode/ruby-2.0.0-rpm/master/ruby200.spec
$ rpmbuild -bb ruby200.spec

自分の環境では約5分程ビルドが走って最後に下記ファイルが作成されれば成功。

書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-2.0.0p353-2.el6.x86_64.rpm

インストール。

$ cd ~/rpmbuild/RPMS/x86_64/
$ su
パスワード:
# rpm -ivh ruby-2.0.0p353-2.el6.x86_64.rpm
# ruby -v
ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux]

無事 Ruby 2.0 rpm 作成完了。
この後、Redmine や RMagick も問題無く動きました。

# gem env
RubyGems Environment:
- RUBYGEMS VERSION: 2.0.14
- RUBY VERSION: 2.0.0 (2013-11-22 patchlevel 353) [x86_64-linux]
- INSTALLATION DIRECTORY: /usr/lib64/ruby/gems/2.0.0
- RUBY EXECUTABLE: /usr/bin/ruby
- EXECUTABLE DIRECTORY: /usr/bin
- RUBYGEMS PLATFORMS:
- ruby
- x86_64-linux
- GEM PATHS:
- /usr/lib64/ruby/gems/2.0.0
- /root/.gem/ruby/2.0.0
- GEM CONFIGURATION:
- :update_sources => true
- :verbose => true
- :backtrace => false
- :bulk_threshold => 1000
- REMOTE SOURCES:
- https://rubygems.org/

とりあえず問題なさそう。今回はここまで。

CentOS 6での Ruby 1.9.3 rpm 作成方法 (追記あり)

CentOS 6.5(x86_64)で、fedora プロジェクトの srpm から ruby 1.9.3 rpm を作成したので、そのメモ。

個人的に rpm で管理することが多いので、そのメモになります。

fedora プロジェクトの 1.9.3 srpm を入手

fedora 17, 18 で ruby 1.9.3 が採用されているのでその srpm を使用します。(※ fedora 19, 20 では ruby 2.0.0が採用されていますが、こっちではビルドに失敗。)
最新のパッチが適用された rpm は、 updates 配下から入手します。

$ wget http://ftp.iij.ad.jp/pub/linux/fedora/updates/18/SRPMS/ruby-1.9.3.484-32.fc18.src.rpm

なお、fedora 18は、2014年1月14日にサポート期限が切れているので、そのうち mirror ftp サイトから updates ディレクトリが消えます。その場合は、残ってる mirror ftp サイトから入手すればOK。

RPMビルド環境準備

なお、 CentOS6 に Ruby1.9.3 をインストールする手順 - present を参考にRPMビルド環境を整えました。

なお、srpm からのリビルドなので checkinstall は不要。

$ su
# yum -y install gcc
# yum -y install rpm-build
# yum -y install gdbm-devel
# yum -y install libffi-devel
# yum -y install tk-devel

Ruby1.9.3 をインストールする下準備

ruby のビルドは、ビルド環境にある範囲でライブラリなどをビルドするので
パッケージが不足していると、ライブラリが欠けた ruby rpm パッケージが出来てしまうので
あらかじめ以下を全部インストールしておく。

# yum -y install gcc-c++
# yum -y install zlib-devel
# yum -y install httpd-devel
# yum -y install openssl-devel
# yum -y install curl-devel
# yum -y install ncurses-devel
# yum -y install gdbm-devel
# yum -y install readline-devel
# yum -y install sqlite-devel
# yum -y install ImageMagick ImageMagick-devel

libyaml-devel もインストールしないといけないけど、リポジトリにパッケージが無い。
EPEL には存在するので、Yum リポジトリに EPEL を追加する。

# wget http://ftp.iij.ad.jp/pub/linux/fedora/epel/RPM-GPG-KEY-EPEL-6
# rpm --import RPM-GPG-KEY-EPEL-6
# rm -f RPM-GPG-KEY-EPEL-6
# vi /etc/yum.repos.d/epel.repo

epel.repo には次の内容を記述する。

[epel]
name=EPEL RPM Repository for Red Hat Enterprise Linux
baseurl=http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/$basearch/
gpgcheck=1
enabled=0

あとは libyaml-devel をインストール

# yum --enablerepo=epel install libyaml-devel

なお、ruby-1.9.3.484-32.fc18.src.rpm を使用してビルドすると、下記バグで、2014/1/1 以降、IMAPTest がエラーするので、それ以前の日付に変更にしておく必要があります。

# date -s "Sat Dec 21 00:00:00 JST 2013"

Bug #9341: IMAPTest#test_imaps_with_ca_file test failed after 2014-01-01. - ruby-trunk - Ruby Issue Tracking System

一般ユーザーで rpm をビルド開始

root でビルドするとシステム側をいろいろいじる可能性があるので、一般ユーザー権限に戻って rpm をビルドします。

ここでそのままビルドしようとすると、「インストール済み(ただし未伸張)ファイルが見つかりました」となりビルドに失敗します。

「インストール済み(ただし未伸張)ファイルが見つかりました」の対処 - bokuju とか tabe1hands の日記

事前にチェックファイルの定義を確認します。

$ rpmbuild --showrc | grep __check
-14: __check_files %{_rpmconfigdir}/check-files %{buildroot}

チェックファイルが定義されていたので、マクロを消してビルド実行。
※ ビルドは、 ~rpmbuild 配下で実行されます。

$ rpmbuild --rebuild fedora18/ruby-1.9.3.484-32.fc18.src.rpm --define="__check_files %{nil}"

ビルドが走って最後に下記ファイルが作成されれば成功。

書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-1.9.3.484-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-devel-1.9.3.484-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-libs-1.9.3.484-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/rubygems-1.8.23-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/rubygems-devel-1.8.23-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/rubygem-rake-0.9.2.2-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/ruby-irb-1.9.3.484-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/rubygem-rdoc-3.9.5-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-doc-1.9.3.484-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/rubygem-bigdecimal-1.1.0-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/rubygem-io-console-0.3-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/rubygem-json-1.5.5-32.el6.x86_64.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/noarch/rubygem-minitest-2.5.1-32.el6.noarch.rpm
書き込み完了: /home/naitoh/rpmbuild/RPMS/x86_64/ruby-tcltk-1.9.3.484-32.el6.x86_64.rpm

$ cd rpmbuild/RPMS/
$ su

# rpm -ivh x86_64/ruby* noarch/ruby*

準備中... ########################################### [100%]
1:ruby-libs ########################################### [ 14%]
2:rubygem-bigdecimal ########################################### [ 29%]
3:rubygem-io-console ########################################### [ 43%]
4:rubygems ########################################### [ 57%]
5:ruby ########################################### [ 71%]
6:ruby-irb ########################################### [ 86%]
7:rubygem-rdoc ########################################### [100%]

# ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]

無事、 rubyrpm でインストールできました。

※ ビルドが終了したので日付を戻しておいてください。

2014.02.09 追記

この後、Redmine をインストールしようとすると

# rake generate_secret_token
rake aborted!
cannot load such file -- sqlite3/sqlite3_native

と出て動作しない。

# gem env
RubyGems Environment:
- RUBYGEMS VERSION: 1.8.23
- RUBY VERSION: 1.9.3 (2013-11-22 patchlevel 484) [x86_64-linux]
- INSTALLATION DIRECTORY: /usr/local/share/gems
- RUBY EXECUTABLE: /usr/bin/ruby
- EXECUTABLE DIRECTORY: /usr/local/bin
- RUBYGEMS PLATFORMS:
- ruby
- x86_64-linux
- GEM PATHS:
- /usr/local/share/gems
- /root/.gem/ruby/1.9.1
- /usr/share/gems

なので、他の環境の gem のインストールディレクトリ配下を見比べてみて

# cp /usr/local/share/gems/gems/sqlite3-1.3.8/ext/sqlite3/sqlite3_native.so /usr/local/share/gems/gems/sqlite3-1.3.8/lib/sqlite3/

と所定の位置にsqlite3_native.soをコピーすると動作した。

# gem install sqlite3 -V

で調べてみると、下記に作成されていた。

/usr/local/lib64/gems/exts/sqlite3-1.3.8/lib/sqlite3/sqlite3_native.so

どうやら、fedora18ベースでrpmビルドしたのはこんな感じになってた模様。

パス 用途 備考
1 /usr/share/gems/ RPM で入れたgem用
2 /usr/local/share/gems/ gem install(Bundler) で入れたgem用
3 /usr/local/lib64/gems/ext/ native extension でビルドされた *.so ライブラリ ここの位置がダメ

rmagick をインストールすると同様に 3 に RMagick2.so が作成されて同じ現象に。毎回手動コピーはやってられないから、ダメですね。

正直 1 もいらないんだけど、OSパッケージ(RPM)管理のgemとBundler管理の gemとで分ける必要はあるんですよね。
2を3と同じパスにできればいいんだけど、やり方がよくわからないので、fedora18ベースの作成方法は断念。 fedora20ベースになると思われる RHEL7/CentOS7 では問題が起きない事を期待。

第6回shinagawa.redmine勉強会のご紹介と中間アンケート結果 #47redmine

2014/02/15(土) に第6回shinagawa.redmine勉強会を開催します。

自分も一部執筆を担当させて頂いた Redmine超入門 の発売を記念して、初めてRedmine を使い始める人を意識した勉強会にしたいと考えています。

今回の自分の発表は、Redmine の導入部分でつまづく人が多いので、実際にRedmine をサーバーにインストールする実演を行い、Redmine のインストール作業がどんなものか見て頂こうと考えています。

なお、今回は募集時に Redmine に関するアンケートを行っています。

33名分の中間集計ですが、どのようなRedmine利用者が勉強会に参加されようとしているか参考にしてみてください。

f:id:ju-na:20140202174054p:plain

Redmine 2.3/2.4 を利用している人が大半ですが、最新のRedmine 2.4への移行が始まっている感じです。
バージョンアップはプラグインが対応しているかでつまづくポイントなので、
Redmine 1.4 止まりの人が多少おられますが、この辺りも一つのハードルと思われます。
(※複数回答なので、古い環境も平行して使われている人もいるようです。)

f:id:ju-na:20140202162038p:plain

プラグインの利用数は、3〜6個ぐらいが多いみたいです。10個以上使用している人は少ないんですね。
ちなみに、自分は仕事では10個以上使っているのでバージョンアップ時は各プラグインの動作確認が非常に大変です…。

f:id:ju-na:20140202162047p:plain

バージョン管理ツールは、Subversion だけでなく Git を使っている方も結構おられますね。
Mercurial な方も1名いらっしゃいます。

アンケート結果は以上になります。
最終的なアンケート結果は、勉強会の場で公開することになると思いますが、
興味がある方は、ぜひ勉強会に足を運んでみてください。

懇親会も募集していますので、ぜひ!