Docker 组合容器相互达到构建时间

Docker compose containers reach each other build time

我有一个 wordpress docker-compose,其中包含 3 个服务。

1 - php,apache

2 - mysql

3 - php我的管理员

我想做的是在构建时安装 wordpress 核心和插件, 原因很明显,我不想每次重新启动容器时都重新执行所有步骤并安装插件和...。所以我需要连接到数据库,但似乎构建时间我无法访问我的 mysql 容器。 我在某处读到我需要在构建阶段指定网络,但我无法让它工作。 这是我的 docker-compose 文件:

version: '3.8'

volumes:
  mhndev_systems_mysql_data:
  mhndev_systems_wp_uploads:

networks:
  mhndev_network:


services:
  ## --------------------------------------------
  ## | 1: Wordpress
  ## --------------------------------------------
  mhndev_systems_wp:
    build:
      context: .
      dockerfile: docker/Dockerfile
      args:
        WP_VERSION: ${WP_VERSION}
        MYSQL_HOST: ${MYSQL_HOST}
        MYSQL_PORT: ${MYSQL_PORT}
        MYSQL_DATABASE_NAME: ${MYSQL_DATABASE_NAME}
        MYSQL_USERNAME: ${MYSQL_USERNAME}
        MYSQL_PASSWORD: ${MYSQL_PASSWORD}
        SITE_URL: ${SITE_URL}
        DB_TABLE_PREFIX: ${DB_TABLE_PREFIX}
        ENV: ${ENV}
        UID: ${UID}
        GID: ${GID}
        SITE_TITLE: ${SITE_TITLE}
        SITE_ADMIN_USERNAME: ${SITE_ADMIN_USERNAME}
        SITE_ADMIN_PASSWORD: ${SITE_ADMIN_PASSWORD}
        SITE_ADMIN_EMAIL: ${SITE_ADMIN_EMAIL}
      network: "mhndev_network"
    ports:
      - 8191:80
    env_file:
      - .env
    volumes:
      - ./themes/dt-the7-child:/var/www/html/wp-content/themes/dt-the7-child
      - ./plugins/teamcity:/var/www/html/wp-content/plugins/teamcity
      - mhndev_systems_wp_uploads:/var/www/html/wp-content/uploads
    depends_on:
      - mhndev_systems_mysql
    networks:
      - mhndev_network

  ## --------------------------------------------
  ## | 2: Mysql
  ## --------------------------------------------
  mhndev_systems_mysql:
    image: mysql:5.7.21
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE_NAME}
      MYSQL_USER: ${MYSQL_USERNAME}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mhndev_systems_mysql_data:/var/lib/mysql
    networks:
      - mhndev_network

  ## --------------------------------------------
  ## | 3: PhpMyAdmin
  ## --------------------------------------------
  mhndev_systems_phpmyadmin:
    image: phpmyadmin/phpmyadmin:5.0.2
    depends_on:
      - mhndev_systems_mysql
    ports:
      - "7191:80"
    environment:
      PMA_HOST: mhndev_systems_mysql
    networks:
      - mhndev_network

这是我的 Dockerfile :

FROM php:7.4-apache

ARG WP_VERSION=5.5.1

RUN apt-get update && apt-get install -y \
    sendmail \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    netcat \
    gnupg \
    libzip-dev \
    zip \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd pdo_mysql zip \
    && docker-php-ext-install mysqli && docker-php-ext-enable mysqli

ADD ./docker/apache.conf /etc/apache2/sites-enabled/000-default.conf

RUN \
    printf "\nServerName localhost" >> /etc/apache2/apache2.conf &&\
    a2enmod rewrite expires

### install wp cli
RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar &&\
    chmod +x wp-cli.phar &&\
    mv wp-cli.phar /usr/local/bin/wp

WORKDIR /var/www/html
RUN wp core download --allow-root

### copy plugins and themes and php config files to container
COPY ["./plugins/*.zip", "/docker/plugins/"]
COPY ["./themes/*.zip", "/docker/themes/"]
COPY ./themes/dt-the7-child /var/www/html/wp-content/themes/dt-the7-child
COPY ./plugins/teamcity /var/www/html/wp-content/plugins/teamcity
COPY ./docker/php.ini /usr/local/etc/php/php.ini
COPY ./docker/wp-config.php /var/www/html/wp-config.php
COPY ["./plugins/plugins_*.txt", "/docker/plugins/"]
COPY ["./uploads/", "/docker/uploads/"]
COPY ["./docker/commands/*.sh", "/docker/bin/"]

RUN chmod a+x /docker/bin/*.sh

RUN /bin/bash -c "source /docker/bin/setup-theme-plugins.sh"

RUN chown -R www-data:www-data /var/www/html/

CMD apachectl -D FOREGROUND

这里有一个bash文件(setup-theme-plugins.sh),负责安装wordpress核心和插件

#!/bin/bash

echo '-------------------whoami--------------------'
echo $(whoami)
echo '---------------------------------------------'

printf "3[0;32m > Waiting for mysql  ...\x1b[0m \n"
until nc -z -v -w30 "$MYSQL_HOST" "$MYSQL_PORT"
do
echo "Waiting for database connection..."
# wait for 5 seconds before check again
sleep 1
done

printf "3[0;32m >Mysql is ready ...\x1b[0m \n"
echo '---------------------------------------------'

printf "3[0;32m > Copy wordpress uploads if not exists (usually first time ) \x1b[0m \n"

FILE=/var/www/html/wp-content/uploads/2020/01/dell.jpg

if [ -f "$FILE" ]; then
echo "$FILE exists, so no need to copy uploads"
else
echo "$FILE does not exist, copying ..."
cp -r /docker/uploads/* /var/www/html/wp-content/uploads
fi
echo '---------------------------------------------'


sed -i s/__DB_NAME__/"${MYSQL_DATABASE_NAME}"/g /var/www/html/wp-config.php
sed -i s/__DB_USER__/"${MYSQL_USERNAME}"/g /var/www/html/wp-config.php
sed -i s/__DB_PASSWORD__/"${MYSQL_PASSWORD}"/g /var/www/html/wp-config.php
sed -i s/__DB_HOST__/"${MYSQL_HOST}"/g /var/www/html/wp-config.php
sed -i s/__SITE_URL__/"${SITE_URL}"/g /var/www/html/wp-config.php


if [[ -n "${DB_TABLE_PREFIX}" ]]; then
sed -i s/__DB_TABLE_PREFIX__/"${DB_TABLE_PREFIX}"/g /var/www/html/wp-config.php
else
sed -i s/__DB_TABLE_PREFIX__/"${DB_TABLE_PREFIX}"/g /var/www/html/wp-config.php
fi

### set WP_DEBUG, SCRIPT_DEBUG based on DEV environment variable
if [ "${ENV}" = "dev" ]; then
sed -i s/__WP_DEBUG__/true/g /var/www/html/wp-config.php
sed -i s/__SCRIPT_DEBUG__/true/g /var/www/html/wp-config.php
else
sed -i s/__WP_DEBUG__/false/g /var/www/html/wp-config.php
sed -i s/__SCRIPT_DEBUG__/false/g /var/www/html/wp-config.php
fi



wp option update home "${SITE_URL}" --allow-root
wp option update siteurl "${SITE_URL}" --allow-root

### set WP_DEBUG_LOG to php://stdout to always output logs to stdout so be available for docker logs
old_string='__WP_DEBUG_LOG__'
new_string='php://stdout'
sed -i "s%$old_string%$new_string%g" /var/www/html/wp-config.php


if [[ -n "${SITE_ADMIN_USERNAME}" ]]; then
DASHBOARD_USER_NAME="${SITE_ADMIN_USERNAME}"
else
DASHBOARD_USER_NAME="admin"
fi

if [[ -n "${UID}" ]]; then
usermod -u "${UID}" www-data
groupmod -g "${GID}" www-data
fi

chown -R www-data:www-data /var/www/html/

### install wordpress
printf "3[0;32m > Checking if wordpress core installed, if not Installing it  ...\x1b[0m \n"
wp core is-installed --allow-root
retVal=$?
if [ "$retVal" == "1" ];then
printf "3[0;32m > Trying to Install wordpress ...\x1b[0m \n"
printf "3[0;32m > Command to execute is : wp core install --url="${SITE_URL}" --title="${SITE_TITLE}" --admin_user="${DASHBOARD_USER_NAME}" --admin_password="${SITE_ADMIN_PASSWORD}" --admin_email="${SITE_ADMIN_EMAIL}" --allow-root ...\x1b[0m \n"
wp core install --url="${SITE_URL}" --title="${SITE_TITLE}" --admin_user="${DASHBOARD_USER_NAME}" --admin_password="${SITE_ADMIN_PASSWORD}" --admin_email="${SITE_ADMIN_EMAIL}" --allow-root
fi
echo '---------------------------------------------'

### install The7 theme from zip file
# shellcheck disable=SC2059
printf "3[0;32m > Checking if theme: $FILE installed, if not Installing it  ...\x1b[0m \n"

wp theme is-installed The7 --allow-root
is_theme_installed=$?
if [[ is_theme_installed -eq 1 || ! -d /var/www/html/wp-content/themes/dt-the7 ]]; then
rm -rf /var/www/html/wp-content/themes/dt-the7
wp theme install /docker/themes/dt-the7.zip --force --allow-root;
fi
echo '---------------------------------------------'

### install plugins from plugins_dev.txt or plugins_prod.txt based on environment
# shellcheck disable=SC2162
while read line; do
IFS='=' read -r -a array <<< "$line"
printf "3[0;32m > Checking if plugin:%s  is installed else Installing %s:%s ...\x1b[0m \n" "${array[0]}" "${array[0]}" "${array[1]}"

#  if wp plugin is-installed "${array[0]}" --allow-root; then
wp plugin install "${array[0]}" --version="${array[1]}" --activate --force --allow-root
#  fi
echo '---------------------------------------------'
done < /docker/plugins/plugins_"${ENV}".txt


### install plugins from zip file
for FILE in /docker/plugins/*.zip;
do
# shellcheck disable=SC2059
printf "3[0;32m > Installing plugin $FILE ...\x1b[0m \n"
wp plugin install "$FILE" --force --allow-root;
echo '---------------------------------------------'
done

printf "3[0;32m > Checking if hello plugin exist and if so uninstall it ...\x1b[0m \n"
#if ! wp plugin is-installed hello --allow-root; then
wp plugin uninstall hello  --allow-root
#fi

echo '---------------------------------------------'

printf "3[0;32m > Checking if akismet plugin exist and if so uninstall it ...\x1b[0m \n"
#if ! wp plugin is-installed akismet --allow-root; then
wp plugin uninstall akismet --allow-root
#fi
echo '---------------------------------------------'

printf "3[0;32m > activating the7 child theme  ...\x1b[0m \n"
wp theme activate dt-the7-child --allow-root
echo '---------------------------------------------'

printf "3[0;32m > Uninstalling initial themes  ...\x1b[0m \n"
#if ! wp theme is-installed twentynineteen --allow-root; then
wp theme delete twentynineteen --allow-root
#fi
echo '---------------------------------------------'

#if ! wp theme is-installed twentyseventeen --allow-root; then
wp theme delete twentyseventeen --allow-root
#fi
echo '---------------------------------------------'

#if ! wp theme is-installed twentytwenty --allow-root; then
wp theme delete twentytwenty --allow-root
#fi
echo '---------------------------------------------'

正如您在我的 bash 文件中看到的那样,我正在连接到 mysql 容器。 我怎样才能做到这一点?

我的猜测是您无法连接,因为数据库服务已启动但尚未就绪。在 the docker documentation on depends_on 中说明了这一点(我也遇到过这个问题):

depends_on does not wait for db and redis to be “ready” before starting web - only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.

(强调我的)

在我的例子中,我以一种非常丑陋的方式解决了它(在我的 python 脚本中有一个 sleep() 10 秒),但如果这对你来说没有解决方案,也许官方策略文档 found here 可能会有帮助。

[编辑] 你用什么作为主机变量名?在 docker compose 中使用网络时,机器应该能够使用它们的容器名称相互通信(即 mhndev_systems_mysql 在您的情况下)。你能试试吗?

您根本无法从 Docker 文件连接到数据库。

其中一部分是图像工作原理的基本 Docker 模型。 Docker 镜像仅包含一个文件系统和一些描述如何从中启动容器的元数据;您可以将其复制到不同的系统并在那里 运行。如果您构建了一个图像并尝试更新数据库作为它的一部分,然后 运行 在不同的系统上使用相同的图像,它不会有数据库设置;同样,你可以删除并重新创建本地数据库而不用重建图像,你不会有任何数据库内容。

从机制上讲,docker build 步骤(或其等效的 Compose)运行 在受限环境中。最值得注意的是,它没有连接到任何 Compose 网络,因此没有网络设置来解析像 mhndev_systems_mysql 这样的主机名。 (从技术上讲,它位于 default bridge network 上,与 Compose default 网络或您为构建容器指定的任何 networks: 不同。)

在典型的应用程序中,您希望将“应用程序代码”与“数据库设置”分开。当应用程序启动时,您必须 运行 database-setup 部分(通常是“迁移”),或者与启动应用程序分开;但你不能在构建时这样做。

构建时间和运行时间之间存在一个简单的误解;所有容器将在 运行 时间内可用,而不是构建时间。因此,在构建时无法访问您的 MySQL 容器。 我的建议是从你的 Dockerfile 中删除所有需要 MySQL 的步骤并将它们移动到你的入口点,然后在你的 Dockerfile 中设置一个布尔值 ENV 并在你的入口点开始时检查该值 运行 您的命令,如果您将该 ENV 设置为 true;现在,如果您需要 运行 您的入口点(也就是设置您的 WP & MySQL),只需在构建时传递该 ENV。

docker build --build-arg var_name=${VARIABLE_NAME}