Kleine Docker-images die in zichzelf geloofden*

[verwijzing naar het Amerikaanse kinderboek 'De Kleine Locomotief die het Kon' ('The Little Engine That Could')] — vert. van de auteur*

Kleine Docker-images die in zichzelf geloofden*

Hoe je automatisch kleine docker-images kunt maken voor je eigen behoeften

Ongewone obsessie

De afgelopen maanden was ik geobsedeerd door de vraag: hoe klein kan een Docker-image worden, zodat de applicatie nog steeds functioneert?

Ik weet dat het een vreemde gedachte is.

Voordat ik in de details en technische diepten duik, wil ik uitleggen waarom dit probleem me zo heeft geraakt en hoe het jou betreft.

Waarom formaat er toe doet

Door de inhoud van een Docker-image te verkleinen, verkleinen we ook de lijst met kwetsbaarheden. Bovendien maken we de images schoner, omdat ze alleen bevatten wat nodig is om applicaties te draaien.

Er is nog een klein voordeel — de images worden iets sneller gedownload, maar dat vind ik niet zo belangrijk.

Let op: als je je zorgen maakt over de grootte, zijn Alpine-images van zichzelf al klein en passen ze waarschijnlijk goed bij je.

Distroless-images

Het Distroless-project biedt een collectie basis 'distroless'-images, die geen pakketbeheerders, shell-omgevingen of andere hulpprogramma's bevatten die je gewend bent te zien in de commandoregel. Hierdoor kun je geen pakketbeheerders zoals pip en apt gebruik maken van:

FROM gcr.io/distroless/python3
RUN  pip3 install numpy

Dockerfile, gebruikt distroless-image Python 3

Versturen van buildcontext naar Docker-daemon  2.048kB
Stap 1/2 : FROM gcr.io/distroless/python3
 ---> 556d570d5c53
Stap 2/2 : RUN  pip3 install numpy
 ---> Running in dbfe5623f125
/bin/sh: 1: pip3: niet gevonden

Pip is niet aanwezig in het image

Dit probleem wordt meestal opgelost door middel van een multi-stage build:

FROM python:3 als builder
RUN  pip3 install numpy

FROM gcr.io/distroless/python3
COPY --from=builder /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.5/

Multi-stage build

Hierdoor ontstaat een image van 130MB. Niet slecht! Ter vergelijking: het standaard Python-image weegt 929MB, terwijl het 'afgevallen' (3,7-slim) — 179MB, het Alpine-image (3,7-alpine) — 98,6MB, terwijl de basis distroless-image, gebruikt in het voorbeeld, — 50,9MB.

Het is terecht om aan te geven dat we in het vorige voorbeeld een hele directory kopiëren /usr/local/lib/python3.7/site-packages, waarin zich onnodige afhankelijkheden kunnen bevinden. Hoewel het duidelijk is dat de grootte van alle bestaande basisimages Python varieert.

Op het moment van schrijven ondersteunt Google distroless niet veel images: Java en Python zijn nog in experimentele fase, en Python is alleen beschikbaar voor 2.7 en 3.5.

Kleine images

Laten we terugkomen op mijn fascinatie voor het creëren van kleine images.

Ik wilde eigenlijk kijken hoe distroless images zijn opgebouwd. Het distroless-project maakt gebruik van Google's build tool bazel. Maar om Bazel te installeren en mijn eigen images te schrijven, moest ik flink zwoegen (en om eerlijk te zijn, het voelde als het wiel opnieuw uitvinden — dat is leuk en leerzaam). Ik wilde het maken van compacte images vereenvoudigen: het proces van het maken van een image moet uiterst eenvoudig zijn, banaliteit. Er mogen geen configuratiebestanden zijn, alleen één regel in de console: gewoon een image bouwen voor.

Dus als je je eigen images wilt maken, weet dan: er is zo'n unieke docker image, scratch. Scratch is een 'lege' image, er zijn geen bestanden in, toch weegt het standaard — wauw! — 77 bytes.

FROM scratch

De scratch image

Het idee achter de scratch image is dat je er afhankelijkheden van de hostmachine in kunt kopiëren en deze ofwel binnen het Dockerfile kunt gebruiken (dit is als het kopiëren van ze in apt en van nul installeren), of later, wanneer de Docker image is gematerialiseerd. Dit stelt je in staat om de inhoud van de Docker-container volledig te controleren, en dus ook de grootte van de image volledig te beheersen.

En nu moeten we op de een of andere manier die afhankelijkheden verzamelen. Bestaande tools zoals apt kunnen pakketten downloaden, maar ze zijn gebonden aan de huidige machine en ondersteunen uiteindelijk geen Windows of MacOS.

En dus begon ik mijn eigen tool te bouwen die automatisch de basisimage van de kleinste mogelijke grootte zou opbouwen en ook elk applicatie zou laten draaien. Ik gebruikte pakketten van Ubuntu/Debian, voerde selecties uit (ontvangen pakketten rechtstreeks uit de repositories) en vond hun afhankelijkheden recursief. Het programma moest automatisch de nieuwste stabiele versie van een pakket downloaden, terwijl de veiligheidsrisico's geminimaliseerd werden.

Ik noemde de tool fetchy, omdat het… vindt en brengt… wat je nodig hebt [van het Engelse 'fetch', 'brengen' — vert. toel.]. De tool werkt via een opdrachtregelinterface, maar biedt ook een API.

Om een image te bouwen met fetchy (laten we deze keer de Python image nemen), hoef je alleen maar de CLI als volgt te gebruiken: fetchy dockerize python. Je kunt om het doelsysteem en de codenaam worden gevraagd, aangezien fetchy voorlopig alleen Debian en Ubuntu-gebaseerde pakketten gebruikt.

Nu kun je kiezen welke afhankelijkheden helemaal niet nodig zijn (in onze context) en ze uitsluiten. Bijvoorbeeld, Python is afhankelijk van Perl, terwijl het prima werkt zonder geïnstalleerde Perl.

Resultaten

De Python-afbeelding, gemaakt met het commando fetchy dockerize python3.5 weegt slechts 35MB (ik ben er meer dan zeker van dat het in de toekomst nog verder kan worden verlicht). Dat betekent dat we met de distroless-afbeelding nog eens 15MB hebben kunnen besparen.

Alle tot nu toe verzamelde beelden kun je bekijken hier.

Project — here.

Als je functies mist, maak dan gewoon een verzoek — ik help je graag 🙂 Sterker nog, ik werk momenteel aan de integratie van andere pakketbeheerders in fetchy, zodat de noodzaak voor meerstaps builds vervalt.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster