LabHub
배우기 러닝패스 코스

Ansible基礎

変数の優先順位とファクト

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

Ansible 에서 "변수가 안 먹는다"는 대부분 변수가 없어서가 아니라 더 센 곳에서 정의된 값이 이기고 있어서다.

階層図: 더 센 곳에서 정의된 값이 이기고 있어서・분기가 늘어나는 것을 막는 구조적 선택・첫째, 덮어써도 되는 값은 defaults 에 둔다.・-e 는 일회성 예외에만 쓴다.

왜 이게 필요했나

같은 애플리케이션을 dev·stage·prod 에 배포한다고 하자. 포트와 복제 수와 로그 레벨만 다르다. 이때 플레이북을 세 벌 복사하면 6개월 뒤에는 세 파일이 서로 다른 생물이 되어 있다. 값을 코드 밖으로 빼내는 것은 취향이 아니라 분기가 늘어나는 것을 막는 구조적 선택이다.

문제는 값을 넣을 수 있는 자리가 너무 많다는 점이다. 플레이의 vars, 별도 vars_files, group_vars/, host_vars/, 인벤토리 안의 변수, 명령줄 -e, 롤의 defaultsvars, 그리고 실행 중 set_fact 로 만든 값까지. 이 자리마다 우선순위가 다르고, 그 순서를 모르면 "분명히 값을 바꿨는데 반영이 안 되는" 하루를 보낸다.

어떻게 동작하나

세부 순서는 길지만 실무에서 기억할 뼈대는 짧다.

강도 자리 성격
가장 약함 롤의 defaults/main.yml "이 롤의 기본값" — 덮어쓰라고 만든 자리
약함 인벤토리 그룹 변수 환경별 공통값
중간 인벤토리 호스트 변수 그 서버만의 예외
강함 플레이의 vars, vars_files 이 실행에서의 값
매우 강함 set_fact 로 만든 값 실행 중 계산된 값
가장 강함 명령줄 -e (extra vars) 무엇이든 이긴다

두 가지 원칙만 지키면 대부분 해결된다. 첫째, 덮어써도 되는 값은 defaults 에 둔다. 둘째, -e 는 일회성 예외에만 쓴다. -e 가 습관이 되면 그 값이 어디에 기록되지 않아 다음 사람이 재현할 수 없다.

팩트(fact)는 성격이 다르다. 이것은 사람이 적는 값이 아니라 대상 서버에서 수집한 사실이다. gather_facts: true 면 플레이 시작 시 setup 모듈이 돌아 OS 종류, 아키텍처, 호스트 이름, 네트워크 인터페이스, 메모리 같은 것을 모아 ansible_facts 아래에 넣는다. 팩트를 쓰면 "우분투면 apt, RHEL 이면 dnf" 같은 분기를 대상별로 자동으로 태울 수 있다.

수집에는 비용이 있다. 호스트가 수백 대면 팩트 수집만으로 수십 초가 나간다. 팩트가 필요 없는 플레이는 gather_facts: false 로 꺼야 하고, 반복 실행이 잦다면 팩트 캐싱을 고려한다.

register 는 세 번째 종류다. 태스크의 실행 결과 전체(표준 출력, 종료 코드, changed 여부)를 변수에 담는다. 여기서 흔한 실수가 결과 객체 전체를 그대로 파일에 쓰는 것이다. 필요한 것은 대개 .stdout 하나다.

현장에서 만나는 모습

첫째, -e 로 급한 불을 끄고 코드에 반영하지 않는 것. 장애 대응 중 -e replicas=10 으로 넘겨 서비스를 살렸다면, 그 값은 반드시 저장소에 커밋되어야 한다. 그러지 않으면 다음 배포가 조용히 그것을 되돌린다. 이것은 Terraform 의 드리프트와 정확히 같은 구조의 사고다.

둘째, 팩트를 신뢰하되 검증하기. ansible_distribution 같은 팩트는 대체로 정확하지만, 컨테이너나 특수 이미지에서는 예상과 다른 값이 나온다. 팩트로 분기할 때는 예상 밖의 값에 대한 기본 분기를 남겨 둔다.

값이 어디서 왔는지 확인하는 법

우선순위를 외우는 것보다 실용적인 것은 지금 이 호스트에서 그 값이 무엇인지 직접 물어보는 것이다. 추론은 틀리지만 실측은 틀리지 않는다.

가장 빠른 방법은 태스크 하나로 값을 찍어 보는 것이다. 애드혹으로 debug 를 돌려 특정 변수를 출력하면, 그 호스트 기준으로 최종 승자가 무엇인지 바로 나온다. 그리고 여러 호스트에 한 번에 돌려 보는 것이 특히 값지다. 한 대만 값이 다르면 그 호스트의 host_vars 나 인벤토리 줄에 예외가 적혀 있다는 뜻이고, 그것이 대개 문제의 원인이다.

값이 아니라 어디서 왔는지가 궁금하면 범위를 좁혀 가며 확인한다. 그룹 변수를 잠시 지워 보고 값이 바뀌면 거기서 온 것이고, 안 바뀌면 더 센 자리에서 오는 것이다. 그리고 실행할 때 자세한 출력을 켜면 어떤 변수 파일을 읽었는지가 로그에 나오므로, 읽히지 않은 파일을 고치고 있었다는 흔한 상황을 바로 잡을 수 있다.

이름 짓기도 이 문제에 크게 작용한다. 변수 이름에 접두사를 붙이는 습관을 들이면 충돌이 거의 사라진다. port 보다 myapp_port 가 낫고, 롤의 변수는 롤 이름을 접두사로 쓴다. 짧은 이름은 어느 롤이든 쓸 수 있어서, 다른 롤이 같은 이름을 쓰는 순간 둘 중 하나가 조용히 진다.

마지막으로 어느 자리에 무엇을 둘지 팀 규칙으로 정해 두는 것이 우선순위를 외우는 것보다 효과가 크다. 환경별로 다른 값은 그룹 변수에, 서버 하나의 예외는 호스트 변수에, 롤이 제안하는 기본값은 defaults 에. 이 세 줄만 지켜지면 나머지 자리는 거의 쓸 일이 없고, 쓸 일이 없으면 우선순위 때문에 놀랄 일도 없다.

다음 실습에서 할 것

플레이 변수·변수 파일·그룹 변수·호스트 변수를 각각 만들어 어느 것이 이기는지 눈으로 확인하고, 명령줄 -e 가 그 전부를 이기는 것을 본다. 팩트를 수집해 JSON 으로 남기고, registerset_fact 로 값을 조합해 최종 요약 파일을 만든다.