[Docker Volume Plugin] 自己动手编写 Docker Volume 插件

一、  背景介绍

          为了满足扩展性需求,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 build
2.6    启动插件
# cd $GOPATH/src/docker-volume-plugin-example
# ./docker-volume-plugin-example
2.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


猜你喜欢

转载自blog.csdn.net/shida_csdn/article/details/79722648