2011年12月28日水曜日

S3オリジンのCloudFrontでインデックスドキュメントやエラードキュメントを利用できるようにする

スズキです。

CloudFrontには"Default Root Object"という設定項目があり
"http://cloudfront.suz-lab.com/"などでアクセスすると、
上記項目で指定したファイル(ページ)が表示されるようになります。

しかし、"http://cloudfront.suz-lab.com/sub/"などの
サブディレクトリに対するアクセスには対応してません。

また、ステータスコード404などに対するエラードキュメントに関しても、
独自のエラーページを表示させることはできません。

CloudFrontのオリジンをS3にしている場合(S3オリジン)は、
まさに上記の制限が、そのままきいてしまいます。

ちなみにカスタムオリジンの場合はオリジン次第なので、オリジンサーバが
インデックスドキュメントやエラードキュメントを指定できるようにしていれば、
独自のエラードキュメントやアブディレクトリに対するインデックスドキュメントも
可能となります。

しかし、やはりコンテンツは、高い可用性と耐久性を誇るS3に置いておきたいところです。

そもそも、S3には"Web Site Hosting"と呼ばれる機能があり、その機能を有効にすると
サブディレクトリを含めたインデックスドキュメントとエラードキュメントを
独自のファイルに指定することができます。ただし、"Web Site Hosting"用のURLから
アクセスしてはじめて上記の機能を利用することができます。

ここでまた、CloudFrontに戻ります。

CloudFrontを設定するときに、S3オリジンでバケットを指定するのではなく、
カスタムオリジンで上記の"Web Site Hosting"のURLを指定することも、実はできます。

カスタムオリジンにすれば"Web Site Hosting"機能の結果がキャッシュされるため、
サブディレクトリを含めたインデックスドキュメントや独自のエラードキュメントも、
実質、利用できていることになります。

キャッシュ期間には注意です!
--------
http://www.suz-lab.com

2011年12月27日火曜日

VPC上のELBに対するサブネットのCIDRブロックについて

スズキです。

VPC上のELBはサブネットに所属しますが、そもそもELBはスケールするので、
サブネットのIPアドレスレンジはある程度確保しておかなければいけないはずです。

じゃあ、いくつ確保しておけばいいのか?という話ですが、
下記に記載されていました。
How Do I Use Elastic Load Balancing in Amazon VPC?

つまり、次の引用のとおりです。
In order to ensure that your load balancer can scale properly,
the subnet that you attach the load balancer to should be at least a /25 CIDR block
and should have enough free IP addresses to scale.

ELBが適切にスケールできるように、ELBが所属するサブネットのCIDRブロックは
少なくとも"/25"にし、自由に利用できるIPアドレスを確保すべきです。

例えば、下記のようにサブネットを割り振る感じになると思います。
サブネット: 192.168.1.0/25
ネットマスク: 255.255.255.128
ネットワーク: 192.168.1.0
ホストのレンジ: 192.168.1.1 - 192.168.1.126
ブロードキャスト: 192.168.1.127

ちなみにVPCの場合、
192.168.1.1 192.168.1.2 192.168.1.3
の先頭3つのIPアドレスが予約されているので、実際にEC2に割り振れるIPアドレスは
192.168.1.4 - 192.168.1.126
となります。

とりあえずELBのサブネットは"/25"ってことですね。
--------
http://www.suz-lab.com

2011年12月26日月曜日

NagiosのCloudWatchプラグイン(PHP版)

スズキです。

以前、@j3tm0t0さんが公開してくれたNagiosのプラグイン、check_cloudwatch
ほぼ同様のものをCloudWatchとの通信はPHPを使ったもので作ってみました。
(元祖check_cloudwatchはコマンドラインツールを利用しています)

下記のように利用できます。

# ./check_cloudwatch \
> -c 20 \
> -w 10 \
> -f credential.yml \
> -r ap-northeast-1 \
> -n AWS/EC2 \
> -m CPUUtilization \
> -s Average \
> -u Percent \
> -d InstanceId \
> -v i-xxxxxxxx \
CloudWatch CRITICAL - AWS/EC2 CPUUtilization Average InstanceId i-xxxxxxxx: 25.092 Percent;| data=25.092;10;20;0;

コードは下記のようになっています。(エラー処理が甘々ですが...)

require_once("/opt/aws/php/latest/sdk.class.php");

// define status
$ok       = array("code" => 0, "name" => "OK");
$warning  = array("code" => 1, "name" => "WARNING");
$critical = array("code" => 2, "name" => "CRITICAL");
$unknown  = array("code" => 3, "name" => "UNKNOWN");

// set option
$option = getopt("c:w:f:r:n:m:s:u:d:v:");
$critical_size = $option["c"];
$warning_size  = $option["w"];
$credential    = yaml_parse_file($option["f"]);
$region        = $option["r"];
$namespace     = $option["n"];
$metrics       = $option["m"];
$statistics    = $option["s"];
$unit          = $option["u"];
$name          = $option["d"];
$value         = $option["v"];

// init cw
$cw = new AmazonCloudWatch($credential["accessKey"], $credential["secretKey"]);
$cw->set_region($region);
date_default_timezone_set("Asia/Tokyo");

// get metric statistics
$response = $cw->get_metric_statistics(
    $namespace,
    $metrics,
    "-10 minutes",
    "now",
    300,
    $statistics,
    $unit,
    array(
        "Dimensions" => array(
            array("Name" => $name, "Value" => $value)
        )
    )
);
$data = (float)$response->body->GetMetricStatisticsResult->Datapoints->member->$statistics->to_string();

// check status
if($data > $critical_size) {
    $status = $critical["code"];
} elseif($data > $warning_size) {
    $status = $warning["code"];
} elseif($data > 0) {
    $status = $ok["code"];
} else {
    $status = $unknown["code"];
}

// output status
$output = " - ${namespace} ${metrics} ${statistics} ${name} ${value}: ${data} ${unit};| data=${data};${warning_size};${critical_size};0;";
switch($status) {
    case $ok["code"]:
        print("CloudWatch " . $ok["name"]       . $output);
        exit($ok["code"]);
    case $warning["code"]:
        print("CloudWatch " . $warning["name"]  . $output);
        exit($warning["code"]);
    case $critical["code"]:
        print("CloudWatch " . $critical["name"] . $output);
        exit($critical["code"]);
    case $unknown["code"]:
        print("CloudWatch " . $unknown["name"]  . $output);
        exit($unknown["code"]);
}
print("CloudWatch " . $unknown["name"] . $output);
exit($unknown["code"]);

AccessKey、SecretKeyは下記のファイルにまとめています。

accessKey: "XXXXXXXXXXXXXXXXXXXX"
secretKey: "ssssssssssssssssssssssssssssssssssssssss"

もう少しブラッシュアップしたら、また、ちゃんと紹介しようと思います。
--------
http://www.suz-lab.com

2011年12月20日火曜日

PostfixからSESにリレー(stunnel編)

スズキです。

と言っても、下記のAWSドキュメント通りですが...

とりあえず、SESのSMPTポート(465)をたたいてみると、
下記のように何も反応が返って来ません。
# telnet email-smtp.us-east-1.amazonaws.com 465
Trying 107.22.229.233...
Connected to email-smtp.us-east-1.amazonaws.com.
Escape character is '^]'.

これは、SESへの(SMTP)接続はTLSでの暗号化を必要とするので、
当然、直接のtelnetでは接続できなくなります。

ということで、下記のようにstunnelをインストールして、
SESに対するTLSの処理はstunnelでおこない、stunnnelにはTLS無しで
透過的にSESに接続できるようにしてみました。
# yum -y install stunnel

stunnelの設定ファイルは下記のとおりです。
# cat /etc/stunnel/stunnel.conf
[smtp-tls-wrapper]
accept  = 2525
client  = yes
connect = email-smtp.us-east-1.amazonaws.com:465

stunnelの起動は次のとおりですが、起動スクリプトも、
どこかで用意しておきたいところです。
# /usr/bin/stunnel

そして、stunnelに対して接続すると、下記のように今度は無事、
SMTPのレスポンスが返ってくることがわかります。
# telnet localhost 2525
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 email-smtp.amazonaws.com ESMTP SimpleEmailService-194655181

次は実際にPostfixからSESにリレーしてみます。
--------
http://www.suz-lab.com

2011年12月19日月曜日

そろそろCloudFormation(VpcCloudFormation編)

スズキです。

下記で簡単な(EC2一つ立ち上げるだけの)CloudFormationを試しましたが、
そろそろCloudFormation(HelloCloudFormation編)
今回は、これをVPC上で立ち上げてみました。

テンプレートは下記のようになります。

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "Create an Amazon EC2 instance running the SUZ-LAB CentOS AMI.",

  "Parameters" : {
    "KeyName" : {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
      "Type"        : "String"
    }
  },

  "Mappings" : {
    "RegionMap" : {
      "ap-northeast-1" : { "AMI" : "ami-de9126df" }
    }
  },

  "Resources" : {
    "Ec2Instance" : {
      "Type"       : "AWS::EC2::Instance",
      "Properties" : {
        "KeyName"          : { "Ref" : "KeyName" },
        "ImageId"          : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ]},
        "InstanceType"     : "m1.small",
        "SubnetId"         : "subnet-9bd939f2",
        "PrivateIpAddress" : "10.0.0.100"
      }
    }
  },

  "Outputs" : {
    "InstanceId" : {
      "Description" : "InstanceId of the newly created EC2 instance",
      "Value"       : { "Ref" : "Ec2Instance" }
    }
  }
}

前回同様、下記に示す上記のテンプレートURLを使って
AWS Management Consoleから起動すればOKです。
https://s3-ap-northeast-1.amazonaws.com/www.suz-lab.com/formation/VpcCloudFormation.template


ただし、サブネットやルーティング、Network ACLsなどの設定は、まだ、
できないようなので、VPC(サブネット/ルーティング/Network ACLs)の設定は
事前にしておく必要があるようです。

また、最近VPC上で利用できるようになったELBですが、こちらも、まだ、
対応していないようです。ですので、お金はかかりますがVPC上のELBも、
予め作成しておく必要があるようです。
(そしてインスタンスのぶら下げるのは手動になってしまうと思います)

次は何編にしよう?
--------
http://www.suz-lab.com

そろそろCloudFormation(HelloCloudFormation編)

スズキです。

CloudFormationの用途に、一時的に利用する開発環境や検証環境、バックアップ環境の
起動/停止を一気にまとめて行うというものがあると思います。
(というか、それしか思い浮かばないのですが...)

ということで、そろそろ、そのような要件が出てきたので、
ちゃんと一から試すことにしました。(つまりHelloCloudFormationです)

下記は一番簡単な、AMIからEC2を起動するHelloCloudFormationテンプレートです。
(シンプルなテンプレートなので意味合いは直感的にわかると思います)

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "Create an Amazon EC2 instance running the SUZ-LAB CentOS AMI.",

  "Parameters" : {
    "KeyName" : {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
      "Type" : "String"
    }
  },

  "Mappings" : {
    "RegionMap" : {
      "ap-northeast-1" : { "AMI" : "ami-de9126df" }
    }
  },

  "Resources" : {
    "Ec2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "KeyName" : { "Ref" : "KeyName" },
        "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ]}
      }
    }
  },

  "Outputs" : {
    "InstanceId" : {
      "Description" : "InstanceId of the newly created EC2 instance",
      "Value" : { "Ref" : "Ec2Instance" }
    }
  }
}

ということで、上記のテンプレートを実際にAWS Management Consoleから利用してみます。

まずは、上記のテンプレートを下記のようにS3に配置します。
https://s3-ap-northeast-1.amazonaws.com/www.suz-lab.com/formation/HelloCloudFormation.template

次に、下記のように"Provide a Template URL"を選択し、テンプレートのURLを入力します。


そして、パラメータを入力します。ここで入力するパラメータは上記のテンプレートで
"Parameters"で定義されたもので、任意に指定することができます。


最後に、情報の確認して問題なければ、構築を開始します。


起動後、"Outputs"タグを確認すると、下記のようにテンプレートの"Outputs"で定義した情報が
出力されていることがわかります。


次は、VPC!
--------
http://www.suz-lab.com

S3にブラウザから直接アップロードするフォーム(HTML)をPHPで生成

スズキです。

S3にブラウザから直接アップロードする方法を下記で紹介しましたが、
JAWS-UG宮崎第3回勉強会LT資料(Browser Uploads to S3 using HTML POST Forms)
そのときは、静的なHTMLフォームを前提として、Base64のpolicyやsignatureは
別プログラム(Ruby)で作成していました。

今回は、そのHTMLフォームをPHPで生成し、有効期限やアップロードファイル名、
リダイレクト先などを動的に変更できるようにしてみました。

コードは下記のとおりです。

<?php
$id         = "00000000";
$expiration = gmdate("Y-m-d\TH:i:s\Z",strtotime("1 minute"));
$redirect   = "http://www.suz-lab.com/index.html?id=$id";
$policy = <<<EOS
{
    "expiration": "$expiration",
    "conditions": [
        {"bucket": "www.suz-lab.com"},
        ["starts-with", "\$key", ""],
        {"acl": "private"},
        {"success_action_redirect": "$redirect"},
        ["starts-with", "\$Content-Type", ""],
        ["content-length-range", 0, 1048576]
    ]
}
EOS;
$signature = hash_hmac("sha1", base64_encode($policy), "SECRET_KEY", true);
?>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  </head>
  <body>
    <form action="http://www.suz-lab.com.s3.amazonaws.com/"
          method="post" enctype="multipart/form-data">
      <input type="hidden" name="key"
             value="<?php print("${id}.txt"); ?>"/>
      <input type="hidden" name="AWSAccessKeyId"
             value="ACCESS_KEY"/>
      <input type="hidden" name="acl"
             value="private"/>
      <input type="hidden" name="success_action_redirect"
             value="<?php print($redirect); ?>"/>
      <input type="hidden" name="policy"
             value="<?php print(base64_encode($policy)); ?>"/>
      <input type="hidden" name="signature"
             value="<?php print(base64_encode($signature)); ?>"/>
      <input type="hidden" name="Content-Type"
             value="text/plain"/>
      <input type="file"   name="file"/>
      <input type="submit" name="button" value="Upload"/>
    </form>
  </body>
</html>

上記は有効期限をHTMLフォームが作成されてから1分以内とし、アップロードファイル名や
リダイレクトURLにid(ユーザーIDなど)が反映されるようにしています。

S3へのアップロードが成功すると、下記にリダイレクトされ、リダイレクトURLに
事前につけておいたパラメータ(id=00000000)も引き継がれていることがわかります。

http://www.suz-lab.com/index.html?id=00000000&bucket=www.suz-lab.com&key=00000000.txt&etag=%22...%22

さらに、HTMLフォームが生成された後、1分後にアップロードすると、
次のように、ちゃんとエラーになりました。


これで、いつでも使えます!
--------
http://www.suz-lab.com/

2011年12月17日土曜日

JAWS-UG宮崎第3回勉強会LT資料(Browser Uploads to S3 using HTML POST Forms)

スズキです。

第3回 AWS User Group - Japan【宮崎】 勉強会
でLTします。

LTの内容は下記の資料の通り、
HTMLフォームからS3に直接ファイルをアップロードする方法についてです。


それでは、宮崎の皆さん、よろしくお願いします。
--------
http://www.suz-lab.com

2011年12月9日金曜日

Varnishで"www.suz-lab.com"を"suz-lab.com"にリダイレクト

スズキです。

以前下記で、Varnishによる"suz-lab.com"を"www.suz-lab.com"に
リダイレクトする方法を紹介しましたが、
Varnishでリダイレクト専用Webサーバの構築
今回は、その逆の"www.suz-lab.com"を"suz-lab.com"にリダイレクトする方法です。

上記の設定は、設定ファイル"default.vcl"の"vcl_error"で行い、
具体的には下記のようになります。

sub vcl_error {
    if(req.http.host == "www.suz-lab.com") {
      set obj.http.Location = "http://" regsub(req.http.host, "www\.(.*)", "\1") req.url;
      set obj.status = 301;
    }
    return(deliver);
}

近頃、S3への小細工は、全部Varnishでやってます...
--------
http://www.suz-lab.com

2011年12月7日水曜日

オレゴンリージョン(us-west-2)のAKI(pv-grub-hd0/00)

スズキです。

下記AMIの作成にて、
SUZ-LAB謹製 CentOS AMI (6.0.5 64bit us-west-2)
"User Provided Kernel"で使うAKI(pv-grub-hd0/00)を調査したのでまとめておきます。

▼aki-c2e26ff2
ec2-public-images-us-west-2/pv-grub-hd0_1.02-i386.gz.manifest.xml

▼aki-c0e26ff0
ec2-public-images-us-west-2/pv-grub-hd00_1.02-i386.gz.manifest.xml

▼aki-98e26fa8
ec2-public-images-us-west-2/pv-grub-hd0_1.02-x86_64.gz.manifest.xml

▼aki-94e26fa4
ec2-public-images-us-west-2/pv-grub-hd00_1.02-x86_64.gz.manifest.xml

他のリージョンに関しては、下記でまとめてあります。
"User Provided Kernel"で使う"pv-grub-hd0/00"が1.02になってた

久しぶりにAMIのリージョン移行した...
--------
http://www.suz-lab.com

SUZ-LAB謹製 CentOS AMI (6.0.5 64bit us-west-2)

スズキです。

オレゴンリージョン(us-west-2)にリリースしました。

AMIを「suz」で検索してもらえれば、
811118151095/suz-lab_ebs_centos-core-i386-6.0.5
として見つかるはずです。

AMIの内容は、下記と同様となります。
SUZ-LAB謹製 CentOS AMI (6.0.5 64bit ap-northeast-1)

ただし利用しているAKIは下記となります。
▼aki-98e26fa8
ec2-public-images-us-west-2/pv-grub-hd0_1.02-x86_64.gz.manifest.xml
Enjoy!
--------
http://www.suz-lab.com

SUZ-LAB謹製 CentOS AMI (6.0.5 64bit ap-northeast-1)

スズキです。

下記をアップデートしました。
SUZ-LAB謹製 CentOS AMI (6.0.2 64bit ap-northeast-1)
AMIを「suz」で検索してもらえれば、
811118151095/suz-lab_ebs_centos-core-x86_64-6.0.5
として見つかるはずです。

アップデート内容は下記の32bitのものと同じになります。
SUZ-LAB謹製 CentOS AMI (6.0.3 32bit ap-northeast-1)

SUZ-LAB謹製 CentOS AMI (6.0.4 32bit ap-northeast-1)

SUZ-LAB謹製 CentOS AMI (6.0.5 32bit ap-northeast-1)

Enjoy!
--------
http://www.suz-lab.com

2011年12月6日火曜日

SUZ-LAB謹製 CentOS AMI (6.0.5 32bit ap-northeast-1)

スズキです。

下記をアップデートしました。
SUZ-LAB謹製 CentOS AMI (6.0.4 32bit ap-northeast-1)
AMIを「suz」で検索してもらえれば、
811118151095/suz-lab_ebs_centos-core-i386-6.0.5
として見つかるはずです。

アップデート内容は下記となります。

パッケージのアップデート
# yum -y update

よく要求されるセキュリティ要件を反映

まず、"/etc/pam.d/password-auth"を下記のように修正しています。

▼6回以上の失敗アクセスがあった場合、システム管理者がリセットするまで無効とする。
auth        required      pam_tally2.so deny=6

▼過去4回のパスワードを回転使用できないようにする。
password    sufficient    pam_unix.so try_first_pass use_authtok nullok sha512 shadow remember=4

最終的に"/etc/pam.d/password-auth"は下記のようになっています。
auth        required      pam_env.so
auth        required      pam_tally2.so deny=6
auth        sufficient    pam_unix.so try_first_pass nullok
auth        required      pam_deny.so

account     required      pam_unix.so

password    requisite     pam_cracklib.so try_first_pass retry=3 type=
password    sufficient    pam_unix.so try_first_pass use_authtok nullok sha512 shadow remember=4
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so

次に、監査ログ(/var/log/audit/audit.log)へのアクセス情報も記録するように、
"/etc/audit/audit.rules"に下記を追加しておきます。
-w /var/log/audit/audit.log

"audit.log"にアクセス(tail)すると、下記のように記録されます。
type=SYSCALL msg=audit(1323176229.195:36): arch=40000003 syscall=5 success=yes exit=3 a0=bf9b27d8 a1=8000 a2=0 a3=bf9b1050 items=1 ppid=875 pid=902 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="tail" exe="/usr/bin/tail" key=(null)
type=CWD msg=audit(1323176229.195:36):  cwd="/var/log/audit"
type=PATH msg=audit(1323176229.195:36): item=0 name="/var/log/audit/audit.log" inode=7132 dev=ca:01 mode=0100600 ouid=0 ogid=0 rdev=00:00

Enjoy!
--------
http://www.suz-lab.com

2011年12月5日月曜日

#サンタクラウド のデータ

スズキです。

#サンタクラウドのページですが、下記のJSONデータをJavaScriptで読み込んで
表示しています。
http://www.suz-lab.com/santacloud/tweet.json

JSONの内容は下記の通りです。
[
    {
        "tweet_id":"0000000000000000",
        "tweet_text":"VPC上でRDSが利用できるようになりますように!",
        "tweet_user_name":"suz_lab",
        "tweet_user_image":"ツイートしたユーザーの画像のURL",
        "retweet_users":[
            {
                "retweet_user_name":"kaz_goto",
                "retweet_user_image":"リツイートしたユーザーの画像のURL"
            },
            ...
        ]
    },
    ...
]

@tottokugさん、面白い結果、期待してます。!
--------
http://www.suz-lab.com

2011年12月1日木曜日

S3のファイルサイズをチェックするNagiosプラグイン(PHP)

スズキです。

下記でS3(バケット)のファイルサイズが取得できるようになったので
S3で利用されているバケットのファイルサイズの取得
S3で利用されているファイルサイズ(全バケット)の取得
今回はS3のファイルサイズをチェックするNagiosプラグインを作ってみました。

まずは下記のようなAWSのAPIを利用するためのキーを保存したファイルを用意します。

▼ credential.yml
accessKey: "AAAAAAAAAAAAAAAAAAAA"
secretKey: "ssssssssssssssssssssssssssssssssssssssss"

次に下記のスクリプト(PHP)を用意します。

▼ check_s3size
require_once("/opt/aws/php/latest/sdk.class.php");

// define status
$ok       = array("code" => 0, "name" => "OK");
$warning  = array("code" => 1, "name" => "WARNING");
$critical = array("code" => 2, "name" => "CRITICAL");
$unknown  = array("code" => 3, "name" => "UNKNOWN");

// set option
$option = getopt("c:w:f:b:");
$critical_size = $option["c"];
$warning_size  = $option["w"];
$credential    = yaml_parse_file($option["f"]);
if(isset($option["b"])) {
    $bucket = $option["b"];
}

// init s3
$s3 = new AmazonS3($credential["accessKey"], $credential["secretKey"]);
$s3->use_ssl = false;

// get s3 size
if(isset($bucket)) {
    $total_filesize = $s3->get_bucket_filesize($bucket, false);
} else {
    $bucket_list = $s3->get_bucket_list();
    $total_filesize = 0;
    foreach($bucket_list as $bucket) {
        $total_filesize += $s3->get_bucket_filesize($bucket, false);
    }
}

// check status
if($total_filesize > $critical_size) {
    $status = $critical["code"];
} elseif($total_filesize > $warning_size) {
    $status = $warning["code"];
} elseif($total_filesize > 0) {
    $status = $ok["code"];
} else {
    $status = $unknown["code"];
}

// output status
switch($status) {
    case $ok["code"]:
        print($ok["name"]       . " - s3 size: " . $total_filesize . "|size=" . $total_filesize . ";");
        exit($ok["code"]);
    case $warning["code"]:
        print($warning["name"]  . " - s3 size: " . $total_filesize . "|size=" . $total_filesize . ";");
        exit($warning["code"]);
    case $critical["code"]:
        print($critical["name"] . " - s3 size: " . $total_filesize . "|size=" . $total_filesize . ";");
        exit($critical["code"]);
    case $unknown["code"]:
        print($unknown["name"]  . " - s3 size: " . $total_filesize . "|size=" . $total_filesize . ";");
        exit($unknown["code"]);
}
print($unknown["name"]);
exit($unknown["code"]);

実行オプションは下記の通りです。
-c: CRITICALの閾値
-w: WARNINGの閾値
-f: キーファイル
-b: バケット(指定しない場合はすべてのバケットが対象)

オプションを指定して実行すると下記のような結果になります。

# ./check_s3_size -c 1000 -w 100 -f credential.yml -b cdn.cloudpack.jp
CRITICAL - s3 size: 238725|size=238725;

バケット指定(-b)をしないで実行すると、
下記のようにすべてのバケットのファイルサイズを合計したものが対象となります。

./check_s3_size -c 1000 -w 100 -f credential.yml
CRITICAL - s3 size: 31145892|size=31145892;

次はこのプラグインを実際にNagiosで利用してみます。
--------
http://www.suz-lab.com

S3で利用されているファイルサイズ(全バケット)の取得

スズキです。

下記は指定した単一バケットに対するファイルサイズの取得でしたが、
S3で利用されているバケットのファイルサイズの取得
今回はすべてのバケットのファイルサイズの合計を出力するものです。

require_once("/opt/aws/php/latest/sdk.class.php");
$s3 = new AmazonS3(
    "AAAAAAAAAAAAAAAAAAAA",
    "ssssssssssssssssssssssssssssssssssssssss"
);
$s3->use_ssl = false;
$bucket_list = $s3->get_bucket_list();
$total_filesize = 0;
foreach($bucket_list as $bucket) {
  $total_filesize += $s3->get_bucket_filesize($bucket, false);
}
var_dump($total_filesize);

次はこれをNagiosプラグイン化します。
--------
http://www.suz-lab.com

S3で利用されているバケットのファイルサイズの取得

スズキです。

下記スクリプト(PHP)で可能です。

require_once("/opt/aws/php/latest/sdk.class.php");
$s3 = new AmazonS3(
    "AAAAAAAAAAAAAAAAAAAA",
    "ssssssssssssssssssssssssssssssssssssssss"
);
$s3->use_ssl = false;
$response = $s3->get_bucket_filesize("cdn.cloudpack.jp", false);
print($response);

結果は次のようになります。

# ./get-bucket-filesize 
238725

"get_bucket_filesize"の第二引数を"true"にすると、

...
$response = $s3->get_bucket_filesize("cdn.cloudpack.jp", true);
...

単位付きの読みやすい結果になります。

# ./get-bucket-filesize 
233.13 kB

次は、全バケットの容量チェックできるようにして、Nagiosのプラグインにして...
--------
http://www.suz-lab.com

"AWS SDK for PHP"のS3関係のメソッドで証明書関係のエラー対策

スズキです。

とりあえず、下記で紹介はしたのですが、
"AWS SDK for PHP"でS3にファイルアップロードしようとしたら証明書関係のエラー
メソッドによっては対策方法の、次のオプションが指定できないものもあるので、
"curlopts" => array(CURLOPT_SSL_VERIFYPEER => false)
そのようなメソッドを利用する場合の対策方法です。

まず、下記のようなスクリプトを実行すると、

require_once("/opt/aws/php/latest/sdk.class.php");
$s3 = new AmazonS3(
    "AAAAAAAAAAAAAAAAAAAA",
    "ssssssssssssssssssssssssssssssssssssssss"
);
$response = $s3->get_bucket_filesize("cdn.cloudpack.jp", true);
print($response);

次のようなエラーになってしまいます。
("cdn.cloudpack.jp.s3.amazonaws.com"は"*.s3.amazonaws.com"の証明書でもエラー!)

PHP Fatal error:  Uncaught exception 'RequestCore_Exception' with message 'cURL resource: Resource id #9; cURL error: SSL: certificate subject name '*.s3.amazonaws.com' does not match target host name 'cdn.cloudpack.jp.s3.amazonaws.com' (51)' in /opt/aws/php/1.4.7/lib/requestcore/requestcore.class.php:824
Stack trace:
#0 /opt/aws/php/1.4.7/services/s3.class.php(728): RequestCore->send_request()
#1 /opt/aws/php/1.4.7/services/s3.class.php(1397): AmazonS3->authenticate('cdn.cloudpack.j...', Array)
#2 /opt/aws/php/1.4.7/services/s3.class.php(1950): AmazonS3->list_objects('cdn.cloudpack.j...')
#3 /opt/suzuki/bin/get-bucket-filesize(9): AmazonS3->get_bucket_filesize('cdn.cloudpack.j...', true)
#4 {main}
  thrown in /opt/aws/php/1.4.7/lib/requestcore/requestcore.class.php on line 824

下記対策が使えないメソッドは、そもそもSSLの通信をしないようにして対策できます。
"curlopts" => array(CURLOPT_SSL_VERIFYPEER => false)
SSLの通信をしない方法は次のように"$s3->use_ssl = false;"で実現できます。

require_once("/opt/aws/php/latest/sdk.class.php");
$s3 = new AmazonS3(
    "AAAAAAAAAAAAAAAAAAAA",
    "ssssssssssssssssssssssssssssssssssssssss"
);
$s3->use_ssl = false;
$response = $s3->get_bucket_filesize("cdn.cloudpack.jp", true);
print($response);

HTTPSの通信にはならないけど...
--------
http://www.suz-lab.com

2011年11月24日木曜日

HAProxy(1.4)でMemcachedの負荷分散(障害時の切り離しも)

スズキです。

こちらのMySQLに引き続き、
HAProxy(1.4)でMySQLの自動フェイルオーバーにあわせて接続先を変更
Memcachedも試してみました。

HAProxyの設定ファイル(/etc/haproxy/haproxy.cfg)は下記のようにしています。

global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    mode                    tcp
    log                     global
    retries                 3
    timeout connect         10s
    timeout client          1m
    timeout server          1m

listen memcache
    bind 0.0.0.0:11212
    mode tcp
    balance roundrobin
    server memcache1 suz-lab-ha1:11211 check port 11211
    server memcache2 suz-lab-ha2:11211 check port 11211

上記で、Memcachedの負荷分散や障害時の切り離しが確認できました。
Repcachedを利用すれば、障害時キャッシュのロストも防げると思います。

全部、ローカルHAProxy経由にすればいいのか!?
--------
http://www.suz-lab.com

HAProxy(1.4)でMySQLの自動フェイルオーバーにあわせて接続先を変更

スズキです。

下記で自動フェイルオーバーするMySQLができたので、
VPC上のEC2(CentOS 6.0)でDRBD(8.3)とHeartbeat(3.0)とMySQL(5.5)
次は、それに合わせて接続先を変更する仕組みを考えます。

EC2はVPCでも仮想IPアドレスが利用できないので、
まず、hostsファイルを変更したり、内部DNSをたて、DNSレコードを変更したりして、
自動フェイルオーバーに合わせた接続変更(接続ホスト名のIPアドレスを変更)を
行う方法を考えると思います。

ただ、上記の方法は、ミドルウェアのインストール&設定のみで完結せず、
多少の仕組みの作り込みが必要になってしまうはずです。

今回はMySQLに接続するサーバには必ずHAProxyをインストールし、
アプリケーションはlocalhostのMySQL(HAProxy)に接続し、
フェイルオーバーに伴う接続先変更はHAProxyに任せる方法を試してみました。

まずは、HAProxyのインストールです。
# yum -y install haproxy

"haproxy.cfg"は下記のようにしました。
# cat /etc/haproxy/haproxy.cfg 
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
defaults
    mode                    tcp
    log                     global
    retries                 3
    timeout connect         10s
    timeout client          1m
    timeout server          1m
listen mysql
    bind 0.0.0.0:3306
    mode tcp
    option mysql-check
    balance roundrobin
    server mysql1 suz-lab-ha1:3306 check port 3306
    server mysql2 suz-lab-ha2:3306 check port 3306

ただし、このままだと、HAProxyからMySQLへのヘルスチェックで、
MySQLがHAProxyからの接続をブロックしてしまうので、
MySQLの設定を下記のようにしておきます。

# cat /etc/my.cnf
[mysqld]
...
max_connect_errors=999999999
...

上記のHAProxy経由でMySQLに接続すると、MySQL側をフェイルオーバーさせても、
数秒でHAProxyが接続先をフェイルオーバー後のMySQLに変更してくれ、
アプリケーションは問題なくMySQLに接続し続けることができるようになります。

そして、この方法なら、ミドルウェア(HAProxy)のインストール&設定で、
自動フェイルオーバーにあわせた接続先変更が可能です。

この方法が一番いいような...
--------
http://www.suz-lab.com

2011年11月23日水曜日

VPC上のEC2(CentOS 6.0)でDRBD(8.3)とHeartbeat(3.0)とMySQL(5.5)

スズキです。

下記に続いてMySQL連携も試してみます。
VPC上のEC2(CentOS 6.0)でDRBD(8.3)とHeartbeat(3.0)
DRBD/Heartbeat/MySQLの連携に関しては、下記ですでに紹介もしています。
そろそろDRBDに挑戦 (MySQL連携編)

【suz-lab-ha1/suz-lab-ha2で実行】

▼mysqlサーバのインストール
# yum -y install mysql-server

▼/var/lib/mysqlをdrbdのファイルシステムにシンボリックリンク
# chown mysql.mysql /mnt/drbd/mysql
# rmdir /var/lib/mysql
# ln -s /mnt/drbd/mysql /var/lib/mysql

【suz-lab-ha1で実行】

▼データベースの作成
# /etc/init.d/mysqld start
MySQL データベースを初期化中:  Installing MySQL system tables...
OK
Filling help tables...
OK
...
※suz-lab-ha1のDRBDがプライマリとします。

▼haresourcesファイルの修正(mysqld追加)
# cat /etc/ha.d/haresources
suz-lab-ha1 drbddisk::mysql Filesystem::/dev/drbd0::/mnt/drbd/mysql mysqld

【suz-lab-ha2で実行】

▼haresourcesファイルの修正(mysqld追加)
# cat /etc/ha.d/haresources
suz-lab-ha2 drbddisk::mysql Filesystem::/dev/drbd0::/mnt/drbd/mysql mysqld

【suz-lab-ha1/suz-lab-ha2で実行】
# /etc/init.d/heartbeat restart

この状態だと、suz-lab-ha1のDRBDがプライマリで、MySQLが起動しています。
(suz-lab-ha2のMySQLは起動していません)

suz-lab-ha1のHeartbeatを停止すると、suz-lab-ha2のDRBDがプライマリになり、
その後、MySQLも起動します。(自動フェイルオーバーします)

次はHAProxyでMySQLの接続先が切り替わることの確認です。
--------
http://www.suz-lab.com

VPC上のEC2(CentOS 6.0)でDRBD(8.3)とHeartbeat(3.0)

スズキです。

下記に続いてHeartbeat連携も試してみます。
VPC上のEC2(CentOS 6.0)でDRBD(8.3)
Heatbeatに関しては、すでに下記で紹介もしています。

【suz-lab-ha1/suz-lab-ha2で実行】

▼heartbeatのインストール
# yum -y install heartbeat

▼heartbeatの設定ファイル作成
# cd /etc/ha.d
# cp /usr/share/doc/heartbeat-3.0.4/ha.cf ./
# cp /usr/share/doc/heartbeat-3.0.4/authkeys ./
# cp /usr/share/doc/heartbeat-3.0.4/haresources ./

▼authkeysファイルの編集
# cat authkeys
auth 1
1 crc
#2 sha1 HI!
#3 md5 Hello!

▼authkeysファイルのパーミッション変更
# chmod 600 authkeys

【suz-lab-ha1で実行】

▼ha.cfファイルの編集
# cat ha.cf
debugfile /var/log/ha-debug
logfile /var/log/ha-log
logfacility     local0
keepalive 2
deadtime 30
warntime 10
initdead 120
udpport 694
ucast eth0 10.0.1.87
auto_failback off
node    suz-lab-ha1
node    suz-lab-ha2

▼haresourcesファイルの編集
# cat haresources
suz-lab-ha1 drbddisk::mysql Filesystem::/dev/drbd0::/mnt/drbd/mysql


【suz-lab-ha2で実行】

▼ha.cfファイルの編集
# cat ha.cf
debugfile /var/log/ha-debug
logfile /var/log/ha-log
logfacility     local0
keepalive 2
deadtime 30
warntime 10
initdead 120
udpport 694
ucast eth0 10.0.0.228
auto_failback off
node    suz-lab-ha1
node    suz-lab-ha2

▼haresourcesファイルの編集
# cat haresources
suz-lab-ha2 drbddisk::mysql Filesystem::/dev/drbd0::/mnt/drbd/mysql

【suz-lab-ha1/suz-lab-ha2で実行】

▼heartbeatの起動
# /etc/init.d/heartbeat start

上記で、DRBDがプライマリーのHeartbeat(suz-lab-ha1)をストップすると、
自動的にsuz-lab-ha2のDRBDがプライマリーになり、マウントされることが確認できます。

次はMySQLをこの上にのせないと...
--------
http://www.suz-lab.com

VPC上のEC2(CentOS 6.0)でDRBD(8.3)

スズキです。

以前、下記のように物理サーバで試していたのですが、
そろそろDRBDに挑戦(設定&稼働編)
それをVPC上のEC2でも試して見ました。

環境は下記のとおりです。
▼suz-lab-ha1
IP: 10.0.0.54(subnet: 10.0.0.0/24: a zone)
EBS: /dev/xvdf1

▼suz-lab-ha2
IP: 10.0.1.36(subnet: 10.0.1.0/24: b zone)
EBS: /dev/xvdf1

AMIは当然、
SUZ-LAB謹製 CentOS AMI (6.0.4 32bit ap-northeast-1)
です!

▼hostnameの設定

【suz-lab-ha1で実行】
# cat /etc/sysconfig/network
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=suz-lab-ha1

【suz-lab-ha2で実行】
# cat /etc/sysconfig/network
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=suz-lab-ha2

【suz-lab-ha1/suz-lab-ha2で実行】

▼DRBD8.3のインストール
# yum -y install drbd83-utils kmod-drbd83
※8.4は、なんかうまくいかなかったので...

▼hostsファイルの設定
# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain
10.0.0.54   suz-lab-ha1
10.0.1.36   suz-lab-ha2

▼drbdファイルの設定
# cat /etc/drbd.d/mysql.res
resource mysql {
    on suz-lab-ha1 {
        device /dev/drbd0;
        disk /dev/xvdf1;
        address 10.0.0.228:7789;
        meta-disk internal;
    }
    on suz-lab-ha2 {
        device /dev/drbd0;
        disk /dev/xvdf1;
        address 10.0.1.87:7789;
        meta-disk internal;
    }
}

▼drbdの構築
# drbdadm create-md mysql
Writing meta data...
initializing activity log
NOT initializing bitmap
New drbd meta data block successfully created.
success

▼drbdの起動

【suz-lab-ha1で実行】
# /etc/init.d/drbd start
Starting DRBD resources: [ d(mysql) ]........

【suz-lab-ha2で実行】
# /etc/init.d/drbd start
Starting DRBD resources: [ d(mysql) n(mysql) ].

※両方ほぼ同時に起動する必要があります。

【suz-lab-ha1で実行】

▼ファイルシステムの作成
# drbdsetup /dev/drbd0 primary -o
# mkfs.ext4 /dev/drbd0 
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
65536 inodes, 262127 blocks
13106 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=268435456
8 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
 32768, 98304, 163840, 229376

Writing inode tables: done                            
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 28 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

▼ファイルシステムのマウント
# mkdir -p /mnt/drbd/mysql
# cat /etc/fstab
/dev/xvda1      /               ext4   defaults       1 1
none            /proc           proc   defaults       0 0
none            /sys            sysfs  defaults       0 0
none            /dev/pts        devpts gid=5,mode=620 0 0
none            /dev/shm        tmpfs  defaults       0 0
/mnt/swap/0.img swap            swap   defaults       0 0
/dev/drbd0      /mnt/drbd/mysql ext4   defaults       0 0
# mount /mnt/drbd/mysql

これで設定完了です。

下記の最後の方に記述しているテストも試して見ましたが、
そろそろDRBDに挑戦(設定&稼働編)
問題ありませんでした。

次は、Heartbeat連携!
--------
http://www.suz-lab.com

2011年11月22日火曜日

SUZ-LAB謹製 CentOS AMI (6.0.4 32bit ap-northeast-1)

スズキです。

下記をアップデートしました。
SUZ-LAB謹製 CentOS AMI (6.0.3 32bit ap-northeast-1)
AMIを「suz」で検索してもらえれば、
811118151095/suz-lab_ebs_centos-core-i386-6.0.4
として見つかるはずです。

アップデート内容は下記となります。

パッケージのアップデート
# cat /etc/yum.conf 
[main]
...
exclude=kernel*
...
# yum -y update
※カーネルアップデートすると起動しなくなったので、カーネルは除外しています。

ローケール関係を日本語に
CentOS(6.0)でロケール関係の設定

ELRepoをyumリポジトリ追加
CentOS6.0にDRBDをインストール

Enjoy!
--------
http://www.suz-lab.com

CentOS6.0にDRBDをインストール

スズキです。

EC2上でDRBDの動作確認をすることになったので、
まずはCentOS6.0にDRBDをインストールしてみました。

ちなみにCentOS5.xな場合は、以前、下記のように本ブログで紹介しています。

CentOS6.0の場合は、下記のように"yum"リポジトリの追加から必要となります。

# rpm -Uvh http://elrepo.org/elrepo-release-6-4.el6.elrepo.noarch.rpm
# cat /etc/yum.repos.d/elrepo.repo
...
[elrepo]
...
enabled=1
...
[elrepo-extras]
...
enabled=1
...
※"elrepo-extras"も"enabled=1"にしています。

すると、下記のようにリポジトリが追加されていることが確認できます。

# yum repolist
...
repo id         repo name                                                status
base            CentOS-6 - Base                                          4558
cr              CentOS-6 - CR                                            1705
elrepo          ELRepo.org Community Enterprise Linux Repository - el6    127
elrepo-extras   ELRepo.org Community Enterprise Linux Repository - el6      6
epel            Extra Packages for Enterprise Linux 6 - i386             5391
extras          CentOS-6 - Extras                                           1
remi            Les RPM de remi pour Enterprise Linux 6 - i386            403
rpmforge        RHEL 6 - RPMforge.net - dag                              4142
updates         CentOS-6 - Updates                                        783
...

ここまで準備できたら、下記のように、いつもの"yum"コマンドでインストールできます。

# yum -y install drbd84-utils kmod-drbd84

次はVPC上のEC2でDRBDの実験です。
--------
http://www.suz-lab.com

2011年11月17日木曜日

#サンタクラウドの集計(その2)

スズキです。

下記に引き続き、少しづつ進めています。
#サンタクラウドの集計(その1)

該当Tweetの取得は検索APIを利用しているのですが、時間が経つと
昔のTweetが検索できなくなってしまう可能性があります。

なので前回(#AWS77)は、Tweetデータを"togetter"から取得(スクレイピング)を
していたのですが、スクレイピングは好きじゃないので、今回は前回のTweetデータに
新規Tweetを追加する形にしました。

具体的には検索からのTweetの集合から前回のTweet集合の差分をとり、
その差分を前回のTweet集合に追加したものを、最新のTweet集合としています。
そして、その処理を一時間に一回実行しています。

最終的なスクリプトは下記のような感じになっています。

// 初期設定
require_once("/opt/aws/php/default/sdk.class.php");
date_default_timezone_set("Asia/Tokyo");
define("AWS_KEY"       , "AAAAAAAAAAAAAAAAAAAA");
define("AWS_SECRET_KEY", "ssssssssssssssssssssssssssssssssssssssss");
$s3 = new AmazonS3();
$s3->set_region(AmazonS3::REGION_APAC_NE1);

// 前回作成したランキング情報の取得
$response = $s3->get_object(
    "www.suz-lab.com",
    "santacloud/tweet.json",
    array("curlopts" => array(CURLOPT_SSL_VERIFYPEER => false))
);
$ranking_old = json_decode($response->body, true);

// 最新のランキング情報の取得
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
$keyword = "%23jawsug%20%23%E3%82%B5%E3%83%B3%E3%82%BF%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89"; 
$page    = 1;
$ranking_current = array();
do {
    curl_setopt($curl, CURLOPT_URL, "http://search.twitter.com/search.json?result_type=recent&rpp=100&page=$page&q=$keyword");
    $tweet_json   = curl_exec($curl);
    $tweet_object = json_decode($tweet_json, true);
    foreach($tweet_object["results"] as $tweet) {
        if(strpos($tweet["text"], "RT ") === false) {
            array_push($ranking_current, array(
                "tweet_id"         => $tweet["id_str"],
                "tweet_text"       => $tweet["text"],
                "tweet_user_name"  => $tweet["from_user"],
                "tweet_user_image" => $tweet["profile_image_url"],
            ));
        }
    }
    $page++;
} while(count($tweet_object["results"]));

// 前回のランキング情報と最新のランキング情報の差を取得し、その差を前回のランキングに追加
$ranking_diff = array_udiff($ranking_current, $ranking_old, function($a, $b) {
    if($a["tweet_id"] === $b["tweet_id"]) {
        return 0;
    } else {
        return ($a["tweet_id"] > $b["tweet_id"]) ? 1 : -1;
    }
});
$ranking_tmp = array_merge($ranking_diff, $ranking_old);

// Retweet情報を取得
$ranking = array();
foreach($ranking_tmp as $tweet) {
    curl_setopt($curl, CURLOPT_URL, "http://api.twitter.com/1/statuses/" . $tweet["tweet_id"] . "/retweeted_by.json");
    $retweet_json   = curl_exec($curl);
    $retweet_object = json_decode($retweet_json, true);
    if(!isset($retweet_object["error"])) {
        $tweet["retweet_users"] = array();
        foreach($retweet_object as $retweet) {
            array_push($tweet["retweet_users"], array(
                "retweet_user_name"  => $retweet["screen_name"],
                "retweet_user_image" => $retweet["profile_image_url"]
            ));
        }
        array_push($ranking, $tweet);
    }
}

// Retweet数順にソート
usort($ranking, function($a, $b) {
    if(count($a["retweet_users"]) == count($b["retweet_users"])) {
        return 0;
    } else {
        return (count($a["retweet_users"]) > count($b["retweet_users"])) ? -1 : 1;
    }
});

// 最新のランキング情報をS3にアップロード
$response = $s3->create_object("www.suz-lab.com", "santacloud/tweet.json", array(
    "body"        => json_encode($ranking),
    "acl"         => AmazonS3::ACL_PUBLIC,
    "contentType" => "text/javascript",
    "curlopts"    => array(CURLOPT_SSL_VERIFYPEER => false)
));

// 最新のランキング情報をアーカイブとしてS3にアップロード
$date = date("YmdHis");
$response = $s3->create_object("www.suz-lab.com", "santacloud/tweet/$date.json", array(
    "body"        => json_encode($ranking),
    "acl"         => AmazonS3::ACL_PUBLIC,
    "contentType" => "text/javascript",
    "curlopts"    => array(CURLOPT_SSL_VERIFYPEER => false)
));

次は下記を改善する予定です。
  • 同じようなTweetをマージ
  • TwitterAPIの100回/時の実行制限対策

今のTweetは80弱なので、そとそろ100回/時の対策をしないと...
--------
http://www.suz-lab.com

2011年11月15日火曜日

#サンタクラウドの集計(その1)

スズキです。

#サンタクラウド の集計ページはS3上で公開しています。
つまり、S3のWebサイトホスティング機能を使っています。

そして、一時間に一回更新されるランキングの部分は、下記のようなスクリプトで
適当なEC2でランキングデータのJSONファイルを毎時作成し、
JavaScriptで読み込んでいます。

// 初期設定
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
$keyword = "%23jawsug%20%23%E3%82%B5%E3%83%B3%E3%82%BF%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89"; 
$page    = 1;
$ranking = array();

do {

    // 該当するTweetを取得
    curl_setopt($curl, CURLOPT_URL, "http://search.twitter.com/search.json?result_type=recent&rpp=100&page=$page&q=$keyword");
    $tweet_json   = curl_exec($curl);
    $tweet_object = json_decode($tweet_json, true);

    foreach($tweet_object["results"] as $tweet) {

        // RT以外のTweetのRetweetを取得
        if(strpos($tweet["text"], "RT ") === false) {
            curl_setopt($curl, CURLOPT_URL, "http://api.twitter.com/1/statuses/" . $tweet["id_str"] . "/retweeted_by.json");
            $retweet_json   = curl_exec($curl);
            $retweet_object = json_decode($retweet_json, true);

            // Tweetがまだ存在してたら
            if(!isset($retweet_object["error"])) {
                $retweet_users = array();

                // Retweet情報の追加
                foreach($retweet_object as $retweet) {
                    array_push($retweet_users, array(
                        "retweet_user_name"  => $retweet["screen_name"],
                        "retweet_user_image" => $retweet["profile_image_url"]
                    ));
                }

                // Tweet情報の追加
                array_push($ranking, array(
                    "tweet_id"         => $tweet["id_str"],
                    "tweet_text"       => $tweet["text"],
                    "tweet_user_name"  => $tweet["from_user"],
                    "tweet_user_image" => $tweet["profile_image_url"],
                    "retweet_users"    => $retweet_users
                ));
            }
        }
    }

    $page++;
} while(count($tweet_object["results"]));

// Retweet数順にソート
usort($ranking, function($a, $b) {
    if(count($a["retweet_users"]) == count($b["retweet_users"])) {
        return 0;
    } else {
        return (count($a["retweet_users"]) > count($b["retweet_users"])) ? -1 : 1;
    }
});

// JSONファイルにしてS3にアップロード
require_once("/opt/aws/php/default/sdk.class.php");
define("AWS_KEY"       , "AAAAAAAAAAAAAAAAAAAA");
define("AWS_SECRET_KEY", "ssssssssssssssssssssssssssssssssssssssss");
$s3 = new AmazonS3();
$s3->set_region(AmazonS3::REGION_APAC_NE1);

$response = $s3->create_object("www.suz-lab.com", "santacloud/tweet.json", array(
    "body"        => json_encode($ranking),
    "acl"         => AmazonS3::ACL_PUBLIC,
    "contentType" => "text/javascript",
    "curlopts"    => array(CURLOPT_SSL_VERIFYPEER => false)
));

今回は、Tweet情報は"togetter"からでなく、直接Twitterから取得しています。
なので、ほっとくと昔のTweetが取得できなくなってしまうので、早いうちに(今日中に)、
前回のTweet情報に新規分を追加する形にします。

また、TwitterへのAPI接続は一時間に100回しかできないので、Tweetが100以上になると、
対策をしなければいけません...

とりあえず、昔のTweetが消える前に、新規追加形式にしないと...
--------
http://www.suz-lab.com

2011年11月14日月曜日

"AWS SDK for PHP"でS3にファイルアップロードしようとしたら証明書関係のエラー

スズキです。

#サンタクラウド の準備してるときに起きたエラーです。

いつものように下記のコードでS3にファイルをアップロードしようとしたら、

$response = $s3->create_object("www.suz-lab.com", "santacloud/tweet.json", array(
    "body"        => json_encode($ranking),
    "acl"         => AmazonS3::ACL_PUBLIC,
    "contentType" => "text/javascript"
));

次のようなエラーが発生してしまいました。

PHP Fatal error:  Uncaught exception 'RequestCore_Exception' with message 'cURL resource: Resource id #10; cURL error: SSL: certificate subject name '*.s3-ap-northeast-1.amazonaws.com' does not match target host name 'www.suz-lab.com.s3-ap-northeast-1.amazonaws.com' (51)' in /opt/aws/php/1.4.2.1/lib/requestcore/requestcore.class.php:824
Stack trace:
#0 /opt/aws/php/1.4.2.1/services/s3.class.php(723): RequestCore->send_request()
#1 /opt/aws/php/1.4.2.1/services/s3.class.php(1230): AmazonS3->authenticate('www.suz-lab.com', Array)
#2 /opt/suzuki/bin/twitter(38): AmazonS3->create_object('www.suz-lab.com', 'santacloud/twee...', Array)
#3 {main}
  thrown in /opt/aws/php/1.4.2.1/lib/requestcore/requestcore.class.php on line 824

"*.s3-ap-northeast-1.amazonaws.com"の証明書だと、
"www.suz-lab.com.s3-ap-northeast-1.amazonaws.com"にはマッチしない、
というエラーです。
"suz-lab.s3-ap-northeast-1.amazonaws.com"なら問題ないようですが、
バケット名に"."がある場合は、上記のエラーが発生してしまうようです。

回避方法は下記のように、証明書の検証をしないように
"cURL"のオプション(CURLOPT_SSL_VERIFYPEER => false)をつける方法となります。

$response = $s3->create_object("www.suz-lab.com", "santacloud/tweet.json", array(
    "body"        => json_encode($ranking),
    "acl"         => AmazonS3::ACL_PUBLIC,
    "contentType" => "text/javascript",
    "curlopts"    => array(CURLOPT_SSL_VERIFYPEER => false)
));

少し調べてみると、このオプションは
cURL 7.10 以降、デフォルト値は TRUE です。また、 cURL 7.10 以降、デフォルトでインストールされています。
とのことです。

実際にバージョンを調べてみると、下記のとおりで、

# curl --version
curl 7.19.7 (i686-pc-linux-gnu) libcurl/7.19.7 NSS/3.12.7.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp 
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz 

以前このオプション無しで問題なかったのは、
この"cURL"のバージョンが古かったからだと思います。

とりあえず、解決してよかった...
--------
http://www.suz-lab.com

2011年11月8日火曜日

Mailmanのダイジェストメールを一日一回から一時間に一回にする

スズキです。

"cloudpack"の監視サービスで、一時間に一回アラートメールの
ダイジェストメールを送る必要性が出てきました。

実現手段として、ML(Mailman)のダイジェストメール機能
(一日分のメールを一つのメールにまとめて一日一回送る機能)
を利用することにしたのですが、当然、一日一回を一時間に一回にする必要があります。

といっても、簡単で、ダイジェストメールは下記のcrontabで、
定期的にコマンドを実行することで実現しています。

# cat /etc/cron.d/mailman 
...
# Noon, mail digests for lists that do periodic as well as threshhold delivery.
0 * * * * mailman /usr/lib/mailman/cron/senddigests
...

デフォルトは一日一回になるように設定されているのですが、
上記のように一時間に一回実行されるように変更すれば、
ダイジェストメールも一時間に一回になります。

ちなみに、"/etc/cron.d/mailman"は"/etc/init.d/mailman start"で、
"/usr/lib/mailman/cron/crontab.in"が"/etc/cron.d/mailman"に
コピーされるようになっています。

ですので、恒久的に"cron"の設定を反映する場合は、
"/usr/lib/mailman/cron/crontab.in"も同様に修正する必要があります。

Mailmanはこれで終了。次はML運用方法の整理とSES化...
--------
http://www.suz-lab.com

Mailmanのメールの文章を変更

スズキです。

Mailmanは監視関係で送信しているアラートメールの、1時間ごとダイジェストを
送るために利用しています。
なので、ダイジェストメールの文面も、それように変更したいところです。

文面は下記にテンプレートがあるので、適当なものを修正すればOKです。

# pwd
/usr/lib/mailman/templates/ja
# ls *.txt
adminsubscribeack.txt    bounce.txt    cronpass.txt  invite.txt    nomoretoday.txt  postheld.txt  subauth.txt       unsubauth.txt
adminunsubscribeack.txt  checkdbs.txt  disabled.txt  masthead.txt  postack.txt      probe.txt     subscribeack.txt  userpass.txt
approve.txt              convert.txt   help.txt      newlist.txt   postauth.txt     refuse.txt    unsub.txt         verify.txt

メールのサブジェクトなどは、下記を修正する必要もあります。

# pwd
/usr/lib/mailman/messages/ja/LC_MESSAGES
# ls
mailman.mo  mailman.po

修正は次のように"po"ファイルを編集して、"gettext"パッケージの
msgfmtで"mo"ファイルを更新する形となります。

# yum -y install gettext
# msgfmt mailman.po -o mailman.mo

変更したい文面が、どのファイルにあるかの調査がめんどう...
--------
http://www.suz-lab.com

Mailmanのインストール

スズキです。

監視の仕組みでML的な機能が必要になってしまったので...

早速インストールして起動してみると、エラーです...

# yum -y install mailman
# /etc/init.d/mailman start
mailman を起動中: サイトリスト名がありません: mailman
                                                           [失敗]

"mailman"という名前のMLが無いとのことなので、下記のように作成します。

# /usr/lib/mailman/bin/newlist mailman
Enter the email of the person running the list: alert@cloudpack.jp
Initial mailman password: 
To finish creating your mailing list, you must edit your /etc/aliases (or
equivalent) file by adding the following lines, and possibly running the
`newaliases' program:

## mailman mailing list
mailman:              "|/usr/lib/mailman/mail/mailman post mailman"
mailman-admin:        "|/usr/lib/mailman/mail/mailman admin mailman"
mailman-bounces:      "|/usr/lib/mailman/mail/mailman bounces mailman"
mailman-confirm:      "|/usr/lib/mailman/mail/mailman confirm mailman"
mailman-join:         "|/usr/lib/mailman/mail/mailman join mailman"
mailman-leave:        "|/usr/lib/mailman/mail/mailman leave mailman"
mailman-owner:        "|/usr/lib/mailman/mail/mailman owner mailman"
mailman-request:      "|/usr/lib/mailman/mail/mailman request mailman"
mailman-subscribe:    "|/usr/lib/mailman/mail/mailman subscribe mailman"
mailman-unsubscribe:  "|/usr/lib/mailman/mail/mailman unsubscribe mailman"

Hit enter to notify mailman owner...

そして上記の指示通り、下記のように"aliases"に追記し、

# cat /etc/aliases
...
## mailman mailing list
mailman:              "|/usr/lib/mailman/mail/mailman post mailman"
mailman-admin:        "|/usr/lib/mailman/mail/mailman admin mailman"
mailman-bounces:      "|/usr/lib/mailman/mail/mailman bounces mailman"
mailman-confirm:      "|/usr/lib/mailman/mail/mailman confirm mailman"
mailman-join:         "|/usr/lib/mailman/mail/mailman join mailman"
mailman-leave:        "|/usr/lib/mailman/mail/mailman leave mailman"
mailman-owner:        "|/usr/lib/mailman/mail/mailman owner mailman"
mailman-request:      "|/usr/lib/mailman/mail/mailman request mailman"
mailman-subscribe:    "|/usr/lib/mailman/mail/mailman subscribe mailman"
mailman-unsubscribe:  "|/usr/lib/mailman/mail/mailman unsubscribe mailman"

"newaliases"で反映して、念のためPostfixも再起動して、
Mailmanを起動すると、今度は下記のように無事、成功しました。

# newaliases
# /etc/init.d/postfix restart
Shutting down postfix:                                     [  OK  ]
Starting postfix:                                        
# /etc/init.d/mailman start
Starting mailman:                                          [  OK  ]

これで、アラートメールのダイジェストができる...
--------
http://www.suz-lab.com

2011年11月7日月曜日

iPhone4Sでプレゼンテーション

スズキです。

下記、LTしてきました。
JAWS-UG大阪第4回勉強会LT資料(AWStatsでS3&CloudFrontのアクセス解析)

今回のLTは、iPhone4Sのミラーリング機能を使って、
iPhone4Sを直接プロジェクターにつないで行いました。

プロジェクターにつなぐには、下記のVGAアダプターが必要となります。


実際にプロジェクターに移した資料は、パワーポイントで作成し、
PDFに変換して、Dropboxに入れて、GoodReaderでiPhone側で表示してました。

注意点は2つほどあります。

  • プレゼン中に画面がロックされないように、画面ロックを無効にする。
  • プレゼン中にメールなどの通知がこないようにフライトモードにしておく。

パワーポイントで資料作ってる所がまだダサい感じですが、
これでプレゼンだけならiPhoneのみ持っていけばよくなりました。

Japan AWS User Group (JAWS-UG) - Kyoto勉強会 第2回
もiPhoneでLT予定です。
--------
http://www.suz-lab.com

CentOS(6.0)でロケール関係の設定

スズキです。

CentOS(5.x)では下記のように設定して問題なかったのですが、
CentOSで初期起動時にLANGを設定
下記のCentOS(6.0)ではうまくいきませんでした…
SUZ-LAB謹製 CentOS AMI (6.0.3 32bit ap-northeast-1)

まず、利用できるロケールを確認します。

# locale -a | grep ja_JP
ja_JP
ja_JP.eucjp
ja_JP.ujis
ja_JP.utf8

次に、下記のように、LANGが"ja_JP.utf8"になるように設定します。

# cat /etc/sysconfig/i18n
LANG="ja_JP.utf8"

そして再起動後、下記のようにロケールの状態を確認すると、
警告メッセージが出てしまいました…

# locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: LC_ALL?????????????????????: ??????????????????????
LANG=ja_JP.utf8
LC_CTYPE=UTF-8
LC_NUMERIC="ja_JP.utf8"
LC_TIME="ja_JP.utf8"
LC_COLLATE="ja_JP.utf8"
LC_MONETARY="ja_JP.utf8"
LC_MESSAGES="ja_JP.utf8"
LC_PAPER="ja_JP.utf8"
LC_NAME="ja_JP.utf8"
LC_ADDRESS="ja_JP.utf8"
LC_TELEPHONE="ja_JP.utf8"
LC_MEASUREMENT="ja_JP.utf8"
LC_IDENTIFICATION="ja_JP.utf8"
LC_ALL=

解決策としては、LC_CTYPEも"ja_JP.utf8"になるように設定すると、

# cat /etc/sysconfig/i18n
LANG="ja_JP.utf8"
LC_CTYPE="ja_JP.utf8"

再起動後、下記のように警告メッセージが出なくなります。

# locale
LANG=ja_JP.utf8
LC_CTYPE=ja_JP.utf8
LC_NUMERIC="ja_JP.utf8"
LC_TIME="ja_JP.utf8"
LC_COLLATE="ja_JP.utf8"
LC_MONETARY="ja_JP.utf8"
LC_MESSAGES="ja_JP.utf8"
LC_PAPER="ja_JP.utf8"
LC_NAME="ja_JP.utf8"
LC_ADDRESS="ja_JP.utf8"
LC_TELEPHONE="ja_JP.utf8"
LC_MEASUREMENT="ja_JP.utf8"
LC_IDENTIFICATION="ja_JP.utf8"
LC_ALL=

"SUZ-LAB謹製 CentOS AMI"にも反映しなければ…
--------
http://www.suz-lab.com

2011年11月4日金曜日

JAWS-UG大阪第4回勉強会LT資料(AWStatsでS3&CloudFrontのアクセス解析)

スズキです。

簡単なものですが...


以下は本ブログの関連記事です。

それでは、大阪の皆さん、よろしくお願いします。
--------
http://www.suz-lab.com

JAWS-UG大阪&京都でLTします

スズキです。

下記のJAWS-UG(大阪&京都)でLTします。

Japan AWS User Group (JAWS-UG) - Osaka勉強会 第4回
日時 :2011/11/05 14:00 to 18:00
会場 :テレビ大阪 西館1F (大阪市中央区大手前1-2-15)
AWStatsでS3&CloudFrontのアクセス解析

Japan AWS User Group (JAWS-UG) - Kyoto勉強会 第2回
日時 :2011/11/10 19:30 to 21:30
会場 :株式会社はてな 8F セミナールーム (京都府京都市中京区高宮町206 御池ビル9F)
S3とVarnishを組み合わせた事例的なもの

近頃携わっていた、「cloudpack Tools」の経験をアウトプットする感じでしょうか?

セキュリティグループの状態をテキストに出力してバージョン管理するやつも、
どこかで話したいなー...
--------
http://www.suz-lab.com

2011年11月2日水曜日

Monitでファイルの存在チェック

スズキです。

下記のような設定でファイルの存在確認をし、
存在しない場合は、適当なスクリプトを実行できるようになります。

check file test with path /tmp/test.txt
   start program = "/tmp/start.sh"
   stop program = "/tmp/stop.sh"
   if does not exist then restart
   if 5 restarts within 5 cycles then timeout

上記はファイルが存在しなかったら、
リスタート(start.sh & stop.sh)するようになっています。
そして、5回リスタートしても復旧しなかった場合は、
それ以上リスタートしなくなるようになります。

これを"s3fs"の監視&自動復旧に使おう。
--------
http://www.suz-lab.com

RDS(MySQL)をNagiosで監視

スズキです。

RDSは下記のような感じでNagiosで監視しています。

define host {
    use                    cloudpack-dummy-host
    host_name              suz-lab-rds
    address                suzlab.xxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
}
define service {
    use                    cloudpack-service
    host_name              suz-lab-rds
    service_description    dns
    check_command          check-dns-ip-cloudpack!xxx.xxx.xxx.xxx
}
define service {
    use                    cloudpack-service
    host_name              suz-lab-rds
    service_description    mysql
    check_command          check-mysql-cloudpack!user!pass
}

define host {
    name                     cloudpack-dummy-host
    use                      generic-host
    register                 0
    check_command            check-dummy-cloudpack
    max_check_attempts       1
    normal_check_interval    5
    retry_check_interval     1
}
define service {
    name                     cloudpack-service
    use                      generic-service
    register                 0
    max_check_attempts       3
    normal_check_interval    5
    retry_check_interval     1
}
define command {
    command_name    check-dummy-cloudpack
    command_line    $USER1$/check_dummy 0
}
define command {
    command_name    check-mysql-cloudpack
    command_line    $USER1$/check_mysql -H $HOSTADDRESS$ -u $ARG1$ -p $ARG2$
}
define command {
    command_name    check-dns-ip-cloudpack
    command_line    $USER1$/check_dns -H $HOSTADDRESS$ -a $ARG1$
}
(ブログ用に調整しているので、このままでは動作しないかもしれません...)

まず、RDSのHOSTとしてのチェックですが、RDS自体はPINGが通らないので、
下記のように"check_dummy"コマンドを利用するようにしています。
Nagiosで"ping"できないHostを監視

次に、下記で紹介したMulti-AZのフェイルオーバー検知として、DNSのチェックも行なっています。
RDSのフェイルオーバーに関する注意点

最後は、通常のMySQLの監視です。

ブログ、全然書いてない...
--------
http://www.suz-lab.com

2011年10月25日火曜日

PHPでSESを使ってみた

スズキです。

以前下記で、PerlスクリプトでのSESの利用を紹介しましたが、
SESを使ってみた(AWS)
今回は、PHPで同様のことをやってみました。

まずは、下記スクリプトで送信元メールアドレスの承認です。

#!/usr/bin/php
<?php
require_once("/opt/aws/php/default/sdk.class.php");
$ses = new AmazonSES(
    "XXXXXXXXXXXXXXXXXXXX",
    "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
);
$response = $ses->verify_email_address($argv[1]);
var_dump($response->body);
?>

下記のようにスクリプトを実行すると、

# ./verify-email-address alert@suz-lab.com

次のような結果となり、

object(CFSimpleXML)#5 (2) {
    ["@attributes"]=>
    array(1) {
        ["ns"]=>
        string(40) "http://ses.amazonaws.com/doc/2010-12-01/"
    }
    ["ResponseMetadata"]=>
    object(CFSimpleXML)#3 (1) {
        ["RequestId"]=>
        string(36) "885fc1ba-fe3a-11e0-b112-7f7789067247"
    }
}

下記のようなメールが届きます。

Dear Amazon SES customer:

We have received a request to authorize an email address for use with Amazon SES. To confirm that you are authorized to use this email address, please go to the following URL:

https://email-verification.us-east-1.amazonaws.com/?...
Your request will not be processed unless you confirm the address using this URL.

To learn more about sending email from Amazon SES, please refer to the Amazon SES Developer Guide.

Sincerely, Amazon Web Services

メール本文のURLをクリックすると、下記画面となり、
指定したメールアドレスが承認され、そのアドレスを送信もととしてメールが、
SESで送信できるようになります。


実際に下記のスクリプトでメールを送信してみます。

#!/usr/bin/php
<?php
require_once("/opt/aws/php/default/sdk.class.php");
$ses = new AmazonSES(
    "XXXXXXXXXXXXXXXXXXXX",
    "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
);
$response = $ses->send_email(
    $argv[1],
    array("ToAddresses" => $argv[2]),
    array(
        "Subject.Data"   => "Email Test " . time(),
        "Body.Text.Data" => "This is a simple test message " . time()
    )
);
var_dump($response->body);
?>

次のように、スクリプトを実行すると、

# ./send-email alert@suz-lab.com suzuki@suz-lab.com

下記のようなレスポンスになり、メールが送信されます。
※既にPuroductionモードなので任意のメールアドレスに送信できます。

object(CFSimpleXML)#5 (3) {
    ["@attributes"]=>
    array(1) {
        ["ns"]=>
        string(40) "http://ses.amazonaws.com/doc/2010-12-01/"
    }
    ["SendEmailResult"]=>
    object(CFSimpleXML)#3 (1) {
        ["MessageId"]=>
        string(60) "0000013335e96bb2-2f904dc4-78fe-433e-8eed-1320b955f687-000000"
    }
    ["ResponseMetadata"]=>
    object(CFSimpleXML)#6 (1) {
        ["RequestId"]=>
        string(36) "017ee15c-fe3c-11e0-a309-e313d04d677b"
    }
}

これでNagiosのアラートメールがSESでの送信なる...
----------
http://www.suz-lab.com

2011年10月24日月曜日

PHPで外部テキストを変数展開

スズキです。

いろいろあってNagiosのアラートメール通知を、
サービスごとに行えるようにしているところです。

このような仕組みを作るときは、メールテンプレートをどうしようか?
と悩むところだと思いますが、今回は可能な限りシンプルに仕様と思い、
外部ファイルのテキストを"eval"で変数展開する方法を試してみました。

まずは下記のようなメールテンプレートを用意しておきます。

$NOTIFICATIONTYPE $CONTACTGROUPNAME $HOSTNAME $HOSTSTATE

TYPE     : $NOTIFICATIONTYPE($HOSTSTATE)
ACCOUNT  : $CONTACTGROUPNAME
HOST     : $HOSTNAME($HOSTADDRESS)
DATETIME : $LONGDATETIME
OUTPUT   : $HOSTOUTPUT

これを下記のスクリプトで変数展開します。

$CONTACTGROUPNAME = $argv[1];
$HOSTNAME         = $argv[2];
$NOTIFICATIONTYPE = $argv[3];
$HOSTSTATE        = $argv[4];
$HOSTADDRESS      = $argv[5];
$LONGDATETIME     = $argv[6];
$HOSTOUTPUT       = $argv[7];

$template = file_get_contents("/opt/cloudpack//conf/$CONTACTGROUPNAME/nagios/mail/host/$HOSTNAME.txt");
$eval = "return <<<EVAL\n{$template}EVAL;\n";
file_put_contents("/tmp/sample.txt", eval($eval));

ごちゃごちゃしていますが、要点としては、外部テキストを
return <<<EVAL
外部テキスト
EVAL;
といった感じに調整して、"eval"しています。
※タイトルとは関係ないですが外部ファイルは取得は引数によって変更する形です。

そして下記のように実行すると、

./tmp-nagios-notify-host "admin_cloudpack.jp" "admin-a" "RECOVER" "UP" "xxx.xxx.xxx.xxx" "2011/11/11 11:11:11" ""

ちゃんと変数展開されて、次のようなファイルが出力されます。

RECOVER admin_cloudpack.jp admin-a UP

TYPE     : RECOVER(UP)
ACCOUNT  : admin_cloudpack.jp
HOST     : admin-a(xxx.xxx.xxx.xxx)
DATETIME : 2011/11/11 11:11:11
OUTPUT   : 

次は上記で作成したメール本文をSESで送るところです。

SES、久しぶりだ...
--------
http://www.suz-lab.com

2011年10月21日金曜日

"cloudpack"と"Varnish"

スズキです。

"cloudpack"には「リダイレクトサービス」と「プロキシーサービス」と呼ばれる
付加サービスがあり内部的にはVarnishを利用しています。


リダイレクトサービスは下記のように、"www"無しのドメイン名(suz-lab.com)のみで
アクセスしてきたHTTPリクエストを、"www"のついたものにリダイレクトする
サービスです。


このサービスは、ELB、S3、CloudFrontを独自ドメインで利用するときに、DNSに対して
CNAMEレコードで設定する必要があるのですが、ドメイン名(suz-lab.com)に対しては
CNAMEが利用できないので、その対策としてのサービスです。
(現在はELBに関してはRoute53で対応できますが...)

そしてVarnishでどのように実現しているかというと、下記の記事で紹介した通りです。
Varnishでリダイレクト専用Webサーバの構築


プロキシーサービスはVarnishで実現できることを、共通のVarnishサーバで「if文」を用い
個別(アクセスHost名ごとなど)に設定するものです。

今までの実績として、下記のようなものを行っています。


キャッシュを使ったコンテンツの高速配信


普通のキャッシュサーバでるVarnishの使い方です。場合によっては、
ディレクトリや拡張子ごとに、キャッシュの設定を細かく変更して利用しています。

このあたりは本ブログでも下記で紹介しています。
Varnishでキャッシュ期限を指定


FacebookアプリのコンテンツをS3に配置


Facebookアプリで利用するコンテンツをS3に配置しようとしたら、
FacebookアプリはコンテンツをPOSTメソッドで取得しにくるのですが、
S3はGETメソッドしか対応していないので、Varnishを経由させPOSTメソッドを
GETメソッドに変換してFacebookアプリでもS3を利用できるようにしています。

このあたりは本ブログでも下記で紹介しています。
Varnish経由でS3のコンテンツをPOSTで取得(Facebookアプリ用)


S3(独自ドメイン設定)でHTTPSを利用


S3のコンテンツを独自ドメインで公開することは、現在HTTPではできるのですが、
HTTPSは対応していません。ですのでS3のコンテンツを独自ドメインでHTTPSで
公開する場合は、上記のように、ELBでHTTPSのリクエストを受け、ELBの
SSLターミネーションでHTTPSの処理を行い、その後Varnish経由でS3にHTTPで
接続する形で実現しています。

このあたりは本ブログでも下記で紹介しています。
ELBを利用して1つのEC2で複数ドメインのHTTPS(SSL)


S3で携帯サイト(Softbank)の著作権保護


S3で独自ヘッダをつけることはできるのですが、"x-amz-meta-XXX"の形式のキーしか
付けることができません。ですのでSoftbank携帯サイトでの著作権保護機能に必要な
"x-jphone-copyright"のようなヘッダは直接S3で付けることができないので、
アクセスをVarnish経由にしてVarnishでそのヘッダを付けるようにしています。

このあたりは本ブログでも下記で紹介しています。
S3のオブジェクトのユーザー独自ヘッダのキーの形式は"x-amz-meta-XXX"
VarnishでS3のコンテンツに"x-jphone-copyright"ヘッダを付与


以上のように"cloudpack"ではAWSのサービスを補完するような形でVarnishを利用しています。

そろそろ「プロキシー職人」をメニュー化するか!?
--------
http://www.suz-lab.com

AWStatsでS3のログを解析

スズキです。

以前、下記のようにCloudFrontのログをAWStatsで解析してみましたが、
AWStatsでCloudFrontのログを解析
今回は、S3のログをAWStatsで解析してみます。

S3のアクセスログのフォーマットは下記で説明されている通りで
Server Access Log Format
具体的には、下記のような感じです。

323b798c3d8329a972f387aa85470ab7fd4f8fcc6a038055d847a0ca91770090 www.suz-lab.com [20/Oct/2011:00:38:09 +0000] 000.000.000.000 - 3001D4D544D44477 WEBSITE.GET.OBJECT image/logo.jpg "GET /image/logo.jpg HTTP/1.1" 200 - 7366 7366 26 25 "-" "check_http/v1.4.14 (nagios-plugins 1.4.14)" -

これをAWStatsで解析するには、設定ファイルを下記のようにする必要があります。

...
LogType=W
LogFormat="%other %virtualname %time1 %host %other %other %other %other %methodurl %code %other %bytesd %other %other %other %refererquot %uaquot %other"
LogSeparator=" "
LoadPlugin="timezone +9"
...

"LogFormat"は上記のS3のログフォーマットに合わせた形に調整し、
またS3のアクセスログのタイムスタンプはUTCなので、解析結果が日本時間になるように、
"LoadPlugin"で"timezone +9"も指定しています。

あとは、いつものAWStatsでWebでS3のアクセス状況を確認することができるはずです。

AWStats系はおわりで、次はNagios系の整理だ。。。
--------
http://www.suz-lab.com

2011年10月19日水曜日

RDSのスナップショットの上限は20らしい

スズキです。

RDSのスナップショットを取るPHPスクリプトが下記のような
エラーを出力して、スナップショットがとれない現象がありました。
cannot create more than 20 user snapshots

RDSのスナップショットの上限は、RDSインスタンスと同じ20がデフォルトのようです。

下記でも同じようなことを質問していました。
"cannot create more than 20 user snapshots" on RDS?

上限を上げるためには下記のフォームから申請する必要があります。
RDSインスタンスの上限値を上げるためのフォームですが、
自由入力欄で別途、スナップショット数の上限値UPを依頼すれば、
インスタンス数とは別の上限値をスナップショットの上限値として設定できるらしいです。


びっくりした...
--------
http://www.suz-lab.com

EC2の状態をYAMLで出力(Gitでバージョン管理)

スズキです。

下記のセキュリティグループに続き、
セキュリティグループの状態をテキスト化&バージョン管理(Git)
今度はEC2の構成もYAMLで出力してGitで管理してGitWebで閲覧できるようにしてみました。

YAMLに出力するPHPスクリプトは下記のような感じです。

require_once("/opt/aws/php/default/sdk.class.php");

$regions = array(
  AmazonEC2::REGION_US_E1,
  AmazonEC2::REGION_US_W1,
  AmazonEC2::REGION_EU_W1,
  AmazonEC2::REGION_APAC_SE1,
  AmazonEC2::REGION_APAC_NE1
);

$yaml = yaml_parse_file("/opt/cloudpack/conf/$argv[1]/credential.yaml");
$ec2 = new AmazonEC2($yaml["accessKey"], $yaml["secretKey"]);

foreach($regions as $region) {
  $ec2->set_region($region);
  $response = $ec2->describe_instances();
  file_put_contents(
    "/opt/cloudpack/var/$argv[1]/git/ec2/$region.yaml",
    yaml_emit(json_decode($response->body->reservationSet->to_json(), true))
  );
}

exit(0);

今回は、アクセスキーやシークレットキーを下記のように
外部のYAMLファイルから取得するようにしています。
PHPでYAMLファイルの読み込み

セキュリティグループ同様、下記のようにリージョンごとにYAMLファイルが作成されます。

# ./output-ec2-create suz-lab
# ls -1 /opt/cloudpack/var/suz-lab/git/ec2/
ap-northeast-1.yaml
ap-southeast-1.yaml
eu-west-1.yaml
us-east-1.yaml
us-west-1.yaml

実際のYAMLファイルは以下の通りです。

# cat /opt/cloudpack/var/suz-lab/git/ec2/ap-northeast-1.yaml 
---
item:
  reservationId: r-00000000
  ownerId: "000000000000"
  groupSet:
    item:
    - groupId: sg-00000000
      groupName: default
    - groupId: sg-00000000
      groupName: web
  instancesSet:
    item:
      instanceId: i-00000000
      imageId: ami-00000000
      instanceState:
        code: "16"
        name: running
      privateDnsName: ip-000-000-000-000.ap-northeast-1.compute.internal
      dnsName: ec2-000-000-000-000.ap-northeast-1.compute.amazonaws.com
      reason: []
      keyName: suz-lab_ap-northeast-1
      amiLaunchIndex: "0"
      productCodes: []
      instanceType: m1.large
      launchTime: "2011-09-20T01:51:18.000Z"
      placement:
        availabilityZone: ap-northeast-1a
        groupName: []
        tenancy: default
      kernelId: aki-00000000
      ramdiskId: ari-00000000
      monitoring:
        state: enabled
      privateIpAddress: 000.000.000.000
      ipAddress: 000.000.000.000
      groupSet:
        item:
        - groupId: sg-00000000
          groupName: default
        - groupId: sg-00000000
          groupName: web
      architecture: x86_64
      rootDeviceType: ebs
      rootDeviceName: /dev/sda1
      blockDeviceMapping:
        item:
        - deviceName: /dev/sda1
          ebs:
            volumeId: vol-00000000
            status: attached
            attachTime: "2011-09-20T01:51:37.000Z"
            deleteOnTermination: "true"
        - deviceName: /dev/sdf1
          ebs:
            volumeId: vol-7833b915
            status: attached
            attachTime: "2011-09-20T01:53:46.000Z"
            deleteOnTermination: "false"
      virtualizationType: paravirtual
      clientToken: []
      tagSet:
        item:
          key: Name
          value: web01
      hypervisor: xen
  requesterId: 058890971305
...

そして適当にEC2の内容を変更して(タグを変更)、コミットして、
下記のようにGitWebで差分を確認すると
GitWebでセキュリティグループの状態を確認
次のようにセキュリティグループ同様、構成の変更がわかります。


あとは、同じパターンで増やせばいいだけ。
--------
http://www.suz-lab.com

2011年10月18日火曜日

PHPでYAMLファイルの読み込み

スズキです。

近頃、AWS関係の運用ツール作る機会が増えたので、
基本となる"accessKey"と"secretKey"をPHPでファイルから読み込む部分を
改めて試してみました。

とりあえず他の言語から使う可能性もあるので、設定ファイルは
下記のようなYAMLとしています。

▼ credential.yaml
---
accessKey: "XXXXXXXXXXXXXXXXXXXX"
secretKey: "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
...

そして次のようなPHPスクリプトで簡単にYAMLファイルを読み込むことができます。

▼ credential.php
$yaml = yaml_parse_file("credential.yaml");
print($yaml["accessKey"] . "\n");
print($yaml["secretKey"] . "\n");

実行すると、ちゃんとファイルを読み込み、必要な値が取得できていることがわかります。

# php credential.php
XXXXXXXXXXXXXXXXXXXX
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

だいぶ基本的な仕様がかたまってきたかも。
--------
http://www.suz-lab.com

GitWebでセキュリティグループの状態を確認

スズキです。

下記のようにセキュリティーグループの状態をGitでバージョン管理するようになったので、
セキュリティグループの状態をテキスト化&バージョン管理(Git)
これをWebUIで閲覧できるようにします。

WebUIは以前、下記で紹介したGitWebを利用することにしました。
"gitweb"をインストール

GitWebはかなりお手軽で、下記の設定(Apache)がきいているディレクトリに、
"/var/www/git/gitweb.cgi"をコピーするだけで利用できます。

Options +ExecCGI
AddHandler cgi-script .cgi

当然、多少の設定は必要ですが複数のGitWebを利用する場合は、"/etc/gitweb.conf"に
指定するのではなく、下記のように"gitweb.cgi"に直接記述する形となります。

...
our $projectroot = "/opt/cloudpack/var/suz-lab/git";
our @stylesheets = ("http://cdn.cloudpack.jp/git/gitweb.css");
our $logo = "http://cdn.cloudpack.jp/git/git-logo.png";
our $favicon = "http://cdn.cloudpack.jp/git/git-favicon.png";
our $javascript = "http://cdn.cloudpack.jp/git/gitweb.js";
...

せっかくなので、静的なファイルはCloudFrontに置いています。

そして、ようやく下記のように、セキュリティグループの更新履歴や差分を
下記のようにWebで確認する手段が整いました。


もう少し微調整したら、memorycraftにバトンタッチかな。
--------
http://www..suz-lab.com

2011年10月17日月曜日

セキュリティグループの状態をテキスト化&バージョン管理(Git)

スズキです。

下記で紹介した内容のアップデートです。
セキュリティグループの状態をテキストで取得

セキュリティグループをテキスト化するスクリプトは、
上記から下記のように変更しています。

require_once("/opt/aws/php/default/sdk.class.php");

$regions = array(
  AmazonEC2::REGION_US_E1,
  AmazonEC2::REGION_US_W1,
  AmazonEC2::REGION_EU_W1,
  AmazonEC2::REGION_APAC_SE1,
  AmazonEC2::REGION_APAC_NE1
);

if(!file_exists("/opt/suzuki/var/suzuki_suz-lab.com/git/securitygroup")) {
  mkdir("/opt/suzuki/var/suzuki_suz-lab.com/git/securitygroup", 0644, true);
}

$ec2 = new AmazonEC2(
  "XXXXXXXXXXXXXXXXXXXX",
  "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
);

foreach($regions as $region) {
  $ec2->set_region($region);
  $response = $ec2->describe_security_groups();
  file_put_contents(
    "/opt/suzuki/var/suzuki_suz-lab.com/git/securitygroup/$region.yaml",
    yaml_emit(json_decode($response->body->securityGroupInfo->to_json(), true))
  );
}

exit(0);

実行すると、下記のようにリージョンごとのセキュリティグループのYAMLが出力されます。

ap-northeast-1.yaml 
ap-southeast-1.yaml
eu-west-1.yaml
us-east-1.yaml
us-west-1.yaml

前回の出力するYAMLは中途半端なものだったので、今回は"php-pecl-yaml"パッケージを
インストールして利用可能になる"yaml_emit"を使ってYAMLを作成しています。
(いったんJSONにしてYAMLにするといい感じになります)

---
item:
- ownerId: "000000000000"
  groupId: sg-xxxxxxxx
  groupName: default
  groupDescription: default group
  ipPermissions:
    item:
    - ipProtocol: tcp
      fromPort: "0"
      toPort: "65535"
      groups:
        item:
          userId: "000000000000"
          groupId: sg-xxxxxxxx
          groupName: default
      ipRanges: []
...

というのを下記スクリプトを定期的に実行することで、
セキュリティグループのバージョン管理(Git)を行います。

#!/bin/sh
/opt/suzuki/bin/output-securitygroup
cd /opt/suzuki/var/suzuki_suz-lab.com/git
git add .
git commit -m "cloudpack" .

適当にセキュリティグループを変更し、上記を再度実行し
"git diff HEAD^"で差分を確認すると、以下のように、
変更された部分を確認することができます。

diff --git a/securitygroup/ap-northeast-1.yaml b/securitygroup/ap-northeast-1.yaml
index 719a63c..28bc7f6 100644
--- a/securitygroup/ap-northeast-1.yaml
+++ b/securitygroup/ap-northeast-1.yaml
@@ -55,13 +55,6 @@ item:
         item:
         - cidrIp: xxx.xxx.xxx.xxx/32
         - cidrIp: yyy.yyy.yyy.yyy/32
-    - ipProtocol: tcp
-      fromPort: "3389"
-      toPort: "3389"
-      groups: []
-      ipRanges:
-        item:
-          cidrIp: xxx.xxx.xxx.xxx/32
   ipPermissionsEgress: []
 - ownerId: "000000000000"
   groupId: sg-xxxxxxxx

次は、これをGitWebで閲覧します。
--------
http://www.suz-lab.com

2011年10月11日火曜日

S3のログをデイリーにまとめる

スズキです。

S3のログは下記のように1日の中でもさらに細切れになった単位でファイル化されます。
access_log-2011-10-11-00-15-33-7E85B60CC1846A80
access_log-2011-10-11-00-16-02-74E3E7E940850885
access_log-2011-10-11-01-15-16-4F73FF952D5D961D
access_log-2011-10-11-01-15-38-3290ED7965705AC2
access_log-2011-10-11-01-15-51-8C02CBA57C279DE8
このままでは扱いづらいので、1日ごとにまとめる(デイリー化)下記のような
シェルスクリプトを作成してみました。

#!/bin/sh
export LANG=C

LOGDIR="/opt/cloudpack/s3/suzuki_suz-lab.com/www.suz-lab.com_s3-log-original"
ARCHIVEDIR="/opt/cloudpack/s3/suzuki_suz-lab.com/www.suz-lab.com_s3-log-daily"
WORKDIR="/opt/cloudpack/tmp/`mkpasswd -l 8 -s 0 -C 8 -c 0 -d 0`"

TARGETDATE=`date -d "$1" "+%Y-%m-%d"`
NEXTDATE=`date -d "$1 1 day" "+%Y-%m-%d"`
PATTERN="\[`date -d "$1" "+%d/%b/%Y"`"

mkdir -p $WORKDIR
cp $LOGDIR/access_log-$TARGETDATE* $WORKDIR/
cp $LOGDIR/access_log-$NEXTDATE* $WORKDIR/
logresolvemerge.pl $WORKDIR/* | grep "$PATTERN" > $WORKDIR/access_log-$TARGETDATE
gzip $WORKDIR/access_log-$TARGETDATE
cp $WORKDIR/access_log-$TARGETDATE.gz $ARCHIVEDIR/access_log-$TARGETDATE.gz
cp $WORKDIR/access_log-$TARGETDATE.gz $ARCHIVEDIR/access_log.gz
rm -rf $WORKDIR

上記のシェルスクリプトは次のような感じで作成しています。

(1) S3のログはs3fsでファイルシステムとしてマウントして取得

(2) 作業ディレクトリは"/opt/cloudpack/tmp/XXXXXXXX"

(3) デイリー化したいログの日付は引数に20111010のように指定

(4) 指定した日付の元ログ(次の日のログも)を作業ディレクトリにコピー
次の日付けののログ(0:00付近)にも前日のログが残っていることがあります。

(5) 一つのログにマージして該当日付のものだけファイル化
"logresolvemerge.pl"でマージして、該当日付にマッチするものだけ"grep"しています。

(6) 圧縮して指定ディレクトリに配置
ログ解析用に最新ものが"access_log.gz"になるようにもしています。

(7) 作業ディレクトリを削除

ちなみに"mkpasswd"と"logresolvemerge.pl"は下記のように本ブログで紹介済みです。

▼mkpasswd
Linuxでランダム文字列の生成

▼logresolvemerge.pl
CloudFrontのログとAWStatsを使ったログのマージ&ソート(日付)

次はこのログをAWStatsで解析します。
--------
http://www.suz-lab.com

2011年10月4日火曜日

Linuxでランダム文字列の生成

スズキです。

"mkpasswd"でできるのですが、"expect"パッケージに含まれているので、
下記のようにインストールしておきます。

# yum -y install expect

オプションは次のようになっています。
-l : パスワードの文字数
-d : 数字の最小数
-c : 小文字の最小数
-C : 大文字の最小数
-s : 特殊文字の最小数

大文字だけの8文字のランダム文字列が必要な場合は、下記のオプションで可能です。

# mkpasswd -l 8 -s 0 -C 8 -c 0 -d 0
LTZWEACZ

とりあえず、シェルスクリプト用です。
--------
http://www.suz-lab.com

2011年10月3日月曜日

SUZ-LAB謹製 CentOS AMI (6.0.3 32bit ap-northeast-1)

スズキです。

下記をアップデートしました。
SUZ-LAB謹製 CentOS AMI (6.0.2 32bit ap-northeast-1)
AMIを「suz」で検索してもらえれば、
811118151095/suz-lab_ebs_centos-core-i386-6.0.3
として見つかるはずです。

アップデート内容は下記となります。

"Continuous Release ( CR ) Repository"の追加とパッケージのアップデート
CentOS-6.0 Continuous Release ( CR ) Repository

"s3fs-cloudpack"のインストール
"SUZ-LAB謹製 CentOS AMI"に"s3fs-cloudpack"をインストール

"openssh-clients"のインストール
"bash: scp: command not found"の対策

Enjoy!
--------
http://www.suz-lab.com

Varnishでキャッシュ期限を指定

スズキです。

"vcl_fetch"で下記のようにすれば、Varnishでキャッシュ期間を5分にすることができます。
ちなみに"vcl_fetch"はオリジンからコンテンツを取得した後に呼ばれる関数です。

sub vcl_fetch {
    unset beresp.http.expires;
    set beresp.http.cache-control = "max-age=300";
    set beresp.ttl = 300s;
}

上記では、オリジンからのExpiresヘッダを削除して、Cache-Controlヘッダを
300秒(5分)にし、最後に、キャッシュ自体の期限(TTL)も300秒(5分)にしています。

これで5分間はクライアント、もしくはVarnishのキャッシュが利用されるように
なるはずです。

Varnishネタは一旦終了。
--------
http://www.suz-lab.com

Varnishでキャッシュのヒット状況をヘッダに追加

スズキです。

下記を真似すればOKです。
Adding a header indicating hit/miss

具体的には、こんな感じです。

sub vcl_deliver {
    if(obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}

これでヘッダを確認することで、キャッシュ上のコンテンツかどうか判断することができます。

Varnish、負荷に強すぎ...
--------
http://www.suz-lab.com

2011年9月30日金曜日

"SUZ-LAB謹製 CentOS AMI"に"s3fs-cloudpack"をインストール

スズキです。

"SUZ-LAB謹製 CentOS AMI"に下記で紹介した"s3fs-cloudpack"をインストールしてみました。
S3ってなんじゃ?(s3fs-c編)
インストールしたCentOSのバージョンは6.0です。

といっても、ほぼ以前下記で紹介した通りです。
"CentOS 5.6"に"fuse-2.8.5"と"s3fs-1.40"のインストール

まずは必要なパッケージのインストールです。

# yum -y install gcc-c++
# yum -y install pkgconfig
# yum -y install make
# yum -y install curl-devel
# yum -y install libxml2-devel
# yum -y install openssl-devel

次に"fuse"のインストールです。

# curl -OL http://sourceforge.net/projects/fuse/files/fuse-2.X/2.8.6/fuse-2.8.6.tar.gz
# cd fuse-2.8.6
# tar xvzf fuse-2.8.6.tar.gz
# ./configure
# make
# make install

そして"s3fs-cloudpack"をインストールします。

# curl -OL https://github.com/memorycraft/s3fs-cloudpack/tarball/master
# tar xvzf master
# cd memorycraft-s3fs-cloudpack-a5e4da3/
# chmod 755 configure
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
./configure
# make
# make install

最後に"ldconfig"です。

# cat /etc/ld.so.conf
/usr/local/lib
include ld.so.conf.d/*.conf
# ldconfig

これも、次の"SUZ-LAB謹製 CentOS AMI"で組み込もう!
--------
http://www.suz-lab.com

CentOS-6.0 Continuous Release ( CR ) Repository

スズキです。

これは、CentOSの次期バージョン(6.1)のアップデートパッケージを、
現在のバージョン(6.0)で利用するためのリポジトリです。
セキュリティやバグフィックのアップデートパッケージもあるので、
導入が強く推奨されています。

導入は下記のように行います。

# yum info centos-release-cr
...
Available Packages
Name       : centos-release-cr
Arch       : i686
Epoch      : 10
Version    : 6
Release    : 0.el6.centos
Size       : 3.9 k
Repo       : extras
Summary    : CentOS continuous release configs
URL        : http://wiki.centos.org/AdditionalResources/Repositories/CR
License    : GPL
Description: CentOS continuous release configs

# yum install -y centos-release-cr

すると下記のようなリポジトリが追加されます。

# cat /etc/yum.repos.d/CentOS-CR.repo 
# CentOS-CR.repo
#
# The continuous release  ( CR ) repository contains rpms from the
# next point release of CentOS, which isnt itself released as yet.
#
# Look at http://wiki.centos.org/AdditionalResources/Repositories/CR
# for more details about how this repository works and what users 
# should expect to see included / excluded

[cr]
name=CentOS-$releasever - CR
baseurl=http://mirror.centos.org/centos/$releasever/cr/$basearch/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

そして下記のようにアップデートすると、実際にCRのパッケージでアップデートされます。

# yum update

次の"SUZ-LAB謹製 CentOS AMI"で組み込もう!
--------
http://www.suz-lab.com