{"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 von Arbeiten mit Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ich habe meine ersten Webseiten Ende der 90er Jahre erstellt. Damals war es sehr einfach, sie in einen funktionsf\u00e4higen Zustand zu versetzen. Es gab einen Apache-Server auf einem beliebigen Shared Hosting, auf diesen Server konnte man per FTP zugreifen, indem man in die Adresszeile des Browsers etwas wie <code>ftp:\/\/ftp.example.com<\/code>eingab. Dann musste man den Benutzernamen und das Passwort eingeben und die Dateien auf den Server hochladen. Es waren andere Zeiten, damals war alles 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 von Arbeiten 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, die seitdem vergangen sind, hat sich jedoch viel ver\u00e4ndert. Die Webseiten sind komplexer geworden, sie m\u00fcssen vor der Ver\u00f6ffentlichung in die Produktion zusammengebaut werden. Ein einziger Server ist zu vielen Servern geworden, die hinter Lastenausgleichern 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 brauchte, die Website in der Produktion zu betreiben, indem ich nur eine Aktion ausf\u00fchre: den Code in den Branch <code>master<\/code> auf GitHub zu schreiben. Zudem wusste ich, dass ich mich um die Verwaltung eines riesigen Kubernetes-Clusters oder die Nutzung der Docker Swarm-Technologie oder die Betreuung eines Serverparks mit Pods, Agenten und anderen Komplexit\u00e4ten nicht k\u00fcmmern wollte, um mein kleines Webanwendungsprojekt am Laufen zu halten. Um mein Ziel, die Arbeit maximal zu vereinfachen, zu erreichen, musste ich mich mit CI\/CD vertraut machen.<\/p>\n<p>Wenn Sie ein kleines Projekt haben (in unserem Fall handelt es sich um ein Node.js-Projekt) und wissen m\u00f6chten, wie Sie die Bereitstellung dieses Projekts automatisieren k\u00f6nnen, sodass das, was im Repository gespeichert ist, genau dem entspricht, was in der Produktion l\u00e4uft, dann 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 <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 uneingeschr\u00e4nkt als \"Leitfaden\" bezeichnet werden kann. Es ist eher ein Dokument, in dem ich teile, was ich gelernt habe, und den Prozess beschreibe, der mir beim Testen und Bereitstellen von Code in der Produktion gef\u00e4llt, und zwar \u00fcber einen einzigen automatisierten Durchlauf.<\/p>\n<p>So sieht letztendlich mein Arbeitsprozess aus.<\/p>\n<p>F\u00fcr Code, der in einen beliebigen Branch des Repositories geschickt wird, au\u00dfer <code>master<\/code>, werden folgende Schritte durchgef\u00fchrt:<\/p>\n<ul>\n<li>Ein Build des Projekts wird auf Travis CI gestartet.<\/li>\n<li>Es werden alle Unit-, Integrations- und End-to-End-Tests durchgef\u00fchrt.<\/li>\n<\/ul>\n<p>\nNur f\u00fcr den Code, der in <code>master<\/code>, erfolgt Folgendes:<\/p>\n<ul>\n<li>Alles, was oben gesagt wurde, plus...<\/li>\n<li>Erstellung eines Docker-Images basierend auf dem aktuellen Code, den Einstellungen und der Umgebung.<\/li>\n<li>Bereitstellung des Images auf Docker Hub.<\/li>\n<li>Verbindung zum Produktionsserver.<\/li>\n<li>Herunterladen des Images von Docker Hub auf den Server.<\/li>\n<li>Anhalten des aktuellen Containers und Start eines neuen, basierend auf dem neuen Image.<\/li>\n<\/ul>\n<p>\nWenn Sie absolut nichts \u00fcber Docker, \u00fcber Images und Container wissen \u2013 keine Sorge. 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 \u201ekontinuierliche Integration\/kontinuierliches Deployment\u201c.<\/p>\n<h3>\u258dKontinuierliche Integration<\/h3>\n<p>\nKontinuierliche Integration ist der Prozess, bei dem Entwickler Commits ins Hauptrepository des Projekts (gew\u00f6hnlich in den Branch <code>master<\/code>). Dabei wird die Qualit\u00e4t des Codes durch automatisierte Tests sichergestellt.<\/p>\n<h3>\u258dKontinuierliches Deployment<\/h3>\n<p>\nKontinuierliches Deployment ist die h\u00e4ufige automatisierte Bereitstellung von Code in der Produktion. Der zweite Teil der Abk\u00fcrzung CI\/CD wird manchmal als \u201eContinuous Delivery\u201c (\u201ekontinuierliche Lieferung\u201c) erkl\u00e4rt. Das ist im Grunde dasselbe wie \u201ekontinuierliches Deployment\u201c, jedoch impliziert \u201ekontinuierliche Lieferung\u201c die Notwendigkeit einer manuellen Best\u00e4tigung von \u00c4nderungen vor dem Start des Deployment-Prozesses des Projekts.<\/p>\n<h2>Erste Schritte<\/h2>\n<p>\nDie Anwendung, mit der ich das alles gelernt habe, hei\u00dft <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Es handelt sich um ein Webprojekt, an dem ich arbeite, das dazu dient, Notizen zu machen. Zuerst versuchte ich, ein <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">JAMStack<\/a><\/noindex>-Projekt oder nur eine Frontend-Anwendung ohne Server zu erstellen, um die standardm\u00e4\u00dfigen Hosting- und Deploymentm\u00f6glichkeiten zu nutzen, die <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>anbietet. Im Laufe des Wachstums der Komplexit\u00e4t der Anwendung musste ich auch einen Server-Teil 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 stellt die Anwendung einen Express-Server dar, der in der Node.js-Umgebung l\u00e4uft, eine Single-Page-React-Anwendung bedient und eine gesicherte 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 f\u00fcr Full-Stack-Authentifizierung zu finden ist.<\/p>\n<p>Ich habe mich mit einem <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">Freund beraten<\/a><\/noindex>, der Experte f\u00fcr Automatisierung ist, und fragte ihn, was ich tun muss, damit all das so funktioniert, wie ich es brauche. Er gab mir die Idee, wie der automatisierte Arbeitsablauf aussehen sollte, der im Abschnitt \u201eZiele\u201c dieses Artikels dargelegt ist. Dass ich mir solche Ziele gesetzt habe, bedeutete, dass ich verstehen musste, wie man Docker benutzt.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker ist ein Tool, das durch Containerisierungstechnologie das einfache Verbreiten von Anwendungen sowie deren Bereitstellung und Ausf\u00fchrung in derselben Umgebung erm\u00f6glicht, selbst wenn die Docker-Plattform in verschiedenen Umgebungen l\u00e4uft. Zuerst musste ich die Docker-Befehlszeilenwerkzeuge (CLI) zur Verf\u00fcgung haben. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">Die Anleitung<\/a><\/noindex> zur Installation von Docker kann nicht als sehr klar und verst\u00e4ndlich bezeichnet werden, aber man kann erfahren, 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 etwa 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 ist ein Online-Repository f\u00fcr Docker-Images. Es ist die Verbindung, mit der Docker Desktop arbeitet.<\/p>\n<p>Um mit Docker zu arbeiten, m\u00fcssen also zwei Dinge erledigt werden:<\/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>\nDanach k\u00f6nnen Sie die Funktionsf\u00e4higkeit der Docker CLI \u00fcberpr\u00fcfen, indem Sie den folgenden Befehl zur Versions\u00fcberpr\u00fcfung von Docker eingeben:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nGeben Sie dann bei der Aufforderung Ihren Benutzernamen und Ihr Passwort f\u00fcr Docker Hub ein:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nUm Docker zu verwenden, m\u00fcssen Sie die Konzepte von Images und Containern verstehen.<\/p>\n<h3>\u258dImages<\/h3>\n<p>\nEin Image ist eine Art Plan, der Anweisungen zum Erstellen eines Containers enth\u00e4lt. Es ist ein unver\u00e4nderlicher Schnappschuss des Dateisystems und der Anwendungseinstellungen. Entwickler k\u00f6nnen Images einfach 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 der folgenden \u00dcberschrift aus:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nAnschlie\u00dfend werden wir einige Beispiele f\u00fcr Befehle im gleichen Format betrachten \u2013 zuerst kommt der Befehl mit einem Kommentar und dann ein Beispiel daf\u00fcr, 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. Bei diesem Ansatz wird die Anwendung immer gleich funktionieren, unabh\u00e4ngig von der Infrastruktur: in einer isolierten Umgebung und in derselben Umgebung. Es geht darum, dass in unterschiedlichen Umgebungen Instanzen desselben Images ausgef\u00fchrt 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 des 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>Team<\/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>Image aus Dockerfile erstellen<\/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>Image taggen<\/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>Ausgabe der Liste der Images<\/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>Container basierend auf einem Image 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 ins Repository \u00fcbertragen<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Image<\/p>\n<p>Image aus dem Repository 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>Ausgabe der Liste der Container<\/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>Unbenutzte Container und Images entfernen<\/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 zum Erstellen einer fertigen React-Anwendung gedacht ist. Au\u00dferdem habe ich einen Befehl, der einen auf Node.js basierenden Server auf Port <code>5000<\/code>startet. So sieht das aus:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # Abh\u00e4ngigkeiten installieren\nnpm run build # React-Anwendung bauen\nnpm run start # Node-Server starten<\/code><\/pre>\n<p>\nEs ist anzumerken, dass ich keine Beispielanwendung f\u00fcr dieses Material habe. Aber hier eignet sich jede einfache Node-Anwendung f\u00fcr Experimente.<\/p>\n<p>Um den Container zu nutzen, m\u00fcssen Sie Docker Anweisungen geben. Dies geschieht \u00fcber eine Datei, die nennt sich <code>Dockerfile<\/code>, die sich im Stammverzeichnis des Projekts befindet. Diese Datei scheint zun\u00e4chst recht komplex.<\/p>\n<p>Aber der Inhalt beschreibt lediglich, mit speziellen Befehlen, etwas \u00c4hnliches wie die Konfiguration einer 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. Er gibt das Basis-Image an, auf dem 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 Ausf\u00fchren von Befehlen.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Einrichten des Ports.<\/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 folgenderma\u00dfen 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. Das liegt daran, dass einige Basis-Images (wie Node Alpine Linux) so konzipiert sind, dass sie so kompakt wie m\u00f6glich sind. Infolgedessen k\u00f6nnen sie einige Programme fehlen, auf die Sie angewiesen sind.<\/p>\n<h3>\u258dBauen, Taggen und Starten des Containers<\/h3>\n<p>\nDer lokale Aufbau und das Starten des Containers ist, nachdem wir haben, <code>Dockerfile<\/code>, ziemlich einfach. Bevor das Image auf Docker Hub hochgeladen wird, muss es lokal getestet werden.<\/p>\n<h3>\u258dBauen<\/h3>\n<p>\nZuerst muss gebaut werden, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">Image<\/a><\/noindex>, indem der Name angegeben wird, und, was optional ist, ein Tag (wenn kein Tag angegeben wird, wird das System dem Image automatisch einen Tag zuweisen. <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>\nNach Ausf\u00fchrung dieses Befehls kann man beobachten, wie Docker das Image baut.<\/p>\n<pre><code class=\"plaintext\">Den Build-Kontext an das Docker-Daemon senden \u00a0 2.88MB\nSchritt 1\/9 : VON node:12-alpine\n\u00a0---&gt; ...Bauphasen werden ausgef\u00fchrt...\nErfolgreich gebaut 123456789123\nErfolgreich getaggt :<\/code><\/pre>\n<p>\nDer Build kann einige Minuten dauern \u2013 das h\u00e4ngt davon ab, wie viele Abh\u00e4ngigkeiten Sie haben. Nach Abschluss des Builds kann man den Befehl ausf\u00fchren, <code>docker images<\/code> und sich die Beschreibung seines neuen Images ansehen.<\/p>\n<pre><code class=\"plaintext\">REPOSITORY\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TAG               IMAGE ID\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ERSTELLT              GR\u00d6SSE\n \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 Vor etwa einer Minute   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dStart<\/h3>\n<p>\nDas Image wurde erstellt. Das bedeutet, dass nun ein Container basierend auf diesem Image gestartet werden kann. Da ich m\u00f6chte, dass ich auf die Anwendung, die im Container l\u00e4uft, unter der Adresse zugreifen kann, <code>localhost:5000<\/code>, habe ich auf der linken Seite des Paares <code>5000:5000<\/code> in dem folgenden Befehl festgelegt. Auf der rechten Seite befindet sich der Port des Containers. <code>5000<\/code>.<\/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, da der Container erstellt und gestartet wurde, kann man den Befehl verwenden, <code>docker ps<\/code> um die Informationen zu diesem Container anzusehen (oder man kann den Befehl <code>docker ps -a<\/code>, verwenden, der Informationen \u00fcber alle Container anzeigt, nicht nur \u00fcber die laufenden).<\/p>\n<pre><code class=\"plaintext\">CONTAINER ID        IMAGE               COMMAND                   CREATED              STATUS                   PORTS                    NAMES\n987654321234                     \"\/bin\/sh -c 'npm run\u2026\"   vor 6 Sekunden        L\u00e4uft seit 6 Sekunden      0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nWenn Sie jetzt die Adresse aufrufen, <code>localhost:5000<\/code> \u2013 k\u00f6nnen Sie die Seite der laufenden Anwendung sehen, die genauso aussieht wie die Seite der Anwendung, die in der Produktionsumgebung l\u00e4uft.<\/p>\n<h3>\u258dTag zuweisen und ver\u00f6ffentlichen<\/h3>\n<p>\nUm eines der erstellten Images auf dem Produktionsserver zu verwenden, m\u00fcssen wir die M\u00f6glichkeit haben, dieses Image von Docker Hub herunterzuladen. Das bedeutet, dass wir zun\u00e4chst ein Repository f\u00fcr das Projekt auf Docker Hub erstellen m\u00fcssen. Danach steht uns ein Speicherplatz zur Verf\u00fcgung, an den wir das Image senden k\u00f6nnen. Das Image muss so umbenannt werden, dass sein Name mit unserem Benutzernamen auf Docker Hub beginnt. Danach folgt der Name des Repositories. Am Ende des Namens kann ein beliebiges Tag stehen. Im Folgenden wird ein Beispiel f\u00fcr die Benennung von Images gem\u00e4\u00df diesem Schema gezeigt.<\/p>\n<p>Jetzt k\u00f6nnen wir das Image unter einem neuen Namen erstellen und den Befehl ausf\u00fchren <code>docker push<\/code> um es in das Docker Hub-Repository 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 l\u00e4uft, wird das Image auf Docker Hub verf\u00fcgbar sein und kann einfach auf den Server geladen oder an andere Entwickler weitergegeben werden.<\/p>\n<h2>N\u00e4chste Schritte<\/h2>\n<p>\nBis jetzt haben wir sichergestellt, dass die Anwendung als Docker-Container lokal funktioniert. Wir haben den Container auf Docker Hub hochgeladen. All das bedeutet, dass wir bereits recht gut auf unser Ziel hingearbeitet haben. Nun m\u00fcssen wir noch zwei Fragen kl\u00e4ren:<\/p>\n<ul>\n<li>Einrichtung des CI-Tools zum Testen und Bereitstellen des Codes.<\/li>\n<li>Einrichtung des Produktionsservers, damit er unseren Code herunterladen 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 \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Es ist erw\u00e4hnenswert, dass hier auch eine andere Kombination von Diensten genutzt werden kann. Zum Beispiel kann anstelle von Travis CI auch CircleCI oder Github Actions verwendet werden. Und anstelle von DigitalOcean sind AWS oder Linode m\u00f6glich.<\/p>\n<p>Wir haben uns entschieden, mit Travis CI zu arbeiten, und in diesem Dienst ist bereits einiges f\u00fcr mich eingerichtet. Deshalb werde ich jetzt kurz erkl\u00e4ren, wie man ihn f\u00fcr die Arbeit vorbereitet.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI ist ein Tool zum Testen und Bereitstellen von Code. Ich m\u00f6chte nicht ins Detail gehen, wie man Travis CI einrichtet, da jedes Projekt einzigartig ist und dies nicht viel n\u00fctzen w\u00fcrde. Aber ich werde die Grundlagen vorstellen, die Ihnen den Einstieg erleichtern, falls Sie sich entscheiden, Travis CI zu verwenden. Egal, ob Sie Travis CI, CircleCI, Jenkins oder etwas anderes w\u00e4hlen, die grundlegenden Methoden zur Einrichtung sind \u00e4hnlich.<\/p>\n<p>Um mit Travis CI zu arbeiten, gehen Sie zu <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">die Projektwebsite<\/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, das Sie automatisieren m\u00f6chten, und den Zugriff darauf aktivieren. (Ich verwende GitHub, bin mir aber sicher, dass Travis CI auch mit BitBucket, GitLab und anderen \u00e4hnlichen Diensten integriert werden kann).<\/p>\n<p>Jedes Mal, wenn Travis CI beginnt zu arbeiten, startet es einen Server, der die im Konfigurationsdatei angegebenen Befehle ausf\u00fchrt, einschlie\u00dflich des Deployments der entsprechenden Branches des Repositories.<\/p>\n<h3>\u258d Lebenszyklus eines Jobs<\/h3>\n<p>\nDie Konfigurationsdatei von Travis CI, die genannt wird <code>.travis.yml<\/code> und sich im Wurzelverzeichnis des Projekts befindet, unterst\u00fctzt das Konzept von Ereignissen <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">des Lebenszyklus<\/a><\/noindex> des Jobs. Hier sind diese Ereignisse in der Reihenfolge, in der sie stattfinden:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>cache components<\/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>\u258d Testen<\/h3>\n<p>\nIn der Konfigurationsdatei werde ich einen lokalen Server von Travis CI einrichten. Als Sprache habe ich Node Version 12 gew\u00e4hlt und der Software mitgeteilt, dass sie die f\u00fcr die Verwendung von Docker notwendigen Abh\u00e4ngigkeiten installieren soll.<\/p>\n<p>Alles, was in <code>.travis.yml<\/code>, aufgef\u00fchrt ist, wird bei der Ausf\u00fchrung aller Pull-Requests f\u00fcr alle Branches des Repositories ausgef\u00fchrt, es sei denn, es wird etwas anderes angegeben. Dies ist eine n\u00fctzliche Funktion, da sie bedeutet, dass wir den gesamten Code testen k\u00f6nnen, der in das Repository eingeht. So k\u00f6nnen wir feststellen, ob der Code bereit ist, in den Branch <code>master<\/code>, geschrieben zu werden, und ob er den Build-Prozess des Projekts nicht st\u00f6rt. In dieser globalen Konfiguration installiere ich alles lokal, starte den Webpack-Entwicklungsserver im Hintergrund (das ist Teil meines Workflows) und f\u00fchre die Tests aus.<\/p>\n<p>Wenn Sie m\u00f6chten, dass in Ihrem Repository Badges mit Informationen zur Testabdeckung angezeigt werden, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">hier<\/a><\/noindex> k\u00f6nnen Sie eine kurze Anleitung zur Verwendung von Jest, Travis CI und Coveralls finden, um diese Informationen zu sammeln und anzuzeigen.<\/p>\n<p>Hier ist also 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 Aktionen, die f\u00fcr alle Branches des Repositories und f\u00fcr Pull-Requests ausgef\u00fchrt werden.<\/p>\n<h3>\u258d Deployment<\/h3>\n<p>\nVorausgesetzt, 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 m\u00f6chten <code>master<\/code>, wir geben dem System entsprechende Anweisungen in den Bereitstellungseinstellungen. Bevor Sie versuchen, den Code, den wir im Folgenden besprechen werden, in Ihrem Projekt zu verwenden, m\u00f6chte ich Sie darauf hinweisen, dass Sie ein echtes Skript haben sollten, das f\u00fcr die Bereitstellung aufgerufen wird.<\/p>\n<pre><code class=\"plaintext\">deploy:\n  # Docker-Container erstellen 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>Erstellung, Tagging und Senden des Images auf Docker Hub mit Hilfe von CI-Tools (in unserem Fall Travis CI).<\/li>\n<li>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>\nZun\u00e4chst muss der automatische Prozess zum Erstellen, Taggen und Senden des Images an Docker Hub eingerichtet werden. Das Ganze \u00e4hnelt sehr dem, was wir bereits manuell gemacht haben, mit der Ausnahme, dass wir hier eine Strategie zur Zuweisung einzigartiger Tags f\u00fcr die Images und zur Automatisierung der Anmeldung ben\u00f6tigen. Ich hatte Schwierigkeiten mit einigen Details des Bereitstellungsskripts, wie z.B. der Tagging-Strategie, der Anmeldung, der Kodierung der SSH-Schl\u00fcssel und der Einrichtung der SSH-Verbindung. Gl\u00fccklicherweise 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>Also, der erste Teil des Skripts besteht darin, das Image zu Docker Hub zu senden. Das ist ziemlich einfach. Das von mir verwendete Tagging-Schema sieht vor, dass der Git-Hash und das Git-Tag, falls vorhanden, kombiniert werden. Dies sorgt daf\u00fcr, dass ein einzigartiges Tag erstellt wird und erleichtert die Identifizierung des Builds, auf dem es basiert. <code>DOCKER_USERNAME<\/code> und <code>DOCKER_PASSWORD<\/code> \u2014 dies sind benutzerdefinierte Umgebungsvariablen, die \u00fcber die Benutzeroberfl\u00e4che von Travis CI festgelegt werden k\u00f6nnen. Travis CI behandelt geheime Daten automatisch so, dass 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>\nWie der zweite Teil des Skripts aussehen wird, h\u00e4ngt vollst\u00e4ndig davon ab, welchen Host Sie verwenden und wie die Verbindung zu ihm eingerichtet ist. In meinem Fall, da ich Digital Ocean nutze, werden die Befehle zum Verbinden mit dem Server verwendet <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. Bei der Verwendung von AWS wird das Tool <code>aws<\/code>, und so weiter, verwendet.<\/p>\n<p>Es war nicht besonders schwierig, die Serverarbeit einzurichten. So habe ich einen Droplet auf Basis eines Basis-Images eingerichtet. Ich sollte anmerken, dass das von mir gew\u00e4hlte System eine einmalige manuelle Installation von Docker und einen einmaligen manuellen Start von Docker erfordert. Ich habe f\u00fcr die Docker-Installation Ubuntu 18.04 verwendet, daher k\u00f6nnen Sie, wenn Sie ebenfalls Ubuntu benutzen, einfach folgen <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">diesem<\/a><\/noindex> einfachen Leitfaden.<\/p>\n<p>Ich spreche hier nicht \u00fcber spezifische Befehle f\u00fcr den Dienst, da dieser Aspekt in verschiedenen F\u00e4llen stark variieren kann. Ich werde nur einen allgemeinen Aktionsplan angeben, der nach der Verbindung per SSH mit dem Server, auf dem das Projekt bereitgestellt wird, durchgef\u00fchrt wird:<\/p>\n<ul>\n<li>Zuerst m\u00fcssen Sie den Container finden, der derzeit l\u00e4uft, und ihn stoppen.<\/li>\n<li>Dann m\u00fcssen Sie im Hintergrund einen neuen Container starten.<\/li>\n<li>Sie m\u00fcssen den lokalen Serverport auf den Wert einstellen <code>80<\/code> \u2013 das erm\u00f6glicht den Zugriff auf die 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 Sie alle alten Container und Images l\u00f6schen.<\/li>\n<\/ul>\n<p>\nHier ist die Fortsetzung des Scripts.<\/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 Punkte, auf die man achten sollte<\/h2>\n<p>\nM\u00f6glicherweise sehen Sie, wenn Sie sich per SSH mit dem Server aus Travis CI verbinden, eine Warnung, die die Installation fortschreiten l\u00e4sst, da das System auf eine Benutzereingabe wartet.<\/p>\n<pre><code class=\"plaintext\">Die Echtheit des Hosts ' ()' kann nicht hergestellt werden.\nDer RSA-Schl\u00fcsselfingerabdruck ist .\nM\u00f6chten Sie die Verbindung fortsetzen (ja\/nein)?<\/code><\/pre>\n<p>\nIch habe erfahren, dass der Zeichenfolgeschl\u00fcssel in base64 kodiert werden kann, um ihn in einem Format zu speichern, mit dem man bequem und sicher arbeiten kann. W\u00e4hrend der Installation kann der \u00f6ffentliche Schl\u00fcssel dekodiert und in die Datei <code>known_hosts<\/code> geschrieben werden, um die oben beschriebene Fehlermeldung zu beseitigen.<\/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 \"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\" | base64<\/code><\/pre>\n<p>\nUnd so sieht die Ausgabe aus \u2014 eine Zeichenfolge in base64-Kodierung:<\/p>\n<pre><code class=\"plaintext\">MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vwa0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==<\/code><\/pre>\n<p>\nHier ist das oben erw\u00e4hnte Team<\/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 beim Herstellen einer Verbindung verwendet werden, da Sie m\u00f6glicherweise einen privaten Schl\u00fcssel ben\u00f6tigen, um auf den Server zuzugreifen. Bei der Arbeit mit dem Schl\u00fcssel m\u00fcssen Sie lediglich sicherstellen, dass er sicher in einer Umgebungsvariable von Travis CI gespeichert wird und nicht irgendwo ausgegeben wird.<\/p>\n<p>Ein weiterer Punkt, den man beachten sollte, ist, dass es n\u00f6tig sein kann, das gesamte Bereitstellungsskript in einer einzigen Zeile auszuf\u00fchren, zum Beispiel mit Hilfe von <code>doctl<\/code>. Das kann einige zus\u00e4tzliche Anstrengungen erfordern.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"alle Befehle werden hier &amp;&amp; hier\"<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL und Lastenverteilung<\/h2>\n<p>\nNachdem ich alles erledigt hatte, was oben besprochen wurde, besteht mein letztes Problem darin, 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 funktionieren<\/a><\/noindex> Nginx und Let\u2019s Encrypt Reverse Proxy zum Laufen zu bringen, ist ein gewisser Aufwand erforderlich.<\/p>\n<p>Ich wollte diese SSL-Einstellungen nicht manuell vornehmen, also habe ich einfach einen Lastenausgleich erstellt und die Informationen dazu im DNS gespeichert. Bei DigitalOcean zum Beispiel ist das Erstellen eines automatisch aktualisierten selbstsignierten Zertifikats auf dem Lastenausgleich eine einfache, kostenlose und schnelle Angelegenheit. Dieser Ansatz hat den zus\u00e4tzlichen Vorteil, dass er es erm\u00f6glicht, SSL sehr einfach auf vielen Servern zu konfigurieren, die hinter dem Lastenausgleich arbeiten. Das bedeutet, dass die Server selbst sich um SSL keine Gedanken machen m\u00fcssen, aber weiterhin wie gewohnt den Port <code>80<\/code>verwenden. Daher ist die Konfiguration von SSL auf dem Lastenausgleich viel einfacher und komfortabler als alternative Methoden zur SSL-Einrichtung.<\/p>\n<p>Jetzt k\u00f6nnen Sie auf dem Server alle Ports, die eingehende Verbindungen annehmen, schlie\u00dfen, mit Ausnahme des Ports <code>80<\/code>, der f\u00fcr die Kommunikation mit dem Lastenausgleich verwendet wird, und des Ports <code>22<\/code> f\u00fcr SSH. Infolgedessen wird der Versuch, direkt auf den Server \u00fcber beliebige Ports, mit Ausnahme dieser beiden, scheitern.<\/p>\n<h2>Ergebnisse<\/h2>\n<p>\nNachdem ich alles gemacht habe, was ich in diesem Material beschrieben habe, hatte ich keine Angst mehr vor der Docker-Plattform oder den Konzepten automatisierter CI\/CD-Pipelines. Ich konnte eine kontinuierliche Integrationspipeline einrichten, in der der Code getestet wird, bevor er in die Produktion gelangt, und die automatische Bereitstellung des Codes auf dem Server erfolgt. Das alles ist f\u00fcr mich noch relativ neu, und ich bin sicher, dass es M\u00f6glichkeiten gibt, meinen automatisierten Arbeitsablauf zu verbessern und ihn effizienter zu gestalten. Also, wenn Sie Ideen dazu haben, lassen Sie es mich wissen. <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">mir<\/a><\/noindex> Ich hoffe, dieser Artikel hat Ihnen in Ihren Belangen geholfen. Ich m\u00f6chte glauben, dass Sie beim Lesen genauso viel gelernt haben, wie ich, als ich alles durchging, was ich darin beschrieben habe.<\/p>\n<p><b>P.S.<\/b> wird es gew\u00f6hnliche Befehle enthalten. <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 zur kostenlosen Testung zur Verf\u00fcgung.<\/p>\n<p><b>Liebe 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 von Arbeiten 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 5.0.1.1 - 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.\" \/>\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) 5.0.1.1\" \/>\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.\" \/>\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 sehr einfach, sie zum Laufen zu bringen.","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.","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":{"focus":[],"additional":[]},"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":"2026-08-11 12:50:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"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}]}}