2012年11月23日金曜日

逆引きKVM(Kernel-based Virtual Machine)

この記事はKVM(Kernel-based Virtual Machine)の逆引きTipsです。 CentOS 6.3のKVMを基にしています。


最小構成のCentOS 6.3 64bitにKVMをインストール

CentOS 6.3 64bitを最小構成でインストールする
SELinuxを切る
# setenforce 0
# vi /etc/sysconfig/selinux
---
SELINUX=disabled
---
Firewallを切る
# /etc/init.d/iptables stop
# chkconfig iptables off
ネットワークインターフェースをブリッジにする
# vi /etc/sysconfig/network-scripts/ifcfg-eth0
---
DEVICE=eth0
ONBOOT=yes
BRIDGE=br0
---
# vi /etc/sysconfig/network-scripts/ifcfg-br0
---
DEVICE=br0
ONBOOT=yes
TYPE=Bridge
IPADDR=192.168.1.2
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
---
# /etc/init.d/network restart
# ifconfig eth0 0.0.0.0 promisc up
kvmをyumで入れる
# yum -y groupinstall "Virtualization" "Virtualization Client" "Virtualization Platform" "Virtualization Tools" "X Window System"
virt-manager用に日本語fontを入れる
# yum -y install "vlgothic-p-fonts"
mDNS クライアントの作成失敗のエラー回避のためavahiを入れる
# yum -y install avahi
# /etc/init.d/messagebus restart
# /etc/init.d/avahi-daemon restart
# chkconfig messagebus on
# chkconfig avahi-daemon on
# /etc/init.d/libvirtd start
libvirtdの起動
# /etc/init.d/libvirtd start
# chkconfig libvirtd on

WindowsからGUI(virt-manager)でKVMを管理する

Linux付属の仮想マシンマネージャ(virt-manager)を利用します。
sshのX11 Forwadingという機能で、仮想マシンマネージャのX Windowをローカルに転送します。
手順
  • X11サーバをクライアントに入れます (xmingがお勧め)
  • X11サーバを起動します
  • sshクライアントで、X11のフォワーディングを有効にする
    • puttyならば「メニュー」→「接続」→「SSH」→「X11」→「X11フォワーディングを有効にする」をチェック
    • teratermならば「設定」→「SSH転送」→「リモートの(X)アプリケーションをローカルのXサーバに表示する」をチェックして、設定を保存。再度teratermを立ち上げる必要あり。
  • その状態で sshでKVMホストにログイン
  • ログインしたら「virt-manager」とコマンドを打てば、仮想マシンマネージャが起動します。
# virt-manager

MacからGUI(virt-manager)でKVMを管理する

Linux付属の仮想マシンマネージャ(virt-manager)を利用します。
sshのX11 Forwadingという機能で、仮想マシンマネージャのX Windowをローカルに転送します。
手順
KVMにssh。その際X11を飛ばすためにオプション -Xをつける
# ssh -X root@(KVMホスト)
virt-magagerを起動すると、Macに画面だけ飛んできます。
# virt-manager
注意
仮想マシンのコンソールにアクセスしたときに alt+ctrlが効かなくてマウスを出せなくなるので、
ユーザのホームディレクトリに .Xmodmapを作成して、下の設定する必要があります
# vi ~/.Xmodmap
---
clear Mod1
keycode 66 = Alt_L
keycode 69 = Alt_R
add Mod1 = Alt_L
add Mod1 = Alt_R
---

コンソールからvirshでKVMを管理する

コンソールでKVMを管理する場合、Linux付属のvirshコマンドを使います

起動中の仮想マシン一覧表示

# virsh list

全ての仮想マシン一覧表示

# virsh list --all

仮想マシンの情報

# virsh dominfo (仮想マシン名)

仮想マシンの起動

# virsh start (仮想マシン名)

仮想マシンの停止

# virsh shutdown (仮想マシン名)

仮想マシンの強制停止(電源OFF)

# virsh destroy (仮想マシン名)

仮想マシンの一時停止

# virsh suspend (仮想マシン名)

仮想マシンの一時停止の再開

# virsh resume (仮想マシン名)

仮想マシンの保存(メモリをファイルに書き出す)

# virsh save (仮想マシン名) (保存ファイル名)

保存した仮想マシンの復元(ファイルからメモリに戻す)

# virsh restore (保存ファイル名)

仮想マシンの構成変更(メモリ量変更など)

  1. 仮想マシン停止
  2. /etc/libvirt/qemu/(仮想マシン名).xml を変更
  3. 変更を反映
    # virsh define /etc/libvirt/qemu/(仮想マシン名).xml
  4. 起動

仮想マシンのコンソールログインする設定

以下の設定を入れる これをすると、ホストOSからvirsh consoleで接続できるようになる。
grubの設定ファイル変更(CentOS 5系&6系共に実施が必要)
# vi /etc/grub.conf
---
serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1 # この行を追加
terminal --timeout=5 serial console # この行を追加
title CentOS (2.6.18-128.4.1.el5)
       root (hd0,0)
       kernel /vmlinuz (中略) console=ttyS0,115200n8 # console以降を追加
       initrd /initrd-2.6.18-128.4.1.el5.img
---
コンソール端末を作成 (CentOS 5 系のみで必要)
# vi /etc/inittab
---
S0:12345:respawn:/sbin/agetty ttyS0 115200 # この行を追加
1:2345:respawn:/sbin/mingetty tty1
---
コンソールからrootでログインできるようにする。(CentOS 5 系のみで必要)
# vi /etc/securetty
---
ttyS0 #追記
---

仮想マシンのコンソールに接続

# virsh console (ドメイン名)
※ 抜ける場合はctrl + ]

仮想マシンを起動&そのままコンソール接続

# virsh start (ドメイン名) --console

KVMの運用

仮想マシンのバックアップ

基本的には、以下の二つのファイルをバックアップしておけばOK
  • 仮想マシン設定ファイル
    • /etc/libvirt/qemu/(仮想マシン名).xml
  • 仮想ディスクイメージファイル
    • 上記設定ファイルにイメージファイルの場所は書いてあります
    • 通常は /var/lib/libvirt/images/(仮想マシン名).img

仮想マシンのバックアップからのリストア

  • 仮想ディスクイメージファイルを適切な場所(設定ファイルあるパス)に置く
    • 通常は /var/lib/libvirt/images/(仮想マシン名).img
  • 仮想マシン設定ファイルを適切な場所に置く
    • 通常は /etc/libvirt/qemu/(仮想マシン名).xml
  • 以下のコマンドで仮想マシン設定ファイルの読み込み
# virsh define (仮想マシン設定ファイル)

バックアップスクリプト

rubyのバックアップスクリプト作ったので、もしよかったら使ってください→ コチラ

VMWareの仮想ディスクマシンをKVM用に変換

vmwareの仮想ディスクイメージ(vmdk)をkvmに変換するときは以下のコマンドを打つ。
qemu-img convert (vmdkファイル) -O raw (kvmイメージファイル)

KVMのトラブルシューティング(CentOS6系)

mDNS クライアントの作成に失敗

/var/log/libvirt/libvirtd.logのエラーメッセージ
2012-07-17 15:47:49.240+0000: 15548: info : libvirt version: 0.9.10,
package: 21.el6_3.1 (CentOS BuildSystem <[http://bugs.centos.org>,]
2012-07-03-16:15:49, c6b8.bsys.dev.centos.org)
2012-07-17 15:47:49.240+0000: 15548: error : virNetServerMDNSStart:460
: 内部エラー mDNS クライアントの作成に失敗しました: Daemon not running
対応
# yum -y install avahi
# /etc/init.d/messagebus restart
# /etc/init.d/avahi-daemon restart
# /etc/init.d/libvirtd restart
# chkconfig messagebus on
# chkconfig avahi-daemon on
# chkconfig libvirtd on
参考

virt-managerの日本語fontが表示されない

fontが足りていない
対応
# yum install "vlgothic-p-fonts"

適切なエミュレーターを見つけられない

/var/log/libvirt/libvirtd.logのエラー
2012-07-24 01:43:07.595+0000: 2126: error : qemuCapsExtractVersion:1566 : 内部エラー x86_64 の適切なエミュレーターを見つけられません
対応
# ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64
# /etc/init.d/libvirtd restart

KVMのカーネルモジュールが読み込まれない

KVM を利用できません。これは KVM パッケージがインストールされていない。また
は、KVM のカーネルモジュール (kvm.ko) が読み込まれていないことを意味します。
QEMU が使われるので動作が遅くなるでしょう。

warning: KVM is not available. This may mean the KVM package is not installed, or the KVM kernel modules are not loaded. Your virtual machines my perform poorly
一時的な対応はカーネルモジュールの読み込めばOK
# modprobe kvm
# modprobe kvm-intel
再起動後も設定を継続させる場合は、 /etc/rc.d/rc.sysinitに以下を記載
modprobe kvm
modprobe kvm-intel

仮想マシンがKVMの内部のネットワークから外に出れない。

仮想マインのインターフェースがプロミスきゃすになっていない
対応
# ifconfig eth0 0.0.0.0 promisc up

2012年11月20日火曜日

Rubyでバッチを書きやすくするフレームワーク作りました

私はよくRubyでバックアップやディレクトリ同期等のバッチスクリプトを書くのですが、
ログ出力とか設定ファイル読み込みとか勝手にやってくれるものがほしかったので、作りました。

コチラです→RBatch:Ruby-baseバッチスクリプトフレームワーク

MongoDB設定値全解説を書きました

githubにMongoDBの設定値の日本語解説がなかったので書きました。

コチラです→MongoDB設定値全解説

こちらは丸の内MongoDB勉強会で発表したものです。こちらもよろしく。

2012年10月20日土曜日

Ruby CGI + Oauth2 (facebook) Web Application Sample

facebookのOAuth2を使って、RubyのCGIウェブアプリケーションを認可するサンプルです。

Preparation

  • Ruby CGI environment
    • For example, Apache 2.2 and ruby 1.8.7
  • facebook application ID and secret ID

Sequence

 

Code

login.rb

#!/usr/local/bin/ruby
require "cgi"

client_id    = "xxxxxxxx"  # set your facebook application client_id
redirect_uri = CGI.escape("http://myapp.com/top.rb") # set your application url

#
# 2. Redirect
#
url = "https://graph.facebook.com/oauth/authorize?" +
  "response_type=code&" +
  "client_id=#{client_id}&" +
  "redirect_uri=#{redirect_uri}"
cgi = CGI.new
print cgi.header({ 'status' => 'REDIRECT', 'Location' => url })


top.rb

#!/usr/local/bin/ruby
require 'rubygems'
require 'json'
require 'cgi'

client_id     = "xxxxxx"  # set your facebook application client_id
client_secret = "xxxxxx"  # set your facebook application client_secret
redirect_uri  = CGI.escape("http://myapp.com/top.rb")  # set your application url


#
# 5. Access Token Request
#
code = CGI.new()["code"]
url = "/oauth/access_token?" +
  "client_id=#{client_id}&" +
  "client_secret=#{client_secret}&" +
  "redirect_uri=#{redirect_uri}&"+
  "code=#{code}"

# start ssl
require 'net/https'
https = Net::HTTP.new('graph.facebook.com',443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE

access_token=""
https.start {
  response = https.get(url)
  # perse string such as  "access_token=AAAsZCLvBgZDZD&expires=4992995"
  response.body.split("&").each do |param|
    access_token = param.split("=")[1] if param.split("=")[0] == "access_token"
  end
}

#
# 7. Request User Information Using Access Token
#
json=""
url = "/me?access_token=#{access_token}"
https.start {
  json = https.get(url).body
}

#
# 9. User Infomation Page
#
name = JSON.parse(json)["name"]
print "Content-Type: text/html\n\nHello #{name}\n<hr>\n#{json}\n"

2012年9月21日金曜日

KVM Disk Images Backup Script with "ruby-libvirt"

ruby-libvirtを使ってKVMのディスクイメージをcpやscpでバックアップするスクリプトです。

Test Enviroment
  • CentOS release 6.3 (Final)
  • ruby 1.9.3p194 (2012-04-20) [x86_64-linux]
  • ruby-libvirt (0.4.0)
Script

#
# KVM Disk Image Backup Script
#
#                fetaro@gmail.com
#
# usage : $ ruby kvmbackup.rb (domain-name)
#
# 1. Get Disk image paths from domain-xml
# 2. Susupend domain if running
# 3. Backup Disk image to BACKUP_DIR with BACKUP_CMD (cp or scp)
# 4. Resume domain if suspended
#
require 'libvirt'

BACKUP_CMD="scp"
#BACKUP_CMD="cp"
BACKUP_DIR="host:/path/to/backup/dir"
#BACKUP_DIR="/path/to/backup/dir"

if ARGV[0].nil?
  STDERR.puts "There is no argument"
  exit 1
end

domain=ARGV[0]
conn = Libvirt::open("qemu:///system")
dom = conn.lookup_domain_by_name(domain)

# get disk image path
img_paths = []
require "rexml/document"
xml = REXML::Document.new(dom.xml_desc)
xml.elements.to_a("domain/devices/disk").each do |disk|
  if disk.attributes["device"] == "disk"
    img_paths << disk.elements["source"].attributes["file"]
  end
end

return_code = 0
begin
  if dom.state[0] == 1 # domain is running
    puts "suspend #{domain}"
    dom.suspend
  end

  img_paths.each do |img_path|
    cmd = "#{BACKUP_CMD} #{img_path} #{BACKUP_DIR}"
    puts cmd
    system cmd
    if($? != 0) then
      STDERR.puts "fail to #{cmd}"
      return_code=1
    end
  end
rescue => e
  p e
  return_code = 1
ensure
  if dom.state[0] == 3 # domain is paused
    puts "resume #{domain}"
    dom.resume
  end
end

exit return_code

2012年7月8日日曜日

[Cloud Stack 3]Web管理画面にadmin/passwordでログインできない

Cloud Stack 3.0.2で公式マニュアル通りにセットアップを行い、いざWeb管理画面にログインしようとしたところ、ログインできない問題に遭遇。

マニュアル通りに、デフォルトのユーザ名「admin」とパスワード「password」でログインしようとするもNG。

MySQLのcloud.userテーブルを見ても、空になっててadminのユーザがいない模様。

/var/log/cloud/management/management-server.logには以下のようなエラーが。。。
2012-07-08 02:10:17,949 WARN  [cloud.server.ConfigurationServerImpl] (main:null) Failed to create default security group for default admin account due to
java.sql.SQLException: Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
2012-07-08 02:10:18,062 ERROR [cloud.servlet.CloudStartupServlet] (main:null) Exception starting management server
com.cloud.utils.exception.CloudRuntimeException: DB Exception on: org.apache.commons.dbcp.DelegatingPreparedStatement@7297e3a5
Caused by: java.sql.SQLException: Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)

いろいろ調べていると、公式の掲示板にあった sjuanluさんの投稿に解決策が書いてあった。

どうやら、MySQLの設定でBinaryログを設定していたのが悪かったようなので、「log-bin」の設定を消す必要があるらしい。

たすかりました!

2012年6月1日金曜日

LPIC304 合格体験記

LPIC304に受かりました! 体験を共有したいと思います。

勉強期間と点数

勉強期間は、合計すると丸10日ぐらいで、テストの結果は90%でした。

※)ただ、私は普段からkvmを使って仮想マシンの管理をしていたので、仮想化についてはそれなりに知識がありました。そして、クラスタについてはほとんど素人でした。その前提で、この勉強時間です。

感想

LPICの中では、問題が素直で簡単でした。少なくとも301のほうが細かいことを聞かれた気がします。

 また、かなりの時間を実機で勉強したのですが、やりすぎた感があり、もう少し手を抜いてもよかったかなあと思いました。

勉強法

以下の順序で勉強していきました。
  1. LPIの公式HPで試験の出題範囲と、各項目の重要度をチェック。
  2. 書店でLPIC304の問題集を買い、どんな問題が出題されるか確認。
  3. その中で、よく出題されるソフトウェアについては、実機で動かしながら勉強。それ以外のソフトウェアはネットなどを調べて座学。
  4. 問題集を一通り解けるようにする。
実機での勉強は、まず勉強がてらXenのマシンを作成し、その上に仮想マシンや仮想ネットワークを作って、その上でいろいろなソフトウェアを試してみるという方法をとりました。

実機で動かしてみたソフトウェアは以下のとおり。
  • Xen:インストールから仮想マシンの作成・複製・編集などかなりみっちり
  • KVM:普段から使っていたので、あまり勉強せず。
  • LVS、keepalive:実際にロードバランスさせてみた
  • HAProxy:実際にロードバランスさせてみた
  • Heartbeat、pacemaker:実際にクラスタを組んで、フェールオーバできることまで確認した
  • DRBD:実際にクラスタストレージを組んで、 フェールオーバできることまで確認した
特にpacemakerが難しくかなりハマりましたが、クラスタの基本知識がついたので、すごく勉強になりました。

以下のソフトウェアは実機を動かさずに、座学ですませました。
  • 他の仮想化製品 VMWare, VirtualBox, Hyper-V, etc
  • RHCS (本当は動かしたかったが、うまくインストールできず断念)
  • GFS
あと、座学の中では、特に仮想化の技術的な知識はしっかり勉強しました。
特に、各仮想化製品ごとの特権命令実行時の処理について勉強しました。

さいごに

LPIC304は素直な問題が多いので、普通に上記のソフトウェアを一回使ってみれば合格できると思います。 つたない文章でしたが、今からLPIC304を受けようと思っている方の役に立てたら幸いです。

2012年3月17日土曜日

Pacemaker+drbd+heartbeatでMySQLクラスタ

LPIC304の勉強で、Pacemaker+DRBD+heartbeatでMySQLのクラスタ構成を作ってみる。

はじめに

環境は以下の通り。
  • CentOS 5.6
  • drbd83-8.3.12-2.el5.centos
  • heartbeat-3.0.3-2.3.el5
  • pacemaker-1.0.12-1.el5.centos
  • mysql-server-5.0.95-1.el5_7.1
各プロダクトのインストールはyumで出来るので割愛。基本的には公式HPをもとにして、いろいろ補足を入れていきます。

作る環境は以下の通り
  • 共有IP「192.168.4.30」 にMySQLを立てる
  • ノード「db1」
    • サービス用:eth1(192.168.4/24に接続)
    • ヘルスチェック用:eth2(IP:192.168.5.31) 
  • ノード「db2」
    • サービス用:eth1(192.168.4/24に接続)
    • ヘルスチェック用:eth2(IP:192.168.5.32) 

準備

まずDRBDの設定は以下の通り。/etc/drbd.confを編集。 (DRBDの初期化作業は割愛させてもらいます。 )
resource r0 {
    protocol C;
    device /dev/drbd0;
    meta-disk internal;

    on db1 {
        address 192.168.5.31:7801;
        disk /dev/xvdb1;
    }
    on db2 {
        address 192.168.5.32:7801;
        disk /dev/xvdb1;
    }
}


heartbeatの設定は以下の通り。/etc/ha.d/ha.cfを編集。
pacemaker on
logfile /var/log/ha-log
logfacility     local0
keepalive 2
udpport 694
ucast eth2 192.168.5.32 #←db2の方は192.168.5.31
node    db1
node    db2

次に、mysqlのデータディレクトリをDRBDのレプリケーションディレクトリに変える。/etc/my.cnfを編集。
[mysqld]
datadir=/service/mysql  #←DRBDのレプリケーション対象の/service以下にする
(以下略)
ここまでできたら、heartbeatを起動。
# /etc/init.d/heartbeat start
起動したら、crm_monのコマンドで、クラスタの状態を確認。heartbeatが起動すると
============
Last updated: Sat Mar 17 12:01:31 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
0 Resources configured.
============

Online: [ db1 db2 ]
になる。

これで準備はOK。pacemakerの設定に入っていく。どちらかのノードで、crmのコマンドを実行し、設定ツールを起動。その後configureを入力し、設定モードへ。
[root@db1 ~]# crm
crm(live)# configure
crm(live)configure#
この後は以下の順序で設定していく。
  1. 全体の設定
  2. 共有IPアドレスをリソースとして追加
  3. DRBDをリソースとして追加
  4. ファイルシステム(マウント定義)をリソースとして追加
  5. MySQLをリソースとして追加
  6. リソースのグループ、リソース間の制約・順序の設定

全体の設定

まず全体の設定は、以下コマンドをcrm(live)configure#のプロンプトに打ち込めばOK。
property $id="cib-bootstrap-options" no-quorum-policy="ignore" stonith-enabled="false"
no-quorum-policyは2台構成の場合は必ずignoreにしないといけないらしい。 stonith-enabledは相手が半死にした場合に止めを刺す機能だけど、止めておく。

共有IPアドレスのリソースの追加

次に、共有IPアドレスのリソースの追加
primitive ip_mysql ocf:heartbeat:IPaddr2 params ip="192.168.4.30" nic="eth1"
「ocf」っていうのはOpen Cluster Frameworkの略で、Open Cluster Framework projectが 推奨するタイプのスクリプト。/usr/lib/ocf/resource.d以下にある。
この段階で、一回commitして挙動を確認する。
crm(live)configure# commit
公式HPでは、全部設定してからcommitする手順になっているが、一つ一つ確認したほうが確実なので、こまめにcommitするのがよいと思う。
commitしてしばらくすると、crm_monの表示が以下のように変わる。
============
Last updated: Sat Mar 17 12:20:37 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
1 Resources configured.
============

Online: [ db1 db2 ]

ip_mysql        (ocf::heartbeat:IPaddr2):       Started db1 (←リソースが追加された)
ifconfigを打つと、確かにdb1のeth1に192.168.4.30が付いていることが分かる(場合によってはdb2につく場合もある)。

DRBDのリソースを追加

次に、DRBDのリソースを追加していく。
primitive drbd_mysql ocf:linbit:drbd params drbd_resource="r0"
ms ms_drbd_mysql drbd_mysql meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" notify="true"
「ms」というのは、マスタースレーブ構成の場合に設定する項目。内容は公式HP参照。
そんでコミットすると、crm_monの表示は以下の通り。(タイムアウトが短いとかいうワーニングが出るが無視)
============
Last updated: Sat Mar 17 12:23:59 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
2 Resources configured.
============

Online: [ db1 db2 ]

ip_mysql        (ocf::heartbeat:IPaddr2):       Started db1
 Master/Slave Set: ms_drbd_mysql (←リソースが追加された)
     Masters: [ db1 ]
     Slaves: [ db2 ]
確認のために「cat /proc/drbd」を打つと、db1がprimaryになっていることが分かる。

ファイルシステム(マウント定義)のリソースを追加

続いて、ファイルシステムのリソースを追加する。
primitive fs_mysql ocf:heartbeat:Filesystem params device="/dev/drbd/by-res/r0" directory="/service/" fstype="ext3"
そんでコミットすると、crm_monの表示は以下の通り。
============
Last updated: Sat Mar 17 13:34:00 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
3 Resources configured.
============

Online: [ db1 db2 ]

ip_mysql        (ocf::heartbeat:IPaddr2):       Started db1
 Master/Slave Set: ms_drbd_mysql
     Masters: [ db1 ]
     Slaves: [ db2 ]
fs_mysql        (ocf::heartbeat:Filesystem):    Started db1
確認のために「df」コマンドを打つと、db1でDRBDのパーティションをマウントしていることが分かる。

MySQLのリソースの追加

続いて、MySQLのリソースの追加。
primitive mysqld lsb:mysqld
「lsb」というのは、Linux Standard Baseの略。Linux標準で提供される/etc/init.d以下にある起動スクリプトのこと。

で、コミットするとcrm_monは以下のようになる。
============
Last updated: Sat Mar 17 13:35:23 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
4 Resources configured.
============

Online: [ db1 db2 ]

ip_mysql        (ocf::heartbeat:IPaddr2):       Started db1
 Master/Slave Set: ms_drbd_mysql
     Masters: [ db1 ]
     Slaves: [ db2 ]
fs_mysql        (ocf::heartbeat:Filesystem):    Started db1
mysqld  (lsb:mysqld):   Started db1  (←追加された)
SELinuxが動いていると、別パーティションへのデータ保存はできないため、MySQLの起動エラーになる可能性あり。SELinuxを切ればOK

リソースのグループ、リソース間の制約・順序の設定

最後に、リソースのグループ、リソース間の制約・順序の設定する。
group     mysql fs_mysql ip_mysql mysqld
colocation mysql_on_drbd inf: mysql ms_drbd_mysql:Master
order      mysql_after_drbd inf: ms_drbd_mysql:promote mysql:start
設定の意味は、
  • 1行目は「ファイルシステムと共有IPとMySQLをグループ『mysql』に指定」
  • 2行目は「mysqlグループとDRBDのマスターは同時に存在しなくてはならない」
  • 3行目は「DRBDのマスターが起動した後に、mysqlグループが起動しなくてはならない」
この指定によって、仮にちぐはぐにリソースが配置されいたとしても、上記の制約に従って自動的に再配置が行われる。
この「inf:」という文字列は謎で、公式HPを読んでも何も書いていない(筆者はInfinityの略ではないかと思っている)。よくわからないので、仕方なくいわれるがままに指定している。せめて公式HPにはオプションの意味ぐらい書いてほしいところだ。

最終的にcrm_monは以下のようになる。
============
Last updated: Sat Mar 17 13:36:23 2012
Stack: Heartbeat
Current DC: db2 (88696624-7159-4ad1-bdf3-aac07d645d99) - partition with quorum
Version: 1.0.12-unknown
2 Nodes configured, unknown expected votes
2 Resources configured.
============

Online: [ db1 db2 ]

 Master/Slave Set: ms_drbd_mysql
     Masters: [ db1 ]
     Slaves: [ db2 ]
 Resource Group: mysql (←まとまった)
     fs_mysql   (ocf::heartbeat:Filesystem):    Started db1
     ip_mysql   (ocf::heartbeat:IPaddr2):       Started db1
     mysqld     (lsb:mysqld):   Started db1

設定のリセット

ちなみに、設定に失敗した場合は、以下の手順でリセットできる。
# /etc/init.d/heartbeat stop
# rm -rf /var/lib/heartbeat/crm/*

2012年3月10日土曜日

XenカーネルでDRBDを使うときにハマったこと

CentOS5.6(xenカーネル)で、DRBDを動かそうと思い、drbdをyumでインストール。
# yum install drbd83.i386 kmod-drbd83.i686
色々設定後、サービスの起動で以下のようなエラーメッセージが出た。
# /etc/init.d/drbd start
Starting DRBD resources: Can not load the drbd module.
どうやら、普通のDRBDのカーネルモジュール(kmod-drbd83)はxen環境では動作しないようだ。
 冷静に考えれば、カーネルが違うんだから、モジュールも違って当たり前か。

 xenの場合はkmod-drbd83-xenというカーネルモジュールを入れる必要がある。
# yum remove kmod-drbd83
# yum install kmod-drbd83-xen
これでうまくいった。

ちなみに、上記手順でもサービス起動時にカーネルモジュールが読み込まれない場合は、以下のコマンドでカーネルモジュールの依存関係を更新してやる。
# depmod -a

2012年3月9日金曜日

CentOS 5.6でheartbeatとpacemakerのコンパイルエラー

LPIC304の勉強の一環で、Linuxのpacemaker + heartbeatを試すことに。
環境は以下の通り。
  • OS:CentOS 5.6
  • heartbeat-3.0.7
  • pacemaker-1.1.6-1
  • autoconf-2.68
  • automake-1.11
とりあえず公式HP を参考にソースコードからコンパイル。
すると以下のエラーが出た。
Making all in doc
gmake[1]: ディレクトリ `/root/src/Heartbeat-3-0-7e3a82377fa8/doc' に入ります
\
        --xinclude \
        http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl heartbeat.xml
gmake[1]: --xinclude: コマンドが見つかりませんでした
gmake[1]: *** [heartbeat.8] エラー 127
gmake[1]: ディレクトリ `/root/src/Heartbeat-3-0-7e3a82377fa8/doc' から出ます
make: *** [all-recursive] エラー 1
どうやらドキュメントの生成中に、xincludeというxml関連のコマンドが無くて失敗している模様。別にドキュメントなんていらないんだけど、迷惑な話だ。
調べていると、Linux-HA mailing list でも同じエラーが話題になっている。
どうやら、libxsltのRPMを入れればよいらしいので、yumでいれて問題解消。
その後./bootstrapからやり直したらうまくいった。(ちなみに、その後のmakeめちゃ長いです)


続いてpacemaker。公式HPの手順通り進めていくと、以下のエラーが出た

autoreconf: running: /usr/local/bin/autoconf --force --warnings=no-portability
configure.ac:86: error: possibly undefined macro: AC_LIBTOOL_DLOPEN
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
configure.ac:87: error: possibly undefined macro: AC_LIBLTDL_CONVENIENCE
configure.ac:88: error: possibly undefined macro: AC_PROG_LIBTOOL
autoreconf: /usr/local/bin/autoconf failed with exit status: 1
Now run ./configure
configure: error: cannot find install-sh, install.sh, or shtool in . "."/.
これはlibtoolのバージョンが古かったために発生したようで、最新のlibtool-2.4をソースから入れたら解消した。

続いてこんなエラー
configure: error: The libxslt developement headers were not found
これはヘッダファイルがないとのことなので、libxlst-develを入れればよさそうな気がする。yumで入れたら解消した。

さらにこんなエラー
./configure: line 16612: syntax error near unexpected token `libqb,'
./configure: line 16612: `PKG_CHECK_MODULES(libqb, libqb, HAVE_libqb=1, HAVE_libqb=0)'
調べていると、どうやらpkg-configが悪さをしているようだ。
PRMのバージョンを調べてみると、pkgconfig-0.21-2.el5であり、2006年ぐらいのもの。
autoconfやautomakeは最新のものをソースからコンパイルして入れているので、pkg-configだけ古いが悪かったようだ。
ココから最新のpkg-config-0.26のソースコードを落としてきて、インストール。
これでうまくいった。

つづいて、こんなエラー

configure: error: Package requirements (libcpg) were not met:

No package 'libcpg' found

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

Alternatively, you may set the environment variables cpg_CFLAGS
and cpg_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
PKG_CONFIG_PATHの環境変数が悪いかもとか言われているので、PKG_CONFIG_PATH=/usr/lib/pkgconfigと設定してみたが、効果なし。
完全に行き詰った。


もう最新のソースはあきらめて、RPMで入れることにした。。。
# rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
# wget -O /etc/yum.repos.d/clusterlabs.repo  http://clusterlabs.org/rpm/epel-5/clusterlabs.repo
# yum install pacemaker.i386
嗚呼、なんて簡単なんだ。。。orz

2012年3月7日水曜日

keepalived 1.2.2とLVSの連携でハマった点

LPIC304の試験の勉強として、keepalivedとLVSを連携させてみようと試みたところ、いくつかはまったので書いておく。

環境は以下の通り。
  • OS : CentOS release 5.6 (Final)
  • Kernel : 2.6.18-238.el5xen
  • keepalived : 1.2.2
keepalivedのコンパイル中に変なことに気付いた。

# tar zxvf keepalived-1.2.2.tar.gz
# cd keepalived-1.2.2
# ./configure
(中略)
Keepalived configuration
------------------------
Keepalived version       : 1.2.2
Compiler                 : gcc
Compiler flags           : -g -O2 -DETHERTYPE_IPV6=0x86dd
Extra Lib                : -lpopt -lssl -lcrypto
Use IPVS Framework       : No ←★LVSとの連携ができていない
IPVS sync daemon support : No ←★
Use VRRP Framework       : Yes
Use Debug flags          : No
LVSは入っていて、動作確認までしていたのに、連携できないとはどういうことか!

調べていると、どうやらkernelのソースコードに入っているip_vs.hがないとだめのようだ。
そもそも、kernelのソースコードは入っていなかったのでyumでインストール。
# yum install kernel-devel
# find / -name ip_vs.h
/usr/src/kernels/2.6.18-274.18.1.el5-i686/include/net/ip_vs.h
これでip_vs.hが入れられたので、./configureのオプションに場所を教えてあげる。
# ./configure --with-kernel-dir=/usr/src/kernels/2.6.18-274.18.1.el5-i686
(中略)
Keepalived configuration
------------------------
Keepalived version       : 1.2.2
Compiler                 : gcc
Compiler flags           : -g -O2 -DETHERTYPE_IPV6=0x86dd
Extra Lib                : -lpopt -lssl -lcrypto
Use IPVS Framework       : Yes★←うまくいってます!
IPVS sync daemon support : Yes★←
IPVS use libnl           : No
Use VRRP Framework       : Yes
Use Debug flags          : No
うまくいった!つづいてmake
# make
が、そこで以下のようなエラー(一部略)
/usr/include/sys/types.h:62: error: conflicting types for ‘dev_t’
/usr/src/kernels/2.6.18-274.18.1.el5-i686/include/linux/types.h:22: error: previous declaration of ‘dev_t’ was here
/usr/include/sys/types.h:67: error: conflicting types for ‘gid_t’
/usr/src/kernels/2.6.18-274.18.1.el5-i686/include/linux/types.h:54: error: previous declaration of ‘gid_t’ was here
/usr/include/sys/types.h:72: error: conflicting types for ‘mode_t’
/usr/src/kernels/2.6.18-274.18.1.el5-i686/include/linux/types.h:24: error: previous declaration of ‘mode_t’ was here
/usr/include/sys/types.h:77: error: conflicting types for ‘nlink_t’
/usr/src/kernels/2.6.18-274.18.1.el5-i686/include/linux/types.h:25: error: previous declaration of ‘nlink_t’ was here
/usr/include/sys/types.h:82: error: conflicting types for ‘uid_t’
/usr/include/time.h:105: error: conflicting types for ‘timer_t’
/usr/include/sys/select.h:78: error: conflicting types for ‘fd_set’
/usr/include/sys/types.h:235: error: conflicting types for ‘blkcnt_t’
調べていると、keepalivedはkernelのバージョンによってコンパイルできないことがあるらしい。
参考にしたのはこちら

結局、最新の1.2.2ではなく、古い1.1系の最終版である1.1.20をつかうことに。
これはうまくいった。

2012年1月14日土曜日

rubyのoauth2を使ってfacebook連携

rubyのoauth2を使ってfacebookとのOAuth連携を試みてみた。環境は以下の通り。
  • ruby: 1.9.2p180
  • oauth2: 0.5.2
まずは、facebook認証のURLを生成するコードを書いてみる。
require 'oauth2'

site = "https://graph.facebook.com"
token_url= "/oauth/access_token"
key="xxxx"                          # facebookのAPP ID
secret="xxx"                        # facebookのAPP SECRET
callback_url="http://xxx.com/"      # callbackしてほしいURL

#インスタンス作成
client = OAuth2::Client.new(key,secret,:site => site,:token_url => token_url)

#URL作成
url = client.auth_code.authorize_url( :redirect_uri => callback_url)

#URL表示
puts url
これで出来たURLでアクセスすると、facebookのログイン画面が出るので、そこでログインすると次のようなLocationを指定したリダイレクト要求のレスポンスが返ってくる。

Location: (callbackしほしいURL)/?code="(認証コード)"

次に認証コードを使って、tokenを取得するスクリプトを書いてみる。
require 'oauth2'

site = "https://graph.facebook.com"
token_url= "/oauth/access_token"
key="xxxx"                          # facebookのAPP ID
secret="xxx"                        # facebookのAPP SECRET
callback_url="http://xxx.com/"      # callbackしてほしいURL
code ="xxxx"                        # 前のスクリプトで取得したcode

#インスタンス作成
client = OAuth2::Client.new(key, secret, :site => site, :token_url => token_url)

#tokenの取得
access_token = client.auth_code.get_token( code, { :redirect_uri => callback_url })

#token表示
puts access_token.token
これを実行すると、以下のようなエラーがでた。
/usr/local/lib/ruby/gems/1.9.1/gems/oauth2-0.5.2/lib/oauth2/client.rb:129:in `get_token': OAuth2::Error (OAuth2::Error)
まったくわからなかったので、ソースを見ると、問題の個所はこのようになっており、
      response = request(options[:token_method], token_url, opts)
      raise Error.new(response) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
ここのoptions[:raise_errors]というのがtrueのため例外を飛ばしていることが分かった。
原因を調査していると、githubの掲示板にこのことが書いてあり、acvwilsonさんが解決策を書いてくれている。
まとめると「facebookからのレスポンスの形式(=が入っているやつ)に対応できていないので、以下のコードを追加してパーサを対応させてくれ。」とのこと
OAuth2::Response.register_parser(:text, 'text/plain') do |body|
  key, value = body.split('=')
  {key => value}
end
これを追加するとうまく動いた!

2012年1月9日月曜日

herokuをつかってみる(デプロイ編)

前回、アカウント登録〜SSH鍵登録まで行った。 今回は、HelloWorldアプリのデプロイまでやってみる。 環境は以下の通り
  • OS: Linux (CentOS 5.5)
  • Ruby: 1.9.2
  • git: 1.7.4
公式HPのGetting Started with Rails 3.0 on Heroku/Cedarにしたがって実施。 まず、herokuのgemをインストール
# gem install heroku
続いて、railsのアプリ作成
# rails new myapp # cd myapp
作ったアプリをpostgresql用に変更。Gemfileの「gem 'sqlite3'」を「gem 'pg'」に変更して、「bundle install」を実行することによりGemfile.lockを更新する。
# bundle install
次にgitに登録。
# git init
# git add .
# git commit -m "init"
次に、herokuにstackを作る。
# heroku create --stack cedar
Invalid gemspec in [/usr/local/lib/ruby/gems/1.9.1/specifications/heroku-2.18.0.gemspec]: invalid date format in specification: "2012-01-09 00:00:00.000000000Z"
Invalid gemspec in [/usr/local/lib/ruby/gems/1.9.1/specifications/term-ansicolor-1.0.7.gemspec]: invalid date format in specification: "2011-10-13 00:00:00.000000000Z"
/usr/local/lib/ruby/site_ruby/1.9.1/rubygems.rb:925:in `report_activate_error': Could not find RubyGem heroku (>= 0) (Gem::LoadError)
エラー発生。Gemのロードエラーとのこと。 とりあえず、gemspecのフォーマットエラーが出てるので、エラーを解消してみる。 解消の仕方は、多少強引だが、エラーになっている.gemspacファイルの"2011-10-13 00:00:00.000000000Z"の部分を、読めるフォーマットの"2011-10-13"に書き直す。 再度実行。
# heroku create --stack cedar
Creating warm-sunrise-9514... done, stack is cedar
http://warm-sunrise-9514.herokuapp.com/ | git@heroku.com:warm-sunrise-9514.git
Git remote heroku added
今度はうまくいった。 これで、herokuに 「warm-sunrise-9514」という名前のアプリができる。 「heroku list」コマンドで確認できる。
# heroku list growing-snow-4642
また、herokuのWebサイトにログインしても確認できる。 ちなみに「--stack cedar」というののが意味不明だが、「cedar」は、rails3用のアプリのコンテナ(?)的なものらしく、rails3を使う場合は、これを指定しないといけないらしい。miagoさんのブログに解説されています。 また、作られたアプリ名が「warm-sunrise-9514」とこれまた意味不明だが、 これはherokuが勝手にランダムに二つの単語をつなげてアプリ名にするようです。 後でWebサイトかえられます。 いよいよデプロイ。 ローカルのgitのレポジトリを、herokuにpushすることによりデプロイができる。
# git push heroku master
これも問題なく完了。 その後は、railsでdbを初期化するために以下を実行
# heroku run rake db:migrate
seedもいれてみる。
heroku run rake db:seed

herokuをつかってみる(アカウント登録〜SSH鍵登録)

rails3をherokuで使ってみようと思い、試みる。環境は以下の通り。
  • OS: Linux (CentOS 5.5)
  • Ruby: 1.9.2
公式HPに従ってユーザアカウントの作成。
そして、heroku-clientのダウンロード。
# wget http://assets.heroku.com/heroku-client/heroku-client.tgz
# tar zxvf heroku-client.tgz
さっそくherokuへログインを試みる。
# cd heroku-client
# ./heroku
すると以下のようなエラーが発生。
/usr/local/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': no such file to load -- readline (LoadError)
どうやら、readlineのrequireに失敗している模様。
tetu1984の日記を参考にさせてもらい、readlineをインストール。
まず、OSにインストール。
# yum install readline-devel
次に、rubyのextentionをインストール。
# cd ruby-1.9.2-p180/ext/readline
# ruby extconf.rb
# make
# make install
再度herokuへのログインを試みる。
# ./heroku login
Enter your Heroku credentials.
Email: xxxx@xxxx.xxxx
Password:
Could not find an existing public key.
Would you like to generate one? [Yn] Y
Generating new SSH public key.

Uploading ssh public key /root/.ssh/id_rsa.pub
/root/heroku-client/lib/heroku/auth.rb:195:in `read': No such file or directory - /root/.ssh/id_rsa.pub (Errno::ENOENT)
エラー発生!
どうやら、初回ログイン時にSSHの鍵ペアを作成して、herokuに登録する動きのようだ。
デフォルトでは、秘密鍵「~/.ssh/id_rsa」と公開鍵「~/.ssh/id_rsa.pub」を作ろうとする。

しかし、私の環境では、前者の秘密鍵がすでに別の用途で存在していて、かつ公開鍵が存在していないために、エラーが起きたっぽい。
解決策は、デフォルトとは違う名前で鍵を作る。
本家のホームページにデフォルトから変える場合の手順が書いてあった。
どうやら、SSHの鍵ペアを作り、「heroku key」の引数を渡せば渡せばよいっぽい。
まずSSHの鍵ペアを作る。
# ssh-keygen -t rsa -f heroku.key
そして、herokuに公開鍵登録。
# ./heroku keys:add ~/.ssh/heroku.key.pub
Uploading ssh public key /root/.ssh/heroku.key.pub
確認。
# ./heroku keys
=== 1 key for xxxx@xxx.xxx
ssh-rsa AAxxxxxx...6gx7ek4w== xxx@xxx.xxx
できた。

heroku-clientがsshする時に使う秘密鍵ファイルがわからないはずなので、sshのconfigを書いておく。
# vi ~/.ssh/config
Host heroku.com
User git
Port 22
Hostname heroku.com
IdentityFile ~/.ssh/heroku.key ←作った秘密鍵

ちなみに、herokuに登録したSSHの公開鍵を消すには、以下のようにすればよい。

まず、以下のコマンドでSSHの公開鍵の名前を調べる。
$ heroku keys
=== 1 key for joe@example.com
ssh-dss AAAAB8NzaC...DVj3R4Ww== joe@workstation.local←これが鍵の名前

そして、「heroku key:remove」。
$ heroku keys:remove joe@workstation.local
Key joe@workstation.local removed.

詳しくは、公式ドキュメント参照