Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Docker API for compatibility #798

Open
mniak opened this issue Feb 10, 2022 · 15 comments
Open

Idea: Docker API for compatibility #798

mniak opened this issue Feb 10, 2022 · 15 comments
Labels
enhancement New feature or request

Comments

@mniak
Copy link

mniak commented Feb 10, 2022

Hi, I really love this project and have been using it in conjunction with lima.

But lately I had to get back to other alternatives that expose the docker API.
It happens that some tools (like VS Code Remote Containers, Testcontainer framework) need to communicate via API, tipically the docker.sock unix socket.

I have an Idea of creating a tool that exposes an API that behaves the same as the normal docker API.
It could use nerdctl as a library (not sure if currently possible) or maybe try to use it as a subprocess.

I would like to ask if someone has heard of a simmilar project I could use/contribute instead of starting a new one from scratch.

Also, if you could point me in the direction of which way to integrate with nerdctl is the best one in your opinion (as subprocess, as library, some kind of inter-process commuication, etc.)

@AkihiroSuda AkihiroSuda added the enhancement New feature or request label Feb 10, 2022
@AkihiroSuda
Copy link
Member

Also, if you could point me in the direction of which way to integrate with nerdctl is the best one in your opinion (as subprocess, as library, some kind of inter-process commuication, etc.)

As subprocesses.

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 5, 2022

At first I thought this idea was a bit crazy, first remove dockerd - only to add a limited version of it back again ?

But it could actually be quite handy, to have a nerdctl.sock that would accept a subset of the API commands.

I would like to ask if someone has heard of a simmilar project I could use/contribute instead of starting a new one from scratch.

@mniak: Except for moby and libpod, I don't know of one. And I don't think either of the other ones are appropriate here.

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 5, 2022

Here was something that I hacked together, this server is able to answer a docker version call using 1.12 API:

package main

import (
	"log"
	"net/http"
	"os/exec"
	"runtime"
	"strings"

	"github.com/gin-gonic/gin"
)

func nerdctlVersion() string {
	nv, err := exec.Command("nerdctl", "--version").Output()
	if err != nil {
		log.Fatal(err)
	}
	v := strings.TrimSuffix(string(nv), "\n")
	v = strings.Replace(v, "nerdctl version ", "", 1)
	return v
}

func setupRouter() *gin.Engine {
	r := gin.Default()

	r.GET("/_ping", func(c *gin.Context) {
		c.Writer.Header().Set("API-Version", "1.24")
		c.Writer.Header().Set("Content-Type", "text/plain")
		c.String(http.StatusOK, "OK")
	})

	r.GET("/v1.24/version", func(c *gin.Context) {
		var ver struct {
			Version       string
			APIVersion    string `json:"ApiVersion"`
			GitCommit     string
			GoVersion     string
			Os            string
			Arch          string
			KernelVersion string `json:",omitempty"`
			Experimental  bool   `json:",omitempty"`
			BuildTime     string `json:",omitempty"`
		}
		ver.Version = nerdctlVersion()
		ver.APIVersion = "1.24"
		ver.GoVersion = runtime.Version()
		ver.Os = runtime.GOOS
		ver.Arch = runtime.GOARCH
		ver.Experimental = true
		c.Writer.Header().Set("Content-Type", "application/json")
		c.JSON(http.StatusOK, ver)
	})

	return r
}

func main() {
	r := setupRouter()
	//r.Run(":2375")
	r.RunUnix("nerdctl.sock")
}

I used https://gin-gonic.com/ which was easy to get started with. It also supports the required JSON shuffling.

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 5, 2022

Seems to be mostly an exercise in converting between fuzzy human formats, like dates and sizes.

e.g. nerdctl images --format '{{json .}}'

{"CreatedAt":"2021-12-26 15:51:20 +0100 CET","CreatedSince":"2 months ago","Digest":"sha256:b5cfd4befc119a590ca1a81d6bb0fa1fb19f1fbebd0397f25fae164abe1e8a6a","ID":"b5cfd4befc11","Repository":"busybox","Tag":"latest","Size":"1.3 MiB","Platform":"linux/amd64"}

https://github.com/docker/engine-api/blob/v0.3.3/types/types.go#L88 (for the ancient version used)

// Image contains response of Remote API:
// GET "/images/json"
type Image struct {
        ID          string `json:"Id"`
        ParentID    string `json:"ParentId"`
        RepoTags    []string
        RepoDigests []string
        Created     int64
        Size        int64
        VirtualSize int64
        Labels      map[string]string
}

The nerdctl interface doesn't really have a machine readable format, even if it does have some json.

Note: I used the original interface from Docker 1.12 for a smaller API, it should probably target 20.10.

https://docs.docker.com/engine/api/#api-version-matrix

Docker API
20.10 1.41
19.03 1.40
18.03 1.37
17.03 1.26
1.12 1.24

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 5, 2022

I added some more code for _ping, version, info, and images.

$ export DOCKER_HOST=unix://nerdctl.sock
$ docker version
Client:
 Version:           20.10.7
 API version:       1.26
 Go version:        go1.13.8
 Git commit:        20.10.7-0ubuntu5~20.04.2
 Built:             Mon Nov  1 00:34:17 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          0.17.1
  API version:      1.26 (minimum version 1.24)
  Go version:       go1.17.2
  Git commit:       20233c26d26f11ca73a9a775fba87ca884ab14d2
  Built:            
  OS/Arch:          linux/amd64
  Experimental:     true

Server-side looks like:

[GIN-debug] HEAD   /_ping                    --> main.setupRouter.func1 (3 handlers)
[GIN-debug] GET    /_ping                    --> main.setupRouter.func2 (3 handlers)
[GIN-debug] GET    /v1.26/version            --> main.setupRouter.func3 (3 handlers)
[GIN-debug] GET    /v1.26/info               --> main.setupRouter.func4 (3 handlers)
[GIN-debug] GET    /v1.26/images/json        --> main.setupRouter.func5 (3 handlers)
[GIN-debug] Listening and serving HTTP on unix:/nerdctl.sock
[GIN] 2022/03/05 - 18:51:13 | 200 |       2.276µs |                 | HEAD     "/_ping"
[GIN] 2022/03/05 - 18:51:13 | 200 |   13.181391ms |                 | GET      "/v1.26/version"

There are a lot of empty/unknown fields, in the docker info output.

See https://github.com/afbjorklund/nerdctld for the rest of the code...


anders@ubuntu:~/nerdctld (main)$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       latest    21a3deaa0d32   22 hours ago   6.19MB
busybox      latest    b5cfd4befc11   2 months ago   1.36MB
ubuntu       latest    8ae9bafbb64f   19 hours ago   81.6MB
anders@ubuntu:~/nerdctld (main)$ nerdctl images
REPOSITORY    TAG       IMAGE ID        CREATED         PLATFORM       SIZE        BLOB SIZE
alpine        latest    21a3deaa0d32    22 hours ago    linux/amd64    5.9 MiB     2.7 MiB
busybox       latest    b5cfd4befc11    2 months ago    linux/amd64    1.3 MiB     758.8 KiB
ubuntu        latest    8ae9bafbb64f    19 hours ago    linux/amd64    77.8 MiB    27.2 MiB

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 8, 2022

Added support for pull ps and build as well, but run will be trouble (with the streams)...

$ export DOCKER_HOST=unix://nerdctl.sock
$ docker pull alpine
Using default tag: latest
docker.io/library/alpine:latest
$ docker build -t mytest build
Sending build context to Docker daemon  4.096kB
unpacking docker.io/library/mytest:latest (sha256:1470fd40aef71a1a33625fc80ec3dde8b36c216a168a4ef89c9d5d2ff265c696)...done
$ nerdctl images
REPOSITORY    TAG       IMAGE ID        CREATED           PLATFORM       SIZE       BLOB SIZE
alpine        latest    21a3deaa0d32    10 seconds ago    linux/amd64    5.9 MiB    2.7 MiB
mytest        latest    1470fd40aef7    3 seconds ago     linux/amd64    1.3 MiB    757.6 KiB

Naturally there are dozens of flags and special cases missing, from this "proof-of-concept".

@mniak
Copy link
Author

mniak commented Mar 11, 2022

That is very nice, @afbjorklund
I will try to test and contribute when I have some spare time.

@afbjorklund
Copy link
Contributor

afbjorklund commented Mar 11, 2022

The "docker run" is almost working, for the absolutely most trivial case.

Currently it is using a hack of nerdctl run -d and nerdctl logs -f.
It is a bit of a problem, API is broken down into "create" and "attach"

But at least upgrading the HTTP stream and outputting text works OK...

Starting and stopping should be fine, it was for the interactive terminal.
It also expects a bunch of events to be issued, those look like tedious.

https://github.com/moby/moby/blob/17.03.x/docs/api/v1.24.md#monitor-dockers-events

@afbjorklund

This comment was marked as outdated.

@gaocegege
Copy link

Personally, I also vote for the issue. It makes nerdctl integrate with existing infra seamlessly.

@afbjorklund
Copy link
Contributor

I happy to promote my hack for further testing, main remaining issue was interactive "run"

Starting a container in the background is not so much of a problem, but starting a shell...

One needs to handle events, and then there is the whole business of switching protocols

So it will not be a substitute for dockerd, but then again nerdctl is not for docker either ?

@afbjorklund
Copy link
Contributor

afbjorklund commented Sep 18, 2022

@mniak made an initial release of "nerdctld", with some binaries and systemd units

DOCKER_HOST=unix://$XDG_RUNTIME_DIR/nerdctl.sock docker version

@afbjorklund
Copy link
Contributor

Made another release (0.1.1), with bug fixes and new features for "build". And implementations of "inspect" and "history".

@afbjorklund
Copy link
Contributor

There was a 0.2.1 release, with deb/rpm packaging and lima examples.

https://github.com/afbjorklund/nerdctld/releases

The various hacks for the run command are currently on this branch:

https://github.com/afbjorklund/nerdctld/tree/run

@afbjorklund
Copy link
Contributor

limactl start ./lima/nerdctl.yaml

INFO[0060] Message from the instance "nerdctl":         
To run `docker` on the host (assumes docker-cli is installed), run the following commands:
------
export DOCKER_HOST=unix:///home/anders/.lima/nerdctl/sock/nerdctl.sock
docker version
------

docker version

Server:
 nerdctl:
  Version:          1.2.1
 buildctl:
  Version:          0.11.3
  GitCommit:        4ddee42a32aac4cd33bf9c2be4c87c2ffd34747b
 containerd:
  Version:          1.6.19
  GitCommit:        1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f
 runc:
  Version:          1.1.4
 tini:
  Version:          0.19.0 - git.de40ad0
 Engine:
  Version:          1.2.1
  API version:      1.40 (minimum version 1.24)
  Go version:       go1.20.1
  Git commit:       a0bbfd75ba92bcb11ac6059bf4f6f4e50c6da0b8
  Built:            
  OS/Arch:          linux/amd64
  Experimental:     true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants