46.案例篇|为什么应用容器化后,启动慢了很多?

使用docker打包应用程序,部署,升级,带来一些性能降低、排错困难问题

排查问题:

  1. 查看容器状态
1
$ docker ps -a

分析:如果容器处于Exited,说明容器已经退出

  1. 查看容器状态、退出码以及错误信息,确定容器退出原因
1
2
3
4
5
# 显示容器状态,jq 用来格式化 json 输出
$ docker inspect ubuntu -f '{{json .State}}' | jq

# 打印
{"Status":"running","Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":1876,"ExitCode":0,"Error":"","StartedAt":"2020-07-06T04:49:45.9801914Z","FinishedAt":"2020-07-04T04:54:01.910248094Z"}

分析:Status 容器状态,running表示正常运行

  1. 查看容器内部内存
1
$ docker exec ubuntu free - m
  1. 用其他性能工具分析docker进程性能,比如pidstat

47/48.案例篇|服务器总是时不时丢包,我该怎么办?

网络丢包的情况贯穿了整个网络协议栈

网络丢包位置图

  • 两台VM连接之间,可能会发生传输的错误,比如网络拥堵、线路错误
  • 在网卡收包后,可能会因为网络帧校验失败、Qos等而丢包
  • 在传输层,可能因为端口未建厅、资源占用超过内核限制等而丢包
  • 在套接字层,可能会因为套接字缓冲区溢出而丢包
  • 在应用层,可能会因为应用程序异常而丢包
  • 如果配置了iptables规则,这些网络包也可能因为iptables过滤规则而丢包。

1.链路层

1
2
3
4
5
6
# netstat -i
#打印
Kernel Interface table
Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0      1500   129325      0      0 0         70347      0      0      0 BMRU
lo       65536        6      0      0 0             6      0      0      0 LRU

注:

  • 输出中的 RX-OK、RX-ERR、RX-DRP、RX-OVR ,分别表示接收时的总包数、总错误数、进入 Ring Buffer 后因其他原因(如内存不足)导致的丢包数以及 Ring Buffer 溢出导致的丢包数。
  • TX-OK、TX-ERR、TX-DRP、TX-OVR 指发送时对应的各个指标。
1
 $ tc -s qdisc show dev eth0

2.网络层和传输层

使用netstat -s查看协议的收发汇总

netstat -s

3.iptables

iptables基于Netfilter框架,通过一系列的规则,对网络数据包进行过滤(如防火墙)和修改(如 NAT)。

  1. 查看连接跟踪数
1
2
3
4
5
# 主机终端中查询内核配置
# 最大连接跟踪数
$ sysctl net.netfilter.nf_conntrack_max
# 现存连接跟踪数
$ sysctl net.netfilter.nf_conntrack_count
  1. 通过 iptables -nvL 命令,查看各条规则的统计信息
1
$ iptables -t filter -nvL
  1. tcpdump抓取端口的包
1
2
3
$ tcpdump -i eth0 -nn port 80
# 另外的终端执行curl命令
$ curl --max-time 3 http://192.168.0.30/

49.案例篇|内核线程 CPU 利用率太高,我该怎么 办?

Linux 在启动过程中,有三个特殊的进程,也就是 PID 号最小的三个进程

  • 0号进程为 idle 进程,这也是系统创建的第一个进程,它在初始化 1 号和 2 号进程后,演变 为空闲任务。当 CPU 上没有其他任务执行时,就会运行它。
  • 1号进程为 init 进程,通常是 systemd 进程,在用户态运行,用来管理其他用户态进程。
  • 2号进程为 kthreadd 进程,在内核态运行,用来管理内核线程。

查看内核线程,要从2号进程开始

1
$ ps -f --ppid 2 -p 2
  • kswapd0:用于内存回收。
  • kworker:用于执行内核工作队列,分为绑定CPU(名称格式为 kworker/CPU:ID)和未绑 定 CPU(名称格式为 kworker/uPOOL:ID)两类。
  • migration:在负载均衡过程中,把进程迁移到CPU上。每个CPU都有一个migration内核线程。 jbd2/sda1-8:jbd 是 Journaling Block Device 的缩写,用来为文件系统提供日志功能,以 保证数据的完整性;名称中的 sda1-8,表示磁盘分区名称和设备号。每个使用了 ext4 文件系 统的磁盘分区,都会有一个 jbd2 内核线程。
  • pdflush:用于将内存中的脏页(被修改过,但还未写入磁盘的文件页)写入磁盘(已经在 3.10 中合并入了 kworker 中)。
1
2
# 采样 30s 后退出
$ perf record -a -g -p 9 -- sleep 30
  • net_rx_action 和 netif_receive_skb,表明这是接收网络包(rx 表示 receive)。
  • br_handle_frame ,表明网络包经过了网桥(br 表示 bridge)。
  • br_nf_pre_routing ,表明在网桥上执行了 netfilter 的 PREROUTING(nf 表示 netfilter)。 而我们已经知道 PREROUTING 主要用来执行 DNAT,所以可以猜测这里有 DNAT 发生。
  • br_pass_frame_up,表明网桥处理后,再交给桥接的其他桥接网卡进一步处理。比如,在新的网卡上接收网络包、执行 netfilter 过滤规则等等。 火焰图 火焰图
  • 横轴表示采样数和采样比例。一个函数占用的横轴越宽,就代表它的执行时间越长。同一层的 多个函数,则是按照字母来排序。
  • 纵轴表示调用栈,由下往上根据调用关系逐个展开。换句话说,上下相邻的两个函数中,下面 的函数,是上面函数的父函数。这样,调用栈越深,纵轴就越高。

焰图可以分为下面这几种:

  • on-CPU火焰图:表示 CPU 的繁忙情况,用在 CPU 使用率比较高的场景中。
  • off-CPU火焰图:表示 CPU 等待 I/O、锁等各种资源的阻塞情况。
  • 内存火焰图:表示内存的分配和释放情况。
  • 热/冷火焰图:表示将 on-CPU 和 off-CPU 结合在一起综合展示。
  • 差分火焰图:表示两个火焰图的差分情况,红色表示增长,蓝色表示衰减。差分火焰图常用来比较不同场景和不同时期的火焰图,以便分析系统变化前后对性能的影响情况。