Tuesday, April 19, 2016

Docker: Btrfs Storage in Practice

One of the ways Docker makes containerization so easy is by managing an overlay-style filesystem, allowing containers and images to incrementally change the filesystem layout of the image without requiring large copies of multiple images kicking around. This is a copy-on-write approach: parent layers are held read-only, and changes are reflected in the working layer.

The Docker has support for different image/container layer storage drivers:[7]
  • aufs
  • btrfs
  • devicemapper
  • overlay
  • vfs
  • zfs 
Your choice of storage driver can affect the performance of your containerized applications. So it’s important to understand the different storage driver options available and select the right one for your application.

In this article, we will focus only on btrfs (B-tree file system) storage.

Storage Driver and Backing Filesystem


To begin with, let's study different storage-related entities that form a container:
  • Images
    • Is a tagged hierarchy of read-only layers plus some metadata
    • docker images command can be used to list all images and report their virtual sizes
  • Image layers
    • Each successive layer (with a UUID tag) builds on top of the layer below it
    • Reuse layers can improve image build time[8]
      • Each Dockerfile instruction generates a new layer
      • You should put instructions least likely to change at the top of your Dockerfile to reuse layers as much as possible and try to make changes only at the bottom of your Dockerfile.
    • Image layers can be shared among images
    • Docker limits the number of layers to 127
      • Layers don’t come for free, depending on storage driver used there are some penalties to pay
        • For example, in AUFS, each layer can introduce latency to container write performance on the first write to each file existing in the image layers stack, especially if the file is big and exists below many image layers.
    • docker history command can be used to list all layers of an image
  • Storage drivers
    • Docker has a pluggable storage driver architecture. 
      • This gives you the flexibility to “plug in” the storage driver that is best for your environment and use-case.
    • Each Docker storage driver is based on a Linux filesystem or volume manager
    • The Docker daemon can only run one storage driver, and all containers created by that daemon instance use the same storage driver.
    • Each storage driver is free to implement the management of image layers and the container layer in its own unique way. 
      • This means some storage drivers perform better than others in different circumstances.
      • See [7] to learn more on which storage driver you should choose

Storage Driver and Backing Filesystem


Which storage driver you use, in part, depends on the backing filesystem you plan to use for your Docker host’s local storage area. Some storage drivers can operate on top of different backing filesystems. However, other storage drivers require the backing filesystem to be the same as the storage driver. For example, the btrfs storage driver requires a btrfs backing filesystem. 

The following table lists each storage driver and whether it must match the host’s backing file system or not:

|Storage driver |Must match backing filesystem |
|---------------|------------------------------|
|overlay        |No                            |
|aufs           |No                            |
|btrfs          |Yes                           |
|devicemapper   |No                            |
|vfs*           |No                            |
|zfs            |Yes                           |

The btrfs Backend


The backing filesystem refers to the filesystem that was used to create the Docker host’s local storage area under /var/lib/docker.  The brtfs (B-tree file system) backend requires /var/lib/docker to be on a btrfs filesystem and uses the filesystem level snapshotting to implement layers.

# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvdb4       11G  7.1G  2.5G  75% /var/lib/docker
<snipped>

# mount -l
/dev/xvdb4 on /var/lib/docker type btrfs (rw)
<snipped>


You can find the layers of the images in the folder /var/lib/docker/btrfs/subvolumes.  Each layer is stored as a btrfs subvolume inside the folder  and start out as a snapshot of the parent subvolume (if any).

The btrfs driver is very fast for docker build - but like devicemapper does not share executable memory between devices. Mounting /var/lib/docker on a different filesystem than the rest of your system is recommended in order to limit the impact of filesystem corruption.

You can set the storage driver by passing the --storage-driver= option to the docker command line, or by setting the option on the DOCKER_OPTS line in the /etc/default/docker file.  For example, to set the btrfs storage driver, do:
# docker -d -s btrfs -g /mnt/btrfs_partition ...

  -s, --storage-driver=""  Storage driver to use
  -g, --graph=""           Path to use as the root of the Docker runtime.  
                             Default is /var/lib/docker.

To verify if btrfs storage driver is used in your docker container, do:
# docker info
Containers: 1
Images: 19
Storage Driver: btrfs
...

References

  1. Btrfs
  2. LVM dangers and caveats
  3. 20 Linux Server Hardening Security Tips
  4. ZFS Vs. BTRFS
  5. How to Use Different Docker Filesystem Backends
  6. Daemon storage-driver option
  7. Select a storage driver
  8. Optimizing Docker images for image size and build time
  9. Docker Filesystems: Understanding the btrfs Backend (Xml and More)
  10. Oracle WebLogic Server on Docker Containers (white paper)
  11. WebLogic on Docker (GitHub)
    • Sample Docker configurations to facilitate installation, configuration, and environment setup for DevOps users. This project includes quick start dockerfiles and samples for both WebLogic 12.1.3 and 12.2.1 based on Oracle Linux and Oracle JDK 8 (Server).