2013年3月26日火曜日

Elasticacheってなんじゃ?(memcache-topってなんじゃ?)

別にElasticache関係なく、memcacheの話題です。

memcacheをレプリケートしたり、クラスタ的に分散して使用していると、どのノードにどれだけキャッシュされているかわかりづらく、WEB UIで確認するほどの内容でもないので、簡単な状態チェックツールがあると便利です。

そこでmoongiftさんでも紹介されていたmemcache-topを使ってみました。


インストール


下のページのダウンロードリンクをコピーします。




コピーしたURLを、memcacheノードのあるセグメント内のホストでPATHの通った場所にダウンロードします。
ダウンロードされるのはバイナリ実行ファイルなので、実行権限を与えます。
# cd /usr/local/bin
# curl -OL https://memcache-top.googlecode.com/files/memcache-top-v0.6
# chmod 755 memcache-top-v0.6
# ln -s memcache-top-v0.6 memcache-top

実行時に
Can't locate Time/HiRes.pm in @INC (@INC contains: /usr/local/lib/perl5 /usr/local/share/perl5 /usr/lib/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib/perl5 /usr/share/perl5 .)
となる場合は、以下のようにライブラリをインストールします。
# yum install -y perl-Time-HiRes

実行


実行は、おもに以下のオプションを使用します。

  • --instances:memcacheノードのIPまたはホスト名をカンマ区切り、もしくは複数オプション
  • --port:ポート番号(デフォルト11211)
  • --sleep:モニタ間隔(デフォルト3秒)

他にもいくつかありますが、よくつかうのは上記3つです。

それでは以下のように実行します。
$ memcache-top --instances 10.32.11.171,10.32.11.172 --port 11210

memcache-top v0.6 (default port: 11210, color: on, refresh: 3 seconds)

INSTANCE  USAGE HIT % CONN TIME EVICT/s READ/s WRITE/s
10.32.11.171:11210 1.5% 93.8% 5 1.0ms 0.0 210 390
10.32.11.172:11210 1.5% 0.0% 5 0.8ms 0.0 221 11.3K

AVERAGE:  1.5% 46.9% 5 0.9ms 0.0 215 5967

TOTAL:  1.9MB/ 0.1GB  10 1.8ms 0.0 431 11.7K
(ctrl-c to quit.)


Elasticacheでも同じように結果の取得ができます。
./memcache-top-v0.6 --instances memorycraft.5horbl.0001.apne1.cache.amazonaws.com,memorycraft.5horbl.0002.apne1.cache.amazonaws.com,memorycraft.5horbl.0003.apne1.cache.amazonaws.com

memcache-top v0.6 (default port: 11211, color: on, refresh: 3 seconds)

INSTANCE  USAGE HIT % CONN TIME EVICT/s READ/s WRITE/s
memorycraft.5horbl.0001 0.0% 0.0% 5 2.0ms 0.0 2 367
memorycraft.5horbl.0002 0.0% 0.0% 5 1.8ms 0.0 2 367
memorycraft.5horbl.0003 0.0% 0.0% 5 1.8ms 0.0 2 367

AVERAGE:  0.0% 0.0% 5 1.9ms 0.0 2 367

TOTAL:  244B/ 3.5GB  15 5.6ms 0.0 6 1101
(ctrl-c to quit.)


簡単な状態のモニタリングをするにはとても便利です。
以上です。

2013年3月23日土曜日

EMRってなんじゃ?(Bootstrap Actionで実行時メモリ使用量を設定する)

EMRを実行するときにメモリが足りなくてエラーになる場合があります。
EMRではクラスタのJavaのメモリ指定ができるのでやってみます。

EMRはジョブフローを起動するときに、Bootstrapアクションを指定出来ます。
AWSコンソールでジョブフローを起動する場合は、ジョブフロー作成のダイアログ中にBootstrapアクションを設定できます。

「BOOTSTRAP ACTIONS」のフェーズで「Configure your Bootstrap Actions」を選択すると以下のような画面が出てきます。



Action Typeにはいくつかあり、監視ツールの「Install Ganglia」や「Memory Intensive Configuration」などのプリセットとカスタムアクションがあります。
「Amazon S3 Location」欄にS3上のアドレスを記載すると、そこに置かれたシェルスクリプトなどがジョブの起動時に実行されるようです。


Memory Intensive Configuration


 ここでは、メモリ集中型設定というのをやってみます。 Action Typeから「Memory Intensive Configuration」という項目のを選びます。 「Amazon S3 Location」欄にS3アドレスが表示されます。



このアドレスに該当するスクリプトを取得すると以下のような内容になっています。
$ curl http://elasticmapreduce.s3.amazonaws.com/bootstrap-actions/configurations/latest/memory-intensive
#!/usr/bin/ruby

## Copyright 2010-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
## Licensed under the Apache License, Version 2.0 (the "Liense"). You may not use this file except in compliance with the License.
## A copy of the License is located at http://aws.Amazon/apache2.0/
## or in the "license" file accompanying this file.
## This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and limitations under the License.

require 'json'
require 'hpricot'
require 'tempfile'

CONFIG_HEADER = "<?xml version=\"1.0\"?>\n<?xml-stylesheet type=\"text/xsl\" href=\"configuration.xsl\"?>"

conf_fields = [
  { :field => "mapred.child.java.opts={VAR}",                   :roles => [:slave]          },
  { :field => "mapred.tasktracker.map.tasks.maximum={VAR}",     :roles => [:master, :slave] },
  { :field => "mapred.tasktracker.reduce.tasks.maximum={VAR}",  :roles => [:master, :slave] }
]

configs = {
  "m1.small"    => ["-Xmx512m",  "2",  "1"],
  "m1.large"    => ["-Xmx1024m", "3",  "1"],
  "m1.xlarge"   => ["-Xmx1024m", "8",  "3"],
  "c1.medium"   => ["-Xmx512m",  "2",  "1"],
  "c1.xlarge"   => ["-Xmx512m",  "7",  "2"],
  "m2.xlarge"   => ["-Xmx3072m", "3",  "1"],
  "m2.2xlarge"  => ["-Xmx4096m", "6",  "2"],
  "m2.4xlarge"  => ["-Xmx4096m", "14", "4"]
}

heap_fields = [
  { :field => "HADOOP_NAMENODE_HEAPSIZE={VAR}",    :roles => [:master, :slave] },
  { :field => "HADOOP_JOBTRACKER_HEAPSIZE={VAR}",  :roles => [:master, :slave] },
  { :field => "HADOOP_TASKTRACKER_HEAPSIZE={VAR}", :roles => [:master, :slave] },
  { :field => "HADOOP_DATANODE_HEAPSIZE={VAR}",    :roles => [:master, :slave] }
]

heaps = {
  "m1.small"    => ["512",  "512",  "256",  "128"],
  "m1.large"    => ["1024", "3072", "512",  "512"],
  "m1.xlarge"   => ["3072", "9216", "512",  "512"],
  "c1.medium"   => ["512",  "768",  "256",  "128"],
  "c1.xlarge"   => ["1024", "2048", "512",  "512"],
  "m2.xlarge"   => ["2048", "4096", "512",  "512"],
  "m2.2xlarge"  => ["2048", "8192", "1024", "1024"],
  "m2.4xlarge"  => ["8192", "8192", "1024", "1024"]
}

def parse_config_file(config_file_path)
  ret = []
  if File.exist?(config_file_path) then
    doc = open(config_file_path) { |f| Hpricot(f) }
    (doc/"configuration"/"property").each do |property|
      val = {:name => (property/"name").inner_html, :value => (property/"value").inner_html }
      if (property/"final").inner_html != "" then
        val[:final] =  (property/"final").inner_html
      end
      ret << val
    end
  else
    puts "#{config_file_path} does not exist, assuming empty configuration"
  end
  return ret
end

def dump_config_file(file_name, config)
  open(file_name, 'w') do |f|
    f.puts CONFIG_HEADER
    f.puts '<configuration>'
    for entry in config
      f.print "  <property><name>#{entry[:name]}</name><value>#{entry[:value]}</value>"
      if entry[:final] then
        f.print "<final>#{entry[:final]}</final>"
      end
      f.puts '</property>'
    end
    f.puts '</configuration>'
  end
end

def merge_config(default, overwrite)
  for entry in overwrite
    cells = default.select { |x| x[:name] == entry[:name]}
    if cells.size == 0 then
      puts "'#{entry[:name]}': default does not have key, appending value '#{entry[:value]}'"
      default << entry
    elsif cells.size == 1 then
      puts "'#{entry[:name]}': new value '#{entry[:value]}' overwriting '#{cells[0][:value]}'"
      cells[0].replace(entry)
    else
      raise "'#{entry[:name]}': default has #{cells.size} keys"
    end
  end
end

def do_overwrites(conf_list, heap_list)
  file = "/home/hadoop/conf/mapred-site.xml"
  default = parse_config_file(file)
  for arg in conf_list
    puts "Processing default file #{file} with overwrite #{arg}"
    key   = arg.split('=', 2)[0]
    value = arg.split('=', 2)[1]
    overwrite = [{:name => key, :value => value }]
    merge_config(default,overwrite)
  end
  dump_config_file(file + ".new", default)
  if File.exist?(file) then
    File.rename(file, file + ".old")
  end
  File.rename(file + ".new", file)
  puts "Saved #{file} with overwrites. Original saved to #{file}.old"
  file = "/home/hadoop/conf/hadoop-user-env.sh"
  if File.exist?(file) then
    File.delete(file)
  end
  open(file, 'w') do |f|
    f.puts "#!/bin/bash"
    for arg in heap_list
      f.puts arg
    end
  end
end

class JsonInfoFile

  INFO_DIR = "/mnt/var/lib/info/"

  def initialize(file_type)
    @json = JSON.parse(File.read(File.join(INFO_DIR, file_type + ".json")))
  end

  def [](json_path)
    json = @json
    begin
      path = json_path.split('.')
      visited = []
      for item in path
        if !json.kind_of? Hash then
          raise "#{visited.join('.')} not of type object, got '#{json.inspect}' from #{@json.inspect}"
        end
        visited << item
        json = json[item]
      end
      if json == nil then
        raise "#{visited.join('.')} does not exist"
      end
      return json
    rescue
      puts "Unable to process path '#{json_path}', #{$!}"
      exit -1
    end
  end
end

def warn(msg)
  STDERR.puts "#{Time.now.utc} WARN " + msg
end

def substitute_in(row, fields, instance_role)
  if row.size != fields.size then
    raise RuntimeError, "Incompatible row and field list row=#{row}, fields=#{fields}"
  end
  result = []
  for index in 0 ... row.size do
    if fields[index][:roles].include?(instance_role) then
      result << fields[index][:field].sub('{VAR}', row[index])
    end
  end
  return result
end

HPC_INSTANCE_TYPES = [ "cc1.4xlarge", "cg1.4xlarge" ]

jobflow_info = JsonInfoFile.new("job-flow")
instance_info = JsonInfoFile.new("instance")
if instance_info['isMaster'].to_s == 'true'  then
  instance_type = jobflow_info["masterInstanceType"]
  instance_role = :master
else
  instance_group_id = instance_info['instanceGroupId']
  instance_groups = jobflow_info['instanceGroups']
  index = instance_groups.index { |g| g['instanceGroupId'] == instance_group_id }
  instance_group = instance_groups[index]
  instance_type = instance_group['instanceType']
  instance_role = :slave
end

if HPC_INSTANCE_TYPES.include?(instance_type) then
  warn "This bootstrap action is not supported for the HPC instances (cc1.4xlarge and cg1.4xlarge)"
else
  conf_list = substitute_in(configs[instance_type], conf_fields, instance_role)
  heap_list = substitute_in(heaps[instance_type], heap_fields, instance_role)
  do_overwrites(conf_list, heap_list)
end

RUBYスクリプトのようです。
このスクリプトではインスタンスサイズに応じてネームノード、JobTracker、TaskTracker、データノードに対して、メモリ集中型のヒープサイズをそれぞれの環境変数に設定するように
/home/hadoop/conf/hadoop-user-env.sh
を上書きしています。

そして、hadoop-user-env.shは
/home/hadoop/conf/hadoop-env.sh
から以下のように読み込まれ、ジョブ実行時に実行される仕組みのようです。

#!/bin/bash

export HADOOP_DATANODE_HEAPSIZE="96"
export HADOOP_NAMENODE_HEAPSIZE="192"
export HADOOP_JOBTRACKER_HEAPSIZE="576"
export HADOOP_TASKTRACKER_HEAPSIZE="192"
export HADOOP_OPTS="$HADOOP_OPTS -server"
if [ -e /home/hadoop/conf/hadoop-user-env.sh ] ; then
  . /home/hadoop/conf/hadoop-user-env.sh
fi


これがメモリ集中型の場合
  • HADOOP_JOBTRACKER_HEAPSIZE:512
  • HADOOP_NAMENODE_HEAPSIZE:512
  • HADOOP_TASKTRACKER_HEAPSIZE:256
  • HADOOP_DATANODE_HEAPSIZE:128

のようになるようです。
ネームノードにより多くのメモリが割り当てられるようです。
これで実行すると、新しいメモリ割り当ての設定でジョブが実行されます。



Custom Action



プリセット以外にも、「Custom Action」という自由にスクリプトなどをアップできるオプションもあります。
「Amazon S3 Location」欄に自分でアップしたスクリプトファイルのS3アドレスを入力します。
ここでは、例として
s3://memorycraft-emr/script/heap.sh として以下のファイルアップしておきます。
前述のMemory Intensive Configurationではインスタンスごとに決め打ちのヒープサイズが自動で適用されましたが、このように自分で各ヒープサイズを指定するように hadoop-user-env.shを作るようなスクリプトを書くこともできます。



そして、以下のようにS3アドレスを入力します。


あとは通常どおり実行します。



インスタンスサイズは上げたくないけどOutOfMemoryを回避したいという場合には、まずこのようにBootstrapActionで調整してみるといいと思います。

また、Bootstrap設定はSDKなどのAPI経由のジョブフロー起動時にも行うことが可能です。
  .... 略 .....
   'AmiVersion'       => 'latest',
   'LogUri'           => "$LOG_BUCKET_LOCATION",
   'BootstrapActions' => array(
    array(
     'Name'                  => 'Custom Action',
     'ScriptBootstrapAction' => array(
      'Path' => 's3://memorycraft-emr/script/heap.sh',
     ),
    ),
   ),
   'Steps' => array(
    new \CFStepConfig(array(
  .... 略 .....


メモリ設定以外にも処理前に自由に処理を挿入できるので、カスタム処理を行ったり、実行環境を最適化できるので、いろいろ便利そうです。


以上です。


2013年3月20日水曜日

S3ってなんじゃ?(S3のログファイルを別アカウントでダウンロード)

S3でWEBホスティングをする場合、アクセスログを別のバケットに出力することができます。
そのS3のアクセスログに別のアカウントからアクセスしてダウンロードなどをしたい場合があります。
今回はその方法を紹介します。

S3のアクセスログを設定にするには、AWSコンソールでS3のバケットの設定をします。


Webホスティングの設定をしていると、上図のように設定することで、memorycraft-logのlogs/以下にこのバケットのアクセスログが出力されます。



このログファイルの一覧をSDK(PHP)を使って別アカウントから取得してみます。
$s3 = new AmazonS3(array('key'=>'別アカウントのアクセスキー','secret'=>'別アカウントのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);
$objects = $s3->get_object_list($bucket);
echo "objects=".print_r($objects, true);


権限がないため取得できないため、出力もされません
objects=


また、ログファイルを別アカウントでダウンロードしようとすると、、、、
$s3 = new AmazonS3(array('key'=>'別アカウントのアクセスキー','secret'=>'別アカウントのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);
$res = $s3->get_object('memorycraft-log', 'logs/2013-03-17-05-34-13-347E8EF10D93460B');
print_r($res);


権限がないためアクセスできません。
CFResponse Object
(
    [header] => Array
        (
            [x-amz-request-id] => 8702DF59DF4D6E86
            [x-amz-id-2] => G/Vvv2NzYqTDbI3WAUHgtp2GCqyBQdMoMaivWNii2utRXezRJUxD/hvCOQjBTCGw
            [content-type] => application/xml
            [transfer-encoding] => chunked
            [date] => Tue, 19 Mar 2013 17:44:30 GMT
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
                    [content_type] => application/xml
                    [http_code] => 403
                    [header_size] => 254
                    [request_size] => 751
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.644282
                    [namelookup_time] => 0.551464
                    [connect_time] => 0.551971
                    [pretransfer_time] => 0.615103
                    [size_upload] => 0
                    [size_download] => 231
                    [speed_download] => 358
                    [speed_upload] => 0
                    [download_content_length] => -1
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.644058
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.206
                    [redirect_url] => 
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 17:44:30 GMT
/memorycraft-log/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 17:44:30 GMT
                    [Authorization] => AWS XXXXXXXXXXXXXXXXXX:UocmEFK0Cv4YveXulElQxR2J2cw=
                    [Expect] => 
                )

        )

    [body] => <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8702DF59DF4D6E86</RequestId><HostId>G/Vvv2NzYqTDbI3WAUHgtp2GCqyBQdMoMaivWNii2utRXezRJUxD/hvCOQjBTCGw</HostId></Error>
    [status] => 403
)



ここで、現状のアクセス権限をみてみます。
バケットは以下のような設定です。
  • memorycraft(バケットオーナー):フルコントロール
  • LogDelivery(AWSのログ出力ユーザー):更新、権限チェック




また、ログファイル自体は以下のようになっています。
  • memorycraft(バケットオーナー):フルコントロール
  • s3-log-service(ファイルオーナー):フルコントロール




このように、通常だとAWSのログサービスとバケットオーナーにしか権限が与えられていないためアクセスできません。


ここで、バケットにSDKから権限を設定してみます。
$target = 'memorycraft-log';
$owner_canonical_id = 'オーナーの標準ユーザーID';
$other_canonical_id = '別アカウントの標準ユーザーID';
$s3 = new AmazonS3(array('key'=>'オーナーのアクセスキー','secret'=>'オーナーのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);

$res = $s3->set_bucket_acl($target, array(
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_READ_ACP),
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_WRITE),
  array('id' => $other_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
  array('id' => $owner_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
));

ここでいう標準ユーザーIDは、canonical_idとも言い、S3でのリソース共有に使用されるAWSアカウントに紐づく識別子です。標準ユーザーIDはアカウントのセキュリティページで確認できます。



 設定が完了すると、AWSコンソール上では以下のように権限が追加されたことが確認できます。




一覧取得とダウンロードを再度試してみます。

ファイルの一覧はできるようになりました。
objects=Array
(
    [0] => logs/
    [1] => logs/2013-03-17-02-33-50-04A9653054E658C0
    [2] => logs/2013-03-17-02-34-38-B68F986342D3F643
    [3] => logs/2013-03-17-02-37-03-633F736726FEDD7D
    [4] => logs/2013-03-17-03-15-48-4930EA30E66582CB
    [5] => logs/2013-03-17-03-15-49-29960D2BF338C203
    [6] => logs/2013-03-17-03-16-26-ED6062BA31A1B490
    [7] => logs/2013-03-17-05-16-45-99184D52B11E91BA
    [8] => logs/2013-03-17-05-16-58-ADD438AA47F74064
    [9] => logs/2013-03-17-05-29-26-D7BF5ECA4A0EB11C
    [10] => logs/2013-03-17-05-29-56-32C233B0252FF726
    [11] => logs/2013-03-17-05-34-13-347E8EF10D93460B
    [12] => logs/2013-03-17-05-35-02-65E964A6CE7F2809
    [13] => logs/2013-03-17-05-38-17-3C15C22739B25B64
    [14] => logs/2013-03-17-14-29-27-CE3C5D978D0410EC
    [15] => logs/2013-03-18-02-17-28-C2CA7570ADE47218
    [16] => logs/2013-03-19-15-16-34-97BE8A99E16EAB77
    [17] => logs/2013-03-19-15-16-39-80209A13C1DD677A
    [18] => logs/2013-03-19-15-17-03-551FB7D0F64B54AE
    [19] => logs/2013-03-19-15-30-00-3108CB5D1133380D
    [20] => logs/2013-03-19-15-32-53-7866E864159240D4
    [21] => logs/2013-03-19-15-33-15-B5E022E930A4DE0F
    [22] => logs/2013-03-19-15-36-45-F8194516221FF1F4
    [23] => logs/2013-03-19-15-37-26-E8855A5E344B083F
    [24] => logs/2013-03-19-17-15-41-444231C9F7F6FD69
    [25] => logs/2013-03-19-17-16-07-1FE5A409A6EEE3F3
    [26] => logs/2013-03-19-17-16-40-9D477C0727E9CF9F
    [27] => logs/2013-03-19-17-34-22-AB7B385D4C73382E
    [28] => logs/2013-03-19-17-42-05-69E17011ED7FEC05
    [29] => logs/2013-03-19-19-15-26-C806C3AB6A82BABE
    [30] => logs/2013-03-19-19-15-51-C75A28CBEA846E35
    [31] => logs/2013-03-19-19-15-59-01AD1F375E4639C3
    [32] => logs/2013-03-19-19-29-22-573E6302C719D044
    [33] => logs/2013-03-19-19-32-06-6724AB9B33D97F52
    [34] => logs/2013-03-19-19-34-13-60F916D79985BDF0
    [35] => logs/2013-03-19-19-34-37-6A6430488AEB5195
    [36] => logs/2013-03-19-19-38-02-29AF737531B69E29
)


しかしダウンロードはできません。
CFResponse Object
(
    [header] => Array
        (
            [x-amz-request-id] => C6D3DB10776F16FA
            [x-amz-id-2] => owcKsbILBrXmdTdu8XG81Rpe2ozrZmgOniQz5dUlUzH2b5c6TVVg3yEwue8q+QqI
            [content-type] => application/xml
            [transfer-encoding] => chunked
            [date] => Tue, 19 Mar 2013 20:03:25 GMT
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
                    [content_type] => application/xml
                    [http_code] => 403
                    [header_size] => 254
                    [request_size] => 751
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.318179
                    [namelookup_time] => 0.206527
                    [connect_time] => 0.209524
                    [pretransfer_time] => 0.277762
                    [size_upload] => 0
                    [size_download] => 231
                    [speed_download] => 726
                    [speed_upload] => 0
                    [download_content_length] => -1
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.317943
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.76
                    [redirect_url] =>
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 20:03:25 GMT
/memorycraft-log/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 20:03:25 GMT
                    [Authorization] => AWS AKIAIHEOBPDCINSPTZMQ:2unBCNgC8U6OIxpFPOppb5vOF70=
                    [Expect] =>
                )

        )

    [body] => <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>C6D3DB10776F16FA</RequestId><HostId>owcKsbILBrXmdTdu8XG81Rpe2ozrZmgOniQz5dUlUzH2b5c6TVVg3yEwue8q+QqI</HostId></Error>
    [status] => 403
)


これはダウンロード権限がファイルについているためです。
AWSコンソールからロギング設定をすると、S3がログに出力する際のファイル権限が自動で設定されるため、変更できないように見えますが、API経由で設定すると出力時のファイル権限を任意の内容に設定出来ます。

$src = 'myfirst-bucket';
$target = 'memorycraft-log';
$prefix = 'logs/';
$owner_canonical_id = 'オーナーの標準ユーザーID';
$other_canonical_id = '別アカウントの標準ユーザーID';
$s3 = new AmazonS3(array('key'=>'オーナーのアクセスキー','secret'=>'オーナーのシークレットキー'));

$res = $s3->enable_logging($src, $target, $prefix, array(
  'users' => array(
    array('id' => $owner_canonical_id,     'permission' => AmazonS3::GRANT_FULL_CONTROL ),
    array('id' => $other_canonical_id,     'permission' => AmazonS3::GRANT_FULL_CONTROL ),
  )
));


このようにAPI経由で設定したら、設定後に出力されたファイルの権限は以下のようになります。



このファイルを取得をしてみます。

CFResponse Object
(
    [header] => Array
        (
            [x-amz-id-2] => KF+Szr8tu2uvDLpz0DHl8ZNxbUmIqLtwXhJ9TISQvKUQR9x15RUNZzmpRFmfB910
            [x-amz-request-id] => EFE2ACA5DADC8273
            [date] => Tue, 19 Mar 2013 21:02:05 GMT
            [last-modified] => Tue, 19 Mar 2013 19:38:03 GMT
            [etag] => "ada5251bcbd676782e98153cd0cca821"
            [accept-ranges] => bytes
            [content-type] => text/plain
            [content-length] => 304
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-19-19-38-02-29AF737531B69E29
                    [content_type] => text/plain
                    [http_code] => 200
                    [header_size] => 345
                    [request_size] => 751
                    [filetime] => 1363721883
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.25437
                    [namelookup_time] => 0.139468
                    [connect_time] => 0.1404
                    [pretransfer_time] => 0.202015
                    [size_upload] => 0
                    [size_download] => 304
                    [speed_download] => 1195
                    [speed_upload] => 0
                    [download_content_length] => 304
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.254312
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.206
                    [redirect_url] => 
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-19-19-38-02-29AF737531B69E29
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 21:02:04 GMT
/memorycraft-log/logs/2013-03-19-19-38-02-29AF737531B69E29
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 21:02:04 GMT
                    [Authorization] => AWS XXXXXXXXXXXXXXXXXX:qqBlRlhCi9XzHcFE9Guc8J/W0yc=
                    [Expect] => 
                )

        )

    [body] => b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543 myfirst-bucket [19/Mar/2013:18:22:39 +0000] 10.186.158.41 b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543 22988F1BD9A4CBA1 REST.GET.LOCATION - "GET /myfirst-bucket?location HTTP/1.1" 200 - 142 - 21 - "-" "S3Console/0.4" -

    [status] => 200
)

無事取得できました!
以上です。

2013年3月11日月曜日

EMRってなんじゃ?(EMRの詳細なログをみる)

EMRをつかっていて、ステップの途中でエラーが出たときに、デバッグ用のログ出力先を設定すると実行ログがAWSコンソール上で見ることができます。

例として、S3のログバケットをまとめるHiveScriptを動かした時のエラーを見てみます。


エラーが発生しています。
LogURIを指定していると、このようにコンソールからLogFileとして確認することができます。
エラーが起こった3番目のステップのそれぞれのリンクをみてみると以下のようになっています。


controller
2013-03-11T11:31:41.005Z INFO Fetching jar file.
2013-03-11T11:31:48.146Z INFO Working dir /mnt/var/lib/hadoop/steps/3
2013-03-11T11:31:48.146Z INFO Executing /usr/lib/jvm/java-6-sun/bin/java -cp /home/hadoop/conf:/usr/lib/jvm/java-6-sun/lib/tools.jar:/home/hadoop:/home/hadoop/hadoop-core.jar:/home/hadoop/hadoop-tools.jar:/home/hadoop/hadoop-core-0.20.205.jar:/home/hadoop/hadoop-tools-0.20.205.jar:/home/hadoop/lib/*:/home/hadoop/lib/jetty-ext/* -Xmx1000m -Dhadoop.log.dir=/mnt/var/log/hadoop/steps/3 -Dhadoop.log.file=syslog -Dhadoop.home.dir=/home/hadoop -Dhadoop.id.str=hadoop -Dhadoop.root.logger=INFO,DRFA -Djava.io.tmpdir=/mnt/var/lib/hadoop/steps/3/tmp -Djava.library.path=/home/hadoop/native/Linux-i386-32 org.apache.hadoop.util.RunJar /mnt/var/lib/hadoop/steps/3/script-runner.jar s3://ap-northeast-1.elasticmapreduce/libs/hive/hive-script --run-hive-script --base-path s3://ap-northeast-1.elasticmapreduce/libs/hive/ --hive-versions latest --args -f s3n://memorycraft-archive/script/s3.hql -d INPUT_BUCKET_LOCATION=s3://memorycraft-log/logs/ -d OUTPUT_BUCKET_LOCATION=s3://memorycraft-archive/rslt/ -d YYYY=2013 -d MM=03 -d DD=04
2013-03-11T11:34:20.383Z INFO Execution ended with ret val 255
2013-03-11T11:34:20.384Z WARN Step failed with bad retval
2013-03-11T11:34:26.454Z INFO Step created jobs: job_201303111128_0001


stderr
Logging initialized using configuration in file:/home/hadoop/.versions/hive-0.8.1/conf/hive-log4j.properties
Hive history file=/mnt/var/lib/hive_081/tmp/history/hive_job_log_hadoop_201303111132_1431275973.txt
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/hadoop/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/hadoop/.versions/hive-0.8.1/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
OK
Time taken: 13.475 seconds
OK
Time taken: 0.765 seconds
Total MapReduce jobs = 1
Launching Job 1 out of 1
Number of reduce tasks determined at compile time: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201303111128_0001, Tracking URL = http://ip-10-121-17-157.ap-northeast-1.compute.internal:9100/jobdetails.jsp?jobid=job_201303111128_0001
Kill Command = /home/hadoop/bin/hadoop job  -Dmapred.job.tracker=10.121.17.157:9001 -kill job_201303111128_0001
Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1
2013-03-11 11:33:11,849 Stage-1 map = 0%,  reduce = 0%
2013-03-11 11:34:12,380 Stage-1 map = 0%,  reduce = 0%
2013-03-11 11:34:19,431 Stage-1 map = 100%,  reduce = 100%
Ended Job = job_201303111128_0001 with errors
Error during job, obtaining debugging information...
Examining task ID: task_201303111128_0001_m_000002 (and more) from job job_201303111128_0001
Exception in thread "Thread-47" java.lang.RuntimeException: Error while reading from task log url
 at org.apache.hadoop.hive.ql.exec.errors.TaskLogProcessor.getErrors(TaskLogProcessor.java:130)
 at org.apache.hadoop.hive.ql.exec.JobDebugger.showJobFailDebugInfo(JobDebugger.java:211)
 at org.apache.hadoop.hive.ql.exec.JobDebugger.run(JobDebugger.java:81)
 at java.lang.Thread.run(Thread.java:662)
Caused by: java.io.IOException: Server returned HTTP response code: 400 for URL: http://10.122.24.232:9103/tasklog?taskid=attempt_201303111128_0001_m_000000_1&start=-8193
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1436)
 at java.net.URL.openStream(URL.java:1010)
 at org.apache.hadoop.hive.ql.exec.errors.TaskLogProcessor.getErrors(TaskLogProcessor.java:120)
 ... 3 more
Counters:
FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.MapRedTask
MapReduce Jobs Launched: 
Job 0: Map: 1  Reduce: 1   HDFS Read: 0 HDFS Write: 0 FAIL
Total MapReduce CPU Time Spent: 0 msec
Command exiting with ret '255'


stdout
Downloading 's3://ap-northeast-1.elasticmapreduce/libs/hive/hive-script' to '/mnt/var/lib/hadoop/steps/3/.'
2013-03-11 11:31:52 GMT - INFO Running: /home/hadoop/.versions/hive-0.8.1/bin/hive '-f' 's3n://memorycraft-archive/script/s3.hql' '-d' 'INPUT_BUCKET_LOCATION=s3://memorycraft-log/logs/' '-d' 'OUTPUT_BUCKET_LOCATION=s3://memorycraft-archive/rslt/' '-d' 'YYYY=2013' '-d' 'MM=03' '-d' 'DD=04'
2013-03-11 11:34:20 GMT - ERROR Error executing cmd: /home/hadoop/.versions/hive-0.8.1/bin/hive '-f' 's3n://memorycraft-archive/script/s3.hql' '-d' 'INPUT_BUCKET_LOCATION=s3://memorycraft-log/logs/' '-d' 'OUTPUT_BUCKET_LOCATION=s3://memorycraft-archive/rslt/' '-d' 'YYYY=2013' '-d' 'MM=03' '-d' 'DD=04'


syslog
2013-03-11 11:31:52,225 INFO org.apache.hadoop.fs.s3native.NativeS3FileSystem (main): Opening '/libs/hive/hive-script' for reading


一番詳しく出ているのが、stderrのようですが、これだけではよくわかりません。
実際は、AWSコンソール上で確認できるEMRログはこれで全てではなく、詳細なタスクのログがEMRログバケットに出力されています。


追記:
TaskとTask Attemptsに関しては、右側のView Jobsから掘っていくことで、EMRのDebugダイアログでも確認でしましたが、それ以外のログはやはりS3バケットでしかみれなそうです。


EMRログバケットを見てみます。


ジョブフローIDごとにフォルダがあり、その中のstepsというフォルダが、AWSコンソールで表示されるログが入っています。


steps/3 以下に先ほどのログファイルが存在しています。
これ以上くわしい実行タスクのログを見るには、task-attemptsというフォルダに詳細なログが出力されています。



このtask-attempts以下の各サブフォルダにあるsyslogというファイルに詳細なログが出力されています。


task-attempts/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/syslog
2013-03-11 11:33:13,112 INFO org.apache.hadoop.util.NativeCodeLoader (main): Loaded the native-hadoop library
2013-03-11 11:33:13,268 INFO org.apache.hadoop.mapred.TaskRunner (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/distcache/-5343292120509370400_-2048022790_1496912286/10.121.17.157/mnt/var/lib/hive_081/tmp/scratch/hive_2013-03-11_11-32-20_837_75945303502865249/-mr-10001/3d096d97-37e9-47c7-a184-8152010d592a <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/HIVE_PLAN3d096d97-37e9-47c7-a184-8152010d592a
2013-03-11 11:33:13,354 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/job.jar <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/job.jar
2013-03-11 11:33:13,362 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/.job.jar.crc <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/.job.jar.crc
2013-03-11 11:33:13,370 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/META-INF <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/META-INF
2013-03-11 11:33:13,378 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/org <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/org
2013-03-11 11:33:13,386 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/hive-exec-log4j.properties <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/hive-exec-log4j.properties
2013-03-11 11:33:13,430 INFO org.apache.hadoop.filecache.TrackerDistributedCacheManager (main): Creating symlink: /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/jars/javaewah <- /mnt/var/lib/hadoop/mapred/taskTracker/hadoop/jobcache/job_201303111128_0001/attempt_201303111128_0001_m_000000_0/work/javaewah
2013-03-11 11:33:13,838 WARN org.apache.hadoop.metrics2.impl.MetricsSystemImpl (main): Source name ugi already exists!
2013-03-11 11:33:14,037 INFO org.apache.hadoop.mapred.MapTask (main): Host name: ip-10-122-12-88.ap-northeast-1.compute.internal
2013-03-11 11:33:14,088 INFO org.apache.hadoop.util.ProcessTree (main): setsid exited with exit code 0
2013-03-11 11:33:14,106 INFO org.apache.hadoop.mapred.Task (main):  Using ResourceCalculatorPlugin : org.apache.hadoop.util.LinuxResourceCalculatorPlugin@1dfd868
2013-03-11 11:33:14,342 INFO com.hadoop.compression.lzo.GPLNativeCodeLoader (main): Loaded native gpl library
2013-03-11 11:33:14,358 WARN com.hadoop.compression.lzo.LzoCodec (main): Could not find build properties file with revision hash
2013-03-11 11:33:14,358 INFO com.hadoop.compression.lzo.LzoCodec (main): Successfully loaded & initialized native-lzo library [hadoop-lzo rev UNKNOWN]
2013-03-11 11:33:14,367 WARN org.apache.hadoop.io.compress.snappy.LoadSnappy (main): Snappy native library is available
2013-03-11 11:33:14,367 INFO org.apache.hadoop.io.compress.snappy.LoadSnappy (main): Snappy native library loaded
2013-03-11 11:33:14,452 WARN org.apache.hadoop.hive.conf.HiveConf (main): hive-site.xml not found on CLASSPATH
2013-03-11 11:33:16,876 INFO org.apache.hadoop.fs.s3native.NativeS3FileSystem (main): Opening 's3://memorycraft-log/logs/2013-03-04-09-16-51-45565F6169B3E2F2' for reading
2013-03-11 11:33:17,238 INFO org.apache.hadoop.io.retry.RetryInvocationHandler (main): Exception while invoking retrievePair of class org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore. Not retrying.Status Code: 403, AWS Service: Amazon S3, AWS Request ID: 2981A88871482E0B, AWS Error Code: InvalidObjectState, AWS Error Message: The operation is not valid for the object's storage class, S3 Extended Request ID: 7yHCAsfLHZKbf+5ojWMS9q2B41bBwizoqW4zldSdfEpLoLrKvI9X1WI0brHGsC49
 at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:548)
 at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:288)
 at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:170)
 at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:2619)
 at com.amazonaws.services.s3.AmazonS3Client.getObject(AmazonS3Client.java:809)
 at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrievePair(Jets3tNativeFileSystemStore.java:266)
 at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrievePair(Jets3tNativeFileSystemStore.java:252)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:82)
 at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:59)
 at org.apache.hadoop.fs.s3native.$Proxy7.retrievePair(Unknown Source)
 at org.apache.hadoop.fs.s3native.NativeS3FileSystem.open(NativeS3FileSystem.java:1002)
 at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:420)
 at org.apache.hadoop.mapred.LineRecordReader.<init>(LineRecordReader.java:78)
 at org.apache.hadoop.mapred.TextInputFormat.getRecordReader(TextInputFormat.java:51)
 at org.apache.hadoop.hive.ql.io.CombineHiveRecordReader.<init>(CombineHiveRecordReader.java:65)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileRecordReader.initNextRecordReader(HadoopShimsSecure.java:331)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileRecordReader.<init>(HadoopShimsSecure.java:292)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileInputFormatShim.getRecordReader(HadoopShimsSecure.java:406)
 at org.apache.hadoop.hive.ql.io.CombineHiveInputFormat.getRecordReader(CombineHiveInputFormat.java:549)
 at org.apache.hadoop.mapred.MapTask$TrackedRecordReader.<init>(MapTask.java:199)
 at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:423)
 at org.apache.hadoop.mapred.MapTask.run(MapTask.java:377)
 at org.apache.hadoop.mapred.Child$4.run(Child.java:255)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.Subject.doAs(Subject.java:396)
 at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1059)
 at org.apache.hadoop.mapred.Child.main(Child.java:249)

2013-03-11 11:33:17,315 INFO org.apache.hadoop.mapred.TaskLogsTruncater (main): Initializing logs' truncater with mapRetainSize=-1 and reduceRetainSize=-1
2013-03-11 11:33:17,452 INFO org.apache.hadoop.io.nativeio.NativeIO (main): Initialized cache for UID to User mapping with a cache timeout of 14400 seconds.
2013-03-11 11:33:17,453 INFO org.apache.hadoop.io.nativeio.NativeIO (main): Got UserName hadoop for UID 106 from the native implementation
2013-03-11 11:33:17,456 WARN org.apache.hadoop.mapred.Child (main): Error running child
java.io.IOException: java.lang.reflect.InvocationTargetException
 at org.apache.hadoop.hive.io.HiveIOExceptionHandlerChain.handleRecordReaderCreationException(HiveIOExceptionHandlerChain.java:97)
 at org.apache.hadoop.hive.io.HiveIOExceptionHandlerUtil.handleRecordReaderCreationException(HiveIOExceptionHandlerUtil.java:57)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileRecordReader.initNextRecordReader(HadoopShimsSecure.java:345)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileRecordReader.<init>(HadoopShimsSecure.java:292)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileInputFormatShim.getRecordReader(HadoopShimsSecure.java:406)
 at org.apache.hadoop.hive.ql.io.CombineHiveInputFormat.getRecordReader(CombineHiveInputFormat.java:549)
 at org.apache.hadoop.mapred.MapTask$TrackedRecordReader.<init>(MapTask.java:199)
 at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:423)
 at org.apache.hadoop.mapred.MapTask.run(MapTask.java:377)
 at org.apache.hadoop.mapred.Child$4.run(Child.java:255)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.Subject.doAs(Subject.java:396)
 at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1059)
 at org.apache.hadoop.mapred.Child.main(Child.java:249)
Caused by: java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
 at org.apache.hadoop.hive.shims.HadoopShimsSecure$CombineFileRecordReader.initNextRecordReader(HadoopShimsSecure.java:331)
 ... 11 more
Caused by: Status Code: 403, AWS Service: Amazon S3, AWS Request ID: 2981A88871482E0B, AWS Error Code: InvalidObjectState, AWS Error Message: The operation is not valid for the object's storage class, S3 Extended Request ID: 7yHCAsfLHZKbf+5ojWMS9q2B41bBwizoqW4zldSdfEpLoLrKvI9X1WI0brHGsC49
 at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:548)
 at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:288)
 at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:170)
 at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:2619)
 at com.amazonaws.services.s3.AmazonS3Client.getObject(AmazonS3Client.java:809)
 at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrievePair(Jets3tNativeFileSystemStore.java:266)
 at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.retrievePair(Jets3tNativeFileSystemStore.java:252)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:82)
 at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:59)
 at org.apache.hadoop.fs.s3native.$Proxy7.retrievePair(Unknown Source)
 at org.apache.hadoop.fs.s3native.NativeS3FileSystem.open(NativeS3FileSystem.java:1002)
 at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:420)
 at org.apache.hadoop.mapred.LineRecordReader.<init>(LineRecordReader.java:78)
 at org.apache.hadoop.mapred.TextInputFormat.getRecordReader(TextInputFormat.java:51)
 at org.apache.hadoop.hive.ql.io.CombineHiveRecordReader.<init>(CombineHiveRecordReader.java:65)
 ... 16 more
2013-03-11 11:33:17,500 INFO org.apache.hadoop.mapred.Task (main): Runnning cleanup for the task


内容をみると、
Error Message: The operation is not valid for the object's storage class, S3 Extended Request ID
となっており、ストレージクラスが良くないと言われています。

EMRの入力ログバケットを見ると、Glaceirになっているクラスがありました。
EMRはGlaceirファイルは処理できないので、エラーになっていたようです。

EMRでワケの分からないエラーが出たらEMRログのS3バケットを見るようにしよう。
以上です。



2013年3月9日土曜日

Cassandraってなんじゃ?(CQL)

CassandraにはCQLというSQLライクに問い合わせるインターフェースが用意されています。
いままでのスクリプト風の問い合わせよりも多くの人に馴染みやすいと思うので、ちょっと触ってみます。



CQLコンソール


CQLのインターフェースはいままでのcassandra-cliとは別に、binディレクトリにあるcqlshコマンドを使用します。
まずhelpを見てみます。

# /usr/local/cassandra/bin/cqlsh --help
Usage: cqlsh [options] [host [port]]

CQL Shell for Apache Cassandra

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -C, --color           Always use color output
  --no-color            Never use color output
  -u USERNAME, --username=USERNAME
                        Authenticate as user.
  -p PASSWORD, --password=PASSWORD
                        Authenticate using password.
  -k KEYSPACE, --keyspace=KEYSPACE
                        Authenticate to the given keyspace.
  -f FILE, --file=FILE  Execute commands from FILE, then exit
  -t TRANSPORT_FACTORY, --transport-factory=TRANSPORT_FACTORY
                        Use the provided Thrift transport factory function.
  --debug               Show additional debugging information
  --cqlversion=CQLVERSION
                        Specify a particular CQL version (default: 3).
                        Examples: "2", "3.0.0-beta1"
  -2, --cql2            Shortcut notation for --cqlversion=2
  -3, --cql3            Shortcut notation for --cqlversion=3

Connects to localhost:9160 by default. These defaults can be changed by
setting $CQLSH_HOST and/or $CQLSH_PORT. When a host (and optional port number)
are given on the command line, they take precedence over any defaults.


-2や-3とオプションを渡すと指定されたバージョンのCQLを使用します。
ここではバージョン2を指定してみます。
# /usr/local/cassandra/bin/cqlsh -2

Connected to Memorycraft Cluster at localhost:9160.
[cqlsh 2.3.0 | Cassandra 0.0.0 | CQL spec 2.0.0 | Thrift protocol 19.35.0]
Use HELP for help.
cqlsh>


このようにSQLクライアント風のコンソールが開始されます。
コンソール上でもう一度helpを打ってみます。
cqlsh:Hogebook> help

Documented shell commands:
===========================
ASSUME   CONSISTENCY  DESC      EXIT  SHOW    TRACING
CAPTURE  COPY         DESCRIBE  HELP  SOURCE

CQL help topics:
================
ALTER                        CREATE_KEYSPACE       SELECT_EXPR    
ALTER_ADD                    CREATE_TABLE          SELECT_LIMIT   
ALTER_ALTER                  CREATE_TABLE_OPTIONS  SELECT_TABLE   
ALTER_DROP                   CREATE_TABLE_TYPES    SELECT_WHERE   
ALTER_WITH                   DELETE                TEXT_OUTPUT    
APPLY                        DELETE_COLUMNS        TIMESTAMP_INPUT
ASCII_OUTPUT                 DELETE_USING          TIMESTAMP_OUTPUT
BEGIN                        DELETE_WHERE          TRUNCATE       
BLOB_INPUT                   DROP                  TYPES          
BOOLEAN_INPUT                DROP_COLUMNFAMILY     UPDATE         
CONSISTENCYLEVEL             DROP_INDEX            UPDATE_COUNTERS
CREATE                       DROP_KEYSPACE         UPDATE_SET     
CREATE_COLUMNFAMILY          DROP_TABLE            UPDATE_USING   
CREATE_COLUMNFAMILY_OPTIONS  INSERT                UPDATE_WHERE   
CREATE_COLUMNFAMILY_TYPES    SELECT                USE            
CREATE_INDEX                 SELECT_COLUMNFAMILY   UUID_INPUT     


さらに各コマンドでhelpを打つと詳細が分かります。
cqlsh:Hogebook> help SELECT

        SELECT [FIRST n] [REVERSED] <selectExpr>
          FROM [<keyspace>.]<table>
            [USING CONSISTENCY <consistencylevel>]
            [WHERE <clause>]
            [ORDER BY <colname> [DESC]]
            [LIMIT m];

        SELECT is used to read one or more records from a CQL table. It returns
        a set of rows matching the selection criteria specified.


        Note that FIRST and REVERSED are only supported in CQL 2, and ORDER BY
        is only supported in CQL 3 and higher.

        For more information, see one of the following:

          HELP SELECT_EXPR
          HELP SELECT_TABLE
          HELP SELECT_WHERE
          HELP SELECT_LIMIT
          HELP CONSISTENCYLEVEL

かなりSQLっぽく使えるようです。
いくつかコマンドを打ってみたいと思います。



DESC TABLE
cqlsh:Hogebook> desc table User;

CREATE TABLE User (
  KEY text PRIMARY KEY,
  email text,
  gender text
) WITH
  comment='' AND
  comparator=text AND
  read_repair_chance=0.100000 AND
  gc_grace_seconds=864000 AND
  default_validation=text AND
  min_compaction_threshold=4 AND
  max_compaction_threshold=32 AND
  replicate_on_write='true' AND
  compaction_strategy_class='SizeTieredCompactionStrategy' AND
  compression_parameters:sstable_compression='SnappyCompressor';

CREATE INDEX User_gender_idx ON User (gender);



SELECT
cqlsh:Hogebook> select * from User;

 KEY             | email                      | gender
-----------------+----------------------------+--------
     memorycraft |      memorycraft@gmail.com |   male
 memorycraftgirl | memorycraft+girl@gmail.com | female



INSERT
cqlsh:Hogebook> insert into User(KEY, email, gender) VALUES('hellocql', 'memorycraft+cql@gmail.com', 'male');
cqlsh:Hogebook>
cqlsh:Hogebook> select * from User;

 KEY             | email                      | gender
-----------------+----------------------------+--------
     memorycraft |      memorycraft@gmail.com |   male
        hellocql |  memorycraft+cql@gmail.com |   male
 memorycraftgirl | memorycraft+girl@gmail.com | female

これなら違和感なくデータの操作や参照ができそうです。



APIアクセス



これらのCQLコマンドはコンソール上でも使用しますが、アプリケーションコードからも発行できます。
以前の記事で使用したphpcasaからも使うことができます。

例えば以下はテーブルの全データを取得して出力するスクリプトになります。


このスクリプトを実行してみます。

$ php ./cql.php 
cassandra\CqlResult Object
(
    [type] => 1
    [rows] => Array
        (
            [0] => cassandra\CqlRow Object
                (
                    [key] => memorycraft
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => memorycraft
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft@gmail.com
                                    [timestamp] => 1589299048
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => male
                                    [timestamp] => 1596347048
                                    [ttl] => 
                                )

                        )

                )

            [1] => cassandra\CqlRow Object
                (
                    [key] => hellocql
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => hellocql
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft+cql@gmail.com
                                    [timestamp] => 1562593385
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => male
                                    [timestamp] => 1562593384
                                    [ttl] => 
                                )

                        )

                )

            [2] => cassandra\CqlRow Object
                (
                    [key] => memorycraftgirl
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => memorycraftgirl
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft+girl@gmail.com
                                    [timestamp] => 1611666048
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => female
                                    [timestamp] => 1602906048
                                    [ttl] => 
                                )

                        )

                )

        )

    [num] => 
    [schema] => cassandra\CqlMetadata Object
        (
            [name_types] => Array
                (
                    [KEY] => AsciiType
                )

            [value_types] => Array
                (
                    [KEY] => UTF8Type
                    [email] => UTF8Type
                    [gender] => UTF8Type
                )

            [default_name_type] => UTF8Type
            [default_value_type] => UTF8Type
        )

)


出力をみると、オブジェクト形式で結果が帰ってくるようです。 これなら普段のSQLと近い感覚でコードがかけるので、移行もしやすいと思います。

以上です。


Cassandraってなんじゃ?(EC2で起動時自動ノード追加とノードの削除)

Cassandraを使っているときに、容量が足らない、負荷が高いなどの理由でノードを増やしたい場合があります。また、負荷が長期的に落ち着き、予算を抑えるためにノードを減らすこともあります。

今回はノードの追加と削除をやってみます。



トークンの算出



ノードを追加するケースはリングの負荷状態によって、大きく分けて
  1. 保存するデータのハッシュやアクセスに偏りが大きく、特定ノードの容量または処理の負荷が高いので負荷集中を分散したい。
  2. リング全体の容量、または処理の負荷が高いので、全体的に数を増やして均一に分散したい。

の2通りかと思います。

1の場合、負荷の高いノードの近くにノードを追加し、ノードの均一化はせずに特定のトークンの範囲にノードを集中させたほうがよさそうです。
2の場合、ノードを追加したら、全ノードが均等にリング上に再配置したほうが全体としての負荷は抑えられそうです。

今回は1の場合を例にとってみます。


まず、トークンを計算するためのツールをつくっておくと便利です。
引数にノードの数を与えるとそれがリング上に均等に配置されるようなトークンのリストを返すツールです。

計算式はPartitionerによってことなり、
#RandomPartitionerの場合
 
def tokens(nodes):
  for x in xrange(nodes):
    print 2 ** 127 / nodes * x
 
#Murmur3Partitionerの場合
 
def tokens(num_nodes):
  for i in range(num_nodes):
    print ((2**64 / num_nodes) * i) - 2**63

のようになります。

今回はMurmur3Partitionerを使用しているので、
以下のようなpythonスクリプトを作ります。


引数にノード数を入れて実行すると、ノードリングに均等に配置された場合のハッシュトークンのリストが出力されます。
$ ./tokens.py 4
-9223372036854775808
-4611686018427387904
0
4611686018427387904
 
$ ./tokens.py 8
-9223372036854775808
-6917529027641081856
-4611686018427387904
-2305843009213693952
0
2305843009213693952
4611686018427387904
6917529027641081856


既存のノードが4つだったとして、トークンが以下のように配置されているとします。
  • A:0
  • B:4611686018427387904
  • C:-9223372036854775808
  • D:-4611686018427387904




この場合Aの両脇にノードを2つ追加しておくとよさそうです。
また、追加ノードのトークンは、
  • B、DもCに比べて負荷が高めであればA-B, A-Cのそれぞれ中間
  • BDの負荷はそれほどでもなければもっとAより

に配置する形になります。
前者であれば、./tokens 8、後者であれば./tokens 12 などでAの位置の両脇のトークンを使用します。
たとえば後者のパターンで追加してみます。

$ ./tokens 12
-9223372036854775808
-7686143364045646507
-6148914691236517206
-4611686018427387905
-3074457345618258604
-1537228672809129303
-2
1537228672809129299
3074457345618258600
4611686018427387901
6148914691236517202
7686143364045646503

-2の部分がAのノード位置です。
その両脇の
  • -1537228672809129303
  • 1537228672809129299

が今回の追加ノードとなります。




以前の記事でちょっと触れましたが、自動でリングに追加されるようなAMIを作ります。



AMIの作成



AMIの作成をします。cassandraをインストールしただけでまだ一度も起動していないインスタンスをベースに作業を行うとスムーズです。

以下の設定ファイルにはIPなどノード固有の情報(赤字の部分)が含まれます。

/usr/local/cassandra/conf/cassandra.yaml
cluster_name: 'クラスタ名'
initial_token: トークン
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
- seeds: "シードIP"

listen_address: 自分のIP
endpoint_snitch: Ec2Snitch
auto_bootstrap: シードならfalse,非シードならtrue

/etc/hosts
127.0.0.1       localhost.localdomain localhost  ip-10-0-1-10

この固有の項目を書き換えるようなシェルスクリプトを書きます。

このスクリプトは後でUserDataから呼び出され、第1引数にトークン、第2引数にauto_bootstrapのブーリアン文字列、第3引数にシードIPが渡されます。

/etc/cloud/cassandra.sh


ここまで設定したらno rebootでAMIを作成します。




ノードの自動追加



このAMIをつかって新規ノードを追加します。
クラスタも何もない状態で、一番最初のノードを立ち上げる場合は以下のようにUserDataを指定します。

#!/bin/sh
/etc/cloud/cassandra.sh "0" false 10.0.1.10


そして、プライベートIPアドレスを10.0.1.10に固定して起動します。



また、2個め以降(ここでは例として6個目)の追加ノードは以下のようにUserDataを指定します。
ここで、第1引数のトークンは冒頭で記した計算ツールで算出しておきます。

#!/bin/sh
/etc/cloud/cassandra.sh "1537228672809129299" true 10.0.1.10,10.0.1.146,10.0.1.176,10.0.1.232,10.0.1.134


特にプライベートIPを指定する必要はありません。



これにより、立ち上がったインスタンスでは、hostsファイルとcassandra.yamlが自動的に設定され、cassandraが起動し、リングに自動的に追加されます。

起動前は、

# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                     
                                                                               -4611686018427387905                       
10.0.1.146      1a          Up     Normal  75.43 KB        66.67%              -1537228672809129303                       
10.0.1.10       1a          Up     Normal  61.65 KB        50.00%              0                                         
10.0.1.176      1a          Up     Normal  58.33 KB        58.33%              -9223372036854775808                       
10.0.1.232      1a          Up     Normal  58.45 KB        50.00%              4611686018427387901                       
10.0.1.134      1a          Up     Normal  58.48 KB        75.00%              -4611686018427387905

だったのが、起動後


# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                       
                                                                               -4611686018427387905                        
10.0.1.213      1a          Up     Normal  49.55 KB        33.33%              1537228672809129299                         
10.0.1.146      1a          Up     Normal  63.7 KB         66.67%              -1537228672809129303                        
10.0.1.10       1a          Up     Normal  66.59 KB        50.00%              0                                           
10.0.1.176      1a          Up     Normal  63.28 KB        50.00%              -9223372036854775808                        
10.0.1.232      1a          Up     Normal  63.4 KB         33.33%              4611686018427387901                         
10.0.1.134      1a          Up     Normal  63.43 KB        66.67%              -4611686018427387905 

↑のようにノード追加されていることが分かります。



ノードの取り外し



逆に問題があるノードを取り外すときは、そのノードに入って、

# /usr/local/cassandra/bin/nodetool decommission

とすれば外れます。
再び接続させるには、cassandraを追加します。

#/etc/init.d/cassandra stop
#/etc/init.d/cassandra start



接続できない場合


たまに、リングに接続できなかったりした場合は、まず7000(ノード間), 9160(Thrift), 7199(JMX)などのポートを準備できているかセキュリティグループやnsetstatで確認します。netstatでlistenできていない場合は、cassandraを再起動させたりログを調べたりして原因を探していく流れになります。




# netstat -tupln | grep -i listen | grep java
tcp        0      0 0.0.0.0:9160                0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:38250               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:53170               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 10.0.1.213:7000             0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:7199                0.0.0.0:*                   LISTEN      3967/java



これで、ノードを簡単に増やせるようになりました。
以上です。

2013年3月4日月曜日

RDSってなんじゃ?(Free SpaceのAlarmが出たら)

今回はさらっとオプティマイズの話です。
RDSのMySQLでCloudWatchのFree Spaceに関するAlarmがでることがあります。

RDSに限ったことではないですが、MySQLを運用しているとディスクスペースが減っていき、データをDeleteしてもディスク空き容量が増えないことが多くあります。
データを削除しても、削除した箇所はフリースペースとして確保され、開放されないことが多く、長い間放置しているとディスク容量を圧迫してしまいます。

このようなときには、MySQLのOptimizeを行います。

 サンプルとしてtpcc-mysqlのデータを投入しておきます。

RDSでデータの多いテーブルの偶数行を削除します。




削除してしばらくしてもデータ空き容量は増えないため、Optimizeを行なってみます。



"Table does not support optimize"と表示されます。
ドキュメントを見ると、InnoDBではOPTIMIZE TABLEはALTER TABLEにマップされると書いてあります。
そのため、ALTER TABLEを行なっても最適化されるようです。

再びいろいろデータを削除してみます。


削除してしばらくしてもやはり、データ空き容量は増えません。
今度はALTER TABLEをしてみます。




少し待って、RDSのCloudWatchを見てみます。



すると、OPTIMIZE TABLEやALTER TABLEをした時点で使用領域が開放され、ディスク容量が増えているのがわかります。

ALTER TABLEなのでメンテナンス時間に行う必要がありますが、
追加削除の頻度が高いDBで、ディスク容量を増やすことが難しい場合には丁度よい対策になりますし、アクセスも高速になるため定期的におこなった方がよいかと思います。

オプティマイズに関してはRDSに限ったことではないのですが、CloudWatchを使うとオプティマイズ時期の目安にもなりとても便利です。

以上です。

2013年3月2日土曜日

StorageGatewayってなんじゃ?(GlusterFSで復旧編)

前回GlusterFSを使ってStorageGatewayインスタンスを冗長化しました。
今回は、インスタンスが落ちた場合の復旧方法を見てみます。

Gatewayインスタンスが落ちた場合





gatewayインスタンスを落とします。



ブリックである/mnt/sgwをみてみると


# ls -l /mnt/sgw
ls: reading directory /mnt/sgw: 入力/出力エラーです
合計 16
-rw-r--r-- 2 root root 27  3月  1 13:24 2013 1.txt
-rw-r--r-- 2 root root 27  3月  1 13:24 2013 4.txt


IOエラーがでます。


Glusterボリュームのマウントポイントをみてみると


# ls -lrt /mnt/gv
合計 24
-rw-r--r-- 1 root root 27  3月  1 13:51 2013 1.txt
-rw-r--r-- 1 root root 27  3月  1 13:51 2013 2.txt
-rw-r--r-- 1 root root 27  3月  1 13:51 2013 3.txt
-rw-r--r-- 1 root root 27  3月  1 13:51 2013 4.txt
-rw-r--r-- 1 root root 27  3月  1 13:51 2013 5.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 6.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 7.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 8.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 9.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 10.txt
-rw-r--r-- 1 root root 27  3月  1 13:52 2013 11.txt
-rw-r--r-- 1 root root 27  3月  1 13:53 2013 12.txt
-rw-r--r-- 1 root root 27  3月  1 13:53 2013 13.txt
-rw-r--r-- 1 root root 27  3月  1 13:53 2013 14.txt
-rw-r--r-- 1 root root 27  3月  1 13:53 2013 15.txt

# cat /mnt/gv/*
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ


正しくレプリケーションされています。


gatewayインスタンスを復帰させます。




インスタンスが立ち上がったら、一度10.0.1.8のglusterfsを解除して、iSCSIボリュームを再マウントします。

# ps -ax | grep gluster
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
24350 ?        Ssl    0:00 /usr/sbin/glusterd -p /var/run/glusterd.pid
24371 ?        Ssl    0:00 /usr/sbin/glusterfsd -s localhost --volfile-id gv0.10.0.1.8.mnt-sgw -p /var/lib/glusterd/vols/gv0/run/10.0.1.8-mnt-sgw.pid -S /tmp/6f580d4f3eab1dcb22c5aef9d7fb2275.socket --brick-name /mnt/sgw -l /var/log/glusterfs/bricks/mnt-sgw.log --xlator-option *-posix.glusterd-uuid=4f2e8dff-1b51-423e-8ac0-b0e892bd2e0d --brick-port 24013 --xlator-option gv0-server.listen-port=24013
24377 ?        Ssl    0:00 /usr/sbin/glusterfs -s localhost --volfile-id gluster/nfs -p /var/lib/glusterd/nfs/run/nfs.pid -l /var/log/glusterfs/nfs.log -S /tmp/f34a5ad241dbfecb7dcb5f2c14aab1f4.socket
24383 ?        Ssl    0:00 /usr/sbin/glusterfs -s localhost --volfile-id gluster/glustershd -p /var/lib/glusterd/glustershd/run/glustershd.pid -l /var/log/glusterfs/glustershd.log -S /tmp/c019293c2e261dcab94469694dc5674e.socket --xlator-option *replicate*.node-uuid=4f2e8dff-1b51-423e-8ac0-b0e892bd2e0d
24393 pts/1    S+     0:00 grep gluster
# kill -9 24350 24371 24377 24383

# umount /mnt/sgw
# umount /mnt/gv
# iscsiadm --mode discovery --type sendtargets --portal 10.0.1.5:3260
# iscsiadm --mode node --targetname iqn.1997-05.com.amazon:memorycraft-sgw1 --portal 10.0.1.5:3260,1 --logout
# iscsiadm --mode node --targetname iqn.1997-05.com.amazon:memorycraft-sgw1 --portal 10.0.1.5:3260,1 --login
# mount /dev/sda /mnt/sgw
# /etc/init.d/glusterd start
# mount -t glusterfs 10.0.1.8:/gv0 /mnt/gv



ここでブリック(GlusterFSを構成するディレクトリ)である/mnt/sgwをみてみると、

# ls -l /mnt/sgw
合計 112
-rw-r--r-- 2 root root  0  3月  1 13:51 2013 1.txt
-rw-r--r-- 2 root root  0  3月  1 13:52 2013 10.txt
-rw-r--r-- 2 root root  0  3月  1 13:52 2013 11.txt
-rw-r--r-- 2 root root 27  3月  1 14:07 2013 12.txt
-rw-r--r-- 2 root root  0  3月  1 13:53 2013 13.txt
-rw-r--r-- 2 root root 27  3月  1 14:07 2013 14.txt
....略....
-rw-r--r-- 2 root root  0  3月  1 14:00 2013 58.txt
-rw-r--r-- 2 root root  0  3月  1 13:52 2013 6.txt
-rw-r--r-- 2 root root 27  3月  1 14:07 2013 7.txt
-rw-r--r-- 2 root root  0  3月  1 13:52 2013 8.txt
-rw-r--r-- 2 root root  0  3月  1 13:52 2013 9.txt



ファイルの内容は同期されましたが、ファイルサイズが0のままです。
これをマウントポイントから読み出しを行うと即座に完全同期できます。

# find /mnt/gv -type f -exec dd if='{}' of=/dev/null bs=1M \; > /dev/null 2>&1

# ls -l /mnt/sgw
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 1.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 10.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 11.txt
-rw-r--r-- 2 root root 27  3月  1 14:07 2013 12.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 13.txt
-rw-r--r-- 2 root root 27  3月  1 14:07 2013 14.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 15.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 16.txt
...略.....
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 56.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 57.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 58.txt
-rw-r--r-- 2 root root 27  3月  1 14:14 2013 59.txt
-rw-r--r-- 2 root root 27  3月  1 14:12 2013 6.txt
-rw-r--r-- 2 root root 27  3月  1 14:14 2013 60.txt

これで同期が復旧しました。
その後も投入されつづけたファイルが同期されているのがわかります。




イニシエータインスタンスが落ちた場合




10.0.1.8をstopしてみます。




10.0.1.9では投入が反映され続けられています。
# ls -lrt /mnt/gv
.....略......
-rw-r--r-- 1 root root 27  3月  1 17:37 2013 562.txt
-rw-r--r-- 1 root root 27  3月  1 17:37 2013 563.txt
-rw-r--r-- 1 root root 27  3月  1 17:37 2013 564.txt
-rw-r--r-- 1 root root 27  3月  1 17:37 2013 565.txt
-rw-r--r-- 1 root root 27  3月  1 17:38 2013 566.txt
-rw-r--r-- 1 root root 27  3月  1 17:38 2013 567.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 568.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 569.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 570.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 571.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 572.txt
-rw-r--r-- 1 root root 27  3月  1 17:39 2013 573.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 574.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 575.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 576.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 577.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 578.txt
-rw-r--r-- 1 root root 27  3月  1 17:40 2013 579.txt
-rw-r--r-- 1 root root 27  3月  1 17:41 2013 580.txt
-rw-r--r-- 1 root root 27  3月  1 17:41 2013 581.txt


10.0.1.8をstartします。



SSHで入り、ブリックと、Glusterボリュームをマウントしなおします。

# df -h
Filesystem            Size  Used Avail Use% マウント位置
/dev/xvde1            6.0G  2.6G  3.1G  46% /
none                  3.7G     0  3.7G   0% /dev/shm

# mount /dev/sda /mnt/sgw
# mount -t glusterfs 10.0.1.8:/gv0 /mnt/gv
# ls -lrt /mnt/gv
.....略......
-rw-r--r-- 1 root root 27  3月  1 20:34 2013 1602.txt
-rw-r--r-- 1 root root 27  3月  1 20:34 2013 1603.txt
-rw-r--r-- 1 root root 27  3月  1 20:34 2013 1604.txt
-rw-r--r-- 1 root root 27  3月  1 20:34 2013 1605.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1606.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1607.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1608.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1609.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1610.txt
-rw-r--r-- 1 root root 27  3月  1 20:35 2013 1611.txt
-rw-r--r-- 1 root root 27  3月  1 20:36 2013 1612.txt


# ls -lrt /mnt/sgw
.....略.....
rw-r--r-- 2 root root 27  3月  1 20:34 2013 1602.txt
-rw-r--r-- 2 root root 27  3月  1 20:34 2013 1603.txt
-rw-r--r-- 2 root root 27  3月  1 20:34 2013 1604.txt
-rw-r--r-- 2 root root 27  3月  1 20:34 2013 1605.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1606.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1607.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1608.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1609.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1610.txt
-rw-r--r-- 2 root root 27  3月  1 20:35 2013 1611.txt
-rw-r--r-- 2 root root 27  3月  1 20:36 2013 1612.txt

マウントポイントに復帰され、投入が反映されています。
起動時に自動でマウントするにはfstabに以下のように設定します。

# cat /etc/fstab
/dev/sda         /mnt/sgw  ext4    _netdev         0 0
10.0.1.8:/gv0 /mnt/gv glusterfs defaults,_netdev 0 0


以上です。