Skip to content

Commit d8b5e28

Browse files
authored
Merge pull request #10985 from prezha/multinode-cni-dns
Fix CNI issue related to picking up wrong CNI
2 parents a044732 + d69e24d commit d8b5e28

File tree

9 files changed

+100
-60
lines changed

9 files changed

+100
-60
lines changed

cmd/minikube/cmd/start_flags.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,9 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
392392
if _, ok := cnm.(cni.Disabled); !ok {
393393
klog.Infof("Found %q CNI - setting NetworkPlugin=cni", cnm)
394394
cc.KubernetesConfig.NetworkPlugin = "cni"
395+
if err := setCNIConfDir(&cc, cnm); err != nil {
396+
klog.Errorf("unable to set CNI Config Directory: %v", err)
397+
}
395398
}
396399
}
397400

@@ -415,6 +418,24 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
415418
return createNode(cc, kubeNodeName, existing)
416419
}
417420

421+
// setCNIConfDir sets kubelet's '--cni-conf-dir' flag to custom CNI Config Directory path (same used also by CNI Deployment) to avoid conflicting CNI configs.
422+
// ref: https://github.com/kubernetes/minikube/issues/10984
423+
// Note: currently, this change affects only Kindnet CNI (and all multinodes using it), but it can be easily expanded to other/all CNIs if needed.
424+
func setCNIConfDir(cc *config.ClusterConfig, cnm cni.Manager) error {
425+
if _, kindnet := cnm.(cni.KindNet); kindnet {
426+
// auto-set custom CNI Config Directory, if not user-specified
427+
eo := fmt.Sprintf("kubelet.cni-conf-dir=%s", cni.CustomCNIConfDir)
428+
if !cc.KubernetesConfig.ExtraOptions.Exists(eo) {
429+
klog.Infof("auto-setting extra-config to %q", eo)
430+
if err := cc.KubernetesConfig.ExtraOptions.Set(eo); err != nil {
431+
return fmt.Errorf("failed auto-setting extra-config %q: %v", eo, err)
432+
}
433+
klog.Infof("extra-config set to %q", eo)
434+
}
435+
}
436+
return nil
437+
}
438+
418439
func checkNumaCount(k8sVersion string) {
419440
if viper.GetInt(kvmNUMACount) < 1 || viper.GetInt(kvmNUMACount) > 8 {
420441
exit.Message(reason.Usage, "--kvm-numa-count range is 1-8")

pkg/drivers/kvm/domain.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ const domainTmpl = `
6565
<target dev='hda' bus='virtio'/>
6666
</disk>
6767
<interface type='network'>
68-
<source network='{{.Network}}'/>
68+
<source network='{{.PrivateNetwork}}'/>
6969
<model type='virtio'/>
7070
</interface>
7171
<interface type='network'>
72-
<source network='{{.PrivateNetwork}}'/>
72+
<source network='{{.Network}}'/>
7373
<model type='virtio'/>
7474
</interface>
7575
<serial type='pty'>

pkg/minikube/cni/cni.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ const (
3939
DefaultPodCIDR = "10.244.0.0/16"
4040
)
4141

42+
var (
43+
// CustomCNIConfDir is the custom CNI Config Directory path used to avoid conflicting CNI configs
44+
// ref: https://github.com/kubernetes/minikube/issues/10984
45+
CustomCNIConfDir = "/etc/cni/net.mk"
46+
)
47+
4248
// Runner is the subset of command.Runner this package consumes
4349
type Runner interface {
4450
RunCmd(cmd *exec.Cmd) (*command.RunResult, error)
@@ -62,6 +68,7 @@ type tmplInput struct {
6268
ImageName string
6369
PodCIDR string
6470
DefaultRoute string
71+
CNIConfDir string
6572
}
6673

6774
// New returns a new CNI manager
@@ -73,6 +80,12 @@ func New(cc config.ClusterConfig) (Manager, error) {
7380

7481
klog.Infof("Creating CNI manager for %q", cc.KubernetesConfig.CNI)
7582

83+
// respect user-specified custom CNI Config Directory, if any
84+
userCNIConfDir := cc.KubernetesConfig.ExtraOptions.Get("cni-conf-dir", "kubelet")
85+
if userCNIConfDir != "" {
86+
CustomCNIConfDir = userCNIConfDir
87+
}
88+
7689
switch cc.KubernetesConfig.CNI {
7790
case "", "auto":
7891
return chooseDefault(cc), nil

pkg/minikube/cni/kindnet.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ spec:
130130
volumes:
131131
- name: cni-cfg
132132
hostPath:
133-
path: /etc/cni/net.d
133+
path: {{.CNIConfDir}}
134+
type: DirectoryOrCreate
134135
- name: xtables-lock
135136
hostPath:
136137
path: /run/xtables.lock
@@ -158,6 +159,7 @@ func (c KindNet) manifest() (assets.CopyableFile, error) {
158159
DefaultRoute: "0.0.0.0/0", // assumes IPv4
159160
PodCIDR: DefaultPodCIDR,
160161
ImageName: images.KindNet(c.cc.KubernetesConfig.ImageRepository),
162+
CNIConfDir: CustomCNIConfDir,
161163
}
162164

163165
b := bytes.Buffer{}

site/content/en/docs/commands/start.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ minikube start [flags]
111111
-h, --help
112112
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
113113
--log_dir string If non-empty, write log files in this directory
114-
--log_file string If non-empty, use this log file (default "")
114+
--log_file string If non-empty, use this log file
115115
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
116116
--logtostderr log to standard error instead of files
117117
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level)

site/content/en/docs/tutorials/includes/hello-deployment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ spec:
2222
podAntiAffinity:
2323
requiredDuringSchedulingIgnoredDuringExecution:
2424
- labelSelector:
25-
matchExpressions: [{ key: app, operator: In, values: [hello-from] }]
25+
matchExpressions: [{ key: app, operator: In, values: [hello] }]
2626
topologyKey: "kubernetes.io/hostname"
2727
containers:
2828
- name: hello-from

site/content/en/docs/tutorials/multi_node.md

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,24 @@ date: 2019-11-24
2222
minikube start --nodes 2 -p multinode-demo
2323
```
2424
```
25-
😄 [multinode-demo] minikube v1.16.0 on Darwin 10.15.7
26-
✨ Automatically selected the docker driver. Other choices: hyperkit, virtualbox
25+
😄 [multinode-demo] minikube v1.18.1 on Opensuse-Tumbleweed
26+
✨ Automatically selected the docker driver
2727
👍 Starting control plane node multinode-demo in cluster multinode-demo
28-
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
29-
🐳 Preparing Kubernetes v1.20.0 on Docker 20.10.0 ...
30-
🔗 Configuring CNI (Container Networking Interface) ...
28+
🔥 Creating docker container (CPUs=2, Memory=8000MB) ...
29+
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.3 ...
3130
▪ Generating certificates and keys ...
3231
▪ Booting up control plane ...
3332
▪ Configuring RBAC rules ...
33+
🔗 Configuring CNI (Container Networking Interface) ...
3434
🔎 Verifying Kubernetes components...
35+
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
3536
🌟 Enabled addons: storage-provisioner, default-storageclass
3637
3738
👍 Starting node multinode-demo-m02 in cluster multinode-demo
38-
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
39+
🔥 Creating docker container (CPUs=2, Memory=8000MB) ...
3940
🌐 Found network options:
4041
▪ NO_PROXY=192.168.49.2
41-
🐳 Preparing Kubernetes v1.20.0 on Docker 20.10.0 ...
42+
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.3 ...
4243
▪ env NO_PROXY=192.168.49.2
4344
🔎 Verifying Kubernetes components...
4445
🏄 Done! kubectl is now configured to use "multinode-demo" cluster and "default" namespace by default
@@ -50,9 +51,9 @@ minikube start --nodes 2 -p multinode-demo
5051
kubectl get nodes
5152
```
5253
```
53-
NAME STATUS ROLES AGE VERSION
54-
multinode-demo Ready master 72s v1.18.2
55-
multinode-demo-m02 Ready <none> 33s v1.18.2
54+
NAME STATUS ROLES AGE VERSION
55+
multinode-demo Ready control-plane,master 99s v1.20.2
56+
multinode-demo-m02 Ready <none> 73s v1.20.2
5657
```
5758

5859
- You can also check the status of your nodes:
@@ -68,7 +69,6 @@ host: Running
6869
kubelet: Running
6970
apiserver: Running
7071
kubeconfig: Configured
71-
timeToStop: Nonexistent
7272
7373
multinode-demo-m02
7474
type: Worker
@@ -106,9 +106,9 @@ service/hello created
106106
kubectl get pods -o wide
107107
```
108108
```
109-
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
110-
hello-c7b8df44f-qbhxh 1/1 Running 0 31s 10.244.0.3 multinode-demo <none> <none>
111-
hello-c7b8df44f-xv4v6 1/1 Running 0 31s 10.244.0.2 multinode-demo <none> <none>
109+
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
110+
hello-695c67cf9c-bzrzk 1/1 Running 0 22s 10.244.1.2 multinode-demo-m02 <none> <none>
111+
hello-695c67cf9c-frcvw 1/1 Running 0 22s 10.244.0.3 multinode-demo <none> <none>
112112
```
113113

114114
- Look at our service, to know what URL to hit
@@ -117,31 +117,31 @@ hello-c7b8df44f-xv4v6 1/1 Running 0 31s 10.244.0.2 multinod
117117
minikube service list -p multinode-demo
118118
```
119119
```
120-
|-------------|------------|--------------|-----------------------------|
121-
| NAMESPACE | NAME | TARGET PORT | URL |
122-
|-------------|------------|--------------|-----------------------------|
123-
| default | hello | 80 | http://192.168.64.226:31000 |
124-
| default | kubernetes | No node port | |
125-
| kube-system | kube-dns | No node port | |
126-
|-------------|------------|--------------|-----------------------------|
120+
|-------------|------------|--------------|---------------------------|
121+
| NAMESPACE | NAME | TARGET PORT | URL |
122+
|-------------|------------|--------------|---------------------------|
123+
| default | hello | 80 | http://192.168.49.2:31000 |
124+
| default | kubernetes | No node port | |
125+
| kube-system | kube-dns | No node port | |
126+
|-------------|------------|--------------|---------------------------|
127127
```
128128

129129
- Let's hit the URL a few times and see what comes back
130130

131131
```shell
132-
curl http://192.168.64.226:31000
132+
curl http://192.168.49.2:31000
133133
```
134134
```
135-
Hello from hello-c7b8df44f-qbhxh (10.244.0.3)
135+
Hello from hello-695c67cf9c-frcvw (10.244.0.3)
136136
137-
curl http://192.168.64.226:31000
138-
Hello from hello-c7b8df44f-qbhxh (10.244.0.3)
137+
curl http://192.168.49.2:31000
138+
Hello from hello-695c67cf9c-bzrzk (10.244.1.2)
139139
140-
curl http://192.168.64.226:31000
141-
Hello from hello-c7b8df44f-xv4v6 (10.244.0.2)
140+
curl http://192.168.49.2:31000
141+
Hello from hello-695c67cf9c-bzrzk (10.244.1.2)
142142
143-
curl http://192.168.64.226:31000
144-
Hello from hello-c7b8df44f-xv4v6 (10.244.0.2)
143+
curl http://192.168.49.2:31000
144+
Hello from hello-695c67cf9c-frcvw (10.244.0.3)
145145
```
146146

147147
- Multiple nodes!

test/integration/multinode_test.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,12 @@ func validateDeployAppToMultiNode(ctx context.Context, t *testing.T, profile str
393393
// Create a deployment for app
394394
_, err := Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "apply", "-f", "./testdata/multinodes/multinode-pod-dns-test.yaml"))
395395
if err != nil {
396-
t.Errorf("failed to create hello deployment to multinode cluster")
396+
t.Errorf("failed to create busybox deployment to multinode cluster")
397397
}
398398

399-
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "rollout", "status", "deployment/hello"))
399+
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "rollout", "status", "deployment/busybox"))
400400
if err != nil {
401-
t.Errorf("failed to delploy hello to multinode cluster")
401+
t.Errorf("failed to delploy busybox to multinode cluster")
402402
}
403403

404404
// resolve Pod IPs
@@ -423,24 +423,25 @@ func validateDeployAppToMultiNode(ctx context.Context, t *testing.T, profile str
423423

424424
// verify both Pods could resolve a public DNS
425425
for _, name := range podNames {
426-
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "exec", name, "nslookup", "kubernetes.io"))
426+
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "exec", name, "--", "nslookup", "kubernetes.io"))
427427
if err != nil {
428428
t.Errorf("Pod %s could not resolve 'kubernetes.io': %v", name, err)
429429
}
430430
}
431-
// verify both pods could resolve to a local service.
431+
// verify both Pods could resolve "kubernetes.default"
432+
// this one is also checked by k8s e2e node conformance tests:
433+
// https://github.com/kubernetes/kubernetes/blob/f137c4777095b3972e2dd71a01365d47be459389/test/e2e_node/environment/conformance.go#L125-L179
432434
for _, name := range podNames {
433-
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "exec", name, "nslookup", "kubernetes.default.svc.cluster.local"))
435+
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "exec", name, "--", "nslookup", "kubernetes.default"))
434436
if err != nil {
435-
t.Errorf("Pod %s could not resolve local service (kubernetes.default.svc.cluster.local): %v", name, err)
437+
t.Errorf("Pod %s could not resolve 'kubernetes.default': %v", name, err)
436438
}
437439
}
438-
439-
// clean up, delete all pods
440+
// verify both pods could resolve to a local service.
440441
for _, name := range podNames {
441-
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "delete", "pod", name))
442+
_, err = Run(t, exec.CommandContext(ctx, Target(), "kubectl", "-p", profile, "--", "exec", name, "--", "nslookup", "kubernetes.default.svc.cluster.local"))
442443
if err != nil {
443-
t.Errorf("fail to delete pod %s: %v", name, err)
444+
t.Errorf("Pod %s could not resolve local service (kubernetes.default.svc.cluster.local): %v", name, err)
444445
}
445446
}
446447
}

test/integration/testdata/multinodes/multinode-pod-dns-test.yaml

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
apiVersion: apps/v1
22
kind: Deployment
33
metadata:
4-
name: hello
4+
name: busybox
5+
labels:
6+
app: busybox
57
spec:
68
replicas: 2
7-
strategy:
8-
type: RollingUpdate
9-
rollingUpdate:
10-
maxUnavailable: 100%
119
selector:
1210
matchLabels:
13-
app: hello
11+
app: busybox
1412
template:
1513
metadata:
1614
labels:
17-
app: hello
15+
app: busybox
1816
spec:
17+
containers:
18+
- name: busybox
19+
# flaky nslookup in busybox versions newer than 1.28:
20+
# https://github.com/docker-library/busybox/issues/48
21+
# note: gcr.io/kubernetes-e2e-test-images/dnsutils:1.3
22+
# has similar issues (ie, resolves but returns exit 1)
23+
image: busybox:1.28
24+
command:
25+
- sleep
26+
- "3600"
27+
imagePullPolicy: IfNotPresent
28+
restartPolicy: Always
1929
affinity:
2030
# ⬇⬇⬇ This ensures pods will land on separate hosts
2131
podAntiAffinity:
2232
requiredDuringSchedulingIgnoredDuringExecution:
2333
- labelSelector:
24-
matchExpressions: [{ key: app, operator: In, values: [hello-from] }]
34+
matchExpressions: [{ key: app, operator: In, values: [busybox] }]
2535
topologyKey: "kubernetes.io/hostname"
26-
containers:
27-
- name: hello-from
28-
image: pbitty/hello-from:latest
29-
ports:
30-
- name: http
31-
containerPort: 80
32-
terminationGracePeriodSeconds: 1

0 commit comments

Comments
 (0)