Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix ambiguous networks #1831

Merged
merged 9 commits into from
Sep 14, 2020

Conversation

BenTheElder
Copy link
Member

@BenTheElder BenTheElder commented Sep 1, 2020

fixes #1596

Rough algorithm:

  • check if network exists if it does, end here
  • create network
  • check if there are now > 1 networks
  • sort networks based on:
    • # of attached containers (available when inspecting the network)
    • CreatedAt (creation timestamp, standard docker object metadata)
    • Id comparison (dockerd assigns UIDs as the primary identifier centrally)
  • delete N-1 networks, keeping the one that sorts first

This ensures that we will remove the ambiguous extra networks when we have two kind create cluster "win the race" of docker network create and simultaneously create identically named networks within the timeframe that docker doesn't catch that the name already exists during the internal non-atomic check for an existing network by the name.

This should be safe because we deterministically sort based on "is this network being used somehow" then "when was this network created" and finally if those are identical on the UID docker assigns them.

TODO:

  • I need to rework the network list / sort logic to list then inspect and sort by # of attached containers, currently it only sorts by ID and CreatedAt
  • [ ] There's still perhaps a race when doing the cleanup, where process A and B win the race, A wins it faster and somehow continues on to creating containers attached to this while B is going to cleanup. I'm not sure if this can actually happen and it's difficult to reproduce, but if it did we might get an error during container creation, in which case we should retry because the cleanup is coming. I think this isn't feasible as in all my experiments when I pull out the network logic and "win the race" by running it simultaneously in two goroutines I see that the following list check observes both networks and handles it, but this is probably still worth adding as a mitigation

EDIT: The second case should not happen now. If somehow I'm wrong it will be a simple mitigation in the future though and this already provides a significant improvement (complete?) in avoiding #1596.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Sep 1, 2020
@k8s-ci-robot k8s-ci-robot added the area/provider/docker Issues or PRs related to docker label Sep 1, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: BenTheElder

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Sep 1, 2020

// deterministically sort networks
// NOTE: THIS PART IS IMPORTANT!
// TODO(fixme): we should be sorting on active usage first!
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this TODO is the one thing keeping this WIP.
I will come back and rework this later. the overall code will be more or less the same otherwise.

@aojea
Copy link
Contributor

aojea commented Sep 1, 2020

let's verify it

diff --git a/pkg/cluster/internal/providers/docker/network_test.go b/pkg/cluster/internal/providers/docker/network_test.go
index 789a0d93..a5fb1d2b 100644
--- a/pkg/cluster/internal/providers/docker/network_test.go
+++ b/pkg/cluster/internal/providers/docker/network_test.go
@@ -19,8 +19,46 @@ package docker
 import (
        "fmt"
        "testing"
+
+       "sigs.k8s.io/kind/pkg/exec"
 )
 
+func TestEnsureNetworkConcurrent(t *testing.T) {
+       defer func() {
+               cmd := exec.Command(
+                       "docker", "network", "rm", "test-kind",
+               )
+               cmd.Run()
+       }()
+
+       // Create multiple networks concurrenctly
+       errCh := make(chan error, 3)
+       for i := 0; i < 3; i++ {
+               go func() {
+                       errCh <- ensureNetwork("test-kind")
+               }()
+       }
+       for i := 0; i < 3; i++ {
+               if err := <-errCh; err != nil {
+                       t.Errorf("error creating network: %v", err)
+               }
+       }
+
+       cmd := exec.Command(
+               "docker", "network", "ls",
+               "--filter=name=^test-kind$",
+               "--format={{.Name}}",
+       )
+
+       lines, err := exec.OutputLines(cmd)
+       if err != nil {
+               t.Errorf("obtaining the docker networks")
+       }
+       if len(lines) != 1 {
+               t.Errorf("wrong number of networks created: %d", len(lines))
+       }
+}
+
 func Test_generateULASubnetFromName(t *testing.T) {
        t.Parallel()
        cases := []struct {

@aojea
Copy link
Contributor

aojea commented Sep 1, 2020

maybe the name has not to be unique but the subnet should be

=== RUN   TestEnsureNetworkConcurrent
    TestEnsureNetworkConcurrent: /home/aojea/go/src/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network_test.go:43: error creating network: command "docker network create -d=bridge -o com.docker.network.bridge.enable_ip_masquerade=true --ipv6 --subnet fc00:3587:1532:ea5a::/64 test-kind" failed with error: exit status 1
    TestEnsureNetworkConcurrent: /home/aojea/go/src/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network_test.go:43: error creating network: command "docker network create -d=bridge -o com.docker.network.bridge.enable_ip_masquerade=true --ipv6 --subnet fc00:3587:1532:ea5a::/64 test-kind" failed with error: exit status 1
--- FAIL: TestEnsureNetworkConcurrent (0.36s)
FAIL
coverage: 15.7% of statements
exit status 1

@BenTheElder
Copy link
Member Author

BenTheElder commented Sep 1, 2020

#1831 (comment)

yeah, so AFAICT this only happens when we use the default IPAM (AKA the no-IPv6 case).

EDIT: ignore previous note here. see integration test discussion below.

@aojea
Copy link
Contributor

aojea commented Sep 1, 2020

I'm not overly inclined to add unit tests that depend on docker / aren't self-contained just yet, we probably want to consider having an integration test pattern similar to kubernetes vs our unit and e2e testing.

not asking to include it , just to run it before merge to be sure, it is not working for me ... I couldn't spend much time though

@BenTheElder BenTheElder mentioned this pull request Sep 1, 2020
@BenTheElder
Copy link
Member Author

picking this back up now.

@BenTheElder
Copy link
Member Author

testing with go test -count=40 -run ^TestIntegrationEnsureNetworkConcurrent -v ./pkg/cluster/internal/providers/docker is cropping up the odd occasion that no network exists when all the ensure calls are done. I haven't figured out that race yet.

@BenTheElder
Copy link
Member Author

I think it may have been in the timestamp parse/comparison. Since dropping that I've not had any failures in 160 iterations.

)

func TestIntegrationEnsureNetworkConcurrent(t *testing.T) {
Copy link
Member Author

@BenTheElder BenTheElder Sep 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can consider reworking the make targets based on this pattern in a follow-up.

to run only "unit": go test -short ...
to run only "integration": go test -run ^TestIntegration ...

@BenTheElder
Copy link
Member Author

/retest
CI flakes from being on the legacy build cluster. We should talk to CI signal about moving KIND testing over with the other jobs setting resource limits.

@BenTheElder
Copy link
Member Author

/retitle fix ambiguous networks

@k8s-ci-robot k8s-ci-robot changed the title WIP: fix ambiguous networks fix ambiguous networks Sep 11, 2020
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 11, 2020
Copy link
Contributor

@amwat amwat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! This should mitigate the main ambiguous docker network issue 👍
The other remaining possibilities of race conditions seem like weird corner cases which we'll most likely not run into in practice.

pkg/cluster/internal/providers/docker/network.go Outdated Show resolved Hide resolved
@aojea
Copy link
Contributor

aojea commented Sep 11, 2020

SGTM
just suggesting to squash some of the first commits so we can have a safe revert if necessary, not criticizing the PR, just the opposite, I think that this is a big improvement that tries to solve a well known docker broken behavior

@BenTheElder
Copy link
Member Author

currently refactoring to make #1831 (comment) a bit cleaner to address.

initially I didn't want to expose the inspect details but I think it's worth it to break these up and make them more testable, upon reflection.

@BenTheElder
Copy link
Member Author

/retest

@k8s-ci-robot
Copy link
Contributor

@BenTheElder: The following tests failed, say /retest to rerun all failed tests:

Test name Commit Details Rerun command
pull-kind-conformance-parallel-ga-only 0fc4229 link /test pull-kind-conformance-parallel-ga-only
pull-kind-conformance-parallel-1-15 0fc4229 link /test pull-kind-conformance-parallel-1-15

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

Copy link
Contributor

@amwat amwat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Sep 14, 2020
@BenTheElder
Copy link
Member Author

I profiled this before the last commit and improved the performance slightly, the happy path is just as cheap as before now

We're going to need to do something about those two CI jobs, but they're unrelated to this PR.

@BenTheElder BenTheElder merged commit 295318a into kubernetes-sigs:master Sep 14, 2020
@BenTheElder BenTheElder deleted the no-dupe-networks branch September 14, 2020 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/provider/docker Issues or PRs related to docker cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

network kind is ambiguous
4 participants