👋

Hugoで技術ドキュメント環境を作る

2023/01/04に公開約8,100字

はじめに

本稿の対象

Hugoによるシステムの構築およびかんたんな運用まわりについて述べる。

要件の整理

データサイエンス系に関するアウトプットはローカルのサーバにて保存したい。
そのための環境を構築する。

基本的な要件は以下の通りとした。

  • 構築やリカバリが容易である。
  • 動作が軽量である。
    • CMSやWordPressなどのようなゴテゴテしたものは不要。
  • markdownで書ける。
  • (できれば)画面の印刷がきれいにできる環境が用意されている。
  • カスタマイズ性(UIの変更)は二の次。
  • 外部ネットワークからのアクセスは発生しない。

結論

最終的には以下の構成にすることとした。

Hugoを選択した理由はGo言語の強みであるワンバイナリで稼働するところ。
これは家庭内のサーバの移設やクラッシュからのリカバリも容易に行えるというところに強みがある。

Hugoの外装に相当する部分をthemeというが、上記要件を鑑みて、Relearnを採用した。
特にnode.jsが必要となるパッケージのリカバリの知識もなく、自信がもてないことから、そのようなthemeの採用は今回は見送ることとした。

OSをUbuntuにした理由も個人的によく使っているから以外の理由はない。
rpmもdebも配布されているため、好きな方を使えばよいと思う。

構築

Ubuntu Server 22.04 LTS

タイムゾーンの調整

タイムゾーンの設定はファイルの更新に影響を与えることから、一応調整する。
timedatectl で現在の Time zone を確認する。

terminal
timedatectl

以下のように、Time zoneがAsia/Tokyo (JST, +0900)として表示されるかを確認する。

               Local time: Sat 2022-12-31 15:36:29 JST
           Universal time: Sat 2022-12-31 06:36:29 UTC
                 RTC time: Sat 2022-12-31 06:36:30    
                Time zone: Asia/Tokyo (JST, +0900)    
System clock synchronized: yes                        
              NTP service: active                     
          RTC in local TZ: no 

もし、タイムゾーンが日本時間(JST)になっていない場合は、下記コマンドで日本時間に変更する。

terminal
sudo timedatectl set-timezone Asia/Tokyo

ディレクトリの設定

Hugoのコンテンツは/opt/contents/hugo_contentsに配置する。
なお、作業するユーザはuser01という一般ユーザが行うものとする。

コンテンツを格納するディレクトリの作成および権限系の付与を行う。

terminal
mkdir /opt/contents
sudo chmod 0777 /opt/contents
sudo chown user01:user01 /opt/contents

Hugoのインストール

下記リンク先より、Hugoの最新バイナリを取得することができる。

https://github.com/gohugoio/hugo/releases

ここではSCSSのコンパイルも可能なextendedバージョンをダウンロードする。

Ubuntu等であれば.deb、CentOS等であれば.rpmが配布されているので、それを用いてインストールするのが楽である。
それにならい、ここでは.debをダウンロードして、aptを実行する。

terminal
export HUGO_VERSION=0.109.0

cd /tmp
curl -LO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
sudo apt install ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb

Hugoがインストールされたことを確認する。

terminal
hugo version

Hugo環境の構築

プロジェクトの作成

以下の通り、サイトのテンプレートを作成する。

terminal
cd /opt/contents
hugo new site hugo_contents

gitによる管理もここでスタートさせる。

terminal
cd hugo_contents
git init

テーマの設定

relearnテーマの取得を行う。

terminal
cd /opt/contents/hugo_contents/themes
git clone https://github.com/McShelby/hugo-theme-relearn.git hugo-theme-relearn
rm -rf .git

config.tomlを編集する。

terminal
vi /opt/contents/hugo_contents/config.toml

現状は以下のものを採用しているが、逐次編集を行っていく

なお、編集の方針としては、以下サイトを参考にするとよい。

title = "いろんなもの置き場"
languageCode = "en"
defaultContentLanguage = "en"
theme = "hugo-theme-relearn"

baseURL = "https://example.com/"
staticDir = ["static"]

canonifyURLs = true
relativeURLs = true
uglyURLs = false
disableHugoGeneratorInject = true

[outputs]
  home = ["HTML", "RSS", "PRINT", "SEARCH", "SEARCHPAGE"]
  section = ["HTML", "RSS", "PRINT"]
  page = ["HTML", "RSS", "PRINT"]

[markup]
  [markup.highlight]
    guessSyntax = true
    style = "xcode-dark"

  [markup.goldmark.renderer]
    unsafe = true

[server]
  [[server.headers]]
    for = "**.html"
    [server.headers.values]
       X-UA-Compatible = "IE=edge"

[params]
  # hugoとテーマの生成者バージョンのmetaタグの生成を無効にしたい場合は
  # trueに設定する。
  # HugoのdisableHugoGeneratorInject=trueも忘れずに設定すること。
  disableGeneratorVersion = false

  # サブメニューを展開する(true)か、折りたたむ(false)かを制御する。
  # 設定を与えない場合、最初のメニューレベルはfalseに設定され、
  # 他のレベルはtrueに設定されます。
  # これはページのフロントマターでオーバーライドすることができます。
  alwaysopen = true

  # 現在のページを編集するためのプレフィックスURLを指定します。
  # これにより, 各ページの右上に "Edit"ボタンが表示されるようになります.
  # あなたのドキュメントに対してマージリクエストを作成する機会を人々に与えるのに便利です.
  # 例として, このドキュメントサイトにあるconfig.tomlファイルをご覧下さい.
  editURL = ""

  # サイトの作者, メタ情報に使用される
  author = ""
  # サイトの説明, メタ情報の中で利用されます
  description = ""

  # 訪問したページのチェックマークをメニューに表示する
  showVisitedLinks = false

  # 検索機能を無効にする。検索バーが非表示になる
  disableSearch = false

  # すべてのページが隠されていてもタグの用語は表示されるが、隠されたページがタグページに表示されることを無効にする
  disableTagHiddenPages = false

  # JavascriptとCSSのキャッシュは、新しいバージョンのサイトが生成されたときに自動的に破棄されます。
  # この動作を無効にするには、これをtrueに設定します(一部のプロキシは、この最適化をうまく処理できません)
  disableAssetsBusting = false

  # インラインコード用の copy-to-clipboard ボタンの無効化
  # 無効にするには、これを true に設定する。
  disableInlineCopyToClipBoard = true
  # メニューのショートカットのタイトルが設定の無効化
  # 無効にするには、これを true に設定する。
  disableShortcutsTitle = false

  #### MathJax ####
  # MathJaxの読み込み
  # falseに設定すると、MathJaxショートコードが存在するかどうかに関わらず、
  # すべてのページでMathJaxモジュールがロードされます。
  disableMathJax = false
  # MathJax.jsのリモートロケーションの指定
  customMathJaxURL = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
  # MathJaxの初期化パラメータ
  mathJaxInitialize = "{}"

  #### Mermaid ####
  # Mermaidの読み込み
  # falseに設定すると、MermaidショートコードやMermaidコードフェンスが存在するか
  # どうかに関わらず、すべてのページでMermaidモジュールをロードします。
  disableMermaid = false
  # Mermaid.jsのリモートロケーションの指定
  customMermaidURL = "https://unpkg.com/mermaid/dist/mermaid.min.js"
  # Mermaidの初期化パラメータ
  mermaidInitialize = "{ \"theme\": \"default\" }"

  #### Swagger ####
  # Swaggerの読み込み
  # falseに設定すると、Swaggerショートコードがあるなしに関わらず、
  # すべてのページでSwaggerモジュールをロードします。
  disableSwagger = false
  # RapiDoc.jsのリモートロケーションの指定
  customSwaggerURL = "https://unpkg.com/rapidoc/dist/rapidoc-min.js"
  # Swaggerの初期化パラメータ
  swaggerInitialize = "{ \"theme\": \"light\" }"

  # メニューの検索バーの下にホームボタンの表示
  # falseに設定すると、ホームボタンが表示されます。
  # 指定された場合, 現在の言語のランディングページにリダイレクトされます.
  # (デフォルトは"/"です)
  disableLandingPageButton = true

  # 多言語のウェブサイトを利用している場合, 
  # trueにすると言語切り替えボタンを無効化する。
  disableLanguageSwitchingButton = true

  # ページのヘッダーにある目次メニュー
  # trueに設定すると非表示になる。
  disableToc = false

  # ヘッダーのパンくずを隠し、現在のページタイトルのみを表示する
  disableBreadcrumb = false

  # メニューのセクションを "weight"または "title"で並び替えます。
  # デフォルトは "weight"
  # これはページのフロントマターで上書きすることができる
  ordersectionsby = "weight"

  # デフォルトの配色を別の配色に変更します.
  # 例えば, "red", "blue", "green"または["blue", "green"]のような配列にすることができます.
  themeVariant = [ "blue", "relearn-light", "relearn-dark", "learn", "neon", "green", "red" ]

  # タイトルのセパレータを変更します。デフォルトは"::"です.
  titleSeparator = "::"

  # trueに設定すると, サイドバーのメニューが折りたたみ可能なツリービューで表示されます. 
  # 古いブラウザ(IE11)でも機能は動作しますが、
  # エキスパンダーアイコンの表示はモダンブラウザに限定されます
  collapsibleMenu = true

  # 通常はコンテンツの横に縦長に表示される次ページと前ページのボタンを隠す
  disableNextPrev = false

  # 1つのページが複数の言語でコンテンツを含むことができる場合、それらをここに追加します。
  additionalContentLanguage = [ "en" ]

  disableSeoHiddenPages = false

Hugo環境の起動

手動実行

Hugoを実行する。

ここでlocalhost以外からのアクセスを受け付ける--bind 0.0.0.0
起動時に再ビルドを行う--disableFastRenderをオプションに付与して起動させる。

terminal
hugo server --bind 0.0.0.0 --disableFastRender

あとはコンテンツを作るだけ。

サービス化

基本的にはサービスによる稼働をさせたい。

terminal
sudo vi /etc/systemd/system/hugo.service

パラメータの意味は以下の通り。

  • Restart=alwaysを指定することで異常・正常にかかわらず再起動を試みます。
  • StartLimitBurstは再起動を実施する回数の上限、StartLimitIntervalSecは再起動のインターバルの秒数です。
[Unit]
Description=Hugo Contents

[Service]
Type=simple
WorkingDirectory=/opt/contents/hugo_contents
ExecStart=/usr/local/bin/hugo server --bind 0.0.0.0 --disableFastRender
Restart=always
StartLimitBurst=5
StartLimitIntervalSec=10
User=user01
Group=user01

[Install]
WantedBy=multi-user.target

設定ファイル再読み込みをおこない、起動できるか確認する。

terminal
sudo systemctl daemon-reload
sudo systemctl start hugo.service
sudo systemctl status hugo.service

OKであれば、有効化を行う。

terminal
sudo systemctl enable hugo.service

運用関連

バックアップとリストア

基本的にリストアは以下をどこかに退避しておけば、容易に行うことができる。

  • hugoのdebパッケージ(今回だと"hugo_extended_0.109.0_linux-amd64.deb")
  • コンテンツデータ

コンテンツデータとは、/opt/contents/hugo_contents配下のデータのことを指す。
/opt/contentsに移動して、hugo_contents配下のデータを日時情報を付与したtarファイルにまとめるサンプルを以下に示す。

#!/bin/bash
BACKUP_TIME=`date "+%Y%m%d_%H%M%S"`

cd /opt/contents
tar zcvf hugo_contents_${BACKUP_TIME}.tar.gz hugo_contents/

あとはこれをどこかに転送する仕組みを追加し、crontabなどで定期的に実行すればよい。

GitHubで編集を提案

Discussion

ログインするとコメントできます