30天休假-day4

泉州本地有各种宗教, 市区有佛教寺庙, 伊斯兰教堂, 还有基督教教堂. 然后有各种吃的.

不知道为什么, 从福州开始, 发现这边很多道路没有非机动车道, 非机动车要和汽车抢道路, 有时候2车道的汽车2边以及中间都是电瓶车.

上午去了泉州著名的西街, 不过我对西街的感觉就是一个小镇的步行街的感觉. 开元寺还是不错的, 寺庙里面的榕树真大, 寺庙上的彩绘也很不错. 西街那头的钟楼, 可能我一开始理解错了, 我以为是其它城市的那种老式钟楼, 鼓楼, 发现就是一个现代建筑, 上面挂一个大钟.

福建本地的姜母鸭, 我只吃了一口, 也能不符合我的胃口, 我还是更钟爱本地的海鲜粥.

看到小红书上很多人说泉州需要慢慢体会, 或者需要文化底蕴才能欣赏当地的人文, 自认为没有文化底蕴和时间, 于是去往下一站厦门.

从泉州到厦门只要1个小时, 于是中间去了石狮市的洛咖寺, 那个寺庙沿海而建, 在夕阳下拍出的照片还不错.

夜宿厦门, 定了厦门鼓浪屿对面的一个海景酒店, 虽然能看到海, 看到鼓浪屿, 但是那个酒店没有停车库, 麻烦死了, 要停到不远的另外一个酒店附近, 一晚上至少60的停车费, 对于不开车的人更好一些. 还差点没停到一个每小时10元的一个车库去.

30天休假-day3

day3上午了三坊七巷, 那条主街还不错, 好多小吃和类似展馆的地方, 有些里面还是私宅. 当时看一个导游介绍三坊七巷里涉及的名人, 真的感叹福建姓林的名人真不少啊, 不知道为啥突然想到了电视剧《破冰行动》里的林姓族长.

喝了当地的有名的花生汤, 太甜, 不符合我的胃口. 当地的特色鱼丸也试了下, 感觉也一般.

看了平潭岛还不错, 于是下午开车1个多小时到了平潭岛. 然后沿着沿海公路转一圈. 有些公路没有在海边, 相对美国的西海岸公路, 还是差点意思. 不过中间很多地方都设置了观景点. 虽说现在不是时候, 天气偏冷, 风特别大, 还是有不少游客. 大多数跟团来的都是老年游客, 年轻人基本都是自驾过来的.

最后看看海景也没啥, 选了一家点评上不错的饭馆吃了顿晚饭.

平潭县城估计还不错, 但是当地的民宿基本是农村样式的, 不会太好, 于是直接开到了泉州. 平潭县离泉州只有1个多小时的车程.

30天休假-day1

转眼间入职eBay中国已经超过10年了. 公司的一项福利就是每5年可以休连续4周的假期.

上次5年的时候, 正好赶上儿子出生几个月的时候, 所以基本就在家了, 没有出门. 这次满10年的时间是2022年的4月1日, 在那之前一直想计划去中国西部玩一圈. 去西部的话, 最好在5月之后, 所以之前只是确定了方向, 没有计划. 结果还没有到4月1日, 上海的疫情就愈演愈烈, 后面就是封城3个月, 直到6月多才放开. 放开之后, 各种查健康码, 查阴性证明, 去哪里都不方便. 后来新疆又封了, 所以出行计划基本搁置.

好在这个假期可以在一年内使用, 就一直等在疫情有所好转. 好的事情是, 国家在过年前放开了防疫政策, 所以年后基本可以出行了, 不过要在4月1日前用完, 所以去西部基本不可能了.

于是想, 不如直接一路向南吧. 所以计划就是从上海一直向南, 开车过去, 直到海南. 由于还是3月份, 气温还没升起来, 所以要尽快往南赶, 然后到海南, 再折返回来.

其实只是确定了方向, 并没有详细的计划. 直到出发前一夜, 才确定第一站: 雁荡山. 第一站的选择基本在浙江南部, 另外有2个候选的地方, 都是因为周末人太多限流, 最终选择了曾经去过一次的雁荡山.

去之前几天已经开始记录着想要带什么东西, 比如药, 耳温枪, 现金等. 可是等到真的出发了200多公里, 才发现忘记带公司的Yubi Key了. 导致这一段旅程没法接入公司网络, 彻底和公司VPN失去了联系.

从上海出发, 出了金山, 就到了浙江嘉善, 然后就会发现一路的高速基本都是限速100以内, 并且基本都是区间限速, 过一个区间, 立马进入另外一个区间. 不过这倒挺适合电车的, 电车只要不到110以上, 基本都能跑到表显的路程. 定速巡航还是很好开的.

高速公路的充电桩大部份都是4个, 也有2个坏掉的, 也有8个全都能用的. 只要不是节假日, 基本都有空余的.

进入浙江开始, 隧道特别多, 基本都是山区, 因此高速过路费也比较贵. 另外有些爬坡路段, 对于电车来说, 爬坡耗电, 下坡则省电, 甚至充电.

去雁荡上路非常好走, 基本下高速, 在开几公里就到了雁荡山景区.

由于早上出发晚, 路上还充了一次电, 到雁荡山就下午了, 所以就逛了灵岩景区. 好久没爬山了, 当天感觉还可以, 第二天早上就感觉小腿很肿痛. 不过好在6岁的儿子这次爬山没让抱, 基本自己爬.

夜宿雁荡山民宿, 虽然不是旺季, 但是民宿的价格基本和二线城市市中心的舒适级别的酒店价格差不多.

linux 上如何测试一个网页能不能用

今天有个同事说, 她在生产环境某个container里面的代码访问某个网页页面, 返回的结果不是期望的. 能不能一起看看到底是哪里出错了? 竟然有这么有趣的问题, 于是满口答应, 一块去探个究竟.

当然, 第一步就是去重现一下场景. 于是kubectl exec <pod_id> -n <ns> bash 登录container, 想着使用curl去模拟访问, 结果很沮丧. 为啥, 这个 container 真干净, 上面的代码是Java写的, 只有jdk, 没有curl, 没有wget, 让我怎么复现? 难道要自己写个Java Client, 打包编译, 然后去复现?

除了使用Java代码去实现访问网页http请求, 其他真没办法了吗? 让我们整理一下在Linux上常见的使用工具访问web网页的办法.

使用 curl

curl 是最方便的测试网页访问的工具, 很多Linux发布版都带curl, 支持很多协议.

$ curl -vvv 'https://www.tianxiaohui.com/index.php/about.html'

使用 wget

wget 支持 http, https, ftp, ftps这些协议去访问某个页面或资源, 也很方便使用, 很多Linux 发行版也自带.

$ wget 'https://www.tianxiaohui.com/index.php/about.html'

使用 netcat | ncat

netcat 是 GNU 一个网络实用工具, 不过根据官方网站在2004年发完0.7.1版本之后, 就没更新过了, 我发现我 Ubuntu 使用的是openBSD 版本的(https://packages.debian.org/sid/netcat-openbsd). 它最常用的是打开端口, 建立连接, 至于http协议, 那要自己去实现. 实现http协议还好, 如果实现证书认证https, 那可是有点难度.

所以, 你看我只能拿到一个400的错误. 不过要是访问非https的, 还是很可行的.

$ printf “GET /index.php/about.html HTTP/1.0\r\nHost: www.tianxiaohui.com\r\n\r\n” | nc www.tianxiaohui.com 443
F<html><head><title>400 Bad Request</title></head><body>
<h2>HTTPS is required</h2>
<p>This is an SSL protected page, please use the HTTPS scheme instead of the plain HTTP scheme to access this URL.<br />
<blockquote>Hint: The URL should starts with <b>https</b>://</blockquote> </p>
<hr />
Powered By LiteSpeed Web Server<br />
<a href='http://www.litespeedtech.com'><i>http://www.litespeedtech.com</i></a>
</body></html>

有人称它为网络debug的瑞士军刀, 没有继续开发, 是不是可惜了. 所以鼎鼎大名的nmap工具下面, 发扬了netcat的功能, 开了一ncat, 比netcat 更好用. 还支持 ssl. 那必须测试一把

$ printf "GET /index.php/about.html HTTP/1.0\r\nHost: www.tianxiaohui.com\r\n\r\n" | ncat -v --ssl www.tianxiaohui.com 443

Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: SSL connection to 154.213.16.200:443.
Ncat: SHA-1 fingerprint: 8461 369A 0DE4 7401 AE26 4259 B0C2 CC9F FE0B 66F5
HTTP/1.0 200 OK
Connection: close
content-type: text/html; charset=UTF-8
x-pingback: https://www.tianxiaohui.com/index.php/action/xmlrpc
date: Thu, 16 Feb 2023 15:08:37 GMT
server: LiteSpeed
vary: User-Agent,User-Agent

<!DOCTYPE HTML>
<html class="no-js">
<head>
...省略
</html>

完美拿到数据

使用 openssl

openssl 是一个开源的, 通用的加密安全的工具软件, 很多Linux 发行版都带了它, 使用它进行网页访问, 也不在话下.

$printf "GET /index.php/about.html HTTP/1.0\r\nHost: www.tianxiaohui.com\r\n\r\n" | openssl s_client -quiet -state -connect www.tianxiaohui.com:443
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = www.tianxiaohui.com
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
HTTP/1.0 200 OK

仅仅使用 bash, 你没看错

如果你的container里面非常干净, 上面的软件全没有, 但恰巧你安装的是bash, 那么这个任务还是能完成. 仔细看:

supra@suprabox:~$ exec 4<>/dev/tcp/www.tianxiaohui.com/443
supra@suprabox:~$ echo -e "GET /index.php/about.html HTTP/1.0\r\nHost: www.tianxiaohui.com\r\n\r\n" >&4
supra@suprabox:~$ cat  <&4
<html><head><title>400 Bad Request</title></head><body>
<h2>HTTPS is required</h2>
<p>This is an SSL protected page, please use the HTTPS scheme instead of the plain HTTP scheme to access this URL.<br />
<blockquote>Hint: The URL should starts with <b>https</b>://</blockquote> </p>
<hr />
Powered By LiteSpeed Web Server<br />
<a href='http://www.litespeedtech.com'><i>http://www.litespeedtech.com</i></a>
</body></html>

虽然, 能拿到结果, 但是没有ssl协商, 所以网站只给了一个400. 所以 bash 这种方式, 对于https只能验证连接成功, 不能验证拿到的结果.

那么我们就拿一个http的网站来练习一下, 可是如今到哪里去找一个还支持http的网站呢, 哈哈, 还真找到一个. 如下:

supra@suprabox:~$ exec 5<>/dev/tcp/www.henu.edu.cn/80
supra@suprabox:~$ echo -e "GET /index.htm HTTP/1.0\r\nHost: www.henu.edu.cn\r\n\r\n" >&5
supra@suprabox:~$ less  <&5

完美拿到结果, 自己处理ssl 太难了, 不过对于仅仅http, 还是很管用的.

对于上面3步的解释:

  1. /dev/protocol/host/port 是bash的一个重定向文件, 它帮你建立一个连接, 协议可以是tcp或udp, host可以是主机名或ip, 所以第一行通过exec建立一个文件描述符是5的重定向.
  2. 第二步是把 http 协议的request 发送到这个连接
  3. 第三步读出返回的结果网页.

总结

上面的很多工具都很好用, 有些还支持设置代理, 设置timeout 等, 据有相当丰富的功能. 在container 里面受限的情况下, 还有哪些好用的网页测试工具? 当然, 你说我container 里面有 python, 有 perl, wow, 那你太幸福了.

查看一个socket的开始时间

问题起源

有一个服务分布在3个数据中心, 有一个统一的FQDN(svc1.vip.tianxioahui.com)让别人去访问它, 同时每个数据中心都有一个单独的IP来访问该服务, 也就是说这个域名能解析到3个不同的IP. 有一次某个数据中心的该服务出现了点问题, 于是对应的IP被从DNS服务器上临时去掉了. 当问题修复之后, DNS服务器上恢复对应的IP, 该数据中心的流量却没有恢复, 还是流向了其它2个数据中心. 问题到底出现在哪呢?

初步调查

该服务的使用方是我们内部的一个服务, 于是我们首先检查客户端的DNS解析. 同时, 我们的DNS服务器会根据查询者所在的数据中心首先返回本数据中心的IP, 当没有对应数据中心的IP之后, 才会返回不同的数据中心的IP.

正常情况下的服务访问情况:
Screen Shot 2023-02-10 at 20.47.20.png

当 DC-C 的服务出问题的时候:
Screen Shot 2023-02-10 at 20.49.26.png

于是我们登录之前出问题数据中心某个客户端, 查询 DNS 解析情况, 发现查询的结果就是已经恢复的数据中心的IP, 也就是DNS解析已经完全恢复.

于是通过 ss 去查看现存的tcp连接, 发现确实是连接到其它数据中心的.

为什么恢复后还连接到其它数据中心?

首先经过连续多次测试, DNS解析的结果仍然是当前数据中心的IP, 所以DNS解析完全是正常的. 所以怀疑这个连接是之前早就建立好的, 并且一直 keep-alive, 所以一直还是正常在用的.

如何确定这个连接已经存活多久了?

我们想通过最简单的方式: ss 命令, 看看它是否提供了一个tcp连接的开始时间man ss答案是, 并没有.

于是, 我们找到这篇: https://superuser.com/questions/565991/how-to-determine-the-socket-connection-up-time-on-linux

这个问题的最高评答案里面给的过程是:

  1. 找到这个tcp连接所在的进程和proc文件系统的inode
  2. 然后使用stat命令去查看文件的最后访问时间(回答里用的是: query the file access time of the symbolic link)

然而这个并不是socket uptime, 而是socket最后的访问时间, 可是那个shell函数又命名为suptime, 真是对不上.

但是, 他使用stat访问文件的创建时间是对的

文件创建时间

通常情况下,使用stat都能查看文件的创建时间, 比如:

supra@suprabox:~$ stat ./bootstrap.sh
  File: ./bootstrap.sh
  Size: 4162          Blocks: 16         IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 10763859    Links: 1
Access: (0775/-rwxrwxr-x)  Uid: ( 1000/   supra)   Gid: ( 1000/   supra)
Access: 2022-11-22 19:06:03.507882931 -0800
Modify: 2022-11-22 19:05:59.487695031 -0800
Change: 2022-11-22 19:05:59.491695212 -0800
 Birth: 2022-11-22 19:05:59.487695031 -0800

上面最后一行 Birth 就是它的创建时间.

查看socket inode的创建时间

其实上面输出中的 Birth 是在 inode 的元数据里的, 所以, 如果我们按照那个答案的方法去访问socket虚拟文件的inode是不是就找到了socket 的创建时间?

sudo ss -t -p -e
State    Recv-Q  Send-Q   Local Address:Port     Peer Address:Port      Process
ESTAB    0       0        10.249.64.103:ssh      10.226.199.52:61340    users:(("sshd",pid=812231,fd=4),("sshd",pid=812101,fd=4)) timer:(keepalive,54min,0) ino:2211652 sk:5063 cgroup:/system.slice/ssh.service <->

ss命令通过制定 -p 参数, 现实进程号和fd信息(pid=812101,fd=4), 通过-e参数, 显示inode 信息(ino:2211652). 于是, 我们可以查看这个虚拟文件的inode 信息了

supra@suprabox:~$ sudo stat /proc/812101/fd/4
  File: /proc/812101/fd/4 -> socket:[2211652]
  Size: 64            Blocks: 0          IO Block: 1024   symbolic link
Device: 17h/23d    Inode: 2228948     Links: 1
Access: (0700/lrwx------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-02-11 01:51:35.510079439 -0800
Modify: 2023-02-11 01:51:26.442196746 -0800
Change: 2023-02-11 01:51:26.442196746 -0800
 Birth: -

可是Birth 这行是空.
文件的符号链接指向 socket:[2211652], 这个方括号里面的值就是我们通过ss查看得到的 inode id.

同时, 我们去查看 cat /proc/net/tcp 也能看到一样的行

最终, 也没找到一个可以查看socket创建时间的命令.