inDocker首发于范浩博科学院我们经常使用PhpStorm结合Xdebug调试代码断点,可以跟踪程序执行过程,方便代码调试,发现潜在问题。博主将开发环境迁移到Docker后,Xdebug调试遇到了一些问题。下面介绍在Docker中使用Xdebug的方法和注意事项。注:开发调试环境为本地Docker中的LNMP,IDE环境为本地Win10下的PhpStorm。本例中Xdebug属于远程调试模式,IDE和本地IP为192.168.1.101,Docker中LNMP容器IP为172.17.0.2。问题描述在Docker中安装配置Xdebug,并在PhpStorm中设置相应的Debug参数后,Debug无法正常工作。此时php.ini中的Xdebug配置如下:xdebug.idekey=phpstormxdeb??ug.remote_enable=onxdebug.remote_connect_back=onxdebug.remote_port=9001//PhpStorm监听本地9001端口xdebug.remote_handler=dbgpxdebug.remote_log=/home/tmp/x调试。日志开始收集问题的详细描述。首先观察PhpStorm的Debug控制台状态:Waitingforincomingconnectionwithidekey***然后查看Xdebug调试日志xdebug.log,有如下错误:I:Checkingremoteconnectbackaddress.I:Checkingheader'HTTP_X_FORWARDED_FOR'.I:检查标头'REMOTE_ADDR'.I:找到远程地址,连接到172.17.0.1:9001.W:为'172.17.0.1:9001'创建套接字,轮询成功,但错误:操作正在进行中(29).E:无法连接到客户端。:-(分析问题查看这些问题描述,基本上可以定位为Xdebug和PhpStorm之间的网络通信问题,然后一步步定位到具体问题。查看本地9001端口,win-ant命令下执行netstat:protocollocaladdressexternaladdressstateunloadingstateTCP0.0.0.0:90010.0.0.0:0LISTENINGInHost9001端口监听正常,然后在容器中使用telnet尝试与本地9001端口建立TCP连接:$telnet192.168.1.1019001Trying192.168.1.101...Connectedto192.168.1.101.Escapecharacteris'^]'表示容器与本地9001的TCP连接是正常的,但是为什么Xdebug报连接失败呢?在这个点,至少可以排除不是因为PhpStorm端配置问题。排查Xdebug问题回去查看Xdebug错误日志,观察失败时注意连接信息:I:Remoteaddressfound,connectingto172.17.0.1:9001.W:Creatingsocketfor'172.17.0.1:9001',轮询成功,但错误:操作正在进行中(29)。E:无法连接到客户端。:-(此时在容器中使用tcpdump截获的数据包如下:$tcpdump-nnAport9001#尝试建立连接,但是Failed12:20:34.318080IP172.17.0.2.40720>172.17.0.1.9001:Flags[S],seq2365657644,win29200,options[mss1460,sackOK,TSval833443ecr0,nop,wscale7]length0E..<..@.@.=.........#)...,...r.XT..................12:20:34.318123IP172.17.0.1.9001>172.17.0.2.40720:Flags[R.],seq0,ack2365657645,win0,length0E..(.]@.@..M.......#)..........-P....B..可以确认Xdebug正在向IP172.17.0.1和端口9001发送目标机器尝试建立TCP连接,而不是正确的本地IP192.168.1.101。到底发生了什么?首先,为了了解Xdebug和PhpStorm的交互过程,查??阅了官方手册了解到,当Xdebug工作在远程调试模式时,有两种工作方式:1、IDE所在机器的IP/单人开发图,由于IDE的IP和监听端口都是已知的,所以Xdebug端在DBGP交互时可以清楚的知道IDE目标机信息,所以Xdebug只需要配置xdebug.remote_host和xdebug.remote_port.2.IDE所在机器IP未知/团队开发由于IDE的IP未知或者有多个IDE,Xdebug无法提前预知DBGP交互的目标IP,所以xdebug.remote_host项不能直接配置(remote_port项可以确定),必须设置xdebug.remote_connect_backisOn标志(xdebug.remote_host项将被忽略)。此时Xdebug在通信时会优先从HTTP_X_FORWARDED_FOR和REMOTE_ADDR中获取一个值作为IDE端的目标IP,这可以通过Xdebug.log记录来确认。I:Checkingremoteconnectbackaddress.I:Checkingheader'HTTP_X_FORWARDED_FOR'.I:Checkingheader'REMOTE_ADDR'.I:Remoteaddressfound接下来,你可以知道Xdebug工作在远程调试模式2,Xdebug会通过HTTP_X_FORWARDED_FOR和REMOTE_ADDR项获取目标机器的IP。Docker启动容器时,80端口已经被映射,忽略宿主机与Docker容器之间复杂的包转发规则,先拦截容器80端口包:$tcpdump-nnAport80#requestinformation13:30:07.017770IP172.17.0.1.33976>172.17.0.2.80:标志[P.],seq1:208,ack1,win229,选项[nop,nop,TSval1250713ecr1250713],长度207E....=@。@...........P....+................Y........GET/v2/room/list.jsonHTTP/1.1Accept:*/*Cache-Control:no-cacheHost:localhostConnection:Keep-AliveUser-Agent:Apache-HttpClient/4.5.2(Java/1.8.0_152-release)Accept-Encoding:gzip,deflate可以看到,数据包的源地址是172.17.0.1,不是真正的源地址192.168.1.101,HTTP请求头中没有HTTP_X_FORWARDED_FOR项。注意:172.17.0.1其实是Docker创建的虚拟网桥docker0的地址,也是所有容器的默认网关。Docker网络通信模式默认为Bridge模式。通信过程中,宿主机会对数据包进行SNAT转换,源地址变为docker0。那么,如何在Docker中获取客户端的真实IP呢?.最后可以确定,由于没有定义HTTP_X_FORWARDED_FOR,所以Xdebug会将REMOTE_ADDR作为IDE的IP。同时由于Docker特殊的网络转发规则,REMOTE_ADDR会被改为网关IP,所以Xdebug与PhpStorm之间的DBGP交互会失败。解决问题由于在Docker容器中获取真实客户端IP比较复杂,所以这里使用Xdebug的remotemode1明确IDEIP,避免源IP被修改,最终解决Xdebug调试问题。方式一Xdebug的主要配置是://没有xdebug.remote_connect_back项xdebug.idekey=phpstormxdebug.remote_enable=onxdebug.remote_host=192.168.1.101xdebug.remote_port=9001xdebug.remote_handler=dbgprestartphp-fpm,使用php--ri确认xdebug无误,使用PhpStorm重新调试。再次在容器中tcpdump抓取9001端口包:#连接源地址正确14:05:27.379783IP172.17.0.2.44668>192.168.1.101.9001:Flags[S],seq3444466556,win29200,选项[mss1460,sackOK,TSval1462749ecr0,nop,wscale7],长度0E..<2.@.@................e.|#).Nc|......r.nO..........Q......再次使用PhpStorm的RESTClient断点调试API时,Debug控制台如下:所以,当使用Xdebug进行远程debugging,需要选择合适的调试模式,Docker下推荐使用远程模式1。其他说明Xdebug版本与PHP版本一致。并非每个Xdebug版本都与每个PHP版本兼容。可以直接使用官方工具选择合适的Xdebug版本。本地文件和远程文件的映射关系如上图所示。使用PhpStorm进行远程调试时,需要配置本地文件与远程文件的目录映射关系,以便IDE根据Xdebug传递的当前执行文件路径匹配本地文件路径。实现断点调试、单步调试等。
