您现在的位置是:首页 >技术教程 >Docker镜像与Docker容器详解网站首页技术教程

Docker镜像与Docker容器详解

凤凰战士芭比Q 2024-08-16 12:01:02
简介Docker镜像与Docker容器详解

image-20230602194114293

Docker帮助信息

[root@localhost ~]# docker --help     # 显示Docker的帮助信息
[root@localhost ~]# docker version    # 显示Docker的版本信息
[root@localhost ~]# docker info       # 显示Docker的系统信息

官方文档

https://docs.docker.com/engine/reference/commandline/docker/

里面可以查询Docker的命令如下图

image-20230602200957234

Docker镜像

拉取镜像

Docker主机安装之后,本地并没有镜像可以使用 docker images列出Docker本地存储的镜像。

该命令会显示镜像的名称标签镜像 ID创建时间所占用的空间等信息

[root@localhost ~]# docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

可以看到吗,确实一个镜像都没有,我们可以先拉取一个镜像使用docker image pull <想要拉取的镜像名称>,如果你想在Docker主机使用最新的mysql镜像,那么你就可以通过如下方式拉取他

[root@localhost ~]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
72a69066d2fe: Pull complete 
93619dbc5b36: Pull complete 
99da31dd6142: Pull complete 
626033c43d70: Pull complete 
37d5d7efb64e: Pull complete 
ac563158d721: Pull complete 
d2ba16033dad: Pull complete 
688ba7d5c01a: Pull complete 
00e060b6d11d: Pull complete 
1c04857f594f: Pull complete 
4d7cfa90e6ea: Pull complete 
e0431212d27d: Pull complete 
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest

当执行 docker pull mysql 命令时,Docker 首先会从 Docker Hub 中下载最新版本的 mysql 镜像。该镜像的默认标签为 latest。执行该命令后,会输出以下详细信息:

  1. Using default tag: latest:表示使用的是 MySQL 镜像的默认标签 latest
  2. latest: Pulling from library/mysql:表示正在从 Docker Hub 的 library/mysql 仓库中拉取 MySQL 镜像的最新版本。
  3. 72a69066d2fe: Pull completee0431212d27d: Pull complete:这些行表示 Docker 正在从远程仓库下载 MySQL 镜像的各个层,每个层都是一个只读的文件系统。当一个镜像层下载完成后,会输出 Pull complete
  4. Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709:表示 MySQL 镜像的摘要信息,该信息包含了镜像的完整性校验信息。
  5. Status: Downloaded newer image for mysql:latest:表示已经成功下载最新版本的 MySQL 镜像。
  6. docker.io/library/mysql:latest:表示 MySQL 镜像的完整名称,其中 docker.io 是默认的 Docker 镜像仓库地址,library 是默认的镜像库名称,mysql:latest 是镜像的名称和标签。
[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               latest              3218b38490ce        17 months ago       516M

就像你们所看到的一样,刚才拉取的镜像已经存在于Docker主机本地仓库中。

镜像仓库

Docker镜像存储在镜像仓库服务当中,Docker客户端的镜像仓库服务是可以配置的,默认使用Docker Hub

镜像仓库服务包含多个镜像仓库,同样,一个镜像仓库中可以包含多个镜像。如下图

image-20230602205711080

其中每个镜像仓库中都包含一个或多个镜像

官方与非官方镜像仓库

Docker Hub 是 Docker 官方的镜像仓库,提供了各种类型的镜像,包括官方仓库非官方仓库

官方仓库:是由 Docker 官方团队维护的镜像仓库,这些镜像被广泛使用,因为它们被认为是可靠和安全的。官方仓库中包括了各种不同类型的镜像,如操作系统镜像,应用程序镜像等。

非官方仓库:是由其他人或组织创建和维护的镜像仓库,这些镜像可能没有经过官方团队的审核和验证,所以使用这些镜像需要谨慎。非官方仓库中的镜像可能存在安全漏洞或错误,因此使用这些镜像可能会导致系统不稳定或不安全。

使用非官方仓库中的镜像时,需要谨慎选择,尽可能选择受信任的镜像仓库,以避免潜在的安全风险。

拉取指定标签的镜像

在拉取镜像的时候只需要给出镜像的名字和标签,就能够在官方仓库中定位一个镜像(采用:分隔)。从官方仓库拉取镜像时,docker pull 命令格式如下

docker pull [选项] [镜像名[:标签名]]
选项:
-a:拉取镜像仓库中所有标签的镜像
-q:只输出镜像 ID

在之前的Linux示例中,我们拉取了mysql镜像但是没指定镜像标签,但是我想安装mysql5.7版本呢,执行如下命令

[root@localhost ~]# docker pull mysql:5.7
# 该命令会从官方mysql库拉取标签为5.7的镜像

如果,镜像后面没有指定具体的镜像标签,则Docker会假设用户想要拉取标签为latest的镜像

不要以为标有latest标签的镜像就是最新镜像,例如Alpine仓库中最新的镜像通常是edge,使用latest时要谨慎

从非官方仓库拉取镜像也是类似的,只需要在仓库名称前加上Docker Hub 的用户名或是组织名称。如下

[root@localhost ~]# docker pull ayuqaq/csqdiytomcat:v1.0
# 该命令会从我自己的Docker Hub账户为命名空间的下载一个mycentos的镜像标签为v1

如果你希望获取第三方镜像仓库服务从中获取镜像(非Docker hub),则需要在镜像仓库名称前加上第三方镜像仓库服务的DNS名称。

假设上的镜像位于阿里云容器镜像仓库服务中,如下图所示需要自己注册一下,就有了自己的镜像仓库了

image-20230603200549957

image-20230603201006502

image-20230603200941538

为镜像打多个标签

一个镜像可以根据用户的需求设置多个标签,因为标签是存放在镜像数据的任意数字或字符串。如下案例

在docker pull -a 参数拉取仓库中全部的镜像,通过docker images 查看已经拉取的镜像

[root@localhost ~]# docker pull -a nigelpoulton/tu-demo
[root@localhost ~]# docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
nigelpoulton/tu-demo   latest              c610c6a38555        23 months ago       58.1MB
nigelpoulton/tu-demo   v2                  6ba12825d092        23 months ago       58.1MB
nigelpoulton/tu-demo   v1                  c610c6a38555        23 months ago       58.1MB

当在拉取一个包含多个标签的镜像时,实际上只会下载一个镜像到本地主机上。

在这个例子中,我们拉取了一个包含了3个标签的镜像,但实际上只下载了2个镜像,其中一个镜像拥有两个标签。也就是说,这3个标签都指向了同一个镜像ID,即这3个标签所代表的镜像是完全一样的。可以通过查看IMAGE ID来确认这一点,可以看到v1和latest标签指向了相同的IMAGE ID,这意味着这两个标签属于相同的镜像。

因此,在实际使用中,可以根据需要选择使用哪个标签来代表所需的镜像。这也证明了latest是一个非强制标签,不保证指向仓库中最新的镜像

搜索镜像

docker search 命令用于在 Docker 镜像仓库中搜索指定的镜像。

其基本格式如下:

docker search [选项] [镜像名]
--filter=STARS=3000         # 搜索出来的镜像就是STARS大于3000的
--filter="is-official=true" # 仅显示官方镜像 

使用案例

假如我们要想要看看Docker Hub仓库里面的mysql镜像有多少

image-20230603205901155

如上镜像有那么多!我只想要看到官方提供的mysql镜像该怎么做呢?

image-20230603210009972

如果我想看到收藏总量在1000以上的mysql镜像呢

image-20230603211738322

镜像和分层

Docker镜像由一些松耦合的只读镜像层组成

image-20230603213522827

Docker负责堆叠这些镜像层,并且将他们表示为单个统一的对象

有多种方式可以查看和检查构成某个镜像的分层,我们先拉取一个镜像看看命令输出内容

[root@localhost ~]# docker pull nginx
Using default tag: latest
a2abf6c4d29d: Pull complete 
a9edb18cadd1: Pull complete 
589b7251471a: Pull complete 
186b1aaa4aa6: Pull complete 
b4df32aa5a72: Pull complete 
a0bcbecc962e: Pull complete 
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest

在上面输出中,以Pull complete结尾的每一行都代表了镜像中某个被拉取的镜像层。可以看到有6个镜像层。如下图,以图片的形式将镜像层ID作为表示展示了这些分层

image-20230604140021198

另一种查看镜像分层的方式是通过 docker inspect 命令。

[root@localhost ~]# docker inspect nginx:latest
[
    {
        "Id": "sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85",
        "RepoTags": [
            "nginx:latest"
..........
.........
.........
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f",
                "sha256:e379e8aedd4d72bb4c529a4ca07a4e4d230b5a1d3f7a61bc80179e8f02421ad8",
                "sha256:b8d6e692a25e11b0d32c5c3dd544b71b1085ddc1fddad08e68cbd7fda7f70221",
                "sha256:f1db227348d0a5e0b99b15a096d930d1a69db7474a1847acbc31f05e4ef8df8c",
                "sha256:32ce5f6a5106cc637d09a98289782edf47c32cb082dc475dd47cbf19a4f866da",
                "sha256:d874fd2bc83bb3322b566df739681fbd2248c58d3369cb25908d68e7ed6040a6"
            ]
        }
    }
]

上述案例内容是缩减后的,我们可以看到镜像包含了6个层,只不过这次输出内容中使用了镜像的SHA256散列值来表示镜像层。两种命令都显示了镜像包含了6个层。

docker history 命令显示了镜像的构建历史,但其并不是严格意义上的镜像分层。

例如,有些Dockerfile中的指令并不会创建新的镜像层。比如ENV、EXPOSE、CMD、以及ENTTRYPOINT,不过,这些命令会在镜像中添加元数据。

所有的Docker镜像起始于一个基础镜像层,当进行修改或增加新内容时,就会在当前镜像层之上,创建新的镜像层。

我们也可以查看一下怎么构建的镜像层,使用docker history命令来查看

[root@localhost ~]# docker history nginx
IMAGE          CREATED         CREATED BY                                       SIZE      COMMENT
605c77e624dd   17 months ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B        
<missing>      17 months ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT            0B        
<missing>      17 months ago   /bin/sh -c #(nop)  EXPOSE 80                     0B        
<missing>      17 months ago   /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B        
<missing>      17 months ago   /bin/sh -c #(nop) COPY file:09a214a3e07c919a…   4.61kB    
<missing>      17 months ago   /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB    
<missing>      17 months ago   /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0…   1.96kB    
<missing>      17 months ago   /bin/sh -c #(nop) COPY file:65504f71f5855ca0…   1.2kB     
<missing>      17 months ago   /bin/sh -c set -x     && addgroup --system -…   61.1MB    
<missing>      17 months ago   /bin/sh -c #(nop)  ENV PKG_RELEASE=1~bullseye    0B        
<missing>      17 months ago   /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.1         0B        
<missing>      17 months ago   /bin/sh -c #(nop)  ENV NGINX_VERSION=1.21.5      0B        
<missing>      17 months ago   /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B        
<missing>      17 months ago   /bin/sh -c #(nop)  CMD ["bash"]                  0B        
<missing>      17 months ago   /bin/sh -c #(nop) ADD file:09675d11695f65c55…   80.4MB 

image-20230604142553105

在添加额外镜像层的同时,镜像始终保持是当前所有镜像层的组合

每个镜像层都有一个唯一的 ID,称为 SHA256 ID,它是根据该镜像层的内容计算出来的。Docker 会根据所有镜像层的 SHA256 ID 计算出一个唯一的镜像 ID,这个镜像 ID 对应的就是当前所有镜像层的组合。因此,无论你添加了多少额外镜像层,镜像的 ID 都是固定的,只有镜像的大小和 SHA256 ID 发生变化。这样的设计也方便了 Docker 镜像的版本管理和复用,可以方便地基于已有的镜像层构建新的镜像。

如下图,举例,每个镜像层包含了三个文件,而镜像包含了来自两个镜像层的6个文件

image-20230604144648934

如下图展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层的文件7是文件5的一个更新版本。这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得忘记的更新版本作为一个新镜像层添加到镜像当中。

image-20230604164115552

Docker 通过存储引擎的方式来实现镜像层堆栈,保证多镜像层对外展示位统一的文件系统。

如下图展示了显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。

image-20230604164526214

共享镜像层

多个镜像之间可以并且确实会共享镜像层。这样可以有效节省空间并提升性能

拉取nigelpoulton/tu-demo 的全部标签镜像使用 docker pull -a

[root@localhost ~]# docker pull -a nigelpoulton/tu-dem
latest: Pulling from nigelpoulton/tu-demo
5843afab3874: Pull complete 
2dfaacf7024e: Pull complete 
2dbba127c6aa: Pull complete 
1a467efa2204: Pull complete 
865369a531d5: Pull complete 
d2a40eab4219: Pull complete 
b7a31967f5c1: Pull complete 
2660d2ef4202: Pull complete 
05b2eaa6d9c1: Pull complete 
Digest: sha256:af111729d35a09fd24c25607ec045184bb8d76e37714dfc2d9e55d13b3ebbc67
v1: Pulling from nigelpoulton/tu-demo
5843afab3874: Already exists 
2dfaacf7024e: Already exists 
2dbba127c6aa: Already exists 
1a467efa2204: Already exists 
865369a531d5: Already exists 
d2a40eab4219: Already exists 
b7a31967f5c1: Already exists 
2660d2ef4202: Already exists 
ba76edbc4091: Pull complete 
Digest: sha256:fa07d8c1e404df50aa7acadd23176fb9f688ab1b4338a795f115033e9e6a9c71
v2: Pulling from nigelpoulton/tu-demo
Digest: sha256:af111729d35a09fd24c25607ec045184bb8d76e37714dfc2d9e55d13b3ebbc67
Status: Downloaded newer image for nigelpoulton/tu-demo
docker.io/nigelpoulton/tu-demo

注意那些以Already exists 结尾的行

在使用 Docker 拉取镜像时,Docker 可以通过镜像的层次结构和哈希值来判断哪些镜像层已经在本地存在,从而避免重复下载。在本例中,Docker 首先尝试拉取标签为 latest 的镜像,如果本地已经存在了这个镜像的某些层次结构,那么 Docker 就会直接使用本地的镜像层,从而加快拉取速度。当 Docker 拉取标签为 v1 和 v2 的镜像时,由于这两个镜像都是基于前面镜像构建的,因此在拉取 v1 和 v2 镜像时,Docker 只需要下载缺失的镜像层,而不需要重新下载已经存在的镜像层,从而避免了重复下载和节省了时间和带宽。

根据摘要拉取镜像

上面介绍了如何通过标签来拉取镜像,这也是常见的方式。但问题是,标签是可以变的,意味着可能偶尔会出现给镜像打错标签的情况,有时甚至会给新镜像打一个已经存在的标签。这些都是有可能导致的问题

假设镜像 golftrack:1.5 存在一个已知的BUG。因此可以拉取该镜像后修复它,并使用相同的标签将更新的镜像重新推送回仓库。

镜像golftrack:1.5存在 Bug,这个镜像已经应用于生产环境。如果创建一个新版的镜像,并修复了bug。那么问题来了,构建新镜像并将其推送回仓库时使用了与问题镜像相同的标签,原镜像被覆盖,但生产环境种遗留了大量运行种的容器,没有上面好办法区分正在使用的镜像版本是修复前还是修复后的,因为两个镜像的标签是相同的

这时候就要用到镜像摘要了

每个镜像现在都有一个基于其内容的密码散列值。摘要代指的是散列值,因为摘要是镜像内容的一个散列值,所以镜像内容的变更一定导致散列值的改变

每次拉取镜像,摘要都会作为docker pull 命令返回代码的一部分。只需要在 docker images 命令之后1添加 --digests 参数即可在本地查看镜像摘要。

[root@localhost ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Pull complete 
c7a4e4382001: Pull complete 
4044b9ba67c9: Pull complete 
c8388a79482f: Pull complete 
413c8bb60be2: Pull complete 
1abfd3011519: Pull complete 
Digest: sha256:db485f2e245b5b3329fdc7eff.....9d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
REPOSITORY          TAG                 DIGEST                                                                    IMAGE ID            CREATED             SIZE
redis               latest              sha256:db485f2e245b5b3329.....3e09d8feb9ca720788059fdc2ed8339   7614ae9453d1        17 months ago       113MB

从上面的代码片段可知,redis 签名值如下

sha256:db485f2e245b5b3329…3e09d8feb9ca720788059fdc2ed8339

现在我们已知镜像摘要,那么可以使用摘要值再次拉取这个镜像。这种方法可以确保准确拉取想要的镜像

我们使用 docker rmi 删除redis:latest镜像,然后显示如何通过摘要而不是标签来再次拉取该镜像

[root@localhost ~]# docker rmi  redis
Untagged: redis:latest
Untagged: redis@sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Deleted: sha256:7614ae9453d1d87e740a2056257a6de7135c84037c367e1fffa92ae922784631
Deleted: sha256:49c70179bc923a7d48583d58e2b6c21bde1787edf42ed1f8de9e9b96e2e88e65
Deleted: sha256:396e06df5d1120368a7a8a4fd1e5467cdc2dd4083660890df078c654596ddc1c
Deleted: sha256:434d118df2e9edb51238f6ba46e9efdfa21be68e88f54787531aa39a720a0740
Deleted: sha256:2047f09c412ff06f4e2ee8a25d105055e714d99000711e27a55072e640796294
Deleted: sha256:13d71c9ccb39b206211dd1900d06aa1984b0f5ab8abaa628c70b3eb733303a65
Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f

[root@localhost ~]# docker pull redis@sha256:db485f2e245b5b3......f913e09d8feb9ca720788059fdc2ed8339
sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339: Pulling from library/redis
a2abf6c4d29d: Pull complete 
c7a4e4382001: Pull complete 
4044b9ba67c9: Pull complete 
c8388a79482f: Pull complete 
413c8bb60be2: Pull complete 
1abfd3011519: Pull complete 
Digest: sha256:db485f2e245b5b3......f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis@shasha256:db485f2e245b5b3......f913e09d8feb9ca720788059fdc2ed8339
docker.io/library/redissha256:db485f2e245b5b3......f913e09d8feb9ca720788059fdc2ed8339

删除镜像

当你不在需要某个镜像的时候,可以通过docker rmi 命令从docker 主机删除该镜像。其中,rmi是remove image 的缩写。

删除操作会在当前主机上删除该镜像以及相关的镜像层。这意味着无法通过docker images 命令看到删除后的镜像,并且对应的包含镜像层数据的目录也会被删除。但是,如果某个镜像层被多个镜像共享,那只用当全部依赖该镜像层的镜像都删除后,该镜像层才会被删除。

如下案例

# 语法如下
docker rmi [OPTIONS] IMAGE [IMAGE...]
参数:
OPTIONS:可选参数,用于指定删除镜像的选项。
IMAGE:必需参数,用于指定要删除的镜像的名称或ID。
选项:
-f 或 --force:强制删除正在使用的镜像。
-q 或 --quiet:只显示删除的镜像ID,不显示其他信息。
# 使用案例如下
[root@localhost ~]# docker rmi 7614ae9453d1
Untagged: redis@sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Deleted: sha256:7614ae9453d1d87e740a2056257a6de7135c84037c367e1fffa92ae922784631
Deleted: sha256:49c70179bc923a7d48583d58e2b6c21bde1787edf42ed1f8de9e9b96e2e88e65
Deleted: sha256:396e06df5d1120368a7a8a4fd1e5467cdc2dd4083660890df078c654596ddc1c
Deleted: sha256:434d118df2e9edb51238f6ba46e9efdfa21be68e88f54787531aa39a720a0740
Deleted: sha256:2047f09c412ff06f4e2ee8a25d105055e714d99000711e27a55072e640796294
Deleted: sha256:13d71c9ccb39b206211dd1900d06aa1984b0f5ab8abaa628c70b3eb733303a65
Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f

如果删除的镜像上面存在着运行的容器,那么操作不会被允许的,在执行删除镜像命令之前需要停止并删除该镜像相关的全部容器。

一个命令删除Docker主机上面全部镜像

[root@localhost ~]# docker rmi -f $(docker images -q) 

总结

  • ==docker pull == 是下载镜像的命令。镜像从远程镜像服务的仓库中下载。默认情况下,镜像会从Docker Hub的仓库中拉取。docker pull redis:latest 命令会从Docker Hub 的redis仓库中拉取标签为 latest 的镜像
  • ==docker images ==列出了本地Docker 主机上存储的镜像。可以通过–digests参数来查看镜像的SHA256签名
  • docker inspect 命令非常有用,该命令完美展示了镜像的细节,包括镜像层数据和元数据
  • ==docker rmi ==命令用于删除镜像。 docker rmi redis:latest 命令含以是删除redis:latest 镜像。当镜像存在关联的容器,并且容器处于运行或停止(Exited)状态时,不允许删除该镜像

Docker容器

启动一个简单的容器

启动容器的一个简单的方式是通过 docker run 命令 下面的命令启动了一个简单的容器,其中运行了容器化版本的Linux Centos

[root@localhost ~]# docker run -it centos:latest /bin/bash
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
a1d0c7532777: Already exists 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
[root@03b21aa04f62 /]# 

解释如下:

  • docker run:运行Docker容器的命令。
  • -it:以交互模式启动容器,并打开一个终端。
  • centos:latest:要启动的镜像名称和版本号。
  • /bin/bash:在容器中执行的命令,这里表示打开一个Bash shell终端。
  • Unable to find image 'centos:latest' locally:表示本地没有该镜像,需要从Docker Hub上下载。
  • latest: Pulling from library/centos:从Docker Hub上拉取 centos:latest 镜像。
  • a1d0c7532777: Already exists:表示本地已经存在该镜像的某些层,无需重新下载。
  • Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177:镜像的唯一标识符。
  • Status: Downloaded newer image for centos:latest:镜像下载成功。
  • [root@03b21aa04f62 /]#:容器启动成功,进入容器的Bash shell终端,标志符 root@03b21aa04f62 表示当前所在的容器ID,/ 表示当前所在的路径。

命令的基础格式为

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 参数
# OPTIONS:可选参数,用于指定容器的配置选项。
# IMAGE:必需参数,用于指定要启动的镜像名称或ID。
# COMMAND:可选参数,用于指定容器启动后要执行的命令。
# ARG:可选参数,用于指定传递给容器的参数。
# 常用选项
# -d 或 --detach:在后台运行容器,并返回容器ID。
# -it:在容器中打开一个交互式终端。
# --name:为容器指定一个名称。
# -p:将容器的端口映射到主机的端口。
# -v:将主机的目录或文件挂载到容器中。
# --env:设置容器的环境变量。

上述案例中,本地缓存并未包含该镜像,所以Docker 接下来查询在 Docker Hub中是否存在对应镜像。找到该镜像后,Docker将镜像拉取到本地,一旦镜像拉取到本地,daemon就创建容器并在其中运行指定的应用。

如果仔细观察,就会发现Shell提示符发生了变化,说明目前已经位于容器内部了。

可以在容器内执行一些基础命令,可能有些命令无法正常工作,这是因为容器大部分镜像都是经过优化的,这意味着某些命令或包可能没有安装。如下命令一个执行成功一个执行失败

[root@b1435fc61e1a /]# ls -l 
total 0
lrwxrwxrwx.   1 root root   7 Nov  3  2020 bin -> usr/bin
drwxr-xr-x.   5 root root 360 Jun  5 02:55 dev
drwxr-xr-x.   1 root root  66 Jun  5 02:55 etc
....
....
[root@b1435fc61e1a /]# ifconfig
bash: ifconfig: command not found

容器进程

在上面启动CentOS容器时,让容器运行 Bash Shell (/bin/bash)。这使得Bash Shell 成为容器中运行的且唯一运行的进程。可以使用 ps -elf 命令在容器内部查看

[root@b1435fc61e1a /]# ps -elf
F S UID         PID   PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root          1      0  0  80   0 -  3035 do_wai 02:55 pts/0    00:00:00 /bin/bash
0 R root         28      1  0  80   0 - 11163 -      06:39 pts/0    00:00:00 ps -elf

上面的输出中看起来好像由两个正在运行的进程,起始并非如此。PID为1的进程,是容器被告知要运行的Bash shell,第二个进程是ps -elf 命令产生的,这是个临时进程,并且在输出后就已经退出了。也就是说,这个容器当前只允许了一个进程——/bin/bash

这意味着如果通过 exit 退出 Bash Shell,那么容器也会退出(终止)。原因是容器如果不运行任何进程则无法存在——杀死Bash Shell即杀死了容器唯一运行的进程,导致这个容器也被杀死。

按下 Ctrl + PQ 组合键则会退出容器但并不会终止容器运行。这样做会切回到Docker主机的Shell,并保持容器在后台运行

[root@localhost ~]# docker ps
CONTAINER ID  IMAGE   COMMAND    CREATED    STATUS    PORTS       NAMES
b1435fc61e1a   centos:latest     "/bin/bash"    4 hours ago   Up 4 hours    tender_hugle

当前容器仍然在运行,并且可以通过 docker exec 命令重新连接到Docker

docker exec 命令用于在运行中的容器中执行命令。)

[root@localhost ~]# docker exec -it b1435fc61e1a /bin/bash
[root@b1435fc61e1a /]# 

正如你所见,我们使用了docker exec 在运行中的容器执行了 /bin/bash 命令。如果再次运行ps命令,就会看到两个Bash

[root@b1435fc61e1a /]# ps -elf
F S UID         PID   PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root          1      0  0  80   0 -  3035 poll_s 02:55 pts/0    00:00:00 /bin/bash
4 S root         30      0  0  80   0 -  3009 do_wai 06:49 pts/1    00:00:00 /bin/bash
0 R root         43     30  0  80   0 - 11163 -      06:53 pts/1    00:00:00 ps -elf

这是因为docker exec 执行了形的Bash进程并连接到了容器。这意味着 在当前shell输入 exit 并不会导致容器的终止,因为原Bash 进程还在运行当中

[root@b1435fc61e1a /]# exit
exit
[root@localhost ~]# docker ps
CONTAINER ID  IMAGE     COMMAND   CREATED    STATUS   PORTS   NAMES
b1435fc61e1a    centos:latest   "/bin/bash"  4 hours ago     Up 4 hours  tender_hugle
# 果然还在运行

如果要删除容器

(docker stop )

(docker rm )

-f:强制删除容器。

# 先停止容器
[root@localhost ~]# docker stop b1435fc61e1a
b1435fc61e1a
# 然后删除容器
[root@localhost ~]# docker rm b1435fc61e1a
b1435fc61e1a
# 查看容器是否存在
[root@localhost ~]# docker ps
CONTAINER ID    IMAGE  COMMAND   CREATED   STATUS     PORTS   NAMES
# 删除成功

容器生命周期

前面介绍了如何使用docker run 命令来启动容器,接下来会重新启动一个新的容器,这样就可以观察其完整的生命周期。

[root@localhost ~]# docker run --name chenshiren -it centos:latest /bin/bash
[root@b20d513bf4fc /]# 

创建新的容器,名称为"chenshiren"

接下来把该容器投入使用,将一部分数据写入其中,在新容器内部shell中,执行下面的步骤来将部分数据写入到 tmp 目录下的某个文件中,确认数据是否写入成功

[root@b20d513bf4fc /]# cd tmp/
[root@b20d513bf4fc tmp]# ls -al
total 12
....
....
[root@b20d513bf4fc tmp]# echo "DevOps FTW" > newfile
[root@b20d513bf4fc tmp]# cat newfile 
DevOps FTW

按下 Ctrl + PQ组合键退出容器

现在使用 docker stop 命令来停止运行容器

[root@localhost ~]# docker stop chenshiren
chenshiren
[root@localhost ~]# docker ps
CONTAINER ID    IMAGE      COMMAND     CREATED   STATUS  PORTS     NAMES

新建的容器没有查看到,因为你停止了,加上-a参数再次运行前面的命令,就会显示出全部的容器,包括停止状态的

[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE    COMMAND   CREATED  STATUS   PORTS      NAMES
b20d513bf4fc   centos:latest  "/bin/bash"    14 minutes ago  Exited (0) 10 minutes ago chenshiren

可以看到容器状态为 Exited(0) 停止容器就像停止虚拟机一样,尽管已经停止运行,容器的全部配置内容仍保存在 Docker 主机文件系统中,可以随时启动

[root@localhost ~]# docker start chenshiren
chenshiren
[root@localhost ~]# docker ps
CONTAINER ID  IMAGE   COMMAND    CREATED   STATUS    PORTS      NAMES
b20d513bf4fc    centos:latest    "/bin/bash"  17 minutes ago   Up 2 seconds   chenshiren

现在停止的容器已经重新启动了,此时确认之前创建的文件是否存在

[root@localhost ~]# docker exec -it chenshiren /bin/bash
[root@b20d513bf4fc /]# cat tmp/newfile 
DevOps FTW

之前创建的文件依然存在,着证明了停止容器运行并不会损毁容器或其中的数据

总结一下容器的生命周期,可以根据需要多次停止、启动、暂停以及重启容器,但是容器及其数据是安全的。直至明确删除容器前,容器都不会丢弃其中的数据

下面来介绍一下为什么推荐两阶段方式来停止退出容器

优雅的退出容器

当使用docker rm -f 来销毁容器时,不会发出任何的告警。这个过程很暴力,容器会被毫无征兆地被销毁,会令容器和应用猝不及防,来不及“处理后事”

但是 docker stop 命令就会礼貌很多,该命令会给容器内进程发送要停止的警告信息,给进程机会处理停止前要做的事情,一旦docker stop 命令返回,就可以使用docker rm 删除容器了

想要怎么用大家可以选择

利用重启策略进行容器的自我修复

通常建议在运行容器时配置好重启策略。这是容器的一种自我修复能力,可以在指定时间或者错误后重启来完成自我修复。

重启策略应用于每个容器,可以作为参数被强制传入docker run 命令中,或者在 compose 文件声明中。

容器支持的重启策略包括:always、unless-stopped、on-failed。

always策略是一种简单的方式。除非容器被明确停止,比如通过 docker stop 命令,否则策略会一直尝试重启处于停止状态的容器。

如下案例

[root@localhost ~]# docker run --name neversaydie -it --restart always centos:7.9.2009 sh

# 等待几秒输入 exit
[root@localhost ~]# docker ps
CONTAINER ID  IMAGE           COMMAND  CREATED        STATUS   PORTS              NAMES
50073280e113  centos:7.9.2009  "sh"  About a minute ago   Up 21 seconds     neversaydie

注意,容器于 一分钟前被创建,但却在 21秒前才启动。这是因为在容器中输入退出命令的时候,容器被杀死,任何Docker 又重新启动了该容器。

–restart always 策略由一个很有意思的特性,当daemon重启的时候,停止的容器也会被重启。

如下案例

[root@localhost ~]# docker stop 50073280e113
50073280e113
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE   COMMAND  CREATED   STATUS  PORTS      NAMES
[root@localhost ~]# systemctl restart docker
[root@localhost ~]# docker ps
CONTAINER ID  IMAGE           COMMAND  CREATED        STATUS   PORTS              NAMES
50073280e113  centos:7.9.2009  "sh"  About a minute ago   Up 21 seconds     neversaydie

always和unless-stopped的最大区别,就是那些指定了 --restart unless-stopped 并处于 Stopped(Exited)状态的容器,不会再Dokcer daemon 重启的时候被重启。

如下案例

(1)创建容器

创建两个新容器,其中一个容器指定 --restart always 策略,另一个容器指定 --restart unless-stopped 策略。

[root@localhost ~]# docker run -d --name always --restart always centos:latest sleep 1d
[root@localhost ~]# docker run -d --name unless-stopped --restart unless-stopped centos:latest sleep 1d

(2)停止两容器

使用docker stop 命令停止

[root@localhost ~]# docker ps
CONTAINER ID  IMAGE     COMMAND   CREATED   STATUS       PORTS       NAMES
4c66b9f3ea8c centos:latest  "sleep 1d" About a minute ago Up About a minute unless-stopped
d34fe3ec5727  centos:latest  "sleep 1d" 2 minutes ago   Up 2 minutes  always
[root@localhost ~]# docker stop always unless-stopped
always
unless-stopped
[root@localhost ~]# docker ps -n=2 
CONTAINER ID    IMAGE  COMMAND  CREATED    STATUS    PORTS     NAMES
4c66b9f3ea8c        centos:latest       "sleep 1d"          3 minutes ago       Exited (137) 6 seconds ago                       unless-stopped
d34fe3ec5727        centos:latest       "sleep 1d"          4 minutes ago       Exited (137) 6 seconds ago                       always

(3)重启docker

[root@localhost ~]# systemctl restart docker

(4)查看两个容器的状态

image-20230605161859941

可以看到启动时指定了 --restart always 策略的容器已经重启了,但是启动时指定 --restart unless-stopped 策略的容器并没有重启。

还有一种策略没介绍就是 on-failure策略会再退出容器并且返回值不是0的时候,重启容器。就算容器处于 stopped状态,再Docker daemon 重启的时候,容器也会被重启。

Web服务器案例

在本次实验中,我们使用的是nginx镜像,这个镜像会在80端口启动一个简单的web服务

示例如下

[root@localhost ~]# docker run -d --name nginx01 -p 8080:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete 
a9edb18cadd1: Pull complete 
589b7251471a: Pull complete 
186b1aaa4aa6: Pull complete 
b4df32aa5a72: Pull complete 
a0bcbecc962e: Pull complete 
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
70178d9a22053334d69be045b836786f487266f42ac8cc2951a87c2cd1af5a88

具体解释如下:

  • docker run :启动一个新的容器。
  • -d :后台运行容器。
  • --name nginx01 :指定容器的名称为nginx01
  • -p 8080:80 :将容器的80端口映射到宿主机的8080端口。这样,当我们在浏览器中访问http://localhost:8080时,实际上是访问了容器中的nginx服务。
  • nginx :要运行的镜像名称,这里是官方的nginx镜像。 执行此命令后,Docker会在后台启动一个新的nginx容器,并将容器的标准输出重定向到当前终端。

使用docker ps 命令可以查看当前运行的容器及端口的映射情况

image-20230605163508626

现在的容器已经运行,端口也映射成功,可以通过浏览器来访问该容器,需要在浏览器中指定Docker主机的IP地址或DNS名称,端口号是8080,下图展示了容器服务提供的网页

image-20230605163657717

查看容器详情

在之前的案例中,我们并没有指定容器中具体的应用,但是容器却启动了一个简单的Web服务。这是如何发生的?

当构建Docker镜像的时候,可以通过嵌入指令来列出希望容器运行时启动的默认应用。执行docker inspect 命令来查看运行容器时使用的镜像,就能看到容器启动时将要运行的应用列表了

image-20230605164508346

image-20230605164524438

Cmd一项中展示了容器将会执行的就命令或应用,除非在启动的时候指定另外的应用。如果去掉示例脚本中的转义字符,可以得到这样的命令:nginx -g daemon off; 其中,nginx 表示要启动的nginx服务,-g daemon off 表示以前台进程方式运行nginx服务,并在控制台上输出日志信息。

快速清理

接下里了解一种快速清理Docker主机上全部运行容器的方法。这种方法会强制删除所以的容器,并且不会给容器完成清理的机会。这种操作一定不能在生成环境系统或者运行着重要容器的系统上运行

如下命令

[root@localhost ~]# docker rm $(docker ps -aq) -f

总结

  • docker run 是启动新容器的命令。该命令的最简形式接收镜像和命令作为参数。镜像用于创建容器,而命令则是希望容器运行的应用或者操作。docker run -it centos:latest /bin/bash 命令会在前台启动一个 Centos容器,并运行 BASH Shell
  • Ctrl + PQ 会断开shell和容器终端之间的链接,并在退出后保持容器在后台处于运行状态
  • docker ps 用于列出所有在运行状态的容器。如果使用 -a 还可以看到处于停止状态的容器
  • docker exec 运行用户在运行状态的容器中,启动一个新进程。docker exec -it <container-name or container-id> bash 命令会在容器内部启动一个 Bash shell进程,并连接到该shell。为了使该命令生效,用于创建容器的镜像必须包含Bash shell。
  • ==docker stop == 命令会停止运行中的容器,并将状态设置为Exited(0)。
  • docker start 会重启处于停止(Exited)状态的容器。docker start 后面接容器名称和容器ID
  • docker rm 会删除停止运行的容器。可以通过容器名称或者ID来指定要删除的容器。推荐使用docker stop 命令停止容器,然后使用 docker rm 来完成删除
  • docker inspect 命令会显示容器的配置细节和运行时的信息。docker inspect 后面接容器名称和容器ID
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。