Quản Lý Container với Docker
Giới Thiệu
Trong thế giới phát triển phần mềm hiện đại, Docker đã trở thành một công cụ không thể thiếu, định nghĩa lại cách chúng ta xây dựng, triển khai và quản lý ứng dụng. Docker sử dụng công nghệ containerization để đóng gói ứng dụng cùng với tất cả các thư viện, framework và cấu hình cần thiết vào một đơn vị độc lập, di động, gọi là "container". Điều này giúp loại bỏ vấn đề "nó chạy trên máy của tôi" và đảm bảo ứng dụng hoạt động nhất quán trên mọi môi trường.
Tầm quan trọng và Use Cases:
- Tính nhất quán: Đảm bảo môi trường phát triển, thử nghiệm và sản xuất giống hệt nhau.
- Tính di động: Dễ dàng di chuyển ứng dụng giữa các máy chủ, môi trường đám mây khác nhau.
- Hiệu quả tài nguyên: Container chia sẻ kernel của hệ điều hành host, giúp tiết kiệm tài nguyên hơn máy ảo truyền thống.
- Triển khai nhanh chóng: Khởi động container chỉ trong vài giây, rút ngắn thời gian triển khai.
- CI/CD: Tích hợp hoàn hảo vào quy trình Tích hợp Liên tục/Triển khai Liên tục (CI/CD).
- Microservices: Lý tưởng để triển khai kiến trúc microservices, mỗi service chạy trong một container riêng biệt.
Metadata:
- Thời gian thực hiện: ~2-3 giờ (đối với người mới bắt đầu).
- Độ khó: Trung bình.
- Yêu cầu: Kiến thức cơ bản về dòng lệnh Linux và khái niệm về hệ điều hành.
Yêu Cầu Hệ Thống
Để có trải nghiệm tốt nhất với Docker, hệ thống của bạn cần đáp ứng các yêu cầu sau:
Cấu hình tối thiểu:
- Hệ điều hành: Bất kỳ bản phân phối Linux nào hỗ trợ Docker (Ubuntu 18.04+, CentOS 7+, Fedora 30+), Windows 10 (WSL2) hoặc macOS.
- Kernel Linux: Phiên bản 3.10 trở lên.
- RAM: 2GB.
- Dung lượng đĩa: 20GB trống.
- CPU: Hỗ trợ ảo hóa (enabled trong BIOS/UEFI).
Cấu hình khuyến nghị:
- Hệ điều hành: Bản phân phối Linux mới nhất (Ubuntu LTS, Fedora Workstation), Windows 10 Pro (WSL2) hoặc macOS mới nhất.
- RAM: 8GB trở lên.
- Dung lượng đĩa: SSD 50GB trở lên.
- CPU: 2 lõi trở lên, hỗ trợ ảo hóa.
Các Bước Thực Hiện Chi Tiết
1. Cài Đặt Docker Engine
Chúng ta sẽ hướng dẫn cài đặt Docker trên Ubuntu, một trong những bản phân phối Linux phổ biến nhất.
# ⚙️ Bước 1: Cập nhật chỉ mục gói apt và cài đặt các gói cần thiết
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
# ⚙️ Bước 2: Thêm khóa GPG chính thức của Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# ⚙️ Bước 3: Thiết lập kho lưu trữ Docker
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# ⚙️ Bước 4: Cập nhật lại chỉ mục gói apt sau khi thêm kho lưu trữ Docker
sudo apt update
# ⚙️ Bước 5: Cài đặt Docker Engine, CLI và Containerd
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# ⚙️ Bước 6: Thêm người dùng hiện tại vào nhóm 'docker' để không cần dùng sudo
sudo usermod -aG docker "$USER"
# ⚠️ Quan trọng: Bạn cần đăng xuất và đăng nhập lại (hoặc khởi động lại máy)
# để thay đổi nhóm có hiệu lực.
# Hoặc chạy lệnh sau để áp dụng ngay cho phiên hiện tại (chỉ một số trường hợp):
# newgrp docker
# ⚙️ Bước 7: Xác minh cài đặt Docker
docker run hello-world
✅ Nếu bạn thấy thông báo "Hello from Docker!" thì Docker đã được cài đặt thành công.
2. Khái Niệm Cơ Bản Về Docker
Trước khi đi sâu vào các lệnh, hãy cùng tìm hiểu các khái niệm cốt lõi:
- Image (Ảnh): Một template chỉ đọc chứa hệ điều hành tối thiểu, thư viện, ứng dụng và cấu hình cần thiết để tạo ra một container. Ví dụ:
ubuntu,nginx,mysql. - Container: Một thể hiện đang chạy của một image. Container là một môi trường độc lập, cô lập, nơi ứng dụng của bạn thực thi.
- Registry (Kho chứa): Nơi lưu trữ và phân phối các Docker image. Docker Hub là registry công cộng mặc định.
- Dockerfile: Một tệp văn bản chứa các lệnh để tự động xây dựng một Docker image.
- Volume: Cơ chế để lưu trữ dữ liệu liên tục (persistent data) bên ngoài container, đảm bảo dữ liệu không bị mất khi container bị xóa.
- Network: Docker cung cấp các cơ chế mạng để các container có thể giao tiếp với nhau và với thế giới bên ngoài.
3. Thao Tác Với Docker Images
Images là nền tảng của container.
Tìm kiếm Image
# 🌐 Tìm kiếm image Nginx trên Docker Hub
docker search nginx
Tải Image
# 📥 Tải image Ubuntu phiên bản 22.04 từ Docker Hub
docker pull ubuntu:22.04
# 📥 Tải image Nginx phiên bản stable
docker pull nginx:stable
# 💡 Nếu không chỉ định tag (ví dụ: `ubuntu`), Docker sẽ tải tag `latest` mặc định.
docker pull alpine
Liệt kê Images
# 📊 Liệt kê tất cả các image đã tải về trên máy cục bộ
docker images
# 📊 Liệt kê images theo định dạng cụ thể
docker images --format "{{.ID}}: {{.Repository}} ({{.Tag}}) {{.Size}}"
Xóa Image
# 🗑️ Xóa một image bằng ID hoặc tên:tag
docker rmi alpine
# ⚠️ Bạn không thể xóa một image đang được sử dụng bởi một container đang chạy.
# Nếu có container phụ thuộc, bạn cần xóa chúng trước hoặc dùng cờ -f (force).
# docker rmi -f ubuntu:22.04
4. Thao Tác Với Docker Containers
Container là nơi ứng dụng của bạn chạy.
Chạy Container
# 🚀 Chạy một container từ image `hello-world`
docker run hello-world
# 🚀 Chạy một container từ image `nginx` và hiển thị nó ở chế độ tương tác
docker run -it nginx /bin/bash
# 💡 Lệnh trên sẽ mở một shell bên trong container Nginx.
# Gõ `exit` để thoát khỏi shell và dừng container.
Chạy Container ở Chế Độ Nền (Detached Mode)
# 🌐 Chạy Nginx ở chế độ nền (-d) và ánh xạ cổng 80 của container sang cổng 8080 của host (-p)
docker run -d -p 8080:80 --name my-nginx-app nginx:stable
# 💡 Bây giờ bạn có thể truy cập Nginx qua trình duyệt tại http://localhost:8080
Liệt kê Containers
# 📊 Liệt kê các container đang chạy
docker ps
# 📊 Liệt kê tất cả các container (bao gồm cả những container đã dừng)
docker ps -a
Dừng và Khởi động lại Container
# 🛑 Dừng container có tên 'my-nginx-app'
docker stop my-nginx-app
# 🚀 Khởi động lại container 'my-nginx-app'
docker start my-nginx-app
# 🔄 Dừng và sau đó khởi động lại container 'my-nginx-app'
docker restart my-nginx-app
Thực thi Lệnh Trong Container
# 🔒 Thực thi lệnh `ls -l /` bên trong container 'my-nginx-app'
docker exec my-nginx-app ls -l /
# 🔒 Mở một shell tương tác bên trong container 'my-nginx-app'
docker exec -it my-nginx-app /bin/bash
# (Gõ `exit` để thoát khỏi shell nhưng container vẫn chạy)
Xem Log của Container
# 📜 Xem nhật ký của container 'my-nginx-app'
docker logs my-nginx-app
# 📜 Xem nhật ký và theo dõi liên tục (-f)
docker logs -f my-nginx-app
Xóa Container
# 🗑️ Dừng container trước khi xóa
docker stop my-nginx-app
# 🗑️ Xóa container 'my-nginx-app'
docker rm my-nginx-app
# ⚠️ Để xóa tất cả các container đã dừng:
docker container prune
5. Quản lý Persistent Data (Docker Volumes)
Mặc định, dữ liệu trong container sẽ bị mất khi container bị xóa. Volume giúp lưu trữ dữ liệu bền vững.
Tạo Volume
# 💾 Tạo một Docker volume có tên 'my-data-volume'
docker volume create my-data-volume
# 📊 Liệt kê các volume hiện có
docker volume ls
Sử dụng Volume
# 🚀 Chạy một container Nginx và gắn 'my-data-volume' vào thư mục `/usr/share/nginx/html`
# Dữ liệu bạn đặt vào thư mục này trong container sẽ được lưu trữ trên volume.
docker run -d \
-p 8081:80 \
--name nginx-with-volume \
-v my-data-volume:/usr/share/nginx/html \
nginx:stable
# 💡 Thử tạo một tệp index.html trong volume này
# Đầu tiên, tìm đường dẫn thực của volume trên host:
docker volume inspect my-data-volume
# Output sẽ có trường "Mountpoint". Ví dụ: /var/lib/docker/volumes/my-data-volume/_data
# Sau đó, tạo tệp:
# sudo sh -c "echo '`<h1>`Hello from Docker Volume!</h1>' > /var/lib/docker/volumes/my-data-volume/_data/index.html"
# (Thay thế đường dẫn Mountpoint thực tế của bạn)
# ✅ Truy cập http://localhost:8081 để kiểm tra.
Xóa Volume
# 🗑️ Dừng và xóa container sử dụng volume trước
docker stop nginx-with-volume
docker rm nginx-with-volume
# 🗑️ Xóa volume 'my-data-volume'
docker volume rm my-data-volume
# ⚠️ Để xóa tất cả các volume không được sử dụng bởi bất kỳ container nào:
docker volume prune
6. Quản lý Docker Networks
Docker cung cấp các tùy chọn mạng để container có thể giao tiếp.
Liệt kê Networks
# 🌐 Liệt kê tất cả các Docker network mặc định và tùy chỉnh
docker network ls
Tạo Custom Network
# 🌐 Tạo một bridge network tùy chỉnh có tên 'my-app-network'
docker network create my-app-network
Sử dụng Custom Network
# 🚀 Chạy một container MySQL và kết nối nó vào 'my-app-network'
# Container này sẽ có thể được truy cập bằng tên 'mysql-db' từ các container khác trong cùng network.
docker run -d \
--name mysql-db \
--network my-app-network \
-e MYSQL_ROOT_PASSWORD=mysecretpassword \
mysql:8.0
# 🚀 Chạy một container ứng dụng (ví dụ: Alpine) và kết nối nó vào 'my-app-network'
docker run -it --rm \
--name my-app-client \
--network my-app-network \
alpine /bin/sh
# 💡 Từ bên trong container 'my-app-client', bạn có thể ping 'mysql-db'
# ping mysql-db
# (Gõ `exit` để thoát khỏi container Alpine)
Xóa Network
# 🗑️ Dừng và xóa các container đang sử dụng network trước
docker stop mysql-db
docker rm mysql-db
# 🗑️ Xóa network 'my-app-network'
docker network rm my-app-network
7. Sử Dụng Dockerfile (Xây Dựng Image Tùy Chỉnh)
Dockerfile là công cụ mạnh mẽ để định nghĩa cách xây dựng image của bạn.
Ví dụ Dockerfile đơn giản (Ứng dụng Node.js)
Tạo một thư mục mới, ví dụ my-nodejs-app, và bên trong đó tạo các tệp sau:
app.js:
const http = require('http');
const hostname = '0.0.0.0';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Docker Node.js App!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
package.json:
{
"name": "my-nodejs-app",
"version": "1.0.0",
"description": "A simple Node.js app in Docker",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {}
}
Dockerfile:
# Sử dụng một image nền tảng Node.js chính thức
FROM node:18-alpine
# Đặt thư mục làm việc bên trong container
WORKDIR /app
# Sao chép tệp package.json và package-lock.json (nếu có)
# Điều này giúp tận dụng bộ nhớ cache của Docker khi không có thay đổi
COPY package*.json ./
# Cài đặt các dependencies của ứng dụng
RUN npm install
# Sao chép mã nguồn ứng dụng vào thư mục làm việc
COPY . .
# Mở cổng 3000 để ứng dụng lắng nghe
EXPOSE 3000
# Lệnh để chạy ứng dụng khi container khởi động
CMD ["npm", "start"]
Build Image
Trong thư mục my-nodejs-app, chạy lệnh sau để build image:
# 🏗️ Build Docker image và đặt tên là 'my-nodejs-app' với tag '1.0'
docker build -t my-nodejs-app:1.0 .
# 💡 Dấu chấm (.) ở cuối lệnh chỉ ra rằng Dockerfile nằm trong thư mục hiện tại.
Chạy Container từ Image Tùy Chỉnh
# 🚀 Chạy container từ image 'my-nodejs-app:1.0', ánh xạ cổng 3000 của container sang cổng 4000 của host
docker run -d -p 4000:3000 --name node-app my-nodejs-app:1.0
# ✅ Truy cập http://localhost:4000 trong trình duyệt để kiểm tra ứng dụng của bạn.
Troubleshooting và Các Vấn Đề Thường Gặp
-
permission denied while trying to connect to the Docker daemon socket- Nguyên nhân: Người dùng hiện tại không có quyền truy cập vào socket của Docker daemon.
- Giải pháp: Đảm bảo bạn đã thêm người dùng vào nhóm
dockervà khởi động lại phiên terminal (hoặc đăng xuất/đăng nhập lại).sudo usermod -aG docker "$USER"
# Sau đó, đăng xuất/đăng nhập lại hoặc khởi động lại máy.
-
Container không khởi động hoặc dừng ngay lập tức.
- Nguyên nhân: Lệnh
CMDhoặcENTRYPOINTtrong Dockerfile có lỗi, ứng dụng bên trong container gặp sự cố, hoặc port đã bị chiếm dụng. - Giải pháp:
- Kiểm tra log của container:
docker logs <container_name_or_id> - Kiểm tra trạng thái container:
docker ps -a - Chạy container ở chế độ tương tác để debug:
docker run -it <image_name> /bin/bash
- Kiểm tra log của container:
- Nguyên nhân: Lệnh
-
Port đã bị sử dụng (
port is already allocated).- Nguyên nhân: Cổng bạn cố gắng ánh xạ trên máy host đã được sử dụng bởi một ứng dụng khác hoặc một container Docker khác.
- Giải pháp:
- Kiểm tra xem cổng nào đang mở trên host:
sudo netstat -tulpn | grep <port_number> - Sử dụng một cổng khác cho host:
docker run -p <new_host_port>:<container_port> ... - Dừng và xóa container hoặc ứng dụng đang chiếm dụng cổng.
- Kiểm tra xem cổng nào đang mở trên host:
-
Image không tìm thấy (
image not found).- Nguyên nhân: Tên image hoặc tag không chính xác, hoặc image chưa được tải về.
- Giải pháp:
- Kiểm tra lại chính tả tên image và tag.
- Chạy
docker pull <image_name>:<tag>để tải image. - Chạy
docker imagesđể xem các image hiện có.
-
Docker daemon không chạy (
Cannot connect to the Docker daemon).- Nguyên nhân: Dịch vụ Docker chưa khởi động hoặc đã bị dừng.
- Giải pháp:
sudo systemctl start docker # Khởi động dịch vụ Docker
sudo systemctl enable docker # Đảm bảo Docker khởi động cùng hệ thống
sudo systemctl status docker # Kiểm tra trạng thái dịch vụ
Kết Luận
Qua bài hướng dẫn này, bạn đã làm quen với các khái niệm cơ bản và các lệnh thiết yếu để quản lý Docker container, image, volume và network. Docker không chỉ đơn thuần là một công cụ mà còn là một nền tảng mạnh mẽ giúp đơn giản hóa quy trình phát triển và triển khai ứng dụng, từ đó nâng cao hiệu suất và độ tin cậy.
Best Practices:
- Sử dụng Image nhỏ gọn: Chọn các base image nhỏ như
alpineđể giảm kích thước image, tăng tốc độ build và giảm bề mặt tấn công. - Multi-stage builds: Tận dụng multi-stage builds trong Dockerfile để loại bỏ các công cụ và dependency chỉ cần thiết trong quá trình build, không cần trong runtime.
- Quản lý Volume hiệu quả: Luôn sử dụng Docker Volumes cho dữ liệu cần lưu trữ liên tục để đảm bảo an toàn dữ liệu.
- Tận dụng Docker Networks: Tạo custom networks để cô lập và định tuyến giao tiếp giữa các container một cách rõ ràng và an toàn.
- Giới hạn tài nguyên: Sử dụng các cờ như
--memoryvà--cpu-shareskhi chạy container để giới hạn tài nguyên, tránh một container chiếm dụng quá nhiều tài nguyên của host. - Bảo mật: Luôn sử dụng các image chính thức, quét lỗ hổng bảo mật, và tránh chạy container với quyền root nếu không cần thiết.
Docker là một công nghệ phát triển không ngừng. Việc liên tục học hỏi và cập nhật kiến thức về Docker sẽ giúp bạn tận dụng tối đa sức mạnh của nó trong các dự án của mình.
Tài liệu tham khảo: