2022年

1 背景

受“新冠肺炎疫情”影响,出现不能回办公室上班的问题,所以制定一套安全的远程办公方案。

由于办公室有外网IP,原来的方案就是利用路由器的端口映射功能,把各个台式机(操作系统是Windows)的“远程桌面”端口直接映射到外网。这方案缺点如下:

  • “远程桌面”如果存在漏洞,比如绕过登录,电脑上的资料就可能被随意访问。
  • 需要远程访问的台式机,要24小时开机,否则连不上。
  • 需要远程访问的台式机,起码占用路由器的一个端口。

2 解决方案

结合SSH服务、wake on lan、远程桌面,实现更安全和灵活的远程办公。

  • 部署Linux服务器,只映射其SSH服务端口到外网,作为安全入口。
  • SSH客户端几乎覆盖所有平台(包括移动平台),且其功能强大。
  • 使用SSH的端口转发(Port Forward)功能,连上办公室内网的指定IP的“远程桌面”端口。
  • 各个台式机开启wake on lan功能,实现按需开机,工作完关机。
  • 各种操作系统有对应的远程客户端。Windows,使用微软的“远程桌面”客户端,全平台支持;Linux,使用SSH客户端;Mac操作系统,使用VNC客户端。

但是此方案仍有缺点:

  • 需要用户理解SSH及其功能。
  • 使用Linux远程开机命令(wakeonlan),即使把命令简化为Shell脚本,也不是普通人会用。
  • M系列CPU的Mac电脑,不能使用wake on lan,目前只能长期开机。

3 办公室部署

3.1 路由器

路由器的网络需要可外网访问,并且支持端口映射功能。基本路由器都支持端口映射,具体配置参考路由器说明书。

配置路由器外网端口,映射到Linux服务器的SSH服务端口。

3.2 Linux服务器

  1. 安装wakeonlan命令。

Debian或Ubuntu,执行以下命令安装

sudo apt install wakeonlan
  1. 部署SSH服务,作为安全入口。需要SSH服务的安全配置,例如:
  • 仅使用SSHv2协议

    Protocol 2
  • 禁止root用户登录。

    PermitRootLogin no
  • 禁止用户空密码登录。

    PermitEmptyPasswords no
  • 指定白名单用户。

    AllowUsers user1 user2 user3
  • 指定禁止登录的用户(一般指定白名单即可)。

    DenyUsers root user4 user5
  • 限制身份验证最大重试次数。

    MaxAuthTries 3
  • 登录用户的密码,使用强密码,甚至配置使用“密钥”验证登录。
  • 显示最后一次登录的日期和时间。

    PrintLastLog yes
  • 防止特权升级(一般默认配置)

    UsePrivilegeSeparation sandbox
  • 禁用 GSSAPI 认证

    GSSAPIAuthentication no

更详细的设置,可以搜索“Secure SSH”或者“SSH安全加固”等内容。

另外,最好配置一下服务器保持TCP连接的选项,避免客户端自动断开:

  • 开启保持TCP连接

    TCPKeepAlive yes
  • 向客户端发送是否存活的消息的时间间隔,单位是秒,默认是0,不发送

    ClientAliveInterval 30
  • 请求后客户端无响应则自动断开的最大次数

    ClientAliveCountMax 3

3.3 台式机

  1. 主板开启wake on lan功能。具体BIOS设置,需要查询主板的说明书。一般注意以下几点:

    • 板载有线网卡设置启用。
    • wake on lan设置启用。
    • 启动项,允许PCIE设备启动。
    • 启动项,出现pxe rom可选。
  2. 操作系统开启wake on lan功能。即操作系统执行关机时,让主板不要完全断电,并允许网卡运行于可接收Magic Package的状态,用于网络启动电脑。

  3. 开启远程访问服务。各个操作系统配置如下:

    • windows,开启“远程桌面”服务。
    • Linux,开启SSH服务。一般默认开启的。
    • Mac OS,开启“远程访问”服务,可以SSH客户端访问,即字符界面。
    • Mac OS,开启“远程桌面”服务,可以VNC客户端访问,即图形界面。

4 客户端部署

主要就是SSH客户端 + 远程客户端。

4.1 SSH客户端

4.1.1 Linux

一般Linux操作系统默认安装SSH客户端,如果没有,安装“OpenSSH”或者“Dropbear SSH”的客户端即可。

4.1.2 Windows

Windows 10或11可以通过“WinGet”命令安装“OpenSSH”客户端。例如:

winget install opensssh

Windows 7可以使用“PuTTY”。Windows都可以安装这个。

4.1.3 Android

可以使用“Termux”,再安装“OpenSSH”。

pkg install openssh

或者使用其它SSH客户端App。

4.1.4 iOS

安装Termius。需要注册账号,免费版可以使用SSH客户端和端口转发功能。

4.2 远程桌面客户端

  • Windows,自带“远程桌面”客户端。
  • Linux,推荐安装“Remmina”。
  • Android,安装微软官方“远程桌面”App。
  • iOS,安装微软官方“远程桌面”App。

4.3 VNC客户端

  • Windows,使用开源的“TightVNC”。
  • 其它,待补充。

5 客户端使用

以Windows远程桌面为例,其默认端口为3389,并假设该台式机的IP为192.168.0.123。其它服务类似操作。

  1. 远程开机。

    启动SSH客户端并登录,使用wakeonlan命令 + MAC地址,启动对应的台式机。注意,需要记录该台式机有线网卡的MAC地址。
  2. 开启端口转发。

    启动SSH客户端,设置本地端口(例如 43389)转发到办公室内网指定电脑端口(例如 192.168.0.123:3389)。
  3. 连接远程桌面。

    远程桌面客户端连接到本机端口(例如 127.0.0.1:43389),即可访问。如果是管理员帐号登录,需勾选“管理员模式”。

5.2 远程开机

普通用户执行wakeonlan命令,参数是对应台式机网卡的MAC地址。然后使用ping命令,检查该台式机是否开机成功。

要注意,Windows操作系统,不要使用shutdown /s命令关机,会导致wakeonlan命令无法开机。

5.3 开启端口转发

假设,办公室的外网域名为remote.office.com,SSH映射外网端口为22222,SSH登录用户为r-user,需要通过访问192.168.0.123:3389的“远程桌面”服务,并且本机开启43389端口去访问。

5.3.1 SSH命令

使用SSH客户端(例如OpenSSH客户端)的,直接执行以下命令,然后输入密码,让其一直运行即可。

ssh -f -N -L 43389:192.168.0.123:3389 r-user@remote.office.com -p 22222 -o ServerAliveInterval=30

关键参数说明如下:

  • -f后台运行。
  • -N不执行命令。
  • -L 43389:192.168.0.123:3389是把本机43389端口转发到办公室内网的192.168.0.123:3389端口。
  • -o ServerAliveInterval=30是每30秒向服务器发生一条表示客户端存活的消息,用于保持连接。

关于客户端保持连接,可以修改/etc/ssh/ssh_config文件,在Host *的配置下,加入以下配置。然后运行ssh命令,不用加上-o ServerAliveInterval=30这个参数。

ServerAliveInterval 30
ServerAliveCountMax 3

5.3.2 PuTTY设置

  1. 点Category -> Session,在Host name填remote.office.com,Port填22222,Connection Type选SSH。
  2. 点Category -> Connection -> Data,在Auto-login username填r-user
  3. 点Category -> Connection -> SSH -> Tunnels,Add new forward port下,Source port填43389,Destination填192.168.0.123:3389,勾选下面的“Local”和“Auto”,再点“Add”。
  4. 点Category -> Connection,在Seconds between keepalives (0 to turn off)填10,并勾选Enable TCP keepalives (SO_KEEPALIVE option)选项。这一步是设置客户端保持连接。
  5. 点Category -> Session,在Saved Sessions填remote_office,再点“Save”保存配置。
  6. 连接时,点Category -> Session,选中remote_office,点“Open”。输入密码后让其保持运行即可。

5.3.3 iOS设置Termius

  1. 安装Termius,并注册账户。
  2. 设置保持后台运行。

    • 在Settings -> SESSIONS -> 开启”Active Connect Saver“和”Save Location Data“。
    • 据说是使用了“获取地理位置”权限,实现App保持后台运行。
  3. 新建Hosts。

    • 填写连接到办公室的域名remote.office.com和SSH端口22222,然后命名为remote_office
  4. 新建Port Forwarding。

    • 在Port Forwarding,点“+”新建。
    • -> 选Local,点Continue。
    • ->“Set the local port and binding address”的Port number填写映射到本机的端口,例如3389,点CONTINUE。
    • -> 点Select a host,并选sdoffice。
    • -> “Set the destination host”填写目标电脑的内网IP和远程桌面端口,例如address为192.168.0.123,port为3389,点CONTINUE。
    • -> 最后填写标签,例如101-rdp,点DONE
  5. 连接。

    • 在Port Forwarding,长按101-rdp,点Connect。

5.4 远程桌面客户端

添加电脑,电脑名称为127.0.0.1:43389。如果是使用管理员账号,记得开启“管理员模式”。

6 其它方案

6.1 前端安全替代

  1. 使用虚拟内网,即VPN。连上VPN就等于进入办公室内网。

    • Android和iOS原生支持L2TP、IPSec、IKEv2等协议,不用安装客户端。
    • 路由器同样只需映射VPN服务的端口。
  2. 使用堡垒机做入口。

    • JumpServer。未了解。
    • Next Terminal。了解过,当前版本安全方面考虑不足,手机访问“远程桌面”不支持触屏等。
  3. 其它商业解决方案

    • TeamViewer
    • 向日葵远程控制软件

6.2 网络启动功能替代

可以使用WiFi开关 + 电脑通电启动,实现替代,但需要购买WiFi开关硬件。

本来为了ESP32-C3刷上LVGL,才玩Arduino。但是Arduino确实没MicroPython好玩,而且,我真的需要LVGL吗?这里先记录一下相关操作。

1 刷Arduino固件

使用Arduino IDE操作,最简单。

参考教程:

1.1 设置开发板为ESP32-C3

安装好Arduino IDE(本文所用版本是1.8.19),运行。进入“File” -> “Preferences” -> “Settings”,在“Additional Boards Manager URLs”输入以下网址,并点“OK”。

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json

要注意,如果本机不能访问以上链接,可在“File” -> “Preferences” -> “Network”设置代理。

然后进入“Tools” -> “Board: xxx” -> “Boards Manager…”。在“Boards Manager”弹出框,搜“esp32”,并选择最高版本,点“Install”。

安装完成后,再次进入“Tools” -> “Board: xxx”,选中“ESP32C3 Dev Module”即可。可以看到“Tools”菜单显示“Board: ESP32C3 Dev Module”,并在下面显示硬件相关信息。

1.2 刷入固件

先在“Tools” -> “Flash Mode”要选“DIO”(这个很重要), 再点“Tools” -> “Burn Bootloader”,等待刷入成功即可。

“Tools”显示的硬件信息参考:

Board: "ESP32C3 Dev Module"
Upload Speed: "921600"
USB CDC on Boot: "Disabled"
CPU Frequency: "160MHz (WiFi)"
Flash Frequency: "80MHz"
Flash Mode: "DIO"
Flash Size: "4MB (32Mb)"
Partition Scheme: "Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)"
Core Debug Level: "None"
Erase All Flash before Sketch Upload: "Disabled"
Port: "/dev/ttyACM0"

2 使用I2C OLED屏

这里使用的I2C OLED屏,SSD1315(可用SSD1306的驱动),0.96寸,4针。详细参考如下:

2.1 接线

OLED屏 ESP32-C3
GND<-->25, GND
VCC<-->26, 3.3V
SCL<-->27, GPIO05, I2C_SCL
SDA<-->28, GPIO04, I2C_SDA

2.2 示例代码

以下示例是在屏幕上显示一行文字”Hello, Fox!“。其最麻烦的地方,是找个合适的字体。上传程序前,记得“Tools” -> “Flash Mode”要选“DIO”。

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 5, /* data=*/ 4);

void setup(void) {
  u8g2.begin();
}

void loop(void) {
  u8g2.clearBuffer();         // clear the internal memory
  u8g2.setFont(u8g2_font_chargen_92_mf); // choose a suitable font
  u8g2.drawStr(0,14,"Hello, Fox!");  // write something to the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display
  delay(1000);  
}

终于用上了Windows 11,版本是22h2。记录一下针对开发人员的相关优化。

1 安装时跳过TPM限制

安装过程,在提示“这台电脑无法安装Windows11”的界面,按Shift + F10,弹出CMD窗口输入“regedit”,打开注册表编辑器。

在注册表编辑器进入[HKEY_LOCAL_MACHINE\SYSTEM\Setup],新建“项”,名为“LabConfig”(注意大小写一致)。

在“LabConfig”下,新建两个“DWORD (32位)值”,如下(记得注意大小写一致):

  • 数值名称:BypassTPMCheck,数值数据:00000001,基数:十六进制(H)
  • 数值名称:BypassSecureBootCheck,数值数据:00000001,基数:十六进制(H)

完成后关闭“注册表编辑器”、“CMD窗口”,按返回上一步的按钮,再按下一页,就通过系统限制检测。

2 安装时避免强制登录账户

按Shift + F10,弹出CMD窗口输入“regedit”,打开注册表编辑器。

在注册表编辑器进入[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE],新建一个“DWORD (32位)值”,如下(记得注意大小写一致),如下。

  • 数值名称:BypassNRO,数值数据:00000001,基数:十六进制(H)

退出注册表编辑器后,输入命令“logoff”,即可即可跳过强制联网登录账号。

如果不想编辑注册表,在CMD窗口输入命令“oobe\BypassNRO.cmd”,系统重启后即可。

3 创建本地用户

安装成功后,需要创建本地用户。最好不要登录微软账户。

最简单的是创建和使用管理员账号(Administrator)登录,避免访问其它硬盘的已存在文件时,提示需要管理员账号。

如果使用自定义账号(即拥有管理员权限的非Administrator账号),需要修改电脑上已存在文件的权限,改为新增账号拥有“完全控制”的权限。如果存在大量零碎文件,这个过程超级漫长。

4 解锁任务栏

在Windows 11中,用户无法通过“任务栏”使用“快速启动工具栏”,通过安装“ExplorerPatcher”解锁相关功能。

ExplorerPatcher - GitHub
https://github.com/valinet/ExplorerPatcher

我整理了一堆快捷方式,大多是常用的,利用“快速启动工具栏”,实现快速点击,类似开始菜单的效果。

5 优化内存占用

1)关闭不必要的自启动

依次进入:设置 -> 应用 -> 启动。把不需要自启软件关闭。

2)关闭动画效果

依次进入:设置 -> 辅助功能 -> 视觉效果。“动画效果”设置关闭。这个优化,大概省了1GB内存。

6 恢复旧的右键菜单

新的右键菜单过于简单,隐藏了太多的功能。使用管理员运行CMD,输入以下命令。执行成功后重启,即可看到效果。

reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve

7 安装 WinMerge

开源的文件对比和合并工具,用于替代Beyond Compare。

启用“高级菜单”,选择文件对比时更方便。运行WinMerge,依次进入“编辑”->“选项”->左侧菜单点“系统集成”,在“资源管理器”下,勾选“添加到上下文菜单”、“启用高级菜单”。

8 显示多个时区的时间

依次进入:设置 -> 时间和语言 -> 日期和时间 -> 相关链接 -> 附加时钟。设置完毕后按“确定”。建议设置以下时间:

  • (UTC) 协调世界时。

设置完成后,点击任务栏的时间,即可看到新增的时区时间。

9 Office软件

目前使用开源的LibreOffice,代替微软Office,用着还行。

10 安装WSL2

Window上运行Linux软件,包括GUI软件。这样连SSH客户端都不用安装了。

主要过程是,开启Windows功能、安装内核、安装Linux发行版。相关文档如下:

11 解决不能自动关机

关机时,会提示有进程正在运行,需要点按钮才能关机。这问题导致使用shutdown命令和远程桌面都不能关机。

解决方案:打开注册表,进入“\HKEY_USERS.DEFAULT\Control Panel\Desktop”,新建“字符串值(S)”,名称为“AutoEndTasks”,值为“1”。

经过一段时间的爆米花机烘焙咖啡豆,对烘豆有了进一步的认识和感受,于是决心升级一下装备。

1 烘豆的原因

首先还是要来个灵魂敲问,为什么要烘豆?

通过亲自烘焙咖啡豆,对咖啡的味道有很多的把控,而不局限于手冲的过程。另外,烘焙出来的豆子更多的了解,可以更好地针对目标味道而配合不同的手冲方法。

如果想再进一步对咖啡出品的把控,就需要参与到咖啡树的种植和咖啡生豆的处理。显然,对大多咖啡爱好者来说,都是遥不可及的事情。烘焙咖啡豆有适合个人玩家的器具、方式,处理好的咖啡生豆也容易买到,所以适合去玩。

不过,重点是喜欢和享受烘焙咖啡豆这个事情。

2 烘豆操作

烘焙咖啡豆,就是通过对咖啡生豆进行加热,制作出具有独特香气和味道的咖啡熟豆的过程。

碍于个人的认识和篇幅所限,只能点出重点,详细内容需要参考相关资料,或者亲自体会。

2.1 整个操作

主要操作是挑豆、暖机、进豆、加热和翻炒、下豆冷却、养豆。

挑豆是把咖啡生豆中的杂质和瑕疵豆挑选出来,避免咖啡冲煮出杂味。一般买到的咖啡生豆,都已经去除了杂质,包括石头、沙子等。瑕疵豆,包括带壳豆、发霉豆、发酵豆、未成熟豆、虫蛀豆、贝壳豆、残缺豆、黑豆、死豆等。

暖机是进豆前,把机器加热到一定的温度。一般认为,热风烘豆机加热速度快,不需要暖机。

进豆就是把生豆倒入机器。此时烘焙过程开始,并进行计时。

加热和翻炒,目的是均匀地给咖啡豆加热,避免某些豆子或豆子的某部分过度加热而碳化(就是变得焦黑)。这过程就是烘焙过程。

下豆冷却,是为了降低咖啡豆的梅纳反应,锁住咖啡豆风味。这个时间越短越好。一般带风晒的“冷却盘”,还能实现去掉银皮的效果。

养豆,是由于咖啡豆本身的梅纳反应还在继续,会排出二氧化碳。一般养豆3~7天,让其味道稳定下来。拿刚烘焙和养豆后的咖啡豆,冲煮对比一下,就能明显感受到区别。

2.2 烘焙过程

主要分为脱水、转黄、一爆、二爆这几个阶段。比较专业的划分是:

  • 脱水期,从开始烘焙到转黄,决定豆子是否容易夹生。
  • 梅纳反应期,又叫美娜德反应,从转黄到一爆开始,产生大部分芳香物质。
  • 发展期,从一爆到下豆,决定豆子的风味。

爆米花机一般10分钟左右下豆(完成烘焙),具体时间要看豆子的状态,以及烘焙师想要达到的烘焙度。

DTR,即Development Time Ratio,是指“发展期”占烘豆总时间的百分比。《咖啡烘豆的科學》一书中,作者Scott Rao认为,DTR应于20~25%之间。

3 上一版的不足

升级,就是为了弥补上一版的不足。上一版的缺点包括:

  • 1)只能烘焙50~60g生豆,份量太少。
  • 2)想要提升风力和热量,需要更换配件,比较折腾。
  • 3)热风采用侧风出口,不仅噪音大,还翻动不均匀。
  • 4)组装的零件多,主要是玻璃和不锈钢材质,烘焙过程由于爆米花机震动导致噪音大,影响一爆的声音。

4 新版的解决方案

针对上一版的缺点,解决方案如下:

  • 1)爆米花机改为“北欧欧慕(Nathome)NBM001”。
    热风采用底部直风出口,功率达到1400w,能够翻动100g生豆且受热均匀。其改装过程跟上一个一样,只需把直流电机的电源线,从主板上分离,再接上直流变压器的母头(注意正负极)。
  • 2)采用透明玻璃酒瓶和过滤豆浆渣的布袋,作为爆米花机上盖,避免噪音。
    把酒瓶去底,直接插到爆米花机豆仓上。酒瓶出口就套上过滤豆浆渣的布袋,用于收集银皮。这个简化的装置,几乎没有噪音,而且能清晰观察豆子的烘焙程度。
  • 3)热风温度很高,能在冬天烘豆。
    发热管70%的电压,能吹出220°C热风。

5 总结

升级后的烘豆机,出品很均匀,能够实现不同程度的烘焙度。使用中深烘的云南廉价豆(练习豆),能逼出焦糖风味,甜感爆棚。也试过浅烘的“云南佐园经典日晒”,酸味很突出。

烘焙时,可以试试慢烘与快烘。慢烘是参照别人的烘焙曲线,通过调整发热管电压,实现爬温效果。快烘就是一直高温吹豆,大概3分钟出锅。

总结了几个使U盘更强大的工具。

1. 文件系统

选择exFat文件系统。支持超过4GB的大文件,Windows、Linux、Android都原生支持。注:Android 13才开始原生支持exFat。

2. 启动盘管理

以前一直用YUMI,实现一个U盘启动多个ISO系统镜像(同一时间只能启动一个ISO)。但是每次添加或删除ISO,都要用YUMI处理。后来发现Ventoy更强大,只要把IOS文件放到U盘,就能启动(前提是先把U盘弄成Ventoy启动盘)。

Ventoy默认把U盘划分两个分区。其中空间大很多的分区,默认格式化为exFat(制作完,可手工格式化为其它文件系统),可当作普通U盘使用,直接存放文件。用来启动电脑的ISO文件放进去,即可在启动时选择,超级方便!

3. WinPE启动盘

主要用于维护Windows的电脑,比如Ghost分区。其中这类WinPE当中,很多人推荐微PE

运行exe文件,可以生成ISO文件。U盘使用Ventoy处理后,直接把ISO文件拷贝进去即可。

4. 绿色工具管理

工作的电脑是Windows,有些软件不想装进去,于是找到PortableApps

PortableApps是一个管理“绿色”软件的软件,特别适合装在U盘上。其建立一个“市场”,里面有一大堆常用软件的“绿色版”,包括Chrome。于是,对已安装软件的升级也方便,只是网络比较慢(一般早上8点前,网速会好点)。

Nmon (Nigel's Monitor) 是AIX系统与Linux 系统上,开源免费的监控资源的工具。Njmon则是其下一代的形态。

Njmon的主要特点是:

  • 采用JSON保存数据。
  • 原生支持发送数据到InfluxDB。
  • 可配置不监控的数据。
  • 不支持终端显示数据。
  • 添加了相关工具,包括njmond、nmeasure、njmonchart等。

由于想玩玩InfluxDB,于是按照官方建议,部署了Njmon + InfluxDB + Grafana。然后总结以下缺点:

  • Njmon对Linux支持不足。比如Debian系统,需要自己编译。但是编译过程没什么困难。
  • Njmon虽然支持InfluxDB v2+,但Grafana上没找到能直接使用的模板。Grafana找到的模板是针对InfluxDB v1。
  • 相对Nmon,Njmon参考资料比较少。但是官方有提供Youtube视频教程。

Njmon + InfluxDB + Grafana的部署要点:

  1. njmon命令运行于“nimon”模式,直接连接到InfluxDB时,需要加参数-I
  2. njmon命令连接InfluxDB v2+,需要使用-O 组织名称 -T token这两个参数进行验证。

    # 示例,njmon连接到InfluxDB v2
    sudo ./njmon_Debian11_unknown_v80 -I -s 1 -i 127.0.0.1 -p 8086 -x bucket -O 组织名 -T xxxxxxxxxxx
  3. InfluxDB v2+同时提供v1接口时,需要创建对应数据库(Database)和保存策略(Retention Policies)。

    # 示例,InfluxDB v2创建对应的v1数据库和保存策略
    influx v1 dbrp create --db bucket-db --rp bucket-rp --bucket-id xxxxxx --default --org '组织名' --token 'xxxxxxxxxxx'
  4. InfluxDB可以使用Docker部署。

    # 示例,使用Docker部署InfluxDB v2.4.0
    docker run --name influxdb -p 8086:8086 -v /opt/influxdb/config.yml:/etc/influxdb2/config.yml --volume /opt/influxdb/data:/var/lib/influxdb2 influxdb:2.4.0
  5. Grafana如果没找到相关模板,只能找接近的,再自己修改一下。

总结。体验过InfluxDB + Grafana,挺爽的。只是,如果是用作监控,那么采集数据端不一定使用Njmon。

dstat是一个Linux监控工具。可定制采集数据,可设置采集频率,可输出字符界面和导出CSV。默认一秒一条监测数据。其中以top开头的参数,可以记录检测类型最大值的进程。比如--top-cpu记录CPU占用最大的命令,--top-cpu-adv还会记录CPU占用最大的进程ID等。参数--time的时间格式,需要通过环境变量DSTAT_TIMEFMT进行定义。

示例操作命令:

# 设置时间格式
export DSTAT_TIMEFMT='%Y-%m-%d %H:%M:%S'

# 执行监测,并导出CSV文件
dstat --time --cpu --mem --disk --io --net --sys --top-cpu-adv --top-mem --top-bio-adv --top-io-adv --output /opt/dstat_log/dstat_$(date +%Y%m%d).csv

使用时,可结合tmux,随时查看其采集数据,即时输出在终端。导出的CSV文件,需要下载到本地,并使用第三方工具生成图表。

在众多监控方案中,dstat不算优秀的解决方案,而且只有采集数据的功能。其记录数据,采用CSV格式。如果终端不够宽时,不能完整显示每行的采集数据。而且CSV格式不好扩展,比如--top-cpu-adv记录的数据,不适合机器理解。这里记录一下相关经验。

1 正式版的bug

安装过0.7.3和0.7.4两个版本,并使用Python3运行,都存在以下两个Bug。幸好是使用Python开发,可以直接修复。其安装路径为/usr/bin/dstat

a)在Debian 10以上使用Python3运行时,出现以下Bug:

/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib and slated for removal in Python 3.12; see the module's documentation for alternative uses
  import imp
Terminal width too small, trimming output.
Traceback (most recent call last):
  File "/usr/bin/dstat", line 2847, in <module>
    main()
  File "/usr/bin/dstat", line 2687, in main
    scheduler.run()
  File "/usr/lib/python3.10/sched.py", line 151, in run
    action(*argument, **kwargs)
  File "/usr/bin/dstat", line 2806, in perform
    oline = oline + o.showcsv() + o.showcsvend(totlist, vislist)
  File "/usr/bin/dstat", line 547, in showcsv
    if isinstance(self.val[name], types.ListType) or isinstance(self.val[name], types.TupleType):
NameError: name 'types' is not defined. Did you mean: 'type'?

解决办法,参考以下文档:

简单来说,改两行代码。如下:

# 第547行,改为:
if isinstance(self.val[name], (tuple, list)):

# 第552行,改为:
elif isinstance(self.val[name], str):

b)--top-mem参数统计错误的bug

参考文章:

修改方法def proc_splitline(filename, sep=None),改为:

if filename.startswith("/proc/") and filename.endswith("/stat") and filename != "/proc/stat":
    tmp = linecache.getline(filename, 1).split(sep)
    it = [i for i,c in enumerate(tmp) if c.endswith(')')]
    it = 2 if not it else it[-1]+1
    return tmp[0:1] + [' '.join(tmp[1:it])] + tmp[it:]
else:
    return linecache.getline(filename, 1).split(sep)

2 应用场景

感觉比较适合单机版,或者指定采集一些系统数据。不适合生产机大规模部署。如果非要用dstat不可,可以考虑 dstat + Fluentd + Influxdb + Grafana 这种组合方案。

最近因工作需要,研究了一下Linux服务器的监控方案,收获颇丰。

1 监控需求

服务器需要监控什么?可以分为硬件和软件,或者系统数据和业务数据。一般的监控解决方案,都是针对硬件、操作系统和常用软件(比如数据库、Docker之类)。涉及业务数据,需要二次开发。

2 监控方案设计

一般的监控方案,分为采集、存储、展示、告警,这四大模块或者功能。针对被监控服务器的数量,可灵活实施四个模块的部署方式。比较完善的整体解决方案,还包括“控制”模块,实现服务器集群的统一管理。

2.1 采集

数据采集程序,或者叫“探针”,一般是部署在被监控服务器上的程序。用于采集相关数据,要求占用系统资源小(主要是CPU、内存、磁盘、网络等),对系统影响小。

数据采集的方式,可以采用“推”(push)和“拉”(pull)模式。

“推”是数据采集程序主动把数据从采集端发到存储端。数据具有良好的实时性,方便内网部署并推送到外网服务器。但采集端太多,或者采集的数据比较大,需要考虑存储端的承受能力。遇到存储端没有收到数据时,不能确定是网络问题还是采集端问题。

“拉”是数据采集程序暴露出来,例如开放HTTP服务的端口,存储端去访问并获取数据。存储端也有更多的主动权,决定拉取频率,甚至决定采集样本,能降低带宽、减轻存储端的压力。采集不到数据时,能区分网络问题和采集端问题。如果对数据有实时需求,采集端需要保存未拉走的数据。

针对采集端有时只能部署在内网的情况,有的解决方案会提供“代理”或者“跳板”功能,实现采集端与存储断之间的数据连接。

2.2 存储

数据存储,就是把监控数据持久化,可以是文件(比如CSV文件),也可以是数据库(比如MySQL、InfluxDB)。目前主流的方案,基本采用时序数据库,例如InfluxDB。专门针对这种大量连续时间的数据,提供存储、查询、统计等功能。

2.3 展示

数据展示,一般是把监控数据生成图表,以便更直观地查看和分析。简单的方案是用Microsoft Excel之类的软件,根据导出CSV文件的数据,生成各种图表。主流和灵活的方案是使用可视化软件,例如Grafana,连接时序数据库并生成各种图表。Grafana能够实现实时展示和历史分析。

2.4 告警

通过检测采集的数据,发现超过指定危险指标时,向相关人员发送消息,就是告警。由于相关人员一般不会24小时盯着服务器,所以需要机器进行告警。InfluxDB、Prometheus、Grafana等都有告警功能。一般开源系统只提供邮件或Web Hook(调用钉钉接口)通知,商业系统(例如:阿里云的云监控)会有短信或电话通知。

3 解决方案

服务器监控的解决方案,像编程语言一样,没有一个万能方案,需要根据情况进行选择。这里列举一些相关软件或方案。

3.1 dstat

基于Python。默认一秒采集一条数据,数据定制性高。数据可显示在终端,也可导出CSV文件。需要使用第三方软件,例如Microsoft Excel之类,生成图表和分析数据。高级玩法是,搭配Fluentd,保存数据到InfluxDB,再用Grafana展示、分析。

优点是占用资源小,数据简单。缺点是由于使用CSV格式,复杂数据记录得不够好。而且当前版本(0.7.4)有bug,部署时需要自己修正。

3.2 njmon

C语言开发。nmon的升级版,可设置不收集的数据,数据格式采用JSON。不支持终端显示,原生支持导出文件和发送到InfluxDB。有官方工具处理保存的JSON文件。推荐的玩法是njmon + InfluxDB + Grafana。

优点是占用资源小,作者对整个监控方案考虑比较全面。缺点是对AIX较好但对Linux支持不足。比如Debian 11需要自己编译。Grafana的njmon模板大多针对AIX,而且仅有的Linux模板是针对InfluxDB v1,即使用InfluxQL而不是Flux。

3.3 glances

基于Python,开源跨平台,界面优秀。支持三种模式:单独运行、C/S、Web。提供XML-RPC服务、RESTful JSON接口,也可把数据保存到其它系统,包括InfluxDB。本身支持配置“Actions”,根据事件触发相应脚本,实现告警。

占用资源较大(包括CPU和磁盘空间),界面优秀,玩法多。适合桌面系统的监控。

3.4 InfluxDB

InfluxDB只是个时序数据库,但是该公司开发了Telegraf作为数据收集工具(采用“推”模式),并且InfluxDB新增了告警和图形化展示,形成一个完整的数据收集方案。

3.5 Prometheus

基于Go,监控、告警工具,使用“拉”模式采集数据。

3.6 Zabbix

企业级的开源的服务器监控管理系统,是完整的解决方案,基本可以替代阿里云的云监控之类的系统。Web控制台基于PHP,支持中文显示;采集端基于C,升级版改为Go;数据存储使用MySQL,未支持时序数据库是最突出的缺点。另外,告警功能不支持电话和短信通知。

非常适合企业内部管理服务器集群,便于运维人员使用。

3.7 阿里云的云监控

一般云主机的服务商都提供云监控功能,且基本监控免费,高级功能收费。阿里云的云监控,还能监控非阿里云的主机。使用这些云监控前,要确定是否可以把服务器监控数据发给云厂商,甚至安装云厂商的采集软件。

4 方案选型

根据不同的情况,总结一下各个方案的选型。

4.1 云主机

如果是购买云主机,可以考虑云服务提供商的云监控,一般免费提供基础监控功能,例如阿里云的云监控。但前提是,云主机可以安装云监控的采集端软件,并且接受相关数据上传到云服务提供商那边。另外,高级监控功能,需要付额使用。

4.2 运维管理的服务器集群

企业内部,有专门的运维人员管理服务器,针对硬件或操作系统相关数据的监控,则可以考虑Zabbix。

4.3 业务数据

node_exporter + Prometheus + Grafana,或者 Telegraf + InfluxDB + Grafana,这种方案适合收集业务日志。部署了InfluxDB,还能存储其它数据,个人觉得比较好玩。

近来项目遇到要显示SVG图像文件。本来SVG已不是新鲜事物,应该很好使用,现实并非如此。

设计说用AI(Adobe Illustrator)做出来的图,用Chrome打开会出现错误,比如图形位置不对、该显示的图形没显示等。用文本文件打开那图,会显示xml标签开头,而不是svg标签开头。

图片显示有误的问题,只能让设计去修正了。至于文件格式的问题,找到svgcleaner这个工具,转换一下就好了,体积还有所减少。虽然svgcleaner貌似不更新了,但工作正常。

以前一直用Inkscape代替曾经的Flash,设计矢量图,比如图标之类,还不错。不知道为什么大厂出品的AI会是这么多问题。

近来遇到关于库存与并发的问题。由于一直接触的系统都是没有考虑过商品库存的并发,加上解决过的并发问题,也只是简单直接地采用锁表的方式。所以导致踩坑。

1 问题1,商品基础数据与库存数量,设计在同一个表。

商品基础数据,包括库存数量,主要用于查询。但库存数量,还要解决经常变化,且可能出现并发的情况。如果简单使用锁,即使只锁一行数据,也会导致正在进行下单(涉及扣减库存)的商品不能被浏览(因为锁住,不能查询)。

为了减轻这个情况,下单时,检查库存数量是否足够购买时,不锁数据,等到保存订单数据,真正扣减库存时才加锁。本想着通过减少锁数据的时间,减少商品数据不能查询的情况。但是系统采用Java开发,使用了Spring + Hibernate框架。而Hibernate在事务内使用了一级缓存,即事务内未提交时,查询到的业务数据都放到一级缓存。事务内查询时,会先查询一级缓存,若命中,则不再查询数据库。就导致了检查库存时已获取了商品数据,扣减库存时(从一级缓存获取)不能获取到最新库存(特别是两个客户同时下单同一个商品的情况),最后在并发情况下扣减库存,就出现库存扣少1了的问题。

解决方案很简单,把商品基础数据与库存数据分开两个表存放。库存数据在扣减时,不影响商品浏览。

2 问题2,库存数量,需减少锁定时间。

由于客户浏览商品,或者添加商品到购物车,都需要查询库存数据。如果使用悲观锁,即锁表或锁数据后不能查询,会导致客户不能浏览。参考了以下文章,决定使用乐观锁,即不使用数据库锁。

目前系统规模比较小,且没有涉及分布式,于是决定在扣减库存时直接更新数据的方式。即使用update语句扣减库存时,用where条件判断是否足够扣减,并返回是否扣减成功。

由于使用MySQL,update语句不能返回指定数据(但是,sql server可以使用update...output,PostgreSQL可用update...returning)。加上Hibernate不能同时执行update和select两个语句,最后采用存储过程。参考以下网址:

3 解决方案

总的来说,使用乐观锁(即没有使用数据库的锁),并利用MySQL存储过程实现扣减库存后返回结果。

1)库存表

create table `product_stock` (
    `productId` bigint not null comment '商品ID',
    `instock` int not null default '0' comment '库存数量',
    `createTime` datetime(3) default null comment '创建时间',
    `updateTime` datetime(3) default null comment '更新时间',
    primary key ( productId )
) engine=InnoDB default charset=utf8mb4 collate=utf8mb4_0900_ai_ci comment='商品库存';

2)扣减库存的存储过程

利用存储过程的out参数,返回扣减结果。当outUpdateQty返回的值大于零,扣减成功,否则失败。扣减成功,outStockAfter的值才是正确。

delimiter //
create procedure `product_reduce_instock`(
    in inProductId bigint, /*传入参数:商品ID*/
    in inReduceQty int, /*传入参数:扣减数量*/
    out outUpdateQty int, /*传出参数:实际扣减数量*/
    out outStockAfter int /*传出参数:更新后库存数量*/
)
begin
    -- 初始化返回的值
    set @updateQty=0;
    set @stockAfter=0;
    
    -- 执行扣减库存
    update product_stock 
    set instock = (@stockAfter := instock - (@updateQty := inReduceQty)), updateTime = now() 
    where productId = inProductId and instock >= inReduceQty;
    
    -- 传出参数赋值,即返回扣减结果
    set outUpdateStock=@updateQty;
    set outStockAfter=@updateQty;
end //