Ansible 아티팩트 재사용하기
Ansible 아티팩트 재사용하기 (Reusing Ansible artifacts)
간단한 플레이북은 하나의 아주 큰 파일로 작성할 수 있고, 대부분의 사용자가 그 '한 파일' 방식을 먼저 배워요. 하지만 자동화 작업을 더 작은 파일들로 나누는 것은 복잡한 작업 집합을 정리하고 재사용하는 훌륭한 방법이에요. 더 작고 분산된 아티팩트를 사용하면 같은 변수, 작업, 플레이를 여러 플레이북에서 재사용해서 다양한 사용 사례를 처리할 수 있어요.
출처: 문서
본문
간단한 플레이북은 하나의 아주 큰 파일로 작성할 수 있고, 대부분의 사용자가 한 파일 방식부터 배워요. 하지만 자동화 작업을 더 작은 파일들로 나누는 것은 복잡한 작업 집합을 정리하고 재사용하는 훌륭한 방법이에요. 더 작고 분산된 아티팩트를 사용하면 같은 변수, 작업, 플레이를 여러 플레이북에서 재사용해서 서로 다른 사용 사례를 다룰 수 있어요. 분산 아티팩트를 여러 부모 플레이북에 걸쳐 쓰거나, 심지어 하나의 플레이북 안에서 여러 번 쓸 수도 있어요. 예를 들어 고객 데이터베이스를 업데이트하는 일을 여러 다른 플레이북의 일부로 하고 싶다고 해볼게요. 데이터베이스 업데이트와 관련된 모든 작업을 작업 파일이나 롤에 넣어 두면, 한 곳에서만 유지 관리하면서도 여러 플레이북에서 재사용할 수 있어요.
재사용 가능한 파일과 롤 만들기
Ansible은 네 가지 분산·재사용 가능한 아티팩트를 제공해요: 변수 파일, 작업 파일, 플레이북, 롤.
- 변수 파일(variables file)은 변수만 담아요.
- 작업 파일(task file)은 작업만 담아요.
- 플레이북은 최소한 하나의 플레이를 포함하고, 변수·작업·기타 내용을 담을 수 있어요. 목적이 분명한 플레이북은 재사용할 수 있지만, 정적으로만 재사용할 수 있고 동적으로는 불가능해요.
- 롤(role)은 정의된 파일 트리 안에 관련된 작업, 변수, 기본값, 핸들러, 심지어 모듈이나 다른 플러그인 집합을 담아요. 변수 파일·작업 파일·플레이북과 달리 롤은 Ansible Galaxy를 통해 쉽게 업로드하고 공유할 수 있어요. 롤 생성과 사용에 대한 자세한 내용은 롤 (Roles)을 참고하세요.
(버전 2.4에 추가됨)
플레이북 재사용하기
여러 플레이북을 메인 플레이북에 통합할 수 있어요. 다만 플레이북을 재사용할 때는 import만 사용할 수 있어요. 예:
- import_playbook: webservers.yml
- import_playbook: databases.yml
import는 다른 플레이북의 플레이북을 정적으로 통합해요. Ansible은 각 import된 플레이북의 플레이와 작업을 나열된 순서대로 실행하는데, 마치 메인 플레이북에 직접 정의된 것처럼 동작해요.
import할 플레이북 파일 이름을 변수로 정의한 뒤, --extra-vars나 vars 키워드로 그 변수를 전달하면 런타임에 어떤 플레이북을 import할지 선택할 수 있어요. 예:
- import_playbook: "/path/to/{{ import_from_extra_var }}"
- import_playbook: "{{ import_from_vars }}"
vars:
import_from_vars: /path/to/one_playbook.yml
이 플레이북을 ansible-playbook my_playbook -e import_from_extra_var=other_playbook.yml로 실행하면, Ansible은 one_playbook.yml과 other_playbook.yml을 둘 다 import해요.
플레이북을 롤로 바꿔야 할 때
일부 사용 사례에서는 단순한 플레이북이 잘 동작해요. 하지만 일정 수준 이상의 복잡도에서는 롤이 플레이북보다 더 잘 맞아요. 롤을 쓰면 기본값, 핸들러, 변수, 작업을 하나의 긴 문서 대신 별도 디렉터리에 저장할 수 있어요. 롤은 Ansible Galaxy에서 공유하기 쉬워요. 복잡한 사용 사례에서는 대부분의 사용자가 올인원 플레이북보다 롤이 읽고, 이해하고, 유지 관리하기 더 쉽다고 생각해요.
파일과 롤 재사용하기
Ansible은 플레이북에서 파일과 롤을 재사용하는 두 가지 방법을 제공해요: 동적(dynamic)과 정적(static).
- 동적 재사용을 하려면 플레이의 tasks 섹션에
include_*작업을 추가하세요: - 정적 재사용을 하려면 플레이의 tasks 섹션에
import_*작업을 추가하세요:
작업 include와 import 문은 임의의 깊이에서 사용할 수 있어요.
플레이 레벨에서 베어 roles 키워드를 여전히 사용해서 롤을 플레이북에 정적으로 통합할 수 있어요. 다만 한때 작업 파일과 플레이북 레벨 include 모두에 쓰였던 베어 include 키워드는 이제 폐기(deprecated)됐어요.
Include: 동적 재사용
롤, 작업, 변수를 include하면 플레이북에 동적으로 추가돼요. Ansible은 포함된 파일과 롤을 플레이북에서 나타나는 순서대로 처리해요. 따라서 include된 작업은 최상위 플레이북 안의 앞선 작업 결과에 영향을 받을 수 있어요. include된 롤과 작업은 핸들러와 비슷해요. 최상위 플레이북의 다른 작업 결과에 따라 실행될 수도 있고 안 될 수도 있어요.
include_* 문의 주요 장점은 루프예요. include와 함께 루프를 사용하면, include된 작업이나 롤이 루프의 각 항목마다 한 번씩 실행돼요.
include된 롤, 작업, 변수의 파일 이름은 include되기 전에 템플릿 처리돼요.
include에 변수를 전달할 수 있어요. 변수 상속과 우선순위에 대한 자세한 내용은 변수 우선순위: 변수는 어디에 둬야 할까요?를 참고하세요.
Import: 정적 재사용
롤, 작업, 플레이북을 import하면 플레이북에 정적으로 추가돼요. Ansible은 플레이북의 어떤 작업을 실행하기 전에 import된 파일과 롤을 전처리해요. 따라서 import된 내용은 최상위 플레이북 안의 다른 작업에 절대 영향을 받지 않아요.
import된 롤과 작업의 파일 이름은 템플릿을 지원하지만, Ansible이 import를 전처리할 때 그 변수들이 사용 가능해야 해요. 이는 vars 키워드나 --extra-vars로 처리할 수 있어요.
import에 변수를 전달할 수 있어요. import된 파일을 플레이북에서 한 번 이상 실행하려면 변수를 반드시 전달해야 해요. 예:
tasks:
- import_tasks: wordpress.yml
vars:
wp_user: timmy
- import_tasks: wordpress.yml
vars:
wp_user: alice
- import_tasks: wordpress.yml
vars:
wp_user: bob
변수 상속과 우선순위에 대한 자세한 내용은 변수 우선순위: 변수는 어디에 둬야 할까요?를 참고하세요.
include와 import 비교: 동적 vs 정적 재사용
분산된 Ansible 아티팩트를 재사용하는 두 접근법에는 각각 장점과 한계가 있어요. 어떤 플레이북에는 동적 재사용을, 다른 플레이북에는 정적 재사용을 선택할 수 있어요. 하나의 플레이북에서 동적과 정적 재사용을 모두 쓸 수는 있지만, 플레이북마다 한 가지 방식을 선택하는 것이 좋아요. 정적과 동적 재사용을 섞으면 진단하기 어려운 버그가 플레이북에 생길 수 있어요. 아래 표는 주요 차이점을 정리한 것이므로, 만드는 각 플레이북에 가장 좋은 방식을 고를 수 있어요.
| Include_* | Import_* | |
|---|---|---|
| 재사용 유형 | 동적 | 정적 |
| 처리 시점 | 런타임, 만날 때 | 플레이북 파싱 중 전처리 |
| 작업 또는 플레이 | 모든 include는 작업 | import_playbook은 작업이 될 수 없음 |
| 작업 옵션 | include 작업 자체에만 적용 | import 안의 모든 자식 작업에 적용 |
| 루프에서 호출 | 루프 항목마다 한 번씩 실행 | 루프에서 사용할 수 없음 |
--list-tags 사용 |
include 안의 태그는 나열되지 않음 | 모든 태그가 --list-tags에 표시됨 |
--list-tasks 사용 |
include 안의 작업은 나열되지 않음 | 모든 작업이 --list-tasks에 표시됨 |
| 핸들러 통지 | include 안의 핸들러를 트리거할 수 없음 | 개별 import된 핸들러를 트리거할 수 있음 |
--start-at-task 사용 |
include 안의 작업에서 시작할 수 없음 | import된 작업에서 시작할 수 있음 |
| 인벤토리 변수 사용 | include_*: {{ inventory_var }} 가능 |
import_*: {{ inventory_var }} 불가능 |
| 플레이북과 함께 | include_playbook 없음 |
전체 플레이북 import 가능 |
| 변수 파일과 함께 | 변수 파일 include 가능 | 변수 import에는 vars_files: 사용 |
참고: 리소스 소비와 성능에도 큰 차이가 있어요. import는 꽤 가볍고 빠른 반면, include는 많은 관리와 회계(accounting)가 필요해요.
핸들러로 작업 재사용하기
플레이북의 핸들러: 변경 시 작업 실행 (Handlers) 섹션에서도 include와 import를 사용할 수 있어요. 예를 들어 Apache를 재시작하는 방법을 정의하고 싶다면, 모든 플레이북을 위해 그것을 한 번만 정의하면 돼요. 다음과 같은 restarts.yml 파일을 만들 수 있어요:
# restarts.yml
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
- name: Restart mysql
ansible.builtin.service:
name: mysql
state: restarted
import 또는 include 어느 쪽에서도 핸들러를 트리거할 수 있지만, 재사용 방식에 따라 절차가 달라요. 파일을 include하면 include 자체를 notify해야 하는데, 그러면 restarts.yml의 모든 작업이 트리거돼요. 파일을 import하면 restarts.yml 안의 개별 작업을 notify해야 해요. 직접 작업 및 핸들러와 include/import된 작업 및 핸들러를 섞어 쓸 수도 있어요.
include된(동적) 핸들러 트리거하기
include는 런타임에 실행되므로 플레이 실행 중에는 include의 이름이 존재하지만, include 자체가 트리거되기 전까지는 include된 작업이 존재하지 않아요. 동적 재사용으로 Restart apache 작업을 사용하려면 include 자체의 이름을 참조하세요. 이 접근법은 include된 파일의 모든 작업을 핸들러로 트리거해요. 예를 들어 위에 보인 작업 파일은 다음과 같이 써요:
- name: Trigger an included (dynamic) handler
hosts: localhost
handlers:
- name: Restart services
include_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart services
import된(정적) 핸들러 트리거하기
import는 플레이가 시작되기 전에 처리되므로 플레이 실행 중에는 import의 이름이 더 이상 존재하지 않지만, 개별 import된 작업의 이름은 존재해요. 정적 재사용으로 Restart apache 작업을 사용하려면 import된 파일 안의 각 작업 이름을 참조하세요. 예를 들어 위에 보인 작업 파일은 다음과 같이 써요:
- name: Trigger an imported (static) handler
hosts: localhost
handlers:
- name: Restart services
import_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart apache
- command: "true"
notify: Restart mysql
더 알아보기 (Learn more)
- 롤 (Roles) — 롤 생성과 사용 자세히 보기
- 핸들러: 변경 시 작업 실행 —
include_tasks/import_tasks로 핸들러 구성하기 - 유틸리티 모듈 (Utilities modules) — 여기서 다룬
include*/import*모듈 문서 - Galaxy 사용자 가이드 — Galaxy에서 롤 공유하기