Better late than never. Or how we almost made a serious mistake, without the support of ordinary Dockerfiles to build images of the application.
It will be about werf
— GitOps-based utility that integrates with any CI/CD system which manages the entire lifecycle of the app, enabling:
- to collect and publish images,
- to deploy the application in Kubernetes,
- remove unused images using a special politician.
The philosophy of the project is to collect low-level tools into a single unified system, which gives DevOps engineers control applications. Should be part of the existing utilities (like Helm and Docker). If the solution to some problem there — we can create and maintain all necessary for this.
Prehistory: the collector of images
So what happened with the collector of images werf: the usual Dockerfile we need. If a quick plunge into the history of the project, this problem was already evident in the first versions werf (then known as dapp
Creating a tool to build applications in the Docker images, we quickly realized that we Dockerfile is not suitable for some very specific tasks:
- the Need to collect typical small web application in the following standard scheme:
- to install system-wide application dependencies,
- install bundle library dependencies of applications
- to collect assets,
- and the most important is to update the code in the image quickly and efficiently.
- in the project files, the assembler must quickly create a new layer by overlaying the patch for changed files.
- If you have changed certain files, you must rebuild the appropriate radio stage.
Today in our collector there are many other possibilities, but the initial desire and the urge were as follows.
In General, without thinking twice, we have armed used programming language (see below)
and went on a path to implement a private DSL
! Matching the tasks, it was intended to describe the build process in stages and define these stages from dependency files. And supplemented his own collector
, which could turn the DSL to the ultimate goal — the assembled image. At first DSL was in Ruby, and as the transition to Golang
— the config of our collector has been described in a YAML file.
Old config for dapp in Ruby
Current config for werf in YAML
The mechanism of collector is also changing with time. At first we just generated on the fly a temporary Dockerfile from our configuration, and then began to run the Assembly instructions in a temporary container and do a commit.
NB: At the moment, our collector, which works with my config (in YAML) and is called Stapel-collector, has developed into quite a powerful tool. His detailed description deserves a separate articles, and basic details can be found from documentatie.
understanding the problem
But we realized, not immediately, that made one mistake: not added the ability to collect images through a standard Dockerfile
and integrate them into the same infrastructure integrated management of the application (i.e. to collect images, to deplot and cleaned them).How could you make a deployment tool in Kubernetes and not implement Dockerfile support, i.e. a standard way to describe images for most projects? ..
Instead of answering such a question, we propose a solution to it. What if you already have a Dockerfile (or a set of Dockerfiles) and want to use werf?
NB : By the way, why would you even want to use werf? The main features are as follows:
A more complete list of them can be found on the project page .
- full application management cycle including image cleanup;
- the ability to control the assembly of several images at once from a single config;
- improved Helm compatible chart deployment process.
So, if earlier we would suggest to rewrite the Dockerfile to our config, now now we’ll be happy to say: “Let werf build your Dockerfiles!”
How to use?
The full implementation of this feature appeared in the release of werf v1.0.3-beta.1
. The general principle is simple: the user specifies the path to the existing Dockerfile in the werf config, and then runs the
... command and that's it - werf will collect the image. Let's look at an abstract example.
Declare the following
in the root of the project:
FROM ubuntu: 18.04
RUN echo Building ...
And declare the
that uses this
Everything! It remains to run
In addition, you can declare the following
for building several images from different Dockerfiles at once:
Finally, it also supports the transfer of additional build parameters - such as
- through the werf config. A complete description of the Dockerfile image configuration is available on the documentation page
How does it work?
During the build process, the standard local layer cache in Docker functions. However, importantly, werf also integrates the Dockerfile configuration in its infrastructure
. What does this mean?
- Each image assembled from the Dockerfile consists of one stage called
dockerfile (more about what stages in werf are, you can read here ).
- For stage’s
dockerfile werf calculates a signature that depends on the contents of the Dockerfile configuration. When the Dockerfile configuration is changed, the signature of the
dockerfile stage is changed and werf initiates the rebuilding of this stage with the new Dockerfile config. If the signature does not change, then werf takes the image from the cache (for more information on using signatures in werf, see this report ) .
- Next, the collected images can be published using the
werf publish command (or
werf build-and-publish ) and used for deployment in Kubernetes. Published images in the Docker Registry will be cleaned with standard werf cleaners, i.e. there will be an automatic cleanup of old images (over N days), images associated with nonexistent Git branches and other policies.
Read more about this here time, you can check the documentation:
notes and precautions
1. External URL in the ADD is not supported
At the moment not supported using the external URL in the Directive
. Werf will not initiate a rebuild when you change the resource at the specified URL. Will soon be adding this functionality.
2. You can't add .git in the way
Generally speaking, the addition of the directory
bad practice and here's why:
- If the
.git remains in the final image, it violates the principles of 12 factor app: since the final image must be associated with one commit, should not be possible to do
git checkout of arbitrary commit.
.git increases the size of the image (the repository can be large due to the fact that he once added a large files and then deleted). The size of the work-tree associated only with a certain commit, will not depend on the history of Git operations. The addition and subsequent removal of the
.git of the final image won't work: the image will still acquire an extra layer — so runs Docker.
- Docker can trigger unnecessary rebuilds even if there is a build of the same commit, but from different work-tree. For example, Networks create a separate cloned directory to
/home/networks-runner/builds/HASH/[0-N]/yourproject enabled parallel build. Unnecessary rebuilding will be linked to the fact that the directory
.git is different in different cloned versions of the same repository, even if going to the same commit.
The last point is a consequence when using werf. Werf requires that the collected cache was present when you run some commands (for example,
). During operation of such teams werf expects the signature stages for the images specified in the
, and they should be in the Assembly cache or the command will not be able to continue working. If the signature stages will depend on the content of
, we get unstable to changes in the irrelevant files, cache, and werf will not be able to forgive this oversight (see documentation
In General, add only certain necessary files:
in any case, increases the efficiency and reliability of written
, and also improves cache collected by this
to irrelevant changes in Git.
Our original way of writing your collector for specific requirements were heavy, honest and straightforward: instead of using the crutches on top of a standard Dockerfile we wrote my solution with a custom syntax. And it had its pros: Stapel-collector copes with its task.
However, in the process of writing a private collector we overlooked support for already existing Dockerfile's. Now this bug has been fixed, and in the future we plan to develop support for Dockerfile along with our custom collector Stapel for distributed Assembly and for the Assembly using Kubernetes (i.e.builds on runner’s inside Kubernetes, as done by kaniko).
So if you suddenly have a couple of Dockerfiles lying around ... try werf
P.S. Related documentation list
Also read on our blog: “ werf - our tool for CI/CD in Kubernetes (review and video report)