多服务Docker容器构建
- 2026-03-04 21:41:00
- 丁国栋
- 原创 3
假设你是一个Docker容器技术专家,现在需要你制作一个包含 Debian Linux + Apache2 + PHP + MySQL + Redis 等服务的Docker容器,通过这个容器,可以运行这些服务中的部分或者全部以承载应用程序。
以下是一些基本要求:
- 能够通过docker build使用Dockerfile构建出这个容器镜像;
- 能使用包管理器安装的组件尽可能使用包管理器安装;
- Docker镜像中使用的多进程管理器可以使用s6或者supervisord甚至systemd;
- Debian Linux、Apache2、PHP、MySQL、Redis组件的版本在构建镜像阶段可以指定为特定版本,这样可以非常灵活的选择要使用的版本;
- PHP的版本可以支持php7.2、php7.4、php8.1、php8.4和最新稳定版等主流版本;
- 默认构建时所有软件使用最新稳定版本;
- 可以很方便地添加、启用、禁用PHP扩展;
- 可以通过环境变量决定是否启动MySQL、Redis服务;
- 可以通过环境变量控制Apache2、PHP、MySQL、Redis的一些关键参数;
- 所有的需要持久化存储的目录均使用 /data 目录,将 /data 目录下的子目录通过软链接方式链接到需要使用的位置;
- 通过环境变量来控制哪些其他配置文件(例如应用程序的配置文件等)需要持久化;
- 通过环境变量来控制哪些其他目录(例如应用程序的数据目录等)需要持久化;
- 尽可能的控制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
特点总结
- 版本灵活性: 支持通过构建参数指定Debian、PHP、MySQL、Redis版本
- 服务控制: 通过环境变量控制各个服务的启停
- 配置灵活性: 所有关键参数都可通过环境变量配置
- PHP扩展管理: 方便启用/禁用PHP扩展
- 持久化存储: 统一使用
/data目录,支持自定义持久化配置 - 健康检查: 完整的健康检查机制
- 轻量级: 使用s6进程管理器,清理不必要的文件
- 生产就绪: 包含日志、监控、安全配置
- 多阶段构建: 优化镜像大小
- 容器友好: 遵循12要素应用原则
这个方案提供了企业级的灵活性,同时保持了镜像大小优化,适合开发和
发表评论