共计 10839 个字符,预计需要花费 28 分钟才能阅读完成。
本篇文章为大家展示了怎样进行 Docker 镜像管理的分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
运行容器示例 Nginx
直接下载镜像并启动容器,这里选择 alpine 版的:
$ docker run --name web1 -p 8001:80 -d nginx:alpine
Unable to find image nginx:alpine locally
alpine: Pulling from library/nginx
e7c96db7181b: Downloading
3fb6217217ef: Download complete
alpine: Pulling from library/nginx
e7c96db7181b: Pull complete
3fb6217217ef: Pull complete
Digest: sha256:17bd1698318e9c0f9ba2c5ed49f53d690684dab7fe3e8019b855c352528d57be
Status: Downloaded newer image for nginx:alpine
01c17a72e943e93d71b56b433bea7a3d6ffa1f848dc3947f2adaf2bb2e3e7fee
$
启动参数说明:
-d,表示启动容器后在宿主机的后台运行。
-p,端口映射,将宿主机的 8001 端口,映射到容器内部的 80 端口。端口映射是 network 的内容,之后会详细说明。
查看启动的容器:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f57cd5f9d50f nginx:alpine nginx -g daemon of… 13 minutes ago Up 12 minutes 0.0.0.0:8001- 80/tcp web1
$
可以看到端口和端口映射的情况。
这里主要关注 COMMAND,上面的显示被截断了:
$ docker container ls --no-trunc --format {{.Command}}
nginx -g daemon off;
$
这里启动 nginx 加了参数,daemon off 字面的意思就是关闭守护进程。这是为了让 nginx 在前台运行。
如果是 nginx 默认的启动方式,那么 nginx 程序将在后台运行,一旦 nginx 启动完就没有任何程序了,结果容器也就退出了。
在容器中执行任何程序或服务,一定不能在容器中运行在后台。只要运行在后台,一启动就会终止。
既然 Nginx 已经启动,就可以直接用浏览器访问了。并且做了端口映射,所以可以直接通过宿主机的端口来进行访问:http://[宿主机的 IP 地址]:8001。
容器的日志
每一个容器的目的只是为了运行一个程序,这个程序就是容器的主进程 PID=1。传统的程序的日志一般是保存在日志文件中的,但是容器中没有这个必要。因为现在整个容器就只为了运行一个进程,日志就可以直接打印在控制台上了,就是程序直接在前台运行的效果。
使用下面的命令可以查看日志:
$ docker container logs web1
查看后,访问几次页面再看下是否有访问日志刷新。
redis
首先,直接启动一个 redis:
$ docker container run --name redis -d redis:alpine
容器启动后,依然是停留在宿主机的命令行界面。
进入容器内部
现在需要进入到容器内部进行操作,就像之前的 busybox 那样。但是,这次容量内部运行的是一个 redis-server 的程序,并且一个容器内部一般只运行一个程序。所以容器里并没有 shell。
这里和之前的 busybox 容器的情况不同,在 busybox 容器内部就有一个 shell。所以直接进入是没有任何终端界面的。这里需要启动一个 shell 然后进入:
$ docker container exec -it redis /bin/sh
/data # ps
PID USER TIME COMMAND
1 redis 0:00 redis-server
12 root 0:00 /bin/sh
23 root 0:00 ps
/data #
进入并且执行命令查看当前容器内的进程。
这里看到,除了 ps 命令,还有原本的 redis-server 以及进入容器时启动的 shell。所以在容器内部运行多个进程也是可以的,现在就是这个情况。不过一般也就这在这种场景下需要在容器中运行多个进程。
执行其他操作
既然都进来了,就运行些命令。看下系统的时间:
/data # date
Tue Jul 16 13:03:05 UTC 2019
/data #
时间没问题,不过时区不对,这个略过。
查看端口监听情况:
/data # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN
tcp 0 0 :::6379 :::* LISTEN
/data #
使用 redis-cli 命令:
/data # redis-cli
127.0.0.1:6379 set age 23
127.0.0.1:6379 set name Adam
127.0.0.1:6379 keys *
1) name
2) age
127.0.0.1:6379 exit
/data # exit
$
这里看到 redis-cli 自带用户界面,所以不用启动 /bin/sh 也能直接进来:
$ docker container exec -it redis redis-cli
127.0.0.1:6379 exit
$
Docker 镜像
Docker 镜像的基础知识。
镜像启动
docker 镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动 docker 容器。
采用分层构建机制,最底层为 bootfs,次之为 rootfs:
bootfs: 用于系统引导的文件系统,包括 bootloader 和 kernel, 容器启动完成后会被卸载以节约内存资源
rootfs: 位于 bootfs 之上,表现为 docker 容器的根文件系统:
传统模式中,系统启动之时,内核挂载 rootfs 时会首先将其挂载为“只读”模式,完整性自检完成后将其重新挂载为读写模式
docker 中,rootfs 由内核挂载为“只读”模式,而后通过 联合挂载“技术额外挂载一个”可写“层
启动一个 busybox 容器,命令 ls 查看容器内部,拥有完整意义上的文件系统:
$ docker container run --name shell -it busybox
/ # ls
bin dev etc home proc root sys tmp usr var
/ # exit
$
分层构建
分层构建的镜像:
位于下层的镜像为父镜像(parent image),最底层的称为基础镜像(base image)
最上层为 可读写”层,其下的均为 只读“层
如图,是一个 Apache 镜像。最底层是一个 Debian 的基础镜像,一个纯净的操作系统。在系统之上,添加了一个 emacs,这是一个代码编辑器。再然后添加了一个 Apache。这里每添加一个软件都是一个独立的层次。
最最下面的 bootfs,在容器启动时,一旦引导完 rootfs 就会被卸载并移除(从内存中移除)。
对于一个容器,所有写操作,只能在最上层的可读写层进行。如果容器删除了,这个最上面的可读写层也会一起被删除。
操作系统基础镜像
关于 Linux 操作系统的基础镜像,可以参考下表来选择合适的基础镜像:
busybox: 临时测试用
alpine: 主要用于测试,也可用于生产环境
centos: 主要用于生产环境,支持 CentOS/Red Hat,常用于追求稳定性的企业应用
ubuntu: 主要用于生产环境,常用于人工智能计算和企业应用
debian: 主要用于生产环境
推荐使用 Alpine 镜像,因为它被严格控制并保持最小尺寸(目前小于 5MB),但它仍然是一个完整的发行版。
alpine 的好处主要是小,并且基本功能全。用于测试是非常方便的,而且生产上也是可以用。虽然不建议这么做,主要是因为缺少调试工具。
busybox 的镜像比 alpine 更小,它并不是一个系统发行版。最初这个工具是为了在一张软盘上创建一个可引导的 GNU/Linux 系统,这可以用作安装盘和急救盘。它是一个集成了三百多个最常用 Linux 命令和工具的软件。所以如果是需要启动一个容器并运行一些系统的工具和命令,那么可以使用这个作为基础镜像。
另外 3 个就是常用的 Linux 发行版,推荐在生产系统上用。镜像大也不是什么问题,因为容器是分层构建的,所以本地的多个镜像理论上是共用同一个基础镜像。
文件系统
Docker 镜像的分层构建和联合挂载依赖于它的专有文件系统。
aufs
在早期这个文件系统是 aufs(advanced multi-layered unification filesystem),高级多层统一文件系统。
overlayfs
aufs 的竞争产品是 overlayfs,overlayfs 在 3.18 版本开始被合并到 Linux 内核。使用 docker info 命令可以找到当前使用的文件系统:
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_tpe: true
Native Overlay Diff: true
overlay2 是一种抽象的二级文件系统,它需要建构在本地文件系统之上。上面的信息显示,这里作为基础的本地文件系统是 xfs。
其他文件系统
docker 的分层镜像,除了 aufs,还支持 btrfs,devicemapper 和 vfs 等。早期默认支持的文件系统:
Ubuntu 系统,默认使用 aufs
CentOS 系统,默认使用 devicemapper
Device Mapper 是 Linux2.6 内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核构架。
Docker Registry
最著名的 Registry 就是 Docker Hub:https://hub.docker.com/
其他的和有比如这个 Quay:https://quay.io/
启动容器时,会先试图从本地获取相关的镜像。如果本地镜像不存在,再从 Registry 中下载镜像并保存到本地。
一个 Registry 通常由 2 部分组成:
Repository
Index
Repository(仓库)
Repository,由特定的 docker 镜像的所有迭代版本组成的镜像仓库。一个 Registry 中可以存在多个 Repository。每个仓库可以包含多个 Tag(标签),每个标签对应一个镜像。
Repository 可分为顶层仓库和用户仓库,用户仓库名称格式为“用户名 / 仓库名”。使用 docker search 命令看一下:
$ docker search --limit 3 nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 11704 [OK]
jwilder/nginx-proxy Automated Nginx reverse proxy for docker con… 1628 [OK]
bitnami/nginx Bitnami nginx Docker Image 69 [OK]
$
这里显示了 3 个,第一个是没有用户名的属于顶层仓库。后面是用户仓库,可以看到分别属于的用户名。
Index
Idxex 的作用:
维护用户账户、镜像的校验以及公共命名空间的信息
相当于为 Registry 提供了一个完成用户认证功能的检索接口
云原生
这里只是简单的提一下这个概念,主要是程序配置文件的问题。
镜像的使用有一个问题,就是镜像内部使用的配置信息。配置信息可以直接注入在镜像里,但是这样就要为不同的配置生成好多个不同版本的镜像。
云原生是一种为了云计算环境运行而生的应用程序,并且可以解决不同配置的信息的问题。
以 Nginx 为例,传统的开发运行在服务器上的程序,使用配置文件来管理配置。如果把它托管到容器云上运行,就会有诸多不便之处,最大的问题就是修改配置文件。
而那些云原生开发的程序,会使用对于云计算场景方便的接口来提供配置逻辑。具体到容器,相当于为应用程序加了一层外壳,再去操作里面的数据是不方便的。有一种做法是向容器传入环境变量来传递配置信息,而配置则可以从环境变量加载自动注入到配置中。
云原生的大量配置都可以直接通过环境变量来获取。
基于容器制作镜像
使用命令 docker commint 会把容器最上面的可写层,单独创建为一个镜像层,生成一个新的镜像。
其他制作镜像的方法,并且是制作镜像的最主要的方法是,基于 Dockerfile 制作镜像。这部分内容很重要也很多,需要单独再写一篇。
修改基础镜像的内容
基于 busybox,添加一个 httpd 的服务。
$ docker run --name httpd -it busybox
/ # echo h2 Hello world. Busybox httpd. /h2 /var/www/index.html
/ # cat /var/www/index.html
h2 Hello world. Busybox httpd. /h2
/ #
在容器内部进行修改
现在创建好了一个 html 文件,但是下次 docker 再启动这个容器时这个文件是不会有的。现在需要做的是将之前做的改变保存好。
保存对容器的修改,生成新的镜像
要保持这个容器的运行状态,那就再另外开一个会话执行 commit 命令:
$ docker commit -p httpd
sha256:5bd093efd84001a2f7412292431ead5c760acef8f4e3a2298abf9f28aa7b3cd7
$
这里的 - p 参数是将容器处于暂停状态,这样可以防止镜像制作过程中可能会有操作来改变容器的内容,建议 - p 参数都加上。
修改镜像标签
查看镜像信息,新制作完成的镜像信息如下:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 5bd093efd840 2 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
由于制作的时候没有指明仓库名和标签名,所以都是空。这两个字段是允许为空的,这样只能通过镜像的 ID 来指明这个镜像。
添加标签信息
为了引用时方便,还是把仓库名和标签名加上吧:
$ docker image tag 5bd093efd840 myimg/httpd:v1
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd v1 5bd093efd840 9 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
一个镜像可以有多个标签,再加一个 latest 标签:
$ docker image tag myimg/httpd:v1 myimg/httpd:latest
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd latest 5bd093efd840 11 minutes ago 1.22MB
myimg/httpd v1 5bd093efd840 11 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
这里可以确认下,2 个标签的镜像的 ID 是一样的,所以这里是一个镜像,只是为这个镜像添加了 2 个标签。
删除标签
没有删除标签的命令,要删除某个标签,就直接用删除镜像的命令:
$ docker image tag myimg/httpd:v1 myimg/httpd:tmp1
$ docker image rm myimg/httpd:tmp1
Untagged: myimg/httpd:tmp1
$
这里又添加了一个标签,然后再把这个标签给删除,命令执行结果显示只是把指定的标签去掉了。所以同一个镜像打了多个标签,本地存的只有一份。删除某个标签的镜像,只是把这个标签从镜像标签的列表里去除。之后删除最后一个标签的时候才是真正的删除了一个镜像。
修改镜像默认启动命令
使用 inspect 命令可以查看 docker 对象的底层信息。这里要找的是镜像的底层信息中的默认启动的命令,具体如下:
$ docker image inspect busybox
Cmd : [
sh
],
$
启动时运行的命令是 sh,这个也是 busybox 镜像默认启动时运行的命令,因为制作新镜像的时候没有指定这个内容。
重新制作一版新的镜像,这次要指定默认启动时运行的命令:
$ docker commit -c CMD [httpd , -f , -h , /var/www/] -p httpd myimg/httpd:v2
sha256:850da6d87c65a2c6084cdbfcabbeeeaf6c13ddbb9fbb984fec5ca05cab38830d
$
参数 - c 不是用来指定命令的,而是指定所有要做的修改,当然这里只要修改启动的命令。
httpd 命令参数说明
关于启动命令 httpd -f -h /var/www/,这个具体可以去看 httpd 的参数说明。- f 表示作为守护进程也就是在前台运行,而 - h 参数则是指定首页的路径。
启动验证镜像
带参数启动镜像:
$ docker container run --name httpd2 -d -p 8002:80 myimg/httpd:v2
80522bb422e16dae4ea052bcb36e51203f4d7b023fefdf3de4114598b3e95b29
$
镜像启动后,可以使用浏览器访问宿主机的 IP 地址加上映射的端口号来打开这个页面,比如:http://192.168.24.170:8002/
镜像导入和导出
可以在已有镜像的主机上把镜像打包,将打包的文件复制到另外一台主机上再把镜像导入,就可以在主机之间传递镜像了。这种方法不需要连接镜像仓库。
导出镜像
导出镜像就是将镜像导出到一个 tar 包:
$ docker image save -o httpd.tar myimg/httpd
这条命令省略了 Tag 标签,这样就会把整个仓库打包,就是打包所有的版本。
save 命令仅有一个参数 -o,就是指定导出的位置。如果没有 - o 参数,那就是输出到终端。不过也不能直接输出到终端,这样的做法是再通过输出重定向来把内容保存起来。所以这条命令的效果是一样的:
$ docker image save myimg/httpd httpd2.tar
可以加上标签信息,就可以指定打包对应的 Tag 的镜像。镜像的参数可以传入多个,就打包多个镜像:
$ docker image save -o httpd3.tar myimg/httpd:v1 myimg/httpd:v2
导出为 tar 文件
导入的文件名可以任意指定,不过建议使用 tar 扩展名。这确实是一个 tar 包,使用 tar 命令来查看 tar 包内部的文件列表:
$ tar -tvf httpd.tar
-rw-r--r-- 0/0 1491 2019-07-18 15:10 25079c1e47bf896a028e55d715dc06e251f3efe53ca655ad63f6085ce6a465a8.json
-rw-r--r-- 0/0 1464 2019-07-18 15:05 7f36d8e3488df22381081d68c7f2215750167250114abd0b2f31d99e81a7bfd7.json
drwxr-xr-x 0/0 0 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/
-rw-r--r-- 0/0 3 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/VERSION
-rw-r--r-- 0/0 1081 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/json
-rw-r--r-- 0/0 4608 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/layer.tar
drwxr-xr-x 0/0 0 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/
-rw-r--r-- 0/0 3 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/VERSION
-rw-r--r-- 0/0 1107 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/json
-rw-r--r-- 0/0 4608 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/layer.tar
drwxr-xr-x 0/0 0 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/
-rw-r--r-- 0/0 3 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/VERSION
-rw-r--r-- 0/0 406 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/json
-rw-r--r-- 0/0 1441280 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/layer.tar
-rw-r--r-- 0/0 579 1970-01-01 08:00 manifest.json
-rw-r--r-- 0/0 238 1970-01-01 08:00 repositories
$
从 IMAGE ID 可以看出,这里确实是将 2 个版本的镜像到打包了。
导出并压缩
用下面的方法完成导出并压缩:
$ docker save myimage:latest | gzip myimage_latest.tar.gz
导入镜像
使用 load 命令可以方便的将镜像导入:
$ docker image load -i httpd3.tar
6194458b07fc: Loading layer [==================================================] 1.441MB/1.441MB
dd0dd7cb79c9: Loading layer [==================================================] 4.608kB/4.608kB
Loaded image: myimg/httpd:latest
Loaded image: myimg/httpd:v1
698704828883: Loading layer [==================================================] 4.608kB/4.608kB
Loaded image: myimg/httpd:v2
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd v2 25079c1e47bf 30 minutes ago 1.22MB
myimg/httpd latest 7f36d8e3488d 35 minutes ago 1.22MB
myimg/httpd v1 7f36d8e3488d 35 minutes ago 1.22MB
$
上面最后一次打包的文件是 httpd3.tar。执行打包命令的时候指定了 v1 和 v2 标签,并没有指定 latest 标签。不过这里能看到所有的 3 个标签。所以标签只是一个标签,一个镜像可以有多个标签,但是不同标签的镜像是同一个镜像。
不使用 - i 参数的话,默认从标准输出导入,用下面的方法也是一样的:
$ docker image load httpd3.tar
导入压缩文件
Load an image or repository from a tar archive (even if compressed with gzip, bzip2, or xz) from a file or STDIN. It restores both images and tags.
导入镜像可以从 tar 文件,也可以从几种压缩文件直接导入。操作起来都一样,程序会自己识别。应该是通过文件名后缀吧。
上述内容就是怎样进行 Docker 镜像管理的分析,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注丸趣 TV 行业资讯频道。