C’t Magazine

Containers met elkaar verbinden: User Defined Networks

Containers elkaar laten vinden: User Defined Networks

- Mirko Dölle

Als je je webserver en database elk in een eigen Docker parkeert, moet je webserver wel weten hoe hij de database kan benaderen. Deze containers werden voorheen aan elkaar geknoopt, maar die manier van werken is verouderd en komt te vervallen. De nieuwe manier van werken: User Defined Networks met een embedded DNS-server voor je containers, inclusief name-resolving.

Voor elke service een eigen container, dat is de kern van het werken met Docker. Als je dit zeer letterlijk neemt, is het bye-bye LAMP-server met Linux, Apache, MySQL en PHP en stap je over op minimaal een webserver- plus databaseco­ntainer. Soms wordt PHP ook nog in een eigen container geparkeerd. Maar het beoogde doel, de services los van elkaar draaien, heeft ook zijn nadelen: er moeten communicat­iekanalen worden ingesteld.

Bij de traditione­le starre LAMP-server is de configurat­ie niet zo van belang. De webserver en MySQL-database draaien op dezelfde host en wisselen lokaal gegevens uit via de loopbackin­terface of een socket. Als ze in Docker-containers zijn gezet gaat het om van elkaar gescheiden processen met een eigen 'werkruimte' en ip-adres die wel met elkaar in contact moeten blijven. Als voorbeeld hebben we een webserver met PHPMYAdmin als database-frontend en een losse MySQL-database. Ze zijn allebei als klant-en-klare container-image te vinden via Docker Hub.

Start de webserver-container met het commando

docker run --name webserver \ -p 80:80 -d phpmyadmin/phpmyadmin

Nu wordt eerst eenmalig de PHPMyAdmin­image gedownload van Docker Hub. We geven niet aan in welk netwerk de container moet komen, dus Docker hangt hem aan de default bridge met als netwerkran­ge 172.17.0.0/24. Daardoor krijgt de MySQLdatab­ase een ip-adres uit het subnet van de standard bridge bridge. Welke dat is, merk je pas na het opstarten via docker network inspect bridge, bijvoorbee­ld 172.17.0.2.

Oude link

Je hebt in de standaard bridge geen invloed op het uitgeven van ip-adressen uit de netwerkran­ge. Je kunt niet eens handmatig een ip-adres aan een container toewijzen. Dat wordt een probleem als je de database-container opstart:

docker run --name dbserver \ -e MYSQL_ROOT_PASSWORD=123456 \ -d mysql:8

Ook deze container belandt zonder afwijkende netwerkcon­figuratie in de standard bridge en krijgt een ip-adres uit de range 172.170.0.0/24. Ook al blijven de ip-adressen langere tijd hetzelfde (alleen een herstart van de hosts kan adressen husselen), het door Docker aangewezen ip-adres van de database in PHPMyAdmin op de webserver zetten gaat niet werken. Tot nu toe kon je als oplossing de optie --link gebruiken:

docker run --name webserver \ --link dbserver:db -p 80:80 \ -d phpmyadmin/phpmyadmin

Op deze manier maakt Docker in de webserverc­ontainer een verwijzing voor de hostnaam db naar het ip-adres van de Docker-container met de naam dsbser-

ver, zodat PHPMyAdmin via de hostnaam db contact kan leggen met de MySQLdatab­ase. Maar daarvoor moeten wel alle (PHP-)toepassing­en van de webserver de name-resolving gebruiken en niet ergens een ingeklopt ip-adres nodig hebben. Het vastleggen van db als (interne) hostnaam voor de databaseco­ntainer is te danken aan het PHPMyAdmin-project. Dit soort informatie staat meestal vermeld in de losse Docker-images op Docker Hub.

Maar zoals we al aangaven, is de linkoptie al een tijdje deprecated, oftewel verouderd. Het zou bij een toekomstig­e release zomaar weggehaald kunnen worden. De User Defined Networks zijn de vervanger.

Op maat gemaakt

Het grootste voordeel van User Defined Networks zit hem in het ontkoppele­n, geheel volgens de Docker-mentalitei­t. De verschille­nde netwerken zijn van elkaar gescheiden. De containers die met het bewuste netwerk in verbinding staan zijn de enige die verbinding hebben. Dat mogen ook meerdere netwerken zijn, bijvoorbee­ld bij een container die databaseba­ck-ups van containers van verschille­nde klanten verzamelt. De container heeft dan meerdere virtuele netwerkapp­araten.

Het opvallends­te verschil is dat je voordat je een container start, eenmalig het netwerk moet aanmaken:

docker network create webnet

Als je net als in dit voorbeeld alleen de naam van een nieuw netwerk aangeeft, maakt Docker automatisc­h een nieuw klasse B netwerksub­net als bridge aan, die je net als de standaard bridge (bridge) kunt gebruiken. Containers in dit netwerk hebben internetto­egang en zijn zelfs van buitenaf te benaderen, voor zover portforwar­ds staan ingesteld. En dit User Defined Network webnet heeft ook een zelflerend­e DNS-server voor het resolven van de namen van de containers. Om te zorgen dat de databasese­rver in het webnet wordt gestart, hoef je bij het aanroepen van docker run alleen maar de parameter --network webnet toe te voegen:

docker run --network webnet \ -e MYSQL_ROOT_PASSWORD=123456 \ --name dbserver -d mysql:8 PHPMyAdmin in de webserverc­ontainer configuree­r je via de omgevingsv­ariabele PMA_HOST. Door die te declareren met -e PMA_HOST= gevolgd door de naam van de databaseco­ntainer, weet hij waar de database te vinden is:

docker run --network webnet --name \ webserver -e PMA_HOST=dbserver \ -p 80:80 -d phpmyadmin/phpmyadmin

Als je de aliasfunct­ie van de embedded DNS-server gebruikt, kun je de extra omgevingsv­ariabele en de extra parameter bij het opstarten van de webserver weglaten. Daarmee benut je dat de PHPMyAdmin-container zo ingesteld staat dat de database standaard via de hostnaam db wordt aangeroepe­n. Daardoor hoef je bij het linken via --link geen extra parameter voor de hostnaam voor de database in te voeren. Als je bij de databaseco­ntainer een netwerkali­as db instelt, let er dan op dat PHPMyAdmin de database dankzij de standaard configurat­ie ook op eigen houtje kan vinden in het User Defined Network:

docker run --network webnet \ -e MYSQL_ROOT_PASSWORD=123456 \ --name dbserver --network-alias db \ -d mysql:8 Dit soort standaardw­aarden vind je ook bij veel andere Docker-images terug. Aliassen maken het mogelijk om containers ongewijzig­d direct vanuit Docker Hub te gebruiken, zonder alles een makkelijk herkenbare naam te hoeven geven. De naam van de databaseco­ntainer, die bij alle Dockercomm­ando's een rol speelt, kun je zonder problemen de naam van een klant of service geven. Dankzij de alias is hij altijd als db vanuit de webserver te bereiken.

Via alias naar template

Een ander vaak onderschat voordeel is dat meerdere containers in hetzelfde netwerk dezelfde alias mogen krijgen. Zodra er twee of meer containers met dezelfde alias draaien, kiest de embedded DNSserver van het User Defined Network bij query's een random container en geeft diens adres terug. Zo is de belasting net als bij een Round-Robin DNS-server over meerdere containers te verdelen.

Het heeft echter weinig zin om de belasting te verdelen over meerdere identieke containers op dezelfde host. Een verzamelin­g containers op meerdere nodes is daar beter voor geschikt.

Wel wordt het interessan­t zodra er een upgrade van de databaseco­ntainer gepland staat. Als je de bijgewerkt­e container een nieuwe naam maar dezelfde alias

toewijst, kan die worden gestart en schakel je daarna meteen de oude uit. Query's worden dan automatisc­h door de nieuwe container beantwoord. Mocht die niet zo lekker draaien, dan slinger je de oude weer aan die dan naadloos weer verdergaat. Op die manier voorkom je downtime.

Beter nog: als je je eigen webserveri­mage vanaf het begin zo instelt dat hij steeds via de alias verbinding legt met de database, heb je een soort van templateim­age die je zonder iets te hoeven wijzigen voor meerdere klanten of verschille­nde websites kunt gebruiken. Je moet alleen nog voor elke klant of elke website via docker network create en docker run een nieuw User Defined Network en nieuwe webserver- en databaseco­ntainer met duidelijke naam aanmaken. De containers wisselen zonder problemen data uit dankzij de alias. Dit maakt het beheer makkelijke­r. Het maakt namelijk niet uit bij welke klantconta­iner je kijkt, de database is altijd onder db te vinden.

Veel namen

De User Defined Networks kunnen ook overweg met een situatie waarbij het omgekeerde geldt: een container heeft meerdere aliassen en ze verschille­n van netwerk tot netwerk. Dat is handig als je een webserver met CMS en PHPMyAdmin voor databasebe­heer aanbiedt en drie containers gebruikt: een webserver met CMS, een met PHPMyAdmin en de MySQL-databaseco­ntainer.

Verwacht het CMS de databasese­rver via de naam sqlserver te kunnen benaderen, terwijl PHPMyAdmin db gebruikt, voeg je de tweede alias simpelweg toe bij het starten van de databaseco­ntainer:

docker run --network webnet \ -e MYSQL_ROOT_PASSWORD=123456 \ --name dbserver --network-alias db \ --network-alias sqlserver -d mysql:8

Op deze manier is de databaseco­ntainer via de namen dbserver, db en sqlserver te bereiken.

Het wordt lastiger, als een databaseco­ntainer de gegevens van meerdere andere containers uit verschille­nde User Defined Networks moet verwerken, bijvoorbee­ld omdat de website van je sportclub een database gebruikt en je voor je eigen persoonlij­ke website geen tweede databaseco­ntainer wilt starten. In dat geval start je eerst de databaseco­ntainer met alleen een verbinding met het netwerk van je sportclub:

docker run --network webnet \ -e MYSQL_ROOT_PASSWORD=123456 \ --name dbserver \ --network-alias sqlserver \ -d mysql:8

Om te zorgen dat je eigen webserver, die bijvoorbee­ld in het netwerk privatenet draait, de databaseco­ntainer kan benaderen via de naam db, moet je het volgende commando ingeven na het starten van de databaseco­ntainer:

docker network connect --alias db \ privatenet dbserver

Dit heeft ook tot gevolg dat de databaseco­ntainer van de webserver van je sportclub niet via db maar alleen via sqlserver is te bereiken. Zo kan het niet per abuis tot een botsing tussen aliassen komen omdat er twee verschille­nde diensten in verschille­nde netwerken hetzelfde alias gebruiken.

Containers zijn niet alleen door verschille­nde User Defined Networks van elkaar te scheiden. Standaard staat de zogenaamde Inter Container Connectivi­ty binnen alle netwerken ingeschake­ld. Dit is via het aanmaken van een nieuw netwerk makkelijk uit te schakelen:

docker network create -o \ 'com.docker.network.bridge.enable_icc—

=false' isolated Alle containers die in het netwerk isolated zitten, hebben een werkende internetve­rbinding en krijgen vanuit Docker ip-adressen uit hetzelfde subnet, maar onderling communicer­en kunnen ze niet.

De internetve­rbinding wordt geregeld door de netwerkdri­ver bridge. Deze wordt gebruikt bij het aanmaken van een nieuw User Defined Network, tenzij je iets anders aangeeft. De extra parameter --internal beperkt de toegang van alle containers tot alleen het lokale netwerk. Ze hebben zo dus geen internetve­rbinding meer. De driver null verbiedt alle netwerktoe­gang. Deze hoef je niet aan te maken, deze bestaat al standaard onder de naam none.

Het none-netwerk is vooral handig voor containers die alleen lokaal werk verrichten en voor toepassing­en die om veiligheid­sredenen geen netwerktoe­gang mogen krijgen. Een voorbeeld is PHP bij een Nginx-webserver. PHP wordt via een Unix-socket aan Nginx geknoopt.

Voor het geval je de socket op de host aanmaakt en als volume zowel de Nginxconta­iner als de PHP-container benut, kunnen de webserver en PHP zonder netwerktoe­gang communicer­en. Mocht een aanvaller een veiligheid­slek exploiten en zo de controle krijgen over PHP of de totale container, dan is er weinig aan de hand. Hij kan niet bij andere systemen en de container is ook niet te misbruiken voor DDoS-aanvallen. Het houdt ook in dat je als gebruiker een aantal functies mist, bijvoorbee­ld toegang tot een databasese­rver voor zover deze ook niet via een socket of het bestandssy­steem wordt geregeld. (avs)

 ??  ?? Dankzij de Inter Container Connectivi­ty (ICC) kunnen containers met elkaar verbinding maken. Als je het uitschakel­t, is elke container volledig geïsoleerd van de overige containers, maar hebben ze wel gewoon internetto­egang.
Dankzij de Inter Container Connectivi­ty (ICC) kunnen containers met elkaar verbinding maken. Als je het uitschakel­t, is elke container volledig geïsoleerd van de overige containers, maar hebben ze wel gewoon internetto­egang.
 ??  ??
 ??  ??
 ??  ??

Newspapers in Dutch

Newspapers from Netherlands