Einrichten von MySQL und Importieren eines Dumps in Dockerfile

126

Ich versuche, eine Docker-Datei für mein LAMP-Projekt einzurichten, habe jedoch einige Probleme beim Starten von MySQL. Ich habe die folgenden Zeilen in meiner Docker-Datei:

VOLUME ["/etc/mysql", "/var/lib/mysql"]
ADD dump.sql /tmp/dump.sql
RUN /usr/bin/mysqld_safe & sleep 5s
RUN mysql -u root -e "CREATE DATABASE mydb"
RUN mysql -u root mydb < /tmp/dump.sql

Aber ich bekomme immer wieder diesen Fehler:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (111)

Irgendwelche Ideen zum Einrichten der Datenbankerstellung und des Dump-Imports während eines Dockerfile-Builds?

Vinnylinux
quelle
Dies liegt daran, dass jeder RUNBefehl in einem anderen Container ausgeführt wird. Es ist hier gut erklärt: stackoverflow.com/questions/17891669/…
Kuhess
Dies erklärt nur, dass RUN-Befehle unterschiedliche Kontexte haben. Aber ich bin abhängig von einem Daemon, nicht vom Kontext.
Winnylinux
1
Ja, aber das erklärt, warum Sie keine Verbindung zu MySQL herstellen können. Dies liegt daran, dass es nur in Ihrer ersten RUNZeile ausgeführt wird.
Kuhess
Um Ihre SQL-Anweisungen auszuführen, müssen Sie MySQL starten und den MySQL-Client im selben Container verwenden: einen RUNmit mehreren Schritten. Ein Beispiel für eine mehrstufige Installation einer Software finden Sie hier: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
Kuhess
Sie können auch einen Blick auf den Dienst mit Docker-Compose werfen : docs.docker.com/compose/wordpress/#build-the-project damit, MySQL kann an Ihre App angehängt werden
aurny2420289

Antworten:

122

Jeder RUNBefehl in a Dockerfilewird in einer anderen Schicht ausgeführt (wie in der Dokumentation vonRUN erläutert ).

In Ihrem Dockerfilehaben Sie drei RUNAnweisungen. Das Problem ist, dass der MySQL-Server erst im ersten gestartet wird. In den anderen Fällen wird kein MySQL ausgeführt. Aus diesem Grund wird der Verbindungsfehler mit dem mysqlClient angezeigt.

Um dieses Problem zu lösen, haben Sie 2 Lösungen.

Lösung 1: Verwenden Sie eine einzeilige RUN

RUN /bin/bash -c "/usr/bin/mysqld_safe --skip-grant-tables &" && \
  sleep 5 && \
  mysql -u root -e "CREATE DATABASE mydb" && \
  mysql -u root mydb < /tmp/dump.sql

Lösung 2: Verwenden Sie ein Skript

Erstellen Sie ein ausführbares Skript init_db.sh:

#!/bin/bash
/usr/bin/mysqld_safe --skip-grant-tables &
sleep 5
mysql -u root -e "CREATE DATABASE mydb"
mysql -u root mydb < /tmp/dump.sql

Fügen Sie diese Zeilen zu Ihrem hinzu Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
quelle
Haben Sie eine Idee, wie ich diese Informationen beibehalten kann? Die Datenbanken und Tabellen, die ich im Dockerfile-Build erstelle, sind nicht verfügbar, wenn ich Inhalte auf Containern ausführe. :(
Vinnylinux
1
Es kommt darauf an, was du mit persist meinst. Wenn Sie möchten, dass die Daten über mehrere docker runAusführungen hinweg erhalten bleiben, müssen Sie Volumes bereitstellen. Wenn Sie nur einen Container mit Ihrem Speicherauszug haben möchten, aber keine weiteren Änderungen beibehalten möchten, können Sie die VOLUMEAnweisung in Ihrem Speicher entfernen Dockerfile.
Kuhess
16
Ich bekomme ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)beim Versuch Lösung 1. mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlmysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Folgen Sie
1
Ich finde es viel besser, beim Start eine Skriptprüfung durchführen zu lassen, um festzustellen, ob MySQL eine Datenbank erstellt hat. Wenn dies der Fall ist, lassen Sie es in Ruhe, andernfalls führen Sie es aus mysql_init_dbund laden Sie es in Ihre Datenbankstruktur. Dies ermöglicht die Flexibilität, die Datenbank beim Testen automatisch zurücksetzen zu lassen. Wenn Sie jedoch möchten, dass sie erhalten bleibt oder verschiedene Datensätze testet, stellen Sie einfach ein Volume unter / var / lib / mysql mithilfe von bereit docker run -v ....
Tu-Reinstate Monica-dor duh
1
@revorgrayson - Das funktioniert auch nicht. Jetzt gibt es den Fehler FEHLER 2003 (HY000): Es kann keine Verbindung zum MySQL-Server unter '127.0.0.1' (111) hergestellt werden
Deep
166

Mit der neuesten Version des offiziellen MySQL-Docker-Images können Sie beim Start Daten importieren. Hier ist meine docker-compose.yml

data:
  build: docker/data/.
mysql:
  image: mysql
  ports:
    - "3307:3306"
  environment:
    MYSQL_ROOT_PASSWORD: 1234
  volumes:
    - ./docker/data:/docker-entrypoint-initdb.d
  volumes_from:
    - data

Hier habe ich meine data-dump.sql, unter docker/datader sich relativ zu dem Ordner befindet, aus dem das Docker-Compose ausgeführt wird. Ich mounte diese SQL-Datei in dieses Verzeichnis /docker-entrypoint-initdb.dauf dem Container.

Wenn Sie interessiert sind, wie dies funktioniert, schauen Sie sich deren docker-entrypoint.shin GitHub an. Sie haben diesen Block hinzugefügt, um den Import von Daten zu ermöglichen

    echo
    for f in /docker-entrypoint-initdb.d/*; do
        case "$f" in
            *.sh)  echo "$0: running $f"; . "$f" ;;
            *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f" && echo ;;
            *)     echo "$0: ignoring $f" ;;
        esac
        echo
    done

Ein zusätzlicher Hinweis: Wenn die Daten auch nach dem Stoppen und Entfernen des MySQL-Containers erhalten bleiben sollen, benötigen Sie einen separaten Datencontainer, wie Sie in der Datei docker-compose.yml sehen. Der Inhalt des Datencontainers Dockerfile ist sehr einfach.

FROM n3ziniuka5/ubuntu-oracle-jdk:14.04-JDK8

VOLUME /var/lib/mysql

CMD ["true"]

Der Datencontainer muss sich für die Persistenz nicht einmal im Startzustand befinden.

Rajiv
quelle
1
Dies ist meiner Meinung nach die bessere Antwort. Es erlaubt NICHT, selbst ein Bild zu erstellen. Danke für diesen Tipp.
Metal3d
5
Eine geringfügige Änderung für Volumes: - Das - ./docker/data:/docker-entrypoint-initdb.dVerzeichnis .vor dem Container wurde entfernt.
David Sinclair
7
Obwohl dies das richtige Verfahren ist, wird der Anwendungsfall nicht sehr gut angesprochen. Einer der Vorteile von Docker ist, dass Sie eine Umgebung sehr schnell hochfahren können. Wenn Sie 3-4 Minuten warten müssen, während Docker beim Start eine MySQL-Datenbank importiert, verlieren Sie diesen Vorteil. Ziel ist es, einen Container zu haben, der die Daten bereits in der Datenbank enthält, damit Sie sie so schnell wie möglich verwenden können.
Garreth McDaid
3
Funktioniert diese Antwort noch mit den neuesten Versionen? scheint nicht mehr zu funktionieren
Daniel
2
Beachten Sie, dass das Docker-Compose-Beispiel hier v2 und nicht v3 ist. "volume_from:" wird in Version 3 nicht unterstützt.
Ernest
37

Ich habe meinen SQL-Dump in einen "db-dump" -Ordner heruntergeladen und gemountet:

mysql:
 image: mysql:5.6
 environment:
   MYSQL_ROOT_PASSWORD: pass
 ports:
   - 3306:3306
 volumes:
   - ./db-dump:/docker-entrypoint-initdb.d

Wenn ich docker-compose upzum ersten Mal starte, wird der Speicherauszug in der Datenbank wiederhergestellt.

Petru
quelle
3
+1, mit einem Zusatz: Der Link zum Skript wird ausgeführt, um besser zu verstehen, was tatsächlich passiert: github.com/docker-library/mariadb/blob/…
Tom Imrei
6
Die neueste mysqlVersion scheint die gedumpte SQL-Datei nicht zu laden, und selbst bei Verwendung von 5.6 habe ich Probleme mit InnoDb-Speicher-Engines.
Zinking
1
das gleiche hier, es hat eine solche Anweisung und ich habe sogar in der Protokollierung gesehen, dass Init-Dateien geladen werden, aber db ist leer!
Holms
11

Ich habe den Ansatz docker-entrypoint-initdb.d verwendet (Dank an @Kuhess). In meinem Fall möchte ich meine Datenbank jedoch anhand einiger Parameter erstellen, die ich in der ENV-Datei definiert habe

1) Zuerst definiere ich eine solche ENV-Datei in meinem Docker-Stammprojektverzeichnis

MYSQL_DATABASE=my_db_name
MYSQL_USER=user_test
MYSQL_PASSWORD=test
MYSQL_ROOT_PASSWORD=test
MYSQL_PORT=3306

2) Dann definiere ich meine Datei docker-compose.yml. Also habe ich die args-Direktive verwendet, um meine Umgebungsvariablen zu definieren, und sie aus der ENV-Datei festgelegt

version: '2'
services:
### MySQL Container
    mysql:
        build:
            context: ./mysql
            args:
                - MYSQL_DATABASE=${MYSQL_DATABASE}
                - MYSQL_USER=${MYSQL_USER}
                - MYSQL_PASSWORD=${MYSQL_PASSWORD}
                - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        ports:
            - "${MYSQL_PORT}:3306"

3) Dann definiere ich einen MySQL-Ordner, der eine Docker-Datei enthält. Das Dockerfile ist das also

FROM mysql:5.7
RUN chown -R mysql:root /var/lib/mysql/

ARG MYSQL_DATABASE
ARG MYSQL_USER
ARG MYSQL_PASSWORD
ARG MYSQL_ROOT_PASSWORD

ENV MYSQL_DATABASE=$MYSQL_DATABASE
ENV MYSQL_USER=$MYSQL_USER
ENV MYSQL_PASSWORD=$MYSQL_PASSWORD
ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD

ADD data.sql /etc/mysql/data.sql
RUN sed -i 's/MYSQL_DATABASE/'$MYSQL_DATABASE'/g' /etc/mysql/data.sql
RUN cp /etc/mysql/data.sql /docker-entrypoint-initdb.d

EXPOSE 3306

4) Jetzt benutze ich mysqldump, um meine Datenbank zu sichern und die Datei data.sql in den Ordner mysql zu legen

mysqldump -h <server name> -u<user> -p <db name> > data.sql

Die Datei ist nur eine normale SQL-Dump-Datei, aber ich füge am Anfang 2 Zeilen hinzu, damit die Datei so aussieht

--
-- Create a database using `MYSQL_DATABASE` placeholder
--
CREATE DATABASE IF NOT EXISTS `MYSQL_DATABASE`;
USE `MYSQL_DATABASE`;

-- Rest of queries
DROP TABLE IF EXISTS `x`;
CREATE TABLE `x` (..)
LOCK TABLES `x` WRITE;
INSERT INTO `x` VALUES ...;
...
...
...

Was also passiert ist, dass ich den Befehl "RUN sed -i / MYSQL_DATABASE / '$ MYSQL_DATABASE' / g '/etc/mysql/data.sql" verwendet habe, um den MYSQL_DATABASEPlatzhalter durch den Namen meiner Datenbank zu ersetzen, in der ich ihn festgelegt habe .env Datei.

|- docker-compose.yml
|- .env
|- mysql
     |- Dockerfile
     |- data.sql

Jetzt können Sie Ihren Container erstellen und ausführen

Saman Shafigh
quelle
Ich mag den Ansatz, eine .envDatei zu verwenden. Doch nach dieser , die .envnur Datei Feature funktioniert , wenn Sie die Verwendung docker-compose upBefehl und funktioniert nicht mit docker stack deploy. Wenn Sie die .envDatei also in der Produktion verwenden möchten, sollten Sie die Docker-Geheimnisse überprüfen , die mit eingeführt wurden docker 17.06. Dann können Sie Ihre .env Datei in Verbindung mit Geheimnisse in beiden Entwicklung nutzen docker compose upund docker stack deployProduktionsphasen
ira
Netter Ansatz, aber ich würde vorschlagen, RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE ;\n/' data.sqlanstelle des von sedIhnen vorgeschlagenen Befehls zu verwenden. Auf diese Weise können Sie jede Dump-Datei verwenden, die Sie haben - es ist nützlich, wenn Sie mit großen Datenmengen arbeiten :)
Tomasz Kapłoński
8

Hier ist eine Arbeitsversion mit v3dem docker-compose.yml. Der Schlüssel ist , die Volumina Richtlinie:

mysql:
  image: mysql:5.6
  ports:
    - "3306:3306"
  environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_USER: theusername
    MYSQL_PASSWORD: thepw
    MYSQL_DATABASE: mydb
  volumes:
    - ./data:/docker-entrypoint-initdb.d

In dem Verzeichnis, das ich habe, habe docker-compose.ymlich ein Verzeichnis data, das .sqlDump-Dateien enthält . Das ist schön, weil man eine haben kann.sql Dump-Datei pro Tabelle haben können.

Ich renne einfach docker-compose upund ich kann loslegen. Die Daten bleiben zwischen den Stopps automatisch erhalten. Wenn Sie die Daten entfernen möchten , und „saugen“ in den neuen .sqlDateien laufen docker-compose downdann docker-compose up.

Wenn jemand weiß, wie er den mysqlDocker dazu /docker-entrypoint-initdb.dbringt , Dateien erneut zu verarbeiten, ohne das Volume zu entfernen, hinterlassen Sie bitte einen Kommentar, und ich werde diese Antwort aktualisieren.

rynop
quelle
2
Diese Antwort hat mir geholfen. Der Hinweis zu docker-compose downwar sehr wichtig, da ich trotz des Neustarts von Docker-Compose keine Änderungen sah. MYSQL_DATABASE ist in diesem Fall eine erforderliche Variable
nxmohamad