Home > Archives > 2010-12

2010-12

MySQLの無停止レプリケーション(最強?の冗長化)〜KeepAlivedとの連携〜

序文

今回は、これまでの内容を、実践投入するべく、MySQLのレプリケーションをKeepAlivedと連携させ、自動フェイルオーバーと、無停止フェイルバックを実現させてみました。

フェイルオーバーの方は、結構簡単なのですが、問題はフェイルバック。無停止でフェイルバックさせるには・・MySQL管理者ならば誰しも当たる壁のような気がします。今回は、ちょっとサーバー台数多めですが、ほぼ完全無停止のMySQL-HAを作成します。

また、本内容は、MySQLレプリケーションのイロハは省略しております。わからないところは、他サイトにたくさん例がありますので、そちらを参考に読んでください。

構成内容

サーバー6台以上必要です。(参照系KeepAlived等をスレーブに同居させると、最小4台で済みますが、わかりやすいのは6台構成です。)構成は、MySQLサーバー4台、KeepAlivedサーバー2台です。

特徴1

一部、準同期レプリケーションを使うことで、故障時のクエリ消失が原則存在しません。これって、結構すごいことだと思うんですが・・。そんなわけで、準同期レプリケーションを使うので、MySQL5.5以降をお薦めします。また、非同期でも、ほぼクエリ損失はありませんでしたので、5.1系でも十分に使えると思います。(サーバー管理者としては、枯れた方を採用しなければならない局面があるので、準同期が使えなくても、実戦投入できるのはいいですね。)もちろん、何の保証もしませんので、実際に採用される場合は自己責任でお願いします。

特徴2

アプリケーション側で、参照クエリ、更新クエリの問い合わせ先を変えていることが前提です。MySQL参照系クエリは、レプリケーションされた3台のサーバーに振り分けますので、無駄はありません。また、再起動時に勝手にMySQLが立ち上がらないようにchkconfigはoffにしておくことをお薦めします。

構成図

次のようになります。

簡単な概要は次の通りです。

名前 稼働パッケージ 概要
Master MySQL、KeepAlived SubMasterとのVRRP連携してます。KeepAlivedのVIPで、更新系クエリを受け付け、SubMasterに準同期でレプリケーション、slave1-1に非同期レプリケーションします。また、KeepAlivedを使って、自身のMySQLの死活監視も行っています。自身のMySQLが応答しない場合は、KeepAlived及びMySQLを停止させます。
slave1-1 MySQL、Cron Masterから非同期レプリケーションしています。複数台にできます。Cronを用いて、自身のレプリケーション監視を行っています。レプリケーション停止時は、自身のMySQLを停止させます。
SubMaster MySQL、KeepAlived MasterとVRRP連携してます。マスタが停止したら自動的にマスタに昇格します。こちらも、KeepAlivedを使って自身のMySQLの死活監視を行っています。自身のMySQLが応答しない場合は、KeepAlived及びMySQLを停止させます。
slave2-1 MySQL、Cron SubMasterから非同期レプリケーションしています。複数台にできます。Cronを用いて、自身のレプリケーション監視を行っています。レプリケーション停止時は、自身のMySQLを停止させます。
KeepAlived Mst KeepAlived KeepAlived Slvと、VRRP連携を行っています。参照系クエリの受付と、分散をDSR構成で行います。
KeepAlived Slv KeepAlived KeepAlived Mstと、VRRP連携を行っています。KeepAlived Mstが停止すると、自動的にKeepAlived Mstに昇格します

構築の前に、フェイルオーバーとフェイルバック挙動について説明します。

slave*-*停止時

※どのslaveでも、挙動はほとんど変わりません。例として、slave1-1が停止した場合を書きます。

フェイルオーバー

1.(自動)Slave1-1のMySQLが停止。

2.(自動)参照受付のKeepAlived Mstが、バックエンドのslave1-1が停止していることを検知し、参照クエリを、SubMasterとslave2-1に振る。

フェイルバック

1.(手動)Slave1-1を再起動。

2.(手動)レプリケーションが追い付くまでは、危険なので、slave1-1のiptablesの設定等で、参照系クエリがslave1-1に飛んでこないように設定。たとえば下のようにレプリケーション以外のパケットをブロックするとかです。

iptables -A INPUT -s 192.168.0.30 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP

3.(手動)レプリケーションが追い付いたのを確認後、パケットブロックの解除等で、参照クエリの受付を開始する。(ブロック解除で自動復帰)

4.(手動)お・わ・り!

フェイルバックは、至って簡単です。

サブマスタ(SubMaster)停止時

※サブマスタのMySQLのみが停止しても、サブマスタ自身が落ちても、ほぼ同一の挙動をとります。

フェイルオーバー

1.(自動)SubMasterのMySQLが停止。

2.(自動)SubMasterのKeepAlivedが自身のMySQLが落ちたことを検知し、自分自身であるKeepAlived停止と、MySQLが固まっていたとき用に、MySQLも停止。

3.(自動)slave2-1がレプリケーション停止を感知し、自身のMySQLを停止。

4.(自動)参照受付KeepAlived Mstが、バックエンドのSubMasterとslave2-1が停止していることを検知し、参照クエリを、slave1-1のみに振る。

フェイルバック

※基本的に、slaveが停止したときと同様です。再生の順番に気を付けてください。

1.(手動)SubMasterの再起動。

2.(手動)SubMasterをレプリケーション以外パケットブロックして、レプリケーションを追い付かせる。

3.(手動)パケットブロックを解除して、参照クエリを受付開始する。(ブロック解除で自動復帰)

4.(手動)KeepAlivedの起動。

5.(手動)Slave2-1だけ落ちているものとして復旧させる。

6.(手動)お・わ・り!

KeepAlived Mst停止時

※KeepAlived Slvが停止した場合は、単純に再起動させるだけです。

フェイルオーバー

1.(自動)KeepAlived Slvが、VRRPによりKeepAlived Mstが停止したことを検知し、参照系VIPアドレス(図の場合192.168.0.2)を引きついでファイルオーバー(参照系の通信断が数秒発生。)

フェイルバック

1.(手動)KeepAlived Mstの再起動。

2.(手動)KeepAlivedを起動。(バックアップとなり、KeepAlived MstとSlvが元々と入れ替わった状態になる。)

3.お・わ・り!

Master停止時

※こいつが一番の肝であり、MySQLをバックエンドに使用している方の大半が苦労されているところだと思います。

フェイルオーバー

1.(自動)MasterのMySQLが停止。

2.(自動)MasterのKeepAlivedが自身のMySQLが落ちたことを検知し、自分自身であるKeepAlived停止と、MySQLが固まっていたとき用に、MySQLも停止。

3.(自動)SubMasterのKeepAlivedが、VRRPにより、MasterのKeepAlivedが停止したことを検知し、更新系VIP(図では192.168.0.1)を引き継いで、フェイルオーバー。この後の更新系クエリは、すべてSubMasterが受け取って処理する。

4.(自動)slave1-1がレプリケーション停止を感知し、自身のMySQLを停止。

5.(自動)参照受付KeepAlived Mstが、バックエンドのSubMasterとslave2-1が停止していることを検知し、参照クエリを、SubMasterとslave2-1に振る。

フェイルバック

※ここが、今回の構成の最も優れたところです。ほぼ、無停止、かつ簡単にフェイルバックが終了します。

1.(手動)アクセス量が少ない時間を見計らって、Slave2-1のMySQLを停止。

2.(手動)Slave2-1のMySQL-DBデータをMasterとSlave1-1に送り、データを同期させる。

3.(手動)Slave2-1を、SubMaster以外パケットブロックし、MySQLを起動させ、レプリケーションを追い付かせる。

4.(手動)Slave2-1のレプリケーションが追い付いたのを確認後、パケットブロックを解除して、参照系受付に復帰させる。(ブロック解除で自動復帰)

5.(手動)SubMasterのレプリケーションをオフにする。

>stop slave;

6.(手動)Masterを再起動し、SubMaster以外パケットブロックし、MySQLを起動させる。

7.(手動)MasterのレプリケーションマスタをSubMasterにする。

>change master to master_host='192.168.0.35',master_user='***',master_password='****',master_log_file='mysql-bin.00****',master_log_pos=****;

8.(手動)Masterのレプリケーションを開始させる。

>start slave;

9.(手動)Masterのレプリケーションが追い付いたのを確認後、パケットブロック解除。(参照には登録されていないので、参照受付は開始されない)

10.(手動)Slave1-1を再起動し、すべてのパケットをブロックし、MySQLを起動させる。

11.(手動)Slave1-1でレプリケーションのスタートポジションを変更させる。

>change master to master_host='192.168.0.30',master_user='***',master_password='****',master_log_file='mysql-bin.00****',master_log_pos=****;

12.(手動)一旦Slave1-1のMySQLを停止させ、Masterからのパケットのみを許可し、MySQLを起動。

13.(手動)Slave1-1のレプリケーションが追い付いたことを確認後、パケットブロック解除し、参照系受付に復帰させる。(ブロック解除で自動復帰

14.KeepAlived SlvのconfigをSubMasterとMasterを入れ替えたものに変更し(事前に用意しておくと楽)、KeepAlivedを再起動。

15.KeepAlived Mstのconfigも書き換え、KeepAlivedを再起動。(参照系の通信断が数秒発生。)

16.お・わ・り!

つまるところ、「MasterとSlave1-1」の集団と、「SubMasterとSlave2-1」の集団をそっくり入れ替えてしまうというのが、今回の肝です。手順内でのダウンタイムは、何と「数秒」以下です。

MasterとSubMaster間に準同期レプリケーションを組む理由

MySQLのレプリケーションは標準で非同期です。これにより、例えば、Masterで更新クエリがCommitされた直後にMasterが落ちた場合、Slaveに更新クエリがいっていないことがあります。KeepAlivedfが自動フェイルオーバーを行った場合に、その後の更新系クエリで異常がでる可能性もあります。

準同期レプリケーションでは、「Commitされる前に、Slaveに更新クエリ内容が渡っている」ことが確実になります。このため、Masterが落ちても、Commitされたクエリは、すべてSlave(今回の場合はSubMaster)に渡っていることが保証されますので、フェイルオーバーしても、Commitされたクエリの欠落が起こらないようになります。(これは安心して寝れますね。)

次回は、この構成についてのそれぞれのconfigや監視スクリプト等を公開したいと思います。

それでは、皆さん、メリークリスマス。(か、よいお年を!)

KeepAlivedのススメ2

前回は、KeepAlivedはなんぞや?ということを中心に書きましたので、今回は、インストールから設定までを書いてみたいと思います。

KeepAlivedのインストール

大体は前回書いたのですが、今回は概念よりも実際の作業内容を書いてみます。

作業の大まかな流れ

  • 1.KeepAlivedのソースを取得
  • 2.ソースを一旦解凍して、specファイルを取り出す
  • 3.ソースとspecファイルを配置
  • 4.RPM化
  • 5.レポジトリに追加
  • 6.ipvsadmもレポジトリに追加
  • 7.yumでインストール
  • 8.確認

ざっとこんな感じです。

1.KeepAlivedのソースを取得

ソースはここから取得します。2010/12/14現在で最新は1.2.1です。tar.gz形式で置いてあります。

2.ソースを一旦解凍して、specファイルを取り出す

RPMの作成に必要な指示書であるspecファイルがkeepalived.spec.inとして中にあります。これを取り出してください。

# tar xzvf keepalived-1.2.1.tar.gz
# cd keepalived-1.2.1
# cp keepalived.spec.in ./../
# cd ..
# rm -rf keepalived-1.2.1

こんな感じです。解凍したものは、取り出した後は不要なので、削除しておきます。

3.ソースとspecファイルを配置

前々回までの投稿内容のようにRPM作成環境があることを前提とします。

# mv ./keepalived.spec.in /home/navi/mkrpm/SPECS/keepalived.spec
# mv ./keepalived-1.2.1.tar.gz /home/navi/mkrpm/SOURCES/
# chown navi:navi  /home/navi/mkrpm/SPECS/keepalived.spec.in
# chown navi:navi  /home/navi/mkrpm/SOURCES/keepalived-1.2.1.tar.gz

最後のふたつは、navi以外の所有者にてここまでの動作を行った場合は実行してください。naviにてすべてを行っていたら必要ありません。

4.RPM化

naviで、rpmbuildします。

# su navi
# cd /home/navi/mkrpm/SPECS
# rpmbuild -ba keepalived.spec

5.レポジトリに追加

レポジトリフォルダは、適宜読みかえてください。

# cp /home/navi/mkrpm/RPMS/x86_64/keepalived-1.2.1-5.x86_64.rpm (apacheの公開ディレクトリ内の、レポジトリフォルダ)
# rpm --addsign (apacheの公開ディレクトリ内の、レポジトリフォルダ)/keepalived-1.2.1-5.x86_64.rpm
# createrepo (apacheの公開ディレクトリ内の、レポジトリフォルダ)

6.ipvsadmもレポジトリに追加

Centであれば、ここにipvsadm-1.24のrpmがあります。

これをダウンロードして、次のように追加します。

# mv ./ipvsadm-1.24-10.x86_64.rpm (apacheの公開ディレクトリ内の、レポジトリフォルダ)
# rpm --resign (apacheの公開ディレクトリ内の、レポジトリフォルダ)/ipvsadm-1.24-10.x86_64.rpm
# createrepo (apacheの公開ディレクトリ内の、レポジトリフォルダ)

7.yumでインストール

あとは、該当機にて、

# yum install ipvsadm keepalived (デフォルトenabled=0なら、--enablerepo=***)

を行えば、インストールは完了です。あとは、configをいじって、

# service keepalived start

を行えば、起動完了!。さらに、再起動時に初期起動させるために

# chkconfig keepalived on

をしておくといいかもです。

KeepAlivedの設定について

上記のインストール方法でインストールを行うと、「/etc/keepalived/keepalived.conf」に、コンフィグファイルのデフォルトが入っているはずです。大きく分けて

  • 「global_defs (アラートメール等の設定箇所)」
  • 「vrrp_instance(vrrpによる自機の冗長化の設定箇所)」
  • 「virtual_server(ロードバランサとしてのLVSラッパの設定箇所)」

の3つに区分されます。他にも、「vrrp_sync_group(複数のvrrpグループを束ねる)」もありますが、これは後で説明します。

簡単に書くと次のようなconfigファイルになります。

! Configuration File for keepalived

global_defs {
# アラートメールの設定
}

vrrp_instance [好きな名前]{
# VRRPによる自機の冗長化設定
}

virtual_server [仮想IP] [ポート番号]{
# LVSによるロードバランサの設定
}


gloval_defsの設定内容

! Configuration File for keepalived
# フェイルオーバーした際のalertメールを送るための設定(無くても可)
global_defs {
    # メールの送り先
    notification_email {
        test@test.local
    }
    # メールの送信元アドレスと、メールサーバー
    notification_email_from KeepAlived@test.local
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    # 機器名をつけておくと、メールの内容がわかりやすくなる。
    router_id test_mast
}

こんな感じです。

vrrp_instanceの設定内容

vrrp_instance [好きな名前]と書いて始めます。名前は、vrrpを組むグループを複数作れるので、その一意性を持たせるためのものです。

vrrp_instance VI_1 {
    # マスタサーバーかバックアップサーバーか。(後述)
    state (MASTER | BACKUP)
    #今起動しているのがなければ、マスタになり、マスタがいれば、スレーブ。
    nopreempt
    # VRRP信号を送出するNIC(監視側みたいなもの)
    interface eth0
    # ネットワークが混雑していて、スイッチがARP信号を処理できない場合は、ARPを少し送らせて送信する設定。
    # 無くても動く。動作が変な場合(VIPを引き継いだのに、外部から認識できない現象が出たら、こいつを設定。)
    # 弊害として、VIPの引継完了が遅くなる。(デフォルトは引き継いだら即ARP信号の送信。)
#    garp_master_delay 1
    # 仮想ルーターのID(VRRP信号を共有する他サーバーのインスタンス間で共通にする。)
    virtual_router_id 51
    # 優先度(MasterはBACKUPよりも高く設定。)
    priority 101
    # VRRP信号を送出するインターバル(間隔)。下記設定では、1秒おき。
    advert_int 1
    # VRRP信号を受け取る際のパスワード等(同じvirtual_router_idならば、同じにする。)
    authentication {
        auth_type AH
        auth_pass pass@la
    }
    # このインスタンスで管理するVIP(同じvirtual_router_idなら、同じにする。※NICは同じ必要ない。)複数設定可能
    virtual_ipaddress {
        192.168.100.12/28 dev eth0
#      192.168.100.13/28 dev eth0
    }
    #フェイルオーバー時にメール通知する場合は、次を追加。
    smtp_alert

    # マスタノードに昇格した(もしくは、マスタノードとして起動した)際に実行するスクリプト。
#    notify_master "/usr/local/tekitou/tekitou.sh"
    # バックアップノードに落ちた(もしくは、バックアップノードとして起動した)際に実行するスクリプト。
#    notify_master "/usr/local/tekitou/tekitou.sh"
}

特に重要なのは、「state」です。フェイルバック時の挙動をここで変更します。例えば、2台構成(機器1・2)で、機器1が初め、masterノードとして働いていたケースを考えます。機器1が故障によりダウンした場合、機器2が仮想IPを振り替えてマスタに昇格します。この後、機器1が故障から復旧した場合、「機器1がマスタに戻ってほしい」場合と「機器2がマスタのままで、機器1はバックアップになってほしい」場合があります。前者は、管理上、機器1が常にマスタの方が好ましいと判断する場合であり、後者は、フェイルバック時にダウンタイムが発生してほしくない場合です。KeepAlivedでは、「state」の設定により、いずれかを選択することができます。

機器2からのフェイルバック時の動作 機器1 機器2 共通
機器1がマスタに戻る MASTER BACKUP
機器1はバックアップとして戻る BACKUP BACKUP nopreempt

また、両方共BACKUPとして設定したとき、どちらが初めのMASTERとなるかですが、これは、「先に起動していた方」となります。さらに、どちらがマスタかわからない場合は、virtual_ipaddressが設定されているのを確認すれば一目瞭然です。

# ip addr show

とかで、ipアドレスを確認してください。

virtual_serverの設定内容

DSRとNATについては、別の回のブログに記載しています。(ちょっと読みにくいようですが・・)

virtual_server 192.168.100.12 3306 {
    #バックエンドの監視を行うインターバル
    delay_loop 3
    #rrラウンドロビン、他のは後述
    lb_algo rr
    #DR:DSR構成、NAT:なっと(別の回のブログに記載)
    lvs_method DR
    protocol TCP
    #バックエンドは3台構成。
    real_server 192.168.100.20 3306 {
        weight 10
        TCP_CHECK {
            connect_port 3306
            connect_timeout 30
        }
        #もしこのサーバーが止まったときに呼び出される処理。
#        notify_down "/usr/local/test/testdown1.sh"
    }

    real_server 192.168.100.21 3306 {
        weight 10
        TCP_CHECK {
            connect_port 3306
            #普通はあまり使わないが、別のIPで監視もできる(同じIPのときは不要)
#         bindto 192.168.100.30
            connect_timeout 30
        }
    }

    real_server 192.168.100.22 3306 {
        weight 10
        TCP_CHECK {
            connect_port 3306
            connect_timeout 30
        }
    }

    #リアルサーバが全滅したときにパケットが転送されるサーバ
    sorry_server 192.168.100.23 3306
}

あれ?、と気づいた方もおられるかな、と思います。3306ポート、有名なMySQLのデフォルトポートです。次回、詳しくは書こうと思うのですが、上記ですと例えば、「sorry_serverはレプリケーションマスタ、バックエンドはそれぞれレプリケーションスレーブ」として、192.168.100.12:3306が、参照系クエリの受付アドレス、みたいな感じです。参照系の負荷分散ですね。checkの方法はまだまだたくさんあるのですが、最も良く使うのがTCPチェックです。単にコネクションが確立できるかどうかをチェックします。タイムアウトは30秒ですが、大抵瞬間でバックエンドの停止を見つけてくれます。(上記設定では、インターバル3秒なので、タイムアウト待つ間もなくコネクション不可がかえってくるようです。)HTTPサーバーの冗長化のときは、HTTP_GETとかSSL_GETとかもあります。(ただし、当然これは負荷も高そうです。)

lb_argo(以前はlvs_schedでした)のアルゴリズムは、次のようなものがあります。(他にもあるのですが、よっぽどでない限り、下記以外ほとんど使わないと思います。)

rr
ラウンドロビン、順番にバックエンドに割り振っていく。weightは無視。
wrr
重み(weight)を加味したラウンドロビン。weightが高いものほど多く振る。
lc
コネクション数がもっとも少ないサーバを選択する。
wlc
重みとコネクション数によるサーバー選択をおこなう。

最後に

こんな感じで、設定完了です。あとは、

# service keepalived start

で、動くとうれしいですね!。

次回は、いよいよkeepalivedを使ったMySQLの冗長化にいってみたいと思います。

KeepAlivedのススメ

KeepAlivedとは

何ともいえないいい奴です。いぶし銀みたいな、安定性も高いし・・。って何の説明にもなっていないですね。KeepAlivedとは、レイヤ4ロードバランサ機能とVRRPによるフェイルオーバー機能を実装したOSSプログラムです。

簡単に言うと「アプリケーションをサーバーごとホットスタンバイで二重化・三重化できてしまうすぐれもの」です。例えば、MySQLを冗長化したり、Apacheを冗長化したり・・・。うちでも様々なところでお世話になっております。半年くらい前にPound+KeepAlivedで記事を書いたのですが、自分でもわかりにくいな・・と思ったので、今回は、KeepAlivedに焦点を当てて記事を書いてみます。

KeepAlivedでできる構成

こんな構成ができます。

KeepAlivedの仕組みについて1〜LVSについて〜

LinuxにはもともとLVS(Linux Virtual Server)というレイヤ4ロードバランサ機能を持っています。その前に、レイヤ4とは、OSI参照モデルのトランスポート層、俗に言うTCP層です。TCP層でロードバランスするということは、簡単に言うと、「IPアドレスとport番号を見て、誰に振るのか決める」みたいな感じです。

{レイヤ3ならばIP層ですので、IPアドレスまで(ルーターです。)、レイヤ2ならMACアドレスでふりわけ(所謂HUBです)します。}

また、話がそれてしまいました。KeepAlivedは、このLVSの機能を簡単に使えるようにラッピングしてくれています。どんなことができるのかと言うと、「ヴァーチャルIP(VIP)を新しく持って、そのIPのポート○○に来たパケットは、△△と、□□と、▲▲のサーバ達に振り分けよう」といった行動ができます。

ここで、勘の良い方は、「パケットをバラバラに分散させていたら、通信の連続性が保てないのじゃないか・・」なんて思いませんでしたか?。LVSはTCPヘッダまでを分析するので、ステートフル(通信の連続性)な通信が保てます。また、もちろん分散のさせかたも、ただのラウンドロビンから重み付け等までサポートしています。さらに、バックエンドのサーバーがダウンしたら、振らないなんて機能もついています。そこらのロードバランサ顔負けですね。

しかし・・LVSだけでは問題があります。「KeepAlived自体が落ちたらどうするの?」という問題も解決する力を持っています。

KeepAlivedの仕組みについて2 〜VRRPについて〜

KeepAlivedは、VRRP(ぶいあーるあーるぴー)というプロトコルで、お互い通信しあいます。この機能により、「一方のKeepAlivedが落ちたら、もう一方のKeepAlivedがマスタになって、業務を継続する」なんてことが、一瞬のうちに自動で行われます。だいたい、次のような動きです。

単純に書きましたが、マスタノードとなったKeepAlivedは、VRRPパケットを1秒おき(設定により変更可)でネットワーク上にマルチキャストアドレス宛に送信します。バックアップ(ホットスタンバイ)ノードのKeepAlivedは、このパケットが届く間は、待機しています。もし、マスタノードに障害が発生した場合は、バックアップノードにパケットが届かなくなります。3回連続で届かない場合、バックアップノードのうち、最もプライオリティ(優先度)の高いものが、「おれ、いまからマスタになるし。」というマルチキャストパケットを送出し、フェイルオーバーが行われます。

 設定によって、マスタが復旧した場合、マスタに自動フェイルバックさせるパターンと、復旧したらバックアップとしてもどるパターンを選択できます。

この機能、別に二台だけで行う機能ではありません。三台でも四台でも、同一セグメント内であれば複数台で動きます。ただし、マスタとして稼働するものは、一台だけです。(※誤解を招かないように追記します。KeepAlivedでは、グループを分けるための仮想ルーターIDというものがあります。これを利用して、同一セグメント内でも、機能別に複数のグループを作成することができます。この場合、同一セグメント内にマスタが複数稼働することになりますが、それぞれのグループ内では、マスタは一台だけです。)

KeepAlivedをインストールする

最新版は1.2.1のようです。www.keepalived.orgからダウンロードできます。ただし、もちろんソース公開です。./configure、make、make installすれば、インストールはできますが、折角なので、rpm化してservice追加してください。ソースのtarballを解凍すると、KeepAlived.spec.inという、specファイルが標準でついています。ラッキ〜。面倒臭いspecファイル記述作業が不要になりました。RPM化については、前回までの記事をご覧ください。ついでに、ipvsadm(CentならBaseレポジトリにipvsadmという名である)もインストールしておいてください。

KeepAlivedの設定について

ここから、次回の投稿とします。お楽しみに!

自作RPMの作成とレポジトリ3〜specファイルの書き方〜

specファイルの書き方は、一種のプログラムか、と思うぐらい複雑でした。今回は、specファイルの書き方を基本から書いてみます。

(独学ですので、間違いがあったらごめんなさい。ぜひ、御指摘お願いします。)

基本構造

基本構造は、次の通りです。(例としてphp5.2を使用・・デフォルトのものではありません。ご注意ください。)

Summary: PHP 5 - A powerful scripting language
Name: php5
Version: 5.2.13
Release: 5
Group: System Environment/Daemons
Source: php-%{version}.tar.gz
Vendor: PHP
BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
BuildPrereq: libjpeg-devel
BuildPrereq: libpng-devel
BuildPrereq: MySQL-shared
Requires: webserver
Requires: MySQL-shared
Requires: libmcrypt
Requires:libmcrypt-devel
Provides: php = 5.2.13
Provides: php-gd php-xml php-mbstring php-mysql php-bcmath

%description
korehatesutodesu

%prep
%setup -q -n php-%{version}

%build
# つくるくん。
./configure --with-apxs2=/usr/local/apache2/bin/apxs \
--enable-mbstring \
--enable-mbregex \
--with-gd \
--with-zlib \

make

%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local/apache2/conf
mkdir $RPM_BUILD_ROOT/usr/local/apache2/bin
mkdir $RPM_BUILD_ROOT/usr/local/apache2/modules
cp /usr/local/apache2/conf/httpd.conf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf

make install INSTALL_ROOT=$RPM_BUILD_ROOT 

rm -rf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf
rm -rf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf.bak

rm -rf $RPM_BUILD_ROOT/.channels
rm -rf $RPM_BUILD_ROOT/.registry
rm -rf $RPM_BUILD_ROOT/.depdb
rm -rf $RPM_BUILD_ROOT/.depdblock
rm -rf $RPM_BUILD_ROOT/.filemap
rm -rf $RPM_BUILD_ROOT/.lock

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)
/usr/local/apache2/modules/libphp5.so
/usr/local/bin/pear
/usr/local/bin/peardev
/usr/local/bin/pecl
/usr/local/bin/php
/usr/local/bin/php-config
/usr/local/bin/phpize
%config /usr/local/etc/pear.conf
/usr/local/include/php/*
/usr/local/lib/php/*
/usr/local/lib/php/.channels/*
/usr/local/lib/php/.channels/.alias/*
/usr/local/lib/php/.registry/*
/usr/local/lib/php/.depdb
/usr/local/lib/php/.depdblock
/usr/local/lib/php/.filemap
/usr/local/lib/php/.lock
/usr/local/man/man1/php*

%changelog
* Thu Oct 28 2010 tesutosakusei tekitou@tekitou.tek
- tesutodesu

個々の部位の説明1(はじめの部分・・パッケージ情報)

まず、はじめの部分は、非常に重要な値がてんこもりです。

Summary: PHP 5 - A powerful scripting language
Name: php5
Version: 5.2.13
Release: 5
Group: System Environment/Daemons
Source: php-%{version}.tar.gz
Vendor: PHP
License: PHP License

Summary :パッケージの簡単な説明(必ず一行で。)

Name:パッケージの名前(スペースやタブは不可)

Version:パッケージのバージョン(*.*.*の形式で書くのが普通。)

Release:パッケージのリリース番号(バージョンは変化しないが、コンフィグを変えてコンパイルしなおした場合等はここをインクリメント。1からスタート)

License:ライセンス情報(必須項目。わからなければ大抵はGPL。)

Group:適当でもいいが、yumのグループインストールをするならば、一意のグループ名が必要。

Source:Sourceのtarballの名称を書きます。rpmmacrosで設定しているので、標準ではSOURCESディレクトリ内のファイル名となります。http://~等でも構いません。(この場合は、インターネット接続が必須ですが・・)また、本来はSource0となります。0は省略可能です。さらに、Sourceが複数ある場合は、Source1、Source2・・と増やしていきます。下は例です。

Source0: php-%{version}.tar.gz
Source1: php.ini
Source2: php.conf

Vender:ベンダ名です。任意ですので、書かなくてもrpm作成できます。

※ほかのところでversion番号とかを使うときは、%{Version}で変数みたいに呼び出せます。(マクロ展開されます)%{Name}と%{Version}は良く使います。

個々の部位の説明1(はじめの部分・・パッケージ情報の続き)

はじめの部分の続きです。

BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
BuildPrereq: libjpeg-devel
BuildPrereq: libpng-devel
BuildPrereq: MySQL-shared
Requires: webserver
Requires: MySQL-shared
Requires: libmcrypt
Requires:libmcrypt-devel
Provides: php = 5.2.13
Provides: php-gd php-xml php-mbstring php-mysql php-bcmath

BuildRoot:RPM化するために仮想的にインストールするルートディレクトリです。特に問題無ければ、どのSPECファイルでも、「%{_tmppath}/%{name}-%{version}-buildroot」でいいかと思います。ちなみに%{_tmppath}はデフォルトで/var/tmpとなります。

BuildPrereq:コンパイルするのに必要なパッケージ名(機能名)を書きます。特に書かなくても問題ないですが、忘れないように書いておくといいです。注意するべき点は「このディレクティブは、rpmを作成するサーバーに必要なものを書くのであって、インストールする先のサーバーに必要なものでは無い」ということです。

Requires:こっちは、rpmをインストールするサーバーに必要なパッケージ名(機能名)を書きます。これを書くことで、依存関係を構築してくれます。バージョン**以上が必要等の書き方は、「Requires: httpd >= 2.2」のように書きます。

Provides:このパッケージが供給する機能名です。パッケージ名はデフォルトで機能名として登録されるので、パッケージ名だけでいい場合は、不要です。ただし、phpのように、コンパイルインストールすると、いろんな機能が一緒にインストールされる場合は、名前を追加する必要があります。この名前が、RequireとかBuildPrereqとかで使われる名前ともなります。また、バージョン番号を付ける場合は、「 php = 5.2.13」のように、=の前後に半角スペースを入れて書いてください。

個々の部位の説明2(description・prepセクション)

次から、各セクションの説明です。セクションは、大抵はじまりが%~~~で、終わりは次のセクション名か、ファイル終了です。(大抵と書いたのは、%setupのようにセクション名でないものもあるためです。)

%description
korehatesutodesu

%prep
%setup -q -n php-%{version}

%descriptionセクションは、パッケージの詳細情報を書くのに使います。yumコマンドで、閲覧できます(僕はあまり使わないですが・・)。また、ここは、次のセクションまで何行でも書くことができます。

%prepセクションは、コンパイル前の準備作業を書くセクションです。この場所には、%setupや%patch等が入ります。

%setupは、セクションではありません。これは、自動的にtarballをBUILDディレクトリに展開するコマンドです。-qで、展開時の情報を表示しないという設定、-n以降は、不要な場合も多いのですが、setupコマンドは、何も指定しないと、tarballを展開してできるディレクトリ名を%{Name}-%{Version}であると判断してコピーを行います。展開後のディレクトリ名がデフォルトと異なる場合は、-n以降で指定する必要があります。今回は、わざとNameをphp5としたので、展開した後の名前「php-5.2.13」と異なりますので、設定が必要です。

patchを当てるときは、ここで行いますが、次回まわしにします。また、%prepセクションでは、必要なソースを追加コピーしたり等も行うことがあります。

個々の部位の説明3(buildセクション)

このセクションは、コンパイルを行うセクションです。

%build
# つくるくん。
./configure --with-apxs2=/usr/local/apache2/bin/apxs \
--enable-mbstring \
--enable-mbregex \
--with-gd \
--with-zlib \

make

普通のコンパイルインストールとほとんど同じです。実は、%setupを行うと、カレントディレクトリが展開されてコピーされたディレクトリに移っているようなので、%buildセクションでは、パスの指定は通常不要です。また、make installはこの場所では行いません。(今回のせたのは、適当ですので、コンフィグ内容は適宜書き加えてください。)

個々の部位の説明4(installセクション)

このセクションは、仮想ルートに対してインストールを行うセクションです。

%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local/apache2/conf
mkdir $RPM_BUILD_ROOT/usr/local/apache2/bin
mkdir $RPM_BUILD_ROOT/usr/local/apache2/modules
cp /usr/local/apache2/conf/httpd.conf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf

make install INSTALL_ROOT=$RPM_BUILD_ROOT 

rm -rf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf
rm -rf $RPM_BUILD_ROOT/usr/local/apache2/conf/httpd.conf.bak

rm -rf $RPM_BUILD_ROOT/.channels
rm -rf $RPM_BUILD_ROOT/.registry
rm -rf $RPM_BUILD_ROOT/.depdb
rm -rf $RPM_BUILD_ROOT/.depdblock
rm -rf $RPM_BUILD_ROOT/.filemap
rm -rf $RPM_BUILD_ROOT/.lock

一行目の「rm -rf $RPM_BUILD_ROOT」はBuildRootで指定したフォルダを消す処理です。$RPM_BUILD_ROOTは%{BuildRoot}と同一ですが、こう書くのが普通のようです。

また、残りの部分でやっている内容は、php専用の特殊処理です。phpは、make installの際、apacheのconfigに追記しようと試みるので、無理矢理仮想インストール環境に、configを追加しました。あと、moduleのインストールを行ってもらうために、フォルダを作っています。

注意するのは「make install」です。普通にmake installを行うと、実環境の方にインストールされてしまいます。(不要なアプリがyum経由でなくインストールされてしまう。非常に問題。)このミスを防ぐためにも、「rootでコンパイルしない」というのが定石です。仮想環境にインストールするために、phpでは、「make install INSTALL_ROOT=$RPM_BUILD_ROOT」と書きます。大抵のものは、「make DESTDIR=$RPM_BUILD_ROOT install」と書くのですが、何故かphpは書き方が異なります。気を付けてください。

make installが終わった後の処理は、単純に不要なディレクトリとかのパッケージに入れないものを消しています。

(phpは結構いろいろしなければならなかったので、apacheの%installセクションの例をのせておきます。普通はこんな感じです。)

%install
#いつも書く。
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
#DESTDIRを入れないと、勝手にこの機体にインストールする。危険。 
make DESTDIR=$RPM_BUILD_ROOT install

個々の部位の説明5(cleanセクション)

このセクションは、パッケージングが終わったあとの処理を書くセクションです。

%clean
rm -rf $RPM_BUILD_ROOT

大抵これですが、ミスを防止するために、

%clean
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT

と書いた方がいいかもしれません。

個々の部位の説明6(filesセクション)

このセクションは、最も面倒臭いセクションであり、必ず書かなければならないセクションです。

%files
%defattr(-,root,root)
/usr/local/apache2/modules/libphp5.so
/usr/local/bin/pear
/usr/local/bin/peardev
/usr/local/bin/pecl
/usr/local/bin/php
/usr/local/bin/php-config
/usr/local/bin/phpize
%config /usr/local/etc/pear.conf
/usr/local/include/php/*
/usr/local/lib/php/*
/usr/local/lib/php/.channels/*
/usr/local/lib/php/.channels/.alias/*
/usr/local/lib/php/.registry/*
/usr/local/lib/php/.depdb
/usr/local/lib/php/.depdblock
/usr/local/lib/php/.filemap
/usr/local/lib/php/.lock
/usr/local/man/man1/php*

rpm化する場合は、ここに書かれたファイルがパッケージ化されます。注意するべき点は、「仮想ディレクトリにインストールされた(buildセクションで最後に残った)ファイルは全て書かなければならない」という点(この点は後述)と、「仮想ルートを基準としたパスで書くこと(実際にインストールされるパス)」です。

%defattr(-,root,root)は、デフォルト属性を設定します。特定のファイルだけパーミッションを変更したい場合は、%attr(755,apache,apache)みたいなものを、ファイルパスの前につけます。

また、%configを前に付けると、インストール時にもしファイルがある場合は、.rpmsaveを名前の後ろにつけてバックアップとして保存します。また、%config(noreplace)をつけると、インストール時にもしファイルがある場合は上書きもバックアップもせず、元のファイルを残します。

個々の部位の説明7(changelogセクション)

このセクションは、変更履歴を残すのに使います。

%changelog
* Thu Oct 28 2010 tesutosakusei tekitou@tekitou.tek
- tesutodesu

通常、タイトル行は「* (日時) (作成者) (メールアドレス) 」で、その下に「-メッセージ」と書きます。メッセージは何行でもいけるようです。 (たくさんある場合の例)

%changelog
* Thu Oct 28 2010 tesutosakusei tekitou@tekitou.tek
- tesutodesu
- tesutodesune
* Thu Oct 10 2010 tesutosakusei tekitou@tekitou.tek
- tesutodesuyo
- tesutodesuka
- tesutodesusi

最も問題になり得る「files」セクションのうまい方法について

実は、最初のころ、ひたすらはまりました。当然ですが、プログラムが何をどこにインストールしているかなんて、全て知っているわけないので・・。現在、僕は次のようにして対処しています。

1. とりあえずfilesセクションには何も書かずにspecファイルを編集して「rpmbuild -ba ***.spec」で、パッケージングしてみる。

2. 当然エラーするが、最後のパッケージングまでたどり着くと、エラー内容が、「未伸張のファイルが見つかりました」というエラーとともに、インストールされるべきファイルの一覧が表示される。

3. この一覧を元に、specファイルのfilesセクションを書いて、再度ビルド。

ミスなくいけるのですが、問題はコンパイルに時間のかかるものだと、二回コンパイルするので余計に時間を食います。他にいい方法があったら、ぜひ教えてください。

エラー内容の例をのせておきます。(a52decのコンパイル)

RPM ビルドエラー:
    インストール済み(ただし未伸張)ファイルが見つかりました:
   /usr/bin/a52dec
   /usr/bin/extract_a52
   /usr/include/a52dec/a52.h
   /usr/include/a52dec/attributes.h
   /usr/include/a52dec/audio_out.h
   /usr/include/a52dec/mm_accel.h
   /usr/lib64/liba52.a
   /usr/lib64/liba52.la
   /usr/man/man1/a52dec.1.gz
   /usr/man/man1/extract_a52.1.gz

次回は、インストール時にユーザーをつくるやり方とか、chkconfigに登録する方法とかをかけるといいな、と思っています。

Home > Archives > 2010-12

Search
Feeds

Return to page top