One place for hosting & domains

      Austricksen eines neuronalen Netzwerks in Python 3


      Der Autor hat Dev Color dazu ausgewählt, im Rahmen des Programms Write for DOnations eine Spende zu erhalten.

      Kann ein neuronales Netzwerk zur Klassifizierung von Tieren ausgetrickst werden? Das Austricksen einer solchen Klassifizierung mag vielleicht wenig Konsequenzen haben, doch sieht das bei Gesichtsauthentifizierung schon anders aus. Und was ist mit der Software von Prototypen selbstfahrender Autos? Zum Glück gibt es zahlreiche Ingenieure und Forscher, die bei unseren mobilen Geräten und Autos zwischen einem Computermodell für Prototypen und Modellen mit Produktionsqualität stehen. Dennoch haben diese Risiken erhebliche Auswirkungen und müssen von Benutzern, die maschinelles Lernen anwenden, berücksichtigt werden.

      In diesem Tutorial versuchen Sie, eine Klassifizierungsanwendung für Tiere zu täuschen bzw. auszutricksen. Wenn Sie das Tutorial durcharbeiten, verwenden Sie OpenCV, eine Computer-Vision-Bibliothek, und PyTorch, eine Deep Learning Library. Wir werden die folgenden Themen im zugehörigen Bereich des Adversarial Machine Learning behandeln:

      • Erstellen eines gezielten Adversial-Beispiels. Auswählen eines Bilds – etwa eines Hunds. Auswählen einer Zielklasse, z. B. eine Katze. Ihr Ziel besteht darin, das neuronale Netzwerk so zu täuschen, dass es denkt, dass der abgebildete Hund eine Katze sei.
      • Erstellen Sie eine Adversarial-Verteidigung. Kurz gesagt: Schützen Sie Ihr neuronales Netzwerk vor diesen trickreichen Bildern, ohne zu wissen, was der Trick ist.

      Am Ende des Tutorials verfügen Sie über ein Werkzeug zum Austricksen neuronaler Netzwerke und ein Verständnis dafür, wie man sich gegen Tricks verteidigen kann.

      Voraussetzungen

      Um dieses Tutorial zu absolvieren, benötigen Sie Folgendes:

      • Eine lokale Entwicklungsumgebung für Python 3 mit mindestens 1 GB RAM. Unter How to Install and Set Up a Local Programming Environment for Python 3 (Installieren und Einrichten einer lokalen Programmierumgebung für Python 3) finden Sie Informationen darüber, wie Sie die benötigten Konfigurationen vornehmen.
      • Es wird empfohlen, Build an Emotion-Based Dog Filter (Erstellen eines emotionsbasierten Hundefilters) zu konsultieren; dieses Tutorial wird nicht explizit verwendet, führt aber in den Begriff der Klassifizierung ein.

      Schritt 1 – Erstellen des Projekts und Installieren von Abhängigkeiten

      Wir wollen einen Arbeitsbereich für dieses Projekt erstellen und die Abhängigkeiten installieren, die wir benötigen. Sie nennen Ihren Arbeitsbereich AdversarialML:

      Navigieren Sie zum Verzeichnis AdversarialML:

      Erstellen Sie ein Verzeichnis, das alle Ihre Ressourcen enthält:

      • mkdir ~/AdversarialML/assets

      Erstellen Sie dann eine neue virtuelle Umgebung für das Projekt:

      • python3 -m venv adversarialml

      Aktivieren Sie Ihre Umgebung:

      • source adversarialml/bin/activate

      Installieren Sie anschließend PyTorch, ein Deep-Learning-Framework für Python, das wir in diesem Tutorial verwenden werden.

      Auf macOS installieren Sie Pytorch mit dem folgenden Befehl:

      • python -m pip install torch==1.2.0 torchvision==0.4.0

      Auf Linux und Windows verwenden Sie die folgenden Befehle für einen reinen CPU-Build:

      • pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
      • pip install torchvision

      Installieren Sie nun vorgepackte Binaries für OpenCV und numpy; das sind Bibliotheken für Computer-Vision bzw. lineare Algebra. OpenCV bietet Hilfsfunktionen wie Bilddrehung und numpy bietet Hilfsfunktionen für lineare Algebra an, z. B. eine Matrixinversion:

      • python -m pip install opencv-python==3.4.3.18 numpy==1.14.5

      Auf Linux-Distributionen müssen Sie libSM.so installieren:

      • sudo apt-get install libsm6 libxext6 libxrender-dev

      Nach Installation der Abhängigkeiten führen wir eine Tierklassifizierung namens ResNet18 aus, was wir als Nächstes beschreiben werden.

      Schritt 2 — Ausführen einer vordefinierten Tierklassifizierung

      Die torchvision-Bibliothek, die offizielle Computer-Vision-Bibliothek für PyTorch, enthält vortrainierte Versionen von verbreiteten neuronalen Computer-Vision-Netzwerken. Diese neuronalen Netzwerke werden alle in ImageNet 2012 trainiert, einem Datensatz mit 1,2 Millionen Trainingsbildern und 1.000 Klassen. Diese Klassen umfassen Fahrzeuge, Orte und vor allem Tiere. In diesem Schritt werden Sie eines dieser vortrainierten neuronalen Netzwerke (ResNet18) ausführen. Wir werden ResNet18, trainiert in ImageNet, als „Tierklassifizierung“ bezeichnen.

      Was ist ResNet18? ResNet18 ist das kleinste neuronale Netzwerk in einer Familie neuronaler Netzwerke, die Residual Neural Networks genannt werden, entwickelt von MSR (et al.). Kurz gesagt: Er hat herausgefunden, dass ein neuronales Netzwerk (angegeben als Funktion f mit Eingabe x und Ausgabe f(x)) mit einer "residual connection" x + f(x) bessere Ergebnisse liefern würde. Diese Residual Connection wird auch heute noch in hochmodernen neuronalen Netzwerken häufig verwendet. Beispielsweise FBNetV2, FBNetV3.

      Laden Sie dieses Bild eines Hundes mit dem folgenden Befehl herunter:

      • wget -O assets/dog.jpg https://www.xpresservers.com/wp-content/uploads/2020/06/How-To-Trick-a-Neural-Network-in-Python-3.png

      Bild von Corgi nahe Teich

      Laden Sie dann eine JSON-Datei herunter, um die neuronale Netzwerkausgabe in einen menschenlesbaren Klassennamen zu konvertieren:

      • wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json

      Erstellen Sie als Nächstes ein Skript zum Ausführen Ihres vortrainierten Modells für das Hundebild. Erstellen Sie eine neue Datei namens step_2_pretrained.py:

      • nano step_2_pretrained.py

      Fügen Sie zunächst das Python-Boilerplate hinzu, indem Sie die erforderlichen Pakete importieren und eine main-Funktion deklarieren:

      step_2_pretrained.py

      from PIL import Image
      import json
      import torchvision.models as models
      import torchvision.transforms as transforms
      import torch
      import sys
      
      def main():
          pass
      
      if __name__ == '__main__':
          main()
      

      Als Nächstes laden Sie das Mapping von neuronaler Netzwerkausgabe zu menschenlesbaren Klassennamen. Fügen Sie dies direkt nach Ihren Importanweisungen und vor Ihrer main-Funktion hinzu:

      step_2_pretrained.py

      . . .
      def get_idx_to_label():
          with open("assets/imagenet_idx_to_label.json") as f:
              return json.load(f)
      . . .
      

      Erstellen Sie eine Funktion zur Bildtransformation, die sicherstellt, dass Ihr Eingabebild zum einen die richtigen Dimensionen aufweist und zum anderen richtig normalisiert ist. Fügen Sie direkt nach der letzten Funktion folgende Funktion hinzu:

      step_2_pretrained.py

      . . .
      def get_image_transform():
          transform = transforms.Compose([
            transforms.Resize(224),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
          ])
          return transform
      . . .
      

      In get_image_transform definieren Sie eine Reihe verschiedener Transformationen für die Bilder, die an Ihr neuronales Netzwerk übergeben werden:

      • transforms.Resize(224): Ändert die kleinere Seite des Bildes auf 224. Wenn Ihr Bild beispielsweise 448 x 672 groß ist, würde diese Operation das Bild beispielsweise auf 224 x 336 reduzieren.
      • transforms.CenterCrop(224): Nimmt einen Zuschnitt von der Bildmitte mit der Größe 224 x 224 vor.
      • transforms.ToTensor(): Konvertiert das Bild in einen PyTorch-Tensor. Alle PyTorch-Modelle erfordern PyTorch-Tensoren als Eingabe.
      • transforms.Normalize(mean=..., std=...): Standardisiert Ihre Eingabe, indem der Mittelwert subtrahiert und dann durch die Standardabweichung geteilt wird. Dies wird in der torchvision-Dokumentation genauer beschrieben.

      Fügen Sie ein Dienstprogramm hinzu, um die Tierklasse je nach Bild vorherzusagen. Diese Methode verwendet die beiden vorherigen Dienstprogramme zur Durchführung der Tierklassifizierung:

      step_2_pretrained.py

      . . .
      def predict(image):
          model = models.resnet18(pretrained=True)
          model.eval()
      
          out = model(image)
      
          _, pred = torch.max(out, 1)  
          idx_to_label = get_idx_to_label()  
          cls = idx_to_label[str(int(pred))]  
          return cls
      . . .
      

      Hier klassifiziert die Funktion predict das bereitgestellte Bild mit einem vortrainierten neuronalen Netzwerk:

      • model.resnet18(pretrained=True): Lädt ein vortrainiertes neuronales Netzwerk namens ResNet18.
      • model.eval(): Ändert das vorhandene Modell so, dass es im Modus ‘evaluation’ ausgeführt wird. Der einzige andere Modus ist der Modus ‘training’, doch ist der Trainingsmodus nicht erforderlich, da Sie das Modell in diesem Tutorial nicht trainieren (d. h. die Parameter des Modells nicht aktualisieren).
      • out = model(image): Führt das neuronale Netzwerk für das bereitgestellte, transformierte Bild aus.
      • _, pred = torch.max(out, 1): Das neuronale Netzwerk gibt eine Wahrscheinlichkeit für jede mögliche Klasse aus. Dieser Schritt berechnet den Index der Klasse mit der höchsten Wahrscheinlichkeit. Beispielsweise wenn out = [0.4, 0.1, 0.2], dann pred = 0.
      • idx_to_label = idx_to_label: Erhält ein Mapping vom Klassenindex zu menschenlesbaren Klassennamen. Beispielsweise könnte das Mapping {0: Katze, 1: Hund, 2: Fisch} lauten.
      • cls = idx_to_label[str(int(pred))]: Konvertiert den vorhergesagten Klassenindex in einen Klassennamen. Die in den letzten beiden Punkten genannten Beispiele würden cls = idx_to_label[0] = 'cat' ergeben.

      Als Nächstes fügen Sie nach der letzten Funktion ein Dienstprogramm zum Laden von Bildern hinzu:

      step_2_pretrained.py

      . . .
      def load_image():
          assert len(sys.argv) > 1, 'Need to pass path to image'
          image = Image.open(sys.argv[1])
      
          transform = get_image_transform()
          image = transform(image)[None]
          return image
      . . .
      

      Dadurch wird ein Bild aus dem im ersten Argument angegebenen Pfad in das Skript geladen. transform(image)[None] wendet die Reihenfolge der in den vorherigen Zeilen definierten Bildtransformationen an.

      Abschließend füllen Sie Ihre main-Funktion mit Folgendem, um Ihr Bild zu laden und das Tier im Bild zu klassifizieren:

      step_2_pretrained.py

      def main():
          x = load_image()
          print(f'Prediction: {predict(x)}')
      

      Vergewissern Sie sich, dass Ihre Datei mit unserem letzten Schritt 2 bei step_2_pretrained.py in GitHub übereinstimmt. Speichern und beenden Sie Ihr Skript und führen Sie die Tierklassifizierung aus:

      • python step_2_pretrained.py assets/dog.jpg

      Dadurch wird folgende Ausgabe erstellt, was zeigt, dass Ihre Tierklassifizierung wie erwartet funktioniert:

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      Das beschließt die Ausführung von Inferenz mit Ihrem vortrainierten Modell. Als Nächstes sehen Sie ein Adversarial-Beispiel in der Praxis, bei dem ein neuronales Netzwerk mit unmerklichen Unterschieden im Bild ausgetrickst wird.

      Schritt 3 — Ausprobieren eines Adversarial-Beispiels

      Jetzt werden Sie ein Adversarial-Beispiel synchronisieren und das neuronale Netzwerk für dieses Beispiel testen. Für dieses Tutorial erstellen Sie verschiedene Adversarial-Beispiele im Format x + r, wobei x das ursprüngliche Bild und r eine „Perturbation“ ist. Sie werden die Perturbation r noch selbst erstellen, doch in diesem Schritt werden Sie eine herunterladen, die wir zuvor für Sie erstellt haben. Starten Sie mit dem Herunterladen der Perturbation r:

      • wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true

      Verbinden Sie nun das Bild mit der Perturbation. Erstellen Sie eine neue Datei namens step_3_adversarial.py:

      • nano step_3_adversarial.py

      In dieser Datei führen Sie den folgenden Prozess mit drei Schritten aus, um ein adversarial-Beispiel zu erstellen:

      1. Transformieren eines Bilds
      2. Anwenden der Perturbation r
      3. Inverses Transformieren des pertubierten Bilds

      Am Ende von Schritt 3 werden Sie über ein gegensätzliches Bild verfügen. Importieren Sie zunächst die erforderlichen Pakete und deklarieren Sie eine Hauptfunktion:

      step_3_adversarial.py

      from PIL import Image
      import torchvision.transforms as transforms
      import torch
      import numpy as np
      import os
      import sys
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Erstellen Sie als Nächstes eine “Bildtransformation”, die die frühere Bildtransformation umkehrt. Platzieren Sie dies nach Ihren Importen vor der main-Funktion:

      step_3_adversarial.py

      . . .
      def get_inverse_transform():
          return transforms.Normalize(
              mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255],  # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
              std=[1/0.229, 1/0.224, 1/0.255])
      . . .
      

      Wie zuvor subtrahiert die Funktion transforms.Normalize den Mittelwert und teilt durch die Standardabweichung (d. h. für das ursprüngliche Bild x, y = transforms.Normalize(mean=u, std=o) = (x - u) / o). Sie machen etwas Algebra und definieren eine neue Operation, die diese Normalisierungsfunktion umkehrt (transforms.Normalize(mean=-u/o, std=1/o) = (y - -u/o) / 1/o = (y + u/o) o = yo + u = x).

      Fügen Sie im Rahmen der inversen Transformation eine Methode hinzu, die einen PyTorch-Tensor in ein PIL-Bild umwandelt. Fügen Sie dies nach der letzten Funktion hinzu:

      step_3_adversarial.py

      . . .
      def tensor_to_image(tensor):
          x = tensor.data.numpy().transpose(1, 2, 0) * 255.  
          x = np.clip(x, 0, 255)
          return Image.fromarray(x.astype(np.uint8))
      . . .
      
      • tensor.data.numpy() konvertiert den PyTorch-Tensor in ein NumPy-Array. .transpose(1, 2, 0) arrangiert (Kanäle, Breite, Höhe) in (Höhe, Breite, Kanäle) um. Dieses NumPy-Array befindet sich etwa im Bereich (0, 1). Abschließend multiplizieren Sie mit 255, um sicherzustellen, dass das Bild nun im Bereich (0, 255) ist.
      • np.clip sorgt dafür, dass alle Werte im Bild zwischen (0, 255) liegen.
      • x.astype(np.uint8) sorgt dafür, dass alle Bildwerte integer sind. Abschließend erstellt Image.fromarray(...) ein PIL-Bildobjekt aus dem NumPy-Array.

      Verwenden Sie dann diese Dienstprogramme zum Erstellen des adversarial-Beispiels mit Folgendem:

      step_3_adversarial.py

      . . .
      def get_adversarial_example(x, r):
          y = x + r
          y = get_inverse_transform()(y[0])
          image = tensor_to_image(y)
          return image
      . . .
      

      Diese Funktion generiert das adversarial-Beispiel, wie zu Anfang des Abschnitts beschrieben:

      1. y = x + r. Nehmen Sie Ihre Perturbation r und fügen Sie sie dem Originalbild x hinzu.
      2. get_inverse_transform: Erhalten und wenden Sie die umgekehrte Bildtransformation an, die Sie in einigen Zeilen vorher definiert haben.
      3. tensor_to_image: Konvertieren Sie abschließend den PyTorch-Tensor wieder in ein Bildobjekt.

      Als Letztes ändern Sie Ihre main-Funktion so, dass sie das Laden des Bilds, das Laden der adversarial-Perturbation r, das Anwenden der Perturbation, das Speichern des adversarial-Beispiels auf Festplatte und das Ausführen der Vorhersage für das adversarial-Beispiel ausführt:

      step_3_adversarial.py

      def main():
          x = load_image()
          r = torch.Tensor(np.load('assets/adversarial_r.npy'))
      
          # save perturbed image
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
          adversarial.save('outputs/adversarial.png')
      
          # check prediction is new class
          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Ihre abgeschlossene Datei sollte mit step_3_adversarial.py in GitHub übereinstimmen. Speichern Sie die Datei, beenden Sie den Editor und starten Sie Ihr Skript mit:

      • python step_3_adversarial.py assets/dog.jpg

      Sie sehen diese Ausgabe:

      Output

      Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      Sie haben nun ein Adversarial-Beispiel erstellt: Es täuscht das neuronale Netzwerk, sodass es denkt, ein Corgi sei ein Goldfisch. Im nächsten Schritt erstellen Sie tatsächlich die Perturbation r, die Sie hier verwendet haben.

      Schritt 4 — Verstehen eines Adversarial-Beispiels

      Informationen zur Klassifizierung finden Sie in „How to Build an Emotion-Based Dog Filter“ („Erstellen eines emotionsbasierten Hundefilters“).

      Sie erinnern sich bestimmt daran, dass Ihr Klassifizierungsmodell für jede Klasse eine Wahrscheinlichkeit ausgibt. Bei der Inferenz prognostiziert das Modell die Klasse mit der höchsten Wahrscheinlichkeit. Beim Training aktualisieren Sie die Modellparameter t so, dass die Wahrscheinlichkeit der richtigen Klasse y angesichts Ihrer Daten x maximiert wird.

      argmax_y P(y|x,t)
      

      Um jedoch Adversarial-Beispiele zu generieren, ändern Sie nun Ihr Ziel. Anstatt eine Klasse zu finden, besteht Ihr Ziel nun darin, ein neues Bild (x) zu finden. Wählen Sie eine beliebige andere Klasse als die richtige Klasse. Wir nennen diese neue Klasse w. Ihr neues Ziel besteht nun darin, die Wahrscheinlichkeit der falschen Klasse zu maximieren.

      argmax_x P(w|x)
      

      Beachten Sie, dass die neuronalen Netzwerkgewichtungen t aus dem obigen Ausdruck fehlen. Das liegt daran, dass Sie nun die Rolle des Adversary übernehmen: Jemand anderes hat ein Modell trainiert und bereitgestellt. Sie dürfen nur Adversarial-Eingaben erstellen und dürfen das bereitgestellte Modell nicht ändern. Um das Adversarial-Beispiel x zu generieren, können Sie “training” ausführen. Allerdings aktualisieren Sie nicht die neuronalen Netzwerkgewichtungen, sondern das Eingabebild mit dem neuen Ziel.

      Zur Erinnerung: In diesem Tutorial gehen Sie davon aus, dass das Adversarial-Beispiel eine affine Transformation von x ist. Mit anderen Worten: Ihr Adversarial-Beispiel nimmt die Form x + r für einige r an. Im nächsten Schritt schreiben Sie ein Skript zur Generierung dieses r.

      Schritt 5 — Erstellen eines Adversarial-Beispiels

      In diesem Schritt lernen Sie eine Perturbation r, sodass Ihr Corgi als Goldfisch falsch klassifiziert wird. Erstellen Sie eine neue Datei namens step_5_perturb.py:

      Importieren Sie zunächst die erforderlichen Pakete und deklarieren Sie eine main-Funktion:

      step_5_perturb.py

      from torch.autograd import Variable
      import torchvision.models as models
      import torch.nn as nn
      import torch.optim as optim
      import numpy as np
      import torch
      import os
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      from step_3_adversarial import get_adversarial_example
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Definieren Sie direkt nach Ihren Importen und vor der main-Funktion zwei Konstanten:

      step_5_perturb.py

      . . .
      TARGET_LABEL = 1
      EPSILON = 10 / 255.
      . . .
      

      Die erste Konstante TARGET_LABEL ist die Klasse, um den Corgi falsch zu klassifizieren. In diesem Fall entspricht der Index 1 „Goldfisch“. Die zweite Konstante EPSILON ist die maximale Perturbation, die für jeden Bildwert erlaubt ist. Diese Grenze wird eingeführt, damit das Bild unmerklich verändert wird.

      Fügen Sie nach Ihren beiden Konstanten eine helper-Funktion hinzu, um ein neuronales Netzwerk und den Perturbationsparameter r zu definieren:

      step_5_perturb.py

      . . .
      def get_model():
          net = models.resnet18(pretrained=True).eval()
          r = nn.Parameter(data=torch.zeros(1, 3, 224, 224), requires_grad=True)
          return net, r
      . . .
      
      • model.resnet18(pretrained=True) lädt ein vortrainiertes neuronales Netzwerk namens ResNet18, wie zuvor. Ebenso wie zuvor versetzen Sie das Modell mit .eval in den Evaluierungsmodus.
      • nn.Parameter(...) definiert eine neue Perturbation r, die Größe des Eingabebilds. Das Eingabebild ist auch von der Größe (1, 3, 224, 224). Das Schlüsselwortargument requires_grad=True sorgt dafür, dass Sie diese Perturbation r in dieser Datei in späteren Zeilen aktualisieren können.

      Als Nächstes beginnen Sie, Ihre main-Funktion zu ändern. Laden Sie zunächst das Modell net sowie die Eingänge x und definieren die Bezeichnung label:

      step_5_perturb.py

      . . .
      def main():
          print(f'Target class: {get_idx_to_label()[str(TARGET_LABEL)]}')
          net, r = get_model()
          x = load_image()
          labels = Variable(torch.Tensor([TARGET_LABEL])).long()
        . . .
      

      Als Nächstes definieren Sie sowohl das Kriterium als auch den Optimizer in Ihrer main-Funktion. Das Kriterium sagt PyTorch, was das Ziel ist – das heißt, welcher Verlust minimiert werden soll. Der Optimizer sagt PyTorch, wie Ihr Parameter r trainiert werden soll:

      step_5_perturb.py

      . . .
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD([r], lr=0.1, momentum=0.1)
      . . .
      

      Fügen Sie direkt danach die Haupttrainingsschleife für Ihren Parameter r hinzu:

      step_5_perturb.py

      . . .
          for i in range(30):
              r.data.clamp_(-EPSILON, EPSILON)
              optimizer.zero_grad()
      
              outputs = net(x + r)
              loss = criterion(outputs, labels)
              loss.backward()
              optimizer.step()
      
              _, pred = torch.max(outputs, 1)
              if i % 5 == 0:
                  print(f'Loss: {loss.item():.2f} / Class: {get_idx_to_label()[str(int(pred))]}')
      . . .
      

      Bei jeder Iteration dieser Trainingsschleife tun Sie Folgendes:

      • r.data.clamp_(...): Stellen Sie sicher, dass der Parameter r klein ist, innerhalb von EPSILON 0.
      • optimizer.zero_grad(): Löschen Sie alle Gradiente, die Sie in der vorherigen Iteration berechnet haben.
      • model(x + r): Führen Sie Inferenz für das modifizierte Bild x + r aus.
      • Berechnen Sie den loss.
      • Berechnen Sie den Gradienten loss.backward.
      • Nehmen Sie einen Gradientenabstiegsschritt optimizer.step vor.
      • Berechnen Sie die Vorhersage pred.
      • Melden Sie abschließend den Verlust und die prognostizierten Klasse print(...).

      Speichern Sie als Nächstes die letzte Perturbation r:

      step_5_perturb.py

      def main():
          . . .
          for i in range(30):
              . . .
          . . .
          np.save('outputs/adversarial_r.npy', r.data.numpy())
      

      Speichern Sie direkt danach, noch in der main-Funktion, das perturbierte Bild:

      step_5_perturb.py

      . . .
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
      

      Führen Sie abschließend die Vorhersage sowohl für das Originalbild als auch das Adversarial-Beispiel aus:

      step_5_perturb.py

          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Überprüfen Sie sorgfältig Ihre Skriptübereinstimmungen step_5_perturb.py in GitHub. Speichern, beenden und führen Sie das Skript aus:

      • python step_5_perturb.py assets/dog.jpg

      Ihr Skript gibt Folgendes aus.

      Output

      Target class: goldfish, Carassius auratus Loss: 17.03 / Class: Pembroke, Pembroke Welsh corgi Loss: 8.19 / Class: Pembroke, Pembroke Welsh corgi Loss: 5.56 / Class: Pembroke, Pembroke Welsh corgi Loss: 3.53 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.99 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.00 / Class: goldfish, Carassius auratus Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      Die letzten beiden Zeilen zeigen, dass Sie die Gestaltung eines Adversarial-Beispiels von Grund auf nun abgeschlossen haben. Ihr neuronales Netzwerk klassifiziert nun ein perfekt vernünftiges Corgi-Bild als Goldfisch.

      Damit haben Sie gezeigt, dass neuronale Netzwerke leicht getäuscht werden können. Außerdem hat das Fehlen von Robustheit bei Adversarial-Beispielen erhebliche Folgen. Die nächste natürliche Frage ist diese: Wie können Sie Adversarial-Beispiele bekämpfen? Verschiedene Organisationen wie OpenAI haben viel Forschung betrieben. Im nächsten Abschnitt führen Sie eine Verteidigungsmaßnahme aus, um dieses Adversarial-Beispiel zu vereiteln.

      Schritt 6 — Verteidigen vor Adversarial-Beispielen

      In diesem Schritt werden Sie eine Verteidigung gegen Adversarial-Beispiele implementieren. Die Idee ist folgendermaßen: Sie sind nun Eigentümer der Tierklassifizierung, die in der Produktion bereitgestellt wird. Sie wissen nicht, welche Adversarial-Beispiele generiert werden können. Sie können jedoch das Bild oder das Modell ändern, um sich vor Angriffen zu schützen.

      Bevor Sie sich schützen, sollten Sie selbst sehen, wie unmerklich die Bildmanipulation ist. Öffnen Sie die beiden folgenden Bilder:

      1. assets/dog.jpg
      2. outputs/adversarial.png

      Hier sehen Sie beide nebeneinander. Ihr Originalbild wird ein anderes Seitenverhältnis aufweisen. Können Sie sagen, welches das Adversarial-Beispiel ist?

      (links) Corgi als Goldfisch, Adversarial, (rechts) Corgi als Hund nicht Adversarial

      Beachten Sie, dass das neue Bild genau wie das Original aussieht. Beim linken Bild handelt es sich um Ihr Adversarial-Bild. Laden Sie das Bild zur Sicherheit herunter und führen Sie Ihr Evaluierungsskript aus:

      • wget -O assets/adversarial.png https://github.com/alvinwan/fooling-neural-network/blob/master/outputs/adversarial.png?raw=true
      • python step_2_pretrained.py assets/adversarial.png

      Dadurch wird die Klasse Goldfisch ausgeben, was als Beleg für das Adversarial-Bild dient:

      Output

      Prediction: goldfish, Carassius auratus

      Sie führen eine ziemlich naive, aber effektive Schutzmaßnahme aus: Komprimieren Sie das Bild durch Schreiben in ein verlustreiches JPEG-Format. Öffnen Sie die interaktive Python-Aufforderung:

      Laden Sie dann das Adversarial-Bild als PNG und speichern Sie nun als JPG.

      • from PIL import Image
      • image = Image.open('assets/adversarial.png')
      • image.save('outputs/adversarial.jpg')

      Geben Sie Strg+D ein, um die interaktive Python-Eingabeaufforderung zu verlassen. Führen Sie als Nächstes Inferenz mit Ihrem Modell für das komprimierte Adversarial-Beispiel aus:

      • python step_2_pretrained.py outputs/adversarial.jpg

      Dadurch wird jetzt die Klasse Corgi ausgegeben, was die Wirksamkeit Ihrer einfachen Verteidigung beweist.

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      Sie haben nun Ihre erste Adversarial-Verteidigung abgeschlossen. Beachten Sie, dass Sie für diese Verteidigung nicht nicht wissen müssen, wie das Adversarial-Beispiel generiert wurde. Dadurch wird die Verteidigung so effektiv. Außerdem gibt es viele andere Formen der Verteidigung, von denen viele ein Neutrainieren des neuronalen Netzwerks beinhalten. Allerdings sind diese Verfahren zum Neutrainieren ein eigenes Thema, das über den Umfang dieses Tutorials hinausgeht. Damit schließt dieser Leitfaden zum Adversarial Machine Learning ab.

      Zusammenfassung

      Um die Auswirkungen Ihrer Arbeit in diesem Tutorial zu verstehen, sehen Sie noch einmal die beiden Bilder nebeneinander an: das Original und das Adversarial-Beispiel.

      (links) Corgi als Goldfisch, Adversarial, (rechts) Corgi als Hund, nicht Adversarial

      Obwohl beide Bilder mit dem menschlichen Auge gleich aussehen, wurde das erste manipuliert, um Ihr Modell zu täuschen. Beide Bilder zeigen eindeutig einen Corgi, doch ist das Modell sehr zuversichtlich, dass das zweite Modell einen Goldfisch enthält. Das sollte Ihnen Sorge bereiten; denken Sie am Ende dieses Tutorial daran, dass Ihr Modell fragil ist. Einfach durch Anwendung einer simplen Transformation können Sie es täuschen. Dies sind reale, plausible Gefahren, denen auch hochmoderne Forschung nicht gewachsen ist. Forschung über Machine-Learning-Sicherheit ist genauso anfällig für diese Fehler; als Benutzer ist es Ihre Aufgabe, maschinelles Lernen sicher anzuwenden. Weiteres Lesematerial finden Sie unter folgenden Links:

      Für mehr Informationen über maschinelles Lernen und Tutorials können Sie unsere Themenseite zum maschinellen Lernen besuchen.



      Source link

      Erstellen eines redundanten Speicherpools mit GlusterFS unter Ubuntu 20.04


      Eine frühere Version dieses Tutorials wurde von Justin Ellingwood verfasst.

      Einführung

      Single Points of Failure stellen bei der Speicherung kritischer Daten ein beträchtliches Risiko dar. Während sich mit vielen Datenbanken und anderer Software Daten im Kontext einer einzelnen Anwendung verteilen lassen, arbeiten andere Systeme auf der Ebene des Dateisystems, um sicherzustellen, dass Daten beim Schreiben auf Festplatte jedes Mal auch an einen anderen Ort kopiert werden.

      GlusterFS ist ein Network-Attached-Storage-Dateisystem (NAS), mit dem Sie Speicherressourcen verschiedener Geräte bündeln können. So lassen sich mehrere Speichergeräte, die auf unterschiedliche Computer verteilt sind, als eine leistungsfähigere Einheit nutzen. Außerdem bietet Ihnen GlusterFS die Möglichkeit, verschiedenartige Speicherkonfigurationen einzurichten, von denen viele funktionell RAID-Leveln ähneln. Zum Beispiel können Sie Daten auf verschiedenen Knoten im Cluster stripen oder für eine höhere Datenverfügbarkeit Redundanz implementieren.

      Ziele

      In diesem Leitfaden erstellen Sie ein redundantes geclustertes Speicherarray, auch als verteiltes Dateisystem oder (wie in der GlusterFS-Dokumentation) als Trusted Storage Pool bezeichnet. Damit erhalten Sie Funktionen, die einer über das Netzwerk gespiegelten RAID-Konfiguration ähneln: Jeder unabhängige Server enthält eine eigene Kopie der Daten, sodass Ihre Anwendungen auf eine beliebige Kopie zugreifen können. Dadurch lässt sich die Leselast besser verteilen.

      Dieser redundante GlusterFS-Cluster wird aus zwei Ubuntu 20.04-Servern bestehen. Er wird sich ähnlich wie ein NAS-Server mit gespiegeltem RAID verhalten. Dann werden Sie auf den Cluster über einen dritten Ubuntu 20.04-Server zugreifen, der als GlusterFS-Client konfiguriert ist.

      Anmerkung zur sicheren Ausführung von GlusterFS

      Wenn Sie einem GlusterFS-Volume Daten hinzufügen, werden diese Daten mit jedem Gerät im Speicherpool, in dem das Volumen gehostet wird, synchronisiert. Dieser Datenverkehr zwischen Knoten wird standardmäßig nicht verschlüsselt, d. h. es besteht das Risiko, dass er von bösartigen Akteuren abgefangen wird.

      Wenn Sie GlusterFS in der Produktion verwenden möchten, wird daher empfohlen, das Dateisystem in einem isolierten Netzwerk auszuführen. Sie könnten GlusterFS beispielsweise so einrichten, dass es in einer Virtual Private Cloud (VPC) oder mit einem VPN zwischen den einzelnen Knoten ausgeführt wird.

      Wenn Sie GlusterFS in DigitalOcean bereitstellen möchten, können Sie es in einem isolierten Netzwerk einrichten, indem Sie Ihre Serverinfrastruktur einer DigitalOcean Virtual Private Cloud hinzufügen. Details zur entsprechenden Einrichtung finden Sie in unserer VPC-Produktdokumentation.

      Voraussetzungen

      Um diesem Tutorial zu folgen, benötigen Sie drei Server, auf denen Ubuntu 20.04 ausgeführt wird. Jeder dieser Server sollte über einen Nicht-root-Benutzer mit Administratorberechtigungen und eine mit UFW konfigurierte Firewall verfügen. Folgen Sie dazu unserem Leitfaden für die Ersteinrichtung des Servers für Ubuntu 20.04.

      Anmerkung: Wie im Abschnitt „Ziele“ erwähnt, wird dieses Tutorial Sie durch die Konfiguration von zwei Ubuntu-Servern als Server in einem Speicherpool und dem dritten Server als Client begleiten; diesen werden Sie für Zugriff auf die beiden Speicherknoten verwenden.

      Aus Gründen der Einfachheit wird sich das Tutorial auf diese Computer mit folgenden Hostnamen beziehen:

      Hostname Rolle im Speicherpool
      gluster0 Server
      gluster1 Server
      gluster2 Client

      Befehle, die entweder auf gluster0 oder gluster1 ausgeführt werden müssen, weisen einen blauen bzw. roten Hintergrund auf:

      Befehle, die nur auf dem Client (gluster2) ausgeführt werden müssen, haben einen grünen Hintergrund:

      Befehle, die auf mehr als einem Computer ausgeführt werden können oder müssen, weisen einen grauen Hintergrund auf:

      Schritt 1 — Konfigurieren der DNS-Auflösung auf jedem Computer

      Das Erstellen einer Auflösung von Hostnamen zwischen den einzelnen Computern kann Ihnen bei der Verwaltung Ihres Gluster-Speicherpools helfen. Wenn Sie in diesem Tutorial später in einem gluster-Befehl auf einen Ihrer Computer verweisen, können Sie dies dann mit einem leicht zu merkenden Domänennamen oder sogar einem Spitznamen anstelle der jeweiligen IP-Adresse tun.

      Wenn Sie keinen freien Domänennamen haben oder einfach nur eine schnelle Einrichtung vornehmen möchten, können Sie stattdessen die Datei /etc/hosts auf den einzelnen Computern bearbeiten. Dies ist eine spezielle Datei auf Linux-Computern, in der Sie das System statisch konfigurieren können, um alle in der Datei enthaltenen Hostnamen in Form statischer IP-Adressen aufzulösen.

      Anmerkung: Wenn Sie Ihre Server zur Authentifizierung mit einer Domäne konfigurieren möchten, die Ihnen gehört, müssen Sie sich zunächst einen Domänennamen von einer Domänenregistrierungstelle wie Namecheap oder Enom verschaffen und dann die entsprechenden DNS-Einträge konfigurieren.

      Sobald Sie für jeden Server einen A-Eintrag konfiguriert haben, können Sie mit Schritt 2 fortfahren. Stellen Sie sicher, dass Sie glusterN.example.com und glusterN durch den Domänennamen ersetzen, der auf den jeweiligen im Beispielbefehl verwiesenen Server auflöst.

      Wenn Sie Ihre Infrastruktur von DigitalOcean erhalten haben, könnten Sie Ihren Domänennamen DigitalOcean hinzufügen und für jeden Ihrer Server einen eindeutigen A-Eintrag erstellen.

      Öffnen Sie die Datei mit root-Berechtigungen mit einem Texteditor Ihrer Wahl auf jedem Ihrer Computer. Wir verwenden hier nano:

      Standardmäßig wird die Datei in etwa so aussehen (mit entfernten Kommentaren):

      /etc/hosts

      127.0.1.1 hostname hostname
      127.0.0.1 localhost
      
      ::1 ip6-localhost ip6-loopback
      fe00::0 ip6-localnet
      ff00::0 ip6-mcastprefix
      ff02::1 ip6-allnodes
      ff02::2 ip6-allrouters
      ff02::3 ip6-allhosts
      

      Fügen Sie auf einem Ihrer Ubuntu-Server unter der lokalen Hostdefinition die IP-Adresse der einzelnen Server hinzu, gefolgt von allen Namen, die Sie verwenden möchten, um auf sie in Befehlen verweisen zu können.

      Im folgenden Beispiel erhält jeder Server einen langen Hostnamen, der auf glusterN.example.com abgestimmt ist, und einen kurzen Hostnamen, der auf glusterN abgestimmt ist. Sie können die Abschnitte glusterN.example.com und glusterN jeder Zeile in einen beliebigen Namen – oder durch einzelne Leerzeichen getrennte Namen – ändern, die Sie für den Zugriff auf einzelne Server verwenden möchten. Beachten Sie jedoch, dass in diesem Tutorial durchgehend die folgenden Beispiele verwenden werden:

      Anmerkung: Wenn Ihre Server Teil eines Infrastrukturpools vom Typ Virtual Private Cloud sind, sollten Sie in der Datei /etc/hosts die privaten IP-Adressen der einzelnen Server anstelle ihrer öffentlichen IP-Adressen verwenden.

      /etc/hosts

      . . .
      127.0.0.1       localhost
      first_ip_address gluster0.example.com gluster0
      second_ip_address gluster1.example.com gluster1
      third_ip_address gluster2.example.com gluster2
      
      . . .
      

      Wenn Sie fertig damit sind, der Datei /etc/hosts eines Computers diese neuen Zeilen hinzuzufügen, kopieren Sie die Zeilen und fügen Sie sie den /etc/hosts-Dateien auf Ihren anderen Computern hinzu. Jede /etc/hosts-Datei sollte dieselben Zeilen enthalten und die IP-Adressen Ihrer Server mit den ausgewählten Namen verknüpfen.

      Speichern und schließen Sie dann die Datei. Wenn Sie nano verwendet haben, drücken Sie STRG+X, Y und dann ENTER​​​.

      Nachdem Sie die Auflösung der Hostnamen zwischen den einzelnen Servern konfiguriert haben, können Sie Befehle leichter ausführen, wenn Sie später einen Speicherpool und ein Volume einrichten. Als Nächstes führen Sie einen weiteren Schritt aus, der auf jedem Ihrer Server abgeschlossen werden muss. Und zwar fügen Sie jedem Ihrer drei Ubuntu-Server das offizielle Personal Package Archive (PPA) des Gluster-Projekts hinzu, um dafür zu sorgen, dass Sie die neueste Version von GlusterFS installieren können.

      Schritt 2 — Einrichten von Softwarequellen auf jedem Computer

      Zwar enthalten die standardmäßigen Ubuntu 20.04-APT-Repositorys GlusterFS-Pakete, doch handelt es sich dabei zum Zeitpunkt der Verfassung dieses Dokuments nicht um die aktuellsten Versionen. Eine Möglichkeit, die neueste stabile Version von GlusterFS (zum Zeitpunkt der Verfassung dieses Dokuments Version 7.6) zu installieren, besteht darin, jedem Ihrer drei Ubuntu-Server das offizielle PPA des Gluster-Projekts hinzuzufügen.

      Fügen Sie das PPA für die GlusterFS-Pakete hinzu, indem Sie auf jedem Server folgenden Befehl ausführen:

      • sudo add-apt-repository ppa:gluster/glusterfs-7

      Drücken Sie ENTER, wenn Sie dazu aufgefordert werden, um zu bestätigen, dass Sie das PPA tatsächlich hinzufügen möchten.

      Aktualisieren Sie nach dem Hinzufügen des PPA den lokalen Paketindex der einzelnen Server. Dadurch wird sich jeder Server der neu verfügbaren Pakete bewusst:

      Nachdem Sie das offizielle PPA des Gluster-Projekts den einzelnen Servern hinzugefügt und den lokalen Paketindex aktualisiert haben, können Sie die erforderlichen GlusterFS-Pakete installieren. Da zwei Ihrer drei Computer als Gluster-Server und der dritte Computer als Client fungieren werden, müssen Sie jedoch zwei separate Installations- und Konfigurationsverfahren befolgen. Zuerst installieren und richten Sie die Serverkomponenten ein.

      Schritt 3 — Installieren von Serverkomponenten und Erstellen eines Trusted Storage Pool

      Ein Speicherpool ist eine beliebige Menge an Speicherkapazität, die aus mehr als einer Speicherquelle aggregiert wird. In diesem Schritt konfigurieren Sie zwei Ihrer Server — gluster0 und gluster1 — als Clusterkomponenten.

      Installieren Sie sowohl auf gluster0 als auch gluster1 das Paket für GlusterFS-Server, indem Sie Folgendes eingeben:

      • sudo apt install glusterfs-server

      Drücken Sie auf Aufforderung Y und dann ENTER, um die Installation zu bestätigen.

      Der Installationsprozess konfiguriert GlusterFS automatisch so, dass eine Ausführung als systemd-Dienst erfolgt. Er sorgt jedoch nicht für einen automatischen Start des Diensts oder das Aktivieren zum Ausführen zur Startzeit.

      Um glusterd (den GlusterFS-Dienst) zu starten, führen Sie den Befehl systemctl start sowohl auf gluster0 als auch gluster1 aus:

      • sudo systemctl start glusterd.service

      Führen Sie dann folgenden Befehl auf beiden Servern aus. Dadurch wird der Dienst jedes Mal gestartet, wenn der Server gestartet wird:

      • sudo systemctl enable glusterd.service

      Anschließend können Sie den Status des Diensts auf einem oder beiden Servern überprüfen:

      • sudo systemctl status glusterd.service

      Wenn der Dienst erfolgreich ausgeführt wird, erhalten Sie eine Ausgabe, die wie folgt aussieht:

      Output

      ● glusterd.service - GlusterFS, a clustered file-system server Loaded: loaded (/lib/systemd/system/glusterd.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2020-06-02 21:32:21 UTC; 32s ago Docs: man:glusterd(8) Main PID: 14742 (glusterd) Tasks: 9 (limit: 2362) CGroup: /system.slice/glusterd.service └─14742 /usr/sbin/glusterd -p /var/run/glusterd.pid --log-level INFO

      Wenn Sie dem Leitfaden zur Ersteinrichtung des Servers gefolgt sind, haben Sie auf jedem Ihrer Computer eine Firewall mit UFW eingerichtet. Aus diesem Grund müssen Sie die Firewall für jeden Knoten öffnen, bevor Sie eine Verbindung zwischen ihnen herstellen und einen Speicherpool einrichten können.

      Der Gluster-Daemon nutzt Port 24007, sodass Sie jedem Knoten über die Firewall der einzelnen Knoten in Ihrem Speicherpool Zugriff auf den Port gewähren müssen. Führen Sie dazu folgenden Befehl auf gluster0 aus. Denken Sie daran, gluster1_ip_address in die IP-Adresse von gluster1 zu ändern:

      • sudo ufw allow from gluster1_ip_address to any port 24007

      Führen Sie dann folgenden Befehl auf gluster1 aus. Vergessen Sie auch hier nicht, gluster0_ip_address in die IP-Adresse von gluster0 zu ändern:

      • sudo ufw allow from gluster0_ip_address to any port 24007

      Außerdem müssen Sie Ihrem Clientcomputer (gluster2) Zugriff auf diesen Port gewähren. Andernfalls werden Sie später Probleme haben, wenn Sie versuchen, das Volumen bereitzustellen. Führen Sie sowohl auf gluster0 als auch gluster1 folgenden Befehl aus, um diesen Port für Ihren Clientcomputer zu öffnen:

      • sudo ufw allow from gluster2_ip_address to any port 24007

      Um sicherzustellen, dass keine anderen Computer auf einem der Server auf den Port von Gluster zugreifen können, fügen Sie dann die folgende Rahmenregel deny sowohl gluster0 als auch gluster1 hinzu:

      Sie können nun eine Verbindung zwischen gluster0 und gluster1 herstellen. Dazu müssen Sie auf einem Ihrer Knoten den Befehl gluster peer probe ausführen. Es spielt dabei keine Rolle, welchen Knoten Sie verwenden. Das folgende Beispiel veranschaulicht die Ausführung des Befehls auf gluster0:

      • sudo gluster peer probe gluster1

      Dieser Befehl weist gluster0 im Wesentlichen an, gluster1 zu vertrauen und als Teil seines Speicherpools zu registrieren. Wenn der Test erfolgreich war, wird folgende Ausgabe zurückgegeben:

      Output

      peer probe: success

      Sie können jederzeit überprüfen, ob die Knoten miteinander kommunizieren, indem Sie auf einem der Knoten den Befehl gluster peer status ausführen. In diesem Beispiel wird er auf gluster1 ausgeführt:

      Wenn Sie diesen Befehl auf gluster1 ausführen, wird eine Ausgabe angezeigt, die wie folgt aussieht:

      Output

      Number of Peers: 1 Hostname: gluster0.example.com Uuid: a3fae496-c4eb-4b20-9ed2-7840230407be State: Peer in Cluster (Connected)

      An diesem Punkt kommunizieren Ihre beiden Server miteinander und sind bereit, gemeinsam Speichervolumes zu erstellen.

      Schritt 4 — Einrichten eines Speichervolumes

      Denken Sie daran, dass das primäre Ziel dieses Tutorials in der Einrichtung eines redundanten Speicherpools besteht. Dazu richten Sie ein Volume mit Replikatfunktion ein, damit Sie mehrere Kopien Ihrer Daten speichern und verhindern können, dass Ihr Cluster einen Single Point of Failure aufweist.

      Um ein Volume zu erstellen, verwenden Sie den Befehl gluster volume create mit dieser allgemeinen Syntax:

      sudo gluster volume create volume_name replica number_of_servers domain1.com:/path/to/data/directory domain2.com:/path/to/data/directory force
      

      Das bedeuten die Argumente und Optionen des Befehls gluster volume create:

      • volume_name: Das ist der Name, mit dem Sie nach der Erstellung auf das Volume verweisen. Der folgende Beispielbefehl sorgt für die Erstellung eines Volumes namens volume1.
      • replica number_of_servers: Nach dem Namen des Volumes können Sie festlegen, welche Art von Volume Sie erstellen möchten. Denken Sie daran, dass das Ziel dieses Tutorials darin besteht, einen redundanten Speicherpool einzurichten, sodass wir den Volume-Typ replica wählen. Dies erfordert ein Argument, mit dem angegeben wird, auf wie viele Server die Daten des Volumes repliziert werden sollen (in diesem Tutorial 2).
      • domain1.com:/… und domain2.com:/…: Diese definieren die Computer und den Speicherort des Verzeichnisses der Bricks (eine GlusterFS-Bezeichnung für die grundlegende Speichereinheit des Systems), was alle Verzeichnisse auf allen Computern umfasst, die als Teil oder Kopie eines größeren Volumes dienen. So entsteht volume1. Im folgenden Beispiel wird im root-Verzeichnis beider Server ein Verzeichnis namens gluster-storage erstellt.
      • force: Diese Option sorgt für das Überschreiben aller Warnungen oder Optionen, die sonst auftreten und die Erstellung des Volumes unterbrechen würden.

      Anhand der in diesem Tutorial zuvor aufgeführten Konventionen können Sie diesen Befehl zur Erstellung eines Volumes ausführen. Beachten Sie, dass Sie den Befehl entweder auf gluster0 oder gluster1 ausführen können:

      • sudo gluster volume create volume1 replica 2 gluster0.example.com:/gluster-storage gluster1.example.com:/gluster-storage force

      Wenn das Volume erfolgreich erstellt wurde, erhalten Sie folgende Ausgabe:

      Output

      volume create: volume1: success: please start the volume to access data

      An diesem Punkt wurde Ihr Volume bereits erstellt, ist aber noch nicht aktiv. Sie können das Volume starten und zur Verwendung bereitstellen, indem Sie folgenden Befehl ausführen (erneut auf einem Ihrer beiden Gluster-Server):

      • sudo gluster volume start volume1

      Wenn das Volume korrekt gestartet wurde, erhalten Sie folgende Ausgabe:

      Output

      volume start: volume1: success

      Überprüfen Sie als Nächstes, ob das Volume online ist. Führen Sie auf einem Ihrer Knoten folgenden Befehl aus:

      • sudo gluster volume status

      Dadurch wird eine Ausgabe zurückgegeben, die der folgenden ähnelt:

      Output

      Status of volume: volume1 Gluster process TCP Port RDMA Port Online Pid ------------------------------------------------------------------------------ Brick gluster0.example.com:/gluster-storage 49152 0 Y 18801 Brick gluster1.example.com:/gluster-storage 49152 0 Y 19028 Self-heal Daemon on localhost N/A N/A Y 19049 Self-heal Daemon on gluster0.example.com N/A N/A Y 18822 Task Status of Volume volume1 ------------------------------------------------------------------------------ There are no active volume tasks

      Laut dieser Ausgabe sind die Bricks auf beiden Servern online.

      Als letzter Schritt zur Konfiguration Ihres Volumes müssen Sie die Firewall auf beiden Servern öffnen, damit Ihr Clientcomputer in der Lage ist, sich mit dem Volume zu verbinden und das Volume bereitzustellen. Gemäß der Beispielausgabe des vorherigen Befehls wird volume1 auf beiden Computern an Port 49152 ausgeführt. Dies ist der Standardport von GlusterFS für das erste Volumen. Weitere Volumes werden also Port 49153, dann 49154 usw. verwenden.

      Führen Sie sowohl auf gluster0 als auch gluster1 folgenden Befehl aus, um gluster2 über die jeweilige Firewall Zugriff auf diesen Port zu gewähren:

      • sudo ufw allow from gluster2_ip_address to any port 49152

      Fügen Sie dann für zusätzliche Sicherheit eine weitere deny-Rahmenregel für den Port des Volumes hinzu – sowohl auf gluster0 als auch gluster1. Dadurch wird sichergestellt, dass auf beiden Servern keine anderen Computer außer Ihrem Client auf das Volume zugreifen können:

      Nachdem Ihr Volume nun ausgeführt wird, können Sie Ihren Clientcomputer einrichten und remote nutzen.

      Schritt 5 — Installieren und Konfigurieren von Clientkomponenten

      Ihr Volume ist nun konfiguriert und zur Verwendung durch Ihren Clientcomputer verfügbar. Bevor Sie beginnen, müssen Sie jedoch das Paket glusterfs-client aus dem PPA installieren, das Sie in Schritt 1 auf Ihrem Clientcomputer eingerichtet haben. Die Abhängigkeiten dieses Pakets umfassen einige gemeinsame Bibliotheken und Übersetzermodule von GlusterFS sowie die für die Arbeit erforderlichen FUSE-Tools.

      Führen Sie folgenden Befehl auf gluster2 aus:

      • sudo apt install glusterfs-client

      Sie werden Ihr Remote-Speichervolume in Kürze auf Ihrem Clientcomputer bereitstellen. Bevor Sie dies tun können, müssen Sie einen Bereitstellungspunkt erstellen. Traditionell befindet sich dieser im Verzeichnis /mnt, doch kann jeder beliebige Ort verwendet werden.

      Erstellen Sie aus Gründen der Einfachheit auf Ihrem Clientcomputer ein Verzeichnis namens /storage-pool als Bereitstellungspunkt. Dieser Verzeichnisname beginnt mit einem Schrägstrich (/), der es im root-Verzeichnis platziert. Daher müssen Sie das Verzeichnis mit sudo-Berechtigungen erstellen:

      Jetzt können Sie das Remotevolume bereitstellen. Werfen Sie zuvor einen Blick auf die Syntax des Befehls mount, den Sie dazu verwenden werden:

      sudo mount -t glusterfs domain1.com:volume_name /path/to/mount/point
      

      mount ist ein Dienstprogramm in vielen Unix-ähnlichen Betriebssystemen. Es dient dazu, Dateisysteme (ob externe Speichergeräte wie SD-Karten bzw. USB-Sticks oder NAS-Systeme wie im Fall dieses Tutorials) im vorhandenen Dateisystem des Computers in Verzeichnissen bereitzustellen. Die von Ihnen verwendete mount-Befehlssyntax umfasst die Option -t, die drei Argumente erfordert: den Typ des Dateisystems, der bereitgestellt werden soll, das Gerät, auf dem sich das bereitzustellende Dateisystem befindet, und das Verzeichnis auf dem Client, in dem Sie das Volume bereitstellen möchten.

      Beachten Sie, dass das Geräteargument in dieser Beispielsyntax auf einen Hostnamen verweist, gefolgt von einem Doppelpunkt und dann dem Namen des Volumes. GlusterFS abstrahiert die tatsächlichen Speicherverzeichnisse auf jedem Host, was bedeutet, dass dieser Befehl nicht das Verzeichnis /gluster-storage, sondern vielmehr das Volume volume1 bereitstellt.

      Beachten Sie außerdem, dass Sie nur ein Mitglied des Speicherclusters angeben müssen. Dies kann einer der beiden Knoten sein, da der GlusterFS-Dienst sie als einen Computer behandelt.

      Führen Sie auf Ihrem Clientcomputer (gluster2) folgenden Befehl aus, um das Volume im von Ihnen erstellten Verzeichnis /storage-pool bereitzustellen:

      • sudo mount -t glusterfs gluster0.example.com:/volume1 /storage-pool

      Führen Sie danach den Befehl df aus. Dadurch wird für Dateisysteme, auf die der aufrufende Benutzer Zugriff hat, der verfügbare Speicherplatz angezeigt:

      Dieser Befehl zeigt an, dass das GlusterFS-Volume am richtigen Ort bereitgestellt wurde:

      Output

      Filesystem 1K-blocks Used Available Use% Mounted on . . . gluster0.example.com:/volume1 50633164 1938032 48695132 4% /storage-pool

      Jetzt können Sie mit der Prüfung fortfahren, ob alle Daten, die Sie in das Volume auf Ihrem Client schreiben, wie erwartet auf Ihren Serverknoten repliziert werden.

      Schritt 6 — Testen von Redundanzfunktionen

      Nachdem Sie Ihren Client zur Verwendung des Speicherpools und Volumes eingerichtet haben, können Sie seine Funktionalität testen.

      Navigieren Sie auf Ihrem Clientcomputer (gluster2) zum im vorherigen Schritt definierten Bereitstellungspunkt:

      Erstellen Sie dann einige Testdateien. Der folgende Befehl erstellt in Ihrem Speicherpool zehn separate leere Dateien:

      • sudo touch file_{0..9}.test

      Wenn Sie sich die zuvor auf den einzelnen Speicherhosts definierten Speicherverzeichnisse ansehen, werden Sie feststellen, dass alle diese Dateien in jedem System vorhanden sind.

      Auf gluster0:

      Output

      file_0.test file_2.test file_4.test file_6.test file_8.test file_1.test file_3.test file_5.test file_7.test file_9.test

      Und auch auf gluster1:

      Output

      file_0.test file_2.test file_4.test file_6.test file_8.test file_1.test file_3.test file_5.test file_7.test file_9.test

      Wie diese Ausgaben zeigen, wurden auch die Testdateien, die Sie dem Client hinzugefügt haben, in beide Knoten geschrieben.

      Sollte jemals einer der Knoten in Ihrem Speichercluster ausfallen, kann es vorkommen, dass er nicht mehr mit dem Speicherpool synchron ist, wenn Änderungen am Dateisystem vorgenommen werden. Wenn der Knoten wieder online ist, können Sie durch Ausführung eines Lesevorgangs am Bereitstellungspunkt des Clients den Knoten auf fehlende Dateien aufmerksam machen:

      Nachdem Sie verifiziert haben, dass Ihr Speichervolumen korrekt bereitgestellt wurde und Sie Daten an beiden Computer im Cluster replizieren können, können Sie den Zugriff auf den Speicherpool sperren.

      Schritt 7 — Beschränken der Redundanzfunktionen

      Gegenwärtig kann sich jeder Computer ganz ohne Einschränkungen mit Ihrem Speichervolume verbinden. Sie können das ändern, indem Sie die Option auth.allow festlegen, um die IP-Adressen der einzelnen Clients zu definieren, die Zugriff auf das Volume haben sollen.

      Wenn Sie die Konfiguration /etc/hosts verwenden, werden die Namen, die Sie für die Server festgelegt haben, nicht korrekt geroutet. Sie müssen stattdessen eine statische IP-Adresse verwenden. Wenn Sie jedoch DNS-Einträge verwenden, wird hier der Domänenname, den Sie konfiguriert haben, funktionieren.

      Führen Sie auf einem Ihrer beiden Speicherknoten (gluster0 oder gluster1) folgenden Befehl aus:

      • sudo gluster volume set volume1 auth.allow gluster2_ip_address

      Wenn der Befehl erfolgreich abgeschlossen wird, gibt er folgende Ausgabe zurück:

      Output

      volume set: success

      Wenn Sie die Einschränkung irgendwann entfernen möchten, können Sie Folgendes eingeben:

      • sudo gluster volume set volume1 auth.allow *

      Dadurch werden wieder Verbindungen von beliebigen Computern aus möglich. Dies ist nicht sicher, kann aber für die Fehlerbehebung nützlich sein.

      Wenn Sie über mehrere Clients verfügen, können Sie ihre IP-Adressen oder Domänennamen gleichzeitig angeben (je nachdem, ob Sie /etc/hosts oder die Auflösung von DNS-Hostnamen verwenden), getrennt durch Kommas:

      • sudo gluster volume set volume1 auth.allow gluster_client1_ip,gluster_client2_ip

      Ihr Speicherpool ist nun konfiguriert, gesichert und einsatzbereit. Als Nächstes werden Sie einige Befehle kennen lernen, die Ihnen helfen, Informationen über den Status Ihres Speicherpools zu erhalten.

      Schritt 8 — Abrufen von Informationen über den Speicherpool mit GlusterFS-Befehlen

      Wenn Sie bestimmte Einstellungen für Ihren GlusterFS-Speicher ändern, können Sie den Überblick darüber verlieren, welche Optionen Sie zur Verfügung haben, welche Volumes live sind und welche Knoten mit einzelnen Volumes verknüpft sind.

      Es gibt verschiedene Befehle, die auf Ihren Knoten verfügbar sind, mit denen Sie diese Daten abrufen und mit Ihrem Speicherpool interagieren können.

      Wenn Sie Informationen über die einzelnen Volumes wünschen, führen Sie den Befehl gluster volume info aus:

      Output

      Volume Name: volume1 Type: Replicate Volume ID: a1e03075-a223-43ab-a0f6-612585940b0c Status: Started Snapshot Count: 0 Number of Bricks: 1 x 2 = 2 Transport-type: tcp Bricks: Brick1: gluster0.example.com:/gluster-storage Brick2: gluster1.example.com:/gluster-storage Options Reconfigured: auth.allow: gluster2_ip_address transport.address-family: inet storage.fips-mode-rchecksum: on nfs.disable: on performance.client-io-threads: off

      Um Informationen über Peers zu erhalten, mit denen dieser Knoten verbunden ist, können Sie Folgendes eingeben:

      Number of Peers: 1
      
      Hostname: gluster0.example.com
      Uuid: cb00a2fc-2384-41ac-b2a8-e7a1793bb5a9
      State: Peer in Cluster (Connected)
      

      Wenn Sie genaue Informationen zur Ausführung einzelner Knoten wünschen, können Sie ein Profil für ein Volume erstellen, indem Sie Folgendes eingeben:

      • sudo gluster volume profile volume_name start

      Nach erfolgreicher Ausführung dieses Befehls können Sie die gesammelten Informationen abrufen, indem Sie Folgendes eingeben:

      • sudo gluster volume profile volume_name info

      Output

      Brick: gluster0.example.com:/gluster-storage -------------------------------------------- Cumulative Stats: %-latency Avg-latency Min-Latency Max-Latency No. of calls Fop --------- ----------- ----------- ----------- ------------ ---- 0.00 0.00 us 0.00 us 0.00 us 30 FORGET 0.00 0.00 us 0.00 us 0.00 us 36 RELEASE 0.00 0.00 us 0.00 us 0.00 us 38 RELEASEDIR Duration: 5445 seconds Data Read: 0 bytes Data Written: 0 bytes Interval 0 Stats: %-latency Avg-latency Min-Latency Max-Latency No. of calls Fop --------- ----------- ----------- ----------- ------------ ---- 0.00 0.00 us 0.00 us 0.00 us 30 FORGET 0.00 0.00 us 0.00 us 0.00 us 36 RELEASE 0.00 0.00 us 0.00 us 0.00 us 38 RELEASEDIR Duration: 5445 seconds Data Read: 0 bytes Data Written: 0 bytes . . .

      Führen Sie wie zuvor gezeigt den Befehl gluster volume status aus, um eine Liste aller zu GlusterFS zugehörigen Komponenten zu erhalten, die auf den einzelnen Knoten ausgeführt werden:

      • sudo gluster volume status

      Output

      Status of volume: volume1 Gluster process TCP Port RDMA Port Online Pid ------------------------------------------------------------------------------ Brick gluster0.example.com:/gluster-storage 49152 0 Y 19003 Brick gluster1.example.com:/gluster-storage 49152 0 Y 19040 Self-heal Daemon on localhost N/A N/A Y 19061 Self-heal Daemon on gluster0.example.com N/A N/A Y 19836 Task Status of Volume volume1 ------------------------------------------------------------------------------ There are no active volume tasks

      Wenn Sie Ihre GlusterFS-Speichervolumes verwalten möchten, kann es eine gute Idee sein, die GlusterFS-Konsole zu nutzen. Dadurch können Sie mit Ihrer GlusterFS-Umgebung interagieren, ohne zunächst sudo gluster eingeben zu müssen:

      Daraufhin wird eine Eingabeaufforderung angezeigt, in der Sie Ihre Befehle eingeben können. help (Hilfe) ist eine gute Methode, um sich einen Überblick zu verschaffen:

      Output

      peer help - display help for peer commands volume help - display help for volume commands volume bitrot help - display help for volume bitrot commands volume quota help - display help for volume quota commands snapshot help - display help for snapshot commands global help - list global commands

      Führen Sie anschließend exit aus, um die Gluster-Konsole zu verlassen:

      Nun können Sie damit beginnen, GlusterFS mit der nächsten Anwendung zu integrieren.

      Zusammenfassung

      Durch Absolvieren dieses Tutorials haben Sie ein redundantes Speichersystem eingerichtet, mit dem Sie gleichzeitig auf zwei separate Server schreiben können. Das kann für verschiedene Anwendungen nützlich sein und dafür sorgen, dass Ihre Daten verfügbar bleiben, auch wenn ein Server ausfällt.



      Source link

      Einrichten eines NFS Mount auf Ubuntu 20.04


      Einführung

      NFS oder Network File System ist ein verteiltes Dateisystem, das es Ihnen ermöglicht, remote Verzeichnisse auf Ihrem Server bereitzustellen. Auf diese Weise können Sie Speicherplatz an einem anderen Ort verwalten und in diesen Speicherplatz aus mehreren Clients schreiben. NFS bietet eine relativ gute Standardmöglichkeit, auf Remote-Systeme über ein Netzwerk zuzugreifen, und funktioniert gut in Situationen, in denen regelmäßig auf die geteilten Ressourcen zugegriffen werden muss.

      In diesem Leitfaden erfahren Sie, wie wir die für NFS benötigte Software auf Ubuntu 20.04 installieren, zwei NFS Mounts auf einem Server und Client installieren und die Remotefreigaben hinzufügen und entfernen.

      Voraussetzungen

      Wir nutzen in diesem Tutorial zwei Server. Davon teilt einer einen Teil seines Dateisystems mit dem anderen. Um dem Tutorial zu folgen, benötigen Sie Folgendes:

      • Zwei Ubuntu 20.04-Server. Jeder dieser Server sollte über einen Nicht-root-Benutzer mit sudo-Berechtigungen, eine mit UFW eingerichtete Firewall und ein privates Netzwerk verfügen, sofern dies Ihnen zur Verfügung steht.

      In diesem Tutorial verweisen wir auf den Server, der seine Verzeichnisse als Host teilt, und den Server, der diese Verzeichnisse als Client verbindet. Sie müssen die IP-Adresse für beide kennen. Nutzen Sie bei Bedarf die private Netzwerkadresse, sofern verfügbar.

      In diesem Tutorial verweisen wir auf diese IP-Adressen durch die Platzhalter host_ip und client_ip. Ersetzen Sie bei Bedarf.

      Schritt 1 – Herunterladen und Installieren der Komponenten

      Wir beginnen mit der Installation der erforderlichen Komponenten auf jedem Server.

      Auf dem Host

      Auf dem Host-Server installieren Sie das nfs-kernel-server Paket, das ermöglicht, Ihre Verzeichnisse freizugeben. Da dieser Vorgang die erste Ausführung mit apt in dieser Sitzung ist, aktualisieren Sie Ihren lokalen Paketindex vor der Installation:

      • sudo apt update
      • sudo apt install nfs-kernel-server

      Sobald diese Pakete installiert sind, wechseln zum Client-Server.

      Auf dem Client

      Auf dem Client-Server müssen wir ein Paket namens nfs-common installieren, das NFS bereitstellt, ohne Server-Komponenten. Aktualisieren Sie erneut den lokalen Paketindex vor der Installation, um sicherzustellen, dass Ihnen aktuelle Informationen angezeigt werden:

      • sudo apt update
      • sudo apt install nfs-common

      Nachdem beide Server über die erforderlichen Pakete verfügen, können wir die Konfiguration starten.

      Schritt 2 – Erstellen der Freigabeverzeichnisse auf dem Host

      Wir werden zwei separate Verzeichnisse mit verschiedenen Konfigurationseinstellungen freigeben, um zwei wesentliche Varianten zu zeigen, wie NFS-Verbindungen hinsichtlich des Superuser-Zugriffs konfiguriert werden können.

      Superuser können auf ihrem System alles überall tun. NFS-verbundene Verzeichnisse sind jedoch nicht Teil des Systems, auf dem sie verbunden werden. Standardmäßig lehnt der NFS-Server die Ausführung von Operationen ab, die Superuser-Berechtigungen erfordern. Diese Standardbeschränkung bedeutet, dass Superuser auf dem Client keine Dateien als root schreiben, die Eigentümerschaft nicht neu zuweisen oder sonstige andere Superuser-Aufgaben auf der NFS-Verbindung ausführen können.

      Manchmal gibt es jedoch vertrauenswürdige Benutzer auf dem Client-System, die diese Aktionen auf dem verbundenen Dateisystem ausführen müssen, aber keinen Superuser Zugriff auf den Host benötigen. Sie können den NFS-Server so konfigurieren, dass er dies erlaubt, obwohl ein Risikoelement eingeführt wird, da ein Benutzer root-Zugriff auf das gesamte Host-System erhalten könnte.

      Beispiel 1: Exportieren einer Allzweck-Verbindung

      Im ersten Beispiel erstellen wir eine Allzweck-NFS-Verbindung, die das Standard-NFS-Verhalten nutzt, das es einem Benutzer mit root-Berechtigungen auf dem Client-Computer erschwert, mit dem Host über diese Client-Superuser-Berechtigungen zu interagieren. Sie könnten etwas in dieser Art nutzen, um Dateien zu speichern, die über ein Content-Management-System hochgeladen wurden, oder um einen Platz für Benutzer zu erstellen, wo sie einfach Projektdateien teilen können.

      Erstellen Sie zuerst das Freigabe-Verzeichnis:

      • sudo mkdir /var/nfs/general -p

      Da wir es mit sudo erstellen, gehört das Verzeichnis dem root-Benutzer des Hosts:

      Output

      drwxr-xr-x 2 root root 4096 May 14 18:36 .

      NFS übersetzt alle root Operationen auf dem Client als Sicherheitsmaßnahme in die Berechtigungsdaten nobody:nogroup. Wir müssen daher die Verzeichniseigentümerschaft auf diese Berechtigungsdaten abstimmen.

      • sudo chown nobody:nogroup /var/nfs/general

      Sie können dieses Verzeichnis nun exportieren.

      Beispiel 2: Exportieren des Home-Verzeichnisses

      In unserem zweiten Beispiel geht es darum, Benutzerhauptverzeichnisse, die auf dem Host gespeichert sind, auf Client-Servern zur Verfügung zu stellen, wobei vertrauenswürdige Administratoren dieser Client-Server der Zugriff genehmigt wird, den sie brauchen, um Benutzer einfach zu verwalten.

      Dazu exportieren wir das Verzeichnis /home. Da es bereits vorhanden ist, müssen wir es nicht erstellen. Wir werden auch die Berechtigungen nicht ändern. Andernfalls könnte dies zu einer Reihe von Problemen für jeden mit einem Home-Verzeichnis auf dem Host-Computer führen.

      Schritt 3 – Konfigurieren der NFS-Exporte auf dem Host-Server

      Als Nächstes nehmen wir uns die NFS Konfigurationsdatei vor, um die Freigabe dieser Ressourcen einzurichten.

      Öffnen Sie die Datei /etc/exports auf dem Host-Computer in Ihrem Texteditor mit root-Berechtigungen:

      Die Datei enthält Kommentare, die die allgemeine Struktur der einzelnen Konfigurationszeilen zeigen. Die Syntax lautet wie folgt:

      /etc/exports

      directory_to_share    client(share_option1,...,share_optionN)
      

      Wir müssen eine Zeile für jedes der Verzeichnisse erstellen, die wir freigeben möchten. Stellen Sie sicher, dass der hier gezeigte Platzhalter client_ip in Ihre tatsächliche IP-Adresse geändert wird:

      /etc/exports

      /var/nfs/general    client_ip(rw,sync,no_subtree_check)
      /home               client_ip(rw,sync,no_root_squash,no_subtree_check)
      

      Wir nutzen hier die gleichen Konfigurationsoptionen für beide Verzeichnisse mit Ausnahme von no_root_squash. Wir wollen uns ansehen, was jede dieser Optionen bedeutet:

      • rw: Diese Option gibt dem Client-Computer sowohl den Lese- als auch den Schreibzugriff auf das Laufwerk.
      • sync: Diese Option zwingt NFS, vor dem Antworten Änderungen auf die Festplatte zu schreiben. Dies führt zu einer stabileren und konsistenteren Umgebung, da die Antwort den tatsächlichen Zustand des entfernten Laufwerks widerspiegelt. Es reduziert jedoch auch die Geschwindigkeit der Dateioperationen.
      • no_subtree_check: Diese Option verhindert die Teilbaum-Prüfung, ein Prozess, bei dem der Host überprüfen muss, ob die Datei für jede Anfrage tatsächlich auch immer noch im exportierten Baum verfügbar ist. Dies kann bei der Umbenennung einer Datei viele Probleme verursachen, wenn der Client sie geöffnet hat. In fast allen Fällen ist es besser, die Teilbaum-Prüfung zu deaktivieren.
      • no_root_squash: Standardmäßig übersetzt NFS Anfragen von einem root-Benutzer remote in einen nicht berechtigten Benutzer auf dem Server. Dies war als Sicherheitsfunktion bestimmt, um zu verhindern, dass ein root-Konto auf dem Client das Dateisystem des Host als root verwendet. no_root_squash deaktiviert dieses Verhalten für bestimmte Freigaben.

      Wenn Sie mit diesen Änderungen fertig sind, speichern und schließen Sie die Datei. Um die Freigaben den von Ihnen konfigurierten Clients dann zur Verfügung zu stellen, starten Sie den NFS-Server mit dem folgenden Befehl neu:

      • sudo systemctl restart nfs-kernel-server

      Bevor Sie jedoch die neuen Freigaben tatsächlich nutzen können, müssen Sie sicher sein, dass der Verkehr zu den Freigaben durch Firewall-Regeln zugelassen ist.

      Schritt 4 – Anpassen der Firewall auf dem Host

      Wir wollen zuerst den Firewall-Status überprüfen, ob dieser aktiviert ist, und wenn ja, sehen, was derzeit zugelassen ist:

      Output

      Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)

      Auf unserem System ist nur SSH-Verkehr zugelassen. Daher müssen wir eine Regel für NFS hinzufügen.

      Bei vielen Anwendungen können Sie sudo ufw app list nutzen und sie nach dem Namen aktivieren, aber nfs gehört nicht dazu. Da ufw jedoch auch /etc/services für den Port und das Protokoll eines Dienstes überprüft, können wir auch weiterhin NFS nach dem Namen hinzufügen. Es wird empfohlen, die restriktivste Regel zu aktivieren, die den Datenverkehr, den Sie zulassen möchten, weiterhin erlaubt, statt Datenverkehr von überall her zuzulassen.

      Verwenden Sie den folgenden Befehl, um Port 2049 auf dem Host zu öffnen, wobei Sie Ihre Client-IP-Adresse ersetzen:

      • sudo ufw allow from client_ip to any port nfs

      Sie können die Änderung überprüfen, indem Sie Folgendes eingeben:

      Sie sollten der zulässigen Verkehr von Port 2049 in der Ausgabe sehen:

      Output

      Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 2049 ALLOW 203.0.113.24 OpenSSH (v6) ALLOW Anywhere (v6)

      Damit wird bestätigt, dass UFW nur NFS-Verkehr an Port 2049 von unserem Client-Computer zulassen wird.

      Schritt 5 — Erstellen von Bereitstellungspunkten und Bereitstellen von Verzeichnissen auf dem Client

      Nachdem der Host-Server konfiguriert ist und seine Freigaben bedient, bereiten wir unseren Client vor.

      Um die Remote-Freigaben auf dem Client verfügbar zu machen, müssen wir die Verzeichnisse auf dem Host, die wir freigeben möchten, in leere Verzeichnisse auf dem Client bereitstellen.

      Anmerkung: Wenn es Dateien und Verzeichnisse in Ihrem Bereitstellungspunkt gibt, werden sie versteckt, wenn Sie die NFS-Freigabe bereitstellen. Um den Verlust wichtiger Dateien zu vermeiden, stellen Sie sicher, dass das vorhandene Verzeichnis, in das Sie bereitstellen möchten, leer ist.

      Wir erstellen zwei Verzeichnisse für unsere Bereitstellungen:

      • sudo mkdir -p /nfs/general
      • sudo mkdir -p /nfs/home

      Nachdem wir nun einen Ort haben, um die remote Freigaben zu platzieren und die Firewall geöffnet haben, können wir die Berechtigungen mit der IP-Adresse unseres Host-Servers bereitstellen:

      • sudo mount host_ip:/var/nfs/general /nfs/general
      • sudo mount host_ip:/home /nfs/home

      Über diese Befehle werden die Freigaben vom Host-Computer auf dem Client-Computer bereitgestellt. Es gibt mehrere Möglichkeiten, um zu überprüfen, ob die Bereitstellung erfolgreich war. Sie können dies mit einem mount– oder findmnt-Befehl überprüfen, df -h bietet jedoch eine besser lesbare Ausgabe:

      Output

      Filesystem Size Used Avail Use% Mounted on udev 474M 0 474M 0% /dev tmpfs 99M 936K 98M 1% /run /dev/vda1 25G 1.8G 23G 8% / tmpfs 491M 0 491M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 491M 0 491M 0% /sys/fs/cgroup /dev/vda15 105M 3.9M 101M 4% /boot/efi tmpfs 99M 0 99M 0% /run/user/1000 10.132.212.247:/var/nfs/general 25G 1.8G 23G 8% /nfs/general 10.132.212.247:/home 25G 1.8G 23G 8% /nfs/home

      Beide bereitgestellten Freigaben erscheinen unten. Da sie von dem gleichen Dateisystem bereitgestellt wurden, zeigen sie die gleiche Festplattennutzung. Um zu sehen, wie viel Speicherplatz tatsächlich in jedem Bereitstellungspunkt genutzt wird, verwenden Sie den Bereitstellungsbefehl du und den Pfad der Bereitstellung. Die Markierung -s liefert eine Zusammenfassung der Nutzung und nicht die Darstellung der Verwendung jeder Datei. -h druckt eine lesbare Ausgabe.

      Beispiel:

      Output

      36K /nfs/home

      Das zeigt uns, dass der Inhalt des gesamten Home-Verzeichnisses nur 36K des verfügbaren Platzes verwendet.

      Schritt 6 — Testen von NFS

      Als Nächstes testen wir den Zugriff auf die Freigaben, indem wir in jede von ihnen schreiben.

      Beispiel 1: Die Allzweck-Freigabe

      Schreiben Sie als erstes eine Testdatei in die Freigabe /var/nfs/general:

      • sudo touch /nfs/general/general.test

      Überprüfen Sie dann den Eigentümer:

      • ls -l /nfs/general/general.test

      Output

      -rw-r--r-- 1 nobody nogroup 0 Aug 1 13:31 /nfs/general/general.test

      Da wir dieses Volumen bereitgestellt haben, ohne das Standardverhalten von NFS zu ändern, und die Datei als root-Benutzer des Client-Computers über den sudo-Befehl erstellt haben, ist der Standardeigentümer nobody:nogroup. Client-Superuser können keine typischen administrativen Aktionen ausführen, wie z. B. das Ändern des Dateieigentümers oder das Erstellen eines neuen Verzeichnisses für eine Gruppe von Benutzern auf der NFS-bereitgestellten Freigabe.

      Beispiel 2: Die Home-Verzeichnis-Freigabe

      Um die Berechtigungen der Allzweck-Freigabe mit der Home-Verzeichnis-Freigabe zu vergleichen, erstellen Sie eine Datei auf gleiche Weise in /nfs/home:

      • sudo touch /nfs/home/home.test

      Sehen Sie sich dann den Eigentümer der Datei an:

      • ls -l /nfs/home/home.test

      Output

      -rw-r--r-- 1 root root 0 Aug 1 13:32 /nfs/home/home.test

      Wir haben home.test als root über den Befehl sudo erstellt, genau so, wie wie wir auch die Datei general.test erstellt haben. In diesem Fall ist der Eigentümer jedoch root, da wir das Standardverhalten überschrieben haben, als wir die Option no_root_squash in dieser Bereitstellung angaben. Dadurch können unsere root-Benutzer auf dem Client-Computer als root agieren, was die Verwaltung von Benutzerkonten wesentlich einfacher macht. Gleichzeitig bedeutet es, dass wir diesen Benutzern keinen root-Zugriff auf dem Host geben müssen.

      Schritt 7 — Bereitstellen der Remote-NFS-Verzeichnisse beim Boot

      Wir können die Remote-NFS-Freigaben automatisch beim Boot bereitstellen, indem wir sie in die /etc/fstab-Datei auf dem Client hinzufügen.

      Öffnen Sie diese Datei mit root-Berechtigungen in Ihrem Texteditor:

      Fügen Sie am Ende der Datei eine Zeile für jede unserer Freigaben hinzu Sie werden ungefähr so aussehen:

      /etc/fstab

      . . .
      host_ip:/var/nfs/general    /nfs/general   nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0
      host_ip:/home               /nfs/home      nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0
      
      

      Anmerkung: Sie können mehr über die Optionen, die wir hier angeben, auf der NFS-Hauptseite finden. Sie können auf diese zugreifen, indem Sie den folgenden Befehl ausführen:

      Der Client wird die Remote-Partitionen automatisch beim Start bereitstellen. Es kann jedoch einige Momente dauern, bis die Verbindung hergestellt ist und die Freigaben verfügbar sind.

      Schritt 8 — Entfernen einer NFS-Remote-Freigabe

      Wenn Sie nicht mehr möchten, dass das Remote-Verzeichnis auf Ihrem System bereitgestellt wird, können Sie es entfernen, indem Sie die Verzeichnisstruktur der Freigabe folgendermaßen verlassen und entfernen:

      • cd ~
      • sudo umount /nfs/home
      • sudo umount /nfs/general

      Beachten Sie, dass der Befehl umount, nicht unmount lautet, wie Sie möglicherweise erwarten.

      Dadurch werden die Remote-Freigaben entfernt, sodass nur noch Ihr lokaler Speicher zugänglich ist:

      Output

      Filesystem Size Used Avail Use% Mounted on udev 474M 0 474M 0% /dev tmpfs 99M 936K 98M 1% /run /dev/vda1 25G 1.8G 23G 8% / tmpfs 491M 0 491M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 491M 0 491M 0% /sys/fs/cgroup /dev/vda15 105M 3.9M 101M 4% /boot/efi tmpfs 99M 0 99M 0% /run/user/1000

      Wenn Sie verhindern möchten, dass sie beim nächsten Neustart wieder hergestellt werden, bearbeiten Sie /etc/fstab und löschen oder kommentieren Sie die Zeile, indem Sie ein #-Zeichen am Anfang der Zeile einfügen. Außerdem können Sie eine automatische Bereitstellung verhindern, indem Sie die Option auto entfernen. Sie können immer noch eine manuelle Bereitstellung vornehmen.

      Zusammenfassung

      In diesem Tutorial haben wir einen NFS-Host erstellt und einige wichtige Verhaltensweisen von NFS gezeigt, indem wir zwei verschiedene NFS-Bereitstellungen erstellt haben, die wir mit einem NFS-Client teilten.

      Wenn Sie NFS in der Produktion implementieren möchten, ist es wichtig, zu beachten, dass das Protokoll selbst nicht verschlüsselt ist. In Fällen, in denen Sie über ein privates Netzwerk freigeben, ist dies kein Problem. In anderen Fällen ist ein VPN oder ein anderer verschlüsselter Tunnel erforderlich, um Ihre Daten zu schützen.



      Source link