Search code examples
windowsdockervisual-studio-codexdebugphp-7.2

Docker xDebug not connecting to VSCode


I'm trying to set up php debugging with VSCode and xDebug, but xDebug can't connect to the host. Thus, VSCode doesn't hit any breakpoints either.

When I start the debug listener in VSCode, run a Bash shell in the php-fpm container and try to connect to the host, it fails:

$ docker-compose exec php-fpm bash
root@178ba0224b37:/application# nc -zv 172.20.0.1 9001
172.20.0.1: inverse host lookup failed: Unknown host
(UNKNOWN) [172.20.0.1] 9001 (?) : Connection refused

I'm confused about the IP addresses, because in the Docker settings the Virtual Switch subnet is set to 10.0.75.0, and the network adapter vEthernet (DockerNAT) uses the IP 10.0.75.1. How do the containers get the IP range 172.20.0.x?

From my desktop I am unable to request the webpage using 172.20.0.1.
It works fine with 10.0.75.1, which shows the phpinfo() as expected, but the breakpoint is not triggered.
phpinfo() shows xDebug is configured and the settings match what I have in the php-ini-overrides.ini config.

I've disabled the firewall, tried different IP's, and checked the port and various xDebug, php, docker-compose, and VSCode debug settings.
I've been searching far and wide, but I guess I'm still missing something. My guess is that it has to do with the network connection, but I don't know what else I can change to fix this issue.

Setup
Host is Windows 10 with docker-compose and VSCode.
I got the docker debug-test directory from https://phpdocker.io/generator
Basically it uses two docker containers: nginx:alpine and phpdocker/php-fpm

My VSCode workspace looks like this:
VSCode workspace
(The readme files come from the phpdocker.io generator and contain some basic Docker info)

index.php contents:

<?php
  phpinfo(); // <-- VSCode breakpoint here
  echo 'hello there';
?>

The IP addresses for the containers:

/debug-test-php-fpm - 172.20.0.3
/debug-test-webserver - 172.20.0.2

$_SERVER['REMOTE_ADDR']: 172.20.0.1 <- the host?

Configs and logs
launch.json contents:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "pathMappings": {
                "/application/public": "${workspaceRoot}/public"
            },
            "log": true,
            "port": 9001,
            "xdebugSettings": {
                    "max_data": 65535,
                    "show_hidden": 1,
                    "max_children": 100,
                    "max_depth": 5
            }
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9001
        }
    ]
}

docker-compose.yml contents:

###############################################################################
#                          Generated on phpdocker.io                          #
###############################################################################
version: "3.1"
services:

    webserver:
      image: nginx:alpine
      container_name: debug-test-webserver
      working_dir: /application
      volumes:
          - .:/application
          - ./phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      ports:
       - "8000:80"

    php-fpm:
      build: phpdocker/php-fpm
      container_name: debug-test-php-fpm
      working_dir: /application
      volumes:
        - .:/application
        - ./phpdocker/php-fpm/php-ini-overrides.ini:/etc/php/7.2/fpm/conf.d/99-overrides.ini

php-ini-overrides.ini contents:

upload_max_filesize = 100M
post_max_size = 108M

# added for debugging with Docker and VSCode
xdebug.remote_enable=1
xdebug.remote_connect_back=On
xdebug.remote_autostart=1
# xdebug.remote_host=172.20.0.1 # using remote_connect_back instead, which should work for any IP
xdebug.remote_connect_back=1
xdebug.remote_port=9001
xdebug.profiler_enable=0
xdebug.var_display_max_depth = 5
xdebug.var_display_max_children = 256
xdebug.var_display_max_data = 1024 

xdebug.remote_log = /application/xdebug.log
xdebug.idekey = VSCODE

xdebug.log contents after one visit to the page:

Log opened at 2019-01-30 12:37:39
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.20.0.1:9001.
W: Creating socket for '172.20.0.1:9001', poll success, but error: Operation now in progress (29).
E: Could not connect to client. :-(
Log closed at 2019-01-30 12:37:39

Log opened at 2019-01-30 12:37:39
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.20.0.1:9001.
W: Creating socket for '172.20.0.1:9001', poll success, but error: Operation now in progress (29).
E: Could not connect to client. :-(
Log closed at 2019-01-30 12:37:39

This is no paste error, it actually logs the request two times for some reason.

Debug console in VSCode after starting the debug listener:

<- launchResponse
Response {
  seq: 0,
  type: 'response',
  request_seq: 2,
  command: 'launch',
  success: true }

Any thoughts? I'm lost.. Perhaps it has to do with the DockerNAT setup?

Sorry for the long post. I'm still new to Docker, I hope this has all the info needed.

Edit: solved

See my answer below.


Solution

  • After some coding I stumbled upon the solution.
    The IP address in the php debug settings was incorrect. Since my system has VPN connections, multiple ethernet adapters, multiple virtual switches, and multiple virtual machines, it's a bit tricky to find out what's used where.

    I discovered the IP by accident when I ran netstat on the php container during a request:

    $ docker-compose ps --services
    php
    app
    
    $ docker-compose exec php sh
    /var/www/html # netstat
    Active Internet connections (w/o servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State
    tcp        0      0 08674b3fd785:58060      192.168.137.12:http     TIME_WAIT
    tcp        0      0 08674b3fd785:58062      192.168.137.12:http     TIME_WAIT
    [...]
    udp        0      0 08874b3cd785:35298      192.168.65.1:domain     ESTABLISHED
    

    I tried the 192.168.65.1 IP first, but that didn't work.
    The 192.168.137.12 is the IP of a Hyper-V Virtual Machine that the php script connects to. Apparently the php container can connect to that, so maybe then it could also connect to the Windows adapter that's bound to that virtual switch, in other words: 192.168.137.1 .

    Adding this to the xDebug settings solved the problem:
    xdebug.remote_host = 192.168.137.1.