Home

tap dev blog

執筆者が増えました

最近、また更新が滞っていました。

そんな弊社ブログに、つ、ついに新しい執筆者が増えました。

次は、Cent7(RHEL7)系でのレポジトリの作り方と、Cent7でのKeepalived・DRBDについて記事を投稿予定です。

よろしくお願い申し上げます。

  • Comments (Close): 0
  • Trackbacks (Close): 0

MySQLのmysqlbinlogは使いづらいので、mysqlbinlogexを作ってみました。

仕事でしょっちゅうmysqlのバイナリログを掘っているのですが、mysqlbinlogが使いづらくて困っていました。なんせmysqlのバイナリログは、以下の問題があります。

・すぐサイズが大きくなるため、1つのDBのログを追うために、相当時間がかかる。

・mysqlbinlogコマンドから出るデータは、1行になっていないため、grepとかしづらい。

・バイナリデータ突っ込んだりしている場合は、文字化けて、続きが読めなくなったりする。

・mysqlbinlogコマンドも、1Gクラスのログだと、とにかく遅い。

・ログを追ったり調査するだけの人間からすると、無意味なログが多すぎる。

apacheのログみたいに、1行1リクエストになっていれば、grepですぐに引けるのですが、目的のクエリ探すのが、しんどい、しんどい。

また、うちは1システムで、1日1GBクラスのログがたまるため(バイナリアップロード分はほとんど入っていないにもかかわらず・・)ログローテートサイズを1Gにしているのですが、このログをmysqlbinlogコマンドで掘るのに時間がかかりすぎて・・

ついに、やってられなくなったため、木曜から2日かけて(やっつけですが)、以下の仕様で、バイナリログを閲覧するコマンドアプリを作ってみました。

・高速化のため、バイナリログを直接読み込む

・ログはgrepとか行いやすいように、「1行1クエリ」にする。(クエリ内改行は、スペース置換)

・クエリは、utf8mb4文字列以外であれば「?」に置換して、化けないようにする。

・DB(スキーマ)ごとにログをフォルダに振り分けて保存してくれる。

・時間フィルタとかも使いたい。

* 名付けて、mysqlbinlogexです。(パクリくさい名前ですね)

ソースはここ(github)に置いてあります。同じような悩みを抱えている方、一緒にブラッシュアップしていきませんか?(行ベースロギングには現時点(2014/05/24)では対応していません・・。)

なお、mysqlのバイナリログのフォーマットが、探しかたが悪いのか、どこを探しても断片的にしか無く、結局mysqlのソース追う羽目になったため、以下に調査内容をまとめておきます。


* mysqlのバイナリログフォーマットについて

1. 全体のおおまかなフォーマット

  ・ はじめの4byteは、必ず「0xfe 0x62 0x69 0x6e」。これがmysqlのバイナリログの証です。

  ・ その後は、次の形式のがループして最後まで続きます。

| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |

  ・ つまり、binlogファイルの中身は、以下のような感じです。

0xfe 0x62 0x69 0x6e
| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |
| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |
| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |
| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |
・・・・・

 2. 各クエリセグメントのフォーマット

・ 各クエリセグメントは、上記のように、以下の形式です。

| ログヘッダ(19Byte) | クエリとクエリステータス(可変) |

・ 19byteのログヘッダは、下記の内容を含んでいます。なお、すべてリトルエンディアンです。ここ(本家)が詳しいです。

| ts(4byte) | type(1byte) | masterID(4byte) | size(4byte) | master_pos(4byte) | flags(2byte) |

– ts(4byte):クエリ動作日時。unix_timestamp。

– type(1byte):0x0f(バイナリログ開始)、0x02(通常のステートメントログ)、0x17-0x19(rowベースステートメント)とか。重要

– masterID(4byte):読んで字の如く。自分の場合は、自分のID。

– size(4byte):このセグメントのバイト長。ログヘッダの19byteを含む。超重要

– master_pos:読んで字の如く。

– flags:普通のログは、0x00。その他は本家(上にリンクあり)参照。

・ 19byteのデータを読んだら、「クエリとクエリステータス」の長さが決まります。「クエリとクエリステータス」は以下のフォーマットです。

 | クエリヘッダ(13byte) | ステータスデータ(可変) | データベース名称(可変) | 0x00 | クエリ(可変) |

・ クエリヘッダは以下のフォーマットです。

 | thread_id(4byte) | exec_time_sec(4byte) | error_code(2byte) | status_size(2byte) |

– thread_id(4byte):クエリ実行者のスレッドID。同じであれば、同じコネクションで動作したことがわかる。

– exec_time_sec(1byte):クエリ実行時間

– error_code(2byte):エラーコードらしい。内容はあまりよくわからない。

– size(4byte):続きの「ステータスデータ」のバイト数。超重要

・ ここまで来て、やっと最も大事なDB名称とクエリの位置がわかります。データベース名称は0x00が入っているわけないので、ステータスデータが終わったところから、初めて0x00が来たところまでが、DB名です。空の場合もあります。(useしないで動作させると空になるようです。sequel proとか、navicatとかで操作した場合とか。)残りがクエリです。ただし、mysql5.6.1以降のバージョンが吐き出したバイナリログの場合、クエリ部の最後に、crc32チェックサムが付いています。このため、mysql5.6.1以上の場合は、終わり4byteを削ってあげる必要性があります。

・ クエリセグメントについて全部記載すると、以下のようになります。こいつが何度も繰り返されているイメージです。

| ts(4byte) | type(1byte) | masterID(4byte) | size(4byte) | master_pos(4byte) | flags(2byte) |
| thread_id(4byte) | exec_time_sec(4byte) | error_code(2byte) | status_size(2byte) |
| ステータスデータ(可変) | データベース名称(可変) | 0x00 | クエリ(可変) |

久しぶりに投稿しました。次は、いつになるだろ。予定は、「mysqlレプリケーションで、マスタが落ちた場合にスレーブを落とさなければならないときの注意事項」を書きたいと思います。(5.6.17で4台レプリ組んで、検証していたときにはまりました。)

全然関係ないですが、弊社では、現在、開発チームメンバを大々募集中です。このブログをこの位置まで読んでいただいた方、ぜひ応募してみてください。給与アップも(きっと)間違いなし(かも・・)。一緒にwebシステムのジェネラリストになりませんか?

  • Comments (Close): 0
  • Trackbacks (Close): 0

最近ご無沙汰していました。

長らく投稿していませんでした。ご覧頂いている方には大変申し訳ありません。

そろそろ書きたいことも山ほどたまってきましたので、再開したいと思います。

次のお題は・・・

 ・zabbix

 ・MySQL5.6

 ・php5.4とか

のいずれかでいきたいと思います。乞うご期待ください。

連休前には始めたいですね〜〜。

  • Comments (Close): 0
  • Trackbacks (Close): 0

Apacheのmod_proxy_balancerには、mod_proxy_httpが必要

題名の通りです。

mod_proxy_balancerで、バックエンドのapache数台(APサーバー)に振り分けをしようとしたのですが、マニュアル通りmod_proxyとmod_proxy_balancerだけで(、と依存関係書いてある)、コンパイルして、rpm化して、インストールして。・・・

config書いて、いざ、アクセス。

レスポンス 500

エラーしました。

なぜなのかわからず、奮闘数十分。やはり、モジュールが足りていないのであろうとの推測のもと、きっとmod_proxy_httpっぽいと予想してみます。

「DSO(動的共有オブジェクト)・・所謂mod_***.soファイルをLoadModuleするパターン」のときは必要との記事をやっと捜し当てて・・・それなら当然、自分でソースからコンパイルしてバイナリ組込みしていても必要ですよね・・必要でした。

マニュアルにも、そこのところしっかり書いてほしいですよね。

httpでバックエンドに投げる場合はmod_proxy_httpが必要です。
これは、mod_proxy_balancerを使用していても同様です。
mod_proxy_balancerは、リバースプロキシとして振る相手を分散させる機能だけを
有しているので、アクセスにはプロトコル別のアクセスラッパのモジュール
(例えばmod_proxy_httpやmod_proxy_ajpなど)が必要です。

たぶん、こんな感じのようだと思います。マニュアルに載っていればすぐにわかったのですが・・・。

MySQL5.5-MySQL5.6+KeepAlivedを使った、2台構成HA型準同期レプリケーションの作り方

MySQLは、レプリケーションという負荷分散にもってこいな機能があります。ただし、負荷分散はできるのですが、サーバーが故障やハングアップなどによるダウンしたときの「無停止化」をするのは、なかなか難しく、MySQLCluster等を使えば無停止化も可能なのですが、サーバー台数が多数必要であることと、join系のクエリに弱い等の問題があります。

今回は、そんなに負荷は高くない(一台で賄える)のだが、停止するのは困る、といったよくあるパターンでのレプリケーションをご紹介します。

当然ですが、使用する場合は、何の保証もしませんので、自己責任でお願いします。また、テストで使ったのはCentOS5.5 64bitです(他のでも問題ないと思います)。

通常のMySQLレプリケーションの問題点1

MySQL5.1までのレプリケーション機能では、マスタが停止した場合、クエリの損失という問題点が発生する場合がありました。次のような感じです。

1. スレーブが、マスタから最新のバイナリログ更新分を取得する。(更新クエリAは入っていない)

2. クライアントがマスタに更新クエリAを投げる。

3. マスタがクエリを実行し、クライアントに更新クエリAの終了を出す。

4. マスタが落ちる。

5. スレーブが再びマスタから最新のバイナリログを要求するが、落ちているので取得できない。

この状態で、スレーブを手動にてマスタに昇格させると、更新クエリAは当然反映されません。クライアントから見ると、更新クエリは受け付けされて実行されているはずにも係わらず、です。これが良く言われている非同期レプリケーションの問題点でした。

これを解決するのが、準同期レプリケーション(MySQL5.5から採用)です。上記の場合、次のような挙動をします。

1. スレーブが、マスタから最新のバイナリログ更新分を取得する。(更新クエリAは入っていない)

2. クライアントがマスタに更新クエリAを投げる。

3. マスタがクエリを実行し、スレーブに更新クエリAを渡しに行って、渡し終わったら、クライアントに更新クエリAの終了を出す。

4. マスタが落ちる。

準同期では、クエリのログをスレーブに渡し終わった後に、クライアントに終了を返すので、もし、この間にマスタが落ち、スレーブにクエリが渡っていなかったとすると、クライアント側からは、クエリが失敗したように見え、整合性がとれるという訳です。

今回は、これを採用し、レプリケーションにてクエリの不整合がほぼ起こらない状態での自動フェイルオーバーを組みます。

通常のMySQLレプリケーションの問題点2

そもそも、MySQLは単体でフェイルオーバーに対応していません。マスタが落ちた場合は、スレーブを手動にてマスタに昇格させるしか手がありません。これは、夜も枕を高くして寝ていたいサーバー管理者にとっては頭痛の種です。今回は、この点をKeepAlivedによってカバーします。

通常のMySQLレプリケーションの問題点3

MySQLをフェイルオーバーさせ、スレーブをマスタに昇格させた際、頭の痛い問題が「マスタの故障がなおった」場合にどうするかです。思いつく手段は、「一旦止める」ですが、うれしくありません。特に、手動にて昇格させた場合は、mysqlのマニュアルのような手(reset masterを実行して、全ロックしてスナップショットを取る)がありますが、自動フェイルオーバーされてしまったら、どうしようもありません・・。今回はこんな問題に足を踏み入れ、毎日のバックアップと連携させて無停止復旧をおこなえるようにします。

概念

こんな感じです。今回の構成では、一台はホットスタンバイ兼バックアップ作成用です。一台で参照、更新のクエリを受け付けます。(keepalivedのconfigを変更して、参照をもう一台に振る形もできますが、そっちはそのうち。今回はシンプルにいきます。)

[サーバA]:実IP 192.168.0.1 仮想IP 192.168.0.10 (MySQLマスタ)
[サーバB]:実IP 192.168.0.2(MySQLスレーブ)

仮想IPの3306ポートで、MySQLとアクセスします。間違っても実IPでアクセスしてはダメです。

サーバAがダウンすると仮想IPがKeepAlivedのVRRPにて自動的に振り変わり、次のようになります。

[サーバA(死亡)]:実IP 192.168.0.1(MySQL停止)
[サーバB]:実IP 192.168.0.2 仮想IP 192.168.0.10 (MySQLマスタ)

フェイルバックを行うと、次のようにサーバAをサーバBのスレーブにします。

[サーバB]:実IP 192.168.0.2 仮想IP 192.168.0.10 (MySQLマスタ)
[サーバA]:実IP 192.168.0.1 (MySQLスレーブ)

実装方法(mysql編)

セットアップは至って単純です。

1. MySQL5.5.8以降のMySQLを入れたサーバーを二台用意する。(サーバAとサーバBとします)

2. mysql_secure_installationとかを終わった後に、mysqlクライアントからmysqlにログイン。

3. 次の2つを走らせて、準同期レプリケーションを使えるようにする。

INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

4. my.cnfの設定を行う。(*注意点だけ書いておきます。)

# 当然、外部アクセスさせる場合はポート指定。
port = 3306
socket = /var/lib/mysql/mysql.sock
# default-character-set=utf8は、5.5以降はエラーするので注意。
character-set-server = utf8
# よくあるおまじない。(文字化け防止)
skip-character-set-client-handshake

#ログファイルの名前は何でもいいが、今回は説明のためのデフォルト。
log-bin = mysql-bin
relay-log = relay-bin
#お好みで。
slow_query_log
#サーバ毎に一意に振る。今回の初期値は1(サーバA)と2(サーバB)(今回の作戦では、変更かけなければならない場合がある)
server-id = 1
# これが大事。レプリケーションで取得したログをバイナリログにも書き込む。(後述しますが、今回の作戦には必須です)
log_slave_updates
# 勝手にスレーブ起動はいや。
skip-slave-start
# セミシンクロ(準同期)レプリケーションを許可。(マスタはマスタだけ、スレーブはスレーブだけではなく、どっちにもどっちも書く。)
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_slave_enabled = 1

これでmysqlを再起動。

実装方法(KeepAlived編)

こちらも至って簡単です。

1. KeepalivedをサーバAとサーバBに入れる。

2. configを設定した後、keepalivedは止めておく。

keepalived.confは、ちょっとした細工をしてあります。MySQLが停止していたら、KeepAlivedを落とす処理が必要です。(KeepAlived側からMySQLを監視する)

例えば、次のような感じで。(Cronで監視しても問題ないですが、1分間隔はちょっと遅いので)

! Configuration File for keepalived

# フェイルオーバーした際のalertメールを送るための設定(無くても可)
global_defs {
    # メールの送り先
    notification_email {
        test@korehatest.jp
    }
    # メールの送信元アドレスと、メールサーバー
    notification_email_from KeepAlived@korehatest.jp
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    # サーバAでは、mysqlsvrA、サーバBではmysqlsvrBとかにしておくと、メールが来たときわかりやすい。
    router_id mysqlsvrA
}

#グローバル側
#[vrrp_instance ][好きな名前]
vrrp_instance VI_1000 {
    # マスタサーバー
    state BACKUP
    # VIPを割り当てるNIC
    interface eth0
    #今起動しているのがなければ、マスタになり、マスタがいれば、スレーブ。
    nopreempt
    # ネットワークが混雑していて、スイッチがARP信号を処理できない場合は、ARPを少し送らせて送信する設定。
    # 無くても動く。動作が変な場合(VIPを引き継いだのに、外部から認識できない現象が出たら、こいつを設定。)
    # 弊害として、VIPの引継完了が遅くなる。(デフォルトは引き継いだら即ARP信号の送信。)
    #    garp_master_delay 1
    # 仮想ルーターのID(VRRP信号を共有する他サーバーのインスタンス間で共通にする。)
    virtual_router_id 11
    # 優先度(MasterはBACKUPよりも高く設定。今回は一緒)
    priority 100
    # VRRP信号を送出する間隔
    advert_int 1
    #メール通知
    smtp_alert
    # VRRP信号を受け取る際のパスワード等(同じvirtual_router_idならば、同じにする。)
    authentication {
        auth_type AH
        auth_pass test@test
    }
    # このインスタンスで管理するVIP(同じvirtual_router_idなら、同じにする。※NICは同じ必要ない。)
    virtual_ipaddress {
        192.168.0.10/24 dev eth0
    }
}

#監視のためにつくってある。
#マスタ側
virtual_server 127.0.0.1 3307 {
    #監視の間隔
    delay_loop 5
    #rrラウンドロビン、
    lb_algo rr
    #DR:DSR構成、NAT:なっと
    lb_kind DR
    protocol TCP
    #マスタはこいつだけ。
    # サーバAでは192.168.0.1、サーバBでは192.168.0.2にする。
    real_server 192.168.0.1 3306 {
        weight 10
        TCP_CHECK {
            connect_port 3306
            connect_timeout 30
        }
        #もしマスタmysqlが止まったときに呼び出される処理。keepalivedを落とす。(後述)
        notify_down "/usr/local/keepscript/shutdown_keepalived.sh"
    }
}

スクリプト「shutdown_keepalived.sh」は次のような感じで。(生きているかどうかだけなので、何でチェックしてもかまいません。)

#!/bin/bash
####
#もしマスタが落ちたときの処理スクリプト
#とりあえず最終確認して、本当にダメだったらkeepalivedを落とす。
####
#ちょっと待つ
sleep 5
#本当にmysqlが死んでいるかどうかの確認
mysqlans=`mysql -u hoge -phogehoge -h 192.168.0.1 < /usr/local/keepscript/showmaster.sql | grep Bytes_received `
#もし、本当に死んでいるときは、keepalivedを落とす。
if [ "" = "$mysqlans" ]; then
    /etc/rc.d/init.d/mysql stop
    /etc/rc.d/init.d/keepalived stop
fi

「showmaster.sql」はこれ!(これは酷い・・)

show status;

スクリプト「shutdown_keepalived.sh」は単純に最終確認として、show status;を行って、結果が帰ってこなかったら、mysqlとkeepalivedを停止させるという処理です。

mysqlはとまっているはずですが、ハングアップとかの場合も考えて、こっちも落とす処理をしておきます。

実稼働させるには

まず、レプリケーションを開始させなければなりません。

新規データベースでいくなら、次の感じですね。

サーバAのmysqlにて

GRANT REPLICATION SLAVE ON *.* TO hoge@'192.168.0.%' IDENTIFIED BY 'hogehoge';

サーバBのmysqlにて、

change master to master_host='192.168.0.1',master_user='hoge',master_password='hogehoge',master_log_file='mysql-bin.000001',master_log_pos=107;
start slave;

これでOK。準同期も非同期も同じです。準同期が行われているかどうかを確認するには、マスタ側で

show status like 'Rpl%';

+--------------------------------------------+-------------+
| Variable_name                              | Value       |
+--------------------------------------------+-------------+
| Rpl_semi_sync_master_clients               | 1           |
| Rpl_semi_sync_master_net_avg_wait_time     | 0         |
| Rpl_semi_sync_master_net_wait_time         | 0      |
| Rpl_semi_sync_master_net_waits             | 0         |
| Rpl_semi_sync_master_no_times              | 0           |
| Rpl_semi_sync_master_no_tx                 | 0        |
| Rpl_semi_sync_master_status                | ON          |
| Rpl_semi_sync_master_timefunc_failures     | 0           |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0         |
| Rpl_semi_sync_master_tx_wait_time          | 0       |
| Rpl_semi_sync_master_tx_waits              | 0         |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0           |
| Rpl_semi_sync_master_wait_sessions         | 0           |
| Rpl_semi_sync_master_yes_tx                | 0         |
| Rpl_semi_sync_slave_status                 | OFF         |
| Rpl_status                                 | AUTH_MASTER |
+--------------------------------------------+-------------+

みたいに、「Rpl_semi_sync_master_clients」が1になっていればOKです。

スレーブ側でも、「show slave status\G;」とかで、レプリケーションの確認を行っておいてください。

レプリケーションが始まったら、KeepalivedをサーバAで起動、サーバBで起動の順に起動して、サーバAに仮想IPが振られていることを確認してください。

動作について

ここまでの設定で、フェイルオーバーは自動的に行われるようになっています。次のように動きます。

1. サーバAがクライアントからのクエリを受け付けている。

2. サーバAのMySQLがハングアップ

3. クライアントからのクエリが失敗しはじめる。

4. サーバAのKeepAlivedが監視失敗して、スクリプトを起動。(アラートメールが飛ぶ)→サーバAのmysqlとKeepalivedを停止。

5. サーバBのKeepAlivedがサーバAのKeepAlivedが停止したことを確認し、仮想IPを引き継ぐ。(アラートメールが飛ぶ)

6. サーバBのMySQLが自動的に仮想IPの3306ポートで待ちはじめる。(仮想IPを引き継いだら勝手に行う)

7. クライアントからのクエリがサーバBに行くようになり、クエリが成功するようになる。

だいたい、フェイルオーバー時のダウンタイムが数秒です。これでもかなりいい気がするのですが、このままでは、冗長構成に戻せません。

バックアップ作成について

このシステムの第二の肝となるのが、バックアップです。これをうまく利用することで、安心と信頼のサーバーになります(多分)

バックアップはスレーブ側で行います。(スレーブは、止めても問題ないためです。)

crontab -e
00 04 * * * /usr/local/mysqlbackup/backup_mysql.sh

みたいにスケジュールして、スクリプトは

#!/bin/bash
/etc/rc.d/init.d/keepalived stop
/usr/sbin/tmpwatch -m 50 /usr/local/mysqlbackup/backupdata
svr_name=`uname -n`
/etc/rc.d/init.d/mysql stop
sleep 3
daystr=`date -d '1 days ago' '+%Y%m%d'`
cd /var/lib/mysql
tar czvf /usr/local/mysqlbackup/backupdata/$svr_name$daystr.tgz ./* > /dev/null 2>&1
/etc/rc.d/init.d/mysql start
sleep 3
/etc/rc.d/init.d/keepalived start
exit

みたいな感じで。(自分で使っている物は、これにレプリケーションが止まっていないかや、stop slaveして、クエリが追い付くまで待つ等のチェックを加えています。)

ポイントは特に無いのですが、バイナリログも全部バックアップしているところでしょうか。このため、結構重くなる可能性があるので、別スクリプトか、同じスクリプトで、

バイナリログを最新のひとつだけ残して、後は消してしまうなんていうことも書いておくといいかもしれません。

また、スレーブのHDDがアクセス不能になったときに備えて、バックアップは他のサーバーにコピーしておいた方がいいかもしれないです。

サーバAが停止したときのフェイルバック動作について

今回の肝2となる点です。サーバAはサーバBのスレーブにしたいので、まず、サーバBにて

stop slave;
change master to master_host='192.168.2.1',master_user='dummy',master_password='dummy',master_log_file='mysql-bin.000001',master_log_pos=107;

みたいにして、スレーブ動作を行えないようにしておきます。(二行目は、ダミーアドレス・ダミーユーザーです。本来必要ないのですが、誤って双方向レプリケーションにならないように、ダミー設定しておくことをお薦めします。)

次に、サーバAで、mysqlを立ち上げる前に、

cd /var/lib/mysql
rm mysql-*
rm relay-*
rm master.info
rm ib*

として、すべてのデータを一旦削除します。(MyISAMを使っているときは、データベースフォルダも削除)

そして、毎日行っているバックアップファイルをコピーしてきて(以下はサーバAでの動作)

scp 192.168.0.2:/usr/local/mysqlbackup/backupdata/***20110101.tar.gz ~/
tar xzvf ***20110101.tar.gz

データベースファイルだけを/var/lib/mysql下にコピーします。(innoDBなら、ib*のように、ログファイルも。MyISAMなら、データベースフォルダをすべて。)

次に、どこまで進んでいる分かをmysqlbinlogにて確認します。

# 必ずバイナリログの最終ファイルを見ること(今回はmysql-bin.000005だったと仮定します)
mysqlbinlog mysql-bin.000005

ずらずらと流れるバイナリログのうち、最後の方で、

# at 468263
#110111 12:16:27 server id 60  end_log_pos 468290       Xid = 4267
COMMIT/*!*/;
# at 468290
#110111 14:58:40 server id 162  end_log_pos 468309      Stop
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;

みたいなDELIMITER行があると思います。この直前の # at が最終ログポジションなので、上記の場合は、468290になります。

最後に、my.cnfのserver-idを変更してmysqlを起動します。

#サーバ毎に一意に振る。今回の初期値は1(サーバA)と2(サーバB)(今回の作戦では、変更かけなければならない場合がある)
#スレーブに移行したときは、サーバーIDを1→11に変更
server-id = 11

サーバーIDを変更するのは、理由があります。(後述。すごい大事。忘れると、レプリケーションエラーする可能性非常にたかい。)

ここまで準備ができたら、サーバAをサーバBのスレーブに設定するだけです。先ほどログを見てメモしたログファイルとポジションを使って、

change master to master_host='192.168.0.2',master_user='hoge',master_password='hogehoge',master_log_file='mysql-bin.000005',master_log_pos=468290;
start slave;

これで、元マスタだったサーバAがスレーブになりました。忘れずにKeepAlivedを起動しましょう。(KeepAlivedの起動は最後の最後です。また、コンフィグの書き換えは不要です。)

サーバBが停止したとき

大抵の場合は、再起動かけて、mysqlを起こして、start slaveするのとkeepalivedを起こしてしまえば復旧します。エラーするようなら、バックアップファイルから、スレーブを作りなおしてください。

サーバーIDを変更する理由

ここにつまづいて、二日ぐらい悩みました・・。

レプリケーションのログや、バイナリログは、サーバーIDというものが書き込まれています。これは、巡回レプリケーション(例えばマルチマスタとか)を組んだ場合に、「レプリケーションで取得してきたログのうち、自分のサーバーIDのものは実行しない」という規則のために存在します。これがない場合、マルチマスタとかで、log_slave_updatesをかけると、ログが永遠に巡回してしまい、一回のinsert命令が何度も実行される、なんていうことになります。

本来、上記の機能は有効な機能であり、問題ないのですが、今回のようなパターンのときには問題が発生します。

たとえば、サーバA(ID:1)がマスタ、サーバB(ID:2)がスレーブを初期状態として、次のような場合です。

1. サーバーB上で、バックアップを取る。(binlogポジション5000番までとする)

2. サーバAがしばらくクエリを受け付けていたが、突然落ちた。(このとき、サーバーB上のbinlogポジションを8000番とする)

3. サーバーBがフェイルオーバーして、クエリを受付始める。

4. サーバーAが復旧できたので、上記の手順で、サーバBのスレーブとして復旧させる。(このとき、サーバーBのbinlogポジションを15000番とする。)

おわかりの方も多いと思うのですが、4でサーバAが復旧したとき、サーバBから取得した、バックアップを使いますので、サーバAは、binlogポジション5000番の状態です。レプリケーションを開始すると、サーバBのbinlogポジションの15000番まで追い付こうとします。ところで、この間のbinlogに記録されているserver-IDは、次のようになります。

5000〜8000 : ID 1
8000〜15000 :ID 2

もし、サーバAのserver-IDを変更せずにスレーブとして復旧させた場合、5000番〜8000番のbinlogは、自分と同じserver-IDなので、mysqlは巡回してきたログであると判断し(つまり、既に実行済みのクエリだと判断し)、読み飛ばします。

このため、レプリケーションが追い付いた時点で、一部のクエリがそっくり抜けた状態になってしまいます。(そもそもレプリケーションが追い付く前にエラーするかもしれません。)

これを防ぐには、サーバーIDを変更する必要があります。

終わりに

いかがでしたでしょうか。単純な仕組みですが、非常に高効率でHA構成が組めると思います。実測での停止時間ですが、次のようなかんじでした。

・バックアップ時にスレーブを落としたとき、準同期レプリケーションが止まってしまうため、このときにクエリを発行していたら失敗する分(発行していなければダウンタイム無し。)
    →数秒
・マスタが停止したときのフェイルオーバ動作
    →最大約10秒(大抵5秒位)

サービス停止時間がたったこれだけとは、驚きですよね・・きっと。

問題点は、つぎの点ぐらいです。

「バックアップやってる最中にマスタが落ちる」ときがどうすることもできない点(バックアップ開始時からマスタ停止時までのクエリが飛ぶ)
二台同時、もしくはフェイルオーバーしてからフェイルバックする前に落ちたら終わり。(二台では限界ですね。)

疑問点や、間違いの御指摘等、ぜひお願いします!

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に登録する方法とかをかけるといいな、と思っています。

自作RPMの作成とレポジトリ2〜RPMパッケージをyumで使う〜

前回のコンパイル環境で、rpmを作ることはできるのですが、yumにてインストールはできません。(できないことは無いのですが・・)

今回は、yumのレポジトリの作成について書きたいと思います。

yumのレポジトリ作成の前に

 yumにてインストールするためには、rpmにサインしなければなりません。(無くてもできますが、あったほうがいいということで・・)

サインするためには、署名をつくる必要があります。とりあえず、ユーザーはnaviでやってみます。。

まずはnaviでログインします。rootからsuで移っていると、鍵生成の段階でキャンセルされるので注意。

# mkdir .gnupg
# gpg --gen-key

2行目のやつで、鍵作成がスタートします。

gpg (GnuPG) 1.4.5; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.

ご希望の鍵の種類を選択してください:
   (1) DSAとElgamal (既定)
   (2) DSA (署名のみ)
   (5) RSA (署名のみ)
選択は? 

既定でいいのでEnter。

DSA keypair will have 1024 bits.
ELG-E keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 

これも既定でいいのでEnter。

要求された鍵長は2048ビット
鍵の有効期限を指定してください。
         0 = 鍵は無期限
      <n>  = 鍵は n 日間で満了
      <n>w = 鍵は n 週間で満了
      <n>m = 鍵は n か月間で満了
      <n>y = 鍵は n 年間で満了
鍵の有効期間は? (0)

これもまた既定でいいのでEnter。

Key does not expire at all
これで正しいですか? (y/N) 

ここは,yを入れてEnter。

あなたの鍵を同定するためにユーザーIDが必要です。
このソフトは本名、コメント、電子メール・アドレスから
次の書式でユーザーIDを構成します:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

本名:
電子メール・アドレス: 
コメント: 

署名の本名/アドレス/コメントはRPMパッケージを公開するときは、公開されます。5文字以上の任意の名前をいれます。

次のユーザーIDを選択しました:
    “test_user (tesutodesu) <test@test.local>”

名前(N)、コメント(C)、電子メール(E)の変更、またはOK(O)か終了(Q)? 

問題無ければ、oでEnter。このユーザーIDは、後で使います。

秘密鍵を保護するためにパスフレーズがいります。

パスフレーズは、これから署名を行うときにも何度も聞かれます。忘れないものを付けておいてください。

二回パスフレーズを入力すると、乱数生成が始まります。

今から長い乱数を生成します。キーボードを打つとか、マウスを動かすとか、ディスクにアクセスするとかの他のことをすると、乱数生成子で乱雑さの大きないい乱数を生成しやすくなるので、お勧めいたします。
..++++++++++....+++++++++++++++.++++++++++.++++++++++.++++++++++++++++++++.++++++++++++++++++++++++++++++++++++++++..++++++++++++++++++++++++++++++.+++++..+++++>.++++++++++>+++++>+++++...............<+++++..>.+++++..........................................................................>.+++++..<.+++++.........................<+++++...........................+++++^^^^^
十分な長さの乱数が得られません。OSがもっと乱雑さを収集できるよう、何かしてください! (あと284バイトいります)

大抵、こんな感じで怒られるので、キーボードを叩きまくるといいです。

gpg: /home/navi/.gnupg/trustdb.gpg: 信用データベースができました
gpg: 鍵604D4506を絶対的に信用するよう記録しました
公開鍵と秘密鍵を作成し、署名しました。

gpg: 信用データベースの検査
gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、PGP信用モデル
gpg: 深さ: 0  有効性:   1  署名:   0  信用: 0-, 0q, 0n, 0m, 0f, 1u
pub   1024D/604D4506 2010-11-03
                 指紋 = 3F9C DBC8 4B26 0866 FA65  15C2 CDB3 2644 604D 4506
uid                  test_user (tesutodesu) <test@test.local>
sub   2048g/AB9F9E3B 2010-11-03

できました。最初に作った.gnupgフォルダにいろいろ入っています。

最後に、前回も使った.rpmmacrosファイルに情報を追記します。

# vi .rpmmacros
%topdir /home/navi/mkrpm
%_gpg_path /home/navi/.gnupg
%_gpg_name test_user (tesutodesu) <test@test.local>
%_gpgbin /usr/bin/gpg
%_signature     gpg

下四行を追記します。_gpg_nameは、先ほどつけたユーザーIDをコピペしてください。

最後に、.gnupgフォルダのパーミッションを700に変更しておいてください。

#chmod 700 -R .gnupg

次に署名したものを外から検証するための、公開鍵を作ります。

# gpg -a --export "test_user" > RPM-GPG-KEY-TEST

出力ファイルの名前は何でもいいのですが、” ”でくくられた部分には、鍵作成のときに付けた本名が必要です。

このファイルは、各クライアントに配布する必要があります。(後述)

署名のしかた

1.どこかから持ってきたrpmに署名するとき

# rpm --resign ***.rpm

2.自分が作ったrpmに署名するとき

# rpm --addsign ***.rpm

ついでに、フォルダの下のすべてを署名するときは、

# rpm --resign ~/rpms/*

でワイルドカード指定できます。

レポジトリを立てる。

必要なものはhttpサーバーと、createrepoパッケージです。今回は、apacheを入れることを前提に書きます。

rootで、

# yum install httpd
# yum install createrepo

centOSとかでは、apacheのデフォルトの公開ディレクトリは/var/www/htmlになると思います。ここに、レポジトリ用フォルダを作っておいてください。

# mkdir /var/www/html/myrepo
# chown navi:navi /var/www/html/myrepo

レポジトリフォルダの所有者をnaviに変えておくといいです。

suにてnaviになって、rpmをレポジトリフォルダにどんどん入れてください。

そして、公開する前に

# createrepo /var/www/html/myrepo

とすると、レポジトリができあがります。(apacheは起動しておいてください。)

クライアント側のための設定

最後に、レポジトリをyumから読ませる方法です。クライアントPCにて、

# cd /etc/yum.repos.d/
# vi local.repo

#レポジトリの名前
[test_repo]
#レポジトリの表示名
name=test_repo
#レポジトリの場所(ftpかhttp、同一PCだけならfileでもいける。)
baseurl=http://192.168.0.1/myrepo/
#署名のチェックを行うかどうか
gpgcheck=1
#このレポジトリがデフォルトで有効かどうか。
enabled=1
#署名の公開キーの場所。
gpgkey=file:///etc/yum.repos.d/RPM-GPG-KEY-TEST

で、/etc/yum.repos.d/下にlocal.repoファイルを作り、同一フォルダに、先ほどの署名の公開鍵をscpとかして置きます。

これで、yumコマンドでレポジトリを観に行ってくれるようになります。

インストールとかの一回目は、キーを取り込んでもいいか聞かれるはずです。

Home

Search
Feeds

Return to page top