Rails+Capistrano+Apache2.2+Mongrel_clusterのメモ

10月 17th, 2008 admin

まるまる2日間かかった。いろいろはまりすぎ。

前提

debian etch
mod_rails(passenger)はメモリを食いすぎて使えなかった
subversionを使っている
デプロイは自動化したい

OpenVZのVEの設定

まず最初に仮想マシンの容量が足りなくてRailsが起動しない。

vzctl set 2000 --privvmpages 2G --save #メモリ上限を2Gにする
vzctl set 2000 --diskspace 10G --save

インストール

サーバになにも入っていないのでどんどん入れる

aptitude install apache

aptitude install ruby
aptitude install rubygems

aptitude install mysql-server
aptitude install libmysqlclient-dev
aptitude install subvesion

gem install rails
gem install mysql
gem install gettext
gem install mongrel
gem install mongrel_cluster
gem install capistrano

#今回のアプリ用のもの
gem install ferret
gem install image_science
gem install amazon-ecs

$ unzip FreeImage3100.zip
$ cd FreeImage
$ make

Apache2.2の設定

このへんを見ながらやらせてもらった。はまったのはmod_proxyの設定で
/etc/apache2/mods-enabled/proxy.confのProxyディレクティブのDeny from allに気がつかなかったこと
conf.d以下以外に.confがあるなんて知りませんでした。

a2enmod rewrite
a2enmod proxy_balancer

でproxy_balancerとproxyを有効にする。

バーチャルホストの設定にこんなのを加える

HTML:
  1. <proxy *>
  2.                 Order deny,allow
  3.                 Deny from all
  4.                 Allow from all
  5.         </proxy>
  6.  
  7.         ProxyRequests Off
  8.         ProxyPass / balancer://appname/ timeout=2
  9.         ProxyPassReverse / balancer://appname/
  10.  
  11.         <proxy balancer://appname>
  12.                 BalancerMember http://127.0.0.1:3000 loadfactor=10
  13.                 BalancerMember http://127.0.0.1:3001 loadfactor=10
  14.                 BalancerMember http://127.0.0.1:3002 loadfactor=10
  15.                 BalancerMember http://127.0.0.1:3003 loadfactor=10
  16.         </proxy>

/etc/init.d/apache2 force-reload

でリロード

参考サイト

burn baby burn!!!(Archives)
【Rails】mongrel cluster&apacheのリバースプロキシで複数のアプリ共存

mongrel clusterの設定

これはあまりはまらなかった。

mongrel_rails cluster::configure

とやると雛形がconfig/mongrel_cluster.ymlでできるので
適当に編集して、こんなのができた

log_file: /home/appname/log/mongrel.log
port: "3000"
environment: production
address: 0.0.0.0
pid_file: tmp/pids/mongrel.pid
servers: 4

あとは

mongrel_rails cluster::start

で動くはず。

参考サイト

83s : config/mongrel_cluster.ymlの書き方、とか
Rubyist - バリケンのRuby日記 - mongrel_cluster

spinファイルの作成

capistranoを使うとファイルの展開後にscript/spinというのを実行してくれる
今回は使わないのでファイルだけ作っておく

$ vi script/spin

↓中身

#!/bin/sh

capstrano

アプリケーションのルートディレクトリで

capify .

でCapistranoのレシピといわれるファイルがconfig/deploy.rbに作られる
それをいい感じに編集

RUBY:
  1. require 'mongrel_cluster/recipes'
  2. set :application, "appname"
  3. set :repository,  "http://path to subversion"
  4.  
  5. set :user, "ssh username"
  6. set :use_sudo, false
  7. set :scm_username, "svnuser"
  8. set :scm_password, "svnpass"
  9.  
  10.  
  11.  
  12. # If you aren't deploying to /u/apps/#{application} on the target
  13. # servers (which is the default), you can specify the actual location
  14. # via the :deploy_to variable:
  15. set :deploy_to, "/var/www/#{application}"
  16.  
  17. set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
  18. set :mongrel_clean, true
  19. # If you aren't using Subversion to manage your source code, specify
  20. # your SCM below:
  21.  
  22. role :app, "192.168.1.x"
  23. role :web, "192.168.1.y"
  24. role :db,  "192.168.1.x", :primary => true
  25.  
  26. task :after_update_code, :roles => :app do
  27.   run "chmod u+x #{release_path}/script/spin"
  28. end

cap deploy:setup

でリモートに準備がされる。

cap deploy:cold

でsubversionからファイルが展開されてmongrelが再起動される

参考サイト

つくるぶガイドブログ: capistrano で本番環境にデプロイ
FFTT : Capistrano(2006年4月の情報)
第7回 Rails アプリケーションの起動・停止・再起動 - Capistrano 入門 - Ruby on Rails with OIAX

まとめ

・こんなに苦労する必要があるRailsはなにかおかしい
・Capistranoの資料がもうちょっとあればなぁと思った。
・passenger(mod_rails)は普通に動いたのでメモリがあれば全然使えそう

coreserverをリモートから自動バックアップするメモ

10月 10th, 2008 admin

coreserverというレンタルサーバのデータのバックアップをとるようにしたメモ。

シェルが使えるのでファイルとかはrsyncでとれるのだけど、DBのファイルはみれないのでmysqldump(mysqlhotcopyは使えない。)などを使ってユーザのディレクトリにデータをもってくる必要がある。シェルスクリプトをcoreserverにおいてcronでバックアップしてもいいのだけど、バックアップ関連のスクリプト類は手元で一括管理したいのでRuby経由でSSHしてmysqldumpをするようにした。

シェルが使えて年間5000円のcoreserverはすごいとおもう。

(1)DBのデータを全部ダンプ

RUBY:
  1. require 'rubygems'
  2. require 'net/ssh'
  3.  
  4. host = "ホスト名"
  5. user = "ユーザ名"
  6. key = "秘密鍵の場所"
  7. mysqldump = "/usr/local/mysql/bin/mysqldump"
  8. path = "/virtual/username/"
  9. databases = [
  10.   {:db_name => "aaaa", :id => "username", :password => "pass"},
  11.   {:db_name => "bbbb", :id => "username2", :password => "pass2"},
  12. ]
  13.  
  14.  
  15. Net::SSH.start(host, user, :keys => [key], :passphrase => "") do |ssh|
  16.   databases.each do |db|
  17.     command = sprintf('%s -u %s --password=\'%s\' %s> %s/%s.dump ',mysqldump, db[:id], db[:password], db[:db_name], path, db[:db_name])
  18.     ssh.exec!(command)
  19.   end
  20. end

(2)rsyncでファイルをローカル(バックアップマシン)に移動

CODE:
  1. rsync -av username@example.com:remote_directory local_directory

まとめ

・公開鍵認証を使っているのでSSHのパスワードは聞かれない。(作業メモ
・mysqldumpを絶対パスで指定しないと駄目らしい。

さらにローカルにrsyncされたデータをpdumpfsで毎日バックアップして、さらにpdumpsf-cleanでローテートしている。なんだか面倒なので、みんなどうやってやってるのか教えてほしいなぁ

参考サイト

Pure Ruby な SSH クライアントライブラリ「Net::SSH」 - WebOS Goodies

sshで公開鍵認証(パスフレーズ無し)のメモ

10月 6th, 2008 admin

・サーバのセキュリティを向上のため
・rsyncやRubyのスクリプトなどからパスワードなしでログインするため
・「/秘密のファイル/開くな.txtを開いてコピー&ペースト」をやめるため
の公開鍵認証をするためのメモです。

手順

sshで公開鍵認証をやりたいときにやること
(1)公開鍵と秘密鍵をつくる
(2)公開鍵をリモートに置く

作業

(1)公開鍵と秘密鍵をつくる

まずはローカルで公開鍵と秘密鍵を作ります。

CODE:
  1. $ ssh-keygen -N "" -t rsa

Nはパスフレーズの指定。今回は空にします(セキュリティはその分低下します*1)
tは鍵の種類を選ぶもの。rsa1,rsa,dsaから選べるが最近は強度が高いrsaを選ぶことが多い

上記のコマンドを実行すると$HOME/.sshに
・id_rsa(秘密鍵)
・id_rsa.pub(公開鍵)
が作られる。

(2)公開鍵をリモートに置く

リモートに公開鍵を登録するにはssh-copy-idというそれ専用のコマンドがある

CODE:
  1. ssh-copy-id toyoshi@remote_hostname

これでauthorized_keysに勝手に登録してくれます。便利!

もしくはSPCを使います。SCPコマンドは
"scp 送りたいファイル名 ユーザ名@ホスト名:ディレクトリ"
って感じで。

CODE:
  1. $ cd .ssh/
  2. $ scp id_rsa.pub toyoshi@remote_hostname:directory

んでリモートの$HOME/.sshにauthorized_keysという名前で保存する。
すでにauthorized_keysがある場合は追記するようにする。たとえばこんな感じ

CODE:
  1. $ cat id_rsa.pub>> .ssh/authorized_keys

リモートにおいたauthorized_keysはパーミッションを600とかにしておきましょう。

※ssh-copy-idを使った場合は追記の処理とパーミッションの変更は自動でやってくれます。

まとめ

これで

CODE:
  1. ssh remote_host

とかってやると、IDとパスワードの入力はもちろん、秘密鍵のパスフレーズも聞かれずにログインできるようになった。

*1・・・パスフレーズはなしにするが、パスワード認証とは違い秘密鍵のファイルが盗まれない限り大丈夫なので実際には安全性にそれほど差はないと思う。心配な場合はリモートの公開鍵にcommand="" というのを追加して公開鍵認証してきたユーザができることを制限する方法が「rsync + cron + ssh (rsyncd を立てない編)」では紹介されています。そのほかにも「ssh scp sftp の正しい自動実行方法」なども参考になります。

参考サイト

rsync + cron + ssh (rsyncd を立てない編)
ssh scp sftp の正しい自動実行方法
公開鍵暗号でのsshリモートログイン - 別館 子子子子子子(ねこのここねこ)

第3回 コンパイラを作ろう

10月 6th, 2008 admin

10月5日にCSNagoyaで第3回コンパイラを作ろうを開催しました。

第1部 コンパイラを作ろう

今回は「コンパイラ入門」の第7章である「文法定義」を読みました。この章は次から作るパーサ構築のための準備段階で、パーサの役割と、パーサ構築のために必要な文法変換という作業とその方法について学ぶことができました。
うれしいことに僕はここまで読み進めてきて「コンパイラ=無理難題」みたいな先入観がなくなりました。もちろん凄いコンパイラは凄いことをやってるんだろうけど、基本原理としてはソースコードを受け取って、それを一定の規則にしたがって変換するだけのフィルタなんだなと、やるまえに散々M氏に言われていたことがインストールされたようです。

第2部 ライトニングトーク

SATソルバで解く4クイーン問題

SATソルバというものを初めて聞きました、SAT(充足可能性問題)とは、ある命題(乗法標準形)の値を真にできるかどうかという問題のことで、それを解くプログラムをSATソルバというそうです。いかにも数学という感じでSAT自体には興味はわきませんでしたが、それに興味を持っている発表者にすごく興味がわきました。自分が全然知らない話を聞くのは面白いです。

全手動マリオの解説

一部で有名な全手動マリオなどを作っている方本人が技術解説をしてくださいました。この動画を初めて見たときは「面白いことを考える人がいるもんだなー」と感心していたのですが、まさか直接話を聞かせてもらえるなんて思ってもいなかったので感激しました。新作動画の「棒歌ロイドが家の中でゆっくりしていたんだけど・・・」も力作過ぎます。

CSNagoyaの今後について

まず現在は隔週で「コンパイラを作ろう」と「CPUの創りかた」をやっており、「CPUの創りかた」の後半でverilog勉強会をしているという状態です。
all1.gif
んで上記の図が今後の予定です。もうCSNagoyaが会社なら絶対に投資してはいけない感じになってます。
今後SICP読書会とVerilog入門をスタートすることが決まっています。

ただし、あんまり勉強ばっかりしても意味がないし休日が潰れすぎてもいけないので、僕も参加しないセッションを決めたり平日の開催にするなどして上手に調整できたらなと思ってます。

新しく始める予定のSICP読書会とverilog入門については少しあとに正式に参加者の募集を行いたいと思います。

第2回コンパイラを作ろう スキャナ構築編

10月 4th, 2008 admin

9月21日にCSNagoyaで第2回コンパイラを作ろうを開催しました。
ブログでの報告が遅れたのは宿題であるスキャナの作成が遅れたからです・・・

第1部 コンパイラを作ろう

「コンパイラ入門」という本の6章「スキャナの構築」を読みながら実際にスキャナのコードを書きました。
スキャナとはコンパイラの構成要素の一つで、コードを読み込んで、それをプログラムの最小構成であるトークンに分解するものです。echo 'Hello';というコードがあった場合、["echo","Hello",";"]という感じにコードを分解します。

コード

僕はRubyで書きました。

RUBY:
  1. class Token
  2.   attr_accessor :def, :str, :type
  3.  
  4.   def initialize(fp)
  5.     @def = ""
  6.     @str = ""
  7.     @type = ""
  8.  
  9.     @fp = fp
  10.   end
  11.  
  12.   def get_token
  13.     @def = "EOF"
  14.     @str = ""
  15.     @type = "EOF"
  16.     c = get_character
  17.     while(is_white_space(c))
  18.       c = @fp.read(1)
  19.     end
  20.  
  21.     if(c == ";") then @def = "SEMICOLON"; @str = c;end
  22.     if(c == ",") then @def = "COMMA";     @str = c;end
  23.     if(c == "(") then @def = "OPEN";      @str = c;end
  24.     if(c == ")") then @def = "CLOSE";     @str = c;end
  25.     if(c == ".") then @def = "PERIOD";    @str = c;end
  26.     if(c == "+") then @def = "PLUS";      @str = c;end
  27.     if(c == "-") then @def = "MINUS";     @str = c;end
  28.     if(c == "*") then @def = "MULT";      @str = c;end
  29.     if(c == "/") then @def = "DIV";       @str = c;end
  30.     if(c == "=") then @def = "EQ";        @str = c;end
  31.  
  32.     if(c == ":")
  33.       @str = @str + c
  34.       c = get_character
  35.       if(c == "=")
  36.         @def = "ASSIGN"
  37.         @str = @str + c
  38.       else
  39.         @def = "COLON"
  40.         unget_character(c)
  41.       end
  42.     elsif(c == ">")
  43.       @str = @str + c
  44.       c = get_character
  45.       if(c == "=")
  46.         @def = "GE"
  47.         @str = @str + c
  48.       else
  49.         @def = "GT"
  50.       end
  51.     elsif(c == "<")
  52.       @str = @str + c
  53.       c = get_character
  54.       if(c == "=")
  55.         @def = "LE"
  56.         @str = @str + c
  57.       elsif(c == ">")
  58.         @def = "NE"
  59.         @str = @str + c
  60.       else
  61.         @def = "LT"
  62.       end
  63.     elsif(c == '"')
  64.       c = get_character
  65.       until(c == '"')
  66.         @str = @str + c
  67.         c = get_character
  68.       end
  69.       @def = "STR"
  70.     elsif(is_digit(c))
  71.       while(is_digit(c))
  72.         @str = @str + c
  73.         c = get_character
  74.       end
  75.       unget_character(c)
  76.       @def = "NUMBER"
  77.  
  78.     elsif(is_alpha(c))
  79.       while(is_alpha(c) || is_digit(c))
  80.        @str = @str + c
  81.        c = get_character
  82.       end
  83.       unget_character(c)
  84.      
  85.       @def = "IDENT"
  86.       @def = "MODULE"     if @str == "MODULE"
  87.       @def = "BEGIN"      if @str == "BEGIN"
  88.       @def = "END"        if @str == "END"
  89.       @def = "VAR"        if @str == "VAR"
  90.       @def = "INTEGER"    if @str == "INTEGER"
  91.       @def = "STRING"     if @str == "STRING"
  92.       @def = "IF"         if @str == "IF"
  93.       @def = "THEN"       if @str == "THEN"
  94.       @def = "ELSE"       if @str == "ELSE"
  95.       @def = "WHILE"      if @str == "WHILE"
  96.       @def = "DO"         if @str == "DO"
  97.     elsif(is_EOF(c))
  98.       @def = "EOF"
  99.     end
  100.   end
  101.  
  102.   def get_character
  103.     c = @fp.read(1)
  104.     return c
  105.   end
  106.  
  107.   def unget_character(c)
  108.     return @fp.ungetc(c[0]) unless c.nil?
  109.   end
  110.  
  111.  
  112.   def is_EOF(i)
  113.     return true if i.nil?
  114.   end
  115.  
  116.   def is_white_space(i)
  117.     return true if i =~ /\s/
  118.   end
  119.  
  120.   def is_digit(i)
  121.     return true if i =~ /[0-9]/
  122.   end
  123.  
  124.   def is_alpha(i)
  125.     return true if i =~ /[a-zA-Z]/
  126.   end
  127. end
  128.  
  129.  
  130.  
  131.  
  132.  
  133. token = Token.new(open(ARGV[0],"r"))
  134. token.get_token
  135. until token.def == "EOF"
  136.  puts "DEF[" + token.def + "] \t\t STR[" + token.str + "]"
  137.  token.get_token
  138. end

これにたとえばこんなコードを食わせる

RUBY:
  1. MODULE HelloWorld;
  2. BEGIN
  3.   VAR = 1 + 100 * 2
  4.   if (VAR>= 100)
  5.     WriteStr("Hello World!")
  6.   END
  7. END HelloWorld.

と、それぞれがトークンに分解されて出力されます。

RUBY:
  1. DEF[MODULE]              STR[MODULE]
  2. DEF[IDENT]               STR[HelloWorld]
  3. DEF[SEMICOLON]           STR[;]
  4. DEF[BEGIN]               STR[BEGIN]
  5. DEF[VAR]                 STR[VAR]
  6. DEF[EQ]                  STR[=]
  7. DEF[NUMBER]              STR[1]
  8. DEF[PLUS]                STR[+]
  9. DEF[NUMBER]              STR[100]
  10. DEF[MULT]                STR[*]
  11. DEF[NUMBER]              STR[2]
  12. DEF[IDENT]               STR[if]
  13. DEF[OPEN]                STR[(]
  14. DEF[VAR]                 STR[VAR]
  15. DEF[GE]                  STR[>=]
  16. DEF[NUMBER]              STR[100]
  17. DEF[CLOSE]               STR[)]
  18. DEF[IDENT]               STR[WriteStr]
  19. DEF[OPEN]                STR[(]
  20. DEF[STR]                 STR[Hello World!]
  21. DEF[CLOSE]               STR[)]
  22. DEF[END]                 STR[END]
  23. DEF[END]                 STR[END]
  24. DEF[IDENT]               STR[HelloWorld]
  25. DEF[PERIOD]              STR[.]

第2部 サーバ/インフラ構築入門 -冗長化の基本-

id:dominion525氏による講義。前回のWebサーバの冗長化の話の続きでルータやロードバランサの冗長化の話でした。

まとめ

各自が好きな言語で実装しているので人のコードがあまり参考にならないのが辛いなと思いました。まぁその分他の人のコードを見るのが楽しみです。
次回の勉強会は明日!みんな宿題はやってくるでしょうか