跳转至

博客

数字调制

背景

最近在学习 802.11,需要学习很多数字调制相关的知识,因此自学了一下通信原理。

在 Linux 上使用 Netgear A6210 USB 无线网卡

背景

最近要让一台 Linux 机器连接无线网,所以要买一个对 Linux 支持比较好的 USB 无线网卡。以前曾经用过一些 USB 无线网卡,但对 Linux 的支持大多不好,要么是需要 out of tree module,要么就忽然不能工作。因此前期的调研十分重要。

挑选 USB 无线网卡

在调研的时候,发现了 morrownr/USB-WiFi 仓库,里面总结了一些 Linux 支持比较好的 USB 无线网卡,由于是外国人写的,所以里面很多型号在国内都买不到,但实际上 USB 无线网卡的芯片组一般就是那些,所以需要先确定芯片组,再根据芯片组找对应的 USB 无线网卡。

Transport Layer Interface 考古

Transport Layer Interface

现在网络编程主要采用的是 BSD Sockets API,但实际上当年还有另一套 API,就是 TLI(Transport Layer Interface),后来 BSD Sockets 胜出,进入了 POSIX 标准,TLI 后面也标准化为了 XTI,现在可以在部分 Unix 系统中找到。TLI/XTI 的使用方法和 Sockets API 有些类似,但是比较特别的一点在于,Sockets API 第一步是 socket 调用,传的参数就决定了这是 TCP 还是 UDP 还是其他什么协议,而 TLI 是通过打开不同的设备文件来进行区分:

int fd = t_open("/dev/udp", O_RDWR, NULL);

比如 TCP 就是 /dev/tcp,UDP 就是 /dev/udp,同理还有 /dev/icmp 等等。这颇有 Unix 的哲学:everything is a file。而 BSD Sockets API 则是有对应的系统调用,libc 基本不需要做什么事情。

OpenBSD xonly 实现原理

背景

最近看到 xonly status,看到 OpenBSD 最近在实现 xonly,也就是让一些页只能执行,不能读不能写。以往类似的做法是 W^X,也就是可以执行的时候不能写,可以写的时候不能执行。显然,xonly 是更加严格的,连读都不可以。查了一下历史,W^X 最早也是在 OpenBSD 中实现的,说不定以后 xonly 也会被各个操作系统实现。

Solaris 11.4 安装

下载安装镜像

访问 https://www.oracle.com/solaris/solaris11/downloads/solaris-downloads.html,点击下载,登录后跳转到一个新的页面。在 Platform 下拉框选择 x86,会出现一系列可以下载的文件。以 11.4.42.111.0 为例,需要下载的是:V1019840-01.iso Oracle Solaris 11.4.42.111.0 Interactive Text Install ISO (x86) for (Oracle Solaris on x86-64 (64-bit)), 890.5 MB。可以直接在浏览器中下载,也可以点击网页中的 WGET Options,用 wget 脚本下载。

下载以后,挂载 ISO 到虚拟机,正常按照指示进行安装。

FreeBSD/NetBSD/OpenBSD/DragonFlyBSD Cookbook

背景

最近在维护 lsof 的时候,需要在 FreeBSD/NetBSD/OpenBSD/DragonFlyBSD 上进行开发和测试,于是就装了虚拟机,特此记录我在使用过程中,与 Linux 不一样的一些常用 FreeBSD/NetBSD/OpenBSD/DragonFlyBSD 命令。

CHI 学习笔记

本文的内容已经整合到知识库中。

CHI 介绍

CHI 协议是 AMBA 5 标准中的缓存一致性协议,前身是 ACE 协议。最新的 CHI 标准可以从 AMBA 5 CHI Architecture Specification 处下载。

相比 AXI,CHI 更加复杂,进行了分层:协议层,物理层和链路层。因此,CHI 适用于片上网络,支持根据 Node ID 进行路由,而不像 AXI 那样只按照物理地址进行路由。CHI 的地位就相当于 Intel 的环形总线。CHI 也可以桥接到 CCIX 上,用 CCIX 连接 SMP 的的多个 Socket,或者连接支持 CCIX 的显卡等等。

Intel 处理器

Xeon 系列

命名方式

  • 第一位数字:8-9 对应 Platinum,5-6 对应 Gold,4 对应 Silver,3 对应 Bronze
  • 第二位数字:对应代次,1 对应 1st Generation,2 对应 2nd Generation,依此类推
  • 第三位第四位数字:一般越大性能越好
  • 后缀:H/L/M/N/P/Q/S/T/U/V/Y/Y+/+
  • L:大内存
  • M:媒体/大内存
  • N:网络
  • P:虚拟化,IaaS
  • Q: 水冷
  • S:存储/搜索
  • T:长寿命
  • U:单插槽
  • V:虚拟化,SaaS
  • Y: Speed Select

MIT 6.824 Distributed Systems 学习笔记

背景

本来打算去年上分布式系统课的,但是由于时间冲突没有选,今年想上的时候课程又没有开,因此利用寒假时间自学 MIT 6.824 Distributed Systems 课程 Spring 2022Archive),跟着看视频,Lecture Notes 还有论文,同时也完成课程的实验。在这里分享一下我在学习过程中的一些笔记和感悟。有趣的是,MIT 6.824 Spring 2023 年把课号改成了 6.5840,类似地 6.828 OS 也改成了 6.1810。

AMD 处理器

Ryzen 系列

注:下表中省略了 PRO 前缀,部分型号有带 PRO 和不带 PRO 的版本,部分型号仅有带 PRO 的版本,部分型号没有带 PRO 的版本。

Ryzen 5000

代号 用途 核显 插槽 微架构 型号
Vermeer 桌面 AM4 Zen 3 5950X/5945/5900(X)/5845/5800(X(3D))/5700(X(3D))/5645/5600(X(3D))
Chagall 工作站 sWRX8 Zen 3 5995WX/5975WX/5965WX/5955WX/5945WX
Cezanne 桌面 GCN5 AM4 Zen 3 5750G(E)/5700G(E)/5650G(E)/5600G(E)/5600GT/5500(GT)/5350G(E)/5300G(E)
Cezanne 笔记本 GCN5 FP6 Zen 3 5980HX/5980HS/5900HX/5900HS/5800H(S)/5800U/5600H(S)/5600U/5560U/5400U
Barceló 笔记本 GCN5 FP6 Zen 3 5825U/5825C/5625U/5625C/5425U/5425C/5125C
Lucienne 笔记本 GCN5 FP6 Zen 2 5700U/5500U/5300U

注:Ryzen 5 5500 虽然代号是 Cezanne,但是去掉了核显。

ACPI 学习笔记

标准

ACPI 标准可以从官网下载。

ACPI 的表现形式为一颗树加若干个表,表的结构比较规整,里面每个字段都有固定的含义。树的结点可能是属性,或者是一些函数。操作系统可以操作上面的属性,调用 ACPI 中的函数,来进行一些硬件相关的操作。ACPI 一般与主板密切相关,主板厂家配置好 ACPI 后,操作系统就不需要给每个主板都写一遍代码了。

InfiniBand 学习笔记

本文的内容已经整合到知识库中。

概览

InfiniBand 的网络分为两层,第一层是由 End Node 和 Switch 组成的 Subnet,第二层是由 Router 连接起来的若干个 Subnet。有点类似以太网以及 IP 的关系,同一个二层内通过 MAC 地址转发,三层间通过 IP 地址转发。

在 IB 网络中,End Node 一般是插在结点上的 IB 卡(Host Channel Adapter,HCA)或者是存储结点上的 Target Channel Adapter。End Node 之间通过 Switch 连接成一个 Subnet,由 Subnet Manager 给每个 Node 和 Switch 分配 Local ID,同一个 Subnet 中通过 LID(Local ID)来路由。但是 LID 位数有限,为了进一步扩展,可以用 Router 连接多个 Subnet,此时要通过 GID(Global ID)来路由。

升级 Mellanox 网卡固件

背景

最近发现有一台机器,插上 ConnectX-4 IB 网卡后,内核模块可以识别到设备,但是无法使用,现象是 ibstat 等命令都看不到设备。降级 OFED 从 5.8 到 5.4 以后问题消失,所以认为可能是新的 OFED 与比较旧的固件版本有兼容性问题,所以尝试升级网卡固件。升级以后,问题就消失了。

安装 MFT

首先,在 https://network.nvidia.com/products/adapter-software/firmware-tools/ 下载 MFT,按照指示解压,安装后,启动 mst 服务,就可以使用 mlxfwmanager 得到网卡的型号以及固件版本:

Device Type: ConnectX4
Description: Mellanox ConnectX-4 Single Port EDR PCIE Adapter LP
PSID:        DEL2180110032
Versions:    Current
  FW         12.20.1820

升级固件

从 PSID 可以看到,这是 DELL OEM 版本的网卡,可以在 https://network.nvidia.com/support/firmware/dell/ 处寻找最新固件,注意需要保证 PSID 一致,可以找到这个 PSID 的 DELL 固件地址:https://www.mellanox.com/downloads/firmware/fw-ConnectX4-rel-12_28_4512-06W1HY_0JJN39_Ax-FlexBoot-3.6.203.bin.zip。

下载以后,解压,然后就可以升级固件:

mlxfwmanager -u -i fw-ConnectX4-rel-12_28_4512-06W1HY_0JJN39_Ax-FlexBoot-3.6.203.bin

升级以后重启就工作了。

考虑到类似的情况之后还可能发生,顺便还升级了其他几台机器的网卡,下面是一个例子:

Device Type: ConnectX4
Description: ConnectX-4 VPI adapter card; FDR IB (56Gb/s) and 40GbE; dual-port QSFP28; PCIe3.0 x8; ROHS R6
PSID:        MT_2170110021
Versions:    Current
  FW         12.25.1020

注意这里的 PSID 是 MT_ 开头,说明是官方版本。这个型号可以在 https://network.nvidia.com/support/firmware/connectx4ib/ 找到最新的固件,注意 PSID 要正确,可以找到固件下载地址 https://www.mellanox.com/downloads/firmware/fw-ConnectX4-rel-12_28_2006-MCX454A-FCA_Ax-UEFI-14.21.17-FlexBoot-3.6.102.bin.zip。用同样的方法更新即可。

还有一个 ConnectX-3 的例子:

Device Type: ConnectX3
Description: ConnectX-3 VPI adapter card; single-port QSFP; FDR IB (56Gb/s) and 40GigE; PCIe3.0 x8 8GT/s; RoHS R6
PSID:        MT_1100120019
Versions:    Current
  FW         2.36.5150

ConnectX-3 系列的网卡固件可以在 https://network.nvidia.com/support/firmware/connectx3ib/ 找,根据 PSID,可以找到固件下载地址是 http://www.mellanox.com/downloads/firmware/fw-ConnectX3-rel-2_42_5000-MCX353A-FCB_A2-A5-FlexBoot-3.4.752.bin.zip。

小结

如果遇到 Mellanox 网卡能识别 PCIe,但是不能使用,可以考虑降级 OFED 或者升级网卡固件。

可以用 mlxfwmanager 查看 PSID 和更新固件。根据 PSID,判断是 OEM(DELL)版本还是官方版本。如果是 OEM 版本,要到对应 OEM 的固件下载地址找,例如 https://network.nvidia.com/support/firmware/dell/;如果是官方版,在 https://network.nvidia.com/support/firmware/firmware-downloads/ 找。

CXL 学习笔记

本文的内容已经整合到知识库中。

背景

前段时间学习了 PCIe,趁此机会,进一步学习一下密切相关的 CXL。

CXL 的标准是公开下载的:https://www.computeexpresslink.org/download-the-specification,我目前参考的是 2022 年 8 月 1 日的 CXL 3.0 版本。

在 GNURadio Companion 中收听 FM 广播

背景

以前买过 RTL-SDR,用 Gqrx 做过收音机,当时还给 Homebrew 尝试提交过几个 sdr 相关的 pr,但是限于知识的缺乏,后来就没有再继续尝试了。

前两天,@OceanS2000 讲了一次 Tunight: 高级收音机使用入门,又勾起了我的兴趣,所以我来尝试一下在 GNURadio Companion 中收听 FM 广播电台。

我没有上过无线电相关课程,所以下面有一些内容可能不正确或者不准确。

ESXi 配置 LACP 链路聚合

背景

给 ESXi 接了两路 10Gbps 的以太网,需要用 LACP 来聚合。ESXi 自己不能配置 LACP,需要配合 vCenter Server 的 Distributed Switch 来配置。

用 Nix 编译 Rust 项目

背景

Rust 项目一般是用 Cargo 管理,但是它的缺点是每个项目都要重新编译一次所有依赖,硬盘空间占用较大,不能跨项目共享编译缓存。调研了一下,有若干基于 Nix 的 Rust 构建工具:

  • cargo2nix: https://github.com/cargo2nix/cargo2nix
  • carnix: 不再更新
  • crane: https://github.com/ipetkov/crane
  • crate2nix: https://github.com/kolloch/crate2nix
  • naersk: https://github.com/nix-community/naersk
  • nocargo: https://github.com/oxalica/nocargo

下面我分别来尝试一下这几个工具的使用。

invalid date 报错与时区的关系

背景

最近在验题的时候,@HarryChen 发现了一个现象:

$ date -d "1919-04-13"
date: invalid date ‘1919-04-13’
$ TZ=UTC date -d "1919-04-13"
Sun Apr 13 00:00:00 UTC 1919

也就是说,这个现象与时区有关,那么为啥 1919-04-13 是一个不合法的日期呢?

时区

实际上,对于某一个时区来说,有的时间是不存在的,最常见的就是夏令时。在 Timezone DB 里可以看到,恰好在 1919 年 4 月 13 日发生了一次 UTC+8 到 UTC+9 的变化,因此零点变成了一点,就变成了不合法的日期。

这个数据,实际上保存在 tzdata 中,可以用 zdump 工具查看:

$ tzdata -v Asia/Shanghai
Asia/Shanghai  Fri Dec 13 20:45:52 1901 UTC = Sat Dec 14 04:45:52 1901 CST isdst=0
Asia/Shanghai  Sat Dec 14 20:45:52 1901 UTC = Sun Dec 15 04:45:52 1901 CST isdst=0
Asia/Shanghai  Sat Apr 12 15:59:59 1919 UTC = Sat Apr 12 23:59:59 1919 CST isdst=0
Asia/Shanghai  Sat Apr 12 16:00:00 1919 UTC = Sun Apr 13 01:00:00 1919 CDT isdst=1
Asia/Shanghai  Tue Sep 30 14:59:59 1919 UTC = Tue Sep 30 23:59:59 1919 CDT isdst=1
Asia/Shanghai  Tue Sep 30 15:00:00 1919 UTC = Tue Sep 30 23:00:00 1919 CST isdst=0
Asia/Shanghai  Fri May 31 15:59:59 1940 UTC = Fri May 31 23:59:59 1940 CST isdst=0
Asia/Shanghai  Fri May 31 16:00:00 1940 UTC = Sat Jun  1 01:00:00 1940 CDT isdst=1
Asia/Shanghai  Sat Oct 12 14:59:59 1940 UTC = Sat Oct 12 23:59:59 1940 CDT isdst=1
Asia/Shanghai  Sat Oct 12 15:00:00 1940 UTC = Sat Oct 12 23:00:00 1940 CST isdst=0
Asia/Shanghai  Fri Mar 14 15:59:59 1941 UTC = Fri Mar 14 23:59:59 1941 CST isdst=0
Asia/Shanghai  Fri Mar 14 16:00:00 1941 UTC = Sat Mar 15 01:00:00 1941 CDT isdst=1
Asia/Shanghai  Sat Nov  1 14:59:59 1941 UTC = Sat Nov  1 23:59:59 1941 CDT isdst=1
Asia/Shanghai  Sat Nov  1 15:00:00 1941 UTC = Sat Nov  1 23:00:00 1941 CST isdst=0
Asia/Shanghai  Fri Jan 30 15:59:59 1942 UTC = Fri Jan 30 23:59:59 1942 CST isdst=0
Asia/Shanghai  Fri Jan 30 16:00:00 1942 UTC = Sat Jan 31 01:00:00 1942 CDT isdst=1
Asia/Shanghai  Sat Sep  1 14:59:59 1945 UTC = Sat Sep  1 23:59:59 1945 CDT isdst=1
Asia/Shanghai  Sat Sep  1 15:00:00 1945 UTC = Sat Sep  1 23:00:00 1945 CST isdst=0
Asia/Shanghai  Tue May 14 15:59:59 1946 UTC = Tue May 14 23:59:59 1946 CST isdst=0
Asia/Shanghai  Tue May 14 16:00:00 1946 UTC = Wed May 15 01:00:00 1946 CDT isdst=1
Asia/Shanghai  Mon Sep 30 14:59:59 1946 UTC = Mon Sep 30 23:59:59 1946 CDT isdst=1
Asia/Shanghai  Mon Sep 30 15:00:00 1946 UTC = Mon Sep 30 23:00:00 1946 CST isdst=0
Asia/Shanghai  Mon Apr 14 15:59:59 1947 UTC = Mon Apr 14 23:59:59 1947 CST isdst=0
Asia/Shanghai  Mon Apr 14 16:00:00 1947 UTC = Tue Apr 15 01:00:00 1947 CDT isdst=1
Asia/Shanghai  Fri Oct 31 14:59:59 1947 UTC = Fri Oct 31 23:59:59 1947 CDT isdst=1
Asia/Shanghai  Fri Oct 31 15:00:00 1947 UTC = Fri Oct 31 23:00:00 1947 CST isdst=0
Asia/Shanghai  Fri Apr 30 15:59:59 1948 UTC = Fri Apr 30 23:59:59 1948 CST isdst=0
Asia/Shanghai  Fri Apr 30 16:00:00 1948 UTC = Sat May  1 01:00:00 1948 CDT isdst=1
Asia/Shanghai  Thu Sep 30 14:59:59 1948 UTC = Thu Sep 30 23:59:59 1948 CDT isdst=1
Asia/Shanghai  Thu Sep 30 15:00:00 1948 UTC = Thu Sep 30 23:00:00 1948 CST isdst=0
Asia/Shanghai  Sat Apr 30 15:59:59 1949 UTC = Sat Apr 30 23:59:59 1949 CST isdst=0
Asia/Shanghai  Sat Apr 30 16:00:00 1949 UTC = Sun May  1 01:00:00 1949 CDT isdst=1
Asia/Shanghai  Fri May 27 14:59:59 1949 UTC = Fri May 27 23:59:59 1949 CDT isdst=1
Asia/Shanghai  Fri May 27 15:00:00 1949 UTC = Fri May 27 23:00:00 1949 CST isdst=0
Asia/Shanghai  Sat May  3 17:59:59 1986 UTC = Sun May  4 01:59:59 1986 CST isdst=0
Asia/Shanghai  Sat May  3 18:00:00 1986 UTC = Sun May  4 03:00:00 1986 CDT isdst=1
Asia/Shanghai  Sat Sep 13 16:59:59 1986 UTC = Sun Sep 14 01:59:59 1986 CDT isdst=1
Asia/Shanghai  Sat Sep 13 17:00:00 1986 UTC = Sun Sep 14 01:00:00 1986 CST isdst=0
Asia/Shanghai  Sat Apr 11 17:59:59 1987 UTC = Sun Apr 12 01:59:59 1987 CST isdst=0
Asia/Shanghai  Sat Apr 11 18:00:00 1987 UTC = Sun Apr 12 03:00:00 1987 CDT isdst=1
Asia/Shanghai  Sat Sep 12 16:59:59 1987 UTC = Sun Sep 13 01:59:59 1987 CDT isdst=1
Asia/Shanghai  Sat Sep 12 17:00:00 1987 UTC = Sun Sep 13 01:00:00 1987 CST isdst=0
Asia/Shanghai  Sat Apr 16 17:59:59 1988 UTC = Sun Apr 17 01:59:59 1988 CST isdst=0
Asia/Shanghai  Sat Apr 16 18:00:00 1988 UTC = Sun Apr 17 03:00:00 1988 CDT isdst=1
Asia/Shanghai  Sat Sep 10 16:59:59 1988 UTC = Sun Sep 11 01:59:59 1988 CDT isdst=1
Asia/Shanghai  Sat Sep 10 17:00:00 1988 UTC = Sun Sep 11 01:00:00 1988 CST isdst=0
Asia/Shanghai  Sat Apr 15 17:59:59 1989 UTC = Sun Apr 16 01:59:59 1989 CST isdst=0
Asia/Shanghai  Sat Apr 15 18:00:00 1989 UTC = Sun Apr 16 03:00:00 1989 CDT isdst=1
Asia/Shanghai  Sat Sep 16 16:59:59 1989 UTC = Sun Sep 17 01:59:59 1989 CDT isdst=1
Asia/Shanghai  Sat Sep 16 17:00:00 1989 UTC = Sun Sep 17 01:00:00 1989 CST isdst=0
Asia/Shanghai  Sat Apr 14 17:59:59 1990 UTC = Sun Apr 15 01:59:59 1990 CST isdst=0
Asia/Shanghai  Sat Apr 14 18:00:00 1990 UTC = Sun Apr 15 03:00:00 1990 CDT isdst=1
Asia/Shanghai  Sat Sep 15 16:59:59 1990 UTC = Sun Sep 16 01:59:59 1990 CDT isdst=1
Asia/Shanghai  Sat Sep 15 17:00:00 1990 UTC = Sun Sep 16 01:00:00 1990 CST isdst=0
Asia/Shanghai  Sat Apr 13 17:59:59 1991 UTC = Sun Apr 14 01:59:59 1991 CST isdst=0
Asia/Shanghai  Sat Apr 13 18:00:00 1991 UTC = Sun Apr 14 03:00:00 1991 CDT isdst=1
Asia/Shanghai  Sat Sep 14 16:59:59 1991 UTC = Sun Sep 15 01:59:59 1991 CDT isdst=1
Asia/Shanghai  Sat Sep 14 17:00:00 1991 UTC = Sun Sep 15 01:00:00 1991 CST isdst=0
Asia/Shanghai  Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 11:14:07 2038 CST isdst=0
Asia/Shanghai  Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 11:14:07 2038 CST isdst=0

可以看到,它列出来了历史上 Asia/Shanghai 时区的变化历史。具体的历史,可以查看 中国时区

此外,历史上,从儒略历到格里高利历的演变过程,也出现了一段“不存在”的日期,如 Setting October 14 ,1582 fails in java.sql.Date

Ceph Cookbook

概念

  • OSD:负责操作硬盘的程序,一个硬盘一个 OSD
  • MON:管理集群状态,比较重要,可以在多个节点上各跑一个
  • MGR:监测集群状态
  • RGW(optional):提供对象存储 API
  • MDS(optional):提供 CephFS

使用 Ceph 做存储的方式:

  1. librados: 库
  2. radosgw: 对象存储 HTTP API
  3. rbd: 块存储
  4. cephfs: 文件系统

认证

Ceph 客户端认证需要用户名 + 密钥。默认情况下,用户名是 client.admin,密钥路径是 /etc/ceph/ceph.用户名.keyringceph --user abc 表示以用户 client.abc 的身份访问集群。

用户的权限是按照服务类型决定的。可以用 ceph auth ls 显示所有的用户以及权限:

$ ceph auth ls
osd.0
        key: REDACTED
        caps: [mgr] allow profile osd
        caps: [mon] allow profile osd
        caps: [osd] allow *
client.admin
        key: REDACTED
        caps: [mds] allow *
        caps: [mgr] allow *
        caps: [mon] allow *
        caps: [osd] allow *

可以看到,osd.0 对 OSD 有所有权限,对 mgr 和 mon 都只有 osd 相关功能的权限;client.admin 有所有权限。profile 可以认为是预定义的一些权限集合。

新建用户并赋予权限:

ceph auth get-or-create client.abc mon 'allow r'

修改权限:

ceph auth caps client.abc mon 'allow rw'

获取权限:

ceph auth get client.abc

删除用户:

ceph auth print-key client.abc

OSD

管理 OSD 实际上就是管理存储数据的硬盘。

查看状态:

ceph osd stat

显示有多少个在线和离线的 OSD。

ceph osd tree

显示了存储的层级,其中 ID 非负数是实际的 OSD,负数是其他层级,例如存储池,机柜,主机等等。

CRUSH

CRUSH 是一个算法,指定了如何给 PG 分配 OSD,到什么类型的设备,确定它的 failure domain 等等。例如,如果指定 failure domain 为 host,那么它就会分配到不同 host 上的 osd,这样一个 host 挂了不至于全军覆没。类似地,还可以设定更多级别的 failure domain,例如 row,rack,chassis 等等。

OSD 可以设置它的 CRUSH Location,在 ceph.conf 中定义。

为了配置数据置放的规则,需要设置 CRUSH Rule。

列举 CRUSH Rule:

ceph osd crush rule ls
ceph osd crush rule dump

查看 CRUSH 层级:

ceph osd crush tree --show-shadow

在里面可能会看到 default~ssd,它指的意思就是只保留 default 下面的 ssd 设备。

文本形式导出 CRUSH 配置:

ceph osd getcrushmap | crushtool -d - -o crushmap
cat crushmap

可以看到 Rule 的定义,如:

# simple replicated
rule replicated_rule {
        id 0
        # a replicated rule
        type replicated
        # iterate all devices of "default"
        step take default
        # select n osd with failure domain "osd"
        # firstn: continuous
        step chooseleaf firstn 0 type osd
        step emit
}

# erasure on hdd
rule erasure-hdd {
        id 4
        # an erasure rule
        type erasure
        # try more times to find a good mapping
        step set_chooseleaf_tries 5
        step set_choose_tries 100
        # iterate hdd devices of "default", i.e. "default~hdd"
        step take default class hdd
        # select n osd with failure domain "osd"
        # indep: replace failed osd with another
        step choose indep 0 type osd
        step emit
}

# replicated on hdd
rule replicated-hdd-osd {
        id 5
        # a replicated rule
        type replicated
        # iterate hdd devices of "default", i.e. "default~hdd"
        step take default class hdd
        # select n osd with failure domain "osd"
        # firstn: continuous
        step choose firstn 0 type osd
        step emit
}

# replicated on different hosts
rule replicated-host {
        id 6
        # a replicated rule
        type replicated
        # iterate all devices of "default"
        step take default
        # select n osd with failure domain "host"
        # firstn: continuous
        step chooseleaf firstn 0 type host
        step emit
}

# replicate one on ssd, two on hdd
rule replicated-ssd-primary {
        id 7
        # a replicated rule
        type replicated

        # iterate ssd devices of "default"
        step take default class ssd
        step chooseleaf firstn 1 type host
        step emit

        # iterate hdd devices of "default"
        step take default class hdd
        step chooseleaf firstn 2 type host
        step emit
}

choose 和 chooseleaf 的区别是,前者可以 choose 到中间层级,例如先选择 host,再在 host 里面选 osd;而 chooseleaf 是直接找到 osd。所以 choose type osdchooseleaf type osd 是等价的。

如果这个搜索条件比较复杂,例如找到了某一个 host,里面的 osd 个数不够,就需要重新搜。

新建一个 Replicated CRUSH Rule:

# root=default, failure domain=osd
ceph osd crush rule create-replicated xxx default osd
# root=default, failure domain=host, class=ssd
ceph osd crush rule create-replicated yyy default host ssd

如果指定了 device class,它只会在对应类型的设备上存储。

Pool

Pool 是存储池,后续的 RBD/CephFS 功能都需要指定存储池来工作。

创建存储池:

ceph osd pool create xxx
ceph osd pool create PG_NUM

为了性能考虑,可以设置 PG(Placement Group)数量。默认情况下,会创建 replicated 类型的存储池,也就是会存多份,类似 RAID1。也可以设置成 erasure 类型的存储池,类似 RAID5。

每个 Placement Group 里的数据会保存在同一组 OSD 中。数据通过 hash,会分布在不同的 PG 里。

列举所有的存储池:

ceph osd lspools

查看存储池的使用量:

rados df

存储池的 IO 状态:

ceph osd pool stats

对存储池做快照:

ceph osd mksnap xxx snap-xxx-123

PG

PG 是数据存放的组,每个对象都会放到一个 PG 里面,而 PG 会决定它保存到哪些 OSD 上(具体哪些 OSD 是由 CRUSH 决定的)。PG 数量只有一个的话,那么一个 pool 的所有数据都会存放在某几个 OSD 中,一旦这几个 OSD 都不工作了,那么整个 pool 的数据都不能访问了。PG 增多了以后,就会分布到不同的 OSD 上,并且各个 OSD 的占用也会比较均匀。

查看 PG 状态:

ceph pg dump
Auto Scale

PG 数量可以让集群自动调整:

ceph osd pool set xxx pg_autoscale_mode on

设置 autoscale 目标为每个 OSD 平均 100 个 PG:

ceph config set global mon_target_pg_per_osd 100

全局 autoscale 开关:

# Enable
ceph osd pool unset noautoscale
# Disable
ceph osd pool set unautoscale
# Read
ceph osd pool get noautoscale

查看 autoscale 状态:

ceph osd pool autoscale-status

如果没有显示,说明 autoscale 没有工作,可能的原因是,部分 pool 采用了指定 osd class 的 crush rule,例如指定了 hdd 盘,但是也有部分 pool 没有指定盘的类型,例如默认的 replicated_rule。这时候,把这些盘也设置成一个指定 osd class 的 crush rule 即可。

RBD

RBD 把 Ceph 暴露为块设备。

创建

初始化 Pool 用于 RBD:

rbd pool init xxx

为了安全性考虑,一般会为 RBD 用户创建单独的用户:

ceph auth get-or-create client.abc mon 'profile rbd' osd 'profile rbd pool=xxx' mgr 'profile rbd pool=xxx'

创建 RBD 镜像:

rbd create --size 1024 xxx/yyy

表示在 Pool xxx 上面创建了一个名字为 yyy 大小为 1024MB 的镜像。

状态

列举 Pool 里的镜像:

rbd ls
rbd ls xxx

默认的 Pool 名字是 rbd

查看镜像信息:

rbd info yyy
rbd info xxx/yyy

扩容

修改镜像的容量:

rbd resize --size 2048 yyy
rbd resize --size 512 yyy --allow-shrink

挂载

在其他机器挂载 RBD 的时候,首先要修改 /etc/ceph 下配置,确认有用户,密钥和 MON 的地址。

然后,用 rbd 挂载设备:

rbd device map xxx/yyy --id abc

以用户 abc 的身份挂载 Pool xxx 下面的 yyy 镜像。

这时候就可以在 /dev/rbd* 或者 /dev/rbd/ 下面看到设备文件了。

显示已经挂载的设备:

rbd device list

CephFS

创建

如果配置了编排器(Orchestrator),可以直接用命令:

ceph fs volume create xxx
创建一个名为 xxx 的 CephFS。

也可以手动创建:

ceph osd pool create xxx_data0
ceph osd pool create xxx_metadata
ceph fs new xxx xxx_metadata xxx_data0

这样就创建了两个 pool,分别用于存储元数据和文件数据。一个 CephFS 需要一个 pool 保存元数据,若干个 pool 保存文件数据。

创建了 CephFS 以后,相应的 MDS 也会启动。

状态

查看 MDS 状态:

ceph mds stat

客户端配置

在挂载 CephFS 之前,首先要配置客户端。

在集群里运行 ceph config generate-minimal-conf,它会生成一个配置文件:

$ ceph config generate-minimal-conf
# minimal ceph.conf for <fsid>
[global]
        fsid = <fsid>
        mon_host = [v2:x.x.x.x:3300/0,v1:x.x.x.x:6789/0]

把内容复制到客户端的 /etc/ceph/ceph.conf。这样客户端就能找到集群的 MON 地址和 FSID。

接着,我们在集群上给客户端创建一个用户:

ceph fs authorize xxx client.abc / rw

创建一个用户 abc,对 CephFS xxx 有读写的权限。把输出保存到客户端的 /etc/ceph/ceph.client.abc.keyring 即可。

挂载

挂载:

mount -t ceph abc@.xxx=/ MOUNTPOINT
# or
mount -t ceph abc@<fsid>.xxx=/ MOUNTPOINT
# or
mount -t ceph abc@<fsid>.xxx=/ -o mon_addr=x.x.x.x:6789,secret=REDACTED MOUNTPOINT
#or
mount -t ceph abc@.xxx=/ -o mon_addr=x.x.x.x:6789/y.y.y.y:6789,secretfile=/etc/ceph/xxx.secret MOUNTPOINT
# or
mount -t ceph -o name=client.abc,secret=REDACTED,mds_namespace=xxx MON_IP:/ MOUNTPOINT

以用户 client.abc 的身份登录,挂载 CepFS xxx 下面的 / 目录到 MOUNTPOINT。它会读取 /etc/ceph 下面的配置,如果已经 ceph.conf 写了,命令行里就可以不写。

fsid 指的不是 CephFS 的 ID,实际上是集群的 ID:ceph fsid

限额

CephFS 可以对目录进行限额:

setfattr -n ceph.quota.max_bytes -v LIMIT PATH
setfattr -n ceph.quota.max_files -v LIMIT PATH
getfattr -n ceph.quota.max_bytes PATH
getfattr -n ceph.quota.max_files PATH

限制目录大小和文件数量。LIMIT 是 0 的时候表示没有限制。

NFS

可以把 CephFS 或者 RGW 通过 NFS 的方式共享出去。

启动 NFS 服务:

ceph nfs cluster create xxx
ceph nfs cluster create xxx "host1,host2"

在主机上运行 NFS 服务器,NFS 集群的名字叫做 xxx。

查看 NFS 集群信息:

ceph nfs cluster info xxx

列举所有 NFS 集群:

ceph nfs cluster ls

NFS 导出 CephFS:

ceph nfs export create cephfs --cluster-id xxx --pseudo-path /a/b/c --fsname some-cephfs-name [--path=/d/e/f] [--client_addr y.y.y.y]

这样就导出了 CephFS 内的一个目录,客户端可以通过 NFS 挂载 /a/b/c 路径(pseudo path)来访问。可以设置客户端的 IP 访问权限。

这样在客户端就可以 mount:

mount -t nfs x.x.x.x:/a/b/c /mnt

RadosGW

RGW 提供了 S3 或者 OpenStack Swift 兼容的对象存储 API。

TODO

编排器

由于 Ceph 需要运行多个 daemon,并且都在不同的容器中运行,所以一般会跑一个系统级的编排器,用于新增和管理这些容器。

查看当前编排器:

$ ceph orch status
Backend: cephadm
Available: Yes
Paused: No

比较常见的就是 cephadm,安装的时候如果用了 cephadm,那么编排器也是它。

被编排的服务:

ceph orch ls

被编排的容器:

ceph orch ps

被编排的主机:

ceph orch host ls

添加新机器

首先,复制 /etc/ceph/ceph.pub 到新机器的 /root/.ssh/authorized_keys

接着,添加机器到编排器中:

ceph orch host add xxxx y.y.y.y

导出编排器配置:

ceph orch ls --export > cephadm.yaml

如果想让一些 daemon 只运行在部分主机上,可以修改:

# change
placement:
  host_pattern: '*'
# to
placement:
  host_pattern: 'xxx'

然后应用:

ceph orch apply -i cephadm.yaml

配置监控

添加监控相关的服务:

ceph orch apply node-exporter
ceph orch apply alertmanager
ceph orch apply prometheus
ceph orch apply grafana
ceph orch ps

然后就可以访问 Grafana 看到集群的状态。

单机集群

在集群只有单机的时候,由于默认 MGR 每个 host 只能有一个,所以会导致无法升级。

一种方法是在创建 cluster 的时候,传入 --single-host-defaults 参数,详见 Single host deployment

另一种方法是,动态修改 MGR 的 mgr_standby_modules 选项为 false

  1. 运行 ceph config set mgr mgr_standby_modules false
  2. 创建一个 mgr.yaml 文件:

    ```yaml
    service_type: mgr
    service_name: mgr
    placement:
      hosts:
        - YOUR_HOSTNAME_HERE
      count_per_host: 2
    ```
    
  3. 告诉 cephadm,让它在 YOUR_HOSTNAME_HERE 机器上部署两个 MGR:ceph orch apply -i mgr.yaml

  4. 这样就成功了,可以用 ceph orch ps 确认有两个 MGR,这样就可以升级 ceph 了

更新

使用容器编排器来升级:

ceph orch upgrade start --ceph-version x.x.x
ceph orch upgrade start --image quay.io/ceph/ceph:vx.x.x

如果 docker hub 上找不到 image,就从 quay.io 拉取。

查看升级状态:

ceph orch upgrade status
ceph -s

查看 cephadm 日志:

ceph -W cephadm

参考文档