Skip to content

Instantly share code, notes, and snippets.

@cristobal
Last active February 27, 2024 23:36
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save cristobal/fcb0987871d7e1f7449e to your computer and use it in GitHub Desktop.
Save cristobal/fcb0987871d7e1f7449e to your computer and use it in GitHub Desktop.
Machine Diskutil to mount/unmont external volumes inside docker machines running on Virtualbox
#!/usr/bin/env sh
# @see http://stackoverflow.com/questions/30040708/how-to-mount-local-volumes-in-docker-machine
# @see https://github.com/boot2docker/boot2docker/blob/master/doc/FAQ.md
################################################################################
# Dependency Section #
# #
################################################################################
check_deps() {
## Make sure commands are available
local name
for name in docker-machine VBoxManage grep ; do
type $name > /dev/null 2>&1
if [ $? -eq 1 ] ; then
echo "Error: Command \`$name\` not found,"
echo "make sure it is available from your \`\$PATH\`"
exit 1
fi
done
}
################################################################################
# Helper Functions #
# #
################################################################################
volume_exists() {
if [ ! -d $volume ] ; then
echo "Error: The volume \`$volume\` does not exist,"
echo "make sure to specify the correct volume path"
exit 1
fi
}
################################################################################
# Machine Helper Functions #
# #
################################################################################
machine_exists() {
docker-machine ls | grep "$machine" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "Error: The docker machine \`$machine\` does not exist,"
echo "make sure to specify the correct docker machine."
exit 2
fi
}
machine_stop() {
docker-machine status $machine | grep -E -i 'stopped' > /dev/null 2>&1
if [ $? -ne 1 ] ; then
return
fi
echo "Info: Attempting to stop the docker-machine \`$machine\`."
echo "> docker-machine stop $machine"
docker-machine stop $machine
if [ $? -ne 0 ] ; then
echo "Error: Failed to stop the docker-machine \`$machine\`."
exit 3
fi
echo "[OK]"
}
machine_start() {
docker-machine status $machine | grep -E -i 'running' > /dev/null 2>&1
if [ $? -ne 1 ] ; then
return
fi
echo "Info: Attempting to start the docker-machine \`$machine\`."
echo "> docker-machine start $machine"
docker-machine start $machine
if [ $? -ne 0 ] ; then
echo "Error: Failed to start the docker-machine \`$machine\`."
exit 4
fi
echo "[OK]"
}
machine_mount_volume() {
echo "Info: Attempting to mount the volume \`$volume\`"
echo "> docker-machine ssh $machine \"sudo mkdir -p $volume\""
docker-machine ssh $machine "sudo mkdir -p $volume"
# create boot point
if [ $? -ne 0 ] ; then
echo "Error: Could not create the directory \`$volume\`"
exit 5
fi
echo "[OK]"
# mount volume
local args="sudo mount -t vboxsf -o defaults,uid=\`id -u docker\`,gid=\`id -g docker\` $volume $volume"
echo "> docker-machine ssh $machine \"$args\""
docker-machine ssh $machine "$args"
if [ $? -ne 0 ] ; then
echo "Error: Could not mount the volume \`$volume\`"
exit 5
fi
echo "[OK]"
}
machine_unmount_volume() {
# if the the shared folder exists remove it
docker machine ssh "sudo ls -l $volume" > /dev/null 2>&1
if [ $? -ne 1 ] ; then
return
fi
echo "Info: Attempting to remove the mount point \`$volume\`"
local args="sudo rm -rf $volume"
echo "> docker-machine ssh $machine $args"
docker-machine ssh $machine $args
if [ $? -ne 0 ] ; then
echo "Warning: Could not remove the mount point \`$volume\` properly"
fi
echo "[OK]"
}
machine_make_volume_bootable() {
echo "Info: Attempting to make the volume \`$volume\` automountable"
local dir='/var/lib/boot2docker'
docker-machine ssh $machine "sudo ls $dir" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "Error: Could not make the \`$volume\` automountable,"
echo "the expected \`$dir\` does not exist."
exit 1
fi
local file="${dir}/bootlocal.sh"
# create the file if it does not exist
docker-machine ssh $machine "sudo ls $file" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
# create file
# make executable
# add she-bang
docker-machine ssh $machine "sudo touch $file"
docker-machine ssh $machine "sudo chmod +x $file"
docker-machine ssh $machine "echo \"#!/usr/bin/env sh\" | sudo tee $file" > /dev/null 2>&1
fi
# only append if the entry does not already exist in the file
docker-machine ssh $machine "sudo cat $file | grep -E -i $volume" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
# set make mnt dir
# set mount <shared-folder> <mnt dir>
docker-machine ssh $machine "echo \"mkdir -p $volume\" | sudo tee -a $file" > /dev/null 2>&1
docker-machine ssh $machine "echo \"mount -t vboxsf -o defaults,uid=\`id -u docker\`,gid=\`id -g docker\` $volume $volume\" | sudo tee -a $file" > /dev/null 2>&1
fi
echo "[OK]"
echo "Info: \`$file\` configuration is the following:"
docker-machine ssh $machine "sudo cat $file"
echo "[OK]"
}
machine_unmake_volume_bootable() {
echo "Info: Attempting to unmake the volume \`$volume\` automountable"
local file="/var/lib/boot2docker/bootlocal.sh"
docker-machine ssh $machine "sudo ls $file" > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "Warning: the expected \`$file\` does not exist,"
echo "> no startup entries for automount to remove".
echo "[OK]"
return
fi
local total=$(docker-machine ssh $machine "sudo cat $file | wc -l" | xargs)
local lines=$(docker-machine ssh $machine "sudo tail -n $(expr $total - 1) $file | grep -E -v $volume" | xargs)
# add she-bang
# add lines
docker-machine ssh $machine "echo \"#!/usr/bin/env sh\" | sudo tee $file" > /dev/null 2>&1
docker-machine ssh $machine "echo \"$lines\" | sudo tee -a $file" > /dev/null 2>&1
echo "[OK]"
docker-machine ssh $machine "sudo cat $file | grep -E -i '(mkdir|mount)" > /dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "Info: after the changes the configuration for the file \`$file\`,"
echo "is the following:"
docker-machine ssh $machine "sudo cat $file"
else
docker-machine ssh $machine "sudo rm -f $file"
echo "Info: after the changes the configuration for the file \`$file\`,"
echo "had no entries and was removed."
fi
echo "[OK]"
}
################################################################################
# Virtualbox Helper Functions #
# #
################################################################################
vbox_volume_exists() {
VBoxManage showvminfo $machine | grep $volume > /dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "Error: The shared folder \`$volume\` already exists on the docker machine,"
echo "please unmount it first."
exit 1
fi
}
vbox_volume_add() {
echo "Info: Attempting to add the volume \`$volume\` as a sharedfolder,"
echo "to the docker machine \`$machine\`"
local args="--automount --name $volume --hostpath $volume"
echo "> VBoxManage sharedfolder add $machine $args"
VBoxManage sharedfolder add $machine $args
if [ $? -ne 0 ] ; then
log "Error: Failed to add the shared folder \`$volume\`."
exit 1
fi
echo "[OK]"
}
vbox_volume_remove() {
echo "Info: Attempting to remove the volume \`$volume\` as sharedfolder,"
echo "from the docker machine \`$machine\`"
VBoxManage showvminfo $machine | grep -E -i $volume > /dev/null 2>&1
if [ $? -ne 0 ] ; then
echo "> no shared folder with name \`$volume\` to remove"
echo "[OK]"
return
fi
local args="--name $volume"
echo "> VBoxManage sharedfolder remove $machine $args"
VBoxManage sharedfolder remove $machine $args
if [ $? -ne 0 ] ; then
echo "Warning: could not remove the shared folder \`$volume\` properly"
fi
echo "[OK]"
}
################################################################################
# Mount Section #
# #
################################################################################
mount_print_help() {
cat <<EOF
Mount a disk as a shared folder to a docker machine.
Usage: mount [MACHINE] [VOLUME]
Arguments:
MACHINE The name of the machine to mount the volume path.
VOLUME The path to the volume to mount.
EOF
}
mount_parse_args() {
# ask for help
if [ "$1" = '-h' ] || [ "$1" = '--help' ] ; then
mount_print_help
exit 0
fi
# too few arguments
if [ $# -lt 2 ] ; then
mount_print_help
exit 0
fi
machine=$1
volume=$(echo $2 | sed "s,/$,," | xargs)
}
mount_print_header() {
cat <<EOF
##########################
# MACHINE DISKUTIL MOUNT #
##########################
EOF
}
mount_run() {
machine_exists
volume_exists
vbox_volume_exists
machine_stop
vbox_volume_add
machine_start
machine_mount_volume
machine_make_volume_bootable
}
mount() {
mount_parse_args $@
mount_print_header
mount_run
}
################################################################################
# Unmount Section #
# #
################################################################################
unmount_print_help() {
cat <<EOF
Unmount a disk that has been mounted as shared folder by this script.
Usage: unmount [MACHINE] [VOLUME]
Arguments:
MACHINE The name of the machine to unmount the volume path from.
VOLUME The path to the volume to unmount.
EOF
}
unmount_parse_args() {
if [ "$1" = '-h' ] || [ "$1" = '--help' ] ; then
unmount_print_help
exit 0
fi
# too few arguments
if [ $# -lt 2 ] ; then
unmount_print_help
exit 0
fi
machine=$1
volume=$(echo $2 | sed "s,/$,," | xargs)
}
unmount_print_header() {
cat <<EOF
############################
# MACHINE DISKUTIL UNMOUNT #
############################
EOF
}
unmount_run() {
local restart=0
machine_exists
docker-machine status $machine | grep -E -i 'running' > /dev/null 2>&1
if [ $? -eq 0 ] ; then
restart=1
machine_stop
fi
vbox_volume_remove
machine_start
machine_unmount_volume
machine_unmake_volume_bootable
if [ $restart -ne 1 ] ; then
machine_stop
fi
}
unmount() {
unmount_parse_args $@
unmount_print_header
unmount_run
}
################################################################################
# MAIN ENTRYPOINT #
# #
# 1. Parse command #
# 2. Run command #
# 2.1 Print help command if cmd equals 'help' #
# 2.2 Run the command if it's one of [build, tag, login, push] #
# 2.3 Print command not found if none of the above. #
# #
################################################################################
main_print_help() {
cat<<EOF
Docker Machine Disk utitlity for mounting / unmounting shared folders.
Usage:
machine-diskutil [COMMAND] [ARGS....]
machine-diskutil -h|--help
Commands:
mount Mount a volume as a shared folder on the virtual machine.
unmount Unmount a shared folder from the virtual machine.
EOF
}
main_parse_command() {
if [ $# -eq 0 ] ; then
cmd='help'
return
fi
if [ "$1" = '-h' ] || [ "$1" = '--help' ] ; then
cmd='help'
return
fi
cmd=$1
}
main_run_command() {
case $cmd in
help)
main_print_help
return
;;
mount|unmount)
;;
*)
main_print_cmd_not_found
exit 1
;;
esac
check_deps
shift
$cmd $@
}
main_parse_command $@
main_run_command $@
@cristobal
Copy link
Author

About

On OS X external disks HD, that are not mounted under the /Users path is not available from inside the docker-machine.
So when you start up the docker-machine via the docker run command or through docker-compose and link an external hd that is not available from under the /Users path it will just be empty inside the docker-machine (symlinks do not work :/).

It is possible to mount disk under the /Users folder, but it may depend on the disk how it was formatted and so on. e.g.

diskutil unmountDisk /dev/<disk-id>
mkdir ~/mount-point
sudo mount -w -t hfs /dev/<disk-id> ~/mount-point

This script is intended for the case when you can't mount the volume under the /Users path or if you want to mount /Volumes/hd-name as it is onto the docker-machine.

This script is based on the solution from this Stackoverflow thread

References:

Installing

This script is intended to mount/unmount external /Volumes on your Mac, so that they are accesible from you docker-machine(s) that are running inside Virtualbox.

Download this script some place on your machine and make sure it's executable e.g.

cd ~/Downloads && \
curl -L https://gist.github.com/cristobal/fcb0987871d7e1f7449e/download | \
tar -xvz --strip-components=1 && \
mv machine-diskutil.sh /usr/local/bin/. && \
chmod +x /usr/local/bin/machine-diskutil.sh && \
cd -

Mounting

Then you can mount your external volume e.g.:

machine-diskutil.sh mount machine-name /Volumes/hd-name

Where:

  1. machine-name is the name of the docker-machine you want to mount the volume onto.
  2. /Volumes/hd-name is the full path of the external hd you want make available from the docker-machine.

This will mount the external hd /Volumes/hd-name as a shared folder /Volumes/hd-name in your Virtualbox docker machine.
The external hd will be mounted with the same path it has on your machine. So that you can refer to it with it's canonical path e.g. /Volumes/hd-name/some/path using the docker run command or via docker-compose, this works as you would expect with any other path on your system that is under /User.

Unmounting

If you want to unmount the volume you mounted run it with same arguments that you sent to the mount command e.g.:

machine-diskutil.sh unmount machine-name /Volumes/hd-name

Where:

  1. machine-name is the name of the docker-machine you want to unmount the volume from.
  2. /Volumes/hd-name is the full path of external hd that you want remove from the docker-machine.

@wanghaisheng
Copy link

wanghaisheng commented Apr 29, 2016

my env

[wanghs@db2 machine-diskutil]$ docker version
Client:
 Version:      1.11.1
 API version:  1.23
 Go version:   go1.5.4
 Git commit:   5604cbe
 Built:        Wed Apr 27 00:34:42 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.11.1
 API version:  1.23
 Go version:   go1.5.4
 Git commit:   5604cbe
 Built:        Wed Apr 27 00:34:42 2016
 OS/Arch:      linux/amd64
[wanghs@db2 machine-diskutil]$ docker-machine version
docker-machine version 0.7.0, build a650a40


i am trying to use this wonderful shell to mount host folder into my container

[wanghs@db2 machine-diskutil]$ machine-diskutil.sh unmount registry /home/wanghs
############################
# MACHINE DISKUTIL UNMOUNT #
############################
Info: Attempting to stop the docker-machine `registry`.
> docker-machine stop registry
Stopping "registry"...
Machine "registry" was stopped.
[OK]
Info: Attempting to remove the volume `/home/wanghs` as sharedfolder,
from the docker machine `registry`
> VBoxManage sharedfolder remove registry --name /home/wanghs
VBoxManage: error: Could not find a shared folder named '/home/wanghs'
VBoxManage: error: Details: code VBOX_E_OBJECT_NOT_FOUND (0x80bb0001), component SessionMachine, interface IMachine, callee nsISupports
VBoxManage: error: Context: "RemoveSharedFolder(Bstr(name).raw())" at line 1086 of file VBoxManageMisc.cpp
[OK]
Info: Attempting to start the docker-machine `registry`.
> docker-machine start registry
Starting "registry"...
(registry) Check network to re-create if needed...
(registry) Waiting for an IP...
Machine "registry" was started.
Waiting for SSH to be available...
Detecting the provisioner...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.
[OK]
Info: Attempting to remove the mount point `/home/wanghs`
> docker-machine ssh registry sudo rm -rf /home/wanghs
[OK]
Info: Attempting to unmake the volume `/home/wanghs` automountable
Warning: the expected `/var/lib/boot2docker/bootlocal.sh` does not exist,
> no startup entries for automount to remove.
[OK]
[wanghs@db2 machine-diskutil]$ machine-diskutil.sh mount registry /home/wanghs
##########################
# MACHINE DISKUTIL MOUNT #
##########################
Error: The shared folder `/home/wanghs` already exists on the docker machine,
please unmount it first.
[wanghs@db2 machine-diskutil]$ VBoxManage showvminfo registry
Name:            registry
Groups:          /
Guest OS:        Linux 2.6 / 3.x / 4.x (64-bit)
UUID:            53b7ae31-07b1-441f-ac93-b4c8c4fefa9b
Config file:     /home/wanghs/.docker/machine/machines/registry/registry/registry.vbox
Snapshot folder: /home/wanghs/.docker/machine/machines/registry/registry/Snapshots
Log folder:      /home/wanghs/.docker/machine/machines/registry/registry/Logs
Hardware UUID:   53b7ae31-07b1-441f-ac93-b4c8c4fefa9b
Memory size:     1024MB
Page Fusion:     off
VRAM size:       8MB
CPU exec cap:    100%
HPET:            on
Chipset:         piix3
Firmware:        BIOS
Number of CPUs:  1
PAE:             on
Long Mode:       on
CPUID Portability Level: 0
CPUID overrides: None
Boot menu mode:  disabled
Boot Device (1): DVD
Boot Device (2): DVD
Boot Device (3): HardDisk
Boot Device (4): Not Assigned
ACPI:            on
IOAPIC:          on
Time offset:     0ms
RTC:             UTC
Hardw. virt.ext: on
Nested Paging:   on
Large Pages:     on
VT-x VPID:       on
VT-x unr. exec.: on
Paravirt. Provider: Default
State:           running (since 2016-04-29T07:59:01.601000000)
Monitor count:   1
3D Acceleration: off
2D Video Acceleration: off
Teleporter Enabled: off
Teleporter Port: 0
Teleporter Address:
Teleporter Password:
Tracing Enabled: off
Allow Tracing to Access VM: off
Tracing Configuration:
Autostart Enabled: off
Autostart Delay: 0
Default Frontend:
Storage Controller Name (0):            SATA
Storage Controller Type (0):            IntelAhci
Storage Controller Instance Number (0): 0
Storage Controller Max Port Count (0):  30
Storage Controller Port Count (0):      30
Storage Controller Bootable (0):        on
SATA (0, 0): /home/wanghs/.docker/machine/machines/registry/boot2docker.iso (UUID: edcbe0d7-8f03-4323-a542-c4272709dfd6)
SATA (1, 0): /home/wanghs/.docker/machine/machines/registry/disk.vmdk (UUID: 370e9110-87ae-412f-94a8-220f4b2882e0)
NIC 1:           MAC: 080027CAC041, Attachment: NAT, Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 0 Mbps, Boot priority: 0, Promisc Policy: deny, Bandwidth group: none
NIC 1 Settings:  MTU: 0, Socket (send: 64, receive: 64), TCP Window (send:64, receive: 64)
NIC 1 Rule(0):   name = ssh, protocol = tcp, host ip = 127.0.0.1, host port = 40926, guest ip = , guest port = 22
NIC 2:           MAC: 080027F57C3D, Attachment: Host-only Interface 'vboxnet0', Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 0 Mbps, Boot priority: 0, Promisc Policy: deny, Bandwidth group: none
NIC 3:           disabled
NIC 4:           disabled
NIC 5:           disabled
NIC 6:           disabled
NIC 7:           disabled
NIC 8:           disabled
Pointing Device: PS/2 Mouse
Keyboard Device: PS/2 Keyboard
UART 1:          disabled
UART 2:          disabled
UART 3:          disabled
UART 4:          disabled
LPT 1:           disabled
LPT 2:           disabled
Audio:           disabled
Clipboard Mode:  disabled
Drag and drop Mode: disabled
Session name:    headless
Video mode:      720x400x0 at 0,0 enabled
VRDE:            disabled
USB:             disabled
EHCI:            disabled
XHCI:            disabled

USB Device Filters:

<none>

Available remote USB devices:

<none>

Currently Attached USB Devices:

<none>

Bandwidth groups:  <none>

Shared folders:  <none>

VRDE Connection:    not active
Clients so far:     0

Video capturing:    not active
Capture screens:    0
Capture file:       /home/wanghs/.docker/machine/machines/registry/registry/registry.webm
Capture dimensions: 1024x768
Capture rate:       512 kbps
Capture FPS:        25

Guest:

Configured memory balloon size:      0 MB
OS type:                             Linux26_64
Additions run level:                 2
Additions version:                   5.0.16 r105871


Guest Facilities:

Facility "VirtualBox Base Driver": active/running (last update: 2016/04/29 07:59:20 UTC)
Facility "VirtualBox System Service": active/running (last update: 2016/04/29 07:59:28 UTC)
Facility "Seamless Mode": not active (last update: 2016/04/29 07:59:20 UTC)
Facility "Graphics Mode": not active (last update: 2016/04/29 07:59:20 UTC)


[wanghs@db2 machine-diskutil]$ docker-machine ssh registry
                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.11.0, build HEAD : 32ee7e9 - Wed Apr 13 20:06:49 UTC 2016
Docker version 1.11.0, build 4dc5990
docker@registry:~$ cd /home/
docker@registry:/home$ ls
docker/
docker@registry:/home$
docker@registry:/home$
docker@registry:/home$
docker@registry:/home$ ls -al
total 0
drwxrwxr-x    3 root     staff           60 Apr 29 07:59 ./
drwxr-xr-x   16 tc       staff          400 Apr 29 07:59 ../
drwxr-sr-x    5 docker   staff          160 Apr 29 08:00 docker/
docker@registry:/home$

@cristobal
Copy link
Author

cristobal commented Sep 29, 2016

@wanghaisheng sorry for not seeing this comment until now.
I guess it may have been something todo with your HD.

However at this point i would just recommend to use Docker for Mac where you don't need VirtualBox anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment