25 cze

Docker: Dzielenie się danymi w kontenerach

dane-kontenerach-docker

Docker to popularne narzędzie do konteneryzacji, używane do dostarczania aplikacjom systemu plików zawierającego wszystko, czego potrzebują do uruchomienia. Korzystanie z kontenerów Dockera zapewnia, że oprogramowanie będzie zachowywać się w ten sam sposób niezależnie od tego, gdzie jest wdrażane, ponieważ jego środowisko uruchomieniowe jest spójne.

Zasadniczo, kontenery Dockera są efemeryczne i działają tak długo, jak potrzeba na wykonanie polecenia wydanego w kontenerze. Czasami jednak aplikacje muszą współdzielić dostęp do danych lub utrwalić dane po usunięciu kontenera. Bazy danych, treści generowane przez użytkowników dla witryny internetowej i pliki dziennika to tylko kilka przykładów danych, które są niepraktyczne lub niemożliwe do włączenia do obrazu Docker, ale do których aplikacje muszą mieć dostęp. Stały dostęp do danych zapewnia Docker Volumes.

Docker Volumes można tworzyć i dołączać za pomocą tego samego polecenia, które tworzy kontener, lub można je tworzyć niezależnie od dowolnych kontenerów i dołączać później. W tym artykule przyjrzymy się czterem różnym sposobom udostępniania danych między kontenerami.

Będziemy potrzebowali

Aby postępować zgodnie z tym artykułem, potrzebujesz serwera Ubuntu 20.04 z następującymi elementami:

Uwaga: Mimo że Wymagania wstępne zawierają instrukcje dotyczące instalowania Dockera na Ubuntu 20.04, polecenia dokowania dla woluminów danych Dockera w tym artykule powinny działać na innych systemach operacyjnych, o ile Docker jest zainstalowany, a użytkownik sudo został dodany do grupy docker.

Tworzenie niezależnego woluminu

Polecenie docker volume create, wprowadzone w wersji Dockera 1.9, pozwala utworzyć wolumin bez powiązania go z żadnym konkretnym kontenerem. Użyjemy tego polecenia, aby dodać wolumin o nazwie WoluminDanych1:

docker volume create --name DataVolume1

Wyświetlana jest nazwa wskazująca, że polecenie zakończyło się powodzeniem:

DataVolume1

Aby skorzystać z woluminu, utworzymy nowy kontener z obrazu Ubuntu, używając flagi –rm do automatycznego usunięcia go po wyjściu. Użyjemy również opcji -v, aby zamontować nowy wolumin. -v wymaga nazwy woluminu, dwukropka, a następnie ścieżki bezwzględnej do miejsca, w którym wolumin powinien pojawić się wewnątrz kontenera. Jeśli katalogi na ścieżce nie istnieją jako część obrazu, zostaną utworzone podczas uruchamiania polecenia. Jeśli istnieją, podłączony wolumin ukryje istniejącą zawartość:

docker run -ti --rm -v DataVolume1:/datavolume1 ubuntu

W kontenerze zapiszmy trochę danych do woluminu:

echo "Example1" > /datavolume1/Example1.txt

Ponieważ użyliśmy flagi –rm, nasz kontener zostanie automatycznie usunięty po wyjściu. Nasz wolumin będzie jednak nadal dostępny.

exit

Możemy sprawdzić, czy wolumin jest obecny w naszym systemie, docker volume inspect:

docker volume inspect DataVolume1
[
    {
        "CreatedAt": "2018-07-11T16:57:54Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/DataVolume1/_data",
        "Name": "DataVolume1",
        "Options": {},
        "Scope": "local"
    }
]

Uwaga: możemy nawet spojrzeć na dane na hoście na ścieżce wymienionej jako punkt montowania. Powinniśmy jednak unikać ich zmiany, ponieważ może to spowodować uszkodzenie danych, jeśli aplikacje lub kontenery nie będą świadome zmian.

Następnie uruchomimy nowy kontener i dołączymy WoluminDanych1:

docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu

Zweryfikuj zawartość:

cat /datavolume1/Example1.txt
Example1

W tym przykładzie utworzyliśmy wolumin, dołączyliśmy go do kontenera i zweryfikowaliśmy jego trwałość.

Tworzenie woluminu, który będzie istniał po usunięciu kontenera

W naszym następnym przykładzie utworzymy wolumin (jednocześnie z utworzeniem kontenera), usuniemy kontener, a następnie dołączymy wolumin do nowego kontenera.

Użyjemy polecenia docker run, aby utworzyć nowy kontener przy użyciu podstawowego obrazu Ubuntu. -t zapewni nam terminal, a -i pozwoli nam na interakcję z nim. Dla jasności użyjemy –name do zidentyfikowania kontenera.

Flaga -v pozwoli nam utworzyć nowy wolumin, który nazwiemy WoluminDanych2. Użyjemy dwukropka, aby oddzielić jego nazwę od ścieżki, w której wolumin powinien być zamontowany w kontenerze. Na koniec określimy podstawowy obraz Ubuntu i będziemy polegać na domyślnej komendzie w pliku Docker obrazu podstawowego Ubuntu, bash, aby wprowadziła nas do powłoki:

docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu

Uwaga: Flaga -v jest bardzo elastyczna. Może powiązać lub nazwać wolumin z niewielką modyfikacją składni. Jeśli pierwszy argument zaczyna się od / lub ~/, tworzysz powiązanie. Usuń to, a nadasz nazwę woluminowi. Na przykład:

  • -v /path:/path/in/container podłącza katalog gospodarza, /path w /path/in/container
  • -v path:/path/in/container tworzy wolumin o nazwie path bez związku z gospodarzem (hostem).

W kontenerze zapiszemy trochę danych do woluminu:

echo "Example2" > /datavolume2/Example2.txt
cat /datavolume2/Example2.txt
Example2

Wyjdźmy z kontenera:

exit

Po ponownym uruchomieniu kontenera wolumin zostanie zamontowany automatycznie:

docker start -ai Container2

Sprawdźmy, czy wolumin rzeczywiście się zamontował, a nasze dane są nadal dostępne:

cat /datavolume2/Example2.txt
Example2

Na koniec wyjdźmy i posprzątajmy:

exit

Docker nie pozwoli nam usunąć woluminu, jeśli jest do niego przyporządkowany kontener. Zobaczmy, co się stanie, gdy spróbujemy to zrobić:

docker volume rm DataVolume2

Komunikat mówi nam, że wolumin jest nadal w użyciu, i podaje długą wersję identyfikatora kontenera:

Error response from daemon: unable to remove volume: remove DataVolume2: volume is in use - [d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63]

Możemy użyć tego identyfikatora do usunięcia kontenera:

docker rm d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63

Usunięcie pojemnika nie wpłynie na głośność. Widzimy, że nadal jest obecny w systemie, wymieniając woluminy poleceniem docker volume ls:

docker volume ls
DRIVER              VOLUME NAME
local               DataVolume2

Możemy użyć polecenia docker volume rm, aby go usunąć:

docker volume rm DataVolume2

W tym przykładzie utworzyliśmy pusty wolumin danych jednocześnie z utworzeniem kontenera. W naszym kolejnym przykładzie zbadamy, co się stanie, gdy utworzymy wolumin z katalogiem kontenera, który już zawiera dane.

Tworzenie woluminu z istniejącego katalogu z danymi

Zasadniczo, tworzenie woluminu niezależnie za pomocą docker volume create, oraz tworzenie woluminu podczas tworzenia kontenera jest równoważne, z jednym wyjątkiem. Jeśli tworzymy wolumin jednocześnie z kontenerem i podamy ścieżkę do katalogu zawierającego dane w obrazie podstawowym, dane te zostaną skopiowane do woluminu.

Jako przykład, utworzymy kontener i dodamy wolumin danych w katalogu /var, który zawiera dane w obrazie podstawowym:

docker run -ti --rm -v DataVolume3:/var ubuntu

Cała zawartość z katalogu /var obrazu podstawowego jest kopiowana do woluminu i możemy zamontować ten wolumin w nowym kontenerze.

Opuść obecny kontener:

exit

Tym razem – zamiast polegać na domyślnym poleceniu bash obrazu podstawowego – wydamy własne polecenie ls, które wyświetli zawartość woluminu bez wchodzenia do powłoki:

docker run --rm -v DataVolume3:/datavolume3 ubuntu ls datavolume3

Katalog wolumindanych3 ma teraz kopię zawartości katalogu /var obrazu podstawowego:

backups
cache
lib
local
lock
log
mail
opt
run
spool
tmp

Jest mało prawdopodobne, że chcielibyśmy zamontować /var/ w ten sposób, ale może to być pomocne, jeśli stworzyliśmy własny obraz i chcemy w łatwy sposób zachować dane. W naszym następnym przykładzie pokażemy, w jaki sposób wolumin można współdzielić między wieloma kontenerami.

Dzielenie danych pomiędzy wieloma kontenerami

Do tej pory dołączaliśmy wolumin do jednego kontenera na raz. Często chcemy, aby wiele kontenerów dołączało się do tego samego woluminu danych. Jest to stosunkowo proste do osiągnięcia, ale jest jedno krytyczne zastrzeżenie: w tej chwili Docker nie obsługuje blokowania plików. Jeśli potrzebujesz zapisać wiele woluminów w kontenerach, aplikacje działające w tych kontenerach muszą być zaprojektowane do zapisu we współdzielonych magazynach danych, aby zapobiec uszkodzeniu danych.

Utwórz Kontener4 i WoluminDanych4

Użyj polecenia docker run, aby utworzyć nowy kontener o nazwie Kontener4 z dołączonym woluminem danych:

docker run -ti --name=Container4 -v DataVolume4:/datavolume4 ubuntu

Następnie utworzymy plik i dodamy tekst:

echo "This file is shared between containers" > /datavolume4/Example4.txt

Następnie opuścimy kontener:

exit

Powoduje to powrót do wiersza polecenia hosta, w którym utworzymy nowy kontener, który montuje wolumin danych z Kontener4.

Utwór Kontener5 i zamontuj woluminy z Kontener4

Zamierzamy stworzyć Kontener5 i zamontować woluminy z Kontener4:

docker run -ti --name=Container5 --volumes-from Container4 ubuntu

Sprawdźmy trwałość danych:

cat /datavolume4/Example4.txt
This file is shared between containers

Dodajmy teraz tekst z Kontener5:

echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt

Wreszcie wyjdziemy z kontenera:

exit

Następnie sprawdzimy, czy nasze dane są nadal obecne w Kontener4.

Zobacz zmiany dokonane w Kontener5

Sprawdźmy zmiany, które zostały zapisane w woluminie danych przez Kontener5 poprzez ponowne uruchomienie Kontener4:

docker start -ai Container4

Sprawdźmy zmiany:

cat /datavolume4/Example4.txt
This file is shared between containers
Both containers can write to DataVolume4

Teraz, gdy zweryfikowaliśmy, że oba kontenery były w stanie odczytywać i zapisywać z woluminu danych, opuścimy kontener:

exit

Ponownie – Docker nie obsługuje żadnego blokowania plików, więc aplikacje muszą same uwzględnić tę kwestię. Możliwe jest zamontowanie woluminu Docker jako tylko do odczytu, aby zapobiec przypadkowemu uszkodzeniu danych, gdy kontener wymaga dostępu tylko do odczytu, dodając :ro. Zobaczmy, jak to działa.

Uruchom Kontener6 i zamontuj wolumin tylko do odczytu

Po zamontowaniu woluminu w kontenerze zamiast odmontowywania go tak, jak w przypadku typowego systemu plików Linux, możemy zamiast tego utworzyć nowy kontener zamontowany tak, jak chcemy i, w razie potrzeby, usunąć poprzedni kontener. Aby wolumin był tylko do odczytu, dodajemy :ro na końcu nazwy kontenera:

docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu

Sprawdzimy stan trybu tylko do odczytu, próbując usunąć nasz przykładowy plik:

rm /datavolume4/Example4.txt
rm: cannot remove '/datavolume4/Example4.txt': Read-only file system

Wreszcie wyjdziemy z kontenera i wyczyścimy nasze testowe kontenery i woluminy:

exit

Teraz, gdy już skończyliśmy, posprzątajmy nasze kontenery i wolumin:

docker rm Container4 Container5 Container6
docker volume rm DataVolume4

W tym przykładzie pokazaliśmy, jak udostępniać dane między dwoma kontenerami przy użyciu woluminu danych oraz jak zamontować wolumin danych jako tylko do odczytu.

Podsumowanie

W tym wpisie utworzyliśmy wolumin danych, który pozwalał na zachowanie danych poprzez usunięcie kontenera. Udostępniliśmy woluminy danych między kontenerami, z zastrzeżeniem, że aplikacje będą musiały być zaprojektowane do obsługi blokowania plików, aby zapobiec uszkodzeniu danych. Na koniec pokazaliśmy, jak zamontować wspólny wolumin w trybie tylko do odczytu.