😱 搭建个环境要一天?用 Docker 一键部署,只需 10 分钟!

首页 编程分享 PHP丨JAVA丨OTHER 正文

洛卡卡了 转载 编程分享 2025-03-08 22:11:13

简介 最近一段时间接了个项目一直在忙碌,遇到了一个非常无语的问题,因为该项目的测试服务器及环境是由甲方人员进行购买、搭建环境,我在本地开发后推到测试服务器上面进行构建测试、由于环境不一致的问题导致产生


前言

最近一段时间接了个项目一直在忙碌,遇到了一个非常无语的问题,因为该项目的测试服务器及环境是由甲方人员进行购买、搭建环境,我在本地开发后推到测试服务器上面进行构建测试、由于环境不一致的问题导致产生了很多问题,出现了经典的 在我机器上就可以运行没问题 的人生哲理......我滴妈 不想吐槽了。

所以说。在本地和测试环境中,使用 Docker 来安装和管理服务已经成为最简单、最常见的方式。通过 docker compose up -d,我们可以一键启动所有依赖服务,提高开发和测试效率。此外,结合 Dockerfile 进行环境自定义,不仅可以实现个性化配置,还能方便团队协作,确保所有成员的环境一致。

Docker

平常情况下当我们入职新公司拿到新电脑的时候 第一步就是安装自己的本地开发环境,无论是Windows环境的安装包还是Mac的brew安装 都需要我们去一步一步手动安装 Nginx、MySQL、Redis、MQ 等服务,并配置各种依赖。这种安装方式无法保证我们一次性顺利安装成功 可能会遇到一些问题导致我们折腾一上午或者一天,这对于我们这种 “经验丰富”的牛马来说是不能容忍的。 那么使用docker安装的好处就体现出来了。

无论我们使用的是Windows系统还是Mac系统我们都可以通过安装Docker Desktop,它提供了简单易用的图形化界面,并且内置了 Docker Engine 和 Docker CLI,适用于本地开发和测试环境。

Docker Desktop安装方式我就不介绍了 不了解的可以参考其他关于介绍Docker Desktop的文章进行了解安装。

验证安装 打开终端(Terminal),运行:

docker -v

看到 Docker 版本信息即安装成功。

使用 Docker 单独安装环境

这里举个例子 比如我们有个项目需要安装php开发环境。我需要安装php、mysql、nginx、redis、rabbitmq这些服务。

1. 安装 PHP

PHP 运行环境可以通过官方提供的 Docker 镜像来安装:

docker pull php:8.2-fpm

然后运行 PHP 容器:

docker run -d --name php-container -p 9000:9000 -v $(pwd)/php:/var/www/html php:8.2-fpm
  • -d:后台运行容器
  • --name php-container:指定容器名称
  • -p 9000:9000:映射 PHP-FPM 端口
  • -v $(pwd)/php:/var/www/html:将本地 php 目录挂载到容器的 /var/www/html,便于开发

容器启动后就是这样的。

可以进入 PHP 容器,运行 php -v 查看版本:

docker exec -it php-container php -v


2. 安装 MySQL

我们可以通过 Docker 轻松安装 MySQL 5.7:

docker run -d --name mysql-container -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:5.7
  • -e MYSQL_ROOT_PASSWORD=123456:设置 root 用户密码
  • -p 3306:3306:映射数据库端口
  • --name mysql-container:指定容器名称

验证 MySQL 运行

docker exec -it mysql-container mysql -uroot -p

输入 123456 进入 MySQL 命令行。


3. 安装 Nginx

docker run -d --name nginx-container -p 80:80 -v $(pwd)/nginx/conf.d:/etc/nginx/conf.d -v $(pwd)/php:/var/www/html nginx
  • -p 80:80:映射 Web 端口
  • -v $(pwd)/nginx/conf.d:/etc/nginx/conf.d:挂载本地 Nginx 配置
  • -v $(pwd)/php:/var/www/html:确保 Nginx 可以访问 PHP 代码目录

测试 Nginx

curl http://localhost

如果返回默认的 Nginx 页面,说明安装成功。


4. 安装 Redis

Redis安装方式如下:

docker run -d --name redis-container -p 6379:6379 redis:5.0
  • -p 6379:6379:映射 Redis 端口
  • --name redis-container:指定容器名称

连接 Redis

docker exec -it redis-container redis-cli

输入 ping,如果返回 PONG,说明 Redis 运行正常。


5. 安装 RabbitMQ

RabbitMQ安装方法如下:

docker run -d --name rabbitmq-container -p 5672:5672 -p 15672:15672 rabbitmq:latest
  • -p 5672:5672:RabbitMQ 消息端口
  • -p 15672:15672:RabbitMQ Web 管理界面端口

访问 Web 管理界面 浏览器打开 http://localhost:15672/,默认用户名密码为 guest/guest


单独docker run的问题

上面我们介绍了如何使用 docker run 命令分别安装 PHP、MySQL、Nginx、Redis 和 RabbitMQ。这种方式虽然能让我们快速启动各个服务,但能发现感觉好麻烦,大概总结一下麻烦点:

1. 多个容器管理麻烦

  • 当我们需要启动多个服务时,每次都要运行多个 docker run 命令,手动指定端口、环境变量、挂载目录等。
  • 例如,如果要启动 PHP、MySQL、Nginx、Redis 和 RabbitMQ,我们需要运行 5 条命令,每次启动、停止、删除都要手动执行,费时费力。

2. 容器之间的网络通信需要手动配置

  • 直接使用 docker run 启动容器后,它们默认属于不同的网络,彼此之间无法直接访问,必须手动创建 Docker 网络:

    docker network create my-network
    

    然后在每个 docker run 命令中加上 --network my-network,这样容器之间才能互相通信,配置过程较为繁琐。

3. 复杂的环境变量和配置管理

  • 我们也许需要为每个服务设置环境变量(如 MySQL 的 MYSQL_ROOT_PASSWORD、Redis 的配置等),如果用 docker run,这些变量必须手动传递,而且不方便维护。
  • 配置文件(如 nginx.conf、PHP 的 php.ini、MySQL 的 my.cnf)需要挂载到正确的路径,使用 docker run 逐个配置很麻烦。

4. 版本控制和团队协作困难

  • 在团队开发中,不同开发人员的环境可能存在差异,比如某人使用 MySQL 5.7,而我自己用了 MySQL 8.0,导致程序行为不同。
  • 直接用 docker run,每个人都要手动执行相同的命令,容易出错,也难以保证所有人的开发环境完全一致。

Docker Compose 的解决方案

这时候就不得不说使用Docker Compose的好处了。Docker Compose 是通过一个 docker-compose.yml 文件 定义所有服务,并提供了一键启动的能力:

  • 统一管理所有服务,只需一个命令即可启动/停止所有容器:

    docker compose up -d
    
  • 自动创建网络,让所有容器在同一个网络下自由通信,无需手动配置。

  • 更方便的配置管理,所有环境变量、挂载路径都可以直接写入 docker-compose.yml,可读性更好。

  • 易于版本控制和团队协作,只需共享 docker-compose.yml 文件,所有人都能获得相同的环境。

示例:使用 Docker Compose 启动 PHP + MySQL + Nginx + Redis + RabbitMQ

用Docker Compose来启动多项服务的话 建议最好设置一个目录来进行统一的文件映射与存放。 比如我本地的项目目录结构是这样的:

project-root/
│── docker-compose.yml
│── php/
│── mysql/
│   ├── my.cnf
│── nginx/
│   ├── conf.d/
│── redis/
│── rabbitmq/
│── wwwroot/
  • php/:PHP 代码目录
  • mysql/my.cnf:MySQL 配置文件
  • nginx/conf.d/:Nginx 配置目录
  • redis/rabbitmq/ 只是占位符目录
  • wwwroot/ 项目存放目录

然后我们这次用新建 docker-compose.yml的方式来一键启动上面我们安装的服务。比如我们新建 docker-compose.yml,然后填入以下内容:

services:
  php:
    image: php:8.2-fpm
    container_name: php-container
    volumes:
      - ./wwwroot:/var/www/html
    networks:
      - my-network
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:5.7
    container_name: mysql-container
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - my-network

  nginx:
    image: nginx:latest
    container_name: nginx-container
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./wwwroot:/var/www/html
    networks:
      - my-network
    depends_on:
      - php

  redis:
    image: redis:5.0
    container_name: redis-container
    restart: always
    ports:
      - "6379:6379"
    networks:
      - my-network

  rabbitmq:
    image: rabbitmq:latest
    container_name: rabbitmq-container
    restart: always
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - my-network

networks:
  my-network:
    driver: bridge

docker-compose.yml 中,每个服务(service)都有不同的配置项,如 imagecontainer_namevolumesnetworksdepends_on 等。下面我们简单逐个解析它们的作用和使用方式。

1. image:指定使用的 Docker 镜像

image: php:8.2-fpm
  • 作用:指定容器使用哪个 Docker 镜像。

  • 示例

    • php:8.2-fpm:使用官方 PHP 8.2-FPM 镜像。
    • mysql:5.7:使用官方 MySQL 5.7 镜像。
    • nginx:latest:使用最新版本的 Nginx 镜像。
  • 如果本地没有这个镜像,Docker 会自动从 Docker Hub 拉取。


2. container_name:指定容器名称

container_name: php-container
  • 作用:为容器指定一个固定的名称,便于管理和操作。

  • 示例

    • container_name: mysql-container → 我们可以用 docker ps 看到容器的名称是 mysql-container
    • 这样我们可以用 docker exec -it mysql-container bash 进入容器,而不用记住随机生成的容器 ID。

注意:如果不指定 container_name,Docker 会自动给容器分配一个随机名称,比如 adoring_turing 这样的名字。


3. volumes:挂载本地文件或目录

volumes:
  - ./wwwroot:/var/www/html
  • 作用:将 宿主机的目录(或文件) 挂载到容器的指定目录,方便开发和数据持久化。

  • 格式

    volumes:
      - 宿主机路径:容器内部路径
    
  • 示例

    • ./wwwroot:/var/www/html
      → 让 PHP 容器 使用本地 wwwroot 目录作为代码目录。这样我们本地修改 wwwroot 里的文件,容器里会同步更新。
    • ./mysql/data:/var/lib/mysql
      → 让 MySQL 容器 将数据库数据存储在 ./mysql/data 目录,防止数据丢失。
    • ./nginx/conf.d:/etc/nginx/conf.d
      → 让 Nginx 容器 使用本地 nginx/conf.d/ 目录中的配置文件,方便修改 Nginx 配置。

注意:如果不使用 volumes,容器中的数据会丢失,因为容器被删除后,它内部的文件也会被清空


4. networks:管理容器间的网络

networks:
  - my-network
  • 作用:将容器加入同一个网络,让它们可以互相通信,而不用手动创建网络。

  • 示例

    networks:
      my-network:
        driver: bridge
    
    • 这样所有在 my-network 网络中的容器都可以通过 容器名称 互相访问。例如:

      • PHP 可以通过 mysql-container 连接 MySQL,而不用 localhost
      • Nginx 可以通过 php-container 访问 PHP-FPM,而不用 127.0.0.1

如果不指定 networks,Docker 默认会创建一个独立的网络,每个容器只能用 localhost 访问自己,不能访问其他容器。


5. depends_on:指定容器依赖关系

depends_on:
  - mysql
  - redis
  • 作用:确保 php 容器 mysqlredis 之后启动

  • 示例

    php:
      depends_on:
        - mysql
        - redis
    
    • 这样 php 不会先于 mysqlredis 启动,可以避免 PHP 启动时 MySQL 还没准备好导致连接失败的情况。

注意

  • depends_on 只保证 容器启动顺序,但 不保证服务内部完全就绪
  • 更可靠的方式 是在 php 容器启动时,加入一个等待逻辑,确保 mysqlredis 可用,比如使用 wait-for-it.sh 脚本。

总结一下

配置项 作用 示例
image 指定 Docker 镜像 php:8.2-fpm
container_name 设定容器名称 php-container
volumes 挂载本地目录到容器 ./wwwroot:/var/www/html
networks 让容器互相通信 my-network
depends_on 设置容器依赖关系 php -> mysql, redis

启动项目

docker-compose.yml 所在目录下,运行:

docker compose up -d

这将:

  • 拉取所需镜像
  • 启动 PHP、MySQL、Nginx、Redis 和 RabbitMQ
  • 自动创建网络 my-network,让各个服务互相通信

停止 & 删除容器

docker compose down

这将停止并移除所有容器。


上面我们已经使用 docker-compose.yml 成功编排了 PHP、MySQL、Nginx、Redis 和 RabbitMQ,并且能够一键启动所有服务。但是,虽然 环境的版本一致了,但仍然会遇到一些 额外的问题,比如我们需要进行 PHP 扩展的安装

问题:为什么仅使用 docker-compose.yml 还不够?

让我们来看看,在 docker-compose.yml 方式下,可能会出现哪些问题:

1. 需要手动进入容器安装 PHP 扩展

当我们启动 PHP 容器后,默认的 php:8.2-fpm 镜像 只包含 PHP 运行环境,但实际项目可能需要用到 pdo_mysqlgdredisbcmath 等扩展。例如,我们需要执行:

docker exec -it php-container bash

然后在容器内手动安装:

docker-php-ext-install pdo_mysql gd bcmath
docker-php-ext-enable redis

这样虽然可以解决问题,但 每次换一台机器都要手动操作一次,非常麻烦。

2. 团队协作时,每个人都要重复安装

假设我们的同事也使用了我们的 docker-compose.yml 启动环境:

docker compose up -d

他虽然能启动 PHP 容器,但仍然需要手动进入容器安装扩展。如果 有 10 个人 都要这么做,那就是 10 次重复劳动,这不仅费时,还可能导致版本不一致(有些人可能少装了某个扩展)。

3. 难以维护,容易出错

  • 如果某人忘记安装某个扩展,可能会导致 代码无法运行,然后需要额外的时间排查问题。
  • 新成员加入团队时,还需要花额外的时间指导他们如何手动安装扩展,增加了学习成本。

解决方案:使用 Dockerfile

我们最终希望:

  • PHP 镜像启动后,扩展就已经安装好了,不需要每个人手动操作。
  • 所有人都能使用相同的 PHP 运行环境,包含所有必要的扩展
  • 一次配置,所有人受益,避免重复劳动。

👉 这就是 Dockerfile 的作用!我们可以使用 Dockerfile 定制自己的 PHP 镜像,在构建镜像时自动安装所有需要的扩展,从而彻底解决这个问题。

接下来,我们将使用 Dockerfile 来构建一个包含 PHP 扩展的自定义镜像,确保每个人启动 PHP 容器后,就能直接使用项目所需的环境!🚀

我们在项目的 php/ 目录下,新建 Dockerfile(不带扩展名),然后填入以下内容:

# 使用 php:8.2-fpm 作为基础镜像
FROM php:8.2-fpm

# 安装常见的扩展依赖(这适用于很多 PHP 扩展的依赖)
RUN apt-get update && apt-get install -y \
    libzip-dev \
    libxml2-dev \
    libcurl4-openssl-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libmemcached-dev \
    libssl-dev \
    libssh2-1-dev \
    libsodium-dev \
    libicu-dev \
    libmagickwand-dev \
    gettext \
    unzip \
    && rm -rf /var/lib/apt/lists/*

# 安装 PHP 扩展
RUN docker-php-ext-install -j$(nproc) pdo_mysql

# 安装并启用 Redis 扩展
RUN pecl install redis && docker-php-ext-enable redis

# 安装并启用 Memcached 扩展
RUN pecl install memcached && docker-php-ext-enable memcached

# 安装并启用 MongoDB 扩展
RUN pecl install mongodb && docker-php-ext-enable mongodb

# 安装并启用 Xdebug 扩展
RUN pecl install xdebug && docker-php-ext-enable xdebug

# 安装并启用 Opcache 扩展
RUN docker-php-ext-enable opcache

# 安装并启用 Swoole 扩展(如果需要)
RUN pecl install swoole && docker-php-ext-enable swoole

# 安装并启用 igbinary 扩展(用于更高效的序列化)
RUN pecl install igbinary && docker-php-ext-enable igbinary

# 安装并启用 GD 扩展
RUN docker-php-ext-install gd

# 安装并启用 PCNTL 扩展
RUN docker-php-ext-install pcntl 

# 安装 Composer(PHP 依赖管理工具)
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# 设置 Composer 为全局可用
RUN ln -s /usr/local/bin/composer /usr/bin/composer

# 清理缓存以减小镜像大小
RUN apt-get clean

# 配置 Xdebug
RUN echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
    echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
    echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
    echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
    echo "xdebug.client_port=9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

# 默认启用 OPcache
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

# 设置工作目录
WORKDIR /var/www/html

# 默认命令
CMD ["php-fpm"]

修改 docker-compose.yml 使用 Dockerfile

docker-compose.yml 中,我们需要 修改 PHP 的 image 配置,让它从 Dockerfile 构建自定义镜像,而不是直接拉取 php:8.2-fpm

services:
  php:
    build:
      context: ./php   # 指定 Dockerfile 所在目录
      dockerfile: Dockerfile  # 这个参数可以省略,默认就是 Dockerfile
    container_name: php-container
    volumes:
      - ./wwwroot:/var/www/html  # 挂载项目代码
    networks:
      - my-network
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:5.7
    container_name: mysql-container
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - my-network

  nginx:
    image: nginx:latest
    container_name: nginx-container
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./wwwroot:/var/www/html  # 让 Nginx 也能访问 PHP 代码
    networks:
      - my-network
    depends_on:
      - php

  redis:
    image: redis:5.0
    container_name: redis-container
    restart: always
    ports:
      - "6379:6379"
    networks:
      - my-network

  rabbitmq:
    image: rabbitmq:latest
    container_name: rabbitmq-container
    restart: always
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - my-network

networks:
  my-network:
    driver: bridge
改动点
  1. build:

    • 替换 image: php:8.2-fpm,改为:

      build:
        context: ./php
        dockerfile: Dockerfile
      
    • 这样 Docker Compose 会在 php/ 目录 下查找 Dockerfile 并构建自定义镜像。

  2. volumes:

    • 仍然保持 ./wwwroot:/var/www/html,保证代码能够挂载到 PHP 容器中。

构建并运行容器

第一次运行(需要构建镜像)

docker compose up -d --build
  • --build 选项确保 重新构建 PHP 镜像,并安装扩展。

之后的启动(无需重建)

docker compose up -d


验证 PHP 是否安装了扩展

进入 PHP 容器:

docker exec -it php-container /bin/bash

然后运行:

php -m | grep -E "pdo_mysql|gd|bcmath|opcache|redis|memcached|mongodb|xdebug|swoole|igbinary"

如果输出如下:

说明所有扩展已正确安装!🎉

这样做的好处

解决了手动安装 PHP 扩展的问题,所有扩展在构建镜像时自动安装。
保证团队环境一致,不需要每个开发者手动安装扩展。
包含 Composer,方便管理 PHP 依赖。
支持 Xdebug 远程调试,方便开发和调试代码。
构建一次,所有人都可以直接使用相同的环境,提升开发效率。

后续优化(可选)

如果我们还想:

  1. 优化 PHP 配置,可以在 php/ 目录下创建 php.ini,并在 Dockerfile 里挂载:

    COPY php.ini /usr/local/etc/php/php.ini
    
  2. 减少镜像体积,可以在 RUN 语句后加上 rm -rf /var/lib/apt/lists/* 来清理不必要的缓存。

除了 PHP,我们安装的 MySQL、Redis、Nginx 和 RabbitMQ 也可以使用 Dockerfile 进行 自定义构建配置优化
虽然 docker-compose.yml 可以直接拉取官方镜像,但通过 Dockerfile 自定义构建,我们可以实现:

  • 定制化配置(如 MySQL 自定义 my.cnf、Nginx 自定义 nginx.conf)。
  • 安装额外插件(如 MySQL 额外扩展、Nginx 额外模块)。
  • 统一环境管理,避免不同机器上手动修改配置导致的问题。

其他服务的Dockerfile我就不一一写示例了,总之我们可以通过类似的方式在 mysql/redis/nginx/rabbitmq/ 目录下创建各自的 Dockerfile 来完成自定义。 然后修改docker-compose.yml对应的地方来进行重新构建启动。


到这里,我们已经: ✅ 了解了 docker-compose.yml 如何进行服务编排。
✅ 发现了 docker-compose.yml 的局限性(仍需手动安装 PHP 扩展)。
✅ 通过 Dockerfile 自定义了 PHP 运行环境(扩展已安装、环境已配置)。
✅ 了解了 MySQL、Redis、Nginx、RabbitMQ 也可以使用 Dockerfile 进行自定义构建,并列出了使用 Dockerfile 的优势。


其他

针对上面的一些示例。我再简单说一些可以完善的地方以供参考哈。

1. 如何优化 Docker 镜像大小?

目前我们的 Dockerfile 直接基于 php:8.2-fpm,并安装了很多扩展。如果需要优化镜像大小,可以:

  • 使用 multi-stage build 来减少不必要的组件(特别适用于构建 PHP 应用)。

  • 减少 apt-get 安装后的缓存

    RUN apt-get update && apt-get install -y \
        some-packages \
        && rm -rf /var/lib/apt/lists/* && apt-get clean
    
  • 避免安装不必要的软件,尽量精简。

如果我们的 PHP 代码 最终是要在生产环境运行,那么可以用 更轻量的镜像,如 php:8.2-alpine

FROM php:8.2-fpm-alpine

这样可以减少很多无用的 Linux 库。


2. 如何管理环境变量?

在我们的 docker-compose.yml 里面的 MySQL 密码、数据库名是写死的:

environment:
  MYSQL_ROOT_PASSWORD: 123456
  MYSQL_DATABASE: mydb
  MYSQL_USER: myuser
  MYSQL_PASSWORD: mypassword

但在 生产环境中,明文暴露密码是非常危险的!
建议改为 .env 文件管理环境变量

environment:
  MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
  MYSQL_DATABASE: ${MYSQL_DATABASE}
  MYSQL_USER: ${MYSQL_USER}
  MYSQL_PASSWORD: ${MYSQL_PASSWORD}

然后创建 .env 文件:

MYSQL_ROOT_PASSWORD=your_secure_password
MYSQL_DATABASE=mydb
MYSQL_USER=myuser
MYSQL_PASSWORD=mypassword

这样可以 避免密码硬编码,并且更方便切换不同的环境配置。


3. 如何让 Nginx 正确解析 PHP 请求?

我们虽然在 docker-compose.yml 里定义了 Nginx 服务:

volumes:
  - ./nginx/conf.d:/etc/nginx/conf.d

但如果 没有正确配置 nginx.conf,Nginx 可能不会解析 PHP 文件。比如我们提供一个示例 nginx.conf

server {
    listen 80;
    server_name localhost;
    root /var/www/html;

    index index.php index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ .php$ {
        include fastcgi_params;
        fastcgi_pass php-container:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

这样,Nginx 就能正确解析 .php 文件,并将请求转发到 php-container


4. 如何管理数据持久化?

目前 docker-compose.yml 里挂载了 mysql/data 目录:

volumes:
  - ./mysql/data:/var/lib/mysql

但我们可以 进一步优化数据管理

  • 使用 Docker Volume,而不是直接挂载宿主机目录

    volumes:
      mysql-data:
    services:
      mysql:
        volumes:
          - mysql-data:/var/lib/mysql
    

    这样即使 docker-compose down,数据仍然保留在 docker volume 里,而不会误删。

  • 对 Redis 进行持久化(AOF 方式)

    redis:
      volumes:
        - ./redis/data:/data
    

    确保 Redis 不会在容器重启后丢失数据


5. 使用 Makefile 简化管理

比如我们现在启动项目需要运行:

docker compose up -d --build

如果要停止:

docker compose down

那我们其实可以创建一个 Makefile,让这些命令更简洁:

up:
	docker compose up -d --build

down:
	docker compose down

logs:
	docker compose logs -f

restart:
	docker compose down && docker compose up -d --build

这样,执行 make up 就可以一键启动环境,比手动输入命令要方便很多。


6. 如何在 CI/CD 中使用 Docker Compose?

如果我们的团队需要在 CI/CD(持续集成/持续部署) 流程中使用 Docker,可以在 GitHub Actions 或 GitLab CI 里集成 docker-compose,自动构建和部署服务。例如:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Docker
        uses: docker/setup-buildx-action@v1

      - name: Build and run services
        run: |
          docker compose up -d --build

这样,每次推送代码,GitHub Actions 就会 自动启动 Docker 容器 进行测试。


7. 如何优化 Xdebug 远程调试?

因为上面我们在 Dockerfile 里已经安装了 Xdebug:

RUN pecl install xdebug && docker-php-ext-enable xdebug

但如果本地开发时 想用 VS Code 进行远程调试,需要添加 .vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug",
      "type": "php",
      "request": "launch",
      "port": 9003,
      "pathMappings": {
        "/var/www/html": "${workspaceFolder}"
      }
    }
  ]
}

这样,我们就可以在 VS Code 里直接调试 PHP 代码 了。


8. 生产环境 vs 开发环境

目前我们的 Dockerfile 适用于 开发环境,但在 生产环境,也许我们可能希望:

  • 去掉 Xdebug(提高性能)。

  • 使用 php:8.2-fpm-alpine,减少镜像大小

  • 增加 healthcheck 来自动检测 MySQL、Redis 是否存活

    mysql:
      healthcheck:
        test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
        interval: 30s
        timeout: 10s
        retries: 3
    

    这样,容器会自动检测 MySQL 是否可用,如果失败,Docker 会自动重启它。


最后

通过上面的示例,我们可以看到,使用 Docker 及 Docker Compose 可以极大地简化本地和测试环境的搭建,让我们能够快速启动完整的 PHP 运行环境,包括 PHP、MySQL、Nginx、Redis、RabbitMQ 等常见服务。同时,使用 Dockerfile 进一步定制化,使得每个开发人员的环境都保持一致,避免了手动安装扩展、配置环境的麻烦。

然而,我要说下 上面的这些示例 主要适用于本地开发和测试环境。当我们进入 正式生产环境 时,就需要考虑更严谨的 Dockerfile 编写和镜像构建,比如:

  • 精简 Docker 镜像,使用 php:8.2-fpm-alpine 以减少不必要的软件包,优化启动速度和安全性。
  • 环境变量管理,使用 .env 文件或 Kubernetes Secret 来存储敏感信息,避免密码硬编码。
  • 数据持久化和备份,使用 Docker Volumes 或 NFS 来管理数据库、Redis 缓存等关键数据的存储,确保高可用性。
  • 日志管理,使用 docker logs 并结合 ELK(Elasticsearch + Logstash + Kibana)或 Loki 进行日志收集和分析。
  • 监控与健康检查,结合 Prometheus + Grafana 监控 MySQL、Redis、Nginx 的健康状态,避免服务宕机。
  • 结合 Kubernetes 进行集群部署,通过 K8s + Helm 实现容器编排,提供弹性扩展、自动恢复等能力,适用于大规模生产环境。

🚨 最后提醒
上面的示例主要适用于本地和测试环境,生产环境请慎重使用!
在生产环境中,应根据实际需求优化 Dockerfile、镜像大小、安全策略、数据持久化方案、监控和运维策略,确保服务的稳定性和安全性。

在本地和测试环境,我们通常使用 Docker 搭建 MySQL、Redis、Elasticsearch,这非常方便,也能快速重置环境。但在生产环境中,使用 Docker 直接运行存储型服务可能存在数据丢失的风险,因为:

  1. 容器是无状态的,默认情况下,Docker 容器内的数据会随容器销毁而丢失。
  2. 数据存储依赖 volumes 或宿主机磁盘,但如果没有做好 数据持久化(如 RAID 备份、云盘挂载),当服务器宕机时,数据可能丢失或损坏。
  3. 数据一致性和高可用性问题,对于 MySQL 这类数据库,需要 主从复制分布式存储,而 Docker 自建方案在生产环境下维护成本较高。

生产环境的推荐做法

使用云服务的数据库:如 AWS RDS、阿里云 RDS、腾讯云 MySQL 代替 Docker 运行的 MySQL。
使用云服务的 Redis/ES:如 阿里云 Redis、腾讯云 Redis、AWS ElastiCache,保证 高可用、自动备份和恢复
使用 Kubernetes 挂载持久化存储:如果必须在 Kubernetes 集群内运行 MySQL、Redis、ES,可以使用 Ceph、NFS、EBS(AWS)、Alibaba Cloud Disk 持久化存储,避免数据丢失。

🚨 不建议直接用 Docker 运行 MySQL、Redis、ES 在生产环境,除非:

  1. 做好了数据备份和容灾措施(定期快照、主从同步)。
  2. 使用分布式数据库架构(如 MySQL 高可用集群)。
  3. Redis 只用作缓存,而不是数据存储(Redis 数据丢失影响较小)。

转载链接:https://juejin.cn/post/7478288498673795087


Tags:


本篇评论 —— 揽流光,涤眉霜,清露烈酒一口话苍茫。


    声明:参照站内规则,不文明言论将会删除,谢谢合作。


      最新评论




ABOUT ME

Blogger:袅袅牧童 | Arkin

Ido:PHP攻城狮

WeChat:nnmutong

Email:nnmutong@icloud.com

标签云