从零开始学 Docker run:核心命令与实践 – wiki基地


从零开始学 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 Engine (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 镜像的默认 COMMANDsh。我们可以直接覆盖它:

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 <大小>:限制容器可以使用的内存量(例如 512m1g)。
  • --cpus <数量>:限制容器可以使用的 CPU 核心数量(例如 0.52)。

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 -tulnpss -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 的强大功能将为你打开全新的开发和部署世界。祝你在容器化的道路上一帆风顺!

发表评论

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

滚动至顶部