我团队中的任何人都可以通过SSH连接到我们的特殊部署服务器,并从那里运行Ansible playbook将新代码推送到计算机。
如果两个人试图同时进行部署,我们会担心会发生什么。我们想要这样做,以便如果其他人正在运行它,剧本将会失败。
有关如何执行此操作的任何建议吗?标准解决方案是使用pid文件,但Ansible没有内置支持。
答案 0 :(得分:27)
我个人使用RunDeck(http://rundeck.org/)作为我的Ansible剧本的包装器有多种原因:
当然还有很多很好的理由,但我的手指已经厌倦了打字;)
答案 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
中引发一条消息。