[Study Notes] Docker

Study notes from udemy course docker-kubernetes-the-practical-guide

Jay Kim
4 min readMar 6, 2022

Useful commands

docker build -t <IMG_NAME>:<TAG> /build/context/path

docker image prune -a to remove all unused images

docker container prune to remove all stopped containers

docker tag <IMG_NAME> <REPO_NAME>:<TAG>

docker login

docker push <REPO_NAME>:<TAG>

docker pull <REPO_NAME>:<TAG>

docker exec -it <CONTAINER_NAME> /bin/sh

Volumes

  • Use bind mounts to connect host machine filesystem
  • Named volumes: persist data
  • Anonymous volumes: useful for saving data which might be overwritten by some setup step

Networking

Container to WWW

Needed when container needs to make API calls which exist on outer world. Out of the box, dockerized containers can send requests to WWW.

Container to Host machine

use host.docker.internal as IP address inside the container

Container to Container

First create network with docker network create network-name.

$ docker network ls

$ docker run -d --name mongodb --network network-name mongo

On the client, use container name of the server as address for DB URL, for example mongodb://mongodb:27017/swfavorites, and run docker run --name favorites --network network-name -d --rm -p 3000:3000 favorites-node

Multi-Container Applications (Development setup)

Project setup:

  • React: live source code update needed
  • Node: Log data must be persisted, live source code update needed
  • Mongo DB: Data must be persisted, access should be limited

Create Network

docker network create goals-net

Mongo DB

Volume is needed for storing data in between the tear down of containers.

docker run --name mongodb -v data:/data/db --rm -d --network goals-net mongo

For security, environment variables can be used.

docker run --name mongodb -v data:/data/db --rm -d --network goals-net mongo -e MONGO_INITDB_ROOT_USERNAME=username -e MONGO_INITDB_ROOT_PASSWORD=password

Then, DB URL becomes mongodb://username:password@mongodb:27017/course-goals?authSource=admin. If ENV variables are used then, use mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@${processs.env.MONGODB_URL}:27017/course-goals?authSource=admin with Javascript backticks.

Node Backend

Make sure mongoose connects to mongodb://mongodb:27017/course-goals instead of mongodb://host.docker.internal:27017/course-goals.

FROM nodeWORKDIR /appCOPY package.json .RUN npm installCOPY . .EXPOSE 80ENV MONGODB_USERNAME=default-username
ENV MONGODB_PASSWORD=default-password
ENV MONGODB_URL=default-url
# CMD ["node", "app.js"] # use nodemon for live source code updateCMD ["nodemon", "app.js"]

docker run --name goals-backend -e MONGODB_USERNAME=username -e MONGODB_PASSWORD=password -rm -d --network goals-net -p 80:80 goals-node

Two volumes are needed for logs (named volume) and live source code update (bind mount). Also, one (anonymous volume) for preventing node_modules to be overwritten if the host machine does not have node_modules directory.

docker run --name goals-backend -e MONGODB_USERNAME=username -e MONGODB_PASSWORD=password -v logs:/app/logs -v ~/backend:/app -v /app/node_modules -rm -d --network goals-net -p 80:80 goals-node

Some directories and files don’t need to be copied into docker image. Use .dockerignore file:

node_modules
Dockerfile
.git

React Frontend

Make sure axios makes requests to http://localhost/goals because React runs on browser not Docker. So Node Backend needs to expose port 80 as well.

FROM nodeWORKDIR /appCOPY package.json .RUN npm installCOPY . .EXPOSE 3000CMD ["npm", "start"]

Dev server requires interactive mode -it.

docker run --name goals-frontend -rm -d -p 3000:3000 -it goals-react

For live source code update, docker run -v ~/frontend/src:/app/src --name goals-frontend -rm -d -p 3000:3000 -it goals-react

Some directories and files don’t need to be copied into docker image. Use .dockerignore file:

node_modules
Dockerfile
.git

Docker Compose

docker-compose up

To run all in detach mode, use -d.

Images can be forced to build with --build.

docker-compose down

To remove volumes, use -v

Deploying Docker Containers (Production setup)

Important:

  • Bind mounts should not be used in production (replace with volumes or COPY)
  • Multiple containers might need multiple hosts
  • Multi-stage builds help with apps that need a build step (ex, React apps)

Deploy to AWS EC2

  1. Create and launch EC2 instance, VPC, and security group
  2. Configure security group to expose required ports to WWW
  3. Install Docker and run container using ssh

By default, EC2 instance is not accessible from the web. So inbound rules inside the security group needs to be configured.

Deploy to AWS ECS

Elastic Container Service is a fully managed remote machines. Creation, management, update are handled automatically and monitoring and scaling are simple.

Mongo DB

On ECS, it is not possible to resolve container name as IP address using docker network. If containers are added in the same task on ECS, then they are guaranteed to run on the same machine. ECS allows localhost to be used as IP because Node backend and Mongo DB are part of the same task.

Multi-Stage Builds for React Frontend

One dockerfile with multiple stages. Stages can copy files from each other.

Build using npm then use nginx, Dockerfile.prod

FROM node:14-alpine as buildWORKDIR /appCOPY package.json .RUN npm installCOPY . .RUN npm run buildFROM nginx:stable-alpineCOPY --from=build /app/build /usr/share/nginx/htmlEXPOSE 80CMD ["nginx", "-g", "daemon off;"]

Frontend app needs to be in a different task because one task with both frontend and backend cannot launch two web servers. Therefore, having frontend in a different task requires backend URLs to be changed.

const backendUrl = process.env.NODE_ENV === 'development' ? 'http://localhost' : 'http://BACKEND_LB_DNS_NAME'axios.get(backendUrl + '/goals')

--

--