既存のLinuxサーバーでAnsibleを使えるようにするための準備


概要

本記事はAnsible Advent Calendar 2019の16日目の記事になります。

Ansibleを使うことを意識せずにセットアップされたLinuxマシンに対して、何をすればAnsible Readyになるかについて説明します。

Ansibleを使ったサーバーやネットワーク構築の情報は豊富だし、「この環境を使えばすぐにAnsible動かせます」という環境も割とあると思うのですが、「今あるこの手元(特にオンプレ)の環境でAnsibleを使い始めるにはどうすればよいか」についての情報が少ないと(私が個人的に)感じたので、その内容についてまとめました。

ですので本記事は「Ansibleを勉強し始めるにはどうすればよいか」ではなく「Ansibleを使ったサーバー構築を始めるときは何を準備すればいいか」という内容になっています。


構成

CentOSを前提としています。

ホスト名 IPアドレス 役割
control-node 192.168.0.140 コントロールノード
target-node01 192.168.0.141 ターゲットノード
target-node02 192.168.0.142 ターゲットノード

Ansibleのコマンド(ansibleansible-playbookなど)を実行するホストをコントロールノード、コントロールノードのAnsibleによって処理される対象のホストをターゲットノードと言います。

  • コントロールノードに必要なもの

    • Ansible
    • Python
  • ターゲットノードに必要なもの

    • Python
    • SSHサーバー
    • sudoによるrootへの昇格

また本記事では、コントロールノード・ターゲットノードそれぞれ、作業用のユーザ名(非root)と認証用パスワードは全て同一である前提です。

インストールと設定

Ansible本体

CentOS7の場合、標準のYumリポジトリだとAnsible 2.4がインストールされますが、ちょっと古すぎます(2019.12時点で最新は2.9)

$ yum info ansible
:
:
利用可能なパッケージ
名前                : ansible
アーキテクチャー    : noarch
バージョン          : 2.4.2.0
リリース            : 2.el7
容量                : 7.6 M
リポジトリー        : extras/7/x86_64
要約                : SSH-based configuration management, deployment, and task

:
:

ですので、CentOS7の場合はEPELリポジトリにあるパッケージでインストールすると楽で良いです。

$ sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
$ sudo yum install ansible

これで最新安定板がインストールできるはずです。
バージョンを確認するにはansible --versionを実行します。

[zaki@control-node ~]$ ansible --version
ansible 2.9.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/zaki/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
[zaki@control-node ~]$

RHELやDebian/Ubuntuなどその他のプラットフォームの場合については、AnsibleのInstallation Guideに載っています。
また、pipを使ってもインストールできます。

インストールができたら簡単な動作確認として、対象でAnsibleが実行できるかを確認するpingモジュールをlocalhost宛に実行してみます。

$ ansible localhost -m ping
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

SUCCESSと表示されればOKです。
※ ICMPを使ったLinuxのpingコマンドと異なり、疎通というよりはAnsibleが実行可能かどうかを確認します。また、localhostの場合はSSH接続を要しません

Python

yumなどのパッケージマネージャでインストールできます…が、最近のよく使われるディストリビューションであればPythonは初めから入っているはずです。
また、Ansibleのインストール要件にPythonは必要とされるので、Ansibleがインストール済みであればPythonもインストール済みになっているはずです。

$ python --version

でPythonのバージョンが確認できます。
※ Python version2 は、2020年1月1日でサポートが終了します。

SSHアクセス

SSHサーバーもメジャーなディストリビューションであれば、OSインストール直後の状態で起動しているはずです。
Ansibleを使えるようにするには、コントロールノードからターゲットノードへSSHアクセスできる必要があります。(ターゲットノードにSSHサーバーが動いていること)

認証についてはパスワード認証・公開鍵認証どちらでも実行可能ですが、パスワード認証やパスフレーズ付き公開鍵認証の場合は実行のたびにパスワード/パスフレーズの入力が必要になるので、パスフレーズ無しの秘密鍵を設定すると構築の際は手間が無くて便利です。

$ ssh-keygen -t rsa -f $HOME/.ssh/id_rsa -N ""

これで~/.ssh/id_rsaにパスフレーズ無しの公開鍵が作成されます。
キーペアを作成したら、各ターゲットノードへ公開鍵を配布します。

$ ssh-copy-id [email protected] # ターゲットノード1
$ ssh-copy-id [email protected] # ターゲットノード2

これで認証無しで各ターゲットノードへSSHアクセスできるはずです。

[zaki@control-node ~]$ ssh 192.168.0.141
[zaki@target-node01 ~]$
[zaki@control-node ~]$ ssh 192.168.0.142
[zaki@target-node02 ~]$

ここまで設定できれば、2台のターゲットノードに対してpingモジュールでアクセスできるはずです。

ターゲットノードを定義した以下の内容のインベントリファイルを作成。

inventory.ini
[nodes]
192.168.0.141
192.168.0.142

このインベントリファイルを-iで指定しansibleを実行。この時、ローカルを対象に実行した際にlocalhostと指定したところを、インベントリの最初の行に書いた[nodes]というグループ名を指定します。

$ ansible nodes -i inventory.ini -m ping
192.168.0.142 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.0.141 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

これでコントロールノードからターゲットノードに対してAnsibleが実行できることが確認できました。

パスフレーズ無しの公開鍵設定を行わない場合は、以下のようにすることでAnsibleは実行できます。

認証方式 Ansibleの操作
パスワード認証 -kを付与することでパスワード認証のプロンプトを表示する
パスフレーズ付き公開鍵認証 自動でパスフレーズの入力プロンプトが表示される

sudo設定

さらににもう1点。
Ansibleを使ってサーバー構築する場合、(yumによるパッケージインストールなど)大抵の作業はroot権限が必要です。
ターゲットノードにおいてsudoがそもそも使用不可能である場合は、対象ユーザに対して(wheelグループに追加するなどの)sudoersの設定を行ってください。

また、sudo実行時にパスワードが不要な設定にしておけば、Ansible実行時にもパスワード入力が必要なくなるため、手間が無くて便利です。

/etc/sudoers
%wheel  ALL=(ALL)       NOPASSWD: ALL

viじゃなくてvisudoを使いましょう

これは全ターゲットノードで実施します。(コントロールノードに対しても処理を行う場合はコントロールノードにも設定)
設定できたら-bオプションを追加(デフォルトrootとして動作)してAnsibleを実行します。

$ ansible nodes -i inventory.ini -b -m ping
192.168.0.142 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.0.141 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

エラーにならなければ、ターゲットノード上ではroot権限で処理が実行されます。
といってもpingモジュールだとわかりづらいので、もし全ノードでyum updateしてもよいのであれば、以下のコマンドを実行してみましょう。全てのパッケージが更新されます。

$ ansible nodes -i inventory.ini -b -m yum -a "name=* state=latest"

パスワード無しのsudo設定を行わない場合は、実行時に-Kを付与することでroot権限でAnsibleは実行できます。
パスワード認証用の-kと併用可能です。

初期構築のAnsibleによる自動化

ここまで手作業でAnsibleのインストールとSSH/sudoの設定について説明しましたが、せっかくAnsibleをインストールするのだから、じゃがいもに怒られないように自動化できるところは自動化しちゃいましょう。
特に全てのターゲットノードに対して、SSH公開鍵を配布とsudo設定は手作業でポチポチやるものじゃないです。

前提

  • コントロールノードでのAnsibleのインストール(手動)
  • ターゲットノードで作業ユーザがwheelグループに所属しておりsudoでroot昇格できること(ただし要パスワード)
  • 作業ユーザ名とパスワードは全ノード共通
  • パスワード認証でSSH接続でき、Pythonがインストール済みである

それ以外の要件であるパスフレーズ無しSSH公開鍵設定とパスワード無しsudo設定をAnsibleでやります。

ansible.cfg

ここまで説明してないので、初回SSH実行時の手間を減らすおまじないだと思ってください。

ansible.cfg
[defaults]
host_key_checking = False

[ssh_connection]
ssh_args -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null

インベントリ

inventory.ini
[nodes]
192.168.0.141
192.168.0.142

※ Ansibleを実行するコントロールノード(192.168.0.140)も構築対象にする場合、それも追加してください。

プレイブック

playbook.yml
---
- hosts: localhost
  tasks:
  - name: create directory for ssh keypair
    file:
      path: "{{ lookup('env','HOME') }}/.ssh/"
      state: directory
      mode: 0700

  - name: create ssh privatekey
    openssh_keypair:
      path: "{{ lookup('env','HOME') }}/.ssh/id_rsa"

- hosts: all
  tasks:
  - name: publickey copy to target-nodes
    authorized_key:
      user: "{{ ansible_env.USER }}"
      key: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/id_rsa.pub' )}}"

- hosts: all
  become: True
  tasks:
  - name: configure non-password sudo
    copy:
      dest: /etc/sudoers.d/nopass
      mode: 0600
      content: |
        %wheel ALL=(ALL) NOPASSWD: ALL

この3つのファイルを同一ディレクトリに置いて、以下のコマンドを実行。

$ ansible-playbook -i inventory.ini playbook.yml -kK

-kでパスワード認証、-Kでsudoの昇格用パスワード認証がまだ未設定(このAnsibleで設定する)なので、忘れないように。

lookupプラグインなどを使って以下のものを外部参照しています。

変数 内容
{{ lookup('env','HOME') }} 実行ユーザのホームディレクトリのパス
{{ ansible_env.USER }} ユーザ名
{{ lookup('file', lookup('env', 'HOME') + '/.ssh/id_rsa.pub' )}} ~/.ssh/id_rsa.pubのファイルの内容

以上を踏まえて、このプレイブックの各タスクでは以下の処理が行われます。
(既にパスワード無しでsudoできるなど、設定済みのものがあれば削ってください)

  • create directory for ssh keypair (ローカル実行)
    • ~/.sshディレクトリを作成
  • create ssh privatekey (ローカル実行)
    • SSH秘密鍵キーペアを~/.ssh/id_rsaに作成
  • publickey copy to target-nodes (リモートアクセス)
    • 全ターゲットノードのauthorized_keyに~/.ssh/id_rsa.pubをコピー
  • configure non-password sudo (要root権限)
    • 全ターゲットノードのsudoのNOPASSWD設定

ね、簡単でしょう?

(補足)openssh_keypairモジュールについて

ドキュメントにも記載がありますが、このモジュールはversion 2.8から使用可能な新しいモジュールです。それ以前のAnsibleバージョンだと使えません。

それ以前のバージョンを使っている場合は、モジュールでは鍵作成ができないので、shellモジュールで作成するタスクに置き換えます。

playbook.yml
  - name: check exists ssh privatekey
    shell: test -f $HOME/.ssh/id_rsa
    register: exist_key
    ignore_errors: True
    check_mode: False
    changed_when: False
  - name: create ssh privatekey
    shell: ssh-keygen -t rsa -f $HOME/.ssh/id_rsa -N ""
    when: exist_key.rc != 0

ちょっと長いけど「~/.ssh/id_rsaがあるかどうかチェック」して「なければssh-keygen」という2段階になっています。
初回実行時は~/.ssh/id_rsaが無いためエラーの表示が出力されますが、ignore_errors: Trueによって無視して処理が継続されます。

ssh-keygenって強制上書きモードないんかな??

まとめ

ということで、Ansibleを使ったサーバー構築をするための準備について、具体的な作業手順(マニュアル&Ansibleによる自動構築)について説明しました。

台数が2,3台であれば手作業でもミスなく頑張れると思いますが、10台20台になってくると絶対に自動化した方が良いです。

ネットワークの自動化やクラウド環境だとまたちょっと変わってくると思いますが、オンプレでのAnsible使ったサーバー構築の自動化はこれで始めることができると思います。

余談だけど、AnsibleのインストールはAnsibleで自動化できないのだろうか。。


参考