开发者必备:Docker 常用命令指南 – wiki基地


开发者必备:Docker 常用命令指南

在现代软件开发的领域,Docker 已经不再是一个可选项,而是一个几乎必备的工具。它以前所未有的方式简化了应用程序的构建、打包、分发和运行过程。对于开发者而言,掌握 Docker 的常用命令是提高效率、确保环境一致性和简化协作的关键。

本文将作为一份详尽的指南,深入探讨开发者日常工作中会频繁使用的 Docker 命令,帮助你从入门到熟练,充分利用 Docker 的强大能力。

为什么开发者需要 Docker?

在深入命令之前,我们先快速回顾一下 Docker 为什么对开发者如此重要:

  1. 环境一致性: “在我的机器上能跑!” 这句话在有了 Docker 后将成为历史。Docker 容器包含了应用程序及其所有依赖项(库、系统工具、代码、运行时等),无论在开发者的笔记本上、测试服务器上还是生产环境中,同一个 Docker 镜像都能以相同的方式运行。
  2. 隔离性: 每个容器都是一个独立的运行环境,它们之间相互隔离。这意味着你可以在同一台机器上运行多个不同版本或不同技术的应用程序,而不会产生冲突。
  3. 快速搭建开发环境: 无论是数据库、缓存还是消息队列,只需简单的命令即可拉取预构建的镜像并运行容器,大大缩短了环境配置的时间。
  4. 简化依赖管理: 应用程序的依赖都被打包在镜像中,开发者无需在本地安装和管理复杂的系统级依赖。
  5. 加速部署: Docker 镜像提供了一种标准化的打包格式,使得应用程序可以快速、可靠地从开发环境迁移到任何支持 Docker 的平台。
  6. 版本控制: Docker 镜像可以通过标签进行版本控制,你可以轻松回滚到旧版本或测试新版本。

掌握 Docker 命令,就是掌握了通往这些优势的钥匙。

Docker 基础概念回顾

在学习命令之前,理解 Docker 的几个核心概念至关重要:

  • 镜像 (Image): Docker 镜像是一个只读的模板,包含了创建 Docker 容器所需的所有文件系统、代码、运行时、库等。你可以将镜像看作是容器的“类”或“蓝图”。镜像是分层的,这使得共享和存储更高效。
  • 容器 (Container): Docker 容器是镜像的一个运行实例。容器是一个轻量级、可执行、独立运行的软件包。每个容器都运行在一个隔离的环境中,有自己的文件系统、网络和进程空间。你可以创建、启动、停止、移动或删除容器。
  • 仓库 (Registry): Docker 仓库是存放 Docker 镜像的地方。最著名的公共仓库是 Docker Hub,你也可以搭建私有仓库。仓库允许用户上传(push)和下载(pull)镜像。
  • Dockerfile: Dockerfile 是一个文本文件,包含了一系列构建 Docker 镜像的指令。通过执行 Dockerfile 中的命令,Docker 会自动构建出所需的镜像。

开发者常用 Docker 命令详解

我们将命令按照功能进行分组,方便查阅和学习。

1. Docker 客户端与信息命令

这些命令用于检查 Docker 的安装情况、版本信息以及系统状态。

  • docker --version

    • 描述: 显示 Docker 客户端的版本号。
    • 语法: docker --version
    • 示例:
      bash
      docker --version
      # 输出类似:Docker version 24.0.5, build ced0996
  • docker version

    • 描述: 显示 Docker 客户端和 Docker 守护进程(Engine)的详细版本信息。
    • 语法: docker version
    • 示例:
      bash
      docker version
      # 输出包含 Client 和 Engine 的详细信息,如版本、API 版本、操作系统等
  • docker info

    • 描述: 显示 Docker 系统的详细信息,包括容器数量、镜像数量、存储驱动、日志驱动、运行时信息等。对于了解 Docker 环境的整体健康状况和配置非常有用。
    • 语法: docker info
    • 示例:
      bash
      docker info
      # 输出包含许多关于 Docker 系统的统计和配置信息

2. 镜像管理命令

这些命令用于获取、构建、查看和删除 Docker 镜像。

  • docker pull

    • 描述: 从配置的仓库中下载(拉取)镜像到本地。默认从 Docker Hub 拉取。
    • 语法: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
    • 参数:
      • NAME: 镜像名称,如 ubuntu, nginx, mysql.
      • TAG: 镜像标签,通常代表版本,如 latest, 18.04, 5.7。如果不指定标签,默认拉取 latest 标签的镜像。
    • 示例:
      bash
      docker pull ubuntu # 拉取最新版本的 Ubuntu 镜像
      docker pull ubuntu:18.04 # 拉取 Ubuntu 18.04 版本的镜像
      docker pull nginx:latest # 拉取最新版本的 Nginx 镜像
    • 提示: 拉取大镜像可能需要一些时间,取决于你的网络速度。
  • docker imagesdocker image ls

    • 描述: 列出本地已经下载或构建的所有镜像。
    • 语法: docker images [OPTIONS] [REPOSITORY]
    • 参数:
      • -a, --all: 显示所有镜像,包括中间层镜像。
      • -q, --quiet: 只显示镜像 ID。
      • -f, --filter: 根据条件过滤显示的镜像(例如 dangling=true 显示悬空镜像)。
    • 示例:
      bash
      docker images # 列出所有本地镜像
      docker images -q # 只列出本地镜像的 ID
      docker images ubuntu # 只列出与 'ubuntu' 仓库相关的镜像
  • docker build

    • 描述: 使用 Dockerfile 构建一个新的镜像。这是开发者最常用的命令之一,用于将应用程序代码打包成镜像。
    • 语法: docker build [OPTIONS] PATH | URL | -
    • 参数:
      • PATH: Dockerfile 所在的目录路径。Docker 会将这个目录作为“构建上下文”发送给 Docker 守护进程。
      • -t, --tag NAME[:TAG]: 给构建的镜像指定一个名称和标签。非常重要,用于后续引用镜像。
      • -f, --file Dockerfile: 指定 Dockerfile 的路径(如果不在上下文根目录或名称不是 Dockerfile)。
      • --no-cache: 构建时禁用缓存。
      • --build-arg VARNAME=value: 设置构建时的变量。
    • 示例:
      假设当前目录下有一个 Dockerfile 和应用程序代码:
      bash
      docker build -t my-web-app:1.0 .
      # '.' 表示使用当前目录作为构建上下文
      # '-t my-web-app:1.0' 给构建的镜像命名为 my-web-app,标签为 1.0
    • 提示: 构建上下文的大小会影响构建速度。在 .dockerignore 文件中忽略不需要的文件(如 node_modules.git)可以优化构建过程。
  • docker tag

    • 描述: 为现有的镜像添加一个新标签。这通常用于在推送镜像到不同仓库或使用更友好的名称时。
    • 语法: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
    • 示例:
      bash
      docker tag my-web-app:1.0 myregistry.com/my-web-app:production
      # 将本地的 my-web-app:1.0 镜像打上新标签 myregistry.com/my-web-app:production
  • docker rmidocker image rm

    • 描述: 删除一个或多个本地镜像。
    • 语法: docker rmi [OPTIONS] IMAGE [IMAGE...]
    • 参数:
      • -f, --force: 强制删除,即使该镜像正在被容器使用(不推荐,除非你知道自己在做什么)。
    • 示例:
      bash
      docker rmi ubuntu:18.04 # 删除指定标签的镜像
      docker rmi image_id # 通过镜像 ID 删除镜像
      docker rmi $(docker images -q) # 删除所有本地镜像(谨慎使用!)
    • 提示: 无法删除正在运行或被容器引用的镜像,除非使用 -f。通常需要先删除容器再删除镜像。
  • docker history

    • 描述: 查看一个镜像的构建历史,即构建该镜像的 Dockerfile 中的每个指令对应的层。
    • 语法: docker history [OPTIONS] IMAGE
    • 示例:
      bash
      docker history nginx:latest
      # 显示构建 nginx:latest 镜像的步骤和每一步产生的大小变化
  • docker inspect [image]

    • 描述: 获取镜像的详细元数据,包括配置、层信息、环境变量等。
    • 语法: docker inspect [OPTIONS] IMAGE [IMAGE...]
    • 示例:
      bash
      docker inspect my-web-app:1.0
      # 输出一个大型 JSON 结构,包含镜像的所有详细信息

3. 容器管理命令

这些命令是 Docker 使用的核心,用于运行、查看、控制和删除容器。

  • docker run

    • 描述: 创建并启动一个新的容器。这是最复杂但也最常用的命令,因为它集成了创建容器的各种配置选项。
    • 语法: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
    • 参数 (常用):
      • -d, --detach: 在后台运行容器,并打印容器 ID。
      • -it: 分配一个伪终端 (-t) 并保持标准输入开放 (-i)。常用于需要交互式会话的容器(如运行一个 shell)。
      • -p, --publish hostPort:containerPort: 将容器内部的端口映射到宿主机的端口。重要!
      • -v, --volume hostPath:containerPathvolumeName:containerPath: 挂载卷或绑定宿主机目录到容器路径,用于数据持久化或共享。
      • --name containerName: 给容器指定一个唯一的名称。
      • --rm: 容器停止后自动删除容器文件系统,不保留。适合运行一次性任务。
      • -e, --env KEY=value: 在容器中设置环境变量。
      • --network networkName: 连接容器到指定的网络。
      • --link containerName:alias: 连接到另一个容器(旧版用法,推荐使用网络)。
      • -w, --workdir DIR: 指定容器内的工作目录。
      • --restart policy: 定义容器的重启策略(如 always, on-failure, no)。
    • 示例:
      bash
      docker run ubuntu /bin/echo "Hello from Docker!" # 运行一个容器执行一个简单命令然后退出
      docker run -it ubuntu /bin/bash # 交互式运行一个 Ubuntu 容器并进入 bash shell
      docker run -d -p 80:80 nginx # 后台运行 Nginx 容器,将宿主机 80 端口映射到容器 80 端口
      docker run -d --name my-mysql -e MYSQL_ROOT_PASSWORD=secret mysql:5.7 # 后台运行一个命名为 my-mysql 的 MySQL 容器,设置 root 密码
      docker run -it --rm -v $(pwd):/app my-web-app:1.0 /bin/bash # 运行一个容器,挂载当前目录到容器的 /app,并在容器停止后自动删除
    • 提示: docker run 是一个复合命令,它实际上包含了 docker createdocker start 的功能。
  • docker psdocker container ls

    • 描述: 列出当前正在运行的容器。
    • 语法: docker ps [OPTIONS]
    • 参数:
      • -a, --all: 显示所有容器,包括已停止的。
      • -q, --quiet: 只显示容器 ID。
      • -s, --size: 显示容器的大小。
      • -f, --filter: 根据条件过滤显示的容器(例如 status=exited 显示已停止的容器)。
    • 示例:
      bash
      docker ps # 列出所有正在运行的容器
      docker ps -a # 列出所有容器(包括运行和已停止)
      docker ps -q # 只列出正在运行容器的 ID
      docker ps -a -f status=exited # 列出所有已停止的容器
  • docker start

    • 描述: 启动一个或多个已停止的容器。
    • 语法: docker start [OPTIONS] CONTAINER [CONTAINER...]
    • 示例:
      bash
      docker start my-mysql # 启动名为 my-mysql 的容器
      docker start container_id # 通过 ID 启动容器
  • docker stop

    • 描述: 温柔地停止一个或多个正在运行的容器(发送 SIGTERM 信号,等待容器进程自行结束,超时后发送 SIGKILL)。
    • 语法: docker stop [OPTIONS] CONTAINER [CONTAINER...]
    • 参数:
      • -t, --time seconds: 停止前的等待时间(默认 10 秒)。
    • 示例:
      bash
      docker stop my-mysql # 停止名为 my-mysql 的容器
      docker stop $(docker ps -q) # 停止所有正在运行的容器
  • docker restart

    • 描述: 重启一个或多个容器(先停止再启动)。
    • 语法: docker restart [OPTIONS] CONTAINER [CONTAINER...]
    • 示例:
      bash
      docker restart my-web-app
  • docker kill

    • 描述: 强制停止一个或多个正在运行的容器(直接发送 SIGKILL 信号)。应谨慎使用,可能导致数据丢失。
    • 语法: docker kill [OPTIONS] CONTAINER [CONTAINER...]
    • 示例:
      bash
      docker kill naughty_container # 强制停止一个不响应 stop 的容器
  • docker rmdocker container rm

    • 描述: 删除一个或多个已停止的容器。
    • 语法: docker rm [OPTIONS] CONTAINER [CONTAINER...]
    • 参数:
      • -f, --force: 强制删除正在运行的容器(会先尝试停止再删除)。
      • -v, --volumes: 删除与容器关联的匿名卷。
    • 示例:
      bash
      docker rm my-mysql # 删除名为 my-mysql 的容器
      docker rm container_id # 通过 ID 删除容器
      docker rm $(docker ps -a -q) # 删除所有容器(包括运行和停止的,谨慎使用!)
  • docker logs

    • 描述: 查看容器的标准输出和标准错误日志。对于调试非常有用。
    • 语法: docker logs [OPTIONS] CONTAINER
    • 参数:
      • -f, --follow: 持续输出日志(类似 tail -f)。
      • -t, --timestamps: 显示日志时间戳。
      • --tail count: 显示日志末尾的指定行数。
    • 示例:
      bash
      docker logs my-web-app # 查看容器 my-web-app 的所有日志
      docker logs -f my-web-app # 实时查看容器 my-web-app 的日志
      docker logs --tail 100 my-web-app # 查看容器 my-web-app 最后 100 行日志
  • docker exec

    • 描述: 在正在运行的容器中执行一个命令。这是进入容器内部进行调试或执行维护任务的常用方法。
    • 语法: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
    • 参数:
      • -it: 交互式地执行命令,并分配伪终端。常用与进入 shell。
    • 示例:
      bash
      docker exec my-mysql cat /etc/mysql/mysql.conf.d/mysqld.cnf # 在 my-mysql 容器中执行 cat 命令
      docker exec -it my-web-app /bin/bash # 在 my-web-app 容器中启动一个 bash shell 并进入交互模式
      docker exec -it my-container sh # 如果容器没有 bash,尝试 sh
    • 提示: docker exec 在容器外部启动一个新进程在容器内部运行,而 docker attach 是连接到容器的主进程的标准输入/输出。对于运行后台服务的容器,exec 通常比 attach 更安全和方便。
  • docker inspect [container]

    • 描述: 获取容器的详细元数据,包括其状态、配置、网络设置、挂载点等。
    • 语法: docker inspect [OPTIONS] CONTAINER [CONTAINER...]
    • 示例:
      bash
      docker inspect my-mysql # 输出 my-mysql 容器的详细 JSON 信息
      docker inspect -f '{{.NetworkSettings.IPAddress}}' my-mysql # 提取 my-mysql 容器的 IP 地址
    • 提示: 结合 -f 选项使用 Go Template 语法可以提取特定的信息,非常强大。
  • docker cp

    • 描述: 在宿主机和容器之间复制文件或目录。
    • 语法:
      • docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|- (从容器复制到宿主机)
      • docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH (从宿主机复制到容器)
    • 示例:
      bash
      docker cp my-web-app:/app/logs/error.log . # 从容器复制文件到当前目录
      docker cp ./config.yml my-web-app:/app/config.yml # 从当前目录复制文件到容器

4. 数据管理命令 (Volumes)

卷是 Docker 推荐的持久化数据的方式。

  • docker volume create

    • 描述: 创建一个 Docker 卷。
    • 语法: docker volume create [OPTIONS] VOLUME
    • 示例:
      bash
      docker volume create my-data-volume
  • docker volume ls

    • 描述: 列出所有 Docker 卷。
    • 语法: docker volume ls [OPTIONS]
    • 示例:
      bash
      docker volume ls
  • docker volume inspect

    • 描述: 查看卷的详细信息,包括挂载点(在宿主机上的实际存储位置)。
    • 语法: docker volume inspect [OPTIONS] VOLUME [VOLUME...]
    • 示例:
      bash
      docker volume inspect my-data-volume
      # 输出卷的详细信息,包括 Mountpoint 字段
  • docker volume rm

    • 描述: 删除一个或多个 Docker 卷。只有当卷未被任何容器使用时才能删除,除非使用 -f
    • 语法: docker volume rm [OPTIONS] VOLUME [VOLUME...]
    • 示例:
      bash
      docker volume rm my-data-volume
    • 提示:docker run 命令中使用 -v volumeName:containerPath 来使用已创建的卷,或者使用 -v /host/path:/container/path 进行绑定挂载(Bind Mount)。对于开发者来说,绑定挂载常用于将本地代码目录挂载到容器中,方便代码修改后的实时生效。

5. 网络管理命令

Docker 网络允许容器之间以及容器与外部世界进行通信。默认有 bridge (桥接), host (宿主机), none 三种网络模式,以及用户自定义网络。

  • docker network ls

    • 描述: 列出 Docker 网络。
    • 语法: docker network ls [OPTIONS]
    • 示例:
      bash
      docker network ls
      # 会看到 bridge, host, none 以及可能有的用户自定义网络
  • docker network inspect

    • 描述: 查看网络的详细信息,包括连接到该网络的容器。
    • 语法: docker network inspect [OPTIONS] NETWORK [NETWORK...]
    • 示例:
      bash
      docker network inspect bridge
      docker network inspect my-app-network
  • docker network create

    • 描述: 创建一个新的 Docker 网络。自定义网络通常是推荐的,因为它提供了更好的隔离性和服务发现(容器可以通过容器名称互相访问)。
    • 语法: docker network create [OPTIONS] NETWORK
    • 参数:
      • --driver DRIVER: 指定网络驱动,默认为 bridge
    • 示例:
      bash
      docker network create my-app-network
    • 提示:docker run 命令中使用 --network my-app-network 将容器连接到该网络。连接到同一用户自定义网络的容器可以通过它们的容器名称相互解析和访问(服务发现)。
  • docker network rm

    • 描述: 删除一个或多个网络。
    • 语法: docker network rm [OPTIONS] NETWORK [NETWORK...]
    • 示例:
      bash
      docker network rm my-app-network

6. Docker Compose 命令

对于包含多个服务(例如,一个 Web 应用、一个数据库、一个缓存)的应用程序,使用 docker-compose 工具(或 docker compose,取决于 Docker 版本)来定义和管理是更常见和高效的方式。它使用一个 YAML 文件 (docker-compose.yml) 来配置应用程序的服务。

以下是 Docker Compose 的一些基本命令,通常在包含 docker-compose.yml 文件的目录下执行:

  • docker-compose updocker compose up

    • 描述: 构建(如果需要)并创建、启动服务中定义的容器。如果服务已经存在,它会尝试停止并重建(如果配置或镜像有变化)。
    • 语法: docker-compose up [OPTIONS] [SERVICE...]
    • 参数:
      • -d: 在后台运行容器。
      • --build: 在启动前重新构建镜像。
      • --no-deps: 不启动服务所依赖的其他服务。
    • 示例:
      bash
      docker-compose up # 启动 docker-compose.yml 中定义的所有服务,在前台显示日志
      docker-compose up -d # 后台启动所有服务
      docker-compose up --build web db # 构建 web 镜像,然后只启动 web 和 db 服务
  • docker-compose downdocker compose down

    • 描述: 停止并删除 docker-compose.yml 文件定义的服务创建的容器、网络和卷。
    • 语法: docker-compose down [OPTIONS]
    • 参数:
      • -v, --volumes: 删除与服务关联的匿名卷。
      • --rmi type: 删除镜像(all, local)。
    • 示例:
      bash
      docker-compose down # 停止并删除容器和网络
      docker-compose down -v # 停止、删除容器、网络并删除匿名卷
  • docker-compose psdocker compose ps

    • 描述: 列出 Docker Compose 项目中的服务容器状态。
    • 语法: docker-compose ps [OPTIONS] [SERVICE...]
    • 示例:
      bash
      docker-compose ps # 列出所有服务的状态
  • docker-compose logsdocker compose logs

    • 描述: 查看服务的日志输出。
    • 语法: docker-compose logs [OPTIONS] [SERVICE...]
    • 参数:
      • -f, --follow: 持续输出日志。
    • 示例:
      bash
      docker-compose logs web # 查看 web 服务的日志
      docker-compose logs -f # 实时查看所有服务的日志
  • docker-compose execdocker compose exec

    • 描述: 在指定的服务容器中执行命令。
    • 语法: docker-compose exec [OPTIONS] SERVICE COMMAND [ARGS...]
    • 参数:
      • -it: 交互式执行。
    • 示例:
      bash
      docker-compose exec web bash # 进入 web 容器的 bash shell
      docker-compose exec db mysql -uroot -p # 在 db 容器中执行 mysql 客户端命令
  • docker-compose builddocker compose build

    • 描述: 只构建或重新构建服务中定义的镜像,而不启动容器。
    • 语法: docker-compose build [OPTIONS] [SERVICE...]
    • 参数:
      • --no-cache: 构建时禁用缓存。
    • 示例:
      bash
      docker-compose build # 构建所有服务的镜像
      docker-compose build web # 只构建 web 服务的镜像

7. 清理命令

Docker 在使用过程中会产生大量的镜像、容器、卷和网络。定期清理可以释放磁盘空间。

  • docker system prune

    • 描述: 清理所有未使用的(即没有被任何容器引用的)容器、网络、镜像(只有 dangling 镜像)和悬空构建缓存。非常强大的清理命令。
    • 语法: docker system prune [OPTIONS]
    • 参数:
      • -a, --all: 删除所有未使用的镜像(包括没有关联标签的,不仅仅是 dangling 镜像)。
      • --volumes: 同时删除所有未使用的卷。
    • 示例:
      bash
      docker system prune # 清理未使用的容器、网络、dangling 镜像和构建缓存
      docker system prune -a # 清理未使用的容器、网络、所有未使用的镜像和构建缓存
      docker system prune -a --volumes # 清理所有未使用的容器、网络、镜像、卷和构建缓存(谨慎使用,确保卷中的数据不再需要!)
    • 提示: 这是最推荐的清理命令,通常在不需要保留任何旧容器/镜像/卷时使用。
  • docker volume prune

    • 描述: 清理所有未使用的卷。
    • 语法: docker volume prune [OPTIONS]
    • 示例:
      bash
      docker volume prune
  • docker network prune

    • 描述: 清理所有未使用的网络。
    • 语法: docker network prune [OPTIONS]
    • 示例:
      bash
      docker network prune
  • docker container prune

    • 描述: 清理所有已停止的容器。
    • 语法: docker container prune [OPTIONS]
    • 示例:
      bash
      docker container prune
  • docker image prune

    • 描述: 清理 dangling 镜像(即没有标签且没有被任何容器引用的镜像)。
    • 语法: docker image prune [OPTIONS]
    • 参数:
      • -a, --all: 删除所有未使用的镜像(包括没有关联标签的)。
    • 示例:
      bash
      docker image prune # 清理 dangling 镜像
      docker image prune -a # 清理所有未使用的镜像

进一步学习与实践

本文涵盖了开发者最常使用的 Docker 命令。但 Docker 的功能远不止于此。为了更深入地掌握 Docker,建议:

  • 学习 Dockerfile 编写: 了解每个指令的作用,学会编写高效、安全、分层的 Dockerfile。
  • 深入理解网络和卷: 掌握不同的网络模式和卷类型,以及它们在不同场景下的应用。
  • 熟练使用 Docker Compose: 学习 docker-compose.yml 文件的语法,定义复杂的应用栈。
  • 了解 Docker Hub 或私有仓库: 学习如何推送和管理自己的镜像。
  • 探索更高级的主题: 例如多阶段构建、BuildKit、安全加固、Swarm 或 Kubernetes 等容器编排工具。

总结

Docker 通过提供一致、隔离、轻量级的容器化环境,极大地提高了开发者的效率和协作能力。掌握 docker run, docker build, docker ps, docker logs, docker exec, docker compose up/down 等核心命令,是每个现代开发者必备的技能。

从拉取镜像开始,到构建自己的应用镜像,再到运行和管理单容器或多容器应用,直到最后的清理工作,本文提供的命令指南为你构建了扎实的 Docker 基础。

实践是掌握任何工具的最佳途径。现在就开始在你自己的项目中使用 Docker 吧!不断尝试这些命令,理解它们的选项和参数,你将很快成为一个熟练的 Docker 用户。当你遇到问题时,回顾这份指南,或查阅官方 Docker 文档,那里有最权威和全面的信息。

祝你在 Docker 的世界里开发愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部