<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Servivum · Blog]]></title><description><![CDATA[IT. Routine. Nonsens.]]></description><link>https://blog.servivum.com/</link><image><url>https://blog.servivum.com/favicon.png</url><title>Servivum · Blog</title><link>https://blog.servivum.com/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Tue, 14 Apr 2026 10:11:45 GMT</lastBuildDate><atom:link href="https://blog.servivum.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[PHP 8.5 veröffentlicht 🎉]]></title><description><![CDATA[Die finale Version PHP 8.5 wurde veröffentlicht und steht bei Servivum zur Verfügung.]]></description><link>https://blog.servivum.com/php-8-5-veroffentlicht/</link><guid isPermaLink="false">6921aa4a31e430000119e9a3</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Sat, 22 Nov 2025 12:30:05 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2025/11/ben-griffiths-2Rd-hwT2xQ0-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2025/11/ben-griffiths-2Rd-hwT2xQ0-unsplash.jpg" alt="PHP 8.5 veröffentlicht 🎉"><p>Der Release Candidate von PHP 8.5 war bereits seit einigen Monaten auf unserer Plattform verfügbar. Nun wurde die finale Version 8.5.0 veröffentlicht. Sie steht ab sofort bei Servivum zur Verfügung.</p><p>Die neue Version bietet viele spannende Features wie die neue URI-Extension, den Pipe-Operator, Property-Modifikation beim Klonen, das NoDiscard-Attribut und vieles mehr. Natürlich bringt PHP 8.5 auch wieder viele Performance-Verbesserungen mit sich.</p><p><a href="https://www.php.net/releases/8.5/en.php">Weitere Details</a> zur neuen Version sowie einen <a href="https://www.php.net/manual/en/migration85.php">Migration Guide</a> findet ihr auf der offiziellen Website.</p><p>Die aktuell bei Servivum verfügbaren Module und Extensions für PHP 8.5 könnt ihr auf unserer <a href="https://php8.5.servivum.com/">PHP-Info-Seite</a> einsehen.</p><p>Viel Spaß mit PHP 8.5!</p><p>Foto von <a href="https://unsplash.com/de/@benofthenorth">Ben Griffiths (unsplash.com)</a></p>]]></content:encoded></item><item><title><![CDATA[Gefälschte Servivum-E-Mails im Umlauf]]></title><description><![CDATA[<p>Mehrere unserer Kunden haben heute Phishing-E-Mails erhalten, die sich als Servivum ausgeben. Bitte sei wachsam.</p><h2 id="woran-erkennst-du-die-f-lschung">Woran erkennst du die Fälschung?</h2><p>Die betrügerische E-Mail hat den Betreff <strong>"Action Required: Your Storage Limit Expires"</strong> und behauptet, dein Speicherplatz läuft ab.</p><p><strong>Das ist Betrug, denn:</strong></p><ul><li>Servivum verschickt KEINE Storage-Warnungen per E-Mail</li><li>Wir fragen</li></ul>]]></description><link>https://blog.servivum.com/gefalschte-servivum-e-mails-im-umlauf/</link><guid isPermaLink="false">68baad1831e430000119e987</guid><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Fri, 05 Sep 2025 10:12:01 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2025/09/scott-rodgerson-BwMcYuHI9OI-unsplash-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2025/09/scott-rodgerson-BwMcYuHI9OI-unsplash-1.jpg" alt="Gefälschte Servivum-E-Mails im Umlauf"><p>Mehrere unserer Kunden haben heute Phishing-E-Mails erhalten, die sich als Servivum ausgeben. Bitte sei wachsam.</p><h2 id="woran-erkennst-du-die-f-lschung">Woran erkennst du die Fälschung?</h2><p>Die betrügerische E-Mail hat den Betreff <strong>"Action Required: Your Storage Limit Expires"</strong> und behauptet, dein Speicherplatz läuft ab.</p><p><strong>Das ist Betrug, denn:</strong></p><ul><li>Servivum verschickt KEINE Storage-Warnungen per E-Mail</li><li>Wir fragen NIEMALS per E-Mail nach deinen Login-Daten</li><li>Die E-Mail kommt nicht von unseren Servern</li></ul><h2 id="was-solltest-du-tun">Was solltest du tun?</h2><p>✅ <strong>E-Mail erhalten?</strong> → Sofort löschen, nicht auf Links klicken<br>🚨 <strong>Bereits geklickt?</strong> → Ändere sofort dein Passwort über servivum.com</p><h2 id="wie-konnte-das-passieren">Wie konnte das passieren?</h2><p>Die Angreifer haben KEINE Kundendaten erbeutet. Sie identifizieren Servivum-Kunden über öffentliche DNS-Einträge und verschicken dann Massen-Phishing-Mails. Das ist eine gängige Methode und bedeutet kein Datenleck.</p><h2 id="was-unternehmen-wir">Was unternehmen wir?</h2><ul><li>E-Mail-Sicherheit wurde verschärft</li><li>Gefälschte Mails werden künftig automatisch blockiert</li></ul><h2 id="wichtig-zu-merken">Wichtig zu merken</h2><p><strong>Echte Servivum-E-Mails:</strong></p><ul><li>Kommen nur von @servivum.com</li><li>Enthalten nie Login-Aufforderungen</li><li>Drohen nicht mit Kontosperrung</li></ul><h2 id="fragen">Fragen?</h2><p>Unsicher, ob eine E-Mail echt ist? Kontaktiere uns:</p><p><strong>Support:</strong> <a href="mailto:support@servivum.com">support@servivum.com</a><br><strong>Sicherheit:</strong> <a href="mailto:security@servivum.com">security@servivum.com</a></p><p>Vielen Dank für deine Aufmerksamkeit und dein Vertrauen.</p><p>Dein Servivum Team</p>]]></content:encoded></item><item><title><![CDATA[PHP 8.4 veröffentlicht 🎉]]></title><description><![CDATA[Die finale Version PHP 8.4.1 wurde veröffentlicht und steht auch bei Servivum zur Verfügung.]]></description><link>https://blog.servivum.com/php-8-4-veroeffentlicht/</link><guid isPermaLink="false">6740b92231e430000119e946</guid><category><![CDATA[PHP]]></category><category><![CDATA[Servivum]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Fri, 22 Nov 2024 17:46:20 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2024/11/ben-griffiths-4wxWBy8Jo1I-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2024/11/ben-griffiths-4wxWBy8Jo1I-unsplash.jpg" alt="PHP 8.4 veröffentlicht 🎉"><p>Der Release Candidate von PHP 8.4 war bereits seit einigen Monaten auf unserer Plattform verfügbar. Gestern wurde nun die finale Version 8.4.1 veröffentlicht. Sie steht nun ebenfalls bei Servivum zur Verfügung.</p><p>Die neue Version bietet viele spannende Features wie Property Hooks, Asymmetric Visibility, BCMath Object API und vieles mehr. Natürlich bringt PHP 8.4 auch wieder viele Performance-Verbesserungen mit sich.‌‌</p><p><a href="https://www.php.net/releases/8.4/en.php">Weitere Details</a> zur neuen Version sowie einen <a href="https://www.php.net/manual/en/migration84.php">Migration Guide</a> findet ihr auf der offiziellen Website.‌‌</p><p>Viel Spaß mit PHP 8.4!</p><p>Foto von <a href="https://unsplash.com/de/@benofthenorth">Ben Griffiths (unsplash.com)</a></p>]]></content:encoded></item><item><title><![CDATA[100.000 Websites verteilen Schadcode über polyfill.io]]></title><description><![CDATA[<p>In den letzten Tagen wurde bekannt, dass die Domain polyfill.io, über die die bekannte JavaScript-Bibliothek Polyfill.js bereitgestellt wird, von einer chinesischen Firma übernommen wurde und nun Schadcode verbreitet.</p><p>Diese Bibliothek wird verwendet, um in älteren Browsern moderne Funktionen nachzurüsten, was man auch als Polyfill bezeichnet. Aufgrund der nun</p>]]></description><link>https://blog.servivum.com/schadcode-uber-polyfill-io-handelt-jetzt/</link><guid isPermaLink="false">6692499931e430000119e84e</guid><category><![CDATA[polyfill]]></category><category><![CDATA[security]]></category><category><![CDATA[csp]]></category><category><![CDATA[open-source]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Fri, 28 Jun 2024 12:06:00 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2024/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2024/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg" alt="100.000 Websites verteilen Schadcode über polyfill.io"><p>In den letzten Tagen wurde bekannt, dass die Domain polyfill.io, über die die bekannte JavaScript-Bibliothek Polyfill.js bereitgestellt wird, von einer chinesischen Firma übernommen wurde und nun Schadcode verbreitet.</p><p>Diese Bibliothek wird verwendet, um in älteren Browsern moderne Funktionen nachzurüsten, was man auch als Polyfill bezeichnet. Aufgrund der nun offengelegten Sicherheitslücke ist es dringend erforderlich, dass alle, die diesen Polyfill über polyfill.io eingebunden haben, sofort handeln, um ihre Websites und ihre Nutzer:innen zu schützen.</p><h4 id="was-ist-passiert">Was ist passiert?</h4><p>Polyfill.js ist eine Open-Source-Bibliothek und kann sicher über Plattformen wie GitHub heruntergeladen und eingebunden werden. Häufig wurde dies nicht getan, sondern die Polyfill-Bibliothek wurde mittels Skript-Tag von der externen Domain polyfill.io heruntergeladen. Diese Domain wurde mittlerweile aber von einer chinesischen Firma übernommen, und statt der Originalversion wird nun eine manipulierte Version mit Schadcode ausgeliefert. Hier sind einige der Artikel, die das Problem im Detail beschreiben:</p><ul><li><a href="https://sansec.io/research/polyfill-supply-chain-attack" rel="noreferrer">Sansec: Polyfill Supply Chain Attack</a></li><li><a href="https://www.heise.de/news/Jetzt-handeln-Schadcode-ueber-CDN-des-JavaScript-Service-Polyfill-io-verteilt-9778256.html" rel="noreferrer">Heise: Schadcode über CDN des JavaScript-Service Polyfill.io verteilt</a></li><li><a href="https://www.golem.de/news/angriff-via-polyfill-io-ueber-100-000-webseiten-verbreiten-ploetzlich-malware-2406-186452.html" rel="noreferrer">Golem: Angriff via polyfill.io - Über 100.000 Webseiten verbreiten plötzlich Malware</a></li></ul><h4 id="warum-polyfill-nicht-mehr-ben-tigt-wird">Warum Polyfill nicht mehr benötigt wird</h4><p>Der Autor von Polyfill.js hat bereits darauf hingewiesen, dass man es nicht mehr verwenden soll. Das hat damit zu tun, dass ältere Browser, die diese Polyfills benötigen, mittlerweile einen sehr geringen Marktanteil haben. Modernen Browser hingegen benötigen keine Polyfills mehr. Solltest du alte Applikationen betreiben, die auf die Kompatibilität zu älteren Browser angewiesen ist, z. B. für interne Prozesse, kannst die gehosteten Alternativen von Polyfill.js bei <a href="https://cdnjs.cloudflare.com/polyfill/">Cloudflare</a> oder Fastly nutzen oder noch besser eine eigene Version mit deiner Applikation bereitstellen, sodass du nicht von externen Quellen abhängig bist.</p><h4 id="ma-nahmen-zur-vermeidung-solcher-angriffe">Maßnahmen zur Vermeidung solcher Angriffe</h4><p>Diese Art von Angriffen nennt man Supply Chain Attack. Heutige Apps können aufgrund der Komplexität nicht komplett selbstständig entwickelt werden, sondern benötigen in der Regel externe Abhängigkeiten. Daher werden Supply Chain Attacks immer häufiger. Hier ein paar Maßnahmen, die du ganz generell ergreifen kannst, um deine Applikation auch vor ähnlichen Angriffen in Zukunft zu schützen.</p><p><strong>Aktualisiert eure Bibliotheken:</strong> Nutzt nur vertrauenswürdige und aktuelle Bibliotheken.</p><p><strong>Nutzt sichere Quellen:</strong> Ladet externe Bibliotheken direkt von vertrauenswürdigen Quellen wie GitHub herunter, um sicherzustellen, dass ihr die Originalversion verwendet. Nutzt keine Ressourcen von externen CDNs, wenn dies nicht notwendig ist. Ladet die benötigten Dateien herunter und stellt sie über eure eigene App bereit. Dies gibt euch die Kontrolle darüber, welche Version der Datei verwendet wird, und schützt euch vor unerwünschten Änderungen durch Dritte. Mit dem Aufkommen von HTTP/2 sind die früheren Vorteile von CDNs, wie schnelleres Laden und geringere Latenz bei der Integration von Assets geringer geworden. CDNs sind weiterhin sinnvoll, wenn der gesamte Inhalt darüber ausgeliefert und damit beschleunigt wird.</p><p><strong>Verwendet Subresource Integrity (SRI):</strong> SRI erlaubt es, eine Integritätsprüfung (einen Hash) anzugeben, um sicherzustellen, dass der heruntergeladene Inhalt nicht verändert wurde. Hier ein Beispiel:</p><blockquote>&lt;script src="<a href="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?version=4.8.0">https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?version=4.8.0</a>" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxFf/2g+7t1LZ8q7v6AJN0GRd3vZ1sT" crossorigin="anonymous"&gt;&lt;/script&gt;</blockquote><p>Mit SRI wird das Skript nur geladen, wenn der Hash des heruntergeladenen Skripts mit dem angegebenen Hash übereinstimmt. Wenn der Inhalt geändert wurde, lädt der Browser das Skript nicht.</p><p><strong>Verwendet Content Security Policy (CSP):</strong> Implementiert eine Content Security Policy, um zu kontrollieren, welche Skripte und Ressourcen auf euren Webseiten geladen werden dürfen. Eine CSP kann das Nachladen von Schadcode von nicht autorisierten Domains unterbinden. Zum Beispiel hätte eine CSP in diesem Fall das Laden von Schadcode von <code>www.googie-anaiytics.com</code> verhindert.</p><p>Um CSP in einer nginx-Konfiguration zu aktivieren, könnt ihr folgenden Code verwenden:</p><blockquote>add_header Content-Security-Policy "script-src 'self' <a href="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?version=4.8.0">https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?version=4.8.0</a>";</blockquote><p>Diese Richtlinie erlaubt nur Skripte von der eigenen Domain (<code>self</code>) und von <code>cdnjs.cloudflare.com</code>. Da der Schadcode von einer anderen Domain nachgeladen wird, hätte die CSP dies verhindert.</p><p><strong>Bleibt informiert:</strong> Abonniert Sicherheitsupdates und informiert euch regelmäßig über potenzielle Bedrohungen.</p><p>Indem ihr diese Maßnahmen befolgt, könnt ihr das Risiko von Supply Chain Angriffen erheblich reduzieren und die Sicherheit eurer Websites gewährleisten.</p>]]></content:encoded></item><item><title><![CDATA[Unsere brandneue Website ist online 🎉]]></title><description><![CDATA[<p>Wir freuen uns sehr, euch unsere brandneue Website zu präsentieren. Seit unserer Gründung im Jahr 2012 haben wir kontinuierlich daran gearbeitet, nicht nur eine zuverlässige, sondern auch intuitive Hosting-Plattform anzubieten. Unsere Website hinkte dabei den aktuellen Entwicklungen immer ein bisschen hinterher, aber das ändert sich nun.</p><p>Unsere neue Website deckt</p>]]></description><link>https://blog.servivum.com/unsere-brandneue-website-ist-online/</link><guid isPermaLink="false">65b922c67c1470000162b885</guid><category><![CDATA[Website]]></category><category><![CDATA[Servivum]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Tue, 30 Jan 2024 12:37:00 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2024/01/oc-gonzalez-xg8z_KhSorQ-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2024/01/oc-gonzalez-xg8z_KhSorQ-unsplash.jpg" alt="Unsere brandneue Website ist online 🎉"><p>Wir freuen uns sehr, euch unsere brandneue Website zu präsentieren. Seit unserer Gründung im Jahr 2012 haben wir kontinuierlich daran gearbeitet, nicht nur eine zuverlässige, sondern auch intuitive Hosting-Plattform anzubieten. Unsere Website hinkte dabei den aktuellen Entwicklungen immer ein bisschen hinterher, aber das ändert sich nun.</p><p>Unsere neue Website deckt jetzt umfassender als zuvor alle unsere Leistungen ab, von Apps über Domains bis hin zum E-Mail-Hosting. Wie immer legen wir großen Wert auf Einfachheit. Die Inhalte sollen klar und verständlich sein. Wir hoffen, das ist uns mit der neuen Website gelungen. Uns ist wichtig, dass neue Kund:innen noch besser als zuvor unsere Philosophie für ein faires und nachhaltiges Hosting-Angebot begreifen.</p><p>Wir sind auch sehr stolz, so viele positive Kund:innenstimmen für unsere Website erhalten zu haben. Besonders unser Support wird hier immer wieder lobend erwähnt. Um den Support gar nicht erst notwendig zu machen, haben wir die Produktseiten um passende Fragen und Antworten ergänzt, die wir häufig erhalten. Das einfache Preismodell ist dort ebenfalls zu finden.</p><p>Nicht weniger stolz sind wir darauf, wie gut unsere Website beim <a href="https://www.websitecarbon.com/website/servivum-com/">Website Carbon Calculator</a> abschneidet. Dies unterstreicht unsere Bemühungen zu einem sauberen und effizienten Hosting-Angebot.</p><p>Für die Entwicklung unserer Website haben wir auf <a href="https://nextjs.org/">Next.js</a> und <a href="https://tailwindui.com/">Tailwind UI</a> gesetzt, zwei herausragende Tools, die es ermöglichen, eine schnelle, responsive und ästhetisch ansprechende Websites zu kreieren. Vielen Dank an die Macher:innen dieser tollen Tools 🙏</p><p>Und nun viel Spaß mit der neuen Website ✨</p><p>Foto von <a href="https://unsplash.com/de/@ocvisual">OC Gonzalez (unsplash.com)</a></p>]]></content:encoded></item><item><title><![CDATA[PHP 8.3 ist da]]></title><description><![CDATA[<p><strong>Entdecke die neueste Version bereits bei Servivum</strong></p><p>Wir freuen uns, eine spannende Nachricht mit euch zu teilen: PHP 8.3 ist nun offiziell veröffentlicht und bei Servivum verfügbar! Nach einer erfolgreichen Testphase mit dem <a href="https://blog.servivum.com/php-8-3-rc-steht-bereit/">Release Candidate</a>, in der wir wichtige Erkenntnisse sammeln konnten, steht dir jetzt die finale Version von</p>]]></description><link>https://blog.servivum.com/php-8-3-ist-da/</link><guid isPermaLink="false">6568b9727c1470000162b82a</guid><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Thu, 30 Nov 2023 16:43:54 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2023/11/mohammad-rahmani-8qEB0fTe9Vw-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2023/11/mohammad-rahmani-8qEB0fTe9Vw-unsplash.jpg" alt="PHP 8.3 ist da"><p><strong>Entdecke die neueste Version bereits bei Servivum</strong></p><p>Wir freuen uns, eine spannende Nachricht mit euch zu teilen: PHP 8.3 ist nun offiziell veröffentlicht und bei Servivum verfügbar! Nach einer erfolgreichen Testphase mit dem <a href="https://blog.servivum.com/php-8-3-rc-steht-bereit/">Release Candidate</a>, in der wir wichtige Erkenntnisse sammeln konnten, steht dir jetzt die finale Version von <a href="https://www.php.net/releases/8.3/en.php">PHP 8.3</a> zur Verfügung.</p><p>Was bedeutet das für dich? Die finale Version von PHP 8.3 bringt eine Fülle von Verbesserungen und neuen Features, die deine Entwicklungserfahrung auf ein neues Niveau heben werden. Von optimierter Performance bis hin zu neuen Sprachfunktionen – PHP 8.3 ist darauf ausgerichtet, deine Applikationen effizienter, schneller und sicherer zu machen.</p><p>Einige Highlights von PHP 8.3 umfassen:</p><ul><li><strong>Verbesserte Performance:</strong> Genieße eine schnellere Ausführung deiner Anwendungen dank weitreichender Optmierungen.</li><li><strong>Neue Sprachfeatures:</strong> Entdecke neue Sprach-Features, die es dir ermöglichen, eleganteren und update-freundlicheren Code zu schreiben.</li><li><strong>Erhöhte Sicherheit:</strong> Mit verbesserten Sicherheitsmechanismen kannst du sicher sein, dass deine Anwendungen noch robuster gegenüber Bedrohungen sind.</li></ul><p>Die Umstellung auf PHP 8.3 ist einfach. Wenn du bereits den Release Candidate genutzt hast, braucht du nichts weiter tun. Wir kümmern uns um das Update. Falls du noch bei einer älteren Version bist, kannst du ganz unkompliziert über unser <a href="https://app.servivum.com/">Servivum App</a> die Version für jede deiner Applikation anpassen.</p><p>Als große PHP-Fans sind wir bei Servivum stolz darauf mit PHP 8.3 den nächsten wichtigen Meilenstein in der technologischen Entwicklungen anbieten zu können. Uns ist es wichtig, dass wir dir die Tools bieten, die deine tägliche Arbeit verbessern.</p><p>Für weitere Informationen zu den neuen Funktionen von PHP 8.3 schau auf der <a href="https://www.php.net/releases/8.3/en.php">offiziellen Seite</a> vorbei oder bei den Kolleg:innen von <a href="https://laravel-news.com/php-8-3-0">Laravel News</a>. Viel Spaß!</p><p>Foto von <a href="https://unsplash.com/de/@afgprogrammer">Mohammad Rahmani (unsplash.com)</a></p>]]></content:encoded></item><item><title><![CDATA[PHP 8.3 RC steht bereit]]></title><description><![CDATA[<p>Wir haben tolle Neuigkeiten. Ab sofort bieten wir für unsere Servivum Apps PHP in der Version 8.3 als Release Candidate an. Was das bedeutet? Du hast schon jetzt die Möglichkeit, die brandneuen Features von PHP 8.3 zu testen.</p><p>Derzeit läuft bei uns der Release Candidate 4 (RC4). Die</p>]]></description><link>https://blog.servivum.com/php-8-3-rc-steht-bereit/</link><guid isPermaLink="false">6532c00b7c1470000162b7dd</guid><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Fri, 20 Oct 2023 18:10:56 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2023/10/ben-griffiths-gAe1pHGc6ms-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2023/10/ben-griffiths-gAe1pHGc6ms-unsplash.jpg" alt="PHP 8.3 RC steht bereit"><p>Wir haben tolle Neuigkeiten. Ab sofort bieten wir für unsere Servivum Apps PHP in der Version 8.3 als Release Candidate an. Was das bedeutet? Du hast schon jetzt die Möglichkeit, die brandneuen Features von PHP 8.3 zu testen.</p><p>Derzeit läuft bei uns der Release Candidate 4 (RC4). Die finale Version von PHP 8.3 wird voraussichtlich am 23. November 2023 veröffentlicht. Bis dahin werden wir dich natürlich mit allen weiteren Release Candidates versorgen und auch die finale Version wird dir automatisch zur Verfügung stehen.</p><p>Was genau ist ein Release Candidate? Ein Release Candidate (RC) ist quasi die "Generalprobe" vor der großen Premiere. Es ist eine Version, die fast fertig ist und nur noch auf das finale Go wartet. Hier und da kann es noch kleine Unstimmigkeiten geben, aber genau dafür ist ein RC da: Die letzten Fehler auszumerzen.</p><p>Weitere Informationen zu PHP 8.3 findest du in <a href="https://www.zend.com/blog/php-8-3">diesem Artikel von Zend</a>.</p><p>Wir bemühen uns bei Servivum immer am Puls der Zeit zu sein und freuen uns sehr, dir diese Neuerung präsentieren zu können. Probiere PHP 8.3 aus und lass uns wissen, wie es dir gefällt!</p><p>Foto von <a href="https://unsplash.com/de/fotos/blauer-elefant-pluschtier-auf-schwarzem-laptop-gAe1pHGc6ms">Ben Griffiths (unsplash.com)</a></p>]]></content:encoded></item><item><title><![CDATA[🐳 Provisionieren eines Docker-Hosts]]></title><description><![CDATA[<p>Lange wurde hier im Blog nichts mehr geschrieben, aber nun möchte ich das Schreiben wieder aufnehmen. Bitte entschuldigt, wenn der Satzbau noch etwas holprig ist. Das wird sich mit den kommenden Artikel alles wieder finden.</p><p>Ich möchte heute ein paar Worte über die Einrichtung eines Servers mit Docker, also einem</p>]]></description><link>https://blog.servivum.com/provisionieren-eines-docker-hosts/</link><guid isPermaLink="false">5dd7f8a68735e90001fa8ba1</guid><category><![CDATA[Docker]]></category><category><![CDATA[Container]]></category><category><![CDATA[Host]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Sat, 23 Nov 2019 22:10:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="🐳 Provisionieren eines Docker-Hosts"><p>Lange wurde hier im Blog nichts mehr geschrieben, aber nun möchte ich das Schreiben wieder aufnehmen. Bitte entschuldigt, wenn der Satzbau noch etwas holprig ist. Das wird sich mit den kommenden Artikel alles wieder finden.</p><p>Ich möchte heute ein paar Worte über die Einrichtung eines Servers mit Docker, also einem Docker-Host verlieren.</p><p>Für den schnellen Going-Live einer Applikation bietet sich noch immer ein Root-Server, egal ob Bare Metal oder VM, an. VMs gibt es mittlerweile wie Sand am Meer und man ist noch nicht mal gezwungen zu einem der drei Großen (AWS, Azure, Google Cloud) zu gehen. Empfehlen kann ich beispielsweise die VMs in der Hetzner Cloud.</p><p>Ich gehe in diesem Beispiel von einer Maschine mit Debian Buster aus, zu der ihr Root-Zugang erhalten haben solltet. Debian ist bei mir das Mittel der Wahl, weil es im Verhältnis zu einigen anderen Distributionen, wie beispielsweise Ubuntu noch mal deutlich schlanker daherkommt. Weniger Pakete reduzieren den Update-Aufwand und die möglichen Angriffsvektoren. Das Host-System muss ja auch eigentlich gar nicht viel können. Im Gegenteil: Ich möchte es sauber halten, um mich vom Wirt nicht zu stark abhängig zu machen und ihn im Zweifel auch schnell wieder ersetzen zu können - parasite mode on!</p><h3 id="docker-engine-der-stoff-aus-dem-die-tr-ume-gemacht-sind-">Docker Engine - der Stoff aus dem die Träume gemacht sind 🐳</h3><p>Die <a href="https://docs.docker.com/install/linux/docker-ce/debian/">offizielle Dokumentation</a> beschreibt ziemlich gut, wie man Docker auf der Maschine installiert bekommt. Ein paar Gedanken dazu: </p><ol><li>Ich nutze grundsätzlich die Installation mittels Repository, worauf in diesem Artikel auch der Fokus liegt.</li><li>Eine Ausnahme stellt nur die Installation von Docker auf einem RaspberryPi dar. Da dies über das Repository nicht funktioniert. Dann greife ich auf das Convenience Script zurück.</li><li>Das Anpinnen einer bestimmten Docker-Version ist sinnvoll, allerdings versuche ich immer mit der aktuellen stabilen Version zu arbeiten. Derzeit ist das 19.03.5. Ich pinne die Version also in der Regel nicht an.</li></ol><p>Auf der Debian-Maschine wird durch den Root-Login kein sudo genutzt. Das können wir also weglassen.</p><p>Zunächst wird der Paket-Index aktualisiert:</p><pre><code>apt-get update</code></pre><p>Als nächstes werden die Abhängigkeiten installiert:</p><pre><code>apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common</code></pre><p>Jetzt lädt man den GPG-Schlüssel von Docker, um die Signierung des Pakets zu überprüfen:</p><pre><code>curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -</code></pre><p>Anschließend kann man den Fingerprint des Paketes überprüfen:</p><pre><code>apt-key fingerprint 0EBFCD88</code></pre><p>Nun wird dem Paket-Manager apt eine neue Quelle hinzugefügt, über die die Docker-Engine heruntergeladen werden kann:</p><pre><code>add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"</code></pre><p>Jetzt wird der Paket-Index erneut aktualisiert und Docker installiert:</p><pre><code>apt-get update
apt-get install docker-ce docker-ce-cli containerd.io</code></pre><p>Anschließend kann man einen ersten Hallo-Welt-Container starten, um die Funktionsfähigkeit von Docker zu überprüfen.</p><pre><code>docker run hello-world</code></pre><p>Erhält man eine passende Ausgabe, haben wir Docker erfolgreich installiert.</p><h3 id="n-tzliches">Nützliches</h3><p>Ein paar nützliche Helfer dürfen es natürlich doch sein:</p><pre><code>apt install fail2ban git htop nano mc rsync sudo unzip</code></pre><!--kg-card-begin: markdown--><ul>
<li><strong>fail2ban</strong> blockt ungewünschte SSH-Verbindungen</li>
<li><strong>git</strong> kann man für die Arbeit mit Git-Repos immer gebrauchen</li>
<li><strong>htop</strong> für die Anzeige der Auslastung in einer moderneren Darstellung als es top ermöglicht.</li>
<li><strong>nano</strong>: Ja, ich nutze nano und nicht vi oder vim</li>
<li><strong>mc</strong>: Manchmal finde ich die Darstellung des Midnight Commanders auch ganz praktisch.</li>
<li><strong>rsync</strong> ist ideal, um Daten auch über die Grenzen von Maschinen hinweg zu bewegen</li>
<li><strong>sudo</strong> macht für das Herabsetzen von Nutzerprivilegien Sinn.</li>
<li>Um mit gezippten Daten zu arbeiten, macht auch <strong>unzip</strong> Sinn.</li>
</ul>
<!--kg-card-end: markdown--><p>Hierbei bringt jeder sicherlich noch seine persönlichen Präferenzen ein, aber diese Liste soll ein erster Anhaltspunkt sein.</p><h3 id="post-installation-steps">Post-Installation Steps</h3><p>Wie in der offiziellen Doku erwähnt, kann man nun auch noch <a href="https://docs.docker.com/install/linux/linux-postinstall/">weitere Maßnahmen</a> ergreifen, sobald die Docker Engine installiert ist. Interessant finde ich die Einrichtung eines weiteren Systemnutzers, damit man nicht immer auf den Root-Nutzer zurückgreifen muss, wenngleich der zusätzliche Systemnutzer Zugriff auf den Docker-Socket bekommt und dadurch Root-ähnliche Rechte erhält. Docker rootless zu betreiben ist seit Längerem ein Thema. <a href="https://medium.com/@tonistiigi/experimenting-with-rootless-docker-416c9ad8c0d6">Hier kann man dazu Weiteres lesen</a>.</p><p>Zurück zum Thema! Bei der Docker-Installation unter Debian wird bereits eine Gruppe "docker" angelegt. Es braucht also nur noch einen eigenen Systemnutzer und anschließend weisen wir dem die Docker-Gruppe zu.</p><pre><code>adduser deploy
usermod -aG docker deploy</code></pre><p>Anschließend sollte es möglich sein einen Hallo-Welt-Container als Nutzer "deploy" zu starten.</p><pre><code>su deploy
docker run hello-world</code></pre><p>Wenn man auch nun wieder wieder Ausgabe des Containers sehen kann, hat die Einrichtung funktioniert.</p><h3 id="kernel-tweaks">Kernel Tweaks</h3><p>Docker setzt im Wesentlichen auf Features des Linux-Kernels, wie chroot, Namespaces, etc. Der Kernel ist somit essentiell für das Betreiben von Containern. Über die Jahre haben sich so einige Probleme mit Limits, die im Kernel gesetzt sind ergeben. Auf manche stößt man recht schnell. Beispielsweise beim Starten von Redis oder Elasticsearch, wird man darauf hingewiesen gewisse Einstellungen zu tätigen, um die volle Leistung nutzen zu können. Auf andere Probleme stößt man erst, wenn man die 10. MySQL-Instanz auf der Maschine starten möchte.</p><p>Es folgen meine Kernel Tweaks, die sich im Laufe der Jahre durch den langen Betrieb von Containern ergeben haben. Diese lege ich in einer eigenen Datei unter /etc/sysctl.d/80-docker.conf ab.</p><pre><code># Elasticsearch optimization
vm.max_map_count=262144

# MySQL optimization
fs.aio-max-nr=1048576

# Redis optimization
vm.overcommit_memory=1
net.core.somaxconn=65535

# Have a larger connection range available
net.ipv4.ip_local_port_range=1024 65000

# Reuse closed sockets faster
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_fin_timeout=15

# The maximum number of "backlogged sockets".  Default is 128.
# net.core.somaxconn=4096
net.core.netdev_max_backlog=4096

# 16 MB per socket - which sounds like a lot, but will virtually never consume that much.
net.core.rmem_max=16777216
net.core.wmem_max=16777216

# Various network tunables
net.ipv4.tcp_max_syn_backlog=20480
net.ipv4.tcp_max_tw_buckets=400000
net.ipv4.tcp_no_metrics_save=1
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_syn_retries=2
net.ipv4.tcp_synack_retries=2
net.ipv4.tcp_wmem=4096 65536 16777216
#vm.min_free_kbytes=65536

# Connection tracking to prevent dropped connections (usually issue on LBs)
net.netfilter.nf_conntrack_max=262144
# Error by setting the following: cannot stat /proc/sys/net/ipv4/netfilter/ip_conntrack_generic_timeout: No such file or directory
net.ipv4.netfilter.ip_conntrack_generic_timeout=120
net.netfilter.nf_conntrack_tcp_timeout_established=86400

# ARP cache settings for a highly loaded Docker Swarm
net.ipv4.neigh.default.gc_thresh1=8096
net.ipv4.neigh.default.gc_thresh2=12288
net.ipv4.neigh.default.gc_thresh3=16384
</code></pre><h3 id="system-sauber-halten">System sauber halten</h3><p>Trotz schlanker Container, wird beim kontinuierlichen Bauen und Ausrollen neuer Versionen das System doch schnell voll. Dafür bietet Docker die Prune-Befehle, die ich auch in <a href="https://blog.servivum.com/putzfee-gesucht-docker-hosts-sauber-halten/">diesem Artikel</a> thematisiert habe, allerdings nutze ich die darin beschrieben automatischen Möglichkeiten so nicht mehr. Mittlerweile ist dafür ein eigenes Projekt entstanden, welches auf <a href="https://github.com/servivum/docker-host-cleanup">GitHub</a> zu finden ist.</p><p>Beim Deployment von Containern setze ich auf den Swarm Mode und so starte ich den Service zum regelmäßigen Aufräumen der Maschine als Docker Stack.</p><p>Zunächst lade ich das Compose-File dazu in einen neuen Ordner:</p><pre><code>mkdir -p /var/www/srv-cleanup
cd /var/www/srv-cleanup
wget https://raw.githubusercontent.com/servivum/docker-host-cleanup/master/docker-compose.production.yml</code></pre><p>Nun initialisiere ich den Swarm Mode und starte den Stack:</p><pre><code>docker swarm init
docker stack deploy -c docker-compose.production.yml srv-cleanup</code></pre><p>Abschließend kann man noch einen Blick darauf werfen, ob der Dienst erfolgreich gestartet wurde, was natürlich etwas dauert, da zunächst noch das Container-Image aus dem Docker Hub geladen werden muss.</p><pre><code>docker stack services srv-cleanup</code></pre><h3 id="abschlie-ende-gedanken">Abschließende Gedanken</h3><p>Um ein produktives System zu betreiben, reichen diese Maßnahmen natürlich noch nicht aus. Es braucht ein Backup-and-Restore-Konzept, sowie ein Monitoring, welches über den Zustand der Maschine berichtet.</p><p>Interessant ist zudem, wie man Web-Applikationen schnell und einfach mit HTTPS-Verschlüsselung online bringen kann. Diesem Thema widme ich mich in einem der kommenden Beiträge.</p><p><em>Bild von <a href="https://unsplash.com/@tvick">Taylor Vick</a></em></p>]]></content:encoded></item><item><title><![CDATA[Road to Production: Tipps für schlanke Docker-Images 🐳]]></title><description><![CDATA[Nach vielen vielen gebauten und gestarteten Containern möchte ich ein paar praktische Tipps zum Image-Bau geben, die einem das Leben erleichtern. Entstehen soll ein ganzheitlicher Blick.]]></description><link>https://blog.servivum.com/road-to-production-tipps-fur-schlanke-docker-images/</link><guid isPermaLink="false">5c0e493b42e8830001d2492f</guid><category><![CDATA[Docker]]></category><category><![CDATA[Container]]></category><category><![CDATA[Layer]]></category><category><![CDATA[Image]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Tue, 16 Jan 2018 11:08:00 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2018/12/image.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2018/12/image.png" alt="Road to Production: Tipps für schlanke Docker-Images 🐳"><p>Nach vielen vielen gebauten und gestarteten Containern möchte ich ein paar praktische Tipps zum Image-Bau geben, die einem das Leben erleichtern. Entstehen soll ein ganzheitlicher Blick von der lokalen Entwicklungsumgebung, über das Bauen und Testen in der Continuous Delivery Pipeline, bis zum Betrieb in Produktion.</p><h2 id="beware-of-the-layers"><strong>Beware of the Layers</strong></h2><p>Eine der ersten Regeln, die man sich beim Bau von Images vor Augen führen sollte, ist das Kombinieren von Befehlen, um keine unnötigen Layer zu erzeugen. Dateien, die in einem Layer erzeugt wurden, können in einem anderen Layer nicht mehr entfernt werden, um Speicherplatz zu reduzieren.</p><pre><code># Do
RUN apt-get update &amp;&amp; \
    apt-get install -y \
    package-bar \
    &amp;&amp; \
    rm -rf /var/lib/apt/lists/*

# Don't
RUN apt-get update
RUN apt-get install package-bar
RUN rm -rf /var/lib/apt/lists/*</code></pre><p>Zudem spielen Layer eine wichtige Rolle, wenn es um die Build-Geschwindigkeit geht, da hier der Build Cache für Beschleunigung sorgt.</p><h2 id="ein-dockerfile-dem-befehl-docker-container-commit-vorziehen"><strong>Ein Dockerfile dem Befehl „docker container commit“ vorziehen</strong></h2><p>Man muss kein Dockerfile als Basis für seine Applikation nutzen. Stattdessen kann man auch einfach einen Basis-Container mit der Linux-Distribution meiner Wahl hochfahren, um „from scratch“ zu starten. Anschließend kann man manuell seine Applikation und Abhängigkeiten über die Shell hinzufügen. Mit <code>docker container commit</code> lässt sich dann der Container wieder als Image verpacken. Für ein erstes Rumprobieren halte ich diese Vorgehensweise für nützlich und zielführend.</p><p>Im Gegensatz dazu, steht die Möglichkeit ein Image basierend auf einem Dockerfile zu bauen. In dieser textlichen Form ist es versionierbar, ist dadurch auch für die Kollegen nachvollziehbar und sorgt für eine bessere Automation und Reproduzierbarkeit, die im Umfeld von CI/CD sehr wichtig ist. Wer seit Längerem mit Continuous Delivery Pipelines zu tun hat, weiß aber, dass externe Abhängigkeiten die Reproduzierbarkeit torpedieren können. Wenn der Server, von dem ich eine Abhängigkeit wie ein Tar-Ball oder dergleichen beziehe, offline ist, habe ich trotzdem ein Problem. Das sollte man im Hinterkopf behalten.</p><h2 id="it-runs-on-my-laptop-it-runs-in-production-"><strong>It runs on my laptop != It runs in production </strong>⚠️</h2><p>Wir alle kennen das: Das gerade frisch entwickelte neue Feature funktioniert tadellos auf der eigenen Maschine, aber nicht in Produktion. Die Docker-Container sollen dies aufgrund ihrer Parität (Parity) jedoch minimieren. Ich schreibe bewusst minimieren, denn wir verfolgen mit den verschiedenen Umgebungen verschiedene Interessen. Lokal wollen wir schnell und komfortabel entwickeln. In der Test-Umgebung soll möglichst automatisiert eine Überprüfung der Qualität statttfinden. Auf dem Produktivsystem wünschen wir uns, dass die Applikation möglichst 24/7 erreichbar ist. Das fasst die Ziele natürlich nur ganz grob zusammen, aber ein grundsätzlicher Unterschied, der dadurch entsteht, ist die Verwendung von Volumes in der Produktivumgebung, die nicht die Applikation, sondern nur die Daten persistieren sollen. Lokal arbeitet man in der Regel so, dass die komplette Applikation aus dem Git-Repository ausgecheckt wird und auf der Host-Maschine liegt. Host-Mounts, die unsere Änderung in die gestarteten Container bringen, werden dadurch notwendig. Bereits das ist ein Unterschied, der nicht zu vernachlässigen ist und es geht weiter mit dem Orchestrator. Lokal arbeitet man unter Umständen mit Docker-Compose und das verteilte Live-System wird mit Docker Swarm gesteurt. Eine Anwendungen zentral auf einer Entwicklungsmaschine zu betreiben unterscheidet sich vom Betrieb in verteilten Systemen.</p><p>In meinen Augen sollte man daher dafür sorgen, dass zumindest die Images in allen Umgebungen identisch sind. Da ein Dockerfile die Basis für ein Image darstellt, ist es auch der Ausgangspunkt, um eine möglichst große Parität zu erhalten. Ein Image muss daher über die notwendige Intelligenz verfügen, lokal in Debugging-Szenarien zur Seite zu stehen, was in Production mittels Environment Variable oder reingerichter Konfigurationsdatei deaktiviert wird. Das Image stellt also den gemeinsamen Nenner dar und kann mittels Host-Mounts, Ports, Env-Variablen, Secrets und Configs an die jeweilige Umgebung angepasst werden. Das Thema Sicherheit steckt hier natürlich die Grenzen ab. Am Ende wollen wir nicht den lokalen Komfort in der Entwicklung über die Sicherheit im Produktivsystem stellen.</p><h2 id="devops-"><strong>DevOps </strong>👯‍♂️</h2><p>Ich bin ein Freund des Ansatzes „You build it, you run it!“, der im Zusammenhang mit dem DevOps-Ansatz immer wieder Erwähnung findet. Als Entwickler*in bekommt man mit Docker ein Werkzeug an die Hand, wodurch man sich zwangsläufig mehr mit der Laufzeitumgebung Gedanken machen muss, die man zuvor in Ops Händen gesehen hat. Dadurch stellt sich ein breiteres Wissen selbst bei den Technologien ein, die man bereits jahrelang genutzt hat. Als PHP-Entwickler*in lernt man, wie der FastCGI Process Manager (FPM) arbeitet, wo er seine Log-Dateien und sein Process File ablegt. In meinen Augen bekommt man einen ganzheitlicheren Blick, den man für das Betreiben in der Live-Umgebung auch wirklich benötigt. Menschen, die man klassischer Weise Operations zuordnen würde, fungieren mehr als Makro-Architekten und stecken die Grenzen für Entwickler*innen ab, welche mit Containern sehr viel mehr Freiheiten bekommen.</p><h2 id="offizielle-images"><strong>Offizielle Images</strong></h2><p>Nutzt die offiziellen Images! Sie stellen ein absolut solides Fundament für eure Applikation dar und werden von Leuten betreut, die sich damit auskennen. Dinge selbst von der Pieke auf zu erstellen, sorgt für einen gehörigen Aufwand, den man nicht unterschätzen sollte. Schnell hat man sich ein Image aus dem Docker Hub geladen und zum Laufen gebracht, aber wer garantiert einem, dass dieses Image sicher ist oder in einem halben Jahr noch gepflegt wird? Mit den offiziellen Images haben die Verantwortlichen bereits ihren langen Atem bei der Pflege der Software-Pakete bewiesen.</p><h2 id="alpine-linux"><strong>Alpine Linux </strong></h2><p>Es lohnt sich mit dem kleinen Alpine Linux und dessen Paket Manager auseinander zu setzen. Meine ersten Docker-Gehversuche habe ich mit Debian gestartet, weil mir <code>apt</code> vertraut war. Nachdem ich mich mit Alpine auseinander gesetzt habe, kann ich euch nur wärmstens dazu raten, denn die kleinen Images machen beim Bewegen und Bauen extrem viel Spaß. Die CI-/CD-Pipeline wird enorm beschleunigt und auch die lokale Entwicklungsumgebung ist in Nullkommanichts aufgesetzt. Spaß beim Entwickeln ist in meinen Augen ein unterschätztes Gut, was es als Verantwortlicher zu jeder Zeit zu wahren gilt. Das einzig Negative, was ich über Alpine Linux berichten kann, ist der verwendete C Compiler, der in seltenen Fällen für Probleme sorgt und das ein oder andere Derivat an CLI-Tools, was sich vielleicht doch etwas vom Original unterscheidet. Ein Blick wert ist es aber auf jeden Fall und wenn es nur der schmale Footprint ist. Ja auch als Entwickler*in kann man seinen Beitrag zum Umweltschutz leisten. 🌳</p><h2 id="dockerfile-best-practices"><strong>Dockerfile Best Practices</strong></h2><p>Docker selbst hat in seiner Dokumentation <a href="https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/">Best Practices zum Schreiben von Dockerfiles</a> definiert, die jeder mal gelesen haben sollte. Zur Überprüfung des Regelwerks wurden ein paar Linter-Projekte ins Leben gerufen. Eine Empfehlung dafür kann ich bisher jedoch noch nicht aussprechen.</p><h2 id="-dockerignore"><strong>.dockerignore</strong></h2><p>Die Datei <code>.dockerignore</code> bekommt für mein Empfinden zu wenig Aufmerksamkeit, obwohl sie den Build-Prozess ähnlich stark beeinflussen kann, wie das schlanke Alpine Linux. Dazu muss man sich jedoch vor Augen führen, wie aus dem Dockerfile ein Image gebaut wird. Wichtig bei diesem Vorgang ist der sogenannte Kontext. Das ist ein Ort, aus dem sich Docker beim Bau von Images bedient, um Anweisungen wie <code>COPY</code> auszuführen. Dateien werden also nicht direkt vom lokalen Dateisystem in das Docker-Image kopiert, sondern gehen einen Umweg über den Kontext. Der Kontext entspricht, wenn nicht anders definiert, dem Ordner in dem ich <code>docker image build .</code>ausführe. Alle Dateien und Ordner innerhalb dieses Verzeichnisses werden also vor dem eigentlichen Build-Prozess zum Docker Daemon übertragen, was bei großen Projekten einige Zeit in Anspruch nimmt, nur um anschließend Dinge aus diesem Kontext ins finale Image zu kopieren. Hier kommt die Datei <code>.dockerignore</code> ins Spiel. Damit kann man, ähnliche Gits „.gitignore“, Dateien und Ordner ausnehmen, damit diese nicht zum Docker Daemon übertragen werden. Klassische Einträge dieser Datei umfassen den Ordner <code>.git</code>, die Dokumentation des Projektes, Pipeline as Code, Infrastructure as Code, Secrets, die im finalen Container nichts zu suchen haben. Mit dieser Datei auseinander gesetzt, kann man die CI/CD Pipeline spürbar beschleunigen.</p><h2 id="build-and-runtime-dependencies"><strong>Build and Runtime Dependencies</strong></h2><p>Mach dir Gedanken darüber, welche Abhängigkeiten es zur Laufzeit und welche es zur Build Time gibt. Ein Beispiel wäre <code>make</code> zum Kompilieren, welches zur Laufzeit nicht benötigt wird und entsprechend in einem Layer installiert, damit kompiliert und anschließend wieder entfernt werden kann. Gleichzeitig sollte man eine Entwickler*in nicht einschränken. Wenn er <code>make</code>benötigt, muss das Tool natürlich auch im Container zur Verfügung stehen.</p><h2 id="labels"><strong>Labels</strong></h2><p>Das absolute Mindestmaß an Metadaten, das ein Image-Bauer zur Verfügung stellen sollte, sind sein Name und die Kontaktdaten. So sieht man schnell, wer diese Datei verzapft hat und kann sich bei Fragen an ihn wenden. Wer sich hier mehr wünscht, kann sich mit dem <a href="http://label-schema.org/">Label Schema</a> beschäftigen, welches dafür eine Spezifikation liefert (veraltet). Alternativ kann man bei <a href="https://github.com/projectatomic/ContainerApplicationGenericLabels">Project Atomic</a> oder der <a href="https://github.com/opencontainers">Open Container Initiative</a> in entsprechende Entwürfe einarbeiten.</p><pre><code>FROM golang:1.9.2-alpine3.7
LABEL maintainer "Patrick Baber &lt;patrick.baber@ueber.io&gt;"</code></pre><h2 id="multi-stage-builds"><strong>Multi-Stage-Builds</strong></h2><p>Diesem Thema habe ich einen <a href="https://ueberdosis.io/artikel/schlanke-docker-images-mit-multi-stage-builds/">eigenen Artikel</a> gewidmet, weil es sich um eine mächtige Möglichkeit handelt, ein Image basierend auf mehreren Base-Images zu erstellen. Assets bauen, mit Node, die man dann in ein nginx-Image kopiert. Solche Dinge werden damit möglich.</p><h2 id="abschlie-endes-gebrabbel"><strong>Abschließendes Gebrabbel</strong></h2><p>Es lohnt sich in schlanke und robuste Images zu investieren. Die Lernkurve ist meiner Meinung nach nicht besonders steil, da man sich den verschiedenen Themen peu à peu annehmen kann, ohne gezwungen zu sein, gleich alles zu erledigen. Im Fokus sollte die Verwendbarkeit des Images in den verschiedenen Umgebungen stehen, was zwar ein wenig zusätzliche Logik erfordert, sich aber zur Freude von Dev und Ops lohnen wird.</p><p>Bild von <a href="https://ueberdosis.io/">Nick Hirche</a></p>]]></content:encoded></item><item><title><![CDATA[Gimme more: Mehrere Prozesse im Docker-Container mit supervisor]]></title><description><![CDATA[Applikationscontainer sind vor allem als isoliert laufende Prozesse zu sehen, weshalb der Vergleich mit virtuellen Maschinen stark hinkt. Nichtsdestotrotz gibt es Szenarien, in denen mehr als ein Prozess im Container Sinn macht.]]></description><link>https://blog.servivum.com/gimme-more-mehrere-prozesse-im-docker-container-mit-supervisor/</link><guid isPermaLink="false">5c0e49c842e8830001d24936</guid><category><![CDATA[Docker]]></category><category><![CDATA[supervisor]]></category><category><![CDATA[Image]]></category><category><![CDATA[Container]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Cron]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Wed, 27 Dec 2017 11:10:00 GMT</pubDate><media:content url="https://blog.servivum.com/content/images/2018/12/image-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.servivum.com/content/images/2018/12/image-1.png" alt="Gimme more: Mehrere Prozesse im Docker-Container mit supervisor"><p>Applikationscontainer sind vor allem als isoliert laufende Prozesse zu sehen, weshalb der Vergleich mit virtuellen Maschinen stark hinkt. Nichtsdestotrotz gibt es Szenarien, in denen mehr als ein Prozess im Container Sinn macht. <code>supervisor</code> als selbsternanntes „Process Control System“ hilft hier weiter.</p><p><code>supervisor</code> kommt aus der Prä-Container-Ära und hilft bei der Verwendung simpler Programme, die vielleicht kein eigenes Prozess-Management mitbringen. Damit lässt sich beispielsweise die Ausgabe eines Prozesses fangen und diese in ein Logfile schreiben oder wacklige Tools können damit neu gestartet werden. <code>SIGTERMS</code> zu den Subprozessen werden auch weitergeleitet. Wird <code>supervisord</code> angewiesen sich zu beenden, leitet es dies also an seine Subprozesse weiter.</p><p>Die Einsatzmöglichkeiten sind vielfältig, wie ein kurzer Blick in die <a href="http://supervisord.org/introduction.html">Dokumentation</a> zeigt. Weitere Tools in dem Bereich sind <a href="http://smarden.org/runit/">runit</a> und <a href="https://blog.tutum.co/2014/12/02/docker-and-s6-my-new-favorite-process-supervisor/">S6</a>. Ich greife gern auf <code>supervisor</code> zurück, weil es in den meisten Distributionen enthalten ist und damit schnell installiert und auch eingerichtet ist. Wer es leichtgewichtiger mag, kann auch über ein Bash-Skript nachdenken, wie es in der offiziellen <a href="https://docs.docker.com/engine/admin/multi-service_container/">Docker-Doku</a> skizziert wird.</p><h2 id="ok-wie-machen-wir-s"><strong>OK, wie machen wir's?</strong></h2><p>Ziel soll es sein, PHP zusammen mit einem Cronjob laufen zu lassen. Legen wir los und betrachten das folgende Dockerfile.</p><pre><code>FROM php:7.2-fpm-alpine
LABEL maintainer "Patrick Baber &lt;patrick.baber@ueber.io&gt;"

# Install supervisor
RUN apk --update add supervisor

# Configure supervisor
COPY supervisord.conf /etc/supervisor/supervisord.conf

# Configure cron
COPY crontab /etc/cron/crontab

# Init cron
RUN crontab /etc/cron/crontab

# Run supervisor
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]</code></pre><p>Uns soll zunächst ein einfaches PHP-Image als Fundament dienen. Dann installieren wir das Paket <code>supervisor</code> und kopieren dafür die Konfigurationsdatei. Es folgt alles Notwendige für <code>cron</code>. Was das ist, kann meinem <a href="https://ueberdosis.io/artikel/auf-die-minute-genau-cronjobs-im-docker-container/">vorigen Artikel</a> entnommen werden.</p><p>Abschließend definieren wir das Kommando, welches später im Container laufen soll, dem wir den Pfad zur Konfigurationsdatei übergeben.</p><p>Spannend wird nun der Inhalt der Datei <code>supervisord.conf</code>, die im selben Verzeichnis abgelegt ist.</p><pre><code>[supervisord]
logfile = /dev/null
loglevel = info
pidfile = /var/run/supervisord.pid
nodaemon = true

;[include]
;files = /etc/supervisor/conf.d/*.conf

[program:php-fpm]
command = php-fpm
autostart = true
autorestart = true
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes = 0

[program:crond]
command = crond -f
autostart = true
autorestart = true
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes = 0</code></pre><p>Die Syntax entspricht einer klassischen .ini-Datei, die über Sections in eckigen Klammern strukturiert wird.</p><h3 id="-supervisord-"><strong>[supervisord]</strong></h3><p>In der ersten Section wird der Daemon (supervisord) konfiguriert, den wir bereits in der ersten Zeile dazu anleiten kein Daemon zu sein, damit der Prozess im Vordergrund läuft und seine Ausgaben nach <code>stdout</code> schreibt, wie man sich das bei Docker-Containern wünscht. Ein zusätzliches Logfile benötigen wir daher nicht, weshalb wir es mit <code>logfile = /dev/null</code> deaktivieren.</p><h3 id="-include-"><strong>[include]</strong></h3><p>Wer sich mehr Struktur wünscht, kann seine Konfiguration auch in mehrere Dateien aufteilen, was gerade bei der Verwendung vieler Prozesse innerhalb des Containers Sinn macht, allerdings führt man damit das Prinzip der isolierten Prozesse ad absurdum. Die include-Anweisung ist dennoch recht nützlich, weshalb ich sie auskommentiert hier aufgenommen habe.</p><h3 id="-program-xyz-"><strong>[program:XYZ]</strong></h3><p>Hier sind wir nun beim spannenden Teil angelangt. Wir können nun für jedes Programm, einen eigenen Abschnitt definieren, der als zentrale Eigenschaft vor allem das Kommando zum Starten des Prozesses benötigt. Man merkt, ich verwende Programm und Prozess hier synonym. Vorbereitet ist eine Sektion für <code>php-fpm</code>, was im offiziellen PHP-Image von dem wir ableiten, der einzige Prozess wäre, gefolgt von einem Abschnitt für den zweiten Subprozess <code>crond -f</code>. Ich habe hier keinerlei <code>crontab</code>, die den Cronjob auch nur eine Sache ausführen lassen würde, aber dies soll nur als Beispiel dienen. Wer einen vollwärtigen Cronjob nutzen möchte, schaut bitte in <a href="https://blog.servivum.com/auf-die-minute-genau-cronjobs-im-docker-container/">diesen Artikel</a>. <code>autorestart</code> startet die Anwendung neu, ob sie bewusst oder unbewusst heruntergefahren wurde. Es folgen nun Einstellungen, welche die Programmausgabe von <code>stdout</code> und <code>stderr</code> direkt an Docker weiterleiten.</p><p>Bevor wir uns ans Bauen des Images machen können, benötigen wir noch die <code>crontab</code>, die in der Realität eher ein PHP-Skript asychron zum herkömmlichen Aufruf per FastCGI ausführt. Hier übernehme ich einfach die Crontab aus meinem <a href="https://blog.servivum.com/auf-die-minute-genau-cronjobs-im-docker-container/">vorigen Artikel</a>, die für eine einfache Shell-Ausgabe sorgt. Zur Veranschaulichung unseres Vorhabens soll das aber reichen.</p><pre><code>* * * * * echo "Hello world" &gt;&gt; /dev/stdout 2&gt;&amp;1
# crontab requires empty line at end of file</code></pre><h2 id="build-n-run"><strong>Build 'n' Run</strong></h2><p>Das Image kann nun gebaut und der Container gestartet werden.</p><pre><code># Build
docker image build -t supervisor-image .

# Run
docker container run --rm --name supervisor-container supervisor-image</code></pre><p>Es folgt die Ausgabe des Containers.</p><pre><code>2017-12-11 16:17:50,592 CRIT Supervisor running as root (no user in config file)
2017-12-11 16:17:50,593 INFO supervisord started with pid 1
2017-12-11 16:17:51,597 INFO spawned: 'php-fpm' with pid 9
2017-12-11 16:17:51,599 INFO spawned: 'crond' with pid 10
[11-Dec-2017 16:17:51] NOTICE: fpm is running, pid 9
[11-Dec-2017 16:17:51] NOTICE: ready to handle connections
2017-12-11 16:17:52,624 INFO success: php-fpm entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
2017-12-11 16:17:52,624 INFO success: crond entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Hello world
Hello world</code></pre><p>Die erste Meldung lässt einen direkt unangenehm aufstoßen. Sie lässt sich vermeiden, indem man <code>supervisord</code> durch einen anderen Benutzer startet. Die Rechte des Nutzers müssen allerdings für die Tätigkeit des Subprozesses reichen. Um beispielsweise einen Low-Port (&lt; 1024) zu öffnen, braucht es root-Rechte oder zumindest die entsprechende Capability aus dem Kernel. Das Thema ist zu groß, um es in diesem Artikel zu behandelt, allerdings möchte ich darauf hinweisen.</p><p>Darüber hinaus entnehmen wir dem Log, dass <code>supervisord</code> korrekt startet und seine Subprozesse ebenfalls. Anschließend produziert PHP auch die erste Ausgabe - zu erkennen an der anderen Datumsformatierung. Das ist nicht ideal. Falls jemand weiß, wie man das eleganter lösen kann, dabei aber trotzdem die Infos bei Docker ankommen, gibt Bescheid.</p><p>Zusätzlich gibt es noch die Info von <code>supervisord</code>, dass es zufrieden mit den beiden Prozessen ist und ihren Zustand entsprechend als <code>RUNNING</code> definiert.</p><p>Nach spätestens einer Minute meldet sich dann auch <code>crond</code> mit seiner Testausgabe „Hello world“.</p><p>Die beiden Prozesse verrichten also, wie gewünscht, ihren Dienst. Ist also alles schnell umgesetzt. Viel Spaß mit mehreren Prozessen im Container!</p><p>Bild von <a href="https://ueberdosis.io/">Nick Hirche</a></p>]]></content:encoded></item><item><title><![CDATA[Auf die Minute genau: Cronjobs im Docker-Container]]></title><description><![CDATA[Für zeitgesteuerte Aufgaben hilft noch immer das gute alte crond. In diesem Beitrag möchte ich zeigen, wie man im Docker-Umfeld damit arbeitet.]]></description><link>https://blog.servivum.com/auf-die-minute-genau-cronjobs-im-docker-container/</link><guid isPermaLink="false">5c0e4a7542e8830001d2493f</guid><category><![CDATA[Docker]]></category><category><![CDATA[Cron]]></category><category><![CDATA[Alpine]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Mon, 11 Dec 2017 11:13:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1512856246663-647a81ef198e?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1512856246663-647a81ef198e?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Auf die Minute genau: Cronjobs im Docker-Container"><p>Für zeitgesteuerte Aufgaben hilft noch immer das gute alte <code>crond</code>. In diesem Beitrag möchte ich zeigen, wie man auf Basis eines schlanken Alpine Linux wiederkehrende Befehle ausführt.</p><p>Ich beginne mit einer schmalen Datei (crontab) in Cron-Syntax. Die Ausgabe wird, wie für einen Docker-Container üblich, nach <code>/dev/stdout</code> geschrieben, damit Docker sie fangen und je nach gewählten Logging Driver (<a href="https://blog.servivum.com/logging-von-docker-containern-mit-journald/">Beitrag dazu</a>) verarbeiten kann. Im Standard lässt sich die Ausgabe einfach mit <code>docker container logs</code> verfolgen. Man beachte die Leerzeile am Ende der Datei (!!!).</p><pre><code>* * * * * echo "Hello world" &gt;&gt; /dev/stdout 2&gt;&amp;1
# crontab requires an empty line at the end of the file

</code></pre><p>Nun brauchen wir einen Bauplan für unser Container-Image in Form des Dockerfiles. Ich gehe davon aus, dass unsere beiden Dateien im gleichen Verzeichnis liegen.</p><pre><code>FROM alpine:3.8
LABEL maintainer "Patrick Baber &lt;patrick.baber@ueber.io&gt;"

# Configure cron
COPY crontab /etc/cron/crontab

# Init cron
RUN crontab /etc/cron/crontab

CMD ["crond", "-f"]</code></pre><p>Um ein möglichst kleines Image zu erhalten, wähle ich als Base-Image <code>alpine</code> mit dem Tag <code>3.8</code>, um eine konkrete Version im Sinne der Reproduzierbarkeit zu erhalten. Es wird unsere zuvor erstellte Datei im Image platziert. Der nachfolgende Schritt ist wichtig. Er führt <code>crontab</code> aus und veranlasst somit unsere Datei dorthin zu verschieben, wo sie <code>crond</code> erwartet. Nebenbei werden auch noch die Dateirechte angepasst. Zum Abschluss des Dockerfiles wird der Cron-Daemon im Vordergrund gestartet.</p><p>Wir haben nun alle Zutaten parat und beginnen mit dem Bau des Docker Images und starten im Anschluss unser Werk.</p><pre><code># Build
docker image build -t cron-image .

# Run
docker container run -d --rm --name cron-container cron-image</code></pre><p>Ob alles funktioniert überprüfen wir in den Logs, die nach einem kurzen In-den-Stuhl-fallen-lassen die erwartete Meldung auch bereits zeigen.</p><pre><code>$ docker container logs cron-container
Hello world
Hello world
Hello world</code></pre><p>Das Image ist mit seinen knapp 4 MB nun bereit für größere Aufgaben. Es braucht also nicht immer ein „full-blown“ Ubuntu als Basis. Ein Blick in die Kommandozeilenhilfe von <code>crond</code> zeigt weitere interessante Einstellungen, wie das Log-Level.</p><p>Gern wird ein Cronjob auch im PHP-Umfeld gebraucht, um asynchrone Aufgabe anzustoßen. Tipp: Unter Umständen braucht es hier nur PHP als CLI und nicht den FPM (FastCGI Process Manager).</p><p><em>Bild von <a href="https://unsplash.com/@nooryounis">Noor Younis</a></em></p>]]></content:encoded></item><item><title><![CDATA[Putzfee gesucht: Docker Hosts sauber halten]]></title><description><![CDATA[Ja klar, Applikationscontainer sind klein und leichtgewichtig, aber durch Methoden, wie CI/CD werden die Release-Zyklen immer kleiner. Es wird also immer häufiger gebaut und verschifft. Dadurch entstehen zwar kleine, aber viele ungenutzte Images, Container, Volumes und Netzwerke.]]></description><link>https://blog.servivum.com/putzfee-gesucht-docker-hosts-sauber-halten/</link><guid isPermaLink="false">5c0e4ac242e8830001d24946</guid><category><![CDATA[Docker]]></category><category><![CDATA[prune]]></category><category><![CDATA[cleanup]]></category><category><![CDATA[Container]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Mon, 30 Oct 2017 11:15:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1563453392212-326f5e854473?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1563453392212-326f5e854473?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Putzfee gesucht: Docker Hosts sauber halten"><p>Ja klar, Applikationscontainer sind klein und leichtgewichtig, aber durch Methoden, wie CI/CD werden die Release-Zyklen immer kleiner. Es wird also immer häufiger gebaut und verschifft. Dadurch entstehen zwar kleine, aber viele ungenutzte Images, Container, Volumes und Netzwerke.</p><p>So geschehen auf unserem Jenkins-Server. Um für Ordnung zu sorgen gibt es seit Docker Version 1.13 den Befehl <code>docker system prune</code>. Alternativ kann man auch kleinteiliger vorgehen und einen der folgenden Befehle nutzen:</p><pre><code># Remove unused images
docker image prune

# Remove all stopped containers
docker container prune

# Remove all unused volumes
docker volume prune

# Remove all unused networks
docker network prune</code></pre><p>Auf der eigenen Entwicklungsmaschine reicht es sicher einen solchen Befehl gelegentlich mal auszuführen. Im Produktivsystem könnte man auf jedem Docker Host einen Cronjob einrichten, der regelmäßig einen solchen Aufräum-Befehle startet, allerdings gibt es eine elegantere Variante. Wir nutzen den Swarm Mode von Docker, der als Orchestrator die Möglichkeit besitzt Container auf den verschiedenen Nodes im Cluster laufen zu lassen. Wir halten also gleich unser gesamtes Docker Cluster sauber! Im Container benötigen wir dafür lediglich Docker CLI und den Docker Socket (<code>/var/run/docker.sock</code>) des Host-Systems, damit der Aufräum-Befehl auf dem Host ausgeführt werden kann. Den Code dazu findet ihr auf <a href="https://github.com/servivum/docker-system-prune">GitHub</a> und das Image liegt im <a href="https://hub.docker.com/r/servivum/docker-system-prune/">Docker Hub</a>.</p><p>Einen eingerichteten Swarm Mode vorausgesetzt, könnt ihr die Putzfee per <code>docker service create</code> anstoßen oder per Stack File. Das Ganze sieht dann so aus:</p><pre><code># Option 1: docker service command
docker service create \
    --name docker-system-prune \
    --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
    --mode global \
    --restart-delay 86400s \
    servivum/docker-system-prune

# Option 2: Docker stack file
docker stack deploy -c docker-compose.yml docker-system-prune</code></pre><p>Für Option 2 wird natürlich noch das Compose File benötigt. Das findet man ebenfalls auf <a href="https://github.com/servivum/docker-system-prune/blob/master/docker-compose.yml">GitHub</a>. <code>--restart-delay 86400s</code> sorgt dafür, dass einmal am Tag der Container neugestartet wird, denn nach dem Reinigungsprozess beendet sich der Container von selbst. Und so konnte ich auch unseren Jenkins-Server von Altlasten befreien: <code>Total reclaimed space: 342.6GB</code></p><p>Bild von <a href="https://unsplash.com/@jeshoots">Jeshoots</a></p>]]></content:encoded></item><item><title><![CDATA[Logging von Docker-Containern mit journald]]></title><description><![CDATA[Logging bittet eine Möglichkeit, das diffuse Bild einer Container-Applikation aufzuklaren. Dieser Artikel beschreibt den Docker Logging Driver für journald.]]></description><link>https://blog.servivum.com/logging-von-docker-containern-mit-journald/</link><guid isPermaLink="false">5c0e4b0f42e8830001d2494d</guid><category><![CDATA[Docker]]></category><category><![CDATA[Container]]></category><category><![CDATA[journald]]></category><category><![CDATA[Logging]]></category><category><![CDATA[Driver]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Tue, 22 Aug 2017 10:16:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1472235008642-bb3ce23994ac?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1472235008642-bb3ce23994ac?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Logging von Docker-Containern mit journald"><p>Oft etwas vernachlässigt, bietet das Logging eine Möglichkeit, das diffuse Bild einer Container-Applikation aufzuklaren. Der generische Ansatz von Docker hilft dabei unterschiedlichsten Applikationen gerecht zu werden. Nach einer kurzen Einführung in das Docker-Logging soll dieser Artikel eine Anbindung an <code>journald</code> erläutern.</p><h2 id="starten-der-demo-applikation"><strong>Starten der Demo-Applikation</strong></h2><p>Um ein Gefühl für Logging mit Docker zu erhalten, benötigen wir zunächst einen Applikationscontainer. Ein nginx, der als Webserver fungiert und im schlanken Alpine-Gewand daherkommt, erfüllt diesen Zweck hervorragend. Auf einem Linux-Host starten wir also den Container ...</p><pre><code>$ docker container run -p 80:80 -d --name=web nginx:1.15-alpine</code></pre><h2 id="docker-container-logs"><strong>docker container logs</strong></h2><p>Anschließend rufen wir im Browser die Demo-Seite ein paar mal auf, da nginx jeden Aufruf protokolliert. Die Logs des Containers rufen wir über den Namen oder die ID ab. In unserem Fall mit <code>docker container logs web</code>. Zusätzlich gibt es noch eine Handvoll Flags für den Befehl, um beispielsweise genauere Timesstamps zu erhalten oder die Ergebnismenge einzugrenzen. Dies ist die Ausgabe der einfachen Befehlsform:</p><pre><code>127.0.0.1 - - [21/Jul/2017:17:04:09 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
127.0.0.1 - - [21/Jul/2017:17:04:10 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"
127.0.0.1 - - [21/Jul/2017:17:04:11 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" "-"</code></pre><p>Docker sammelt grundsätzlich alles, was die Applikation im Container in <code>stdout</code> und <code>stderr</code> schreibt. Im offiziellen Docker-Image des nginx linken die Standard-Logdateien „access.log“ und „error.log“ nach <code>/dev/stdout</code> und <code>/dev/stderr</code>, wie dem <a href="https://github.com/nginxinc/docker-nginx/blob/7e278fff2f12f852ef1be2aed17e9a2f822365ac/stable/alpine/Dockerfile#L133">Dockerfile</a> zu entnehmen ist. Ein Hand-Anlegen ist also nicht notwendig. Doch schauen wir uns den Container mit <code>docker container inspect web</code> doch mal genauer an:</p><pre><code>[
    {
        // [...]
        "LogPath": "/var/lib/docker/containers/0c6f45-gekuerzt-json.log",
        // [...]
        "HostConfig": {
            // [...]
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
        },
        // [...]
    }
]</code></pre><p>Unter dem angegebenen <code>LogPath</code> werden die von Docker gesammelten Logs auf die Festplatte geschrieben. Zudem wird angezeigt, welcher Logging Driver für den verwendeten Container im Einsatz ist. Standardmäßig ist das <code>json-file</code>. Ruft man diese mit <code>cat /var/lib/docker/containers/0c6f45-gekuerzt-json.log</code> direkt auf, sieht das formatiert so aus:</p><pre><code>{
  "log": "127.0.0.1 - - [21/Jul/2017:17:04:10 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\" \"-\"\n",
  "stream": "stdout",
  "time": "2017-07-21T17:04:09.625609119Z"
}
{
  "log": "127.0.0.1 - - [21/Jul/2017:17:04:11 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\" \"-\"\n",
  "stream": "stdout",
  "time": "2017-07-21T17:04:10.591183632Z"
}
{
  "log": "127.0.0.1 - - [21/Jul/2017:17:04:12 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\" \"-\"\n",
  "stream": "stdout",
  "time": "2017-07-21T17:04:11.134125994Z"
}</code></pre><p>Wir erhalten einen deutlich tieferen Blick in Dockers Logging-Mechanismus und sehen zusätzlich noch aus welchem <code>stream</code> (stdout/stderr) der Log-Eintrag stammt und erhalten nun auch genaueren Zeitstempel. nginx schreibt übrigens beim Aufruf eines nicht vorhandenen Pfads, also bei einem Fehler 404, in <code>stderr</code>.</p><h2 id="logging-driver"><strong>Logging Driver</strong></h2><p>Docker bringt von Hause eine Vielzahl von <a href="https://docs.docker.com/engine/admin/logging/overview/#supported-logging-drivers">Logging Drivers</a> mit. Neben dem Einsatz einer JSON-Datei können Logs beispielsweise zum <code>syslog</code> Daemon des Host-Systems übergeben werden. Das Graylog Extended Log Format, kurz <code>gelf</code>, kann gewählt werden, um die Log-Einträge an einen Graylog-Server oder Logstash zu übertragen. Produkte der bekannten Cloud-Provider sind natürlich ebenfalls nutzbar.</p><p>Ich möchte mich jedoch zunächst mit dem Treiber für <code>journald</code> beschäftigen. <code>journald</code> kommt zusammen mit dem Init-System <code>systemd</code> und ist bei diversen Linux Distributionen vorinstalliert. Als dedizierter Logging-Dienst beherrscht er unter anderem Logrotation, was <code>json-file</code> fehlt.</p><p>Die Zuweisung eines Logging Drivers kann auf vielen verschiedenen Ebenen stattfinden.</p><pre><code># Definition am Container
$ docker container run --log-driver journald --log-opt env=SOME_ENV_VAR [...]

# Definition am Service
$ docker service create --log-driver journald --log-opt env=SOME_ENV_VAR [...]

# Definition am Service im Docker Compose File
$ cat docker-compose.yml
version: "3"

services:
  web:
    image: nginx:1.12-alpine
    ports:
      - "80:80"
    logging:
      driver: "journald"
      options:
        env: "SOME_ENV_VAR"</code></pre><p>An den Beispielen sieht man, dass je nach Log-Treiber auch noch Optionen übergeben werden können, wie Zugangsdaten zu einem externen Dienst. Welche das sind, erfährt man in der ausführlichen <a href="https://docs.docker.com/engine/admin/logging/overview/">Dokumentation</a>.</p><h2 id="logging-im-docker-daemon"><strong>Logging im Docker Daemon</strong></h2><p>Neben den oben gezeigten Möglichkeiten zur Definition eines Logging-Treibers auf sehr kleinteiliger Ebene, können wir auch einen generellen Treiber im Docker Daemon bestimmen, der dann für die gestarteten Container genutzt wird. <a href="https://docs.docker.com/engine/admin/logging/overview/#configure-the-default-logging-driver">Laut Dokumentation</a> wird nach dieser Config-Datei geschaut: <code>/etc/docker/daemon.json</code>. Unter Debian kann ich das nicht bestätigen. Hier ist noch ein zusätzlicher Kniff notwendig, aber der folgt nach dem Inhalt der JSON-Datei.</p><pre><code>$ cat &gt; /etc/docker/daemon.json &lt;&lt; EOF
{
  "log-driver": "journald"
}
EOF</code></pre><p>Unter Debian, und ich vermute auch Ubuntu, müssen wir dem Docker Daemon noch klarmachen, dass er die Config-Datei beim Start hinzuziehen soll. Wir erweitern daher das Docker-Profil in <code>systemd</code> und starten den Dienst anschließend neu.</p><pre><code>$ mkdir -p /etc/systemd/system/docker.service.d
$ cat &gt; /etc/systemd/system/docker.service.d/dockerd.conf &lt;&lt; EOF
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// --config-file /etc/docker/daemon.json
EOF
$ systemctl daemon-reload
$ service docker restart</code></pre><p>In der Ausgabe von <code>docker info</code> taucht nun Folgendes auf: <code>Logging Driver: journald</code></p><h2 id="verwendung-von-journald"><strong>Verwendung von journald</strong></h2><p>Docker ermöglicht nun weiterhin die Nutzung von <code>docker container logs web</code>, da Docker mit <code>journald</code> umzugehen weiß, allerdings lassen sich die Logs nun auch mit dem <code>journalctl</code> abrufen. Beispielsweise so:</p><pre><code>$ journalctl CONTAINER_NAME=webserver</code></pre><p>Detailliert auf <code>journald</code> einzugehen, würde an dieser Stelle zu weit führen, weshalb ich auf die Dokumentation von <a href="https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html">journald</a> und <a href="https://www.freedesktop.org/software/systemd/man/journalctl.html">journalctl</a> und den <a href="https://docs.docker.com/engine/admin/logging/journald/">Eintrag zum Docker Logging Driver</a> verweisen möchte.</p><h2 id="fazit"><strong>Fazit</strong></h2><p>Zusammen mit den <a href="https://blog.servivum.com/dr-docker-wie-geht-es-meiner-app/">Healthchecks</a>, die Aufschluss über den Gesundheitszustand unserer App geben und dem Monitoring, also dem Sammeln von Metriken, z. B. mit Prometheus, stellt das Logging ein essentielles Werkzeug zur Überprüfung dar. <code>journald</code> ist dabei nur eine von vielen verschiedenen Möglichkeiten, die Log-Einträge der Applikationscontainer zu sammeln. Gemessen am verwendeten Standard Logging Driver <code>json-file</code>, der als Basis-Einstellung zu verstehen ist, hat sich <code>journald</code> allerdings im Linux-Umfeld einen Namen gemacht und findet sich daher in diversen Linux Distros, wo es an die eigenen Anforderungen, wie Logrotation und Per­sis­tenz, angepasst werden kann. Es lohnt sich daher dem Thema Logging etwas mehr Aufmerksamkeit zu schenken.</p><p><em>Bild von <a href="https://unsplash.com/@patrykgradyscom">Patryk Grądys</a></em></p>]]></content:encoded></item><item><title><![CDATA[Datenbank-Migration im Docker Swarm Mode]]></title><description><![CDATA[Am Beispiel einer PHP-Applikation auf Basis von Laravel möchte ich den Umgang mit Schema-Änderungen (Migrations) demonstrieren, die darauf warten müssen, dass ein benachbarter MySQL-Container bereit ist.]]></description><link>https://blog.servivum.com/datenbank-migration-im-docker-swarm-mode/</link><guid isPermaLink="false">5c0e4b5942e8830001d24954</guid><category><![CDATA[Docker]]></category><category><![CDATA[wait-for]]></category><category><![CDATA[Readiness]]></category><category><![CDATA[Migrations]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Wed, 09 Aug 2017 10:17:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1520038410233-7141be7e6f97?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1520038410233-7141be7e6f97?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Datenbank-Migration im Docker Swarm Mode"><p>Am Beispiel einer PHP-Applikation auf Basis von Laravel möchte ich den Umgang mit Schema-Änderungen (Migrations) demonstrieren, die darauf warten müssen, dass ein benachbarter MySQL-Container bereit ist.</p><p>Zunächst ein schneller Blick auf das Stack-File, welches später mit <code>docker stack deploy</code> die Applikation startet. Die Datei ist etwas gekürzt, um den Fokus nicht zu verlieren. Zusätzliche Netzwerke sind ebenso entfernt, wie auch die Behandlung von TLS-Zertifikaten im nginx. Über die Eigenschaft <code>deploy:</code>, die in Swarm Clustern besonders nützlich ist, verliere ich später noch ein Wort.</p><pre><code>version: "3.7"

services:
  nginx:
    image: registry.example.com/project/nginx
    ports:
      - "80:80"
  php:
    image: registry.example.com/project/php
    environment:
      ENV_FILE_SOURCE: /run/secrets/php_env
    secrets:
      - php_env
  mysql:
    image: mariadb:10.4
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_DATABASE_FILE: /run/secrets/mysql_name
      MYSQL_USER_FILE: /run/secrets/mysql_user
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
    secrets:
      - mysql_root_password
      - mysql_name
      - mysql_user
      - mysql_password

secrets:
  php_env:
    file: secrets/php_env.txt
  mysql_root_password:
    file: secrets/mysql_root_password.txt
  mysql_name:
    file: secrets/mysql_name.txt
  mysql_user:
    file: secrets/mysql_user.txt
  mysql_password:
    file: secrets/mysql_password.txt</code></pre><p>Für die Zugangsdaten sind die Docker Secrets im Einsatz, die in <a href="https://blog.servivum.com/sicher-ist-sicher-zugangsdaten-mit-docker-secrets/">diesem Artikel</a> genauer von mir erörtert werden. Das MariaDB-Image kann damit von Hause aus umgehen. Jeder einzelne Wert schlummert dabei in einer eigenen Datei und wird per Secret in den Dienst gegeben. Anschließend wird per Environment Variable der Pfad zum Secret übergeben. Das Secret <code>php_env</code> beinhaltet Laravels <code>.env</code> Datei.</p><h2 id="docker-hochseetauglich"><strong>Docker hochseetauglich</strong></h2><p>Zunächst einmal sollte man sich Gedanken über die Abhängigkeiten machen. nginx nimmt Anfragen der Benutzer entgegen und leitet diese an den PHP-Server weiter. Der PHP-Dienst, der die Projekt-Dateien beinhaltet, soll später den Befehl zur Datenbank-Migration ausführen und benötigt dazu eine Verbindung zum MariaDB-Server. Dabei ist allerdings Vorsicht geboten, denn nur weil der MariaDB-Container läuft, heißt es noch lange nicht, dass der Dienst auch per TCP erreichbar ist. MariaDB verhält sich da genau wie seine antiquierte Bruder MySQL und verrichtet allerlei Dinge, wie das Überprüfen von korrupten Tabellen, bevor es seine Schotten öffnet - doch dazu später mehr.</p><p>Leider berücksichtigt <code>docker stack deploy</code> die im Standard des <a href="https://docs.docker.com/compose/compose-file/#depends_on">Docker Compose Files</a> nützliche Eigenschaft <code>depends_on</code> nicht, womit Dienste aufeinander warten, wenn sie einander bedingen. Darüber ist in den GitHub-Issues eine rege Diskussion entbrannt. Viele sind der Meinung, dass der Gesundheitszustand eines Containers, der mittels <code>HEALTHCHECK</code> überprüft wird, hinzugezogen werden sollte, bevor der davon abhängige Dienst gestartet wird. Auf der anderen Seite wird mit „Resilience“ argumentiert, denn die einzelnen Dienste sollten mit dem Ausfall eines benötigen Dienst umgehen können. Das Verhalten des Containers beim Ausrollen sei ja mit der in Version 3 integrierten Eigenschaft <code>deploy</code> steuerbar. Nachzulesen ist die Diskussion <a href="https://github.com/docker/compose/issues/4305">hier</a>, <a href="https://github.com/moby/moby/issues/30404">hier</a> und <a href="https://github.com/moby/moby/issues/31333">hier</a>.</p><h2 id="asynchron-er-arbeitet-asynchron-"><strong>Asynchron, er arbeitet asynchron!</strong></h2><p>Die obige <code>docker-compose.yml</code> wird, nachdem die darin gebrauchten Images in der Registry abgelegt sind, auf einen Manager Node übertragen. Theoretisch ginge das auch über einen geöffneten Port des Docker Daemons, aber der müsste wieder abgesichert werden, was uns an dieser Stelle zu aufwendig ist. Aus diesem Grund landet die Datei per SCP, rsync oder Quantenteleportation auf dem Docker Swarm Manager unserer Wahl. Ich lege eine solche Datei meist in einen Ordner, wie <code>/var/www/project</code>, um zumindest die Stack-Definition dort liegen zu haben, denn dank Applikations-Containern liegen die Projektdaten dort nicht mehr.</p><p><strong><strong>Tipp:</strong></strong> Der Swarm Mode lässt sich auch auf dem eigenen Rechner mit dem Befehl <code>docker swarm init</code> einrichten. Anschließend ist man in der Lage, Stack-Files auf der lokalen Maschine „auszurollen“. Im Hinterkopf sollte man nur behalten, dass das Cluster nur aus einem Node besteht, womit sich viele Probleme, wie die Beziehung zwischen Container und Volume, gar nicht ergeben.</p><p>Weiter im Text: Um einen Stack zu erstellen, nutzen wir nun also den folgenden Befehl.</p><pre><code>docker stack deploy -c docker-compose.yml --with-registry-auth --prune mystack</code></pre><p>Beim Pfad zur <code>docker-compose.yml</code> sollte man darauf achten, dass eventuell darin enthaltene Verknüpfungen wie z. B. <code>env_file: .env-mysql</code> nicht aufgelöst werden können, sollte man den oberen Befehl nicht aus dem Verzeichnis, in dem die Datei selbst liegt, aufrufen. Mit <code>--with-registry-auth</code> werden die Authentifizierungsinformationen im Swarm Cluster verteilt, damit andere Nodes in die Lage versetzt werden das geschützte Image aus der Registry zu laden. Vorausgesetzt wird dabei natürlich, dass auf dem Manager Node zuvor ein <code>docker login</code> durchgeführt wurde. Es kann auch nicht schaden, das mit in die CI-/CD-Pipeline mit aufzunehmen.</p><p>Achtung: Mit dem Flag <code>--prune</code>, der mit Version 17.05 Einzug gehalten hat, werden nicht mehr nötige Dienste automatisch entfernt.</p><p>Nun könnte jemand meinen, dass man sich jetzt einfach mit geschicktem Filtern die ID des PHP-Containers schnappt und mit <code>docker exec CONTAINER_ID</code> die Schema-Migration anstößt. <code>docker stack deploy</code> arbeitet allerdings asynchron. Nach Bestätigen des Befehls wird man nur kurz über die aus dem Stack-File resultierenden Dienste informiert und landet dann auch schon wieder im Shell-Prompt. Ein Großteil der Magie, wie das Erzeugen der Tasks, das Laden der Images und Starten der Container passiert im Hintergrund und braucht seine Zeit. Mit <code>docker service ls</code> lässt sich ein flüchtiger Blick auf die Spalte <code>Replicas</code> werfen. Bei <code>0/1</code> scheint er also noch nicht fertig zu sein. Alternativ lässt sich mit <code>docker service ps --no-trunc SERVICE_ID</code> ein Blick in die Tasks werfen oder es wird noch genauer mit <code>docker service inspect SERVICE_ID</code>. Sich anhand dieser Infos einen Prozess in seiner CI-/CD-Pipline zu bauen halte ich aber auch für etwas umständlich.</p><h2 id="entrypoint-to-the-rescue"><strong>Entrypoint to the Rescue</strong></h2><p>Mein Tipp an dieser Stelle ist mal wieder der Gebrauch eines Shell-Scripts, welches im Container als <code>ENTRYPOINT</code> fungiert. Netter Nebeneffekt ist, dass unsere CI-/CD-Skripte schlanker werden und weitere Logik zum Betreiben der App, wie eben das Ausführen unserer Migrations, in das Container-Image wandern, wo es ja auch gut aufgehoben ist. Der Container erhält somit mehr Intelligenz. Gemessen an dem, was so alles im <code>ENTRYPOINT</code> des <a href="https://github.com/docker-library/mariadb/blob/f76084f0f9dc13f29cce48c727440eb79b4e92fa/10.3/docker-entrypoint.sh">MariaDB-Images</a> passiert, hält sich das bei uns allerdings in Grenzen. Nach einem kurzen Blick in das Dockerfile des PHP-Images, folgt der Inhalt der Datei <code>docker-entrypoint.sh</code>.</p><pre><code>FROM php:7.1-fpm-alpine

# Build app
[...]

# Helper for MySQL readiness
RUN curl -o /usr/local/bin/wait-for https://raw.githubusercontent.com/Eficode/wait-for/master/wait-for &amp;&amp; \
    chmod +x /usr/local/bin/wait-for

# Entrypoint
COPY entrypoint.sh /usr/local/bin
ENTRYPOINT ["entrypoint.sh"]
CMD ["php-fpm"]</code></pre><p>Im Dockerfile wird die PHP-Applikation zusammengebaut. Anschließend laden wir ein kleines Helfer-Skript von GitHub, welches ohne weitere Abhängigkeiten, einfach mit der Shell funktioniert. Anschließend wird der <code>ENTRYPOINT</code> definiert.</p><pre><code>#!/bin/sh
set -e

ENV_FILE_DESTINATION="/var/www/html/.env"
MYSQL_HOST="mysql"
MYSQL_PORT="3306"
MYSQL_TIMEOUT="60"

# Link secret for production environment
if [ -s "$ENV_FILE_SOURCE" ];then
    ln -s "$ENV_FILE_SOURCE" "$ENV_FILE_DESTINATION"
    echo "Secret linked."
else
    echo "No secret linked."
fi

echo "$(date) Waiting for MySQL service ..."
wait-for --timeout="$MYSQL_TIMEOUT" "$MYSQL_HOST":"$MYSQL_PORT" -- \
echo "$(date)" php artisan migrate --force

exec "$@"</code></pre><p>Laravels Konfigurationsdatei <code>.env</code> gelangt per Secret in den Container und wird nun per Symlink an die richtige Stelle befördert. Mittels Helfer <code>wait-for</code> wird nun auf den benachbarten Container mit dem Hostnamen <code>mysql</code> gewartet, um anschließend die Datenbank-Migration mit dem Befehl <code>php artisan migrate --force</code> durchzuführen. Sollte der MySQL-Dienst nicht innerhalb von 60 Sekunden (<code>--timeout=60</code>) zur Verfügung stehen, bricht <code>wait-for</code> den Vorgang ab. Mit den beiden Datumsausgaben im Skript lässt sich schnell verifizieren, wie lange es gedauert hat, bis der MySQL-Dienst erreichbar ist.</p><h2 id="fazit"><strong>Fazit</strong></h2><p>Mit unserer Arbeit stellen wir sicher, dass der PHP-Container wartet bis der MySQL-Dienst zur Verfügung steht. Dieser führt die Datenbank-Migration durch und startet anschließend den eigentlichen PHP-Dienst, damit nginx Anfragen an ihn stellen kann. Die Ausführungen sollen selbstverständlich nur als erster Ansatz dienen. Der MySQL-Hostname ließe sich beispielsweise per Environment Variable in den <code>ENTRYPOINT</code> einschleusen, statt ihn statisch zu hinterlegen. Den Timeout von <code>wait-for</code> könnte man auch noch weiter verarbeiten und z. B. das weitere Starten des PHP-Prozesses (php-fpm) unterbinden. Dann würde sich der Swarm Mode mit einer <code>restart-policy</code>, die man in der <code>docker-compose.yml</code> mit der Direktive <code>deploy</code> definiert, darum kümmern, den Container neu zu starten. Weiteres dazu kann man selbstverständlich in der <a href="https://docs.docker.com/compose/compose-file/#resources">offiziellen Dokumentation</a> nachlesen. Wem das alles noch nicht reicht, dem empfehle ich einen Blick auf <a href="https://flywaydb.org/">Flyway</a> zu werfen.</p><p><em>Bild von <a href="https://unsplash.com/@erdaest">Erda Estremera</a></em></p>]]></content:encoded></item><item><title><![CDATA[Trick 17: Einfacher SQL-Import mit Docker]]></title><description><![CDATA[Hier ein schneller Tipp, der gerade im Zusammenhang mit Legacy-Applikationen helfen kann, bei denen man einen SQL-Dump beim Starten des MySQL-Containers auch gleich importiert haben möchte.]]></description><link>https://blog.servivum.com/trick-17-einfacher-sql-import-mit-docker/</link><guid isPermaLink="false">5c0e4c9742e8830001d2495a</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Patrick Baber]]></dc:creator><pubDate>Thu, 27 Jul 2017 10:18:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1571786256017-aee7a0c009b6?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1571786256017-aee7a0c009b6?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Trick 17: Einfacher SQL-Import mit Docker"><p>Hier ein schneller Tipp, der gerade im Zusammenhang mit Legacy-Applikationen helfen kann, bei denen man einen SQL-Dump beim Starten des MySQL-Containers auch gleich importiert haben möchte.</p><p>In meiner <code>docker-compose.yml</code> nutze ich das offizielle MariaDB-Image. Mit dem MySQL-Image funktioniert das aber genauso. Man achte auf die letzte Zeile. Dort wird mittels Host-Mount ein SQL-Dump aus dem Projekt-Verzeichnis innerhalb des Containers, im Ordner <code>/docker-entrypoint-initdb.d</code>, verfügbar gemacht.</p><pre><code>version: "3.7"

services:
  db:
    image: mariadb:10.4
    environment:
      MYSQL_ROOT_PASSWORD: myrootpassword
      MYSQL_DATABASE: mydatabase
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
    ports:
      - 3306:3306
    volumes:
      - ./sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql</code></pre><p>Mit <code>docker-compose up</code> wird nun der Dienst gestartet und der SQL-Dump in die angegebene Datenbank importiert. Wer die Magie verstehen möchte, muss nur einen Blick in die als <code>ENTRYPOINT</code> hinterlegte Datei <a href="https://github.com/docker-library/mariadb/blob/master/10.3/docker-entrypoint.sh#L170">docker-entrypoint.sh</a> des Images werfen. Dort wird nämlich innerhalb des Ordners nach Dateien mit den Endungen <code>.sh</code>, <code>.sql</code> und <code>.sql.gz</code> geschaut und diese ausgeführt, bzw. importiert. Möglich ist somit auch das Importieren mehrerer SQL-Dumps, indem man den gesamten Ordner mountet.</p><p><em>Bild von <a href="https://unsplash.com/@ianjbattaglia">Ian Battaglia</a></em></p>]]></content:encoded></item></channel></rss>