Repository navigation
Fix exposed ports not being mapped on Docker Desktop - #1602
Draft
cristianrgreco wants to merge 3 commits into
Draft
cristianrgreco wants to merge 3 commits into
cristianrgreco wants to merge 3 commits into
Conversation
Request random host ports with an empty HostPort instead of "0". For "0" Docker Desktop opens the port number the engine picked inside its VM on the host as well, without checking that it is free there, and leaves the port unmapped when it is not. For an empty HostPort it picks the host port itself. Keep "0" when running inside a container, where the host is reached through the bridge gateway and the port must be the one the engine bound.
With an empty HostPort Docker Desktop drops the first datagram sent to a freshly mapped UDP port now and then, and Docker Desktop before 4.67.0 reported such a port as 0.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On Docker Desktop a container's first start can fail with
Timed out after 30000ms while waiting for container ports to be bound to the host, although the container is running.withExposedPortsasked for a random host port withHostPort: "0". For that value the engine inside the Desktop VM picks the port, and Desktop then listens on the same number on the host without checking that it is free there. When it is not, Desktop logsbind: address already in useand the port is never mapped: inspect keeps returning"8080/tcp": [].[::]:<port>from the same user (what Node'sserver.listen(port)creates), or any socket on that port owned by another user. On the machine this was found on, the holders were long-lived sockets of macOS daemons (remoted,apsd,rapportd).This PR sends
HostPort: ""instead. For an empty host port Desktop picks the host port itself, so there is no number that can clash. Docker described the difference in docker/for-mac#5588:"0"skips that code "by accident".Other implementations: Java sends
""(ContainerDef.java), and .NET sends""and rewrites"0"to it, citing the same Docker comment (ContainerBuilder.cs). Go sends"0"."0"is kept in two cases:""the port in inspect is the host's, and the VM listens on a different one.resolveHostconnects through the bridge gateway when/.dockerenvexists, and that getsECONNREFUSEDfor the host's port. This repository's dev container is such a setup.isInContainermoves to its own file so both places use it.""Docker Desktop 4.94.0 dropped the first datagram sent to a freshly mapped port in 5 of 300 containers started 50 at a time, against 0 of 300 with"0". Docker Desktop before 4.67.0 also reported such a port as 0 (fixed in its release notes for 4.67.0).This is a different defect from the stale port after a restart in #1599. Neither change fixes the other, and they combine.
Verification
On macOS arm64 with Docker Desktop 4.82.0 (engine 29.6.1) and 4.94.0 (engine 29.8.2).
npm run format,npm run lint,npm run knip,npm run check-compiles: clean.{ container: 8080, host: 0 }shows where the engine's counter is, the test listens on the next three port numbers, then starts a container withwithExposedPorts(8080)and checks that it answers.HostPort: "0", on both Desktop versions:Error: Timed out after 30000ms while waiting for container ports to be bound to the host, frominspectContainerUntilPortsExposedviaGenericContainer.startContainer.generic-container.test.ts: 52 passed, 1 skipped, in 3 of 3 runs.resolve-host,generic-container-reuse,generic-container-restart,port-forwarder,reaperandhost-port-wait-strategytests: 40 passed (7 files).Limits:
"0", so on Docker Desktop the test can hit the defect itself when the pilot's number is blocked on the host. That was about 0.3% of port numbers on the machine used."0"branch for a process inside a container has no test.Not breaking
""and"0"alike: both mean "allocate a port".withReuse()hashes the create options, so a reusable container created by an earlier version is not matched once and a new one is created.