0%

大端数据格式

1
ffmpeg -i test.mp3 -f s16be -ar 16000 -ac 1 -acodec pcm_s16be pcm16k.pcm

小端数据格式

1
ffmpeg -i test.mp3 -f s16le -ar 16000 -ac 1 -acodec pcm_s16le pcm16k.pcm

说明:

  1. -acodec pcm_s16be:输出pcm格式,采用signed 16编码,字节序为大尾端(小尾端为le);
  2. -ar 16000: 采样率为16000
  3. -ac 1: 声道数为1

前言

Next 各版本的仓库

年份 版本 仓库
2014 ~ 2017 v5 https://github.com/iissnan/hexo-theme-next
2018 ~ 2019 v6 ~ v7 https://github.com/theme-next/hexo-theme-next
2020 v8 https://github.com/next-theme/hexo-theme-next

Next 与 Hexo 版本适配关系

NodeJS 版本升级

NodeJS 版本升级不是必须的,可以根据自己的实际情况选择是否升级,首先在 NodeJS 官网下载最新版的二进制安装包,解压后配置系统环境变量即可。

1
2
3
4
5
6
7
8
9
# 配置环境变量
# vim /etc/profile
export PATH=$PATH:/usr/local/node-v14.16.1/bin
NODE_PATH=/usr/local/node-v14.16.1/lib/node_modules
PATH=$PATH:$NODE_PATH
export NODE_PATH PATH

# 使配置生效
# source /etc/profile

Hexo 版本升级

笔者是从 Hexo 3.9.0 升级到 Hexo 5.4.0,步骤如下:

全局升级 Hexo 版本

若曾经在系统里,直接使用过 hexo 的命令,才需要执行以下升级操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 清理NPM缓存
$ npm cache clean -f

# 全局安装版本检测、版本升级工具
$ npm install -g npm-check
$ npm install -g npm-upgrade

# 全局检测哪些模块可以升级,这里可以根据打印的提示信息,手动安装最新版本的模块
$ npm-check -g

# 全局更新模块
$ npm update -g

# 全局安装或更新Hexo的最新版本
$ npm install --global hexo

博客升级 Hexo 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 进入博客的根目录
$ cd /blog-root

# 检测Hexo哪些模块可以升级
$ npm-check

# 删除package-lock.json
# rm -rf package-lock.json

# 更新package.json
$ npm-upgrade

# 删除整个模块目录,这样可以避免很多坑
$ rm -rf node_modules

# 更新Hexo的模块
$ npm update --save

# 若出现依赖的问题,用以下命令检查一下,然后把报错的统一修复一下即可
$ npm audix

# 或者强制更新
$ npm update --save --force

由于新版的 Hexo 一般增加了不少新特性,因此需要使用新版 Hexo 默认的配置模版文件 _config.yml,同时还需要稍微更改旧版的 package.json 配置文件,否则容易出现各种兼容错误

1
2
3
4
5
6
7
# 进入博客的根目录
$ cd /blog-root

# 备份旧版的配置文件
$ mv _config.yml _config.yml.bak
$ mv package.json package.json.bak

1
2
3
4
5
6
7
8
$ 单独初始化全新的Hexo博客目录
$ hexo init hexo-upgrade

$ 拷贝新的配置模版文件到博客的根目录
$ cp hexo-upgrade/_config.yml /blog-root/_config.yml
$ cp hexo-upgrade/package.json /blog-root/package.json

# 最后在新的配置模版文件里,重新追加旧版的Hexo配置内容

升级 Next 主题

注意事项

笔者 从 Next 7.8.0 升级到 Next 8.3.0,值得一提的是,Next 版本升级必须注意以下事项:

  • Next 与 Hexo 的版本必须兼容
  • 必须使用新版 Next 主题的 _config.yml 配置文件,若继续使用 Next 旧版的 _config.yml 配置文件,容易出现各种兼容错误

版本升级

1
2
3
4
5
6
7
8
9
10
11
# 进入博客的主题目录
$ cd /boot-root/theme

# 备份旧版主题的配置
$ mv next next-bak

# 拉取最新的代码(注意:Next不同版本使用不同的仓库)
$ wget https://github.com/next-theme/hexo-theme-next next

# 最后将旧版的Next配置内容追加到Next新版的配置文件中,包括拷贝旧版自定义的样式、布局文件等

本地下载第三方库文件

在 Next 8.3.0 的 _config.yml 中,新增了 vendors.internal 属性(如下配置)来指定加载本地的第三方库文件,默认存放路径为 themes/next/source/lib;启用后站点就不再需要依赖第三方的 CDN 资源,而是直接使用本地站点的资源文件,这可以站点的访问速度更稳定。

1
2
3
4
5
6
7
8
9
10
11
# It's recommended to use the same version as in `_vendors.yml` to avoid potential problems.
# Remember to use the HTTPS protocol of CDN links when you enable HTTPS on your site.
vendors:
# The CDN provider of NexT internal scripts.
# Available values: local | jsdelivr | unpkg | cdnjs
# Warning: If you are using the latest master branch of NexT, please set `internal: local`
internal: local
# The default CDN provider of third-party plugins.
# Available values: local | jsdelivr | unpkg | cdnjs
# Dependencies for `plugins: local`: https://github.com/next-theme/plugins
plugins: jsdelivr

themes/next/_vendors.yml 里定义的第三方库文件统一下载下来,并存放在 themes/next/source/lib 目录下,具体步骤如下

  • 临时更改 themes/next/scripts/events/lib/vendors.js 源文件的代码(如下所示),然后执行 hexo g 命令,从输出日志信息中得到批量下载第三方库文件的 curl 命令
  • 将得到的所有 curl 命令保存到 Shell 脚本文件里,然后执行 Shell 脚本文件批量下载第三方库文件到 themes/next/source/lib 目录下
  • 最后还原 themes/next/scripts/events/lib/vendors.js 源文件的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
const { name, version, file, alias, unavailable } = value;

// 打印下载命令
var _path = `https://cdn.jsdelivr.net/npm/${name}@${version}/${file}`;
console.log("curl " + _path + " --create-dirs -o ${DIR}" + "/" + name + "/" + file);
continue;

const links = {
local : url_for.call(hexo, `lib/${name}/${file}`),
jsdelivr: `https://cdn.jsdelivr.net/npm/${name}@${version}/${file}`,
unpkg : `https://unpkg.com/${name}@${version}/${file}`,
cdnjs : `https://cdnjs.cloudflare.com/ajax/libs/${alias || name}/${version}/${file.replace(/^(dist|lib|)\/(browser\/|)/, '')}`
};

批量下载 Next 8.3.0 版本所需的第三方库文件的 Shell 脚本文件如下,DIRthemes/next/source/lib 目录的绝对路径,请自行更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/sh

DIR=/usr/local/hexo-develop/themes/next/source/lib
rm -rf ${DIR}/*

curl https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js --create-dirs -o ${DIR}/animejs/lib/anime.min.js
curl https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css --create-dirs -o ${DIR}/@fortawesome/fontawesome-free/css/all.min.css
curl https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-core.min.js --create-dirs -o ${DIR}/prismjs/components/prism-core.min.js
curl https://cdn.jsdelivr.net/npm/prismjs@1.23.0/plugins/autoloader/prism-autoloader.min.js --create-dirs -o ${DIR}/prismjs/plugins/autoloader/prism-autoloader.min.js
curl https://cdn.jsdelivr.net/npm/prismjs@1.23.0/plugins/line-numbers/prism-line-numbers.min.js --create-dirs -o ${DIR}/prismjs/plugins/line-numbers/prism-line-numbers.min.js
curl https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-mml-chtml.js --create-dirs -o ${DIR}/mathjax/es5/tex-mml-chtml.js
curl https://cdn.jsdelivr.net/npm/katex@0.13.0/dist/katex.min.css --create-dirs -o ${DIR}/katex/dist/katex.min.css
curl https://cdn.jsdelivr.net/npm/katex@0.13.0/dist/contrib/copy-tex.min.js --create-dirs -o ${DIR}/katex/dist/contrib/copy-tex.min.js
curl https://cdn.jsdelivr.net/npm/katex@0.13.0/dist/contrib/copy-tex.min.css --create-dirs -o ${DIR}/katex/dist/contrib/copy-tex.min.css
curl https://cdn.jsdelivr.net/npm/@next-theme/pjax@0.4.0/pjax.min.js --create-dirs -o ${DIR}/@next-theme/pjax/pjax.min.js
curl https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js --create-dirs -o ${DIR}/jquery/dist/jquery.min.js
curl https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js --create-dirs -o ${DIR}/@fancyapps/fancybox/dist/jquery.fancybox.min.js
curl https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css --create-dirs -o ${DIR}/@fancyapps/fancybox/dist/jquery.fancybox.min.css
curl https://cdn.jsdelivr.net/npm/medium-zoom@1.0.6/dist/medium-zoom.min.js --create-dirs -o ${DIR}/medium-zoom/dist/medium-zoom.min.js
curl https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js --create-dirs -o ${DIR}/lozad/dist/lozad.min.js
curl https://cdn.jsdelivr.net/npm/pangu@4.0.7/dist/browser/pangu.min.js --create-dirs -o ${DIR}/pangu/dist/browser/pangu.min.js
curl https://cdn.jsdelivr.net/npm/quicklink@2.1.0/dist/quicklink.umd.js --create-dirs -o ${DIR}/quicklink/dist/quicklink.umd.js
curl https://cdn.jsdelivr.net/npm/disqusjs@1.3.0/dist/disqus.js --create-dirs -o ${DIR}/disqusjs/dist/disqus.js
curl https://cdn.jsdelivr.net/npm/disqusjs@1.3.0/dist/disqusjs.css --create-dirs -o ${DIR}/disqusjs/dist/disqusjs.css
curl https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.min.js --create-dirs -o ${DIR}/gitalk/dist/gitalk.min.js
curl https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.css --create-dirs -o ${DIR}/gitalk/dist/gitalk.css
curl https://cdn.jsdelivr.net/npm/firebase@8.3.1/firebase-app.js --create-dirs -o ${DIR}/firebase/firebase-app.js
curl https://cdn.jsdelivr.net/npm/firebase@8.3.1/firebase-firestore.js --create-dirs -o ${DIR}/firebase/firebase-firestore.js
curl https://cdn.jsdelivr.net/npm/algoliasearch@4.8.6/dist/algoliasearch-lite.umd.js --create-dirs -o ${DIR}/algoliasearch/dist/algoliasearch-lite.umd.js
curl https://cdn.jsdelivr.net/npm/instantsearch.js@4.19.0/dist/instantsearch.production.min.js --create-dirs -o ${DIR}/instantsearch.js/dist/instantsearch.production.min.js
curl https://cdn.jsdelivr.net/npm/pdfobject@2.2.5/pdfobject.min.js --create-dirs -o ${DIR}/pdfobject/pdfobject.min.js
curl https://cdn.jsdelivr.net/npm/mermaid@8.9.2/dist/mermaid.min.js --create-dirs -o ${DIR}/mermaid/dist/mermaid.min.js
curl https://cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css --create-dirs -o ${DIR}/animate.css/animate.min.css
curl https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.js --create-dirs -o ${DIR}/nprogress/nprogress.js
curl https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.css --create-dirs -o ${DIR}/nprogress/nprogress.css
curl https://cdn.jsdelivr.net/npm/ribbon.js@1.0.2/dist/ribbon.min.js --create-dirs -o ${DIR}/ribbon.js/dist/ribbon.min.js

常见问题

问题一

完成上述升级操作后,执行 hexo generator 命令后,themes/next/scripts/events/lib/vendors.js 的代码里面抛出以下错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
INFO  Start processing
FATAL {
err: TypeError: Cannot read property 'call' of undefined
at module.exports (/usr/local/hexo-develop/themes/next/scripts/events/lib/vendors.js:27:25)
at Hexo.<anonymous> (/usr/local/hexo-develop/themes/next/scripts/events/index.js:9:27)
at Hexo.tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Hexo.<anonymous> (/usr/local/hexo-develop/node_modules/bluebird/js/release/method.js:15:34)
at /usr/local/hexo-develop/node_modules/hexo/lib/extend/filter.js:67:52
at tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Object.gotValue (/usr/local/hexo-develop/node_modules/bluebird/js/release/reduce.js:166:18)
at Object.gotAccum (/usr/local/hexo-develop/node_modules/bluebird/js/release/reduce.js:155:25)
at Object.tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromiseCtx (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:641:10)
at _drainQueueStep (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:97:12)
at _drainQueue (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:86:9)
at Async._drainQueues (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:102:5)
at Immediate.Async.drainQueues [as _onImmediate] (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:15:14)
at processImmediate (internal/timers.js:461:21)

这一般是 Hexo 的部分模块没有成功更新引起,例如 hexo-util 模块更新失败,最终导致代码不兼容。首先检查 hexo-util 模块的版本,然后可以尝试执行以下命令,强制更新 hexo-util 模块

1
2
3
4
5
6
# 进入博客的主题目录
$ cd /boot-root/

# 强制更新
$ npm update --save --force

问题二

完成上述升级操作后,执行 hexo generator 命令后,Hexo 会抛出以下错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
FATAL { err:
TypeError: line.matchAll is not a function
at res.value.res.value.split.map.line (/usr/local/hexo-develop/node_modules/hexo-util/lib/highlight.js:128:26)
at Array.map (<anonymous>)
at closeTags (/usr/local/hexo-develop/node_modules/hexo-util/lib/highlight.js:126:37)
at highlight (/usr/local/hexo-develop/node_modules/hexo-util/lib/highlight.js:119:10)
at highlightUtil (/usr/local/hexo-develop/node_modules/hexo-util/lib/highlight.js:23:16)
at data.content.dataContent.replace (/usr/local/hexo-develop/node_modules/hexo/lib/plugins/filter/before_post_render/backtick_code_block.js:92:17)
at String.replace (<anonymous>)
at Hexo.backtickCodeBlock (/usr/local/hexo-develop/node_modules/hexo/lib/plugins/filter/before_post_render/backtick_code_block.js:19:30)
at Hexo.tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Hexo.<anonymous> (/usr/local/hexo-develop/node_modules/bluebird/js/release/method.js:15:34)
at Promise.each.filter (/usr/local/hexo-develop/node_modules/hexo/lib/extend/filter.js:67:52)
at tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Object.gotValue (/usr/local/hexo-develop/node_modules/bluebird/js/release/reduce.js:166:18)
at Object.gotAccum (/usr/local/hexo-develop/node_modules/bluebird/js/release/reduce.js:155:25)
at Object.tryCatcher (/usr/local/hexo-develop/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromise0 (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:649:10)
at Promise._settlePromises (/usr/local/hexo-develop/node_modules/bluebird/js/release/promise.js:729:18)
at _drainQueueStep (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:93:12)
at _drainQueue (/usr/local/hexo-develop/node_modules/bluebird/js/release/async.js:86:9)


NodeJS 从 12.0.0 才开始支持函数 String.matchAll(),如果 NodeJS 的版本低于 12.0.0,那么执行 Hexo 的构建命令就会出现上述的错误,解决方法如下:

  • 方法一:将 NodeJs 升级到高于 12.0.0 的版本
  • 方法二:更改 Hexo 的配置文件 _config.yml,禁用 highlight 的功能,这样就可以避免出错,但这将会关闭代码的高亮显示功能
1
2
highlight:
enable: false

环境准备

本地安装 Git NodeJS

检查环境

1
2
3
4
5
git -v

node -v

npm -v

切换镜像站,具体参考

1
npm config set registry https://registry.npmmirror.com

注册Github账号,Gitea账号(可选)
[^注] Github由于网络问题会经常无法链接,可使用Gitea作为中转,先将代码提交道Gitea,然后Gitea配置自动推送到Github

Github Pages初始化

Hexo环境搭建

1
2
3
4
5
6
7
8
npm install 

# 插件
npm install hexo-asset-img # 头像
npm install hexo-auto-category # 自动分类
npm install hexo-generator-searchdb # 生成搜索数据库
npm install hexo-backlink # Obsdian链接转换
npm install hexo-deploy-git # git自动发布

Hexo主题配置

Hexo 配置

参考
【2024】从零开始用Hexo+GithubPage搭建个人网站(保姆级) - 知乎 (zhihu.com)
Hexo + Obsidian + Git 完美的博客部署与编辑方案 - 个人文章 - SegmentFault 思否
Obsidian+Git完美维护Hexo博客 - 知乎 (zhihu.com)

官方文档(中文)

节点名称 节点IP 配置 系统版本
VIP 192.168.50.220 虚拟IP
k8s-master-221 192.168.50.221 4核 2G debian 11
k8s-master-222 192.168.50.222 4核 2G debian 11
k8s-master-223 192.168.50.223 4核 2G debian 11
k8s-node-224 192.168.50.224 4核 2G debian 11
k8s-node-225 192.168.50.225 4核 2G debian 11

主机配置

时间同步

1

配置 hostname

注意节名称不能重复

1
hostnamectl --static set-hostname k8s-master-221

配置防火墙

1
2
3
4
5
service iptables stop 

iptables -F

systemctl stop firewalld && systemctl disable firewalld

如果需要打开防火墙,执行以下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# master节点执行
ufw allow 6443/tcp
ufw allow 2379/tcp
ufw allow 2380/tcp
ufw allow 10250/tcp
ufw allow 10251/tcp
ufw allow 10252/tcp
ufw allow 10255/tcp
ufw reload

# worker节点执行
ufw allow 10250/tcp
ufw allow 30000:32767/tcp
ufw reload

关闭交换分区

1
2
swapoff -a
set -ri 's/.*swap.*/#&/' /etc/fstab

若需允许交换分区参考官方文档 交换分区的配置

配置hosts

1
2
3
4
5
6
7
cat >> /etc/hosts << EOF
192.168.50.221 k8s-master-221
192.168.50.222 k8s-master-222
192.168.50.223 k8s-master-223
192.168.50.224 k8s-worker-224
192.168.50.225 k8s-worker-225
EOF

开启 bridge 网桥过滤功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 桥接的ipv4流量转到iptables
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 设置所需的 sysctl 参数,参数在重新启动后保持不变
cat << EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1 # 开启网桥模式(必须)
net.bridge.bridge-nf-call-ip6tables = 1 # 开启网桥模式(必须)
net.ipv4.ip_forward = 1 # 转发模式(默认开启)
vm.panic_on_oom = 0 # 开启OOM(默认开启)
vm.swappiness  = 0 # 禁止使用swap空间
vm.overcommit_memory = 1 # 不检查物理内存是否够用
EOF

# 应用 sysctl 参数而不重新启动
sudo sysctl --system

配置 IPVS

1
2
3
4
5
6
7
8
9
10
11
modprobe br_netfilter

cat > /etc/sysconfig/modules/ipvs.modules << EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv
EOF

安装工具

安装 Containerd

1
2
3
4
5
6
7
# 安装
apt update
apt install -y containerd

# 导出默认配置
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1

设置cgroupdriversystemd,编辑 /etc/containerd/config.toml 文件,找到 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 部分,添加一行内容:SystemdCgroup = true

1
sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

重启containerd并设置开机启动

1
2
systemctl restart containerd
systemctl enable containerd

安装 keadm,kubelete,kubectl

1
2
3
4
5
6
# 添加安装源

# 安装
apt update
apt install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

部署高可用(仅 master 节点)

安装

1
apt install keepalived haproxy

修改haproxy配置

/etc/haproxy/haproxy.cfg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
global
maxconn 2000
ulimit-n 16384
log 127.0.0.1 local0 err
stats timeout 30s

defaults
log global
mode http
option httplog
timeout connect 5000
timeout client 50000
timeout server 50000
timeout http-request 15s
timeout http-keep-alive 15s

frontend monitor-in
bind *:33305
mode http
option httplog
monitor-uri /monitor

frontend k8s-master
bind 0.0.0.0:16443
bind 127.0.0.1:16443
mode tcp
option tcplog
tcp-request inspect-delay 5s
default_backend k8s-master

backend k8s-master
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server k8s-master1 172.16.12.111:6443 check
server k8s-master2 172.16.12.112:6443 check
server k8s-master3 172.16.12.113:6443 check

配置 keepalived

interface # 网卡名称
mcast_src_ip # 节点ip
virtual_ipaddress # vip地址

k8s-master-221配置文件/etc/keepalived/keepalived.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh" #健康检查脚本
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER #高可用主1
interface eth0 #网卡名称
mcast_src_ip 192.168.50.221 #该节点 IP
virtual_router_id 51
priority 100 #设置最高级优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

k8s-master-222配置文件/etc/keepalived/keepalived.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP #高可用 从1
interface ens33 #网卡名称
mcast_src_ip 192.168.50.222 #该节点 IP
virtual_router_id 51
priority 50 #设置优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

k8s-master-222配置文件/etc/keepalived/keepalived.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP #高可用从2
interface ens33 #网卡名称
mcast_src_ip 192.168.50.223 #该节点 IP
virtual_router_id 51
priority 49 #设置优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH
}
virtual_ipaddress {
192.168.50.220 #vip地址
}
track_script {
chk_apiserver
}
}

健康检查脚本 /etc/keepalived/check_apiserver.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
err=0
for k in $(seq 1 3);do
check_code=$(pgrep haproxy)
if [[ $check_code == "" ]]; then
err=$(expr $err + 1)
sleep 1
continue
else
err=0
break
fi
done

if [[ $err != "0" ]]; then
echo "systemctl stop keepalived"
/usr/bin/systemctl stop keepalived
exit 1
else
exit 0
fi

给监测脚本添加执行权限

1
chmod +x /etc/keepalived/check_apiserver.sh

启动keepalive和haproxy

1
2
3
4
5
6
systemctl daemon-reload
# 启动并设置开机启动
# systemctl enable --now haproxy
systemctl start haproxy && systemctl enable haproxy
# systemctl enable --now keepalived
systemctl start keepalived && systemctl enbale keepalived

测试vip漂移

1
2
3
4
5
# 查看ip与vip
hostname -I

# 测试vip的16443端口是否通
nc -v 192.168.50.220 16443

初始化集群

拉取镜像

1
2
3
4
5
# 查看需要的镜像文件
kubeadm config images list

# 拉取镜像
kubeadm config images pull

master 节点初始化

1
2
3
4
5
# 导出默认初始化配置
kubeadm config print init-defaults > kubeadm-config.yaml

# token过期后生成信息token
kubeadm token create --print-join-command

master 节点加入集群

1
2
3
4
5
6
# master节点需要生成certificate-key
kubeadm init --control-plane-endpoint=192.168.50.220:16443

kubeadm join 192.168.50.220:16443 --token {token} \
--discovery-token-ca-cert-hash {} \
--control-plane --certificate-key {}

worker 节点加入集群

1
2
kubeadm join 192.168.50.220:16643 --token {token} \
--discovery-token-ca-cert-hash {}

从集群种移除节点

1
kubectl delete node {node-name}

配置环境变量,用于访问集群

1
2
3
4
5
cat << EOF >> ~/.bashrc
export KUBECONFIG=/etc/kubernetes/admin/conf
EOF

source ~/.bashrc

查看集群节点状态

1
2
3
4
5
6
# 查看节点状态
kubectl get nodes

# 查看系统组件
kubectl get all -n kube-system -o wide

安装网络组件(只在master-221节点操作)

Calico
Flannel

去除 master节点污点

如果你打算让Master节点也参与到平常的Pod调度(生产环境一般不会这样做,以保证master节点的稳定性),那么你需要使用以下命令将Master节点上的 taint(污点标记)解除

1
kubectl taint nodes --all node-role.kubernetes.io/master-

最后我们使用以下命令查看当前集群的状态,发现Scheduler和Controller Manager组件处理不健康状态:

1
kubectl get cs

解决上述问题需要将每个Master节点上的 /etc/kubernetes/manifests/kube-scheduler.yaml 和 /etc/kubernetes/manifests/kube-controller-manager.yaml 文件中的- –port=0注释掉,然后重启一下各Master节点上的kubelet即可.

测试集群

1
2
3
4
kubectl create deployment nginx --image nginx --replicas 2
kubectl expose deployment nginx --name nginx --type NodePort --port 80 --target-port 80 --node-port 8080

curl http://192.168.50.220:8080

参考
如何用 Kubeadm 在 Debian 11 上安装 Kubernetes 集群 | Linux 中国 - 知乎 (zhihu.com)
Kubernetes多主多从高可用集群部署 - 个人文章 - SegmentFault 思否
搭建多主节点k8s高可用集群(三主两从一VIP)_kubernetes部署多主多从集群-CSDN博客
github - 基于Ubuntu22.04部署KubeEdge-v1.18.0环境 - 云原生_KubeEdge - SegmentFault 思否

原文链接:Git应用详解第十讲:Git子库:submodule与subtree

一个中大型项目往往会依赖几个模块,git提供了子库的概念。可以将这些子模块存放在不同的仓库中,通过submodulesubtree实现仓库的嵌套。本讲为Git应用详解的倒数第二讲,胜利离我们不远了!

一、submodule

submodule:子模块的意思,表示将一个版本库作为子库引入到另一个版本库中:

1.引入子库

需要使用如下命令:

git submodule add 子库地址 保存目录

比如:

1
git submodule add git@github.com:AhuntSun/git_child.git mymodule

执行上述命令会将地址对应的远程仓库作为子库,保存到当前版本库的mymodule目录下:

随后查看当前版本库的状态:

可以发现新增了两个文件。查看其中的.gitmodules文件:

可以看到当前文件的路径和子模块的url,随后将这两个新增文件添加提交推送。在当前仓库git_parent对应的远程仓库中多出了两个文件:

其中mymodule文件夹上的3bd7f76 对应的是子仓库git_child中的最新提交

点击mymodule文件夹,会自动跳转到子仓库中:

通过上述分析,可以得出结论:两个仓库已经关联起来了,并且仓库git_child为仓库git_parent的子仓库;

2.同步子库变化

当被依赖的子版本库发生变化时:在子版本库git_child中新增文件world.txt并提交到远程仓库:

这个时候依赖它的父版本库git_parent要如何感知这一变化呢?

方法一

这个时候git_parent只需要进入存放子库git_child的目录mymodule,执行git pull就能将子版本库git_child的更新拉取到本地:

方法二

当父版本库git_parent依赖的多个子版本库都发生变化时,可以采用如下方法遍历更新所有子库:首先回到版本库主目录,执行以下指令:

1
git submodule foreach git pull

该命令会遍历当前版本库所依赖的所有子版本库,并将它们的更新拉取到父版本库git_parent

拉取完成后,查看状态,发现mymodule目录下文件发生了变化,所以需要执行一次添加、提交、推送操作:

3.复制父版本库

如果将使用了submodule添加依赖了子库的父版本库git_parent,克隆一份到本地的话。在克隆出来的新版本库git_parent2中,原父版本库存放依赖子库的目录虽在,但是内容不在:

进入根据git_parent复制出来的仓库git_parent2,会发现mymodule目录为空:

解决方法:可采用多条命令的分步操作,也可以通过参数将多步操作进行合并。

分步操作

这是在执行了clone操作后的额外操作,还需要做两件事:

  • 手动初始化submodule

    1
    git submodule init
  • 手动拉取依赖的子版本库;:

    1
    git submodule update --recursive

执行完两步操作后,子版本库中就有内容了。由此完成了git_parent的克隆;

合并操作

分步操作相对繁琐,还可以通过添加参数的方式,将多步操作进行合并。通过以下指令基于git_parent克隆一份git_parent3

1
git clone git@github.com:AhuntSun/git_parent.git git_parent3 --recursive

--recursive表示递归地克隆git_parent依赖的所有子版本库。

4.删除子版本库

git没有提供直接删除submodule子库的命令,但是我们可以通过其他指令的组合来达到这一目的,分为三步:

  • submodule从版本库中删除:

    1
    git rm --cache mymodule

git rm的作用为删除版本库中的文件,并将这一操作纳入暂存区;

  • submodule从工作区中删除;
  • 最后将.gitmodules目录删除;

完成三步操作后,再进行添加,提交,推送即可完成删除子库的操作:

二、subtree

1.简介

subtreesubmodule的作用是一样的,但是subtree出现得比submodule晚,它的出现是为了弥补submodule存在的问题:

  • 第一:submodule不能在父版本库中修改子版本库的代码,只能在子版本库中修改,是单向的;
  • 第二:submodule没有直接删除子版本库的功能;

subtree则可以实现双向数据修改。官方推荐使用subtree替代submodule

2.创建子库

首先创建两个版本库:git_subtree_parentgit_subtree_child然后在git_subtree_parent中执行git subtree会列出该指令的一些常见的参数:

3.建立关联

首先需要给git_subtree_parent添加一个子库git_subtree_child:

第一步:添加子库的远程地址:

1
git remote add subtree-origin git@github.com:AhuntSun/git_subtree_child.git

添加完成后,父版本库中就有两个远程地址了:

这里的subtree-origin就代表了远程仓库git_subtree_child的地址。

第二步:建立依赖关系:

1
2
git subtree add --prefix=subtree subtree-origin master --squash
//其中的--prefix=subtree可以写成:--p subtree 或 --prefix subtree

该命令表示将远程地址为subtree-origin的,子版本库上master分支的,文件克隆到subtree目录下;

注意:是在某一分支(如master)上将subtree-origin代表的远程仓库的某一分支(如master)作为子库拉取到subtree文件夹中。可切换到其他分支重复上述操作,也就是说子库的实质就是子分支。

--squash是可选参数,它的含义是合并,压缩的意思。

  • 如果不增加这个参数,则会把远程的子库中指定的分支(这里是master)中的提交一个一个地拉取到本地再去创建一个合并提交;
  • 如果增加了这个参数,会将远程子库指定分支上的多次提交合并压缩成一次提交再拉取到本地,这样拉取到本地的,远程子库中的,指定分支上的,历史提交记录就没有了。

拉取完成后,父版本库中会增添一个subtree目录,里面是子库的文件,相当于把依赖的子库代码拉取到了本地:

此时查看一下父版本库的提交历史:

会发现其中没有子库李四的提交信息,这是因为--squash参数将他的提交压缩为一次提交,并由父版本库张三进行合并和提交。所以父版本库多出了两次提交。

随后,我们在父版本库中进行一次推送:

结果远程仓库中多出了一个存放子版本库文件的subtree目录,并且完全脱离了版本库git_subtree_child,仅仅是属于父版本库git_subtree_parent的一个目录。而不像使用submodule那样,是一个点击就会自动跳转到依赖子库的指针

  • subtree的远程父版本库:
  • submodule的远程父版本库:

submodulesubtree子库的区别为:

4.同步子库变化

在子库中创建一个新文件world并推送到远程子库:

在父库中通过如下指令更新依赖的子库内容:

1
git subtree pull --prefix=subtree subtree-origin master --squash

此时查看一下提交历史:

发现没有子库李四的提交信息,这都是--squash的作用。子库的修改交由父库来提交。

5.参数--squash

该参数的作用为:防止子库指定分支上的提交历史污染父版本库。比如在子库的master分支上进行了三次提交分别为:abc,并推送到远程子库。

首先,复习一下合并分支时遵循的三方合并原则:

当提交46需要合并的时候,git会先寻找二者的公共父提交节点,如图中的2,然后在提交2的基础上进行246的三方合并,合并后得到提交7

父仓库执行pull操作时:如果添加参数--squash,就会把远程子库master分支上的这三次提交合并为一次新的提交abc;随后再与父仓库中子库的master分支进行合并,又产生一次提交X。整个pull的过程一共产生了五次提交,如下图所示:

存在的问题:

由于--squash指令的合并操作,会导致远程master分支上的合并提交abc与本地master分支上的最新提交2,找不到公共父节点,从而合并失败。同时push操作也会出现额外的问题。

最佳实践:要么全部操作都使用--squash指令,要么全部操作都不使用该参数,这样就不会出错。

错误示范:

为了验证,重新创建两个仓库AB,并通过subtreeB设置为A的子库。这次全程都没有使用参数--squash,重复上述操作:

  • 首先,修改子库文件;
  • 然后,通过下列指令,在不使用参数--squash的情况下,将远程子库A变化的文件拉取到本地:
1
git subtree pull --prefix=subtree subtree-origin master

此时查看提交历史:

可以看到子库儿子的提交信息污染了父版本库的提交信息,验证了上述的结论。

所以要么都使用该指令,要么都不使用才能避免错误;如果不需要子库的提交日志,推荐使用--squash指令。

补充:echo 'new line' >> test.txt:表示在test.txt文件末尾追加文本new line;如果是一个>表示替换掉test.txt内的全部内容。

6.修改子库

subtree的强大之处在于,它可以在父版本库中修改依赖的子版本库。以下为演示:

进入父版本库存放子库的subtree目录,修改子库文件child.txt,并推送到远程父仓库:

此时远程父版本库中存放子库文件的subtree目录发生了变化,但是独立的远程子库git_subtree_child并没有发生变化。

  • 修改独立的远程子库:

    可执行以下命令,同步地修改远程子版本库:

    1
    git subtree push --prefix=subtree subtree-origin master

    如下图所示,父库中的子库文件child.txt新增的child2内容,同步到了独立的远程子库中:

  • 修改独立的本地子库:

    回到本地子库git_subtree_child,将对应的远程子库进行的修改拉取到本地进行合并同步:

    由此无论是远程的还是本地的子库都被修改了。

实际上使用subtree后,在外部看起来父仓库和子仓库是一个整体的仓库。执行clone操作时,不会像submodule那样需要遍历子库来单独克隆。而是可以将整个父仓库和它所依赖的子库当做一个整体进行克隆。

存在的问题

父版本库拉取远程子库进行更新同步会出现的问题:

  • 子仓库第一次修改:

    经历了上述操作,本地子库与远程子库的文件达到了同步,其中文件child.txt的内容都是child~4。在此基础上本地子库为该文件添加child5~6

    然后推送到远程子库。

  • 父仓库第一次拉取:

    随后父版本库通过下述指令,拉取远程子库,与本地父仓库git_subtree_parent中的子库进行同步:

    1
    git subtree pull --p subtree subtree-origin master --squash

    结果出现了合并失败的情况:

    我们查看冲突产生的文件:

    发现父版本库中的子库与远程子库内容上并无冲突,但是却发生了冲突,这是为什么呢?

    探究冲突产生的原因之前我们先解决冲突,先删除多余的内容:

    随后执行git add命令和git commit命令标识解决了冲突:

    解决完冲突后将该文件推送到独立的远程子库,发现文件并没有发生更新,也就是说git认为我们并没有解决冲突:

  • 子仓库第二次修改与父仓库第二次拉取:

    再次修改本地子库的文件并推送到对应的远程仓库,父版本库再次将远程子库更新的文件拉取到本地进行同步:

    这次却成功了!为什么同样的操作,有的时候成功有的时候失败呢?

解决方案

原因出现在--squash指令中。实际上,--squash指令把子库中的提交信息合并了,导致父仓库在执行git pull操作时找不到公共的父节点,从而导致即使文件没有冲突的内容,也会出现合并冲突的情况。其实不使用--squash也会有这种问题,问题的根本原因仍然是三方合并时找不到公共父节点。我们打开gitk

从图中不难看出,当使用subtree时,子库与父库之间是没有公共节点的,所以时常会因为找不到公共节点而出现合并冲突的情况,此时只需要解决冲突,手动合并即可。

不使用subtree时,普通的版本库中的各分支总会有一个公共节点:

再次强调:使用--squash指令时一定要小心,要么都使用它,要么都不使用。

7.抽离子库

git subtree split

当开发过程中出现某些子库完全可以复用到其他项目中时,我们希望将它独立出来。

  • 方法一:可以手动将文件拷贝出来。缺点是,这样会丢失关于该子库的提交记录;

  • 方法二:

    使用

    1
    git subtree split

    指令,该指令会把关于独立出来的子库的每次提交都记录起来。但是,这样存在弊端:

    • 比如该独立子库为company.util,当一次提交同时修改了company.utilcompany.server两个子库时。
    • 通过上述命令独立出来的子库util只会记录对自身修改的提交,而不会记录对company.server的修改,这样在别人看来这次提交就只修改了util,这是不完整的。