前言最初 netbird 的 management/signal/coturn/relay 等服务是独立的，后来为了方便部署使用，统一成了一个 netbird-server，但也保留了独立部署服务的能力（提高性能和高可用），根据 官方文档 所述，relay/signal 均可以独立部署。我使用 netbird 主要为了国外服务器之间的互联，都有公网 IP，其实对 relay 服务器需求不是那么高，但是折腾的心按捺不住。同 Netbird Proxy + Nginx 部署记录 中所述，我需要基于 nginx 进行部署，不过 relay 倒是不像 proxy 有 TLS 透传要求，比较简单，随便记录一下吧。部署注意：当前如果指定了外部 relay 服务器的话，那 netbird-server 内置的 relay 服务器就不会生效了，预计后续会支持共存 (见 issue #5351)生成 authSecretopenssl rand -base64 32注意：该密钥生成一次即可，所有的 relay server 需要共享相同的密钥部署 relay 服务如前言所说，我需要使用 nginx 来处理 TLS，但是呢，官方文档提供的说明中，又是让 relay server 来处理 TLS，要么采用 ACME 申请证书，要么提供证书文件。但是看 relay server 的作用，本身提供的能力有两个：STUN 服务（UDP，用于 p2p 发现）Netbird Relay (quic/ws，http 服务，用于流量中转)就服务而言，没有必要非得要在 relay server 上进行 tls 终结。实际操作了下，确实如我所想。准备环境变量准备环境变量文件 relay.env，文件内容示例如下：NB_LOG_LEVEL=info # relay server 监听的地址，容器部署，该端口为容器内监听端口，保持 443 无所谓 NB_LISTEN_ADDRESS=:443 # 实际 client 需要连接的 relay 地址 NB_EXPOSED_ADDRESS=rels://relay.netbird.10101.io:443 # 生成的 authSecret NB_AUTH_SECRET=XprUtOMCVMdVYFdjLJqNl5mW+RUWyXjyJDgpoZhX0c8= # Embedded STUN (comma-separated for multiple ports, e.g., 3478,3479) NB_ENABLE_STUN=true NB_STUN_PORTS=3478这里去掉了官方示例中的证书相关环境变量，即不让 relay server 来处理 tls，仅对外提供 http 服务和 stun 服务。保证 NB_EXPOSED_ADDRESS 为实际 client 需要连接的地址即可。docker-compose依旧采用 docker-compose 进行部署，relay docker-compose.yaml 内容如下：services: relay: image: netbirdio/relay:latest container_name: netbird-relay restart: unless-stopped networks: [netbird] ports: # 监听在 127.0.0.1:33080，不占用 443 端口 - '127.0.0.1:33080:443' # 暴露所有的 NB_STUN_PORTS，具体定义在环境变量文件 relay.env 中 - '3478:3478/udp' env_file: # 使用环境变量文件 - relay.env volumes: - ./relay_data:/data logging: driver: "json-file" options: max-size: "500m" max-file: "2" networks: netbird: ipam: config: - subnet: 172.30.0.0/16 gateway: 172.30.0.1启动服务：docker compose up -d # 查看日志，确保正常启动并监听 docker compose logs relay服务正常启动后，就可以使用 nginx 反代服务了，这里就不赘述了。配置 netbird-serverrelay server 启动后，还需要在 netbird-server 的配置文件中增加 relay 的配置，才能正确下发给 client。编辑 config.yaml文件，增加 relay 和 stun 配置，示例如下： # External STUN servers stuns: # stun 地址 - uri: "stun:relay.netbird.10101.io:3478" proto: "udp" # External relay servers relays: addresses: # relay 地址 - "rels://relay.netbird.10101.io:443" # 生成的 authSecret secret: "XprUtOMCVMdVYFdjLJqNl5mW+RUWyXjyJDgpoZhX0c8=" credentialsTTL: "24h"然后重启 netbird-server: docker compose restart netbird-server正常的话，client 端就会接收到新的 relay 服务器了，可以通过 netbird status -d，如下：Relays: [stun:relay.netbird.10101.io:3478] is Available [rels://relay.netbird.10101.io:443] is Available via ws如果需要更多的 relay server 的话，重复上述步骤（注意使用同一 authSecret）即可。参考资料官方文档 set up external relays官方文档 scaling your self-hosted deployment
前言之前有用 wireguard 来做服务器间的互联（服务暴露），但是一直都是手动配置的，有些繁琐。遂有考虑服务器间的 full mesh 互联方案，偶然间了解到了 netbird，基于 wireguard 的零信任方案，对比 tailscale，服务端/客户端全开源，果断选择了它。netbird 的整个部署过程，早期还是比较繁琐的，主要还是 IDP 部分，坑很多，当初自建颇费了一番功夫。不过随着发展，目前部署已经相当方便了。之前想着写文章记录下的，但是太懒了搁置了（看我更新频率就知道了 (）。然后前段时间 netbird 更新了 reverse-proxy 功能，我感觉还是很方便的：基础反向代理功能，支持 L4/L7 代理；支持自动申请证书；相同的 proxy 域名自动组成 cluster，可以在不同区域部署多个 proxy 实例，结合分区域解析的 DNS 可以快速构建一个简单的类 CDN 的加速方案；支持增加各种认证，保护后端服务刚好，这两天又搞了个 netbird-proxy 实例，那就……记录下吧。限制反代 http/https 服务的话，由于 netbird-proxy 对外提供 HTTPS，且必须要在 netbird-proxy 上进行证书验证（它目前要么通过 ACME 自动申请，要么提供泛域名证书），所以 netbird-proxy 之前如果还有代理的话，必须进行 tls 透传。目前官方是要求使用 traefik，但是我习惯了使用 nginx，也有一些服务已经依托于 nginx 了，所以不想切换到 traefik，所以本文也是基于 nginx 做前置进行部署。另外，对于多个 proxy 实例，由于每个实例都是自助进行证书申请的，那么在需要反代的 service 很多、实例很多的时候，很大概率会达到 Lets‘ Encrypt 的速率限制。官方建议使用泛域名证书，但是却只能指定一个证书，我有多个域名，并不想合并到一个证书中，这个方法行不通。本来想了一个方案，一个主 proxy 负责证书申请，然后同步到其他 proxy 中，但是发现若关闭了 proxy 的自动证书申请，那它就自动要读取本地的泛域名证书，没有的话 proxy 压根就跑不起来了，路又被堵住了。 ~目前这个没想到什么好办法解决，暂时控制下 proxy 数量，看看后续 netbird 是否提供更好的方案~ 。官方在 BYOP 文档中 提到了多个 proxy 使用共享网络存储存放证书，会通过 cross-replica lock 来尽量确保一致性，但是 flock 在共享存储中并不可靠（取决于共享存储实现），感觉也不是很优秀的方案。[scode type="green" size=""]测试了下 NFSv4, flock 锁应该是有效的，暂时可以采用 NFS 共享 certs 目录来解决证书更新的问题。不过还是更希望官方能有一个中心化的证书管理方案，比如通过 management server 来统一负责证书的申请和分发[/scode]部署过程Nginx 透传 TLSnetbird-proxy 本身完全可以自己独立工作（监听 443 端口），但由于我有一些需求需要通过 nginx 实现，所以要考虑这俩共存。在限制一节中已经提到，netbird-proxy 需要其前的代理透传 TLS，但是 nginx 的 http 块是不支持 TLS 透传的，需要采用 stream 模块进行 L4 代理。但是，同时，也要确保 http 块的反代请求正常。所以，方案如下：使用 stream 模块，监听所有的 443 端口请求；根据 sni 进行分流：需要 nginx http 反代的走 nginx http 监听端口；需要 netbird proxy 反代的走 netbird proxy 监听端口具体的配置示例如下：# 省略部分内容，请勿直接复制使用，根据实际情况修改 # ========================================================================= # 1. STREAM 块 (负责 443 端口的四层 SNI 分流) # ========================================================================= stream { # 开启 SNI 预读 ssl_preread on; # 根据域名映射后端 map $ssl_preread_server_name $stream_map { netbird.10101.io nginx_http_backend; default netbird_proxy_backend; # 默认透传给 netbird-proxy } # 监听 443 端口 server { listen 443; listen [::]:443; proxy_pass $stream_map; proxy_protocol on; # 开启 proxy protocol，向后端保留客户端 IP proxy_connect_timeout 60s; proxy_timeout 30m; } # 后端：Netbird 代理 upstream netbird_proxy_backend { server 127.0.0.1:18443; } # 后端：本机的 HTTP/HTTPS 服务块 upstream nginx_http_backend { server 127.0.0.1:8443; } } # ========================================================================= # 2. HTTP 块 (负责七层业务处理，如证书解密、静态资源、反向代理) # ========================================================================= http { # 使用 real_ip 模块从 PROXY PROTOCOL 中提取真实的客户端 IP # 这样后端应用就能拿到真实的客户端 IP set_real_ip_from 127.0.0.1; real_ip_header proxy_protocol; # 业务服务器：接收来自 stream 块分流过来的请求 server { # 【重要】这里必须加上 ssl 和 proxy_protocol listen 8443 ssl proxy_protocol; listen [::]:8443 ssl proxy_protocol; server_name netbird.10101.io; } }需要特别注意的是 proxy protocol 的处理，否则 netbird 端的 access log 会缺失真实的客户端 IP。Netbird Proxy 部署netbird-proxy 部署就比较简单了，docker compose 一把梭。完整 docker-compose.ymlservices: proxy: image: netbirdio/reverse-proxy:latest container_name: netbird-proxy restart: unless-stopped networks: [netbird] ports: - 127.0.0.1:18443:8443 env_file: - ./proxy.env volumes: - ./certs:/certs depends_on: crowdsec: condition: service_healthy logging: driver: "json-file" options: max-size: "500m" max-file: "2" crowdsec: image: crowdsecurity/crowdsec:v1.7.8 container_name: netbird-crowdsec restart: unless-stopped networks: [netbird] environment: COLLECTIONS: crowdsecurity/linux volumes: - ./crowdsec/config:/etc/crowdsec - ./crowdsec/db:/var/lib/crowdsec/data healthcheck: test: ["CMD", "cscli", "lapi", "status"] interval: 10s timeout: 5s retries: 15 logging: driver: "json-file" options: max-size: "500m" max-file: "2" networks: netbird: ipam: config: - subnet: 172.30.0.0/16 gateway: 172.30.0.1如上，除了 proxy 外，还加了个 crowdsec service，下面具体说明下CrowdsecCrowdsec 是一款社区维护的威胁情报引擎，目前 netbird 集成了 crowdsec，可多提供一层保护，可以跟随 netbird proxy 一起部署。# 1. 启动 crowdsec docker compose up -d crowdsec # 2. 查看状态 docker compose exec crowdsec cscli lapi status # 3. 获取 key docker compose exec crowdsec cscli bouncers add netbird-proxy -o raw复制好生成的 API key，后续 proxy 注册需要用到Proxy创建 proxy token# 1. 生成新的 token docker exec -it netbird-server /go/bin/netbird-server --name "my-proxy1" --config <netbird-data-dir>/config.yaml token create # 2. 查看所有的 token docker exec -it netbird-server /go/bin/netbird-server --name "my-proxy1" --config <netbird-data-dir>/config.yaml token list # 3. 撤销 token docker exec -it netbird-server /go/bin/netbird-server --name "my-proxy1" --config <netbird-data-dir>/config.yaml token revoke <token-id>netbird 官方建议每个 proxy 实例使用不同的 token。配置环境变量配置 proxy 的环境变量文件 proxy.env，具体内容参考如下：# proxy 域名，cname 用 NB_PROXY_DOMAIN=proxy.netbird.10101.io NB_PROXY_TOKEN={生成的 proxy token} # netbird management 地址 NB_PROXY_MANAGEMENT_ADDRESS=https://netbird.10101.io:443 NB_PROXY_ALLOW_INSECURE=false NB_PROXY_ADDRESS=:8443 NB_PROXY_ACME_CERTIFICATES=true NB_PROXY_ACME_CHALLENGE_TYPE=tls-alpn-01 NB_PROXY_CERTIFICATE_DIRECTORY=/certs NB_PROXY_CROWDSEC_API_URL=http://crowdsec:8080 NB_PROXY_CROWDSEC_API_KEY={生成的 crowdsec API key} # Enable PROXY protocol to preserve client IPs through L4 proxies (TCP passthrough) NB_PROXY_PROXY_PROTOCOL=true # Trust Proxy's IP for PROXY protocol headers # NB_PROXY_TRUSTED_PROXIES=172.30.0.10 NB_PROXY_PRIVATE=true这里需要注意的是 NB_PROXY_PROXY_PROTOCOL，开启 proxy protocol 支持，确保 netbird 能正确显示访问者源 IP配置好后，启动即可：docker compose up -d proxy其他更多环境变量可参考 官方文档参考资料netbird 官方文档
机架整体迁移到新机房后，IP 地址发生变更，所有 ceph 节点都需要更新 IP 地址。由于操作时未能一一记录，只能凭记忆大致还原整个过程。本 Ceph 集群使用 cephadm 进行管理。[scode type="yellow" size=""]仅作个人折腾记录，生产环境请谨慎[/scode]原网络如下：public network：10.182.65.0/24cluster network: 10.90.90.0/24cluster network 在一个单独 vlan 中，可以不动，只需要修改 public network。最开始的思路是，10.182.65.0/24 网段保留（由于该网段无法公开访问，放在一个 vlan 中），增加新的 public network，这样假如可以访问到 10.182.65.0/24 网段，直接用该网段即可（因为该 vlan 所在链路带宽更大），无法访问到该网段则使用新的 public network。但是实际操作下来发现，虽然 osd 节点监听在 0.0.0.0，但实际上报给 MON 的只有一个 IP（可以通过 ceph osd dump 看到具体 IP），由于原网段和新网段无法互联，导致可能出现 ceph 客户端无法访问 osd 的情况。所以，最后只能全部替换掉原 public network，迁移后网络如下：public network：10.184.17.0/2410.184.18.0/24cluster network: 10.90.90.0/24环境准备将集群中所有主机原 public network 接口放入同一个 vlan 中，然后新增一个接口，配置新的 public network 网段 IP 地址启动 cluster，确保集群正常启动修改 HOST 地址将 host 的地址更新为新 IP 段ceph orch host set-addr <hostname> <new-addr>MON 节点MON 节点涉及到的 IP 地址配置文件主要有 3 个：/etc/ceph/ceph.conf/var/lib/ceph/{fsid}/config/ceph.conf/var/lib/ceph/{fsid}/{mon.id}/config迁移后比较棘手的就是 MON 节点，主要问题点：直接修改上述三个文件中的 IP 地址，然后启动 MON 节点，发现会有问题，主要原因是 MON 节点的节点信息维护在 monmap 中，直接修改配置文件不会改变 monmap 中的节点信息；尝试通过 monmaptool 工具来直接修改 monmap 中的节点信息，但是由于集群是通过 cephadm 管理的（podman 容器），monmap 注入时需要停止 mon 节点，但停止了 mon（容器）又无法进行进行注入了官方文档建议，通过新增一个 MON 节点再移除一个旧的 MON 节点的方式来迁移网络。由于不想新增虚拟机，所以决定接受集群短暂出现 HEALTH WARN 状态，每次移除一个 MON 后，再新增一个新 IP 的 MON 节点。删除和部署 MON 过程如下：# 取消 MON 节点的自动部署 ceph orch apply mon --unmanaged # 移除一个 MON 节点 ceph orch daemon rm mon.<hostname> # 新增一个 MON 节点，指定 IP 为新的 IP 地址 ceph orch daemon add mon <hostname>:<new_ipaddr>注意每替换一个，确认集群恢复正常后（ceph -s）再进行下一步操作。MON 节点更新完之后，使用 ceph mon stat/dump 确认所有 MON 节点都符合预期：root@ceph-mon-01:/# ceph mon dump epoch 26 fsid 5e08383c-18d1-11ee-a099-005056a354fb last_changed 2025-05-27T11:41:34.580004+0000 created 2023-07-02T12:10:07.025273+0000 min_mon_release 19 (squid) election_strategy: 1 0: [v2:10.184.18.34:3300/0,v1:10.184.18.34:6789/0] mon.ceph-mon-01 1: [v2:10.184.17.111:3300/0,v1:10.184.17.111:6789/0] mon.ceph-mon-02 2: [v2:10.184.17.121:3300/0,v1:10.184.17.121:6789/0] mon.ceph-mon-03 dumped monmap epoch 26设置 public network，注意需先保留原 public network，以保证 OSD 等节点的 network 在列表内：ceph config set mon public_network 10.182.65.0/24,10.184.17.0/24,10.184.18.0/24重新开启自动部署，由 label 进行管理：ceph orch apply mon --placement "label:ceph-mon"部署 MON 节点后，会自动对 daemon 进行 reconfig，更新配置文件，可以检查下配置是否正确OSD/MGR/MDS 节点这几个节点的配置文件存放目录：OSD: /var/lib/ceph/{fsid}/osd.<id>/configMGR: /var/lib/ceph/{fsid}/mgr.<name>/configMDS: /var/lib/ceph/{fsid}/mds.<name>/config正常情况下，重新部署 MON 后，会自动 reconfig，更新这些配置文件，若遇到问题，可以检查下这些配置。这些节点更改起来就比较容易了，操作都类似，这里仅以 OSD 节点为例进行说明。更改 OSD 节点的 public network：ceph config set osd public_network 10.184.17.0/14,10.184.18.0/24然后重启所有的 OSD 节点，这样，OSD 节点将监听在指定网段的 IP 地址上：!Ceph 集群迁移（修改 IP 地址）记录-1748435896243.jpeg所有节点重启完毕后，确认所有节点上报的 OSD IP 地址都是正确的：ceph osd dump --format json | jq '.osds[] | {osd: .osd, up: .up, in: .in, public_addr: .public_addr, cluster_addr: .cluster_addr}'然后可以去掉 OSD 的 public network 设置：ceph config rm osd public_networkMGR/OSD/MDS 改完之后，可以将 MON 的 public network 中的原网段去掉：ceph config set mon public_network 10.184.17.0/24,10.184.18.0/24NFS 导出NFS 主要分为两部分：NFS daemon 配置：/var/lib/ceph/{fsid}/nfs.{name}/configNFS ingress 配置：主要是 virtual ip 配置修改操作如下：# 先导出 nfs 和 ingress.nfs 的配置 ceph orch ls --service-type nfs --export > /tmp/nfs.yaml ceph orch ls --service-type ingress --export > /tmp/ingress.yaml # 按照需求更改，主要是 virtual_ip # 修改后重新部署 ceph orch apply -i /tmp/nfs.yaml ceph orch apply -i /tmp/ingress.yaml
前言最近组里申领了几台服务器，每台上面有 8 块 8T 的硬盘，我们的需求对硬盘要求不大，所以想着把这些硬盘用起来，避免闲置了。大概想了下，可能的需求有：块存储或者 NFS 存储挂载到 vSphere 中，供其他主机使用；NFS 或者 SMB 存储挂载到 PC 上使用开始的时候想着弄个 NAS，但是这种集中式的存储方案对于多台服务器来说还是不够方便。然后就想到分布式存储方案，搜了下发现了 Ceph 这个玩意，遂对照着官方文档尝试着搭建了个。Ceph 简介Ceph 是一个统一的分布式存储系统，提供三种存储接口：对象存储块设备存储文件存储Ceph 基本组件Monitors：维护着集群状态 map，保证集群数据的一致性Managers：负责追踪集群数据，同时提供 Web 及 REST API 服务OSDs: 用于集群中所有数据与对象的存储，处理集群数据的复制、恢复MDSs：为 Ceph 文件存储提供元数据Ceph 集群网络ceph 只有一个 public network 即可以正常工作，但是 OSDs 间的数据传输流量可能会比较大，为其划分一个单独的 cluster network 可能会对性能有所提升Ceph 主机划分共有 7 台 ESXi 主机，每台主机上有 8*8T 硬盘，计划如下：3 台上建立 3 台虚拟机用于 MON 节点各个主机上分别建立 1 台虚拟机用于 OSD 节点，将每台主机上的 8 块硬盘直通挂在 OSD 虚拟机上所有 OSD 节点采用两块网卡，一个用于 public network，一个用于 cluster network所有节点配置如下：nodeesxicorememorydiskceph diskinterface10.182.65.231-ceph-mon-0110.182.65.8432GB60GB-110.182.65.232-ceph-mon-0210.182.65.2432 GB60GB-110.182.65.233-ceph-mon-0310.182.65.3432GB60GB-110.182.65.221-ceph-osd-0110.182.65.8432GB10GB8*8T210.182.65.222-ceph-osd-0210.182.65.2432GB10GB8*8T210.182.65.222-ceph-osd-0310.182.65.3432GB10GB8*8T210.182.65.222-ceph-osd-0410.182.65.4432GB10GB8*8T210.182.65.222-ceph-osd-0510.182.65.5432GB10GB8*8T210.182.65.222-ceph-osd-0610.182.65.6432GB10GB8*8T210.182.65.222-ceph-osd-0710.182.65.7432GB10GB8*8T2Ceph 集群搭建安装依赖各个 node 上安装以下依赖：Python3SystemdPodman/DockerNTPLVM2由于我们在虚拟机中操作，可以在一台虚拟机中把环境准备好后，克隆虚拟机即可安装 cephadmCeph 官方提供的 cephadm 工具可以很方便地用于 ceph 的创建和管理。安装方式有两种：直接下载 cephadm 脚本；通过包管理器下载包管理器提供的版本可能会落后，此处采用脚本方式：# 下载 cephadm，对应最新稳定版 ceph——quincy curl --silent --remote-name --location https://github.com/ceph/ceph/raw/quincy/src/cephadm/cephadm # 增加可执行权限 chmod +x cephadm # 将 cephadm 安装到 PATH 中 ./cephadm add-repo --release quincy ./cephadm install创建 Ceph 集群创建第一个 MON 进程在 ceph-mon-01 节点上执行如下命令创建第一个 MON 进程和 MGR 进程cephadm bootstrap --mon-ip 10.182.65.231 --cluster-network 10.90.90.0/24说明：--mon-ip 指定了该 MON 进程节点可对外访问的 IP 地址；--cluster-network 指定了 OSD daemons 间创建副本、恢复和心跳流量使用的网络创建完成之后，查看该节点：# 进入 ceph 环境 (本文后续操作无特殊说明均在该环境下进行) cephadm shell # 查看 ceph host root@ceph-mon-01:/# ceph orch host ls HOST ADDR LABELS STATUS ceph-mon-01 10.182.65.231 _admin添加所有节点# 1. 在 `ceph-mon-01` 节点上为各个节点增加密钥登录 # 以 10.182.65.232-ceph-mon-02 为例 ssh-copy-id -f -i /etc/ceph/ceph.pub root@10.182.65.232 # 2. 添加节点，对 MON 节点增加 _admin 标签 ceph orch host add ceph-mon-02 10.182.65.232 --labels _admin ceph orch host add ceph-mon-03 10.182.65.233 --labels _admin ceph orch host add ceph-osd-01 10.182.65.221 ceph orch host add ceph-osd-02 10.182.65.222 ceph orch host add ceph-osd-03 10.182.65.223 ceph orch host add ceph-osd-04 10.182.65.224 ceph orch host add ceph-osd-05 10.182.65.225 ceph orch host add ceph-osd-06 10.182.65.226 ceph orch host add ceph-osd-07 10.182.65.227添加好之后，查看所有节点：root@ceph-mon-01:/# ceph orch host ls HOST ADDR LABELS STATUS ceph-mon-01 10.182.65.231 _admin ceph-mon-02 10.182.65.232 _admin ceph-mon-03 10.182.65.233 _admin ceph-osd-01 10.182.65.221 ceph-osd-02 10.182.65.222 ceph-osd-03 10.182.65.223 ceph-osd-04 10.182.65.224 ceph-osd-05 10.182.65.225 ceph-osd-06 10.182.65.226 ceph-osd-07 10.182.65.227 10 hosts in cluster root@ceph-mon-01:/#增加 MON 进程Ceph 建议一个典型的 ceph 集群应该有 3 或者 5 个 MON 进程（分布在不同的节点上）。ceph 会根据集群的大小自动调整 MON 进程的数量，因为我们已经对虚拟机的用途进行了规划，所以我们这里不采用 ceph 的自动部署，而由我们自己来进行控制。# 1. 关闭 MON 进程自动部署 ceph orch apply mon --unmanaged # 2. 给需要部署 MON 进程的节点增加 ceph-mon 标签 ceph orch host label add ceph-mon-02 ceph-mon ceph orch host label add ceph-mon-03 ceph-mon # 3. 在指定标签的节点上部署 MON 进程 ceph orch apply mon --placement="label:ceph-mon"添加之后，查看 MON 进程运行状态：root@ceph-mon-01:/# ceph orch ls | grep -E "(NAME|mon)" NAME PORTS RUNNING REFRESHED AGE PLACEMENT mon 3/3 7m ago 5d label:ceph-mon # 或者更详细的进程信息 root@ceph-mon-01:/# ceph orch ps --daemon-type mon NAME HOST PORTS STATUS REFRESHED AGE MEM USE MEM LIM VERSION IMAGE ID CONTAINER ID mon.ceph-mon-01 ceph-mon-01 running (5d) 10m ago 6d 560M 2048M 17.2.6 cc5b7b143311 f451cea94c3f mon.ceph-mon-02 ceph-mon-02 running (5d) 10m ago 5d 546M 2048M 17.2.6 cc5b7b143311 9973056c4b6c mon.ceph-mon-03 ceph-mon-03 running (5d) 10m ago 5d 546M 2048M 17.2.6 cc5b7b143311 43a86843dc68部署 OSD# 查看所有的硬盘设备 ceph orch device ls # 自动部署 osd 到所有可用的节点上 ceph orch apply osd --all-available-devices # 查看 osd 进程运行状态 ceph orch ls | grep -E "(NAME|osd)" # 或者 ceph orch ps --daemon-type osd这里部署时有遇到一个问题，部署完之后，再次 ceph orch device ls 查看硬盘时发现存在 REJECT RESONS：root@ceph-mon-01:/# ceph orch device ls HOST PATH TYPE DEVICE ID SIZE AVAILABLE REFRESHED REJECT REASONS ceph-osd-01 /dev/sda hdd VMware_Virtual_SATA_Hard_Drive_01000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdb hdd VMware_Virtual_SATA_Hard_Drive_02000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdc hdd VMware_Virtual_SATA_Hard_Drive_03000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdd hdd VMware_Virtual_SATA_Hard_Drive_04000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sde hdd VMware_Virtual_SATA_Hard_Drive_05000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdf hdd VMware_Virtual_SATA_Hard_Drive_06000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdg hdd VMware_Virtual_SATA_Hard_Drive_07000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked ceph-osd-01 /dev/sdh hdd VMware_Virtual_SATA_Hard_Drive_08000000000000000001 8001G No 25m ago Insufficient space (<10 extents) on vgs, LVM detected, locked然后我一直以为部署存在着什么问题，查了很多资料花了很久也没有解决，一直都会报这个错误。后来一想，可能是硬盘已经被 ceph 使用了，再次查看设备时，硬盘可用状态就应该是显示为 NO？以上，Ceph 集群就差不多搭建好了，接下来就是如何使用它了，由于 vSphere 也可以挂载 NFS 存储，Linux 挂载 NFS 存储也很方便，所以先拿 NFS 存储试试手。Ceph 部署 NFS 存储服务Ceph 如果需要支持 NFS 服务的话，需要通过 CephFS 或者 Ceph 对象存储导出，我们这里使用 CephFS。创建 CephFS使用 CephFS 存储需要部署 MDS 进程，同样地，我们按照之前的规划，将 MDS 进程部署到带有特定标签的节点上对要部署 MDS 进程的节点增加标签root@ceph-mon-01:/# ceph orch host label add ceph-osd-01 mds Added label mds to host ceph-osd-01 root@ceph-mon-01:/# ceph orch host label add ceph-osd-02 mds Added label mds to host ceph-osd-02 root@ceph-mon-01:/# ceph orch host label add ceph-osd-03 mds Added label mds to host ceph-osd-03 root@ceph-mon-01:/# ceph orch host label add ceph-osd-07 mds Added label mds to host ceph-osd-07创建 Cephfsroot@ceph-mon-01:/# ceph fs volume create rootfs --placement="label:mds"以上命令进行了如下操作：创建了一个名为 rootfs 的 CephFS volume；在带有 mds 标签的节点上创建了 MDS 进程；创建了两个存储 pool查看 MDS 进程运行状态root@ceph-mon-01:/# ceph orch ps --daemon-type mds NAME HOST PORTS STATUS REFRESHED AGE MEM USE MEM LIM VERSION IMAGE ID CONTAINER ID mds.rootfs.ceph-osd-01.rbrgqn ceph-osd-01 running (5d) 4m ago 5d 23.7M - 17.2.6 cc5b7b143311 5963562520f5 mds.rootfs.ceph-osd-02.naecmr ceph-osd-02 running (5d) 4m ago 5d 363M - 17.2.6 cc5b7b143311 0aef185fced3 mds.rootfs.ceph-osd-03.rlksrg ceph-osd-03 running (5d) 4m ago 5d 24.3M - 17.2.6 cc5b7b143311 f140a3d82bf1 mds.rootfs.ceph-osd-07.qgqprj ceph-osd-07 running (5d) 4m ago 5d 26.8M - 17.2.6 cc5b7b143311 1785e4aaa2cd查看 CephFS fs volume 信息root@ceph-mon-01:/# ceph fs volume info rootfs { "mon_addrs": [ "10.182.65.231:6789", "10.182.65.232:6789", "10.182.65.233:6789" ], "pools": { "data": [ { "avail": 136819773538304, "name": "cephfs.rootfs.data", "used": 0 } ], "metadata": [ { "avail": 136819773538304, "name": "cephfs.rootfs.meta", "used": 98304 } ] } }在 rootfs volume 下创建一个 fs subvolume，用于后续 NFS 服务# 创建 subvolume ceph fs subvolume create rootfs vspherefs # 查看 subvolume 信息 ceph fs subvolume info rootfs vspherefs创建 NFS Cluster由于可以在多个节点上部署 NFS 服务，同样地，我们也使用标签来控制服务部署在哪些节点上root@ceph-mon-01:/# ceph orch host label add ceph-osd-04 nfs Added label nfs to host ceph-osd-04 root@ceph-mon-01:/# ceph orch host label add ceph-osd-05 nfs Added label nfs to host ceph-osd-05 root@ceph-mon-01:/# ceph orch host label add ceph-osd-06 nfs Added label nfs to host ceph-osd-06创建 NFS clusterroot@ceph-mon-01:/# ceph nfs cluster create pdt-nfs "label:nfs" --ingress --virtual_ip 10.182.65.220说明：--virtual_ip 指定了 NFS Cluster 集群对外地址查看 NFS cluster 信息root@ceph-mon-01:/# ceph nfs cluster info pdt-nfs { "pdt-nfs": { "virtual_ip": "10.182.65.220", "backend": [ { "hostname": "ceph-osd-04", "ip": "10.182.65.224", "port": 12049 }, { "hostname": "ceph-osd-05", "ip": "10.182.65.225", "port": 12049 }, { "hostname": "ceph-osd-06", "ip": "10.182.65.226", "port": 12049 } ], "port": 2049, "monitor_port": 9049 } }创建 CephFS 导出获取要导出的 CephFS volume 的路径root@ceph-mon-01:/# ceph fs subvolume getpath rootfs vspherefs /volumes/_nogroup/vspherefs/49bedaf9-5a48-4159-b1c7-9acd2869c31a创建 CephFS 导出root@ceph-mon-01:/# ceph nfs export create cephfs --cluster-id pdt-nfs --pseudo-path /vspherefs --fsname rootfs --path=/volumes/_nogroup/vspherefs/49bedaf9-5a48-4159-b1c7-9acd2869c31a { "bind": "/vspherefs", "fs": "rootfs", "path": "/volumes/_nogroup/vspherefs/49bedaf9-5a48-4159-b1c7-9acd2869c31a", "cluster": "pdt-nfs", "mode": "RW" }查看创建的导出信息# 查看指定 NFS Cluster 下所有的导出信息 ceph nfs export ls pdt-nfs --detailed # 查看指定导出的详细信息 root@ceph-mon-01:/# ceph nfs cluster info pdt-nfs { "pdt-nfs": { "virtual_ip": "10.182.65.220", "backend": [ { "hostname": "ceph-osd-04", "ip": "10.182.65.224", "port": 12049 }, { "hostname": "ceph-osd-05", "ip": "10.182.65.225", "port": 12049 }, { "hostname": "ceph-osd-06", "ip": "10.182.65.226", "port": 12049 } ], "port": 2049, "monitor_port": 9049 } }Linux 挂载 NFSmount -t nfs <ganesha-host-name>:<pseudo_path> <mount-point> # 其中，ganesha-host-name 为上面配置的 virtual_ip，例如 mkdir -p /data/vspherefs mount -t nfs -o nfsvers=4.1,proto=tcp 10.182.65.220:/vspherefs /data/vspherefs后话以上搭建过程中，基本上是怎么简单怎么来，能 ceph 自动处理的基本让它自动处理，所以还是有很多不明白的东西，没有任何的调优，先用一段时间看看。附 Dashboard：
前言在前一篇文章 Arch Linux 安装篇 中有提到连接网络可以选择多种方式，如 iwd、netctl、systemd-networkd、NetworkManager 等。由于我对于 GUI 的要求不是那么高，最终我选择的是 systemd-networkd + iwd 的方式管理和连接网络。由于平时可能需要用到集聚接口 / vlan 子接口，所以查找了下 systemd-networkd 创建集聚接口和 vlan 子接口的相关资料，特做记录。ps: 文章其实和 Arch 这个特定的 Linux 发行版没啥大关系，只是决定使用 Arch 这个发行版了，后续的 Linux 上的一些使用方面的记录可能都会冠以 「Arch Linux 使用小记」这个 title。接口配置文件systemd 配置接口主要涉及的文件有 netdev 及 network 。netdev 文件netdev 文件用于配置虚拟网络设备，例如 bond / vlan / tun / wireguard 等各种类型的接口，详见 systemd.netdev主要配置的字段有 [NetDev] 及具体的接口类型段。network 文件network 文件用于配置接口具体的网络信息，如 IP / MAC / 路由等，详见 systemd.network主要配置的字段有 [Match] 及 [Network] 。集聚接口Debian Wiki 中提到 systemd 默认会创建一个名为 bond0 模式为 balance round robin 的集聚口，且模式不可更改（未去验证），所以为避免可能出现问题以下配置时均不采用 bond0 这个名字。更改配置后，可执行 systemctl restart systemd-networkd 生效。有线与无线聚合这个的需求主要是有线和无线之间作为主备，比如拔掉网线，可以无缝使用无线网络进行连接。创建集聚口 netdev 文件，如 /etc/systemd/network/bond1.netdev：[NetDev] Name=bond1 # 创建的集聚口名字 Kind=bond # 类型为 bond [Bond] Mode=active-backup # 模式为 active-backup，即主备模式 PrimaryReselectPolicy=always MIIMonitorSec=1s创建集聚口 network 文件，如 /etc/systemd/network/bond1.network:[Match] Name=bond1 # 需要匹配的集聚口名字 [Network] DHCP=ipv4创建有线接口 network 文件，如 /etc/systemd/network/eno1.network:[Match] Name=eno1 # 需要匹配的有线接口名字 [Network] Bond=bond1 # 属于的 bond 接口 PrimarySlave=true # 优先使用有线创建无线接口 network 文件，如 /etc/systemd/network/wireless.network:[Match] Name=wlan0 # 无线接口名字 [Network] Bond=bond1 # 属于的 bond 接口LACP 动态链路聚合这个的需求主要是 Linux 上联交换机时，为增大带宽，Linux 与交换机相连的接口做聚合，并且开启 LACP 进行动态协商。创建集聚接口 netdev 文件，如 /etc/systemd/network/bond2.netdev:[NetDev] Name=bond2 # 集聚接口名字 Description=LAG/Bond to a switch Kind=bond [Bond] Mode=802.3ad # 集聚接口工作模式，802.3ad 即为 lacp创建集聚接口 network 文件，如 /etc/systemd/network/bond2.network:[Match] Name=bond2 # 匹配的集聚接口名字 [Network] BindCarrier=enp4s0f0 enp4s0f1 # 集聚接口的成员接口名创建集聚接口的成员接口 network 文件，这里我将两个成员接口放在一个 network 文件中，如 /etc/systemd/network/bond2_members.network:[Match] Name=enp4s0f0 Name=enp4s0f1 # 匹配的成员接口名，可以使用模糊匹配，这里我用的精确的接口名字 [Network] Bond=bond2 # 属于的集聚口名VLAN 子接口物理接口有限，我需要创建一些 vlan 子接口满足一些特别的需求。这里以在上面步骤中创建的集聚接口为基础添加 vlan 子接口为例，物理接口添加方法类似。修改 /etc/systemd/network/bond2.network 文件，添加 vlan 子接口配置：[Match] Name=bond2 [Network] BindCarrier=enp4s0f0 enp4s0f1 VLAN=bond2.401 # vlan 接口名 VLAN=bond2.402 VLAN=bond2.403 VLAN=bond2.404创建相应的 vlan 子接口 netdev 文件，以 bond2.401 接口为例，如 /etc/systemd/network/bond2.401.netdev:[NetDev] Name=bond2.401 # vlan 接口名 Kind=vlan # 虚拟设备类型为 vlan [VLAN] Id=401 # vlan id 为 401如果需要对 vlan 子接口做 IP 或者路由等网络配置的话，可以创建相应的 network 文件，这里不再赘述。参考资料Configuring the bond devicenetwork_systemdVLAN
前言之前的一台老电脑装着 Windows 和 Manjaro 的双系统，但是 Windows 也一直没在使用，就想着把它干脆把它格了装成 Linux 单系统。由于习惯了 pacman 和 aur，就想着重装成 Manjaro 或者 Arch 了，毕竟之前装过 Manjaro 了，是时候体验下装 Arch 了（瞎折腾~）。Arch Linux 的官方 Wiki 一直都是业界楷模，所以照着维基安装问题不大。本文对自己安装过程（(LVM + XFS) / Btrfs + KDE）进行记录整合，方便自己使用，整个内容在 Wiki 基本都能找到说明，建议想要入坑的直接查看 Wiki。准备工作下载镜像；制作 U 盘启动盘，可采用 Rufus 或者 Ventoy系统安装启动到 Live 环境进入 BIOS 设置 U 盘优先启动，启动后进入 archiso shell 环境（默认为 zsh）；验证启动模式是否为 UEFI 模式：# 能够正常显示该目录，则为 uefi 模式 ls /sys/firmware/efi/efivars本文采用 UEFI 模式启动。连接网络由于安装过程中需要安装一些必要的软件，所以需要连接到网络，我采用的是无线网络（iwd），有线网络的话正常插上线（DHCP）就可使用了：# 1. 查看网卡设备 ip link show # 2. 输入 iwctl 进入 iwd 模式下 [root@archiso~] iwctl [iwd#] # 3. 扫描和获取附近网络 [iwd#] station <device> scan [iwd#] station <device> get-networks # 例如： [iwd#] station wlan0 scan [iwd#] station wlan0 get-networks Available networks -------------------------------------------------------------------------------- Network name Security Signal -------------------------------------------------------------------------------- > 12333_5461 psk **** CU_cH82 psk **** CMCC-cd3x psk **** Tenda_304 psk **** JC2902544410000B07050225 psk **** JSCN_SZ_4296B9 psk **** # 4. 连接 WiFi，连接是会提示输入密码，按照实际情况输入即可 [iwd#] station <device> connect <network name> # 5. 连接后，如无报错，则连接成功，输入 exit 退出 # 6. ping baidu.com 确认网络连接正常同步系统时间# 设置 ntp 同步 timedatectl set-ntp true # 查看同步状态 timedatectl status硬盘分区与格式化硬盘分区我采用的是 GPT 分区表，硬盘分区工具可使用 fdisk 或者 cfdisk。LVM + XFS 方案采用 LVM 方案来方便进行空间的动态调整，文件系统采用的是 XFS 格式（不支持压缩空间），所以刚开始配置的时候可以给各分区小点的空间。假设存在两块硬盘 /dev/sda 及 /dev/sdb，因为我使用的是 UEFI 模式启动，需要创建相应的 EFI 系统分区（如 /dev/sda1），不能使用 LVM，该分区大小可以设置在 500M 左右，分区类型为 EFI System；将 /dev/sda 及 /dev/sdb 剩余空间分别创建分区 /dev/sda2 、/dev/sdb1，分区类型为 Linux LVM，用于 LVM；配置 LVM（创建 PV --> 创建 VG --> 配置 LV）：创建物理卷：# 查看可以用做物理卷的设备 lvmdiskscan # 创建物理卷 pvcreate /dev/sda2 pvcreate /dev/sdb1 # 查看物理卷 pvdisplay创建卷组：# 创建卷组命令 vgcreate <vg-name> <device> # 例如 vgcreate arch-vg /dev/sda2 /dev/sdb1 # 查看卷组 vgdisplay创建逻辑卷：# 逻辑卷创建命令 lvcreate -L <volume size> <vg name> -n <lv name> # 例如：先创建 root/home/swap 三个 LV，后续有需要可以再加 lvcreate -L 50G arch-vg -n root lvcreate -L 2G arch-vg -n swap lvcreate -L 100G arch-vg -n home # 查看逻辑卷 lvdisplay对创建好的逻辑卷进行格式化：# 格式化 EFI System 分区，采用格式为 fat32 mkfs.fat -F32 /dev/sda1 # 格式化 root/home 分区，采用格式为 xfs mkfs.xfs /dev/mapper/arch--vg-root mkfs.xfs /dev/mapper/arch--vg-home # 格式化 swap 分区 mkswap /dev/mapper/arch--vg-swap挂载分区：# 将 root 分区挂载到 /mnt 下 mount /dev/mapper/arch--vg-root /mnt # 将 efi 分区挂载到 /mnt/boot 下 mkdir /mnt/boot mount /dev/sda1 /mnt/boot # 将 home 分区挂载到 /mnt/home 下 mkdir /mnt/home mount /dev/mapper/arch--vg-home /mnt/home # 启用 swap 分区 swapon /dev/mapper/arch--vg-swapBtrfs 方案Btrfs 支持 LVM 功能，子卷方便进行快照，不清楚有没有啥毛病，尝鲜用。以下以一块硬盘做记录使用 fdisk 或者 cfdisk 进行分区，只分两个区（如需要可以分一个 swap 出来，我这里使用 swapfile），一个 EFI System 分区 /dev/sda1，大小 500M 左右，剩余空间均给 /dev/sda2；格式化：# EFI System 分区 mkfs.fat -F32 /dev/sda1 # btrfs 文件系统 mkfs.btrfs -L arch /dev/sda2 # -L 为指定 label 创建子卷，类似于分区，先不对空间进行限制：# 1. 挂载 /dev/sda2 到 /mnt mount /dev/sda2 /mnt # 2. 创建子卷，这里先只创建 root 及 home，@ 仅作为标识 btrfs su cr /mnt/@ btrfs su cr /mnt/@home btrfs su cr /mnt/@swap # 3. 取消挂载 umount /mnt # 4. 挂载子卷及 boot mount -o subvol=@ /dev/sda2 /mnt mkdir /mnt/{boot,home,swap} mount -o subvol=@home /dev/sda2 /mnt/home mount -o subvol=@swap /dev/sda2 /mnt/swap mount /dev/sda1 /mnt/boot # 5. swapfile 设置 btrfs filesystem mkswapfile --size 4g /mnt/swap/swapfile swapon /mnt/swap/swapfile安装软件包由于镜像源在连接网络后会通过 Reflector 自动帮忙排序，我们可以不需要手动挑选镜像源。pacstrap /mnt base base-devel linux linux-firmware vim iwd lvm2 # - base / base-devel 一些基础软件包 # - linux linux 内核软件包 # - linux-firmware 固件软件包，虚拟机或者容器可不安装 # - vim 文本编辑器 # - iwd 无线联网 # - lvm2 lvm 软件生成 fstab 文件# 生成 fstab 文件，用于分区挂载 genfstab -U /mnt >> /mnt/etc/fstab # 生成后，检查 /mnt/etc/fstab 文件是否正确 # 如果按照上述 btrfs 子卷方式使用了 swapfile，可以手动加上 swapfile 的挂载 /swap/swapfile none swap defaults 0 0系统配置Chroot# 进入到安装好的新系统中，根目录（/）挂载在 /mnt 下 arch-chroot /mnt时区设置ln -sf /usr/share/zoneinfo/Region/City /etc/localtime # 如 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 通过 [hwclock](https://man.archlinux.org/man/hwclock.8) 生成 /etc/adjtime hwclock --systohc本地化设置# 1. 编辑 /etc/locale.gen 文件，按需取消地区前的注释，如 en_US UTF-8 和 zh_CN UTF-8 # 2. 运行 locale-gen 生成 locale 文件 locale-gen # 3. 创建 /etc/locale.conf 文件，设置 LANG 变量 LANG=en_US.UTF-8网络配置编辑文件 /etc/hostname 配置主机名；编辑文件 /etc/hosts 配置 localhost 的域名解析127.0.0.1 localhost ::1 localhost 127.0.1.1 myhostname.localdomain myhostname # 主机名.本地域名 主机名（optional）使用 iwd 连接网络，配置 iwd 配置文件 /etc/iwd/main.conf ，允许 DHCP[General] EnableNetworkConfiguration=true（optional）使用 systemd-networkd 管理网络，参考配置如下：# 1. 新建一个 network 配置，如 `/etc/systemd/network/eth0.network` # 两种选其一 # 配置 DHCP [Match] Name=eth0 [Network] DHCP=ipv4 # 配置静态 IP [Match] Name=eth0 [Network] Address=10.10.10.2/24 Gateway=10.10.10.1 DNS=10.188.8.8 # 2. 允许自启 systemctl enable systemd-networkd （optional）如果后续需要使用图形界面的话，可以使用 NetworkManager 管理网络，后续操作可在 UI 下配置，更简单。注意: step 3/4/5 可选择性的搭配使用，比如 systemd-networkd + iwd配置 Initramfs如果分区时采用了 LVM，需要做如下修改：# 1. 编辑 /etc/mkinitcpio.conf 文件，找到 HOOKS，在 block 和 filesystem 之间插入 lvm2： HOOKS=(base udev autodetect modconf block lvm2 filesystems keyboard fsck) # 2. 执行下面命令，创建新的 initramfs mkinitcpio -P如果采用的是 Btrfs 文件系统，需要做如下修改：# 1. 编辑 /etc/mkinitcpio.conf 文件，修改如下内容 MODULES=(btrfs) # 2. 执行命令 mkinitcpio -P设置用户# 为 root 用户添加密码 passwd # 增加非 root 用户 useradd -m <user> # 为非 root 用户设置密码 passwd <user> # 修改 /etc/sudoers 添加权限 sudoedit /etc/sudoers安装引导程序安装引导程序pacman -S grub efibootmgr安装引导到 EFI 分区下grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB生成 grub 配置grub-mkconfig -o /boot/grub/grub.cfg配置完成后，如果不需要图形界面，可按照下面步骤重启：使用 exit 退出 chroot；使用 umount -R /mnt 取消挂载；使用 reboot 命令重启，记得重启前拔除 U 盘。这样，基本上一个没有图形界面的系统就安装配置完成了。需要图形界面的话可以参考下面 「配置图形界面」配置图形界面# 1. 安装显示服务 pacman -S xorg # 2. 安装显卡驱动 pacman -S xf86-video-intel # intel pacman -S nvidia # 如果安装 nvidia 闭源驱动，可能需要另外的配置，详见 Wiki，否则可能无法进入桌面 # 3. 安装桌面环境 pacman -S plasma-meta kde-applications # 4. 安装显示管理器 pacman -S sddm sddm-kcm systemctl enable sddm # 允许 sddm 服务自启参考资料Installation guide - ArchWikiArchlinux 最新安装教程Arch Linux with BTRFS Installation
前言因为常用的环境都在服务器端，大部分的工作也都是 SSH 上服务器或者通过 vscode 的 remote-ssh 远程完成的，所以就会有 SSH 断开工作环境丢失的问题。然后几个月前了解到了 Tmux 这个终端复用工具，用了一段时间之后感觉还不错，遂对其做个记录。Tmux 简介单元模块服务器（server）：tmux 为 C/S 模型，输入 tmux 时则代表开启了一个 tmux 服务器会话（session）：开启 tmux 服务后，默认新建一个会话；一个 tmux 服务器可以创建多个会话窗口（window）：新建一个会话后，默认新建一个窗口；一个会话可以创建多个窗口窗格（pane）：新建一个窗口后，默认新建一个窗格；一个窗口可以创建多个窗格主要功能对于我来说，tmux 主要功能有：会话和窗口分离，窗口关闭，会话不结束，用以维持环境；窗口可以进行分割，在一个窗口不同窗格下进行不同的操作；基于 C/S 模型，多个客户端可以同时连接到同一个 session，方便共享；插件系统，方便扩展功能，如记录日志（可以不依赖终端软件了）Tmux 自定义配置tmux 可以自定义配置，针对个人用户的配置文件，修改 $HOME/.tmux.conf 文件即可。前缀键tmux 可以通过 tmux + command 的方式进行操作；也可以在进入 tmux 窗口中后通过快捷键进行操作，快捷键通过前缀键唤醒。默认的前缀键是 ctrl+b，但是这个前缀键比较不友好，实际使用中有点别扭，所以我对其进行了修改。关于前缀键的选取，主要考虑的因素有是否方便敲击和是否与其他快捷键有冲突。比较理想的按键是 ` 键，冲突少，只要敲击一个键，+ 数字键切换窗口时也很方便，但是我的 NiZ Plum Atom66 键盘这个键很不方便，所以放弃了，最后采用的是 ctrl+s 键，修改如下：set -g prefix C-s unbind C-b bind C-s send-prefix关于前缀键的选取可参考如下文章 / 提问：为 tmux / screen 选择一个合适的引导键hotkeys - What's the least conflicting prefix/escape sequence for screen or tmux? - Super User常用基础选项# 设置延时 set -sg escape-time 1 # 修改默认延时，设置为1ms，增加响应时间 # 开启鼠标模式，可以使用鼠标进行窗格大小的调整 set -g mouse on # 关闭自动改名 set-option -g allow-rename off # 开启剪贴板 set -g set-clipboard on # 设置 buffer 长度 set -g history-limit 50000 # 加载 tmux 配置文件的快捷键: prefix + r bind r source-file ~/.tmux.conf\; display "Reloaded!"文本复制模式使用快捷键 prefix + [ 可以进入 Copy 模式，使用 Enter 键退出，平时修改配置时习惯使用 Vim 进行操作，所以修改文本复制模式为 vi:# 开启 vi 按键 setw -g mode-keys vi set -g status-keys vi bind-key -T vi-edit Up send -X history-up bind-key -T vi-edit Down send -X history-down不过我一般不使用键盘来进行复制操作，多是进入 Copy 模式后进行屏幕搜索窗口配置# 由于 0 键的位置，切换窗口时不好寻找，设置窗口索引从 1 开始 set -g base-index 1 # 切换窗口快捷键，在窗口间前后切换，-r 代表可以多次敲击 bind -r C-h select-window -t :- bind -r C-l select-window -t :+ # 修改默认的分割 window 快捷键，采用 | 和 - 更加形象好记 bind | split-window -h bind - split-window -v窗格配置# 设备窗格索引从 1 开始 setw -g pane-base-index 1 # 选择窗格快捷键，使用 Vim 按键模式 bind h select-pane -L # 左 bind j select-pane -D # 下 bind k select-pane -U # 上 bind l select-pane -R # 右 # 调节窗格大小快捷键，使用 Vim 按键模式 bind -r H resize-pane -L 5 bind -r J resize-pane -D 5 bind -r K resize-pane -U 5 bind -r L resize-pane -R 5 # 窗格边缘显示，在窗格上方显示 pane 索引及名字 set -g pane-border-status top状态栏配置# 状态栏刷新间隔 set -g status-interval 1 # 状态栏窗口居中 set -g status-justify centre # 左侧状态栏长度 set -g status-left-length 40 # 开启活动通知 setw -g monitor-activity on set -g visual-activity on配色# 以 256 色显示内容 set -g default-terminal "screen-256color" # 配置窗口样式 # > 窗口列表 set -g window-status-format '#[dim]#I:#[default]#W#[fg=dim]' set -g window-status-current-format '#[fg=cyan,bold]#I#[fg=blue]:#[fg=cyan]#W#[fg=dim]' # 配置窗格样式 # > 配置窗格颜色 set -g pane-border-style fg=cyan set -g pane-border-style bg=default # > 配置当前窗格颜色 set -ag pane-active-border-style fg=green set -ag pane-active-border-style bg=default # 设置命令行或消息的颜色 set -g message-style fg=white set -g message-style bg=black set -g message-style bright # 设置底部状态栏的背景色、前景色及内容 set -g status-style fg=white set -g status-style bg=default set -g status-left "#[fg=green][#[fg=cyan]#S#[fg=green]] #{prefix_highlight}" set -g status-right "#[fg=green][#[fg=cyan]%Y-%m-%d %R#[fg=green]]"插件配置tpmtpm 为 tmux 的插件管理器，使用它可以方便的对插件进行安装和更新管理。安装：git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm在 tmux 配置文件中加入以下内容：set -g @plugin 'tmux-plugins/tpm' # 初始化 tpm，注意此行加载配置文件最底部 run '~/.tmux/plugins/tpm/tpm'绑定的快捷键：prefix + I：下载配置文件中列出的插件；prefix + U：更新配置文件中列出的插件；prefix + alt + u: 卸载配置文件中没有的插件后面提到的插件均使用绑定的快捷键进行下载和更新tmux-logging该插件用于日志或者屏幕记录，添加方法如下：set -g @plugin 'tmux-plugins/tmux-logging' # 设置日志保存文件夹 set -g @logging-path "$HOME/.tmux/logs" # 设置日志保存的文件名格式 set -g @logging-filename "tmux-#{session_name}-#{window_name}-#{pane_title}-%Y%m%dT%H%M%S.log"绑定的快捷键为：prefix + shift + p: 记录日志，针对的是单一窗格；prefix + alt + p: 屏幕捕捉；prefix + shift + alt + p: 记录所有历史记录；tmux-resurrect该插件用于保存会话，方便设备重启后恢复会话记录，添加内容如下：set -g @plugin 'tmux-plugins/tmux-resurrect' set -g @resurrect-capture-pane-contents 'on' # 开启恢复窗格内容功能 set -g @resurrect-save-shell-history 'on' # 恢复 shell 环境绑定的快捷键为：prefix + Ctrl-s: 保存；prefix + Ctrl-r: 恢复；tmux-continuum该插件对于 tmux-resurrect 插件进行补充，用于自动备份和恢复，添加内容如下：set -g @plugin 'tmux-plugins/tmux-continuum' # 设置自动备份的时间间隔，单位为分钟 set -g @continuum-save-interval '1440' # 设置自动恢复 set -g @continuum-restore 'on'tmux-yank该插件用于对 tmux 的复制进行补充，可将内容复制到系统剪贴板，添加内容如下：set -g @plugin 'tmux-plugins/tmux-yank'绑定的快捷键为：normal 模式：prefix + y: 将命令行上的文本复制到系统剪贴板；prefix + Y: 将当前窗格的当前路径复制到系统剪贴板；copy 模式：y: 将选取的内容复制到系统剪贴板；Y: 将选取的内容复制并粘贴到命令行；tmux-prefix-highlight该插件用于在状态栏显示 prefix / copy 模式状态，方便判断是否按下了前缀键，添加方法如下：set -g @plugin 'tmux-plugins/tmux-prefix-highlight' # set -g @prefix_highlight_fg 'white' # default is 'colour231' # set -g @prefix_highlight_bg 'blue' # default is 'colour04' set -g @prefix_highlight_show_copy_mode 'on' # set -g @prefix_highlight_copy_mode_attr 'fg=black,bg=yellow,bold' # default is 'fg=default,bg=yellow' set -g @prefix_highlight_show_sync_mode 'on' # set -g @prefix_highlight_sync_mode_attr 'fg=black,bg=green' # default is 'fg=default,bg=yellow'Tmux 常用命令及快捷键说明：本章节记录自己使用过程中常会用到的命令或者快捷键，上文中提到的自定义绑定的快捷键不做记录，仅记录未做修改的默认快捷键。命令可通过指定参数的方式对任何会话/窗口/窗格进行操作，而快捷键作用范围为当前会话系统篇命令快捷键描述tmux list-keys?列出所有快捷键，查看说明tmux list-commands 列出所有命令，查看参数及说明 :进入命令行模式，可以输入支持的 tmux 命令会话篇命令快捷键描述tmux new-session/new 创建新会话tmux attach-session/attach/a 连接已有会话tmux kill-session 删除会话tmux detach-client/detachd脱离会话，退出窗口，不删除会话tmux list-sessionss查看所有会话，可通过方向键进行切换tmux rename-session/rename$重命名会话窗口篇命令快捷键描述tmux new-window/newwc创建新窗口tmux list-windowsw列出所有窗口，可通过方向键选择窗口tmux rename-window/renamew,重命名窗口tmux kill-window/killw&删除窗口 数字键切换至指定数字索引的窗口tmux swap-window 交换窗口tmux move-window.移动窗口，相当于修改索引；命令模式可将窗口加入到不同会话中tmux setw synchronize-panes 同步该窗口下所有窗格，用于同步输入内容，再次输入关闭窗格篇命令快捷键描述 %水平方向创建窗格 “垂直方向创建窗格tmux select-pane -T 重命名窗格 q显示窗格索引，当出现索引数字时，按下数字键则进入对应窗格 x关闭窗格 z最大化窗格 space切换窗格布局 !将当前窗格置于新窗口中，即新建一个窗口，窗口中只包含当前窗格tmux swap-pane -U{向前置换当前窗格tmux swap-pane -D}向后置换当前窗格完整配置见 dotfiles/tmux (与上文介绍的可能有些许改动)参考OpenBSD manual page of tmuxTmux 配置指北Tmux 使用手册打造最强终端之二：Tmux配置及使用教程
最近有连接两个内网的小需求，想起了之前写过的 WireGuard 搭建和使用折腾小记，决定尝试着使用 WireGuard 来实现。因为两个内网都在 NAT 后，自己也没有固定的公网 IP，所以只能借助一台 VPS 作为中继服务器，用来分别连接两个内网并进行流量转发。以下内容为自己的一点记录，其实大体上和之前的文章区别不大，不过这次主要以手动命令配置为主，方便自己进一步理解。拓扑环境NAT-A (192.168.0.0/24) --- Gateway (VPS) --- NAT-B (10.180.0.0/16)wg 隧道接口 IP 我这里采用的是 10.10.10.0/24 网段的 IP，如下：NAT-A：10.10.10.2/24GW：10.10.10.1/24NAT-B：10.10.10.3/24安装与配置环境依赖NAT-A、NAT-B、VPS 均采用一台 Linux PC 作为 WireGuard endpoint；WireGuard 安装过程不再赘述，见官方文档；iptables、iproute2配置过程GW 端# 1. 生成密钥对 wg genkey | tee privatekey | wg pubkey > publickey # 2. 创建 WireGuard tunnel 接口 ip link add dev wg0 type wireguard # 3. 为 wg 接口配置 IP 地址 ip address add dev wg0 10.10.10.1/24 # 4. 指定 gw 端 wg0 接口私钥及接听端口 wg set wg0 private-key $(cat privatekey) listen-port 60000 # 5. 启用 wg0 接口 ip link set up dev wg0 # 6. 开启流量转发 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf sysctl -p # > filter 表转发链中允许 wg0 接口的出入流量 iptables -A FORWARD -i wg0 -j ACCEPT iptables -A FORWARD -o wg0 -j ACCEPT # > nat 表动态伪装 snat iptables -t nat -A POSTROUTING -o $OUTBOUND_INTERFACE -j MASQUERADENAT-A 端# 1. 生成密钥对 wg genkey | tee privatekey | wg pubkey > publickey # 2. 创建 WireGuard tunnel 接口 ip link add dev wg0 type wireguard # 3. 为 WireGuard 接口配置 IP 地址 ip address add dev wg0 10.10.10.2/24 # 4. 指定 NAT-A 端接口密钥及 peer 信息（注意替换字段内容） # > allowed-ips: 指定需要经隧道的 IP 段，即指定相应 IP 段的静态路由；这里除了添加 WireGuard 网段，还需添加 NAT-B 网段地址，即相当于添加路由 ip route add 10.180.0.0/16 dev wg0 # > endpoint: 指定 gw 端的地址和监听端口 # > persistent-keepalive：用于保持长连接 wg set wg0 private-key $(cat privatekey) peer $GW_PUBKEY allowed-ips 10.10.10.0/24,10.180.0.0/16 endpoint $GW_IP:60000 persistent-keepalive 10 # 5. 启用 wg0 接口 ip link set up dev wg0 # 6. 开启流量转发 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf sysctl -p # > filter 表转发链中允许 wg0 接口的出入流量 iptables -A FORWARD -i wg0 -j ACCEPT iptables -A FORWARD -o wg0 -j ACCEPT # > nat 表动态伪装 snat iptables -t nat -A POSTROUTING -o $OUTBOUNG_INTERFACE -j MASQUERADENAT-B 端# 1. 生成密钥对 wg genkey | tee privatekey | wg pubkey > publickey # 2. 创建 WireGuard tunnel 接口 ip link add dev wg0 type wireguard # 3. 为 WireGuard 接口配置 IP 地址 ip address add dev wg0 10.10.10.3/24 # 4. 指定 NAT-B 端接口密钥及 peer 信息（注意替换字段内容） # > allowed-ips: 指定需要经隧道的 IP 段，相当于指定了相应 IP 段的静态路由；这里除了指定 WireGuard 网段，还添加了 NAT-A 网段地址，即相当于添加路由 ip route add 192.168.0.0/24 dev wg0 # > endpoint: 指定 gw 端的地址和监听端口 # > persistent-keepalive：用于保持长连接；设置为 0 代表不打开 wg set wg0 private-key $(cat privatekey) peer $GW_PUBKEY allowed-ips 10.10.10.0/24,192.168.0.0/24 endpoint $GW_IP:60000 persistent-keepalive 10 # 5. 启用 wg0 接口 ip link set up dev wg0 # 6. 开启流量转发 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf sysctl -p # > filter 表转发链中允许 wg0 接口的出入流量 iptables -A FORWARD -i wg0 -j ACCEPT iptables -A FORWARD -o wg0 -j ACCEPT # > nat 表动态伪装 snat iptables -t nat -A POSTROUTING -o $OUTBOUNG_INTERFACE -j MASQUERADEGW 端添加 peer# 1. 添加 peer NAT-A，注意 allowed-ips 添加 NAT-A 网段，相当于添加到 NAT wg set wg0 peer $NAT_A_PUBKEY allowed-ips 10.10.10.2/32,192.168.0.0/24 # 2. 添加 peer NAT-B，注意 allowed-ips 添加 NAT-B 网段 wg set wg0 peer $NAT_B_PUBKEY allowed-ips 10.10.10.3/32,10.180.0.0/16至此，就全部配置完成了，NAT-A (192.168.0.0/24) 与 NAT-B (10.180.0.0/16) 两个 LAN 可以通过 GW 进行通信了。如需添加更多 NAT 网络，同样操作即可，主要就是 allowed-ips 即路由的设置。配置文件上述过程可以编写成配置文件（默认保存位置 /etc/wireguard）通过 wg-quick 命令快速运行，这里不再赘述（可参考 WireGuard 搭建和使用折腾小记），各端配置文件示例如下：# GW [Interface] ListenPort = 60000 Address = 10.10.10.1/24 PrivateKey = $GW_PRIVATE_KEY PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o $OUTBOUND_INTF -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o $OUTBOUND_INTF -j MASQUERADE [Peer] PublicKey = $NAT_A_PUBKEY AllowedIPs = 10.10.10.2/32, 192.168.0.0/24 [Peer] PublicKey = $NAT_B_PUBKEY AllowedIPs = 10.10.10.3/32, 10.180.0.0/16 # NAT-A [Interface] Address = 10.10.10.2/24 PrivateKey = $NAT_A_PRIVATEKEY [Peer] PublicKey = $GW_PUBKEY AllowedIPs = 10.10.10.0/24,10.180.0.0/16 Endpoint = $GW_IP:60000 PersistentKeepalive = 10 # NAT-B [Interface] Address = 10.10.10.3/24 PrivateKey = $NAT_B_PRIVATEKEY [Peer] PublicKey = $GW_PUBKEY AllowedIPs = 10.10.10.0/24,192.168.0.0/24 Endpoint = $GW_IP:60000 PersistentKeepalive = 10参考https://staaldraad.github.io/2017/04/17/nat-to-nat-with-wireguard/https://www.wireguard.com/
前言最近尝试使用了 Windows Terminal 和 WSL，同时安装了许久的 remote development 扩展也终于尝试着用了起来。遂对 Windows Terminal、WSL、VSCode+Remote Development 等配置内容进行一个简单的记录，方便自己换环境时快速恢复。WSL因为自己平时使用的 VPS 都一直采用的是 Debian，所以 WSL 我也就继续使用 Debian 了。安装比较简单，开启 Linux 子系统功能后 Windows 应用商店下载就好了。文件权限WSL 访问 Windows 盘符下的文件时，显示的权限都是 777，同时，WSL 新建的文件夹权限也是 777，需要稍微修改下更改下默认的权限。# 新建 /etc/wsl.conf 文件 [automount] enabled = true root = /mnt/ options = "metadata,umask=22,fmask=111" mountFsTab = true #[filesystem] #umask = 022 # 新建 /etc/fstab 文件，挂载 c 盘，以使用 Windows 下的应用程序 C:\ /mnt/c drvfs rw,noatime,uid=1000,gid=1000,metadata,umask=22,fmask=11 0 0 # zsh 配置好后，在 .zshrc 文件中添加 if "$(umask)" == '000' ; then umask 022 fi常用配置基础软件sudo apt install vim git curlshell 配置（oh-my-zsh）shell：zsh (oh-my-zsh)prompt: pureplugin: git z zsh-autosuggestions zsh-syntax-highlighting# 安装 zsh sudo apt install zsh # 安装 oh-my-zsh 并启用 sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # 下载插件 # > zsh-syntax-highlighting git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting # > zsh-autosuggestions git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions # 下载 pure prompt mkdir -p "$HOME/.zsh" git clone https://github.com/sindresorhus/pure.git "$HOME/.zsh/pure" # 修改 ~/.zshrc # > 添加 pure path fpath+=$HOME/.zsh/pure # > 修改主题为 '' ZSH_THEME="" # > 使 pure 生效，添加在 source $ZSH/oh-my-zsh.sh 后 autoload -U promptinit; promptinit prompt pure # > 添加插件 plugins=(git z zsh-autosuggestions zsh-syntax-highlighting)python 环境配置# 系统 python 环境 sudo apt install python3 python3-pip ln -s /usr/bin/python3 /usr/bin/python # 安装 pyenv git clone https://github.com/pyenv/pyenv.git ~/.pyenv echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.zshrc # 安装 pipenv pip3 install pipenv / sudo apt install pipenvWindows Terminal暂时未对 Windows Terminal 进行过多的自定义，默认的英文字体挺满意的，整体也很简洁，暂时不想过多折腾了。profiles 创建示例 （WT 下拉栏选择设置进行编辑）：{ // guid 需唯一 "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441c5}", "hidden": false, "name": "Ubuntu-10.180.8.24", // 使用 SSH Keys 登录，避免输入密码 "commandline": "ssh root@10.180.8.24 -i C:\\users\\withdewhua\\.ssh\\id_rsa", // 强制使用自定义的 tab title "suppressApplicationTitle": true }Remote Developmentvscode 扩展商店搜索安装即可，安装后左下角可看到远程状态栏：点击可看到具体的连接选项：Remote WSL两种打开方式：在 vscode 中直接连接 wsl（即图中的 Remote-WSL）在 wsl 下，使用命令 code 打开Remote SSH在 vscode 中通过 Remote-SSH 进行配置和连接，可配置使用密钥登录，避免输入密码：# SSH configuration file Host 10.180.10.200 HostName 10.180.10.200 User root ForwardAgent yes # 指定私钥文件路径 IdentityFile C:\Users\withdewhua\.ssh\id_rsa参考WSL 使用中遇到的问题及解决方案 #2 - DrvFs 文件系统权限问题Windows Terminal Tab Title Setting
前言好久没用 Manjaro 了，今天准备打开看下，发现因为 Windows 的自动更新，导致 Linux 的引导没了。本来准备修复一下的，后来想想反正 Linux 下没啥重要数据，就将 Manjaro-deepin 重装成了 Manjaro-xfce。虽然之前已经用过一段时间，发现太久没用很多东西都忘了，然后又要重新进行搜索一番。虽说网上关于 Manjaro 的配置文章已经非常多了，但是还是想重新记录一下，帮助自己下次快速恢复下环境。系统配置pacman 源sudo pacman-mirrors -i -c China -m rank在弹出框内选择自己需要的源。AUR 源AUR 是为用户而建、由用户主导的 Arch 软件仓库。# 添加 archlinuxcn 仓库 sudo nano /etc/pacman.conf # 在末尾依次输入 [archlinuxcn] SigLevel = Optional TrustedOnly Server = http://mirrors.163.com/archlinux-cn/$arch # 保存后退出 # 更新 sudo pacman -Syy # 安装 archlinuxcn-keyring sudo pacman -S archlinuxcn-keyring # 更新 sudo pacman -Syyuyaypacman 不支持 AUR 源，采用 yay AUR 助手在保证 pacman 功能的同时支持 AUR 仓库。sudo pacman -S yaySnap部分软件通过 Snap 进行发布，可安装 Snap 进行补充sudo pacman -S snapd sudo systemctl enable --now snapd.socket sudo ln -s /var/lib/snapd/snap /snapShell采用 oh-my-zsh，建议参考 定制终端# 插件，安装之后在 $HOME/.zshrc 中 plugin 行添加以下插件名 # > zsh-syntax-highlighting git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting # > zsh-autosuggestions git clone git://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions软件配置Web BrowserChromeyay -S google-chromeClash# github 下载 release 包 # https://github.com/Dreamacro/clash/releases # 运行 clash，生成配置文件夹后关闭 chmod +x clash && ./clash # 将自己的配置文件覆盖默认配置 $HOME/.config/clash/config.yaml # 采用 pm2 管理 #> 1. 安装 pm2 wget -qO- https://getpm2.com/install.sh | bash #> 2. 启动 clash pm2 start clash # 设置代理 cat >> /etc/profile << EOF export http_proxy=http://127.0.0.1:7890 export https_proxy=http://127.0.0.1:7890 EOF # 注销后重新登录Input Methodibus-rime# 安装 yay -S ibus-rime # 配置环境变量 nano $HOME/.xprofile export GTK_IM_MODULE=ibus export XMODIFIERS=@im=ibus export QT_IM_MODULE=ibus ibus-daemon -x -d # 注销生效 # 配合坚果云恢复 Rime 环境Cloud Storage坚果云yay -S nutstoreText EditorVimyay -S vimTyporayay -S typoraVisual Studio Code# aur yay -S visual-studio-code-binInstant MessagingTelegramyay -S telegram-desktop-binTerminalelecterm下载 electerm 对桌面自带终端进行补充# 使用 snap sudo snap install electerm # aur yay -S electerm-binthefuck提高终端体验，用于更正输入的命令pip3 install --user thefuck # 添加 alias vim ~/.zshrc # 粘贴以下内容后保存 eval $(thefuck --alias) # 生效 source ~/.zshrcNotevnoteyay -S vnote-binUtilitiesflameshotyay -S flameshot # 设置快捷键 # > Setting Manager --> Keyboard # > 添加触发器：/usr/bin/flameshot guiredshiftyay -S redshiftBitwardenyay -S bitwarden美化配置中文字体配置# 文泉驿 yay -S wqy-bitmapfont wqy-microhei wqy-microhei-lite wqy-zenhei # 思源字体 yay -S noto-fonts-cjk adobe-source-han-sans-cn-fonts adobe-source-han-serif-cn-fonts # emoji yay -S noto-fonts-emoji