Skip to content

Commit 1efed43

Browse files
committed
add ip_pref CNI options for primary pod ip
This fixes the TODO of this function and also expands on how the primary pod ip is selected. This change allows the operator to prefer ipv4, ipv6, or retain the ordering provided by the return results of the CNI plugins. This makes it much more flexible for ops to configure containerd and how IPs are set on the pod. Signed-off-by: Michael Crosby <[email protected]>
1 parent a4d64e5 commit 1efed43

4 files changed

Lines changed: 67 additions & 21 deletions

File tree

docs/cri/config.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ version = 2
238238
# This will be deprecated when kubenet is deprecated.
239239
# See the "CNI Config Template" section for more details.
240240
conf_template = ""
241+
# ip_pref specifies the strategy to use when selecting the main IP address for a pod.
242+
# options include:
243+
# * ipv4, "" - (default) select the first ipv4 address
244+
# * ipv6 - select the first ipv6 address
245+
# * cni - use the order returned by the CNI plugins, returning the first IP address from the results
246+
ip_pref = "ipv4"
241247

242248
# 'plugins."io.containerd.grpc.v1.cri".image_decryption' contains config related
243249
# to handling decryption of encrypted container images.

pkg/cri/config/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ type CniConfig struct {
111111
// a temporary backward-compatible solution for them.
112112
// TODO(random-liu): Deprecate this option when kubenet is deprecated.
113113
NetworkPluginConfTemplate string `toml:"conf_template" json:"confTemplate"`
114+
// IPPreference specifies the strategy to use when selecting the main IP address for a pod.
115+
//
116+
// Options include:
117+
// * ipv4, "" - (default) select the first ipv4 address
118+
// * ipv6 - select the first ipv6 address
119+
// * cni - use the order returned by the CNI plugins, returning the first IP address from the results
120+
IPPreference string `toml:"ip_pref" json:"ipPref"`
114121
}
115122

116123
// Mirror contains the config related to the registry mirror

pkg/cri/server/sandbox_run.go

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ func (c *criService) setupPodNetwork(ctx context.Context, sandbox *sandboxstore.
372372
logDebugCNIResult(ctx, id, result)
373373
// Check if the default interface has IP config
374374
if configs, ok := result.Interfaces[defaultIfName]; ok && len(configs.IPConfigs) > 0 {
375-
sandbox.IP, sandbox.AdditionalIPs = selectPodIPs(configs.IPConfigs)
375+
sandbox.IP, sandbox.AdditionalIPs = selectPodIPs(ctx, configs.IPConfigs, c.config.IPPreference)
376376
sandbox.CNIResult = result
377377
return nil
378378
}
@@ -475,28 +475,46 @@ func toCNIDNS(dns *runtime.DNSConfig) *cni.DNS {
475475
}
476476
}
477477

478-
// selectPodIPs select an ip from the ip list. It prefers ipv4 more than ipv6
479-
// and returns the additional ips
480-
// TODO(random-liu): Revisit the ip order in the ipv6 beta stage. (cri#1278)
481-
func selectPodIPs(ipConfigs []*cni.IPConfig) (string, []string) {
482-
var (
483-
additionalIPs []string
484-
ip string
485-
)
486-
for _, c := range ipConfigs {
487-
if c.IP.To4() != nil && ip == "" {
488-
ip = c.IP.String()
489-
} else {
490-
additionalIPs = append(additionalIPs, c.IP.String())
491-
}
478+
// selectPodIPs select an ip from the ip list.
479+
func selectPodIPs(ctx context.Context, configs []*cni.IPConfig, preference string) (string, []string) {
480+
if len(configs) == 1 {
481+
return ipString(configs[0]), nil
492482
}
493-
if ip != "" {
494-
return ip, additionalIPs
483+
toStrings := func(ips []*cni.IPConfig) (o []string) {
484+
for _, i := range ips {
485+
o = append(o, ipString(i))
486+
}
487+
return o
495488
}
496-
if len(ipConfigs) == 1 {
497-
return additionalIPs[0], nil
489+
var extra []string
490+
switch preference {
491+
default:
492+
if preference != "ipv4" && preference != "" {
493+
log.G(ctx).WithField("ip_pref", preference).Warn("invalid ip_pref, falling back to ipv4")
494+
}
495+
for i, ip := range configs {
496+
if ip.IP.To4() != nil {
497+
return ipString(ip), append(extra, toStrings(configs[i+1:])...)
498+
}
499+
extra = append(extra, ipString(ip))
500+
}
501+
case "ipv6":
502+
for i, ip := range configs {
503+
if ip.IP.To16() != nil {
504+
return ipString(ip), append(extra, toStrings(configs[i+1:])...)
505+
}
506+
extra = append(extra, ipString(ip))
507+
}
508+
case "cni":
509+
// use func default return
498510
}
499-
return additionalIPs[0], additionalIPs[1:]
511+
512+
all := toStrings(configs)
513+
return all[0], all[1:]
514+
}
515+
516+
func ipString(ip *cni.IPConfig) string {
517+
return ip.IP.String()
500518
}
501519

502520
// untrustedWorkload returns true if the sandbox contains untrusted workload.

pkg/cri/server/sandbox_run_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2727
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
2828
"github.com/stretchr/testify/assert"
29+
"golang.org/x/net/context"
2930
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
3031

3132
"github.com/containerd/containerd/pkg/cri/annotations"
@@ -260,12 +261,26 @@ func TestSelectPodIP(t *testing.T) {
260261
ips []string
261262
expectedIP string
262263
expectedAdditionalIPs []string
264+
pref string
263265
}{
264266
"ipv4 should be picked even if ipv6 comes first": {
265267
ips: []string{"2001:db8:85a3::8a2e:370:7334", "192.168.17.43"},
266268
expectedIP: "192.168.17.43",
267269
expectedAdditionalIPs: []string{"2001:db8:85a3::8a2e:370:7334"},
268270
},
271+
"ipv6 should be picked even if ipv4 comes first": {
272+
ips: []string{"2001:db8:85a3::8a2e:370:7334", "192.168.17.43"},
273+
expectedIP: "2001:db8:85a3::8a2e:370:7334",
274+
expectedAdditionalIPs: []string{"192.168.17.43"},
275+
pref: "ipv6",
276+
},
277+
"order should reflect ip selection": {
278+
ips: []string{"2001:db8:85a3::8a2e:370:7334", "192.168.17.43"},
279+
expectedIP: "2001:db8:85a3::8a2e:370:7334",
280+
expectedAdditionalIPs: []string{"192.168.17.43"},
281+
pref: "cni",
282+
},
283+
269284
"ipv4 should be picked when there is only ipv4": {
270285
ips: []string{"192.168.17.43"},
271286
expectedIP: "192.168.17.43",
@@ -289,7 +304,7 @@ func TestSelectPodIP(t *testing.T) {
289304
IP: net.ParseIP(ip),
290305
})
291306
}
292-
ip, additionalIPs := selectPodIPs(ipConfigs)
307+
ip, additionalIPs := selectPodIPs(context.Background(), ipConfigs, test.pref)
293308
assert.Equal(t, test.expectedIP, ip)
294309
assert.Equal(t, test.expectedAdditionalIPs, additionalIPs)
295310
}

0 commit comments

Comments
 (0)