📚

【過去Blogからの移行記事】Ansibleはじめの一歩メモ

2022/09/18に公開

Ansibleの使い方についてメモを殴り書き。

事前FAQ

  • この文書が前提としてる読み手のスキルってなに?
    • sshが使えて、sshによって自分がどのホストOSを操作しているのか、どんな権限で操作してるのかを自分で把握できていること。
  • ansibleを練習するのに、AWS使うとかどこぞのVPS契約するとかしないとだめ?
    • 操作しているPC上でVirtualBoxやVMwareによる仮想環境が使えれば、それだけで十分ansibleの練習はできる。
  • VirtualBoxに立てたOSをansibleで色々やるのに、vagrantって必要なん?
    • ansibleのことググったら「vagrantとセットじゃないと使えない?」みたいな印象を持つ人もいるかもしれないが、そんなことはない。

使い方超概要

  • playbook と呼ばれる yaml形式のファイルに対してやりたいことを記述していく。
  • ansibleを使って環境構築をする場合に関係するホストは次の通り。
    • 自分が操作するPC
      • ansible制御サーバへログインするために使う。
      • 操作PCにansibleパッケージ一式をインストールして使うことも可能なので、その場合は次項の「ansible制御サーバ」はすなわち「自分が操作するPC」ということになる。
    • ansible制御サーバ (と呼ぶことにする)
      • ansibleパッケージ一式をインストールし、playbookを配置する。
      • ここでキックされたansibleプロセスがsshで各targetサーバへログインしplaybookに書いてあることを順次実行していく。
    • targetサーバ (と呼ぶことにする)
      • アプリケーション環境を構築する対象サーバ。
      • 1つのplaybookでい複数のtargetサーバを取り扱い可能。
      • 対象サーバのグルーピングもできる。
  • ホスト構成についての補足情報
    • targetサーバ側に個々のエージェントプログラムを入れるなどといった特別な下準備は必要ない。各々のtargetサーバへsshでログインする権限さえあれば、ansibleを使って制御できる。
    • targetサーバについて
      • 構築したいアプリケーションは当該OSにインストールできるものであればなんでも良い。
      • 例えばWebサーバであったり、DBサーバであったり、SMTPサーバであったり、Proxyサーバであったり。やる気があればsource持ってきてコンパイルからやってもいいはず。
    • ansible制御サーバについて
      • ansible制御サーバ自身をtargetとすることも出来る。
      • ansible制御サーバ側で持っているファイル等をtargetサーバの指定pathへ配置することができる。
      • なので、svn や git の checkout や sourceのビルド等といった処理で予め ansible制御サーバ側で出来ることがあれば済ませて、出来上がったものを各種targetへ送り込むのもいいはず。

とりあえず手軽に疎通だけ実行してみる

1. ssh接続できる「targetサーバ」「ansible制御サーバ」を用意する

  • 仮にそれぞれのIPアドレスを次の通りと仮定する
    • 192.168.56.10 - targetサーバ
    • 192.168.56.5 - ansible制御サーバ
  • それぞれは、自分のPC上にVirtualBoxやVMware等を使ってセットアップしたOSでも構わないし、AWSやさくらクラウドや各種VPSサービスなどに立てたOSインスタンスでも構わない。
  • 相互の通信がインターネット越しになっても構わないし、ローカルネットワーク内でも構わない。sshで通信できればそれでいい。

2. ansible制御サーバにログイン

ansibleパッケージ一式をインストールする。

3. テキトウな作業ディレクトリを作りそこへ cd

次のような記述をしたファイルを作る。(※このファイルはインベントリファイルと呼ばれるもので、ansibleの制御対象サーバをここにリストしていくことになる)

  • ファイル名: (任意)
    • 仮に「test.hosts」とでもしておく。
  • ファイルの内容:
 [connect_test]
 localhost
 192.168.56.10

4. おもむろに次のコマンドを実行する。

$ ansible connect_test -i test.hosts -m ping -k

  • この時、「SSH password:」とパスワードの入力を求めるプロンプトが出るので、とりあえずansible制御サーバへのsshログインパスワードを入力する
  • その結果が例えば次のようだった場合いちおうansibleは走っており、localhost(制御サーバ自身)に対してansibleを介したログインはできているが、ansibleを介したtargetサーバへのログインが失敗していることになる。
192.168.56.10 | UNREACHABLE! => {
   "changed": false,
   "msg": "Authentication failure.",
   "unreachable": true
}
localhost | SUCCESS => {
   "changed": false,
   "failed": false,
   "ping": "pong"
}

ではどうする?

ひとまず、「ansible制御サーバ上でansibleコマンドを実行するユーザ」と同一のユーザー名&パスワードをtargetサーバ上に作成すれば上記のコマンドは成功するはず。

targetサーバのsshログインユーザをansibleコマンドのオプションで指定することもできる。ansible制御サーバとtargetサーバとでsshログインユーザが異なる場合は、ansibleコマンドのオプションに「-u (targetサーバへのログインユーザ)」を指定するとよい。(下記が例だが、今度は localhost の ping が UNREACHABLE! となることでしょう)

$ ansible connect_test -i hosts.test -m ping -u vagrant -k

オススメは

ansible制御サーバ上に「targetサーバへsshログインする用の秘密鍵」を持たせて公開鍵認証方式で各種targetサーバの操作をできるようにすること。

  • 既にtargetサーバに対して公開鍵認証方式によるsshログイン設定が済んでいる場合は、上述のansibleコマンドのオプション「-k」に変えて「–private-key="(秘密鍵のパス)"」を指定するとよい。(※必要に応じて上述の -u オプションも併せて使う)
  • 複数targetサーバのsshログイユーザが各々で異なる場合でも、インベントリファイルの書き方やssh_configの指定の仕方次第でイケる。(後述)

5. 色々乗り越えて

上記のコマンド結果から「UNREACHABLE!」が無くなって全てSUCCESSになったら、次のステップへ進む準備OK。

ここまでの操作で次のことが読み取れたら、この後のステップの内容に工夫を加えて色々試せるはず。

  • ansibleコマンドの「-i」オプションで、targetサーバをリストアップしたファイルを指定できる。(上記の場合は test.hosts)
  • test.hosts に書いた [connect_test]という記述がグループを示していて、ansibleコマンドの第一引数でグループの呼び分けができる。
  • targetを制御サーバ自身とすることもできるので、playbookの記述練習をするだけなら実はわざわざ別途targetサーバを用意しなくても出来る。

簡単なplaybookを書いて実行してみる

1. test.hosts に次の追記を行う。

追記する内容

[deploy_test]
192.168.56.10

2. 次のような記述をしたファイルをカレントディレクトリに作成する。

  • ファイル名: (任意)
    • 仮に「testbook.yml」とでもしておく。
  • ファイル内容:
- hosts: deploy_test
  become: no
  tasks:
    ###########################################################################
    # 環境情報の取り扱い例
    - name: "dump: 実行中サーバの inventory_hostname "
      debug:
        msg: "実行中のサーバ名: {{ inventory_hostname }}"
    - name: "dump 実行中サーバの hostvars "
      debug: var=hostvars[inventory_hostname][item]
      with_items: "{{ hostvars[inventory_hostname] }}"
      when: item == "ansible_version" or item == "ansible_env" or item == "ansible_user_id"
    ###########################################################################
    # fileモジュールの取り扱い例
    - name: "TASK [Gathering Facts] で取得できる情報を使って touchコマンドを実行"
      file:
        state: touch
        path: "{{ hostvars[inventory_hostname]['ansible_env']['PWD'] }}/hoge.txt"
    ###########################################################################
    # copyモジュールの取り扱い例
    - copy:
        content: |
          {% set line = [] %}
          {% for key in hostvars[inventory_hostname] %}
          {%   set _ = line.append(key) %}
          {% endfor %}
          {{ line|join(', ') }}
        dest: "{{ hostvars[inventory_hostname]['ansible_env']['PWD'] }}/{{ inventory_hostname }}_hasKey.csv"
        backup: yes
      register: copy_result
    - name: "dump TASK:copy による処理の結果を確認"
      debug: var=copy_result

3. 上記 playbook を次のようにして実行する。

コマンド
$ ansible-playbook -i test.hosts testbook.yml

上記 testbook.yml の実施によってtargetサーバに起こる変化は「fileモジュールの state: touch」というタスクと「copyモジュールによるコンテンツの配置」というタスクの2つのみなので、実行結果のサマリー表示(最後の部分)は次のようになるはず。

PLAY RECAP *************************************************************************************************************************************************************
192.168.56.10 : ok=6 changed=2 unreachable=0  failed=0

4. さらに続けて上述のコマンドを実行する。

実行結果のサマリー表示は少しだけ変わって次のようになるはず。

PLAY RECAP *************************************************************************************************************************************************************
192.168.56.10 : ok=6 changed=1 unreachable=0  failed=0
  • 2度目のコマンド実行によるこの結果については、次のような事情によるものである。
    • touchのタスクは、対象ファイルのタイムスタンプを変更しているので「changed」のカウントが増える。
    • copyのタスクは、対象ファイルの内容が1回目と2回目で変化しないので「chenged」のカウントが増えない。
  • このような性質を冪等性(べきとうせい)と言うらしい。
  • 1度目の実行と2度目の実行とで、targetサーバ上に生成されたファイルの更新時刻を ls -l 等で見比べてみるとよい。

何はともあれ ssh

ansible で何をどう使うにしても、 ssh設定を思い通りに扱えるかどうかが最初の鍵になる。
ansibleではssh接続設定について様々柔軟な記述が出来るように設計されているようなので好みのやり方を見つけたらいいと思う。
現時点で筆者はひとまず次のようなやり方を採用している。
※以下の記述はansible制御サーバ上で行う作業で、上記までで示したplaybookの実行例に対応した内容にしてある。

  1. ansible制御サーバからtargetサーバへssh接続するのに使用する秘密鍵を ~/.ssh/ansCtlRsa_key に置く。
  2. 作業ディレクトリ直下に ansible.cfg を作成し、次のように記述する。
[defaults]
ask_pass = False
hash_behaviour = merge

[ssh_connection]
ssh_args = -o ForwardAgent=yes -F /home/ansiblectl/<プロジェクトファイル配置パス>/ssh_config/config
  1. 作業ディレクトリ直下にディレクトリ ssh_config/ を作成する。
  2. ディレクトリ ssh_config/ 直下にファイル config を作成し、次のように記述する。
TCPKeepAlive yes

Include ~/<プロジェクトファイル配置パス>/ssh_config/conf.d*/*

Host localhost
    HostName  localhost
    User      <制御サーバのログインユーザー名>
    IdentityFile ~/.ssh/ansCtlRsa_key
    StrictHostKeyChecking no*
  1. ディレクトリ ssh_config/ 直下にディレクトリ conf.d/ を作成する。
  2. ディレクトリ ssh_config/conf.d/ 直下に任意の名前でファイルを作成し、次のように記述する。
Host 192.168.56.10
  User <targetサーバのログインユーザー名>
  HostName 192.168.56.10
  IdentityFile ~/.ssh/ansCtlRsa_key
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  • targetサーバを増やす時は ssh_config/conf.d/配下に置いたファイルへ適宜ssh接続用の設定を追記していけばよい。
  • グルーピングを見分けたいなら、ssh_config/conf.d/配下でファイルをグループごとに分けると良い。

※「<プロジェクトファイル配置パス>」のように「<〜〜〜>」で記述した箇所は適宜書き換える必要あり。

Discussion