Docker镜像
- 一、Docker镜像的分层结构
- 二、Docker镜像的构建
-
- 构建方法一(常规)
- 构建方法二(Dockerfile)
- 三、Dockerfile详解
- 四、镜像的封装及优化
-
- Docker封装rhel7+nginx镜像实例
- 封装后镜像的优化
一、Docker镜像的分层结构
镜像的分层结构:
- 共享宿主机的kernel
- base镜像提供的是最小的Linux发行版
- 同一docker主机支持运行多种Linux发行版
- 采用分层结构的最大好处是:共享资源
- Copy-on-Write 可写容器层
- 容器层以下所有镜像层都是只读的
- docker从上往下依次查找文件
- 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
- 一个镜像最多127层
二、Docker镜像的构建
镜像的分层结构:base image提供了根文件系统,所有容器数据都分类存放在/var/lib/docker目录中,可以共享资源,减少网络带宽资源占用
构建方法一(常规)
- docker commit 构建新镜像三部曲
- 运行容器
- 修改容器
- 将容器保存为新的镜像
-
缺点:
效率低、可重复性弱、容易出错
使用者无法对镜像进行审计,存在安全隐患
这里我们先在server1上拉取busybox镜像
然后进行构建新镜像三部曲
#运行容器
docker run -it --name demo busybox
#修改容器 (以下命令在容器内运行)
echo helloworld > testfile
#将容器保存为新的镜像
docker commit demo demo:v1
#查看镜像
docker images demo:v1
将demo容器提交保存为demo:v1新的镜像后,查看比较busybox和demo:v1可以看到demo:v1是在busybox镜像的基础上新加了一层
构建方法二(Dockerfile)
创建一个Dockerfile
此处需要注意:
- dockerfile有审计功能,每新加一层镜像都会有动作审计说明
- dockerfile不能放在根下,否则会把根下所有数据发送给docker引擎
(1)创建docker目录,在目录中建立编辑dockerfile
(2)使用dockerfile创建一个demo:v2新的镜像
(3)新的镜像已经建立为demo:v2,查看比较busybox和demo:v2可以看到demo:v2是在busybox镜像的基础上新加了一层
三、Dockerfile详解
dockerfile常用指令
-
FROM
指定base镜像,如果本地不存在会从远程仓库下载。
-
MAINTAINER
设置镜像的作者,比如用户邮箱等。
-
COPY
把文件从build context复制到镜像
支持两种形式:COPY src dest 和 COPY [“src”, “dest”]
src必须指定build context中的文件或目录
-
ADD
用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像:
ADD html.tar /var/www
ADD http://ip/html.tar /var/www
-
ENV
设置环境变量,变量可以被后续的指令使用:
ENV HOSTNAME sevrer1.example.com
-
EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去:
EXPOSE 80
-
VOLUME
申明数据卷,通常指定的是应用的数据挂在点:
VOLUME ["/var/www/html"]
-
WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建。
-
RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim
-
CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。
Shell和exec格式的区别
Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而下面的exec格式不会:
cat Dockerfile
///
FROM busybox
ENV name world
ENTRYPOINT ["/bin/echo", "hello, $name"]
shell需要改写成以下形式:
cat Dockerfile
///
FROM busybox
ENV name world
ENTRYPOINT ["/bin/sh", "-c", "echo hello, $name"]
Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换。在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数。
cat Dockerfile
///
FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]
下面可以看出在运行容器时的区别:
官方推荐使用exec格式书写
///Exec
docker run --rm busybox:v1
hello world
///Sell
docker run --rm busybox:v1 linux
hello linux
四、镜像的封装及优化
- 选择最精简的基础镜像
- 减少镜像的层数
- 清理镜像构建的中间产物
- 注意优化网络请求
- 尽量去用构建缓存
- 使用多阶段构建镜像
Docker封装rhel7+nginx镜像实例
(1)删除之前建立的所有demo容器,真实主机下载rhel7和nginx的镜像源压缩包并发送给server1
docker rmi `docker images | grep ^demo | awk '{print $3}'` #删除之前所有的demo容器
///真机中
scp rhel7.tar nginx-1.20.1.tar server1:
(2)准备软件仓库文件
(3)创建交互式容器,用于测试镜像环境是否损坏
(4)将nginx和仓库文件拷贝到容器中,并编辑Dockerfile文件,创建新的镜像
(5)使用rhel7镜像创建一个容器demo
docker inspect demo查看容器信息,看到分配的ip和在真实主机上挂载的路径
(6)在真实主机挂载路径上创建默认发布文件index.html,curl进行访问,可以获取到发布内容
封装后镜像的优化
封装好的镜像就可以正常运行了,但是查看镜像历史及列表,会发现该镜像的空间占用较大,不符合轻量化的原则,因此需要进行优化
1.减少镜像层数,减少中间产物
重新编辑Dockerfile
vim Dockerfile
创建镜像v2,可以看到镜像大小减少了
2.使用多阶段构建镜像进行优化
再次编辑Dockerfile:
vim Dockerfile
创建镜像v3,可以看到镜像更小了
最终优化需要下载base-debian10.tar ,下载并导入
vim Dockerfile
////
FROM nginx as base
ARG Asia/Shanghai
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
CMD ["nginx", "-g", "daemon off;"]
////
docker build -t rhel7:v4 #创建新的rhel7镜像
docker images rhel7 #查看镜像大小发现笔之前小了很多
优化为如此小之后依然可以运行一个容器,通过curl看到nginx发布的页面!