heartbeatで仮想IPを冗長化
参考
hnakamur’s blog: CentOSでheartbeatでVIP引き継ぎ
heartbeatによるクラスタシステムへのリソースの追加と監視の仕組み (1/3):EnterpriseZine(エンタープライズジン)
heartbeatのインストール
yum install heartbeat heartbeat-pils heartbeat-stonith
heartbeatの設定
- 基本設定
/etc/ha.d/ha.cf
use_logd yes keepalive 500ms deadtime 10 warntime 5 initdead 60 ucast eth0 192.168.24.122 auto_failback off #auto_failback on node fedora-a node fedora-c uuidfrom nodename
-
- fedora-c側の設定
ucast eth0 192.168.24.111
- リソースに仮想IPアドレスを設定
/etc/ha.d/haresources
fedora-a IPaddr::192.168.24.130/24
- 認証の設定
/etc/ha.d/authkeys
auth 1 1 crc
-
- 権限を変える
chmod 600 /etc/ha.d/authkeys
- IPアドレスの名前解決
/etc/hosts
192.168.24.111 fedora-a 192.168.24.122 fedora-c
- 起動と停止コマンド
service heartbeat start service heartbeat stop
memcached1.4.5に対応したrepcaheをインストールしてみる
参考
- ここで知った。
repcachedのmemcached 1.4対応 - Atzy->getLog()
- 64bit版だとエラーがでる。
memcached.c:1074:16: error: dereferencing type-punned pointer will break strict-aliasing rules
"-Wstrict-aliasing"で抑止する。
フリーソフトウェア徹底活用講座(12)
Fedora14にインストール
Gitとコンパイルツールをインストール
yum install git yum install libevent-devel yum install autoconf automake sysconftool m4
memcachedのインストール
- memcachedのクローン作成
cd /usr/local/src git clone https://github.com/mdounin/memcached.git
- repcachedのブランチを引っ張ってくる
cd memcached git pull origin repcached
- コンパイルしてインストール
export CFLAGS=-Wstrict-aliasing ./configure --enable-replication --enable-64bit make make install mkdir -p /var/run/memcached chown -R root:root /var/run/memcached
- スクリプトの一部をインストール
cp -p scripts/memcached-tool /usr/local/bin/.
memcachedのサービス設定
※ 前のコピー Repcacheを調べてみる - sadaharu28の日記
- 設定ファイル作成
/etc/sysconfig/memcached
PORT=11211 USER=root MAXCONN=1024 CACHESIZE=640 OPTIONS= REPHOST=リモート側のノード
/etc/init.d/repcache
#! /bin/sh # # chkconfig: - 55 45 # description: The memcached daemon is a network memory cache service. # processname: memcached # config: /etc/sysconfig/memcached # pidfile: /var/run/memcached/memcached.pid # Standard LSB functions #. /lib/lsb/init-functions # Source function library. . /etc/init.d/functions #EXEC=/opt/memcached-1.2.8-repcached-2.2/bin/memcached EXEC=/usr/local/bin/memcached REPHOST=localhost PORT=11211 USER=memcached MAXCONN=1024 CACHESIZE=64 OPTIONS="" if [ -f /etc/sysconfig/memcached ];then . /etc/sysconfig/memcached fi # Check that networking is up. . /etc/sysconfig/network if [ "$NETWORKING" = "no" ] then exit 0 fi RETVAL=0 prog="repcache(memcached)" pidfile=${PIDFILE-/var/run/memcached/memcached.pid} lockfile=${LOCKFILE-/var/lock/subsys/memcached} start () { echo -n $"Starting $prog: " # Ensure that /var/run/memcached has proper permissions if [ "`stat -c %U /var/run/memcached`" != "$USER" ]; then chown $USER /var/run/memcached fi daemon --pidfile ${pidfile} $EXEC -d -x $REPHOST -p $PORT -u $USER -m $CACHESIZE -c $MAXCONN -P ${pidfile} $OPTIONS RETVAL=$? echo [ $RETVAL -eq 0 ] && touch ${lockfile} } stop () { echo -n $"Stopping $prog: " killproc -p ${pidfile} $EXEC RETVAL=$? echo if [ $RETVAL -eq 0 ] ; then rm -f ${lockfile} ${pidfile} fi } restart () { stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p ${pidfile} $prog RETVAL=$? ;; restart|reload|force-reload) restart ;; condrestart|try-restart) [ -f ${lockfile} ] && restart || : ;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|try-restart}" RETVAL=2 ;; esac exit $RETVAL
- スクリプトに実行権限付与
chmod +x /etc/init.d/repcache
- サービス登録&自動起動の設定
chkconfig --add repcache chkconfig repcache on chkconfig --list | grep repcache
- サービスとして起動&停止
service repcache start service repcache stop
cache_fuとcache_fu_find_hookを使ってみる
cache_fuとcache_fu_find_hookのインストール
cache_fuのインストール
ruby script/plugin install git://github.com/defunkt/cache_fu.git
エラーになるので・・・
Initialized empty Git repository in C:/rails2/app2/vendor/plugins/cache_fu/.git/ remote: Counting objects: 39, done. remote: Compressing objects: 100% (37/37), done. remote: Total 39 (delta 3), reused 23 (delta 0) Unpacking objects: 100% (39/39), done. From git://github.com/defunkt/cache_fu * branch HEAD -> FETCH_HEAD ** Checking for memcached in path... script/plugin: No such file or directory - which memcached Plugin not found: ["git://github.com/defunkt/cache_fu.git"]
vendor/plugins/cache_fu/install.rb の以下をコメントアウト
puts "** Checking for memcached in path..." if `which memcached`.strip.empty? $errors += 1 puts "!! Couldn't find memcached in your path. Are you sure you installed it? !!" puts "!! Check the README for help. You can't use acts_as_cached without it. !!" end
もう一度インストール
ruby vendor/plugins/cache_fu/install.rb
** Checking for memcache-client gem... ** Trying to copy memcached.yml.default to ./config/memcached.yml... ** Trying to copy memcached_ctl.default to ./script/memcached_ctl... ** acts_as_cached installed with no errors. Please edit the memcached.yml file t o your liking. ** Now would be a good time to check out the README. Enjoy your day.
cache_fu_find_hookのインストール
ruby script/plugin install git://github.com/morimori/cache_fu_find_hook.git
config/environment.rbを修正。
Rails::Initializer.run do |config| ... config.plugins = [:cache_fu, :cache_fu_find_hook, :all] ... end
使ってみる
Modelにこれだけを定義
models/user.rb
class User < ActiveRecord::Base acts_as_cached after_save :reset_cache after_destroy :expire_cache end
有効期限やfinderを指定することも可能
acts_as_cached :ttl => 1.minutes, :find_by => "col1"
※ finder を変更した場合は、:reset_cache, :expire_cache等は使えない。
Observerに定義できるか確認してみた
models/sqlite_observer.rb
class SqliteObserver < ActiveRecord::Observer observe :user acts_as_cached after_save :reset_cache after_destroy :expire_cache end
エラーになる・・・
C:/rails2/app2/vendor/plugins/cache_fu_find_hook/lib/cache_fu_find_hook.rb:20:in `acts_as_cached': undefined method `find' for class `Class' (NameError) from C:/rails2/app2/app/models/sqlite_observer.rb:4 from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.10/lib/active_support/dependencies.rb:406:in `load_without_new_constant_marking' ・・・
Observerに設定するのは無理っぽい
cache_fuのsessionについて
config/memcached.yml の sessions: を true にすると、セッション作成時にエラーになってしまう。
"1424" => Booting Mongrel => Rails 2.3.10 application starting on http://0.0.0.0:3002 => Call with -d to detach => Ctrl-C to shutdown server Wed Jan 12 01:10:08 +0900 2011: Read error: #<RuntimeError: #<ActionController::Session::MemCacheStore:0x2895468> unable to find server during initialization.> c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.10/lib/action_controller/session/mem_cache_store.rb:21:in `initialize' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.10/lib/action_controller/middleware_stack.rb:72:in `new' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.10/lib/action_controller/middleware_stack.rb:72:in `build' c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.10/lib/action_controller/middleware_stack.rb:116:in `build' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.10/lib/active_support/inflector.rb:364:in `inject' ・・・
環境の所為なのか、どうなのか分からないが、cache_fuのsessionは使えない模様。
ただ、config/memcached.yml の sessions: を false にして、別にセッションの保管先をmemcachedに指定することで、セッションをmemcachedに保管することは可能。
以下、確認した時の session_store.rb と cache_fu の設定
config/initializers/session_store.rb
config = YAML.load(ERB.new(IO.read(File.dirname(__FILE__) + "/../memcached.yml")).result) ActionController::Base.session = { :memcache_server => config["defaults"]["servers"], :expire_after => config["defaults"]["session_ttl"], :namespace => config["defaults"]["namespace"] + "-#{ENV['RAILS_ENV']}", :key => "_app2_session", :secret => "789db92e8a6162f2185368ab0a1e5d8f2761bb25ebfb88e0b3e7ae4fe803d42981d32d96ca544aeb178e840f00f477c83bd370ff7d11f3de3f232052b59c83ce" } ActionController::Base.session_store = :mem_cache_store
config/memcached.yml
defaults: ttl: 1800 readonly: false urlencode: false c_threshold: 10000 compression: true debug: false namespace: app2 sessions: false session_servers: false fragments: true memory: 640 servers: - 192.168.24.111:11211 - 192.168.24.122:11211 benchmarking: false raise_errors: false fast_hash: false fastest_hash: false session_ttl: 86400
Repcacheを調べてみる
Repcacheを調べるに至るまでの経緯
Railsにおけるデータの高速化及び可用性を高めたい!
ということで、ネットを彷徨っていたら memcachedという素晴らしいツールがあるということが分かった。
高速化については、memcachedでキャッシュサーバを構築し、セッションやDBのデータをそこで保管する方法がある。
しかし、memcached単体では単なるキャッシュサーバとしてしか機能せず、データの保全という意味では冗長化は望めない。
もともと冗長化のコンセプトはなさそうなので、仕方のないところ。
Webアプリ側で、冗長化を図ることは可能だが、アプリ側が意識しないところでデータが冗長化されているのが望ましい。
そして、なによりRails(Ruby)に、memcachedを冗長化させるオプションがない。
なお、PHPには"redundancy"というオプションがあるらしい。
ネットを彷徨っていると、memcachedにレプリケーション機能を実装しているrepcacheに出会った。
これを利用しない手はない。
http://lab.klab.org/wiki/Repcached
素晴らしいツールをありがとうございますm(_ _)m
環境
OSと使用パッケージなど
OS | Fedora14(x86_64)*1 |
memcached | 1.2.8 |
repcache | 2.2 |
レプリケーションの構成
node1:* | → | node2:11212 |
node1:11212 | ← | node2:* |
準備作業
chkconfig memcached off service memcached stop
- ファイアウォールを無効にする
service iptables stop chkconfig iptables off chkconfig ip6tables off
- SELinuxを無効にする。
vi /etc/sysconfig/selinux
# This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - SELinux is fully disabled. SELINUX=disabled # SELINUXTYPE= type of policy in use. Possible values are: # targeted - Only targeted network daemons are protected. # strict - Full SELinux protection. SELINUXTYPE=targeted
- とりあえずupdate
yum update -y
- 必要なパッケージをインストール
※ memcached-tool を使いたいので memcached もインストール
yum install -y memcached wget libevent-devel make gcc gcc-c++
- なんとなく再起動
reboot
Repcacheのインストール
wget http://downloads.sourceforge.net/repcached/memcached-1.2.8-repcached-2.2.tar.gz tar zxf memcached-1.2.8-repcached-2.2.tar.gz cd memcached-1.2.8-repcached-2.2 ./configure --enable-replication --prefix=/opt/memcached-1.2.8-repcached-2.2 make make install
- 起動テスト
-
- node1側
cd /opt/memcached-1.2.8-repcached-2.2/bin ./memcached -v -x node2 -u root
-
- node2側
cd /opt/memcached-1.2.8-repcached-2.2/bin ./memcached -v -x node1 -u root
config/initializers/session_store.rb
ActionController::Base.session = { :memcache_server => ["node1:11211", "node2:11211"], :expire_after => 10, :namespace => "app-#{ENV['RAILS_ENV']}", :key => '_app_session', :secret => '789db92e8a6162f2185368ab0a1e5d8f2761bb25ebfb88e0b3e7ae4fe803d42981d32d96ca544aeb178e840f00f477c83bd370ff7d11f3de3f232052b59c83ce' } ActionController::Base.session_store = :mem_cache_store
-
- Railsのアプリを適当に動かす
-
- node1側のキャッシュを確認
memcached-tool node1 dump
-
- node2側のキャッシュを確認
memcached-tool node2 dump
-
- 両nodeに同じ情報が書き込まれていたら成功
Dumping memcache contents
Number of buckets: 1
Number of items : 1
Dumping bucket 6 - 1 total items
add app-development:35863ec3d930ce3d21239e4de3e31293 0 1294514243 211
Repcache用のサービス作成
vi /etc/init.d/repcache
#! /bin/sh # # chkconfig: - 55 45 # description: The memcached daemon is a network memory cache service. # processname: memcached # config: /etc/sysconfig/memcached # pidfile: /var/run/memcached/memcached.pid # Standard LSB functions #. /lib/lsb/init-functions # Source function library. . /etc/init.d/functions EXEC=/opt/memcached-1.2.8-repcached-2.2/bin/memcached REPHOST=localhost PORT=11211 USER=memcached MAXCONN=1024 CACHESIZE=64 OPTIONS="" if [ -f /etc/sysconfig/memcached ];then . /etc/sysconfig/memcached fi # Check that networking is up. . /etc/sysconfig/network if [ "$NETWORKING" = "no" ] then exit 0 fi RETVAL=0 prog="repcache(memcached)" pidfile=${PIDFILE-/var/run/memcached/memcached.pid} lockfile=${LOCKFILE-/var/lock/subsys/memcached} start () { echo -n $"Starting $prog: " # Ensure that /var/run/memcached has proper permissions if [ "`stat -c %U /var/run/memcached`" != "$USER" ]; then chown $USER /var/run/memcached fi daemon --pidfile ${pidfile} $EXEC -d -x $REPHOST -p $PORT -u $USER -m $CACHESIZE -c $MAXCONN -P ${pidfile} $OPTIONS RETVAL=$? echo [ $RETVAL -eq 0 ] && touch ${lockfile} } stop () { echo -n $"Stopping $prog: " killproc -p ${pidfile} $EXEC RETVAL=$? echo if [ $RETVAL -eq 0 ] ; then rm -f ${lockfile} ${pidfile} fi } restart () { stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p ${pidfile} $prog RETVAL=$? ;; restart|reload|force-reload) restart ;; condrestart|try-restart) [ -f ${lockfile} ] && restart || : ;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|try-restart}" RETVAL=2 ;; esac exit $RETVAL
※/etc/init.d/memcachedをコピーして作った。
- スクリプトに実行権限付与
chmod +x /etc/init.d/repcache
- サービス登録&自動起動の設定
chkconfig --add repcache chkconfig repcache on chkconfig --list | grep repcache
- 設定ファイルにレプリケーション用のホスト登録
vi /etc/sysconfig/memcached
PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" OPTIONS="" REPHOST=node1
- サービスとして起動&停止
service repcache start service repcache stop
参考
http://lab.klab.org/wiki/Repcached
http://kapi.jp/kapi_blog/268
http://doruby.kbmj.com/daoka_tips/20100517/Rails_repcached_
自宅サーバー Fedora » OpenSSH の起動 | FedoraでLinuxサーバー構築
調べた結果
OS的に同一スペックのマシン同士であれば、何の支障もなくレプリケーションできる。
しかし少しでもスペックが異なるとレプリケーション出来なかった。
- 試した例と結果
node1 | node2 | 結果 | 内容 |
---|---|---|---|
CentOS 5.5 32bit | Fedora14 64bit | NG | node1からnode2にコピーした場合、node2が残りっぱなしになる。node2からnode1にコピーできない。 |
Fedora14 64bit(ノーマル) | Fedora14 64bit(Oracleインストール版)*2 | NG | node1からnode2にコピーした場合、node2が残りっぱなしになる。node2からnode1にコピーできない。 |
Fedora14 64bit(ノーマル) | Fedora14 64bit(ノーマル) | OK | 正常に動いた。 |
レプリケーションを有効にした場合、マルチスレッドが無効になるところが辛いが、Webアプリの高速化と可用性を向上する方法は確認できた。
とりあえず、Repcacheを導入する方向で進めよう。
Rails+SQL Serverの文字コード対策
環境
Ruby | 1.8.7p302 |
Rails | 2.3.10 |
DB | SQL Server |
クライアント(ブラウザ) | UTF-8 |
DB | CP932 |
$KCODEとWIN32OLE
config/environment.rb に次のような設定をしたが、更新時にエラーになる。
$KCODE = "u" require "win32ole" WIN32OLE.codepage=WIN32OLE::CP_UTF8
エラーメッセージのコピペ
ODBC::Error: 37000 (105) [Microsoft][SQL Native Client][SQL Server]Unclosed quotation mark after the character string '�E�010年ももぁE��ぐ終わめE)'.: INSERT INTO [kamis] ([naiyou], [created_at], [updated_at], [odai]) VALUES(N'大晦日も仕事(;ω;)', '2010-12-30 21:50:58.594', '2010-12-30 21:50:58.594', N'2010年ももうすぐ終わり')
参考:
http://yugui.jp/articles/572
SQL Serverを使う - azuki note
参考にしたページでは上手いこといくらしい。。。
自分の環境ではエラーになっちゃうのは何故?
Modelのコールバックを使う
Model の コールバックで変換を行うことでエラーにならないようになった。
class Kami < ActiveRecord::Base def after_find self.odai = Iconv.conv("UTF-8", "CP932", self.odai) self.naiyou = Iconv.conv("UTF-8", "CP932", self.naiyou) end def before_save self.odai = Iconv.conv("CP932", "UTF-8", self.odai) if self.odai_changed? self.naiyou = Iconv.conv("CP932", "UTF-8", self.naiyou) if self.naiyou_changed? end end
参考:
Ruby on Rails - Tips - かえるの尻尾
ActiveRecord::Callbacks
http://blog.mizzy.org/articles/2005/12/30/learningRails06
after_initializeに関して - カノ エ ノ メモ - cucumberグループ
http://jror4883.rubyforge.org/svn/trunk/activerecord/lib/active_record/callbacks.rb
RailsのPooling機能覚書
環境
Ruby | 1.8.7p302 |
Rails | 2.3.10 |
DB | SQL Server |
接続タイムアウトは指定できる?
何のことはない database.yml に wait_timeout を指定するだけで良かった。
因みに秒指定。
development: adapter: sqlserver mode: odbc dsn: mssql_development database: testdb_dev username: testuser password: ******** pool: 8 wait_timeout: 8 ★
だけど、これはRubyのバージョンが1.9未満の場合だけらしい。
activerecord-2.3.10/lib/active_record/connection_adapters/abstract/connection_pool.rb
# default 5 second timeout unless on ruby 1.9 @timeout = if RUBY_VERSION < '1.9' spec.config[:wait_timeout] || 5 end
Failoverに対応してる?
ネットを彷徨っていると、対応していない、というページがヒットした・・・
参考:
Rails(ActiveRecord)でデータベースへのコネクションプーリングをさせなくする - はまさき
(LVS + keepalivedだとダメ、ということみたい。)
それは困る!
ということで、とりあえず2.3.10のソースコードを調べてみた。
checkoutの際、新規接続も既存接続も verify! が呼ばれるようになっている。
activerecord-2.3.10/lib/active_record/connection_adapters/abstract/connection_pool.rb
def checkout_and_verify(c) c.verify! c.run_callbacks :checkout @checked_out << c c end
もう少し掘り下げて、SQL Serverのアダプタで、verify! が有効か調べてみた。
activerecord-2.3.10/lib/active_record/connection_adapters/abstract_adapter.rb
# Checks whether the connection to the database is still active (i.e. not stale). # This is done under the hood by calling <tt>active?</tt>. If the connection # is no longer active, then this method will reconnect to the database. def verify!(*ignored) reconnect! unless active? end
activerecord-sqlserver-adapter-2.3.13/lib/active_record/connection_adapters/sqlserver_adapter.rb
# CONNECTION MANAGEMENT ====================================# def active? connected = case @connection_options[:mode] when :dblib !@connection.closed? when :odbc true else :adonet true end return false if !connected raw_connection_do("SELECT 1") true rescue *lost_connection_exceptions false end def reconnect! disconnect! connect active? end
有効っぽいな・・・とりあえず安心。
(時間が出来たら実際に試してみよう。)
もっと詳しく調べている人がいた。
参考:
ActiveRecordのDBコネクションの接続切れと再接続について。reconnectオプションは危険だなーとかも - odeの開発メモ日記