# H3. Docker --- ## Inhoud 1. Van bare metal naar containers 2. Docker 3. Docker Compose --- # 3.1 Van bare metal naar containers --- ## Vroeger, toen de dieren nog spraken ...
Notes: Hoewel virtualisatietechnologieën al decennia bestaan, werden ze pas in de afgelopen twintig jaar praktisch en kostenefficiënt genoeg voor het gemiddelde bedrijf om ze breed toe te passen. Voor die tijd werd doorgaans slechts één besturingssysteem rechtstreeks op de fysieke hardware van een server geïnstalleerd (bare metal). Binnen dat ene besturingssysteem werden vervolgens alle benodigde services, zoals een website, mailserver, database of gameserver, op dezelfde machine uitgevoerd. --- ## Veel nadelen - Alle software (per klant) is geïnstalleerd op een enkele server met een enkel besturingssysteem - Heel veel files en directories op verschillende plaatsen - Moeilijk om tegelijk verschillende versies van bepaalde software te draaien - Bv. mariadb:9, mariadb:10, mariadb:11, ... - Als het besturingssysteem of een programma crasht, is er een kans dat alles crasht Notes: Dit bracht enkele nadelen met zich mee: - Een server kon slechts door één klant worden gebruikt, tenzij je werkte met meerdere accounts. - Bij het gebruik van veel services stonden de configuratiebestanden op verschillende locaties (bijvoorbeeld een website in `/var/www/html`, een database in `/var/lib/mysql`, configuratiebestanden in `/etc`, enzovoort). Sommige services werden geïnstalleerd via de package manager, andere handmatig in `/opt`. Dit maakte servers chaotisch en onoverzichtelijk: welke services draaien er op de server? Waar staan de configuratiebestanden van elke service? - Het was niet mogelijk om meerdere versies van software tegelijkertijd te draaien. Als je bijvoorbeeld `mariadb` 9 installeerde, gebruikte deze de database in `/var/lib/mysql` en de configuratiebestanden in `/etc/mysql/my.cnf`. Als je een andere versie van `mariadb` wilde draaien, zou deze dezelfde mappen en bestanden willen gebruiken. Om echt twee versies tegelijkertijd te draaien, moest je ze zo instellen dat er geen overlap was, wat soms complex was. - Aangezien er slechts één besturingssysteem draaide, kon een crash van een programma of het besturingssysteem ervoor zorgen dat de server volledig offline ging. --- - Sterke koppeling tussen soft- en hardware - Moeilijk en foutgevoelig om software of data te verhuizen tussen hardware - Bv. wanneer een schijf defect raakt - Kan hardware niet dynamisch schalen - Bv. als we meer RAM, opslag, ... nodig hebben - Heeft veel fysieke ruimte en elektriciteit nodig Notes: - Servers werden chaotisch en onoverzichtelijk door de vele services, waardoor het overzetten van data en services niet eenvoudig was. - Dynamisch schalen van servers was niet mogelijk: bijvoorbeeld, als een server te weinig RAM had, moest je de server uitschakelen en fysiek RAM toevoegen. - Een server per klant betekende dat bij een toename van het aantal klanten, je ook meer fysieke ruimte en elektriciteit moest voorzien voor elke nieuwe server. --- - De hardware wordt waarschijnlijk onderbenut - Verspilling van middelen == mislopen inkomsten
Notes: Bij het gebruik van een enkele server per klant werden servers vaak onderbenut. Als een klant bijvoorbeeld alleen een website wil hosten, is een hele server vaak overkill en zal deze voor het merendeel van de tijd idle draaien met een laag CPU- en RAM-verbruik. Als hostingbedrijf moet je echter wel een volledige server aanschaffen, betalen voor de elektriciteit en ruimte voorzien in je infrastructuur. Wat als we een server konden opsplitsen in meerdere virtuele servers om de kosten te drukken? --- ## Oplossing: virtual machines
Notes: Door de introductie van hypervisors werd het mogelijk om een enkele fysieke server op te delen in meerdere virtuele servers, ook wel Virtual Machines (VM's) genoemd. Een VM is een virtuele en geïsoleerde simulatie van een computer of server. In plaats van een volledige bare metal server te gebruiken voor één klant, kunnen we nu de server opsplitsen in meerdere VM's en slechts één daarvan toewijzen aan die klant. De overige VM's kunnen aan andere klanten worden toegewezen. Hierdoor worden de kosten van de server gedeeld over meerdere klanten en wordt er geen CPU/RAM meer verspild. --- ## Wat is een hypervisor? - Gespecialiseerd besturingssysteem of software waarop virtuele machines kunnen draaien - 2 types: - Type 1 (bare metal) - Besturingssysteem tussen hardware en virtuele machines - Meest efficiënt, minder verspilde middelen - Type 2 (gehost) - Programma bovenop besturingssysteem - Je kan reguliere programma's naast de hypervisor draaien - Vaak een pak eenvoudiger Notes: Een hypervisor is software die het mogelijk maakt om VM's op een apparaat te creëren. Er zijn twee soorten hypervisors: type 1 en type 2. - Type 1 hypervisors worden direct op de hardware geïnstalleerd. In plaats van een besturingssysteem zoals Windows of Linux, draait de hypervisor zelf als een lichtgewicht besturingssysteem. Binnen deze hypervisor kunnen vervolgens VM's worden aangemaakt. Een voorbeeld hiervan is [Proxmox](https://www.proxmox.com). - Type 2 hypervisors zijn programma's die binnen een bestaand besturingssysteem draaien. Je kan deze gebruiken als je al Windows of Linux hebt geïnstalleerd: je installeert de hypervisor als een normaal programma en maakt vervolgens VM's aan binnen deze hypervisor. Een voorbeeld hiervan is [VirtualBox](https://www.virtualbox.org). Beide typen hebben hun voor- en nadelen. Type 1 hypervisors zijn efficiënter omdat er geen besturingssysteem tussen de hypervisor en de hardware zit. Dit maakt de installatie en het gebruik echter ook complexer. Ze worden meestal gebruikt voor servers die specifieke diensten moeten aanbieden, niet op gewone desktops of laptops. Type 2 hypervisors zijn minder efficiënt, maar gemakkelijker te gebruiken. Ze worden vaak gebruikt door gebruikers op hun desktop of laptop om te experimenteren, te onderzoeken of om bij te leren. --- ### Type 1
--- ### Type 2
--- ### Ter vergelijking
--- Nog steeds veel nadelen: - Een VM per applicatie creëert veel overhead - Meerdere OS-lagen (bv. hypervisor type 2) - Het maken van een VM kost tijd - Bv. het elke keer installeren van het OS is omslachtig - Een VM is groot - Heeft veel schijfruimte nodig (vaak door het OS) - Veel complexer Notes: Hoewel VM's helpen om kosten te besparen en het verspillen van CPU/RAM te verminderen, zijn ze niet altijd de ideale oplossing: - Voor eenvoudige services is een volledige VM vaak overkill en verspilt het schijfruimte. Een Linux VM kan bijvoorbeeld gemakkelijk 15 GB innemen, terwijl een Windows VM vaak 50 GB nodig heeft. - Het opzetten van een nieuwe VM vanaf nul vereist het installeren van een besturingssysteem, wat tijdrovend kan zijn. - Het beheren van meerdere VM's kost veel tijd en moeite, omdat elke VM een volledige gesimuleerde computer is waarop dingen fout kunnen gaan. Hoewel VM's kostenbesparend zijn, blijft het beheren van veel systemen of geïsoleerde services complex. Wat als we services in kleine, lichte containers konden plaatsen die weinig schijfruimte en installatie vereisen? --- ## Volgende oplossing: containers
Notes: Hoewel VM's nog steeds een belangrijke rol spelen in IT, systeembeheer en netwerkinfrastructuur, hebben ze ook enkele nadelen. Daarom heeft containertechnologie de afgelopen tien jaar aan populariteit gewonnen. Deze trend werd grotendeels gedreven door [Docker](https://www.docker.com/), maar inmiddels zijn er meerdere containertechnologieën beschikbaar, zoals [Podman](https://podman.io/). In plaats van een hypervisor gebruiken containers een container runtime om ze te beheren en uit te voeren. Een container biedt een virtuele en geïsoleerde omgeving voor het draaien van een applicatie of service, maar verschilt fundamenteel van een VM. Terwijl een VM een volledige simulatie van een fysiek systeem omvat, inclusief hardwarecomponenten zoals CPU, RAM en opslag, werkt een container lichter en efficiënter. Containers delen de kernel van het besturingssysteem van de hostmachine, waardoor alleen de applicatie en de benodigde afhankelijkheden worden geïsoleerd binnen de container. Dit heeft aanzienlijke voordelen: - Snellere opstarttijden: Containers hoeven geen volledig besturingssysteem te booten bij elke start. - Minder overhead: Er is geen extra schijfruimte of systeembronnen nodig voor een apart OS binnen de container. - Efficiënt gebruik van resources: Containers benutten de onderliggende infrastructuur optimaal, zonder de zware belasting van een hypervisor en meerdere VM's. Kortom, containers bieden een lichtere, snellere en flexibelere manier om applicaties te draaien, waardoor ze een essentieel onderdeel zijn geworden van moderne softwareontwikkeling en infrastructuurbeheer. --- ## Even herhalen
--- ## Wanneer kies je welke technologie? - Virtuele machines - Bv. bij het uitrollen van infrastructuur - Containers - Bv. bij het uitrollen van applicaties - Kies afhankelijk van je situatie - Elk heeft zijn voor- en nadelen Notes: VM's en containers worden beide veel gebruikt voor het opzetten van systemen en het hosten van services. Bedrijven en organisaties vertrouwen vaak op VM's om verschillende services te hosten. VM's zijn ook zeer handig om te experimenteren of te testen voordat iets op echte hardware wordt uitgevoerd. Containers worden vaak gebruikt om een enkele service of applicatie te isoleren en uit te voeren op verschillende Windows- en Linux-versies. Beide technologieën hebben hun plaats in systeem- en netwerkbeheer. Het is belangrijk om de voor- en nadelen van elk te kennen, zodat je later kan bepalen welke technologie het beste is voor een specifieke situatie. --- # 3.2 Docker --- ## Images - Een bestand dat alles bevat wat nodig is om een applicatie te draaien - "sjabloon", "blauwdruk", "template", ... - Gedefinieerd door een `Dockerfile` - Onveranderlijk ("immutable") - Bestaat uit verschillende lagen - Maak een image als je je applicatie wil dockerizen zodat anderen deze gemakkelijk kunnen gebruiken Notes: Een programmeur die zijn applicatie wil delen met anderen, moet zijn programma compileren voor het juiste platform (x86/x64, ARM, ...) en voor het juiste besturingssysteem (Windows, Mac, Linux, Android, iOS, ...). Daarnaast moeten ook alle benodigde bibliotheken, hulpprogramma's en andere afhankelijkheden gebundeld worden met het programma. Dit maakt het verspreiden en uitvoeren van het programma op verschillende systemen complex voor zowel de ontwikkelaar als de gebruiker. Docker biedt hiervoor een eenvoudiger alternatief. Een ontwikkelaar kan een image maken: een sjabloon van een gevirtualiseerde omgeving waarin het programma zit, samen met alle benodigde afhankelijkheden. Een image wordt gecreëerd door code te plaatsen in een Dockerfile. De commando's in een Dockerfile definiëren verschillende lagen die samen de image vormen. Eenmaal de image is gemaakt, kan deze niet meer veranderd worden en is deze klaar om te verspreiden aan gebruikers. Zij kunnen op basis van deze image een container maken om daarin het programma uit te voeren. --- ## Containers - Een draaiend exemplaar van een image - Een geïsoleerde omgeving voor jouw code - Heeft geen kennis van jouw systeem - Is een image met een veranderlijke (mutable) laag erboven - Gebruik een container als je eenvoudig een bestaande applicatie wil gebruiken waarvoor een image bestaat - Je zal waarschijnlijk vaker containers gebruiken dan images maken Notes: Wanneer een gebruiker een programma wil uitvoeren, kan hij een container maken op basis van een image. Een container is eigenlijk een kopie van de image met een beschrijfbare laag bovenop. De container isoleert het programma van de host[^1]. Het programma draait in een virtuele omgeving en merkt niet dat het binnen een container draait; het denkt dat het rechtstreeks op de host draait. Hierdoor heeft het programma geen toegang tot de bestanden van de host. Kortom, ontwikkelaars maken een image van hun programma, en gebruikers maken een container op basis van die image om het programma uit te voeren. Voor het uploaden en downloaden van images kan je gebruikmaken van [DockerHub](https://hub.docker.com/), de online repository van Docker. [^1]: Definitie van de term "host" In de context van servers, virtual machines (VM's), en containers, verwijst "host" naar verschillende concepten afhankelijk van de omgeving: 1. Servers: - Host: Een fysieke machine die diensten aanbiedt aan andere computers in een netwerk. Bijvoorbeeld, een webserver die websites host. 2. Virtual Machines (VM's): - Host Machine: De fysieke machine waarop de hypervisor draait en waarop de VM's worden uitgevoerd. - Guest Machine: De virtuele machine zelf die draait op de host machine. - Host OS: Het besturingssysteem dat op de fysieke machine draait. - Guest OS: Het besturingssysteem dat binnen de VM draait. 3. Containers: - Host: De fysieke of virtuele machine waarop de container runtime (zoals Docker) draait. - Container Host: De machine die de containers uitvoert. - Container: Een lichtgewicht, geïsoleerde omgeving die een applicatie en alle benodigde afhankelijkheden bevat. In alle gevallen verwijst "host" naar de machine of omgeving die de basis biedt voor het draaien van andere systemen of applicaties. --- ## Hoe werken images en containers samen? - Bv. compiled program [](https://mermaid.live/edit#pako:eNqFjktPwzAQhP-KtacipVGedfGBCxw5II5gDm6yeUiJbbk2FKr8d-xWJQVVxfJhZ8ffjPdQqRqBQTOoj6oTxpLHZy6JP1vlTIXBfuVwFCQosti8x0Qqi1rUcaX1DYe3I4I7rJwVmyEgs_hN-P1MzCVkubw7C_gbeLC1Ua0RY-rTn47jKZUYJ2UvW7JIL33nnM6u0dl_dH6NzgMNEbSmr4FZ4zCCEc0ogoR9yOVgOxyRA_NjjY1wg-XA5eQxLeSLUuOJNMq1HbBGDFuvnK6FxYdehOqfJyhrNPfKSQsspYcIYHvYActpEa-SdXKbUrrO8ywpIvgEVtC48BYty1WW-1tMEXwdSpN4TcvpG1d0rCU) Notes: Het concept van een image en een container kunnen we vergelijken met het uitvoeren van een programma, zoals NotePad. Op je SSD of harde schijf bestaat NotePad als een exe-bestand. Dit bestand zelf wordt nooit aangepast, het blijft een statisch en onveranderlijk programma. Pas wanneer je het uitvoert - bijvoorbeeld door erop te dubbelklikken - wordt NotePad geladen in het RAM-geheugen. Op dat moment ontstaat er een actieve instantie van het programma waarin je een tekst kan typen. Wil je tegelijkertijd een ander bestand bewerken? Dan start je eenvoudig een nieuwe instantie van NotePad door opnieuw op het exe-bestand te dubbelklikken. Elke keer dat je NotePad opent, wordt er een aparte kopie van het programma in het RAM geladen. Elke instantie werkt onafhankelijk van de andere en heeft zijn eigen tijdelijke geheugen waarin tekst wordt bewerkt. Toch blijft de originele exe op de schijf onveranderd. Dit principe lijkt sterk op de werking van images en containers in containertechnologie: - Een image is vergelijkbaar met het exe-bestand: het is een onveranderlijke blauwdruk die op schijf staat opgeslagen. - Een container is als een draaiende instantie van dat programma: zodra een container op basis van een image wordt gestart, wordt er een actieve en aanpasbare laag toegevoegd waarin gegevens kunnen worden bewerkt, zonder dat de originele image verandert. - Net zoals je meerdere exemplaren van NotePad kan openen, kan je meerdere containers draaien op basis van dezelfde image, waarbij elke container zijn eigen onafhankelijke omgeving en gegevens heeft. Kortom, een image vormt de basis, terwijl containers de dynamische en geïsoleerde uitvoeringen ervan zijn, net zoals meerdere geopende instanties van een programma. ---
--- [](https://mermaid.live/edit#pako:eNp9kD9vgzAQxb-KdVMiEcTfQD10ScZOHVt3cMEYq_gcuaZNi_juNaCkycLJkv3u3e-d5AEqUwug0HTmu2q5deTpmSHxVZvqQ9hGdeKVwfEqGLwtvtJcTtZ8k837V0hQKjxvrxP_CWS3e1yAG3ZuVgYdVyhs7KMOF7EkEdsjKpRkE2_v196jyRqarKLpGppOKAQgraqBOtuLALSwmk8ShimUgWuF9p9C_bMWDe87x4Dh6LETxxdj9IW0ppct0IZ3n171p5o7cVRcWq6vXSuwFvZgenRAk3jOADrAGWhaZOE-KqOHuCjKNE2iLIAfoFkRZt4q8nyfpP5kYwC_89YoLIs8uql4_AOh9pvp) --- ## Dockerfile - De source code van een image ```docker # Base image (with tag) FROM ubuntu:22.04 # Install app dependencies RUN apt-get update && apt-get install -y python3 python3-pip RUN pip install flask==2.1.* # Install app COPY hello.py / # Final configuration ENV FLASK_APP=hello EXPOSE 8000 CMD flask run --host 0.0.0.0 --port 8000 ``` Notes: Een image wordt beschreven aan de hand van code in een `Dockerfile`. Een tutorial kan je vinden op: https://docs.docker.com/build/building/packaging/ . --- ## DockerHub - Een centrale repository op het internet: https://hub.docker.com/ - Je kan er images van downloaden om te gebruiken - Je kan jouw eigen images uploaden zodat anderen die kunnen downloaden en gebruiken
Notes: [DockerHub](https://hub.docker.com) is de meest bekende repository voor Docker images. Hier kan je zelf images uploaden zodat anderen jouw programma kunnen gebruiken, of kan je er images van downloaden om een programma uit te voeren via Docker. --- ## Docker workflows
Notes: Deze afbeelding toont de belangrijkste Docker-commando's en hoe ze met elkaar samenhangen. Hieronder worden ze kort uitgelegd, in de labo's zal je ze in detail leren gebruiken. - `docker build`: Bouwt een image op basis van een Dockerfile. De instructies in het Dockerfile worden stap voor stap uitgevoerd om een image te creëren. - `docker tag`: Kent een tag (label) toe aan een image, bijvoorbeeld om een versienummer toe te voegen. - `docker run`: Maakt een container aan op basis van een image en start deze op. Dit is het commando dat je het vaakst zal gebruiken. - `docker stop`, `docker start`, `docker restart`: Beheert de levenscyclus van een draaiende container. Een gestopte container blijft bestaan en kan opnieuw gestart worden. - `docker commit`: Slaat de huidige staat van een container op als een nieuwe image. Dit is handig als je handmatig wijzigingen hebt aangebracht in een container en deze wil bewaren. - `docker push`: Uploadt een image naar een Docker registry (zoals DockerHub) zodat anderen deze kunnen downloaden. - `docker pull`: Downloadt een image van een Docker registry naar je lokale machine. - `docker save` en `docker load`: Exporteert een image naar een tar-bestand (`backup.tar`) of importeert een image vanuit een tar-bestand. Dit is handig voor het maken van back-ups of het verplaatsen van images zonder een registry te gebruiken. --- ## In de praktijk - "_Ik heb een applicatie ontwikkeld en wil deze delen met anderen_" - Maak een image (`Dockerfile`) en upload deze naar DockerHub - "_Ik wil een bestaande applicatie gebruiken_" - Haal de image van DockerHub en start een container op basis van die image --- ## Demo: run een container ```console sudo docker run hello-world ``` Notes: Zie labo [Docker containers](/h3/5-docker-containers) ---
--- - Welke images en containers hebben we nu? - Kan je zien dat container `thirsty_feynman` is gemaakt van image `hello-world`? - De naam wordt random gegenereerd en zal bij iedereen telkens anders zijn - Tip: Vergeet de `-a` optie niet om gestopte containers te zien!
--- ## Port bindings - Docker containers zijn geïsoleerd - Ze kunnen de host (bv. je laptop) niet "zien" of beïnvloeden - Ze denken dat ze zelf een volwaardige computer zijn - We kunnen alleen zien wat ze op de terminal laten zien - Vaak willen we verbinding maken met de applicatie - Bv. een webserver - Oplossing: port bindings - `-p port_op_host:port_in_container` Notes: Een port binding is het proces waarbij een containerpoort wordt gekoppeld aan een poort op de host. Dit maakt het mogelijk om toegang te krijgen tot de services die binnen de container draaien vanaf de buitenwereld. Zonder port binding zou de container geïsoleerd blijven en niet bereikbaar zijn vanaf de host of het netwerk. Dit is essentieel voor het draaien van netwerkservices zoals webservers, databases, API's, ... . Door een poort van de container te binden aan een poort op de host, kunnen gebruikers en andere services communiceren met de applicatie binnen de container. Stel dat je een webserver in een container draait en je wil deze toegankelijk maken via poort 8123 op jouw laptop. Je kan dit bereiken door de container poort 80 (standaard HTTP-poort gebruikt door de webserver binnen de container) te binden aan poort 8123 (een poort naar keuze) op de laptop met `-p 8123:80`. --- ## Demo: maak een port binding - Poort 8123 op de host (bv. jouw Ubuntu VM) - Poort 80 binnen de container (bv. nginx webserver) - Nu kan je surfen naar http://localhost:8123 - Wanneer je de container stopt, kan je niet meer naar de website surfen ```console sudo docker run -p 8123:80 nginx ```
Notes: Zie labo [Port bindings en volumes](/h3/6-port-bindings-en-volumes) --- ```language-plantuml @startuml scale 2 actor user component localhost { portin 8123 user --> 8123 component container { portin 80 8123 --> 80 } } @enduml ``` --- ## Volumes - Veranderingen in een container gaan verloren wanneer je de container verwijdert - We willen databases, configuratiebestanden, ... behouden - We willen dat de container toegang heeft tot bestanden op de host (bv. jouw laptop) - Bv. we willen dat nginx de website op de host kan opdienen - Oplossing: volumes - `-v volume_naam:map_in_container` (named volume) - `-v map_op_host:map_in_container` (bind mount) Notes: Wanneer een Docker container wordt verwijderd, gaan alle wijzigingen binnenin de container verloren. Vaak willen we ook bestanden delen tussen de hostmachine en de container (bijvoorbeeld een website voor een webserver). Volumes in Docker bieden een manier om gegevens persistent op te slaan buiten de levenscyclus van een container en worden gebruikt om data te delen tussen de host en de container, of tussen meerdere containers. Volumes zijn de voorkeursmethode voor het bewaren van data in Docker, omdat ze onafhankelijk van de container worden beheerd en daardoor niet verloren gaan wanneer een container wordt verwijderd. Wanneer je in Docker met data persistence werkt, heb je twee belangrijke opties: named volumes en bind mounts. Beide methoden laten je toe om gegevens buiten de container op te slaan, zodat deze niet verloren gaan bij het stoppen of verwijderen van een container. Toch zijn er belangrijke verschillen tussen beide technieken, afhankelijk van hoe en waar je de data wil opslaan en gebruiken. - Named volumes worden beheerd door Docker zelf. Ze bevinden zich in de Docker storage directory op de host (`/var/lib/docker/volumes/` op Linux) en worden onafhankelijk van de container beheerd. Wanneer je een named volume aanmaakt, hoef je enkel een naam te geven: ```bash docker volume create mijn-volume ``` En je koppelt het aan een container als volgt: ```bash docker run -v mijn-volume:/data my-container ``` Hier wordt het volume "mijn-volume" gebruikt om aan een map `/data` binnen de container te koppelen. - Bij een bind mount wordt een specifieke map op de host gekoppeld aan een map in de container. Dit is handig voor het delen van bestanden en directories tussen de host en de container, en zorgt ervoor dat je rechtstreeks toegang hebt tot de bestanden. Je kan deze dus bewerken vanop de host met de tools die op de host geïnstalleerd zijn. Een bind mount opzetten gebeurt door een specifiek pad op de host te koppelen aan een map in de container: ```bash docker run -v /home/user/project:/app my-container ``` Hier wordt de map `/home/user/project` op de host gekoppeld aan een map `/app` binnen de container. --- ## Demo: maak volumes - Map `./website` op host (bv. jouw Ubuntu VM). - Maak een website en plaats deze hier. - Map `/usr/share/nginx/html` binnen de container (bv. nginx webserver) ```console sudo docker run -p 8123:80 -v ./website/:/usr/share/nginx/html nginx ``` - Gebruik `ro` als je niet wil dat Docker de bestanden op de host kan bewerken (**r**ead**o**nly). ```console sudo docker run -p 8123:80 -v ./website/:/usr/share/nginx/html:ro nginx ``` Notes: Zie labo [Port bindings en volumes](/h3/6-port-bindings-en-volumes) ---
--- ## Tags - Bepaalt een specifieke versie van een image - Geen tag betekent standaard de `latest` tag - Dit kan breaking changes introduceren tijdens een update! Notes: Tags in Docker worden gebruikt om verschillende versies van een Docker image te identificeren en te beheren. Een tag is een label dat je aan een image kan geven om deze te onderscheiden van andere images. Dit is vooral handig voor versiebeheer en het bijhouden van verschillende stadia van een applicatie, zoals `v1.0`, `beta`, enz. Standaard gebruikt Docker de tag `latest` als er geen specifieke tag wordt opgegeven. Hoewel dit handig kan zijn, is het geen goed idee om `latest` te gebruiken voor productieomgevingen. Het is namelijk onduidelijk welke versie van de image daadwerkelijk wordt gebruikt, wat kan leiden tot onverwachte problemen wanneer de `latest` tag wordt bijgewerkt naar een nieuwe versie en de image wordt geüpdatet. --- - Gebruik tags om steeds een bepaalde versie te gebruiken ```console sudo docker run -p 8123:80 -v ./website/:/usr/share/nginx/html:ro nginx:1.24.0 ``` - Tags worden soms gebruikt om onderscheid te maken op basis van andere aspecten dan de versie, zoals de basisimage - Bv. nginx image gebaseerd op ubuntu, alpine, ... ---
---
--- ## Environment variables - Soms hebben containers extra informatie nodig - Bv. root password voor container, timezone, ... - `-e VARIABLE=waarde` Notes: Environment variables in Docker worden gebruikt om configuratie-instellingen aan containers door te geven. Ze bieden een flexibele manier om parameters zoals database-URL's, wachtwoorden, API-sleutels en andere configuratiegegevens in te stellen zonder de image zelf te hoeven aanpassen. Dit maakt het mogelijk om dezelfde image in verschillende omgevingen te gebruiken door eenvoudigweg de environment variables aan te passen. Stel dat je een Docker-container hebt die een webapplicatie draait en je wil de database-URL en het wachtwoord instellen via environment variables. ```bash sudo docker run -d --name webapp -e DATABASE_URL=mysql://user:password@host:3306/dbname -e DATABASE_PASSWORD=my-secret-password myapp:latest ``` - `-e DATABASE_URL=mysql://user:password@host:3306/dbname` stelt de environment variable `DATABASE_URL` in met de waarde van de database URL. - `-e DATABASE_PASSWORD=my-secret-password` stelt de environment variable `DATABASE_PASSWORD` in met het wachtwoord voor de database. Binnen de container kan de applicatie deze environment variables lezen en gebruiken om verbinding te maken met de database. Dit maakt het eenvoudig om de configuratie te wijzigen zonder de image opnieuw te hoeven bouwen. Environment variables bieden een krachtige manier om configuraties te beheren en zorgen voor flexibiliteit en herbruikbaarheid van Docker images in verschillende omgevingen. --- ## Extra's - Probeer deze kleine spelletjes uit, kan je verbinden en spelen? - https://hub.docker.com/r/modem7/wordle - https://hub.docker.com/r/defnotgustavom/tetris - Host je eigen Minecraft-server, kan je verbinden en spelen? Blijft de wereld bestaan als je de container verwijdert en een nieuwe start? - https://docker-minecraft-server.readthedocs.io/en/latest/ --- # 3.3 Docker Compose --- ## Waarom een extra tool?
--- - Docker op de CLI kan leiden tot lange commando's ```console sudo docker run -p 8123:80 -v ./website/:/usr/share/nginx/html:ro nginx:1.24.0 ``` - We willen dat meerdere containers samenwerken, in een geïsoleerd netwerk - Bv. nginx webserver + database voor website. - Database moet niet bereikbaar zijn voor de buitenwereld --- ## `docker-compose.yml` - Geen configuratie meer op de CLI - Alles in `docker-compose.yml` - [Infrastructure as code](https://www.ibm.com/topics/infrastructure-as-code) - Zeer handig in combinatie met VCS zoals `git`! - Gewoon een YAML-bestand Notes: Docker Compose is een tool die is ontworpen om het beheer van multi-container Docker-applicaties te vereenvoudigen. Hoewel Docker zelf krachtig is voor het beheren van individuele containers, wordt het complexer wanneer je meerdere containers moet coördineren die samen een applicatie vormen. Docker Compose biedt een oplossing voor dit probleem door een eenvoudige manier te bieden om meerdere containers te definiëren en te beheren. - Eenvoudige configuratie: Met Docker Compose kan je alle services van je applicatie definiëren in een enkel `docker-compose.yml`-bestand. Dit bestand beschrijft hoe de containers moeten worden gebouwd, welke netwerken ze moeten gebruiken, welke volumes moeten worden gekoppeld, en welke environment variables moeten worden ingesteld. - Multi-container applicaties: Docker Compose maakt het eenvoudig om meerdere containers te beheren die samen een applicatie vormen. Bijvoorbeeld, een webapplicatie die een webserver, een database en een cache-service nodig heeft, kan allemaal worden gedefinieerd en beheerd met één enkel `docker-compose.yml` bestand. - Geïsoleerde netwerken: Docker Compose creëert automatisch een geïsoleerd netwerk voor de containers in een applicatie, waardoor ze gemakkelijk met elkaar kunnen communiceren zonder interferentie van andere containers op de host. - Vermijden van lange Docker-commando's: In plaats van lange en complexe Docker-commando's te typen voor elke container, kan je eenvoudigweg `docker compose up` gebruiken om alle containers te starten zoals gedefinieerd in het `docker-compose.yml`-bestand. - Infrastructure as Code: Docker Compose past het principe van Infrastructure as Code (IaC) toe, waarbij de infrastructuurconfiguratie wordt vastgelegd in code (in dit geval een YAML-bestand). Dit maakt het eenvoudig om de configuratie te beheren, te versioneren en te delen via versiebeheersystemen zoals `git`. - Herhaalbare omgevingen: Docker Compose zorgt ervoor dat je ontwikkel-, test- en productieomgevingen consistent zijn. Het `docker-compose.yml` bestand kan worden gedeeld en gebruikt om dezelfde omgeving op verschillende machines op te zetten. - Gemakkelijk opstarten en stoppen: Met eenvoudige commando's zoals `docker compose up` en `docker compose down` kan je alle containers in je applicatie opstarten en stoppen. Dit maakt het beheer van je applicatie veel eenvoudiger. --- ## Voorbeeld 1 ```console sudo docker run -p 8123:80 -v ./website/:/usr/share/nginx/html:ro nginx:1.24.0 ``` wordt ```yml services: website: image: nginx:1.24.0 ports: - 8123:80 volumes: - ./website/:/usr/share/nginx/html:ro ``` Notes: Kopieer deze code naar een bestand `docker-compose.yml` in een lege folder in jouw VM. Plaats in die folder ook een subfolder `website` met een eenvoudige `index.html` erin. Ga naar die folder in de terminal en voer het commando `docker compose up` uit. Open de browser en surf naar http://localhost:8123 om de website te bekijken. --- - Gebruik `docker compose up` om alle services (containers) en het interne netwerk aan te maken en te starten - Gebruik `-d` of `--detach` om alles op de achtergrond te laten draaien - Gebruik `docker compose down` om de containers te stoppen en te vernietigen ---
--- - Andere handige commando's - Lijst van draaiende compose projecten: `docker compose ls` - Lijst van images gebruikt door de gemaakte containers: `docker compose images` - Lijst van draaiende containers: `docker compose ps` - `-a` toont ook gestopte containers ---
--- ## Voorbeeld 2 ```yml services: database: image: mariadb environment: MARIADB_ROOT_PASSWORD: g8reg6qerg4rg6qr5e1g MARIADB_DATABASE: some_db MARIADB_USER: some_user MARIADB_PASSWORD: s1gqer4gqerqrgg32 adminer: image: adminer depends_on: - database ports: - 8123:8080 ``` Notes: Kopieer deze code naar een bestand `docker-compose.yml` in een lege folder in jouw VM. Ga naar die folder in de terminal en voer het commando `docker compose up` uit. Open de browser en surf naar http://localhost:8123 om de Adminer web interface te bekijken. Gebruik de credentials uit het Docker Compose bestand om aan te melden, de server is `database`. ---
--- - Docker-netwerk heeft zijn eigen DNS - Geen IP-adressen nodig - Servicenaam in `docker-compose.yml` (`database`, `adminer`) zijn de container DNS-namen Notes: De naam van een container in Docker Compose is hetzelfde als de naam van de service die je definieert in het bestand `docker-compose.yml`. Bijvoorbeeld, als je een service `database` noemt, dan is de containernaam ook `database`. Docker-netwerken hebben hun eigen ingebouwde DNS (Domain Name System). Dit betekent dat je geen IP-adressen hoeft te gebruiken om containers met elkaar te laten communiceren. --- - Probeer in te loggen als root
--- - Probeer in te loggen vanaf je host met een client op de CLI:
- Dat lukt niet ... - Er is geen port mapping voor `database` - Normaal zou de database niet toegankelijk mogen zijn buiten het lokale netwerk - Best practice voor cybersecurity! Notes: Als je een website hebt die een database gebruikt, is het een slechte gewoonte om de database toegankelijk te maken voor buitenaf: - Beveiliging: Als je de database toegankelijk maakt voor buitenaf, stel je deze bloot aan potentiële aanvallen van buitenaf. Dit kan leiden tot datalekken, gegevensverlies of andere beveiligingsproblemen. Alleen de website heeft toegang nodig tot de database. Door de toegang te beperken tot alleen de website, kan je beter controleren wie toegang heeft tot de gegevens en de kans op ongeautoriseerde toegang verminderen. - Netwerkverkeer: Door de database alleen toegankelijk te maken voor de website, beperk je het netwerkverkeer tot het interne netwerk. Dit kan de prestaties verbeteren en de kans op netwerkgerelateerde problemen verminderen. In plaats daarvan kan je de database in een privé-netwerk plaatsen dat alleen toegankelijk is voor de webserver. Dit kan eenvoudig worden gedaan met Docker Compose door de database en de webserver in hetzelfde netwerk te plaatsen en de databasepoort niet naar buiten door te sturen. --- - Een port mapping is niet nodig voor `database` - `adminer` kan het bereiken op het lokale Docker Compose netwerk - `adminer` moet wel een poort mappen - Anders kan de host (bv. jouw laptop) niet binnen het Docker Compose netwerk komen - Je wil immers de `adminer` web interface kunnen bezoeken --- ```language-plantuml @startuml scale 2 skinparam componentStyle rectangle actor user component localhost { portin 8123 user --> 8123 component docker-compose { component database { portin 3306 } component adminer { portin 8080 8123 --> 8080 } } } @enduml ``` --- Als je het echt wil (bv. voor ontwikkeling), kan je een port mapping toevoegen: ```yml services: database: image: mariadb ports: - 3306:3306 environment: MARIADB_ROOT_PASSWORD: g8reg6qerg4rg6qr5e1g MARIADB_DATABASE: some_db MARIADB_USER: some_user MARIADB_PASSWORD: s1gqer4gqerqrgg32 adminer: image: adminer depends_on: - database ports: - 8123:8080 ``` --- ```language-plantuml @startuml scale 2 skinparam componentStyle rectangle actor user component localhost { portin 8123 portin 3306 user --> 8123 user --> 3306 component docker-compose { component database { portin 3306. 3306 --> 3306. } component adminer { portin 8080 8123 --> 8080 } } } @enduml ``` ---
--- ## Voorbeeld 3 ```yml services: database: image: mariadb:11 volumes: - ./database/:/var/lib/mysql environment: MARIADB_ROOT_PASSWORD: g8reg6qerg4rg6qr5e1g MARIADB_DATABASE: wordpress_db MARIADB_USER: wordpress_user MARIADB_PASSWORD: s1gqer4gqerqrgg32 wordpress: image: wordpress:6.9.0 depends_on: - database ports: - "8123:80" environment: WORDPRESS_DB_HOST: database:3306 WORDPRESS_DB_NAME: wordpress_db WORDPRESS_DB_USER: wordpress_user WORDPRESS_DB_PASSWORD: s1gqer4gqerqrgg32 ``` --- ## Let op! - Geen IP-adressen nodig - Bv. `WORDPRESS_DB_HOST: database:3306` - Het Docker Compose netwerk heeft zijn eigen DNS - Servicenaam (`database`, `wordpress`) zijn de container DNS-namen Notes: In Docker Compose netwerken is het niet nodig om IP-adressen te gebruiken. In plaats daarvan kan je de servicenaam gebruiken, zoals `database` of `wordpress`, die fungeren als DNS-namen voor de containers. Bv. `WORDPRESS_DB_HOST: database:3306` verwijst naar de `database` container op poort 3306. --- - Wees voorzichtig met `localhost` - ⚠️ Bijna nooit nodig in `docker-compose.yml` - Waarschijnlijk een fout! - Bv. `WORDPRESS_DB_HOST: localhost:3306` zou zoeken naar de database in zijn eigen (`wordpress`) container in plaats van de `database` container - Onthoud: `docker-compose.yml` virtualiseert hier 2 computers in een netwerk Notes: Het gebruik van `localhost` in een `docker-compose.yml` bestand is bijna nooit nodig en kan vaak een fout zijn. Bv. `WORDPRESS_DB_HOST: localhost:3306` zou proberen verbinding te maken met een database in de `wordpress` container zelf, in plaats van de `database` container. Het is belangrijk om te onthouden dat `docker-compose.yml` twee computers in een netwerk virtualiseert, waardoor `localhost` niet naar de juiste container verwijst. --- ## Er ging iets mis, wat nu? - Bekijk de logs van alle services: `docker compose logs` - Bekijk de logs van een of meer services: `docker compose logs
...` - Bv. - `docker compose logs wordpress` - `docker compose logs database` - `docker compose logs wordpress database` - Gebruik `-f` of `--follow` om de logs live te volgen - Vergelijkbaar met `tail -f` ---
--- ## Dit is nog maar het begin! - https://docs.docker.com/get-started/overview/ - https://docs.docker.com/compose/ - https://www.linuxserver.io/ - https://github.com/awesome-selfhosted/awesome-selfhosted - https://kubernetes.io/ --- Notes: - https://www.backblaze.com/blog/vm-vs-containers/ - https://www.atlassian.com/microservices/cloud-computing/containers-vs-vms - https://ubuntu.com/blog/containerization-vs-virtualization - https://learn.microsoft.com/en-us/virtualization/windowscontainers/about/containers-vs-vm - https://www.docker.com/blog/how-to-use-the-official-nginx-docker-image/ - https://docs.docker.com/get-started/08_using_compose/