DRY

Web関連の技術の事、食事/お酒の事、旅行の事など

Vagrant(CentOS6.5)にchefでRails4.1 + Ruby2.1.2 + Nginx + Unicorn + MySQL環境を作る

何かいつも同じ事やってる気がするので自分のためにもまとめておきます。

  1.  Vagrant のインストール
    公式サイトから最新版(執筆時は1.6.3)をダウンロードしてインストール
    Vagrant
  2. VirtualBox を公式サイトからインストール
    公式サイトから最新版(執筆時は4.3.12)をダウンロードしてインストール

    Oracle VM VirtualBox

  3. Boxの追加
    $ vagrant box add centos65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
    ==> box: Adding box 'centos65' (v0) for provider:    box: Downloading: https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box||<==> box: Successfully added box 'centos65' (v0) for 'virtualbox'!
    その他のBoxは下記にあるので、そこから別のBOXをダウンロードしてインストールしてもOK

    A list of base boxes for Vagrant - Vagrantbox.es

    作成出来たBOXを一応確認
    $ vagrant box list
    centos65 (virtualbox, 0)
  4. Vagrantfileの作成
    どこか任意の場所にディレクトリ作成
    $ mkdir -p /vagrant/develop
    $ cd /vagrant/develop
    $ vagrant init
    $ vi Vagrantfile

    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2"
    Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    config.vm.box = "centos65"

    config.vm.network "private_network", ip: "192.168.33.11"
    config.vm.synced_folder "./data", "/vagrant_data"

    #config.vm.network :forwarded_port, guest: 8080, host: 8080

    config.vm.provider :virtualbox do |vb|
    # Use VBoxManage to customize the VM. For example to change memory:
    vb.customize ["modifyvm", :id, "--memory", "512"]
    vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
    vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
    end

    Encoding.default_external = 'UTF-8'
    end
    private_networkはお好きなIPでどうぞ
    vb.customizeも別に無くてもいいと思います。

    フォワードしたいポートがあれば、以下のようにコメントアウト外して記載してください

    #config.vm.network :forwarded_port, guest: 8080, host: 8080

     

  5. Syncフォルダの生成

    # config.vm.synced_folder "./data", "/vagrant_data"

    この記載でここのdataフォルダをvagrant内の/vagrant_dataに結び付けたいのでディレクトリを作成しておきます
    $ mkdir data

  6. Vagrantの起動
    $ vagrant up

    Bringing machine 'default' up with 'virtualbox' provider...
    ==> default: Fixed port collision for 22 => 2222. Now on port 2200.
    ==> default: Clearing any previously set network interfaces...
    ==> default: Preparing network interfaces based on configuration...
        default: Adapter 1: nat
        default: Adapter 2: hostonly
    ==> default: Forwarding ports...
        default: 8080 => 8080 (adapter 1)
        default: 22 => 2200 (adapter 1)
    ==> default: Running 'pre-boot' VM customizations...
    ==> default: Booting VM...
    ==> default: Waiting for machine to boot. This may take a few minutes...
        default: SSH address: 127.0.0.1:2200
        default: SSH username: vagrant
        default: SSH auth method: private key
        default: Warning: Connection timeout. Retrying...
    ==> default: Machine booted and ready!

    Vagrantにログイン出来るかを確認

    $ vagrant ssh

    [vagrant@vagrant-centos65 ~]$

    SSHが確認できたら一度またホストに戻ります。

  7. SSHの設定 

    起動したVMをknife-solo(後述)から簡単に扱えるようにするため、ssh-configを~/.ssh/configに追記しておく。
    $ vagrant ssh-config --host development >> ~/.ssh/config
    $ cat ~/.ssh/config

    Host development
      HostName 127.0.0.1
      User vagrant
      Port 2200
      UserKnownHostsFile /dev/null
      StrictHostKeyChecking no
      PasswordAuthentication no
      IdentityFile /Users/YOUR_ID/.vagrant.d/insecure_private_key
      IdentitiesOnly yes
      LogLevel FATAL

    普通にSSHも出来る事を確認

    $ ssh development

    Last login: Wed Jul 16 04:53:22 2014 from 10.0.2.2
    [vagrant@vagrant-centos65 ~]$ logout
    Connection to 127.0.0.1 closed.

     

  8. ホストにknife-soloのインストール
    $ gem install knife-solo
    $ knife configure

    knife configureを実行すると色々聞かれるが、とりあえずは全部デフォルトで

  9. knife-soloを使ってChefレポジトリを初期化
    $ knife solo init chef-repo

    # vagrant_develop用のcookbookの雛形を生成
    $ cd chef-repo

    # yumパッケージ関係
    $ knife cookbook create yum -o site-cookbooks

    # nginxインストールのため
    $ knife cookbook create yum-epel -o site-cookbooks

    # nginx
    $ knife cookbook create nginx -o site-cookbooks

    # git
    $ knife cookbook create git -o site-cookbooks

    # ps axu | grep mysql + ruby
    $ knife cookbook create rbenv-ruby -o site-cookbooks

    # MySQL
    $ knife cookbook create mysql -o site-cookbooks

    # iptables止めるため
    $ knife cookbook create iptables -o site-cookbooks

    全レシピはここに置いてあります。

    chef-Rails4.1-Ruby2.1.2-Nginx-Unicorn-MySQL/chef-repo at master · k1row/chef-Rails4.1-Ruby2.1.2-Nginx-Unicorn-MySQL · GitHub


    全体的なディレクトリ構成はこんな感じです。

    $ pwd
    /vagrant/develop

    [/vagrant/develop]$ ls

    Vagrantfile    chef-repo/    data/

    [/vagrant/develop]$ cd chef-repo/

    [/vagrant/develop/chef-repo]$ ls

    Berksfile    cookbooks/    data_bags/    environments/    nodes/        roles/        site-cookbooks/

    [/vagrant/develop/chef-repo]$ cd site-cookbooks/

    [/vagrant/develop/chef-repo/site-cookbooks]$ ls

    git/        iptables/    mysql/        nginx/        rbenv-ruby/    yum/        yum-epel/
  10. nodes/development.jsonを作成し編集する。
    $ vi /vagrant/develop/chef-repo/nodes/development.json{

      "run_list": [
            "recipe[yum]",
            "recipe[yum-epel]",
            "recipe[git]",
            "recipe[nginx]",
            "recipe[mysql]",
            "recipe[rbenv-ruby]",
            "recipe[iptables]"
      ]
    }

    ※このレシピをVagrant以外のサーバにも適用したくなるかもなので、Vagrantfile内には記載しない方式にしました

  11. knife soloの準備
    $ knife solo prepare development

    Bootstrapping Chef...


    Preparing...                ########################################### [100%]
       1:chef                   ########################################### [100%]
    Thank you for installing Chef!

     

  12. chefの実行
    $ knife solo cook development
    20分ぐらいたぶんかかります。

    # まっさらな状態からプロビジョニングし直したい場合
    $ vagrant destroy -f && vagrant up && knife solo prepare development && cd chef-repo && knife solo cook development

  13. インストール状況の確認
    vagrantに入って諸々のインストール状況を確認してみます
    $ vagrant ssh

    $ ps axu | grep nginx

    vagrant   5495  0.0  0.1 103240   852 pts/0    S+   05:56   0:00 grep nginx
    root      9075  0.0  0.3  97180  2008 ?        Ss   05:43   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
    nginx    21216  0.0  0.4  97568  2352 ?        S    05:44   0:00 nginx: worker process


    $ ps axu | grep mysql
    vagrant   5497  0.0  0.1 103240   852 pts/0    S+   05:56   0:00 grep mysql
    root     20302  0.0  0.0 108164    72 ?        S    05:44   0:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pid-file=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
    mysql    20552  0.1 10.5 1075884 53092 ?       Sl   05:44   0:01 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock
    $ rbenv versions
    * 2.1.2 (set by /usr/local/rbenv/version)
    $ ruby -v
    ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]

    という事で良さそうに見えます。

  14. Railアプリの設定
    実はここで http://192.168.33.11/ を見ても 404 Not Found なのでRailsアプリを新規で作成してみます。

    実際はMacにもRuby on Railsの同じ環境が入っている人が多いと思いますのでMac上というかホストサイドから直接やってもいので、syncディレクトに設定した /vagrant_data にRailsアプリを作りたいと思います。

    実はそのためにchefでnginxのドキュメントルートの設定を /vagrant_data/public/ にしてありました。

    $ cat /etc/nginx/conf.d/192.168.33.11.conf

    upstream unicorn {
      server unix:/tmp/unicorn.sock fail_timeout=0;
    }

    server {
        listen 80;
        server_name 192.168.33.11;
        root /vagrant_data/public/;


        location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;

            if (!-f $request_filename) {
                proxy_pass http://unicorn;
                break;
            }
        }

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

    vagrant上にて
    $ cd /vagrant_data/
    $ bundle init

    Writing new Gemfile to /vagrant_data/Gemfile


    $ vi Gemfile

    + gem 'rails', '4.1.1'


    $ bundle install --path vendor/bundle
    $ bundle exec rails new . --skip-test-unit --database=mysql
    gem 'unicorn'
    gem 'therubyracer',  platforms: :ruby
    コメントアウトを外す
    変更後のGemfile

    # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
    gem 'rails', '4.1.1'
    # Use mysql as the database for Active Record
    gem 'mysql2'
    # Use SCSS for stylesheets
    gem 'sass-rails', '~> 4.0.3'
    # Use Uglifier as compressor for JavaScript assets
    gem 'uglifier', '>= 1.3.0'
    # Use CoffeeScript for .js.coffee assets and views
    gem 'coffee-rails', '~> 4.0.0'
    # See https://github.com/sstephenson/execjs#readme for more supported runtimes
    gem 'therubyracer',  platforms: :ruby

    # Use jquery as the JavaScript library
    gem 'jquery-rails'
    # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
    gem 'turbolinks'
    # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
    gem 'jbuilder', '~> 2.0'
    # bundle exec rake doc:rails generates the API under doc/api.
    gem 'sdoc', '~> 0.4.0',          group: :doc

    # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
    gem 'spring',        group: :development

    # Use ActiveModel has_secure_password
    # gem 'bcrypt', '~> 3.1.7'

    # Use unicorn as the app server
    gem 'unicorn'

    # Use Capistrano for deployment
    # gem 'capistrano-rails', group: :development

    # Use debugger
    # gem 'debugger', group: [:development, :test]

    $ bundle install --without production

    Fetching gem metadata from https://rubygems.org/...........

    Running `bundle update` will rebuild your snapshot from scratch, using only
    the gems in your Gemfile, which may resolve the conflict.


    などと言われたので素直にupdateする
    $ bundle update

  15. お試しページの作成をしてみる
    $ bundle exec rails g controller Roots index

    ※`autodetect': Could not find a JavaScript runtime.
    のエラーが出たら gem 'therubyracer' が有効になってるか確認

    Routeの変更
    $ vi config/routes.rb

    Rails.application.routes.draw do
    +  root :to => 'roots#index'
    +  #get 'roots/index'

    database.ymlの変更
    $ vi config/database.yml

    + password: root_passwd

    パスワードの記載を追加
    $ bundle exec rake db:create

  16. config/unicorn.rbの作成

    # -*- coding: utf-8 -*-

    RAILS_ROOT = File.expand_path("../..", __FILE__)

    listen "/tmp/unicorn.sock"
    pid "tmp/pids/unicorn.pid"

    # ワーカの数を指定
    worker_processes 2

    # リクエストのタイムアウト秒を指定
    timeout 15

    # ダウンタイムをなくすため、アプリをプレロード
    #preload_app true

    stdout_path File.expand_path('log/unicorn-stdout.log', ENV['RAILS_ROOT'])
    stderr_path File.expand_path('log/unicorn-stderr.log', ENV['RAILS_ROOT'])

    # before_fork, after_forkではUnicornのプロセスがフォークする前後の挙動を指定できる
    # 以下のおまじないの詳細はドキュメント参照
    before_fork do |server, worker|
        defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

        old_pid = "#{server.config[:pid]}.oldbin"
        unless old_pid == server.pid
            begin
                Process.kill :QUIT, File.read(old_pid).to_i
            rescue Errno::ENOENT, Errno::ESRCH
            end
        end

        defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
    end

    after_fork do |server, worker|
      Signal.trap 'TERM' do
        puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
      end

      defined?(ActiveRecord::Base) and
        ActiveRecord::Base.establish_connection
    end

    def rails_root
      require "pathname"
      Pathname.new(__FILE__) + "../../"
    end

    注意点は listen "/tmp/unicorn.sock" のsockのパスと
    /etc/nginx/conf.d/192.168.33.11.conf の記載のパスが一致すること
    upstream unicorn {
      server unix:/tmp/unicorn.sock fail_timeout=0;
    }

  17. 起動
    $ bundle exec unicorn_rails -c config/unicorn.rb -E development -D

    http://192.168.33.11/
    にアクセスして

    Roots#index

    Find me in app/views/roots/index.html.erb


    が見えれば成功

    Unicornを終了したい時は直接プロセスを切るらしい
    $ ps -ef | grep unicorn | grep -v grep

    vagrant   7685     1  0 06:46 ?        00:00:00 unicorn_rails master -c config/unicorn.rb -E development -D
    vagrant   7688  7685  8 06:46 ?        00:00:26 unicorn_rails worker[0] -c config/unicorn.rb -E development -D
    vagrant   7690  7685  9 06:46 ?        00:00:28 unicorn_rails worker[1] -c config/unicorn.rb -E development -D

    $ kill -9 7685
    $ !ps
    ps -ef | grep unicorn | grep -v grep

    Unicornのエラーなどが見たければ
    $ tail -f log/unicorn-stderr.log

    1. I, [2014-07-16T06:46:37.781500 #7688]  INFO -- : Refreshing Gem list
      I, [2014-07-16T06:46:37.784650 #7690]  INFO -- : Refreshing Gem list


今回の全ファイルは以下のGitHubに置いてあります。

chef-Rails4.1-Ruby2.1.2-Nginx-Unicorn-MySQL/chef-repo at master · k1row/chef-Rails4.1-Ruby2.1.2-Nginx-Unicorn-MySQL · GitHub