pixel
Select Page

NETWAYS Blog

DockerCon 2022: Wie geht Containersecurity?

Buzzwords wie Software Supply Chain, Container Security Scanning oder Software Bill of Materials (SBOM) sind in den vergangenen zwei Jahren vermehrt in aller Munde, nicht zuletzt aufgrund des anhaltenden Trends zur Containerisierung vormals monolithischer Anwendungen und deren Betrieb als sog. Microservices. Allerdings kann nach wie vor nicht jeder, der auf die ein oder andere Weise mit Docker oder Containern im Allgemeinen zu tun hat, etwas mit diesen Begriffen anfangen. Aus diesem Grund habe ich den Fokus meines virtuellen Besuchs der diesjährigen DockerCon auf genau diesen Themebereich – Containersecurity – gelegt und möglichst viele Best Practices für Dich zusammengefasst.

 

Die Ausgangslage

Die großen Sicherheitslücken der vergangenen zwei Jahre, sei es der Solarwinds-Breach oder die Log4J/Log4Shell-Exploits haben einmal mehr schmerzlich bewusst gemacht: In fast jedem Softwareprodukt, egal ob proprietär oder Open Source, befinden sich zahlreiche mehr oder weniger gut gepflegte Abhängigkeiten verschiedenster Maintainer – in vielen Applikationen stecken bis zu 80% Open Source Code, die es “auf dem Schirm” zu behalten gilt. Dies gilt natürlich auch für Container(-images), die letzten Endes nichts anderes tun, als die Applikation zu bündeln und (weitestgehend) losgelöst vom ausführenden Hostsystem ausführbar zu machen.

Dennoch gelten Container paradoxerweise oft als besonders sicher, sei es aufgrund der von außen wahrgenommenen “Kapselung” oder der geringen Größe – soviel potentiell vulnerable oder bösartige Software kann da doch gar nicht drinstecken, oder? Mögen diese Wahrnehmungen in der Theorie und im Bestfall auch stimmen, sieht die Praxis in den allermeisten Fällen anders aus, wie die ein oder andere Keynote im Rahmen der DockerCon gezeigt hat.

Laut SysDig, einem Spezialisten für Cloudsecurity, laufen 58% der in den einschlägigen Containerregistries (DockerHub, Google, Github, etc.) verfügbaren Containerimages als root, anstatt einen geeigneteren, unprivilegierten Nutzer zu verwenden. Außerdem beinhalten selbst die offiziellen, von den Registries kuratierten Containerimages beliebter Frameworks oder Distributionen dutzende detektierbare Schwachstellen.

Offensichtlich gibt es also zum Einen ein falsches Gefühl von Sicherheit innerhalb der Nutzergemeinschaft von Containern, zum Anderen aber schlichtweg keine Blaupause oder “OneFitsAll”-Lösung, die für beliebige Container(-images) absolute Sicherheit verspricht. Nur eine Sache ist klar: “Hinten” anzufangen, ist nicht rentabel – Container einfach zu deployen und dann im Betrieb nach Sicherheitslücken zu suchen, ist teuer, erzeugt vermeidbaren zusätzlichen Aufwand und nimmt Flexibilität. Die Absicherung der Container muss “nach links” geschoben werden, möglichst an den Beginn der Containerisierung. Doch wo ansetzen?

 

Die Möglichkeiten

Um den Ursprung für Schwachstellen in Container(-images) und damit einhergehend mögliche Ansatzpunkte zur Absicherung zu identifizieren, muss man die “Lebensphasen” eines Container(-images) verstehen. Diese lassen sich kurz und bündig wie folgt darstellen:

 

  • Bau des Images (lokal oder via CI/CD)
  • Distribution des Images – Push in eine Registry (Self-hosted oder bei einem Anbieter), Pull von Usern/Programmen
  • Deployment des Images (Openshift, (Managed) Kubernetes, etc.) und Betrieb des Containers

 

An dieser Stelle wird hoffentlich klar, warum ich permanent “Container(-image)” schreibe – abhängig von der betrachteten Lebensphase haben wir es beim Thema Containersicherheit entweder mit einem erstellten Image oder mit einem laufenden Container, quasi einer Instanziierung dieses Images zu tun. Mit dieser Aufschlüsselung in verschiedene Abschnitte können wir uns nun mögliche Schritte zur Absicherung unserer Container anschauen.

Containerimages werden entweder lokal von Entwickler:innen, DevOps-Engineers, etc. auf Grundlage eines sog. Dockerfiles gebaut – alternativ lässt sich dieser Vorgang aber auch von CI/CD Pipelines umsetzen, wenn man den Dockerfile und andere benötigten Ressourcen bspw. in GitLab eincheckt. Die meisten Möglichkeiten zur Absicherung des späteren Container(-images) ergeben sich bereits in diesem ersten Schritt – der Quellcode der zu containerisierenden Applikation liegt vor, ebenso die Definition des Containers selbst, und auch möglicherweise vulnerable oder bösartige Abhängigkeiten wurden noch nicht in das spätere Containerimage eingebettet.

Ist das Containerimage lokal oder in einer Pipeline gebaut worden, muss man es zur späteren Nutzung in Produktion in eine Containerregistry übertragen. Diese kann entweder selbst gehosted werden, als private, aber gemanagte Instanz in der Cloud laufen oder öffentlich von jedem beliebigen Nutzer einsehbar sein. Auch bei diesem Vorgang gibt es Möglichkeiten, die Supply Chain zwischen Entwickler und Endnutzer abzusichern.

Auch wenn im Dockerfile gewisse Best Practices befolgt werden, kann der Container diese in vielen Fällen beim Deployment überschreiben – das letzte Wort haben hierbei immer Kubernetes, Openshift, Docker Desktop und Konsorten. Aus diesem Grund müssen einige der Überlegungen, die in der Build-Phase des Containerimages stattgefunden haben, auch hier noch einmal herangezogen, betrachtet und evaluiert werden, um die für die konkrete Nutzung besten Einstellungen und Kompromisse zu finden.

Ist der Container erst einmal deployed, gibt es nicht mehr viele Möglichkeiten, ihn “von innen” weiter abzusichern. Dennoch kann und sollte man sich fortlaufend Gedanken bspw. um die Erreichbarkeit innerhalb des Clusters, des Netzwerkes, der Cloud etc. machen, Regeln nachziehen wo nötig und natürlich auch Update- und Backupstrategien im Hinterkopf behalten. Am Ende des Tages merkt man spätestens jetzt: Nach dem Deployment eines Containers seine Sicherheit zu überprüfen und zu garantieren, ist nicht sinnvoll – wir brauchen einen Left Shift.

 

Die Umsetzung

Buildphase

Nach viel Theorie und Konjunktiv können wir uns jetzt konkrete Umsetzungsmöglichkeiten der besprochenen Ansätze anschauen. Beginnen werden wir “ganz links” beim Bau der Containerimages. Vieles lässt sich hier über verschiedene Direktiven im genutzten Dockerfile festlegen – Docker listet alle möglichen Direktiven sowie sinnvolle Best Practices und Caveats in der Docker Dokumentation auf. Für uns besonders interessant sind die Direktiven ADD, COPY und USER. Wie eingangs erwähnt, nutzen mehr als die Hälfte aller Containerimages per Default den root Nutzer innerhalb des Containers, obwohl das in vielen Fällen gar nicht notwendig wäre – auf einem klassischen Server läuft ein Apache2 Webserver schließlich auch als User apache, warum sollte/müsste das innerhalb eines Containers bspw. auf Debian-Basis anders sein?

Die anderen beiden erwähnten Direktiven beziehen sich auf die Abwägung, ob man gewisse Verzeichnisse und Dateien aus seiner Entwicklungsumgebung denn tatsächlich im endgültigen Container braucht – oder ob man die Applikation direkt lokal baut, und lediglich die fertige Binary via COPY in das Containerimage überträgt. In diesem Fall muss man natürlich darauf achten, dass etwaige zur Laufzeit benötigte Tools (ich meine dich, curl) und Bibliotheken sich auch im endgültigen Containerimage befinden. Alternativ kann man innerhalb eines Dockerfiles eine build und eine run Umgebung schaffen – auf diese Weise kann man die Applikation innerhalb des Containers bauen und im Anschluss lediglich die benötigten binären Artefakte und andere benötigten Ressourcen in das Lauzeitimage kopieren. Für diese Vorgehensweise würde es sich anbieten, das Entwicklungsrepository via ADD in die build Umgebung des Containerimages zu übertragen.

Diese Vorgehensweise bringt uns direkt zur nächsten “beliebten” Unsicherheit in Container(-images): Lokal hinterlegte Credentials, Entwicklertokens, Cloudzugänge etc. Es gibt vermutlich keine Art von geheimen Daten, die nicht bereits versehentlich in Containerimages “vergessen” wurde. Das kann schnell passieren – ein Entwickler nutzt ein Shellscript mit Zugangsdaten, um von seiner Entwicklungsumgebung in ein Testcluster zu deployen, eine .yaml-Datei, um Daten aus einer Entwicklerdatenbank zu lesen etc. Im “schlimmsten Fall” überträgt er wissentlich eine Datei mit Zugangsdaten, die die containerisierte Applikation später in Produktion nutzen soll, oder hinterlegt sensible Daten im Containerimage als Umgebungsvariable mittels ENV.

Zur Bewältigung dieser Problematik gibt es die Möglichkeit, ähnlich wie eine .gitignore Datei für Git-Repositories eine .dockerignore Datei für den Buildvorgang eines Containerimages zu hinterlegen. Alle in dieser Datei aufgeführten Dateien und Verzeichnisse werden vom Dockerdaemon bei der Verarbeitung des Build-Contexts, von ADD und von COPY Direktiven ignoriert und finden sich somit zu keinem Zeitpunkt im Containerimage wieder. Dringend benötigte Umgebungsvariablen zur Konfiguration der containerisierten Applikation können auch zum Zeitpunkt des Deployments noch übergeben werden, bspw. mittels des Parameters -e via Docker-CLI oder dem Einlesen von Secrets als Umgebungsvariablen in Kubernetes.

Grundlegende Maßnahmen wie die Nutzung eines passenden Base-Images in der FROM Direktive (es muss nicht immer debian:buster oder ubuntu:20.04 sein), das Vermeiden der Öffnung unbenötigter Ports via EXPOSE sowie der Nutzung sog. Kannibalen-Tags (latest zeigt nach jedem Imageupdate auf eine neue Version) sollten darüber hinaus natürlich immer befolgt und beachtet werden.

Distributionsphase

Haben wir nun lokal oder in der Pipeline ein in sich möglichst sicheres Containerimage gebaut, muss es auf die eine oder andere Art und Weise seinen Weg in eine Containerregistry finden, um von anderen Nutzern heruntergeladen und deployed werden zu können. Für diese Vorgänge werden einem von den meisten Containerregistries Werkzeuge an die Hand gegeben, mit deren Hilfe wir den Prozess des Up-/Downloads von Containerimages absichern können.

Letzten Endes sind Containerregistries nichts anderes als APIs mit der Fähigkeit, Nutzer zu authentifizieren und zu authorisieren, Containerimages in Empfang zu nehmen, zu speichern und deren Versionierung im Blick zu behalten. Wie immer, wenn man über das Internet mit einer API spricht, gilt: HTTPS ist Pflicht! Darüber hinaus bieten Registries verschiedene Möglichkeiten, zusätzliche Maßnahmen gegen die Verbreitung unsicherer Images oder die Manipulation vorhandener Containerimages zu treffen. So können Imageregistries oftmals alle verwalteten Containerimages auf bekannte Schwachstellen scannen und den Download solcher Images durch Endnutzer untersagen. Auch die digitale Signierung von verwalteten Containerimages ist oftmals möglich, z.B. mittels sigstore und cosign.

Deploymentphase

Wird unser Containerimage nun im Rahmen eines Kubernetes-Deployments oder Docker-CLI-Befehls aus der Registry gepulled und deployed, haben wir einmal mehr die Möglichkeit, Sicherheit zu forcieren: Wie bereits erwähnt, können wir zum Einen die Voreinstellungen in Hinblick auf Umgebungsvariablen, User- und Gruppenkontext uvm. überschreiben, zum Anderen bietet natürlich auch die Absicherung der ausführenden Infrastruktur selbst die Möglichkeit, dass Deployment so sicher wie möglich zu gestalten.

Hierzu zählen z.B. die Nutzung sog. rootless Container, die von einem Container Runtime Interface (CRI) wie Docker oder containerd ausgeführt werden, die selbst nicht im root Kontext laufen. Auch die Nutzung restriktiver Netzwerk- und Firewallpolicies kann dabei helfen, die Risiken durch möglicherweise vulnerable oder bösartige Container zu minimieren. Konfiguration und Forcierung dieser Maßnahmen innerhalb eines Clusters können schnell zur Sisyphusarbeit werden – hier kann ein gemanagtes Kubernetes-Cluster von Vorteil sein, bspw. Managed Kubernetes von NETWAYS Web Services. Darüber hinaus sollte man eine nachhaltige Update-Strategie für seine Containerimages verfolgen: Es gilt, einen guten Kompromiss zwischen regelmäßigen Updates zu finden, aber nicht sofort jede neue Version (und deren evtl. neu eingeführte Schwachstellen) in Produktion zu deployen.

 

Das Fazit

Container(-images) sicher zu erstellen, zu verwalten und zu deployen ist ein langer Weg voller Stolpersteine. Wie so oft beim Thema Sicherheit wird man die 100% aller Voraussicht nach nicht erreichen – Nichtexistenz von Sicherheitslücken lässt sich nun einmal nicht beweisen. Dennoch habe ich Dich hoffentlich für das Thema und die teils falschen, gängigen Annahmen und Praktiken sensibilisieren können und nebenbei einige Best Practices an die Hand gegeben, die das Risiko durch vulnerable Container deutlich verringern.

Solltest Du nun mehr über den Vorgang des Imagebaus, den Betrieb von Containern in Kubernetes oder Containerisierung im Allgemeinen erfahren wollen, schau Dir doch einmal unser Kubernetes Schulungsangebot von NETWAYS an. In diesem eintägigen Workshop vermittle ich Dir praxisnah und einsteigerfreundlich alles, was Du für die ersten eigenen Schritte mit Docker und Kubernetes wissen musst. Ich freue mich auf Dich!

Daniel Bodky
Daniel Bodky
Consultant

Daniel kam nach Abschluss seines Studiums im Oktober 2021 zu NETWAYS und berät nun Kunden zu den Themen Icinga2 und Kubernetes. Nebenher schreibt er in seiner Freizeit kleinere Tools für verschiedenste Einsatzgebiete, nimmt öfters mal ein Buch in die Hand oder widmet sich seinem viel zu großen Berg Lego. In der wärmeren Jahreszeit findet man ihn außerdem oft auf dem Fahrrad oder beim Wandern.

openSUSE-Kubic getestet…

Schon mal von openSUSE-Kubic gehört? Nein? Okay, als Fan des Chamäleons habe ich davon schon öfter gelesen, aber ausprobiert habe ich es  noch nie.
Also dann beschreibe ich kurz was sich hinter Kubic versteckt.

Kubic ist eine zertifizierte Kubernetes Distribution, die auf openSUSE-MicroOS basiert und dem openSUSE-Tumbleweed (Rolling-Release) zu Grunde liegt.
Es ist sozusagen ein fertiges Betriebssystem für den Einsatz von Kubernetes, alle nötigen Tools sind schon vorinstalliert, damit man sofort beginnen kann
seinen K8s Cluster aufzusetzen oder z.B. Docker Container zu bauen.
Hier wurde ein transactional Server eingerichtet, das heißt, es ist ein read-only BTRFS Filesystem gemountet und man kann nicht wie unter openSUSE/SLES bekannt zypper
per Kommandozeile benutzen um Software-Paket nach zu installieren, aber ich werde noch CodeSnippets mit einbauen wo das ersichtlich wird. Sondern jedes neu installiertes Software-Paket wird vorerst als Snapshot zur Verfügung gestellt, der dann nach dem Reboot des Servers mit einbezogen wird.
Diese Distribution Kubic kann man sich in verschiedenen Formaten herunterladen, z.B. als RaspberryPI, openStack oder ISO-Image uvm siehe Link. Ich habe mir das in unserer openStack Umgebung als Image geladen und getestet.

So dann schauen wir uns das mal auf der Kommandozeile an:
kubic:~ # rpm -qa |grep kube*
patterns-containers-kubic_worker-5.0-25.1.x86_64
hello-kubic-k8s-yaml-1.4-1.3.noarch
kube-prometheus-k8s-yaml-0.5.0+git20200729.f0955e0-1.5.noarch
kuberlr-0.3.1-1.6.x86_64
kubernetes1.22-client-1.22.2-1.1.x86_64
patterns-containers-kubeadm-5.0-25.1.x86_64
patterns-containers-kubic_admin-5.0-25.1.x86_64
kubic-haproxycfg-0.12.2-1.1.x86_64
kubernetes1.22-client-common-1.22.2-1.1.x86_64
kubicctl-0.12.2-1.1.x86_64
kubernetes-client-1.22.2-21.2.x86_64
health-checker-plugins-kubic-1.6-1.1.noarch
kubicd-0.12.2-1.1.x86_64
kubernetes1.22-kubelet-1.22.2-1.1.x86_64
kubernetes1.22-kubelet-common-1.22.2-1.1.x86_64
patterns-containers-kubic_loadbalancer-5.0-25.1.x86_64
kubernetes1.21-kubelet-1.21.5-2.1.x86_64
kubernetes-kubelet-1.22.2-21.2.x86_64
kubernetes1.22-kubeadm-1.22.2-1.1.x86_64
cri-o-kubeadm-criconfig-1.22.0-1.2.x86_64
patterns-containers-container_runtime_kubernetes-5.0-25.1.x86_64
kubernetes-kubeadm-1.22.2-21.2.x86_64
kubic:~ # rpm -qa |grep helm
helm-3.6.3-2.1.x86_64

Das Paket Docker musste ich noch nachinstallieren und mich noch in die Gruppe docker hinzufügen, damit ich mit dem Tool Minikube meine ersten Kubernetes Cluster testen konnte.

Da ich das schon im Vorfeld getan habe zeige ich nochmal kurz den Befehl mit dem ich das Paket Docker, Tmux installiert habe:
ransactional-update pkg install tmux docker
Checking for newer version.
transactional-update 3.6.2 started
Options: pkg install tmux docker
Separate /var detected.
2021-11-29 13:42:32 tukit 3.6.2 started
2021-11-29 13:42:32 Options: -c10 open
2021-11-29 13:42:32 Using snapshot 10 as base for new snapshot 11.
2021-11-29 13:42:32 Syncing /etc of previous snapshot 9 as base into new snapshot "/.snapshots/11/snapshot"
ID: 11
2021-11-29 13:42:33 Transaction completed.
Calling zypper install
zypper: nothing to update
Removing snapshot #11...
2021-11-29 13:42:34 tukit 3.6.2 started
2021-11-29 13:42:34 Options: abort 11
2021-11-29 13:42:34 Discarding snapshot 11.
2021-11-29 13:42:34 Transaction completed.
transactional-update finished

Diese Meldung kommt jetzt, da diese Pakete schon vorhanden sind, sonst würde zypper im Hintergrund die Pakete installieren.
Anschließend muss man einen Reboot durchführen, sonst wird das nicht live geschaltet.
Dann noch folgendes:
usermod -a username -G docker
Damit man mit einem bestimmten $user docker run … ausführen kann.

Hier nochmal zur Vollständigkeit eine Installation vom Paket LXC
transactional-update pkg install lxc
Checking for newer version.
transactional-update 3.6.2 started
Options: pkg install lxc
Separate /var detected.
2021-11-30 08:37:36 tukit 3.6.2 started
2021-11-30 08:37:36 Options: -c11 open
2021-11-30 08:37:36 Using snapshot 11 as base for new snapshot 12.
2021-11-30 08:37:36 Syncing /etc of previous snapshot 10 as base into new snapshot "/.snapshots/12/snapshot"
ID: 12
2021-11-30 08:37:36 Transaction completed.
Calling zypper install
2021-11-30 08:37:37 tukit 3.6.2 started
2021-11-30 08:37:37 Options: callext 12 zypper -R {} install lxc
2021-11-30 08:37:37 Executing `zypper -R /.snapshots/12/snapshot install lxc`:
Loading repository data...
Reading installed packages...
Resolving package dependencies...
The following 5 NEW packages are going to be installed:
libcap-progs liblxc1 lxc lxcfs lxcfs-hooks-lxc

5 new packages to install.
Overall download size: 860.6 KiB. Already cached: 0 B. After the operation, additional 2.6 MiB will be used.
Continue? [y/n/v/...? shows all options] (y):

Checking for file conflicts: [......done]
Warning: 5 packages had to be excluded from file conflicts check because they are not yet downloaded.

Note: Checking for file conflicts requires not installed packages to be downloaded in advance in
order to access their file lists. See option '--download-in-advance / --dry-run --download-only'
in the zypper manual page for details.
2021-11-30 08:37:54 Application returned with exit status 0.
2021-11-30 08:37:55 Transaction completed.
Trying to rebuild kdump initrd
2021-11-30 08:37:55 tukit 3.6.2 started
2021-11-30 08:37:55 Options: close 12
2021-11-30 08:37:55 New default snapshot is #12 (/.snapshots/12/snapshot).
2021-11-30 08:37:55 Transaction completed.

Wichtig hier:
Please reboot your machine to activate the changes and avoid data loss.
New default snapshot is #12 (/.snapshots/12/snapshot).
transactional-update finished

Wie man jetzt zum Testen von Kubernetes mit z.B. Minikube kommt, werde ich hier nicht beschreiben, denn dazu gibt es
genügend Informationen im Internet. Vor allem kann ich das Kubernetes , das wir hosten, sehr empfehlen, einfach Anfrage bei uns starten.

Nur wie gesagt, wer den Einstieg in ein selbst gehostetes K8s lernen möchte, ist der Einstieg mit openSUSE-Kubic sehr vorteilhaft, da vieles schon vorkonfiguriert ist.

So ich denke für den ersten Überblick von Kubic reicht das vorerst, ich wünsche schon mal viel Spaß beim ausprobieren und besucht unser K8s Training!

Johannes Carraro
Johannes Carraro
Senior Systems Engineer

Bevor Johannes bei NETWAYS anheuerte war er knapp drei Jahre als Systemadministrator in Ansbach tätig. Seit Februar 2016 verstärkt er nun unser Team Operations als Senior Systems Engineer. In seiner Freizeit spielt Johannes E-Gitarre, bastelt an Linux Systemen zuhause herum und ertüchtigt sich beim Tischtennisspielen im Verein, bzw. Mountainbiken, Inlinern und nicht zuletzt Skifahren.

Docker – ein erster Eindruck!

Ich habe in den letzten Monaten meiner Ausbildung schon viel über verschiedene Software, Strukturen und Programiersprachen gelernt. Seit einigen Wochen arbeite ich in der Managed Services-Abteilung mit Docker und bin wirklich fasziniert, was Docker alles kann und wie es funktioniert.

Was genau ist Docker?

Docker ist eine Containerisierungstechnologie, die die Erstellung und den Betrieb von Linux-Containern ermöglicht, die man nicht mit virtuellen Maschinen verwechseln sollte.

Docker vereinfacht die Bereitstellung von Anwendungen, weil sich Container, die alle nötigen Pakete enthalten, leicht als Dateien transportieren und installieren lassen. Container gewährleisten die Trennung und Verwaltung der auf einem Rechner genutzten Ressourcen. Das beinhaltet laut Aussage der Entwickler: Code, Laufzeitmodul, Systemwerkzeuge, Systembibliotheken – alles was auf einem Rechner installiert werden kann.

Welche Vorteile bringt Docker?

Ein Vorteil ist, dass man diese Container sehr flexibel erstellen, einsetzen, kopieren und zwischen Umgebungen hin- und her verschieben kann. Sogar ein Betrieb in der Cloud ist dadurch möglich und eröffnet viele neue Möglichkeiten. Das Ausführen verschiedener Container bietet zusätzlich einige Sicherheitsvorteile. Wenn man Anwendungen in verschiedenen Containern ausführt, hat jeder Container nur Zugriff auf die Ports und Dateien, die der andere Container explizit freigibt.

Darüber hinaus bieten Container ein höheres Maß an Kontrolle darüber, welche Daten und Software auf dem Container installiert sind. Schadsoftware, die in einem Container ausgeführt wird, wirkt sich nicht auf andere Container aus.

Was ist der Unterschied zwischen Docker-Containern und virtuellen Maschinen?

Virtuelle Maschinen enthalten von der simulierten Hardware, über das Betriebssystem bis zu den installierten Programmen eine große Menge an Informationen. Die Folge: Sie verbrauchen viel Speicherplatz und Ressourcen.

Der große und schöne Unterschied zu herkömmlichen virtuellen Maschinen ist, dass die Container kein Betriebssystem beinhalten und booten müssen, sondern lediglich die wichtigen Daten der Anwendung enthalten. Wollten wir früher beispielsweise eine Datenbank und einen Webserver getrennt voneinander starten, benötigen wir zwei VMs. Mit Docker werden hier nur noch zwei Container benötigt, ohne dass man die VMs booten muss, die kaum Ressourcen verbrauchen – im Vergleich zu den virtuellen Maschinen.

Virtuelle Maschinen jedoch haben nach wie vor ihre Daseinsberechtigung: zum Beispiel wenn auf einem Host mehrere Maschinen mit jeweils unterschiedlichen Betriebssystemen oder Hardware-Spezifikationen simuliert werden müssen. Denn ein Docker-Container enthält kein eigenes Betriebssystem und keine simulierte Hardware. Hier wird auf das System des Hosts zugegriffen, so dass alle Container Betriebssystem und Hardware gemeinsam nutzen.

Fazit zum Thema Docker:

Den Einsatz von Docker betrachte ich als sinnvoll. Es ergeben sich viele Vorteile und so ist es inzwischen möglich, eine Softwareumgebung rascher aufzubauen, ohne viel Speicherplatz und Ressourcen zu verbrauchen.

Out-of-Memory: Marathon 1.6+ und Heap Size

Auch wenn in Sachen Container Orchestrierung Kubernetes klar gegen Marathon und Mesos gewonnen hat bietet das Duo seit Jahren eine zuverlässig Plattform. Durch die stetige Weiterentwicklung und die Umstellung auf Systemd ändert sich leider auch immer wieder die Konfigurationen der Dienste.

Für einen stabilen Betrieb von Marathon sollte die maximale Heap-Size der Java Virtual Machine (JVM) eine angemessene Größe haben. Zu wenig Memory äußert sich z.B. durch erhöhte Antwortzeiten oder durch die Aktivierung des OOM-Killer. Wo in vergangenen Versionen und Konfigurationen noch die /etc/defaults/marathon zum Setzen der Javaoption verwendet wurde, kann man hier seit Marathon 1.6 vergeblich sein Glück suchen. Aus meiner Sicht ist ein Verzicht auf /etc/defaults/marathon und eine saubere Definition in einer systemd Unit-Datei natürlich zu begrüßen. Die Umgebungsvariablen lassen sich auch dort einfach definieren, aber wie genau muss das für Marathon aussehen?

# systemctl edit marathon.service
[Service]
Environment=JAVA_OPTS=-Xmx2g

Mit systemclt edit kann man eine bestehende Unit-Datei einfach erweitern und der Parameter Environment setzt die Umgebungsvariable.  Alternativ könnte man auch Environmentfile=/etc/defaults/marathon verwenden um das gewohnte Verhalten wieder herzustellen.

Nach dem Speichern braucht es einen Daemon-Reload, damit die neue Unit-Datei eingelesen wird.

# systemctl daemon-reload
# systemctl restart marathon.service

Eigentlich bleibt also alles beim Alten, man muss nur die zusätzlichen Parameter in die JAVA_OPTS setzen und den Service neu starten ?.

 

Achim Ledermüller
Achim Ledermüller
Lead Senior Systems Engineer

Der Exil Regensburger kam 2012 zu NETWAYS, nachdem er dort sein Wirtschaftsinformatik Studium beendet hatte. In der Managed Services Abteilung ist unter anderem für die Automatisierung des RZ-Betriebs und der Evaluierung und Einführung neuer Technologien zuständig.

Was ist Consul und wozu dient es?

Eine Beschreibung in einem Wort ist hier Service Mesh. Was jemanden, der sich zuerst mit Consul beschäftigt, ebenfalls nicht wirklich weiterhilft. Dieser Blogpost zeigt die Features von Consul auf und gibt Hinweise zu deren Anwendung.

Consul verwaltet Services und Knoten in der Art eines “Verzeichnisdienstes”, also in der Form was läuft wo? Zugegriffen wird dabei über DNS oder alternativ mittels HTTP(s). Consul wird entweder im Modus Server oder Agent betrieben. Die Server speichern die Daten, kommen mehrere zum Einsatz werden die Daten automatisch synchronisiert.

Auf die Daten kann man direkt auf den Servern mittels DNS oder HTTP(s) zugreifen oder über den eh benötigten Agenten. Über den Agenten meldet sich jeder beliebige Knoten bzw. Host in Consul an und wird als Node registriert. Ebenfalls können dann auch Services registriert werden. Ein Webserver z.B. registriert sich so zum Beispiel als neues Cluster-Mitglied zu einem bereits bestehenden Services. Zusätzlich können auch Tags gesetzt werden, die sich via DNS-Abfrage dann als Aliases darstellen.

Nur was macht man nun mit diesen Informationen? Einfach wäre es nun seinen eigentlichen DNS auf diese Informationen weiterzuleiten. Dies geht wie oben schon angedeutet mit Anfragen entweder direkt an die Server oder über einen auf den DNS-Servern betriebenen Agenten. Damit ist leicht ein DNS-Round-Robin zu realisieren.

Möchte man hingegen einen Load-Balancer als Proxy für seinen Dienst betreiben, kommt Consul-Templates als eigenständiges Produkt ins Spiel. Es handelt sich hierbei um eine Template-Engine, die die Informationen aus Consul in Konfigurationsdateien überführt. Die Konfiguration kann dabei noch um zusätzliche Konfigurationsparamter, aus dem in Consul integrierten Key-Value-Store, angereichert werden.

Abschließend bietet Consul als weiteres Feature die Verwaltung einer Certificate Authority (CA). Die ausgestellten Zertifikate können sogleich mit Hilfe des Agents via Proxy (sidecar proxy) zur Absicherung der betriebenen Dienste mittels TLS eingesetzt werden. Das geschieht völlig transparent und Konfigurationsdateien müssen nicht angepasst werden.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.