Linuxが立ち上がって、SSHログインできるマシンがあったとしましょう。それでもサーバとして使うには、gitやらhttpdやらをyum/aptしないと使えません。
それを何台も繰り返さなければいけない時は絶望します。
そんな時、リモートかつできるだけ自動でセットアップできると楽ですね。
bashスクリプトを書いて送り込んでもいいのですが、再利用できるように、ホスト名やアドレスが違っても対応できるものを書こうとすると面倒です。考えるだけでうへぁです。
そんなときは構成管理自動化ツール、Ansibleを使ってみるのが一つの手です。
Ansible is 何?
構成管理ツールです。ChefやらPuppetやらと同類。RedHatに買われました。
Ansibleでは、構成や実行したいコマンドをYAMLで記載します。Playbookと呼ぶそうです。
Chefのレシピみたいなものです。
構築したいパターンに合わせてPlaybookを用意すればよい、ということですね。
- いいところ:
- ssh(とPython)がセットアップされているマシンなら、まず管理対象にできる。管理対象のマシンに特別なエージェントを導入する必要はありません。
- yumやらapt一発でインストールできる。バックエンドにDBが必要、とかの前提条件がなく、あまり環境を汚さず軽量
- 必ずしもサーバが必要なわけではない。何ならローカルでPlaybookを走らせてもok。
- 冪等性。Playbookを走らせれば何回やっても基本同じ結果になる(理想的には)。
- よくないところ:
- Playbookやhostsの書き方など、結局Ansibleを使う上での学習コストはかかる
- GUI(Web UI)が欲しい?有料です(Ansible Tower)。
- OSのインストールすらだるい?別のツール(Vagrantとか)使ってね。
Ansibleのセットアップ
管理対象にはSSHサーバ(sshd)とPython(ここではPython2.7)をインストールしておきます。特別なセットアップではないので、特に迷わないはず。
別途コントローラ用のマシンを用意し、Ansibleをセットアップしていきます。
とりあえずPythonが入っていれば動かすことができますのでお好みの環境で。今回はCentOS7を使いました。
以下の作業はコントローラでの作業になります。
インストール
前提条件として、コントローラにPython2がインストールされていることを確認します。Python3への移行が進められているようですが、Python2系でも動きます。
また、yum
を使う場合には追加レポジトリ(epel-release
)もインストールしておきます。
1 2 3 4 | $ python --version Python 2.7.5 $ yum install epel-release $ yum install ansible |
インストールの確認。2.3.1が入りました。
1 2 3 4 5 | $ ansible --version ansible 2.3.1.0 config file = /etc/ansible/ansible.cfg configured module search path = Default w/o overrides python version = 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] |
sshの設定
暗号鍵を作成する関係上、管理対象と同じユーザ名でログインしておきます。
コントローラでパスフレーズなしのSSH鍵(~/.ssh/id_rsa
)を作成し、管理対象(target address
の部分がアドレス)に送り込んでおきます。
1 2 | ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa ssh-copy-id (user)@(target address) |
疎通テスト
コントローラ上で作業用にフォルダを作成し、移動しておきます。さらにinventory
という名前のサブフォルダを作り、その中に管理対象を列挙するためのhosts
というファイルを作成します。
1 2 3 4 | $ mkdir work $ cd work $ mkdir inventory $ vim inventory/hosts |
hosts
ファイルには[targets]
タグで対象のアドレスを書いておきます。どんなタグでもいいのですが、後ほど作成するPlaybookで使うので、使いやすいものを指定して下さい。下記は例です。
1 2 | [targets] 192.168.100.10 |
さて、ansible
コマンドを使って疎通を確認してみましょう。下記のようにSUCCESS
が返ってくればまずは大丈夫です。
1 2 3 4 5 | $ ansible all -i inventory/hosts -m ping 192.168.100.10 | SUCCESS => { "changed": false, "ping": "pong" } |
コマンドの意味は、インベントリファイルinventory/hosts
に記載されたホストのうち、全て(all
)に対してping
モジュールを実行する、というものです。
対象のマシンにPythonがセットアップされていない場合はModule failure(モジュール不足)になります。
1 2 3 4 5 6 7 8 | 192.168.100.10 | FAILED! => { "changed": false, "failed": true, "module_stderr": "Shared connection to 192.168.... closed.\r\n", "module_stdout": "/bin/sh: 1: /usr/bin/python: not found\r\n", "msg": "MODULE FAILURE", "rc": 0 } |
また、SSH公開鍵を送り込めていない場合にはPermission denied(権限不足)になります。ssh-copy-id
をやり直してみましょう。
Playbookの作成例
コントローラで作成したディレクトリ構成はこんな感じ。
先に作成したinventory/hosts
ファイルとは別に、group_vars
ディレクトリを作成してymlファイルを入れておきます。ファイル名はhosts
ファイルに書いた[targets]
タグと同じ名前にしています。Playbookの本体はmain.yml
です。
1 2 3 4 5 6 | (current directory) ├─main.yml ├─group_vars │ └─targets.yml └─inventory └─hosts |
main.yml
ファイルに実行したいタスクを書いていきます。
基本的にはAnsibleが備えているモジュール(ここではget_url
やapt
、copy
)でやりたいタスクが実現できますし、最期の手段としてshell
モジュールなどで直接コマンドをたたくこともできます。
モジュールの種類や使い方については長くなるのでここでは省略しますが、何となくわかると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | - hosts: targets tasks: - name: download package file get_url: url=http://{{ repo_server }}/package.tar.gz dest=/tmp - name: install packages become: yes apt: name={{ item }} state=present update_cache=yes with_items: - vim - apache2 - name: send local file to remote copy: src=/tmp/localfile dest=/tmp/remotefile |
targets.yml
ファイルには、main.yml
ファイル内で参照する変数を書いておきます。
これで特定の設定を外出しにできるわけですね。
またgroup_vars
ディレクトリの名前は前提条件なので、別の名前ではAnsibleが変数を認識してくれません。
1 | repo_server: repo.server.net |
Playbookを実行するには次のコマンドを実行します。
1 | ansible-playbook -i inverntory/hosts main.yml --ask-become-pass |
--ask-become-pass
は、管理対象のマシン上でsudo
を実行する際の昇格パスワードを、上記のコマンドを実行した直後に入力させるオプションです。
上記のPlaybookの例にあるように、例えばパッケージのインストールには普通root権限が必要になりますので、そのようなタスクは昇格(become: yes
)して実行させます。
sudo
のパスワードは別途ファイルに書いておいたりすることもできます(平文ではなく暗号化するやり方もあり)が、この実行時に入力させるやり方が取り扱い易いと思います。