Docker下网络性能问题排查

一 背景

docker 下的nginx的服务,在一些情况下访问请求会反馈比较慢,根据网文学习下,记录下一些实验过程。

二 验证环境

docker 太难下载了,找了一个老的环境的nginx,导入到系统中来:

// 导入nginx
# docker load -i  nginx.tar.gz 
// 启动nginx 以本地端口启动
# docker run -d -p 8153:80 --name my-nginx nginx 

查看验证下web是否启动

[root@nms ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                  NAMES
f5de348d753e        nginx               "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:8153->80/tcp   my-nginx
7a2a244a57f8        splos:5.1nms        "/bin/sh /etc/rcS_do…"   2 weeks ago          Up 23 hours                                5.1nms
[root@nms ~]# netstat -antp|grep 8153
tcp6       0      0 :::8153                 :::*                    LISTEN      2480/docker-proxy 

# 访问也是正常的
[root@nms ~]# curl http://127.0.0.1:8153
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

用ab测试下web的性能:

// -c 是5000个并发,一共发起请求10w个 -r 接受到错误仍然继续,-s 超时时间为2s
# ab -c 5000 -n 100000 -r -s 2 http://10.xx.xx.xxx:8153/
Document Path:          /
Document Length:        612 bytes

Concurrency Level:      5000
Time taken for tests:   9.158 seconds
Complete requests:      100000
Failed requests:        5397
   (Connect: 0, Receive: 0, Length: 2741, Exceptions: 2656)
Write errors:           0
Total transferred:      82191460 bytes
HTML transferred:       59528016 bytes
Requests per second:    10919.47 [#/sec] (mean)
Time per request:       457.898 [ms] (mean)
Time per request:       0.092 [ms] (mean, across all concurrent requests)
Transfer rate:          8764.52 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  255 741.4      5    7029
Processing:    18  112 277.4     50    7372
Waiting:        0  107 266.6     48    6758
Total:         27  367 817.0     57    8660

Percentage of the requests served within a certain time (ms)
  50%     57
  66%     66
  75%    249
  80%    449
  90%   1059
  95%   1429
  98%   3062
  99%   3272
 100%   8660 (longest request)

注意看下几个关键指标:

Requests per second:    10919.47 [#/sec] (mean)  每秒平均请求数量10919.47
Time per request:       457.898 [ms] (mean) 每个请求的平均延迟为457ms
Connect:        0  255 741.4      5    7029: 建立链接的平均延迟255ms 

在启动一个直接映射本地端口的nginx的容器:

// 启动一个直接映射端口的nginx容器
# docker run -d -p 9123:80 --network=host --privileged  --name my-nginx-host1  nginx 

本想这样启动将容器的80端口映射到9123 ,结果没效果,设置了host网络后,映射端口的配置失效,更改如下:

// 拷贝一个nginx.conf 然后直接做文件映射替换原来的文件,结果如下
[root@nms ~]# docker run -d  --network=host --privileged  --name my-nginx-host4 -v /root/nginx.conf:/etc/nginx/nginx.conf nginx

用ab这个工具继续测试:

// 说明同上
[root@localhost spiderflow]# ab -c 5000 -n 100000 -r -s 2  http://10.xx.xx.xxx:9153/
// 关键信息如下
Document Path:          /
Document Length:        612 bytes

Concurrency Level:      5000
Time taken for tests:   5.356 seconds
Complete requests:      100000
Failed requests:        16296
   (Connect: 0, Receive: 25, Length: 8192, Exceptions: 8079)
Write errors:           0
Total transferred:      77597195 bytes
HTML transferred:       56200572 bytes
Requests per second:    18670.58 [#/sec] (mean)
Time per request:       267.801 [ms] (mean)
Time per request:       0.054 [ms] (mean, across all concurrent requests)
Transfer rate:          14148.28 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  139 442.0      5    3036
Processing:    11   58 113.6     31    1663
Waiting:        0   49 108.7     27    1653
Total:         16  196 480.4     37    4651

Percentage of the requests served within a certain time (ms)
  50%     37
  66%     56
  75%    100
  80%    137
  90%    443
  95%   1041
  98%   1825
  99%   3036
 100%   4651 (longest request)

可以看到通过配置host网络方式,即端口直接映射到主机上,不做端口转换的情况下,性能会提升不少;

Requests per second:    18670.58 [#/sec] (mean)  平均请求数量从1w左右提升到了1.8w
Time per request:       267.801 [ms] (mean) 每个请求平均延迟也从457ms降低到了267ms
Connect:        0  139 442.0      5    3036     平均建链延迟从255ms降低到139ms

不过这个异常数也更多,异常数更改-s后面的超时时间,可以显著降低,这个先不关注;
需要环境建立好了之后就可以排查原因了。

三 问题排查

3.1 抓包排查问题

排查网络问题,抓包一般必不可少,我们先来抓点包看看什么情况:

[root@nms ~]# tcpdump -i ens192 tcp port 8153 -w a.pcap

用wireshark打开分析:


专家信息

从信息来看不少包重组失败,实际看下来,有各种重传错误,REST包等,如下:


一个流交互信息

3.2 丢包原因分析

从上面报文重组失败的情况来看,显然是发生了丢包,因为程序是一样的,运行环境是一样的,所以丢包肯定只能是内核里面丢包,需要排查具体哪里丢的,什么原因丢的。
可以使用动态追踪工具来排查丢包原因,eBPF 在centos下相对来说安装比较麻烦,我们用systemstap,追踪脚本如下:

#! /usr/bin/env stap

############################################################
# Dropwatch.stp
# Author: Neil Horman <nhorman@redhat.com>
# An example script to mimic the behavior of the dropwatch utility
# http://fedorahosted.org/dropwatch
############################################################

# Array to hold the list of drop points we find
global locations

# Note when we turn the monitor on and off
probe begin { printf("Monitoring for dropped packets\n") }
probe end { printf("Stopping dropped packet monitor\n") }

# increment a drop counter for every location we drop at
probe kernel.trace("kfree_skb") { locations[$location] <<< 1 }

# Every 5 seconds report our drop locations
probe timer.sec(5)
{
  printf("\n")
  foreach (l in locations-) {
    printf("%d packets dropped at %s\n",
           @count(locations[l]), symname(l))
  }
  delete locations
}

简单来说,就是打印内核调用kfree_skb的位置,这些位置即是丢包的位置;
打印的结果类似:

// 执行
stap -g  --all-modules dropwatch.stp

12 packets dropped at nf_hook_slow
11 packets dropped at ip_rcv_finish
8 packets dropped at ip6_mc_input
1 packets dropped at icmpv6_rcv

9 packets dropped at nf_hook_slow
6 packets dropped at ip6_mc_input
5 packets dropped at ip_rcv_finish
1 packets dropped at tcp_v6_rcv

19 packets dropped at ip_rcv_finish
12 packets dropped at nf_hook_slow
8 packets dropped at ip6_mc_input
^CStopping dropped packet monitor

通过上面脚本我们知道具体的丢包函数集中在:nf_hook_slow ,这个函数是 Netfilter 框架的一部分,它负责执行挂钩(hooking)到内核网络层的自定义函数,这些函数通常用于包过滤、网络地址转换(NAT)、数据包修改等。另外换个思路,但是我们注意到上面的测试情况,在同一台主机上,同一个镜像,唯一的区别就是一个做了端口映射,一个直接通过主机网络,通过主机网络这种方式直接在主机的默认网络空间开的端口,没有经过NAT转换,也就是说NAT转换造成的性能差异,我们先看看NAT转换的配置:

# 即显示NAT转换表 -n 表示不做ip转成域名、-L显示列表 -t nat 只查询nat表;
#iptables -nL -t nat

我们知道docker如果默认的网络的IP是内部172的IP段,和外部服务交互的时候,需要将172这个ip转成主机的IP,这就要做SNAT转换; 另外外部服务回消息的时候,要通过DNAT转成内部的端口。
我们来看下NAT表内容:

[root@nms ~]# iptables -nL -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:80

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8153 to:172.17.0.3:80

其中比较关键的配置:MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 表示从容器IP段172.17.0.0/16 发出来的所有包(目的地址不做限制,协议不限制)都做源地址伪造,即源地址替换为本机地址;
MASQUERADE tcp -- 172.17.0.3 172.17.0.3 tcp dpt:80 这条规则比较少见,对于tcp协议,源地址为172.17.0.3 地址,(即咱们前面启动的nginx容器,映射的端口为8153端口)访问的目的地址为:172.17.0.3 地址,tcp端口为80的时候,将做源地址伪造,举例:

172.17.0.3:1234------> 172.17.0.3:80

即172.17.0.3连接本机的80端口,会被规则改变成:

10.xx.xx.xx:1234------> 172.17.0.3:80

这条没看出来有啥重要作用,感觉不映射也问题不大,毕竟都是本机访问,连网卡都不用走吧,为什么需要,知道的兄弟告知下。

重要的还有下面一条规则:

DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8153 to:172.17.0.3:80

即在tcp报文中,任意ip访问任意ip的时候,如果tcp的目的端口是8153,将它转发给容器的172.17.0.3 的80端口,上面的RETURN表示其他情况直接返回正常流程处理。

通过上面可知,docker访问外部的时候,源IP地址被转换为主机的ip,返回的时候通过DNAT配置把映射主机的端口,映射到具体的容器的端口上,这里是80端口。

通过上面分析,只是梳理了一下Docker容器的网络包转发的流程,并没有找到慢的原因。

3.2 NAT转换性能排查

由于我们上面几乎可以肯定是NAT转换问题,那问题就转到如何排查的,通过上面的规则分析,请求和返回的时候都需要做NAT地址信息的转换,转换的时候是需要保存每个连接的状态,跟踪连接的状态的,才能根据映射端口映射到正确的容器的正确端口上去。

cat /proc/net/nf_conntrack    

四 问题解决

4.1 systemtap安装

在centos下安装:

yum install systemtap kernel-devel yum-utils kernel

4.2 systemtap 丢包打印 只有地址没有打印函数名

错误现象:

[root@miao miao]# ./dropwatch.stp 
Monitoring for dropped packets
18 packets dropped at 0xffffffff8341ab57
17 packets dropped at 0xffffffff8342704d
24 packets dropped at 0xffffffff8342704d
8 packets dropped at 0xffffffff8341ab57
8 packets dropped at 0xffffffff8341ab57
3 packets dropped at 0xffffffff8342704d
1 packets dropped at 0xffffffff834df57c
...

原因就是缺少内核符号表:

vim /etc/yum.repos.d//CentOS-Linux-Debuginfo.repo  

将里面的enable改成1.
安装内核符号表:

debuginfo-install -y kernel-$(uname -r)
// 或安装
yum install kernel-debuginfo kernel-devel

// 执行准备
stap-prep
sysctl -w kernel.printk="7 4 1 7"
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容