Home Restore einer RDS Instanz bei AWS
Post
Cancel

Restore einer RDS Instanz bei AWS

Mein altes Verständins bzgl. eines Datenbankrestores ware bislang ja folgender:
Wir haben ein Datenbankdump.
Wir haben eine installierten und fertig konfigurierten Datenbankservice.
Wir können den Dump mit Bordmittel in den Service drücken.
Fertig.

Und sobald man diese AWS Welt betritt, muss man alles über den Haufen werfen, weil nichts mehr so funktioniert, wie man es jahrelang vorher gemacht hat.

Wenn man anfängt nach einem ähnlichen Szenario für eine RDS (das ist der Datenbankendpunkt bei AWS) zu suchen, wird es sehr schnell sehr dünn, was hilfreiche Informationen betrifft.

Wenn man noch Glück hat, bekommt man eine Bilderstecke, wie man sich das im Web zusammenklickt.
Wenn man richtig Pech hat, ein YouTube Video, bei dem man mit “freundlichen Produktempfehlungen meiner Partner” zugeschissen wird.

Ganz selten bekommt man etwas, was man in der bash ausführen kann.

Zielsetzung

Meine Zielsetzung war und ist es, ein RDS Backup von einer produktiven Umgebung in die Abnahmeumgebung zu übertragen.
In einer on-premise Installation .. siehe oben.
Dafür hat wohl jeder Admin “sein” Script in seiner Toolbox.

Aber wir sind bei AWS …

Erkenntnisse

Die erste Erkenntniss die ich hatte: So geht das mal gar nicht!
Ein RDS Backup ist kein Dump im herkömmlichen Sinne, sondern eher ein Snapshot der Instanz in der die konfigurierten Ressourcen gespeichert werden, plus den Daten.
Jetzt macht der Restore aber wenig Sinn, wenn ich die QA Instanz mit deutlich anderen Settings erstellt habe! Vielleicht gibt es dafür auch noch eine Lösung.

Also anderer Ansatz.
Bei dem restore eines RDS Backups wird eine komplett neue Instanz aus dem Boden gestampft. Je nach Umfang geht das ziemlich zügig, oder dauert halt ein paar Minuten.

Die neue Instanz wird parallel zu den bestehenden erstellt, ein Schwenk von A nach B wäre also problemlos möglich.

DB Snapshots

Wir brauchen also als erstes die ID eines bestehenden Snapshots bzw. Backups.

Mit den aws Tools sollte man das ungefähr so hinbekommen:

1
2
aws rds describe-db-snapshots \
  --query 'DBSnapshots[].DBSnapshotArn'

Prima. Ganz viel Ausgabematsch. Das müssen wir also noch etwas eindampfen!

Da ich weiß, von welcher Instanz ich den Restore benötige, kann ich auch die Instanz-ID als Filter benutzen.
(ACHTUNG describe-db-snapshots kennt nur wenige spezielle Filter! Hier hilft die entsprechende Doku weiter!)

1
2
3
aws rds describe-db-snapshots \
  --filters "Name=db-instance-id,Values=prod-1" \
  --query 'DBSnapshots[].DBSnapshotArn'

Und das sieht dann schon weitaus besser aus. Allerdings ist der letzte Dump mein Ziel. Daher werfen wir den ganzen Output noch jq vor die Füße und Filtern weiter:

1
2
3
SNAPSHOT=$(aws rds describe-db-snapshots \
  --filters "Name=db-instance-id,Values=prod-1" \
  --query 'DBSnapshots[].DBSnapshotArn' | jq --raw-output '.[-1:] | .[]')

Et voilà, wir haben das letzte Backup!

Noch mehr Informationen

Neben dem Snapshot benötigen wir noch weiter Informationen, die wir uns besorgen, bzw. definieren müssen:

  • instance_identifier
  • subnet_group_name
  • parameter_group_name
  • vpc_security_group_ids

Da ich in meinen ersten Versuchen keinen effizienteren Weg gefunden habe, gehe ich das ganze etwas Hemdsärmelig an:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DB_INSTANCE_ID=$(aws rds describe-db-snapshots \
  --db-snapshot-identifier ${SNAPSHOT} \
  --query 'DBSnapshots[].DBInstanceIdentifier' | jq --raw-output '.[]')

DB_INSTANCE=$(aws rds describe-db-instances \
  --db-instance-identifier ${DB_INSTANCE_ID} \
  --query 'DBInstances[]')

DBSubnetGroupName=$(
  echo "${DB_INSTANCE}" | jq --raw-output '.[].DBSubnetGroup.DBSubnetGroupName'
)

DBParameterGroupName=$(
  echo "${DB_INSTANCE}" | jq --raw-output '.[].DBParameterGroups[].DBParameterGroupName'
)

VpcSecurityGroupId=$(
  echo "${DB_INSTANCE}" | jq --raw-output '.[].VpcSecurityGroups[].VpcSecurityGroupId'
)

Damit haben wir alle Informationen zusammen, die wir für einen Restore benötigen.

Instanz aus einem Snapshot erstellen

Das erstellen einer neuen RDS Instanz ist jetzt nur noch ein Kinderspiel.

Wir denken uns einen tollen Namen für die neue Instanz aus und definieren hier unseren instance_identifier

1
2
3
4
5
6
7
export instance_identifier="RDS_RESTORE_$(date +"%Y%m%d-%H%M%S")"

aws rds restore-db-instance-from-db-snapshot \
  --db-subnet-group-name ${DBSubnetGroupName} \
  --db-instance-identifier "${instance_identifier}" \
  --db-snapshot-identifier ${SNAPSHOT}

Wie ich bereits weiter oben beschrieb, kann so ein Restore etwas Zeit kosten. Also müssen wir warten, bis wir weitere Aktionen ausführen können.

Time to wait

Wir benötigen im gesamten Verlauf ein paar Mal etwas Ruhe und müssen sicherstellen, dass unsere Aktionen auch abgeschlossen wurden.
Daher definieren wir uns 2 bash Funktionen:

  • wait_until_instance_status
  • wait_until_instance_deleted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
wait_until_instance_status() {
  status=unknown
  while [ "${status}" != "available" ]
  do
    sleep 20
    status=$(aws rds describe-db-instances \
      --db-instance-identifier "${instance_identifier}" | jq -r '.DBInstances[0].DBInstanceStatus')
  done
}

wait_until_instance_deleted() {
  count=1
  while [ "$count" != "0" ]
  do
    sleep 30
    count=$(aws rds describe-db-instances \
      --db-instance-identifier "STAGE_INSTANZ" 2>/dev/null | grep -i dbinstancestatus | wc -l)
  done
}

Bei wait_until_instance_status überprüfen wir alle 20 Sekunden, ob unsere Instanz zur Verfügung steht.
Ja, sie ist nicht sauber, da hier eine “deadloop” entstehen kann, wenn die Instanz nie den Status available erreicht. Aber für meinen Zweck macht sie das was sie soll. Und mit etwas positiven … lassen wir das :)

Modifizieren der neuen Instanz

Wenn die neue Instanz haben, können wir uns freuen. Und auch nicht.

Denn erreichbar ist sie nicht, da ihr die entsprechenden Konfigurationen, z.B. die Security Groups fehlen!

Die hatten wir uns aber schon weiter oben besorgt, also können wir unsere Instanz jetzt damit modifizieren:

1
2
3
4
aws rds modify-db-instance \
  --db-instance-identifier "${instance_identifier}" \
  --vpc-security-group-ids ${VpcSecurityGroupId} \
  --db-parameter-group-name ${DBParameterGroupName}

Ab jetzt ist die RDS wieder nicht erreichbar, was für uns kein Problem darstellen dürfte, da wir sie bislang auch noch nicht einmal benutzen konnten. Aber, wir müssen wieder warten, bevor wir weiter machen können. Dafür nutzen wir einfach unsere Funktion von eben:

1
wait_until_instance_status

Dem aufmerksamen Leser ist jetzt mit Sicherheit ein Problem aufgefallen:

Die Security Group sind nicht die von unserer Ziel-, sondern von der Quellumgebung!

Damit könnte auch das gewollte Environment nicht auf die RDS Instanz zugreifen.

RICHTIG ! Aber ich denke, es sollte jetzt logisch und klar sein, wie man die Security Group der Zielumgebung erhalten kann.

Nachbearbeitung

Wir haben eine neue RDS Instanz! YEAH
Und doch muss ich den Jubel kurz einmal unterbrechen.

Wir haben eine NEUE Instanz!
Die kam jetzt zusätzlich hinzu.

Das sind 2 Probleme auf einmal:

  • die neue Instanz stellt einen neuen Endpunkt zur Verfügung.
  • wir müssen eine der alten Instanz entfernen bzw. still legen.

Wenn wir bislang mit den bestehenden Endpunktnamen gearbeitet haben, müssen wir jetzt alle Applikationen neu konfigurieren und diese neu starten.
Um einen Neustart werden wir so oder so nicht herum kommen ..

Eine Lösung hierfür wäre ein CNAME im DNS anzulegen.
Wer einen eigenen DNS pflegt, möge sich mit dessen API auseinandersetzen.
Wer DNS auf die /etc/hosts beschränkt (ja, so etwas gibt es noch immer) möge eine neue Datei erstellen und diese über sein Deploymenttool der Wahl ausrollen.

AWS bietet mit route53 einen eigenen DNS Service an, damit die Kundenbindung noch enger wird.

In jedem der Fälle benötigen wir den neuen Endpunktnamen:

1
2
3
aws rds describe-db-instances \
  --db-instance-identifier "${instance_identifier}" \
  --query 'DBInstances[].Endpoint'

Nach dem wir den CNAME aktualisiert haben, müssen wir nur noch unsere alte, nicht mehr gewollte, RDS Instanz löschen.

1
2
3
aws rds delete-db-instance \
  --db-instance-identifier "STAGE_INSTANZ" \
  --skip-final-snapshot > /dev/null 2>&1

Da auch hier warten angesagt ist, nutzen wir die weiter oben bereits definiere Funktion:

1
wait_until_instance_deleted

offene Punkte

Mit all den Schritten kann ich eine RDS Instanz aus einem gewünschten Snapshot wiederherstellen.
Mein aktuelles Problem sind die Ressourcen, die an einer Instanz gebunden sind.

Während Produktion auf Leistung ausgelegt ist, sind Stage oder QA Instanzen deutlich kleiner und kostengünstiger.
Ich muß jetzt also die Ressourcen der neuen Instanz nachbessern.

Wenn ich einmal alle meine Probleme beseitigt habe, muss das Skript noch in eine CI/CD Umgebung integriert werden, damit ich nicht der einzige bin, der den Task ausführen kann und - wenn gewünscht - das ganze einmal im Monat automagisch stattfindet.

Und wenn ich das alles geschafft habe, werfe ich das mal in ein gist.

This post is licensed under CC BY 4.0 by the author.