【AWS】.ssh/config を利用して、ログイン時のコマンドを省略する

概要

インスタンスに毎回ログインする際、ユーザ名と鍵 PATH を記載するのが面倒くさいので、設定ファイル (.ssh/config) にログインに必要な情報を記載して、ログイン時のコマンドを省略しようと思います。

.ssh/config とは

SSH を利用してインスタンスへログインする際に利用される設定ファイルです。

インスタンスSSH する前に...

ユーザ名の確認

サーバ OS によって、ログイン時に利用するユーザ名が異なる様です。

サーバ OS ユーザ名
 Amazon Linux  ec2-user
 RHEL  ec2-user または root
 Ubuntu  ubuntu または root
 Centos  centos
 Fedora  ec2-user
 SUSE  ec2-user または root

参考:SSH を使用した Linux インスタンスへの接続 - Amazon Elastic Compute Cloud

鍵 PATH の確認

初めてインスタンスを作成したときに、キーペアを作成して、自分のPCにダウンロードしていると思います。

もし、キーペアが見つからない時は、既存のキーペアは再ダウンロードできないので、新しく作り直してください。

f:id:kyamanak83:20170815001757p:plain

ログイン方法

SSHコマンドでログイン

ssh -i <鍵 PATH> <ユーザ名>@<ホスト名> のフォーマットでコマンドを実行します。

$ ssh -i amazon_private_key-20160805.pem ec2-user@ec2-00-00-000-000.us-west-2.compute.amazonaws.com

.ssh/config を利用してログイン

設定ファイルの記載内容を確認します。ホスト名や鍵名は自分のものに書き換えてください。

$ cat .ssh/config
Host AWS
    HostName ec2-00-00-000-000.us-west-2.compute.amazonaws.com
    User ec2-user
    IdentityFile amazon_private_key-20160805.pem

設定ファイルに記載しておくと、簡単なコマンドでログインができます。

$ ssh AWS
Last login: Wed Nov 23 06:38:46 2016 from aa200000000000000000.userreverse.dion.ne.jp

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2016.09-release-notes/

 まとめ

今回は、Amazon Linux を利用してログインしてみました。

インスタンスにログインするための最低限の .ssh/config の設定をまとめました。

【Ruby on Rails】ルーティング member と collection の違い

概要

Rails には index, show, new, edit, create, update, destroy の 7 つのデフォルトアクションが用意されています。

もし、これ以外のアクションを新しく追加したい場合、ルーティングに設定を追記する必要があります。

この場合、routes.rb をどう書くのか調べたところルーティングのメソッドに、member と collection と言うものがありました。

この 2 つの使い方を簡単にまとめました。

member の使い方

member は特定のデータに対するアクションに利用します。

例えば、ユーザーのフォローを行う follow アクションを新しく追加したいとします。

この場合、フォローを行うのは、特定のユーザーです。

『 A さんが B さんをフォローする』の様な処理になると思います。

なので、URL を作成するとしたら、下記の様になります。

特定のユーザーを示す id が含まれていることに注目してください。

http://$(DNS)/users/1/follow

member を使った routes.rb の記載方法は下記の様になります。

$ cat config/routes.rb
Rails.application.routes.draw do
  resources :users do
    post :follow, on: :member
  end
end

collection の使い方

collection は全部のデータに対するアクションに利用します。

例えば、ユーザーの検索を行う search アクションを新しく追加したいとします。

この場合、検索対象は全部のユーザーです。

A さんが検索しても、B さんが検索しても同じ結果になるはずです。

なので、URL を作成するとしたら、下記の様になります。

http://$(DNS)/users/search

collection を使った routes.rb の記載方法は下記の様になります。

$ cat config/routes.rb
Rails.application.routes.draw do
  resources :users do
    get :search, on: :collection
  end
end

まとめ

member と collection の使い方をまとめました。

member は特定のデータに対するアクションに利用します。

collection は全部のデータに対するアクションに利用します。

和訳すると collection は集団、member は (集団の) 一員 みたいな意味なので、そう覚えておけば分からなくならないと思います。

【Ruby on Rails】ルーティング scope と namespace の違い

概要

RailsAPI を作成するときに、URL 設計を気にすると思います。

例えは、ユーザ情報を操作する users API のエンドポイントを下記のようなパスで作成したいとします。

http://$(DNS)/api/v1/users

この場合、routes.rb をどう書くのか調べたところルーティングのメソッドに namespace と scope と言うものがありました。

この 2 つの使い方を簡単にまとめました。

namespace の使い方

namespace を使うときは、Controller に URL のパス同様に /api/v1/ のディレクトリを作成する必要があります。

$ tree app/controllers/
app/controllers/
├── api
│   └── v1
│       └── users_controller.rb
├── application_controller.rb
└── concerns

app/controllers/api/v1/users_controller.rb の中身は、module を使って URL のパス同様の構成にします。

$ cat app/controllers/api/v1/users_controller.rb
module Api
  module V1
    class UsersController < ApplicationController
      def index
       render json: { status: 200, message: 'Success' }
      end
    end
  end
end

routes.rb は namespace を利用すると、このように記載できます。

$ cat config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users, only: :index
    end
  end
end

ルーティングを確認します。

ひとまず、作成したい API のエンドポイントが用意できました。

$ rake routes
      Prefix Verb URI Pattern             Controller#Action
api_v1_users GET  /api/v1/users(.:format) api/v1/users#index

最後にエンドポイントにアクセスできるか確認してみます。

$ curl 'http://localhost/api/v1/users'
{"status":200,"message":"Success"}

出来ました!レスポンスに想定通りの結果が返ってきました!

scope の使い方

scope を使うときには、Controller にわざわざディレクトリを作成する必要はありません。そのままで大丈夫です。

$ tree app/controllers/
app/controllers/
├── application_controller.rb
├── concerns
└── users_controller.rb

app/controllers/users_controller.rb の中身もそのままで大丈夫です。

$ cat users_controller.rb
class UsersController < ApplicationController
  def index
    render json: { status: 200, message: 'Success' }
  end
end

routes.rb は scope を利用すると、このように記載できます。

$ cat config/routes.rb
Rails.application.routes.draw do
  scope :api do
    scope :v1 do
      resources :users, only: :index
    end
  end
end

ルーティングを確認します。

ひとまず、作成したい API のエンドポイントが用意できました。

$ rake routes
Prefix Verb URI Pattern             Controller#Action
 users GET  /api/v1/users(.:format) users#index

最後に、エンドポイントにアクセスできるか確認してみます。

$ curl 'http://localhost/api/v1/users'
{"status":200,"message":"Success"}

こちらの方法でも、想定通りの結果を受けることが出来ました!

まとめ

namespace と scope で URL を設計する方法をまとめました。

routes.rb の記載方法はどちらも同じです。

大きく異なるのは namespace を利用するときは、Controller 内に実際の URL パスと同様のディレクトリ構成を作る必要があるという点です。

namespace と scope どちらを使って URL 設計すれば良いかは、アプリケーションによると思います。

例えば、Web サーバの機能の一部に API の機能が混じっているアプリケーションの場合、Contoroller 内に API 用のディレクトリを作成して、namespace を使った URL 設計を行うのが綺麗な気がします。

API 機能しかないアプリケーションであれば、コントローラー内にわざわざディレクトリを作成せずに、scope を使ってを URL 設計を使うのが綺麗な気がします。

【AWS】Ruby on Rails + Nginx + Unicorn + MySQL 環境構築

概要

AWS に初めて Rails のアプリを作成した時の手順をまとめました。

はじめに

こちらの環境でアプリケーションを作成しました。

サーバOS   Amazon Linux
Web サーバ   Nginx
Rack サーバ   Unicorn
データベース   MySQL
フレームワーク   Rails 5.0.X
プログラミング言語   Ruby 2.4.X

環境構築

下記手順に沿って実行して頂ければ、Rails アプリの起動まで出来ると思います。

タイムゾーンの設定

初めにタイムゾーンの設定を変更します。

Amazon Linux では、デフォルトのタイムゾーンUTC (協定世界時間) に設定されています。

このままでは扱いづらいので、JST (日本標準時間) に変更します。

$ sudo vim /etc/sysconfig/clock
ZONE="Asia/Tokyo" # ZONE エントリを書き換えます
UTC=true # 変更しないでください

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ sudo shutdown -r now

$ date
Tue Jul 25 21:57:06 JST 2017 # JST に変更されています

参考:Linux インスタンスの時刻の設定 - Amazon Elastic Compute Cloud

 ロケールの設定

次にロケールの設定を行います。

基本的に UTF-8 に変更しようと思います。

$ sudo vim /etc/sysconfig/i18n
LANG=ja_JP.UTF-8
LC_CTYPE=ja_JP.UTF-8
LC_NUMERIC=ja_JP.UTF-8
LC_TIME=ja_JP.UTF-8
LC_COLLATE=ja_JP.UTF-8
LC_MONETARY=ja_JP.UTF-8
LC_MESSAGES=ja_JP.UTF-8
LC_PAPER=ja_JP.UTF-8
LC_NAME=ja_JP.UTF-8
LC_ADDRESS=ja_JP.UTF-8
LC_TELEPHONE=ja_JP.UTF-8
LC_MEASUREMENT=ja_JP.UTF-8
LC_IDENTIFICATION=ja_JP.UTF-8

先ほどと date コマンドを実行してみると、日本語表記になりました。

$ date
2017年  7月 25日 火曜日 21:58:56 JST

参考:ロケールとは - 国際化対応言語環境の利用ガイド

標準ライブラリ インストール

$ sudo yum install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel

Git インストール

$ sudo yum install git

MySQL インストール

$ sudo yum install mysql mysql-devel

Nginx インストール

$ sudo yum install nginx

Ruby インストール

現在の Ruby の Version を確認します。

$ ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]

Rails 5.0.0 以上は Ruby 2.2.2 以上が必要となります。

Rails 5.0.0 以上を使いたいので、Ruby の Version を 2.4.0 にアップデートします。

$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

$ rbenv install -v 2.4.0
$ rbenv rehash
$ rbenv global 2.4.0
$ ruby -v
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]

参考

Bundler インストール

$ gem install bundler

Rails インストール

Rails 5.0.X 系の最新バージョンをインストールします

$ gem install --no-ri --no-rdoc rails --version="~>5.0.0"
$ rails -v
Rails 5.0.4

 Rails アプリ作成

hogehoge アプリケーションを作成します。

# 作業用ディレクトリ確保します
$ sudo mkdir /var/workspace
$ sudo chown ec2-user /var/workspace
$ cd /var/workspace

# データベースは MySQL を指定します
# Gemfile を修正したいので、この段階では bundle install を skip します
$ rails new hogehoge -d mysql --skip-test --skip-bundle

# 作成したアプリケーションに移動してください
$ cd hogehoge

Gemfile を修正

Rails では アプリケーションで利用したい gem パッケージを管理した、Gemfile というものがあります。

この Gemfile に unicorn の設定を追加してインストールする必要があります。

ちなみに、Javascript を利用するために必要なので、therubyracer も追加してください。(既に Gemfile に記載されているので、コメントアウトを外すだけでもいいです。)

# この 2 行を Gemfile に追加してください
$ vim Gemfile
gem 'unicorn', '~> 5.3'
gem 'therubyracer', platforms: :ruby

# Gemfile に書かれた gem パッケージと、その依存パッケージをインストールします
$ bundle install --path vendor/bundle

 Nginx の設定

3 箇所ほど修正点があります。

$ sudo vim /etc/nginx/nginx.conf
user ec2-user; # user 変更
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    index   index.html index.htm;

   # unicornapp 追加
    upstream unicornapp {
     server unix:/var/workspace/hogehoge/tmp/unicorn.sock;
    }
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  localhost;
        root         /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;

        location / {
            proxy_pass http://unicornapp; # proxy_pass 追加
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}

設定ファイルを更新したら、Nginx を再起動します

$ sudo service nginx restart
nginx を停止中:                                            [  OK  ]
nginx を起動中:                                            [  OK  ]

 EC2 ダッシュボードに戻って、セキュリティグループにインターネットからの HTTP アクセス許可のルールを追加してください。

f:id:kyamanak83:20170725235134p:plain

ここまで完了すると、インスタンスのパブリックDNS、又はパブリック IP にブラウザからアクセスした時、Nginx のエラー画面が見れるはずです。

 

f:id:kyamanak83:20170726223713p:plain

データベース接続の設定

Rails アプリケーションで利用する、データベースとの接続設定を行います。

データベースは既にRDSインスタンスに用意してあるものとして話を進めます。

$ vim config/database.yml
default: &default
 adapter: mysql2
 encoding: utf8
 pool: 5
 port: 3306
 username:  # ユーザ名
 password:  # パスワード
development:
 <<: *default
 host: # ホスト名
 database: # データベース名

RDS ダッシュボードに戻って、セキュリティグループに、データベースに接続したい EC2 インスタンスのプライベート IP を追加してください。

f:id:kyamanak83:20170726225648p:plain

Unicorn の設定

最後に Rails アプリケーションの画面を表示するために、Unicorn の設定を行います。

Google で検索すると色んな人の設定が出てくると思います。

自分は下記のように設定しました。(自分も Google 検索で誰かのをパクりました^^;)

新しく config/unicorn.rb ファイルを作成します。

$ vim config/unicorn.rb
rails_root = File.expand_path('../../', __FILE__)

worker_processes 2

working_directory rails_root
timeout 30
preload_app true

# unicorn.sock ファイルの PATH を変更する場合は nginx.conf の修正も必要です
listen "#{rails_root}/tmp/unicorn.sock"
pid "#{rails_root}/tmp/unicorn.pid"

stderr_path File.expand_path('../../log/unicorn_stderr.log', __FILE__)
stdout_path File.expand_path('../../log/unicorn_stdout.log', __FILE__)

preload_app true

before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      puts "Sending #{sig} signal to old unicorn master..."
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

  sleep 1
end

after_fork do |server, worker|
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
end

Unicorn を起動させます

$ bundle exec unicorn -c config/unicorn.rb -E development -D

すると 、Rails の画面が表示されました!

 

f:id:kyamanak83:20170726225834p:plain

 これで、Rails アプリケーションの作成が完了です。おしまい。

【Ruby on Rails】緯度経度から 2 点間の距離を算出する

概要

今日では、企業が公開している API を利用して、簡単にアプリケーションを作成できるようになりました。

その中には、店舗情報を扱う API も数多く存在しています。

店舗情報を扱う API の多くは、店舗の位置情報を表すため、緯度経度を利用しています。

位置情報を利用したアプリケーションを作成する場合、緯度経度から 2 点間の距離を求めたいと思うことがあるかもしれません。

例えば、店舗情報 API と組み合わせて、『現在地周辺の半径 ○○ m 以内のレストランを近い順に表示したい』と思った場合、これに該当します。

今回、緯度経度から 2 点間の距離を算出するプログラムを作成する機会があったので、その時のことをまとめました。

計算式

地球は球体なので、地球上の 2 点間の距離を算出するには、大円距離を求める必要があります。

 大円距離 - Wikipedia

大円距離の計算式はいくつかあるようですが、全ての距離に対して用いることのできる Vincenty 法を利用することとします。

Vincenty法 - Wikipedia

プログラムを作成

Vincenty 法に当てはめていきます。

def distance(lat1, lng1, lat2, lng2)
  # ラジアン単位に変換
  x1 = lat1.to_f * Math::PI / 180
  y1 = lng1.to_f * Math::PI / 180
  x2 = lat2.to_f * Math::PI / 180
  y2 = lng2.to_f * Math::PI / 180
  
# 地球の半径 (km) radius = 6378.137
# 差の絶対値 diff_y = (y1 - y2).abs
calc1 = Math.cos(x2) * Math.sin(diff_y) calc2 = Math.cos(x1) * Math.sin(x2) - Math.sin(x1) * Math.cos(x2) * Math.cos(diff_y)
# 分子 numerator = Math.sqrt(calc1 ** 2 + calc2 ** 2)
# 分母 denominator = Math.sin(x1) * Math.sin(x2) + Math.cos(x1) * Math.cos(x2) * Math.cos(diff_y)
# 弧度 degree = Math.atan2(numerator, denominator)
# 大円距離 (km) degree * radius end

 

abs は数値の絶対値を取得するメソッドです。

絶対値を取得する - 数値(Numeric)クラス - Ruby入門

数値計算用のメソッドはこちらを参考にしてください。

module Math (Ruby 2.4.0)

算出結果の確認

先ほどのプログラムで新宿駅と渋谷駅の距離を求めます。

# 新宿駅
lat1 = 35.689407
lng1 = 139.700306

# 渋谷駅
lat2 = 35.658034
lng2 = 139.701636

distance = distance(lat1, lng1, lat2, lng2)
puts "#{distance.round(6)} km" # 小数点 6 桁で四捨五入

プログラムを実行します。

$ ruby distance.rb
3.494497 km

 

下記サイトで 2 点間の緯度経度を入力した結果と同じになりました。

2地点間の距離と方位角 - 高精度計算サイト

まとめ

緯度経度から 2 点間の距離を算出する場合、大円距離を求める必要があります。

プログラムは Vincenty 法 に当てはめるだけなので、簡単に実装できます。

最後に

Rails で位置情報を扱う場合、ここら辺の gem が有名かと思います。

今回、作成したプログラムも機能として盛り込まれていると思います。

他にも、多くの機能が用意されているので、gem を利用した方法も有りだと思います。

自分のアプリケーションにあった方法を採用してください。

【Ruby on Rails】 gem を使わないで環境毎に定数管理をする

概要

Rails で定数管理するにはたくさんの gem が存在しています。

定数管理の gem で有名なものだとここら辺の名前が上がるかと思います。

config は環境毎に Yaml ファイルを用意して定数を管理するので、大規模アプリケーション向けのイメージがあります。管理する定数が多い場合にオススメです。

settingslogic は 1 つの Yaml ファイルで環境毎の定数を管理するので、中小規模アプリケーション向けのイメージがあります。管理する定数が少ないのであれば見やすくてオススメです。

 

ただ、ここで....

『定数管理ぐらい自分で実装できないの?』と疑問に思ったことがあります。

そこで、自分が実装した定数管理方法についてまとめました。

はじめに

gem を使わない定数管理方法だと、最初にこの 2 つの方法が思いつきます。

  • application_controller.rb で定数を管理する方法
  • app/initializers/constants.rb のようなファイルを用意して、そこで定数を管理する方法

application_controller.rb はアプリケーション共通の処理を管理する場所なので、定数を管理するには相応しくないと思いました。

app/initializers/constants.rb のようなファイルを用意して、そこで定数を管理する方法は賛成です。

ただし、環境毎の定数管理を見やすくするため、Ruby ファイルではなく、Yaml ファイルを利用します。

Yaml ファイルを利用した定数管理用法

用意するファイルは 2 つです。

  • config/constants.yml
  • config/initializers/00_load_config.rb

config/constants.yml

これは定数を管理するためのファイルです。

Yaml のアンカーとエイリアスを利用して、環境毎の出し分けを行います。

# アプリケーション共通の設定を記載します
common: &common
domain:
example: http://example.com/
port:
- 80
- 443
message: This is application common setting
# 開発環境の設定を記載します development: <<: *common
message: This is development environment setting
# 本番環境の設定を記載します production: <<: *common
message: This is production environment setting

config/initializers/00_load_config.rb

これは定数管理用ファイルを読み込むためのファイルです。

file = "#{Rails.root}/config/constants.yml"

# 再帰的にオブジェクトを凍結します
def deep_freeze(hash)
hash.freeze.each_value do |i|
i.kind_of?(Hash) ? deep_freeze(i) : i.freeze
end
end

CONFIG = deep_freeze(YAML.load_file(file)[Rails.env].deep_symbolize_keys)

 

deep_symbolize_keys は再帰的にシンボル形式に変換してくれます。

https://apidock.com/rails/v4.0.2/Hash/deep_symbolize_keys

freeze は オブジェクトを凍結、つまり変更不可 (定数) にします。

http://ref.xaio.jp/ruby/classes/object/freeze

定数取得

定数の取得を試してみます。

$ rails c
Loading development environment (Rails 5.0.2)
irb(main):001:0> CONFIG
=> {:domain=>{:example=>"http://example.com/", :port=>[80, 443]}, :message=>"This is development environment setting"}

# 開発環境用の設定に上書きされています
irb(main):002:0> CONFIG[:message]
=> "This is development environment setting"

# 凍結されています
irb(main):003:0> CONFIG.frozen?
=> true

# ネストした値も凍結されています
irb(main):004:0> CONFIG[:domain][:exapmle].frozen?
=> true

# 変更しようとするとエラーになります
irb(main):005:0> CONFIG[:message] << 'Hello'
RuntimeError: can't modify frozen String

まとめ

定数管理を自分で実装することは簡単にできました。

自分で実装することの利点はこの 2つかなと思います。

  • 仕様を把握できる点
  • 自分で拡張できる点

ただし、1 つの Yaml ファイルで全ての定数を管理するので、中小規模アプリケーション向けの方法だと思います。

最後に

Rails の定数管理方法に答えはないと思います。

どの方法も一長一短なので、開発しているアプリケーションに合った方法を見極めて採用するのがいいと思います。