diff --git a/cmd/moby/build.go b/cmd/moby/build.go index 66b91a0da..93da8d180 100644 --- a/cmd/moby/build.go +++ b/cmd/moby/build.go @@ -35,7 +35,37 @@ func (o *outputList) Set(value string) error { } var streamable = map[string]bool{ - "tar": true, + "docker": true, + "tar": true, +} + +type addFun func(*tar.Writer) error + +const dockerfile = ` +FROM scratch + +COPY . ./ +RUN rm -f Dockerfile + +ENTRYPOINT ["/sbin/tini", "--", "/bin/rc.init"] +` + +var additions = map[string]addFun{ + "docker": func(tw *tar.Writer) error { + log.Infof(" Adding Dockerfile") + hdr := &tar.Header{ + Name: "Dockerfile", + Mode: 0644, + Size: int64(len(dockerfile)), + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := tw.Write([]byte(dockerfile)); err != nil { + return err + } + return nil + }, } // Process the build arguments and execute build @@ -127,6 +157,7 @@ func build(args []string) { } var outputFile *os.File + var addition addFun if *buildOutputFile != "" { if len(buildOut) > 1 { log.Fatal("The -output option can only be specified when generating a single output format") @@ -150,6 +181,7 @@ func build(args []string) { } defer outputFile.Close() } + addition = additions[buildOut[0]] } size, err := getDiskSizeMB(*buildSize) @@ -194,7 +226,7 @@ func build(args []string) { buf = new(bytes.Buffer) w = buf } - buildInternal(moby, w, *buildPull) + buildInternal(moby, w, *buildPull, addition) if outputFile == nil { image := buf.Bytes() @@ -272,7 +304,7 @@ func enforceContentTrust(fullImageName string, config *TrustConfig) bool { // Perform the actual build process // TODO return error not panic -func buildInternal(m Moby, w io.Writer, pull bool) { +func buildInternal(m Moby, w io.Writer, pull bool, addition addFun) { iw := tar.NewWriter(w) if m.Kernel.Image != "" { @@ -341,6 +373,15 @@ func buildInternal(m Moby, w io.Writer, pull bool) { if err != nil { log.Fatalf("failed to add filesystem parts: %v", err) } + + // add anything additional for this output type + if addition != nil { + err = addition(iw) + if err != nil { + log.Fatalf("Failed to add additional files") + } + } + err = iw.Close() if err != nil { log.Fatalf("initrd close error: %v", err) diff --git a/cmd/moby/linuxkit.go b/cmd/moby/linuxkit.go index e4d2a9f1b..03169ef8a 100644 --- a/cmd/moby/linuxkit.go +++ b/cmd/moby/linuxkit.go @@ -60,7 +60,7 @@ func ensureLinuxkitImage(name string) error { } // TODO pass through --pull to here buf := new(bytes.Buffer) - buildInternal(m, buf, false) + buildInternal(m, buf, false, nil) image := buf.Bytes() kernel, initrd, cmdline, err := tarToInitrd(image) if err != nil { diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..d7463c5d0 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,23 @@ +Examples of building an image to run on LinuxKit or on a host + +Currently the `moby` tool can output formats suitable for LinuxKit to boot on +a VM, and also some formats for running natively. + +The `docker` format adds a `Dockerfile` to the tarball and expects a similar +file structure to `LinuxKit` but with the low level system setup pieces removed +as this will already be set up in a container. + +The `mobytest/init-container` image in this repository has an example setup that +initialises `containerd` in exactly the same way as it runs in LinuxKit, which will +then start the `onboot` and `service` containers. The example below shows how you +can run `nginx` on either of these base configs. + +``` +moby build -output docker -o - docker.yml nginx.yml | docker build -t dockertest - +docker run -d -p 80:80 --privileged dockertest + +moby build -output kernel+initrd linuxkit.yml nginx.yml +linuxkit run nginx +``` + +Both of these will run the same `nginx` either in a VM or a container. diff --git a/examples/docker.yml b/examples/docker.yml new file mode 100644 index 000000000..32186c048 --- /dev/null +++ b/examples/docker.yml @@ -0,0 +1,9 @@ +init: + - mobytest/init-container:b5de246b2790d74d53bae583a95c87869ca003e6 + - linuxkit/runc:3a4e6cbf15470f62501b019b55e1caac5ee7689f + - linuxkit/containerd:5749f2e9e65395cc6635229e8da0e0d484320ddf + - linuxkit/ca-certificates:75cf419fb58770884c3464eb687ec8dfc704169d +trust: + org: + - linuxkit + - mobytest diff --git a/examples/linuxkit.yml b/examples/linuxkit.yml new file mode 100644 index 000000000..80a462b72 --- /dev/null +++ b/examples/linuxkit.yml @@ -0,0 +1,18 @@ +kernel: + image: "linuxkit/kernel:4.9.x" + cmdline: "console=ttyS0" +init: + - linuxkit/init:1b8a7e394d2ec2f1fdb4d67645829d1b5bdca037 + - linuxkit/runc:3a4e6cbf15470f62501b019b55e1caac5ee7689f + - linuxkit/containerd:5749f2e9e65395cc6635229e8da0e0d484320ddf + - linuxkit/ca-certificates:75cf419fb58770884c3464eb687ec8dfc704169d +onboot: + - name: dhcpcd + image: "linuxkit/dhcpcd:7d2b8aaaf20c24ad7d11a5ea2ea5b4a80dc966f1" + command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] +services: + - name: rngd + image: "linuxkit/rngd:1fa4de44c961bb5075647181891a3e7e7ba51c31" +trust: + org: + - linuxkit diff --git a/examples/nginx.yml b/examples/nginx.yml new file mode 100644 index 000000000..e5405c3cd --- /dev/null +++ b/examples/nginx.yml @@ -0,0 +1,12 @@ +services: + - name: nginx + image: "nginx:alpine" + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_CHOWN + - CAP_SETUID + - CAP_SETGID + - CAP_DAC_OVERRIDE +trust: + org: + - library diff --git a/pkg/init-container/Dockerfile b/pkg/init-container/Dockerfile new file mode 100644 index 000000000..08f620890 --- /dev/null +++ b/pkg/init-container/Dockerfile @@ -0,0 +1,14 @@ +FROM linuxkit/alpine:630ee558e4869672fae230c78364e367b8ea67a9 AS mirror +RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ +RUN apk add --no-cache --initdb -p /out alpine-baselayout busybox musl tini + +# Remove apk residuals. We have a read-only rootfs, so apk is of no use. +RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache + +FROM scratch +ENTRYPOINT [] +CMD [] +WORKDIR / +COPY --from=mirror /out/ / +COPY etc etc/ +COPY bin bin/ diff --git a/pkg/init-container/Makefile b/pkg/init-container/Makefile new file mode 100644 index 000000000..0a4d78637 --- /dev/null +++ b/pkg/init-container/Makefile @@ -0,0 +1,15 @@ +.PHONY: tag push +default: push + +ORG?=mobytest +IMAGE=init-container +DEPS=Dockerfile $(wildcard etc/init.d/*) $(wildcard bin/*) + +HASH?=$(shell git ls-tree HEAD -- ../$(notdir $(CURDIR)) | awk '{print $$3}') + +tag: $(DEPS) + docker build --no-cache --network=none -t $(ORG)/$(IMAGE):$(HASH) . + +push: tag + DOCKER_CONTENT_TRUST=1 docker pull $(ORG)/$(IMAGE):$(HASH) || \ + DOCKER_CONTENT_TRUST=1 docker push $(ORG)/$(IMAGE):$(HASH) diff --git a/pkg/init-container/bin/rc.init b/pkg/init-container/bin/rc.init new file mode 100755 index 000000000..77d898a42 --- /dev/null +++ b/pkg/init-container/bin/rc.init @@ -0,0 +1,10 @@ +#!/bin/sh + +# execute other init processes +INITS="$(find /etc/init.d -type f | sort)" +for f in $INITS +do + $f & +done + +wait diff --git a/pkg/init-container/etc/init.d/010-containerd b/pkg/init-container/etc/init.d/010-containerd new file mode 100755 index 000000000..9fcccb64f --- /dev/null +++ b/pkg/init-container/etc/init.d/010-containerd @@ -0,0 +1,46 @@ +#!/bin/sh + +# set global ulimits TODO move to /etc/limits.conf +ulimit -n 1048576 +ulimit -p unlimited + +# bring up containerd +printf "\nStarting containerd\n" +/usr/bin/containerd & + +# wait for socket to be there +while [ ! -S /run/containerd/containerd.sock ] +do + sleep 0.1 +done + +# start onboot containers, run to completion + +if [ -d /containers/onboot ] +then + for f in $(find /containers/onboot -mindepth 1 -maxdepth 1 | sort) + do + base="$(basename $f)" + #/bin/mount --bind "$f/rootfs" "$f/rootfs" + #mount -o remount,rw "$f/rootfs" + /usr/bin/runc run --bundle "$f" "$(basename $f)" + printf " - $base\n" + done +fi + +# start service containers + +if [ -d /containers/services ] +then + for f in $(find /containers/services -mindepth 1 -maxdepth 1 | sort) + do + base="$(basename $f)" + #/bin/mount --bind "$f/rootfs" "$f/rootfs" + #mount -o remount,rw "$f/rootfs" + log="/var/log/$base.log" + ctr run --runtime-config "$f/config.json" --rootfs "$f/rootfs" --id "$(basename $f)" $log >$log & + printf " - $base\n" + done +fi + +wait