루프
루프 (Loops)
Ansible은 작업을 여러 번 실행하기 위해 loop, with_<lookup>, until 키워드를 제공해요. 흔히 쓰이는 루프 예시로는 file 모듈로 여러 파일/디렉터리의 소유권을 바꾸거나, user 모듈로 여러 사용자를 만들거나, 특정 결과에 도달할 때까지 폴링 단계를 반복하는 경우가 있어요.
출처: 문서
본문
Ansible은 작업을 여러 번 실행하기 위해 loop, with_<lookup>, until 키워드를 제공해요. 흔히 쓰이는 루프 예시로는 file 모듈로 여러 파일/디렉터리의 소유권을 바꾸기, user 모듈로 여러 사용자 만들기, 특정 결과에 도달할 때까지 폴링 단계를 반복하는 것이 있어요.
참고
- Ansible 2.5에서 루프를 더 단순히 하는 방법으로
loop를 추가했어요. 대부분의 사용 사례에 이것을 권장해요.with_<lookup>사용은 폐기하지 않았어요. 그 문법은 가까운 미래에도 여전히 유효할 거예요.loop와with_<lookup>은 상호 배타적이에요.until아래에 중첩하는 것은 가능하지만, 각 루프 반복에 영향을 줘요.
루프 비교하기
until의 일반적인 사용 사례는 실패할 가능성이 있는 작업과 관련이 있고,loop와with_<lookup>은 약간의 변형이 있는 반복 작업을 위한 거예요.loop와with_<lookup>은 입력으로 사용된 목록의 항목마다 작업을 한 번씩 실행하는 반면,until은 조건이 충족될 때까지 작업을 다시 실행해요. 프로그래머 관점에서 전자는 "for 루프"이고 후자는 "while/until 루프"예요.with_<lookup>키워드는 Lookup 플러그인에 의존해요.items조차도 lookup이에요.loop키워드는with_list와 동일하며, 단순한 루프에 가장 좋은 선택이에요.loop키워드는 문자열을 입력으로 받지 않아요. 'loop에 리스트 입력 보장하기: lookup 대신 query 사용하기'를 참고하세요.until키워드는 "암묵적으로 템플릿 처리되는"({{ }}불필요) '끝 조건(end conditional)'(참/거짓을 반환하는 표현식)을 받아요. 보통 작업에 대해register한 변수를 기반으로 해요.loop_control은loop와with_<lookup>모두에 영향을 주지만until에는 영향을 주지 않아요.until은retries와delay라는 자체 동반 키워드가 있어요.- 일반적으로 'with_X에서 loop로 마이그레이션하기'에서 다루는
with_*의 모든 사용은loop를 쓰도록 업데이트할 수 있어요. with_items를loop로 바꿀 때는 주의해야 해요.with_items는 암시적인 단일 레벨 평탄화(single-level flattening)를 수행하기 때문이에요. 정확히 같은 결과를 얻으려면loop와 함께| flatten(1)을 사용해야 할 수 있어요. 예를 들어 다음과 같은 출력을 얻으려면:
다음과 같이 해야 해요:with_items: - 1 - [2,3] - 4loop: "{{ [1, [2, 3], 4] | flatten(1) }}"- 루프 안에서
lookup을 사용해야 하는with_*문은loop키워드를 쓰도록 변환하면 안 돼요. 예를 들어 다음처럼 하기보다는:
다음을 유지하는 게 더 깔끔해요:loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"with_fileglob: '*.txt'
루프 사용하기
단순 리스트 반복하기
반복 작업은 단순한 문자열 리스트에 대한 표준 루프로 작성할 수 있어요. 리스트를 작업에서 직접 정의할 수 있어요.
- name: Add several users
ansible.builtin.user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
리스트를 변수 파일이나 플레이의 'vars' 섹션에 정의한 다음, 작업에서 리스트 이름을 참조할 수 있어요.
loop: "{{ somelist }}"
위 두 예시 중 어느 쪽이든 다음 예시와 동일해요:
- name: Add user testuser1
ansible.builtin.user:
name: "testuser1"
state: present
groups: "wheel"
- name: Add user testuser2
ansible.builtin.user:
name: "testuser2"
state: present
groups: "wheel"
일부 플러그인은 리스트를 파라미터에 직접 전달할 수 있어요. yum과 apt 같은 대부분의 패키징 모듈이 이 기능을 가져요. 가능하다면 작업을 루프로 돌리는 것보다 리스트를 파라미터로 전달하는 것이 더 좋아요. 예:
- name: Optimal yum
ansible.builtin.yum:
name: "{{ list_of_packages }}"
state: present
- name: Non-optimal yum, slower and may cause issues with interdependencies
ansible.builtin.yum:
name: "{{ item }}"
state: present
loop: "{{ list_of_packages }}"
특정 모듈의 파라미터에 리스트를 전달할 수 있는지 모듈 문서를 확인하세요.
해시 리스트 반복하기
해시 리스트가 있다면 루프에서 하위 키를 참조할 수 있어요. 예:
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
조건문을 루프와 결합하면 when: 문이 각 항목마다 별도로 처리돼요. 예시는 when과 함께하는 기본 조건문을 참고하세요.
딕셔너리 반복하기
dict를 루프로 돌리려면 dict2items를 사용하세요:
- name: Using dict2items
ansible.builtin.debug:
msg: "{{ item.key }}: {{ item.value.ip_address }} {{ item.value.role }}"
loop: "{{ server_configs | dict2items }}"
vars:
server_configs:
web_01:
ip_address: "10.1.1.50"
role: "frontend"
db_01:
ip_address: "10.1.1.100"
role: "backend_db"
이 예시는 server_configs를 반복하면서 키와 선택된 중첩 필드를 출력해요.
딕셔너리의 값이 다시 딕셔너리일 때(예: 각 그룹이 gid를 담은 dict로 매핑될 때), dict2items를 적용한 후 각 루프 항목에는 item.key와 item.value 두 속성이 있다는 것을 기억하세요. 중첩 필드에는 item.value.<field>로 접근해요.
루프로 변수 등록하기
루프의 출력을 변수로 등록할 수 있어요. 예:
- name: Register loop output as a variable
ansible.builtin.shell: "echo {{ item }}"
loop:
- "one"
- "two"
register: echo
루프와 함께 register를 사용하면 변수에 담긴 데이터 구조는 모듈의 모든 응답 목록인 results 속성을 포함해요. 이것은 루프 없이 register를 사용할 때 반환되는 데이터 구조와는 달라요. results 옆에 있는 changed/failed/skipped 속성은 전체 상태를 나타내요. changed/failed는 반복 중 하나라도 변경/실패를 트리거하면 true가 되고, skipped는 모든 반복이 건너뛰어졌을 때만 true가 돼요.
{
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": "echo \"one\" ",
"delta": "0:00:00.003110",
"end": "2013-12-19 12:00:05.187153",
"invocation": {
"module_args": "echo \"one\"",
"module_name": "shell"
},
"item": "one",
"rc": 0,
"start": "2013-12-19 12:00:05.184043",
"stderr": "",
"stdout": "one"
},
{
"changed": true,
"cmd": "echo \"two\" ",
"delta": "0:00:00.002920",
"end": "2013-12-19 12:00:05.245502",
"invocation": {
"module_args": "echo \"two\"",
"module_name": "shell"
},
"item": "two",
"rc": 0,
"start": "2013-12-19 12:00:05.242582",
"stderr": "",
"stdout": "two"
}
]
}
등록된 변수를 반복해서 결과를 검사하는 이후의 루프는 이렇게 생겼을 수 있어요:
- name: Fail if return code is not 0
ansible.builtin.fail:
msg: "The command ({{ item.cmd }}) did not have a 0 return code"
when: item.rc != 0
loop: "{{ echo.results }}"
반복 중에는 현재 항목의 결과가 변수에 담겨요.
- name: Place the result of the current item in the variable
ansible.builtin.shell: echo "{{ item }}"
loop:
- one
- two
register: echo
changed_when: echo.stdout != "one"
(버전 2.21에 추가됨)
변수 등록 없이 작업 결과에 접근하거나 여러 변수를 한 번에 등록하려면 register projection을 사용할 수도 있어요. 자세한 내용은 변수 등록하기 (Registering variables)를 참고하세요.
반복 중 현재 항목의 결과는 암시적 변수 _task의 result 속성에서도 접근할 수 있어요. 이를 통해 변수 등록 없이 루프 항목 결과에 접근할 수 있어요.
- name: Place the result of the current item in the variable
ansible.builtin.shell: echo "{{ item }}"
loop:
- one
- two
changed_when: _task.result.stdout != "one"
register projection과 _task 변수에 대한 자세한 내용은 변수 등록하기 (Registering variables)를 참고하세요.
반복 중에 누적된 전체 루프 결과 목록에 접근하려면 _task 암시적 변수의 loop_result 속성을 사용할 수 있어요.
- name: Run a loop and access individual item output
ansible.builtin.shell: "{{ item }}"
register:
foo_output: _task.loop_result.results[0].stdout
bar_output: _task.loop_result.results[1].stdout
loop:
- /usr/bin/foo
- /usr/bin/bar
참고: Register projection 표현식은 작업이 완료된 후 평가되므로, 등록된 변수를 작업 후에만 사용한다면
_task.loop_result에 접근할 때default가 필요 없어요. 하지만 등록된 projection 변수를 반복 중 조건부에서 사용하려면, 그 변수는 첫 번째 반복에 존재하지 않으므로 register 표현식 자체에서default를 사용해야 해요.- name: Using registered projection in conditional during iteration ansible.builtin.shell: "{{ item }}" register: first_output: _task.loop_result.results[0].stdout | default('') # default needed to use first_output during iteration loop: [1, 2, 3] when: first_output != 'skip'조건부에서
_task를 직접 사용하는 예시는 조건문 (Conditionals)을 참고하세요.
여러 변수를 등록할 때 이름만 있는 변수 등록과 같은 기능에 접근하려면 _task 암시적 변수의 polymorphic_result 속성을 사용할 수 있어요. 반복 중에는 가장 최근 루프 반복의 결과를 담고, 이후에는 전체 작업 결과를 담아요. 이렇게 하면 다음 두 작업이 동일해져요:
- name: Run a loop and register a single variable
ansible.builtin.shell: "{{ item }}"
register: foo_bar_result
loop:
- /usr/bin/foo
- /usr/bin/bar
- name: Register the same variable in the multi-variable register format
ansible.builtin.shell: "{{ item }}"
register:
foo_bar_result: _task.polymorphic_result
loop:
- /usr/bin/foo
- /usr/bin/bar
조건이 충족될 때까지 작업 재시도하기
(버전 1.4에 추가됨)
until 키워드를 사용하면 특정 조건이 충족될 때까지 작업을 재시도할 수 있어요. 예시:
- name: Retry a task until a certain condition is met
ansible.builtin.shell: /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
이 작업은 각 시도 사이에 10초의 지연을 두고 최대 5번 실행돼요. 어떤 시도의 결과 stdout에 "all systems go"가 있으면 작업은 성공해요. "retries"의 기본값은 3이고 "delay"는 5예요.
개별 재시도의 결과를 보려면 -vv로 플레이를 실행하세요.
until로 작업을 실행하고 결과를 변수로 등록하면, 등록된 변수에는 그 작업의 재시도 횟수를 기록하는 "attempts"라는 키가 포함돼요.
until을 지정하지 않으면 작업은 성공할 때까지 재시도하지만 최대 retries 횟수까지예요 (버전 2.16에 추가됨).
until 키워드를 loop나 with_<lookup>과 결합할 수 있어요. 루프의 각 요소에 대한 작업 결과가 변수에 등록되고 until 조건에서 사용될 수 있어요. 예시:
- name: Retry combined with a loop
uri:
url: "https://{{ item }}.ansible.com"
method: GET
register: uri_output
with_items:
- "galaxy"
- "docs"
- "forum"
- "www"
retries: 2
delay: 1
until: "uri_output.status == 200"
참고: 루프에서
timeout키워드를 사용하면 작업 액션의 각 시도에 적용돼요. 자세한 내용은 TASK_TIMEOUT을 참고하세요.
암시적 _task 변수를 사용하면 변수를 등록하지 않고 until에서 현재 결과에 접근할 수 있어요:
- name: Wait for each service to be ready
ansible.builtin.command: systemctl is-active {{ item }}
loop:
- nginx
- postgresql
- redis
retries: 5
delay: 2
until: _task.result.rc == 0
참고:
_task암시적 변수와 register projection에 대한 자세한 내용은 '변수 등록하기'를 참고하세요.
인벤토리 반복하기
보통 플레이 자체가 인벤토리에 대한 루프예요. 하지만 때로는 작업이 다른 호스트 집합에 대해 같은 일을 해야 할 수도 있어요. 인벤토리 또는 그 일부를 반복하려면 ansible_play_batch나 groups 변수와 함께 일반 loop를 사용할 수 있어요.
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ groups['all'] }}"
- name: Show all the hosts in the current play
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ ansible_play_batch }}"
inventory_hostnames라는 특정 lookup 플러그인도 있는데, 이렇게 사용할 수 있어요:
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all') }}"
- name: Show all the hosts matching the pattern, ie all but the group www
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all:!www') }}"
패턴에 대한 자세한 내용은 패턴: 호스트와 그룹 타겟팅에서 찾을 수 있어요.
loop에 리스트 입력 보장하기: lookup 대신 query 사용하기
loop 키워드는 입력으로 리스트를 요구하지만, lookup 키워드는 기본적으로 쉼표로 구분된 값의 문자열을 반환해요. Ansible 2.5는 항상 리스트를 반환하는 query라는 새 Jinja2 함수를 도입했어요. 이 함수는 loop 키워드를 쓸 때 lookup 플러그인에서 더 단순한 인터페이스와 더 예측 가능한 출력을 제공해요.
wantlist=True를 사용해서 lookup이 loop에 리스트를 반환하도록 강제하거나, 대신 query를 사용할 수 있어요.
다음 두 예시는 같은 일을 해요.
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
루프에 제어 추가하기
(버전 2.1에 추가됨)
loop_control 키워드를 사용하면 루프를 유용한 방식으로 관리할 수 있어요.
label로 루프 출력 제한하기
(버전 2.2에 추가됨)
복잡한 데이터 구조를 루프로 돌리면 작업의 콘솔 출력이 엄청나게 커질 수 있어요. 표시되는 출력을 제한하려면 loop_control과 함께 label 지시문을 사용하세요.
- name: Create servers
digital_ocean:
name: "{{ item.name }}"
state: present
loop:
- name: server1
disks: 3gb
ram: 15Gb
network:
nic01: 100Gb
nic02: 10Gb
# ...
loop_control:
label: "{{ item.name }}"
이 작업의 출력은 각 item의 name 필드만 표시하고, 여러 줄짜리 {{ item }} 변수의 전체 내용은 표시하지 않아요.
참고: 이것은 콘솔 출력을 더 읽기 쉽게 만들기 위한 것이지, 민감한 데이터를 보호하기 위한 것이 아니에요.
loop에 민감한 데이터가 있다면 작업에no_log: true를 설정해서 노출을 방지하세요.
루프 안에서 일시 중지하기
(버전 2.2에 추가됨)
작업 루프의 각 항목 실행 사이의 시간(초)을 제어하려면 loop_control과 함께 pause 지시문을 사용하세요.
# main.yml
- name: Create servers, pause 3s before creating next
community.digitalocean.digital_ocean:
name: "{{ item }}"
state: present
loop:
- server1
- server2
loop_control:
pause: 3
루프에서 빠져나오기
(버전 2.18에 추가됨)
Jinja2 표현식에 기반해서 어떤 항목 후에 루프를 종료하려면 loop_control과 함께 break_when 지시문을 사용하세요.
# main.yml
- name: Use set_fact in a loop until a condition is met
vars:
special_characters: "!@#$%^&*(),.?:{}|<>"
character_set: "digits,ascii_letters,{{ special_characters }}"
password_policy: '^(?=.*\d)(?=.*[A-Z])(?=.*[{{ special_characters | regex_escape }}]).{12,}$'
block:
- name: Generate a password until it contains a digit, uppercase letter, and special character (10 attempts)
set_fact:
password: "{{ lookup('password', '/dev/null', chars=character_set, length=12) }}"
loop: "{{ range(0, 10) }}"
loop_control:
break_when:
- password is match(password_policy)
- fail:
msg: "Maximum attempts to generate a valid password exceeded"
when: password is not match(password_policy)
register projection을 사용할 때 break_when에서 등록된 변수를 참조할 수 있어요:
- name: Break after processing two items
ansible.builtin.debug:
msg: "Processing {{ item }}"
loop: [1, 2, 3, 4, 5]
loop_control:
break_when: items_processed == 2
register:
items_processed: _task.loop_result.results | length
index_var로 루프 진행 상황 추적하기
(버전 2.5에 추가됨)
루프에서 현재 위치를 추적하려면 loop_control과 함께 index_var 지시문을 사용하세요. 이 지시문은 현재 루프 인덱스를 담을 변수 이름을 지정해요.
- name: Count our fruit
ansible.builtin.debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
참고: index_var는 0부터 시작해요.
확장 루프 변수
(버전 2.8에 추가됨)
Ansible 2.8부터 loop control에 extended 옵션을 사용해서 확장 루프 정보를 얻을 수 있어요. 이 옵션은 다음 정보를 노출해요.
| 변수 | 설명 |
|---|---|
ansible_loop.allitems |
루프의 모든 항목 목록 |
ansible_loop.index |
루프의 현재 반복 (1부터 시작) |
ansible_loop.index0 |
루프의 현재 반복 (0부터 시작) |
ansible_loop.revindex |
루프 끝에서부터의 반복 횟수 (1부터 시작) |
ansible_loop.revindex0 |
루프 끝에서부터의 반복 횟수 (0부터 시작) |
ansible_loop.first |
첫 번째 반복이면 True |
ansible_loop.last |
마지막 반복이면 True |
ansible_loop.length |
루프의 항목 수 |
ansible_loop.previtem |
루프의 이전 반복 항목. 첫 번째 반복 동안에는 정의되지 않음 |
ansible_loop.nextitem |
루프의 다음 반복 항목. 마지막 반복 동안에는 정의되지 않음 |
loop_control:
extended: true
참고:
loop_control.extended를 사용하면 제어 노드에서 더 많은 메모리를 사용하게 돼요.ansible_loop.allitems가 매 루프마다 전체 루프 데이터에 대한 참조를 담고 있기 때문이에요. 메인 ansible 프로세스 안의 callback 플러그인에서 표시를 위해 결과를 직렬화할 때, 이 참조들이 역참조되어 메모리 사용량이 증가할 수 있어요.
(버전 2.14에 추가됨)
메모리 소비를 줄이기 위해 ansible_loop.allitems 항목을 비활성화하려면 loop_control.extended_allitems: false를 설정하세요.
loop_control:
extended: true
extended_allitems: false
loop_var의 이름 접근하기
(버전 2.8에 추가됨)
Ansible 2.8부터 loop_control.loop_var에 제공된 값의 이름을 ansible_loop_var 변수로 얻을 수 있어요.
롤 작성자의 경우, 필요한 loop_var 값을 정해두는 대신 루프를 허용하는 롤을 작성하면서 다음을 통해 값을 얻을 수 있어요:
"{{ lookup('vars', ansible_loop_var) }}"
중첩 루프 (Nested Loops)
이 예시들에서는 loop를 사용하고 있지만, with_<lookup>에도 같은 내용이 적용돼요.
중첩 리스트 반복하기
'중첩' 루프를 만드는 가장 간단한 방법은 루프를 중첩하지 않고, 같은 결과를 얻도록 데이터를 포맷하는 것이에요. Jinja2 표현식을 사용해서 복잡한 리스트를 반복할 수 있어요. 예를 들어 루프가 중첩 리스트를 결합하여 중첩 루프를 시뮬레이션할 수 있어요.
- name: Give users access to multiple databases
community.mysql.mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: true
password: "foo"
loop: "{{ ['alice', 'bob'] | product(['clientdb', 'employeedb', 'providerdb']) | list }}"
include_tasks로 루프 쌓기 (Stacking loops)
(버전 2.1에 추가됨)
include_tasks를 사용해서 두 개의 루프 작업을 중첩할 수 있어요. 하지만 기본적으로 Ansible은 각 루프에 대해 루프 변수 item을 설정해요. 즉 내부 중첩 루프가 외부 루프의 item 값을 덮어써요. 이를 피하려면 loop_control과 함께 loop_var로 각 루프의 변수 이름을 지정할 수 있어요.
# main.yml
- include_tasks: inner.yml
loop:
- 1
- 2
- 3
loop_control:
loop_var: outer_item
# inner.yml
- name: Print outer and inner items
ansible.builtin.debug:
msg: "outer item={{ outer_item }} inner item={{ item }}"
loop:
- a
- b
- c
참고: Ansible이 현재 루프가 이미 정의된 변수를 사용하고 있음을 감지하면 작업을 실패시키는 오류를 발생시켜요.
Until과 loop
until 조건은 loop의 item마다 적용돼요:
- debug: msg={{item}}
loop:
- 1
- 2
- 3
retries: 2
until: item > 2
이렇게 하면 Ansible이 처음 2개 항목을 2번 재시도하고, 3번째 시도에서 그 항목을 실패시키며, 3번째 항목은 첫 시도에서 성공하고, 결국 전체 작업은 실패해요.
[started TASK: debug on localhost]
FAILED - RETRYING: [localhost]: debug (2 retries left).Result was: {
"attempts": 1,
"changed": false,
"msg": 1,
"retries": 3
}
FAILED - RETRYING: [localhost]: debug (1 retries left).Result was: {
"attempts": 2,
"changed": false,
"msg": 1,
"retries": 3
}
failed: [localhost] (item=1) => {
"msg": 1
}
FAILED - RETRYING: [localhost]: debug (2 retries left).Result was: {
"attempts": 1,
"changed": false,
"msg": 2,
"retries": 3
}
FAILED - RETRYING: [localhost]: debug (1 retries left).Result was: {
"attempts": 2,
"changed": false,
"msg": 2,
"retries": 3
}
failed: [localhost] (item=2) => {
"msg": 2
}
ok: [localhost] => (item=3) => {
"msg": 3
}
fatal: [localhost]: FAILED! => {"msg": "One or more items failed"}
with_X에서 loop로 마이그레이션하기
대부분의 경우 루프는 with_X 스타일 루프보다 loop 키워드와 함께 사용할 때 가장 잘 동작해요. loop 문법은 query나 lookup을 복잡하게 사용하는 것보다 필터로 표현하는 것이 대개 가장 좋아요.
이 예시들은 많은 일반적인 with_ 스타일 루프를 loop와 필터로 변환하는 방법을 보여줘요.
with_list
with_list는 loop로 직접 대체돼요.
- name: with_list
ansible.builtin.debug:
msg: "{{ item }}"
with_list:
- one
- two
- name: with_list -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop:
- one
- two
with_items
with_items는 loop와 flatten 필터로 대체돼요.
- name: with_items
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
with_indexed_items
with_indexed_items는 loop, flatten 필터, loop_control.index_var로 대체돼요.
- name: with_indexed_items
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_indexed_items: "{{ items }}"
- name: with_indexed_items -> loop
ansible.builtin.debug:
msg: "{{ index }} - {{ item }}"
loop: "{{ items|flatten(levels=1) }}"
loop_control:
index_var: index
with_flattened
with_flattened는 loop와 flatten 필터로 대체돼요.
- name: with_flattened
ansible.builtin.debug:
msg: "{{ item }}"
with_flattened: "{{ items }}"
- name: with_flattened -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten }}"
with_together
with_together는 loop와 zip 필터로 대체돼요.
- name: with_together
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_together:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_together -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|zip(list_two)|list }}"
복잡한 데이터를 사용하는 또 다른 예시:
- name: with_together -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
loop: "{{ data[0]|zip(*data[1:])|list }}"
vars:
data:
- ['a', 'b', 'c']
- ['d', 'e', 'f']
- ['g', 'h', 'i']
with_dict
with_dict는 loop와 dictsort 또는 dict2items 필터로 대체할 수 있어요.
- name: with_dict
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ dictionary }}"
- name: with_dict -> loop (option 1)
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ dictionary|dict2items }}"
- name: with_dict -> loop (option 2)
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ dictionary|dictsort }}"
with_sequence
with_sequence는 loop와 range 함수, 그리고 잠재적으로 format 필터로 대체돼요.
- name: with_sequence
ansible.builtin.debug:
msg: "{{ item }}"
with_sequence: start=0 end=4 stride=2 format=testuser%02x
- name: with_sequence -> loop
ansible.builtin.debug:
msg: "{{ 'testuser%02x' | format(item) }}"
loop: "{{ range(0, 4 + 1, 2)|list }}"
루프의 범위는 끝점을 제외해요.
with_subelements
with_subelements는 loop와 subelements 필터로 대체돼요.
- name: with_subelements
ansible.builtin.debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
- name: with_subelements -> loop
ansible.builtin.debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
loop: "{{ users|subelements('mysql.hosts') }}"
with_nested/with_cartesian
with_nested와 with_cartesian은 loop와 product 필터로 대체돼요.
- name: with_nested
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_nested:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_nested -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|product(list_two)|list }}"
with_random_choice
with_random_choice는 loop 없이 random 필터만 사용하는 것으로 대체돼요.
- name: with_random_choice
ansible.builtin.debug:
msg: "{{ item }}"
with_random_choice: "{{ my_list }}"
- name: with_random_choice -> loop (No loop is needed here)
ansible.builtin.debug:
msg: "{{ my_list|random }}"
tags: random
더 알아보기 (Learn more)
- 조건문 (Conditionals) — 루프와 결합한 조건 처리
- 변수 등록하기 (Registering variables) —
register와_task변수, projection - Lookup 플러그인 —
query/lookup사용법 - 필터 (Filters) — 루프와 함께 쓰는
flatten,product,zip,dict2items등