Check if there is a plugin socket first under `/run/docker/plugins/NAME.sock`. If there is no socket for a plugin, check `/etc/docker/plugins/NAME.spec` and `/usr/lib/docker/plugins/NAME.spec` for spec files. Signed-off-by: David Calavera <david.calavera@gmail.com>
6.7 KiB
Experimental: Docker Plugin API
Docker plugins are out-of-process extensions which add capabilities to the Docker Engine.
This page is intended for people who want to develop their own Docker plugin. If you just want to learn about or use Docker plugins, look here.
This is an experimental feature. For information on installing and using experimental features, see the experimental feature overview.
What plugins are
A plugin is a process running on the same docker host as the docker daemon, which registers itself by placing a file in one of the plugin directories described in Plugin discovery.
Plugins have human-readable names, which are short, lowercase strings. For
example, flocker
or weave
.
Plugins can run inside or outside containers. Currently running them outside containers is recommended.
Plugin discovery
Docker discovers plugins by looking for them in the plugin directory whenever a user or container tries to use one by name.
There are three types of files which can be put in the plugin directory.
.sock
files are UNIX domain sockets..spec
files are text files containing a URL, such asunix:///other.sock
..json
files are text files containing a full json specification for the plugin.
UNIX domain socket files must be located under /run/docker/plugins
, whereas
spec files can be located either under /etc/docker/plugins
or /usr/lib/docker/plugins
.
The name of the file (excluding the extension) determines the plugin name.
For example, the flocker
plugin might create a UNIX socket at
/run/docker/plugins/flocker.sock
.
You can define each plugin into a separated subdirectory if you want to isolate definitions from each other.
For example, you can create the flocker
socket under /run/docker/plugins/flocker/flocker.sock
and only
mount /run/docker/plugins/flocker
inside the flocker
container.
Docker always searches for unix sockets in /run/docker/plugins
first. It checks for spec or json files under
/etc/docker/plugins
and /usr/lib/docker/plugins
if the socket doesn't exist. The directory scan stops as
soon as it finds the first plugin definition with the given name.
JSON specification
This is the JSON format for a plugin:
{
"Name": "plugin-example",
"Addr": "https://example.com/docker/plugin",
"TLSConfig": {
"InsecureSkipVerify": false,
"CAFile": "/usr/shared/docker/certs/example-ca.pem",
"CertFile": "/usr/shared/docker/certs/example-cert.pem",
"KeyFile": "/usr/shared/docker/certs/example-key.pem",
}
}
The TLSConfig
field is optional and TLS will only be verified if this configuration is present.
Plugin lifecycle
Plugins should be started before Docker, and stopped after Docker. For
example, when packaging a plugin for a platform which supports systemd
, you
might use systemd
dependencies to
manage startup and shutdown order.
When upgrading a plugin, you should first stop the Docker daemon, upgrade the plugin, then start Docker again.
If a plugin is packaged as a container, this may cause issues. Plugins as containers are currently considered experimental due to these shutdown/startup ordering issues. These issues are mitigated by plugin retries (see below).
Plugin activation
When a plugin is first referred to -- either by a user referring to it by name
(e.g. docker run --volume-driver=foo
) or a container already configured to
use a plugin being started -- Docker looks for the named plugin in the plugin
directory and activates it with a handshake. See Handshake API below.
Plugins are not activated automatically at Docker daemon startup. Rather, they are activated only lazily, or on-demand, when they are needed.
API design
The Plugin API is RPC-style JSON over HTTP, much like webhooks.
Requests flow from the Docker daemon to the plugin. So the plugin needs to implement an HTTP server and bind this to the UNIX socket mentioned in the "plugin discovery" section.
All requests are HTTP POST
requests.
The API is versioned via an Accept header, which currently is always set to
application/vnd.docker.plugins.v1+json
.
Handshake API
Plugins are activated via the following "handshake" API call.
/Plugin.Activate
Request: empty body
Response:
{
"Implements": ["VolumeDriver"]
}
Responds with a list of Docker subsystems which this plugin implements. After activation, the plugin will then be sent events from this subsystem.
Volume API
If a plugin registers itself as a VolumeDriver
(see above) then it is
expected to provide writeable paths on the host filesystem for the Docker
daemon to provide to containers to consume.
The Docker daemon handles bind-mounting the provided paths into user containers.
/VolumeDriver.Create
Request:
{
"Name": "volume_name"
}
Instruct the plugin that the user wants to create a volume, given a user specified volume name. The plugin does not need to actually manifest the volume on the filesystem yet (until Mount is called).
Response:
{
"Err": null
}
Respond with a string error if an error occurred.
/VolumeDriver.Remove
Request:
{
"Name": "volume_name"
}
Create a volume, given a user specified volume name.
Response:
{
"Err": null
}
Respond with a string error if an error occurred.
/VolumeDriver.Mount
Request:
{
"Name": "volume_name"
}
Docker requires the plugin to provide a volume, given a user specified volume name. This is called once per container start.
Response:
{
"Mountpoint": "/path/to/directory/on/host",
"Err": null
}
Respond with the path on the host filesystem where the volume has been made available, and/or a string error if an error occurred.
/VolumeDriver.Path
Request:
{
"Name": "volume_name"
}
Docker needs reminding of the path to the volume on the host.
Response:
{
"Mountpoint": "/path/to/directory/on/host",
"Err": null
}
Respond with the path on the host filesystem where the volume has been made available, and/or a string error if an error occurred.
/VolumeDriver.Unmount
Request:
{
"Name": "volume_name"
}
Indication that Docker no longer is using the named volume. This is called once per container stop. Plugin may deduce that it is safe to deprovision it at this point.
Response:
{
"Err": null
}
Respond with a string error if an error occurred.
Plugin retries
Attempts to call a method on a plugin are retried with an exponential backoff for up to 30 seconds. This may help when packaging plugins as containers, since it gives plugin containers a chance to start up before failing any user containers which depend on them.