シンプルなRails環境を最速プロビジョニング。各種ツールの利用比較 (Chef [Berkshelf], Ansible [Playbook], Docker [Docker Compose], 手動)

プロビジョニングのための構成管理フレームワークには様々なものがあり、例に挙げると以下のようなものがあります。

- Chef
- Ansible
- Puppet
- SaltStack
- CFEngine
- PowerShell DSC
- Itamae
- AWS CloudFormation
- Terraform
- Mobingi


[Itamae](https://github.com/itamae-kitchen/itamae)はCookpadの社員の方が開発した、機能がシンプルで学習コストが低く、Chefを簡略化したようなものです。[AWS CloudFormation](https://aws.amazon.com/jp/cloudformation/)はAWS上のサーバなどの構成をJSONまたはYAMLで記述して管理するものです。[Terraform](https://www.terraform.io/)は[Vagrant](https://www.vagrantup.com/)や[Packer](https://www.packer.io/)などで有名なHashiCorp社により開発されたもので、AWSやGCP、Azureなどの様々なクラウドに対応した管理ツールとなっています。[Mobingi](https://mobingi.co.jp/)は、従来のようなChefやAnsibleは開発者をターゲットとした扱いの難しいものとして、クラウドのデスクトップとしてGUIベースで管理できるというものです。

ここでは、Chef,Ansible,Dockerによる設定例を取り上げます。
ChefはBerkshelf、AnsibleはAnsible Playbook、DockerはDocker Composeを用いました。また、手動による設定のインストールの例も取り上げました。


例ではそれぞれ最後に、ChefはAWS OpsWorks、AnsibleはAmazon EC2、DockerはAmazon ECSを例に行っていますが、他の環境の場合は適宜置き換えて対応してください。

#Chef [Berkshelf]
ローカルでテストせずにOpsWorksでデプロイする場合はVagrant周りの設定は不要で、サブタイトルに(※)のマークを入れた

- Gemfileの作成
- Berksfileの作成
- Recipeの作成

の3つをやれば良い。

##ディレクトリ構成

```
.
├── Berksfile
├── Gemfile
├── README.md
├── Vagrantfile
└── site-cookbooks
    ├── nginx
    │   ├── CHANGELOG.md
    │   ├── README.md
    │   ├── attributes
    │   │   └── default.rb
    │   ├── metadata.rb
    │   ├── recipes
    │   │   └── default.rb
    │   └── templates
    │       └── default
    │           └── nginx.conf.erb
    ├── nodejs
    │   ├── CHANGELOG.md
    │   ├── README.md
    │   ├── attributes
    │   │   └── default.rb
    │   ├── metadata.rb
    │   └── recipes
    │       └── default.rb
    └── ruby-env
        ├── CHANGELOG.md
        ├── README.md
        ├── attributes
        │   └── default.rb
        ├── metadata.rb
        ├── recipes
        │   └── default.rb
        └── templates
            └── default
```

##VagrantでCentOS 6.7の環境設定

```
$ vagrant box add centos6-7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
$ mkdir -p mkdir ./projects/chef_zero_test
$ cd projects/chef_zero_test
$ vagrant init centos6.7
```

Vagrantの設定ファイルを以下のように編集する。

```
$ vim Vagrantfile
```

```
Vagrant.configure("2") do |config|
  config.vm.box = "centos6.7"
  config.vm.box_url = "https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", "4096"]
  end
end
```

このままでVagrantを立ち上げると、SSH接続の設定で

```
default: Warning: Authentication failure. Retrying...
```
のようなエラーが繰り返し表示されて権限の問題でできないので、VagrantにSSHで入って


```
vagrant ssh
```

ホスト側で以下のコマンドを実行する。

```
cd /home
chmod 755 -R ./vagrant
exit
```
http://qiita.com/jshimazu/items/9db49ce64478e82d511e

Vagrantを立ち上げる。

```
vagrant up
```

##Vagrantfileの設定
Vagrantfileをプロビジョニングの設定を以下のように追加する。

```
Vagrant.configure("2") do |config|
  config.vm.box = "centos6.7"
  config.vm.box_url = "https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", "4096"]
  end

  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = ["./cookbooks", "./site-cookbooks"]
    chef.json = {
      nginx: {
        env: ["ruby"]
      }
    }
    chef.run_list = %w[
      recipe[yum-epel]
      recipe[nginx]
      recipe[ruby-env]
      recipe[nodejs]
    ]
  end
end
```

##Gemfileの作成(※)
以下のようにGemfileを作成する。

```
$ vim Gemfile
```

```Gemfile
source 'https://rubygems.org'

gem 'chef'
gem 'knife-solo'
gem 'berkshelf'
```



```
$ cd projects/chef_zero_test
$ rbenv exec bundle install
```

##Berksfileの作成(※)
以下のようにBerksfileを作成する。

```
$ vim Berksfile
```

```Berksfile
source "https://supermarket.chef.io"

cookbook "yum-epel"
cookbook "nginx", path: "./site-cookbooks/nginx"
cookbook "ruby-env", path: "./site-cookbooks/ruby-env"
cookbook "nodejs", path: "./site-cookbooks/nodejs"
```

※最初、```site :opscode```の宣言方法はDeplicated

##Recipeの作成(※)
###nginxのレシピ
1.Cookbookの作成。

```
bundle exec knife cookbook create nginx -o ./site-cookbooks
```

2.Recipeファイルの作成。

```
vim site-cookbooks/nginx/recipes/default.rb
```

```default.rb
include_recipe "yum-epel"

package "nginx" do
  action :install
end

service "nginx" do
  action [ :enable, :start ]
  supports :status => true, :restart => true, :reload => true
end

template 'nginx.conf' do
  path '/etc/nginx/nginx.conf'
  source "nginx.conf.erb"
  owner 'root'
  group 'root'
  mode '0644'
  notifies :reload, "service[nginx]"
end
```

<!--
```
sudo chown shogo site-cookbooks/nginx/recipes/default.rb
```
-->

3.attributeファイルの作成。

```
vim site-cookbooks/nginx/attributes/default.rb
```

```default.rb
default['nginx']['env'] = []
```

4.nginx.confのテンプレートファイルの作成。

```
sudo cp /usr/local/etc/nginx/
nginx.conf ~/workspace/chef-tests/chef-test/projects/chef_zero_test/site-cookbooks/nginx/templates/default/nginx.conf.erb
```

```
vim site-cookbooks/nginx/template/default/nginx.conf.erb
```

```nginx.conf.erb
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    upstream app_server {
      server unix:/tmp/unicorn.sock;
    }

    server {
        listen       80 default_server;
        server_name  _;

        location / {
            rewrite ^/app_server/(.+) /$1 break;
            proxy_pass http://app_server/$1;
        }
    }
}

```

<!--
```nginx.conf.erb
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    <% if node['nginx']['env'].include?("ruby") %>
        upstream unicorn {
          server unix:/tmp/unicorn.sock;
        }
    <% end %>

    server {
        listen       80 default_server;
        server_name  _;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm index.php;
        }

        error_page  404              /404.html;
        location = /404.html {
            root   /usr/share/nginx/html;
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

        <% if node['nginx']['env'].include?("ruby") %>
        location /unicorn {
            rewrite ^/unicorn/(.+) /$1 break;
            proxy_pass http://unicorn/$1;
        }
        <% end %>
    }
}

```
-->

###Rubyのレシピ
1.Cookbookの作成。

```
$ bundle exec knife cookbook create ruby-env -o ./site-cookbooks
```

2.Recipeファイルの作成。

```
$ vim site-cookbooks/ruby-env/recipes/default.rb
```

```
%w{vim git openssl-devel sqlite-devel readline-devel}.each do |pkg|
    package pkg do
        action :install
    end
end

git "/home/#{node['ruby-env']['user']}/.rbenv" do
    repository node["ruby-env"]["rbenv_url"]
    action :sync
    user node['ruby-env']['user']
    group node['ruby-env']['group']
end

template ".bash_profile" do
    source ".bash_profile.erb"
    path "/home/#{node['ruby-env']['user']}/.bash_profile"
    mode 0644
    owner node['ruby-env']['user']
    group node['ruby-env']['group']
    not_if "grep rbenv ~/.bash_profile", :environment => { :'HOME' => "/home/#{node['ruby-env']['user']}"  }
end

directory "/home/#{node['ruby-env']['user']}/.rbenv/plugins" do
    owner node['ruby-env']['user']
    group node['ruby-env']['group']
    mode 0755
    action :create
end

git "/home/#{node['ruby-env']['user']}/.rbenv/plugins/ruby-build" do
    repository node["ruby-env"]["ruby-build_url"]
    action :sync
    user node['ruby-env']['user']
    group node['ruby-env']['group']
end

execute "rbenv install #{node['ruby-env']['version']}" do
    command "/home/#{node['ruby-env']['user']}/.rbenv/bin/rbenv install #{node['ruby-env']['version']}"
    user node['ruby-env']['user']
    group node['ruby-env']['group']
    environment 'HOME' => "/home/#{node['ruby-env']['user']}"
    not_if { File.exists?("/home/#{node['ruby-env']['user']}/.rbenv/versions/#{node['ruby-env']['version']}")}
end


execute "rbenv global #{node['ruby-env']['version']}" do
    command "/home/#{node['ruby-env']['user']}/.rbenv/bin/rbenv global #{node['ruby-env']['version']}"
    user node['ruby-env']['user']
    group node['ruby-env']['group']
    environment 'HOME' => "/home/#{node['ruby-env']['user']}"
    not_if { File.exists?("/home/#{node['ruby-env']['user']}/.rbenv/versions/#{node['ruby-env']['version']}")}
end


execute "rbenv shell #{node['ruby-env']['version']}" do
    command "/home/#{node['ruby-env']['user']}/.rbenv/bin/rbenv shell #{node['ruby-env']['version']}"
    user node['ruby-env']['user']
    group node['ruby-env']['group']
    environment 'HOME' => "/home/#{node['ruby-env']['user']}"
    not_if { File.exists?("/home/#{node['ruby-env']['user']}/.rbenv/versions/#{node['ruby-env']['version']}")}
end

```

3.attributeファイルの作成。

```
$ vim site-cookbooks/ruby-env/attributes/default.rb
```

```
default['ruby-env']['user'] = "vagrant"
default['ruby-env']['group'] = "vagrant"
default['ruby-env']['version'] = "2.3.1"
default['ruby-env']['rbenv_url'] = "https://github.com/sstephenson/rbenv"
default['ruby-env']['ruby-build_url'] = "https://github.com/sstephenson/ruby-build"
```

※EC2にデプロイする場合は以下のようにuserとgroupの内容をec2-userにする。

```
default['ruby-env']['user'] = "ec2-user"
default['ruby-env']['group'] = "ec2-user"
default['ruby-env']['version'] = "2.3.1"
default['ruby-env']['rbenv_url'] = "https://github.com/sstephenson/rbenv"
default['ruby-env']['ruby-build_url'] = "https://github.com/sstephenson/ruby-build"
```

4..bash_profileのテンプレートファイルの作成

```
$ vim site-cookbooks/ruby-env/template/default/.bash_profile.erb
```

```.bash_profile.erb
# .bash_profile

if [ -f ~/.bashrc] ; then
    . ~/.bashrc
fi

PATH=$PATH:$HOME/bin
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
```

###Node.jsのレシピ
1.Cookbookの作成。

```
$ bundle exec knife cookbook create nodejs -o ./site-cookbooks
```

2.Recipeファイルの作成。

```
$ vim site-cookbooks/nodejs/recipes/default.rb
```

```
bash "install nodejs" do
    user "root"
    cwd "/tmp"
    code <<-EOC
        curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -
        yum -y install gcc-c++ make nodejs
     EOC
end
```

<!--
#PHPのCookbook
##Recipeの作成

```
vim recipes/default.rb
```

```default.rb
%w{php-fpm}.each do |pkg|
  package pkg do
    action :install
    notifies :restart, "service[php-fpm]"
  end
end

service "php-fpm" do
  action [:enable, :start]
end
```

```
vim recipes/php55.rb
```

```
yum_repository 'remi' do
  description 'Les RPM de Remi - Repository'
  baseurl 'http://rpms.famillecollet.com/enterprise/6/remi/x86_64'
  gpgkey 'http://rpms.famillecollet.com/RPM-GPG-KEY-remi'
  fastestmirror_enabled true
  action :create
end


yum_repository 'remi-php55' do
  description 'Les RPM de remi de PHP 5.5 pour Enterprise Linux 6'
  baseurl 'http://rpms.famillecollet.com/enterprise/6/php55/$basearch/'
  gpgkey 'http://rpms.famillecollet.com/RPM-GPG-KEY-remi'
  fastestmirror_enabled true
  action :create
end

%w{php php-fpm php-opcache}.each do |pkg|
  package pkg do
    action :install
    notifies :restart, "service[php-fpm]"
  end
end

service "php-fpm" do
  action [:enable, :start]
end
```

nginx.conf.erbに以下を追記

```
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
<% if node['nginx']['env'].include?("php") %>
location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}
<% end %>
```
-->


##Berkshelfの実行 + Vagrantのプロビジョニング
Berkshelfを実行して、Vagrantでプロビジョニングを実行する。

```
$ bundle exec berks vendor ./cookbooks
$ vagrant reload
$ vagrant provision
```


※```bundle exec berks install --path ./cookbooks```はdeprecated

##Railsの動作確認
ホスト側でRailsをインストールする。

```
$ vagrant ssh
```


```
# rbenv shell 2.3.1
$ gem install rails -V
$ rails new sample --skip-bundle
$ cd sample/
$ mkdir -p shared/{pids,log}
```

Gemfileを開いて```gem 'unicorn'```の一行を追加する。

```
vim Gemfile
```

```
source 'https://rubygems.org'


gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
gem 'sqlite3'
gem 'puma', '~> 3.0'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2'
gem 'jquery-rails'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'

gem 'unicorn'

group :development, :test do
  gem 'byebug', platform: :mri
end

group :development do
  gem 'web-console'
  gem 'listen', '~> 3.0.5'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
```

<!--
group :doc do
  gem 'sdoc', require: false
end

gem 'unicorn'
gem 'ffi', '~> 1.9.14'
-->

以下のコマンドで上記の内容をインストール。

```
bundle install
```

unicornの設定ファイルを以下のように編集する。

```
vim config/unicorn.rb
```

```
listen "/tmp/unicorn.sock"
worker_processes 2

pid "/home/vagrant/sample/shared/pids/unicorn.pid"
stderr_path "/home/vagrant/sample/shared/log/unicorn.log"
stdout_path "/home/vagrant/sample/shared/log/unicorn.log"
```
   
      
            
```
cd sample/ 
bundle exec unicorn -c config/unicorn.rb [-D]
```

UnicornではなくPumaで動かす場合は以下のコマンド

```
bundle exec rails s (-e production) -b '0.0.0.0'
```

　```http://192.168.33.10:3000```　にアクセスすると以下のような画面が現れる。

![スクリーンショット 2016-11-17 0.59.18.png](https://qiita-image-store.s3.amazonaws.com/0/81083/0b355ea5-acec-cd61-ebb0-99ff40186b8c.png "スクリーンショット 2016-11-17 0.59.18.png")



Unicornのプロセスの終了(-Dでデーモンで立ち上げた場合)

```
kill -QUIT `cat /home/vagrant/test_unicorn/shared/pids/unicorn.pid`
```


##OpsWorksでデプロイ
###Berkshelfによるパッケージ化
以下のコマンドを実行して、S3にアップロードする。

```
$ bundle exec berks package cookbooks.tar.gz
$ aws s3 cp cookbooks.tar.gz s3://sample-bucket/
```

###OpsWorks(OpsWorks Stacks)の操作
1. マネジメントコンソールからOpsWorksを選択
2. [Go to OpsWorks Stacks]を選択。

####Stackの設定
3. [Stacks]から[Add stack]を選択。
4. [Chef 12 stack]タブを選択し、以下のように入力する。

Stack name: sample-stack
Region: Asia Pacific (Tokyo)
VPC: vpc-xxxxxxxx (default)
Default subnet: xxx.xxx.xxx.xxx/xx - ap-northeast-1a
Default operating system: Linux, Amazon Linux 2016.09
Default SSH key: Do not use a default SSH key
Chef version: 12
Use custom Chef cookbooks: Yes
Repository type: S3 Archive
Repository URL: http://<bucket name>.s3-website-ap-northeast-1.amazonaws.com/cookbooks.tar.gz
Access key ID: AKIAXXXXXXXXXXXXXXXXXXXX
Secret access key: xxxxxxxxxxxxxxxxxxxxxxxxx
Stack color: blue (default)

Advanced optionsは以下のような項目がある。
Default root device type: EBS backed
IAM role:
Default IAM instance profile: 
API endpoint region: ap-northeast-1a (REGIONAL)
Hostname theme: Layer Dependent
OpsWorks Agent version: 4021(Dec 16th 2016)
Custom JSON: (空欄)
Use OpsWorks security groups: Yes

####Layerの設定
1. [Layers]の設定
2. [Add layer]を選択し、以下のように入力する。
Name: sample-layer
Short name: sample

[Add layer]を選択。

3. 作成したLayerを選択し、[Recipes]を選択。
4. Setupに以下の5つを追加する。

- nginx::default
- nodejs::default
- ruby-env::default
- yum::default
- yum-epel::default

[Save]を選択。

※これを忘れた場合、Chefによるプロビジョニングが行われずに、後述のインスタンスが起動してしまう。

####Instanceの作成
1.[Instances]を選択。
2.[+ Instance]を選択し、以下のように入力する。

Hostname: sample
Size: t2.micro
Subnet: XXX.XXX.XXX.XXX/XX - ap-northeast-1a

[Add Instance]を選択。

3.作成したインスタンスのActionsから[start]を選択。以降プロビジョニングが始まる。もし、起動に失敗した場合は、Hostnameのホスト名を選択した時に遷移するページの最下部にあるLogsから確認出来るLogを確認する。


#補足

##Berkshelfの実行

```
bundle exec berks install --path ./cookbooks
```
これはdeprecatedなので以下を実行

```
bundle exec berks vendor --path ./cookbooks
```

##注意点
###ホスト側でgemコマンドが見つからない

```
rbenv shell 2.3.1
```
を実行することでgemを認識するようになる。
http://qiita.com/kasumani/items/042bf799d6794bd2e4f2



#Ansible
##導入
インストール

```
brew install ansible
```

試しにホストに疎通確認。

```hosts
[sample]
<IP address> ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/id_rsa
```

```
$ ansible -i hosts all -m ping
<IP address> | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
```

##Ansible Playbookによるプロビジョニング
###ディレクトリ構成

```
.
├── ansible.cfg
├── group_vars
│   └── sample
├── inventory
│   └── hosts
├── sample.yml
└── roles
    ├── nginx
    │   ├── defaults
    │   │   └── main.yml
    │   ├── handlers
    │   │   └── main.yml
    │   ├── tasks
    │   │   └── main.yml
    │   └── templates
    │       ├── 404.html.j2
    │       ├── 50x.html.j2
    │       ├── index.html.j2
    │       └── nginx.conf.j2
    ├── rails
    │   ├── defaults
    │   │   └── main.yml
    │   └── tasks
    │       └── main.yml
    ├── rbenv
    │   └── tasks
    │       └── main.yml
    └── yum
        └── tasks
            └── main.yml
```

###ファイル内容

```sample.yml
- hosts: sample
  become: yes 
  roles:
    - yum 
    - rbenv
    - rails
    - nginx
```

```ansible.cfg
[defaults]
remote_user=ec2-user
private_key_file=~/.ssh/id_rsa
inventory=./inventory/hosts
executable = /bin/bash -l
```

- inventory

```hosts
[defualts]
<IP address>
```

- group_vars

```sample
rbenv_user: ec2-user
rbenv_ruby_version: 2.3.1
```

- roles
	- yum

```
yum
└── tasks
    └── main.yml
```

```main.yml
---
- name: Install package for bundle install
  yum: name={{ item }} state=latest
  with_items:
    - sqlite-devel
    - gcc-c++
- name: Update all packages
  yum: name=* state=latest
```
　```gcc-c++```はtherubyracerのインストールに必要

- rbenv

```
rbenv
└── tasks
    └── main.yml
```

```
---
- name: Install dependencies for rbenv
  yum: name={{ item }} state=latest
  with_items:
    - git

- name: Install rbenv
  become: yes
  become_user: "{{ rbenv_user }}"
  git: repo=https://github.com/sstephenson/rbenv.git dest=~/.rbenv

- name: Add ~.rbenv/bin to PATH
  become: yes
  become_user: "{{ rbenv_user }}"
  lineinfile: >
    dest="~/.bash_profile"
    line="export PATH=$HOME/.rbenv/bin:$PATH"
- name: Eval rbenv init in ~/.bash_profile
  become: yes
  become_user: "{{ rbenv_user }}"
  lineinfile: >
    dest="~/.bash_profile"
    line='eval "$(rbenv init -)"'

- name: Install dependencies for ruby-build (see. https://github.com/sstephenson/ruby-build/wiki)
  yum: name={{ item }} state=latest
  with_items:
    - gcc
    - openssl-devel
    - libyaml-devel
    - libffi-devel
    - readline-devel
    - zlib-devel
    - gdbm-devel
    - ncurses-devel

- name: Install ruby-build as rbenv plugin
  become: yes
  become_user: "{{ rbenv_user }}"
  git: repo=https://github.com/sstephenson/ruby-build.git dest=~/.rbenv/plugins/ruby-build

- name: Check if version is installed ruby
  become_method: yes
  become_user: "{{ rbenv_user }}"
  shell: "rbenv versions | grep {{ rbenv_ruby_version }}"
  register: rbenv_check_install
  changed_when: False
  ignore_errors: yes

- name: Install ruby
  become_method: yes
  become_user: "{{ rbenv_user }}"
  shell: "rbenv install {{ rbenv_ruby_version }}"
  when: rbenv_check_install|failed

- name: Check if version is the default ruby version
  become_method: yes
  become_user: "{{ rbenv_user }}"
  shell: "rbenv version | grep {{ rbenv_ruby_version }}"
  register: rbenv_check_default
  changed_when: False
  ignore_errors: yes

- name: Set default ruby version
  become_method: yes
  become_user: "{{ rbenv_user }}"
  shell: "rbenv global {{ rbenv_ruby_version }}"
  when: rbenv_check_default|failed
```

※注意点で、rbenvのコマンドのパスを通すところで、.bash_profileに設定を追記しているが、sourceコマンドでは反映されない。なので、パスを適用させるために、シェルはログインシェルとして実行することで解決できる。具体的には、ansible.cfg中で```executable = /bin/bash -l```を宣言すると良い。


- rails

```
rails
├── defaults
│   └── main.yml
└── tasks
    └── main.yml
```

- defaults

```main.yml
---
railsenv_deploy_dir: "/var/www/sample"
railsenv_deploy_user: ec2-user
railsenv_deploy_group: ec2-user
```

- tasks

```main.yml
---
- name: Install mysql-devel
  yum: name={{ item }} state=latest
  with_items:
    - mysql-devel
    - gcc-c++

- name: Install dependencies for nokogiri
  yum: name={{ item }} state=latest
  with_items:
    - patch

- name: Install bundler
  become_user: "{{ rbenv_user }}"
  gem: name={{ item }} user_install=False
  with_items:
    - bundler

- name: Create deploy directory
  file: path={{ railsenv_deploy_dir }} state=directory owner={{ railsenv_deploy_user }} group={{ railsenv_deploy_group }} mode=0755
```

- nginx

```
nginx
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
└── templates
    ├── index.html.j2
    └── nginx.conf.j2
```

- tasks

```main.yml
---
- name: Install nginx
  yum: name=nginx state=latest

- name: Set nginx service to start on boot
  service: name=nginx enabled=true

- name: Put nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf backup=true mode=0644
  notify: restart nginx

- name: Put share index.html
  template: src=index.html.j2 dest=/usr/share/nginx/html/index.html mode=644
```

<!--
```
---
- name: Install nginx
  yum: name=nginx state=latest

- name: Set nginx service to start on boot
  service: name=nginx enabled=true

- name: Put nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf backup=true mode=0644
  notify: restart nginx

- name: Create self signed certificate 
  shell: |
    mkdir -p /etc/nginx/certs &&
    crt_file="/etc/nginx/certs/local-ssl.crt" &&
    key_file="/etc/nginx/certs/local-ssl.key" &&
    crt_and_key_file="/etc/nginx/certs/local-ssl.crt_and_key" &&
    subject="/C=JP/ST=Osaka/L=Osaka City/CN=local-ssl" &&
    openssl req -new -newkey rsa:2048 -sha1 -x509 -nodes \
      -set_serial 1 \ 
      -days 3650 \
      -subj "$subject" \
      -out "$crt_file" \
      -keyout "$key_file" &&
    cat "$crt_file" "$key_file" >> "$crt_and_key_file" &&
    chmod 400 "$key_file" "$crt_and_key_file"
    creates="/etc/nginx/certs/local-ssl.crt"
- name: Put share index.html
  template: src=index.html.j2 dest=/usr/share/nginx/html/index.html mode=644

- name: Put share 404.html
  template: src=404.html.j2 dest=/usr/share/nginx/html/404.html mode=644

- name: Put share 50x.html
  template: src=50x.html.j2 dest=/usr/share/nginx/html/50x.html mode=644
```
-->

- handlers

```main.yml
---
- name: restart nginx
  service: name=nginx state=restarted
```

- defualts

```main.yml
---
nginx_woker_processes: "auto"
nginx_server_tokens: "off"
nginx_includes: []
```

- templates

```index.html.j2
<html>
    <body>
        <h1>Hello, world!</h1>
    </body>
</html>
```

```nginx.conf.j2
user  nginx;
worker_processes  {{ nginx_woker_processes }};

error_log  /var/log/nginx/error.log;

pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    error_log  /var/www/sample/log/nginx.error.log; 
    access_log /var/www/sample/log/nginx.access.log; 

    server_tokens {{ nginx_server_tokens }};

    sendfile        on;

    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;
{% for include in nginx_includes %}
    include {{ include }};
{% endfor %}

    index   index.html index.htm;

    client_max_body_size 2G;
    upstream app_server {
        server unix:/var/www/sample/tmp/sockets/.unicorn.sock fail_timeout=0;
    }
   
   server {
        listen 80;
        server_name <IP address>;

        keepalive_timeout 5;
        root /var/www/sample/public;
        try_files $uri/index.html $uri.html $uri @app;
        location @app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app_server;
        }
    }
}
```

<!--
server {
        listen 80;
        server_name <IP address>;

        keepalive_timeout 5;
        root /var/www/sample/public;
        try_files $uri/index.html $uri.html $uri @app;
        location @app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app_server;
        }
        error_page 500 502 503 504 /500.html;
        location = /500.html {
            root /var/www/sample/public;
        }
    }
-->

###実行

```
ansible-playbook sample.yml
```

ansible.cfg中で```remote_user```, ```private_key_file```, ```inventory```の設定を追加しない場合は以下のようにコマンドを打つ必要がある。

```
ansible-playbook -i "<IP address>," --user=ec2-user --private-key=~/.ssh/id_rsa sample.yml
```

<!--
```
rails s -b '0.0.0.0'
```

でブラウザからアクセスできるようになればOK
-->

##注意点
- rbenvの設定ファイルを.bash_profileの内容がsourceでは反映されないのでshellモジュールを使う時は必ずログインシェルとして実行する

```
[defaults]
executable = /bin/bash -l
```

```
- name: Sample shell execution
  become_method: yes
  become_user: ec2-user
  shell: "~~~"
```

http://www.bunkei-programmer.net/entry/2015/05/16/162020

- therubyracerの依存パッケージでgcc-c++をyumで入れる。

<!--
```
ok: [<IP address>]
 ___________________________________________________ 
< TASK [rbenv : Check if version is installed ruby] >
 --------------------------------------------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

fatal: [<IP address>]: FAILED! => {"changed": false, "cmd": "sudo -iu ec2-user rbenv versions | grep 2.3.0", "delta": "0:00:00.096489", "end": "2017-01-03 07:22:48.106530", "failed": true, "rc": 1, "start": "2017-01-03 07:22:48.010041", "stderr": "", "stdout": "", "stdout_lines": [], "warnings": ["Consider using 'become', 'become_method', and 'become_user' rather than running sudo"]}
...ignoring
```

JSエンジンのためにNodeJSかtherubyracerを入れる。
sdocの依存パッケージでsqlite-develをyumで入れる。
therubyracerの依存パッケージでgcc-c++をyumで入れる。
-->

<!--
##Ansible ディレクトリ　ベストプラクティス
http://docs.ansible.com/ansible/playbooks_best_practices.html

##設定ファイル
http://docs.ansible.com/ansible/intro_configuration.html



sudo -> become
sudo_user -> become_user

shell: "sudo -iu {{rbenv_user}} ~~~"
-->

#Docker [Docker Compose]

```Dockerfile
FROM ruby:2.3.1

ENV APP_ROOT /app

RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       libmysqld-dev \
                       libmysqlclient-dev \
                       mysql-client \
                       --no-install-recommends && \
    rm -Rf /var/lib/opt/lists/*

RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT

ADD . /app
RUN bundle install
```

```docker-compose.yml
version: '2' 
services:
  app:
    build: .
    command: bundle exec rails s -b '0.0.0.0'
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    links:
      - db
  db: 
    image: mysql:5.6.30
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
```

ただし、Gemfile中で```therubyracer```を使用しない場合は、Dockerfile中で```apt-get install```で```nodejs```もインストールする。


##ビルド & 実行
以下のコマンドはターミナルのウィンドウを新たに開くたびに実行する。

```
docker-machine start default
eval $(docker-machine env defualt)
```

ビルド

```
docker-compose build
```


実行

```
docker-compose up
```

IPアドレスを以下のコマンドで調べる。

```
docker-machine ip
```

192.168.99.100:3000のようにブラウザにアクセスする。

##ECSでデプロイ
ECSでデプロイする場合は別投稿の下記のURLを参考にして下さい。
http://qiita.com/hayashier/items/b34f82c42053f85e5b09

#マニュアルで環境構築
## Railsの環境準備

```
sudo yum install -y git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel mysql mysql-server mysql-devel ImageMagick ImageMagick-devel epel-release
sudo yum install -y nodejs npm --enablerepo=epel
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv 
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile 
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source .bash_profile
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 2.3.1
rbenv global 2.3.1
ruby -v
```

Webサーバのインストール

```
sudo yum install gem
gem install bundle
bundle -v
rake secret
sudo yum install nginx
sudo service nginx start
```

以下、Capistrano等のデプロイツールを用いてデプロイする場合は必ずしも必要ではありません。

## gitの準備

```
vim ~/.netrc
cd ~/.ssh
ssh-keygen -t rsa -C "<メールアドレス>"
ssh-add ~/.ssh/id_rsa
cat id_rsa_admin.pub 
ssh -T git@github.com
```

GitHubに公開鍵の登録をする。

- .netrc

```
machine github.com
login <GitHubユーザ名>
password xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

## Webサーバの環境・設定

```
cd /var/www/
sudo git clone https://github.com/<GitHubユーザ名>/<レポジトリ名>.git
sudo chown -R ec2-user:ec2-user <レポジトリ名>/
cd <レポジトリ名>/
bundle install --path ./vendor/bundle/ 
sudo vim /etc/nginx/conf.d/admin.conf 
sudo service nginx restart
bundle exec rake secret
cat config/secrets.yml 
vim .env
source .env
echo $SECRET_KEY_BASE
sudo service nginx restart
rails s -e production
```

- .env

```
export DATABASE_PASSWORD_PRODUCT=xxxxxxxxxxxxxxx
```

config/database.yml中のデータベースへのパスワードを以下のように環境変数として定義しておき、.env中でインポートする。

<%= ENV['DATABASE_PASSWORD_PRODUCT'] %>

- sample.conf


```
error_log  /var/www/sample/log/nginx.error.log; 
access_log /var/www/sample/log/nginx.access.log;

client_max_body_size 2G;
upstream app_server {
  server unix:/var/www/sample/tmp/sockets/.unicorn.sock fail_timeout=0; 
}
server {
  listen 80;
  server_name <IPアドレス>;
  # nginx so increasing this is generally safe...
  keepalive_timeout 5;
  # path for static files
  root /var/www/sample/public; 
  # page cache loading
  try_files $uri/index.html $uri.html $uri @app;
  location @app {
    # HTTP headers
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }     
  # Rails error pages
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/sample/public; 
  }
}
```

#参考
##Chef
- https://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/welcome.html
- http://ruby-rails.hatenadiary.com/entry/20150721/1437404524



<!--
##参考になるかも
https://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/workingcookbook-chef12-linux.html
http://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/chef-12-linux.html
http://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/best-practices-packaging-cookbooks-locally.html
http://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/workingcookbook-assigningcustom.html
http://blog.mmmcorp.co.jp/blog/2016/03/31/deploy-rails-to-opsworks-chef12/
https://docs.aws.amazon.com/ja_jp/opsworks/latest/userguide/gettingstarted-node.html
-->

##Ansible
- Ansible ディレクトリ　ベストプラクティス
http://docs.ansible.com/ansible/playbooks_best_practices.html
- 設定ファイル
http://docs.ansible.com/ansible/intro_configuration.html
- http://qiita.com/katsuhiko/items/c84a282f3a56d71c404f

##Docker
https://docs.docker.com/compose/rails/