Chuyển đến nội dung chính

Docker Mount: Giải thích Volumes, Bind Mounts và tmpfs

Docker mounts kết nối container với bộ nhớ bền vững qua ba loại – volumes cho dữ liệu sản xuất, bind mounts cho quy trình phát triển, và tmpfs cho tệp tạm thời.
· 15 phút đọc

Khám phá cùng AI

Bạn vừa dừng container Docker để cập nhật một tệp cấu hình, nhưng khi khởi động lại, toàn bộ dữ liệu đều biến mất. Tôi cũng từng như vậy.

Điều này xảy ra vì container mặc định là ngắn hạn (ephemeral) – khi chúng bị xóa, mọi dữ liệu lưu trong lớp ghi (writable layer) của chúng sẽ biến mất. Ứng dụng thực tế không thể vận hành như vậy. Bạn cần cơ sở dữ liệu tồn tại lâu dài, tệp cấu hình không mất sau khi khởi động lại, và log có thể truy cập được.

Docker mount giải quyết vấn đề này bằng cách kết nối lưu trữ của container với vị trí bên ngoài. Có ba loại: volumes dành cho dữ liệu sản xuất, bind mounts cho quy trình phát triển, và tmpfs cho tệp tạm thời lưu trên bộ nhớ RAM.

Trong bài viết này, tôi sẽ hướng dẫn bạn cách chọn loại mount phù hợp với tình huống sử dụng và triển khai đúng cách. Để hiểu hết nội dung, bạn cần nắm vững Docker và containerization ở mức khá. Hãy học khóa Intermediate Docker của chúng tôi để bắt kịp nhanh.

Docker xử lý lưu trữ như thế nào

Container Docker dùng hệ thống tệp phân lớp (layered filesystem) và mặc định coi mọi thứ là tạm thời.

Khi bạn build một image Docker, mỗi chỉ thị trong Dockerfile tạo ra một lớp chỉ đọc mới. Các lớp này xếp chồng lên nhau như một bộ bài. Nếu bạn kéo một image Ubuntu có cài Python, bạn sẽ có một lớp cho hệ điều hành nền tảng và một lớp khác cho Python.

Điểm then chốt là tất cả các lớp này đều chỉ đọc. Bạn không thể chỉnh sửa chúng.

Hệ thống tệp của container và lớp ghi

Khi bạn khởi động một container, Docker thêm một lớp nữa lên trên cùng - lớp ghi của container (writable container layer).

Mọi thay đổi diễn ra tại đây. Mỗi tệp bạn tạo, cấu hình bạn sửa, bản ghi bạn thêm đều nằm ở đây.

Nghe có vẻ ổn, nhưng vấn đề là lớp này gắn với vòng đời của container.

Khi bạn dừng và xóa container bằng docker rm, lớp ghi biến mất. Mọi thứ bạn đã làm cũng biến mất. Docker sẽ không hỏi xác nhận. Nó chỉ xóa tất cả.

Thiết kế này hợp lý cho các ứng dụng stateless không cần ghi nhớ gì giữa các lần chạy. Nhưng ứng dụng thực tế không phải stateless.

Vì sao mounts là cần thiết

Lớp ghi có hai vấn đề lớn trong môi trường sản xuất.

Thứ nhất, bạn sẽ mất dữ liệu khi container dừng. Một container cơ sở dữ liệu mà quên hết mọi dòng dữ liệu sau khi khởi động lại thì có ích gì? Tôi chỉ nghĩ ra mỗi trường hợp chạy kiểm thử tích hợp cho ứng dụng của bạn.

Thứ hai, bạn không thể chia sẻ dữ liệu giữa các container. Giả sử bạn chạy một web app và một worker nền, cả hai đều cần truy cập cùng một bộ tệp. Nếu các tệp đó nằm trong lớp ghi của một container, container kia sẽ không thấy được.

Docker mounts giải quyết cả hai vấn đề bằng cách kết nối container với bộ nhớ tồn tại bên ngoài vòng đời container. Bạn có thể mount một thư mục từ máy chủ (host) hoặc một volume do Docker quản lý. Dữ liệu của bạn vẫn tồn tại ngay cả khi container bị xóa. Nhiều container có thể mount cùng một vị trí và chia sẻ tệp theo thời gian thực.

Đó là lý do bạn cần dùng mounts. Tiếp theo hãy điểm qua các loại mount, rồi tôi sẽ minh họa cách chúng hoạt động.

Tổng quan các loại mount trong Docker

Docker cung cấp ba cách xử lý dữ liệu bền vững, mỗi cách giải quyết các vấn đề khác nhau. Dưới đây là chức năng và thời điểm nên dùng từng loại.

Volumes

Volumes là câu trả lời mặc định của Docker cho lưu trữ bền vững.

Docker tạo và quản lý volumes cho bạn trong một thư mục chuyên dụng trên máy host. Bạn không cần biết chính xác thư mục đó ở đâu, mọi thứ đều do Docker xử lý. Điều này giúp volumes có tính di động giữa các hệ thống và an toàn cho môi trường sản xuất.

Khi bạn xóa một container, volume vẫn nguyên vẹn. Nếu bạn khởi động một container mới và gắn cùng volume đó, toàn bộ dữ liệu của bạn vẫn ở đúng vị trí.

Volumes phù hợp nhất cho cơ sở dữ liệu sản xuất, trạng thái ứng dụng và mọi dữ liệu bạn không thể để mất.

Bind mounts

Bind mounts kết nối trực tiếp một thư mục cụ thể trên máy host với container.

Bạn chọn chính xác đường dẫn trên host – như /home/user/project – và Docker ánh xạ nó vào container. Khi bạn thay đổi tệp trên host, container sẽ thấy thay đổi ngay. Sửa tệp trong container, thay đổi xuất hiện trên host.

Sự đồng bộ theo thời gian thực này khiến bind mounts trở nên hoàn hảo cho phát triển.

Nhưng bind mounts có rủi ro. Chúng phơi bày các đường dẫn host cho container và phụ thuộc vào cấu trúc thư mục cụ thể có thể không tồn tại trên máy khác.

tmpfs mounts

tmpfs mounts lưu dữ liệu trong bộ nhớ RAM của host thay vì trên đĩa.

Không có gì được ghi xuống hệ thống tệp. Khi container dừng, dữ liệu biến mất hoàn toàn. Điều này khiến tmpfs mounts hữu ích cho dữ liệu tạm thời bạn không muốn lưu lại – như token xác thực, dữ liệu phiên, hoặc tệp cache mà bạn sẽ dựng lại.

Tuy nhiên, tmpfs mounts bị giới hạn bởi RAM sẵn có và chỉ hoạt động trên host Linux.

Docker Volumes cho dữ liệu bền vững

Volumes là giải pháp lưu trữ sẵn sàng cho sản xuất của Docker, và bạn nên mặc định sử dụng chúng trừ khi có lý do cụ thể để không dùng.

Chúng được Docker quản lý hoàn toàn, hoạt động nhất quán trên các nền tảng khác nhau và theo thiết kế sẽ tồn tại sau khi container bị xóa. Nếu bạn chạy cơ sở dữ liệu, lưu trữ trạng thái ứng dụng, hoặc xử lý bất kỳ dữ liệu nào cần sống lâu hơn một container, volumes là câu trả lời.

Cách Docker volumes hoạt động

Docker lưu volumes trong một thư mục chuyên dụng trên máy host:

  • Linux: /var/lib/docker/volumes/
  • macOS: ~/Library/Containers/com.docker.docker/Data/vms/0/data/
  • Windows: \\wsl$\docker-desktop-data\data\docker\volumes\, giả định backend WSL2

Bạn không quản lý trực tiếp thư mục này. Docker xử lý việc tạo, phân quyền và dọn dẹp thông qua API của chính nó. Sự tách biệt này đồng nghĩa volumes hoạt động như nhau dù bạn dùng Linux, Mac hay Windows, từ đó giúp thiết lập container của bạn có tính di động giữa môi trường phát triển và sản xuất.

Điều cần nhớ là volumes tồn tại độc lập với bất kỳ container nào. Khi bạn tạo một volume, gắn nó vào container, chạy ứng dụng, rồi dừng và xóa container đó, volume vẫn giữ nguyên cùng toàn bộ dữ liệu.

Nếu bạn khởi động một container mới và gắn cùng volume, dữ liệu của bạn vẫn ở đó.

Tạo và tái sử dụng volumes

Bạn có thể tạo một volume có tên trước khi khởi động bất kỳ container nào:

docker volume create mydata

Image 1 - Creating a Docker volume

Sau đó gắn nó khi bạn chạy container với cờ --mount:

docker run -d \
  --name postgres-db \
  --mount source=mydata,target=/var/lib/postgresql/data \
  postgres:18

Image 2 - Mounting a volume to a Postgres database

Lệnh này gắn volume mydata vào /var/lib/postgresql/data trong container, nơi Postgres lưu tệp cơ sở dữ liệu.

Giờ bạn có thể dừng và xóa container này, rồi khởi động một container mới với cùng volume:

docker rm -f postgres-db

docker run -d \
  --name postgres-db-new \
  --mount source=mydata,target=/var/lib/postgresql/data \
  postgres:18

Cơ sở dữ liệu của bạn quay lại với toàn bộ bảng và dòng dữ liệu nguyên vẹn.

Đó chính là mục đích của volumes – tính bền vững dữ liệu qua các vòng đời container.

Quản lý và bảo trì dữ liệu trong volume

Bạn có thể chạy lệnh này để kiểm tra các volume hiện có trên hệ thống:

docker volume ls

Image 3 - Listing all Docker volumes

Sau đó, bạn có thể chạy lệnh này để kiểm tra chi tiết một volume cụ thể, xem nó được lưu ở đâu và container nào đang dùng:

docker volume inspect mydata

Image 4 - Docker volume details

Lệnh này hiển thị điểm mount trên host và metadata hữu ích. Nhưng bạn hiếm khi cần truy cập trực tiếp thư mục này – đó là việc của Docker.

Nếu bạn đã xong với volume và muốn xóa nó, chỉ cần chạy lệnh này:

docker volume rm mydata

Docker sẽ không cho bạn xóa một volume đang được gắn vào container đang chạy. Hãy dừng container trước, rồi xóa volume.

Image 5 - Trying to remove a volume attached to a running container

Cuối cùng, nếu bạn muốn dọn dẹp tài nguyên và giải phóng dung lượng đĩa, có thể chạy lệnh này để xóa tất cả volumes không dùng tới cùng lúc:

docker volume prune

Image 6 - Deleting all unused volumes

Đối với môi trường sản xuất, Docker hỗ trợ volume driver kết nối tới các hệ thống lưu trữ bên ngoài như NFS, AWS EFS hoặc khối lưu trữ đám mây. Bạn chỉ định driver khi tạo volume và Docker lo phần còn lại. Cách này cho phép bạn lưu dữ liệu hoàn toàn bên ngoài máy host, điều quan trọng với các thiết lập high-availability nơi container di chuyển giữa các máy chủ.

Tiếp theo, hãy bàn về bind mounts.

Bind Mounts cho phát triển cục bộ

Bind mounts cho phép bạn truy cập trực tiếp hệ thống tệp của host từ trong container, và đó chính là lý do các nhà phát triển yêu thích chúng.

Chúng hoàn hảo cho phát triển cục bộ nhưng có đánh đổi khiến chúng rủi ro cho sản xuất. Bạn có đồng bộ tệp thời gian thực và không cần bước build, nhưng mất tính di động và có thể mở ra lỗ hổng bảo mật.

Cách bind mounts hoạt động

Một bind mount ánh xạ trực tiếp một thư mục cụ thể trên host vào container.

Bạn chỉ định đường dẫn chính xác – như /home/user/myapp – và Docker cung cấp nó tại một đường dẫn trong container. Không có sao chép, không có lưu trữ do Docker quản lý, không có lớp trừu tượng. Container thấy các tệp thực sự trên host của bạn.

Nếu bạn thay đổi tệp trên host, container thấy ngay. Tương tự, nếu bạn sửa tệp trong container, nó được cập nhật trên host. Cả hai phía làm việc với cùng các tệp theo thời gian thực.

Ví dụ bind mount thực tế:

docker run -d \
  --name dev-app \
  --mount type=bind,source=/Users/dradecic/Desktop/app,target=/app \
  python:3.14

Image 7 - Using a bind mount with Docker

Lệnh này gắn /Users/dradecic/Desktop/app từ host của tôi vào /app trong container. Khi tôi chỉnh sửa một tệp Python trong /Users/dradecic/Desktop/app bằng trình soạn thảo, ứng dụng trong container thấy thay đổi ngay lập tức.

Bạn cũng có thể dùng cú pháp ngắn hơn:

Các quy trình phát triển phổ biến

Trường hợp dùng phổ biến nhất là gắn thư mục mã nguồn của bạn trong quá trình phát triển.

Giả sử bạn đang xây dựng ứng dụng FastAPI. Bạn có thể mount thư mục dự án vào container, bật hot reload và có một môi trường phát triển hoàn chỉnh:

docker run -d \
  --name fastapi-dev \
  --mount type=bind,source=/Users/dradecic/Desktop/app,target=/app \
  -w /app \
  -p 8000:8000 \
  python:3.14 \
  sh -c "pip install fastapi uvicorn && uvicorn main:app --reload --host 0.0.0.0"

Image 8 - FastAPI application with bind mount

Tham khảo thêm, đây là tệp main.py của tôi:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(
    title="FastAPI Docker Demo",
    description="A minimal FastAPI app running inside Docker",
    version="1.0.0",
)

class Item(BaseModel):
    name: str
    price: float
    in_stock: bool = True

@app.get("/")
def read_root():
    return {
        "message": "FastAPI is running",
        "docs": "/docs",
        "redoc": "/redoc",
    }

@app.get("/health")
def health_check():
    return {"status": "ok"}

@app.post("/items")
def create_item(item: Item):
    return {
        "message": "Item received",
        "item": item,
    }

Sau khi chạy lệnh Docker, ứng dụng khả dụng từ máy host của tôi trên cổng 8000:

Image 9 - Accessing the FastAPI application

Nếu bạn chỉnh sửa main.py trong trình soạn thảo và lưu tệp, FastAPI sẽ tự động reload. Không cần build lại image, không cần khởi động lại container. Bạn viết mã như trên máy cục bộ, nhưng ứng dụng chạy trong môi trường container nhất quán.

Rủi ro và hạn chế

Bind mounts phơi bày hệ thống tệp host cho container, và điều đó có thể tạo ra vấn đề bảo mật.

Một container với bind mount có thể đọc và ghi tệp trên host. Chạy container với quyền root – mặc định là vậy – và nó có quyền root với các tệp được gắn. Mã độc hoặc container bị xâm nhập có thể sửa hoặc xóa bất cứ thứ gì trong thư mục được mount.

Tính di động cũng là một vấn đề.

Bind mounts phụ thuộc vào các đường dẫn cụ thể tồn tại trên host. /Users/dradecic/Desktop/app không tồn tại trên máy của bạn hay trên máy chủ sản xuất. Điều này phá vỡ lời hứa "chạy được mọi nơi" của container.

Sự khác biệt nền tảng cũng đáng kể. Windows và Mac dùng VM để chạy Docker, nên bind mounts phải qua một lớp chuyển đổi bổ sung. Điều này khiến thao tác tệp chậm hơn và có thể gây lỗi tinh vi với file watching và symlink.

Môi trường sản xuất không bao giờ nên dùng bind mounts.

Chúng quá phụ thuộc đường dẫn cụ thể của host, quá rủi ro về bảo mật và không thể đưa vào kiểm soát phiên bản. Volumes giải quyết tất cả các vấn đề này, đó là lý do chúng là tiêu chuẩn trong sản xuất.

Hãy coi bind mounts là công cụ phát triển – nhanh, tiện, mạnh, nhưng không nên xuất hiện gần môi trường sản xuất.

tmpfs Mounts cho dữ liệu tạm thời

tmpfs mounts lưu dữ liệu trong RAM của host thay vì trên đĩa. Điều này khiến chúng phù hợp cho dữ liệu bạn không muốn lưu lại.

Hành vi lưu trữ trong bộ nhớ

Một tmpfs mount tồn tại hoàn toàn trong bộ nhớ.

Docker cấp phát RAM trên host và cung cấp nó như một hệ thống tệp trong container. Một tệp ghi vào tmpfs mount sẽ không bao giờ đụng tới đĩa. Dữ liệu nằm trong bộ nhớ cho đến khi container dừng.

Khi bạn dừng container, mọi thứ trong tmpfs mount bị xóa. Không cần dọn dẹp, không còn tệp thừa, không dấu vết. Mỗi lần bạn khởi động lại container, bạn có một tmpfs mount trống mới.

Tóm lại, tmpfs mounts dành cho dữ liệu bạn chủ đích không muốn giữ lại – tính toán tạm, token phiên, hoặc thông tin nhạy cảm không nên tồn tại sau khi sử dụng.

Các trường hợp sử dụng điển hình

Trường hợp thường gặp nhất là lưu trữ secrets hoặc dữ liệu nhạy cảm.

Giả sử bạn chạy container cần API key hoặc mật khẩu cơ sở dữ liệu. Hãy lưu nó trong tmpfs mount, và secret sẽ không bao giờ được lưu trên đĩa. Khi container dừng, secret biến mất khỏi bộ nhớ. Không có tệp nào vô tình bị commit vào kiểm soát phiên bản hoặc để lộ trên hệ thống tệp.

Cache cũng là một lựa chọn hay. Các artifact build, mã đã biên dịch, hoặc phụ thuộc đã tải xuống mà bạn sẽ tạo lại không cần lưu bền. Đặt chúng trong tmpfs để truy cập nhanh hơn trong thời gian container chạy, rồi để chúng biến mất khi xong.

Tệp tạm cũng phù hợp – như dữ liệu phiên, lock file, hoặc kết quả xử lý trung gian chỉ có ý nghĩa khi container đang chạy.

Cấu hình cơ bản

Chạy lệnh này để tạo một tmpfs mount bằng cờ --tmpfs:

docker run -d \
  --name temp-app \
  --tmpfs /tmp:rw,size=100m \
  python:3.14

Lệnh này tạo một tmpfs mount 100MB tại /tmp trong container. Tùy chọn size giới hạn lượng RAM mount có thể dùng.

Bạn cũng có thể dùng cú pháp --mount:

docker run -d \
  --name temp-app \
  --mount type=tmpfs,destination=/tmp,tmpfs-size=104857600 \
  python:3.14

Giá trị tmpfs-size tính bằng byte – 104857600 byte tương đương 100MB.

Nếu bạn không đặt giới hạn dung lượng, tmpfs có thể dùng tới một nửa RAM hệ thống. Điều đó nguy hiểm – vì lý do hiển nhiên. Hãy luôn đặt giới hạn dung lượng rõ ràng.

Điểm trừ lớn là tmpfs mounts chỉ hoạt động trên Linux.

Docker Desktop trên Mac và Windows không hỗ trợ vì chúng chạy Docker trong một VM Linux, và tmpfs cần hỗ trợ trực tiếp từ kernel.

Cú pháp và cấu hình Docker Mount

Docker cung cấp hai cách định nghĩa mounts, và chọn đúng cú pháp giúp lệnh của bạn dễ đọc và dễ debug hơn.

Cả hai đều hoạt động, nhưng một cách sẽ mở rộng tốt hơn khi bạn cần tùy chọn nâng cao hoặc nhiều mount trong một container.

-mount so với --volume

Cờ --mount dùng cặp khóa-giá trị (key-value) rõ ràng, trong khi -v hoặc --volume dùng chuỗi phân tách bởi dấu hai chấm.

Đây là cùng một volume mount với cả hai cú pháp:

# Using --mount
docker run -d \
  --mount type=volume,source=mydata,target=/app/data \
  python:3.14

# Using -v
docker run -d \
  -v mydata:/app/data \
  python:3.14

Cả hai đều tạo một volume tên mydata và gắn nó vào /app/data trong container.

Hãy dùng --mount cho mọi thứ vượt quá thiết lập cơ bản. Nó dài dòng hơn, nhưng các khóa rõ ràng giúp bạn hiểu từng phần làm gì. Khi bạn thêm tùy chọn như chỉ-đọc hoặc driver volume tùy chỉnh, nó vẫn dễ đọc trong khi -v có thể trông như một chuỗi khó hiểu.

Cú pháp -v phù hợp cho quy trình phát triển đơn giản nơi bạn gõ lệnh bằng tay.

Các tùy chọn mount phổ biến

Tùy chọn readonly ngăn container sửa đổi dữ liệu được mount:

docker run -d \
  --mount type=volume,source=mydata,target=/app/data,readonly \
  python:3.14

Điều này hữu ích cho tệp cấu hình hoặc dữ liệu tham chiếu mà container chỉ nên đọc chứ không bao giờ được sửa. Một container cố ghi vào mount chỉ-đọc sẽ gặp lỗi quyền truy cập.

Với volumes, tùy chọn volume-nocopy bỏ qua việc sao chép dữ liệu hiện có từ image của container vào volume:

docker run -d \
  --mount type=volume,source=mydata,target=/app/data,volume-nocopy \
  python:3.14

Mặc định, Docker sao chép bất cứ thứ gì tồn tại tại điểm mount trong image vào volume mới. Khi bạn đặt volume-nocopy, bạn sẽ nhận một volume trống bất kể trong image có gì.

Với tmpfs mounts, tùy chọn tmpfs-size đặt giới hạn bộ nhớ:

docker run -d \
  --mount type=tmpfs,target=/tmp,tmpfs-size=104857600 \
  python:3.14

Tùy chọn này giới hạn tmpfs mount ở mức 100MB. Nếu không, một tmpfs mount có thể ngốn toàn bộ RAM có sẵn.

Mount đè lên dữ liệu hiện có

Khi bạn mount vào một thư mục đã tồn tại trong image của container, mount sẽ hoàn toàn che khuất những gì có sẵn ở đó.

Giả sử image của bạn có thư mục /app/data với các tệp cấu hình tích hợp sẵn. Khi bạn mount một volume vào /app/data, các tệp cấu hình đó sẽ biến mất. Container chỉ nhìn thấy nội dung trong volume.

Điều này xảy ra với mọi loại mount – volumes, bind mounts và tmpfs. Nội dung được mount có ưu tiên, và thư mục gốc trở nên không thể truy cập khi mount đang hoạt động.

Sử dụng Mounts với Docker Compose

Docker Compose giúp định nghĩa và chia sẻ mounts giữa nhiều container trong ngăn xếp ứng dụng của bạn một cách dễ dàng.

Thay vì gõ dài dòng các lệnh docker run với cờ mount, bạn khai báo mọi thứ trong tệp docker-compose.yml. Để tôi minh họa.

Định nghĩa volumes và bind mounts trong Compose

Dưới đây là tệp Compose có cả ví dụ volume và bind mount:

services:
  service-1:
    image: ubuntu:latest
    command: sleep infinity
    volumes:
      - ./code:/app          # Bind mount for development
      - shared:/data         # Named volume shared with worker

  service-2:
    image: ubuntu:latest
    command: sleep infinity
    volumes:
      - shared:/data         # Same volume as service-1

volumes:
  shared:

Khóa volumes dưới mỗi service định nghĩa những gì sẽ được mount. Các đường dẫn tương đối như ./code tạo bind mounts, trong khi các tên như shared tham chiếu tới volumes có tên.

Phần volumes cấp cao nhất khai báo các volumes có tên mà Compose sẽ tạo và quản lý. Cả service-1 và service-2 cùng mount volume shared, nên chúng thấy cùng một tập tệp. Ghi một tệp từ container này, container kia có thể đọc ngay.

Lệnh sleep infinity giữ container chạy để bạn có thể exec vào – chỉ nhằm mục đích minh họa.

Duy trì và xác minh dữ liệu

Khởi động stack với docker compose up -d, rồi kiểm tra mounts có hoạt động không:

# Write data to the shared volume from app
docker compose exec service-1 sh -c "echo 'test' > /data/file.txt"

# Read it from worker
docker compose exec service-2 cat /data/file.txt

Nếu cả hai lệnh đều hoạt động, volumes của bạn được cấu hình đúng.

Image 10 - Sharing data between volumes

Giờ bạn có thể chạy lệnh này để dừng và xóa mọi thứ:

docker compose down

Các volume có tên của bạn vẫn còn. Nếu bạn khởi động stack lại với docker compose up -d, dữ liệu bạn đã ghi trước đó vẫn sẽ ở đó. Đây là cách cơ sở dữ liệu tồn tại qua các lần triển khai – volume sống lâu hơn container.

Để xóa volumes khi dừng stack, thêm cờ -v:

docker compose down -v

Lệnh này xóa tất cả volumes được định nghĩa trong tệp Compose. Dùng khi bạn muốn bắt đầu sạch.

Nạp dữ liệu khởi tạo vào volumes

Mẫu phổ biến nhất là dùng một init container riêng để seed volume dùng chung:

services:
  init:
    image: ubuntu:latest
    command: sh -c "mkdir -p /source && echo 'initial data' > /source/seed.txt && cp /source/* /dest/"
    volumes:
      - shared:/dest

  service-1:
    image: ubuntu:latest
    command: sleep infinity
    depends_on:
      - init
    volumes:
      - shared:/data

  service-2:
    image: ubuntu:latest
    command: sleep infinity
    depends_on:
      - init
    volumes:
      - shared:/data

volumes:
  shared:

Container init tạo dữ liệu seed và sao chép vào volume shared, rồi thoát. Cả service-1 và service-2 khởi động sau đó và thấy dữ liệu đã được seed sẵn sàng dùng.

Image 11 - Initial data

Compose xử lý sự phức tạp của việc điều phối nhiều container và bộ nhớ dùng chung trong một tệp duy nhất, có kiểm soát phiên bản.

Hiệu năng và bảo mật của Docker Mount

Chọn sai loại mount có thể làm chậm container hoặc tạo lỗ hổng bảo mật mà bạn không ngờ tới. Đọc phần này nếu bạn không muốn điều đó xảy ra.

Đánh đổi hiệu năng giữa các loại mount

Volumes cho hiệu năng tốt nhất trên Linux vì chúng được lưu trực tiếp trên hệ thống tệp host mà không có lớp chuyển đổi.

Trên Mac và Windows, Docker chạy trong một VM Linux. Volumes vẫn hoạt động tốt vì chúng nằm trong VM đó. Ngược lại, bind mounts phải đồng bộ tệp giữa hệ điều hành host và VM Linux, gây thêm độ trễ. Thao tác tệp trên bind mounts có thể chậm đáng kể trên Mac và Windows so với Linux thuần.

tmpfs là lựa chọn nhanh nhất cho đọc/ghi vì mọi thứ diễn ra trong RAM. Không I/O đĩa, không overhead hệ thống tệp. Nhưng bạn bị giới hạn bởi bộ nhớ sẵn có và dữ liệu biến mất khi container dừng.

Nếu bạn dùng Linux và cần hiệu năng tối đa, hãy dùng volumes. Nếu bạn dùng Mac hoặc Windows và thấy thao tác tệp chậm trong phát triển, có lẽ bạn đang gặp overhead của bind mount. Hãy chuyển sang volumes cho tải công việc sản xuất.

Hệ lụy bảo mật của mounts

Mỗi mount trao cho container quyền truy cập ra ngoài hệ thống tệp cách ly của nó, và điều đó tạo rủi ro.

Bind mounts là mối lo lớn nhất. Nếu bạn mount /home/user vào container, một container bị xâm nhập có thể đọc khóa SSH của bạn, sửa cấu hình shell, hoặc xóa tệp trên toàn bộ thư mục home. Chạy container đó với quyền root – mặc định – và nó có quyền root với các tệp đó.

Volumes giảm rủi ro này vì chúng được cô lập trong thư mục lưu trữ của Docker. Một container không thể mount tùy tiện đường dẫn host thông qua volumes. Nhưng volumes vẫn có thể làm rò rỉ dữ liệu giữa các container nếu bạn chia sẻ thiếu cẩn trọng.

tmpfs mounts giảm thiểu rủi ro lưu trữ bền vững – secrets lưu trong bộ nhớ biến mất khi container dừng. Nhưng chúng không bảo vệ khỏi tấn công ở thời điểm chạy khi container bị xâm nhập đọc secrets từ bộ nhớ.

Quy tắc chung là mounts phá vỡ sự cô lập của container, nên hãy dùng một cách cẩn trọng.

Thực hành tốt để mount an toàn

Chỉ mount những gì container cần và không hơn.

Thay vì mount toàn bộ thư mục dự án, chỉ mount thư mục con mà container sử dụng. Thay vì mount /var/log với quyền ghi, hãy mount chỉ-đọc nếu container chỉ cần đọc log.

Dùng tùy chọn readonly bất cứ khi nào có thể:

docker run -d \
  --mount type=bind,source=/app/config,target=/config,readonly \
  ubuntu:latest

Điều này ngăn container sửa đổi dữ liệu được mount, hạn chế thiệt hại nếu bị xâm nhập.

Chạy container với người dùng không phải root để giảm tác động của lỗ hổng bind mount. Tạo một user trong Dockerfile và chuyển sang user đó trước khi container khởi động:

RUN useradd -m appuser
USER appuser

Thường xuyên dọn dẹp volumes không dùng với docker volume prune. Các volume cũ sẽ tích tụ theo thời gian, ngốn dung lượng đĩa và có thể chứa dữ liệu nhạy cảm từ các container đã xóa.

Không bao giờ mount các thư mục host nhạy cảm như /, /etc hoặc /var trừ khi bạn có lý do cụ thể và hiểu rủi ro. Mỗi mount nên có mục đích rõ ràng và phạm vi tối thiểu.

Khắc phục sự cố Docker Mount

Vấn đề mount thường xuất hiện dưới dạng lỗi quyền truy cập, thiếu tệp, hoặc container không khởi động được – và gần như luôn do cùng một tập nguyên nhân.

Dưới đây là cách chẩn đoán và sửa các vấn đề phổ biến nhất mà bạn sẽ gặp.

Lỗi quyền truy cập và quyền sở hữu

Lỗi quyền xảy ra khi người dùng bên trong container không có quyền truy cập tệp được mount.

Container Docker mặc định chạy với quyền root. Khi root tạo một tệp trong bind mount, tệp đó sẽ thuộc sở hữu của root trên host. Nếu bạn cố chỉnh sửa bằng tài khoản người dùng thường, bạn sẽ gặp lỗi từ chối quyền.

Chiều ngược lại cũng xảy ra. Mount một thư mục bạn sở hữu vào container chạy với người dùng không phải root, và container có thể không ghi được vào đó.

Bạn có thể kiểm tra quyền sở hữu tệp với ls -la trên thư mục được mount:

ls -la /path/to/mounted/directory

Nếu tệp thuộc sở hữu root nhưng container của bạn chạy với người dùng khác, bạn đang bị lệch. Sửa bằng cách chạy container với cùng người dùng sở hữu tệp:

docker run -d \
  --user $(id -u):$(id -g) \
  -v ./data:/app/data \
  ubuntu:latest

Lệnh này chạy container với người dùng hiện tại của bạn thay vì root, khớp quyền sở hữu tệp trong bind mount.

Với volumes, Docker tự xử lý quyền khi container tạo tệp. Nhưng nếu bạn vẫn gặp lỗi, hãy kiểm tra ứng dụng trong container chạy với user nào và liệu nó có quyền ghi vào điểm mount không.

Sai đường dẫn và cấu hình

Lỗi phổ biến nhất là mount một đường dẫn không tồn tại trên host.

Cố mount /home/user/project khi thư mục đó không tồn tại, Docker sẽ tạo một thư mục trống thuộc sở hữu root. Container của bạn khởi động, nhưng nó mount sai – một thư mục trống thay vì dự án thực sự của bạn.

Luôn xác minh đường dẫn tồn tại trước khi mount chúng:

ls /home/user/project

Nếu thư mục không tồn tại, hãy tạo trước hoặc sửa đường dẫn trong lệnh mount của bạn.

Trong Docker Compose, đường dẫn tương đối được tính từ thư mục chứa tệp docker-compose.yml của bạn. Nếu tệp nằm ở /home/user/app/ và bạn dùng ./data, Docker sẽ tìm /home/user/app/data.

Di chuyển tệp Compose, và mount sẽ hỏng.

Một lỗi phổ biến khác là mount vào sai đường dẫn đích trong container. Mount vào /app/data khi ứng dụng của bạn kỳ vọng /data, và ứng dụng sẽ không tìm thấy tệp của nó. Hãy kiểm tra tài liệu ứng dụng hoặc Dockerfile để xác nhận vị trí dữ liệu.

Những khác biệt theo nền tảng

Trên Linux, bind mounts làm việc trực tiếp với hệ thống tệp host.

Trên Mac và Windows, Docker chạy trong một VM Linux. Bind mounts đồng bộ tệp giữa hệ điều hành host và VM đó, gây ra vấn đề thời điểm. Các trình theo dõi tệp – công cụ reload ứng dụng khi tệp thay đổi – đôi khi bỏ lỡ cập nhật do độ trễ đồng bộ.

Mac và Windows cũng xử lý quyền tệp khác nhau. VM dịch quyền giữa host OS và Linux, có thể khiến tệp hiển thị sai quyền sở hữu trong container.

Symlink không hoạt động ổn định trong bind mounts trên Mac và Windows. VM không phải lúc nào cũng resolve được symlink trỏ ra ngoài thư mục được mount, khiến tệp trông như mất hoặc hỏng trong container.

tmpfs mounts hoàn toàn không hoạt động trên Mac và Windows vì VM không cung cấp tmpfs cho host. Nếu bạn cố dùng tmpfs mount, Docker sẽ âm thầm bỏ qua hoặc báo lỗi tùy phiên bản.

Nếu bạn phát triển trên Mac hoặc Windows và gặp vấn đề đồng bộ tệp kỳ quặc, hãy chuyển sang volumes có tên để có hiệu năng và độ tin cậy tốt hơn. Dành bind mounts cho quy trình phát triển nơi đồng bộ thời gian thực quan trọng hơn tính nhất quán hoàn hảo.

Kết luận

Tóm lại, hãy dùng volumes cho dữ liệu sản xuất cần tồn tại bền vững, như cơ sở dữ liệu, tệp tải lên, trạng thái ứng dụng – bất cứ thứ gì bạn không thể để mất. Chúng do Docker quản lý, có tính di động giữa các nền tảng và là lựa chọn an toàn nhất cho dữ liệu quan trọng.

Bind mounts thuộc về quy trình phát triển, nơi bạn cần đồng bộ tệp thời gian thực giữa host và container. Khi bạn chỉnh sửa mã trong trình soạn thảo, bạn sẽ thấy thay đổi ngay trong ứng dụng chạy trong container. Nhưng hãy tránh dùng trong sản xuất vì chúng quá phụ thuộc đường dẫn host cụ thể và tạo ra rủi ro bảo mật không cần thiết.

Khi bạn sẵn sàng đào sâu hơn vào containerization và virtualization, hãy xem khóa học của chúng tôi: Containerization and Virtualization with Docker and Kubernetes.

Dario Radečić

Tác giả

Dario Radečić

Chuyên gia Khoa học Dữ liệu Cấp cao, làm việc tại Croatia. Tác giả Công nghệ Hàng đầu với hơn 700 bài viết đã xuất bản, đạt trên 10 triệu lượt xem. Tác giả cuốn sách Tự động hóa Machine Learning với TPOT.

Câu hỏi thường gặp về Docker Mount

Làm thế nào để tối ưu hiệu năng volume của Docker?

Hãy dùng volumes thay vì bind mounts trên Mac và Windows – chúng được lưu bên trong VM Linux của Docker và tránh được overhead đồng bộ giữa hệ điều hành host và VM. Trên mọi nền tảng, tránh mount volumes vào các đường dẫn có tần suất ghi cao trừ khi thực sự cần. Với khối lượng công việc thiên về đọc, cân nhắc dùng tùy chọn mount readonly để giảm overhead hệ thống tệp.

Thực hành tốt nhất khi dùng Docker volumes trong sản xuất là gì?

Luôn dùng volumes có tên thay vì volumes ẩn danh để bạn có thể theo dõi và quản lý dễ dàng. Thiết lập sao lưu định kỳ bằng cách sao chép dữ liệu volume sang lưu trữ ngoài hoặc dùng volume driver kết nối tới dịch vụ lưu trữ đám mây. Dọn dẹp volumes không dùng với docker volume prune để tránh vấn đề dung lượng đĩa, và không bao giờ chia sẻ volumes giữa môi trường sản xuất và phát triển.

Sự khác nhau giữa Docker volumes và bind mounts là gì?

Volumes do Docker quản lý và được lưu trong thư mục chuyên dụng do Docker kiểm soát, giúp chúng có tính di động và độc lập nền tảng. Bind mounts ánh xạ trực tiếp các đường dẫn host cụ thể vào container, cho phép đồng bộ tệp thời gian thực nhưng ràng buộc thiết lập của bạn vào cấu trúc thư mục chính xác. Volumes hoạt động nhất quán trên Linux, Mac và Windows, trong khi bind mounts chậm hơn trên Mac và Windows do overhead của VM.

Làm sao để đảm bảo tính bền vững dữ liệu khi dùng Docker containers?

Mount volumes vào các thư mục nơi ứng dụng của bạn lưu dữ liệu – như /var/lib/postgresql/data cho cơ sở dữ liệu hoặc /app/uploads cho tệp người dùng. Đừng bao giờ dựa vào lớp ghi của container cho bất kỳ thứ gì bạn cần giữ lại, vì dữ liệu đó sẽ biến mất khi bạn xóa container. Trong Docker Compose, hãy khai báo volumes ở phần volumes cấp cao nhất và tham chiếu chúng trong các service để đảm bảo dữ liệu tồn tại qua các lần khởi động lại và xóa container.

How do I ensure data persistence when using Docker containers?

Mount volumes to directories where your application stores data - like /var/lib/postgresql/data for databases or /app/uploads for user files. Never rely on the container's writable layer for anything you need to keep, because that data disappears when you remove the container. In Docker Compose, declare volumes in the top-level volumes section and reference them in your services to ensure data survives container restarts and removals.

Chủ đề

Liên quan