Docker cho người mới: Từ zero đến container

Học Docker từ đầu: containers là gì, cài đặt Docker, Dockerfile, images, và commands cơ bản cho DevOps engineers.

Container là gì?

Container là một đơn vị phần mềm đóng gói code và tất cả dependencies để ứng dụng chạy nhất quán trên mọi môi trường.

Container vs Virtual Machine

┌──────────────────────────────────────────────────────────┐
│            Container                  Virtual Machine    │
├──────────────────────────────────────────────────────────┤
│  ┌─────┐ ┌─────┐ ┌─────┐    ┌─────┐ ┌─────┐ ┌─────┐    │
│  │App A│ │App B│ │App C│    │App A│ │App B│ │App C│    │
│  ├─────┤ ├─────┤ ├─────┤    ├─────┤ ├─────┤ ├─────┤    │
│  │Bins │ │Bins │ │Bins │    │Bins │ │Bins │ │Bins │    │
│  └─────┴─┴─────┴─┴─────┘    ├─────┤ ├─────┤ ├─────┤    │
│  ┌─────────────────────┐    │Guest│ │Guest│ │Guest│    │
│  │   Container Engine  │    │ OS  │ │ OS  │ │ OS  │    │
│  │      (Docker)       │    └─────┴─┴─────┴─┴─────┘    │
│  ├─────────────────────┤    ┌─────────────────────┐    │
│  │     Host OS         │    │     Hypervisor      │    │
│  ├─────────────────────┤    ├─────────────────────┤    │
│  │    Infrastructure   │    │     Host OS         │    │
│  └─────────────────────┘    ├─────────────────────┤    │
│                             │    Infrastructure   │    │
│                             └─────────────────────┘    │
│                                                         │
│  Lightweight, Fast          Heavy, Slower               │
│  Share Host OS kernel       Full OS per VM              │
│  MB in size                 GB in size                  │
│  Seconds to start           Minutes to start            │
└──────────────────────────────────────────────────────────┘

Tại sao DevOps cần Docker?

Vấn đềDocker giải quyết
”Works on my machine”Môi trường nhất quán
Dependency conflictsIsolated environments
Slow deploymentsFast, lightweight containers
Manual setupAutomated, reproducible

Cài đặt Docker

macOS

  1. Tải Docker Desktop
  2. Kéo vào Applications
  3. Mở Docker Desktop và chờ khởi động

Linux (Ubuntu)

# Update packages
sudo apt update

# Install prerequisites
sudo apt install -y ca-certificates curl gnupg

# Add Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Add user to docker group
sudo usermod -aG docker $USER
# Logout và login lại

# Verify
docker --version
docker compose version

Windows

  1. Enable WSL 2
  2. Tải Docker Desktop
  3. Cài đặt và restart

Verify Installation

# Check version
docker --version
# Docker version 24.0.7, build afdd53b

# Run test container
docker run hello-world

Docker Architecture

┌─────────────────────────────────────────────────────────┐
│                    Docker Client                        │
│                   (docker CLI)                          │
└────────────────────────┬────────────────────────────────┘
                         │ REST API

┌─────────────────────────────────────────────────────────┐
│                   Docker Daemon                         │
│                    (dockerd)                            │
├─────────────────────────────────────────────────────────┤
│  ┌───────────┐  ┌───────────┐  ┌───────────────────┐   │
│  │  Images   │  │ Containers│  │    Networks       │   │
│  └───────────┘  └───────────┘  └───────────────────┘   │
│  ┌───────────┐  ┌───────────────────────────────────┐  │
│  │  Volumes  │  │      Docker Registry              │  │
│  └───────────┘  │      (Docker Hub, ECR)            │  │
│                 └───────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

Key Concepts

ConceptMô tả
ImageTemplate read-only để tạo containers
ContainerInstance đang chạy của image
DockerfileScript để build image
RegistryNơi lưu trữ images (Docker Hub, ECR)
VolumePersistent storage
NetworkContainer networking

Docker Commands Cơ bản

Images

# List images
docker images

# Pull image từ registry
docker pull nginx
docker pull nginx:1.25    # Specific version

# Remove image
docker rmi nginx

# Search images
docker search nginx

Containers

# Run container
docker run nginx                    # Foreground
docker run -d nginx                 # Detached (background)
docker run -d -p 8080:80 nginx      # Port mapping
docker run -d --name web nginx      # Named container

# List containers
docker ps                           # Running
docker ps -a                        # All (including stopped)

# Stop/Start container
docker stop web
docker start web
docker restart web

# Remove container
docker rm web
docker rm -f web     # Force remove running container

# View logs
docker logs web
docker logs -f web   # Follow logs

# Execute command in container
docker exec -it web bash
docker exec web ls -la

Practical Examples

# Run Nginx web server
docker run -d -p 8080:80 --name nginx-server nginx
# Access: http://localhost:8080

# Run MySQL database
docker run -d \
  --name mysql-db \
  -e MYSQL_ROOT_PASSWORD=secret \
  -p 3306:3306 \
  mysql:8

# Run Node.js app
docker run -d \
  --name node-app \
  -p 3000:3000 \
  -v $(pwd):/app \
  node:20-alpine \
  node /app/index.js

Dockerfile

Dockerfile là “recipe” để build Docker image.

Cấu trúc cơ bản

# Dockerfile

# Base image
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files first (for caching)
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY . .

# Expose port
EXPOSE 3000

# Default command
CMD ["node", "server.js"]

Dockerfile Instructions

InstructionMô tả
FROMBase image
WORKDIRSet working directory
COPYCopy files vào image
ADDCopy + extract (tar, URL)
RUNExecute command khi build
CMDDefault command khi run
ENTRYPOINTMain executable
EXPOSEDocument exposed port
ENVSet environment variable
ARGBuild-time variable
VOLUMECreate mount point
USERSet user to run commands
HEALTHCHECKContainer health check

Build Image

# Build image
docker build -t my-app .
docker build -t my-app:v1.0 .

# Build với Dockerfile khác tên
docker build -f Dockerfile.prod -t my-app:prod .

# Build với build args
docker build --build-arg VERSION=1.0 -t my-app .

Multi-stage Builds

Giảm image size bằng cách tách build và runtime:

# Stage 1: Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]

Before vs After Multi-stage

Single-stage:  1.2GB (includes dev dependencies, source)
Multi-stage:   150MB (only runtime)

Docker Volumes

Volumes lưu data persistent, không bị mất khi container bị xóa.

# Create volume
docker volume create my-data

# List volumes
docker volume ls

# Use volume
docker run -d \
  --name db \
  -v my-data:/var/lib/mysql \
  mysql:8

# Bind mount (map host directory)
docker run -d \
  --name web \
  -v $(pwd)/html:/usr/share/nginx/html \
  nginx

# Remove volume
docker volume rm my-data

# Remove unused volumes
docker volume prune

Volume Types

┌─────────────────────────────────────────────────────────┐
│                    Volume Types                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Named Volumes        Bind Mounts        tmpfs          │
│  ┌────────────┐      ┌────────────┐     ┌──────────┐   │
│  │ Docker     │      │ Host       │     │ Memory   │   │
│  │ manages    │      │ filesystem │     │ only     │   │
│  └────────────┘      └────────────┘     └──────────┘   │
│                                                         │
│  -v data:/app       -v /host:/app      --tmpfs /app    │
│  Best for DBs       Dev workflows      Sensitive data  │
└─────────────────────────────────────────────────────────┘

Docker Networking

# List networks
docker network ls

# Create network
docker network create my-network

# Run containers on same network
docker run -d --name app --network my-network my-app
docker run -d --name db --network my-network mysql

# Containers có thể communicate bằng name
# app container: mysql://db:3306

Network Types

TypeMô tả
bridgeDefault, isolated network
hostShare host’s network
noneNo networking
overlayMulti-host (Swarm)

Thực hành: Dockerize Node.js App

Project Structure

my-node-app/
├── Dockerfile
├── .dockerignore
├── package.json
├── server.js
└── src/
    └── index.js

1. Create Application

// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Docker!', timestamp: new Date() });
});

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'healthy' });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
// package.json
{
  "name": "my-node-app",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}

2. Create Dockerfile

# Dockerfile
FROM node:20-alpine

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source
COPY . .

# Change ownership
RUN chown -R appuser:appgroup /app

# Switch to non-root user
USER appuser

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# Start command
CMD ["npm", "start"]

3. Create .dockerignore

# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
.env

4. Build and Run

# Build image
docker build -t my-node-app:v1 .

# Run container
docker run -d \
  --name my-app \
  -p 3000:3000 \
  my-node-app:v1

# Test
curl http://localhost:3000

# View logs
docker logs my-app

# Check health
docker inspect --format='{{.State.Health.Status}}' my-app

Docker Best Practices

1. Use Specific Tags

# Bad
FROM node

# Good
FROM node:20-alpine

2. Non-root User

RUN addgroup -S app && adduser -S app -G app
USER app

3. Layer Caching

# Dependencies change less often
COPY package*.json ./
RUN npm ci

# Source changes often
COPY . .

4. Small Base Images

node:20        ~1GB
node:20-slim   ~200MB
node:20-alpine ~150MB

5. .dockerignore

node_modules
.git
*.log
.env

Cleanup Commands

# Remove all stopped containers
docker container prune

# Remove unused images
docker image prune
docker image prune -a   # Including unused

# Remove unused volumes
docker volume prune

# Remove all unused resources
docker system prune -a

# View disk usage
docker system df

Bước tiếp theo

  1. ✅ Hiểu containers và Docker architecture
  2. ✅ Viết Dockerfile
  3. ✅ Build và run containers
  4. 📖 Học Docker Compose cho multi-container apps
  5. 🚀 Push images lên registry

💡 Pro tip: Luôn sử dụng .dockerignore và multi-stage builds để giảm image size!

Bài tiếp theo: Docker Compose - Orchestrate multi-container applications.