Search code examples
node.jsangulardockerdocker-compose

How to correctly configure Angular such that it can run successfully behind Docker for development


I was looking for a better way to use the Multi-Stage Build process for my Angular Application. Fortunately, after consulting a couple of resources, I came across something I had thought would suffice for my needs here.

I have also taken advantage of the knowledge I have already gained working with Docker & Docker-Compose. However, my recent Angular service does not seem to want to come to life.

Directory Structure

Below is what my stack's directory structure looks like:

.
├── app
├── docker-compose.yaml <--- The docker-compose file used to boot services
├── portal
│   ├── README.md
│   ├── angular.json
│   ├── dist
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── src
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   └── tsconfig.spec.json
└── sys
    ├── mongodb
    ├── portal <--- Where the default.conf & Dockerfile reside for portal service
    └── rabbitmq

The portal directory contains the Angular App we are trying to Dockerize. The sys directory contains both the default.conf & Dockerfile files.

The Dockerfile

I am still in the testing stage of this build and the problem is that when docker-compose tries to build the containers it fails with the portal service. Below is, first, a typical output that shows:

Building portal
[+] Building 4.5s (11/15)
 => [internal] load build definition from Dockerfile                                                                                                                                                   0.0s
 => => transferring dockerfile: 864B                                                                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                                                        0.0s
 => [internal] load metadata for docker.io/library/nginx:stable-alpine                                                                                                                                 1.1s
 => [internal] load metadata for docker.io/library/node:16-alpine                                                                                                                                      1.1s
 => [builder 1/6] FROM docker.io/library/node:16-alpine@sha256:a1f9d027912b58a7c75be7716c97cfbc6d3099f3a97ed84aa490be9dee20e787                                                                        0.0s
 => [stage-1 1/4] FROM docker.io/library/nginx:stable-alpine@sha256:210650bba35a8e714a56aadb4a5abed9db1e55a90122b4bfa38c3977be831db9                                                                   0.0s
 => [internal] load build context                                                                                                                                                                      3.3s
 => => transferring context: 4.91MB                                                                                                                                                                    3.2s
 => CACHED [builder 2/6] WORKDIR /app                                                                                                                                                                  0.0s
 => ERROR [builder 3/6] COPY package.json .                                                                                                                                                            0.0s
 => CACHED [stage-1 2/4] RUN rm -rf /usr/share/nginx/html/*                                                                                                                                            0.0s
 => CACHED [stage-1 3/4] COPY sys/portal/default.conf /etc/nginx/nginx.conf                                                                                                                            0.0s
------
 > [builder 3/6] COPY package.json .:
------
failed to compute cache key: "/package.json" not found: not found
ERROR: Service 'portal' failed to build : Build failed

I have thoroughly inspected my Dockerfile and ensured that everything makes sense and is configured based on my directory structure.

The actual Dockerfile looks as shown below:

###### Install dependencies only when needed ######
FROM node:16-alpine AS builder

ARG CONFIGURATION='development'

# Make /app as working directory
WORKDIR /app

# Copy package.json file
COPY package.json .

# Install dependencies
RUN npm install --legacy-peer-deps

# Copy the source code to the /app directory
COPY . .

# Build the application
RUN npm run build --  --output-path=dist --configuration=$CONFIGURATION --output-hashing=all


######  Use NgInx alpine image  ###### 
FROM nginx:stable-alpine

# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy nginx config file
COPY sys/portal/default.conf /etc/nginx/nginx.conf

# Copy dist folder fro build stage to nginx public folder
COPY --from=builder /app/dist /usr/share/nginx/html

# Start Nginx service
CMD ["nginx", "-g", "daemon off;"]

From the way I see it, things should be working okay with regards to the build stage since I declare that /app is my working directory and the fact that I have this directory also mapped under the volumes entry in docker-compose.yaml file.

services:
  portal:
    container_name: coolstuff_portal
    image: coolstuffportal:local
    build:
      context: .
      dockerfile: sys/portal/Dockerfile
    ports:
      - 9300:80
    expose:
      - 80
    volumes:
      - ./portal:/app
      - /app/node_modules
    networks:
      - coolstuff-solutions

With all these configurations in place, I am left confused as to what I am getting wrong based on my directory structure.

I'd appreciate pointers in the right direction, thanks.


Solution

  • In the Compose file, you say

    build:
      context: . # relative to the location of docker-compose.yml
    

    and so in the Dockerfile when you say

    COPY package.json .
    

    it looks for the file on the left-hand side in the top-level directory.

    The way your file structure is currently set up, you need the build context directory where it is (you can't COPY from a parent or sibling directory, so it must be a parent or ancestor directory of all of the files that are included. You need to change the two COPY lines to be relative to the parent directory

    COPY portal/package.json ./
    ...
    COPY portal/ ./
    

    The volumes: block isn't used at all during the image build. I'd recommend removing it entirely: the first line replaces absolutely all of the content you put in the Dockerfile, so you're never running your image proper, and the second line takes the node_modules tree from a Docker named volume that needs to be separately maintained. This setup runs Node in Docker more than "Dockerizing your application".