运维| mkdocs文档博客搭建

  • centos7 系统
  • mkdocs
  • material 主题

kDocs 可以同时编译多个 markdown 文件,形成书籍一样的文件。有多种主题供你选择,很适合项目使用。

MkDocs 是快速,简单和华丽的静态网站生成器,可以构建项目文档。文档源文件在 Markdown 编写,使用单个 YAML 配置文件配置。

MkDocs 是一款快速、简单且极其出色的静态网站生成器,专门用于构建项目文档。文档源文件以 Markdown 编写,并使用单个 YAML 配置文件进行配置。它易于使用,并且可以使用第三方主题、插件和 Markdown 扩展进行扩展。

现在都在使用python3,先安装pip3

text

python --version
yum install python3-pip

text

python3 -m pip install mkdocs  -i https://pypi.tuna.tsinghua.edu.cn/simple/
# 先选择好目录,我这里把博客文档放到/data/blog下面,根据自己要求更改
# 生成项目文档
cd /data/blog
python3 -m mkdocs new . 

text

[root@bdser mkdocs]# python3 -m mkdocs new . 
INFO     -  Writing config file: ./mkdocs.yml
INFO     -  Writing initial docs: ./docs/index.md
[root@bdser mkdocs]# tree -L 2 
.
├── docs
│   └── index.md
├── mkdocs.yml
## 启动,命令后可以自定义监听端口
[root@bdser mkdocs]# python3 -m mkdocs serve -a 0.0.0.0:8001
INFO     -  Building documentation...
WARNING  -  Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to
            connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party
            production-ready server instead.
INFO     -  Cleaning site directory
INFO     -  Documentation built in 0.11 seconds
INFO     -  [15:11:22] Serving on http://0.0.0.0:8001/

<补图>

简单修改配置,在启动,就可以看到最新配置过的界面了

text

site_name: My Docs
nav:
  - Home: index.md
  - About: about.md

下面我们重点讲解优化mkdocs.yaml配置,在mkdocs.yml中配置站点名称、描述、作者、ur、导航菜单等信息等。

基础配置如下:

  • site name:站点名称
  • site ur:站点 URL链接
  • site author:站点作者
  • site description:站点描述
  • copyright:版权信息
  • repo ur:站点仓库 URL
  • nav: 站点导航
  • theme: 站点主题
  • markdown extensions:Markdown扩展

text

python3 -m pip   install mkdocs-material  -i https://pypi.tuna.tsinghua.edu.cn/simple/ 

text

# 编辑mkdocs.yml
# theme: material 

谷歌分析地址: https://analytics.google.com/ 先注册账号,可能需要谷歌邮箱注册,成功登录后账号后,新建媒体资源,媒体资源就是你的网站

媒体资源-媒体收集和分析-选择并打开数据流-衡量ID,把这个ID复制到配置中

text

google_analytics:
  - 'UA-XXXXXXXX-X'
  - 'auto'

推荐giscus

利用 GitHub Discussions 实现的评论系统,让访客借助 GitHub 在你的网站上留下评论和反应吧!本项目深受 utterances 的启发。

  • 开源。🌏
  • 无跟踪,无广告,永久免费。📡 🚫
  • 无需数据库。所有数据均储存在 GitHub Discussions 中。:octocat:
  • 支持自定义主题!🌗
  • 支持多种语言。🌐
  • 高可配置性。🔧
  • 自动从 GitHub 拉取新评论与编辑。🔃
  • 可自建服务!🤳

在mkdocs添加配置

text

theme:
  name: material
  custom_dir: docs/overrides  #主要是这一行

参考下图新建overrides文件,结构图如下:

text

[root@bdser mkdocs]# tree -L 3 | head -n 10 
.
├── docs
│   ├── index.md
│   └── overrides
│       └── partials
├── mkdocs.yml

建议github仓库中,新建库,选择公开 https://github.com/apps/giscus

然后访问绑定仓库 - settings - Discussions(勾选启用),本仓库 Discussions 添加成功

text

{% if page.meta.comments %}
  <h2 id="__comments">{{ lang.t("meta.comments") }}</h2>


  <!-- Insert generated snippet here 高亮这里 --> 
  <script src="https://giscus.app/client.js"
  data-repo="你的仓库名称(如Wcowin/hexo-site-comments)"
  data-repo-id=" "
  data-category=" "
  data-category-id=" "
  data-mapping="pathname"
  data-strict="0"
  data-reactions-enabled="1"
  data-emit-metadata="0"
  data-input-position="bottom"
  data-theme="preferred_color_scheme"
  data-lang="zh-CN"
  crossorigin="anonymous"
  async>


</script>
  <!-- Synchronize Giscus theme with palette -->
  <script>
    var giscus = document.querySelector("script[src*=giscus]")

    // Set palette on initial load
    var palette = __md_get("__palette")
    if (palette && typeof palette.color === "object") {
      var theme = palette.color.scheme === "slate"
        ? "transparent_dark"
        : "light"

      // Instruct Giscus to set theme
      giscus.setAttribute("data-theme", theme) 
    }

    // Register event handlers after documented loaded
    document.addEventListener("DOMContentLoaded", function() {
      var ref = document.querySelector("[data-md-component=palette]")
      ref.addEventListener("change", function() {
        var palette = __md_get("__palette")
        if (palette && typeof palette.color === "object") {
          var theme = palette.color.scheme === "slate"
            ? "transparent_dark"
            : "light"

          // Instruct Giscus to change theme
          var frame = document.querySelector(".giscus-frame")
          frame.contentWindow.postMessage(
            { giscus: { setConfig: { theme } } },
            "https://giscus.app"
          )
        }
      })
    })
  </script>
{% endif %}

打开https://giscus.app/zh-CN 走完这个页面的流程就会得到(这会在你的Github创建新的仓库,建议自己先去新建个 Discussions)

text

<script src="https://giscus.app/client.js"
        data-repo="[在此输入仓库]"
        data-repo-id="[在此输入仓库 ID]"
        data-category="[在此输入分类名]"
        data-category-id="[在此输入分类 ID]"
        data-mapping="pathname"
        data-strict="0"
        data-reactions-enabled="1"
        data-emit-metadata="0"
        data-input-position="bottom"
        data-theme="preferred_color_scheme"
        data-lang="zh-CN"
        crossorigin="anonymous"
        async>
</script>

复制将此代码,替换最上面👆🏻comments.html中高亮的代码

每次编写markdown博客的时候,我们需要再开头编写元信息,需要加入如下元数据说明

text

---
title: 留言板 
comments: true #开启评论
---

安装插件glightbox

text

pip install mkdocs-glightbox -i  https://pypi.tuna.tsinghua.edu.cn/simple/

配置mkdocs.yml

text

plugins:
  - search
  - glightbox   #加载插件
  - tags:
      tags_file: tags.md

重新启动,生效

yaml

plugins:
  - search
  - glightbox    
  - tags:                   #增加如下俩行
      tags_file: tags.md
......

nav:
  - 标签: tags.md 

把下面复制到tags.md文件中

shell

# Tags

Following is a list of relevant tags:

<!-- material/tags -->

yaml

extra_javascript:
  - https://vercount.one/js
  - javascript/extra.js    #添加脚本

脚本如下 如果docs,没有Javascript目录,可以新建

shell

mkdir /docs/javascript
## js代码
//雪花
const fps = 30;
const mspf = Math.floor(1000 / fps) ; 

let width = window.innerWidth || document.documentElement.clientWidth;
let height = window.innerHeight || document.documentElement.clientHeight;
let canvas;
window.addEventListener('resize', () => {
  width = window.innerWidth || document.documentElement.clientWidth;
  height = window.innerHeight || document.documentElement.clientHeight;
  if (canvas) {
    canvas.width = width;
    canvas.height = height;
  }
});

let particles = [];
let wind = [0, 0];
let cursor = [0, 0];

function velocity(r) {
  return 70 / r + 30;
}

function sine_component(h, a) {
  return [2 * Math.PI / h, Math.random() * a, Math.random() * 2 * Math.PI]; // [frequency, amplitude, phase]
}

function calc_sine(components, x) {
  let sum = 0;
  for (let i = 0; i < components.length; i++) {
    const [f, a, p] = components[i];
    sum += Math.sin(x * f + p) * a;
  }
  return sum;
}

function gen_particle() {
  let r = Math.random() * 4 + 1;
  return {
    radius: r,
    x: Math.random() * width,
    y: -r,
    opacity: Math.random(),
    sine_components: [sine_component(height, 3), sine_component(height / 2, 2), sine_component(height / 5, 1), sine_component(height / 10, 0.5)],
  };
}

function update_pos(dt) {
  const n = particles.length;
  for (let i = 0; i < n; i++) {
    const v = velocity(particles[i].radius);
    particles[i].x += calc_sine(particles[i].sine_components, particles[i].y) * v / 5 * dt;
    particles[i].y += v * dt;

    // const dist = Math.hypot(particles[i].x - cursor[0], particles[i].y - cursor[1]) + 1;
    // particles[i].x += wind[0] * dt / dist
    // particles[i].y += wind[1] * dt / dist;

    if (particles[i].y - particles[i].radius > height) {
      particles[i] = gen_particle();  
    }
  }
}

let context_cache;
function get_context() {
  if (context_cache)
    return context_cache;

  canvas = document.createElement('canvas');
  canvas.id = 'snow-canvas';
  canvas.width = width;
  canvas.height = height;
  canvas.style = 'position: fixed; top: 0; left: 0; overflow: hidden; pointer-events: none; z-index: 256;';
  if ((document.documentElement.dataset.darkreaderMode || "").startsWith('filter'))
    canvas.style.filter = 'invert(1)';
  document.body.appendChild(canvas);

  context_cache = canvas.getContext('2d');
  return context_cache;
}

function draw() {
  const ctx = get_context();

  ctx.clearRect(0, 0, width, height);

  const n = particles.length;
  for (let i = 0; i < n; i++) {
    const p = particles[i];
    ctx.fillStyle = `rgba(255, 255, 255, ${p.opacity})`;
    ctx.shadowColor = '#80EDF7';
    ctx.shadowBlur = 7;
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.radius, 0, 2*Math.PI);
    ctx.fill();
  }
}

let focused = true;
let disabled = false;
let lastTime = performance.now();
const requestFrame = () => setTimeout(loop, mspf);
function loop() {
  const dt = (performance.now() - lastTime) / 1000;

  if (particles.length < 120 && Math.random() < 0.1) {
    particles.push(gen_particle());
  }

  update_pos(dt);
  draw();

  lastTime = performance.now();
  if (focused && !disabled)
    requestFrame();
}


window.addEventListener('focus', () => {
  console.log('snow start');
  focused = true;
  lastTime = performance.now();
  requestFrame();
});

window.addEventListener('blur', () => {
  console.log('snow stop');
  focused = false;
});

window.addEventListener('keydown', e => {
  if (e.ctrlKey && e.key == 's') {
    e.preventDefault();
    disabled = !disabled;
    if (disabled) {
      canvas.style.display = 'none';
    } else {
      canvas.style.display = 'block';
      lastTime = performance.now();
      requestFrame();
    }
  }
});

requestFrame();
//雪花

docs/overrides文件下新建404.html即可

text

root@DESKTOP-CK75KU2:/mnt/d/mkdocs# tree -L 3 docs/overrides/ 
docs/overrides/
├── 404.html
└── partials
    └── comments.html

text

<html>
 <head> 
  <meta charset="UTF-8" /> 
  <meta name="description" content="公益404页面是由腾讯公司员工志愿者自主发起的互联网公益活动。" /> 
  <link rel="icon" href="data:image/gif;base64,R0lGODdhIAAgANUAAAAAAAgFBgYICAwMDBAPDxAQDxQTFBUYFxcaGRwcHCQkJCQoJykqKTQ0ND09PUJCQktMTFZWVltcXF1hYGNjY2doaGpqanNzc3d5eHp6eoODg4uLi5eXl5mamqOjo62tra+wr7S0tLe5uLu7u7/AwMPEw83NzdbX1tfa2dra2uTk5Ovr6+/w7/T09Pj39/f4+P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAADEALAAAAAAgACAAAAb/QJhwOGyZOpIGQ6FgNCQcU4tIrcJaIwlgy+1uJaOplZqydBOSzGaTkSS6lNN4ONoOAJRQajV0rVAgFF0jcyEAbw9hc1cjD4cAIVZ1DAAXKotEKxkAlIREJ48bYphFGwAKAHJCLWaVo6RFF1sWYnUDDpewVSoOd4QtWgCeMCmXLaqfUyopdF9GWxRiKQAVKh3CRHUdKhQEzFeCAEdbH6uOh28A3ypbCQhbEGIfWx2CCd8wJhB2CpFDIQvuAIBgYkiKN0kASOAzRJYjBwyFrGgAwNGFTFqUAMjwYogpA1woMFwRDgDIDUQ2LQGAcoiGLRr2QRgp8yUADURMMWFJRYMH3BglJOQSwq0EDA84c56ilOGVLisqKUaISKVFihIjRJRI4XTIiggAGggygKxICAoguSDI0/UEyAnXAJTLJMtLlwxUYcwD0MEEtIgtBKGi0OHDh3qn8IwKvEVKuGHXGAwI8apFCAOUOvxzBqNOAVzgthi1UgKasl7YrtS9MMXI6DEjpKieNWoaSA1dF7V4+QafkNKoLOlSIQsVCUkmATgYwWIRCxEOAByANKc0AAEAJnzYI6bFihQfBAUQjSlF3S0GIqjZgCFC2i0XfM/BUtJuSEVPjdRTwsQJYdljBAEAOw==" /> 
  <title>404 您访问的页面搞丢了</title> 
  <script src="https%3A//volunteer.cdn-go.cn/404/latest/404.js" rendertarget="404DlV"></script>
 </head> 
 <body style="overflow-x: hidden; max-width: 100vw;">
  <div style="margin:0px;padding:0px;background-color:rbga(0,0,0,0);">
   <div style="position:relative;left:50%;margin-left:-800px;width:1600px;height:1000px;">
    <img alt="404!您要访问的页面走丢了!" src="https://volunteer.cdn-go.cn/404/latest/img/dream4school.jpg" style="left:50%;width:1600px;margin-left:calc(50% - 800px);filter: brightness(75%);" />
    <div style="position:relative;margin-top:-1200px;width:100%;text-align:center;color:white;font-size:28px;">
     <div style="display:inline-block;width:98vw;max-width:1600px;height:calc(100vh - 250px);max-height:750px;">
      <div style="font-size:128px;font-weight: 800;">
       404
      </div>您访问的页面走丢在寻找梦想的路上了
      <br />不过您还可以和腾讯志愿者一起
      <br />
      <i><b style="font-size:1.2em">为孩子们点亮一个梦想</b></i>
     </div>
     <div style="display:inline-block;width:98vw;max-width:1600px;text-align:left;margin:0px 10px;font-size:0.75em">
      <a href="https://volunteer.cdn-go.cn/404/latest/img/dream4schoolQR.png"><img src="https://volunteer.cdn-go.cn/404/latest/img/dream4schoolQR.png" alt="点击进入支持页面" style="width:160px;" /></a>
      <br />扫码点亮一个梦想
      <div style="float:right;text-align:right;margin-right:1em;margin-top:-4.5em">
       <br>照片拍摄于湖南省岳阳市平江县三市镇新村完小
       <br>拍摄时间:二零二三年七月十一日
       <br/>(感恩基金会供稿)
       <br><a href="https://support.qq.com/products/378306" style="color:lightgray;font-size:0.8em;">我要反馈</a> 
      </div>
     </div>
    </div>
   </div>
  </div>
 </body>
</html>

text

在 mkdocs.yml 同一路径下,使用以下命令启动服务:
python3 -m mkdocs serve -a 0.0.0.0:8001

在 mkdocs.yml 同一路径下,使用以下命令依据 mkdocs.yml 配置文件,生成网页:

text

mkdocs build

成功后,将在项目文件夹下生成一个名为 site 的文件夹,其中包含了所有生成的网页。

将 site 文件夹下的所有文件拷贝到项目根目录下,与 docs文件夹及其包含的文本,共同组成网页资源。

当我们部署网页时,只需要将上述网页资源进行部署即可(忽略 mkdocs.yml 和 site 文件夹)

text

scp -rp /data/mkdocs/site  root@10.x.x.x:/data/mkdocs/site   

通过certbot生成了ssl证书,本站写过文档,可以参考下

下面是nginx配置,/data/mkdocs/site: 网站目录,可以自定义,启动nginx,可以正常访问

text

server {
  charset utf-8;
  server_name wiki.budongshu.cn;  # 改成你的 IP
  access_log         /data/nginx/logs/wiki.budongshu.cn-access.log;

  location / {
     root /data/mkdocs/site; 
     index index.html; 
     try_files $uri  $uri/ /index.html; 

  }
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/wiki.budongshu.cn/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/wiki.budongshu.cn/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = wiki.budongshu.cn) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
  listen 80;
  server_name wiki.budongshu.cn;
    return 404; # managed by Certbot


}

网页访问: https://wiki.budongshu.cn