LabHub
배우기 러닝패스 코스

Ansible Fundamentals

See what will change before you change it

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

점검 모드와 diff 를 '플래그 두 개' 가 아니라 '태스크마다 정해 주는 행동' 으로 다룹니다. 거짓 실패를 직접 만들어 고치고, 승인에 붙일 계획 산출물과 드리프트 게이트까지 만듭니다.

왜 중요한가

운영 서버에 플레이북을 처음 거는 날, 이 플레이북이 스무 대에 무엇을 할지 아무도 모릅니다. --check 는 그 자리에 놓인 도구인데 두 번 배신합니다 — 점검 모드가 깨끗했는데 진짜 실행이 실패하고, 점검 모드가 빨갛게 죽었는데 진짜 실행은 멀쩡합니다. 두 배신의 원인은 같습니다. 점검 모드는 실제로 실행하지 않은 채 결과를 추측하는 모드이고, 추측의 품질은 모듈마다 다릅니다. 그래서 점검 모드를 믿을 수 있게 만드는 일은 플래그를 붙이는 일이 아니라 태스크마다 '너는 점검 모드에서 어떻게 행동해라' 를 정해 주는 일입니다. 그 일을 마치고 나면 dry run 은 배포 전에 보는 그림을 넘어, 승인 근거이자 드리프트를 잡는 게이트가 됩니다.

단계

  1. /root/anschk/hosts.ini 를 만드세요 — [web]web1(ansible_host=127.0.0.1, ansible_port=2222), [all:vars]ansible_user=root. 그다음 /root/anschk/site.yml 을 만드세요: 플레이 변수 app_env(기본 labapp_dir_mode(기본 "0755"motd_owner(기본 unset) 를 두고, 태스크 넷으로 /root/anschk/app 디렉터리를 app_dir_mode 권한으로 만들고, /root/anschk/app/app.confenv=<app_env>listen=8080 두 줄을 권한 0644 로 쓰고, /root/anschk/app/motdWelcome=labhubOwner=unset 두 줄을 권한 0644 로 쓰고, 마지막으로 motd^Owner= 줄을 Owner=<motd_owner> 로 맞춥니다(그 파일이 없으면 만들도록 create 를 켜세요 — 점검 모드에서 앞 태스크가 파일을 실제로 만들지 않기 때문입니다). 아직 수렴시키지 말고 --check --diff 로만 돌려 출력을 /root/anschk/out/check1.txt 에 저장하세요.
  2. site.yml--diff 와 함께 기본값으로 한 번 실제 실행해 출력을 /root/anschk/out/converge.txt 에 저장하세요. 그다음 값 셋을 명령줄로 덮어써서 --check --diff 로 다시 돌리세요 — app_env=stage, app_dir_mode=0750, motd_owner=platform-team. 그 출력을 /root/anschk/out/diff.txt 에 저장하세요. 실제 파일은 그대로 env=lab·권한 0755·Owner=unset 이어야 합니다.
  3. /root/anschk/probe.yml 을 만드세요. ansible.builtin.commandgetent passwd root 를 실행해 pw 로 register 하고(changed_when: false), 그 값과 지금이 점검 모드인지를 ansible.builtin.debugcheck_mode=<참거짓> pwline=<읽은 값> 형태로 한 줄에 냅니다. 이 플레이북을 --check 로 돌린 출력을 표준 오류까지 /root/anschk/out/probe-check.txt 에 저장하되, 점검 모드에서도 실제 값이 찍혀야 합니다. 건너뛰어지는 태스크가 하나도 없어야 합니다.
  4. /root/anschk/patch.yml 을 만드세요 — 태스크 둘입니다. 하나는 /root/anschk/app/fresh.confenv=lablisten=9090 두 줄을 권한 0644 로 쓰고, 다른 하나는 그 파일의 ^listen= 줄을 listen=9443 으로 고칩니다. 먼저 이 상태 그대로 --check 로 돌려 실패 출력을 표준 오류까지 /root/anschk/out/false-failure.txt 에 저장하세요. 그다음 뒤 태스크에 점검 모드에서는 건너뛰는 가드를 달고 다시 --check 로 돌려 그 출력을 /root/anschk/out/false-fixed.txt 에 저장하세요. 두 번 모두 점검 모드이므로 /root/anschk/app/fresh.conf 는 끝까지 만들어지면 안 됩니다.
  5. /root/anschk/dryrun.yml 을 만드세요. /root/anschk/app/never.conffeature=on 한 줄을 권한 0644 로 쓰는 태스크 하나인데, --check 없이 평소대로 돌려도 절대 파일을 만들지 않고 바뀔 것이라는 보고만 해야 합니다. 같은 플레이북에 태스크를 하나 더 두세요 — /root/anschk/app/dryrun-ran.txtreal-run 한 줄을 권한 0644 로 쓰는 평범한 태스크입니다. 이 플레이북을 아무 플래그 없이 실행해 출력을 /root/anschk/out/dryrun.txt 에 저장하세요. 끝나면 /root/anschk/app/dryrun-ran.txt 는 있고 /root/anschk/app/never.conf 는 없어야 합니다.
  6. /root/anschk/undef.yml 을 만드세요 — 정의되지 않은 변수(missing_var)를 내용에 쓰는 copy 태스크 하나입니다. /root/anschk/badmod.yml 도 만드세요 — 모듈 이름을 ansible.builtin.coppy 로 오타 낸 태스크 하나입니다. 그다음 /root/anschk/gates.sh 를 만들어 두 플레이북 각각에 --syntax-check·--list-tasks·--check 를 차례로 걸고 종료 코드만 /root/anschk/out/gates.txt 에 한 줄씩 적으세요: <파일이름> syntax-check=<코드> list-tasks=<코드> check=<코드>. 스크립트를 실행해 파일을 남기세요.
  7. /root/anschk/plan.sh 를 만드세요. ansible.posix.json 을 stdout 콜백으로 지정해 site.yml--check --diff -e app_env=stage 로 돌리고 그 JSON 전체를 /root/anschk/out/plan.raw.json 에 저장한 뒤, 거기서 바뀔 태스크의 이름만 뽑아 JSON 배열로 /root/anschk/out/plan.json 에 저장합니다. 스크립트를 실행해 두 파일을 남기세요. 배열에는 이름이 정확히 하나만 들어 있어야 합니다.
  8. /root/anschk/drift-gate.sh 를 만드세요. site.yml 을 점검 모드로 돌려 요약의 changed 가 0 이면 첫 줄이 CLEAN 으로 시작하는 메시지를 내고 0 으로 끝나고, 1 이상이면 첫 줄이 DRIFT 로 시작하는 메시지를 내고 1 로 끝납니다. 점검 모드 실행 자체가 실패하면 DRIFT-UNKNOWN 으로 시작하는 메시지를 내고 1 로 끝냅니다. 스크립트에 준 인자는 그대로 ansible-playbook 에 넘어가야 합니다. 수렴된 상태에서 인자 없이 돌린 출력을 /root/anschk/out/gate-clean.txt 에, -e app_env=stage 를 주고 돌린 출력을 /root/anschk/out/gate-dirty.txt 에 저장하세요.

참고

만들자마자 점검 모드로 먼저 본다

/root/anschk/hosts.ini 를 만드세요 — [web]web1(ansible_host=127.0.0.1, ansible_port=2222), [all:vars]ansible_user=root. 그다음 /root/anschk/site.yml 을 만드세요: 플레이 변수 app_env(기본 labapp_dir_mode(기본 "0755"motd_owner(기본 unset) 를 두고, 태스크 넷으로 /root/anschk/app 디렉터리를 app_dir_mode 권한으로 만들고, /root/anschk/app/app.confenv=<app_env>listen=8080 두 줄을 권한 0644 로 쓰고, /root/anschk/app/motdWelcome=labhubOwner=unset 두 줄을 권한 0644 로 쓰고, 마지막으로 motd^Owner= 줄을 Owner=<motd_owner> 로 맞춥니다(그 파일이 없으면 만들도록 create 를 켜세요 — 점검 모드에서 앞 태스크가 파일을 실제로 만들지 않기 때문입니다). 아직 수렴시키지 말고 --check --diff 로만 돌려 출력을 /root/anschk/out/check1.txt 에 저장하세요.

점검 모드는 대상에 변경을 가하지 않고, 변경이 일어났을 것 같으면 그 태스크를 changed 로 보고합니다. 그래서 아직 아무것도 없는 상태에서 돌리면 만들어질 것 전부가 changed 로 나옵니다. --diff 를 함께 주면 생길 파일의 내용이 + 줄로 보이는데, 없던 파일이 생기는 diff 는 앞쪽 범위가 0 으로 찍힙니다 — 그 표시가 '이때는 정말 파일이 없었다' 는 증거로 남습니다. 출력은 표준 출력과 표준 오류를 함께 받는 편이 안전합니다.

한 번 수렴시키고 값 셋을 바꿔 세 종류의 diff 를 본다

site.yml--diff 와 함께 기본값으로 한 번 실제 실행해 출력을 /root/anschk/out/converge.txt 에 저장하세요. 그다음 값 셋을 명령줄로 덮어써서 --check --diff 로 다시 돌리세요 — app_env=stage, app_dir_mode=0750, motd_owner=platform-team. 그 출력을 /root/anschk/out/diff.txt 에 저장하세요. 실제 파일은 그대로 env=lab·권한 0755·Owner=unset 이어야 합니다.

--diff 는 점검 모드 전용이 아닙니다 — 진짜 실행에 붙이면 바꾸면서 무엇을 바꿨는지 보여 줍니다. 그 두 쓰임을 한 단계에서 나란히 봅니다. 세 값을 바꾸면 diff 도 세 종류가 나옵니다: 파일 내용이 바뀌는 diff, 파일 내용이 아니라 속성만 바뀌어 JSON 이 비교되는 diff, 그리고 한 줄만 바뀌는 diff. 명령줄 변수는 플레이 변수보다 우선하므로 -e 이름=값 을 세 번 주면 됩니다. 마지막에 실제 파일을 눈으로 확인하는 것을 잊지 마세요.

점검 모드에서 건너뛰어진 조회 태스크를 되살린다

/root/anschk/probe.yml 을 만드세요. ansible.builtin.commandgetent passwd root 를 실행해 pw 로 register 하고(changed_when: false), 그 값과 지금이 점검 모드인지를 ansible.builtin.debugcheck_mode=<참거짓> pwline=<읽은 값> 형태로 한 줄에 냅니다. 이 플레이북을 --check 로 돌린 출력을 표준 오류까지 /root/anschk/out/probe-check.txt 에 저장하되, 점검 모드에서도 실제 값이 찍혀야 합니다. 건너뛰어지는 태스크가 하나도 없어야 합니다.

점검 모드 지원 여부는 모듈이 정합니다. 명령 모듈은 그 명령이 무엇을 할지 알 수 없으니 지원하지 않고, 지원하지 않는 모듈의 태스크는 그냥 건너뛰어집니다. 건너뛰면 register 변수가 비고 뒤 태스크가 무너집니다 — 먼저 그대로 한 번 돌려 pwline= 뒤가 비는 것을 눈으로 보세요. 되살리는 키는 태스크에 붙이는 한 줄이고, 붙이는 기준은 하나입니다: 이 태스크가 대상을 바꾸지 않는다고 확신하는가.

점검 모드의 거짓 실패를 만들어 보고 가드로 고친다

/root/anschk/patch.yml 을 만드세요 — 태스크 둘입니다. 하나는 /root/anschk/app/fresh.confenv=lablisten=9090 두 줄을 권한 0644 로 쓰고, 다른 하나는 그 파일의 ^listen= 줄을 listen=9443 으로 고칩니다. 먼저 이 상태 그대로 --check 로 돌려 실패 출력을 표준 오류까지 /root/anschk/out/false-failure.txt 에 저장하세요. 그다음 뒤 태스크에 점검 모드에서는 건너뛰는 가드를 달고 다시 --check 로 돌려 그 출력을 /root/anschk/out/false-fixed.txt 에 저장하세요. 두 번 모두 점검 모드이므로 /root/anschk/app/fresh.conf 는 끝까지 만들어지면 안 됩니다.

점검 모드에서는 앞 태스크가 파일을 실제로 만들지 않습니다. 그러니 그 파일이 있다고 전제한 뒤 태스크는 없는 파일을 고치려다 죽습니다 — 플레이북이 틀린 게 아니라 점검 모드의 한계입니다. 실패 메시지에 그 사실이 그대로 적혀 나오니 먼저 읽어 보세요. 고치는 흔한 방법은 뒤 태스크에 조건을 하나 다는 것이고, 지금이 점검 모드인지는 매직 변수 하나가 알려 줍니다. 앞 태스크에 check_mode: false 를 붙이는 방법도 있지만 그 순간 dry run 이 아니게 되므로 이 단계에서는 쓰지 않습니다.

실제 실행에서도 절대 바꾸지 않는 태스크를 만든다

/root/anschk/dryrun.yml 을 만드세요. /root/anschk/app/never.conffeature=on 한 줄을 권한 0644 로 쓰는 태스크 하나인데, --check 없이 평소대로 돌려도 절대 파일을 만들지 않고 바뀔 것이라는 보고만 해야 합니다. 같은 플레이북에 태스크를 하나 더 두세요 — /root/anschk/app/dryrun-ran.txtreal-run 한 줄을 권한 0644 로 쓰는 평범한 태스크입니다. 이 플레이북을 아무 플래그 없이 실행해 출력을 /root/anschk/out/dryrun.txt 에 저장하세요. 끝나면 /root/anschk/app/dryrun-ran.txt 는 있고 /root/anschk/app/never.conf 는 없어야 합니다.

앞 단계에서 쓴 키와 같은 이름의 키인데 값이 반대입니다. 그 키를 태스크에 달면 그 태스크만 언제나 dry run 으로 고정됩니다. 아직 켜면 안 되는 태스크를 플레이북에 미리 적어 두고 영향만 보고 싶을 때, 또는 위험한 태스크를 사람이 확인하기 전까지 묶어 둘 때 쓰는 자리입니다. 'changed 로 보고되는데 파일은 없다' 는 어색한 상태가 바로 이 키가 하는 일입니다.

세 도구의 종료 코드가 어디서 갈리는지 표로 세운다

/root/anschk/undef.yml 을 만드세요 — 정의되지 않은 변수(missing_var)를 내용에 쓰는 copy 태스크 하나입니다. /root/anschk/badmod.yml 도 만드세요 — 모듈 이름을 ansible.builtin.coppy 로 오타 낸 태스크 하나입니다. 그다음 /root/anschk/gates.sh 를 만들어 두 플레이북 각각에 --syntax-check·--list-tasks·--check 를 차례로 걸고 종료 코드만 /root/anschk/out/gates.txt 에 한 줄씩 적으세요: <파일이름> syntax-check=<코드> list-tasks=<코드> check=<코드>. 스크립트를 실행해 파일을 남기세요.

앞의 두 도구는 대상에 붙지도 않고 템플릿을 펼치지도 않습니다. 변수는 태스크가 실제로 점검 모드로 실행될 때 비로소 펼쳐집니다 — 그래서 두 플레이북의 결과가 서로 다른 모양으로 갈립니다. 어느 쪽이 어디서 처음 잡히는지가 이 단계의 답입니다. 종료 코드는 명령 바로 뒤에 $? 로 받습니다. 출력은 버리고 코드만 남기면 됩니다. CI 를 짤 때 이 표가 순서를 정해 줍니다 — 값싼 것부터 거는 이유가 여기 있습니다.

승인에 붙일 계획 산출물을 만든다

/root/anschk/plan.sh 를 만드세요. ansible.posix.json 을 stdout 콜백으로 지정해 site.yml--check --diff -e app_env=stage 로 돌리고 그 JSON 전체를 /root/anschk/out/plan.raw.json 에 저장한 뒤, 거기서 바뀔 태스크의 이름만 뽑아 JSON 배열로 /root/anschk/out/plan.json 에 저장합니다. 스크립트를 실행해 두 파일을 남기세요. 배열에는 이름이 정확히 하나만 들어 있어야 합니다.

화면에 흘러가는 초록·노랑 글자는 승인 근거로 남지 않습니다. 콜백을 바꾸면 실행 전체가 구조화된 JSON 하나로 나옵니다 — 환경변수 ANSIBLE_STDOUT_CALLBACK 으로 지정합니다. 어떤 콜백이 있는지는 ansible-doc -t callback -l 로 봅니다. JSON 안에서 태스크는 .plays[].tasks[] 아래에 있고, 태스크 이름은 .task.name, 호스트별 결과는 .hosts 아래에 있습니다. app_env 만 바꿨으니 바뀔 태스크는 하나뿐입니다 — 나머지는 이미 그 상태라 changed 가 아닙니다.

점검 모드를 게이트로 바꾼다

/root/anschk/drift-gate.sh 를 만드세요. site.yml 을 점검 모드로 돌려 요약의 changed 가 0 이면 첫 줄이 CLEAN 으로 시작하는 메시지를 내고 0 으로 끝나고, 1 이상이면 첫 줄이 DRIFT 로 시작하는 메시지를 내고 1 로 끝납니다. 점검 모드 실행 자체가 실패하면 DRIFT-UNKNOWN 으로 시작하는 메시지를 내고 1 로 끝냅니다. 스크립트에 준 인자는 그대로 ansible-playbook 에 넘어가야 합니다. 수렴된 상태에서 인자 없이 돌린 출력을 /root/anschk/out/gate-clean.txt 에, -e app_env=stage 를 주고 돌린 출력을 /root/anschk/out/gate-dirty.txt 에 저장하세요.

수렴이 끝났는데도 점검 모드가 바뀔 것을 찾아낸다면 누군가 손으로 고쳤거나 플레이북이 스스로 수렴하지 못한다는 뜻입니다 — 그 조건에서 실패로 끝나는 스크립트를 CI 에 걸면 드리프트가 다음 배포 전에 드러납니다. 요약 줄에서 숫자를 뽑을 때는 changed= 뒤의 수를 보면 되고, 실행이 실패해 요약이 아예 없는 경우도 따로 다뤄야 합니다 — 숫자를 못 읽었는데 조용히 통과시키면 게이트가 아니라 장식이 됩니다. 스크립트 인자를 그대로 넘기는 방법은 "$@" 입니다. 게이트가 실패로 끝나므로 출력을 저장할 때 셸이 거기서 멈추지 않게 하세요.