Proposal: Flag to persist dynamically allocated ports across restarts#10052
Proposal: Flag to persist dynamically allocated ports across restarts#10052cezarsa wants to merge 4 commits intomoby:masterfrom
Conversation
70242c8 to
753c674
Compare
|
Okay, I've found a bug in my proposed implementation, when the daemon is restarted, docker may try to use persisted ports in other containers, preventing container with persisted port to start correctly. This is also an issue if one calls run with This problem could be addressed by allocating ports for containers with explicit host ports in HostConfig first when starting the daemon. I'll try to add this to this PR. |
753c674 to
485552f
Compare
|
I updated the PR including a test case and a fix to the issue I described in my previous comment. The solution involves initializing the network of containers with explicit host ports before containers with dynamically allocated ports when starting the daemon. |
|
Docs LGTM |
|
Seems like instead of a flag we should just keep the port for the life of the container no matter what. |
|
@cpuguy83 If that's the way to go I can change this pull request (or create another one) removing the flag and making this the default behavior. Just let me know. |
|
I'll defer to the core maintainers on this, and @aluzzardi who was working on the "stable networking" stuff, which I think this would fall under. |
|
First of all, thank you for the contribution: it's a sensible feature being implemented in a sensible way! Now, without an extensive review: if I understand correctly, what the implementation does is take the That makes we wonder on two things (which may very well end up being dismissed as irrelevant):
|
|
You're right, after the container starts it's as if it was started with The inspect output is slightly different. With But I don't see that exactly as a lie. It's true that the host port is now part of the configuration as it will remain the same. About your last question, yes, the container would fail to start the same way it would if it was started with That said, some of the changes in this pull request regards preventing docker itself from using a port that was persisted, specially when the daemon starts and multiple containers have to be restarted. This prevents "port already allocated" errors from showing up unless some external process is using it. |
|
Indeed, Swarm should be aware of this change since it uses that information to know which ports are available in the cluster. Also, stable networking was dropped back then since it introduced a UX regression. I think that if we do this, we should also persist the IPs since it would be weird to keep the same ports but change the IP. |
|
@aluzzardi Is this the code in swarm in question: https://github.com/docker/swarm/blob/master/scheduler/filter/port.go#L34? Containers started with However, just from reading the code (I haven't tried it), I can see that there's a possible bug that may already happen today. |
|
@cezarsa Thank you very much for looking into it! Would you mind opening an issue on Swarm regarding the bug you found? |
There was a problem hiding this comment.
Just a suggestion, but we could simplify/clarify this a little with:
"Allow dynamically allocated, published ports to persist after restarting a container.
|
Docs LGTM. You can take or leave my suggestion. |
485552f to
c1f4b62
Compare
This commit introduces the PersistPorts flag in HostConfig, and the --persist-ports parameter for `docker create` and `docker run`. This flag will make dynamically allocated ports to be persisted across container restarts by saving it after the first time it's allocated. Signed-off-by: Cezar Sa Espinola <cezarsa@gmail.com>
Signed-off-by: Cezar Sa Espinola <cezarsa@gmail.com>
Signed-off-by: Cezar Sa Espinola <cezarsa@gmail.com>
Containers with explicit host ports in hostConfig now have their network initialized before containers with dynamic port allocation. This allows containers to use explicit host ports in the dynamic range without the risk of conflicts. Signed-off-by: Cezar Sa Espinola <cezarsa@gmail.com>
c1f4b62 to
46f3465
Compare
|
Has merge conflicts. |
|
I wonder whether this should be the default or not, that way no need for another flag. /cc @crosbymichael @aluzzardi |
|
I'll reiterate what I suggest: I think we should not add a new flag, and simply allocate ports on create, and free ports on removal by default. That way ports will be persistent. The only bad thing is that if you have a huge ton of stopped containers, then you have less available ports, but it should be fine in practice. |
|
@tiborvass I agree with you on that (attaching ports lifecycle to create/remove). It might break some existing feature and/or design principle, though. Featuring ports persistence would allow me to get rid of same logic in docker-fw; creation should indeed fail if those persisted ports are not available. |
|
@tiborvass It seems a little odd to reserve a port that is not actually in use by a running container to be considered "allocated". Per @icecrime's point, it seems problematic that every container launched on a daemon with The main problem here is that allocating something with an "ephemeral port" says two things:
Locking down this seems to make these no longer ephemeral port allocations, since it breaks both of the above invariants. I do understand the benefit here (of eliminating SPOF for bind notification), but couldn't that be done by managing the port allocation externally and then issue container creation/restart commands with |
|
@stevvooe Interesting point you're making there :) Made me analyze a little more why I was thinking keeping the port on stop made sense. You're saying that the ports are supposed to be ephemeral. I agree. They would still be, at each container creation. Which means the creator of the container can never know what port it will get, and that's good. The container's fs is ephemeral as well, but it's gone only on removal, not on stop. The argument for keeping the port in memory for stopped containers, is that a stopped container is still the same container, and likely to be restarted. I guess the main question is: should ports "live" and "die" on their own as they are used (like, on another scale, containers), or are ports part of the state of a container? Beyond the philosophical debate, what's the use-case for not having the same port after a restart? Or, what would be the usecase that we would kill, by persisting the ports? |
|
@tiborvass Your analysis of my response is accurate.
This is a key question. IMHO, we kill the use case of ephemeral ports. The main issue here is the behavior upon restart of a container. Does docker take measures to ensure that the ephemeral port is always ready for use? Or, does it re-allocate a new port on a collision, guaranteeing the ports are actually ephemeral? If we choose the latter, it makes keeping the port mapping around an optimization to avoid sending port updates to consuming services. Two things follow from this:
I am not sure if this proposal follows these two items, but they should be considered. |
|
Collective review with @LK4D4 @calavera @jfrazelle @crosbymichael @tiborvass The debate around this PR is that you'll always be left having to manage something yourself:
If we proceed with this, stopped containers will eventually fill the automatic port allocation pool and make it impossible to run any new containers. We can't really force people to garbage collect their containers (although it's good practice), so this comes with new risks too. Sorry, but we're gonna close this. Thanks again for the contribution though! |
|
Docker already handles automatic port allocation, so why should an external system have to do the same thing unnecessarily. I think there could be a middle ground if the user provides a port range from which an automatic selection can be made to persist (and a recommendation that it be outside of the default ephemeral range). e.g. If this custom range is provided, the container would be started as if with Thoughts? |
This PR adds the
PersistPortsflag to HostConfig which allows Docker server to save dynamically allocated published ports inside HostConfig after the allocation takes place.This way, if the container is restarted or goes through a stop/start, Docker will bind the same host port.
This is quite useful for services that start docker containers with dynamically allocated ports as they will only need to verify which port was allocated once, when the container started for the first time.
Currently this kind of service would either have to manually manage host ports (replicating some work that Docker already does) or have some kind of mechanism that is notified every time the container is restarted so that port mappings can be updated.
We take the latter approach in tsuru PaaS, and this PR hopes to eliminate our need to do that, as relying on an external agent to be notified and update port mappings creates yet another point of failure.
This flag is exposed to the CLI as the
--persist-portsparameter available tocreateandrun.