🔧

변수와 조건문

최민석·2026-03-11

변수와 조건문

🔧 Ansible 실습 시리즈

이전 포스팅에서 nginx를 설치하는 playbook을 작성했습니다. 이번에는 패키지명을 하드코딩하지 않고 **변수(vars)**로 관리하고, **조건문(when)**으로 상황에 따라 task 실행을 분기하는 방법을 실습합니다.


변수 (vars)

Chap 1에서 작성한 nginx.yml을 다시 보겠습니다.

---
- name: Ansible nginx install & execute
  hosts: managed
  become: true
  gather_facts: false

  tasks:
    - name: apt install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
    - name: nginx service start
      ansible.builtin.service:
        name: nginx
        state: started

nginx라는 패키지명이 여러 곳에 하드코딩되어 있습니다. 다른 패키지를 설치하려면 playbook 전체를 수정해야 합니다.

변수로 리팩토링

---
- name: "Ansible {{ package_name }} install & execute"
  hosts: managed
  gather_facts: false
  vars:
    package_name: nginx

  tasks:
    - name: "install {{ package_name }}"
      become: true
      ansible.builtin.apt:
        name: "{{ package_name }}"
        state: present
    - name: "{{ package_name }} service start"
      become: true
      ansible.builtin.service:
        name: "{{ package_name }}"
        state: started
  • vars: — play 레벨에서 변수를 정의
  • "{{ package_name }}" — Jinja2 문법으로 변수를 참조

⚠️ 따옴표 필수

{{ }}를 사용할 때는 반드시 따옴표로 감싸야 합니다. YAML 파서가 {를 객체 시작으로 인식하여 문법 오류가 발생하기 때문입니다.

# OK
name: "{{ package_name }}"

# 에러
name: {{ package_name }}

변수 정의 방법

변수를 정의하는 방법은 여러 가지입니다.

# 1. play 레벨 vars
- hosts: managed
  vars:
    port: 80

# 2. 별도 파일로 분리
- hosts: managed
  vars_files:
    - vars/common.yml

# 3. inventory에서 정의 (Chap 1에서 이미 사용)
[managed:vars]
ansible_user=ansible

# 4. CLI에서 주입 (가장 높은 우선순위)
ansible-playbook playbook/nginx.yml -e "package_name=curl"

Helm으로 비유하면 이렇습니다:

Ansible Helm
vars: 차트 내 기본값
vars_files: values.yaml
-e (CLI) --set 옵션

우선순위도 비슷합니다. CLI(-e)로 넘긴 값이 가장 높고, vars:의 기본값이 가장 낮습니다.


변수만으로는 부족한 상황

변수로 리팩토링한 playbook에 CLI로 curl을 넘겨보겠습니다.

ansible-playbook playbook/install_and_execute.yml -e "package_name=curl"
TASK [install curl] ***************************************************
ok: [node1]
ok: [node3]
ok: [node2]

TASK [curl service start] *********************************************
fatal: [node2]: FAILED! => {"changed": false, "msg": "Could not find the requested service curl: host"}
fatal: [node3]: FAILED! => {"changed": false, "msg": "Could not find the requested service curl: host"}
fatal: [node1]: FAILED! => {"changed": false, "msg": "Could not find the requested service curl: host"}

curl은 설치는 성공하지만, 서비스가 아니기 때문에 service start task에서 실패합니다.

결과를 보면:

node1 : ok=1  changed=1  unreachable=0  failed=1  skipped=0
  • ok=1 — 첫 번째 task(설치)만 성공
  • changed=1 — curl이 새로 설치됨
  • failed=1 — 두 번째 task(서비스 시작)에서 실패

서비스가 있는 패키지와 없는 패키지를 구분해서 처리할 방법이 필요합니다. 이것이 **조건문(when)**의 역할입니다.


조건문 (when)

when은 조건이 참일 때만 해당 task를 실행합니다.

서비스 패키지 목록으로 분기

서비스 시작이 필요한 패키지를 목록으로 정의하고, when으로 분기합니다:

---
- name: "Ansible {{ package_name }} install & execute"
  hosts: managed
  gather_facts: false
  vars:
    service_packages:
      - nginx
  tasks:
    - name: "install {{ package_name }}"
      become: true
      ansible.builtin.apt:
        name: "{{ package_name }}"
        state: present
    - name: "{{ package_name }} service start"
      become: true
      ansible.builtin.service:
        name: "{{ package_name }}"
        state: started
      when: package_name in service_packages
  • service_packages — 서비스 시작이 필요한 패키지 목록
  • when: package_name in service_packagespackage_name이 목록에 포함될 때만 실행

⚠️ when에서는 {{ }}를 쓰지 않는다

when은 다른 키워드와 달리 이미 Jinja2 컨텍스트에서 평가됩니다. {{ }}를 쓰면 오히려 문자열로 변환되어 의도와 다르게 동작합니다.

# OK
when: package_name in service_packages

# 잘못된 사용
when: "{{ package_name }} in {{ service_packages }}"

실행 결과 — nginx

ansible-playbook playbook/install_and_execute.yml -e "package_name=nginx"
TASK [install nginx] **********************************************
ok: [node1]
ok: [node3]
ok: [node2]

TASK [nginx service start] ****************************************
ok: [node3]
ok: [node2]
ok: [node1]

PLAY RECAP ********************************************************
node1 : ok=2  changed=0  unreachable=0  failed=0  skipped=0
node2 : ok=2  changed=0  unreachable=0  failed=0  skipped=0
node3 : ok=2  changed=0  unreachable=0  failed=0  skipped=0
  • ok=2 — 설치와 서비스 시작 모두 성공
  • changed=0 — 이미 설치되고 실행 중이므로 변경 없음 (멱등성)
  • skipped=0 — nginx는 service_packages에 포함되어 있으므로 스킵 없음

실행 결과 — curl

ansible-playbook playbook/install_and_execute.yml -e "package_name=curl"
TASK [install curl] ***********************************************
ok: [node3]
ok: [node2]
ok: [node1]

TASK [curl service start] *****************************************
skipping: [node1]
skipping: [node2]
skipping: [node3]

PLAY RECAP ********************************************************
node1 : ok=1  changed=0  unreachable=0  failed=0  skipped=1
node2 : ok=1  changed=0  unreachable=0  failed=0  skipped=1
node3 : ok=1  changed=0  unreachable=0  failed=0  skipped=1
  • ok=1 — 설치만 성공
  • skipped=1curlservice_packages에 없으므로 서비스 시작 task가 스킵
  • failed=0 — 에러 없이 정상 종료

이전에는 failed=1로 playbook이 실패했지만, when 조건을 추가한 뒤에는 서비스가 없는 패키지도 안전하게 처리됩니다.


주요 조건 표현식

when: ansible_os_family == "Debian"           # 문자열 비교
when: ansible_memtotal_mb >= 2048             # 숫자 비교
when: package_name is defined                 # 변수 존재 여부
when: package_name != "curl"                  # 불일치
when: ansible_distribution == "Ubuntu" and ansible_distribution_version == "24.04"  # AND
when: package_name == "nginx" or package_name == "apache2"                          # OR

쿠버네티스의 nodeSelectornodeAffinity로 특정 라벨의 노드에만 Pod을 배치하는 것처럼, when으로 특정 조건의 호스트에만 task를 실행할 수 있습니다.

특히 gather_facts: true로 수집한 시스템 정보(ansible_distribution, ansible_os_family 등)와 함께 사용하면, OS별로 다른 패키지 매니저를 분기하는 등 실용적인 활용이 가능합니다.