Konfigurowanie MySQL i importowanie zrzutu w Dockerfile

127

Próbuję skonfigurować plik Dockerfile dla mojego projektu LAMP, ale mam kilka problemów podczas uruchamiania MySQL. Mam następujące wiersze w moim pliku Dockerfile:

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

Ale ciągle otrzymuję ten błąd:

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

Masz jakieś pomysły, jak skonfigurować tworzenie bazy danych i importować zrzuty podczas kompilacji pliku Dockerfile?

vinnylinux
źródło
Wynika to z faktu, że każde RUNpolecenie jest wykonywane w innym kontenerze. Jest to dobrze wyjaśnione tutaj: stackoverflow.com/questions/17891669/…
Kuhess
To tylko wyjaśnia, że ​​polecenia RUN mają różne konteksty. Ale jestem zależny od demona, a nie od kontekstu.
vinnylinux
1
Tak, ale to wyjaśnia, dlaczego nie możesz połączyć się z MySQL. Dzieje się tak, ponieważ działa tylko w pierwszej RUNlinii.
Kuhess
Aby wykonać swoje instrukcje SQL, musisz uruchomić MySQL i używać klienta MySQL w tym samym kontenerze: jeden RUNz kilkoma krokami. Przykład z wieloetapową instalacją oprogramowania można znaleźć tutaj: stackoverflow.com/questions/25899912/install-nvm-in-docker/ ...
Kuhess
Możesz również spojrzeć na usługę z docker -compose: docs.docker.com/compose/wordpress/#build-the-project z tym, że mysql można dołączyć do swojej aplikacji
aurny2420289

Odpowiedzi:

122

Każda RUNinstrukcja w a Dockerfilejest wykonywana w innej warstwie (jak wyjaśniono w dokumentacjiRUN ).

W swoim Dockerfilemasz trzy RUNinstrukcje. Problem w tym, że serwer MySQL jest uruchamiany dopiero w pierwszej. W innych nie działa MySQL, dlatego pojawia się błąd połączenia z mysqlklientem.

Aby rozwiązać ten problem, masz 2 rozwiązania.

Rozwiązanie 1: użyj jednej linii 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

Rozwiązanie 2: użyj skryptu

Utwórz wykonywalny skrypt 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

Dodaj te wiersze do Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
źródło
Czy masz pojęcie, jak mogę utrwalić te informacje? Bazy danych i tabele, które tworzę w kompilacji Dockerfile, nie są dostępne, gdy uruchamiam rzeczy na kontenerach. :(
vinnylinux
1
To zależy od tego, co masz na myśli, mówiąc wytrwałość. Jeśli chcesz, aby dane były utrwalane w kilku docker runwykonaniach, musisz zamontować woluminy. Jeśli chcesz tylko mieć kontener ze swoim zrzutem, ale nie chcesz utrwalać dalszych modyfikacji, możesz pozbyć się VOLUMEinstrukcji w swoim Dockerfile.
Kuhess
16
Otrzymuję ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2), próbując Rozwiązanie 1. Postępując zgodnie z logami mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlimysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Vituel
1
Uważam, że znacznie lepiej jest sprawdzić skrypt podczas uruchamiania, aby zobaczyć, czy mysql utworzył bazę danych. Jeśli tak, zostaw go w spokoju, w przeciwnym razie uruchom mysql_init_dbi załaduj strukturę bazy danych. Pozwala to na elastyczność automatycznego resetowania bazy danych podczas testowania, ale jeśli chcesz, aby utrzymywała lub testowała różne zestawy danych, po prostu montujesz wolumin w / var / lib / mysql za pomocą docker run -v ....
tu-Przywróć Monica-dor duh
1
@trevorgrayson - To też nie działa. Teraz wyświetla błąd ERROR 2003 (HY000): Nie można połączyć się z serwerem MySQL na '127.0.0.1' (111)
Deep
166

Najnowsza wersja oficjalnego obrazu dockera mysql umożliwia importowanie danych podczas uruchamiania. Oto mój plik 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

Tutaj mam docker/dataplik data-dump.sql, pod którym jest względny folder, z którego działa docker-compose. Podłączam ten plik sql do tego katalogu /docker-entrypoint-initdb.dw kontenerze.

Jeśli jesteś zainteresowany, aby zobaczyć, jak to działa, spójrz na ich docker-entrypoint.shw GitHub. Dodali ten blok, aby umożliwić importowanie danych

    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

Dodatkowa uwaga, jeśli chcesz, aby dane były utrwalane nawet po zatrzymaniu i usunięciu kontenera mysql, musisz mieć oddzielny kontener danych, jak widać w pliku docker-compose.yml. Zawartość kontenera danych Dockerfile jest bardzo prosta.

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

VOLUME /var/lib/mysql

CMD ["true"]

Aby zachować trwałość, kontener danych nie musi nawet znajdować się w stanie początkowym.

Rajiv
źródło
1
To jest lepsza odpowiedź IMHO. Pozwala NIE tworzyć obrazu siebie. Dzięki za tę wskazówkę.
Metal3d
5
Jedna drobna zmiana dla woluminów: - - ./docker/data:/docker-entrypoint-initdb.dusunięto .przed katalogiem kontenerów.
David Sinclair
7
Chociaż jest to poprawna procedura, nie odnosi się ona zbyt dobrze do przypadku użycia. Jedną z zalet Dockera jest to, że możesz bardzo szybko uruchomić środowisko. Jeśli musisz poczekać 3-4 minuty, aż Docker importuje bazę danych MySQL podczas uruchamiania, tracisz tę przewagę. Celem jest posiadanie kontenera, który zawiera już dane w bazie danych, abyś mógł z nich jak najszybciej skorzystać.
Garreth McDaid
3
czy ta odpowiedź nadal działa z najnowszymi wersjami? wydaje się już nie działać
Daniel
2
Zwróć uwagę, że przykład docker-compose tutaj to v2, a nie v3. „Volume_from:” nie jest obsługiwany w wersji 3.
Ernest
37

To, co zrobiłem, to pobranie zrzutu sql do folderu „db-dump” i zamontowanie go:

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

Kiedy uruchamiam docker-compose uppo raz pierwszy, zrzut jest przywracany w bazie danych.

Petru
źródło
3
+1, z jednym dodatkiem: link do uruchamianego skryptu w celu lepszego zrozumienia, co się właściwie dzieje: github.com/docker-library/mariadb/blob/ ...
Tom Imrei
6
najnowszy mysqlwydaje się nie ładuje zrzuconego pliku sql, a nawet używając 5.6 mam problem z silnikami pamięci masowej InnoDb.
mruganie
1
to samo tutaj, ma taką instrukcję, a nawet widziałem w logowaniu, które ładuje pliki init, ale db jest pusty!
holms
11

Użyłem podejścia docker-entrypoint-initdb.d (dzięki @Kuhess) Ale w moim przypadku chcę utworzyć moją bazę danych na podstawie niektórych parametrów, które zdefiniowałem w pliku .env, więc zrobiłem te

1) Najpierw definiuję plik .env coś takiego w moim głównym katalogu projektu docker

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

2) Następnie definiuję mój plik docker-compose.yml. Więc użyłem dyrektywy args do zdefiniowania moich zmiennych środowiskowych i ustawiłem je z pliku .env

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) Następnie definiuję folder mysql zawierający plik Dockerfile. Więc to jest plik Dockerfile

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) Teraz używam mysqldump do zrzucenia mojej bazy danych i umieszczenia data.sql w folderze mysql

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

Plik jest zwykłym plikiem zrzutu sql, ale dodaję 2 wiersze na początku, aby plik wyglądał tak

--
-- 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 ...;
...
...
...

Więc co się stało, to że użyłem polecenia "RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql", aby zastąpić MYSQL_DATABASEsymbol zastępczy nazwą mojej bazy danych, w której ją ustawiłem plik .env.

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

Teraz możesz zbudować i uruchomić swój kontener

Saman Shafigh
źródło
Podoba mi się podejście do korzystania z .envpliku. Jednak zgodnie z tym , .envfunkcja plik działa tylko podczas korzystania z docker-compose uppolecenia i nie pracować docker stack deploy. Tak więc, jeśli chcesz użyć .envpliku w środowisku produkcyjnym, możesz sprawdzić sekrety Dockera, które zostały wprowadzone w docker 17.06. Następnie można użyć pliku .env w połączeniu z tajemnic zarówno w rozwoju docker compose upi produkcji docker stack deployfazach
IRA
Niezłe podejście, ale sugerowałbym użycie RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE ;\n/' data.sqlzamiast sedsugerowanego polecenia. W ten sposób można użyć dowolnego pliku zrzutu masz - to przydatny jeśli pracujesz z dużą ilością danych :)
Tomasz Kapłoński
9

Oto działająca wersja korzystająca v3z docker-compose.yml. Kluczem jest dyrektywa o objętości :

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

W katalogu Mam docker-compose.ymlMam datadir, który zawiera .sqlpliki zrzutu. Jest to miłe, ponieważ możesz mieć .sqlplik zrzutu na tabelę.

Po prostu biegnę docker-compose upi jestem gotowy. Dane pozostają automatycznie między przystankami. Jeśli chcesz usunąć dane i „zassać” nowe .sqlpliki, uruchom docker-compose downwtedy docker-compose up.

Jeśli ktoś wie, jak zmusić mysqldockera do ponownego przetwarzania plików /docker-entrypoint-initdb.dbez usuwania woluminu, zostaw komentarz, a zaktualizuję tę odpowiedź.

rynop
źródło
2
ta odpowiedź pomogła mi. notatka włączona docker-compose downbyła bardzo ważna, ponieważ nie widziałem żadnych zmian pomimo ponownego uruchomienia docker-compose. MYSQL_DATABASE jest zmienną wymaganą w tym przypadku
nxmohamad