Introduction
The main question everyone asks after writing code is how quickly it can be deployed. If the next step involves creating a container image and deploying it to a container service such as Kubernetes, then you know that usually requires writing a Dockerfile. This process of converting source code to an image can be made much faster using Buildpacks.
Dockerfiles:
A Dockerfile is a text file that is used to create images. It is a set of commands written line by line, starting with a Dockerfile keyword, each creating a layer of the image. Here is a sample Dockerfile.
- Consider a simple Python application that prints a message. Let the filename be app.py. Assuming there are no additional dependencies or requirements, the Dockerfile for such an application would look like:
FROM python:3.8
COPY app.py /tmp/
WORKDIR /tmp/
CMD [“python”, “app.py”]
Here, the base image used is Python. The image will have multiple layers based on the Dockerfile keywords, and the base image already comes with several layers too. Upon building the image and running it, the output of app.py will be seen as expected.
Buildpacks
A Buildpack is a script (or a set of scripts) that builds images based on the source code.
Buildpacks detect the language and framework of your application, install dependencies, compile the code, and provide you with an output image. They work in two stages: detect and build. During detection, the source code is analyzed to check if a buildpack is suitable, and if it works, then it proceeds to build. Building is essentially setting the environment, installing dependencies, and, optionally code compilation and start-up script definition. Buildpacks can even build images from just a command and source code if the image is relatively basic. Also, the images generated by Buildpacks are production-ready and can be easily deployed.
One of the most notable highlights of Buildpacks is the Cloud Native Buildpacks (CNB) project. This was launched by the Cloud Native Computing Foundation (CNCF) to create OCI-compliant (Open Container Initiative) images without writing a Dockerfile. This has been adopted by multiple tools and vendors, such as Cloud Foundry, Google Cloud, Kubernetes, and Heroku, to name a few. This has brought on several improvements, such as:
- Reusability is improved by combining multiple modular Buildpacks.
- When code is updated, only a small number of layers need to be changed.
- Faster base image updates via rebasing. Instead of rebuilding the whole image, the base image can be directly updated. This helps update a large number of images at the same time without consuming a lot of bandwidth.
- Software Bill-of-Materials. This is a document containing the dependencies and other components that make up the image. It is essentially metadata with information about the image and what it contains, including the vulnerabilities of the application.
In order to demonstrate, the following terms will be used:
- Pack CLI Pack CLI is a tool that is used to build container images from code using CNB. It can detect the language and platform and perform the required operations to create an image from code. Its features also include updating images, custom builder creations, image inspection, etc.
- Paketo Buildpack is a specific implementation of CNB.
- Procfile: It is a text file that specifies the command to run. Although Procfiles are optional in some cases, they can be used to override the default run commands. Here, a Procfile is mandatory as we are using a Python application.
- Builder: Builders are a specific combination of Buildpacks having a base image, a lifecycle, and a reference to a run image. For example, A Python builder checks for requirements.txt and if it is present, it will run the installation steps ideally. Builders can also have more than one Buildpack and perform as per requirement depending on the type of application.
Buildpack will be used to create a simple Python application (just like the one used for the Dockerfile example). We have used Pack CLI and Paketo Buildpack to build our application into an image.
Procfile:
web: python app.py
Command to build an image using pack CLI:
pack build python-helloworld-app –buildpack paketo-buildpacks/python –builder
paketobuildpacks/builder:base
Now the image is ready to be used. We didn’t have to create a Dockerfile.
Here we have a comparison of the two images that we built. As you can see there are some differences including size, but the application works either way.
Key features and comparison
- Buildpacks provide an automated and simplified build process from source code to container image. Dockerfiles require a few more steps if we consider the size of an average Dockerfile.
- Detection and installation of dependencies and runtimes, improving compatibility and speeding up the process. Buildpacks use a simple script to do it all, while in a Dockerfile, you have to use lines of specific commands to achieve the same.
- Automatic patching of vulnerabilities and dependencies. Buildpacks can do this without requiring a rebuild of the source code, while Dockerfiles require a rebuild.
- Improved size and performance of your application. Buildpacks automatically generate an optimized image, while with Dockerfiles, you have to manually make the changes to the file, implement multistage, change the base image, adjust dependencies, etc. to optimize the image.
- Buildpacks are easier to integrate into a CI/CD pipeline as they provide a higher level of abstraction, while Dockerfiles require more effort. All in all, Buildpacks have the upper hand when it comes to automation, while Dockerfiles have points for precision as they can be customized to the highest extent.
Although Buildpacks sound like the better option so far, there are cases where a Dockerfile might be a better option. This happens in cases such as:
- To obtain more visibility and control over each layer of the image as well as the build. Access to and control over each layer may be necessary for some images.
- When no Buildpacks provide the dependencies needed or there are custom requirements.
- Although Dockerfiles require more knowledge, they are more used and familiar for a lot of users, making them a more popular option.
Conclusion
In conclusion, Buildpacks are suitable for fast and automated deployment of source code to production-ready images, while Dockerfiles are more suitable when you are an experienced user and have time to create a tailor-made image. Buildpacks and Dockerfiles both have pros and cons; the choice between them depends entirely on the use case, user preferences, and the acceptable trade-offs of the users.