Wordpress到Hugo的博客迁移

Migrate wordpress blog to hugo

Wordpress到Hugo的博客迁移
Page content

一. Why?

最近由于腾讯云主机到期,又不想续租,决定将托管在腾讯云ECS的博客系统迁走。 之前阿里做活动已经入手了一台ECS,配置很低(1核2G,1M带宽,40G机械盘)。 跑个wordpress需要PHP,MySQL,Nginx/Apache这三个软件,对于1核2G的ECS来说 压力比较大,但是优化优化还是没问题的。话又说回来,我又不想花额外的时间 来管理和配置及优化这些软件,再说了,ECS已经在内核级别很大程度上进行了优化, 说白了给你1核的性能是优化后的性能,在应用层的优化也是有条件的,比如做 缓存,那得内存大。

说到这里,我不得不寻找轻量级的博客系统,而且还要速度快。经过一翻寻找, 发现一个叫vuepress的平台不错,vuepress是vue框架的作者基于vue 开发的轻量级静态站点生成器,开发其的目的是为了生成vue的官方文档,其默认 对技术文档有很好的优化。所以对于技术博客来说,使用vuepress也是不错的, 而且上手很快,非常简单。但是经过一翻尝试,发现文章变多后,vuepress生成 网站的速度极慢,一百篇左右的markdown文章需要接近8分钟,这样的话每修改 某个配置或者文章都得等等很久,而且vuepress除了需要node.js,还需要使用 npm/yarn等包管理器来解决一些依赖问题。

说到这里,我又不得不放弃vuepress,继续寻找。后面又找到一个叫halo的 基于java开发的博客系统,有完整的后台管理系统,还可以导入markdown文件 直接生成博客页面,很方便。但是在简单的使用后又发现速度很慢,而且占用 内存也比较大,我的2G内存根本不够,我又放弃了。

最后发现了我现在正在使用的博客系统,hugo(雨果)。其是使用golang语言 开发的静态站点生成器,而且最重要的是hugo是开源的,有比较完整的生态, 有很多主题基于这些主题的二次开发主题,基本上稍微看得懂CSS/HTML/JS 的人就可以自定义自己的hugo站点,另外hugo使用了go语言模板来强化 html的功能,实质上hugo的go语言模板就是html加上一些变量和函数组合 成的html。比如:

<!-- cat themes/mainroad/layouts/partials/footer.html -->
<footer class="footer">
        <div class="container footer__container flex">
                {{ partial "footer_links.html" . }}
                <div class="footer__copyright">
                        &copy; {{ now.Format "2006" }} {{ .Site.Params.copyright | default .Site.Title }}.
                        <span class="footer__copyright-credits">{{ T "footer_credits" | safeHTML }}</span>
                </div>
        </div>
</footer>

上面 {{ 和 }} 之间的内容就是一些变量和函数,打开页面时才会确定里面 的内容,然后生成存html。

经过简单查看快速上手教程 我选择了Mainroad主题作为博客主题,因为Mainroad的布局和我以前的 博客布局类似。接下来就是安装hugo,我的系统是Ubuntu 20.04.1 LTS (Focal Fossa) 直接使用预编译的二进制包安装hugo 其实就一个二进制hugo文件,将其复制到/usr/sbin即可。

二. Mainroad

Mainroad 主题github地址: https://github.com/Vimux/Mainroad

2.1 博客组织结构

root@suosuoli:/home/hugo-blog-online/blog# tree -L 1
. # 博客的根目录
├── archetypes
├── config.toml # hugo配置文件
├── content     # 所以文章所在目录
├── data
├── layouts     # 自定义页面和go template
├── LICENSE
├── modify_indexXML.sh
├── node_modules
├── package.json  # npm包管理配置
├── package-lock.json
├── process_md.sh
├── public      # 生成的站点
├── README.md
├── resources
├── start_hugo_server.sh
├── static     # CSS/JS/image
└── themes     # mainroad主题

root@suosuoli:/home/hugo-blog-online/blog# tree -L 1 content/
content/  # 文章
├── author
├── cicd
├── db
├── docker
├── kubernetes
├── linux
├── loadbalance_middleware
├── log_monitor
├── search.html  # 实现搜索功能
├── virtualization
└── webServer

root@suosuoli:/home/hugo-blog-online/blog# tree -L 1 public/
public/ # hugo生成的站点,如果使用nginx来部署,那么root指定的目录就到public/
├── 404.html
├── all_notes
├── apple-touch-icon.png
├── author
├── categories
├── cicd
├── css
├── db
├── docker
├── favicon.ico
├── gen
├── img
├── index.html
├── index.xml
├── js
├── kubernetes
├── linux
├── loadbalance_middleware
├── log_monitor
├── page
├── readme
├── search
├── sitemap.xml
├── tags
├── virtualization
└── webServer

2.2 hugo配置文件config.toml

下面是我的站点使用的配置

# 站点UR改L
baseURL = "http://39.105.69.237/"
title = "SUOSUOLI.CN"
languageCode = "en-us"
# 使用的主题
theme = ["mainroad"]
# 单个页面显示的文章数
paginate = "10"

readmore = true
# 下面的选选项加上是为了文章概述能显示中文
hasCJKLanguage = true
# 文章概述的字节数
summaryLength = 120

# 关于作者,会在文章末显示
[Author]
  name = "suo.li"
  bio = "suo.li likes sharing."
  avatar = "img/site/suo.png"

###### menu section #####
# 文章主菜单和次级菜单,mainroad不支持次级菜单
# 后面会介绍如何让它支持次级菜单
[menu]

  [[menu.main]]
    identifier = "linux"
    name = "linux"
    pre = "<i class='fa fa-road'></i>"
    url = "/linux/"
    weight = -120
  [[menu.main]]
    name = "shell"
    url = "/tags/shell/"
    parent = "linux"
    weight = 1 
  [[menu.main]]
    name = "文本三剑客"
    url = "/tags/grepSedAwk/"
    parent = "linux"
    weight = 2

  [[menu.main]]
    identifier = "docker"
    name = "docker"
    pre = "<i class='fa fa-heart'></i>"
    url = "/docker/"
    weight = -110

  [[menu.main]]
    name = "k8s"
    pre = "<i class='fa fa-road'></i>"
    url = "/kubernetes/"
    weight = -100

  [[menu.main]]
    identifier = "cicd"
    name = "cicd"
    pre = "<i class='fa fa-road'></i>"
    url = "/cicd/"
    weight = -90
  [[menu.main]]
    name = "gitlabJenkins"
    url = "/tags/gitlabJenkins/"
    parent = "cicd"
    weight = 1 
  [[menu.main]]
    name = "deploy"
    url = "/tags/deploy/"
    parent = "cicd"
    weight = 3 
  [[menu.main]]
    name = "ansible"
    url = "/tags/ansible/"
    parent = "cicd"
    weight = 2 

  [[menu.main]]
    identifier = "db"
    name = "db"
    pre = "<i class='fa fa-road'></i>"
    url = "/db/"
    weight = -80
  [[menu.main]]
    name = "mysql"
    url = "/tags/mysql/"
    parent = "db"
    weight = 1 
  [[menu.main]]
    name = "redis"
    url = "/tags/redis/"
    parent = "db"
    weight = 2 
  [[menu.main]]
    name = "mongodb"
    url = "/tags/mongodb/"
    parent = "db"
    weight = 3
  [[menu.main]]
    name = "postgresql"
    url = "/tags/postgresql/"
    parent = "db"
    weight = 4

  [[menu.main]]
    identifier = "webServer"
    name = "WEB服务"
    pre = "<i class='fa fa-road'></i>"
    url = "/webServer/"
    weight = -70
  [[menu.main]]
    name = "nginx"
    url = "/tags/nginx/"
    parent = "webServer"
    weight = 1
  [[menu.main]]
    name = "tomcat"
    url = "/tags/tomcat/"
    parent = "webServer"
    weight = 2
  [[menu.main]]
    name = "apache"
    url = "/tags/apache/"
    parent = "webServer"
    weight = 3
  [[menu.main]]
    name = "LAMP"
    url = "/tags/LAMP/"
    parent = "webServer"
    weight = 4

  [[menu.main]]
    identifier = "virt"
    name = "虚拟化"
    pre = "<i class='fa fa-road'></i>"
    url = "/virtualization/"
    weight = -60
  [[menu.main]]
    name = "KVM"
    url = "/tags/kvm/"
    parent = "virt"
    weight = 1
  [[menu.main]]
    name = "ESXi"
    url = "/tags/esxi/"
    parent = "virt"
    weight = 2


  [[menu.main]]
    identifier = "logMonitor"
    name = "日志与监控"
    pre = "<i class='fa fa-road'></i>"
    url = "/log_monitor/"
    weight = -50
  [[menu.main]]
    name = "elkStack"
    url = "/tags/elk/"
    parent = "logMonitor"
    weight = 1
  [[menu.main]]
    name = "zabbix"
    url = "/tags/zabbix/"
    parent = "logMonitor"
    weight = 2

  [[menu.main]]
    identifier = "lbAndMiddleware"
    name = "负载均衡和中间件"
    pre = "<i class='fa fa-road'></i>"
    url = "/loadbalance_middleware/"
    weight = -40
  [[menu.main]]
    name = "haproxy"
    url = "/tags/haproxy/"
    parent = "lbAndMiddleware"
    weight = 1
  [[menu.main]]
    name = "lvs"
    url = "/tags/lvs/"
    parent = "lbAndMiddleware"
    weight = 2
  [[menu.main]]
    name = "keepalived"
    url = "/tags/keepalived/"
    parent = "lbAndMiddleware"
    weight = 3
  [[menu.main]]
    name = "mq"
    url = "/tags/mq/"
    parent = "lbAndMiddleware"
    weight = 4
  [[menu.main]]
    name = "kafka"
    url = "/tags/kafka/"
    parent = "lbAndMiddleware"
    weight = 5
  [[menu.main]]
    name = "zookeeper"
    url = "/tags/zookeeper/"
    parent = "lbAndMiddleware"
    weight = 6
  [[menu.main]]
    name = "dubbo"
    url = "/tags/dubbo/"
    parent = "lbAndMiddleware"
    weight = 7

  [[menu.main]]
    name = "Author"
    pre = "<i class='fa fa-road'></i>"
    url = "/author/"
    weight = -30
###### menu section #####


[Params]
# 主页配置信息
  description = "suo.li's personal blog for sharing."
  #copyright = "Copyright &#xA9; 2020 suo.li. All Rights Reserved."
  opengraph = true
  schema = true
  authorbox = true
# 显示文章目录
  toc = true
# 文章列表分页
  pager = true
# 定义在主页根据时间顺序显示的文章区块(每个文章区块就是一个位于content/目录的文件夹)
  mainSections = ["linux", "docker", "kubernetes", "db", "cicd", "webServer", "virtualization", "log_monitor", "loadbalance_middleware"]
# 发布文章需要记录的元信息
  post_meta = ["author", "date", "categories", "translations"]
  dateformat = "2006-01-02"
  mathjax = true
  mathjaxPath = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js"
  mathjaxConfig = "TeX-AMS-MML_HTMLorMML"
  googleFontsLink = "https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700"
# 自定义CSS/JS位置
  customCSS = ["css/custom.css"]
  customJS = ["js/custom.js"]

[Params.style.vars]
# 选中的文字高亮颜色
  highlightColor = "#e22d30"

  fontFamilyPrimary = "'Open Sans', Helvetica, Arial, sans-serif"
  fontFamilySecondary = "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"

[Params.logo]
# 博客左上角logo和二级标题,title我用了图片,所以没写
  image = "http://39.105.69.237/img/site/logo.png"
  title = ""
  subtitle = "LINUX PLATFORM & OPEN SOURCE"

# 侧边栏配置
---

## 一.Redis 介绍

Redis  Memcached 都是非关系型数据库也称为 NoSQL 数据库MySQL
MariadbSQL ServerPostgreSQLOracle 数据库属于关系型数据
关系型数据库(RDBMS, Relational Database Management System)。
......

搜索功能使用了网站的index.xml文件,使用js在浏览器进行搜索并将 搜索结果拼接成html在搜索页面展示。

  1. 首先编辑config.toml
...
[outputs]
# 添加一个输出选项为RSS
    home = ["HTML", "RSS"]
...

访问http://ip/index.xml看是否生成相关xml文件

root@suosuoli:/home/hugo-blog-online/blog# curl http://39.105.69.237/index.xml 2> /dev/null | head
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>SUOSUOLI.CN</title>
    <link>http://localhost:8080/</link>
   <description>Recent content on SUOSUOLI.CN</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 28 Aug 2020 14:50:02 +0800</lastBuildDate><atom:link href="http://localhost:8080/index.xml" rel="self" type="application/rss+xml" />
    <item>

基于该文件,就可以使用js代码来搜索相关文章。

  1. 然后编辑content/search.html
+++
title = "搜索"
menu = "main"
weight = 30
toc = false
+++

<style>
  /* 手机适配 */
  @media screen and (max-width: 500px) {
     .search{
       padding-right: 25px;
     }

     .search input{
       width: 100%;
     }

     .search button{
       display: none;
     }
  }
  /* 电脑适配 */
  @media screen and (min-width: 500px) {
      .search{
        width: 500px;
      }

      .search input{
        width: 444px;
      }
  }

  /* 通用样式 */
  .search{
    margin: auto;
  }

  .search input{
    outline: none;
    border: 2px solid #c05b4d;
    height: 32px;
    padding: 10px;
  }
  .search button{
    outline: none;
    border: 0px;
    height: 32px;
    width:56px;
    position:absolute;
    background-color:#c05b4d ;
  }
  .search .icon{
    width: 28px;
    height: 28px;
  }
</style>
<div class="search">
  <input type="text" placeholder="请输入搜索内容..." id="search-key" />
  <button onclick="search()">
    <svg t="1583982313567" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1271"
      width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink">
      <defs>
        <style type="text/css"></style>
      </defs>
      <path d="M694.857143 475.428571q0-105.714286-75.142857-180.857142T438.857143 219.428571 258 294.571429 182.857143 475.428571t75.142857 180.857143T438.857143 731.428571t180.857143-75.142857T694.857143 475.428571z m292.571428 475.428572q0 29.714286-21.714285 51.428571t-51.428572 21.714286q-30.857143 0-51.428571-21.714286l-196-195.428571q-102.285714 70.857143-228 70.857143-81.714286 0-156.285714-31.714286t-128.571429-85.714286-85.714286-128.571428T36.571429 475.428571t31.714285-156.285714 85.714286-128.571428 128.571429-85.714286T438.857143 73.142857t156.285714 31.714286 128.571429 85.714286 85.714285 128.571428T841.142857 475.428571q0 125.714286-70.857143 228l196 196q21.142857 21.142857 21.142857 51.428572z"
        p-id="1272" fill="#ffffff"></path>
    </svg>
  </button>
</div>
<h1 id="search-tip" style="color: #c05b4d;text-align: center;display: none;">搜索中,请稍后 ...</h1>
<br />
<div id="result"></div>

<script type="text/javascript">
  // enter
  window.onload = function() {
    document.onkeydown = function(ev) {
      var event = ev || event
      if (event.keyCode == 13) {
        search()
      }
    }
  }

  // search
  function search() {
    key = document.getElementById("search-key").value;
    if (key === "") {
      return;
    }
    document.getElementById("search-key").value = "";

    // tip
    document.getElementById("search-tip").innerText = "Searching ...";
    document.getElementById("search-tip").style.display = "block";

    // clear
    var el = document.getElementById('result');
    var childs = el.childNodes;
    for (var i = childs.length - 1; i >= 0; i--) {
      el.removeChild(childs[i]);
    }

    // xml
    xmltext = new XMLHttpRequest;
    //xmltext.open("GET", "/index.xml", false);
    xmltext.open("GET", "/gen/dex.xml", false);
    xmltext.send();
    resp = xmltext.responseXML;
    items = resp.getElementsByTagName("item");
    // search
    var i = 0;
    haveResult = false;
    while (i < items.length) {
      txt = items[i].getElementsByTagName("title")[0].innerHTML + items[i].getElementsByTagName("description")[0].innerHTML
      if (txt.indexOf(key) > -1) {
        haveResult = true;
        title = items[i].getElementsByTagName("title")[0].innerHTML;
        link = items[i].getElementsByTagName("link")[0].innerHTML;
        time = items[i].getElementsByTagName("pubDate")[0].innerHTML;
        mark = items[i].getElementsByTagName("description")[0].innerHTML;
        addItem(title, link, time, mark)
      }
      i++;
    }
    if (!haveResult) {
      document.getElementById("search-tip").innerText = "无结果 ...";
      document.getElementById("search-tip").style.display = "block";
    }
  }

  // add
  function addItem(title, link, time, mark) {
    document.getElementById("search-tip").style.display = "none";
    tmpl = "<article class=\"post\" style=\"border-bottom: 1px solid #e6e6e6;\" >" +
      "<header class=\"post-header\">" +
      "<h1 class=\"post-title\"><a class=\"post-link\" href=\"" + link + "\" target=\"_blank\">" + title + "</a></h1>" +
      "<div class=\"post-meta\">" +
      " <span class=\"post-time\">" + time + "</span>" +
      "</div>" +
      " </header>" +
      "<div class=\"post-content\">" +
      "<div class=\"post-summary\">" + mark + "</div>" +
      "<div class=\"read-more\">" +
      "<a href=\"" + link + "\" class=\"read-more-link\" target=\"_blank\">阅读更多</a>" +
      "</div>" +
      " </div>" +
      "</article>"
    div = document.createElement("div")
    div.innerHTML = tmpl;
    document.getElementById('result').appendChild(div)
  }
</script>

2.5 其它

  1. 修改footer的文章版权声明

vim themes/mainroad/i18n/zh-cn.yaml

...
# Footer
- id: footer_credits
  translation: "基于 <a href=\"https://gohugo.io/\" rel=\"nofollow noopener\" target=\"_blank\">Hugo</a> 引擎和 \
  <a href=\"https://github.com/Vimux/Mainroad/\" rel=\"nofollow noopener\" target=\"_blank\">Mainroad</a>主題"
...