In this tutorial we’ll make docker-compose files for angular and write a simple deploy script to build and deploy the images from your local machine.
Development
Let’s start with the dev environment. First, add .dockerignore
file in the root of your project:
.git
.gitignore
.vscode
docker-compose*.yml
Dockerfile
node_modules
Create .docker
directory in the root of your project. Add dev.dockerfile
:
FROM node:10
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 . .
We are using node 10 image and using a less privileged node
user. npm ci
“is similar to npm install
, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment — or any situation where you want to make sure you’re doing a clean install of your dependencies.” – npm-ci | npm Docs (npmjs.com)
Create docker-compose.yml
file in the root of your project:
# docker-compose
version: '3.7'
services:
services:
app:
container_name: 'your-container-name'
build:
context: .
dockerfile: .docker/dev.dockerfile
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:
With this setup, the node_modules will be overridden when we build a new container. Basically, this means you may have to run docker-compose run app npm install
when you need to update your packages. Rebuilding the image is not going to do it for you.
For alternative setups, check out this stackoverflow answer.
In you package.json
you should have the definition of the npm start command:
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0",
"build": "ng build"
},
Run docker-compose build
and docker-compose up
.
Deployment
Docker Setup
Let’s add production.dockerfile
to .docker
directory:
# Stage 1
FROM node:10 as node
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 . .
# max_old_space_size is optional but can help when you have a lot of modules
RUN node --max_old_space_size=4096 node_modules/.bin/ng build --prod
# Stage 2
# Using a light-weight nginx image
FROM nginx:alpine
COPY --from=node /home/node/app/dist /usr/share/nginx/html
COPY --from=node /home/node/app/.docker/nginx.conf /etc/nginx/conf.d/default.conf
Add docker-compose.production.yml
file:
version: '3.7'
services:
services:
app:
build:
context: .
dockerfile: .docker/production.dockerfile
image: production-image
container_name: production-container
ports:
- 80:80
Deploy script
We are going to ssh into our destination server and copy the updated image directly. Using a repository has a lot of advantages over this approach, but if you need something simple this will work:
#!/bin/sh
# Build the image locally, upload to your production box and start the new container based on the latest image
{
echo "Create an image"
docker-compose -f docker-compose.yml -f docker-compose.production.yml build
echo "Upload the latest image"
echo $(date +"%T")
docker save production-image:latest | ssh -C [email protected]_server_ip docker load
echo "Stop and restart containers"
ssh -C [email protected]_server_ip "echo Stopping container at $(date +'%T'); \
docker stop production-container || true; \
docker rm production-container || true; \
docker container run -d --restart unless-stopped -p 80:80 --name production-container production-image:latest; \
echo Restarted container at $(date +'%T'); \
docker image prune -f || true"
echo "Finished"
echo $(date +"%T")
} || {
# catch
echo "Something went wrong"
}
We are starting a new container based on the latest uploaded image on our destination host and mapping the host port 80 to the container port 80.
Helpful resources: