Kontakt: +49 511 59095 – 942

Mit allen verbunden Teil 2

Thumbnail

Infrastrukturtest des Transit Gateway mit kitchen und Inspec

Transit Gateway (What is a Transit Gateway? – Amazon Virtual Private Cloud) ist ein neuer AWS Service, der VPC Peering komplett ablösen kann. Zusätzlich kann damit auch ein „transitives” Peering gebaut werden. Das heißt, anders als bei einer VPC Peering Verbindung, die immer nur zwei VPCs miteinander verbindet, werden hier alle Netze miteinander verbunden. Das lohnt es sich genau zu testen! Diesen Test will ich hier möglichst vollautomatisiert aufbauen.

Dafür brauche ich zwei BlogPosts.

Teil 1 war “Test von CloudFormation mit taskCat”.

Dies ist Teil 2: Test der Transit-Gateway Infrastruktur mit Inspec.

Der Versuchsaufbau

Versuchsaufbau)

Zuerst baut kitchen aus dem Chef Framework für uns die Testinstanz auf. Dann definieren wir den Test selber mit inspec. Nach einer kurzen Betrachtung der testgetriebenen Entwicklung bauen wir letztendlich das Transit Gateway und testen die Infrastruktur.

Automatischer Aufbau von Testinstanzen mit kitchen

Kitchen ist der Test Controller vom Chef Framework. Die sprachliche Analogie vom (Küchen) Chef findet sich an vielen Stellen wieder, so auch hier. Andere Tools heißen z.B. “_Knife_” oder “_foodcritic_”.

Mehr zu Chef unter Chef.io.

Um zu Testen baut kitchen Instanzen auf (create), und bereitet diese auf den Test vor. Danach kann ich mit “verify” Test ausführen. Diese Test sind in einer eigenen DSL geschrieben, inspec genannt.

Doch zuerst zur Kücheneinrichtung:

Konfiguration von kitchen

In der Konfigurationsdatei “.kitchen.yml” konfiguriere ich, wie kitchen mit AWS zusammenspielt. Zuerst teile ich kitchen mit, was für eine Art von Instanz erstellt werden soll. Ich kann z.B. auch Vagrant oder docker statt des AWS Treibers verwenden, wenn ein lokaler Test ausreicht. Für den Test der AWS Infrastruktur verwende ich den kitchen-ec2 Treiber.

Driver

---
driver:
  name: ec2
  region: eu-central-1

Der driver ist also ec2. Zusätzlich definiere ich die Region, hier Frankfurt. Der EC2 Driver hat ein eigenes Github Projekt. Zu Anfang definiere ich den Standard Driver. In der platform Sektion kann ich einen anderen Treiber definieren. Hier verwende ich die Platform Daten, um weitere Konfigurationen vorzunehmen.

Provisioner

provisioner:
  name: chef_zero
  always_update_cookbooks: true

Der provisioner wird nur benötigt, wenn ich Chef Rezepte teste. Daher wird er hier nicht verwendet. Da Chef normalerweise eine Client Server Infrastruktur verwendet, würde bei einem Test von Chef Konfigurationen bzw. Kochbüchern cookbooks mit dem Provisioner chef_zero der Server lokal simuliert werden.

Verifier

Der verifier führt die eigentlichen Tests aus.

verifier:
  name: inspec

Für die verify Phase wird inspec definiert.

Transport

transport:
 username: ["ec2-user"]

Der Datentransport kann in Kitchen mit ssh oder winrm passieren. Hier definieren wir den richtigen Usernamen für das AMI.

Platform Definition

platforms:
  - name: amazon
    driver: 
      region: eu-central-1
      image_id: ami-0cfbf4f6db41068ac
      instance_type: t2.large
      subnet_id: subnet-01cb511f85c1b2356
      tags:
        Name: "Kitchen Test Instance ec2"

Hier setze manuell ich die Subnet ID aus dem erzeugten VPC/Subnet A ein.

Testsuites

Hier können verschiedene Test in “Suites” zusammengefasst werden. Für dieses einfache Beispiel verwende ich nur die Standard suite.

suites:
  - name: default

Ablauf des Test

Wenn ihr also den Test nachbauen wollt, muss Folgendes in .kitchen.yaml angepasst werden:

  • Die Region
  • Die Subnet ID

Definition von Test mit der Inspec DSL

Für die Definition der Tests gibt es eine spezielle DSL Sprache, die viele Testarten vordefiniert hat. Hier verwende ich diese Sprache inspec (Inspec Webseite). Genau genommen ist es in ruby geschrieben, baut aber eine eigene Sprache oben drauf.

Der Vorteil dabei ist, dass viele Resourcentypen bereits vordefiniert sind. Inspec definiert dabei auch viele AWS Resourcen, wie vpc, subnet, s3 usw. Zusammen mit kitchen werden die notwendigen Test Instanzen automatisch aufgebaut.

Da ich hier nur die Erreichbarkeit der Instanzen teste, kann ich die Resource http verwenden. Ein einfaches Beispiel ist:

describe http('http://localhost') do
  its('status') { should cmp 200 }
End

Der code erschließt sich so sehr einfach. Abgefragt wird die URL http://localhost und die Antwort wird auf den Status geprüft. Natürlich kann ich viele andere Attribute prüfen. (Siehe Dokumentation inspec Resourcen) Für Netz A definiere ich diesen Test in test/integration/default/default_test.rb:

describe http('http://10.0.1.16') do
  its('status') { should cmp 200 }
  its('body') { should eq 'Node A' }
end

Der Webserver auf der Instanz in VPC a wird auf Erreichbarkeit geprüft und der Inhalt wird auf den Text “Node A” geprüft. Dies korrespondiert mit der UserData der Instanz:

UserData:
  Fn::Base64: |
    #!/bin/bash -xe
    yum update -y
    yum install -y httpd24
    service httpd start
    chkconfig httpd on
    echo "Node A" >/var/www/html/index.html

Test 1) Der Webserver auf Instanz 10.0.1.16 in VPC A wird auf Port 80 innerhalb des VPC A getestet.

Ablauf beim Testen

Die grundsätzliche Philosophie des testgetriebenen Vorgehens ist, dass man sich vor der Erstellung der Funktionalität überlegt, was denn das Ergebnis der Funktion (/des Programms/der Infrastruktur) sein sollte.

Rot

Dann erstelle ich einen Test, der dann bei der ersten Ausführung fehlschlagen muss, also “Rot” ergibt. Das ist genau das erwartete Ergebnis, da die Funktionalität ja noch nicht vorhanden ist.

Grün

Jetzt baue ich die Funktionalität und teste immer wieder, bis die Funktion meinen Erwartungen entspricht, also “Grün” ist.

Umbau

Jetzt habe ich eine Funktion und einen automatisierten Test, dass diese Funktion ihren Zweck erfüllt. Jetzt kann ich unter der Haube umbauen und mit dem Test sicherstellen (verify Phase), dass die Funktionalität immer noch gegeben ist.

Lets Test

Alle Vorarbeiten und Vorüberlegungen sind abgeschlossen, wir können loslegen. Die meisten Schritte sind in einem Makefile automatisiert.

Eine Übersicht der Schritte bekommt man mit make help. Der Ablauf des Test in der Übersicht:

1) VPCs erstellen: make deploy

2) Region und Subnet ID von VPC A in .kitchen.yml updaten

3) Testumgebung aufbauen: make start-kitchen

4) Test ausführen: make test-kitchen

Ohne VPC Peering und Transit Gateway bekommen ich dann z.B. folgende Ausgabe vom Test:

 http GET on http://10.0.1.16
     ✔  status should cmp == 200
     ✔  body should eq "Node A"
  http GET on http://10.0.2.16
     ×  status should cmp == 200

     expected: 200
          got: nil

     (compared using `cmp` matcher)

     ×  body should eq "Node B"

     expected: "Node B"
          got: nil

     (compared using ==)

  http GET on http://10.0.3.16
     ×  status should cmp == 200

     expected: 200
          got: nil

     (compared using `cmp` matcher)

     ×  body should eq "Node C"

     expected: "Node C"
          got: nil

     (compared using ==)


Test Summary: 2 successful, 4 failures, 0 skipped

Im lokalen Netz A, in dem auch die Testinstanz läuft ist der Zugriff auf 10.0.1.16 möglich, auf die anderen Netze noch nicht.

Test A OK

Nun endlich kann ich das Transit Gateway als Verbindung konfigurieren.

Transit Gateway

Dazu brauche ich folgende Komponenten:

  • das Transit Gateway selber
  • Pro VPC ein Transit Gateway Attachment
  • Routen in den VPC zu den anderen VPCs

Das Transit Gateway (TGW)ist der Knotenpunkt, der Router der alles verbindet. Um eine Verbindung vom VPC zu dem TGW zu bekommen, muss ein VPC mit dem TGW verbunden werden. Diese Verbindung ist das Transit Gateway Attachment. Als letztes Element muss man im VPC die Subnets, die mit den anderen VPS verbunden werden sollen mit Routen versorgen. Die Routen Tabellen hängen am Subnet, nicht am VPC.

Ein Gesamtdurchlauf

Zusammen mit den VPCs aus Teil 1 können wir jetzt einen Gesamtdurchlauf machen:

Tools installieren

  • ChefDK
  • Clouds-aws
  • Aws cli

VPCs aufbauen

Eingabe: make deploy Dauer: ca. 1min

clouds  -r eu-central-1 update -c --events  demo-vpc-b &
clouds  -r eu-central-1 update -c --events   demo-vpc-c &
clouds  -r eu-central-1 update -c --events   demo-vpc-a
2018-12-30/15:17:08 CREATE_IN_PROGRESS  AWS::CloudFormation::Stack  demo-vpc-a  User Initiated
2018-12-30/15:17:08 CREATE_IN_PROGRESS  AWS::CloudFormation::Stack  demo-vpc-b  User Initiated
2018-12-30/15:17:08 CREATE_IN_PROGRESS  AWS::CloudFormation::Stack  demo-vpc-c  User Initiated
2018-12-30/15:17:11 CREATE_IN_PROGRESS  AWS::EC2::VPC               VPC
2018-12-30/15:17:11 CREATE_IN_PROGRESS  AWS::EC2::InternetGateway   InternetGateway
2018-12-30/15:17:11 CREATE_IN_PROGRESS  AWS::EC2::VPC
...

In der AWS Console sehe ich nun die drei VPCs:

3 VPCs

Vom Subnet des VPCs A merke ich mir die ID:

Subnet A

Die ID trage ich in die .kitchen.yml ein:

kitchen

Kitchen Testinstanz aufbauen

Vorbereiten der Verwendung ChefDK auf Bash:

eval "$(chef shell-init bash)"

Eingabe: make start-kitchen Dauer: ca. 1. Min.

kitchen create
-----> Starting Kitchen (v1.23.3)
WARN: Unresolved specs during Gem::Specification.reset:
      concurrent-ruby (~> 1.0)
WARN: Clearing out unresolved specs.
Please report a bug if this causes problems.
-----> Creating <default-amazon>...
       Detected platform: amazon version 2018.03. on x86_64. Instance Type: t2.large. Default username: ec2-user (default).
       If you are not using an account that qualifies under the AWS
free-tier, you may be charged to run these suites. The charge
should be minimal, but neither Test Kitchen nor its maintainers
are responsible for your incurred costs.

       Created automatic security group sg-0bcd26ac38ceea95c
       Created automatic key pair kitchen-defaultamazon-silberkopf-GernotsMBPfritzbox-2018-12-30T15:23:20Z-u9w48qe6
       Instance <i-09467b6ad93ddda02> requested.
       Polling AWS for existence, attempt 0...
       Attempting to tag the instance, 0 retries
       EC2 instance <i-09467b6ad93ddda02> created.
       Waited 0/300s for instance <i-09467b6ad93ddda02> volumes to be ready.
       Waited 0/300s for instance <i-09467b6ad93ddda02> to become ready.
       Waited 5/300s for instance <i-09467b6ad93ddda02> to become ready.
       Waited 10/300s for instance <i-09467b6ad93ddda02> to become ready.
       Waited 15/300s for instance <i-09467b6ad93ddda02> to become ready.
       Waited 20/300s for instance <i-09467b6ad93ddda02> to become ready.
       EC2 instance <i-09467b6ad93ddda02> ready (hostname: 35.159.15.219).
       Waiting for SSH service on 35.159.15.219:22, retrying in 3 seconds
       Waiting for SSH service on 35.159.15.219:22, retrying in 3 seconds
       Waiting for SSH service on 35.159.15.219:22, retrying in 3 seconds
       Waiting for SSH service on 35.159.15.219:22, retrying in 3 seconds
       [SSH] Established
       Finished creating <default-amazon> (0m41.87s).
-----> Kitchen is finished. (0m48.18s)

Erster Test

Eingabe: make test-kitchen

kitchen verify
-----> Starting Kitchen (v1.23.3)
WARN: Unresolved specs during Gem::Specification.reset:
      concurrent-ruby (~> 1.0)
WARN: Clearing out unresolved specs.
Please report a bug if this causes problems.
-----> Converging <default-amazon>...
...
  http GET on http://10.0.1.16
     ✔  status should cmp == 200
     ✔  body should eq "Node A"
  http GET on http://10.0.2.16
     ×  status should cmp == 200
...
Test Summary: 2 successful, 2 failures, 0 skipped

Da die kitchen Test instanz im VPC A erstellt wurde, kann sie auf den Webserver in VPC A zugreifen, nicht aber in B und C.

Transit Gateway erstellen

In der AWS Console:

  • VPC-Transit Gateways (ganz unten links)
    • Create Transit Gateway

Name Tag: “demo TGW” Description: “demo TGW” Alle anderen Einstellungen können im Standard bleiben. Jetzt dauert es ein paar Minuten, bis das TGW erstellt wurde.

demo TGW

Wie auch bei der EC2 Konsole, wenn man eine Instanz erstellt, kann Klick auf den Refresh Knopf ab und zu nicht schaden.

Refresh

Transit Gateway Attachment A erstellen

In der AWS Console:

  • VPC-Transit Gateways (ganz unten links)
    • Create Transit Gateway Attachment

Transit Gateway ID: Eben erstelltes TGW wählen Attachment type: VPC Attachment name tag: Demo A VPC ID: VPC Demo A wählen.

  • Create Attachment klicken.

Sobald das TGW Attachment in den Status “available” wechselt, erscheinen automatisch in der TGW Route Table unter “Associations”, “Propagations” und “Routes” Einträge.

Transit Gateway Attachment B erstellen

Das Gleiche wird jetzt für VPC B wiederholt: In der AWS Console:

  • Service VPC wählen
  • VPC-Transit Gateways (ganz unten links)
    • Create Transit Gateway Attachment

Transit Gateway ID: Eben erstelltes TGW wählen Attachment type: VPC Attachment name tag: Demo B VPC ID: VPC Demo B wählen.

Test erneut ausführen

Da VPC A und B jetzt am TGW hängen, erwartet man ja irgendwie, dass die beiden sich jetzt unterhalten können… Die Ausführung des Kitchen Test make test-kitchen belehrt uns eines Besseren: A und B sehen sich noch nicht!

Test Summary: 2 successful, 2 failures, 0 skipped

Was fehlt? Es fehlt der Rückweg! Oder der Hinweg, je nachdem wie man es betrachtet. Wir müssen noch in den Routentabellen von den Subnetzen in VPC A und B Routen für das TGW erstellen.

Route für VPC A erstellen

In der AWS Console:

  • VPC-Route Tables (oben links)
    • Demo_A Public Routes wählen
      • Routes/Edit Routes
      • Route hinzufügen: Destination: 10.0.0.0/16 Target: Eben erstelltes Transit Gateway

Die Tabelle sieht danach in etwa so aus:

Tabelle

Auch jetzt ergibt der Test noch “Rot”. Erst wenn wir auch die Route vom VPC B zum TGW hinzufügen, klappt es:

 http GET on http://10.0.1.16
     ✔  status should cmp == 200
     ✔  body should eq "Node A"
  http GET on http://10.0.2.16
     ✔  status should cmp == 200
  http GET on http://10.0.3.16
     ×  status should cmp == 200

     expected: 200
          got:

     (compared using `cmp` matcher)


Test Summary: 3 successful, 1 failure, 0 skipped

Und damit steht die Verbindung zwischen den VPCs! Durch das Testgetriebene Vorgehen konnten wir an jeder Stelle mit make test-kitchen testen, ob die IP Verbindung steht und die Routen sowie die Security Groups usw. korrekt eingerichtet sind. Ohne Testen ist es so etwas wie ein Blindflug.

Deswegen gilt auch für Infrastrukturprojekte: Test first! U

nd um das Nachbauen einfacher zu machen, haben wir den Code auf github veröffentlicht:

tecracer/demo-transit-gateway

Benötigen Sie Unterstützung bei Ihrem AWS oder Chef Projekt? Kontaktieren sie uns gerne hier.