Angular Development in Docker - Part 1
June 24, 2020
Angular Development in Docker
We need to create a local working directory four our source code, if your using
WSL, place this in your WSL file system rather than a mnt/c location.
.dockerignore
Create a .dockeringore file in the project root directory.
.git
.gitignore
.vscode
docker-compose*.yml
Dockerfile
node_modules
It’s worth adding any file/folder that you docker image does not require as the ignore file stops the build process from copying the data into your image.
Dockerfile
Next we can create a Dockerfile in the project root directory
# Dockerfile
FROM node:lts
RUN mkdir /home/node/app && chown node:node /home/node/app
RUN mkdir /home/node/app/node_modules && chown node:node /home/node/app/node_modules
WORKDIR /home/node/app
USER node
This downloads the current NodeJs LTS image, we use the full image for a development environment so we have access to git.
The Dockerfile creates our WORKDIR and localtion for node_modules
directory to /srv/app where we will bind our source code and our node_modules
volume.
Finally we set our user to the default node user supplied with the node image
so we don’t run as root. If we don’t set this we will have issues
with npx shortly. We also need to make sure the node user owns the working
directory and node_modules otherwise we will have installation issues.
docker-compose.yml
Now lets create a docker-compose.yml to manage our setup.
# docker-compose
version: '3.7'
services:
app:
build: .
command: echo 'ready'
volumes:
- ./:/home/node/app
- node_modules:/home/node/app/node_modules
volumes:
node_modules:
build: . will build the Dockerfile in the current location when
docker-compose build is run.
command: we will replace this with our call to the development server shortly.
volumes: we are using this to mount our current working directory in to the
container and a volume for node_modules to reside in.
We have two options for building the image, via docker or docker-compose
docker build -t app .
docker-compose build
We will be using the docker-compose from now on as it simplifies the commands.
Now that our bse node image is building, we can leverage node without the need
to install on our local machine. We can use docker-compose to launch an image
that is bound to our source folder and start using it to install node packages.
First we need to start the container and get shell
$ docker-compose run --rm app bash
You will be in the container shell ready to work with npx, to use the
Angular CLI to create a new app we want to move back
a folder and generate a new project.
$ cd ..
$ npx -p @angular/cli ng new app --strict=true
--strict=true - because TypeScript.
If you have a look at your local folder, you will be please to see that you have all the you Angular configuration and a bootstrapped app almost ready to develop with.
node_modules
Now lets look at our Dockerfile, we need to optimise the layer building process.
Lets start by copying our package.json and package-lock.json
# Dockerfile
# ...
COPY --chown=node:node package.json package-lock.json ./
Now we have our package.json and package-lock.json we can populate our
node_modules.
# Dockerfile
# ...
RUN npm ci --quiet
This will populate the node_modules folder as part of our image build, as this
is a layer of our Docker image, if package.json or package-lock.json changes
the layer is rebuilt and node_modules is repopulated. If there are no changes
to package.json or package-lock.json docker uses the cached layer until one
of these files changes.
We use npm ci to install, so our dependencies don’t change underneath us.
Finally we can copy the rest of the source files into the container, we should end up with something like this
# Dockerfile
FROM node:lts
RUN mkdir /home/node/app && chown node:node /home/node/app
RUN mkdir /home/node/app/node_modules && chown node:node /home/node/app/node_modules
WORKDIR /home/node/app
USER node
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --quiet
COPY --chown=node:node . .
ng serve
Now let’s get docker-compose to start our app and expose the correct port
# docker-compose
version: '3.7'
services:
services:
app:
build: .
command: sh -c "npm start"
ports:
- 4200:4200
working_dir: /home/node/app
volumes:
- ./:/home/node/app
- node_modules:/home/node/app/node_modules
volumes:
node_modules:
command: - runs a shell command to start our development server.
We have exposed the Angular CLI port 4200 so when we docker-compose run we
we can go to localhost:4200 and hit the Angular CLI development server.
As we are running inside a docker container we need to bind the Angular CLI
server correctly by detailing the host. Todo this we update package.json
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
The --host 0.0.0.0 binds the server within the docker container so that we
can expose the server port 4200 on our localhost, this means you get your
normal Angular CLI workflow.
Build
Lets use docker-compose to build
$ docker-compose build
Running
To start
$ docker-compose up -d
-d Launches you container in a detached state, we can browse to
localhost:4200 and start developing as we normally would.
To stop the container
$ docker-compose down
In the next part we will look at how we are able to take this starting point and build a distribution of our application before using the complied distribution and running it in a containerised server.