使用Ansible防止同时部署
我的团队中的任何人都可以使用SSH连接到我们的特殊部署服务器,然后从那里运行Ansible playbook,将新代码推送到机器上 我们担心如果两个人同时进行部署会发生什么。我们希望这样做,如果其他人正在运行剧本,剧本就会失败使用Ansible防止同时部署,ansible,Ansible,我的团队中的任何人都可以使用SSH连接到我们的特殊部署服务器,然后从那里运行Ansible playbook,将新代码推送到机器上 我们担心如果两个人同时进行部署会发生什么。我们希望这样做,如果其他人正在运行剧本,剧本就会失败 有什么建议吗?标准解决方案是使用pid文件,但Ansible没有内置的对这些文件的支持。您是否考虑过在limits.conf中设置maxsyslogins?您可以按组对此进行限制 # for a group called 'deployers' @deployers
有什么建议吗?标准解决方案是使用pid文件,但Ansible没有内置的对这些文件的支持。您是否考虑过在limits.conf中设置
maxsyslogins
?您可以按组对此进行限制
# for a group called 'deployers'
@deployers - maxsyslogins 1
这比你要求的要严重得多。您可能想先在虚拟机上试用它。请注意,如果系统上有任何其他用户,部署人员中的任何人都无权访问,1限制不仅仅包括部署人员。此外,如果您作为用户多路传输ssh连接(ControlMaster auto),您仍然可以多次登录;其他用户将被锁定。您可以为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()作为Ansible剧本的包装,原因有多种:
- 您可以将RunDeck“作业”设置为只能一次运行(或将其设置为同时运行任意次数)
- 您可以在系统中设置用户,以便对谁运行了列出的内容进行审核
- 您可以设置附加变量,并限制可以使用的变量(指定选项列表)
- 它比Ansible Tower便宜很多(RunDeck是免费的)
- 它有一个完整的API,用于从构建系统实际运行作业
- 您不需要围绕ansible playbook命令编写复杂的bash包装
- SSH可以成为“某些东西需要编写一个ansible脚本”的试金石——我不允许SSH访问,除非在完全中断/修复的情况下,因此我们有了更快乐的SA
- 最后,在“很好拥有”类别中,您可以安排RunDeck作业,以非常简单的方式运行ansible playbooks,任何登录到控制台的人都可以在运行时查看运行的内容
当然还有很多更好的理由,但我的手指已经厌倦了打字;) 您还可以使用包装器的简单变体:
# 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
您可以使用flock命令,该命令将使用基于文件系统的flock(2)包装您的命令: 以最适合用户的方式包装它。这将在/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
我把这个放在我的主要剧本里
hosts: all.
lock\u file\u path
:此文件的存在表明当前正在运行的ansible deploy或之前有一个由于某种原因而中止的deploy
force\u ignore\u lock
:此默认值为false,通过可在命令行包装中设置为ansible的选项标志重置。它使ansible能够继续部署
这是干什么的
pre_任务
第一个pre_任务
检查lock_file_路径
是否存在,并将结果记录在名为lock_file
的寄存器中
然后,下一个任务检查该文件是否存在,以及部署人员是否选择忽略该文件(希望在与其他队友沟通后)。否则,作业将失败,并显示一条有用的错误消息
如果用户选择继续部署,即使存在锁定文件
,下一个任务将删除先前创建的锁定文件
,并创建一个新文件。然后,所选角色中的其他任务将愉快地继续
post\u任务
这是在部署中的所有任务完成后立即调用的钩子。此处的任务将删除锁定文件
,使下一个人能够愉快地部署,而不会出现任何问题
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
当部署作业可以从多个生成主机运行时,包装器脚本没有用处。对于这种情况,锁定必须由playbook处理 Ansible现在有一个可用于锁定的模块。下面是一个简短的示例(不考虑过期锁):
Ansible将在可配置的超时时间内检查锁文件,如果在该时间段内未删除,则放弃。我将研究一种分布式锁机制,如zookeeper,我将其作为一个角色包括在内,因为有一个模块。为实现高可用性和目标节点的写锁定而分发
角色将在开始时在
/deployment/
下写入目标名称的znode,然后将其删除。如果锁已经存在,您可以失败并在块中抛出一条消息
,我实际上已经在谷歌上搜索了一些方法,作为可能的解决方案,但找不到它。谢谢哇!由于测试和创建锁文件之间的时间间隔,此脚本将主要阻止两个实例运行,但不会完全阻止。你需要一个原子操作来获得真正的保护,虽然我已经过时了*nix,但我认为脚本无法做到这一点。我假设在下一个答案中提到的RunDeck可能具有所需的功能,使用IPC(信号量等)。理论上,是的,没有100%。您可以通过执行类似于[-e$lock]| |(echo$SSH|u CLIENT | cut-f1-d'>$lock)
(并相应地调整脚本)的操作来降低差距,这需要不到一毫秒的时间。实际上,这听起来很合理。或者使用受限制的烫发写入锁定文件,以便其他用户无法覆盖它。总而言之,当然
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
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