使用git和Makefile备份您的网站

将站点转换为一组静态网页,可以减少服务器的负载,甚至可以利用免费存储,还可以提高站点的可靠性,速度和安全性。 在本文中,我将讨论如何使用熟悉的gitMakefile工具执行此操作。 这种方法的优点是可以控制网页内容的版本。


本文介绍了如何制作用于服务器发行的静态网页版本,以及如何将其放置在存储库中以进行版本控制和备份。 同时,静态文件和媒体文件可以分别存储,也可以通过其他方式存档(静态文件通常存放在网站程序代码的存储库中)。 该方法还适用于具有Unicode名称的页面(例如,对于西里尔域)。 最后是一个正常的Makefile。


作者使用django / uwsgi / nginx堆栈,这是一种运行GNU / Linux的虚拟专用服务器,但是本文的内容几乎与特定技术无关。


载入页面


我们将使用标准的wget程序保存网站页面。 我们将每个站点保存在一个单独的目录中(该目录可能与该站点的域名无关)。


在每个目录中,将使用wget -r 递归保存页面(假定可以通过主页上的链接访问所有页面)。 缺省情况下,递归复制达到5级,但是可以使用-l开关更改。


如果我们将媒体和静态文件与文本页面分开存储,则使用-X开关将忽略相应的目录。


完整的命令如下所示:


mkdir primer cd primer wget -r -nH -X ,static --restrict-file-names=nocontrol . 

-nH表示--no-host-directories 。 缺省情况下, wget -r example.com会将所有内容放入example.com/目录,此选项将取消使用主机名创建目录。


--restrict-file-names选项指示在创建本地文件时URL中的字符转义。 nocontrol的值表示禁用转义,对于保存带有西里尔链接的页面非常重要。 没有它,页面将保存在名称稍有更改的文件中,并且尚不清楚如何将其发布到服务器。 不幸的是,对于Windows用户,-- restrict-file-names = nocontrol将不适用于他们,这是一个已知问题


添加到git


使用git init命令创建一个新的存储库。 默认情况下,它是在.git文件夹中的当前目录中创建的,但是我们希望服务器只能访问与该站点的打开页面名称相对应的文件。 因此,在../.git-primer文件夹中创建干净(裸)存储库的完整命令如下所示:


 git init --bare ../.git-primer 

要进一步使用此非标准存储库,您需要向git传递git-dirwork-tree选项:


 git --git-dir=../.git-primer --work-tree=. add . 

编写一个Makefile


让我们开始宣布我们的项目:


 SITES := example primer all : $(SITES) .PHONY : $(SITES) 

SITES变量包含我们项目的名称。 默认目标是第一个目标,即所有目标,即完成所有步骤,只需键入一个make命令SITES中的所有目标都是虚构的( PHONY ):将执行每个目标的配方,而不考虑目录的存在和更改时间。
例如,可以在此处阅读make的基本介绍,而基本指南是info makeoriginaltranslation )。


每个项目的规则如下所示:


 $(SITES) : if [[ -d .git-$@ ]]; \ then \ $(get-data); \ $(mgit) add . && \ if [[ -n "`$(mgit) status --porcelain`" ]]; then \ $(mgit) commit -m "Update $@."; \ fi \ else \ $(init-git); \ fi 

该规则本质上是单个shell命令。
$ @是一个自动变量 ,其中包含当前目标的名称(例如,primer)。
首先,我们检查.git-primer目录是否存在。 如果是这样,请转到项目目录,下载页面,然后将其添加到git中。
如果页面的内容未更改,则git 不会添加任何内容,但是在这种情况下, 提交将导致错误,并且Makefile的执行将停止。 因此,首先我们将git status选项称为- 瓷器 ,该选项旨在用于脚本中。 如果git status --porcelain的输出行长度不为零,则可以进行提交( 从此处开始 )。


get-data,mgit和init-git是Makefile中的罐装配方。 例如, mgit是一个git调用,用于指定包含存储库和工作目录文件的目录:


 define mgit = git --git-dir=../.git-$@ --work-tree=. endef 

当可以在多个配方中使用一个命令序列时,就会创建罐装配方。 它们可以由几行组成,每行都将在配方中的一个选项卡中自动突出显示(更确切地说,使用.RECIPEPREFIX符号)。 在我们的示例中,缩进仅用于Makefile的可读性。
在执行配方期间,准备好的序列的每一行都被解释为配方的单独一行,也就是说, 为此 ,它们可以使用自动变量。


完整的Makefile如下所示:


 SITES := primer example SERVERHOST := example # .  punicode SERVERHOSTNAME := xn--e1afmkfd.xn--p1ai SERVERPATH := ~/archive all : $(SITES) .PHONY : $(SITES) # target-specific variables primer : DOMAIN := . primer : EXCLUDEDIRS := ,static example : DOMAIN := example.com ifeq ($(SERVERHOSTNAME),$(shell hostname)) # Server define mgit = git --git-dir=../.git-$@ --work-tree=. endef define init-git = mkdir -p $@ && \ $(get-data) && \ git init --bare ../.git-$@ && \ $(mgit) add . && \ $(mgit) commit -m "Initial commit of $@." endef define get-data = cd $@ && \ wget -r -nH -X $(EXCLUDEDIRS) --restrict-file-names=nocontrol $(DOMAIN) endef else # Workstation define init-git = git clone $(SERVERHOST):$(SERVERPATH)/.git-$@ $@ endef endif $(SITES) : ifeq ($(SERVERHOSTNAME),$(shell hostname)) # Server if [[ -d .git-$@ ]]; \ then \ $(get-data); \ $(mgit) add . && \ if [[ -n "`$(mgit) status --porcelain`" ]]; then \ $(mgit) commit -m "Update $@."; \ fi \ else \ $(init-git); \ fi else # Workstation if [[ -d $@/.git ]]; \ then \ cd $@ && git pull; \ else \ $(init-git); \ fi endif 

在第四段中,有特定目标的变量:对于每个目标,您可以为此变量设置自己的值。 还会根据每个目标的先决条件和所使用的准备好的配方来传输这些值,也就是说,我们可以确保使用正确的站点名称及其目录执行每个站点的配方。
对于每个项目,我们都可以通过EXCLUDEDIRS变量传输未归档的目录,或将其保留为空。 同样,您可以更改用于从工作计算机进行归档的服务器名称( SERVERHOST ),以及服务器上具有站点归档文件的目录( SERVERPATH )的路径。 为简单起见,在此示例中,所有站点都位于同一服务器上,并存档在同一目录中。
由于配方的每一行(包括准备好的每一行)都在单独的shell中执行,因此对以下命令而言,向目录的转换仍然有效,因此我们使用“和” &&运算符,并对行\的末尾进行转义。


接下来是条件 Makefile 构造 :使用shell hostname命令,我们检查make是在服务器上还是在本地计算机上运行。 Makefile会完全忽略不满足条件指令当前分支的行。


本地存储库和服务器存储库之间的区别

本地计算机主要用于存储数据,因此我们仅将数据从服务器( git pull )复制到该计算机,并且为了方便在git本地工作(查看日志或文件版本),我们使用默认存储库结构(.git文件夹中的常用存储库) )
在两种情况下,一个make命令就足够了。 对于自动复制,可以使用cron计划程序。 为了不每次都输入用于访问服务器的密码,将生成ssh密钥。


为了方便在服务器上工作,可以使用指定的配置从当前站点的目录中创建别名 git:


 alias mgit="git --work-tree=. --git-dir=../.git-${PWD##*/}" 

变量$ {PWD ## * /}包含当前目录名称(不带路径),它POSIX标准的一部分,也就是说,它可以在所有启用POSIX的shell中使用。


条件指令也可以在配方中使用,唯一的限制是它的开始和结束不能在不同的文件中。


伺服器

运行make之后,归档目录如下所示:


 $ ls -a . .. .git-example .git-primer Makefile example primer $ ls -a primer . .. index.html - - $ # ,     .    ./-  ./- 

例如,nginx配置文件.rf可能如下所示:


 server { server_name xn--e1afmkfd.xn--p1ai; charset utf-8; location = / { root /home/user/archive/primer; try_files /index.html =404; } location / { root /home/user/archive/primer; default_type "text/html"; try_files $uri =404; } location = /index.html { return 404; } } 

第一个位置对应于网站的主页example.rf。 wget将其保存为index.html文件。 如果不是,则发出错误404。


对于所有其他URI,将检查底漆目录中名称为URI的文件。 如果找不到它们,则返回404。


最后,为了避免内容重复,我们明确拒绝访问example.rf / index.html(404)链接。 有关此配置的更多详细信息,请参见此处


结论


可以使用标准工具wgetgitmake来备份站点。 您可以复制站点的所有页面,也可以在wget允许的范围内准确排除媒体和许多其他文件。 同样,使用.gitignore ,您可以控制将哪些静态页面添加到存储库中进行备份,而哪些则不会。 Makefile允许您灵活管理各种项目的各种配置。 上面的客户端和服务器的完整Makefile示例仅包含约60行。


假定站点内容的更改和添加是通过标准机制发生的,即为此启动了CMS或CMF。 如果这种情况很少发生,则可以在工作后将其关闭,从而释放系统资源并显示已保存的静态页面。 更完整的自动化的示例可能值得一提。


提出的方法主要适用于很少更新的小型项目,因此此处几乎不考虑性能和安全性问题。 由于我们指示wget不要从URI中转义字符,因此,如果任意用户可以将文件添加到站点,则应立即进行转义或禁止添加文件。


更改页面的页面时,还可以通过数据库保存网站内容的版本。 但这需要CMF模型的版本支持,以及对数据库转储的更大控制(在任何页面编辑后完全复制它)。 在建议的方法中,如果内容有很小的更改,则仅将此更改添加到存储库中,并且不需要使用数据库的完整副本。 另外,生成的静态页面可以由服务器直接使用,也可以在浏览器中查看(更改设计或其他站点代码也将被复制)。


此处列出备用备份程序。 要存储和同步媒体文件,您应该注意git-annex 。 从工作树中分离.git存储库也已成功用于管理用户配置文件( dotfiles )。 如今,有些服务器直接支持使用git存储库。

Source: https://habr.com/ru/post/zh-CN425259/


All Articles