Skip to content

Commit c5693e5

Browse files
committed
Use vment-helper in vfkit driver
This is a quick proof of concept, using vment-helper to connect vfkit to vment network shared mode instead of the "nat" network. We should add network options for vfkit and use vmnet-helper only when using the vmnet network, but this is good enough to play with vmnet-helper and run stress tests. We also need documentation, but for now here is how to play with vmnet-helper: 1. Install latest release: https://github.com/nirs/vmnet-helper?tab=readme-ov-file#installation 2. Setup vment-helper sudoers rule: https://github.com/nirs/vmnet-helper?tab=readme-ov-file#granting-permission-to-run-vmnet-helper At this point you can start clusters with vfkit driver: % out/minikube start --driver vfkit --container-runtime containerd 😄 minikube v1.35.0 on Darwin 15.3.1 (arm64) ✨ Using the vfkit (experimental) driver based on user configuration 👍 Starting "minikube" primary control-plane node in "minikube" cluster 🔥 Creating vfkit VM (CPUs=2, Memory=6000MB, Disk=20000MB) ... 📦 Preparing Kubernetes v1.32.2 on containerd 1.7.23 ... ▪ Generating certificates and keys ... ▪ Booting up control plane ... ▪ Configuring RBAC rules ... 🔗 Configuring bridge CNI (Container Networking Interface) ... 🔎 Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 🌟 Enabled addons: default-storageclass, storage-provisioner 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default The cluster IP will be in the vment shared network: % out/minikube profile list |----------|-----------|------------|---------------|------|---------|--------|-------|----------------|--------------------| | Profile | VM Driver | Runtime | IP | Port | Version | Status | Nodes | Active Profile | Active Kubecontext | |----------|-----------|------------|---------------|------|---------|--------|-------|----------------|--------------------| | minikube | vfkit | containerd | 192.168.105.8 | 8443 | v1.32.2 | OK | 1 | * | * | |----------|-----------|------------|---------------|------|---------|--------|-------|----------------|--------------------| We use the same --interface-id (generated from the machine name) when starting the vmnet interface, so we will always get the same MAC address (assigned by the vmnet framework) and the same IP address (assigned by the DHCP server). The vmnet-helper process is started in the background with vfkit, and must remain running while vfkit is running. Like vfkit, we start it in the background, in a new process group, so it not terminated if the minikube process group is terminate. Since vment-helper requires root to start the vmnet interface, we start it with sudo, creating 2 child processes. vment-helper drops privileges immediately after starting the vment interface, and run as the user and group running minikube. Stopping the cluster will stop sudo, which will stop the vmnet-helper process. Deleting the cluster kill both sudo and vment-helper by killing the process group.
1 parent 974e806 commit c5693e5

File tree

3 files changed

+110
-13
lines changed

3 files changed

+110
-13
lines changed

pkg/drivers/vfkit/vfkit.go

+100-11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444

4545
"k8s.io/klog/v2"
4646
pkgdrivers "k8s.io/minikube/pkg/drivers"
47+
"k8s.io/minikube/pkg/drivers/vmnet"
4748
"k8s.io/minikube/pkg/minikube/exit"
4849
"k8s.io/minikube/pkg/minikube/firewall"
4950
"k8s.io/minikube/pkg/minikube/out"
@@ -69,6 +70,9 @@ type Driver struct {
6970
Cmdline string
7071
MACAddress string
7172
ExtraDisks int
73+
74+
// For network=vmnet-shared.
75+
VmnetHelper *vmnet.Helper
7276
}
7377

7478
func NewDriver(hostName, storePath string) drivers.Driver {
@@ -144,7 +148,7 @@ func checkPid(pid int) error {
144148
return process.Signal(syscall.Signal(0))
145149
}
146150

147-
func (d *Driver) GetState() (state.State, error) {
151+
func (d *Driver) getVfkitState() (state.State, error) {
148152
if _, err := os.Stat(d.pidfilePath()); err != nil {
149153
return state.Stopped, nil
150154
}
@@ -174,6 +178,35 @@ func (d *Driver) GetState() (state.State, error) {
174178
return state.None, nil
175179
}
176180

181+
func (d *Driver) getVmnetHelperState() (state.State, error) {
182+
if d.VmnetHelper == nil {
183+
return state.None, nil
184+
}
185+
return d.VmnetHelper.GetState()
186+
}
187+
188+
// GetState returns driver state. Since vfkit driver may use 2 processes
189+
// (vmnet-helper, vfkit), this may returns combined state of both processes.
190+
func (d *Driver) GetState() (state.State, error) {
191+
helperState, helperErr := d.getVmnetHelperState()
192+
vfkitState, vfkitErr := d.getVfkitState()
193+
194+
// If at least one is running, we need to treat state as running, nedeed by
195+
// Stop(), Remove(), and Restart().
196+
if helperState == state.Running || vfkitState == state.Running {
197+
return state.Running, nil
198+
}
199+
200+
// If at least one is in error state we treat the state as error.
201+
if helperState == state.Error || vfkitState == state.Error {
202+
return state.Error, fmt.Errorf("vfkit: %w, vmnet-helper: %w", vfkitErr, helperErr)
203+
}
204+
205+
// We can have many combinations (state.None, state.Stopped) of the 2
206+
// states. Letss simplify by treating vfkit state as the main state.
207+
return vfkitState, nil
208+
}
209+
177210
func (d *Driver) Create() error {
178211
var err error
179212
if d.SSHPort, err = d.GetSSHPort(); err != nil {
@@ -215,6 +248,42 @@ func (d *Driver) Create() error {
215248
}
216249

217250
func (d *Driver) Start() error {
251+
var helperSock, vfkitSock *os.File
252+
var err error
253+
254+
if d.VmnetHelper != nil {
255+
helperSock, vfkitSock, err = vmnet.Socketpair()
256+
if err != nil {
257+
return err
258+
}
259+
defer helperSock.Close()
260+
defer vfkitSock.Close()
261+
262+
if err := d.VmnetHelper.Start(helperSock); err != nil {
263+
return err
264+
}
265+
266+
d.MACAddress = d.VmnetHelper.GetMACAddress()
267+
}
268+
269+
if err := d.startVfkit(vfkitSock); err != nil {
270+
d.stopVmnetHelper()
271+
return err
272+
}
273+
274+
if err := d.setupIP(d.MACAddress); err != nil {
275+
d.stopVmnetHelper()
276+
return err
277+
}
278+
279+
log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress)
280+
281+
return WaitForTCPWithDelay(fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort), time.Second)
282+
}
283+
284+
// startVfkit starts the vfkit child process. If vfkitSock is non nil, vfkit is
285+
// connected to the vmnet network via the socket instead of "nat" network.
286+
func (d *Driver) startVfkit(vfkitSock *os.File) error {
218287
machineDir := filepath.Join(d.StorePath, "machines", d.GetMachineName())
219288

220289
var startCmd []string
@@ -227,8 +296,15 @@ func (d *Driver) Start() error {
227296
startCmd = append(startCmd,
228297
"--device", fmt.Sprintf("virtio-blk,path=%s", isoPath))
229298

230-
startCmd = append(startCmd,
231-
"--device", fmt.Sprintf("virtio-net,nat,mac=%s", d.MACAddress))
299+
if vfkitSock != nil {
300+
// The guest will be able to access other guests in the vmnet network.
301+
startCmd = append(startCmd,
302+
"--device", fmt.Sprintf("virtio-net,fd=%d,mac=%s", vfkitSock.Fd(), d.MACAddress))
303+
} else {
304+
// The guest will not be able to access other guests.
305+
startCmd = append(startCmd,
306+
"--device", fmt.Sprintf("virtio-net,nat,mac=%s", d.MACAddress))
307+
}
232308

233309
startCmd = append(startCmd,
234310
"--device", "virtio-rng")
@@ -263,14 +339,7 @@ func (d *Driver) Start() error {
263339
if err := os.WriteFile(d.pidfilePath(), []byte(fmt.Sprintf("%v", pid)), 0600); err != nil {
264340
return err
265341
}
266-
267-
if err := d.setupIP(d.MACAddress); err != nil {
268-
return err
269-
}
270-
271-
log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress)
272-
273-
return WaitForTCPWithDelay(fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort), time.Second)
342+
return nil
274343
}
275344

276345
func (d *Driver) setupIP(mac string) error {
@@ -311,7 +380,17 @@ func isBootpdError(err error) bool {
311380
return strings.Contains(err.Error(), "could not find an IP address")
312381
}
313382

383+
func (d *Driver) stopVmnetHelper() error {
384+
if d.VmnetHelper == nil {
385+
return nil
386+
}
387+
return d.VmnetHelper.Stop()
388+
}
389+
314390
func (d *Driver) Stop() error {
391+
if err := d.stopVmnetHelper(); err != nil {
392+
return err
393+
}
315394
if err := d.SetVFKitState("HardStop"); err != nil {
316395
return err
317396
}
@@ -366,7 +445,17 @@ func (d *Driver) extractKernel(isoPath string) error {
366445
return nil
367446
}
368447

448+
func (d *Driver) killVmnetHelper() error {
449+
if d.VmnetHelper == nil {
450+
return nil
451+
}
452+
return d.VmnetHelper.Kill()
453+
}
454+
369455
func (d *Driver) Kill() error {
456+
if err := d.killVmnetHelper(); err != nil {
457+
return err
458+
}
370459
if err := d.SetVFKitState("HardStop"); err != nil {
371460
return err
372461
}

pkg/drivers/vmnet/vmnet.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ type interfaceInfo struct {
6363
func HelperAvailable() bool {
6464
version, err := exec.Command("sudo", "--non-interactive", executablePath, "--version").Output()
6565
if err != nil {
66-
log.Debugf("Failed to run vmnet-helper: %w", executablePath, err)
66+
log.Debugf("Failed to run vmnet-helper: %w", err)
6767
return false
6868
}
69-
log.Debugf("Using vmnet-helper version %q", executablePath, version)
69+
log.Debugf("Using vmnet-helper version %q", version)
7070
return true
7171
}
7272

pkg/minikube/registry/drvs/vfkit/vfkit.go

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/docker/machine/libmachine/drivers"
2727

2828
"k8s.io/minikube/pkg/drivers/vfkit"
29+
"k8s.io/minikube/pkg/drivers/vmnet"
2930
"k8s.io/minikube/pkg/minikube/config"
3031
"k8s.io/minikube/pkg/minikube/download"
3132
"k8s.io/minikube/pkg/minikube/driver"
@@ -51,6 +52,7 @@ func init() {
5152
}
5253

5354
func configure(cfg config.ClusterConfig, n config.Node) (interface{}, error) {
55+
// XXX Not needed when using vmnet-helper.
5456
mac, err := generateMACAddress()
5557
if err != nil {
5658
return nil, fmt.Errorf("generating MAC address: %v", err)
@@ -69,6 +71,12 @@ func configure(cfg config.ClusterConfig, n config.Node) (interface{}, error) {
6971
MACAddress: mac,
7072
Cmdline: "",
7173
ExtraDisks: cfg.ExtraDisks,
74+
75+
// XXX Create only when using vmnet network.
76+
VmnetHelper: &vmnet.Helper{
77+
MachineName: config.MachineName(cfg, n),
78+
StorePath: localpath.MiniPath(),
79+
},
7280
}, nil
7381
}
7482

0 commit comments

Comments
 (0)