Docker镜像加载原理
UnionFS(联合文件系统)
一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像(Docker下载镜像时一层层下载就是这个)
特征:一次性同时加载多个文件系统,但从外面看起来只能看见一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
Docker的镜像实际上是由一层一层的文件系统组成,这种层级的文件系统即UnionFS
bootfs(boot file system):主要包括bootloads和kernel内核,bootloads主要是引导加载内核,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux系统是一样的,包括boot加载器和内核,当boot加载完成之后整个内核就在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs
rootfs(root file system):在bootfs上,包括的就是典型的Linux系统中的/dev、/proc、/bin、/etc等标准目录和文件,rootfs就是各种不同的操作系统发行版,比如Ubuntu、Centos等
所有的镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层:
例如基于Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层
Docker镜像都是只读的,当容器启动时,一个新的可写层被夹在到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层
然后可以将新的一层和原来的层一起打包成大的镜像进行发布
Commit镜像
docker commit 提交容器成为一个副本
docker commit -m=”提交的描述信息” -a=”作者” 容器id 目标镜像名:[TAG]
容器数据卷
为了容器的持久化和同步操作-容器间实现数据共享
MySQL,容器删了数据就没了==需求:数据可以存储在本地
将容器内的目录挂载到虚拟机上
例:docker run -p 3306:3306 --name mysql --privileged=true
-v /mydata/mysql/log:/var/log/mysql
-v /mydata/mysql/data:/var/lib/mysql
-v /mydata/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=root
-d mysql:8.0.26
-v /root/mysql/logs:/logs:将主机目录(/root/mysql)下的 logs 目录挂载到容器中的
/logs 日志目录
-v /root/mysql/data:/var/lib/mysql :将宿主机目录(/root/mysql)下的data目录挂载到容
器的 /var/lib/mysql 数据目录
docker inspect mysql(Mounts)查看挂载信息,数据挂载成功
进入容器内部docker exec -it mysql /bin/bash,cd /var/lib/mysql查看容器内部数据
查看宿主数据目录,内容保持一致
Dockerfile
就是用来构建docker镜像的构建文件,命令脚本
--镜像是一层层的,脚本是一个个的命令,通过脚本可构建镜像
在Java8的环境下,将宿主/var/lib/docker/volumes/xxx/_data挂载到容器内部的/temp目录
Docker执行Dockerfile的大致流程如下:
(1)docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
(4)docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成
DockerFile常用保留字指令
FROM -基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
MAINTAINER -镜像维护者的姓名和邮箱地址
RUN -容器构建时需要运行的命令,有两种形式,一是后面加shell命令如RUN yum -y install vim,另一种是exec格式,如RUN [“./test.php”, “dev”, “offline”]
EXPOSE -当前容器对外暴露出的端口
WORKDIR -指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
USER -指定该镜像以什么样的用户去执行,如果都不指定,默认是root
ENV用来在构建镜像过程中设置环境变量,这个环境变量可以在后续的其他指令中使用
ADD -将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
COPY -类似ADD,拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
VOLUME -容器数据卷,用于数据保存和持久化工作
CMD -指定容器启动后的要干的事情,格式跟RUN相似,和前面RUN命令的区别:CMD是在docker run 时运行,RUN是在 docker build时运行
docker学习(四):DockerFile微服务实战及docker端口映射_dockerfile指定端口映射_拒绝冗余的博客-CSDN博客
Docker网络
- 容器间的互联和通信以及端口映射
- 容器IP变动时候可以通过服务名直接网络通信而不受到影响
ens和lo接口:可以理解为宿主机的物理接口
在CentOS7的安装过程中如果有选择虚拟化的服务安装系统后,启动网卡时会发现有一个以太网桥连接的私网地址virbr0网卡(默认:192.168.122.1),作为虚拟机网桥的使用,为虚拟网卡提供NAT访问外网的功能。
启动docker后会产生一个名为docke0的虚拟网桥(桥接模式):
- Linux与Docker容器之间通过docker0是可以ping通的
- Docker容器与容器之间是可以相互ping通的
Docker网络模式:
- bridge:为每个容器分配设置IP,并将容器连接到docker0虚拟网桥,默认该模式
- host:容器不会虚拟出自己的网卡配置IP等,而是使用宿主机的IP和端口
- none:容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair和网桥连接、IP等只有一个lo(即禁用网络功能),需要手动为容器添加网卡配置IP等
- container:新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等,使用--network container:NAME或者容器ID指定
当docker启动MySQL,MySQL容器的连接到Bridge上的veth接口,形成veth-pair,默认的Bridge网络,网关默认是docker0,docker0的地址市172.17.0.1/24,则接入设备的IP以此类推:172.17.0.2/24
Docker网络,网络工程师还不赶紧收藏!_李白你好的博客-CSDN博客
查看Bridge上接口的信息:bridge link
得到容器ID和veth bridge interface的关系
- 新建文件touch docker_eth_pair.sh
- vim docker_eth_pair.sh(具体内容见博客快速得到容器ID和veth bridge interface的关系_格洛米爱学习的博客-CSDN博客)
- 赋予权限(chmod 777 docker_eth_pair.sh)
- 执行脚本./docker_eth_pair.sh
get_network_mode() {
docker inspect --format='{{.HostConfig.NetworkMode}}' "$1"
}
created_by_kubelet() {
[[ $(docker inspect --format='{{.Name}}' "$1") =~ ^/k8s_ ]]
}
for container_id in $(docker ps -q); do
network_mode=$(get_network_mode "${container_id}")
# skip the containers whose network_mode is 'host' or 'none',
# but do NOT skip the container created by kubelet.
if [[ "${network_mode}" == "host" || \
$(! created_by_kubelet "${container_id}") && "${network_mode}" == "none" ]]; then
echo "${container_id} => ${network_mode}"
continue
fi
# if one container's network_mode is 'other container',
# then get its root parent container's network_mode.
while grep container <<< "${network_mode}" -q; do
network_mode=$(get_network_mode "${network_mode/container:/}")
# skip the containers whose network_mode is 'host' or 'none',
# but do NOT skip the container created by kubelet.
if [[ "${network_mode}" == "host" || \
$(! created_by_kubelet "${container_id}") && "${network_mode}" == "none" ]]; then
echo "${container_id} => ${network_mode}"
continue 2
fi
done
# get current container's 'container_id'.
pid=$(docker inspect --format='{{.State.Pid}}' "${container_id}")
# get the 'id' of veth device in the container.
veth_id=$(nsenter -t "${pid}" -n ip link show eth0 |grep -oP '(?<=eth0@if)\d+(?=:)')
# get the 'name' of veth device in the 'docker0' bridge (or other name),
# which is the peer of veth device in the container.
veth_name=$(ip link show |sed -nr "s/^${veth_id}: *([^ ]*)@if.*/\1/p")
echo "${container_id} => ${veth_name}"
done
使用docker network inspect bridge查看特定网络的信息
docker network create xx 创建网络(默认Bridge)
docker network rm xx删除指定网络
Docker容器内部的IP是会发生变化的