$ cd /home/Lu/

Keep-learning Lu

17 Oct 2021

部署Hugo

这个项目是在今年夏天完成的,该设想在全年年底就已经形成了,迫于工作上总是有别的事情要忙而迟迟没有开始。前期的我一直在完善Rapid.Space 使用手册Rapid.Space 学习进程,对于部署软件在SlapOS上的实际操作总是手痒。好在法国的夏天,工作没有这么紧迫,我终于在夏天快要结束的时候完成了这个小项目。虽然里尔的夏天其实并没有来,今年一直都好冷。

目前在网页端IDE(Theia)运行Hugo的教程已经在Rapid.Space 用户使用手册上发布了:

该项目的主旨也是在Theia环境中进行网站的修改,并以此为云平台和服务器进行网站的发布和运行。当然我还添加了Nginx服务,或许可以在未来作为产品网站的服务器,但在本次版本中还未得到区分。本次开发过程,我也是在Theia上完成的。Theia已经在SlapOS上发布并处于测试阶段。

附:项目的Merge Request源码:software/hugo: Add Hugo software in slapos

添加组件(Component)

众所周知,Hugo是一款由go语言编写的静态网站生成工具,所以我们首先要确认的是,SlapOS架构中是否已经有go的存在,这将为我们省去不少麻烦。Golang 确实已经实现了,那我们就可以直接开始写hugo的组件了。

根据Hugo的github仓库的提示:

Build and Install the Binaries from Source (Advanced Install)

Prerequisite Tools

Fetch from GitHub

Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. The easiest is to clone Hugo in a directory outside of GOPATH, as in the following example:

mkdir $HOME/src
cd $HOME/src
git clone https://github.com/gohugoio/hugo.git
cd hugo
go install

我们的实现过程主要就是获取仓库内容以及用go安装。

我们可以先通过[hugo-get]实现下拉仓库的功能:

[hugo-get]
<= go-git-package
go.importpath = github.com/gohugoio/hugo
repository    = https://github.com/gohugoio/hugo.git
revision      = f6821b88abbc1237af0e28cefbc624e6cda3305a

在产品端的revision,我们一般还是更倾向于固定commit,方便管理。

接着就是go install的部分:

[gowork]
install =
  ${hugo-get:location}:./...
environment =
  CGO_ENABLED = 1
buildflags = --tags extended

[gowork.goinstall]
depends_gitfetch  =
    ${hugo-get:recipe}

command = set -e
  . ${gowork:env.sh}
  cd ${hugo-get:location}
  go build ${gowork:buildflags} -o ${gowork:bin}/hugo

具体的参数设置可以在hugo的文档中找到,并对应在buildout中设置。

最后通过[hugo]进行打包,该组件便可以实现快速复用。

[hugo]
<= gowork.goinstall

添加Hugo软件

software.cfg

让我们先从software.cfg看起:

extends =
	...
  ../../component/hugo/buildout.cfg
	...

parts =
  ...
  hugo
  ...

首先在extends中添加刚才写的hugo组件,并提供上文中提到的打包好的hugo。如此一来,我们就拥有了hugo的工作环境,

[profile-common],我定义了一些必要的路径变量:

[profile-common]
nginx_location = ${nginx:location}
dash_location = ${dash:location}
go_environment = ${gowork:env.sh}
openssl_location = ${openssl:location}
template_nginx_conf = ${template_nginx_conf:target}
template_mime_types = ${template_mime_types:target}
template_index_html = ${template_index_html:target}
template_monitor = ${monitor-template:rendered}

接下来是必要的模版[template-cfg],它具体对应的就是下文会详细展开的instance.cfg.in

[template-cfg]
recipe = slapos.recipe.template:jinja2
rendered = ${buildout:directory}/template.cfg
template = ${:_profile_base_location_}/${:filename}
mode = 0644
context =
  section buildout buildout
  section parameter_list profile-common

可以在content部分看到,我将profile-common定义给了parameter_listparameter_list就可以代表profile-commoninstance.cfg.in被使用了。

接下来是使用download菜谱,并通过<=复用:

[download-base]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_update_hash_filename_}
mode = 0644

[template_nginx_conf]
<= download-base

[template_mime_types]
<= download-base

[template_index_html]
<= download-base

复用的含义,举个例子:

[template_nginx_conf]
<= download-base

其实就相当于:

[template_nginx_conf]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_update_hash_filename_}
mode = 0644

为了减少代码的赘余,而将下载菜谱模块化。而下载菜谱的意义就是使用template_nginx_conftemplate_mime_typestemplate_index_html这几个模版。

instance.cfg.in

接下来,我们来看看hugo的实例instance.cfg.in是如何完成的:

定义参数:

[slap-configuration]
recipe = slapos.cookbook:slapconfiguration
computer = ${slap-connection:computer-id}
partition = ${slap-connection:partition-id}
url = ${slap-connection:server-url}
key = ${slap-connection:key-file}
cert = ${slap-connection:cert-file}
configuration.hugo-monitor-httpd-port = 8196
configuration.nginx-monitor-httpd-port = 8197
configuration.site = default

创建需要的路径:

[directory]
...

[basedirectory]
...

[tempdirectory]
...

定义端口:

[hugo-frontend-port]
recipe = slapos.cookbook:free_port
minimum = 1313
maximum = 1323
ip = ${slap-configuration:ipv6-random}

[nginx-frontend-port]
recipe = slapos.cookbook:free_port
minimum = 1324
maximum = 1334
ip = ${slap-configuration:ipv6-random}

当然,我们也可以直接在[slap-configuration]中直接定义,但是我还是用了free_port的烹饪书进行空余端口的筛选,这样可以保证不管是hugo的服务器也好,还是nginx的服务器也好,都可以顺利地启动而不受冲突端口的干扰。

接下来的[hugo]模块同样也主要是一些参数的设置,包括hugo/nginx服务器的后端地址及其端口,一些路径等。

[hugo]
nb-workers = 2
go-environment = {{ parameter_list['go_environment'] }}

ip = ${slap-configuration:ipv6-random}
hugo-port = ${hugo-frontend-port:port}
nginx-port = ${nginx-frontend-port:port}

hugo-access-url = http://[${:ip}]:${:hugo-port}
nginx-access-url = https://[${:ip}]:${:nginx-port}

path-pid = ${basedirectory:run}/nginx.pid
path-log = ${basedirectory:log}/nginx.log
path-access-log = ${basedirectory:log}/nginx.access.log
path-error-log = ${basedirectory:log}/nginx.error.log
path-tmp = ${tempdirectory:tmp}

path-nginx-conf = ${directory:etc}/nginx.conf
path-mime-types = ${directory:etc}/mime_types
path-nginx = {{ parameter_list['nginx_location'] }}/sbin/nginx

# Docroot
docroot = ${basedirectory:data}/${slap-configuration:configuration.site}/public

其中Nginx的设置也主要是参考Nginx的官方资料,比如其log、配置文件的路径。

这里我也定义了go环境,在使用hugo之前,我们总是需要先source go环境,

go-environment = {{ parameter_list['go_environment'] }}

如果你还记得的话,你会发现,parameter_list是来自software.cfg中的[profile-common]模块。

接下来是一个Hugo服务器的实现。

[hugo-server]
recipe = slapos.recipe.template:jinja2
rendered = ${directory:bin}/hugo-server
mode = 0700
template =
  inline:#!/bin/sh
  . ${hugo:go-environment}
  cd ${basedirectory:data}/${slap-configuration:configuration.site}
  if [ -d "public" ]; then rm -Rf public; fi
  hugo && hugo server --bind=${hugo:ip} --port=${hugo:hugo-port} --baseURL=${hugo-frontend:connection-secure_access} --appendPort=false

${directory:bin}路径下的程序可以一直运行,这正符合了我们对hugo服务器的要求。

template =
  inline:#!/bin/sh

用这种方法,我们可以简便地将bash语句放入模版中。具体的hugo命令需参考Hugo官方文档。我这里主要是保证public文件为最新,因为它会被放在在Ngnix服务器上。另外就是一些端口和IP地址的配置。

剩余部分,则可以参考HTML5AS教程。

Templates

模版部分,我最后只留下了3个比较冗长的内容。其他我都尽量以template =形式写在software.cfg中。

index.html.in很明显就是一个主页html。但其中也免不了一些来自software.cfginstance.cfg.in中的参数:

<pre><code>$ cd {{ data }}</code></pre>

nginx.conf.in也很熟悉,是nginx服务器配置的必要文件。

测试

最后是必不可少的测试部分。可参考How To Test Your Software Release

为了快点结束,~~因为夏天要过去了,~~我只进行了几项简单内容的测试。

未完成的工作

这个软件的开发其实十分粗糙,因为一开始我把自己局限在了HTML5AS的教程里,试图依葫芦画瓢地将Hugo做出来。这让后期设计上的改动变得无比困难,也就是费时间。我可以大致罗列一下问题:

  1. 为了运行hugo,命令行是必须的。也就是一个像Theia一样的IDE是必不可少的。如果能把Hugo嵌入Theia,那么用户在使用的时候就可以一步到位,并且有一个标准化的生产环境。
  2. 这一版Hugo中,我我留下了两个web服务器:Hugo和nginx。本意是Hugo将作为调试服务器,而nginx则变成生产服务器。但是目前这两个服务器的作用是一样的,如果后期能改进,希望可以进行明确区分。比如对于Nginx服务器,可以直接通过一个git仓库的URL的参数而运行。
  3. 测试方面还有很多没有涵盖到的地方。

纸上读来终觉浅,绝知此事要躬行。

另外,我想起来为什么自己之前都不怎么用中文写了:为了打Markdown符号需要中英文转换。。。

comments powered by Disqus