Skip to content

[DevOps Series] Part 3: Docker and the world of containerization

Lilhuy

📚 Series Table of Contents

  1. 📖 Chapter 0: Introduction and Stories
  2. 📚 Chapter 1: Some concepts and terminologies
  3. 🚀 Chapter 2: A noob guy deploy his web app
  4. 🐳 Chapter 3: Docker and the world of containerization (You are here) 🎯
  5. ☸️ Chapter 4: K8s in a nutshell ⚙️
  6. 🔧 Chapter 5: K8s in details 🛠️
  7. 🏠 Chapter 6: Before go to the ground 🏡
  8. 🐧 Chapter 7: Ubuntu server and the world of Linux 🖥️
  9. Chapter 8: MicroK8s the simple and powerful K8s ⚙️
  10. ☁️ Chapter 9: Harvester HCI the native cloud 🌐
  11. 🏭 Chapter 10: More about Harvester HCI 🏢
  12. 🖥️ Chapter 11: Promox VE the best VM manager 💾
  13. 🌐 Chapter 12: Turn a server into a router with Pfsense 🔌
  14. 🛠️ Chapter 13: Some tools, services that you can installed for your devops pipeline 🔧
  15. 🌍 Chapter 14: Hello Internet with Cloudflare Zero Trust 🔒
  16. 🎉 Chapter 15: Maybe it the end of the series 🏁

For me, Docker is like a “Hello, World” to the DevOps world. At first glance, it’s hard to understand because it introduces many new concepts. In this chapter, I’ll simplify Docker for you. Let’s start!

Why we need containerization?

This is the reason

It works on my machine

Yes, but not every time. Apps that work on your machine might not work on mine. Modern applications have many dependencies, libraries, and other components… It works on my machine

Create your first container using Docker

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
sudo docker run hello-world # Test if Docker is installed correctlybash

By default, the Docker daemon runs as root, so remember to add sudo before every Docker command.

mkdir docker-nodejs-backend
cd docker-nodejs-backend
npm init -y
npm install expressbash
const express = require('express');
const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});index.js
FROM node:20-alpine # Base image - Alpine Linux with Node.js 20
WORKDIR /app # Working directory (like mkdir -p /app && cd /app)
COPY package.json . # Copy package.json to container
COPY index.js . # Copy index.js to container
RUN npm install # Install dependencies
EXPOSE 3000 # Expose port 3000 (optional but recommended)
CMD ["node", "index.js"] # Command to run your appDockerfile
sudo docker build -t docker-nodejs-backend .
sudo docker run -p 3000:3000 docker-nodejs-backend

That’s it! Now you can add “Docker Master” to your CV. :))))

My confession: I now always ask AI to write Dockerfiles for me. :))))

Dive into Docker

Docker command cheat sheet

Essential Docker Commands

Docker compose for an easier life

version: '3.8'

services:
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

  database:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    volumes:
      - ./backend:/app
    depends_on:
      - redis
      - database
    environment:
      DATABASE_URL: postgresql://user:password@database:5432/mydb
      REDIS_URL: redis://redis:6379
    networks:
      - app-network

  frontend:
    build: ./frontend
    ports:
      - "8080:80"
    depends_on:
      - backend
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db-data:docker-compose.yaml

Advanced Concepts

Layer Caching

Each Dockerfile instruction creates a cached layer. I copy package.json and run npm install before copying source code because if only source code changes (not dependencies), Docker won’t reinstall packages, saving build time.

Multi-stage Builds

Optimize image size by using multiple FROM instructions. For example, build a React app in one stage, then copy the built files to a smaller nginx image for production.

Best Practices

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app

# Copy package files first for layer caching
COPY package.json package-lock.json ./
RUN npm ci

# Copy source code and build
COPY . .
RUN npm run build

# Runtime stage
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Dockerfile

Quick Summary

What we covered:

Conclusion

This blog covers Docker basics and common features. Docker has many advanced features not covered here - check the Docker documentation for more. While Docker introduces many new concepts, it’s not hard once you understand the fundamentals. The best way to learn is to install Docker and practice, not just read blogs!


📚 Series Navigation

Previous ChapterSeries InfoNext Chapter
← Previous Chapter
🚀 A noob guy deploy his web app
DevOps Series
Chapter 3 of 16
Next Chapter →
☸️ K8s in a nutshell
Edit this post
Previous
[DevOps Series] Part 4: K8s in a nutshell
Next
[DevOps Series] Part 2: A Noob Guy Deploys His Web App