首發於 樊浩柏科學院
咱們常常會使用 PhpStorm 結合 Xdebug 進行代碼斷點調試,這樣能追蹤程序執行流程,方便調試代碼和發現潛在問題。博主將開發環境遷入 Docker 後,Xdebug 調試遇到了些問題,在這裏整理出 Docker 中使用 Xdebug 的方法和注意事項。php
說明:開發和調試環境爲本地 Docker 中的 LNMP,IDE 環境爲本地 Win10 下的 PhpStorm。這種狀況下 Xdebug 屬於遠程調試模式,IDE 和本地 IP 爲 192.168.1.101,Docker 中 LNMP 容器 IP 爲 172.17.0.2。
在 Docker 中安裝並配置完 Xdebug ,並設置 PhpStorm 中對應的 Debug 參數後,可是 Debug 並不能正常工做。html
此時,php.ini
中 Xdebug 配置以下:git
xdebug.idekey = phpstorm xdebug.remote_enable = on xdebug.remote_connect_back = on xdebug.remote_port = 9001 //PhpStorm監聽本地9001端口 xdebug.remote_handler = dbgp xdebug.remote_log = /home/tmp/xdebug.log
開始收集問題詳細表述。首先,觀察到 PhpStorm 的 Debug 控制檯出現狀態:github
Waiting for incoming connection with ide key ***
而後查看 Xdebug 調試日誌xdebug.log
,存在以下錯誤:docker
I: Checking remote connect back address. I: Checking header 'HTTP_X_FORWARDED_FOR'. I: Checking header 'REMOTE_ADDR'. I: Remote address found, connecting to 172.17.0.1:9001. W: Creating socket for '172.17.0.1:9001', poll success, but error: Operation now in progress (29). E: Could not connect to client. :-(
查看這些問題表述,基本上能夠定位爲 Xdebug 和 PhpStorm 之間的 網絡通訊 問題,接下來一步步定位具體問題。json
Win 下執行 netstat -ant
命令:網絡
協議 本地地址 外部地址 狀態 卸載狀態 TCP 0.0.0.0:9001 0.0.0.0:0 LISTENING InHost
端口 9001 監聽正常,而後在容器中使用 telnet 嘗試同本地 9001 端口創建 TCP 鏈接:phpstorm
$ telnet 192.168.1.101 9001 Trying 192.168.1.101... Connected to 192.168.1.101. Escape character is '^]'.
說明容器同本地 9001 創建 TCP 鏈接正常,可是 Xdebug 爲何會報鏈接失敗呢?此時,至少能夠排除不會是由於 PhpStorm 端配置的問題。socket
回過頭來看看 Xdebug 的錯誤日誌,注意觀察到失敗時的鏈接信息:tcp
I: Remote address found, connecting to 172.17.0.1:9001. W: Creating socket for '172.17.0.1:9001', poll success, but error: Operation now in progress (29). E: Could not connect to client. :-(
此時,在容器中使用 tcpdump 截獲的數據包以下:
$ tcpdump -nnA port 9001 # 嘗試創建鏈接,可是失敗了 12:20:34.318080 IP 172.17.0.2.40720 > 172.17.0.1.9001: Flags [S], seq 2365657644, win 29200, options [mss 1460,sackOK,TS val 833443 ecr 0,nop,wscale 7], length 0 E..<..@.@.=...........#)...,......r.XT......... ............ 12:20:34.318123 IP 172.17.0.1.9001 > 172.17.0.2.40720: Flags [R.], seq 0, ack 2365657645, win 0, length 0 E..(.]@.@..M........#).........-P....B..
能夠肯定的是, Xdebug 是向 IP 爲 172.17.0.1 且端口爲 9001 的目標機器嘗試創建 TCP 鏈接,而非正確的 192.168.1.101 本地 IP。到底發生了什麼?
首先,爲了搞懂 Xdebug 和 PhpStorm 的交互過程,查了 官方手冊 得知,Xdebug 工做在遠程調試模式時,有兩種工做方式:
一、IDE 所在機器 IP 肯定/單人開發
圖中,因爲 IDE 的 IP 和監聽端口都已知,因此 Xdebug 端能夠很明確知道 DBGP 交互時 IDE 目標機器信息,因此 Xdebug 只需配置 xdebug.remote_host、xdebug.remote_port 便可。
二、IDE 所在機器 IP 未知/團隊開發
因爲 IDE 的 IP 未知或者 IDE 存在多個 ,那麼 Xdebug 沒法提早預知 DBGP 交互時的目標 IP,因此不能直接配置 xdebug.remote_host 項(remote_port 項能夠肯定),必須設置 xdebug.remote_connect_back 爲 On 標識(會忽略 xdebug.remote_host 項)。這時,Xdebug 會優先獲取 HTTP_X_FORWARDED_FOR 和 REMOTE_ADDR 中的一個值做爲通訊時 IDE 端的目標 IP,經過Xdebug.log
記錄能夠確認。
I: Checking remote connect back address. I: Checking header 'HTTP_X_FORWARDED_FOR'. I: Checking header 'REMOTE_ADDR'. I: Remote address found
接下來,能夠知道 Xdebug 端是工做在遠程調試的模式 2 上,Xdebug 會經過 HTTP_X_FORWARDED_FOR 和 REMOTE_ADDR 項獲取目標機 IP。Docker 啓動容器時已經作了 80 端口映射,忽略宿主機同 Docker 容器複雜的數據包轉發規則,先截取容器 80 端口數據包:
$ tcpdump -nnA port 80 # 請求信息 13:30:07.017770 IP 172.17.0.1.33976 > 172.17.0.2.80: Flags [P.], seq 1:208, ack 1, win 229, options [nop,nop,TS val 1250713 ecr 1250713], length 207 E....=@.@..............P.. .+.......Y...... ........GET /v2/room/list.json HTTP/1.1 Accept: */* Cache-Control: no-cache Host: localhost Connection: Keep-Alive User-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 的 遠程模式 1 明確 IDE 端 IP 來規避源 IP 被修改的狀況,最終解決 Xdebug 調試問題。
模式 1 的 Xdebug 主要配置爲:
//並無xdebug.remote_connect_back項 xdebug.idekey = phpstorm xdebug.remote_enable = on xdebug.remote_host = 192.168.1.101 xdebug.remote_port = 9001 xdebug.remote_handler = dbgp
重啓 php-fpm,使用php --ri xdebug
肯定無誤,使用 PhpStorm 從新進行調試。
再次在容器中 tcpdump 抓取 9001 端口數據包:
# 鏈接的源地址已經正確 14:05:27.379783 IP 172.17.0.2.44668 > 192.168.1.101.9001: Flags [S], seq 3444466556, win 29200, options [mss 1460,sackOK,TS val 1462749 ecr 0,nop,wscale 7], length 0 E..<2.@.@..........e.|#).Nc|......r.nO......... ..Q.........
再次使用 PhpStorm 的 REST Client 斷點調試 API 時, Debug 控制檯以下:
因此,使用 Xdebug 進行遠程調試時,須要選擇合適的調試模式,在 Docker 下建議使用遠程模式 1。
並非每一個 Xdebug 版本都適配 PHP 每一個版本,能夠直接使用 官方工具,選擇合適的 Xdebug 版本。
如上圖,在使用 PhpStorm 時進行遠程調試時,須要配置本地文件和遠端文件的目錄映射關係,這樣 IDE 才能根據 Xdebug 傳遞的當前執行文件路徑與本地文件作匹配,實現斷點調試和單步調試等。