Multi-step Docker build process
Published: 2023-04-05 07:41:58

Multi-step Docker build process is a technique that allows you to optimize your Docker images by breaking the build process into multiple stages, and it involves the following steps:

### 1. Define the base image

Start by defining the base image for your Dockerfile using the FROM statement. This is the image that will be used to create the first stage of the build process.

### 2. Copy the necessary files 

Use the COPY or ADD command to copy the necessary files and folders into the first stage of the Docker build.

### 3. Build your application

Run any necessary build commands, such as compiling code, installing dependencies, or running tests, within the first stage of the build process.

### 4. Define the second stage

Use another FROM statement to define a second stage for the build process. This second stage will be based on a different image, such as a minimal runtime image, and will not include any unnecessary files or dependencies from the first stage.

### 5. Copy files from the first stage

Use the COPY --from= command to copy the necessary files and artifacts from the first stage into the second stage of the Docker build.

### 6. Define the startup command

Finally, use the CMD or ENTRYPOINT command to define the startup command for the second stage of the Docker build.

By using a multi-step Docker build process, you can create smaller and more efficient Docker images by only including the necessary files and dependencies in the final image.

## Let’s get started 

1. make sure you have nodejs installed
`$ node -v`

2. Create a React app called `frontend`
`$ npx create-react-app frontend`

3. React startup commands are:

- Development server: `$ npm run start`

- Run tests: `$ npm run test`

- Builds a production version: `$ npm run build`

4. Create `Dockerfile.dev`

```
FROM node:16-alpine

WORKDIR '/app'

#copy package.json to /app
COPY package.json .
#install all our dependencies
RUN npm install

#copy everything else from our project directory
COPY . .

#startup our project
CMD ["npm", "run", "start"]
```

5. Build our docker container in our development env
`$ docker build -t frontend -f Dockerfile.dev .`

6. Run your build
`$ docker run frontend -p 3000:3000 -v /app/node_modules -v $(pwd):/app`

Check out [http://localhost/3000](http://localhost/3000) to see your brand new app running in a docker container.

## Docker compose

Docker Compose is a tool that allows you to define and run multi-container Docker applications. It is used to manage the dependencies and services required by your application, and it simplifies the process of defining, running, and connecting multiple Docker containers.

In a Docker Compose file, you define the services that make up your application, including the images used, environment variables, volumes, network connections, and other settings. You can then use the docker-compose command to create and start all the necessary containers for your application.

Docker Compose simplifies the process of building and running complex multi-container applications by providing a simple and flexible way to manage the dependencies and configurations of your services.

Let’s create a docker-compose file for our dev environment so we can run our container in just 1 simple command.

7. Create `docker-compose.yml`

```
version: '3'
services:
  web:
    container_name: web
    build:
      context: . #specifying where we want all the files and folders for this image to be pulled from
      dockerfile: Dockerfile.dev #location of dockerfile to be used
    ports:
      - "3000:3000"
    volumes:
      - /app/node_modules #this means "do not try to map a folder up against node_modules inside the container"
      - .:/app #the . is the current folder outside the container: the /app folder is inside the container

```

8. Run your docker-compose file
`$ docker-compose up`

Check out [http://localhost/3000](http://localhost/3000) again to see your app is running, but this time from a single command with the help of docker-compose.

## Production

For the production we need nginx as a webserver to run our website. Let’s see how this works.

Multi-step Docker build process:

- Build Phase: Use node:alpine -> Copy the package.json file -> Install dependencies -> Run 'npm run build'

- Run Phase: Use nginx -> Copy over the result of 'npm run build' -> Start nginx

11. Create `Dockerfile`

```
#Build Phase
FROM node:16-alpine as builder #tagged as the builder-fase
WORKDIR '/app'
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

#Run Phase
FROM nginx
COPY --from =builder /app/build  /usr/share/nginx/html #copy the stuff we care about from the other phase to this specifical nginx folder
#default command of the ngxin container/image is going to startup nginx for us
```

12. Build your container
`$ docker build .`

13. Run your build
`$ docker run -p 8080:80 [build_image_id]`

In production we won’t use docker-compose, this is only for our dev environment.