실패한 Shell 및 Command 모듈 디버깅을 위한 실용 가이드
register, stdout, stderr, rc, failed_when, changed_when 예제를 사용하여 Ansible shell 및 command 실패를 디버깅합니다.
실패한 Shell 및 Command 모듈 디버깅을 위한 실용 가이드
Ansible command 및 shell 모듈은 전용 모듈이 없을 때 유용하지만, 디버깅이 까다로울 수 있습니다. 실패한 태스크는 직접 명령 출력을 캡처하지 않으면 반환 코드만 표시될 수 있습니다.
이 가이드에서는 rc, stdout, stderr를 확인한 다음 failed_when과 changed_when을 사용하여 Ansible이 실제 결과를 보고하도록 하는 방법을 통해 실패한 shell 및 command 모듈을 디버깅하는 방법을 보여줍니다.
Command vs. Shell: 차이점 이해
디버깅에 들어가기 전에 두 모듈의 근본적인 차이점을 이해하는 것이 중요합니다. 실행 환경이 실패 모드에 영향을 미치기 때문입니다.
ansible.builtin.command
이 모듈은 표준 셸 환경을 거치지 않고 명령을 직접 실행합니다. 변수 보간, 글로빙, 파이프(|), 리디렉션(>)과 같은 셸 기능을 피하므로 더 안전하고 예측 가능합니다.
모범 사례: 태스크가 간단하고 셸 기능이 필요하지 않은 경우 command를 사용하십시오.
ansible.builtin.shell
이 모듈은 원격 호스트의 표준 셸(/bin/sh 또는 이와 동등한 것)을 통해 명령을 실행합니다. 이는 복잡한 작업, 환경 변수 또는 표준 셸 구문(예: cd /tmp && ls -l)을 사용해야 할 때 필요합니다.
경고: shell은 환경에 의존하기 때문에 PATH 구성, 숨겨진 환경 변수 또는 복잡한 따옴표 처리와 관련된 예측 불가능한 실패가 발생하기 쉽습니다.
Ansible 명령 실패의 구조
기본적으로 Ansible은 프로세스의 **반환 코드(RC)**를 기반으로 command 또는 shell 모듈 태스크의 성공 또는 실패를 결정합니다.
| 반환 코드(RC) | 해석 |
|---|---|
rc = 0 |
성공 (태스크 계속) |
rc != 0 |
실패 (태스크 즉시 중지, 호스트 실패로 표시) |
그러나 이 간단한 검사는 실제 스크립트의 미묘한 차이를 포착하지 못하는 경우가 많습니다. 명령이 RC 0을 반환하지만 원치 않는 결과(논리적 실패)를 생성하거나 명령이 예상된 0이 아닌 RC(예: grep이 일치하는 항목을 찾지 못하면 1을 반환)를 반환할 수 있습니다.
이러한 미묘한 차이를 처리하려면 출력을 캡처하고 조건부로 실패 상태를 제어해야 합니다.
1단계: register로 명령 출력 캡처
효과적인 디버깅의 첫 번째 단계는 register 키워드를 사용하여 사용 가능한 모든 출력 스트림을 Ansible 변수에 캡처하는 것입니다. 이를 통해 반환 코드, 표준 출력 및 표준 오류를 검사할 수 있습니다.
초기 테스트 중에 0이 아닌 반환 코드가 발생해도 플레이북이 즉시 중단되지 않도록 하려면 ignore_errors: yes를 임시로 사용하는 것이 좋습니다.
- name: 잠재적으로 신뢰할 수 없는 명령을 실행하고 결과 캡처
ansible.builtin.shell: |
/usr/local/bin/check_config.sh 2>&1 || exit 1
register: cmd_output
ignore_errors: yes # RC != 0이 계속 진행되도록 임시 허용
등록되면 cmd_output 변수에는 여러 유용한 키가 포함되며, 가장 중요한 것은 다음과 같습니다.
cmd_output.rc: 정수 반환 코드.cmd_output.stdout: 표준 출력 스트림.cmd_output.stderr: 표준 오류 스트림.cmd_output.failed: Ansible이 현재 태스크를 실패한 것으로 간주하는지 여부를 나타내는 부울 값입니다.
2단계: debug로 캡처된 데이터 검사
실패한 태스크 직후에 debug 모듈을 사용하여 등록된 변수의 내용을 검사합니다. 이는 실제 기술적 실패(예: 명령을 찾을 수 없음)와 논리적 실패(예: 스크립트가 실행되었지만 내부 오류 보고)를 구별하는 데 도움이 됩니다.
- name: 디버깅을 위해 캡처된 전체 출력 표시
ansible.builtin.debug:
var: cmd_output
# 'when'을 사용하여 태스크가 실패한 경우에만 표시하여 출력 정리
when: cmd_output.failed is defined and cmd_output.failed
- name: stderr 내용 강조 표시
ansible.builtin.debug:
msg: "캡처된 STDERR: {{ cmd_output.stderr }}"
when: cmd_output.stderr | length > 0
전체 출력을 검사하면 실제 실패를 나타내는 특정 오류 메시지나 패턴을 정확히 찾아낼 수 있습니다.
3단계: failed_when으로 기본 실패 동작 재정의
failed_when 조건문은 복잡한 셸 모듈 결과를 디버깅하고 관리하기 위한 가장 강력한 도구입니다. 기본 반환 코드에 관계없이 Jinja2 표현식을 사용하여 태스크를 실패로 표시해야 하는지 여부를 결정하는 사용자 정의 논리를 정의할 수 있습니다.
시나리오 A: 예상된 0이 아닌 반환 코드 처리
일부 유틸리티는 예상된 결과에 대해 0이 아닌 코드를 반환합니다. 예를 들어 grep은 일치하는 항목이 없으면 1을 반환하고 실제 오류에 대해서는 1보다 큰 값을 반환합니다.
- name: 설정이 있는지 확인하지만 없을 때 실패하지 않음
ansible.builtin.command: grep -q '^feature_enabled=true' /etc/myapp.conf
register: grep_result
failed_when: grep_result.rc > 1
changed_when: false
시나리오 B: 논리적 오류 시 실패(RC=0이지만 잘못된 출력)
스크립트가 내부 오류가 발생해도 항상 RC=0을 반환하지만 stdout 또는 stderr에 특정 오류 문자열을 출력하는 경우 failed_when을 사용하여 해당 문자열을 포착합니다.
- name: 데이터베이스 연결 스크립트 검증
ansible.builtin.shell: /opt/scripts/db_connect_test.sh
register: db_result
# 일반적인 오류 문구에 대해 stdout과 stderr를 모두 확인
failed_when: >
('Connection refused' in db_result.stderr) or
('Authentication failure' in db_result.stdout)
시나리오 C: RC 및 출력 검사 결합
강력한 검사를 위해 논리 연산자(and, or, 괄호)를 사용하여 반환 코드와 내용 검사를 결합합니다.
- name: 배포 로그 확인
ansible.builtin.shell: tail -n 50 /var/log/deployment.log
register: log_check
# RC가 0이 아니거나 성공 출력에 'FATAL'이라는 단어가 포함된 경우 실패
failed_when: log_check.rc != 0 or 'FATAL' in log_check.stdout
팁:
failed_when을 사용할 때는 일반적으로ignore_errors: yes를 제거해야 합니다. 실패를 기록하면서 플레이를 계속 진행하려는 경우가 아니라면 말입니다.
안정적인 명령 실행을 위한 모범 사례
복잡한 디버깅의 필요성을 최소화하려면 command 또는 shell을 사용하는 태스크를 작성할 때 다음 표준을 따르십시오.
1. 항상 절대 경로 사용
원격 사용자의 $PATH에 의존하지 마십시오. 항상 실행 파일의 전체 경로를 지정하십시오(예: python이 아닌 /usr/bin/python). 이는 일관되지 않은 환경이나 실행 경로의 미묘한 차이로 인한 실패를 방지합니다.
2. 셸 로직보다 조건문 활용
shell 모듈 내에서 || 또는 &&와 같은 복잡한 셸 로직을 사용하는 대신 Ansible의 기본 조건문(when:, failed_when:, changed_when:)과 register 키워드를 활용하십시오. 이렇게 하면 플레이북 로직이 투명해지고 디버깅이 더 쉬워집니다.
3. 변경 감지 명시적 제어(changed_when)
기본적으로 command 및 shell은 반환 코드가 0이면 태스크를 changed로 표시합니다. 스크립트가 실행되지만 시스템에 변경 사항이 없는 경우(예: 간단한 상태 확인) changed_when을 사용하여 태스크가 변경을 초래하는 시기를 수동으로 정의해야 합니다.
- name: 디스크 공간 확인('changed'가 발생하지 않아야 함)
ansible.builtin.command: df -h /data
changed_when: false
4. 가능하면 상태 모듈 사용
파일 존재 확인, 서비스 시작/중지 또는 패키지 설치를 위해 shell을 사용하고 있다면 중단하고 전용 Ansible 모듈(예: ansible.builtin.stat, ansible.builtin.service, ansible.builtin.package)을 찾으십시오. 전용 모듈은 내부적으로 멱등성과 오류 검사를 처리하므로 디버깅 노력이 크게 줄어듭니다.
최종 요점
셸 또는 명령 태스크가 실패하면 먼저 결과를 캡처하고 rc, stdout, stderr를 검사한 다음 failed_when에 실제 성공 조건을 인코딩하십시오. 태스크가 안정화되면 changed_when을 추가하여 상태 검사가 모든 플레이북 실행에서 잘못된 변경 사항을 표시하지 않도록 하십시오.