AWS
vagrant
Ansible
vagrant-aws

vagrantではじめるクラウド開発環境(AWS編)

More than 1 year has passed since last update.

vagrantとは

aws01

敢えて説明する必要がないくらい有名なツールですが一応、Wikipediaではこのように説明されています。

Vagrant is free and open-source software for creating and configuring virtual development environments. It can be considered a wrapper around virtualization software such as VirtualBox and configuration management software such as Chef, Salt and Puppet.

vagrantVirtualBoxなど、仮想環境を手軽に扱えるようにするためのrubyで書かれたラッパーソフトですが、プラグインで拡張することでAWSDigitalOceanなどクラウドのインスタンスまで扱えるようになります。

背景

社内でいまいちクラウド活用が進まないなー、なんでかなーと悩んでいました。使い始めてしまえば便利過ぎるAWSですが、確かに慣れるまではいまいち使い方がピンとこなかったり、マネジメントコンソールのUIが悪いせいで尻込みしてしまったり...

そこで vagrant です。細かいことは気にせずに、ほしいときに瞬時にテスト用のEC2インスタンスを使える環境を整えれば、社内でみんなもっとカジュアルに使い始めるんぢゃないかなーと(安直な発想)

まー、ネタとしてはもう旬な時期は終わってますけど。。

したごしらえ

vagrantからはじめてAWSを使うために最低限、必要なことを説明します。

以降の設定は説明のためだいぶ簡略化してあるので、本格的にvagrantからAWSを使う際は所属している会社や部署の運用ポリシーやセキュリティーポリシー等に従うようにしてください。

AWSアカウントの作成

AWSを使うわけですから、まずはAWSを契約しなければはじまりません。
AWSのアカウントがまだなければこちらから作成します(クレジットカードの登録が必要です)

AWSのはじめ方に関する情報はネットに溢れているので、詳しい手順は割愛します。

アカウントを作成したらAWSマネジメントコンソールにログインします。

リージョンの選択

AWSのリージョンは特別な理由がなければTokyoリージョンを選択します。

以降は Tokyo リージョンで作業する前提で進めます。

vagrantユーザーの作成

IAMの管理画面で vagrant ユーザーを作成します。
その際、以下の情報を確実に控えておきます(特にSecretAccessKeyは後から確認できません)

  • AccessKeyId
  • SecretAccessKey

vagrantユーザーを作成したら vagrant という権限グループを作成して所属させます。
SelectPolicyTemplateの選択では PowerUserAccess あたりを選んでおけばいいです。

以降は vagrant ユーザーの認証情報を使う前提で進めます。

SecurityGroupの作成

EC2の管理画面からvagrantで利用する専用のSecurityGroupを作成します。

key value
GroupName vagrant
Description sg-vagrant
VPC デフォルトVPC(* が付いてるやつ)

Inboundはこんな感じで。

Type Protocol PortRange Source
SSH TCP 22 0.0.0.0/0
HTTP TCP 80 0.0.0.0/0
HTTPS TCP 443 0.0.0.0/0

以降は vagrant というSecurityGroupを使う前提で進めます。

sshキーペアの生成

最後に、EC2の管理画面からvagrantで使う専用のsshキーペアを生成します。
(もちろん、自分で生成したsshキーペアをアップロードして使ってもokです)

KeyPairNameは vagrant と指定します。

生成すると vagrant.pem という名前の秘密鍵をダウンロードできるので、
/Users/ユーザー名/.ssh/vagrant.pemとして保存します。

保存したら、以下のようにPermissionを設定します。

chmod 700 ~/.ssh ;
chmod 400 ~/.ssh/vagrant.pem ;

以降は、この vagrant.pem を使う前提で進めます。

導入

したごしらえが済んだらvagrantを使う準備をしましょう。

環境

作業環境は MacOSX 10.9.2 Mavericks を使う前提で進めます。

vagrantのインストール

公式サイトからインストーラーをダウンロードしてインストールします。
特に躓くことはないと思います。

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

vagrantからAWSを操作するにはvagrant-awsというプラグインを利用します。
設定ファイルを個別に管理できるdotenvというプラグインもインストールしておきましょう。

以下のコマンドを実行します。

vagrant plugin install dotenv ;
vagrant plugin install vagrant-aws ;
vagrant plugin list ;

設定

以降は、以下のディレクトリで作業する前提で進めます。

/Users/ユーザー名/vagrant-test

Vagrantfile

vagrantからAWSを操作するための設定ファイルを作ります。
下記のコードを Vagrantfile というファイル名で保存します。

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

Dotenv.load

# change default provider to digital_ocean
ENV['VAGRANT_DEFAULT_PROVIDER'] = "aws"

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.provider :aws do |provider, override|
    override.vm.hostname          = "vagrant-test"
    override.vm.box_url           = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
    override.vm.box               = "aws"
    override.ssh.username         = ENV['AWS_SSH_USERNAME']
    override.ssh.private_key_path = ENV['AWS_SSH_KEY']
    override.ssh.pty              = false

    provider.access_key_id        = ENV['AWS_ACCESS_KEY_ID']
    provider.secret_access_key    = ENV['AWS_SECRET_ACCESS_KEY']
    provider.keypair_name         = ENV['AWS_KEYPAIR_NAME']
    provider.region               = "ap-northeast-1"  # Tokyo
    provider.availability_zone    = "ap-northeast-1c" # Tokyo
    provider.ami                  = "ami-c9562fc8"    # Tokyo Amazon Linux AMI 2014.03 (64-bit)
    provider.instance_type        = "t1.micro"
    provider.instance_ready_timeout = 120
    provider.terminate_on_shutdown  = false

    provider.security_groups      = [
                                      ENV['AWS_SECURITY_GROUP'], # sg-vagrant
                                    ]

    provider.tags                 = {
                                      "Name"        => "vagrant-test",
                                      "Description" => "Boot from vagrant-aws",
                                    }

    provider.block_device_mapping = [{
                                      "DeviceName"              => "/dev/sda1",
                                      "VirtualName"             => "VagrantDisk",
                                      "Ebs.VolumeSize"          => "8",
                                      "Ebs.DeleteOnTermination" => true,
                                      "Ebs.VolumeType"          => "standard",
                                     #"Ebs.VolumeType"          => "io1", # only if you choose PIOPS
                                     #"Ebs.Iops"                => 1000,  # only if you choose io1
                                    }]

    # enable these properties only if you plan not to use Default VPC
    #provider.subnet_id            = ENV['AWS_SUBNET_ID']
    #provider.private_ip_address   = "172.31.16.10"
    #provider.elastic_ip           = true

    # enable sudo without tty
    # NOTE: setting [ ssh.pty = true ] causes file provisioner fail
    provider.user_data = <<-USER_DATA
#!/bin/sh
echo "Defaults    !requiretty" > /etc/sudoers.d/vagrant-init
chmod 440 /etc/sudoers.d/vagrant-init
    USER_DATA

    # disable synced_folder:
    override.vm.synced_folder "./", "/vagrant", disabled: true

    # provision
    # Do whatever you wanna do !!
  end

end

.env

続いて、AWSの認証情報を .env というファイルに記述します。
.envはVagrantfileと 同じ場所 に保存します。

以下のテンプレートを編集して使います。

AWS_SSH_USERNAME="ec2-user"
AWS_SSH_KEY=${HOME}/.ssh/vagrant.pem
AWS_ACCESS_KEY_ID="置き換える"
AWS_SECRET_ACCESS_KEY="置き換える"
AWS_KEYPAIR_NAME="vagrant"
AWS_SECURITY_GROUP="vagrant"
#AWS_SUBNET_ID="置き換える"

以上で設定は終わりです。

使い方

では早速、使ってみましょう。

Vagrantfile のある場所に移動して以下のコマンドを実行します。

vagrant up --provider=aws

VagrantfileでVAGRANT_DEFAULT_PROVIDER=awsを設定しているので--provider=awsは実は省略可能ですが、ひとつのVagrantfileで複数プロバイダーに対応させることもできるので、使用するプロバイダーは明示する癖をつけておきましょう。

初回はEC2インスタンスの起動に10分ほどかかるとおもいます。
起動できたか現在のステータスを確認してみましょう。

vagrant status

無事に起動できたらsshでログインしてみます。

vagrant ssh

拍子抜けするほど簡単に使えてしまいましたね?

以下のコマンドを実行しておけばvagrant経由でなくともインスタンスにsshできるようになります。
いちいちPublicIPを調べなくていいので非常に便利です。

# sshの設定をvagrant-testという名前で書き出す
vagrant ssh-config --host vagrant-test >> ~/.ssh/config

# vagrant-testと名前を付けたインスタンスにsshでログインする
ssh vagrant-test

使い終わったインスタンスは 忘れずに 停止または削除しましょう。

# シャットダウン
vagrant halt

# インスタンス削除
vagrant destroy

起動したまま放置プレイすると、インスタンスのタイプによっては死ねます(−人−)チーン
この点だけは、 MAX 注意しましょう!

プロビジョニングしてみる

vagrantからEC2のインスタンスを起動してsshできるようになりました、それも極短時間で!
便利ですね、便利ですがこれだけだと ふぅーん って感じですね。。

それだと残念なので、vagrant upでプロビジョニングも同時に実行するようにしてみましょう。
shell provisioneransibleを組み合わせたサンプルコードを用意しました。

git clone してからブランチをcheckoutします。

git clone https://github.com/msykiino/codebox.git vagrant-casual-aws ;
cd vagrant-casual-aws ;
git checkout vagrant-casual-aws ;

.env のAWSの認証情報を書き換えます。
これまでの行程をすべてやっていれば、先で作った .env をそのままコピーして使えます。

vim ./.env

認証情報を書き換えたらいざ、実行。

vagrant up --provider=aws --provision

EC2のインスタンスが作成され、nginxとJenkins、munin、GoAccessがインストールされすぐに利用できる状態になっているはずです(t1.microなのでコマンド実行から構築が終わるまで7分少々かかるかもしれません)

vagrant up が終わったらサーバーのIPアドレスを確認しましょう。

vagrant ssh-config # HostNameの項目のIPアドレスをコピーしてブラウザでアクセス

確認したらブラウザでアクセスします。

# Jenkins
http://IPアドレス/

JenkinsはJettyで起動します。

aws02

# munin
http://IPアドレス/munin/

aws03

Jenkinsおぢさんとmuninの画面が出てきましたか?

muninが初回起動時に正常に起動しないことがあるようなので、50xページが表示されるようならプロビジョニングを再実行することで起動すると思います(これは1分ほどで終わります)

vagrant provision

ブラウザで表示できたらmuninにアクセスしつつGoAccessでリアルタイム解析してみましょう。
vagrant sshしてサーバーにログインしたら以下のコマンドを実行します。

goaccess -p /etc/goaccess/ltsv.nginx -f /var/log/nginx/munin_access.log

このように、vagrantとAWS、構成管理ツールを組み合わせることで必要な時に、必要な環境を、極短時間で構築することができます。便利すぎて困りますねww

このサンプルはあくまでもサンプルです。
本番環境ではアクセス元の制限や通信の暗号化など セキュリティー対策 をしっかり実施してください。

余談ですが

2014/04/17 現在、先日のSSLのHeartbleedバグに起因して、curlやlibcurlのバージョン7.36.0-2.44ではhttpsへのアクセスでIllegal instructionというエラーが発生して正常に処理できません。同じ症状がgitコマンドでも起きるため、https経由でgit cloneするとリポジトリがクローンできないため、サンプルコードでは暫定対応として意図的にcurlをダウングレードしています。

2014/04/18 現在、curlのパッケージが修正されたのでコードからダウングレード処理を削除しました。

詳しくはこちらのフォーラムを参照してみてください。

落とし穴

設定しているときに幾つか嵌ったポイントがあるので参考まで。

DefaultVPC

複数メンバーいるプロジェクトでは、AWSに各自で独立したインスタンスを作ることが一般的だと思いますが、通常インスタンス毎にグローバルIPが必要になると思います(VPNな環境での運用は除く)。当然、インスタンス毎にElasticIPを割り当てるようなことはしたくないのでPublicIP(PublicDNS)を利用することになるのですがいまの vagrant-aws プラグインでは PublicIPはDefaultVPCに対してしか割り当てができません

利用しているリージョンのVPCにDefaultVPCがあるかどうかは、VPC管理画面のYour VPCsの一覧のDefault VPCの項目で確認できます。この項目が Yes となっていればそのVPCがDefaulVPCです。

このDefaultVPCは消すこともできますが、 消してしまうともう自分で作ることはできません ので、もし消してしまった場合はこちらのページにあるように、AWSのサポートに連絡してDefaultVPCの復元依頼をするしか手がありません。

ぼくがテストしていたときは、既にTokyoリージョンのDefaultVPCが消されてしまっていたため、どうやってもPublicIPが取得できずに大嵌りしました;;

requiretty

AWSのAmazonLinuxの初期状態では、/etc/sudoersが以下のような設定になっています。

#
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear.
#         You have to run "ssh -t hostname sudo <cmd>".
#
Defaults    requiretty

これだとvagrantでssh越しにsudoが実行できないため、最初は以下の設定で対応していました。

override.ssh.pty = true

これは一見、問題なく動作するように見えましたし事実、ssh越しにsudoも実行できました。
しかし、設定を進めるうちにこの設定ではshell provisionerがうまく動かないことが分かりました。

具体的には、以下のコードは期待通りの動きをしません。

override.vm.provision :file, source: "./provision/files/.gitconfig", destination: "/home/#{ENV['AWS_SSH_USERNAME']}/.gitconfig"

上記のコードは実際エラーにはなりませんが、アップロードしたファイルがlogoutというファイルにリダイレクトされてしまいます。内部の処理までは追っていないのであれですが、結論としてはuser_dataを設定することで回避できます。

    provider.user_data = <<-USER_DATA
#!/bin/sh
echo "Defaults    !requiretty" > /etc/sudoers.d/vagrant-init
chmod 440 /etc/sudoers.d/vagrant-init
    USER_DATA

ちなみに、ネットでは /etc/sudoers を直接修正しているケースが多いようですが、sudoersの設定は /etc/sudoers.d/ 配下の設定ファイルで上書きできるので /etc/sudoers.d/vagrant-init!requiretty を設定しています。

余談ですが、AWSでもec2-userにパスワードなしのsudo権限を与える設定が /etc/sudoers.d/cloud-init で設定されています。興味のあるヒトは覗いてみると良いと思います。

まとめ

さて、vagrantからAWSを利用するのがいかに簡単で便利か伝わったでしょうか。
初回の面倒なAWSの設定さえ終えてしまえばいつでも手軽にAWSを利用できるようになります。

プロビジョニングの設定を共通化しておけば、環境差異のない開発環境も即座に用意できます!
もう「ぼくの環境では動くんですけどー」とか言わせません!

プロジェクトの引き継ぎや、プロジェクトに新しいメンバーが加わったときなど、
以下 4つの設定を共有するだけでアプリケーションが動作する環境をすぐにでも共有できます。

  • vagrant.pem
  • Vagrantfile
  • .env
  • プロビジョニング設定

もう使わない理由が見当たりませんよね?

これでうちの会社もクラウド利用がもっと活発になってくれればいいんですけどね...