2018-01-30 :-)
_ [ansible]ansible に入門した
2周遅れくらいですが。
ansible とは
- 構成管理ツールである
- サーバーを構築したり
- OSセットアップしたりユーザー追加したり
- といった各種操作ができる
- 冪等性(べきとうせい。読めなかった)がある
- 行った操作は必ず同じ結果となることが保証されている んだそうだ
- 例えば既にユーザーを追加してあるのに再度ユーザーを追加しようとしてもエラーとはならず ansible がよきに計らってくれる
- (useradd は成功すると 0 を返す。失敗すると非0 を返す)
- 主な構成
- インベントリ: 接続先ホストの情報を書く
- モジュール: 被管理ホストでおこなう各種操作(ユーザー追加とか)
- プレイブック: 被管理ホストでおこなう操作を手順として書いたファイル
ファイルとしては、インベントリとプレイブックを作成する。モジュールはプレイブック内に記述する。
とはいえ、最低限インベントリがあればいいらしい。
よちよち.ansible
「ansible 入門」でググると初っ端から Vagrant を使う前提だったりする記事がヒットして混乱したんだけど ansible についても仮想環境についても右も左も分からないような拙者にとっては Ansible ABC が最高のコンテンツでした。
ようするに「管理ホストが被管理ホストへ ssh 接続してイイ感じに操作する」というものらしい。基本的には以下のように操る。別に Vagrant 必須じゃなかった。物理ホストでもよかった。ただイイ感じ(とは?)にホストを使い捨てるなど仮想環境の利点を活かすときになったらまたそのときに考える。
「Ansible ABC」で腑に落ちたのは、最初に以下のように localhost 内で完結させる説明を書いていたこと。この説明で理解できた。
インストール
Installing the Control Machine に各種 OS やらパッケージでのインストール手順が書いてある。パッケージが無い OS (NetBSDのことだ) はがんばろう。オレはがんばらない。手元にたまたま Debian の Virtualbox があったので試してみました。インストール手順は上記ページ参照。
localhost で通信してみる
インストールしたら localhost どうしで通信してみる。いいですねー
% ansible localhost -m shell -a "uname -rsm" localhost | SUCCESS | rc=0 >> Linux 4.9.0-4-amd64 x86_64
この構成です。
被管理ホストに通信してみる
localhost 以外(後述)の被管理ホストについては FQDN をインベントリに書いておく必要がある。インベントリのファイルはデフォルトでは /etc/ansible/hosts のファイルである。任意のファイルを使いたい場合は -i オプションを指定し、ansible -i ファイル名 として実行すればよい。
[]
で括っておくとグループとして扱われる。グループを何に使うのかまだ分かってない。
たとえばこう。
[localnet] 192.168.1.30 ansible_user=USER ansible_ssh_pass=PASSWORD ansible_python_interpreter=/usr/pkg/bin/python2.7 192.168.1.35 ansible_user=USER ansible_ssh_pass=PASSWORD ansible_python_interpreter=/usr/local/bin/python2.7
こんな構成。
ssh の鍵を作成し、公開鍵を被管理ホストへコピーしておけば ansible_user や ansible_ssh_pass は不要(だと思う)けど、いまはなるべく単純な構成にしておきたいのでまだやらない。
ansible は、被管理ホストへ ssh 接続して、被管理ホスト上で python を実行する。python の PATH は /usr/bin/python にあることが前提となっている( How do I handle python pathing not having a Python 2.X in /usr/bin/python on a remote machine? )ので、それ以外の PATH に python がある場合は ansible_python_interpreter で指定する。
192.168.1.30 に接続してみる。いいですねー
% ansible 192.168.1.30 -m shell -a "uname -rsm" 192.168.1.30 | SUCCESS | rc=0 >> NetBSD 8.99.9 amd64
192.168.1.35 に接続してみる。いいですねー
% ansible 192.168.1.35 -m shell -a "uname -rsm" 192.168.1.35 | SUCCESS | rc=0 >> FreeBSD 11.1-RELEASE amd64
(おまけ)ssh 認証エラー
ssh 鍵も管理せず、インベントリに ansible_user も書いておかない場合、以下のようにエラーとなる。たんにログインできないだけなんだが、メッセージには「Permission denied」等と書いてあって何のヒントにもならない。
% ansible 192.168.1.30 -m shell -a "uname -rsm" The authenticity of host '192.168.1.30 (192.168.1.30)' can't be established. ECDSA key fingerprint is SHA256:Iz2lkIpdFNCtClHPBMzp8VKJWmCANlf3M7vqxmPqA9s. Are you sure you want to continue connecting (yes/no)? yes 192.168.1.30 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.1.30' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,password,keyboard-interactive).\r\n", "unreachable": true }
(おまけ)デバッグ出力に改行コードが印字されるんですが
ansible コマンドのデバッグ出力するときは -vvvv をつければいいんだけど \r\n も印字されてしまい人間には読めない。なんだこれ。
% ansible 192.168.1.30 -m ping -vvvv ansible 2.4.2.0 config file = /etc/ansible/ansible.cfg configured module search path = [u'/home/rin/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/dist-packages/ansible executable location = /usr/bin/ansible python version = 2.7.13 (default, Nov 24 2017, 17:33:09) [GCC 6.3.0 20170516] Using /etc/ansible/ansible.cfg as config file setting up inventory plugins Parsed /etc/ansible/hosts inventory source with ini plugin Loading callback plugin minimal of type stdout, v2.0 from /usr/lib/python2.7/dist-packages/ansible/plugins/callback/__init__.pyc META: ran handlers Using module file /usr/lib/python2.7/dist-packages/ansible/modules/system/ping.py <192.168.1.30> ESTABLISH SSH CONNECTION FOR USER: rin <192.168.1.30> SSH: EXEC sshpass -d12 ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o User=rin -o ConnectTimeout=10 -o ControlPath=/home/rin/.ansible/cp/4095337cf6 192.168.1.30 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"'' <192.168.1.30> (0, '/home/rin\n', 'OpenSSH_7.4p1 Debian-10+deb9u2, OpenSSL 1.0.2l 25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/rin/.ansible/cp/4095337cf6" does not exist\r\ndebug2: resolving "192.168.1.30" port 22\r\ndebug2: ssh_connect_direct: needpriv 0\r\ndebug1: Connecting to 192.168.1.30 [192.168.1.30] port 22.\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: fd 3 clearing O_NONBLOCK\r\ndebug1: Connection established.\r\ndebug3: timeout: 10000 ms remain after connect\r\ndebug1: key_load_public: No such file or directory\r\ndebug1: identity file /home/rin/.ssh/id_rsa type -1\r\ndebug1: key_load_public: No such file or directory\r\ndebug1: identity file /home/rin/.ssh/id_rsa-cert type -1\r\ndebug1: key_load_public: No such fi : :
(おまけ)localhost というホスト名
被管理ホストはインベントリに書く必要があるんだけど「localhost」だけは特別である。インベントリに書かなくてよい。ハードコーディングされてるため。
https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/data.py#L156
: # if host is not in hosts dict if matching_host is None and hostname in C.LOCALHOST: # might need to create implicit localhost matching_host = self._create_implicit_localhost(hostname) :
なお LOCALHOST は constants.py に定義されてる。ぽい
https://github.com/ansible/ansible/blob/devel/lib/ansible/constants.py#L96
LOCALHOST = ('127.0.0.1', 'localhost', '::1')