目录
3. Dockerfile详解(dockerfile常用指令)
1. 镜像简介
1.1 Docker镜像是什么
传统企业是以交付应用的方式进行发布的,交付应用相当于可执行性程序,其整个应用与环境是分开维护的。随着容器技术的兴起,提出了交付环境的概念。交付环境与交付应用相比,交付的不仅是可执行程序,还交付可执行程序依赖的配置文件、类库甚至是整个文件系统。在Docker语境里面,环境就是镜像。从上图左下角镜像示例图可以看出,镜像本身的组织结构是分层的。其优点是,虽然它包含了所有的依赖,但是发布部署的时候不会显著增加信息的传输量。
1.2 镜像的表示
镜像的表示分为四部分:红色的部分是镜像中心域名,黄色的部分是镜像命名空间,我们可以根据命名空间进行权限控制等操作,绿色是镜像的名称,每个镜像有一个版本(即标签)。Docker官方的镜像不需要镜像中心的域名,有一些镜像可以省略命名空间。
1.3 镜像的分层结构
1. base 镜像简单来说就是不依赖其他任何镜像,完全从0开始建起, 其他镜像都是建立在他的之上,可以比喻为大楼的地基,docker镜像的鼻祖。 base 镜像有两层含义: (1)不依赖其他镜像,从 scratch 构建; (2)其他镜像可以之为基础进行扩展。 所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像, 比如 Ubuntu, Debian, CentOS 等。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。
添加文件:在容器中创建文件时,新文件被添加到容器层中。 读取文件:在容器中读取某个文件时,Docker会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。
修改文件:在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
删除文件:在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
只有当需要修改时才复制一份数据,这种特性被称作Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
2. 镜像的构建
2.1 commit提交
- docker commit 构建新镜像三部曲 运行容器 修改容器 将容器保存为新的镜像 - 缺点: 效率低、可重复性弱、容易出错 使用者无法对镜像进行审计,存在安全隐患 - 运行容器 # docker run -it --name demo demo:v1 修改容器 (以下命令在容器内运行) # echo helloworld > testfile 将容器保存为新的镜像 # docker commit demo demo:v2 查看镜像 # docker images demo:v1
[root@server1 ~]# docker run -it --name demo busybox ##生成容器demo / # ls bin etc file2 home root tmp var dev file1 file3 proc sys usr / # [root@server1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cf8ac9c4e00f busybox "sh" 51 seconds ago Exited (0) 8 seconds ago demo [root@server1 ~]# docker commit -m "add files" demo demo:v1 ##通过容器生成镜像 [root@server1 ~]# docker images ##v1是生成的镜像 REPOSITORY TAG IMAGE ID CREATED SIZE demo v1 16b70d3f6375 13 seconds ago 1.15MB busybox latest 59788edf1f3e 2 years ago 1.15MB game2048 latest 19299002fdbe 4 years ago 55.5MB mario latest 9a35a9e43e8c 5 years ago 198MB
uname 容器与真机公用同一个内核
2.2 通过Dockerfile提交
dockerfile的创建原理—>相当于一个一个进行提交
## 创建一个Dockerfile [root@server1 ~]# mkdir docker [root@server1 ~]# cd docker [root@server1 docker]# vim Dockerfile [root@server1 docker]# cat Dockerfile FROM busybox RUN touch file1 file2 file3 ##构建镜像 [root@server1 docker]# docker build -t demo:v2 .
在新建一个镜像,使用的是镜像v1的缓存
3. Dockerfile详解(dockerfile常用指令)
ctel+q+p 可以将容器打入后台,而不直接瑞出。
运行中的使用rm -f
暂停状态使用rm
删除镜像使用rmi3.1 FROM
指定base镜像,如果本地不存在会从远程仓库下载
3.2 MAINTAINER
设置镜像的作者,比如用户邮箱等
3.3 COPY
- 把文件从build context复制到镜像 支持两种形式:COPY src dest 和 COPY ["src", "dest"] src必须指定build context中的文件或目录
3.4 ADD
- 用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像: ADD nginx-1.20.2.tar.gz /
3.5 ENV
设置环境变量,变量可以被后续的指令使用: ENV HOSTNAME sevrer1.example.com
3.6 EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去: EXPOSE 80
mario测试8080端口
80映射容器的8080docker会自动开启虚拟网络宅
3.7 VOLUME
申明数据卷,通常指定的是应用的数据挂在点: VOLUME ["/data"]
查看挂载点位置 改变目录里的内容容器也会改变
3.8 WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建。
3.9 RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim3.10 CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。- Shell和exec格式的区别 # cat Dockerfile FROM busybox ENV name world ENTRYPOINT echo "hello, $name" - Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而下面的exec格式不会: # cat Dockerfile FROM busybox ENV name world ENTRYPOINT ["/bin/echo", "hello, $name"] - 需要改写成以下形式: # cat Dockerfile FROM busybox ENV name world ENTRYPOINT ["/bin/sh", "-c", "echo hello, $name"] - 官方推荐使用exec格式书写
- Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换。在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数。 # cat Dockerfile FROM busybox ENTRYPOINT ["/bin/echo", "hello"] CMD ["world"]
CMD
ENTRYPOINT和CMD
4.镜像的优化—搭建最简nginx
- 选择最精简的基础镜像 减少镜像的层数 清理镜像构建的中间产物 注意优化网络请求 尽量去用构建缓存 使用多阶段构建镜像
4.1 centos中安装nginx
删除镜像,保证实验环境纯净拉去centos
将nginx源码包复制进容器 配置阿里云软件仓库解决依赖性编译注释掉debug编译更快查看进程
4.2 通过Dockerfile文件安装nginx容器
官网提供的nginx包
[root@server1 docker]# vim Dockerfile [root@server1 docker]# cat Dockerfile FROM centos COPY CentOS-Base.repo /etc/yum.repos.d/ ADD nginx-1.20.0.tar.gz /mnt WORKDIR /mnt/nginx-1.20.0 RUN rpmdb --rebuilddb RUN yum install -y gcc make pcre-devel zlib-devel RUN ./configure RUN make RUN make install EXPOSE 80 VOLUME ["/usr/local/nginx/html"] CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"] [root@server1 docker]# docker build -t webserver:v1 . [root@server1 docker]# docker images ##比较images大小
5.3 减少镜像层数,清理镜像构建的中间产物
结果与5.2的大小接近
5.4 使用多阶段构建镜像
[root@server1 docker]# vim Dockerfile [root@server1 docker]# cat Dockerfile FROM centos as build COPY CentOS-Base.repo /etc/yum.repos.d/ ADD nginx-1.20.0.tar.gz /mnt WORKDIR /mnt/nginx-1.18.0 RUN rpmdb --rebuilddb && yum install -y gcc make pcre-devel zlib-devel &> /dev/null && ./configure &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -fr /mnt/nginx-1.18.0 && yum remove -y gcc make && yum clean all FROM rhel7 COPY --from=build /usr/local/nginx /usr/local/nginx EXPOSE 80 VOLUME ["/usr/local/nginx/html"] CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"] [root@server1 docker]# docker build -t webserver:v3 . [root@server1 docker]# docker images webserver
结果相差甚远,说明多阶段确实可以减少内存
5.5 选择最精简的基础镜像(google)
[root@server1 docker]# docker run -it --rm webserver:v3 bash bash-4.2# ldd /usr/local/nginx/sbin/nginx ##执行nginx,base中下面的执行文件、库文件都必须有。这些都需要复制到base中 linux-vdso.so.1 => (0x00007ffeb0d8a000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f34521ff000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f3451fe3000) libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f3451dac000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f3451b4b000) libz.so.1 => /lib64/libz.so.1 (0x00007f3451935000) libc.so.6 => /lib64/libc.so.6 (0x00007f3451574000) /lib64/ld-linux-x86-64.so.2 (0x00007f3452403000) libfreebl3.so => /lib64/libfreebl3.so (0x00007f34512f5000) ## 更精简的镜像:(goole准备好了)
github上的查找方法(下载一般需要外网)
可以通过命令直接下载最简单的base(不需要外网) 我们使用下载好的包 找到Dockerfile的配置文档
[root@server1 ~]# cd nginx/ [root@server1 nginx]# pwd /root/nginx [root@server1 nginx]# vim Dockerfile [root@server1 nginx]# cat Dockerfile FROM nginx:1.18.0 as base ##下载一个nginx1.18.0 # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones ARG TIME_ZONE RUN mkdir -p /opt/var/cache/nginx && \ cp -a --parents /usr/lib/nginx /opt && \ cp -a --parents /usr/share/nginx /opt && \ cp -a --parents /var/log/nginx /opt && \ cp -aL --parents /var/run /opt && \ cp -a --parents /etc/nginx /opt && \ cp -a --parents /etc/passwd /opt && \ cp -a --parents /etc/group /opt && \ cp -a --parents /usr/sbin/nginx /opt && \ cp -a --parents /usr/sbin/nginx-debug /opt && \ cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \ cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \ cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \ cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \ cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime FROM gcr.io/distroless/base-debian10 COPY --from=base /opt / EXPOSE 80 443 ENTRYPOINT ["nginx", "-g", "daemon off;"]
查看镜像为31.7MB比之前三组都要小
5. 总结补充
- 镜像常用子命令 images 显示镜像列表 history 显示镜像构建历史 commit 从容器创建镜像 build 从Dockerfile构建镜像 tag 给镜像打标签 search 搜索镜像 pull 从仓库拉取镜像 push 上传镜像到仓库 rmi 删除镜像