• Capistrano3のタスク内でトップレベルのタスク名を取得する

    Capistrano3でタスク書いていて、タスク内でcapコマンドとして実行されたトップレベルのタスク名を引いて、処理を分岐させる必要が生じて、そのトップレベルのタスク名取得に苦労したので、ここに備忘録化しておく。

    基本的にCapistrano3でタスク内で自分のタスク名を取得したい場合、次のようにブロックの引数として引ける。

    desc "main task"
    task :main_task => :sub_task do |task|
        run_locally do
            info ": This task name: #{task}"
            info ": Running main deploy task!"
        end
    end
    
    desc "sub task"
    task :sub_task do |task|
        run_locally do
            current_task = task.name_with_args.split(':').last
            info ": This task name: #{current_task}"
        end
    end
    

    上記タスクを実行してみると、こうなる。

    $ cap test deploy:main_task
    INFO: This task name: sub_task
    INFO: This task name: deploy:main_task
    INFO: Running main deploy task!
    

    ただ、デプロイの共通設定を読み込むとかのサブタスクを作って、全てのメインタスクの前にそのサブタスクを実行するようなタスクチェーンを構築した場合に、特定のメインタスクが実行された時のみ、サブタスクの一部処理を分岐させたいとかの要望が発生すると、サブタスク内でメインタスクのタスク名を取得する必要が出てくる。つまりは、capコマンドで実行されるトップレベルタスク(例で言うところのメインタスク)を取得したいのだが、これがなかなかTIPSが見つからなくて実現するのに苦労した。 Capistranoのタスク処理は、コアで使われているRakeクラスで実現されているので、そのRakeクラスのApplicationメソッドから取得する必要があった。

    ...
  • 素のRubyでMySQLクエリの結果を取得する

    Ruby+MySQLの処理をする時、たいていはMySQL操作系のライブラリでruby-mysqlmysql2とかを使うケースが多いのだろうが、それらのライブラリなしの素のRuby環境でMySQLのクエリ操作を行う必要があったので、その際に書いたソースを汎用化してまとめてみた。まぁ…ほとんどニーズはないかもしれんが、一応TIPSとして共有化しておこうかと。

    mysql_query.rb

    db = { :db_config => "~/.user.cnf", :db_name => "database_name" }
    
    query = "SELECT * FROM db_table_name WHERE primary_key_id = 1;"
    res = `/usr/bin/mysql --defaults-extra-file=#{db.fetch(:db_config)} -D #{db.fetch(:db_name)} -e \"#{query}\" `
    
    if res.empty? then
        p "data is none."
    else
        lines = res.split("\n")
        columns = lines.first.split("\t")
        results = []
        lines.each_with_index { |line, i| 
            if i > 0 then
                tmp_hash = {}
                values = line.split("\t")
                values.each_index { |j| 
                    if values[j].match(/^\d+$/) then
                        tmp_hash[columns[j]] = values[j].to_i
                    else
                        tmp_hash[columns[j]] = values[j].to_s
                    end
                }
                results << tmp_hash
            end
        }
        p results
    end
    

    やってることは、コマンドラインでMySQLのクエリを発行しているのと同義で、標準出力としてのMySQLクエリの結果をRuby側で取り込んでハッシュにパースしているだけです。 なお、MySQL5.6からコマンドラインにパスワードを付与してmysqlコマンドを実行すると警告が出てしまうので、MySQLへの接続は.user.cnfのファイルに書いて、そっちからインポートするようにしている。

    ...
  • authorクエリを利用したWordPressのユーザー名漏洩を防ぐ方法

    DEVLABはWordPressで運用しているのだが、そのWordPressについて気になる記事を見つけた。 WordPressサイトのホームURLに「?author=x」のGETクエリをつけることで、サイト内のユーザー名がばれてしまう というものだ(※ 詳しくはMT SystemsさんのWordPress TIPを参照)。?author=xのxは数値で、WordPressのユーザーID(ユーザーテーブルのプライマリキー)となる。つまりxに1を指定すると、WordPressのルート管理ユーザーになるわけだ。 さっそく、DEVLABでも試してみたところ・・・ https://dev.blog.colorkrew.com?author=1がみごとにパーマリンク設定で指定されているリライトルールに沿ってリライトされ、https://blog.colorkrew.com/author/<ルート管理ユーザー名>/にリダイレクトされてしまった。このままだと、管理ユーザーのユーザー名に対してパスワードの総当りをされて、もしログイン成功とかされちゃうと、サイトがクラッキングされてしまうじゃないですか! ただ、DEVLABの場合、一般的なWordPressのログイン画面wp-login.phpは無効化してあって、ログイン方法を探すのが大変なので、一応は安全ではある。しかし、ユーザー名が漏洩してしまう脆弱性があるのは防止しないといけないので、対策を考えてみた次第。

    MT Systemsさんのサイトで紹介されているように、著作者アーカイブページのテンプレートauthor.phpによってリダイレクトしてしまう方法ではHTTPのレスポンスヘッダにリダイレクトのlocationとしてユーザー名が出力されてしまうため、たとえ.htaccessにrewriteルールを追加したとしても防止できないのが厄介だ。 MT Systemsさんのサイトでは、最終的にユーザーデータのuser_nicenameを書き換えて、ログインアカウントであるuser_loginと異なる値にしてしまう対処策が掲載されていたんだが、管理パネルから直接編集できない項目でもあって、なかなかに難儀である。

    もうちょっと簡単に対策できないものか…。 ──と、言うわけで、対策方法を自作してみた。

    // prevent the leakage of user name by author query on WordPress.
    function knockout_author_query() {
        // disable author rewrite rule
        global $wp_rewrite;
        $wp_rewrite->flush_rules();
        $wp_rewrite->author_base = '';
        $wp_rewrite->author_structure = '/';
        // for author query request
        if (isset($_REQUEST['author']) && !empty($_REQUEST['author'])) {
            $user_info = get_userdata(intval($_REQUEST['author']));
            if ($user_info && array_key_exists('administrator', $user_info->caps) && in_array('administrator', $user_info->roles)) {
                wp_redirect(home_url());
                exit;
            } else {
                // enable author rewrite rule
                $wp_rewrite->author_base = 'author';
                $wp_rewrite->author_structure = '/author/%author%/';
            }
        }
    }
    add_action('init', 'knockout_author_query');
    

    上記のソースをテーマのfunction.phpなどに追加することで即時有効になります。

    ...
  • Capistrano3でタスクが二重起動してしまう時の対処法

    Capistrano3でステージ環境を変えてタスクを実行した時に、タスクが二重起動するという症状に陥った。二重でタスクが実行されるので、ラウンチするAWSインスタンスを“2つ”と設定していても“4つ”起つし、MySQLに発行するクエリも2倍になって、INSERTするレコードが重複して挿入される。一時的にファイルに書き出していた設定なども二重起動している後続タスクによって上書きされてしまい、もうデプロイはしっちゃかめっちゃかな状態だった。 解決できたので、その時の対処法を備忘録として残しておく。

    原因はinvoke設定でデフォルトのデプロイ環境を指定していたためだった。

    はじめ、試験環境でデプロイタスクの開発を行っていた時、Capfileに下記のような設定を書いていた。

    Rake::Task[:develop].invoke
    invoke :develop
    

    これを書いておくと、デフォルトデプロイ環境がdevelopに固定化されるので、デプロイコマンドを実行するときに本来ならデプロイ環境(ステージ名)を指定して、

    $ cap develop deploy:task_name
    

    のようにするところを、

    $ cap deploy:task_name
    

    と、デプロイ環境を省略できてちょっとだけ楽だったのだが、これがデフォルトデプロイ環境以外にデプロイを行う時に悪影響を及ぼしたのだ。

    今回、試験環境でデプロイが上手くいったので、次に本番環境でデプロイを行うことになり、デプロイ環境(ステージ名)をproductionで実行することになった。デプロイ内容は試験環境とまったく同一だったため、デプロイタスク設定の差分はなく、ロール設定やSSH接続設定も同じだったため、config/deploy/develop.rbをコピーしてconfig/deploy/production.rbを作成していた。 この状態で、下記のようにデプロイ環境を指定してデプロイを実行した次第。

    $ cap production deploy:task_name
    

    これだと、省略されているデフォルトデプロイ環境へのデプロイも同時に起動してしまうわけです。実際には、

    $ cap develop production deploy:task_name
    

    と環境を二つ指定してデプロイを実行しているのと同じことになっているわけです。

    この状態を解消するには、前述のinvoke設定を削除するだけです。 というか、複数環境に対してデプロイが発生する時は混乱の元になるんで、invoke設定とかはしない方がいいですね。

    いやはや、何気に解決するまで数時間ハマってました。今後は注意しないと。

    ...
  • Capistrano3でEC2インスタンス新規作成から初期設定までのデプロイ(まとめ)

    ここまでAWSのEC2インスタンスを新規作成して、そのインスタンスに対しての初期設定までを、Capistrano3でタスク化することをやって来ました。難儀したものの、ようやくEC2インスタンスの準備が出来て、あとはミドルウェアやアプリケーションをインストールするだけ──というところまでたどり着きました。そこで今回は、これまでのデプロイの流れを一度総括してまとめてみようかと思います。

    まず、Capistrano3を稼動させるデプロイサーバの準備から。公式のAmazon Linux環境などのEC2インスタンスをAWSマネージメントコンソール等からラウンチして、ログインしたら、Capistrano3をインストールします1

    # yum update -y
    # yum groupinstall -y "Development Tools"
    # openssl version
    OpenSSL 1.0.1h-fips 5 Jun 2014
    # openssl version -d
    OPENSSLDIR: "/etc/pki/tls"
    # curl -L https://get.rvm.io | bash -s stable
    # source /etc/profile.d/rvm.sh
    # rvm list
    (※ rvm rubies と表示されればインストール完了)
    # ruby -v
    ruby 2.0.0p451 (2014-02-24 revision 45167) [x86_64-linux]
    # rvm install 2.0.0 -- --with-openssl-dir=/etc/pki/tls
    # ruby -v
    ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-linux]
    # gem install rails --no-ri --no-rdoc
    # gem install capistrano
    # gem install capistrano_colors
    # gem install capistrano-ext
    # gem install railsless-deploy
    # cap --v
     (※ cap aborted! ~と表示されればインストール完了)
    # gem install aws-sdk
    

    デプロイサーバにてデプロイプロジェクトを実行するユーザを作成しておきます。デプロイタスクの内容にもよるのですが、対象のユーザはsudo権限を持っていた方が都合が良いかと思います。ユーザを作成したら、そのユーザで再ログインして、ホームディレクトリで、Capistrano3用のプロジェクトを作成します。

    ...
  • Capistranoのタスクを新EC2インスタンスが完全起動するまでsleepさせる

    Capistranoで新規作成したEC2インスタンスが完全に起動し切っていない状態で、そのインスタンスに対してSSHアクセスするタスクを実行すると、どこかしらでエラーになってタスクが完了しません。そこで、デプロイ対象となるEC2インスタンスの起動状態をチェックして、完全に起動していない状態の場合sleepして起動を待つようなタスクを作りました。

    AWSのEC2インスタンスには3つのステータス情報があり、この全てのステータスを確認しないと、インスタンスの完全起動状態とは言えないので、注意が必要でした(下図参照)。

    AWSマネージメントコンソールで確認できるEC2インスタンスステータス

    インスタンスが起動しているかどうかの確認は、AWSマネージメントコンソールでいうところの「Instance State」がrunningであるかを判定すればOKなのですが、実際にインスタンスにSSH接続ができるかどうかの確認は「Status Checks」欄に「2/2 checks」とあるように2つのステータス(INSTANCESTATUSとSYSTEMSTATUSのreachability)が共にpassedであるかまでを確認する必要があるのです。通常、インスタンスが起動すると「Instance State」は数秒から十数秒程度でrunningになるのですが、「Status Checks」は数分程度initializingで初期化処理を行っています。このステータスが共にpassedにならないとSSHアクセスでコケます。

    今まで私が使っていたcheckタスクだと、「Instance State」のステータス1つしか確認していなかったので、後続タスクがSSHアクセスで中断したりしていました。それを回避するためのタスクが今回のcheckタスクになります。

    task :check do
      run_locally do
        created_instances_list = 'CREATED_INSTANCES'
    
        def check_instance_status(instance_ids=[]) 
          ec2 = AWS::EC2.new
          AWS.memoize do
            ec2info = ec2.client.describe_instance_status({'instance_ids' => instance_ids})
            sys_status = ec2info.instance_status_set.map { |i| i.system_status.details[0].status }
            ins_status = ec2info.instance_status_set.map { |i| i.instance_status.details[0].status }
            status = sys_status + ins_status
            return status.include?('initializing') ? false : true
          end
        end
    
        ec2 = AWS::EC2.new
          begin
            if test "[ -f ~/#{created_instances_list} ]"
              created_instances = capture("cd ~; cat #{created_instances_list}").chomp
              ci = created_instances.gsub(/(\[|\s|\])/, '').split(',')
              target_instances = ec2.instances.select { |i| i.exists? && i.status == :running && ci.include?(i.id) }.map(&:id)
              raise "Is not still created all instances" if target_instances.length < fetch(:instance_count)
              if target_instances.length == fetch(:instance_count) then
                # 全インスタンス起動(Instance Stateがrunning)
                chk_retry = 0
    
                while !check_instance_status(target_instances) do
                  # インスタンスステータスが全てOKでない場合は15秒待つ(※20回までリトライする)
                  info "In preparation of the instance: Status check " + (chk_retry > 0 ? "(retry #{chk_retry + 1} times)" : "")
                  sleep 15
                  if check_instance_status(target_instances) then
                    break
                  end
                  chk_retry += 1
                  raise "Instance is not still ready. Please run the task again after waiting for a while." if chk_retry >= 20
                end
    
                # ここからが全インスタンス完全起動後の処理(初回ssh接続設定)
                target_instance_private_ips = ec2.instances.select { |i| i.exists? && i.status == :running && ci.include?(i.id) }.map(&:private_ip_address)
                pkfn = fetch(:private_key_file)
                target_instance_private_ips.each { |var| 
                  server var, user: 'ec2-user', roles: %w{web app}, ssh_options: { keys: %W(/home/deploy-user/#{pkfn}), forward_agent: true }
                }
              end
            end
          rescue => e
            info e
            exit
          end
      end
    
      # 後続タスクのサンプル(SSHしてhostnameを表示する)
      on roles(:web) do
        info capture "hostname"
      end
    end
    

    上記設定から、後続タスクのサンプル部分を削除したタスクを、CapistranoでEC2インスタンスを作成した後のデプロイタスクの直前に挿入してやる感じです。

    ...
  • Capistranoで新規作成したEC2インスタンスの初期設定

    本項では、Capistranoで新規作成したEC2インスタンスへSSHで初回ログインした際の、保守用ユーザの作成、初期ユーザに対してのパスワード設定、サーバのホスト名設定など、いわゆるサーバ環境の初期設定を行うタスクを作ってみます。

    その前に、ホスト名のつけ方としての色々と試してみての所感なのですが、初回SSHログイン後にそれぞれのインスタンスに対してHOSTS設定する際にEC2側にタグ付けてしていってもできるのですが、まずインスタンス作成する時にあらかじめホスト名の元となるタグを付けておいて、各インスタンスログイン後はそのタグを参照してHOST名を設定する方がスマートだと思いました。特に複数インスタンスを同時に立てる時などにホスト名に連番を振りたいとかいう時は、カウンター変数を使って回しているec2.instances.create()時にそのカウンターの数値を転用できるので簡単でした。ということで、インスタンス作成時にタグを追加する方法ですが、

    # タグ情報
    set :host_name, 'deploy-client'
    
    ~(中略)~
    
        created_instances = []
        cnt = 0
        while cnt < fetch(:instance_count) do
          i = ec2.instances.create(
            ~(中略)~
          )
          sleep 10 while i.status == :panding
          i.tags['Name'] = [ fetch(:host_name), format("%02d", cnt+1) ].join('-')
          created_instances << i.id
          cnt += 1
        end
    
    ~(省略)~
    

    ── と、ec2.instances.create()の後でタグを付けてやればOKです1。 今回はこのNameタグの値をその後のタスクでインスタンスのホスト名として利用します。

    いきなり横道に逸れましたが、本題に戻ります。 初回SSH時のタスクとして、前回作成したinitタスクを使います。流れとしては、デプロイサーバ側で新たに作成するユーザ用のキーペアを作成しておいて、デフォルトユーザにてSSHログイン後、まず保守用の新規ユーザアカウントを作成ます。その後、そのユーザに公開鍵認証によるSSH設定を行い、デフォルトユーザにはパスワードを設定してsudo権限を剥奪、ホスト名を設定して一旦ログアウトしています。 まず、タスク設定前に各種パラメータを定義します。

    ...
  • Capistranoで新規作成したEC2インスタンスにSSH接続する

    前回に引き続き、Capistranoで新たに作成したEC2インスタンスにSSHでログインしてみます。

    まず、事前準備として、AWS側で新規インスタンス用のキーペアを作成しておきます。AWSマネージメントコンソールの「EC2」メニューから「NETWORK & SECURITY」カテゴリの「Key Pairs」メニューで、キーペアを作成できるので、必要に応じて作成してください。本項の例では、「deploy-test」というCapistranoが稼動しているデプロイ環境用インスタンスで使用しているキーペアを使います。

    AWSマネージメントコンソール:キーペア

    そして、利用するキーペアのプライベートキーファイル(本項例ではdeploy-test.pemファイル)をCapistranoを実行するデプロイ環境のデプロイを実行するユーザのホームディレクトリ(本項例では/home/deploy-user/)にアップロードしておきます1。 アップロードしたプライベートキーには適切な読み込み権限を付与しておく必要があるので、ファイルのパーミッションを変更します。

    $ cd ~
    $ chmod 600 deploy-test.pem
    $ ls -l *.pem
    -rw------- 1 deploy-user deploy-user 1692 Jul  2 10:18 deploy-test.pem
    

    デプロイ設定ファイルconfig/deploy.rbは下記のように修正。

    # config valid only for Capistrano 3.1
    lock '3.2.1'
    
    # AWS SDK for Ruby を読み込む
    require 'aws-sdk'
    
    # AWS SDK用の設定
    AWS.config({
      :access_key_id => '<AWS ACCESS KEY ID>', 
      :secret_access_key => '<AWS SECRET ACCESS KEY>', 
      :region => 'ap-northeast-1', 
    })
    
    # AMIのimage_id
    # Amazon Linux AMI 2014.03.2 (HVM)
    set :ami_image_id, 'ami-29dc9228'
    
    # 作成するInstance数
    set :instance_count, 2
    
    # 作成するInstanceタイプ
    set :ec2_instance_type, 't2.micro'
    
    # 作成先のAvailability Zones
    set :availability_zones, [ 'ap-northeast-1a', 'ap-northeast-1c' ]
    
    # 作成先のsubnet_id(必要に応じて)
    set :subnet_ids, [ 'subnet-********', 'subnet-********' ]
    
    # 使用するキーペア名
    set :key_pair_name, 'deploy-test'
    
    # プライベートキーファイル
    set :privert_key_file, 'deploy-test.pem'
    
    # 利用するセキュリティグループID
    set :security_groups, [ 'sg-********', 'sg-********', 'sg-********', 'sg-********' ]
    
    # Capistranoデフォルトのタスクを削除する
    framework_tasks = [:starting, :started, :updating, :updated, :publishing, :published, :finishing, :finished]
    framework_tasks.each do |t|
      Rake::Task["deploy:#{t}"].clear
    end
    Rake::Task[:deploy].clear
    
    desc 'Launch an EC2 instance to each availability zone different'
    task :launch do
      run_locally do
        ec2 = AWS::EC2.new
    
        created_instances = []
        cnt = 0
        while cnt < fetch(:instance_count) do
          if cnt.even? then
            current_az = fetch(:availability_zones)[0]
            current_sn = fetch(:subnet_ids)[0]
          else
            current_az = fetch(:availability_zones)[1]
            current_sn = fetch(:subnet_ids)[1]
          end
          i = ec2.instances.create(
            :image_id => fetch(:ami_image_id),
            :monitoring_enabled => false,
            :availability_zone => current_az,
            :subnet => current_sn,
            :key_name => fetch(:key_pair_name),
            :security_group_ids => fetch(:security_groups),
            :disable_api_termination => true,
            :instance_type => fetch(:ec2_instance_type),
            :count => 1,
            :associate_public_ip_address => true
          )
          sleep 10 while i.status == :panding
          created_instances << i.id
          cnt += 1
        end
        execute "echo -n #{created_instances} > ~/CREATED_INSTANCES"
    
      end
    end
    

    では、インスタンスラウンチタスクを実行してみます。

    ...
  • CapistranoでAWS EC2インスタンスをデプロイする時の注意点

    前回のデプロイ設定ファイルで新たに作成されたEC2インスタンスには、キーペアセキュリティグループなどが設定されていなかったため、そのままでは作成したインスタンスにSSHでアクセスできませんでした…1orz

    その後、色々とデプロイ設定を修正して、作成したEC2インスタンスにSSHでログインするところまで出来たので、その経緯を備忘録として書いてみた次第。 まぁ、CapistranoでAWSのEC2インスタンスを作成する際…というより、「AWS SDK for Ruby」でEC2インスタンスを作成する時の注意点…に近いのかもしれない。

    まず、前回のconfig/deploy.rbからインスタンスラウンチのタスク部分を見てみる。

    desc 'Launch an EC2 instance to each availability zone different'
    task :launch do
      run_locally do
        ec2 = AWS::EC2.new
    
        created_instances = []
        cnt = 0
        while cnt < fetch(:instance_count) do
          if cnt.even? then
            current_az = fetch(:availability_zones)[0]
            current_sn = fetch(:subnet_ids)[0]
          else
            current_az = fetch(:availability_zones)[1]
            current_sn = fetch(:subnet_ids)[1]
          end
          i = ec2.instances.create(
            :image_id => fetch(:ami_image_id), 
            :availability_zone => current_az,
            :subnet => current_sn, 
            :instance_type => fetch(:ec2_instance_type), 
            :count => 1
          )
          sleep 10 while i.status == :panding
          created_instances << i.id
          cnt += 1
        end
        execute "echo -n #{created_instances} > ~/CREATED_INSTANCES"
    
      end
    end
    

    作成するインスタンスに対して、AvailabilityZoneとSubnetの設定しかしていないので、そりゃあアクセス不能になります。最低限、セキュリティグループを設定して、外部からのアクセス経路を確保して、キーペアを設定して認証ユーザがログイン可能にしてあげる必要はあります。あとは、PublicIP(PublicDNS)を自動で割り振られるようにして、インターネットからのアクセスも出来るようにしておくあたりまでが、最小設定となるかと。

    ...
  • PV InstanceからHVM Instanceへ変換(CentOS6)

     

    準備するもの

    ① EC2 Instance(CentOS6)既存の動いているものでもOK ② 変換元のRoot deviceのSnapshot ③ ②から作成したEBS Volume (SSDのほうが作業が早い) ③ 空のEBS Volume (SSDのほうが作業が早い)

    DEVICEの確認

    # fdisk -l |grep dev
    Disk /dev/xvda: 10.7 GB, 10737418240 bytes
    Disk /dev/xvdb: 4289 MB, 4289200128 bytes
    Disk /dev/xvdf: 10.7 GB, 10737418240 bytes <-PV環境のRoot device
    Disk /dev/xvdg: 10.7 GB, 10737418240 bytes <-空のEBS Volume
    

    作業開始

    1.PVのDiskを縮小し、コピー容量を減らす

    ...
  • Capistranoで異なるAvilabilityZoneにEC2インスタンスをラウンチする

    前回、

    次回はデータベースの作成や初期設定を直前タスクとして挿入して、さらにWordPressの設定ファイルの書き換え、GitHubからWordPressテーマをダウンロードするところまでやってみようと思います。

    とか云っていたのですが、都合によりCapistrano3を使ってAWSのEC2インスタンス作成を行ったので、そのTIPSを残しておこうかと思います。 今回は、デプロイ用のEC2インスタンスから公式のAMIを利用してAvailabilityZonesが異なるエリアにそれぞれEC2インスタンスを立ててみました。試験的に、AMIは最新のインスタンスタイプ「t2.micro」に対応した「Amazon Linux AMI 2014.03.2 (HVM)」を利用して、Tokyoリージョンの1aと1cのAvailabilityZoneにそれぞれ2台ずつ、合計4インスタンスを同時に立ててみます。

    ※ 事前準備として、インスタンスを立てるAWSアカウントにてVPCやSubnet等EC2インスタンスを作成する上で必要最小限の設定をしておく必要があります。特に今回は異なるAvailabilityZoneへのインスタンスを立てるのでそれぞれのゾーンにSubnetを設定しておく必要があります。

    さて、早速デプロイの手順です。 はじめに、デプロイ環境(サーバ)の設定を行うため、config/deploy/test.rbを編集します。 今回は動的に作成したEC2インスタンスをデプロイ環境とするため、事前のサーバ設定ができません。というのも、AWSで立ち上がる新規インスタンスにはElasticIPを付与しないので、毎回PublicIPが異なり、エンドポイントURLも動的に変わってしまうためです。なので、今回はインスタンスが起動した後にCapistoranoのタスクにて対象サーバを設定するようにします。 初期のサーバ設定は不要となるので、記述をコメントアウトしておきます。なお、SSHのオプションだけは最終的に共通で利用する予定なので残しておきます(今回のデプロイタスクでは使いませんが…)。

    $ vim config/deploy/test.rb
    
    #role :app, %w{deploy@localhost}
    #role :web, %w{deploy@localhost}
    #role :db, %w{deploy@localhost}
    
    set :ssh_options, {
      keys: %w(/home/deploy/.ssh/id_rsa),
      forward_agent: true,
    }
    

    今回のデプロイでは、「AWS SDK for Ruby」を利用するので、あらかじめSDKをインストールしておきます。

    $ sudo gem install aws-sdk
    

    次に、config/deploy.rbにデプロイ内容を設定します。

    $ vim config/deploy.rb
    
    # config valid only for Capistrano 3.1
    lock '3.2.1'
    
    # AWS SDK for Ruby を読み込む
    require 'aws-sdk'
    
    # AWS SDK用の設定
    AWS.config({
      :access_key_id => '<AWSアカウントのACCESS KEY ID>', 
      :secret_access_key => '<AWSアカウントのSECRET ACCESS KEY>', 
      :region => 'ap-northeast-1', # EC2 Instanceを作成するリージョン
    })
    
    # AMIのimage_id
    # Amazon Linux AMI 2014.03.2 (HVM)
    set :ami_image_id, 'ami-29dc9228'
    
    # 作成するInstance数
    set :instance_count, 4
    
    # 作成するInstanceタイプ
    set :ec2_instance_type, 't2.micro'
    
    # 作成先のAvailability Zones
    set :availability_zones, [ 'ap-northeast-1a', 'ap-northeast-1c' ]
    
    # 作成先のsubnet_id
    set :subnet_ids, [ 'subnet-********', 'subnet-********' ]
    
    # Capistranoデフォルトのタスクを削除する
    framework_tasks = [:starting, :started, :updating, :updated, :publishing, :published, :finishing, :finished]
    framework_tasks.each do |t|
      Rake::Task["deploy:#{t}"].clear
    end
    Rake::Task[:deploy].clear
    
    desc 'Launch an EC2 instance to each availability zone different'
    task :launch do
      run_locally do
        ec2 = AWS::EC2.new
    
        created_instances = []
        cnt = 0
        while cnt < fetch(:instance_count) do
          if cnt.even? then
            current_az = fetch(:availability_zones)[0]
            current_sn = fetch(:subnet_ids)[0]
          else
            current_az = fetch(:availability_zones)[1]
            current_sn = fetch(:subnet_ids)[1]
          end
          i = ec2.instances.create(
            :image_id => fetch(:ami_image_id), 
            :availability_zone => current_az,
            :subnet => current_sn, 
            :instance_type => fetch(:ec2_instance_type), 
            :count => 1
          )
          sleep 10 while i.status == :panding
          created_instances << i.id
          cnt += 1
        end
        execute "echo -n #{created_instances} > ~/CREATED_INSTANCES"
    
      end
    end
    
    desc 'Check the activation status of new instances'
    task :check do
      created_instances_list = 'CREATED_INSTANCES'
    
      run_locally do
        ec2 = AWS::EC2.new
    
        begin
          if test "[ -f ~/#{created_instances_list} ]"
            created_instances = capture("cd ~; cat #{created_instances_list}").chomp
            ci = created_instances.gsub(/(\[|\s|\])/, '').split(',')
            target_instances = ec2.instances.select { |i| i.exists? && i.status == :running && ci.include?(i.id) }.map(&:private_ip_address)
            if target_instances.length == 0 then
              raise "No created instances"
            end
            target_instances.each { |var| 
              server var, user: 'ec2-user', roles: %w{web app}
            }
          end
        rescue => e
          info e
          exit
        end
    
      end
    end
    
    task :deploy => :check do
      run_locally do
    
        info roles(:all)
        info 'Next task of deploy on new instances'
    
        # deploy start
    
      end
    end
    

    実際にインスタンスのラウンチをしてみる。

    ...
  • Capistrano3でWordPressのデプロイをしてみる

    Capistrano3を使って、自ホストに最新版のWordPressをデプロイしてみたのでその手順をログとして残しつつ、デプロイツール「Capistrano」の理解を進めていこうと思います。 事前準備として、デプロイ用の環境をAmazonEC2にt2.microインスタンスとしてラウンチして、WordPressが動作する環境(Apache+MySQL+PHP)、Rubygem(とRVM)、そしてCapistrano3のインストールまで出来ている状態で記載しています(この辺の事前準備の手順も後日TIPSとしてまとめたいと思っていますが、今回は省略します)。

    さて、早速手順に入ります。 はじめに、デプロイプロジェクト用のディレクトリを作成して、Capistranoをインストールします。

    $ cd ~
    $ mkdir test-project
    $ cd test-project
    $ cap install STAGES=test
    mkdir -p config/deploy
    create config/deploy.rb
    create config/deploy/test.rb
    mkdir -p lib/capistrano/tasks
    Capified
    

    Capistrano3ではマルチステージデプロイ機能がデフォルトでONになっているので、単にcap installを行っただけだと、productionステージとstagingステージが作成されてしまいます。 今回はテスト用に自分のローカルホストのみを対象にデプロイを試すので、testステージのみのプロジェクトを環境変数STAGESの引数に指定します1

    次にCapistranoの初期設定を行います。必要ならば、プロジェクトディレクトリ直下に作成されたCapfileを編集します。

    $ vim Capfile
    

    今回はrvmやrbenvをデプロイに使わないので、編集項目はありません2

    続いて、デプロイ環境(サーバ)の設定を行うため、config/deploy/test.rbを編集します。

    $ vim config/deploy/test.rb
    ----
    role :app, %w{deploy@localhost}
    role :web, %w{deploy@localhost}
    role :db, %w{deploy@localhost}
    
    set :ssh_options, {
      keys: %w(/home/deploy/.ssh/id_rsa),
      forward_agent: true,
    }
    

    そして、config/deploy.rbにデプロイ内容を設定します。

    ...
  • Google Glass Word Lens と Play game

    Google Glassレポートその2です。

    前回は借り物でしたが、今回は Google Glass Explorer Edition Ver 2.0 を自社で購入してのレビューです。
    すごい!代表カッコイイ!!!

    まず、借りていたver1と今回のver2の違いについて。

    ggっても明確な情報は捕まりません。

    Google Glass 2 offers minor but important hardware updates

    一番よくまとまっていると思われたのが、↑のサイト。

    で、その情報によれば

     

    • イヤホンが付いてくる

    Glassはメガネのツルにあたる部分に骨伝導スピーカーが付いています。
    大体はこれで事足りると思うのですが、聞こえない場合の手段が追加された形だと思います。
    より万人向けに。素晴らしいことです。

     

    • 処理速度10%向上

    ほんの少しですがパフォーマンスが向上している模様。
    イイネ!

     

     

    overheat

    ちなみに重たいアプリを動かしていると熱くなり使用を控えるようメッセージが出るのは変わりませんでした。
    残念…

     

     

    続いて前回おざなりなレビューをした2つのアプリの使い方について

     

     

    device-2014-06-11-170958

    OK glass が表示されている状態で、Glassの右こめかみのあたりをタップ。
    インストールされているアプリを切り替えられる状態になります。
    メガネのツルの部分を左右にスワイプし目的のアプリを探します。

     

     

    translate_title
    Word Lensを見つけたら、すかさずタップ。

    ...
  • Google Glass ファーストインプレッション

    噂のGoogle Glass。USでは既にかなり楽に入手できるようになっています。
    んが、日本では買えません。なぜ買えないんだぁー!
    リスクのある並行輸入業者に頼めということでしょうか。
    いや、無理です。(Glassのお値段的に)

    でも、持っている人は持っているんですよねぇ。
    Early Adopterな方々との情報格差がまた広がる…そんな絶望感を抱えながら、我社の代表にダメ元で相談したところ、期間限定ではありますが、Glassをお知り合いの方から借りてくれるとのこと!
    ありがとうございます!さすがです!!

    そんなこんなで触らせて頂いたGoogle Glassの感想です。
    120%私見です。ご容赦下さい。


    さて、メガネ型のウェラブルデバイスといえば、どうしても期待してしまうのがAR。
    拡張現実ではないでしょうか。

     

    AR1

    ↑こんなの。

    現実を上書きし、世界の有り様を拡張してしまう….サイバー!!(鼻血ブー
    Google Glassはどこまでそんな世界を引き寄せてくれるのでしょうか?
    ワクワクしながら、試してみました。

    結果:
    Google Glassは ARを実現するツールではない。

    まあ、ある程度分かっていた事ではあるのですが、Glassは視界の右上に小さなディスプレイを追加するものです。
    決して存在しないものをあたかも存在するかのように感じさせてくれるデバイスではありません。
    ただ、それでもなお、この製品が市場に受け入れられ順調に成長を続けていけば、いずれは万人が思い浮かべるARの世界を実現するようなデバイスにたどり着くかもしれない…そんな可能性は感じました。

    このデバイスの可能性を試すには、アプリを追加する必要があります。
    ところがアプリを追加するために必要なAndroidアプリ、MyGlassは日本からはダウンロードできません。
    なってこった。

    しょうがないので野良アプリをインストールします。
    私は以下からダウンロードしました。

     

    Download Latest MyGlass APK

    あくまで自己責任でお願いします。

    アプリを起動しBluetoothでペアリングすれば準備完了。
    ワシワシアプリをインストールしましょう。

    私がインストールしたアプリの中で特に気になったのは以下の2つです。

    Mini Games

    首の動きで操作するのが新しい!そして若干ARぽい。

     

    Word Lens

    見たものを翻訳できる!
    これは…未来ぽい!

    ...