セイバーメトリクス -OPS編-

前回、得失点差がプラスであればあるほど勝率が高い傾向にあると書きました。 「得失点差がプラスであればあるほど」ということは、得点を多く取りかつ、失点を少なくする必要があります。 今回は得点を多くとるということについてフォーカスを当ててみようと思います。

打撃成績でよく見られる「打率」「出塁率」「長打率」「OPS」と、得点の関連性について見てみましょう。

打率

野球の打撃成績として「打率」というのがよく登場します。打率とはヒットを打つ確率(ホームランなども含まれます)のことで、以下の計算で求められます。

打率 = 安打 / 打数

ヒットを打てば打つほど(=打率が高ければ高いほど)多く得点が入るというのは理にかなっているように見えますね。得点と打率の相関関係を表すグラフを見てみましょう。

(※この記事のグラフは全て2005-2015の11年間×セパ12球団の計132チーム分のデータを元に作成しています)

f:id:t_ohtsuka:20151014011904p:plain

相関係数は0.693なので、そこそこ強い相関関係にあることがわかります。

出塁率長打率

次に出塁率長打率です。それぞれ以下の様な計算で求めることができます。

出塁率 = (安打 + 四球 + 死球) / (打数 + 四球 + 死球 + 犠飛)
長打率 = 塁打 / 打数

出塁率とはヒットやフォアボールを全てひっくるめて、打者が塁に出る確率を表したものです。極端に言うと、チーム全員の出塁率が100%の場合は、誰一人アウトにならないという無限ループ状態に突入して、永遠と点が入り続けるというホラー状態に陥ります。

長打率は、1打数あたりの塁打数の期待値を表す指標です。例えば同じヒットでも、ただのヒットとホームランでは進める塁の数が違うので、ホームランはヒット4本分と考えようというのが長打率の考え方です(打率では両方同じ1本のヒットとカウントされます)。ホームランが多い選手などは必然的に高い値となります。

こちらもグラフを見てみましょう。

f:id:t_ohtsuka:20151014011916p:plain

出塁率長打率の相関係数はそれぞれ0.726、0.813となり打率よりも強い相関関係にあることが分かります。

OPS

OPSはOn-base plus sluggingの略で、単純に出塁率長打率を足したものです。

OPS = 出塁率 + 長打率

例のごとく、こちらもグラフを見てみましょう。

f:id:t_ohtsuka:20151014011925p:plain

今までの値より更に強い相関関係があることが分かります。(相関係数は0.887)

この結果から、単純に打率よりもOPS(塁に出る確率が高い+塁打を稼ぐ能力がある)が高いほうがより多くの得点を期待できるということが分かります。

これらは既に知られている話で、有名な「マネーボール」では、当時注目されていなかった出塁率を重視したチームを作ることによりチームが快進撃を続けるという話となっております。

機会があればそのへんの話も書いてみたいと思います。

セイバーメトリクス -勝率を高めるために編-

セイバーメトリクスとは

野球は「数字のスポーツ」と言われるほど、たくさんの数字が登場するスポーツです。
セイバーメトリクスとは、これらの数字を客観的に分析して、選手の評価や戦略を考える分析手法のことです。
なお、セイバーメトリクスという言葉は、アメリカ野球学会(Society for American Baseball Research)の略称 SABR と測定基準( metrics )を組み合わせた造語です。

今回のテーマ

プロ野球の各チームは優勝することを最大の目的としています。
そのために数多く勝つこと(=勝率を高めること)が欠かせません。
勝率を高くするために何が必要か?
実は先人の分析結果より、勝率が高いチームは得失点差が大きくプラスとなる傾向にあること(逆もまた然り)が知られています。
まず各用語の説明をします。

勝率

勝率 = 勝利数 / 試合数

読んで字のごとく、チームが勝つ確率のことです。 勝率が高いチームがリーグ覇者となるため、各チームは勝率No1をめざしてシーズンを戦います。

得失点差

得失点差 = 得点 - 失点

チームの得点から失点を引いた数です。 良い値を残すためには、多くの点を取り、失点を少なくする必要があります。

分析結果

2005-2015年のセパ両リーグの各12チーム(計132チーム分)の勝率と得失点差を調べてグラフにしてみました。(縦軸が勝率、横軸が得失点差です)

f:id:t_ohtsuka:20151012222834p:plain

見ていただけたら分かる通り、勝率と得失点差に強い相関関係があることが分かります。 このように、高い勝率を残すためには点を多く取り、失点を少なくすることが重要という傾向にあるということが見て取れます。

そんなこと当たり前じゃねーか!という声が聞こえてきそうですが、イメージでそう思っていたものでも客観的に分析して見える化するのは面白いですね。

bundle gem doala

動機

ドアラの顔文字どんなんだっけ?とか、ドアラのビジネスライクな仲間のペンギンの名前なんだっけ?ということ。よくあると思います。それを解決するためにgemをつくりました。

bundle gem doala

雛形作成に bundler というgemを使います。 コマンドを叩いてみましょう。こんな感じのファイル群が生成されます。

$ bundle gem doala
Creating gem 'doala'...
      create  doala/Gemfile
      create  doala/.gitignore
      create  doala/lib/doala.rb
      create  doala/lib/doala/version.rb
      create  doala/doala.gemspec
      create  doala/Rakefile
      create  doala/README.md
      create  doala/bin/console
      create  doala/bin/setup
      create  doala/.travis.yml
Initializing git repo in /Users/ohtsuka/myapp/doala

lib配下のファイルに実際のコードを記述していきます。今回はこんな感じ

$ cat lib/doala.rb
require "doala/version"

module Doala
  def face
    '(⌒(´・△・`)⌒)'
  end

  def business_friend
    'つば九郎'
  end

  module_function :face, :business_friend
end

buildしよう

bundlerさんがrakeタスクを用意してくれているので、ありがたく使わせていただきましょう

$ rake -T
rake build          # Build doala-0.1.0.gem into the pkg directory
...

早速実行してみましょう

$ rake build
rake aborted!
WARNING:  licenses is empty, but is recommended.  Use a license abbreviation from:
http://opensource.org/licenses/alphabetical
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
ERROR:  While executing gem ... (Gem::InvalidSpecificationException)
    "FIXME" or "TODO" is not a description

Tasks: TOP => build
(See full trace by running task with --trace)

doala.gemspecのsummaryとdescriptionにTODOと含まれているとエラーとなるようです。それらを書き換えて再実行

$ rake build
doala 0.1.0 built to pkg/doala-0.1.0.gem.

うまくいきました。pkgディレクトリ配下にgemが作られています。 では動作確認をしてみましょう。 普通にgem installできます。

$ gem i pkg/doala-0.1.0.gem
Successfully installed doala-0.1.0
1 gem installed

pryで動作確認

$ pry
[1] pry(main)> require 'doala'
=> true
[2] pry(main)> Doala.face
=> "(⌒(´・△・`)⌒)"
[3] pry(main)> Doala.business_friend
=> "つば九郎"

よさそうですね

テストを書こう

テストを書きます。今回はminitestというgemを使います。 Gemfileでなくdoala.gemspecに追加してbundle installします

Make your own gem - RubyGems Guides

上記ページを参考にテストの設定を行います。これでrake testでテストが実行できるようになります

テストを書いてコマンドを叩くとテストが通ったことを確認できます

$ rake test
Run options: --seed 14895

# Running:

..

Finished in 0.000904s, 2211.3301 runs/s, 2211.3301 assertions/s.

2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

リリースしよう

rake releaseコマンドを叩きます

$ rake release
doala 0.1.0 built to pkg/doala-0.1.0.gem.
Tagged v0.1.0.
Untagging v0.1.0 due to error.
rake aborted!

...

fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin master

Tasks: TOP => release => release:source_control_push
(See full trace by running task with --trace)

色々メッセージが出てきて失敗しました。追随するブランチが設定されてなかった?(初めてこのエラーを見たのでよく分かっていませんm(_ _)m)のが原因のようです。以下のコマンドでエラーは出なくなりました

$ git push --set-upstream origin master
Branch master set up to track remote branch master from origin.
Everything up-to-date

再度チャレンジ

$ rake release
doala 0.1.0 built to pkg/doala-0.1.0.gem.
Tagged v0.1.0.
Pushed git commits and tags.
rake aborted!
ERROR:  "https://rubygems.org" is not allowed by the gemspec, which only allows "TODO: Set to 'http://mygemserver.com'"
Pushing gem to https://rubygems.org...

Tasks: TOP => release => release:rubygem_push
(See full trace by running task with --trace)

rubygemsに登録するときにはspec.metadata['allowed_push_host']あたりを消しておこう - Qiita

上記Qiitaによると、誤ってrubygemに公開されるのを避けるようになっているらしい。doala.gemspecを修正して (あと、homepageも変える必要あり)再チャレンジ

$ rake release
doala 0.1.0 built to pkg/doala-0.1.0.gem.
Tag v0.1.0 has already been created.
Pushed doala 0.1.0 to rubygems.org.

うまくリリースされた模様

doala | RubyGems.org | your community gem host

動作確認してみましょう

$ gem i doala
Fetching: doala-0.1.0.gem (100%)
Successfully installed doala-0.1.0
1 gem installed
$ pry
[1] pry(main)> require 'doala'
=> true
[2] pry(main)> Doala.face
=> "(⌒(´・△・`)⌒)"

終わりっ!!

AWSでRailsアプリを動かしてみよう計画【最終回:RDS連携とCircleCI連携】

計3回にわたってお送りした「AWSRailsアプリを動かしてみよう計画」も今回で最終回です

今回はRDS連携とCircleCI連携を目指します。以下のことが達成できれば目的達成とします

RDS連携…EC2で動いているアプリをRDS上のDBに接続する

CircleCI連携githubにpushしてテストが通ったら自動的にデプロイされるようにする

RDS連携

RDSとは

Amazon Relational Database Service (Amazon RDS) を使用すると、クラウドで簡単にリレーショナルデータベースを設定、運用、スケールできます。このサービスは、手間のかかるデータベース管理タスクをお客様の代わりに行いながら、コスト効率がよく、サイズ変更が可能な容量を提供します。これによってお客様は自身のアプリケーション開発やビジネスに集中することができます。

だそうです。訳さないでいいので楽ちん

第1回で軽く述べていますが、自動的にバックアップを取ってくれたり色々便利だそうです

現状

今まではとりあえず動かすということに注力していたため、DB周りは何も触っておらずデフォルトのsqlite3を使っていました

ちなみにRDSのインスタンスはたてており、かつPostgreSQLを導入済みとします(画面上からポチポチ出来たはずです)

EC2→RDSの接続確認

PostgreSQL導入

EC2インスタンスのコンソール上からRDSにアクセス出来るか確認してみます(以下の作業はEC2にsshで接続して行っています)

psqlコマンドで接続確認を行います。意気揚々とpsqlコマンドを叩いたらpostgresql-client-commonを入れろみたいなことを言われたので入れます

EC2側はPostgreSQLのクライアント側のみあれば良さそうなのでここを参考にインストール

$ sudo apt-get install postgresql-client-common

再挑戦

ubuntu@ip-xx-xx-xx-xx:~$ psql -h
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_CTYPE = "ja_JP.UTF-8",
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Error: You must install at least one postgresql-client-<version> package.

最下行でエラー発生、postgresql-client-<version>をインストールする(バージョン指定するの忘れてました(・ω<))

$ sudo apt-get install postgresql-client

バージョンを確認してみると9.3が入った模様

ついでにここを参考にlocaleの警告にも対応

# ~/.bashrc
export LC_ALL=en_US.UTF-8

これでpsqlコマンドが使えるようになりました

接続確認

下記のオプションを使ってEC2→RDSの接続確認を行います(オプション群に名を連ねていませんが--dbnameも指定します) 参考

Connection options:
  -h, --host=HOSTNAME      database server host or socket directory (default: "local socket")
  -p, --port=PORT          database server port (default: "5432")
  -U, --username=USERNAME  database user name (default: "ubuntu")
  -w, --no-password        never prompt for password
  -W, --password           force password prompt (should happen automatically)

実際に叩くコマンドは以下の様な感じ

$ psql --host=[HOSTNAME] --port=[PORT] --username=[USERNAME] --password --dbname=[DBNAME]

ポートが開放されていなかった模様

psql: could not connect to server: Connection timed out
        Is the server running on host "[HOSTNAME]" (xx.xx.xx.xx) and accepting
        TCP/IP connections on port 5432?

PostgreSQLのデフォルトポートである5432ポートを解き放ちます(ブラウザ上からRDSにインバウンドを追加します) 参考

ubuntu@ip-xx-xx-xx-xx:~$ psql --host=[HOSTNAME] --port=[PORT] --username=[USERNAME] --password --dbname=[DBNAME]
Password for user [USERNAME]:
psql (9.3.9, server 9.4.1)
WARNING: psql major version 9.3, server major version 9.4.
         Some psql features might not work.
SSL connection (cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.
[DBNAME]=>

接続自体はできたようですがバージョンが違うため警告が出ているようです。癪に障るのでバージョン9.4を入れて警告が出なくなったことを確認

ubuntu@ip-xx-xx-xx-xx:~$ psql --version
psql (PostgreSQL) 9.4.4

ubuntu@ip-xx-xx-xx-xx:~$ psql --host=[HOST] --port=[PORT] --username=[USERNAME] --password --dbname=[DBNAME]
Password for user [USERNAME]:
psql (9.4.4, server 9.4.1)
SSL connection (protocol: TLSv1.2, cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

[DBNAME]=>

接続確認終了

RailsアプリとPostgreSQLを接続

EC2→RDS間の接続確認ができたので、続いてRailsアプリとRDS上のPostgreSQLを接続させていきます

設定ファイルの反映

サーバのshared配下のdatabase.yml修正

# /var/www/[APPLICATION_NAME]/shared/config/database.yml

production:
  adapter: postgresql
  encoding: unicode
  database: [DBNAME]
  pool: 5
  username: [USERNAME]
  password: [PASSWORD]
  host: [HOST]
  port: [PORT]

production環境でpostgresqlを使うようにします

# Gemfile

group :dvelopment, :test do
  gem 'sqlite3'
end

gem 'pg', group: :production

bundle install後にpushしてコマンド叩くとエラー発生

$ be cap production deploy BRANCH=postgresql

(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as ubuntu@xx.xx.xx.xx: bundle exit status: 5
bundle stdout: An error occurred while installing pg (0.18.2), and Bundler cannot continue.
Make sure that `gem install pg -v '0.18.2'` succeeds before bundling.
bundle stderr: Nothing written

ググってみたらlibpq-devを入れたら良さそう

$ sudo apt-get install libpq-dev

libpq-dev導入後にcapコマンドが通るようになりました

これで反映されているはずです

データが登録されているか確認

Railsアプリにブラウザ上からアクセスしてユーザを作りDBを覗いてちゃんと作られているか確認(Userモデルがあり、ブラウザ上からcreateできるという想定です)

EC2上で以下のコマンドでDBにアクセスしてテーブル一覧確認を確認してみます

$ RAILS_ENV=production bundle exec rails db

usersというテーブルがあれば以下のような感じで表示されるはずです(※編集の都合上テーブルのフォーマットいじっています)

[DBNAME]=> \d

          List of relations
| Schema | Name  | Type  | Owner      |
|--------|-------|-------|------------|
| public | users | table | [USERNAME] |

データが入っていることを確認できると思います

[DBNAME]=> select * from users;

| id | name | email            | password   |
|----|------|------------------|------------|
| 1  | hoge | hoge@example.com | [PASSWORD] |

これでEC2インスタンス上のRailsアプリから、RDSインスタンス上のPostgreSQLに接続できるようになりました

CircleCI連携

CircleCIとは

Improve Productivity, Reduce Risk, and Scale with CircleCI

Let CircleCI help your team focus on making a great product. Speed up your testing and development cycle to improve productivity. CircleCI is flexible to run in your environment and scale with your growth. Have the peace of mind by reducing bugs and improving the quality of your application.

  • 生産性の向上、リスク低減
  • テストや開発サイクルの高速化
  • バグの減少とアプリケーションの質の向上により心の平和を保つ

ちゃんと把握しきれていませんが「テストや開発サイクル(デプロイとか?)の高速化」というのがメインの機能かなという認識です

Setup

Getting started with CircleCIにも書いてあるように容易にセットアップが完成します(ほとんどのアプリで以下の3ステップで完結するようです)

  • CircleCIにサインアップする
  • 権限を与える
  • プロジェクトをクリックする

setup後、pushしたら自動でbuildしてくれるみたいです

push後buildは淡々と進んでいきますが、bundle installで落ちました

Gem::InstallError: babosa requires Ruby version >= 2.0.0.

バージョン指定して再度挑戦したら通りました

# Gemfile
ruby '2.2.2'

buildが通ることを確認してsetup終了

デプロイさせる

今までは開発環境からcapコマンドを打っていましたが、githubにpushした時に問題(テスト失敗など)がなければ自動的にcapコマンドを実行してくれるように設定します

circle.ymlを下記記事を参照して設定します

色々設定ができるようですが、今回はmasterブランチにpushしたらデプロイが行われるようにします

machine:
  ruby:
    version: 2.2.2

deployment:
  production:
    branch: master
    commands:
      - bundle exec cap production deploy

pushしてみたら$ bundle exec cap production deployのところでbuildに失敗しました

DEBUG [887a13b5] Running /usr/bin/env [ -d ~/.rbenv/versions/2.2.2 ] as ubuntu@xx.xx.xx.xx
DEBUG [887a13b5] Command: [ -d ~/.rbenv/versions/2.2.2 ]
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as ubuntu@xx.xx.xx.xx: Authentication failed for user ubuntu@xx.xx.xx.xx

Net::SSH::AuthenticationFailed: Authentication failed for user ubuntu@xx.xx.xx.xx

Tasks: TOP => rbenv:validate

bundle exec cap production deploy returned exit code 1

(See full trace by running task with --trace) Action failed: bundle exec cap production deploy

sshの認証で失敗している様子なので~/Download/oooooootuka.pemの中身をCircleCIのSSH keysに追加して、再度pushするとデプロイまで通ったようです。push後、currentディレクトリのリンク先がpushした時刻になっていることを確認します

ubuntu@ip-xx-xx-xx-xx:/var/www/[APPLICATION_NAME]$ ls -l
total 16
lrwxrwxrwx 1 ubuntu ubuntu   47 Jun 25 06:19 current -> /var/www/[APPLICATION_NAME]/releases/20150625061845
...
...

大丈夫そう


設定とか調整する必要はありますが、これで「AWSRailsアプリを動かしてみよう計画(CircleCIのおまけ付き)」は一旦終了です。めでたしめでたし

nginxとunicorn上で動いているrailsアプリをcapistranoでデプロイする

この記事の続きです

(今回の記事は実際に試してから時間が経っているため色々と漏れがあるかもしれません)

今回の目標

nginxとunicorn上で動いているrailsアプリをcapistranoでデプロイできること

capistranoとは

Capistrano is a remote server automation tool.

It supports the scripting and execution of arbitrary tasks, and includes a set of sane-default deployment workflows.

Capistrano can be used to:

  • Reliably deploy web application to any number of machines simultaneously, in sequence or as a rolling set
  • To automate audits of any number of machines (checking login logs, enumerating uptimes, and/or applying security patches)
  • To script arbitrary workflows over SSH
  • To automate common tasks in software teams.
  • To drive infrastructure provisioning tools such as chef-solo, Ansible or similar.

Capistrano is also very scriptable, and can be integrated with any other Ruby software to form part of a large r tool.

Capistranoはリモートサーバの自動化ツール

  • Webアプリケーションのデプロイが出来る
  • ログチェックなどの監査が出来る
  • SSH経由で動く
  • タスクの自動化
  • chef-soloなどのインフラプロビジョニングツールを実行できる

ふむ、ログのチェックとかセキュリティパッチ当てたりとかは聞いたことなかったな〜

$ bundle exec cap production deployのようにコマンド一発でデプロイできます

capコマンドを実行する

capistranocapistrano-rails を導入(Gemfileに書いてbundle install

導入後$ bundle exec cap installコマンドを叩くと必要なファイル群を作成してくれます

├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
└── lib
    └── capistrano
            └── tasks

githubを参考に設定(そのままコピペしました。必要に応じて修正すべきだと思います)

# Capfile
+ require 'capistrano/rails'

# config/deploy.rb
+ set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')
+ set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')

続いてconfig配下のファイルにリポジトリやデプロイ先のサーバの設定を記述します

# config/deploy.rb
- set :application, 'my_app_name'
- set :repo_url, 'git@example.com:me/my_repo.git'
+ set :application, 'application_name'
+ set :repo_url, 'git@github.com:hogehoge/fugafuga.git'

# config/deploy/production.rb
- server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
+ server 'xx.xx.xx.xx', user: 'ubuntu', roles: %w{app db web}, my_property: :my_value

- set :ssh_options, {
-   keys: %w(/home/rlisowski/.ssh/id_rsa),
-   forward_agent: false,
-   auth_methods: %w(password)
- }
+ set :ssh_options, {
+   keys: %w(~/.ssh/oooooootuka.pem),
+   forward_agent: false,
+   auth_methods: %w(publickey)
+ }

設定終了。すわ!capコマンドを叩いたら対象サーバの/var/wwwディレクトリの権限が無いと怒られた

mkdir stderr: mkdir: cannot create directory '/var/www': Permission denied

そもそもwwwディレクトリ自体が無かったため作成して権限付与

ubuntu@ip-xx-xx-xx-xx:/var$ sudo mkdir www
ubuntu@ip-xx-xx-xx-xx:/var$ sudo chown ubuntu:ubuntu www

再度capコマンドを叩くが今度は別のエラーで怒られる

DEBUG [5687b546]        /usr/bin/env: bundle: No such file or directory

見て欲しいのと違うパスを見に行ってしまっている様子。rbenvを使っているので capistrano-rbenv をインストール

githubを参考に設定追加(バージョン以外コピペ)してcapリベンジ

# Capfile
-# require 'capistrano/rbenv'
+  require 'capistrano/rbenv'

# config/deploy.rb
+ set :rbenv_type, :user
+ set :rbenv_ruby, '2.2.2'
+ set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
+ set :rbenv_map_bins, %w{rake gem bundle ruby rails}
+ set :rbenv_roles, :all # default value

sqlite3がGemfileにありませんよと怒られる。Gemfileでgroup指定しているのが原因かと思い、外しても状況は変わらずで悩んでいたら、pushするのを忘れていました(・ω<)

Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord).

masterにpushするのもなんなので$ be cap production deploy BRANCH=hogehogeのようにブランチを指定してデプロイできるようにしてそこからpushする

# config/deploy.rb
+ set :branch, ENV['BRANCH'] || :master

push後、capコマンドが通ることを確認

unicorn, nginx連携する

下準備として、nginxやunicornで今までホームディレクトリに仮置きしていたアプリを見ていたので/var/www配下を見に行くようにする

# config/nginx.conf
- server unix:/home/ubuntu/my_new_application/tmp/sockets/.unicorn.sock fail_timeout=0;
+ server unix:/var/www/my_new_application/current/tmp/sockets/.unicorn.sock fail_timeout=0;

- root /home/ubuntu/my_new_application/public;
+ root /var/www/my_new_application/current/public;

- root /home/ubuntu/my_new_application/public;
+ root /var/www/my_new_application/current/public;

capistrano-unicorn-nginx を導入

Capistrano tasks for automatic and sensible unicorn + nginx configuraion.

Goals of this plugin:

  • automatic unicorn and nginx configuration for Rails apps
  • no manual ssh to the server required
  • zero downtime deployments enabled
  • support for single node as well as cluster deployments

Specifics:

  • generates nginx config file on the server (web role)
  • generates unicorn initializer and config files (app role) application is started automatically after server restart
  • capistrano tasks for server management, example: unicorn:restart see below for all available tasks
  • automatic load balancing setup when there are multiple app nodes

capistrano-unicorn-nginx works only with Capistrano 3!

unicorn + nginx環境で最適化したcapistranoタスクだよ☆みたいな感じ

プラグインの目的:

  • Railsアプリのための設定の自動化
  • 手動SSHは不要
  • 中断時間がない
  • 複数環境へのデプロイもサポートしている

仕様:

  • nginxの設定ファイルを生成
  • サーバ再起動後にunicornの初期化とconfigファイルの生成を自動的に始める
  • unicorn:restartなどのサーバ管理タスクが使える
  • 複数ノードの時負荷を分散してくれる?

capistrano3でのみ動作する!


Capfileやconfig配下の設定ファイルを修正

# Capfile
+ require 'capistrano/unicorn_nginx'

# config/nginx.conf
- server unix:/var/www/my_new_application/current/tmp/sockets/.unicorn.sock fail_timeout=0;
+ server unix:/tmp/unicorn.my_new_application_production.sock fail_timeout=0;

# config/unicorn.rb
- listen '/var/www/my_new_application/current/tmp/sockets/.unicorn.sock' # by default Unicorn listens on port 8080
+ listen '/tmp/unicorn.my_new_application_production.sock' # by default Unicorn listens on port 8080

上記対応で、capコマンドを叩いた後にunicornとnginxを再起動してくれるようになりました


今回は一度動作させることを確認した後でcapistranoデプロイするという手順を踏みましたが、configファイルを自動生成してくれるらしいので、最初からcapistrano-unicorn-nginxを使ったほうが良かったのかなと思ったりしました。

nginxとunicornを連携させてrailsを動かす

nginx

nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP proxy server

nginx(エンジンエックス)はHTTPサーバ、リバースプロキシサーバ、メールプロキシサーバ、そして汎用的なTCPプロキシサーバである

HTTPサーバに加えて、様々な代理サーバ(プロキシサーバ)的な役割もこなせますよ。といった感じ?

導入は容易です。ubuntuの場合、apt-get install nginxのコマンドをたたくだけで終了です。ブラウザ上からアクセスして「Welcome to nginx!」と表示されていればOKです。確認方法としてtail -f /var/log/nginx/access.logコマンドをたたいてからアクセスすると、nginx経由でアクセスできていることが確認できます。

unicorn

Unicorn is an HTTP server for Rack applications designed to only serve fast clients on low-latency, high-bandwidth connections and take advantage of features in Unix/Unix-like kernels. Slow clients should only be served by placing a reverse proxy capable of fully buffering both the the request and response in between Unicorn and slow clients.

unicornはrackアプリケーション用に設計されたHTTPサーバで、遅延の少なくコネクションの良い(?)サービスを提供し、そしてUnixUnixライクな機能を活用する。低速なクライアントはunicornとの間に要求と応答をバッファリングできるリバースプロキシを置く必要がある。

こんな感じか。分からん

こちらも導入は容易。Gemfileにgem 'unicorn'と書いてbundle installするだけです。導入後、rails sではなくunicornとコマンドを叩くと8080ポートでアプリが起動します。

連携

unicornのgithubにnginxとunicornの設定のexampleファイルとしてnginx.confunicorn.conf.minimal.rbというファイルがあります。これらのファイルを修正してnginxとunicornを連携させてrailsアプリを動かすことを目標とします。(unicornの設定ファイルに関してはunicorn.conf.rbというものもあったのですが、「とりあえず動かす」という目的から見るといじることが少なそうなminimalのほうが適切かと思いそちらを採用しました。)diffの雰囲気はこんな感じです。(homeディレクトリにtest_appというrailsアプリを作ったという想定です。一部おかしいところがあるかもしれないのであくまで雰囲気を感じ取っていただければと思います。)

各設定ファイルの置き場所ですがnginxは/etc/nginx配下(nginxのデフォルト)に、unicornrailsのconfigディレクトリ配下にそれぞれ配置しました。

diffを見ていただければわかると思うのですが、基本的にpath/to/hogehogeとなっているところを実際の値に書き換えていく作業でした。しかし以下の2点にハマりました。

1,unicorn.rbのlistenに何を指定するか

unicorn.rbのsocketパスとnginx.confのserver unix:pathを統一することで、unicornとnginxの連携が可能になるようです。

# unicorn.rb
listen '/home/doala/test_app/tmp/sockets/.unicorn.sock'

# nginx.conf
server unix:/home/doala/test_app/tmp/sockets/.unicorn.sock fail_timeout=0;

参考:http://prototype-beta.com/server/entry/12_ubuntu%E3%81%A7nginx_+_unicorn_-_%E8%A8%AD%E5%AE%9A%E7%B7%A8

2,[warn] duplicate MIME type "text/html"

nginxの再起動時(だったと思います)に上記のような警告が出ました。今ひとつ分かっていませんが"text/html"はデフォルトでgzipに変換されているらしいため、confファイルに記述しなくていいらしいです。

# config/nginx.conf
gzip_types text/plain text/html text/xml text/css # 修正前
gzip_types text/plain text/xml text/css # 修正後

参考:http://www.laddy.info/2012/08/26076/

unicorn -c config/unicorn.rbのようにcオプションで設定ファイルを指定して(unicorn.conf.minimal.rbからリネームしています)unicornを起動するとブラウザ上からrailsアプリが動いていることを確認できました。とりあえず目標は達成。

第64回全日本大学野球選手権大会決勝戦

f:id:t_ohtsuka:20150616004817j:plain

決勝のカードは早稲田大学流通経済大学

先攻は早稲田大学流通経済大学の先発マウンドは「将来の夢は公務員の150キロ右腕」生田目(なばため)。先頭の重信がヒットを放ち、2番河原が送りバントを決めたところで筆者が球場に到着、バックネット裏の2階席に陣取る。四球をはさみ4番丸子のタイムリーで早稲田大学が1点先制(早1-0流)。なおも1、3塁のピンチだったがここは生田目が後続を断ち無失点で切り抜ける。早稲田大学の先発ピッチャーは甲子園を沸かせた浦和学院出身の小島。先頭二人を打ち取るも、その後3安打1死球で2点を奪われ逆転される(早1-2流)。両投手不安定な立ち上がり。2、3回は両者無得点、しかし小島の調子が良くない。2、3回ともに得点圏にランナーを背負い、3回、5番橋川に10球粘られ四球を出したところで投手交代。右のアンダーハンド吉野がマウンドに上がり後続を断つ。得点差は1点だが積極的にランナーを動かしたり生田目投手が牽制でランナーを刺したりと流れは流通経済大学に傾きつつあった。

4回に試合が動く。1アウトから流通経済大学の8番折橋選手(応援歌「よっしゃあ漢唄角田信朗)がヒットで出塁。送りバントの後、1番高橋選手のタイムリーで追加点を挙げる(早1-3流)。その後両者5、6回は無得点。特に生田目投手は4者連続三振を奪うなど圧巻のピッチング。そして得点こそ挙げられないものの流通経済大学打線は小技や機動力で積極果敢な攻撃を見せ、引き続き流れは流通経済大学にあった。流通経済大学サイドの応援席のボルテージも高まる。

流通経済大学の勝利、そして優勝が見えてきたがそうは問屋が卸さない。7回、疲れが見えてきた生田目に早稲田打線が襲い掛かる。9番、代打渡辺のヒットをきっかけに4安打1四球を浴びせかけ3点を奪い生田目をKO、変わった小原からもタイムリーを放ちその差を3点に広げ、流れが一気に早稲田大学に傾く(早6-3流)。しかしその裏、流通経済大学打線が5回途中から登板している大竹を攻め、3番大崎のヒット、4番笹田のホームランでその差を1点まで追い上げる(早6-5流)。どちらに転んでもおかしくない試合展開だったが、9回、早稲田大学が5番石井のダメ押し2ランホームランで試合に決着をつけた(早8-5流)。その裏、大竹が流通経済大学の攻撃を3人で切って取り早稲田大学の優勝が決まった。

所感

流通経済大学のスピード感のある攻撃や、生田目選手の小気味よいピッチング、終盤の早稲田の猛攻など見どころ満載で、勝利の行方も最後まで分からない好ゲームでした。初めてスコアを書きながら観戦していたのですが、見返してみるといろんなことがわかって面白かったです。(毎打席相手投手に5球以上投げさせている選手がいたり、4打席中3回バントをしていた選手がいたり…etc)早稲田大学のランナーコーチが全力ダッシュでコーチャーズボックスに向かっているのも「学生っぽい!」という感じで印象深かったです。あと、初めてバックネット裏から観戦しましたがグラウンド全体が見れてめっちゃ良かったです。