Обновляйте по одному хосту

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

Введение

У Courtyard Sessions уже работает сайт на двух серверах. Новая программа готова, но одновременное обновление обоих узлов может оставить посетителей без доступного сайта. С помощью Ansible проверьте состояние и опубликуйте новый пакет по одному серверу за раз.

Нужны базовые навыки работы с каталогами в терминале и опыт использования небольшого инвентаря Ansible, переменных и задач file/copy. Шлюз, доступ по SSH и пакеты уже подготовлены. До первого применения лабораторная работа объясняет место выполнения задач, делегирование, партии узлов, ссылки на версии и полные проверки HTTP. Продолжительность оценена автором с учётом чтения и обычных исправлений.

Проверьте, где выполняется каждая задача

На этом шаге вы создадите playbook для наблюдения без изменения состояния: он покажет активную версию каждого сервера и фактический состав пула маршрутизации.

У Courtyard Sessions уже есть работающий сайт для посетителей. Откройте вкладку Website. Прочитайте Good things, around the corner. и нажмите The program. Страница прокрутится к двум мероприятиям, первое — Coffee in the courtyard. Нажмите Download the program (CSV) и откройте загруженный файл: после заголовка time,session,place идут два показанных мероприятия. CSV — текстовая таблица, в которой поля разделены запятыми. Во время обновления эта настоящая загрузка должна оставаться доступной.

Шлюз распределяет короткие HTTP-запросы между двумя серверами. Исключение сервера из активного пула прекращает передачу ему новых запросов шлюза; добавление возвращает его в чередование. Подготовленный небольшой шлюз не завершает длительные соединения перед отключением и не воспроизводит целую облачную платформу публикации. Оба сервера — контейнеры, управляемые по SSH и использующие ядро этой учебной VM. Контроллер — машина, на которой работают Ansible и шлюз.

Переключитесь на вкладку Controller. Начальный каталог — /home/labex/project. pwd показывает текущий каталог, а cat выводит файл:

pwd
cat inventory.yml
cat group_vars/all.yml

Инвентарь содержит структурированные данные, совместимые с YAML. В его группу website входят web1 и web2, каждый с реальным адресом ansible_host и подготовленными параметрами SSH. Группа control содержит controller с локальным подключением. Используйте inventory.yml, который выбран проектным ansible.cfg; исходный inventory.ini образа не является инвентарём курса.

Переменные группы задают backend_port (8090), доступный только на контроллере pool_api, разрешённый release_root, локальный package_root и идентификаторы версий. baseline_release — courtyard-preview, а approved_release — courtyard-weekend. Это имена каталогов, а не версии Ansible или программного обеспечения. Порт HTTP обозначает сервис, слушающий по определённому адресу. /identity возвращает JSON — структурированный текст, определяющий реально работающую версию. /pool возвращает текущий список активных узлов контроллера.

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

По умолчанию Ansible выполняет задачу по SSH на каждом выбранном сервере. Там 127.0.0.1 означает сам этот сервер. delegate_to: controller выполняет конкретную задачу на контроллере, где собственный 127.0.0.1 обслуживает API пула. Делегирование не меняет inventory_hostname: он по-прежнему обозначает текущий управляемый сервер. Поэтому запрос с контроллера может относиться к правильному серверу.

Создайте inspect.yml. cat > filename записывает файл, заменяя существующий файл с тем же именем. <<'YAML' начинает here-document: вставляйте текст до завершающего YAML на отдельной строке. Разделитель в кавычках сохраняет вставленный текст буквально; Ansible обрабатывает выражения при выполнении playbook. Для отступов используются пробелы. {{ variable }} подставляет переменную Ansible. gather_facts: false отключает сбор не относящихся к упражнению сведений о машине.

Модуль uri отправляет настоящий HTTP-запрос; его метод по умолчанию GET читает ресурс. register сохраняет реальный результат под заданным именем. Для ответа JSON поле .json содержит разобранные значения. debug выводит сохранённые наблюдения; он не создаёт версию или состояние пула.

cat > inspect.yml <<'YAML'
- name: Inspect the running website without changing it
  hosts: website
  gather_facts: false
  tasks:
    - name: Read this backend on its own loopback address
      ansible.builtin.uri:
        url: "http://127.0.0.1:{{ backend_port }}/identity"
      register: backend
    - name: Read the pool on the controller
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
      delegate_to: controller
      register: pool
    - name: Show the actual release and active pool
      ansible.builtin.debug:
        msg:
          node: "{{ inventory_hostname }}"
          release: "{{ backend.json.release }}"
          active: "{{ pool.json.active }}"
YAML

Запустите playbook. ansible-playbook читает задачи и применяет их к инвентарю:

ansible-playbook inspect.yml

Каждый сервер должен показать release: courtyard-preview, а active должен включать web1 и web2. Форматирование и адреса могут отличаться. Заметьте: первый uri выполняется на сервере, а чтение пула показывает делегирование вроде web1 -> controller. Обе задачи только читают состояние.

Если отступы YAML неверны, снова скопируйте полный блок, исправив файл, и повторите запуск. Этот playbook никогда не должен копировать файлы или менять пул. Теперь у вас есть полезный инструмент для повторного наблюдения после публикации.

Публикуйте по одному серверу за раз

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

Оставайтесь во вкладке Controller, в /home/labex/project. Посмотрите список пакетов и прочитайте утверждённую программу. ls перечисляет содержимое каталога:

ls packages
cat packages/courtyard-weekend/schedule.csv

Утверждённая программа содержит три мероприятия: Bread and stories, Acoustic afternoon и Print a postcard. Правильной главной страницы недостаточно: посетителям нужен соответствующий полный загружаемый файл. Пакет courtyard-incomplete оставлен для следующей лабораторной работы; здесь выбирайте courtyard-weekend, как уже задано переменной release_id.

Playbook обрабатывает партию из одного хоста с помощью serial: 1. Внутри партии он исключает узел из пула, копирует пакет, меняет ссылку на активную версию, проверяет оба ресурса и возвращает узел. Только после этого начинается следующая партия. order: sorted начинает демонстрацию с web1. any_errors_fatal: true явно распространяет необработанную ошибку, чтобы остановить play. В этих партиях из одного хоста его отказ уже означает отказ всей партии. Параметр не отменяет завершённые задачи. Следующая лабораторная работа добавляет восстановление.

API пула принимает HTTP POST к /pool с телом JSON: node обозначает управляемый узел, а enabled — логическое значение false для исключения или true для добавления. body_format: json правильно сериализует тело. Задачи пула выполняются на контроллере, файловые задачи — на выбранном SSH-сервере.

file с state: directory создаёт каталог; режим '0755' позволяет владельцу записывать, а всем — читать и проходить каталог. copy передаёт выбранный пакет с контроллера на сервер. Завершающий / в src копирует содержимое, используя режим '0644' для читаемых файлов. file с state: link создаёт или обновляет символическую ссылку: current указывает на каталог версии. Подготовленный HTTP-сервер при каждом запросе читает настоящие файлы через эту ссылку. Её смена активирует сайт без перезапуска сервера и сохраняет исходный каталог.

В HTTP-пробах на контроллере hostvars[inventory_hostname].ansible_host явно получает адрес управляемого сервера. При делегировании переменная подключения без такой ссылки может относиться к делегированному хосту. return_content: true сохраняет настоящее тело в .content.

assert требует истинности выражения that. lookup('file', ...) читает ожидаемый файл пакета на контроллере; ~ соединяет строки пути. rstrip=False сохраняет завершающие переводы строк для сравнения полного тела, а не обрезанного фрагмента. До возврата узла должны совпасть вся главная страница кандидата и весь утверждённый CSV. Код 404 означает, что сервер ответил, но ресурс отсутствует; ошибка соединения означает недоступность сервиса. Ни то ни другое не подтверждает исправную версию.

Запишите полный сценарий развёртывания. Here-document использует тот же приём, что и инструмент наблюдения:

cat > rollout.yml <<'YAML'
- name: Release one website backend at a time
  hosts: website
  order: sorted
  gather_facts: false
  serial: 1
  any_errors_fatal: true
  tasks:
    - name: Drain this backend from the pool
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
        method: POST
        body_format: json
        body:
          node: "{{ inventory_hostname }}"
          enabled: false
      delegate_to: controller
    - name: Create this release directory
      ansible.builtin.file:
        path: "{{ release_root }}/releases/{{ release_id }}"
        state: directory
        mode: '0755'
    - name: Copy the selected package over SSH
      ansible.builtin.copy:
        src: "{{ package_root }}/{{ release_id }}/"
        dest: "{{ release_root }}/releases/{{ release_id }}/"
        mode: '0644'
    - name: Activate the selected release
      ansible.builtin.file:
        src: "{{ release_root }}/releases/{{ release_id }}"
        dest: "{{ release_root }}/current"
        state: link
    - name: Read the real candidate homepage
      ansible.builtin.uri:
        url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
        return_content: true
      delegate_to: controller
      register: homepage
    - name: Require the complete candidate homepage
      ansible.builtin.assert:
        that: homepage.content == lookup('file', package_root ~ '/' ~ release_id ~ '/index.html', rstrip=False)
    - name: Read the real candidate download
      ansible.builtin.uri:
        url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
        return_content: true
      delegate_to: controller
      register: download
    - name: Require the complete approved program
      ansible.builtin.assert:
        that: download.content == lookup('file', package_root ~ '/' ~ approved_release ~ '/schedule.csv', rstrip=False)
    - name: Rejoin only after both business checks
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
        method: POST
        body_format: json
        body:
          node: "{{ inventory_hostname }}"
          enabled: true
      delegate_to: controller
YAML

Сначала проверьте YAML и структуру задач без развёртывания. --syntax-check проверяет синтаксис, но не доказывает функциональный результат:

ansible-playbook rollout.yml --syntax-check

При ошибке написания или отступов снова скопируйте весь файл и повторите проверку. Затем выполните настоящее развёртывание:

ansible-playbook rollout.yml

Задачи web1 должны завершиться до задач web2. Оба хоста должны закончить с failed=0 и unreachable=0. Операционные запросы исключения или добавления могут показывать ok; успешный HTTP-ответ и реальный эффект важнее счётчика changed в сводке. Если ошибка редактирования прерывает выполнение, исправьте весь файл и повторите запуск с тем же утверждённым пакетом. Повторный запуск снова исключает, проверяет и возвращает узлы; это не развёртывание без операций.

Проверьте состояние исходным инструментом:

ansible-playbook inspect.yml

Оба узла должны сообщать courtyard-weekend и быть активными в пуле. Переключитесь на Website и обновите страницу. Заголовок должен быть A little room for a good afternoon. Нажмите The program, проверьте три мероприятия, снова загрузите CSV и откройте новый файл, чтобы подтвердить три полные строки. При наличии предыдущей загрузки браузер может добавить номер к имени. Plan a visit по-прежнему должна вести к информации о входе без ступенек через сад.

Обновлённая программа содержит три утверждённых мероприятия; настоящая загрузка CSV находится рядом со списком.

Обновлённый сайт Courtyard Sessions с тремя мероприятиями программы

Это демонстрирует функциональное обновление двух узлов в подготовленной среде коротких запросов. Оно не устанавливает гарантию публикации без перерывов для произвольного трафика или производственного приложения.

Резюме

Вы создали инструмент наблюдения без изменения состояния и выполнили последовательное развёртывание Ansible, доставив настоящий сайт и полную загрузку его программы. Делегированные изменения пула и реальные проверки связывают каждое решение о публикации с нужным управляемым сервером.