Ask Not What You Can Do For Your Container Build…

…Ask What Your Container Build Can Do For You

Sami Islam
6 min readOct 20, 2020
unDraw by Katerina Limpitsouni

While learning about Docker containers it is common to start with a Dockerfile to specify the steps and dependencies required to build a Docker image. In this article, I am going to build docker images containing applications each using different technology - dotnet, node, and python.

The article will focus on two different ways of building the images:

  1. We describe exactly how to build an image using a Dockerfile
  2. We let the build process figure out how to build the image - no Dockerfile

My project structure is as follows:

Project structure
Project structure

We work for the container build process

When we really care about which base image should be used by our projects we create a Dockerfile manually and use that to build our images.

DotNet Core:

For the dotnet core console app project our dockerfile is a follows:

Dockerfile dotnet console app
Dockerfile.dotnet console app

Here we define which base image to use, which in our case is the dotnet/core/runtime:3.1. Then we copy our published binary and define an entry point to use to run out app.

Before we can create a docker image we build the dotnet console project and publish it in the console folder:

Build the dotnet project and publish it
Build the dotnet project and publish it

Now we build and run the console image using the Dockerfile Dockerfile.dotnet:

Build console image using dockerfile and run container
Build console image using dockerfile and run container

Node:

For our Node project we use the following docker file:

Dockerfile.node Node app

We use the base image node:14.14.0-alpine3.12. We copy our package*.json files in the working directory and install all dependencies. In our case, we have a single dependency on express.js as we see below. We then copy all our files to the image and after exposing port 8080 we start our node app server.js.

Our package.json file is as follows:

Node package.json
Node package.json

The thing to note here is that we specify the node engine we use. This is extremely important for the next section of the article as we shall see later.

We build our Node image and run it using the Dockerfile.node:

Build Node image using dockerfile and run container
Build Node image using dockerfile and run container

Python:

For our python project our Dockerfile looks like the following:

Dockerfile.python for Python App
Dockerfile.python for Python App

A very simple dockerfile using Python 3.8 base image, copying the main.py to the working directory, and starting it.

Building the python image and running it using the Dockerfile.python is as follows:

Build Python image using dockerfile and run container
Build Python image using dockerfile and run container

Everything that we have seen so far is a vanilla docker image creation process using Dockerfile. We specified exactly what dependencies we wanted to have and where to copy our files and how to start it.

In a cloud environment, we probably don’t care about what base image is being used. We just want something that takes our code and figures out what dependencies are required and how to start our application. We can do just that using Buildpacks.

The container build process working for us

According to the Cloud Native Computing Foundation:

“A buildpack is a unit of work that inspects your app source code and formulates a plan to build and run your application.”

It actually uses various ways of detecting how to build your source code and run your application but I am not going to go into details in this article. Here we are going to actually see how to use buildpacks to create an image without using a Dockerfile.

DotNet Core:

Once again we start by building our console app and publishing it. We then use the command line tool pack for Cloud Native Buildpacks to build our image. This is of course console:2.0:

Pack Building the Dotnet app — Part 1
Pack Building the Dotnet app — Part 1

On the first part of the window we see the command line:

pack build s/console:2.0 --builder gcr.io/buildpacks/builder:v1

It uses the pack build command to create a s/console:2.0 image. Instead of using a Dockerfile it uses the google cloud builder gcr.io/buildpacks/builder:v1 which is itself an Ubuntu 18 base docker image providing buildpacks for DotNet, Go, Java, Node.js, and Python. As we’ll see, this is the only builder we need for all the rest of our projects.

One can see how it detects the version of .Net Core to use (it does this by looking at the .csproj file). It then builds the myconsole app and creates the docker image s/console:2.0.

Pack Building the Dotnet app — Part 2
Pack Building the Dotnet app — Part 2

We run the image in the usual way:

Run the console 2.0 container
Run the console 2.0 container

Node:

Similarly, for our Node project we run:

Pack Building the Node App — Part 1
Pack Building the Node App — Part 1

Note that it looks at our package.json to detect the node version to use.

Pack Building the Node App — Part 2
Pack Building the Node App — Part 2

Again the only command we had to run was:

pack build s/node:2.0 --builder gcr.io/buildpacks/builder:v1

Running the image and connecting to localhost:

Run the Node 2.0 container
Run the Node 2.0 container

Python:

For Python, we do need to help the buildpack by providing the exact command to run when running our container. There are several ways to do this but we use the environment variable “way”:

pack build s/python:2.0 --builder gcr.io/buildpacks/builder:v1 --env GOOGLE_ENTRYPOINT="python main.py"

And the output:

Pack Building the Python App — Part 1
Pack Building the Python App — Part 1
Pack Building the Python App — Part 2

And running it:

Run the Python 2.0 container
Run the Python 2.0 container

That’s it!! We just created docker images for applications using three different technology without self-created Dockerfiles. How cool is that?! 😃

Conclusion

I have touched the surface of what Cloud Native Builders and Buildpacks are capable of and can’t wait to explore them further. I hope this introduction will make you curious enough to explore them as well.

There are various other builders one can use. The following command lists some of them:

A list of builders
A list of builders

We have used Google builders in our article.

You will find all the code for this article in my GitHub Repo.

--

--