Introdução
A página inicial do Riverlight Weekend está pronta, mas os visitantes não conseguem abrir a programação nem a página do local. Os dois serviços de origem já funcionam. Use o Ansible para conectar a entrada aos membros selecionados do inventário, recarregar apenas quando a configuração mudar e proteger as rotas funcionais contra uma substituição inadequada.
Você precisa de navegação básica no terminal e prática na leitura de um inventário, variáveis e um playbook pequenos do Ansible. O laboratório explica papéis HTTP, grupos, variáveis entre hosts, JSON, consultas somente de leitura e handlers antes de usá-los. Todo o software e os serviços estão preparados. O tempo é uma estimativa do autor, incluindo leitura e correções comuns.
Conecte as páginas de programação e visita
Nesta etapa, você conectará o site do evento às origens preparadas de programação e local, usando membros do inventário em vez de endereços fixos em um modelo.
O Riverlight Weekend tem uma página inicial e um guia funcionais. Os links de programação e visita mostram, por enquanto, uma mensagem de página indisponível. A entrada é um pequeno proxy reverso HTTP preparado: encaminha uma solicitação a outro serviço e devolve sua resposta real. Ela não copia os dados da programação para a página inicial. A entrada roda no controlador; os dois nós de origem são contêineres que compartilham o kernel desta VM de prática. Não são três VMs independentes. Software e serviços estão prontos; você gerencia apenas as conexões da entrada.
Abra o Tab Event Site. A página inicial deve mostrar A little closer to the river. Clique em Explore the program: This page is taking a break. é a resposta HTTP 502 esperada porque a rota ainda não está conectada. Use Back to the weekend na página e depois Visitor guide na navegação. Essa página já funciona e deve continuar disponível.
Mude para Controller, que começa em /home/labex/project. pwd mostra o diretório atual:
pwd
Se perder o diretório de trabalho, volte com cd /home/labex/project. Um nome de arquivo incorreto causa “No such file or directory”; confira a grafia, use ls templates para listar o diretório se necessário e tente novamente. Não é preciso reinicializar a VM.
cat mostra um arquivo. Leia a seleção aprovada, o inventário e os dados de cada host:
cat approved-services.txt
cat inventory.ini
cat host_vars/web1.yml
cat host_vars/web2.yml
Os grupos [program] e [venue] escolhem uma origem cada. web1 fornece a programação atual; web2 fornece o local. Os arquivos de host oferecem o service_address real e as portas dos serviços. Os endereços podem mudar entre VMs. Alguns nós também têm um serviço de arquivo histórico; estar acessível não o torna a programação atual aprovada.
O grupo [gateway] contém entry. ansible_connection=local significa que o Ansible gerencia a entrada no controlador, sem uma conexão SSH adicional. /usr/bin/python3 é o interpretador Python local preparado. Uma porta HTTP identifica um serviço que escuta em um endereço; um nó pode executar vários serviços em portas distintas.
Antes de mudar a entrada, leia diretamente cada origem selecionada. Os arquivos de configuração curl preparados contêm as URLs reais. --config lê opções de um arquivo; fail faz erros HTTP resultarem em falha, silent oculta o indicador de progresso e show-error mantém mensagens úteis. Esses arquivos não contêm senha nem token.
cat requests/web1.conf
curl --config requests/web1.conf
cat requests/web2.conf
curl --config requests/web2.conf
As respostas JSON identificam program e venue, o site riverlight e suas edições. JSON é texto estruturado com campos nomeados. Uma solicitação bem-sucedida comprova que a origem funciona diretamente, mas ainda não que as rotas do site funcionam.
Crie o modelo de rotas. groups do Ansible associa nomes de grupo aos membros; [0] escolhe o primeiro membro de um grupo que contém apenas um. hostvars recupera suas variáveis. O playbook abaixo chama esses membros de program_host e venue_host. Nas expressões Jinja, ~ une valores como texto, incluindo portas numéricas. | to_json produz uma string JSON corretamente delimitada por aspas, evitando montá-las manualmente de forma insegura.
O comando shell usa > para substituir somente este arquivo. <<'EOF' entrega as linhas seguintes literalmente ao cat; as aspas simples impedem a interpretação das chaves Jinja pelo shell. Cole até o fechamento EOF em sua própria linha. Se esquecer o delimitador e o shell ficar aguardando continuação, pressione Ctrl+C e cole novamente o comando completo.
cat > templates/routes.json.j2 <<'EOF'
{
"program": {{ ('http://' ~ hostvars[program_host].service_address ~ ':' ~ hostvars[program_host].program_port) | to_json }},
"venue": {{ ('http://' ~ hostvars[venue_host].service_address ~ ':' ~ hostvars[venue_host].venue_port) | to_json }}
}
EOF
Escreva o playbook da entrada. gather_facts: false evita coletar informações de máquina não utilizadas. vars define expressões avaliadas com o inventário atual. template gera o arquivo, aplica o modo 0644 (o proprietário pode escrever; todos podem ler) e notifica um handler apenas quando o arquivo de rotas muda. Um handler é uma tarefa executada após uma notificação, normalmente no fim do play.
A entrada guarda as rotas em memória; substituir só o arquivo não basta. O auxiliar preparado envia um sinal SIGHUP real apenas ao processo existente desta entrada e espera sua confirmação. O command do handler usa argv, uma lista de argumentos separados. playbook_dir é o diretório do playbook e mantém o caminho gerenciado dentro deste projeto. Você não precisa de sudo nem reiniciar o serviço.
cat > connect.yml <<'EOF'
- name: Connect the event services
hosts: gateway
gather_facts: false
vars:
program_host: "{{ groups['program'][0] }}"
venue_host: "{{ groups['venue'][0] }}"
tasks:
- name: Write the inventory-derived routes
ansible.builtin.template:
src: templates/routes.json.j2
dest: "{{ playbook_dir }}/entry/routes.json"
mode: '0644'
notify: Reload entry
handlers:
- name: Reload entry
ansible.builtin.command:
argv:
- python3
- /opt/labex/service-links/reload.py
- "{{ playbook_dir }}/entry"
EOF
Verifique primeiro a estrutura YAML e depois visualize a mudança prevista. --syntax-check não aplica tarefas. --check prevê alterações compatíveis; --diff mostra o conteúdo antigo e o proposto. Essa prévia não envia sinal ao gateway nem comprova o funcionamento do site final.
ansible-playbook connect.yml --syntax-check
ansible-playbook connect.yml --check --diff
O diff deve substituir as rotas desconectadas 127.0.0.1:9 pelos endereços e portas atuais. Aplique o playbook normalmente:
ansible-playbook connect.yml
Você deve ver a alteração do modelo e RUNNING HANDLER [Reload entry]. Volte a Event Site, use Program e leia as três sessões atuais: Paper and ink, Acoustic afternoon e Shared supper. Clique em Download the schedule para baixar schedule.csv; horários, sessões e locais devem ser os mesmos da tabela. Siga Visit até Riverside Yard e depois Visitor guide. A página inicial e o guia devem continuar funcionando.
A programação conectada deve mostrar essas três sessões, com Download the schedule logo abaixo da tabela:

Volte a Controller. Você também pode salvar e examinar o download real dentro da VM. No curl, -f falha em erros HTTP, -sS esconde o progresso, mas mostra erros, e -o define o arquivo de saída:
curl -fsS http://127.0.0.1:8081/program/schedule.csv -o schedule.csv
cat schedule.csv
Conteúdo esperado:
time,session,place
10:30,Paper and ink,Making table
13:00,Acoustic afternoon,River stage
16:00,Shared supper,Long table
127.0.0.1 significa este controlador, onde a entrada escuta na porta 8081. O endereço público do navegador encaminha ao mesmo serviço.
Execute o mesmo playbook novamente:
ansible-playbook connect.yml
O resumo deve indicar changed=0 e failed=0, sem executar o handler. Isso é convergência: a declaração atual já corresponde ao arquivo. As páginas e o download reais comprovam o resultado utilizável; o resumo sozinho não basta.
Proteja as rotas funcionais antes de substituí-las
Nesta etapa, você adicionará verificações reais de identidade HTTP antes da escrita das rotas e rejeitará um serviço de arquivo histórico acessível preservando o site funcional.
Continue em Controller, no diretório /home/labex/project. HTTP 200 significa que o servidor respondeu com sucesso; não estabelece o tipo de serviço, evento ou edição. A programação e o local atuais se identificam por /identity. O arquivo preparado group_vars/all.yml contém o site e as edições aprovados:
cat group_vars/all.yml
Adicione uma verificação prévia, isto é, anterior à mudança. assert para o play quando uma condição é falsa. A primeira asserção exige exatamente um membro por grupo de serviço. Cada tarefa uri faz uma solicitação HTTP GET real a partir do controlador. return_content: true mantém o corpo; uma resposta JSON também fica disponível em .json. register salva o resultado sob um nome de variável para tarefas seguintes. As asserções finais comparam identidades reais com os valores aprovados.
As tarefas HTTP usam check_mode: false de propósito: essas solicitações somente de leitura devem executar também na prévia. Caso contrário, uma tarefa uri pode ser ignorada no modo de verificação. A opção pertence só às consultas de leitura; não permite aplicar um modelo ou recarregar durante a prévia. O limite de três segundos evita que uma origem ausente bloqueie o exercício por muito tempo.
Substitua o playbook por esta versão completa com verificações. Mantenha o modelo e todas as asserções de identidade antes da tarefa de modelo:
cat > connect.yml <<'EOF'
- name: Connect the event services
hosts: gateway
gather_facts: false
vars:
program_host: "{{ groups['program'][0] }}"
venue_host: "{{ groups['venue'][0] }}"
tasks:
- name: Require one member for each service
ansible.builtin.assert:
that:
- groups['program'] | length == 1
- groups['venue'] | length == 1
- name: Read the selected program identity
ansible.builtin.uri:
url: "http://{{ hostvars[program_host].service_address }}:{{ hostvars[program_host].program_port }}/identity"
return_content: true
timeout: 3
check_mode: false
register: program_probe
- name: Read the selected venue identity
ansible.builtin.uri:
url: "http://{{ hostvars[venue_host].service_address }}:{{ hostvars[venue_host].venue_port }}/identity"
return_content: true
timeout: 3
check_mode: false
register: venue_probe
- name: Check the actual service identities
ansible.builtin.assert:
that:
- program_probe.json.service == 'program'
- program_probe.json.site == event_site
- program_probe.json.edition == program_edition
- venue_probe.json.service == 'venue'
- venue_probe.json.site == event_site
- venue_probe.json.edition == venue_edition
- name: Write the inventory-derived routes
ansible.builtin.template:
src: templates/routes.json.j2
dest: "{{ playbook_dir }}/entry/routes.json"
mode: '0644'
notify: Reload entry
handlers:
- name: Reload entry
ansible.builtin.command:
argv:
- python3
- /opt/labex/service-links/reload.py
- "{{ playbook_dir }}/entry"
EOF
ansible-playbook connect.yml --syntax-check
Salve agora uma impressão digital do arquivo de rotas funcional. sha256sum calcula um hash do conteúdo; > salva a saída separadamente para comparação posterior:
sha256sum entry/routes.json > routes-before.sha256
O inventário alternativo seleciona um arquivo histórico como programação. Leia-o e teste-o deliberadamente com -i, que escolhe um inventário apenas para esse comando. O inventário normal não é alterado:
cat inventory-wrong-service.ini
ansible-playbook -i inventory-wrong-service.ini connect.yml
Espera-se falha com código diferente de zero e failed=1. O arquivo histórico responde e retorna JSON, mas sua edição não está aprovada. A asserção deve falhar antes de Write the inventory-derived routes; o resumo deve mostrar changed=0, sem executar o handler. Isso difere de uma conexão recusada, na qual uri não consegue obter resposta alguma. Ambos os casos devem impedir a escrita.
Confira a impressão digital salva. -c pede ao sha256sum que compare o arquivo atual ao hash registrado:
sha256sum -c routes-before.sha256
Espere entry/routes.json: OK. Em Event Site, abra Program e baixe novamente o horário atual. Uma proposta rejeitada deve deixar a programação utilizável, não apenas um arquivo presente.
Retorne a Controller e use o inventário normal:
ansible-playbook connect.yml --check --diff
ansible-playbook connect.yml
As duas identidades escolhidas devem passar. As rotas já corretas dispensam mudanças: o resumo indica changed=0 e o handler não executa. Se surgir erro de indentação YAML, compare o bloco completo, use espaços em vez de tabulações e reescreva connect.yml. Erro de sintaxe não significa que a verificação prévia rejeitou corretamente uma origem.
Seu escopo é connect.yml, templates/routes.json.j2 e as rotas de entrada por meio desse playbook. Conserve inventários fornecidos, dados dos hosts, edições aprovadas, arquivos curl, conteúdo das origens, anotações da organização, página inicial e guia. Use /opt/labex/service-links/reload.py com o diretório de entrada deste projeto para recarregar. Não fixe nomes ou endereços atuais no modelo ou nas consultas, não copie páginas de origem para a entrada, não oculte falhas, não reinicie os nós de origem nem edite o gateway preparado. Os membros e portas configurados devem continuar determinando a conexão quando o inventário mudar.
Resumo
Você conectou serviços reais de programação e local por rotas derivadas do inventário, examinou o site e seu download CSV exato e usou um handler para recarregar somente configurações alteradas. Depois rejeitou uma origem acessível, mas não aprovada, antes da escrita das rotas, preservando o site atual. O próximo laboratório independente muda os membros e mantém a origem anterior e seus dados.



