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.