我团队中的任何人都可以通过SSH连接到我们的特殊部署服务器,并从那里运行Ansible playbook将新代码推送到计算机。
如果两个人试图同时进行部署,我们会担心会发生什么。我们想要这样做,以便如果其他人正在运行它,剧本将会失败。
有关如何做到这一点的任何建议?标准解决方案是使用pid文件,但Ansible没有内置支持。
您可以为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:修正了拼写错误
我个人使用RunDeck(http://rundeck.org/)作为我的Ansible剧本的包装器有多种原因:
当然还有很多很好的理由,但我的手指已经厌倦了打字;)
之后,我把它放在我的主要剧本中
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
你考虑过在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),您仍然可以多次登录;这是其他被锁定的用户。
您可以使用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
当可以从多个构建主机运行部署作业时,包装器脚本无用。对于那些情况,锁定必须由剧本处理。
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将检查锁定文件的可配置超时时间,然后放弃,如果在该时间段内未删除。
您还可以使用简单的包装变体:
znode
我会研究像zookeeper这样的分布式锁机制,我将其作为一个角色,因为有一个/deployment/
模块。分布式以实现高可用性并从目标节点写入锁定。
该角色将在开头的block
下写入目标名称的znode,然后将其删除。如果锁已经存在,您可能会失败并在qazxswpoi中抛出一条消息。