创建容器镜像
镜像的内部机制
镜像层Layer
容器镜像会将一些重复的部分抽取出来,形成镜像层,术语叫“Layer”,以某种方式来共享这部分数据。
容器镜像内部是由多个镜像层组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,然后多个层像搭积木一样堆叠起来,再使用一种叫“Union FS 联合文件系统”的技术把它们合并在一起,就形成了容器最终看到的文件系统。
docker inspect
查看镜像的组成,镜像的分层信息在“RootFS"部分
docker inspect nginx:alpine
镜像层操作的逻辑
Docker在执行docker pull、docker rmi
等命令操作镜像的时候,会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会删除。
Dockerfile
Dockerfile是用来规定如何构建容器镜像的文件,相当于建筑施工的“施工图纸”。
一个简单的Dockerfile
# Dockerfile.busybox
FROM busybox # 选择基础镜像
CMD echo "hello world" # 启动容器时默认运行的命令
第一条指令是 FROM,所有的 Dockerfile 都要从它开始,表示选择构建使用的基础镜像,相当于“打地基”
第二条指令是 CMD,它指定 docker run 启动容器时默认运行的命令
构建镜像
docker build -f Dockerfile.busybox .
-f 参数指定 Dockerfile 文件名,后面必须跟一个文件路径,叫做“构建上下文”(build’s context),这里只是一个简单的点号,表示当前路径的意思
查看和运行镜像
docker inspect 115d
docker run 115d
docker images
编写正确、高效的Dockerfile
FROM
如果关注的是镜像的安全和大小,那么一般会选择 Alpine;如果关注的是应用的运行稳定性,那么可能会选择 Ubuntu、Debian、CentOS
FROM alpine:3.15 # 选择Alpine镜像
FROM ubuntu:bionic # 选择Ubuntu镜像
COPY
本机开发测试上的一些源码、配置等文件,需要打包到镜像,可以使用COPY命令,不过拷贝的源文件必须是“构建上下文”路径里的。
COPY ./a.txt /tmp/a.txt # 把构建上下文里的a.txt拷贝到镜像的/tmp目录
COPY /etc/hosts /tmp # 错误!不能使用构建上下文之外的文件
RUN
RUN 通常会是 Dockerfile 里最复杂的指令,会包含很多的 Shell 命令,但 Dockerfile 里一条指令只能是一行,所以有的 RUN 指令会在每行的末尾使用续行符 \,命令之间也会用 && 来连接,这样保证在逻辑上是一行
RUN apt-get update \
&& apt-get install -y \
build-essential \
curl \
make \
unzip \
&& cd /tmp \
&& curl -fSL xxx.tar.gz -o xxx.tar.gz\
&& tar xzf xxx.tar.gz \
&& cd xxx \
&& ./config \
&& make \
&& make clean
超长的RUN指令很不美观,可以把一些Shell命令集中到一个脚本文件中,用COPY命令拷贝进去再用RUN来执行:
COPY setup.sh /tmp/ # 拷贝脚本到/tmp目录
RUN cd /tmp && chmod +x setup.sh \ # 添加执行权限
&& ./setup.sh && rm setup.sh # 运行脚本然后再删除
ARG和ENV
RUN 指令实际上就是 Shell 编程,它有变量的概念,可以实现参数化运行,这在 Dockerfile 里也可以做到,需要使用两个指令 ARG 和 ENV
ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine"
ENV PATH=$PATH:/tmp
ENV DEBUG=OFF
区别:
- ARG:只在镜像构建过程中可见,容器运行时不可见
- ENV:不仅能够在构建镜像过程中使用,在容器运行时,也能以环境变量的形式被应用程序使用。
EXPOSE
用来声明容器对外服务的端口号。
对现在基于 Node.js、Tomcat、Nginx、Go 等开发的微服务系统来说非常有用
EXPOSE 443 # 默认是tcp协议
EXPOSE 53/udp # 可以指定udp协议
因为每个指令都会生成一个镜像层,所以 Dockerfile 里最好不要滥用指令,尽量精简合并 只有 RUN, COPY, ADD 会生成新的镜像层,其它指令只会产生临时层,不影响构建大小
docker build如何工作的
真正的镜像构建工作是由服务器端“Docker daemon”来完成的,“docker”客户端会将“构建上下文”目录打包上传,服务器端的“Docker daemon”才能获取这些文件。
.dockerignore
排除一些不需要的文件
# docker ignore
*.swp
*.sh
表示不打包上传后缀是“swp”“sh”的文件
-f 参数
加上-f
,显示指定Dockerfile文件
如果省略这个参数,docker build会在当前目录下找名字为Dockerfile的文件。
-t 参数
加上-t
,指定镜像的标签(tag),用:
分隔名字和标签,如果不提供标签默认为“latest”
总结
容器镜像是由多个只读的 Layer 构成的,同一个 Layer 可以被不同的镜像共享,减少了存储和传输的成本
编写Dockerfile:
- 创建镜像需要编写 Dockerfile,写清楚创建镜像的步骤,每个指令都会生成一个 Layer。
- Dockerfile 里,第一个指令必须是 FROM,用来选择基础镜像,常用的有 Alpine、Ubuntu 等。其他常用的指令有:COPY、RUN、EXPOSE,分别是拷贝文件,运行 Shell 命令,声明服务端口号。
- docker build 需要用 -f 来指定 Dockerfile,如果不指定就使用当前目录下名字是“Dockerfile”的文件。
- docker build 需要指定“构建上下文”,其中的文件会打包上传到 Docker daemon,所以尽量不要在“构建上下文”中存放多余的文件。
- 创建镜像的时候应当尽量使用 -t 参数,为镜像起一个有意义的名字,方便管理。
关于创建镜像还有很多高级技巧,比如使用缓存、多阶段构建等等,可以再参考 Docker 官方文档(docs.docker.com/engine/refe…),或者一些知名应用的镜像(如 Nginx、Redis、Node.js 等)进一步学习。
《极客时间-Kubernetes入门实战课》学习笔记 Day5