Skip to content

Commit 40967e8

Browse files
committed
minikube: add ip-family flags and core IPv6 config plumbing
1 parent 524347e commit 40967e8

File tree

3 files changed

+141
-2
lines changed

3 files changed

+141
-2
lines changed

cmd/minikube/cmd/start_flags.go

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cmd
1818

1919
import (
2020
"fmt"
21+
"net"
2122
"runtime"
2223
"strings"
2324
"time"
@@ -61,6 +62,7 @@ const (
6162
kubernetesVersion = "kubernetes-version"
6263
noKubernetes = "no-kubernetes"
6364
hostOnlyCIDR = "host-only-cidr"
65+
hostOnlyCIDRv6 = "host-only-cidr-v6"
6466
containerRuntime = "container-runtime"
6567
criSocket = "cri-socket"
6668
networkPlugin = "network-plugin" // deprecated, use --cni instead
@@ -84,6 +86,8 @@ const (
8486
apiServerPort = "apiserver-port"
8587
dnsDomain = "dns-domain"
8688
serviceCIDR = "service-cluster-ip-range"
89+
serviceCIDRv6 = "service-cluster-ip-range-v6"
90+
ipFamily = "ip-family"
8791
imageRepository = "image-repository"
8892
imageMirrorCountry = "image-mirror-country"
8993
mountString = "mount-string"
@@ -144,8 +148,12 @@ const (
144148
socketVMnetClientPath = "socket-vmnet-client-path"
145149
socketVMnetPath = "socket-vmnet-path"
146150
staticIP = "static-ip"
151+
staticIPv6 = "static-ipv6"
147152
gpus = "gpus"
148153
autoPauseInterval = "auto-pause-interval"
154+
subnetv6 = "subnet-v6"
155+
podCIDR = "pod-cidr"
156+
podCIDRv6 = "pod-cidr-v6"
149157
)
150158

151159
var (
@@ -209,6 +217,7 @@ func initMinikubeFlags() {
209217
startCmd.Flags().Bool(disableMetrics, false, "If set, disables metrics reporting (CPU and memory usage), this can improve CPU usage. Defaults to false.")
210218
startCmd.Flags().Bool(disableCoreDNSLog, false, "If set, disable CoreDNS verbose logging. Defaults to false.")
211219
startCmd.Flags().String(staticIP, "", "Set a static IP for the minikube cluster, the IP must be: private, IPv4, and the last octet must be between 2 and 254, for example 192.168.200.200 (Docker and Podman drivers only)")
220+
startCmd.Flags().String(staticIPv6, "", "Set a static IPv6 address for the minikube cluster, for example fd00::100 (Docker and Podman drivers only)")
212221
startCmd.Flags().StringP(gpus, "g", "", "Allow pods to use your GPUs. Options include: [all,nvidia,amd] (Docker driver with Docker container-runtime only)")
213222
startCmd.Flags().Duration(autoPauseInterval, time.Minute*1, "Duration of inactivity before the minikube VM is paused (default 1m0s)")
214223
}
@@ -260,6 +269,7 @@ func initDriverFlags() {
260269

261270
// virtualbox
262271
startCmd.Flags().String(hostOnlyCIDR, "192.168.59.1/24", "The CIDR to be used for the minikube VM (virtualbox driver only)")
272+
startCmd.Flags().String(hostOnlyCIDRv6, "fd00::1/64", "The IPv6 CIDR to be used for the minikube VM (virtualbox driver only)")
263273
startCmd.Flags().Bool(dnsProxy, false, "Enable proxy for NAT DNS requests (virtualbox driver only)")
264274
startCmd.Flags().Bool(hostDNSResolver, true, "Enable host resolver for NAT DNS requests (virtualbox driver only)")
265275
startCmd.Flags().Bool(noVTXCheck, false, "Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)")
@@ -282,6 +292,9 @@ func initDriverFlags() {
282292
startCmd.Flags().String(listenAddress, "", "IP Address to use to expose ports (docker and podman driver only)")
283293
startCmd.Flags().StringSlice(ports, []string{}, "List of ports that should be exposed (docker and podman driver only)")
284294
startCmd.Flags().String(subnet, "", "Subnet to be used on kic cluster. If left empty, minikube will choose subnet address, beginning from 192.168.49.0. (docker and podman driver only)")
295+
startCmd.Flags().String(subnetv6, "", "IPv6 subnet (CIDR) for the Docker/Podman network. If empty, minikube picks an internal ULA. (docker and podman driver only)")
296+
startCmd.Flags().String(podCIDR, "", "IPv4 CIDR to use for pod IPs (bridge CNI).")
297+
startCmd.Flags().String(podCIDRv6, "", "IPv6 CIDR to use for pod IPs (bridge CNI).")
285298

286299
// qemu
287300
startCmd.Flags().String(qemuFirmwarePath, "", "Path to the qemu firmware file. Defaults: For Linux, the default firmware location. For macOS, the brew installation location. For Windows, C:\\Program Files\\qemu\\share")
@@ -293,7 +306,9 @@ func initNetworkingFlags() {
293306
startCmd.Flags().StringSliceVar(&registryMirror, "registry-mirror", nil, "Registry mirrors to pass to the Docker daemon")
294307
startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \"auto\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers")
295308
startCmd.Flags().String(imageMirrorCountry, "", "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.")
296-
startCmd.Flags().String(serviceCIDR, constants.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.")
309+
startCmd.Flags().String(serviceCIDR, constants.DefaultServiceCIDR, "The IPv4 CIDR to be used for service cluster IPs.")
310+
startCmd.Flags().String(serviceCIDRv6, constants.DefaultServiceCIDRv6, "The IPv6 CIDR to be used for service cluster IPs.")
311+
startCmd.Flags().String(ipFamily, "ipv4", "Cluster IP family mode: one of 'ipv4' (default), 'ipv6', or 'dual'.")
297312
startCmd.Flags().StringArrayVar(&config.DockerEnv, "docker-env", nil, "Environment variables to pass to the Docker daemon. (format: key=value)")
298313
startCmd.Flags().StringArrayVar(&config.DockerOpt, "docker-opt", nil, "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)")
299314

@@ -494,6 +509,102 @@ func getNetwork(driverName string, options *run.CommandOptions) string {
494509
return n
495510
}
496511

512+
// normalizeAndValidateIPFamily sets defaults, validates CIDRs, and guards Desktop vs Linux daemon for v6.
513+
func normalizeAndValidateIPFamily(cc *config.ClusterConfig) {
514+
fam := strings.ToLower(strings.TrimSpace(cc.KubernetesConfig.IPFamily))
515+
switch fam {
516+
case "", "ipv4", "ipv6", "dual":
517+
// ok
518+
default:
519+
exit.Message(reason.Usage, "Invalid --ip-family {{.fam}}. Must be one of: ipv4, ipv6, dual.", out.V{"fam": cc.KubernetesConfig.IPFamily})
520+
}
521+
if fam == "" {
522+
fam = "ipv4"
523+
cc.KubernetesConfig.IPFamily = fam
524+
}
525+
// default v6 CIDRs if needed
526+
if fam != "ipv4" {
527+
if cc.KubernetesConfig.ServiceCIDRv6 == "" {
528+
cc.KubernetesConfig.ServiceCIDRv6 = constants.DefaultServiceCIDRv6
529+
}
530+
if cc.KubernetesConfig.PodCIDRv6 == "" {
531+
cc.KubernetesConfig.PodCIDRv6 = constants.DefaultPodCIDRv6
532+
}
533+
}
534+
// defaults so dual has both sides unless the user overrides
535+
if fam != "ipv6" && cc.KubernetesConfig.PodCIDR == "" {
536+
cc.KubernetesConfig.PodCIDR = cni.DefaultPodCIDR
537+
}
538+
539+
if fam != "ipv4" && cc.KubernetesConfig.PodCIDRv6 == "" {
540+
cc.KubernetesConfig.PodCIDRv6 = constants.DefaultPodCIDRv6
541+
}
542+
543+
// basic CIDR sanity
544+
if cidr := cc.Subnetv6; cidr != "" {
545+
if _, _, err := net.ParseCIDR(cidr); err != nil {
546+
exit.Message(reason.Usage, "--subnet-v6 must be a valid IPv6 CIDR: {{.e}}", out.V{"e": err})
547+
}
548+
}
549+
if cidr := cc.KubernetesConfig.ServiceCIDRv6; cidr != "" {
550+
if _, _, err := net.ParseCIDR(cidr); err != nil {
551+
exit.Message(reason.Usage, "--service-cluster-ip-range-v6 must be a valid IPv6 CIDR: {{.e}}", out.V{"e": err})
552+
}
553+
}
554+
if cidr := cc.KubernetesConfig.PodCIDRv6; cidr != "" {
555+
if _, _, err := net.ParseCIDR(cidr); err != nil {
556+
exit.Message(reason.Usage, "PodCIDRv6 must be a valid IPv6 CIDR: {{.e}}", out.V{"e": err})
557+
}
558+
}
559+
560+
if s := cc.KubernetesConfig.PodCIDR; s != "" {
561+
if _, _, err := net.ParseCIDR(s); err != nil {
562+
exit.Message(reason.Usage, "--pod-cidr must be a valid IPv4 CIDR: {{.e}}", out.V{"e": err})
563+
}
564+
}
565+
566+
if s := cc.KubernetesConfig.PodCIDRv6; s != "" {
567+
if _, _, err := net.ParseCIDR(s); err != nil {
568+
exit.Message(reason.Usage, "--pod-cidr-v6 must be a valid IPv6 CIDR: {{.e}}", out.V{"e": err})
569+
}
570+
}
571+
572+
// validate static IPv6 if provided
573+
if s := cc.StaticIPv6; s != "" {
574+
ip := net.ParseIP(s)
575+
if ip == nil || ip.To4() != nil {
576+
exit.Message(reason.Usage, "--static-ipv6 must be a valid IPv6 address")
577+
}
578+
}
579+
580+
// Docker driver guardrails: Linux daemon + IPv6 must be enabled
581+
if driver.IsDocker(cc.Driver) && fam != "ipv4" {
582+
// Desktop vs Linux daemon hint (we can't reliably detect IPv6 enabled here)
583+
si, err := oci.CachedDaemonInfo(cc.Driver)
584+
if err != nil {
585+
si, err = oci.DaemonInfo(cc.Driver)
586+
if err != nil {
587+
exit.Message(reason.Usage, "Failed to query Docker daemon info: {{.e}}", out.V{"e": err})
588+
}
589+
}
590+
// On non-Linux hosts we assume Docker Desktop; on Linux it's a native Engine
591+
// unless DockerOS explicitly says "Docker Desktop".
592+
isLinuxDaemon := runtime.GOOS == "linux" && si.DockerOS != "Docker Desktop"
593+
if !isLinuxDaemon {
594+
if fam == "ipv6" {
595+
exit.Message(reason.Usage,
596+
"IPv6 clusters require a Linux Docker daemon (Desktop is not supported). "+
597+
"Use a Linux/WSL2 daemon or set --ip-family=ipv4.")
598+
}
599+
out.WarningT("Dual-stack on Docker Desktop may be limited. For full IPv6 support, use a Linux Docker daemon.")
600+
}
601+
602+
// Friendly reminder about enabling daemon IPv6 (actual failure will occur during network create otherwise)
603+
out.Styled(style.Tip,
604+
"If Docker daemon IPv6 is disabled, enable it in /etc/docker/daemon.json and restart:\n {\"ipv6\": true, \"fixed-cidr-v6\": \"fd00:55:66::/64\"}")
605+
}
606+
}
607+
497608
func validateQemuNetwork(n string) string {
498609
switch n {
499610
case "socket_vmnet":
@@ -576,6 +687,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
576687
KicBaseImage: viper.GetString(kicBaseImage),
577688
Network: getNetwork(drvName, options),
578689
Subnet: viper.GetString(subnet),
690+
Subnetv6: viper.GetString(subnetv6),
579691
Memory: getMemorySize(cmd, drvName),
580692
CPUs: getCPUCount(drvName),
581693
DiskSize: getDiskSize(),
@@ -590,6 +702,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
590702
InsecureRegistry: insecureRegistry,
591703
RegistryMirror: registryMirror,
592704
HostOnlyCIDR: viper.GetString(hostOnlyCIDR),
705+
HostOnlyCIDRv6: viper.GetString(hostOnlyCIDRv6),
593706
HypervVirtualSwitch: viper.GetString(hypervVirtualSwitch),
594707
HypervUseExternalSwitch: viper.GetBool(hypervUseExternalSwitch),
595708
HypervExternalAdapter: viper.GetString(hypervExternalAdapter),
@@ -631,6 +744,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
631744
SocketVMnetClientPath: detect.SocketVMNetClientPath(),
632745
SocketVMnetPath: detect.SocketVMNetPath(),
633746
StaticIP: viper.GetString(staticIP),
747+
StaticIPv6: viper.GetString(staticIPv6),
634748
KubernetesConfig: config.KubernetesConfig{
635749
KubernetesVersion: k8sVersion,
636750
ClusterName: ClusterFlagValue(),
@@ -644,6 +758,10 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
644758
CRISocket: viper.GetString(criSocket),
645759
NetworkPlugin: chosenNetworkPlugin,
646760
ServiceCIDR: viper.GetString(serviceCIDR),
761+
ServiceCIDRv6: viper.GetString(serviceCIDRv6),
762+
PodCIDR: viper.GetString(podCIDR),
763+
PodCIDRv6: viper.GetString(podCIDRv6),
764+
IPFamily: viper.GetString(ipFamily),
647765
ImageRepository: getRepository(cmd, k8sVersion),
648766
ExtraOptions: getExtraOptions(),
649767
ShouldLoadCachedImages: viper.GetBool(cacheImages),
@@ -697,6 +815,8 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
697815
}
698816
}
699817

818+
normalizeAndValidateIPFamily(&cc)
819+
700820
return cc
701821
}
702822

@@ -831,11 +951,15 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
831951
updateStringFromFlag(cmd, &cc.MinikubeISO, isoURL)
832952
updateStringFromFlag(cmd, &cc.KicBaseImage, kicBaseImage)
833953
updateStringFromFlag(cmd, &cc.Network, network)
954+
updateStringFromFlag(cmd, &cc.Subnetv6, subnetv6)
955+
updateStringFromFlag(cmd, &cc.KubernetesConfig.PodCIDR, podCIDR)
956+
updateStringFromFlag(cmd, &cc.KubernetesConfig.PodCIDRv6, podCIDRv6)
834957
updateStringFromFlag(cmd, &cc.HyperkitVpnKitSock, vpnkitSock)
835958
updateStringSliceFromFlag(cmd, &cc.HyperkitVSockPorts, vsockPorts)
836959
updateStringSliceFromFlag(cmd, &cc.NFSShare, nfsShare)
837960
updateStringFromFlag(cmd, &cc.NFSSharesRoot, nfsSharesRoot)
838961
updateStringFromFlag(cmd, &cc.HostOnlyCIDR, hostOnlyCIDR)
962+
updateStringFromFlag(cmd, &cc.HostOnlyCIDRv6, hostOnlyCIDRv6)
839963
updateStringFromFlag(cmd, &cc.HypervVirtualSwitch, hypervVirtualSwitch)
840964
updateBoolFromFlag(cmd, &cc.HypervUseExternalSwitch, hypervUseExternalSwitch)
841965
updateStringFromFlag(cmd, &cc.HypervExternalAdapter, hypervExternalAdapter)
@@ -853,6 +977,7 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
853977
updateDurationFromFlag(cmd, &cc.StartHostTimeout, waitTimeout)
854978
updateStringSliceFromFlag(cmd, &cc.ExposedPorts, ports)
855979
updateStringFromFlag(cmd, &cc.SSHIPAddress, sshIPAddress)
980+
updateStringFromFlag(cmd, &cc.StaticIPv6, staticIPv6)
856981
updateStringFromFlag(cmd, &cc.SSHUser, sshSSHUser)
857982
updateStringFromFlag(cmd, &cc.SSHKey, sshSSHKey)
858983
updateIntFromFlag(cmd, &cc.SSHPort, sshSSHPort)
@@ -865,6 +990,8 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
865990
updateStringFromFlag(cmd, &cc.KubernetesConfig.CRISocket, criSocket)
866991
updateStringFromFlag(cmd, &cc.KubernetesConfig.NetworkPlugin, networkPlugin)
867992
updateStringFromFlag(cmd, &cc.KubernetesConfig.ServiceCIDR, serviceCIDR)
993+
updateStringFromFlag(cmd, &cc.KubernetesConfig.ServiceCIDRv6, serviceCIDRv6)
994+
updateStringFromFlag(cmd, &cc.KubernetesConfig.IPFamily, ipFamily)
868995
updateBoolFromFlag(cmd, &cc.KubernetesConfig.ShouldLoadCachedImages, cacheImages)
869996
updateDurationFromFlag(cmd, &cc.CertExpiration, certExpiration)
870997
updateStringFromFlag(cmd, &cc.MountString, mountString)
@@ -921,7 +1048,7 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
9211048
if cc.ScheduledStop != nil && time.Until(time.Unix(cc.ScheduledStop.InitiationTime, 0).Add(cc.ScheduledStop.Duration)) <= 0 {
9221049
cc.ScheduledStop = nil
9231050
}
924-
1051+
normalizeAndValidateIPFamily(&cc)
9251052
return cc
9261053
}
9271054

pkg/minikube/config/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type ClusterConfig struct {
5050
InsecureRegistry []string
5151
RegistryMirror []string
5252
HostOnlyCIDR string // Only used by the virtualbox driver
53+
HostOnlyCIDRv6 string // IPv6 CIDR for the virtualbox driver
5354
HypervVirtualSwitch string
5455
HypervUseExternalSwitch bool
5556
HypervExternalAdapter string
@@ -85,6 +86,7 @@ type ClusterConfig struct {
8586
ListenAddress string // Only used by the docker and podman driver
8687
Network string // only used by docker driver
8788
Subnet string // only used by the docker and podman driver
89+
Subnetv6 string // IPv6 subnet for docker and podman driver
8890
MultiNodeRequested bool
8991
ExtraDisks int // currently only implemented for hyperkit and kvm2
9092
CertExpiration time.Duration
@@ -105,6 +107,7 @@ type ClusterConfig struct {
105107
SocketVMnetClientPath string
106108
SocketVMnetPath string
107109
StaticIP string
110+
StaticIPv6 string // Static IPv6 address for the cluster
108111
SSHAuthSock string
109112
SSHAgentPID int
110113
GPUs string
@@ -126,6 +129,10 @@ type KubernetesConfig struct {
126129
NetworkPlugin string
127130
FeatureGates string // https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
128131
ServiceCIDR string // the subnet which Kubernetes services will be deployed to
132+
ServiceCIDRv6 string // the IPv6 subnet which Kubernetes services will be deployed to
133+
PodCIDR string // the IPv4 subnet which Kubernetes pods will be deployed to
134+
PodCIDRv6 string // the IPv6 subnet which Kubernetes pods will be deployed to
135+
IPFamily string // IP family mode: ipv4, ipv6, or dual
129136
ImageRepository string
130137
LoadBalancerStartIP string // currently only used by MetalLB addon
131138
LoadBalancerEndIP string // currently only used by MetalLB addon
@@ -143,6 +150,7 @@ type KubernetesConfig struct {
143150
type Node struct {
144151
Name string
145152
IP string
153+
IPv6 string // IPv6 address of the node
146154
Port int
147155
KubernetesVersion string
148156
ContainerRuntime string

pkg/minikube/constants/constants.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ const (
8080
ClusterDNSDomain = "cluster.local"
8181
// DefaultServiceCIDR is The CIDR to be used for service cluster IPs
8282
DefaultServiceCIDR = "10.96.0.0/12"
83+
// DefaultServiceCIDRv6 is The IPv6 CIDR to be used for service cluster IPs
84+
DefaultServiceCIDRv6 = "fd00::/108"
85+
// DefaultPodCIDRv6 is The IPv6 CIDR to be used for pod IPs
86+
DefaultPodCIDRv6 = "fd01::/64"
8387
// HostAlias is a DNS alias to the container/VM host IP
8488
HostAlias = "host.minikube.internal"
8589
// ControlPlaneAlias is a DNS alias pointing to the apiserver frontend

0 commit comments

Comments
 (0)