LF Docker and Docker-Compose
Docker
Docker is used to deploy apps in a containerized way, as a strategy to avoid a “dependency hell” due to installed packages on developer’s machine, also helps to standardize the developer, testing and production environments for the apps being developed.
Images and containers
A Docker image has all the instructions to run a container (something like object - instance). An image is something static that can be built and a container is a running and disposable instance of that image. As the containers are disposable you have to make sure to preserve any data managed by the container that you might need later, one way to do this is by using Docker volumes which are something like network-drive mounted on the container and residing on the host.
Docker Hub
Docker hub (hub.docker.com) is a repository of images that you can use to run into containers, or to use as base for your own images. Take for example the Postgres image, as you can see there is a “tags section”, every tag is an image version that you can use, so “15.2-alpine”, and “15.2-bullseye” are both Postgres 15.2 installations: “15.2-bullseye” based on Debian Bullseye, and “15.2-alpine” based on Alpine (a very small linux distro very popular on Docker images). On the Postgres image there is also a “How to use” section, where you can find parameters that the container can receive (dbName, password, etc).
Docker-Compose
A docker command can easily become hard to read, and more than one complicated command could be needed to achieve our goal. This is when Docker-Compose comes into play, using at least one YML file with the description of the containers, it builds them if necessary, run them and even creates a network for them.
api prod.Dockerfile
# use node image from Docker hub, tag 16.15.0
FROM node:16.15.0
# RUN and CMD commands will be executed in this directory
WORKDIR /usr/src/app
# COPY from host to container
COPY ./api/package*.json /usr/src/app/
# following command will be executed on build time
RUN npm install
COPY ./api/ /usr/src/app/
COPY ./shared/ /usr/src/shared/
RUN npm install -g nodemon
# CMD is the command that will be executed by default when container is created.
# not to be executed on build time.
CMD npm run migrate:dev:db && nodemon --exec 'npm run start:prod || echo // `date` >> crashlog.js'
webapp prod.Dockerfile
# This file uses a 2 stage build, each stage starts with FROM keyword
# use a Docker hub image: node, with tags: 16.13.2
# we will name this part of the process "build"
FROM node:16.13.2 as build
# RUN and CMD Commands will be run on this container directory
WORKDIR /usr/src/app
# from the host copy the files to DEST=/usr/src/app (on container)
COPY ./webapp/package.json ./webapp/.npmrc ./webapp/pnpm-lock.yaml /usr/src/app/
RUN npm install pnpm -g && pnpm install
COPY ./webapp/ /usr/src/app/
COPY ./shared/ /usr/src/shared/
RUN pnpm run build
# the FROM indicates the start of the second stage
# use nginx image, tag:1.15
FROM nginx:1.15
# the --from=build, is used to get the files from the first stage
COPY --from=build /usr/src/app/dist /var/www/litefarm
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/
# exposed ports
EXPOSE 80 443
beta.docker-compose.yml
# compose version file
version: "3.7"
volumes:
postgres-data:
driver: local
# service declaration, each service is a container
services:
#service name
db:
#name when running "docker ps" command
container_name: litefarm-db
#it uses a docker hub image, tag 13
image: postgres:13
# port mapping, preserving the original
ports:
- "5433:5432"
# this are parameteres that the postgres container recieves
# all specified on the hub documentation for that image.
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_DB: ${POSTGRES_DB}
# nice docker-compose adds
# this will change the status of the container if test fails.
healthcheck:
test: [ "CMD", "pg_isready", "-U", "postgres" ]
interval: 5s
timeout: 5s
retries: 5
# all the data is perserved on this folder.
volumes:
- ~/docker/volumes/postgres:/var/lib/postgresql/data
# backend service, backend will be the name on the network
backend:
# docker ps will list this container name
container_name: litefarm-api
# nice docker-compose adds
# this will restart the backend if it fails, will try 3 times.
deploy:
restart_policy:
condition: on-failure
max_attempts: 3
# not using docker hub file, but building own image
build:
context: ./packages/
dockerfile: ./api/prod.Dockerfile
# volume for letsencrypt ssl certificate
volumes:
- /etc/letsencrypt:/usr/src/letsencrypt
# port mapping to same port.
ports:
- "5000:5000"
environment:
# check how it is using container name
WAIT_HOSTS: litefarm-db:5432
NODE_ENV: integration
DEV_DATABASE_HOST: ${DEV_DATABASE_HOST}
DEV_DATABASE: ${DEV_DATABASE}
DEV_DATABASE_USER: ${DEV_DATABASE_USER}
DEV_DATABASE_PASSWORD: ${DEV_DATABASE_PASSWORD}
frontend:
container_name: litefarm-web
build:
context: ./packages/
dockerfile: ./webapp/prod.Dockerfile
volumes:
- /etc/letsencrypt:/etc/letsencrypt
ports:
- "80:80"
- "443:443"
# no netwok declared, but a network has been created for the 3 services.