☰
Docker从入门到实践:镜像、容器、数据卷与Compose编排全解析
2026/9/26 4:34:24 网站建设 项目流程

1. 镜像管理命令,先把手里的“原材料”管明白

用 Docker 干活,本质上是围绕两样东西转:镜像和容器。镜像可以理解成只读的模板,容器是模板跑起来之后的实例。我刚开始接触 Docker 的时候,光对着docker pull和docker images这两个命令翻来覆去地用,后来才慢慢把整套镜像管理的操作捋顺。这一节先把这块讲透,因为后面的所有容器操作都建立在镜像之上。

1.1 镜像拉取与查看

docker pull是最常用的命令,没有之一。比如我想部署 MySQL 8.0,最简单的做法就是:

docker pull mysql:8.0

这里有个小细节:默认不加 tag 会用latest,但生产环境我强烈建议指定具体的版本号,比如mysql:8.0.31。原因很简单,latest是会变的,今天拉下来一个版本,半年后再拉可能就不一样了。你写部署文档、写脚本的时候,如果锁定了版本号,其他人复现出来的环境才完全一致。我自己就吃过亏,早期写了一个用latest的部署脚本,三个月后跑了一次,数据库行为变了,排查半天才发现是镜像版本悄悄升级了。

查看本地已存在的镜像用:

docker images

如果只想看某个镜像,可以加过滤条件:

docker images | grep mysql docker images --filter "dangling=true"

第二个命令会列出所有悬空镜像,也就是那些标签被覆盖之后残留的中间镜像块。这类镜像特别占磁盘,后面讲清理的时候会提到。还有一个小技巧,docker images默认只显示顶层镜像,如果你想看某个镜像的历史层,可以用docker history:

docker history mysql:8.0

这个命令在排查镜像体积问题的时候很好用,能看到每一层占了多大空间。

1.2 镜像删除与改名

镜像删错了或者想清理不用的镜像,用docker rmi:

docker rmi mysql:8.0 docker rmi -f mysql:8.0

-f是强制删除。但是注意,如果镜像已经被某个容器使用了,直接删会报错,提示镜像正在被使用。这时候要么先删容器,要么用-f强删,不过强删之后原容器可能就无法正常工作了。

给镜像打标签用docker tag:

docker tag mysql:8.0 myregistry.com/library/mysql:8.0

这个命令在本地调试镜像推送逻辑时很常用。你想推送到自己的私有仓库,先打一个带仓库地址的 tag,然后docker push推上去。比如:

docker tag myapp:1.0 registry.example.com/myapp:1.0 docker push registry.example.com/myapp:1.0

这里要注意,如果镜像仓库是需要登录的,先执行docker login。

1.3 镜像构建

构建镜像是从零做一个自定义环境的核心操作。最基础的构建命令:

docker build -t myapp:1.0 .

-t指定新镜像的名字和标签,最后的.是构建上下文路径,也就是 Dockerfile 所在的目录。这里有一个经常让新手困惑的点:Docker 构建时会把上下文目录里的所有文件都打包发送给 Docker 守护进程。如果你的目录里有一堆没用的缓存文件、日志文件,构建就会特别慢,而且镜像可能会变大。所以我在项目目录里一般都会放一个.dockerignore文件,语法和.gitignore类似:

node_modules .git *.log dist

指定 Dockerfile 路径时用-f:

docker build -f docker/Dockerfile.prod -t myapp:1.0 .

这个场景常见于同一套代码要打不同环境的镜像,比如开发环境一个 Dockerfile,生产环境一个 Dockerfile,放在不同目录里。

如果你只是想保存当前容器改动后的状态,可以用docker commit。但我必须说句实在话:正常情况下我不推荐这个命令。用 commit 生成的镜像没办法追踪每一层是怎么产生的,Dockerfile 的可复现性完全没有了。我见过有人把数据库容器跑了一阵之后直接 commit 一次拿来当备份,这个做法隐患很大,镜像里包含了运行时产生的所有状态,磁盘占用大,而且行为不可预测。正确做法永远是写 Dockerfile,用docker build构建出干净、可追溯的镜像。

1.4 镜像加速源配置

国内最常见的痛点就是镜像拉取慢。Docker Hub 的访问速度确实时好时坏,所以很多人会配置镜像加速源。Docker 守护进程配置文件通常在/etc/docker/daemon.json,Windows 上对应的是 Docker Desktop 的设置界面。配置格式如下:

{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }

改完之后重启 Docker 服务:

sudo systemctl daemon-reload sudo systemctl restart docker

需要注意,不同的加速源可用性随时可能变化,我一般是多配几个备用的,拉取失败时 Docker 会自动尝试下一个。另外,加速源只对公开镜像有效,你自己推到私有仓库里的镜像,或者公司的私有仓库镜像,不受加速源影响。

2. 容器生命周期操作,让容器按照你的节奏运转

镜像只是静态的模板,真正跑起来要靠容器。容器生命周期这一块,是我在日常排障时用最多的地方。一个容器从启动到结束,涉及的命令看起来不多,但参数组合起来非常灵活。

2.1 创建并运行容器

docker run是创建容器并启动的命令。用一个实际例子来看看常用的参数组合:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -v /opt/mysql-data:/var/lib/mysql \ --restart always \ mysql:8.0

这一条命令包含了几个关键参数,分开解释:

  • -d:后台运行容器,当前终端不会阻塞。
  • --name:给容器起名字,后续操作直接通过名字引用,比记容器 ID 方便太多。
  • -p:端口映射,宿主机端口:容器端口。左边是外部访问的端口,右边是容器内监听的端口。
  • -e:向容器传递环境变量,MySQL 镜像通过这个变量初始化 root 密码。
  • -v:卷挂载,把宿主机目录映射到容器内目录。数据持久化全靠这个。
  • --restart:重启策略,always表示容器异常退出后会自动重启,非常适合常驻服务。

关于端口映射,我要多说一句。-p 3306:3306会把宿主机的 3306 端口暴露给所有网络接口,如果服务器有公网 IP,这点要格外小心。只希望本机访问的服务,建议写成-p 127.0.0.1:3306:3306。这个细节能帮你避免很多安全上的麻烦。

如果你想临时跑一个一次性容器做测试,不想让它在后台停留,可以用:

docker run --rm -it ubuntu:22.04 bash

--rm表示容器退出时自动删除,适合临时代码测试。-it是-i和-t的组合,-i保持标准输入打开,-t分配一个终端。这个组合在进行交互式操作时是固定的搭配,比如进入容器内部或者跑一个需要输入的脚本。

2.2 查看容器状态

查看运行中的容器:

docker ps

查看所有容器,包括已停止的:

docker ps -a

docker ps输出的信息里包含容器 ID、镜像、创建时间、状态、端口映射等。状态一栏常见的有Up、Exited、Restarting、Paused等,排障的时候看一眼状态就能大致判断问题方向。

只看某个容器的详细信息,用:

docker inspect mysql8

这个命令输出的 JSON 信息非常详细,包括网络配置、挂载卷、环境变量、重启策略等。排查问题的时候,docker inspect是我的第一选择。如果你只想看某一项,可以配合--format参数:

docker inspect --format='{{.State.Status}}' mysql8 docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql8

这个--format语法用的是 Go template,刚开始用会有点别扭,但熟练之后特别管用。比如想知道容器映射了几个端口,一条命令就出来。

2.3 启动、停止、重启、删除

容器已经存在的情况下:

docker start mysql8 docker stop mysql8 docker restart mysql8 docker pause mysql8 docker unpause mysql8

stop会先给容器内的主进程发 SIGTERM 信号,等待一段时间后再发 SIGKILL。所以停止一个容器是需要时间窗口的,如果里面有任务正在写数据,强行 kill 可能会造成数据损坏。遇到这种场景,我一般会先docker stop,观察一下,实在停不下来再考虑后续手段。

删除容器:

docker rm mysql8 docker rm -f mysql8

-f可以强制删除正在运行的容器。实际操作中我遇到最多的情况是:

  • 端口被占用,需要删掉旧容器重新起一个新的;
  • 容器处于Exited状态,而且再也用不到了;
  • 想用同一套配置重新部署,干脆删除重建。

删除前确认一下这个容器是否还有需要保留的数据。如果挂载了宿主机目录或命名卷,容器删除后数据不会消失;如果是容器内直接写数据且没有挂载,删除容器等于数据全没。这个坑请一定记住。

2.4 网络相关参数补充

容器启动时不指定网络,默认会连接到bridge桥接网络。手动指定网络可以用:

docker run -d --network my-network nginx:latest

同一个自定义网络下的容器可以直接通过容器名互相访问,比如在一个部署了 Redis 和应用的 compose 网络里,应用连接 Redis 的地址可以直接写redis:6379。这种容器间通信方式比用 IP 靠谱得多,因为容器重启后 IP 是可能变化的。

3. 容器交互与排障,进去了才能看到真相

容器跑起来只是第一步,真正的挑战在于怎么跟容器里的进程对话、怎么看日志、怎么把问题捞出来。这一节讲的都是日常排障的高频操作。

3.1 进入容器内部

最常用的进入容器命令:

docker exec -it mysql8 bash

如果容器里没有 bash,可以用 sh。有的精简镜像(比如 alpine 系列)连 bash 都没有,直接进 sh。

要注意exec和attach的区别。exec是启动一个新的进程进入容器,执行完命令后退出,不会影响容器主进程。attach是挂接到容器主进程的输入输出上,如果主进程不支持交互,attach 之后可能直接卡住。所以我的习惯是:诊断问题用exec,尽量不要用attach。

非交互式地执行单条命令也很常见:

docker exec mysql8 mysql -uroot -p123456 -e "SHOW DATABASES;"

这种写法在写自动化脚本时会非常有用,不需要人工进入容器再执行 SQL。

3.2 查看日志

容器日志是排障的核心信息来源:

docker logs mysql8 docker logs -f mysql8 docker logs --tail 200 mysql8 docker logs --since 30m mysql8

-f是持续跟踪输出,相当于tail -f。--tail控制只看最后多少行,这个在日志量大的场景下非常实用。--since可以只看某个时间段之后的日志,适合定位刚才发生的异常。

实际使用中,我遇到最多的情况是容器启动后立刻退出,这时候日志就尤其重要:

docker logs --tail 50 mysql8

如果镜像是通过 entrypoint 脚本启动的,日志里会包含脚本执行的输出,基本能判断是环境变量缺失、端口被占用还是配置文件有错。

3.3 文件复制与进程查看

宿主机和容器之间互相传文件:

docker cp /opt/test.txt mysql8:/tmp/test.txt docker cp mysql8:/tmp/test.txt /opt/test.txt

这个命令不需要容器处于运行状态,即使容器是Exited状态,只要容器还在就能复制文件。我在处理数据库容器崩溃时,常用这个命令把容器里的备份文件捞出来。

查看容器内进程:

docker top mysql8

查看资源占用:

docker stats

docker stats会实时显示所有运行中容器的 CPU、内存、网络和磁盘 I/O 使用情况。排查性能瓶颈的时候,先跑一下这个命令,基本能定位是哪个容器在吃资源。不过要注意,docker stats输出的内存值包含了页面缓存,实际物理内存占用可能比显示的低。

3.4 端口映射查看

有时候容器起来了,但是从外部访问不通,首先要确认端口映射。用:

docker port mysql8

输出会告诉你容器端口映射到了宿主机的哪个端口。如果输出为空,说明这个容器没有做端口映射。这个问题新手很容易忽略:容器内部服务明明正常,但外部访问不了,结果一看根本忘了加-p参数。

再配合前面讲的docker inspect --format='{{.NetworkSettings.Ports}}' mysql8,能看出端口绑定的具体 IP。如果你用了127.0.0.1:3306:3306这种绑定方式,外部机器当然访问不到,这不是故障,是安全策略。

4. 网络与数据卷,连通是基础,持久化是底线

容器是临时性的,删了重建很正常。但数据不能跟着消失,网络也不能各玩各的。所以网络和数据卷这两块必须单独拎出来了解。

4.1 Docker 网络模型与实际操作

Docker 默认有三种网络驱动:bridge、host、none。bridge是默认网络,容器通过虚拟网桥互相通信;host让容器直接使用宿主机网络栈,不会有 NAT 转换,性能好但隔离性弱;none没有网络,适合特殊场景。

自定义网络推荐用bridge驱动:

docker network create my-network docker network ls docker network inspect my-network docker network connect my-network mysql8 docker network disconnect my-network mysql8

docker network inspect能查看这个网络下挂了多少容器、各自的 IP 和别名。排查容器间通信问题第一步就是看它们是否在同一个网络里。

容器默认有两个网络接口:一个是docker0桥接网络,一个是你在docker run时通过--network指定的网络。如果容器启动时没指定,它就在docker0这个默认桥接网络里。两个不同的自定义网络里的容器默认是不相通的,这点需要记住。

4.2 网络不通排查思路

我遇到过很多次“docker 网络不通”的问题。常见的排查路径是:

  1. 先确认容器是否在同一个网络里:docker network inspect 网络名。
  2. 测试连通性:进入容器内ping另一台容器的 IP 或容器名。
  3. 检查宿主机防火墙:如果容器网络没问题,检查宿主机 iptables 规则或者防火墙放行策略。
  4. 确认端口映射是否正确:docker port 容器名。
  5. 查看容器内进程是否真的在监听端口:docker exec -it 容器名 netstat -tlnp或ss -tlnp。

如果是 Docker Desktop 环境,跨容器访问还要注意 WSL2 的虚拟网络和 Windows 防火墙之间的互动。经常是 Windows 防火墙弹窗拦截了 docker 相关的网络,放行之后问题就消失了。

4.3 数据卷管理

数据卷是 Docker 中数据持久化的核心机制。理解三种挂载方式:

  • 绑定挂载(bind mount):直接把宿主机目录挂进容器,比如-v /opt/mysql-data:/var/lib/mysql。
  • 命名卷(named volume):由 Docker 管理,存储目录在宿主机上但具体路径由 Docker 控制,比如-v mysql-data:/var/lib/mysql。
  • 匿名卷(anonymous volume):没有名字的卷,容器删除后卷往往还会残留。

命名卷的推荐使用方式:

docker volume create mysql-data docker run -d --name mysql8 -v mysql-data:/var/lib/mysql mysql:8.0

查看和管理卷:

docker volume ls docker volume inspect mysql-data docker volume rm mysql-data

绑定挂载和命名卷各有适用场景。配置文件、日志文件适合绑定挂载,因为你需要直接在宿主机上修改;数据库文件、缓存文件适合命名卷,因为数据目录的具体位置你不需要关心,Docker 帮你管好。

关于数据卷,我最想啰嗦一句的是:在你删除容器之前,先确认一下数据到底有没有落在持久化存储里。我见过不止一次,因为图省事没挂卷,容器一删,整个数据库数据全没了。要验证的话,可以docker inspect看Mounts字段:

docker inspect --format='{{json .Mounts}}' mysql8

如果Mounts为空,那说明容器没有任何挂载,数据全在容器可写层,直接删容器等于删数据。

5. Docker Compose,多容器编排的核心工具

单个容器好办,多容器一多,手打docker run就完全不是办法。Docker Compose 的价值就在于把多个容器的配置统一写在一个docker-compose.yml文件里,一条命令完成启动、停止、重建。

5.1 Compose 文件结构与基本命令

一个最简示例:

services: web: image: nginx:latest ports: - "8080:80" redis: image: redis:7.0

启动:

docker compose up -d

-d仍然是后台运行。如果不加-d,Compose 会以前台模式运行,终端会显示所有服务的日志,这在调试时很有用,调试完按 Ctrl+C 停止。

停止并删除容器:

docker compose down

down默认会删除所有由 Compose 启动的容器,但不会删除卷。如果你想连数据卷一起删:

docker compose down -v

这个操作要非常慎重,-v会连命名卷一起删掉。我建议除非你想彻底重置环境,否则不要轻易加-v。

查看 Compose 管理的服务状态:

docker compose ps docker compose logs -f docker compose exec web bash

5.2 用 Compose 快速部署 Redis 主从

这个场景我在本地开发环境里反复使用。假设要搭一套 Redis 一主一从,最简单的docker-compose.yml长这样:

services: redis-master: image: redis:7.0 container_name: redis-master command: ["redis-server", "--requirepass", "123456"] ports: - "6379:6379" volumes: - redis-master-data:/data networks: - redis-net redis-slave: image: redis:7.0 container_name: redis-slave command: ["redis-server", "--slaveof", "redis-master", "6379", "--masterauth", "123456"] depends_on: - redis-master ports: - "6380:6379" volumes: - redis-slave-data:/data networks: - redis-net volumes: redis-master-data: redis-slave-data: networks: redis-net:

这里面的关键点:

  • 从节点通过--slaveof redis-master 6379指向主节点,因为在同一个自定义网络里,容器之间可以直接通过容器名互访。
  • depends_on只控制启动顺序,不保证主节点已经可用。如果你的从节点同步失败,多半是主节点还没完全启动,可以等几秒再重启从节点。
  • 通过redis-net定义了独立的桥接网络,外部访问需要通过端口映射。

启动和验证:

docker compose up -d docker compose ps docker exec -it redis-slave redis-cli -p 6379 -a 123456 info replication

看到role:slave并且主从连接状态正常,就说明主从关系建立成功了。

5.3 用 Compose 部署 MySQL 8.0

MySQL 8.0 的 Compose 配置需要注意字符集和认证方式这几个点:

services: mysql: image: mysql:8.0 container_name: mysql8 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "root123" MYSQL_DATABASE: "appdb" TZ: "Asia/Shanghai" command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci volumes: - mysql-data:/var/lib/mysql restart: always volumes: mysql-data:

启动:

docker compose up -d

验证:

docker exec -it mysql8 mysql -uroot -proot123 -e "SHOW VARIABLES LIKE '%character%';"

看到character_set_server是utf8mb4就说明字符集配置生效了。MySQL 8.0 默认的认证插件是caching_sha2_password,如果你用老版本客户端连接,可能会报认证失败,这个不是容器的问题,是客户端版本太老不支持新认证方式。

5.4 Compose 与 Docker Desktop 配合使用

在 Windows 上使用 Docker Desktop 时,Compose 命令同样可用。一个常见的坑是:修改了docker-compose.yml后没有重新构建镜像。如果你在文件里改了镜像的build配置,要用:

docker compose up -d --build

这样 Compose 会重新构建受影响的服务镜像。如果只是修改了环境变量或端口映射,直接docker compose up -d就行,Compose 会检测配置变化并重建对应容器。

6. 清理、权限与故障排查记录

最后这部分是压箱底的实操经验。前面讲的都是怎么把容器跑起来,这一节专门讲容器如何优雅地退场,以及遇到各种报错时的排查思路。

6.1 资源清理,给 Docker 减负

容器跑久了,镜像、卷、容器堆积,磁盘空间不知不觉就被吃满了。我常用的清理命令:

docker system df

这个命令会输出磁盘占用概览,分镜像、容器、卷、构建缓存几类。先看这个,再决定要清理什么。

清理悬空镜像:

docker image prune

清理停止状态的容器:

docker container prune

删除所有未使用的镜像、容器、网络、构建缓存:

docker system prune -a

-a会删除所有没有被容器使用的镜像,如果你只是想清理悬空镜像,就不要加-a。加--volumes会连卷一起删:

docker system prune -a --volumes

这个命令我很少用,因为它很可能误删你还想保留的数据卷。建议操作之前先docker volume ls看一眼。

6.2 Docker 权限错误怎么解决

Linux 下安装 Docker 后,直接跑docker ps经常会遇到权限错误,报错类似permission denied while trying to connect to the Docker daemon socket。这是因为当前用户不在docker用户组里。

解决方案一:当前命令临时加 sudo:

sudo docker ps

解决方案二:把用户加入 docker 组,然后重新登录:

sudo usermod -aG docker $USER newgrp docker

注意加入 docker 组之后需要重新登录会话才生效,有些环境下要重启机器。把用户加入 docker 组是常用的本地开发做法,权限上等同于 root,所以如果是在生产服务器上,请务必想清楚再执行。

另一种权限错误发生在 Docker Desktop 场景,Windows 下常见的是failed to connect to the docker api at npipe这类报错,一般意思是 Docker Desktop 的后台服务没有正常运行,或者当前终端会话没有权限连接管道。先看 Docker Desktop 的 Whale 图标状态,确认是运行状态再执行命令,不要盲改用户权限。

6.3 Docker 服务启动失败排查

Linux 下 Docker 服务启动失败,最常见的报错包括:

  • dockerd启动失败
  • 容器启动失败并退出

排查步骤:

systemctl status docker journalctl -u docker --no-pager | tail -50

常见原因有:

  • daemon.json 写错了 JSON 语法,导致服务启动失败。
  • 磁盘空间不足。
  • 与 selinux 或防火墙冲突。
  • Docker 版本不兼容当前系统内核。

6.4 Docker Desktop 的坑

Windows 环境用 Docker Desktop 最常见的问题集中在 WSL2 后端。安装好 Docker Desktop 之后,启动时可能会提示虚拟化支持未启用,这个一般要去 BIOS 里开启 CPU 虚拟化,并且在 Windows 功能里打开“适用于 Linux 的 Windows 子系统”和“虚拟机平台”。

如果docker ps报错cannot connect to the Docker daemon at unix:///var/run/docker.sock(Linux)或者npipe:////./pipe/docker_engine(Windows),多半是 Docker 服务没起来。WSL2 环境下还要检查当前 WSL 发行版是否被 Docker Desktop 正确设置,可以在 Docker Desktop 设置里查看 WSL 集成选项。

我自己踩过最坑的一次是:Windows 上 WSL2 内核版本太旧,Docker Desktop 启动后一切正常,但是跑容器就崩溃,而且报错信息不明确。最后是去 Windows Update 里更新了 WSL2 的内核组件才解决。如果你遇到 Docker Desktop 能启动但容器无法工作的情况,优先检查 WSL2 内核版本。

6.5 常见报错速查表

报错信息可能原因处理方式
port is already allocated宿主机端口被占用换一个宿主机端口,或先停掉占用端口的进程
Cannot connect to the Docker daemonDocker 服务未启动或不匹配Linux 检查 systemd 服务,Windows 检查 Docker Desktop 状态
docker: permission denied当前用户不在 docker 组加入 docker 组或临时用 sudo
No space left on device磁盘空间不足docker system prune清理镜像和缓存
exec: "bash": executable file not found容器镜像里没有 bash改用sh或其他可用 shell
Error response from daemon: driver failed programming external connectivity端口映射配置冲突重启 Docker 服务或删掉冲突容器
failed to start service unit(Linux)Docker 服务启动失败查看/etc/docker/daemon.json语法,确认磁盘空间

实际排障时,我有一套固定的动作:先docker ps -a看容器状态,再docker logs --tail 100 容器名看具体日志,如果日志不明确,再docker inspect看配置。这三步能解决绝大多数问题,比到处搜报错信息要高效得多。

7. 最后分享几个我一直在用的习惯

说了这么多命令,最后聊点使用习惯上的经验,这些习惯帮我少踩了不少坑。

第一个习惯是:用完即查,别硬记。Docker 命令非常多,我到现在都还经常docker run --help去查某个参数的具体写法。关键是理解各个参数之间的关系,比如-d和-it通常不会同时用,--rm和-d搭配时容器退出会自动清理,而--restart always和--rm是冲突的配置。理解了这些,查文档就会快很多。

第二个习惯是:写一个常用的 compose 模板库。比如 MySQL、Redis、Nginx、PostgreSQL,每种服务我都存了一份可复用的docker-compose.yml模板。新项目要用,直接改改配置就能跑,效率很高,而且能保证每次部署的基础配置是经过验证的。

第三个习惯是:不要随便删数据卷。清理容器时宁可多保留一个卷,也不要误删数据。容器可以重建,数据丢了就真的没了。

第四个习惯是:定期检查磁盘占用。我会在每周五快下班的时候跑一次docker system df,顺手清理悬空镜像和停止状态的容器。这个习惯让我很少遇到“磁盘被 Docker 打满”的尴尬。

Docker 的命令体系本身不算复杂,真正有价值的都是那些从实际踩坑中沉淀下来的细节。希望这篇文章里讲到的那些小知识、小习惯,能在你后面使用 Docker 的时候帮上一点忙。如果你平时也遇到什么奇怪的 Docker 问题,多试几遍排查思路,多看看日志,很多问题没有想象中那么神秘。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询