把 Hugo 博客封装到 Docker 镜像中

发表于 更新于

前言

对于 Hugo 生成的静态网站而言,通常是不必使用容器化技术部署的。但如果你想让所有用户保持配置的一致性,尽可能把所有优化都无缝推给用户,那么 Docker 化部署也会是一个不错的选择。

本文将向你演示怎么做!这是一篇新手向教程。过程十分简单!

更合理的配置

在进入正题前,我们先讨论下何为前言中提及的“优化”配置。这是一个常见的生产模式下的 JS 模板部分:

{{- with resources.Get "js/main.js" -}}
  {{- with . | js.Build (dict "minify" true) | fingerprint -}}
  <script
    src="{{ .RelPermalink }}"
    integrity="{{- .Data.Integrity }}"
    crossorigin="anonymous"
  ></script>
  {{- end -}}
{{- end -}}

它会生成如下的 HTML:

<script
  src="/js/main.75f5d6116a0351406e2e1c9216828af8ddc072281511a4aed31e7908e96b9d20.js"
  integrity="sha256-dfXWEWoDUUBuLhySFoKK&#43;N3AcigVEaSu0x55COlrnSA="
  crossorigin="anonymous"
></script>

注意看这个 JS 资源的命名,主要由其基础名称和哈希值构成。在模板中我们使用 fingerprint 函数生成资源的哈希值,一旦资源内容变化生成的值就是不同的。

这在前端领域其实是一个很常见的优化技巧。这样做只是第一步,第二步是将这个资源的缓存设置得足够久:

Cache-Control: max-age=31536000

它可以达成的目的是让用户不会再重复下载第二次,几乎永远使用本地的缓存。因为资源的名称根据其内容生成,一旦我们更改了内容就是一个新的资源。所以这样的缓存策略永远是安全的,一般被称作“高效缓存”。

31536000 实际上对应一年,因为我们无法真正意义上的设置为“永久”。

类似的,我们还可以单独对 CSS、字体等资源配置高效缓存。尤其是主题开发者,没有人比你更清楚资源该怎样设置缓存,用户不总是能做到最优化的配置。

更多页面

不仅需要优化缓存策略,一些特殊的页面如 404 等也需要用户手动配置。

HTML 缓存修正

如果你启用了 uglyURLs 设置,我们的页面后缀将是 .html。浏览器会一定程度上缓存我们的页面本身,更有甚者一些 App 的内嵌浏览器会非常愚蠢的几乎永久缓存 HTML 后缀的网页(参考这里)。

这会造成明显的负面影响,我们也需要给 .html 设置正确的缓存。虽然 Hugo 生成的是静态网站,但实际上我们却是经常改动文章(甚至主题)内容的。

构建镜像

我们将使用一个 Dockerfile 和一份 Nginx 配置构建博客。它几乎不需要用户配置任何东西,只是简单的反向代理即可达成最优效果!

Nginx 配置

创建 .nginx/default.conf 文件:

server {
  listen 80;
  server_name blog.hentioe.dev;

  root /public;
  index index.html;
  error_page 404 /404.html;
  access_log /var/log/nginx/blog.log;

  location ~* \.html$ {
    add_header Cache-Control "no-cache";
  }

  # Main CSS file
  location ~ ^/main.*\.css$ {
    add_header Cache-Control "public, max-age=31536000";
  }

  # Main JS file
  location ~ ^/js/main.*\.js$ {
    add_header Cache-Control "public, max-age=31536000";
  }

  # Remix Icon font
  location ~ ^/remixicon\.woff2$ {
    add_header Cache-Control "public, max-age=31536000";
  }
}

此配置以我的博客为例子,对 JS/CSS 以及 Remix 图标字体配置了高效缓存。对 .html 配置了 no-cache 策略。添加了 404 页面的设置。

Dockerfile

# 构建 Hugo 网站
FROM hugomods/hugo:reg-exts as builder
ARG TZ=Asia/Shanghai
ENV TZ=$TZ
WORKDIR /src
COPY . /src
RUN set -xe \
    && apk add tzdata \
    && npm install --location=global pnpm@9.5 \
    && pnpm --prefix themes/my-theme install \
    && pnpm install \
    && hugo

# 封装到 Nginx 镜像
FROM nginx:stable-alpine
ARG DOMAIN=blog.hentioe.dev
COPY .nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /src/public /public
RUN set -xe \
    && sed -i "s/blog.hentioe.dev/${DOMAIN}/g" /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

这里使用多阶段构建,先通过 hugomods/hugo 镜像构建出网站,再用 nginx:stable-alpine 镜像封装进去。注意我们还复制了配置文件,然后用变量替换了配置中的域名。

我们这样构建它:

docker build -t local/blog --build-arg="DOMAIN=your.domain.com" .

your.domain.com 替换为你自己的域名即可。

由于我的模板中的 CSS 部分由于使用了 NPM 以及其它技术,和一般的 Hugo 网站构建方式可能不同。请自行修改。

运行

直接启动镜像即可,你唯一需要配置的可能就是端口映射了(容器内部监听的是 80 端口)。此镜像没有考虑 TLS 证书,因为我建议你从外部再添加一个普通的反向代理,一切配置和优化不用关心,自然而然就存在!

你不必在意多了一个 Nginx 带来的开销。毕竟 Nginx 以极低的占用和高效闻名于世,它的开销实际上是微乎其微忽略不计的(大概 4-5MB)。

结束语

这就是 Hugo 博客容器化运行的好处。如果你的模板面向很多普通用户,你可以以这种方式分发配置。不过我仍然要再次强调,这不是必须做的。直接将网站放在外部,并手动配置外部 Nginx 一切都一样。

还有以上我提供的配置只是简化的版本,实际上我的博客引用的资源很复杂。越是像我这样的,就越需要一个可重现的配置,而不是让每一个人都手动配置一遍。

作者头像 一点点入门知识 打赏作者
本文由作者按照 CC BY 4.0 进行授权
分享:

相关文章