libnetwork: ref-count service aliases for VIP DNS records#52236
Conversation
corhere
left a comment
There was a problem hiding this comment.
Great find! Though I am not confident the fix is quite so straightforward. For better or worse, there is a distinct service config for every container. So I don't think it would be correct to assume that the service config for the most recently created container is the canonical config for all containers referencing the same service ID. With this change as written, the old aliases would be deleted once the first task in the rolling update is started, while old tasks are still running that expect the old alias to still exist. New aliases should be added at the start of a rolling update and the old aliases deleted at the end. I'm afraid the aliases for a service may have to be ref-counted.
27e010b to
3045a0c
Compare
|
@corhere thanks for catching this — you were right. Force-pushed a rewrite that ref-counts aliases at the service level instead of treating any one task's alias list as canonical:
Verified the mid-flight case end-to-end: during a rolling update that removes an alias, the alias still resolves while at least one old task still references it, and only disappears once the last old task is gone — which is the property the previous version was missing. Re-requesting review. |
corhere
left a comment
There was a problem hiding this comment.
Thank you! At a glance this change looks sane but I'll need to trace deeper through libnetwork before I can be confident in the correctness of this PR.
There was a problem hiding this comment.
Pull request overview
This PR fixes stale VIP DNS records for Swarm service network aliases during rolling updates by ref-counting aliases so VIP alias records persist until the last task using an alias is gone.
Changes:
- Move VIP-alias DNS record management out of
addEndpointNameResolution/deleteEndpointNameResolutionand into service-binding lifecycle. - Introduce alias ref-counting (
service.aliasRefs) and per-backend alias tracking (lbBackend.aliases) to correctly add/remove VIP alias records on 0→1 / 1→0 transitions. - Update cleanup flow to pass backend-specific alias lists so
cleanupServiceBindingsdecrements the correct aliases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| daemon/libnetwork/service_common.go | Adds alias ref-counting logic, per-backend alias storage, and centralizes VIP-alias record add/remove in service binding paths. |
| daemon/libnetwork/service.go | Replaces service.aliases with service.aliasRefs and adds lbBackend.aliases to support accurate decrements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3045a0c to
c09bf96
Compare
|
@corhere Force-pushed: per-LB aliasRefs (per-network, not per-service), add bool helper inlined into the diff loops, your loop-shape and slices.Clone suggestions applied, and added TestServiceAliasRefCounting covering rolling-update persistence, per-network scoping, and same-eID rebind. |
634a509 to
c09bf96
Compare
corhere
left a comment
There was a problem hiding this comment.
I am satisfied that this change is logically sound. I just have some nits to pick with commentary and tests.
If you ever need to resolve merge conflicts or otherwise sync with the base branch of a PR, please rebase and force-push instead of merging the base branch into your PR branch. We want to keep a clean Git history.
| // Keyed per-network because Swarm services can attach to multiple | ||
| // networks with different alias sets. |
There was a problem hiding this comment.
What do you mean by "keyed per-network" here?
| // 2. Aliases are scoped per-network: configuring an alias on network A | ||
| // does not publish a VIP DNS record for it on network B, even though | ||
| // both networks belong to the same service. |
There was a problem hiding this comment.
Networks do not "belong to" services.
| // 2. Aliases are scoped per-network: configuring an alias on network A | |
| // does not publish a VIP DNS record for it on network B, even though | |
| // both networks belong to the same service. | |
| // 2. Aliases are scoped per-network: configuring an alias on network A | |
| // does not publish a VIP DNS record for it on network B, or vice | |
| // versa. |
| // Old task with alias "old". | ||
| assert.NilError(t, c.addServiceBinding(svcName, svcID, n1.ID(), "ep-old", "ctr-old", | ||
| vip1, nil, []string{"old"}, nil, net.ParseIP("172.20.0.10"), "test")) | ||
| assert.Check(t, is.Equal(true, resolves(t, n1, "old")), "alias 'old' should resolve after first task") |
There was a problem hiding this comment.
| assert.Check(t, is.Equal(true, resolves(t, n1, "old")), "alias 'old' should resolve after first task") | |
| assert.Check(t, resolves(t, n1, "old"), "alias 'old' should resolve after first task") |
It's shorter to type, and yields more helpful failure messages automatically. Save the Equal assertions for comparing non-boolean values.
During rolling updates, VIP DNS records for service aliases were never cleaned up when aliases were removed from the service spec. VIP alias records were only added when the first backend joined a network (addService=true) and only removed when the last backend left (rmService=true). During rolling updates there is always at least one backend, so neither condition triggered and stale aliases persisted. Each container has its own service config, so there is no single canonical alias list per service while a rolling update is in flight. Instead, ref-count aliases per (network, alias) on each loadBalancer: add the VIP DNS record on the 0->1 transition (first task on that network claiming the alias) and remove it on the 1->0 transition (last task referencing it has left). Ref counts are per-network because a Swarm service can attach to multiple networks with different alias sets, and a VIP DNS record only makes sense on the network that actually has the alias configured. Aliases now survive a rolling update for as long as any task -- old or new -- still references them on that network, and new aliases register as soon as the first task carrying them starts. Signed-off-by: Mads Jon Nielsen <madsjon@gmail.com>
c09bf96 to
1143f41
Compare
|
@corhere Addressed your nits and rebased onto current master (was previously sync'd via merge — apologies, won't do that again):
No code changes, just commentary and tests. Re-requesting review. |
corhere
left a comment
There was a problem hiding this comment.
LGTM! Thank you so much for fixing this bug!
Fixes #44009
- What I did
Fixed stale VIP DNS records for network aliases persisting after service updates in Docker Swarm.
- How I did it
Each container has its own service config, so there is no single canonical alias list per service while a rolling update is in flight. Aliases are now ref-counted at the service level (
service.aliasRefs):addServiceBindingincrements the ref count for each alias the task brings, and adds the VIP DNS record on a 0→1 transition.rmServiceBindingdecrements on full removal, and deletes the VIP DNS record on a 1→0 transition.lbBackend.aliases) so the decrement uses the task's actual claim, even fromcleanupServiceBindings.The VIP-alias add/remove paths in
addEndpointNameResolution/deleteEndpointNameResolutionare removed; the svcName VIP record stays where it is.addServiceBindingalso diffs against any prior aliases stored on the same endpoint, so a re-bind (disable→enable, or alias-set change for the same eID) only adjusts ref counts by the delta.The previous version of this PR removed stale aliases as soon as the first new task in a rolling update arrived, which would have broken old tasks still serving on the old alias. Ref-counting keeps the alias registered until the last task referencing it is gone.
- How to verify it
Verified end-to-end on a single-node swarm:
testaliastasks.testalias- Human readable description for the release notes