Create a custom docker image

Yashod Perera
4 min readApr 18, 2020

Let’s start with docker. For that we need to know how to run our projects inside a docker container and in this tutorial I go through

  • Basic structure of custom docker image
  • Common mistakes we do
  • How to run any project on docker in development environment
  • Run a react project on docker

Basic structure of custom docker image

Before creating a custom docker image we should understand the basic structure of the docker image and docker file

Docker image consists of base image, Dependencies and files added and the startup command.

Dockerfile is the one which holds instructions to create the image. Then it should basically have,

  • Instructions to add base image — Base image consists of file structure.
  • Instructions to install dependencies and copy files
  • Startup command

Let’s identify the basic Dockerfile structure using a code.

FROM node                    // Define a base imageCOPY . .                     // Copy project files to the container
RUN npm install // Install node packages
PORT 3000 // Define the PORT
CMD ["npm", "start"] // Define startup command

As you see there are three main section in basic docker file as described above. Let’s go through with keywords one by one.

  • FROM <base_image> — To indicate the base image
  • COPY <source> <destination> — Copy files from local machine to image. Then the source is from the local machine and destination is from the base image.
  • RUN <command> — This is to execute and setup dependencies and packages inside the image.
  • PORT <port_number> — This is to define the port which our application is running. This is only for developer use.
  • CMD [command] — This is to define the startup command

Common mistakes we do

  • Forget to define a working directory

In Dockerfile it is good to mention a working directory as follows. It helps us to separate our code from basic file structure because if there is a folder named same as which is in project there might be some problems.

WORKDIR <location>

If the mentioned location is not available it will automatically build the directory. Then all the COPY folders and files will go to that specific location

  • Copy modules which can be generated. Which leads to copy unnecessary data. Ex: node_modules
  • Choose wrong base image

Other thing we missed is, choosing correct images. In above example also I choose node as my base image which will took the node:latest which is heavier (300MB+) but I can select node:alpline as my base image which will do the same (30MB+).

If we choose a wrong image it will increase our image and container size.

How to run any project on docker in development environment

There are some basic rules you have to follow. Note that there are some cases you have to follow multiple steps which will be cover in another post.

  1. Select an appropriate base image
  2. Copy project into a project directory and generate and install dependencies if needed.
  3. Define the startup command which is the project start command

Run a react project on docker

Let’s make a react project and make the Dockerfile and run it on docker container.

create-react-app myapp

Then make a Dockerfile inside the project directory

myapp
- node_modules
- public
- src
.gitignore
Dockerfile
package-lock.json
package.json
README.md

Let’s create the docker file.

FROM node:alpineWORKDIR /user/app
COPY package.json .
RUN npm install
COPY . .
PORT 3000
CMD ["npm", "start"]

We are ready with our docker file let’s run it. But before you do it make sure to remove the node_modules. Then build it as follows. (Please don’t forget to put the “.” at last because it indicates all the files in project directory. 😉)

docker build -t yashod/myapp .

Quick tip : It is easy to tag your image when building it because it will help to remember the name not the id.

docker build -t <repository_name>/<project_name>:version .

Then it will give output as follows.

Step 1/6 : FROM node:alpine
alpine: Pulling from library/node
Status: Downloaded newer image for node:alpine
---> bcfeabd22749
Step 2/6 : WORKDIR /user/app
---> Running in 3a4b81250301
Removing intermediate container 3a4b81250301
---> 8985045f8548
Step 3/6 : COPY package.json .
---> 1ed815c5108a
Step 4/6 : RUN npm install
---> Running in 61bd9e07980c
Removing intermediate container 61bd9e07980c
---> fce45205d52b
Step 5/6 : COPY . .
---> 79dd365b6d97
Step 6/6 : CMD ["npm", "start"]
---> Running in a5ae55d4882a
Removing intermediate container a5ae55d4882a
---> e65809425baf
Successfully built e65809425baf
Successfully tagged yashodgayashan/myapp:latest

As you in the above all the steps we mentioned in the Dockerfile will be executed one by one and finally create a image called e65809425baf or yashodgayashan/myapp:latest as we tagged it.

Then let’s run that image as follows.

docker run yashodgayashan/myapp:latest

And it will give you output as follows

You can now view myapp in the browser.Local:            http://localhost:3000
On Your Network: http://172.17.0.2:3000
Note that the development build is not optimized.
To create a production build, use npm run build.

If you try to run the “http://localhost:3000it wont begin because the port 3000 is in the container and it is not exposed to our local machine then it has to be mapped as follows,

docker run -p <local_port>:<container_port> image 

Then our application is accessible throughout port 4000 on our local machine

docker run -p 4000:3000 yashodgayashan/myapp:latest

You can access the myapp through “http://localhost:4000”.

If you have found this helpful please hit that 👏 and share it on social media :)

--

--

Yashod Perera

Technical Writer | Tech Enthusiast | Open source contributor