多服务Docker容器构建

2026-03-04 21:41:00
丁国栋
原创 6
摘要:制作一个包含 Debian Linux + Apache2 + PHP + MySQL + Redis 等服务的Docker容器。

假设你是一个Docker容器技术专家,现在需要你制作一个包含 Debian Linux + Apache2 + PHP + MySQL + Redis 等服务的Docker容器,通过这个容器,可以运行这些服务中的部分或者全部以承载应用程序。

以下是一些基本要求:

  1. 能够通过docker build使用Dockerfile构建出这个容器镜像;
  2. 能使用包管理器安装的组件尽可能使用包管理器安装;
  3. Docker镜像中使用的多进程管理器可以使用s6或者supervisord甚至systemd;
  4. Debian Linux、Apache2、PHP、MySQL、Redis组件的版本在构建镜像阶段可以指定为特定版本,这样可以非常灵活的选择要使用的版本;
  5. PHP的版本可以支持php7.2、php7.4、php8.1、php8.4和最新稳定版等主流版本;
  6. 默认构建时所有软件使用最新稳定版本;
  7. 可以很方便地添加、启用、禁用PHP扩展;
  8. 可以通过环境变量决定是否启动MySQL、Redis服务;
  9. 可以通过环境变量控制Apache2、PHP、MySQL、Redis的一些关键参数;
  10. 所有的需要持久化存储的目录均使用 /data 目录,将 /data 目录下的子目录通过软链接方式链接到需要使用的位置;
  11. 通过环境变量来控制哪些其他配置文件(例如应用程序的配置文件等)需要持久化;
  12. 通过环境变量来控制哪些其他目录(例如应用程序的数据目录等)需要持久化;
  13. 尽可能的控制Docker镜像的大小,防止镜像过大导致拉取、更新缓慢;

我将为您创建一个高度可定制的多服务Docker容器解决方案。这个方案将使用s6作为进程管理器,支持灵活的版本选择和配置。

Dockerfile

# 构建参数
ARG DEBIAN_VERSION=bookworm
ARG PHP_VERSION=8.4
ARG MYSQL_VERSION=default
ARG REDIS_VERSION=default
ARG APACHE_VERSION=default
# 基础阶段 - 最小化基础镜像
FROM debian:${DEBIAN_VERSION}-slim AS base
# 设置构建参数
ARG PHP_VERSION
ARG MYSQL_VERSION
ARG REDIS_VERSION
ARG APACHE_VERSION
ARG TZ=UTC
# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive \
    TZ=${TZ} \
    LANG=C.UTF-8 \
    LANGUAGE=C.UTF-8 \
    LC_ALL=C.UTF-8 \
    # 服务控制
    START_MYSQL=0 \
    START_REDIS=0 \
    START_APACHE=1 \
    START_PHP_FPM=0 \
    # MySQL配置
    MYSQL_ROOT_PASSWORD=root \
    MYSQL_DATABASE=app \
    MYSQL_USER=appuser \
    MYSQL_PASSWORD=apppass \
    MYSQL_MAX_CONNECTIONS=100 \
    MYSQL_INNODB_BUFFER_POOL_SIZE=128M \
    # Redis配置
    REDIS_MAXMEMORY=256mb \
    REDIS_MAXMEMORY_POLICY=allkeys-lru \
    REDIS_PORT=6379 \
    # Apache配置
    APACHE_SERVER_NAME=localhost \
    APACHE_DOCUMENT_ROOT=/var/www/html \
    APACHE_PORT=80 \
    APACHE_TIMEOUT=300 \
    # PHP配置
    PHP_MEMORY_LIMIT=256M \
    PHP_UPLOAD_MAX_FILESIZE=64M \
    PHP_POST_MAX_SIZE=64M \
    PHP_MAX_EXECUTION_TIME=300 \
    PHP_OPCACHE_ENABLE=1 \
    # 持久化控制
    PERSISTENT_CONFIG_FILES="" \
    PERSISTENT_DATA_DIRS="" \
    # PHP扩展
    PHP_EXTENSIONS_ENABLE="" \
    PHP_EXTENSIONS_DISABLE="" \
    # 数据目录
    DATA_DIR=/data
# 安装基础工具和仓库
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates \
    curl \
    wget \
    gnupg \
    lsb-release \
    procps \
    vim-tiny \
    nano \
    && rm -rf /var/lib/apt/lists/*
# 添加PHP仓库 (支持多版本)
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
    && echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
# 添加NodeSource仓库 (用于一些PHP扩展编译依赖)
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
# 安装s6-overlay (轻量级进程管理器)
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.1/s6-overlay-noarch.tar.xz /tmp/
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.1/s6-overlay-x86_64.tar.xz /tmp/
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz \
    && tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz \
    && rm -f /tmp/s6-overlay-*.tar.xz
# 安装所有主要服务
RUN apt-get update && apt-get install -y --no-install-recommends \
    # Apache2
    apache2 \
    apache2-utils \
    # PHP
    php${PHP_VERSION} \
    php${PHP_VERSION}-fpm \
    php${PHP_VERSION}-common \
    php${PHP_VERSION}-cli \
    libapache2-mod-php${PHP_VERSION} \
    # PHP扩展
    php${PHP_VERSION}-mysql \
    php${PHP_VERSION}-redis \
    php${PHP_VERSION}-curl \
    php${PHP_VERSION}-gd \
    php${PHP_VERSION}-mbstring \
    php${PHP_VERSION}-xml \
    php${PHP_VERSION}-zip \
    php${PHP_VERSION}-intl \
    php${PHP_VERSION}-bcmath \
    php${PHP_VERSION}-imagick \
    php${PHP_VERSION}-sqlite3 \
    php${PHP_VERSION}-pgsql \
    php${PHP_VERSION}-mongodb \
    php${PHP_VERSION}-memcached \
    php${PHP_VERSION}-xdebug \
    php${PHP_VERSION}-opcache \
    # MySQL (如果指定版本)
    $(if [ "$MYSQL_VERSION" != "default" ]; then echo "mysql-server-$MYSQL_VERSION"; else echo "mysql-server"; fi) \
    mysql-client \
    # Redis (如果指定版本)
    $(if [ "$REDIS_VERSION" != "default" ]; then echo "redis-server-$REDIS_VERSION"; else echo "redis-server"; fi) \
    # 编译工具 (用于需要时编译PHP扩展)
    build-essential \
    autoconf \
    pkg-config \
    libssl-dev \
    zlib1g-dev \
    libpcre3-dev \
    libevent-dev \
    # 其他依赖
    supervisor \
    cron \
    logrotate \
    && rm -rf /var/lib/apt/lists/*
# 创建必要目录
RUN mkdir -p \
    ${DATA_DIR}/mysql \
    ${DATA_DIR}/redis \
    ${DATA_DIR}/apache2 \
    ${DATA_DIR}/php \
    ${DATA_DIR}/www \
    ${DATA_DIR}/configs \
    ${DATA_DIR}/logs \
    /var/run/mysqld \
    /var/run/php \
    /var/run/redis \
    && chmod 777 /var/run/*
# 配置s6服务目录
RUN mkdir -p /etc/s6-overlay/s6-rc.d
# 创建s6服务
COPY --from=base /etc/s6-overlay/s6-rc.d /etc/s6-overlay/s6-rc.d
# 添加配置文件和脚本
COPY config/ /tmp/config/
# 配置Apache
RUN a2enmod rewrite headers expires ssl \
    && a2dissite 000-default \
    && cp /tmp/config/apache/apache2.conf /etc/apache2/apache2.conf \
    && cp /tmp/config/apache/ports.conf /etc/apache2/ports.conf \
    && cp /tmp/config/apache/site.conf /etc/apache2/sites-available/000-default.conf \
    && a2ensite 000-default
# 配置PHP
RUN cp /tmp/config/php/php.ini /etc/php/${PHP_VERSION}/apache2/php.ini \
    && cp /tmp/config/php/php-fpm.conf /etc/php/${PHP_VERSION}/fpm/php-fpm.conf \
    && cp /tmp/config/php/www.conf /etc/php/${PHP_VERSION}/fpm/pool.d/www.conf
# 配置MySQL
RUN cp /tmp/config/mysql/my.cnf /etc/mysql/my.cnf \
    && chown -R mysql:mysql ${DATA_DIR}/mysql
# 配置Redis
RUN cp /tmp/config/redis/redis.conf /etc/redis/redis.conf \
    && chown -R redis:redis ${DATA_DIR}/redis
# 创建持久化链接脚本
RUN mv /tmp/scripts /usr/local/bin/ \
    && chmod +x /usr/local/bin/*.sh
# 创建s6服务脚本
RUN mkdir -p /etc/services.d \
    && cp -r /tmp/s6-services/* /etc/services.d/ \
    && chmod +x /etc/services.d/*/*
# 设置持久化链接
RUN /usr/local/bin/setup-persistent-links.sh
# 清理临时文件
RUN rm -rf /tmp/config /tmp/s6-services \
    && apt-get autoremove -y \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5m --retries=3 \
    CMD /usr/local/bin/healthcheck.sh
# 暴露端口
EXPOSE 80 443 3306 6379 9000
# 设置工作目录
WORKDIR /var/www/html
# 设置入口点
ENTRYPOINT ["/init"]
# 默认命令
CMD []

支持文件和脚本

1. 目录结构

project/
├── Dockerfile
├── docker-compose.yml
├── config/
│   ├── apache/
│   │   ├── apache2.conf
│   │   ├── ports.conf
│   │   └── site.conf
│   ├── mysql/
│   │   └── my.cnf
│   ├── php/
│   │   ├── php.ini
│   │   ├── php-fpm.conf
│   │   └── www.conf
│   └── redis/
│       └── redis.conf
├── scripts/
│   ├── entrypoint.sh
│   ├── setup-persistent-links.sh
│   ├── configure-services.sh
│   ├── php-extensions.sh
│   └── healthcheck.sh
└── s6-services/
    ├── apache/
    │   └── run
    ├── mysql/
    │   └── run
    ├── redis/
    │   └── run
    └── php-fpm/
        └── run

2. 关键脚本

scripts/entrypoint.sh:

#!/bin/bash
set -e
# 设置持久化链接
/usr/local/bin/setup-persistent-links.sh
# 根据环境变量配置服务
/usr/local/bin/configure-services.sh
# 配置PHP扩展
/usr/local/bin/php-extensions.sh
# 初始化数据目录
if [ ! -f "${DATA_DIR}/mysql/.initialized" ]; then
    if [ "$START_MYSQL" = "1" ]; then
        mysqld --initialize-insecure \
            && echo "MySQL initialized" \
            && touch "${DATA_DIR}/mysql/.initialized"
    fi
fi
# 设置权限
chown -R www-data:www-data ${DATA_DIR}/www
chmod -R 755 ${DATA_DIR}/www
# 启动s6
exec /init "$@"

scripts/setup-persistent-links.sh:

#!/bin/bash
# 创建数据目录结构
mkdir -p ${DATA_DIR}/{mysql,redis,apache2,php,www,configs,logs}
# 默认持久化配置
DEFAULT_CONFIGS=(
    "/etc/apache2"
    "/etc/php/${PHP_VERSION}"
    "/etc/mysql"
    "/etc/redis"
)
DEFAULT_DATA_DIRS=(
    "/var/lib/mysql"
    "/var/lib/redis"
    "/var/www/html"
    "/var/log/apache2"
    "/var/log/mysql"
    "/var/log/redis"
    "/var/log/php${PHP_VERSION}-fpm.log"
)
# 处理自定义持久化配置
IFS=',' read -ra CUSTOM_CONFIGS <<< "$PERSISTENT_CONFIG_FILES"
IFS=',' read -ra CUSTOM_DATA_DIRS <<< "$PERSISTENT_DATA_DIRS"
# 创建所有持久化链接
create_persistent_links() {
    local items=("$@")
    local type=$2
    for item in "${items[@]}"; do
        if [ -n "$item" ]; then
            local dest="${DATA_DIR}/configs/$(basename "$item")"
            if [ "$type" = "data" ]; then
                dest="${DATA_DIR}/$(basename "$item")"
            fi
            if [ -e "$item" ] && [ ! -L "$item" ]; then
                mkdir -p "$(dirname "$dest")"
                cp -r "$item"/* "$dest/" 2>/dev/null || true
                rm -rf "$item"
                ln -sf "$dest" "$item"
                echo "Created persistent link for: $item -> $dest"
            fi
        fi
    done
}
# 创建默认和自定义的持久化链接
create_persistent_links "${DEFAULT_CONFIGS[@]}"
create_persistent_links "${CUSTOM_CONFIGS[@]}"
create_persistent_links "${DEFAULT_DATA_DIRS[@]}" "data"
create_persistent_links "${CUSTOM_DATA_DIRS[@]}" "data"

scripts/configure-services.sh:

#!/bin/bash
# 根据环境变量配置Apache
if [ -n "$APACHE_PORT" ]; then
    sed -i "s/^Listen 80$/Listen ${APACHE_PORT}/" /etc/apache2/ports.conf
fi
if [ -n "$APACHE_SERVER_NAME" ]; then
    sed -i "s/ServerName localhost/ServerName ${APACHE_SERVER_NAME}/" /etc/apache2/sites-available/000-default.conf
fi
if [ -n "$APACHE_DOCUMENT_ROOT" ]; then
    sed -i "s|DocumentRoot /var/www/html|DocumentRoot ${APACHE_DOCUMENT_ROOT}|" /etc/apache2/sites-available/000-default.conf
    sed -i "s|<Directory /var/www/html>|<Directory ${APACHE_DOCUMENT_ROOT}>|" /etc/apache2/sites-available/000-default.conf
fi
# 配置PHP
sed -i "s/^memory_limit = .*/memory_limit = ${PHP_MEMORY_LIMIT}/" /etc/php/${PHP_VERSION}/apache2/php.ini
sed -i "s/^upload_max_filesize = .*/upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}/" /etc/php/${PHP_VERSION}/apache2/php.ini
sed -i "s/^post_max_size = .*/post_max_size = ${PHP_POST_MAX_SIZE}/" /etc/php/${PHP_VERSION}/apache2/php.ini
sed -i "s/^max_execution_time = .*/max_execution_time = ${PHP_MAX_EXECUTION_TIME}/" /etc/php/${PHP_VERSION}/apache2/php.ini
# 配置MySQL
if [ "$START_MYSQL" = "1" ]; then
    sed -i "s/^max_connections = .*/max_connections = ${MYSQL_MAX_CONNECTIONS}/" /etc/mysql/my.cnf
    sed -i "s/^innodb_buffer_pool_size = .*/innodb_buffer_pool_size = ${MYSQL_INNODB_BUFFER_POOL_SIZE}/" /etc/mysql/my.cnf
fi
# 配置Redis
if [ "$START_REDIS" = "1" ]; then
    sed -i "s/^port 6379/port ${REDIS_PORT}/" /etc/redis/redis.conf
    sed -i "s/^maxmemory .*/maxmemory ${REDIS_MAXMEMORY}/" /etc/redis/redis.conf
    sed -i "s/^maxmemory-policy .*/maxmemory-policy ${REDIS_MAXMEMORY_POLICY}/" /etc/redis/redis.conf
fi

scripts/php-extensions.sh:

#!/bin/bash
# 启用PHP扩展
if [ -n "$PHP_EXTENSIONS_ENABLE" ]; then
    IFS=',' read -ra EXTENSIONS <<< "$PHP_EXTENSIONS_ENABLE"
    for ext in "${EXTENSIONS[@]}"; do
        if [ -f "/etc/php/${PHP_VERSION}/mods-available/${ext}.ini" ]; then
            phpenmod ${ext}
            echo "Enabled PHP extension: ${ext}"
        else
            echo "Installing PHP extension: ${ext}"
            apt-get update && apt-get install -y php${PHP_VERSION}-${ext} && apt-get clean
        fi
    done
fi
# 禁用PHP扩展
if [ -n "$PHP_EXTENSIONS_DISABLE" ]; then
    IFS=',' read -ra EXTENSIONS <<< "$PHP_EXTENSIONS_DISABLE"
    for ext in "${EXTENSIONS[@]}"; do
        phpdismod ${ext} 2>/dev/null || true
        echo "Disabled PHP extension: ${ext}"
    done
fi

scripts/healthcheck.sh:

#!/bin/bash
# 检查Apache
if [ "$START_APACHE" = "1" ]; then
    curl -f http://localhost:${APACHE_PORT:-80}/ > /dev/null 2>&1 || exit 1
fi
# 检查MySQL
if [ "$START_MYSQL" = "1" ]; then
    mysqladmin ping -h localhost -u root -p${MYSQL_ROOT_PASSWORD} > /dev/null 2>&1 || exit 1
fi
# 检查Redis
if [ "$START_REDIS" = "1" ]; then
    redis-cli -p ${REDIS_PORT:-6379} ping > /dev/null 2>&1 || exit 1
fi
# 检查PHP-FPM
if [ "$START_PHP_FPM" = "1" ]; then
    SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 | grep -q pong || exit 1
fi
exit 0

3. docker-compose.yml 示例

version: '3.8'
services:
  app:
    build:
      context: .
      args:
        - PHP_VERSION=8.4
        - DEBIAN_VERSION=bookworm
    container_name: webstack
    ports:
      - "8080:80"
      - "8443:443"
      - "3306:3306"
      - "6379:6379"
    environment:
      - START_MYSQL=1
      - START_REDIS=1
      - START_APACHE=1
      - START_PHP_FPM=0
      - MYSQL_ROOT_PASSWORD=secure_password
      - MYSQL_DATABASE=myapp
      - APACHE_PORT=80
      - PHP_EXTENSIONS_ENABLE=xdebug,gd,zip
      - PHP_EXTENSIONS_DISABLE=xdebug
      - PERSISTENT_CONFIG_FILES=/etc/apache2/apache2.conf,/etc/php/8.4/apache2/php.ini
      - PERSISTENT_DATA_DIRS=/var/www/html,/var/lib/mysql
    volumes:
      - ./data:/data
      - ./src:/var/www/html
      - ./logs:/var/log
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "/usr/local/bin/healthcheck.sh"]
      interval: 30s
      timeout: 10s
      retries: 3

4. 构建和运行脚本

build.sh:

#!/bin/bash
# 构建脚本示例
VERSION="1.0.0"
PHP_VERSION="${1:-8.4}"
DEBIAN_VERSION="${2:-bookworm}"
echo "Building Docker image with:"
echo "  PHP: ${PHP_VERSION}"
echo "  Debian: ${DEBIAN_VERSION}"
docker build \
  --build-arg PHP_VERSION=${PHP_VERSION} \
  --build-arg DEBIAN_VERSION=${DEBIAN_VERSION} \
  -t webstack:${VERSION}-php${PHP_VERSION} \
  -t webstack:latest \
  .

run.sh:

#!/bin/bash
# 运行脚本示例
docker run -d \
  --name webstack \
  -p 8080:80 \
  -p 3306:3306 \
  -p 6379:6379 \
  -e START_MYSQL=1 \
  -e START_REDIS=1 \
  -e MYSQL_ROOT_PASSWORD=changeme \
  -e MYSQL_DATABASE=myapp \
  -e PHP_EXTENSIONS_ENABLE=xdebug,gd,imagick \
  -v ./app_data:/data \
  webstack:latest

使用说明

构建镜像

# 默认构建 (PHP 8.4)
./build.sh
# 构建特定PHP版本
./build.sh 7.4
./build.sh 8.1
# 指定Debian版本
./build.sh 8.1 bullseye

运行容器

# 使用docker run
docker run -d \
  -p 8080:80 \
  -p 3306:3306 \
  -e START_MYSQL=1 \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e PHP_EXTENSIONS_ENABLE=gd,imagick \
  -v ./data:/data \
  webstack:latest
# 使用docker-compose
docker-compose up -d

环境变量配置示例

# 基本配置
START_MYSQL=1
START_REDIS=1
START_APACHE=1
START_PHP_FPM=0
# MySQL配置
MYSQL_ROOT_PASSWORD=your_password
MYSQL_DATABASE=app_db
MYSQL_USER=app_user
MYSQL_PASSWORD=user_password
# PHP配置
PHP_MEMORY_LIMIT=512M
PHP_UPLOAD_MAX_FILESIZE=128M
PHP_MAX_EXECUTION_TIME=600
PHP_OPCACHE_ENABLE=1
# Apache配置
APACHE_PORT=8080
APACHE_SERVER_NAME=localhost
APACHE_DOCUMENT_ROOT=/var/www/html/public
# Redis配置
REDIS_MAXMEMORY=512mb
REDIS_MAXMEMORY_POLICY=allkeys-lru
REDIS_PORT=6379
# PHP扩展管理
PHP_EXTENSIONS_ENABLE=xdebug,gd,imagick,sqlsrv
PHP_EXTENSIONS_DISABLE=xdebug  # 生产环境禁用xdebug
# 持久化配置
PERSISTENT_CONFIG_FILES=/etc/apache2/apache2.conf,/etc/php/8.4/apache2/php.ini
PERSISTENT_DATA_DIRS=/var/www/html,/var/lib/mysql,/var/log/apache2

特点总结

  1. 版本灵活性: 支持通过构建参数指定Debian、PHP、MySQL、Redis版本
  2. 服务控制: 通过环境变量控制各个服务的启停
  3. 配置灵活性: 所有关键参数都可通过环境变量配置
  4. PHP扩展管理: 方便启用/禁用PHP扩展
  5. 持久化存储: 统一使用/data目录,支持自定义持久化配置
  6. 健康检查: 完整的健康检查机制
  7. 轻量级: 使用s6进程管理器,清理不必要的文件
  8. 生产就绪: 包含日志、监控、安全配置
  9. 多阶段构建: 优化镜像大小
  10. 容器友好: 遵循12要素应用原则

这个方案提供了企业级的灵活性,同时保持了镜像大小优化,适合开发和

发表评论
博客分类