소개
템플릿 자동화는 하나의 소스를 유지하면서 각 호스트의 입력으로 구성을 생성합니다. 변수와 팩트로 파일을 생성하고, 내장 Jinja 필터로 헤더를 변환하며, 조건 분기를 선택하고, 핸들러로 검증된 nginx 구성을 로드합니다. 실제 HTTP 요청과 잘못된 후보의 거부를 통해 소스 텍스트, 관리 파일, 실제 서비스 동작의 차이를 확인합니다.
호스트 변수와 팩트를 관리 콘텐츠로 렌더링하기
이 단계에서는 페이지 템플릿 하나를 만들고 같은 소스로 두 대상에 서로 다른 실제 파일을 렌더링합니다.
준비된 노드에는 사전 요구 사항으로 nginx가 설치되어 있습니다. 여러분은 패키지를 다시 설치하는 대신 구성을 렌더링하고 적용합니다. 템플릿은 각 대상의 변수 컨텍스트를 사용하여 컨트롤러에서 렌더링된 다음 관리 대상으로 전송됩니다.
컨트롤러 프로젝트로 이동하고 templates 디렉터리를 만듭니다. mkdir -p는 필요하면 디렉터리를 만들고 이미 있어도 실패하지 않습니다.
cd /home/labex/project
mkdir -p templates
유형이 지정된 호스트 입력을 정의합니다. 환경과 수신 포트가 다르며 따옴표 없는 maintenance_mode 값은 YAML 부울입니다.
cat > host_vars/web1.yml <<'YAML'
environment_label: development
service_port: 8081
maintenance_mode: true
YAML
cat > host_vars/web2.yml <<'YAML'
environment_label: production
service_port: 8082
maintenance_mode: false
YAML
이중 중괄호 안의 Jinja 표현식은 텍스트에 값을 삽입합니다. .j2 접미사는 템플릿 소스에 유용한 관례이며 관리 대상 파일 이름의 필수 조건은 아닙니다. 현재 인벤토리 이름, 환경 변수, 발견된 배포판 팩트를 사용하는 소스 하나를 만듭니다.
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
렌더링에는 template 모듈을 사용합니다. copy는 보통 이 Jinja 표현식을 해석하지 않고 소스 바이트를 전송합니다. 배포판 값을 사용하기 전에 팩트를 수집하고 관리 소유권과 권한을 명시합니다.
cat > templates.yml <<'YAML'
- name: Render host-specific website content
hosts: all
gather_facts: true
become: true
tasks:
- name: Prepare the rendered site directory
ansible.builtin.file:
path: /var/www/template-site
state: directory
owner: root
group: root
mode: "0755"
- name: Render each host's website content
ansible.builtin.template:
src: templates/page.txt.j2
dest: /var/www/template-site/index.html
owner: root
group: root
mode: "0644"
YAML
두 관리 페이지를 렌더링합니다.
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
실제 대상 파일을 읽습니다.
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
각 페이지는 자신의 호스트, 맞는 development/production 환경, distribution=Ubuntu를 식별해야 합니다. 컨트롤러에는 여전히 재사용 소스 하나가 있지만 두 관리 출력은 다릅니다.
내장 필터 및 조건부 구성 분기 추가하기
이 단계에서는 각 호스트 입력에 따라 HTTP 동작을 바꾸는 유효한 nginx 구성을 렌더링합니다.
Jinja 필터는 파이프 기호로 값을 변환합니다. upper는 텍스트를 대문자로 바꾸는 내장 필터이며 사용자 정의 플러그인이 아닙니다. Jinja 제어문은 퍼센트와 중괄호 구분자를 사용합니다. 아래 if/else/endif 분기는 부울 입력에 따라 유지보수 헤더 하나를 선택합니다.
완전한 nginx 구성 템플릿을 만듭니다. 포트, 환경 헤더, 유지보수 헤더는 모두 호스트별 데이터에서 나옵니다. root와 index 설정은 이미 렌더링한 페이지를 제공합니다.
cat > templates/nginx.conf.j2 <<'JINJA'
events {}
http {
server_tokens off;
server {
listen {{ service_port }};
root /var/www/template-site;
index index.html;
add_header X-Environment "{{ environment_label | upper }}" always;
{% if maintenance_mode %}
add_header X-Maintenance "enabled" always;
{% else %}
add_header X-Maintenance "disabled" always;
{% endif %}
}
}
JINJA
기존 플레이북에 구성 및 서비스 태스크와 이름 있는 재로드 핸들러를 덧붙입니다. template 태스크의 validate 명령은 관리 구성을 대체하기 전에 임시 렌더링 후보(%s)를 테스트합니다. 유효한 구성이 변경되면 핸들러에 알리며 일반 service 태스크는 nginx 실행을 유지합니다.
cat >> templates.yml <<'YAML'
- name: Render and validate the nginx candidate before replacement
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
validate: /usr/sbin/nginx -t -c %s
notify: Reload nginx
- name: Keep the rendered site service running
ansible.builtin.service:
name: nginx
state: started
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
YAML
전체 플레이북을 적용합니다.
ansible-playbook templates.yml
구성 태스크는 두 호스트를 변경하고 재로드 핸들러를 실행해야 합니다. web1의 포트 8081과 web2의 포트 8082를 점검합니다. curl -i는 본문과 함께 응답 헤더를 포함합니다.
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
web1은 X-Environment: DEVELOPMENT와 X-Maintenance: enabled를, web2는 X-Environment: PRODUCTION과 X-Maintenance: disabled를 반환해야 합니다. 두 본문 모두 올바른 호스트별 페이지를 유지합니다. 헤더 날짜는 달라지므로 데이터 기반 값과 성공 상태를 비교하세요.
입력 변경 및 잘못된 후보 거부하기
이 단계에서는 템플릿을 편집하지 않고 영구 입력을 갱신하고 실제 실행 결과를 관찰하며 검증이 작동 중인 구성을 보호함을 증명합니다.
기존 포트는 유지하면서 web1의 저장된 환경을 staging으로, 유지보수 모드를 false로 바꿉니다.
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
같은 소스 템플릿을 적용합니다.
ansible-playbook templates.yml
web1의 페이지와 구성은 변경되고 재로드 핸들러에 알려야 하며 web2는 변경되지 않아야 합니다. web1을 다시 점검합니다.
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
본문에는 environment=staging이, 헤더에는 X-Environment: STAGING과 X-Maintenance: disabled가 있어야 합니다. 입력이 바뀌어도 템플릿은 재사용 가능한 상태로 유지되었습니다.
이제 후보 거부를 테스트합니다. cp는 유효한 컨트롤러 소스를 복원할 수 있게 저장합니다. nginx 템플릿만 의도적으로 잘못된 텍스트로 대체합니다.
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
다음 플레이북 실행은 후보 검증에 실패해야 합니다. 셸의 ||는 0이 아닌 종료 후에만 메시지를 실행하여 이 의도적인 실패 연습을 계속할 수 있게 합니다.
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
태스크 실패를 읽으세요. nginx는 임시 후보를 거부해야 합니다. 관리 중인 정상 구성이 그대로 유지되어야 하므로 두 실제 서비스는 여전히 올바르게 응답해야 합니다.
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
mv는 저장한 유효 소스를 원래 파일 이름으로 복원합니다. 전체 플레이북을 한 번 더 실행합니다.
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
두 호스트 모두 재로드 핸들러가 예약되지 않은 채 changed=0, failed=0, unreachable=0을 보고해야 합니다. 검증은 잘못된 후보 사본을 원하는 최종 상태로 만들지 않고 실제 구성을 보호했습니다.
요약
template 모듈로 호스트 변수와 팩트를 렌더링하고 내장 upper 필터 및 부울 Jinja 분기를 사용했으며 핸들러로 검증된 구성을 적용했습니다. 소스를 다시 쓰지 않고도 입력 변경이 서로 다른 실제 출력을 만들었습니다. 잘못된 후보는 대체 전에 거부되었고 복원된 유효 템플릿은 추가 변경 없이 수렴했습니다.


