How to get the most out of Docker technology
Docker, software that performs “containerization,” or virtualization, at the OS level, is gaining popularity among developers. The container engine technology for Windows and Linux-based applications promises enhanced developer productivity, faster deployment and time to market, IT operational efficiency, IT infrastructure reduction and more rapid issue resolution.
Many organizations are investigating the potential for and benefits of introducing Docker to their application development and deployment processes. They have questions and doubts about compatibility, creating containers, security and tools. Here, we offer insights and tips based on our application development experience.
Can Docker work with any application?
When examining Docker and its offerings, the first question we generally hear is whether it is necessary to adapt an existing — potentially, a legacy — application, or whether Docker can work with any application, new or old.
The best fit for Docker are applications that have been broken down into microservices — smaller, composable pieces that work together — preferably without a GUI and ideally with only one Docker container per microservice. This setup makes use of encapsulation, scalability, distribution, availability and other Docker advantages. If an application is currently in development, it is useful to break it down into microservices from the outset and develop them for Docker.
Many existing applications don’t currently fit this model, and it may not be feasible or make economic sense to move all applications to a microservices architecture.
Applications with web GUIs can also be provisioned with Docker containers, while applications with a standard GUI such as Microsoft Word can’t be used from the “outside” when they are encapsulated within a container. If a traditional application with a standard GUI is placed into Docker, it needs to be rewritten by partitioning it down into microservices and changing the GUI to a web interface. For legacy applications without a “fat-customer” GUI, tools are available to transform them into containers. As the underlying architecture of Windows and Linux-based applications is similar, this approach applies to both application types.
As for compatibility with servers and operating systems, Docker uses the kernel of the underlying OS running on a server, whether it is Linux, Windows or other supported platforms. Therefore, the Linux application image will run on a server with an operating system that uses a Linux kernel, as well as on a different Linux flavor. It will not, however, run on a “native” Docker installation on a Windows server unless the server is a Hyper-V virtual machine, which has a Linux kernel. A “native” Windows image will not work on a Linux Docker installation.
To decide whether you can package an application into a Docker image and then create a container, you need to gather the following information:
- Compatibility with one of the Docker-supported OS
- Capability to run without a standard GUI (only web GUI)
- How the binary is built
- Required parameters to configure the application and how they should be set up
- Port mapping
Setting up the containers
After deciding on application compatibility with Docker, you need to set up the containers. A valuable benefit when using Docker in a DevOps context is that it offers “Run/Ship/Deploy anywhere.” This means that an application well-structured with containers (i.e., microservices) during its creation will also run accurately in production. Here are some recommendations for achieving the best results:
Use more than one Docker image per application
There are three main reasons to use more than one Docker image per application:
- Each microservice should run in its own container/image
- Each container should run just one single application process. If an application is dependent on another service such as a database, LDAP directory or portal, the two should be created as separate containers. Although it is technically possible to package databases into Docker images as long as they run on a Docker-supported OS, we recommend packaging them only if they are small. We discourage Docker usage for large legacy databases
- You can use several Docker images for versioning of different application environments — e.g., development, test and production — which then can be named accordingly (“tagging” images with a version number or a text phrase such as “stable”).
To use the same Docker image in test, preproduction and production, you must separate the image from the configurations, which are specific for each environment. The application can make use of these configurations via external config files in shared storage (volumes).
Link containers
Docker images often need to communicate with one another, which you can ensure by linking the containers. If a potentially large number of Docker images will work together without interference, you can take advantage of newer Docker versions that offer two top-level objects for optimal collaboration: networks and services.
Networks can be created and managed independently from containers. Containers can be assigned to networks so that they can communicate with other containers in the same network. Containers can publish services in the whole cluster (not just a single host) and can be addressed by using these services. To manage larger environments, we recommend using either Docker Swarm or Kubernetes orchestration services.
To decide whether you can package an application into a Docker image and then create a container, you need to gather the following information:
- Compatibility with one of the Docker-supported OS
- Capability to run without a standard GUI (only web GUI)
- How the binary is built
- Required parameters to configure the application and how they should be set up
- Port mapping
Tips for creating images
When considering image creation, the question of size often arises, partly because it has an impact on hosting space. Since Docker images include the application software, frameworks and libraries, they tend to be quite large. However, you can reduce their size, either by choosing a smaller base image or by re-architecting applications and then using smaller units or microservices. Using smaller base images can potentially have an impact on development teams, as smaller frameworks and libraries introduce more complexity and resulting risk.
There are two ways for an application to create files in an image: interactively or with statements in a Dockerfile.
In the interactive approach, a container based on an image must be up and running. Afterward, you can create files in this container and change the image by inserting the “docker commit” command.
Both ways are supported in Docker Union File Systems (UFS). Docker images consist of multiple read-only file system layers. For each statement in a Dockerfile, a new read-only layer residing on top of all former layers will be created. If a container is created based on an image, an additional read/write layer is created on top, which is then used for changes to files in the container. Files created by an application in the container exist only in the read/write layer of the container file system and will disappear if the container is deleted. If they should be added to the image itself, a “docker commit” command needs to be executed.
Files external to the container can be accessed via volumes, which are files or directories mounted on the host and do not belong to the standard UFS. Volumes can also be used by other containers, and all changes to them are made directly on the host file system. To enable volumes during image creation, you can declare a directory as a volume in one of three ways: (1) writing a VOLUME statement in a Dockerfile, (2) using “Docker run” with flag “-v” or (3) referencing a Docker volume provided by a Docker volume plug-in.
Alleviating security concerns
Despite the apparent benefits of introducing and using Docker, security concerns still torment many customers. To avoid any security impact, we highly recommend signing Docker images. Doing this will help avoid using or distributing corrupted or manipulated Docker images, ensure secure (IT) operations and guarantee that the Docker images are exactly the ones published by the developer.
If images are not signed, attacks can start on other host users’ files directly out of the Docker containers because there are currently no user namespaces, and file unique identifiers (UIDs) are directly imaged on the host system. Furthermore, Docker containers access some kernel commands via whitelists, and the kernel’s vulnerability can lead to a breach of the container onto the host. Moreover, the daemon runs with root administrative rights, and currently there are no options to restrict executable commands on a role-based model. Therefore, Docker and DXC Technology strongly urge that access be granted only to trustworthy users and signed images.
When developing Docker images in an ideal setup, with the Docker Trusted Registry scanner in place, operations teams can build an image and then sign it using Docker Content Trust — in effect, blessing the image as ready for developers to use. Developers can then pull down the approved image to populate into the internal programs group. Once finished, they can upload the image to a registry. The scanner shows the results, allowing developers to update the package when vulnerabilities are eliminated.
The Docker Cloud hosted container service now offers the ability to scan containers for known security vulnerabilities using a security service called Docker Security Scanning. Using this service, the Docker Trusted Registry scans images in customer repositories to verify that they are free from known security vulnerabilities or exposures. The scanner automates the process of identifying weak code that could be exploited by attackers.
In a typical setup, developers build a Docker image and then push it to the cloud. Before it is stored in the cloud, the image is scanned for software packages with known vulnerabilities. The scan generates a list of all the software packages and libraries it can identify and cross-references them against the Common Vulnerabilities and Exposures (CVE) list. The developer gets a notification if any vulnerabilities are in the image. By contrast, most administrators usually find out about a new high-profile vulnerability only through mailing lists or by tech press, and lower-profile vulnerabilities may not even be discovered at all.
Recommended tools for Docker developers
When evaluating Docker’s impact on development teams’ setup times, customers often want to know whether a Docker image with developer tools is available or could be created. From a DXC point of view, there is no “ready-to-go Docker developer image” because, in general, images do not contain user interface (UI) visualization. However, one approach is to use Eclipse Che, a Docker-based integrated development environment (IDE) that uses an internet browser as the front end. Eclipse Che works with any container, even multicontainer runtimes, running the workspace on Kubernetes, OpenShift or plain Docker.
Tools for Docker developers are described below.
Administration
To best administer Docker images, we suggest staying within one technology, meaning that if Docker is the chosen product, the repository and image management should also come out of the Docker product family. Docker EE is a good choice, as it covers the full product life cycle. It uses repositories, which are GUI-driven web services that can be accessed directly from anywhere via an API and a software development kit (SDK).
Repositories offer advantages over file systems — for example, file systems cover only a general purpose, are designed more or less for local usage, and offer limited user management. Repositories, which can be single or multiple, vary in look and feel, formats and cost models. To set up internal and supplier repositories, we recommend using multiple repositories instead of a central repository — and always only one repository per application environment (development, testing, staging or production).
Automation
To automate image creation, testing and application validation, we recommend using a basic Jenkins Pipeline:
- First, trigger the Pipeline via event or time to automatically build an image and push it to a repository manager.
- Second, use an additional Pipeline to run the Docker container with the newly created image and check basic features such as network and file systems.
- Optionally, trigger the Pipeline to run specific application tests, such as processes, ports and core software functions.
Google’s Container-Structure-Tests framework or binaries can be used to automate
the following Docker image tests with YAML or JSON config files:
- Command tests: Test output or errors of a specific command
- File existence tests: Make sure a file is or is not present in the image’s file system
- File content tests: Make sure files in the image’s file system contain or do not contain specific contents
- Singular metadata test: Make sure certain container metadata is correct
Configuing logs
To log an application in Docker images, use Dockerfile to configure the logs to standard out or standard error. To log an application outside a container, access logs interactively via Docker logs $ID or file-based on the Docker host logging driver.
Monitoring containers
Docker checks the availability (status) of an application in a container, and this feature can be augmented with a health check using a shell command such as curl. The incumbent monitoring systems need to be enhanced so that they check and analyze Docker logs. Complex Docker setups with multiple microservice applications have increasable logs that can be consolidated, extended and visualized with additional tools such as ELK Stack.
Monitoring can further check the workload of a container — including CPU, memory, disk space and network I/O) — with Docker stats. More complex Docker environments need specifically adapted monitoring tools such as Prometheus or Heapster (Kubernetes).
A technology for today and tomorrow
Docker is helping to revolutionize software development, making environments more portable and simplifying the development process. Enterprises would be wise to consider incorporating Docker technology into their application development processes — a task that’s not difficult if developers are sufficiently versed in the available tools and techniques.