As the world moves farther down the path toward containerizing every workload, security is becoming a central part of every enterprise's container management implementation. One of the most common approaches is to run security scans against all containers that are being used within the environment. The beautiful thing about this approach is that it catches more vulnerabilities and potential problems than just scanning the source code, as scanning the full container will include all transient dependencies. At the same time, the downside of this approach is that it scans all transient dependencies – and there are often a lot of extra packages included in that final container that don’t technically need to be there.
Those extra packages also add to the overall size of the container image. An extra hundred megabytes in any given container might not seem like much – until you run hundreds or thousands of those containers and find yourself needing to accommodate terabytes of data.
For many developers and operators who want to control as many aspects of their application platform as possible, having a purpose-built base container image that only includes exactly what the application needs seems like the ideal solution. That’s the best way to build the absolute smallest possible container image, as you can design it with the absolute minimum set of required libraries. This approach makes the most sense when you’re working with small and statically-compiled applications that can contain all of their required libraries. For legal reasons, however, that’s not possible for most applications.
Scratch containers get a lot more complicated when you use a language like Python, which requires the inclusion of lots of dynamic libraries just to get to the point where you can run the application. This complexity adds time to initial development as well as ongoing maintenance. Every time a CVE that impacts the included libraries is identified, and every time you want to include a feature from a newer version, you’ll need to reevaluate the entire container to ensure that all dependencies required by the update library are also updated. While you could always just grab the latest versions of everything, there wouldn’t be any consistency between builds. That would also create a whole new set of problems and diagnostic headaches when you’re tracking down defects.
Base container images that are built from scratch have their positives and negatives. Importantly, there are other ways to get a smaller base image.
The first (and probably most popular) alternative method to get a container image that is stripped down yet functional is to pull a purpose-built container image from a known organization. If you choose this option, it’s important to pull an image that was built by a relatively well-known organization with enough backing to ensure that the image is regularly updated. Alpine Linux, an offshoot of the LEAF project (whose objective is to create a tiny Linux distribution), is a prime example. It has become one of the most popular lightweight base container images, with millions of downloads from sites like DockerHub.
The best part about these lightweight base container images is that they often have language-specific versions (like alpinelinux/golang). This allows you to skip the steps of adding the language in which the application was written to the base container, which also reduces the effort of going from nothing to having a container.
On the other hand, these distributions often have things that aren’t necessary, like shells and debugging utilities (which makes them bigger than a scratch image). Still, they’re far more useful in development and testing, and also when you’re trying to diagnose the root cause of any given defect.
Another alternative to building a scratch image that will eliminate unused libraries is to use something like DockerSlim. DockerSlim will analyze a container image and remove everything that isn’t loaded when the container is running. The benefits of this approach include the ability to use a community-provided image in development and testing. In addition, you’ll be able to enjoy most of the same benefits you’d have with a scratch-built container in production.
To sum it up, scratch containers can be as secure as any other option, but securing them takes a lot more time and effort. So, you’ll have to decide if you want to spend your time and energy tracking down transitive dependencies on every CVE that is identified against the components you’re including in your base image, or if you’d rather go with another option. If you use slimming technology, you’ll have a lot more time to focus on improving your products and the services you’re providing to your customers. Scratch containers normally can’t compete in this area, so the decision seems pretty easy.
Vince Power is an Enterprise Architect with a focus on digital transformation built with cloud-enabled technologies. He has extensive experience working with Agile development organizations delivering their applications and services using DevOps principles including security controls, identity management, and test automation. You can find @vincepower on Twitter.