[PYTHON] Memo zur Migration von Djangos DB von SQLite3 nach MySQL auf Docker auf Raspberry Pi 4B

Zweck dieser Zeit

Es ist ein bisschen chaotisch.

  1. Migrieren Sie Ihr eigenes Django-Projekt, das (direkt) auf Raspberry Pi 3B + ausgeführt wurde, auf Raspberry Pi 4B
  2. Die SQLite3-Datenbank ist aufgebläht (Vakuum ca. 120 MB) und wurde auf MySQL umgestellt, um Parallelität sicherzustellen.
  3. Stellen Sie sicher, dass Sie das Django-Projekt mit Docker-Compose ausführen.
  4. Lassen Sie uns MySQL mit Docker-Compose ausführen
  5. Führen Sie ein periodisches Ausführungsskript aus, das die Datenbank automatisch mit Docker-Compose aktualisiert

1. Ersteinrichtung von Raspberry Pi 4B

Richten Sie zuerst den Raspberry Pi 4B ein.

Ich habe gehört, dass Stretch, das auf 3B + funktioniert, auf 4B nicht funktioniert, deshalb habe ich einen neuen Raspbian Buster von Raspbian für Raspberry Pi herunterladen mitgebracht. Brennen Sie auf eine Micro-SD-Karte. Buster ist Lite anstelle von Desktop, da es anscheinend Speicher spart (obwohl es zugenommen hat) (für den Servergebrauch sowieso).

~~ Ich hatte kein Mini-HDMI-Kabel / Adapter (es ist schwer, obwohl es Micro ist ...), also ~~ (20/03/07 Nachtrag: falsch. Micro HDMI. Mini ist ein dünner Typ?) Monitorlos einrichten. Es kostet 100 Yen, also besorgen Sie es sich bei Bedarf. Legen Sie die Micro SD-Karte in das Hauptgerät ein und verbinden Sie sie mit einem LAN-Kabel mit dem Router. Schließen Sie ein 5V 3A Typ-C-Netzteil an (fest, von Micro ersetzen) und schalten Sie das Gerät ein (Wenn Sie das Netzteil nicht ersetzen möchten, weil die Konvertierung zwischen Micro-USB und Typ-C zu 100% zu sein scheint, ist dies etwas beängstigend).

Überprüfen Sie die vom Router über DHCP zugewiesene IP-Adresse und stellen Sie im LAN eine SSH-Verbindung (anfängliches Kennwort) her.

Wenn "Zu viele Authentifizierungsfehler" angezeigt wird oder mit "publickey" gespielt wird, kann die Authentifizierung nicht mit dem öffentlichen Schlüssel erfolgen. Setzen Sie daher als Option des Befehls ssh "-o PreferredAuthentications = password" oder "-o PubkeyAuthentication = no". Oder fügen Sie ~ / .ssh / config`` PreferredAuthentications password oder PubkeyAuthentication no hinzu.

Passwort ändern / Benutzernamen ändern

Ändern Sie gleichzeitig das Passwort und dann den Benutzernamen. Sie können den Benutzernamen nicht ändern, während Sie beim pi-Benutzer angemeldet sind. Erstellen Sie daher einen neuen sudoer-Benutzer und melden Sie sich erneut an (diesmal im LAN, sodass Sie möglicherweise ein temporäres Root-Kennwort festlegen können).

Erstellen Sie einen tmpuser, indem Sie den folgenden Befehl als pi-Benutzer ausführen.

#Das Home-Verzeichnis wurde nicht für useradd erstellt
sudo useradd tmpuser
sudo passwd tmpuser

Fügen Sie dann tmpuser zu sudoers hinzu.

sudo adduser tmpuser sudo

Wenn Sie einen kleinen Umweg wünschen, bearbeiten Sie / etc / sudoers und fügen Sie tmpuser hinzu. Irgendwie sind / etc / sudoers und andere schreibgeschützt (chmod ist in Ordnung), also erstelle /etc/sudoers.d/011_tmpuser (du kannst es der sudo-Gruppe hinzufügen).

# /etc/sudoers.d/011_tmpuser
tmpuser ALL=(ALL:ALL) ALL

Melden Sie sich einmal ab, melden Sie sich erneut als tmpuser-Benutzer an, ändern Sie den Namen des pi-Benutzers mit dem folgenden Befehl, ändern Sie den Namen der pi-Gruppe und verschieben Sie schließlich das Ausgangsverzeichnis.

sudo usermod -l NEW_NAME pi
sudo groupmod -n NEW_NAME pi
sudo usermod -m -d /home/NEW_NAME NEW_NAME

Melden Sie sich vom Benutzer tmpuser ab, melden Sie sich erneut als Benutzer NEW_NAME an und löschen Sie den Benutzer tmpuser. Wenn Sie einen Umweg machen, löschen Sie auch /etc/sudoers.d/011_tmpuser. Standardmäßig gehört der pi-Benutzer zur sudo-Gruppe, sodass der Benutzer NEW_NAME nicht erneut zu sudoers hinzugefügt werden muss (sollte).

sudo userdel tmpuser
# sudo rm /etc/sudoers.d/011_tmpuser

Änderung des Hostnamens

# /etc/hostname
NEW_HOSTNAME

# /etc/hosts
...
127.0.1.1 NEW_HOSTNAME

Authentifizierung mit öffentlichem Schlüssel

Registrieren Sie den öffentlichen Schlüssel beim Benutzer NEW_NAME und bearbeiten Sie / etc / ssh / sshd_config, um die SSH-Authentifizierung nur zum öffentlichen Schlüssel zu machen.

pi Seite

mkdir ~/.ssh
chmod 700 ~/.ssh

Host-Seite

cd ~/.ssh
ssh-keygen -f KEY_NAME
scp KEY_NAME.pub RPI4_HOST:.ssh/

pi Seite

cd ~/.ssh
cat KEY_NAME.pub >> authorized_keys
chmod 600 authorized_keys

Sie müssen lediglich "IdentityFile" in ~ / .ssh / config angeben. Wenn immer noch "Zu viele Authentifizierungsfehler" angezeigt wird, fügen Sie "IdentitiesOnly yes" hinzu.

Bearbeiten Sie / etc / ssh / sshd_config, um bei Bedarf PasswordAuthentication no festzulegen.

2. Docker / Docker-Compose auf Raspberry Pi 4B eingeführt

sudo curl -fsSL https://get.docker.com/ | sh
sudo apt install python3-pip
sudo apt install libffi-dev
sudo pip3 install docker-compose

3. Migrieren Sie das Django-Projekt nach Docker / Docker-Compose

Da das Django-Projekt von git verwaltet wurde, habe ich das Programm mit "git clone" auf den neuen Server migriert. DB (SQLite3) wird mit scp migriert.

Da die Umgebung von virtualenv auf dem alten Server verwaltet wurde, wird von hier aus die Datei require.txt generiert.

pip3 freeze > requirements.txt

Da es eine große Sache ist, werden wir die Umgebung für den neuen Server mit Docker / Docker-Compose erstellen. Es war eine Konfiguration von Django: wsgi-gunicorn-nginx, aber vor allem war es ein Betriebstest für sich.

# Dockerfile
FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
# docker-compose.yml
version: '3'

services:
    web:
        build: .
        command: python manage.py runserver 0.0.0.0:8000
        volumes:
            - .:/code
        ports:
            - "127.0.0.1:8000:8000"
        environment:
            - ENVIRONMENT=production
sudo docker-compose up

4. Führen Sie MySQL auf Docker aus (Docker-Compose / Raspberry Pi 4).

Führen Sie MySQL (MariaDB) mit Docker-Compose auf Raspberry Pi aus. Verwenden Sie jsurf / rpi-mariadb.

...
    db:
        # image: mariadb
        image: jsurf/rpi-mariadb
        command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
        volumes:
            - DATABASE_DIRECTORY:/var/lib/mysql
        environment:
            - MYSQL_ROOT_PASSWORD=ROOT_PASSWORD
            - MYSQL_DATABASE=DATABASE_NAME
            - MYSQL_USER=USER
            - MYSQL_PASSWORD=PASSWORD
    web:
...

5. Ändern Sie die DB-Einstellungen von Django von SQLite3 in MySQL

Ich werde es später zurückgeben. Um die Rückgabe zu vereinfachen, werde ich entsprechend mit settings.py von Django spielen und die DB aus der Umgebungsvariablen angeben.

DATABASE_ENGINE = os.environ.get('DATABASE_ENGINE', 'django.db.backends.sqlite3')
DATABASE_OPTIONS = {}
if DATABASE_ENGINE == 'django.db.backends.mysql':
    DATABASE_OPTIONS = {
        'charset': os.environ.get('DATABASE_CHARSET'),
    }

DATABASES = {
    'default': {
        'ENGINE': DATABASE_ENGINE,
        'HOST': os.environ.get('DATABASE_HOST'),
        'PORT': os.environ.get('DATABASE_PORT'),
        'NAME': os.environ.get('DATABASE_NAME', os.path.join(BASE_DIR, 'db.sqlite3')),
        'USER': os.environ.get('DATABASE_USER'),
        'PASSWORD': os.environ.get('DATABASE_PASSWORD'),
        'OPTIONS': DATABASE_OPTIONS,
    },
}

Bearbeiten Sie docker-compose.yml wie folgt. Kommentieren Sie den gesamten DATABASE-Teil der Umgebung aus oder erstellen Sie eine andere "docker-compose.yml", damit die Datenbank an SQLite3 zurückgegeben werden kann.

    web:
...
        environment:
            - ENVIRONMENT=production
            - DATABASE_ENGINE=django.db.backends.mysql
            - DATABASE_HOST=db
            - DATABASE_PORT=3306
            - DATABASE_NAME=DATABASE_NAME
            - DATABASE_USER=USER
            - DATABASE_PASSWORD=PASSWORD
            - DATABASE_CHARSET=utf8mb4
        depends_on:
            - db

Fügen Sie "PyMySQL" zu "require.txt" hinzu und fügen Sie oben in "manage.py" Folgendes hinzu.

if os.environ.get('DATABASE_ENGINE') == 'django.db.backends.mysql':
    import pymysql
    pymysql.install_as_MySQLdb()

Wenn Django zugreift, bevor die Initialisierung von MySQL abgeschlossen ist, wird Django einen Fehler löschen. Führen Sie daher "Docker-Compose Up" bei der ersten Ausführung erneut aus. Wenn Django bei der zweiten Migration zuerst gestartet wird, können Sie einen entsprechenden Ruhezustand einfügen oder ein Warteskript erstellen. Nach dem Sandwiching von Gunicorn im 9. Abschnitt verschwindet der Fehler (wie), selbst wenn Django (Gunicorn) zuerst startet, sodass Sie sich möglicherweise nicht zu viele Sorgen machen müssen.

        command: bash -c "sleep 5 && python manage.py runserver 0.0.0.0:8000"

6. Beseitigung des Migrationsfehlers

Abhängig von der Definition des DB-Modells geben "sudo docker-compose up -d" und "sudo docker-compose exec web python3 manage.py migrate" einen Fehler aus. Wenn Sie beispielsweise ein TextField mit einer eindeutigen Einschränkung haben und nicht "max_length" angeben. Dieses Mal habe ich die URL von TextField anstelle von URLField in URLField geändert und max_length (255 oder weniger) für TextField angegeben, das als kurze Zeichenfolge bekannt ist, und es gelöst (auf Raspberry Pi reicht dies jedoch nicht aus. Es hat nicht funktioniert, daher habe ich die eindeutige Einschränkung später entfernt.

In diesem Bereich habe ich das Projekt und die Datenbank zur Beschleunigung auf die Hauptmaschine verschoben und Experimente durchgeführt. Dieses Mal muss das Image von MySQL geändert werden. Der gute Punkt von Docker ist jedoch, dass es (im Allgemeinen) automatisch dieselbe Umgebung vorbereitet und die Host-Umgebung nicht verschmutzt / beeinträchtigt (kompatibel ohne offizielles Image). Sex macht mir Sorgen ...?).

7. Exportieren Sie Django DB-Daten nach json

Nachdem Sie den Migrationsfehler behoben haben, geben Sie die Datenbank an SQLite3 zurück, migrieren Sie sie und sichern Sie die Daten an json.

sudo docker-compose run web bash
python3 manage.py makemigrations
python3 manage.py migrate
# python3 manage.py dumpdata > dump.json
python3 manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.Permission > dump.json

8. Schreiben Sie Django DB-Daten von json zurück

python3 manage.py migrate
python3 manage.py loaddata dump.json
django.db.utils.IntegrityError: Problem installing fixture '/code/dump.json': Could not load APP.MODELNAME(pk=PK_NUM): (1062, "Duplicate entry 'ONE_FIELD_NUM' for key 'ONE_FIELD'")

Es scheint nicht gut zu sein, die Einschränkung unique_together für OneToOneField festzulegen (OneToOne kann nicht einzeln verwendet werden), daher habe ich sie in ForeignKey geändert. Zu diesem Zeitpunkt funktionierte es zwar nicht mit mariadb, aber nicht, da der Fehler um die Schlüssellänge wahrscheinlich nicht verschwand, weil er in jsurf / rpi-mariadb auf utf8mb4 gesetzt war, sodass ich die eindeutige Einschränkung aller Zeichenketten entfernte. Außerdem musste ich die Dateien unter Migrationen direkt neu schreiben, da die Migration hier auf halbem Weg gestoppt wurde. Selbst wenn ich die von einem anderen PC verarbeitete Datenbank direkt gesendet habe, hat sie nicht funktioniert, sodass ich mir immer noch Sorgen um die Kompatibilität mache. Nach vielen Versuchen und Fehlern konnte ich endlich Daten laden.

9. Platzieren Sie Gunicorn zwischen dem Host-Webserver (Reverse Proxy) und Django

Füge gunicorn zu require.txt hinzu.

Bearbeiten Sie "docker-compose.yml". Da es Speicher verbraucht (glaube ich), passen Sie die Anzahl der Arbeiter -w nach Bedarf an.

        # command: /usr/local/bin/gunicorn -w 4 -b 0.0.0.0:8000 MY_PROJECT.wsgi -t 300
        command: bash -c "sleep 5 && gunicorn -w 4 -b 0.0.0.0:8000 MY_PROJECT.wsgi -t 300"

Fügen Sie wie bei manage.py oben in wsgi.py Folgendes hinzu.

if os.environ.get('DATABASE_ENGINE') == 'django.db.backends.mysql':
    import pymysql
    pymysql.install_as_MySQLdb()

10. Führen Sie das Skript für die regelmäßige Ausführung in Docker aus

(20/02/15 Nachtrag)

Ich habe die Einstellungen mit Busybox Crond unten vorgenommen, aber es scheint, dass das Produktionsskript aufgrund der Unannehmlichkeiten im Protokoll nicht gut funktioniert hat. Deshalb habe ich ein Skript für die regelmäßige Ausführung in Python und einen Container mit derselben Konfiguration wie der Django-Container geschrieben Ich beschloss, einen anderen zu machen und ihn auszuführen. Da es jedoch redundant ist, ist es möglicherweise besser, einen Endpunkt für die regelmäßige Ausführung auf der Django-Containerseite zu erstellen und ihn zu einem Container zu machen, der nur HTTP-Anforderungen überspringt.


(Alte Version)

Führen Sie dieses Mal einen regulären Lauf im selben Container wie Django aus.

Bisher wurde das periodische Ausführungsskript normalerweise in Python geschrieben oder vom Systemd-Timer ausgeführt. Diesmal war es systemd / timer, also habe ich versucht, es in den Docker-Container zu verschieben, aber obwohl ich das Skript im Docker-Container vom Host mit exec ausführen kann, führe ich systemd / timer im Docker-Container aus Ich bin mir nicht sicher.

Wie auch immer, das Basis-Betriebssystem von python: 3 ist Debian und es scheint kein systemd (init.d) zu geben, also werde ich es regelmäßig mit cron ausführen.

Es war mein erstes Mal, dass ich Cron benutzte, also habe ich mich endlich verlaufen.

Ich möchte die von Docker angegebenen Umgebungsvariablen erben, daher verwende ich crond, das in der Busybox enthalten ist.

Erstellen Sie zunächst die folgende Datei "crontab" im Ausführungsverzeichnis.

# * * * * * cd /code && echo `env` >> env.txt
0 */6 * * * cd /code && /usr/local/bin/python3 AUTORUN_SCRIPT.py

Oben ist die Einstellung zum Schreiben der Umgebungsvariablen jede Minute in eine Datei (zum Debuggen) und unten die Einstellung zum automatischen Ausführen von /code/AUTORUN_SCRIPT.py als Root-Benutzer und Arbeitsverzeichnis / Code alle 6 Stunden. Die Zeit ist in Ordnung mit JST.

Definieren Sie als Nächstes die Installation von crond und das Hinzufügen der Konfigurationsdatei in der Docker-Datei. Die richtige Antwort lautet, dass / var / spool / cron / crontabs / root eher eine Datei als ein Verzeichnis ist.

# Dockerfile
...
RUN apt update && apt install -y \
  busybox-static
ENV TZ Asia/Tokyo
COPY crontab /var/spool/cron/crontabs/root
...

Stellen Sie dann sicher, dass crond startet, wenn der Docker-Container startet. Beachten Sie, dass "CMD" in "Dockerfile" nicht ausgeführt wird, da wir diesmal Docker-Compose verwenden. Fügen Sie stattdessen den Startbefehl für crond zu command in docker-compose.yml hinzu. Da crond im Hintergrund ausgeführt wird, wird gunicorn automatisch gestartet.

# docker-compose.yml
...
        # command: bash -c "busybox crond && gunicorn -w 4 -b 0.0.0.0:8000 MY_PROJECT.wsgi -t 300"
        command: bash -c "sleep 5 && busybox crond && gunicorn -w 4 -b 0.0.0.0:8000 MY_PROJECT.wsgi -t 300"
...

Da ich mit der DB von Django spiele, habe ich pymysql install in AUTORUN_SCRIPT.py hinzugefügt (wie manage.py, wsgi.py) und bestätigt, dass das experimentelle Skript funktioniert. .. Löschen Sie die Cron-Einstellungen für das Debuggen und vervollständigen Sie die Einstellungen.

11. Automatische Ausführung beim Start und Hintergrundausführung

Fügen Sie restart: always zu docker-compose.yml hinzu, damit es automatisch startet, wenn der Host gestartet wird, und starten Sie es im Hintergrund mit sudo docker-compose up -d. Danach können Sie den Host neu starten (sudo docker-compose ps, sudo docker ps).

Ergebnis

Wir haben die Hardware (Raspberry Pi) erfolgreich migriert, die Datenbank migriert, die DB-Engine migriert, auf Docker migriert und sie dauerhaft gemacht.

Die Leistung hat sich (anscheinend) aufgrund von Verbesserungen der Hardwarespezifikationen und der Migration zu MySQL verbessert, und DB-Vorgänge können jetzt parallel (wie) ausgeführt werden, also beim gleichzeitigen Zugriff auf die DB Der aufgetretene Fehler "Datenbank ist gesperrt" wird nicht mehr angezeigt.

Es ist ein persönliches Projekt, also versuche ich, die Protokolleinstellungen zu brechen oder sie eindeutig zu schneiden ... Ich habe aufgegeben, weil ich lange gebraucht habe. Nach dem Laden der Daten kann es jedoch möglich sein, durch Migration ein eindeutiges Ergebnis zurückzugeben.

Danach dachte ich, dass es besser wäre, cron in separate Container zu trennen, aber da es genau die gleiche Abhängigkeit wie das Django-Projekt hat, habe ich es zusammengestellt, ohne es zu teilen. Wie teilt man das auf ...?

Recommended Posts

Memo zur Migration von Djangos DB von SQLite3 nach MySQL auf Docker auf Raspberry Pi 4B
Stellen Sie mit Python auf Raspberry Pi eine Verbindung zu MySQL her
Memo zur Bereitstellung von Django × Postgresql auf Docker für Heroku
Installation von Docker auf Raspberry Pi und L Chika
Port FreeRTOS auf Raspberry Pi 4B
Himbeer Pi 4 Centos7 auf Docker installieren
Installieren Sie ghoto2 auf Raspberry Pi (Hinweis)
Ausgabe von Raspberry Pi an Line
Hinweise zum Verbinden von Bluetooth von einem Smartphone / PC mit Raspeye 4
USB-Boot auf Raspberry Pi 4 Model B.
Herstellen einer Verbindung von Python zu MySQL unter CentOS 6.4
Erstellen Sie eine OpenCV-Python-Umgebung auf Raspberry Pi B +
So installieren Sie NumPy auf Raspeye
Warum DetectMultiScale () auf Raspberry Pi B + langsam ist
Erstellen Sie eine Django-Umgebung auf Raspai (MySQL)
Stellen Sie DHT11 mit Raspeye + Python zur Verfügung (Hinweis)
Es kann keine Verbindung zu MySQL über die Docker-Umgebung (Debian) hergestellt werden.
Einführung von Ceph mit Kubernetes auf Raspberry Pi 4B (ARM64)
Reverse Proxy von Apache auf GCP zu lokalem Raspeye Apache (Wake on LAN über NAT [3])
Vom Einrichten des Raspberry Pi bis zur Installation der Python-Umgebung
Führen Sie die LED-Matrix interaktiv mit Raspberry Pi 3B + auf Slackbot aus
Implementierung von persönlich optimierten Fotorahmen mit Raspberry Pi
Steuern Sie das Ein- und Ausschalten des USB-Anschlusses des Raspberry Pi
Ausgabe auf "7 Segment LED" mit Python mit Raspberry Pi 3!
Spielen Sie, um Slack mithilfe von AWS PaaS über Raspberry Pi3 über Umgebungsdaten von SensorTag zu informieren