Стадия жизненного цикла модуля: Experimental
У модуля есть требования для установки

На этой странице приведены готовые сценарии работы с модулем ansible. Сценарии не зависят друг от друга, и каждый из них содержит манифест, а также описание того, что появляется в статусе задания после его выполнения. Полное описание полей задания приведено в руководстве пользователя.

Перед началом работы

Все примеры рассчитаны на неймспейс dvp-examples, виртуальную машину с лейблом role: example и Secret с учётными данными SSH. Подготовьте эти объекты перед выполнением примеров.

Подготовка машины и Secret…

Модуль не настраивает SSH внутри гостевой ОС. Пользователь с правом sudo и его открытый ключ передаются на машину через cloud-init-сценарий.

apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachine
metadata:
  name: example-vm-01
  namespace: dvp-examples
  labels:
    role: example
spec:
  # Ресурсы, образ и диск — по вашему шаблону.
  cloudInit:
    type: UserData
    userData: |
      #cloud-config
      packages:
        - openssh-server
        - qemu-guest-agent
      users:
        - name: ansible
          lock_passwd: false
          sudo: ALL=(ALL) NOPASSWD:ALL
          ssh_authorized_keys:
            - ssh-ed25519 <SSH_PUBLIC_KEY> ansible@example
      runcmd:
        - systemctl enable --now ssh
        - systemctl enable --now qemu-guest-agent

В параметре <SSH_PUBLIC_KEY> укажите открытый ключ пары, закрытая часть которой будет помещена в Secret.

Дождитесь, пока виртуальная машина перейдёт в фазу Running и получит адрес:

d8 k get vm example-vm-01 -n dvp-examples \
  -o jsonpath='{.status.phase}{"  "}{.status.ipAddress}{"\n"}'

Учётные данные задания хранятся в Secret в том же неймспейсе. Для подключения используется либо приватный ключ, либо пароль.

d8 k create secret generic ssh-creds -n dvp-examples \
  --from-literal=username=ansible \
  --from-file=ssh-privatekey=./id_ed25519

d8 k create secret generic ssh-creds-password -n dvp-examples \
  --from-literal=username=ansible \
  --from-literal=password=ansible

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

Простейший сценарий состоит из одной машины, выбранной по лейблу, и плейбука с одной задачей. С него рекомендуется начинать работу с модулем, поскольку он проверяет весь путь от создания задания до записи результата в статус.

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: inline-ping
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    inline: |
      ---
      - name: Connectivity check
        hosts: all
        tasks:
          - name: Ping
            ansible.builtin.ping:

Задание завершается фазой PlaybookSucceeded, а в списке status.hosts появляется запись с именем машины, её адресом и значением ok: 1:

d8 k get ansiblerun inline-ping -n dvp-examples
d8 k get ansiblerun inline-ping -n dvp-examples -o jsonpath='{.status.hosts}' | jq

Плейбук из ConfigMap

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

Манифест и результат…

apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-playbook
  namespace: dvp-examples
data:
  playbook.yaml: |
    ---
    - name: Hello from a ConfigMap
      hosts: all
      tasks:
        - name: Print hostname
          ansible.builtin.debug:
            msg: "Hello from {{ inventory_hostname }} ({{ ansible_hostname }})"
---
apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: hello-from-configmap
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: ConfigMap
    configMapRef:
      name: hello-playbook
      # key: playbook.yaml — значение по умолчанию.

В логах пода раннера появится строка TASK [Print hostname] с именем машины. Если ресурс ConfigMap ещё не создан, задание ожидает его появления в фазе Pending и не завершается ошибкой.

Выбор нескольких целей

Цели задания задаются либо выражением над лейблами, либо перечислением имён. Одновременно использовать оба способа нельзя, а манифест с пустым селектором API-сервер отклоняет. Задание выполняется однократно, поэтому цели требуется задавать явно.

Оба способа…

Выражение над лейблами в примере ниже выбирает машины, у которых значение лейбла role входит в список, а лейбл env задан:

spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchExpressions:
          - key: role
            operator: In
            values: [web, db]
          - key: env
            operator: Exists

Список имён не может быть пустым. В качестве имён указываются значения поля metadata.name машин из того же неймспейса:

spec:
  target:
    type: VirtualMachines
    virtualMachines:
      names: [example-vm-01, example-vm-02]

Если машина подошла под выбор, но не может быть настроена в момент запуска задания, она попадает в список status.skippedHosts с указанием причины, а плейбук выполняется на остальных машинах.

Отладка задания

Блок runner изменяет режим запуска утилиты ansible-playbook, не влияя на состав задач. Параметр diff показывает, что именно изменилось в файлах, параметр verbosity задаёт подробность лога, а параметр dryRun выполняет проверочный запуск без изменений на хостах.

Манифест и что появится в логах…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: debug-template-with-diff
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  runner:
    diff: true
    verbosity: 2
  playbook:
    type: Inline
    inline: |
      ---
      - name: Render a file with --diff
        hosts: all
        become: true
        tasks:
          - name: Write a message of the day
            ansible.builtin.copy:
              dest: /etc/motd
              content: |
                Managed by AnsibleRun
                Host: {{ inventory_hostname }}
              mode: '0644'

С параметром diff: true Ansible выводит блок изменений файла, а с параметром verbosity: 2 добавляет подробности по каждой задаче. Уровень 4 увеличивает объём лога настолько, что поиск в нём затрудняется, поэтому его следует использовать только в том случае, если второго уровня недостаточно.

Группы и переменные хоста в аннотациях

Группы Ansible и факты о машине задаются на самой машине двумя аннотациями, которые контроллер переносит в файл инвентаря. Благодаря этому плейбук получает группу web и переменную app_role независимо от конкретного задания.

Аннотации и задание, нацеленное на группу…

apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachine
metadata:
  name: example-vm-01
  namespace: dvp-examples
  labels:
    role: example
  annotations:
    ansible.deckhouse.io/groups: "web,production"
    vars.ansible.deckhouse.io/app_role: "frontend"
    vars.ansible.deckhouse.io/datacenter: "dc1"

Задание обращается к такой группе так же, как к группе из обычного файла инвентаря:

spec:
  playbook:
    type: Inline
    inline: |
      ---
      - name: Frontend rollout
        hosts: web
        tasks:
          - name: Show host variables
            ansible.builtin.debug:
              msg: "{{ app_role }} in {{ datacenter }}"

Именами групп и переменных являются идентификаторы Ansible. Недопустимое имя в аннотации команда d8 k apply не отклоняет. Задание создаётся, а затем завершается фазой Error с причиной InvalidInventoryAnnotations, и в сообщении указаны машина и аннотация. Полный список запрещённых имён приведён в руководстве пользователя.

Установка пакета с повышением привилегий

Задачам, которые изменяют систему, требуется параметр become: true. Пароль для повышения привилегий контроллер берёт из ключа become-password в Secret подключения. Пакетный менеджер выбирается по фактам, собранным Ansible.

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: install-htop
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    inline: |
      ---
      - name: Install htop
        hosts: all
        become: true
        tasks:
          - name: Update the apt cache
            ansible.builtin.apt:
              update_cache: true
              cache_valid_time: 3600
            when: ansible_facts.pkg_mgr == 'apt'

          - name: Install the package
            ansible.builtin.package:
              name: htop
              state: present

При первом запуске задача установки возвращает состояние changed, а при повторном — состояние ok, поскольку пакет уже установлен. Если пользователю требуется пароль для sudo, добавьте его в Secret в ключе become-password.

Идемпотентная настройка сервиса

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

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: render-nginx-config
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    inline: |
      ---
      - name: Configure nginx
        hosts: all
        become: true
        tasks:
          - name: Write the site config
            ansible.builtin.copy:
              dest: /etc/nginx/conf.d/ansible-demo.conf
              mode: '0644'
              validate: 'nginx -t -c %s'
              content: |
                server {
                  listen 8080 default_server;
                  server_name {{ inventory_hostname }};
                  location / {
                    return 200 "OK from {{ inventory_hostname }}\n";
                  }
                }
            notify: Reload nginx
        handlers:
          - name: Reload nginx
            ansible.builtin.service:
              name: nginx
              state: reloaded

В списке status.hosts первого запуска будет значение changed: 1, а второго — значение changed: 0. Проект с ролями и шаблонами удобнее хранить в Git-репозитории, чем в поле inline.

Сбор фактов о машинах

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

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: weekly-audit
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    inline: |
      ---
      - name: Weekly audit
        hosts: all
        gather_facts: true
        tasks:
          - name: Show the snapshot
            ansible.builtin.debug:
              msg:
                os: "{{ ansible_distribution }} {{ ansible_distribution_version }}"
                kernel: "{{ ansible_kernel }}"
                cores: "{{ ansible_processor_vcpus }}"
                mem_mb: "{{ ansible_memtotal_mb }}"
                uptime_sec: "{{ ansible_uptime_seconds }}"

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

Пропущенные машины в статусе

Машина, которая подошла под выбор, но не может быть настроена в момент запуска задания, не приводит к ошибке. Такая машина попадает в список status.skippedHosts с указанием причины, а плейбук выполняется на остальных машинах.

Пример статуса с пропущенными машинами…

Ниже приведён статус задания для трёх машин, из которых работает только одна:

status:
  phase: PlaybookSucceeded
  summary: { total: 1, successful: 1, failed: 0, unreachable: 0, skipped: 2 }
  skippedHosts:
    - name: example-vm-02
      phase: Stopped
      reason: TargetNotRunning
    - name: example-vm-03
      phase: Running
      reason: NoAddress

Если не подошла ни одна машина, задание завершается фазой Error с причиной NoEligibleTargets, а список status.skippedHosts содержит причину отказа по каждой машине. Все причины пропуска приведены в разделе Диагностика.

Зашифрованные значения Ansible Vault

Проект с файлами, зашифрованными Ansible Vault, выполняется без изменений. Пароль хранится в Secret подключения в ключе ansible-vault-password, а задание передаёт его утилите ansible-playbook как файл пароля.

Secret и задание…

d8 k create secret generic ssh-creds-vault -n dvp-examples \
  --from-literal=username=ansible \
  --from-file=ssh-privatekey=./id_ed25519 \
  --from-literal=ansible-vault-password='vault-pass'

Значение шифруется командой ansible-vault encrypt_string и помещается в плейбук без изменений:

spec:
  connection:
    secretRef:
      name: ssh-creds-vault
  playbook:
    type: Inline
    inline: |
      ---
      - name: Use a vault-encrypted value
        hosts: all
        vars:
          api_token: !vault |
            $ANSIBLE_VAULT;1.1;AES256
            6633...6464
        tasks:
          - name: Write the token
            become: true
            ansible.builtin.copy:
              dest: /etc/example/api-token
              content: "{{ api_token }}"
              mode: '0600'

Без ключа ansible-vault-password плейбук запускается, но завершается ошибкой на этапе расшифровки. В списке status.failures в этом случае будет ошибка Ansible об отсутствующем пароле.

Плейбук-проект из публичного Git-репозитория

Реальный проект Ansible представляет собой дерево каталогов с roles/, group_vars/ и шаблонами. Источник Git загружает репозиторий целиком, поэтому роли и файлы рядом с плейбуком выполняются без изменений, а объявленные зависимости Galaxy устанавливаются до запуска плейбука.

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: git-playbook
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Git
    git:
      url: https://github.com/example-org/ansible-demo
      revision: v1.4.0                 # ветка, тег или SHA коммита
      path: playbooks/site.yml         # по умолчанию playbook.yaml

Коммит, который был фактически выполнен, контроллер записывает в поле status.playbookCommit. Если в дереве присутствует файл requirements.yml, roles/requirements.yml или collections/requirements.yml, объявленные роли и коллекции устанавливаются до запуска плейбука. Эти шаги видны в логах init-контейнеров git-clone и galaxy-install.

Приватный Git-репозиторий

Учётные данные репозитория хранятся в отдельном Secret и монтируются только на шаг загрузки, поэтому задачи плейбука их не видят. Для адресов вида ssh:// такой Secret является обязательным, а ключи хоста проверяются строго.

Оба способа доступа…

Для доступа по SSH требуются приватный ключ и содержимое файла known_hosts. Получить ключи хоста можно командой ssh-keyscan:

d8 k create secret generic git-ssh -n dvp-examples \
  --from-file=ssh-privatekey=./deploy_key \
  --from-file=known_hosts=<(ssh-keyscan github.com)
spec:
  playbook:
    type: Git
    git:
      url: ssh://git@github.com/example-org/ansible-project.git
      revision: main
      path: playbooks/site.yml
      secretRef:
        name: git-ssh

Для HTTPS токен передаётся как пароль, а имя пользователя зависит от сервиса. В GitLab используется имя oauth2, а для job-токена — имя gitlab-ci-token:

d8 k create secret generic git-https -n dvp-examples \
  --from-literal=username=oauth2 \
  --from-literal=password=<TOKEN>

В параметре <TOKEN> укажите токен с правом чтения репозитория. Для сервера с собственным удостоверяющим центром добавьте в тот же Secret ключ ca.crt. Если загрузить репозиторий не удалось, задание завершается с причиной GitCloneFailed, а подробности остаются в логах контейнера git-clone.

Задание в дополнительной сети

Если служба SSH в гостевой ОС слушает только в дополнительной сети модуля sdn, одного адреса машины для подключения недостаточно, поскольку маршрута из сети подов в другой L2-домен нет. Поле connection.network помещает в эту сеть и само задание.

Машина, задание и требования к сети…

В примере ниже машина подключена к дополнительной сети наравне с основной:

apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachine
metadata:
  name: vm-in-vlan
  namespace: dvp-examples
  labels:
    role: vlan-example
spec:
  networks:
    - type: Main
    - type: ClusterNetwork
      name: vlan-64

Блок network задания повторяет элемент списка spec.networks машины, поэтому его значения переносятся из манифеста машины:

spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: vlan-example
  connection:
    secretRef:
      name: ssh-creds
    network:
      type: ClusterNetwork      # Main (по умолчанию) | Network | ClusterNetwork
      name: vlan-64

У сети обязательно должен быть пул адресов, поскольку адрес из него получают и машина, и под раннера. Если пула нет, задание завершается с причиной NetworkWithoutIPAM, а неизвестное имя сети приводит к причине NetworkNotFound. Машина без адреса в этой сети попадает в список пропущенных с причиной NoAddressInNetwork. В гостевой ОС на этом интерфейсе требуется DHCP-клиент, иначе платформа покажет адрес, по которому машина не отвечает.

Диагностика неуспешного задания

Задания, завершившиеся с ошибкой, делятся на два вида, которые разбираются по-разному. Фаза PlaybookFailed означает, что плейбук выполнялся и его задачи завершились с ошибкой, а фаза Error означает, что выполнение не дошло до задач. Ошибки, которые плейбук обработал самостоятельно, оставляют задание успешным.

Куда смотреть в каждом случае…

Задачи, завершившиеся с ошибкой, перечислены в статусе задания, но не более 20 записей:

status:
  phase: PlaybookFailed
  failures:
    - host: example-vm-01
      task: This one fails
      message: "Expected failure"

Под задания, завершившегося с ошибкой, сохраняется до удаления объекта, поэтому полный вывод доступен в его логах:

d8 k logs -n dvp-examples -l ansible.deckhouse.io/owner=<RUN_NAME>

Фаза Error означает, что вывод Ansible отсутствует. Такое происходит, если не хватило зависимости, не подошла ни одна цель или не удалось получить проект из Git. Причина указана в условии Completed, а её разбор приведён в разделе Диагностика.

Ошибки, обработанные блоком rescue или параметром ignore_errors, попадают в счётчики rescued и ignored, и задание остаётся успешным. Порядок чтения таких счётчиков описан в разделе Результат задания.

Запуск по расписанию

Расписание создаёт задания по cron так же, как ресурс CronJob создаёт объекты Job. Имя задания складывается из имени расписания и времени тика.

Манифест и управление…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRunSchedule
metadata:
  name: nightly-ping
  namespace: dvp-examples
spec:
  schedule: "0 4 * * *"
  timeZone: "Europe/Moscow"          # по умолчанию UTC
  template:
    spec:
      target:
        type: VirtualMachines
        virtualMachines:
          selector:
            matchLabels:
              role: example
      connection:
        secretRef:
          name: ssh-creds
      playbook:
        type: Inline
        inline: |
          ---
          - name: Nightly check
            hosts: all
            tasks:
              - name: Ping
                ansible.builtin.ping:

Задания, созданные расписанием, отбираются по лейблу:

d8 k get ansibleruns -n dvp-examples \
  -l ansible.deckhouse.io/schedule-owner=nightly-ping

Расписание, включая шаблон задания, редактируется во время работы, и изменения действуют со следующего тика. Приостановить расписание можно полем suspend:

d8 k patch ansiblerunschedules nightly-ping -n dvp-examples \
  --type=merge -p '{"spec":{"suspend":true}}'

Одновременные задания и история расписания

Четыре поля расписания определяют, что делать, если задание не завершилось до следующего тика, насколько поздно пропущенный тик ещё имеет смысл выполнять и сколько заданий оставлять в неймспейсе.

Манифест и поведение каждого поля…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRunSchedule
metadata:
  name: nightly-baseline
  namespace: dvp-examples
spec:
  schedule: "0 4 * * *"
  timeZone: "Europe/Moscow"
  concurrencyPolicy: Forbid          # Forbid (по умолчанию) | Allow | Replace
  startingDeadlineSeconds: 600
  successfulRunsHistoryLimit: 3
  failedRunsHistoryLimit: 1
  template:
    spec:
      target:
        type: VirtualMachines
        virtualMachines:
          selector:
            matchLabels:
              role: example
      connection:
        secretRef:
          name: ssh-creds
      playbook:
        type: Inline
        inline: |
          ---
          - name: Baseline
            hosts: all
            tasks:
              - name: Ping
                ansible.builtin.ping:

Значение Forbid пропускает тик, пока предыдущее задание активно. Такое поведение подходит для работы с одними и теми же машинами, поскольку параллельное задание в любом случае пропустит их с причиной TargetBusy. Значение Allow подходит, когда задания выполняются на разных машинах, а значение Replace — когда важно последнее состояние, а не завершённость предыдущей попытки.

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

Хосты, заданные адресом

Адреса некоторых хостов платформе неизвестны. К таким хостам относятся машины с адресом, настроенным вручную внутри гостевой ОС или полученным от внешнего DHCP-сервера, а также физические серверы и сетевые устройства. Такие хосты задаются адресом в манифесте задания.

Цели типа Hosts доступны только в коммерческих редакциях. В Community Edition задание работает с виртуальными машинами платформы, а манифест с целями типа Hosts кластер отклоняет при создании.

Список хостов, их переменные и группы…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: patch-appliances
  namespace: dvp-examples
spec:
  target:
    type: Hosts
    hosts:
      - address: 192.168.55.10
        groups: [web]
        vars:
          - name: app_role
            value: frontend
      - address: db.example.com
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    inline: |
      ---
      - name: Baseline
        hosts: all
        tasks:
          - name: Ping
            ansible.builtin.ping:

У такого хоста нет ресурса VirtualMachine и, следовательно, нет аннотаций, поэтому переменные и группы задаются в самом списке и записываются так же, как переменные задания. Ожидать фазу машины не требуется, а занятость адреса не проверяется, поэтому два задания могут выполняться на одном адресе одновременно. В поле status.hosts[].name записывается сам адрес, а список status.skippedHosts остаётся пустым.

Для хоста в изолированной сети требуется также поле connection.network, поскольку сам адрес под раннера в эту сеть не помещает.

Переменные задания

Переменные передают плейбуку значения и позволяют не изменять сам плейбук, поэтому один проект обслуживает несколько окружений. Значение указывается непосредственно в манифесте строкой, числом, списком или отображением.

Манифест и результат…

apiVersion: ansible.deckhouse.io/v1alpha1
kind: AnsibleRun
metadata:
  name: deploy-1-4-2
  namespace: dvp-examples
spec:
  target:
    type: VirtualMachines
    virtualMachines:
      selector:
        matchLabels:
          role: example
  connection:
    secretRef:
      name: ssh-creds
  playbook:
    type: Inline
    vars:
      - name: app_version
        value: "1.4.2"                 # в кавычках — значит строка
      - name: packages
        value: [nginx, curl]
      - name: limits
        value:
          cpu: 2
          mem: 4Gi
    inline: |
      ---
      - name: Deploy
        hosts: all
        tasks:
          - name: Show the version
            ansible.builtin.debug:
              msg: "deploying {{ app_version }}"

В логе появится строка deploying 1.4.2. Версию следует указывать в кавычках, поскольку значение 1.10 без кавычек YAML преобразует в число 1.1. Для следующего развёртывания требуется новый объект задания, так как блок spec изменять нельзя и отредактировать переменную в созданном задании невозможно.

Значения из Secret и ConfigMap

Пароль или готовый набор значений не требуется переносить в манифест задания. Поле valueFrom берёт значение из ключа Secret или ConfigMap, а поле varsFiles подключает целый YAML-документ с сохранением типов значений.

Оба способа и поведение при отсутствии источника…

spec:
  playbook:
    type: Inline
    vars:
      - name: db_password
        valueFrom:
          secretKeyRef:
            name: app-secrets
            key: db-password
    varsFiles:
      - configMapRef:
          name: app-config           # ключ по умолчанию — vars.yaml
      - secretRef:
          name: app-secrets
          key: prod.yaml
          optional: true
    inline: |
      ---
      - name: Deploy
        hosts: all
        tasks:
          - name: Use the password
            ansible.builtin.debug:
              msg: "password length: {{ db_password | length }}"

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

Отсутствующий объект удерживает задание в фазе Pending с причиной VarsSourceNotFound, пока объект не появится, а отсутствующий ключ завершает задание ошибкой. Параметр optional: true разрешает выполнить задание без этих переменных.

Приоритет переменных

Одно имя переменной может быть задано в четырёх местах: в аннотации машины, в плейбуке, в файле varsFiles и в поле vars задания. Переменные задания имеют приоритет над всеми значениями, которые задают плейбук и его проект.

Сценарий с одним именем в трёх местах…

# VirtualMachine
metadata:
  annotations:
    vars.ansible.deckhouse.io/app_version: "from-annotation"    # уровень 8
---
# AnsibleRun
spec:
  playbook:
    type: Inline
    vars:
      - name: app_version
        value: "from-the-run"                                   # уровень 22
    inline: |
      ---
      - hosts: all
        vars:
          app_version: "from-the-playbook"                      # уровень 12
        tasks:
          - ansible.builtin.debug:
              msg: "{{ app_version }}"

Задача выведет значение from-the-run. Если удалить поле vars из задания, задача выведет значение from-the-playbook, а не значение аннотации, поскольку у блока vars внутри play приоритет выше.

Полная таблица уровней приоритета приведена в руководстве пользователя.

Запрещённые имена переменных

Часть имён переменных задание не принимает. К таким именам относятся имена, которые модуль устанавливает самостоятельно, и magic-переменные Ansible. Для первых в API уже предусмотрены отдельные поля, а значения вторых Ansible заполняет сам.

Что именно отклоняется и как выглядит отказ…

Переменная Где задавать вместо неё
ansible_host hosts[].address или адрес машины из статуса
ansible_user Ключ username Secret подключения
ansible_password, ansible_ssh_pass Ключ password
ansible_ssh_private_key_file Ключ ssh-privatekey
ansible_become_password, ansible_become_pass Ключ become-password
ansible_connection spec.connection.type

Имя, которое не является идентификатором Ansible, API-сервер отклоняет в ответ на команду d8 k apply. Имя из таблицы выше или magic-переменная завершают задание до создания пода:

status:
  phase: Error
  message: 'The variable "omit" in spec.playbook.vars[0] cannot be used: it shadows an Ansible magic variable, which Ansible fills in itself.'
  conditions:
    - type: Completed
      status: "False"
      reason: InvalidVariables

Остальные переменные с префиксом ansible_ задавать можно, например ansible_port, ansible_python_interpreter или ansible_become_user. Для аннотации машины действует более строгое правило. Она не может задавать переменные с префиксом ansible_, кроме ansible_port, поскольку машину редактирует не тот пользователь, который запускает плейбук.

Имена внутри документа varsFiles не проверяются, поскольку файл имеет тот же уровень доверия, что и плейбук.