mirror of
https://github.com/containerd/containerd.git
synced 2026-06-24 08:48:48 +00:00
Improve documentation for mount manager
Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
308
docs/mounts.md
308
docs/mounts.md
@@ -35,14 +35,55 @@ handle custom mount types.
|
||||
|
||||
The interface for a custom mount handler is very simple.
|
||||
|
||||
```.go
|
||||
```go
|
||||
type Handler interface {
|
||||
Mount(context.Context, Mount, string, []ActiveMount) (ActiveMount, error)
|
||||
Unmount(context.Context, string) error
|
||||
}
|
||||
```
|
||||
|
||||
### Mount formatting
|
||||
#### Built-in Mount Handlers
|
||||
|
||||
##### Loopback Handler
|
||||
|
||||
The loopback handler (`loop`) allows mounting files as loopback devices. This is useful
|
||||
for mounting disk images or filesystem images without requiring a pre-configured loopback device.
|
||||
It is preferable to use the `loop` option on mounts when supported by other mount types to allow
|
||||
other mount types to optimize when a loopback is needed, when this handler is used with
|
||||
another mount type it may force a loopback to be used even when not necessary.
|
||||
|
||||
```go
|
||||
// Example mount using loopback
|
||||
mount.Mount{
|
||||
Type: "loop",
|
||||
Source: "/path/to/disk.img",
|
||||
Options: []string{},
|
||||
}
|
||||
```
|
||||
|
||||
The handler automatically:
|
||||
- Sets up the loopback device using the first available loop device
|
||||
- Makes device available at mount point
|
||||
- Handles cleanup on unmount
|
||||
|
||||
### Mount Transformers
|
||||
|
||||
Mount transformers are interfaces that can modify mounts based on previous mount state.
|
||||
Transformers are useful for preparing mounts before they are activated, such as creating
|
||||
directories or formatting filesystems.
|
||||
|
||||
```go
|
||||
type Transformer interface {
|
||||
Transform(context.Context, Mount, []ActiveMount) (Mount, error)
|
||||
}
|
||||
```
|
||||
|
||||
Transformers are specified in the mount type using a prefix pattern: `<transformer>/<mount-type>`.
|
||||
Multiple transformers can be chained: `<transformer1>/<transformer2>/<mount-type>`.
|
||||
|
||||
#### Built-in Transformers
|
||||
|
||||
##### Format Transformer (`format/`)
|
||||
|
||||
In order to chain mounts together, the results from a previous mount may be
|
||||
needed for subsequent mounts. Some of these mount parameters may not be
|
||||
@@ -51,7 +92,7 @@ mount values. Formatted mounts allow providing templated values for mount
|
||||
parameters to be filled in at mount activation time using the previous
|
||||
mounts' results.
|
||||
|
||||
Formatted mounts have a type that starts with `format/` and following by the
|
||||
Formatted mounts have a type that starts with `format/` followed by the
|
||||
intended mount type after filling in format values.
|
||||
Uses templating based on [go templates](https://pkg.go.dev/text/template) to
|
||||
fill in values.
|
||||
@@ -70,11 +111,266 @@ Formatted mounts are handled differently than other custom mounts. If the
|
||||
resulting mount after formatting is a supported system mount, it does not
|
||||
need to be mounted by the mount handlers like custom mounts do.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
// First mount provides the lower layer
|
||||
mount.Mount{
|
||||
Type: "bind",
|
||||
Source: "/var/lib/containerd/snapshots/1",
|
||||
Options: []string{"ro"},
|
||||
},
|
||||
// Second mount uses formatting to reference the first mount
|
||||
mount.Mount{
|
||||
Type: "format/overlay",
|
||||
Source: "overlay",
|
||||
Options: []string{
|
||||
"lowerdir={{ mount 0 }}",
|
||||
"upperdir=/upper",
|
||||
"workdir=/work",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
##### Mkfs Transformer (`mkfs/`)
|
||||
|
||||
The mkfs transformer creates and formats filesystem images. It supports creating
|
||||
ext2, ext3, ext4, and xfs filesystems in files that can then be mounted as loopback devices.
|
||||
|
||||
Mount options with the `X-containerd.mkfs.` prefix are consumed by the transformer:
|
||||
|
||||
| Option | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `X-containerd.mkfs.size` | Size of the filesystem image (supports units like MiB, GiB) | `X-containerd.mkfs.size=100MiB` |
|
||||
| `X-containerd.mkfs.fs` | Filesystem type (ext2, ext3, ext4, xfs) | `X-containerd.mkfs.fs=ext4` |
|
||||
| `X-containerd.mkfs.uuid` | UUID for the filesystem | `X-containerd.mkfs.uuid=550e8400-e29b-41d4-a716-446655440000` |
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
mount.Mount{
|
||||
Type: "mkfs/loop",
|
||||
Source: "/path/to/disk.img",
|
||||
Options: []string{
|
||||
"X-containerd.mkfs.size=1GiB",
|
||||
"X-containerd.mkfs.fs=ext4",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Create a 1GiB file at `/path/to/disk.img`
|
||||
2. Format it as ext4
|
||||
3. Set up a loopback device for the file
|
||||
4. Return the loop device for subsequent mounting
|
||||
|
||||
##### Mkdir Transformer (`mkdir/`)
|
||||
|
||||
The mkdir transformer creates directories before mounting. This is useful for
|
||||
ensuring overlay upperdir and workdir directories exist, or for creating mount points.
|
||||
|
||||
Mount options with the `X-containerd.mkdir.` prefix are consumed by the transformer:
|
||||
|
||||
| Option Format | Description |
|
||||
|--------------|-------------|
|
||||
| `X-containerd.mkdir.path=<dir>` | Create directory with default permissions (0700) |
|
||||
| `X-containerd.mkdir.path=<dir>:<mode>` | Create directory with specified octal mode |
|
||||
| `X-containerd.mkdir.path=<dir>:<mode>:<uid>:<gid>` | Create directory with mode and ownership |
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
mount.Mount{
|
||||
Type: "format/mkdir/overlay",
|
||||
Source: "overlay",
|
||||
Options: []string{
|
||||
"X-containerd.mkdir.path={{ mount 0 }}/upper:0755",
|
||||
"X-containerd.mkdir.path={{ mount 0 }}/work:0755",
|
||||
"lowerdir={{ mount 1 }}",
|
||||
"upperdir={{ mount 0 }}/upper",
|
||||
"workdir={{ mount 0 }}/work",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Chaining Transformers
|
||||
|
||||
Transformers can be chained together to perform multiple operations in sequence:
|
||||
|
||||
```go
|
||||
mount.Mount{
|
||||
Type: "mkfs/loop",
|
||||
Source: "/data/fs.img",
|
||||
Options: []string{
|
||||
"X-containerd.mkfs.size=500MiB",
|
||||
"X-containerd.mkfs.fs=xfs",
|
||||
},
|
||||
},
|
||||
mount.Mount{
|
||||
Type: "xfs",
|
||||
Source: "{{ source 0 }}", // Loop device from previous mount
|
||||
Options: []string{},
|
||||
},
|
||||
mount.Mount{
|
||||
Type: "format/mkdir/overlay",
|
||||
Source: "overlay",
|
||||
Options: []string{
|
||||
"X-containerd.mkdir.path={{ mount 1 }}/upper:0755",
|
||||
"X-containerd.mkdir.path={{ mount 1 }}/work:0755",
|
||||
"lowerdir=/lower",
|
||||
"upperdir={{ mount 1 }}/upper",
|
||||
"workdir={{ mount 1 }}/work",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This example:
|
||||
1. Creates and formats a 500MiB XFS image
|
||||
2. Sets up a loop device and mounts the XFS filesystem
|
||||
3. Creates directories on the XFS filesystem and sets up an overlay
|
||||
|
||||
### Garbage Collection and Backreferences
|
||||
|
||||
The mount manager integrates with containerd's garbage collection system to ensure
|
||||
mounts are properly tracked and cleaned up. Mounts can reference other resources
|
||||
using special labels:
|
||||
|
||||
| Label | Description |
|
||||
|-------|-------------|
|
||||
| `containerd.io/gc.bref.container.*` | Back reference to a container |
|
||||
| `containerd.io/gc.bref.content.*` | Back reference to content in the content store |
|
||||
| `containerd.io/gc.bref.image.*` | Back reference to an image |
|
||||
| `containerd.io/gc.bref.snapshot.*` | Back reference to a snapshot |
|
||||
|
||||
The `.*` suffix allows for named backreferences separated by `.` or `/`.
|
||||
|
||||
**Example:**
|
||||
```go
|
||||
info, err := mountManager.Activate(ctx, "my-mount", mounts,
|
||||
mount.WithLabels(map[string]string{
|
||||
"containerd.io/gc.bref.container": "container-id-123",
|
||||
"containerd.io/gc.bref.snapshot.overlayfs": "active-snapshot-key",
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
These labels ensure that the mount won't be garbage collected while the
|
||||
referenced resources still exist, and the mount will be automatically cleaned
|
||||
up when the references are removed.
|
||||
|
||||
### Relationship with runtimes
|
||||
|
||||
The runtime should use the mount manager to initiate activation of the mounts
|
||||
before setting up the rootfs for a container. The runtime name should be passed
|
||||
along to the activation call so that the mount manager may be configured for
|
||||
runtime specific behavior. For example, a runtime is able to understand
|
||||
formatting or specific mount types, then the mount manager can avoid performing
|
||||
those mounts and let the runtime handle it.
|
||||
runtime specific behavior.
|
||||
|
||||
The `ActivateOptions` allow runtimes to indicate which mount types they can handle:
|
||||
|
||||
```go
|
||||
// Runtime can handle formatting, so don't let mount manager do it
|
||||
info, err := mountManager.Activate(ctx, name, mounts,
|
||||
mount.WithAllowMountType("format/*"),
|
||||
)
|
||||
|
||||
// Runtime can handle loop devices
|
||||
info, err := mountManager.Activate(ctx, name, mounts,
|
||||
mount.WithAllowMountType("loop"),
|
||||
)
|
||||
```
|
||||
|
||||
#### Support with containerd shims
|
||||
|
||||
By default, the containerd runtime will call the mount manager to activate mounts,
|
||||
which will perform any transformations and custom mounts. However, a runtime shim may
|
||||
choose to handle some mount types or transformations itself in order to optimize
|
||||
performance based on the runtime environment. For example, a VM based runtime may
|
||||
choose to handle loopback mounts itself by passing the disk image file directly to
|
||||
the VM instead of setting up a loop device on the host. The runtime shim may export
|
||||
the annotation `containerd.io/runtime-allow-mounts` in its runtime info to indicate
|
||||
which mount types the shim can handle. The values are comma separated and passed to
|
||||
via the `mount.WithAllowMountType` option when activating mounts.
|
||||
|
||||
### Mount Manager Interface
|
||||
|
||||
The complete mount manager interface:
|
||||
|
||||
```go
|
||||
type Manager interface {
|
||||
Activate(context.Context, string, []Mount, ...ActivateOpt) (ActivationInfo, error)
|
||||
Deactivate(context.Context, string) error
|
||||
Info(context.Context, string) (ActivationInfo, error)
|
||||
Update(context.Context, ActivationInfo, ...string) (ActivationInfo, error)
|
||||
List(context.Context, ...string) ([]ActivationInfo, error)
|
||||
}
|
||||
```
|
||||
|
||||
**Methods:**
|
||||
- `Activate`: Activate a set of mounts with a unique name
|
||||
- `Deactivate`: Unmount and cleanup an activation
|
||||
- `Info`: Get information about an active mount
|
||||
- `Update`: Update an active mount (not yet implemented)
|
||||
- `List`: List all active mounts, optionally filtered
|
||||
|
||||
**ActivationInfo** contains:
|
||||
- `Name`: Unique identifier for the activation
|
||||
- `Active`: Mounts that were handled by the mount manager
|
||||
- `System`: Remaining mounts that must be performed by the system/runtime
|
||||
- `Labels`: Labels associated with the activation
|
||||
|
||||
### Storage and Persistence
|
||||
|
||||
The mount manager stores activation state in a BoltDB database and maintains
|
||||
mount targets in a dedicated directory. This provides:
|
||||
|
||||
- Crash recovery: Mounts can be tracked and cleaned up after daemon restart
|
||||
- Garbage collection: Integration with containerd's GC system
|
||||
- Lease support: Mounts can be associated with leases for lifecycle management
|
||||
|
||||
### Example Usage
|
||||
|
||||
```go
|
||||
// Initialize mount manager
|
||||
mm, err := manager.NewManager(
|
||||
db,
|
||||
targetDir,
|
||||
manager.WithMountHandler("loop", mount.LoopbackHandler()),
|
||||
)
|
||||
|
||||
// Create mounts for a writable overlay with custom block device
|
||||
mounts := []mount.Mount{
|
||||
{
|
||||
Type: "mkfs/loop",
|
||||
Source: "/data/writable.img",
|
||||
Options: []string{
|
||||
"X-containerd.mkfs.size=1GiB",
|
||||
"X-containerd.mkfs.fs=ext4",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "ext4",
|
||||
Source: "{{ source 0 }}",
|
||||
},
|
||||
{
|
||||
Type: "mkdir/format/overlay",
|
||||
Source: "overlay",
|
||||
Options: []string{
|
||||
"X-containerd.mkdir.path={{ mount 1 }}/upper:0755",
|
||||
"X-containerd.mkdir.path={{ mount 1 }}/work:0755",
|
||||
"lowerdir=/snapshots/base",
|
||||
"upperdir={{ mount 1 }}/upper",
|
||||
"workdir={{ mount 1 }}/work",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Activate with lease and backreference
|
||||
info, err := mm.Activate(ctx, "container-123-rootfs", mounts,
|
||||
mount.WithLabels(map[string]string{
|
||||
"containerd.io/gc.bref.container": "container-123",
|
||||
}),
|
||||
)
|
||||
|
||||
// info.Active contains mounts handled by mount manager
|
||||
// info.System contains remaining mounts to perform in container namespace
|
||||
|
||||
// Later, cleanup
|
||||
err = mm.Deactivate(ctx, "container-123-rootfs")
|
||||
```
|
||||
Reference in New Issue
Block a user