Docker under Ubuntu 16.04, set up for direct-lvm storage
Recently i wanted to install docker on my ubuntu server the right way and use direct-lvm as storage layer for the devicemapper. For production environments this is the preferred method. Unfortunately it was not working out of the box, which is why i am writing this and maybe prevent some users from getting as much headaches as i was getting.
Preparing LVM
First step is to make some space in our volume group or even make a dedicated one for docker. If you want to do so, you have to create a new partition. You may have many partitions inside one volume group but you cannot have multiple volume groups on one partition.
I am not a fan of partitions, so i choose to use my existing volume group and reduce the the size of my logical volume in order to have some space for the docker's storage.
The command vgdisplay
shows my existing volume groups and its properties. Interesting for us is the VG Size, which shows the overall volume group size, and the Alloc PE / Size, which shows the allocated size by existing logical volumes.
# vgdisplay --- Volume group --- VG Name ubuntu-vg System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 3 VG Access read/write VG Status resizable MAX LV 0 Cur LV 2 Open LV 2 Max PV 0 Cur PV 1 Act PV 1 VG Size 36,80 GiB PE Size 4,00 MiB Total PE 9422 Alloc PE / Size 9416 / 36,78 GiB Free PE / Size 6 / 24,00 MiB VG UUID e5LZI7-Uo19-9LBg-3Mcj-YgXm-traf-bGdgS8
In this example of my virtual host nearly all allocatable size is allocated already, so next step is to reduce the logical volume's size so there is enough space to build the docker's storage withing our existing volume group.
We cannot resize mounted logical volumes and since our root filesystem is on the only logical volume we have to boot into a rescue environment and perform our changes from there.
After booting into the rescue system i can reduce my local volume as follows:
#$ lvreduce --resizefs -L 20G /dev/ubuntu-vg/root fsck von util-linux 2.27.1 /dev/mapper/ubuntu--vg-root: 226401/2281104 Dateien (0.1% nicht zusammenhängend), 1417926/9118720 Blöcke resize2fs 1.42.13 (17-May-2015) Die Größe des Dateisystems auf /dev/mapper/ubuntu--vg-root wird auf 5242880 (4k) Blöcke geändert. Das Dateisystem auf /dev/mapper/ubuntu--vg-root is nun 5242880 (4k) Blöcke lang. Size of logical volume ubuntu-vg/root changed from 34,79 GiB (8905 extents) to 20,00 GiB (5120 extents). Logical volume root successfully resized.
I decided to reduce my root filesystem to 20G, which leaves me about 15G of free space for my docker storage. The --resizefs
automatically resizes the underlying filesystem to its new size, so we don't have to do this manually. To figure out, which logical volume you have to resize, use the lvdisplay
command. It shows all available logical volumes within a volume group:
#$ lvdisplay ubuntu-vg --- Logical volume --- LV Path /dev/ubuntu-vg/root LV Name root VG Name ubuntu-vg LV UUID 4KUUhN-PHJ2-5Q26-82Du-os3m-HusW-uKE2eI LV Write Access read/write LV Creation host, time ubuntu, 2016-06-30 08:18:44 +0000 LV Status available # open 0 LV Size 20,00 GiB Current LE 5120 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 252:0 --- Logical volume --- LV Path /dev/ubuntu-vg/swap_1 LV Name swap_1 VG Name ubuntu-vg LV UUID s4o2Jm-F0Sz-SysU-x8f7-eJaX-RRoJ-7piRMB LV Write Access read/write LV Creation host, time ubuntu, 2016-06-30 08:18:44 +0000 LV Status available # open 0 LV Size 2,00 GiB Current LE 511 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 252:1
With this we have successfully prepared our volume group ubuntu-vg to hold our new docker storage.
Install docker
Next we have to install docker. Since docker is supported very well by most distributions we have a recent version in the ubuntu repository. So all we have to do is
#$ sudo apt-get install docker.io ...
After successful installation we can see the docker daemon is up and running:
sudo service docker status ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Active: active (running) since Di 2016-07-12 15:25:26 CEST; 21s ago Docs: https://docs.docker.com Main PID: 4993 (docker) CGroup: /system.slice/docker.service └─4993 /usr/bin/docker daemon -H fd:// Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.400038358+02:00" level=info msg="Graph migration to content-addressability took 0.00 seconds" Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.415527884+02:00" level=info msg="Firewalld running: false" Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.457709818+02:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.519826610+02:00" level=warning msg="Your kernel does not support swap memory limit." Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.560283918+02:00" level=info msg="Loading containers: start." Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.560353891+02:00" level=info msg="Loading containers: done." Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.560365431+02:00" level=info msg="Daemon has completed initialization" Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.560382383+02:00" level=info msg="Docker daemon" commit=20f81dd execdriver=native-0.2 graphdriver=aufs version=1.10.3 Jul 12 15:25:26 docker-lvm systemd[1]: Started Docker Application Container Engine. Jul 12 15:25:26 docker-lvm docker[4993]: time="2016-07-12T15:25:26.587885630+02:00" level=info msg="API listen on /var/run/docker.sock"
For convenience i put my user into the docker group so i don't have to sudo all the docker commands:
#$ sudo usermod -aG docker <username>
after log out - log in my user is able to perform docker commands since it has access to docker's communication socket file:
#$ docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 1.10.3 Storage Driver: aufs Root Dir: /var/lib/docker/aufs Backing Filesystem: extfs Dirs: 0 Dirperm1 Supported: true Execution Driver: native-0.2 Logging Driver: json-file Plugins: Volume: local Network: null host bridge Kernel Version: 4.4.0-28-generic Operating System: Ubuntu 16.04 LTS OSType: linux Architecture: x86_64 CPUs: 2 Total Memory: 3.859 GiB Name: docker-lvm ID: HQAE:HBS4:6RIR:7T4F:PTRU:3JBZ:ZVDM:T4LH:H24F:XUVW:VL22:SHKT WARNING: No swap limit support
What we can see now is that docker is using aufs as storage driver, which seems to be fine in development environments but also lacks performance, which is why we want to use direct-lvm. To do so we follow the instructions from the docker's website.
configuring logical volume for direct-lvm
Since we already have a volume group we don't need to create one. therefore we skip steps 1,2, 3 and 4 and resume creating a logical volume named thinpool. Since we already have a logical volume in our volume group we obviously cannot use 95% of the volume group's size for our thinpool. Instead for the thinpool itself we take 95% of whats left and for the thinpool metadata 25% of whats left of whats left (:
#$ sudo lvcreate --wipesignatures y -n thinpool ubuntu-vg -l 95%FREE Logical volume "thinpool" created. #$ sudo lvcreate --wipesignatures y -n thinpoolmeta ubuntu-vg -l 25%FREE Logical volume "thinpoolmeta" created.
Next we convert our pool into a thinpool:
#$ sudo lvconvert -y --zero n -c 512K --thinpool ubuntu-vg/thinpool --poolmetadata ubuntu-vg/thinpoolmeta WARNING: Converting logical volume ubuntu-vg/thinpool and ubuntu-vg/thinpoolmeta to pool's data and metadata volumes. THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.) Converted ubuntu-vg/thinpool to thin pool.
Next we configure autoextension for the thinpool:
#$ sudo mkdir -p /etc/lvm/profile
#$ sudo tee /etc/lvm/profile/docker-thinpool.profile <<EOF
activation {
thin_pool_autoextend_threshold=80
thin_pool_autoextend_percent=20
}
EOF
afterwards we enable the profile:
#$ sudo lvchange --metadataprofile docker-thinpool ubuntu-vg/thinpool
Logical volume "thinpool" changed.
To verify that the thinpool is monitored, do:
$# sudo lvs -o+seg_monitor
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert Monitor
root ubuntu-vg -wi-ao---- 20,00g
swap_1 ubuntu-vg -wi-ao---- 2,00g
thinpool ubuntu-vg twi-a-t--- 14,07g 0,00 0,02 monitored
All we have to do now is to tell the docker-daemon to use this storage for its images and containers. Since Ubuntu 16.04 is now using systemd the /etc/default/docker
file is not applying anymore. The easiest way to configure the new storage is to create a file named /etc/docker/daemon.json
:
#$ sudo tee /etc/docker/daemon.json <<EOF
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/ubuntu--vg-thinpool",
"dm.use_deferred_removal=true"
]
}
EOF
Now we stop the docker-daemon, remove the folder /var/lib/docker
and start the daemon again:
$# sudo systemctl stop docker
$# sudo rm -rf /var/lib/docker
$# sudo systemctl start docker
Congratulations! Now we have an up and running docker-daemon with a direct-lvm storage layer.
The headache
After all this i was very upset, that after reboot the docker-daemon was not coming up.
#$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Di 2016-07-12 16:22:33 CEST; 1min 30s ago
Docs: https://docs.docker.com
Process: 1880 ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS (code=exited, status=1/FAILURE)
Main PID: 1880 (code=exited, status=1/FAILURE)
Jul 12 16:22:33 docker-lvm systemd[1]: Starting Docker Application Container Engine...
Jul 12 16:22:33 docker-lvm docker[1880]: time="2016-07-12T16:22:33.304838820+02:00" level=fatal msg="Error starting daemon: error initializing graphdriver: devicemapper: Non existing device ubuntu--vg-thinpool"
Jul 12 16:22:33 docker-lvm systemd[1]: docker.service: Main process exited, code=exited, status=1/FAILURE
Jul 12 16:22:33 docker-lvm systemd[1]: Failed to start Docker Application Container Engine.
Jul 12 16:22:33 docker-lvm systemd[1]: docker.service: Unit entered failed state.
Jul 12 16:22:33 docker-lvm systemd[1]: docker.service: Failed with result 'exit-code'.
Jul 12 16:22:33 docker-lvm systemd[1]: docker.service: Start request repeated too quickly.
Jul 12 16:22:33 docker-lvm systemd[1]: Failed to start Docker Application Container Engine.
The status messages indicated that my thinpool was not available. lvdisplay
said the same:
#$ sudo lvdisplay ubuntu-vg/thinpool
--- Logical volume ---
LV Name thinpool
VG Name ubuntu-vg
LV UUID Or2cLx-ENFq-sZqh-kxUw-0FEQ-rEzi-vXCi3C
LV Write Access read/write
LV Creation host, time docker-lvm, 2016-07-12 15:57:06 +0200
LV Pool metadata thinpool_tmeta
LV Pool data thinpool_tdata
LV Status NOT available
LV Size 14,07 GiB
Current LE 3601
Segments 1
Allocation inherit
Read ahead sectors auto
LV Status NOT available
So i tried to activate it:
#$ sudo lvchange -ay ubuntu-vg/thinpool
/usr/sbin/thin_check: execvp failed: Datei oder Verzeichnis nicht gefunden
Check of pool ubuntu-vg/thinpool failed (status:2). Manual repair required!
After some research i found that the tools for check and repair are configured in /etc/lvm/lvm.conf
and there i found the following paragraph:
# Configuration option global/thin_check_executable.
# The full path to the thin_check command.
# LVM uses this command to check that a thin metadata device is in a
# usable state. When a thin pool is activated and after it is
# deactivated, this command is run. Activation will only proceed if
# the command has an exit status of 0. Set to "" to skip this check.
# (Not recommended.) Also see thin_check_options.
# (See package device-mapper-persistent-data or thin-provisioning-tools)
# This configuration option has an automatic default value.
# thin_check_executable = "/usr/sbin/thin_check"
So i looked for the packages device-mapper-persistent-data and thin-provisioning-tools and i found that there is a package thin-provisioning-tools, which is not installed:
#$ sudo apt-cache policy thin-provisioning-tools
thin-provisioning-tools:
Installiert: (keine)
Installationskandidat: 0.5.6-1ubuntu1
Versionstabelle:
0.5.6-1ubuntu1 500
500 http://de.archive.ubuntu.com/ubuntu xenial/universe amd64 Packages
After installing it i could finally activate my docker storage again:
#$ sudo lvchange -ay ubuntu-vg/thinpool
And also after reboot the docker's logical volume was activated and the docker-daemon was started successfully.