読者です 読者をやめる 読者になる 読者になる

VagrantのプロバイダとしてEC2を利用する。

vagrant ec2 aws mac linux

本番で利用するようなEC2インスタンスを、そのままVagrantコマンドで透過的に操作できると便利と思い、

まあ、実際のところはVirtualBoxなどからVagrantboxを作成するのが面倒になったのだけど。。。

vagrant-aws-plugin を用いて、ローカルのvagrantから指定AWS VPC内のインスタンスをupしたり、

haltしたり操作できるようにしてみる。

前提

  • Vagrant1.7.8
  • MacOS siella 10.12
  • Java1.8.0_25
  • aws cliツールはインストール済み

まずはvagrant-aws-pluginをインストールする

$ vagrant plugin install vagrant-aws

Vagrantファイルは以下のようになる

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

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "dummy"
  config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
  config.ssh.pty = true
  config.vm.provider :aws do |aws, override|
    aws.access_key_id = ENV["AWS_ACCESS_KEY"]
    aws.secret_access_key = ENV["AWS_SECRET_KEY"]
    aws.ami = "{AMI-id}"
    aws.instance_type="t2.micro"
    aws.security_groups=["{セキュリティグループ}", "{セキュリティグループ}"]
    aws.keypair_name = "{キーペア名}"
    aws.region = "ap-northeast-1"
    aws.associate_public_ip = true
    aws.subnet_id = "{subnet id}"
    override.ssh.username= ENV['AWS_USER_NAME']
    override.ssh.private_key_path = '~/.ssh/id_rsa'
  end
  config.vm.synced_folder "../web", "/var/www/web", create: true, owner: "apache", group:"apache", mount_options: ["dmode=775", "fmode=664"]
  • config.vm.providerには:awsを指定

  • AWSキーなどべた書きにしたくないので以下のようにして、環境変数から取得する

macなら ~/.bashrcなんかに

export AWS_ACCESS_KEY=xxxxxxxxx
export AWS_SECRET_KEY=xxxxxxxxx
export AWS_USER_NAME=xxxxx

を記述する。

そして上記がととのったら

$ vagrant up

Bringing machine 'default' up with 'aws' provider...
==> default: Warning! The AWS provider doesn't support any of the Vagrant
==> default: high-level network configurations (`config.vm.network`). They
==> default: will be silently ignored.
==> default: Starting the instance...
==> default: Waiting for instance to become "ready"...
==> default: Waiting for SSH to become available...
==> default: Machine is booted and ready for use!
==> default: Rsyncing folder: xxxx
==> default: Rsyncing folder: xxxx
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

これでVirtualBoxなどローカルでイメージを作る場合と同じ要領で、カジュアルにEC2インスタンスVagrantとしてつかえるので便利です。

試しに、vagrant status

$ vagrant status
Current machine states:

default                   running (aws)

The EC2 instance is running. To stop this machine, you can run
`vagrant halt`. To destroy the machine, you can run `vagrant destroy`.

vagrant ssh

$ vagrant ssh
Last login: Fri Nov 25 07:34:49 2016 from x.x.x.x
[xxxxxx@ip-192-168-11-186 ~]$

Vagrantの共有フォルダ(ローカル<-->Vagrant)の扱い

EC2をVagrantのプロバイダとして利用する場合の、同期フォルダの扱いはrsyncとなる。

  • ローカル → rsync → EC2

気をつけなくてはいけないのは、EC2の変更はローカルに反映されない。

同期が発生するタイミングは、vagrant upを実行したときと手動で vagrant rsync を実行したタイミングとなる。

$ vagrant rsync

==> default: Rsyncing folder: ローカルフォルダ/ => リモートフォルダ/

ローカルに変更がある場合、自動でリモートに反映する

自動で反映するには、$ vagrant rsync-autoを実行する。

$ vagrant rsync-auto
==> default: Doing an initial rsync...
==> default: Rsyncing folder: ローカルフォルダ => リモートフォルダ
==> default: Watching: ローカルフォルダ
==> default: Watching: ローカルフォルダ

...

VagrantでEC2をプロバイダとすると、VirtualBoxなどをプロバイダとしてローカルに環境を構築する場合と同じ要領でインスタンスを操作できて、 非常に便利です。 例えば、本番で利用するamiを元にVagrant→EC2で開発をやれば、本番にしたとき環境の差異で動かない問題など解決するのではないでしょうか。

(・∀・)イイネ!!

MacでVagrant1.8.7にしたらvagrant box add / initがうまくできなくなった話

mac vagrant linux

MacでVagrant1.8.7にしたらvagrant box add / initがうまくできなくなった。 vagrantを開発・検証環境用に多用していて困るので、なんとか解決した話。

環境は。

Vagrant 1.8.7

ホストOS

OS X 10.11.6(El Capitan)

作成したvagrant boxを追加しようと。

vagrant box add package.box --name <box名>

といつものようにコマンドを打つと。

An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.

なるエラーがでるじゃないですか。

なんだと思いつつvagrantデバッグログを出力設定。

export VAGRANT_LOG=DEBUG

再度同じようにコマンドを入力すると。

DEBUG subprocess: Selecting on IO
DEBUG subprocess: stderr: dyld: Library not loaded: @rpath/libcurl.4.dylib
  Referenced from: /opt/vagrant/embedded/bin/curl
  Reason: Incompatible library version: curl requires version 9.0.0 or later, but libcurl.4.dylib provides version 7.0.0
DEBUG subprocess: Waiting for process to exit. Remaining to timeout: 32000
DEBUG subprocess: Exit status: 5
 WARN downloader: Downloader exit code: 5
ERROR downloader: Exit code: 5

...


/opt/vagrant/embedded/gems/gems/vagrant-1.8.7/plugins/commands/box/command/add.rb:78:in `execute'
/opt/vagrant/embedded/gems/gems/vagrant-1.8.7/plugins/commands/box/command/root.rb:61:in `execute'
/opt/vagrant/embedded/gems/gems/vagrant-1.8.7/lib/vagrant/cli.rb:42:in `execute'
/opt/vagrant/embedded/gems/gems/vagrant-1.8.7/lib/vagrant/environment.rb:308:in `cli'
/opt/vagrant/embedded/gems/gems/vagrant-1.8.7/bin/vagrant:189:in `<main>'
 INFO interface: error: An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.


An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.


 INFO interface: Machine: error-exit ["Vagrant::Errors::DownloaderError", "An error occurred while downloading the remote file. The error\nmessage, if any, is reproduced below. Please fix this error and try\nagain.\n\n"]

ということで、

incompatible library version: curl requires version 9.0.0 or later, but libcurl.4.dylib provides version 7.0.0

curlの互換性の問題っぽい。

github.com

↑を参考にして、回避策が議論されていたので。以下を実行。

sudo ln -nsf /usr/bin/curl /opt/vagrant/embedded/bin/curl

そして、再度 vagrant box add package.box --name <box名> を実行。

...

 INFO interface: success: Successfully added box 'centos67_lamp' (v0) for 'virtualbox'!
 INFO interface: success: ==> box: Successfully added box 'centos67_lamp' (v0) for 'virtualbox'!
==> box: Successfully added box 'centos67_lamp' (v0) for 'virtualbox'!
 INFO warden: Calling OUT action: #<Vagrant::Action::Builtin::BoxAdd:0x000001020752d0>
 INFO environment: Running hook: environment_unload
 INFO runner: Preparing hooks for middleware sequence...
 INFO runner: 1 hooks defined.
 INFO runner: Running action: environment_unload #<Vagrant::Action::Builder:0x0000010329f050>

ということで無事に、vagrant box addが成功しました。

めでたしめでたし(・∀・)イイネ!!

お久しぶりのrsyncメモ(中身で比較、ディレクトリを除外など)

linux

久しぶりにrsyncを使う場面があり、実際つかってみると、やらかしてしまうことが多いrsync。 よく使うオプションの記述方法をまとめてみました。

rsync / と の違い

間違えるとえらいことになるので注意。

  • → 自体も同期対象
  • / → 配下が同期対象

中身を比較して差分を同期する。

$ rsync -avzc <SRC DIR>/ <DST DIR>/

  • -a / あれこれオプションのまとめ。(--recursive --links --perms --times --group --owner --devices)
  • -v / 詳細表示
  • -c / チェックサムを常に使う(中身の比較となる)
  • -z / 圧縮転送

いきなりやるまえに、dry-runしましょう。

$ rsync -avnc <SRC DIR>/ <DST DIR>/

  • -n / dry-run

これやっておけば、更新対象のファイルを事前に確認できる。

除外ディレクトリ(--exclude)を指定する。.gitなど

$ rsync -avz --exclude '.git' --exclude '.project' <SRC DIR>/ <DST DIR>/

ハマりどころの1つ、除外ディレクトリが複数ある場合は、--excludeの指定を複数繰り返せばいいが、 指定方法が、 <SRC DIR> からの相対パス での指定となります。

除外ディレクトリをファイルで指定する。

$ rsync -avz --exclude '.git' --exclude '.project' <SRC DIR>/ <DST DIR>/

は、

ex.lstファイルが以下の中身とすると。

.git
.project

$ rsync -avz --exclude-from=ex.lst <SRC DIR>/ <DST DIR>/

とシンプルに記述できる。

<SRC DIR>にないファイルは削除する

$ rsync -avz --delete <SRC DIR>/ <DST DIR>/

  • --delete / ソースディレクトリにないファイルは削除する

と簡単によく使う備忘録でまとめてみました。

それでは(・o・)

CLIでCloudWatchをまとめて設定する

aws cloudwatch ec2 linux

CloudWatchをGUIから1つづつ設定すると面倒なので、 AWS CLIからまとめて設定するシェルを作成した。

アラートの通知にはSNSを利用しるので、通知先SNSトピックを予め作成して、ARNを取得します。

を設定します。

DISK使用率 / プロセス存在チェックはAWSの基本のメトリクスでは取得できないので、 カスタムメトリクスとして設定します

ysh.hateblo.jp

SERVER_SIGNATURE は識別該当のInstanceIdのインスタンスに対して、識別し易い名前をつけます。

よく使うので、ざっとまとめてみました。 それでは(´ω`)

Laravel5.1でのEloquentORMをCRUDでまとめてみた

php laravel

Laravel5.1たぶん5.2もそんなに変わらないと思うんですが、よく自分でも忘れてしまうことがあるのでまとめ。

Eloquentを利用するときは、そもそも利用する前提のモデルを設計することがこつ。

登録、参照、更新、削除のメモをする前に、前提のモデル。

massassignでの登録をしたいので、 対象のフィールドを $fillable で宣言しています。モデルの登録で記述している例だと、massassignを使っているので、 $fillableを宣言していなくて、はまることがよくある話なので注意。

class User extends Eloquent {
    
    protected $primaryKey = 'id';
    
    protected $fillable = [
        'name',
        'age'
    ];
}
  • CRUD
    • 登録
    • 参照
    • 更新
    • 削除

モデルの登録

単純に登録(Insert)

User::create([
    'name' => 'テスト太郎',
    'age' => 25
]);

モデルがPersistenceレイヤーにない場合登録、あれば更新する

nameが'テスト太郎'さんがいれば、name'テスト次郎', age'28'で更新。 いなければ登録。

User::updateOrCreate([ ['name' => 'テスト太郎'], ['name' => 'テスト次郎', 'age' => 28] ]);

モデルの参照

Persistenceレイヤーから取り出しModelにマッピングする。

  • その1
$user = User::find(1);

Persistence層に該当のレコードが存在しない場合は null が返却される。

  • その2
$user = User::findOrFail(1);

該当のレコードが存在しない場合は、Illuminate\Database\Eloquent\ModelNotFoundException が発生する。 JavaなんかのORMではこっちのパターンが多い気がする。 これはこれで、catchしてそのまま404返すとか使いどころ次第で便利だと思う。

尚、Modelのプロパティは

$user->nameで取り出せる。

モデルの更新

参照するモデルを作ってから更新するのが王道でしょうか。

//更新対象のモデルをオブジェクト化
$user = User::find(1);

$user->name = 'テスト次郎';
$user->save();

モデルの削除

//削除対象のモデルをオブジェクト化
$user = User::find(1);

$user->delete();

ざっと、よく使うものをメモ程度にまとめてみました。

それでは(´ω`)

SSHポートフォワーディングのまとめ

linux

普段何気なく利用しているSSHのポートフォワード。

よくコマンドを忘れてしますのでまとめてみました。 ひとえにポートフォワードといっても、

  • ローカルフォーワード
  • リモートフォワード

があります。

SSHローカルフォワード

<Local> port:8080 --- via SSH --------
              ------------>>> <Remote Host> ----->>> <Target Host> port:80

ローカルホストの特定ポートへの情報をターゲットホストの特定のポートへ転送する技術です。 ローカルホストの特定のポートを、SSHトンネルを介してリモートホストへ接続して、接続先のリモートホストから接続が可能なターゲットの特定のポートにマッピング。 上の例だと、ローカルホストのポート8080を、ターゲットホストのポート80にマッピングしています。

ローカルフォワーディングのコマンド

$ ssh -N -f -L <Local Port>:<Target Host>:<Target Port> <Remote Host>

上のコマンドで-N、-fオプションはそれぞれ。

  • -N : コマンドを発行しない
  • -f : バッググランド実行

SSHリモートフォワード

<Local> port:9000 <<<--- via SSH --------
              ------------- <Remote Host> <<<----- <Target Host> port:9001

リモートホストの特定ポートへの情報をローカルホストの特定のポートへ転送する技術。 ローカルホストのポートとターゲットホストのポートのマッピングはローカルホストと同じ、コマンドオプションが違うだけ。

SSHリモートフォワーディングのコマンド

$ ssh -N -f -R <Target port>:<Target Host>:<Local Port> <Remote Host>

xdebugなど、ターゲットホストからローカルホストへ接続する必要があるものなどで役に立ちます。

簡単にまとめて、それでは。

CloudWatchのモニタリングスクリプトでハマった話

aws cloudwatch

CloudWatchのモニタリングスクリプト設定してあるのに、該当のインスタンスIDのデータがCloudWatchにputDataされていなくてハマった話。

ハマったインスタンスは、すでにクラウドウォッチが設定してあるAMIから立ち上げたインスタンスで、 AMIに配置されている、CloudWatchにputDataするスクリプト自体は、インスタンスIDに依存していないから、 そのまま問題なく正しいインスタンスIDでputDataされるはず。             

だが、実際はAMI作成ものインスタンスIDでメトリクスが登録されてしまう。。

インスタンスIDを確認

# curl -s http://169.254.169.254/latest/meta-data/instance-id
i-xxxxxxxx

で紹介したスクリプトを利用している。

# /usr/bin/check_cw_stats

MemoryUtilization: 12.86724312544 (Percent)
DiskSpaceUtilization [/]: 6.65698036312681 (Percent)
Using AWS credentials file </etc/aws/awscreds.conf>
Endpoint: https://monitoring.ap-northeast-1.amazonaws.com
Payload: {"MetricData":[{"Timestamp":1460957603,"Dimensions":[{"Value":"i-yyyyyyyy","Name":"InstanceId"}],"Value":12.86724312544,"Unit":"Percent","MetricName":"MemoryUtilization"},{"Timestamp":1460957603,"Dimensions":[{"Value":"/dev/xvda1","Name":"Filesystem"},{"Value":"i-yyyyyyyy","Name":"InstanceId"},{"Value":"/","Name":"MountPath"}],"Value":6.65698036312681,"Unit":"Percent","MetricName":"DiskSpaceUtilization"}],"Namespace":"System/Linux","__type":"com.amazonaws.cloudwatch.v2010_08_01#PutMetricDataInput"}
Received HTTP status 200 on attempt 1


Successfully reported metrics to CloudWatch. Reference Id: 1038cd0d-0527-11e6-b8f3-fbed7fa60205

あきらかに、PayloadのインスタンスIDが実際にインスタンスメタデータで取得したInstanceIdと異なっている。 ちなみにi-yyyyyyyyのインスタンスIDはAMIのインスタンスIDになる。

AWSから提供されているCloudWatch登録のスクリプトをおってみる。

  • /usr/local/src/CloudWatch-1.0.20.0/aws-scripts-mon/mon-put-instance-data.pl
338: my $instance_id = CloudWatchClient::get_instance_id();
  • /usr/local/src/CloudWatch-1.0.20/aws-script-mon
sub get_instance_id
...
{
  if (!$instance_id) {
    $instance_id = get_meta_data('/instance-id', USE_CACHE);
  }
  return $instance_id;
}
...

USE_CACHEだと!!というわけで、キャッシュをするようです。

インスタンスIDを取得しているようなメソッドが。

よくよくAmazon EC2 Linux インスタンスのメモリとディスクのメトリックスのモニタリングを読んでみると以下のようにキャシュに関する文章が。

スクリプトパッケージに含まれる CloudWatchClient.pm モジュールは、インスタンスメタデータをローカルでキャッシュします。スクリプトを実行しているインスタンスから AMI を作成すると、キャッシュ TTL(デフォルト: 6 時間、Auto Scaling グループでは 24 時間)以内にこの AMI から起動したすべてのインスタンスは、元のインスタンスの ID を使用してメトリックスを出力します。キャッシュ TTL 期間が経過した後は、スクリプトは新しいデータを取得し、スクリプトは現在のインスタンスの ID を使用します。これをすぐに修正するには、$ rm /var/tmp/aws-mon/instance-id を使用してキャッシュされたデータを削除します

気を取り直し、キャッシュを削除

rm -rf /var/tmp/aws-mon/*

再度メトリクススクリプトの実行。

# /usr/bin/check_cw_stats

MemoryUtilization: 13.8519340228981 (Percent)
DiskSpaceUtilization [/]: 6.65734560054434 (Percent)
Using AWS credentials file </etc/aws/awscreds.conf>
Endpoint: https://monitoring.ap-northeast-1.amazonaws.com
Payload: {"MetricData":[{"Timestamp":1460958577,"Dimensions":[{"Value":"i-xxxxxxxx","Name":"InstanceId"}],"Value":13.8519340228981,"Unit":"Percent","MetricName":"MemoryUtilization"},{"Timestamp":1460958577,"Dimensions":[{"Value":"/dev/xvda1","Name":"Filesystem"},{"Value":"i-xxxxxxxx","Name":"InstanceId"},{"Value":"/","Name":"MountPath"}],"Value":6.65734560054434,"Unit":"Percent","MetricName":"DiskSpaceUtilization"}],"Namespace":"System/Linux","__type":"com.amazonaws.cloudwatch.v2010_08_01#PutMetricDataInput"}
Received HTTP status 200 on attempt 1

となり、正しいインスタンスIDでputDataができました。

ということで、AMIなどから新しいインスタンス、オートスケールなどを実行する際には、

ユーザーデータで

#!/bin/bash
rm -rf /var/tmp/aws-mon/*

と設定しておけば安心です。

AMIからのインスタンス作成し、CloudWatchモニタリングスクリプトではまってしまった お話でした。

_| ̄|○