【Ansible】🤢🤮

f:id:hira98:20190727170143p:plain

【結論】

  • Ansibleでは別フィルを呼び出すことで、関数呼び出し的なことをできる。
  • ただし、引数や戻り値を渡す方法はないため、全てグローバル変数でやり取りする。
  • 結果、現場のエンジニアが「動いているのも不思議」とぼやくレベルの💩コードが出来上がる。

【目次】

はじめに

現在、Ansibleでホットスタンバイ方式で構成された機器への設定を行うプレイブックを開発しています。

制御対象の機器は、Active状態とStandby状態が自動で切り替わる仕様です。

よって、設定を行う前に、現在どちらがActive状態にあるかを判断して、接続先を分岐する必要があります。

この処理をフローで表すと下図のようになります。

f:id:hira98:20190727170210p:plain

1系と2系の状態によって設定先を変更する処理を、Ansibleで書くためにかなりの時間を溶かしてしまったため、自戒の念を込めてブログにまとめました。

Pythonで書いた場合

接続先情報を事前に定義しておき使い回しています。

#接続先情報の定義
SERVER_INFO = [
    {
        "ip": "111.111.111.111",
        "username": "admin_1",
        "password": "hoge_1" 
    },{
        "ip": "222.222.222.222",
        "username": "admin_2",
        "password": "hoge_2" 
    }
]

#接続先情報よりActive機器のIDを取得
active_num = check_active_server(SERVER_INFO)

#Active機器の接続情報を表示
print(SERVER_INFO[active_num]["ip"])
print(SERVER_INFO[active_num]["username"])
print(SERVER_INFO[active_num]["password"])

#Active機器へ設定を行う(省略)

上記処理を、以下のように実装しているPythonプログラマーはいないと思いますが、もしいたとしたら次の理由により、淘汰されるべきだと思っています。

  • 辞書やリストを使ってデータ構造を定義していない。
  • 関数を呼び出す際に引数や戻り値を使わず、全てグローバル変数を介してやり取りしている。
#接続先情報の定義
SERVER_1_IP = "111.111.111.111"
SERVER_1_USERNAME = "admin_1"
SERVER_1_PASSWORD = "hoge_1" 
SERVER_2_IP = "222.222.222.222"
SERVER_2_USERNAME = "admin_2"
SERVER_2_PASSWORD = "hoge_2"

server_ip = ""
server_username = ""
server_password = ""
active_num = -1

# 接続先情報よりActive機器のIDを取得
# 引数や戻り値はグローバル変数を直接参照する。
check_active_server()

if active_num == 1:
    server_ip       = SERVER_1_IP
    server_username = SERVER_1_USERNAME
    server_password = SERVER_1_PASSWORD
if active_num == 2:
    server_ip       = SERVER_2_IP
    server_username = SERVER_2_USERNAME
    server_password = SERVER_2_PASSWORD

#Active機器の接続情報を表示
print(server_ip)
print(server_username)
print(server_password)

#Active機器へ設定を行う(省略)

Ansibleで書いた場合

同じ処理をAnsibleで書くと次のようなイメージになります。

※あくまで、イメージを掴むためのソースであるため、動作検証しておらず間違っている可能性があります。

---
- hosts: localhost  # 自分自身に接続する
  tasks:
  - name: 接続先情報の定義
    set_fact:
      SERVER_1_IP:       "111.111.111.111"
      SERVER_1_USERNAME: "admin_1"
      SERVER_1_PASSWORD: "hoge_1" 
      SERVER_2_IP:       "222.222.222.222"
      SERVER_2_USERNAME: "admin_2"
      SERVER_2_PASSWORD: "hoge_2"

  - name: 接続先情報よりActive機器のIDを取得
    import_tasks: check_active_server.yaml

  - name: 1系がActiveの場合
    set_fact:
      server_ip:       "{{ SERVER_1_IP }}"
      server_username: "{{ SERVER_1_USERNAME }}"
      server_password: "{{ SERVER_1_PASSWORD }}"
    when: active_num == 1

  - name: 2系がActiveの場合
    set_fact:
      server_ip:       "{{ SERVER_2_IP }}"
      server_username: "{{ SERVER_2_USERNAME }}"
      server_password: "{{ SERVER_2_PASSWORD }}"
    when: active_num == 2
  
  # Active機器の接続情報を表示(省略)
  # Active機器へ設定を行う(省略)

Ansibleでは複雑なデータ構造は定義できないため、冗長なコードでデータ定義を実装する方法しかないようです。

import_taskを用いて別ファイルを読み出すことで、Pythonの関数呼び出しと同じような処理を実装することができます。

しかし、引数や戻り値を受け渡す方法はないため、set_factを使ってグローバル変数へ値を格納することで、擬似的に引数の受け渡しを実装しています。

さいごに

1ヶ月ほどAnsibleで開発して得た情報と、現場で活躍しているAnsibleエンジニアの話を聞いた限りでは、「AnsibleではPythonだと🤢🤮レベルの💩コードしか書けない。」という結論に至ってしまいAnsibleアレルギーになりそうです。

Ansibleアレルギーにならないための対策として「Ansible Night in Tokyo」に参加して、Ansibleラブなエンジニアに洗脳されてきます。