8.22-8.23每日一讲:

8.22-8.23每日一讲:

如何配置 Ansible 的清单(inventory)和 SSH 免密登录?

配置 Inventory

默认清单文件位于 /etc/ansible/hosts,也可用 -i 指定自定义文件(INI 或 YAML 格式)。

INI 格式示例 hosts.ini

[webservers]
web1.example.com
web2.example.com ansible_host=192.168.1.20 ansible_port=2222

[dbservers]
db1.example.com

[all:vars]
ansible_user=root
ansible_ssh_private_key_file=~/.ssh/id_rsa

验证ansible -i hosts.ini webservers -m ping(或 ansible all -i hosts.ini --list-hosts

YAML 格式示例 hosts.yml

all:
  hosts:
    web1.example.com:
    web2.example.com:
      ansible_host: 192.168.1.20
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:

配置 SSH 免密登录

第一步:生成密钥对(在控制端执行,如已有可跳过)

ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa

第二步:将公钥复制到目标主机(逐台执行)

ssh-copy-id -i ~/.ssh/id_rsa.pub root@web1.example.com

ssh-copy-id 时也可手动追加:
cat ~/.ssh/id_rsa.pub | ssh root@web1.example.com "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

第三步:验证

ssh root@web1.example.com 'echo OK'      # 不再提示输密码
ansible -i hosts.ini all -m ping -u root  # 免密执行

常见问题

  • 连接失败:检查目标机 /etc/ssh/sshd_configPasswordAuthentication no 是否有误、authorized_keys 权限(应为 600)、家目录权限(应为 700)。
  • 不想改默认用户:在 inventory 中通过 ansible_useransible_ssh_private_key_file 等变量按组或全局指定。
  • 批量免密:可写一个简单循环脚本遍历 inventory 中的所有主机执行 ssh-copy-id

编写一个 playbook 来安装 Nginx 并启动服务。

Playbook:

---
- name: Install and start Nginx
  hosts: webservers
  become: yes          # 以 root 权限执行

  tasks:
    - name: Install nginx
      apt:              # Debian/Ubuntu 系统;CentOS/RHEL 改用 yum/dnf
        name: nginx
        state: present
        update_cache: yes

    - name: Ensure nginx is running and enabled
      service:
        name: nginx
        state: started    # 启动服务
        enabled: yes      # 开机自启

使用方法

ansible-playbook -i hosts.ini nginx.yml

说明

  • become: yes:安装服务需要 root 权限,对应之前配置的免密 sudo。
  • 包管理器apt 用于 Debian/Ubuntu;RedHat 系用 yum(可加 state: latest)。
  • state: started:确保服务正在运行;若已停止则启动。
  • enabled: yes:设置开机自启(可选,生产环境建议保留)。
  • 如需校验结果,可追加 playbook 验证(也可省去,默认即成功退出):
    - name: Verify nginx is running
      command: systemctl status nginx --no-pager -l
      register: result
      failed_when: "'active (running)' not in result.stdout"

总结:用 apt/yum 安装 Nginx,再用 service(或 systemd)模块确保 state: started 且开机自启即可。

如何调试 Ansible 的 playbook?如何提高 Ansible 的执行效率?

调试手段

手段 说明
--syntax-check 只做语法检查,不执行:ansible-playbook playbook.yml --syntax-check
--check(试运行) 干跑模式,只预测不执行变更:ansible-playbook playbook.yml --check
--diff --check 连用,显示文件/配置的差异:--check --diff
--step 逐步执行,每步询问是否继续
--limit 只针对部分主机:--limit web1(先小范围验证)
-v / -vvvv 提升输出详细度:-v 显示任务结果,-vvvv 显示 SSH 及全部细节
debug 模块 打印变量:- debug: msg="{{ var_name }}"
register + debug 捕获命令输出并打印:- debug: var=result.stdout

调试思路:先 --syntax-check 排除语法错误 → --check --diff 预览变更 → --limit 单台主机实跑 → 失败时用 -vvv 看详细报错,配合 register/debug 查看中间变量。


提高执行效率

  1. 并行与连接
ansible-playbook playbook.yml --forks 20        # 增加并行数(默认 5)
ansible-playbook playbook.yml -e "ansible_ssh_pipelining=true"
  • ansible.cfg 全局开启 ssh_pipelining = True(免 sudo 时可显著提速,减少 SSH 往返)。
  • 使用 SSH ControlPersist(Ansible 默认已开启,减少重复握手)。
  1. 减少执行内容
  • serial:滚动分批部署,如 serial: 10(生产分批更安全,也避免瞬时负载)。
  • gather_facts: no:不需要系统信息时跳过事实收集(首个任务往往是瓶颈)。
  • tags 定向执行:给任务打标签,只跑需要的部分:ansible-playbook playbook.yml --tags deploy
  • when 条件跳过:已满足前置条件的任务直接跳过(如已安装的软件包,state: present 的 apt 本身就会跳过)。
  • changed_when:合理定义"变更"判定,避免误触发 handler 连锁。
  1. 架构与缓层
  • --check 不影响:正式执行时可先并行预热。

  • 事实缓存ansible.cfg):

    gathering = smart
    fact_caching = jsonfile
    fact_caching_connection = ~/.ansible/facts
    fact_caching_timeout = 3600
    
  • role/模块化:任务按角色拆分、复用公共代码,减少重复编写和冗余执行。

总结:调试靠 --syntax-check--check --diff-vvvdebug;提效靠加大 forks、开 ssh_pipelining、按需 gather_facts/tags 以及事实缓存。

Metrics(指标)、Sample(样本)、Time Series(时间序列)是什么?

  1. Metrics(指标

指标描述某个被监控对象的一个可度量的特征,由指标名 + **标签(label)**组成。

指标名: http_requests_total
标签:   method="GET", status="200"
  • 指标名:语义化命名(如 http_requests_totalcpu_usage),符合正则 [a-zA-Z_:][a-zA-Z0-9_:]*
  • 标签:键值对(如 method="GET"),用于区分同一指标的不同维度,是查询和聚合的关键。
  1. Sample(样本

样本是某个指标在某一时刻具体测量值,对应 Prometheus 返回的一次时间序列中的一个数据点。

http_requests_total{method="GET"} = 1024  @  2024-01-01 10:00:00
                      (值)                 (时间戳:毫秒)

一个样本 = 时间戳(timestamp)+ 值(value),可能还有附加的 labels。

  • 时间戳:毫秒精度(如 1704067200000)。
  • :float64 浮点数(10243.14)。
  1. Time Series(时间序列

时间序列是同一个指标在同一组标签组合下,按时间有序排列的样本集合

http_requests_total{method="GET", status="200"}
  10:00:00 → 1024
  10:00:30 → 1100
  10:01:00 → 1187
  (...)
  • 每个唯一标签组合(labelset)对应一条独立的时间序列。
  • 序列在存储内部按时间戳升序排列,同一序列内不允许重复时间戳样本。

关系

Metrics = 指标名 + 标签(定义"测什么、怎么区分");
Time Series = 同一标签组合下按时间排序的样本流;
Sample = 时间序列中的单个数据点(时刻 + 值)。

类比:Metrics 是"温度计"这个测量项,Time Series 是它一天 24 小时的连续读数记录,Sample 是其中某一刻的读数。

Prometheus 中的查询示例

# 选出标签组合为 method="GET", status="200" 的时间序列
http_requests_total{method="GET", status="200"}

# 对时间序列的样本值做 5 分钟聚合 → 产生新的时间序列
rate(http_requests_total{job="api"}[5m])

四种指标类型(Counter, Gauge, Histogram, Summary)的区别和用途。

Counter(计数器):只增不减的累计值,用于计数类指标,比如请求总数、错误次数、任务完成数。查询时通常配合 rate() 计算每秒速率。

Gauge(仪表值):可增可减的瞬时值,反映当前状态,比如内存使用量、当前并发连接数、温度、磁盘剩余空间。直接查当前值即可。

Histogram(直方图):把观测值按预设区间做分布统计,记录每个区间的累积计数、总和和总数。用于无法提前预知取值分布的场景,比如请求耗时、响应大小。分位数在服务端查询时计算,可以跨实例聚合,查询时用 histogram_quantile() 得到 P95 等。

Summary(摘要):和直方图类似,但分位数在客户端就计算好了,直接暴露 quantile 值。适用于已知大致分布、已确定目标分位数的耗时类指标。缺点是分位数无法跨实例聚合。

核心区别:Counter 只增,Gauge 可增可减,两者都是简单数值;Histogram 和 Summary 都是分布统计,区别在于分位数在哪边算——Histogram 服务端算、可聚合,Summary 客户端算、不可聚合。

总结:计数用 Counter,当前状态用 Gauge,需要分布或分位数就用 Histogram(更灵活、推荐),客户端要固定分位数才用 Summary。

PromQL 的基本语法,如何做聚合、筛选、计算?

基本语法

核心结构:指标名 + 标签筛选 + 时间范围。

指标名{标签筛选}[时间范围]

例如 http_requests_total{method="GET", status="200"}[5m],表示选出满足标签条件的指标,取最近 5 分钟的所有样本。范围查询结果需要经过函数(如 rate、sum)处理后才能直接展示。

标签匹配符
= 精确相等,!= 不相等,=~ 正则匹配,!~ 正则不匹配,多个条件用逗号连接,是"与"关系。

筛选(选择器)

选出特定指标或特定维度,例如:
node_cpu_seconds_total{instance="192.168.1.10"} 只选该实例,{job=~"api.*"} 正则匹配 job 前缀,{__name__=~"node_.*"} 按指标名正则匹配。

聚合(Aggregation)

用聚合运算符把多条时间序列合并成一条,常用:
sum 求和,avg 平均值,max/min 最大最小,count 计数,topk/bottomk 取前 K 名,stddev 标准差。聚合时可带 by 或 without 保留维度,例如 sum by (method) (rate(...)) 按 method 分组求和,sum without (instance) 去掉实例维度。

count_values 是按值计数,group 是分组。

计算(运算)

算术运算:加减乘除、取模 + - * / %,例如 (memory_total - memory_free) / memory_total 计算使用率,^ 幂运算。

比较运算> >= < <= == !=,结果只保留满足条件的序列,可配合 bool 修饰符输出 0 或 1,例如 cpu_usage > 0.8 筛选超阈值主机,cpu_usage > 0.8 bool 返回 0/1。

逻辑运算and(同时满足)、or(任一满足)、unless(左有右无),用于序列合并筛选,例如 up == 1 and cpu_usage > 0.8

向量匹配:不同标签集合的序列做运算时,用 onignoring 指定按哪些标签匹配,例如 ignoring(cpu) rate(node_cpu_seconds_total[5m]) 匹配实例维度的两组序列。

一元运算:负号 -、取反 !

常用函数

rate() 求每秒速率(Counter 用,如 rate(http_requests_total[5m])),increase() 求增长量,irate() 求瞬时速率,histogram_quantile() 算分位数,avg_over_time() 等 over_time 系列做时间窗口内的聚合(如 max_over_time(cpu_usage[1h]))。

8月23日技术每日一讲:

如何配置 Alertmanager 实现告警的分组、抑制、静默和路由(到钉钉/微信)?

分组(Grouping)

分组在配置文件 route 段完成,目的是把互相关联的告警合并成一条通知,防止告警风暴。核心参数有三个:

group_by 指定按哪些标签分组,比如按告警名和实例分组,则同一实例同一告警类型的多条告警只发一条。group_wait 是收到这组第一条告警后、发出通知前的等待时间,这段时间用于吸收陆续到达的同类告警并合并;group_interval 是同组中又有新告警时,距上次通知多久再补发一次。另有 repeat_interval 控制告警一直未恢复时多久重复提醒一次,它与分组无关,但也在 route 顶层配置。

路由(Routing)

路由解决"告警发给谁",配置是一棵规则树。根路由定义默认行为(默认接收者、默认分组参数),子路由按标签条件逐层分流,支持精确匹配 match 和正则匹配 match_re。告警从根开始向下匹配,命中哪个子路由就发往对应的 receiver。典型做法是 severity 为 critical 的走独立接收者、某个团队标签走企业微信、其余走钉钉。

抑制(Inhibition)

抑制写在 inhibit_rules 列表里,解决"大故障时噪音告警刷屏"的问题。规则含义是:当源告警(比如机房宕机的 critical 告警)存在时,自动静音目标告警(比如该机房内各主机的 warning 告警)。equal 指定源和目标在哪些公共标签上必须一致才生效,防止抑制错伤其他实例的告警。

静默(Silencing)

静默是人工临时屏蔽某类告警,典型用途是计划维护窗口。它不在 yml 中配置,而是运行时创建:用 amtool 命令行或 Web UI 添加静默,指定标签匹配条件(支持正则)和持续时间,到期自动失效。静默优先级最高,被静默的告警不会触达任何接收者,适合运维操作前临时压掉已知噪音。

路由到钉钉 / 企业微信

Alertmanager 没有钉钉和微信的原生接收器,统一做法是走 webhook:在 receivers 里给每个名字配置 webhook_configs,填入钉钉机器人或企业微信机器人的 Webhook URL,send_resolved 设为 true 让恢复通知也能送达。如需标题关键字、@人等定制格式,可再接 prometheus-webhook-dingtalk 这类桥接工具做转换和签名。

要注意平台安全设置:钉钉机器人要求内容含指定关键词(如"告警")或带加签密钥,否则推送被拒;企业微信机器人可按关键词或 IP 白名单限制。告警内容中必须带上该关键词。

示例

route:
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'webhook-dingtalk'
  routes:
    - match:
        severity: 'critical'
      receiver: 'webhook-dingtalk'

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match_re:
      severity: 'warning'
    equal: ['instance']

receivers:
  - name: 'webhook-dingtalk'
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=你的token'
        send_resolved: true

总结

route 树把告警分组并分流到不同 receiver(钉钉、企业微信通过 webhook 实现);inhibit_rules 在源告警存在时自动压掉衍生告警;静默由人工用 amtool 或 Web UI 按条件、按时长临时屏蔽,优先级最高。改完配置用 amtool check-config 校验,amtool reload 热加载即可。

如何用 Recording Rules 提升查询性能?

重新思考:如何用 Recording Rules 提升查询性能

原理:

Prometheus 查询慢,通常不是因为查一条序列慢,而是因为每次查询都要现场做聚合运算:跨长时间窗口取样本(如 rate 一小时的数据)、多指标做除法或关联、在高基数序列集合上做 sum。这些运算在 Grafana 每次刷新时都会重复执行一遍,多个看板、多人同时看,就是同一计算被反复做。

Recording Rule 做的事情是:把某个复杂 PromQL 表达式按固定周期(evaluate_interval,默认 15 秒)在后台算一次,把算出的结果作为一条新时间序列写入存储。之后查询这个结果时,Prometheus 只是做一次普通的序列读取,没有任何聚合运算——查询从"重计算"变成了"轻读取"。

怎么写

规则写在独立规则文件里,用 rule_files 在 prometheus.yml 中引入:

groups:
  - name: http
    rules:
      - record: job:http_errors:rate5m
        expr: sum by (job) (rate(http_errors_total[5m]))

record 是新指标名,expr 是要预计算的表达式。命名遵循 level:metric:operation 约定,让人看出层级和算法。

使用场景

被频繁查询且计算重的表达式:多个仪表盘反复加载的聚合视图;派生指标(错误率、成功率);多条规则共用的中间结果。这类表达式记录后收益最直接,告警规则里较重的范围聚合同样适用。

代价

第一,计算总量没减少,只是后移到了后台——每 15 秒无条件算一遍,就算没人查也照算,所以只记录真正被复用的查询,否则等于给 Prometheus 加了持续负担。第二,by 的标签选择决定维度:过早降维失去下钻能力,保留过多维度又会让结果序列基数膨胀、存储变大。第三,结果序列本身占用存储,评估增量成本。第四,它只缓解计算重的问题,不解决基数大、存储膨胀的问题,那要靠压缩标签、限制保留时长等配合。

总结

Recording Rule 的本质是"空间换时间":把反复出现、计算重的聚合表达式,用固定周期的后台预计算换成一条随时可读的存储序列,让查询从重计算变成轻读取,从而降低查询延迟和查询负载;但代价是持续的后台计算和额外存储,因此只对复用价值明确的查询做记录,并小心控制结果序列的维度。

简述 Kubernetes 的架构(Master和Node组件及其功能)。

Master(控制平面)

负责集群的决策与控制,组件可运行在任何节点上。

  • kube-apiserver:集群的"总入口",所有组件和命令(kubectl)都通过它交互,负责认证、授权和转发请求,也是唯一直接读写 etcd 的组件。
  • etcd:集群的数据库,保存所有配置、状态和对象数据(键值存储),是高可用的关键。
  • kube-controller-manager:运行各类控制器(节点控制器、副本控制器、端点控制器等),持续把实际状态调整到期望状态,如维持副本数。
  • kube-scheduler:调度器,为新建的 Pod 选择最合适的 Node(综合资源、亲和性、污点等条件)。

Node(工作节点)

负责实际运行容器。

  • kubelet:节点上的"代理",向 apiserver 注册节点,接收 Pod 指令并维护 Pod 生命周期(启动、健康检查、重启),上报状态。
  • kube-proxy:维护节点网络规则,实现 Service 的访问(通过 iptables/IPVS 做负载均衡和流量转发)。
  • 容器运行时(Container Runtime):实际拉取镜像、启停容器的组件,如 containerd、CRI-O(不是二进制进程而是一个接口抽象,但属于 Node 关键部分)。

工作流程:

用户用 kubectl 发请求 → apiserver 校验并存入 etcd → scheduler 选定节点 → 该节点 kubelet 收到指令 → 通过容器运行时启动 Pod → kube-proxy 为 Service 配置转发规则。

总结

Master 负责"决策"(API 入口、存储、调度、状态管理),Node 负责"执行"(运行容器、网络转发、节点代理),两者通过 apiserver 和 kubelet 之间的通信协作。

Pod, Deployment, Service, Ingress 的概念和关系。

概念

Pod:Kubernetes 中最小的调度和运行单元,是一个或多个容器的组合,共享网络(同一 IP)和存储。容器必须跑在 Pod 里。

Deployment:管理 Pod 副本的控制器。声明期望的副本数,负责滚动更新、回滚、扩缩容,并保证实际运行的 Pod 数量与期望一致。

Service:给一组 Pod 提供一个稳定的访问入口(ClusterIP 和 DNS 名)。Pod 的 IP 会随重启变化,Service 通过标签选择器绑定这组 Pod,并做负载均衡转发流量,Pod 重启后入口不变。

Ingress:集群对外部流量的入口层。它不是转发组件本身,而是定义转发规则的资源,由 Ingress Controller(如 Nginx Ingress)落地实现:按域名、路径把外部 HTTP/HTTPS 请求路由到对应的 Service,还能做 TLS 终止、路径重写。

关系

外部请求 → Ingress → Service → 一组 Pod(由 Deployment 管理)

  • Deployment 负责把 Pod 拉起并维持副本数,但它只管"Pod 存在",不管"怎么访问"。
  • Pod 的 IP 不稳定且不可对外,所以用 Service 给这组 Pod 一个固定入口,做负载均衡。
  • Service 的地址默认只在集群内部可达,集群外访问要再经过 Ingress(或 NodePort/LoadBalancer),Ingress 按域名/路径把外部请求分流到对应 Service。
  • 如果不需要对外暴露(比如内部调用),可以只有 Deployment + Service,不需要 Ingress。

总结

Deployment 管"有多少个 Pod 且稳定可用",Service 管"集群内怎么稳定访问这组 Pod",Ingress 管"集群外流量怎么按域名/路径进来",三者按 外部 → Ingress → Service → Deployment 管辖的 Pod 串成完整链路。

ReplicaSet 和 Deployment 的区别。

Deployment 是上层控制器,内部就是靠 ReplicaSet 来保证副本数的:Deployment 管理一个或多个 ReplicaSet,ReplicaSet 负责具体维持期望数量的 Pod。

核心区别

职责不同:ReplicaSet 只管"维持指定数量的 Pod 副本",Pod 少了就补、多了就删;Deployment 在此基础上还多了滚动更新、回滚、版本记录等发布能力,以及暂停/恢复更新的控制。

更新方式不同:直接改 Pod 模板时,ReplicaSet 只会原地替换 Pod 模板(不对存量 Pod 生效,通常需要手动滚动);Deployment 修改模板后会创建一个新 ReplicaSet,按策略逐步用新副本替换旧副本(滚动更新),完成后旧 ReplicaSet 缩到 0 但保留下来,用于回滚。

回滚能力不同:ReplicaSet 没有版本历史,回滚要靠手动操作;Deployment 自动记录每次更新的 ReplicaSet 历史,一条命令即可回滚到任意版本。

使用场景不同:日常几乎不用直接操作 ReplicaSet——Deployment 已封装好副本管理和发布能力;ReplicaSet 主要作为 Deployment 的内部实现被间接使用,单独创建 ReplicaSet 的场景极少(如需要精确控制且不需要发布功能时)。

总结

ReplicaSet 只保证"有几个 Pod 在跑",Deployment 在其上增加了滚动更新和回滚能力,并内部通过多个 ReplicaSet 管理版本——Deployment 是面向使用的控制器,ReplicaSet 是它里面的副本保障机制。

Service 的类型(ClusterIP, NodePort, LoadBalancer)及其使用场景。

ClusterIP(默认)

  • 是什么:为 Service 分配一个集群内部专用的虚拟 IP,只在集群内可达,外部无法直接访问。
  • 使用场景:服务之间的内部调用,如后端 API 给前端、微服务互相通信、数据库被应用访问。绝大多数内部服务用这种。

NodePort

  • 是什么:在 ClusterIP 基础上,在每个节点的固定端口(默认 30000-32767)上开放访问。外部通过 任意节点IP:NodePort 访问。
  • 使用场景:需要从集群外访问,但集群没有云负载均衡器(自建集群、裸机、测试环境),或临时对外暴露。缺点:端口范围受限、要自己记端口、节点多时无负载均衡可言。

LoadBalancer

  • 是什么:请求云厂商的负载均衡器(阿里云 SLB、AWS ELB、GCP LB 等),由云平台把外部流量分发到各节点,底层常由 NodePort 支撑。外部通过云负载均衡器提供的公网 IP/域名访问。
  • 使用场景:部署在公有云上、需要对外提供稳定入口的服务(Web 应用、对外 API)。是云上对外暴露的标准方式;在自建裸机集群中不受支持(需 MetalLB 之类的方案模拟)。

总结

ClusterIP 只给集群内部用;NodePort 在每台节点上开固定端口,无云环境时对外暴露的土办法;LoadBalancer 借助云厂商负载均衡器对外提供服务,是云上标准做法。Ingress 通常搭配 NodePort/LoadBalancer 类型 Service 使用,充当统一入口。

Read more

Linux 运维文件写入操作

Linux 运维文件写入操作

面向 Linux 运维/SRE 的文件写入操作参考手册。既讲每条命令怎么用,也讲它背后的机制(为什么这么行为),配真实输出示例与边界情况,最后给出常见陷阱速查表与标准动作模板。 目录 1. 总览:运维"写文件"的全景 2. Shell 重定向:机制与基础符号 3. 重定向顺序的坑:为什么 2>&1 > f 不行 4. 文件描述符与 exec:持久打开、交换、关闭 5. 防止误覆盖:noclobber 与 >| 6. Here 文档与 Here 字符串 7. tee:

By Admin
Linux 定时任务(附学习指南)

Linux 定时任务(附学习指南)

Linux 系统的定时任务(Scheduled Tasks)是运维和开发中最常用的自动化手段之一。系统中最核心的两套定时任务工具是 cron(定时器) 和 at(一次性任务队列)。下面从原理到实战逐层展开。 一、cron:周期性定时任务 1. cron 是什么 cron 是一个守护进程(daemon),负责在约定的时间点自动执行用户定义的任务。它对应的服务名在大多数发行版中是 crond,相关的命令和文件包括: 命令/文件 作用 crond cron 守护进程 crontab 用户管理自己的定时任务 /etc/crontab 系统级定时任务(注意有用户字段,格式不同) /etc/cron.d/ 系统级定时任务的扩展目录(每个文件类似 /etc/crontab) /etc/cron.hourly/、daily/、weekly/

By Admin
自定义你的DSH

自定义你的DSH

DSH 界面主题定制插件 给 DSH 换一套界面主题 把设计 Token 变成所见即所得的「DIY 主题」设置面板。配色、字体、背景、阴影、圆角,改完立即生效,不用写一行代码。 GitHub 仓库 更多内容 DeepSeek Harness(DSH)是 DeepSeek 推出的 AI 编程助手框架。通过 dsh web 启动浏览器界面,就能和 AI 对话、读写文件、执行命令、规划任务。功能强大,但外观一直比较朴素,默认只有浅色、深色、跟随系统三档。 我写了一个插件 dsh-ui-customizer,把界面定制做成了所见即所得的「主题设置面板」:进入 设置

By Admin
狩夜人笔记app

狩夜人笔记app

本地优先 · 离线可用 · 桌面笔记工具 狩夜人笔记 轻量 · 专注 · 个人笔记管理工具 把零散的知识,整理成自己的体系。用「主板块 / 子板块 / 文档」三级结构组织笔记,支持富文本与代码混排、语法高亮、标签、全文搜索、知识图谱与版本快照。数据保存在本地,不依赖云端。 下载安装 查看源码 核心功能 01 三级知识组织 主板块 / 子板块 / 文档,支持置顶、星标、标签与拖拽排序。 02 富文本与代码混排 TipTap 富文本编辑器,原生支持 Markdown;代码块按语言高亮、可随时切换。 03 标签与全文搜索 标签自定义颜色、侧边栏筛选;标题与正文全文检索,标签命中加权。 04 知识图谱 可视化文档之间的关联关系,直观发现知识之间的连接。

By Admin