{"id":69351,"date":"2020-02-18T14:44:06","date_gmt":"2020-02-18T11:44:06","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker"},"modified":"2020-03-03T16:15:00","modified_gmt":"2020-03-03T13:15:00","slug":"sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Erstellung einer CI\/CD-Pipeline und Automatisierung der Arbeit mit Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ich habe meine ersten Websites Ende der 90er Jahre erstellt. Damals war es sehr einfach, sie funktionsf\u00e4hig zu machen. Es gab einen Apache-Server auf einem Shared Hosting, auf den man per FTP zugreifen konnte, indem man in die Adresszeile des Browsers etwas wie <code>ftp:\/\/ftp.example.com<\/code>. Dann musste ich einen Benutzernamen und ein Passwort eingeben und die Dateien auf den Server hochladen. Es waren andere Zeiten, alles war damals einfacher als heute.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Erstellung einer CI\/CD-Pipeline und Automatisierung der Arbeit mit Docker\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>In den zwei Jahrzehnten seitdem hat sich viel ver\u00e4ndert. Websites sind komplexer geworden, sie m\u00fcssen vor dem Produktionsstart zusammengestellt werden. Ein einzelner Server ist mittlerweile eine Vielzahl von Servern, die hinter Lastverteilern arbeiten, und die Verwendung von Versionskontrollsystemen ist zur Norm geworden.<\/p>\n<p>F\u00fcr mein pers\u00f6nliches Projekt hatte ich eine spezielle Konfiguration. Und ich wusste, dass ich die M\u00f6glichkeit ben\u00f6tige, die Website in der Produktion mit nur einem einzigen Schritt zu deployen: Code in einen Branch zu schreiben. <code>master<\/code> auf GitHub. Zudem wusste ich, dass ich f\u00fcr den Betrieb meiner kleinen Webanwendung nicht das Management eines riesigen Kubernetes-Clusters \u00fcbernehmen, Docker Swarm nutzen oder einen Serverpark mit Pods, Agenten und anderen Komplikationen pflegen wollte. Um mein Ziel der maximalen Vereinfachung zu erreichen, musste ich mich mit CI\/CD vertrautmachen.<\/p>\n<p>Wenn Sie ein kleines Projekt haben (in diesem Fall handelt es sich um ein Node.js-Projekt) und erfahren m\u00f6chten, wie Sie das Deployment dieses Projekts automatisieren k\u00f6nnen, sodass das, was im Repository gespeichert ist, genau dem entspricht, was in der Produktion l\u00e4uft, k\u00f6nnte Sie dieser Artikel interessieren.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Voraussetzungen<\/h2>\n<p>\nEs wird erwartet, dass der Leser dieses Artikels grundlegende Kenntnisse im Umgang mit der Kommandozeile und im Schreiben von Bash-Skripten hat. Dar\u00fcber hinaus ben\u00f6tigt er Konten bei <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> und <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>Ziele<\/h2>\n<p>\nIch w\u00fcrde nicht sagen, dass dieser Artikel bedingungslos als \u201eLeitfaden\u201c bezeichnet werden kann. Es handelt sich eher um ein Dokument, in dem ich teile, was ich gelernt habe, und den Prozess beschreibe, der f\u00fcr mich beim Testen und der Bereitstellung von Code in der Produktion bei einem automatisierten Durchlauf zufriedenstellend ist.<\/p>\n<p>So sieht mein Arbeitsprozess letztendlich aus.<\/p>\n<p>F\u00fcr Code, der in einen beliebigen anderen Branch des Repositories gesendet wird, au\u00dfer <code>master<\/code>, werden folgende Aktionen durchgef\u00fchrt:<\/p>\n<ul>\n<li>Der Projekt-Build wird auf Travis CI gestartet.<\/li>\n<li>Alle Modul-, Integrations- und End-to-End-Tests werden durchgef\u00fchrt.<\/li>\n<\/ul>\n<p>\nNur f\u00fcr Code, der in <code>master<\/code>, wird Folgendes ausgef\u00fchrt:<\/p>\n<ul>\n<li>Alles, was oben erw\u00e4hnt wurde, plus\u2026<\/li>\n<li>Der Docker-Image wird basierend auf dem aktuellen Code, den Einstellungen und der Umgebung erstellt.<\/li>\n<li>Das Image wird auf Docker Hub bereitgestellt.<\/li>\n<li>Verbindung zum Produktionsserver wird hergestellt.<\/li>\n<li>Image von Docker Hub auf den Server herunterladen.<\/li>\n<li>Den aktuellen Container anhalten und einen neuen starten, der auf dem neuen Image basiert.<\/li>\n<\/ul>\n<p>\nWenn Sie absolut nichts \u00fcber Docker, Images und Container wissen, machen Sie sich keine Sorgen. Ich werde Ihnen alles dar\u00fcber erz\u00e4hlen.<\/p>\n<h2>Was ist CI\/CD?<\/h2>\n<p>\nDie Abk\u00fcrzung CI\/CD steht f\u00fcr \u201eContinuous Integration\/Continuous Deployment\u201c \u2013 also \u201ekontinuierliche Integration\/kontinuierliches Deployment\u201c.<\/p>\n<h3>\u258dKontinuierliche Integration<\/h3>\n<p>\nKontinuierliche Integration ist der Prozess, bei dem Entwickler \u00c4nderungen in das Hauptrepository des Projekts einpflegen (in der Regel in den Branch <code>master<\/code>). Die Qualit\u00e4t des Codes wird durch automatisierte Tests sichergestellt.<\/p>\n<h3>\u258dKontinuierliches Deployment<\/h3>\n<p>\nKontinuierliches Deployment bezeichnet das h\u00e4ufige, automatisierte Bereitstellen von Code in der Produktionsumgebung. Der zweite Teil der Abk\u00fcrzung CI\/CD wird manchmal als \u201eContinuous Delivery\u201c (\u201ekontinuierliche Lieferung\u201c) interpretiert. Dies ist im Wesentlichen dasselbe wie \u201ekontinuierliches Deployment\u201c, jedoch setzt \u201ekontinuierliche Lieferung\u201c voraus, dass \u00c4nderungen manuell genehmigt werden, bevor der Deployment-Prozess f\u00fcr das Projekt gestartet wird.<\/p>\n<h2>Erste Schritte<\/h2>\n<p>\nDie Anwendung, mit der ich all dies erlernt habe, hei\u00dft <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Dies ist ein Webprojekt, an dem ich arbeite und das dazu dient, Notizen zu machen. Zun\u00e4chst habe ich versucht, <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">JAMStack<\/a><\/noindex>-Projekt oder nur eine Frontend-Anwendung ohne Server, um die Standardm\u00f6glichkeiten f\u00fcr Hosting und Deployment zu nutzen, die angeboten werden von <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Mit wachsender Komplexit\u00e4t der Anwendung musste ich auch einen Server erstellen, was bedeutete, dass ich eine eigene Strategie f\u00fcr die automatisierte Integration und das automatisierte Deployment des Projekts entwickeln musste.<\/p>\n<p>In meinem Fall handelt es sich bei der Anwendung um einen Express-Server, der in der Node.js-Umgebung l\u00e4uft, ein einseitiges React-Anwendungs-Frontend bedient und eine sichere serverseitige API unterst\u00fctzt. Diese Architektur folgt einer Strategie, die in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">diesem<\/a><\/noindex> Leitfaden zur Full-Stack-Authentifizierung zu finden ist.<\/p>\n<p>Ich habe mit <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">einem Freund gesprochen<\/a><\/noindex>, der Experte f\u00fcr Automatisierung war, und fragte ihn, was ich tun m\u00fcsse, damit alles so funktioniert, wie ich es ben\u00f6tige. Er gab mir eine Idee, wie der automatisierte Workflow aussehen sollte, der im Abschnitt \u201eZiele\u201c dieses Artikels dargestellt ist. Dass ich mir solche Ziele gesetzt habe, bedeutete, dass ich lernen musste, wie man Docker nutzt.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker ist ein Tool, das durch Containerisierungstechnologie das einfache Verteilen von Anwendungen erm\u00f6glicht und deren Bereitstellung sowie Ausf\u00fchrung in derselben Umgebung erm\u00f6glicht, selbst wenn die Docker-Plattform in unterschiedlichen Umgebungen arbeitet. Zun\u00e4chst ben\u00f6tigte ich die Docker-CLI-Tools. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">Die Anleitung<\/a><\/noindex> zur Installation von Docker ist nicht besonders klar und verst\u00e4ndlich, aber sie informiert dar\u00fcber, dass man, um den ersten Schritt zur Installation zu machen, Docker Desktop (f\u00fcr Mac oder Windows) herunterladen muss.<\/p>\n<p>Docker Hub ist ungef\u00e4hr das gleiche wie <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> f\u00fcr Git-Repositories, oder ein Registry <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> f\u00fcr JavaScript-Pakete. Es handelt sich um ein Online-Repository f\u00fcr Docker-Images. Genau damit verbindet sich Docker Desktop.<\/p>\n<p>Um mit Docker zu beginnen, m\u00fcssen Sie zwei Dinge tun:<\/p>\n<ul>\n<li>Installieren Sie <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>Registrieren Sie sich bei <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nAnschlie\u00dfend k\u00f6nnen Sie die Funktionsf\u00e4higkeit der Docker CLI \u00fcberpr\u00fcfen, indem Sie den folgenden Befehl zur \u00dcberpr\u00fcfung der Docker-Version eingeben:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nDanach melden Sie sich bei Docker Hub an, indem Sie Ihren Benutzernamen und Ihr Passwort eingeben, wenn Sie dazu aufgefordert werden:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nUm Docker zu nutzen, m\u00fcssen Sie die Konzepte von Images und Containern verstehen.<\/p>\n<h3>\u258dImages<\/h3>\n<p>\nEin Image ist eine Art Vorlage, die Anweisungen zum Erstellen eines Containers enth\u00e4lt. Es handelt sich um einen unver\u00e4nderlichen Snapshot des Dateisystems und der Anwendungseinstellungen. Entwickler k\u00f6nnen Images problemlos austauschen.<\/p>\n<pre><code class=\"plaintext\"># \u0412\u044b\u0432\u043e\u0434 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0437\u0430\u0445\ndocker images<\/code><\/pre>\n<p>\nDieser Befehl gibt eine Tabelle mit dem folgenden Header aus:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nAls N\u00e4chstes werden wir einige Beispielbefehle im gleichen Format betrachten \u2013 zuerst der Befehl mit einem Kommentar, gefolgt von einem Beispiel, was er ausgeben kann.<\/p>\n<h3>\u258dContainer<\/h3>\n<p>\nEin Container ist ein ausf\u00fchrbares Paket, das alles enth\u00e4lt, was zur Ausf\u00fchrung einer Anwendung erforderlich ist. Mit diesem Ansatz funktioniert die Anwendung immer gleich, unabh\u00e4ngig von der Infrastruktur: in einer isolierten Umgebung und in der gleichen Umgebung. Es geht darum, dass in verschiedenen Umgebungen Instanzen desselben Images gestartet werden.<\/p>\n<pre><code class=\"plaintext\"># \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432\ndocker ps -a\nCONTAINER ID \u00a0 \u00a0 IMAGE     COMMAND \u00a0 \u00a0 CREATED     STATUS     PORTS \u00a0 \u00a0 NAMES\n---<\/code><\/pre>\n<p><\/p>\n<h3>\u258dTags<\/h3>\n<p>\nEin Tag ist ein Verweis auf eine bestimmte Version eines Images.<\/p>\n<h3>\u258dKurz\u00fcbersicht \u00fcber Docker-Befehle<\/h3>\n<p>\nHier ist eine \u00dcbersicht \u00fcber einige h\u00e4ufig verwendete Docker-Befehle.<\/p>\n<p>Der Befehl<\/p>\n<p>Kontext<\/p>\n<p>Aktion<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Bauen eines Images aus einem Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Tagging eines Images<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Liste der Images anzeigen<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">docker run<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Einen Container auf der Basis eines Images starten<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Image in ein Registry senden<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Image aus der Registry herunterladen<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Liste der Container anzeigen<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/system_prune\/\">docker system prune<\/a><\/noindex><\/p>\n<p>Image\/Container<\/p>\n<p>Entfernen von nicht verwendeten Containern und Images<\/p>\n<h3>\u258dDockerfile<\/h3>\n<p>\nIch wei\u00df, wie man eine Anwendung lokal f\u00fcr die Produktion startet. Ich habe eine Webpack-Konfiguration, die f\u00fcr den Build einer fertigen React-Anwendung gedacht ist. Au\u00dferdem habe ich einen Befehl, der einen auf Node.js basierenden Server auf dem Port <code>5000<\/code>startet. Es sieht so aus:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # Abh\u00e4ngigkeiten installieren\nnpm run build # React-Anwendung erstellen\nnpm run start # Node-Server starten<\/code><\/pre>\n<p>\nEs ist wichtig zu erw\u00e4hnen, dass ich keine Beispielanwendung f\u00fcr dieses Material habe. Aber f\u00fcr Experimente eignet sich jede einfache Node-Anwendung.<\/p>\n<p>Um den Container zu nutzen, m\u00fcssen Sie Docker-Anweisungen geben. Dies geschieht \u00fcber eine Datei, die man nennt <code>Dockerfile<\/code>, die sich im Stammverzeichnis des Projekts befindet. Diese Datei scheint zun\u00e4chst ziemlich unverst\u00e4ndlich.<\/p>\n<p>Was darin enthalten ist, beschreibt lediglich, mit speziellen Befehlen, eine Art von Konfiguration der Arbeitsumgebung. Hier sind einige dieser Befehle:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">FROM<\/a><\/noindex> \u2014 Dieser Befehl beginnt die Datei. Hier wird das Basis-Image angegeben, auf dessen Grundlage der Container aufgebaut wird.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Kopieren von Dateien aus einer lokalen Quelle in den Container.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Festlegen des Arbeitsverzeichnisses f\u00fcr die folgenden Befehle.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">RUN<\/a><\/noindex> \u2014 Befehle ausf\u00fchren.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Port konfiguriert.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Angabe des auszuf\u00fchrenden Befehls.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> kann ungef\u00e4hr so aussehen:<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0437\nFROM node:12-alpine\n\n# \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app\/\nCOPY . app\/\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c app\/ \u0432 \u0440\u043e\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438\nWORKDIR app\/\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (\u043a\u043e\u043c\u0430\u043d\u0434\u0430 npm ci \u043f\u043e\u0445\u043e\u0436\u0430 npm i, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043e\u043a)\nRUN npm ci --only-production\n\n# \u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0430\nRUN npm run build\n\n# \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442\nEXPOSE 5000\n\n# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Node-\u0441\u0435\u0440\u0432\u0435\u0440\nENTRYPOINT npm run start<\/code><\/pre>\n<p>\nJe nach dem gew\u00e4hlten Basis-Image m\u00fcssen m\u00f6glicherweise zus\u00e4tzliche Abh\u00e4ngigkeiten installiert werden. Einige Basis-Images (wie Node Alpine Linux) wurden entwickelt, um so kompakt wie m\u00f6glich zu sein. Daher fehlen m\u00f6glicherweise einige Programme, auf die Sie angewiesen sind.<\/p>\n<h3>\u258dBauen, Taggen und Starten des Containers<\/h3>\n<p>\nDas lokale Bauen und Starten des Containers ist, nachdem wir das haben, <code>Dockerfile<\/code>, eine ziemlich einfache Aufgabe. Bevor Sie das Image auf Docker Hub senden, sollte es lokal getestet werden.<\/p>\n<h3>\u258dBauen<\/h3>\n<p>\nZuerst m\u00fcssen wir das <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">Image<\/a><\/noindex>, benennen und optional ein Tag angeben (falls kein Tag angegeben wird, weist das System dem Image automatisch das Tag <code>latest<\/code>).<\/p>\n<pre><code class=\"plaintext\"># \u0421\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t &lt;image&gt;:&lt;tag&gt; .<\/code><\/pre>\n<p>\nzu). Nachdem dieser Befehl ausgef\u00fchrt wurde, k\u00f6nnen Sie beobachten, wie Docker das Image erstellt.<\/p>\n<pre><code class=\"plaintext\">Sending build context to Docker daemon   2.88MB\nStep 1\/9 : FROM node:12-alpine\n ---&gt; ...f\u00fchre Build-Schritte aus...\nErfolgreich gebaut 123456789123\nErfolgreich getaggt :<\/code><\/pre>\n<p>\nDas Bauen kann einige Minuten in Anspruch nehmen \u2013 es h\u00e4ngt alles davon ab, wie viele Abh\u00e4ngigkeiten Sie haben. Nach Abschluss des Builds k\u00f6nnen Sie den Befehl <code>docker images<\/code> ausf\u00fchren und die Beschreibung Ihres neuen Images ansehen.<\/p>\n<pre><code class=\"plaintext\">REPOSITORY          TAG               IMAGE ID            CREATED              SIZE\n              latest            123456789123        Vor einer Minute        x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dStarten<\/h3>\n<p>\nDas Image wurde erstellt. Das bedeutet, dass ich auf dessen Basis einen Container starten kann. Da ich die M\u00f6glichkeit haben m\u00f6chte, auf die Anwendung, die im Container l\u00e4uft, \u00fcber die Adresse <code>localhost:5000<\/code>, habe ich in der linken Teile der Pair <code>5000:5000<\/code> in dem n\u00e4chsten Befehl festgelegt <code>5000<\/code>. Auf der rechten Seite befindet sich der Container-Port.<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u0442\u0430 5000 \u0438 \u043f\u043e\u0440\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 5000\ndocker run -p 5000:5000 &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nJetzt, wo der Container erstellt und gestartet wurde, kann ich den Befehl <code>docker ps<\/code> verwenden, um Informationen \u00fcber diesen Container anzuzeigen (oder ich kann den Befehl <code>docker ps -a<\/code>, der Informationen \u00fcber alle Container und nicht nur \u00fcber die laufenden ausgibt, verwenden).<\/p>\n<pre><code class=\"plaintext\">CONTAINER ID\u00a0 \u00a0 \u00a0 \u00a0 IMAGE               COMMAND\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATED              STATUS                  \u00a0 \u00a0 PORTS                    NAMES\n987654321234\u00a0 \u00a0 \u00a0 \u00a0 &lt;image&gt;             &quot;\/bin\/sh -c 'npm run\u2026&quot; \u00a0 6 seconds ago        Up 6 seconds            \u00a0 \u00a0 0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nWenn ich jetzt die Adresse <code>localhost:5000<\/code> aufrufe, kann ich die Seite der laufenden Anwendung sehen, die genau so aussieht wie die Seite der Anwendung im Produktionsumfeld.<\/p>\n<h3>\u258dTagging und Ver\u00f6ffentlichung<\/h3>\n<p>\nUm eines der erstellten Images auf dem Produktionsserver nutzen zu k\u00f6nnen, muss es m\u00f6glich sein, dieses Image von Docker Hub herunterzuladen. Das bedeutet, dass zun\u00e4chst ein Repository f\u00fcr das Projekt auf Docker Hub erstellt werden muss. Danach steht uns ein Speicherplatz zur Verf\u00fcgung, in den das Image gesendet werden kann. Das Image sollte so umbenannt werden, dass der Name mit unserem Benutzernamen auf Docker Hub beginnt. Danach folgt der Name des Repositories. Am Ende des Namens kann ein beliebiges Tag stehen. Unten sehen Sie ein Beispiel f\u00fcr die Benennung von Images nach diesem Schema.<\/p>\n<p>Jetzt k\u00f6nnen Sie das Image mit einem neuen Namen erstellen und den Befehl ausf\u00fchren <code>docker push<\/code> um es in das Repository auf Docker Hub zu senden.<\/p>\n<pre><code class=\"plaintext\">docker build -t \/: .\ndocker tag \/: \/:latest\ndocker push \/:\n\n# In der Praxis k\u00f6nnte das zum Beispiel so aussehen:\ndocker build -t user\/app:v1.0.0 .\ndocker tag user\/app:v1.0.0 user\/app:latest\ndocker push user\/app:v1.0.0<\/code><\/pre>\n<p>\nWenn alles gut geht, wird das Image auf Docker Hub verf\u00fcgbar sein und kann leicht auf den Server heruntergeladen oder an andere Entwickler weitergegeben werden.<\/p>\n<h2>Die n\u00e4chsten Schritte<\/h2>\n<p>\nWir haben mittlerweile sichergestellt, dass die Anwendung als Docker-Container lokal funktioniert. Wir haben den Container auf Docker Hub hochgeladen. Das bedeutet, dass wir bereits einen gro\u00dfen Schritt in Richtung Ziel gemacht haben. Jetzt m\u00fcssen noch zwei Fragen gekl\u00e4rt werden:<\/p>\n<ul>\n<li>Einrichtung des CI-Tools f\u00fcr das Testen und das Deployment des Codes.<\/li>\n<li>Einrichtung des Produktionsservers, damit dieser unseren Code laden und ausf\u00fchren kann.<\/li>\n<\/ul>\n<p>\nIn unserem Fall verwenden wir als CI\/CD-L\u00f6sung <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Als Server dient <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Es ist erw\u00e4hnenswert, dass hier auch andere Kombinationen von Services genutzt werden k\u00f6nnen. Zum Beispiel kann man anstelle von Travis CI auch CircleCI oder GitHub Actions verwenden. Und anstelle von DigitalOcean k\u00f6nnten AWS oder Linode genutzt werden.<\/p>\n<p>Wir haben uns entschieden, mit Travis CI zu arbeiten, und in diesem Service ist bereits einiges konfiguriert. Daher werde ich jetzt kurz erl\u00e4utern, wie man ihn f\u00fcr den Betrieb vorbereitet.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI ist ein Tool zum Testen und Bereitstellen von Code. Ich m\u00f6chte nicht zu sehr ins Detail der Konfiguration von Travis CI gehen, da jedes Projekt einzigartig ist und dies nicht besonders hilfreich w\u00e4re. Aber ich werde die Grundlagen erl\u00e4utern, die Ihnen den Einstieg erleichtern, falls Sie sich entscheiden, Travis CI zu nutzen. Egal, ob Sie sich f\u00fcr Travis CI, CircleCI, Jenkins oder etwas anderes entscheiden, \u00e4hnliche Konfigurationsmethoden kommen \u00fcberall zur Anwendung.<\/p>\n<p>Um mit Travis CI zu beginnen, besuchen Sie <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">die Projektseite<\/a><\/noindex> und erstellen Sie ein Konto. In der Folge integrieren Sie Travis CI mit Ihrem GitHub-Konto. W\u00e4hrend der Systemkonfiguration m\u00fcssen Sie das Repository angeben, dessen Automatisierung Sie w\u00fcnschen, und den Zugriff darauf aktivieren. (Ich nutze GitHub, bin mir aber sicher, dass Travis CI auch mit BitBucket, GitLab und \u00e4hnlichen Diensten integriert werden kann.)<\/p>\n<p>Jedes Mal, wenn Travis CI in Betrieb genommen wird, startet ein Server, der die im Konfigurationsdatei angegebenen Befehle ausf\u00fchrt, einschlie\u00dflich der Bereitstellung der entsprechenden Branches des Repositories.<\/p>\n<h3>\u258dLebenszyklus eines Jobs<\/h3>\n<p>\nDie Konfigurationsdatei von Travis CI, genannt <code>.travis.yml<\/code> und im Stammverzeichnis des Projekts gespeichert, unterst\u00fctzt das Ereigniskonzept <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">des Lebenszyklus<\/a><\/noindex> von Aufgaben. Hier die Ereignisse in der Reihenfolge, in der sie auftreten:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>Cache-Komponenten<\/code><\/li>\n<li><code>before_install<\/code><\/li>\n<li><code>install<\/code><\/li>\n<li><code>before_script<\/code><\/li>\n<li><code>script<\/code><\/li>\n<li><code>before_cache<\/code><\/li>\n<li><code>after_success oder after_failure<\/code><\/li>\n<li><code>before_deploy<\/code><\/li>\n<li><code>deploy<\/code><\/li>\n<li><code>after_deploy<\/code><\/li>\n<li><code>after_script<\/code><\/li>\n<\/ul>\n<p><\/p>\n<h3>\u258dTesten<\/h3>\n<p>\nIn der Konfigurationsdatei werde ich den lokalen Travis CI-Server einrichten. Als Sprache habe ich Node 12 gew\u00e4hlt und der Software mitgeteilt, dass die f\u00fcr die Verwendung von Docker erforderlichen Abh\u00e4ngigkeiten installiert werden m\u00fcssen.<\/p>\n<p>Alles, was in <code>.travis.yml<\/code>, aufgef\u00fchrt ist, wird ausgef\u00fchrt, wenn Pull-Requests in alle Zweige des Repositories durchgef\u00fchrt werden, es sei denn, es wird etwas anderes angegeben. Dies ist eine n\u00fctzliche Funktion, da sie bedeutet, dass wir den gesamten Code, der in das Repository eingeht, testen k\u00f6nnen. So wissen wir, ob der Code bereit ist, in den Branch geschrieben zu werden <code>master<\/code>, und ob er den Build-Prozess st\u00f6ren wird. In dieser globalen Konfiguration installiere ich alles lokal, starte den Webpack-Entwicklungsserver im Hintergrund (das ist ein Merkmal meines Arbeitsablaufs) und f\u00fchre Tests durch.<\/p>\n<p>Wenn Sie m\u00f6chten, dass in Ihrem Repository Abzeichen mit Informationen \u00fcber die Codeabdeckung angezeigt werden, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">hier<\/a><\/noindex> Sie k\u00f6nnen eine kurze Anleitung zur Verwendung von Jest, Travis CI und Coveralls zur Sammlung und Ausgabe dieser Informationen finden.<\/p>\n<p>Hier ist der Inhalt der Datei <code>.travis.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\"># \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044f\u0437\u044b\u043a\nlanguage: node_js\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e Node.js\nnode_js:\n\u00a0\u00a0- '12'\n\nservices:\n\u00a0\u00a0# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 Docker\n\u00a0\u00a0- docker\n\ninstall:\n\u00a0\u00a0# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm ci\n\nbefore_script:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm run dev &amp;\n\nscript:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b\n\u00a0\u00a0- npm run test<\/code><\/pre>\n<p>\nHier enden die Schritte, die f\u00fcr alle Branches im Repository und f\u00fcr Pull-Requests durchgef\u00fchrt werden.<\/p>\n<h3>\u258dBereitstellung<\/h3>\n<p>\nUnter der Annahme, dass alle automatisierten Tests erfolgreich abgeschlossen wurden, k\u00f6nnen wir, was nicht notwendig ist, den Code auf dem Produktionsserver bereitstellen. Da wir dies nur f\u00fcr den Code aus dem Branch <code>master<\/code>, geben wir dem System die entsprechenden Anweisungen in den Bereitstellungseinstellungen. Bevor Sie den Code, den wir weiter unten besprechen, in Ihrem Projekt verwenden, m\u00f6chte ich Sie darauf hinweisen, dass Sie ein reales Skript haben m\u00fcssen, das f\u00fcr die Bereitstellung aufgerufen wird.<\/p>\n<pre><code class=\"plaintext\">deploy:\n  # Docker-Container bauen und an Docker Hub senden\n  provider: script\n  script: bash deploy.sh\n  on:\n    branch: master<\/code><\/pre>\n<p>\nDas Bereitstellungsskript erf\u00fcllt zwei Aufgaben:<\/p>\n<ul>\n<li>Bauen, Taggen und Senden des Images an Docker Hub mittels CI-Tools (in unserem Fall Travis CI).<\/li>\n<li>Das Hochladen des Images auf den Server, Stoppen des alten Containers und Starten des neuen (in unserem Fall l\u00e4uft der Server auf der Plattform DigitalOcean).<\/li>\n<\/ul>\n<p>\nZuerst m\u00fcssen wir den automatischen Prozess f\u00fcr das Erstellen, Taggen und Hochladen des Images auf Docker Hub einrichten. Das \u00e4hnelt sehr dem, was wir bereits manuell gemacht haben, abgesehen davon, dass wir hier eine Strategie f\u00fcr die eindeutige Vergabe von Tags f\u00fcr die Images sowie die Automatisierung des Logins ben\u00f6tigen. Ich hatte Schwierigkeiten mit einigen Details des Bereitstellungsskripts, wie der Tagging-Strategie, dem Login, der SSH-Schl\u00fcssel-Codierung und dem Herstellen der SSH-Verbindung. Zum Gl\u00fcck kann mein Freund sehr gut mit Bash umgehen, wie auch mit vielen anderen Dingen. Er hat mir geholfen, dieses Skript zu schreiben.<\/p>\n<p>Die erste Phase des Skripts besteht darin, das Image auf Docker Hub hochzuladen. Das ist ziemlich einfach. Das von mir verwendete Tagging-Schema sieht vor, den Git-Hash und den Git-Tag zu kombinieren, wenn dieser existiert. So wird sichergestellt, dass ein einzigartiger Tag erstellt wird und die Identifizierung des Builds, auf dem er basiert, erleichtert wird. <code>DOCKER_USERNAME<\/code> und <code>DOCKER_PASSWORD<\/code> \u2014 dies sind benutzerdefinierte Umgebungsvariablen, die \u00fcber die Travis CI-Oberfl\u00e4che festgelegt werden k\u00f6nnen. Travis CI verarbeitet die geheimen Daten automatisch, sodass sie nicht in die falschen H\u00e4nde geraten.<\/p>\n<p>Hier ist der erste Teil des Skripts <code>deploy.sh<\/code>.<\/p>\n<pre><code class=\"plaintext\">#!\/bin\/sh\nset -e # \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a\n\nIMAGE=&quot;&lt;username&gt;\/&lt;repository&gt;&quot; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # \u041e\u0431\u0440\u0430\u0437 Docker\nGIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-\u0445\u044d\u0448 \u0438 \u0442\u0435\u0433\u0438\n\n# \u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t ${IMAGE}:${GIT_VERSION} .\ndocker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest\n\n# \u0412\u0445\u043e\u0434 \u0432 Docker Hub \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\necho &quot;${DOCKER_PASSWORD}&quot; | docker login -u &quot;${DOCKER_USERNAME}&quot; --password-stdin\ndocker push ${IMAGE}:${GIT_VERSION}<\/code><\/pre>\n<p>\nDer Inhalt des zweiten Teils des Skripts h\u00e4ngt vollst\u00e4ndig davon ab, welchen Host Sie verwenden und wie die Verbindung zu diesem organisiert ist. In meinem Fall, da ich Digital Ocean nutze, werden zur Verbindung mit dem Server die Befehle <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>verwendet. Bei der Arbeit mit AWS kommt das Tool <code>aws<\/code>zum Einsatz, und so weiter.<\/p>\n<p>Die Einrichtung des Servers war nicht besonders schwierig. Ich habe einen Droplet basierend auf dem Basis-Image konfiguriert. Es ist erw\u00e4hnenswert, dass das von mir gew\u00e4hlte System die einmalige manuelle Installation von Docker und den einmaligen manuellen Start von Docker erfordert. Ich habe zur Installation von Docker Ubuntu 18.04 verwendet, daher k\u00f6nnen Sie, wenn Sie ebenfalls Ubuntu nutzen, einfach diesem <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">einfachen Leitfaden<\/a><\/noindex> folgen.<\/p>\n<p>Ich spreche hier nicht von spezifischen Befehlen f\u00fcr den Dienst, da dieser Aspekt in verschiedenen F\u00e4llen stark variieren kann. Ich werde lediglich einen allgemeinen Aktionsplan skizzieren, der nach der SSH-Verbindung zum Server ausgef\u00fchrt wird, auf dem das Projekt bereitgestellt wird:<\/p>\n<ul>\n<li>Zun\u00e4chst m\u00fcssen Sie den aktuell laufenden Container finden und ihn stoppen.<\/li>\n<li>Dann muss im Hintergrund ein neuer Container gestartet werden.<\/li>\n<li>Sie m\u00fcssen den lokalen Serverport auf den Wert einstellen <code>80<\/code> \u2014 das erm\u00f6glicht den Zugang zur Website unter einer Adresse wie <code>example.com<\/code>, ohne den Port anzugeben, anstatt eine Adresse wie <code>example.com:5000<\/code>.<\/li>\n<li>Und schlie\u00dflich m\u00fcssen alle alten Container und Images gel\u00f6scht werden.<\/li>\n<\/ul>\n<p>\nHier ist die Fortsetzung des Skripts.<\/p>\n<pre><code class=\"plaintext\"># \u041d\u0430\u0439\u0442\u0438 ID \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\nCONTAINER_ID=$(docker ps | grep takenote | cut -d&quot; &quot; -f1)\n\n# \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439, \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443\ndocker stop ${CONTAINER_ID}\ndocker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}\ndocker system prune -a -f<\/code><\/pre>\n<p><\/p>\n<h2>Einige Dinge, auf die Sie achten sollten<\/h2>\n<p>\nEs kann sein, dass Sie beim Verbinden mit dem Server \u00fcber SSH aus Travis CI eine Warnung sehen, die eine Fortsetzung der Installation verhindert, da das System auf eine Benutzerreaktion wartet.<\/p>\n<pre><code class=\"plaintext\">Die Authentizit\u00e4t des Hosts ' (&lt;IP-Adresse&gt;)' kann nicht festgestellt werden.\nDer RSA-Schl\u00fcssel-Fingerabdruck ist &lt;Fingerabdruck&gt;.\nSind Sie sicher, dass Sie mit der Verbindung fortfahren m\u00f6chten (ja\/nein)?<\/code><\/pre>\n<p>\nIch habe herausgefunden, dass der String-Schl\u00fcssel in base64 kodiert werden kann, um ihn in einer Form zu speichern, mit der bequem und zuverl\u00e4ssig gearbeitet werden kann. W\u00e4hrend der Installation kann der \u00f6ffentliche Schl\u00fcssel dekodiert und in eine Datei geschrieben werden <code>known_hosts<\/code> um den oben beschriebenen Fehler zu beheben.<\/p>\n<pre><code class=\"plaintext\">echo  | base64 # gibt  aus<\/code><\/pre>\n<p>\nIn der Praxis k\u00f6nnte dieser Befehl so aussehen:<\/p>\n<pre><code class=\"plaintext\">echo &quot;123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom\/BWDSU\nGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3\nPbv7kOdJ\/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK\/7XA\nt3FaoJoAsncM1Q9x5+3V0Ww68\/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw\/Pb0rwert\/En\nmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z\/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx\nNrRFi9wrf+M7Q== you@example.com&quot; | base64<\/code><\/pre>\n<p>\nSo sieht der Output aus \u2014 eine Zeichenkette in Base64-Codierung:<\/p>\n<pre><code class=\"plaintext\">MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vpa0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==<\/code><\/pre>\n<p>\nHier ist der Befehl, der oben erw\u00e4hnt wurde.<\/p>\n<pre><code class=\"plaintext\">install:\n  - echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nDer gleiche Ansatz kann auch mit dem privaten Schl\u00fcssel verwendet werden, wenn eine Verbindung hergestellt wird, da Sie m\u00f6glicherweise den privaten Schl\u00fcssel ben\u00f6tigen, um auf den Server zuzugreifen. Achten Sie darauf, ihn sicher in der Umgebungsvariable von Travis CI zu speichern, ohne dass er irgendwo ausgegeben wird.<\/p>\n<p>Eine weitere wichtige Sache ist, dass Sie m\u00f6glicherweise das gesamte Bereitstellungsskript in einer einzigen Zeile ausf\u00fchren m\u00fcssen, zum Beispiel mit <code>doctl<\/code>. Das kann einige zus\u00e4tzliche Anstrengungen erfordern.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh &lt;droplet&gt; --ssh-command &quot;\u0432\u0441\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u0437\u0434\u0435\u0441\u044c &amp;&amp; \u0437\u0434\u0435\u0441\u044c&quot;<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL und Lastenausgleich<\/h2>\n<p>\nNachdem ich alles gemacht hatte, was oben erw\u00e4hnt wurde, war das letzte Problem, das sich mir stellte, dass der Server kein SSL hatte. Da ich einen Node.js-Server benutze, um <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose\">zu arbeiten<\/a><\/noindex> ben\u00f6tigt man einige Anstrengungen, um den Nginx-Reverse-Proxy und Let\u2019s Encrypt zum Laufen zu bringen.<\/p>\n<p>Ich hatte wirklich keine Lust, all diese SSL-Einstellungen manuell vorzunehmen, also habe ich einfach einen Lastenausgleich erstellt und die Informationen dazu in DNS eingetragen. Bei DigitalOcean beispielsweise ist das Erstellen eines automatisch aktualisierten selbstsignierten Zertifikats auf dem Lastenausgleich eine einfache, kostenlose und schnelle Aufgabe. Ein weiterer Vorteil dieses Ansatzes ist, dass es sehr einfach ist, SSL f\u00fcr mehrere Server, die hinter dem Lastenausgleich arbeiten, einzurichten. Dadurch m\u00fcssen sich die Server \u00fcberhaupt nicht um SSL k\u00fcmmern und k\u00f6nnen weiterhin wie gewohnt \u00fcber den Port <code>80<\/code>arbeiten. Die Einrichtung von SSL auf dem Lastenausgleich ist also viel einfacher und bequemer als alternative Methoden zur SSL-Konfiguration.<\/p>\n<p>Jetzt k\u00f6nnen Sie auf dem Server alle Ports, die eingehende Verbindungen akzeptieren, schlie\u00dfen \u2013 au\u00dfer dem Port <code>80<\/code>, der f\u00fcr die Kommunikation mit dem Lastenausgleich verwendet wird, und dem Port <code>22<\/code> f\u00fcr SSH. Daher wird jeder Versuch, den Server \u00fcber andere Ports als diese beiden zu erreichen, scheitern.<\/p>\n<h2>Ergebnisse<\/h2>\n<p>\nNachdem ich alles umgesetzt habe, was ich in diesem Artikel beschrieben habe, hat mich weder die Plattform Docker noch die Konzepte automatisierter CI\/CD-Pipelines eingesch\u00fcchtert. Ich konnte eine Continuous Integration-Pipeline einrichten, in deren Verlauf der Code getestet wird, bevor er in die Produktion gelangt, sowie eine automatische Bereitstellung des Codes auf dem Server. Das ist f\u00fcr mich alles noch relativ neu, und ich bin mir sicher, dass es M\u00f6glichkeiten gibt, meinen automatisierten Workflow zu verbessern und effizienter zu gestalten. Wenn Sie also Ideen dazu haben \u2013 lassen Sie es mich wissen. <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">Ich<\/a><\/noindex> hoffe, dieser Artikel hat Ihnen bei Ihren Vorhaben geholfen. Ich m\u00f6chte glauben, dass Sie beim Lesen ebenso viel gelernt haben, wie ich, w\u00e4hrend ich mich mit den Themen auseinandersetze, die darin behandelt werden.<\/p>\n<p><b>P.S.<\/b> In unserem <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">Marktplatz<\/a><\/noindex> gibt es ein Image <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, das mit einem Klick installiert wird. Sie k\u00f6nnen die Funktionalit\u00e4t der Container auf <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>\u00fcberpr\u00fcfen. Allen neuen Kunden stehen 3 Tage kostenlos zur Verf\u00fcgung, um zu testen.<\/p>\n<p><b>Sehr geehrte Leser!<\/b> Nutzen Sie CI\/CD-Technologien in Ihren Projekten?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Erstellung einer CI\/CD-Pipeline und Automatisierung der Arbeit mit Docker\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":69352,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-69351","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-02-18T11:44:06+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-03T13:15:00+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Erstellung einer CI\/CD-Pipeline und Automatisierung der Arbeit mit Docker | ProHoster","description":"Ich habe meine ersten Webseiten Ende der 90er Jahre erstellt. Damals war es ganz einfach, sie zum Laufen zu bringen. Es gab einen Apache-Server auf einem Shared Hosting, auf den man per FTP zugreifen konnte, indem man in die Adresszeile des Browsers etwas wie ftp:\/\/ftp.example.com eingab. Dann musste man seinen Benutzernamen und sein Passwort eingeben und die Dateien auf den Server hochladen. Es waren andere Zeiten, alles war damals viel einfacher.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster","og:description":"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-02-18T11:44:06+00:00","article:modified_time":"2020-03-03T13:15:00+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"69351","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:24:45","updated":"2022-09-28 11:40:45"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/69351","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}