Best Practices für Kubernetes. Kleine Behälter erstellen

Best Practices für Kubernetes. Kleine Behälter erstellen

Der erste Schritt der Bereitstellung auf Kubernetes besteht darin, Ihre Anwendung in einem Container zu platzieren. In dieser Serie schauen wir uns an, wie Sie ein kleines, sicheres Container-Image erstellen können.
Dank Docker war die Erstellung von Container-Images noch nie so einfach. Geben Sie ein Basis-Image an, fügen Sie Ihre Änderungen hinzu und erstellen Sie einen Container.

Best Practices für Kubernetes. Kleine Behälter erstellen

Diese Technik eignet sich zwar hervorragend für den Einstieg, die Verwendung von Standard-Basis-Images kann jedoch zu unsicherem Arbeiten mit großen Images voller Schwachstellen führen.

Darüber hinaus verwenden die meisten Images in Docker Debian oder Ubuntu als Basis-Image. Dies bietet zwar hervorragende Kompatibilität und einfache Anpassung (eine Docker-Datei benötigt nur zwei Zeilen Code), Basis-Images können Ihrem Container jedoch Hunderte Megabyte zusätzlicher Last hinzufügen. Beispielsweise ist eine einfache node.js-Datei für eine Go-Anwendung „hello-world“ etwa 700 Megabyte groß, während Ihre eigentliche Anwendung nur wenige Megabyte groß ist.

Best Practices für Kubernetes. Kleine Behälter erstellen

All diese zusätzliche Arbeitsbelastung ist also eine Verschwendung von digitalem Platz und ein tolles Versteck für Sicherheitslücken und Bugs. Sehen wir uns also zwei Möglichkeiten an, die Größe eines Container-Images zu reduzieren.

Das erste ist die Verwendung kleiner Basisbilder, das zweite die Verwendung des Builder-Musters. Die Verwendung kleinerer Basisbilder ist wahrscheinlich die einfachste Möglichkeit, die Größe Ihres Containers zu reduzieren. Höchstwahrscheinlich stellt die von Ihnen verwendete Sprache oder der Stack ein Original-Anwendungsbild bereit, das viel kleiner als das Standardbild ist. Werfen wir einen Blick auf unseren node.js-Container.

Best Practices für Kubernetes. Kleine Behälter erstellen

Standardmäßig beträgt in Docker die Basisbildgröße „node:8“ 670 MB und die Bildgröße „node:8-alpine“ nur 65 MB, also zehnmal kleiner. Durch die Verwendung des kleineren Alpine-Basisbildes reduzieren Sie die Größe Ihres Containers deutlich. Alpine ist eine kleine und leichte Linux-Distribution, die bei Docker-Benutzern sehr beliebt ist, da sie mit vielen Anwendungen kompatibel ist und gleichzeitig die Container klein hält. Im Gegensatz zum standardmäßigen Docker-„node“-Image entfernt „node:alpine“ viele Dienstdateien und Programme, sodass nur diejenigen übrig bleiben, die zum Ausführen Ihrer Anwendung ausreichen.

Um zu einem kleineren Basis-Image zu wechseln, aktualisieren Sie einfach die Docker-Datei, um mit dem neuen Basis-Image zu arbeiten:

Best Practices für Kubernetes. Kleine Behälter erstellen

Im Gegensatz zum alten Onbuild-Image müssen Sie jetzt Ihren Code in den Container kopieren und alle Abhängigkeiten installieren. In einer neuen Docker-Datei beginnt der Container mit einem node:alpine-Image, erstellt dann ein Verzeichnis für den Code, installiert Abhängigkeiten mithilfe des NPM-Paketmanagers und führt schließlich server.js aus.

Best Practices für Kubernetes. Kleine Behälter erstellen

Dieses Upgrade führt zu einem Container, der zehnmal kleiner ist. Wenn Ihre Programmiersprache oder Ihr Stack nicht über eine Basisfunktion zur Bildreduzierung verfügt, verwenden Sie Alpine Linux. Es bietet auch die Möglichkeit, den Inhalt des Containers vollständig zu verwalten. Die Verwendung kleiner Basisbilder ist eine großartige Möglichkeit, schnell kleine Container zu erstellen. Mit dem Builder-Pattern lässt sich aber eine noch größere Reduzierung erreichen.

Best Practices für Kubernetes. Kleine Behälter erstellen

In interpretierten Sprachen wird der Quellcode zunächst an den Interpreter übergeben und dann direkt ausgeführt. In kompilierten Sprachen wird der Quellcode zunächst in kompilierten Code umgewandelt. Bei der Kompilierung werden jedoch häufig Tools verwendet, die zum Ausführen des Codes eigentlich nicht benötigt werden. Dies bedeutet, dass Sie diese Werkzeuge vollständig aus dem endgültigen Behälter entfernen können. Sie können hierfür Builder Pattern verwenden.

Best Practices für Kubernetes. Kleine Behälter erstellen

Der Code wird im ersten Container erstellt und kompiliert. Der kompilierte Code wird dann in einen endgültigen Container gepackt, ohne dass die Compiler und Tools zum Kompilieren dieses Codes erforderlich sind. Lassen Sie uns durch diesen Prozess eine Go-Anwendung ausführen. Zuerst wechseln wir vom Onbuild-Image zu Alpine Linux.

Best Practices für Kubernetes. Kleine Behälter erstellen

In der neuen Docker-Datei beginnt der Container mit einem golang:alpine-Image. Anschließend erstellt es ein Verzeichnis für den Code, kopiert ihn in den Quellcode, erstellt diesen Quellcode und führt die Anwendung aus. Dieser Container ist viel kleiner als der Onbuild-Container, enthält aber immer noch den Compiler und andere Go-Tools, die wir nicht wirklich brauchen. Also extrahieren wir einfach das kompilierte Programm und legen es in einem eigenen Container ab.

Best Practices für Kubernetes. Kleine Behälter erstellen

Möglicherweise fällt Ihnen in dieser Docker-Datei etwas Seltsames auf: Sie enthält zwei FROM-Zeilen. Der erste 4-Zeilen-Abschnitt sieht genauso aus wie die vorherige Docker-Datei, außer dass er das Schlüsselwort AS verwendet, um diese Stufe zu benennen. Der nächste Abschnitt enthält eine neue FROM-Zeile, um ein neues Bild zu starten, wobei wir anstelle des Bildes „golang:alpine“ Raw alpine als Basisbild verwenden werden.

Bei Raw Alpine Linux sind keine SSL-Zertifikate installiert, was dazu führt, dass die meisten API-Aufrufe über HTTPS fehlschlagen. Installieren wir also einige Root-CA-Zertifikate.

Jetzt kommt der spaßige Teil: Um den kompilierten Code vom ersten Container in den zweiten zu kopieren, können Sie einfach den COPY-Befehl in Zeile 5 des zweiten Abschnitts verwenden. Es wird nur eine Anwendungsdatei kopiert und hat keine Auswirkungen auf die Go-Dienstprogrammtools. Die neue mehrstufige Docker-Datei wird ein Container-Image enthalten, das nur 12 Megabyte groß ist, verglichen mit dem ursprünglichen Container-Image, das 700 Megabyte groß war, was einen großen Unterschied darstellt!
Die Verwendung kleiner Basisbilder und des Builder-Musters sind daher großartige Möglichkeiten, ohne großen Arbeitsaufwand viel kleinere Container zu erstellen.
Es ist möglich, dass es je nach Anwendungsstapel zusätzliche Möglichkeiten gibt, die Bild- und Containergröße zu reduzieren. Aber haben kleine Container wirklich einen messbaren Vorteil? Schauen wir uns zwei Bereiche an, in denen kleine Container äußerst effektiv sind: Leistung und Sicherheit.

Um die Leistungssteigerung zu bewerten, berücksichtigen Sie die Dauer des Prozesses zum Erstellen eines Containers, zum Einfügen in die Registrierung (Push) und zum anschließenden Abrufen von dort (Pull). Sie sehen, dass ein kleinerer Behälter einen deutlichen Vorteil gegenüber einem größeren Behälter hat.

Best Practices für Kubernetes. Kleine Behälter erstellen

Docker speichert die Ebenen zwischen, sodass nachfolgende Builds sehr schnell erfolgen. Allerdings speichern viele CI-Systeme, die zum Erstellen und Testen von Containern verwendet werden, keine Layer im Cache, sodass erhebliche Zeiteinsparungen möglich sind. Wie Sie sehen, beträgt die Zeit zum Bau eines großen Containers je nach Leistung Ihrer Maschine 34 bis 54 Sekunden und bei Verwendung eines mit dem Builder-Muster reduzierten Containers 23 bis 28 Sekunden. Bei Betrieben dieser Art beträgt die Produktivitätssteigerung 40-50 %. Denken Sie also einfach darüber nach, wie oft Sie Ihren Code erstellen und testen.

Nachdem der Container erstellt wurde, müssen Sie sein Image (Push-Container-Image) in die Container-Registrierung übertragen, damit Sie es dann in Ihrem Kubernetes-Cluster verwenden können. Ich empfehle die Verwendung von Google Container Registry.

Best Practices für Kubernetes. Kleine Behälter erstellen

Mit Google Container Registry (GCR) zahlen Sie nur für Rohspeicher und Netzwerk, und es fallen keine zusätzlichen Gebühren für die Containerverwaltung an. Es ist privat, sicher und sehr schnell. GCR verwendet viele Tricks, um den Pull-Vorgang zu beschleunigen. Wie Sie sehen, dauert das Einfügen eines Docker Container Image-Containers mit go:onbuild je nach Computerleistung 15 bis 48 Sekunden, und der gleiche Vorgang mit einem kleineren Container dauert 14 bis 16 Sekunden und für weniger produktive Maschinen Der Vorteil in der Betriebsgeschwindigkeit erhöht sich um das Dreifache. Bei größeren Maschinen ist die Zeit ungefähr gleich, da GCR einen globalen Cache für eine gemeinsame Datenbank mit Bildern verwendet, was bedeutet, dass Sie diese überhaupt nicht laden müssen. Bei einem Low-Power-Computer ist die CPU der Flaschenhals, daher ist der Vorteil der Verwendung kleiner Container hier viel größer.

Wenn Sie GCR verwenden, empfehle ich dringend, Google Container Builder (GCB) als Teil Ihres Build-Systems zu verwenden.

Best Practices für Kubernetes. Kleine Behälter erstellen

Wie Sie sehen, können Sie mit seiner Verwendung viel bessere Ergebnisse bei der Verkürzung der Dauer des Build+Push-Vorgangs erzielen als mit einer produktiven Maschine – in diesem Fall wird der Prozess des Erstellens und Sendens von Containern an den Host um fast das Zweifache beschleunigt . Darüber hinaus erhalten Sie jeden Tag 2 kostenlose Bauminuten, was in den meisten Fällen Ihren Bedarf an Containerbau deckt.

Als nächstes kommt die wichtigste Leistungsmetrik – die Geschwindigkeit beim Abrufen oder Herunterladen von Pull-Containern. Und wenn Ihnen die für einen Push-Vorgang aufgewendete Zeit egal ist, dann hat die Länge des Pull-Vorgangs erhebliche Auswirkungen auf die Gesamtsystemleistung. Nehmen wir an, Sie haben einen Cluster aus drei Knoten und einer von ihnen fällt aus. Wenn Sie ein Verwaltungssystem wie Google Kubernetes Engine verwenden, wird der tote Knoten automatisch durch einen neuen ersetzt. Dieser neue Knoten ist jedoch völlig leer und Sie müssen alle Ihre Container hineinziehen, damit er funktioniert. Wenn der Pull-Vorgang lange genug dauert, wird Ihr Cluster die ganze Zeit über mit geringerer Leistung ausgeführt.

Es gibt viele Fälle, in denen dies passieren kann: das Hinzufügen eines neuen Knotens zu einem Cluster, das Aktualisieren von Knoten oder sogar der Wechsel zu einem neuen Container für die Bereitstellung. Daher wird die Minimierung der Pull-Extraktionszeit zu einem Schlüsselfaktor. Es ist unbestreitbar, dass ein kleiner Container viel schneller heruntergeladen wird als ein großer. Wenn Sie mehrere Container in einem Kubernetes-Cluster ausführen, kann die Zeitersparnis erheblich sein.

Best Practices für Kubernetes. Kleine Behälter erstellen

Schauen Sie sich diesen Vergleich an: Ein Pull-Vorgang an kleinen Containern dauert je nach Leistung der Maschine 4-9 Mal weniger Zeit als der gleiche Vorgang mit go:onbuild. Durch die Verwendung gemeinsamer, kleiner Container-Basisimages wird die Zeit und Geschwindigkeit, mit der neue Kubernetes-Knoten bereitgestellt und online geschaltet werden können, erheblich beschleunigt.

Schauen wir uns das Thema Sicherheit an. Kleinere Behälter gelten als wesentlich sicherer als größere, da sie eine geringere Angriffsfläche haben. Ist das wirklich? Eine der nützlichsten Funktionen von Google Container Registry ist die Möglichkeit, Ihre Container automatisch auf Schwachstellen zu scannen. Vor ein paar Monaten habe ich sowohl Onbuild- als auch Multistage-Container erstellt, also schauen wir mal, ob es dort irgendwelche Schwachstellen gibt.

Best Practices für Kubernetes. Kleine Behälter erstellen

Das Ergebnis ist verblüffend: In einem kleinen Container wurden nur 3 mittlere Schwachstellen erkannt, in einem großen Container wurden 16 kritische und 376 weitere Schwachstellen gefunden. Wenn wir uns den Inhalt eines großen Containers ansehen, können wir erkennen, dass die meisten Sicherheitsprobleme nichts mit unserer Anwendung zu tun haben, sondern mit Programmen zusammenhängen, die wir gar nicht nutzen. Wenn also von einer großen Angriffsfläche die Rede ist, meinen sie genau das.

Best Practices für Kubernetes. Kleine Behälter erstellen

Die Erkenntnis ist klar: Erstellen Sie kleine Container, weil sie Ihrem System echte Leistungs- und Sicherheitsvorteile bieten.

Best Practices für Kubernetes. Organisation von Kubernetes mit Namespace

Einige Anzeigen 🙂

Vielen Dank, dass Sie bei uns geblieben sind. Gefallen Ihnen unsere Artikel? Möchten Sie weitere interessante Inhalte sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder an Freunde weiterempfehlen. Cloud-VPS für Entwickler ab 4.99 $, ein einzigartiges Analogon von Einstiegsservern, das von uns für Sie erfunden wurde: Die ganze Wahrheit über VPS (KVM) E5-2697 v3 (6 Kerne) 10 GB DDR4 480 GB SSD 1 Gbit/s ab 19 $ oder wie teilt man sich einen Server? (verfügbar mit RAID1 und RAID10, bis zu 24 Kerne und bis zu 40 GB DDR4).

Dell R730xd 2-mal günstiger im Equinix Tier IV-Rechenzentrum in Amsterdam? Nur hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbit/s 100 TV ab 199 $ in den Niederlanden! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gbit/s 100 TB – ab 99 $! Lesen über Wie baut man ein Infrastrukturunternehmen auf? Klasse mit dem Einsatz von Dell R730xd E5-2650 v4 Servern im Wert von 9000 Euro für einen Cent?

Source: habr.com

Kommentar hinzufügen