Ansible 네트워크 자동화 – 3

Ansible 네트워크 자동화 -3

앤시블 인벤토리(Inventory) 만들기

 

인벤토리 없이 플레이북을 실행하려면 명령어에 여러 플래그들이 필요하다. 또한 하나의 장비에 대해 플레이북을 실행하는 것은 같은 작업을 수동으로 하는 것과 비교하여 크게 효율적이지도 않다.
ansible_network_os 와 ssh 사용자같은 정보를 가지는 managed node를 구성하기위해 인벤토리 파일을 사용한다. 모든 기능을 가지는 인벤토리 파일은 네트워크의 진짜 소스로 쓰일 수 있다.
인벤토리 파일을 사용하여 한 플레이북에서 단일 명령으로 수백개의 네트워크 장비를 유지할 수 있다.

기본 인벤토리

먼저, 인벤토리를 논리적으로 그룹화한다. 우수 사례는 용도(어플리케이션, 스택이나 마이크로서비스), 장소(데이터센터나 지역), 시점(개발 단계)별로 서버와 네트워크 장비를 그룹화 하는 것이다.

그룹 이름에 공백, -, 숫자를 사용하면 안된다. 그룹이름은 대소문자를 구별한다.

아래 간단한 datacenter 예제는 기본 그룹 구조를 보여준다. 그룹은 [metagroupname:children] 문법을 사용하고 메타그룹의 멤버로 그룹을 나열한다. network 그룹은 모든 leap와 spine을 v포함한다. datacenter 그룹은 모든 네트워크장비와 모든 웹서버를 추가한다.

---

leafs:
  hosts:
    leaf01:
      ansible_host: 192.168.100.11
    leaf02:
      ansible_host: 192.168.100.12

spines:
  hosts:
    spine01:
      ansible_host: 192.168.100.13
    spine02:
      ansible_host: 192.168.100.14

network:
  children:
    leafs:
    spines:

webservers:
  hosts:
    webserver01:
      ansible_host: 192.168.100.15
    webserver02:
      ansible_host: 192.168.100.16

datacenter:
  children:
    network:
    webservers:

아래 INI 포맷의 인벤토리는 위와 정확히 동일하다.

[leafs]
leaf01
leaf02

[spines]
spine01
spine02

[network:children]
leafs
spines

[webservers]
webserver01
webserver02

[datacenter:children]
network
webservers

인벤토리에 변수 추가하기.

인벤토리에 ansible 명령어에 필요한 변수와 값을 설정하여 ansible-play 명령을 단순화할 수 있다.
아래의 인벤토리 예제는 각 네트워크 장비의 IP, OS, ssh 사용자가 포함된다.
네트워크 장비를 IP 주소로만 접근할 수 있는 경우 인벤토리 파일에 IP 주소를 추가해야한다.
만약 네트워크장비를 호스트네임을 사용하여 접근 가능하면 IP 주소는 필요 없다.

---

leafs:
  hosts:
    leaf01:
      ansible_host: 192.168.100.11
      ansible_network_os: vyos.vyos.vyos
      ansible_user: my_vyos_user
    leaf02:
      ansible_host: 192.168.100.12
      ansible_network_os: vyos.vyos.vyos
      ansible_user: my_vyos_user

spines:
  hosts:
    spine01:
      ansible_host: 192.168.100.13
      ansible_network_os: vyos.vyos.vyos
      ansible_user: my_vyos_user
    spine02:
      ansible_host: 192.168.100.14
      ansible_network_os: vyos.vyos.vyos
      ansible_user: my_vyos_user

network:
  children:
    leafs:
    spines:

webservers:
  hosts:
    webserver01:
      ansible_host: 192.168.100.15
      ansible_user: my_server_user
    webserver02:
      ansible_host: 192.168.100.16
      ansible_user: my_server_user

datacenter:
  children:
    network:
    webservers:

인벤토리 내의 그룹 변수

OS나 ssh 사용자 같은 변수 값을 공유하는 그룹의 장비에서 그룹 변수로 통합해 중복을 줄이고 유지관리를 단순하게 할 수 있다.

leafs:
  hosts:
    leaf01:
      ansible_host: 192.168.100.11
    leaf02:
      ansible_host: 192.168.100.12
  vars:
    ansible_network_os: vyos.vyos.vyos
    ansible_user: my_vyos_user

spines:
  hosts:
    spine01:
      ansible_host: 192.168.100.13
    spine02:
      ansible_host: 192.168.100.14
  vars:
    ansible_network_os: vyos.vyos.vyos
    ansible_user: my_vyos_user

network:
  children:
    leafs:
    spines:

webservers:
  hosts:
    webserver01:
      ansible_host: 192.168.100.15
    webserver02:
      ansible_host: 192.168.100.16
  vars:
    ansible_user: my_server_user

datacenter:
  children:
    network:
    webservers:

변수 문법

변수 값의 문법은 인벤토리, 플레이북, group_vars 파일에서 다르다.
플레이북과 grup_vas 파일 모두 YAML로 작성되지만 변수 사용에는 차이가 있다.

ini 스타일의 인벤토리 파일에서는 key=value 형식을 사용해야한다. (예: ansible_network_os=vyos.vyos.vyos)
플레이북이나 group_vars를 포함하는 .yml 이나 .yaml 확장자를 가지는 파일에서는 반드시 YAML 문법을 사용해야한다. (예: key: value)
group_vars 파일에서는 전체 key 이름을 사용한다. (예: ansible_network_os: vyos.vyos.vyos)
플레이북에서 ansible 접두사를 생략하는 짧은 형식의 key 이름을 사용한다. (예: network_os: vyos.vyos.vyos)

플랫폼별 인벤토리 그룹

인벤토리가 커질수록 플랫폼별로 장비를 구룹화 할 수 있다. 이것으로 해당 플랫폼의 모든 장비에 대해 플랫폼별 변수를 쉽게 지정할 수 있다.

---

leafs:
  hosts:
    leaf01:
      ansible_host: 192.168.100.11
    leaf02:
      ansible_host: 192.168.100.12

spines:
  hosts:
    spine01:
      ansible_host: 192.168.100.13
    spine02:
      ansible_host: 192.168.100.14

network:
  children:
    leafs:
    spines:
  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_network_os: vyos.vyos.vyos
    ansible_user: my_vyos_user

webservers:
  hosts:
    webserver01:
      ansible_host: 192.168.100.15
    webserver02:
      ansible_host: 192.168.100.16
  vars:
    ansible_user: my_server_user

datacenter:
  children:
    network:
    webservers:

이 설정에서 first_playbook.yml은 두개의 플래그로 실행할 수 있다.
ansible-playbook -i inventory.yml -k first_playbook.yml

-k 플래그는 ssh 비밀번호를 묻는 프롬프트를 제공한다.
ansible-vault를 사용하여 group_vars 파일에 ssh 비밀번호를 안전하게 저장할 수 있다.

인벤토리 확인

테스트 인벤토리를 다음과 같이 작성.

---

branch1:
  hosts:
    b1_l3:
      ansible_host: 192.168.215.1
    b1_l2:
      ansible_host: 192.168.215.2
  vars:
    ansible_network_os: extreme.exos.exos
    ansible_user: admin

branch2:
  hosts:
    b2_l3:
      ansible_host: 192.168.232.121
    b2_l2:
      ansible_host: 192.168.232.120
  vars:
    ansible_network_os: cisco.ios.ios
    ansible_user: admin


network:
  children:
    branch1:
    branch2:


headquarter:
  children:
    network:

ansible-inventory 명령으로 ansible이 보는것 처럼 인벤토리를 표시할 수 있다.

$ ansible-inventory -i inventory_1.yml --list
{
    "_meta": {
        "hostvars": {
            "b1_l2": {
                "ansible_connection": "ansible.netcommon.network_cli",
                "ansible_host": "192.168.215.2",
                "ansible_network_os": "extreme.exos.exos",
                "ansible_user": "admin"
            },
            "b1_l3": {
                "ansible_connection": "ansible.netcommon.network_cli",
                "ansible_host": "192.168.215.1",
                "ansible_network_os": "exos.exos.exos",
                "ansible_user": "admin"
            },
            "b2_l2": {
                "ansible_connection": "ansible.netcommon.network_cli",
                "ansible_host": "192.168.232.120",
                "ansible_network_os": "cisco.ios.ios",
                "ansible_user": "admin"
            },
            "b2_l3": {
                "ansible_connection": "ansible.netcommon.network_cli",
                "ansible_host": "192.168.232.121",
                "ansible_network_os": "cisco.ios.ios",
                "ansible_user": "admin"
            }
        }
    },
    "all": {
        "children": [
            "headquarter",
            "ungrouped"
        ]
    },
    "branch1": {
        "hosts": [
            "b1_l2",
            "b1_l3"
        ]
    },
    "branch2": {
        "hosts": [
            "b2_l2",
            "b2_l3"
        ]
    },
    "headquarter": {
        "children": [
            "network"
        ]
    },
    "network": {
        "children": [
            "branch1",
            "branch2"
        ]
    }
}

ansible-vault로 민감한 변수 보호하기

ansible-vault 명령은 비밀번호같은 개별 변수나 파일의 암호화를 제공한다.
아래와 같은 명령으로 데이타베이스 비밀번호, 권한 상승 비밀번호, ssh 접속 비밀번호같은 민감한 정보를 암화화 할 수 있다.

1. ansible-vault 를 위한 비밀번호를 만든다.
비밀번호는 ansible-vault-password 를 사용할 것이다.

$ echo "ansible-vault-password" > ansible-vault-pw-file

2. 위에서 만든 ansible-vault 비밀번호 파일을 사용하여 cisco ios 장비의 비밀번호를 암호화한다.

$ ansible-vault encrypt_string --vault-id admin@~/ansible-vault-pw-file 'adminpasswd' --name 'ansible_password'
ansible_password: !vault |
          $ANSIBLE_VAULT;1.2;AES256;admin
          65636463613231633732313436356164356636666331376238333235363234653565393431363937
          3339613931356635373434316362616236353734656635350a656464353036353961333236323433
          62646261383632303335366530353262303064343536653438643437616464373165656162336163
          6231303935383831320a303264386239393161336533356636623434396562313132333463646231
          6537
Encryption successful

id 뒤에 파일을 지정하는 대신 prompt를 사용하면, 아래처럼 비밀번호를 입력하는 프롬프트를 내보낸다.

$ ansible-vault encrypt_string --vault-id admin@prompt 'adminpasswd' --name 'ansible_password'
New vault password (admin):
Confirm new vault password (admin):
ansible_password: !vault |
          $ANSIBLE_VAULT;1.2;AES256;admin
          36646430336231653236633435346463343665363866633533383862396430343166383962393165
          3662303731626331343434636166646266386364333065320a643166616634616532383037366265
          62366335396264636434633333626264366333336663646435313432356465643434303166636132
          3363386164616239620a633263343432626332306439663933383036366138306138383930363536

–vault-id 플래그는 다른 사용자나 다른 접근 수준에 대하여 서로다른 vault 암호를 허용한다.
출력은 ansible-vault 명령의 사용자 admin이 포함되며 YAML 구문을 사용한다.

INI 포맷은 인라인 vault를 지원하지 않으므로, YAML포맷을 사용해야한다.

테스트 인벤토리를 아래처럼 수정한다.

---

branch1:
  hosts:
    b1_l3:
      ansible_host: 192.168.245.62
    b1_l2:
      ansible_host: 192.168.245.25
  vars:
    ansible_network_os: extreme.exos.exos
    ansible_user: admin
    ansible_password: !vault |
          $ANSIBLE_VAULT;1.2;AES256;admin
          65313437336130346432383833323639303365303764313462393531613039336563613066313361
          6537623138383233343234306631636439373032313738300a663063653733366264356565636139
          39303766333534333162356162633038643763323234663836393966323263633531393932626635
          3935633935616565610a386632353730653562623131623637666636326132376235623438393464
          3731


branch2:
  hosts:
    b2_l3:
      ansible_host: 192.168.232.121
    b2_l2:
      ansible_host: 192.168.232.120
  vars:
    ansible_network_os: cisco.ios.ios
    ansible_user: admin
    ansible_password: !vault |
          $ANSIBLE_VAULT;1.2;AES256;admin
          65636463613231633732313436356164356636666331376238333235363234653565393431363937
          3339613931356635373434316362616236353734656635350a656464353036353961333236323433
          62646261383632303335366530353262303064343536653438643437616464373165656162336163
          6231303935383831320a303264386239393161336533356636623434396562313132333463646231
          6537

network:
  children:
    branch1:
    branch2:
  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_user: admin


headquarter:
  children:
    network:

vault 비밀번호 파일로 플레이북을 실행.

$ ansible-playbook -i inventory_1.yml --vault-id admin@~/ansible-vault-pw-file first_playbook.yml


PLAY [Network Getting Started First Playbook] ******************************************************************

TASK [Gathering Facts] *****************************************************************************************
ok: [b1_l3]
ok: [b1_l2]
ok: [b2_l3]
ok: [b2_l2]

TASK [Get config for IOS devices] ******************************************************************************
ok: [b1_l3]
ok: [b1_l2]
ok: [b2_l3]
ok: [b2_l2]

TASK [Display the config] **************************************************************************************
fatal: [b1_l3]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_net_version' is undefined\n\nThe error appears to be in '/home/snowfox/ansible/first_playbook.yml': line 13, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Display the config\n      ^ here\n"}
fatal: [b1_l2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_net_version' is undefined\n\nThe error appears to be in '/home/snowfox/ansible/first_playbook.yml': line 13, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Display the config\n      ^ here\n"}
ok: [b2_l3] => {
    "msg": "The hostname is M_Training and the OS is 12.2(55)SE11"
}
ok: [b2_l2] => {
    "msg": "The hostname is Mirae_5F_Support_212.8 and the OS is 12.2(55)SE1"
}

PLAY RECAP *****************************************************************************************************
b1_l2                      : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
b1_l3                      : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
b2_l2                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
b2_l3                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

비밀번호 파일 대신 직접입력하려면 파일 이름대신 prompt를 사용한다.(당연히 입력하는 비밀번호는 파일에 있는것과 동일해야함)

$ ansible-playbook -i inventory_1.yml --vault-id admin@prompt first_playbook.yml
Vault password (admin):

PLAY [Network Getting Started First Playbook] ******************************************************************

TASK [Gathering Facts] ***

....

원래의 값을 보기위해 디버그 모듈을 사용할 수 있다.
YAML 파일에 ansible_connection 변수를 정의하는경우, 아래 명령처럼 실행해야 적용된다.
이것을 방지하려면 ansible_connection 변수 없이 복사본을 만들면 된다.

참고문서: https://docs.ansible.com/ansible/latest/network/getting_started/basic_concepts.html#inventory

답글 남기기

Your email address will not be published.