从零开始学 Docker run:核心命令与实践
导言:为什么是 Docker,以及 docker run 的核心地位
在当今的软件开发和部署领域,Docker 已经成为一个家喻户晓的名字。它通过容器化技术,彻底改变了我们构建、发布和运行应用程序的方式。简单来说,Docker 允许你将应用程序及其所有依赖(代码、运行时、系统工具、库等)打包到一个独立的、可移植的“容器”中。这个容器可以在任何支持 Docker 的环境中以相同的方式运行,从而解决了“在我机器上能跑”的经典问题。
而 docker run 命令,正是你与 Docker 世界交互的起点,是启动和管理容器的核心。它像是通往容器化应用的大门,理解并熟练运用它,是掌握 Docker 的第一步,也是最关键的一步。
本文将带领你从零开始,深入探索 docker run 命令的各个方面,从最简单的用法到各种高级选项,配合丰富的实例,帮助你彻底掌握这个强大的工具。
第一章:初识 Docker 与环境准备
在深入 docker run 之前,确保你的系统已经安装了 Docker。
1.1 Docker 安装与验证
- 安装 Docker Desktop (Windows/macOS):推荐新手使用,它集成了 Docker Engine、CLI、Docker Compose 和 Kubernetes。
- 访问 Docker 官方网站 下载并安装。
 
 - 安装 Docker Engine (Linux):
- 遵循 Docker 官方文档 的指引,针对你的 Linux 发行版进行安装。
 
 
安装完成后,打开终端或命令提示符,输入以下命令验证 Docker 是否安装成功:
bash
docker --version
docker info
如果能看到 Docker 的版本信息和系统信息,说明你已经准备就绪。
1.2 Docker 核心概念回顾
在继续之前,我们快速回顾几个与 docker run 紧密相关的核心概念:
- 镜像 (Image):一个只读的模板,包含了创建 Docker 容器所需的一切,如操作系统、应用程序、库、配置等。你可以把它理解为面向对象编程中的“类”。
 - 容器 (Container):镜像的一个运行实例。容器是轻量级、可移植、独立的,并且包含了应用程序运行所需的所有环境。可以把它理解为面向对象编程中的“对象”。
 - 仓库 (Registry):存放 Docker 镜像的地方,最常用的是 Docker Hub(Docker 官方的公共仓库)。你可以从仓库中拉取镜像,也可以将自己创建的镜像推送到仓库。
 
docker run 命令就是用来根据一个镜像来创建并启动一个容器的。
第二章:docker run 的基础用法:启动你的第一个容器
docker run 命令最基本的语法是:
bash
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
其中:
*   IMAGE:指定要运行的镜像的名称。
*   COMMAND:在容器启动后要执行的命令。如果镜像有默认命令,你可以省略或覆盖它。
*   ARG...:传递给 COMMAND 的参数。
2.1 运行你的第一个容器:hello-world
让我们从最简单的 hello-world 镜像开始。这个镜像旨在验证你的 Docker 安装是否正常。
bash
docker run hello-world
当你第一次执行这个命令时,Docker 会做几件事:
1.  检查本地镜像:Docker 会检查你的本地是否存在 hello-world 镜像。
2.  拉取镜像 (Pull Image):如果本地没有,Docker 会从 Docker Hub(默认仓库)自动下载 hello-world 镜像。
3.  创建容器 (Create Container):根据下载的 hello-world 镜像,创建一个新的容器。
4.  启动容器 (Start Container):启动这个容器。
5.  执行命令 (Execute Command):容器内部会执行 hello-world 镜像中预定义的程序。
6.  输出信息:程序执行完成后,会输出一段信息到你的终端。
7.  停止并退出:由于 hello-world 程序的特性,它执行完任务后会立即停止并退出。
你会看到类似这样的输出:
“`
Unable to find image ‘hello-world:latest’ locally
latest: Pulling from library/hello-world
… (下载进度) …
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
… (更多说明信息) …
“`
恭喜你!你已经成功运行了你的第一个 Docker 容器。
2.2 运行一个带有自定义命令的容器:busybox
busybox 是一个非常小的 Linux 工具集,包含了许多常用的 Unix 工具。我们可以用它来执行一些简单的命令。
bash
docker run busybox echo "Hello from BusyBox container!"
输出:
“`
Unable to find image ‘busybox:latest’ locally
latest: Pulling from library/busybox
… (下载进度) …
Status: Downloaded newer image for busybox:latest
Hello from BusyBox container!
“`
这个例子中,我们指定了在 busybox 容器中执行 echo "Hello from BusyBox container!" 命令。容器启动,执行命令,然后退出。
第三章:docker run 的核心选项深度解析
docker run 真正的强大之处在于其丰富的选项。这些选项允许你精细地控制容器的生命周期、网络、存储、资源等方方面面。
3.1 后台运行容器:-d 或 --detach
默认情况下,docker run 会在前台运行容器,当你关闭终端或者容器内的程序执行完毕,容器就会停止。如果想让容器在后台持续运行,你需要使用 -d 或 --detach 选项。
示例:运行一个 Nginx Web 服务器
bash
docker run -d nginx
nginx:这是一个流行的 Web 服务器镜像。-d:让nginx容器在后台运行。
执行后,你会得到一串长长的容器 ID,表示容器已在后台成功启动。
bash
a1b2c3d4e5f6... (容器ID)
要查看正在运行的容器,可以使用 docker ps 命令:
bash
docker ps
你会看到类似这样的输出:
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
a1b2c3d4e5f6   nginx     "/docker-entrypoint...."   10 seconds ago   Up 9 seconds             quizzical_morse
CONTAINER ID:容器的唯一标识符。IMAGE:容器所基于的镜像。COMMAND:容器启动时执行的命令。CREATED:容器创建的时间。STATUS:容器的运行状态(Up表示正在运行)。PORTS:端口映射信息(稍后解释)。NAMES:容器的名称(稍后解释)。
要停止这个后台运行的容器,可以使用 docker stop <CONTAINER_ID_或_NAME>:
bash
docker stop a1b2c3d4e5f6 # 或者 docker stop quizzical_morse
停止后,容器仍然存在,只是处于停止状态。要查看所有容器(包括已停止的),可以使用 docker ps -a。
要彻底删除一个已停止的容器,可以使用 docker rm <CONTAINER_ID_或_NAME>:
bash
docker rm a1b2c3d4e5f6 # 或者 docker rm quizzical_morse
3.2 交互式运行容器并分配伪终端:-it
对于需要与容器内部进行交互的场景(例如,进入容器的 shell 环境),-i 和 -t 选项组合使用非常重要。
-i或--interactive:保持标准输入 (STDIN) 打开,允许你向容器发送输入。-t或--tty:分配一个伪终端 (pseudo-TTY),这样你就可以像操作普通终端一样与容器交互。
示例:进入 Ubuntu 容器的 Bash shell
bash
docker run -it ubuntu bash
ubuntu:官方的 Ubuntu 操作系统镜像。bash:在 Ubuntu 容器中启动 Bash shell。
执行后,你的终端提示符会变成容器内的提示符(例如 root@<container_id>:/#),你就可以在容器中执行 Linux 命令了:
bash
root@a1b2c3d4e5f6:/# ls /
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
root@a1b2c3d4e5f6:/# exit
当你输入 exit 后,容器会停止并退出。
小技巧:退出而不停止容器
如果你在 docker run -it 启动的容器中,想退出容器的 shell 但不停止容器,可以按 Ctrl+P 接着按 Ctrl+Q。这样容器会在后台继续运行。
3.3 为容器命名:--name
Docker 会为每个容器自动生成一个随机的、不重复的名称(比如前面看到的 quizzical_morse)。但这些随机名称难以记忆和管理。使用 --name 选项可以为你的容器指定一个有意义的名称。
示例:为 Nginx 容器命名
bash
docker run -d --name my-nginx-web -p 80:80 nginx
现在,docker ps 命令会显示你的容器名为 my-nginx-web:
bash
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS              NAMES
abcdef123456   nginx     "/docker-entrypoint...."   2 seconds ago   Up 1 second    0.0.0.0:80->80/tcp   my-nginx-web
使用命名容器的好处:
*   易于识别:一眼就能看出容器的作用。
*   方便管理:你可以使用容器名称而不是随机 ID 来停止、启动、删除、查看日志等操作。例如 docker stop my-nginx-web。
*   网络解析:在 Docker 网络中,容器可以通过名称相互访问(稍后会介绍)。
注意:容器名称必须是唯一的。如果你尝试用相同的名称运行第二个容器,Docker 会报错。
3.4 端口映射:-p 或 --publish
容器默认是相互隔离的,包括网络端口。如果你希望从宿主机或其他网络访问容器内部运行的服务(例如 Web 服务器的 80 端口),就需要进行端口映射。
-p <宿主机端口>:<容器端口>:将宿主机的某个端口映射到容器的某个端口。-p <宿主机IP>:<宿主机端口>:<容器端口>:指定宿主机的特定 IP。-P或--publish-all:将容器暴露的所有端口随机映射到宿主机的高位端口(不推荐用于生产环境)。
示例:映射 Nginx 的 80 端口
Nginx 容器默认在其内部的 80 端口提供 Web 服务。要从宿主机访问它,我们需要将宿主机的端口映射到容器的 80 端口。
bash
docker run -d --name my-nginx-mapped -p 8080:80 nginx
-p 8080:80:这表示将宿主机的8080端口映射到容器内部的80端口。
现在,你可以通过访问宿主机的 http://localhost:8080 来访问 Nginx 服务了。
“`bash
在宿主机终端执行
curl http://localhost:8080
“`
你将看到 Nginx 的欢迎页面 HTML 内容。
多个端口映射:
一个容器可以映射多个端口,只需多次使用 -p 选项:
bash
docker run -d --name my-app -p 8080:80 -p 8443:443 my-web-app-image
3.5 数据持久化:-v 或 --volume
容器是短暂的 (ephemeral)。当容器被删除时,其中产生的所有数据都会丢失。为了实现数据的持久化存储,或者在宿主机与容器之间共享数据,我们需要使用数据卷 (Volumes)。
-v <宿主机路径>:<容器路径>:将宿主机上的一个目录或文件挂载到容器内部的指定路径。-v <卷名称>:<容器路径>:使用 Docker 管理的具名卷 (named volume) 进行挂载。
示例1:挂载宿主机目录到 Nginx
假设你在宿主机有一个 HTML 文件目录 /path/to/my/html,你想让 Nginx 提供这些文件。
“`bash
首先,在宿主机上创建一些测试文件
mkdir -p /tmp/my_nginx_html
echo “
Hello from Docker Nginx!
” > /tmp/my_nginx_html/index.html
运行 Nginx 容器并挂载数据卷
docker run -d –name my-nginx-volume -p 8081:80 -v /tmp/my_nginx_html:/usr/share/nginx/html nginx
“`
现在,访问 http://localhost:8081,你将看到 “Hello from Docker Nginx!”。即使你删除了这个容器,宿主机上的 /tmp/my_nginx_html 目录及其内容依然存在。
示例2:使用具名卷 (Named Volumes)
具名卷是由 Docker 管理的,更推荐用于持久化存储。它们存储在 Docker 宿主机的特定位置,由 Docker 负责创建和管理。
“`bash
创建一个具名卷
docker volume create my-app-data
运行一个 MySQL 容器,将数据存储到具名卷
docker run -d –name my-mysql –env MYSQL_ROOT_PASSWORD=mysecret -v my-app-data:/var/lib/mysql mysql
“`
my-app-data是我们创建的具名卷的名称。:/var/lib/mysql是 MySQL 容器内部存储数据的地方。
现在,即使 my-mysql 容器被删除,其数据也保留在 my-app-data 卷中,下次启动新容器时可以重新挂载,实现数据的不丢失。
3.6 设置环境变量:-e 或 --env
许多应用程序通过环境变量来配置。docker run 允许你在启动容器时设置这些环境变量。
示例:为 MySQL 容器设置 root 用户密码
MySQL 官方镜像要求在启动时设置 MYSQL_ROOT_PASSWORD 环境变量。
bash
docker run -d --name my-mysql-env -e MYSQL_ROOT_PASSWORD=your_secure_password mysql
-e MYSQL_ROOT_PASSWORD=your_secure_password:在容器内部设置一个名为MYSQL_ROOT_PASSWORD的环境变量,值为your_secure_password。
你可以通过 docker exec 进入容器内部查看环境变量:
“`bash
docker exec -it my-mysql-env bash
在容器内部执行
env | grep MYSQL_ROOT_PASSWORD
应该会输出 MYSQL_ROOT_PASSWORD=your_secure_password
exit
“`
3.7 指定或覆盖容器启动命令:COMMAND 和 --entrypoint
COMMAND(命令行参数):当你提供IMAGE后的所有内容,都会被当作在容器内部执行的命令或参数。如果镜像定义了ENTRYPOINT,那么你提供的COMMAND会作为ENTRYPOINT的参数。如果镜像没有定义ENTRYPOINT,那么COMMAND会直接作为容器启动时执行的程序。--entrypoint:用于覆盖镜像中定义的ENTRYPOINT。
示例1:覆盖默认命令
假设 busybox 镜像的默认 COMMAND 是 sh。我们可以直接覆盖它:
bash
docker run busybox ls -l /
这会启动一个 busybox 容器,并立即执行 ls -l / 命令,然后退出。
示例2:覆盖 Entrypoint
有些镜像为了统一启动方式,会将主要的执行程序定义为 ENTRYPOINT。例如,debian 镜像可能默认 ENTRYPOINT 是 /bin/bash,但你也可以覆盖它来运行其他程序:
bash
docker run --entrypoint echo debian "Hello from a custom entrypoint!"
这会启动一个 debian 容器,但不再执行其默认的 ENTRYPOINT,而是执行 echo 命令,并输出 “Hello from a custom entrypoint!”。
3.8 容器的重启策略:--restart
当容器意外退出或 Docker Daemon 重启时,你可能希望容器能够自动重新启动。--restart 选项可以实现这一点。
no:默认值,容器不会自动重启。on-failure:仅当容器以非零状态码退出时才重启(表示异常退出)。always:无论容器退出状态如何,都会尝试自动重启。unless-stopped:无论容器退出状态如何,都会尝试自动重启,但如果容器被手动停止,则不会重启。
示例:Nginx 容器始终自动重启
bash
docker run -d --name my-nginx-restart --restart always -p 8082:80 nginx
现在,即使 Nginx 容器因为某些原因停止(例如 Docker Daemon 重启,或者容器内部服务崩溃),Docker 也会尝试重新启动它,确保服务持续运行。
3.9 网络配置:--network
Docker 提供了强大的网络功能,允许容器相互通信,或与宿主机及外部网络通信。--network 选项让你指定容器连接到哪个网络。
- bridge (默认):Docker 的默认桥接网络。此网络上的容器可以相互通信,也可以通过宿主机的 IP 访问外部网络。
 - host:容器直接使用宿主机的网络栈,共享宿主机的 IP 地址和端口空间。这意味着容器可以直接监听宿主机的端口,但会失去容器的网络隔离性。
 - none:容器没有网络接口,完全隔离。
 - 自定义网络:你可以创建自己的桥接网络,提供更好的隔离和名称解析功能。
 
示例:创建自定义网络并连接容器
首先,创建一个自定义桥接网络:
bash
docker network create my-app-net
然后,将你的应用程序和数据库容器连接到这个网络:
“`bash
运行数据库容器
docker run -d –name my-db –network my-app-net -e MYSQL_ROOT_PASSWORD=secret mysql
运行应用程序容器,它可以通过容器名 “my-db” 访问数据库
docker run -d –name my-app –network my-app-net my-web-app-image
“`
在 my-app 容器内部,你可以直接使用 my-db 作为主机名来连接数据库,因为 Docker 提供了内置的 DNS 服务,可以将容器名称解析为对应的 IP 地址。
第四章:实践场景与高级技巧
4.1 清理容器:--rm
对于一些临时运行的容器(例如一次性脚本、测试任务),你可能不希望它们在执行完毕后仍然占用资源。--rm 选项可以在容器停止时自动删除它。
bash
docker run --rm busybox echo "This container will be removed after execution."
这个容器运行完 echo 命令后会立即停止并被删除,你不会在 docker ps -a 中看到它。
4.2 调试运行中的容器:docker exec
如果容器在后台运行,但你想进入其内部执行一些命令进行调试,docker exec 是你的好帮手。
“`bash
启动一个 Nginx 容器
docker run -d –name my-debug-nginx -p 8083:80 nginx
进入 Nginx 容器的 Bash shell
docker exec -it my-debug-nginx bash
“`
现在你就可以在容器内部进行文件查看、日志分析等操作了。
bash
root@<container_id>:/# ls /etc/nginx/
root@<container_id>:/# cat /var/log/nginx/access.log
root@<container_id>:/# exit
docker exec 不会停止容器,只是在容器内部启动了一个新的进程。
4.3 查看容器日志:docker logs
容器化应用程序通常会将日志输出到标准输出 (stdout) 和标准错误 (stderr)。docker logs 命令可以方便地查看这些日志。
“`bash
docker run -d –name my-log-test -p 8084:80 nginx
查看容器日志
docker logs my-log-test
实时跟踪日志(类似 tail -f)
docker logs -f my-log-test
“`
当你访问 http://localhost:8084 时,docker logs -f 会实时显示 Nginx 的访问日志。
4.4 资源限制:--memory 和 --cpus
在生产环境中,你可能需要限制容器使用的资源,以防止单个容器占用过多资源影响其他服务。
--memory <大小>:限制容器可以使用的内存量(例如512m、1g)。--cpus <数量>:限制容器可以使用的 CPU 核心数量(例如0.5、2)。
bash
docker run -d --name my-resource-limited-app --memory 256m --cpus 0.5 my-app-image
这将确保 my-resource-limited-app 容器最多使用 256MB 内存和 0.5 个 CPU 核心。
第五章:docker run 的最佳实践与常见问题
5.1 最佳实践
- 使用明确的镜像标签 (Tag):避免使用 
latest标签,因为它指向的镜像内容会不断变化。使用如nginx:1.20.1这样具体的版本号,以确保环境的可重现性。 - 容器最小化原则:一个容器只运行一个主要服务。例如,Nginx 容器只运行 Nginx,MySQL 容器只运行 MySQL。
 - 数据卷用于持久化:始终使用数据卷来存储需要持久化的数据,而不是直接写入容器文件系统。
 - 善用 
--name:为容器命名,提高可读性和管理效率。 - 理解网络模式:根据需求选择合适的网络模式,特别是自定义桥接网络,它提供更好的服务发现和隔离。
 - 配置重启策略:为重要的生产容器配置合适的 
--restart策略,提高服务的可用性。 - 资源限制:在生产环境为容器设置内存和 CPU 限制,避免资源争抢。
 
5.2 常见问题与排查
- 容器启动后立即退出:
- 原因:容器内的主进程执行完毕后退出,或者启动命令本身有问题。
 - 排查:
- 检查 
docker logs <container_name>查看日志。 - 尝试使用 
docker run -it <image> bash进入容器,手动执行启动命令,看是否有报错。 - 有些服务(如 Nginx、Apache)需要以前台模式运行才能保持容器存活,如果你的 Dockerfile 或 
COMMAND没有确保这一点,它们可能会启动后立即退出。 
 - 检查 
 
 - 端口不通:
- 原因:端口映射错误,防火墙阻止,或者容器内部服务未启动或监听错误端口。
 - 排查:
- 确认 
-p选项是否正确:<宿主机端口>:<容器端口>。 - 检查宿主机防火墙 (
ufw status,firewall-cmd --list-all) 是否允许入站连接到宿主机端口。 - 使用 
docker exec -it <container_name> sh进入容器,使用netstat -tulnp或ss -tulnp查看容器内部监听的端口。 
 - 确认 
 
 - 文件权限问题:
- 原因:通过 
-v挂载宿主机目录时,宿主机文件或目录的权限与容器内进程的用户不匹配。 - 排查:
- 在宿主机上,确保挂载目录的权限允许容器内部的用户写入(例如 
chmod -R 777 /path/to/my/html,但在生产环境应更精细地控制权限)。 - 查看 Dockerfile,了解容器内部运行进程的用户 ID。
 
 - 在宿主机上,确保挂载目录的权限允许容器内部的用户写入(例如 
 
 - 原因:通过 
 - 无法拉取镜像:
- 原因:网络问题,镜像名称拼写错误,或需要认证(私有仓库)。
 - 排查:
- 检查网络连接。
 - 确认镜像名称和标签是否正确。
 - 如果从私有仓库拉取,确保已通过 
docker login登录。 
 
 
结语:docker run 只是开始
恭喜你!到这里,你已经详细了解了 docker run 命令的绝大部分核心功能和实践方法。从最简单的 hello-world 到复杂的持久化、网络配置和资源限制,你已经掌握了启动和管理 Docker 容器的基石。
然而,docker run 只是 Docker 旅程的起点。随着你的项目变得越来越复杂,你可能会发现手动管理多个 docker run 命令变得繁琐。这时,你就可以探索以下更高级的工具和概念:
- Dockerfile:用于自动化构建自定义 Docker 镜像的脚本。
 - Docker Compose:用于定义和运行多容器 Docker 应用程序的工具,它通过一个 YAML 文件来配置服务,然后通过一个命令 (
docker-compose up) 启动所有服务。 - Docker Swarm / Kubernetes:用于容器编排的平台,管理和调度大量容器集群。
 
继续学习,继续实践,Docker 的强大功能将为你打开全新的开发和部署世界。祝你在容器化的道路上一帆风顺!