ブログ

ryuzeeによるブログ記事。不定期更新

Vagrant1.1 + vagrant-awsで複数インスタンスを作る方法

※2013/5更新:最新のvagrant-awsの仕様にあわせて修正

Vagrant(1.1)でvagrant-awsを使ってEC2に複数インスタンスをまとめて作ることが可能です。やり方は通常のMultiVMの場合と同じです。 以下は冗長ですが、サンプルのソースです。WebサーバとDBサーバのインスタンスを起動し、それぞれChefサーバに接続してそれぞれ設定されたロールの通りにセットアップします。なお、vagrant-awsをまだインストールしていない場合は

vagrant plugin install vagrant-aws

としてください。

ちなみに以下のままだと色々問題がありますが、それは後ほど説明します。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

  # 共通設定
  box_name = "dummy"
  box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
  ssh_username = "root"

  #---- ここからWebサーバ作る
  config.vm.define :web do |web|
    # EC2使う時も指定が必要なのでダミーを.
    web.vm.box = box_name 
    web.vm.box_url = box_url

    web.vm.provider :aws do |aws, override|
      aws.access_key_id = ENV['AWS_ACCESS_KEY_ID']
      aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] 
      aws.keypair_name = ENV['AWS_KEYPAIR_NAME']
      override.ssh.username = ssh_username 
      override.ssh.private_key_path = ENV['AWS_PRIVATE_KEY_PATH'] 

      # タグを指定します
      aws.tags = ["test", "dev", "web"] 
      # リージョンを設定します
      aws.region = "ap-northeast-1"
      # アベイラビリティゾーンを設定する必要がある場合
      aws.availability_zone = "ap-northeast-1c"
      aws.ami = "ami-cd9212cc"
      # インスタンスタイプを設定します
      aws.instance_type = "t1.micro"
      aws.ssh_username = "root"
      aws.security_groups = ["web"]
    end

    web.vm.provision :chef_client do |chef|
      chef.chef_server_url = "http://chef.ryuzee.com:4000"
      chef.validation_key_path = "~/.chef/validation.pem"
      chef.environment = "development"
      chef.validation_client_name = "chef-validator"
      chef.client_key_path = "/etc/chef/client.pem"
      chef.add_role "web"
    end
  end

  #---- ここからDBサーバ作る
  config.vm.define :db do |db|
    # EC2使う時も指定が必要なのでダミーを.
    db.vm.box = box_name 
    db.vm.box_url = box_url
    db.ssh.username = ssh_username 

    db.vm.provider :aws do |aws, override|
      aws.access_key_id = ENV['AWS_ACCESS_KEY_ID']
      aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] 
      aws.keypair_name = ENV['AWS_KEYPAIR_NAME']
      override.ssh.username = ssh_username 
      override.ssh.private_key_path = ENV['AWS_PRIVATE_KEY_PATH'] 

      # タグを指定します
      aws.tags = ["test", "dev", "db"] 
      # リージョンを設定します
      aws.region = "ap-northeast-1"
      # アベイラビリティゾーンを設定する必要がある場合
      aws.availability_zone = "ap-northeast-1c"
      aws.ami = "ami-cd9212cc"
      # インスタンスタイプを設定します
      aws.instance_type = "t1.micro"
      aws.ssh_username = "root"
      aws.security_groups = ["db"]
    end

    db.vm.provision :chef_client do |chef|
      chef.chef_server_url = "http://chef.ryuzee.com:4000"
      chef.validation_key_path = "~/.chef/validation.pem"
      chef.environment = "development"
      chef.validation_client_name = "chef-validator"
      chef.client_key_path = "/etc/chef/client.pem"
      chef.add_role "db"
    end
  end
end

これでインスタンスが作られますが、いくつか問題があります。 ・EIPなしだと、WebサーバとDBサーバ間の通信を制御しようとしてもソースアドレスが絞れないのでまずい ・vagrant-awsにはEIPを自動で割り当てる機能がない。したがってインスタンスを起動してから自分でEIPを割り当てないといけない ・せっかく自動でインスタンス作ってるのに、手でEIP割り当てるとか辛い。さらにプラグイン作るとなんとかなるかもしんない。

ということで、このままだと複数台構成を一気に楽ちんに作ることはできなそうな気がします。

そこで、VPCとの組み合わせ技を使います。

  • vagrant-awsでVPCを使うようにするには、Vagrantfileの中でローカルIPアドレスとセキュリティグループとサブネットIDを指定すればOK(下のサンプルの抜粋を参照)
  • vagrantを起動すると、起動したインスタンスにはpublic subnetでもprivate subnetでもローカルIPアドレスが割り当てられる。public subnetでEIPをVagrant側から割り当てたりはできない
  • 起動されたインスタンスはローカルIPしか持たないが、Vagrantではインスタンスを起動したあとにsshやscpでインスタンスにつないでゴニョゴニョする
  • したがってVagrantを操作しているマシンがネットワーク的にpublic subnetやprivate subnetと繋がってないといけない
  • ということで、VPCにVPN接続が必須(機器でもOpenVPNでも)

VPC使う場合はVagrantfileのawsのprovider部分は以下のようになる。

  # なぜか下記のprivate_ip_addressで設定する値と同じ値を設定する必要があります。
  web.ssh.host = "10.0.0.10" 
  # ユーザー名の問題も同じです。内部のSSHでの接続の実装の問題な気がする。
  web.ssh.username = ssh_username
  config.vm.provider :aws do |aws, override|
    aws.access_key_id = ENV['AWS_ACCESS_KEY_ID']
    aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
    aws.keypair_name = ENV['AWS_KEYPAIR_NAME']
    override.ssh.username = ssh_username
    override.ssh.private_key_path = ENV['AWS_PRIVATE_KEY_PATH']

    #---- VPC固有の設定 ----#
    # VPC内のローカルIPを指定します
    # Public Subnet内を指定した場合
    aws.private_ip_address = "10.0.0.10"
    # サブネットIDを指定します。
    aws.subnet_id = "subnet-xxxxxxxx"
    # セキュリティグループを設定します。
    # ここではIDのリストを指定します
    aws.security_groups = ["sg-xxxxxxxx"]
    #---- VPC固有の設定ここまで ----#

    # タグを指定します(任意)
    aws.tags = ["vpc", "dev"]
    # リージョンを設定します
    aws.region = "ap-northeast-1"
    # アベイラビリティゾーンを設定する必要がある場合
    aws.availability_zone = "ap-northeast-1c"
    # この記事の冒頭で紹介した方法で作ったAMIのIDを指定します
    # 当たり前ですが、起動したいリージョンにあるAMIでないといけません
    aws.ami = "ami-cd9212cc"
    # インスタンスタイプを設定します
    aws.instance_type = "t1.micro"
    # このインスタンスにログインするユーザー名
    aws.ssh_username = "root"
  end

ちょいとユースケースを考える

(1) 個人が好き勝手にVPC内にインスタンスをたちあげて開発に使うケース

IAMでアカウント作っておいて、VPCは共有しないと勿体無い。Web+DBみたいな構成でpublicとprivateのサブネットに分けるとNATインスタンスが必要で、さらにVPN接続用にOpenVPNとか用意するか機器ベースでつなげるかしないといけないので、開発者個別にVPC作るのは無理がある。となると、割り当てるローカルIPをうまくバッティングしないように開発チーム内で制御がいりそう。 あとは、このインスタンスをお客さんに見せたいってときはEIPを付け替えたり、ELBで向き先変えたり・・・めんどくさい。 このケースならVirtualBox使ってローカルでやったほうが楽かなという気がしなくもない。

(2)共用の開発機とかステージングとかで使うケース

こっちの方が妥当。ただしこのケースだと気軽にvagrant upしてインスタンス作ってvagrant destroyでインスタンス破棄して、という使い方があまり無さそうにも見える。まぁ起動したときに自動でChefとかChef Solo使ってプロビジョニングしてくれるので構成管理をしやすいというメリットはある。それからVPC内に作ったほうがセキュリティ的には良いのは間違いない。

ということでVagrant1.1でEC2にインスタンスは作れるけど、どういうユースケースでどのように使っていくかはちょっと考える必要があるように思います。

アジャイルコーチングやトレーニングを提供しています

株式会社アトラクタでは、アジャイル開発に取り組むチーム向けのコーチングや、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください。

詳細はこちら