!TL;DR
Ansible知ってるandソースだけ欲しい人はここで終了です。
https://github.com/Blue-Pix/eclair
なぜeclairという名前で作ったかというと、Ansibleの語源からインスピレーションを得ました。
Ansible is 何?
インフラのオートメーションツール。
ChefとかPuppetと並んで引き合いに出されます。
環境構築 is 時間の無駄
・プロジェクトごとに毎回Vagrant構築するのしんどい
・テキストベースで環境構築マニュアル作るのしんどい
・コマンド1つ1つメモるのか
・構築するものが多い(Ruby, Node.js, MySQL, ElasticSearch, Redis, ...etc.)
・プロジェクトごとに細かいバージョン違って死ぬ
Why not Chef ?
ざっくりChefとAnsibleについて調べた結果、
- 導入コストが少なそうだったから
- エージェントレスだから(対象サーバーにPythonさえ入ってれば構築できる)
- シンプルだから
- Pythonとyamlで書くから(僕はRubyが苦手です)
です。
Ansible
- yamlで書く
- エージェント不要
- ssh経由で構築を行う
- Pythonがインストールされてる環境ならOK
- build and scrapに向いてる
Chef
- Rubyで書く
- Chef-Serverとかknifeとか特有のツールがいる
- 複数台のホスト管理に向いてる(というか前提?)
サクッと開発環境を作るならAnsible一択かなあと思います。
今回の目的
macOS High Sierra
でCentOS7.2
のVagrantを立てて、
以下をインストールします。
- Ruby 2.4.2
- MySQL5.7
- Node.js 8.1.2
- Redis stable
- ElasticSearch 5.6.8
なぜこのバージョンかと言うと、携わるプロジェクトがこの構成だったからです。
バージョンはそれぞれ好みのものに変えれる用に作ってはありますが、
検証はしてないので互換性がないやつは普通にエラー出ると思います。
MySQL
は5.6
, 5.7
に対応させてありますが、5.1
とかはたぶん無理です。
パスワード周りが5.6
と5.7
で地味に違うのめんどくさいよね。
Ruby
はrbenv
, Node.js
はn
を使って入れます。
RailsアプリはGit clone済みで、Vagrantにマウントする想定です。
OSはバージョン以上に深刻な問題で、CentOS7
系以外では無理です。
どうしてもUbuntu
で開発したいんだ俺は!!という方はせっせと
yum
をapt-get
に書き換えたりして対応してください。
設定ファイルの位置とかsystemdとか細かく違うので同じCentOSと言えど6系も無理です。
VMの構築
Vagrantはインストールしておいてくださいね。
$ vagrant box add CentOS7.2 https://github.com/CommanderK5/packer-centos-template/releases/download/0.7.2/vagrant-centos-7.2.box
$ vagrant init CentOS7.2
# コメントアウトを外す
config.vm.network "private_network", ip: "192.168.33.10"
# フォワードしたいポートを追記する
config.vm.network "forwarded_port", guest: 3000, host: 30000
config.vm.network "forwarded_port", guest: 3035, host: 30350
# マウントするフォルダを追記する
config.vm.synced_folder "./app/my_app", "/home/vagrant/my_app", owner: "vagrant", group: "vagrant"
マウントするRailsアプリと今回の環境構築のフォルダは
同じ階層に置いてます。プロジェクトごとにフォルダ作って、
アプリと構成管理をそれぞれgit clone
してくる流れで開発してます。
VagrantFile
は元々あるVagrantFile.example
をコピーして作ってください。
ここで1回起動しておきます。
$ vagrant up
Ansible
はssh経由でリモートホストの環境を構築するので、
sshでこのVMにつなげるように手配して置く必要があります。
VMのホスト名をhoge
として、sshの接続情報をmacのホームディレクトリに記述します。
~/.ssh/config
に書くか、プロジェクトごとに別ファイルを切るかはお好みだと思います。
どこに書こうが特に支障はありません。
$ vagrant ssh-config --host hoge >> ~/.ssh/hoge-config
sshでログインできるか確認
$ ssh hoge
Last login: Sat Jan 6 19:39:59 2018 from 10.0.2.2
[vagrant@localhost ~]$
構築終わり。
開発はデフォルトのvagrantユーザーで進めるので、
別ユーザーでやりたい場合は適宜作ってください。
Ansibleのインストール
brew
一発です。
$ brew install ansible
$ ansible --version
ansible 2.4.2.0
$ ansible-playbook --version
ansible-playbook 2.4.2.0
Ansibleの初期設定
どのソフトをインストールして設定ファイルをこう書き換えて〜
みたいなことはPlaybook
と呼ばれるファイルに記述するのですが、
それとはまた別のレイヤーで、どのホストに対してどういう構成を当てるかや、
変数の宣言などをあらかじめしておかなければならないのでそこの設定です。
inventory file
対象となるホストを記述するファイルです。
yaml
ではなくINI
形式。
ファイル名に決まりはないですが、hosts
という名前が一般的です。
グループ名とホスト名の対ですが、グループ名は省略できます。
hoge
WebサーバーやらDBサーバーやら一緒くたにするので
グループ化する必要はないのでホスト名だけ。
Docker的な思考でサービスごとに分離したい時や、
本番環境で運用する場合はこの辺り整備する必要があります。
今回はローカルでの開発用なので思考停止してください。
Ansibleを本番で使おうとする場合は、
もっともっともっともっと考え抜いて設計しなければいけません。
ansible.cfg
Ansibleの設定ファイルです。
ansible
コマンドを使うときにオプションとしても指定できますが、
ファイルで定義していた方が楽なので。
[defaults]
inventory = hosts
retry_files_enabled = False
[privilege_escalation]
become = True
[ssh_connection]
control_path = %(directory)s/%%h-%%r
ssh_args = -o ControlPersist=15m -F /Users/hoge/.ssh/hoge-config -q
scp_if_ssh = True
[defaults]
inventory: 先ほど定義したホスト情報のファイル名を指定
retry_files_enabled: 構成に失敗した時に.retry
ファイルを作るかどうか[privilege_escalation]
become: 管理者権限で実行するかどうか[ssh_connection]
control_path: コントロールソケットを保持するパス
ssh_args: sshコマンドの引数
scp_if_ssh: scpでファイルを転送するかどうか
重要なのはsshの接続情報をちゃんと参照できるようにするのと、
inventory
でホスト情報を指定すること。
また、become: True
で管理者権限でサーバーをいじるようにすること。
vagrant
ユーザーでは触れないところがあるので、基本はroot権限で
インストールやら何やかんやをして、必要に応じてvagrant
ユーザーに
戻るという手順を取ることにする。
▼参考
https://dev.classmethod.jp/server-side/ansible/ansible-tutorial-2017/
▼他にもたくさんオプションがあるので詳しくは公式を
http://docs.ansible.com/ansible/latest/intro_configuration.html
▼スケルトンがあるのでそれを使うのもあり
https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg
site.yml
Playbookというやつです。
ここにどのhost
に対してどのtask
を実行するかを定義します。
- hosts: hoge
roles:
- init
- mysql
- ruby
- nodejs
- redis
- elasticsearch
task
はベタ書きするのではなく、role
という単位にまとめてます。
1role
1middleware
のイメージです。
こうすることで見通しも良くなるし、
role
ごとに再利用可能になり、
このプロジェクトではredis
使わないとかなったら
- redis
だけコメントアウトすればよくなります。
Ansibleの実行
hosts
, ansible.cfg
, site.yml
が書けたら、
以下のコマンドを打つだけで構築が完了します。
$ ansible-playbook site.yml
Rails開発環境できましたね。
これ以降はそれぞれのrole
のtask
について
Ansibleの解説になるので興味ない人は読み飛ばしちゃってください。
変数について
Ansibleにおいて変数を定義できる場所はたくさんあります。
ゆえに散らばらせるとたくさん上書きされてよくわからないことになります。
シンプルにするために以下のように自分ルールを定めます。
-
role
に依存するものはroles/xxx/vars/main.yml
に定義する。
__version: stable
__port: 6379
__data_dir: /var/lib/redis
__log_dir: /var/log/redis
__log_level: notice
versionなんかほとんどのミドルウェアで使う変数名だと思うので、
グローバルに定義すると上書きが起こってしまい不安です。
ミドルウェアごとにプレフィックスをつけるのもありっちゃありですが、
そもそもスコープをrole
内に絞るのが自然かと思います。
あ、プレフィックスに__
を付けているのはpythonicな習慣です。
あらかじめ定義してあり、代入は禁止ということを表したいのでこういう書き方を取りました。
というのも、task
の中で標準出力を変数に格納する場面があるのですが、
タスク実行中に定義された変数と、あらかじめ定義してある変数をごっちゃにしたくないのです。
また、変数を使う時には基本ブラケットで括るのですが({{ __version }}
)、
when
オプションの中などではWarningが出るため、生で書かなければいけない時があります。
when: __version == 5.6
__
がないと変数であることが非常に分かりくいので可読性のためにもつけることにしてます。
when: version == 5.6
-
role
をまたいで使うものはsite.yml
に定義する。
- hosts: hoge
roles:
- init
- mysql
- ruby
- nodejs
- redis
- elasticsearch
vars:
__working_user: vagrant
__working_user
はいろんなrole
で参照しているのでグローバルにここで定義します。
色々とやり方はありますが自分好みの構成はこれです。
変数の管理とか、ディレクトリ構成は以下の記事が参考になると思います。
https://qiita.com/kkitta/items/27f8aca89e55b719cb6f
https://qiita.com/ArimaRyunosuke/items/1f9d840311584d8160bc
https://qiita.com/kotarella1110/items/79af4485bd7985935d6b
または公式のBest Practiceに倣う。
Rolesの作成
ではでは具体的なtask
の記述について。
init
.bash_profile
の配置と、ロケール設定です。
色々とインストールをしていく中でPATH
がカオスなことにならないように、
以下の記事のようなフラグメント化を行なっています。
.bash_profileはフラグメント化してプロビジョニングしやすくしよう!
- name: deploy .bash_profile
template:
src: .bash_profile
dest: ~/.bash_profile
mode: 0644
become_user: "{{ __working_user }}"
roles/init/templates
にデフォルトの.bash_profile
を用意して、
vagrantユーザーのホームに置く。実行時はrootユーザーなので、
become_user
でvagrantユーザーに成りすます。
- name: mkdir .bash.d
file:
path: ~/.bash.d
state: directory
mode: 0755
become_user: "{{ __working_user }}"
ディレクトリの作成にはFilesモジュールの
state: directory
を使う。
shell: mkdir ~
だと自分で冪等性を管理しなくてはいけないので面倒。
Ansible組み込みのモジュールを使うと自動的に冪等性を保証してくれるので積極的に使う。(yumなど)
- name: stop and disable firewalld
service:
name: firewalld
state: stopped
enabled: no
ファイアウォールを止める。
systemctl
コマンドはSystemモジュールのservice
で扱える。
- name: set LANG={{ __locale }} in /etc/locale.conf
lineinfile:
dest: /etc/locale.conf
regexp: "^LANG="
line: "LANG={{ __locale }}"
backup: yes
ロケールを日本語にする。
すでにある設定ファイルなどの中身を書き換える時はFilesモジュールのlineinfile
で正規表現を使って置換する。
一応backup: yes
で破壊的な変更を防ぐ。
MySQL
個人的にMySQL入れるのが一番めんどくさい。
パスワードポリシーとかバージョンによって違ったり、
community server
どうやって入れるんだっけとか毎回調べてる気がする。
Ansibleで書くときも厄介なのがパスワード問題。
- name: check if pre-installed mariadb exists
yum:
list: mariadb-libs
register: mariadb
デフォルトでmariadbが入っているかどうかチェックします。
結果はmariadb
という変数に格納します。
- name: uninstall pre-installed mariadb
yum:
name: mariadb-libs
state: absent
最初から入ってるmariadbをアンインストールする。
yumモジュールを使うのでアンイストール済みかどうかは勝手にチェックしてくれる。
- name: remove pre-installed mariadb's file
file:
path: /var/lib/mysql
state: absent
when: mariadb.results[0].yumstate == 'installed'
mariadbをアンインストールするときに、変な影響が出ないように関連するファイルも削除する。
when
オプションを付けているのは、冪等性のため。
2回目以降実行するときに、新しくインストールしたMySQLのファイルを削除してしまうのを防ぐため。
- name: add yum repository
yum:
name: "{{ __repo_url }}"
state: present
mysql57-community-release-el7-11.noarch.rpm
のyumリポジトリを追加する。
- name: intall yum-utils
yum:
name: yum-utils
state: present
バージョンによってリポジトリを切り替えなければいけないため、
yum-config-manager
コマンドを使えるようにyum-utils
をインストールする。
- name: switch yum repository (for version 5.6)
shell: |
yum-config-manager --disable mysql57-community
yum-config-manager --enable mysql56-community
when: __version == 5.6 and mariadb.results[0].yumstate == 'installed'
- name: switch yum repository (for version 5.7)
shell: |
yum-config-manager --disable mysql56-community
yum-config-manager --enable mysql57-community
when: __version == 5.7 and mariadb.results[0].yumstate == 'installed'
バージョンによってリポジトリを切り替える。
shell
で処理するので、2回目以降不要なchanged
が出ないように、
mariadbをアンインストールした時(=初回実行時)のみ実行するようにする。
ちなみに!!!!
when
オプションの中で変数を{{ }}
で括ろうとすると以下のエラーが出る。
[WARNING]: when statements should not include jinja2 templating delimiters
such as {{ }} or {% %}.
見にくいが`{{ }}`なしで変数名を記述する。
```yaml:roles/mysql/tasks/main.yml
- name: install MySQL{{ __version }}
yum:
name: "{{ item }}"
state: present
with_items:
- mysql
- mysql-devel
- mysql-server
- mysql-utilities
- MySQL-python
色々インストールします。複数個処理するときは
with_items
でスッキリ書く。
- name: start and enable server
service:
name: mysqld
state: started
enabled: yes
1回起動しておきます。自動起動の設定も
enabled
で有効に。
- name: check if my.cnf.bk already deployed
stat:
path: "/etc/my.cnf.bk"
register: default_my_conf_backup
以降の処理の冪等性を保つため、
my.cnf
のバックアップが作られているかどうかを確認します。作成済みであれば、これ以降の処理は実行済みとみなしてスキップします。
- name: get default MySQL root users's password
shell: |
less /var/log/mysqld.log | grep password | awk '{ print $NF }'
register: mysql_default_password
when: not default_my_conf_backup.stat.exists and __version == 5.7
rootの初期パスワードがログに吐かれているので取り出します。5.6の場合はファイルが違うのでスキップ。
- name: change password policy
shell: |
mysql -u root -p'{{ mysql_default_password.stdout }}' --connect-expired-password -e "SET GLOBAL validate_password_length='{{ __password_length }}';"
mysql -u root -p'{{ mysql_default_password.stdout }}' --connect-expired-password -e "SET GLOBAL validate_password_policy='{{ __password_policy }}';"
when: not default_my_conf_backup.stat.exists and __validate_password and __version == 5.7
先ほど取り出した初期パスワードでログインして、シェル上からSQLステートメントを発行します。
connect-expired-password
オプションを付けないとログインに失敗するので注意。
ここではパスワードポリシーを変更する場合のみ、指定のポリシーに変更する処理を行っています。
また、バージョン5.6だとそもそも
validate_password
プラグインが有効になっていないのでスキップする。
- name: disable validate password
shell: |
mysql -u root -p'{{ mysql_default_password.stdout }}' --connect-expired-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ mysql_default_password.stdout }}';"
mysql -u root -p'{{ mysql_default_password.stdout }}' --connect-expired-password -e "uninstall plugin validate_password;"
when: not default_my_conf_backup.stat.exists and not __validate_password and __version == 5.7
パスワードポシリーを無効化したい場合のタスクです。
uninstall plugin validate_password
で無効化できるのですが、先に初期パスワードをリセットしないとこのステートメントを実行することはできません。
適当なパスワードに再設定してもいいのですがシンプルさを保つため、
前述のファイルから取り出した初期パスワードでそのまま上書きます(笑)
これでもパスワードはリセットされたと認識してくれます。
- name: change root user password (for version 5.6)
shell: |
/usr/bin/mysqladmin -u root password "{{ __root_new_password }}"
when: not default_my_conf_backup.stat.exists and __version == 5.6
- name: change root user password (for version 5.7)
shell: |
mysql -u root -p'{{ mysql_default_password.stdout }}' --connect-expired-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ __root_new_password }}';"
when: not default_my_conf_backup.stat.exists and __version == 5.7
rootユーザーのパスワードを指定のパスワードに変更します。
5.6と5.7でやり方が違う。5.7は前述のタスクでファイルから取り出したパスワードを使う。
- name: backup default my.cnf
shell: |
mv /etc/my.cnf /etc/my.cnf.bk
when: not default_my_conf_backup.stat.exists
my.cnf
のバックアップを作ります。
- name: deploy my.cnf
template:
src: "my.cnf_{{ __version }}.j2"
dest: "/etc/my.cnf"
owner: root
group: root
mode: 0644
when: not default_my_conf_backup.stat.exists
notify: restart mysqld
自分好みにカスタマイズした
my.cnf
を配置します。バージョンによって使えないオプションとかあるので注意。
配置したら設定を反映させるため、notify
を使って再起動を通知します。
handler
はroles/xxx/handlers/main.yml
に書く。
roles/mysql/handlers/main.yml
- name: restart mysqld
service:
name: mysqld
state: restarted
```yaml:roles/mysql/tasks/main.yml
- name: create database [{{ __project_db_name }}]
mysql_db:
login_user: root
login_password: "{{ __root_new_password }}"
name: "{{ __project_db_name }}"
state: present
encoding: "{{ __project_db_encoding }}"
when: __project_db_create
オプションでアプリのデータベースを作成します。今時のフレームワークはマイグレーションの仕組みがそれぞれあるので自分でデータベースを作成するのは稀かもしれませんが。
mysql_db
という便利なモジュールがあるのでそれを使います。
- name: create MySQL user [{{ __project_db_user_name }}] for database [{{ __project_db_name }}]
mysql_user:
login_user: root
login_password: "{{ __root_new_password }}"
user: "{{ __project_db_user_name }}"
password: "{{ __project_db_user_password }}"
host: localhost
priv: "{{ __project_db_name }}.*:ALL,GRANT"
state: present
when: __project_db_create
さっき作ったデータベース用のユーザーを作成します。
同じくmysql_user
というモジュールを活用します。(便利!)
Ruby
- name: install git and other dependencies
yum:
name: "{{ item }}"
state: latest
with_items:
- git
- nss
- nss-util
- nspr
- curl
Ruby入れるために色々インストールします。
- name: install rbenv
git:
repo: https://github.com/rbenv/rbenv.git
dest: ~/.rbenv
become_user: "{{ __working_user }}"
rbenv
インストールします。git
もモジュールあるよ!
- name: set global rbenv path
template:
src: 001_rbenv.sh
dest: ~/.bash.d/001_rbenv.sh
mode: 0755
become_user: "{{ __working_user }}"
rbenv
パス通します。
中身はこんな感じ。
roles/ruby/templates/001_rbenv.sh
export PATH=~/.rbenv/bin:~/.rbenv/shims:$PATH
eval "$(rbenv init -)"
```yaml:roles/ruby/tasks/main.yml
- name: install dependencies for ruby build
yum:
name: "{{ item }}"
state: present
with_items:
- gcc
- openssl-devel
- libyaml-devel
- libffi-devel
- readline-devel
- zlib-devel
- gdbm-devel
- ncurses-devel
Rubyビルドするために色々インストールします。
- name: install ruby-build
git:
repo: https://github.com/sstephenson/ruby-build.git
dest: ~/.rbenv/plugins/ruby-build
become_user: "{{ __working_user }}"
ruby-build
インストールします。
- name: make app directory (if rails new)
file:
path: "{{ __app_dir }}"
state: directory
mode: 0755
become_user: "{{ __working_user }}"
when: __rails_new
既存のRailsアプリがない場合、指定のバージョンでスケルトン作るのでアプリ用のディレクトリ作ります。
- name: check current ruby version
shell: |
/bin/bash -lc "ruby -v | grep -oP '^ruby \d{1}.\d{1}.\d{1}' | cut -f 2 -d ' '"
args:
chdir: "{{ __app_dir }}"
register: current_ruby_version
become_user: "{{ __working_user }}"
changed_when: False
Rubyのバージョンチェックします。初回実行時はシステムデフォルトのバージョン(2.0とか)が取れるので、それでRubyインストールするかどうかを判断します。
一点注意すべきなのは、shell
は/bin/sh
を使うので、bash
で通したPATHとかを使おうとしてもエラーになることです。/bib/bash -lc "コマンド"
で明示的にbashシェルで実行します。
- name: shell relogin
shell: |
/bin/bash -lc "source ~/.bash_profile"
become_user: "{{ __working_user }}"
when: current_ruby_version.stdout != __version
ログインし直してパス通します。
- name: install ruby
shell: |
/bin/bash -lc "rbenv install {{ __version }}"
/bin/bash -lc "gem install bundler"
/bin/bash -lc "rbenv rehash"
/bin/bash -lc "rbenv local {{ __version }}"
args:
chdir: "{{ __app_dir }}"
become_user: "{{ __working_user }}"
when: current_ruby_version.stdout != __version
指定のバージョンのRubyインストールします。で、アプリのルートで使います。
- name: check rails new succeeded
stat:
path: "{{ __app_dir }}/Gemfile.lock"
become_user: "{{ __working_user }}"
register: rails_new_gemfile
when: __rails_new
Railsアプリのスケルトンを作る場合のみ、すでにスケルトン作成済みかどうかチェックします。
- name: init bundler
shell: |
/bin/bash -lc "bundle init"
args:
chdir: "{{ __app_dir }}"
become_user: "{{ __working_user }}"
when: __rails_new and not rails_new_gemfile.stat.exists
- name: gem install rails
lineinfile:
path: "{{ __app_dir }}/Gemfile"
regexp: '# gem "rails"'
line: 'gem "rails", "{{ __rails__version }}"'
become_user: "{{ __working_user }}"
when: __rails_new and not rails_new_gemfile.stat.exists
スケルトン用の
Gemfile
作って、指定のバージョンのrailsを追記します。
- name: rails new
shell: |
/bin/bash -lc "gem install bundler"
/bin/bash -lc "rbenv rehash"
/bin/bash -lc "bundle install --path vendor/bundle --jobs=4"
/bin/bash -lc "bundle exec rails new -f {{ __app_dir }}"
args:
chdir: "{{ __app_dir }}"
become_user: "{{ __working_user }}"
when: __rails_new and not rails_new_gemfile.stat.exists
rails new
します。
- name: bundle install (if app already exists)
shell: |
/bin/bash -lc "bundle install"
args:
chdir: "{{ __app_dir }}"
become_user: "{{ __working_user }}"
when: current_ruby_version.stdout != __version and not __rails_new
すでにRailsアプリをgit clone済み(マウント済み)でスケルトンを作らない場合、初回
bundle install
します。
Node.js
- name: check if nodejs already installed
stat:
path: /usr/local/bin/node
register: nodejs_bin
すでにインストール済みかチェック。
- name: download and setup nodejs source
shell: |
curl -sL https://rpm.nodesource.com/setup_8.x | bash -
when: not nodejs_bin.stat.exists
インストールされていない初期状態であれば、
curl
でソースを持ってきてビルドします。
- name: install latest nodejs temporary
yum:
name: "{{ item }}"
state: present
with_items:
- nodejs
when: not nodejs_bin.stat.exists
yum経由でインストールします。この時点では一時的なのでバージョンとかなんでもいいんです。
- name: install n
npm:
name: n
state: present
global: yes
when: not nodejs_bin.stat.exists
npm
コマンドが使えるようになったので、バージョン管理のためのn
というパッケージをインストールします。
- name: install version specific nodejs using n
shell: |
n {{ __version }}
when: not nodejs_bin.stat.exists
この
n
を使って好きなバージョンをインストールしちゃいましょう。
- name: set global npm path
template:
src: 201_nodejs.sh
dest: ~/.bash.d/201_nodejs.sh
mode: 0755
become_user: "{{ __working_user }}"
when: not nodejs_bin.stat.exists
NODE_PATH
をセットする設定を.bash_profile
に追加します。(直接追加してるわけではないけど)
中身はこれです。
roles/nodejs/templates/201_nodejs.sh
export NODE_PATH=$(npm root -g)
```yaml:roles/nodejs/tasks/main.yml
- name: install yarn
npm:
name: yarn
state: present
global: yes
yarn
よく使うので入れちゃいます。
Redis
- name: check if source already downloaded
stat:
path: /tmp/redis-{{ __version }}
register: redis_source
インストール済みかチェックする。
- name: download and unarchive source
unarchive:
src: http://download.redis.io/releases/redis-{{ __version }}.tar.gz
dest: /tmp
copy: no
when: not redis_source.stat.exists
まだインストールしてなかったらソース持ってきて解凍する。
unarchive
モジュール便利!
- name: make and install
make:
chdir: /tmp/redis-{{ __version }}
target: install
when: not redis_source.stat.exists
make
,make install
もモジュールでできちゃいます。
- name: check if binary source already deployed
stat:
path: /usr/local/bin/redis-server
register: redis_server_bin_check
redis-server
コマンドにパスが通ってるかチェックする。
- name: deploy binary source
shell: |
cp redis-server redis-cli redis-sentinel redis-benchmark redis-check-rdb redis-check-aof /usr/local/bin
args:
chdir: /tmp/redis-{{ __version }}/src
when: not redis_server_bin.stat.exists
通ってなかったら諸々パス通します。
- name: check if config file deployed
stat:
path: /etc/redis/{{ __port }}.conf
register: redis_conf
設定ファイル配置済みかチェックする。
- name: make config directory
file:
path: /etc/redis
state: directory
mode: 0755
redisの設定ファイル置き場作る。
- name: deploy config file
shell: |
mv redis.conf /etc/redis/{{ __port }}.conf
args:
chdir: /tmp/redis-{{ __version }}
when: not redis_conf.stat.exists
設定ファイル置く。
/etc/redis/ポート番号.conf
という形にしてる。
- name: edit config file
lineinfile:
path: /etc/redis/{{ __port }}.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: present
with_items:
- {regexp: "^dir ", line: "dir {{ __data_dir }}"}
- {regexp: "^logfile ", line: "logfile {{ __log_dir }}/{{ __port }}.conf"}
- {regexp: "^loglevel ", line: "loglevel {{ __log_level }}"}
- {regexp: "^daemonize ", line: "daemonize yes"}
- {regexp: "^port ", line: "port {{ __port }}"}
- {regexp: "^supervised ", line: "supervised systemd"}
設定ファイルまとめて書き換えてます。テンプレート化してもいいんだけど、バージョンによってどこまで差分出てくるかよく分からないので。。。
- name: create usergroup for redis daemon
group:
name: redis
state: present
system: yes
- name: create user for redis daemon
user:
name: redis
group: redis
state: present
createhome: no
system: yes
redis用のグループとユーザー作ります。
- name: create data and log directory
file:
path: "{{ item }}"
state: directory
owner: redis
group: redis
mode: 0755
with_items:
- "{{ __data_dir }}"
- "{{ __log_dir }}"
データとログの置き場作ります。
- name: check if service already daemonized
stat:
path: /etc/systemd/system/redis.service
register: redis_service
デーモン化されているかチェックする。
- name: daemonize service
template:
src: redis.service.j2
dest: /etc/systemd/system/redis.service
owner: root
group: root
mode: 0755
when: not redis_service.stat.exists
デーモン化して
systemd
に管理させます。
テンプレートはこんな感じ。
roles/redis/templates/redis.service.j2
[Unit]
Description=Redis
[Service]
Type=notify
ExecStart=/usr/local/bin/redis-server /etc/redis/{{ __port }}.conf
ExecStop=/usr/local/bin/redis-cli -p {{ __port }} shutdown
User=redis
Group=redis
[Install]
WantedBy=multi-user.target
```yaml:roles/redis/tasks/main.yml
- name: reload daemon
shell: |
systemctl daemon-reload
notify: start and enable server
when: not redis_service.stat.exists
再起動して終了。
Elasticsearch
- name: install JDK-1.8.0
yum:
name: "{{ item }}"
state: present
enablerepo: "*debug*"
with_items:
- java-1.8.0-openjdk-devel
- java-1.8.0-openjdk-debuginfo
JDKをインストールします。
- name: deploy yum repository info
template:
src: elasticsearch.repo
dest: /etc/yum.repos.d/elasticsearch.repo
mode: 0644
Elasticsearchのリポジトリを登録します。
中身はこんな感じ。
roles/elasticsearch/templates/elasticsearch.repo
[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
>他のバージョン使いたいときは適宜作ってくださいますよう。
```yaml:roles/elasticsearch/tasks/main.yml
- name: install
yum:
name: elasticsearch-{{ __version }}
state: present
指定のバージョンをインストールします。
- name: check if kuromoji plugin already installed
stat:
path: /usr/share/elasticsearch/plugins/analysis-kuromoji
register: kuromoji_check
kuromoji
プラグインがインストール済みかチェックします。yumとかで入れるわけではないのでこの辺は自前で冪等性を担保する必要があります。
- name: install kuromoji plugin
shell: |
./elasticsearch-plugin install analysis-kuromoji
args:
chdir: /usr/share/elasticsearch/bin/
when: not kuromoji_check.stat.exists
kuromoji
プラグインのインストール。
- name: edit elasticsearch.yml
lineinfile:
path: /etc/elasticsearch/elasticsearch.yml
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: "^http.port: ", line: "http.port: {{ __port }}" }
- { regexp: "^bootstrap.memory_lock: ", line: "bootstrap.memory_lock: true" }
- { regexp: "^path.logs: ", line: "path.logs: /var/log/elasticsearch" }
- { regexp: "^network.host: ", line: "network.host: 127.0.0.1" }
設定ファイルをよしなに書き換えます。
- name: unlock memory limit for elasticsearch user
lineinfile:
path: /etc/security/limits.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: "^elasticsearch soft memlock unlimited", line: "elasticsearch soft memlock unlimited" }
- { regexp: "^elasticsearch hard memlock unlimited", line: "elasticsearch hard memlock unlimited" }
- name: add LimitMEMLOCK option for elasticsearch.service
lineinfile:
path: /usr/lib/systemd/system/elasticsearch.service
regexp: "^LimitMEMLOCK"
line: "LimitMEMLOCK = infinity"
Elasticsearchがメモリを最大限使えるように解放します。
- name: edit jvm.options
lineinfile:
path: /etc/elasticsearch/jvm.options
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: "^-Xms", line: "-Xms{{ __jvm_xms }}" }
- { regexp: "^-Xmx", line: "-Xmx{{ __jvm_xmx }}" }
notify: start and enable service
jvmのヒープサイズを引き下げます。スペックにもよりますが、多分大半の場合メモリ不足になってElasticsearchが起動できなくなります。自分のマシンのスペックに応じて色々と変えてみてください。
以上でした
他によく使うミドルウェアってなんですかねー。
次はLAMP+Laravelでも作ろうかなー。