Digital Ocean K8s Cluster Part 1 | Managed Datenbanken Mysql & Redis

Wie bereits angekündigt werde ich eine Reihe an Beiträgen verfassen die sich mit einem Shopware 6 in Digital Ocean befassen. Bevor ich allerdings mit Kubernetes in Digital Ocean starte möchte ich die wichtigen Produkte in „Managed Database“ testen und wichtige Konfigurationen teilen.

Vorwort

Warum splitte ich das ganze in 3-4 Teile? Das Setup der DBs und das einstellen darauffolgend in in Shopware birgt die ein oder andere Hürde. Gerade was das Mysql Setting in DO und den Query String für das Session Handling mit Redis angeht. Wer noch nie mit DO gearbeitet hat wird den ein oder anderen Schritt auch zu schätzen wissen.

Account

Solltet Ihr euch für diese Tutorial Reihe interessieren empfehle ich euch den folgenden Link mit dem Ihr 100 USD zum testen erhaltet. Damit könnt Ihr euren Cluster mit Datenbanken etc. für einige Tage laufen lassen.

https://m.do.co/c/7a7721397e44

Voraussetzungen

Wie oben erwähnt benötigt Ihr einen DO Account, zudem werde ich ein Repository zur Verfügung stellen in dem Ihr die meisten der in der GUI oder per Command gezeigten Setups auch via doctl ausführen könnt.

Das ist besonders für Automatisierungen und Geschwindigkeit interessant. Das Schreiben des Artikels hat mich ca. 3-4 Stunden gekostet. Das testen auf eurer Seite sollte mit einem bestehendem Shop max. 30-60 min dauern.

Doctl

Offizielle Doku mit allen OS Installationsanleitungen

Ich habe hier mal die snap Installation gewählt, bei der ist es wichtig das Ihr folgende commands noch mit sudo ausführt:

sudo snap install doctl
# Grant permissions
sudo snap connect doctl:kube-config
sudo snap connect doctl:ssh-keys :ssh-keys
sudo snap connect doctl:dot-docker

Authentifizierung

Tokens für die Auth bekommt Ihr hier

Legt euch diesen sicher in 1PW oder einem PW Manager eurer Wahl ab.

Danach führt Ihr

doctl auth init --context <NAME>

aus. Den Context benötigt Ihr nur wenn Ihr mehrere Accounts connecten möchtet. In meinem Fall der private + Firmenaccount.

doctl auth init --context PRIVATE

In meinem Fall also PRIVATE da dieser Blog in meiner Freizeit verfasst wird und auf einem persönlichem DO Account läuft.

Da ich bereits einen Account hatte muss ich jetzt noch den Context switchen:

doctl auth switch --context PRIVATE

Nun einfach doctl in das CLI eingeben und Ihr erhaltet:

doctl is a command line interface (CLI) for the DigitalOcean API.

Usage:
  doctl [command]

Available Commands:
  1-click         Display commands that pertain to 1-click applications
  account         Display commands that retrieve account details
  apps            Display commands for working with apps
  auth            Display commands for authenticating doctl with an account
  balance         Display commands for retrieving your account balance
  billing-history Display commands for retrieving your billing history
  completion      Generate the autocompletion script for the specified shell
  compute         Display commands that manage infrastructure
  databases       Display commands that manage databases
  help            Help about any command
  invoice         Display commands for retrieving invoices for your account
  kubernetes      Displays commands to manage Kubernetes clusters and configurations
  monitoring      [Beta] Display commands to manage monitoring
  projects        Manage projects and assign resources to them
  registry        Display commands for working with container registries
  sandbox         Develop functions in a sandbox prior to deploying them in an app
  version         Show the current version
  vpcs            Display commands that manage VPCs

Flags:
  -t, --access-token string   API V2 access token
  -u, --api-url string        Override default API endpoint
  -c, --config string         Specify a custom config file (default "/home/micha/.config/doctl/config.yaml")
      --context string        Specify a custom authentication context name
  -h, --help                  help for doctl
  -o, --output string         Desired output format [text|json] (default "text")
      --trace                 Show a log of network activity while performing a command
  -v, --verbose               Enable verbose output

Use "doctl [command] --help" for more information about a command.

Zur Sicherheit prüfen wir mal ob es bereits Datenbanken gibt:

doctl databases list

# Output
ID    Name    Engine    Version    Number of Nodes    Region    Status    Size

Mysql 8.0

Datenbank Erstellung
doctl databases create shopware6 --engine=mysql \
 --region=fra1 \
 --size=db-s-2vcpu-4gb \
 --num-nodes=2 \ 

Hier wird eine DB mit dem Namen shopware6 erstellt und zwar in der Location Frankfurt mit 2 VCPUs / 4 GB Ram und 2 Nodes (einer davon ist ein Standby Node)

Einstellungen

WICHTIG

Ihr müsst die sql-modes anpassen. Diese sind Standardmäßig nicht nach dem default von Mysql 8.0 gesetzt

Vorher

Nachher

NO_ENGINE_SUBSTITUTION
NO_ZERO_DATE
NO_ZERO_IN_DATE
STRICT_TRANS_TABLES
ERROR_FOR_DIVISION_BY_ZERO

Sonst passiert folgendes:

SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'shopware6.product.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Datenmigration

Ihr könnt euch von einem Droplet aus verbinden und ein Backup einspielen oder eure Private IP zu den vertrauten Quellen hinzufügen.

Die von DO angebotene Migration über einen öffentlich zugänglichen Db Server finde ich keine gute Lösung. Hier sollte man einen SSH Tunnel zur Konfiguration anbieten

Für eine Migration benötigen wir erstmal ein lauffähiges System mit Datenbank. Dabei setzen wir in unserem Fall auf das Production Template von Shopware. Hier gehe ich nicht genauer drauf ein, da ich davon ausgehe das dies meisten dies schon haben.

git clone git@github.com:shopware/production.git && cd production && composer install 

bin/console system:setup

bin/console system:install --create-database --basic-setup

Nun migrieren wir die gerade erstellte oder bereits bestehende DB via mysql. Die Zugangsdaten werden euch in einem kleinen Panel bereits generiert – Ihr braucht nur einen Mysql Dump (.sql File)

mysql -u shopware6_user -p -h shopware6-mysql-do-user-XXXXXXXX-0.b.db.ondigitalocean.com -P 25060 -D shopware6 < shopware.sql

Beim ausführen könnte folgender Fehler erscheinen:

mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 3750 (HY000) at line 31: Unable to create or change a table without a primary key, when the system variable ’sql_require_primary_key‘ is set. Add a primary key to the table or unset this variable to avoid this message. Note that tables without a primary key can cause performance problems in row-based replication, so please consult your DBA before changing this setting.

Dazu gibt es hier einen Eintrag -> https://www.digitalocean.com/community/questions/how-to-disable-sql_require_primary_key-in-digital-ocean-manged-database-for-mysql

Ihr könnt dieses Setting via API setzen oder einfach an den Beginn der SQL File

SET SESSION sql_require_primary_key = 0;

sed -i '1s/^/SET SESSION sql_require_primary_key = 0;\n/' shopware.sql

schreiben.

Nun sollte der Import durchlaufen, dafür könnt Ihr auch mal in die Logs schauen

Point in time recovery

Eins der Features welches mich von der Managed DB noch überzeugt hat ist das wiederherstellen einer Datenbank zum Zeitpunkt X (+/- 5 min). Auf Rückfrage bei Digital Ocean wie genau dieses PITR ist kam folgende Antwort:

We are taking one daily full backup of the managed database and the bin-log is also backup every 5 minutes. Backups can be viewed and restored from the Cloud Control Panel by clicking on your cluster and going to the „Backups“ sub-tab. This will restore your entire database cluster to that point in time. As the write-ahead log backs up every five minutes, in case of any failure, the most recent writes to the database may be lost if the cluster needs to recover this way.

Digital Ocean Support

Test von PITR

Dafür habe ich zwei Bestellungen erstellt und einen Locust Load Test mit 50 Usersn laufen lassen

Bestellung um 10:44 Uhr
Bestellung um 08:17 UHr
Bestellübersicht nach PITR aus dem Timepicker von 09:15 Uhr
PITR Modal

Wichtig dazu, DO erstellt eine neue DB, das ganze dauert je nach Größe ein paar Minuten. In meinem Fall ca. 5min. Das sollte auch nur im absoluten Notfall gemacht werden (Anbindung hat alle Daten in SW6 verändert / Migrationsabbruch mit hartem Error und keiner Möglichkeit zu resumen etc.).

Redis Datenbank Erstellung

Nachtrag 04.10.2022

Zum aktuell Zeitpunkt empfehle ich die Redis DB von DO nicht mehr. Die Performance zeigte sich in Production unter aller Sau. Der Support wollte/konnte nicht helfen. Holt euch für Prod lieber ein Droplet für 14 USD und installiert dort schnell Redis und sichert es via interner IP und UFW ab. Damit seid Ihr 1 USD günstiger unterwegs und knapp 2-3 mal so schnell in der Connection Time und Ihr habt keine Timeouts (diese kamen Regelmäßig vor)

Nicht zwingend notwendig, da Ihr Redis auch im Cluster laufen lassen könnt, aber wenn Ihr die Redis Cart Integration (Obacht, bei mir funtkionierte im Default das entfernen des letzten Cart Items nicht: Stand 13.06.2022) nutzt würde ich dies der einfachheitshalber ebenfalls hier aufsetzen. Es kommt hier sicher auf euren Need an, mir sind die Kosten von 20-50 USD egal, wenn ich mich deshalb nicht um das managen und monitoren der Redis Pods kümmern muss.

doctl databases create shopware6-redis --engine=redis \
 --region=fra1 \
 --size=db-s-2vcpu-4gb \
 --num-nodes=2 \ 

redis – mysql cluster

Die ganzen Redis Connections könnt Ihr euch einfach per Shellscript generieren.

https://github.com/Isengo1989/k8s-digital-ocean-demo/tree/main/prod

./set-redis-connection.sh
#framework.yaml

framework:
  lock: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_LOCK_DB|'
  session:
      handler_id: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_SESSION_DB|'
  mailer:
    message_bus: 'messenger.default_bus'
# shopware.yaml
shopware:
  admin_worker:
    enable_admin_worker: false
  cart:
    redis_url: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_CART_DB|?persistent=1'
  cache:
    invalidation:
      http_cache: []
      delay: 0
      count: 150
  mail:
    update_mail_variables_on_send: false
  increment:
    user_activity:
      type: 'redis'
      config:
        url: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_INCREMENT_USER_DB|'
    message_queue:
      type: 'redis'
      config:
        url: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_INCREMENT_QUEUE_DB|'
  number_ranges:
    increment_storage: "Redis"
    redis_url: '|REDIS_DSN||REDIS_USER|:|REDIS_PASSWORD|@|REDIS_URL|:|REDIS_PORT|/|REDIS_NUMERRANGE_DB|'
Redis in K8s

Wenn Ihr Redis aus Kostengründen in K8s laufen lassen möchtet empfehle ich euch folgendes Tutorial.

https://www.containiq.com/post/deploy-redis-cluster-on-kubernetes

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.