基於容器部署hugo博客 – hugo個人博客折騰記之後篇

2022-02-03 路由器的那些事兒

這篇主要講一下這個博客是怎麼部署的

微信用戶: 點擊查看原文格式會更好一些。

hugo 主題選取

主題選取這個其實在前篇已經說過了

這裡說下這個主題的特點吧:

5色可選。5種雙色主題 (orange is default, red, blue, green, pink)可以直接在配置文件裡修改。

Fira Code作為默認等寬字體

響應式設計,各種解析度瀏覽效果都不錯

採用 PrismJS 實現客戶端代碼高亮

最重要的是,它簡潔。
沒錯,真的很簡潔,沒有過多的功能,甚至沒有archive頁面,沒有tag cloud,只自帶了3個shortcode。
對於hugo新手來說,越是簡單的主題,你才越好下手修改。要不然剛接觸hugo,就用了一個功能特別複雜的主題,
這對於掌握主題的DIY技能是沒有幫助的。
現在你看到我的博客,我已經是根據自己的需求做了非常多的調整了。

我調整後的terminal主題:

同樣5色可選。

使用Jetbrains Monohttps://github.com/JetBrains/JetBrainsMono等寬字體進行代碼高亮

響應式設計保持不變

採用hugo自帶的代碼高亮方案(輸出class) + 自定義的Gruvbox高亮配色

調整hugo默認的RSS 2.0 feed輸出為atom輸出,調整RSS摘要輸出為全文輸出

增加archive,tagcloud模板,增加基本的KaTex自動渲染LaTex支持。

增加TOC顯示,返回TOP按鈕,平滑滾動。

cover圖片顯示增加page bundle支持

如果你碰巧也喜歡這個主題,老燈的代碼是開放的,自取:https://github.com/ttys3/hugo-theme-terminal/tree/ttys3

代碼高亮

這裡要說一下,在hugo裡面,代碼高亮有兩種選擇,一種是客戶端實現方式,另一種是後端實現方式。
客戶端實現就是用markdown的fenced code使代碼在輸出時保持原樣(當然,跟html本身衝突的<或>會進行處理),
然後由客戶端加載js進行高亮渲染。後端的方式,即是採用hugo默認的markdown解析引擎goldmark來調用
chroma進行高亮渲染.

chroma跟python裡的Pygments 採用一致的方式進行高亮,甚至連風格樣式也保持一致。
所有Chroma的配色都是使用_tools/style.py腳本自Pygments的配色轉換而來。
所以一般來說,這兩個的渲染結果應該是一致的,Pygments有什麼配色,chroma就有什麼配色,自然,
hugo就會有什麼配色。配色效果可以去Chroma配色相冊查看

雖然Chroma是porting自Pygments, 但是也不是完全一樣,比如作者解釋了為什麼Chroma比Pygments支持的高亮語言更少一些:

I mostly only converted languages I had heard of, to reduce the porting cost.

不過這完全不是問題,因為常用的語言它都支持了,而那些名字都沒聽過的語言,我也不需要高亮。

那麼,既然hugo自帶高亮, 主題也自帶高亮,我是要用主題的前端方案呢?還是用後端的hugo方案呢?

前後端高亮方案對比對比項hugo後端高亮PrismJS前端高亮RSS feed輸出可保持高亮配色(當noClasses=true)無高亮瀏覽器兼容性不相關相關inline CSS支持支持不支持官方配色數量較多很少(8)第三方配色數量較多較多

看上去兩者相比不分高下。所以,主要看需求了。
如果要求RSS feed輸出也保持一致的代碼高亮,則要使用hugo自帶的高亮且啟用inline CSS輸出。

如果要求支持所有瀏覽器,則也應該選擇hugo自帶的方案。
see PrimsJS Limitations

No IE 6-10 support. If someone can read code, they are probably in the 85% of the population with a modern browser.

如果是使用hugo自帶的高亮且使用的是class的方式的話,我覺得應該用這兩個都OK。
就看你喜歡的那個配色在對應的方案裡有沒有提供。

package main

import "fmt"

// This is a comment
func main() {
    fmt.Println("hello world")
}

輸出對比:
我把二者的輸出用在線工具格式化一下方便對比 https://codebeautify.org/htmlviewer/

PrismJS

<pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4" class=" language-go">
    <code class=" language-go" data-lang="go">
        <span class="token keyword">package</span> main

<span class="token keyword">import</span>
        <span class="token string">"fmt"</span>
        <span class="token keyword">func</span>
        <span class="token function">main</span>
        <span class="token punctuation">(</span>
        <span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
    fmt
        <span class="token punctuation">.</span>
        <span class="token function">Println</span>
        <span class="token punctuation">(</span>
        <span class="token string">"hello world"</span>
        <span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
    </code>
</pre>

hugo

<pre class="chroma">
    <code class="language-go" data-lang="go">
        <span class="kn">package</span>
        <span class="nx">main</span>
        <span class="kn">import</span>
        <span class="s">"fmt"</span>
        <span class="kd">func</span>
        <span class="nf">main</span>
        <span class="p">()</span>
        <span class="p">{</span>
        <span class="nx">fmt</span>
        <span class="p">.</span>
        <span class="nf">Println</span>
        <span class="p">(</span>
        <span class="s">"hello world"</span>
        <span class="p">)</span>
        <span class="p">}</span>
    </code>
</pre>

二者的解析引擎都是基於正則表達式的, 有錯漏肯定是難免的。
看上去解析結果差不太多,不過貌似PrismJS 沒有把 fmt.Println 調用裡的 fmt 解析正確。
chroma的輸出結果使用了更簡短的class名稱,看上去更精簡一些。

最終我還是選定了hugo內置的高亮。放棄了前端方案。

內置的配色我感覺沒有喜歡的,於是看看base64有沒有chroma的配色,還真找到一個。

對於這個porting不太滿意,因為它太多紫色的東西。
jetbrains 有一個 gruvbox theme自帶的gruvbox color各方面都不錯,我把它port過來了
https://github.com/ttys3/base16-chroma/blob/master/chroma-base16-css/cssvar/base16-gruvbox-dark-jb.css

最終形成我自己的fork:
https://github.com/ttys3/base16-chroma

由於 base16 配色並不是專門為Chroma或Pygments設計的,因此,得把base16的 scheme 人肉轉換到 Chroma 過來。

最終效果就是現在你看到的高亮效果。

這期間花了我不少時間,關於如何製作新的 base16 配色並應用於 hugo, 後續有空我再單獨發文分享。

另外這個主題默認的Fira Code我給它換成Jetbrains Monohttps://github.com/JetBrains/JetBrainsMono woff2字體,woff2體積幾乎比woff小一半。
如果你查看兼容資料庫https://caniuse.com/#woff2, 你會發現只有IE11 和 Opera Mini不支持。
所以,可以果斷地用woff2了。

內容組織

由於 hugo 不像 WP 這類有資料庫的動態博客系統, hugo是生成靜態頁面的。

用目錄來組織文章, markdown文件一般不會遇到問題,但是圖片和其它附件呢?

如何有效的管理?方便寫文章和修改,同時方便後期的查找和管理?

幸好,hugo 有一種叫 page bundle的模式。

就是一個頁面下可以包含其它資源。

如果是一個葉節點頁面bundle, 那麼它下面可以放一個index.md 及 圖片和其它附件。

比如我現在這個文章deploy-hugo-blog-witn-container 就是一個page bundle,文件結構如下:

content/post/hugo/deploy-hugo-blog-witn-container
├── cover-2020-04-16-03-50-14.png
└── index.md

這樣的好處非常明顯, 文章自己的圖片放在文章自己的目錄。

如果是公用的圖片怎麼辦?放到 static/img 下面啊。

還有一個問題,在寫文章的時候,怎麼方便的在截圖完成後,把圖片存到當前目錄並自動引入markdown中?

有一個神奇的 VS Code 插件叫Markdown Paste(telesoho.vscode-markdown-paste-image), 這個非常方便的功能就由它實現了。

用起來非常絲滑順手。

剪切板中的圖片,Ctrl+Alt+V 就能直接到markdown文件所在目錄,並且自由粘貼markdown的圖片代碼。

這裡主要是要感謝王不對,
如果沒有看他的文章,我還一直不會去了解hugo的page bundle這個功能。

當然,這是圖片存在本伺服器的情況,如果是用又拍雲、七牛或者騰訊雲COS做圖床的話,VS Code裡另外有其它的插件可以現實。

因為我是全站走CDN了,因此就懶得再弄CDN的圖床了。

配置

配置的話,採用最小化配置。
也就是,凡是hugo默認的配置,我們不寫出來,我們只增加自己要修改的部分。

除了個單個config.toml配置,hugo還引入配置目錄的功能。

每個頂層配置對象都擁有一個文件,如下圖示:

├── config
│   ├── _default
│   │   ├── config.toml
│   │   ├── languages.toml
│   │   ├── menus.en.toml
│   │   ├── menus.zh.toml
│   │   └── params.toml
│   ├── production
│   │   ├── config.toml
│   │   └── params.toml
│   └── development
│       ├── config.toml
│       └── params.toml

考慮到上述的結構,運行hugo --environment development時,hugo將讀取config/_default的默認配置
然後和development合併。
也就是_default是默認配置,production 或 development 可以覆蓋,也可以增加配置

直接運行hugo serve時的默認環境是development
直接運行hugo時的默認環境是production

這樣非常方便本地開發和部署到線上時切換不同的配置。

但是看了一下,官方的文檔對於這個功能的描述非常的不清晰。
看了這個功能相關的issue 大概了解了一些用法。
我想還是暫是不使用這個功能。

評論系統

由於hugo是靜態博客生成器,因此,我們要引入評論功能,只能通過API來嵌入評論。
hugo本身已經內置了Disqus,只需要配置一個id就能啟用。

雖然國外的 Disqus評論系統其實還算好用,但是國內很無奈,主域名基本無法直接訪問,然而它的js裡也嵌入了api請求域名,
算起來差不多有三四個域名,所以,用反向代理我也嫌棄麻煩。放棄之。

國內的有changyan,也提供免費使用,但是肯定是不考慮的。原因不用多說。

基於gihtub的評論系統也有一些,比如gitment, gitalk等,但是我還是不想用這種,因為最近連github也是不能直接訪問了。

還有個Valine是基於LeanCloud的服務的,這個我也不想用,用的什麼對象存儲,然後嚴重依賴LeanCloud這個國內的服務。
說是「無後端評論系統」,其實是把後端放在了LeanCloud。另外,這個新版本也要移除郵件提醒功能了,
需要郵件提醒還得再整一個第三方郵件提醒。

isso是基於py的,貌似很久沒人維護了,這個也不滿足條件。

go-isso實現還沒完成,暫時也不是可用狀態,繼續找。

找到一個叫remark42的評論系統,這個評論系統啥都好,就是:一,沒有中文。二,不記錄郵箱和網址。三,發郵件提醒只能用SMTP。
關於第一點,我已經提交了一個PR給官方,官方已經把中文語言merge進去了。關於第二點,基於隱私,不記錄郵箱,
這個我可以理解,但是不記錄網址,博客與博客之間就達不到那種互動互踩的效果了。其它博主在你的博客留言了,你卻不知道他的博客網址。
不記錄評論者網址這一點,我有時間再想辦法改進一下。
第三點,我提交了一個使用sendgrid和mailgun的WEB API發郵件的PR給官方,然後官方拒絕了。
理由是「這會使代碼膨脹」, 這是什麼理由?你集成了oauth登錄功能,加了常見的github, google, twiiter,突然有一天你覺得yandex
也要加上了,然後你就把yandex也加上了,你的代碼就不「膨脹」了?

為什麼SMTP已經是「能用」狀態了,帶要用web api發送郵件?很簡單,我都是用自己的VPS架設的博客,基於安全考慮,不希望SMTP協議暴露源站
IP。用WEB API可以很輕鬆地解決這個問題。

然後remark42官方提議,我另外弄一個side container跑一個SMTP到WEB API發送郵件的bridge服務。
然後,發郵件的流程就變成了:remark42 => SMTP-WEB_API-bridge => SendGrid/Mailgun WEB API

問題是,明明可以通過代碼解決了,再加個side container,至於麼?

這是把開發者的責任,轉架到了使用者身上。

並且,他拒絕我這個PR,然而他自己的oauth實現也是這樣實現的。如果按照他這個邏輯,這個oauth登錄功能也會使「代碼膨脹」,

因為提供oauth登錄服務的廠家也是在變動的。你們俄羅斯(remark42的作者是俄羅斯的)的用yandex, 到我們這了可能就要加別的廠家的登錄接口功能了。

另外,我看了下他那個Dockerfile, 寫得層數實在太多。我數了一下他最終生成的鏡像層數,不多不少,正好17層。

一般我做鏡像,基本上都會控制在10層以下,一般在5層左右。

因為docker用的是overlayfs, 一層一層覆蓋的,層數越多,io效率越低。

那你可能會多,我層數多,加一個配置文件都是一個新層,方便更新啊。如果你要這樣說的話,那我反問你,
做base image的人,是不是也要一個文件一個軟體地加,給你弄個100層?方便你更新?

但是個人維護這麼一個項目是極其要時間了,這也是為什麼,我還是比較傾向於讓官方接受我這個PR。

但是remark42的項目主負責人對於源站IP洩漏這種安全問題壓根沒有什麼意識,在我的PR裡他還反問我,大家的伺服器都是public IP了,
這個IP本身就是公開的,還存在什麼源站IP洩漏?我只能呵呵了。

我回了一句:你的伺服器沒有被DDos 攻擊,你當然不會意識到這是一個問題。

最後,歡迎大家關注我的remark42 fork: https://github.com/ttys3/remark42/

同時也歡迎有興趣的童鞋一起完善改進。

我現在使用的remark42是編譯自我的fork的,然後通過github actions自動提交到了dockerhub:

https://hub.docker.com/r/80x86/remark42

remark42部署效果圖:

LaTeX

hugo目前的goldmark媽蛋解析引擎還不支持latex。
雖然結合katex js,實現一些簡單的LaTeX解析是沒問題的,但是如果LaTeX裡有_和[] 這種媽蛋裡也有的標記,
就會導致LaTeX的標記走丟了(被媽蛋搶走了)。

容器部署

hugo產出的HTML直接用nginx容器跑http服務。
remark42容器提供評論API。
前端採用envoy做代理,hugo和remark42埠由envoy開放。

nginx容器部署

一個基本的適用於hugo站點的nginx配置(/etc/nginx/conf.d/default.conf)如下:

# nginx config for hugo site
# author: ttys3
server {
    listen       8080;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    index  index.html;
    # absolute_redirect must off for hugo site if no `$uri/index.html` on try_files
    # absolute_redirect off;
    # port_in_redirect off;
    # server_name_in_redirect off;

    root   /app/public;

    location / {
        try_files $uri $uri/index.html =404; #fixup for hugo
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

然後直接運行docker官方的nginx容器nginx:1.17.9,映射/etc/nginx/conf.d/default.conf配置文件,並映射/app/public目錄:

sudo docker run -d --name hugo \
    -v hugo-public目錄位置:/app/public:ro,z \
    -v default.conf文件位置:/etc/nginx/conf.d/default.conf:z \
    --mount type=tmpfs,destination=/tmp \
    nginx:1.17.9

remark42容器部署

怎麼申請github, google, twitter的oauth登錄接口,這裡有詳細的步驟:https://github.com/ttys3/remark42#register-oauth2-providers

sudo docker run -d --name remark42 \
-e REMARK_PORT=8081 \
-e REMARK_URL='https://remark42的訪問地址' \
-e PUID=1000 \
-e TIME_ZONE="Asia/Shanghai" \
-e SITE='網站域名(不需要帶http和埠)' \
-e ADMIN_SHARED_ID='管理員的用戶id' \
-e ADMIN_SHARED_EMAIL='管理員郵件地址@example.com' \
-e ADMIN_PASSWD='管理員密碼' \
-e SECRET='用於加密的密鑰,自己隨便md5一下弄個串出來放這就好了' \
-e AUTH_EMAIL_ENABLE=false \
-e AUTH_EMAIL_FROM='郵件認證或通知提醒時的發件人郵箱@sg.ttys3.net' \
-e AUTH_GITHUB_CID='github-api-key填寫這' \
-e AUTH_GITHUB_CSEC='github-api-key-secret填寫這' \
-e AUTH_GOOGLE_CID='谷歌api-key填寫這' \
-e AUTH_GOOGLE_CSEC='谷歌api-key-secret填寫這' \
-e AUTH_TWITTER_CID='twitter-api-key填寫這' \
-e AUTH_TWITTER_CSEC='twitter-api-key-secret填寫這' \
-e NOTIFY_TYPE=email \
-e AUTH_EMAIL_SUBJ='荒野無燈weblog登錄確認' \
-e NOTIFY_EMAIL_VERIFICATION_SUBJ='確認訂閱來自荒野無燈weblog的評論' \
-e NOTIFY_EMAIL_ADMIN=true \
-e EMAIL_PROVIDER=sendgrid \
-e EMAIL_SG_API_KEY='你的sendgrid密鑰填這' \
-e EMOJI=true \
-v /home/data/remark42:/srv/var:rw,z \
80x86/remark42:latest

AUTH_GITHUB_CID / AUTH_GITHUB_CID 這幾個不是必須的。
比如你只想啟用github登錄評論,則只需要配置AUTH_GITHUB_CID 和 AUTH_GITHUB_CSEC。
目前我實現了mailgun和sendgrid這兩個發送郵件的provider, 如果你用mailgun,請修改上面的
EMAIL_PROVIDER=sendgrid為 EMAIL_PROVIDER=mailgun

更多配置信息請去https://github.com/ttys3/remark42 查看。

envoy容器部署

Envoy 是什麼?

Envoy 是專為大型現代 SOA(面向服務架構)架構設計的 L7 代理和通信總線。

Envoy Proxy 在代理方面性能咋樣呢?老燈這裡放兩張2018年的圖(來源):

Envoy Proxy 主要是用來在微服務架構中做service mesh的, 但是這裡我們用它來做edge proxy對它來說當然更是小菜一碟。

關於做edge proxy的例子,官方的最佳實踐文檔中有:Configuring Envoy as an edge proxy

這裡老燈分享上我的配置envoy.yaml:

# https://www.envoyproxy.io/docs/envoy/latest/configuration/best_practices/edge
overload_manager:
  refresh_interval: 0.25s
  resource_monitors:
  - name: "envoy.resource_monitors.fixed_heap"
    typed_config:
      "@type": type.googleapis.com/envoy.config.resource_monitor.fixed_heap.v2alpha.FixedHeapConfig
      # TODO: Tune for your system.
      max_heap_size_bytes: 2147483648 # 2 GiB
  actions:
  - name: "envoy.overload_actions.shrink_heap"
    triggers:
    - name: "envoy.resource_monitors.fixed_heap"
      threshold:
        value: 0.95
  - name: "envoy.overload_actions.stop_accepting_requests"
    triggers:
    - name: "envoy.resource_monitors.fixed_heap"
      threshold:
        value: 0.98

admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    per_connection_buffer_limit_bytes: 32768 # 32 KiB
    filter_chains:
      - filters:
        - name: envoy.filters.network.http_connection_manager
          typed_config:
            "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
            stat_prefix: ingress_http
            use_remote_address: true
            common_http_protocol_options:
              idle_timeout: 3600s # 1 hour
            http2_protocol_options:
              max_concurrent_streams: 100
              initial_stream_window_size: 65536 # 64 KiB
              initial_connection_window_size: 1048576 # 1 MiB
            stream_idle_timeout: 60s # 1 mins, must be disabled for long-lived and streaming requests
            request_timeout: 60s # 1 mins, must be disabled for long-lived and streaming requests
            access_log:
              - name: envoy.file_access_log
                config:
                  path: "/dev/stderr"
            route_config:
              virtual_hosts:
                - name: comment
                  domains:
                  - "cmt.ttys3.net"
                  routes:
                    - match: { prefix: "/" }
                      route:
                        cluster: service_comment
                        idle_timeout: 15s # must be disabled for long-lived and streaming requests
                - name: hugo
                  domains:
                    - "ttys3.net"
                  routes:
                    - match: { prefix: "/" }
                      route:
                        cluster: service_hugo
                        idle_timeout: 15s # must be disabled for long-lived and streaming requests
                - name: cgit
                  domains:
                    - "git.ttys3.net"
                  routes:
                    - match: { prefix: "/" }
                      route:
                        cluster: service_cgit
                        idle_timeout: 15s # must be disabled for long-lived and streaming requests
            # hugo nginx only listen on http
            http_filters:
              - name: envoy.router
                config: {}
  clusters:
  - name: service_hugo
    connect_timeout: 3s
    per_connection_buffer_limit_bytes: 32768 # 32 KiB
    lb_policy: round_robin
    type: "STRICT_DNS"
    hosts:
    - socket_address:
        address: hugo
        port_value: 8080
  - name: service_comment
    connect_timeout: 3s
    per_connection_buffer_limit_bytes: 32768 # 32 KiB
    lb_policy: round_robin
    type: "STRICT_DNS"
    hosts:
      - socket_address:
          address: remark42
          port_value: 8081
  - name: service_cgit
    connect_timeout: 3s
    per_connection_buffer_limit_bytes: 32768 # 32 KiB
    lb_policy: round_robin
    type: "STRICT_DNS"
    hosts:
      - socket_address:
          address: cgit
          port_value: 8082

注意:

Envoy Proxy的配置文件是YAML格式,注意要用空格,不要用tab.
 docker的DNS服務會自動把容器名稱映射到容器的IP位址,因此clusters配置這裡的address: hugo要與前面創建的hugo容器名稱hugo對應。

這裡老燈開了3個服務,一個hugo, 一個comment, 一個cgit.
我在創建容器的時候,分別讓hugo監聽8080, remark42 comment監聽 8081, cgit 監聽8082埠。

然後為了簡單起見,我們只讓Envoy Proxy對外暴露出80埠,因此我這裡也沒有配置SSL。
為什麼呢?因為通過走cloudflare CDN之後,CF可以自動提供SSL證書。
如果要弄SSL,咱也可以直接弄個自籤名的SSL就行了,因為 CF 也認。
或者,講究一點,弄個ACME自動證書。

這裡開放的9901 埠是用於查詢一些Envoy Proxy的狀態的,注意這個埠是不需要認證就能訪問的,因此用容器跑的時候,
一般不要綁定到0.0.0.0讓外網能訪問到。
因為通過這個WEB接口是能關掉Envoy Proxy,甚至能修改配置的。

這個WEB UI可以說是非常的簡潔了,只有非常基礎的界面:

運行Envoy Proxy容器:

 sudo docker run -d --name envoy \
    -v envoy.yaml文件的絕對路徑:/etc/envoy/envoy.yaml:z,ro \
    -p 127.0.0.1:9901:9901 \
    -p 80:80 \
    envoyproxy/envoy:v1.14.1

這裡我們只開放了80埠,如果啟用了SSL, 需要加上-p 443:443 \

podman注意事項

由於老燈目前是在用podman,沒有用docker, 也就是以上命令裡的docker替換成podman就能跑了。

使用docker的注意以上運行容器的命令都要加上--restart=unless-stopped, 不然機器重啟後容器不會自動啟動。

使用podman的則要使用podman generate systemd 容器id或名稱 來生成systemd unit文件並啟用服務來實現開機自啟。

其它

Envoy Proxy容器可不可以用nginx做基於域名的虛擬主機來做反向代理替換掉?
完全可以。

hugo public目錄發布的問題

我暫時用了最簡單的方法,直接一個up.sh腳本完事

#!/bin/sh

env TZ='Asia/Shanghai' hugo --gc --minify --enableGitInfo && \
rsync -ravz --progress --checksum --delete public/ root@伺服器地址:/home/http/html/ttys3.net

這裡的/home/http/html/ttys3.net 就是hugo網站的根目錄了。

也可以採用在服務端架設gitolite,然後用git hook實現自動發布hugo站點,這個後續有時間再分享。

其它部署方式?

hugo支持很多部署方式,包括直接託管在github pages
或者用Github Actions實現自動化部署Hugo博客,
以及託管在netlify.
更多部署方式可以在hugo官網找到。

郵件發送API的選擇

Mailgun和SendGrid都提供免費的plan,默認情況下,Mailgun每月的發送量送得更多(限制1萬封)。
SendGrid在試用期過後,每天的郵件數量會限制在100封,感覺可用性不是很高。

Mailgun(https://www.mailgun.com/) Free Plan provides 10,000 Emails per month

SendGrid(https://sendgrid.com) Trial Plan provides 40,000 emails for 30 days.
After your trial ends, you can send 100 emails/day for free

優秀的hugo主題推薦

淺色系:

https://github.com/olOwOlo/hugo-theme-even

https://github.com/xianmin/hugo-theme-jane

https://github.com/zhengzangw/hugo-theme-ztyblog

暗黑系:

https://github.com/panr/hugo-theme-hello-friend

hello-friend的第三方fork版:https://github.com/rhazdon/hugo-theme-hello-friend-ng

更多主題請去https://themes.gohugo.io/查看。

hugo基礎教程?

Hugo 從入門到會用 https://blog.olowolo.com/post/hugo-quick-start/

博客遷移——Hugo使用整體概覽 https://www.rectcircle.cn/posts/blog-migration/

TODO

相關焦點

  • 利用 Github Pages 和 Hugo 快速搭建免費的個人網站
    點擊上方「程序猿技術大咖」,關注並選擇「設為星標」你有個人網站嗎?在自媒體時代,人人都在不斷向網際網路等不同媒介輸出內容,如:博客、微博以及抖音等,並在特定領域,依靠於廣大社交媒體/平臺,如:CSDN、掘金、知乎、InfoQ 等等,發表著自己的心得、學習經歷、日常生活等。
  • 設計開發自己的Hugo 主題
    引言隨著創作者們在個人Blog、知識管理方面需求的逐漸增長,優雅、合理的靜態博客能夠很好地支撐人們對於內容體系的管理和展示。當前在搭建和使用靜態Blog這方面的內容應該說非常多,比如Hugo,Hexo等,每個也都有著自己的主題展示網站。
  • 手把手教你從零搭建和部署自己的個人博客
    但是過了一段時間之後,當我們再次碰到類似的問題時,早已忘記以前是怎麼解決的了,於是又要到網上去搜,浪費大量的時間和精力。面對這些重複的問題,我們為什麼不能把它們記錄下來呢?在我看來,搭建自己的個人博客主要有以下好處:      •  有助於養成歸納總結的習慣,幫助記憶,把別人的知識變成自己的知識。
  • 【讀者投稿】用Github+docsify,我花了半天就搭好了個人博客
    搭建博客只是一個小任務,為啥遲遲不能完成?只能說明鄙人執行力太差。想的多做的少,大多數時候我們只要開始行動之後,好多問題都會迎刃而解了。引用最新網上很流行的一段話,與君共勉之:我們遇到什麼困難也不要怕,微笑著面對它!消除恐懼的最好辦法就是面對恐懼!堅持,才是勝利。加油!奧利給!因此,幹就完了。
  • 不會代碼,如何零成本搭建個人博客?
    公眾號首創的原創保護和讚賞機制,讓它成了許多內容生產者喜愛的一個平臺。 但如果你經常使用公眾號,可能會發現它也存在一些限制:不能跳轉到外部連結、新開的公眾號沒有留言功能等。 而這些限制,對於博客來說,是不存在的。
  • 如何搭建一個比簡書更精美的個人博客
    想必大家都有過在一些大平臺上發布博客的經歷。無奈,不是排版太醜,就是不適合發布代碼,有的甚至是充斥著各種心煩的廣告,像牛皮癬一樣,極大地降低了技術人員在分享過程中獲得的成就感,唉,人在屋簷下不得不低頭。
  • 英文博客模板之一篇搞定「How-to」博客
    博客提綱Outline包含:標題:既然是Howto模板當然是用Howto開頭。60個字符以內最好。關鍵字:這裡放你的目標關鍵字。關鍵字哪裡來?單是這個問題都可以再開一個系列單寫。英文博客標題Introduction為文章做100-200字的引言– 為何某物重要,誰/哪個行業/哪個領域需要看和文章涵蓋什麼。
  • 那些看上去高大上的項目文檔和個人博客原來用了這些技術
    胖哥寫了幾百篇公眾號都是用Markdown寫的。無論是新手還是老手,MarkDown都是要掌握的。另外這裡推薦一款很好用的MarkDown編輯器 Typora ,真的很好用,配合圖床,誰用誰知道。3.如果你想要開始使用它,只需要創建一個 index.html 就可以開始編寫文檔並直接部署在 GitHub Pages 或者Gitee Pages。它提供了中文文檔:https://docsify.js.org/#/zh-cn/如果你會MarkDown,半個小時你就能搭建一個屬於你自己的文檔網站,它相當簡單好用。
  • 手把手教你使用Hexo + Github Pages搭建個人獨立博客
    每個帳號只能有一個倉庫來存放個人主頁,而且倉庫的名字必須是username/username.github.io,這是特殊的命名約定。你可以通過http://username.github.io 來訪問你的個人主頁。這裡特別提醒一下,需要注意的個人主頁的網站內容是在master分支下的。
  • CloudNotes之桌面客戶端篇:將筆記發布到博客園
    事實上,該插件不僅僅支持博客園,它可以支持所有基於MetaWeblog API的博客系統。發布博客不多說,先上圖。在該版本的桌面客戶端中,打開設置對話框,進入「擴展功能」標籤,您可以看到在工具型擴展功能列表中,新增了兩個擴展功能(也就是常說的插件):發布到博客和插入語法高亮代碼:後面我會介紹插入語法高亮代碼插件,先看看這個博客發布插件。
  • 我花了半年,重構了蘑菇博客!
    並且,既然搭博客網站的初衷是為了學習,而學習的方法是一個技術一個技術去嘗試、去復現、去思考。如果直接用現成的技術很快的搭建起了自己的網站,雖然節省了很多時間,但是這其中少了很多學習技術的機會。比如,網頁怎麼布局的,前後端怎麼交互的,瀏覽器怎麼加載的,代碼怎麼打包的,網站怎麼部署的,等等等等。tip:其實選擇怎樣的學習方式,全都取決於自己。只要堅持下來就一定會有收穫。
  • 維權親歷:我的新浪博客慘遭封殺之後……
    維權親歷:我的新浪博客慘遭封殺之後…… 周一被封殺,周六被解封,一周時間裡,圍繞著我的新浪博客被封殺事件
  • 不敢想,做個博客竟如此簡單!
    大家好,我是魚皮,最近買了個不錯的域名,為了不浪費它呢,我心血來潮打算做個博客網站。在動手做之前,我先梳理了一下目前常用的搭建個人博客的方法,並簡單分析了它們的優缺點,分享出來,幫助大家根據自己的需求去選擇合適的方法,實現自己的個人博客網站。
  • 我很懷念那時候的博客,雖然那時候的博客沒有一分錢的收益
    它的中文名字叫做「我的家」,是一個博客。我的第一個博客也是在這個網站,那會我家裡還沒有寬帶,要發布一篇文章有多麼不容易。加之也常有我的文章被推薦到首頁,讓我很有成就感滿足感,所以,我對它傾注了我的很深的很多的情感。有一年好像是評選年度受歡迎10篇文章還是10位作者,反正,我有幸當選,獎品就是這本有「x5dj」標記的筆記本。
  • 古天樂終於停止更新博客,博客時代,或從此結束
    事實上,這條博客文字是7月9日就已經發布了,直到16日才被媒體報導,由此可見,新浪博客真的已經被人遺忘太久了。古天樂從2008年3月開始進駐博客,到現在,12年了,幾乎每天都在更新。,更重要的是,古天樂的博客文字都是自己寫的,從來沒有轉載。
  • 技術人員為什麼要寫博客?
    以下是正文:來源:Zery zhang 的博客www.cnblogs.com/zery/p/3343893.html如有好文章投稿,請點擊 → 這裡了解詳情本文只代表個人見解,不代表任立場,如果您認為我的想法是錯的那很正常,因為這是我的想法,如果您覺得您的想法和我一樣,那我們就是傳說中的 「激友」(對生活衝滿激情的朋友)。
  • 【簡友佳微】我的博客心得
    特別是相熟幾個文友的博客,更是每日必到,碰到主人,一定會大灌其水,主人不在,也要留下隻言片語,斷沒有「尋隱者不遇」的風度。特別是加入博客圈,跟博友們交流、回帖打成一片,迎來送往,倒也心情愉快,自得其樂。博客越寫越有勁,我的地盤,我做主!但是,逛文友的博客時間長了,就發現問題:人家的博客打理、裝修得圖文並茂,可自己的博客只堆砌乾巴的文字,人靠衣裳馬靠鞍,美文(姑且這樣說吧)還要美圖配。
  • 使用GitHub+Hexo快速搭建自己的技術博客
    做自己的技術博客要不要花錢呢?很多人一想到做網站就會想到說要買域名買伺服器等等, 一年又得不少錢吶, 其實不用, 做一個技術博客可以完全免費的, 我們可以借用Github給我們提供的免費倉庫來存放我們的博客項目, 也可以使用Github給我們提供的靜態頁面地址來訪問到我們的博客, 所以都是免費的, 如果你想看起來高大上一些, 那你可以花錢買一個自己喜歡的域名, 那也花不了多少錢.
  • 關於HUGO2的更多,有音質,還有其他...
    hugo2給予更紮實的低頻能量,在金磚下的低頻,相對會有點軟綿綿。換上捆綁後,能聽得出低頻在小聲時的克制和凝聚,響度變大力量感也隨之增強。能量感,是在接觸到HUGO2之後的第二聽感,卻是最大的感受。第一聽感是它的聲音均衡而平和,並有著豐富細節。這一點不少器材能做到,也許水準沒那麼優秀,不過是能感受到是走均衡方向的。但能量感,能把整個聲場都撐起來,而且是自然的撐起來,不是想做就能做得到的,這一點在目前的便攜耳放裡甚是難得。
  • Chord Hugo2 靚聲指數49,152taps
    以藍牙方式與Hugo2連接,可見Hugo2屬支持aptX的藍牙系統▲如同旗艦般的高水平 Chord hugo2緊接的另一篇測試是把Mojo的功能從普通DAC耳擴變成無線接收系統的實際應用方法,至於質素更高的Hugo2,毫無疑問因為多方面的進一步改良,加上濾波taps數由Hugo的26,000增至49,152而大勝從前。