这是前一篇的内容,能够先来练习回顾一下容器的操做。html
直接下载镜像并启动容器,这里选择alpine版的:nginx
$ 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 $
启动参数说明:web
查看启动的容器:redis
$ 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
$ docker container ls --no-trunc --format '{{.Command}}' "nginx -g 'daemon off;'" $
这里启动nginx加了参数,daemon off 字面的意思就是关闭守护进程。这是为了让nginx在前台运行。
若是是nginx默认的启动方式,那么nginx程序将在后台运行,一旦nginx启动完就没有任何程序了,结果容器也就退出了。shell
在容器中执行任何程序或服务,必定不能在容器中运行在后台。只要运行在后台,一启动就会终止。json
既然Nginx已经启动,就能够直接用浏览器访问了。而且作了端口映射,因此能够直接经过宿主机的端口来进行访问:http://[宿主机的IP地址]:8001
。ubuntu
容器的日志
每个容器的目的只是为了运行一个程序,这个程序就是容器的主进程PID=1。传统的程序的日志通常是保存在日志文件中的,可是容器中没有这个必要。由于如今整个容器就只为了运行一个进程,日志就能够直接打印在控制台上了,就是程序直接在前台运行的效果。
使用下面的命令能够查看日志:centos
$ docker container logs web1
查看后,访问几回页面再看下是否有访问日志刷新。浏览器
首先,直接启动一个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 OK 127.0.0.1:6379> set name Adam OK 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容器。
采用分层构建机制,最底层为bootfs,次之为rootfs:
启动一个busybox容器,命令ls查看容器内部,拥有完整意义上的文件系统:
$ docker container run --name shell -it busybox / # ls bin dev etc home proc root sys tmp usr var / # exit $
分层构建的镜像:
如图,是一个Apache镜像。最底层是一个 Debian 的基础镜像,一个纯净的操做系统。在系统之上,添加了一个 emacs,这是一个代码编辑器。再而后添加了一个 Apache。这里每添加一个软件都是一个独立的层次。
最最下面的bootfs,在容器启动时,一旦引导完rootfs就会被卸载并移除(从内存中移除)。
对于一个容器,全部写操做,只能在最上层的可读写层进行。若是容器删除了,这个最上面的可读写层也会一块儿被删除。
关于Linux操做系统的基础镜像,能够参考下表来选择合适的基础镜像:
推荐使用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等。早期默认支持的文件系统:
Device Mapper 是 Linux2.6 内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核构架。
最著名的 Registry 就是Docker Hub: https://hub.docker.com/
其余的和有好比这个Quay: https://quay.io/
启动容器时,会先试图从本地获取相关的镜像。若是本地镜像不存在,再从Registry中下载镜像并保存到本地。
一个Registry一般由2部分组成:
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个,第一个是没有用户名的属于顶层仓库。后面是用户仓库,能够看到分别属于的用户名。
Idxex的做用:
这里只是简单的提一下这个概念,主要是程序配置文件的问题。
镜像的使用有一个问题,就是镜像内部使用的配置信息。配置信息能够直接注入在镜像里,可是这样就要为不一样的配置生成好多个不一样版本的镜像。
云原生是一种为了云计算环境运行而生的应用程序,而且能够解决不一样配置的信息的问题。
以Nginx为例,传统的开发运行在服务器上的程序,使用配置文件来管理配置。若是把它托管到容器云上运行,就会有诸多不便之处,最大的问题就是修改配置文件。
而那些云原生开发的程序,会使用对于云计算场景方便的接口来提供配置逻辑。具体到容器,至关于为应用程序加了一层外壳,再去操做里面的数据是不方便的。有一种作法是向容器传入环境变量来传递配置信息,而配置则能够从环境变量加载自动注入到配置中。
云原生的大量配置均可以直接经过环境变量来获取。
使用命令docker commint
会把容器最上面的可写层,单首创建为一个镜像层,生成一个新的镜像。
其余制做镜像的方法,而且是制做镜像的最主要的方法是,基于Dockerfile制做镜像。这部份内容很重要也不少,须要单独再写一篇。
基于busybox,添加一个httpd的服务。
$ docker run --name httpd -it busybox / # echo "<h1>Hello world. Busybox httpd.</h1>" > /var/www/index.html / # cat /var/www/index.html <h1>Hello world. Busybox httpd.</h1> / #
在容器内部进行修改
如今建立好了一个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文件,也能够从几种压缩文件直接导入。操做起来都同样,程序会本身识别。应该是经过文件名后缀吧。