docker run 命令使用指南
本文将深入解析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 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 命令语法
基本格式
docker run
命令的格式很固定,基本是这样:
docker run [选项] 镜像 [要执行的命令] [命令的参数]
- docker run:固定的开头
- [选项]:一些可选的标志,用来控制容器的各种行为,比如网络、存储等
- 镜像:必须有的部分,告诉 Docker 用哪个镜像来创建容器
- [要执行的命令]:可选。如果你提供了,它会覆盖镜像里默认的命令
- [命令的参数]:可选,是上面那个命令的参数
最简单的用法就是 docker run hello-world
,它会用所有默认设置来运行容器。
镜像:指定用哪个"安装包"
镜像是告诉 Docker 用哪个模板来创建容器。有几种指定方式:
镜像名:比如 ubuntu
、nginx
,或者 你的用户名/你的应用名
。
标签 (Tag):用来区分不同版本,跟在镜像名后面,用冒号隔开,比如 ubuntu:22.04
。如果不写标签,Docker 默认使用 latest
。但在正式环境最好指定一个明确的版本,因为 latest
随时可能更新。
摘要 (Digest):这是最精确的方式,用一长串哈希值来指定一个独一无二的镜像版本,格式是 @sha256:<哈希值>
。
例如:
docker run nginx@sha256:f8f4ffc1f02179a3c7518527891a668132fe487f4a37b03657ba5f4b83041f23
用摘要可以保证每次运行的都是完全一样的镜像。
自定义运行的程序
每个镜像都有一个默认启动时运行的命令。docker run
允许你在运行时修改它。
覆盖默认命令 (CMD):在镜像名后面加上你想运行的命令和参数,就可以替换掉镜像默认的 CMD。比如,ubuntu 镜像默认运行 bash,但如果你运行 docker run ubuntu ls -l /
,它就会执行 ls -l /
命令,列出根目录文件然后退出。
覆盖入口点 (ENTRYPOINT):ENTRYPOINT 定义了容器运行的主程序。要修改它,需要用 --entrypoint
选项。比如,一个镜像的 ENTRYPOINT 是 python,你想用 bash 来调试,可以这样写:
docker run --entrypoint /bin/bash my-image
docker run 常用选项说明
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与其他容器生命周期管理的命令区别和联系
docker run
开启了容器的生命,但它只是管理容器生命周期的众多命令之一。理解它和 create
、start
、exec
等命令的关系,对高效管理容器很重要。
三个命令的对比:run vs. create vs. start
这三个命令的关系可以看作是从准备到执行的过程。
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 run vs docker start 数据是否保留
docker run
和 docker 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实际使用场景
下面通过几个常见场景来看看 docker run
怎么用。
场景 1:部署一个网站服务器 (Nginx)
目标是部署一个简单的 Nginx 网站服务器,让它能稳定地在后台运行。
命令:
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 镜像
场景 2:搭建一个临时的开发环境 (Ubuntu)
目标是创建一个隔离的开发环境,代码在主机上编辑,但在容器里编译运行。
命令:
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 命令,进入命令行
场景 3:运行一个需要持久化数据的数据库 (MySQL)
这是一个非常重要的真实场景,演示如何正确运行数据库,保证数据不丢失。
第 1 步:创建一个专用网络 为需要互相通信的服务创建一个独立的网络是好习惯。
docker network create mysql-net
第 2 步:创建一个命名卷来存数据 为了让数据库的数据在容器删除后依然存在,我们创建一个命名卷。
docker volume create mysql-db-data
第 3 步:运行 MySQL 容器
用 docker run
命令把所有需要的选项组合起来,启动数据库。
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
命令启动一个新的。你会发现,因为新容器挂载了同一个数据卷,之前创建的所有数据库和数据都还在。
场景 4:多个容器通过网络通信
这个场景演示了不同服务(容器)之间如何通过 Docker 网络互相访问。我们来运行一个数据库管理工具 (Adminer),让它连接到上一步创建的 MySQL 容器。
第 1、2 步:确保上个场景的 mysql-net 网络和 my-database 容器还在运行。
第 3 步:在同一个网络里运行 Adminer 容器
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 run
、docker create
和 docker start
的区别,是掌握容器生命周期管理的关键。create
和 start
提供了更细致的控制,而 run
则是一个方便的组合命令,一步到位地完成了从找镜像到运行程序的整个过程。
彻底理解它的用法和在容器生命周期中的位置,是成为一个合格的Docker使用者的标志。