LabHub
배우기 러닝패스 코스

Ansible Fundamentals

It reported 37 hosts succeeded: designing for failure

LabHub 에서 이어서 보기

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

목표

한 대만 실패하게 만들어 놓은 호스트 셋짜리 인벤토리에서, 실패를 다루는 손잡이를 하나씩 켜 보고 그때마다 무엇이 멈추고 무엇이 계속 가는지를 표식 파일로 직접 확인합니다.

왜 중요한가

실패는 일어납니다. 설계해야 할 것은 실패를 없애는 일이 아니라 실패했을 때 무엇이 일어날지를 미리 정해 두는 일입니다. Ansible 의 기본값은 '실패한 호스트만 조용히 빠지고 나머지는 계속 간다' 인데, 서버를 한 대씩 독립적으로 고치는 일에는 좋은 기본값이고 40대가 한 서비스를 이루고 있다면 나쁜 기본값입니다. 그래서 이 실습은 세 가지 질문을 순서대로 다룹니다 — 이 실패는 진짜 실패인가(failed_when), 실패한 그 호스트는 어떻게 할 것인가(ignore_errors·block/rescue), 다른 호스트들은 어떻게 할 것인가(any_errors_fatal·max_fail_percentage). 여기에 시작하기 전에 막는 assert 와, 실패와는 다른 칸으로 세는 unreachable 이 더해지면 배포 리포트를 읽을 수 있게 됩니다. 마지막 단계에서 그 리포트를 직접 만듭니다.

단계

  1. /root/ans/err/hosts.iniweb 그룹(web1, web2) · db 그룹(db1) · ghosts 그룹(ghost1)을 적으세요. 네 호스트 모두 ansible_host=127.0.0.1, ansible_user=root 이고 포트는 ghost12223, 나머지는 2222 입니다. 그리고 /root/ans/err/p01.ymlweb:db 에 도는 플레이를 만드세요 — 첫 태스크는 web2 에서만 실패하고, 둘째 태스크는 /root/ans/err/a1/reached-<호스트이름> 을 남깁니다. 실행 출력은 /root/ans/err/out/run1.txt 에 저장하세요.
  2. /root/ans/err/p02.yml 을 1단계와 같은 구조로 만들되 실패하는 태스크에 ignore_errors 를 붙이고, 표식은 /root/ans/err/a2/reached-<호스트이름> 에 남기세요. 출력은 /root/ans/err/out/run2.txt 에 저장합니다. 이번에는 세 호스트 모두 표식이 남고 PLAY RECAPignored 칸이 올라가야 합니다.
  3. /root/ans/err/sample.conflisten_port=8080 · env=prod · workers=4 세 줄로 만드세요. 그리고 /root/ans/err/p03.ymlweb1 에서 그 파일에 nosuchkey 가 몇 번 나오는지 세는 태스크를 만드세요 — 조회일 뿐이므로 변경으로 세지 않게 하고, 못 찾아서 나오는 종료 코드는 실패가 아니게 기준을 정하세요. 이어서 그 종료 코드를 /root/ans/err/a3/grep-rc.txt 에 한 줄로 남기고 출력은 /root/ans/err/out/run3.txt 에 저장하세요.
  4. /root/ans/err/p04.ymlweb1 에 도는 플레이를 만드세요. block 안에 실패하는 태스크와 그 뒤에 /root/ans/err/a4/never.txt 를 만드는 태스크를 두고, rescue/root/ans/err/a4/rescued.txt실패한 태스크의 이름을 담고, always/root/ans/err/a4/always.txt 를 남기게 하세요. 출력은 /root/ans/err/out/run4.txt 에 저장합니다. never.txt 는 만들어지면 안 되고 PLAY RECAPrescued 가 1 이어야 합니다.
  5. /root/ans/err/p05.ymllocalhost 에 도는 플레이로 만들되 assert 태스크 하나만 두세요. deploy_envdev·stage·prod 중 하나인지 검사하고, fail_msg허용되지 않은 배포 환경입니다 로 시작해 지금 값을 함께 보여 줘야 합니다. -e deploy_env=prod 로 돌린 출력을 /root/ans/err/out/assert-ok.txt 에, -e deploy_env=qa 로 돌린 출력을 /root/ans/err/out/assert-fail.txt 에 저장하세요.
  6. /root/ans/err/p06.yml 을 1단계와 같은 구조로 만들되 플레이에 any_errors_fatal 을 켜고 표식은 /root/ans/err/a6/reached-<호스트이름> 에 남기게 하세요. 출력은 /root/ans/err/out/run6.txt 에 저장합니다. 이번에는 아무 호스트도 표식을 남기지 못해야 합니다.
  7. /root/ans/err/p07.ymlweb:db 에 도는 플레이를 만드세요. max_fail_percentage50, 변수 run_tag 의 기본값은 continue, 변수 doomed 의 기본값은 web2 하나입니다. 첫 태스크가 /root/ans/err/a7/<run_tag> 디렉터리를 만들고, 다음 태스크가 doomed 에 든 호스트에서만 실패하고, 마지막 태스크가 /root/ans/err/a7/<run_tag>/reached-<호스트이름> 을 남깁니다. 기본값으로 한 번 돌려 /root/ans/err/out/run7-continue.txt 에, run_tagabort 로 두고 doomedweb2db1 을 넣어 한 번 더 돌려 /root/ans/err/out/run7-abort.txt 에 저장하세요.
  8. /root/ans/err/p08.ymlall 에 도는 플레이로 만드세요 — 첫 태스크는 ping 이되 닿지 않는 것을 무시하고, 둘째 태스크는 /root/ans/err/a8/reached-<호스트이름> 을 남깁니다. 출력은 /root/ans/err/out/run8.txt 에 저장하세요. 그리고 실행 기록 파일 하나를 인자로 받아 hosts=N ok=N changed=N unreachable=N failed=N skipped=N rescued=N ignored=N 한 줄을 내는 /root/ans/err/recap.sh 를 쓰고, 그것으로 /root/ans/err/out/failure-report.json 을 만드세요. 키는 default_failed(1단계 실행의 failed 합) · ignored(2단계) · rescued(4단계) · unreachable(8단계) · fatal_reached(a6 의 표식 수) · maxfail_reached(a7/continue 의 표식 수) 여섯 개이고 값은 모두 숫자입니다.

참고

기본 동작 — 실패한 호스트만 빠진다

/root/ans/err/hosts.iniweb 그룹(web1, web2) · db 그룹(db1) · ghosts 그룹(ghost1)을 적으세요. 네 호스트 모두 ansible_host=127.0.0.1, ansible_user=root 이고 포트는 ghost12223, 나머지는 2222 입니다. 그리고 /root/ans/err/p01.ymlweb:db 에 도는 플레이를 만드세요 — 첫 태스크는 web2 에서만 실패하고, 둘째 태스크는 /root/ans/err/a1/reached-<호스트이름> 을 남깁니다. 실행 출력은 /root/ans/err/out/run1.txt 에 저장하세요.

호스트가 전부 같은 sshd 로 붙으므로 '이 대만 실패' 는 파일이 아니라 inventory_hostname 조건으로 만듭니다. ghost1 의 포트는 아무도 듣고 있지 않은 번호라 8단계에서 닿지 않는 호스트 노릇을 합니다. 플레이북이 0 이 아닌 값으로 끝나므로 출력을 저장할 때 스크립트가 거기서 멈추지 않게 하세요.

결과만 무시하면 무엇이 달라지나

/root/ans/err/p02.yml 을 1단계와 같은 구조로 만들되 실패하는 태스크에 ignore_errors 를 붙이고, 표식은 /root/ans/err/a2/reached-<호스트이름> 에 남기세요. 출력은 /root/ans/err/out/run2.txt 에 저장합니다. 이번에는 세 호스트 모두 표식이 남고 PLAY RECAPignored 칸이 올라가야 합니다.

ignore_errors 는 판정을 바꾸지 않고 결과만 무시합니다. 그래서 태스크는 여전히 실패로 기록되지만 호스트는 빠지지 않습니다. PLAY RECAP 에서 failed 가 아니라 어느 칸이 올라가는지 직접 보세요.

무엇을 실패로 볼 것인가를 직접 정하기

/root/ans/err/sample.conflisten_port=8080 · env=prod · workers=4 세 줄로 만드세요. 그리고 /root/ans/err/p03.ymlweb1 에서 그 파일에 nosuchkey 가 몇 번 나오는지 세는 태스크를 만드세요 — 조회일 뿐이므로 변경으로 세지 않게 하고, 못 찾아서 나오는 종료 코드는 실패가 아니게 기준을 정하세요. 이어서 그 종료 코드를 /root/ans/err/a3/grep-rc.txt 에 한 줄로 남기고 출력은 /root/ans/err/out/run3.txt 에 저장하세요.

찾는 낱말이 없으면 1 이 나오고 파일이 없거나 인자가 틀리면 2 이상이 나옵니다. 앞은 답이고 뒤가 오류입니다. register 로 받은 결과의 종료 코드로 기준을 적으세요. ignore_errors 로 덮는 것과는 다른 일입니다 — 이 플레이북은 failed=0 으로 깨끗하게 끝나야 합니다.

block·rescue·always 로 롤백을 구조로 만들기

/root/ans/err/p04.ymlweb1 에 도는 플레이를 만드세요. block 안에 실패하는 태스크와 그 뒤에 /root/ans/err/a4/never.txt 를 만드는 태스크를 두고, rescue/root/ans/err/a4/rescued.txt실패한 태스크의 이름을 담고, always/root/ans/err/a4/always.txt 를 남기게 하세요. 출력은 /root/ans/err/out/run4.txt 에 저장합니다. never.txt 는 만들어지면 안 되고 PLAY RECAPrescued 가 1 이어야 합니다.

실패한 태스크의 이름은 rescue 안에서만 쓸 수 있는 변수에 담겨 있습니다 — 이름에 failed 와 task 가 들어갑니다. rescue 가 끝까지 성공하면 그 호스트는 실패하지 않은 것으로 취급돼 초록불이 됩니다. 그래서 롤백이 돌았다는 증거를 파일로 남기는 것이 이 구조의 절반입니다.

전제가 어긋나면 아무것도 건드리기 전에 멈춘다

/root/ans/err/p05.ymllocalhost 에 도는 플레이로 만들되 assert 태스크 하나만 두세요. deploy_envdev·stage·prod 중 하나인지 검사하고, fail_msg허용되지 않은 배포 환경입니다 로 시작해 지금 값을 함께 보여 줘야 합니다. -e deploy_env=prod 로 돌린 출력을 /root/ans/err/out/assert-ok.txt 에, -e deploy_env=qa 로 돌린 출력을 /root/ans/err/out/assert-fail.txt 에 저장하세요.

기본 실패 메시지는 한 줄뿐이라 조건이 여러 개면 어느 것이 거짓이었는지 알 수 없습니다. 지금 값을 메시지에 박아 두면 로그 한 줄로 끝납니다. 명령줄 변수는 플레이의 vars 보다 세므로 같은 플레이북을 값만 바꿔 두 번 돌릴 수 있습니다. 채점기도 같은 방법으로 이 플레이북을 직접 돌려 봅니다.

한 대라도 실패하면 전부 멈추게 하기

/root/ans/err/p06.yml 을 1단계와 같은 구조로 만들되 플레이에 any_errors_fatal 을 켜고 표식은 /root/ans/err/a6/reached-<호스트이름> 에 남기게 하세요. 출력은 /root/ans/err/out/run6.txt 에 저장합니다. 이번에는 아무 호스트도 표식을 남기지 못해야 합니다.

이 손잡이는 플레이 수준에 붙습니다(태스크가 아닙니다). 실패하지 않은 호스트도 남은 태스크를 실행하지 않으므로 표식 디렉터리는 비어 있게 됩니다. PLAY RECAP 에서 실패하지 않은 호스트들의 ok 칸이 어떻게 되는지 보세요. 표식 디렉터리는 미리 만들어 두어야 '비어 있다' 와 '없다' 를 구분할 수 있습니다.

몇 대까지는 견딘다 — 비율로 정하기

/root/ans/err/p07.ymlweb:db 에 도는 플레이를 만드세요. max_fail_percentage50, 변수 run_tag 의 기본값은 continue, 변수 doomed 의 기본값은 web2 하나입니다. 첫 태스크가 /root/ans/err/a7/<run_tag> 디렉터리를 만들고, 다음 태스크가 doomed 에 든 호스트에서만 실패하고, 마지막 태스크가 /root/ans/err/a7/<run_tag>/reached-<호스트이름> 을 남깁니다. 기본값으로 한 번 돌려 /root/ans/err/out/run7-continue.txt 에, run_tagabort 로 두고 doomedweb2db1 을 넣어 한 번 더 돌려 /root/ans/err/out/run7-abort.txt 에 저장하세요.

세 대 중 한 대는 33 퍼센트, 두 대는 66 퍼센트입니다. 기준을 넘었는지가 갈림길입니다. 목록 변수를 명령줄에서 덮어쓸 때는 JSON 으로 통째로 넘기면 편합니다. 표식 디렉터리를 만드는 태스크를 실패하는 태스크보다 앞에 두어야 멈춘 실행에서도 빈 디렉터리가 남습니다.

닿지 않는 호스트와 여섯 번의 실행을 한 장으로

/root/ans/err/p08.ymlall 에 도는 플레이로 만드세요 — 첫 태스크는 ping 이되 닿지 않는 것을 무시하고, 둘째 태스크는 /root/ans/err/a8/reached-<호스트이름> 을 남깁니다. 출력은 /root/ans/err/out/run8.txt 에 저장하세요. 그리고 실행 기록 파일 하나를 인자로 받아 hosts=N ok=N changed=N unreachable=N failed=N skipped=N rescued=N ignored=N 한 줄을 내는 /root/ans/err/recap.sh 를 쓰고, 그것으로 /root/ans/err/out/failure-report.json 을 만드세요. 키는 default_failed(1단계 실행의 failed 합) · ignored(2단계) · rescued(4단계) · unreachable(8단계) · fatal_reached(a6 의 표식 수) · maxfail_reached(a7/continue 의 표식 수) 여섯 개이고 값은 모두 숫자입니다.

닿지 않는 것은 태스크의 실패가 아니라 연결의 실패라 무시하는 인자가 따로 있습니다. 그 인자는 붙인 태스크에만 듣고, 무시하기로 했어도 플레이북의 종료 코드는 0 이 아닙니다. 합산은 PLAY RECAP아래만 읽어야 합니다 — 위의 태스크 출력에도 비슷한 글자가 섞여 있습니다. 채점기는 자기가 만든 기록 파일로도 이 스크립트를 돌려 보므로, 답을 적어 둔 스크립트는 통과하지 못합니다.