Ansible覚え書き

Ansibleに関する自分のための覚え書き。常時更新。内容が増えたらページを分けます。

# インストール

## CentOS7

``` shell
### 必要なパッケージのインストール
$ sudo yum install gcc python-devel python-yaml libffi-devel openssl-devel
$ sudo sh -c "curl -kL https://bootstrap.pypa.io/get-pip.py | python"

### 念のためsetuptoolsのアップグレード
$ sudo pip install --upgrade setuptools

### ansibleのインストール
$ sudo pip install ansible
```

プロキシ設定が必要な場合

``` shell
### yumのプロキシ設定
$ sudo vi /etc/yum.conf
================================================
...
proxy=http://proxy.example.com:8080
================================================

### 必要なパッケージのインストール
$ sudo yum install python-devel python-yaml libffi-devel openssl-devel

### pipのインストールスクリプト取得
$ curl --proxy http://proxy.example.com:8080 -O https://bootstrap.pypa.io/get-pip.py

### pipのインストール
$ LANG=C sudo sh -c "python get-pip.py --proxy='http://proxy.example.com:8080'" 
================================================
Collecting pip
  Downloading pip-9.0.1-py2.py3-none-any.whl (1.3MB)
    100% |################################| 1.3MB 888kB/s
Collecting setuptools
  Downloading setuptools-33.1.1-py2.py3-none-any.whl (472kB)
    100% |################################| 481kB 2.0MB/s
Collecting wheel
  Downloading wheel-0.29.0-py2.py3-none-any.whl (66kB)
    100% |################################| 71kB 6.8MB/s
Installing collected packages: pip, setuptools, wheel
Successfully installed pip-9.0.1 setuptools-33.1.1 wheel-0.29.0
================================================

### 念のためsetuptoolsのアップグレード
$ LANG=C sudo pip --proxy http://proxy.example.com:8080 install --upgrade setuptools
================================================
Requirement already up-to-date: setuptools in /usr/lib/python2.7/site-packages
================================================

### ansibleのインストール
$ LANG=C sudo pip --proxy http://proxy.example.com:8080 install ansible
================================================
Collecting ansible
  Downloading ansible-2.2.1.0.tar.gz (2.5MB)
    100% |################################| 2.5MB 428kB/s
Collecting paramiko (from ansible)
  Downloading paramiko-2.1.1-py2.py3-none-any.whl (172kB)
    100% |################################| 174kB 2.1MB/s
...

================================================
```

# モジュール

## [Files](http://docs.ansible.com/ansible/list_of_files_modules.html)

* [ディレクトリが無かったら作成する](http://qiita.com/hnakamur/items/b5a17d8cb289432014d5)

## [git](http://docs.ansible.com/ansible/git_module.html)

``` shell:git
### NeoBundleを~/.vim/bundle配下にcloneする。depth=1で最新のcommitだけ取ってくる。
git: dest=~/.vim/bundle/neobundle.vim repo=git://github.com/Shougo/neobundle.vim depth=1
```

## [copy](http://docs.ansible.com/ansible/copy_module.html)

``` shell:copy
### .vimrcの設定ファイルを作成。ファイルはfilesディレクトリに配置しておく
copy: src=.vimrc dest=~/.vimrc mode=0644 backup=yes
```

# -mで指定して使われやすいモジュール

## ping
hostsファイルを作成し、対象のサーバをping確認

```
$ vi hosts
===================
[ansible_test]
mydev ansible_ssh_host=192.168.33.32
===================
# hostsファイルは単にIPアドレスを羅列するだけでもとりあえず使える

$ ansible -i hosts 192.168.33.32 -u vagrant -m ping -k -c paramiko
===================
SSH password:
mydev | success >> {
    "changed": false,
    "ping": "pong"
}
===================
```

## setup
サーバの情報を収集して出力する

``` shell
### 単純にホストを指定するだけなら以下のようにシンプルに書ける
$ vi hosts
================================================
192.168.33.32
================================================

$ ansible -i hosts 192.168.33.32 -u vagrant -m setup -k -c paramiko
```

## オプション
| オプション | 意味 |
|:-----:|:---------:|
| -i | hostsファイル指定。このファイルにあるホスト以外には実行できない |
| -u | 接続先のユーザ名 |
| -m | モジュール指定。ここではping |
| -k | パスワードが必要な場合指定 |
| -c paramiko | sshにParamikoを利用する |

* -c paramiko はsshpassをインストールしてくださいというメッセージを回避したい場合つける

# サンプル

## フルパスからディレクトリまでのパスとファイル名をそれぞれに変数に入れててループする

このファイルはあそこ、あのファイルはここに配置したいなぁという時。
例えば

``` shell
app/controllers/gwbbs/admin/piece/menus_controller.rb
app/controllers/gwbbs/admin/basic_controller.rb
...
```
のようなパスから

``` shell
### ループ1回目
file_name=menus_controller.rb
dir_path=app/controllers/gwbbs/admin/piece

### ループ2回目
file_name=basic_controller.rb
dir_path=app/controllers/gwbbs/admin
```

のようにディレクトリのパスとファイル名をそれぞれ変数に入れてループしたい時は以下のようにする。

``` shell
### 1value_to_2variable.yml
================================================
---
- hosts: all
  tasks:
    - name: フルパスをディレクトリとファイル名に分けて変数に格納する
      include: 1value_to_2variable_work.yml
      with_items:
        - fullpath: app/controllers/gwbbs/admin/piece/menus_controller.rb
        - fullpath: app/controllers/gwbbs/admin/basic_controller.rb
      loop_control:
        loop_var: fullpath_for_work
================================================

### 1value_to_2variable_work.yml
================================================
---
- name: ファイル名を変数file_nameに格納する
  shell: basename {{ fullpath_for_work.fullpath }}
  register: file_name

- name: ディレクトリのパスを変数dir_pathに格納する
  shell: echo {{ fullpath_for_work.fullpath }} | sed -e 's%/{{ file_name.stdout }}%%'
  register: dir_path

- debug: var=file_name.stdout
  ignore_errors: True

- debug: var=dir_path.stdout
  ignore_errors: True
================================================
```

実行結果

``` shell
$ ansible-playbook -i "127.0.0.1," -k 1value_to_2variable.yml -c paramiko
================================================
SSH password:

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [フルパスをディレクトリとファイル名に分けて変数に格納する] ********************************************
included: /home/prefuser/pref_tokushima_jp/deploy/jorurigw/1value_to_2variable_work.yml for 127.0.0.1
included: /home/prefuser/pref_tokushima_jp/deploy/jorurigw/1value_to_2variable_work.yml for 127.0.0.1

TASK [ファイル名を変数file_nameに格納する] **************************************************
changed: [127.0.0.1]

TASK [ディレクトリのパスを変数dir_pathに格納する] ***********************************************
changed: [127.0.0.1]

TASK [debug] *******************************************************************
ok: [127.0.0.1] => {
    "file_name.stdout": "menus_controller.rb"
}

TASK [debug] *******************************************************************
ok: [127.0.0.1] => {
    "dir_path.stdout": "app/controllers/gwbbs/admin/piece"
}

TASK [ファイル名を変数file_nameに格納する] **************************************************
changed: [127.0.0.1]

TASK [ディレクトリのパスを変数dir_pathに格納する] ***********************************************
changed: [127.0.0.1]

TASK [debug] *******************************************************************
ok: [127.0.0.1] => {
    "file_name.stdout": "basic_controller.rb"
}

TASK [debug] *******************************************************************
ok: [127.0.0.1] => {
    "dir_path.stdout": "app/controllers/gwbbs/admin"
}

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=11   changed=4    unreachable=0    failed=0
================================================
```


## test.yml
対象のサーバで echo "hello"を実行する

```
$ vi hosts
===================
[ansible_test]
mydev02 ansible_ssh_host=192.168.33.32
mydev03 ansible_ssh_host=192.168.33.33
===================
 
$ vi test.yml
===================
- hosts: all
  user: vagrant
  gather_facts: no
  tasks:
    - name: run command and ignore the result
      command: /bin/echo "hello"
      register: hello

    - debug: var=hello.stdout
      ignore_errors: True
===================
```
 
gather_facts: no   実行前に対象サーバの情報を収集する。
時間がかかるのでテストはnoにしている。

```
$ ansible-playbook -i hosts test.yml -vvv -k
SSH password:
PLAY [all] ********************************************************************
TASK: [run command and ignore the result] *************************************
<192.168.33.32> ESTABLISH CONNECTION FOR USER: vagrant on PORT 22 TO 192.168.33.32
<192.168.33.33> ESTABLISH CONNECTION FOR USER: vagrant on PORT 22 TO 192.168.33.33
...
TASK: [debug var=hello.stdout] ************************************************
<192.168.33.32> ESTABLISH CONNECTION FOR USER: vagrant on PORT 22 TO 192.168.33.32
<192.168.33.33> ESTABLISH CONNECTION FOR USER: vagrant on PORT 22 TO 192.168.33.33
ok: [mydev02] => {
    "hello.stdout": "hello"
}
ok: [mydev03] => {
    "hello.stdout": "hello"
}
PLAY RECAP ********************************************************************
mydev02                    : ok=2    changed=1    unreachable=0    failed=0
mydev03                    : ok=2    changed=1    unreachable=0    failed=0
```

## hand_out_sshkey.yml
対象のサーバにSSH公開鍵を設置する。このままだと既存に設定があっても追記してしまうので
本来は設定があれば追記しないような内容にした方がよい。いずれページを分ける予定。

```
$ vi hosts
===================
[ansible_test]
mydev02 ansible_ssh_host=192.168.33.32
===================
 
$ vi hand_out_sshkey.yml
===================
- hosts: all
  user: vagrant
  gather_facts: no
  vars:
    user_name: vagrant
    user_group: vagrant
    src_public_key_file: /home/vagrant/.ssh/id_rsa.pub
    dest_home_path: /home/{{user_name}}
  tasks:
    - copy: src={{src_public_key_file}} dest={{dest_home_path}} owner={{user_name}} group={{user_group}} mode=0600
    - command: mkdir {{dest_home_path}}/.ssh creates={{dest_home_path}}/.ssh
    - shell: cat {{dest_home_path}}/id_rsa.pub >> {{dest_home_path}}/.ssh/authorized_keys removes={{dest_home_path}}/.ssh
    - command: chmod 600 {{dest_home_path}}/.ssh/authorized_keys removes={{dest_home_path}}/.ssh/authorized_keys
    - command: rm {{dest_home_path}}/id_rsa.pub removes={{dest_home_path}}/id_rsa.pub
===================
``` 
vars: 変数の設定

```
$ ansible-playbook -i hosts hand_out_sshkey.yml -vvv -k
SSH password:
PLAY [all] ********************************************************************
TASK: [copy src=/home/vagrant/.ssh/id_rsa.pub dest=/home/vagrant owner=vagrant group=vagrant mode=0600] ***
...
PLAY RECAP ********************************************************************
mydev02                    : ok=5    changed=5    unreachable=0    failed=0


$ ssh -l vagrant 192.168.33.32
Last login: Tue Jul  8 09:07:13 2014 from 192.168.33.31
```

## リモートホストで実行結果を取得して利用する

MySQL5.7で/var/log/mysqld.logの中にある仮パスワードを取得してrootパスワードを設定する例

``` shell
---
- hosts: webservers
  gather_facts: no
  sudo: yes
  tasks:
    - name: MySQL Communityのrpmのインストール
      yum: name=http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm

    - name: 必要なパッケージのインストール
      yum: name={{ item }} state=latest
      with_items:
        - mysql-community-server

    - name: mysqlの起動と自動起動設定
      service: name=mysqld state=started enabled=yes

    - name: 仮パスワードの取得
      shell: grep 'password is generated' /var/log/mysqld.log | awk -F'root@localhost:' '{print $2}' | tr -d ' '
      register: mysql_tmp_pass

    - name: rootユーザのパスワード変更
      shell: /usr/bin/mysql -u root -p'{{mysql_tmp_pass.stdout}}' -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'P@ssw0rd'"
      ignore_errors: True
```

## オプション

### よく使うオプション
| オプション | 意味 |
|:-----:|:---------:|
| -k | sshパスワードを入力する |
| -K | sudoパスワードを入力する |
| -c paramiko | sshにParamikoを利用する |
| -vvv | vの数が多いほど情報量が多いデバッグ用オプション |

* -c paramiko をつけないとsshpassをインストールしてくださいという警告が出ることが有る
    * sshpassをインストールすれば回避できる
    * そもそも鍵認証で接続するという方法もある。こっちが主流？
* -vvv は後ほどある実行関連オプションの--checkをつけると標準出力はstdoutに出力されない

### 実行関連オプション

| オプション | 意味 |
|:-----:|:---------:|
| --syntax-check | 文法チェック |
| --list-tasks | タスクリストの表示 |
| --check | ドライrun |
| --step | step実行 |
| (何もなし) | 実行 |

## Symlinkが存在するかどうかの確認

``` shell
- name: /home/vagrant/hogeがsymlinkであることの確認
  stat: path=/home/vagrant/hoge
  register: st

- fail:
    msg: "/home/vagrant/hogeがありません"
  when: st.stat.islnk is not defined

- fail:
    msg: "/home/vagrant/hogeはsymlinkではありません"
  when: st.stat.islnk == False
```

## PATHを追加する

envの処理でPATHが通らなくてエラーになる場合

```
shell: /usr/local/rvm/rubies/ruby-2.3.4/bin/gem install bundler
================================================
...
    "msg": "/usr/bin/env: ruby: No such file or directory",
    "rc": 127,
    "stderr": "/usr/bin/env: ruby: No such file or directory\n",
    "stderr_lines": [
        "/usr/bin/env: ruby: No such file or directory"
...
================================================

### 以下でPATHを設定して実行できる
shell: PATH=/usr/local/rvm/rubies/ruby-2.3.4/bin:${PATH} && gem install bundler
================================================
    "stderr": "",
    "stderr_lines": [],
    "stdout": "Successfully installed bundler-1.15.3\nParsing documentation for bundler-1.15.3\nInstalling ri documentation for bundler-1.15.3\nDone installing documentation for bundler after 4 seconds\n1 gem installed",
================================================
```

environmentで設定すれば一つ一つ設定する必要がなくなる

``` shell
---
- hosts: containers
  connection: docker
  gather_facts: no
  become: yes
  vars:
    rvm_ruby_version: 2.3.4
  environment:
    PATH: /usr/local/rvm/rubies/ruby-{{ rvm_ruby_version }}/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
  roles:
...
```

# リンク

* [はじめてAnsibleを使う人が知っておきたい7つのモジュール](http://www.infiniteloop.co.jp/blog/2013/08/ansible/)
* [explain about module(Japanese)](http://yteraoka.github.io/ansible-tutorial/ansible-in-detail.html)
* [Ansibleでファイルの中身を一括して置換する方法](http://qiita.com/myaaaaa_chan/items/f0a6258a757f3b18d65e)
* [GATHERING FACTS(Ansible Tutorial)](http://yteraoka.github.io/ansible-tutorial/#gather-facts)  
setupモジュールにつて
* [Inventory File](https://github.com/yteraoka/ansible-tutorial/wiki/Inventory-File)  
グループ化やansible用環境変数など