[Study Notes] Docker
Study notes from udemy course docker-kubernetes-the-practical-guide
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
- Create and launch EC2 instance, VPC, and security group
- Configure security group to expose required ports to WWW
- 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')