AnsibleでAmazonLinuxにRubyとNginxをインストール

# はじめに

先日TerraformでEC2やALBを作成したのですが、
各サーバにRuby(rbenv)のインストールとnginxのインストールと簡単な設定を行います。

# 環境情報

pythonのvenvで環境は作成しています。
AWSのリソースを扱うので、boto,boto3,botocoreあたりは必須になります。
事前にpip installしておきましょう。

~~~shell
% python -V
Python 3.8.3

% ansible --version
ansible 2.9.9

% pip list
Package         Version
--------------- -------
ansible         2.9.9
boto            2.49.0
boto3           1.14.12
botocore        1.17.12
cffi            1.14.0
cryptography    2.9.2
docutils        0.15.2
Jinja2          2.11.2
jmespath        0.10.0
MarkupSafe      1.1.1
pip             19.2.3
pycparser       2.20
python-dateutil 2.8.1
PyYAML          5.3.1
s3transfer      0.3.3
setuptools      41.2.0
six             1.15.0
urllib3         1.25.9
~~~~


# ディレクトリ構成

最終的なディレクトリ構成は以下です。
かなり多いですが大半は使わないディレクトリです。

~~~shell
% tree
.
├── README.md
├── ansible.cfg
├── inventory
│   ├── ec2.ini
│   └── ec2.py
├── playbook.yml
└── roles
    ├── common
    │   ├── README.md
    │   ├── defaults
    │   │   └── main.yml
    │   ├── files
    │   ├── handlers
    │   │   └── main.yml
    │   ├── meta
    │   │   └── main.yml
    │   ├── tasks
    │   │   └── main.yml
    │   ├── templates
    │   ├── tests
    │   │   ├── inventory
    │   │   └── test.yml
    │   └── vars
    │       └── main.yml
    ├── nginx
    │   ├── README.md
    │   ├── defaults
    │   │   └── main.yml
    │   ├── files
    │   ├── handlers
    │   │   └── main.yml
    │   ├── meta
    │   │   └── main.yml
    │   ├── tasks
    │   │   ├── main.yml
    │   │   └── nginx.yml
    │   ├── templates
    │   │   └── nginx.conf.j2
    │   ├── tests
    │   │   ├── inventory
    │   │   └── test.yml
    │   └── vars
    │       └── main.yml
    └── ruby
        ├── README.md
        ├── defaults
        │   └── main.yml
        ├── files
        ├── handlers
        │   └── main.yml
        ├── meta
        │   └── main.yml
        ├── tasks
        │   ├── main.yml
        │   ├── rbenv_settings.yml
        │   └── ruby_packages.yml
        ├── templates
        │   └── rbenv_system.sh.j2
        ├── tests
        │   ├── inventory
        │   └── test.yml
        └── vars
            └── main.yml
~~~


# インベントリ、ansible.cfgの設定

## ダイナミックインベントリの設定

EIPを使っている場合は固定IPなので問題無いのですが、動的パブリックIPのときはダイナミックインベントリを使うことになります。
ダイナミックインベントリ用のpythonスクリプトは何故か最新の物は動かなかったので少し古いものをダウンロードしました。

~~~shell
% ls -l inventory
total 160
-rw-r--r--  1 user  staff   4890  6 28 18:29 ec2.ini
-rwxr-xr-x  1 user  staff  73056  6 13 01:22 ec2.py
~~~

ec2.iniで以下パラメータに変更します。regionは環境に合わせます。
`cache_max_age = 0`
`regions = ap-northeast-1`

## ansible.cfgの設定

次にanisble.cfgの設定変更します。
まずansible.cfgをダウンロードします。

~~~shell
% wget https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg
~~~


ダウンロードしたasnible.cfgの[default]セクションを書き換えます。
`private_key_file`は適宜修正します。

~~~shell
% vim ansible.cfg

以下書き換え
[defaults]
inventory        = inventory/ec2.py
remote_user      = ec2-user
private_key_file = ~/.ssh/test-key.pem
host_key_checking = False
~~~

すでにAWS CLIでログイン濟みであれば以下でsuccessとなるはずです。

~~~shell
% ansible -i inventory/ec2.py -m ping
~~~


# コードの作成

## roleの作成

role を作成します。小分けで`common`、`ruby`、`nginx`としました。（名前は、わかり易ければなんでもOKです。）
ディレクトリは`ansible-galaxy init`コマンドで一発で作成出来ます。

~~~shell
% ansible-galaxy init roles/common
% ansible-galaxy init roles/ruby
% ansible-galaxy init roles/nginx
~~~

### サーバ基本設定

role=役割なのでそれぞれのtasksを書いていきます。
commonはユーザの作成などサーバの基本設定ですが、今回は簡単にパッケージのアップデートのみにしています。

~~~yaml:./roles/common/tasks/main.yml
---
- name: update packages
  yum:
    name: '*'
    state: latest
  become: true
~~~

### rubyの設定

次にrubyの設定をします。
rubyに必要なパッケージのインストールとrbenvの設定でファイルを分けました。
```include_tasks:```で同一ディレクトリのファイルも読み取ってくれます。

~~~yaml:./roles/ruby/tasks/main.yml
---
- include_tasks: ruby_packages.yml
- include_tasks: rbenv_settings.yml
~~~

~~~yaml:./roles/ruby/tasks/rbenv_settings.yml
---
- name: Install rbenv & ruby_build
  git:
    repo: "{{item.repo}}"
    dest: "{{item.directory}}"
  loop:
    - { repo: "https://github.com/sstephenson/rbenv.git", directory: "~/.rbenv"}
    - { repo: "https://github.com/sstephenson/ruby-build.git", directory: "~/.rbenv/plugins/ruby-build" }
- name: Edit bashrc
  blockinfile:
    path: ~/.bashrc
    create: yes
    insertafter: EOF
    marker: "###<!-- {mark} ANSIBLE MANAGED BLOCK -->###"
    block: |
      export RBENV_ROOT="${HOME}/.rbenv"
      export PATH="${RBENV_ROOT}/bin:${PATH}"
      eval "$(rbenv init -)"
- name: Check the Ruby Version Installed
  shell: bash -lc "rbenv versions | grep {{ruby_version}} | tr '*' ' ' | sed 's/^[ \t]*//' | cut -f1 -d' '"
  register: installed_ruby
- name: Install Ruby
  shell: bash -lc "RUBY_CONFIGURE_OPTS="--disable-install-rdoc" rbenv install -s {{ ruby_version }}"
  when: installed_ruby.stdout == ""
- name: Check Current Ruby Version
  shell: bash -lc "rbenv version" | grep "{{ ruby_version }}" | cut -f1 -d' '
  register: current_ruby_version
- name: Set default Ruby version
  shell: bash -lc "rbenv global {{ ruby_version }} && rbenv rehash"
  when: ruby_version not in current_ruby_version.stdout
- name: Install bundler and so on
  shell: bash -lc "gem install {{ item }}"
  loop:
    - rbenv-rehash
    - bundler -v {{ bundler_version }}
    - rails
~~~

rbenvで指定したrubyのバージョンはvarsに書いておきます。このくらいなら直書きでも大丈夫ですが、、

~~~yaml:./roles/ruby/vars/main.yml
---
ruby_version: 2.6.3
bundler_version: 2.1.4
~~~

### nginxの設定

nginx側のインストール＆設定
ポイントはALBからDNS名(デフォルトでついてくる長いやつ）を抜いて、nginxのコンフィグを書き換えています。
AWS側の環境はTerrafromで作成したのでALBの名前がTerraformにしています。
```server_names_hash_bucket_size```はAWSのデフォルトでつくDNS名が長すぎてnginxが起動しないので変えてます

~~~yaml:./roles/ruby/vars/main.yml
---
- name: install nginx
  command: amazon-linux-extras install nginx{{ nginx_version }} -y
  args:
    creates: /sbin/nginx
  become: yes
- name: configuration nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/conf.d/sampleapp.conf
  become: yes
  notify: restart_nginx
- name: get alb_dns_name
  delegate_to: 127.0.0.1
  elb_application_lb_info:
    names: "Terraform"
  register: alb
  run_once: true
- name: replace dns name
  replace:
    path: /etc/nginx/conf.d/sampleapp.conf
    regexp: "ALB_DNS_NAME"
    replace: "{{ alb.load_balancers.0.dns_name }}"
  become: yes
  notify: restart_nginx
- name: insert server_name_hash
  lineinfile:
    path: /etc/nginx/nginx.conf
    insertafter: "http {"
    line: "server_names_hash_bucket_size 128;"
    state: present
  become: yes
~~~

~~~yaml:./roles/nginx/vars/main.yml
---
nginx_version: 1.12
~~~


nginxのコンフィグ

~~~yaml:./roles/nginx/templates/nginx.conf.j2
upstream unicorn_server {
    server unix:/var/www/projects/sample-app/tmp/unicorn.sock
    fail_timeout=0;
}

server {
    listen 80;
    client_max_body_size 4G;
    server_name ALB_DNS_NAME;
    keepalive_timeout 5;
    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;

    # Location of our static files
    root /var/www/projects/sample-app/public;

    location ~ ^/assets/ {
        root /var/www/projects/sample-app/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_server;
            break;
        }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /var/www/projects/sample-app/public;
    }
}
~~~

nginxのハンドラー

~~~yaml:./roles/ruby/vars/main.yml
---
- name: restart_nginx
  systemd:
    name: nginx
    state: restarted
  become: true
~~~

### playbook

最後に全部まとめたplaybookの中身
```hosts```はAWSのEC2についてるタグを指定します。（stageのところに実際についてるタグ名をつける）

~~~yaml:./roles/ruby/vars/main.yml
---
- name: test
  hosts: tag_env_stage
  gather_facts: no
  roles:
    - role: common
      tags: base_install
    - role: ruby
      tags: ruby_insall
    - role: nginx
      tags: nginx_install
~~~