Dockerise the React Getting Started
One of the drawbacks that affect teams when developing software is sharing a common environment that allows everyone in the team to build against frameworks and dependencies.
One of the solutions to this problem can be using containers.
Containers are sometimes defined as lightweight virtual machines.
Running a container is similar to running a virtual machine with the only noticeable difference is that to interact with it (logging, launch commands, run applications) one would generally use the Docker CLI.
Like with virtual machines, containers have images that can be instantiated and run.
Containers abstract operative systems, not hardware.
Containers are a solution to the problem of how to get software to run reliably when moved from one computing environment to another. This could be from a developer’s laptop to a test environment, from a staging environment into production, and perhaps from a physical machine in a data center to a virtual machine in a private or public cloud.
In this post, I will dockerize a React application.
Docker
The first step will be downloading and installing Docker Desktop.
Docker is architected as a client/server solution. Client-side a CLI is provided that allows interacting with a daemon running on your computer.
You can choose the Operative System you want your containers to run, Windows and Linux containers are available to date.
I will use Windows containers in this article.
A dockerised application is composed of its source code and a Dockerfile. A Dockerfile allows to interact with Docker and instruct how to build and run a container and the image within it.
In a Dockerfile, at its simplest form, one would generally:
- Pick an image for the container from a Registry or from those publicly available on Docker Hub. For example on this post the following image will be used:
- Configure the environment according to what an application would need to do (like, opening ports).
- Run your executables inside the container. In this step, one would specify a command for the engine to run on start.
As a reference, this would be a typical Dockerfile:
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-nanoserver-1909
WORKDIR /app
ENTRYPOINT ["dotnet", "website.dll"]
FROM
The
FROM
instruction initializes a new build stage and sets the Base Image for subsequent instructions. As such, a validDockerfile
must start with aFROM
instruction. The image can be any valid image – it is especially easy to start by pulling an image from the Public Repositories.
WORKDIR
The
WORKDIR
instruction sets the working directory for anyRUN
,CMD
,ENTRYPOINT
,COPY
andADD
instructions that follow it in theDockerfile
.
ENTRYPOINT
An
ENTRYPOINT
allows you to configure a container that will run as an executable.
In a nutshell, ENTRYPOINT [“dotnet”, “website.dll”] would be similar to start your container using the configured image, log on into it and type “dotnet website.dll”.
Dockerise a React Frontend
In order to build a trivial web application using React, I followed this nice multi-part getting started tutorial:
I also took the working code from the official getting started:
I simplified a bit the webpack configuration that is suggested:
In a nutshell, I removed the suggested output folder and kept the dist folder that is used by default.
Also, as of webpack 4 there is no need any more to set the entry point as far as is in /src/index.js.
I also changed the index.js to use JSX, the syntax extension to JavaScript.
In a classic scenario, a new developer working on this code would need to install node on their machine. They would also need to install all the node modules.
This steps can be automated using a “Dockerfile”, this is the one I used:
FROM node:latestWORKDIR /usr/src/appCOPY package.json ./RUN npm installCOPY . .EXPOSE 80ENTRYPOINT npx webpack-dev-server --host 0.0.0.0 --port 80
I had to explicitly ask webpack-dev-server to listen on IP 0.0.0.0 as I was otherwise receiving an EMPTY_RESPONSE error. The container automatically redirects calls coming from outside to the 0.0.0.0 IP while webpack-dev-server is configured to bind to localhost, 127.0.0.1.
To build an image, the command to be passed to the Docker CLI is:
docker build . -t frontend
The newly built image will now be visible running:
docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE
frontend latest 52159d9a7188 5 days ago 1.05GB
node latest 07e774543bdf 2 weeks ago 939MB
Two images are available locally, one containing the installation of Node.js, the other created starting from that with our source code in the “/usr/src/app” folder.
Next is time to start the container, the following command will do that:
docker run -p 49160:80 -d frontend
Running:
docker ps
Will show information about the running container.
The React get started will also be browsable.
Running on HTTPS
Running HTTPS on development will require an SSL certificate to be trusted and mounted within a volume inside the container.
The Dockerfile will need to change as “webpack-dev-server” will need to be configured to use the newly created certificate.
FROM node:latestWORKDIR /usr/src/appCOPY package.json ./RUN npm installCOPY . .EXPOSE 80ENTRYPOINT npx webpack-dev-server --host 0.0.0.0 --https --port 443 --pfx=/etc/ssl/certs/backend.pfx --pfx-passphrase=**yourpassword**
The changes I made where:
- The port is now 443
- We reference a certificate, named “backend.pfx”
- We pass a password to access it
After modifying the Dockerfile we will need to rebuild the image:
docker build . -t frontend
One way to generate and trust a self-signed certificate might be by using OpenSSL. However, today I will be using the “dotnet dev-certs” tool.
The steps to create and trust a self-signed certificate are detailed here:
Once the .pfx file is exported inside the “$Env:USERPROFILE\.aspnet\https” folder, it can be mounted as volume passing the following option to the “docker run” command:
- -v $Env:USERPROFILE\.aspnet\https:/etc/ssl/certs/ frontend:latest
The final command will be:
docker run --rm -it -p 443:443 -v $Env:USERPROFILE\.aspnet\https:/etc/ssl/certs/ frontend:latest
Compiling JSX Manually
If you are curious to see what Babel does under the wood see this great response:
I installed the Babel CLI:
npm install babel-cli --save-dev
I then transformed the JSX file manually:
npx babel --plugins transform-react-jsx .\src\index.js
This was the result:
Summary
Switching to containers in development allows an application to run isolated from their OS of choice.
One big limitation of developing a front-end application using Docker is that we might not be able to watch changes.