Docker学习笔记02
Docker镜像详解
UnionFS(联合文件系统)
我们下载的时候看到的一层层就是这个!
UnionFS (联合文件系统): Union文件系统( UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性∶一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootis(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
黑屏±–开机进入系统
rootfs (root fle system),在boots之上。包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu , Centos等等。
为什么每个容器都很小呢?
对于一个精简的OS , rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kerne|T自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别,因此不同的发行版可以公用bootfs。
分层理解
可以观察一下下载一个镜像的时候的日志输出,会发现镜像资源是一层一层的在下载的。
为什么Docker镜像要采用这种分层的结构呢?
最大的好处莫过于是资源共享了。比方说有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需要在磁盘上保留一份 base 镜像,同时内存中也只需要加载一份 base 镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。(只需要去下载没有的层。)
我们可以通过 docker inspect命令从 查看镜像分层
- 所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建新的镜像层。
- Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部
- 这一层就是我们通常说的容器层,容器之下的都叫镜像层
commit 镜像
创建镜像有两种方式,一种是再由镜像创建的容器的基础上做了修改后(通常是增加更多东西),然后讲该容器commit提交成一个镜像,之后就可以根据这个新的镜像直接创建容器。还有一种是根据Dockerfile来创建镜像,这个后面讲。
docker commit 提交容器成为一个新的副本
docker commit -m=“提交的描述信息” -a=“作者” 容器id 目标镜像名[tag]
-
案例:
- 问题:在 tomcat 容器的 webapps 下是没有文件的 ,每次启动的时候都要将 webapps.dist 目录下的内容拷贝到 webapps 目录下(cp -r webapps.dist/* webapps/)
- 解决方案:自己打包一个镜像,方便以后使用
之前的实战中已经将webapps.dist 目录下的内容拷贝到 webapps 目录下了,现在只需要将该容器commit成一个镜像之后使用就可以了。
在宿主机上执行命令:docker commit -a=“yh” -m=“add webappps content” 9179ed315114 yh/tomcat:v2
实践如下:
[root@muyi ~]# docker commit -a="yh" -m="add webappps content" 9179ed315114 yh/tomcat:v2 sha256:18e6806965319e6ad7860afaf8a7b3226b30aa433ade2c253a8b9744826d9bfe [root@muyi ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE yh/tomcat v2 18e680696531 6 seconds ago 673MB tomcat 9.0 710ec5c56683 3 days ago 668MB nginx latest 08b152afcfae 2 weeks ago 133MB mysql 5.7 8cf625070931 2 weeks ago 448MB portainer/portainer latest 580c0e4e98b0 4 months ago 79.1MB hello-world latest d1165f221234 5 months ago 13.3kB centos latest 300e315adb2f 8 months ago 209MB elasticsearch 7.6.2 f29a1ee41030 16 months ago 791MB
到了这里常用的操作基本都会了,剩下的还有一些比较复杂的内容:
容器数据卷、DockersFile、Docker网络
容器数据卷
什么是容器数据卷
docker的理念将运行的环境打包形成容器运行,运行可以伴随容器,但是我们对数据的要求是希望持久化,容器之间可以共享数据,Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据作为容器的一部分保存下来,那么当容器被删除之后,数据也就没了,为了能够保存数据,在docker容器中使用卷。卷就是目录或者文件,存在于一个或者多个容器中,但是不属于联合文件系统,因此能够绕过Union File System提供一些用于持久化数据或共享数据的特点
作用
卷的设计目的就是数据的持久化,完全独立与容器的生命周期,因此Docker不会在容器删除时删除其挂载的数据卷。
特点:
1. 数据卷可以在容器之间共享和重用数据。
2. 卷的更改可以直接生效。容器的配置就可以直接在主机中编写而不需要进入容器了。
3. 数据卷的更改不会包含在镜像的更新中。
4. 数据卷的生命周期一直持续到没有容器使用它为止。
容器的持久化
容器间继承+共享数据
使用
指定路径挂载
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
eg:
docker run -it -v /home/ceshi:/home centos /bin/bash
在/home/目录下新创建一个文件夹ceshi,然后与centos镜像新建的容器的/home目录双向绑定。一方修改后会立即同步,即使容器停止了,我们在主机中做了修改也会同步。
查看数据卷是否挂载成功。
docker inspect 容器ID,其中有Binds信息就成功了,
实战安装mysql5.7
查看数据卷用dockers volume ls
[root@muyi home]# docker volume ls
DRIVER VOLUME NAME
local 97443f8a2f7d76036e354fa3efcc624ffa1cfa59748002cf1d0f8fc2bb329396
使用docker inspect VOLUME NAME 能够查看该卷挂载在哪里。
[root@muyi home]# docker inspect 97443f8a2f7d76036e354fa3efcc624ffa1cfa59748002cf1d0f8fc2bb329396
[
{
"CreatedAt": "2021-08-10T20:04:38+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/97443f8a2f7d76036e354fa3efcc624ffa1cfa59748002cf1d0f8fc2bb329396/_data",
"Name": "97443f8a2f7d76036e354fa3efcc624ffa1cfa59748002cf1d0f8fc2bb329396",
"Options": null,
"Scope": "local"
}
]
默认路径挂载
匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
安装后通过 docker volume ls 查看所有数据卷的情况
具名挂载 (挂载时写上卷名与容器内路径)
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
docker volume ls时就会会先卷名
查看挂载目录
方式一:
通过docker inspect+容器名 nginx02,查看宿主机juming-nginx 的完整路径
方式二:
也可以 docker volume inspect +宿主机挂载路径 juming-nginx 就能直接看到完整路径
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volume/xxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况都使用具名挂载
#如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载
卷的权限
通过 -v 容器路径:ro rw 改变读写权限
ro #只读
rw #可读可写
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
只要看到ro 就说明这个路径智能通过宿主机来操作,容器 内部都是无法操作.(-P大写P表示的是匿名端口映射)
从容器上挂载,这样会更加方便 ,新创建的容器和之前的容器共享一个容器数据卷。
docker run -d -P --name nginx03 --volume-from nginx02 nginx
以上方式是在命令行中执行的,可以在dockerfile中写volume,这样就会直接创建了。
Dockerfile
可以在dockerhub中搜索一些镜像,然后点击其版本会跳转到github上对应的Dockerfile,可以参考他们的格式。
Docker Dockerfile | 菜鸟教程 (runoob.com)
构建镜像的方式有两种:一种是基于容器制作,另一种就是通过Dockerfile。
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
dockerfile命令
命令 | 作用 |
---|---|
FROM | 指令用于指定其后构建新镜像所使用的基础镜像。FROM 指令必是 Dockerfile 文件中的首条命令 |
RUN | 在镜像的构建过程中执行特定的命令,并生成一个中间镜像。 |
COPY | 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的<目标路径> 位置。 |
ADD | ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。比如<源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到<目标路径> 去。 |
ENV | 设置环境变量 |
EXPOSE | 为构建的镜像设置监听端口,使容器在运行时监听 |
VOLUME | OLUME用于创建挂载点,即向基于所构建镜像创始的容器添加卷: |
WORKDIR | 通过WORKDIR设置工作目录后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。 |
USER | USER 用于指定运行镜像所使用的用户:可以使用用户名、UID 或 GID,或是两者的组合。 |
CMD | 用于指定在容器启动时所要执行的命令 |
ENTRYPOINT | ENTRYPOINT 用于给容器配置一个可执行程序。ENTRYPOINT 与 CMD 非常类似,不同的是通过docker run 执行的命令不会覆盖 ENTRYPOINT,而docker run 命令中指定的任何参数,都会被当做参数再次传递给 ENTRYPOINT。 |
LABEL | LABEL用于为镜像添加元数据,元数以键值对的形式指定: |
ONBUILD | ONBUILD用于设置镜像触发器:当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。 |
使用 Dockerfile 定制镜像
编写Dockerfile
在一个空目录下,新建一个名为 Dockerfile 文件,并在文件内添加以下内容:
From centos
MAINTAINER yh<515294355@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
LABEL version="version01"
RUN指令每次都会构建一个中间层镜像
开始构建镜像
docker build -f Dockerfile -t mycentos:0.1 .
其中-f指定dockerfile文件,如果dockerfile文件名就是Dockerfile ,那么可以省略-f Dockerfile。
-t是给构建的镜像设置tag。
. 代表本次执行的上下文路径,
之后我们就可以查看我们自己构建的镜像了。
[root@muyi dockerfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 0.1 76af58bb2371 8 minutes ago 303MB
现在使用我们自己的镜像创建容器
docker run -it mycentos:0.1
查看镜像构建的步骤
docker history 镜像id
Docker网络连接原理:
只要安装docker,使用ip addr查看ip时就会看见docker0这个网卡。
docker守护进程就是通过docker0为docker容器提供网络连接的各种服务。docker0 就是Linux的虚拟网桥。网桥是OSI七层模型中的数据链路层的东西。
docker守护进程在一个容器启动时,实际上它要创建网络连接的两端。一端是在容器中的网络设备,而另一端是在运行docker守护进程的主机上打开一个名为veth*的一个接口,用来实现docker这个网桥与容器的网络通信。
在同一宿主机下,docker的容器是通过虚拟网桥来进行连接的。那么在默认情况下,在同一宿主机中运行的容器都是可以互相连接的。
Docker 容器互联
第一部分学习的端口映射相当于是容器和外部环境互联,使得容器能被外部访问。
这部分要学习的是容器之间相互访问。
默认情况下,在同一宿主机中运行的容器都是可以互相连接的。但是要基于ip,如果想要基于容器名来ping通,则需要使用其它方式,最开始的方式是在创建容器时使用–link 容器1Id 给创建的这个容器连接 容器1,但是使用link方式的话,实际上就是创建这个容器的时候在其host文件中添加了对容器1的映射,如果ip地址一换就不行了。
随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 --link
参数。
新建网络
下面先创建一个新的 Docker 网络。
$ docker network create -d bridge my-net
查看所有网络
docker network ls
这里面有个bridge名字的网络,该网络实际上就是docker01所在的网络,如果如果我们创建容器时不声明其所属网络,则默认会加到这个默认网络中。
新建容器连接到该网络
新建两个容器:tomcat01,tomcat02
docker run -itd --name tomcat01 --network my-net tomcat:9.0 /bin/bash
docker run -itd --name tomcat02 --network my-net tomcat:9.0 /bin/bash
进入tomcat01,然后ping tomcat02,检验是否连接成功
docker exec -it tomcat01 /bin/bash
ping tomcat02
发现ping成功。
这里我又新创建了tomcat03,tomcat04并加到该网络my-net中,然后使用docker network inspect my-net命令可以查看my-net中的所有容器网络信息:
发现他们都在一个网段内,ip地址从该网段内的2开始,因为1被分给网关了。
好处:
不同的集群创建不同的网络,保证容器间网络隔离。
如mysql集群一个网络,redis集群一个网络。
不同网络间联通
既然会为容器创建不同的网络,那么不同的网络间如何通信呢?
首先,由于不同网络内的容器属于不同的网段,不能直接相连
这里我创建一个新的容器tomcat05,不指定其network,这样就会加入到默认网络bridge中,和docker01一个网段内,它和tomcat01-04就属于不同网段,不能直接通信。
docker run -itd --name tomcat05 tomcat:9.0 /bin/bash
然后进入该容器,ping 容器tomcat02的ip :172.18.0.2,结果如下,证实不同network内的容器不能直连。
要想相连需要以下命令:
docker network connect [OPTIONS] NETWORK CONTAINER
实践:现在我们将tomcat05连接到my-net中,
docker network connect my-net tomcat05
然后再ping就发现ping通了,而且我们inspect tomcat05容器,会发现其network一栏中同时包括两个网络,相当于这个容器同时处于两个网络中。
Springboot项目打包Docker镜像
1.编写springboot项目
2.打包应用
maven里面的package打包,然后该目录下生成jar文件。
3.编写Dockerfile(根目录编写Dockerfile文件)
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
4.构建镜像()
然后将Dockerfile文件和jar包放在一个空目录下,执行
docker build . t my-image .
之后就创建了这个镜像。
5.发布运行
docker run -d - P --name my-web-app my-image
运行之后查看该容器的IP地址
docker ps
然后根据ip地址去访问。如果是容器所在主机访问就直接curl localhost:容器映射端口即可
所以学习docker之后,交付给别的项目可以就是一个镜像。
多个镜像如何处理?Docker Compose | 菜鸟教程 (runoob.com)](https://www.runoob.com/docker/docker-compose.html)
集权部署Swarm:Swarm 集群管理 | 菜鸟教程 (runoob.com)
参考
https://www.runoob.com/docker/docker-container-connection.html
https://www.bilibili.com/video/BV1og4y1q7M4?p=39