Cách chia sẻ Dữ liệu giữa các Docker Container
Docker là một công cụ Containerization phổ biến dùng để cung cấp các ứng dụng phần mềm một đơn vị độc lập chứa đầy đủ những môi trường hay tài nguyên cần thiết để chúng hoạt động. Sử dụng container Docker đảm bảo rằng phần mềm sẽ hoạt động theo cùng một cách, bất kể nơi nó được triển khai, bởi vì môi trường run-time của nó là hoàn toàn giống nhau.
Nói chung, container Docker chỉ chạy trong khoảng thời gian cần thiết để thực hiện các lệnh. Tuy nhiên thỉnh thoảng các ứng dụng cần phải chia sẻ quyền truy cập vào dữ liệu hoặc lưu lại dữ liệu sau khi một container sẽ bị xóa. Database, nội dung do người dùng tạo ra cho một trang web, và file log là một vài ví dụ về những dữ liệu không thực tế hoặc không thể đưa vào một Docker image mà ứng dụng cần truy cập. Các truy cập ổn định vào dữ liệu được cung cấp bởi Volume Docker.
Yêu cầu
Bạn sẽ cần một Cloud Server Ubuntu 16.04 với:
Một non-root user với quyền sudo. Tham khảo bài viết Thiết lập ban đầu cho server chay Ubuntu 16.04
Docker được cài đặt theo Bước 1 và Bước 2 trong bài viết Cách Cài đặt và Sử dụng Docker trên Ubuntu 16.04
Note: Các lệnh docker
cho các Docker data volume trong bài viết này hoạt động trên cả các nền tảng hệ điều hành khác ngoài Ubuntu 16.04, miễn là sudo
user đã được thêm vào nhóm docker
.
Docker Volume có thể được tạo ra và đính kèm trong lệnh tạo ra một container, hoặc họ có thể được tạo ra một cách độc lập với bất kỳ container nào và được đính kèm với container sau đó. Trong bài viết này, ta sẽ xem xét bốn cách khác nhau để chia sẻ dữ liệu giữa các container.
1 — Tạo một Volume độc lập
Được giới thiệu trong phiên bản Docker 1.9, lệnh docker volume create
cho phép tạo một volume mà không gắn nó với một container riêng biệt nào. Dùng lệnh này để thêm vào volume tên DataVolume1
:
docker volume create --name DataVolume1
Output:
Output DataVolume1
Để sử dụng volume, ta sẽ tạo một container mới từ Ubuntu image, với cờ --rm
cho phép tự động xóa nó khi thoát, -v
để cài volume với tên volume, dấu ":" và địa chỉ tuyệt đối nơi volume được lưu trữ trong container. Nếu địa chỉ không tồn tại, lệnh này sẽ tạo ra địa chỉ mới, còn nếu nó tồn tại thì volume sẽ ần những thành phần đã tồn tại trong địa chỉ ấy
docker run -ti --rm -v DataVolume1:/datavolume1 ubuntu
Sau đó ghi dữ liệu vào Volume:
echo "Example1" > /datavolume1/Example1.txt
Với cờ --rm
, container sẽ bị xóa ngay khi thoát, tuy nhiên volume trong nó vẫn có thể truy cập được.
exit
Kiểm tra lại với lệnh docker volume inspect
:
docker volume inspect DataVolume1
Output[
{
"CreatedAt": "2017-12-21T21:02:53Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/DataVolume1/_data",
"Name": "DataVolume1",
"Options": {},
"Scope": "local"
}]
Note: Ta thậm chí có thể truy cập vào dữ liệu trên host được lưu tại Mountpoint. Không nên thay đổi dứ liệu trên đó để tránh lỗi gián đoạn dữ liệu.
Giờ hãy tạo một container mới và kết nối nó đến DataVolume1
:
docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu
cat /datavolume1/Example1.txt
OutputExample1
Sau đó thoát khỏi container:
exit
2 — Tạo một Volume ổn định khi Container bị gỡ bỏ
Ta sẽ dùng lệnh docker run
để tạo một container mới. -t
sẽ cho ta một terminal trong container, và -i
cho phép thao tác trên nó. Ta sẽ dùng --name
để định danh container.
Tương tự ở bước 1, ta sẽ dùng cờ -v
để tạo volume với path cụ thể:
docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu
Note: Cờ -v
có rất nhiều cách sử dụng. Chẳng hạn:
-v /path:/path/in/container
: Lệnh này mount thư mục host, /path
ở đây là /path/in/container
-v path:/path/in/container
: Lệnh này tạo một volume tên path
không có quan hệ gì với thư mục host.Thêm một số dữ liệu vào volume:
echo "Example2" > /datavolume2/Example2.txt
cat /datavolume2/Example2.txt
OutputExample2
Sau đó thoát container:
exit
Khi khởi động lại, volume sẽ tự động mount đến path được cài ở trên
docker start -ai Container2
Kiểm tra lại bằng cách mở file vừa thêm vào volume:
cat /datavolume2/Example2.txt
OutputExample2
Thoát volume:
exit
Docker không chó phép gỡ volume khi được kết nối với container. Nếu gõ lệnh:
docker volume rm DataVolume2
Output trả về lỗi do container( với ID màu vàng) tham chiếu đến volume đang chạy:
Output Error response from daemon: unable to remove volume: remove DataVolume2: volume is in use - [d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63]
Dùng ID đó để xóa container:
docker rm d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
Outputd0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
Việc xóa container không ảnh hưởng tới volume. Kiểm tra lại điều này với lệnh docker volume ls
:
docker volume ls
OutputDRIVER VOLUME NAME local DataVolume2
Sau đó dùng docker volume rm
để xóa nó:
docker volume rm DataVolume2
3 — Tạo một Volume từ một Thư mục có sẵn
Thường thì tạo volume độc lập với lệnh docker volume create
và tạo volume cùng với container là tương đương nhau, ngoại trù một số trường hợp. Nếu tạo volume cùng lúc với container và cung cấp path chứa dữ liệu trong base image, dữ liệu sẽ được copy vào volume.
Ví dụ ở đây ta sẽ tạo một container và thêm vào data volume với path /var
docker run -ti --rm -v DataVolume3:/var ubuntu
Tất cà thành phần trong thư mục /var của base image được copy đến volume, và ta có thể mount volume đó với một container mới.
Thoát khỏi container hiện tại:
exit
Đến đây, thay vì sử dụng lệnh bash
của base image, ta sẽ dùng lệnh ls
để hiển thị các thành phần của volume:
docker run --rm -v DataVolume3:/datavolume3 ubuntu ls datavolume3
Thư mục datavolume3
giờ đã chứa bản copy các thành phần của thư mục /var
của base image:
Outputbackups
cache
lib
local
lock
log
mail
opt
run
spool
tmp
4 — Chia sẻ Dữ liệu giữa nhiều Docker Container
Đến đây, ta đã kết nối volume đến một container. Nhưng thường thì ta sẽ muốn nhiều container cùng được đính kèm vào một data volume. Việc này tương đối đơn giản để thực hiện, nhưng có một vấn đề là Docker không xử lý file locking. Nếu bạn cần nhiều container ghi dữ liệu đến volume, các ứng dụng đang chạy trong container phải được thiết kế để ghi đến các dữ liệu được chia sẻ nhằm tránh việc gián đoạn dữ liệu.
Tạo Container4 và DataVolume4
Dùng docker run
để tạo một container mới tên là Container4
cùng với data volume đi kèm với nó:
docker run -ti --name=Container4 -v DataVolume4:/datavolume4 ubuntu
Tạo một file text rồi thêm vào nội dung cho nó:
echo "This file is shared between containers" > /datavolume4/Example4.txt
Thoát container:
exit
Tạo Container5 và Mount Volume từ Container4
Tương tự như trên, tạo container mới là Container5, nhưng kết nối nới data volume của Container4
docker run -ti --name=Container5 --volumes-from Container4 ubuntu
cat /datavolume4/Example4.txt
OutputThis file is shared between containers
Thêm vào dòng sau từ container thứ hai:
echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt
Cuối cùng thoát container:
exit
Theo dõi thay đổi trên Container5
Khởi động Container4 ,sau đó mở file text chứa trong data volume kết nối với nó:
docker start -ai Container4
cat /datavolume4/Example4.txt
OutputThis file is shared between containers Both containers can write to DataVolume4
Thoát khỏi container:
exit
Docker không xử lí file locking, nên các ứng dụng phải tự xử lí file locking. Ta cũng có thể mount Docker volume ở chế độ read-only để đảm bảo không xảy ra gián đoạn dữ liệu bằng cờ :ro
.
Khởi động Container 6 và Mount Volume ở chế độ Read-Only
Để đặt volume ở chế độ read-only, ta sẽ thêm :ro
vào sau container name:
docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu
Kiểm tra lại bằng cách thử xóa file trong volume:
rm /datavolume4/Example4.txt
Outputrm: cannot remove '/datavolume4/Example4.txt': Read-only file system
Sau đó thoát và xóa các volume và container:
exit
docker rm Container4 Container5 Container6
docker volume rm DataVolume4
Tổng kết
Trong bài viết này, ta đã tạo một data volume cho phép dữ liệu được lưu trữ khi xóa container, chia sẻ data volume giữa các container, với điều kiện ứng dụng phải tự xử lí file locking để tránh gián đoạn dữ liệu và cách cấu hình data volume để chỉ cho phép ghi dữ liệu.