1. 问题描述
如图所示,从反向代理Nginx请求一个简单的PHP脚本,告警提示:There was a problem sending 193 bytes on socket 7: Broken pipe
2. 检查端口连通性
由于Nginx和FPM都是基于Alpine自行构建的,在Nginx容器中,没有telnet
工具,可以使用nc
工具替代;
nc
的安装,可以基于https://pkgs.alpinelinux.org/contents
查询命令,其他Alpine包也可以通过检索安装;
fpm为fpm所在主机号,Docker中可以直接用服务名作为主机号
1
2
3
4
5
6
7
8
| // 安装strace和nc
apk add strace netcat-openbsd
-z Zero-I/O mode (scanning)
-v Verbose
/etc/nginx # nc -zv fpm 9000
Connection to fpm 9000 port [tcp/*] succeeded!
|
3. 启用strace调试准备
由于strace在容器中默认是禁用的,如果单独的Doker命令run起来的,可以加上--privileged Give extended privileges to this container
;
docker-composer.yml配置中,通过加入SYS_PTRACE
能力支持,更多可以参考:https://docs.docker.com/compose/compose-file/#cap_add-cap_drop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| version: "3.7"
services:
nginx:
ports:
- 86:86
networks:
phpfpm-net:
proxy-net:
aliases:
- phpfpm-nginx
- phpngx
volumes:
- /data/www:/data/www
- /data/learn:/data/learn
depends_on:
- fpm
restart: always
fpm:
cap_add:
- SYS_PTRACE
security_opt:
- seccomp:unconfined
build:
context: ./fpm
image: tkstorm/phpfpm
networks:
phpfpm-net:
volumes:
- /data/www:/data/www
- /data/learn:/data/learn
restart: always
networks:
phpfpm-net:
name: phpfpm-net
proxy-net:
external: true
name: proxy-net
|
4. 进入fpm容器中跟踪调试
1
2
| // 调试www相关进程
strace -s 1024 -p $(pgrep www|tr '\n' ','|sed 's/,$//')
|
通过curl请求phpinfo.php脚本页面时候,直接终止在recvfrom(7
这个系统调用
1
2
3
4
5
6
7
8
9
10
11
| [pid 15] writev(6, [{iov_base="[15] <- eval -i 12 -- KHN0cmluZy"..., iov_len=67}, {iov_base=NULL, iov_len=0}], 2) = 67
[pid 15] getpid() = 15
[pid 15] writev(6, [{iov_base="[15] -> <response xmlns=\"urn:deb"..., iov_len=223}, {iov_base=NULL, iov_len=0}], 2) = 223
[pid 15] write(7, "257\0<?xml version=\"1.0\" encoding"..., 262) = 262
[pid 15] recvfrom(7, "eval -i 13 -- KHN0cmluZykoJF9TRV"..., 128, 0, NULL, NULL) = 59
[pid 15] getpid() = 15
[pid 15] writev(6, [{iov_base="[15] <- eval -i 13 -- KHN0cmluZy"..., iov_len=67}, {iov_base=NULL, iov_len=0}], 2) = 67
[pid 15] getpid() = 15
[pid 15] writev(6, [{iov_base="[15] -> <response xmlns=\"urn:deb"..., iov_len=236}, {iov_base=NULL, iov_len=0}], 2) = 236
[pid 15] write(7, "270\0<?xml version=\"1.0\" encoding"..., 275) = 275
[pid 15] recvfrom(7, strace: Process 6 detached
|
再往上找这个文件描述符7,会发现是由于打开了一个192.168.65.2:9801
的套接字
1
2
3
4
5
6
7
8
| [pid 15] socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 7
[pid 15] fcntl(7, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
[pid 15] fcntl(7, F_SETFD, FD_CLOEXEC) = 0
[pid 15] connect(7, {sa_family=AF_INET, sin_port=htons(9801), sin_addr=inet_addr("192.168.65.2")}, 16) = -1 EINPROGRESS (Operation in progress)
[pid 15] poll([{fd=7, events=POLLIN|POLLPRI|POLLOUT}], 1, 200) = 1 ([{fd=7, revents=POLLOUT}])
[pid 15] getpeername(7, {sa_family=AF_INET, sin_port=htons(9801), sin_addr=inet_addr("192.168.65.2")}, [28->16]) = 0
[pid 15] fcntl(7, F_SETFL, O_RDONLY) = 0
[pid 15] setsockopt(7, SOL_TCP, TCP_NODELAY, "\1\0\0\0\0\0\0\0", 8) = 0
|
这个配置的端口其实就是xdebug扩展模块,配置的与jebrain应用的tcp通信调试端口,xdebug基于DBGp
协议,所以才会往套接字中写入与协议相关的内容,参考,https://xdebug.org/docs/remote
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| Connect
<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1"
xmlns:xdebug="http://xdebug.org/dbgp/xdebug"
fileuri="file:///home/httpd/www.xdebug.org/html/docs/index.php"
language="PHP"
protocol_version="1.0"
appid="13202"
idekey="derick">
<engine version="2.0.0RC4-dev"><![CDATA[Xdebug]]></engine>
<author><![CDATA[Derick Rethans]]></author>
<url><![CDATA[http://xdebug.org]]></url>
<copyright><![CDATA[Copyright (c) 2002-2007 by Derick Rethans]]></copyright>
</init>
(cmd)
|
5. 排查xdebug配置
1
2
3
4
5
6
7
| zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
xdebug.remote_autostart=1
xdebug.idekey=PHPSTORM
xdebug.remote_enable=on
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9801
xdebug.remote_log=/tmp/xdebug.log
|
默认的xdebug的使用过程中,通常需要通过COOKIE或者GET,携带上相关的标识信息(或者利用相关插件,比如firebug之类的);
我不想使用这类插件,通信由希望与容器中的PHP进程进行调试,所以配置的默认远端调试自动启动;
1
2
3
4
| xdebug.remote_autostart
Type: boolean, Default value: false
Normally you need to use a specific HTTP GET/POST variable to start remote debugging (see Remote Debugging). When this setting is set to 1, Xdebug will always attempt to start a remote debugging session and try to connect to a client, even if the GET/POST/COOKIE variable was not present.
|
到此,我才发现我开了的PHPSTORM已经默默阻塞了多个请求!!