Menyiapkan MySQL dan mengimpor dump dalam Dockerfile

127

Saya mencoba menyiapkan Dockerfile untuk proyek LAMP saya, tetapi saya mengalami beberapa masalah saat memulai MySQL. Saya memiliki baris berikut di Dockerfile saya:

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

Tapi saya terus mendapatkan kesalahan ini:

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

Adakah ide tentang cara mengatur pembuatan database dan membuang impor selama pembuatan Dockerfile?

vinnylinux.dll
sumber
Ini karena fakta bahwa setiap RUNperintah dieksekusi dalam wadah yang berbeda. Ini dijelaskan dengan baik di sini: stackoverflow.com/questions/17891669/…
Kuhess
Ini hanya menjelaskan bahwa perintah RUN memiliki konteks yang berbeda. Tapi saya bergantung pada daemon, bukan konteksnya.
vinnylinux
1
Ya, tapi itu menjelaskan mengapa Anda tidak dapat terhubung ke MySQL. Itu karena, ini hanya berjalan di RUNbaris pertama Anda .
Kuhess
Untuk menjalankan pernyataan SQL Anda, Anda perlu memulai MySQL, dan menggunakan klien MySQL dalam wadah yang sama: satu RUNdengan beberapa langkah. Anda dapat menemukan contoh dengan penginstalan multi-langkah perangkat lunak di sini: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
Kuhess
Anda juga dapat melihat layanan dengan docker-compose: docs.docker.com/compose/wordpress/#build-the-project dengan itu, mysql dapat dilampirkan ke aplikasi Anda
aurny2420289

Jawaban:

121

Setiap RUNinstruksi dalam a Dockerfiledieksekusi di lapisan yang berbeda (seperti yang dijelaskan dalam dokumentasiRUN ).

Dalam Anda Dockerfile, Anda memiliki tiga RUNinstruksi. Masalahnya adalah bahwa server MySQL hanya dimulai di awal. Di sisi lain, tidak ada MySQL yang berjalan, itulah sebabnya Anda mendapatkan kesalahan koneksi dengan mysqlklien.

Untuk mengatasi masalah ini, Anda memiliki 2 solusi.

Solusi 1: Gunakan satu baris 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

Solusi 2: Gunakan skrip

Buat skrip yang dapat dieksekusi 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

Tambahkan baris ini ke Anda Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
sumber
Apakah Anda tahu bagaimana saya dapat mempertahankan informasi itu? Database dan tabel yang saya buat di build Dockerfile tidak tersedia ketika saya menjalankan sesuatu di container. :(
vinnylinux
1
Itu tergantung dari apa yang Anda maksud dengan bertahan. Jika Anda ingin data tetap ada di beberapa docker runeksekusi, Anda perlu memasang volume. Jika Anda hanya ingin memiliki wadah dengan dump Anda, tetapi Anda tidak ingin mempertahankan modifikasi lebih lanjut, Anda dapat membuang VOLUMEinstruksi di file Dockerfile.
Kuhess
16
Saya mendapatkan ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)ketika mencoba Solusi 1. Mengikuti log mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqldanmysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Vituel
1
Saya merasa jauh lebih baik untuk melakukan pemeriksaan skrip saat startup untuk melihat apakah mysql telah membuat database. Jika sudah, biarkan saja, jika tidak jalankan mysql_init_dbdan muat dalam struktur database Anda. Hal ini memungkinkan fleksibilitas menyetel ulang database secara otomatis saat pengujian, tetapi jika Anda ingin mempertahankan atau menguji kumpulan data yang berbeda, Anda cukup memasang volume di / var / lib / mysql menggunakan docker run -v ....
tu-Reinstate Monica-dor duh
1
@ trevorgrayson - Itu juga tidak berhasil. Sekarang memberikan kesalahan ERROR 2003 (HY000): Tidak dapat terhubung ke server MySQL pada '127.0.0.1' (111)
Jauh
166

Versi terbaru dari image buruh pelabuhan mysql resmi memungkinkan Anda untuk mengimpor data saat startup. Ini adalah buruh pelabuhan-compose.yml saya

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

Di sini, saya memiliki data-dump.sql saya docker/datayang relatif terhadap folder tempat penulisan galangan berjalan. Saya memasang file sql itu ke direktori ini /docker-entrypoint-initdb.ddi wadah.

Jika Anda tertarik untuk melihat cara kerjanya, lihat docker-entrypoint.shdi GitHub mereka. Mereka telah menambahkan blok ini untuk mengizinkan pengimporan data

    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

Catatan tambahan, jika Anda ingin data tetap ada bahkan setelah kontainer mysql dihentikan dan dihapus, Anda perlu memiliki wadah data terpisah seperti yang Anda lihat di docker-compose.yml. Isi dari wadah data Dockerfile sangat sederhana.

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

VOLUME /var/lib/mysql

CMD ["true"]

Penampung data bahkan tidak harus dalam keadaan mulai untuk persistensi.

Rajiv
sumber
1
Ini adalah jawaban yang lebih baik IMHO. Ini memungkinkan untuk TIDAK membuat gambar sendiri. Terima kasih atas tip ini.
Metal3d
5
Satu perubahan kecil untuk volume: - ./docker/data:/docker-entrypoint-initdb.d—hapus .di depan direktori container.
David Sinclair
7
Meskipun ini adalah prosedur yang benar, ini tidak menangani kasus penggunaan dengan baik. Salah satu kelebihan Docker adalah Anda dapat memutar lingkungan dengan sangat cepat. Jika Anda harus menunggu selama 3-4 menit sementara Docker mengimpor MySQL DB selama start up, Anda kehilangan keuntungan ini. Tujuannya agar memiliki container yang sudah berisi data dalam database, agar bisa digunakan secepat mungkin.
Garreth McDaid
3
apakah jawaban ini masih berfungsi dengan versi terbaru? tampaknya tidak berfungsi lagi
Daniel
2
Perhatikan bahwa contoh docker-compose di sini adalah v2 bukan v3. "volumes_from:" tidak didukung di v3.
Ernest
37

Yang saya lakukan adalah mengunduh sql dump saya di folder "db-dump", dan memasangnya:

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

Ketika saya menjalankan docker-compose upuntuk pertama kalinya, dump dikembalikan di db.

Petru
sumber
3
+1, dengan satu tambahan: tautan ke skrip dijalankan untuk lebih memahami apa yang sebenarnya terjadi: github.com/docker-library/mariadb/blob/…
Tom Imrei
6
terbaru mysqltampaknya tidak memuat file sql yang dibuang, dan bahkan menggunakan 5.6 Saya punya masalah dengan mesin penyimpanan InnoDb.
zinking
1
sama di sini, ia memiliki instruksi seperti itu dan saya bahkan melihat di logging yang memuat file init, tetapi db kosong!
holms
11

Saya menggunakan pendekatan docker-entrypoint-initdb.d (Terima kasih kepada @Kuhess) Tetapi dalam kasus saya, saya ingin membuat DB saya berdasarkan beberapa parameter yang saya tentukan dalam file .env jadi saya melakukan ini

1) Pertama saya mendefinisikan file .env seperti ini di direktori proyek root docker saya

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

2) Kemudian saya mendefinisikan file docker-compose.yml saya. Jadi saya menggunakan arahan args untuk mendefinisikan variabel lingkungan saya dan saya mengaturnya dari file .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) Lalu saya mendefinisikan folder mysql yang menyertakan Dockerfile. Jadi Dockerfile adalah ini

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) Sekarang saya menggunakan mysqldump untuk membuang db saya dan meletakkan data.sql di dalam folder mysql

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

File ini hanya file dump sql biasa tetapi saya menambahkan 2 baris di awal sehingga file akan terlihat seperti ini

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

Jadi yang terjadi adalah saya menggunakan perintah "RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql" untuk mengganti MYSQL_DATABASEplaceholder dengan nama DB saya yang telah saya setel di dalamnya file .env.

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

Sekarang Anda siap untuk membangun dan menjalankan penampung Anda

Saman Shafigh
sumber
Saya suka pendekatan menggunakan .envfile. Namun menurut ini , .envfitur file hanya berfungsi ketika Anda menggunakan docker-compose upperintah dan tidak berfungsi docker stack deploy. Jadi, jika Anda ingin menggunakan .envfile dalam produksi, Anda mungkin ingin memeriksa rahasia buruh pelabuhan yang diperkenalkan dengan docker 17.06. Kemudian Anda dapat menggunakan file .env bersama dengan rahasia dalam fase pengembangan docker compose updan produksidocker stack deploy
ira
Pendekatan yang bagus tetapi saya akan menyarankan untuk menggunakan RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE;\n/' data.sql daripada sedperintah yang Anda sarankan. Dengan cara ini Anda dapat menggunakan file dump apa pun yang Anda miliki - berguna jika Anda bekerja dengan data dalam jumlah besar :)
Tomasz Kapłoński
9

Berikut ini adalah versi kerja menggunakan v3dari docker-compose.yml. Kuncinya adalah arahan volume :

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

Di direktori yang saya miliki, saya docker-compose.ymlmemiliki datadir yang berisi .sqlfile dump. Ini bagus karena Anda dapat memiliki .sqlfile dump per tabel.

Saya hanya berlari docker-compose updan saya siap berangkat. Data secara otomatis tetap ada di antara perhentian. Jika Anda ingin menghapus data dan "menyedot" baru .sqlfile dijalankan docker-compose downkemudian docker-compose up.

Jika ada yang tahu cara meminta mysqlburuh pelabuhan untuk memproses ulang file /docker-entrypoint-initdb.dtanpa menghapus volume, silakan tinggalkan komentar dan saya akan memperbarui jawaban ini.

rynop
sumber
2
jawaban ini membantu saya. catatan di docker-compose downsangat penting karena saya tidak melihat perubahan meskipun memulai ulang docker-compose. MYSQL_DATABASE adalah variabel yang diperlukan dalam kasus ini
nxmohamad
0

Saya tidak terlalu menyukai jawaban yang diterima Kuhess sebagai sleep 5 tampaknya agak hackish bagi saya karena mengasumsikan bahwa daemon db mysql telah dimuat dengan benar dalam kerangka waktu ini. Itu asumsi, tidak ada jaminan. Juga jika Anda menggunakan image buruh pelabuhan mysql yang disediakan, image itu sendiri sudah mengatur tentang memulai server; Saya tidak akan mencampuri ini dengan kebiasaan /usr/bin/mysqld_safe.

Saya mengikuti jawaban lain di sekitar sini dan menyalin skrip bash dan sql ke dalam folder /docker-entrypoint-initdb.d/ di dalam kontainer buruh pelabuhan karena ini jelas merupakan cara yang dimaksudkan oleh penyedia gambar mysql. Semua yang ada di folder ini dijalankan setelah daemon db siap, oleh karena itu Anda harus dapat mengandalkannya.

Sebagai tambahan dari yang lain - karena tidak ada jawaban lain yang secara eksplisit menyebutkan ini: selain skrip sql Anda juga dapat menyalin skrip bash ke folder itu yang mungkin memberi Anda kontrol lebih.

Ini adalah yang saya perlukan misalnya karena saya juga perlu mengimpor dump, tetapi dump saja tidak cukup karena tidak menyediakan database mana yang harus diimpor. Jadi dalam kasus saya, saya memiliki skrip yang diberi nama db_custom_init.shdengan konten ini:

mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'create database my_database_to_import_into'
mysql -u root -p$MYSQL_ROOT_PASSWORD my_database_to_import_into < /home/db_dump.sql

dan Dockerfile ini menyalin skrip itu:

FROM mysql/mysql-server:5.5.62
ENV MYSQL_ROOT_PASSWORD=XXXXX
COPY ./db_dump.sql /home/db_dump.sql
COPY ./db_custom_init.sh /docker-entrypoint-initdb.d/

edit: Perhatikan bahwa ini hanya akan mengimpor sql dump pada saat pembuatan kontainer, bukan pada waktu pembuatan gambar!

onoSendai
sumber