インベントリをディレクトリに移したらグループが半分しか出てこなかった
한국어 원문으로 표시합니다.
목표
인벤토리 소스가 여러 개일 때 무엇이 합쳐지고 무엇이 이기는지를 하나씩 실측합니다. 마지막에는 '이 변수는 결국 어느 값이 이기는가' 를 답해 주는 도구를 직접 만듭니다.
왜 중요한가
인벤토리가 파일 하나일 때는 읽으면 압니다. 그런데 파일 하나로 끝나는 조직은 드뭅니다 — 공통 호스트는 플랫폼 팀이, 서비스별 호스트는 각 팀이 관리하고, 운영과 스테이징은 파일을 나누며, 클라우드 자원은 동적 소스가 만들어 냅니다. 그러면 사고가 두 종류로 납니다. 같은 그룹 이름 때문에 엉뚱한 서버가 대상에 들어오는 것, 그리고 옛 파일이나 새 group_vars 때문에 엉뚱한 값이 이기는 것입니다. 둘 다 파일을 읽어 추론하는 방식으로는 막지 못합니다. 막는 방법은 하나뿐입니다 — 합쳐진 결과를 실행 전에 기계에게 묻는 것. 이 실습은 그 질문을 여덟 번 던지고, 마지막에 그 질문을 대신 던져 주는 도구를 남깁니다.
단계
/root/ans/inv/src/base.ini에web그룹(web1,web2)과[web:vars] tier=base를,/root/ans/inv/src/extra.ini에web그룹(web3) ·db그룹(db1) ·[web:vars] tier=extra를 적으세요. 모든 호스트는ansible_host=127.0.0.1이고base.ini의[all:vars]에ansible_port=2222와ansible_user=root를 둡니다.-i를 두 번 주어(base.ini먼저) 합쳐진 결과를/root/ans/inv/out/merged.json에, 그래프를/root/ans/inv/out/merged-graph.txt에 저장하고, 합쳐진 뒤web1의tier가 무엇인지를/root/ans/inv/out/two-sources.txt에tier=<값>한 줄로 적으세요./root/ans/inv/dir/을 만들고 1단계의 두 파일과 같은 내용을 확장자 없이10-base와20-extra로 넣으세요. 거기에/root/ans/inv/dir/30-late.ini를 하나 더 두는데, 이 파일은web그룹에web9를 넣고[web:vars] tier=late를 적습니다.-i /root/ans/inv/dir로 합쳐진 결과를/root/ans/inv/out/dir-merged.json에 저장하고,/root/ans/inv/out/dir-order.txt에 두 줄을 적으세요 — 첫 줄은web1의tier를tier=<값>으로, 둘째 줄은 디렉터리를 훑을 때 건너뛰어진 파일 이름을skipped=<파일이름>으로./root/ans/inv/dir/group_vars/all.yml에pool: gv-all과owner: platform을,/root/ans/inv/dir/group_vars/web.yml에pool: gv-web을,/root/ans/inv/dir/host_vars/web1.yml에pool: hv-web1을 적으세요. 그리고/root/ans/inv/out/levels.json에 세 호스트가 최종적으로 갖는pool값을web1·web2·db1세 키의 JSON 으로 적으세요./root/ans/inv/dir/20-extra의[web:vars]에region=inline-extra를 더하고,/root/ans/inv/dir/group_vars/web.yml에region: file-web을 더하세요. 그리고/root/ans/inv/out/inline-vs-file.txt에 두 줄을 적으세요 — 첫 줄은web2가 최종적으로 갖는 값을region=<값>으로, 둘째 줄은 그 값이 어디서 왔는지를source=inline또는source=group_vars중 하나로./root/ans/inv/prio/hosts.ini를 만드세요 — 호스트app1이alpha와zulu두 그룹에 모두 속하고,[alpha:vars]에stack=from-alpha,[zulu:vars]에stack=from-zulu를 적습니다. 그리고[alpha:vars]에ansible_group_priority=10을 더해alpha가 이기게 하세요./root/ans/inv/out/priority-before.txt에는 그 줄이 없을 때 이기는 값을,/root/ans/inv/out/priority-after.txt에는 그 줄이 있을 때 이기는 값을 각각stack=<값>한 줄로 적으세요./root/ans/inv/tree/hosts.ini를 만드세요 —web그룹에web1,db그룹에db1을 넣고 두 그룹을zprod의 자식으로 묶습니다.[zprod:vars]에env=from-zprod와owner=platform을,[web:vars]에env=from-web을 적으세요. 그리고/root/ans/inv/out/children.txt에 세 줄을 사전순 그대로 적으세요 —db1.env=<값>·web1.env=<값>·web1.owner=<값>./root/ans/inv/pat/hosts.ini를 만드세요 —web그룹에web1·web2·web3,db그룹에db1, 두 그룹을prod의 자식으로 묶고,staging그룹에web3와db1을 넣습니다. 그리고web:&prod:!staging패턴에 맞는 호스트 이름만 한 줄에 하나씩 사전순으로/root/ans/inv/out/pattern-hosts.txt에 저장하고, 그래프를/root/ans/inv/out/pat-graph.txt에 저장하세요./root/ans/inv/whowins.sh를 쓰세요. 인자를 셋 받습니다 — 인벤토리 경로 · 호스트 이름 · 변수 이름. 그 호스트가 최종적으로 갖는 값을<변수>=<값>한 줄로 내고, 그 변수가 정의돼 있지 않으면 표준 오류에 까닭을 적고 0 이 아닌 값으로 끝나야 합니다. 그 도구로/root/ans/inv/out/merge-report.txt를 네 줄로 만드세요 —dir의web1의tier,dir의web1의pool,dir의web2의region,prio/hosts.ini의app1의stack순서입니다.
참고
- 작업 디렉터리는
/root/ans/inv입니다. 이 실습은 플레이북을 돌리지 않습니다 — 인벤토리를 해석하는 명령만 씁니다. - 세 가지 물음:
--graph는 구조를,--list는 전체를 JSON 으로,--host는 한 호스트의 최종 변수를 답합니다. - 흔한 실수: 디렉터리 안에 확장자가 붙은 인벤토리 파일을 두고 왜 안 읽히는지 몰라 헤매는 것.
- 흔한 실수: 값을 파일에서 눈으로 찾아 적는 것. 소스가 둘 이상이면 눈으로는 틀립니다.
- 흔한 실수: 패턴의 결과가 비어 있는데도 오류가 아니라서 아무 대상 없이 초록불로 끝나는 것.
- 인벤토리 만들기 · 호스트 패턴 · ansible-inventory 명령 · 변수 사용하기 · 설정 항목 목록
소스를 두 개 주면 무엇이 합쳐지나
/root/ans/inv/src/base.ini 에 web 그룹(web1, web2)과 [web:vars] tier=base 를, /root/ans/inv/src/extra.ini 에 web 그룹(web3) · db 그룹(db1) · [web:vars] tier=extra 를 적으세요. 모든 호스트는 ansible_host=127.0.0.1 이고 base.ini 의 [all:vars] 에 ansible_port=2222 와 ansible_user=root 를 둡니다. -i 를 두 번 주어(base.ini 먼저) 합쳐진 결과를 /root/ans/inv/out/merged.json 에, 그래프를 /root/ans/inv/out/merged-graph.txt 에 저장하고, 합쳐진 뒤 web1 의 tier 가 무엇인지를 /root/ans/inv/out/two-sources.txt 에 tier=<값> 한 줄로 적으세요.
호스트와 그룹은 합집합이 되고 같은 이름의 변수는 나중에 읽힌 소스가 이깁니다. 합쳐진 결과를 보는 명령은 --list 와 --graph 두 가지이고, 한 호스트의 최종 변수만 보려면 --host 를 씁니다. 값을 짐작해서 적지 말고 실제로 물어서 적으세요 — 채점기도 같은 방법으로 다시 계산해 대조합니다.
디렉터리를 주면 파일 이름이 순서를 정한다
/root/ans/inv/dir/ 을 만들고 1단계의 두 파일과 같은 내용을 확장자 없이 10-base 와 20-extra 로 넣으세요. 거기에 /root/ans/inv/dir/30-late.ini 를 하나 더 두는데, 이 파일은 web 그룹에 web9 를 넣고 [web:vars] tier=late 를 적습니다. -i /root/ans/inv/dir 로 합쳐진 결과를 /root/ans/inv/out/dir-merged.json 에 저장하고, /root/ans/inv/out/dir-order.txt 에 두 줄을 적으세요 — 첫 줄은 web1 의 tier 를 tier=<값> 으로, 둘째 줄은 디렉터리를 훑을 때 건너뛰어진 파일 이름을 skipped=<파일이름> 으로.
디렉터리 안의 파일은 이름 순서대로 읽힙니다. 그런데 셋 중 하나는 읽히지 않습니다 — 그 파일이 넣으려던 호스트가 결과에 있는지 보면 바로 알 수 있습니다. 설정 항목 inventory_ignore_extensions 의 기본 목록을 ansible-config list 로 확인해 보세요. 파일 이름만 적습니다(경로가 아닙니다).
그룹 변수와 호스트 변수가 만드는 세 층
/root/ans/inv/dir/group_vars/all.yml 에 pool: gv-all 과 owner: platform 을, /root/ans/inv/dir/group_vars/web.yml 에 pool: gv-web 을, /root/ans/inv/dir/host_vars/web1.yml 에 pool: hv-web1 을 적으세요. 그리고 /root/ans/inv/out/levels.json 에 세 호스트가 최종적으로 갖는 pool 값을 web1·web2·db1 세 키의 JSON 으로 적으세요.
이 디렉터리들은 인벤토리 옆에 있어야 읽힙니다. 넓은 것부터 좁은 것 순서로 덮이고 좁은 것이 이깁니다 — 모두에게 걸리는 것, 그 그룹에만 걸리는 것, 그 호스트에만 걸리는 것. 세 값을 손으로 추론하지 말고 호스트마다 물어서 적으세요.
인벤토리 파일 안의 그룹 변수가 가장 약하다
/root/ans/inv/dir/20-extra 의 [web:vars] 에 region=inline-extra 를 더하고, /root/ans/inv/dir/group_vars/web.yml 에 region: file-web 을 더하세요. 그리고 /root/ans/inv/out/inline-vs-file.txt 에 두 줄을 적으세요 — 첫 줄은 web2 가 최종적으로 갖는 값을 region=<값> 으로, 둘째 줄은 그 값이 어디서 왔는지를 source=inline 또는 source=group_vars 중 하나로.
같은 디렉터리에 있어도 두 자리의 세기가 다릅니다. 공식 우선순위 목록에서 inventory file group vars 와 inventory group_vars 중 어느 쪽이 아래에 있는지가 답입니다. web1 이 아니라 web2 로 물어야 호스트 변수에 가려지지 않습니다.
같은 깊이의 그룹은 사전순, 그것을 뒤집는 손잡이
/root/ans/inv/prio/hosts.ini 를 만드세요 — 호스트 app1 이 alpha 와 zulu 두 그룹에 모두 속하고, [alpha:vars] 에 stack=from-alpha, [zulu:vars] 에 stack=from-zulu 를 적습니다. 그리고 [alpha:vars] 에 ansible_group_priority=10 을 더해 alpha 가 이기게 하세요. /root/ans/inv/out/priority-before.txt 에는 그 줄이 없을 때 이기는 값을, /root/ans/inv/out/priority-after.txt 에는 그 줄이 있을 때 이기는 값을 각각 stack=<값> 한 줄로 적으세요.
손잡이가 없을 때의 기본 규칙은 이름입니다 — 사전순으로 병합되고 뒤가 이깁니다. 그 값을 확인하려면 그 줄을 뺀 사본을 잠깐 만들어 물어보면 됩니다(원본은 그대로 두세요). 이 변수는 인벤토리 소스 안에 적어야 듣습니다. group_vars 파일에 적으면 아무 일도 일어나지 않습니다.
자식 그룹이 부모 그룹을 이긴다
/root/ans/inv/tree/hosts.ini 를 만드세요 — web 그룹에 web1, db 그룹에 db1 을 넣고 두 그룹을 zprod 의 자식으로 묶습니다. [zprod:vars] 에 env=from-zprod 와 owner=platform 을, [web:vars] 에 env=from-web 을 적으세요. 그리고 /root/ans/inv/out/children.txt 에 세 줄을 사전순 그대로 적으세요 — db1.env=<값> · web1.env=<값> · web1.owner=<값>.
부모 이름을 일부러 사전순으로 뒤에 오게 지었습니다 — 그래야 이긴 이유가 이름이 아니라 깊이라는 것이 드러납니다. 부모는 넓고 자식은 좁으며 언제나 좁은 것이 이깁니다. 자식이 정의하지 않은 변수는 부모의 값이 그대로 내려옵니다. 세 값 모두 물어서 적으세요.
패턴으로 대상을 좁히고 눈으로 확인하기
/root/ans/inv/pat/hosts.ini 를 만드세요 — web 그룹에 web1·web2·web3, db 그룹에 db1, 두 그룹을 prod 의 자식으로 묶고, staging 그룹에 web3 와 db1 을 넣습니다. 그리고 web:&prod:!staging 패턴에 맞는 호스트 이름만 한 줄에 하나씩 사전순으로 /root/ans/inv/out/pattern-hosts.txt 에 저장하고, 그래프를 /root/ans/inv/out/pat-graph.txt 에 저장하세요.
패턴 기호는 합집합·교집합·차집합 셋입니다. 대상 목록만 뽑아 주는 옵션이 있는데, 그 출력의 첫 줄은 개수라 호스트 이름이 아닙니다. 공백도 함께 들어오니 다듬어 저장하세요. 차집합은 결과가 비어도 오류가 아니므로 실행 전에 눈으로 세어 보는 습관이 중요합니다.
이 변수는 결국 어느 값이 이기는가 — 도구로 만들기
/root/ans/inv/whowins.sh 를 쓰세요. 인자를 셋 받습니다 — 인벤토리 경로 · 호스트 이름 · 변수 이름. 그 호스트가 최종적으로 갖는 값을 <변수>=<값> 한 줄로 내고, 그 변수가 정의돼 있지 않으면 표준 오류에 까닭을 적고 0 이 아닌 값으로 끝나야 합니다. 그 도구로 /root/ans/inv/out/merge-report.txt 를 네 줄로 만드세요 — dir 의 web1 의 tier, dir 의 web1 의 pool, dir 의 web2 의 region, prio/hosts.ini 의 app1 의 stack 순서입니다.
파일을 grep 해서 답하면 group_vars 도 그룹 우선순위도 자식 그룹도 보지 못합니다. 합쳐진 결과를 내놓는 명령에게 직접 물으세요. 값이 없는 것과 값이 빈 문자열인 것을 구분하려면 키가 있는지를 먼저 확인해야 합니다. 채점기는 자기가 만든 인벤토리로도 이 도구를 돌려 보고, 정의되지 않은 변수를 물었을 때 어떻게 끝나는지도 봅니다.