LoginSignup
14
17

More than 5 years have passed since last update.

pryとfogでお手軽マルチクラウド対応CLI

Last updated at Posted at 2015-01-11

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クラウドAWSDigitalOceanでは以下のようになります。
defaultを簡単に切り替えられるようアンカーとエイリアスを使用しています。

~/.fog
---

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は内部的にlistTemplatestemplatefitler=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")

publictrue にすることでファイルを公開することができます。

>> 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のコマンドのコードも参考になると思います。

14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17