Sung-Kyu Yoo

Software Developer
Navigation
 » Home
 » About Me
 » Category
 » XML Feed

Ansible Module ๊ฐœ๋ฐœํ•˜๊ธฐ

14 Nov 2017 » all, ansible

Ansible ์€ ๋Œ€๊ทœ๋ชจ์˜ ์„œ๋ฒ„ ์žฅ๋น„์— ๋Œ€ํ•œ ์„ค์น˜ ๋ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐฐํฌ์™€ ์„œ๋น„์Šค ์šด์˜์— ๋Œ€ํ•œ ์ž๋™ํ™” ๋ถ€๋ถ„์„ ๋น„๊ต์  ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ํŠธ๋ Œ๋“œ์ธ DevOps ๋ฅผ ๊ฐ€๋Šฅ์ผ€ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘์— ํ•˜๋‚˜๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Ansible ์€ SSH ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ˆ˜ํ–‰๋˜๋ฉฐ, ์›๊ฒฉ ์žฅ๋น„์˜ SSH ์ ‘๊ทผ ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„์˜ ๋ฐ๋ชฌ์ด๋‚˜ ์—์ด์ „ํŠธ๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์›๊ฒฉ ์žฅ๋น„ (๊ธฐ๋ณธ ์ œ๊ณต๋˜๋Š” Ansible Module ์˜ ๊ฒฝ์šฐ) Python-2.6 ์ด์ƒ๋งŒ ์‹œ์Šคํ…œ์— ์„ค์น˜๊ฐ€ ๋˜์–ด ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. (์ผ๋ถ€ ๋ชจ๋“ˆ์€ ๋ณ„๋„์˜ ํŒŒ์ด์ฌ ๋ชจ๋“ˆ์„ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.)
  • Ansible Module ์€ ๋ฉฑ๋“ฑ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€, ๋ชจ๋“ˆ์— ์˜ˆ์™ธ์ ์œผ๋กœ ๋ฉฑ๋“ฑ์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ฌธ์„œ์— ์ฃผ์˜์‚ฌํ•ญ์„ ๊ผญ ๋‚จ๊ฒจ๋†“์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์†Œ๊ฐœ

Ansible Module ์€ Ansible Playbook ์˜ ํ•˜๋‚˜์˜ Task ์—์„œ ์–ด๋– ํ•œ ๋ชฉ์ ์„ ๊ฐ€์ง€๋Š” ์ผ๋ จ์˜ ๊ธฐ๋Šฅ ์ง‘ํ•ฉ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, โ€œํŒŒ์ผ์„ A ๊ฒฝ๋กœ์—์„œ B ๊ฒฝ๋กœ๋กœ ํŒŒ์ผ์„ ์˜ฎ๊ธฐ๋Š” ๊ธฐ๋Šฅโ€ ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ, Ansible ์—์„œ ๊ธฐ๋ณธ ๋ชจ๋“ˆ๋กœ ์ œ๊ณตํ•˜๋Š” โ€œfileโ€ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

tasks:
    - name: move a file from A to B path
      file: src="A" dest="B" # A ์˜ ํŒŒ์ผ์„ B ๋กœ move ํ•ฉ๋‹ˆ๋‹ค.
      register: file_result # file ๋ชจ๋“ˆ์˜ ๊ฒฐ๊ณผ๋ฅผ "file_result" ๋ผ๋Š” ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
    
    - debug: msg="" #  file ๋ชจ๋“ˆ์ด STDOUT ์œผ๋กœ ์ถœ๋ ฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ํ„ฐ๋ฏธ๋„์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๋‹จ์ˆœํžˆ I/O ๊ด€์ ์—์„œ ๋ณผ ๋•Œ, Ansible Module ์€ Attributes ๋กœ ์ž…๋ ฅ์„ ์ „๋‹ฌํ•˜๊ณ , ์ž…๋ ฅ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ผ๋ จ์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚˜๋ฉด, JSON Format ์„ STDOUT ์œผ๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. Ansible Module ์€ ์ผ๋ จ์˜ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋˜๋Š” ๊ณผ์ •์—์„œ ๋‹จ์ˆœ I/O ์— ๊ตญํ•œํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์—ฐ๋™๊ณผ ๊ฐ™์ด Side-Effect ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์ด๋Š” ์ ์ ˆํ•œ ๋ฒ”์œ„์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ์œ ์šฉํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

๊ตฌํ˜„

์ž…๋ ฅ์œผ๋กœ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๋ฐ›๊ณ , ํ•ด๋‹น ๊ฒฝ๋กœ์— ์žˆ๋Š” ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ชจ๋“ˆ ํ•˜๋‚˜๋ฅผ ์˜ˆ์ œ๋กœ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. Python 2.7 / Ansible 2.2 ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


๊ฐœ๋ฐœํ™˜๊ฒฝ ๊ตฌ์ถ•

๊ตฌํ˜„์„ ์ง„ํ–‰ํ•˜๊ธฐ์— ์•ž์„œ ๋จผ์ € Ansible Module ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Github ์˜ Ansible ํ”„๋กœ์ ํŠธ ์•ˆ์— ๋ชจ๋“ˆ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# Ansible Module ์ด ์ €์žฅ๋  library ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
$ mkdir library; cd library

# Ansible ํ”„๋กœ์ ํŠธ๋ฅผ Git ์ €์žฅ์†Œ๋กœ๋ถ€ํ„ฐ ๋ณต์ œํ•œ๋‹ค. ์‰˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ import ํ•œ๋‹ค.
$ git clone git://github.com/ansible/ansible.git --recursive
$ . ansible/hacking/env-setup

# test-module cli ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“ˆ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
$ ansible/hacking/test-module -m ./ls.py -a 'path="."'

๋ชจ๋“ˆ ๋ช…์„ธ ์ž‘์„ฑ

๊ฐœ๋ฐœํ™˜๊ฒฝ ๊ตฌ์ถ•์„ ์™„๋ฃŒํ•˜์˜€๋‹ค๋ฉด, Ansible Module ํŒŒ์ด์ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํŽธ์ง‘๊ธฐ๋กœ ์—ด๊ณ , ๋จผ์ € ๋ชจ๋“ˆ ๋ช…์„ธ๋ถ€ํ„ฐ ์ž‘์„ฑํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ๋ชจ๋“ˆ ๊ตฌํ˜„์— ์•ž์„œ ๋ชจ๋“ˆ์˜ I/O ์ŠคํŽ™์— ๋Œ€ํ•ด์„œ ๋จผ์ € ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ๊ตฌํ˜„๋ณด๋‹ค ๋จผ์ € ์„ ํ–‰๋˜๋ฉด ๋ฌด์—‡์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋  ์ง€ ๋” ๋ช…ํ™•ํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

#!/usr/bin/env python2

DOCUMENTATION = """
module: ls
short_description: Listing files in a given path
"""

EXAMPLES = """
- name: listing files in current directory
  ls: path="."
"""

๋ชจ๋“ˆ ๊ฐœ๋ฐœ

๋ชจ๋“ˆ ๋ช…์„ธ ์ž‘์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฉด, Ansible Module ๊ตฌํ˜„์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„์— ์•ž์„œ ๋ช‡๊ฐ€์ง€ ์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. Ansible Module ์€ ๋‹น์—ฐํžˆ ํŒŒ์ด์ฌ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋˜๋ฉฐ, Python 2 ์„ ๊ณต์‹์ ์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Python 3 ๋„ Ansible 2.2 ๋ถ€ํ„ฐ ์ง€์›์„ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ, ์ผ๋ถ€ ๋ชจ๋“ˆ์ด ๋Œ€์‘์ด ๋˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ( Ansible Python 3 Support )

๊ทธ๋ฆฌ๊ณ , Ansible Module ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŒŒ์ด์ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ฐ€๊ธ‰์  Ansible ์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ชจ๋“ˆ ์œ ํ‹ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์ด ๊ฒฝ์šฐ์—๋Š” ๋ชจ๋“ˆ ๋ช…์„ธ์— ๋ฐ˜๋“œ์‹œ ์–ธ๊ธ‰์„ ํ•ด์ฃผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Ansible Module ์€ ๋Œ€๋ถ€๋ถ„ ์•„๋ž˜์˜ ์ˆœ์„œ๋Œ€๋กœ ๊ตฌํ˜„์ด ์ง„ํ–‰ ๋ฉ๋‹ˆ๋‹ค.

  1. ๋ชจ๋“ˆ์˜ ์†์„ฑ์„ ์ •์˜ํ•˜๊ณ  ์†์„ฑ์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…, ํ•„์ˆ˜ ์—ฌ๋ถ€, ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ’, ๊ธฐ๋ณธ ๊ฐ’ ๋“ฑ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ชจ๋“ˆ์ด ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค. ์ •์ƒ ์ฝ”๋“œ ์ด์™ธ์˜ ๋ชจ๋“  ์—๋Ÿฌ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ชจ๋“ˆ์ด ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ํ™˜๊ฒฝ์ด ๋™์ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (๋ฉฑ๋“ฑ์„ฑ)
  3. ๋ชจ๋“ˆ์— ์ž…๋ ฅ ๋ฐ›์€ ์†์„ฑ์— ๋”ฐ๋ผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ , ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ JSON Format ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  4. ๋ชจ๋“ˆ์˜ ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, exit_json (์ •์ƒ) ํ˜น์€ fail_json (์‹คํŒจ) ์ค‘์— ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
from ansible.module_utils.basic import *

import urllib
import urllib2
import sys
import os

no_error_status_code = 0
error_status_code = 1 

status_msg = {
    error_status_code: "ERROR!",
}

def listing(params):
    path = params['path']
    if not os.path.exists(path):
        return error_status_code, False, []

    files = []
    for dirname, dirnames, filenames in os.walk(path):
        for subdirname in dirnames:
            files.append(os.path.join(dirname, subdirname))
        for filename in filenames:
            files.append(os.path.join(dirname, filename))
    return no_error_status_code, True, files

def main():
    fields = {
        "path": {"required": True, "type": "str"},
    }
    module = AnsibleModule(argument_spec=fields)
    status_code, has_changed, files = listing(module.params)

    if status_code == 0:
        module.exit_json(changed=has_changed, files=files)
    else:
        module.fail_json(msg=status_msg[status_code])

if __name__ == '__main__':
    main()


๋ชจ๋“ˆ ํ…Œ์ŠคํŠธ

๋ชจ๋“ˆ ๊ตฌํ˜„์ด ์™„๋ฃŒ๋œ ์ดํ›„์— ์˜๋„ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# ๊ฐœ๋ฐœ์ด ์™„๋ฃŒ๋˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.
$ ansible/hacking/test-module -m ./ls.py -a 'path="."'
* including generated source, if any, saving to: /home/zicprit/.ansible_module_generated
* ansiballz module detected; extracted module source to: /home/zicprit/debug_dir
***********************************
RAW OUTPUT

{"files": ["./ls.py"], "invocation": {"module_args": {"path": "."}}, "changed": true}


***********************************
PARSED OUTPUT
{
    "changed": true, 
    "files": [
        "./ls.py"
    ], 
    "invocation": {
        "module_args": {
            "path": "."
        }
    }
}

์ฐธ๊ณ  ๋งํฌ

[1] Ansible Module ๊ฐœ๋ฐœ ๊ณต์‹ ๊ฐ€์ด๋“œ

[2] 10๋ถ„ ์•ˆ์— Ansible Module ๋งŒ๋“ค๊ธฐ