LabHub
배우기 러닝패스 코스

Ansible 실전 · 컬렉션 - 롤보다 큰 배포 단위 · 이론

컬렉션 - 이름에 출처가 적혀 있는 이유

LabHub 에서 이어서 보기

한 줄 요약

컬렉션은 롤·모듈·플러그인을 한 덩어리로 묶어 판(version)을 붙여 배포하는 단위이고, ansible.builtin.copy 같은 긴 이름은 그 덩어리의 출처를 이름 안에 적어 둔 것이다.

왜 이게 필요했나

2019년까지 Ansible 은 모듈 수천 개를 본체에 함께 담아 배포했다. mysql_user 하나를 고치려면 Ansible 전체가 새 판을 내야 했고, 반대로 Ansible 을 올리면 쓰지도 않는 모듈 수천 개가 함께 바뀌었다. 커뮤니티 모듈의 관리자와 Ansible 코어 관리자가 같은 저장소에서 부딪혔고, 릴리스는 점점 무거워졌다.

이름 충돌도 실제 사고였다. 사내에서 만든 deploy 롤이 있는데 누군가 같은 이름의 롤을 roles/ 에 하나 더 두면, 실행된 것이 어느 쪽인지 로그만 보고는 알 수 없었다. 검색 경로의 순서가 결과를 정했고, 그 순서는 사람이 아니라 환경이 정했다.

Ansible 2.9 에서 도입된 컬렉션은 이 두 문제를 한 번에 손댄다. 내용물을 <네임스페이스>.<이름> 으로 묶고, 그 묶음에 SemVer 판을 붙이고, 태스크에서 부를 때는 <네임스페이스>.<이름>.<모듈> 이라는 완전한 이름(FQCN) 으로 부른다. 이름을 보면 출처를 알 수 있고, 같은 짧은 이름이 둘이어도 FQCN 은 겹치지 않는다.

어떻게 동작하나

컬렉션 소스는 정해진 디렉터리 모양을 가진다. ansible-galaxy collection init <네임스페이스>.<이름> 이 그 뼈대를 만들어 준다.

| 자리 | 담는 것 |
| --- | --- |
| galaxy.yml | 네임스페이스·이름·판·저자·라이선스·태그. 빌드가 이 파일을 읽는다 |
| roles/ | 롤. 롤 안의 규약은 기존과 똑같다 |
| plugins/ | 모듈·필터·룩업·콜백 같은 플러그인. 종류마다 하위 디렉터리가 정해져 있다 |
| playbooks/ | 컬렉션이 함께 배포하는 플레이북 |
| meta/runtime.yml | 최소 ansible 판(requires_ansible), 이름 바꾸기(plugin_routing), 액션 그룹 |
| docs/ | 문서 |

galaxy.ymlversionSemVer 여야 한다. 1.2 처럼 두 자리로 적으면 빌드가 거절한다. 이 규칙이 있어야 >=1.2.0,<2.0.0 같은 범위 표기가 뜻을 가진다.

배포는 세 걸음이다.

ansible-galaxy collection init acme.platform --init-path src   # 뼈대ansible-galaxy collection build --output-path dist             # 묶음 만들기ansible-galaxy collection install dist/acme-platform-1.2.0.tar.gz -p collections

build 가 만드는 것은 그냥 tar.gz 가 아니다. 안에 MANIFEST.json(메타데이터)과 FILES.json(파일마다의 체크섬)이 함께 들어간다. 설치할 때 이 목록으로 무결성을 확인하므로, 사내 파일 서버에 묶음만 올려 두어도 "우리가 만든 그 판이 맞는가"를 기계가 판정할 수 있다.

설치할 판은 명령줄이 아니라 파일에 적는다.

# requirements.ymlcollections:  - name: acme.platform    version: "1.2.0"           # Galaxy 나 사내 저장소에서 받을 때  - name: /srv/artifacts/acme-platform-1.2.0.tar.gz    type: file                 # 로컬 묶음 파일을 그대로 설치할 때

ansible-galaxy collection install -r requirements.yml -p <경로> 로 설치한다. 고정 파일이 있으면 소스 저장소가 1.3.0 으로 앞서 나가도 설치되는 것은 1.2.0 이다. 이 한 줄이 컬렉션을 도입하는 가장 큰 이유다 - 저장소의 최신 상태와 배포되는 판이 분리된다.

찾는 순서도 알아 둘 값어치가 있다. ansible 은 ANSIBLE_COLLECTIONS_PATH(또는 ansible.cfg 의 collections_path)에 적힌 경로들과 플레이북 옆의 collections/ 디렉터리를 본다. 플레이에 collections: 키워드를 적으면 그 컬렉션들을 짧은 이름으로 부를 수 있지만, 공식 문서는 FQCN 을 권한다. 짧은 이름은 검색 순서에 기대는 방식이고, 그 순서는 환경이 정하기 때문이다.

현장에서 만나는 모습

첫째, "설치했는데 못 찾는다". 거의 언제나 경로 문제다. -p 로 설치한 경로가 실행할 때의 검색 경로에 들어 있지 않으면 ansible 은 그 컬렉션을 모른다. ansible-galaxy collection list -p <경로> 로 "거기 있는가"를, ansible-doc -t role <FQCN> 로 "여기서 보이는가"를 따로 확인하는 습관이 시간을 아낀다.

둘째, 롤 인자 명세. meta/argument_specs.yml 에 인자의 타입·기본값·허용값을 적어 두면, 롤이 첫 태스크를 돌기 전에 인자를 검증하고 틀리면 그 자리에서 멈춘다. 남이 쓸 롤을 배포한다면 이것이 문서이자 방어선이다. 명세가 없으면 잘못된 값은 다섯 번째 태스크쯤에서 엉뚱한 오류로 터진다.

셋째, 사내 배포. Galaxy 에 올릴 수 없는 코드가 대부분이다. 실무에서는 CI 가 build 한 묶음을 사내 파일 서버나 Artifactory 에 올리고, 각 팀의 requirements.yml 이 그 주소와 판을 가리킨다. 파이프라인은 실행 직전에 install -r requirements.yml 을 한 번 돌린다. 이 구조에서는 "어제 되던 것이 오늘 안 되는" 일이 줄어든다 - 판이 파일에 적혀 있기 때문이다.

넷째, 의존성은 공짜가 아니다. galaxy.ymldependencies: 에 다른 컬렉션을 적으면 설치할 때 함께 끌어오는데, 이는 끌어올 수 있는 저장소가 있을 때의 이야기다. 인터넷이 막힌 망에서는 의존 컬렉션의 묶음까지 손으로 함께 날라야 한다. 이 실습 환경도 그런 망이라 의존성 자동 해결은 다루지 않는다.

다음 실습에서 할 것

acme.platform 컬렉션을 뼈대부터 만들어 galaxy.yml 을 채우고, 그 안에 롤 하나를 넣고, meta/argument_specs.yml 로 인자를 강제하고, 묶음을 빌드해 내용물과 MANIFEST 를 확인하고, 로컬 경로에 설치해 ansible-doc 으로 조회하고, FQCN 으로 불러 실행한다. 마지막에는 소스의 판을 1.3.0 으로 올린 뒤 requirements.yml 이 1.2.0 을 그대로 지키는 것을 눈으로 본다.

참고 문서