Подтвердить что ты не робот

Предотвращение одновременного развертывания с помощью Ansible

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

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

Любые предложения по тому, как это сделать? Стандартное решение - использовать файл pid, но Ansible не имеет встроенной поддержки для них.

4b9b3361

Ответ 1

Вы можете написать оболочку для следующих команд:

ansible-playbook() {
  lock="/tmp/ansible-playbook.lock"

  # Check if lock exists, return if yes
  if [ -e $lock ]; then
    echo "Sorry, someone is running already ansible from `cat $lock`"
    return
  fi

  # Install signal handlers
  trap "rm -f $lockfile; trap - INT TERM EXIT; return" INT TERM EXIT

  # Create lock file, saving originating IP
  echo $SSH_CLIENT | cut -f1 -d' ' > $lock

  # Run ansible with arguments passed at the command line
  `which ansible-playbook` "[email protected]"

  # Remove lock file
  rm $lock

  # Remove signal handlers
  trap - INT TERM EXIT
}

Определите эту функцию в ~/.bashrc ваших пользователей в окне развертывания, и вы настроены. Вы можете сделать то же самое для команды ansible, если хотите, но с учетом вопроса я не уверен, что это необходимо.

EDIT: переписан с обработчиком сигналов, чтобы предотвратить блокировку файла блокировки, если пользователи нажимают Ctrl-C.

EDIT2: исправлена ​​опечатка

Ответ 2

Лично я использую RunDeck (http://rundeck.org/) в качестве обертки вокруг моих загрузочных плееров Ansible по нескольким причинам:

  • Вы можете установить задание RunDeck 'только для того, чтобы его можно было запускать за один раз (или настроить его на выполнение столько раз, сколько хотите)
  • Вы можете настроить пользователей в системе, чтобы аудит того, кто запустил то, что указано в списке.
  • Вы можете установить дополнительные переменные с ограничениями на то, что можно использовать (указать список параметров)
  • Его намного дешевле, чем Ansible Tower (RunDeck бесплатно).
  • Он имеет полный API для эффективного выполнения заданий из систем сборки.
  • Вам не нужно писать сложные обертки bash вокруг команды ansible-playbook
  • SSH может стать лакмусовой бумажкой "что-то нуждается в ansible script, написанном" - я не разрешаю доступ к SSH, кроме как полностью в ситуациях break/fix, и в результате мы получаем более счастливое SA
  • Наконец, и определенно, в категории "хороший, чтобы иметь", вы можете запланировать задания RunDeck для быстрого запуска загружаемых загрузочных книг для всех, кто входит в консоль, чтобы увидеть, что работает, когда

Есть еще много веских причин, конечно, но мои пальцы устали печатать;)

Ответ 3

Я помещаю это в свою основную книгу, после

    hosts: all. 

lock_file_path: Это файл, существование которого указывает на то, что в настоящее время выполняется одноразовое развертывание или раньше было развертывание, которое по какой-то причине прервалось.

force_ignore_lock: по умолчанию это значение false, reset с помощью флажка параметра, который можно установить в оболочку командной строки для доступа. Это позволяет продолжить развертывание.

Что это делает

pre_tasks

Первый pre_task проверяет, существует ли lock_file_path, и записывает результат в регистр с именем lock_file.

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

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

post_tasks

Этот вызов называется сразу после завершения всех задач развертывания. Задача здесь удаляет lock_file, позволяя счастливому развертыванию следующего человека без каких-либо проблем.

vars:
  lock_file_path=/tmp/ansible-playbook-{{ansible_ssh_user}}.lock

pre_tasks: 
   - stat: path={{lock_file_path}}
     register: lock_file

   - fail: msg="Sorry, I found a lockfile, so I'm assuming that someone was already running ansible when you started this deploy job. Add -f to your deploy command to forcefully continue deploying, if the previous deploy was aborted."
   when: lock_file.stat.exists|bool and not force_ignore_lock|bool

   - file: path={{lock_file_path}} state=absent
     sudo: yes
     when: "{{force_ignore_lock}}"

   - file: path={{lock_file_path}} state=touch
     sudo: yes

post_tasks:
   - file: path={{lock_file_path}} state=absent
     sudo: yes

Ответ 4

Рассматривали ли вы установку maxsyslogins в limits.conf? Вы можете ограничить это по группе.

# for a group called 'deployers'
@deployers        -       maxsyslogins      1

Это довольно немного тяжелее, чем вы просили. Сначала вы можете попробовать его на виртуальной машине. Обратите внимание, что ни у кого из разработчиков не будет доступа, если в системе вообще есть какие-либо другие пользователи, лимит 1 не просто учитывает развертывание. Кроме того, если в качестве пользователя вы мультиплексируете свои ssh-соединения (ControlMaster auto), вы все равно сможете входить в систему несколько раз; это другие пользователи, которые будут заблокированы.

Ответ 5

Вы можете использовать команду flock, которая обернет вашу команду файловым стадом (2):

$ flock /tmp/ansible-playbook.lock ansible-playbook foo bar baz

Оберните, что, однако, это лучше всего подходит вашим пользователям. Это оставит постоянный файл блокировки в /tmp, но заметьте, что он не безопасен для его удаления [1]. Он атомарный и очень простой.

[1]
A: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
B: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
   B blocks waiting for lock on /tmp/foo.lock
A: Finish, deleting /tmp/foo.lock
B: Runs, using lock on now deleted /tmp/foo.lock
C: flock /tmp/foo.lock -c "echo running; sleep 5; rm /tmp/foo.lock"
   Creates new /tmp/foo.lock, locks it and runs immediately, parallel with B

Ответ 6

Скрипты Wrapper не полезны, когда задания развертывания могут выполняться из нескольких узлов сборки. Для таких случаев блокировка должна обрабатываться в учебнике.

Ansible теперь имеет wait_for модуль, который можно использовать для блокировки. Вот краткий пример (без учета устаревших замков):

vars:
  lock_file: "{{ deploy_dir }}/.lock"
pre_tasks:
  - name: check for lock file
    wait_for:
      path: "{{ lock_file }}"
      state: absent
  - name: create lock file
    file:
      path: "{{ lock_file }}"
      state: touch
post_tasks:
  - name: remove lock file
    file:
      path: "{{ lock_file }}"
      state: absent

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

Ответ 7

Вы также можете использовать простой вариант оболочки:

# Check lock file - if exists then exit. Prevent running multiple ansible instances in parallel
while kill -0 $(cat /tmp/ansible_run.lock 2> /dev/null) &> /dev/null; do
  echo "Ansible is already running. Please wait or kill running instance."
  sleep 3
done
# Create lock file
echo $$ > /tmp/ansible_run.lock

ansible-playbook main.yml

# Remove lock
rm -f /tmp/ansible_run.lock