Understanding LXC and Docker Containers on Oracle Linux

Version 5

    by Ginny Henningsen

     

    This article describes typical use cases for Linux containers (LXC) and Docker containers, and provides quick-reference guides for common LXC and Docker commands.

     

    Table of Contents
    Choosing a Container Technology
    Container Technologies: The Basics
       LXC Containers
       Docker Containers
    Important LXC and Docker Concepts
    "Best Fit" Use Cases
    Final Thoughts
    Learn More
    Appendix A
       LXC Commands Quick Reference
       Docker Commands Quick Reference
    About the Author

     

    Choosing a Container Technology

     

    Why should you deploy applications in a container? There are well-known benefits associated with container technologies: the ability to sandbox running applications, control system resources, and easily replicate application environments to speed deployments. But deciding which technology to select for application delivery on Oracle Linux—LXC or Docker—requires some thought. While there are advantages with respect to each container technology, there really is no single "best" choice—the optimal solution depends on the specific application and requirements. In other words, your best choice is truly the technology that's the best fit for the task at hand.

     

    This article explores LXC and Docker container technologies to help IT administrators understand their similarities and differences, as well as typical use cases. When the time comes to use either LXC or Docker, this article also includes a handy quick reference "cheat sheet" containing sample commands for commonly performed tasks.

     

    Container Technologies: The Basics

     

    Let's take a quick look at the similarities and differences between LXC and Docker containers and how they differ from well-established hypervisor technologies (such as Xen, Microsoft Hyper-V, or VMware's ESXi). As shown in Figure 1, traditional virtualization solutions implement the hypervisor as an additional software layer on top of the native host operating system. The hypervisor supports multiple virtual machine (VM) guests, performing hardware emulation to access underlying physical server resources. The additional work of emulation imposes some degree of performance overhead, which is why LXC and Docker containers are considered a lighter-weight approach to isolating applications in comparison to conventional virtualization solutions.

     

    f1.png

    Figure 1. LXC and Docker containers versus hypervisor-based virtualization.

     

    With traditional virtualization solutions, each VM guest contains an isolated, full-blown operating system instance with its own OS kernel and user space. This allows a single physical machine to support applications running simultaneously on completely different operating systems while fully sandboxing them from one another. As an example, Oracle VM (which uses the Xen hypervisor) can support applications that run within separate Linux, Oracle Solaris, and Microsoft Windows guest VMs.

     

    LXC Containers

     

    In contrast, LXC containers share the same kernel space (with the same drivers and kernel modules) as the host operating system, but each container has its own isolated user space (Figure 1). LXC containers take advantage of the cgroups resource management and namespace capabilities in Linux, which is how LXC containers implement resource control and isolation.

     

    LXC containers can be deployed in two different ways, either as application or as system containers (although they are primarily deployed as system containers):

     

    • LXC application containers can isolate a single application, similar to a chroot jail. Application containers make it easy to scale an application or a service, because additional container instances can be deployed quickly in a cookie-cutter fashion to meet demand. LXC application containers share the host's root file system and are created with lxc-execute, which runs a cut-down version of init. Because Docker containers provide similar application container functionality and offer other advantages, Docker containers are typically implemented rather than LXC for use cases where application containers are the best fit.
    • LXC system containers are the typical use case for LXC containers. Each LXC system container runs an init process that provides an isolated execution environment, enabling support for a complete multiprocess application stack. Like a hypervisor-based virtualization solution, LXC system containers provide sandboxing of the entire stack—processes running in one container can't see or impact processes running in another. But unlike a hypervisor-based virtualization solution using VMware ESXi, Microsoft Hyper-V, or Oracle VM, LXC containers can support only Linux guest VMs that use the host's kernel and device drivers.

     

     

    Docker Containers

     

    A Docker container is an application container best suited for a single application or service. Unlike an LXC system container, a Docker container does not run a separate init process, so a single container can't easily host a multitiered stack. Instead, multiple Docker containers (each running a separate service or application) must be connected to one another to construct a full multitiered stack. Docker containers are ideally suited for isolating a service, such as a web service, and container instances can be easily cloned to scale the service and support greater capacity.

     

    Docker containers are built using file system layers that capture the complete application environment into a virtual container. The base Docker container image consists of OS binaries and libraries. When the base image is modified—typically by installing application packages and configuring software—the new container shares the same binaries and libraries as the base, as depicted in Figure 1.

     

    Docker containers are managed and run by the Docker Engine, which makes system calls to the underlying OS kernel. You can define a small text file (a Dockerfile) that captures the application container and all of its dependencies. This file can then be moved to any system that has a Docker Engine and be rebuilt, greatly simplifying application portability. At this time, Docker Engines are available for all major Linux distributions and Microsoft operating systems. The ability to capture an application, its environment, and all dependencies and move that across platforms is a significant difference in comparison to Linux-only LXC containers.

     

    Important LXC and Docker Concepts

     

    Because they are both lightweight virtualization tools, LXC and Docker containers have some notable similarities. Unlike a traditional hypervisor-based approach, LXC containers don't require the duplication of a full operating system installed as a guest, and Docker containers rely on efficiently layered images. Both LXC and Docker containers are open source–based, taking advantage of private Linux namespaces to sandbox applications and Linux cgroups to enable resource allocations and limits. And with both technologies, each container gets its own networking stack—a private network interface with a virtual bridge that allows the container to communicate while providing a separate VLAN. On Linux systems, both LXC and Docker containers can leverage an underlying Btrfs file system for fast snapshots, which helps to create new Docker image layers quickly or copy a root file system rapidly when you are cloning an LXC container.

     

    LXC and Docker containers also feature some significant differences—even beyond the prominent difference of an LXC system container supporting an init process while a Docker container does not. Docker containers are built from previously captured Docker images (Figure 2). Images can be downloaded from (as well as published to) a Docker registry, either the public Docker Hub Registry maintained by Docker, Inc. or a privately maintained local registry. Oracle publishes images for various distribution versions of Oracle Linux 6, Oracle Linux 7, and MySQL to the Docker Hub Registry, and these can be freely downloaded and run as a base layer of a Docker container.

     

    Docker captures all changes made to a base image (such as installing application packages, resolving dependencies, and configuring settings), and you can commit these changes to build a new container image. To save space, Docker stores only changes to a base image in layers to reconstruct the full container image. You can build and run a container image interactively in the Docker Engine, but more commonly, a Dockerfile is defined to build the container image and run the application.

     

    f2.png

    Figure 2. Docker captures changes to a container image and uses layers to efficiently reconstruct a container.

     

    As an example, Figure 2 depicts a container image created on the local host by pulling an Oracle Linux 6.5 image from the Docker Hub Registry. The following docker run command runs the base Oracle Linux 6.5 image as a container named ContainerA, executing /bin/bash. Running the shell allows the user to then interactively install packages in the container.

     

    # docker run -i -t --name ContainerA oraclelinux:6.5 /bin/bash

     

    Suppose the administrator configures this Oracle Linux 6.5 image by installing Apache web server packages and web server content. Once the container is installed and configured, the configuration can be committed as follows, generating a new Docker image named ContainerAprime:

     

    # docker commit -m "httpd installed" `docker ps -l -q` ContainerAprime

     

    Figure 2 illustrates how Docker uses layering in container images—the A' image consists of the base Oracle Linux 6.5 image and changes made to that image to construct ContainerAprime. To deploy three web servers, the Docker Engine can be given these commands to generate three containerized web servers from the same image:

     

    # docker run -d --name websvr0 -p 8080:80 ContainerAprime /usr/sbin/httpd -D FOREGROUND
    # docker run -d --name websvr1 -p 8081:80 ContainerAprime /usr/sbin/httpd -D FOREGROUND
    # docker run -d --name websvr2 -p 8082:80 ContainerAprime /usr/sbin/httpd -D FOREGROUND

     

     

    It's important to understand that Docker container storage is ephemeral. Docker containers are transient in nature and data does not persist beyond the lifespan of a container's execution. Associating a data volume with a container using the -v flag allows the container to access, modify, and persist data on the host. For example, in the following command, the -v flag causes the directory /home/datadir on the host to be mounted as /var/lib/mysql (the default database location for MySQL) on the container db:

     

    # docker run --name db -d -e MYSQL_ROOT_PASSWORD=s5cr5t -v /home/datadir:/var/lib/mysql \
         mysql/mysql-server:db1

     

    In contrast to the layering used to construct Docker images, LXC containers are built from template scripts that live on the underlying host Linux system in /usr/share/lxc/templates.  The Oracle Linux 7 distribution includes templates that can create user space containers for many common Linux distributions (including Oracle Linux, Fedora, Gentoo, Ubuntu, and so on). These template scripts configure an LXC container, defining a number of system settings (such as the host name, root file system location, white-listed cgroup devices, and network interface configurations) and container resources. The lxc-oracle template also defines and sets default passwords for the oracle and root users. The -t argument specifies the template to be used in creating the container:

     

    # lxc-create -n ol7ctr1 -B btrfs -t oracle -- --release=7.latest

     

    Rather than editing a given template, configuration files in /usr/share/lxc/config can be used to tweak settings for a particular container type. It's also possible to modify configuration parameters (such as network settings) in a container-specific configuration file (such as /container/ContainerAprime/config).

     

    "Best Fit" Use Cases

     

    There are a number of use cases that are well-suited for LXC containers, including the following:

     

    • Running multiple Linux distributions and versions on the same machine. For example, on an Oracle Linux 7 host, applications can execute in LXC containers that run Oracle Linux 5, Oracle Linux 6, or Oracle Linux 7. Oracle tests and fully supports specific Oracle distribution versions as LXC containers (see "Supported Oracle Linux Container Versions" in the Oracle Linux documentation). This capability allows an existing application qualified on Oracle Linux 5, for example, to execute on an Oracle Linux 7 system. Templates are also provided for other Linux distributions (although these containers are not tested or supported by Oracle). Because LXC containers share the same kernel with the host, containerized applications cannot have any dependencies on release-specific drivers or kernel modules to function properly.
    • Creating isolated environments for application development and Quality Assurance (QA) testing. LXC containers can be deployed as development and test sandboxes, effectively quarantining applications. Oracle Database 12c is fully supported on LXC containers (see the Supported Virtualization and Partitioning Technologies for Oracle Database and RAC Product Releases web page and the Oracle Database Release Notes for Linux). This support makes it easy to deploy isolated development and testing environments for Oracle Database. An LXC container created for development purposes can be easily and quickly cloned to replicate the environment for QA testing.
    • Running a multitier software stack. Because LXC containers run an init process, they can easily support a multitier stack such as LAMP (Linux, Apache, MySQL, Perl/PHP/Python) for deploying web services. Depending on the load and server resources, multiple copies of the stack can be replicated to provision additional servers, with each copy of the stack isolated from the others. Each container has its own IP address, which allows each web server to operate independently. In the event of compromise (such as a Denial of Service attack), each container instance is isolated.
    • Creating environments that are subject to resource controls. Because LXC containers can use Linux control group mechanisms, it's possible to allocate and limit resources for applications within a container. For example, by limiting CPU shares or memory for containers that host noncritical workloads, an administrator can prevent resource starvation for critical workloads.

     

     

    Docker application containers are well-suited to deploying a single application service, such web service deployments of NGINX, Apache, lighttpd, or other httpd servers. By creating additional container instances, it's easy to scale service capacity and support additional demand—while at the same time isolating workloads for better service reliability.

     

    Docker offers additional flexibility because it captures deployment requirements and dependencies, making it easy to move an application service to a different platform. Docker registries also provide mechanisms for application publishing and sharing. Developers can publish an application (including the full environment in which the application runs properly) internally to a corporate registry or publicly on the Docker Hub Registry.

     

    Final Thoughts

     

    Container technologies have substantial benefits, especially given the need to isolate applications to increase service levels. Both LXC and Docker containers provide application sandboxes so that if a security flaw is exploited or a containerized application is compromised, it's unable to affect other applications and services running in other containers. Clearly there are use cases in which lightweight LXC or Docker container technologies are a good fit.

     

    Learn More

     

    The Oracle Linux documentation (the Oracle Linux Administrator's Solutions Guide for Release 6 or the Oracle Linux Administrator's Guide for Oracle Linux 7) has detailed sections on getting started with both LXC and Docker container technologies. There are also excellent resources for learning more about Docker at http://docs.docker.com/, including information on installing Docker on different operating systems, the Docker Engine User Guide, and the Docker reference manual.

     

    Appendix A

     

    To help you get started with LXC and Docker technologies, this appendix contains quick reference "cheat sheets." For both technologies, Table 1 and Table 2 list tasks that administrators frequently perform and an example of the appropriate command-line syntax.

     

    LXC Commands Quick Reference

     

    Table 1. Common LXC Tasks and Commands

                     

    Install and configure LXC using the installation instructions for your OS. For optimal performance when cloning, create a Btrfs file system and mount it for LXC containers to use.

     

    See the chapter "Configuring Operating System Containers" in the Oracle Linux Administrator's Solutions Guide for Release 6 or the Oracle Linux Administrator's Guide for Oracle Linux 7.
    Run a bashshell in an application container.

     

    lxc-execute -n mycon1 -o /tmp/mycon1.log /bin/bash
    Create an Oracle Linux 6 system container named ol6ctr1 using the lxc-oracletemplate script.

     

    lxc-create -n ol6ctr1 -B btrfs -t oracle -- --release=6.latest
    Create the ol6ctr1 container using the lxc-oracletemplate and a configuration file.

     

    lxc-create -n ol6ctr1 -B btrfs -f config -t oracle -- --release=6
    Start the system container ol6ctr1.

     

    lxc-start -n ol6ctr1 -d -o /container/ol6ctr1_debug.log -l DEBUG
    Start the container ol6ctr1as a daemon that writes its diagnostic output to the specified log.

     

    lxc-start -n ol6ctr1 -d -o /container/ol6ctr1_debug.log -l DEBUG
    Log in to the container named ol6ctr1.

     

    lxc-console -n ol6ctr1
    Snapshot the root file system of a container for later cloning.

     

    btrfs subvolume snapshot /container/ol6ctr1/rootfs /container/ol6ctr1/rootfs_snap
    Clone a container (method 1).

     

    lxc-clone -o ol6ctr1 -n ol6ctr2
    Clone a container (method 2) using a previously captured snapshot of its root file system.

     

    lxc-create -n ol6ctr3 -B btrfs -t oracle -- --templatefs=/container/ol6ctr1/rootfs_snap
    List all LXC containers or list containers running on the host.

     

    lxc-ls
    lxc-ls --active
    List usage statistics and the IP address for a container.

     

    lxc-info --stats --ips --name ol6ctr1
    Attach to a running container from the host and run a command there.

     

    lxc-attach -n ol6ctr1 -- /bin/ps aux
    Suspend or resume container execution.

     

    lxc-freeze -n ol6ctr1
    lxc-unfreeze -n ol6ctr1
    Shut down a container after halting processes.

     

    lxc-stop --nokill -n ol6ctr1
    Shut down a container, terminating its processes immediately.

     

    lxc-stop -k -n ol6ctr1
    Monitor a container's state (for example, starting, running, stopping, stopped, and so on).

     

    lxc-monitor -n ol6ctr1
    Display the CPU cores on which container processes can run.

     

    lxc-cgroup -n ol6ctr1 cpuset.cpus
    Restrict container processes to run on the specified CPU cores.

     

    lxc-cgroup -n ol6ctr1 cpuset.cpus 0,1
    Set a container's relative share of CPU time.

     

    lxc-cgroup -n ol6ctr2 cpu.shares 256
    Limit a container to 256 MB when memory is low or contended; otherwise, set a hard limit of 512 MB.

     

    lxc-cgroup -n ol6ctr2 memory.soft_limit_in_bytes 268435456
    lxc-cgroup -n ol6ctr2 memory.limit_in_bytes 53687091
    Modify the file /container/name/configto apply cgroup settings permanently.

     

    lxc.cgroup.cpu.shares=256
    lxc.cgroup.blkio.weight=500

     

    Docker Commands Quick Reference

     

    Table 2. Common Docker Tasks and Commands

                                 

    Install and configure Docker using the installation instructions for your OS. For optimal performance in cloning, create a Btrfs file system and mount it for Docker containers to use.

     

    See the chapter “Installing and Configuring the Docker Engine on Oracle Linux 6” or “Installing and Configuring the Docker Engine on Oracle Linux 7” in the Oracle Linux Docker User's Guide.
    Display information about the host and the version of the Docker Engine.

     

    docker info
    Pull the Oracle Linux 6.6 image from the Docker Hub Registry.

     

    docker pull oraclelinux:6.6
    Pull the latest Oracle Linux 7 image from the Docker Hub Registry.

     

    docker pull oraclelinux:7
    Pull the latest MySQL image from the Docker Hub Registry.

     

    docker pull mysql/mysql-server
    Log in to the Docker Hub Registry as the user myaccount.

     

    docker login --username=myaccount --email=myemail@mycompany.com
    Push an image to the Docker Hub Registry for the user myaccount.

     

    docker push myaccount/myimage
    List the available images to run using the host's Docker Engine.

     

    docker images
    Create a container named guest from the Oracle Linux 7 image and run a bashshell interactively in the container.

     

    docker run -i -t --name guest oraclelinux:7 /bin/bash
    Show information about running containers or about all containers.

     

    docker ps
    docker ps -a
    Show information about the most recently created container, displaying only its numeric ID.

     

    docker ps -l -q
    Stop a running container named mynginx.

     

    docker stop mynginx
    Send the "hang up" (HUP) signal to a container.

     

    docker kill -s HUP mynginx
    Restart the container myginx.

     

    docker restart mynginx
    Create a new image mycon/httpd:r1from the latest running container, capturing the most recent changes.

     

    docker commit -m "configured web server" `docker ps -l -q` mycon/httpd:r2
    Show the history of changes made to a Docker image.

     

    docker history mycon:/httpd:r2
    Remove a container that is not currently running.

     

    docker rm guest
    Remove a container image.

     

    docker rmi oraclelinux:6.6
    Run httpdas a foreground process in the container, mapping port 80 in the container to port 8080 on the host.

     

    docker run -d --name ol6ctr1 -p 8080:80 mycon/httpd:r1 /usr/sbin/httpd -D FOREGROUND
    Run the NginX server as a background process.

     

    docker run -d -p 80:80 my_image nginx -g 'daemon off;'
    Run the HTTP server with a relative weight of CPU shares (the value is relative to the default of 1024).

     

    docker run -d --cpushares=512 --name guest2 -p 8080:80 mycon/httpd:r2
    Run the HTTP server with memory limited to 12 MB (the minimum hard limit is 4 MB).

     

    docker run -d --memory=12m --name guest2 -p 8080:80 mycon/httpd:r2
    Run a MySQL server in a container named db, mounting /home/datadir on the host as /var/lib/mysql.

     

    docker run --name db -d -e MYSQL_ROOT_PASSWORD=s5cr5t -v /home/datadir:/var/lib/mysql \
         mysql/mysql-server:db1
    List running processes in a container; display a live stream of container resource usage statistics.

     

    docker top guest; docker stats mycon1 mycon2
    Execute a command on a container, in this case, the container mycon1.

     

    docker exec -i -t mycon1 bash
    Display information about a container, in this case, the IP address of the container mycon1.

     

    docker inspect -f '{{ .NetworkSettings.IPAddress }}' mycon1
    Save the container (and all of its layers) as a tarfile.

     

    docker save -o webserver.tar mycon/httpd:r1
    Load a tar file from a saved container tarfile image.

     

    docker load -i webserver.tar
    Build a new Docker container image from a Dockerfile.

     

    docker build -t mycon/httpd:r2 ./Dockerfile
    A simple Dockerfile example that configures and runs an Apache web server on an Oracle Linux 7 base image:

     

    FROM oraclelinux:7
    MAINTAINER E. Jones <ejones@email-address.com>
    RUN yum install -y httpd perl && yum clean all
    COPY webpagecontent /var/www/html/index.html
    EXPOSE 80
    CMD /usr/sbin/httpd -D FOREGROUND

     

    Note: A more complex Dockerfile example is one that installs the Oracle WebLogic 12.2.1 Quick Install Distro, available here.
    Import a file system image from a local tarfile.

     

    cat myimage.tgz | docker import - example/imagelocal
    Copy from a container to the local file system (perhaps to save output from a container application to the local host).

     

    docker cp mycon1:/tmp/mydir /tmp
    Copy files or directories from a local file system on the host into a container.

     

    docker cp /tmp/app.conf mycon1:/etc/myapp

     

    About the Author

     

    Ginny Henningsen has worked for the last 18 years as a freelance writer developing technical collateral and documentation for high-tech companies. Prior to that, Henningsen worked for Sun Microsystems, Inc. as a systems engineer in King of Prussia, Pennsylvania, and in Milwaukee, Wisconsin, with a focus on solution architectures, operating systems, and security. Henningsen has a BA from Carnegie-Mellon University and an MSCS from Villanova University.