Поиск по сайту:
Тень живет лишь при свете (Ж. Ренар).

Ansible. Принятие решений в Ansible

19.11.2020
Начало работы с Ansible

В этой статье вы узнаете, как добавить навыки принятия решений в свои сценарии Ansible.

Вы научитесь:

  • Используйте операторы when для условного выполнения задач.
  • Используйте инструкции блока для реализации обработки исключений.
  • Используйте обработчики Ansible для запуска задач при изменении.

Излишне говорить, что вы должны быть знакомы со статьями Ansible, специальными командами и другими основами Ansible, чтобы понять эту статью.

В этой статье используется та же настройка, которая была упомянута в первой статье: 1 элемент управления Red Hat, 3 узла CentOS и 1 узел Ubuntu.

 

Выбор того, когда запускать задачи

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

 

Использование когда с фактами

Вы можете использовать условные выражения when для запуска задачи, только если выполняется определенное условие. Для демонстрации создайте новую книгу воспроизведения с именем ubuntu-server.yml со следующим содержимым:

[destroyer@andreyex]$ cat ubuntu-server.yml 
---
- name: Using when with facts 
  hosts: all
  tasks:
    - name: Detect Ubuntu Servers
      debug:
        msg: "This is an Ubuntu Server."
      when:  ansible_facts['distribution'] == "Ubuntu"

 

Теперь запустите playbook:

[destroyer@andreyex]$ ansible-playbook ubuntu-servers.yml 

PLAY [Using when with facts] *******************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]
ok: [node1]
ok: [node3]
ok: [node2]

TASK [Detect Ubuntu Servers] ***************************************************
skipping: [node1]
skipping: [node2]
skipping: [node3]
ok: [node4] => {
    "msg": "Это сервер Ubuntu."
}

PLAY RECAP *********************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    

node2                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    
node3                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    
node4                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0

 

Обратите внимание, как мы использовали факт Ansible ansible_facts[‘distribution’] в условии when, чтобы проверить, на каких узлах работает Ubuntu. Также обратите внимание, что вам не нужно заключать переменные в фигурные скобки при использовании условных выражений when.

В выходных данных playbook обратите внимание, как TASK [Detect Ubuntu Servers] пропущены первые три узла, поскольку все они работают под CentOS и работают только на node4, поскольку он работает под управлением Ubuntu.

 

Использование when с регистрами

Вы также можете использовать условные выражения с зарегистрированными переменными. Например, следующая книга centos-servers.yml покажет, на каких узлах работает CentOS:

[destroyer@andreyex]$ cat centos-servers.yml 
---
- name: Using when with registers
  hosts: all
  tasks:
    - name: Save the contents of /etc/os-release
      command: cat /etc/os-release
      register: os_release

    - name: Detect CentOS Servers
      debug:
        msg: "Запуск CentOS..."
      when: os_release.stdout.find('CentOS') != -1

 

Плейбук сначала начинается с сохранения содержимого файла /etc/os-release в регистр os_release . Затем вторая задача отображает сообщение «Запуск CentOS…», только если слово «CentOS» встречается в   стандартном выводе os_release .

Идите вперед и запустите playbook:

[destroyer@andreyex]$ ansible-playbook centos-servers.yml 

PLAY [Using when with registers] ***********************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]
ok: [node1]
ok: [node3]
ok: [node2]

TASK [Save the contents of /etc/os-release] ************************************
changed: [node4]
changed: [node1]
changed: [node2]
changed: [node3]

TASK [Detect CentOS Servers] ***************************************************
ok: [node1] => {
    "msg": "Запуск CentOS..."
}
ok: [node2] => {
    "msg": "Запуск CentOS..."
}
ok: [node3] => {
    "msg": "Запуск CentOS..."
}
skipping: [node4]

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=1

 

Обратите внимание, как работает TASK [Detect CentOS Servers] только на первых трех узлах и пропускается node4 (Ubuntu).

 

Тестирование нескольких условий с помощью when

Вы также можете проверить несколько условий одновременно, используя логические операторы. Например, следующая перезагрузка-centos8.yml Playbook использует логический и оператора для перезагрузки серверов, работающих под управлением CentOS версии 8:

[destroyer@andreyex]$ cat reboot-centos8.yml 
---
- name: Reboot Servers
  hosts: all
  tasks:
    - name: Reboot CentOS 8 servers
      reboot: 
        msg: "Сервер перезагружается..."
      when: ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "8"

 

Вы также можете использовать логический оператор или для запуска задачи, если выполняется какое-либо из условий. Например, следующая задача будет перезагружать серверы , на которых работает CentOS или RedHat :

  tasks:
    - name: Reboot CentOS and RedHat Servers 
      reboot: 
        msg: "Сервер перезагружается..."
      when: ansible_facts['distribution'] == "CentOS" or ansible_facts['distribution'] == "RedHat"

 

Использование when с петлями

Если вы объедините условный оператор when с циклом, Ansible проверит условие для каждого элемента в цикле отдельно.

Например, следующий сценарий print-even.yml напечатает все четные числа в диапазоне (1,11):

[destroyer@andreyex]$ cat print-even.yml
---
- name: Print Some Numbers
  hosts: node1
  tasks:
    - name: Print Even Numbers
      debug:
        msg: Number {{ item }} is Even.
      loop: "{{ range(1,11) | list }}"
      when: item % 2 == 0

 

Продолжайте и запустите playbook, чтобы увидеть список всех четных чисел в диапазоне (1,11):

[destroyer@andreyex]$ ansible-playbook print-even.yml

PLAY [Print Some Numbers] **********************************

TASK [Gathering Facts] ****************************
ok: [node1]

TASK [Print Even Numbers] ******************************
skipping: [node1] => (item=1) 
ok: [node1] => (item=2) => {
    "msg": "Число 2 четное."
}
skipping: [node1] => (item=3) 
ok: [node1] => (item=4) => {
    "msg": "Число 4 четное."
}
skipping: [node1] => (item=5) 
ok: [node1] => (item=6) => {
    "msg": "Число 6 четное."
}
skipping: [node1] => (item=7) 
ok: [node1] => (item=8) => {
    "msg": "Число 8 четное."
}
skipping: [node1] => (item=9) 
ok: [node1] => (item=10) => {
    "msg": "Число 10 четное."
}

PLAY RECAP ***********************************
node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0

 

Читать  Полное руководство по удалению образов Docker

Использование when с переменными

Вы также можете использовать условные операторы when с вашими собственными определенными переменными. Помните, что условные выражения требуют логических входных данных; то есть, для срабатывания условия тест должен быть истинным, поэтому вам нужно использовать фильтр bool с небулевыми переменными.

Чтобы продемонстрировать это, взгляните на следующий сборник сценариев isfree.yml :

[destroyer@andreyex]$ cat isfree.yml 
---
- name: 
  hosts: node1
  vars:
    weekend: true
    on_call: "no"
  tasks:
    - name: Run if "weekend" is true and "on_call" is false
      debug:
        msg: "Вы свободны!"
      when: weekend and not on_call | bool

 

Обратите внимание, что здесь я использовал фильтр bool для преобразования значения on_call в его логический эквивалент (no -> false).

Кроме того, вы должны знать, что not false — это истина, и поэтому в этом случае все условие будет оцениваться как true; ты свободен!

Вы также можете проверить, установлена ли переменная или нет; например, следующая задача будет выполняться, только если определена переменная car :

tasks:
    - name: Run only if you got a car
      debug:
        msg: "Давай отправимся в путешествие..."
      when: car is defined

 

Следующая задача использует сбой модуля потерпеть неудачу , если ключи переменная не определена:

tasks:
    - name: Fail if you got no keys
      fail:
        msg: "Эта игра требует некоторых ключей"
      when: keys is undefined

 

Обработка исключений с помощью блоков

Поговорим об обработке исключений.

 

Группировка задач с блоками

Вы можете использовать блоки для группировки связанных задач. Для демонстрации взгляните на следующую книгу install-apache.yml :

[destroyer@andreyex]$ cat install-apache.yml 
---
- name: Install and start Apache Play
  hosts: webservers
  tasks:
    - name: Install and start Apache
      block:
         - name: Install httpd
           yum:
             name: httpd
             state: latest

         - name: Start and enable httpd
           service:
             name: httpd
             state: started
             enabled: yes

    - name: This task is outside the block
      debug:
        msg: "Сейчас я нахожусь за пределами блока..."

 

Playbook запускается на хостах группы веб-серверов и имеет один блок с именем Установить и запустить Apache, который включает в себя две задачи:

  1. Установить httpd
  2. Запустить и включить httpd

Первая задача Install httpd использует модуль yum для установки пакета httpd apache. Вторая задача использует служебный модуль для запуска и разрешает запуск httpd при загрузке.

Обратите внимание, что в playbook есть третья задача, которая не принадлежит блоку Install and start Apache.

Теперь запустите playbook, чтобы установить и запустить httpd на узлах веб-серверов:

[destroyer@andreyex]$ ansible-playbook install-apache.yml 

PLAY [Install and start Apache Play] *******************************************

TASK [Gathering Facts] *********************************************************
ok: [node3]
ok: [node2]

TASK [Install httpd] ***********************************************************
changed: [node2]
changed: [node3]

TASK [Start and enable httpd] **************************************************
changed: [node3]
changed: [node2]

TASK [This task is outside the block] ******************************************
ok: [node2] => {
    "msg": "Сейчас я нахожусь за пределами квартала..."
}
ok: [node3] => {
    "msg": "Сейчас я нахожусь за пределами квартала..."
}

PLAY RECAP *********************************************************************
node2                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0     
node3                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

 

Вы также можете выполнить специальную команду, чтобы убедиться, что httpd действительно запущен и работает:

[destroyer@andreyex]$ ansible webservers -m command -a "systemctl status httpd"
node3 | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2020-11-03 19:35:13 UTC; 1min 37s ago
     Docs: man:httpd.service(8)
 Main PID: 47122 (httpd)
   Status: "Running, listening on: port 80"
    Tasks: 213 (limit: 11935)
   Memory: 25.1M
   CGroup: /system.slice/httpd.service
           ├─47122 /usr/sbin/httpd -DFOREGROUND
           ├─47123 /usr/sbin/httpd -DFOREGROUND
           ├─47124 /usr/sbin/httpd -DFOREGROUND
           ├─47125 /usr/sbin/httpd -DFOREGROUND
           └─47126 /usr/sbin/httpd -DFOREGROUND

Nov 03 19:35:13 node3 systemd[1]: Starting The Apache HTTP Server...
Nov 03 19:35:13 node3 systemd[1]: Started The Apache HTTP Server.
Nov 03 19:35:13 node3 httpd[47122]: Server configured, listening on: port 80
node2 | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2020-11-03 19:35:13 UTC; 1min 37s ago
     Docs: man:httpd.service(8)
 Main PID: 43695 (httpd)
   Status: "Running, listening on: port 80"
    Tasks: 213 (limit: 11935)
   Memory: 25.1M
   CGroup: /system.slice/httpd.service
           ├─43695 /usr/sbin/httpd -DFOREGROUND
           ├─43696 /usr/sbin/httpd -DFOREGROUND
           ├─43697 /usr/sbin/httpd -DFOREGROUND
           ├─43698 /usr/sbin/httpd -DFOREGROUND
           └─43699 /usr/sbin/httpd -DFOREGROUND

Nov 03 19:35:13 node2 systemd[1]: Starting The Apache HTTP Server...
Nov 03 19:35:13 node2 systemd[1]: Started The Apache HTTP Server.
Nov 03 19:35:13 node2 httpd[43695]: Server configured, listening on: port 80

 

Обработка сбоев с помощью блоков

Вы также можете использовать блоки для обработки ошибок задач, используя разделы rescue и always. Это очень похоже на обработку исключений в языках программирования, таких как try-catch в Java или try-except в Python.

Читать  9 полезных примеров команды Split в Linux

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

Для демонстрации давайте взглянем на следующий пример:

tasks:
  - name: Handling error example
    block:
      - name: run a command
        command: uptime

      - name: run a bad command
        command: blabla

      - name: This task will not run
        debug:
          msg: "Я не запускаю потому что вышеприведенная задача провалилась."

    rescue: 
      - name: Runs when the block failed
        debug:
          msg: "Блок не удался; давайте попробуем исправить это здесь..."

 

Обратите внимание, как вторая задача в блоке run a bad command генерирует ошибку, и, в свою очередь, третья задача в блоке никогда не получает возможности запустить. Задачи внутри спасательной секции будут работать , потому что вторая задача в блоке не удалась.

Вы также можете использовать ignore_errors: yes, чтобы гарантировать, что Ansible продолжит выполнение задач в playbook, даже если задача не удалась:

tasks:
  - name: Handling error example
    block:
      - name: run a command
        command: uptime

      - name: run a bad command
        command: blabla
        ignore_errors: yes

      - name: This task will run
        debug:
          msg: "Запуск потому что вышеуказанные ошибки задачи были проигнорированы."

    rescue: 
      - name: This will not run
        debug:
          msg: "Ошибки были проигнорированы! ... не собирается запускаться."

 

Обратите внимание, что в этом примере вы проигнорировали ошибки во второй задаче в блоке run a bad command, и поэтому третья задача была запущена. Кроме того, спасательный раздел не будет работать , как вы игнорировали ошибку во второй задаче в блоке.

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

Чтобы продемонстрировать это, взгляните на следующую книгу воспроизведения handle-errors.yml, в которой есть все три раздела блока (block, rescue, always):

[destroyer@andreyex]$ cat handle-errors.yml 
---
- name: Handling Errors with Blocks
  hosts: node1
  tasks:
    - name: Handling Errors Example
      block:
        - name: run a command
          command: uptime

        - name: run a bad command
          command: blabla

        - name: This task will not run
          debug:
            msg: "Я не запускаюсь потому что вышеприведенная задача провалилась!"

      rescue:
        - name: Runs when the block fails
          debug:
            msg: "Блок провалился! давайте попробуем исправить это здесь..."

      always:
        - name: This will always run
          debug:
            msg: "Независимо от того, провалился блок или нет ... Я всегда буду запускаться.!"

 

Идите вперед и запустите playbook:

[destroyer@andreyex]$ ansible-playbook handle-errors.yml 

PLAY [Handling Errors with Blocks] *********************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [run a command] ***********************************************************
changed: [node1]

TASK [run a bad command] *******************************************************
fatal: [node1]: FAILED! => {"changed": false, "cmd": "blabla", "msg": "[Errno 2] No such file or directory: b'blabla': b'blabla'", "rc": 2}

TASK [Runs when the block fails] ***********************************************
ok: [node1] => {
    "msg": "Block failed! let's try to fix it here ..."
}

TASK [This will always run] ****************************************************
ok: [node1] => {
    "msg": "Whether the block has failed or not ... I will always run!"
}

PLAY RECAP *********************************************************************
node1                      : ok=4    changed=1    unreachable=0    failed=0    skipped=0

 

Как вы видете; спасательный раздел ли работать как 2 — й задача в блоке произошел сбой, и вы не игнорировать ошибки. Кроме того, всегда выполнялся (и будет всегда) раздел всегда.

 

Запуск задач при изменении с помощью обработчиков

Посмотрим, как менять обработчики и запускать задачи.

 

Запуск вашего первого обработчика

Вы можете использовать обработчики для запуска задач при изменении на ваших управляемых узлах. Чтобы продемонстрировать это, взгляните на следующий сборник handler-example.yml:

[destroyer@andreyex]$ cat handler-example.yml 
---
- name: Simple Handler Example
  hosts: node1
  tasks:
    - name: Create engineers group
      group:
        name: engineers
      notify: add destroyer

    - name: Another task in the play
      debug:
        msg: "I am just another task."

  handlers: 
    - name: add destroyer
      user:
        name: destroyer
        groups: engineers
        append: yes

 

Первая задача Create engineers groupсоздает группу инженеров, а также уведомляет обработчика add destroyer.

Давайте запустим сценарий, чтобы увидеть, что произойдет:

[destroyer@andreyex]$ ansible-playbook handler-example.yml 

PLAY [Simple Handler Example] **************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Create engineers group] **************************************************
changed: [node1]

TASK [Another task in the play] ************************************************
ok: [node1] => {
    "msg": "I am just another task."
}

RUNNING HANDLER [add destroyer] ***************************************************
changed: [node1]

PLAY RECAP *********************************************************************
node1                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

 

Обратите внимание, что создание инженеров вызвало изменение на node1 и в результате запустило add destroyerобработчик.

Вы также можете запустить быструю специальную команду, чтобы убедиться, что пользователь destroyer действительно является членом группы инженеров:

[destroyer@andreyex]$ ansible node1 -m command -a "id destroyer"
node1 | CHANGED | rc=0 >>
uid=1000(destroyer) gid=1000(destroyer) groups=1000(destroyer),4(adm),190(systemd-journal),1004(engineers)

 

Читать  Инструмент командной строки: HTOP

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

Чтобы полностью понять концепцию идемпотентности Ansible; запустите playbook handler-example.yml еще раз:

[destroyer@andreyex]$ ansible-playbook handler-example.yml 

PLAY [Simple Handler Example] **************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Create engineers group] **************************************************
ok: [node1]

TASK [Another task in the play] ************************************************
ok: [node1] => {
    "msg": "I am just another task."
}

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0

 

Как вы видете; на этот раз Create engineers group задача не вызвала и не сообщила об изменении, потому что группа инженеров уже существует на node1 и, как результат; обработчик add destroyer не запустился.

 

Контроль, когда сообщать об изменении

Вы можете использовать ключевое слово changed_when, чтобы указать, когда задача должна сообщать об изменении. Чтобы продемонстрировать это, взгляните на следующий сценарий control-change.yml:

[destroyer@andreyex]$ cat control-change.yml 
---
- name: Control Change
  hosts: node1
  tasks:
    - name: Run the date command
      command: date
      notify: handler1

    - name: Run the uptime command
      command: uptime

  handlers:
     - name: handler1
       debug:
         msg: "I can handle dates"

 

Обратите внимание, как первая задача Run the date commandзапускает handler1. А теперь запустите playbook:

[destroyer@andreyex]$ ansible-playbook control-change.yml 

PLAY [Control Change] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Run the date command] ****************************************************
changed: [node1]

TASK [Run the uptime command] **************************************************
changed: [node1]

RUNNING HANDLER [handler1] *****************************************************
ok: [node1] => {
    "msg": "I can handle dates"
}

PLAY RECAP *********************************************************************
node1                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

 

Обе задачи Run the date command и Run the uptime command зарегистрированные изменения, и обработчик handler1 был запущен. Вы можете утверждать, что выполнение команд date и uptime на самом деле ничего не меняет на управляемом узле, и вы совершенно правы!

Теперь давайте отредактируем playbook, чтобы задача не сообщала об изменениях:

[destroyer@andreyex]$ cat control-change.yml 
---
- name: Control Change
  hosts: node1
  tasks:
    - name: Run the date command
      command: date
      notify: handler1
      changed_when: false

    - name: Run the uptime command
      command: uptime

  handlers:
     - name: handler1
       debug:
         msg: "I can handle dates"

 

Теперь снова запустите playbook:

[destroyer@andreyex]$ ansible-playbook control-change.yml 

PLAY [Control Change] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [node1]

TASK [Run the date command] ****************************************************
ok: [node1]

TASK [Run the uptime command] **************************************************
changed: [node1]

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

 

Как видите, на этот раз задача не сообщила об изменении, и в результате handler1 не был запущен.

 

Настройка сервисов с обработчиками

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

Чтобы продемонстрировать это, взгляните на следующую инструкцию configure-ssh.yml :

[destroyer@andreyex]$ cat configure-ssh.yml 
---
- name: Configure SSH 
  hosts: all
  tasks:
     - name: Edit SSH Configuration
       blockinfile:
         path: /etc/ssh/sshd_config
         block: |
            MaxAuthTries 4
            Banner /etc/motd
            X11Forwarding no
       notify: restart ssh

  handlers: 
    - name: restart ssh
      service:
        name: sshd
        state: restarted

 

Обратите внимание, что мы использовали модуль blockinfile для вставки нескольких строк текста в файл конфигурации /etc/ssh/sshd_config. Задача также вызывает обработчик restart ssh по изменению.

Идите вперед и запустите playbook:

[destroyer@andreyex]$ ansible-playbook configure-ssh.yml 

PLAY [Configure SSH] ***********************************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]
ok: [node3]
ok: [node1]
ok: [node2]

TASK [Edit SSH Configuration] **************************************************
changed: [node4]
changed: [node2]
changed: [node3]
changed: [node1]

RUNNING HANDLER [restart ssh] **************************************************
changed: [node4]
changed: [node3]
changed: [node2]
changed: [node1]

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    
node2                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    
node4                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0

 

Все хорошо! Теперь давайте быстро взглянем на последние несколько строк в файле/etc/ssh/sshd_config:

[destroyer@andreyex]$ ansible node1 -m command -a "tail -5 /etc/ssh/sshd_config"
node1 | CHANGED | rc=0 >>
# BEGIN ANSIBLE MANAGED BLOCK
MaxAuthTries 4
Banner /etc/motd
X11Forwarding no
# END ANSIBLE MANAGED BLOCK

 

Удивительно! Точно так, как вы ожидали. Имейте в виду, что если вы повторно запустите playbook configure-ssh.yml, Ansible не будет редактировать (или добавлять) файл /etc/ssh/sshd_config. Вы можете попробовать это сами.

Мы также рекомендуем вам взглянуть на страницы документации blockinfile и lineinfile, чтобы понять различия и использование каждого модуля:

[destroyer@andreyex]$ ansible-doc blockinfile
[destroyer@andreyex]$ ansible-doc lineinfile

 

Хорошо! На этом мы подошли к концу нашей статьи по принятию решений в Ansible.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (2 оценок, среднее: 5,00 из 5)
Загрузка...
Поделиться в соц. сетях:


0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

**ссылки nofollow

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Это может быть вам интересно


Рекомендуемое
Как вы используете SSH для входа в контейнер Docker? Традиционный подход…

Спасибо!

Теперь редакторы в курсе.