Ubuntu 20.04 原生 WireGuard 初体验

前不久 Ubuntu 20.04 正式 release 了,挺多期待已久的新特性都转正成为了“原生”,如zfs、WireGuard. 而作为一名 gopher, 我留意到,负责维护 golang 标准库中网络相关(net/http)的 committer Bradfitz 前不久从Google离职加入了基于 WireGuard 技术创业公司 Tailscale. 这引发我强烈的好奇,因此 Ubuntu 20.04 发布,自然是要体验一下 WireGuard.

WireGuard 的关键特性

WireGuard 一直宣传自己是现代的快速安全的VPN。快速主要体现在部署的便捷、高性能(通过内核原生+CPU友好的加密算法实现);安全则是从设计之初就考虑到了了前辈的一些安全问题,以及学院派研究的加持,更多细节可以参考 technical whitepaper. 当然,从当前的limitation看,ID还无法做到前向安全,因此无法做到 no-tracking。

WireGuard 安装及配置

安装直接参考官方Installation文档,配置可以参考 Set Up WireGuard VPN on Ubuntu.

几点疑问及思考

WireGuard 能作为你特殊用途协议吗?

不能。主要原因有两点:

  • 协议没有做混淆,也没有隐藏包特征,过不了DPI(Deep Packet Inspection)检测。
  • ID非前向安全。因此通信一方compromised以后,会泄露历史通信peer.

WireGuard Server 支持 DDNS 吗?

支持。在一些场景,可能 server 端并没有一个固定IP,这种时候我们一般是使用DDNS将动态IP绑定在一个域名上面。这种场景下,配置client端配置时,将Server Public IP配置为域名即可:

[Peer]
PublicKey = <server public="" key="">
Endpoint = <server ddns="" domain="">:51820
AllowedIPs = 10.0.0.2/24, fd86:ea04:1115::5/64

WireGuard Server 是否可以动态添加 peer?

不可以也可以。因为 WireGuard 理念认为提前设计和分配自己的网络更佳好的方式。不过,也并不是完全没有work around的方式。如果你有动态IP和配置的需求,个人用户可以参考wg-dynamic, 企业用户推荐直接使用 Tailscale 的产品。

一个出生在内核的VPN到底有何特殊的意义?

回到本源,VPN本身的目的是什么?私以为是anytime, anywhere, 简单、快速、安全的网络互联。而这与WireGuard的设计目标是完全一致的。互联网底座这几年的发展趋势之一是加密整个互联网:元老级应用层协议http在发展过程中不断进化,来到http/2 的时候我们看到的一个显著改变那就是 mutilplex 和 force https。而作为更加底层的网络基建VPN却一直没有出现这样的角色。WireGuard的出现让我们看到了这个可能,但也只是一个更好的VPN的定位。而直到20.04确认将其加入内核,为其加密整个网络插上翅膀,我们才有理由相信这个角色到位了。

从这个角度来看,WireGuard无限接近理想的VPN,或者说它重新定义了VPN的初心。因此,不要拿个人日常运动小需求来揣测WireGuard的鸿鹄之志。顺着这个方向继续看,可以看到 WireGuard 的(潜在)应用场景有:

  • 远程办公网络基建。传统的VPN能不能解决?能。但是都解决得不好,要么是部署太过复杂,要么协议本身就因为历史原因漏洞一大把。而WireGuard部署成本极低(如果你按照上述文档部署一次,应该不会超过10分钟),全客户端支持,code base 只有4000行,非常容易发现、修复、迭代。而这次疫情的出现,让远程办公成为新常态推进了一大步。但是扪心自问,有多少所谓的远程办公是战斗力残废,又有多少其实是临时让IT开了个网络口子裸奔办公?因此,私以为基于WireGuard的远程办公在未来一两年是能看到几家公司成长起来的。
  • 异地多活架构。K8S生态解决单IDC可用性问题是足够的,但是在当前越来越强调稳定、体验产品时代,很多公司都开始走上异地多活的架构。WireGuard可能带来两个契机:1. 云厂商IDC互通的大一统;2. 自主异地多活架构的标准化,而这可能带来一波IDC异地多活网络中间件的开源生态繁荣。
  • 智能家居网络路由器。虽然现在的家电只要搭配一个wifi就号称自己是智能家电,但大多数不过是把家电连到厂家的服务器。这只能算是智能家电社会主义初级阶段,未来理想的形态是家庭中的设备与业主anytime, anywhere的互通。以前的解决方案都太trick了,而WireGuard找了个好爹,并且其极低的门槛都有希望在这个系分领域有所建树。

而以上这些,不过是 Bradfitz 的新东家 Tailscale 已经发布的部分创业产品。过两年以后,我会回头再次审视以上判断,说不定,上面的判断全都错了。

2020之Macbook Pro DIY 升级 SSD及Time Machine数据恢复

一直接受不了touchbar世代macbook的键盘和type-c接口,因此手上的工作主力依然是2013 late 的macbook pro 13 inch 和 2015 mid macbook 15 inch. 两台机器的硬盘都是256GB,实际可用约240G左右。经过多年使用下来,经常提示硬盘 almost full,严重影响使用心情。而这两款作为最后一代可以自行升级硬盘的macbook, 自然是不能放过苹果少有的bug福利,准备自行更换升级SSD.

哪些macbook支持自行升级SSD硬盘

2013 Macbook Pro A1493 A1502 ( MD864 MD865 MD293 Md294) SSD

2013 MacBook Pro retina A1398 A1502 (ME864 ME865 ME866 ME293 ME294) SSD

2013 Macbook Air A1465 A1466 ( MD711 MD712 MD760 MD761) SSD

2014 Macbook Air A1465 A1466 ( MD711 MD712 MD760 MD761) SSD

2014 MacBook Pro retina A1502 A1398 (MGX72 MGX82 MGX92 MGXA2 MGXC2) SSD

2015 MacBook Pro retina A1502 A1398(MF839 MF840 MF841 MGXA2 MGXC2) SSD

2015 MacBook Air A1465 A1466 (MJVM2 MJVP2 MJVE2 MJVG2) and Mac Pro ME253 MD878 SSD

及2016年份的AIR PRO 机器

机器型号可以通过把笔记本翻过来看背面的标识来确认。

也有一个简单方法来确认自己的macbook是否可以自行更换硬盘:2013~2016年购买,且A面带有苹果光环Logo的都是可以自行更换的。

选择什么样的SSD

虽然是苹果少有的福利,但是毕竟是一个bug福利,市面上的NVME SSD是没法直接在macbook上使用的。需要一块转接卡,将NVME的SSD转换为苹果的特殊接口,这个我们放在后面单独说。

市面上一般推荐的NVME SSD有 intel 760p, 三星 970 EVO, 三星 970 EVO Plus, 三星 970 Pro,还有最近比较火的海康 C2000 Pro. 因为作为日常主力机使用,因此我个人的考虑优先级是这样的:

  1. 兼容性和容量优先,容量1T起步,淘汰三星 970 EVO Plus 和 海康 C2000 Pro:
  2. 三星 970 EVO Plus: 看到有几个反馈兼容性问题,有解决方法,但不想没事找事。相对于 970 Evo唯一的提升是写入性能,但是在这个速度级别,这个提升我并不care了。
  3. 海康 C2000 Pro: 可能不应该把这货跟 760p 和 970 一起比较,不过架不住这货实在便宜呀。但是便宜是有代价的,缓存用的还是上古时代的DDR3,既不是低电压也不是DDR4, 差点意思,而且多个案例反馈睡眠唤醒也有点问题。
  4. 然后考虑SSD的寿命和质保,并且寿命 > 质保。这块淘汰760p, 只有144TBW,而970 EVO 有 600TBW。质保上,海康终身质保,不过数据无价,优先考虑先天体质较好的。
  5. 在高温与温度之间,倾向于低温,妥协性能。这点上 760p 比 970 EVO 更优。但无奈寿命和性能与 EVO 差异太大,价格也不占优势,最后1399的价格入了三星 970 EVO 1T. (当前SSD价格没有太大优势,不过再等等估计就只有QLC了…)

顺便多说一点,拆机后发现苹果原装的SSD也是三星提供的,因此特别担心兼容性问题的话,就直接看 970 系列吧。

NVME转接卡的选择

因为转接卡只负责NVME到苹果特殊接口的针脚线映射,没有任何芯片引入,因此这个小东西基本不存在兼容和坑。有些同学可能遇到了NVME速度跑不满的问题,那是因为13寸的macbook只能支持到PCI x2,15寸的macbook才能支持到PCI x4(俗称满血版)。但是相信我,即使是日常开发,你也用不出来区别,优先考虑升级SSD的容量和兼容性即可。

我选了一个15块包邮的绿色小板,因为的确没什么技术含量,做工不错,针脚映射也非常整齐。

备份数据

在拆机升级SSD之前,需要先备份数据。我使用 Time Machine 把全部数据备份到了一块320GB的移动硬盘。因为是上古时代USB 2.0速度的机械移动硬盘,因此大概花了5个小时备份200GB的数据,我估计USB 3.0或SSD移动硬盘能在2个小时内完成。

Time Machine 备份数据会把目标硬盘格式化为苹果文件系统格式,因此你需要拿一块可以格式化或者空的移动硬盘进行备份。备份占用空间与原数据占用空间基本是1:1. 此外,时间机器支持增量备份,在恢复的时候你可以选择需要恢复的时间点,跟windows 还原点的概念类似。

拆机更换SSD

整体没什么难度。讲几个大家关心的点:

  1. 转接卡插入苹果机器接口需要用力,听到咔哒一声才算完全插入。此时,你应该看不见转接口的金手指针脚。因此推荐的安装顺序是:a. 安装转接卡到机身;b. 安装NVME SSD到转接卡(这里轻轻一推即可);c. 安装尾部螺丝。
  2. 很多人说更换SSD需要断电,我……因为懒,没有断电,直接拆的旧硬盘和安装的新硬盘。其实机身的电源线挺难拆的,我试了几秒钟,没有拆下来,因此选择直接安装。
  3. 有人说970 EVO不带螺丝和散热片,比较小家子气。嗯,的确小家子气。不过如果你是升级macbook SSD这个场景,其实这两个根本就不需要:螺丝用原装的即可无缝安装,散热片那是给台式机准备的,macbook根本装不进去。

恢复系统和数据

上面说备份的时候,我们只提到了备份数据,恢复的时候,因为是全新的SSD,系统也是没有的。因此整个恢复过程可以分为恢复系统和数据两部分。因为我们上面备份的数据里面没有系统,因此实际上从 Time Machine 恢复的时候,是先从网络自动恢复系统,然后再恢复数据。由于需要从网络下载macOS, 因此推荐你使用有线网络连接macbook进行恢复。

几个关键步骤:

  1. 开机按住 Command (⌘)-R ,然后选择Disk Utility, GUID + APFS 方式格式化硬盘;
  2. 然后选择从时间机器备份恢复
  3. 整个时间分两部分:转地球部分是在下载系统,使用有线网络大概用了1个半小时;接下来是恢复备份数据,大概也是5个小时,跟备份时间相当😂
  4. 恢复完数据以后,根据官方指引重置RP
  5. 然后拔掉网线和硬盘,重新开机。如果你以前数据较多,第一次输入用户名密码以后,会出现黑屏,只能看见一个鼠标。不要着急,耐心等待10分钟即可。
  6. 备份和恢复数据时间比较长,我都是选择在晚上完成,早上起来点一下按钮。因此,即使你只有一台macbook作为主力机,更换升级SSD过程中也完全不会影响你白天的使用。

升级后使用感受总结

  1. 升级后SSD性能提升到写 2500MB/s, 读 3000MB/s, 比原装SSD提升了好几倍,但使用上体会不到区别。
  2. 没有修改任何设置的前提下,即没有睡眠问题也没有耗电问题。购入的三星 970 EVO 出厂日期是 2020年1月,因此,我推测凡是今年购入的 970 EVO 应该升级后都可以完美兼容。
  3. SSD温度问题没有专门关注过,因为跟原厂一样的使用感受,具体是什么温度就不care了。
  4. SSD容量扩大4倍以后,补充了几个需要使用但是频率不高的软件,尤其是安装了几个虚拟机。不考虑喜新厌旧的前提下,感觉还能再战3年。

记一次数据库bug故障恢复

从一件小事说起

最近一个月不知何故,博客的VPS IP无法ping通。提交ticket要求更换ip, 毫无意外的被拒绝了。于是直接从当前VPS clone 一台间接实现了IP的更换。clone的文章参考官方指引即可。因为是克隆操作,因此所有的环境和数据都保持不变,配置成本约定于0。整个过程加上修改DNS,10分钟搞定。正准备来杯清茶休息一下的时候,发现新机器的mysql挂了:

2020-03-21T12:00:03.913175Z 0 [Note] InnoDB: Apply batch completed
2020-03-21T12:00:04.016847Z 0 [Note] InnoDB: Removed temporary tablespace data file: “ibtmp1”
2020-03-21T12:00:04.016974Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2020-03-21T12:00:04.017051Z 0 [Note] InnoDB: Setting file ‘./ibtmp1’ size to 12 MB. Physically writing the file full; Please wait …
2020-03-21T12:00:04.020703Z 0 [Note] InnoDB: Starting in background the rollback of uncommitted transactions
2020-03-21T12:00:04.020779Z 0 [Note] InnoDB: Rolling back trx with id 35499526, 1 rows to undo
12:00:04 UTC – mysqld got signal 11 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
Attempting to collect some information that could help diagnose the problem.
As this is a crash and something is definitely wrong, the information
collection process might fail.

key_buffer_size=8388608
read_buffer_size=131072
max_used_connections=0
max_threads=151
thread_count=0
connection_count=0
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 68195 K bytes of memory
Hope that’s ok; if not, decrease some variables in the equation.

Thread pointer: 0x0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong…
stack_bottom = 0 thread_stack 0x40000
/usr/sbin/mysqld(my_print_stacktrace+0x2c)[0xed2ebc]
/usr/sbin/mysqld(handle_fatal_signal+0x451)[0x7b4ea1]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x10340)[0x7f85fd1a5340]
/usr/sbin/mysqld(_Z32page_cur_search_with_match_bytesPK11buf_block_tPK12dict_index_tPK8dtuple_t15page_cur_mode_tPmS9_S9_S9_P10page_cur_t+0x168)[0xf89528]
/usr/sbin/mysqld(_Z27btr_cur_search_to_nth_levelP12dict_index_tmPK8dtuple_t15page_cur_mode_tmP9btr_cur_tmPKcmP5mtr_t+0x1121)[0x10b2d31]
/usr/sbin/mysqld(_Z22row_search_index_entryP12dict_index_tPK8dtuple_tmP10btr_pcur_tP5mtr_t+0x11f)[0xffd4ff]
/usr/sbin/mysqld[0x11cb0d6]
/usr/sbin/mysqld(_Z12row_undo_insP11undo_node_tP9que_thr_t+0x22b)[0x11cc0bb]
/usr/sbin/mysqld(_Z13row_undo_stepP9que_thr_t+0x405)[0x1019385]
/usr/sbin/mysqld(_Z15que_run_threadsP9que_thr_t+0x871)[0xfa97c1]
/usr/sbin/mysqld(_Z31trx_rollback_or_clean_recoveredm+0xcdc)[0x106c8dc]
/usr/sbin/mysqld(trx_rollback_or_clean_all_recovered+0x4e)[0x106d7ee]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x8182)[0x7f85fd19d182]
/lib/x86_64-linux-gnu/libc.so.6(clone+0x6d)[0x7f85fc6aa47d]
The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
information that should help you find out what is causing the crash.
2020-03-21T12:00:04.056733Z mysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended

从错误信息 mysqld got signal 11 看,应该是启动过程是尝试fix table触发了bug,因此启动失败。

如何解决 mysqld got signal 11

既然是fix corrupted table导致的问题,那么思路自然是先把mysql启动起来,备份数据,然后找到存在问题的table, 再逐步恢复数据(我的mysql server版本是:5.7.23,不同版本可能稍有差异):

  1. 如果服务器未正常停止,先停止mysql: service mysql stop
  2. 备份文件/var/lib/mysql/ib*: mkdir /tmp/fixdb &amp;&amp; cp -rp /var/lib/mysql/ib* /tmp/fixdb
  3. 在配置文件/etc/mysql/my.cnf中添加如下内容:

[mysqld]

innodb_force_recovery = 3

  1. 启动mysql: service mysql start. 如果启动失败,尝试改大步骤3的数字,直到启动成功。
  2. 备份数据表: mysqldump -A -uUSERNAME -p &gt; dump.sql
  3. 删除所有用户数据库:

mysql -uUSERNAME -p

drop database xxx…

  1. 停止mysql: service mysql stop
  2. 删除ib文件:rm /var/lib/mysql/ib*
  3. 删除步骤3中添加的两行内容
  4. 启动mysql: service mysql start. 在mysql错误文件(/var/log/mysql/error.log)中应该可以看到最开始导致启动失败的corrupted table.
  5. 恢复数据:mysql -uUSERNAME -p &lt; dump.sql. 如果恢复过程中出现 Tablespace exists错误,则尝试删除ibd文件,然后重新导入数据:

ERROR 1813 (HY000) at line 268: Tablespace ‘DBNAME.TABLENAME‘ exists.

rm /var/lib/mysql/DBNAME/*.ibd

mysql -uUSERNAME -p < dump.sql

终于大功告成。恢复过程中小紧张了一下,使用了10年的VPS终于满血复活。

参考文献