PHP报错Call to undefined function解决

前言

运行PHP代码,突然报错:"Call to undefined function xxx()"?这个错太常见了,一般就是扩展没装或者文件没引入。本文讲怎么排查和解决。

一、报错长啥样

Fatal error: Uncaught Error: Call to undefined function mysql_connect() in xxx.php on line 5

意思:你调用了一个不存在的函数。

二、常见原因

原因1:PHP扩展没装

比如用了 mysqli_connect(),但没装mysqli扩展。

或者用了 gd库 的函数,但没装gd。

原因2:文件没引入

你自己写的函数,忘了include/require文件。

原因3:函数名写错了

拼写错了,比如 str_lower 写成 strlower。

原因4:PHP版本不对

低版本PHP没有高版本才有的函数。

三、排查步骤

第一步:看是哪个函数报错

报错信息里会告诉你函数名:

Call to undefined function curl_init()

这里就是 curl_init() 函数没找到。

第二步:判断是内置函数还是自己写的

  • 内置函数:PHP自带的,比如 curl_init、mysqli_connect
  • 自己写的:你在别的文件里定义的

第三步:如果是内置函数,查扩展装了没

php -m | grep curl

看看curl扩展装了没。

或者写个phpinfo:

<?php
phpinfo();
?>

搜这个函数名对应的扩展,看有没有。

四、安装扩展

例子1:装curl扩展

# CentOS
yum install php-curl

# Ubuntu
apt install php-curl

例子2:装mysqli扩展

yum install php-mysqli

例子3:装gd库

yum install php-gd

装完重启

systemctl restart php-fpm

五、如果是自己写的函数

检查文件引入

<?php
// 引入函数文件
require 'functions.php';

// 调用
my_function();

检查函数名对不对

// functions.php里定义的
function my_function() {
    // ...
}

调用的时候名字要一模一样。

检查命名空间

如果用了命名空间,要注意:

<?php
namespace App;

// 调用别的命名空间的函数
\OtherNamespace\my_function();

六、常见函数和对应扩展

函数 需要的扩展
mysqli_connect mysqli
curl_init curl
imagecreate gd
mb_strlen mbstring
openssl_encrypt openssl
pdo连接 pdo_mysql
json_encode json(默认有)
file_get_contents 不需要(默认有)

七、常见坑

坑1:mysql函数被废弃了

PHP7以后,mysql_* 函数都没了,要用mysqli或者PDO。

Call to undefined function mysql_connect()

解决: 换成mysqli或者PDO。

坑2:CLI和FPM装的扩展不一样

命令行里装了,网页上没有。

原因: CLI和FPM用不同的php.ini。

解决: 看phpinfo里的配置,装到对应的PHP版本里。

坑3:装了扩展没重启

装完忘了重启php-fpm。

解决:

systemctl restart php-fpm

坑4:函数名大小写

PHP函数名不区分大小写,但自己写的类方法区分。

坑5:Composer自动加载没生效

用了Composer的库,但没 vendor/autoload.php。

require 'vendor/autoload.php';

八、快速排查

第一步:看报错的函数名

Call to undefined function xxx()

第二步:查扩展

php -m | grep 扩展名

第三步:没装就装

yum install php-扩展名

第四步:重启

systemctl restart php-fpm

九、常用扩展安装

一次性装常用扩展

yum install php-mysqli php-curl php-gd php-mbstring php-xml php-json

Ubuntu

apt install php-mysqli php-curl php-gd php-mbstring php-xml

总结

记住:

  1. 报错会告诉你是哪个函数没找到
  2. 内置函数就查扩展装了没
  3. 自己写的就查文件引入了没
  4. 装完扩展要重启php-fpm
  5. mysql_* 函数PHP7没了,换mysqli

排查流程:

看报错函数名 → 判断是内置还是自己写的 → 内置查扩展 → 自己写的查引入 → 解决

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:38 

PHP mysqli连接MySQL,完整示例代码

前言

PHP连MySQL用mysqli扩展,比mysql扩展好用,支持预处理、事务等。本文写个完整的连接和增删改查示例。

一、连接数据库

面向过程写法

<?php
$host = '127.0.0.1';
$user = 'root';
$pass = '123456';
$dbname = 'test';
$port = 3306;

// 连接
$conn = mysqli_connect($host, $user, $pass, $dbname, $port);

// 检查连接
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

echo '连接成功';

面向对象写法

<?php
$conn = new mysqli('127.0.0.1', 'root', '123456', 'test', 3306);

if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

echo '连接成功';

二、设置字符集

mysqli_set_charset($conn, 'utf8mb4');
// 或者
$conn->set_charset('utf8mb4');

注意: 一定要设utf8mb4,不然中文表情存不进去。

三、查询数据(SELECT)

面向过程

$sql = "SELECT id, name, age FROM users WHERE status = 1";
$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {
    while ($row = mysqli_fetch_assoc($result)) {
        echo "ID: " . $row['id'] . " 姓名: " . $row['name'] . "<br>";
    }
} else {
    echo "没有数据";
}

面向对象

$sql = "SELECT id, name, age FROM users WHERE status = 1";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "ID: " . $row['id'] . " 姓名: " . $row['name'] . "<br>";
    }
}

取所有数据为数组

$list = [];
while ($row = mysqli_fetch_assoc($result)) {
    $list[] = $row;
}

print_r($list);

四、插入数据(INSERT)

直接写SQL

$name = '张三';
$age = 20;

$sql = "INSERT INTO users (name, age) VALUES ('$name', $age)";

if (mysqli_query($conn, $sql)) {
    echo "插入成功,ID: " . mysqli_insert_id($conn);
} else {
    echo "插入失败: " . mysqli_error($conn);
}

预处理(防SQL注入,推荐)

$stmt = mysqli_prepare($conn, "INSERT INTO users (name, age) VALUES (?, ?)");

mysqli_stmt_bind_param($stmt, 'si', $name, $age);
// 'si' 表示:string, int

$name = '李四';
$age = 25;

if (mysqli_stmt_execute($stmt)) {
    echo "插入成功,ID: " . mysqli_insert_id($conn);
} else {
    echo "失败: " . mysqli_error($conn);
}

五、更新数据(UPDATE)

$id = 1;
$name = '王五';

$sql = "UPDATE users SET name = '$name' WHERE id = $id";

if (mysqli_query($conn, $sql)) {
    echo "更新成功,影响行数: " . mysqli_affected_rows($conn);
}

六、删除数据(DELETE)

$id = 1;

$sql = "DELETE FROM users WHERE id = $id";

if (mysqli_query($conn, $sql)) {
    echo "删除成功";
}

七、关闭连接

mysqli_close($conn);
// 或者
$conn->close();

八、完整示例封装

<?php
class DB
{
    private $conn;

    public function __construct()
    {
        $this->conn = new mysqli('127.0.0.1', 'root', '123456', 'test', 3306);
        if ($this->conn->connect_error) {
            die('连接失败');
        }
        $this->conn->set_charset('utf8mb4');
    }

    // 查询多行
    public function queryAll($sql)
    {
        $result = $this->conn->query($sql);
        $list = [];
        while ($row = $result->fetch_assoc()) {
            $list[] = $row;
        }
        return $list;
    }

    // 查询单行
    public function queryOne($sql)
    {
        $result = $this->conn->query($sql);
        return $result->fetch_assoc();
    }

    // 关闭
    public function __destruct()
    {
        $this->conn->close();
    }
}

// 使用
$db = new DB();
$users = $db->queryAll("SELECT * FROM users");
print_r($users);

九、常见坑

坑1:SQL注入

直接拼接变量到SQL里,有注入风险。

解决: 用预处理语句(?占位符)。

坑2:中文乱码

忘了设字符集。

解决: 连接后立刻 set_charset('utf8mb4')。

坑3:只取一行

用 mysqli_fetch_assoc 循环多次才取完。

注意: 这个函数每次只取一行,要循环。

坑4:连接失败没报错

生产环境关了错误显示。

解决: 手动检查 mysqli_connect_error()。

坑5:忘了关连接

频繁连接不关,占资源。

解决: 用完就关,或者用单例。

总结

记住:

  1. mysqli_connect 连接数据库
  2. set_charset('utf8mb4') 设字符集
  3. mysqli_query 执行SQL
  4. mysqli_fetch_assoc 取数据
  5. 防注入用预处理(?占位符)
  6. 用完 mysqli_close 关连接

最常用:

$conn = mysqli_connect('127.0.0.1', 'root', '123456', 'test', 3306);
mysqli_set_charset($conn, 'utf8mb4');

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:36 

PHP上传文件大小限制,修改php.ini

前言

上传个大点的文件,提示"文件过大"或者直接报错?那是PHP上传限制太小了。本文讲怎么改php.ini调大上传限制。

一、默认限制多大

PHP默认上传限制一般是:

  • upload_max_filesize = 2M
  • post_max_size = 8M

传个10M的文件就不行了。

二、要改哪几个参数

1. upload_max_filesize

单个文件最大大小:

upload_max_filesize = 100M

2. post_max_size

整个POST请求最大大小:

post_max_size = 100M

注意: post_max_size要 >= upload_max_filesize。

3. memory_limit

脚本可用内存:

memory_limit = 128M

要比post_max_size大。

4. max_execution_time

脚本最大执行时间(秒):

max_execution_time = 300

传大文件要久一点,设长点。

5. max_input_time

接收数据最大时间(秒):

max_input_time = 300

三、找php.ini

php --ini

或者写个phpinfo:

<?php
phpinfo();
?>

看Loaded Configuration File。

四、修改php.ini

改成100M上传

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M
max_execution_time = 300
max_input_time = 300

改成50M上传

upload_max_filesize = 50M
post_max_size = 50M
memory_limit = 64M

五、改完重启

PHP-FPM

systemctl restart php-fpm

Apache

systemctl restart httpd

六、验证

写个phpinfo,看这几个参数改了没:

<?php
phpinfo();
?>

搜 upload_max_filesize,看是不是改过来了。

七、Nginx也要改

光改PHP不够,Nginx也有限制。

Nginx配置

client_max_body_size 100M;

改完重载

nginx -t
nginx -s reload

八、常见坑

坑1:改了php.ini不生效

改的不是实际加载的那个。

解决: 用phpinfo看实际加载的是哪个。

坑2:只改了upload_max_filesize

post_max_size没改,还是不行。

注意: 两个都要改,post_max_size >= upload_max_filesize。

坑3:Nginx挡了

PHP改了,Nginx的client_max_body_size没改,还是413错误。

解决: Nginx也改。

坑4:内存不够

Allowed memory size of ... bytes exhausted

解决: 加大memory_limit。

坑5:上传超时

大文件传一半就断了。

解决: 加大max_execution_time和max_input_time。

坑6:.htaccess里覆盖了

有些主机在.htaccess里设了php_value。

检查: 看看.htaccess里有没有。

九、快速排查

上传报错

  1. 先看PHP的upload_max_filesize够不够
  2. 再看post_max_size够不够
  3. 再看Nginx的client_max_body_size够不够
  4. 再看memory_limit够不够

十、推荐配置

一般网站(20M上传)

upload_max_filesize = 20M
post_max_size = 20M
memory_limit = 64M

文件站(100M上传)

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M
max_execution_time = 300
max_input_time = 300

Nginx对应

client_max_body_size 100M;

总结

记住:

  1. 改三个参数:upload_max_filesize、post_max_size、memory_limit
  2. post_max_size >= upload_max_filesize
  3. Nginx也要改client_max_body_size
  4. 改完重启php-fpm和reload nginx
  5. 大文件要加大超时时间

最常用:

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:35 

PHP开启错误显示,php.ini配置修改

前言

PHP报错了,页面白屏或者500,啥也不显示,不知道哪错了?开了错误显示就能看到具体错误了。本文讲怎么改php.ini开启错误显示。

一、为什么不显示错误

生产环境为了安全,默认不显示错误,防止暴露敏感信息(路径、数据库密码等)。

但开发的时候就不方便了,啥也看不到。

二、找到php.ini

找php.ini位置

php --ini

输出:

Loaded Configuration File: /etc/php.ini

或者:

/etc/php/7.4/cli/php.ini

注意:CLI和FPM的php.ini可能不一样。

看当前加载的是哪个

写个phpinfo:

<?php
phpinfo();
?>

浏览器访问,看Loaded Configuration File。

三、修改php.ini

1. 显示错误

display_errors = On

2. 报错级别

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

或者全开:

error_reporting = E_ALL

3. 错误日志

log_errors = On
error_log = /var/log/php_errors.log

4. 显示启动错误

display_startup_errors = On

四、改完重启

PHP-FPM

systemctl restart php-fpm

或者:

systemctl reload php-fpm

Apache

systemctl restart httpd

五、代码里临时开启

不想改php.ini,在代码开头加:

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

或者:

ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);

六、常见错误类型

E_ALL

所有错误和警告。

E_ERROR

致命运行错误,脚本停止。

E_WARNING

运行时警告,脚本继续。

E_NOTICE

运行时注意,比如未定义变量。

七、常见坑

坑1:改了php.ini不生效

改的不是实际加载的那个php.ini。

解决: 用 php --ini 看实际加载的是哪个。

坑2:FPM和CLI的php.ini不一样

命令行改了,网页上不生效。

原因: CLI和FPM用不同的php.ini。

解决: 改FPM的那个,比如 /etc/php/7.4/fpm/php.ini。

坑3:页面白屏

可能是语法错误,php.ini都没加载起来。

检查: 看PHP错误日志。

坑4:开了错误显示有安全风险

生产环境不能开!

注意: 排查完一定要改回去。

坑5:.htaccess里覆盖了

有些主机在.htaccess里设了php_flag。

检查: 看看.htaccess里有没有。

八、生产环境配置

生产环境不要开display_errors,但要开日志:

display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED

九、查看错误日志

PHP错误日志

tail -f /var/log/php_errors.log

Nginx错误日志

tail -f /var/log/nginx/error.log

Apache错误日志

tail -f /var/log/httpd/error_log

十、快速排查

第一步:代码里临时开

ini_set('display_errors', 1);
error_reporting(E_ALL);

第二步:看具体报错

刷新页面,看报什么错。

第三步:解决完删掉

改完了把代码里的ini_set删掉,或者改回生产配置。

总结

记住:

  1. display_errors = On 显示错误
  2. error_reporting = E_ALL 显示所有错误
  3. 改完重启php-fpm
  4. 生产环境关掉display_errors,开log_errors
  5. 代码里可以用ini_set临时开

开发环境推荐:

display_errors = On
error_reporting = E_ALL
log_errors = On

生产环境推荐:

display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_NOTICE

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:34 

Composer安装依赖超时,国内镜像源配置

前言

composer install 装依赖,半天没反应,最后超时失败?那是因为默认的源在国外,国内访问慢。换个国内镜像就快了。

一、为什么会超时

Composer默认从 packagist.org 下载包,服务器在国外,国内访问慢,经常超时。

换个国内镜像源,速度飞快。

二、常用国内镜像

1. 阿里云镜像

https://mirrors.aliyun.com/composer/

2. 腾讯云镜像

https://mirrors.tencent.com/composer/

3. 华为云镜像

https://mirrors.huaweicloud.com/repository/php/

4. 豆瓣镜像(老了,不推荐)

https://packagist.phpcomposer.com

三、全局配置镜像

方法1:命令行配置

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

方法2:改config文件

编辑 ~/.composer/config.json:

{
    "repositories": {
        "packagist": {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/"
        }
    }
}

查看当前镜像

composer config -g repo.packagist

四、项目单独配置

在项目根目录的 composer.json 里加:

{
    "repositories": {
        "packagist": {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/"
        }
    }
}

或者用命令:

composer config repo.packagist composer https://mirrors.aliyun.com/composer/

五、临时使用镜像

不想改全局配置,临时用一次:

composer install --prefer-dist -vvv --repository-url=https://mirrors.aliyun.com/composer/

六、其他常用配置

1. 禁用github接口调用

有时候github的API调用会卡住,禁用掉:

composer config -g github-protocols https

2. 国内composer下载地址

安装composer的时候也慢,用国内下载:

php -r "copy('https://mirrors.aliyun.com/composer/composer.phar', 'composer.phar');"

3. 查看配置

composer config -g --list

七、安装依赖

安装

composer install

更新

composer update

加-v看详细过程

composer install -vvv

八、常见坑

坑1:装了还是慢

缓存没清。

解决:

composer clear-cache

坑2:报SSL错误

SSL certificate problem

解决: 关掉SSL验证(不推荐临时用):

composer config -g secure-http false

坑3:提示文件权限不够

Failed to download ...

解决: 目录权限不对。

chmod -R 777 vendor
chmod -R 777 composer.json

坑4:内存不够

Allowed memory size of ... bytes exhausted

解决: 加大内存:

php -d memory_limit=-1 /usr/local/bin/composer install

坑5:镜像也挂了

阿里云镜像有时候也抽风。

解决: 换一个镜像,比如腾讯云的。

九、验证镜像生效

查看当前镜像源

composer config -g repo.packagist

输出应该是你设的阿里云地址。

装个小包试试速度

composer require monolog/monolog

看下载速度快不快。

十、推荐配置

一键配置阿里云镜像

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

清缓存

composer clear-cache

安装

composer install

总结

记住:

  1. 慢就换国内镜像,阿里云的最稳
  2. 全局配置:composer config -g repo.packagist composer 地址
  3. 不行就清缓存:composer clear-cache
  4. 换镜像:阿里云、腾讯云、华为云
  5. 临时用:加 --repository-url=地址

最常用:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:32 

ThinkPHP6 500报错,开启错误日志查看原因

前言

ThinkPHP6网站突然500报错,页面啥也不显示,不知道哪错了?本文讲怎么开启错误显示和日志,快速定位问题。

一、为什么500不显示错误

生产环境为了安全,关闭了错误显示,直接返回500,不显示具体错误信息。

这是对的,不然用户能看到你的代码路径、数据库密码等敏感信息。

但排查问题就不方便了。

二、临时开启错误显示

修改 .env 文件

项目根目录下有个 .env 文件:

APP_DEBUG = true

把 APP_DEBUG 改成 true。

刷新页面,就能看到具体错误了。

注意

排查完记得改回 false! 生产环境不能开debug。

三、开启日志记录

就算不开debug,错误也会记录到日志文件里。

日志配置

config/log.php 文件:

return [
    // 默认日志通道
    'default' => 'file',

    // 日志通道列表
    'channels' => [
        'file' => [
            // 日志记录方式
            'type' => 'File',
            // 日志保存目录
            'path' => '',
            // 单文件日志写入
            'single' => false,
            // 独立日志级别
            'apart_level' => [],
            // 最大日志文件数量
            'max_files' => 30,
        ],
    ],
];

日志位置

默认在 runtime/log/ 目录下。

按日期分目录:

runtime/log/
├── 2026-10/
│   ├── 08.log
│   ├── 07.log
│   └── 06.log
└── 2026-09/
    └── ...

查看今天的错误

tail -f runtime/log/2026-10/08.log

四、常见500错误排查

1. 目录权限问题

runtime目录没权限写:

Permission denied

解决:

chmod -R 755 runtime
chown -R www:www runtime

2. 缺少扩展

Call to undefined function xxx()

解决: 装对应的PHP扩展。

3. 数据库连接失败

SQLSTATE[HY000] [1045] Access denied

检查: 数据库配置对不对。

.env 或 config/database.php:

'hostname' => '127.0.0.1',
'database' => '数据库名',
'username' => '用户名',
'password' => '密码',

4. 路由错误

Route not found

检查: 路由配置对不对。

5. 模板找不到

template not exists

检查: 模板文件路径对不对。

五、手动写日志

代码里可以手动写日志:

// 记录错误
Trace::error('这是一个错误信息');

// 记录警告
Trace::warning('这是一个警告');

// 记录信息
Trace::info('这是一个信息');

或者用助手函数:

trace('这是调试信息', 'debug');

六、查看PHP错误日志

除了ThinkPHP的日志,PHP本身也有错误日志。

查看PHP错误日志位置

php --ini | grep error_log

Nginx错误日志

tail -f /var/log/nginx/error.log

七、常见排查步骤

第一步:开debug看具体错误

改 .env:

APP_DEBUG = true

刷新页面,看具体报什么错。

第二步:看ThinkPHP日志

tail -f runtime/log/2026-10/08.log

第三步:看Nginx错误日志

tail -f /var/log/nginx/error.log

第四步:看PHP错误日志

tail -f /var/log/php-fpm/error.log

第五步:解决完改回debug

改完 .env:

APP_DEBUG = false

八、常见坑

坑1:开了debug还是不显示

缓存没清。

解决: 删除runtime目录下的缓存:

rm -rf runtime/cache/*

坑2:日志目录没权限

stream_socket_client(): unable to connect

runtime目录没写权限。

解决:

chmod -R 755 runtime

坑3:生产环境开着debug

危险!用户能看到你的敏感信息。

解决: 排查完立刻改回 APP_DEBUG = false。

坑4:日志太大占磁盘

日志越积越多,磁盘满了。

解决: 设 max_files,自动删除旧日志:

'max_files' => 30,

坑5:502和500搞混

  • 500:PHP代码错误
  • 502:PHP-FPM挂了

区分: 看Nginx错误日志。

总结

记住:

  1. 改 .env 的 APP_DEBUG = true 临时看错误
  2. 生产环境必须改回 false
  3. 日志在 runtime/log/ 目录
  4. 排查完记得清缓存
  5. 500是PHP代码问题,502是FPM问题

排查流程:

开debug → 看具体报错 → 看ThinkPHP日志 → 看Nginx日志 → 解决问题 → 关debug

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:31 

PHP FPM进程过多导致服务器卡顿优化

前言

PHP FPM进程太多,把服务器内存吃满,网站卡顿甚至502?本文讲怎么优化PHP FPM进程数。

一、为什么FPM进程多会卡

PHP FPM每处理一个请求,就开一个进程。

进程开多了:

  • 内存占满
  • CPU切换不过来
  • 服务器卡顿
  • 甚至OOM被杀

二、查看当前进程数

查看PHP FPM进程

ps aux | grep php-fpm

数一下有多少个进程。

看每个进程占多少内存

ps aux | grep php-fpm | awk '{print $6/1024 "MB"}'

算一下总内存

ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/1024 "MB"}'

三、FPM配置文件在哪

CentOS

/etc/php-fpm.d/www.conf

或者:

/etc/php/7.4/fpm/pool.d/www.conf

Ubuntu

/etc/php/7.4/fpm/pool.d/www.conf

四、进程管理模式

PHP FPM有三种进程管理模式:

1. static(静态)

固定进程数,启动就开这么多。

pm = static
pm.max_children = 50

2. dynamic(动态)

默认模式,按需开进程。

pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35

3. ondemand

没人访问的时候不开进程,来了请求才开。

pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 10s

五、怎么算合适的进程数

公式

pm.max_children = 可用内存 / 每个进程平均内存

例子

  • 服务器2G内存
  • 系统和其他服务占1G
  • 剩下1G给PHP
  • 每个PHP进程平均占40M
pm.max_children = 1024 / 40 ≈ 25

所以设25个进程差不多。

别设太大

设太大了,内存不够用,就会OOM。

六、推荐配置

小服务器(1G内存)

pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5

中服务器(2-4G内存)

pm = dynamic
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20

大服务器(8G以上)

pm = dynamic
pm.max_children = 80
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 40

七、其他优化参数

request_terminate_timeout

单个请求最长执行时间,超时就杀掉。

request_terminate_timeout = 30s

slow log

慢日志,看看哪个请求慢。

slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 10s

php_admin_value[memory_limit]

每个PHP进程的内存限制。

php_admin_value[memory_limit] = 128M

八、改完配置重载

systemctl reload php-fpm

或者:

systemctl restart php-fpm

九、验证效果

看进程数

ps aux | grep php-fpm | wc -l

看内存

free -h

看负载

top

十、常见坑

坑1:max_children设太大

比如2G内存设了100个进程,内存直接爆。

解决: 按上面的公式算,别拍脑袋。

坑2:改完配置不生效

忘了重载。

解决:

systemctl reload php-fpm

坑3:进程数一直涨

有慢请求,占着进程不放。

检查:

request_terminate_timeout = 30s

设个超时,超时就杀。

坑4:502 Bad Gateway

进程都忙不过来,Nginx连不上FPM。

解决: 加max_children,或者优化慢请求。

坑5:用了static模式

static固定进程数,不用就浪费内存。

推荐: 用dynamic或ondemand。

十一、排查步骤

第一步:看是不是FPM进程多

ps aux | grep php-fpm | wc -l

第二步:看内存够不够

free -h

第三步:看有没有慢请求

tail -f /var/log/php-fpm/slow.log

第四步:调整配置

改www.conf里的pm.max_children等参数。

第五步:重载验证

systemctl reload php-fpm

总结

记住:

  1. 进程数别设太大,按内存算
  2. 公式:可用内存 / 每个进程平均内存
  3. 推荐用dynamic模式
  4. 设个request_terminate_timeout,防慢请求占着进程
  5. 改完记得reload

推荐配置:

pm = dynamic
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
request_terminate_timeout = 30s

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:29 

Docker容器自动重启,restart=always参数

前言

服务器重启了,Docker容器没跟着起来?或者容器崩了,不自动重启?本文讲怎么配置自动重启。

一、restart参数

Docker用 --restart 参数控制容器的重启策略。

四种策略

策略 说明
no 不自动重启(默认)
on-failure 非正常退出时重启
always 总是自动重启
unless-stopped 除非手动停了,否则一直重启

二、always策略

用法

docker run -d --name nginx --restart=always nginx

什么时候会重启

  1. 容器崩了(非正常退出)
  2. Docker服务重启
  3. 服务器重启(只要Docker开机自启)

特点

  • 不管什么原因停了,都会自动起来
  • 服务器重启后也会自动启动

三、unless-stopped策略

用法

docker run -d --name nginx --restart=unless-stopped nginx

和always的区别

  • always:就算你手动停了,重启Docker后还是会起来
  • unless-stopped:如果你手动停了,重启Docker后就不起来了

推荐用unless-stopped,更灵活。

四、on-failure策略

用法

docker run -d --name nginx --restart=on-failure:5 nginx

说明

  • 只有非正常退出时才重启
  • :5 表示最多重启5次,超过就不重启了

什么时候用

比如批处理任务,失败了重试几次,一直失败就算了。

五、no策略(默认)

docker run -d --name nginx --restart=no nginx

不自动重启,停了就停了。

六、修改已有容器的重启策略

运行中的容器

docker update --restart=always 容器名

不用删容器,直接改。

例子

docker update --restart=always nginx
docker update --restart=unless-stopped mysql

七、Docker Compose里的配置

services:
  nginx:
    image: nginx
    restart: always

或者:

restart: unless-stopped

八、服务器开机自启

1. Docker服务开机自启

systemctl enable docker

2. 容器开机自启

只要容器设了 --restart=always 或 unless-stopped,Docker启动后容器就会自动启动。

验证

重启服务器试试:

reboot

重启完看看容器起来没:

docker ps

九、查看重启策略

docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' 容器名

输出:

always

十、常见坑

坑1:设了always,服务器重启后没起来

原因: Docker服务没设开机自启。

解决:

systemctl enable docker

坑2:手动停了,又自动起来了

用了 always 策略,手动停了也会起来。

解决: 想彻底停掉:

docker update --restart=no 容器名
docker stop 容器名

坑3:一直重启,死循环

容器启动就崩,崩了又重启,死循环。

检查:

docker logs 容器名

看看为什么启动失败,先把问题修了。

坑4:on-failure重启次数

--restart=on-failure:5

最多重启5次,超过就不重启了。

注意: 这个5是次数,不是间隔。

十一、推荐配置

生产环境推荐

docker run -d \
  --name nginx \
  --restart=unless-stopped \
  -p 80:80 \
  nginx
  • 服务器重启自动起来
  • 手动停了就不自动起
  • 灵活

数据库推荐

docker run -d \
  --name mysql \
  --restart=unless-stopped \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

总结

记住:

  1. --restart=always 总是自动重启
  2. --restart=unless-stopped 推荐,手动停了不自动起
  3. 改已有容器:docker update --restart=xxx 容器名
  4. 服务器开机自启要先 systemctl enable docker

最常用:

docker run -d --name nginx --restart=unless-stopped nginx

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:28 

Docker网络模式详解,bridge、host、none

前言

Docker容器怎么联网的?bridge、host、none这些网络模式啥区别?本文一次讲清楚。

一、Docker网络模式

Docker有5种网络模式:

  1. bridge:默认模式,桥接网络
  2. host:主机网络,和宿主机共用
  3. none:无网络
  4. container:和另一个容器共用网络
  5. 自定义网络:自己建的网络

二、bridge模式(默认)

是什么

Docker默认的网络模式。每个容器有自己的虚拟网络,通过docker0网桥和宿主机通信。

特点

  • 每个容器有自己的IP
  • 容器之间可以通过容器名互相访问
  • 外部要访问容器,要靠 -p 端口映射

用法

docker run -d --name nginx -p 80:80 nginx

没指定 --network,默认就是bridge。

容器之间通信

docker run -d --name mysql mysql:5.7
docker run -d --name web --link mysql nginx

或者用自定义网络,容器名直接解析:

docker network create mynet
docker run -d --name mysql --network mynet mysql:5.7
docker run -d --name web --network mynet nginx

web容器里直接用 mysql 这个主机名就能连数据库。

三、host模式

是什么

容器直接用宿主机的网络,没有自己的虚拟网卡。

特点

  • 没有独立IP,和宿主机共用
  • 不用 -p 映射,直接用宿主机的端口
  • 性能最好,因为少了一层网络转发
  • 安全性差点,容器直接暴露在宿主机网络上

用法

docker run -d --name nginx --network host nginx

注意

host模式下,-p 参数无效。容器用什么端口,直接就是宿主机的端口。

比如Nginx监听80,宿主机的80就被占了。

四、none模式

是什么

容器没有网络,完全断网。

特点

  • 没有网卡,没有IP
  • 只能通过localhost访问
  • 适合不需要联网的任务,比如批量处理文件

用法

docker run -d --name test --network none nginx

查看

docker exec test ip addr

只能看到lo回环网卡。

五、container模式

是什么

新创建的容器和一个已有的容器共用网络。

特点

  • 两个容器共用同一个网络命名空间
  • 用 127.0.0.1 就能互相访问
  • 适合一个应用拆成多个容器的场景

用法

# 先跑一个web容器
docker run -d --name web nginx

# 再跑一个sidecar容器,和web共用网络
docker run -d --name sidecar --network container:web busybox

sidecar容器里 127.0.0.1 就是web容器的localhost。

六、自定义网络

创建网络

docker network create mynet

使用网络

docker run -d --name mysql --network mynet mysql:5.7
docker run -d --name redis --network mynet redis

好处

  • 容器名自动解析,不用写IP
  • 网络隔离,不同网络的容器不能互相访问
  • 更灵活

查看网络

docker network ls

查看网络详情

docker network inspect mynet

七、常用命令

查看容器网络模式

docker inspect -f '{{.HostConfig.NetworkMode}}' 容器名

查看容器IP

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名

查看所有网络

docker network ls

删除网络

docker network rm mynet

八、常见场景选什么模式

场景1:跑Web服务,要外网访问

选bridge,用 -p 映射端口。

场景2:追求高性能,比如数据库

选host,少一层转发,性能好。

场景3:不需要联网的任务

选none,安全。

场景4:两个容器紧耦合

选container,共用网络。

场景5:多容器编排

选自定义网络,容器名自动解析。

九、常见坑

坑1:容器之间ping不通

bridge模式下,容器之间默认是通的,但如果用了none就不通。

检查: 网络模式是什么?

docker inspect 容器名 | grep NetworkMode

坑2:host模式下端口冲突

host模式直接用宿主机端口,端口被占了就起不来。

检查: 宿主机端口有没有被占。

坑3:容器名解析不了

默认bridge网络不支持容器名解析。

解决: 用自定义网络。

坑4:改了网络模式,要重新建容器

网络模式是容器创建时定的,不能改。

解决: 删掉容器重新跑。

总结

记住:

  1. bridge:默认,端口映射,常用
  2. host:共用宿主机网络,性能好
  3. none:断网,安全
  4. container:和别的容器共用网络
  5. 自定义网络:容器名自动解析,推荐

最常用:

docker network create mynet
docker run -d --name nginx --network mynet nginx

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:27 

Docker运行Nginx,挂载配置文件和网页

前言

用Docker跑Nginx,想改网站文件、改配置,每次进容器里改太麻烦。挂载出来就方便多了。本文讲怎么挂载网页和配置文件。

一、基本运行

最简单的跑法

docker run -d --name nginx -p 80:80 nginx

问题: 网站文件和配置都在容器里,改起来不方便。

二、挂载网页目录

完整命令

docker run -d \
  --name nginx \
  -p 80:80 \
  -v /data/www:/usr/share/nginx/html \
  nginx

解释

  • 宿主机 /data/www 目录
  • 容器里 /usr/share/nginx/html 目录

这样直接在宿主机改网页文件,刷新浏览器就生效了。

测试

在宿主机创建index.html:

echo "<h1>Hello Docker Nginx</h1>" > /data/www/index.html

浏览器访问:

http://服务器IP

就能看到了。

三、挂载配置文件

1. 先从容器里拷出默认配置

docker run -d --name nginx nginx
docker cp nginx:/etc/nginx/nginx.conf /data/nginx/nginx.conf
docker cp nginx:/etc/nginx/conf.d /data/nginx/conf.d
docker rm -f nginx

2. 挂载配置文件

docker run -d \
  --name nginx \
  -p 80:80 \
  -v /data/www:/usr/share/nginx/html \
  -v /data/nginx/nginx.conf:/etc/nginx/nginx.conf \
  -v /data/nginx/conf.d:/etc/nginx/conf.d \
  nginx

3. 改配置

直接在宿主机改 /data/nginx/conf.d/default.conf,改完重载:

docker exec nginx nginx -s reload

四、挂载日志

docker run -d \
  --name nginx \
  -p 80:80 \
  -v /data/www:/usr/share/nginx/html \
  -v /data/nginx/logs:/var/log/nginx \
  nginx

日志直接在宿主机看:

tail -f /data/nginx/logs/access.log

五、Docker Compose完整配置

version: '3'
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /data/www:/usr/share/nginx/html
      - /data/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /data/nginx/conf.d:/etc/nginx/conf.d
      - /data/nginx/logs:/var/log/nginx

启动:

docker-compose up -d

六、常用操作

重载配置

改了配置文件后:

docker exec nginx nginx -s reload

查看日志

docker logs nginx
# 实时看
docker logs -f nginx

进容器

docker exec -it nginx sh

七、常见坑

坑1:挂载后网站403

原因: 宿主机目录权限不对。

解决:

chmod -R 755 /data/www
chown -R nginx:nginx /data/www

坑2:改了配置不生效

原因: 没重载配置。

解决:

docker exec nginx nginx -s reload

坑3:挂载文件不存在,变成目录

-v /data/nginx.conf:/etc/nginx/nginx.conf

如果 /data/nginx.conf 不存在,Docker会自动建一个叫nginx.conf的目录。

解决: 先创建文件:

touch /data/nginx.conf

坑4:端口被占用

80端口被宿主机的Nginx占了。

解决: 停掉宿主机的Nginx,或者换端口。

坑5:改了代码不生效

如果用了CDN或者浏览器缓存,强刷一下(Ctrl+F5)。

八、多站点配置

在 /data/nginx/conf.d/ 下建多个配置文件:

/data/nginx/conf.d/
├── site1.conf
├── site2.conf
└── default.conf

site1.conf:

server {
    listen 80;
    server_name site1.com;
    root /data/www/site1;
    index index.html;
}

改完重载:

docker exec nginx nginx -s reload

九、HTTPS配置

挂载证书:

docker run -d \
  --name nginx \
  -p 80:80 \
  -p 443:443 \
  -v /data/www:/usr/share/nginx/html \
  -v /data/nginx/conf.d:/etc/nginx/conf.d \
  -v /data/ssl:/etc/nginx/ssl \
  nginx

配置文件里引用证书:

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    root /data/www;
}

总结

记住:

  1. 挂载网页目录:-v /data/www:/usr/share/nginx/html
  2. 挂载配置文件:-v /data/nginx/nginx.conf:/etc/nginx/nginx.conf
  3. 挂载日志:-v /data/nginx/logs:/var/log/nginx
  4. 改完配置重载:docker exec nginx nginx -s reload

最常用:

docker run -d --name nginx -p 80:80 \
  -v /data/www:/usr/share/nginx/html \
  -v /data/nginx/conf.d:/etc/nginx/conf.d \
  nginx

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:25 

Docker运行MySQL,数据持久化配置

前言

用Docker跑MySQL,删了容器数据就没了?那是没挂数据卷。本文讲怎么用Docker跑MySQL并持久化数据。

一、基本运行

最简单的跑法

docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:5.7

问题: 删了容器,数据库里的数据全没了。

二、加数据卷持久化

完整命令

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

解释

  • -p 3306:3306 端口映射
  • -e MYSQL_ROOT_PASSWORD=123456 设置root密码
  • -v /data/mysql:/var/lib/mysql 数据卷挂载
    • 宿主机 /data/mysql 目录
    • 容器里 /var/lib/mysql 目录

这样容器删了,数据还在宿主机上。

三、其他常用配置

1. 设置字符集

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  -v /data/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
  mysql:5.7 \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_unicode_ci

2. 创建数据库和用户

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=mydb \
  -e MYSQL_USER=myuser \
  -e MYSQL_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

3. 挂载配置文件

先在宿主机创建my.cnf:

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000

然后挂载:

docker run -d \
  --name mysql \
  -v /data/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

四、Docker Compose方式

version: '3'
services:
  mysql:
    image: mysql:5.7
    container_name: mysql
    restart: always
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
    volumes:
      - /data/mysql:/var/lib/mysql
      - /data/mysql/my.cnf:/etc/mysql/conf.d/my.cnf

启动:

docker-compose up -d

五、连接MySQL

进入容器连接

docker exec -it mysql mysql -uroot -p123456

从宿主机连接

mysql -h127.0.0.1 -P3306 -uroot -p123456

用Navicat等工具

  • 主机:服务器IP
  • 端口:3306
  • 用户名:root
  • 密码:123456

六、备份和恢复

备份数据库

docker exec mysql mysqldump -uroot -p123456 mydb > /data/backup/mydb.sql

恢复数据库

docker exec -i mysql mysql -uroot -p123456 mydb < /data/backup/mydb.sql

七、常见坑

坑1:密码不对

报错:

Access denied for user 'root'@'localhost'

检查: 环境变量 MYSQL_ROOT_PASSWORD 设了没?

坑2:权限问题

宿主机 /data/mysql 目录权限不对:

chmod -R 777 /data/mysql

坑3:端口被占用

3306端口被宿主机的MySQL占了:

Bind for 0.0.0.0:3306 failed: port is already allocated

解决: 换个端口:

-p 3307:3306

坑4:数据没持久化

没加 -v 挂载,删了容器数据就没了。

检查: 有没有 -v /data/mysql:/var/lib/mysql?

坑5:第一次启动慢

MySQL第一次启动要初始化数据,要等个十几秒。

等不及就先别连,过一会再试。

坑6:字符集不对,中文乱码

加参数:

--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

八、推荐配置

docker run -d \
  --name mysql \
  --restart=always \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=你的密码 \
  -v /data/mysql:/var/lib/mysql \
  -v /data/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
  mysql:5.7
  • --restart=always 开机自动启动
  • 数据挂载到 /data/mysql
  • 配置文件挂载

总结

记住:

  1. 一定要挂数据卷,不然删容器数据就没了
  2. 用 -e MYSQL_ROOT_PASSWORD 设密码
  3. 用 -p 3306:3306 映射端口
  4. 配置文件也可以挂载进去

最常用:

docker run -d --name mysql -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:24 

Dockerfile编写教程,构建自己的镜像

前言

想用Docker跑自己的项目,不能每次都手动装环境吧?写个Dockerfile,一键构建镜像。本文讲怎么写Dockerfile。

一、什么是Dockerfile

Dockerfile就是一个文本文件,里面写了怎么构建镜像的步骤。

就像写脚本一样,告诉Docker:

  1. 基于哪个基础镜像
  2. 装什么软件
  3. 复制什么文件
  4. 启动什么命令

二、基本结构

# 基于哪个镜像
FROM nginx:alpine

# 维护者信息
MAINTAINER yourname <email@xx.com>

# 工作目录
WORKDIR /usr/share/nginx/html

# 复制文件
COPY index.html .

# 暴露端口
EXPOSE 80

# 启动命令
CMD ["nginx", "-g", "daemon off;"]

三、常用指令

FROM:基础镜像

FROM nginx:alpine
FROM php:7.4-fpm
FROM mysql:5.7

WORKDIR:工作目录

WORKDIR /app

后面的命令都在这个目录里执行。

COPY:复制文件

COPY index.html /usr/share/nginx/html/
COPY ./src /app

RUN:构建时执行命令

RUN apt-get update && apt-get install -y vim

构建镜像的时候执行。

CMD:容器启动时执行

CMD ["nginx", "-g", "daemon off;"]

容器启动后跑的命令。

EXPOSE:暴露端口

EXPOSE 80
EXPOSE 443

只是声明,实际用的时候还要用-p映射。

ENV:设置环境变量

ENV MYSQL_ROOT_PASSWORD=123456
ENV APP_ENV=production

VOLUME:数据卷

VOLUME ["/var/lib/mysql"]

四、实战例子

例子1:部署静态网站

FROM nginx:alpine

WORKDIR /usr/share/nginx/html

COPY index.html .
COPY about.html .

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

例子2:部署PHP项目

FROM php:7.4-fpm

WORKDIR /var/www/html

COPY . .

RUN docker-php-ext-install mysqli pdo pdo_mysql

EXPOSE 9000

CMD ["php-fpm"]

例子3:Python应用

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 5000

CMD ["python", "app.py"]

五、构建镜像

构建命令

docker build -t mynginx:v1 .
  • -t 给镜像起名字和标签
  • . 当前目录找Dockerfile

查看构建过程

docker build --progress=plain -t mynginx:v1 .

指定Dockerfile路径

docker build -f docker/Dockerfile -t mynginx:v1 .

六、常用优化技巧

1. 用小镜像

用alpine版本,小很多:

FROM nginx:alpine

2. 合并RUN命令

RUN apt-get update && \
    apt-get install -y vim wget && \
    rm -rf /var/lib/apt/lists/*

不要写多个RUN,合并成一个,减少层数。

3. .dockerignore文件

不想复制的文件写在.dockerignore里:

.git
node_modules
*.log

4. 多层缓存

把不变的放前面,变的放后面。

# 先复制requirements.txt,装依赖(这个不常变)
COPY requirements.txt .
RUN pip install -r requirements.txt

# 再复制代码(这个常变)
COPY . .

七、常见坑

坑1:Dockerfile文件名不对

必须叫 Dockerfile,首字母大写,没有后缀。

坑2:构建时找不到文件

docker build -t mynginx:v1 .

最后那个点不能少,是构建上下文。

坑3:COPY路径不对

COPY index.html .

这个路径是相对于构建上下文的,不是Dockerfile所在目录。

坑4:CMD和RUN搞混

  • RUN 是构建镜像的时候跑
  • CMD 是容器启动的时候跑

坑5:镜像太大

用alpine版本,或者多阶段构建。

八、多阶段构建

# 构建阶段
FROM node:16 AS build
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

这样最终镜像很小,只有编译后的文件。

总结

记住:

  1. FROM 基础镜像
  2. WORKDIR 工作目录
  3. COPY 复制文件
  4. RUN 构建时执行
  5. CMD 启动时执行
  6. docker build -t 名字 . 构建镜像

最常用:

FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:23 

Docker查看容器资源占用,stats命令

前言

容器跑多了,哪个占CPU、哪个占内存,一眼看出来。本文讲Docker stats命令。

一、基本用法

查看所有运行中的容器

docker stats

输出:

CONTAINER ID   NAME       CPU %   MEM USAGE / LIMIT   MEM %   NET I/O   BLOCK I/O   PIDS
a1b2c3d4e5f6   mysql      2.50%   500MiB / 2GiB       25.0%   1MB/2MB   10MB/5MB    30
f7e8d9c0b1a2   nginx      0.10%   20MiB / 2GiB        1.0%    5MB/3MB   1MB/0B      5

列解释

  • CPU %:CPU使用率
  • MEM USAGE / LIMIT:内存使用 / 内存限制
  • MEM %:内存使用率
  • NET I/O:网络收发流量
  • BLOCK I/O:磁盘读写
  • PIDS:进程数

二、查看单个容器

docker stats 容器名

只看某一个容器。

三、只看一次,不刷新

默认是实时刷新的,按Ctrl+C退出。

只看一次:

docker stats --no-stream

四、自定义格式

只看CPU和内存

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

JSON格式输出

docker stats --format json --no-stream

五、其他常用命令

查看容器占用磁盘

docker system df

查看单个容器磁盘占用

docker ps -s

看SIZE列,就是容器写的磁盘大小。

查看容器网络IO

docker stats --no-stream

看NET I/O列。

六、常见场景

场景1:哪个容器占CPU高?

docker stats

看CPU %列,高的那个就是。

场景2:哪个容器占内存高?

看MEM USAGE列。

场景3:某个容器网络流量大

看NET I/O列。

七、限制容器资源

跑的时候可以限制CPU和内存:

限制内存512M

docker run -d --memory 512m nginx

限制CPU 0.5核

docker run -d --cpus 0.5 nginx

同时限制

docker run -d \
  --memory 512m \
  --cpus 0.5 \
  nginx

八、常见坑

坑1:stats一直刷新,退不出来

按Ctrl+C退出。

坑2:看不准CPU使用率

Docker stats显示的是CPU使用率百分比,多核的话可能超过100%(比如4核跑满就是400%)。

坑3:内存看的是驻留内存

MEM USAGE是容器实际用的内存,包括缓存。

坑4:容器挂了,stats看不到了

docker stats

只看运行中的容器,停止的看不到。

总结

记住:

  1. docker stats 看所有运行中的容器资源占用
  2. 看CPU、内存、网络、磁盘IO
  3. --no-stream 只看一次
  4. 跑容器时可以限制资源

最常用:

docker stats

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:22 

Docker删除所有停止的容器,一键清理

前言

用Docker久了,一堆停止的容器占磁盘,一个个删太麻烦。本文讲怎么一键清理。

一、查看所有停止的容器

先看看有多少个停止的容器:

docker ps -a

看STATUS列,显示Exited的就是停止的。

只看停止的:

docker ps -a -f status=exited

二、删除所有停止的容器

一键清理

docker container prune

输出:

Total reclaimed space: 1.2GB

直接删掉所有停止的容器。

确认提示

默认会问你:

WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N]

输入y确认。

跳过确认

docker container prune -f

加个 -f 直接删,不问。

三、其他清理命令

删除所有没用的镜像

docker image prune

只删悬空镜像(dangling images),就是没有标签的。

删所有没用的镜像(包括没在用的)

docker image prune -a

清理所有没用的东西

docker system prune

包括:

  • 停止的容器
  • 没用的网络
  • 悬空镜像
  • 构建缓存

清理所有没用的东西(包括镜像)

docker system prune -a

连没在用的镜像也删了。

清理数据卷

docker volume prune

注意: 这个会删没用的数据卷,里面的数据也没了!

四、手动删除单个容器

如果只想删某一个:

docker rm 容器ID或名字

强制删除运行中的容器:

docker rm -f 容器ID或名字

五、批量删除停止的容器

方法1:用docker rm

docker rm $(docker ps -a -q -f status=exited)

解释:

  • docker ps -a -q 列出所有容器ID
  • -f status=exited 只筛停止的
  • docker rm 删掉

方法2:用docker container prune

docker container prune -f

推荐用这个,简单。

六、查看磁盘占用

清理之前看看占了多少:

docker system df

输出:

TYPE            TOTAL   ACTIVE  SIZE    RECLAIMABLE
Images          20      5       10GB    7GB (70%)
Containers      15      5       500MB   400MB (80%)
Local Volumes   8       3       2GB     1GB (50%)
Build Cache     -       -       1GB     800MB

看RECLAIMABLE列,就是可以回收的。

七、常见场景

场景1:测试完一堆容器,想全清了

docker container prune -f

场景2:磁盘满了,全面清理

docker system prune -a -f --volumes
  • -a 删所有没用的镜像
  • -f 不问
  • --volumes 连数据卷也删

注意: 这个很猛,数据卷也删了,重要数据先备份!

场景3:只清理镜像,不动容器

docker image prune -a -f

八、常见坑

坑1:删了数据卷,数据没了

docker system prune --volumes

加了 --volumes 会删没用的数据卷。

注意: 数据库的数据卷别删!

坑2:删了镜像,重新拉要很久

docker image prune -a

把所有没在用的镜像都删了,下次用还要重新拉。

建议: 常用镜像留着,别全删。

坑3:误删了运行中的容器?

docker container prune 只删停止的,运行中的删不了。

放心,安全。

坑4:删了容器,数据卷还在

docker rm 容器名

只删容器,数据卷还在。

想连数据卷一起删:

docker rm -v 容器名

九、定期清理脚本

写个脚本,定期清理:

#!/bin/bash
# 清理停止的容器
docker container prune -f
# 清理悬空镜像
docker image prune -f
# 清理没用的网络
docker network prune -f

加到crontab,每周跑一次:

0 3 * * 0 /home/clean-docker.sh

总结

记住:

  1. docker container prune 删所有停止的容器
  2. docker image prune -a 删所有没用的镜像
  3. docker system prune 全面清理
  4. 删数据卷要小心,别把数据库删了

最常用:

docker container prune -f

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:21 

Docker数据卷挂载,容器数据持久化

前言

删了Docker容器,数据全没了?那是因为你没挂数据卷。本文讲怎么用数据卷让容器数据持久化。

一、为什么需要数据卷

容器里的数据,容器删了就没了。

docker run -d --name mysql mysql:5.7
# 删了容器,数据就没了
docker rm -f mysql

解决: 把数据存到宿主机,容器删了数据还在。

二、三种数据持久化方式

1. 绑定挂载(bind mount)

把宿主机的目录直接挂到容器里。

docker run -v /宿主机路径:/容器路径 镜像名

例子:

docker run -d \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

特点: 简单直接,数据在你自己的目录里。

2. 命名卷(named volume)

Docker管理的数据卷。

docker volume create mydata
docker run -v mydata:/var/lib/mysql mysql:5.7

特点: Docker帮你管理位置,不用你关心存在哪。

3. 临时挂载(tmpfs)

存在内存里,重启就没。

docker run --tmpfs /tmp 镜像名

特点: 临时用,存敏感数据。

三、常用场景

场景1:MySQL数据持久化

docker run -d \
  --name mysql \
  -v /data/mysql:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:5.7

场景2:Nginx挂载网站文件

docker run -d \
  --name nginx \
  -v /data/www:/usr/share/nginx/html \
  -p 80:80 \
  nginx

场景3:挂载配置文件

docker run -d \
  -v /data/nginx/nginx.conf:/etc/nginx/nginx.conf \
  nginx

场景4:挂载日志

docker run -d \
  -v /data/logs/nginx:/var/log/nginx \
  nginx

四、Docker Compose里的挂载

services:
  mysql:
    image: mysql:5.7
    volumes:
      - /data/mysql:/var/lib/mysql
      # 或者用命名卷
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

五、常用命令

查看所有数据卷

docker volume ls

查看数据卷详情

docker volume inspect 数据卷名

删除没用的数据卷

docker volume prune

查看容器挂载

docker inspect -f '{{.Mounts}}' 容器名

六、只读挂载

只想让容器读,不让写:

docker run -v /data/www:/usr/share/nginx/html:ro nginx

后面加个 :ro 就是只读。

七、常见坑

坑1:改了配置文件不生效

挂了配置文件,改了宿主机的文件,重启容器还是旧的。

原因: 容器没真正重启。

解决:

docker restart 容器名

坑2:权限问题

挂载的目录,容器里的用户读不了。

原因: 宿主机目录权限不对。

解决:

chmod -R 777 /data/mysql

坑3:Windows/Mac路径问题

Windows路径写法不一样:

docker run -v D:\data:/var/lib/mysql mysql

Mac/Linux用:

docker run -v /Users/xxx/data:/var/lib/mysql mysql

坑4:挂载文件不存在

宿主机的文件不存在,Docker会自动建一个目录。

比如:

docker run -v /data/nginx.conf:/etc/nginx/nginx.conf nginx

如果 /data/nginx.conf 不存在,Docker会建一个叫 nginx.conf 的目录,容器里就挂错了。

解决: 先创建文件:

touch /data/nginx.conf

坑5:数据卷删了数据就没了

docker volume rm mydata

数据卷删了,里面的数据也没了。

解决: 重要数据定期备份。

八、最佳实践

  1. 数据库一定要挂载数据卷,不然容器删了数据就没了
  2. 配置文件挂载到宿主机,改起来方便
  3. 日志挂载到宿主机,方便查看和切割
  4. 网站文件挂载到宿主机,更新代码不用重新打镜像

总结

记住:

  1. -v 宿主机路径:容器路径 就是挂载
  2. 数据库一定要挂数据卷
  3. 删容器不删数据,因为数据在宿主机
  4. 挂载文件不存在会变成目录,要注意

最常用:

docker run -v /data/mysql:/var/lib/mysql mysql

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:19 

Docker端口映射,-p参数用法详解

前言

容器跑起来了,但外部访问不了,就是端口没映射好。本文讲Docker端口映射的各种用法。

一、什么是端口映射

Docker容器有自己的内部网络,外部要访问容器里的服务,得把宿主机的端口映射到容器的端口。

二、基本用法

格式

-p 宿主机端口:容器端口

例子

docker run -d -p 8080:80 nginx

意思:访问宿主机的8080端口,转发到容器的80端口。

浏览器访问

http://服务器IP:8080

三、各种写法

1. 指定宿主机端口和容器端口

-p 8080:80

2. 只指定容器端口,宿主机随机分配

docker run -d -P nginx

大写P,宿主机随机找个空闲端口。

3. 绑定指定IP

-p 127.0.0.1:8080:80

只允许本机访问,外网访问不了。

4. UDP端口

-p 53:53/udp

5. 映射多个端口

docker run -d \
  -p 80:80 \
  -p 443:443 \
  -p 2222:22 \
  nginx

四、查看端口映射

查看容器端口

docker port 容器名

输出:

80/tcp -> 0.0.0.0:8080

查看详细信息

docker ps

看PORTS列。

五、Docker Compose里的端口映射

services:
  nginx:
    image: nginx
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:8080:80"

六、常见场景

场景1:跑Nginx

docker run -d -p 80:80 --name mynginx nginx

场景2:跑MySQL

docker run -d -p 3306:3306 --name mysql \
  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

场景3:跑Redis

docker run -d -p 6379:6379 --name redis redis

场景4:多个容器跑同一个端口

比如跑两个Nginx,分别用不同端口:

docker run -d -p 8081:80 --name nginx1 nginx
docker run -d -p 8082:80 --name nginx2 nginx

七、端口被占用怎么办

报错

Bind for 0.0.0.0:80 failed: port is already allocated

查谁占用了80端口

netstat -tlnp | grep :80
# 或者
lsof -i:80

解决

  1. 换个端口:-p 8080:80
  2. 或者把占用端口的程序关掉

八、端口映射的坑

坑1:只映射了容器端口,没映射宿主机端口

docker run -d -p 80 nginx

这样宿主机随机分配端口,不好记。

坑2:防火墙没开

端口映射了,但防火墙挡了,外网还是访问不了。

解决: 开放端口:

firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reload

坑3:改不了端口映射

容器启动后,想改端口映射,改不了。

解决: 删掉容器重新跑。容器数据用卷挂载了就不怕丢。

坑4:绑定127.0.0.1后外网访问不了

-p 127.0.0.1:8080:80

这个只能本机访问,外网访问不了。

原因: 故意绑的本机。

解决: 改成 0.0.0.0:8080:80 或者直接 8080:80。

总结

记住:

  1. 基本格式:-p 宿主机端口:容器端口
  2. 多个端口就写多个 -p
  3. 端口被占用就换一个
  4. 防火墙也要开

常用:

docker run -d -p 80:80 --name nginx nginx

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:18 

Docker进入容器内部,exec和attach区别

前言

要进容器里改配置、看日志,用什么命令?docker exec和docker attach有什么区别?本文讲清楚。

一、docker exec(推荐)

作用

在运行中的容器里开一个新进程。

进入容器

docker exec -it 容器名 /bin/bash

或者:

docker exec -it 容器名 sh

参数说明

参数 说明
-i 保持交互
-t 分配终端
-it 合起来用

退出

输入 exit 或者按 Ctrl+D。

关键: 退出后容器还在运行,不受影响。

执行单个命令

不进交互模式,直接执行命令:

docker exec 容器名 ls /var/www/html
docker exec 容器名 cat /etc/nginx/nginx.conf
docker exec 容器名 php -v

二、docker attach

作用

连接到容器正在运行的主进程。

用法

docker attach 容器名

危险!

退出会导致容器停止!

按 Ctrl+C 退出后,容器就停了。

怎么安全退出

按 Ctrl+P 然后 Ctrl+Q,这样退出不会停容器。

三、exec和attach区别

对比 exec attach
原理 开新进程 连接主进程
退出后容器 继续运行 会停止
常用场景 进容器操作 看主进程输出
推荐度 推荐 不推荐

记住: 日常用exec,别用attach。

四、常见场景

场景1:进容器改配置

docker exec -it mynginx /bin/bash

场景2:在容器里执行PHP命令

docker exec myphp php artisan migrate

场景3:看容器里的日志文件

docker exec mynginx tail -f /var/log/nginx/access.log

场景4:容器里没bash

有些精简镜像没有bash,用sh:

docker exec -it 容器名 sh

场景5:以root用户进入

docker exec -u root -it 容器名 /bin/bash

五、如果容器没bash怎么办

用nsenter

# 先拿容器PID
PID=$(docker inspect --format "{{.State.Pid}}" 容器名)

# 进入
nsenter --target $PID --mount --uts --ipc --net --pid

或者用docker run

用同一个镜像开个交互式容器:

docker run -it --rm 镜像名 /bin/bash

六、常用命令汇总

命令 说明
docker exec -it 容器名 /bin/bash 进容器
docker exec 容器名 命令 执行命令
docker exec -u root -it 容器名 /bin/bash 以root进
docker attach 容器名 连接主进程(慎用)

常见坑

坑1:attach退出后容器停了

用attach进去,按Ctrl+C退出,容器就停了。

解决: 用exec,或者Ctrl+P Ctrl+Q退出。

坑2:进容器报/ bin/bash not found

镜像里没有bash。

解决: 用sh:

docker exec -it 容器名 sh

坑3:进容器报permission denied

不是root用户,权限不够。

解决: 加 -u root:

docker exec -u root -it 容器名 /bin/bash

坑4:exec进不去,说容器没运行

容器停了,exec进不去。

解决: 先启动容器:

docker start 容器名
docker exec -it 容器名 /bin/bash

坑5:多个终端连同一个容器

exec可以开多个终端,互不影响。

这是exec比attach好的地方,attach只能连一个。

总结

记住两点:

  1. 日常用 docker exec -it 容器名 /bin/bash
  2. 别用attach,退出会把容器搞停

补充: 精简镜像没有bash,用sh代替。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:17 

Docker查看容器IP,docker inspect命令

前言

容器之间要互相通信,得知道容器的IP。本文讲怎么用docker inspect查看容器IP和详细信息。

一、查看容器IP

最简单的方法

docker inspect 容器名

输出一大串JSON,找IPAddress字段。

只看IP

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名

直接输出IP地址,比如:172.17.0.2

查看所有运行中容器的IP

docker ps -q | xargs docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

输出:

/mynginx - 172.17.0.2
/mymysql - 172.17.0.3
/myphp - 172.17.0.4

二、docker inspect常用用法

查看容器挂载

docker inspect -f '{{.Mounts}}' 容器名

查看容器启动命令

docker inspect -f '{{.Config.Cmd}}' 容器名

查看容器环境变量

docker inspect -f '{{.Config.Env}}' 容器名

查看容器端口映射

docker inspect -f '{{.NetworkSettings.Ports}}' 容器名

查看容器网络

docker inspect -f '{{.NetworkSettings.Networks}}' 容器名

查看容器状态

docker inspect -f '{{.State.Status}}' 容器名

查看容器启动时间

docker inspect -f '{{.State.StartedAt}}' 容器名

查看容器日志路径

docker inspect -f '{{.LogPath}}' 容器名

三、查看镜像信息

查看镜像详细信息

docker inspect 镜像名

查看镜像作者

docker inspect -f '{{.Author}}' nginx

查看镜像默认启动命令

docker inspect -f '{{.Config.Cmd}}' nginx

四、容器之间怎么通信

用容器名通信(推荐)

Docker默认网络里,容器可以用容器名互相访问:

# 在nginx容器里ping mysql
docker exec mynginx ping mysql

不用记IP,直接用容器名就行。

用自定义网络

# 创建网络
docker network create mynet

# 容器加入网络
docker run --name mysql --network mynet -d mysql:5.7
docker run --name nginx --network mynet -d nginx

五、常见场景

场景1:Nginx反向代理连PHP

nginx配置里:

fastcgi_pass php:9000;

这里的php就是容器名,Docker会自动解析成容器IP。

场景2:PHP连MySQL

<?php
$pdo = new PDO('mysql:host=mysql;dbname=mydb', 'root', '123456');
?>

host填mysql,就是MySQL容器的名字。

六、容器IP会变吗?

会的。

容器重启后,IP可能会变。所以不要硬编码IP,用容器名通信最稳。

如果要固定IP: 用自定义网络,指定静态IP。

# docker-compose.yml
services:
  mysql:
    networks:
      mynet:
        ipv4_address: 172.20.0.10

常见坑

坑1:inspect输出太多,找不到IP

解决: 用 -f 参数过滤,只看你要的字段。

坑2:容器停了就看不到IP

容器停止状态下,IPAddress是空的。

解决: 先启动容器:docker start 容器名

坑3:不同网络的容器互相ping不通

原因: 容器在不同网络里。

解决: 把它们加到同一个网络:

docker network connect mynet 容器名

坑4:用容器名ping不通

原因: 不是默认网络,或者Docker版本太老。

解决: 用自定义网络,或者直接用IP。

坑5:host网络模式看不到IP

用 --network=host 启动的容器,IP和宿主机一样。

解决: 不用查了,就是宿主机的IP。

总结

常用命令:

命令 说明
docker inspect 容器名 看容器全部信息
docker inspect -f '{{.NetworkSettings.IPAddress}}' 容器名 只看IP
docker inspect -f '{{.Mounts}}' 容器名 看挂载
docker inspect -f '{{.Config.Env}}' 容器名 看环境变量

记住: 容器之间用容器名通信,别硬编码IP,重启就变了。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:15 

Docker容器内时间和宿主机不一致修复

前言

Docker容器里的时间和宿主机差了8个小时,日志时间不对,定时任务也跑偏。本文讲怎么修复容器时区问题。

一、问题现象

# 宿主机时间
date
# 输出:Thu Oct  8 08:14:01 CST 2026

# 容器里时间
docker exec 容器名 date
# 输出:Thu Oct  8 00:14:01 UTC 2026

差了8个小时,因为容器默认用UTC时区。

二、查看容器时区

docker exec 容器名 date

或者:

docker exec 容器名 cat /etc/timezone

三、修复方法

方法1:挂载时区文件(推荐)

启动容器时挂载宿主机的时区文件:

docker run -v /etc/localtime:/etc/localtime:ro 镜像名

这样容器就和宿主机用同一个时区了。

方法2:设置环境变量

docker run -e TZ=Asia/Shanghai 镜像名

方法3:进容器手动改

# 进容器
docker exec -it 容器名 bash

# 装时区数据
apt-get update && apt-get install -y tzdata

# 设置时区
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone

# 验证
date

方法4:Docker Compose设置

在docker-compose.yml里加:

services:
  app:
    image: xxx
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /etc/localtime:/etc/localtime:ro

四、PHP容器额外设置

PHP的时区和系统时区是两回事,还要改php.ini:

[Date]
date.timezone = Asia/Shanghai

或者在代码里设:

<?php
date_default_timezone_set('Asia/Shanghai');
?>

五、MySQL容器额外设置

MySQL的时区也要改:

-- 查看当前时区
SELECT @@global.time_zone;

-- 设置为东八区
SET GLOBAL time_zone = '+8:00';

永久生效改my.cnf:

[mysqld]
default-time-zone = '+8:00'

六、验证

# 宿主机时间
date

# 容器时间
docker exec 容器名 date

两个时间一致就对了。

常见坑

坑1:改了时区不生效

改了时区文件,重启容器又变回UTC了。

原因: 容器重启后文件系统重置了。

解决: 用挂载的方式,不要进容器改。

坑2:只改了系统时区,PHP时区不对

系统时间对了,但PHP date()还是错的。

原因: PHP有自己的时区设置。

解决: 改php.ini的date.timezone。

坑3:日志时间不对

日志里的时间还是UTC。

原因: 程序启动时读的时区,改了时区要重启程序。

解决: 重启容器或重启程序。

坑4:Windows/Mac Docker Desktop

Windows和Mac上Docker Desktop的时区处理不一样。

解决: 直接用TZ环境变量最稳。

坑5:时区列表找不到Asia/Shanghai

原因: 容器里没装tzdata。

解决: 先装tzdata:

apt-get install -y tzdata

总结

最稳的方法:

  1. 挂载 /etc/localtime:/etc/localtime:ro
  2. 加环境变量 TZ=Asia/Shanghai
  3. PHP改 date.timezone
  4. MySQL改 default-time-zone

记住: 时区不一致是Docker最常见的问题之一,部署完第一件事先验证时间。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:14 

Docker Compose部署LNMP环境完整示例

前言

手动装Nginx、PHP、MySQL太麻烦,用Docker Compose一键部署LNMP环境,几分钟搞定。

一、项目结构

lnmp/
├── docker-compose.yml
├── nginx/
│   ├── conf.d/
│   │   └── default.conf
│   └── nginx.conf
├── php/
│   └── php.ini
├── mysql/
│   └── my.cnf
└── www/
    └── index.php

二、docker-compose.yml

version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: lnmp_nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./www:/var/www/html
    depends_on:
      - php
    networks:
      - lnmp

  php:
    image: php:fpm
    container_name: lnmp_php
    volumes:
      - ./www:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - lnmp

  mysql:
    image: mysql:5.7
    container_name: lnmp_mysql
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
    networks:
      - lnmp

networks:
  lnmp:
    driver: bridge

三、Nginx配置

nginx/conf.d/default.conf

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

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /var/www/html$fastcgi_script_name;
        include        fastcgi_params;
    }
}

四、PHP配置

php/php.ini

[PHP]
date.timezone = Asia/Shanghai
upload_max_filesize = 20M
post_max_size = 20M
memory_limit = 128M

五、MySQL配置

mysql/my.cnf

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

六、测试文件

www/index.php

<?php
phpinfo();
?>

七、启动

cd lnmp
docker-compose up -d

查看状态

docker-compose ps

访问

http://localhost

能看到phpinfo页面就是成功了。

八、常用命令

命令 说明
docker-compose up -d 启动
docker-compose down 停止并删除容器
docker-compose restart 重启
docker-compose logs -f 看日志
docker-compose exec php bash 进PHP容器
docker-compose exec mysql bash 进MySQL容器

九、连接MySQL

从宿主机连接

mysql -h 127.0.0.1 -P 3306 -u root -p123456

从PHP连接

<?php
$pdo = new PDO('mysql:host=mysql;dbname=mydb', 'root', '123456');
echo "连接成功";
?>

注意:host填mysql,不是localhost,是docker-compose.yml里的服务名。

十、安装PHP扩展

进入PHP容器

docker-compose exec php bash

安装pdo_mysql

docker-php-ext-install pdo_mysql
docker-php-ext-install mysqli

重启PHP

docker-compose restart php

常见坑

坑1:Nginx报502

502 Bad Gateway,原因是PHP容器没启动,或者地址不对。

解决:确认PHP容器在运行,fastcgi_pass填服务名php:9000。

坑2:MySQL启动失败

原因:数据目录权限不对,或者密码没设。

解决:确认MYSQL_ROOT_PASSWORD设了,数据目录权限改成777。

坑3:文件修改不生效

改了www里的文件,浏览器刷新没变。

解决:确认volumes挂载了./www:/var/www/html。

坑4:端口被占用

port is already allocated,原因是宿主机80端口被占了。

解决:改端口,比如改成8080。

坑5:数据丢失

删了容器,MySQL数据没了。

解决:一定要挂载数据卷,./mysql/data:/var/lib/mysql。

总结

部署五步:建项目目录结构,写docker-compose.yml,写配置文件,docker-compose up -d启动,浏览器访问测试。

记住:数据一定要挂载,不然删容器数据就没了。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:13