De “VOLUME”-instructie in DockerFile begrijpen

Hieronder staat de inhoud van mijn “Dockerfile”

FROM node:boron
# Create app directory
RUN mkdir -p /usr/src/app
# Change working dir to /usr/src/app
WORKDIR /usr/src/app
VOLUME . /usr/src/app
RUN npm install
EXPOSE 8080
CMD ["node" , "server" ]

In dit bestand verwacht ik VOLUME . /usr/src/appinstructie om te mounten
inhoud van de huidige werkmap in de host die moet worden gemount op /usr/src/app
map of container.

Laat me weten of dit de juiste manier is?


Antwoord 1, autoriteit 100%

Kortom: Nee, uw volumeinstructie is niet correct.

Dockerfile’s volumespecificeert een of meer volumes gegeven paden aan de containerzijde. Maar het staat de auteur van de afbeelding niet toe om een ​​hostpad op te geven. Aan de hostzijde worden de volumes gemaakt met een zeer lange ID-achtige naam in de Docker-root. Op mijn computer is dit /var/lib/docker/volumes.

Opmerking: omdat de automatisch gegenereerde naam extreem lang is en vanuit het perspectief van een mens niet logisch is, worden deze volumes vaak “naamloos” of “anoniem” genoemd.

Uw voorbeeld dat een ‘.’ gebruikt karakter zal niet eens op mijn machine worden uitgevoerd, ongeacht of ik de punt het eerste of tweede argument maak. Ik krijg deze foutmelding:

docker: Foutreactie van daemon: oci runtime-fout: container_linux.go:265: starten van containerproces veroorzaakte “process_linux.go:368: container init veroorzaakte “open /dev/ptmx: geen dergelijk bestand of map””.

Ik weet dat wat tot nu toe is gezegd waarschijnlijk niet erg waardevol is voor iemand die volumeen -vprobeert te begrijpen en het biedt zeker geen oplossing voor wat je probeert te bereiken. Dus hopelijk zullen de volgende voorbeelden wat meer licht werpen op deze problemen.

Minitutorial: Volumes specificeren

Gezien dit Docker-bestand:

FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2

(Voor de uitkomst van deze minitutorial maakt het geen verschil of we vol1 vol2of /vol1 /vol2specificeren — dit komt omdat de standaard werkmap binnen een Dockerfile is /)

Bouw het:

docker build -t my-openjdk

Uitvoeren:

docker run --rm -it my-openjdk

Voer in de container lsuit in de opdrachtregel en je zult zien dat er twee mappen zijn; /vol1en /vol2.

Als u de container uitvoert, worden er ook twee mappen of “volumes” aan de hostzijde gemaakt.

Terwijl de container draait, voer je docker volume lsuit op de hostmachineen je ziet zoiets als dit (ik heb het middelste deel van de naam vervangen door drie stippen voor de beknoptheid):

DRIVER    VOLUME NAME
local     c984...e4fc
local     f670...49f0

Terug in de container, voer touch /vol1/weird-ass-fileuit (maakt een leeg bestand op de genoemde locatie).

Dit bestand is nu beschikbaar op de hostcomputer, in een van de niet nader genoemde volumes lol. Het kostte me twee pogingen omdat ik eerst het eerste vermelde volume probeerde, maar uiteindelijk vond ik mijn bestand in het tweede vermelde volume, met behulp van deze opdracht op de hostcomputer:

sudo ls /var/lib/docker/volumes/f670...49f0/_data

Op dezelfde manier kunt u proberen dit bestand op de host te verwijderen, waarna het ook in de container wordt verwijderd.

Opmerking: de map _datawordt ook wel een “koppelpunt” genoemd.

Verlaat de container en vermeld de volumes op de host. Ze zijn weg. We gebruikten de vlag --rmbij het uitvoeren van de container en deze optie wist niet alleen de container bij het afsluiten, maar ook de volumes.

Voer een nieuwe container uit, maar specificeer een volume met -v:

docker run --rm -it -v /vol3 my-openjdk

Dit voegt toeeen derde volume en het hele systeem heeft uiteindelijk drie niet nader genoemde volumes. De opdracht zou zijn gecrasht als we alleen -v vol3hadden opgegeven. Het argument moet een absoluutpad binnende container zijn. Aan de hostzijde is het nieuwe derde volume anoniem en bevindt het zich samen met de andere twee volumes in /var/lib/docker/volumes/.

Er werd eerder vermeld dat de Dockerfileniet kan worden toegewezen aan een hostpad, wat een soort probleem voor ons vormt wanneer we proberen bestanden van de host naar de container te brengen tijdens runtime. Een andere -vsyntaxis lost dit probleem op.

Stel je voor dat ik een submap in mijn projectdirectory ./srcheb die ik wil synchroniseren met /srcin de container. Dit commando doet de truc:

docker run -it -v $(pwd)/src:/src my-openjdk

Beide zijden van het teken :verwachten een absoluut pad. De linkerkant is een absoluut pad op de hostmachine, de rechterkant is een absoluut pad binnen de container. pwdis een commando dat “huidige/werkmap afdrukt”. Als u de opdracht in $()plaatst, wordt de opdracht tussen haakjes geplaatst, wordt deze uitgevoerd in een subshell en wordt het absolute pad naar onze projectdirectory teruggegeven.

Alles bij elkaar genomen, neem aan dat we ./src/Hello.javain onze projectmap op de hostcomputer hebben met de volgende inhoud:

public class Hello {
    public static void main(String... ignored) {
        System.out.println("Hello, World!");
    }
}

We bouwen dit Docker-bestand:

FROM openjdk:8u131-jdk-alpine
WORKDIR /src
ENTRYPOINT javac Hello.java && java Hello

We voeren deze opdracht uit:

docker run -v $(pwd)/src:/src my-openjdk

Hierop wordt “Hallo wereld!” afgedrukt.

Het beste is dat we volledig vrij zijn om het .java-bestand bij een tweede run te wijzigen met een nieuw bericht voor een andere uitvoer – zonder de afbeelding opnieuw te hoeven bouwen =)

Laatste opmerkingen

Ik ben vrij nieuw bij Docker en de eerder genoemde “tutorial” weerspiegelt informatie die ik heb verzameld van een driedaagse hackathon op de commandoregel. Ik schaam me bijna dat ik geen links heb kunnen geven naar duidelijke Engels-achtige documentatie die mijn verklaringen ondersteunt, maar ik denk eerlijk gezegd dat dit te wijten is aan een gebrek aan documentatie en niet aan persoonlijke inspanning. Ik weet wel dat de voorbeelden werken zoals geadverteerd met mijn huidige setup, namelijk “Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce”.

De tutorial lost het probleem niet op “hoe specificeren we het pad van de container in de Dockerfile en laten we het run-commando alleen het hostpad specificeren”. Er is misschien een manier, ik heb hem alleen niet gevonden.

Ten slotte heb ik het gevoel dat het specificeren van volumein de Dockerfile niet alleen ongebruikelijk is, maar dat het waarschijnlijk een best practice is om volumenooit te gebruiken. Om twee redenen. De eerste reden die we al hebben geïdentificeerd: we kunnen het hostpad niet specificeren – wat een goede zaak is omdat Dockerfiles erg onverschillig zou moeten zijn voor de specifieke kenmerken van een hostmachine. Maar de tweede reden is dat mensen misschien vergeten de optie --rmte gebruiken bij het uitvoeren van de container. Men kan eraan denken om de container te verwijderen, maar vergeet het volume te verwijderen. En zelfs met het beste van het menselijk geheugen kan het een ontmoedigende taak zijn om erachter te komen welke van alle anonieme volumes veilig kunnen worden verwijderd.


Antwoord 2, autoriteit 40%

De officiële tutorial over docker zegt:

Een datavolume is een speciaal daarvoor bestemde directory binnen een of meer containers die het Union File System omzeilt. Gegevensvolumes bieden verschillende handige functies voor permanente of gedeelde gegevens:

  • Volumes worden geïnitialiseerd wanneer een container wordt gemaakt. Als de basisafbeelding van de container gegevens bevat op het opgegeven koppelpunt,
    dat bestaande gegevens op volume naar het nieuwe volume worden gekopieerd
    initialisatie. (Merk op dat dit niet van toepassing is bij het aankoppelen van een host
    map.)

  • Gegevensvolumes kunnen worden gedeeld en hergebruikt tussen containers.

  • Wijzigingen aan een datavolume worden direct doorgevoerd.

  • Wijzigingen in een gegevensvolume worden niet opgenomen wanneer u een afbeelding bijwerkt.

  • Gegevensvolumes blijven bestaan, zelfs als de container zelf wordt verwijderd.

In Dockerfilekunt u alleen de bestemming van een volume ineen container specificeren. bijv. /usr/src/app.

Wanneer u een container uitvoert, b.v. docker run --volume=/opt:/usr/src/app my_image, u magmaar hoeft het montagepunt niet op te geven (/opt) op de hostcomputer. Als u het argument --volumeniet opgeeft, wordt het koppelpunt automatisch gekozen, meestal onder /var/lib/docker/volumes/.


Antwoord 3, autoriteit 20%

Het specificeren van een volumeregel in een Dockerfile configureert een beetje metadata op je afbeelding, maar hoe die metadata wordt gebruikt is belangrijk.

Ten eerste, wat deden deze twee regels:

WORKDIR /usr/src/app
VOLUME . /usr/src/app

De regel WORKDIRdaar maakt de directory aan als deze niet bestaat, en werkt enkele metadata van afbeeldingen bij om alle relatieve paden te specificeren, samen met de huidige directory voor commando’s zoals RUNop die locatie zal zijn. De regel volumedaar specificeert twee volumes, één is het relatieve pad ., en de andere is /usr/src/app, beide zijn toevallig dezelfde map. Meestal bevat de regel volumeslechts één map, maar het kan er meerdere bevatten zoals u hebt gedaan, of het kan een json-geformatteerde array zijn.

U kunt geen volumebron specificeren in het Dockerfile: een veelvoorkomende bron van verwarring bij het specificeren van volumes in een Dockerfile is het proberen overeen te komen met de runtime-syntaxis van een bron en bestemming tijdens het bouwen van de afbeelding, dit werkt niet. De Dockerfile kan alleen de bestemming van het volume specificeren. Het zou een triviale beveiligingsexploit zijn als iemand de bron van een volume zou kunnen definiëren, omdat ze een gemeenschappelijke afbeelding op de docker-hub zouden kunnen bijwerken om de hoofdmap in de container te koppelen en vervolgens een achtergrondproces in de container te starten als onderdeel van een toegangspunt dat voegt logins toe aan /etc/passwd, configureert systemd om een ​​bitcoin miner te starten bij de volgende herstart, of zoekt in het bestandssysteem naar creditcards, SSN’s en privésleutels om naar een externe site te sturen.

Wat doet de VOLUME-regel?Zoals gezegd, stelt het sommige afbeeldingsmetadata in om te zeggen dat een map in de afbeelding een volume is. Hoe worden deze metadata gebruikt? Elke keer dat u een container van deze afbeelding maakt, dwingt docker die map om een ​​volume te zijn. Als u geen volume opgeeft in uw run-commando of bestand samenstellen, is de enige optie voor docker het maken van een anoniem volume. Dit is een lokaal benoemd volume met een lange unieke id voor de naam en geen andere indicatie waarom het is gemaakt of welke gegevens het bevat (anonieme volumes zijn waar gegevens verloren gaan). Als u het volume overschrijft en naar een benoemd of hostvolume wijst, gaan uw gegevens daarheen.

VOLUME breekt dingen:je kunt een volume niet uitschakelen dat eenmaal is gedefinieerd in een Dockerfile. En nog belangrijker, het RUNcommando in docker wordt geïmplementeerd met tijdelijke containers. Die tijdelijke containers krijgen een tijdelijk anoniem volume. Dat anonieme volume wordt geïnitialiseerd met de inhoud van uw afbeelding. Alle schrijfbewerkingen in de container van uw RUN-opdracht worden naar dat volume gedaan. Wanneer de opdracht RUNis voltooid, worden wijzigingen in de afbeelding opgeslagen en worden wijzigingen in het anonieme volume genegeerd.Daarom raad ik u ten zeerste af om een ​​volumein het Docker-bestand. Het resulteert in onverwacht gedrag voor downstreamgebruikers van uw afbeelding die de afbeelding willen uitbreiden met initiële gegevens op de volumelocatie.

Hoe moet u een volume specificeren?Om aan te geven waar u volumes aan uw afbeelding wilt toevoegen, geeft u een docker-compose.ymlop. Gebruikers kunnen dat wijzigen om de volumelocatie aan te passen aan hun lokale omgeving, en het legt andere runtime-instellingen vast, zoals publicatiepoorten en netwerken.

Iemand zou dit moeten documenteren!Dat hebben ze. Docker neemt waarschuwingen op over het VOLUME-gebruik in hun documentatie over de Dockerfilesamen met advies om de bron tijdens runtime te specificeren:

  • Het volume wijzigen vanuit de Dockerfile:als een buildstap de gegevens binnen het volume wijzigt nadat het is gedeclareerd,
    die wijzigingen worden verwijderd.

  • De hostdirectory wordt gedeclareerd tijdens runtime van de container:De hostdirectory (het koppelpunt) is van nature host-afhankelijk. Dit is
    om de draagbaarheid van afbeeldingen te behouden, aangezien een bepaalde hostmap niet kan worden
    gegarandeerd beschikbaar op alle hosts. Om deze reden kunt u niet
    mount een hostdirectory vanuit het Dockerfile. Het volume
    instructie ondersteunt het specificeren van een host-dirparameter niet. U
    moet het koppelpunt specificeren wanneer u de container maakt of uitvoert.

Antwoord 4, autoriteit 15%

Voor een beter begrip van de volume-instructie in dockerfile, laten we het typische volumegebruik leren in de officiële mysql-implementatie van docker-bestanden.

VOLUME /var/lib/mysql

Referentie:
https://github.com/docker-library/mysql/blob/3362baccb20145267 /8.0/Dockerbestand

De /var/lib/mysqlis de standaardlocatie van MySQL die gegevensbestanden opslaat.

Als u de testcontainer alleen voor testdoeleinden uitvoert, mag u het bevestigingspunt niet specificeren, bijvoorbeeld

docker run mysql:8

dan gebruikt de mysql-containerinstantie het standaard aankoppelpad dat is gespecificeerd door de volume-instructie in dockerfile. de volumes zijn gemaakt met een zeer lange ID-achtige naam in de Docker-root, dit wordt “naamloos” of “anoniem” volume genoemd. In de map van het onderliggende hostsysteem /var/lib/docker/volumes.

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

Dit is erg handig voor snelle testdoeleinden zonder dat het montagepunt hoeft te worden gespecificeerd, maar kan toch de beste prestaties krijgen door Volume te gebruiken voor gegevensopslag, niet de containerlaag.

Voor formeel gebruik moet u het aankoppelpad specificeren door het genoemde volume of bind-aankoppel te gebruiken, bijvoorbeeld

docker run  -v /my/own/datadir:/var/lib/mysql mysql:8

De opdracht koppelt de map /my/own/datadir van het onderliggende hostsysteem als /var/lib/mysql in de container. De datadirectory /my/own/datadir wordt niet automatisch verwijderd, zelfs niet de container verwijderd.

Gebruik van de officiële mysql-afbeelding (kijk in het gedeelte ‘Waar gegevens op te slaan’):

Referentie:
https://hub.docker.com/_/mysql/

Other episodes