Skip to content

Container hóa Docker

Lời nói đầu

"Chạy được trên máy tôi" là lời bào điển kinh điển nhất của lập trình viên, và Docker khiến lời bào chữa này biến mất hoàn toàn. Công nghệ container đóng gói ứng dụng cùng tất cả các phụ thuộc thành một đơn vị tiêu chuẩn hóa, đảm bảo chạy nhất quán trên mọi môi trường. Đây là nền tảng của phân phối phần mềm hiện đại.

Bài viết này sẽ giúp bạn học được gì?

Sau khi học xong chương này, bạn sẽ có được:

  • Khái niệm cốt lõi: hiểu ba khái niệm nền tảng: image, container, registry
  • So sánh kiến trúc: hiểu sự khác biệt bản chất giữa container và máy ảo
  • Kỹ năng thực hành: nắm vững cách viết Dockerfile và các lệnh thường dùng
  • Cơ sở编排: học cách quản lý ứng dụng đa dịch vụ với Docker Compose
  • Thực hành tốt nhất: biết tối ưu image, tăng cường bảo mật và các thực hành cấp sản xuất
ChươngNội dungKhái niệm cốt lõi
Chương 1Tại sao cần containerNhất quán môi trường, hiệu quả tài nguyên, phân phối tiêu chuẩn hóa
Chương 2Khái niệm cốt lõiImage, container, registry, Dockerfile
Chương 3Vòng đời DockerViết, build, push, chạy, quản lý
Chương 4Docker ComposeOrchestration đa dịch vụ, mạng, volume dữ liệu
Chương 5Thực hành tốt nhấtTối ưu image, bảo mật, build đa giai đoạn

1. Tại sao cần container?

Trước khi container xuất hiện, việc triển khai ứng dụng cần cài đặt thủ công runtime, cấu hình biến môi trường, xử lý xung đột phụ thuộc trên máy chủ. Sự khác biệt giữa các môi trường (phát triển, kiểm thử, sản xuất) là mảnh đất màu mỡ cho bug.

Virtual Machines vs Containers
Switch between both virtualization models to compare their architecture
App A / App B / App C
App A + Bins/Libs
App B + Bins/Libs
App C + Bins/Libs
Docker Engine
Host OS
Physical hardware
Startup speedSeconds
Resource usageShared host OS kernel (MB scale)
IsolationProcess-level isolation
DensityHundreds of containers per host
Image sizeMB scale

Container giải quyết vấn đề gì?

Vấn đềCách truyền thốngCách dùng container
Môi trường không nhất quán"Chạy được trên máy tôi"Đóng gói tất cả phụ thuộc, nhất quán mọi nơi
Xung đột phụ thuộcApp A cần Node 14, App B cần Node 18Mỗi container có môi trường riêng
Lãng phí tài nguyênMỗi VM cần một hệ điều hành đầy đủChia sẻ kernel, chi phí ở mức MB
Triển khai chậmCài đặt và cấu hình thủ côngMột lệnh docker run
Khó mở rộngTạo VM mới, cài môi trường, triển khaiKhởi động container mới trong vài giây

Bản chất của container

Container không phải là máy ảo nhẹ. Bản chất của nó là một tiến trình bị cách ly. Kernel Linux thực hiện container thông qua hai cơ chế:

  • Namespace: cách ly tầm nhìn của tiến trình (PID, mạng, hệ thống tệp, v.v.)
  • Cgroups: giới hạn sử dụng tài nguyên của tiến trình (CPU, bộ nhớ, IO)

Tiến trình trong container về bản chất không khác gì tiến trình bình thường trên máy chủ, chỉ là bị "nhốt trong một căn phòng không nhìn thấy bên ngoài".


2. Khái niệm cốt lõi

Thế giới Docker xoay quanh ba khái niệm nền tảng: Image (Ảnh镜像), Container (容器), và Registry (Kho).

Khái niệmSo sánhMô tả
ImageLớp / MẫuMẫu chỉ đọc, chứa mã, runtime, thư viện, cấu hình
ContainerThể hiện / Đối tượngThể hiện đang chạy của image, có thể đọc ghi, có vòng đời độc lập
RegistryCửa hàng ứng dụngDịch vụ lưu trữ và phân phối image (Docker Hub, ACR, ECR)
DockerfileCông thức / Bản thiết kếTệp văn bản định nghĩa cách build image
VolumeỔ cứng ngoàiDữ liệu bền vững, khi xóa container dữ liệu không mất

Cấu trúc phân tầng của image

Image Docker được tạo thành từ nhiều tầng chỉ đọc (Layer), mỗi chỉ thị trong Dockerfile tạo ra một tầng:

┌─────────────────────────┐
│  CMD ["node", "app.js"] │  ← Tầng lệnh khởi động
├─────────────────────────┤
│  COPY . /app            │  ← Tầng mã ứng dụng (thường thay đổi)
├─────────────────────────┤
│  RUN npm install        │  ← Tầng cài đặt phụ thuộc (thỉnh thoảng thay đổi)
├─────────────────────────┤
│  FROM node:18-alpine    │  ← Tầng image cơ sở (hiếm khi thay đổi)
└─────────────────────────┘

Tại sao phân tầng quan trọng?

Docker lưu đệm mỗi tầng. Nếu một tầng không thay đổi, build sẽ tái sử dụng đệm. Vì vậy trong Dockerfile nên đặt các chỉ thị ít thay đổi ở phía trước (như cài đặt phụ thuộc), các chỉ thị hay thay đổi ở phía sau (như sao chép mã). Như vậy hầu hết các lần build sẽ trúng đệm, tốc độ nhanh hơn nhiều.


3. Vòng đời Docker

Từ viết Dockerfile đến chạy container, quy trình làm việc của Docker là một đường dây sản xuất rõ ràng.

Docker Lifecycle
Click each stage to inspect the details
📝
Write a Dockerfile
🔨
Build the image
☁️
Push to registry
▶️
Run the container
⚙️
Manage containers
Write a Dockerfile
A Dockerfile is the recipe for building an image. It starts from a base image and defines how to assemble the application environment step by step. Each instruction creates an image layer that Docker can cache for later builds.
Common commands
FROM node:18-alpineChoose the base image
WORKDIR /appSet the working directory
COPY package*.json ./Copy dependency files for cache reuse
RUN npm installInstall dependencies
COPY . .Copy application code
EXPOSE 3000Declare the port
CMD ["node", "server.js"]Start command

Tra cứu nhanh các chỉ thị Dockerfile

Chỉ thịTác dụngVí dụ
FROMChỉ định image cơ sởFROM node:18-alpine
WORKDIRThiết lập thư mục làm việcWORKDIR /app
COPYSao chép tệp vào imageCOPY package.json ./
RUNThực thi lệnh khi buildRUN npm install
ENVThiết lập biến môi trườngENV NODE_ENV=production
EXPOSEKhai báo cổng (chỉ dùng tài liệu)EXPOSE 3000
CMDLệnh khởi động containerCMD ["node", "app.js"]
ENTRYPOINTĐiểm vào container (khó ghi đè)ENTRYPOINT ["nginx"]

4. Docker Compose: Orchestration đa dịch vụ

Dự án thực tế thường không chỉ có một container. Một ứng dụng web có thể cần: máy chủ ứng dụng + cơ sở dữ liệu + Redis + Nginx. Docker Compose định nghĩa và quản lý nhiều container bằng một tệp YAML.

Ví dụ docker-compose.yml

yaml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret

  redis:
    image: redis:7-alpine

volumes:
  db-data:

Các khái niệm cốt lõi của Compose

Khái niệmMô tảVí dụ
servicesĐịnh nghĩa các dịch vụ containerapp, db, redis
volumesVolume dữ liệu bền vữngdb-data lưu tệp cơ sở dữ liệu
networksMạng tùy chỉnh (tự động tạo mặc định)Các dịch vụ truy cập lẫn nhau qua tên dịch vụ
depends_onPhụ thuộc thứ tự khởi độngapp phụ thuộc db và redis
environmentBiến môi trườngMật khẩu cơ sở dữ liệu, địa chỉ kết nối

Khám phá dịch vụ

Trong Docker Compose, tên dịch vụ chính là tên máy chủ. Container app có thể truy cập trực tiếp cơ sở dữ liệu bằng db:5432 và Redis bằng redis:6379, không cần biết địa chỉ IP. Đây là nhờ DNS tích hợp của Docker.


5. Thực hành tốt nhất

5.1 Build đa giai đoạn (Multi-stage Build)

Build đa giai đoạn là công cụ mạnh mẽ để tối ưu kích thước image. Giai đoạn build cài đặt tất cả công cụ và phụ thuộc, giai đoạn cuối chỉ giữ lại các tệp cần thiết khi chạy.

dockerfile
# Giai đoạn build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Giai đoạn chạy
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

5.2 Danh sách tối ưu image

Mục tối ưuCách làmHiệu quả
Chọn image cơ sở nhỏDùng alpine thay vì ubuntuImage từ ~200MB giảm xuống ~50MB
Gộp chỉ thị RUNNối nhiều lệnh bằng &&Giảm số tầng image
Dùng .dockerignoreLoại trừ node_modules, .git, v.v.Tăng tốc build, giảm ngữ cảnh
Build đa giai đoạnTách môi trường build và chạyImage cuối không chứa công cụ build
Cố định số phiên bảnnode:18.17-alpine thay vì node:latestBuild có thể tái lập

5.3 Thực hành bảo mật

Thực hànhMô tả
Không chạy bằng rootUSER node chỉ định người dùng không phải root
Quét lỗ hổngdocker scout hoặc Trivy quét image
Quyền tối thiểuChỉ cài các gói cần thiết, không cài công cụ debug
Không hardcode bí mậtDùng biến môi trường hoặc Docker Secrets
Cập nhật image cơ sở thường xuyênSửa lỗ hổng bảo mật kịp thời

Tóm tắt

Container hóa Docker là hạ tầng của phân phối phần mềm hiện đại, hiểu nó là cực kỳ quan trọng với mọi lập trình viên.

Ôn lại các điểm chính của chương này:

  1. Container vs máy ảo: container chia sẻ kernel máy chủ, nhẹ hơn, nhanh hơn, nhưng cách ly hơi kém hơn VM
  2. Bộ ba cốt lõi: image (mẫu), container (thể hiện), registry (phân phối)
  3. Dockerfile: build phân tầng, tận dụng đệm, chỉ thị ít thay đổi đặt phía trước
  4. Docker Compose: định nghĩa ứng dụng đa dịch vụ bằng YAML, tên dịch vụ chính là tên máy chủ
  5. Thực hành sản xuất: build đa giai đoạn giảm kích thước, image cơ sở alpine, chạy bằng user không phải root

Đọc thêm