I though it is easy to expose my docker service hosted in my local Windows PC for public access:
- Use docker-compose or docker run -p 9999:8080 ... for my docker image,
- Open port 999 in firewall
- Access by: http://public_ip:9999
But this had taken me couple of hours, searching on web, ask ChatGPT ...
Verify:
By ping with my public_ip: ✅
By curl http://localhost:9999: ✅
By curl http://127.0.0.1:9999: ✅
By telnet public_ip 9999: keep fail ❌
By curl http://public_ip:999: keep fail ❌
Tried again and again ...
Fuck it, I want to give up 😅. Network stuff are so magic, so hard, I am so stupid ...
Few hours later ...
Found out this stackoverflow issue: https://stackoverflow.com/questions/43769806/docker-desktop-for-windows-cannot-access-service-on-exposed-port-in-windows-con
Turns out to be a limitation, and the solution is map the port from Windows Host to WSL.
$wsl_ip = (wsl -d "docker-desktop" -- "ifconfig" "eth0" "|" "grep" "inet addr:").trim("").split(":").split()[2]
netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$wsl_ip
And later
Found a WSL experimental feature hostAddressLoopback.
Only applicable when wsl2.networkingMode is set to mirrored. When set to True, will allow the Container to connect to the Host, or the Host to connect to the Container, by an IP address that's assigned to the Host. The 127.0.0.1 loopback address can always be used,this option allows for all additionally assigned local IP addresses to be used as well. Only IPv4 addresses assigned to the host are supported
So, finally here is the step:
Put the .wslconfig under C:\Users\Your_UserName.wslconfig
# https://learn.microsoft.com/en-us/windows/wsl/wsl-config [wsl2] networkingMode=mirrored [experimental] hostAddressLoopback=true
wsl --shutdown
wsl --list --running: wait for all shutdown
Restart docker desktop
Finally!!!
Update on 2024-12-29
A nice video about docker network and let me learned a lot:
It turns out that previously, I tried to use .wslconfig to set networkingMode to mirrored and it is not what I epected. After a clean and retry, I found it does not work at all.
I fallback the old way to use netsh interface portproxy add v4tov4 and my docker compose does not need to config any network related stuff at all. The port mapping also works as expected. And it is simplier and easier for me to understand this DevOps stuff.
F# script for auto:
let publishPorts =
stage "publish-ports" {
whenCmdArg "--ports" "" "This require admin permission. To bind the local port on Windows to the port on WSL." true
run (fun ctx -> asyncResult {
let! result = ctx.RunCommandCaptureOutput("""powershell -Command wsl -d "docker-desktop" -- "ifconfig" "eth0" """)
let targetIP = result.Split("\n")[1] |> fun x -> x.Split(":")[1] |> fun x -> x.Split(" ")[0]
AnsiConsole.MarkupLine ""
AnsiConsole.MarkupLine $"IP address of the Linux virtual machine: [green]{targetIP}[/]"
AnsiConsole.MarkupLine ""
let ports = ctx.TryGetCmdArg("--ports") |> ValueOption.defaultValue "9999" |> fun x -> x.Split(",")
for port in ports do
do!
ctx.RunCommand(
$"netsh interface portproxy add v4tov4 listenport={port} listenaddress=0.0.0.0 connectport={port} connectaddress={targetIP}"
)
})
run "netsh interface portproxy show v4tov4"
}