PYY0715's Tech Blog v3.1.0

Search the Post!

[Ansible for Kubespray] 4. Loop, Conditions

Cloudnet@ K8S Deploy — Week2

Introduction

지난 글에서 Playbook, Variables, Facts를 다루었습니다. 하지만 실제 인프라 자동화에서는 동일한 작업을 여러 대상에 반복하거나, 특정 조건에서만 작업을 수행해야 하는 경우가 많습니다.

이때 다수의 사용자 생성이나 패키지 설치와 같은 반복 작업을 위해 동일한 Task를 여러 번 작성하는 것은 코드의 유지보수성을 크게 떨어뜨립니다. 대신 반복문(Loops)을 활용하면 하나의 Task 정의만으로도 여러 대상을 효율적으로 관리할 수 있습니다.

이번 글에서는 이러한 loop를 활용한 반복 실행과, 상황에 따라 Task 실행 여부를 결정하는 when을 사용한 조건부 실행을 다룹니다.

Loops

반복문은 동일한 모듈을 여러 번 호출해야 하는 비효율을 해결해 줍니다. Ansible 2.5 이전에는 with_items, with_file등의 with_ 접두사를 사용하여 반복문을 구현할 수 있었습니다. 하지만 Ansible 2.5부터는 loop를 사용하여 반복문을 작성할 수 있습니다.

Basic Loop

가장 기본적인 형태는 리스트를 반복하는 것입니다. loop 키워드에 리스트를 제공하면, Ansible은 각 항목을 {{ item }} 변수에 할당하여 Task를 반복 실행합니다.

---
- name: Install essential packages
  ansible.builtin.apt:
      name: "{{ item }}"
      state: present
  loop:
      - git
      - curl
      - vim

위 코드는 git, curl, vim 패키지를 순차적으로 설치합니다.

Loop with Dictionaries

단순 문자열이 아니라, 여러 속성을 가진 복잡한 데이터를 반복해야 할 때는 리스트 안에 딕셔너리(Key-Value)를 넣어서 사용합니다. 이때는 item.key 또는 item['key'] 형태로 접근합니다.

---
- name: Dictionary Loop Example
  hosts: all
  become: true
  tasks:
      - name: Ensure groups exist
        ansible.builtin.group:
            name: "{{ item }}"
            state: present
        loop:
            - admin
            - developer

      - name: Create users with specific groups
        ansible.builtin.user:
            name: "{{ item.name }}"
            groups: "{{ item.groups }}"
            state: present
        loop:
            - { name: "alice", groups: "admin" }
            - { name: "bob", groups: "developer" }

Loop with Register Variable

반복문의 실행 결과를 변수에 저장하여 재사용할 수 있습니다. loopregister를 함께 사용하면, 결과 변수 안에 results라는 리스트가 생성되며, 이곳에 각 반복 회차별 실행 결과가 저장됩니다.

---
- name: Play for echoing numbers
  hosts: localhost
  tasks:
      - name: Loop echo test
        ansible.builtin.shell: "echo {{ item }}"
        changed_when: false
        loop:
            - one
            - two
        register: echo_results

      - name: Show result
        ansible.builtin.debug:
            var: echo_results.results

echo_results 리스트의 각 항목은 해당 회차의 item 값과 실행 결과(stdout, rc, changed 등)를 모두 포함하고 있습니다.

Register1

echo_results.results의 실행 결과는 배열 형식이기 때문에 특정 값을 Playbook에서 사용할 경우, 이 역시 아래와 같이 Loop를 사용하여 제어할 수 있습니다.

---
- name: Play for echoing numbers
  hosts: localhost
  tasks:
      - name: Loop echo test
        ansible.builtin.shell: "echo {{ item }}"
        changed_when: false
        loop:
            - one
            - two
        register: echo_results

      - name: Show result
        ansible.builtin.debug:
            msg: "Item {{ item.item }} output: {{ item.stdout }}"
        loop: "{{ echo_results.results }}"

Register2

Conditionals

Playbook을 작성하다 보면 모든 호스트에 똑같은 작업을 수행하는 것이 아니라, 특정 조건에 따라 다른 작업을 수행해야 할 때가 있습니다. 이때 when 구문을 사용합니다.

When Statement

when은 특정 조건이 True일 때만 Task를 실행하게 합니다. 조건이 거짓이면 해당 Task는 Skipped 상태가 됩니다.

---
- name: Conditional Example
  hosts: all
  become: yes
  tasks:
      - name: Install nginx on Debian/Ubuntu
        ansible.builtin.apt:
            name: nginx
            state: present
        when: ansible_facts['os_family'] == "Debian"

      - name: Install nginx on RedHat/CentOS
        ansible.builtin.yum:
            name: nginx
            state: present
        when: ansible_facts['os_family'] == "RedHat"

Debian 운영체제인 tnode1tnode2는 2번째 Task에서 Skipped 상태가 되었고, tnode3은 1번째 Task에서 Skipped 상태가 되었습니다.

Conditional Conditional

Comparison Operators

when 절에서는 다양한 비교 연산자를 사용하여 조건을 상세하게 제어할 수 있습니다.

Operator Description Example
==, != 같음 / 다름 ansible_facts['distribution'] == "Ubuntu"
> , >= 크다 / 이상 ansible_facts['memtotal_mb'] > 1024
in 포함됨 ansible_facts['distribution'] in ['Ubuntu', 'Debian']
is defined 변수 존재 여부 my_var is defined
not 부정 not is_completed

여러 조건을 동시에 만족해야 할 때는 리스트 형태로 작성하면 AND 조건을 구현할 수 있습니다.

---
- hosts: all

  tasks:
      - name: Print os type
        ansible.builtin.debug:
            msg: >-
                OS Type: {{ ansible_facts['distribution'] }}
                OS Version: {{ ansible_facts['distribution_version'] }}
        when: >
            ( ansible_facts['distribution'] == "Rocky" and
              ansible_facts['distribution_version'] == "9.6" )
            or
            ( ansible_facts['distribution'] == "Ubuntu" and
              ansible_facts['distribution_version'] == "24.04" )

>는 줄바꿈을 공백으로 연결하여 한 줄로 만듭니다. 주로 긴 조건문을 여러 줄로 작성할 때 사용합니다.

|-\|는 줄바꿈을 그대로 유지합니다. 여러 줄의 문자열을 작성할 때 사용합니다. 개행문자의 유무로 구분합니다.

Challenges

이번 실습에서는 4가지 도전과제를 통해 반복문과 조건문의 활용법을 익힙니다.

  1. 반복문(range)을 사용하여 10명 Linux User 생성 후, 삭제
  2. 반복문(sequence)을 사용하여 대량의 로그 파일 생성 후, 삭제
  3. Ubuntu OS이면서 fqdn으로 tnode1 인 경우, debug 모듈을 사용하여 OS 정보와 fqdn 정보를 출력
  4. 반복문+조건문을 함께 사용

Mission1

신규 입사자 10명의 계정을 생성해야 한다고 가정해 봅시다. range() 함수를 사용하여 user1부터 user10까지 생성 후, 삭제할 수 있습니다.

---
- name: Create/Remove multiple users
  hosts: all
  become: true
  tasks:
      - name: Create user1 to user10
        ansible.builtin.user:
            name: "user{{ item }}"
            state: present
        loop: "{{ range(1, 11) | list }}"

      - name: Remove user1 to user10
        ansible.builtin.user:
            name: "user{{ item }}"
            state: absent
        loop: "{{ range(1, 11) | list }}"

Mission2

Sequence를 활용하여 /var/log/test1.log부터 test100.log까지 100개의 파일을 생성하고, 삭제합니다.

---
- name: Create/Remove 100 test files
  hosts: all
  become: yes
  tasks:
      - name: Create files
        ansible.builtin.file:
            path: "/var/log/test{{ item }}.log"
            state: touch
            mode: "0644"
        loop: "{{ query('sequence', 'start=1 end=100') }}"

      - name: Remove files
        ansible.builtin.file:
            path: "/var/log/test{{ item }}.log"
            state: absent
        loop: "{{ query('sequence', 'start=1 end=100') }}"

Mission3

Ubuntu OS이면서 fqdn으로 tnode1 인 경우, debug 모듈을 사용하여 OS 정보와 fqdn 정보를 출력합니다.

---
- name: Check specific host
  hosts: all
  tasks:
      - name: Display info for Ubuntu tnode1
        ansible.builtin.debug:
            msg:
                - "OS: {{ ansible_facts['distribution'] }}"
                - "FQDN: {{ ansible_facts['fqdn'] }}"
        when:
            - ansible_facts['distribution'] == 'Ubuntu'
            - ansible_facts['fqdn'] == 'tnode1'

Mission 4

Loop에서 조건문을 함께 사용할 수도 있습니다.

- name: Run with items greater than 5
  hosts: localhost

  tasks:
      - name: Run with items greater than 5
        ansible.builtin.command: echo {{ item }}
        loop: [0, 2, 4, 6, 8, 10]
        when: item > 5
Why don't you read something next?
[Ansible for Kubespray] 5. Handlers and Notifications

[Ansible for Kubespray] 5. Handlers and Notifications

Share

Comments