การตั้งค่า MySQL และการนำเข้าดัมพ์ภายใน Dockerfile


127

ฉันกำลังพยายามตั้งค่า Dockerfile สำหรับโปรเจ็กต์ LAMP ของฉัน แต่ฉันมีปัญหาเล็กน้อยเมื่อเริ่ม MySQL ฉันมีบรรทัดต่อไปนี้บน 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

แต่ฉันได้รับข้อผิดพลาดนี้เรื่อย ๆ :

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

มีแนวคิดเกี่ยวกับวิธีตั้งค่าการสร้างฐานข้อมูลและการนำเข้าการถ่ายโอนข้อมูลระหว่างการสร้าง Dockerfile หรือไม่?


เนื่องจากแต่ละRUNคำสั่งดำเนินการในคอนเทนเนอร์ที่แตกต่างกัน มีการอธิบายไว้อย่างดีที่นี่: stackoverflow.com/questions/17891669/…
Kuhess

แค่นี้ก็อธิบายได้ว่าคำสั่ง RUN มีบริบทที่แตกต่างกัน แต่ฉันขึ้นอยู่กับภูตไม่ใช่บริบท
vinnylinux

1
ใช่ แต่นั่นอธิบายว่าทำไมคุณไม่สามารถเชื่อมต่อกับ MySQL ได้ เป็นเพราะมันทำงานในRUNบรรทัดแรกของคุณเท่านั้น
Kuhess

ในการดำเนินการคำสั่ง SQL ของคุณคุณจำเป็นต้องเริ่ม MySQL และใช้ไคลเอนต์ MySQL ในคอนเทนเนอร์เดียวกันซึ่งRUNมีหลายขั้นตอน คุณสามารถดูตัวอย่างการติดตั้งซอฟต์แวร์แบบหลายขั้นตอนได้ที่นี่: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
Kuhess

นอกจากนี้คุณยังสามารถดูบริการด้วยdocker -compose: docs.docker.com/compose/wordpress/#build-the-projectด้วยเหตุนี้ mysql สามารถเชื่อมต่อกับแอปของคุณได้
aurny2420289

คำตอบ:


121

แต่ละRUNคำสั่งใน a Dockerfileจะดำเนินการในเลเยอร์ที่แตกต่างกัน (ตามที่อธิบายไว้ในเอกสารประกอบRUN )

ในของDockerfileคุณคุณมีRUNคำแนะนำสามข้อ ปัญหาคือเซิร์ฟเวอร์ MySQL เริ่มต้นในครั้งแรกเท่านั้น ในส่วนอื่น ๆ ไม่มี MySQL ทำงานนั่นคือสาเหตุที่คุณได้รับข้อผิดพลาดในการเชื่อมต่อกับmysqlไคลเอนต์

เพื่อแก้ปัญหานี้คุณมี 2 วิธี

โซลูชันที่ 1: ใช้บรรทัดเดียว 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

โซลูชันที่ 2: ใช้สคริปต์

สร้างสคริปต์ปฏิบัติการ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

เพิ่มบรรทัดเหล่านี้ในDockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh

คุณมีความคิดว่าฉันจะเก็บรักษาข้อมูลนั้นได้อย่างไร? ฐานข้อมูลและตารางที่ฉันสร้างบนโครงสร้าง Dockerfile ไม่พร้อมใช้งานเมื่อฉันเรียกใช้สิ่งต่างๆบนคอนเทนเนอร์ :(
vinnylinux

1
มันขึ้นอยู่กับสิ่งที่คุณหมายถึงโดยคงอยู่ หากคุณต้องการให้ข้อมูลคงอยู่ในdocker runการดำเนินการหลาย ๆ ครั้งคุณต้องต่อเชื่อมไดรฟ์ข้อมูล หากคุณแค่ต้องการมีคอนเทนเนอร์พร้อมดัมพ์ของคุณ แต่คุณไม่ต้องการที่จะแก้ไขต่อไปคุณสามารถกำจัดVOLUMEคำแนะนำในDockerfileไฟล์.
Kuhess

16
ฉันได้รับERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)เมื่อลองวิธีแก้ปัญหา 1 ตามบันทึกmysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlและmysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Vituel

1
ฉันพบว่ามันดีกว่ามากหากมีการตรวจสอบสคริปต์เมื่อเริ่มต้นเพื่อดูว่า mysql ได้สร้างฐานข้อมูลหรือไม่ ถ้ามีให้ปล่อยไว้เฉยๆมิฉะนั้นให้รันmysql_init_dbและโหลดในโครงสร้างฐานข้อมูลของคุณ นี้จะช่วยให้มีความยืดหยุ่นของการมีการตั้งค่าฐานข้อมูลโดยอัตโนมัติเมื่อการทดสอบ แต่ถ้าคุณต้องการที่จะยังคงมีอยู่หรือการทดสอบชุดข้อมูลที่แตกต่างกันคุณเพียงแค่ติดตั้งไดรฟ์ที่ / var / lib / mysql docker run -v ...ที่ใช้
tu-Reinstate Monica-dor duh

1
@trevorgrayson - ไม่ได้ผลเช่นกัน ตอนนี้มีข้อผิดพลาด ERROR 2003 (HY000): ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ MySQL บน '127.0.0.1' (111)
ลึก

166

mysql docker imageเวอร์ชันล่าสุดช่วยให้คุณสามารถนำเข้าข้อมูลเมื่อเริ่มต้น นี่คือ 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

ที่นี่ฉันมี data-dump.sql ของฉันdocker/dataซึ่งสัมพันธ์กับโฟลเดอร์ที่นักเทียบท่าทำงานอยู่ ฉันติดตั้งไฟล์ sql นั้นลงในไดเร็กทอรีนี้/docker-entrypoint-initdb.dบนคอนเทนเนอร์

หากคุณสนใจที่จะดูวิธีการทำงานสามารถดูได้ที่docker-entrypoint.shGitHub พวกเขาได้เพิ่มบล็อกนี้เพื่ออนุญาตการนำเข้าข้อมูล

    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

หมายเหตุเพิ่มเติมหากคุณต้องการให้ข้อมูลยังคงอยู่แม้ว่าจะหยุดและลบคอนเทนเนอร์ mysql แล้วคุณจะต้องมีที่เก็บข้อมูลแยกต่างหากดังที่คุณเห็นใน docker-compose.yml เนื้อหาของ Dockerfile ที่เก็บข้อมูลนั้นง่ายมาก

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

VOLUME /var/lib/mysql

CMD ["true"]

ที่เก็บข้อมูลไม่จำเป็นต้องอยู่ในสถานะเริ่มต้นเพื่อการคงอยู่


1
นี่คือคำตอบที่ดีกว่า IMHO ไม่อนุญาตให้สร้างภาพของเราเอง ขอบคุณสำหรับเคล็ดลับนี้
Metal3d

5
การเปลี่ยนแปลงเล็กน้อยสำหรับไดรฟ์ข้อมูล: - - ./docker/data:/docker-entrypoint-initdb.dลบ.ด้านหน้าไดเร็กทอรีคอนเทนเนอร์
David Sinclair

7
แม้ว่านี่จะเป็นขั้นตอนที่ถูกต้อง แต่ก็ไม่ได้จัดการกับกรณีการใช้งานได้เป็นอย่างดี ข้อดีอย่างหนึ่งของ Docker คือคุณสามารถหมุนสภาพแวดล้อมได้เร็วมาก หากคุณต้องรอประมาณ 3-4 นาทีในขณะที่ Docker นำเข้า MySQL DB ระหว่างการเริ่มต้นระบบคุณจะสูญเสียข้อได้เปรียบนี้ วัตถุประสงค์คือการมีคอนเทนเนอร์ที่มีข้อมูลอยู่แล้วในฐานข้อมูลเพื่อให้คุณสามารถใช้งานได้โดยเร็วที่สุด
Garreth McDaid

3
คำตอบนี้ยังใช้ได้กับเวอร์ชันล่าสุดหรือไม่ ดูเหมือนจะไม่ทำงานอีกต่อไป
Daniel

2
โปรดทราบว่าตัวอย่าง Docker-compose ที่นี่คือ v2 ไม่ใช่ v3 "volumes_from:" ไม่รองรับใน v3
เออร์เนสต์

37

สิ่งที่ฉันทำคือดาวน์โหลดไฟล์ sql ของฉันในโฟลเดอร์ "db-dump" และติดตั้ง:

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

เมื่อฉันเรียกใช้docker-compose upเป็นครั้งแรกการถ่ายโอนข้อมูลจะถูกเรียกคืนในฐานข้อมูล


3
+1 พร้อมการเพิ่มเพียงครั้งเดียว: ลิงก์ไปยังสคริปต์ที่ทำงานเพื่อให้เข้าใจได้ดีขึ้นว่าเกิดอะไรขึ้น: github.com/docker-library/mariadb/blob/…
Tom Imrei

6
ล่าสุดmysqlดูเหมือนจะไม่โหลดไฟล์ sql ที่ถูกทิ้งและถึงแม้จะใช้ 5.6 ฉันก็มีปัญหากับเอ็นจิ้นการจัดเก็บ InnoDb
zinking

1
เหมือนกันที่นี่มันมีคำสั่งดังกล่าวและฉันยังเห็นในการบันทึกว่ากำลังโหลดไฟล์ init แต่ db ว่างเปล่า!
Holms

11

ฉันใช้วิธี docker-entrypoint-initdb.d (ขอบคุณ @Kuhess) แต่ในกรณีของฉันฉันต้องการสร้างฐานข้อมูลของฉันตามพารามิเตอร์บางอย่างที่ฉันกำหนดไว้ในไฟล์. env ดังนั้นฉันจึงทำสิ่งเหล่านี้

1)ก่อนอื่นฉันกำหนดไฟล์. env บางอย่างเช่นนี้ในไดเร็กทอรีโปรเจ็กต์ root ของนักเทียบท่า

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

2)จากนั้นฉันกำหนดไฟล์ docker-compose.yml ของฉัน ดังนั้นฉันจึงใช้คำสั่ง args เพื่อกำหนดตัวแปรสภาพแวดล้อมของฉันและฉันตั้งค่าจากไฟล์. 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)จากนั้นฉันกำหนดโฟลเดอร์ mysql ที่มี Dockerfile 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)ตอนนี้ฉันใช้ mysqldump เพื่อถ่ายโอนข้อมูลฐานข้อมูลของฉันและวาง data.sql ไว้ในโฟลเดอร์ mysql

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

ไฟล์นี้เป็นเพียงไฟล์ดัมพ์ sql ธรรมดา แต่ฉันเพิ่ม 2 บรรทัดที่จุดเริ่มต้นดังนั้นไฟล์จะมีลักษณะดังนี้

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

สิ่งที่เกิดขึ้นคือฉันใช้คำสั่ง "RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql เพื่อแทนที่MYSQL_DATABASEตัวยึดตำแหน่งด้วยชื่อฐานข้อมูลของฉันที่ฉันตั้งไว้ .env ไฟล์

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

ตอนนี้คุณพร้อมที่จะสร้างและเรียกใช้คอนเทนเนอร์ของคุณแล้ว


ฉันชอบวิธีการใช้.envไฟล์ อย่างไรก็ตามตามนี้ที่.envคุณลักษณะแฟ้มทำงานเฉพาะเมื่อคุณใช้คำสั่งและไม่ได้ทำงานกับdocker-compose up docker stack deployดังนั้นในกรณีที่คุณต้องการใช้.envไฟล์ในการผลิตคุณอาจต้องการตรวจสอบความลับของนักเทียบท่าที่แนะนำด้วยdocker 17.06. จากนั้นคุณสามารถใช้ไฟล์. env ของคุณร่วมกับความลับทั้งในขั้นตอนการพัฒนาdocker compose upและdocker stack deployขั้นตอนการผลิต
ira

วิธีการที่ดี แต่ฉันขอแนะนำให้ใช้RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE ;\n/' data.sqlแทนsedคำสั่งที่คุณแนะนำ ด้วยวิธีนี้คุณสามารถใช้ไฟล์ดัมพ์ใด ๆ ที่คุณมีซึ่งจะมีประโยชน์หากคุณกำลังทำงานกับข้อมูลจำนวนมาก :)
Tomasz Kapłoński

9

นี่คือรุ่นที่ทำงานโดยใช้ของv3 docker-compose.ymlกุญแจสำคัญคือคำสั่งไดรฟ์ข้อมูล :

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

ในไดเร็กทอรีที่ฉันมีdocker-compose.ymlฉันมีdatadir ที่มี.sqlไฟล์ดัมพ์ นี่เป็นสิ่งที่ดีเพราะคุณสามารถมี.sqlไฟล์ดัมพ์ต่อตารางได้

ฉันแค่วิ่งdocker-compose upและฉันก็พร้อมที่จะไป ข้อมูลจะยังคงอยู่โดยอัตโนมัติระหว่างจุดหยุด หากคุณต้องการลบข้อมูลและ "ดูด" ใหม่.sqlไฟล์ที่ทำงานแล้วdocker-compose downdocker-compose up

หากใครรู้วิธีรับmysqlนักเทียบท่าเพื่อประมวลผลไฟล์ใหม่/docker-entrypoint-initdb.dโดยไม่ต้องลบโวลุ่มโปรดแสดงความคิดเห็นและฉันจะอัปเดตคำตอบนี้


2
คำตอบนี้ช่วยฉัน โน้ตบนdocker-compose downนั้นสำคัญมากเพราะฉันไม่เห็นการเปลี่ยนแปลงใด ๆ แม้จะรีสตาร์ท Docker-compose MYSQL_DATABASE เป็นตัวแปรที่จำเป็นในกรณีนี้
nxmohamad

0

แก้ไข:ฉันเข้าใจผิดคำถามที่นี่ คำตอบต่อไปนี้ของฉันอธิบายวิธีเรียกใช้คำสั่ง sql ในเวลาสร้างคอนเทนเนอร์แต่ไม่ใช่ในเวลาสร้างอิมเมจตามที่ OP ต้องการ

ฉันไม่ค่อยชอบคำตอบที่ยอมรับของ Kuhess เนื่องจากsleep 5ดูเหมือนว่าฉันจะแฮ็คนิดหน่อยเพราะถือว่า mysql db daemon โหลดอย่างถูกต้องภายในกรอบเวลานี้ นั่นเป็นข้อสันนิษฐานไม่มีการรับประกัน นอกจากนี้หากคุณใช้อิมเมจนักเทียบท่า mysql ที่ให้มารูปภาพจะดูแลเกี่ยวกับการเริ่มต้นเซิร์ฟเวอร์อยู่แล้ว ฉันจะไม่ Interfer /usr/bin/mysqld_safeกับเรื่องนี้มีกำหนดเอง

ฉันทำตามคำตอบอื่น ๆ ที่นี่และคัดลอกสคริปต์ bash และ sql ลงในโฟลเดอร์/docker-entrypoint-initdb.d/ภายในคอนเทนเนอร์นักเทียบท่าเนื่องจากเป็นวิธีที่ชัดเจนโดยผู้ให้บริการภาพ mysql ทุกอย่างในโฟลเดอร์นี้จะถูกดำเนินการเมื่อ db daemon พร้อมดังนั้นคุณควรวางใจได้

นอกเหนือจากคำตอบอื่น ๆ - เนื่องจากไม่มีคำตอบอื่นใดที่กล่าวถึงสิ่งนี้อย่างชัดเจน: นอกจากสคริปต์ sql แล้วคุณยังสามารถคัดลอกสคริปต์ทุบตีลงในโฟลเดอร์นั้นซึ่งอาจทำให้คุณควบคุมได้มากขึ้น

นี่คือสิ่งที่ฉันต้องการตัวอย่างเช่นเนื่องจากฉันต้องการนำเข้าดัมพ์ แต่การถ่ายโอนข้อมูลเพียงอย่างเดียวไม่เพียงพอเนื่องจากไม่ได้ระบุฐานข้อมูลที่ควรนำเข้า ดังนั้นในกรณีของฉันฉันมีสคริปต์ที่db_custom_init.shมีชื่อเนื้อหานี้:

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

และ Dockerfile นี้คัดลอกสคริปต์นั้น:

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/
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.