2020年8月

wireshark TSL 1.0 显示协议列为 1.2 的问题

今天遇到一个问题: 新代码发布上去之后, 应用程序启动失败, 显示如下报错信息:

ConnectionException: Socket connection to security.tianxiaohui.com:443 failed with 1 retries
Received fatal alert: protocol_version

从这个出错信息看, 这个客户端连接对应的 server 的 https 出错, 出错的信息显示协议版本问题. 遇到这种问题, 一般我们都会认为客户端和服务端的 TLS 协议的版本不一致造成的.
为了尽快恢复问题, 我们立即对新版本 rollback. 可是出乎意料的是, 老版本也报这个错. 只能这么猜测: 这个 https 访问只在服务器启动的时候访问, 上次老版本发布的时候, 两端的 TLS 协议版本还是能够匹配工作的. 从上次发布完到最近一次新版本发布, 服务器端做了改动, 导致应用不管老版本, 还是新版本, 都无法访问这个 https 的服务.

为了确认这个猜想, 我们对服务器重启, 以便获取 tcp dump, 然后去验证. 结果在使用 wireshark 对这个 tcpdump 进行分析的时候, 遇到了这个 wireshark 显示 TLS 版本不统一的问题, 如下图:
tls.png

在 Protocol 列, 无论是 client hello 还是 server 的 response 都显示为 TLSv1.2, 其实如果查看 client 的详细版本, 会发现内部其实是 TLSv1.0. 如果不仔细查看, 可能会得出错误的结论.

为什么会出现这种情况呢? 这要回到 wireshark 的 SSL dissector 的源码, 从源码看, 对于这个 client hello 协议版本的判断, 是即根据 client 又根据 server 两边做判断的. 正常情况下, 2 端都是一直的, 或能正常协商的情况下, 依照服务端版本取值的. 如果服务端没回应, 这个时候, 是以 client 端的 client hello 的版本做设置的. 我们这里出现的这个问题, 就是属于服务端有回应, 是不同的版本, 导致 wireshark 的显示为和 client 真正发的不一致的问题.

官方网站有个这样的问题: https://www.wireshark.org/lists/wireshark-users/201701/msg00004.html

Browsing the SSL dissector's code it appears that the SSL session
version is based on not just the client hello but also the server
hello. So it would seem that in file-c.pcap the server has responded
that TLS v1.2 is used while in file-u.pcap either the server's
response was not seen or responded that TLS 1.0 will be used.

诊断一台服务器 CPU 稍微偏高的问题案例

运行同一Java web 应用的一个集群里面, 有台服务器最近 CPU 的使用率持续偏高一些, 其它服务器的 CPU 使用率大概都在5%一下, 而这台服务器却保持在15%左右. 它运行的代码和其它server 的代码一模一样. 另外即便重启应用, 还是一样的问题.
cpualittlehigh.png

那么这台服务器为什么会出现这种问题呢? 首先做了 profiling, 发现它的火焰图和其它服务器的火焰图并无二致. 如果登上机器, 使用 top 命令查看, 明显看到它的 CPU 使用率要相对高一些. 另外, 我们发现这台服务器上的所有的进程的 CPU 使用率相对其它同样服务器高一些.

使用 vmstat 命令, 我们可以看到, 它的 interrupt 和 context switch 明显比其他服务器高:
inter&cs.png

于是进一步查看 /proc/interrupts, 也能明显确认 Rescheduling Interrupts 也明显高(这个是累计, 可以使用 procinfo 查看 per second):
res.png

一般来说, 这种 Interrupts 都是由于某个进程持续不断的发出中断导致的, 这种发出中断的进程要想一只发生中断, 那么它也也要不断的运行, 它的 CPU 使用率也不会太低. 所以经常见的就是排除法 -> shutdown 某个进程, 看系统 CPU 使用率有没有改进. 于是我们尝试了对 CPU 使用率较高的几个进程使用了排除法. 可以仍然没有奏效.

考虑到这个 server 是一个 VM (使用 KVM), 所以我们有进一步查看了同一个 Hypervisor 上面的其它 VM, 发现其它 VM 也有相同的问题, 于是我们转而怀疑问题出现在 Hypervisor 上面.

于是对 Hypervisor 上的进程使用排除法, 实际排除的时候先找最像的. 于是我们找到了这个元凶进程 -> kswapd0:
image2020-8-18_2-4-41.png

从上面截图可以看到这个 kswapd 进程 CPU 使用率并不低, swap 的使用量(used)的是3.9G, 其实最开始发现它的时候是 swap 14个G, 后来截图的时候, 已经通过 swapoff 释放了很多.

关于 cgroup

cgroup 有现在(2020)有 2 个版本, 尽管大部分公司还在使用 v1, 不过 v2 更好. 官方文档:
https://www.kernel.org/doc/Documentation/cgroup-v1/
https://www.kernel.org/doc/Documentation/cgroup-v2.txt

下面的内容都是基于 v1:

如何查看一个进程的 cgroup 的 group name?

$ pgrep java #找到我要查看的进程号
23850
# cat /proc/23850/cgroup #这个进程的 cgroup 名字
11:devices:/docker/dfa566d
10:pids:/docker/dfa566d
9:blkio:/docker/dfa566d
8:hugetlb:/docker/dfa566d
7:freezer:/docker/dfa566d
6:cpuset:/docker/dfa566d
5:memory:/docker/dfa566d
4:cpuacct,cpu:/docker/dfa566d
3:net_prio,net_cls:/docker/dfa566d
2:perf_event:/docker/dfa566d
1:name=systemd:/docker/dfa566d

$ mount | grep cgroup # 查看 cgroup 文件系统挂载的路径 默认是 /sys/fs/cgroup/
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
(rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
...
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)

$ ls -lah /sys/fs/cgroup/cpu/docker/dfa566d/  #该进程的 cgroup 文件
-rw-r--r-- 1 root root 0 Aug 13 05:05 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 13 05:05 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 13 05:07 cgroup.procs
-r--r--r-- 1 root root 0 Aug 13 05:05 cpuacct.stat
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpuacct.usage
-r--r--r-- 1 root root 0 Aug 13 05:05 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Aug 13 05:05 cpu.shares
-r--r--r-- 1 root root 0 Aug 13 05:05 cpu.stat
-rw-r--r-- 1 root root 0 Aug 13 05:05 notify_on_release
-rw-r--r-- 1 root root 0 Aug 13 05:05 tasks

CPU:

https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
https://engineering.squarespace.com/blog/2017/understanding-linux-container-scheduling
cpu usage soft限制: 使用 cpu.shares 文件. 它基于各 group 之间的比重, 默认比重是 1024. 如果某个 group 的进程使用量达不到自己的比重, 就会把自己剩余的 CPU 使用时间放到一个公共的 pool, 其它使用量超过自己使用量的比重的 group 里面的进程都可以到这个 pool 去取用. 当公共 pool 里面没有可用的时候, 就按照竞争CPU 的各个 group 的比重去分配. 所以如果竞争不激烈, 这个值意义不大, 比重低的group 里面的进程也有可能使用最多的 CPU(其他 group 都有剩余).

cpu usage hard 限制: 使用 cpu.cfs_period_us & cpu.cfs_quota_us 配合. period 代表一段时间, 单位是微妙,默认值是 100ms (100000), 最大值是 1s (1000000). quota 代表在 period 期间该 group 可以使用的量, 最小可能值是 1ms(1000). -1 代表不限制. 在多 CPU core 情况下, quota 的值可能比 period 大, 代表可能是用 n 个 CPU, 比如 period 是 100ms, quota 是 300ms 代表可以使用 3 个 CPU 的 core 的量.

内存 Memory

https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
内存的限额使用 memory.limit_in_bytes 限制, 当前的使用量可以查看 memory.usage_in_bytes.

/sys/fs/cgroup/memory/docker/dfa566d/

文件系统

在 host 上面可以通过 /proc//root/ 访问 container 里面的文件
或者可以通过 docker cp 命令来 copy 文件
在 container 里面访问 host 文件要在 container 里面加 volume 的方式

linux /proc/pid/ 下常用的文件

/proc/ 下常见的文件

 $ ls -lah
----
cmdline  # 系统 boot 的信息博阿凯 image, 和其他参数
cpuinfo  # CPU 信息
crypto   # 系统支持的加密方法
devices  # 系统的快设备和字符设备
diskstats   # 磁盘统计
filesystems # 系统支持的文件系统
interrupts  # 系统启动以来收到的 interrupt
kallsyms    # kernel 符号表
meminfo     # 内存信息
modules     
mounts      # 类似 mount 命令
partitions  # 分区信息
self        # 当前进程目录链接
swaps       # swap 信息
verison     # 系统版本
vmstat     
zoneinfo

/proc/pid/ 目录下常见有用的文件

cd /proc/11748/
ls -lah 
-----
cgroup  # 当前进程的 cgroup 信息 /sys/fs/cgroup/...
cmdline # 启动进程的命令 包括选项 参数
comm    # 启动进程的命令
coredump_filter   # core dump 要包含那些 segments
cpuset  # cpu pin(绑定)的 core
cwd     # 进程工作路径 current work directory
environ # 当前进程的环境变量
exe     # 启动进程的命令的符号链接  通过 #sudo file /proc/11748/exe 可看
fd      # 当前进程打开并在使用的所有文件  #sudo ls -lah /proc/11748/fd 可看
fdinfo  # 当前进程打开并在使用的所有文件id
limits  # 当前进程的资源限额  使用 ulimit -a 查看系统的
status  # 当前进程的基本信息, 比如父进程 ID, 内存信息
task    # 当前进程的 thread 信息