「CONTAINERD」- 服务部署

on Ubuntu 24.04 TLS with APT
# 04/09/2025
https://github.com/containerd/containerd/blob/main/docs/getting-started.md ⇒ https://docs.docker.com/engine/install/ubuntu/
安装过程大致如下,细节请参考官方文档:

# Add Docker’s official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
“deb [arch=$(dpkg –print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo “${UBUNTU_CODENAME:-$VERSION_CODENAME}”) stable” | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install containerd.io

sudo apt-mark hold containerd.io

containerd.io 1.5.11-1 (from Docker)
注意,该方式是通过 Docker 官方仓库提供的 contanerd.io 包进行部署,注意与 containerd 包进行区别。
安装 containerd 服务:

# 我们使用 Docker 仓库里的 containerd 程序

apt-get update \
&& apt-get install -y apt-transport-https ca-certificates curl[……]

READ MORE

「CONTAINER-RUNTIME」- containerd

认识
containerd 是一个开源的容器运行时管理工具,是 Docker 的核心组件之一,最初由 Docker 公司开发。
官网:https://containerd.io/
文档:https://containerd.io/docs/
仓库:https://github.com/containerd/containerd/
组成
它提供了一个标准化的接口,可以与各种容器运行时(如 runc、cri-o 等)进行交互,从而管理容器的生命周期、镜像管理、文件系统管理等操作。
性质
与 Docker 不同的是,Containerd 更加轻量级、灵活,可以与各种容器编排工具(如 Kubernetes)集成使用。
应用
containerd 的目标是成为一个通用的容器运行时管理工具,为容器生态系统提供一个标准的接口,促进容器技术的发展和普及;[……]

READ MORE

「MICROSERVICES」- 微服务

认识
微服务是一种软件架构风格,其中应用程序被构建为一组小型服务,每个服务都运行在独立的进程中,可以通过轻量级机制(通常是 HTTP API)进行通信。每个服务都专注于执行一个单独的业务功能或一组相关的业务功能,并且可以独立地进行部署、扩展和维护。
与传统的单体应用程序相比,微服务架构提供了更高的可伸缩性、更好的灵活性、更高的可靠性。由于每个服务都是独立的,因此可以独立地进行部署,这使得开发人员可以更快地进行迭代和部署新功能。此外,由于每个服务都是独立的,因此可以更容易地进行故障排除和维护。
微服务通过拆分和解耦提升系统灵活性,但需要成熟的 DevOps 和分布式系统管理能力。选择时需权衡业务规模、团队能力和运维成本,避免“为微服务而微服务”。对于初创项目,可从单体开始,随业务增长逐步拆分。
组成
服务拆分
将单体应用按业务功能拆分为多个独立服务(如用户服务、订单服务、支付服务)。
每个服务专注于单一职责,可独立开发、部署和扩展。
去中心化
数据管理分散:每个服务拥有自己的数据库(如 MySQL、MongoDB 等),避免共享数据库。
技术多样性:不同服务可用不同编程语言或框架实现(如 Spring Boot、Go、Node.js)。
轻量级通信
服务间通过 API(REST/gRPC)或异步消息(Kafka/RabbitMQ)交互。
通常采用 JSON/Protobuf 等通用数据格式。
独立部署
每个服务可单独部署和扩展,无需重启整个系统。
服务发现 | Service Discovery
服务发现的基本原理
服务发现有三个角色:「服务提供者」、「服务消费者」、「服务中介」。「服务中介」是联系「服务提供者」和「服务消费者」的桥梁。「服务提供者」将自己提供的服务地址注册到「服务中介」,「服务消费者」从「服务中介」那里查找自己想要的服务的地址,然后享受这个服务。「服务中介」提供多个服务,每个服务对应多个「服务提供者」。
注册中心 | Register Center
eureka
=
配置中心 | Configuration Center
WIP
构建
—— 该部分将介绍用于构建微服务架构的典型技术栈、常用组件、工具,以及相关问题的解决办法。
微服务架构典型技术栈有:

开发框架:例如,Spring Cloud、Micronaut、Quarkus(Java)、Go Micro 等等,不同的开发语言具有不同的生态系统。
服务通信:同步调用的 RESTful API、gRPC、Apache Thrift 等等,异步消息的 Kafka RabbitMQ RocketMQ 等等。
服务注册与发现:例如 Eureka、Nacos、Consul、Zookeeper 等等;
配[……]

READ MORE

「Kubernetes」- 将单 Master 升级为多 Master 集群(Single Master to Multiple Master)

早期,我们学艺不精,在开发环境部署 Single Master Kubernetes Cluster 用于日常研发环境。但随着业务扩展,工作负载的增多,此时运行在开发环境的测试服务越来越多(除了业务应用程序,还包括很多基础服务)。
如果 Master Node 故障,比如磁盘损坏,导致其无法恢复,那时我们将只能重建开发环境。各种配置文件、各种资源的恢复,各种服务的重建和部署,将是一场灾难。
现在,我们希望将开发环境的 Single Master Kubernetes Cluster 升级为 Multiple Master Kubernetes Cluster 环境,试图将开发环境转为高可用集群,以防止 Master 故障后集群处于不可用状态。
该笔记将记录:如何将单 Master 集群升级为多 Master 集群,即将集群转换为高可用集群。
我们参考 如何将单 master 升级为多 master 集群 文章(backup-article-1697039.png),并结合我们的实际情况进行调整。
需求概述

Single Master Kubernetes Cluster | Multiple Master Kubernetes Cluster
————————————————————————–
192.168.10.70 k8s70-cp00 | 192.168.10.70 k8s70 vip
|
| 192.168.10.71 k8s70-cp01
| 192.168.10.72 k8s70-cp02
| 192.168.10.73 k8s70-cp03
|
192.168.10.74 k8s70-wn04 | 192.168.10.74 k8s70-wn04
192.168.10.75 k8s70-wn04 | 192.168.10.75 k8s70-wn04

我们希望:
1)将原始的 Master IP Address 作为 VIP 使用,实现无需调整 Worker 节点及其他引用该集群的服务;
2)并再向集群中添加三个 Master 节点;
3)我们使用 kube-vip 来提[……]

READ MORE

「HOMELAB」- Kubernetes Cluster

问题描述
补充说明:
1)NSC,泛指 网络、存储、计算,或网络环境、存储环境、网络环境
部分服务作为 网络环境、存储环境、计算环境 的依赖,例如 监控、日志、告警、认证、DNS 等等,需要独立运行。
原因是,该依赖服务的运行不能受到 NSC 的影响,当 NSC 出现故障时,不能影响依赖服务的运行。再比如,如果监控系统运行在 C 内,如果 C 出现故障,那么监控也随着故障,进而会影响告警使得我们无法获知问题。
解决方案
所以,我们需要在 DC-HIVE 中,部署独立的监控集群,作为监管服务来检测整个 Homelab 环境
用途说明:部署于裸机之上,其内运行管理平台,为内网其他应用提供基础服务,;
硬件信息
Master:迷你主机
Intel 赛扬 N2805参数/频率/功耗/温度/内存/PICE
迷你小主机 n2805 小电脑可装 win7win10、达菲、旁路由、linux 服务器、homeassistant 智能家居系统。仅剩白色!!!
内存硬盘都可以升级,千兆有线网卡,无线 wifi,尺寸 13*13*3cm,4.5w 低功耗 cpu,1 个 usb3.0,2 个 usb2.0 接口。整机功耗 7w 左右;
Intel 赛扬 N2805,CPU 主频 1.46 GHz;核心数量 2 核看跑分修改;线程数量 2 线程;单核睿频 1.46 GHz 单核评测;全核睿频 1.46 GHz 性能评测
成本:167
Worker:ThinkPad T540P
WIP
Worker:ASUS k53sd
WIP
Worker:System76
WIP[……]

READ MORE

「NFS」- 服务搭建(Ubuntu、CentOS、Debian)

该笔记将记录:在 Linux 中,如何快速部署 NFS 服务,并配置访问,以及常见问题的解决办法;
环境信息
系统环境:Debian,Ubuntu,CentOS
ClearOS release 7.5.0 (Final) | nfs-utils 1.3.0
CentOS release 6.5 (Final) | nfs-utils-1.2.3-75.el6_9.x86_64 | nfs-utils-lib-1.1.5-13.el6.x86_64
第一步、部署服务
执行如下命令,以安装 NFS 服务端:

# ——————————————————— # on Ubuntu 18.04 LTS

apt install -y nfs-kernel-server

systemctl enable nfs-kernel-server.service
systemctl start nfs-kernel-server.service
systemctl status nfs-kernel-server.service

# ——————————————————— # on CentOS 6.5

yum install -y nfs-utils nfs-utils-lib

chkconfig nfs on
chkconfig rpcbind on
service rpcbind start
service nfs start

# ——————————————————— # on ClearOS 7.5

yum install -y nfs-utils nfs-utils-lib

systemctl start nfs-server.service
systemctl enable nfs-server.service

# ——————————————————— # on Kylin V10

yum install -y nfs-utils
systemctl start nfs-server.service
systemctl enable nfs-server.service

第二步、导出文件系统
添加共享目录,如下示例:

# ——————————————————— // 导出目录

# 建议使用系统提供的“配置文件引入”方式,而非直[……]

READ MORE

「Groovy」- 对象 | Object

Q:获取对象类型?
A:someObject.getClass()
R:Groovy / grails how to determine a data type? – Stack Overflow
将对象保存到文件,以及读取文件中对象
java – Groovy serialization without class definition – Stack Overflow[GROOVY-1627] Deserialization fails to work – ASF JIRAjava – Groovy serialization without class definition – Stack Overflow
我们尝试像 Java 那样,将「对象保存到文件中,并从文件中恢复」,但是产生这会产生异常:

Caught: java.lang.ClassNotFoundException: org.openqa.selenium.Cookie
java.lang.ClassNotFoundException: org.openqa.selenium.Cookie
at java_io_ObjectInput$readObject.call(Unknown Source)
at org.d3rm.toolbox.wordpress.CsdnSeleniumCommand.cookieReadFromFile(CsdnSeleniumCommand.groovy:238)
at org.d3rm.toolbox.wordpress.CsdnSeleniumCommand.actionPublishPost(CsdnSeleniumCommand.groovy:112)
at org.d3rm.toolbox.wordpress.CsdnSeleniumCommand$actionPublishPost$0.call(Unknown Source)
at org.d3rm.toolbox.wordpress.CsdnSeleniumCommand.main(CsdnSeleniumCommand.groovy:61)

我们场景是在 Jenkins Pipeline 中使用 Groovy 语言调用 Selenium Java 绑定。按照「对象保存到文件中,并从文件中恢复」方法将 Cookie 对象保存到文件,然后下次再将 Cookie 对象恢复,以保存登录状态。但是会出现上述错误;
问题原因
问题的原因和解决在 [GROOVY-1627] Deserialization fails to work – ASF JIRA 中已经给出

after[……]

READ MORE

「Groovy」- Closure

Closure – 闭包
在 Java 中的 Lambda 和 Groovy 的 Closure 非常相似,但是底层的实现机制是非常不同的。
简单示例
对于一个「闭包」的简单使用如下:

// 声明一个闭包
Closure listener = { e -> println “Clicked on $e” }

// 两种调用方式
listener(“str”)
listener.call(“str”)

调用方式两种:以函数的方式调用;或者调用 call()方法。
作为参数传递
「闭包」也可以作为方法的参数传入。类似于“传递函数的调用”。看下面的示例:

class DelegateDemo {

static void main(String[] args) {
Closure c = {
test() // 调用代理对象的方法
}
c.delegate = new DelegateDemo() // 设置代理对象。在 Closure 中有一个属性为 delegate,该属性表示我们可以为闭包设置一个代理对象
c.call() // 运行闭包。即,调用代理对象的 test()方法
}

void test() {
println(“this is delegate”)
}

}

# 利用 Groovy 模仿 Gradle 中的 dependencies 依赖声明

// // 该类同时将作为一个闭包的代理对象。
class Dependency {

Set<String> api = new HashSet<>(); // 保存所有的依赖
// 这就是一个普通的方法
void api(String text){
api.add(text)
}
// 这就是一个普通的方法
void exec(){
println(api)
}
}

// // 声明一个方法,该方法的参数为一个闭包。
static void dependencies(Closure closure) {
println “Start Dependencies……”
def dependency = new Dependency() // 声明一个代理对象
closure.delegate = dependency // 设置委托
closure.call() // 运行闭包
dependency.exec() // 执行
}

// // 使用
static void main(Stri[……]

READ MORE

「NFS」- 网络文件系统 | Network File System

认识
通过 NFS 服务,允许系统通过网络与他人共享目录和文件,使得用户和程序可以像访问本地文件一样访问远程系统上的文件。虽然 NFS 远不及许多高可用文件系统(GlusterFS,CephFS,MinIO),但是我们认为在中小规模的环境中 NFS 还是有用武之地的,所以我们依旧会探索 NFS 服务相关的内容;
NFS(Network File System)是一种用于在计算机网络中共享文件的协议。它允许在不同的计算机上通过网络访问和共享文件和目录。NFS 服务是在服务器上运行的软件,用于提供 NFS 协议的支持;
组成
NFS 服务的工作原理是:

Server 上的 NFS 软件将文件和目录标记为共享,并允许远程计算机通过网络连接进行访问。
在远程计算机上,NFS Client 软件可以通过指定服务器的 IP Adress 和共享的路径来访问这些文件和目录。* 一旦连接建立,远程计算机就可以像访问本地文件一样访问和操作共享的文件和目录;

性质
通过 NFS 服务,能够获得最显着的好处有:
1)本地工作站使用较少的磁盘空间,因为常用数据可以存储在单台计算机上,并且仍然可以通过网络访问其他人;
2)用户不需要在每台网络机器上都有独立的主目录。主目录可以在 NFS 服务器上设置并在整个网络中可用;
3)存储设备(如软盘,CDROM 驱动器和 USB 拇指驱动器)可供网络上的其他计算机使用。这可能会减少整个网络中可移动媒体驱动器的数量;
应用
总而言之,NFS 服务是一种用于在计算机网络中共享文件和目录的协议,它提供了方便的方法来访问远程服务器上的文件,使用户能够像访问本地文件一样进行操作;
使用 NFS 服务,用户可以将文件和目录在不同的计算机之间共享,类似于将文件存储在本地磁盘上一样;
通过 NFS,用户可以在本地计算机上访问远程服务器上的文件,就像它们存储在本地一样。这种共享方式对于组织中需要共享文件的团队和用户非常有用;[……]

READ MORE

「ELASTICSEARCH」- 搜索引擎 | Search and Analytics

认识
ElasticSearch 是一个基于 Lucene 的搜索服务。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful web 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便;
官网
https://www.elastic.co/elasticsearch
文档
官方手册:Elasticsearch Reference
中文手册:(「Elasticsearch 权威指南(中文版)」)(「Elasticsearch: 权威指南」2.x)
《Elasticsearch: The Definitive Guide》
《Elasticsearch 实战》
《Elasticsearch 搜索引擎开发实战》
《深入理解 Elasticsearch》 作者:(美)拉斐尔·酷奇(Rafa Kuc),(美)马雷克·罗戈任斯基(Marek Rogozinski) 著;张世武,余洪淼,商旦译著作
《相关性搜索(利用 Solr 与 Elasticsearch 创建智能应用)》
《Elasticsearch 源码解析与优化实战》
《Elasticsearch 集成 Hadoop 最佳实践》
各大论坛、社区、Stackoverflow、Stackexchange、…
仓库
https://github.com/elastic/elasticsearch
组成
Elasticsearch 是一个基于 Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库;
但是,Lucene 只是一个库。想要使用它,你必须使用 Java 来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene 非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的;
构建
WIP
性质
Elasticsearch 也使用 Java 开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单;
不过,Elasticsearch 不仅仅是 Lucene 和全文搜索,我们还能这样去描述它:

分布式的实时文件存储,每个字段都被索引并可被搜索
分布式的实时分析搜索引擎
可以扩展到上百台服务器,处理 PB 级结构化或非结构化数据

而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的 RESTful API、各种语言的客户端甚至命令行与之[……]

READ MORE

「APACHE-GROOVY」- 数组 | Array

增 | 创建数组
Array of strings in groovy – Stack Overflow
定义数组:

String[] names = [“lucas”, “Fred”, “Mary”]

def names = [“lucas”, “Fred”, “Mary”].toArray()[……]

READ MORE

「KUBERNETES」- 集群创建与管理 | 集群部署

该笔记将记录:多个 Kubernetes 集群的创建及管理方案,以及相关问题的解决办法;
版本选择
https://kubernetes.io/releases/

版本选择,取决于业务需求。如果无特殊要求,则我们将部署最新版本。
如果生产环境使用云服务,则我们部署云服务能够支持的集群版本。
如果业务生产环境仅支持特定版本的 Kubernetes 集群,则我们部署对于版本(或,最接近生产环境的版本)。

场景 | 边缘计算 | 开发学习
在实际的实践中,我们需要部署和管理多个 Kubernetes 集群,以满足多种业务需求;
Cluster API
A Kubernetes sub-project focused on providing declarative APIs and tooling to simplify provisioning, upgrading, and operating multiple Kubernetes clusters.
官网:https://cluster-api.sigs.k8s.io/
文档:https://cluster-api.sigs.k8s.io/
仓库:https://github.com/kubernetes-sigs/cluster-api
minikube
minikube:快速创建本地集群,聚焦于应用开发及新用户;
用于快速创建学习和测试使用的本地 Kubernetes 集群;
kind
多用于 Local Development 与 CI 流程;使用 Docker Container 作为节点,来运行 Kubernetes 集群;用于测试 Kubernetes 自身、本地测试、持续集成;
官网:https://kind.sigs.k8s.io/
Microk8s
Zero-ops, pure-upstream Kubernetes, from developer workstations to production. 根据官方描述,其能够用于生产环境;
KubeOne
https://docs.kubermatic.com/kubeone/v1.8/
K3s
轻量级 Kubernetes 集群;
官网:https://k3s.io/
文档:https://docs.k3s.io/
仓库:https://github.com/k3s-io/k3s/
k3d (k3s in Docker)
特点:轻量级 k3s 集群(Rancher 开发),适合资源有限的环境。
场景:边缘计算、本地开发。
Turnkey (On-Premises Solutions)
允许你使用少量命令,在内部安全的云网络上创建 Kub[……]

READ MORE

「Kubernetes」- 使用服务(Service)

(1)集群内的POD通讯
(2)应用程序如何互相发现对方
(3)如何公开POD,以便从集群外访问
Service为Pod提供VirtualIP。即使移除Pod或者添加Pod,Service也会保证客户端能够访问Pod中运行的容器。
VirtualIP的作用是为了将访问发送到一个或多个Pod。
kube-proxy负责VIP和Pod之间的映射,每个节点都有该进程。kube-proxy查询apiserver以获知集群中的新服务,并更新iptables规则。
5.1. Creating a Service to Expose Your Application
创建部署:kubectl run nginx –image nginx
创建服务:kubectl expose depoly/nginx –port 80
查看服务:kubectl describe svc/nginx
如果想访问该服务,可以在另外一个终端运行代理:

# kubectl proxy

Starting to server on 127.0.0.1:8001

然后浏览器反问,http://localhsot:8001/api/v1/proxy/namespaces/default/services/nginx/
或者可以使用YAML文件来创建服务:

kind: Service
apiVersion: v1
metadata:
name: nginx
spec:
selector:
run: nginx
ports:
– port: 80

通过selector来选择匹配条件(与Pod的标签相同)的Pod。并且,kubernetes根据Service来配置所有节点的防火墙,从而使节点能够访问构成该服务的容器。
!!!如果服务运行出现问题,可以检查selector中的标签,并确认kubectl get endpoints可以返回一组访问点。如果不能,则很可能是selector没有找到可以匹配的Pod。
!!!Pod的监控程序(部署、副本控制器等等)可以直接操作服务。监控程序和服务可以通过标签找到Pod,但是他们的只能不同:监控程序负责管理Pod的健康并中期Pod,而服务则负责提供访问渠道。
5.2. Verifying the DNS Entry of a Service
默认的服务类性是ClusterIP,并通过集群内部IP公布服务。如果DNS集群插件正常工作,可以通过$SERVICENAME.$NAMESPACE.svc.cluster.local的形式访问服务。
可以使用busybox来验证:

# kubectl run busybox –image busybox -it[……]

READ MORE

「Groovy」- 控制结构 | Loop | 循环 | If | Switch | 条件 | 循环

if
println false ? 1 : 2 // 2
println false ?: 2 // 2
println true ?: 2 // true

dictionary – groovy: safely find a key in a map and return its value – Stack Overflow

def mymap = [name:”Gromit”, id:1234]
def x = mymap.find{ it.key == “likes” }?.value
if(x)
println “x value: ${x}”

println x.getClass().name

?. checks for null and does not create an exception in Groovy. If the key does not exist, the result will be a org.codehaus.groovy.runtime.NullObject.
Switch

// 将 Gitee 的团队名转化到对应的 k8s 的命名空间
// 因为团队名不符合规范,所以加个转换层,映射到 k8s 命名空间
def giteeTeamToK8sNamespace(String giteeTeamName) {
def returnVal = ‘default’

switch (giteeTeamName) {
case ‘KaiFa’:
returnVal = ‘df-common’
break
case ‘YunWei’:
returnVal = ‘df-om’
break
}

return returnVal
}

循环 | Loop
使用 each 方法

listFoo.each { item ->
// do some stuff
}

使用 find 方法
Can you break from a Groovy “each” closure?
当使用 find 遍历时,在 Cloure 中返回 true 将停止遍历:

def a = [1, 2, 3, 4, 5, 6, 7]

a.find {
if (it > 5)
return true // break
println it // do the stuff that you wanted to before break
return false // keep loop[……]

READ MORE

「BYPASS-ROUTER」- 旁路由

认识
旁路由(也称为辅助路由或次级路由)是网络架构中的一种部署方式,通常与主路由协同工作,用于分担特定功能或优化网络流量。
注意,旁路由的常见英文翻译包括 Bypass Router / Secondary Router / Auxiliary Router / Side Router / Helper Router 等等。在专业网络架构中,可能不会直接使用上述词汇,而是通过功能描述 Policy-based Routing Node(基于策略的路由节点)、Transparent Proxy Gateway(透明代理网关)等等。
组成
旁路由,是种角色,其仍旧为一台路由器,仅是在网络中起到辅助作用的。
网络拓扑:旁路由通常与主路由处于同一局域网(LAN),设备可手动选择通过旁路由转发流量,而其他设备仍走主路由。
构造
硬件选择:低功耗设备,例如,树莓派、软路由、旧电脑、……。
系统配置:

安装 OpenWrt、iStoreOS 等系统。
关闭旁路由的 DHCP 功能,由主路由分配 IP。
手动指定部分设备的 Gateway / DNS Server 为旁路由 IP。

透明网关模式:通过防火墙规则或策略路由,强制流量经旁路由处理。
性质
角色定位:旁路由不直接作为默认网关,而是处理主路由分流的部分任务(如科学上网、广告过滤、流量监控等)。
应用
功能扩展:主路由性能有限时,旁路由承担复杂任务(如 VPN、Docker 服务)。
流量管控:实现分设备 / 分应用的科学上网(如仅手机走旁路由代理)。
安全与过滤:部署广告拦截(AdGuard Home)、防火墙(OpenWrt 的流量规则)。
实验环境:在不影响主网络的情况下测试新功能(如 IPv6、QoS)。
# 04/11/2025 在家庭网络中,我们的出口路由器使用 pfSense 系统,但是该路由器并不具有无线模块,所以无法提供无线网络。为了能够接入无线终端(手机、笔记本、……),我们在该路由器下接入 TP Link 路由器作为旁路由,两台路由器 LAN 口互联,并配置同网段 IP 地址。该旁路由仅提供无线接入功能。
参考
DeepSeek / 介绍旁路由[……]

READ MORE

「NGINX Ingress Controller」- Nginx Rewrite to Root

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
name: rewrite
namespace: default
spec:
rules:
– host: rewrite.bar.com
http:
paths:
– backend:
serviceName: http-svc
servicePort: 80
path: /something/(.*)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
– http:
paths:
– pathType: Prefix
path: /api(/|$)(.*)
backend:

参考文献
kubernetes – Is there a best practice to implement ingress rule with rewrite to root – Stack OverflowRemoving url prefixes in nginx Kubernetes Ingress – Graphics Unplugged[……]

READ MORE

「Groovy」- XML

问题描述
在知晓 Jsoup 之前,我们使用本文 XMLParser 及 XMLSlurper 库操作 HTML 文本,本笔记将介绍这两个类库使用方法。
注意事项
如果可能,尽量不要使用 XML 类库操作 HTML 文本。
使用方法
处理 XML 文本
有关XML处理,可以参考「Apache Groovy/Processing XML」文档。
处理 HTML 文本
在处理HTML文本时,最主要的问题是HTML不时标准XML文档,“它是格式错误的XML文档”:

////////////////////////////////////////////////////////////////////////////////
// 方法一
// https://stackoverflow.com/questions/28110181/how-to-parse-non-well-formatted-html-with-xmlslurper
////////////////////////////////////////////////////////////////////////////////
@Grab(group=’net.sourceforge.nekohtml’, module=’nekohtml’, version=’1.9.14′)
import org.cyberneko.html.parsers.SAXParser
import groovy.util.XmlSlurper

def parser = new SAXParser()
def page = new XmlSlurper(parser).parseText(‘some html string’)

////////////////////////////////////////////////////////////////////////////////
// 方法一
// https://stackoverflow.com/questions/30753884/how-to-work-around-groovys-xmlslurper-refusing-to-parse-html-due-to-doctype-and
////////////////////////////////////////////////////////////////////////////////
@Grab(‘org.ccil.cowan.tagsoup:tagsoup:1.2.1’)
import org.ccil.cowan.tagsoup.Parser

def parser = new Parser()
def page = new XmlSlurper(parser)[……]

READ MORE

「KUBERNETES-INGRESS-CONTROLLER」- 部署 Ingress Controller 组件 | KUBERNETES-INGRESS-CONTROLLER

认识
在 Kubernetes 中,Ingress Controller 负责处理 Ingress 资源以实现服务的暴露。Ingress Controller 是 Kubernetes 中管理外部访问集群内服务的关键组件,它实现了 Ingress API 对象的功能。Ingress Controller 负责处理来自集群外部的 HTTP 和 HTTPS 路由请求,根据规则将流量转发到相应的服务。
组成
仅当在 Kubernetes Cluster 中部署 Ingress Controller 之后,我们定义的 Ingress 资源才能生效。当然 Ingress Controller 有很多实现,比如 NGINX Ingress Controller、Traefik Ingress Controller、HAProxy Ingress 等等(参考 Ingress Controllers | Kubernetes 页面)。
该部分笔记将记录:在 Kubernetes Cluster 中,如何部署 Ingress Controller 组件,以及常见问题处理。
基本工作原理

管理员创建 Ingress 资源定义路由规则
Ingress Controller 监控这些资源变化
Ingress Controller 根据规则配置底层负载均衡器
外部请求到达时,根据规则路由到对应服务

Ingress, Ingress Controllers, Nginx Ingress Controller
在 Kubernetes 中,所有的”资源定义“(YAML)只是仅是”声明“,而真正应用这些”声明“的是 Controller 组件,它才是真正工作的组件。
假如我们定义如下 Ingress 资源,来接受 example.com 的 HTTP 请求,将其转发到 nginx-default 服务(Service):

# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-default
spec:
rules:
– host: example.com
http:
paths:
– backend:
serviceName: nginx-default
servicePort: 80

很明显我们这里仅是定义配置,那么在集群里中必须有组件来应用这些配置(否则没有任何意义)。这就是在集群中运行的 Ingress Controller 组件的任务,Ingress Controller 读取 Ingress 定义,并保证这些配置得到执行。[……]

READ MORE

「COMPUTER-NETWORKING」- 流量工程

认识
传统 IP 网络
传统的 IP 网络中,节点不考虑带宽等因素,选择最短的路径作为最优路由。这样,容易出现流量集中于最短路径而导致拥塞,其他可选的路径则较为空闲;

解决方案

网络工程,其指通过设计网络来满足流量需求的过程。其实质是通过调研了解到网络流量的需求,然后根据实际的需求设计和部署网络的一个过程,其实就是一个新建网络的过程。简而言之,其实质是按照流量的需求来规划、设计、部署网络的一个过程。
流量工程,其指网络节点可以根据网络中的可用资源建立数据转发路径,并具有为关键流量预留网络带宽的能力。通过动态监控网络的流量和设备的负载,实时调整流量管理参数、路由参数和资源约束参数等,优化网络资源的使用,避免负载不均导致的拥塞;
流量工程是现有网络已经存在,通过对流量的合理规划,实现资源的优化配置和提升网络的性能;
如果将网络工程比作道路新建、改扩建的话,流量工程就是缓堵保畅;
组成
流量工程(TE,Traffic Engineering)是最重要的网络业务之一。历史上流行的TE技术是基于MPLS的,被称为MPLS TE。它可以精确的控制流量流经的路径,从而充分的利用现有带宽资源。
部署流量工程后网络
流量工程通过建立基于一定约束条件的转发路径,当流量到达路径首节点时,根据流量特征使特定的流量按照指定的路径进行传输;
使用流量工程,网络管理员可以通过部署不同的转发路径来合理分配网络资源,避免网络拥塞;

性质

路径规划:不同的业务流量规划不同转发路径;
流量调优:当重大事件、新闻等导致流量不均衡时,将一部分流量分配到空闲的链路上,使网络中流量的分配更合理;
故障保护:设备或链路故障后的快速切换保护;
应用
MPLS TE[……]

READ MORE

「Groovy」- 快速开始及简单教程

变量:基本语法
How do I create and access the global variables in Groovy? – Stack Overflowmonkeypatching – Dynamically add a property or method to an object in groovy – Stack Overflow

def var3 = ‘var3’

为类添加新的属性(元编程):

g.metaClass.bye = { println “Goodbye, $name” }
g.bye()

// 注意,通过元编程注入的属性是没有 Get Set 方法的;

代码执行:

#!/bin/sh

# 执行脚本:#
groovy groovy-quick-start/function.groovy

# 打印日志。并同时指定日志等级和依赖解析信息
groovy -Divy.message.logger.level=4 -Dgroovy.grape.report.downloads=true \
groovy-quick-start/function.groovy

=
判断变量是否为空(grails – Checking if a collection is null or empty in Groovy – Stack Overflow):

if (members) {
//Some work
}

函数定义:
定义函数

def hello-groovy() {
println “Hello Groovy!”
}

在脚本中,引用全局变量:

// 方法一、使用 @Field 注解
import groovy.transform.Field
@Field def foo = 1234

// 方法二、不使用 def 关键字
gVar = 456

def printGlobalVar() {
println foo
println gVar
}

函数及返回值
修饰符:方法的默认访问「修饰符」是 public
返回类型:方法的「返回类型」可以不需要声明,但需添加 def 关键字;
参数类型:方法的「参数类型」可以被省略,默认为 Object 类型;
返回值:在有返回值的方法中 return 可以被省略,默认返回最后一行代码的运行结果,如果使用了 return 关键字则返回指定的返回值;
隐式的 Getter\Setter 方法
Groovy 默认会隐式的创建 getter、setter 方法,并且会提供带参的构造器;
多重方法
在 Groovy 中,调用的方法将在运行时被选择。这种机制[……]

READ MORE

「Microserivce」- Apollo | 阿波罗 | A reliable configuration management system

认识
Apollo | A reliable configuration management system
官网:https://www.apolloconfig.com/#/
文档:https://www.apolloconfig.com/#/zh/README
仓库:https://github.com/apolloconfig/apollo[……]

READ MORE

「OPNSense」- 开源免费的企业级路由器操作系统

认识
2014 年从 pfSense 分叉而来,因开发者对 pfSense 的闭源趋势不满而创立。由 Deciso BV(荷兰公司)支持,坚持开源(基于 BSD 许可证),社区驱动更强。更注重现代化界面和频繁更新。
组成
1)底层系统:基于 HardenedBSD 系统;
2)应用规模:能够支持大型网络环境;
3)功能特性:能够完成企业组网,支持 VPN、Routing Protocol、NAT、VRRP、SD-WAN、Firewall、Multi-WAN 等等技术;
4)协议许可:BSD 2-Clause “Simplified” License,Commercial use(GitHub/opnsense/core/LICENSE at master)
构建
on VirtualBox
How To Install OPNsense On Virtualbox?
镜像下载:
1)https://opnsense.org/download/%EF%BC%88ISO%EF%BC%89
安装过程:
1)创建 ⇒ 常规虚拟机创建,FreeBSD x64,这里不再赘述;
2)网络 ⇒ 创建网卡:Adapter 1: Internal Network;Adpater 2: Bridge Interface;
3)启动 ⇒ 虚拟机启动,并以 installer / opnsense 登录,以进行系统安装;
4)安装 ⇒ 系统安装,设置密码,这里不再赘述;
访问配置:
1)通过 LAN 接口:https://192.168.1.1%EF%BC%88root/<your-password>)
2)通过 System / Wizard 开始基础设置;
修复 Guru Meditation 错误:

# VritualBox: 5.2.44
# OPNSense: OPNsense-21.7.1-OpenSSL-dvd-amd64.iso
# https://www.youtube.com/watch?v=5Nz8FBcZ3jY

问题描述:当虚拟机启动时,提示 Guru Meditation 错误。

解决方案:vboxmanage modifyvm “<your-vm-name>” –spec-ctrl on

修复系统启动卡住问题:

# VritualBox: 5.2.44
# OPNSense: OPNsense-22.1-OpenSSL-dvd-amd64.iso
# https://exchangetimes.net/?p=559
# https://www.youtube.com/watch?v=5Nz8FBcZ3jY

问题描述:当启动[……]

READ MORE

「Selenium」- 保持登录状态(保存 Cookie 信息)

解决方案
我们没有找到保存 Cookie 的专有方法,所以我们采用自己的方案:
1)将 Cookie 对象保存到文件,
2)在启动时,再载入 Cookie 对象
适用于 Java 语言的方法
将 Cookie 保存到文件:

private void cookieWriteToFile(WebDriver webDriver) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(“/path/to/cookie.bin”));
objectOutputStream.writeObject(webDriver.manage().getCookies());
}

从文件中读取 Cookie:

private void cookieWriteToFile(WebDriver webDriver) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(“/path/to/cookie.bin”));
Set<Cookie> cookies = (Set<Cookie>) objectInputStream.readObject();
for (Cookie cookie : cookies) {
webDriver.manage().addCookie(cookie);
}
}

该方法的本质是:保存二进制对象到文件,之后从文件恢复二进制对象
注意事项,该方法适用于 Java 语言,而 Groovy 语言存在其他问题。
适用于 Groovy 语言的方法
参考 Object 笔记。
参考文献
Selenium Grid Tutorial: Hub & Node (with Example)Selenium Java / API / Overview[……]

READ MORE

「SOFTWARE-ENGINEERING」- 搜索引擎软件

开源搜索引擎实现
1)Apache Solr:基于 Apache Lucene 的全文搜索引擎,支持分布式搜索和索引;
2)Elasticsearch:基于 Lucene 的分布式搜索引擎,支持实时搜索和分析;
3)Sphinx:开源的全文搜索引擎,支持多种数据源和分布式搜索;
4)Xapian:高效的全文搜索引擎,支持多种语言和数据源;
5)Nutch:基于 Java 的开源网络搜索引擎,支持爬取和索引网络内容;
6)OpenSearchServer:基于 Java 的企业级搜索引擎,支持多种数据源和自定义搜索功能;
7)Terrier:基于 Java 的文本信息检索系统,支持多种搜索算法和分布式搜索;[……]

READ MORE

「Groovy」- 连接数据库(使用 MySQL 演示)

连接数据库,并查询第一条数据
Connecting to MySQL using Groovy – Stack Overflow

@GrabConfig(systemClassLoader=true)
@Grab(‘mysql:mysql-connector-java:8.0.21′)
import java.sql.*;
import groovy.sql.Sql

def connectionString = “jdbc:mysql://<ip-address>:<port-number>/<database>”
// jdbc:mysql://localhost/db?useUnicode=true&characterEncoding=UTF-8′ //
def username = “<username>”
def password = “<password>”
def driver = “com.mysql.jdbc.Driver”
def sqlInstance = Sql.newInstance(connectionString, username, password, driver)

// 查询第一条数据
def firstRow = sqlInstance.firstRow(“select * from <database>.<table>”)
print firstRow

指定连接使用 UTF-8 编码
How to set up MySQL on Windows to accept UTF-8 data via Groovy JDBC connections – Stack Overflow

jdbc:mysql://localhost/db?useUnicode=true&characterEncoding=UTF-8’

使用 Prepared Statement 语法
Sql (Groovy 2.5.4)mysql – Insert multiple rows in one SQL statement in Groovy – Stack Overflow
解决数据转义、注入问题,代码如下:

def params = [10, ‘Groovy’, ‘http://groovy.codehaus.org’]
sql.execute(‘insert into PROJECT (id, name, url) values (?, ?, ?)’, params)

插入多条数据:

def updateCounts = sql.withBatch(‘insert into TABLE[……]

READ MORE

「TMV」- Service Mesh | 服务网格

该部分笔记将记录:Service Mesh 是什么,以及相关问题的解决方案。
认识
已知问题
应用的每个部分——即“服务”,都要与其他服务相互协作,来为用户提供所需的内容。如果在线零售应用的用户想购买什么东西,他们得知道该商品是否有货。因此,负责与公司库存数据库通信的服务需要与产品网页进行通信,而产品网页本身,也需要与用户的在线购物车通信。为了增加业务价值,该零售商之后可能会推出一项新服务:在应用中为用户提供产品推荐。要推荐产品,这项新服务除了要与产品标签数据库进行通信外,还需要与产品页面所需的同一个库存数据库进行通信,因此这涉及到大量可重复使用的移动组件。
微服务架构可让开发人员更改应用的服务,而无需全部重新部署。与其他架构的应用开发不同,每个微服务都是由小型团队来构建,他们可以灵活地选择自己的工具和编码语言。总体而言,微服务是独立构建的,它们之间彼此通信,出现故障也只是单独情况,而不会升级为整个应用的中断。

服务间的通信,令微服务成为可能。逻辑管理的通信可以编码到每个服务中,但随着通信变得越来越复杂,服务网格的价值也就愈发显著。
解决方案
对于以微服务架构构建的云原生应用而言,利用服务网格,可以将大量离散服务整合为一个功能应用。
服务网格是一种专门处理服务间通信的基础设施层,通常以轻量级网络代理(Sidecar模式)的形式部署在每个微服务旁,负责服务发现、负载均衡、流量管理、安全通信(如mTLS)、熔断、监控等能力,将通信逻辑从业务代码中解耦。
服务网格(例如开源项目 Istio)用于控制应用的不同部分之间如何共享数据。与用于管理此类通信的其他系统不同,服务网格内置于应用程序中的专用基础架构层。这个可见的基础架构层可以记录应用的不同部分是否能正常交互。因此,随着应用的不断发展,它在优化通信和避免停机方面就显得更加有用。
现代应用常以这种方式拆分,所有组件构成一个服务网络,每一个都分别执行特定的业务功能。要执行相应的功能,一项服务可能需要向其他几项服务请求数据。但如果有些服务(例如零售商的库存数据库)遇到请求超载会怎样呢?这就要靠服务网格了,它会将请求从一项服务路由到下一项,从而优化所有移动组件的协同工作方式。
组成
服务网格是如何运作的?
服务网格不会为应用的运行时环境加入新功能,任何架构中的应用还是需要相应的规则来指定请求如何从 A 点到达 B 点。但服务网格的不同之处在于,它从各个服务中提取逻辑管理的服务间通信,并将其抽象为一个基础架构层。
要这样做,服务网格会以网络代理阵列的形式内置到应用中。代理的概念在企业 IT 中并不陌生,如果您从一台工作计算机访问网页,很可能就会使用代理:

对该网页的请求发出后,首先会被公司的 Web 代理收到……

通过了代理的安全措施检查后,它会被发送到托管此网页的[……]

READ MORE

「JENKINS-PIPELINE」- 连接 MySQL 数据库

在 Jenkins Pipeline 中,我们需要连接 MySQL 数据库,以存储某些状态数据,用于在多个 Job 之间共享。这些数据不属于制品,通过制品管理的方式进行传递是件繁琐的事情,而且存在问题。然而,通过数据库进行传递是最好的方式。因此,我们需要解决在 Jenkins Pipline 中连接数据库的问题。
但是,事情往往没有看起来那样简单。我们难以像「Groovy 连接数据库」那样使用 mysql-connector-java 类库,原因在于 JDBC 驱动的加载方式以及 Jenkins Pipeline 类加载器的设计,这两点导致我们无法直接使用。但是,我们终究是找到解决方案。
该笔记将记录:在 Jenkins Pipeline 中,如何连接 MySQL 数据库,以及常见问题处理。
针对大多数构建任务,没有必要使用 Jenkins Pipeline 访问数据库,这也许是种小众需求。如果在设计中出现这种需求,也许应该重新考虑。我们之所以需要连接数据库,是因为我们要用 Jenkins 做些复杂的事情;
方案一、通过系统类加载器(废弃)
java – Where to put external jars in jdk10 – Stack Overflowconnecting to mysql db from jenkins pipeline not wroking – Stack OverflowHow to add a JDBC driver to Jenkins’s jobs & load the parameter values from SQL Databasemysql – How to add a JDBC driver to a Jenkins pipeline? – Stack Overflow
好在还有其他的方法:通过 Extension 机制,能加载自定义 Jar 包;
首先,在 Jenkins Pipeline 中,执行如下语句,以获取扩展加载位置:

println System.getProperty(“java.ext.dirs”),

在我们的环境中,如上语句输出:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302.b08-0.el7_9.x86_64/jre/lib/ext:/usr/java/packages/lib/ext
然后,下载 mysql-connector-java.jar 类库,并放入 /usr/java/packages/lib/ext/ 中,此时 mysql-connector-java 将由系统类加载器进行加载;
最后,访问数据库的代码如下(这与「Groovy 连接数据库」基本类似,但无需 Grab 引入驱动):

impor[……]

READ MORE

「LAPTOP」- 首个实现

时隔多年,我们已实现该技术方案,但是多少有些出入(但差别并不大):

共 4 层(从上到下,L4、L3、L2、L1)
虚拟化选用 VirtalBox 的原因是:
1)鉴于是移动数据中心,所以涉及桌面环境事件响应(例如休眠处理等等),而桌面虚拟化软件处理的更好;
2)VritualBox 免费;
鉴于虚拟化(L2)及物理层(L1)暂无特殊配置,所以不再详细说明。该部分的后续内容将概述网络层(L3)与客户机层(L4)的实现,及相关问题处理;
# 02/06/2022 OpenWrt 我们最后还是选择 OpenWrt 系统,原因如下:
1)针对我们的场景:OpenWrt 能够满足需求,日后也能够进行更换;
2)良好的生态:学习哪个都一样,我们也疲于折腾(ROI 太低);
第一步、创建 OpenWrt 实例
在 VirtualBox 中,部署 OpenWrt 服务:参考 [OpenWrt Wiki] OpenWrt on VirtualBox HowTo 文档,获取官方配置说明;
我们需要三张不同网络网络类型的网卡(尽量依序创建):
1)NAT:负责网络访问,否则 OpoenWrt 将无法上网;(相当于 WAN 接口)
2)Internal Network:实现 OpenWrt、Linux 的二层互联;(相当于 LAN 接口)
3)Host-only Adapter:能与主机通信,用于从主机连接和管理 OpenWrt 服务;
第二步、配置 OpenWrt 服务
我们需要配置 OpenWrt 服务,使其成为路由设备:[OpenWrt Wiki] OpenWrt as router device
如果遇到问题,或许需要使用 TCPDump 抓包:[OpenWrt Wiki] How to capture, filter and inspect packets using tcpdump or wireshark tools
第三步、配置 Linux Guest 实例
创建 Linux Guest 主机,并添加 Internal Network 类型网络,使其与 OpenWrt 二层互联;
然后,启动 Linux Guest 实例;
如果配置正确,Linux Guest 能够通过 OpenWrt 的 DHCP 获取 IP 地址;
后续的改进工作
# 01/10/2022 首个版本已能够运行,但是还有很多改进工作:
1)继续使用 VirtualBox 桌面虚拟化:KVM 非桌面虚拟化,对桌面场景处理不是很好。比如当 KVM 休眠恢复后,时间跳跃导致 Guest CPU Usage 飙升;
2)替换 Host 系统为 Linux 发行版:Window 10 + VirtualBox 的问题是,网卡[……]

READ MORE

「APACHE-GROOVY」- 环境安装

版本选择
Release notes for Groovy | https://groovy-lang.org/releases.html
各版本 Groovy 要求的 JVM 的版本 | System requirementshttps://groovy.apache.org/download.html#requirements

Groovy JVM Required (non-indy) JVM Required (indy) *
5.0 (in alpha) N/A 11
4.0 N/A 1.8+
3.0 1.8+ 1.8+
2.5 – 2.6 1.7+ 1.7+
2.3 – 2.4 1.6+ 1.7+
2.0 – 2.2 1.5+ 1.7+
1.6 – 1.8 1.5+ N/A
1.5 1.4+ N/A
1.0 1.4-1.7 N/A

环境安装
下载地址 | https://groovy.apache.org/download.html
with asdf
asdf plugin-list all | grep -i groovy
asdf plugin-add groovy
asdf list all groovy
asdf install groovy 2.4.21
asdf global groovy 2.4.21
groovy –version
for Build Tools
例如,针对 Maven 工具

<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>x.y.z</version>
<type>pom</type> <!– required JUST since Groovy 2.5.0 –>[……]

READ MORE

「KUBERNETES」- 集群升级 | kubeadm

该笔记将记录:Kubernetes Cluster 升级的方法,以及升级相关问题的解决办法。
注意事项:
1)这里仅简单记录升级过程,建议阅读相关官方文档,以获取升级过程的详细细节;
2)生产环境的升级还需要考虑业务可用性,建议详细阅读官方文档,以完成升级;
v1.18 to v1.20
Upgrading kubeadm clusters | Kubernetes
该部分将记录:将 Kubernetes Cluster 1.18 升级到 1.20 版本的过程,以及相关问题的处理办法。
鉴于是跨次版本号升级,根据官方文档,升级必须依次进行:1.18 ⇒ 1.19 ⇒ 1.12
1)Upgrading kubeadm clusters | Kubernetes/v1.19
2)Upgrading kubeadm clusters | Kubernetes/v1.20
1)环境检查、重要数据备份;
2)执行升级命令进行升级:

// ============================================================================> Determine which version to upgrade to

apt update
apt-cache madison kubeadm | grep 1.20.15-00

// ============================================================================> For the first control plane node

apt-get install -y kubeadm=1.20.15-00 kubectl=1.20.15-00 –allow-change-held-packages
kubeadm upgrade plan
kubeadm upgrade apply 1.20.15

apt-get install -y kubelet=1.20.15-00 –allow-change-held-packages
systemctl reload kubelet.service
systemctl restart kubelet.service

// ============================================================================> For the other control plane nodes

kubeadm upgrade node
apt-get install -y kubeadm=1.20.15-00 kubelet=1[……]

READ MORE