時は2017年、クラウドストレージやオブジェクトストレージといったものが、当たり前のように使われる時代になりました。特に、Amazon, Microsoft, Google等、パブリッククラウドサービスの事業者が提供するオブジェクトストレージサービスは、可用性・信頼性・スケーラビリティ、そして価格(TCO)の面で、オンプレに比べて大きなアドバンテージがあると言えるでしょう。
今回、私が仕事で使っている組み合わせである、Elixir と Azure BLOB Storage との組み合わせをご紹介します。
Microsoft Azure BLOB Storage について
スケーラブルな非構造化データ用オブジェクトストレージです。詳しくは公式をご覧ください。
比較?
AWSやGCPとの比較にあたっては、一概には甲乙付けがたいのですが、Azureは他と比べてSLAが高めに設定されている点が、エンタープライズ利用を強く意識しているのかな?と思っています。
(Deep dive on AWS vs. Azure vs. Google cloud storage options | Network World より転載)
SLA 99.95% (AWS, Google) と 99.99% (Azure) の差は、年間のサービス停止時間が最大15,768秒 か 3,154秒 かの違いです。
ま、細かいことは気にせず、先に進みましょう。
ExAzure
azukiapp/ex_azure: Azure wrapper for Elixir using :erlazure.
Erlangで利用可能な Azure Storage Services のAPIライブラリである erlazure の、Elixir用ラッパーです。
インストール
公式のREADME.md に書かれている通り・・・ではなく、そちらは書き方がちょっと古いので注意。以下に、本稿執筆時点で動作する書き方で、インストール手順を示します。
dependencies として定義
mix.exs
の deps/0
内のリストに以下のエントリを加えます。
{:ex_azure, "~> 0.1.0"}
Phoenix Framework の場合、以下のようになります。
defp deps do
[
{:phoenix, "~> 1.3.0-rc"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:ex_azure, "~> 0.1.0"}
]
end
OTPアプリケーションとして登録
mix.exs
の application/0
内のリスト内、extra_applications
キーに対応する値のリストに、 :ex_azure
を加えます。(存在しない場合はキーごと作ります。)
Phoenix Framework の場合、以下のようになります。
def application do
[
mod: {MyApp, []},
extra_applications: [:logger, :ex_azure]
]
end
認証情報を設定
use Mix.Config
されているファイル (Phoenix Framework の場合は config/*.exs
) にて、account と access_key の情報が受け取れるようにします。
以下は公式の例で、それぞれの情報を環境変数から取得します。
config :ex_azure,
account: System.get_env("AZURE_ACCOUNT"),
access_key: System.get_env("AZURE_ACCESS_KEY")
なお、 access_key
の取得は、Webブラウザを利用して、 portal.azure.com より、Storage accounts > ${Storage Account名} > Access Keys から行います。
使い方
最低限、以下の3つの処理及び実装方法を押さえておけば :ok
でしょう。
List
コンテナ container_name
内のオブジェクトに関するリストを取得します。
{:ok, blobs} = ExAzure.request(:list_blobs, [container_name])
Get
container_name
コンテナから object_name
オブジェクトをダウンロードします。
{:ok, {:ok, file}} = ExAzure.request(:get_blob, [container_name, object_name])
Create
local_object_path
からデータを読み取り、 container_name
コンテナに object_name
という名前でオブジェクトを保存します。
{:ok, _created} = ExAzure.request(:put_block_blob,
[container_name, object_name, File.read!(local_object_path)])
戻り値の created
は、created
というStringが入っているだけなので、捨てて良いです。
便利な自作ヘルパーモジュール
list_blobs
の戻り値は、このような構造の Dict
になっています。
%{body:
[
{
:cloud_blob,
'some_object_name.jpg',
[],
[],
[
last_modified: 'Sun, 29 Oct 2017 02:44:06 GMT',
etag: '0x8D51E76ECA421B3',
content_length: 125182,
content_type: 'application/octet-stream',
content_encoding: [],
content_language: [],
content_md5: 'PUgbs4kkHJWBpitHhGWsgw==',
cache_control: [],
blob_type: :block_blob,
lease_status: :unlocked,
lease_state: :available
],
[]
},
...
]
}
このうち欲しい情報は限られているので、それを取得する get_info/3
や、URL文字列を構築するための build_url/2
、オブジェクトをダウンロードした上でディスク上への保存まで行う download/3
などを含む、便利なモジュールを自作しました。よろしければご自由にどうぞ。
defmodule MyApp.AzureBlob do
use MyApp.Web, :model
def get_info(x, container_name, acc \\ [])
def get_info([h|t], container_name, acc) do
acc = if elem(h, 0) == :cloud_blob and elem(h, 1) |> List.to_string() == container_name do
acc ++ elem(h, 4)
else
acc
end
get_info(t, container_name, acc)
end
def get_info([], _container_name, acc) do
acc
end
def build_url(object_name, container_name) when is_binary(object_name) and is_atom(container_name) do
Application.get_env(:my_app, :object_storage)[:base_url]
<> "/"
<> Application.get_env(:my_app, :object_storage)[:containers][container_name]
<> "/"
<> object_name
end
def download(object_name, container_name, dirname) when is_binary(object_name) and is_binary(container_name) and is_binary(dirname) do
{:ok, {:ok, file}} = ExAzure.request(:get_blob, [container_name, object_name])
:ok = File.write(Path.absname(dirname <> "/" <> object_name), file)
:ok
end
end
erlazure
gullitmiranda/erlazure: Windows Azure Erlang bindings
ここで、ExAzure
がラップする、Erlang の erlazure
に関しても少し見てみましょう。
Elixir からも利用可能ですが、使い勝手の上では、 erlazure
はプロセスとして予め明示的に立ち上げておく必要があるという点が大きく異なります。
{:ok, pid} = :erlazure.start(account, key)
そして例えば Create
操作をする場合は、以下の要領となります。
{ok, _created} = :erlazure.put_block_blob(pid, container_name, object_name, Binary)
ExAzure
の方が若干シンプルですね。
まとめ
Elixir で Microsoft Azure の BLOB Storage を利用する手法として、 ExAzure
をご紹介しました。
erlazure
のラッパーであるため、今回ご紹介したもの以外にも、様々な関数が実行できるはずです。また、今回
BLOB Storage にフォーカスしましたが、erlazure
はそれだけでなく、Queue Storage、Table Storage にも対応しており、この点は ExAzure
に関しても同様なのではないでしょうか。
Microsoft Azure の BLOB Storage、あるいは 各種Storage Service を利用される際は、ぜひ使ってみて下さい。