一、 背景介绍
为了满足扩展性需求,Docker (1.7 及以后版本)提供了插件支持
用户能够根据自己的需要编写自定义插件来增强 Docker 的功能
一般而言,各类插件与 docker daemon 守护进程的交互原理都是一样的
为了减轻开发者负担,docker 官方提供了 go-plugins-helpers 基础工具包
借助该工具包,我们只需关注实际的业务逻辑,按照接口规范(需要实现哪些方法)编写插件实体即可
二、 开发步骤
2.1 环境准备
操作系统:ubuntu 16.04 LTS
Go:1.9.2
Docker:1.12.6
2.2 在 $GOPATH/src 目录下新建一个文件夹,如 docker-volume-plugin-example
2.3 在 $GOPATH/src/docker-volume-plugin-example 目录下创建 driver.go 源文件
package main import ( "os" "path/filepath" "sync" "errors" "strings" "github.com/Sirupsen/logrus" "github.com/docker/go-plugins-helpers/volume" ) type ExampleDriver struct { volumes map[string]string mutex *sync.Mutex mountPoint string } func NewExampleDriver(mount string) volume.Driver { var d = ExampleDriver{ volumes: make(map[string]string), mutex: &sync.Mutex{}, mountPoint: mount, } filepath.Walk(mount, func(path string, f os.FileInfo, err error) error { if ( f == nil ) {return err} if (strings.Compare(path,mount) == 0){ return nil} if f.IsDir() {d.volumes[f.Name()] = path} return nil }) return d } func (d ExampleDriver) Create(r *volume.CreateRequest) error { logrus.Infof("Create volume: %s", r.Name) d.mutex.Lock() defer d.mutex.Unlock() if _, ok := d.volumes[r.Name]; ok { return nil } volumePath := filepath.Join(d.mountPoint, r.Name) os.MkdirAll(volumePath, os.ModePerm) _, err := os.Lstat(volumePath) if err != nil { logrus.Errorf("Error %s %v", volumePath, err.Error()) return err } d.volumes[r.Name] = volumePath return nil } func (d ExampleDriver) List() (*volume.ListResponse,error) { logrus.Info("Volumes list... ") logrus.Info(d.volumes) var res = &volume.ListResponse{} volumes := make([]*volume.Volume,0) for name, path := range d.volumes { volumes = append(volumes, &volume.Volume{ Name: name, Mountpoint: path, }) } res.Volumes = volumes return res, nil } func (d ExampleDriver) Get(r *volume.GetRequest) (*volume.GetResponse,error) { logrus.Infof("Get volume: %s", r.Name) var res = &volume.GetResponse{} if path, ok := d.volumes[r.Name]; ok { res.Volume = &volume.Volume{ Name: r.Name, Mountpoint: path, } return res, nil } return &volume.GetResponse{}, errors.New(r.Name + " not exists") } func (d ExampleDriver) Remove(r *volume.RemoveRequest) error { logrus.Info("Remove volume ", r.Name) d.mutex.Lock() defer d.mutex.Unlock() if _, ok := d.volumes[r.Name]; ok { os.RemoveAll(filepath.Join(d.mountPoint, r.Name)) delete(d.volumes, r.Name) return nil } return errors.New(r.Name + " not exists") } func (d ExampleDriver) Path(r *volume.PathRequest) (*volume.PathResponse,error) { logrus.Info("Get volume path ", r.Name) var res = &volume.PathResponse{} if path, ok := d.volumes[r.Name]; ok { res.Mountpoint = path return res,nil } return &volume.PathResponse{},errors.New(r.Name + " not exists") } func (d ExampleDriver) Mount(r *volume.MountRequest) (*volume.MountResponse,error) { logrus.Info("Mount volume ", r.Name) var res = &volume.MountResponse{} if path, ok := d.volumes[r.Name]; ok { res.Mountpoint = path return res,nil } return &volume.MountResponse{},errors.New(r.Name + " not exists") } func (d ExampleDriver) Unmount(r *volume.UnmountRequest) error { logrus.Info("Unmount ", r.Name) if _, ok := d.volumes[r.Name]; ok { return nil } return errors.New(r.Name + " not exists") } func (d ExampleDriver) Capabilities() *volume.CapabilitiesResponse { logrus.Info("Capabilities. ") return &volume.CapabilitiesResponse{} }2.4 在 $GOPATH/src/docker-volume-plugin-example 目录下创建 main.go 源文件
package main import ( "log" "github.com/docker/go-plugins-helpers/volume" ) func main() { driver := NewExampleDriver("/tmp/example-volume-mount-root") handler := volume.NewHandler(driver) if err := handler.ServeUnix("example-driver",0); err != nil { log.Fatalf("Error %v", err) } for { } }2.5 编译插件源码
# cd $GOPATH/src/docker-volume-plugin-example # go build2.6 启动插件
# cd $GOPATH/src/docker-volume-plugin-example # ./docker-volume-plugin-example2.7 测试插件
# docker run -it -v c1:/data --volume-driver=example-driver ubuntu:14.04 /bin/bash
执行该命令后,插件会自动在 /tmp/example-volume-mount-root 目录下创建一个名称为 c1 的文件夹,
并挂载到容器中(映射为 /data 路径)
# docker volume ls执行该命令后,将显示所有 volume,包含通过插件创建的卷
# docker volume rm c1执行该命令,将删除数据卷 c1,注意对应 /tmp/example-volume-mount-root 下的 c1 文件夹也会被删除
代码地址【https://github.com/SataQiu/docker-volume-plugin】