Tao
Tao

docker run 命令使用指南

本文将深入解析Docker run命令的核心功能、语法格式和常用选项,提供从基础用法到高级场景的完整指南,帮助开发者掌握容器启动和管理的最佳实践。

docker run 是 Docker 中最重要、最基本的命令。它的主要作用,是在一个叫做"容器"的独立环境里,启动一个应用程序。

不管你是开发者还是系统管理员,学会 docker run是用好容器的第一步。这个命令的核心工作就是根据一个叫"镜像"的东西,创建一个正在运行的新容器。它就像一座桥,连接了打包好的程序和实际运行的程序。

简单来说,docker run 体现了 Docker 的核心价值:把一个程序和它需要的所有东西打包在一起,然后在哪都能一样地运行。它会拿一个叫"镜像"的只读模板,把它变成一个可以运行、可以修改的"容器",让程序在里面跑起来。不管是运行一个简单的 “Hello, World!” 程序,还是部署复杂的应用,都得从docker run开始。

要用好docker run,首先要搞清楚"镜像"和"容器"的区别。这是理解容器技术的基础。

镜像 (Image) 就像是程序的安装包或设计图。它打包了程序运行需要的所有东西:代码、运行环境、工具和设置。镜像是用一个叫 Dockerfile 的文件定义的,由一层层文件系统叠加而成。镜像是只读的,一旦创建就不会变。

容器 (Container) 则是镜像运行起来的实例。执行 docker run 时,Docker 会用这个镜像创建一个容器。它会在只读的镜像上加一个可写的"容器层"。容器运行时产生的新文件或修改,比如日志,都保存在这个可写层里。

这样做的好处是,原来的镜像不会被改变。所以,每次用同一个镜像启动容器,得到的都是一个一模一样的、干净的环境。这保证了程序在开发电脑上能跑,在服务器上也能一样地跑。

docker run 命令看起来很简单,但背后 Docker 做了很多事。了解它具体干了什么,有助于我们解决问题。

当你输入 docker run nginx 时,Docker 会按以下步骤操作:

第 1 步:找镜像 Docker 先在你的电脑上找 nginx 这个镜像。如果找不到,它就会去 Docker Hub(一个默认的镜像仓库)下载。你也可以用 --pull 选项来控制这个行为,比如强制每次都下载 (always),或者只在本地没有时下载 (missing,这是默认的),或者从不下载 (never)。

第 2 步:创建容器 找到镜像后,Docker 就用它来创建一个新容器。这时候容器就有了自己的 ID 和可写的文件层。如果你没用 --name 给它起名字,Docker 会随机给一个。

第 3 步:分配资源和配置 接着,Docker 会根据你给的选项来配置容器。比如:

  • 网络:给容器连上网
  • 端口映射:把电脑的端口连到容器的端口,这样外面就能访问容器里的服务了
  • 存储:把电脑上的文件夹或者 Docker 的数据卷挂到容器里,用来存数据
  • 资源限制:限制容器能用多少 CPU 和内存

这样,一个独立的运行环境就准备好了。

第 4 步:运行程序 Docker 启动容器,运行镜像里预设好的程序(由 ENTRYPOINT 和 CMD 定义)。你也可以在 docker run 命令后面自己指定要运行的程序。

第 5 步:连接终端 默认情况下,容器的输出(比如日志)会直接显示在你的终端窗口里,你也能跟它互动。如果你想让它在后台运行,可以用 -d 选项。

整个过程就是,你告诉 Docker 你想要什么(比如一个运行 Nginx 的容器),docker run 就帮你把所有复杂的事情都搞定。

docker run 命令的格式很固定,基本是这样:

bash

docker run [选项] 镜像 [要执行的命令] [命令的参数]
  • docker run:固定的开头
  • [选项]:一些可选的标志,用来控制容器的各种行为,比如网络、存储等
  • 镜像:必须有的部分,告诉 Docker 用哪个镜像来创建容器
  • [要执行的命令]:可选。如果你提供了,它会覆盖镜像里默认的命令
  • [命令的参数]:可选,是上面那个命令的参数

最简单的用法就是 docker run hello-world,它会用所有默认设置来运行容器。

镜像是告诉 Docker 用哪个模板来创建容器。有几种指定方式:

镜像名:比如 ubuntunginx,或者 你的用户名/你的应用名

标签 (Tag):用来区分不同版本,跟在镜像名后面,用冒号隔开,比如 ubuntu:22.04。如果不写标签,Docker 默认使用 latest。但在正式环境最好指定一个明确的版本,因为 latest 随时可能更新。

摘要 (Digest):这是最精确的方式,用一长串哈希值来指定一个独一无二的镜像版本,格式是 @sha256:<哈希值>

例如:

bash

docker run nginx@sha256:f8f4ffc1f02179a3c7518527891a668132fe487f4a37b03657ba5f4b83041f23

用摘要可以保证每次运行的都是完全一样的镜像。

每个镜像都有一个默认启动时运行的命令。docker run 允许你在运行时修改它。

覆盖默认命令 (CMD):在镜像名后面加上你想运行的命令和参数,就可以替换掉镜像默认的 CMD。比如,ubuntu 镜像默认运行 bash,但如果你运行 docker run ubuntu ls -l /,它就会执行 ls -l / 命令,列出根目录文件然后退出。

覆盖入口点 (ENTRYPOINT):ENTRYPOINT 定义了容器运行的主程序。要修改它,需要用 --entrypoint 选项。比如,一个镜像的 ENTRYPOINT 是 python,你想用 bash 来调试,可以这样写:

bash

docker run --entrypoint /bin/bash my-image

docker run 有很多选项,可以精细地控制容器行为。

这些选项决定容器怎么跑,以及你如何跟它互动。

前台运行 vs. 后台运行

  • 默认是前台运行,容器会占用你的终端窗口,直到程序结束
  • -d--detach 可以让容器在后台运行。这对于需要长时间运行的服务(比如网站服务器)是必须的。命令执行后,你会立刻拿回终端控制权

交互模式-it 是一个常用组合,用来进入容器的命令行界面。它其实是两个选项的合并:

  • -i--interactive:让你能向容器输入命令
  • -t--tty:提供一个像样的终端界面

docker run -it ubuntu bash 是进入 Ubuntu 容器命令行最经典的用法。

容器命名和清理

  • --name:给容器起个好记的名字,比如 --name my-web-server。这样以后管理起来方便多了,比如 docker stop my-web-server
  • --rm:让容器在退出后自动被删除。这对于跑一次就扔掉的临时任务(比如测试)很有用,能防止系统里堆满没用的旧容器

重启策略--restart:定义容器挂了之后怎么办。这对于保证服务稳定很重要。

  • no:默认值,不重启
  • on-failure:只有在程序出错退出时才重启
  • unless-stopped:总是重启,除非是你手动 docker stop 停止的
  • always:总是重启,不管什么原因退出

对于后台服务,--restart unless-stopped 是个不错的选择。

这些选项负责把容器对外暴露出来,以便外部可以访问。

端口映射-p <主机端口>:<容器端口>:这是最常用的选项之一。它把你的电脑(主机)上的一个端口,连接到容器里的一个端口。比如 docker run -p 8080:80 nginx,就是把主机的 8080 端口映射到 Nginx 容器的 80 端口。这样你访问主机的 localhost:8080,就等于访问了容器里的服务。

-P:自动把镜像里所有声明要暴露的端口,都映射到主机上一个随机的端口。方便是方便,但端口号不固定。

网络连接--network:把容器连到一个指定的网络。默认是 bridge 网络。在实际应用中,通常会自己创建一个网络(用 docker network create),然后把需要互相通信的容器都连到这个网络上。这样它们就可以通过容器名直接访问对方。

--network-alias:给容器在网络里起个别名。比如一个叫 database-v1.2 的容器,可以给它一个别名 db,其他容器就能直接用 db 这个名字来连接它。

容器默认是"一次性"的,删了之后里面的数据就没了。但像数据库这样的应用需要永久保存数据。Docker 提供了两种主要方法:数据卷和绑定挂载点。

命名卷 (-v <卷名>:<容器内路径>):这是官方推荐的、最可靠的数据持久化方法。

  • 工作原理:命名卷由 Docker 统一管理,数据实际存在主机的一个特定目录里,但我们一般不直接去动它。关键是,数据卷的生命周期和容器是分开的。即使容器被删了,数据卷和里面的数据还在。下次再创建一个新容器,挂上同一个数据卷,数据就又回来了。

  • 例子docker run -v mysql-data:/var/lib/mysql mysql 会创建一个叫 mysql-data 的数据卷,并把它挂载到容器的 /var/lib/mysql 目录,也就是 MySQL 存放数据的地方。

绑定挂载 (-v /主机路径:/容器内路径):直接把主机上的一个文件或目录挂载到容器里。

  • 工作原理:容器可以直接读写主机上的这个目录,就像是它自己的一部分。
  • 用途:在开发时特别好用。比如,你可以把项目的代码目录挂载到容器里,在主机上用你喜欢的编辑器改代码,容器里马上就能看到变化,实现实时刷新。
  • 注意:这种方式不方便移植,因为它依赖主机上特定的文件路径。在 Windows 或 macOS 上也可能有效率问题。

只读文件系统 (--read-only):为了更安全,可以用这个选项让容器的文件系统变成只读的。这样就算程序被攻击,也无法在容器里乱写文件。如果程序确实需要在某些地方写文件(比如日志或临时文件),可以单独为那些目录挂载可写的数据卷。

下表总结了这几种存储方式的区别:

特性 命名卷 绑定挂载 匿名卷
主要用途 保存生产数据(如数据库) 开发时共享代码、配置文件 临时数据,只用于单个容器
主机位置 Docker 管理的目录 用户指定的任意路径 Docker 管理,但名字是随机的
生命周期 独立于容器,需手动删除 和主机上的文件一样长久 可随容器一起删除 (docker rm -v)
可移植性 高,在哪都能用 低,依赖主机路径 低,不适合共享
性能 在 Linux 上性能好 在 Linux 上性能好,在其他系统可能稍慢 和命名卷一样
docker run 语法 -v my-volume:/app -v /host/path:/app -v /app (前面不写主机路径)

这些选项让你可以在启动容器时传入配置信息。

-e <键>=<值>--env:在容器里设置一个环境变量。这是传递数据库密码、API 密钥等配置信息最常见的方式。

--env-file:如果有很多环境变量,可以把它们写在一个文件里,用这个选项一次性传进去。这样更安全,也更整洁。

-w--workdir:设置容器里的工作目录。之后在容器里执行的命令都会在这个目录下运行。

--entrypoint:前面提过,这个选项可以完全替换掉镜像默认的启动程序,通常用于调试。

在多应用共享的环境里,限制每个容器能用的资源很重要,防止某个容器把资源都占光了。

--cpus & --memory (-m):这是两个最基本的资源限制选项。

  • --cpus="1.5" 限制容器最多使用 1.5 个 CPU 核心
  • --memory="512m"-m 512m 限制容器最多使用 512MB 内存

设置这些限制对保证系统稳定非常重要。

--oom-kill-disable:默认情况下,如果容器用的内存超了,系统会把它杀掉。这个选项可以禁止这种行为,但非常危险。如果一个有内存泄漏的程序用了这个选项,可能会把整个主机的内存都耗尽,导致系统崩溃。

Docker默认提供了很好的隔离,但这些选项可以让你进一步调整安全设置。

--privileged:一个非常强大且危险的选项。它几乎取消了容器和主机之间的所有安全隔离,让容器能直接访问主机设备。除非是特殊情况(比如在容器里再运行 Docker),否则绝对不要用。

--cap-add & --cap-drop:比 --privileged 更精细、更安全的选择。你可以用它们来精确地给容器添加或移除所需的系统权限,而不是一次性给所有权限。

--security-opt:用于设置更高级的安全选项,比如 AppArmor 或 seccomp 配置文件,可以限制容器能做的系统调用,进一步加固安全。

-u--user:很多镜像里的程序默认以 root 用户身份运行,这有安全风险。用这个选项可以指定一个普通用户来运行程序,比如 -u myuser-u 1001。这是一个非常重要的安全习惯。

docker run 开启了容器的生命,但它只是管理容器生命周期的众多命令之一。理解它和 createstartexec 等命令的关系,对高效管理容器很重要。

这三个命令的关系可以看作是从准备到执行的过程。

docker create [镜像]:这个命令只做事的第一步。它用镜像创建一个容器,但不启动它。容器处于"已创建"状态,占着硬盘空间,但不运行,不耗 CPU 和内存。这在你需要先配置好容器再启动时很有用。

docker start:这个命令用来启动一个已经存在(已创建或已停止)的容器。它不创建新容器,只是把一个现有的容器跑起来。它的操作对象是容器,不是镜像。

docker run [镜像]:如前所述,这是一个方便的组合命令,相当于 docker create 加上 docker start。它从一个镜像创建一个新容器,并马上启动它。你不能对一个已经存在的容器用 docker run,因为它的作用永远是创建新的。

下表总结了这几个命令的区别,并加入了另一个常用命令 docker exec

特性 docker run docker start docker create docker exec
主要作用 创建一个新容器并启动它 启动一个已存在的容器 创建一个新容器但不启动 在一个正在运行的容器里执行命令
操作对象 镜像 容器 (ID 或名字) 镜像 容器 (ID 或名字)
容器状态 总是从镜像的全新状态开始 从上次停止的状态继续,保留数据 产生一个"已创建"状态的容器 对一个"运行中"的容器操作
典型用途 第一次部署一个服务 重启一个需要保留数据的服务(如数据库) 预先准备好一个容器,稍后启动 调试、查看或管理一个正在运行的服务

docker rundocker start 的区别对数据管理影响很大。

docker run 每次都从镜像创建一个全新的容器。这对那些不需要保存状态的应用来说是理想的,比如每次运行都希望是干净环境的测试。

docker start 则是为了保留状态。它会恢复一个已停止的容器,并且容器文件系统里之前的所有改动都还在(除非当初是用 --rm 运行的)。这对数据库这类有状态应用至关重要。你 docker stop 一个数据库容器,下次再 docker start 它,所有数据都原封不动。

docker run 启动容器后,还有其他命令来管理它。

docker stop:这是停止容器的标准方法。它会先礼貌地通知容器里的程序"该关了",给它一点时间(默认 10 秒)来保存数据、正常退出。如果超时了还没退,Docker 就会强制杀死它。

docker kill:不打招呼,直接强制杀死容器里的程序。用于 docker stop 不管用的时候。

docker pause / docker unpause:暂停和恢复容器里的所有进程。暂停时,容器还占着内存,但不耗 CPU。这可以用来临时释放 CPU 资源。

docker rm:永久删除一个已停止的容器。如果加 -v 选项 (docker rm -v),它关联的匿名卷也会被一并删除。

下面通过几个常见场景来看看 docker run 怎么用。

目标是部署一个简单的 Nginx 网站服务器,让它能稳定地在后台运行。

命令

bash

docker run --name my-nginx-server -d -p 8080:80 --restart unless-stopped nginx:latest

命令解释

  • --name my-nginx-server:给容器起个名字叫 my-nginx-server,方便以后管理
  • -d:让容器在后台运行
  • -p 8080:80:把主机的 8080 端口映射到容器的 80 端口,这样你就可以通过 http://<你的IP>:8080 访问网站了
  • --restart unless-stopped:设置重启策略。如果容器意外挂了或者主机重启,Docker 会自动把它拉起来
  • nginx:latest:指定使用最新的 Nginx 镜像

目标是创建一个隔离的开发环境,代码在主机上编辑,但在容器里编译运行。

命令

bash

docker run --name dev-env -it --rm -v "$(pwd)"/app:/app -w /app ubuntu:latest bash

命令解释

  • --name dev-env:给这个开发环境起个名字
  • -it:以交互模式进入容器的命令行
  • --rm:当你退出命令行时,自动删除这个容器,保持系统干净
  • -v "$(pwd)"/app:/app:这是一个绑定挂载。它把你主机当前目录下的 app 文件夹,挂载到容器里的 /app 目录。这样你在主机上改代码,容器里马上就能看到
  • -w /app:把容器里的工作目录设为 /app。这样一进去就直接在项目目录里了
  • ubuntu:latest bash:使用最新的 Ubuntu 镜像,并指定运行 bash 命令,进入命令行

这是一个非常重要的真实场景,演示如何正确运行数据库,保证数据不丢失。

第 1 步:创建一个专用网络 为需要互相通信的服务创建一个独立的网络是好习惯。

bash

docker network create mysql-net

第 2 步:创建一个命名卷来存数据 为了让数据库的数据在容器删除后依然存在,我们创建一个命名卷。

bash

docker volume create mysql-db-data

第 3 步:运行 MySQL 容器docker run 命令把所有需要的选项组合起来,启动数据库。

bash

docker run --name my-database -d \
  --network mysql-net \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -v mysql-db-data:/var/lib/mysql \
  mysql:latest

命令解释

  • --name my-database:给数据库容器起个固定的名字
  • -d:让它在后台运行
  • --network mysql-net:把它连到我们刚创建的 mysql-net 网络
  • -e MYSQL_ROOT_PASSWORD=my-secret-pw:通过环境变量设置 MySQL 的 root 用户密码
  • -v mysql-db-data:/var/lib/mysql:这是最关键的一步。它把我们创建的 mysql-db-data 数据卷,挂载到容器里 MySQL 存放数据的 /var/lib/mysql 目录。这样所有数据库文件都会被保存在这个独立的数据卷里,和容器的生命周期无关

验证:你可以试试把这个容器停掉再删掉 (docker stop my-database 然后 docker rm my-database),然后再用完全一样的 docker run 命令启动一个新的。你会发现,因为新容器挂载了同一个数据卷,之前创建的所有数据库和数据都还在。

这个场景演示了不同服务(容器)之间如何通过 Docker 网络互相访问。我们来运行一个数据库管理工具 (Adminer),让它连接到上一步创建的 MySQL 容器。

第 1、2 步:确保上个场景的 mysql-net 网络和 my-database 容器还在运行。

第 3 步:在同一个网络里运行 Adminer 容器

bash

docker run --name my-adminer -d \
  --network mysql-net \
  -p 8888:8080 \
  adminer

命令解释

  • --name my-adminer:给管理工具起个名字
  • -d:后台运行
  • --network mysql-net:关键点!把 Adminer 容器也连到 mysql-net 网络上
  • -p 8888:8080:把 Adminer 的网页界面暴露在主机的 8888 端口

如何连接:现在,在浏览器里访问 http://<你的IP>:8888,会看到 Adminer 的登录页面。在"服务器"或"主机名"一栏,你不需要填 IP 地址,直接输入 my-database 就行了。因为它们在同一个网络里,Docker 的内置 DNS 会自动把 my-database 这个名字解析成 MySQL 容器的内部 IP 地址。

这展示了构建微服务的一个基本方法:服务之间通过名字互相发现和通信。

docker run 它的主要工作是把一个静态的镜像变成一个活的、隔离的容器,同时提供了丰富的选项,让你能精确控制容器的方方面面。

通过 docker run 的各种选项,我们可以配置容器的运行方式、网络、数据存储、资源使用和安全设置。无论是开发时需要一个临时的命令行环境,还是在生产上部署一个能自动重启的稳定服务,或是启动一个需要永久保存数据的数据库。

搞清楚 docker rundocker createdocker start 的区别,是掌握容器生命周期管理的关键。createstart 提供了更细致的控制,而 run 则是一个方便的组合命令,一步到位地完成了从找镜像到运行程序的整个过程。

彻底理解它的用法和在容器生命周期中的位置,是成为一个合格的Docker使用者的标志。

相关内容