Virtualization

Hướng dẫn triển khai và quản lý Docker trên VPS Linux

SSH vào một VPS Linux mới, việc đầu tiên bạn thường làm là cài web server, database, rồi code. Làm lại từ đầu mỗi lần đổi máy thật tốn thời gian. Docker giải quyết đúng bài toán đó: bạn đóng gói ứng dụng cùng toàn bộ môi trường chạy của nó vào một container, mang sang bất kỳ VPS nào cũng chạy y hệt. Bài này hướng dẫn chi tiết cài đặt Docker Engine trên Ubuntu 24.04 LTS, quản lý container bằng lệnh, làm việc với volume, network, và vài mẹo bảo mật thực tế.

Yêu cầu trước khi bắt đầu

  • Một VPS Linux chạy Ubuntu 24.04 LTS, đã có kết nối SSH.
  • User có quyền sudo, không dùng root trực tiếp cho thao tác thường ngày.
  • Tối thiểu 1 GB RAM, khuyến nghị 2 GB trở lên nếu chạy nhiều container.
  • Kiến thức cơ bản về dòng lệnh Linux: cd, ls, nano hoặc vim.

Vì sao nên chạy Docker trên VPS

Docker không phải là máy ảo. Nó dùng chung kernel với hệ điều hành máy chủ, nên một container chỉ tốn vài chục MB RAM thay vì vài GB như VM. Với một VPS có 2 GB RAM, bạn có thể chạy thoải mái Nginx, PostgreSQL, Redis và một ứng dụng Node.js cùng lúc mà không lo nghẽn.

Lợi ích lớn nhất với người làm vận hành là tính nhất quán. Ứng dụng chạy đúng trên máy bạn, thì chạy đúng trên VPS. Không còn chuyện "máy mình chạy được mà lên server lại lỗi". Bạn cũng cô lập được từng dịch vụ: một container bị lỗi không kéo sập container khác, và muốn xóa sạch thì chỉ cần xóa container, không để lại file rác trên hệ thống.

Bước 1 - Cài đặt Docker Engine

Trên Ubuntu 24.04, cách chuẩn là cài từ kho lưu trữ chính thức của Docker, không dùng gói docker.io có sẵn trong kho Ubuntu vì thường xưa hơn. Đầu tiên, cập nhật hệ thống và cài các gói cần thiết:

sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release

Thêm khóa GPG chính thức của Docker:

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

Thêm kho lưu trữ Docker vào nguồn apt:

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

Giờ cài Docker Engine, CLI và plugin Compose v2:

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

Kiểm tra:

sudo systemctl status docker
docker --version

Kết quả mong đợi: service ở trạng thái active (running), và lệnh docker --version trả về phiên bản Docker hiện tại (ví dụ Docker version 27.x.x).

Bước 2 - Thêm user vào nhóm docker

Mặc định, chỉ user root mới chạy được lệnh docker. Để user thường chạy mà không cần sudo mỗi lần, thêm user vào nhóm docker:

sudo usermod -aG docker $USER

Đăng xuất rồi đăng nhập lại để nhóm mới có hiệu lực:

exit
# SSH vào lại
docker run hello-world

Lệnh docker run hello-world tải một image nhỏ, chạy thử và in ra thông báo xác nhận Docker hoạt động. Đây là cách kiểm tra nhanh nhất sau khi cài.

Lưu ý an toàn: User trong nhóm docker có toàn quyền root trên VPS về bản chất, vì họ có thể mount toàn bộ ổ đĩa vào container. Chỉ thêm người bạn tin tưởng vào nhóm này.

Bước 3 - Chạy container đầu tiên với Nginx

Thay vì chạy hello-world xong rồi thôi, ta thử một container có ích thật sự: Nginx. Chạy một container Nginx đơn giản:

docker run -d --name web -p 8080:80 nginx:alpine

Giải thích từng phần:

  • -d: chạy nền (detached), không chiếm terminal.
  • --name web: đặt tên cho container là web để dễ quản lý.
  • -p 8080:80: map port 8080 trên VPS vào port 80 trong container.
  • nginx:alpine: image Nginx, bản alpine nhẹ hơn nhiều so với bản thường.

Kiểm tra container đang chạy:

docker ps

Bạn sẽ thấy container web với trạng thái Up. Giờ mở trình duyệt truy cập http://IP_VPS:8080 sẽ thấy trang chào mừng mặc định của Nginx.

Một số lệnh quản lý cơ bản bạn sẽ dùng hằng ngày:

docker ps -a            # xem tất cả container, kể cả đã dừng
docker stop web          # dừng container
docker start web         # khởi động lại container đã dừng
docker restart web       # khởi động lại (stop rồi start)
docker logs -f web       # xem log realtime của container
docker exec -it web sh   # mở shell bên trong container

Lệnh docker exec -it web sh rất hữu ích khi muốn kiểm tra cấu hình bên trong container mà không cần SSH vào VPS. Image nginx:alpine dùng shell sh, không phải bash.

Bước 4 - Làm việc với volume để lưu dữ liệu bền vững

Container là thứ tạm thời. Xóa container là mất toàn bộ dữ liệu bên trong. Muốn dữ liệu sống sót, bạn cần volume, và nó được mount vào một đường dẫn trong container.

Chạy lại Nginx với volume để lưu file web:

docker volume create webdata
docker run -d --name web-data -p 8081:80 \
  -v webdata:/usr/share/nginx/html \
  nginx:alpine

Giờ thử tạo một file trong volume:

echo "Xin chao tu Docker Volume" | docker exec -i web-data tee /usr/share/nginx/html/index.html

Truy cập http://IP_VPS:8081, bạn sẽ thấy dòng chữ vừa tạo. Giờ xóa container và chạy lại với volume cũ:

docker rm -f web-data
docker run -d --name web-data2 -p 8082:80 \
  -v webdata:/usr/share/nginx/html \
  nginx:alpine

Mở lại http://IP_VPS:8082, dữ liệu vẫn còn nguyên. Đó chính là giá trị của volume.

Ngoài named volume, bạn cũng có thể mount một thư mục từ VPS vào container bằng đường dẫn rất cao:

docker run -d -p 8083:80 \
  -v /home/ubuntu/www:/usr/share/nginx/html \
  nginx:alpine

Cách này tiện khi bạn muốn chỉnh sửa file ngay trên VPS bằng editor quen thuộc, file thay đổi thì container thấy ngay.

Bước 5 - Quản lý network giữa các container

Khi chạy nhiều container, chúng cần nói chuyện với nhau. Cách chuyên nghiệp là tạo một mạng riêng, không phơi port ra ngoài.

Tạo một network có tên app-net:

docker network create app-net

Chạy một container PostgreSQL và một container ứng dụng trong cùng network:

docker run -d --name db \
  --network app-net \
  -e POSTGRES_PASSWORD=matkhau_manh \
  -e POSTGRES_DB=myapp \
  postgres:16-alpine

docker run -d --name app \
  --network app-net \
  -p 3000:3000 \
  node:20-alpine sleep infinity

Trong network app-net, container gọi nhau bằng tên. Từ container app, bạn có thể ping hoặc kết nối tới database bằng hostname db:

docker exec -it app sh
# bên trong container app
getent hosts db

Lệnh getent hosts db trả về IP nội bộ của container db. Đây là cách Docker DNS hoạt động: bạn không cần nhớ IP, chỉ cần tên container.

Kiểm tra các network đang có:

docker network ls
docker network inspect app-net

Lệnh docker network inspect cho bạn thấy danh sách container đang gắn vào network đó, rất hữu ích khi debug kết nối.

Bước 6 - Dùng Docker Compose để quản lý nhiều container

Chạy từng lệnh docker run cho mỗi container sẽ nhanh chóng trở nên rối. Docker Compose cho phép bạn định nghĩa toàn bộ hệ thống trong một file compose.yaml, rồi khởi động bằng một lệnh riêng biệt.

Tạo thư mục dự án và file cấu hình:

mkdir ~/myapp && cd ~/myapp
nano compose.yaml

Nội dung file compose.yaml cho một web app đơn giản gồm Nginx, PHP và MariaDB:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - php

  php:
    image: php:8.3-fpm-alpine
    volumes:
      - ./html:/var/www/html

  db:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: matkhau_manh
      MARIADB_DATABASE: myapp
    volumes:
      - dbdata:/var/lib/mysql

volumes:
  dbdata:

Khởi động toàn bộ hệ thống:

docker compose up -d

Chỉ một lệnh, Docker tải image, tạo network riêng, tạo volume, và khởi động cả ba container đúng thứ tự nhờ depends_on.

Các lệnh Compose bạn sẽ dùng thường xuyên:

docker compose ps           # xem trạng thái các service
docker compose logs -f      # xem log tất cả service
docker compose down         # dừng và xóa container, network
docker compose down -v      # dừng và xóa cả volume (CẨN THẬN, mất dữ liệu!)
docker compose pull         # cập nhật image mới
docker compose up -d --build   # xây dựng lại nếu có Dockerfile

Kiểm tra:

docker compose ps

Kết quả: cả ba service web, php, db đều ở trạng thái Up. Truy cập http://IP_VPS:8080 thấy trang Nginx hoạt động.

Xử lý lỗi thường gặp

1. Container chạy rồi nhưng không truy cập được từ ngoài

Kiểm tra port đã map đúng chưa, và firewall trên VPS đã mở port chưa:

docker ps
sudo ss -tlnp | grep -E 'docker|8080'
sudo ufw status

Nếu dùng UFW, mở port cần thiết:

sudo ufw allow 8080/tcp

Với port 80 và 443 cho web server, bạn nên mở luôn từ đầu.

2. Container cứ khởi động lại liên tục (restart loop)

Xem log để biết lý do:

docker logs -f <container_name>

Nguyên nhân thường gặp: ứng dụng bên trong crash do thiếu biến môi trường, hoặc port trong container đã bị chiếm. Kiểm tra port:

docker port <container_name>

3. Hết dung lượng ổ đĩa vì image và container cũ

Dọn dẹp triệt để:

docker system df                # xem dung lượng đang chiếm
docker system prune -a -f       # xóa container, image, network không dùng
docker system prune -a -f --volumes  # xóa luôn cả volume (CẨN THẬN!)

Chạy docker system prune định kỳ hàng tuần là thói quen tốt để VPS không đầy ổ. Bài xử lý VPS bị full disk có thêm nhiều mẹo hữu ích.

Bảo mật Docker trên VPS

Docker mở ra thêm bề mặt tấn công, nên cần vài biện pháp cơ bản:

  • Không chạy container với user root nếu image cho phép. Thêm user: "1000:1000" trong compose.yaml hoặc dùng flag --user.
  • Hạn chế mount thư mục nhạy cảm. Chỉ mount đúng thư mục cần thiết, không mount cả /etc, /var/run/docker.sock vào container trừ khi thật sự cần.
  • Giữ Docker và image luôn mới. Chạy sudo apt update && sudo apt upgrade định kỳ và docker compose pull để lấy bản vá lỗ hổng.
  • Giới hạn tài nguyên. Container không có giới hạn có thể ngốn hết RAM của VPS:
docker run -d -p 8080:80 --memory="512m" --cpus="0.5" nginx:alpine

Lệnh trên giới hạn container dùng tối đa 512 MB RAM và 0.5 CPU. Đây là cách chống một container "chạy loạn" kéo sập cả VPS, rất đáng làm khi chạy VPS chạy n8n hay automation với nhiều workflow.

Khi đã có nhiều container chạy production, bạn nên tính đến chuyện giám sát. Bài giám sát VPS với Prometheus và Grafana hướng dẫn xây dựng hệ thống theo dõi tài nguyên một cách bài bản.

Câu hỏi thường gặp

Docker trên VPS Linux và máy ảo khác nhau thế nào?

Container dùng chung kernel với hệ điều hành máy chủ nên khởi động trong vài giây và tốn ít RAM hơn nhiều. Máy ảo ảo hóa toàn bộ phần cứng, cô lập mạnh hơn nhưng nặng nề và chậm hơn. Trên một VPS, Docker là lựa chọn phù hợp cho hầu hết ứng dụng web.

Có nên chạy Docker trên VPS 1 GB RAM không?

Được, nhưng cần kỷ luật. Chọn image nhẹ như alpine, giới hạn bộ nhớ từng container, và thường xuyên dọn dẹp image không dùng. Cấu hình swap hợp lý cũng giúp VPS ít RAM tránh bị OOM kill, tham khảo bài cấu hình swap cho VPS RAM thấp.

Docker Compose v2 khác gì bản v1 cũ?

Bản v2 là plugin chính thức, lệnh là docker compose (có dấu cách) thay vì docker-compose (có dấu gạch nối). V2 nhanh hơn, tích hợp sẵn khi cài Docker Engine theo hướng dẫn trên, và v1 đã ngừng hỗ trợ từ năm 2023.

Làm sao để xem log và debug container lỗi?

Dùng docker logs -f <tên_container> để xem log realtime. Nếu ứng dụng không khởi động, dùng docker exec -it <tên_container> sh để vào bên trong kiểm tra. Kiểm tra biến môi trường bằng docker inspect <tên_container> xem phần Env.

Backup dữ liệu container bằng cách nào?

Dữ liệu nằm trong volume, nên backup volume là backup dữ liệu. Dùng lệnh docker run --rm -v <tên_volume>:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /data . để nén volume thành file, rồi tải về máy hoặc đẩy lên object storage. Tham khảo thêm bài backup tự động lên S3 bằng Restic.

Cài Docker trên VPS dùng hệ điều hành khác có được không?

Được. Docker hỗ trợ hầu hết distro phổ biến: Debian, CentOS, AlmaLinux, Rocky Linux. Cách cài tương tự, chỉ khác lệnh quản lý gói (dnf thay apt). Nếu bạn dùng VPS Linux của thueVPS, có thể tự chọn OS khi cài đặt, thoải mái chọn bản bạn quen nhất.

Bài viết liên quan

Lưu ý: Bài viết mang tính tham khảo, tổng hợp kiến thức chung. Mỗi hệ thống, hạ tầng và nhu cầu có đặc thù riêng, nên kiểm thử trong môi trường an toàn và tham vấn kỹ sư trước khi triển khai thực tế.