Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
18 views10 pages

Benni 2018

The document discusses supporting microservices deployment in a safer way through static analysis and automated rewriting of deployment descriptors. It notes that while container technologies like Docker promote reuse, hidden dependencies can cause conflicts. The authors propose a formal model to analyze deployment artifacts based on platforms like Docker. Through static analysis of the deployment descriptor hierarchy, their approach can verify and automatically fix common errors to help ensure safer microservices deployment.

Uploaded by

zsoft
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views10 pages

Benni 2018

The document discusses supporting microservices deployment in a safer way through static analysis and automated rewriting of deployment descriptors. It notes that while container technologies like Docker promote reuse, hidden dependencies can cause conflicts. The authors propose a formal model to analyze deployment artifacts based on platforms like Docker. Through static analysis of the deployment descriptor hierarchy, their approach can verify and automatically fix common errors to help ensure safer microservices deployment.

Uploaded by

zsoft
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Supporting Micro-services Deployment in a Safer Way: a Static

Analysis and Automated Rewriting Approach


Benjamin Benni Sébastien Mosser
Université Côte d’Azur, CNRS, I3S Université Côte d’Azur, CNRS, I3S
Nice, France Nice, France
[email protected] [email protected]

Philippe Collet Michel Riveill


Université Côte d’Azur, CNRS, I3S Université Côte d’Azur, CNRS, I3S
Nice, France Nice, France
[email protected] [email protected]
ABSTRACT 1 INTRODUCTION
The SOA ecosystem has drastically evolved since its childhood in The Service-Oriented Programming (SOP) paradigm has recently
the early 2000s. From monolithic services, micro–services now co- evolved to the definition of microservices that cooperate together
operate together in ultra-large scale systems. In this context, there in a scalable way. Monolithic deployments used until then do not
is a tremendous need to deploy frequently new services, or new comply with the needs associated with such ecosystem [19]. As
version of existing services. Container–based technologies (e.g., part of the microservice paradigm comes the idea of quickly prop-
Docker) emerged recently to tool such deployments, promoting a agating a change from development to production [2], according
black-box reuse mechanism to support off-the-shelf deployments. to a DevOps approach. Development and Operations are no longer
Unfortunately, from the service deployment point of view, such separated in the service lifecycle, and a change in a given service
form of black-box reuse prevent to ensure what is really shipped can be automatically propagated to production servers through an
inside the container with the service to deploy. In this paper, we automated delivery pipeline. In this context, it is up to the service
propose a formalism to model and statically analyze service de- developer to carefully describe how a microservice will be delivered,
ployment artifacts based on state of the art deployment platforms. using dedicated technologies.
The static analysis mechanism leverages the hierarchy of deploy- Among these technologies, the adoption of the container ap-
ment descriptors to verify a given deployment, as well as rewrite proach is tremendously increasing [8]. Containers ensure that a
it to automatically fix common errors. The approach is validated given microservice will run the same regardless of its environment,
through the automation of the guidelines provided by the user com- easing the repeatability of build, test, deployment and runtime ex-
munity associated to the reference Docker engine, and the analysis ecutions [4, 16]. Containers are faster at runtime and boot-time,
of 20,000 real deployment descriptors (hosted on GitHub). lighter than virtual machines, and scale better [9, 17, 20, 25]. In the
container field, the Docker engine quickly became the reference
CCS CONCEPTS platform for builds and deployments of micro-services [23]. It is
• Software and its engineering → Abstraction, modeling and important to note that the work described in this paper is not tied to
modularity; Feature interaction; Reusability; Docker, as the defined formal model is technology agnostic. How-
ever, for the sake of concision, we decided to mainly focus on the
KEYWORDS industrial standard as illustration for the challenges and validation
case study.
Microservice, static analysis, container, Docker
Building a non-trivial container image is a difficult process. De-
ACM Reference Format: scribing an image is an imperative process, where a service deploy-
Benjamin Benni, Sébastien Mosser, Philippe Collet, and Michel Riveill. 2018. ment descriptor is written (e.g., a dockerfile in the Docker ecosystem)
Supporting Micro-services Deployment in a Safer Way: a Static Analysis
to describe as shell commands how the microservice is installed
and Automated Rewriting Approach. In Proceedings of ACM SAC Conference,
Pau,France, April 9-13, 2018 (SAC’18), 10 pages.
and configured inside the container. Following an off-the-shelf ap-
https://doi.org/10.1145/3167132.3167314 proach, a container is defined on top of others (reused as black
boxes). However, this implementation is not compliant with the
Permission to make digital or hard copies of all or part of this work for personal or open/closed principle, as it is open for extensions (a descriptor
classroom use is granted without fee provided that copies are not made or distributed
for profit or commercial advantage and that copies bear this notice and the full citation
extends another one), but not closed for modifications (a descrip-
on the first page. Copyrights for components of this work owned by others than the tor does not provide a clear interface about its contents, making
author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or reuse hazardous). By hiding the contents of an image as a black-
republish, to post on servers or to redistribute to lists, requires prior specific permission
and/or a fee. Request permissions from [email protected]. box, deployment instruction can conflict with the hidden one, e.g.,
SAC’18, April 9-13, 2018, Pau,France overriding executables, duplicating installation of the same piece of
© 2018 Copyright held by the owner/author(s). Publication rights licensed to Associa- software in alternative versions, or shadowing executables. It leads
tion for Computing Machinery.
ACM ISBN 978-1-4503-5191-1/18/04. . . $15.00 to erroneous deployments, detected at runtime. Moreover, the tech-
https://doi.org/10.1145/3167132.3167314 nologies supporting microservice deployment evolve constantly,

1706
to make it more efficient or scalable. This evolution can drasti- connect to a MongoDB database. Then, she moves to a directory
cally change the way the deployment engine is implemented, and named catalogue-service, add a configuration file located on
abstraction leaks can occur (i.e., an internal technological choice her filesystem and the javascript source code into this directory,
inside the deployment engine the final user must take into account and finally starts the nodejs environement to host her service. It
when writing a service descriptor). It is up to the service developer is a good practice to publish the descriptor associated to a given
to stay up to date with ever-changing guidelines that implements image on the repository, and tools exist to decompile an image and
fixes to abstraction leaks. retrieve the essential instructions used to build it1 .
This low level of abstraction make the process of describing An important line to notice is the first one, where Alice states
containers tedious and unsafe for service developers. In this pa- that her image is built on top of the alpine:latest image. This
per, we propose a static analysis approach that support the allows her to not worry about the details of how to setup an op-
safe development of service deployment descriptor by ser- erating system inside her container, by simply reusing the Alpine
vice designers. The originality of the approach is (i) to define a (a lightweight linux distribution, weighting 8Mb and known to
sound formal model that is independent of a dedicated container start quickly) image available on the repository. This black-box
technology and (ii) to support the definition of an evolving set of reuse mechanism is the key of any deployment technology, allow-
checking or rewriting rules while detecting conflict that can occur ing one to reuse off-the-shelf elements. In Docker, the hierarchy
when applying such rules. The approach is validated on a real-life is recursive, until reaching the scratch root image. We describe in
dataset of 24, 357 deployment descriptors hosted on the GitHub List. 2 an example of the hierarchy associated to the Jenkins con-
open source platform. tinuous integration service official image, involving a descriptor
and 4 successive ancestors. The complete hierarchy is available on
2 BACKGROUND & CHALLENGES the companion webpage2 of this paper.
In this section, we discuss how containers are supporting microser-
vice deployment, through the prism of the Docker platform, the de 1 # Descriptor : debian : jessie
FROM scratch
facto industrial standard available since 2013. Our contribution can 2
3 ...
be applied to any container-based system that relies on commands 4 CMD [" bash "]
(e.g., LXC [21], rkt [6], Docker [23], Vagrant [13]). 5
6 # Descriptor : buildpack - deps : jessie - curl
We assume a service developer named Alice, implementing a 7 FROM debian : jessie
given microservice, e.g., a product catalog. Using the container 8 RUN apt - get install curl wget ...
9
approach, Alice will wrap her service inside a turnkey image. This
10 # Descriptor : buildpack - deps : jessie - scm
image is built according to a service deployment descriptor (e.g., a 11 FROM debian : jessie
dockerfile in the Docker ecosystem), i.e., a script that installs inside 12 RUN apt - get install bzr git ...
13
the image all the necessary software stack for the product catalog 14 # Descriptor : openjdk :8 - jdk
to run (e.g., software dependencies, configuration file, tools). Then, 15 FROM buildpack - deps : jessie - scm
the descriptor is compiled into an image, which can be pushed to 16 RUN apt - get install bzip2 ...
17
an image repository, making it available to others. In the Docker 18 # Descriptor : jenkins : latest
ecosystem, the public repository is named the DockerHub, and 19 FROM openjdk :8 - jdk
20 RUN apt - get install git curl ...
contained in September 2017 more than 500, 000 public images. At
21 ...
the operational level, the images are automatically downloaded 22 CMD ["/ usr / local / bin / jenkins . sh "]
from a repository (public or private) and started inside a container. Listing 2: Dockerfiles hierarchy example
A container can be seen as a light virtual machine, relying on
operating system mechanisms at the kernel level to ensure isolation
with other containers running on the very same machine.
1 FROM jenkins : latest
1 FROM alpine : latest 2 ...
2 MAINTAINER Alice < alice@awesome - services . cc > 3 RUN apt - get install npm bzip2 =1.0.1
3 RUN apt - get update 4 ...
4 RUN apt - get install nodejs npm 5 CMD [" nodejs "]
5 RUN npm install express mongoose
6 WORKDIR ./ catalogue - service Listing 3: Service deployment descriptor (bad) reuse
7 ADD config . json *. js .
8 CMD [" nodejs "]

Listing 1: Product catalogue descriptor The mechanisms under the container approach triggers the fol-
lowing two challenges with respect to microservices deployment.

To create an image, Alice creates a descriptor (List. 1), where Following evolving guidelines (C 1 ). The container approach was
she assembles setup instructions for her product catalog. She starts recently adopted by the industry (e.g., Docker started in 2013, even
by installing all the software stack needed by her service, e.g., the if the container underlying technology exists in the linux kernel
NodeJS software stack and the associated dependency manager since 2008). This effervescent context makes features available in
NPM (l.3-4). She also installs the javascript packages needed by her 1 https://hub.docker.com/r/centurylinklabs/dockerfile-from-image/.

service, i.e., express for the service exposition and mongoose to 2 https://github.com/ttben/dockerconflict/blob/master/README.md.

1707
tools added at the same rate obsolete ones are removed, and suffer
from abstractions leaks. For example, the descriptor described in
;: D ×D → D
List. 1 violates a Docker guideline: the update command is not
executed in the same RUN as the installation one (see Sec. 4.3 for (d 1 , d 2 ) 7→ d 12 : Let d 1 = [i 1 , . . . , i n ],
details). This guideline, among others, (i) is the visible side of an d 2 = [i 1′ , . . . , im

],
internal flaw of the engine, (ii) must be followed until now, but (iii)
d 12 = d 1 ; d 2 = [i 1 , i n , i 1′ , im

]
might be removed in future version.
∧ parent (d 2 ) = d 1 (2)
∧ parent (d 12 ) = parent (d 1 )
Safer black-box reuse (C 2 ). The strength of image reusing is also

a strong weakness of the container approach. Following the open/- :D→D
closed principle, an image is “open for extension”", and “closed for
parent (d ) = ∅
 ⇒d
modification”. But as the interface of the image is not defined and d 7→ d = 
parent (d ) , ∅
only considered as a black box with no clear interface contract,  ⇒ parent (d ); d
when reusing an image, Alice has no idea of the contents of the
reused image. It is then possible for Alice to override elements ex- 3.2 Checking rules: Φ
isting in the source image, without knowing it. These errors cannot The intention of a checking rule (or checker) is to statically identify
be detected at build time for an image (e.g., installing a piece of an error that exists in a given deployment descriptor. A checker is
software in a conflicting version, like the bzip2 package install in formally defined as a function φ taking as input a descriptor and
List. 3 that conflicts with the one installed in List. 2, l.16), and returning a boolean stating whether the defect is detected. Thus,
triggers errors when running the deployed services. for a given platform or a given company, one can model the set of
guidelines relevant for her context as a set of rules to be checked
(rules = {φ 1 , . . . , φ n }). This is classic when defining a linter (a static
3 MODELING DESCRIPTORS analyzer), where users can define their own set of rules. Considering
This section describes the formalism defined to model container the composition operator defined previously, the strength of the
descriptors and support (i) the static analysis of a given descriptor proposed approach is to support the application of a checking rule
and (ii) the rewriting of a descriptor to fix common errors when to the normalized version of the deployment descriptor, allowing
possible. Considering the diversity of existing containers platform, one to identify an error that comes from an interaction between
this model must be technology agnostic. We will show in Sec. 4 how the current deployment instructions and the one inherited from the
to instantiate the instruction concept to fit the Docker platform. parental hierarchy (Eq.3). In addition to being technology-specific,
state of the art deployment linters do not provide a way to leverage
this composition, and only provides a static analysis of the current
3.1 Formal Model descriptor.
A service deployment descriptor is implemented as a sequence
φi : D → B ∈ Φ
of shell commands, so in our formalism a descriptor d ∈ D as
a totally ordered set of setup instructions (e.g., running a shell violation? : D × Φn → B
command, copying a configuration file, denoted as i x ∈ I ). To n
_ (3)
model the relationship that may exist between two descriptors (e.g., (d, {φ 1 , . . . , φ n }) 7→ φ i (d )
a dockerfile extends another one, a shell script loads another one i=1
with the source command), we define a function named parent
that returns for a given descriptor its parent, or the root descriptor 3.3 Automated Rewriting rules: R
(denoted as ∅) if no parent exists for this descriptor (Eq.1). This Checkers support the identification of errors that can be automati-
enables to define a normalized version of a deployment descriptor cally detected. It is then up to the writer of the service deployment
d, noted d, which contains all instructions in order from d to the descriptor to fix it. However, for some errors, it is possible to rewrite
top of the parent hierarchy (Eq.2). the descriptor to fix it in an automated way. For example, to reduce
This model is simple, but expressive enough to support the defi- deployment artifact size, reducing the number of instructions in a
nition of checkers and rewriting rules. For a given service deploy- descriptor helps (this is inherent to the container technology, where
ment platform, it needs to be instantiated at the instruction level, each instruction adds an overhead to the final size). One can write
i.e., which kind of setup instructions are available for this very a rule detecting instructions that can be merged together. It is then
platform. possible to automatically compute how the descriptor should be
rewritten. Unfortunately, rules can overlap and conflict in their
decisions. For example, to augment modularity and reuse poten-
tial of a service deployment descriptor, keeping the instructions as
n
d ∈ D = [i 1 , . . . , i n ] ∈ I < separated as possible is a good practice. This clearly overlaps with
parent : D → D (1)
the previous intention of reducing artifacts size. In the context of
ever-changing guidelines associated to containers, it is important
d 7→ d ′ : d loads d ′ to automatically detect such conflicts.

1708
To address this issue, we consider here a rewriting rule ρ ∈ here on three rules from a qualitative point of view, to show how
R as a function taking as input a descriptor d, and producing as the formal framework can be applied. A quantitative evaluation is
output a patch to apply to the descriptor (i.e., a delta) to make it performed in Sec. 5.
compliant with the guideline, and denoted as δ ∈ ∆. The obtained To refine the formal model and adapt it to the Docker platform
δ models how the descriptor must be modified (by changing the (defining Id ⊂ I ), we need to define what instructions are available
instruction sequence) to achieve the rewriting [18]. By reasoning for the rules to work with. The complete model is available on the
on the set of deltas {δ 1 , . . . , δn } obtained when multiple rules must companion webpage, as well as the associated parser that compiles
be applied, one can automatically identify conflicts. Considering a dockerfile into an instance of the model. We focus here only of
the previous example of instruction squashing versus modularity, the kind of commands necessary to illustrate the checking and
one can identify a conflict as the two rules would produce δ s that rewriting rules described in this section. We consider an instruction
would concurrently modify the same instructions in different ways. as a kind (e.g., RUN, CMD), and a totally ordered set of arguments
Inspired by Stickel’s work [24], a δ is defined as a set of substitution passed to the command. One can access to the kind of an instruction
pairs (i → i ′ ) ∈ Σ. Applying such a substitution to a descriptor using a function named kind, and to the arguments using a function
d means to produce a new descriptor d ′ where i ′ replace i in the named arдs. A special kind MULTI-RUN is used to model the RUN
ordered set of instructions. To remove an instruction means to instructions containing multiple shell commands (separated by
substitute it by void (i → ∅), and to introduce a new one at the ampersands).
beginning of a sequence means to substitute void by this instruction
(∅ → i ′ ). To identify conflicts between modifications, we look for
concurrent substitutions that might alter the very same instruction.
4.1 Checker: Overriding Services
Let us consider here the service deployment descriptor described
do : D × Σ → D
in List. 3. From the writer point of view, to build a continuous
(d, σ ) 7→ d ′ : Let d = [. . . , i n , i, im , . . . ], delivery pipeline, we reuse the latest Jenkins official image and
σ = (i → i ′ ) simply add an homemade bridge service (implemented as a nodejs
artifact) to interconnect the Jenkins instance with some internal
d ′ = [. . . , i n , i ′, im , . . . ] tools. Unfortunately, starting our bridge service (List. 3, line 6) will
do + : D × ∆ → D override the CMD command that exists inside the Jenkins container
(4) we are reusing (List. 2, line 22). As a consequence, only our bridge
δ = ∅
 ⇒d
(d, δ ) 7→  service is started, and considering the blackbox reuse mechanism
δ = {σ } ∪ δ ′
 ⇒ do + (do(d, σ ), δ ′ ) advocated by Docker, there is no way for the descriptor writer to
conflict? : ∆ → B know why the Jenkins instance does not start. It is up to the user to
δ 7→ Let (i, a, b) ∈ I 3 , i , ∅, break the black-box approach by hand and crawl the official Docker
store to analyze the root cause of the error by going through the
∃(i → a) ∈ δ, (i → b) ∈ δ, a , b , ∅ hidden hierarchy.
Considering this representation of δ s that support conflict detec- We are defining here a checker instead of a rewriter, and it is up to
tion and the functional representation of rewriters described in the the user to decide which service to start3 . Given a descriptor d, the
previous paragraph, optimizing a given descriptor d by applying checker φos looks inside the descriptor d for a pair of deployment
several rewriting rules ρ i means to compute all the δ s associated instruction (i, i ′ ) starting two different services.
to the given rules, verifying the absence of con f lict and then ap-
plying it. Like the checking mechanism, the rewriting mechanism φos : D → B ∈ Φ
rw benefits from the composition operator and it considers the
complete hierarchy, being applied to the normalized version d ′ of d 7→ ∃(i, i ′ ) ∈ d 2 , i < i ′ (6)

the descriptor d. ∧ kind (i) = kind (i ) = CMD
ρi : D → ∆ ∈ R
rw : D × R n → D|Error 4.2 Rewriter: Reducing Number of Layers
[ We discussed earlier the issue of optimizing an image size by re-
(d, rules) 7→ d ′ : Let δ = ρ (d ),
(5) ducing the number of instructions used to build it (Sec. 3). This is a
ρ ∈r ul es
use case for the rewriting mechanism, through the implementation
 conflict? (δ )
 ⇒ Error of a set of rules that identify useless instructions and rewrite the
d′ = 
 ¬conflict? (δ ) ⇒ do + (d, δ ) service deployment descriptor adequately. The rewriter looks for
equivalent instructions (considering a given equivalence relation ≡)

4 APPLICATION: THE DOCKER CASE
In this section, we refine the formal model presented in the previ-
ous section to fit the Docker container platform, i.e., refining the
available instructions and implementing checkers and rewriters 3 Under some assumptions, one can imagine generating a script that starts the conflict-
associated to this service deployment environment. Among the ing services simultaneously and rewrite the conflicting commands. But this is not true
guidelines defined by the Docker best practices reference, we focus in the general case, as for example some services might use the same network port.

1709
inside a given descriptor, keep one and delete the others. (Rewritten), while (iii) removing the previously existing instruc-
tions (Removed).
ρ≡ : D → ∆ ∈ R install? : Id → B
i 7→ kind (i) = RUN ∧ npm ∈ arдs (i)
d 7→ δ : Let Is = {i : i ∈ d, ∃i ′ ∈ d, i ≡ i ′ },
(7) ∧ install ∈ arдs (i)
 Is = ∅
 ⇒∅
δ= ρpack : D → ∆ ∈ R
 Is = {i} ∪ Is ′ ⇒ {(i ′ → ∅) : i ′ ∈ Is ′ }
 d 7→ δ : Let Is = {i : i ∈ d, install?(i)},
 Is = ∅
 ⇒∅
The difficulty here is to define the equivalence class that exists δ=  Is = {i} ∪ Is ′ ⇒ Rewritten
(9)
among setup instructions. As shell commands have side effects by 
design, even two consecutive invocations of the very same instruc- Removed = {(i ′ → ∅) : i ′ ∈ Is ′ }
tion might not be equivalent. For example, (i) creating a directory [
packaдes = arдs (i)\{npm, install}
named dir, (ii) moving to another location and (iii) creating an-
i ∈Is
other directory named dir. The two instructions used to create the
m = run({npm, install} ∪ packaдes)
directory cannot be merged into one. However, the download of
the same file from the web (e.g., an application server using the Rewritten = {(i → m)} ∪ Removed
wget tool) can be detected and unified (Eq.8).
4.3 Checker & Rewriter: Layer Caching
From the point of view of the service deployment descriptor, execut-
≡dl : Id × Id → B ing two instructions containing one command should be equivalent
(i, i ′ ) 7→ kind (i) = kind (i ′ ) = RUN to executing a single instruction containing the previous command.
∧ wget ∈ arдs (i) ∧ wget ∈ arдs (i ′ ) (8)
However, due to an internal optimization made by the Docker en-
gine called layer caching 4 , this is not the case. The prototypical
∧ ∃url ∈ URL, example of such an error is the usage of the apt-get package mech-
url ∈ arдs (i) ∧ url ∈ arдs (i ′ ) anisms to install the software stack supporting the service to deploy.
It is classical to first update the package source before installing
the pieces of software needed to deploy a given service (see List. 5).
Another way of reducing an image size is to merge instructions
that can be merged together, even if their arguments differ. This is 1 # # Multiple RUNs with single command
classical with package management tools, such as NPM in javascript 2 RUN apt - get update
or APT at the linux operating system level. In List. 4, the first de- 3 RUN apt - get install nginx nodejs
4
scriptor uses three instructions to install three javascript packages 5 # # Single RUN with multiple commands
used by the service, where the second descriptor using a single 6 RUN apt - get update \
7 && apt - get install nginx nodejs
instruction to install the packages. This is particularly important
as the writer can be unaware of the fact that some packages were Listing 5: Multiple & Single RUN instructions
installed in a parent deployment descriptor, leading to multiple
downloads in addition to overweighted service container image.
If a dockerfile containing an apt-get update command has
already been executed on the computer, the engine will use the
associated layer instead of running the update again, leading the
apt-get install instruction to work with outdated packages. We
1 # # Multiple RUNs to install several NPM packages
2 RUN npm install soap
show in this section how the situation can be detected using a
3 RUN npm install json - schema - mapper checker, and how it can be rewritten automatically. This illustrates
4 RUN npm install xml2js the expressiveness of both checking and rewriting rules, showing
5
6 # # Merged instruction
how the two principles apply to the same use case. We first define
7 RUN npm install json - schema - mapper soap xml2js two functions apti and aptu to identify the instructions involved
Listing 4: Merging instructions arguments in this section (Eq.10).
apti ? : Id → B
i 7→ kind (i) = RUN ∧ apt-get ∈ arдs (i)
∧ install ∈ arдs (i)
(10)
To achieve such rewriting, we define the ρpack rule that catches aptu ? : Id → B
all the instructions i ∈ Is using the expected package manager (here i 7→ kind (i) = RUN ∧ apt-get ∈ arдs (i)
npm, but apt, opam, yum and other package managers follow the
∧ update ∈ arдs (i)
same principle), and (i) creates a merged instruction m that installs 4 https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
all packages, (ii) substitutes the first instruction for the merged one #run.

1710
Checker implementation. The checker implementation is straight-
forward. In a given descriptor d, we look for two instructions (u, i)
∆pack = {(i 2 → i 23 ), (i 3 → ∅)}
that update the package manager and install a package, these two
instructions being different in the descriptor. ∆дap = {(i 1 → i 123 ), (i 2 → ∅), (i 3 → ∅)} (13)
∆ = {(i 1 → i 123 ), (i 2 → ∅), (i 2 → i 23 ), (i 3 → ∅)}
φдap : D → B ∈ Φ
(11)
d 7→ ∃(u, i) ∈ d 2 , u , i ∧ apti ?(i) ∧ aptu ?(u) 5 QUANTITATIVE VALIDATION
Rewriter implementation. As merging update and install instruc- In this section we validate that (i) our proposition can handle a real-
tions follows a principle similar to the one described in the previous world use-case in an acceptable amount of time, (ii) analyzing the
section, we assume a merge function denoted as µ, taking as input composition of dockerfiles generates a reasonnable overhead (iii)
the set of instructions Id∗ to merge, and producing as output the there is a substantial gain obtained in detecting guideline violations
expected multi-run instruction Id . We capture all the update and when taking the whole hierarchy into account instead of isolated
install instructions (respectively U s and Is), and substitute an up- deployment descriptors.
date one for a multiple run that contains the packages installation To do so, we built a dataset of dockerfiles available on GitHub,
right after the update (Rewritten and Remove). the community code-versioning reference platform. We collected
an initial set of 24, 357 deployment descriptors. Details about this
µ : Id∗ → Id collection, the composition, and the building of the dataset are
ρдap : D → ∆ ∈ R available in the companion webpage. Over these dockerfiles, 5.8%
d 7→ δ : Let Is = {i : i ∈ d, apti ?(i)}, (1, 412) were considered as trivial (i.e., having less than 3 instruc-
tions5 ) and were removed from the dataset. The remaining 22, 945
Us = {u : u ∈ d, aptu ?(u)},
dockerfiles regroup 178, 088 instructions and represent our experi-
(12)
 Us = ∅
 ⇒∅ mental dataset, denoted DS. The normalized version of our dataset,
δ=  Us = {u} ∪ Us ′ ⇒ Rewritten denoted DS, is made of also 22, 945 dockerfiles but due to extension
 mechanism, it regroups 285, 142 instructions.
Removed = {(i → ∅) : i ∈ Is ∪ Us ′ } Isolated dockerfiles of DS contain between 3 and 202 instruc-
Rewritten = {(u → µ (Is ∪ Us))} ∪ Removed tions with 7.76 instructions per dockerfile on average; normalized
dockerfiles of DS have between 3 and 202 instructions, with 14.37
4.4 Conflict detection on average. The smallest sizes are the same since it is our lower-
We consider here (i) the rewriters defined in the previous sections to threshold for trivial dockerfile. The highest size is a single dockerfile
support layer caching and image weight reduction (the latter adapted that is bigger than every normalized dockerfiles. The most inter-
to work with apt instead of npm) and (ii) the deployment descriptor esting metric here is that dockerfiles double in size on average.
given in List. 6, and denoted as dc . Normalized dockerfiles of DS have between 0 and 6 parent docker-
files with 1.45 level of parents on average. These numbers show
• ∆pack = ρpack (dc ). The rewriter catches the two installa-
the scale our approach must reach to address real life deployment
tion instructions i 2 and i 3 , creates a merged one denoted
descriptors.
as i 23 that installs the two packages, substitutes i 2 for the
From the guidelines in the official Docker webpages 6 , we iden-
merged instruction and removes i 3 .
tified and implemented 15 of them as they are both highly used
• ∆дap = ρдap (dc ). The rewriter catches the three instruc-
by the community and general enough to be relevant for a wide
tions, and merges them in a multi-run instruction denoted
number of dockerfiles.
as i 123 . The update instruction is substituted for i 123 , and the
others are removed.
• ∆ = ∆pack ∪ ∆дap . The conflict detection function is applied 5.1 Analyzing Atomic Descriptors
on the union set containing all the expected substitutions. The remaining 22, 945 dockerfiles in DS regroup 178, 088 instruc-
When triggered, it identifies a conflict on i 2 , as the first rule tions. In this dataset, no parent-child relationship exists and each
wants to remove it when the second one would substitute dockerfile is analyzed as an isolated descriptor.
with something else. Isolated dockerfiles contain between 3 and 202 instructions with
7.76 instructions per dockerfile on average. We first applied each
∃(i 2 , ∅, i 23 ) ∈ I 3 , (i 2 → ∅) ∈ ∆ ∧ (i 2 → i 23 ) ∈ ∆ guideline on our experimental dataset to show the impact of our
contribution on the build-execution time. We underline the fact that,
in this case, each dockerfile is considered as isolated from others,
1 ... and no parent-child relationship is taken into account. The output
2 RUN apt - get update # i1
3 ... of this experiment can be considered as the raw execution time of
4 RUN apt - get install nginx # i2 our analyzer on thousands of unrelated files.
5 ...
6 RUN apt - get install nodejs # i3
7 ... 5A parent reference and a single command.
Listing 6: Descriptor triggering a conflict: dc 6 https://docs.docker.com/engine/reference/builder/, https://docs.docker.com/engine/
userguide/eng-image/dockerfile_best-practices/.

1711
Normalized Task 100
Mode

apply guidelines
normalization
90
Isolated parse dataset 80

Cumulative Frequency
70
0 2000 4000 6000
Time 60
50
Figure 1: Exec. time of applying every guideline on DS in 40
isolated mode. 30
20
10
Measures were made with the JMH7
framework in its 1.5.2 ver-
sion and targeting Java 8 code. Each measure is made 10 times, after 40 1000 2000 3000 4000 5000

a warm-up phase of the JVM used to reduce measure errors and Nb. of Dockerfiles
garbage collection interferences. We ran this benchmark on a vir-
tual machine running CentosOS 7 operating system, with one Intel Figure 2: Perc. of dataset with parent/child relationship es-
Xeon E5-2637v2 3,5GHz of 4 cores, and 4 GB/1600MHz of RAM. tablished when the parent dockerfile is known
The benchmark code, as well as more details on the experimental
protocol can be found in the companion webpage.
Fig. 1 displays the execution times running our 15 official guide- established8 . Therefore we applied our guidelines on the normalized
lines over the 22, 945 dockerfiles, both in isolation and with the version of all the dockerfiles, some having parents, some not.
normalized set we discuss in the next section. In isolated mode The upper part of Fig. 1 displays the execution times running
the analyzer takes around 4 seconds to get through our dataset the 15 official guidelines, this time over DS. Our analyzer took
and yield conflicts. This execution time must be compared to the around 6.5 seconds to analyze our experimental dataset and yield
amount of time needed to list, load and parse those dockerfiles (2, 5 conflicts, which contains 2, 5 seconds of file loading. We consider
seconds on average). We consider that this time is perfectly compat- that this time is also perfectly compatible with a real-life pipeline
ible with a real-life build-chain of nowadays software companies of nowadays companies. We also note that, since dockerfiles have
(e.g., continuous deployment pipeline). between 0 and 6 parent dockerfiles with 1.45 level of parents on
average, and that they have between 3 and 202 instructions with
5.2 Analyzing Normalized Descriptors 7.76 instructions per dockerfile on average, our contribution fits
To show the value of our contribution and our normalized operator, well the relative constrained complexity of our targeted problem.
we need to automatically build hierarchies of dockerfiles, but there Guideline violation. Fig. 3 shows how many dockerfiles are de-
is no pre-established or accessible parent-child relationship with tected as violation of a given guideline. We note that guidelines
these files. This could have been automatically possible if (i) a G 3 , G 4 , G 5 , G 9 , G 10 and G 11 are violated the same amount of times,
dockerfile was a named artefact, and (ii) a named dockerfile could which is very low. This is due to the fact that (i) those errors are
be accessible via an API, which is actually not the case. rarely made and (ii) are more likely to be made by beginners (i.e.,
As our dataset was built without targeting any specific user, at the bottom of the hierarchy). We also note that the 9 remaining
dockerfile, embedded framework or official dockerfile, we analyzed guidelines are more violated when applying the normalized opera-
it and sorted dockerfiles by the most used parents. We then handle tor. This difference corresponds to guidelines violation that cannot
aliases since the very same parent-dockerfile can be extended by be detected without taking the normalized descriptor into account,
child-dockerfiles using different names. Fig. 2 depicts the evolution as our approach does.
of the percentage of the dataset which would have a parent/child Fig. 4 shows how many commands are detected as violating a
relation established if the parent was known. The resulting curve given guideline in isolated and normalized modes (using a logarith-
is not surprising since a lot of dockerfiles extends the same set of mic scale). We note that patterns from Fig. 3 are found in this figure.
dockerfiles (i.e., the official ones). This gives insights about how many times a dockerfile violates a
Since dockerfiles have to be manually retrieved, we decided to given guideline and therefore that a high-level dockerfile has a given
cover 50% of our dataset with an established parent/child relation- flaw. For instance, guideline #2 is violated by a very small amount
ship. To do so, we manually retrieved the 40 most extended dock- of instructions, which impacts a lot of dockerfiles. This means that
erfiles, from common repositories (e.g., DockerHub, DockerStore). fixing a small amount of instructions may fix a lot of dockerfiles.
As a result, we have 11, 527 dockerfiles with a known parent. We
believe that this coverage is relevant enough to serve our purpose 5.3 Conflict detection
and that manually grabbing more dockerfiles will not change our Some guidelines are going to conflict with each other, by construc-
conclusions. tion (e.g., updating before installing, and adding specific arguments
Execution time. We ran the same guidelines as in Sec. 5.1 on the to at − дet command). Half of the extracted guidelines target RU N
whole dataset, this time using the normalized operator. We can commands, hence are more likely to be conflicting. An interference
apply this operator even if no parent/child relationship has been matrix of conflicting guidelines can then be built.
7 http://openjdk.java.net/projects/code-tools/jmh/. 8 as described in Eq.2: par ent (d ) = ∅ ⇒ d .

1712
G i, j 1 5 6 7 13 14 15
1 – 124 2, 212 2, 901 1, 731 2, 810 1, 816
15000

5 – – 524 685 176 436 629


6 – – – 8, 492 2, 110 6, 331 6, 531
7 – – – – 2, 601 8, 690 10, 223
Nb. of dockerfiles
10000

13 – – – – – 2, 351 1, 861
14 – – – – – – 5, 965
15 – – – – – – –
Table 1: Dockerfiles containing guidelines violation pairs
5000

G i, j 1 5 6 7 13 14 15
0

1 – 15 1, 795 5, 445 4, 100 3, 509 671


1 2 3 4 5 6 7 8 9 10 12 14
5 – – 9 360 12 174 314
Guideline ID
6 – – – 20, 094 1, 211 9, 155 14, 655
7 – – – – 5, 239 19, 474 50, 256
Figure 3: Number of dockerfiles violating a given guideline 13 – – – – – 1, 815 1, 211
14 – – – – – – 8, 680
15 – – – – – – –
Table 2: Instructions containing guidelines violation pairs
(i.e., real conflicts)
1e+06
Nb. of commands
1e+04

a lot of instructions, exposing an understanding problem of the


platform by the service designers.

5.4 Rewriting
1e+02

Finally, we applied two rewriting rules on our normalized dataset to


show the benefit of an automated analysis and automated rewriting
of service descriptors. We remember that DS is made of 22, 945
1e+00

dockerfiles, grouping 285, 142 instructions. Among DS, 10, 711 dock-
1 2 3 4 5 6 7 8 9 10 12 14
erfiles (46.68%), grouping 21, 686 RU N instructions (7.60%), install
Guideline ID
packages via the apt manager. These instructions represent 26.84%
of the 80, 799 RU N instructions of our dataset, confirming that
Figure 4: Number of instructions violating a given guideline installing packages with apt-get is a pretty common operation.
Layer caching. We implemented the rule defined in Sec. 4.3. We
found that 19, 309 instructions violate this guideline. This means
Table 1 shows the number of dockerfiles that present potential
that close to 89% of the analyzed descriptors install software with-
conflicts for each guideline pair. We represent only the upper right
out properly updating their dependencies first. These 19, 309 flawed
part of the matrix, as it is symmetric by construction. There is a
commands are spread over 8, 496 dockerfiles, which represent 79.32%
potential conflict when two guidelines are violated on the same
of the descriptors using this package manager, and 37.02% of DS.
dockerfile d and target the same kind k of commands. For example,
This means that 3 out of 4 dockerfiles that installs software using
8, 492 dockerfiles violates guideline 6 and guideline 7, whereas only
apt are introducing a flaw that leads to outdated dependencies. It
124 dockerfiles violated guideline 1 and guideline 5. Two issues
represents more than a third of our entire experimental dataset
can occur on a single dockerfile but on different instructions and
emphasizing how dangerous this abstraction leak is. Thanks to our
therefore create no new conflict, but this information is not available
automatic rewriting system, all these errors have been automati-
on Table 1.
cally detected and fixed by appending the proper arguments into
Table 2 shows the number of instructions that are really con-
the body of the targeted instructions.
flicting, i.e., conflicts occurring on the same instructions of the
same dockerfile and producing different results. We note that some Reducing number of layers. As we said in Sec. 4.2, the weight
guidelines pairs (e.g., G 1 and G 15 ) are violated on many dockerfiles of the final artifact is bound to its number of instructions, i.e., the
(1816) but that only 671 instructions are really in conflict; whereas less instructions are in the dockerfile, the lighter the artifact and
others (e.g., G 6 and G 7 ) are violated on around 8, 500 dockerfiles, vice-versa. By running the described rewriting rule on our dataset,
and that more that 20, 000 instructions are really conflicting. This we found that 79, 045 RUN instructions can be automatically deleted,
result shows that these guidelines are often violated together, on lightening the weight of concerned dockerfiles and of their children.

1713
This means that 97.83% of the RU N commands of our dataset (which 7 CONCLUSIONS & PERSPECTIVES
contains 80, 799 RUN instructions in total) can be safely deleted, In this paper, we illustrated how crucial the deployment descriptor
lightening the size of artifacts. This is due to the fact that developers are in the micro-service development community, and the asso-
assume a 1 − 1 mapping between commands executed on a classic ciated challenges for service designers. We identified two crucial
shell and what happens inside a dockerfile at build time, pictured challenges in this context: (i) how to support an everchanging set of
in List. 4. Again, thanks to our automatic rewriting system, all guidelines when writing descriptors (C 1 ) and (ii) how to deal with
these RUN have been automatically detected and merged together issues introduced by the black-box reuse mechanism associated to
by merging their instructions. containers (C 2 ). To address the latter challenge, we defined a formal
model in a technology independent way, reifying a composition
6 RELATED WORK operator that leverages the image reusing mechanisms to build
The distributed system community faces the challenge of deploy- normalized descriptors. To address the former one, we described
ing multi-tenant pieces of software since decades. Automated ap- how the framework can be instantiated to fit the Docker platform
proaches have been proposed to support the scripting (using both specificities. Moreover, a conflict detection mechanism defined at
declarative [10] or imperative [7] descriptors) of distributed de- the formal level using first order logic supports the detection of
ployments, for example using dedicated architecture deployment overlapping rules, addressing both challenges. We evaluated this
languages [11]. These academic approaches were complemented contribution on a set of more than 20, 000 real deployment descrip-
by industrial implementations during the rise of the cloud era, with tors, showing the benefits of the normalized approach to identify
systems such as Chef [15] or Puppet [1], to orchestrate different hidden errors in designers’ descriptors.
sequences of shell commands to support distributed deployment. To pursue this work, we plan to address two limitations en-
These systems also propose a black-box reuse mechanism that can countered by this contribution. The first limitation comes with the
lead to guideline violation or optimization issue. Our work com- substitution mechanism: even if it seems to us simple to understand
plements state of the art practices by allowing one to statically and use, its expressiveness is limited and makes the implementation
analyze a docker image descriptor and its hierarchy, leading to a of complex rewriting rules tedious. We plan to extend it by moving
safer deployment of micro-services applications. from logical terms substitutions to a graph algebra, and reason on
Static analyzers of dockerfile are available [14, 22] but focus on more precise operations. Another lead to address this perspective
vulnerabilities detection only ; comparing with open databases con- is to rely on the Praxis approach [3] to reason on descriptor modi-
tent. Several prototypes address the difficulty of writing a dockerfile fications rather than on substitutions. The second perspective to
by providing domain-specific languages (DSLs [12]) dedicated to address is the definition of a traceability model associated with
this task, written in OCaml9 , Javascript10 , or Go languages. Using service descriptors. For now, the approach works at the normalized
these languages enables the static analysis of a given descriptor, descriptor level, for checking and rewriting, and consider it as a
and the safe generation of a valid dockerfile. whole. Scattering the rewriting to several descriptors, as well as
Rocker11 is a tool that adds features to ease the writing of dock- identifying the root causes of the rules violations or conflicts will
erfiles. It provides a higher-level language to avoid many mistakes help the maintenance of the service descriptor hierarchy. Finally,
made when writing docker descriptors. Even if it does not pro- from a service engineering point of view, measuring the impact of
vide reasoning on dockerfiles, it shows how difficult the process of the other tools associated with containers to support micro-service
writing a dockerfile can be. scaling is an interesting field which is not addressed by the scientific
The community-linter system12 supports the analysis of a literature.
given dockerfile with respect to the referenced best practices listed
by the Docker company. However, contrary to our contribution, REFERENCES
these approaches do not support the analysis of the whole hierar- [1] Syed Ali. 2015. Configuration Management with Puppet. Apress, Berkeley, CA,
chy associated to the dockerfile, are focused on syntactical errors 109–135. https://doi.org/10.1007/978-1-4842-0511-2_5
[2] Armin Balalaie, Abbas Heydarnoori, and Pooyan Jamshidi. 2016. Microservices
only (e.g., missing or extra symbol in command body), and are not architecture enables DevOps: migration to a cloud-native architecture. IEEE
capable of conflicts detection. Software 33, 3 (2016), 42–52.
[3] Xavier Blanc, Isabelle Mounier, Alix Mougenot, and Tom Mens. 2008. Detecting
An anti-pattern is a literary form that describes a commonly model inconsistency through operation-based model construction. In 30th Inter-
occurring solution to a problem that generates decidedly nega- national Conference on Software Engineering (ICSE 2008), Leipzig, Germany, May
tive consequences [5]. An anti-pattern describes a general form, 10-18, 2008, Wilhelm Schäfer, Matthew B. Dwyer, and Volker Gruhn (Eds.). ACM,
511–520. https://doi.org/10.1145/1368088.1368158
symptoms describing how to recognize the general form, its conse- [4] Carl Boettiger. 2015. An Introduction to Docker for Reproducible Research.
quences, and a refactored solution describing how to change the SIGOPS Oper. Syst. Rev. 49, 1 (Jan. 2015), 71–79. https://doi.org/10.1145/2723872.
anti-pattern into a healthier situation [5]. An anti-pattern can be 2723882
[5] W. Brown, Malveau R., H. McCormick III, and T. Mowbray. (1998). Anti Patterns:
modeled as rule that checks itself against a model and proposes, Refactoring Software, Architectures, and Projects in Crisis. John Wiley and Sons.
if applied, a refactoring. Therefore, the rules described in this pa- Robert Ipsen, 157. http://ff.tu-sofia.bg/~bogi/France/SoftEng/books/Wiley%
20-%20AntiPatterns,%20Refactoring%20Software,%20Architectures,%20and%
per can be considered as anti-pattern detections for micro-services 20Projects%20in%20Crisis.pdf
deployment descriptors. [6] CoreOS. 2017. RKT - A security-minded, standards-based container engine.
https://coreos.com/rkt/. (2017).
9 https://github.com/avsm/ocaml-dockerfile.
[7] G. Deng, D. C. Schmidt, and A. Gokhale. 2008. CaDAnCE: A Criticality-Aware
10 https://www.npmjs.com/package/dockerfile-generator.
Deployment and Configuration Engine. In 2008 11th IEEE International Symposium
11 https://github.com/grammarly/rocker.
on Object and Component-Oriented Real-Time Distributed Computing (ISORC). 317–
12 http://hadolint.lukasmartinelli.ch/. 321. https://doi.org/10.1109/ISORC.2008.58

1714
[8] DevOps.com and ClusterHQ. 2016. Container market adoption - Survey 2016. IEEE International Conference on. 386–393. https://doi.org/10.1109/IC2E.2015.74
https://clusterhq.com/assets/pdfs/state-of-container-usage-june-2016.pdf. (jun [18] Sébastien Mosser, Mireille Blay-Fornarino, and Laurence Duchien. 2012. A
2016). Commutative Model Composition Operator to Support Software Adaptation.
[9] W. Felter, A. Ferreira, R. Rajamony, and J. Rubio. 2015. An updated performance In Modelling Foundations and Applications - 8th European Conference, ECMFA
comparison of virtual machines and Linux containers. In Performance Analysis of 2012, Kgs. Lyngby, Denmark, July 2-5, 2012. Proceedings (Lecture Notes in Com-
Systems and Software (ISPASS), 2015 IEEE International Symposium on. 171–172. puter Science), Antonio Vallecillo, Juha-Pekka Tolvanen, Ekkart Kindler, Har-
https://doi.org/10.1109/ISPASS.2015.7095802 ald Störrle, and Dimitrios S. Kolovos (Eds.), Vol. 7349. Springer, 4–19. https:
[10] Nicolas Ferry, Hui Song, Alessandro Rossini, Franck Chauvel, and Arnor Solberg. //doi.org/10.1007/978-3-642-31491-9_3
2014. CloudMF: Applying MDE to Tame the Complexity of Managing Multi- [19] Irakli Nadareishvili, Ronnie Mitra, Matt McLarty, and Mike Amundsen. 2016.
cloud Applications. In Proceedings of the 7th IEEE/ACM International Conference Microservice Architecture: Aligning Principles, Practices, and Culture. " O’Reilly
on Utility and Cloud Computing, UCC 2014, London, United Kingdom, December Media, Inc.".
8-11, 2014. IEEE Computer Society, 269–277. https://doi.org/10.1109/UCC.2014.36 [20] René Peinl, Florian Holzschuher, and Florian Pfitzer. 2016. Docker Cluster Man-
[11] Areski Flissi, Jérémy Dubus, Nicolas Dolet, and Philippe Merle. 2008. Deploying agement for the Cloud - Survey Results and Own Solution. Journal of Grid
on the Grid with DeployWare. In 8th IEEE International Symposium on Clus- Computing 14, 2 (2016), 265–282. https://doi.org/10.1007/s10723-016-9366-y
ter Computing and the Grid (CCGrid 2008), 19-22 May 2008, Lyon, France. IEEE [21] Rami Rosen. 2014. Linux containers and the future cloud. Linux J 2014, 240
Computer Society, 177–184. https://doi.org/10.1109/CCGRID.2008.59 (2014).
[12] Martin Fowler. 2010. Domain Specific Languages (1st ed.). Addison-Wesley [22] Gareth Rushgrove. 2015. Over 30High Priority Security Vulnerabilities. https:
Professional. //banyanops.com/pdf/BanyanOps-AnalyzingDockerHub-WhitePaper.pdf. (jun
[13] HashiCorp. 2017. Vagrant - DEVELOPMENT ENVIRONMENTS MADE EASY. 2015).
https://www.vagrantup.com/. (2017). [23] Gareth Rushgrove. 2016. DockerCon16 - The Dockerfile Explosion and the Need
[14] Oscar Henriksson. 2017. Static Vulnerability Analysis of Docker Images. http: for Higher Level Tools by Gareth Rushgrove. https://goo.gl/86XPrq. (jun 2016).
//www.diva-portal.se/smash/get/diva2:1118087/FULLTEXT02.pdf. (2017). [24] Mark E. Stickel. 1981. A Unification Algorithm for Associative-Commutative
[15] Matthias Marschall. 2013. Chef Infrastructure Automation Cookbook. Packt Functions. J. ACM 28, 3 (July 1981), 423–434. https://doi.org/10.1145/322261.
Publishing. 322262
[16] Dirk Merkel. 2014. Docker: Lightweight Linux Containers for Consistent [25] M. G. Xavier, M. V. Neves, F. D. Rossi, T. C. Ferreto, T. Lange, and C. A. F. De
Development and Deployment. Linux J. 2014, 239, Article 2 (March 2014). Rose. 2013. Performance Evaluation of Container-Based Virtualization for High
http://dl.acm.org/citation.cfm?id=2600239.2600241 Performance Computing Environments. In 2013 21st Euromicro International
[17] R. Morabito, J. KjÃďllman, and M. Komu. 2015. Hypervisors vs. Lightweight Conference on Parallel, Distributed, and Network-Based Processing. 233–240. https:
Virtualization: A Performance Comparison. In Cloud Engineering (IC2E), 2015 //doi.org/10.1109/PDP.2013.41

1715

You might also like