联合文件系统
Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。虚拟文件系统的目录结构就像普通 linux 的目录结构一样,镜像通过这些文件再加上宿主机的内核共同提供了一个 linux 的虚拟环境,每一层文件系统叫做一层 layer,联合文件系统可以对每一层文件系统设置三种权限,只读(readonly)、读写(readwrite)和写出(whiteout-able)。构建镜像的时候,从一个最基本的操作系统开始,每个构建提交的操作都相当于做一层的修改,增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样,当使用镜像的时候,我们只会看到一个完全的整体,不知道里面有几层,也不需要知道里面有几层。
镜像,即创建容器的模版,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快速的创建并启动容器,镜像的每一层文件系统都是只读的。镜像可以是自己创建,或者在别人的镜像基础上创建。我们常说的”ubuntu”镜像其实不是一个镜像名称,而是代表了一个名为ubuntu的Repository,同时在这个Repository下面有一系列打了tag的Image。
Docker镜像生成Docker容器时会在镜像的只读文件系统中创建一个可写层,容器中的进程就运行在这个可写层,这个可写层有两个状态,即运行态和退出态。当我们 docker run 运行容器后,docker 容器就进入了运行态,当我们停止正在运行中的容器时,docker 容器就进入了退出态。
我们可以通过docker commit将容器可写层的Container提交为Image。
我们可以通过docker image inspect查看docker的镜像层。
[root@VM-8-9-centos ~]# docker images -aREPOSITORY TAG IMAGE ID CREATED SIZE<none> <none> 98c016594fb3 44 minutes ago 231MBmycentos v1.0 047f4d09c048 44 minutes ago 231MBtomcat 9.0.54-jre8-openjdk 07ec6fa0983a 3 days ago 294MBcentos latest 5d0da3dc9764 4 weeks ago 231MB[root@VM-8-9-centos ~]# docker image inspect 047..."RootFS": {"Type": "layers","Layers": ["sha256:62a747bf1719d2d37fff5670ed40de6900a95743172de1b4434cb019b56f30b4","sha256:0b3c02b5d746e8cc8d537922b7c2abc17b22da7de268d068f2a4ebd55ac4f6d7","sha256:9f9f651e9303146e4dd98aca7da21fd8e21dd1a47d71da3d6d187da7691a6dc9","sha256:6ccd0e6bdf7a87d4ab19aa6a58ddb3c1b02b7f1b1bdc1f38d8ca5554c68de574","sha256:68c1e7509c6550f317b6957faa75b116e8041fa33d3c709a6e88d6cdb9cc4677","sha256:09d9063df784f1403edf136090b79ad3e4ad082ce1ef6a9481f9a710a31a65f6","sha256:c957181efe4d8e3b730bd9515d7d5207199fabd27ee050dff92385e8da104bea","sha256:d3906c7e324d82201592bc7d4233be79aaa6cc37e639eb81fa718d0cd09ad15f","sha256:daccf8928b9994bb841bb01d1551b696b895900f664a19e1084605479a47d431","sha256:85f2cb3577a067cc394acbfbc2e842b6f2617d717ae34dd983506d42bd790ba0"]},...
之前说到,docker images只查看最终镜像,docker images -a会查看中间镜像。
[root@VM-8-9-centos ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmycentos v1.0 047f4d09c048 57 minutes ago 231MBtomcat 9.0.54-jre8-openjdk 07ec6fa0983a 3 days ago 294MBcentos latest 5d0da3dc9764 4 weeks ago 231MB[root@VM-8-9-centos ~]# docker images -aREPOSITORY TAG IMAGE ID CREATED SIZEmycentos v1.0 047f4d09c048 57 minutes ago 231MB<none> <none> 98c016594fb3 57 minutes ago 231MBtomcat 9.0.54-jre8-openjdk 07ec6fa0983a 3 days ago 294MBcentos latest 5d0da3dc9764 4 weeks ago 231MB
98c016594fb3就是在构建mycentos:v1.0时创建的中间镜像。
面试题
问题:
- Docker 镜像本质是什么?
- Docker 中一个 centos 镜像为什么只有 200MB,而一个 centos 操作系统的 iso 文件要几个 G?
- Docker 中一个 tomcat 镜像为什么有 500MB,而一个 tomcat 安装包只有 70 多 MB?
答案:
- Docker 的本质是一个联合文件系统。
- Centos 的 iso 镜像文件包含 bootfs 和 rootfs,而 docker 的 centos 镜像服用操作系统的 bootfs,只有 rootfs 和其他镜像层。
由于 docker 中镜像是分层的,tomcat 虽然只有 70 多 MB,但他需要依赖与父镜像和基础镜像,所有整个对外暴露的 tomcat 镜像大小 500 多 MB。
dockerfile格式
dockerfile常用命令
FROM # 基础镜像,一切从这里开始构建MAINTAINER # 镜像是谁写的, 姓名+邮箱RUN # 镜像构建的时候需要运行的命令WORKDIR # 镜像的工作目录VOLUME # 挂载的目录EXPOSE # 保留端口配置CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令ONBUILD # 当构建一个继承了当前DockerFile的镜像的时候就会运行ONBUILD的指令。COPY # 类似ADD,将我们文件拷贝到镜像中ENV # 构建的时候设置环境变量!
创建SpringBoot项目的镜像
```bash D:\docker-build\target>java -jar docker-build-1.0-SNAPSHOT.jar —server.port=8081
. _ /\ / ‘ () \ \ \ \ ( ( )__ | ‘ | ‘| | ‘ \/ ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ‘ |_| .|| ||| |\, | / / / / =========||==============|__/=///_/ :: Spring Boot :: (v2.5.1)
2021-10-17 16:38:24.918 INFO 908 —- [ main] cn.addenda.Application : Starting Application v1.0-SNAPSHOT using Java 1.8.0_221 on LAPTOP-UCPC2H96 with PID 908 (D:\docker-build\target\docker-build-1.0-SNAPSHOT.jar started by ISJINHAO in D:\docker-build\target) 2021-10-17 16:38:24.921 INFO 908 —- [ main] cn.addenda.Application : No active profile set, falling back to default profiles: default 2021-10-17 16:38:25.687 INFO 908 —- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode! 2021-10-17 16:38:25.692 INFO 908 —- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode. 2021-10-17 16:38:25.719 INFO 908 —- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 6 ms. Found 0 Redis repository interfaces. 2021-10-17 16:38:26.628 INFO 908 —- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http) 2021-10-17 16:38:26.640 INFO 908 —- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-10-17 16:38:26.640 INFO 908 —- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46] 2021-10-17 16:38:26.706 INFO 908 —- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-10-17 16:38:26.706 INFO 908 —- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1732 ms 2021-10-17 16:38:27.640 INFO 908 —- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ‘’ 2021-10-17 16:38:27.650 INFO 908 —- [ main] cn.addenda.Application : Started Application in 3.227 seconds (JVM running for 3.656)
有一个Springboot项目的可执行jar包,下面将其构建为docker镜像。<a name="wYfLa"></a>### ENTRYPOINT```bash[root@VM-8-9-centos docker_build]# lltotal 28364-rw-r--r-- 1 root root 29038134 Oct 17 16:56 docker-build-1.0-SNAPSHOT.jar-rw-r--r-- 1 root root 237 Oct 17 17:19 dockerfile[root@VM-8-9-centos docker_build]# cat dockerfile# 指定父镜像FROM java:8# 作者MAINTAINER Addenda# 指定工作目录WORKDIR /apps/deploy# 运行一条指令RUN echo 'application begin to start!'# 从docker build上下文目录拷贝文件进来COPY ./*.jar /apps/deploy/app.jar# 设置参数,可通过命令行配置ENV app_port=8080# 申明端口EXPOSE $app_port# 指定启动命令ENTRYPOINT ["/bin/bash", "-c", "java -jar app.jar --server.port=$app_port"]
启动镜像
[root@VM-8-9-centos docker_build]# docker run -d -it docker-sb9e8b2fdb9c684d66bdbafe2df1b76f461d80ff81a8c7f5447c5a8eac602dc1ab[root@VM-8-9-centos docker_build]# docker exec -it 9e /bin/bashroot@9e8b2fdb9c68:/apps/deploy# curl localhost:8080/hellohello worldroot@9e8b2fdb9c68:/apps/deploy#hello worldroot@9e8b2fdb9c68:/apps/deploy# exit;exit[root@VM-8-9-centos docker_build]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9e8b2fdb9c68 docker-sb "/bin/bash -c 'java …" About a minute ago Up About a minute 8080/tcp naughty_goldwasser
docker ps输出的PORTS是我们在EXPOSE中指定的端口。
[root@VM-8-9-centos docker_build]# docker run -d -it -e app_port=8081 docker-sb834ff74b755ba95a98ad9c0b5ec802adcf5445fce7387c636a764e3e910996ca[root@VM-8-9-centos docker_build]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES834ff74b755b docker-sb "/bin/bash -c 'java …" 3 seconds ago Up 3 seconds 8080/tcp goofy_jemison9e8b2fdb9c68 docker-sb "/bin/bash -c 'java …" 3 minutes ago Up 3 minutes 8080/tcp naughty_goldwasser[root@VM-8-9-centos docker_build]# docker exec -it 83 /bin/bashroot@834ff74b755b:/apps/deploy# curl localhost:8080/hellocurl: (7) Failed to connect to localhost port 8080: Connection refusedroot@834ff74b755b:/apps/deploy# curl localhost:8081/hellohello worldroot@834ff74b755b:/apps/deploy# exitexit
当我们在命令中-e指定了端口之后,这时候docker ps -a显示的端口依然是8080,但是实际上暴露是我们指定的8081端口,所以可以看出,EXPOSE暴露的端口仅仅是一个申明,且在docker镜像制作的时候就确定了。
[root@VM-8-9-centos docker_build]# docker run -d -it -e app_port=8082 -p 80:8082 docker-sb8399d34ed066a51e01af5fc104fff734ceea99424d0ad909a426e2981e68fc9f[root@VM-8-9-centos docker_build]# curl localhost/hellohello world[root@VM-8-9-centos docker_build]#
ENTRYPOINT 追加
[root@VM-8-9-centos docker_build]# cat dockerfileFROM java:8MAINTAINER AddendaWORKDIR /apps/deployRUN echo 'application begin to start!'COPY ./*.jar /apps/deploy/app.jarENTRYPOINT ["java", "-jar", "app.jar"]
[root@VM-8-9-centos docker_build]# docker run -d -it -p 8083:8083 docker-sb-e --server.port=8083[root@VM-8-9-centos docker_build]# curl localhost:8083/hellohello world[root@VM-8-9-centos docker_build]#
CMD 替换
FROM java:8MAINTAINER AddendaWORKDIR /apps/deployRUN echo 'application begin to start!'COPY ./*.jar /apps/deploy/app.jarCMD ["java", "-jar", "app.jar"]
[root@VM-8-9-centos docker_build]# docker run -d -p 8084:8084 docker-sb-c java -jar app.jar --server.port=808414473830e9979b26cf3e78ada5ea1fa5715f496a3f31f319b4d54599ee6e6ada[root@VM-8-9-centos docker_build]# curl localhost:8084/hellohello world[root@VM-8-9-centos docker_build]#
如果dockerfile中用CMD启动服务,后面追加的命令会替换CMD里面的命令
CMD & ENTRYPOINT
当dockerfile里面既有CMD又有ENTRYPOINT时,最终执行的指令时
FROM java:8MAINTAINER AddendaWORKDIR /apps/deployRUN echo 'application begin to start!'COPY ./*.jar /apps/deploy/app.jarCMD ["--server.port=8082"]ENTRYPOINT ["java", "-jar", "app.jar"]
[root@VM-8-9-centos docker_build]# docker run -d -p 8082:8082 docker-sb-ecf79e8a9eb69e28f463036581b69b4cdfa88c9d3545f5aa72928054e3f7d75e0a[root@VM-8-9-centos docker_build]# curl localhost:8082/hellohello world[root@VM-8-9-centos docker_build]#
我们还可以通过docker ps来查看COMMAND命令,但是需要加上—no-trunc,否则会把命令截断。
[root@VM-8-9-centos docker_build]# docker ps -a --no-truncCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf79e8a9eb69e28f463036581b69b4cdfa88c9d3545f5aa72928054e3f7d75e0a docker-sb-ec "java -jar app.jar --server.port=8082" 2 minutes ago Up 2 minutes 0.0.0.0:8082->8082/tcp, :::8082->8082/tcp youthful_cori
可以看出dockerfile里面的CMD命令被当做了ENTRYPOINT命令的参数。
