無能が苦悩したメモ

無能な著者が学んだことを忘れないための覚え書きです...

Ansible:assertモジュールについて

動作確認した環境

assertモジュール

assertは与えられた条件を評価してメッセージを出力するモジュールです。テストやデバッグで、設定が正しく行われているか確認したい場合に役立ちます。

パラメータ 説明
that 条件式。このパラメータの指定は必須。
success_msg 条件が成立した時に表示するメッセージ
fail_msg 条件が不成立の時に表示するメッセージ
quiet ログ表示を詳細に表示するかどうかを表す。 noで表示する、yesで表示しない。デフォルト値はnoとなっている。


実行例1

ホスト名が想定した値かどうかを判定するassert文の例です。必須パラメータであるthatのみ指定しています。

thatで指定した条件が成立しない場合、taskはfatalとなりプレイブックの実行はそこで終了します。引き続き次のタスクを実行させたければ「ignore_errors: yes」をつけておかないといけません。

---
- hosts: all
  tasks:
    - debug:
        msg: "{{ ansible_facts.hostname }}"

    - name: assert [ failed pattern ]
      assert:
        that: ansible_facts.hostname == "dummy_hostname"
      ignore_errors: yes

    - name: assert [ success pattern]
      assert:
        that: ansible_facts.hostname == "CentOS7_6"
      ignore_errors: yes

# ansible-playbook -i hosts assert_test1.yml

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

TASK [Gathering Facts] *********************************************************
ok: [CentOS7_6]

TASK [debug] *******************************************************************
ok: [CentOS7_6] => {
    "msg": "CentOS7_6"
}

TASK [assert [ failed pattern ]] ***********************************************
fatal: [CentOS7_6]: FAILED! => {
    "assertion": "ansible_facts.hostname == \"dummy_hostname\"",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}
...ignoring

TASK [assert [ success pattern]] ***********************************************
ok: [CentOS7_6] => {
    "changed": false,
    "msg": "All assertions passed"
}

PLAY RECAP *********************************************************************
CentOS7_6                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1


実行例2

実行例1と同じくホスト名が想定した値かどうかを判定するassert文の例です。パラメータを付けたしました。

success_msg、fail_msgに指定した文字列が、条件の成立時、不成立時にそれぞれ出力されています。

---
- hosts: all
  tasks:
    - debug:
        msg: "{{ ansible_facts.hostname }}"

    - name: assert [ failed pattern ]
      assert:
        that: ansible_facts.hostname == "dummy_hostname"
        success_msg: "ホスト名はdummy_hostnameです"
        fail_msg: "ホスト名はdummy_hostnameではありません"
      ignore_errors: yes

    - name: assert [ success pattern]
      assert:
        that: ansible_facts.hostname == "CentOS7_6"
        success_msg: "ホスト名はCentOS7_6です"
        fail_msg: "ホスト名はCentOS7_6ではありません"
      ignore_errors: yes


# ansible-playbook -i hosts assert_test2.yml

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

TASK [Gathering Facts] *********************************************************
Enter passphrase for key '/home/tkd/.ssh/id_rsa':
ok: [CentOS7_6]

TASK [debug] *******************************************************************
ok: [CentOS7_6] => {
    "msg": "CentOS7_6"
}

TASK [assert [ failed pattern ]] ***********************************************
fatal: [CentOS7_6]: FAILED! => {
    "assertion": "ansible_facts.hostname == \"dummy_hostname\"",
    "changed": false,
    "evaluated_to": false,
    "msg": "ホスト名はdummy_hostnameではありません"
}
...ignoring

TASK [assert [ success pattern]] ***********************************************
ok: [CentOS7_6] => {
    "changed": false,
    "msg": "ホスト名はCentOS7_6です"
}

PLAY RECAP *********************************************************************
CentOS7_6                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1


実行例3

thatに複数の条件を指定するパターンです。thatには、配列で複数の条件を記載できます。その場合、各条件式はandでつながります。

test_assert3.ymlでは、/var/log/cronが存在し、かつ、/var/log/cronのサイズが0でない場合に、条件がtrueとなります。

---
- hosts: all
  tasks:
    - stat:
        path: /var/log/cron
      register: cron_log

    - debug:
        msg: "{{ cron_log }}"

    - name: assert message
      assert:
        that:
          - cron_log.stat.exists is true
          - cron_log.stat.size != 0


ansible-playbook -i hosts assert_test3.yml

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

TASK [Gathering Facts] *********************************************************
ok: [CentOS7_6]

TASK [stat] ********************************************************************
ok: [CentOS7_6]

TASK [debug] *******************************************************************
ok: [CentOS7_6] => {
    "msg": {
        "changed": false,
        "failed": false,
        "stat": {
            "atime": 1612464614.0189674,
            "attr_flags": "",
            "attributes": [],
            "block_size": 4096,
            "blocks": 8,
            "charset": "unknown",
            "ctime": 1612464601.4457855,
            "dev": 64770,
            "device_type": 0,
            "executable": false,
            "exists": true,
            "gid": 0,
            "gr_name": "root",
            "inode": 1123797,
            "isblk": false,
            "ischr": false,
            "isdir": false,
            "isfifo": false,
            "isgid": false,
            "islnk": false,
            "isreg": true,
            "issock": false,
            "isuid": false,
            "mimetype": "unknown",
            "mode": "0600",
            "mtime": 1612464601.4457855,
            "nlink": 1,
            "path": "/var/log/cron",
            "pw_name": "root",
            "readable": false,
            "rgrp": false,
            "roth": false,
            "rusr": true,
            "size": 492,
            "uid": 0,
            "version": null,
            "wgrp": false,
            "woth": false,
            "writeable": false,
            "wusr": true,
            "xgrp": false,
            "xoth": false,
            "xusr": false
        }
    }
}

TASK [assert message] **********************************************************
ok: [CentOS7_6] => {
    "changed": false,
    "msg": "All assertions passed"
}

PLAY RECAP *********************************************************************
CentOS7_6                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


ちなみに、thatはwhen文で評価可能な式であれば同じく評価することができます。なので、複数の条件式を記載する場合、andやorで条件式をつらねることもできます。test_assert3.ymlのthatで指定した条件式を以下のように書くこともできます。

    - name: assert message
      assert:
        that: >
          cron_log.stat.exists is true
          and cron_log.stat.size != 0

Ansible:インベントリ変数について

インベントリ変数は以下の2つの方法で定義することができます。

  • インベントリファイルに直接記述する
  • host_vars、group_varsを作成し、その下のファイルに記述する

インベントリファイルに変数を記述する場合

ホスト変数

ホスト変数は、インベントリファイル内のホスト名、またはIPアドレスの後に「変数名=値」の書式でスペース区切りで記載します。1つのホストに対して複数のホスト変数を設定すると1行が長くなりますが、途中で改行することはできません…


以下の例では、serverA_01にはfooというホスト変数が定義され、serverA_02にはfooとbarの2つのホスト変数が定義されています。
※ちなみに、ansible_hostはansibleがリモートマシンへの接続のために用いる特殊な変数です。

[serverA]
serverA_01 ansible_host=192.168.1.1 foo="ホスト変数foo"
serverA_02 ansible_host=192.168.1.2 foo="ホスト変数foo" bar="ホスト変数bar"

グループ変数

グループ変数を定義する場合、新たに「グループ名:vars」の名前でセクションを作り、その下に記載します。


以下の例では、serverAというグループにfooとbarの2つの変数を定義しています。

[serverA]
serverA_01 ansible_host=192.168.1.1
serverA_02 ansible_host=192.168.1.2

[serverA:vars]
foo="グループ変数foo"
bar="グループ変数bar"


親子関係のグループの場合

グループが親子関係でも同じように「グループ名:vars」で変数を定義できます。親で定義した変数は子にも受け継がれます。親、子で変数が重複した場合、子の変数が適用されます。

[parent]

[parent:vars]
foo="parentグループ変数foo"
bar="parentグループ変数bar"

[parent:children]
serverA

[serverA]
serverA_01 ansible_host=192.168.1.1

[serverA:vars]
foo="serverAグループ変数foo"

---
- hosts: all
  tasks:
    - debug:
        msg: "{{ foo }}"
    - debug:
        msg: "{{ bar }}"

$ ansible-playbook -i inventory -l serverA_01 play_serverA.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [serverA_01]

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "serverAグループ変数foo"
}

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "parentグループ変数bar"
}

PLAY RECAP **************************************************************************************************************************************************
serverA_01                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


ホスト変数とグループ変数で変数が重複する場合

ホスト変数とグループ変数に重複して変数を定義した場合、ホスト変数の値が適用されます。

[serverA]
serverA_01 ansible_host=192.168.1.1 foo="ホスト変数foo" bar="ホスト変数bar"

[serverA:vars]
foo="グループ変数foo"
bar="グループ変数bar"

---
- hosts: all
  tasks:
    - debug:
        msg: "{{ foo }}"
    - debug:
        msg: "{{ bar }}"

$ ansible-playbook -i inventory -l serverA_01 play_serverA.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [serverA_01]

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "ホスト変数foo"
}

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "ホスト変数bar"
}

PLAY RECAP **************************************************************************************************************************************************
serverA_01                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


全ホストに共通する変数を定義する

インベントリファイルに定義した全てのホストは「all」というグループに所属します。「all:vars」に変数を定義することで全ホストに共通した値を定義することができます。

なお、「グループ名:vars」と「all:vars」で重複して変数を定義した場合、「グループ名:vars」で定義した値が適用されます。

[serverA]
serverA_01 ansible_host=192.168.1.1

[serverA:vars]
foo="serverAグループ変数foo"

[all:vars]
foo="allグループ変数foo"
bar="allグループ変数bar"

---
- hosts: all
  tasks:
    - debug:
        msg: "{{ foo }}"
    - debug:
        msg: "{{ bar }}"

$ ansible-playbook -i inventory -l serverA_01 play_serverA.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [serverA_01]

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "serverAグループ変数foo"
}

TASK [debug] ************************************************************************************************************************************************
ok: [serverA_01] => {
    "msg": "allグループ変数bar"
}

PLAY RECAP **************************************************************************************************************************************************
serverA_01                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


host_vars、group_vars下のファイルに変数を記述する場合

インベントリ変数はインベントリファイルに記載する以外にも、host_varsやgroup_varsのようなディレクトリを用意し、その下のファイルに記載することもできます。


イメージとしては以下のようなディレクトリ構成です。host_vars、group_varsはインベントリファイルと同じ階層に作成しなければなりません。

|-- group_vars
|   |-- all.yml
|   `-- serverA.yml
|-- host_vars
|   |-- serverA_01.yml
|   `-- serverA_02.yml
|-- inventory
`-- play_serverA.yml

ホスト変数

host_varsの下にホスト名と同じ名前でyamlファイルを作成し、その中にホスト変数を定義します。

|-- inventory
`-- host_vars
    |-- serverA_01.yml
    `-- serverA_02.yml

foo: "serverA_01のホスト変数foo"
bar: "serverA_01のホスト変数bar"

foo: "serverA_02のホスト変数foo"
bar: "serverA_02のホスト変数bar"


ホスト変数をさらにファイルに分割する

host_varsの下にホスト名と同じ名前のディレクトリを作成し、その下にホスト変数を記述したyamlファイルを配置することもできます。yamlファイルのファイル名はどんなものでもかまいません。この場合、サーバの機能や特徴ごとにファイルを分けるのがよさそうです。

|-- inventory
`-- host_vars
    `-- serverA_01
        |-- feature1
        `-- feature2

foo: "serverA_01のホスト変数foo"

bar: "serverA_01のホスト変数bar"

グループ変数

group_varsの下にグループ名と同じ名前でyamlファイルを作成し、その中にグループ変数を定義します。

|-- inventory
`-- group_vars
    |-- all
    |-- serverA.yml
    `-- serverB.yml

foo: "serverAのグループ変数foo"
bar: "serverAのグループ変数bar"

foo: "serverBのグループ変数foo"
bar: "serverBのグループ変数bar"

グループ変数をさらにファイルに分割する

ホスト変数と同様に、group_varsの下にグループ名と同じ名前のディレクトリを作成し、その下にグループ変数を記述したyamlファイルを配置できます。yamlファイルのファイル名はどんなものでもかまいません。

|-- inventory
`-- group_vars
    `-- serverA
        |-- feature1.yml
        `-- feature2.yml

foo: "serverAのグループ変数foo"

bar: "serverAのグループ変数bar"


親子関係のグループの場合

親グループも子グループもgroup_varsの下に作成します。

|-- inventory
`-- group_vars
    |-- parent.yml
    `-- serverA.yml

[parent]

[parent:children]
serverA

[serverA]
serverA_01 ansible_host=192.168.1.1

foobar: "parentのグループ変数:foobar"

foo: "serverAのグループ変数:foo"
bar: "serverAのグループ変数:bar"

Ansible:プレイブック中の長い1行を複数行に分割する

*ansible 2.10.4で動作を確認しています

Ansibleのdebugやshellモジュールを使う際に1行が長くなりすぎたため改行したい、という場合があります。


ブロックスタイルを使えば、yamlの中で文字列を複数行にわたり記載できます。


yamlの記法では「フロースタイル」と「ブロックスタイル」が存在するそうです。(以下のサイトを参考させていただきました)

magazine.rubyist.net


フロースタイルの文字列はクォーテーションで囲まれた文字列です。ブロックスタイルは文字列を改行して記載します。以下のymlのdebugメッセージをブロックスタイルで記載しました。


---
- hosts: all
  tasks:
    - name: Pattern 1 [ | ]
      debug:
        # ブロックスタイル1
        # 各行の改行を保存
        msg: |
          aaaaa
          bbbbb
          ccccc

    - name: Pattern 2 [ |+ ]
      debug:
        # ブロックスタイル2
        # 各行の改行を保存、最終行の次の行の改行も保存
        msg: |+
          aaaaa
          bbbbb
          ccccc

    - name: Pattern 3 [ |- ]
      debug:
        # ブロックスタイル3
        # 各行の改行を保存、最終行の改行は取り除く
        msg: |-
          aaaaa
          bbbbb
          ccccc

    - name: Pattern 4 [ > ]
      debug:
        # ブロックスタイル4
        # 最終行以外の改行を半角スペースに置換
        msg: >
          aaaaa
          bbbbb
          ccccc

    - name: Pattern 5 [ >+ ]
      debug:
        # ブロックスタイル5
        # 最終行以外の改行を半角スペースに置換、最終行の次の行の改行も保存
        msg: >+
          aaaaa
          bbbbb
          ccccc

    - name: Pattern 6 [ >- ]
      debug:
        # ブロックスタイル6
        # 各行の改行を半角スペースに置換、最終行の改行は取り除く
        msg: >-
          aaaaa
          bbbbb
          ccccc


実行結果は以下になります。

# ansible-playbook -i hosts block_literal.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [CentOS7.6]

TASK [Pattern 1 [ | ]] **************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa\nbbbbb\nccccc\n"
}

TASK [Pattern 2 [ |+ ]] *************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa\nbbbbb\nccccc\n\n"
}

TASK [Pattern 3 [ |- ]] *************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa\nbbbbb\nccccc"
}

TASK [Pattern 4 [ > ]] **************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa bbbbb ccccc\n"
}

TASK [Pattern 5 [ >+ ]] *************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa bbbbb ccccc\n\n"
}

TASK [Pattern 6 [ >- ]] *************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "aaaaa bbbbb ccccc"
}

PLAY RECAP **************************************************************************************************************************************************
CentOS7.6                  : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


shellモジュールに渡すコマンドもブロックスタイルを使えば改行できます。他にも、jinja2の構文を使う際などに利用するとよさそうです。

---
- hosts: all
  tasks:
    - name: check root user
      shell: >-
        cat /etc/passwd
        | grep ^root
        | wc -l
      register: response

    - debug:
        msg: >-
          {% if response.stdout != "0" %}
          exist
          {% else %}
          not exist
          {% endif %}


# ansible-playbook -i hosts check_root_user.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [CentOS7.6]

TASK [check root user] *******************************************************************************************************************************************
changed: [CentOS7.6]

TASK [debug] ************************************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "exist "
}

PLAY RECAP **************************************************************************************************************************************************
CentOS7.6                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ansible:CentOSのctrl-alt-del.targetを無効にする

CentOSRHELでは、キーボードからCtrl-Alt-Delを入力するとOSの再起動が始まります。何かしらのサービスを提供しているサーバで誤ってCtrl-Alt-Delを打つと、サービスが一時的に停止してしまいます(恐ろしいですね…)。また、Ctrl-Alt-DelはWindowsサーバだとサインイン時に使うので、なおさら入力ミスしてしまう可能性があります。


というわけで、Ctrl-Alt-Delによる再起動を無効化しておく方がよいです。

ターミナルでctrl-alt-del.targetを無効化

ctrl-alt-delを無効にするには、systemctlコマンドでctrl-alt-del.targetをマスクします、

# echo "実行前ステータス確認" > /dev/null
# systemctl status ctrl-alt-del.target
● reboot.target - Reboot
   Loaded: loaded (/usr/lib/systemd/system/reboot.target; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:systemd.special(7)
#
# echo "ctrl-alt-delをマスク" > /dev/null
# systemctl mask ctrl-alt-del.target
#
# echo "実行後ステータス確認" > /dev/null
# systemctl status ctrl-alt-del.target
● ctrl-alt-del.target
   Loaded: masked (/dev/null; bad)
   Active: inactive (dead)


ただ、これだけではctrl-alt-delが2秒以内に7回以上押された場合、再起動が実行されてしまうそうです。ctrl-alt-delが2秒以内に7回以上押されても再起動されないように「/etc/systemd/system.conf」を編集し、CtrlAltDelBurstActionというパラメータにnoneを設定します。

~ 省略 ~
#CtrlAltDelBurstAction=reboot-force
~ 省略 ~

~ 省略 ~
#CtrlAltDelBurstAction=reboot-force
CtrlAltDelBurstAction=none
~ 省略 ~

Ansibleでctrl-alt-del.targetを無効化

先ほどの内容をAnsibleで記述すると以下のようになります。


systemdモジュールでctrl-alt-del.targetをマスクし、copyモジュールで/etc/systemd/system.confを書き換えています。

---
- hosts: all
  become: yes
  become_method: su
  tasks:
    - name: disable ctrl-alt-del.target
      systemd:
        name: ctrl-alt-del.target
        masked: yes
    - name: copy system.conf
      copy:
        src: system.conf
        dest: /etc/systemd/system.conf
        mode: 0644
        owner: root
        group: root
        backup: yes

[Manager]
CtrlAltDelBurstAction=none

OS自作入門:QEMUをインストールしてフロッピーディスクをエミュレート

OSの自作には興味があったので川合秀実さん著の「30日でできる!OS自作入門」を買ってみました。付属のCDが充実していて、ソースコードやOSの実行に必要な各種ツールなどがそろっており、本に記載されている通りの方法で実行できます(素晴らしい!!)。

https://book.mynavi.jp/supportsite/detail/4839919844.html



とはいえ、せっかくなので実行環境の準備は自分でやってみたいな、と思い自身のPC(Windows 10)にQEMUをインストールしました。

QEMUのインストール

まずはQEMUダウンロードサイトにアクセスします。
https://www.qemu.org/download/#windows

64bitのWindowsにインストールするので64bitをクリックします。

f:id:muknow_kuknow:20210110132943p:plain:w550


最新のインストーラをダウンロードします。

f:id:muknow_kuknow:20210110132949p:plain:w550


ダウンロードしたインストーラを実行します。実行するとセットアップウィザードが立ち上がります。


インストール時の言語選択です。「English」のまま「OK」を押します。

f:id:muknow_kuknow:20210110132910p:plain:w200


「Next」を押します。

f:id:muknow_kuknow:20210110132915p:plain:w400


ライセンス条項に同意するか聞かれます。同意する場合は「I Agree」を押します。

f:id:muknow_kuknow:20210110132920p:plain:w400


インストールするコンポーネントを選択します。デフォルトのまま全てにチェックが入った状態で「Next」を押します。

f:id:muknow_kuknow:20210110132925p:plain:w400


インストール先のパスを選択します。特にこだわりはないのでデフォルトのまま「C:\Program Files」の下にします。

f:id:muknow_kuknow:20210110132930p:plain:w400


インストールがはじまります。

f:id:muknow_kuknow:20210110132934p:plain:w400


インストールが終わると完了画面が表示されます。

f:id:muknow_kuknow:20210110132938p:plain:w400



最後に、インストール先である「C:\Program Files\qemu」を環境変数pathに追加します。


QEMUでOSを起動する

OSが起動できるかやってみます。


「30日でできる!OS自作入門」ではフロッピーディスクのディスクイメージからOSをブートします。helloos.imgがディスクイメージです。「hello world」と文字を表示する簡素なものです。


フロッピーディスクをマウントするのでqemuの-deviceでfloppyを指定しています。

PS C:\Users\muknow\Documents\OS自作入門\my_work\helloos0> ls


    ディレクトリ: C:\Users\muknow\Documents\OS自作入門\my_work\helloos0


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2004/04/01      1:07              7 !cons_9x.bat
-a----        2004/04/01      1:09              7 !cons_nt.bat
-a----        2005/05/29     13:36        1474560 helloos.img
-a----        2005/05/29     13:36        1474560 helloos.nas
-a----        2005/03/04     22:13             38 install.bat
-a----        2005/03/03     13:24             85 run.bat


PS C:\Users\muknow\Documents\OS自作入門\my_work\helloos0> qemu-system-x86_64 -blockdev driver=file,node-name=f0,filename=helloos.img -device floppy,drive=f0


無事、起動ができました。

f:id:muknow_kuknow:20210110142437p:plain:w500

Ansible:roles_pathでロール検索パスを設定する

*ansible 2.10.4で動作を確認しています

Ansibleでroleを利用する際、よく見かけるのは以下のようなディレクトリ構成かと思います。プレイブック(playbook.yml)と同じ階層にrolesというディレクトリが存在し、その下にロールが存在するという構成です。

|-- inventory
|-- playbook.yml
`-- roles
    `-- foo
        `-- tasks
            `-- main.yml


Ansibleは標準でロールを検索する際、プレイブックからの相対パスでrolesというディレクトリを検索するそうで、そのため、playbook.ymlからroles/fooを実行できるわけです。

rolesを他の場所に配置したい

以下のように、プレイブック(playbook.yml)とは別の階層にrolesを配置したとします。

|-- host1
|   `-- playbook.yml
|-- inventory
`-- roles
    `-- foo
        `-- tasks
            `-- main.yml


この時、playbook.ymlを実行すると検索パスにrolesが存在しないためエラーが出てしまいます。

---
- hosts: all
  roles:
    - foo


---
- name: output message
  debug:
    msg: "foo"


$ pwd
/home/muknow/workspace/ansible/test_role_path
$ ls
host1  inventory  roles
$ ansible-playbook -i inventory host1/playbook.yml
ERROR! the role 'foo' was not found in /home/muknow/workspace/ansible/test_role_path/host1/roles:/home/muknow/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:/home/muknow/workspace/ansible/test_role_path/host1

The error appears to be in '/home/muknow/workspace/ansible/test_role_path/host1/playbook.yml': line 5, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  roles:
    - foo
      ^ here


roles_pathを設定する

ansible.cfgでroles_pathを設定することで検索パスを追加することができます。検索パスを追加すれば、このプレイブックもうまく実行できるはずです。


今回はansible.cfgをrolesと同じ階層に配置し、ここからansible-playbookコマンドを実行することにします。roles_pathには相対パスでrolesを指定しました。

[defaults]
roles_path    = ./roles


最終的な構成は以下のようになります。

|-- ansible.cfg
|-- host1
|   `-- playbook.yml
|-- inventory
`-- roles
    `-- foo
        `-- tasks
            `-- main.yml


fooが実行されました。成功です。

$ pwd
/home/muknow/workspace/ansible/test_role_path
$ ls
ansible.cfg  host1  inventory  roles
$ ansible-playbook -i inventory host1/playbook.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [CentOS7.6]

TASK [foo : output message] *********************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "foo"
}

PLAY RECAP **************************************************************************************************************************************************
CentOS7.6                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


host1の下にもrolesを作ったらどうなるのか?

気になったのでためしてみました。


先ほどの構成から追加で、host1の下にrolesを作り、barとfooというロールを作成しました。fooに関しては、同じ名前のロールがrolesの下、host1/rolesの下の2か所に存在する場合、どちらが優先されるかを見てみたいと思います。

|-- ansible.cfg
|-- host1
|   |-- playbook.yml
|   `-- roles
|       |-- bar
|       |   `-- tasks
|       |       `-- main.yml
|       `-- foo
|           `-- tasks
|               `-- main.yml
|-- inventory
`-- roles
    `-- foo
        `-- tasks
            `-- main.yml


---
- hosts: all
  roles:
    - bar
    - foo


---
- name: output message
  debug:
    msg: "bar [host1/roles/bar]"


---
- name: output message
  debug:
    msg: "foo [host1/roles/foo]"


---
- name: output message
  debug:
    msg: "foo [roles/foo]"



実行結果は以下となりました。
fooはroles/fooとhost1/roles/fooの2か所に作成しましたが、host1/roles/fooが実行されています。どうやら、roles_pathの設定したパスよりも、プレイブックと同階層のrolesの方が優先されるようです。

$ pwd
/home/muknow/workspace/ansible/test_role_path
$ ls
ansible.cfg  host1  inventory  roles
$ ansible-playbook -i inventory host1/playbook.yml

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

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [CentOS7.6]

TASK [bar : output message] *********************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "bar [host1/roles/bar]"
}

TASK [foo : output message] *********************************************************************************************************************************
ok: [CentOS7.6] => {
    "msg": "foo [host1/roles/foo]"
}

PLAY RECAP **************************************************************************************************************************************************
CentOS7.6                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Ansible:docker_containerモジュールを使ってみた

AnsibleでDockerコンテナを実行したいと思って調べていたらdocker_containerモジュールなるものを見つけました。

docs.ansible.com


ドキュメントを見るとオプションの多さに面食らいます…
ひとまず、細かいオプションは置いておいてコンテナを起動してみました。

実行環境

コントロールノード

  • Ansibleバージョン: 2.10.4
  • Pythonバージョン: 3.7

リモートホスト

  • Dockerバージョン: 19.03.8
  • Docker APIバージョン: 1.40
  • Docker SDK for Python: 4.4.1
  • Pythonバージョン: 3.8

準備

docker_containerモジュールを使うための大前提として、ターゲットホスト上にdockerがインストールされていなければなりません。(dockerのインストール方法については割愛)

また、pythonからdockerを操作するためのDocker SDKをインストールする必要があります。Docker SDKはpipでインストールできます。

$ pip install docker

※公式ドキュメントにも記載されていますが、Python2.6を使う場合は「pip install docker」ではなく「pip install docker-py」する必要があります。


コントロールノード側では、community.generalというコレクションのインストールが必要です。

$ ansible-galaxy collection install community.general
Process install dependency map
Starting collection install process
Installing 'ansible.netcommon:1.4.1' to '/home/muknow/.ansible/collections/ansible_collections/ansible/netcommon'
Installing 'google.cloud:1.0.1' to '/home/muknow/.ansible/collections/ansible_collections/google/cloud'
Installing 'community.general:1.3.1' to '/home/muknow/.ansible/collections/ansible_collections/community/general'
Installing 'community.kubernetes:1.1.1' to '/home/muknow/.ansible/collections/ansible_collections/community/kubernetes'


nginxコンテナを起動する

nginxを立ち上げる簡単なプレイブックを書きました。コンテナ側の80番ポートをホスト側の8080番ポートにフォワーディングしています。


ちなみに、ansible_python_interpreterを使ってpythonバージョンを指定しています。今回の環境では、リモートホストはpyenvを使いpythonの環境を分けています。Docker SDKがインストールされたpython環境で、Ansibleがコマンド実行できるようにするため、ansible_python_interpreterで環境を指定しています。

---
- hosts: all
  become: yes
  become_method: su
  tasks:
    - name: Create nginx
      community.general.docker_container:
        name: nginx
        image: nginx:latest
        state: started
        container_default_behavior: compatibility
        network_mode: bridge
        published_ports: 0.0.0.0:8080:80
      vars:
        ansible_python_interpreter: /root/.pyenv/versions/3.8.1/bin/python


以下がプレイブックの実行結果です。

ansible-playbook -i hosts run_nginx.yml

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

TASK [Gathering Facts] **************************************************************************************************************************
ok: [CentOS7.6]

TASK [Create nginx] *****************************************************************************************************************************
changed: [CentOS7.6]

PLAY RECAP **************************************************************************************************************************************
CentOS7.6                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


リモートホスト側で「docker ps」した結果が以下です。起動成功してそうです。

# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
d9f0b5bc6c23        nginx:latest        "/docker-entrypoint.…"   2 minutes ago       Up 2 minutes        0.0.0.0:8080->80/tcp   nginx