如何在Ubuntu14.04上使用GitHooks将Hugo站点部署到生产环境
介绍
Hugo 是一个静态站点生成器,它允许您通过使用简单的标记语言编写来轻松创建和发布 Web 内容。 Hugo 可以根据提供的要求解析您的内容并应用主题以生成可以轻松托管在任何 Web 服务器或主机上的一致网页。
在 上一篇指南 中,我们介绍了如何将 Hugo 安装到 Ubuntu 14.04 系统上并使用它来创建内容。 在本指南中,我们将向您展示如何使用 git
设置系统,您可以使用该系统将新内容自动部署到生产 Web 服务器。
先决条件
对于本指南,我们假设您已启动并运行 Ubuntu 14.04 机器作为您的开发机器,如我们的 Hugo 安装指南 中所述。
我们将设置一个 second Ubuntu 14.04 服务器来服务于我们的实际生产网站。 在此服务器上,确保您已创建具有 sudo
权限的非 root 用户。 您可以按照我们的初始服务器设置指南创建此帐户。
准备你的开发服务器
我们将从我们的开发服务器开始(通过之前的 Hugo 指南设置的服务器)。 使用您上次使用的非 root 帐户登录该服务器。
我们需要在这台服务器上做一些事情来设置一步部署。 我们要:
- 配置对我们的生产服务器的 SSH 密钥访问
- 将初始
git
存储库传输到生产服务器 - 在我们的站点存储库中将生产服务器添加为
git
远程
让我们开始吧。
配置对生产服务器的 SSH 密钥访问
我们要做的第一件事是在我们的两台服务器之间配置 SSH 密钥访问。 这将使我们无需每次都输入密码即可部署。 如果您想在每次部署时提示您输入密码,您可以跳过此步骤。 有些人喜欢在部署过程中保留密码提示,以便在推送内容之前重新考虑一下。
首先,检查您是否已经在开发服务器上的帐户中配置了 SSH 密钥对:
ls ~/.ssh/id_rsa
如果您返回如下所示的一行,则说明您尚未配置 SSH 密钥对:
Outputls: cannot access /home/demouser/.ssh/id_rsa: No such file or directory
您可以通过键入以下内容来创建缺少的密钥对:
ssh-keygen
在所有提示中按 ENTER 以创建无密码密钥。
另一方面,如果 ls
命令为您提供了如下所示的行,则您的帐户中已经有一个密钥:
Output/home/demouser/.ssh/id_rsa
一旦你有了一个密钥对,你可以通过输入这个来将你的公钥传输到你的生产服务器。 在命令中,替换您在本指南的先决条件阶段在生产服务器上配置的非 root 帐户名称:
ssh-copy-id username@production_domain_or_IP
如果这是您第一次在这两台计算机之间使用 SSH,系统会要求您输入“yes”以确认连接。 之后,系统将提示您输入生产服务器的用户密码。 您的公钥将被传输到生产服务器,让您下次无需密码登录。
通过使用 ssh
命令询问生产服务器的主机名来测试此功能:
ssh username@production_domain_or_IP cat /etc/hostname
这次不应提示您输入密码。 您应该收到生产服务器的主机名:
Outputprodserver
将初始 Git 存储库传输到生产服务器
接下来,我们需要将 Hugo 存储库的初始克隆传输到我们的生产服务器。 我们将需要它以便稍后在生产服务器上设置 post-receive
挂钩。 为此,我们需要创建 git
存储库的“裸”克隆并将其复制到我们的其他服务器。
裸仓库是一个特殊的 git
仓库,没有工作目录。 在传统的 git
存储库中,您的项目文件保存在主目录中,而 git
版本控制数据保存在名为 .git
的隐藏目录中。 裸仓库没有项目文件的工作目录,因此通常保存在隐藏 .git
文件夹中的文件和目录改为位于主文件夹中。 裸存储库通常用于远程服务器,因为它简化了推送内容的过程。
我们将从 /tmp
目录中的主 Hugo 存储库创建一个裸存储库。 裸回购通常由尾随 .git
后缀标识。 要创建此副本,我们将使用带有 --bare
选项的 git clone
命令:
git clone --bare ~/my-website /tmp/my-website.git
我们可以使用 scp
将此裸存储库传输到我们的生产服务器。 确保在命令末尾包含尾随“:”,以便将 repo 放置在远程系统上我们用户的主目录中。
scp -r /tmp/my-website.git username@production_domain_or_IP:
为生产服务器添加 Git 远程
现在我们在生产服务器上拥有了裸 git
存储库,我们可以将生产服务器添加为跟踪的远程存储库。 这将使我们能够轻松地将新内容推送到我们的生产服务器。
回到你的 Hugo 目录:
cd ~/my-website
我们需要做的就是决定遥控器的名称。 在本指南中,我们将使用 prod
。 然后我们可以指定远程系统上裸存储库的连接信息和位置:
git remote add prod username@production_domain_or_IP:my-website.git
在我们在生产服务器上安装 git
之前,您将无法测试此远程链接。
设置生产服务器
现在我们的开发机器已经完全配置好了,我们可以继续设置我们的生产系统了。
在我们的生产系统上,我们需要完成以下步骤:
- 安装
git
、nginx
和pygments
- 安装 Hugo 和 Hugo 主题
- 配置
nginx
以从我们的主目录中的某个位置提供文件 - 创建一个
post-receive
脚本来部署推送到我们存储库的新内容
在生产服务器上安装 Git、Pygments 和 Nginx
我们应该做的第一件事是将 git
、pygments
和 nginx
安装到服务器上。 虽然我们的项目存储库已经在我们的服务器上,但我们需要 git
软件来接收推送并执行我们的部署脚本。 我们需要 pygments
为任何代码块应用服务器端语法高亮。 我们将使用 nginx
作为 Web 服务器,让访问者可以访问我们的内容。
更新本地包索引并从 Ubuntu 的默认存储库安装 git
和 nginx
。 我们需要安装 Python 包管理器 pip
来获取 pygments
:
sudo apt-get update sudo apt-get install git nginx python-pip
然后我们可以使用 pip
来安装 pygments
:
sudo pip install Pygments
下载完成后,我们可以测试我们在开发机器上是否正确设置了远程存储库。 在您的 开发机 上,进入您的 Hugo 项目目录并使用 git ls-remote
命令:
cd ~/my-website git ls-remote prod
如果 git
可以在您的开发和生产机器上的存储库之间建立连接,它将显示一个参考列表,如下所示:
Output103902f5d448cf57425bd6830e544128d9522c51 HEAD 103902f5d448cf57425bd6830e544128d9522c51 refs/heads/master
在生产服务器上安装 Hugo
回到我们的 生产服务器,我们需要安装 Hugo。 我们不会在我们的开发服务器上构建我们的内容,而是在每个 git push
之后在生产服务器上构建静态资产。 为此,我们需要安装 Hugo。
我们可以使用与开发机器相同的方法安装 Hugo。 首先检查生产服务器的架构:
uname -i
接下来,访问 Hugo 发布页面。 向下滚动到最新的 Hugo 版本的“下载”部分。 右键单击与您的体系结构对应的链接:
- 如果
uname -i
命令产生x86_64
,右键复制以amd64.deb
结尾的链接 - 如果
uname -i
命令产生i686
,右键复制以i386.deb
结尾的链接
在您的生产服务器上,进入您的主目录并使用 wget
下载您复制的链接:
cd ~ wget https://github.com/spf13/hugo/releases/download/v0.14/hugo_0.14_amd64.deb
下载完成后,您可以通过键入以下命令安装包:
sudo dpkg -i hugo*.deb
通过要求 Hugo 显示其版本号来测试安装是否成功:
hugo version
您应该会看到显示的版本行,表明 Hugo 现在可用:
OutputHugo Static Site Generator v0.14 BuildDate: 2015-05-25T21:29:16-04:00
在我们继续之前,我们需要将 Hugo 主题克隆到我们的生产服务器:
git clone --recursive https://github.com/spf13/hugoThemes ~/themes
配置 Nginx 以提供部署期间生成的文件
接下来,我们需要稍微修改一下我们的 Nginx 配置。
为了简化我们的部署,不是将我们生成的内容放在 var/www/html
目录中,而是将内容放在我们用户主目录中名为 public_html
的目录中。
现在让我们继续创建 public_html
目录:
mkdir ~/public_html
接下来,让我们打开默认的 Nginx 服务器块配置文件进行必要的调整:
sudo nano /etc/nginx/sites-available/default
我们需要更改的唯一选项是 server_name
,它应该指向您的生产服务器的域名或 IP 地址,以及 root
指令,它应该指向 public_html
我们刚刚创建的目录:
/etc/nginx/sites-available/default
server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; root /home/username/public_html; index index.html index.htm; # Make site accessible from http://localhost/ server_name server_domain_or_IP; . . .
确保将 root
指令中的“用户名”替换为生产服务器上的实际用户名。 完成后保存并关闭文件。
重新启动 Nginx 服务器以应用您的更改:
sudo service nginx restart
我们的 Web 服务器现在已准备好为我们放入 public_html
目录的文件提供服务。
创建一个接收后挂钩以部署 Hugo 站点
现在,我们终于准备好创建我们的 post-receive
部署挂钩脚本了。 每当您将新内容推送到生产代码时,都会调用此脚本。
要创建此脚本,我们将进入生产服务器上的裸存储库,进入名为 hooks
的目录。 现在进入该目录:
cd ~/my-website.git/hooks
这个目录有很多示例脚本,但我们需要一个 post-receive
脚本来编写本指南。 在 hooks
目录中创建并打开一个具有此名称的文件:
nano post-receive
在文件的顶部,在指出这是一个 bash 脚本之后,我们将从定义一些变量开始。 我们将 GIT_REPO
设置为我们的裸存储库。 我们将把它克隆到由变量 WORKING_DIRECTORY
指定的临时存储库,以便 Hugo 可以访问其中的内容以构建实际站点。 因为我们的 git
repo 中的主题目录实际上只是一个指向父目录中某个位置的符号链接,所以我们需要确保工作目录克隆创建在与我们下载的 Hugo 主题相同的位置.
公共 Web 文件夹将由 PUBLIC_WWW
变量指定,备份 Web 文件夹将通过 BACKUP_WWW
变量保持访问。 最后,我们将 MY_DOMAIN
设置为我们服务器的域名或公共 IP 地址:
考虑到这一点,文件的开头应如下所示:
~/my-website.git/hooks/post-receive
#!/bin/bash GIT_REPO=$HOME/my-website.git WORKING_DIRECTORY=$HOME/my-website-working PUBLIC_WWW=$HOME/public_html BACKUP_WWW=$HOME/backup_html MY_DOMAIN=server_domain_or_IP
设置好变量后,我们就可以开始编写脚本的逻辑了。 首先,我们将使用 bash 的 set -e
命令指定脚本在遇到任何错误时应立即退出。 我们将在出现问题时使用它进行清理。
之后,让我们确保为我们的部署设置了环境。 我们想删除任何现有的工作目录,因为我们想在部署期间克隆一个新副本。 我们还想备份我们的 web 目录,以便在出现任何问题时可以恢复。 我们在这里使用 rsync
是因为它比 cp
更一致地处理空目录和其中包含内容的目录。 确保在 $PUBLIC_WWW
之后包含尾随 /
,以便正确解析 ca 命令。
我们要做的最后一个设置过程是设置 trap
命令以在收到“退出”信号时做出响应。 由于我们包含了 set -e
命令,因此只要部署中的命令失败,就会出现退出信号。 在这种情况下,陷阱指定的命令会将我们的备份副本恢复到 web 目录并删除工作 git
目录的任何实例。
~/my-website.git/hooks/post-receive
#!/bin/bash GIT_REPO=$HOME/my-website.git WORKING_DIRECTORY=$HOME/my-website-working PUBLIC_WWW=$HOME/public_html BACKUP_WWW=$HOME/backup_html MY_DOMAIN=server_domain_or_IP set -e rm -rf $WORKING_DIRECTORY rsync -aqz $PUBLIC_WWW/ $BACKUP_WWW trap "echo 'A problem occurred. Reverting to backup.'; rsync -aqz --del $BACKUP_WWW/ $PUBLIC_WWW; rm -rf $WORKING_DIRECTORY" EXIT
现在,我们可以开始部署了。 我们将为我们的裸仓库创建一个常规克隆,以便 Hugo 可以访问仓库内容。 然后,我们将从公共 Web 目录中删除所有内容,以便公共 Web 目录中只有新文件可用。 之后,我们将使用 Hugo 来构建我们的网站。 我们将把它指向我们的新克隆作为源目录,并告诉它把生成的内容放在公共 web 文件夹中。 我们还将向它传递包含我们生产服务器的域名或 IP 地址的变量,以便它可以正确地建立链接。
在 Hugo 构建内容之后,我们将删除工作目录。 然后我们将重置 trap
命令,以便我们的备份副本在脚本尝试退出时不会立即覆盖我们的新内容:
~/my-website.git/hooks/post-receive
#!/bin/bash GIT_REPO=$HOME/my-website.git WORKING_DIRECTORY=$HOME/my-website-working PUBLIC_WWW=$HOME/public_html BACKUP_WWW=$HOME/backup_html MY_DOMAIN=server_domain_or_IP set -e rm -rf $WORKING_DIRECTORY rsync -aqz $PUBLIC_WWW/ $BACKUP_WWW trap "echo 'A problem occurred. Reverting to backup.'; rsync -aqz --del $BACKUP_WWW/ $PUBLIC_WWW; rm -rf $WORKING_DIRECTORY" EXIT git clone $GIT_REPO $WORKING_DIRECTORY rm -rf $PUBLIC_WWW/* /usr/bin/hugo -s $WORKING_DIRECTORY -d $PUBLIC_WWW -b "http://${MY_DOMAIN}" rm -rf $WORKING_DIRECTORY trap - EXIT
至此,我们的脚本就完成了。 完成后保存并关闭文件。
我们现在要做的就是使脚本可执行,以便 git
可以在适当的时候调用它:
chmod +x post-receive
我们的部署系统现已完成。 让我们测试一下。
测试部署系统
现在我们已经设置了系统,我们可以继续测试它。
让我们从测试我们的 post-receive
钩子脚本开始。 这将允许我们使用 Web 内容的初始副本填充我们的 ~/public_html
目录。 它还将有助于验证脚本的主要组件是否按预期工作:
bash ~/my-website.git/hooks/post-receive
这应该运行您的脚本并将正常的 git
和 Hugo 消息输出到屏幕:
OutputCloning into '/home/justin/my-website-working'... done. 0 draft content 0 future content 4 pages created 0 paginator pages created 0 tags created 1 categories created in 26 ms
如果您在 Web 浏览器中访问生产服务器的域名或 IP 地址,您应该会看到您网站的当前版本:
http://production_domain_or_IP
现在,我们可以回到我们用于 Hugo 开发的机器。 在这台机器上,让我们创建一个新帖子:
hugo new post/Testing-Deployment.md
在新帖子中,只需添加一些内容,以便我们可以测试我们的系统:
~/my-website/content/post/Testing-Deployment.md
+++ categories = ["misc"] date = "2015-11-11T16:24:33-05:00" title = "Testing Deployment" +++ ## A Test of the New Deployment System I hope this works!
现在,将内容添加到 git
并提交更改:
git add . git commit -m 'Deployment test'
现在,如果一切按计划进行,我们可以通过推送到我们的生产服务器来部署我们的新更改:
git push prod master
现在,如果您在 Web 浏览器中重新访问您的生产站点,您应该会看到新内容:
http://production_domain_or_IP
我们的部署系统似乎运行正常。
结论
在本指南中,我们设置了一个单独的生产服务器,专门为访问者提供我们的 Web 内容。 在这台服务器上,我们安装并配置了多个组件,以便 Hugo 可以正确构建和提供我们的内容。 然后,我们创建了一个部署脚本,当我们将新内容从我们的开发机器推送到服务器时,该脚本就会被触发。
我们的部署系统中涉及的实际机制是相当基本的。 但是,它们构成了一个易于维护的系统的基础,可以快速、轻松地在 Web 服务器上获取本地内容。 由于部署过程是自动化的,因此您无需与服务器交互即可进行简单的 git push
之外的更改。