【问题标题】:Persisting MySQL data in Docker在 Docker 中持久化 MySQL 数据
【发布时间】:2016-12-15 12:46:36
【问题描述】:

我是 Docker 新手。我已经使用以下 Dockerfile 成功创建了 Docker image

From alpine:3.4
MAINTAINER SomeName - domain.tld

# Timezone
ENV TIMEZONE Asia/Kolkata

# RUN sed -i 's#dl-cdn\.alpinelinux\.org#mirrors\.aliyun\.com#' /etc/apk/repositories

# install mysql, apache and php and php extensions, tzdata, wget
RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
    apk add --update \
    mysql mysql-client \
    apache2 \
    curl wget \
    tzdata \
    php5-apache2 \
    php5-cli \
    php5-phar \
    php5-zlib \
    php5-zip \
    php5-bz2 \
    php5-ctype \
    php5-mysqli \
    php5-mysql \
    php5-pdo_mysql \
    php5-opcache \
    php5-pdo \
    php5-json \
    php5-curl \
    php5-gd \
    php5-gmp \
    php5-mcrypt \
    php5-openssl \
    php5-dom \
    php5-xml \
    php5-iconv \
    php5-xdebug@community

RUN curl -sS https://getcomposer.org/installer | \
    php -- --install-dir=/usr/bin --filename=composer

# configure timezone, mysql, apache
RUN cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && \
    echo "${TIMEZONE}" > /etc/timezone && \
    mkdir -p /run/mysqld && chown -R mysql:mysql /run/mysqld /var/lib/mysql && \
    mysql_install_db --user=mysql --verbose=1 --basedir=/usr --datadir=/var/lib/mysql --rpm > /dev/null && \
    mkdir -p /run/apache2 && chown -R apache:apache /run/apache2 && chown -R apache:apache /var/www/localhost/htdocs/ && \
    sed -i 's#AllowOverride none#AllowOverride All#' /etc/apache2/httpd.conf && \
    sed -i 's#ServerName www.example.com:80#\nServerName localhost:80#' /etc/apache2/httpd.conf && \
    sed -i '/skip-external-locking/a log_error = \/var\/lib\/mysql\/error.log' /etc/mysql/my.cnf && \
    sed -i '/skip-external-locking/a general_log = ON' /etc/mysql/my.cnf && \
    sed -i '/skip-external-locking/a general_log_file = \/var\/lib\/mysql\/query.log' /etc/mysql/my.cnf

# Configure xdebug
RUN echo "zend_extension=xdebug.so" > /etc/php5/conf.d/xdebug.ini && \ 
    echo -e "\n[XDEBUG]"  >> /etc/php5/conf.d/xdebug.ini && \ 
    echo "xdebug.remote_enable=1" >> /etc/php5/conf.d/xdebug.ini && \  
    echo "xdebug.remote_connect_back=1" >> /etc/php5/conf.d/xdebug.ini && \ 
    echo "xdebug.idekey=PHPSTORM" >> /etc/php5/conf.d/xdebug.ini && \ 
    echo "xdebug.remote_log=\"/tmp/xdebug.log\"" >> /etc/php5/conf.d/xdebug.ini

#start apache
RUN echo "#!/bin/sh" > /start.sh && \
    echo "httpd" >> /start.sh && \
    echo "nohup mysqld --skip-grant-tables --bind-address 0.0.0.0 --user mysql > /dev/null 2>&1 &" >> /start.sh && \
    echo "sleep 3 && mysql -uroot -e \"create database db;\"" >> /start.sh && \
    echo "tail -f /var/log/apache2/access.log" >> /start.sh && \
    chmod u+x /start.sh

WORKDIR /var/www/localhost/htdocs/

EXPOSE 80
EXPOSE 3306

#VOLUME ["/var/www/localhost/htdocs","/var/lib/mysql","/etc/mysql/"]
ENTRYPOINT ["/start.sh"]

理想情况下,我希望 docker 映像包含 Apache、PHP 和 MySQL——所有这些都在一个映像中。我希望图像尽可能便携。因此,每次我需要开始一个新项目时,我都可以创建一个新容器并挂载卷,然后继续编写代码。

当我启动一个新容器时,我得到了预期的一切。但是无法挂载mysql数据。我认为这是因为安装是单向的(或者是吗?)如果我在启动新容器时添加-v /project_dir/data:/var/lib/mysql,mysql 不会启动,因为project_dir/data 是空的。我的问题是:

  1. 如何将数据从我的project_dir 挂载到/var/lib/mysql 在容器内
  2. 如何为不同的容器设置不同的mysql密码?

编辑:如果我不挂载我的本地卷(即project_dir/data)并启动容器,获取一个shell tty,我看到/var/lib/mysql 有几个文件,我假设需要运行 mysql 及其初始数据库。但是,如果我将本地卷和ls 安装在/var/lib/mysql 内(在容器内),它是空的。这就是为什么 MySQL 没有启动并且我无法保存来自容器外部的数据的原因。我的问题是:如何让它运行并从本地目录持久化数据。

【问题讨论】:

  • 图像应该独立于您的本地环境。您必须使用 VOLUME 来标记将从外部挂载的目录。当您在 docker 中运行映像时,您可以使用 -v 参数将其连接到本地环境。这样它甚至可以在 Kubernetes 上运行。
  • 不要在这个用例中使用 Docker。 Docker 是一种应用容器技术。您正在构建一个操作系统容器/黄金映像。使用虚拟机,或者如果您坚持使用容器只是为了炫酷,请使用操作系统容器(例如 OpenVZ)。您当前的方法会遇到很多问题。

标签: mysql docker dockerfile


【解决方案1】:

问题

这里发生的事情是您在构建 Docker 映像时创建了一个空数据库:

mysql_install_db --user=mysql --verbose=1 --basedir=/usr --datadir=/var/lib/mysql --rpm > /dev/null && \

但是当你从这个镜像创建一个容器时,你会在/var/lib/mysql 文件夹上挂载一个卷。这会隐藏您的容器数据,以公开您主机的文件夹。因此,您会看到一个空文件夹。

解决方案

如果您查看https://hub.docker.com/_/mysql/,您会发现这个问题是通过在容器实际启动时创建数据库来解决的,而不是在创建映像时。这回答了您的两个问题:

  1. 如果您使用挂载的卷启动容器,那么随后将执行数据库初始化,并且您的数据实际上将写入主机的 FS
  2. 您必须将这些信息作为环境变量传递

换句话说,使用ENTRYPOINT 中的脚本来初始化您的数据库,而不是直接在图像中。

警告

不过有一些警告。并不真正推荐您制作映像的方式,因为 Docker 的理念是“每个容器一个进程”。您在这里遇到的困难是您必须在同一个容器(apache、Mysql 等)上启动多个服务,因此您可能必须在入口点中对这两个服务都进行操作,这很令人困惑。此外,如果一项服务失败,您的容器仍会启动,但无法按预期工作。

然后我建议将您所做的事情拆分为每个进程 1 个图像,然后使用原始 Docker 启动它们,或者使用 docker-compose 之类的用户。

此外,这将使您受益,您将能够使用来自 Docker Hub 的现有且高度可配置的映像:https://hub.docker.com。减少您的工作量,减少出错的可能性。

【讨论】:

  • 谢谢。很有意义。但是,我对 Docker 真的很陌生。您能否详细解释一下如何在ENTRYPOINT 中添加脚本? ENTRYPOINT 不是在 Dockerfile 中使用吗?如果是这样,在构建映像时运行脚本在容器启动时运行脚本之间有什么区别? Dockerfile不是只用来构建镜像的吗?
  • 看看github.com/docker-library/mysql/tree/…。这就是 MySQL 映像的完成方式。再一次,我鼓励您使用它(不过,请选择您需要的 mysql 版本)。现在回答你的问题:ENTRYPOINT 是一个在容器启动时执行的命令。为此,您必须在 Dockerfile 中定义它。所以是的,Dockerfile 用于构建镜像,但也给出了当你用它创建容器时要做什么的说明。例如,配置并启动一个进程。
【解决方案2】:

当您在 docker 容器中拥有数据库时,使用卷是唯一的方法。也许我们应该声明“容器内的数据库”意味着不使用卷并且将数据保存在标准的 10GB 中(如果默认大小已修改,则更大),卷中的数据库意味着 docker run -v myvolume...

文档 docs.docker.com/engine/tutorials/dockervolumes 是必看的

我完全同意 Alexandre 的回答,但我认为将数据库放在容器中是错误的,还有另一个原因。默认情况下,图像的大小(即运行时的容器)为 10 GB。这对于一些可执行文件、库和配置文件来说应该是相当舒适的。您可以修改此默认值,并在创建图像时设置更大的值。但是当你达到这个新限制时会发生什么?假设您认为您的数据库大小将是 100 GB,而实际上您放入的数据比计划的要多,最终需要多 5 或 20 倍?卷可以位于 SAN 上,并且可以根据需要进行扩展。

【讨论】:

  • 你是什么意思:“把你的数据库放在你的容器里”?你的意思是不使用音量?
  • 不,我的意思是使用卷是存储数据库的唯一方法
  • 好的,那我的回答可能不够清楚,因为这也是我的建议:)。你知道我可以如何改进它吗?
  • @user2915097 -- 要么将该评论移到您的答案中,要么删除您的答案,因为您的答案令人困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 2022-08-13
  • 2016-12-30
  • 2020-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多