Erstellung einer CI/CD-Pipeline und Automatisierung der Arbeit mit Docker

Ich habe meine ersten Websites Ende der 90er Jahre erstellt. Damals war es sehr einfach, sie funktionsfähig 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 ftp://ftp.example.com. 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.

Erstellung einer CI/CD-Pipeline und Automatisierung der Arbeit mit Docker

In den zwei Jahrzehnten seitdem hat sich viel verändert. Websites sind komplexer geworden, sie müssen 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.

Für mein persönliches Projekt hatte ich eine spezielle Konfiguration. Und ich wusste, dass ich die Möglichkeit benötige, die Website in der Produktion mit nur einem einzigen Schritt zu deployen: Code in einen Branch zu schreiben. master auf GitHub. Zudem wusste ich, dass ich für den Betrieb meiner kleinen Webanwendung nicht das Management eines riesigen Kubernetes-Clusters übernehmen, 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.

Wenn Sie ein kleines Projekt haben (in diesem Fall handelt es sich um ein Node.js-Projekt) und erfahren möchten, wie Sie das Deployment dieses Projekts automatisieren können, sodass das, was im Repository gespeichert ist, genau dem entspricht, was in der Produktion läuft, könnte Sie dieser Artikel interessieren.

Voraussetzungen

Es wird erwartet, dass der Leser dieses Artikels grundlegende Kenntnisse im Umgang mit der Kommandozeile und im Schreiben von Bash-Skripten hat. Darüber hinaus benötigt er Konten bei Travis CI und Docker Hub.

Ziele

Ich würde nicht sagen, dass dieser Artikel bedingungslos als „Leitfaden“ bezeichnet werden kann. Es handelt sich eher um ein Dokument, in dem ich teile, was ich gelernt habe, und den Prozess beschreibe, der für mich beim Testen und der Bereitstellung von Code in der Produktion bei einem automatisierten Durchlauf zufriedenstellend ist.

So sieht mein Arbeitsprozess letztendlich aus.

Für Code, der in einen beliebigen anderen Branch des Repositories gesendet wird, außer master, werden folgende Aktionen durchgeführt:

  • Der Projekt-Build wird auf Travis CI gestartet.
  • Alle Modul-, Integrations- und End-to-End-Tests werden durchgeführt.

Nur für Code, der in master, wird Folgendes ausgeführt:

  • Alles, was oben erwähnt wurde, plus…
  • Der Docker-Image wird basierend auf dem aktuellen Code, den Einstellungen und der Umgebung erstellt.
  • Das Image wird auf Docker Hub bereitgestellt.
  • Verbindung zum Produktionsserver wird hergestellt.
  • Image von Docker Hub auf den Server herunterladen.
  • Den aktuellen Container anhalten und einen neuen starten, der auf dem neuen Image basiert.

Wenn Sie absolut nichts über Docker, Images und Container wissen, machen Sie sich keine Sorgen. Ich werde Ihnen alles darüber erzählen.

Was ist CI/CD?

Die Abkürzung CI/CD steht für „Continuous Integration/Continuous Deployment“ – also „kontinuierliche Integration/kontinuierliches Deployment“.

▍Kontinuierliche Integration

Kontinuierliche Integration ist der Prozess, bei dem Entwickler Änderungen in das Hauptrepository des Projekts einpflegen (in der Regel in den Branch master). Die Qualität des Codes wird durch automatisierte Tests sichergestellt.

▍Kontinuierliches Deployment

Kontinuierliches Deployment bezeichnet das häufige, automatisierte Bereitstellen von Code in der Produktionsumgebung. Der zweite Teil der Abkürzung CI/CD wird manchmal als „Continuous Delivery“ („kontinuierliche Lieferung“) interpretiert. Dies ist im Wesentlichen dasselbe wie „kontinuierliches Deployment“, jedoch setzt „kontinuierliche Lieferung“ voraus, dass Änderungen manuell genehmigt werden, bevor der Deployment-Prozess für das Projekt gestartet wird.

Erste Schritte

Die Anwendung, mit der ich all dies erlernt habe, heißt TakeNote. Dies ist ein Webprojekt, an dem ich arbeite und das dazu dient, Notizen zu machen. Zunächst habe ich versucht, JAMStack-Projekt oder nur eine Frontend-Anwendung ohne Server, um die Standardmöglichkeiten für Hosting und Deployment zu nutzen, die angeboten werden von Netlify. Mit wachsender Komplexität der Anwendung musste ich auch einen Server erstellen, was bedeutete, dass ich eine eigene Strategie für die automatisierte Integration und das automatisierte Deployment des Projekts entwickeln musste.

In meinem Fall handelt es sich bei der Anwendung um einen Express-Server, der in der Node.js-Umgebung läuft, ein einseitiges React-Anwendungs-Frontend bedient und eine sichere serverseitige API unterstützt. Diese Architektur folgt einer Strategie, die in diesem Leitfaden zur Full-Stack-Authentifizierung zu finden ist.

Ich habe mit einem Freund gesprochen, der Experte für Automatisierung war, und fragte ihn, was ich tun müsse, damit alles so funktioniert, wie ich es benötige. Er gab mir eine Idee, wie der automatisierte Workflow aussehen sollte, der im Abschnitt „Ziele“ dieses Artikels dargestellt ist. Dass ich mir solche Ziele gesetzt habe, bedeutete, dass ich lernen musste, wie man Docker nutzt.

Docker

Docker ist ein Tool, das durch Containerisierungstechnologie das einfache Verteilen von Anwendungen ermöglicht und deren Bereitstellung sowie Ausführung in derselben Umgebung ermöglicht, selbst wenn die Docker-Plattform in unterschiedlichen Umgebungen arbeitet. Zunächst benötigte ich die Docker-CLI-Tools. Die Anleitung zur Installation von Docker ist nicht besonders klar und verständlich, aber sie informiert darüber, dass man, um den ersten Schritt zur Installation zu machen, Docker Desktop (für Mac oder Windows) herunterladen muss.

Docker Hub ist ungefähr das gleiche wie GitHub für Git-Repositories, oder ein Registry npm für JavaScript-Pakete. Es handelt sich um ein Online-Repository für Docker-Images. Genau damit verbindet sich Docker Desktop.

Um mit Docker zu beginnen, müssen Sie zwei Dinge tun:

Anschließend können Sie die Funktionsfähigkeit der Docker CLI überprüfen, indem Sie den folgenden Befehl zur Überprüfung der Docker-Version eingeben:

docker -v

Danach melden Sie sich bei Docker Hub an, indem Sie Ihren Benutzernamen und Ihr Passwort eingeben, wenn Sie dazu aufgefordert werden:

docker login

Um Docker zu nutzen, müssen Sie die Konzepte von Images und Containern verstehen.

▍Images

Ein Image ist eine Art Vorlage, die Anweisungen zum Erstellen eines Containers enthält. Es handelt sich um einen unveränderlichen Snapshot des Dateisystems und der Anwendungseinstellungen. Entwickler können Images problemlos austauschen.

# Вывод сведений обо всех образах
docker images

Dieser Befehl gibt eine Tabelle mit dem folgenden Header aus:

REPOSITORY     TAG     IMAGE ID     CREATED     SIZE
---

Als Nächstes werden wir einige Beispielbefehle im gleichen Format betrachten – zuerst der Befehl mit einem Kommentar, gefolgt von einem Beispiel, was er ausgeben kann.

▍Container

Ein Container ist ein ausführbares Paket, das alles enthält, was zur Ausführung einer Anwendung erforderlich ist. Mit diesem Ansatz funktioniert die Anwendung immer gleich, unabhängig von der Infrastruktur: in einer isolierten Umgebung und in der gleichen Umgebung. Es geht darum, dass in verschiedenen Umgebungen Instanzen desselben Images gestartet werden.

# Перечисление всех контейнеров
docker ps -a
CONTAINER ID     IMAGE     COMMAND     CREATED     STATUS     PORTS     NAMES
---

▍Tags

Ein Tag ist ein Verweis auf eine bestimmte Version eines Images.

▍Kurzübersicht über Docker-Befehle

Hier ist eine Übersicht über einige häufig verwendete Docker-Befehle.

Der Befehl

Kontext

Aktion

docker build

Image

Bauen eines Images aus einem Dockerfile

docker tag

Image

Tagging eines Images

docker images

Image

Liste der Images anzeigen

docker run

Container

Einen Container auf der Basis eines Images starten

docker push

Image

Image in ein Registry senden

docker pull

Image

Image aus der Registry herunterladen

docker ps

Container

Liste der Container anzeigen

docker system prune

Image/Container

Entfernen von nicht verwendeten Containern und Images

▍Dockerfile

Ich weiß, wie man eine Anwendung lokal für die Produktion startet. Ich habe eine Webpack-Konfiguration, die für den Build einer fertigen React-Anwendung gedacht ist. Außerdem habe ich einen Befehl, der einen auf Node.js basierenden Server auf dem Port 5000startet. Es sieht so aus:

npm i         # Abhängigkeiten installieren
npm run build # React-Anwendung erstellen
npm run start # Node-Server starten

Es ist wichtig zu erwähnen, dass ich keine Beispielanwendung für dieses Material habe. Aber für Experimente eignet sich jede einfache Node-Anwendung.

Um den Container zu nutzen, müssen Sie Docker-Anweisungen geben. Dies geschieht über eine Datei, die man nennt Dockerfile, die sich im Stammverzeichnis des Projekts befindet. Diese Datei scheint zunächst ziemlich unverständlich.

Was darin enthalten ist, beschreibt lediglich, mit speziellen Befehlen, eine Art von Konfiguration der Arbeitsumgebung. Hier sind einige dieser Befehle:

  • FROM — Dieser Befehl beginnt die Datei. Hier wird das Basis-Image angegeben, auf dessen Grundlage der Container aufgebaut wird.
  • COPY — Kopieren von Dateien aus einer lokalen Quelle in den Container.
  • WORKDIR — Festlegen des Arbeitsverzeichnisses für die folgenden Befehle.
  • RUN — Befehle ausführen.
  • EXPOSE — Port konfiguriert.
  • ENTRYPOINT — Angabe des auszuführenden Befehls.

Dockerfile kann ungefähr so aussehen:

# Загрузить базовый образ
FROM node:12-alpine

# Скопировать файлы из текущей директории в директорию app/
COPY . app/

# Использовать app/ в роли рабочей директории
WORKDIR app/

# Установить зависимости (команда npm ci похожа npm i, но используется для автоматизированных сборок)
RUN npm ci --only-production

# Собрать клиентское React-приложение для продакшна
RUN npm run build

# Прослушивать указанный порт
EXPOSE 5000

# Запустить Node-сервер
ENTRYPOINT npm run start

Je nach dem gewählten Basis-Image müssen möglicherweise zusätzliche Abhängigkeiten installiert werden. Einige Basis-Images (wie Node Alpine Linux) wurden entwickelt, um so kompakt wie möglich zu sein. Daher fehlen möglicherweise einige Programme, auf die Sie angewiesen sind.

▍Bauen, Taggen und Starten des Containers

Das lokale Bauen und Starten des Containers ist, nachdem wir das haben, Dockerfile, eine ziemlich einfache Aufgabe. Bevor Sie das Image auf Docker Hub senden, sollte es lokal getestet werden.

▍Bauen

Zuerst müssen wir das Image, benennen und optional ein Tag angeben (falls kein Tag angegeben wird, weist das System dem Image automatisch das Tag latest).

# Сборка образа
docker build -t <image>:<tag> .

zu). Nachdem dieser Befehl ausgeführt wurde, können Sie beobachten, wie Docker das Image erstellt.

Sending build context to Docker daemon   2.88MB
Step 1/9 : FROM node:12-alpine
 ---> ...führe Build-Schritte aus...
Erfolgreich gebaut 123456789123
Erfolgreich getaggt :

Das Bauen kann einige Minuten in Anspruch nehmen – es hängt alles davon ab, wie viele Abhängigkeiten Sie haben. Nach Abschluss des Builds können Sie den Befehl docker images ausführen und die Beschreibung Ihres neuen Images ansehen.

REPOSITORY          TAG               IMAGE ID            CREATED              SIZE
              latest            123456789123        Vor einer Minute        x.xxGB

▍Starten

Das Image wurde erstellt. Das bedeutet, dass ich auf dessen Basis einen Container starten kann. Da ich die Möglichkeit haben möchte, auf die Anwendung, die im Container läuft, über die Adresse localhost:5000, habe ich in der linken Teile der Pair 5000:5000 in dem nächsten Befehl festgelegt 5000. Auf der rechten Seite befindet sich der Container-Port.

# Запуск с использованием локального порта 5000 и порта контейнера 5000
docker run -p 5000:5000 <image>:<tag>

Jetzt, wo der Container erstellt und gestartet wurde, kann ich den Befehl docker ps verwenden, um Informationen über diesen Container anzuzeigen (oder ich kann den Befehl docker ps -a, der Informationen über alle Container und nicht nur über die laufenden ausgibt, verwenden).

CONTAINER ID        IMAGE               COMMAND                      CREATED              STATUS                   PORTS                    NAMES
987654321234                     "/bin/sh -c 'npm run…"   vor 6 Sekunden       Hoch 6 Sekunden           0.0.0.0:5000->5000/tcp   stoic_darwin

Wenn ich jetzt die Adresse localhost:5000 aufrufe, kann ich die Seite der laufenden Anwendung sehen, die genau so aussieht wie die Seite der Anwendung im Produktionsumfeld.

▍Tagging und Veröffentlichung

Um eines der erstellten Images auf dem Produktionsserver nutzen zu können, muss es möglich sein, dieses Image von Docker Hub herunterzuladen. Das bedeutet, dass zunächst ein Repository für das Projekt auf Docker Hub erstellt werden muss. Danach steht uns ein Speicherplatz zur Verfügung, 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ür die Benennung von Images nach diesem Schema.

Jetzt können Sie das Image mit einem neuen Namen erstellen und den Befehl ausführen docker push um es in das Repository auf Docker Hub zu senden.

docker build -t /: .
docker tag /: /:latest
docker push /:

# In der Praxis könnte das zum Beispiel so aussehen:
docker build -t user/app:v1.0.0 .
docker tag user/app:v1.0.0 user/app:latest
docker push user/app:v1.0.0

Wenn alles gut geht, wird das Image auf Docker Hub verfügbar sein und kann leicht auf den Server heruntergeladen oder an andere Entwickler weitergegeben werden.

Die nächsten Schritte

Wir 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ßen Schritt in Richtung Ziel gemacht haben. Jetzt müssen noch zwei Fragen geklärt werden:

  • Einrichtung des CI-Tools für das Testen und das Deployment des Codes.
  • Einrichtung des Produktionsservers, damit dieser unseren Code laden und ausführen kann.

In unserem Fall verwenden wir als CI/CD-Lösung Travis CI. Als Server dient DigitalOcean.

Es ist erwähnenswert, dass hier auch andere Kombinationen von Services genutzt werden können. Zum Beispiel kann man anstelle von Travis CI auch CircleCI oder GitHub Actions verwenden. Und anstelle von DigitalOcean könnten AWS oder Linode genutzt werden.

Wir haben uns entschieden, mit Travis CI zu arbeiten, und in diesem Service ist bereits einiges konfiguriert. Daher werde ich jetzt kurz erläutern, wie man ihn für den Betrieb vorbereitet.

Travis CI

Travis CI ist ein Tool zum Testen und Bereitstellen von Code. Ich möchte nicht zu sehr ins Detail der Konfiguration von Travis CI gehen, da jedes Projekt einzigartig ist und dies nicht besonders hilfreich wäre. Aber ich werde die Grundlagen erläutern, die Ihnen den Einstieg erleichtern, falls Sie sich entscheiden, Travis CI zu nutzen. Egal, ob Sie sich für Travis CI, CircleCI, Jenkins oder etwas anderes entscheiden, ähnliche Konfigurationsmethoden kommen überall zur Anwendung.

Um mit Travis CI zu beginnen, besuchen Sie die Projektseite und erstellen Sie ein Konto. In der Folge integrieren Sie Travis CI mit Ihrem GitHub-Konto. Während der Systemkonfiguration müssen Sie das Repository angeben, dessen Automatisierung Sie wünschen, und den Zugriff darauf aktivieren. (Ich nutze GitHub, bin mir aber sicher, dass Travis CI auch mit BitBucket, GitLab und ähnlichen Diensten integriert werden kann.)

Jedes Mal, wenn Travis CI in Betrieb genommen wird, startet ein Server, der die im Konfigurationsdatei angegebenen Befehle ausführt, einschließlich der Bereitstellung der entsprechenden Branches des Repositories.

▍Lebenszyklus eines Jobs

Die Konfigurationsdatei von Travis CI, genannt .travis.yml und im Stammverzeichnis des Projekts gespeichert, unterstützt das Ereigniskonzept des Lebenszyklus von Aufgaben. Hier die Ereignisse in der Reihenfolge, in der sie auftreten:

  • apt addons
  • Cache-Komponenten
  • before_install
  • install
  • before_script
  • script
  • before_cache
  • after_success oder after_failure
  • before_deploy
  • deploy
  • after_deploy
  • after_script

▍Testen

In der Konfigurationsdatei werde ich den lokalen Travis CI-Server einrichten. Als Sprache habe ich Node 12 gewählt und der Software mitgeteilt, dass die für die Verwendung von Docker erforderlichen Abhängigkeiten installiert werden müssen.

Alles, was in .travis.yml, aufgeführt ist, wird ausgeführt, wenn Pull-Requests in alle Zweige des Repositories durchgeführt werden, es sei denn, es wird etwas anderes angegeben. Dies ist eine nützliche Funktion, da sie bedeutet, dass wir den gesamten Code, der in das Repository eingeht, testen können. So wissen wir, ob der Code bereit ist, in den Branch geschrieben zu werden master, und ob er den Build-Prozess stören wird. In dieser globalen Konfiguration installiere ich alles lokal, starte den Webpack-Entwicklungsserver im Hintergrund (das ist ein Merkmal meines Arbeitsablaufs) und führe Tests durch.

Wenn Sie möchten, dass in Ihrem Repository Abzeichen mit Informationen über die Codeabdeckung angezeigt werden, hier Sie können eine kurze Anleitung zur Verwendung von Jest, Travis CI und Coveralls zur Sammlung und Ausgabe dieser Informationen finden.

Hier ist der Inhalt der Datei .travis.yml:

# Установить язык
language: node_js

# Установить версию Node.js
node_js:
  - '12'

services:
  # Использовать командную строку Docker
  - docker

install:
  # Установить зависимости для тестов
  - npm ci

before_script:
  # Запустить сервер и клиент для тестов
  - npm run dev &

script:
  # Запустить тесты
  - npm run test

Hier enden die Schritte, die für alle Branches im Repository und für Pull-Requests durchgeführt werden.

▍Bereitstellung

Unter der Annahme, dass alle automatisierten Tests erfolgreich abgeschlossen wurden, können wir, was nicht notwendig ist, den Code auf dem Produktionsserver bereitstellen. Da wir dies nur für den Code aus dem Branch master, geben wir dem System die entsprechenden Anweisungen in den Bereitstellungseinstellungen. Bevor Sie den Code, den wir weiter unten besprechen, in Ihrem Projekt verwenden, möchte ich Sie darauf hinweisen, dass Sie ein reales Skript haben müssen, das für die Bereitstellung aufgerufen wird.

deploy:
  # Docker-Container bauen und an Docker Hub senden
  provider: script
  script: bash deploy.sh
  on:
    branch: master

Das Bereitstellungsskript erfüllt zwei Aufgaben:

  • Bauen, Taggen und Senden des Images an Docker Hub mittels CI-Tools (in unserem Fall Travis CI).
  • Das Hochladen des Images auf den Server, Stoppen des alten Containers und Starten des neuen (in unserem Fall läuft der Server auf der Plattform DigitalOcean).

Zuerst müssen wir den automatischen Prozess für das Erstellen, Taggen und Hochladen des Images auf Docker Hub einrichten. Das ähnelt sehr dem, was wir bereits manuell gemacht haben, abgesehen davon, dass wir hier eine Strategie für die eindeutige Vergabe von Tags für die Images sowie die Automatisierung des Logins benötigen. Ich hatte Schwierigkeiten mit einigen Details des Bereitstellungsskripts, wie der Tagging-Strategie, dem Login, der SSH-Schlüssel-Codierung und dem Herstellen der SSH-Verbindung. Zum Glück kann mein Freund sehr gut mit Bash umgehen, wie auch mit vielen anderen Dingen. Er hat mir geholfen, dieses Skript zu schreiben.

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. DOCKER_USERNAME und DOCKER_PASSWORD — dies sind benutzerdefinierte Umgebungsvariablen, die über die Travis CI-Oberfläche festgelegt werden können. Travis CI verarbeitet die geheimen Daten automatisch, sodass sie nicht in die falschen Hände geraten.

Hier ist der erste Teil des Skripts deploy.sh.

#!/bin/sh
set -e # Остановить скрипт при наличии ошибок

IMAGE="<username>/<repository>"                             # Образ Docker
GIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-хэш и теги

# Сборка и тегирование образа
docker build -t ${IMAGE}:${GIT_VERSION} .
docker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest

# Вход в Docker Hub и выгрузка образа
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
docker push ${IMAGE}:${GIT_VERSION}

Der Inhalt des zweiten Teils des Skripts hängt vollständig 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 doctlverwendet. Bei der Arbeit mit AWS kommt das Tool awszum Einsatz, und so weiter.

Die Einrichtung des Servers war nicht besonders schwierig. Ich habe einen Droplet basierend auf dem Basis-Image konfiguriert. Es ist erwähnenswert, dass das von mir gewählte 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önnen Sie, wenn Sie ebenfalls Ubuntu nutzen, einfach diesem einfachen Leitfaden folgen.

Ich spreche hier nicht von spezifischen Befehlen für den Dienst, da dieser Aspekt in verschiedenen Fällen stark variieren kann. Ich werde lediglich einen allgemeinen Aktionsplan skizzieren, der nach der SSH-Verbindung zum Server ausgeführt wird, auf dem das Projekt bereitgestellt wird:

  • Zunächst müssen Sie den aktuell laufenden Container finden und ihn stoppen.
  • Dann muss im Hintergrund ein neuer Container gestartet werden.
  • Sie müssen den lokalen Serverport auf den Wert einstellen 80 — das ermöglicht den Zugang zur Website unter einer Adresse wie example.com, ohne den Port anzugeben, anstatt eine Adresse wie example.com:5000.
  • Und schließlich müssen alle alten Container und Images gelöscht werden.

Hier ist die Fortsetzung des Skripts.

# Найти ID работающего контейнера
CONTAINER_ID=$(docker ps | grep takenote | cut -d" " -f1)

# Остановить старый контейнер, запустить новый, очистить систему
docker stop ${CONTAINER_ID}
docker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}
docker system prune -a -f

Einige Dinge, auf die Sie achten sollten

Es kann sein, dass Sie beim Verbinden mit dem Server über SSH aus Travis CI eine Warnung sehen, die eine Fortsetzung der Installation verhindert, da das System auf eine Benutzerreaktion wartet.

Die Authentizität des Hosts ' (<IP-Adresse>)' kann nicht festgestellt werden.
Der RSA-Schlüssel-Fingerabdruck ist <Fingerabdruck>.
Sind Sie sicher, dass Sie mit der Verbindung fortfahren möchten (ja/nein)?

Ich habe herausgefunden, dass der String-Schlüssel in base64 kodiert werden kann, um ihn in einer Form zu speichern, mit der bequem und zuverlässig gearbeitet werden kann. Während der Installation kann der öffentliche Schlüssel dekodiert und in eine Datei geschrieben werden known_hosts um den oben beschriebenen Fehler zu beheben.

echo  | base64 # gibt  aus

In der Praxis könnte dieser Befehl so aussehen:

echo "123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== you@example.com" | base64

So sieht der Output aus — eine Zeichenkette in Base64-Codierung:

MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vpa0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==

Hier ist der Befehl, der oben erwähnt wurde.

install:
  - echo  | base64 -d >> $HOME/.ssh/known_hosts

Der gleiche Ansatz kann auch mit dem privaten Schlüssel verwendet werden, wenn eine Verbindung hergestellt wird, da Sie möglicherweise den privaten Schlüssel benötigen, um auf den Server zuzugreifen. Achten Sie darauf, ihn sicher in der Umgebungsvariable von Travis CI zu speichern, ohne dass er irgendwo ausgegeben wird.

Eine weitere wichtige Sache ist, dass Sie möglicherweise das gesamte Bereitstellungsskript in einer einzigen Zeile ausführen müssen, zum Beispiel mit doctl. Das kann einige zusätzliche Anstrengungen erfordern.

doctl compute ssh  --ssh-command "alle Befehle hier &&& hier"

TLS/SSL und Lastenausgleich

Nachdem ich alles gemacht hatte, was oben erwähnt wurde, war das letzte Problem, das sich mir stellte, dass der Server kein SSL hatte. Da ich einen Node.js-Server benutze, um zu arbeiten benötigt man einige Anstrengungen, um den Nginx-Reverse-Proxy und Let’s Encrypt zum Laufen zu bringen.

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ür mehrere Server, die hinter dem Lastenausgleich arbeiten, einzurichten. Dadurch müssen sich die Server überhaupt nicht um SSL kümmern und können weiterhin wie gewohnt über den Port 80arbeiten. Die Einrichtung von SSL auf dem Lastenausgleich ist also viel einfacher und bequemer als alternative Methoden zur SSL-Konfiguration.

Jetzt können Sie auf dem Server alle Ports, die eingehende Verbindungen akzeptieren, schließen – außer dem Port 80, der für die Kommunikation mit dem Lastenausgleich verwendet wird, und dem Port 22 für SSH. Daher wird jeder Versuch, den Server über andere Ports als diese beiden zu erreichen, scheitern.

Ergebnisse

Nachdem 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üchtert. 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ür mich alles noch relativ neu, und ich bin mir sicher, dass es Möglichkeiten gibt, meinen automatisierten Workflow zu verbessern und effizienter zu gestalten. Wenn Sie also Ideen dazu haben – lassen Sie es mich wissen. Ich hoffe, dieser Artikel hat Ihnen bei Ihren Vorhaben geholfen. Ich möchte glauben, dass Sie beim Lesen ebenso viel gelernt haben, wie ich, während ich mich mit den Themen auseinandersetze, die darin behandelt werden.

P.S. In unserem Marktplatz gibt es ein Image Docker, das mit einem Klick installiert wird. Sie können die Funktionalität der Container auf VPSüberprüfen. Allen neuen Kunden stehen 3 Tage kostenlos zur Verfügung, um zu testen.

Sehr geehrte Leser! Nutzen Sie CI/CD-Technologien in Ihren Projekten?

Erstellung einer CI/CD-Pipeline und Automatisierung der Arbeit mit Docker

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster