一部だけ回したのに、なぜ最終状態ではないのか
한국어 원문으로 표시합니다.
목표
태그로 플레이북의 일부만 돌리는 법을 익히고, 그 편의가 어떤 위험과 짝을 이루는지 직접 재 봅니다. 마지막에는 어떤 태그 선택이 완주할 수 있는지 판정하는 도구를 만듭니다.
왜 중요한가
플레이북은 자랍니다. 태스크가 여든 개인데 설정 한 줄만 고치고 싶은 날이 오고, 태그는 그 자리에 놓인 도구입니다. 문법이 쉬워서 사람들은 배운 다음 날부터 운영에서 씁니다. 사고는 거기서 납니다 — 태그는 실행을 자르는 칼인데, 플레이북은 잘려도 되게 설계되어 있지 않기 때문입니다. 앞 태스크가 만든 디렉터리를 뒤 태스크가 쓰고, 설정을 바꾼 태스크가 핸들러를 부릅니다. 일부만 고르면 그 연결이 끊어지고, 운이 나쁘면 실패조차 하지 않은 채 반쯤 맞는 상태가 남습니다. 그래서 이 실습은 태그 문법을 익히는 절반과, 자른 실행이 무엇을 보장하지 못하는지 눈으로 보는 절반으로 되어 있습니다.
단계
/root/anstags/hosts.ini를 만드세요 —[web]에web1(ansible_host=127.0.0.1,ansible_port=2222),[all:vars]로ansible_user=root./root/anstags/site.yml을 만드세요: 플레이 변수app_env(기본lab), 핸들러reload app(/root/anstags/out/reload.marker에reloaded한 줄을 씁니다), 그리고 태스크 넷입니다 —/root/anstags/app디렉터리를 0755 로 만드는배포 자리를 만든다(태그setup),/root/anstags/app/app.conf에env=<app_env>한 줄을 0644 로 쓰고 핸들러를notify하는설정을 쓴다(태그config),release_id를r-2026으로 정하는배포 번호를 정한다(태그prep, set_fact),/root/anstags/app/release.txt에release_id를 0644 로 쓰는배포 번호를 기록한다(태그deploy). 전체를 한 번 수렴시켜 출력을/root/anstags/out/full.txt에 저장하고, 이어서--tags config로 한 번 더 돌려/root/anstags/out/config.txt에 저장하세요.ansible-playbook -i hosts.ini site.yml --list-tags의 출력을/root/anstags/out/list-tags.txt에 저장하세요. 그다음--list-tasks --tags config의 출력을/root/anstags/out/list-config.txt에 저장하세요. 두 번째 파일에는설정을 쓴다는 있고배포 번호를 기록한다는 없어야 합니다.--skip-tags prep,deploy로 플레이북을 실제로 돌려 출력을/root/anstags/out/skip.txt에 저장하세요. 출력에는배포 자리를 만든다와설정을 쓴다의TASK [...]줄이 있고,배포 번호를 정한다와배포 번호를 기록한다의 줄은 없어야 합니다. 그리고--list-tasks --skip-tags prep,deploy의 출력을/root/anstags/out/list-skip.txt에 저장하세요.- 플레이 자체에 태그
platform을 거세요. 그리고설정을 검증한다라는 이름의 블록을 태스크 끝에 더하고 그 블록에 태그verify를 다세요 — 블록 안에는 태그를 달지 않은 태스크 둘이 들어갑니다:/root/anstags/app/app.conf의 상태를 읽어conf_stat으로 register 하는설정 파일의 상태를 읽는다, 그 결과로 파일이 있는지 단언하는설정 파일이 있는지 단언한다.--list-tasks전체 출력을/root/anstags/out/inherit.txt에,--list-tasks --tags verify출력을/root/anstags/out/block.txt에 저장하세요. - 태스크 둘을 더하세요.
어떤 선택에서도 남기는 표식은/root/anstags/out/always.marker에always한 줄을 0644 로 쓰고 태그는always하나입니다.함부로 돌면 안 되는 태스크는/root/anstags/out/never.marker에danger한 줄을 0644 로 쓰고 태그는never와danger둘입니다.--tags config로 실제로 돌려 출력을/root/anstags/out/always.txt에 저장하세요 —어떤 선택에서도 남기는 표식이 함께 돌아야 합니다. 그리고--list-tasks --tags danger출력을/root/anstags/out/danger.txt에 저장하세요./root/anstags/out/never.marker는 이 실습이 끝날 때까지 만들어지면 안 됩니다. /root/anstags/tasks/common.yml을 만드세요 — 태스크 둘입니다.공통 점검 하나는common-one을 출력하고 태그가 없습니다.공통 점검 둘은common-two를 출력하고 태그deep을 갖습니다. 그다음site.yml끝에 태스크 둘을 더하세요 —import 로 공통 점검을 끌어온다(import_tasks로 그 파일을 끌어오고 태그imported)와include 로 공통 점검을 끌어온다(include_tasks로 같은 파일을 끌어오고 태그included).--list-tasks --tags imported출력을/root/anstags/out/reuse-import.txt에,--list-tasks --tags included출력을/root/anstags/out/reuse-include.txt에 저장하세요.- 먼저
/root/anstags/out/reload.marker를 지우세요. 그다음--start-at-task "배포 번호를 정한다" -e app_env=prod로 플레이북을 실제로 돌리고 출력을/root/anstags/out/startat.txt에 저장하세요. 끝난 뒤/root/anstags/app/app.conf의 내용과/root/anstags/out/reload.marker의 존재 여부를 이어서/root/anstags/out/aftermath.txt에 남기세요 — 첫 줄은conf=<app.conf 의 env 줄>, 둘째 줄은marker=<yes 또는 no>입니다. /root/anstags/tag-check.sh <태그목록>을 만드세요. 그 선택으로site.yml을 점검 모드로 돌려 보고, 정상으로 끝나면 첫 줄에SAFE <태그목록>을 내고 0 으로, 끝나지 않으면 첫 줄에UNSAFE <태그목록> rc=<종료코드>를 내고 1 로 끝냅니다. 그다음./tag-check.sh deploy와./tag-check.sh prep,deploy를 차례로 돌려 두 줄을/root/anstags/out/tagcheck.txt에 이어 저장하세요. 앞은 UNSAFE, 뒤는 SAFE 여야 합니다.
참고
- 먼저 1단계에서 인벤토리와 플레이북을 만들고 전체를 한 번 수렴시키세요. 태그는 그다음부터 쓰는 도구입니다.
- 명령 힌트:
--list-tags는 어떤 태그가 있는지,--list-tasks [--tags X]는 그 선택으로 무엇이 돌지 알려 줍니다. 둘 다 대상에 붙지 않아 운영에서도 안전합니다. - 명령 힌트: 태스크 이름에 공백이 있으므로
--start-at-task '배포 번호를 정한다'처럼 따옴표로 감쌉니다. - 흔한 실수:
--tags로 걸러진 태스크가skipping으로 표시될 것이라고 기대하는 것. 걸러진 태스크는 출력에서 통째로 빠지고 요약의 skipped 도 0 으로 남습니다. - 흔한 실수:
include_tasks에 태그를 달고 그 태그로 돌린 뒤 '아무 일도 안 일어난다' 고 당황하는 것. - 흔한 실수: 중간부터 돌린 실행이
failed=0으로 끝난 것을 보고 배포가 끝났다고 판단하는 것. --step은 사람이 키를 눌러야 진행하는 대화형 모드라 이 실습에서는 다루지 않습니다. 롤에 태그를 거는 이야기도 롤 자체가 다음 코스의 주제라 여기서는 다루지 않습니다.- 태그로 일부만 실행하기 · 중간부터 실행하기 · import 와 include · 핸들러 · ansible-playbook 옵션
태스크에 태그를 달고 전체를 한 번 수렴시킨다
/root/anstags/hosts.ini 를 만드세요 — [web] 에 web1(ansible_host=127.0.0.1, ansible_port=2222), [all:vars] 로 ansible_user=root. /root/anstags/site.yml 을 만드세요: 플레이 변수 app_env(기본 lab), 핸들러 reload app(/root/anstags/out/reload.marker 에 reloaded 한 줄을 씁니다), 그리고 태스크 넷입니다 — /root/anstags/app 디렉터리를 0755 로 만드는 배포 자리를 만든다(태그 setup), /root/anstags/app/app.conf 에 env=<app_env> 한 줄을 0644 로 쓰고 핸들러를 notify 하는 설정을 쓴다(태그 config), release_id 를 r-2026 으로 정하는 배포 번호를 정한다(태그 prep, set_fact), /root/anstags/app/release.txt 에 release_id 를 0644 로 쓰는 배포 번호를 기록한다(태그 deploy). 전체를 한 번 수렴시켜 출력을 /root/anstags/out/full.txt 에 저장하고, 이어서 --tags config 로 한 번 더 돌려 /root/anstags/out/config.txt 에 저장하세요.
태그는 이미 한 번 전체가 돌아간 시스템에 쓰는 도구입니다. 새 서버에 --tags config 만 걸면 디렉터리가 없어 실패하니, 첫 수렴은 반드시 전체로 합니다 — 그래서 이 단계는 전체 실행이 먼저입니다. 태그는 태스크에 tags: [이름] 으로 답니다. --tags config 로 돌린 출력에서 나머지 세 태스크의 TASK [...] 줄이 아예 없는 것을 확인하세요 — 걸러진 태스크는 건너뛴 것으로 표시되는 게 아니라 출력에서 통째로 빠집니다.
돌리기 전에 무엇이 돌지 목록으로 먼저 본다
ansible-playbook -i hosts.ini site.yml --list-tags 의 출력을 /root/anstags/out/list-tags.txt 에 저장하세요. 그다음 --list-tasks --tags config 의 출력을 /root/anstags/out/list-config.txt 에 저장하세요. 두 번째 파일에는 설정을 쓴다 는 있고 배포 번호를 기록한다 는 없어야 합니다.
이 두 도구는 대상에 붙지도 않고 아무것도 바꾸지도 않습니다 — 운영에서 태그를 처음 쓸 때 가장 먼저 걸어야 하는 것이 이것입니다. --list-tags 는 '이 플레이북에 어떤 태그가 있는가' 를 알려 주고, --list-tasks 는 '이번 선택으로 무엇이 돌 것인가' 를 순서대로 알려 줍니다. --list-tasks 에 --tags 를 함께 주면 선택이 그대로 반영됩니다. 태스크마다 붙은 태그 목록도 같은 줄에 나오니 상속을 눈으로 확인할 때도 씁니다.
빼는 쪽으로 고른다
--skip-tags prep,deploy 로 플레이북을 실제로 돌려 출력을 /root/anstags/out/skip.txt 에 저장하세요. 출력에는 배포 자리를 만든다 와 설정을 쓴다 의 TASK [...] 줄이 있고, 배포 번호를 정한다 와 배포 번호를 기록한다 의 줄은 없어야 합니다. 그리고 --list-tasks --skip-tags prep,deploy 의 출력을 /root/anstags/out/list-skip.txt 에 저장하세요.
고르는 방법은 두 가지입니다 — 넣을 것을 부르거나(--tags) 뺄 것을 부르거나(--skip-tags). 태그가 늘어나면 뒤쪽이 편할 때가 많습니다. 둘을 함께 줄 수도 있는데, 그때는 뺀 것이 이깁니다. 목록은 쉼표로 잇습니다. 돌리기 전에 목록으로 먼저 확인하는 습관은 --skip-tags 에서도 똑같이 값어치가 있습니다.
블록과 플레이에 건 태그가 아래로 흐른다
플레이 자체에 태그 platform 을 거세요. 그리고 설정을 검증한다 라는 이름의 블록을 태스크 끝에 더하고 그 블록에 태그 verify 를 다세요 — 블록 안에는 태그를 달지 않은 태스크 둘이 들어갑니다: /root/anstags/app/app.conf 의 상태를 읽어 conf_stat 으로 register 하는 설정 파일의 상태를 읽는다, 그 결과로 파일이 있는지 단언하는 설정 파일이 있는지 단언한다. --list-tasks 전체 출력을 /root/anstags/out/inherit.txt 에, --list-tasks --tags verify 출력을 /root/anstags/out/block.txt 에 저장하세요.
태그는 붙은 자리에서 아래로 흐릅니다. 블록에 달면 블록 안 모든 태스크가 갖고, 플레이에 달면 그 플레이의 모든 태스크가 갖습니다. 그 사실은 --list-tasks 의 각 줄 끝에 붙는 TAGS: [...] 에 그대로 드러납니다 — 블록 안 태스크에 아무것도 안 달았는데 태그가 둘 보이면 상속이 눈에 보인 것입니다. 상태를 읽는 모듈과 전제 조건을 단언하는 모듈은 이름이 떠오르지 않으면 ansible-doc -l ansible.builtin | grep -iE 'stat|assert' 로 찾으세요.
언제나 도는 태스크와 절대 안 도는 태스크
태스크 둘을 더하세요. 어떤 선택에서도 남기는 표식 은 /root/anstags/out/always.marker 에 always 한 줄을 0644 로 쓰고 태그는 always 하나입니다. 함부로 돌면 안 되는 태스크 는 /root/anstags/out/never.marker 에 danger 한 줄을 0644 로 쓰고 태그는 never 와 danger 둘입니다. --tags config 로 실제로 돌려 출력을 /root/anstags/out/always.txt 에 저장하세요 — 어떤 선택에서도 남기는 표식 이 함께 돌아야 합니다. 그리고 --list-tasks --tags danger 출력을 /root/anstags/out/danger.txt 에 저장하세요. /root/anstags/out/never.marker 는 이 실습이 끝날 때까지 만들어지면 안 됩니다.
특수 태그는 다섯 개입니다 — always·never·tagged·untagged·all. 앞의 둘이 실무에서 쓰입니다. always 는 어떤 선택에서도 빠지면 안 되는 태스크(공통 변수 설정, 사실 수집)에 걸고, never 는 코드로는 남기되 손이 미끄러져 돌면 안 되는 태스크에 겁니다 — 주석 처리는 언젠가 풀리지만 never 는 풀리지 않습니다. never 가 붙은 태스크를 부르려면 함께 붙인 다른 이름표를 직접 불러야 합니다. 그 태스크는 --list-tasks 를 그냥 돌렸을 때 목록에 나오지도 않습니다 — 그것부터 확인해 보세요.
import 는 태그를 물려주고 include 는 물려주지 않는다
/root/anstags/tasks/common.yml 을 만드세요 — 태스크 둘입니다. 공통 점검 하나 는 common-one 을 출력하고 태그가 없습니다. 공통 점검 둘 은 common-two 를 출력하고 태그 deep 을 갖습니다. 그다음 site.yml 끝에 태스크 둘을 더하세요 — import 로 공통 점검을 끌어온다(import_tasks 로 그 파일을 끌어오고 태그 imported)와 include 로 공통 점검을 끌어온다(include_tasks 로 같은 파일을 끌어오고 태그 included). --list-tasks --tags imported 출력을 /root/anstags/out/reuse-import.txt 에, --list-tasks --tags included 출력을 /root/anstags/out/reuse-include.txt 에 저장하세요.
둘은 같은 파일을 끌어오지만 언제 끌어오는지가 다릅니다. 하나는 플레이북을 읽는 시점에 그 자리에 펼쳐지고(정적), 다른 하나는 실행 중에 비로소 끼어듭니다(동적). 그 차이가 태그 상속을 가릅니다 — 미리 펼쳐진 쪽은 태그가 태스크 하나하나에 붙고, 실행 중에 끼어드는 쪽은 문 자체에만 붙습니다. 두 목록 파일을 나란히 놓고 끌어온 파일 안의 태스크 이름이 보이는 쪽과 안 보이는 쪽을 확인하세요. 이것이 '오류 없이 아무 일도 안 일어나는' 고장의 정체입니다.
중간부터 돌리면 핸들러도 돌지 않는다
먼저 /root/anstags/out/reload.marker 를 지우세요. 그다음 --start-at-task "배포 번호를 정한다" -e app_env=prod 로 플레이북을 실제로 돌리고 출력을 /root/anstags/out/startat.txt 에 저장하세요. 끝난 뒤 /root/anstags/app/app.conf 의 내용과 /root/anstags/out/reload.marker 의 존재 여부를 이어서 /root/anstags/out/aftermath.txt 에 남기세요 — 첫 줄은 conf=<app.conf 의 env 줄>, 둘째 줄은 marker=<yes 또는 no> 입니다.
--start-at-task 는 그 이름의 태스크부터 시작합니다. 긴 플레이북이 중간에서 죽었을 때 1번부터 다시 돌리지 않으려고 쓰는 도구입니다. 그런데 앞 태스크가 만들었어야 할 것도, 앞 태스크가 보냈어야 할 notify 도 없는 채로 시작합니다 — 그래서 failed=0 으로 끝나도 시스템은 플레이북이 약속한 상태가 아닙니다. -e app_env=prod 를 함께 주었는데 설정 파일이 어떻게 되어 있는지, 핸들러 표식이 생겼는지를 직접 확인해 보세요. 이 단계의 답은 명령이 아니라 그 두 사실입니다.
어떤 태그 선택이 완주할 수 있는지 판정하는 도구
/root/anstags/tag-check.sh <태그목록> 을 만드세요. 그 선택으로 site.yml 을 점검 모드로 돌려 보고, 정상으로 끝나면 첫 줄에 SAFE <태그목록> 을 내고 0 으로, 끝나지 않으면 첫 줄에 UNSAFE <태그목록> rc=<종료코드> 를 내고 1 로 끝냅니다. 그다음 ./tag-check.sh deploy 와 ./tag-check.sh prep,deploy 를 차례로 돌려 두 줄을 /root/anstags/out/tagcheck.txt 에 이어 저장하세요. 앞은 UNSAFE, 뒤는 SAFE 여야 합니다.
배포 번호를 기록한다 는 배포 번호를 정한다 가 정한 값을 씁니다. 앞을 빼고 뒤만 고르면 그 변수는 정의되지 않은 채이고, 그것이 부분 실행이 위험한 첫 번째 방식입니다. 이 도구는 그 위험을 사람의 기억이 아니라 명령으로 바꿉니다. 판정에 점검 모드를 쓰는 이유는 운영에서 그대로 돌릴 수 있어야 하기 때문입니다 — 판정하려고 진짜로 바꾸면 도구가 아니라 사고입니다. 게이트가 1 로 끝나므로 결과를 파일에 모을 때 셸이 거기서 멈추지 않게 하세요.