Rubyの高機能なREPLであるpryとマルチクラウド対応ライブラリのfogを利用して、簡単に複数のクラウドを扱うことができます。
TL;DR
- pryとfogを組み合わせるとクラウドの操作に補完・シンタックスハイライト・履歴・Rubyの配列操作などが使え非常に便利
- fogが提供しているインターフェースにより異なるサービスでも同じコマンドで操作できる
- 仮想マシンだけでなくオブジェクトストレージ、DNS、CDNも扱える
準備
pryとfogをインストールしておきます。
ライブラリのインストール
$ gem install pry
$ gem install fog
pryの設定
~/.pryrc
に下記を追記してpryからfogを使ってクラウドに接続するためのコマンドを追加します。
Pry::Commands.create_command "fog" do
description "Connect to the service provider using Fog"
command_options :keep_retval => true
banner <<-BANNER
Usage:
fog [OPTIONS] PROFILE # Connect to the provider using the specified profile
fog -l # list profiles
BANNER
def setup
require 'yaml'
require 'fog'
@profiles = YAML.load_file(File.expand_path("~/.fog"))
end
def options(opt)
opt.on :l, :list, 'List profiles'
opt.on :r, :resource, 'Resource (compute, storage, dns, cdn)',
:argument => true, :default => 'compute', :as => String
opt.on :p, :provider, 'Provider', :argument => true, :as => String
opt.on :n, :nosession, 'Return a connection object without starting session'
end
def process
if opts[:list]
@profiles.keys.each { |k| output.puts k }
return
end
profile_name = args.first.nil? ? 'default' : args.first
profile = symbolize(@profiles[profile_name])
if profile.nil?
output.puts "No such profile: #{args.first}"
return
end
profile[:provider] = opts[:provider].nil? ? profile[:provider] : opts[:provider]
connection =
case opts[:resource]
when "storage"
Fog::Storage.new(profile)
when "dns"
Fog::DNS.new(profile)
when "cdn"
Fog::CDN.new(profile)
else
Fog::Compute.new(profile)
end
if ! opts[:nosession]
connection.pry :prompt => proc { |_, _, pry|
"[#{pry.input_array.size}] fog[#{opts[:resource].downcase}:#{profile[:provider].downcase}:#{profile_name}]> "
}
end
connection
end
private
def symbolize(hash)
return nil if hash.nil?
hash.keys.each do |key|
hash[(key.to_sym rescue key) || key] = hash.delete(key)
return hash
end
end
end
プロファイルの設定
APIのキーやトークンなどを毎回入力する手間を省くため~/.fog
というファイルに接続情報を書いておきます。
ここでは特定のプロバイダに接続するときのアカウント情報をプロファイルと呼ぶことにします。
設定する必要がある項目はプロバイダ毎に異なりますが、基本的には以下のような構成になっています。
デフォルトではdefaultというプロファイル名が使用されます。
プロファイル名:
provider : プロバイダー
api_key : API_KEY
secret_key : SECRET_KEY
今回試してみたIDCFクラウド、AWS、DigitalOceanでは以下のようになります。
defaultを簡単に切り替えられるようアンカーとエイリアスを使用しています。
---
idcfcloud: &idcfcloud
provider: Cloudstack
cloudstack_scheme: https
cloudstack_host: compute.jp-east.idcfcloud.com
cloudstack_path: /client/api
cloudstack_port: 443
cloudstack_api_key: YOUR_API_KEY
cloudstack_secret_access_key: YOUR_SECRET_KEY
aws: &aws
provider: AWS
aws_access_key_id: YOUR_ACCESS_KEY_ID
aws_secret_access_key: YOUR_SECRET_ACCESS_KEY
digitalocean: &digitalocean
provider: DigitalOcean
digitalocean_api_key: YOUR_API_KEY
digitalocean_client_id: YOUR_CLIENT_KEY
default: *idcfcloud
使い方
クラウドへの接続
pryを起動するとfog
というコマンドが使えるようになっています。
次のように引数にプロファイル名を与えると、そのプロファイルを使ってクラウドに接続し、
新しいpryのセッションが開始されます。プロファイル名を与えなかった場合にはdefault
が使用されます。
[1] pry(main)> fog idcfcloud
[1] fog[compute:cloudstack:idcfcloud]>
スコープがfogの接続オブジェクトになっているので、各種メソッドを実行することができます。
[1] fog[compute:cloudstack:idcfcloud]> zones
exit
コマンドで現在のセッションを抜けることができます。
[2] fog[compute:cloudstack:idcfcloud]> exit
プロバイダの指定
provider
を指定していなかったり、複数のプロバイダの接続情報をまとめて書いている場合にはオプション-p
でプロバイダを指定することができます。
[1] pry(main)> fog -p cloudstack default
戻り値
fogコマンドは_
で接続したfogのオブジェクトを返します。
fogのオブジェクトのみが欲しい場合には、-n
オプションを付けるとセッションを開始せずに_
だけを返します。
[1] pry(main)> fog -n idcfcloud
[2] pry(main)> c = _
[3] pry(main)> c.zones
使用例
以下ではプロンプトを「>>」で表します。出力は適当に省略しています。
IDCFクラウドで仮想マシン作成・削除
まずIDCFクラウドに接続します。
>> fog idcfcloud
仮想マシンを作成するために必要なゾーン、テンプレート、サービスオファリングの情報を取得します。
ゾーンの情報はzones
で取得できます。現在は1ゾーンしかないので最初の要素を取得します。
>> zone = zones.first;
テンプレートはimages
で取得できます。imagesは内部的にlistTemplates
を templatefitler=self
で呼んでいるので、提供されているテンプレートも取得するにはオプションでtemplatefilter
の値を指定する必要があります。
>> images
=> []
>> images.all('templatefilter'=>'executable').collect { |i| i.name }
=> ["CentOS 7.0 64-bit",
"CentOS 6.5 64-bit",
"CentOS 5.10 64-bit",
"CentOS 5.10 32-bit",
"Red Hat Enterprise Linux 6.5 64-bit",
"Red Hat Enterprise Linux 5.10 64-bit",
"Red Hat Enterprise Linux 5.10 32-bit",
"Ubuntu Server 14.04 LTS 64-bit",
"Ubuntu Server 12.04 LTS 64-bit",
"Ubuntu Server 10.04 LTS 64-bit",
"VyOS 1.0.4 64-bit",
"AppTemplate CentOS 6.5 64-bit",
"CentOS 6.5 64-bit for Vagrant",
"ubuntu-1gb-opt",
"Windows Server 2008 R2 Ent SP1",
"Windows Server 2012 R2 Std",
"cl-base-centos65-08192014",
"Windows Server 2012 Std",
"Windows Server 2012 R2 Std + SQL2014 Std",
"Windows Server 2012 R2 Std + SQL2012 Std SP2",
"Ubuntu-1G",
"Windows Server 2012 R2 Std + SQL2008 R2 Std SP3",
"Windows Server 2012 Std + SQL2014 Std",
"Ubuntu-1GB-2",
"Windows Server 2012 Std + SQL2008 R2 Std SP3",
"Windows Server 2008 R2 Ent SP1 + SQL2014 Std",
"Windows Server 2008 R2 Ent SP1 + SQL2012 Std SP2",
"Windows Server 2008 R2 Ent SP1 + SQL2008 R2 Std SP3",
"Ubuntu-1GB-op",
"CoreOS (stable) 494.4.0 64-bit",
"RDB Service BASE Template",
"Windows Server 2012 Std + SQL2012 Std SP2"]
今回はCentOS 7.0 64-bit
を使用します。findメソッドを使って特定の要素を抜き出すことができます。
>> image = images.all('templatefilter'=>'executable').find{|i| /CentOS 7.0 64-bit/ =~ i.name }
サービスオファリングはflavors
で所得できます。getメソッドでUUIDを指定してlight.S1
を指定します。
>> flavors.collect{|f| [f.id, f.name, f.cpu_number, f.cpu_speed, f.memory] }
=> [["e01a9f32-55c4-4c0d-9b7c-d49a3ccfd3f6", "light.S1", 1, 800, 1024],
["d1aac6d2-bb47-4106-90d0-6a73ac3ae78e", "light.S2", 1, 800, 2048],
["55621f17-4d38-457c-ba34-e6199701b67b", "standard.S4", 1, 2400, 4096],
["7ae143a6-5662-4f1d-bc4c-10defa775bcb", "standard.M8", 2, 2400, 8192],
["6a99ff4c-1a24-4aa6-b4cc-600220987ed0", "standard.L16", 4, 2400, 16384],
["354c62e6-b99b-42f2-b5c7-e741f1085422", "standard.XL32", 8, 2400, 32768],
["8cf15770-c3c8-4efc-8ae5-b8327790db76", "highcpu.M4", 2, 2600, 4096],
["435c1aab-e796-42c7-9320-22ebdc8f50aa", "highcpu.L8", 4, 2600, 8192],
["6fda5e0c-e64d-46ea-893d-7e2ac9e128e7", "highcpu.XL16", 8, 2600, 16384],
["7c548831-427b-437c-9c8b-80dde8031303", "highcpu.2XL32", 16, 2600, 32768],
["12e39b73-3ce6-4e57-9036-3dac0c2b2b06", "highmem.M16", 2, 2200, 16384],
["d59817bc-ed79-4083-8b71-51b26c76d311", "highmem.L32", 4, 2200, 32768],
["ee5ee568-76b2-46ad-9221-c695e6f2149d", "highmem.XL64", 8, 2200, 65536],
["9a2f3ee4-af46-4790-9331-753674c16e68", "highio.5XL128", 40, 2492, 131072]]
>> flavor = flavors.get("e01a9f32-55c4-4c0d-9b7c-d49a3ccfd3f6")
仮想マシン作成に必要な情報が集まったので、いよいよ仮想マシンを作成します。
>> server = servers.create(:display_name=>"fogtest", :zone_id=>zone.id, :image_id=>image.id, :flavor_id=>flavor.id)
servers.create
を実行すると作成中のServerのインスタンスが返ります。
作成が完了するのを待つには、次のようにwait_for
メソッドを使用します。
>> server.wait_for { ready? }
仮想マシンを削除する際にはdestroy
メソッドを使用します。
>> server.destroy
DigitalOceanで仮想マシン作成・削除
まずは、DigitalOceanに接続します。
>> fog digitalocean
ほとんどの操作はIDCFクラウドと同じですが、DigitalOceanの場合にはzonesではなくregionsを使用します。
また、作成した仮想マシンにSSHで接続するため作成時にssh_key_ids
を指定しています。
>> region = regions.first
>> images.all.collect{|i| [i.id, i.distribution, i.name] }
=> [[9836782, "CoreOS", "557.0.0 (alpha)"],
[9836871, "CoreOS", "522.4.0 (beta)"],
[9836874, "CoreOS", "522.4.0 (stable)"],
[6370882, "Fedora", "20 x64"],
[6370885, "Fedora", "20 x32"],
[6370968, "Fedora", "19 x64"],
[6370969, "Fedora", "19 x32"],
[6372105, "CentOS", "6.5 x32"],
[6372108, "CentOS", "6.5 x64"],
[6372321, "CentOS", "5.10 x64"],
[6372425, "CentOS", "5.10 x32"],
[6372526, "Debian", "7.0 x64"],
[6372528, "Debian", "7.0 x32"],
[6372581, "Debian", "6.0 x64"],
[6372662, "Debian", "6.0 x32"],
[6374124, "Ubuntu", "10.04 x64"],
[6374125, "Ubuntu", "10.04 x32"],
[6374128, "Ubuntu", "12.04.5 x64"],
[6374130, "Ubuntu", "12.04.5 x32"],
[7053293, "CentOS", "7.0 x64"],
[9801948, "Ubuntu", "14.04 x32"],
[9801950, "Ubuntu", "14.04 x64"],
[9801951, "Ubuntu", "14.10 x32"],
[9801954, "Ubuntu", "14.10 x64"],
[6376601, "Ubuntu", "Ruby on Rails on 14.04 (Nginx + Unicorn)"],
[6423475, "Ubuntu", "WordPress on 14.04"],
[6732690, "Ubuntu", "LEMP on 14.04"],
[6732691, "Ubuntu", "LAMP on 14.04"],
[6734697, "Ubuntu", "Django on 14.04"],
[6798184, "Ubuntu", "MEAN on 14.04"],
[8375425, "Ubuntu", "Drupal 7.34 on 14.04"],
[8383942, "Ubuntu", "Dokku v0.2.3 on 14.04 (w/ Docker 1.3.2)"],
[8412876, "Ubuntu", "Magento 1.9.1.0 on 14.04"],
[8917824, "Ubuntu", "ownCloud 7.0.4 on 14.04"],
[9063170, "Ubuntu", "Ghost 0.5.7 on 14.04"],
[9303378, "Ubuntu", "node-v0.10.34 on 14.04"],
[9435356, "Ubuntu", "Docker 1.4.1 on 14.04"],
[9723053, "Ubuntu", "GitLab 7.6.2 CE on 14.04"],
[7572830, "Ubuntu", "Redmine on 14.04"]]
>> image = images.get(6372108)
>> flavor = flavors.all.find{|f| /512MB/ =~ f.name}
>> ssh_key = ssh_keys.first
>> server = servers.create(:name=>"fogtest", :region_id=>region.id, :image_id=>image.id, :flavor_id=>flavor.id, :ssh_key_ids=>[ssh_key.id])
>> server.wait_for { ready? }
>> server.ssh("hostname").first.stdout
=> "fogtest\r\n"
>> server.destroy
S3でバケット作成・ファイル転送
fogでは仮想マシンの操作だけでなく、オブジェクトストレージを扱うこともできます。
例としてS3の操作をしてみます。
オブジェクトストレージを扱う際にはオプションで-r storage
を指定して接続します。
>> fog -r storage aws
バケットの一覧はdirectories
で見ることができます。
>> directories
=> []
テスト用にバケットを作成します。
>> directories.create(:key=>"fogtest#{Time.now.to_i}")
=> <Fog::Storage::AWS::Directory
key="fogtest1420954226",
creation_date=nil,
location="us-east-1"
>
バケットにファイルを作成します。
>> directories.first.files.create(:key=>"test.txt", :body=>"test")
public
をtrue
にすることでファイルを公開することができます。
>> file.public = true
>> file.save
>> Net::HTTP.get(URI.parse(file.public_url))
=> "test"
body
に値をセットすることで、ファイルの中身を書き換えることができます。
>> file.body = "test2"
>> file.save
これを利用するとローカルのファイルをS3に送ることができます。
>> file.body = File.open("/path/to/file")
>> file.save
もちろん作成時に指定してもOKです。
>> directories.first.files.create(:key=>"test.txt", :body=>File.open("/path/to/file"))
逆にS3上のファイルを持ってくる際にはローカルでファイルを作成しbodyの中身を書き込みます。
>> File.write(file.key, file.body)
destroy
メソッドでファイル・ディレクトリを削除することができます。
>> file.destroy
>> directories.first.destroy
最後に
Vagrantのプラグインを作成するためにfogを使う必要があったので、pryから使ってみたところ想像以上に便利でした。pryからライブラリを使うと便利な場面は他にもたくさんありそうです。カスタムコマンドの作成についてはpryのWikiにも情報がありますがpryのコマンドのコードも参考になると思います。