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.