Zach Ke's Notes

Quick notes


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

基于 prometheus 打造监控报警后台 (5) - alertmanager 的三个概念(Grouping, Inhibition, Silences)

发表于 2022-12-05 | 分类于 prometheus 相关 | | 阅读次数:

前言

通过 基于 prometheus 打造监控报警后台 (4) - 使用 alertmanager 发送警报 我们已经能够在 prometheus 创建警报规则,并且使用 alertmanager 发送警报预警了。

但是 alertmanager 还有几个特性,并没有在上述文章讲述,本节继续延续上节的内容,讲一下 alertmanager 的几个概念。

1. 分组(Grouping)

Grouping 是把同类型的警报进行分组,合并多条警报到一个通知中。在生产环境中,特别是云环境下的业务之间密集耦合时,若出现多台 Instance 故障,可能会导致成千上百条警报触发。在这种情况下使用分组机制, 可以把这些被触发的警报合并为一个警报进行通知,从而避免瞬间突发性的接受大量警报通知,使得管理员无法对问题进行快速定位。

举个例子,比如我在 aws 的美西部署了 100 台服务器, 上面都部署了 node_exporter,然后突然有一天, aws 的美西服务商出现网络波动,导致 prometheus server 连接不到这 100 台服务器,这时候这 100 个节点就会全部触发告警, 瞬间往你的邮箱发送了 100 封邮件,直接给你造成邮件轰炸。

但是如果你有设置分组,将 100 台的主机的 up 检测都放到同一个分组内,那么这个 100 封告警就会合并成一条发送。

阅读全文 »

基于 prometheus 打造监控报警后台 (4) - 使用 alertmanager 发送警报

发表于 2022-11-29 | 分类于 prometheus 相关 | | 阅读次数:

前言

通过 基于 prometheus 打造监控报警后台 (3) - 使用 grafana 创建仪表盘 我们已经能够在 grafana 上面创建漂亮的数据监控仪表盘了。

本节我们讲一下怎么在 prometheus 上设置预警并且发送警报。

简单的架构图如下

在 prometheus 监控系统中,采集与警报是分离的,所以是分为两个步骤的:

  1. 在 prometheus 中创建警报规则,并监控警报和触发警报
  2. 触发警报之后(firing),prometheus 将报警信息转发给独立组件 alertmanager,然后经过 alertmanager 对报警信息处理之后,最后通过接收器发送给指定用户

同时 alertmanager 支持多种接收器(receiver), 比如 Email, Slack , 钉钉, 企业微信 , Webhook

阅读全文 »

基于 prometheus 打造监控报警后台 (3) - 使用 grafana 创建仪表盘

发表于 2022-11-29 | 分类于 prometheus 相关 | | 阅读次数:

前言

通过 基于 prometheus 打造监控报警后台 (2) - 指标类型和数据模型 我们可以知道,虽然 prometheus 采集数据很棒, 但是数据渲染这一块,实在是做的不咋的, 而且官方也推荐使用 grafana 来进行数据渲染: grafana support for prometheus

所以本节就安装一下 grafana 并创建 dashboard 仪表盘

安装

接下来我们直接按照官方文档进行安装: Download Grafana, 一样采用二进制包的方式来安装

1
2
3
4
5
6
7
8
9
10
11
12
# 下载安装
[root@VM-64-9-centos ~]# cd /usr/local/
[root@VM-64-9-centos local]# wget https://dl.grafana.com/enterprise/release/grafana-enterprise-9.2.5-1.x86_64.rpm
[root@VM-64-9-centos local]# sudo yum install grafana-enterprise-9.2.5-1.x86_64.rpm

# 加入到 system 并启动 (安装的时候,有创建 grafana-server.service 文件了,这边不再需要手动创建)
[root@VM-64-9-centos local]# systemctl enable grafana-server.service
[root@VM-64-9-centos local]# systemctl start grafana-server.service

# 查看端口监听, 3000 端口
[root@VM-64-9-centos local]# netstat -anlp | grep 3000
tcp6 0 0 :::3000 :::* LISTEN 8897/grafana-server
阅读全文 »

基于 prometheus 打造监控报警后台 (2) - 指标类型和数据模型

发表于 2022-11-28 | 分类于 prometheus 相关 | | 阅读次数:

前言

之前从 基于 prometheus 打造监控报警后台 (1) - 初试和安装 我们安装了 prometheus 服务,并且监控了当前的主机。 但是数据是有抓取了,怎么看指标乃至图表呢?

从后台查询指标

我们可以从 prometheus ui 后台的 graph 里面来查看, 他有两个 tab 视图展示,一个是 Graph 的图表展示,一个是 Table 的表格展示, 通过输入某一个指标 (Metric),我们就可以查看所有实例中有关于这个指标的所有的数据。 其中有表格形式展示的, 也有图表形式展示,图表形式还可以自己选择时间区间

然后查询的方式,提供了一种称为 PromQL 的函数式查询语言,可以让用户实时选择和聚合时间序列数据。表达式的结果可以显示为图形,也可以以表格数据的形式查看,或者由外部系统通过HTTP API 使用。

PromQL(Prometheus Query Language)是 Prometheus 自己开发的表达式语言,语言表现力很丰富,内置函数也很多。使用它可以对时序数据进行筛选和聚合。

关于查询的话,官网有提供了几个查询实例可供参考: 查询示例

其中包括几个常见的,比如:

阅读全文 »

基于 prometheus 打造监控报警后台 (1) - 初试和安装

发表于 2022-11-28 | 分类于 prometheus 相关 | | 阅读次数:

prometheus 介绍

1. 诞生背景

  1. IT 环境中,监控系统陈旧,监控指标单一,监控误报等,基于 zabbix 搭建,不能有效针对测试环境中k8s和容器的原生支持
  2. IT 环境监控类型繁多,有主机监控,也有应用监控。如:物理服务器、VMWare vSphere 虚拟化体系、交换机等网络设备、其他特殊主机、应用类监控,如 nginx 和 mysql 等
  3. IT 环境监控链路长,并指标复杂

2. 什么是 prometheus

Prometheus 是一个开源系统监控和警报工具包,最初由 SoundCloud 构建。自 2012 年启动以来,许多公司和组织都采用了 Prometheus,该项目拥有非常活跃的开发者和用户社区。它现在是一个独立的开源项目,独立于任何公司进行维护。为了强调这一点,并明确项目的治理结构,Prometheus 于 2016 年作为继 Kubernetes 之后的第二个托管项目加入了云原生计算基金会(CNCF)。

Prometheus 将其指标收集并存储为时间序列数据,即指标信息与记录时的时间戳以及称为标签的可选键值对一起存储。

说是现在最流行的系统监控后台,一点都不为过

阅读全文 »

bug 追踪系统 Sentry (4) -- 关联 sourceMap

发表于 2022-11-01 | 分类于 实用工具集 | | 阅读次数:

前言

通过 bug 追踪系统 Sentry (3) -- 项目引入 sdk 抛送 bug 我们已经知道怎么在项目中引入 sentry 的 sdk,并且将 error 抛送到线上。 但是线上的代码都是通过构建打包压缩过的,基本上没有可读性。

如果抛上去查看的话,错误堆栈长这样子,很难分析:

如果能像开发环境那样子,可以在控制台查看 sourceMap 的话,就可以直接查看对应的源代码就好了。 Sentry 有提供这个功能。

原理

我们都知道构建生成的 sourceMap 不能上传到 release 的线上环境,因为会有安全问题(源代码泄露)。 所以一般也就在开发环境,才会用 sourceMap 去 debug。

那么 sentry 可以使用 sourceMap 原理很简单,就是我们将源代码(包含 sourceMap) 上传到 sentry 后台,他会通过 release 版本来关联, 其实就是结合每次的 release 版本号来管理不同版本号的 sourceMap

因为我们是自建的,所以不用担心代码泄露问题,可以放心的用。

这一块 sentry 官方是有文档的: Sentry - JavaScript - Source Maps, 为此还提供了一个 cli 工具来上传这些 sourceMap: Command Line Interface

阅读全文 »

bug 追踪系统 Sentry (3) -- 项目引入 sdk 抛送 bug

发表于 2022-10-31 | 分类于 实用工具集 | | 阅读次数:

前言

之前花了两个篇幅,讲了 sentry 的 部署和邮件客户端的配置:

  • bug 追踪系统 Sentry (1) -- 单机安装
  • bug 追踪系统 Sentry (2) -- 账号初始化和邮件发送配置

本篇我们讲一下,怎么在项目中引入 sdk 并且抛送 bug 统计, 因为 sentry 支持很多语言:

阅读全文 »

bug 追踪系统 Sentry (2) -- 账号初始化和邮件发送配置

发表于 2022-10-31 | 分类于 实用工具集 | | 阅读次数:

前言

之前通过 bug 追踪系统 Sentry (1) -- 单机安装 我们成功在单机上部署了 sentry 服务,这一节我们讲进入后台的账号初始化和邮件发送配置。

账号初始化

其实在执行 instaill.sh 脚本的时候, 就会有用户初始化账号的引导流程,只不过我之前是通过 --skip-user-prompt 来跳过这个交互。

所以安装完之后,我们就要通过 docker-compose run --rm web createuser 来创建初始账号,具体执行如下

1
[root@VM-64-9-centos self-hosted-master]# docker-compose run --rm web createuser

并且在设置的过程中,也可以指定设置为 superuser 权限

阅读全文 »

bug 追踪系统 Sentry (1) -- 单机安装

发表于 2022-10-28 | 分类于 实用工具集 | | 阅读次数:

前言

现在的前端项目,如果一旦发布上线,代码一般都会进行混淆、压缩甚至加密,如果线上没有bug跟踪系统,客户端一旦报错,前端就无法及时感知,这个时候就需要使用人员上报,一层层上报到技术这边,技术如果要调试或者获得更具体的信息,就没有办法了,大部分情况下只有一张图片,但光靠一张图片,要追查bug产生的原因,有的时候是蛮困难的,等真正查清楚了,黄花菜都凉了。

所以其实是需要一个可以记录 bug 或者抛送异常数据的一个系统的。 其实这一块业内是有一些解决方案,除了一些系统之外, 各大云服务厂商其实也有类似的产品,比如

  1. frontjs
  2. fundebug
  3. trackjs
  4. instabug
  5. rollbar
  6. sentry

但是规模大了,基本上都要商用,都需要钱, 再加上之前也有用过美团开源的 Logan: 使用 Logan 来做前端日志系统, 基本上体验了一阵子下来,也有一些问题没有解决,比如:

  1. 没有多项目管理
  2. 搜索功能鸡肋
  3. 日子列表缺少筛选功能,希望能加上分类筛选,例如用户信息、设备型号、浏览器环境、或者添加自定义字段进行筛选。
  4. 没有分页,无法查看20条以后的数据
  5. 日志丢失内容
  6. 希望可以自定义日志类型与颜色
  7. 在日志详情页面上看不到环境等信息
  8. 日志条目无法显示颜色,例如时间用其他颜色显示
  9. 日志条目详情希望可以解析json
阅读全文 »

使用 ionCube 对 PHP 项目进行加密(2) - 加密指令详解

发表于 2022-10-18 | 分类于 php相关 | | 阅读次数:

前言

之前我们已经有使用 ionCube 对 PHP 项目进行加密: 使用 ionCube 对 PHP 项目进行加密

之前拿来做测试的是 免费申请试用版本, 后面就直接买了一个正式版的 Cerberus 的,$399 一个 license 的,永久使用 (除非换机器)

激活

本质上这个正式的 release 版本和申请试用的版本在功能上没啥差别 (都是 Cerberus 版本),除了加密器的有效期 14 天以及加密文件的有效期 36 小时之外。

不过 release 版本在使用之前是需要先执行激活的,而且一旦激活之外,接下来就只能在这一台机器上才能加密了

阅读全文 »

web 安全之 - 使用CSP(Content Security Policy)来防止 XSS 攻击

发表于 2022-10-09 | 分类于 web安全 | | 阅读次数:

XSS

跨网站脚本(Cross-site scripting,通常简称为XSS或跨站脚本或跨站脚本攻击)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。

即使到了 2022 年, 在 OWASP(Open Web Application Security Project,是一个开放式Web应用程序安全项目组织,旨在帮助计算机和互联网应用程序提供公正、实际、有成本效益的信息。) 的 top10 漏洞中, XSS 依然排名前三 (2021 年的第三名的 注入(Injection),其中 XSS 就属于这一类)

正常我们常用的防护手段一般是对于任何的参数请求以及页面中涉及到的用户信息全部都要进行特殊字符的过滤,进行 html转义,防止触发恶意脚本

但是如果能够理解 XSS 的实际攻击方式,真的要对用户的利益造成损失的话 (不是单纯的只是弹个 alert 框), 一般就两种方式:
1 注入恶意脚本,执行或者引导用户,从而盗取信息或者执行恶意操作
2 直接获取该页面上的用户信息,比如 cookie, 然后抛到攻击者自己的服务器

阅读全文 »

使用 ionCube 对 PHP 项目进行加密

发表于 2022-09-29 | 分类于 php相关 | | 阅读次数:

前言

之前我们尝试使用 yakpro-po 和 Swoole Compiler 对我们的本地化部署的 PHP 项目进行混淆加密

  • 使用 yakpro-po 对 PHP 项目进行混淆加密
  • 使用 swoole compiler 对 PHP 项目进行加密

接下来我们继续试用另一个扩展加密的方案 - ionCube

ionCube

  • PHP Encoder 12

我们用的是 PHP Encoder 12 这个版本 (他的低版本有被破解的风险,这个版本是扩展加密版本,目前外网没有被破解的相关资讯,安全性应该靠谱)

简单的来说 ionCube 编码器将源代码编译为字节码,可以根据需要对编译后的代码进行混淆和加密,并具有以各种方式保护解密密钥的功能。

此外,Pro 和 Cerberus 版本中内置的 PHP 许可功能允许对 PHP 脚本进行许可,限制 PHP 代码可以在哪里运行以及它是否有到期时间。

阅读全文 »

浅谈之 - 怎么有指标和可量化的跟进web站点的性能加载问题

发表于 2022-09-27 | 分类于 服务浅谈系列 | | 阅读次数:

前言

随着对 SEO 的越来越重视, 我们也希望我们提供的站点在加载性能上也会越来越快,越来越好。

但是本质上页面的性能加载是否优越的界限其实是相对比较模糊的,尤其是有一些站点的页面其实服务的是全球的用户,而不同地方用户的不同的网络情况又会极大的影响站点的加载速度。

举个简单的例子,在网络通达的日本和韩国,哪怕你页面写的不怎么样,但是架不住人家网络快,冲浪浏览起来就是快。 然后在印度,非洲这种地方,网速本来就慢, 就算你页面优化的非常到位,有可能还是要 load 很久才会出来 (巧妇难为无米之炊)。

所以如果要跟进和标准量化页面的性能加载问题,那么就要有一套通用的检测和分析的标准。这个标准就会涉及到几个方面:

  1. 检测页面性能的工具
  2. 优化页面性能的固定检测指标
阅读全文 »

使用 swoole compiler 对 PHP 项目进行加密

发表于 2022-09-22 | 分类于 php相关 | | 阅读次数:

前言

之前试了一下 yakpro-po 混淆加密的测试: 使用 yakpro-po 对 PHP 项目进行混淆加密

接下来我们试一下扩展加密 Swoole Compiler 的效果

Swoole Compiler

Swoole Compiler 是swoole官方推出的PHP代码加密和客户端授权解决方案, 通过业内先进的代码加密技术(流程混淆,花指令,变量混淆,函数名混淆,虚拟机保护技术,扁平化代码,sccp优化等)将PHP程序源代码编译为二进制指令,来保护您的源代码,加密技术更先进、更安全。

与 Zend Guard 等传统的PHP加密器不同,Swoole Compiler 没有软件界面,它提供了API,可将 Swoole Compiler 集成到您的打包发布平台中,完全是可编程的。Swoole Compiler 相比其他传统的PHP加密器,安全强度更高。

Swoole Compiler 使用了特殊定制的ZendVM,与普通的PHP程序运行模式有较大差异。并具有如下特性:

  1. 保护程序源码:避免 PHP 源代码泄漏,避免被编辑
  2. 提升性能:使用Swoole Compiler底层内置了多个编译优化器,可优化 opcode,性能比源码执行有较大提高
  3. 授权管理:内置了授权管理功能,可限制PHP程序运行的机器硬件和网络环境

还有一点,就是国人开发,响应时间快

阅读全文 »

使用 yakpro-po 对 PHP 项目进行混淆加密

发表于 2022-09-20 | 分类于 php相关 | | 阅读次数:

前言

之前有讨论 浅谈之 - PHP 项目代码加密方案, 所以本次我们尝试使用 yakpro-po 对我们的 PHP 项目进行混淆加密

  • YAK Pro - Php Obfuscator
  • PHP Parser

环境需求

因为 yakpro-po 是基于 PHP Parser 4.x 的语法解析器做的。 而 PHP Parser 4.x 库的运行环境要求是要在 PHP 7.0 以上环境才能运行, 不过混淆后的代码可以在 PHP 5.2 到 7.3 之间都可以工作。

而我们的 PHP 程序是跑在 PHP 5.6 上面的。 所以可以满足混淆之后运行代码的环境。 而且为了更直观的表现出混淆加密和针对加密文件的运行可以在不同的服务器和 PHP 环境上,本次的测试用两台测试机器来测试,这样子会显得更直观

  1. 一台服务器 -> 混淆加密服务器,上面安装了 7.4 的 PHP 版本, 采用 docker-compose 安装的,参照: LNMP一键安装程序, 这一台用来混淆加密 PHP 代码。
  2. 一台服务器 -> 加密文件运行服务器, 用来运行混淆过的 PHP 程序的代码,PHP 5.6 的环境, 看是否可以成功运行。

嫌麻烦的可以直接用一台,然后在各自的 docker 里面跑

阅读全文 »

浅谈之 - PHP 项目代码加密方案

发表于 2022-09-20 | 分类于 服务浅谈系列 | | 阅读次数:

前因

未来有个项目需要对客户进行远程部署交付,意味着我们的服务会部署在用户自己的服务器上,考虑到商业代码的安全性,有一些服务是用 PHP 开发的项目,PHP 这种解释性语言,其实是直接源文件显示的, 这样子其实就相当于把我们的源代码暴露出来。 这样子肯定是不行的。

所以我们要对 PHP 的代码进行加密,所以结合网上针对 PHP 加密的一些文章, 我这边再次加工自己总结了一份。

PHP 的几种加密方式

按照加密的方式来看, PHP 可以包含以下几种方式:

1. 壳”加密”

这一类“加密”包括:

  • 无扩展加密:phpjiami、zhaoyuanma 的免费版本等
  • 有扩展的加密:php-beast、php_screw、screw_plus、ZoeeyGuard、tonyenc 等市面上几乎所有的开源PHP加密扩展。
阅读全文 »

vue-router history 路由模式的后端配置

发表于 2022-08-17 | 分类于 nginx相关 | | 阅读次数:

前言

前段时间有用 vue3 + vue-router 做了一个前端的单页面应用程序, 那时候为了让路由看起来更美观一点,使用了 vue-router 的 history 模式 (还有一种就是 hash 模式)。

1
2
3
4
const router = createRouter({
history: createWebHistory(),
routes
})

虽然 history和 hash 都是利用浏览器的两种特性实现前端路由,history 是利用浏览历史记录栈的API实现,hash 是监听 location 对象 hash 值变化事件来实现。

但是 history 模式因为是直接走 url 的 pathname 中,所以首次访问或者刷新的时候,都会请求到后端的服务器的。 所以后端的服务器是要适配路由的。

这时候因为是单页面应用程序,所以适配路由其实是全部回到首页,简单的来说,就是在请求路由 404 的情况下, 请求首页 index.html 就行了。

阅读全文 »

记一次 nginx worker_connections 最大可连接数不够用的情况

发表于 2022-08-17 | 分类于 nginx相关 | | 阅读次数:

前言

前段时间在对某一个长连接的 wss 服务进行端口优化的时候, 因为原先的端口是一个不常用端口,想改成一个常用的端口,比如 tls 的 443 端口, 又因为这一台服务器已经有安装 nginx,并启用了 443 端口。

所以就采用了 nginx 转发 ws 端口, 然后变成 wss 的 443 端口。 具体可以看: nginx 转发代理 wss 和 https (目标程序是 ws 和 http)

因为 nginx 的端口转发会需要映射到服务器的可用端口,所以就将服务器的可用端口调成一个比较大的值:

1
2
[kbz@VM-16-9-centos ~]$ cat /proc/sys/net/ipv4/ip_local_port_range
1024 65000

但是只顾了这个要扩大端口号,而忘了也要调整 nginx 的最大可连接数。 导致上线之后没有多久就出现长连接连不上的情况,查看了一下 nginx 的 error log,发现:

1
[alert] 23725#0: *1972528 8000 worker_connections are not enough while connecting to upstream, client: 109.xxx.xxx.92,

解决

查了一下,确实是 nginx 的配置文件中,配置的单核最大可连接数只有 8000:

1
2
3
events {
worker_connections 8000;
}

然后 worker_processes 是 2 核 (几个 CPU 一般就可以调用几个), 所以 nginx 的最大可连接数就是 2 * 8000 = 16000 个, 所以一旦长连接超过了这个数量,就会报上述的错误。

所以解决的方式也很简单,后面将其改成了 50000, 配合我们的 2 核(2 个 cpu), 最大可支持 10w 的最大可连接数 (不可能到 7w, 因为服务器本身的可转发端口就满了)

记一次 nginx 转发代理 https 出现 502 的情况

发表于 2022-08-16 | 分类于 nginx相关 | | 阅读次数:

前言

前段时间我们官网要做一个活动页面, 但是活动页面是用另一个活动页域名, activity.example.com, 但是运营人员需要对外展示的落地页是以官网 www 的域名来处理,所以这时候就会需要在官网的 nginx 指向那边进行页面的代理转发:

1
2
3
4
location  /promo/student-discount {
resolver 8.8.8.8;
proxy_pass https://activity.example.com/promo/student-discount;
}

但是实测的过程中, 却发现代理转发的时候,报了一个 502 的错误

1
2
2022/08/16 11:58:22 [error] 2293#0: *213338285 SSL_do_handshake() failed (SSL: error:1408F10B:SSL routines:ssl3_get_record:wrong version number) while SSL handshaking to upstream, client: 14.xxx.1.86, server: www.example.com, request: "HEAD /promo/student-discount HTTP/1.1", upstream: "https://13.xxx.xxx.101:443/promo/student-discount", host: "www.example.com"
2022/08/16 11:58:22 [warn] 2293#0: *213338285 upstream server temporarily disabled while SSL handshaking to upstream, client: 14.xxx.1.86, server: www.example.com, request: "HEAD /promo/student-discount HTTP/1.1", upstream: "https://13.xxx.125.101:443/promo/student-discount", host: "www.example.com"

看了一下,应该是 nginx 在进行代理请求的时候,就报错了, 应该是 ssl 的握手的错误 SSL_do_handshake()

阅读全文 »

webrtc 视频流在 IOS 下会出现黑屏

发表于 2022-08-16 | 分类于 webrtc相关 | | 阅读次数:

前言

之前在实作 webrtc 浏览器对浏览器的远程投屏的时候, 有发现了一种情况,就是在 IOS 下, 远程传输过来的视频流 (mediaStream), 不管是在 chrome 还是 safari 下都没有办法播放, 不同版本的 IOS 表现还不一样:

  1. IOS 14 及以下,会出现只播放第一帧, 就卡住
  2. IOS 14 以上, 整个视频流直接黑屏

因为是自动播放,之前的代码是这样子的:

1
2
3
4
5
video.play()?.catch(e => {
// 刚开始初始化的时候,要禁音,不然 chrome 会报这个错误 Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.
video.muted = true
video.play()
})

因为相当于也是自动播放, 在 PC 和 android 浏览器都正常,就是 IOS 浏览器不正常。

后面查了一下,发现还真是要加一个 IOS 系统特有的属性:webrtc with firebase :how to fix black screen on ios/safari

在调用 video.play() 之前,要加上这个设置项:

1
video.playsInline = true

就可以正常播放了

后面查了一下,原来在 IOS 系统上,如果要实现自动播放, 直接在标签上添加 autoplay, 或者 js 直接调用 video.play(), 是不行的。

还要加上 playsInline 属性才行, 其实就是允许视频内屏播放(没有这个属性也不一定是默认全屏播放,不过对于 IOS 来说,没有这个属性, 自动播放都不行)。

123…16
Zach Ke

Zach Ke

做最咸的那一条

316 日志
31 分类
83 标签
GitHub
© 2024 Zach Ke
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4