2014年10月31日金曜日

Railsでsitemap_generator使ってS3にサイトマップを作成する方法

Railsでサイトマップを作成して、登録する方法です。
サイトマップの生成は、GitHub - kjvarga/sitemap_generatorのGemを利用して行います。
また、サイトマップ自体はアマゾンのS3に保存しておきます。

インストール

Gemfileに下記を追加して、bundle install

gem 'sitemap_generator'

設定ファイルの作成

下記を実行して、config/sitemap.rb を作成します。

rake sitemap:install

サンプルコード

本来であれば、上記で生成されたconfig/sitemap.rbに設定を書けばよいと思いますが、
rakeでサイトマップ生成とS3のアップロードを行うようにしました。

コードは以下のような感じです。

  namespace :batch do
    desc 'サイトマップを作成して、S3にアップする。'
    task :create_site_map => :environment do |task|
      success_msgs = []
      success_msgs << 'success'

      batch_start_at = Time.now
      puts "run #{task.name} with env: #{Rails.env} : #{batch_start_at}"

      begin

        SitemapGenerator::Sitemap.default_host = 'http://ore-tech.blogspot.jp/'
        SitemapGenerator::Sitemap.public_path = 'tmp/'

        SitemapGenerator::Sitemap.adapter = SitemapGenerator::S3Adapter.new({
            :aws_access_key_id    => 'xxxxxxxxxxxxxxxxxxxxxxxxx',
            :aws_secret_access_key => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
            :aws_region            => 'ap-xxxxxxx-1', # リージョン 東京の場合、ap-northeast-1
            :fog_provider          => 'AWS',
            :fog_directory         => 'bucket-name' # bucket name
        })
        SitemapGenerator::Sitemap.sitemaps_host = 'http://bucket-name.s3-ap-xxxxxxx-1.amazonaws.com'

        site_maps_path = 'sitemap/'
        unless Rails.env == 'production'
          site_maps_path = "sitemap/#{Rails.env}/"
        end

        SitemapGenerator::Sitemap.sitemaps_path = site_maps_path

        SitemapGenerator::Sitemap.create do

          # home
          add '/home', :changefreq => 'daily', :priority => 0.9

          # contact 
          add '/contact_us', :changefreq => 'weekly'

          # Blog
          group(:sitemaps_path => site_maps_path, :filename => 'blog') do
            Blog.all.each do |blog|
              add blog.url, lastmod: blog.updated_at
            end
          end

        end

        if Rails.env == 'production'
          SitemapGenerator::Sitemap.ping_search_engines
        end

        batch_end_at = Time.now
        puts "end time : #{batch_end_at}"
        puts "Processing time #{batch_end_at - batch_start_at}"

      rescue => e
        puts "error occurred batch : #{Time.now}"
        puts "error occurred batch : #{e.message}"
      end

    end
  end

サイトマップ生成の設定

サイトマップ生成の設定は、主に下記の部分で行っています。

SitemapGenerator::Sitemap.default_host = 'http://ore-tech.blogspot.jp/'
SitemapGenerator::Sitemap.public_path = 'tmp/'

SitemapGenerator::Sitemap.adapter = SitemapGenerator::S3Adapter.new({
    :aws_access_key_id    => 'xxxxxxxxxxxxxxxxxxxxxxxxx',
    :aws_secret_access_key => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    :aws_region            => 'ap-xxxxxxx-1', # リージョン 東京の場合、ap-northeast-1
    :fog_provider          => 'AWS',
    :fog_directory         => 'bucket-name' # bucket name
})
SitemapGenerator::Sitemap.sitemaps_host = 'http://bucket-name.s3-ap-xxxxxxx-1.amazonaws.com'

site_maps_path = 'sitemap/'
unless Rails.env == 'production'
  site_maps_path = "sitemap/#{Rails.env}/"
end

SitemapGenerator::Sitemap.sitemaps_path = site_maps_path

上記設定を行った場合、下記の場所にサイトマップ用のファイルが生成されます。

application側

/tmp/sitemap/blog.xml.gz
/tmp/sitemap/sitemap.xml.gz
/tmp/sitemap/sitemap1.xml.gz

S3側

/bucket-name/sitemap/blog.xml.gz
/bucket-name/sitemap/sitemap.xml.gz
/bucket-name/sitemap/sitemap1.xml.gz

また、下記を行っているので実行されるenv環境によって、生成されるファイルの場所が異なります。

site_maps_path = 'sitemap/'
unless Rails.env == 'production'
  site_maps_path = "sitemap/#{Rails.env}/"
end

production => /bucket-name/sitemap/
staging => /bucket-name/sitemap/staging/blog.xml.gz
development => /bucket-name/sitemap/development/blog.xml.gz

サイトの登録

主には以下の部分で、ページを登録しています。
add メソッドでページ単位で登録していきます。

SitemapGenerator::Sitemap.create do

   # home
   add '/home', :changefreq => 'daily', :priority => 0.9

   # contact 
   add '/contact_us', :changefreq => 'weekly'

   # Blog
   group(:sitemaps_path => site_maps_path, :filename => 'blog') do
     Blog.all.each do |blog|
       add blog.url, :lastmod => blog.updated_at
     end
   end

 end

addメソッドのオプション

:changefreq

ページの予想される更新頻度
デフォルトは、weekly

設定可能な値は以下の通り

  • always:アクセスがあるたびに内容を更新するページに使用します。
  • hourly
  • daily
  • weekly
  • monthly
  • yearly
  • never: アーカイブ ページの URL に使用します。

:priority

サイト内のURLの優先順位を指定します。
0.1(重要でない)~1.0(重要)の範囲で指定します。
デフォルトは 0.5

ここの数値で、掲載順位には影響はないようです。

:lastmod

最終更新日を指定します
デフォルトは Time.now 現在日時

:host

ホストを指定します。
デフォルトはdefault_hostの値。
loginページなど、https://xxxxxなどに変更したい時などに使えると思います。

:expires

有効期限を設定可能です。

group

groupを使うことで、別ファイルで管理することができます。

サイトマップの登録

routes.rb

Googleのウェブマスターツールに /sitemapと登録させるために
routes.rbでS3のサイトマップのパスへリダイレクトさせます。

 match '/sitemap'  =>  redirect('http://backet-name.s3.amazonaws.com/sitemap/sitemap.xml.gz')

robots.txt

検索エンジンのrobot用に以下を追加

Sitemap: http://backet-name.s3.amazonaws.com/sitemap/sitemap.xml.gz

参考サイト

Goolge サイトマップを作成する

2014年10月30日木曜日

wheneverで特定のENV環境のみタスクを追加する方法

RailsのCron管理をしてくれる、Github - javan/whenever
本番環境だけとか、ステージング環境だけcronのタスクを追加という場合の対処方法です。
@environmentをcase文で分岐する方法です。
以下のサンプルコードでは、production環境では、rake ‘batch:rate’が追加されて、staging環境では、rake ‘batch:test:rate’されます。
サンプルコード
case @environment

  # 本番のみタスク
  when 'production'

    every '0 * * * *' do
      rake 'batch:rate'
    end
  when 'staging'
    every '0 * * * *' do
      rake 'batch:test:rate'
    end

end

2014年10月29日水曜日

Postgresで年代毎の人数を集計する方法

Postgresで今日現在の年齢で年代別に集計する方法です。
Userテーブルにbirthdayというtimestamp型のカラムがあると想定します。
SELECT
    CASE
        when date_part('year', age(birthday)) between 1 and 9  then 0
        when date_part('year', age(birthday)) between 10 and 19 then 10
        when date_part('year', age(birthday)) between 20 and 29 then 20
        when date_part('year', age(birthday)) between 30 and 39 then 30
        when date_part('year', age(birthday)) between 40 and 49 then 40
        when date_part('year', age(birthday)) between 50 and 59 then 50
        when date_part('year', age(birthday)) between 60 and 69 then 60
        when date_part('year', age(birthday)) between 70 and 79 then 70
        when date_part('year', age(birthday)) between 80 and 89 then 80
        when date_part('year', age(birthday)) between 90 and 99 then 90
        when date_part('year', age(birthday)) >= 100 then 100
    end AS age_group,

    count(
        case 
        when date_part('year', age(birthday)) between 1 and 9  then 0
        when date_part('year', age(birthday)) between 10 and 19 then 10
        when date_part('year', age(birthday)) between 20 and 29 then 20
        when date_part('year', age(birthday)) between 30 and 39 then 30
        when date_part('year', age(birthday)) between 40 and 49 then 40
        when date_part('year', age(birthday)) between 50 and 59 then 50
        when date_part('year', age(birthday)) between 60 and 69 then 60
        when date_part('year', age(birthday)) between 70 and 79 then 70
        when date_part('year', age(birthday)) between 80 and 89 then 80
        when date_part('year', age(birthday)) between 90 and 99 then 90
        when date_part('year', age(birthday)) >= 100 then 100
        end
    ) AS age_count
FROM user   
GROUP BY age_group
ORDER BY age_group
結果は、以下のようになります。値は適当です。
age_group age_count
0 335
10 515
20 27632
30 65276
40 12572
50 819
60 161
70 31
80 3
90 2
100 1
年代の算出に以下を使っています。
age(timestamp)
これは、現在から引数のタイムスタンプを減算します。
それを年に変換してグループ化しています。
例えば、2014-01-01時点の場合は、age(timestamp, timestamp)を使えば、算出可能です。
age('2014-01-01', birthday)

コマンドで最終更新日が1週間経過したファイルを削除する方法

logdata配下の、最終更新日が7日以上経過したファイルを削除することを想定しています。
find /logdata -maxdepth 1 -type f -ctime +7 -delete
-maxdepth : 最大何階層まで検索するか
-type : f => ファイル
-ctime : 最終更新日が、n日以前かを指定
-delete : 削除

Gitでブランチ名をリネームする方法

ローカルリポジトリ

git branch -m [old-branchname] [new-branchname]
例えば、masterをmaster-orgに変更する場合
git branch -m master master-org

リモートリポジトリ

これが本当によい方法かわかりませんが、以下のように対応しました。
  1. localでリネーム
  2. リネームしたブランチをリモートにpush
  3. リモートから元の名前のブランチを削除
例えば、masterをmaster-orgに変更する場合
git branch -m master master-org
git checkout master-org
git push origin/master-org 
git push origin :master

2014年10月28日火曜日

Gitでブランチを削除する方法

gitでhogeブランチを削除する方法。

ローカルブランチ

HEAD にマージされていないコミットがない場合

git branch -d hobe

HEAD にマージされていないコミットがある場合

強制的にブランチを削除します。

git branch -D hobe

リモートブランチ

git push origin :hoge

Rubyでランダムのパスワードを生成する方法

Rubyでランダムのパスワードを生成する方法です。
今回の使用可能文字は英字の大文字小文字、数字、記号(@-_?)を想定しており
その中からランダムで8桁の文字列を生成します。

サンプルコード

    password_array = []
    password_array.concat(('a'..'z').to_a)
    password_array.concat(('A'..'Z').to_a)
    password_array.concat((0..9).to_a)
    password_array.concat(['@','-','_','?'])
    password_array.sample(8).join

結果

上記を実行すると
b9q@zSa_
とか
ArR-hq52
のような結果が取得できます。

2014年10月25日土曜日

AWS EC2のPostgresを9.2から9.3にバージョンアップする方法

EC2とRDSの環境でRailsのdb:migrateしたら、おこられました。
pg_dump:
server version: 9.3.3; pg_dump version: 9.2.9
pg_dump: aborting because of server version mismatch
rake aborted!
ほったらかしにしておいた環境なので、気がついたら、RDSだけPostgresのバージョンが上がっていました。
そのため、EC2側のPostgresを9.3にバージョンアップすることにしました。

インストールされているPostgresの確認

    rpm -qa | grep postgres
をすると、9.2系が入ってることが確認できます。
postgresql92-libs-9.2.9-1.46.amzn1.x86_64
postgresql92-9.2.9-1.46.amzn1.x86_64
postgresql92-devel-9.2.9-1.46.amzn1.x86_64

Postgres9.2のアンインストール

9.2を削除します。
削除方法は下記。
rpm -e postgresql92-libs-9.2.9-1.46.amzn1.x86_64 postgresql92-9.2.9-1.46.amzn1.x86_64 postgresql92-devel-9.2.9-1.46.amzn1.x86_64

Postgres9.3のインストール

yum list すると、Postgresの9.3系は、以下が入っていることが確認できます。
postgresql93.x86_64            
postgresql93-contrib.x86_64    
postgresql93-devel.x86_64      
postgresql93-docs.x86_64       
postgresql93-libs.i686         
postgresql93-libs.x86_64       
postgresql93-plperl.x86_64     
postgresql93-plpython.x86_64   
postgresql93-pltcl.x86_64      
postgresql93-server.x86_64
postgresql93-test.x86_64
この中から、必要なものをインストールしていきます。
それぞれの、パッケージは以下の通りです。
パッケージ名 主に格納しているもの
postgresql-libs libpqなどのPostgreSQLのライブラリ群。PostgreSQL以外のパッケージが必要とすることもあります。
postgresql psqlやpg_dumpなどのPostgreSQLのクライアントユーティリティ
postgresql-server initdbやpg_ctlなどのPostgreSQLのサーバユーティリティ。DBMSサーバとして必要な機能はこれに含まれています。
postgresql-contrib pgbenchやpg_statstatementなどのPostgreSQLのcontribモジュール。
postgresql-devel PostgreSQLのヘッダやpg_configなどの開発用モジュール。
postgresql-doc PostgreSQLのドキュメント。
postgresql-test PostgreSQLのリグレッションテスト用モジュール。

インストール

今回、以下のリポジトリをインストールしました。
yum install postgresql93.x86_64
yum install postgresql93-devel.x86_64
yum install postgresql93-server.x86_64

インストールの確認

rpm -qa | grep postgres
postgresql93-9.3.5-1.52.amzn1.x86_64
postgresql93-devel-9.3.5-1.52.amzn1.x86_64
postgresql93-libs-9.3.5-1.52.amzn1.x86_64

参考サイト

http://lets.postgresql.jp/documents/tutorial/yum/yum
http://d.hatena.ne.jp/kasahi/20070819/1187545636

Postgresで読み取り専用のロール(ユーザー)の作成方法

Postgresで読み取り専用のロール(ユーザー)を作成します。
全のテーブルに対して、読み取り権限のみを設定します。

ロールの作成

CREATE ROLE read_only_user LOGIN REPLICATION PASSWORD 'password';
* ‘password’は任意に設定してください。

ロールの一覧確認

psqlを使っていれば
¥du
とすれば、下記のように出力されます。
Role name Attributes Member of
postgres Superuser, Create role, Create DB, Replication {}

権限の付与

GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only_user;
上記は、read_only_userschemaがpublic全テーブルSelect権限を付与します。
というような感じです。

権限の付与の確認

psqlを使っていれば
¥z
とすれば、下記の様に出力されます。
read_only_user=r/postgres となっていることを確認。
Schema Name Type Access privileges Column access privileges
public user table postgres=arwdDxt/postgres+
read_only_user=r/postgres
public book table postgres=arwdDxt/postgres+
read_only_user=r/postgres
権限の一覧は以下
r -- SELECT(読み取り(read))
w -- UPDATE(書き込み(write))
a -- INSERT (追加(append))
d -- DELETE
R -- RULE
x -- REFERENCES
t -- TRIGGER
X -- EXECUTE
U -- USAGE
C -- CREATE
T -- TEMPORARY

その他

schemaの確認

psqlを使っていれば
¥d
とすれば、下記のように出力されます。
Schema Name Type Owner
public user table postgres
public book table postgres

2014年10月24日金曜日

multi_dbを使ってレプリケーションのDBに接続を振り分ける方法

最初、レプリケーションされた、DB環境でのマスターDBトスレーブDBの接続の振り分けにOctopusを使おうか考えて色々と調べてみましたが、
スレーブでトラブルが起きた時にも、スレーブDBを参照しにいこうとしてしまうようなので、
それは、いかがなものかと思い、こちらの、Git Hub - multi_dbに切り替えました。
このmulti_dbは、スレーブが停止しても、マスターを見るように切り替わってくれるので、その点安心です。
それでは、設定方法を説明していきます。

database.ymlにスレーブの設定追加

スレーブ用のDBの接続情報をdatabase.ymlに追加します。
追加する、スレーブのdatabase.ymlでの名前の命名ルールは以下のようにします。
[environment]_slave_database[_optional_name]
例えば、production環境の1番スレーブDBの場合は、以下のようにします。
production_slave_database_1
[environment]は、下記のいずれか
  • development
  • staging
  • production
[_optional_name] は、数字がわかりやすいとおもいます。また、スレーブ一台構成の時などは、省略も可能です。
  • _1
  • _2
実際の、database.ymlは以下のようになります。
development:
  adapter: postgresql
  encoding: utf8
  database: development_master
  pool: 5
  username: postgres
  password: <%= ENV['DB_PASSWORD'] %>
  port: 5432
  host: <%= ENV['DB_HOST'] %>
  timeout: 5000

production_slave_database: # that would be a slave
  adapter: postgresql
  encoding: utf8
  database: development_slave
  pool: 5
  username: postgres
  password: <%= ENV['DB_PASSWORD'] %>
  port: 5432
  host: <%= ENV['DB_HOST'] %>
  timeout: 5000

初期化処理の追加

passenger未使用の場合

config/environments/[environment].rbのconfig.after_initializeブロックの中にMultiDb::ConnectionProxy.setup! を追加。
config.after_initialize do
  MultiDb::ConnectionProxy.setup!
end
[environment]は、下記のいずれか
  • development
  • staging
  • production

passenger使用の場合

config/initializers/connection_proxy.rbに以下を追加
if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
    if forked
      # ... set MultiDb configuration options, if any ...
      MultiDb::ConnectionProxy.setup!
    end
  end
else # not using passenger (e.g. development/testing)
  # ... set MultiDb configuration options, if any ...
  MultiDb::ConnectionProxy.setup!
end
上記を追加した場合、config/environments/[environment].rbの設定は不要になります。

マスターのみを参照するモデルの指定

UserモデルとBookモデルはマスターしか参照しないという設定にする場合、下記のようにmaster_modelsにモデル名の配列を指定する。
MultiDb::ConnectionProxy.master_models = ['User', 'Book']
MultiDb::ConnectionProxy.setup!

動作確認

これまでの設定で、rails c で
 Blog.find(1)
とすると、下記のような結果が帰ってきます。
[MULTIDB] hijacking connection for Blog
Blog Load (6.6ms)  SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = $1 LIMIT 1  [["id", 1]]

スレーブの重み付け

複数スレーブ環境の場合、各スレーブに対して重み付けもできるようです。
production_slave_database_1:
  <<: *postgres
  host: my.slavedb_1
  weight: 1

production_slave_database_2:
  <<: *postgres
  host: my.slavedb_2
  weight: 10

production_slave_database_3:
  <<: *postgres
  host: my.slavedb_3
  weight: 5
上記のようにそれぞれに、weightを設定した場合、
リクエスト数が以下のようになる様です。
production_slave_database_1 6302クエリ
production_slave_database_2 62764クエリ
production_slave_database_3 30934クエリ
※上記実際に試していないので、公式ドキュメンとそのままです。
AWSでインスタンスのスペックの違いがあるときなんかは、こちらの設定が有効活用できるような気がします。

その他

テスト環境

テスト環境は、Octopasの時も、書きましたが、読み取り専用のユーザーを作成して簡易的にテストしても良いかと思います。
詳しくは、Octopusを使ってレプリケーションのDBに接続を振り分ける方法を参照してください。
本来であれば、ちゃんとレプリケーション環境で試すのが望ましいとおもいますが、、、、

Octopusを使ってレプリケーションのDBに接続を振り分ける方法

DBがレプリケーションの構成になっている場合、更新系のクエリーをマスターDBへ、参照系のクエリーをスレーブDBへクエリを投げるようして、DBの負荷分散をさせる方法です。
RailsのGemでそれを、やってくれるGem octopus (Git Hub - tchandy/octopus) があるので、これを使った実装は説明します。

インストール

gemのインストールは、Gemfileに以下を追加して、bundle install
Gemfile
# DB master slave振り分け
# https://github.com/tchandy/octopus
gem 'ar-octopus'

DBの接続先の設定

接続先の設定は、config/shards.ymlに行って行きます。
公式のConfigファイルの設定に関するWikiはこちら

全クエリの振り分け

更新のクエリは、マスターDB、参照のクエリはスレーブDBと全てのクエリに対して設定を行う場合は、下記。
octopus:
  replicated: true
  environments:
    - development
    - staging
    - production
  development:
   development_slave:
      database: development_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  staging:
    staging_slave1:
      database: staging_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  production:
    production_slave1:
      database: production_slave1
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
    production_slave2:
      database: production_slave2
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

モデル毎の振り分け

モデル毎に、マスター、スレーブを使うかを決める場合は、こちら。
fully_replicated: false を指定する。
octopus:
  replicated: true
  fully_replicated: false
  environments:
    - development
    - staging
    - production
  development:
   development_slave1:
      database: development_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  staging:
    staging_slave1:
      database: staging_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  production:
    production_slave1:
      database: production_slave1
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
    production_slave2:
      database: production_slave2
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
config/shards.ymlの他に、modeleから、replicated_model() メソッドを呼び出します。
#This class is replicated, writes to master and reads to slave.
class Cat < ActiveRecord::Base
  replicated_model()
end

接続の確認

上記まで、設定が完了していれば、rails c で
Cat.find(1)
とすれば、 下記のような、ログがとれるはずです。
[Shard: development_slave1]  Cat Load (0.4ms)  SELECT "cats".* FROM "cats" WHERE "cats"."id" = $1 LIMIT 1  [["id", 1]]

直接DBを選択する場合

usingメソッドを呼び出します。

マスターに投げる場合

Cat.using(:master).find(1)

スレーブに投げる場合

Cat.using(:development_slave1).find(1)
または、ブロックを使って。
Octopus.using(:development_slave1) do 
 Cat.count
end

テスト環境の構築

テスト環境にもマスターDB、スレーブDBを作るのが環境的にはベストかもしれませんが、状況によって作れないこともあると思うので、
そんな時は、読み取り専用のユーザーを作るの楽かと思います。

読み取り専用ユーザーの作成

postgresでの読み取り専用ロールの作り方は以下
CREATE ROLE read_only_user LOGIN REPLICATION PASSWORD 'password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only_user;

shards.ymlの設定変更

下記のように、databaseをマスターDBに向けて、usernameをread_only_userに変更すれば、OKです。
octopus:
  replicated: true
  fully_replicated: false
  environments:
    - development
    - staging
    - production
  development:
   development_slave:
      database: development_master_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: read_only_user
      password: password
      port: 5432
      host: localhost
      timeout: 5000

障害発生時の対応

もし、マスターDBに以上が発生した場合などに、取り合えず、振り分け処理を停止したい時は - production をコメントアウトするのが有効かと思います。
octopus:
  replicated: true
  fully_replicated: false
  environments:
#    - development
#    - staging
#    - production
  development:
   development_slave:
最低限、productionのみコメントにすれば、OKかと思います。
また、開発環境やステージング環境で、レプリケーション不要なんて時もコメントアウトしておけば、振り分け処理はおこなわれません。

2014年10月21日火曜日

Postgresでストリーミング・レプリケーションの環境を構築する方法

ストリーミング・レプリケーションの構築方法を下記します。
postgresのサーバーはそれぞれ以下と仮定します。
master 10.0.4.10
slave  10.0.4.11

マスターの設定

replicatino用のuser 生成

userの作成は任意ですが、今回 repl_userを作成しました。
CREATE ROLE repl_user LOGIN REPLICATION PASSWORD 'repl_user_password';  
一般ユーザーに対して、REPLICATION権限を与えています。
※ repl_user_passwordは適宜設定してください。

pg_hba.confの設定

以下のレプリケーション用の設定を追加する。
slaveからmasterにアクセスできるように接続設定をします。
host replication repl_user 10.0.4.11/32  md5
シンタックス
host  database  user  address  auth-method
host : TCP/IPを使用した接続
database : 仮想データベースと考えてreplicationを指定
user:アクセスするuser
address : slaveのアドレス
auth-method : md5 認証時にMD5暗号化パスワードを要求
pg_hba.confファイルについてはこちら

postgresql.confの設定

postgresql.confを以下のように編集する。
wal_level = hot_standby
max_wal_sender = 2 # standby dbの数+1
# アーカイブ使用時
archive_mode = on
archive_command = 'cp "%p" /pgdata/archive/"%f"'
WALを利用して、/pgdata/archive/配下に配置することを想定しています。
ただし、このままでは、WALアーカイブがたまる一方になってしまうため、置き場のディスク容量不足を避けるため、十分に古いWALアーカイブを定期的に削除する必要があります。
WAL置き場のディレクトリも事前に作成しておきます。
mkdir /pgdata/archive
chown -R postgres:postgres /pgdata/archive
アクセスするユーザーグループはそれぞれ、postgresとしています。

postgres 再起動

上記までの設定が終わったらpostgresを再起動
/etc/init.d/postgresql restart

その他

ダンプ

念のため、masterのbumpを取っておく
pg_dump -U postgres -Fc db_name > /pgdata/db_name_$date.dump

リストア

と、いざという時に、そのリストアの方法
dropdb db_name
pg_restore -C -d db_name /pgdata/db_name_$date.dump

スレーブの設定

DBの停止

もし、DBが動いていたら、DBを一旦停止します。
/etc/init.d/postgresql stop

既存dataファイルの削除

もし、postgresのデータが存在する場合、一度、全て削除しておく。
cd /pgdata
rm -rf *

base backupの実行

以下を実行する場合、master側の/pgdata配下がpostgres権限である必要があります。
pg_basebackup -h 10.0.4.10 -p 5432 -U repl_user -D /pgdata --xlog --checkpoint=fast --progress
chown postgres:postgres -R /pgdata/
chmod 700 -R /pgdata/
-h : マスターのIPを指定
-D : postgres用のデータの配置場所を指定

postgresql.confの設定

postgresql.confを以下のように編集する。
wal_level = hot_standby
standby_mode = on

recovery.confの設定

# standbyを有効化
standby_mode = 'on'

# masterを指定
primary_conninfo = 'host=10.0.4.10 port=5432 user=repl_user password=repl_user_password'

restore_command = 'cp /pgdata/archive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /pgdata/archive %r'

postgresのデータ置き場の権限を変更

chown postgres:postgres -R /pgdata/
chmod 700 -R /pgdata/

postgres 再起動

上記までの設定が終わったらpostgresを再起動
/etc/init.d/postgresql restart

起動確認

master側

SELECT * FROM pg_stat_replication;
上記を実行して、statestreamingになっていればOK
その他の stateは以下です。
  • startup : 接続の確立中
  • backup : pg_basebackup によるバックアップの実施中
  • catchup : 過去の更新を反映中
  • streaming : 更新をリアルタイムに反映中

slave側

SELECT pg_last_xact_replay_timestamp();
上記を実行すると、処理が行われた時刻を取得することができます。

フェールオーバの方法

もし、masterに障害が発生して、フェールオーバーしたい時は、
pg_ctl promoteを実行することで、recovery.confを変更して、レプリケーションを切り離してくれます。
su - postgres -c "/usr/bin/pg_ctl promote -D /pgdata
後は、アプリケーションのmasterに向いている接続をslaveに変更すれば再稼働可能です。

Twitterのフォローボタンを追加する方法

Twitterのフォローボタンを登録するには 、下記 Twitterの公式サイトにアクセスして、ボタンを作成するのが、お手軽です。
Twitterの公式サイトはこちら
フォームからユーザーや、ユーザー名を表示の有無、ボタンの大小、言語を選択することで、HTMLのコードを作成してくれます。
それを、設置したい場所にコピペすれば終了です。

2014年10月18日土曜日

シンボリックリンクを作成する方法

よく利用する、ディレクトリの階層を深く掘ってしまった時に
カレントディレクトリにそのディレクトリのシンボリックリンクを張っておくととっても便利です。
例えば
Devlopment/Workspace/test/test_project
というようなディレクトリよく使うようなケースで
カレントディレクトリに、上記のシンボリックリンクを作成する場合
cd ~/
ln -s Devlopment/Workspace/test/test_project test_project
とすると、カレントディレクトリにtest_projectという名前でシンボリックリンクが作成されます。
なので
cd test_project
とすれば一発で移動できます。

2014年10月17日金曜日

Rubyの配列の扱い方

Rubyの配列でよく利用するメソッド

配列の定義

空の配列を定義する場合
array = []

配列に値を代入 (<<)

array = []
array << 'a'
array << 'b'
上記をやると、以下のような結果になります。
['a', 'b']

配列同士の結合 (concat)

a_array = ['a', 'b']
b_array = ['c', 'd']
a_array.concat(b_array)
a_arrayにb_arrayが結合されます。
結果は、以下のようになります。
['a', 'b', 'c', 'd']

ランダム抽出 (sample)

配列のなかから、ランダムに指定数の要素を抽出する。
a_array = ['a', 'b', 'c', 'd']
a_array.sample(2)
結果は、以下のようになります。
['c', 'b']
*実行する度に結果はかわります

重複を取り除く (uniq)

配列のなかから、重複の要素を排除して、ユニークな値の配列にします。
a_array = ['a', 'b', 'c', 'a', 'c']
a_array.uniq
結果は、以下のようになります。
['a', 'b', 'c']

ActiveRecordでhavingする方法

ActiveRecordでhavingを使いたい場合。
bookテーブルでカテゴリが同じものの、価格の合計が10000円以上のもの見たいな想定です。
Book.group('category').having('sum(price) >= 10000')
発行されるSQLは、以下のようになります。
SELECT
  book.*
FROM book
GROUP BY category
HAVING sum(price) > 10000

2014年10月16日木曜日

夜間バッチでAPIを利用して為替レートを取得する方法2

以前、Git Hub - ExchangeRatesを使って、夜間バッチで為替を取得するもの(夜間バッチでAPIを利用して為替レートを取得する方法)を作ったのですが、
アクセス回数に制限があるようで、更新に失敗するようになってしましたので、
Yahoo!のSQL(Yahoo Query Language)をつかって為替を取得するように変更しました。
他にも、CSVをダウンロードする方法もあるみたいですが、、、
http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s=USDJPY=X
今回はSQLを使いました。

YQLでJSON形式で為替を取得

JSON形式で、ドルエンの為替を取得するためのYQLのリクエストはこんな感じです。
パラメータ qに実際のクエリを書いて、データを取得する感じです。
https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22USDJPY%22)&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
結果は、以下のようになります。
{
query: {
    count: 3,
    created: "2014-10-15T02:39:54Z",
    lang: "ja",
    results: {
        rate: [
            {
                id: "USDJPY",
                Name: "USD to JPY",
                Rate: "107.075",
                Date: "10/15/2014",
                Time: "10:39pm",
                Ask: "107.10",
                Bid: "107.05"
            }
        ]
    }
}
}
以下気をつけ点として
パラメータにformat=jsonを付けて、json形式を指定する。
指定するsqlのpare in (xxxx)のxxxに複数の組み合わせを指定することで、結果も指定した分だけ帰ってきます。
例えば、USDJPY と EURJPYを取得したい場合。
select * from yahoo.finance.xchange where pair in ("USDJPY", "EURJPY")
結果は以下のようになります。
query: {
    count: 3,
    created: "2014-10-15T02:39:54Z",
    lang: "ja",
    results: {
        rate: [
            {
                id: "USDJPY",
                Name: "USD to JPY",
                Rate: "107.075",
                Date: "10/15/2014",
                Time: "10:39pm",
                Ask: "107.10",
                Bid: "107.05"
            },
            {
                id: "EURJPY",
                Name: "EUR to JPY",
                Rate: "135.3278",
                Date: "10/15/2014",
                Time: "10:39pm",
                Ask: "135.3723",
                Bid: "135.2834"
            }
        ]
    }
}
}
クエリーを確認したい場合は、YQLコンソール から確認ができます

実際のコード

実際には以下のようなコードを書きました。
前提条件として
  • rakeで実行する
  • 円とドル、ユーロ、元の3つの為替を取得する。
  namespace :batch do
    desc '為替のレートを取得する'
    task :rate => :environment do |task|
      puts "run #{task.name} with env: #{Rails.env} : #{Time.now}"

      require 'open-uri'

      begin
        Rate.transaction do
          # URL
          url = 'http://query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.xchange where pair in ("USDJPY", "EURJPY", "CNYJPY")&format=json&env=store://datatables.org/alltableswithkeys'
          url = URI.escape(url)
          source = open(url).read()
          json = JSON.parser.new(source)
          query_hash = json.parse()
          results = query_hash['query']['results']
          p "results : #{results}"

          results['rate'].each do |result_rate|
            rate_name = result_rate['id']
            p "rate_name: #{rate_name}"
            rate = Rate.find_by_ratename(rate_name)
            if rate.blank?
              rate = Rate.new
              rate.ratename = rate_name
            end
            rate_value = result_rate['Rate']
            p "rate_value: #{rate_value}"
            rate.rateval = rate_value
            rate.save!
          end

        end # Rate transaction end

        puts '全ての処理に成功しました。'
      rescue => e
        puts "error occurred batch : #{Time.now}"
        puts "error occurred batch : #{e.message}"
      end

    end
  end

git の変更を一時的に退避させる方法

退避

退避させるには
git stash [save]
save はあってもなくてもOK

退避の一覧確認

退避させた一覧を確認する場合
git stash list

退避させたものを元に戻す

退避させたものを元に戻す場合
git stash pop

2014年10月15日水曜日

rvmでrubyをinstallする方法

rubyのバージョン管理にrvmを利用する際に便利なrvmのinstall
rvmの公式ページはこちら

rvmのinstall

curl -L get.rvm.io | bash -s stable

.bashrcの編集

rvmのスクリプトを読み込むように、.bashrcに以下を追加
[ -s ${HOME}/.rvm/scripts/rvm ] && source ${HOME}/.rvm/scripts/rvm
上記設定が終わったら、以下を実行して、.bashrcを再読み込み。
source ~/.bash_profile

rvmの最新に更新

以下を実行して、rvmの最新の状態に更新
rvm get head
rvm get latest

依存関係のあるlibをインストールするように設定

rvm autolibs rvm_pkg

ruby install

ruby 1.9.3-p327の場合
rvm install ruby-1.9.3-p327
ruby-2.0.0-p481の場合
rvm install ruby-2.0.0-p481

2014年10月13日月曜日

bundle installのtiny_tdsのエラー対処

bundle installしたら、tiny_tdsのインストールで以下の様に怒られました
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /Users/name/.rvm/rubies/ruby-1.9.3-p327/bin/ruby extconf.rb
checking for iconv_open() in iconv.h... no
checking for iconv_open() in -liconv... yes
checking for sybfront.h... no
-----
freetds is missing.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/Users/name/.rvm/rubies/ruby-1.9.3-p327/bin/ruby
    --enable-lookup
    --disable-lookup
    --with-iconv-dir
    --without-iconv-dir
    --with-iconv-include
    --without-iconv-include=${iconv-dir}/include
    --with-iconv-lib
    --without-iconv-lib=${iconv-dir}/lib
    --with-freetds-dir
    --without-freetds-dir
    --with-freetds-include
    --without-freetds-include=${freetds-dir}/include
    --with-freetds-lib
    --without-freetds-lib=${freetds-dir}/lib
    --with-iconvlib
    --without-iconvlib

extconf failed, exit code 1

Gem files will remain installed in /Users/name/.rvm/gems/ruby-1.9.3-p327@restir/gems/tiny_tds-0.6.2 for inspection.
Results logged to /Users/name/.rvm/gems/ruby-1.9.3-p327@restir/extensions/x86_64-darwin-11/1.9.1/tiny_tds-0.6.2/gem_make.out
An error occurred while installing tiny_tds (0.6.2), and Bundler cannot continue.
Make sure that `gem install tiny_tds -v '0.6.2'` succeeds before bundling.
どうやら、freetdsのインストールが必要らしいです。
以下のようにインストールします。
sudo brew install freetds
それからもう一度、bundle install

公式サイト

git-hub rails-sqlserver/tiny_tdsはこちら

bundle installのrmagickでエラー対処

bundle installでrmagickのインストールでエラー発生。
brewでimagemagickを入れ直したりしたけど、全然改善せずで、
以下の方法でimagemagickをインストールし直してから
bundle installしたら成功しました。
cd /tmp
curl -OL ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
tar -xzf ImageMagick.tar.gz
cd ImageMagick-6.8.9-8/
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --disable-openmp --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install

rvmをuninstallする方法

rvmのuninstallは以下を実行します。
rvm implode
または
rm -rf ~/.rvm

2014年10月9日木曜日

RailsでCSVを生成して、そのままS3にアップロードする方法

夜間バッチなどで、定期的に外部システム用のCSVをAWSのS3にアップロードすることを想定しています。

作成方法

gemのインストール

まず、gemのインストール。
Gemfileに以下を追加。
# http://aws.amazon.com/sdkforruby/
# https://github.com/aws/aws-sdk-core-ruby
gem 'aws-sdk'
GitHubaws : aws-sdk-core-rubyはこちら。
以下を実行
bundle install

Railsへの実装

前提条件として、S3にbucketが作成済みの想定です。
      require 'csv'

      begin
        users = user.all

        data = CSV.generate do |csv|

          csv << ['id', '氏名']

          users.each do |user|
            raw = []
            raw << user.id
            raw << user.name
            csv << raw
          end
        end

        AWS.config(:access_key_id => 'your-access_key_id',
                   :secret_access_key => 'your-secret_access_key',
                   :region => 'your-bucket_region')

        s3 = AWS::S3.new
        bucket = s3.buckets['your-backet']

        file = data.sjisable.encode(Encoding::SJIS, {:invalid => :replace, :undef => :replace, :replace => '?'})
        file_name = "users.csv"
        file_full_path="user_data/#{file_name}"

        object = bucket.objects[file_full_path]
        object.write(file ,:acl => :public_read)

        puts '全ての処理に成功しました。'
      rescue => e
        puts "error occurred batch : #{Time.now} : #{e.message}"
      end
上記では、全Userのid と 氏名のカラムを持ったCSVを作成して、それを、S3にアップロードすることを想定しています。
途中こちらの部分でS3へのアクセスの設定を行っています。
 AWS.config(:access_key_id => 'your-access_key_id',
                   :secret_access_key => 'your-secret_access_key',
                   :region => 'your-bucket_region')
s3 = AWS::S3.new
bucket = s3.buckets['your-backet']
下記は適宜、自分の環境にあったものを設定してください。
  • your-access_key_id
  • your-secret_access_key
  • your-bucket_region
  • your-backet
頻繁にアクセスが発生する場合は、こちらconfig/initialyzerのしたaws.rb見たいのを作ってそちらで設定してしまってもよいかもしれません。
今回は、単発のバッチということなので、つどつど設定を行っています。

2014年10月7日火曜日

cssで横幅を超えた文字を...で省略する方法

csのみを使用して、横幅を超えた文字列を…で省略する方法
p.item_name {
         width: 100px;
         overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap;
}
widthで横幅を固定して、上記のようにcssを指定すると、100pxを超えた文字列は…で省略して表示されます。
てすと...
見たいに表示されます。

夜間バッチでAPIを利用して為替レートを取得する方法

概要

気がついたら、Googleの為替取得用のAPIが使えなくなっていたので、
その代替えとして、Git Hub - ExchangeRatesを使う事にしました。
このAPIを使うと
http://rate-exchange.appspot.com/currency?from=usd&to=jpyのurlで、下記なようなjsonが取得できます。
{
  to: "jpy",
  rate: 109.365,
  from: "usd"
}
urlのパラメータでfromとtoを変えることで、ユーロや元など他の通貨にも対応可能です。

使い方

今回やりたい事は、1日1回夜間のバッチで為替レートを取得するため、
rake で実行する、スクリプトを作成しました。
そのコードを下記します。
# -*- encoding : utf-8 -*-

namespace :batch do

  desc '為替のレートを取得する'
  task :rate => :environment do |task|
    begin
      Rate.transaction do

        # ドル
        usd_jpy_url = "http://rate-exchange.appspot.com/currency?from=usd&to=jpy"
        source = open(usd_jpy_url).read()
        json = JSON.parser.new(source)
        usd_hash = json.parse()
        puts "USD : #{usd_hash['rate']}"
        usd_rate = Rate.find_by_code('usd')
        usd_rate.rateval = usd_hash['rate']
        usd_rate.save!

        # ユーロ
        eur_jpy_url = "http://rate-exchange.appspot.com/currency?from=eur&to=jpy"
        source = open(eur_jpy_url).read()
        json = JSON.parser.new(source)
        eur_hash = json.parse()
        puts "EUR : #{eur_hash['rate']}"
        eur_rate = Rate.find_by_code('eur')
        eur_rate.rateval = eur_hash['rate']
        eur_rate.save!

        # 元
        cny_jpy_url = "http://rate-exchange.appspot.com/currency?from=cny&to=jpy"
        source = open(cny_jpy_url).read()
        json = JSON.parser.new(source)
        cny_hash = json.parse()
        puts "CNY : #{cny_hash['rate']}"
        cny_rate = Rate.find_by_code('cny')
        cny_rate.rateval = cny_hash['rate']
        cny_rate.save!

      end # Userd transaction end

      puts "success : #{Time.now}"
    rescue => e
      puts "error : #{Time.now} : #{e.message}"
    end

  end
end

putsでは下記のような値が出力されます。
"USD : 109.441"
"EUR : 137.241"
"CNY : 17.8184"
後は、これをcronに設定するば終了です。

2014年10月4日土曜日

Bloggerのサイトマップを登録する方法

Bloggerのサイトマップを登録するには、atomを使うのが楽チンです。
googleのウェブマスターツールのサイトから、サイトマップの追加テストから追加します。
記事数が500以下の場合
atom.xml?redirect=false&max-results=500
記事数が501以上に達したら、サイトマップを追加で
atom.xml?redirect=false&max-results=500&start-index=501
500件単位で、start-indexを1001 => 1501 => 2001と増やしていくとどんどんサイトマップに記事ページが送信されます。
1000件ぐらい記事を追加できるように頑張りたいもんです。

2014年10月2日木曜日

bash の脆弱性 Shell Shockに対応

bashの脆弱性が見つかったといこで、その対応のため、bushをアップデートしました。
診断手順や対策については、こちらを参考にしましたが、ざっくりと手順を下記します。

診断方法

以下のコマンドを実行してみます。
cd /tmp; rm -f /tmp/echo; env 'x=() { (a)=>\' bash -c "echo date"; cat /tmp/echo
もし、セキュリティホールが残っているようであれば/tmp/echoに日時のファイルが作成されます。

対策

sudo yum update bush
で、 yum update bush 後にサーバーを再起動したくなかったので、以下を実行。
/sbin/ldconfig
したのですが、cronが停止していることが判明しました。
/var/log/cron で logを確認したところ
FAILED to authorize user with PAM (Module is unknown)
上記のエラーメッセがでていたので
cronのデーモンを再起動して対応しました。
/etc/init.d/crond restart

2014年10月1日水曜日

Railsのcron管理 Gem wheneverの導入方法

Railsでcron管理に便利なgem wheneverの導入方法です。
公式のgitHubはこちら

gemのInstall

gemfileに以下を追加して、bundle installを実行
Gemfile
# cron
# https://github.com/javan/whenever
gem 'whenever', :require => false

設定ファイル(schedule.rb)の作成

projectのディレクトリに移動してから、設定ファイル生成コマンドを実行
$ cd /apps/[my-project]
$ wheneverize .
そうすると
./config/schedule.rb
が作成されます。

schedule.rbの作成の設定

呼び出し可能な job type

wheneverでは、下記の4つのjob typeをschedule.rbに設定することができます。
job type メソッド 使用例
bashコマンド command command “/usr/bin/some_great_command”
rakeタスク rake rake “some:great:rake:task”
Railsのメソッド runner runner “MyModel.some_method”
scriptファイル script script “script/my-script.sh”

時間の設定

3時間毎
every 3.hours do
  rake "my:rake:task"
end
毎日 AM4:30に実行
every 1.day, :at => '4:30 am' do
  rake "my:rake:task"
end
1時間毎に実行
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
  runner "SomeModel.ladeeda"
end
日曜日pm12時に実行
every :sunday, :at => '12pm' do # Use any day of the week or :weekend, :weekday
  runner "Task.do_something_great"
end
27~31日の00:00に実行
every '0 0 27-31 * *' do
  command "echo 'you can use raw cron syntax too'"
end
このcron(分,時,日,月,曜日)の書き方が慣れているので普段はこれを使っています。

crontabへの反映,削除

上記で設定した内容をcrontabへ反映したり、削除させるための、下記コマンドが用意されています。

crontabへの追加

whenever -w

crontabからの削除

whenever -c

statistics

Arsip