LabHub
배우기 러닝패스 코스

Ansible Fundamentals

Playbooks — Write State, Not Commands

LabHub 에서 이어서 보기

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

한 줄 요약

플레이북의 각 태스크는 "이 명령을 실행하라"가 아니라 "끝났을 때 이런 상태여야 한다"는 선언이다.

Concept map: "지금 상태가 어떤지 확인하는 코드" · 플레이의 목록 · 태스크는 위에서 아래로 순서대로 · 모든 태스크에 name 을 단다.

왜 이게 필요했나

셸 스크립트로 서버를 세팅해 본 사람은 스크립트가 점점 길어지는 경험을 한다. 처음엔 mkdir /opt/app 한 줄이었다가, 두 번째 실행에서 "이미 있다" 오류가 나서 mkdir -p 로 바꾸고, 권한이 다를 수 있어 chmod 를 붙이고, 그게 이미 맞으면 굳이 안 해도 되니 if 로 감싼다. 결국 스크립트의 절반이 "지금 상태가 어떤지 확인하는 코드" 가 된다.

Ansible 의 모듈은 그 절반을 대신 짊어진다. file 모듈에 path, state=directory, mode=0755 를 주면, 없으면 만들고 있으면 두고 권한이 다르면 고친다. 그리고 실제로 무언가를 바꿨을 때만 changed 로 보고한다. 이 보고가 다음 모듈에서 배울 멱등성과 핸들러의 토대가 된다.

어떻게 동작하나

플레이북은 플레이의 목록이고, 플레이는 "어떤 호스트에 어떤 태스크를 순서대로"를 담는다.

- name: 웹 서버 기본 설정          # 플레이 이름
  hosts: web                      # 대상 (인벤토리의 그룹/호스트/패턴)
  gather_facts: true              # 대상의 정보를 먼저 수집할지
  tasks:
    - name: 앱 디렉터리 준비       # 태스크 이름
      ansible.builtin.file:       # 모듈
        path: /opt/app
        state: directory
        mode: "0755"

기억할 규칙은 세 가지다.

  1. 태스크는 위에서 아래로 순서대로 실행된다. 다만 여러 호스트에 대해서는 태스크 하나가 모든 호스트에서 끝난 뒤 다음 태스크로 간다.
  2. 모든 태스크에 name 을 단다. 이름이 없으면 로그에 모듈 이름과 인자가 통째로 찍혀 읽기 어렵고, 태그로 고르기도 어렵다.
  3. command/shell 은 최후의 수단이다. 이 둘은 상태를 모르기 때문에 매번 changed 를 보고한다. 전용 모듈이 있으면 그것을 쓰고, 어쩔 수 없이 셸을 써야 한다면 creates 같은 가드를 붙인다.

실행 전에 확인할 수 있는 수단도 있다. --syntax-check 는 YAML 과 플레이 구조를 검사하고, --check 는 실제로 바꾸지 않고 "무엇이 바뀔지"만 보고한다. --diff 를 함께 주면 파일 내용의 차이까지 보여 준다. 운영 서버에 처음 돌리는 플레이북은 반드시 --check --diff 를 먼저 거친다.

현장에서 만나는 모습

첫째, 태그의 두 얼굴. 태그를 달면 --tags config 로 설정만 다시 밀 수 있어 편하다. 그런데 태그로 일부만 실행하는 습관이 굳으면 "전체를 돌린 적이 없는 플레이북"이 생긴다. 그런 플레이북은 어느 날 전체 실행에서 처음 보는 오류를 낸다. 태그는 디버깅용 가속 장치이지 정상 실행 경로가 아니다.

둘째, 점검 모드의 한계. --check 는 모듈이 그 모드를 지원할 때만 정확하다. command 로 만든 결과에 의존하는 뒷 태스크는 점검 모드에서 엉뚱한 결과를 낼 수 있다. 그래서 점검 모드가 깨끗하다는 것과 실제 실행이 안전하다는 것은 다른 명제다.

셋째, 이름이 곧 문서다. 사고가 났을 때 사람이 읽는 것은 코드가 아니라 실행 로그다. "Install nginx" 보다 "nginx 설치 — 설정 파일은 다음 태스크에서 덮어씀" 이 훨씬 쓸모 있다.

실패했을 때 어디서 멈추나

플레이북이 중간에 실패하면 이미 적용된 것과 아직 안 된 것이 섞인 상태가 남는다. 스크립트와 달리 Ansible 은 여러 호스트를 동시에 다루므로 이 상태가 더 복잡해지고, 기본 동작을 알아 두지 않으면 사고 뒤의 판단이 어려워진다.

기본은 그 호스트만 빠지는 것이다. 태스크가 어느 호스트에서 실패하면 그 호스트는 이후 태스크에서 제외되고, 나머지 호스트는 계속 진행한다. 그래서 10대 중 1대만 실패하면 나머지 9대는 끝까지 가고, 클러스터가 두 가지 버전으로 갈린 상태가 된다.

이 동작을 바꾸는 장치가 몇 가지 있다.

serial 과 핸들러의 관계는 특히 잘 잊힌다. 핸들러는 기본적으로 플레이가 끝날 때 한 번 도는데, serial 을 쓰면 각 묶음이 끝날 때마다 돈다. 그래서 재시작이 묶음 단위로 일어나고, 이것이 실은 순차 배포에서 원하는 동작이다.

그리고 실패한 뒤에 다시 돌리는 것이 안전한지를 미리 판단해 두어야 한다. 모든 태스크가 멱등하면 그냥 다시 돌리면 되지만, 중간에 멱등하지 않은 것이 하나라도 있으면 재실행이 상황을 악화시킨다. 앞 모듈에서 멱등성을 시험으로 강제하라고 한 이유가 여기서 드러난다. 다시 돌려도 되는지 확신할 수 없는 자동화는, 실패했을 때 사람이 손으로 수습해야 하는 자동화입니다.

다음 실습에서 할 것

/root/ans/play/site.yml 을 만들어 디렉터리 생성·설정 파일 배치·기존 파일 한 줄 수정을 태스크로 표현하고, 태그와 점검 모드를 직접 써 본다. 마지막에는 오류가 네 군데 심어진 플레이북을 고쳐 통과시킨다.