Подключите страницы мероприятия

AnsibleBeginner
Практиковаться сейчас

Введение

Главная страница Riverlight Weekend готова, но посетители не могут открыть программу и страницу площадки. Оба сервиса-источника уже работают. С помощью Ansible подключите входной сервис к выбранным участникам инвентаря, перечитывайте конфигурацию только при изменениях и защитите рабочие маршруты от неподходящей замены.

Нужны базовая навигация в терминале и практика чтения небольшого инвентаря, переменных и playbook Ansible. Роли HTTP-сервисов, группы, переменные других узлов, JSON, запросы только для чтения и обработчики объясняются до применения. Все программы и сервисы мероприятия подготовлены. Длительность оценена автором с учётом чтения и обычных исправлений.

Подключите страницы программы и площадки

На этом шаге вы подключите сайт мероприятия к подготовленным источникам программы и площадки, используя участников инвентаря вместо фиксированных адресов в шаблоне.

У Riverlight Weekend работают главная страница и памятка посетителю. Ссылки на программу и площадку пока показывают сообщение о недоступности страницы. Входной сервис — небольшой подготовленный обратный HTTP-прокси: он пересылает запрос другому сервису и возвращает его настоящий ответ. Данные программы не копируются на главную страницу. Входной сервис работает на контроллере; два источника — контейнеры, разделяющие ядро этой учебной VM. Это не три независимые VM. Программы и сервисы готовы; вы управляете только подключениями входного сервиса.

Откройте вкладку Event Site. Главная страница должна показывать A little closer to the river. Нажмите Explore the program: сообщение This page is taking a break. — ожидаемый ответ HTTP 502, поскольку маршрут ещё не подключён. Вернитесь по Back to the weekend, затем откройте Visitor guide в навигации. Эта страница уже работает и должна оставаться доступной.

Перейдите во вкладку Controller, которая начинается в /home/labex/project. pwd выводит текущий каталог:

pwd

Если потеряете рабочий каталог, вернитесь командой cd /home/labex/project. Ошибка в имени файла вызывает «No such file or directory»: проверьте написание, при необходимости посмотрите содержимое каталога через ls templates и повторите. Сбрасывать VM не нужно.

cat выводит файл. Прочитайте одобренный выбор сервисов, инвентарь и данные каждого узла:

cat approved-services.txt
cat inventory.ini
cat host_vars/web1.yml
cat host_vars/web2.yml

Группы [program] и [venue] выбирают по одному источнику. web1 предоставляет текущую программу, а web2 — площадку. Файлы узлов содержат настоящие service_address и порты. Адреса могут различаться между VM. Некоторые узлы также предлагают архивный сервис; доступность ещё не делает его одобренной текущей программой.

Группа [gateway] содержит entry. ansible_connection=local означает, что Ansible управляет входным сервисом на контроллере без дополнительного SSH-перехода. /usr/bin/python3 — подготовленный локальный интерпретатор Python. HTTP-порт обозначает слушающий сервис по адресу; один узел может запускать несколько сервисов на разных портах.

До изменения входного сервиса прочитайте каждый выбранный источник напрямую. Подготовленные конфигурации curl содержат настоящие URL. --config читает параметры curl из файла; fail превращает ошибки HTTP в неуспех команды, silent скрывает индикатор прогресса, а show-error сохраняет полезные сообщения. В этих файлах нет паролей или токенов.

cat requests/web1.conf
curl --config requests/web1.conf
cat requests/web2.conf
curl --config requests/web2.conf

Ответы JSON идентифицируют program и venue, сайт riverlight и их редакции. JSON — структурированный текст с именованными полями. Успешный запрос доказывает прямую работу источника, но ещё не работу маршрутов сайта.

Теперь создайте шаблон маршрутов. Переменная groups в Ansible сопоставляет имена групп их участникам; [0] выбирает первого участника группы, в которой он один. hostvars получает его переменные. Следующий playbook называет этих участников program_host и venue_host. В выражениях Jinja оператор ~ соединяет значения как текст, включая числовые порты. | to_json формирует JSON-строку с корректными кавычками, избегая их небезопасной ручной сборки.

Команда shell использует > для замены только этого файла. <<'EOF' передаёт следующие строки в cat буквально; одинарные кавычки не дают shell интерпретировать скобки Jinja. Вставьте всё до завершающего EOF на отдельной строке. Если пропущенный разделитель оставил shell ждать продолжения, нажмите Ctrl+C и вставьте полную команду заново.

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

Напишите playbook для входного сервиса. gather_facts: false отключает сбор ненужных сведений о машине. vars определяет выражения, вычисляемые по текущему инвентарю. template создаёт файл, устанавливает режим 0644 (владелец может писать, читать могут все) и уведомляет обработчик только при изменении файла маршрутов. Обработчик — задача, запускаемая после уведомления, обычно в конце play.

Входной сервис хранит маршруты в памяти, поэтому одной замены файла недостаточно. Подготовленный помощник отправляет настоящий сигнал SIGHUP только существующему процессу этого входного сервиса и ждёт подтверждения. command обработчика использует argv — список отдельных аргументов. playbook_dir обозначает каталог playbook и ограничивает управляемый путь входного сервиса этим проектом. Ни sudo, ни перезапуск сервиса не нужны.

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

Сначала проверьте структуру YAML, затем просмотрите прогноз изменения файла. --syntax-check не применяет задачи. --check прогнозирует поддерживаемые изменения; --diff показывает прежний и предлагаемый текст. Просмотр не отправляет сигнал шлюзу и не доказывает работу готового сайта.

ansible-playbook connect.yml --syntax-check
ansible-playbook connect.yml --check --diff

Разница должна заменить неподключённые маршруты 127.0.0.1:9 адресами и портами текущих источников. Примените playbook обычным образом:

ansible-playbook connect.yml

Ожидаются изменение шаблона и RUNNING HANDLER [Reload entry]. Вернитесь в Event Site, откройте Program и прочитайте три текущих пункта: Paper and ink, Acoustic afternoon и Shared supper. Нажмите Download the schedule, чтобы скачать schedule.csv: время, названия и места должны совпадать с таблицей. Через Visit откройте Riverside Yard, затем Visitor guide. Главная страница и памятка должны продолжать работать.

Подключённая программа должна показывать эти три пункта, а Download the schedule — находиться сразу под таблицей:

Текущая программа Riverlight и скачивание расписания

Вернитесь в Controller. Настоящий файл можно также сохранить и прочитать в VM. В curl параметр -f вызывает неуспех при HTTP-ошибке, -sS скрывает прогресс, но показывает ошибки, а -o задаёт выходной файл:

curl -fsS http://127.0.0.1:8081/program/schedule.csv -o schedule.csv
cat schedule.csv

Ожидаемое содержимое:

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 означает этот контроллер, где входной сервис слушает порт 8081. Общедоступный адрес в браузере перенаправляет к тому же сервису.

Повторите тот же playbook:

ansible-playbook connect.yml

Итог должен показать changed=0 и failed=0 без запуска обработчика. Это сходимость: текущая декларация уже соответствует файлу. Работоспособность результата подтверждают настоящие страницы и скачивание, а не один итог выполнения.

Защитите рабочие маршруты до замены

На этом шаге вы добавите настоящие проверки HTTP-идентичности до записи маршрутов, затем отклоните доступный архивный сервис, сохранив рабочий сайт.

Продолжайте в Controller, в /home/labex/project. HTTP 200 означает успешный ответ сервера, но не подтверждает тип сервиса, мероприятие или редакцию. Текущие программа и площадка идентифицируются через /identity. В подготовленном group_vars/all.yml записаны одобренные сайт и редакции:

cat group_vars/all.yml

Добавьте предварительную проверку, то есть проверку до изменения. assert останавливает play при ложном условии. Первая проверка требует ровно одного участника в каждой группе сервисов. Каждая задача uri отправляет настоящий HTTP GET с контроллера. return_content: true сохраняет тело, а JSON-ответ также доступен через .json. register сохраняет результат задачи под именем переменной для следующих задач. Последние проверки сравнивают настоящую идентичность с одобренными значениями.

HTTP-задачи намеренно используют check_mode: false: запросы только для чтения должны работать и при предварительном просмотре. Иначе обычная задача uri может пропускаться в режиме проверки. Настройка относится только к чтению и не разрешает применять шаблон или перечитывать конфигурацию при просмотре. Тайм-аут в три секунды не позволяет отсутствующему источнику надолго задержать упражнение.

Замените playbook следующей полной версией с защитой. Сохраните шаблон и расположите все проверки идентичности перед задачей шаблона:

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

Сохраните отпечаток рабочего файла маршрутов. sha256sum вычисляет хеш содержимого, а > сохраняет результат отдельно для последующего сравнения:

sha256sum entry/routes.json > routes-before.sha256

Подготовленный альтернативный инвентарь выбирает архив как программу. Прочитайте его и намеренно попробуйте с -i, выбирающим инвентарь только для этой команды. Обычный файл инвентаря не изменяется:

cat inventory-wrong-service.ini
ansible-playbook -i inventory-wrong-service.ini connect.yml

Ожидается неуспех с ненулевым кодом и failed=1. Архив доступен и возвращает JSON, но его редакция не одобрена. Проверка должна завершиться неуспехом до Write the inventory-derived routes; итог должен показать changed=0 без запуска обработчика. Это отличается от отказа соединения, при котором uri вообще не получает ответ. Оба случая должны остановить запись.

Проверьте сохранённый отпечаток. -c поручает sha256sum сравнить текущий файл с записанным хешем:

sha256sum -c routes-before.sha256

Ожидается entry/routes.json: OK. В Event Site откройте Program и снова скачайте текущее расписание. Отклонённое предложение должно оставить программу доступной, а не просто сохранить какой-либо файл.

Вернитесь в Controller и используйте обычный инвентарь:

ansible-playbook connect.yml --check --diff
ansible-playbook connect.yml

Обе выбранные идентичности должны пройти проверку. Уже правильным маршрутам не нужны изменения: итог показывает changed=0, обработчик не запускается. При ошибке отступов YAML сравните полный блок, используйте пробелы вместо табуляции и перепишите connect.yml. Ошибка синтаксиса не является успешным отклонением источника предварительной проверкой.

Ваши границы — connect.yml, templates/routes.json.j2 и маршруты входного сервиса через этот playbook. Сохраните предоставленные инвентари, данные узлов, одобренные редакции, запросы curl, содержимое источников, заметки организаторов, главную страницу и памятку. Для перечитывания используйте /opt/labex/service-links/reload.py с каталогом входного сервиса этого проекта. Не фиксируйте текущие имена узлов или адреса в шаблоне и запросах, не копируйте страницы во входной сервис, не скрывайте ошибки, не перезапускайте источники и не изменяйте подготовленный шлюз. При изменении инвентаря подключение должны по-прежнему определять заданные участники и порты.

Резюме

Вы подключили настоящие сервисы программы и площадки маршрутами из инвентаря, проверили сайт и точный CSV, а обработчик перечитывал только изменённую конфигурацию. Затем вы отклонили доступный, но неодобренный источник до записи маршрутов, сохранив текущий сайт. Следующая независимая лабораторная меняет участников сервиса, оставляя старый источник и его данные.