What is cgroups ?

cgroup 比较有趣的地方是它没有提供任何的系统调用接口,所以你不能用 API Call 的方式使用 cgroup,实际上 cgroup 实现了 linux 虚拟文件系统 vfs,所以类似我们熟悉的 btfrs, ext4, 因此可以用类似文件系统的方式进行操作。 比如用 mount 命令看一下 linux 上挂载了哪些设备: # mount -t cgroup /dev/sda2 on / type ext4 (rw,relatime) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma) cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot) 可以看到, 第一行是磁盘 sda2 挂载在根目录 /, 它的类型是 ext4 后面几行是 cgroup 挂载在了目录 /sys/fs/cgroup/,类型是 cgroup 如果你的内核比较新的话,将看不到上面那些 cgroup 的行,而是只能看到最后这一行 cgroup2,这是因为新版本的内核使用了 cgroup v2 。 另外类似于 “net_cls”, “rdma” 这些都是 cgroup 子系统的名字,详见本文结尾的附录。...

2023-03-22 · Me

Latency numbers every programmer should know

这期水一篇文章。 网上有很多人流传 Jeff Dean 的这个 Latency numbers,我看过很多遍但总是记不住,干脆把它抄下来算了。 L1 cache reference ......................... 0.5 ns Branch mispredict ............................ 5 ns L2 cache reference ........................... 7 ns Mutex lock/unlock ........................... 25 ns Main memory reference ...................... 100 ns Compress 1K bytes with Zippy ............. 3,000 ns = 3 µs Send 2K bytes over 1 Gbps network ....... 20,000 ns = 20 µs SSD random read ........................ 150,000 ns = 150 µs Read 1 MB sequentially from memory ....

2022-10-02 · Me

使用 Kubernetes 遇到的一些问题和解决思路

update on 2022-05-21 今天在 homelab 的 k8s 集群上发生了同样的情况,我想删除一个 namespace,再确认已经把 namespace 里面所有其他资源都删除的情况下,namespace 始终是 Terminating, 找了很多资料,方法也众说纷纭 。 最后通过看 api-server log 发现原来又是 Unable to authenticate the request due to an error: x509: certificate has expired or is not yet valid root cause 还是我更新 cert 的时候又漏了某些步骤。 事情的起因是 k8s 的 cert 过期了,在目录 /etc/kubernetes/pki/ 下面的这些 cert 都与 k8s 的核心服务息息相关,因此 cert 过期了,整个 k8s 集群就停止服务了。 这个集群是 kubernetes 1.14, 因此需要运行几个命令完成更新,而 1.15 版本以上这个过程简化了不少。 由于之前已经 renew cert 两次了,因此正常按部就班几个操作就完事了,但是这个因为一点小疏忽,加上系统死机重启了一次,花了很多时间去恢复各种服务。 本文记录 debug 的过程中遇到的一些症状,以及后来发现的解决方法,为以后遇到类似问题提供思路。...

2022-04-27 · Me

Makefile 的几个语法坑

Makefile 和 Bash script 在使用的过程中有很多奇奇怪怪的坑,本文做一下纪录。 首先,有两个文件,一个叫 envs,里面定义了一个环境变量,比如 $ cat envs export GOPROXY="test.local" 第二个文件就是 Makefile ,假如我这样写 test: source ./envs echo ${GOPROXY} 所以,总的目标是,我希望在 Makefile 中导入另一个文件中事先定义好的环境变量。 然而这样的写法有很多问题。 source 命令找不到 加入直接运行 make, 很有可能你会看到这样的错误 $ make source ./envs make: source: Command not found 可是在 terminal 里面明明可以用 source 命令啊? 于是,第一个坑出现: source is a (non-POSIX) shell builtin, not an executable program on any conventional UNIX-like system. If source is changed to ., make changes its strategy; instead of trying to find and execute a program, it just passes the rule body to the system shell....

2021-09-26 · Me

bbolt 的设计与实现

关于 bbolt 的分析,网上已经有很多资料,本文只是对资料和源码的整理,主要是自己的学习笔记,文章最后的参考资料中有更多链接。 bbolt DB 整体组织 首先,bbolt 的一个文件是一个 DB,DB 中可以有多个 table, 每一个 table 是一个 B+ 树。而这个 table 在源码中就是 bucket, 整个 DB 就是一个大 bucket,它的子节点有多个 bucket。整体结构如图所示: 顶层 B+ 树,比较特殊,称为 root bucket,其所有叶子节点保存的都是子 bucket B+ 树根的 page id 其他 B+ 树,不妨称之为 data bucket,其叶子节点可能是正常用户数据,也可能是子 bucket B+ 树根的 page id。 这样,就清楚的知道了 bbolt 中 DB,table,和 data 是如何组织的了。 bbolt 的源码很简洁,主要功能分布在以下几个文件: bucket.go:对 bucket 操作的高层封装。包括 kv 的增删改查、子 bucket 的增删改查以及 B+ 树拆分和合并。 node.go:对 node 所存元素和 node 间关系的相关操作。节点内所存元素的增删、加载和落盘,访问孩子兄弟元素、拆分与合并的详细逻辑。 cursor.go:实现了类似迭代器的功能,可以在 B+ 树上的叶子节点上进行随意游走。 page.go: page 是磁盘上一个 4kb 页的表示,注意,相比 page,第二行提到的 node 表示的是内存里的结构。 db....

2021-07-07 · Me

Bigtable 论文阅读笔记

最近因为工作需要用到 Bigtable,而设计一个好的数据库 Schema 对于性能至关重要,因此想找一些资料看看别人是如何根据自身业务特点设计 schema 的。 在网上找到了一篇 GCP 自己的官方文档 , 里面提到了一些 best practice,也提到了哪些坑需要避免,然而还是看的云里雾里。 比如, Row keys to avoid Row keys that start with a timestamp. This will cause sequential writes to be pushed onto a single node, creating a hotspot. If you put a timestamp in a row key, you need to precede it with a high-cardinality value like a user ID to avoid hotspotting. Row keys that cause related data to not be grouped together....

2021-06-20 · Me

Unicode 字符编码

ASCII 我们熟悉的 ASCII 码可以说是字符编码的始祖了。它规定了常用的数字、符号、英文字母与二进制之间的对应关系。 ASCII 的缺点是字符集太少了,只能表示英文和数字,无法表示像中文,日文这样的符号。因此人们就设计出了 Unicode 字符集,囊括了几乎所有人类语言文字的符号。 Unicode Unicode 是一个字符集,而不是一种编码方式。 Unicode 相当于是给人类所有的符号一个独一无二的 ID,只要大家都是用这个 ID 表示字符,就不会出现乱码的问题。 因为 Unicode 是一个字符集,因此它不存在所谓的 “用几个字节表示 unicode” 这样的问题,这是具体的编码方式需要处理的事。 Unicode 把 ID 划分成了 17 组 (Plane),每组有 65536 个字符,编号可以用 U+[XX]YYYY 这样的形式表示,每一位是一个十六进制数字,其中 XX 代表组编号,从 0 到 0x10,一共17个,YYYY 代表这一组中的字符编号,一共 65536 个。 其中第 0 组叫 Basic Multilingual Plane,简称 BMP,它是 Unicode 中最基础和最常用的一部分,码点范围是U+0000 ~ U+FFFF,包含了我们常用的英文和汉字。 UFT-8 UTF-8 是 Unicode 具体的编码方式,除此之外还要 UTF-16, UTF-32 等等。 为什么需要编码方式呢? 直接用 Unicode 的 ID 不就行了吗? 因为我们需要节省存储空间。 UTF-8 是一种变长的编码方式,它可以使用 1-4 个字节表示一个符号,编码规则如下...

2020-08-16 · Me

用 Grafana 展示监控状态

运维或者 SRE 部门经常会弄一个大屏幕展示各种系统状态,看上去很好玩,于是我也用类似的开源软件监控一下家里的主机。 整个过程非常简单,主要是安装三个软件 Node exporter,Prometheus,Grafana。 Node exporter 既然要展示系统状态,那么第一步就是要获得系统的状态数据,比如 CPU 使用率,内存使用率,网络流量等。 Prometheus 官方提供了一个使用 go 语言编写的程序 node_exporter,直接下载项目主页上 release 里的二进制即可。node_exporter 最好直接安装在物理主机上,因为这样才能采集到最准确的数据。 运行 node_exporter 以后,会自动启动一个 http server 并且监听 9100 端口,如果有 client 过来访问, server 返回主机的监控信息。比如: $ curl http://localhost:9100/metrics node_network_transmit_packets_total{device="veth126cb08"} 28859 node_network_transmit_packets_total{device="veth1276a16"} 1383 node_network_transmit_packets_total{device="veth749c501"} 1.108492e+06 返回信息的格式是符合 Prometheus 定义的标准的,因此 Prometheus 能够处理并以简单的图标的形式展现这些数据。 看到这里大家应该不难想到,如果我自己写一个程序 HelloWorld,并且把程序的状态按照一定的格式导出,那么同样可以通过 Prometheus + Grafana 展现。 Prometheus Prometheus 是一个功能齐全的数据库,还提供了 PromSQL 语言方便用户查询,以及一个简单的网页前端。 最简单快捷的方式当然是启动一个容器,唯一需要注意的是把配置文件 prometheus.yml 挂载到容器的 /etc/prometheus/ 目录下。 $ docker run -d -p 9090:9090 \ -v /home/prometheus/:/etc/prometheus/ prom/prometheus 配置文件中需要在 scrape_configs 部分添加 noder exporter 的 IP 地址和端口。...

2020-02-02 · Me

CPU affinity

CPU affinity – CPU 亲和性,指进程更希望运行在哪个 CPU core 上。 指定 core 有什么好处呢? 比如,可以自己决定哪些程序可以独占 CPU 资源,保证这个程序性能的最大化; 指定 CPU 以后可以提高 Cache 的命中率,常用于一些对性能非常高要求的程序,例如 nginx。 命令行指令 taskset 在 Linux 系统中,我们可以用 taskset 命令指定一个进程运行在哪个核心上。 比如我们写一个程序用 while(1) 制造死循环,那么运行这个程序的时候 CPU 会飙到 100% 用以下这条命令运行这个程序 taskset -c 3 ./a.out 意思是把 a.out 运行在从 0 开始数起的第 3 个核心上。 于是,用 htop 命令查看,会看到第 4 个核 CPU 使用率是 100%。 编程的 API 那么在程序的代码里怎么用呢? 先来看看 glibc 提供的系统 API #include <sched.h> int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask); int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask); void CPU_CLR(int cpu, cpu_set_t *set); int CPU_ISSET(int cpu, cpu_set_t *set); void CPU_SET(int cpu, cpu_set_t *set); void CPU_ZERO(cpu_set_t *set); nginx 的 config 文件中,可以为每个工作进程绑定CPU...

2019-09-15 · Me

How TCP backlog works in Linux

原文: http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html 当一个应用程序使用 listen() 系统调用把一个 socket fd 设置成 LISTEN 状态时,也需要指定一个 backlog 值。通常我们可以认为这个 backlog 代表这个 socket fd 可以接受最大的连接请求数。 #include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog); 因为 TCP 的三次握手,在 server 端 accept() 系统调用返回,并且tcp 状态在变成 ESTABLISHED 之前,会有一个短暂的 SYN RECEIVED 状态。那么这个状态的 tcp 链接应该放在哪个 queue 里面呢? 单个 queue,其大小就是 listen() 参数 backlog。当一个 SYN 包到达时,server 返回一个 SYN/ACK 给 client,并且把这个链接放入 queue。当 client 的 ACK 到达时,TCP 的状态变成 ESTABLISHED。这就意味着这一个 queue 有两种不同的状态:SYN RECEIVED 和 ESTABLISHED。只有在 ESTABLISHED 状态的链接才能被 accept() 返回给用户程序。...

2019-08-15 · Me