Simplifying Multi-Step Dockerfile with Drone Pipeline
Despite the adoption of cloud architectures, many companies haven’t achieved optimal results. Learn how to simplify your multi-step Dockerfile deployment with Drone in the cloud.
Over the past few years, many organizations have begun adopting cloud-native architectures. Despite the adoption of these architectures, many companies haven’t achieved optimal results. But why is that? One reason is our adherence to traditional methods of building and deploying applications. As we live in the cloud-native era, every application that we build should be deployable to any cloud through containerization.
To demonstrate what I mean in this tutorial, I’ll use the next.js with-docker-example. You can clone this demo source from my blog demo sources.
The goal here is to deploy this application onto a cloud platform. These are the steps to do that:
- Containerize the application using the Dockerfile
- Push them to any Container Registry, such as Github, Quay.io, etc.
- Deploy the containerized applications to a Cloud platform, such as Kubernetes, AWS Fargate, Google Cloud Run, etc.
Let’s start analyzing our code starting with Dockerfile:
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
FROM node:16-alpine AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
FROM node:16-alpine AS runner
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static
ENV PORT 3000
CMD ["node", "server.js"]
This is a multi-step build, which can be difficult for people newer to these processes. Furthermore, if one of the steps fails, then it becomes difficult for us to know why it failed. But let’s stop and think for a second: don’t we feel that all of these commands should flow in a sequence with each step running in a defined order, one after the other? If you also came to this conclusion, then you’re with me on your first step toward continuous integration (CI).
Why Build is Not the Same as CI
We often get confused between builds and CI. To add fuel to fire, the tools in the market can also be quite confusing, offering plugins and extensions to the build tools, e.g., yarn or Apache Maven, to mix the build with CI. I’m a big fan of the Single Responsibility principle. When we apply that to the application build and deploy process, the build in a cloud-native application should build the application to a container image and then hand off the other steps (such as pushing to the registry, deploying to cloud-native platform, etc.) to CI tools.
With this in mind, when we think about CI, I’ve already introduced you to two important pieces of nomenclature:
- Step – Something that only does one thing, such as build an application, push to registry, etc.
- Pipeline – Putting multiple steps together to embrace CI
Before I go further into how you can move from multistep dockerfile builds to a CI pipeline, let’s meet an open source tool, Drone, a cloud-native self-service CI platform. Although it’s 10 years old, Drone still offers a mature CI system harnessing the scaling and fault tolerance characteristics of cloud-native architectures. Drone is known for its simple, decoupled, and declarative features, enabling us to define some robust pipelines with an ease of understanding.
Adding Dockefile to the Pipeline
Let’s start moving our multistep Dockerfile to a Drone pipeline. This is just like how Dockerfile Drone uses a YAML file called .drone.yml, which usually resides in the root of the project sources. You can read more about Drone on the documentation page. In upcoming sections, we’ll start putting together the .drone.yml.
For the first step in writing a .drone.yml, we need the following information:
- What is the name of the application? nextjs-with-drone
- What type of build will we use? Docker
- What kind of Drone resource will we use? We’ll use Pipeline, as we have multiple steps.
- What kind of platform will we be building on? For most container cases, the platform will be Linux, and the OS architecture will be amd64 or arm64.
With this information, our .drone.yml will look as follows:
So now we have the first few lines of the Pipeline filled up. The next task is to identify our pipeline steps from the Dockerfile, and we can infer that we have the following three steps:
- deps – builds the nodejs dependencies
- builder – builds the node.js application
- runner – the package for the application as linux container image
Now let’s begin adding each of these steps as Drone pipeline steps. The Drone pipeline step at minimum requires the following details:
- What is the name of the step?
- What container image will this step run?
- What commands will execute it within the container?
We have all of this required information from the Dockerfile, so let’s start adding them to the .drone.yml as Drone steps:
As you’ve likely noticed, the commands attribute of the step is just RUN, COPY instructions in Dockerfile. These are translated into equivalent Linux commands. We have added an extra attribute called volumes, which is essentially used to mount any directories or files into the container. In this case, we used it to share the build artifacts of one step with another.
Final Steps: Cleaning Up
One last step is cleaning up the Dockerfile so that we have just one step to build to the final container image. The cleaned-up Dockerfile looks like this:
You’re now all set to do your first CI build of your application! Download the Drone CLI and add it to your $PATH. If it all went well when running the following command, then you should see the output like drone version 1.5.0:
Let us run the pipeline:
If it all went well, then your container image would have been pushed to the ttl.sh repository. Let’s run the container locally to see if our build works:
When you open http://localhost:3000 in your browser, you should see the welcome screen:
Voilà! You have taken your first step toward practicing CI employing software delivery best practices. Now you can improve or add additional steps to the Pipeline to make it deploy to container cloud platforms.
Ready to get started with Drone Desktop? Download the free trial today.