让我们从最基础的概念开始逐步理解。假设你已经准备好了docker 环境。
第一步,让我们先通过实际操作来看看当前系统中的镜像(images)和容器(containers)状态:
docker images # 查看所有镜像
docker ps -a # 查看所有容器(包括未运行的)
输出结果:
REPOSITORY TAG IMAGE ID CREATED SIZE
linqiu1199/trae2api v1.0.1 0d72ae59193b 4 days ago 20.4MB
rustdesk/rustdesk-server latest 9227b43758be 4 weeks ago 12.8MB
mysql latest 3e34946bc4c4 4 weeks ago 797MB
hello-world latest 74cc54e27dc4 4 weeks ago 10.1kB
redis latest fa310398637f 6 weeks ago 117MB
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4fa63eb76d16 linqiu1199/trae2api:v1.0.1 "./main" 16 hours ago Up 3 hours 0.0.0.0:17080->17080/tcp, :::17080->17080/tcp trae2api
25e6b341f3cb rustdesk/rustdesk-server:latest "hbbs" 10 days ago Exited (0) 18 hours ago hbbs
239edf233492 rustdesk/rustdesk-server:latest "hbbr" 10 days ago Exited (0) 18 hours ago hbbr
84fbae57a78c redis "docker-entrypoint.s…" 12 days ago Up 3 hours 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
48b0ee678e43 mysql "docker-entrypoint.s…" 12 days ago Up 3 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
[root@localhost ~]#
镜像(Images)和容器(Containers)的关系:
举一个很好的例子 - redis:
镜像:您系统中有一个 redis (latest) 镜像,大小为 117MB
容器:基于这个镜像运行了一个容器(ID: 84fbae57a78c),正在运行中(Status: Up 3 hours)
让我们通过一个实际操作来理解这个关系。我建议执行以下命令来创建另一个 redis 容器:
docker run --name redis-test -d redis
执行这个命令,然后再次运行:
docker ps -a
- 同一个镜像可以创建多个容器
- 每个容器都是镜像的运行实例
有点类似与JavaSE 中的 类和对象,让我用面向对象的概念来进一步解释:
镜像(Image) => 类(Class)
- 是一个模板
- 包含了所有必要的代码和依赖
- 是只读的
- 可以被多次使用来创建不同的容器
- 不能直接运行,需要实例化成容器
容器(Container) => 对象(Object)
- 是镜像的运行实例
- 有自己的状态和数据
- 可以启动、停止、删除
- 多个容器可以基于同一个镜像创建
- 每个容器都是独立的运行环境
让我们通过实际操作来验证这个概念:
# 基于同一个 redis 镜像再创建一个容器,但使用不同的名字和端口
docker run --name redis-test2 -d -p 6380:6379 redis
- 执行这个命令的时候,你是否会有这样的疑惑呢?由于之前在Docker装过了redis,在主机端口,设置了6380, 这样避开了6379 端口。是考虑到的,但是 容器端口 还是 6379 这样合理吗?
答: 这里的关键点是:
- 容器是完全隔离的环境,每个容器都有自己的网络空间
- 不同容器可以使用相同的内部端口(6379)
- 但宿主机的端口不能重复
然后查看结果:
docker ps -a | grep redis
oot@localhost ~]# docker ps -a | grep redis
a31ec1598cf9 redis "docker-entrypoint.s…" 9 seconds ago Up 8 seconds 0.0.0.0:6380->6379/tcp, :::6380->6379/tcp redis-test2
84fbae57a78c redis "docker-entrypoint.s…" 12 days ago Up 4 hours 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
[root@localhost ~]#
执行后,应该能看到多个 redis 容器(对象),但它们都是基于同一个 redis 镜像(类)创建的。
docker run & docker start
- 先查看当前系统中所有的容器状态,包括已停止的容器。请执行以下命令:
docker ps -a
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a31ec1598cf9 redis "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:6380->6379/tcp, :::6380->6379/tcp redis-test2
4fa63eb76d16 linqiu1199/trae2api:v1.0.1 "./main" 16 hours ago Up 4 hours 0.0.0.0:17080->17080/tcp, :::17080->17080/tcp trae2api
25e6b341f3cb rustdesk/rustdesk-server:latest "hbbs" 10 days ago Exited (0) 18 hours ago hbbs
239edf233492 rustdesk/rustdesk-server:latest "hbbr" 10 days ago Exited (0) 18 hours ago hbbr
84fbae57a78c redis "docker-entrypoint.s…" 12 days ago Up 4 hours 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
48b0ee678e43 mysql "docker-entrypoint.s…" 12 days ago Up 4 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
[root@localhost ~]#
- 下一步,我们将使用
docker run
命令来创建一个新的容器。请执行以下命令:
docker run --name new-redis -d -p 6381:6379 redis
这个命令会基于 redis 镜像创建并启动一个新的容器。
执行完后,请再次运行 docker ps -a
,是否看到了一个名为 “new-redis” 的新容器,以及它的状态如何。
AINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
751466ad80a1 redis "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:6381->6379/tcp, :::6381->6379/tcp new-redis
a31ec1598cf9 redis "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 0.0.0.0:6380->6379/tcp, :::6380->6379/tcp redis-test2
4fa63eb76d16 linqiu1199/trae2api:v1.0.1 "./main" 16 hours ago Up 4 hours 0.0.0.0:17080->17080/tcp, :::17080->17080/tcp trae2api
25e6b341f3cb rustdesk/rustdesk-server:latest "hbbs" 10 days ago Up About a minute hbbs
239edf233492 rustdesk/rustdesk-server:latest "hbbr" 10 days ago Exited (0) 18 hours ago hbbr
84fbae57a78c redis "docker-entrypoint.s…" 12 days ago Up 4 hours 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
48b0ee678e43 mysql "docker-entrypoint.s…" 12 days ago Up 4 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
[root@localhost ~]#
总结一下关键点:
-
docker start
(我们用在 hbbs 容器上):- 启动了一个已存在但停止的容器
- 没有创建新的容器
- 容器 ID 和名称保持不变
-
docker run
(我们用来创建 new-redis):- 创建了一个全新的容器 (ID: 751466ad80a1)
- 自动启动了这个新容器
- 使用了我们指定的名称 (new-redis)
- 映射了新的端口 (6381)
下一步,让我们更深入地理解这两个命令的使用场景。请尝试以下操作:
- 停止 new-redis 容器:
docker stop new-redis
- 然后尝试使用
docker run
再次创建一个同名的容器:
docker run --name new-redis -d -p 6381:6379 redis
[root@localhost ~]# docker run --name new-redis -d -p 6381:6379 redis
docker: Error response from daemon: Conflict. The container name "/new-redis" is already in use by container "751466ad80a1e95003b7df5370ec4657c95e6dab87b927e7ea01104ed8aa2081". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
[root@localhost ~]#
docker run
尝试创建一个新容器,但失败了。- 错误原因是容器名 “new-redis” 已经被使用。
- Docker建议我们要么移除旧容器,要么重命名,才能重用这个名字。
这个结果进一步说明了 docker run
和 docker start
的区别:
docker run
总是尝试创建新容器,即使容器已停止。docker start
则用于启动已存在的容器。
下一步,让我们尝试使用 docker start
来启动已停止的 new-redis 容器:
docker start new-redis
成功运行!!