您现在的位置是:首页 >技术教程 >Docker镜像与Docker容器详解网站首页技术教程
Docker镜像与Docker容器详解
文章目录
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的命令如下图
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
。执行该命令后,会输出以下详细信息:
Using default tag: latest
:表示使用的是 MySQL 镜像的默认标签latest
。latest: Pulling from library/mysql
:表示正在从 Docker Hub 的library/mysql
仓库中拉取 MySQL 镜像的最新版本。72a69066d2fe: Pull complete
到e0431212d27d: Pull complete
:这些行表示 Docker 正在从远程仓库下载 MySQL 镜像的各个层,每个层都是一个只读的文件系统。当一个镜像层下载完成后,会输出Pull complete
。Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
:表示 MySQL 镜像的摘要信息,该信息包含了镜像的完整性校验信息。Status: Downloaded newer image for mysql:latest
:表示已经成功下载最新版本的 MySQL 镜像。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
镜像仓库服务包含多个镜像仓库,同样,一个镜像仓库中可以包含多个镜像。如下图
其中每个镜像仓库中都包含一个或多个镜像
官方与非官方镜像仓库
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名称。
假设上的镜像位于阿里云容器镜像仓库服务中,如下图所示需要自己注册一下,就有了自己的镜像仓库了
为镜像打多个标签
一个镜像可以根据用户的需求设置多个标签,因为标签是存放在镜像数据的任意数字或字符串。如下案例
在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镜像有多少
如上镜像有那么多!我只想要看到官方提供的mysql镜像该怎么做呢?
如果我想看到收藏总量在1000以上的mysql镜像呢
镜像和分层
Docker镜像由一些松耦合的只读镜像层组成
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作为表示展示了这些分层
另一种查看镜像分层的方式是通过 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
在添加额外镜像层的同时,镜像始终保持是当前所有镜像层的组合
每个镜像层都有一个唯一的 ID,称为 SHA256 ID,它是根据该镜像层的内容计算出来的。Docker 会根据所有镜像层的 SHA256 ID 计算出一个唯一的镜像 ID,这个镜像 ID 对应的就是当前所有镜像层的组合。因此,无论你添加了多少额外镜像层,镜像的 ID 都是固定的,只有镜像的大小和 SHA256 ID 发生变化。这样的设计也方便了 Docker 镜像的版本管理和复用,可以方便地基于已有的镜像层构建新的镜像。
如下图,举例,每个镜像层包含了三个文件,而镜像包含了来自两个镜像层的6个文件
如下图展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层的文件7是文件5的一个更新版本。这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得忘记的更新版本作为一个新镜像层添加到镜像当中。
Docker 通过存储引擎的方式来实现镜像层堆栈,保证多镜像层对外展示位统一的文件系统。
如下图展示了显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
共享镜像层
多个镜像之间可以并且确实会共享镜像层。这样可以有效节省空间并提升性能
拉取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)查看两个容器的状态
可以看到启动时指定了 --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
命令可以查看当前运行的容器及端口的映射情况
现在的容器已经运行,端口也映射成功,可以通过浏览器来访问该容器,需要在浏览器中指定Docker主机的IP地址或DNS名称,端口号是8080,下图展示了容器服务提供的网页
查看容器详情
在之前的案例中,我们并没有指定容器中具体的应用,但是容器却启动了一个简单的Web服务。这是如何发生的?
当构建Docker镜像的时候,可以通过嵌入指令来列出希望容器运行时启动的默认应用。执行docker inspect 命令来查看运行容器时使用的镜像,就能看到容器启动时将要运行的应用列表了
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
令