Skip to content

Commit f8acde4

Browse files
author
Ciprian Barbu
committed
feat(9pfs volumes): Add support for 9pfs shared volumes
Implement a method of mounting local paths inside unikernels (Unikraft only at this moment) based on a dedicated annotation. This can be used in conjunction with NFS shared volumes in a Kubernetes cluster, when a NFS provider has been configured. The new annotation is called "com.urunc.unikernel.ninePFSMntPoint" and it holds the value of mount point inside the unikernel, or in other words, the value of "mountPath" specified by the volumeMount field of the Kubernetes template. The recommended usage is by setting it in the Pod specification of the Kubernetes template. In Unikontainer Exec method, urunc will search the list of mount points passed from containerd and find a match for the mount point specified by the annotation. Once found, urunc will be able to determine the local path on the worker node of the PersistenVolume or PersistentVolumeClaim and instruct qemu to mount it via 9pfs provider into the unikernel. Signed-off-by: Ciprian Barbu <ciprian.barbu@thalesgroup.com>
1 parent 8133766 commit f8acde4

File tree

6 files changed

+79
-29
lines changed

6 files changed

+79
-29
lines changed

pkg/unikontainers/config.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ var ErrEmptyAnnotations = errors.New("spec annotations are empty")
3636
// Urunc specific annotations
3737
// ALways keep it in sync with the struct UnikernelConfig struct
3838
const (
39-
annotType = "com.urunc.unikernel.unikernelType"
40-
annotVersion = "com.urunc.unikernel.unikernelVersion"
41-
annotBinary = "com.urunc.unikernel.binary"
42-
annotCmdLine = "com.urunc.unikernel.cmdline"
43-
annotHypervisor = "com.urunc.unikernel.hypervisor"
44-
annotInitrd = "com.urunc.unikernel.initrd"
45-
annotBlock = "com.urunc.unikernel.block"
46-
annotBlockMntPoint = "com.urunc.unikernel.blkMntPoint"
47-
annotUseDMBlock = "com.urunc.unikernel.useDMBlock"
39+
annotType = "com.urunc.unikernel.unikernelType"
40+
annotVersion = "com.urunc.unikernel.unikernelVersion"
41+
annotBinary = "com.urunc.unikernel.binary"
42+
annotCmdLine = "com.urunc.unikernel.cmdline"
43+
annotHypervisor = "com.urunc.unikernel.hypervisor"
44+
annotInitrd = "com.urunc.unikernel.initrd"
45+
annotBlock = "com.urunc.unikernel.block"
46+
annotBlockMntPoint = "com.urunc.unikernel.blkMntPoint"
47+
annotUseDMBlock = "com.urunc.unikernel.useDMBlock"
48+
annotNinePFSMntPoint = "com.urunc.unikernel.ninePFSMntPoint"
4849
)
4950

5051
// A UnikernelConfig struct holds the info provided by bima image on how to execute our unikernel
@@ -58,6 +59,7 @@ type UnikernelConfig struct {
5859
Block string `json:"com.urunc.unikernel.block,omitempty"`
5960
BlkMntPoint string `json:"com.urunc.unikernel.blkMntPoint,omitempty"`
6061
UseDMBlock string `json:"com.urunc.unikernel.useDMBlock"`
62+
NinePFSMntPoint string `json:"com.urunc.unikernel.ninePFSMntPoint,omitempty"`
6163
}
6264

6365
// GetUnikernelConfig tries to get the Unikernel config from the bundle annotations.
@@ -104,6 +106,7 @@ func getConfigFromSpec(spec *specs.Spec) (*UnikernelConfig, error) {
104106
block := spec.Annotations[annotBlock]
105107
blkMntPoint := spec.Annotations[annotBlockMntPoint]
106108
useDMBlock := spec.Annotations[annotUseDMBlock]
109+
ninePFSMntPoint := spec.Annotations[annotNinePFSMntPoint]
107110

108111
Log.WithFields(logrus.Fields{
109112
"unikernelType": unikernelType,
@@ -115,6 +118,7 @@ func getConfigFromSpec(spec *specs.Spec) (*UnikernelConfig, error) {
115118
"block": block,
116119
"blkMntPoint": blkMntPoint,
117120
"useDMBlock": useDMBlock,
121+
"ninePFSMntPoint": ninePFSMntPoint,
118122
}).Info("urunc annotations")
119123

120124
// TODO: We need to use a better check to see if annotations were empty
@@ -132,6 +136,7 @@ func getConfigFromSpec(spec *specs.Spec) (*UnikernelConfig, error) {
132136
Block: block,
133137
BlkMntPoint: blkMntPoint,
134138
UseDMBlock: useDMBlock,
139+
NinePFSMntPoint: ninePFSMntPoint,
135140
}, nil
136141
}
137142

@@ -171,6 +176,7 @@ func getConfigFromJSON(jsonFilePath string) (*UnikernelConfig, error) {
171176
"block": conf.Block,
172177
"blkMntPoint": conf.BlkMntPoint,
173178
"useDMBlock": conf.UseDMBlock,
179+
"ninePFSMntPoint": conf.NinePFSMntPoint,
174180
}).Info(uruncJSONFilename + " annotations")
175181
return &conf, nil
176182
}
@@ -231,6 +237,12 @@ func (c *UnikernelConfig) decode() error {
231237
}
232238
c.UseDMBlock = string(decoded)
233239

240+
decoded, err = base64.StdEncoding.DecodeString(c.NinePFSMntPoint)
241+
if err != nil {
242+
return fmt.Errorf("failed to decode ninePFSMntPoint: %v", err)
243+
}
244+
c.NinePFSMntPoint = string(decoded)
245+
234246
return nil
235247
}
236248

pkg/unikontainers/hypervisors/qemu.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ func (q *Qemu) Execve(args ExecArgs, ukernel unikernels.Unikernel) error {
9292
if args.InitrdPath != "" {
9393
cmdString += " -initrd " + args.InitrdPath
9494
}
95+
96+
if len(args.NinePFSVols) > 0 {
97+
cmdString += " -fsdev local,id=p9idfs1,path=" + args.NinePFSVols[0].Src + ",security_model=mapped-xattr"
98+
cmdString += " -device virtio-9p-pci,fsdev=p9idfs1,mount_tag=fs1"
99+
}
100+
95101
cmdString += ukernel.MonitorCli(qemuString)
96102
exArgs := strings.Split(cmdString, " ")
97103
exArgs = append(exArgs, "-append", args.Command)

pkg/unikontainers/hypervisors/vmm.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,27 @@ import (
2525

2626
const DefaultMemory uint64 = 256 // The default memory for every hypervisor: 256 MB
2727

28+
// MountSpec defines a specification for mounting a path from the host on the guest
29+
type MountSpec struct {
30+
Src string
31+
Dst string
32+
}
33+
2834
// ExecArgs holds the data required by Execve to start the VMM
2935
// FIXME: add extra fields if required by additional VMM's
3036
type ExecArgs struct {
31-
Container string // The container ID
32-
UnikernelPath string // The path of the unikernel inside rootfs
33-
TapDevice string // The TAP device name
34-
BlockDevice string // The block device path
35-
InitrdPath string // The path to the initrd of the unikernel
36-
Command string // The unikernel's command line
37-
IPAddress string // The IP address of the TAP device
38-
GuestMAC string // The MAC address of the guest network device
39-
Seccomp bool // Enable or disable seccomp filters for the VMM
40-
MemSizeB uint64 // The size of the memory provided to the VM in bytes
41-
Environment []string // Environment
37+
Container string // The container ID
38+
UnikernelPath string // The path of the unikernel inside rootfs
39+
TapDevice string // The TAP device name
40+
BlockDevice string // The block device path
41+
InitrdPath string // The path to the initrd of the unikernel
42+
Command string // The unikernel's command line
43+
IPAddress string // The IP address of the TAP device
44+
GuestMAC string // The MAC address of the guest network device
45+
Seccomp bool // Enable or disable seccomp filters for the VMM
46+
MemSizeB uint64 // The size of the memory provided to the VM in bytes
47+
Environment []string // Environment
48+
NinePFSVols []MountSpec // Spec for mounting volumes via 9pfs, (source, destination)
4249
}
4350

4451
type VmmType string

pkg/unikontainers/unikernels/unikernel.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type UnikernelParams struct {
3737
RootFSType string // The rootfs type of the Unikernel
3838
BlockMntPoint string // The mount point for the block device
3939
Version string // The version of the unikernel
40+
NinePFSMntPoint string // Mount point inside unikernel of the 9pfs theshared volume
4041
}
4142

4243
var ErrNotSupportedUnikernel = errors.New("unikernel is not supported")

pkg/unikontainers/unikernels/unikraft.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ type UnikraftNet struct {
4343
}
4444

4545
type UnikraftVFS struct {
46-
RootFS string
46+
VfsString string
4747
}
4848

4949
func (u *Unikraft) CommandString() (string, error) {
5050
return fmt.Sprintf("%s %s %s %s %s -- %s", u.AppName,
5151
u.Net.Address,
5252
u.Net.Gateway,
5353
u.Net.Mask,
54-
u.VFS.RootFS,
54+
u.VFS.VfsString,
5555
u.Command), nil
5656
}
5757

@@ -92,35 +92,38 @@ func (u *Unikraft) Init(data UnikernelParams) error {
9292
}
9393
u.Version = data.Version
9494

95-
return u.configureUnikraftArgs(data.RootFSType, data.EthDeviceIP, data.EthDeviceGateway, data.EthDeviceMask)
95+
return u.configureUnikraftArgs(data.RootFSType, data.EthDeviceIP, data.EthDeviceGateway, data.EthDeviceMask, data.NinePFSMntPoint)
9696
}
9797

98-
func (u *Unikraft) configureUnikraftArgs(rootFsType, ethDeviceIP, ethDeviceGateway, ethDeviceMask string) error {
98+
func (u *Unikraft) configureUnikraftArgs(rootFsType, ethDeviceIP, ethDeviceGateway, ethDeviceMask string, ninePFSMntPoint string) error {
9999
setCompatArgs := func() {
100100
u.Net.Address = "netdev.ipv4_addr=" + ethDeviceIP
101101
u.Net.Gateway = "netdev.ipv4_gw_addr=" + ethDeviceGateway
102102
u.Net.Mask = "netdev.ipv4_subnet_mask=" + ethDeviceMask
103103
// TODO: We need to add support for actual block devices (e.g. virtio-blk)
104104
// and sharedfs or any other Unikraft related ways to pass data to guest.
105105
if rootFsType == "initrd" {
106-
u.VFS.RootFS = "vfs.rootfs=" + "initrd"
106+
u.VFS.VfsString = "vfs.rootfs=" + "initrd"
107107
} else {
108-
u.VFS.RootFS = ""
108+
u.VFS.VfsString = ""
109109
}
110110
}
111111

112112
setCurrentArgs := func() {
113+
u.VFS.VfsString += "vfs.fstab=[ "
113114
u.Net.Address = "netdev.ip=" + ethDeviceIP + "/24:" + ethDeviceGateway + ":8.8.8.8"
114115
// TODO: We need to add support for actual block devices (e.g. virtio-blk)
115116
// and sharedfs or any other Unikraft related ways to pass data to guest.
116117
if rootFsType == "initrd" {
117118
// TODO: This needs better handling. We need to revisit this
118119
// when we better understand all the available options for
119120
// passing info inside unikraft unikernels.
120-
u.VFS.RootFS = "vfs.fstab=[ \"initrd0:/:extract:::\" ]"
121-
} else {
122-
u.VFS.RootFS = ""
121+
u.VFS.VfsString += "\"initrd0:/:extract:::\""
122+
}
123+
if len(ninePFSMntPoint) > 0 {
124+
u.VFS.VfsString += "\"fs1:" + ninePFSMntPoint + ":9pfs:::mkmp\""
123125
}
126+
u.VFS.VfsString += " ]"
124127
}
125128

126129
if u.Version == "" {

pkg/unikontainers/unikontainers.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ func (u *Unikontainer) Exec() error {
154154
unikernelType := u.State.Annotations[annotType]
155155
unikernelVersion := u.State.Annotations[annotVersion]
156156

157+
var mountSpecs []hypervisors.MountSpec
158+
157159
// TODO: Remove this when we chroot
158160
unikernelPath, err := filepath.Rel("/", u.State.Annotations[annotBinary])
159161
if err != nil {
@@ -184,6 +186,7 @@ func (u *Unikontainer) Exec() error {
184186
BlockDevice: "",
185187
Seccomp: true, // Enable Seccomp by default
186188
MemSizeB: 0,
189+
NinePFSVols: []hypervisors.MountSpec{},
187190
Environment: os.Environ(),
188191
}
189192

@@ -276,6 +279,24 @@ func (u *Unikontainer) Exec() error {
276279
}
277280
}
278281

282+
// Handle custom support for 9pfs
283+
// If the POD specifies the magic 'com.urunc.unikernel.9pfsMntPoint, urunc
284+
// will search the list of Mounts in u.Spec and will determine the location
285+
// on the worker node. That location will then be passed to 9pfs to mount it
286+
// inside the unikernel.
287+
ninePFSMntPoint := u.Spec.Annotations[annotNinePFSMntPoint]
288+
if ninePFSMntPoint != "" {
289+
for _, v := range u.Spec.Mounts {
290+
if v.Destination == ninePFSMntPoint {
291+
mountSpec := hypervisors.MountSpec{Src: v.Source, Dst: v.Destination}
292+
// TODO: figure out if we can use a list of mount points or we should limit to just one
293+
mountSpecs = append(mountSpecs, mountSpec)
294+
unikernelParams.NinePFSMntPoint = ninePFSMntPoint
295+
}
296+
}
297+
vmmArgs.NinePFSVols = mountSpecs
298+
}
299+
279300
if unikernel.SupportsBlock() && vmmArgs.BlockDevice == "" && useDevmapper {
280301
rootFsDevice, err := getBlockDevice(rootfsDir)
281302
if err != nil {

0 commit comments

Comments
 (0)