├── README.md └── docker-flexvol.sh /README.md: -------------------------------------------------------------------------------- 1 | # docker-flexvol 2 | 3 | A [FlexVolume](https://github.com/kubernetes/community/blob/master/contributors/devel/flexvolume.md) driver for kubernetes which allows you to mount [Docker](https://docs.docker.com/engine/admin/volumes/volumes/) volumes to your kubernetes pods. 4 | 5 | ## Status 6 | 7 | Proof of concept 8 | 9 | ## Using 10 | 11 | This flex volume plugin can start a fresh docker volume from a specified container image and attach it to the kubernetes 12 | pod. This is useful for scenarios where you need your pod to access a bunch of files and if already have that data 13 | as a docker container image, you can just specify the container image name and the volume name in your kubernetes 14 | pod definition and make it available to whatever is running in the pod. 15 | 16 | ### Installing 17 | 18 | In order to use the flexvolume driver, you'll need to install it on every node in the kubelet `volume-plugin-dir`. By default this is `/usr/libexec/kubernetes/kubelet-plugins/volume/exec/` 19 | 20 | You need a directory for the volume driver vendor, so create it: 21 | 22 | ``` 23 | mkdir -p /usr/libexec/kubernetes/kubelet-plugins/volume/exec/dims.io~docker-flexvol 24 | ``` 25 | 26 | Then drop the binary in there and set the execute permission: 27 | 28 | ``` 29 | mv docker-flexvol.sh /usr/libexec/kubernetes/kubelet-plugins/volume/exec/dims.io~docker-flexvol/docker-flexvol 30 | chmod +x /usr/libexec/kubernetes/kubelet-plugins/volume/exec/dims.io~docker-flexvol/docker-flexvol 31 | ``` 32 | 33 | You can now use Docker volumes as usual! 34 | 35 | ### Pod Config 36 | 37 | An example pod config would look like this. Note the `image` and `name` parameters. The `image` is the name of the 38 | container that we need to start. `name` is the name of the volume we need to mount. 39 | 40 | Note that `image` is mandatory, `name` is optional. If `name` is not specified all the contents of the container are 41 | made available to the pod. 42 | 43 | ```yaml 44 | apiVersion: v1 45 | kind: Pod 46 | metadata: 47 | name: nginx 48 | namespace: default 49 | spec: 50 | containers: 51 | - name: nginx 52 | image: nginx 53 | volumeMounts: 54 | - name: test 55 | mountPath: /data 56 | ports: 57 | - containerPort: 80 58 | volumes: 59 | - name: test 60 | flexVolume: 61 | driver: "dims.io/docker-flexvol" 62 | options: 63 | image: "my-container-image" 64 | name: "/data-store" 65 | ``` 66 | 67 | This will use the `/data-store` volume on the `my-container-image` and mount it on the nginx pod under `/data` directory. 68 | 69 | Run `docker ps -a` and find the container with image "my-container-image", that's the one that has the docker volume. this 70 | should be in the `Created` state. Look for the `Source` directory, it will be something like 71 | `/var/lib/docker/volumes/2ec90b1efa7b1f51913882d035192a450810419d72b56814a1332b31457aa356/_data` 72 | 73 | Run `kubectl exec -it nginx -- /bin/bash`, cd to `/data` directory and create a file with say a datestamp. Now 74 | look under the directory above and you should see the same file and contents. 75 | 76 | ### Container Image with pre-defined volume 77 | 78 | To create a Container Image with Volume, save content below into Dockerfile in an empty directory: 79 | 80 | ``` 81 | FROM alpine 82 | ADD https://raw.githubusercontent.com/kubernetes/kubernetes/master/README.md /data-store/README.md 83 | VOLUME ["/data-store"] 84 | ENTRYPOINT ["/bin/sh"] 85 | ``` 86 | 87 | Create a docker image using 88 | 89 | `docker image build -t my-container-image .` 90 | 91 | Test the image using docker 92 | 93 | `docker run --name my-container-1 -it my-container-image` 94 | 95 | Now this container image is ready to be used with this flexvolume driver. When you use this image with the pod config 96 | above, you can exec into the pod and see the file 97 | 98 | ``` 99 | [dims@bigbox 16:15] ~ ⟩ kubectl exec -it nginx -- /bin/bash 100 | root@nginx:/# cd /data 101 | root@nginx:/data# ls -altr 102 | total 12 103 | -rw------- 1 root root 3241 Jan 1 1970 README.md 104 | drwxr-xr-x 2 root root 4096 Aug 18 20:15 . 105 | drwxr-xr-x 32 root root 4096 Aug 18 20:15 .. 106 | ``` 107 | -------------------------------------------------------------------------------- /docker-flexvol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Notes: 18 | # - Please install "jq" package before using this driver. 19 | usage() { 20 | err "Invalid usage. Usage: " 21 | err "\t$0 init" 22 | err "\t$0 mount " 23 | err "\t$0 unmount " 24 | exit 1 25 | } 26 | 27 | err() { 28 | echo -ne $* 1>&2 29 | } 30 | 31 | log() { 32 | echo -ne $* >&1 33 | } 34 | 35 | ismounted() { 36 | MOUNT=$(findmnt -n ${MNTPATH} 2>/dev/null) 37 | if [ ! -z "$MOUNT" ]; then 38 | echo "1" 39 | else 40 | echo "0" 41 | fi 42 | } 43 | 44 | 45 | domount() { 46 | MNTPATH=$1 47 | mkdir -p ${MNTPATH} &> /dev/null 48 | 49 | UUID=$(uuidgen) 50 | VOLUME_IMAGE=$(echo $2 | jq -r '.image') 51 | VOLUME_NAME=$(echo $2 | jq -r '.name | select(type!="null")') 52 | 53 | docker pull $VOLUME_IMAGE &> /dev/null # check for image updates 54 | 55 | if [[ -z "${VOLUME_NAME}" ]]; then 56 | VOLUME_CONTAINER_ID=$(docker create --name $UUID $VOLUME_IMAGE /bin/true) 57 | if [[ -z "${VOLUME_CONTAINER_ID}" ]]; then 58 | err "{ \"status\": \"Failure\", \"message\": \"Unable to find create container from image ${VOLUME_IMAGE}\"}" 59 | exit 1 60 | fi 61 | 62 | pushd ${MNTPATH} &> /dev/null 63 | docker export $VOLUME_CONTAINER_ID | tar -xf - 64 | popd &>/dev/null 65 | docker rm -f $VOLUME_CONTAINER_ID &> /dev/null 66 | else 67 | VOLUME_CONTAINER_ID=$(docker create -v $VOLUME_NAME --name $UUID $VOLUME_IMAGE /bin/true) 68 | if [[ -z "${VOLUME_CONTAINER_ID}" ]]; then 69 | err "{ \"status\": \"Failure\", \"message\": \"Unable to find create container from image ${VOLUME_IMAGE}\"}" 70 | exit 1 71 | fi 72 | 73 | VOLUME_CONTAINER_DATA_PATH=$(docker inspect $VOLUME_CONTAINER_ID | jq -r '..|.Mounts?[0]|select(type!="null")|select (.Destination=="'${VOLUME_NAME}'")|.Source') 74 | if [[ -z "${VOLUME_CONTAINER_DATA_PATH}" ]]; then 75 | err "{ \"status\": \"Failure\", \"message\": \"Unable to find data path for ${VOLUME_CONTAINER_ID}\"}" 76 | exit 1 77 | fi 78 | 79 | mount --bind $VOLUME_CONTAINER_DATA_PATH $MNTPATH &> /dev/null 80 | if [ $? -ne 0 ]; then 81 | err "{ \"status\": \"Failure\", \"message\": \"Failed to mount ${VOLUME_CONTAINER_DATA_PATH} at ${MNTPATH}\"}" 82 | exit 1 83 | fi 84 | fi 85 | 86 | log '{"status": "Success"}' 87 | exit 0 88 | } 89 | 90 | unmount() { 91 | MNTPATH=$1 92 | if [ $(ismounted) -eq 0 ] ; then 93 | log '{"status": "Success"}' 94 | exit 0 95 | fi 96 | 97 | VOLUME_ID=$(findmnt ${MNTPATH} -cno SOURCE | sed 's/.*\[\([^]]*\)\].*/\1/g' | cut -f 6 -d '/') 98 | if [[ -n "${VOLUME_ID}" ]]; then 99 | # Hack to get the container id from the volume id (See https://github.com/moby/moby/issues/31436) 100 | VOLUME_CONTAINER_ID=$(docker volume rm $VOLUME_ID 2>&1 | sed 's/.*\[\([^]]*\)\].*/\1/g') 101 | umount ${MNTPATH} &> /dev/null 102 | if [ $? -ne 0 ]; then 103 | err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}" 104 | exit 1 105 | fi 106 | 107 | if [[ -n "${VOLUME_CONTAINER_ID}" ]]; then 108 | docker rm -f $VOLUME_CONTAINER_ID &> /dev/null 109 | fi 110 | else 111 | if [[ -n "${MNTPATH}" ]]; then 112 | rm -rf ${MNTPATH}/* 113 | fi 114 | fi 115 | 116 | log '{"status": "Success"}' 117 | exit 0 118 | } 119 | 120 | op=$1 121 | 122 | if ! command -v jq >/dev/null 2>&1; then 123 | err "{ \"status\": \"Failure\", \"message\": \"'jq' binary not found. Please install jq package before using this driver\"}" 124 | exit 1 125 | fi 126 | 127 | if ! command -v uuidgen >/dev/null 2>&1; then 128 | err "{ \"status\": \"Failure\", \"message\": \"'uuidgen' binary not found. Please install jq package before using this driver\"}" 129 | exit 1 130 | fi 131 | 132 | if [ "$op" = "init" ]; then 133 | log '{"status": "Success", "capabilities": {"attach": false}}' 134 | exit 0 135 | fi 136 | 137 | if [ $# -lt 2 ]; then 138 | usage 139 | fi 140 | 141 | shift 142 | 143 | case "$op" in 144 | mount) 145 | domount $* 146 | ;; 147 | unmount) 148 | unmount $* 149 | ;; 150 | *) 151 | log '{"status": "Not supported"}' 152 | exit 0 153 | esac 154 | 155 | exit 1 156 | --------------------------------------------------------------------------------