Docker is the Future of Platform Agnostic Development
Muhammad Dzikra Muzaki
Head of Design @ Webmakers IDHow Docker impacted my experience during the development of Pilar.
As you might know from the previous post, I'm currently assigned a project from my course that has three seperate codebases which are REST API using Django, admin webpage that utilizes React, and the mobile app built on flutter.
To start it off, let me preface this by giving my experience at the start of my first sprint and explain a bit about Docker itself. Before we were assigned any tasks, we had the chance to clone the repository and run all of it locally, however, not long after pulling all the necessary files, I was struck with my first obstacle. It seems that for the backend, some dependecies cannot be resolved due to a combination of a mismatched version of python I was running and conflicts of package versions which (I guess) exists on other platforms but doesn't on Windows (however that may be).
Luckily, my team and I decided to dockerize the backend as our first pre-task even though we haven't specified for the CI/CD to make use of it. After creating all the config files and building the image, the container was ready to go. The result was amazing, not only that none of the previous problems I mentioned non-existent, but the environment itself was self-contained including the database. Because, of that things became easier to adopt as we don't need to handle any platform specific errors when preparing to develop the project.
At this point you might ask, 'What actually is Docker? And how does that help development in any meaningful way?' so let me elaborate on that for a moment.
Docker is a platform so to speak that acts as a container engine which developers will use to, you guessed it, contain projects inside a predefined environment. For example, the Docker container environment we used for the backend of Pilar was python since it is a python codebase.
To better quote from an official source,
Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications.
- Official Docker Documentation
So from there we can conclude that Docker's purpose is to create a cleaner and more seamless development environment. We will touch on more about that at the end of this article.
Continuing where we left off, I thought 'Why stop there?' and immediately after I went on to dockerize the frontend side of the project. The process was fairly simple and much less complex than the backend since the frontend only consists of React and static pages that don't need a database whatsoever. These are the steps I took when creating the Docker image:
I first created a file named Dockerfile
which will contain the initial commands and setup the image needs to prepare. The following code is what I used in my Dockerfile
(or as you can see on my commit here),
To briefly explain the code above:
FROM
specifies what image we want to use for our container. Since we are running a javascript application (with the React library) it needs node as the runtime environment. The version code may vary from one project to another, the reason we picked15.11.00
was because it's the latest and fully compatible with every dependency included in the project.WORKDIR
is the default working directory inside the container where all the commands below it will be ran at.COPY
simply copies the specified path (with.
meaning the current directory) to the second specified path relative to the working directory declared above it.RUN
will execute the arguments given to it in which we told it to install the dependencies and additional packages needed to build the project.- and last but not least
CMD
, which is similar toRUN
but instead is executed last as a means to start the project within the container.
However, the Dockerfile
is not enough to setup the image we are building. So, we're gonna need another file named docker-compose.yml
where it will store the configuration of the image itself. This is the code I implemented on said file (which can also be found here),
Again, I'm going to briefly explain the commands written above:
version
is just specifying which version of Docker engine we want to use. It might impact the format of thedocker-compose.yml
file itself but here there is not much to be considered as the configuration is not that complex.services
define what and how many containers we want to run, as you can see we are only going to initiate one container asweb
.- In the
web
container we are going to name itpilar-web
to give it a more recognizable name. This would help when you have many images running because then it is easier to identify which is which. - Next on is
context
which tells Docker where to look for necessary files during the image building process and we are going to point out thedockerfile
configuration exists in theDockerfile
we have created previously. volumes
is where we define paths that we are going to persist, if we are running multiple containers and want it to access the same persisted data in a volume, we can also define thevolumes
as a top level item which can be referenced by other containers.- And
ports
consists of the external port9000
that we can access from outside of the container and the internal port5000
that can only be accessed from inside the container. The external port can be arbitrary however the internal port depends on where to service is running, in this case theserve
command we are using to run the react project is bound by default to port5000
.
After all of the above, now is the time to build the Docker image which is supposedly the easiest step unless you ran into some errors from the configuration. To start building the image you only need to run the command docker-compose up -d --build
in the directory where the previously created two files are located. Docker will begin to execute the predefined commands and voilà the image is ready to use.
When we open localhost:9000
in our browser we are greeted with the form for the admin page.
If you have finished the development session and want to stop the running container you can use,
docker ps
to view a list of running images and note the name of the container that you want to stop. After that you can just stop it using the command,
docker stop container-name
Or if you are using Windows and have the Docker Desktop installed you can just press the stop button in the images list. It's as easy and straightforward as that.
To close this article, I'm just gonna summarize and conclude based on the experience that I've just walked you through. I used Docker as workaround to the dependecy problem I was having on Windows and it turns out I achieved more than what I needed it to do. By using Docker, it negates the platform specific errors and acts accordingly to the defined environment on whichever platform we decide to run the Docker from. I think this is truly incredible since we can make the codebase more accessible. And we haven't even gotten to the part where Docker can also run scripts to configure other components that will tie in to the CI/CD of the project. However, I'm gonna go into details on that in another post.
My conclusion is, the existence of Docker can become the alternative to serve a project locally during development or even in production builds. The versatility of Docker will come in handy as an option readily available. I guess that's enough for this post, thank you for all the readers and I hope this article might help as a source of knowledge or reference to the viewers.