Ansible 실전 · 필터가 없으면 만든다 · 실습
필터가 없어서 shell 로 때우던 것을 플러그인으로 만든다
목표
Jinja2 필터·테스트·룩업과 사용자 모듈을 직접 만들어 플레이북에서 부릅니다. 네 가지가 각각 어디서 실행되는지, 그 자리 차이가 무엇을 할 수 있고 무엇을 할 수 없게 만드는지를 코드로 확인합니다.
왜 중요한가
플레이북이 커지면 어느 팀에나 같은 자리가 생깁니다 — 이름을 다듬고, 숫자를 분류하고, 어딘가의 표를 찾아보는 일입니다. 표준 필터로 안 되면 사람들은 shell 과 sed 로 때우고, 그 순간 그 태스크는 멱등성도 점검 모드도 잃습니다. 플러그인은 그 자리를 위한 것입니다. 다만 아무 데나 넣으면 되는 것이 아니라, 어디서 도는가가 설계의 절반입니다. 필터·테스트·룩업은 컨트롤러에서 돌아서 대상에 아무것도 설치할 필요가 없는 대신 대상의 상태를 볼 수 없고, 모듈은 대상으로 복사되어 거기서 돌기 때문에 상태를 바꿀 수 있는 대신 멱등성과 점검 모드를 스스로 책임져야 합니다. 이 실습은 그 경계를 네 번 넘나들며 손으로 확인합니다.
단계
1. /root/ansplug/ansible.cfg 를 만들어 기본 인벤토리를 ./inventory/hosts.ini 로 지정하세요. /root/ansplug/inventory/hosts.ini 에는 그룹 셋을 적습니다 — web 에 web1(svc_port 8080, svc_name Web Front 01)과 web2(9090, Web Front 02), db 에 db1(5432, Main DB!), edge 에 cache1(443, Edge Cache). 네 호스트 모두 ansible_host=127.0.0.1 ansible_port=2222 이고 [all:vars] 의 ansible_user 는 root 입니다. 그다음 ansible all -m ansible.builtin.ping -o 를 돌려 표준 출력과 표준 오류를 함께 /root/ansplug/out/ping.txt 에 저장하세요.
2. /root/ansplug/filter_plugins/labfilters.py 를 만들어 slugify 필터 하나를 등록하세요. 이 필터는 문자열을 소문자로 낮추고, 영숫자가 아닌 글자를 모두 - 로 바꾸고, 연달아 나온 - 를 하나로 줄이고, 양 끝의 - 를 떼어 냅니다. 문자열이 아닌 값이 들어오면 AnsibleFilterError 를 던집니다. 그다음 /root/ansplug/slug.yml 을 만들어 'Web Server 01!!' 를 이 필터에 넣은 결과를 /root/ansplug/out/slug.txt 에 쓰고 실행하세요.
3. 같은 /root/ansplug/filter_plugins/labfilters.py 에 port_class 필터를 더하세요. port_class(value, privileged_below=1024, dynamic_from=49152) 는 포트가 privileged_below 보다 작으면 system, dynamic_from 이상이면 dynamic, 그 사이면 user 를 돌려주고, 정수가 아니면 AnsibleFilterError 를 던집니다. 그다음 /root/ansplug/ports.yml 을 만들어 /root/ansplug/out/ports.txt 에 인벤토리의 네 호스트를 이름 오름차순으로 <호스트> <포트> <분류> 한 줄씩 쓰고 실행하세요.
4. /root/ansplug/test_plugins/labtests.py 를 만들어 reserved_port 테스트를 등록하세요 — 포트 번호가 1024 보다 작으면 참입니다. 그다음 /root/ansplug/reserved.yml 을 만들어 /root/ansplug/out/reserved.txt 에 네 호스트를 이름 오름차순으로 <호스트> <포트> <reserved|free> 한 줄씩 쓰고 실행하세요. 판정은 반드시 is reserved_port 꼴로 부릅니다.
5. /root/ansplug/registry.json 에 {"web": "seoul-a", "db": "seoul-b", "edge": "seoul-c"} 를 적으세요. /root/ansplug/lookup_plugins/labregistry.py 를 만들어 labregistry 룩업을 정의합니다 — 열쇠를 받아 등록부의 값을 돌려주고, registry= 키워드 인자로 다른 파일을 지정할 수 있으며(기본값은 /root/ansplug/registry.json), 파일이 없거나 열쇠가 없으면 AnsibleError 를 던집니다. 그다음 /root/ansplug/regions.yml 로 /root/ansplug/out/regions.txt 에 db·edge·web 세 그룹의 <그룹> <지역> 을 이 순서로 쓰고 실행하세요.
6. /root/ansplug/collections/ansible_collections/labhub/site/ 에 컬렉션을 놓으세요 — galaxy.yml 의 namespace 는 labhub, name 은 site 이고, 필터 파일은 plugins/filter/ 아래에 둡니다(2·3단계에서 만든 것을 그대로 복사하면 됩니다). /root/ansplug/ansible.cfg 에 collections_path = ./collections 를 더하고, /root/ansplug/fqcn.yml 로 'Prod DB 02!!' 를 labhub.site.slugify 에 넣은 결과를 /root/ansplug/out/fqcn.txt 에 쓰고 실행하세요.
7. /root/ansplug/library/lab_marker.py 에 사용자 모듈 lab_marker 를 만드세요 — 인자는 path 와 content 이고, 파일 내용이 이미 같으면 changed=false, 다르면 파일을 쓰고 changed=true 로 끝냅니다. supports_check_mode=True 를 선언하고 점검 모드에서는 쓰지 않습니다. /root/ansplug/marker.yml 로 web 그룹에 이 모듈을 돌려 /root/ansplug/out/<호스트이름>.marker 에 그 호스트 이름 한 줄을 쓰게 하고, 플레이북을 두 번 실행해 두 번째 실행의 출력을 /root/ansplug/out/marker_run2.txt 에 저장하세요.
8. /root/ansplug/report.yml 로 /root/ansplug/out/report.txt 에 네 호스트를 이름 오름차순으로 한 줄씩 쓰세요 — <호스트> <이름슬러그> <포트> <포트분류> <reserved|free> <지역> 이고, 이름슬러그는 svc_name 을 slugify 한 값, 포트분류는 port_class, 다섯째 칸은 reserved_port 테스트, 지역은 그 호스트의 첫 그룹 이름을 labregistry 로 물은 값입니다. 플레이북을 두 번 실행하고 두 번째 실행의 출력을 /root/ansplug/out/report_run2.txt 에 저장하세요.
참고
filter_plugins/·test_plugins/·lookup_plugins/·library/는 플레이북 옆에 있어야 찾아집니다. 그래서 이 실습의 플레이북은 전부/root/ansplug바로 아래에 둡니다.- sshd 는 127.0.0.1 의 2222 에 떠 있습니다. 인벤토리의 네 호스트는 이름만 다를 뿐 전부 같은 서버로 붙습니다.
- 부르는 법이 종류마다 다릅니다 — 필터는
값 | 이름, 테스트는값 is 이름, 룩업은lookup('이름', 인자), 모듈은 태스크의 키입니다. - 흔한 실수: 클래스 이름을
FilterModule이 아닌 것으로 지어 놓고 '왜 못 찾지' 하는 것. 파일 이름은 자유지만 클래스 이름은 규약입니다. - 흔한 실수: 애드혹 명령으로 새 필터를 시험해 보고 안 된다고 판단하는 것 — 옆 디렉터리 탐색은 플레이북에만 걸립니다.
- 흔한 실수: 컬렉션을
ansible_collections/층 없이 놓는 것. 오류가 나지 않고 그냥 못 찾습니다. - [Developing plugins](https://docs.ansible.com/ansible/latest/dev_guide/developing_plugins.html) · [Adding modules and plugins locally](https://docs.ansible.com/ansible/latest/dev_guide/developing_locally.html) · [Lookup plugins](https://docs.ansible.com/ansible/latest/plugins/lookup.html) · [Collection structure](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections_structure.html) · [Developing modules](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html)
단계 8개
- 네 대를 흉내 내는 인벤토리부터 세운다
- 첫 필터 — 컨트롤러에서 도는 순수 함수
- 인자를 받는 필터와 잘못된 입력을 끊는 자리
- 테스트 플러그인 — 참거짓만 돌려주는 자리
- 룩업 — 컨트롤러의 파일을 읽어 오는 자리
- 같은 필터를 컬렉션 안으로 옮겨 FQCN 으로 부른다
- 모듈은 대상에서 돈다 — 멱등성과 점검 모드는 그쪽 책임이다
- 네 종류를 한 플레이북에서 잇는다