Docker - an overview Docker is an open platform for developing, shipping, and running applications. Docker ensures quick delivery of your software by providing isolation between your app and the infrastructure. Docker packages and runs everything inside a loosely isolated environment called the container.
Key Terms Image - a complete package that contains everything (application code, required libraries, software dependencies, configurations, etc.) needed to run your app (just like a Class in OOP)
Container - an instance of the image, just like an object in OOP
Volume - images are read-only, to persist data you have to use volumes. In simplest terms, you share a folder (on host OS) with your docker image to read/write data from/to it.
Dockerfile - the blueprints of an image. This is where you define what will be inside the image you are trying to build. Like OS (e.g Ubuntu 16), Softwares (e.g Node), etc.
Tag - for now just consider it in literal terms.
I assume you have your React application ready that you want to containerize using docker, if you don't, you can clone this sample React application and follow along.
You need to create two configuration files, for:
Nginx
The build files of React are just static (HTML, CSS, JS, etc.) files and you need a webserver to serve your static files like Nginx, Apache, OpenLiteSpeed, etc.
Inside your React app, create another directory and name it nginx
. Inside the nginx directory (you just created), create a new file and name it nginx.conf
. You can also use to following commands (one-by-one to achieve it).
1cd my-app 2mkdir nginx 3cd nginx 4touch nginx.conf
Edit the "nginx.conf" file and add the following code to it.
1server { 2 3 listen 80; 4 5 location / { 6 root /usr/share/nginx/html; 7 index index.html index.htm; 8 9 # to redirect all the requests to index.html, 10 # useful when you are using react-router 11 12 try_files $uri /index.html; 13 } 14 15 error_page 500 502 503 504 /50x.html; 16 17 location = /50x.html { 18 root /usr/share/nginx/html; 19 } 20 21}
The gist of this code block is that you're telling Nginx to listen on port 80, redirect every request to "index.html" and the root is "/usr/share/nginx/html" (the directory where to serve from).
Dockerfile
Inside your app directory create a new file and name it as Dockerfile.prod
and add the following code in it:
1# stage1 - build react app first 2FROM node:12.16.1-alpine3.9 as build 3WORKDIR /app 4ENV PATH /app/node_modules/.bin:$PATH 5COPY ./package.json /app/ 6COPY ./yarn.lock /app/ 7RUN yarn 8COPY . /app 9RUN yarn build 10 11# stage 2 - build the final image and copy the react build files 12FROM nginx:1.17.8-alpine 13COPY --from=build /app/build /usr/share/nginx/html 14RUN rm /etc/nginx/conf.d/default.conf 15COPY nginx/nginx.conf /etc/nginx/conf.d 16EXPOSE 80 17CMD ["nginx", "-g", "daemon off;"]
Create a new file and name it as .dockerignore
and add node_modules
inside it. This is simply to tell Docker to ignore the node_modules
directory.
So your directory structure should be like this
my-app
│ Dockerfile.prod
│ .dockerignore
│
└───nginx
nginx.conf
Explanation
Stage 1
FROM
tells what base image to use (required), you can check out base images at Docker HubWORKDIR
is used to specify the working directory (inside the image, not your host OS)ENV PATH
adds node_modules in the PATHCOPY
is used to copy package.json
from the current directory (on the host) to the working directory (in the image).RUN
is used to run the command, here we want to run Yarn
to install the dependencies mentioned in package.jsonCOPY
is run again to copy all the code from the host OS to the working directory in the imageyarn build
to build our appYou copy package.json first and install the dependencies and don't copy node_modules
into the image. This is to leverage the excellent caching system of Docker and reduce build times.
Stage 2
In the first stage, you copied package.json to the working directory, installed the dependencies, copied your code, and built the final static files. In stage 2:
Nginx
as a base image. (nginx
is the image, and 1.17.8-alpine
is the tag. It's like you are telling what particular version/release of the Nginx base image you would like to use)./usr/share/nginx/html
(the default directory where Nginx serves from)/etc/nginx/conf.d/default.conf
EXPOSE
to expose the port of the container. One pitfall here is that it doesn't actually expose the port, rather it is just for the sake of documentationNginx
in the foreground, not as a daemon (i.e in the background).Both CMD
and RUN
are used to run commands. The difference is that RUN
is an image build step, whereas CMD
is the command a container executes by default when it is started.
Step 3: Build and Tag Image From the root directory of your app, run the following command to build & tag your docker image:
docker build -f Dockerfile.prod -t my-first-image:latest .
-f
is used to specify the filename. If you don't specify it then you must rename your file to Dockerfile
- that's what the build command looks for in the current directory by default.-t
is used to tag the image. You can tag your image the way you want (e.g v1.0.0, v2.0.0, production, latest, etc.).
at the end is important, and it should be added to tell docker to use the current directory.**Step 4: Run Container
The final step is to run the built image (as a container)
docker run -it -p 80:80 --rm my-first-image:latest
-it
for interactive mode-p
to expose and bind ports. Here we are exposing port 80 of the container and binding it with port 80 of the host machine. The first one is of your machine (host OS) and the second one is of the docker image container. For example, if you use -p 1234:80
then you will need to go to http://localhost:1234
on your browser.--rm
to remove the container once it is stoppedmy-first-image:latest
the name:tag of the image we want to run container ofNow open up your browser and go to http://localhost
and you will see your app being served from the docker. If you make any changes to your React application code, you will need to rebuild the image (Step 3) and run it again (Step 4).
Extra
docker image ls
to see a list of all the images on your machinedocker container ls
to see all the running containersdocker system prune
to prune the containers (be careful while using this command, read docs for options before using them)Let's connect:
Linkedin: https://www.linkedin.com/in/mubbashir10/
Twitter: https://twitter.com/mubbashir100