2012年5月14日月曜日

"lsyncd"を複数ディレクトリに適用

スズキです。

下記のように"lsyncd"を紹介してきましたが、適用対象は単一のディレクトリでした。
今回は、複数のディレクトリに対して適用する方法を紹介します。

といっても、下記のように設定ファイル(/etc/lsyncd.conf)で、
"sync"ブロックを複数記述するだけで実現します。
settings = {
    logfile    = "/var/log/lsyncd.log",
    statusFile = "/var/log/lsyncd.status",
    nodaemon   = true,
}

s3sync = {
    maxProcesses = 1,
    onStartup = "s3cmd -c /root/.s3cfg -P -r --delete-removed sync ^source ^target/",
    onCreate  = "s3cmd -c /root/.s3cfg -P put ^source^pathname   ^target^pathname",
    onModify  = "s3cmd -c /root/.s3cfg -P put ^source^pathname   ^target^pathname",
    onDelete  = "s3cmd -c /root/.s3cfg    del                    ^target^pathname",
    onMove    = "s3cmd -c /root/.s3cfg    mv  ^target^o.pathname ^target^d.pathname",
}

sync{
    s3sync,
    source = "/tmp/src1",
    target = "s3://www.suz-lab.com/tmp1",
}

sync{
    s3sync,
    source = "/tmp/src2",
    target = "s3://www.suz-lab.com/tmp2",
}

そろそろ、"Write Proxyパターン"としてまとめるか...
--------
http://www.suz-lab.com

"lsyncd"でS3と同期

スズキです。

こちらで"lsyncd"のインストールができたので、
CentOS6.2に"lsyncd"をインストール
次はS3との同期を試してみます。

といっても、"/etc/lsyncd.conf"を下記のようにするだけです。
settings = {
    logfile    = "/var/log/lsyncd.log",
    statusFile = "/var/log/lsyncd.status",
    nodaemon   = true,
}

s3sync = {
    maxProcesses = 1,
    onStartup = "s3cmd -c /root/.s3cfg -P -r --delete-removed sync ^source ^target/",
    onCreate  = "s3cmd -c /root/.s3cfg -P put ^source^pathname   ^target^pathname",
    onModify  = "s3cmd -c /root/.s3cfg -P put ^source^pathname   ^target^pathname",
    onDelete  = "s3cmd -c /root/.s3cfg    del                    ^target^pathname",
    onMove    = "s3cmd -c /root/.s3cfg    mv  ^target^o.pathname ^target^d.pathname",
}

sync{
    s3sync,
    source = "/tmp/src",
    target = "s3://www.suz-lab.com/tmp",
}
当然"s3cmd"のインストールは下記のように行なっておきます。
# yum -y install s3cmd
"lsyncd"再起動後、対象のディレクトリ(/tmp/src)にファイルを追加・変更・削除を行うと、
同様の操作が対象のS3(s3://www.suz-lab.com/tmp)にも反映されていることがわかります。

対象ファイルの制限やS3側のアクセスコントロールなどは、"s3cmd"の設定となります。

FTPなどと連携すれば、"Write Proxyパターン"でしょうか?
--------
http://www.suz-lab.com


2012年5月13日日曜日

CentOS6.2に"lsyncd"をインストール

スズキです。

以前も試したことがある"lsyncd"ですが、
CentOSで"lsyncd"を使った同期
その時はサブディレクトリには対応していなかったりと、あまり使い勝手が
よくなかったのですが、現在は対応しているようなので、再度試してみました。

利用したCentOSはこちらのものとなります。
SUZ-LAB謹製 CentOS AMI (6.2.1 64bit ap-northeast-1)
まずはインストールですが、"yum"で簡単に入ります。
# yum -y install lsyncd
次に下記のような設定ファイル(/etc/lsyncd.conf)を用意します。
settings = {
   logfile    = "/var/log/lsyncd.log",
   statusFile = "/var/log/lsyncd.status",
   nodaemon   = true,
}

echo = {
    maxProcesses = 1,
    onStartup = "/bin/echo telling about ^source", 
    onAttrib  = "/bin/echo attrib ^pathname",
    onCreate  = "/bin/echo create ^pathname",
    onDelete  = "/bin/echo delete ^pathname",
    onModify  = "/bin/echo modify ^pathname",
    onMove    = "/bin/echo move ^o.pathname -> ^d.pathname",
}

sync{
    echo,
    source = "/tmp/src/",
    target = "/tmp/dst/",
}
これは対象のディレクトリでファイル操作をした時に、
標準出力に情報を出力するだけの設定となります。

この状態で"lsyncd"を起動します。
# lsyncd /etc/lsyncd.conf 
17:23:09 Normal: Event Blanket spawns action '/bin/echo telling about ^source'
telling about /tmp/src/
17:23:09 Normal: Startup of '/tmp/src/' finished.
対象ディレクトリにファイルを作成すると、
# touch /tmp/src/sample.txt
下記のようなログが出力されます。
17:24:40 Normal: Event Create spawns action '/bin/echo create ^pathname'
create /sample.txt
17:24:40 Normal: Finished Create on /tmp/src//sample.txt = 0
17:24:40 Normal: Event Modify spawns action '/bin/echo modify ^pathname'
modify /sample.txt
17:24:40 Normal: Finished Modify on /tmp/src//sample.txt = 0
サブディレクトリに対してファイルを作成しても、
# touch /tmp/src/sub/sample.txt
17:24:48 Normal: Event Create spawns action '/bin/echo create ^pathname'
create /sub/sample.txt
17:24:48 Normal: Finished Create on /tmp/src//sub/sample.txt = 0
17:24:48 Normal: Event Modify spawns action '/bin/echo modify ^pathname'
modify /sub/sample.txt
17:24:48 Normal: Finished Modify on /tmp/src//sub/sample.txt = 0
同様にログが出力できていることが確認できます

最後に下記のように起動スクリプトを作っておけば、
起動時に"lsyncd"を有効にすることも可能です。
#!/bin/bash
#
# lsyncd        lsyncd
#
# chkconfig: 2345 99 10
# description: lsyncd
  
# Source function library.
. /etc/init.d/functions
  
prog=lsyncd
lock=/var/lock/subsys/$prog
pid=/var/run/lsyncd.pid
  
# Source config
if [ -f /etc/sysconfig/$prog ] ; then
    . /etc/sysconfig/$prog
fi

start() {
    echo -n $"Starting $prog: "
    $prog -pidfile $pid /etc/lsyncd.conf >> /var/log/lsyncd.log 2>&1 &
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch $lock
    return $RETVAL 
}

stop() {
    echo -n $"Shutting down $prog: "
    kill `cat /var/run/lsyncd.pid`
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f $lock
    [ $RETVAL -eq 0 ] && rm -f $pid
    return $RETVAL
}

case "$1" in
    start)
        start
        RETVAL=$?
        ;;
    stop)
        stop
        RETVAL=$?
        ;;
    restart)
        stop
        start
        RETVAL=$?
        ;;
    status)
        status -p $pid -l $lock $prog
        RETVAL=$?
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|status}"
        RETVAL=1
esac

exit $RETVAL
そしてようやく、"lsyncd"をトリガーにしてS3にアップロードに...
--------
http://www.suz-lab.com

"mod_sed"でHTML上の画像などのURLをS3やCloudFrontに置換

スズキです。

CDPネタです。今回の対象は「URL Rewritingパターン」です。


このパターンの「実装」に
Apacheのフィルターモジュール(mod_ext_filter/mod_sed)や
プロキシーとして用意したNginxなどで動的に書き換えることも可能。
といった記載があります。

以前、本ブログでも"mod_ext_filter"に関しては下記のように紹介しているので
"mod_ext_filter"で画像などのURLをCloudFrontのものに
今回は同様のことを、"mod_sed"で実現してみました。

まずインストールですが、下記のように"atomic"リポジトリを登録して
"yum"で簡単にインストールできました。

# rpm -Uvh http://www6.atomicorp.com/channels/atomic/centos/6/x86_64/RPMS/atomic-release-1.0-14.el6.art.noarch.rpm
# yum -y install mod_sed

フィルタールールは下記のように"httpd.conf"に記述しています。

AddOutputFilter Sed html
OutputSed "s/\"\/\(.*\)\.js\"/\"http:\/\/s3\/\1.js\"/g"
OutputSed "s/\"\/\(.*\)\.css\"/\"http:\/\/s3\/\1.css\"/g"
OutputSed "s/\"\/\(.*\)\.gif\"/\"http:\/\/s3\/\1.gif\"/g"
OutputSed "s/\"\/\(.*\)\.jpg\"/\"http:\/\/s3\/\1.jpg\"/g"

そして、下記のHTMLをソースとして、上記のフィルタールールで
Apacheからコンテンツを確認すると、

<html>
  <head>
    <script type="text/javascript" src="/test.js"></script>
    <script type="text/javascript" src="/test/test.js"></script>
    <script type="text/javascript" src="test.js"></script>
    <script type="text/javascript" src="test/test.js"></script>
    <link href="/test.css" rel="stylesheet" type="text/css">
    <link href="/test/test.css" rel="stylesheet" type="text/css">
    <link href="test.css" rel="stylesheet" type="text/css">
    <link href="test/test.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <img src="/test.gif"/>
 <img src="/test/test.gif"/>
 <img src="test.gif"/>
 <img src="test/test.gif"/>
    <img src="/test.jpg"/>
 <img src="/test/test.jpg"/>
 <img src="test.gif"/>
 <img src="test/test.jpg"/>
    <a href="/test.html">test</a>
 <a href="/test/test.html">test</a>
    <a href="test.html">test</a>
    <a href="test/test.html">test</a>
  </body>
</html>

次のように。該当する部分が変換されていることがわかります。

<html>
  <head>
    <script type="text/javascript" src="http://s3/test.js"></script>
    <script type="text/javascript" src="http://s3/test/test.js"></script>
    <script type="text/javascript" src="test.js"></script>
    <script type="text/javascript" src="test/test.js"></script>
    <link rel="stylesheet" type="text/css" href="http://s3/test.css"/>
    <link rel="stylesheet" type="text/css" href="http://s3/test/test.css"/>
    <link rel="stylesheet" type="text/css" href="test.css"/>
    <link rel="stylesheet" type="text/css" href="test/test.css"/>
  </head>
  <body>
    <img src="http://s3/test.gif"/>
    <img src="http://s3/test/test.gif"/>
    <img src="test.gif"/>
    <img src="test/test.gif"/>
    <img src="http://s3/test.jpg"/>
    <img src="http://s3/test/test.jpg"/>
    <img src="test.gif"/>
    <img src="test/test.jpg"/>
    <a href="/test.html">test</a>
    <a href="/test/test.html">test</a>
    <a href="test.html">test</a>
    <a href="test/test.html">test</a>
  </body>
</html>

"SUZ-LAB AMI"に"atomic"のリポジトリを追加しておこう。
--------
http://www.suz-lab.com

2012年5月12日土曜日

タグを利用したEC2のバックアップ(AMI取得)と世代管理

スズキです。

CDPネタです。今回の対象は「Cloud DIパターン」です。


このパターンの「利点」に
EC2インスタンスの構築だけでなく、AMIやスナップショットの自動取得を行う仕組みを作る場合にも利用できる。
といった記載があります。

今回はEC2のタグ情報を利用して、スナップショット(AMI)の取得や世代管理を
行なうPHPスクリプトを作成しました。

このPHPスクリプトを定期的に実行するだけで、あとはAWSマネジメントコンソールにて
EC2のタグを編集し、スナップショット(AMI)の取得対象にするかどうか、
世代をどれだけ残すか、が容易に管理することができます。

PHPスクリプトは長いの最後に掲載することとし、先に仕様をまとめておきます。
  • 指定のアカウント・リージョンのすべての稼働しているEC2に対して実施
    • 稼働は"instance-state-name"が"running"の状態
  • バックアップ対象EC2は"Backup-Generation"タグがついているもの
    • "Backup-Generation"の値は0以上の数字(0のときはバックアップしない)
  • バックアップは"create_image"つまりAMIの作成で実施
    • "NoReboot"オプションをつけてEC2のリブートは抑制
  • 作成したAMIにはタグを付与
    • "Name"タグはEC2と同じものを付与
    • 自動バックアップとわかるように"Backup-Type"タグを"auto"としても付与
    • 関連するスナップショットにも"Backup-Type"タグを"auto"として付与
    • スナップショットの"Name"タグの値はAMI名とデバイスの値からの文字列
    • ただしタグの付与は作成後すぐだとエラーの可能性があるので最後に実施
  • AMIは最新からEC2の"Backup-Generation"タグの値だけ維持
    • AMIの"Name"タグがEC2と同様のものが対象
    • 残りの古いAMIは削除
    • "Backup-Type"タグが"auto"になっていないものは対象外
  • 削除したAMIに関連するスナップショットも削除
    • "Backup-Type"タグが"auto"のものも対象(注意!)
  • AMIやスナップショットへのタグ付けは作成後に実施
    • 作成後すぐに行うとエラーになる可能性
実際の挙動は下記のようになります。

まずはバックアップ対象のEC2です。バックアップ(AMI)を2世代管理するように
"Backup-Generation"を付け、値を2としています。


この状態で最後に掲載するバックアップスクリプト(PHP)を実行すると、
"Backup-Generation"タグが付いているEC2に対してAMIを作成します。


"Backup-Generation"の値は2なので、バックアップは2世代残るようになっており、
タグも"Name"はEC2と同じもの、そして自動バックアップがわかるように、
"Backup-Type"も"auto"で付いています。

当然AMIの作成と同時にスナップショットも取得されています。


こちらのタグも自動バックアップがわかるように、"Backup-Type"が"auto"として付き、
"Name"はAMI名にアタッチしているデバイス名を付与したものが付いています。

図にすると、こんな感じでしょうか?


最後にPHPスクリプトですが、下記のようになっています。
#!/usr/bin/php
<?php
// 初期設定
require_once("/opt/aws/php/default/sdk.class.php");
date_default_timezone_set("Asia/Tokyo");
$ec2 = new AmazonEC2(array(
  "key"    => "ACCESS KEY",
  "secret" => "SECRET KEY"
));
$ec2->set_region(AmazonEC2::REGION_APAC_NE1);
error_log(date("Y/m/d H:i:s") . " [Info] Begin create images.");

// 対象EC2(runnning)の取得
$response = $ec2->describe_instances(array(
  "Filter" => array(
    array("Name" => "instance-state-name", "Value" => "running")
  )
));
if(!$response->isOK()) {
  error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
}

// 対象EC2に対するバックアップ処理
if(isset($response->body->reservationSet->item)) {
  foreach($response->body->reservationSet->item as $reservation) {
    foreach($reservation->instancesSet->item as $instance) {
      error_log(date("Y/m/d H:i:s") . " [Info] Begin execute instance(" . $instance->instanceId . ").");
      $is_backup   = false;
      $instance_id = $instance->instanceId;
      $image_tag   = $instance_id;

      // バックアップ条件の確認
      if(isset($instance->tagSet->item)) {
        foreach($instance->tagSet->item as $tag) {
          if($tag->key == "Name" && $tag->value != null && trim($tag->value) != "") {
            $image_tag = $tag->value;
          }
          if($tag->key == "Backup-Generation" && is_numeric($tag->value->to_string()) && intval($tag->value) > 0) {
            $is_backup  = true;
            $generation = intval($tag->value);
          }
        }
      }

      // バックアップと世代管理の実施
      if($is_backup) {
        // AMI名の作成
        $image_name  = $image_tag . "-" . date("YmdHis");
        // AMIの作成
        $image_id    = create_image($ec2, $image_name, $instance_id);
        // 削除対象AMIの取得
        $images      = find_delete_images($ec2, $image_tag, $generation);
        // AMIとスナップショットの削除
        delete_images($ec2, $images);
        // AMIにタグ付け
        tag_image($ec2, $image_id, $image_tag);
        // スナップショットにタグ付け
        tag_snapshots($ec2, $image_id);
      } else {
        error_log(date("Y/m/d H:i:s") . " [Info] Skip  create image from " . $instance->instanceId . ".");
      }
      error_log(date("Y/m/d H:i:s") . " [Info] End   execute instance(" . $instance->instanceId . ").");
    }
  }
}

error_log(date("Y/m/d H:i:s") . " [Info] End   create images.");
exit(0);

// AMIの作成
function create_image($ec2, $image_name, $instance_id) {
  error_log(date("Y/m/d H:i:s") . " [Info] Begin create image(" . $image_name . ") from " . $instance_id . ".");
  $response = $ec2->create_image(
    $instance_id,
    $image_name,
    array(
      "Description" => "Create from " . $instance_id . ".",
      "NoReboot"    => true
    )
  );
  if(!$response->isOK()) {
    error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
  }
  error_log(date("Y/m/d H:i:s") . " [Info] End   create image(" . $image_name . ") from " . $instance_id . ".");
  return $response->body->imageId;
}

// 削除対象AMIの取得
function find_delete_images($ec2, $image_tag, $generation) {
  $response = $ec2->describe_images(array("Filter" => array(
    array("Name" => "tag:Name"       , "Value" => $image_tag),
    array("Name" => "tag:Backup-Type", "Value" => "auto")
  )));
  if(!$response->isOK()) {
    error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
  }
  $images = array();
  foreach($response->body->imagesSet->item as $image) {
    $images["$image->name"] = array(
      "id"        => $image->imageId,
      "snapshots" => $image->blockDeviceMapping
    );
  }
  krsort($images);
  return array_slice($images, $generation - 1);
}

// AMIとスナップショットの削除
function delete_images($ec2, $images) {
  foreach($images as $image_name => $image) {
    error_log(date("Y/m/d H:i:s") . " [Info] Begin delete image(" . $image_name . ").");
    $image_id = $image["id"];
    $response = $ec2->deregister_image($image_id);
    if(!$response->isOK()) {
      error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
    }
    error_log(date("Y/m/d H:i:s") . " [Info] End   delete image(" . $image_name . ").");
    foreach($image["snapshots"]->item as $snapshot) {
      $snapshot_id = $snapshot->ebs->snapshotId;
      error_log(date("Y/m/d H:i:s") . " [Info] Begin delete snapshot(" . $snapshot_id . ").");
      $response = $ec2->delete_snapshot($snapshot_id);
      if(!$response->isOK()) {
        error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
      }
      error_log(date("Y/m/d H:i:s") . " [Info] End   delete snapshot(" . $snapshot_id  . ").");
    }
  }
}

// AMIにタグ付け
function tag_image($ec2, $image_id, $image_tag) {
  error_log(date("Y/m/d H:i:s") . " [Info] Begin tag(" . $image_tag . ") image to " . $image_id . ".");
  $response = $ec2->create_tags($image_id, array(
    array("Key" => "Name"       , "Value" => $image_tag),
    array("Key" => "Backup-Type", "Value" => "auto"),
  ));
  if(!$response->isOK()) {
    error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
  }
  error_log(date("Y/m/d H:i:s") . " [Info] End   tag(" . $image_tag . ") image to " . $image_id . ".");
}

// スナップショットにタグ付け
function tag_snapshots($ec2, $image_id) {
  $response = $ec2->describe_images(array("ImageId" => $image_id));
  foreach($response->body->imagesSet->item as $image) {
    foreach($image->blockDeviceMapping->item as $snapshot) {
      $snapshot_id   = $snapshot->ebs->snapshotId;
      $snapshot_tag = $image->name . "-" . basename($snapshot->deviceName);
      error_log(date("Y/m/d H:i:s") . " [Info] Begin tag(" . $snapshot_tag . ") snapshot to " . $snapshot_id . ".");
      $response = $ec2->create_tags($snapshot_id, array(
        array("Key" => "Name"       , "Value" => $snapshot_tag),
        array("Key" => "Backup-Type", "Value" => "auto")
      ));
      if(!$response->isOK()) {
        error_log(date("Y/m/d H:i:s") . " [" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
      }
      error_log(date("Y/m/d H:i:s") . " [Info] End   tag(" . $snapshot_tag . ") snapshot to " . $snapshot_id . ".");
    }
  }
}
?>

軽い気持ちで書き始めたら、予想以上に時間がかかった...
--------
http://www.suz-lab.com/

2012年5月7日月曜日

HAProxyの"backup"オプションで障害時のみ別AZを利用

スズキです。

CDPネタです。今回の対象は「Multi-Datacenterパターン」です。


このパターンの「注意点」に
AZ間の通信速度が気になる場合は、アプリケーションやHAProxyなどの
ミドルウェアで基本的には同一AZのEC2と通信するようにし、
そのEC2が障害時に別AZのEC2と通信するように制御することも可能である。
といった記載があります。

今回はHAProxyを利用して、通常は同一AZのEC2と通信するが、
そのEC2に障害が起きた場合、別AZのEC2を利用するような設定を試してみました。

と言ってもHAProxyの設定は、下記で紹介した"backup"オプションを利用することで
HAProxyを用いた"Read Replica"(RDS)の振り分け
簡単に実現することが可能です。

ちなみに"backup"オプションは"、backup"オプションがついていない
すべてのバックエンドサーバに対する通信ができなくなった場合に限り、
backup"オプションがついているサーバをバックエンドとして利用するものです。

具体的に、下記で別AZで冗長化した"Kyoto Tycoon(Memcache)"に対して、
"Kyoto Tycoon"の冗長化
上記の設定を行なってみます。図にすると、こんな感じでしょうか?


Aゾーンの"haproxy.cfg"の設定は、下記のような感じでしょうか?
listen memcache
    bind 0.0.0.0:11211
    mode tcp
    balance leastconn
    server memcache-a kt-a:11211 check port 11211
    server memcache-b kt-b:11211 check port 11211 backup
BゾーンのEC2(kt-b)に"backup"オプションをつけることで、
"kt-b"は"kt-a"が利用できなくなった時のみ利用されるようになり、
通常は同一AZの通信速度で利用することができます。

近頃、自分のやっている作業は、どれかのCDPに関わってるような気がする...
--------
http://www.suz-lab.com/

2012年5月6日日曜日

EC2起動時にEIPプールから利用していないEIPを取得&関連付け

スズキです。

下記で、EC2起動時にEIPを自分自身に関連付ける方法を紹介しました。
Heartbeat(Pacemaker)でEIPの付け替え
しかし、この方法は固定されたEIPを(他のEC2から外して)関連付けるだけでした。

今回は、EIP群の中から利用されていない(EC2に関連付けられていない)
EIPを取得して、起動時に関連付けるPHPスクリプトを作成してみました。

これは外部サービスなどを利用するために接続するIPアドレスを固定する
必要がある場合に有効です。(特に"Auto Scaling"を利用するとき)

例えば、あらかじめEIPをいくつか取得し、 外部サービスに登録し、
EC2起動時に、これらのEIPから利用されていないものを関連付け、
自動で外部サービスを利用することができるようになります。

図にすると、こんな感じでしょうか?


実際のスクリプトは下記のとおりです。

#!/usr/bin/php
<?php

// 初期設定
require_once("/opt/aws/php/default/sdk.class.php");
$ec2 = new AmazonEC2(array(
  "key"    => "ACCESS KEY",
  "secret" => "SECRET KEY"
));
$ec2->set_region(AmazonEC2::REGION_APAC_NE1);

// EIP群の指定
$eip_list = array(
    "xxx.xxx.xxx.xxx",
    "yyy.yyy.yyy.yyy",
    "zzz.zzz.zzz.zzz"
);

// EIPの取得
$response = $ec2->describe_addresses(array(
    "PublicIp" => $eip_list
));
if(!$response->isOK()) {
  error_log("[" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
}

// 関連付けされていないEIPの取得
foreach($response->body->addressesSet->item as $item) {
    if($item->instanceId == "") {
        $eip = $item->publicIp;
        break;
    }
}

// EIPの関連付け
$response = $ec2->associate_address(
  file_get_contents("http://169.254.169.254/latest/meta-data/instance-id"),
  $eip
);
if(!$response->isOK()) {
  error_log("[" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
  exit(1);
}

exit(0);
?>

上記を下記に出てくる起動スクリプト(/etc/init.d/eip)から呼び出すことで、
Heartbeat(Pacemaker)でEIPの付け替え
起動時にEIPプールからまだ関連付けされていないEIPを自分自身(EC2)に
関連付けることができます。

"Auto Scaling"での起動も試しておきたい...
--------
http://www.suz-lab.com

2012年5月1日火曜日

OpenLDAPでユーザー(OpenLDAP管理)のパスワード変更

スズキです。

下記のようにLDIFファイル作成して、
# cat suzuki1-modify.ldif
dn: uid=suzuki1,ou=user,dc=suz-lab,dc=com
changetype: modify
replace: userPassword
userPassword: suzuki2
"ldapadd"で可能です。
# ldapadd -x -D "cn=Manager,dc=suz-lab,dc=com" -w secret -f suzuki1-modify.ldif

つまり、LDIFファイルにどのように変更するかを記述することになります。

他にもやり方あるんだろうなー...
--------
http://www.suz-lab.com/

"Kyoto Tycoon"の冗長化

スズキです。

以前、下記で"Kyoto Tycoon"のインストールを紹介しましたが、
"Kyoto Tycoon (memcached plugin)"を"CentOS 6.2"にインストール
それを冗長化(レプリケーション)してみました。

といっても、下記のように起動スクリプトを修正して再起動しただけです。

▼"ha-a"の"/etc/init.d/ktserver"
...
mhost="ha-b"
mport="1978"
rtsfile="$basedir/rts"
...
▼"ha-b"の"/etc/init.d/ktserver"
...
mhost="ha-a"
mport="1978"
rtsfile="$basedir/rts"
...

テストは下記のように実施しました。

まず、"ha-a"で書き込みを行い、正しく取得できることを確認します。
# telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
set test1 0 0 7
suzuki1
STORED
get test1
VALUE test1 0 7
suzuki1
END
次に、"ha-b"でも正しく取得できることを確認し、さらに書き込みを行い取得も確認します。
# telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get test1
VALUE test1 0 7
suzuki1
END
set test2 0 0 7
suzuki2
STORED
get test2
VALUE test2 0 7
suzuki2
END
最後に、"ha-a"に戻って"ha-b"で書き込んだものが正しく取得できることを確認します。
# telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get test2
VALUE test2 0 7
suzuki2
END

下記で紹介したHAProxyとの連携も効果的だと思います。
HAProxy(1.4)でMemcachedの負荷分散(障害時の切り離しも)
AZまたぎのHAProxyの負荷分散は、もう少し研究したほうがいいなー...
--------
http://www.suz-lab.com/

NATインスタンスへのルーティングを変更するPHPスクリプト

スズキです。

以前、@c9katayamaさんが、下記のブログで
VPCでアベイラビリティゾーン越しにプライベートIPを共有する
VPCのルーティングを変更することでAZをまたいだ"Floating IPパターン"的な
フェイルオーバーを実現する方法を紹介してくれました。

上記ブログではルーティングテーブルの変更を手動(AWS Management Console)
で行なっていましたが、最終的にはHeatbeat(Pacemaker)などで自動化したい
はずなので、ルーティングを変更するPHPスクリプトを作成してみました。
#!/usr/bin/php
<?php
require_once("/opt/aws/php/default/sdk.class.php");
define("TABLE", "rtb-6cd63105");
define("CIDR" , "0.0.0.0/0");
define("NAT"  , "i-c149d1c1");
$ec2 = new AmazonEC2(array(
  "key"    => "ACCESS KEY",
  "secret" => "SECRET KEY"
));
$ec2->set_region(AmazonEC2::REGION_APAC_NE1);
$response = $ec2->replace_route(TABLE, CIDR, array("InstanceId" => NAT));
if(!$response->isOK()) {
  error_log("[" . $response->body->Errors->Error->Code . "] " . $response->body->Errors->Error->Message);
  exit(1);
}
exit(0);
?>
ポイントは"replace_route"関数を使っているところでしょうか?

下記のようにNATインスタンスが設定されている状態で


上記のPHPスクリプトを実行すると
# ./replace-nat
次のようにルーティング先のNATインスタンスが変更されていることがわかります。


下記と同じ要領で、Hearbeat(Pacemaker)と連動させることも容易にできるはずです。
Heartbeat(Pacemaker)でEIPの付け替え

Hearbeat(Pacemaker)はもう少し深めておかないと…
--------
http://www.suz-lab.com