在 LVM 快照期间阻止服务器(Git/ssh)访问

在 LVM 快照期间阻止服务器(Git/ssh)访问

如果在 LVM 快照时正在进行 Git 操作,则存储库可能会(并且将会)在损坏的状态下被快照。(这已经讨论了很多次http://www.reddit.com/r/programming/comments/1ax0oa/how_kdes_1500_git_repositories_almost_were_lost/

问题是如何正确解决这个问题。Git 访问是通过 git-shell 通过 ssh(带密钥)连接到 Linux 机器的。每晚停止访问 20 分钟左右对于我们的部门来说不是问题,但我担心魔鬼藏在细节中。

我到目前为止想到的东西让我感觉我正在重新发明一些我不知道的轮子。来自一个 cron 作业:

#!/bin/bash
# 0.
# poll during 20 min for ongoing git usage to stop
countdown=$((60*20))
while pgrep -u git >/dev/null ; do
    sleep 1
    countdown=$((countdown-1))
    if [ $countdown -eq 0 ] ; then 
       break
    fi
done

#1
#disable login to the git user by setting the shell to /bin/nologin
chsh -s /bin/nologin git

#2
# 
# wait again for up to 20 min for all processes to complete 
# (we *may* have just missed it between step 1 and 2)
#
countdown=$((60*20))
while pgrep -u git >/dev/null ; do
    sleep 1
    countdown=$((countdown-1))
    if [ $countdown -eq 0 ] ; then 
       break
    fi
done

#3
# kill too slow git sessions (This is actually safe)
if pgrep -u git >/dev/null ; then
    killall -u git 
    sleep 30 
fi
if pgrep -u git >/dev/null ; then
    killall -9 -u git 
    sleep 10
fi
if pgrep -u git >/dev/null ; then
    echo Failed to kill stale git $(pgrep -u git)
fi

#4
# make the lvm snapshot ...

#5
# change back the shell
chsh -s /usr/bin/git-shell git

我很想知道是否有更多标准解决方案,或者我的解决方案是否存在缺陷。

这种方法有些地方感觉太过手工操作了。我也不喜欢考虑死进程之类的极端情况。此外,服务器可能会在脚本执行期间关闭,因此我必须将 shell 重新设置为 git-shell 来修复该问题(通过 cron 或在启动时)。

答案1

我认为正确的方法是使用更合适的备份策略。既然可以使用 git 本身进行备份,为什么要使用 LVM 快照?

简单、未经测试的示例

#!/bin/sh
cd /backups/git
for repo in $(ssh repo_host ls /srv/git); do
    if [ ! -e $repo ]; then
        git clone --mirror repo_host:/srv/git/$repo
    else
        (cd $repo; git fetch origin)
    fi
done

答案2

我不知道这方面的标准化解决方案,但您的方法对我来说似乎很合理。以下几点需要注意:

  1. 我会更进一步,阻止所有非 root 登录,因为您确实不希望用户在您的 LVM 操作运行时登录并使用您的存储。幸运的是,这在 中非常简单/etc/nologin,前提是您的 PAM 配置/etc/pam.d包括pam_nologin.so。许多发行版都这样做,但您需要测试。man pam_nologin了解详细信息。

  2. 用来pkill代替 pgrep+killall。它使用与 pgrep 相同的进程匹配和返回代码逻辑。

  3. 您正在重定向 STDOUT,但不是 STDERR。我使用的一个技巧是将要重定向的命令块括在括号中,然后重定向括号的输出。请记住,如果您正在执行单行命令,{}这些括号需要在最后一个命令后返回或。;


#3 的示例:

{
    echo foo
    echo bar >&2 # output bar to STDERR
} >/dev/null 2>&1

答案3

为什么你不阻止任何 ssh 登录?添加到 sshd_config:

#DenyGroups <a supplementary group for all git users>

然后在备份 cronjob 的开始处:

sed -i -e 's/^#DenyGroups/DenyGroups/' sshd_config
kill -HUP /var/run/sshd.pid

完成后再反转。但是对于当前登录的用户,我知道没有其他选择,只能像您已经做的那样终止他们的会话。或者等到他们注销。

相关内容