Docker 教程

Docker run 命令

1. docker run 命令剖析

docker run 命令遵循一个非常直观的基础语法结构:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

让我们把这句命令拆解开来,逐一认识每个组成部分:

  • docker run: 这是启动容器创建和执行流程的基础指令。
  • [OPTIONS] (选项/参数): 这些标志用来改变 docker run 命令的行为。Docker 提供了非常丰富的选项,让你能够对容器的配置进行细粒度的控制。稍后我们将详细探索最常用、最核心的选项。
  • IMAGE (镜像): 指定你要基于哪个镜像来创建容器。你可以通过“名称+标签”的方式来指定(例如:ubuntu:latestmy-app:1.0),也可以直接使用镜像 ID。如果该镜像在你的本地不存在,Docker 默认会尝试从配置的镜像仓库(通常是 Docker Hub)中拉取(Pull)它。如果你没有指定标签(Tag),Docker 会默认使用 latest(最新版)标签。
  • [COMMAND] (命令): 这是容器启动后将在其内部执行的命令。如果你没有在这里指定具体的命令,Docker 就会使用镜像的 Dockerfile 中定义好的默认命令(即 CMD 指令指定的命令)。
  • [ARG...] (参数): 这些是传递给前面 [COMMAND] 的附加参数。

2. 必备的 docker run 参数选项

docker run 的强大之处就在于它丰富的选项。下面我们将深入剖析最关键的几个参数,带你彻底弄懂它们的用法:

2.1 -d--detach (后台运行)

这个选项会让容器在 分离模式 (Detached mode) 下运行,简单来说就是在后台运行。这对于 Web 服务器、数据库等需要长时间运行的应用程序来说至关重要。

示例:

docker run -d nginx:latest

执行这条命令后,它会在后台启动一个 Nginx Web 服务器容器。你不会在当前的终端里直接看到 Nginx 的运行日志。相反,Docker 会返回一个超长的唯一容器 ID,你可以用这个 ID 在后续管理该容器。想要查看日志,你可以使用 docker logs <容器ID> 命令。

为什么使用它?

   分离模式让你在容器持续运行的同时,能够继续使用当前的终端窗口敲其他命令。对于绝大多数服务端应用来说,这是必选操作。

2.2 -p--publish (端口映射)

这个选项用于在宿主机(你的电脑或服务器)和容器之间映射端口。如果你想让外部世界能够访问容器内部运行的服务,这个参数必不可少。

语法: -p 宿主机端口:容器内端口

示例:

docker run -d -p 8080:80 nginx:latest

这条命令将你宿主机的 8080 端口映射到了容器内部的 80 端口。任何发送到你宿主机 8080 端口的网络流量,都会被自动转发到容器内 80 端口上运行的 Nginx 服务器。启动后,打开浏览器访问 http://localhost:8080 即可看到 Nginx 的欢迎页面。

  • 进阶用法: 你还可以指定宿主机上要绑定的具体 IP 地址,例如:-p 127.0.0.1:8080:80。这样配置后,就只有宿主机本机能访问该容器,外部网络无法访问,提升了安全性。你也可以使用大写的 -P 参数,让 Docker 随机映射容器内所有暴露出来的端口。

为什么使用它?

   容器默认是一个完全隔离的网络环境。端口映射就像是在集装箱上开了一扇特定的窗户,让外部请求可以精准地找到里面的服务。

2.3 -v--volume (数据卷挂载)

这个选项用于创建数据卷挂载,允许你在宿主机和容器之间共享文件或目录。这对于数据的持久化存储(防止容器删除后数据丢失)以及共享配置文件非常重要。

语法: -v 宿主机路径:容器内路径

示例:

docker run -d -v /path/on/host:/path/in/container nginx:latest

这条命令将宿主机上的 /path/on/host 目录挂载到了容器内部的 /path/in/container 目录。在这两个目录中任何一端对文件所做的修改,都会立刻实时同步到另一端。

  • 命名卷 (Named Volumes): 你还可以使用由 Docker 统一管理的“命名卷”,这也是官方推荐的数据持久化方式。 首先,创建一个卷:
docker volume create my-nginx-data

然后,挂载这个卷:

docker run -d -v my-nginx-data:/usr/share/nginx/html nginx:latest
  • 这里将命名卷 my-nginx-data 挂载到了容器内的 /usr/share/nginx/html。即使这个容器被停止或删除了,存在这个卷里的数据也依然安然无恙。
  • 只读挂载: 如果你只想让容器读取数据而不能修改它,可以加上 :ro (read-only) 标志:-v /path/on/host:/path/in/container:ro

为什么使用它?

   容器本身是“阅后即焚”的,删除容器数据就没了。卷(Volumes)为你提供了持久化存储数据的机制,是运行数据库、保存配置文件或任何需要永久保存数据的应用的必备技能。

2.4 -e--env (环境变量)

这个选项用于在容器内部设置环境变量。通过环境变量来动态配置应用程序是云原生时代最标准的做法。

语法: -e 变量名=变量值

示例:

docker run -d -e MYSQL_ROOT_PASSWORD=mysecretpassword mysql:latest

这条命令启动了一个 MySQL 数据库容器,并将名为 MYSQL_ROOT_PASSWORD 的环境变量设置为了 mysecretpassword。MySQL 容器在启动时会读取这个变量,并把它的 root 用户密码设为你指定的值。

  • 设置多个变量: 你可以多次使用 -e 选项来同时注入多个环境变量:
docker run -d -e MYSQL_ROOT_PASSWORD=mysecretpassword -e MYSQL_DATABASE=mydatabase mysql:latest

为什么使用它?

   环境变量让你无需去修改镜像内部的代码或配置文件,就能从外部改变程序的行为。这使得同一个镜像可以非常方便地在开发、测试和生产等不同环境中灵活部署。

2.5 --name (容器命名)

为你的容器指定一个好记的名称。如果不加这个参数,Docker 会随机生成一个由形容词和科学家名字组成的搞笑名字。指定有意义的名称会让管理变得轻松许多。

示例:

docker run -d --name my-nginx-container nginx:latest

容器启动后,名字就是 my-nginx-container。之后你可以直接用这个名字(而不是那一长串难记的 ID)来停止、启动或检查这个容器。

为什么使用它?

   名字比无意义的字母数字组合 ID 好记一百倍,极大地提升了日常运维的效率。

2.6 --rm (自动删除)

当容器停止运行退出时,自动将其删除。这对于那些用完即走、不需要保留状态的“一次性”临时容器非常有用。

示例:

docker run --rm ubuntu:latest /bin/bash -c "echo 'Hello, world!' && sleep 5"

这条命令基于 ubuntu:latest 镜像启动容器,执行一句打印文字和睡眠 5 秒的脚本 echo 'Hello, world!' && sleep 5。等 5 秒钟脚本执行完毕后,容器会自动退出,并且 --rm 参数会让 Docker 立刻把它清理得干干净净。

为什么使用它?

   --rm 可以有效防止你的系统中堆积大量无用的废弃容器,保持系统的整洁。

2.7 -it--interactive --tty (交互模式)

这两个参数通常连在一起用,它会在容器内部为你打开一个交互式的命令行终端。这对于调试问题或者在容器里随便逛逛探索一下环境简直是神器。

示例:

docker run -it ubuntu:latest /bin/bash

这条命令启动了一个 Ubuntu 容器,并直接把你带进了容器内部的 Bash 终端里。现在,你可以像登录了一台远程 Linux 服务器一样,在里面自由地执行命令了。

为什么使用它?

    -it 提供了一个直接与容器文件系统和运行进程交互的窗口,是排查故障、测试环境时最常用的手段。

2.8 --network (网络连接)

将容器连接到一个特定的 Docker 网络 中。Docker 网络使得不同的容器之间可以互相通信。

示例:

# 首先,创建一个自定义网络
docker network create my-network

# 启动 web 容器,并加入该网络
docker run -d --name web --network my-network nginx:latest

# 启动 db 容器,并加入该网络
docker run -d --name db --network my-network mysql:latest

这段代码创建了一个名为 my-network 的网络,并将 webdb 两个容器都放进了这个网络里。神奇的是,它们现在可以使用对方的容器名称作为主机名(Hostname)来进行通信了(例如:web 容器内部的代码可以直接用 db 这个名字连接到数据库容器)。

为什么使用它?

   Docker 网络为你提供了一种隔离不同应用、并安全管理容器间互相访问的机制。我们将在第 4 模块深入讲解 Docker 网络。

2.9 --restart (重启策略)

这个选项定义了容器的重启策略。它告诉 Docker 当容器内的程序意外崩溃或者退出时,Docker 应该怎么做。

常见可选值:

  • no: 无论发生什么,绝不自动重启(这是默认值)。
  • on-failure: 只有当容器以非零状态码(代表异常错误)退出时,才去重启它。
  • always: 无论容器因为什么原因退出(包括正常退出),统统无条件重启它。如果在 Docker 服务启动时容器是停止的,也会被拉起。
  • unless-stopped: 除非是你手动通过命令把它停止了,否则只要退出就会一直尝试重启它。

示例:

docker run -d --restart always nginx:latest

这条命令启动的 Nginx 容器就像有了“不死之身”,不管因为什么原因挂掉,Docker 都会立刻重新启动它。

为什么使用它?

   重启策略是保障线上应用高可用性(HA)的关键手段,确保即使程序崩溃或服务器重启,你的服务依然在线。

3. 综合实战演练

现在,让我们把刚刚学到的各种选项组合起来,看看在真实的生产场景中 docker run 是怎么发威的。

3.1 运行带有持久化和自定义配置的 Redis 容器

docker volume create redis-data

docker run -d --name my-redis \
  -p 6379:6379 \
  -v redis-data:/data \
  -e REDIS_PASSWORD=mysecretpassword \
  --restart unless-stopped \
  redis:latest redis-server --requirepass mysecretpassword

解析:

  1. 创建了一个名为 redis-data 的持久化数据卷。
  2. 以后台模式 (-d) 运行一个名为 my-redis (--name) 的 Redis 容器。
  3. 映射宿主机的 6379 端口到容器内的 6379 端口 (-p)。
  4. 将刚刚创建的卷挂载到容器内部专门存放数据的 /data 目录下 (-v)。
  5. 注入环境变量以防万一 (-e)。
  6. 设置了只要不是人为干预停止,就自动重启 (--restart unless-stopped)。
  7. 最后,给容器内的默认命令传递了附加参数:让 Redis 服务器启动并强制开启密码验证 (redis-server --requirepass mysecretpassword)。

3.2 运行 Python Web 应用 (端口映射 + 环境变量)

首先,编写一个极简的 Python Web 程序代码(保存为 app.py):

from flask import Flask
import os

app = Flask(__name__)

@app.route("/")
def hello():
    name = os.environ.get('NAME', 'World')
    return f"Hello, {name}!"

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

接着,编写对应的 Dockerfile(定义如何打包这个应用):

FROM python:3.9-slim-buster
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
ENV FLASK_APP=app.py
CMD ["flask", "run", "--host=0.0.0.0"]

创建一个依赖列表文件 requirements.txt

Flask

现在,开始构建属于你自己的镜像:

docker build -t my-python-app .

最后,见证奇迹的时刻,运行你的容器:

docker run -d --name my-app -p 5000:5000 -e NAME=Docker my-python-app

这条命令在后台运行了你刚写的 Python 程序,将服务端口 5000 映射到了宿主机,并且巧妙地通过环境变量 -e NAME=Docker 改变了代码中的输出逻辑。现在打开浏览器访问 http://localhost:5000,你将会看到界面上显示的是自定义的 "Hello, Docker!"。