---
title: "VMware-VMs mit Coriolis nach STACKIT migrieren"
description: "Migrieren Sie VMware-VMs mit Coriolis nach STACKIT: Endpoints konfigurieren, Disks übertragen, neue Ziel-VMs testen, Änderungen synchronisieren und kontrolliert umschalten."
scfAsset:
  managed: false
  category: "guide"
  external: false
  tags: ["design-and-mobilize", "use-cases", "relocate", "vmware", "coriolis", "spring-boot", "postgresql", "automation"]
  maintainers:
    - user: "lukas.weberruss"
      role: true
      website: true
source_url: "https://framework.stackit.cloud/de/migration/assetcontainer/stackit/relocate-automation-vmware-coriolis/"
source_file: "docs/de/migration/assetcontainer/stackit/relocate-automation-vmware-coriolis.mdx"
---

## Migrationsarchitektur verstehen

Coriolis kopiert VMware-VM-Disks nach STACKIT, passt das Gastbetriebssystem an die virtuelle
Zielhardware an und startet die migrierte VM in einem STACKIT-Projekt. Anwendung und Datenbank
bleiben in der VM. Dies ist eine Relocate-Migration, kein Neuaufbau der Anwendung und kein Wechsel
zu einer verwalteten Datenbank.

Folgen Sie dieser Reihenfolge: Quelle und Ziel vorbereiten, Coriolis mit beiden Plattformen
verbinden, Disks übertragen, ein isoliertes Ziel testen, Änderungen synchronisieren und den
finalen Cutover durchführen. Die API-Beispiele verwenden `curl` und `jq`; die Python-Helfer des
Beispiel-Repositorys sind nicht erforderlich.

### Datentransfer und VM-Bereitstellung trennen

Coriolis trennt **Diskdaten übertragen** von **einer neuen Ziel-VM bereitstellen**. Ein Transfer
definiert Quelle und Zieleinstellungen. Jede Execution schreibt die vollständigen Diskdaten oder
später geänderte Blöcke auf Ziel-Volumes. Eine abgeschlossene Kopie startet noch keine Anwendungs-VM.

Ein Deployment verwendet den übertragenen Diskzustand, klont ihn für die Test- und finalen Server
dieser Anleitung, passt das Gastbetriebssystem an und erstellt einen neuen STACKIT-Server. Es
bildet den Workload auf neuer virtueller Hardware ab; weder das ursprüngliche ESXi-VM-Objekt noch
dessen CPU-/RAM-Hardware werden unverändert verschoben. Das Quell-Sizing ist die Grundlage für
eine ausdrückliche Zuordnung zu einem STACKIT-Maschinentyp.

```d2
direction: right
source: "VMware-Quelle" {
  grid-columns: 1
  vm: "Laufende VM\nAnwendung + Datenbank"
  disks: "Quell-VMDKs" { shape: cylinder }
}
transfer: "1. Datentransfer" {
  grid-columns: 1
  copy: "Erstkopie + Delta-Executions"
  volumes: "Synchronisierte STACKIT-Volumes" { shape: cylinder }
}
deployment: "2. VM-Bereitstellung" {
  grid-columns: 1
  clones: "Geklonte Deployment-Volumes" { shape: cylinder }
  morphing: "Gast-OS / VirtIO / Boot anpassen"
  vm: "Neue STACKIT-VM\nZugeordnete CPU + RAM, NICs, Firmware"
}
source -> transfer: "Quelldisks lesen"
transfer -> deployment: "Deployment-Anfrage"
```

| Phase | Eingabe | Ergebnis | Was getrennt bleibt |
| --- | --- | --- | --- |
| Transfer-Execution | Disks der Quell-VM und Transfer-Definition. | Kopierte/synchronisierte Ziel-Volumes und Integritätsnachweise. | Finale VM-Erstellung, Gastabnahme und Verkehrsumschaltung. |
| Deployment | Vollständig übertragener Diskzustand und freigegebene Zielzuordnungen. | Neuer Server mit Deployment-Volumes, Ziel-NICs und angepasstem Gastbetriebssystem. | Anwendungs-/Datenabnahme und produktiver Cutover. |

Ein vorhandenes geklontes Test-Deployment bildet einen bestimmten Zeitpunkt ab. Spätere Executions
aktualisieren die übertragenen Volumes, nicht die Disks dieser Test-VM. Erstellen Sie ein neues
Deployment, um eine neuere Synchronisation zu testen. Mit `auto_deploy: false` fordern Sie jede
Disk-Synchronisation und jede VM-Erstellung ausdrücklich getrennt an.

### Migrationsobjekte und Rollen

| Begriff | Bedeutung |
| --- | --- |
| Coriolis-Appliance | Installierte Coriolis-Steuerung mit Weboberfläche, Identitätsdienst, Migrations-API und Workern. |
| Provider | Coriolis-Adapter einer Plattform: `vmware_vsphere` für VMware und `stackit` für STACKIT. |
| Endpoint | Gespeicherte Verbindung zur Quell- oder Zielplattform mit Zugangsdaten und Provider-Einstellungen. |
| Coriolis-Projekt | Berechtigungsbereich für Endpoints und Migrationsaufträge; kein STACKIT-Projekt. |
| Keystone | Identitäts-API der Appliance. Stellt ein Token für das ausgewählte Coriolis-Projekt aus. |
| Coriolis-Worker-Region | Logische Gruppe von Coriolis-Workern für den Endpoint-Zugriff; getrennt von der STACKIT-Cloud-Region. |
| STACKIT-Projekt | Ressourcenbereich für Zielnetzwerk, Server und Volumes. |
| Transfer | Migrationsdefinition mit Quell-VM-IDs, Ziel-Endpoint, Netzwerkzuordnung und Disk-Transfereinstellungen. |
| Execution | Ausführung eines Transfers. Erstkopie überträgt Disks, weitere Ausführungen synchronisieren geänderte Blöcke. |
| Deployment | Vorgang, der aus übertragenen Disks einen Zielserver erstellt. |
| Migrations-Worker | Coriolis-Prozess oder temporäre VM zum Lesen, Übertragen oder Anpassen von Disks; nicht der migrierte Anwendungsserver. |
| OS Morphing | Anpassung von Treibern, Boot- und Netzwerkkonfiguration des Gasts an die Zielplattform. |
| CBT | VMware Changed Block Tracking zur Ermittlung geänderter Diskblöcke seit einer früheren Synchronisation. |
| NFC/NBD | VMware-Netzwerkzugriff auf Disks beim Export; der Coriolis-Worker muss die zuständigen ESXi-Hosts erreichen. |
| Test-Deployment | Isolierte Bereitstellung zur Generalprobe vor dem produktiven Cutover. |
| Cutover | Finaler Schreibstopp, Disk-Synchronisation, Zielstart, Validierung und Verkehrsumschaltung. |

Das Beispiel verwendet `scf-relocate-app`: Ubuntu 24.04, Spring Boot und PostgreSQL 16 in einer VM
mit 12-GiB-Systemdisk und 8-GiB-Datenbankdisk. Für andere unterstützte VMs verwenden Sie dieselben
Coriolis-Vorgänge, aber deren Betriebssystem, Kapazität, Netzwerke und Anwendungsabnahmetests.

### Voraussetzungen und Sicherheitsgrenzen

1. Stellen Sie eine lizenzierte Coriolis-Appliance mit VMware- und STACKIT-Providern bereit. Nutzen Sie
   den <LinkChip href="/de/migration/assetcontainer/stackit/coriolis-stackit-installer/">Coriolis STACKIT Installer</LinkChip>
   für die Bereitstellung auf STACKIT.
2. Prüfen Sie ESXi-/vCenter-Version, Gastbetriebssystem und Diskaufbau anhand der Support-Matrix der
   installierten Provider. Die VMware-Lizenz muss API-Snapshots, CBT und Diskexport erlauben.
3. Erstellen Sie einen dedizierten VMware-Migrationsaccount und einen STACKIT-Service-Account.
   Gewähren Sie benötigte Inventar-, Snapshot-, CBT-, Export- und Datastore-Rechte auf den betroffenen
   VMs/Datastores. Der STACKIT-Account benötigt Zugriff auf das Zielprojekt und Berechtigungen zum
   Erstellen und Verwalten von Migrationsservern, Volumes, NICs und Security Groups.
4. Bereiten Sie Zielkapazität, Netzwerke, Quoten und eine isolierte Testumgebung vor.
5. Definieren Sie Wartungsfenster, Anwendungsabnahme, Verkehrsumschaltung und Rollback-Verantwortung.
   Bewahren Sie Quell-VM und Backups bis zum Ende des Aufbewahrungszeitraums auf.

Die Anfragen verwenden das Transfer-/Deployment-API-Profil der Coriolis-2608.2-Appliance und ihrer
VMware-/STACKIT-Provider. Lesen Sie vor der Erstellung die installierten Provider-Schemas; sie
definieren gültige Felder. Diese Anleitung verwendet Transfers, nicht den separaten Replica-/DR-Ablauf.

Für Server-Agent-Prüfungen aktivieren Sie **STACKIT Agent Service einmal pro Zielprojekt** und
installieren/provisionieren danach den Agent im Zielgast. Projektaktivierung und Gastbereitstellung
sind separate Vorgänge. SSH-basierte Validierung benötigt keinen Server Agent.

### Architektur und Netzwerkverbindungen

```d2
direction: right
operator: "Betriebsteam"
vmware: "VMware-Umgebung" {
  management: "vCenter / ESXi"
  source: "Quell-VM: OS, Anwendung, Datenbank"
}
coriolis: "Coriolis-Appliance" {
  api: "REST-API und Auftragsplanung"
  worker: "Coriolis-Worker"
}
stackit: "STACKIT-Zielprojekt" {
  worker: "Temporärer Migrations-Worker"
  disks: "Übertragene Volumes" { shape: cylinder }
  target: "Migrierte Anwendungs-VM"
}
operator -> coriolis.api: "HTTPS: Endpoints, Transfers, Deployments"
coriolis.api -> coriolis.worker: "Aufgaben planen"
coriolis.worker -> vmware.management: "API 443 und Diskexport 902"
vmware.management -> vmware.source: "Snapshots und Diskzugriff"
coriolis.worker -> stackit.worker: "SSH 22 und HTTPS-Transfer 5566"
stackit.worker -> stackit.disks: "Transferdisks schreiben"
stackit.disks -> stackit.target: "Klonen, anpassen und bereitstellen"
```

| Verbindung | Benötigter Zugriff | Zweck |
| --- | --- | --- |
| Betriebsteam zur Appliance | HTTPS auf dem konfigurierten Web-/API-Port | Authentifizierung, Konfiguration und Aufgabenstatus. |
| Coriolis-Worker zu vCenter/ESXi | TCP/443 | Inventar, Snapshots und API-Vorgänge. |
| Coriolis-Worker zu Quell-ESXi-Hosts | TCP/902 | NFC-/NBD-Diskexport; alle Hosts berücksichtigen, die Disks der ausgewählten VMs bereitstellen können. |
| Appliance/Worker zu STACKIT-APIs | HTTPS/TCP/443 | Authentifizierung und Erstellung von Zielressourcen. |
| Coriolis-Worker zu temporären STACKIT-Workern | TCP/22 und TCP/5566 bei diesem HTTPS-Transferprofil | Worker-Verwaltung und Disktransfer. |
| Zielgast zu Plattformdiensten | Metadaten, DNS und freigegebene Paket-/Agent-Dienste | Gastinitialisierung und Betrieb. |

Routen Sie den Coriolis-Worker zum privaten Migrationsnetzwerk. Im selben STACKIT-Projekt können
Sie ein angeschlossenes Netzwerk oder eine freigegebene geroutete Verbindung nutzen. Für eine
lokale Appliance verwenden Sie Standort-VPN oder private Anbindung. Das VPN stellt Erreichbarkeit
her, ist aber kein Coriolis-Migrationsmechanismus. Ein bestimmtes VPN-Produkt oder Zwischenhost ist
nicht erforderlich.

Halten Sie temporäre Worker privat. Beschränken Sie Worker-Zugriffe auf Coriolis und administrative
Zugriffe auf freigegebene Quellen. Verbinden Sie Test-VMs weder mit produktivem Verkehr noch mit
identitätssensitiven Diensten.

### Arbeitsumgebung vorbereiten

Verwenden Sie auf dem Arbeitsplatz Bash, `curl`, `jq` und vertrauenswürdige TLS-Zertifikate. Für
optionale Server-Agent-Befehle installieren Sie die offizielle STACKIT CLI. Deaktivieren Sie keine
Zertifikatsprüfung. Halten Sie Authentifizierungsdateien und API-Antworten privat und außerhalb von
Git. Nutzen Sie eine dedizierte Bash-Sitzung und stoppen Sie bei fehlgeschlagenen Anfragen, statt
mit leeren oder veralteten Kennungen fortzufahren.

```bash
set -euo pipefail
umask 077
WORK="$HOME/coriolis-migration"
mkdir -p "$WORK"
chmod 700 "$WORK"
CORIOLIS_URL='https://coriolis.example.com'
CORIOLIS_USER='migration-operator'
CORIOLIS_PROJECT='admin'
CORIOLIS_PASSWORD_FILE="$HOME/.config/coriolis/password"
VMWARE_HOST='vcenter.example.com'
VMWARE_USER='migration-user@vsphere.local'
VMWARE_PASSWORD_FILE="$HOME/.config/coriolis/vmware-password"
STACKIT_KEY_FILE="$HOME/.config/stackit/service-account.json"
STACKIT_ORGANIZATION_ID='replace-with-organization-uuid'
STACKIT_PROJECT_ID='replace-with-project-uuid'
STACKIT_REGION='eu01'
STACKIT_AVAILABILITY_ZONE='eu01-1'
MIGRATION_NETWORK_ID='replace-with-migration-network-uuid'
TARGET_NETWORK_ID='replace-with-application-network-uuid'
TARGET_SECURITY_GROUP_ID='replace-with-application-security-group-uuid'
WORKER_IMAGE_ID='replace-with-ubuntu-worker-image-uuid'
WORKER_MACHINE_TYPE='c3i.2'
TARGET_MACHINE_TYPE='c3i.2'
SOURCE_NETWORK='VM Network'
```

Ersetzen Sie vor der Ausführung alle Beispielhostnamen, Nutzernamen und `replace-with-...`-Werte:

| Eingabe | Benötigter Wert | Herkunft |
| --- | --- | --- |
| `CORIOLIS_URL` | HTTPS-Ursprung der Appliance ohne abschließenden Schrägstrich | Webadresse der bereitgestellten Appliance. |
| `CORIOLIS_USER`, `CORIOLIS_PROJECT` | Login und freigegebener Coriolis-Projektname | Appliance-Administration; `admin` ist nur der Beispielbereich. |
| `CORIOLIS_PASSWORD_FILE` | Datei mit Coriolis-Passwort | Secret Store; lokale Datei mit Modus 600. |
| `VMWARE_HOST`, `VMWARE_USER`, `VMWARE_PASSWORD_FILE` | vCenter-/ESXi-Hostname, Migrationsaccount und Passwortdatei mit Modus 600 | VMware-Administration und Secret Store. |
| `STACKIT_KEY_FILE` | Ursprüngliches Service-Account-Schlüssel-JSON mit Modus 600 | STACKIT-Schlüsselerstellung oder Secret Store. |
| `STACKIT_ORGANIZATION_ID`, `STACKIT_PROJECT_ID` | Organisations- und Zielprojekt-UUIDs | Organisations-/Projektdetails im STACKIT Portal. |
| `STACKIT_REGION`, `STACKIT_AVAILABILITY_ZONE` | Zielregion und Zone | Zielprojektkonfiguration; hier `eu01` / `eu01-1`. |
| `MIGRATION_NETWORK_ID` | Netzwerk-UUID für temporäre Worker | Netzwerkansicht/API des Zielprojekts; von Coriolis erreichbar. |
| `TARGET_NETWORK_ID`, `TARGET_SECURITY_GROUP_ID` | UUIDs für Anwendungsnetzwerk und Security Group | Netzwerk-/Security-Group-Ansichten oder API des Zielprojekts. |
| `WORKER_IMAGE_ID` | UUID eines Ubuntu-Worker-Images mit cloud-init | STACKIT-Imagekatalog; im Zielprojekt verfügbares Image auswählen. |
| `WORKER_MACHINE_TYPE`, `TARGET_MACHINE_TYPE` | Maschinentyp für Worker und finalen Server | STACKIT-Katalog und Sizing-Plan; `c3i.2` ist die Wahl des kleinen Beispiels. |
| `SOURCE_NETWORK` | Exakte Quell-Portgruppe/Netzwerkkennung der VM | VMware-Netzwerkadapterkonfiguration; hier `VM Network`. |

Die API liefert `CORIOLIS_PROJECT_ID`, `WORKER_REGION_ID`, `SOURCE_ENDPOINT_ID`,
`TARGET_ENDPOINT_ID`, `VM_ID`, `TRANSFER_ID`, `EXECUTION_ID` und `DEPLOYMENT_ID`. Erfinden Sie keine
IDs und ersetzen Sie sie nicht durch den Anzeigenamen einer VM. `TARGET_SERVER_ID` ist die UUID
des erzeugten STACKIT-Servers. Das Beispiel nutzt ein Zielnetzwerk für Worker und isolierte
Anwendung; bei getrennter Landing-Zone-Architektur verwenden Sie unterschiedliche Netzwerk-IDs.
Deaktivieren Sie Shell-Tracing und ausführliche HTTP-Protokollierung bei Authentifizierungsvorgängen.

## VMware und STACKIT vorbereiten

Migrieren Sie den vorhandenen Gast; installieren Sie OS, Anwendung oder Datenbank nicht als
Vorbereitung neu. Erfassen Sie VM-Kennungen, vCPU/RAM, Firmware, Disks, Mounts, Netzwerkadapter,
Accounts, Anwendungsdienste und Datenabhängigkeiten. Erstellen und prüfen Sie ein anwendungskonsistentes Backup.

Für das Beispiel erfassen Sie dieses Quellinventar:

| Bestandteil | Beispielkonfiguration |
| --- | --- |
| VM | `scf-relocate-app`, Ubuntu 24.04, 2 vCPU, 3 GiB RAM, EFI-Boot. |
| Anwendung | Spring Boot auf Java 21, systemd-Unit `relocate-demo.service`, HTTP auf `127.0.0.1:8080`. |
| Datenbank | PostgreSQL 16, Cluster/Datenbank `relocate`, Port 5432. |
| Systemdisk | 12 GiB für OS und Anwendungsdateien. |
| Datendisk | 8 GiB, UUID-Mount auf `/srv/relocate-data`; PostgreSQL-Daten in `/srv/relocate-data/postgresql`. |
| Testdaten | Tabelle `migration_records`; `/api/evidence` liefert Datensatzanzahlen und Inhaltsprüfsumme; POST `/api/writes` fügt einen synthetischen Datensatz ein. |
| Hintergrundschreiber | `relocate-writer.timer`; bei Vergleichen eines festen Datenstands stoppen. |

Diese Namen, Pfade und Endpoints gehören zum Beispiel. Erfassen Sie für andere Workloads deren
Entsprechungen und fachliche Abnahmetests. Coriolis setzt weder Spring Boot noch PostgreSQL, eine
leere Datendisk oder einen bestimmten VM-Namen voraus.

### Ausgangszustand der Anwendungsdaten erfassen

Erfassen Sie vor der Erstkopie den Anwendungszustand und einen Konsistenzpunkt. Stoppen Sie im
dedizierten Beispiel den synthetischen Schreiber und sichern Sie unabhängige SQL-/HTTP-Nachweise.
Führen Sie die Befehle **im Quellgast** aus, nicht auf Appliance oder Arbeitsplatz:

```bash
umask 077
GUEST_EVIDENCE="$HOME/migration-evidence"
mkdir -p "$GUEST_EVIDENCE"
sudo systemctl stop relocate-writer.timer relocate-writer.service
sudo -u postgres psql -d relocate -At -F '|' -c \
  "SELECT count(*), count(*) FILTER (WHERE kind='seed'), count(*) FILTER (WHERE kind='write'),
    md5(string_agg(record_id || ':' || payload, E'\n' ORDER BY record_id)) FROM migration_records;" \
  > "$GUEST_EVIDENCE/data.txt"
curl --fail --silent --show-error http://127.0.0.1:8080/api/evidence | jq -S \
  > "$GUEST_EVIDENCE/api.json"
findmnt -n -o UUID --target /srv/relocate-data > "$GUEST_EVIDENCE/data-uuid.txt"
sudo -u postgres psql -d relocate -Atc 'SHOW data_directory;'
```

`GUEST_EVIDENCE` ist ein privates Ausgabeverzeichnis im Gast. Bewahren Sie Quellnachweise auf, ohne
sie bei der Zielabnahme zu überschreiben. Halten Sie den Beispielschreiber während Erstkopie und
Test-Deployment gestoppt. Vereinbaren Sie für produktive Anwendungen Backup/Konsistenzpunkt und
Vergleichsverfahren mit den Verantwortlichen; berücksichtigen Sie Schreibzugriffe während der Online-Kopie.

### Lizenzierte VMware-Quelle qualifizieren

Erstellen und entfernen Sie mit dem dedizierten Account einen Test-Snapshot der ausgewählten VM.
Prüfen Sie Datastore-Reserve, Snapshot-Konsolidierung und laufende VMware Tools. Aktivieren Sie CBT
für VM/Disks vor dem ersten Transfer nach dem freigegebenen VMware-Verfahren. Hier ist
`automatically_enable_cbt: false` gesetzt, weil CBT ausdrücklich vorbereitet wird.

Die Quell-VM braucht eine stabile Kennung und muss im Coriolis-Inventar erscheinen. Beheben Sie
fehlende Kennungen, nicht unterstützte Versionen oder Exportfehler vor der Kopie. Ändern Sie
VM-Identitäten oder Provider-Bibliotheken nicht als allgemeinen Migrationsschritt.

Bereiten Sie Zielprojekt, Netzwerke und Security Groups entsprechend der Eingabetabelle vor.
Reservieren Sie Kapazität für übertragene Volumes, temporäre Worker sowie Test-/finale Server.
Bereiten Sie ein Worker-Image mit cloud-init vor und prüfen Sie Maschinentypen und Zone.

## Coriolis mit beiden Plattformen verbinden

Vervollständigen Sie Routing und Firewall-Regeln aus der Architekturtabelle vor der
Endpoint-Konfiguration. Prüfen Sie die Wege vom Coriolis-Worker, nicht nur vom Browser-Arbeitsplatz.
Die Weboberfläche ist eine Steuerungsverbindung und transportiert nicht sämtlichen Diskverkehr.

### An der Coriolis-API authentifizieren

Ermitteln Sie Identitäts- und Migrations-API-Pfade. Der konfigurierte HTTPS-Ursprung ist derselbe
wie für die Weboberfläche:

```bash
curl --fail --silent --show-error "$CORIOLIS_URL/api/config" > "$WORK/config.json"
IDENTITY_URL="$CORIOLIS_URL$(jq -er '.config.servicesUrls.keystone' "$WORK/config.json")"
CORIOLIS_API_URL="$CORIOLIS_URL$(jq -er '.config.servicesUrls.coriolis' "$WORK/config.json")"
USER_DOMAIN=$(jq -er '.config.defaultUserDomain' "$WORK/config.json")
```

Erstellen Sie die Passwortanfrage aus der geschützten Datei und fordern Sie ein zunächst nicht
projektgebundenes Keystone-Token an. Der Antwortheader `X-Subject-Token` enthält das Token:

```bash
jq -n --arg username "$CORIOLIS_USER" --arg domain "$USER_DOMAIN" \
  --rawfile password "$CORIOLIS_PASSWORD_FILE" \
  '{auth:{identity:{methods:["password"],password:{user:{name:$username,
    password:($password|rtrimstr("\n")),domain:{name:$domain}}}},scope:"unscoped"}}' \
  > "$WORK/login.json"
curl --fail --silent --show-error -X POST "$IDENTITY_URL/auth/tokens" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/login.json" \
  -D "$WORK/unscoped.headers" -o "$WORK/unscoped.json"
UNSCOPED_TOKEN=$(awk 'tolower($1)=="x-subject-token:" {gsub("\r","",$2); print $2}' "$WORK/unscoped.headers")
printf 'X-Auth-Token: %s\n' "$UNSCOPED_TOKEN" > "$WORK/unscoped-request.headers"
curl --fail --silent --show-error -H @"$WORK/unscoped-request.headers" \
  "$IDENTITY_URL/auth/projects" > "$WORK/projects.json"
CORIOLIS_PROJECT_ID=$(jq -er --arg project "$CORIOLIS_PROJECT" \
  '[.projects[]|select(.name==$project)]|if length==1 then .[0].id else error("Select one authorized Coriolis project") end' \
  "$WORK/projects.json")
```

Binden Sie das Token an das ausgewählte Coriolis-Projekt. Speichern Sie den Header privat und
verwenden Sie ihn für jede Migrationsanfrage. Das Token wird aus einer Datei gelesen, nicht als
Kommandozeilenargument übergeben:

```bash
jq -n --rawfile token "$WORK/unscoped-request.headers" --arg project "$CORIOLIS_PROJECT_ID" \
  '{auth:{identity:{methods:["token"],token:{id:($token|sub("^X-Auth-Token: ";"")|rtrimstr("\n"))}},
    scope:{project:{id:$project}}}}' > "$WORK/scope.json"
curl --fail --silent --show-error -X POST "$IDENTITY_URL/auth/tokens" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/scope.json" \
  -D "$WORK/scoped.headers" -o "$WORK/scoped.json"
SCOPED_TOKEN=$(awk 'tolower($1)=="x-subject-token:" {gsub("\r","",$2); print $2}' "$WORK/scoped.headers")
printf 'X-Auth-Token: %s\n' "$SCOPED_TOKEN" > "$WORK/coriolis.headers"
API="$CORIOLIS_API_URL/$CORIOLIS_PROJECT_ID"
unset UNSCOPED_TOKEN SCOPED_TOKEN
```

`API` ist die projektbezogene Migrationsbasis, beispielsweise
`https://coriolis.example.com/coriolis/<coriolis-project-id>`. Bei `401` ist neue Authentifizierung
erforderlich; prüfen Sie vor erneuter Anfrage den Zustand des bestehenden Vorgangs.

### Provider-Schemas lesen und Worker auswählen

```bash
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" "$API/providers" > "$WORK/providers.json"
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" "$API/regions" > "$WORK/regions.json"
jq '.regions[]|{id,name,enabled}' "$WORK/regions.json"
WORKER_REGION_ID='replace-with-enabled-coriolis-worker-region-id'
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/providers/vmware_vsphere/schemas/16" > "$WORK/vmware-connection-schema.json"
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/providers/stackit/schemas/16" > "$WORK/stackit-connection-schema.json"
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/providers/vmware_vsphere/schemas/8" > "$WORK/source-environment-schema.json"
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/providers/stackit/schemas/4" > "$WORK/destination-environment-schema.json"
```

Wählen Sie eine aktivierte Region mit erreichbaren Quell-/Zielsystemen und setzen Sie deren ID als
`WORKER_REGION_ID`. `Public` ist nur der Name einer logischen Coriolis-Worker-Gruppe und aktiviert
keine öffentlichen IPs. `STACKIT_REGION` bleibt die Ziel-Cloud-Region. Schema `16` liefert hier
Verbindungseinstellungen, `8` die VMware-Quellumgebung und `4` die STACKIT-Zielumgebung. Validieren
Sie Anfragen vor dem Absenden gegen diese zurückgegebenen JSON-Schemas.

### Plattform-Endpoints erstellen und prüfen

Erstellen Sie den VMware-Endpoint. `host` ist die vCenter- oder unterstützte Standalone-ESXi-Adresse,
nicht die Gast-IP. Der Account muss alle ausgewählten VMs und zugehörigen Datastores sehen:

```bash
jq -n --arg host "$VMWARE_HOST" --arg username "$VMWARE_USER" \
  --rawfile password "$VMWARE_PASSWORD_FILE" --arg worker "$WORKER_REGION_ID" \
  '{endpoint:{name:"vmware-source",type:"vmware_vsphere",mapped_regions:[$worker],
    connection_info:{host:$host,port:443,username:$username,
      password:($password|rtrimstr("\n")),allow_untrusted:false}}}' > "$WORK/vmware-endpoint-request.json"
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/vmware-endpoint-request.json" \
  "$API/endpoints" > "$WORK/vmware-endpoint.json"
SOURCE_ENDPOINT_ID=$(jq -er '.endpoint.id' "$WORK/vmware-endpoint.json")
```

Erstellen Sie den STACKIT-Endpoint. `service_account_key` enthält das ursprüngliche Schlüssel-JSON
in Base64. Das ist Kodierung, keine Verschlüsselung; schützen Sie die Anfragedatei:

```bash
jq -n --arg organization "$STACKIT_ORGANIZATION_ID" --arg project "$STACKIT_PROJECT_ID" \
  --arg region "$STACKIT_REGION" --arg worker "$WORKER_REGION_ID" --rawfile key "$STACKIT_KEY_FILE" \
  '{endpoint:{name:"stackit-destination",type:"stackit",mapped_regions:[$worker],
    connection_info:{organization_id:$organization,project_id:$project,
      region_name:$region,service_account_key:($key|@base64)}}}' > "$WORK/stackit-endpoint-request.json"
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/stackit-endpoint-request.json" \
  "$API/endpoints" > "$WORK/stackit-endpoint.json"
TARGET_ENDPOINT_ID=$(jq -er '.endpoint.id' "$WORK/stackit-endpoint.json")
```

Prüfen Sie beide Verbindungen und verlangen Sie `valid: true`. Aktualisieren Sie danach das Quellinventar:

```bash
for ENDPOINT_ID in "$SOURCE_ENDPOINT_ID" "$TARGET_ENDPOINT_ID"; do
  curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
    -H 'Content-Type: application/json' --data-binary '{"validate-connection":null}' \
    "$API/endpoints/$ENDPOINT_ID/actions" > "$WORK/validate-$ENDPOINT_ID.json"
  jq -e '.["validate-connection"].valid==true' "$WORK/validate-$ENDPOINT_ID.json"
done
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/endpoints/$SOURCE_ENDPOINT_ID/instances?refresh=true&limit=100" > "$WORK/source-instances.json"
jq '.instances[]|{id,name,os_type,power_state}' "$WORK/source-instances.json"
VM_ID='replace-with-selected-instance-id-from-coriolis'
```

Wählen Sie die exakte zurückgegebene VM-ID, gleichen Sie sie mit dem Quellinventar ab und setzen
Sie `VM_ID`. Bei größeren Beständen rufen Sie vor der Auswahl alle Inventarseiten ab. Speichern
Sie jede Ressourcen-ID. Nach unterbrochenem POST prüfen Sie bestehende Endpoints/Aufträge, statt
Duplikate zu erzeugen. Endpoint-Validierung beweist Zugangsdaten/API-Zugriff, nicht den gesamten Diskpfad.

## Zieldisks aufbauen und synchronisieren

Erstellen Sie eine Transfer-Definition für die VM. Während der Erstkopie bleibt die Quelle an;
das Wartungsfenster betrifft finale Synchronisation und Umschaltung. Legen Sie Diskrichtlinie
und spätere Deployment-Zuordnung jetzt fest. Beides gehört zur Migrationsdefinition, Transfer-Execution
und Deployment bleiben aber getrennte Vorgänge. Ergebnis sind synchronisierte Volumes, kein gebooteter Server.

```bash
jq -n --arg source "$SOURCE_ENDPOINT_ID" --arg destination "$TARGET_ENDPOINT_ID" --arg vm "$VM_ID" \
  --arg project "$STACKIT_PROJECT_ID" --arg source_network "$SOURCE_NETWORK" \
  --arg target_network "$TARGET_NETWORK_ID" --arg migration_network "$MIGRATION_NETWORK_ID" \
  --arg security_group "$TARGET_SECURITY_GROUP_ID" --arg image "$WORKER_IMAGE_ID" \
  --arg worker_type "$WORKER_MACHINE_TYPE" --arg target_type "$TARGET_MACHINE_TYPE" \
  --arg zone "$STACKIT_AVAILABILITY_ZONE" \
  '{transfer:{scenario:"live_migration",origin_endpoint_id:$source,destination_endpoint_id:$destination,
    instances:[$vm],source_environment:{export_transfer_mechanism:"openvixdisklib",
      automatically_enable_cbt:false,verify_disk_integrity:true,skip_nfc_validation:false},
    destination_environment:{project:$project,network_map:{($source_network):$target_network},
      migr_network:$migration_network,migr_machine_type:$worker_type,machine_type:$target_type,
      availability_zone:$zone,migr_image_map:{linux:$image},set_dhcp:true,
      migr_worker_use_public_ip:false,use_public_ip:false,preserve_fixed_ips:false,
      retain_user_credentials:true,security_groups:[$security_group],data_transfer_mechanism:"HTTPS",
      volumes_are_zeroed:false,delete_disks_on_server_termination:false},
    network_map:{($source_network):$target_network},clone_disks:true,skip_os_morphing:false}}' \
  > "$WORK/transfer-request.json"
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/transfer-request.json" \
  "$API/transfers" > "$WORK/transfer.json"
TRANSFER_ID=$(jq -er '.transfer.id' "$WORK/transfer.json")
```

| Einstellung | Wirkung |
| --- | --- |
| `instances` | Exakte VM-IDs aus dem Quell-Endpoint-Inventar; mit einer repräsentativen VM beginnen. |
| `network_map` | Quell-Portgruppe auf Zielnetzwerk abbilden; jedes verwendete Quellnetzwerk zuordnen. |
| `migr_network`, `migr_machine_type`, `migr_image_map` | Netzwerk, Sizing und Image temporärer Worker, nicht des finalen Servers. |
| `machine_type`, `availability_zone`, `security_groups` | Sizing, Zone und Anwendungs-Security-Groups des finalen Servers. |
| `openvixdisklib` | Hier verwendeter VMware-Diskexport; muss vom installierten Provider und der Quelle unterstützt werden. |
| `verify_disk_integrity: true` | Ende-zu-Ende-Prüfsummenprüfung der Disks. |
| `skip_nfc_validation: false` | Prüfung des ESXi-Diskpfads bleibt aktiv. |
| `migr_worker_use_public_ip: false`, `use_public_ip: false` | Worker und Zielserver bleiben privat. |
| `set_dhcp: true`, `preserve_fixed_ips: false` | DHCP am Ziel; Quelladresse/MAC werden nicht beibehalten. |
| `retain_user_credentials: true` | Vorhandene Gastnutzer/Zugangsdaten behalten; Ziel-SSH-Richtlinie ausdrücklich bei der Abnahme durchsetzen. |
| `clone_disks: true`, `skip_os_morphing: false` | Disks für Deployment klonen und Gast an Zielhardware anpassen. |

Die Anfrage konfiguriert Linux-Gäste. Für Windows richten Sie Windows-Worker/Image und VirtIO-Treiber
des Providers ein und nutzen Windows-Abnahmetests. Trennen Sie Transfers bei unterschiedlichem
Sizing oder Gastregeln. Für Wellen nehmen Sie alle ausgewählten IDs in `instances` auf und koordinieren
den finalen Schreibstopp je Anwendungsabhängigkeitsgruppe.

### Disk-Synchronisation starten und prüfen

Erstellen Sie eine Execution des Transfers. `shutdown_instances: false` belässt Quellsteuerung bei
Ihnen; `auto_deploy: false` trennt Diskkopie und Zielstart:

```bash
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' \
  --data-binary '{"execution":{"shutdown_instances":false,"auto_deploy":false}}' \
  "$API/transfers/$TRANSFER_ID/executions" > "$WORK/initial-execution.json"
EXECUTION_ID=$(jq -er '.execution.id' "$WORK/initial-execution.json")
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/transfers/$TRANSFER_ID/executions/$EXECUTION_ID?include_task_info=true" \
  > "$WORK/initial-status.json"
jq '.execution|{id,status,tasks:[.tasks[]|{task_type,status}]}' "$WORK/initial-status.json"
```

Wiederholen Sie die GET-Statusabfrage bis zum Endzustand. Fahren Sie erst fort, wenn Execution,
Replikation und `DELETE_TRANSFER_SOURCE_RESOURCES` / `DELETE_TRANSFER_TARGET_RESOURCES` jeweils
`COMPLETED` sind. Die Bereinigung entfernt temporäre Transferressourcen, nicht die Quell-VM.
Beheben Sie `ERROR` und prüfen Sie Bereinigung vor einer weiteren Execution desselben Transfers.
Bei verlorener POST-Antwort listen Sie dessen Executions auf und ermitteln die ID; wiederholen
Sie POST nicht ohne Abgleich des Ergebnisses.

## Neue Ziel-VM erstellen

Erstellen Sie aus dem vollständig übertragenen Diskzustand einen **neuen** STACKIT-Server.
Deployment erstellt Server/NICs/Volumes, bereitet Boot und Treiber durch OS Morphing vor und
startet den Gast. Die Quell-VM bleibt ein separates ESXi-Objekt mit eigener Identität und eigenem Stromzustand.

### Quellkonfiguration auf STACKIT-Ressourcen abbilden

Nutzen Sie Quellinventar als Ausgangspunkt und geben Sie die Zuordnung vor dem Deployment frei:

| Quelle | STACKIT-Zuordnung | Beispiel |
| --- | --- | --- |
| vCPU/RAM | Maschinentyp mit freigegebener Kapazität wählen; keine identischen Namen oder Speicherverhältnisse annehmen. | Quelle: 2 vCPU / 3 GiB; Ziel: Katalogkonfiguration `c3i.2`, tatsächliche CPU-/RAM-Werte prüfen. |
| System-/Datendisks | Pro Disk ein Volume mit ausreichender Kapazität/Performance. | Separate 12-/8-GiB-Quelldisks beibehalten; tatsächliche Zielgrößen abnehmen und dokumentieren. |
| BIOS/EFI und Gast-OS | Unterstützte Bootkonfiguration und OS Morphing. | EFI-Ubuntu mit VirtIO-Disks/-Netzwerk. |
| Netzwerkadapter | NICs im zugeordneten Netzwerk mit freigegebenen Security Groups. | `VM Network` auf `TARGET_NETWORK_ID`; DHCP statt Quell-IP/-MAC. |
| Nutzer/Dienste | Zugriffs-/Identitätsrichtlinie anwenden und Workload validieren. | Accounts, Spring Boot und PostgreSQL erhalten; schlüsselbasiertes SSH durchsetzen. |

`machine_type` dimensioniert den finalen Server; `migr_machine_type` nur temporäre Worker. Ohne
`machine_type` wählt der STACKIT-Provider einen minimal geeigneten Typ anhand der Quellanforderungen.
Hier wird `TARGET_MACHINE_TYPE` ausdrücklich gesetzt, damit die freigegebene Zuordnung verwendet
wird. Die Übertragung der Disks allein ist keine Freigabe des Zielsizings.

### Isoliertes Deployment testen

Erfassen Sie vor der Kopie den Quell-Datenstand. Halten Sie den synthetischen Schreiber bis zur
Testabnahme gestoppt. Eine fortlaufend beschriebene produktive Datenbank braucht ein eigenes
Konsistenz-/Testverfahren; Online-Diskkopie ersetzt weder konsistentes Backup noch finalen Schreibstopp.

Mit `clone_disks: true` entsteht ein Testserver mit separaten Deployment-Volumes, während die
übertragenen Volumes für weitere Synchronisation erhalten bleiben. Halten Sie ihn von produktivem
Verkehr getrennt. Erstellen Sie das Deployment erst nach bestandener Kopie und Bereinigung:

```bash
jq -n --arg transfer "$TRANSFER_ID" \
  '{deployment:{transfer_id:$transfer,clone_disks:true,force:false,skip_os_morphing:false}}' \
  > "$WORK/deployment-request.json"
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/deployment-request.json" \
  "$API/deployments" > "$WORK/rehearsal-deployment.json"
DEPLOYMENT_ID=$(jq -er '.deployment.id' "$WORK/rehearsal-deployment.json")
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/deployments/$DEPLOYMENT_ID?include_info=true&include_task_info=true" \
  > "$WORK/rehearsal-status.json"
jq '.deployment|{id,last_execution_status,tasks:[.tasks[]|{task_type,status}]}' "$WORK/rehearsal-status.json"
```

Verlangen Sie `last_execution_status: COMPLETED` sowie abgeschlossene Anpassungs-, Finalisierungs-
und Bereinigungsaufgaben. `force: false` verhindert erzwungene Bereitstellung. Geklonte Disks erhalten
Transferdaten für spätere Synchronisation. Der Test ist ein separates Ziel, keine produktive Umschaltung.

Ermitteln Sie erzeugte NIC-/Volume-Kennungen:

```bash
jq --arg vm "$VM_ID" \
  '.deployment.info[$vm].instance_deployment_info|{instance_name,nic_ids,volumes_info}' \
  "$WORK/rehearsal-status.json"
```

Ordnen Sie diese IDs in STACKIT dem tatsächlichen Server zu und speichern Sie seine UUID als
`TARGET_SERVER_ID`. Namen sind zwischen Quelle, Tests und finalem Deployment nicht eindeutig.
Prüfen Sie private Adresse, Netzwerk, Security Groups und Disks vor dem Login. Beziehen Sie den
SSH-Hostschlüsselfingerabdruck über einen vertrauenswürdigen Verwaltungsweg; der Quellfingerabdruck
ist kein Nachweis für den Zielhostschlüssel.

## Ziel-VM validieren

Vergleichen Sie Quellinventar und exaktes Ziel. Führen Sie Gastbefehle über freigegebenes privates
SSH/Konsole oder einen provisionierten STACKIT Server Agent aus. Halten Sie Tests von produktiven
Clients, geplanten Schreibern und identitätssensitiven Integrationen getrennt.

Beginnen Sie bei Linux mit diesen Standardprüfungen:

```bash
systemd-detect-virt
uname -r
test -d /sys/firmware/efi && printf 'EFI boot\n'
lsblk -o NAME,SIZE,FSTYPE,UUID,MOUNTPOINTS
findmnt
ip -br address
ip route
systemctl --failed --no-pager
getent passwd
getent group
ls -l /sys/class/block/vd*/device/driver /sys/class/net/*/device/driver
```

| Prüfung | Abnahmekriterium |
| --- | --- |
| Virtuelle Hardware | KVM am Ziel; aktive Disk-/NIC-Bindungen verwenden VirtIO statt VMware-Geräte. Bindungen prüfen, nicht nur geladene Module. |
| Firmware/Storage | Geplantes Bootverfahren, alle Disks, UUID-Mounts und Datenverzeichnisse vorhanden; tatsächliche Größen mit Sizing abstimmen. |
| VMware Tools | Programme/Dienste vom Ziel entfernt; Konfigurationsreste sind keine laufenden Tools. Keine unbeteiligten Kernelmodule entfernen. |
| Netzwerk | Beabsichtigte DHCP-Adresse, Route, DNS und Security Groups; keine störenden alten VMware-Interface-Konfigurationen. |
| Nutzer/Zugriff | Nutzernamen, UIDs/GIDs, Gruppen, sudo und Schlüsselfingerabdrücke entsprechen dem Plan. Keine Passwort-Hashes oder privaten Schlüssel sammeln. |
| Identität | Hostschlüsseländerungen und Maschinen-/Monitoring-Identität dokumentieren. Parallele Testidentitäten isolieren; nur freigegebene Identitätsänderungen durchführen. |
| Dienste | Anwendung/Datenbank gesund; keine ungeklärten fehlgeschlagenen Units. |

Setzen Sie die freigegebene SSH-Richtlinie nach `retain_user_credentials` durch. Im Ubuntu-Beispiel
bleibt die bestehende administrative Sitzung geöffnet. Konfigurieren und prüfen Sie schlüsselbasiertes
SSH vor dem Neuladen:

```bash
printf 'PasswordAuthentication no\n' | sudo tee /etc/ssh/sshd_config.d/00-migration-ssh.conf >/dev/null
printf 'ssh_pwauth: false\n' | sudo tee /etc/cloud/cloud.cfg.d/99-migration-ssh.cfg >/dev/null
sudo systemctl daemon-reload
sudo systemctl start ssh
sudo /usr/sbin/sshd -t
sudo /usr/sbin/sshd -T | grep -E '^(passwordauthentication|pubkeyauthentication) '
sudo systemctl reload ssh
```

Verlangen Sie `passwordauthentication no` und `pubkeyauthentication yes`, danach einen neuen
schlüsselbasierten Login über den freigegebenen Weg. Beheben Sie widersprüchliche wirksame Regeln.
Setzen Sie für die Java-Unit `SuccessExitStatus=143`, damit regulärer SIGTERM-Stopp als Erfolg gilt.
Starten Sie die Anwendung nicht nur neu, um einen ungeklärten Fehler zu verbergen.

### Anwendungs- und Datenbankdaten abnehmen

Prüfen Sie Dienstzustand, Lese-/Schreibverhalten, Datenkonsistenz und externe Abhängigkeiten.
Erwartungswert ist der erfasste Quellzustand, nicht der Datenstand des Tests selbst. Halten Sie
isolierte Abnahmeschreibzugriffe vom Vergleich mit der maßgeblichen Quelle getrennt.

Wiederholen Sie SQL-/HTTP-/Mount-Prüfungen **im Zielgast**, nicht auf Appliance oder Arbeitsplatz:

```bash
umask 077
GUEST_EVIDENCE="$HOME/migration-evidence"
mkdir -p "$GUEST_EVIDENCE"
sudo -u postgres psql -d relocate -At -F '|' -c \
  "SELECT count(*), count(*) FILTER (WHERE kind='seed'), count(*) FILTER (WHERE kind='write'),
    md5(string_agg(record_id || ':' || payload, E'\n' ORDER BY record_id)) FROM migration_records;" \
  > "$GUEST_EVIDENCE/data.txt"
curl --fail --silent --show-error http://127.0.0.1:8080/api/evidence | jq -S \
  > "$GUEST_EVIDENCE/api.json"
findmnt -n -o UUID --target /srv/relocate-data > "$GUEST_EVIDENCE/data-uuid.txt"
sudo -u postgres psql -d relocate -Atc 'SHOW data_directory;'
```

Sammeln Sie Quell-/Zielnachweise in getrennten Ordnern; überschreiben Sie den Ausgangsstand nicht.
Verlangen Sie identische Gesamt-/Seed-/Write-Anzahlen, Inhaltsprüfsumme und Mount-UUID. PostgreSQL
muss `/srv/relocate-data/postgresql` verwenden; `relocate-demo.service` und
`postgresql@16-relocate.service` müssen aktiv sein. Zehn neue Quellschreibzugriffe erhöhen beispielsweise
1.004 auf 1.014 Datensätze; maßgeblich sind Ihre erfassten Zahlen, keine feste Zahl dieser Anleitung.

Prüfen Sie den Server Agent unabhängig von installierten Paketen. Authentifizieren Sie am Arbeitsplatz
die offizielle CLI mit `STACKIT_KEY_FILE`, setzen Sie die abgeglichene Server-UUID und fordern Sie
einen rein lesenden Befehl an:

```bash
TARGET_SERVER_ID='replace-with-matched-stackit-server-uuid'
stackit auth activate-service-account --service-account-key-path "$STACKIT_KEY_FILE"
stackit server command create --server-id "$TARGET_SERVER_ID" --project-id "$STACKIT_PROJECT_ID" \
  --region "$STACKIT_REGION" --template-name RunShellScript \
  --params 'script=id -u; uname -r; systemd-detect-virt' \
  --assume-yes --output-format json > "$WORK/agent-command.json"
AGENT_COMMAND_ID=$(jq -er '.id' "$WORK/agent-command.json")
stackit server command describe "$AGENT_COMMAND_ID" --server-id "$TARGET_SERVER_ID" \
  --project-id "$STACKIT_PROJECT_ID" --region "$STACKIT_REGION" --output-format json
```

Verlangen Sie abgeschlossenen Status und Exitcode 0. `AGENT_COMMAND_ID` ist die zurückgegebene
Befehls-ID, keine Serverkennung. Prüfen Sie Monitoring-Dateneingang, Backup-Umfang und Wiederherstellung vor Übergabe.

## Änderungen der Quelle synchronisieren

Eine weitere Execution **desselben Transfers** synchronisiert Quelländerungen. Schließen Sie
vorherige Kopier-/Deployment-Aufgaben vorher ab. Pro Delta ist kein neuer Transfer erforderlich.
Ein vorhandenes geklontes Test-Deployment erhält diese Änderungen nicht; erstellen Sie ein neues
Deployment aus frisch geklonten Disks für den aktualisierten Datenstand.

Im dedizierten Beispiel prüfen Sie das Delta mit einmalig zehn synthetischen Schreibzugriffen.
Verifizieren Sie vorher die tatsächliche VMware-Quelle statt eines gleichnamigen STACKIT-Klons;
`systemd-detect-virt` muss VMware erkennen. Halten Sie den Hintergrundschreiber gestoppt und
fügen Sie keine synthetischen Datensätze in produktive Anwendungen ein:

```bash
for WRITE_NUMBER in {1..10}; do
  curl --fail --silent --show-error -X POST http://127.0.0.1:8080/api/writes || exit 1
done
curl --fail --silent --show-error http://127.0.0.1:8080/api/evidence | jq -S
```

Prüfen Sie exakt zehn zusätzliche Datensätze, unveränderte Seed-Daten und SQL-/HTTP-Übereinstimmung.
Erfassen Sie die neue Anzahl/Prüfsumme. Wiederholen Sie keine ungewisse Schreibserie; vergleichen
Sie zuerst die Anzahl. Nutzen Sie bei produktiven VMs normale Änderungen und fachliche Konsistenzprüfungen.

Synchronisieren Sie am Arbeitsplatz die Disks und prüfen Sie genau diese neue Execution:

```bash
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' \
  --data-binary '{"execution":{"shutdown_instances":false,"auto_deploy":false}}' \
  "$API/transfers/$TRANSFER_ID/executions" > "$WORK/delta-execution.json"
EXECUTION_ID=$(jq -er '.execution.id' "$WORK/delta-execution.json")
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/transfers/$TRANSFER_ID/executions/$EXECUTION_ID?include_task_info=true" \
  > "$WORK/delta-status.json"
jq '.execution|{id,status,tasks:[.tasks[]|{task_type,status}]}' "$WORK/delta-status.json"
```

Verlangen Sie abgeschlossene Replikation/Prüfsummen und beide Bereinigungsaufgaben. Wiederholen
Sie Deployment mit `clone_disks: true`, speichern Sie die neue `DEPLOYMENT_ID` und gleichen Sie
die neuen STACKIT-Ressourcen-IDs ab. Wiederholen Sie Gast-/Datenabnahme gegen den neuen Quellstand.
Bewahren oder entfernen Sie ältere Testserver nach Testplan; deren alte Daten sind kein Delta-Ergebnis.

## Finaler Cutover und Wiederherstellung

### Zeitlicher Migrationsablauf

Erstkopie und Test-Deployment liegen vor dem Wartungsfenster. Finale Kopie verwendet den eingefrorenen
Quellzustand; produktiver Verkehr wechselt erst nach Abnahme der finalen neuen VM.

```d2
direction: right
preparation: "Vor dem Wartungsfenster" {
  grid-columns: 1
  initial: "1. Ersttransfer der Disks\nQuell-VM bleibt an"
  rehearsal: "2. Neue Test-VM\nDiskklone + OS Morphing"
  delta: "3. Delta-Executions\nMit neuer Test-VM prüfen"
}
window: "Wartungsfenster" {
  grid-columns: 1
  freeze: "4. Schreiber + Quell-VM stoppen\nFinalen Datenstand erfassen"
  copy: "5. Finale Disk-Synchronisation\nNoch keine Anwendungs-VM"
  deployment: "6. Finale neue VM erstellen\nOS + Anwendung + Daten prüfen"
}
operation: "Produktion und Aufbewahrung" {
  grid-columns: 1
  traffic: "7. Freigegebenen Verkehr umschalten\nZielschreiber aktivieren"
  monitor: "8. Überwachen und übergeben\nQuelle für Wiederherstellung auslassen"
}
preparation -> window
window -> operation: "Abnahme bestanden"
```

### Cutover-Sequenz ausführen

Führen Sie den freigegebenen Wartungsplan in dieser Reihenfolge aus:

1. Stoppen Sie Clientschreibzugriffe, Hintergrundjobs und abhängige Schreiber der Migrationsgruppe.
2. Stoppen Sie die Quellanwendung, erfassen Sie finale Datenbankanzahl/Prüfsumme und stoppen Sie die Datenbank sauber.
3. Fahren Sie den Gast regulär über VMware herunter und bestätigen Sie `poweredOff` in vCenter/ESXi.
4. Führen Sie eine finale Execution desselben Transfers aus; Replikation und Bereinigung müssen abgeschlossen sein.
5. Erstellen Sie das finale Deployment, warten Sie auf alle Aufgaben und ermitteln Sie exakte Server-/NIC-/Volume-IDs.
6. Wiederholen Sie OS-, Zugriffs-, Anwendungs- und finale Datenabnahme auf diesem Server.
7. Schalten Sie freigegebene DNS-/Load-Balancer-/Routing-Ziele um, aktivieren Sie Zielschreiber und prüfen Sie Clientverkehr.
8. Überwachen Sie die Anwendung und bewahren Sie die ausgeschaltete Quelle für den vereinbarten Zeitraum auf.

Im Ubuntu-/PostgreSQL-Beispiel stoppen Sie Schreibzugriffe im verifizierten Quellgast:

```bash
sudo systemctl stop relocate-writer.timer relocate-writer.service relocate-demo.service
sudo -u postgres psql -d relocate -At -F '|' -c \
  "SELECT count(*), md5(string_agg(record_id || ':' || payload, E'\n' ORDER BY record_id)) FROM migration_records;"
sudo pg_ctlcluster --mode fast 16 relocate stop
sudo sync
```

Übernehmen Sie finale Anzahl/Prüfsumme vor dem regulären VMware-Herunterfahren in den Abnahmenachweis.
Eine gesendete Shutdown-Anfrage beweist noch keinen ausgeschalteten Zustand. Erzwingen Sie keinen
Power-off, um einen fehlgeschlagenen Anwendungs-/Datenbankstopp zu umgehen.

Die finale Synchronisation am Arbeitsplatz nutzt dieselbe API wie das Delta, jetzt aber mit
ausgeschalteter Quelle und festem erwarteten Datenstand:

```bash
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' \
  --data-binary '{"execution":{"shutdown_instances":false,"auto_deploy":false}}' \
  "$API/transfers/$TRANSFER_ID/executions" > "$WORK/final-execution.json"
EXECUTION_ID=$(jq -er '.execution.id' "$WORK/final-execution.json")
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/transfers/$TRANSFER_ID/executions/$EXECUTION_ID?include_task_info=true" \
  > "$WORK/final-status.json"
jq '.execution|{id,status,tasks:[.tasks[]|{task_type,status}]}' "$WORK/final-status.json"
```

Nach `COMPLETED` für finale Kopie und Bereinigung wiederholen Sie den Deployment-POST mit derselben
`deployment-request.json`. Erfassen und prüfen Sie das neue finale Deployment:

```bash
curl --fail --silent --show-error -X POST -H @"$WORK/coriolis.headers" \
  -H 'Content-Type: application/json' --data-binary @"$WORK/deployment-request.json" \
  "$API/deployments" > "$WORK/final-deployment.json"
DEPLOYMENT_ID=$(jq -er '.deployment.id' "$WORK/final-deployment.json")
curl --fail --silent --show-error -H @"$WORK/coriolis.headers" \
  "$API/deployments/$DEPLOYMENT_ID?include_info=true&include_task_info=true" \
  > "$WORK/final-deployment-status.json"
jq '.deployment|{id,last_execution_status,tasks:[.tasks[]|{task_type,status}]}' "$WORK/final-deployment-status.json"
```

Verlangen Sie abgeschlossene Deployment-Aufgaben. Prüfen Sie finalen Server gegen eingefrorene
Quellanzahl, Prüfsumme und Mount-UUID, nicht gegen eine frühere Test-VM. Führen Sie keine parallelen
Kopier-/Deployment-Vorgänge auf demselben Transfer aus.

Vor Zielschreibzugriffen bedeutet Rollback: Ziel isolieren/stoppen, Verkehr fernhalten und die
aufbewahrte Quelle nach Plan wieder starten. Nach Zielschreibzugriffen müssen neue Zieldaten vor
Rückkehr abgeglichen werden; bloßes Neustarten der alten VM verliert sie. Löschen Sie die Quelle
erst nach Aufbewahrungszeitraum und Freigabe durch die Anwendungsverantwortlichen.

## Nachweise und Betriebsübergabe

Übergeben Sie Quell-/Zielinventar, Endpoint-/Transfer-/Execution-/Deployment-IDs, finale
Server-/NIC-/Volume-IDs, abgeschlossene Aufgaben, Anwendungs-/Datenabnahme, SSH-/Identitätsregeln,
Monitoring und Backup-/Recovery-Konfiguration. Schützen Sie Dateien mit Zugangsdaten und beachten
Sie deren Aufbewahrungsrichtlinie. Veröffentlichen Sie keine rohen Endpoint-Anfragen oder Authentifizierungsheader.

Entfernen Sie übrig gebliebene temporäre Worker erst nach Abgleich von Zugehörigkeit und
Aufgabenstatus. Prüfen Sie Security Groups und Ressourcenkosten. Dokumentieren Sie tatsächliche
Zielvolumegrößen statt Quellgröße anzunehmen. Wiederholen Sie Vorbereitung, Zuordnung und Abnahme
für weitere VMs und migrieren Sie abhängigkeitsgerechte Wellen über dieselbe API-Sequenz.

<LinkCard title="Cloudbase Coriolis" href="https://cloudbase.it/coriolis/" />

<LinkCard
  title="Beispiel-Workload mit Spring Boot und PostgreSQL"
  href="https://github.com/stackitcloud/stackit-cmf-relocate-vmware-coriolis"
/>
