在 OpenMediaVault 上使用 SnapRAID 和 MergerFS

首先来介绍一下这两个软件,SnapRAID 和 MergerFS,不同于其他现有的 NAS 系统,可以把 OpenMediaVault 看成一个简单的带有 Web UI 的 Linux 系统,他使用最基本的文件系统,没有 ZFS 的实时文件冗余,所以需要 SnapRAID 提供的冗余来保护硬盘数据安全。SnapRAID 需要一块单独的硬盘来存放校验数据,这个盘的容量必须大于等于其他任何一个数据盘。SnapRAID 采用快照的方式来做数据冗余,这种设计避免了所有硬盘在没有数据操作情况下也要运转来实时数据备份的消耗。

MergerFS 则是一个联合文件系统,可以将多块硬盘挂载到一个挂载点,通过 MergerFS 来自动决定数据该存储在哪块硬盘上。

Prerequisite

先决条件:

  • 至少三块硬盘,两块硬盘用来演示 MergerFS 合并,一块硬盘用来作为 SnapRAID 冗余备份
  • 一个安装好的 OpenMediaVault 以及安装好 OMV-Extras 相关的插件
  • System - Plugin 下安装 openmediavault-snapraidopenmediavault-unionfilesystem 插件

MergerFS

MergerFS 是一个联合文件系统 (union file system),MergerFS 会将多块硬盘,或者多个文件夹合并到 MergerFS pool 中,这样一个系统就会有一个统一的文件入口,方便管理。

选用 MergerFS 另外一个理由就是,通过 MergerFS 合并的目录并不会对数据进行条带化的处理,每块硬盘上还是保存原来的文件目录和文件,任意一块硬盘单独拿出来放到其他系统上,不需要额外的逻辑卷的配置,就可以直接挂载读取这个硬盘的数据。

创建 MergerFS pool

  • Storage > Union Filesystems
  • Add
  • Give the pool a name
  • In the Branches 选项中,选择所有要合并的磁盘,这里不要选 parity 的磁盘
  • Create policy 中选择 Most free space
  • Minimum free space 中选择一个合适的大小,默认也可以
  • Option 中,默认
  • Save
  • Apply

这样以后在文件系统中就会看到新创建的联合目录。在创建共享文件夹的时候就可以在合并的联合文件系统上进行。

在创建了 MergerFS Pool 后,在 OpenMediaVault 的文件目录 /srv 目录下会多出一个文件夹,这个文件夹就会存放 MergerFS Pool 中的数据。

SnapRAID

SnapRAID 是一个磁盘阵列的冗余备份工具,它可以存储额外的奇偶校验信息用来恢复数据。

SnapRAID 适用于家庭媒体服务器,适合于存储多数不经常变动的大文件场景。

特性:

  • 所有的数据都经过哈希处理,以确保数据完整性来避免可能的磁盘损坏
  • 如果故障磁盘太多而无法恢复,则只会丢失故障磁盘上的数据。其他磁盘中的所有数据都是安全的
  • 如果意外的删除了某些文件,可以轻松的恢复
  • 可以在已经有数据的硬盘上使用
  • 硬盘可以有不同的大小
  • 可以在任何时候添加磁盘
  • 不会占用数据,可以在任何时候停用 SnapRAID 而不用重新格式化
  • 访问数据时,只有一块磁盘会转动,节省电源以及减少噪声

SnapRAID 作为一个备份工具非常强大,强烈推荐阅读官网上关于 SnapRAID 和 [[unRAID]], ZFS 等系统或文件系统提供的备份的对比 1

在 OpenMediaVault 中使用 SnapRAID :

设置需要保护的磁盘

假设三块硬盘中前两块用来存储重要的数据,第三块用来存放奇偶校验信息。那么首先设置数据盘:

  • 在 Services > SnapRAID 菜单,click Drives 选项
  • 点击 Add
  • 选择第一块硬盘
  • 起一个友好的名字
  • 选择 Content
  • 选择 Data
  • 不需要选 Parity
  • 保存

重复上面的步骤将第二块磁盘也添加进来。

设置奇偶校验盘

添加奇偶校验信息盘的时候,和上面步骤相似。不过要注意的是在添加磁盘时

  • 不需要选择 Content
  • 不需要选择 Data
  • 选择 Parity

然后点击保存。

SnapRAID 操作

在添加完硬盘之后,可以进行同步操作:

  • Sync, 同步数据,并更新校验,默认进行差量同步
  • Scrub,检查潜在的错误
  • Diff,列出和上一次存在的差别
  • Fix,尽可能恢复到上一次同步状态
  • Fix silent,修复潜在的错误

SnapRAID Scrub

最后设置 SnapRAID Scrub,scrubbing 的目的是检查数据盘和校验信息盘的错误。可以在 SnapRAID 的 settings 界面中启用 Scheduled diff,启用后会自动创建一个周期性 Crontab 任务。

SnapRAID Rules

对于一些不需要 SnapRAID 进行冗余校验的目录,可以在 Rules 选项中进行排除。比如说经常变动的 metadata 信息,Docker 容器配置,虚拟机等等。

SnapRAID Scheduled Jobs

周期性的执行这些命令。

sync 命令会更新 parity 信息,所有磁盘中修改的文件都会被读取,然后对应的 parity 信息都会更新。

touch 命令会将所有拥有 sub-second 时间戳的文件设置为 0,这样提高了 SnapRAID 识别移动和复制过的文件的能力,可以消除可能的重复。

scrub 命令会验证磁盘阵列中的数据和 sync 命令产生的 hash.

# Run this command for the first time
snapraid sync

# Run this command after the sync is completed
snapraid scrub

# Run this command for status
snapraid status

mergerfsfolders vs unionfilesystems

在安装完 OMV-Extras 后会在插件中看到两个相似的插件:

  • mergerfsfolders, 利用 mergerfs 将多个文件夹挂载到同一个挂载点
  • unionfilesystems,使用 union filesystem mergerfs 来将多块硬盘挂载到一个挂载点

其主要区别就在于一个是合并文件夹,一个是合并硬盘。所以如果对于全新的硬盘,没有任何数据,可以直接利用 unionfilesystems 来将多块硬盘组合成一个 pool.

reference


2020-06-07 openmediavault , snapraid , raid , backup , mergerfs , linux , debian

Linux 设备中的 major 和 minor 数字

Today, when I visit tldr issue and I saw a talk about the command lsblk, although I used a lot before, I really don’t understand the MAJ:MIN in the result. Most time, I use it to check the harddrive disk and partitions.

lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0    7:0    0 260.7M  1 loop /snap/kde-frameworks-5-core18/32
loop1    7:1    0 253.5M  1 loop /snap/electronic-wechat/7
loop2    7:2    0    69M  1 loop /snap/telegram-desktop/1634
loop3    7:3    0  21.3M  1 loop /snap/communitheme/1987
loop4    7:4    0    55M  1 loop /snap/core18/1754
loop5    7:5    0  93.9M  1 loop /snap/core/9066
loop6    7:6    0  54.8M  1 loop /snap/gtk-common-themes/1502
loop7    7:7    0  93.8M  1 loop /snap/core/8935
loop8    7:8    0 373.5M  1 loop /snap/anbox/158
loop10   7:10   0 397.1M  1 loop /snap/redis-desktop-manager/335
loop11   7:11   0 160.2M  1 loop /snap/gnome-3-28-1804/116
loop12   7:12   0    32M  1 loop /snap/git-fame/15
loop13   7:13   0 149.2M  1 loop /snap/postman/109
loop14   7:14   0    16M  1 loop /snap/communitheme/1768
loop15   7:15   0    55M  1 loop /snap/core18/1705
loop16   7:16   0 374.9M  1 loop /snap/redis-desktop-manager/400
loop17   7:17   0    69M  1 loop /snap/telegram-desktop/1627
loop18   7:18   0  62.1M  1 loop /snap/gtk-common-themes/1506
loop19   7:19   0  32.1M  1 loop /snap/git-fame/23
loop20   7:20   0 310.8M  1 loop
loop21   7:21   0 163.6M  1 loop /snap/postman/110
sda      8:0    0 931.5G  0 disk
├─sda1   8:1    0 214.9G  0 part
├─sda2   8:2    0  16.3G  0 part [SWAP]
└─sda3   8:3    0 700.4G  0 part /media/Backup
sdb      8:16   0 232.9G  0 disk
├─sdb1   8:17   0 232.9G  0 part /
└─sdb2   8:18   0     2M  0 part

However, when I take a close look at the output, I can see only the disk device output, but also see the snap package output. So I started to search informations about the MAJ:MIN.

Major and minor device number

We all know that under linux, all devices are managed under /dev folder. So lets check the special device first:

ls -al /dev/zero
crw-rw-rw- 1 root root 1, 5 May 29 19:40 /dev/zero

We can see that, ls output is a little bit different from normal output, /dev/zero device’s major number is 1 and minor is 5

Then let’s check /proc/devices:

cat /proc/devices

This file contains the list of device drivers configured into the current running kernal(block and character).1

We can see that under /proc/devices file, there are a list of number and strings. For example:

Character devices:
1 mem
5 /dev/tty
5 /dev/console
7 vcs

Block devices:
8 sd

Each device node’s type (block or character) and numbers serve as identifiers for the kernel.

On Linux, the canonical list of devices, with a brief explanation of their function, is maintained in the kernel.

  • major number: identify the driver associated with the device. For example /dev/null and /dev/zero are both managerd by driver 1, whereas virtual consoles and serial terminals are managed by driver 4. Kernal uses the major number at open time to dispatch execution to the appropriate driver.
  • minor number: refers to an instance, which is used by the driver itself, specified by the major number. Minor number is used for driver to identify the difference between devices.

After version 2.4, the kernel introduced a new feature, the device file system or devfs. But for now most distributions do not add these feature. Read more from here.

When devfs is not being used, adding a new driver to the system means assigning a major number to it. The assignment should be made at driver (module) initialization by calling the following function, defined in <linux.fs.h>:

int register_chrdev(unsigned int major, const char* name, struct file_operations* fops);

Once the driver has been registered in the kernel table, its operations are associated with the given major number. And a name must be inserted into the /dev directory and associated with your driver’s major and minor numbers.

The command to create a device node on the filesystem is called mknod:

mknod /dev/scull0 c 254 0

Explain:

  • c means: create a char device
  • with major nubmer 254
  • and minor number 0, minor number should be in the range 0 to 255

reference

  • 《Linux Device Drivers, Second Edition by Jonathan Corbet, Alessandro Rubini》

2020-06-01 linux , device , hardware , kernel

利用 AdGuard Home 过滤广告

过滤广告的方式有非常多的方式,比如加本地 Host,比如浏览器中的 Adblock 插件,或者之前在 OpenWrt 或者其他固件上添加的广告过滤插件,甚至在 Android 上也用过通过在本地设定一个代理,所有的流量走代理,然后在代理中将广告过滤掉的应用,那么这个 AdGuard Home 有什么优势呢?在我看来,吸引我使用它的几个优势是:

  • 只需要架设一次,所有局域网中的设备都可以享用,而不需要各个设备单独配置
  • 可视化,最早是在 Twitter 上看到有人分享 AdGuard 后台,大部分情况下,广告过滤插件或者应用都是默默在后台工作,AdGuard 让一切变得可见,那这样就可以分析哪些网站在后面偷偷地做坏事
  • 占用资源小,一个树莓派即可,并且源代码是开放的
  • 支持安全的 DNS 解析

AdGuard 官方的文章也总结了 AdGuard Home 的几大优势:1

  • Ad Blocking, 最基础的服务,可以减少网页的体积,加快速度
  • Browsing Security
  • Parent Control
  • Safe Search,可以过滤成人内容
  • Custom upstream servers, 可以自定义上游的 DNS 服务器
  • Filter lists, 可以自定义过滤列表
  • Query Log,也就是我提到的可视化的访问日志

AdGuard vs AdGuard Home

开始之前要先声明一下,这篇文章后面提到的 AdGuard Home 都会是 AdGuard 这个公司提供的一个产品 —- AdGuard Home.1

AdGuard Home 的原理

上面提到过很多不同的广告过滤方式,但是 AdGuard Home 采用完全不同的方式。首先来介绍一下什么是 AdGuard Home,AdGuard Home 是一个过滤全网范围的广告和追踪代码的 DNS Server,它的设计目的是让用户来全权掌握整个网络环境,它不依赖于任何客户端。所以从本质上来讲 AdGuard Home 是一个 DNS 服务器,通过屏蔽掉黑名单的域名来达到过滤广告的目的。

在 Raspberry Pi 中安装使用 AdGuard Home

在树莓派上安装 AdGuard Home 非常简单,安装 wiki 上执行即可。

给 Raspberry Pi 设定静态 IP 地址

Raspberry Pi 的网络配置 /etc/dhcpcd.conf,在下方添加

interface eth0
static ip_address=192.168.2.3/24
static routers=192.168.2.1
static domain_name_servers=192.168.2.1

注意我这里是使用的 eth0 接口,也就是网线连接的,如果使用 WiFi,那么需要设定 wlan0

然后 sudo reboot 重启树莓派。

之后我的树莓派静态 IP 地址就是 192.168.2.3

验证安装

上面提到过保证树莓派静态地址,然后执行安装向导,设定后台管理页面的端口(一般为 80,可以自行修改),以及 DNS 服务端口(一般为 53)。这样 53 端口就对外提供了 DNS 服务,可以通过

nslookup douban.com 192.168.2.3

来验证 DNS 服务器正常工作,如果正常工作返回

Server:         192.168.2.3
Address:        192.168.2.3#53

Non-authoritative answer:
Name:   douban.com
Address: 154.8.131.171
Name:   douban.com
Address: 154.8.131.172
Name:   douban.com
Address: 154.8.131.165

验证拦截

nslookup doubleclick.net 192.168.2.3
Server:         192.168.2.3
Address:        192.168.2.3#53

** server can't find doubleclick.net: NXDOMAIN

设置路由器和其他设备

如果能够设置路由器,直接去路由器管理后台,将网络的 DNS,改为树莓派的地址,比如我的 192.168.2.3 即可。其他设备直接就生效了。如果改不了路由器就只能每一个设备改了。

其他管理命令

AdGuardHome 安装的命令:

sudo ./AdGuardHome -s install

其他管理命令:

  • AdGuardHome -s uninstall - uninstalls the AdGuard Home service.
  • AdGuardHome -s start - starts the service.
  • AdGuardHome -s stop - stops the service.
  • AdGuardHome -s restart - restarts the service.
  • AdGuardHome -s status - shows the current service status.

Docker 安装

因为 AdGuardHome 是使用 Go 所写,所以跨平台天然支持,Docker 安装自然也非常容易。

docker pull adguard/adguardhome
docker run --name adguardhome \
-v ~/adguardhome/workdir:/opt/adguardhome/work \
-v ~/adguardhome/confdir:/opt/adguardhome/conf \
-p 53:53/tcp -p 53:53/udp \
-p 67:67/udp -p 68:68/tcp -p 68:68/udp \
-p 8080:80/tcp -p 443:443/tcp \
-p 853:853/tcp -p 3000:3000/tcp \
--restart=always -d adguard/adguardhome

说明:

  • -p 67:67/udp -p 68:68/tcp -p 68:68/udp 用来将 AdGuard Home 作为 DHCP 服务,可不映射
  • -p 443:443/tcp 如果要使用 AdGuard Home 作为 HTTPS/DNS-over-HTTPS 服务器
  • -p 853:853/tcp 作为 DNS-over-TLS 服务器
  • -p 784:784/udp 作为 DNS-over-QUIC 服务器
  • -p 5443:5443/tcp -p 5443:5443/udp 作为 DNSCrypt 服务器

参数可以参数官方网站:

设置

所有安装完成之后就可以进入后台进行一番初始化设置,AdGuardHome 默认的设置就已经足够使用了,但假如想更加精细化地设置,比如说上游 DNS,DNS-over-HTTPS,DNS-over-TLS 等等,都可以在后台看到。

AdGuard Home 所有的配置参数都保存在一个名为 AdGuardHome.yaml 的配置文件中。这个配置文件默认路径通常为 AdGuard Home 二进制文件 AdGuardHome 所在的目录。

已知的 DNS 提供商

AdGuard 提供了一份非常详细的 DNS 服务提供商的列表:

进入后台可以看到 AdGuard 默认使用的是

https://dns10.quad9.net/dns-query

不过在国内可能在测试上游 DNS 服务器的时候

服务器 “https://dns10.quad9.net/dns-query”:无法使用,请检查你输入的是否正确

不过问题也不大,勾选“并行请求”,同时用下方的 DNS 提供商的

tls://8.8.8.8
tls://8.8.4.4
tls://dns.google
tls://dns.adguard.com
119.29.29.29
1.2.4.8
tls://1.1.1.1
tls://1.0.0.1
https://dns10.quad9.net/dns-query
8.8.8.8
114.114.114.114
119.29.29.29
223.5.5.5

其他一些

不过这些国内公共 DNS 暂时不支持 DNS over TLS。

Bootstrap DNS 服务器

1.1.1.1:53
1.0.0.1:53
9.9.9.10
149.112.112.10
114.114.114.114:53
2620:fe::10
2620:fe::fe:10

过滤器

AdGuard Home 自身已经内置了一些过滤规则,并且 AdGuard Home 兼容 Adblock 的过滤规则。

  • EasyList China 国内网站广告过滤的主规则 https://easylist-downloads.adblockplus.org/easylistchina.txt
  • Anti Ad 国内过滤规则 https://gitee.com/privacy-protection-tools/anti-ad/raw/master/easylist.txt
  • neohosts https://cdn.jsdelivr.net/gh/neoFelhz/neohosts@gh-pages/basic/hosts.txt
  • EasyPrivacy https://easylist-downloads.adblockplus.org/easyprivacy.txt
  • AdGuard Simplified Domain Names filt https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
  • AdAway https://adaway.org/hosts.txt
  • CJX’s Annoyance List https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-annoyance.txt

外延

.in-addr.arpa

在运行 AdGuard Home 一天后,观察请求域名排行榜,有一些奇怪的域名请求频率异常高,显示着一个 IP 地址,后面接着 .in-addr.arpa. 这就引起了我的好奇,查询之后发现这一类的地址叫做 Reverse DNS lookup,反向 DNS 查询。都知道 DNS 是将域名转换成 IP 地址,那么反过来查询 IP 地址关联的域名就是反向 DNS 查询。

如果想要反解析一个给定的 IP 地址,需要反转 IP 地址,然后在后面添加一个特殊的域名,比如 in-addr.arpa,比如想要反解析 8.8.4.4 对应的域名,需要构造这样的地址:

4.4.8.8.in-addr.arpa

然后可以使用 dig -x 4.4.8.8.in-addr.arpa @8.8.8.8 来进行反向解析查看结果。

SERVFAIL

观察查询日志,在我的内网里面能看到不少的 SERVFAIL,这里就顺带复习一下 DNS RCODE2,DNS 请求的返回码:

  • NOERROR(0),成功响应,解析成功
  • SERVFAIL(2), 服务器失败,域名的权威服务器拒绝响应或者响应 REFUSE,递归服务器返回 Rcode 值为 2 给 CLIENT
  • NXDOMAIN(3), 不存在的记录,域名在权威服务器不存在
  • REFUSE(5),请求的 IP 不在该 DNS 服务器服务的范围

其他更多的码可以参考这里.

AdGuard Home 相较于 Pi-Hole 的优势

AdGuard Home 和 Pi-Hole 利用相似的原理可以达到基本一致的效果,但是 AdGuard Home 相较于 Pi-Hole 有如下几方面的优势:

  • AdGuard Home 支持加密的 DNS 上游服务器 Encrypted DNS upstream servers (DNS-over-HTTPS, DNS-over-TLS, DNSCrypt)
  • 更加完整的过滤系统,和家长控制
  • 安全搜索结果
  • 访问控制,可以实现精确的谁能访问 DNS 服务器

Configuration

AdGuard Home 的配置文件是 yaml 格式,格式非常易读。

DNS TTL

在设置里面有一个 DNS TTL 的设置,这里 TTL 是 Time to Live 缩写,指的是 DNS 需要缓存多久然后才去刷新新的解析结果。

当改变 DNS 配置的时候,需要花费一些时间来通知互联网这个修改,比如修改一个域名对应的 IP 地址,修改 MX 记录等等,TTL 配置就是告诉互联网需要缓存这一次的结果多久才需要再来请求信息。

那么在家用环境里面可以根据自己的情况设置一个合理的值,我个人觉得大部分网站设置一个 10 分钟的缓存就可以了。

bind_host: 0.0.0.0
bind_port: 80
users:
- name: admin
  password: b2a
http_proxy: ""
language: ""
rlimit_nofile: 0
debug_pprof: false
web_session_ttl: 720
dns:
  bind_host: 0.0.0.0
  port: 53
  statistics_interval: 1
  querylog_enabled: true
  querylog_interval: 90
  querylog_size_memory: 1000
  anonymize_client_ip: false
  protection_enabled: true
  blocking_mode: default
  blocking_ipv4: ""
  blocking_ipv6: ""
  blocked_response_ttl: 10
  parental_block_host: family-block.dns.adguard.com
  safebrowsing_block_host: standard-block.dns.adguard.com
  ratelimit: 100
  ratelimit_whitelist: []
  refuse_any: true
  upstream_dns:
  - tls://8.8.8.8
  - tls://8.8.4.4
  - 119.29.29.29
  - 1.2.4.8
  - 114.114.114.114
  - 223.5.5.5
  bootstrap_dns:
  - 9.9.9.10
  - 149.112.112.10
  - 2620:fe::10
  - 2620:fe::fe:10
  all_servers: true
  fastest_addr: false
  allowed_clients: []
  disallowed_clients: []
  blocked_hosts: []
  cache_size: 4194304
  cache_ttl_min: 600
  cache_ttl_max: 0
  bogus_nxdomain: []
  aaaa_disabled: false
  enable_dnssec: false
  edns_client_subnet: false
  filtering_enabled: true
  filters_update_interval: 24
  parental_enabled: false
  safesearch_enabled: false
  safebrowsing_enabled: false
  safebrowsing_cache_size: 1048576
  safesearch_cache_size: 1048576
  parental_cache_size: 1048576
  cache_time: 30
  rewrites: []
  blocked_services: []
tls:
  enabled: false
  server_name: ""
  force_https: false
  port_https: 443
  port_dns_over_tls: 853
  allow_unencrypted_doh: false
  strict_sni_check: false
  certificate_chain: ""
  private_key: ""
  certificate_path: ""
  private_key_path: ""
filters:
- enabled: true
  url: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
  name: AdGuard DNS filter
  id: 1
- enabled: true
  url: https://adaway.org/hosts.txt
  name: AdAway
  id: 2
- enabled: false
  url: https://www.malwaredomainlist.com/hostslist/hosts.txt
  name: MalwareDomainList.com Hosts List
  id: 4
- enabled: true
  url: https://easylist-downloads.adblockplus.org/easylistchina.txt
  name: EasyList China
  id: 1593851523
- enabled: true
  url: https://www.i-dont-care-about-cookies.eu/abp/
  name: I don't care about cookies
  id: 1593851524
- enabled: false
  url: https://gitee.com/privacy-protection-tools/anti-ad/raw/master/easylist.txt
  name: anti ads
  id: 1593851525
- enabled: true
  url: https://filters.adtidy.org/extension/chromium/filters/224.txt
  name: AdGuard Chinese filter
  id: 1594425715
whitelist_filters: []
user_rules:
- '||open.trackerlist.xyz^$important'
- ""
dhcp:
  enabled: false
  interface_name: ""
  gateway_ip: ""
  subnet_mask: ""
  range_start: ""
  range_end: ""
  lease_duration: 86400
  icmp_timeout_msec: 1000
clients: []
log_file: ""
verbose: false
schema_version: 6

更加具体的配置选项可以参考:

reference


2020-05-29 adguard , adguard-home , adblock , browser , dns

Obsidian 未来的笔记应用

看过我过去文章的人都知道在此之前我都使用 WizNote 来作为本地笔记应用,但是这两年在记笔记这件事情上出现了非常多的可能性,虽然我本人一直在关注着不同类型的笔记应用,从传统的 Evernote,OneNote 到 Notion 等等模块化的笔记应用,多多少少也尝试了一下,但是一直没有深入的去用,因为就我目前的需求,WizNote + 本地的 markdown + vim + git 基本就满足了,直到昨天晚上我在豆瓣上阅读了笔记类软件的双向链新浪潮 这篇文章,然后我发现原来我在记笔记的时候遇到的一些问题原来还有这样一种解决方式。

存在的问题

我在用纯手工方式管理笔记的时候遇到的这些问题,中间我也曾想使用 wiki 来代替笔记,尝试了 [[GitBook]], BookStack, [[TiddlyWiki]] 等等,都或多或少的有些问题。

多媒体管理

我用原来 markdown + vim + git 的方式存在的最大的问题就是多媒体文件的管理,如果我只简单的记录文本,没有那么多的媒体(图片,音视频) 需要管理,还是能够非常方便的进行管理的,而一旦图片等等媒体文件成倍增长时,这个时候我就陷入了管理困境。

而 Obsidian 将媒体文件和 markdown 文件存放到一起,并且可以简单的使用名字相互进行关联,这使得管理变的轻松。内建的录音器甚至可以直接录音后在文件中插入音频内容。

链接

关于内链,可能我的思路还停留在上个世纪的“超链接”,我文章的大部分链接都是通过超链接相互关联的,而在 WizNote 中,将不同的笔记内容进行关联,我就只能使用 tag,或者手工进行分类来实现。而分类和 Tag 存在一些缺点,比如有些内容可能并不能以某一个分类概括,又或者可能被划分到多个分类下,而 Tag 虽然可以一定程度上避免分类的问题,但是在确定 Tag 的时候就会遇到如何定义 Tag 的问题,比较笼统的定义还是比较精确的定义。

而在 Obsidian 中,只需要 [[topic]] 就可以非常方便的实现链接到名为 topic 的文章,而这一切都是 Obsidian 做的,并且 topic 的这篇笔记甚至可以不存在,在写的时候写下,然后 Obsidian 会生成链接,只需要点击就可以快速的创建这个 topic. 到这里我才发现原来我那种手工管理内部链接的方式简直太愚蠢了。更甚至通过链入和链出就形成了一个非常庞大完整的网状结构,这样就将历史的笔记也可以以这种形式激活起来,不断的丰富自己的知识网络。

在 Obsidian 中通过 internal link 和 graph view 完美的解决了笔记之间关联的问题。

What is Obsidian?

Obsidian 这样介绍自己:

Obsidian is a both a Markdown editor and a knowledge base app.

直接献上官网地址:

再来看看预览图:

obsidian note taking

特性

Obsidian 这些功能是吸引我让我尝试的理由。

本地化数据

所有数据都以本地文本形式保存。其他一些软件使用私有的格式保存用户数据是让我避而远之的原因之一。

Obsidian 管理的所有数据都在本地,那也就意味着用户可以选用自己喜欢的任何同步工具进行数据加密,同步等等。现成的同步方案 Dropbox 等等,或者 Git ,而我选择使用另一款 分布式同步工具 Syncthing

obsidian note taking create notes

如果上手使用 Obsidian,那么第一感受就是可以在笔记的任何地方非常快速的创建内链,通过内链的方式,系统里面的笔记就形成了网状的结构,这也是 bi-directional linking 双向内链的精髓之处。

Obsidian 在官方的 Demo 中引述了 [[洛克 约翰 洛克]] 在 1690 年发表的 《An Essay Concerning Human Understanding》来解释人类思想形成的方式:

The acts of the mind, wherein it exerts its power over simple ideas, are chiefly these three:

  1. Combining several simple ideas into one compound one, and thus all complex ideas are made.
  2. The second is bringing two ideas, whether simple or complex, together, and setting them by one another so as to take a view of them at once, without uniting them into one, by which it gets all its ideas of relations.
  3. The third is separating them from all other ideas that accompany them in their real existence: this is called abstraction, and thus all its general ideas are made.

简单的翻译:

  • 首先将几个简单的想法组合成一个复杂的想法,从而完成所有复杂的思维过程;
  • 第二,是将简单或复杂的两个想法放到一起,相互设置关联,方便查看它们,而同时又不将它们融合到一起,从而可以一眼查看所有想法之间的关联。
  • 第三,是将这些想法与实际存在的所有其他思想分开:这称为抽象思维,这是所有普适思想形成的过程。

笔记的层级关系 (Hierarchy) 实现了第一点,而内部链接 (linking) 正是第二点,而第三点如何在笔记中应用, Obsidian 的作者也没有想清楚,但他也说了,这可能是更高阶的抽象 —- but it might have something to do with programming or macros.

用户拥有所有工具链

记笔记是一件非常个人化的事情,这也就意味着不可能有一个包容一切的解决方案适用于所有人。

Note-taking is an incredibly personal thing

所以 Obsidian 并不会武断地尝试去提供一个完整的产品,而是提供一个基础功能以及很多的功能模块,用户可以自己创造并实现自己的需求。

最基础的功能包括,查看文件,编辑文件,搜索文件,而这对于一般的需求也已经完全足够。在此之上,你可以构建独立的功能模块来增加笔记的体验,比如:

  • 如果是记录上课的笔记,那么可以使用 audio recorder 和 LaTex math
  • 如果是记录工作笔记,可以使用 slides 和手写支持
  • 而如果你是研究工作者,backlinks(反向链接)和字数统计就很重要

Obsidian 不期望有一个插件可以解决所有的问题,但 Obsidian 提供了足够的自由度,在能够实现不同需求的时候,也不会搞乱界面。

我的常用快捷键和 remapping

最常用的快捷键整理,下面有一些是我已经 remapping 过的,可以根据自己的习惯重新设置快捷键的。

新建笔记相关:

Shortcut description
Cmd+n New note
Cmd+Shift+n New Zettelkasten note
Cmd+p Filter the command

编辑相关的:

Shortcut description
Cmd+b bold
Cmd+i italic selection

笔记间跳转:

Shortcut description
Cmd+o Open quick switcher, jump to different notes by fuzz search
Option+Enter follow the link under cursor
Cmd+Option+Enter Open link under cursor in new pane
Cmd+[ Back
Cmd+] Forward
Cmd+hjkl Navigate to left/below/above/right pane
Cmd+w Close active pane
Cmd+\ Split vertically
Cmd+- Split horizontally

浏览模式:

Shortcut description
Cmd+e Toggle edit/preview mode

Obsidian 的模板功能非常强大,我自定义了一个快捷键 ⌘ + ⇧ + i 来从模板中快速插入到当前的文档中。

Zettelkasten method

卡片标签式笔记法,Obsidian 也可以兼容,启用插件后侧边栏点击就能够快速建立时间戳开头的笔记迅速进入编写。

延伸阅读

  • [[Trilium Notes]]

个人使用技巧

使用 GitHub 作为同步后端

都知道 Obisidian 其实是一个本地的离线客户端,官方截止 11 月份还没有推出同步的功能,但是优先推出了 Publish 服务,可以将笔记一键发布到 Obisidian 提供的网站上。但是对于我而言,我习惯将笔记在本地整理,然后将相关的内容整理成文章发布在这个博客中,所以剩下的问题就是我的本地笔记同步的问题了。

我个人是将 Obsidian 的本地仓库放到一个 Git 仓库中管理,并且每隔一定时间自动提交到 Git 中,这样即使我不在电脑边,也可以第一时间访问到我的内容。我使用 Hammerspoon 提供的 task api,写了一个简单的脚本 自动提交仓库。

Obsidian 的开发 Roadmap

对比其他产品

在整理了这篇文章半年后,我几乎每条都在使用 Obisidian,甚至用 Karabine 绑定了 ob 的快捷键,在任何情况,任何应用中,我只需要按下 o,不松开,然后快速按下 b,就可以 open Obsidian 了。

这是半年后的 Graph

Obsidian graph

虽然使用过程中从来没有遇到任何问题,但是却一直收到其他产品的推荐,比如 Logseq, RoamEdit 等等,虽然 Obisidian 已经足够满足我的使用了,但也经不住尝试了一下其他的产品,只能说今年因为 Roam Research 的创新激活了笔记应用的市场。

Logseq

Logseq 在自己的官网将自己描述为一个受到 Roam Research, Org mode, Tiddlywiki, Workflowy 启发的开源的 Roam 笔记应用,我打开官网尝试了一下,虽然双向链,块引用,运行在浏览器,以 GitHub 作为笔记的存储,看起来很美好,似乎是 Roam Research 的代替品,但是我在使用的过程中,不清楚是以为网络问题还是因为我没有登录的关系,体验并不是很好,一来创建新页面的时候卡了,而来默认的显示状态和编辑状态的变化略大,使得视觉上的体验不是很好。在界面交互上甚至并不能和其提到 Workflowy 的网页应用流畅度相比。不过因为其开源属性使得每一个人都可以根据自己的喜好进行扩展和修改,其未来可期。

另外一个需要注意的就是目前的 Logseq 只有网页版,并且因为 Logseq 网络访问需要经过其代理才能将数据保存到 GitHub,相较于 Obisidian 直接使用本地 Markdown 文件保存,Logseq 并不占优势。不过有能力可以自行搭建。

RoamEdit

看名字就知道又是一款 Roam Research 的仿作,不过让我惊奇的是其网页的流畅度相较于 Logseq 还是不错。但是问题依然是那样的,官网一行隐私说明都没有,一个备案号,一个交流群,看着就不想深入使用的样子。可能也是某位大神练手之作,值得鼓励。不过我就不去尝试使用了。

Workflowy

Workflowy 和 RoamEdit 比较类似,并且在网页操作上也比较流畅,但两者虽然都支持导出数据,但依然需要联网才能使用,和我个人的情况并不相符,就算了。

往往有些时候鱼与熊掌不可兼得,当选择了网页版,就自然地获得了数据的同步,也自然可以期望未来的多客户端同步,但问题也就是在此,一旦这些网络的服务关闭,或者再一个没有网络的地方,那么一切都没有办法获取了。而 Obisidian 使用纯文本的方式,并且桌面客户端并不会发起网络连接,那么自然就丢失了同步的便利,而与此同时你也就获得了自己笔记的所有权,即使 Obisidian 未来不再更新了,那么也可以使用 Vim 或者 VS Code 的 Foam 获得一份相差不多的体验,并且因为是纯文本,所以那些终端里的工具,比如 fzfVimrg 等等都可以直接拾起来用。这样的体验反而要比等待网络连接,然后才能进行操作要来的快捷方便很多。另外 Obisidian 不能在移动端使用的问题,我通过 Syncthing 加上 Markor 完美的解决了。其他移动端的可以参考官网给出的建议

再就是块应用,Obsidian 不支持,但是 Obisidian 可以对笔记的子标题进行引用,对我来说似乎也已经足够了。毕竟 Zettelkasten 的精髓部分就是每一则笔记的原子性。

reference


2020-05-16 obsidian , note-taking , evernote , linux

分析 Java 内存

What Is Heap

The space used by the Java Runtime to allocate memory to Objects and JRE Classes is called Heap. The heap space can be configured using the following JVM arguments:

-Xmx<size> — Setting maximum Java heap size
-Xms<size> — Setting initial Java heap size

Heap dump is a snapshot of the Java memory. It contains information about the Java objects and classes in the heap at the moment the snapshot is triggered.

take heap dump without hanging the application

First, you have to identify the Java process Id:

ps aux |grep "java"

the normal way to capture the heap dump is using jmap:

jmap -dump:live,format=b,file=/tmp/heapdump.hprof PID

Try the following. It comes with JDK >= 7:

/usr/lib/jvm/jdk-YOUR-VERSION/bin/jcmd PID GC.heap_dump FILE-PATH-TO-SAVE

Example:

/usr/lib/jvm/jdk1.8.0_91/bin/jcmd 25092 GC.heap_dump /opt/hd/3-19.11-jcmd.hprof

This dumping process is much faster than dumping with jmap! Dumpfiles are much smaller, but it’s enough to give your the idea, where the leaks are.

Analyse heap file

Memory Analyzer Tool (MAT)

The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption.

Use the Memory Analyzer to analyze productive heap dumps with hundreds of millions of objects, quickly calculate the retained sizes of objects, see who is preventing the Garbage Collector from collecting objects, run a report to automatically extract leak suspects.

Eclipse Memory Analyzer Tool 是一个基于 Eclipse 的分析工具。

Shallow Heap 和 Retained Heap 区别

Out of memory 问题

I recently installed Eclipse MAT (Eclipse Memory Analyzer Version 1.9.1) on Mac OS Catalina (10.15.3). I needed to review a 4g heap dump. The default JVM heap size for MAT is 1024m.

I think the easiest way to increase the JVM’s heap size is to use a shell window - go to the /Applications/mat.app/Contents/Eclipse/ folder. Then vi MemoryAnalyzer.ini and change -Xmx1024m to your required value, in my case I went with -Xmx10g.

To review the change, restart MAT and go to the help -> About Eclipse Memory Analyzer then click installation details, and look for the entry: eclipse.vmargs=-Xmx10g about 50 lines down.

reference


2020-05-07 java , heap , java-memory

在 Trello 中使用 Pomodoro 工作法

我的 Trello 中有这样一张卡片 —- 学习在 Trello 中使用 Pomodoro —- 已经很久了,这期间也看了一些《番茄工作法》的书,重要的是我开始重度依赖于 Trello 来作为我的任务管理以及时间管理工具。这期间也尝试用 Plus for Trello 来提高使用 Trello 的效率。当然也得到一些体验,但到现在我依然觉得有哪个地方不太对,我很多时候没有严格按照 Pomodoro 约定的 25-5 分钟来执行一些任务,而正是这些任务,可能耗费太长时间所以在我的 Trello 看板中想生根了一样,很久很久没有移动。虽然每天都想要移动一下,但终究没有结果。所以我想写这篇文章来回顾一下我的使用方式,以及想在回顾的过程中发现存在的问题。

25-5 时间划分

就像 Pomodoro 约定的那样 25 分钟 Focus,5 分钟休息,我在刚刚开始的时候也非常的不适应,我可能把之前的习惯带了过来,并没有制定计划,也不知道一项任务可能花费的时间,就这样糊涂的开始,然后糊涂的结束。所以我开始思考为什么 Pomodoro 工作法需要将时间划分到 25 分钟,经过几个月的思考,我想是因为,必须要保证我的每一个任务都是可以追踪的,可以预估时间的,而只有细分的任务才有被衡量的价值,这样才能够尽快的达到既定目标。而不是因为目标太遥远,日久之后就渐渐忘掉,也不是因为太过于复杂,需要拆分多个任务而迟迟无法展开。

我的常用看板里面里经常有这样一条,看某某书,学习某个新技能,而这两个例子正是无法追踪和衡量的,看某本书,实际可能是一个比较长的过程,尤其是技术类书籍,利用 Trello 的卡片显然是覆盖不了的,同理和学习一个新技能一样,学到什么程度算是学习,是需要入门这个技术,还是需要到熟悉的程度,还是说要精通,这个卡片显然是不能够决定的。所以我想是不是以后我再添加这些卡片的时候,可以比如提前看一下书籍的目录,根据目录将书划分成多个模块,然后利用一列来管理这些章节的内容,同理学习某一个新技术能不能将入门,比如在我的机器上将其 Demo 跑通,然后阅读某块源码单独作为独立的开片来进行管理。

基于这个思路,以及官方 How to Pomodoro Your Way to Long-Lasting Productivity 中所说,我逐渐将我的任务卡片细化,原来我可能只会在开片上写一个关键字来提醒我,而现在我则有意识的将其写成动名词结构,比如“写一篇关于如何在 Trello 中实践 Pomodoro 的文章”,然后我会使用 Plus for Trello 来预估一个完成这个任务的时间。Plus for Trello 会将预估的时间写入到卡片的评论中,这样所有的 Web 界面都可以实时同步。

plus! 0/2

这表示我预估需花费 2 个小时的时间来完成这个任务(这里的语法是 Spent/Estimate)。而每次开始任务都使用 Plus for Trello 的计时器功能,结束时会自动提交一个评论,比如

plus! 0.62/0

则表示这次花费了 0.62 小时。


2020-05-04 trello , pomodoro , gtd , management

使用 Prometheus 和 Grafana 监控 Proxmox 服务器

虽然 Proxmox 自身已经有一个比较简单的系统监控,但对于我来说每一次都需要登录到其后台才能看到,而且它自身的监控视图是没有报警策略的。所以我想着这两天反正在学习 Prometheus 这不是正好是一个不错的契机来在具体环境中使用一下,所以才有了这篇文章。PrometheusProxmox 相关的内容可以参考之前的文章。

首先来对比一下前后的效果。

Proxmox 后台默认的监控面板。

proxmox default monitoring panel

Grafana 中显示

proxmox grafana monitoring panel

当然如果你不喜欢这个样式,Grafana 给予了用户非常充分的定制化可能,你可以自己打造自己的监控视图。

安装 Prometheus

准备工作

准备工作:

sudo groupadd --system prometheus
sudo useradd -s /sbin/nologin --system -g prometheus prometheus

创建配置目录:

sudo mkdir /var/lib/prometheus
for i in rules rules.d files_sd; do sudo mkdir -p /etc/prometheus/${i}; done

下载 Prometheus 二进制:

mkdir -p /tmp/prometheus && cd /tmp/prometheus
curl -s https://api.github.com/repos/prometheus/prometheus/releases/latest \
  | grep browser_download_url \
  | grep linux-amd64 \
  | cut -d '"' -f 4 \
  | wget -qi -

配置

解压和配置

tar xvf prometheus*.tar.gz
cd prometheus*/
mv prometheus promtool /usr/local/bin/
mv prometheus.yml  /etc/prometheus/prometheus.yml
mv consoles/ console_libraries/ /etc/prometheus/

清理

cd ~/
rm -rf /tmp/prometheus

创建 systemd 配置

这里使用 systemd 来配置管理 Prometheus

sudo tee /etc/systemd/system/prometheus.service<<EOF

[Unit]
Description=Prometheus
Documentation=https://prometheus.io/docs/introduction/overview/
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.listen-address=0.0.0.0:9090 \
  --web.external-url=

SyslogIdentifier=prometheus
Restart=always

[Install]
WantedBy=multi-user.target
EOF

配置相应的权限

for i in rules rules.d files_sd; do sudo chown -R prometheus:prometheus /etc/prometheus/${i}; done
for i in rules rules.d files_sd; do sudo chmod -R 775 /etc/prometheus/${i}; done
sudo chown -R prometheus:prometheus /var/lib/prometheus/

启动及配置开机启动

sudo systemctl daemon-reload
sudo systemctl start prometheus
sudo systemctl enable prometheus

检查

这个时候可以检查一下 9090 端口(直接访问浏览器,或者 netstat -tupln | grep 9090)

Install proxmox-pve-exporter

看过之前关于 Prometheus 那片文章应该就知道 Prometheus 有两种获取数据的方法,pull 和 push,这里我们在 Proxmox 上安装 exporter,然后让 Prometheus 来 pull 数据。

准备

安装必要的组件

apt install python python-pip
pip install prometheus-pve-exporter

创建授权配置

vi /etc/prometheus/pve.yml

写入:

default:
	user: user@pve
	password: your_password_here
	verify_ssl: false

在这里要注意一个坑,使用 prometheus-pve-exporter 依赖于 Proxmox 的用户授权,而这里的用户需要到 Proxmox 后台,DataCenter -> user 标签下查看,对于我而言,这里需要填写 root@pam.

systemd

sudo tee /etc/systemd/system/prometheus-pve-exporter.service<<EOF
[Unit]
Description=Prometheus exporter for Proxmox VE
Documentation=https://github.com/znerol/prometheus-pve-exporter

[Service]
Restart=always
User=prometheus
ExecStart=/usr/local/bin/pve_exporter /etc/prometheus/pve.yml

[Install]
WantedBy=multi-user.target
EOF

reload systemd

systemctl daemon-reload
systemctl start prometheus-pve-exporter
systemctl enable prometheus-pve-exporter

将 proxmox-pve-exporter 添加到 Prometheus

vi /etc/prometheus/prometheus.yml

增加配置:

- job_name: 'proxmox'
  metrics_path: /pve
  static_configs:
  - targets: ['localhost:9221']

重启服务:

systemctl restart prometheus

重启后可以到 Prometheus 界面 Target 查看节点是否 Up.

我在配置的过程中遇到一个问题,就是配置授权的时候用户名写的不对,报错

returned HTTP status 500 INTERNAL SERVER ERROR

使用 journalctl -xe 可以看到错误日志:

May 03 12:32:25 pve pvedaemon[291021]: authentication failure; rhost=127.0.0.1 user=xx@pve msg=no such user ('xx@pve')

这个时候一定要检查用户名。另外也不建议直接使用 root 用户, 可以使用 useradd monitor 新建一个用户来进行管理。

  • 登录终端 useradd monitor 添加 1
  • Web 后台 Permissions -> User 给用户 monitor@pam 设置密码
  • Web 界面,datacenter -> Roles -> Create,新建一个名叫 Monitoring 的角色,赋予 Datastore.Audit, Sys.Audit, Sys.Modify, VM.Audit, VM.Monitor 的权限
  • 然后 Permissions 中新建 User Permission,依次选 /, monitor@pam, Monitoring 然后新建。

此时配置授权的时候就可以使用 monitor@pam 加上设定的密码了。再检查一下 journalctl -xe 日志,/pve 接口返回 200 ,正常了。

pve_exporter 所能提供的打点 Metrics 大致有如下一些:

pve_cluster_info
pve_cpu_usage_limit
pve_cpu_usage_ratio
pve_disk_read_bytes
pve_disk_size_bytes
pve_disk_usage_bytes
pve_disk_write_bytes
pve_guest_info
pve_memory_size_bytes
pve_memory_usage_bytes
pve_network_receive_bytes
pve_network_transmit_bytes
pve_node_info
pve_storage_info
pve_up
pve_uptime_seconds
pve_version_info

Node exporter

安装 node_exporter 的方法之前的文章中已经有写。安装后配置 Prometheus target

  - job_name: 'node_exporter'
	scrape_interval: 10s
	scrape_timeout: 10s
	static_configs:
	  - targets: ['10.0.0.5:9100']

Install Grafana

直接参考 官网

添加 source

sudo tee /etc/apt/sources.list.d/grafana.list<<EOF
deb https://packages.grafana.com/oss/deb stable main
EOF

获取 key

curl https://packages.grafana.com/gpg.key | sudo apt-key add -

安装:

sudo apt update && sudo apt install -y apt-transport-https grafana
sudo systemctl enable --now grafana-server
systemctl status grafana-server.service

访问 localhost:3000 ,用户名密码都是 admin

配置监控

进入 Grafana 后,需要添加数据源,然后添加自己的 Dashboard 和 Panel,如果不熟悉操作过程,可以先导入别人的模板,学习一下别人是怎么做的。

侧边栏,Create -> Import,输入神秘代码:1860,可以导入 别人创建好的模板。

在熟悉了整个工作流程之后,就可以自己依照自己的需求来修改自己的 Dashboard.

几个比较不错的 Dashboard 模板

总结

至此所有的配置过程就全部结束了,但这可能只是我学习 Prometheus 以及 Proxmox 的第一步,因为目前这台服务器上的服务并不多,虚拟机也只是因为学着玩而安装的 OpenMediaVault 以及 Ubuntu Server,而 Prometheus 中数据库的 Exporter,Nginx 的 Exporter,可以使用类似的方式慢慢加入进来,这个时候参考官网以及相关的 GitHub 也就能实现了。

在这个过程中也慢慢的体会到 less is more 的哲学,很早就同说过这句话,但尤其在这里更加深刻的理解了,这篇文章中提到的每一个服务都专注于自己的最核心的功能,Prometheus Server 专注于采集数据,Exporter 负责打点,而 Grafana 则负责可视化,tsdb 负责记录数据,Proxmox 则是站在巨人的肩膀上专注于虚拟化。这些服务各自都在自己的领域做到极致,那么最好只需要完整的把他们组合到一起,就可以实现 1+1>2 的效果。

reference


2020-05-03 prometheus , grafana , proxmox , server , monitoring , linux

Proxmox Web 界面停止工作解决

今天想把 Proxmox 的静态地址改一下的,但是重启后发现 Web UI 竟然不工作了。SSH 登录后台 netstat -tupln 看 8006 端口也没有起来。这一下子突然不知所措,只能一点点 Google,不过幸好问题不算太大。

官方论坛 里翻到一个帖子,照着他的方法:

service pve-cluster restart

运行命令后服务报错了,这就比较好办了,有报错总比抓瞎好,查看服务日志:

journalctl -xe

然后明显的看到红字:

May 01 20:01:22 pve systemd[1]: pve-cluster.service: Start request repeated too quickly.
May 01 20:01:22 pve systemd[1]: pve-cluster.service: Failed with result 'exit-code'.
-- Subject: Unit failed
-- Defined-By: systemd
-- Support: https://www.debian.org/support
--
-- The unit pve-cluster.service has entered the 'failed' state with result 'exit-code'.
May 01 20:01:22 pve systemd[1]: Failed to start The Proxmox VE cluster filesystem.
-- Subject: A start job for unit pve-cluster.service has failed
-- Defined-By: systemd
-- Support: https://www.debian.org/support

继续看帖子,发现是不是有可能是因为 hostname 没有找到对应的 ip。毕竟在我时隔一个月重启后我发现 hostname 被修改成了 ubuntu2,而我之前明明是 pve 来着。于是顺藤摸瓜看看

vi /etc/hostnames
vi /etc/hosts

果然发现 hostnames 被改了,于是改回 pve,然后查看 /etc/hosts 里面的内容的时候,顶部几个注释引起了我的关注

# Your system has configured 'manage_etc_hosts' as True.
# As a result, if you wish for changes to this file to persist
# then you will need to either
# a.) make changes to the master file in /etc/cloud/templates/hosts.debian.tmpl
# b.) change or remove the value of 'manage_etc_hosts' in
#     /etc/cloud/cloud.cfg or cloud-config from user-data

我突然想起来之前安装过 cloud-init ,然后照着注释的内容在对应的文件里面把 hostname 和 IP 填了进去。然后重启问题就解决了。


2020-05-01 proxmox , virtual , linux , hostname

各个职业的誓言

最近因为《太阳的后裔》中姜暮烟的医生宣誓再一次把医生的誓言放到了观众面前,而之前在《浪漫医生金师傅》、《라이프》中都或多或少的提到医术的誓言。我脑海突然一下子浮现了韩剧中各色职业的誓言,编剧都把他们融合到了台词,或者主人公的性格中。《Live》中的警察誓言,《秘密森林》、《检察官内传》中检察官的誓言,到《辅佐官》中议员的誓言。韩剧用一种理想化的方式来表现这些公职角色的形象,而最近《You quiz on the block》中的法医,警察,犯罪心理侧写师则是实实在在的于现实中履行自己的义务。

如果这些公职人员都能做到自己力所能及之事,而普通人始终恪守自己的底线,这个社会又会变得怎么样?

医生

我们来看看真实的希波克拉底誓言,韩国医学院毕业生宣誓的内容。有些时候看着这些无私的誓言会觉得这个世界还是存在一些光明的,尤其是在如今肺炎蔓延到全球的时候。

韩国

이제 의업에 종사할 허락을 받음에, 나의 생애를 인류봉사에 바칠 것을 엄숙히 서약하노라. 나의 은사에 대하여 존경과 감사를 드리겠노라. 나의 양심과 위엄으로서 의술을 베풀겠노라. 나의 환자가 알려준 모든 비밀을 엄중히 지키겠노라. 나는 의업의 고귀한 전통과 명예를 유지하겠노라 나는 동업자를 형제처럼 여기겠노라. 나는 인류, 종교, 국적, 정당, 정파, 또는 사회적 지위 여하를 초월하여 오직 환자에 대한 나의 의무를 지키겠노라. 나는 인간의 생명을 그 수태된 때로부터 지상의 것으로 존중히 여기겠노라. 비록 위협을 당할지라도 나의 지식을 인도에 어긋나게 쓰지 않겠노라

这里是比较简陋的翻译(非逐字翻译):

现在我已获准从事医疗事业,我庄严宣誓,将我的毕生献给全人类,把病人的健康和生命放在首位,无论种族,宗教,国籍,政党派别和社会地位,只对病人履行我的义务。即使受到威胁,也绝不利用我的知识做违背人道的事情。

希波克拉底誓言

Hippocrates:The Oath of Medicine

You do solemnly swear, each by whatever he or she holds most sacred

That you will be loyal to the Profession of Medicine and just and generous to its members

That you will lead your lives and practice your art in uprightness and honor

That into whatsoever house you shall enter, it shall be for the good of the sick to the utmost of your power, your holding yourselves far aloof from wrong, from corruption, from the tempting of others to vice

That you will exercise your art solely for the cure of your patients, and will give no drug, perform no operation, for a criminal purpose, even if solicited, far less suggest it

That whatsoever you shall see or hear of the lives of men or women which is not fitting to be spoken, you will keep inviolably secret

These things do you swear. Let each bow the head in sign of acquiescence

And now, if you will be true to this, your oath, may prosperity and good repute be ever yours; the opposite, if you shall prove yourselves forsworn.

1948 日内瓦宣言

At the time of being admitted as a member of the medical profession

I solemnly pledge myself to consecrate my life to the service of humanity:

I will give to my teachers the respect and gratitude which is their due;

I will practice my profession with conscience and dignity;

The health and life of my patient will be my first consideration;

I will respect the secrets which are confided in me;

I will maintain by all means in my power, the honor and the noble traditions of the medical profession;

My colleagues will be my brothers:

I will not permit considerations of religion, nationality, race, party politics or social standing to intervene between my duty and my patient;

I will maintain the utmost respect for human life, from the time of its conception,

even under threat, I will not use my medical knowledge contrary to the laws of humanity;

I make these promises solemnly, freely and upon my honor…

值此进入医生职业之际,我庄严宣誓为服务于人类而献身。

我对施我以教的师友衷心感佩。

我在行医中一定要保持端庄和良心。

我一定把病人的健康和生命放在一切的首位,病人吐露的一切秘密,我一定严加信守,决不泄露。

我一定要保持医生职业的荣誉和高尚的传统。

我待同事亲如弟兄。

我决不让我对病人的义务受到种族、宗教、国籍、政党和政治或社会地位等方面的考虑的干扰。

对于人的生命,自其孕育之始,就保持最高度的尊重。

即使在威胁之下,我也决不用我的知识作逆于人道法规的事情。

我出自内心以荣誉保证履行以上诺言。

美国

I solemnly pledge to concentrate my life to the service of humanity. I will give my teachers the respect and gratitude that is their due. I will practice my profession with conscience and dignity. The health of my patients will be my number one consideration. I will respect the secrets that are confided in me, even after my patient has died. I will maintain by all the means in my power, the honor and the noble traditions of the medical profession. My colleagues will be my sisters and brothers. I will not permit considerations of age, disease or disability, creed, ethnic origin, gender, race, political affiliation, nationality, sexual orientation, social standing, or any other factor to intervene between my duty and my patient. I will maintain the utmost respect for human life. I will not use my medical knowledge to violate human rights and civil liberties, even under threat. I make these promises solemnly, freely, and upon my honor.

中国

健康所系,性命相托。 当我步入神圣医学学府的时刻,谨庄严宣誓: 我志愿献身医学,热爱祖国,忠于人民,恪守医德,尊师守纪,刻苦钻研,孜孜不倦,精益求精,全面发展。 我决心竭尽全力除人类之病痛,助健康之完美,维护医术的圣洁和荣誉,救死扶伤,不辞艰辛,执着追求,为祖国医药卫生事业的发展和人类身心健康奋斗终生。

警察

韩国

본인은 공직자로서 긍지와 보람을 가지고 국가와 국민을 위하여 신명을 바칠 것을 다짐하면서 다음과 같이 선서합니다. 1.본인은 법령을 준수하고 상사의 직무상의 명령에 복종한다. 1.본인은 국민의 편에 서서 정직과 성실로 직무에 전념한다. 1.본인은 창의적인 노력과 능동적인 자세로 소임을 완수한다. 1.본인은 재직 중은 물론 퇴직 후에라도 직무상 알게 된 기밀을 절대로 누설하지 아니한다. 1.본인은정의의 실천자로서 부정의 발본에 앞장선다.

香港

I will well and faithfully serve Her Majesty and Her Heirs and Successors according to law as a police officer, I will obey, uphold and maintain the laws of the Colony of Hong Kong, I will execute the powers and duties of my office honestly, faithfully and diligently without fear of or favour to any person and with malice or ill will towards none, and I will obey without question all lawful orders of those set in authority over me.

余兹身为警员,愿竭忠诚,依法效力英女皇及其皇储与继统人。余愿遵守,维护,并维持香港之法律。余复愿以不屈不挠,毋枉毋徇之精神,一秉至公,励行本人之职,并愿绝对服从本人上级长官之一切合法命令,此誓。

台湾

余誓以至誠,恪遵國家法令,盡忠職 守,報效國家;依法執行任務,行使職權;勤謹謙和,為民服務。如違誓 言,願受最嚴厲之處罰,謹誓。

检察官

나는 이 순간 국가와 국민의 부름을 받고 영광스러운 대한민국 검사의 직에 나섭니다. 공익의 대표자로서 정의와 인권을 바로 세우고 범죄로부터 내 이웃과 공동체를 지키라는 막중한 사명을 부여받은 것입니다. 나는 불의의 어둠을 걷어내는 용기 있는 검사, 힘없고 소외된 사람들을 돌보는 따뜻한 검사, 오로지 진실만을 따라가는 공평한 검사, 스스로에게 더 엄격한 바른 검사로서, 처음부터 끝까지 혼신의 힘을 다해 국민을 섬기고 국가에 봉사할 것을 나의 명예를 걸고 굳게 다짐합니다.

reference


2020-05-01 oath , vow , thinking

Ansible 介绍及使用

Ansible 是使用 Python 开发的自动化运维工具,如果这么说比较抽象的话,那么可以说 Ansible 可以让服务器管理人员使用文本来管理服务器,编写一段配置文件,在不同的机器上执行。

特性

  • 低学习成本
  • 无需在服务器中安装客户端,基于 SSH 工作,可并行执行
  • 无需服务端,直接终端命令即可
  • 管理的对象可以包括物理机,虚拟机,容器等等
  • 使用 yaml 格式文件编排 playbook

组成

  • control node: 控制节点,可以在任何安装了 Python 环境的机器中使用 ansible,两个重要的可执行文件在 /usr/bin/ansible/usr/bin/ansible-playbook
  • managed node: 被控制的节点
  • inventory: 需要管理的节点,通常配置成 hostfile 文件 1
  • modules: ansible 进行自动化任务时调用的模块,社区提供了非常多 modules
  • Task: Ansible 的执行单元
  • playbook: 编排多个任务
  • roles: roles 是将 playbook 划分多个部分的机制
  • plugins: ansible 插件

工作流程:

  • 读取配置
  • 获取机器列表及分组配置
  • 确定执行模块和配置,modules 目录动态读取
  • Runner 执行
  • 输出

安装

Ansible 的安装方法非常多,PPA,源码安装都可以。2

如果不想 PPA,也可以

sudo apt-get install -y ansible

源码安装

sudo apt-get install -y libffi-dev libssl-dev python-dev
sudo pip install paramiko PyYAML Jinja2 httplib2 six pycrypto
git clone https://github.com/ansible/ansible.git --recursive
cd ansible
git pull --rebase
git submodule update --init --recursive

配置 Bash:

source ./hacking/env-setup

配置

ansible.cfg

ansible.cfg 文件是 Ansible 的主要配置文件,ansible 寻找的路径优先级是:

  • File specified by the ANSIBLE_CONFIG environment variable
  • ./ansible.cfg (ansible.cfg in the current directory)
  • ~/.ansible.cfg (.ansible.cfg in your home directory)
  • /etc/ansible/ansible.cfg

配置内容:

[defaults]
hostfile = hosts
remote_user = admin
remote_port = 222
host_key_checking = False

hostfile 文件指定了当前文件夹下的 hosts 文件。hosts 文件夹中配置:

[server]
10.0.0.1
10.0.0.2

配置 SSH 免密登录的文章可以参考之前的文章.

inventory

inventory 可以对远程服务器 HOST 进行管理。可以配置 Ansible 默认的 /etc/ansible/hosts 创建基本的 inventory.

这里的 inventory 可以看成需要管理的节点的配置,可以直接配置到全局,然后使用 all 来引用,也可以用分组的形式来引用。

比如,未分组形式:

gtk.pw
einverne.info
12.12.12.12
192.168.2.1

或者采用分组形式,用方括号表示下面的 HOST 都属于 webserver 这个组:

[webserver]
127.0.0.1
foo.example.com

如果有多个 HOST 可以用如下语法添加多个:

[webservers]
www[001:006].example.com

[dbservers]
db-[99:101]-node.example.com

或者配置别名:

dbserver1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=22 color=red
dbserver2 ansible_ssh_host=127.0.0.2 ansible_ssh_port=220


[dbserver] #group
dbserver1
dbserver2

[forum:children] #groups of groups
webserver
dbserver

inventory 中可以配置使用别名,但是推荐在 ssh config 中进行配置管理,编辑 vi ~/.ssh/config:

Host ds
	HostName einverne.info
	Port 22
	User username

Host aws1
	HostName aws.einverne.info
	Post 22
	User demo-username

Host oracle1
	HostName 140.1.1.1
	Port 22
	User some-username

然后就可以在 Ansible 的 inventory 中配置使用 ds, aws1 或者 oracle1.

更多 inventory 的配置可以参考官方文档

inventory 同样配置用来管理 AWS EC2,或者 OpenStack。3

使用

基本使用方法:

ansible <pattern> -m <module_name> -a <arguments>

在简单配置后可以运行

# 一个节点
ansible host1 -a "/bin/echo hello"
# 多个节点
ansible host1,host2 -a "/bin/echo hello"
ansible host1:host2 -a "/bin/echo hello"
# 全部节点
ansible all -m ping
# 一组节点
ansible webservers -m service -a "name=httpd state=restarted"
# 多组节点
ansible webservers:dbservers -m ping
# 排除节点,在 groupA 但不在 groupB
ansible groupA:!groupB -m ping
# 多组节点的交集,既在 groupA 也在 groupB 中的节点
ansible groupA:&groupB -m ping

这里选择节点的方式可以有很多种,甚至可以选择组节点中的第几个,或者用正则匹配一些等等。4

ad-hoc command

ad-hoc 命令可以执行单一的任务,ad-hoc 命令很简单,但不能复用,在了解 playbook 之前先体验一下 ad-hoc 感受一下 Ansible 的强大。

ansible [pattern] -m [module] -a "[module options]"

举例:

# 小心使用下面命令,比如重启服务
ansible atlanta -a "/sbin/reboot"
# 管理文件,复制文件
ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"
# 管理包
ansible webservers -m yum -a "name=acme state=present"
# 启动服务
ansible webservers -m service -a "name=httpd state=started"

module

-m 选项后面的就是 module,常见的 module,之前例子中有非常多的 ping,就是用来检测连通性的。

setup

用来查看远程主机信息:

ansible all -m setup

command

执行命令:

ansible all -m command -a "ls -al ."
# 切换到 sub-dir 目录,创建文件
ansible all -m command -a "chdir=sub-dir creates=test.file ls"
# 删除文件
ansible all -m command -a "chdir=sub-dir removes=test.file ls"

file

设置文件属性。

# 创建 soft link
ansible all -m file -a "src=/etc/resolv.conf dest=/tmp/resolv.conf state=link"
# 删除 soft link
ansible all -m file -a "path=/tmp/resolv.conf state=absent"

copy

复制文件到主机

# 复制本地文件到远程主机,并授予权限
ansible all -m copy -a "src=/etc/ansible/ansible.cfg dest=/tmp/ansible.cfg owner=root group=root mode=0644"

shell

在远程执行 shell 脚本

ansible all -m shell -a "~/setup.sh"

更多 module 可以使用 ansible-doc -l 查看。

playbook

上面提到 ad-hoc 可以执行一次性的命令,但如果要把多个 task 组织起来,那就不得不提到 playbook, playbook 可以编排有序的任务,可以在多组主机间,有序执行任务,可以选择同步或者异步发起任务。

一个简单的例子:

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service: name=httpd state=started
  handlers:
    - name: restart apache
      service: name=httpd state=restarted
    - name: restart memcached
      service: name=memcached state=restarted

说明:

  • hosts: 指定哪些服务器执行命令
  • tasks: 一系列任务
  • handlers: 由通知者进行通知,只有 nofity 后 handler 才会执行,等到 tasks 执行完后才会执行,最多执行一次

执行 playbook

ansible-playbook playbook.yml -f 10

role

再来看一个例子:

- hosts:webservers
  roles:
	-tmux

这里 role 定义了 tmux(tmux 编译安装),则表示用 tmux 执行了一系列的命令。role 由其他一些组件组成:

roles/
   tmux/
	 tasks/
	 handlers/
	 files/
	 templates/
	 vars/
	 defaults/
	 meta/

在 tasks 目录下新建 mail.yml:

- name: install tmux package
  package:
	name:
	  - libevent
	  - ncurses
	  - tmux
	state: latest

如果想了解更多拆分 playbook 的方法,可以到官网查看更多 include, role 相关的内容。

Check Mode (dry-run)

当使用 check mode 运行 ansible-playbook 时,Ansible 不会在远程服务器上执行任何命令。

ansible-playbook foo.yml --check

ansible-galaxy

创建 role

ansible-galaxy 命令和 Ansible 命令绑定到了一起,可以通过 ansible-galaxy 来初始化 role.

ansible-galaxy init pyenv

得到:

➜ tree pyenv
pyenv
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
	└── main.yml

在使用时,每一个目录都需要包含一个 mail.yml 文件:

  • tasks: 包含 role 需要执行的任务清单
  • handlers: 包含 handlers, 可能被 role 用到
  • defaults: 默认变量,Using Variables
  • vars: 其他被 role 用到的变量 Variable
  • files: 包含可能被 role 用到的文件
  • templates: 包含可能被 role 用到的 templates
  • meta: 定义 role 的 meta data

YAML 文件可以被引入,比如不同的系统版本:

# roles/example/tasks/main.yml
- name: added in 2.4, previously you used 'include'
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'

# roles/example/tasks/redhat.yml
- yum:
	name: "httpd"
	state: present

# roles/example/tasks/debian.yml
- apt:
	name: "apache2"
	state: present

延伸

其他的运维管理工具 puppet、cfengine、chef、func、fabric.

Redhat 给 Ansible 做了一套 GUI,叫做 Ansible Tower,感兴趣可以了解一下。

reference


2020-05-01 ansible , deploy , linux , management

电子书

最近文章

  • 读书是否是唯一重要的事? 不久之前和朋友约去了趟植物园,聊起读书是否是第一要务的时候产生了一些分歧,关于是否要去学习如何学习这一件事情产生了一些分歧。我站在的立场是读书是必须的,而我朋友则认为在有限的时间里面实践要优先于读书。而关于要不要学习如何学习这一件事情,他仍然坚持自己的实践而非去了解如何学习。
  • Android 上的 RIME 输入法 trime 同文输入法使用 早之前就已经在 Linux 和 macOS 上配置了 RIME 并且一直使用到现在,但是在主力的 Android 上从最早的触宝输入法,后来切换成 Gboard,日常使用倒是没什么大问题,就是有一些词总是需要翻页才能找到,这让我非常不爽,就想手机上能不能用 RIME,于是就有了这篇水文。
  • Obsidian 使用篇一:使用 markdown-clipper 全文保存网页 之前使用整理 Evernote 代替品 的时候就提出了我自己的一个需求,就是有一个完善的 Web Clip 系统,Evernote 和 WizNote 都做的比较不错。但 Obsidian 并没有提供类似的工具,不过幸好 Obsidian 使用 Markdown 来管理文档,这样的开放程度使得我可以寻找一个将网页变为 Markdown 的浏览器扩展就能做到。
  • 使用了半年 macOS 之后 我又回到了 Linux 的怀抱 我在使用了半年 macOS 之后,又回到了 Linux 的怀抱,虽然 macOS 有其自身的优势,我也不否认 macOS 系统上软件生态的友好,但我发现即使我将日常开发主力机器装回到 Linux,也没有丧失操作系统的便捷性和易用性。这或许和我下意识的只使用跨平台的软件有关,并且最长使用的软件几乎都是一套快捷键。
  • 重置 macOS S.M.C 和 NVRAM 今天用得好好的电脑突然三次黑屏,两次发生在早上刚刚使用的时候,一次发生在晚上回家之后。所以一怒之下就直接上官网联系了 Apple Support,但是也不知道是不是我直接登录的 .com 网站,在我提交了 Support 之后一分钟一个外国小哥打了电话过来,我一下子没反应过来,只能用着不那么熟练的英语开始了 macOS 修复之路。