Cách chia sẻ Dữ liệu giữa các Docker Container

4 năm trước

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:

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  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.