docker容器镜像
容器与镜像之间的关系:
docker client 向docker daemon发起创建容器的请求
docker daemon查找有无客户端需要的镜像
如无,则到容器的镜像仓库中下载需要的镜像
拿到容器镜像后,启动容器
容器镜像介绍
Docker 镜像就是一组只读的目录,或者叫只读的 Docker 容器模板,镜像中含有一个Docker 容器运行所需要的文件
系统,所以我们说Docker 镜像是启动一个Docker 容器的基础。
可以将Docker 镜像看成是Docker 容器的静态时,也可将Docker 容器看成是Docker镜像的运行时。
从Docker 的官方文档来看,Docker 容器的定义和 Docker 镜像的定义几乎是相同,Docker 容器和Docker 镜像的区
别主要在于docker 容器多出了一个可写层。
容器中的进程就运行在这个可写层,这个可写层有两个状态,即运行态和退出态。当我们docker run 运行容器后,
docker 容器就进入了运行态,当我们停止正在运行中的容器时,docker 容器就进入了退出态。
我们将容器从运行态转为退出态时,期间发生的变更都会写入到容器的文件系统中(需要注意的是,此处不是写入到
了docker 镜像中)。
联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并
层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。
联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承。例如,用户基于基础镜像(用
来生成其他镜像的基础,往往没有父镜像)来制作各种不同的应用镜像。这些镜像共享同一个基础镜像层,提高了存
储效率。此外,当用户改变了一个Docker镜像(比如升级程序到新的版本),则会创建一个新的层(layer)。因
此,用户不用替换整个原镜像或者重新建立,只需要添加新层即可。用户分发镜像的时候,也只需要分发被改动的新
层内容(增量部分)。这让Docker的镜像管理变得十分轻量级和快速。
制作基础镜像
#打包操作系统的根目录
排除/proc及/sys>
[root@pg ~]# tar --numeric-owner --exclude=/proc --exclude=/sys -cvf centos7u6.tar /
把获取的根打包文件导入Docker Host中
[root@pg ~]# docker import centos7u6.tar centos7u6:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7u6 latest 32adaf903894 8 seconds ago 12.2GB
centos-httpd v2 53811f7e4431 4 hours ago 351MB
centos-httpd v1 a4d8e256efdc 5 hours ago 351MB
centos latest 5d0da3dc9764 16 months ago 231MB
使用基础镜像启动容器
[root@pg ~]# docker run -it --name c1000 centos7u6:latest /bin/bash
[root@8f72e912817a /]#
[root@8f72e912817a /]# ls /proc
1 bus crypto execdomains iomem keys loadavg modules partitions slabinfo sysrq-trigger uptime
34 cgroups devices fb ioports key-users locks mounts sched_debug softirqs sysvipc version
acpi cmdline diskstats filesystems irq kmsg mdstat mtrr schedstat stat timer_list vmallocinfo
asound consoles dma fs kallsyms kpagecount meminfo net scsi swaps timer_stats vmstat
buddyinfo cpuinfo driver interrupts kcore kpageflags misc pagetypeinfo self sys tty zoneinfo
[root@8f72e912817a /]# ip a s
55: eth0@if56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.7/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
应用镜像制作
#使用commit提交镜像
在基础镜像运行的容器中安装应用,此例使用httpd
[root@8f72e912817a /]# yum -y install httpd
使用commit命令对正在运行的容器提交为一个应用镜像
[root@pg ~]# docker commit --help
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Create a new image from a container's changes
Aliases:
docker container commit, docker commit
Options:
-a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change list Apply Dockerfile instruction to the created image
-m, --message string Commit message
-p, --pause Pause container during commit (default true)
=============================================================
[root@pg ~]# docker commit c1000 centos7u6-httpd:v1
sha256:66c87730c9efab9f418899034a69be46f2813a156e5d83a647b84af100a1ded6
[root@pg ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7u6-httpd v1 66c87730c9ef 4 seconds ago 12.5GB
centos7u6 latest 32adaf903894 4 minutes ago 12.2GB
centos-httpd v2 53811f7e4431 4 hours ago 351MB
centos-httpd v1 a4d8e256efdc 5 hours ago 351MB
centos latest 5d0da3dc9764 16 months ago 231MB
使用应用镜像
[root@pg ~]# docker run -it centos7u6-httpd:v1 /bin/bash
[root@c873f03c4a83 /]# echo "ttt" >> /var/www/html/index.html
[root@c873f03c4a83 /]# httpd -k start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.7. Set the 'ServerName' directive globally to suppress this message
[root@c873f03c4a83 /]# curl http://localhost
ttt
[root@c873f03c4a83 /]#
使用Dockerfile创建应用镜像
使用docker build命令
基于Dockerfile
Dockfile原理
在Dockerfile定义所要执行的命令,使用docker build创建镜像,过程中会按照Dockerfile所定义的内容打开临时性容器(使用docker commit进行提交),把Dockerfile文件中的命令全部执行完成,就得到了一个容器应用镜像。
执行命令越多,最终得到的容器应用镜像越大,所以要做优化
Dockerfile关键字
FROM(指定基础image)
MAINTAINER(用来指定镜像创建者信息)
RUN (运行命令)
CMD(设置container启动时执行的操作)
如果容器镜像中有此命令,启动容器时,不要手动让容器执行其它命令
ENTRYPOINT(设置container启动时执行的操作)
USER(设置container容器的用户)
EXPOSE(指定容器需要映射到宿主机器的端口)
ENV(用于设置环境变量)
ADD(从src复制文件到container的dest路径)
VOLUME(指定挂载点)
WORKDIR(切换目录)
##案例
通过Dockerfile创建一个可以在启动容器时就直接启动httpd应用的镜像
步骤:
创建一个目录,用于存储Dockerfile所使用的文件
在此目录中创建Dockerfile文件及制作镜像所使用到的文件
在此目录中使用docker build创建镜像(读取Dockerfile文件)
使用创建的镜像启动容器
思考:
- 基础镜像是谁?centos7u6
- 安装httpd
- yum -y install httpd
- 安装完成后如何启动httpd? 编写一个把httpd启动的脚本文件
- 把httpd放在前端执行还是后端执行?前端
- 暴露端口?tcp80
- 添加一个测试文件,用于验证httpd是否可用?
[root@bogon ~]# mkdir test
[root@bogon ~]# cd test
[root@bogon test]# cat run-httpd.sh
#!/bin/bash
rm -rf /run/httpd/*
exec /sbin/httpd -D FOREGROUND
创建用于测试httpd是否可用的index.html
[root@bogon test]# cat index.html
It's work!!!
创建Dockerfile
[root@bogon test]# cat Dockerfile
FROM centos7u6:latest
MAINTAINER "smartdocker@smartdocker@126.com"
RUN yum clean all
RUN rpm --rebuilddb && yum -y install httpd
ADD run-httpd.sh /run-httpd.sh
RUN chmod -v +x /run-httpd.sh
ADD index.html /var/www/html/
EXPOSE 80
WORKDIR /
CMD ["/bin/bash","/run-httpd.sh"]
使用docker build创建镜像,注意命令最后有一个点,点表示当前目录
[root@bogon test]# docker build -t centos7u6-base-httpd:v1 .
使用上述创建的应用容器启动容器
[root@bogon test]# docker run -d centos7u6-base-httpd:v1
验证容器及httpd是否可用
[root@bogon test]# docker inspect 31d #查看容器IP地址
[root@bogon test]# curl http://172.17.0.11
It's work!!!
2)替代原网站内容案例
[root@bogon test]# mkdir /wwwroot
[root@bogon test]# echo "wwwroot" >> /wwwroot/index.html
[root@bogon test]# docker run -d -v /wwwroot:/var/www/html centos7u6-base-httpd:v1
415e82ad0041aa18bcb92bc8edbdf9bd758915a8345c71266f51937de3a20a26
[root@bogon test]# docker inspect 415
[root@bogon test]# curl http://172.17.0.12
wwwroot
##案例:把nginx应用容器化
要求:
1、通过基础镜像做nginx应用镜像
2、使用nginx应用镜像启动容器时,nginx要求启动
3、验证nginx服务是否启动
步骤:
1、使用哪一个基础镜像 centos:latest
2、需要使用epel YUM源
3、安装nginx
4、修改nginx配置文件,主要用于关闭daemon后台运行
5、验证使用的测试页面
创建目录
[root@localhost ~]# mkdir nginxtest
[root@localhost ~]# cd nginxtest/
[root@localhost nginxtest]#
创建测试文件
[root@localhost nginxtest]# echo 'nginx s running!!!' >> index.html
[root@localhost nginxtest]# ls
index.html
创建Dockerfile
[root@localhost nginxtest]# cat Dockerfile
FROM centos:latest
MAINTAINER "smartaiops<smartaiops@126.com>"
RUN yum clean all && yum -y install yum-plugin-ovl && yum -y install epel-release
RUN yum -y install nginx
ADD index.html /usr/share/nginx/html/
RUN echo "daemon off;" >> /etc/nginx/nginx.conf #取消nginx以daemon身份运行
EXPOSE 80
CMD /usr/sbin/nginx
使用docker build创建nginx应用镜像
[root@localhost nginxtest]# docker build -t centos-nginx:v1 .
启动容器验证nginx服务是否自动开启
[root@localhost nginxtest]# docker images
REPOSITORY TAG IMAGE ID CREATED
SIZE
centos-nginx v1 cf23c20ff2cd 20 seconds ago
466MB
[root@localhost nginxtest]# docker run -d centos-nginx:v1
5b1d6dae77d24d9c8dc136a5bf971ebe296e1463838bda46e586d07d6f572f6d
[root@localhost nginxtest]# docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
5b1d6dae77d2 centos-nginx:v1 "/bin/sh -c /usr/sbi…" 9 seconds ago
Up 8 seconds 80/tcp upbeat_khayyam
[root@localhost nginxtest]# docker inspect 5b1d
[root@localhost nginxtest]# curl http://172.17.0.3
nginx s running!!!
容器镜像在docker host存储位置
从图中可以看出除了最上面的一层为读写层之外,下面的其他的层都是只读的镜像层,并且除了最下面的一层外,其
他的层都有会有一个指针指向自己下面的一层镜像。
虽然统一文件系统(union file system)技术将不同的镜像层整合成一个统一的文件系统,为构成一个完整容器镜像的
层提供了一个统一的视角,隐藏了多个层的复杂性,对用户来说只存在一个文件系统,但图中的这些层并不是不能看
到的,如果需要查看的话可以进入运行Docker的机器上进行查看,从这些层中可以看到Docker 内部实现的一些细
节。
Docker 的容器镜像和容器本身的数据都存放在服务器的 /var/lib/docker/ 这个路径下。不过不同的linux发行版存储
方式上有差别,比如,在ubuntu发行版上存储方式为AUFS,CentOS发行版上的存储方式为Overlay或Overlay2。
centos系统docker默认使用存储驱动是devicemapper,而这种存储驱动有两种模式loop-lvm和direct-lvm,
不巧默认又使用了比较低效的loop-lvm
OverlayFS是一个类似于AUFS 的现代联合文件系统,更快实现简单。
OverlayFS是内核提供的文件系统,overlay和overlay2是docker的存储驱动
Overlay及Overlay2原理
OverlayFS将单个Linux主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载。
OverlayFS底层目录称为lowerdir, 高层目录称为upperdir。合并统一视图称为merged。当需要修改一个文件时,
使用CoW将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层
就是image,可写层就是Container。
overlay2是overlay的改进版,只支持4.0以上内核添加了Multiple lower layers in overlayfs的特性,所以overlay2可
以直接造成muitiple lower layers不用像overlay一样要通过硬链接的方式(最大128层) centos的话支持3.10.0-514及
以上内核版本也有此特性,所以消耗更少的inode
本质区别是镜像层之间共享数据的方法不同
overlay共享数据方式是通过硬连接,只挂载一层,其他层通过最高层通过硬连接形式共享(增加了磁盘inode的负
担)
而overlay2是通过每层的 lower文件
查看docker默认使用的存储驱动方法
[root@bogon overlay2]# docker info
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 1
Server Version: 18.09.5
Storage Driver: overlay2 #注意此处
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: bb71b10fd8f58240ca47fbb579b9d1028eea7c84
runc version: 2b18fe1d885ee5083ef9f0838fee39b62d653e30
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-957.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 3.858GiB
Name: bogon
ID: CMTC:4W3A:NS5L:GZ73:RXOE:6ZTH:4QEM:B265:GP3T:JM7K:VCKB:GKAW
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
#docker运行前
/var/lib/docker/overlay2 路径下的信息在不同的阶段会有变化,了解这几个阶段中新增的数据以及容器与镜像的存
储结构的变化非常有利于我们对Docker容器以及Docker镜像的理解。
Docker运行前、Docker运行后、下载镜像后、运行容器后四个阶段中Docker 存储的变化
运行前:
没有启动docker daemon之间不会在/var/lib/目录中添加docker目录
启动后
[root@bogon ~]# systemctl start docker
[root@bogon ~]# tree /var/lib/docker
/var/lib/docker
├── builder
│ └── fscache.db
├── buildkit
│ ├── cache.db
│ ├── content
│ │ └── ingest
│ ├── executor
│ ├── metadata.db
│ └── snapshots.db
├── containers
├── image
│ └── overlay2
│ ├── distribution
│ ├── imagedb
│ │ ├── content
│ │ │ └── sha256
│ │ └── metadata
│ │ └── sha256
│ ├── layerdb
│ └── repositories.json
├── network
│ └── files
│ └── local-kv.db
├── overlay2 #下载镜像后,存储在此目录中
│ ├── backingFsBlockDev
│ └── l
├── plugins
│ ├── storage
│ │ └── blobs
│ │ └── tmp
│ └── tmp
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
└── metadata.db
29 directories, 8 files
下载镜像后
[root@bogon overlay2]# pwd
/var/lib/docker/overlay2
[root@bogon overlay2]# ls
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d backingFsBlockDev l
[root@bogon overlay2]# ls
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d/
diff link
[root@bogon overlay2]# ls
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d/diff/
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin etc lib media opt root sbin sys usr
[root@bogon overlay2]# pwd
/var/lib/docker/overlay2
[root@bogon overlay2]# ls
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d backingFsBlockDev l
[root@bogon overlay2]# ll l
总用量 0
lrwxrwxrwx 1 root root 72 4月 26 12:58 MBRXMBFE2MYZYMO5RZLRLTMRGJ ->
../2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d/diff
##运行容器后
Overlay2容器结构
#运行容器前
[root@bogon overlay2]# ls
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d backingFsBlockDev l
#运行容器后
[root@bogon overlay2]# ls
00bd7dcc4c91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cda0
backingFsBlockDev
00bd7dcc4c91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cda0-init l
2d297a77c653f510dce69815627fdb485462b1b2fc9e0078a9ecc5ca2d222c8d
[root@bogon overlay2]# ll
00bd7dcc4c91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cda0
总用量 8
drwxr-xr-x 2 root root 6 4月 26 13:15 diff
-rw-r--r-- 1 root root 26 4月 26 13:15 link
-rw-r--r-- 1 root root 57 4月 26 13:15 lower
drwxr-xr-x 1 root root 6 4月 26 13:15 merged
drwx------ 3 root root 18 4月 26 13:15 work
启动一个容器,也是载/var/lib/docker/overlay2目录下生成一层容器层,目录包括diff,link,lower,
merged,work。
diff记录每一层自己内容的数据;link记录该层链接目录(实际是l目录下到层的链接),比如在容器中创建目录
或在diff新增该目录;创建容器时将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录;work
用来完成如copy-on_write的操作。
启动容器后,可以在docker host查看mount情况
[root@bogon overlay2]# mount | grep overlay
overlay on
/var/lib/docker/overlay2/00bd7dcc4c91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cd
a0/merged type overlay
(rw,relatime,lowerdir=/var/lib/docker/overlay2/l/4GXMF7JGUPRUBLW5SM4X4FY5R2:/var/lib/do
cker/overlay2/l/MBRXMBFE2MYZYMO5RZLRLTMRGJ,upperdir=/var/lib/docker/overlay2/00bd7dcc4c
91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cda0/diff,workdir=/var/lib/docker/ove
rlay2/00bd7dcc4c91f9a1a1257b8c0683fdd0b6dfe18af26597457e92b4d15c20cda0/work)