Commit 05f62554 authored by Akihiro Suda's avatar Akihiro Suda Committed by Brad Davidson

add fuse-overlayfs snapshotter (mainly for rootless mode)

Ubuntu and Debian kernels support mounting real overlayfs inside userns, but the vanilla kernel still does not allow it. OTOH fuse-overlayfs can be mounted inside userns with the vanilla kernel (>= 4.18). Signed-off-by: 's avatarAkihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
parent 43f7eaed
......@@ -61,6 +61,7 @@ replace (
)
require (
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
......
......@@ -13,6 +13,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0 h1:LhS8BiMh7ULa6zkkF5XI6piLV5XVTR7mSm9j3hTUB/k=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8=
github.com/Azure/azure-sdk-for-go v43.0.0+incompatible h1:/wSNCu0e6EsHFR4Qa3vBEBbicaprEHMyyga9g8RTULI=
github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
......
......@@ -19,6 +19,7 @@
package containerd
import (
_ "github.com/AkihiroSuda/containerd-fuse-overlayfs/plugin"
_ "github.com/containerd/containerd/metrics/cgroups"
_ "github.com/containerd/containerd/runtime/v1/linux"
_ "github.com/containerd/containerd/runtime/v2"
......
ARG FUSEOVERLAYFS_COMMIT=v1.2.0
ARG ROOTLESSKIT_COMMIT=v0.11.0
ARG SHADOW_COMMIT=4.8.1
FROM golang:1.15-alpine AS containerd-fuse-overlayfs-test
COPY . /go/src/github.com/AkihiroSuda/containerd-fuse-overlayfs
WORKDIR /go/src/github.com/AkihiroSuda/containerd-fuse-overlayfs
ENV CGO_ENABLED=0
ENV GO111MODULE=off
RUN mkdir /out && go test -c -o /out/containerd-fuse-overlayfs.test
# from https://github.com/containers/fuse-overlayfs/blob/53c17dab78b43de1cd121bf9260b20b76371bbaf/Dockerfile.static.ubuntu
FROM docker.io/debian:10 AS fuse-overlayfs
RUN apt-get update && \
apt-get install --no-install-recommends -y \
git ca-certificates libc6-dev gcc g++ make automake autoconf clang pkgconf libfuse3-dev
ARG FUSEOVERLAYFS_REPO
RUN git clone https://github.com/containers/fuse-overlayfs
WORKDIR fuse-overlayfs
ARG FUSEOVERLAYFS_COMMIT
RUN git pull && git checkout ${FUSEOVERLAYFS_COMMIT}
RUN ./autogen.sh && \
LIBS="-ldl" LDFLAGS="-static" ./configure && \
make && mkdir /out && cp fuse-overlayfs /out
FROM golang:1.15-alpine AS rootlesskit
RUN apk add --no-cache git
RUN git clone https://github.com/rootless-containers/rootlesskit.git /go/src/github.com/rootless-containers/rootlesskit
WORKDIR /go/src/github.com/rootless-containers/rootlesskit
ARG ROOTLESSKIT_COMMIT
RUN git pull && git checkout ${ROOTLESSKIT_COMMIT}
ENV CGO_ENABLED=0
RUN mkdir /out && go build -o /out/rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit
FROM alpine:3.12 AS idmap
RUN apk add --no-cache autoconf automake build-base byacc gettext gettext-dev gcc git libcap-dev libtool libxslt
RUN git clone https://github.com/shadow-maint/shadow.git
WORKDIR shadow
ARG SHADOW_COMMIT
RUN git pull && git checkout ${SHADOW_COMMIT}
RUN ./autogen.sh --disable-nls --disable-man --without-audit --without-selinux --without-acl --without-attr --without-tcb --without-nscd && \
make && mkdir -p /out && cp src/newuidmap src/newgidmap /out
FROM alpine:3.12
COPY --from=containerd-fuse-overlayfs-test /out/containerd-fuse-overlayfs.test /usr/local/bin
COPY --from=rootlesskit /out/rootlesskit /usr/local/bin
COPY --from=fuse-overlayfs /out/fuse-overlayfs /usr/local/bin
COPY --from=idmap /out/newuidmap /usr/bin/newuidmap
COPY --from=idmap /out/newgidmap /usr/bin/newgidmap
RUN apk add --no-cache fuse3 libcap && \
setcap CAP_SETUID=ep /usr/bin/newuidmap && \
setcap CAP_SETGID=ep /usr/bin/newgidmap && \
adduser -D -u 1000 testuser && \
echo testuser:100000:65536 | tee /etc/subuid | tee /etc/subgid
USER testuser
# If /tmp is real overlayfs, some tests fail. Mount a volume to ensure /tmp to be a sane filesystem.
VOLUME /tmp
# requires --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/fuse
CMD ["rootlesskit", "containerd-fuse-overlayfs.test", "-test.root", "-test.v"]
DESTDIR ?= /usr/local
bin/containerd-fuse-overlayfs-grpc:
go build -o $@ ./cmd/containerd-fuse-overlayfs-grpc
install:
install bin/containerd-fuse-overlayfs-grpc $(DESTDIR)/bin
uninstall:
rm -f $(DESTDIR)/bin/containerd-fuse-overlayfs-grpc
clean:
rm -rf bin
test:
DOCKER_BUILDKIT=1 docker build -t containerd-fuse-overlayfs-test .
docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/fuse containerd-fuse-overlayfs-test
docker rmi containerd-fuse-overlayfs-test
_test:
go test -exec rootlesskit -test.v -test.root
.PHONY: bin/containerd-fuse-overlayfs-grpc install uninstall clean test _test
# [`fuse-overlayfs`](https://github.com/containers/fuse-overlayfs) snapshotter plugin for [containerd](https://containerd.io)
Unlike `overlayfs`, `fuse-overlayfs` can be used as a non-root user without patching the kernel.
## Requirements
* kernel >= 4.18
* containerd >= 1.4
* fuse-overlayfs >= 0.7.0
## Setup
Two installation options are supported:
1. Embed `fuse-overlayfs` plugin into the containerd binary
2. Execute `fuse-overlayfs` plugin as a separate binary
Choose 1 if you don't mind recompiling containerd, otherwise choose 2.
### Option 1: Embed `fuse-overlayfs` plugin into the containerd binary
Create `builtins_fuseoverlayfs_linux.go` under [`$GOPATH/src/github.com/containerd/containerd/cmd/containerd`](https://github.com/containerd/containerd/tree/master/cmd/containerd)
with the following content, and recompile the containerd binary:
```go
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
// NOTE: the package name was "github.com/AkihiroSuda/containerd-fuse-overlayfs" before v1.0.0
import _ "github.com/AkihiroSuda/containerd-fuse-overlayfs/plugin"
```
No extra configuration is needed.
See https://github.com/containerd/containerd/blob/master/docs/rootless.md for how to run containerd as a non-root user.
### Option 2: Execute `fuse-overlayfs` plugin as a separate binary
* Install `containerd-fuse-overlayfs-grpc` binary. The binary will be installed under `$DESTDIR/bin`.
```console
$ make && DESTDIR=$HOME make install
```
* Create the following configuration in `~/.config/containerd/config.toml`:
```toml
version = 2
# substitute "/home/suda" with your own $HOME
root = "/home/suda/.local/share/containerd"
# substitute "/run/user/1001" with your own $XDG_RUNTIME_DIR
state = "/run/user/1001/containerd"
[grpc]
address = "/run/user/1001/containerd/containerd.sock"
[proxy_plugins]
[proxy_plugins."fuse-overlayfs"]
type = "snapshot"
address = "/run/user/1001/containerd/fuse-overlayfs.sock"
```
* Start [RootlessKit](https://github.com/rootless-containers/rootlesskit) with `sleep infinity` (or any kind of "pause" command):
```console
$ rootlesskit \
--net=slirp4netns --disable-host-loopback \
--copy-up=/etc --copy-up=/run \
--state-dir=$XDG_RUNTIME_DIR/rootlesskit-containerd \
sh -c "rm -rf /run/containerd ; sleep infinity"
```
(Note: `rm -rf /run/containerd` is a workaround for [containerd/containerd#2767](https://github.com/containerd/containerd/issues/2767))
* Enter the RootlessKit namespaces and run `containerd-fuse-overlayfs-grpc`:
```console
$ nsenter -U --preserve-credentials -m -n -t $(cat $XDG_RUNTIME_DIR/rootlesskit-containerd/child_pid) \
containerd-fuse-overlayfs-grpc $XDG_RUNTIME_DIR/containerd/fuse-overlayfs.sock $HOME/.local/share/containerd-fuse-overlayfs
```
* Enter the same namespaces and run `containerd`:
```console
$ nsenter -U --preserve-credentials -m -n -t $(cat $XDG_RUNTIME_DIR/rootlesskit-containerd/child_pid) \
containerd -c $HOME/.config/containerd/config.toml
```
## Usage
```console
$ export CONTAINERD_SNAPSHOTTER=fuse-overlayfs
$ ctr pull ...
$ ctr run ...
```
## How to test
To run the test as a non-root user, [RootlessKit](https://github.com/rootless-containers/rootlesskit) needs to be installed.
```console
$ go test -exec rootlesskit -test.v -test.root
```
// +build linux
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fuseoverlayfs
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/pkg/errors"
)
// supportsReadonlyMultipleLowerDir checks if read-only multiple lowerdirs can be mounted with fuse-overlayfs.
// https://github.com/containers/fuse-overlayfs/pull/133
func supportsReadonlyMultipleLowerDir(d string) error {
td, err := ioutil.TempDir(d, "fuseoverlayfs-check")
if err != nil {
return err
}
defer func() {
if err := os.RemoveAll(td); err != nil {
log.L.WithError(err).Warnf("Failed to remove check directory %v", td)
}
}()
for _, dir := range []string{"lower1", "lower2", "merged"} {
if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
return err
}
}
opts := []string{fmt.Sprintf("lowerdir=%s:%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"))}
m := mount.Mount{
Type: "fuse3." + fuseoverlayfsBinary,
Source: "overlay",
Options: opts,
}
dest := filepath.Join(td, "merged")
if err := m.Mount(dest); err != nil {
return errors.Wrapf(err, "failed to mount fuse-overlayfs (%+v) on %s", m, dest)
}
if err := mount.UnmountAll(dest, 0); err != nil {
log.L.WithError(err).Warnf("Failed to unmount check directory %v", dest)
}
return nil
}
// Supported returns nil when the overlayfs is functional on the system with the root directory.
// Supported is not called during plugin initialization, but exposed for downstream projects which uses
// this snapshotter as a library.
func Supported(root string) error {
if _, err := exec.LookPath(fuseoverlayfsBinary); err != nil {
return errors.Wrapf(err, "%s not installed", fuseoverlayfsBinary)
}
if err := os.MkdirAll(root, 0700); err != nil {
return err
}
if err := supportsReadonlyMultipleLowerDir(root); err != nil {
return errors.Wrap(err, "fuse-overlayfs not functional, make sure running with kernel >= 4.18")
}
return nil
}
module github.com/AkihiroSuda/containerd-fuse-overlayfs
go 1.13
require (
github.com/Microsoft/hcsshim v0.8.9 // indirect
github.com/containerd/containerd v1.4.0
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe
github.com/containerd/ttrpc v1.0.1 // indirect
github.com/containerd/typeurl v1.0.1 // indirect
github.com/coreos/go-systemd/v22 v22.1.0
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/golang/protobuf v1.3.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.6.0 // indirect
go.etcd.io/bbolt v1.3.5 // indirect
google.golang.org/grpc v1.27.1
gotest.tools/v3 v3.0.2 // indirect
)
// +build linux
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fuseoverlayfs
import (
"errors"
fuseoverlayfs "github.com/AkihiroSuda/containerd-fuse-overlayfs"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
)
// Config represents configuration for the fuse-overlayfs plugin.
type Config struct {
// Root directory for the plugin
RootPath string `toml:"root_path"`
}
func init() {
plugin.Register(&plugin.Registration{
Type: plugin.SnapshotPlugin,
ID: "fuse-overlayfs",
Config: &Config{},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
config, ok := ic.Config.(*Config)
if !ok {
return nil, errors.New("invalid fuse-overlayfs configuration")
}
root := ic.Root
if config.RootPath != "" {
root = config.RootPath
}
ic.Meta.Exports["root"] = root
return fuseoverlayfs.NewSnapshotter(root)
},
})
}
# cloud.google.com/go v0.51.0
cloud.google.com/go/compute/metadata
# github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0
## explicit
github.com/AkihiroSuda/containerd-fuse-overlayfs
github.com/AkihiroSuda/containerd-fuse-overlayfs/plugin
# github.com/Azure/azure-sdk-for-go v43.0.0+incompatible
github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute
github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2020-04-01/containerservice
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment