Container Images for OpenShift (Part 1): Objectives
Container Images for OpenShift (Part 1): Objectives
Do you design container images? Here is some advice to help your image's users use your images the way you intend to, particularly on OpenShift.
Join the DZone community and get the full member experience.Join For Free
Insight into the right steps to take for migrating workloads to public cloud and successfully reducing cost as a result. Read the Guide.
This is a transcript of a session I gave at EMEA Red Hat Tech Exchange 2017, a gathering of all Red Hat solution architects and consultants across EMEA. It is about considerations and good practices when creating images that will run on OpenShift. The content is structured in a series of four posts:
- Structuring your images
- Making your images consumable
- Cloud readiness
This is the first part, where we'll be looking at common objectives associated with the usage of container images. These objectives are to be taken into consideration during the design phase of your images.
One of the reasons why containers are seeing so much interest is that they allow the packaging of an application with all its dependencies in a single deployment unit. This deployment unit, the golden image, is then moved from one environment to the next one. You may move it from integration to UAT or from staging to production, for instance. Having all dependencies in a single unit guarantees that what has been validated in a previous stage is also what is being deployed in the next one.
Before the rise of containers, I have seen companies struggling with application promotion. Some of them had written long and detailed installation procedures that had to be manually and carefully followed. Others had invested a large amount of effort in building automation based on Puppet, Chef, or Python scripts. Furthermore, the initial effort also required a significant maintenance effort to deal with evolution and changes. Most of them were experiencing issues here and there. What containers bring is a standard and simple approach to this. Application dependencies (operating system, runtime environment (JVM and the like), libraries, and some of the configurations) are part of the container image, which simply gets used for starting container instances in one environment or another.
Subsequent builds of the image should produce the same result. This is important for having a stable reference that can be used for patches, upgrades, and further evolution. This requires that dependencies (libraries or other images) used for creating the container image to be uniquely referenced and version.
Agile Standard Operating Environment
There are obvious benefits of a level of homogeneity in the enterprise software landscape:
- Time and cost reduction for maintenance by limiting the number of combinations that may need to be supported.
- Having a known environment when operations are required. The operator knows what to expect, which may be critical for resolving incidents in a timely fashion.
- Compliance is easier with a few different environments.
- It is easier to staff. Employees can more easily move from one domain to another as they rely on the same technologies.
This should be taken into consideration when defining the image landscape. That said, strictly enforced and slow evolving SOE has sometimes become an obstacle to business reactivity. Through reusability and the ease of having a central change applied to multiple targets, container images can help with addressing this aspect.
It is important to limit the number of places where components get injected into images like frameworks, application servers, drivers, and scripts. These may be used for ground functionality, connectivity, monitoring, asset tracking and management, security, etc. The idea is that the components of the SOE are created once. Layering approaches with inheritance and composition may support this. Considering the global image landscape rather than single image individual is important in this respect.
The central injection point mentioned in the previous section also provides a central point for patching and upgrades. These changes, possibly made available as container images by software providers, need to be automatically cascaded so that maintenance is made straightforward.
In the pre-container era, I saw companies struggling with keeping their Java or application servers up-to-date with patches and security fixes. Container technologies combine the possibility of applying changes in a central place with having them rolled out across the software landscape with little effort and possibly no downtime. This can have a huge effect in term of security and reliability. Therefore, images need to be designed with maintainability in mind.
A minimal consumption of resources (RAM, CPU, storage, etc.) is an obvious objective when creating applications and, hence, the images used for their packaging. It means higher density and reduced costs. Containers have the advantage compared to virtual machines in that they share the same kernel instead of creating additional instances. This can be pushed further by having containers sharing layers. This is made possible by following some of the objectives already mentioned: SOE and reusability. This plays a bigger role in term of final RAM and storage usage than the image size per se, although the size does impact the time required for pushing and pulling an image.
Besides keeping operating systems, application servers, and other libraries patched in a timely fashion with the latest security fixes, here are some other important aspects in regard to security that need to be taken into consideration during image creation:
- Reducing the attack surface: This can be done by limiting what is installed inside the image to only what is required for running the container.
- Limiting capabilities: Privileged containers should be exceptions undergoing specific security measures. Other capabilities, like mounting the host file system, binding host ports, or running with a specific user id that may match a user on the host, should be granted only when no other option is available and after the security impact has been scrutinized and the risks mitigated.
- Limited resources: The container image should be built in such a way that it is able to run under limited resources (CPU, RAM, network, storage) so that it may not be a vector for denial of services attacks against the host and other containers running on the same host.
PaaS platforms like Kubernetes and OpenShift provide monitoring and self-healing mechanisms. Readiness and liveness probes ensure that:
- No requests are sent to a container when it is not ready, which, in practice, means that either it hasn’t finished starting, it is terminating, or one of its dependencies (database, service) is not available.
- The container is restarted when its liveness probe is unsuccessful. Restarting a container brings it to its initial state, similar to a factory reset.
As an image designer, your responsibility is to make sound readiness and liveness probes available.
Another aspect is that when OpenShift wants to terminate a container, it first takes the container out of the request processing rotation and sends a SIGTERM signal. It gives time for the application to gracefully shutdown before it is terminated. If the allowed period has expired, then it uses the SIGKILL signal. In this regard, the application inside the image should finish processing in-flight requests, releasing resources, and terminate when a SIGTERM signal is received.
Easy to Consume
Reusability has already been mentioned as an objective. This can, however, be achieved at a good level only when images are easy to consume, which includes the following aspects:
- Easiness to extend by inheriting, composing, or making use of extension points. But extension points need to have been thought of in the first place. As an image designer, it is your responsibility to think of how one may want to add capabilities or customize your image and to make it possible in a simple way.
- Documentation: This includes documenting the image's purpose, its usage, and also important aspects of running it, like its entry point, used ports, directories where the application may write data, etc. Providing a quick start for your image is also a way to help the end user understand how the image is supposed to be used.
I hope you found this first part interesting. In the following articles, we will see what techniques and approaches are available for achieving the objectives that have been stated here. Stay tuned!
Published at DZone with permission of Frederic Giloux , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.