使用Ansible防止同时部署

问题描述 投票:25回答:8

我团队中的任何人都可以通过SSH连接到我们的特殊部署服务器,并从那里运行Ansible playbook将新代码推送到计算机。

如果两个人试图同时进行部署,我们会担心会发生什么。我们想要这样做,以便如果其他人正在运行它,剧本将会失败。

有关如何做到这一点的任何建议?标准解决方案是使用pid文件,但Ansible没有内置支持。

ansible
8个回答
18
投票

您可以为ansible命令编写一个包装器,如下所示:

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` "$@"

  # Remove lock file
  rm $lock

  # Remove signal handlers
  trap - INT TERM EXIT
}

在部署框中的用户的~/.bashrc中定义此功能,然后进行设置。如果你愿意的话,你可以为ansible命令做同样的事情,但是考虑到这个问题,我不确定是否需要它。

编辑:重写信号处理程序,以防止用户按Ctrl-C时锁定文件悬空。

EDIT2:修正了拼写错误


27
投票

我个人使用RunDeck(http://rundeck.org/)作为我的Ansible剧本的包装器有多种原因:

  • 您可以将RunDeck“作业”设置为仅能够一次运行(或将其设置为根据需要同时运行多次)
  • 您可以在系统中设置用户,以便审核谁清楚地运行了列出的内容
  • 您可以使用可以使用的约束设置其他变量(指定选项列表)
  • 它比Ansible Tower便宜很多(RunDeck是免费的)
  • 它有一个完整的API,可以从构建系统中实际运行作业
  • 您不需要在ansible-playbook命令周围编写复杂的bash包装器
  • SSH可以成为“需要编写一个ansible脚本的东西”的试金石 - 我不允许SSH访问,除了在完全中断/修复情况下,我们有更快乐的SA的结果
  • 最后,在“很好拥有”类别中,您可以安排RunDeck作业以非常简单的方式运行ansible playbooks,以便任何登录控制台查看运行时的内容

当然还有很多很好的理由,但我的手指已经厌倦了打字;)


11
投票

之后,我把它放在我的主要剧本中

    hosts: all. 

lock_file_path:这是一个文件,其存在表明当前正在运行的ansible部署,或之前有部署,由于某种原因中止。

force_ignore_lock:默认为false,由选项标志重置,您可以在命令行包装器中设置为ansible。它使ansible能够继续部署。

这是做什么的

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 ,让下一个人快乐地部署,没有任何问题。

maxsyslogins

8
投票

你考虑过在limits.conf中设置# for a group called 'deployers' @deployers - maxsyslogins 1 吗?您可以按组限制。

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

这比你要求的要严重得多。您可能希望首先在VM上尝试它。请注意,如果系统上有任何其他用户,则部署者中没有人可以访问,1限制不仅仅计算部署者。此外,如果您作为用户多路复用您的ssh连接(ControlMaster auto),您仍然可以多次登录;这是其他被锁定的用户。


5
投票

您可以使用flock命令,它将使用基于文件系统的flock(2)包装您的命令:

[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

尽管它最适合您的用户。这会在/ tmp中留下一个持久的锁文件,但请注意删除它是不安全的[1]。它是原子的,非常简单。

wait_for

5
投票

当可以从多个构建主机运行部署作业时,包装器脚本无用。对于那些情况,锁定必须由剧本处理。

Ansible现在有一个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 模块,可用于锁定。这是一个简短的例子(没有考虑过时的锁):

# 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

Ansible将检查锁定文件的可配置超时时间,然后放弃,如果在该时间段内未删除。


0
投票

您还可以使用简单的包装变体:

znode

0
投票

我会研究像zookeeper这样的分布式锁机制,我将其作为一个角色,因为有一个/deployment/模块。分布式以实现高可用性并从目标节点写入锁定。

该角色将在开头的block下写入目标名称的znode,然后将其删除。如果锁已经存在,您可能会失败并在qazxswpoi中抛出一条消息。

© www.soinside.com 2019 - 2024. All rights reserved.