SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor

Ansible check mode와 --diff 사용법 및 주의사항

Ansible의 --check와 --diff 옵션을 사용하여 실제 변경 사항을 미리 확인하는 방법을 설명합니다. 체크 모드가 지원되지 않는 모듈의 한계와 드라이 런 실행 시 발생할 수 있는 오해를 방지하기 위한 핵심 가이드를 제공합니다.

Ansible check mode의 기능

Ansible check mode는 모의 실행(dry run)입니다. ansible-playbook --check은 플레이에 포함된 모든 호스트에 연결하여, 각 모듈에 현재 상태가 요청한 상태와 일치하는지 확인하고, 실제로 아무것도 변경하지 않은 채 무엇이 변경될지 보고합니다. --diff을 추가하면 변경될 파일의 수정 전후 내용도 출력합니다. 이 두 기능을 함께 사용하면 실제 실행 전에 반드시 확인해야 할 질문, 즉 "이 서버들에서 무엇이 변경될 것인가?"에 대한 답을 얻을 수 있습니다.

Check mode는 플레이북을 시뮬레이션하는 것이 아닙니다. 서버에 대한 모델이 별도로 존재하는 것도 아닙니다. 각 모듈은 단순히 쓰기 대신 읽기 작업을 수행하도록 요청받을 뿐입니다. 읽기 전용으로 응답할 수 있는 모듈은 changed를 보고하고 다음 단계로 넘어갑니다. 응답할 수 없는 모듈은 아무것도 하지 않으며 아무것도 보고하지 않습니다. Ansible 문서에서는 이를 한 줄로 다음과 같이 설명합니다. "Check mode를 지원하지 않는 모듈은 아무것도 보고하지 않으며 아무것도 수행하지 않습니다." 이 간극이 바로 모의 실행이 잘못된 결과를 줄 수 있는 지점이며, 이 가이드의 대부분은 이 간극을 다룹니다.

드라이 런 실행: --check 및 --diff

ansible-playbook -i inventory.ini site.yml --check --diff --limit web1

-C와 -D은 두 플래그의 단축 형태입니다. --limit는 의도된 것입니다. 호스트 한 대의 diff는 읽을 수 있지만, 호스트 스무 대의 diff는 그냥 지나치게 되기 때문입니다.

네 가지 결과 단어가 전체 보고서의 핵심을 나타냅니다.

  • ok: [web1]은 작업이 실행되었으나 변경 사항이 없음을 의미합니다. apt이나 template처럼 상태를 관리하는 모듈의 경우, 대상 상태가 이미 일치함을 의미합니다. debug이나 set_fact의 경우 단순히 작업이 실행되었음을 뜻합니다.
  • changed: [web1]는 모듈이 무언가를 기록할 것으로 예측됨을 의미합니다. --diff과 함께 사용하면 그 위의 줄들이 무엇이 변경될지 보여줍니다. 대부분의 모듈은 정확하게 예측하지만, 체크 모드를 부분적으로만 지원하는 일부 모듈은 자체 문서에 경고를 명시합니다. 예를 들어 get_url은 체크 모드에서 HEAD 요청만 보내며, 잘못된 변경 상태를 보고할 수 있다고 명시합니다.
  • skipping: [web1]은 작업이 실행되지 않았음을 의미합니다. when가 평가되어 false가 나왔거나, 해당 모듈이 체크 모드에서 실행될 수 없는 경우입니다.
  • fatal: [web1]은 확인 도중 작업이 실패했음을 의미합니다. 플레이북이 고장 났다고 단정하기 전에 메시지를 먼저 읽어보십시오.

--diff은 파일 모듈에 대해 통합 diff를 출력합니다. 삭제된 줄은 -로, 추가된 줄은 +으로 표시되며, --- before와 +++ after로 시작하는 헤더 아래에 대상 경로가 표시됩니다. 파일을 기록하지 않는 모듈은 자체적인 이전/이후 상태를 출력하므로, ansible.builtin.user은 파일 내용 대신 변경될 속성을 보여줍니다.

ansible.cfg에서 diff를 영구적으로 활성화하여 플래그 입력을 잊지 않도록 설정하십시오:

[diff]
always = true
context = 5

체크 모드 이전에 수행할 수 있는 두 가지 가벼운 확인 작업이 있습니다. ansible-playbook site.yml --syntax-check은 호스트에 접속하지 않고 YAML과 플레이 구조를 구문 분석합니다. ansible-playbook site.yml --list-tasks는 실행될 작업을 출력하며, 이를 통해 태그가 지정되었다고 생각한 역할이 실제로는 그렇지 않음을 확인할 수 있습니다. 두 작업 모두 연결을 맺지 않으므로 즉시 완료됩니다.

체크 모드 자체는 연결을 맺습니다. 패턴에 포함된 모든 호스트에 SSH로 접속하여 팩트를 수집하므로, 다운된 호스트가 있으면 드라이 런은 실패합니다. 이는 그 자체로 유용한 신호이며, CI에 드라이 런을 도입하기 전에 도달할 수 없는 호스트에 대해 플레이북이 어떻게 동작해야 할지 결정하는 것이 중요한 이유이기도 합니다.

새 서버에서 체크 모드가 실패하는 이유

이 플레이북은 올바르게 작성되었습니다. nginx가 설치되지 않은 서버에 대해 --check을 실행하면 대부분의 작업이 실패합니다.

- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present

- name: Write the site config
  ansible.builtin.template:
    src: site.conf.j2
    dest: /etc/nginx/conf.d/site.conf

- name: Start and enable nginx
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

apt 작업은 changed를 보고하며, 이는 정상입니다. 패키지가 존재하지 않으므로 실제 실행 시에는 설치가 진행될 것이기 때문입니다. 체크 모드에서는 설치가 수행되지 않습니다. 그 후 template 작업은 해당 호스트에 /etc/nginx/conf.d/가 존재하지 않고 이를 생성한 작업도 없으므로 실패합니다. service 작업 또한 쿼리할 nginx 유닛이 없으므로 실패합니다. 두 실패 모두 플레이북의 버그가 아닙니다. 드라이 런(dry run)은 필요한 상태를 확보하지 못한 채 실행되었으며, 이는 체크 모드가 이전 작업의 변경 사항에 의존하는 작업에 대해 유용한 결과를 생성할 수 없다고 경고하는 문서의 의미와 같습니다.

따라서 이 규칙을 솔직하게 표현하자면 다음과 같습니다. 체크 모드는 플레이북이 이미 적용(converged)된 호스트에서 가장 정확하며, 새로운 호스트에서는 많은 오류를 발생시킵니다. 모든 작업이 ok을 보고하는 --check 실행은 호스트가 이미 적용된 상태임을 나타내는 유의미한 결과입니다. 이는 아무것도 변경되지 않음을 의미하기 때문입니다. 다만 모든 호스트에 적용되는 두 가지 주의 사항이 있습니다. 건너뛴 command 작업은 답변이 아닌 공백으로 처리되며, get_url과 같이 체크 모드를 부분적으로만 지원하는 모듈은 변경 상태를 잘못 보고할 수 있습니다. 완전히 새로운 호스트에서 --check은 주로 해당 호스트가 새것임을 알려줄 뿐입니다. VPS를 대상으로 첫 번째 Ansible 플레이북을 작성할 때, 첫 번째 드라이 런은 붉은색 오류로 가득 찰 것임을 예상하고, 두 번째 실행 결과로 플레이북의 완성도를 판단하십시오.

체크 모드에서 command 및 shell 작업이 건너뛰어지는 이유

ansible.builtin.command와 ansible.builtin.shell은 사용자가 실행하는 명령의 내용을 알 수 없습니다. 임의의 바이너리를 읽기 전용으로 실행할 방법은 없으므로, 체크 모드에서는 해당 모듈의 실행이 거부됩니다. 작업 결과에는 skipped: true가 포함되며 메시지는 Command would have run if not in check mode로 표시되고, 출력에는 skipping: [web1]이 나타납니다.

모듈 문서에서는 이러한 체크 모드 지원을 "부분적(partial)"이라고 부르며, 해결책으로 creates과 removes을 제시합니다. 작업에 creates 경로를 지정하면 체크 모드에서 최소한 파일 테스트를 평가할 수 있습니다.

- name: Extract the release bundle
  ansible.builtin.command: /usr/bin/tar xf /tmp/app.tar.gz -C /opt/app
  args:
    creates: /opt/app/bin/app

/opt/app/bin/app이 이미 존재하면 체크 모드는 Would not run command since '/opt/app/bin/app' exists을 보고하는데, 이는 올바른 결과입니다. 경로가 없으면 Command would have run if not in check mode가 반환되며, 이 또한 올바른 결과입니다. creates이 없으면 해당 작업은 드라이 런(dry run)에서 아무것도 수행하지 않는 빈 공간이 됩니다.

이로 인한 연쇄 효과는 단순한 빈 공간보다 더 심각합니다. 건너뛴 command 작업도 결과를 등록하며, 그 결과에 stdout가 포함되기는 하지만 실행된 내용이 없으므로 빈 문자열이 됩니다. 다음 작업의 조건문은 이 빈 문자열을 기준으로 평가되어 의도치 않은 분기로 조용히 넘어갑니다. 예를 들어 버전 확인 작업에서 버전을 찾지 못하거나, 'active' in svc.stdout과 같은 테스트가 거짓(false)으로 판정될 수 있습니다. 이 경우 플레이북은 실제 실행 시와 드라이 런 시에 서로 다른 결과를 내놓지만, 오류 메시지가 출력되지 않아 이 기능에서 가장 혼란스러운 실패 사례가 됩니다. 'dict object' has no attribute 'stdout' 오류는 다른 유형의 건너뛰기 상황에서 발생합니다. 즉, when 조건이 거짓(false)이어서 건너뛴 작업은 stdout 키 자체가 없는 결과를 등록합니다.

check_mode: false 설정과 그 적절한 사용 위치

작업에 check_mode: false을 설정하면 --check 모드에서도 해당 작업을 실제로 실행합니다. 이는 명령이 건너뛰어지는 문제를 해결하는 방법이며, 읽기 전용 작업에만 안전하게 사용할 수 있습니다.

- name: Read the installed app version
  ansible.builtin.command: /usr/local/bin/app --version
  register: app_version
  check_mode: false
  changed_when: false

해당 작업은 두 모드 모두에서 정직하게 동작합니다. 버전 정보를 읽기만 하고 쓰기 작업은 수행하지 않으므로, changed_when: false를 사용하면 실제로 변경하지 않은 내용을 변경한 것처럼 보고하는 문제를 방지할 수 있습니다. 또한 check_mode: false을 사용하면 드라이 런(dry run) 시 app_version.stdout가 빈 문자열 대신 실제 버전을 유지하게 되어, 이를 기반으로 하는 조건문이 올바르게 평가됩니다.

이 키워드를 다른 곳에 붙여넣기 전에 그 의미를 문자 그대로 이해해야 합니다. check_mode: false가 포함된 작업은 ansible-playbook --check 중에도 서버에 쓰기 작업을 수행합니다. 이를 apt 작업이나 template 작업에 사용하면 드라이 런 결과는 깔끔해 보일지 몰라도, 더 이상 드라이 런이 아니게 됩니다. 쓰기 작업을 안전하게 만들 수 없는 경우에는 대신 가드(guard)를 사용하십시오.

- name: Apply the database migration
  ansible.builtin.command: /usr/local/bin/app migrate --apply
  when: not ansible_check_mode

ansible_check_mode는 Ansible이 체크 모드 실행 중에 true으로 설정하는 매직 변수입니다. 반대되는 키워드도 존재합니다. check_mode: true은 실제 실행 중에도 작업을 항상 체크 모드로 고정하며, 이를 통해 드리프트 탐지(drift probe) 도구로 활용할 수 있습니다. 결과를 등록하고 changed 보고서가 생성된다면, 해당 호스트가 작업에서 정의한 상태와 더 이상 일치하지 않음을 의미합니다.

태스크가 매번 변경됨(changed)으로 보고되는 이유

플레이북을 아무런 변경 없이 연속으로 두 번 실행합니다. 두 번째 실행 시 모든 태스크는 ok 상태여야 합니다. 여전히 changed로 보고되는 태스크가 있다면 다음 두 가지 중 하나를 의미합니다. 모듈이 관리 대상의 상태를 파악하지 못하거나, 입력값이 불안정하다는 뜻입니다. 두 경우 모두 수정이 가능하며, 단순히 무시해야 할 노이즈가 아닙니다.

  • creates, removes, changed_when 없이 command와 shell을 사용하면 모듈이 변경 여부를 확인할 방법이 없으므로 매번 changed을 보고합니다. creates을 추가하거나, 출력 결과의 특정 문자열을 기준으로 changed_when를 설정하십시오.
  • state: touch를 사용한 ansible.builtin.file은 파일을 건드릴 때마다 타임스탬프가 갱신되므로 설계상 매번 changed를 보고합니다. 소유자나 권한만 설정하려는 경우라면 state: file을 사용하십시오.
  • 렌더링된 결과가 매번 바뀌는 template은 실행할 때마다 파일을 다시 씁니다. ansible_date_time의 타임스탬프, now() 호출, 매번 새로 생성되는 비밀번호 등은 모두 다른 바이트를 생성하므로 모듈은 변경 사항이 있다고 올바르게 보고합니다. 템플릿에서 변하는 값을 제거하십시오.
  • password: "{{ pw | password_hash('sha512') }}"을 사용한 ansible.builtin.user은 매번 변경됩니다. password_hash는 호출될 때마다 무작위 솔트(salt)를 생성하므로, 결과 해시값이 /etc/shadow에 저장된 값과 일치하지 않기 때문입니다. 안정적인 값에서 파생된 명시적 솔트를 전달하십시오.
  • 패키지 모듈의 state: latest는 업그레이드 가능한 버전이 있을 때마다 changed를 보고합니다. 이는 정상적인 동작입니다. 이것이 바로 state: latest을 사용하면 결과를 예측할 수 없는 플레이북이 되는 이유입니다. state: present을 사용하고 의도적으로 업그레이드를 수행하십시오.
  • creates 없이 URL을 지정한 ansible.builtin.unarchive은 실행할 때마다 아카이브를 다시 다운로드하며, 대상과 비교할 수 없는 아카이브 형식의 경우 압축도 매번 다시 풉니다. creates 경로를 제공하십시오.

--diff은 이러한 문제를 구분하는 가장 빠른 방법입니다. 태스크가 changed라고 표시하고 diff 결과에서 바이트 차이가 확인된다면 입력값이 불안정한 것입니다. 만약 changed이라고 표시되는데 diff 결과가 아무것도 없다면, 모듈이 변경 내용을 표현하지 못하는 것이며 이는 보통 command 태스크이거나 타임스탬프와 같은 메타데이터 전용 쓰기 작업일 가능성이 높습니다.

시끄러운 태스크를 조용하게 만들려고 changed_when: false를 사용하지 마십시오. 이 옵션은 보고를 억제하므로 notify이 발생하지 않으며, 결과적으로 서비스를 재시작하는 핸들러도 실행되지 않습니다. 대신 태스크를 수정하십시오.

폭발 반경 축소: --limit, --tags 및 --step

Check 모드는 어떤 변경이 발생할지 알려줍니다. 다음 플래그들은 한 번에 몇 대의 서버에 작업을 적용할지 결정합니다.

--limit은 인벤토리의 하위 집합으로 플레이 범위를 좁힙니다. hosts:과 동일한 패턴을 사용하므로 --limit web1와 --limit 'webservers:!web3' 모두 작동합니다. 패턴은 따옴표로 묶으십시오. 대화형 bash 세션에서 따옴표를 사용하지 않은 !은 느낌표에 대한 히스토리 확장을 유발하며, Ansible이 명령을 전달받기 전에 셸이 명령을 다시 작성해 버립니다.

패턴을 신뢰하기 전에 먼저 확인하십시오. ansible-playbook site.yml --limit 'webservers:!web3' --list-hosts는 일치하는 호스트를 출력하고 연결하지 않은 채 종료합니다. 아무것도 일치하지 않는 패턴은 안전합니다. Ansible은 전체 인벤토리로 돌아가지 않기 때문입니다. 호스트 패턴과 일치하는 항목이 없다는 경고를 출력한 뒤, 인벤토리, 호스트 패턴 및 --limit으로 인해 대상 호스트가 없다는 오류와 함께 종료됩니다. 인벤토리 파일이 그룹을 정의하는 방식을 이해하는 것이 패턴을 예측 가능하게 만드는 첫걸음입니다.

--tags deploy는 태그가 지정된 작업만 실행하며, --skip-tags packages는 그 외의 모든 작업을 실행합니다. --list-tags은 사용 가능한 태그를 출력합니다. 플레이가 너무 커져서 전체를 실행하기 부담스러운 시점이 오면 태그의 진가가 드러납니다. 이는 긴 플레이북을 역할(role)로 분리해야 하는 이유 중 하나이기도 합니다.

--start-at-task "Write the site config"은 지정된 작업부터 실패한 실행을 재개합니다. 복구할 때 사용하되, 그에 따른 비용을 이해해야 합니다. 해당 작업 이전의 모든 내용은 건너뛰며, 여기에는 팩트를 설정하거나 이후 작업이 읽을 변수를 등록하는 작업도 포함됩니다.

--step은 각 작업 전에 사용자에게 확인을 요청하며 yes, no, continue 중 하나를 입력할 때까지 대기합니다. 속도는 느리지만, 파괴적인 작업을 처음 실행할 때는 이 도구가 적합합니다. 20개의 작업이 끝난 뒤가 아니라 작업과 작업 사이에서 멈출 수 있기 때문입니다.

serial을 사용한 변경 사항 배포

기본적으로 Ansible은 다음 태스크를 시작하기 전에 플레이 내의 모든 호스트에 대해 하나의 태스크를 실행합니다. 이는 속도가 빠르지만, 잘못된 태스크가 같은 초에 전체 인프라에 영향을 미친다는 의미이기도 합니다. 오류를 확인하고 Ctrl-C를 누를 때쯤이면 이미 변경 사항이 모든 곳에 적용된 상태입니다.

serial는 플레이를 여러 배치로 나눕니다. 전체 플레이가 첫 번째 배치에 대해 실행된 다음, 그다음 배치로 넘어갑니다.

- name: Roll out the web tier
  hosts: webservers
  serial: [1, 5, "30%"]
  max_fail_percentage: 0
  tasks:
    - name: Deploy the release
      ansible.builtin.include_role:
        name: webapp

첫 번째 배치는 한 대의 호스트입니다. 이 호스트가 성공하면 두 번째 배치는 5대가 되며, 그 이후의 모든 배치는 플레이 대상 호스트의 30퍼센트가 됩니다. max_fail_percentage: 0은 배치 내의 어떤 호스트라도 실패하는 즉시 플레이의 나머지를 중단합니다. 임계값을 초과해야 하는데, 어떤 실패든 0을 초과하기 때문입니다. 한 대의 머신에 대한 보장은 해당 설정이 아니라 첫 번째 배치에서 나옵니다. 모든 곳에서 문제를 일으키는 릴리스는 첫 번째 배치의 단일 호스트에서 실패하고 거기서 멈춥니다. 5대 배치에서만 나타나는 실패는 이미 해당 배치의 다른 호스트들에 도달한 상태입니다. Ansible은 다음 태스크를 시작하기 전에 배치 전체에 걸쳐 각 태스크를 실행하기 때문입니다. any_errors_fatal: true은 더 강력한 버전으로, 첫 번째 호스트에서 실패가 발생하면 모든 대상에 대해 플레이를 종료합니다.

먼저 한 대의 호스트에 대해 실행하는 것은 편집증적인 행동이 아니며, 그 이유는 명확합니다. 인벤토리 그룹은 시간이 지나면서 차이가 발생합니다(drift). 다른 서버들보다 6개월 늦게 추가된 서버는 다른 배포판 릴리스를 실행하거나, 누군가 수동으로 설치한 서비스를 포함하거나, 디스크 구성이 다를 수 있습니다. 플레이북은 그룹 전체에는 올바르지만 그 특정 호스트에는 잘못된 것일 수 있으며, 이미 구성이 완료된 호스트를 대상으로 하는 dry run으로는 이를 확인할 수 없습니다. Linux 서버 인프라 관리는 대체로 변경 사항이 적용되기 전에 이러한 예외적인 호스트를 찾아내는 과정입니다.

실행 순서

  1. ansible-playbook site.yml --syntax-check는 네트워크 연결 없이도 YAML 및 구조적 오류를 잡아냅니다.
  2. ansible-playbook site.yml --limit web1 --list-hosts는 패턴이 의도한 대로 일치하는지 확인합니다.
  3. ansible-playbook site.yml --limit web1 --check --diff은 모의 실행(dry run)입니다. 변경 사항(diff)을 확인하십시오.
  4. ansible-playbook site.yml --limit web1 --diff은 해당 호스트 하나에만 설정을 적용합니다.
  5. 4단계를 다시 실행하십시오. 모든 항목이 ok 상태여야 합니다. 여전히 changed인 항목은 전체 인프라에 적용하기 전에 수정해야 할 작업입니다.
  6. 이제 전체 인벤토리를 대상으로 ansible-playbook site.yml --check --diff을 실행하면 의미 있는 결과를 얻을 수 있습니다. 이미 수렴된 호스트는 조용하므로 남은 결과는 실제 변경이 필요한 부분이기 때문입니다.

3단계와 관련하여 주의할 점이 있습니다. --diff은 파일 내용을 터미널과 CI 작업 로그에 출력하므로, 데이터베이스 비밀번호를 렌더링하는 템플릿은 해당 비밀번호를 로그에 그대로 노출합니다. 해당 작업에 diff: false를 설정하여 출력을 억제하거나, no_log: true을 사용하여 결과 전체를 숨기십시오. 또한 값 자체는 저장소에 직접 두지 말고 암호화된 Ansible Vault 파일에 보관하십시오.

FAQ

ansible-playbook --check를 실행하면 서버의 내용이 변경됩니까?

아니요, 사용자가 제어하는 한 가지 예외를 제외하면 변경되지 않습니다. 체크 모드에서는 모든 모듈이 실제 쓰기 작업 대신 보고만 하도록 요청받으며, 이를 지원하지 않는 모듈은 아무것도 보고하거나 수행하지 않습니다. 예외는 check_mode: false 작업 키워드인데, 이는 --check 실행 중에도 해당 단일 작업이 실제로 수행되도록 강제합니다. 드라이 런(dry run)을 신뢰하기 전에 플레이북과 역할에서 check_mode: false를 검색하고, 모든 일치 항목이 상태를 읽기만 하는 작업인지 확인하십시오.

--check와 --diff의 차이점은 무엇입니까?

--check는 실제 작업 수행 여부를 결정합니다. --diff은 표시되는 세부 정보의 양을 결정합니다. --check만 사용하면 파일이 변경될 것임을 알려줍니다. --diff만 사용하면 변경 사항을 적용하고 변경된 줄을 보여줍니다. 읽기 쉬운 드라이 런을 수행하려면 두 옵션을 함께 사용하십시오. 또한 ansible.cfg의 [diff] 아래에 always = true을 설정하여 실제 실행 시에도 --diff가 항상 켜져 있도록 하십시오.

왜 Ansible 작업이 매번 변경(changed)되었다고 보고합니까?

모듈이 자신이 관리하는 상태를 확인하지 못하거나, 전달한 값이 매번 다르기 때문입니다. creates 또는 changed_when을 추가하지 않으면 command 및 shell는 항상 changed를 보고합니다. state: touch를 포함한 file은 설계상 매번 변경됩니다. 타임스탬프나 새로 생성된 비밀번호를 렌더링하는 템플릿은 실행할 때마다 다른 바이트를 생성하므로 파일이 실제로 다시 작성됩니다. 플레이북을 연속으로 두 번 실행하십시오. 두 번째 실행에서도 여전히 changed으로 표시되는 작업이 수정해야 할 대상입니다.

왜 드라이 런 중에 command 및 shell 작업이 건너뛰어집니까?

임의의 명령을 읽기 전용으로 실행할 방법이 없기 때문입니다. 체크 모드에서 command 모듈은 Command would have run if not in check mode 메시지와 함께 skipped: true를 설정합니다. 체크 모드에서 파일 테스트를 평가할 수 있도록 creates 또는 removes를 추가하십시오. 상태를 읽기만 하는 작업의 경우 check_mode: false과 changed_when: false을 함께 설정하십시오. 이렇게 하면 드라이 런 중에도 등록된 결과에 실제 출력이 유지되어 이를 기반으로 하는 조건문이 계속 작동합니다.

왜 체크 모드가 새 서버에서는 실패하고 기존 서버에서는 성공합니까?

체크 모드는 이후 작업이 의존하는 상태를 생성하지 않기 때문입니다. nginx가 없는 호스트에 대해 드라이 런을 수행하면 설치 작업은 changed로 보고되지만, 이후 /etc/nginx/conf.d/ 디렉터리가 생성되지 않았기 때문에 해당 디렉터리에 쓰는 작업에서 실패합니다. 이는 예상된 동작입니다. 체크 모드는 플레이북이 이미 수렴된 호스트에 대한 드리프트 감지 도구입니다. 첫 번째 실행은 검증할 수 없습니다. 새 호스트에서는 한 대의 머신에 플레이북을 적용한 뒤 두 번째 실행 결과를 확인하십시오.

#ansible#check-mode#idempotency#automation#safety