ようへいの日々精進XP

よかろうもん

無料枠でざっくり学ぶ Microsoft Azure その 1 ~ Blob ストレージと Azure SDK for Ruby 等 ~

無料枠が続く限り Microsoft Azure を弄ってみる。

Blob ストレージとは

Microsoft Azure におけるストレージ

azure.microsoft.com

上記に詳しいことはおまかせして...以下の四種類に分類される。

  • Blob ストレージ
  • テーブル ストレージ
  • キュー ストレージ
  • ファイルストレージ(※ドキュメント上ではプレビューとなっている)

また、以下の図でストレージサービスの概要を把握することが出来る。

https://acomdpsstorage.blob.core.windows.net/dpsmedia-prod/azure.microsoft.com/ja-jp/documentation/articles/storage-introduction/20150904075548/storage-concepts.png

(出典:Microsoft Azure Storage の概要)

更に以下のように整理することで個人的にすんなり入ってきた。(※あくまでも個人的見解なので微妙な違いはあると思われる)

Microsoft Azure Amazon Web Service 備考
Blob ストレージ Amazon S3
テーブルサービス Amazon Dynamo DB
キューストレージ Amazon SQS
ファイルストレージ Amazon EFS ※ファイル共有サービスという点では似ているがレイヤーが異なる

ということで、今回は Blob ストレージを触りながら学んでみたい。

Blob とは

ここでの Blob とはストレージに保存する以下のようなファイル等を指す。(ドキュメントより抜粋)

  • ドキュメント
  • 写真、ビデオ、音楽、ブログなどのソーシャル データ
  • ファイル、コンピューター、データベース、およびデバイスのバックアップ データ
  • Web アプリケーションのイメージとテキスト
  • クラウド アプリケーションの構成データ
  • ログやその他の大きなデータセットなどのビッグ データ

Amazon S3 で言うところの「オブジェクト」を指すと思われる。

Blob はコンテナに放り込む

Blob はコンテナと呼ばれる Amazon S3 で言うところの「バケット」に放り込むことになる。ドキュメントには Blob をグルーピングするのがコンテナであると書かれている。コンテナには以下のような特徴がある。

  • ストレージアカウントの上限 500TB を超えない限りはコンテナは作りたい放題
  • コンテナ内の Blob も作りたい放題
  • コンテナ単位でアクセスコントロールを定義することが出来る

三種類の Blob ストレージ

名称 用途、特徴
ブロック BLOB ドキュメント、メディア ファイル、バックアップなどの格納に適している
追加 BLOB 新しいブロックを最後に追加することによってのみ更新できる(※現時点ではちょっと理解し難い)
ページ BLOB (ディスク) ランダムな書き込みをサポートするように最適化されおり、最大 1 TB までサイズを拡張可能、仮想マシンのディスクとして使用可能

ストレージのスペック

気になるストレージのスケーラビリティやスペックについては以下のドキュメントに整理されている。

azure.microsoft.com

コンテナ、Blob の命名規則

コンテナ、Blob には以下のような命名規則があるので注意する。

コンテナ名

  • 英数小文字、記号はハイフンは OK
  • 英数字で始める
  • 連続するハイフンは NG
  • 3 ~ 36 文字

blob 名

  • 任意の文字
  • 1 ~ 1024 文字以下
  • 大文字、小文字は区別される
  • URL 予約文字(: とか / とか...?)はエスケープする
  • パスは 254 階層まで
  • S3 と同様にディレクトリという概念は存在しないが / パスで仮想ディレクトリ構造を作ることが可能

旧ポータルでポチポチ使う Blob ストレージ

ストレージアカウントを作る

f:id:inokara:20150906083001p:plain

[+新規] をクリックしてストレージアカウント作成を開始する。

f:id:inokara:20150906083033p:plain

[簡易作成] から以下を指定する。

をそれぞれプルダウンから指定する。

f:id:inokara:20150906083051p:plain

ストレージアカウント作成完了。引き続き、コンテナを作成する。

コンテナを作る

f:id:inokara:20150906083106p:plain

ストレージアカウントから [コンテナー] タブをクリックして [コンテナーを作成する] をクリック。

f:id:inokara:20150906083119p:plain

[名前] と [アクセス] プルダウンから以下を選択する。

  • プライベート(コンテナの所有者のみアクセス可能)
  • パブリックコンテナー(コンテナとコンテナ内データにアクセス可能)
  • パブリック Blob(コンテナの Blob のみにアクセス可能)

パブリックコンテナーを利用することで Amazon S3ウェブホスティング機能のようなことを実装可能なようだ。

f:id:inokara:20150906083132p:plain

コンテナ作成完了。

Blob をアップロードする(のは...)

  • 管理コンソール(manage.windowsazure.com)からは出来ないので注意
  • Azure Storage Explorer というツールがあるとのこと

Azure SDK for Ruby と使う Blob ストレージ

動作確認環境

$ cat /etc/debian_version
8.1
$ ruby -v
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

参考

azure.microsoft.com

github.com

SDK のインストール

$ sudo gem install azure --no-ri --no-rdoc -V

上記で最新(0.7.0)の Microsoft Azure SDK for Ruby がインストールされるが...今回は最新ではなく一つ前のバージョン(0.6.2)ですすめる。理由は後述...。

アクセスキーを控える

ストレージアカウントにアクセスする為のアクセスキーを確認して控えておく。

f:id:inokara:20150906092906p:plain

ちょっと問題

README を参考にして以下のようなコンテナを作成するサンプルを用意して実行すると...

#!/usr/bin/env ruby
require "azure"

Azure.storage_account_name = "komanechi"
Azure.storage_access_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#
# Create Instance
#
blob_service = Azure.blobs

#
# Create Container
#
blob_service.create_container("hoge")

以下のようなエラー。

/usr/local/bundle/gems/azure-0.7.0/lib/azure/core/http/http_request.rb:150:in `call':c: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. (Azure::Core::Http::HTTPError)
RequestId:c47fa16c-0001-0040-0942-e8bdfa000000
Time:2015-09-06T01:25:49.6986168Z
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/http/signer_filter.rb:28:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/http/signer_filter.rb:28:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/http/http_request.rb:97:in `block in with_filter'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/service.rb:36:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/filtered_service.rb:34:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/core/signed_service.rb:41:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/blob/blob_service.rb:1380:in `call'
        from /usr/local/bundle/gems/azure-0.7.0/lib/azure/blob/blob_service.rb:119:in `create_container'
        from ./demo_0.7.0.rb:20:in `<main>'

HTTP リクエストのヘッダ内に何らの署名が必要なようだ。これ以上の追跡は改めてということで...旧バージョン(0.6.4)を利用してみることにする。

$ gem install azure --version '0.6.4' --no-ri --no-rdoc -V

以下、コンテナを作成するサンプル。

#!/usr/bin/env ruby

gem "azure", "0.6.4"
require "azure"

Azure.storage_account_name = "komanechi"
Azure.storage_access_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#
# Create Instance
#
blob_service = Azure::BlobService.new

#
# Create Container
#
def create_container
  blob_service.create_container("hoge")
end

#
# Check Container List
#
containers = blob_service.list_containers()

#
# Output Container Infomation
#
containers.each do |container|
  puts "Container Name: " + container.name
  puts "Container Properties: " + "#{container.properties}"
  unless container.name = "hoge"
    create_container
  end
end

サンプルスクリプトを実行すると...以下のように出力される。

$ ./demo.rb
Container Name: hoge
Container Properties: {:last_modified=>"Sun, 06 Sep 2015 02:14:19 GMT", :etag=>"\"0x8D2B660DF557864\"", :lease_status=>"unlocked", :lease_state=>"available"}
Container Name: nahanaha
Container Properties: {:last_modified=>"Sun, 06 Sep 2015 00:32:28 GMT", :etag=>"\"0x8D2B652A4D8E918\"", :lease_status=>"unlocked", :lease_state=>"available"}

ポータルを見てみる。

f:id:inokara:20150906113524p:plain

コンテナを確認出来た。

さらに、コンテナに Blob をアップロードするサンプル。

#!/usr/bin/env ruby
gem "azure", "0.6.4"
require "azure"

container_name = ARGV[0]

Azure.config.storage_account_name = "komanechi"
Azure.storage_access_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#
# Create Instance
#
blob_service = Azure::BlobService.new

#
# Upload blob
#
puts "Blob upload..."
content = File.open("test.txt", "rb") { |file| file.read }
blob = blob_service.create_block_blob(container_name,"test-txt", content)

#
# Output blob name
#
puts "Blob List..."
blobs = blob_service.list_blobs(container_name)
blobs.each do |blob|
  puts " Blob Name: " + blob.name
  puts " Blob Properties: " + "#{blob.properties}"
end

サンプルスクリプトを実行すると...以下のように出力される。

$ ./upload.rb ${container_name}
Blob upload...
 done..

Blob List...
 Blob Name: test-txt
 Blob Properties: {:last_modified=>"Sun, 06 Sep 2015 02:46:58 GMT", :etag=>"0x8D2B6656F27EDA2", :lease_status=>"unlocked", :lease_state=>"available", :content_length=>0, :content_type=>"application/octet-stream", :content_encoding=>"ASCII-8BIT", :content_language=>"", :content_md5=>"1B2M2Y8AsgTpgAmY7PhCfg==", :cache_control=>"", :blob_type=>"BlockBlob"}

更に Blob を削除したいので以下のようなサンプルスクリプトを作成。

#!/usr/bin/env ruby
gem "azure", "0.6.4"
require "azure"

@container_name = ARGV[0]

Azure.config.storage_account_name = "komanechi"
Azure.storage_access_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#
# Create Instance
#
@blob_service = Azure::BlobService.new

#
# Delete blob
#
def delete_blob(blob_name)
  puts "Delete blob..."
  @blob_service.delete_blob(@container_name, blob_name)
  puts " done..."
end

#
# Main(Output blob name and Delete blob)
#
puts "Blob List..."
blobs = @blob_service.list_blobs(@container_name)
blobs.each do |blob|
  puts " Blob Name: " + blob.name
  puts " Blob Properties: " + "#{blob.properties}"
  delete_blob(blob.name)
end

サンプルスクリプトを実行すると...

$ ./delete.rb hoge
Blob List...
 Blob Name: test-txt
 Blob Properties: {:last_modified=>"Sun, 06 Sep 2015 03:06:46 GMT", :etag=>"0x8D2B668334886A9", :lease_status=>"unlocked", :lease_state=>"available", :content_length=>0, :content_type=>"application/octet-stream", :content_encoding=>"ASCII-8BIT", :content_language=>"", :content_md5=>"1B2M2Y8AsgTpgAmY7PhCfg==", :cache_control=>"", :blob_type=>"BlockBlob"}
Delete blob...
 done...

# もう一回実行
$ ./delete.rb hoge
Blob List...

雑なスクリプトで恐縮だが、コンテナの作成、Blob のアップロード、リスト、削除を一通り体験。


fluent-plugin-azurestorage と使う Blob ストレージ

せっかくなので

Blob ストレージにログを放り込んでみたいので fluent-plugin-azurestorage というプラグインを利用してアクセスログを Blob ストレージに放り込んでみる。

github.com

尚、最新のバージョン(0.7.0)で動作させようとすると Azure::BlobService からインスタンスを生成する際に以下のようなエラーとなる。

2015-09-06 03:37:58 +0000 [error]: unexpected error error_class=NameError error=#<NameError: uninitialized constant Azure::BlobService>

ということで、Azure SDK for Ruby のバージョンに注意する(最新バージョンは使わない)。

尚、本プラグイン

Azure Storate output plugin buffers logs in local file and upload them to Azure Storage periodically. This plugin is porting from fluent-plugin-s3 for AzureStorage.

とあるので設定ファイルのパラメータレベルでは fluent-plugin-s3 と互換性があると思われる。

動作確認環境

雑にコンテナ化したものを利用する。

github.com

以下、コンテナの fluentd や OS のバージョン。

$ docker exec fluentd cat /etc/debian_version
8.1

$ docker exec fluentd fluentd --version
fluentd 0.12.15

プラグインのインストール

動作確認環境と前後するが、以下のようにインストール。

# プラグインのインストール
$ sudo gem install fluent-plugin-azurestorage --no-ri --no-rdoc -V


# プラグインインストール時に最新の Azure SDK for Ruby がインストールされるのでアンインストールする
$ gem uninstall azure --version '0.7.0'

# 一つ前の Azure SDK for Ruby をインストールする
$ gem install azure --version '0.6.4' --no-ri --no-rdoc -V

fluentd の設定

以下のように設定する。

<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>
<match docker.*>
  type copy
    <store>
      type stdout
    </store>
    <store>
      type azurestorage

      azure_storage_account    fluentd
      azure_storage_access_key ${ストレージアカウントのアクセスキー}
      azure_container          logs
      azure_storage_type       blob
      store_as                 gzip
      auto_create_container    true
      path                     logs/
      azure_object_key_format  %{path}%{time_slice}_%{index}.%{file_extension}
      buffer_path              /var/log/td-agent/buffer/azurestorage

      time_slice_format        %Y%m%d-%H
      time_slice_wait          10m
      utc
      flush_interval           10s
    </store>
</match>

主なパラメータについては以下のとおり。

パラメータ 設定値
azure_storage_account ストレージアカウント名
azure_storage_access_key ストレージアカウントのアクセスキー
azure_container コンテナ名(auto_create_containertrue の場合、自動で作成する)
azure_storage_type blob
store_as コンテナに保存する blob のファイルタイプ(jsongzip 等をサポート)
auto_create_container true(コンテナが存在しない場合にはコンテナを自動作成する)

その他のパラメータについては README を。

ログが保存されていることを確認

  • 以下のようにコンテナを起動する
# fluentd コンテナの起動
docker run -d \
  --name=fluentd \
  -p 24224:24224 \
  --env 'STORAGE_NAME=${STORAGE_NAME}' \
  --env 'STORAGE_ACCESS_KEY=${STORAGE_ACCESS_KEY}' \
  --env 'STORAGE_CONTAINER=${STORAGE_CONTAINER}' \
inokappa/fluentd-azure-blob /app/start.sh

# nginx コンテナの起動
docker run -d \
  -p 80:80 \
  --log-driver=fluentd \
  --log-opt=fluentd-address=${FLUENTD_PORT_24224_TCP_ADDR}:24224 \
  --log-opt=fluentd-tag=docker.{{.FullID}} \
  --name=nginx \
nginx
  • nginx コンテナにアクセス
curl localhost
curl localhost
curl localhost
curl localhost
curl localhost
  • コンテナにログがアップロードされていることを確認

f:id:inokara:20150906143017p:plain

  • ダウンロードして確認
2015-09-06T05:20:59Z   docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"log":"172.17.42.1 - - [06/Sep/2015:05:20:59 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\"","container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e","container_name":"/nginx","source":"stdout"}
2015-09-06T05:21:00Z    docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"container_name":"/nginx","source":"stdout","log":"172.17.42.1 - - [06/Sep/2015:05:21:00 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\"","container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e"}
2015-09-06T05:21:00Z    docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"container_name":"/nginx","source":"stdout","log":"172.17.42.1 - - [06/Sep/2015:05:21:00 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\"","container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e"}
2015-09-06T05:21:01Z    docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"log":"172.17.42.1 - - [06/Sep/2015:05:21:01 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\"","container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e","container_name":"/nginx","source":"stdout"}
2015-09-06T05:21:01Z    docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e","container_name":"/nginx","source":"stdout","log":"172.17.42.1 - - [06/Sep/2015:05:21:01 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\""}
2015-09-06T05:21:01Z    docker.40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e {"container_id":"40ca6635a2ba8706627c2219f074c7ab9eeb9fc8385077cd9c9071af2789546e","container_name":"/nginx","source":"stdout","log":"172.17.42.1 - - [06/Sep/2015:05:21:01 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.33.0\" \"-\""}

一通りの動作確認終わり。


おわり

Blob ストレージとは...からちょっとした応用までやってみたが、Amazon S3 と概念的には似ていると感じた。 尚、コスト、セキュリティ、可用性、耐久性、制限事項等についてはドキュメントを引き続き読んで理解を深めたい。 また、最新版の SDK については手元の環境において発生するエラーについても引き続き調査したいと思う。

以上。