使用Ansible防止同时部署

时间:2014-02-19 02:38:00

标签: ansible

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

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

有关如何执行此操作的任何建议吗?标准解决方案是使用pid文件,但Ansible没有内置支持。

8 个答案:

答案 0 :(得分:27)

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

  • 您可以设置RunDeck'工作'只能一次运行(或将其设置为在您想要的同时运行多次)
  • 您可以在系统中设置用户,以便审核谁清楚地运行了列出的内容
  • 您可以设置其他变量,并对可以使用的内容进行约束(指定选项列表)
  • 它比Ansible Tower便宜了很多(RunDeck是免费的)
  • 它有一个完整的API,用于从构建系统中实际运行作业
  • 你不需要围绕ansible-playbook命令编写复杂的bash包装器
  • SSH可以成为一个试图,需要一个需要写一个安全脚本的东西。 - 除了完整的休息/修复情况外,我不允许SSH访问,结果我们感到更开心了
  • 最后,肯定会在那里很好地拥有'类别是您可以安排RunDeck作业以非常简单的方式运行ansible playbooks,以便任何登录控制台以查看正在运行的内容的人

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

答案 1 :(得分: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:修正了拼写错误

答案 2 :(得分: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

答案 3 :(得分:8)

您是否考虑过在limits.conf中设置maxsyslogins?您可以按组限制。

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

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

答案 4 :(得分:5)

您可以使用flock命令,它将使用基于文件系统的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

答案 5 :(得分:5)

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

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将检查锁定文件的可配置超时时间,然后放弃,如果在该时间段内没有删除。

答案 6 :(得分:0)

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

# 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

答案 7 :(得分:0)

我将研究像Zookeeper这样的分布式锁定机制,因为其中有一个znode模块,所以我将其作为角色包括在内。分布式以实现高可用性并锁定目标节点。

该角色将在开始时在/deployment/下写入目标名称的znode,之后将其删除。如果该锁已经存在,您可能会失败并在block中引发一条消息。