<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jose Armesto&#39;s Blog</title>
    <link>https://blog.armesto.net/tags/development/index.xml</link>
    <description>Recent content on Jose Armesto&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>es-es</language>
    <copyright>Powered by [Hugo](//gohugo.io). Theme by [PPOffice](https://github.com/ppoffice).</copyright>
    <atom:link href="https://blog.armesto.net/tags/development/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Continuous Integrating my Kubernetes controller using Kind</title>
      <link>https://blog.armesto.net/continuous-integrating-my-kubernetes-controller-using-kind/</link>
      <pubDate>Sat, 18 May 2019 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/continuous-integrating-my-kubernetes-controller-using-kind/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/47848258/kubernetes-controller-vs-kubernetes-operator/47857073#47857073&#34;&gt;Kubernetes controllers&lt;/a&gt; are processes that react to changes in the state of some objects saved on the Kubernetes API.
They do this by watching the Kubernetes API so that they get notified whenever the state changes.&lt;/p&gt;

&lt;p&gt;But how do we know if our Kubernetes controller is doing what&amp;rsquo;s supposed to do?
We can write &lt;a href=&#34;https://codeclimate.com/github/fiunchinho/iam-role-annotator&#34;&gt;unit tests&lt;/a&gt; that test our logic in isolation to get some confidence, but it would be great if we could add some &lt;a href=&#34;https://martinfowler.com/articles/practical-test-pyramid.html&#34;&gt;end to end tests&lt;/a&gt; using the real Kubernetes API.&lt;/p&gt;

&lt;p&gt;However, deploying Kubernetes is not an easy task. And even if we managed to accomplish it, it would take a long time to deploy a Kubernetes cluster so we could test every change to our code base.&lt;/p&gt;

&lt;p&gt;In this post, I&amp;rsquo;m going to explain how I used &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt; to test that &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;my controller&lt;/a&gt; is working as expected, using a real Kubernetes API.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;creating-the-cluster-using-kind&#34;&gt;Creating the cluster using kind&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;My Kubernetes controller&lt;/a&gt; is pretty simple. Whenever a Deployment object is created containing an specific annotation, it will add an annotation to the Pods belonging to that Deployment.
I created this controller because we needed a way for developers to specify that the application that they were deploying required AWS IAM credentials to use AWS services, but we didn&amp;rsquo;t want developers to have to know things like the AWS account id to use.
By using this controller, the Kubernetes cluster administrators configure which AWS Account to use on every Kubernetes namespace. And developers just indicate whether their application requires IAM authentication or not, by adding an annotation. Simple.&lt;/p&gt;

&lt;p&gt;Anyway, I wanted to create an end to end test that would do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Install the controller using the version that we are testing&lt;/li&gt;
&lt;li&gt;Create a Deployment that contains the annotation that will trigger the controller&lt;/li&gt;
&lt;li&gt;Test that the Pods of the Deployment contain the IAM annotation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the Kubernetes cluster I used &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt;, a recent project that let&amp;rsquo;s you deploy a Kubernetes cluster using only a Docker container. Obviously, it&amp;rsquo;s not meant to be used on production, but it&amp;rsquo;s perfect for testing purposes. To create a cluster with kind, you just need to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ kind create cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that&amp;rsquo;s it! It&amp;rsquo;s that simple. We can see the cluster is running inside a single container&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                                  NAMES
c27f2c8fa39e        kindest/node:v1.14.1   &amp;quot;/usr/local/bin/entr…&amp;quot;   22 minutes ago      Up 22 minutes       63711/tcp, 127.0.0.1:63711-&amp;gt;6443/tcp   kind-control-plane
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you just need to make &lt;code&gt;kubectl&lt;/code&gt; send requests to that cluster, using the command&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ export KUBECONFIG=&amp;quot;$(kind get kubeconfig-path)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&amp;rsquo;m also creating a new Namespace where I&amp;rsquo;ll run all the things required for this test. This is not important during Continuous Integration (CI), where everything will be deleted when the CI job is finished.
But I do it this way because I can then easily run the same test locally on other Kubernetes clusters like Minikube, while not on CI.
When the test is done, I just remove the namespace to leave no traces of the test execution.&lt;/p&gt;

&lt;h2 id=&#34;installing-my-controller&#34;&gt;Installing my controller&lt;/h2&gt;

&lt;p&gt;Now I need to install the piece of code that I want to test: my controller. The controller repository contains a &lt;a href=&#34;https://helm.sh/&#34;&gt;Helm&lt;/a&gt; chart to make it easy to install it, so I&amp;rsquo;ll just use it. That way, I&amp;rsquo;m also testing whether or not the Helm chart is working as expected.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ helm upgrade --tiller-namespace ${NAMESPACE} --namespace &amp;quot;${NAMESPACE}&amp;quot; --wait --install &amp;quot;iam-role-annotator&amp;quot; &amp;quot;./charts/iam-role-annotator&amp;quot; --set image.tag=&amp;quot;${TRAVIS_COMMIT:-latest}&amp;quot; --set awsAccountId=&amp;quot;${AWS_ACCOUNT_ID}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The value for the &lt;code&gt;AWS_ACCOUNT_ID&lt;/code&gt; variable is important. It&amp;rsquo;s the same AWS Account Id that needs to be added as annotation on applications.
Notice that I passed the &lt;code&gt;--wait&lt;/code&gt; argument to Helm. This will block the command until my controller is running and ready. If it fails to start for whatever reason, the test would automatically fail.&lt;/p&gt;

&lt;h2 id=&#34;create-annotated-deploy-and-check-the-results&#34;&gt;Create annotated deploy and check the results&lt;/h2&gt;

&lt;p&gt;My controller is ready and waiting for applications that contain the right annotation.
Creating a simple hello world application containing that annotation is enough to trigger my controller. Let&amp;rsquo;s use a simple nginx deployment&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat &amp;lt;&amp;lt;EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  namespace: ${NAMESPACE}
  labels:
    app: nginx
  annotations:
    armesto.net/iam-role-annotator: &amp;quot;true&amp;quot;
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
      annotations:
        prometheus.io/scheme: http
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the important annotation for my controller: &lt;code&gt;armesto.net/iam-role-annotator: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now I just need to wait for my controller to react.&lt;/p&gt;

&lt;p&gt;Then I can test if the pods belonging to the hello world deployment contain the required annotation. For that I used &lt;code&gt;jq&lt;/code&gt;, a handly command line tool to inspect json data.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;POD_NAME=$(kubectl get pods --namespace ${NAMESPACE} --field-selector=status.phase=Running -l &amp;quot;app=nginx&amp;quot; -o jsonpath=&amp;quot;{.items[0].metadata.name}&amp;quot;)

if [[ $(kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.metadata.annotations&#39; | jq &#39;contains({&amp;quot;iam.amazonaws.com/role&amp;quot;})&#39;) == &#39;true&#39; ]]; then
  if [[ $(kubectl get pods --namespace ${NAMESPACE} ${POD_NAME} -o json | jq -r &#39;.metadata.annotations.&amp;quot;iam.amazonaws.com/role&amp;quot;&#39;) == &amp;quot;arn:aws:iam::${AWS_ACCOUNT_ID}:role/${DEPLOYMENT_NAME}&amp;quot; ]]; then
    echo &amp;quot;SUCCESS!&amp;quot;
    exit 0
  else
    echo &amp;quot;ERROR: the annotation contains the wrong value&amp;quot;
    kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.&#39;
    exit 1
  fi
else
  echo &amp;quot;ERROR: the POD does not contain the expected annotation&amp;quot;
  kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.&#39;
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Every time I push some changes &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;to this controller&lt;/a&gt;, the CI pipeline kicks in, and &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator/blob/master/e2e_test.sh&#34;&gt;my end to end test&lt;/a&gt; gets executed creating a Kubernetes cluster thanks to &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I have much more confidence that my changes will work as expected when deploying the controller to our production clusters, and that makes a huge difference.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Automatically versioning your Application on Jenkins X</title>
      <link>https://blog.armesto.net/automatically-versioning-your-application-on-jenkins-x/</link>
      <pubDate>Fri, 01 Feb 2019 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/automatically-versioning-your-application-on-jenkins-x/</guid>
      <description>&lt;p&gt;Having a good versioning strategy for our applications is key.
Specially in Jenkins X, which follows the &lt;a href=&#34;https://www.weave.works/blog/what-is-gitops-really&#34;&gt;GitOps flow&lt;/a&gt; to deploy our applications, specifying which version of our application will be used on each environment.&lt;/p&gt;

&lt;p&gt;But having to manually create tags or releases for our applications can be a tedious task. Jenkins X automatically takes care of versioning for us.
It uses a tool called &lt;a href=&#34;https://github.com/jenkins-x/jx-release-version&#34;&gt;jx-release-version&lt;/a&gt; to figure out which is the next version to be released.
In order to do that it checks what&amp;rsquo;s the current released version in the repository looking at the released Git tags. It can also read this version from your &lt;code&gt;pom.xml&lt;/code&gt; file, or your &lt;code&gt;Makefile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we use &lt;a href=&#34;https://semver.org/&#34;&gt;semver semantics&lt;/a&gt;, and versions are written in the format major.minor.patch, jx-release-version will tell you which is the next patch version.&lt;/p&gt;

&lt;p&gt;The cool thing is that you don&amp;rsquo;t need to use Jenkins X to use &lt;a href=&#34;https://github.com/jenkins-x/jx-release-version&#34;&gt;jx-release-version&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s go through some examples.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h1 id=&#34;using-git-tags&#34;&gt;Using git tags&lt;/h1&gt;

&lt;p&gt;Using git tags is probably the easiest way to handle our application versions. We can create a new git tag for every new version that we want to release.&lt;/p&gt;

&lt;p&gt;If we try to use &lt;code&gt;jx-release-version&lt;/code&gt; on a Git repository that has no tags, it will return that the next version number to release is &lt;code&gt;0.0.1&lt;/code&gt;.
Next time we use &lt;code&gt;jx-release-version&lt;/code&gt;, it will increase our patch number.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ git --no-pager tag -l
$ # there are no tags just yet!
$ RELEASE_VERSION=`jx-release-version` &amp;amp;&amp;amp; git tag -fa v${RELEASE_VERSION} -m &#39;Release version ${RELEASE_VERSION}&#39;
$ git --no-pager tag -l
v0.0.1
$ RELEASE_VERSION=`jx-release-version` &amp;amp;&amp;amp; git tag -fa v${RELEASE_VERSION} -m &#39;Release version ${RELEASE_VERSION}&#39;
$ git --no-pager tag -l
  v0.0.1
  v0.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;using-maven-pom-file&#34;&gt;Using maven pom file&lt;/h1&gt;

&lt;p&gt;If you are using a &lt;code&gt;pom.xml&lt;/code&gt; file that also tracks your current application version, you still can use &lt;code&gt;jx-release-version&lt;/code&gt;.
It will try to sync the git tags in your Git repository with the version that you specify in the &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Your release process needs to look something like this&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# First we call the `jx-release-version` binary that will return the next version number to be released.
# Every time we call the binary it will try to figure out the next version number. Normally it&#39;s not a good idea to call it more than once.
RELEASE_VERSION=`jx-release-version`
echo &amp;quot;New release version ${RELEASE_VERSION}

# We update our current pom.xml file with this new version number.
mvn versions:set -DnewVersion=${RELEASE_VERSION}

# Changes to the pom.xml file need to be committed to our repository.
git commit -a -m &#39;release ${RELEASE_VERSION}&#39;

# The git commit containing both our application changes and the change to the pom.xml file will be tagged using the same version number.
git tag -fa v${RELEASE_VERSION} -m &#39;Release version ${RELEASE_VERSION}&#39;

# Push the commit and tag to the remote repository.
git push origin v${RELEASE_VERSION}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we start a new git repository that has no tags, the &lt;code&gt;jx-release-version&lt;/code&gt; will use the version in our &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;project xmlns=&amp;quot;http://maven.apache.org/POM/4.0.0&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; xsi:schemaLocation=&amp;quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&amp;quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

    &amp;lt;groupId&amp;gt;io.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;example&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;packaging&amp;gt;pom&amp;lt;/packaging&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The release version for &lt;code&gt;1.0.0-SNAPSHOT&lt;/code&gt; is &lt;code&gt;1.0.0&lt;/code&gt;, so &lt;code&gt;jx-release-version&lt;/code&gt; will return &lt;code&gt;1.0.0&lt;/code&gt; as the next version number to use.
Similarly, if our &lt;code&gt;pom.xml&lt;/code&gt; file had &lt;code&gt;&amp;lt;version&amp;gt;0.0-23-SNAPSHOT&amp;lt;/version&amp;gt;&lt;/code&gt; in it, &lt;code&gt;jx-release-version&lt;/code&gt; would return &lt;code&gt;0.0.23&lt;/code&gt; as the next version number to use.&lt;/p&gt;

&lt;h1 id=&#34;using-a-makefile&#34;&gt;Using a Makefile&lt;/h1&gt;

&lt;p&gt;Let&amp;rsquo;s say we have this Makefile that tracks the current version of our application&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# This is the current version of your application
VERSION := 2.0.3-SNAPSHOT

# Use jx-release-version to calculate next version
RELEASE_VERSION := $(shell jx-release-version)

build:
	# The git commit containing our application changes will be tagged.
	git tag -fa v${RELEASE_VERSION} -m &#39;Release version ${RELEASE_VERSION}&#39;

	# Push the tag to the remote repository.
	git push origin v${RELEASE_VERSION}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The current version of our application is &lt;code&gt;2.0.3-SNAPSHOT&lt;/code&gt;.
That means that the &lt;code&gt;jx-release-version&lt;/code&gt; will return &lt;code&gt;2.0.3&lt;/code&gt; as the next version number to use.&lt;/p&gt;

&lt;h1 id=&#34;releasing-a-new-major-minor-version&#34;&gt;Releasing a new major/minor version&lt;/h1&gt;

&lt;p&gt;Sometimes we don&amp;rsquo;t want to just release a new patch version (like going from &lt;code&gt;1.0.5&lt;/code&gt; to &lt;code&gt;1.0.6&lt;/code&gt;). Instead, we want to release &lt;code&gt;1.1.0&lt;/code&gt;, or even &lt;code&gt;2.0.0&lt;/code&gt;.
We have said earlier that jx-release-version calculates the next version number based on the current Git tag, or current specified version on &lt;code&gt;pom.xml&lt;/code&gt;/&lt;code&gt;Makefile&lt;/code&gt;.
So if we need to release a new major/minor version, we just have to release a new Git tag or update the &lt;code&gt;pom.xml&lt;/code&gt;/&lt;code&gt;Makefile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if the current version is &lt;code&gt;0.0.2&lt;/code&gt; and we want to release &lt;code&gt;0.1.0&lt;/code&gt;, we first create a git tag for that and let &lt;code&gt;jx-release-version&lt;/code&gt; do it&amp;rsquo;s thing afterwards.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Manually create new tag for the version that we want
git tag -fa v0.1.0 -m &amp;quot;Release version 0.1.0&amp;quot;
# Use jx-release version normally
$ RELEASE_VERSION=`jx-release-version` &amp;amp;&amp;amp; git tag -fa v${RELEASE_VERSION} -m &#39;Release version ${RELEASE_VERSION}&#39;
$ git --no-pager tag -l
v0.0.1
v0.0.2
v0.1.0
v0.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As said in the &lt;a href=&#34;https://github.com/jenkins-x/jx-release-version&#34;&gt;project&amp;rsquo;s README&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your project is new or has no existing git tags then running &lt;code&gt;jx-release-version&lt;/code&gt; will return a default version of &lt;code&gt;0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If your latest git tag is &lt;code&gt;1.2.3&lt;/code&gt; and you Makefile or pom.xml is &lt;code&gt;1.2.0-SNAPSHOT&lt;/code&gt; then &lt;code&gt;jx-release-version&lt;/code&gt; will return &lt;code&gt;1.2.4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If your latest git tag is &lt;code&gt;1.2.3&lt;/code&gt; and your Makefile or pom.xml is &lt;code&gt;2.0.0&lt;/code&gt; then &lt;code&gt;jx-release-version&lt;/code&gt; will return &lt;code&gt;2.0.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Jenkins X Environments</title>
      <link>https://blog.armesto.net/jenkins-x-environments/</link>
      <pubDate>Wed, 05 Dec 2018 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/jenkins-x-environments/</guid>
      <description>&lt;p&gt;This year Jenkins X has been announced. If you haven&amp;rsquo;t heard, Jenkins X is a tool that lets you automate the whole delivery process of your software to a Kubernetes cluster.
One key aspect of Jenkins X is that the deployment of applications is implemented following the &lt;a href=&#34;https://www.weave.works/blog/what-is-gitops-really&#34;&gt;GitOps flow&lt;/a&gt;.
In this approach every environment is represented by a Git repository. In this post we&amp;rsquo;ll see how Jenkins X environments work.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;environments&#34;&gt;Environments&lt;/h2&gt;

&lt;p&gt;An installation of Jenkins X consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Development Environment&lt;/code&gt; which is a kubernetes namespace running tools like Jenkins, Nexus, etc. Each different team within an organization is meant to have its own development environment so that they can be as independent from each other as possible.&lt;/li&gt;
&lt;li&gt;Other &lt;code&gt;Permanent Environments&lt;/code&gt; which are kubernetes namespaces where our applications will be deployed into. By default &lt;code&gt;Staging&lt;/code&gt; and &lt;code&gt;Production&lt;/code&gt; are created. Each team can have as many Permanent Environments as they wish and call them whatever they like.&lt;/li&gt;
&lt;li&gt;Optional &lt;code&gt;Preview Environments&lt;/code&gt;, which are kubernetes namespaces where our applications will be deployed, just like Permanent Environments, but these are created on a PR basis, before you even merge your PR changes into master.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the covers this is implemented creating a custom Kubernetes resource &lt;code&gt;jenkins.io/v1/Environment&lt;/code&gt;.
Assuming that each team has a Kubernetes namespace assigned, each team will normally have a Development Environment and several different Permanent Environments.
Each of these environments is represented by a &lt;code&gt;jenkins.io/v1/Environment&lt;/code&gt; object on the namespace assigned to that team.&lt;/p&gt;

&lt;p&gt;Teams are also represented by a &lt;code&gt;jenkins.io/v1/Team&lt;/code&gt; object, but we will cover that on a different post.&lt;/p&gt;

&lt;p&gt;These &lt;code&gt;Environment&lt;/code&gt; objects contain configuration about which Git repository is associated with it, and which git branch to use.&lt;/p&gt;

&lt;p&gt;There could be one namespace which is where all apps run across all teams - though from a kubernetes RBAC perspective, if you are working in microservices teams, it&amp;rsquo;s better for each team to manage their own microservices in their own production environment and use service linking between teams.&lt;/p&gt;

&lt;h3 id=&#34;development-environment&#34;&gt;Development Environment&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;jenkins.io/v1/Environment&lt;/code&gt; object created on the team’s namespace, which &lt;code&gt;spec.Kind&lt;/code&gt; field is &lt;code&gt;Dev&lt;/code&gt;.
This environment is not linked to a Github repository, we will never promote changes here.
It’s just a Kubernetes namespace used to run applications while developing, thanks to &lt;a href=&#34;https://github.com/GoogleContainerTools/skaffold&#34;&gt;skaffold&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the dev environment Jenkins X installs a number of core applications they believe are required at a minimum to start folks off with CI/CD on Kubernetes. They come with configuration that wires these services together meaning everything works together straight away.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jenkins — provides both Continuous Integration and Continuous Delivery automation.&lt;/li&gt;
&lt;li&gt;Nexus — acts as a dependency cache for applications to dramatically improve build times.&lt;/li&gt;
&lt;li&gt;Docker registry — an in cluster Docker registry where the pipelines push application images.&lt;/li&gt;
&lt;li&gt;Chartmuseum — a registry for publishing Helm charts.&lt;/li&gt;
&lt;li&gt;Monocular — a UI used for discovering and running Helm charts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;permanent-environments&#34;&gt;Permanent Environments&lt;/h3&gt;

&lt;p&gt;These environments are where the applications will ran. By default &lt;code&gt;Staging&lt;/code&gt; and &lt;code&gt;Production&lt;/code&gt; are created, but more environments can be created. &lt;code&gt;Staging&lt;/code&gt; has the Auto promote strategy and &lt;code&gt;Production&lt;/code&gt; the Manual promote strategy. We&amp;rsquo;ll see what that means in a minute.&lt;/p&gt;

&lt;p&gt;These environment are linked to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub repository containing a Helm chart that defines which application charts are to be installed on the environment, which versions of them and any environment specific configuration and additional resources (e.g. Secrets or operational applications like Prometheus etc).&lt;/li&gt;
&lt;li&gt;A Kubernetes namespace where the Helm chart from the repository will be installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you want to deploy to an environment, a Pull Request must be made to that environment’s repository.
We can manually create the PR to deploy an application to an environment, or we can use this handy &lt;code&gt;jx&lt;/code&gt; command&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx promote --app myapp --version 1.2.3 --env production
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Typically we specify the environment while promoting to environments with the Manual promote strategy, like the &lt;code&gt;Production&lt;/code&gt; environment.
Instead of specifying the environment, we can create a Pull Request in all the environments having the Auto promote strategy using this &lt;code&gt;jx&lt;/code&gt; command&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx promote -b --all-auto --timeout 1h --version \$(cat ../../VERSION) --no-wait
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Changing the &lt;code&gt;Production&lt;/code&gt; environment&amp;rsquo;s promote strategy from Manual to Auto you can do Continuous Deployment.&lt;/p&gt;

&lt;p&gt;When these PRs are merged into the environment&amp;rsquo;s git repository, the environment pipeline runs, which then applies the Helm chart living on that repo to the environment&amp;rsquo;s Kubernetes namespace.&lt;/p&gt;

&lt;h3 id=&#34;preview-environments&#34;&gt;Preview Environments&lt;/h3&gt;

&lt;p&gt;Preview Environments are similar to Permanent Environments in that they are defined in a source code Git repository using Helm charts.
The main difference is that Preview Environments are configured inside the application repository in the ./chart/preview folder.
Also they are not permanent but created when a Pull Request is made to an application&amp;rsquo;s git repository, and then deleted some time after (manually or via automatic garbage collection).&lt;/p&gt;

&lt;p&gt;When the Preview Environment is up and running Jenkins X will comment on your Pull Request with a link so in one click your team members can try out the preview.&lt;/p&gt;

&lt;p&gt;You can create a preview environments using&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx preview
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will create the &lt;code&gt;Environment&lt;/code&gt; and &lt;code&gt;Namespace&lt;/code&gt; objects for this environment.
It will also build the application creating a new Docker image that will be deployed to the preview environment using the preview chart defined in the ./chart/preview folder.&lt;/p&gt;

&lt;h2 id=&#34;managing-environments&#34;&gt;Managing environments&lt;/h2&gt;

&lt;p&gt;You can create new environments using jx&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx create env --name prod --label Production --namespace my-prod
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some interesting options are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;label: The Environment label which is a descriptive string like &lt;code&gt;Production&lt;/code&gt; or &lt;code&gt;Staging&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;prefix: Environment repo prefix, your Git repo will be of the form &lt;code&gt;environment-$prefix-$envName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;promotion: The promotion strategy, Auto or Manual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can then list all existing environments&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx get environments
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or edit an existing environment (-b for no interactive)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx edit env -b --name prod --label Production --no-gitops --namespace my-prod
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or finally delete it&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jx delete environment prod
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;deployment-flow&#34;&gt;Deployment Flow&lt;/h2&gt;

&lt;p&gt;In the workflow that Jenkins X proposes, we will have a repository for each application that we want to deploy.
But also we will have a repository for each environment where you want to deploy these applications.
The repositories for the different environments describe all the applications that must be running on them.&lt;/p&gt;

&lt;p&gt;So while developing your application, the usual flow would be&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Pull Request on the application repository: this will execute the application CI pipeline to build and test the application.&lt;/li&gt;
&lt;li&gt;Once the Pull Request on the application repository is merged, the application pipeline will release this newly built version to a Docker Registry and promote it to the environments with the Auto promote strategy using a &lt;code&gt;jx&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;If we want to deploy to production or any environment with the Manual promotion strategy, we need to execute the &lt;code&gt;jx&lt;/code&gt; command targeting the desired environment.&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Layered architecture and Kubernetes operators</title>
      <link>https://blog.armesto.net/layered-architecute-and-kubernetes-operators/</link>
      <pubDate>Mon, 03 Dec 2018 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/layered-architecute-and-kubernetes-operators/</guid>
      <description>&lt;p&gt;If you&amp;rsquo;ve been developing applications for some time, you&amp;rsquo;ve probably developed some kind of HTTP application.
This is the most solved problem there is in our industry, with lots of frameworks and tools that help you solve this problem easily.
Also, there are some patterns and practices that we apply when solving this, that come from years of experience learning the pain points while developing these applications.
I&amp;rsquo;m talking about things like not coupling your business rules to your framework, or using layers to separate things like data access objects from your domain model.&lt;/p&gt;

&lt;p&gt;However, sometimes we forget that we can apply these patterns to almost any kind of software project.
Lately I&amp;rsquo;ve been involved in projects that contain some Kubernetes Operators and the code in there could benefit a lot from the patterns that we already apply to our typical HTTP applications.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3 id=&#34;don-t-let-your-domain-model-depend-on-your-framework&#34;&gt;Don&amp;rsquo;t let your domain model depend on your framework&lt;/h3&gt;

&lt;p&gt;I created &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;this Kubernetes controller&lt;/a&gt; some months ago.
There are several different frameworks to help you create your own Kubernetes Operator/Controller, but in this particular case, I decided to try &lt;a href=&#34;https://github.com/spotahome/kooper&#34;&gt;Kooper&lt;/a&gt;.
If I search the keyword &amp;ldquo;kooper&amp;rdquo; in the codebase of my controller, there are only two matches: &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator/blob/master/Gopkg.toml#L17-L19&#34;&gt;the dependency file declaring the dependency on the kooper library&lt;/a&gt;, and the main file that bootstraps the controller.
All the other files (specially the &lt;code&gt;pkt/service.go&lt;/code&gt; file containing all the business logic) don&amp;rsquo;t depend on the kooper framework at all.
If I would switch to a different framework, I wouldn&amp;rsquo;t need to change pretty much anything, only the bootstrapping of the process that takes care of all the wiring.
All the important bits, the business logic containing what my controller actually does, that wouldn&amp;rsquo;t need to be rewritten or even touched.&lt;/p&gt;

&lt;h3 id=&#34;you-are-using-a-database-believe-it-or-not&#34;&gt;You are using a database, believe it or not&lt;/h3&gt;

&lt;p&gt;Kubernetes Operators and Controllers use the Kubernetes API to get the current state of the cluster, and store data on inside Kubernetes resources.
This means that these processes are using etcd as a database, which we normally access through the Kubernetes API using the &lt;a href=&#34;https://github.com/kubernetes/client-go&#34;&gt;client-go library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In other kind of applications, we have been creating specific objects that take care of accessing our data for years.
We usually call these objects the data access layer, but somehow we don&amp;rsquo;t do it when accessing the data stored in Kubernetes objects.
It&amp;rsquo;s pretty normal to find &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator/blob/5d56a9b2801064d4d1d71f5d47cf8b496a4b37de/pkg/service.go#L73-L77&#34;&gt;code like this all over a Kubernetes operator or controller&lt;/a&gt;&lt;/p&gt;

&lt;script src=&#34;//gist.github.com/fiunchinho/e99adce57a2d676e1d39fa3653a7e78a.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;We are coupling our business logic to the client-go library that we use to talk to the Kubernetes API.
This makes our Kubernetes operators and controllers hard to test, because when this code is executed, it will try to connect to a real Kubernetes cluster.
&lt;a href=&#34;https://godoc.org/k8s.io/client-go/kubernetes/fake&#34;&gt;The client-go library offers some tooling&lt;/a&gt; to help you create Fake API&amp;rsquo;s, but wouldn&amp;rsquo;t be better if we wouldn&amp;rsquo;t need to use that to test every part of our application?&lt;/p&gt;

&lt;p&gt;By encapsulating the data access parts of your code on its own objects, you could write unit tests for the important bits of your application where the business logic is happening, easily stubbing the data access objects.
We could use &lt;a href=&#34;https://martinfowler.com/eaaCatalog/repository.html&#34;&gt;the Repository pattern&lt;/a&gt; for this.
&lt;a href=&#34;https://github.com/fiunchinho/dmz-controller/blob/master/repository/configmap.go&#34;&gt;This is a repository to fetch &lt;code&gt;ConfigMaps&lt;/code&gt;&lt;/a&gt; from the Kubernetes API. That&amp;rsquo;s the real implementation that my controller will use when it&amp;rsquo;s started.
But &lt;a href=&#34;https://github.com/fiunchinho/dmz-controller/blob/master/repository/fake_configmap.go&#34;&gt;this is the implementation that my controller will use when running the unit tests&lt;/a&gt;.
This fake implementation won&amp;rsquo;t try to connect to a real Kubernetes cluster: it just stores the objects in memory, which is fine to run our tests.&lt;/p&gt;

&lt;p&gt;We would still need to write some integration tests, to make sure that everything works well together. But all the complexity of testing your code depending on the client-go library, would only affect your repository objects.&lt;/p&gt;

&lt;h3 id=&#34;dependency-injection&#34;&gt;Dependency injection&lt;/h3&gt;

&lt;p&gt;In order to achieve what we&amp;rsquo;ve been talking about on this post, it&amp;rsquo;s really important that we use &lt;a href=&#34;https://martinfowler.com/articles/injection.html&#34;&gt;dependency injection&lt;/a&gt; to pass the right objects to our methods.
We need to be able to pass either the real data access objects or the stubbed ones.&lt;/p&gt;

&lt;p&gt;Instead of instantiating the objects that your service depends on inside its own functions, declare those dependencies as parameters that need to be passed when creating the service.
This way you can pass the right implementation that you need. On your unit tests, pass the stubbed implementation. On the real bootstrapping of your service, pass the real implementation that talks to the Kubernetes API, &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator/blob/5d56a9b2801064d4d1d71f5d47cf8b496a4b37de/pkg/service.go#L27-L34&#34;&gt;like this&lt;/a&gt;&lt;/p&gt;

&lt;script src=&#34;//gist.github.com/fiunchinho/f53e16bc8c9cdfffed8a9275bd288447.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;This service doesn&amp;rsquo;t care if the &lt;code&gt;client&lt;/code&gt; is the real one or the stubbed one.&lt;/p&gt;

&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;At the end of the day, it&amp;rsquo;s just applying what we have already been applying to our applications for years. As Kubernetes controllers and operators get more complex, a better structure for our code is needed to keep the code maintainable.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>I didn&#39;t know you could do that with a Dockerfile!</title>
      <link>https://blog.armesto.net/i-didnt-know-you-could-do-that-with-a-dockerfile/</link>
      <pubDate>Tue, 30 Oct 2018 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/i-didnt-know-you-could-do-that-with-a-dockerfile/</guid>
      <description>&lt;p&gt;Docker released the &lt;a href=&#34;https://docs.docker.com/develop/develop-images/multistage-build/&#34;&gt;multi-stage builds feature&lt;/a&gt; in the version 17.05. This allowed developers to build smaller Docker images by using a final stage containing the minimum required for the application to work. Even though this is being used more and more over time, there are still multi-stage patterns that are not that widely used.&lt;/p&gt;

&lt;p&gt;In this post I&amp;rsquo;ll show you how to use multi-stage builds to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avoid having different Dockerfiles for every environment&lt;/li&gt;
&lt;li&gt;copy files from remote images&lt;/li&gt;
&lt;li&gt;use parameters in the &lt;code&gt;FROM&lt;/code&gt; image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Multi-stage builds allow us to use the &lt;code&gt;FROM&lt;/code&gt; keyword in several places of the same Dockerfile.
Every time we use the &lt;code&gt;FROM&lt;/code&gt; keyword a new stage will be created.
One important thing is that we can name these stages to refer to them later on in the Dockerfile.&lt;/p&gt;

&lt;h2 id=&#34;you-don-t-need-a-dockerfile-for-each-environment&#34;&gt;You don&amp;rsquo;t need a Dockerfile for each environment&lt;/h2&gt;

&lt;p&gt;Imagine the following scenario: you need certain tools installed on the Docker image that you will use for development, but you don&amp;rsquo;t want those tools on the final image that will be deployed in production.
For example, when developing in PHP it&amp;rsquo;s useful to have &lt;code&gt;xdebug&lt;/code&gt; installed, but you normally don&amp;rsquo;t need it in production.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Use this image as the base image for dev and prod.
FROM php:7.2-apache as common

# The pdo_mysql extension is required for both dev and prod.
RUN a2enmod rewrite; \
    chown -R www-data:www-data /var/www/html; \
    docker-php-ext-install pdo_mysql;

# Here we configure PHP, but this configuration will be overwritten for prod.
COPY php.ini /usr/local/etc/php/php.ini


# In this image we will download the dependencies, but without the development dependencies.
# The dependencies are installed in the vendor folder that will later be copied.
FROM composer as builder-dev

WORKDIR /app

# Copy only the files needed to download dependencies to avoid redownloading them when our code changes.
# This will download development/testing dependencies.
COPY composer.json composer.lock /app/
RUN composer install  \
    --ignore-platform-reqs \
    --no-ansi \
    --no-autoloader \
    --no-interaction \
    --no-scripts

# We need to copy our whole application so that we can generate the autoload file inside the vendor folder.
COPY . /app
RUN composer dump-autoload --optimize --classmap-authoritative


# This is the image using in development.
FROM common as dev

# We only install xdebug in development.
RUN pecl install xdebug-2.6.0; \
    docker-php-ext-enable xdebug

WORKDIR /var/www/html

# We enable the errors only in development.
ENV DISPLAY_ERRORS=&amp;quot;On&amp;quot;

# Copy our application.
COPY . /var/www/html/
# Copy the downloaded dependencies from the previous stage.
COPY --from=builder-dev /app/vendor /var/www/html/vendor
# Setup PHP for development.
COPY php-dev.ini /usr/local/etc/php/php.ini
RUN composer dump-autoload --optimize --classmap-authoritative


# In this image we will download the dependencies, but without the development dependencies.
# The dependencies are installed in the vendor folder that will be copied later into the prod image.
FROM composer as builder-prod

WORKDIR /app

COPY composer.json composer.lock /app/
RUN composer install  \
    --ignore-platform-reqs \
    --no-ansi \
    --no-dev \
    --no-autoloader \
    --no-interaction \
    --no-scripts

# We need to copy our whole application so that we can generate the autoload file inside the vendor folder.
COPY . /app
RUN composer dump-autoload --optimize --no-dev --classmap-authoritative

# This is the image that will be deployed on production.
FROM common as prod

# Add label and exposed port for documentation.
LABEL maintainer=&amp;quot;Jose Armesto &amp;lt;jose@armesto.net&amp;gt;&amp;quot;
EXPOSE 80

# No display errors to users in production.
ENV DISPLAY_ERRORS=&amp;quot;Off&amp;quot;

# Copy our application
COPY . /var/www/html/
# Copy the downloaded dependencies from the builder-prod stage.
COPY --from=builder-prod /app/vendor /var/www/html/vendor
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;building-our-application-image&#34;&gt;Building our application image&lt;/h3&gt;

&lt;p&gt;When building the Docker image now we can choose which stage to build. The stages that will be executed depend on which stage we choose to build and the order in which these stages are defined in the Dockerfile.&lt;/p&gt;

&lt;p&gt;If we are developing and need our &lt;code&gt;dev&lt;/code&gt; version of the application, we can build the Docker image like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker build --tag &amp;quot;my-awesome-app&amp;quot; --target &amp;quot;dev&amp;quot; .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will only execute the Dockerfile lines of the stage named &lt;code&gt;dev&lt;/code&gt;, and the stages that appear right before it: &lt;code&gt;common&lt;/code&gt; and &lt;code&gt;builder-dev&lt;/code&gt;.
If we would&amp;rsquo;ve placed the &lt;code&gt;builder-prod&lt;/code&gt; stage before the definition of the &lt;code&gt;dev&lt;/code&gt; stage (right after the &lt;code&gt;builder-dev&lt;/code&gt; stage), the &lt;code&gt;builder-prod&lt;/code&gt; stage lines would&amp;rsquo;ve got executed when building the &lt;code&gt;dev&lt;/code&gt; stage, even though we don&amp;rsquo;t need them.
So make sure to plan ahead and put the stages in the right order: every stage related to &lt;code&gt;dev&lt;/code&gt; will be placed before any stage related to &lt;code&gt;prod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we need the &lt;code&gt;prod&lt;/code&gt; version that will be deployed in production, we can pass the &lt;code&gt;prod&lt;/code&gt; target or just don&amp;rsquo;t pass any target at all&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker build --tag &amp;quot;my-awesome-app&amp;quot; .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will execute all the instructions in the Dockerfile, because the &lt;code&gt;prod&lt;/code&gt; stage is the last one to appear on the Dockerfile.&lt;/p&gt;

&lt;h3 id=&#34;can-we-improve-the-building-speed-for-development&#34;&gt;Can we improve the building speed for development?&lt;/h3&gt;

&lt;p&gt;It seems that building our docker image for development is kind of slow. Can we make it faster?
Our approach is copying our application files several times from our laptop to the image layer, making the process slow. And it will be slower as our application grows.&lt;/p&gt;

&lt;p&gt;We can merge the &lt;code&gt;builder-dev&lt;/code&gt; and the &lt;code&gt;dev&lt;/code&gt; stages into one big stage to reduce the number of times we copy our application.
Let&amp;rsquo;s remove the &lt;code&gt;builder-dev&lt;/code&gt; stage and change our &lt;code&gt;dev&lt;/code&gt; stage to this&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# In the development image we download dependencies and copy our code to the image.
FROM common as dev

# We only want xdebug in development.
# We need to install some tools required by Composer, which will run in this stage.
RUN apt-get update; apt-get install -y wget zip unzip git; \
    pecl install xdebug-2.6.0; \
    docker-php-ext-enable xdebug; \
    wget https://raw.githubusercontent.com/composer/getcomposer.org/1b137f8bf6db3e79a38a5bc45324414a6b1f9df2/web/installer -O - -q | php -- --install-dir=/usr/bin --filename=composer --quiet

WORKDIR /var/www/html

# Copy only the files needed to download dependencies to avoid redownloading them when our code changes
COPY composer.json composer.lock /var/www/html/
RUN composer install  \
    --ignore-platform-reqs \
    --no-ansi \
    --no-autoloader \
    --no-interaction \
    --no-scripts

# We enable the errors only in development
ENV DISPLAY_ERRORS=&amp;quot;On&amp;quot;

# Copy our application. Now the dependencies are already there.
COPY . /var/www/html/
# Setup PHP for development.
COPY php-dev.ini /usr/local/etc/php/php.ini
RUN composer dump-autoload --optimize --classmap-authoritative
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our development image will contain Composer (and wget, zip&amp;hellip;) too, but I think that&amp;rsquo;s not a big deal in this scenario.&lt;/p&gt;

&lt;h2 id=&#34;parametrized-image-tags&#34;&gt;Parametrized image tags&lt;/h2&gt;

&lt;p&gt;Did you know that you can use parameters for the base image to use when building your image? We can define a parameter that sets the PHP version to use&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ARG PHP_VERSION=7.2

FROM php:${PHP_VERSION}-apache as common
# Here would go the rest of the Dockerfile
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then just pass the right PHP version to use when building the image. If we want to use PHP v7.1 instead&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker build --tag &amp;quot;my-awesome-app&amp;quot; --build-arg &amp;quot;PHP_VERSION=7.1&amp;quot; .
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;combine-targets-with-docker-compose&#34;&gt;Combine targets with docker-compose&lt;/h3&gt;

&lt;p&gt;The reality is that many people use docker-compose while developing locally because it makes it really easy to start other containers along with your application, like a database that your application needs to store information.
In this scenario you can tell docker-compose to build your image and which target to use.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &#39;2.4&#39;
services:
  web:
    build:
      context: .
      target: dev
      args:
        PHP_VERSION: ${PHP_VERSION}
    ports:
      - 8000:80
    volumes:
      - .:/var/www/html
    depends_on:
      - postgres
  postgres:
    image: postgres:11.0-alpine
    environment:
      POSTGRES_USER: dbuser
      POSTGRES_DB: my-db
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can now fire up your application and its dependencies as usual, but you can pass the desired PHP version as an environment variable&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ PHP_VERSION=7.1 docker-compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;copying-from-remote-images&#34;&gt;Copying from remote images&lt;/h2&gt;

&lt;p&gt;We have seen how to copy files from previously generated layers.
But did you know that you can copy files from remote images?&lt;/p&gt;

&lt;p&gt;For example, instead of installing Composer in our &lt;code&gt;dev&lt;/code&gt; stage, we could just copy it from the official Composer image&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# In the development image we download dependencies and copy our code to the image.
FROM common as dev

# We only want xdebug in development.
# We need to install some tools required by Composer, which will run in this stage.
RUN apt-get update; apt-get install -y zip unzip git; \
    pecl install xdebug-2.6.0; \
    docker-php-ext-enable xdebug;

# Copy composer binary from official Composer image.
COPY --from=composer /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

# Copy only the files needed to download dependencies to avoid redownloading them when our code changes
COPY composer.json composer.lock /var/www/html/
RUN composer install  \
    --ignore-platform-reqs \
    --no-ansi \
    --no-autoloader \
    --no-interaction \
    --no-scripts

# We enable the errors only in development
ENV DISPLAY_ERRORS=&amp;quot;On&amp;quot;

# Copy our application. Now the dependencies are already there.
COPY . /var/www/html/
# Setup PHP for development.
COPY php-dev.ini /usr/local/etc/php/php.ini
RUN composer dump-autoload --optimize --classmap-authoritative
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
  </channel>
</rss>