Skip to content

Commit 12331ed

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 12df4aa commit 12331ed

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
@@ -95,6 +95,12 @@ func (q *Qemu) Execve(args ExecArgs, ukernel unikernels.Unikernel) error {
9595
if args.InitrdPath != "" {
9696
cmdString += " -initrd " + args.InitrdPath
9797
}
98+
99+
if len(args.NinePFSVols) > 0 {
100+
cmdString += " -fsdev local,id=p9idfs1,path=" + args.NinePFSVols[0].Src + ",security_model=mapped-xattr"
101+
cmdString += " -device virtio-9p-pci,fsdev=p9idfs1,mount_tag=fs1"
102+
}
103+
98104
cmdString += ukernel.MonitorCli(qemuString)
99105
exArgs := strings.Split(cmdString, " ")
100106
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
@@ -38,6 +38,7 @@ type UnikernelParams struct {
3838
RootFSType string // The rootfs type of the Unikernel
3939
BlockMntPoint string // The mount point for the block device
4040
Version string // The version of the unikernel
41+
NinePFSMntPoint string // Mount point inside unikernel of the 9pfs theshared volume
4142
}
4243

4344
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
@@ -44,7 +44,7 @@ type UnikraftNet struct {
4444
}
4545

4646
type UnikraftVFS struct {
47-
RootFS string
47+
VfsString string
4848
}
4949

5050
func (u *Unikraft) CommandString() (string, error) {
@@ -59,7 +59,7 @@ func (u *Unikraft) CommandString() (string, error) {
5959
u.Net.Address,
6060
u.Net.Gateway,
6161
u.Net.Mask,
62-
u.VFS.RootFS,
62+
u.VFS.VfsString,
6363
u.Command), nil
6464
}
6565

@@ -103,35 +103,38 @@ func (u *Unikraft) Init(data UnikernelParams) error {
103103
u.Command = strings.Join(data.CmdLine[1:], " ")
104104
}
105105

106-
return u.configureUnikraftArgs(data.RootFSType, data.EthDeviceIP, data.EthDeviceGateway, data.EthDeviceMask)
106+
return u.configureUnikraftArgs(data.RootFSType, data.EthDeviceIP, data.EthDeviceGateway, data.EthDeviceMask, data.NinePFSMntPoint)
107107
}
108108

109-
func (u *Unikraft) configureUnikraftArgs(rootFsType, ethDeviceIP, ethDeviceGateway, ethDeviceMask string) error {
109+
func (u *Unikraft) configureUnikraftArgs(rootFsType, ethDeviceIP, ethDeviceGateway, ethDeviceMask string, ninePFSMntPoint string) error {
110110
setCompatArgs := func() {
111111
u.Net.Address = "netdev.ipv4_addr=" + ethDeviceIP
112112
u.Net.Gateway = "netdev.ipv4_gw_addr=" + ethDeviceGateway
113113
u.Net.Mask = "netdev.ipv4_subnet_mask=" + ethDeviceMask
114114
// TODO: We need to add support for actual block devices (e.g. virtio-blk)
115115
// and sharedfs or any other Unikraft related ways to pass data to guest.
116116
if rootFsType == "initrd" {
117-
u.VFS.RootFS = "vfs.rootfs=" + "initrd"
117+
u.VFS.VfsString = "vfs.rootfs=" + "initrd"
118118
} else {
119-
u.VFS.RootFS = ""
119+
u.VFS.VfsString = ""
120120
}
121121
}
122122

123123
setCurrentArgs := func() {
124+
u.VFS.VfsString += "vfs.fstab=[ "
124125
u.Net.Address = "netdev.ip=" + ethDeviceIP + "/24:" + ethDeviceGateway + ":8.8.8.8"
125126
// TODO: We need to add support for actual block devices (e.g. virtio-blk)
126127
// and sharedfs or any other Unikraft related ways to pass data to guest.
127128
if rootFsType == "initrd" {
128129
// TODO: This needs better handling. We need to revisit this
129130
// when we better understand all the available options for
130131
// passing info inside unikraft unikernels.
131-
u.VFS.RootFS = "vfs.fstab=[ \"initrd0:/:extract:::\" ]"
132-
} else {
133-
u.VFS.RootFS = ""
132+
u.VFS.VfsString += "\"initrd0:/:extract:::\""
133+
}
134+
if len(ninePFSMntPoint) > 0 {
135+
u.VFS.VfsString += "\"fs1:" + ninePFSMntPoint + ":9pfs:::mkmp\""
134136
}
137+
u.VFS.VfsString += " ]"
135138
}
136139

137140
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)