4 minutes
Develop an Angular 9 app locally, using Docker
Prerequisites:
- node (used here v13.9.0)
- docker (used here v19.03.5)
- docker-compose (used here v1.25.4)
Aproximate time to complete this setup: 30 minutes.
Install the Angular CLI globally:
npm i -g @angular/cli
Create a new angular project using the CLI and cd into the project directory:
ng new angular-docker-demo
cd angular-docker-demo
Create the Dockerfile:
# base image
FROM node:13.8.0
# install chrome for protractor tests
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN apt-get update && apt-get install -yq google-chrome-stable
# set working directory
WORKDIR /usr/src/app
# install and cache app dependencies
COPY package.json ./package.json
RUN npm install
# copy the source files to the docker image
COPY . .
# make the host 0.0.0.0 so that the app can
# be accesed outside of the container
CMD npm start -- --host 0.0.0.0
Create the .dockerignore file (so that these files are not copied to the image):
node_modules
.git
.gitignore
Create the docker image (with the name angular-docker-demo:latest) and run it for testing purposes:
docker build -t angular-docker-demo .
docker run --rm -it --mount type=bind,source="${PWD}",target=/usr/src/app --mount type=volume,target=/app/node_modules -p 4201:4200 angular-docker-demo:latest
--rmdeletes the container after we are done running it-itallows us to execute shell comands inside the container (behaving like the terminal on our computer)--mount type=bind,source=”\${PWD}”,target=/usr/src/appmounts the Angular app directory as a bind volume in the docker container, allowing us to change the source code in the container on the fly--mount type=volume,target=/app/node_modulesmounts the node_modules directory in the container to a separate anonymous volume so that it is not overwritten by the node_modules directory binded in the previous mount from our computer-p 4201:4200publish the 4200 port on which the Angular app is served and bind it to the 4201 port on the computer
The app should now be accessible on the computer on the address http://localhost:4201.
Update the karma.conf.js (unit testing config) to use Chrome Headless:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/angular-docker-demo'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
// updated <-- HERE
browsers: ['ChromeHeadless'],
// new <-- HERE
customLaunchers: {
ChromeHeadless: {
base: 'Chrome',
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--remote-debugging-port=9222'],
},
},
singleRun: false,
restartOnFileChange: true,
});
};
Check that the unit testing works (the docker container started earlier must still be running — if it’s not, run the previous docker run command again):
docker exec -it CONTAINER npm run test
where CONTAINER is the container name or id which you can find by running
docker ps
Update the protractor.conf.js (e2e testing config) to use Chrome Headless:
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: ['./src/**/*.e2e-spec.ts'],
capabilities: {
browserName: 'chrome',
// new <-- HERE
chromeOptions: {
args: ['--no-sandbox', '--headless', '--window-size=1024,768'],
},
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {},
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json'),
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
},
};
Check that the e2e testing works (the docker container started earlier must still be running — if it is not, run the previous docker run command again):
docker exec -it CONTAINER npm run e2e -- --port 4202
If everything works, we can now stop the container — either by pressing Ctrl-C in the container terminal or by running the following command:
docker stop CONTAINER
Create the docker-compose.yaml file in the Angular project’s root:
version: "3.7"
services:
web:
container_name: project-name-web
build:
context: .
dockerfile: Dockerfile
volumes:
- type: bind
source: .
target: /usr/src/app
- type: volume
target: /usr/src/app/node_modules
ports:
- 4201:4200
Start docker compose:
docker-compose up
Run tests with:
docker-compose exec web npm run test
docker-compose exec web npm run e2e -- --port 4202
And that concludes the setup. I hope that by now you have a good starting point for your next project.