サーバが落ちたメモ
とりあえず備忘録として。
uptimeが半年を超えたサーバのウェブサイトが落ちた。pingは通るがsshで入れない。さくらVPSのコントロールパネルではCPUが2.0Kに振り切ったまま。5時間落ちてた。コンパネから再起動で復帰。
Linux version 2.6.18-348.12.1.el5 (mockbuild@builder10.centos.org) (gcc version 4.1.2 20080704 (Red Hat 4.1.2-54)) #1 SMP Wed Jul 10 05:28:41 EDT 2013
/var/log/messages
Feb 8 17:28:27 www*****u kernel: Mem-info: Feb 8 17:30:21 www*****u kernel: Node 0 DMA per-cpu: Feb 8 17:30:22 www*****u kernel: cpu 0 hot: high 0, batch 1 used:0 Feb 8 17:30:25 www*****u kernel: cpu 0 cold: high 0, batch 1 used:0 Feb 8 17:30:26 www*****u kernel: cpu 1 hot: high 0, batch 1 used:0 Feb 8 17:30:30 www*****u kernel: cpu 1 cold: high 0, batch 1 used:0 Feb 8 17:30:49 www*****u kernel: Node 0 DMA32 per-cpu: Feb 8 17:30:50 www*****u kernel: cpu 0 hot: high 186, batch 31 used:33 Feb 8 17:30:50 www*****u kernel: cpu 0 cold: high 62, batch 15 used:15 Feb 8 17:30:51 www*****u kernel: cpu 1 hot: high 186, batch 31 used:30 Feb 8 17:30:53 www*****u kernel: cpu 1 cold: high 62, batch 15 used:48 Feb 8 17:30:55 www*****u kernel: Node 0 Normal per-cpu: empty Feb 8 17:31:48 www*****u kernel: Node 0 HighMem per-cpu: empty Feb 8 17:33:40 www*****u kernel: Free pages: 6956kB (0kB HighMem) Feb 8 17:39:58 www*****u kernel: Active:127037 inactive:102689 dirty:0 writeback:0 unstable:0 free:1739 slab:6080 mapped-file:1155 map ped-anon:229203 pagetables:13134 Feb 8 17:41:48 www*****u kernel: Node 0 DMA free:2948kB min:36kB low:44kB high:52kB active:0kB inactive:0kB present:9684kB pages_scann ed:0 all_unreclaimable? yes Feb 8 17:42:01 www*****u kernel: lowmem_reserve[]: 0 994 994 994 Feb 8 17:42:02 www*****u kernel: Node 0 DMA32 free:4008kB min:4012kB low:5012kB high:6016kB active:507904kB inactive:410872kB present: 1018072kB pages_scanned:3244940 all_unreclaimable? yes Feb 8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0 Feb 8 17:42:02 www*****u kernel: Node 0 Normal free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 a ll_unreclaimable? no Feb 8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0 Feb 8 17:42:02 www*****u kernel: Node 0 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scan ned:0 all_unreclaimable? no Feb 8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0 Feb 8 17:42:02 www*****u kernel: Node 0 DMA: 5*4kB 4*8kB 5*16kB 4*32kB 2*64kB 2*128kB 1*256kB 0*512kB 2*1024kB 0*2048kB 0*4096kB = 2948kB Feb 8 17:42:02 www*****u kernel: Node 0 DMA32: 104*4kB 7*8kB 1*16kB 16*32kB 15*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 4008kB Feb 8 17:42:02 www*****u kernel: Node 0 Normal: empty Feb 8 17:42:02 www*****u kernel: Node 0 HighMem: empty Feb 8 17:42:02 www*****u kernel: 6961 pagecache pages Feb 8 17:42:02 www*****u kernel: Swap cache: add 9457575874, delete 9457570200, find 2144320127/3685532428, race 1898+6301 Feb 8 17:42:02 www*****u kernel: Free swap = 0kB Feb 8 17:42:02 www*****u kernel: Total swap = 2048276kB Feb 8 17:42:02 www*****u kernel: Free swap: 0kB Feb 8 17:42:02 www*****u kernel: 262141 pages of RAM Feb 8 17:42:02 www*****u kernel: 5628 reserved pages Feb 8 17:42:02 www*****u kernel: 25981 pages shared Feb 8 17:42:02 www*****u kernel: 5768 pages swap cached Feb 8 17:42:02 www*****u kernel: Out of memory: Killed process 9706, UID 48, (httpd). Feb 8 17:42:02 www*****u kernel: httpd invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0 Feb 8 17:42:02 www*****u kernel: Feb 8 17:42:02 www*****u kernel: Call Trace: Feb 8 17:42:02 www*****u kernel: [<ffffffff800cb2ee>] out_of_memory+0x8e/0x2f3 Feb 8 17:42:02 www*****u kernel: [<ffffffff8002e4c7>] __wake_up+0x38/0x4f Feb 8 17:42:02 www*****u kernel: [<ffffffff8000f691>] __alloc_pages+0x27f/0x308 Feb 8 17:42:02 www*****u kernel: [<ffffffff80013081>] __do_page_cache_readahead+0x96/0x17b Feb 8 17:42:02 www*****u kernel: [<ffffffff800139be>] filemap_nopage+0x14c/0x360 Feb 8 17:42:02 www*****u kernel: [<ffffffff80008972>] __handle_mm_fault+0x1fd/0x103b Feb 8 17:42:02 www*****u kernel: [<ffffffff800183d7>] do_sync_write+0xc7/0x104 Feb 8 17:42:02 www*****u kernel: [<ffffffff8006719c>] do_page_fault+0x499/0x842 Feb 8 17:42:02 www*****u kernel: [<ffffffff800a3c28>] autoremove_wake_function+0x0/0x2e Feb 8 17:42:02
ChordWikiにレコメンデーション機能追加
ChordWikiに協調フィルタリングのレコメンデーション「この曲を見た人はこんな曲も見ています」を付ける。Cicindelaは要求スペックが高いとのことでさくらVPSのメモリ1Gでは無理だと思われ、Recommendifyで挑む。Rubyだし。
よくわからん。
情報というかコードも本家の他にこことかここくらいしか見当たらない。
class Recommender < Recommendify::Base max_neighbors 50 input_matrix :chordwiki, :similarity_func => :jaccard, :weight => 5.0 end recommender = Recommender.new
データ追加時も結果取得時も毎回このクラスを定義してnewするの? その先はRedisだからそういうものかもしれない。
max_neighborsとweightがどう使われているのかも読み取れない。レコメンド難しい。
本家サンプルに倣って
recommender.chordwiki.add_set(user_id, items)
して、最後に
recommender.process!
する。user_idはリモートホスト。
結果取得はページ表示時にまたnewして
recommender.for(t).slice(0,5).each
する。chordwikiはどこ行った?と思うが、Redisのキーはrecommendify:chordwiki:itemsとrecommendify:chordwiki:ccmatrixとrecommendify:similaritiesが作られていて、結果はrecommendify:similaritiesを読めばいいらしい。じゃぁ複数のテーブルを持ちたくなったらどうするの?と思うがとりあえず関係ないのでスルー。
リアルタイムでデータ追加することはやめて、Apacheログから投入する。ログを保存してある4週間分を入れて、あとは週一回のcronを設定。1週間分で900MB程のログファイルの処理に数時間かかる。
負荷が心配だけど今のところ問題ない。
RedisのさくらVPSでの起動設定はこのへん参照した。
それでもなおレコメンド結果としては満足の行く出力が得られているとは言い難い。的外れな結果は出ていないが、アクセスが充分あるページに対しても結果0件ということが多い*1。アルゴリズムやパラメータの問題かもしれないが、改良する知識がない。
協調フィルタリングによるレコメンデーションについては広告方式の無料ASPがあっても良いと思っていたけど、必要なデータ量と計算量の大きさを目の当たりにすると、難しいことがよく分かった。
紀伊國屋書店アフィリエイトコード生成ツール
昔作って結局自分でも使わないから放置していたのだけど、紀伊國屋書店のサイトがリニューアルしたことと、Amazonのアソシエイト料率が変わったことを受けて、作り直した。
料率はこうか。
カテゴリ | Amazon | 紀伊國屋書店 |
---|---|---|
本 | 3% | 3% |
電子書籍 | 3.5%-8% | 3% |
DVD・CD | 2% | 3% |
紀伊國屋書店は自分買い(ショッパーズクラブ)ができて5%、さらにポイント1%(提携カードで2%)が付くように見える。
だが料率の問題だけでなく、Amazonアソシエイトと比べて紀伊國屋書店はアフィリエイトコード取得環境が整備されていないので非常に手間がかかる。自動生成する仕組みを構築しないと、ブログに気軽に貼り付けるにはハードルが高い。このツールで少しは軽減できるけど、既に会員になっているかどうかという点でも差が大きいので、結局は収益化できず、使えない。
画像のURLも以前と変わっていて和書以外についても生成するのが面倒で、OGPが設定されているから取得するようにしたけど、APIがないと根本的にどうにもならん。
ブックマークレットも書いておくか。
javascript:(function(){var l=location.hostname;var u='http://worris3.sakura.ne.jp/KinoMyLink/?isbn=';if(l.match(/amazon/)){u=u+document.getElementById('ASIN').value;}else if(l.match(/kinokuniya/)){u=u+location.pathname.split(/dsg-\d\d-/)[1]}else{u=u+prompt('ISBN');}location.href=u;})();
新刊全点RSSフィードからジャンル別に取り出すYahoo! Pipe
bk1から移行したhontoが評判悪いが、中でも日別の新刊案内が消えたのが大きいらしい。
昔作った新刊全点RSSフィードもスクレイピングの修正が必要となり、遅ればせながら対応した。
http://feeds.feedburner.com/TRCNewArrival
さらにジャンル別に見たいという人もいるっぽいのでYahoo! Pipesをpublishしておく。
http://pipes.yahoo.com/worris/trcnewarrival
いっそキーワードで取り出す。
http://pipes.yahoo.com/worris/trcnewarrival2
RSSで読むのなんか最近は流行らないのかなぁ。
コミックはもともと含まれていません。
ChordWikiのサーバをさくらのVPSに移転
前から脳内で計画とシミュレーションしてたけどむしゃくしゃしてたので実行した。
さくらのクラウドがリリースされるのを待ってみたけど、よく見たら必要なかった。
目的
アクセス負荷対策
さくらのレンタルサーバをライトからプレミアムに移行したのが2010年11月で90万PV/月くらいだったが、最近は180万PV/月まで来ていて503 Service Temporarily Unavailableが結構出ている。Webalizerで見ると3%台前半。他のサービスも共存してるけど。
304 Not Modifiedを明示的に返したりrequireを減らすために一ファイルに突っ込んだりと負荷軽減策を打って来たが、一時的に軽減したものの焼け石に水っぽい。
そしてさくらのレンタルサーバでは同一ファイルに対するアクセス制限が課せられているという話が本当なら影響が大きいシステムなので、引っ越せば軽減できると考えた。
副産物
独自ドメイン
chordwiki.orgを取った。
URL正規化
mod_rewriteの使用もコード変更多そうだから躊躇していたがそうでもなかった。代表ページのpermalinkだけ正規化して編集ページなどはパラメータを使うところや、サブドメインjaを無駄に使うなど、もろもろWikipediaに倣う。
ランキングをGoogle Analyticsに
これまで自分でカウントしていたが、上記のUTF-8化でGoogle Analyticsから生成できるようになった。
未達成
*1:COMPLETEDって書いてあるけど嘘。
Ruby から multipart/form-data を OAuth で POST する
Twitter の背景画像を日替わりで変えたくてAPIを叩こうと思い、ググったところ失敗報告しか見つからなかったけど、適当にコピペしてたら出来た。
require 'open-uri' require 'rubygems' require 'oauth' $CONSUMER_KEY = 'xxxxxxxx' $CONSUMER_SECRET = 'xxxxxxxxxxxxxxxx' $consumer = OAuth::Consumer.new( $CONSUMER_KEY, $CONSUMER_SECRET, :site => 'http://api.twitter.com' ) $ACCESS_TOKEN = 'xxxx-xxxxxxxx' $ACCESS_TOKEN_SECRET = 'xxxxxxxxxxxxxxxx' $access_token = OAuth::AccessToken.new( $consumer, $ACCESS_TOKEN, $ACCESS_TOKEN_SECRET ) url = URI.parse("http://api.twitter.com/1/account/update_profile_background_image.json") Net::HTTP.new(url.host, url.port).start do |http| req = Net::HTTP::Post.new(url.request_uri) req["content-type"] = "multipart/form-data; boundary=myboundary" body = "" body.concat("--myboundary\r\n") body.concat("content-disposition: form-data; name=\"image\"; filename=\"bgimage.jpg\"\r\n") body.concat("\r\n") File::open("bgimage.jpg"){|f| body.concat(f.read + "\r\n") } body.concat("--myboundary\r\n") body.concat("content-disposition: form-data; name=\"tile\";\r\n") body.concat("\r\n") body.concat("true\r\n") body.concat("--myboundary--\r\n") req.body = body $consumer.sign!(req,$access_token) res = http.request(req) #puts res.body end