How to connect to a web application inside a container in a Virtual Machine, from outside it?

1.9k Views Asked by At

I have a setup that is:

PC > Fedora(Hyper-V VM) > Container running web application

The problem that i am facing is that i can not connect to the web app from my PC ( PC > Container web app)

When the service starts, it show that it is listening on http://0.0.0.0:8000

[2021-05-27 22:29:57 +0000] [23] [INFO] Starting gunicorn 20.1.0
[2021-05-27 22:29:57 +0000] [23] [INFO] Listening at: http://0.0.0.0:8000 (23)
[2021-05-27 22:29:57 +0000] [23] [INFO] Using worker: sync
[2021-05-27 22:29:57 +0000] [25] [INFO] Booting worker with pid: 25
[2021-05-27 22:29:57 +0000] [26] [INFO] Booting worker with pid: 26
[2021-05-27 22:29:57 +0000] [27] [INFO] Booting worker with pid: 27

Trying to connect to http://0.0.0.0:8000 do not work, rather that, I use the container IP:8000 to connect e.g.: 172.17.0.2:8000

Doing this on my VM works fine, i can connect and use the application, but it does not work on my PC. I tried to connect using the VM ip, the 0.0.0.0 and the container IP too, like in the VM. But none of this options works.

i tried to forward the ports using iptables, but i don't know how to use it exactly. I tried googleing some commands and using that but didn't get to the solution.

Any help? Maybe it would be related to another thing that i am not seeing

2

There are 2 best solutions below

0
On

There are several ways to set VM, host, NAT or bridge.

Reference: https://superuser.com/a/227508/282690

Host-only only permits network operations with the Host OS.

NAT mode will mask all network activity as if it came from your Host OS, although the VM can access external resources.

Bridged mode replicates another node on the physical network and your VM will receive it's own IP address if DHCP is enabled in the network.

so if you set your VM as bridge mode, it will get its own IP address, in same network as your host PC.

with this way, you can connect any ports between them, no extra setup.

If you go with host mode, you need redirect/forward ports. Use Vagrant as sample:

Vagrant.configure("2") do |config|
  config.vm.network "forwarded_port", guest: 80, host: 8080
  # ... for more ports if need
  # ... for more ports if need
end
2
On

You have several layers here, and each has its own set of IP addresses.

+------------------------+
| Host +---------------+ |
|      | VM +--------+ | |
|      |    | Docker | | |
|      |    +--------+ | |
|      +---------------+ |
+------------------------+

The important detail is that each layer can only see one layer further in. If you're outside this stack in another machine on the network, you can't directly access the VM's IP address; if you're on the host, you can't access the container-private IP addresses.

If you use the docker run -p option or Compose ports: option, though, it will cause the layer running the Docker daemon to open up a network listener that forwards to the container. In this setup, since Docker is running as a VM, docker run -p forwards a port from the VM to the container.

For example, say you start the container, inside the VM, with a -p 12345:80 option. If, from the host, you connect to the VM's IP address on port 12345, that will forward to port 80 in the container.

vm$ docker run -d -p 12345:80 my-image
host$ curl http://vm:12345
+---------------------------------------------------+
| Host                +---------------------------+ |
|                     | VM          +-----------+ | |
|                     |             | Docker    | | |
|                     |             |           | | |
|                     | docker run  | ./app     | | |
| http://vm:12345 --> | -p 12345:80 | -addr :80 | | |
|                     |             +-----------+ | |
|                     +---------------------------+ |
+---------------------------------------------------+

This same setup is used in Docker Desktop, though the VM is fairly hidden, but that's why the container-private IP addresses don't work on MacOS or Windows hosts. Similarly, without the VM on a native-Linux host, you can't reach the container-private IP from a different host.

Since you can always reach the container using published ports from outside Docker, or using container networking from inside Docker, you never need to use docker inspect to find a container's IP address.