Docker commit和Dockerfile构建镜像

  • 1 镜像的分层结构
  • 2 镜像的构建
  • 2.1 Docker commit 构建镜像
  • 2.2 Dockerfile构建镜像
  • 3 Dockerfile常用的命令
  • 4 使用Dockerfile构建nginx镜像
  • 5 镜像的优化


1 镜像的分层结构

Docker 支持通过扩展现有镜像,创建新的镜像。新镜像是从 base 镜像一层一层叠加生成的,Dockerfile执行完每一条指令都会创建一个新的镜像,最终的镜像由层层叠加而成。

docker copy命令优化 docker commit build_nginx

分层的好处是共享资源: 当多个镜像从相同的base镜像构建而来,那么Docker host只需要在磁盘保存一份base镜像,同时内存中也只需要加载一份base镜像,就可以为所有的容器服务了。

容器的修改并不会影响镜像: 当容器启动时,一个新的可写层被加载到镜像的顶层,这一层通常被成为“容器层”。对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。容器层是可写的,镜像层是只读的。

2 镜像的构建

2.1 Docker commit 构建镜像

docker commit 构建新镜像三部曲

(1)运行容器:docker run -it --name demo busybox (2)修改容器
(3)将容器保存为新的镜像:docker commit -m "add files" demo demo:v1

[root@server1 sysctl.d]# docker run -it  --name demo busybox ##运行容器
/ # touch file1 ##修改容器 (以下命令在容器内运行)
/ # ls
bin    dev    etc    file1  home   proc   root   sys    tmp    usr    var
/ # 
[root@server1 sysctl.d]# docker commit -m "add files" demo demo:v1  ##将容器保存为新的镜像
sha256:02628d846be25a335e445eb4dfbf61f964e0337d3f2a98010307504f7f6d1c1a
[root@server1 sysctl.d]# docker images  ##查看镜像
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
demo         v1        02628d846be2   9 seconds ago   1.23MB
busybox      latest    b97242f89c8a   11 days ago     1.23MB
  • 查看镜像的分层结构
[root@server1 sysctl.d]# docker history demo:v1 ##查看镜像的分层结构
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
02628d846be2   23 seconds ago   sh                                              15B       add files
b97242f89c8a   11 days ago      /bin/sh -c #(nop)  CMD ["sh"]                   0B        
<missing>      11 days ago      /bin/sh -c #(nop) ADD file:92e389f575fd4d0a4…   1.23MB

镜像的缓存特性

  • 删除容器demo: docker rm demo
[root@server1 sysctl.d]# docker rm demo 
demo
[root@server1 sysctl.d]# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
  • 使用镜像demo:v1启动一个名为demo的容器:docker run -it --name demo demo:v1,可以看到对demo镜像的修改仍然有效(file1存在),查看demo:v1的历史信息无法了解镜像的具体信息,无法对镜像进行审计,存在安全隐患
[root@server1 sysctl.d]# docker run -it  --name demo demo:v1 ##镜像的缓存特性
/ # ls
bin    dev    etc    file1  home   proc   root   sys    tmp    usr    var
/ # 
[root@server1 sysctl.d]# docker history demo:v1 
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
02628d846be2   2 minutes ago   sh                                              15B       add files
b97242f89c8a   11 days ago     /bin/sh -c #(nop)  CMD ["sh"]                   0B
  • 查看 nginx:latest 镜像的历史信息:docker history nginx:latest
[root@server1 sysctl.d]# docker history busybox:latest ##查看镜像的分层结构
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
b97242f89c8a   11 days ago   /bin/sh -c #(nop)  CMD ["sh"]                   0B        
<missing>      11 days ago   /bin/sh -c #(nop) ADD file:92e389f575fd4d0a4…   1.23MB
[root@server1 sysctl.d]# docker images 
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
demo         v1        02628d846be2   3 minutes ago   1.23MB
busybox      latest    b97242f89c8a   11 days ago     1.23MB
[root@server1 sysctl.d]# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED              STATUS                          PORTS     NAMES
7cc55d083c5d   demo:v1   "sh"      About a minute ago   Exited (0) About a minute ago             demo
[root@server1 sysctl.d]# docker rm demo 
demo
[root@server1 sysctl.d]# docker rmi demo:v1 
Untagged: demo:v1
Deleted: sha256:02628d846be25a335e445eb4dfbf61f964e0337d3f2a98010307504f7f6d1c1a
Deleted: sha256:17e2efccb26766262c69378b558644f1723023262fecbf36b3eb6a54cb0a4b96

2.2 Dockerfile构建镜像

Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像

Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM

  • 创建一个Dockerfile
mkdir /docker
 cd /docker
 vim  Dockerfile
FROM busybox ##指定base镜像,如果本地不存在会从远程仓库下载
RUN touch file3
  • 构建镜像:docker build -t demo:v1 .
[root@server1 docker]# docker build -t demo:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : from busybox
 ---> b97242f89c8a
Step 2/2 : RUN touch file2
 ---> Running in 17ab5814c008
Removing intermediate container 17ab5814c008
 ---> 511ef075d432
Successfully built 511ef075d432
Successfully tagged demo:v1
  • 查看镜像的创建的历史过程:docker history demo:v1
[root@server1 docker]# docker history demo:v1 
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
511ef075d432   34 seconds ago   /bin/sh -c touch file2                          0B        
b97242f89c8a   11 days ago      /bin/sh -c #(nop)  CMD ["sh"]                   0B        
<missing>      11 days ago      /bin/sh -c #(nop) ADD file:92e389f575fd4d0a4…   1.23MB

docker copy命令优化 docker commit build_Dockerfile_02

  • 修改Dockerfile文件:vim /docker/Dockerfile
FROM demo:v1
RUN touch file3
  • 构建镜像:docker build -t demo:v2 .
[root@server1 docker]# docker build -t demo:v2 . 
Sending build context to Docker daemon  2.048kB
Step 1/2 : from demo:v1
 ---> 511ef075d432
Step 2/2 : RUN touch file3
 ---> Running in 87737d7a8e23
Removing intermediate container 87737d7a8e23
 ---> 97114c94a571
Successfully built 97114c94a571
Successfully tagged demo:v2
[root@server1 docker]# docker history demo:v2 ##查看镜像的历史创建过程
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
97114c94a571   7 seconds ago    /bin/sh -c touch file3                          0B        
511ef075d432   12 minutes ago   /bin/sh -c touch file2                          0B        
b97242f89c8a   11 days ago      /bin/sh -c #(nop)  CMD ["sh"]                   0B        
<missing>      11 days ago      /bin/sh -c #(nop) ADD file:92e389f575fd4d0a4…   1.23MB

3 Dockerfile常用的命令

(1)FROM:指定基础镜像,必须为第一个命令

(2)MAINTAINER: 维护者信息

(3)RUN:构建镜像时执行的命令

(4)COPY:把文件从build context复制到镜像

  • 支持两种形式:COPY src destCOPY ["src", "dest"]
  • src必须指定build context中的文件或目录

docker copy命令优化 docker commit build_Dockerfile_03

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3 ##创建file3
COPY index.html /  ##复制当前目录index.html文件到镜像的根目录
  • 构建镜像demo:v1:`docker build -t demo:v1 .
  • 查看镜像的创建的历史过程:docker history demo:v1
  • 运行容器:docker run -it --rm demo:v1

docker copy命令优化 docker commit build_docker copy命令优化_04


(5)ADD:用法与COPY类似,不同的是src如果是归档压缩文件,文件会被自动解压到dest(网络压缩资源不会被解压),也可以自动下载URL并拷贝到镜像

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
  • 构建镜像demo:v2:docker build -t demo:v2 .
  • 查看镜像的创建的历史过程:docker history demo:v2

docker copy命令优化 docker commit build_docker copy命令优化_05

  • 运行容器:docker run --rm -it demo:v2,test.tar.gz文件被自动解压到/mnt

docker copy命令优化 docker commit build_docker_06

(6)ENV:设置环境变量,变量可以被后续的指令使用

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
ENV HOSTNAME=server1
  • 构建镜像demo:v3:docker build -t demo:v3 .

docker copy命令优化 docker commit build_docker_07

  • 查看镜像创建的历史过程:docker history demo:v3
  • 运行容器:docker run --rm -it demo:v3

docker copy命令优化 docker commit build_docker_08

(7)EXPOSE:指定于外界交互的端口

如果容器中运行应用服务,可以把服务端口暴露出去, EXPOSE并不会让容器的端口访问到主机;要使其可访问,需要在docker run运行容器时通过-p来发布这些端口

  • 从仓库中下载nginx镜像:docker pull nginx
  • 查看所有的镜像资源:docker images

docker copy命令优化 docker commit build_Dockerfile_09


不做端口映射,会随机分配端口号

  • 将nginx打入后台运行: docker run -d --name demo nginx
  • 查看服务和端口号:netstat -antlp

docker copy命令优化 docker commit build_nginx_10

  • 查看容器的配置信息:docker inspect demo

docker copy命令优化 docker commit build_Dockerfile_11

docker copy命令优化 docker commit build_Dockerfile_12

随机指定端口号

  • 将镜像nginx以后台模式启动一个容器,并将容器的80端口映射到主机随机端口:docker run -d --name demo -P nginx
  • 查看容器的配置信息:docker inspect demo

docker copy命令优化 docker commit build_docker copy命令优化_13

  • 查看服务和端口号:netstat -antlp

docker copy命令优化 docker commit build_nginx_14

(8)VOLUME:用于指定持久化目录

一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:

1-  卷可以容器间共享和重用
2-  容器并不一定要和其它容器共享卷
3-  修改卷后会立即生效
4-  对卷的修改不会对镜像产生影响
5-  卷会一直存在,直到没有任何容器在使用它
  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
ENV HOSTNAME=server1
EXPOSE 80
VOLUME ["/data"]
  • 构建镜像demo:v5:docker build -t demo:v5 .

docker copy命令优化 docker commit build_nginx_15

  • 查看镜像的创建的历史过程:docker history demo:v5

docker copy命令优化 docker commit build_docker_16

  • 运行容器:docker run -it --name demo demo:v5

docker copy命令优化 docker commit build_Dockerfile_17

  • 查看容器的配置信息:docker inspect demo

docker copy命令优化 docker commit build_docker copy命令优化_18

docker copy命令优化 docker commit build_nginx_19

  • 进入卷的挂载资源目录,在该目录中创建file2

docker copy命令优化 docker commit build_nginx_20

  • 进入demo的容器docker attach demo,/data目录中也有file2

docker copy命令优化 docker commit build_nginx_21

  • 列出本地数据卷:docker volume ls
  • 移除未使用的数据卷:docker volume prune

docker copy命令优化 docker commit build_Dockerfile_22

(9)CMD 与 ENTRYPOINT:都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。

  • CMD会被docker run后面的命令行覆盖,nginx不会执行,bash会执行:docker run -it --rm nginx bash

docker copy命令优化 docker commit build_nginx_23

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
ENV HOSTNAME=server1
EXPOSE 80
VOLUME ["/data"]
CMD echo "hello $HOSTNAME"
  • 构建镜像demo:v6:docker build -t demo:v6 .

docker copy命令优化 docker commit build_docker_24

  • 查看镜像的创建的历史过程:docker history demo:v6
  • 运行容器: docker run -it --name demo demo:v6

docker copy命令优化 docker commit build_docker copy命令优化_25

Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,exec格式不会,但所exec格式修改后可以解析变量

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
ENV HOSTNAME=server1
EXPOSE 80
VOLUME ["/data"]
CMD ["/bin/sh", "-c", "echo hello $HOSTNAME"]
  • 构建镜像demo:v7:docker build -t demo:v7 .
  • 运行容器: docker run -it --name demo demo:v7

docker copy命令优化 docker commit build_Dockerfile_26

docker run后面的参数可以传递给ENTRYPOINT指令当作参数Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM busybox
RUN touch file3
COPY index.html /
ADD test.tar.gz  /mnt
ENV HOSTNAME=server1
EXPOSE 80
VOLUME ["/data"]
ENTRYPOINT ["echo", "hello"]
ENTRYPOINT ["echo", "world"]
  • 构建镜像demo:v8:docker build -t demo:v8 .
  • 运行容器: docker run -it --name demo demo:v78

docker copy命令优化 docker commit build_docker_27

4 使用Dockerfile构建nginx镜像

  • 下载nginx的源码 nginx-1.18.0.tar.gz

docker copy命令优化 docker commit build_docker_28

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz   /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel make
RUN ./configure
RUN make
RUN make install 
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
  • 构建镜像webserver:v1:docker build -t webserver:v1 .
  • 查看镜像:docker images
  • 显示镜像的创建过程:docker history webserver:v1

docker copy命令优化 docker commit build_docker copy命令优化_29

  • 运行容器:docker run -d --name webserver webserver:v1

docker copy命令优化 docker commit build_Dockerfile_30

  • 列出所有容器:docker ps

docker copy命令优化 docker commit build_docker copy命令优化_31

  • 查看容器的配置信息:docker inspect webserver

docker copy命令优化 docker commit build_Dockerfile_32

  • 测试

docker copy命令优化 docker commit build_nginx_33

docker copy命令优化 docker commit build_docker copy命令优化_34

docker copy命令优化 docker commit build_nginx_35

5 镜像的优化

镜像优化的方法:

  1. 选择最精简的基础镜像
  2. 减少镜像的层数
  3. 清理镜像构建的中间产物
  4. 优化网络请求
  5. 尽量用构建缓存
  6. 使用多阶段构建镜像

(1)减少镜像的层数 、用构建缓存

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz   /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &>/dev/null  && ./configure &>/dev/null  && make &>/dev/null  && make install &>/dev/null
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
  • 构建镜像webserver:v2: docker build -t webserver:v2 .

docker copy命令优化 docker commit build_docker_36

  • 列出本地所有的镜像:docker images

docker copy命令优化 docker commit build_Dockerfile_37

  • 显示镜像的创建过程:docker history webserver:v2

docker copy命令优化 docker commit build_docker copy命令优化_38

(2)清理镜像构建的中间产物

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz   /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make &>/dev/null  && ./configure &>/dev/null  && make &>/dev/null  && make install &>/dev/null  && yum remove -y gcc make  &>/dev/null  && yum clean all  &>/dev/null
EXPOSE 80 
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
  • 构建镜像webserver:v3: docker build -t webserver:v3 .

docker copy命令优化 docker commit build_docker_39

  • 列出本地所有的镜像:docker images

docker copy命令优化 docker commit build_docker copy命令优化_40

  • 显示镜像的创建过程:docker history webserver:v3
  • docker copy命令优化 docker commit build_docker_41


(3)多阶段构建镜像

通过多阶段构建,最终镜像可以仅包含最后生成的可执行文件,和必须的运行时依赖,大大减少镜像体积。

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM rhel7 as build
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz   /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel  make  && ./configure   && make  && make install && 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;"]
  • 构建镜像webserver:v4: docker build -t webserver:v4 .

docker copy命令优化 docker commit build_nginx_42

  • 列出本地所有的镜像:docker images

docker copy命令优化 docker commit build_docker_43

  • 显示镜像的创建过程:docker history webserver:v4

docker copy命令优化 docker commit build_nginx_44

(4)选择最精简的基础镜像

  • 从仓库中下载nginx1.18.0镜像: docker pull nginx:1.18.0

docker copy命令优化 docker commit build_docker copy命令优化_45

  • 下载基础镜像,Distroless是Google推出的一个仅仅包含运行时环境,不包含包管理器,shell等其他程序
    导入镜像到本地:docker load -i base-debian10.tar

docker copy命令优化 docker commit build_docker_46

  • 编辑Dockerfile文件: vim /docker/Dockerfile
FROM nginx:1.18.0 as base
# 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 / ### 从base中复制编译好的二进制文件

EXPOSE 80 443

ENTRYPOINT ["nginx", "-g", "daemon off;"]
  • 构建镜像webserver:v5:docker build -t webserver:v5 .

docker copy命令优化 docker commit build_Dockerfile_47

  • 显示镜像的创建过程:docker history webserver:v5

docker copy命令优化 docker commit build_Dockerfile_48

  • 查看webserver的镜像:docker images webserver

docker copy命令优化 docker commit build_nginx_49

  • 运行容器:docker run -d --name webserver webserver:v5
  • 查看容器的配置信息:docker inspect webserver

docker copy命令优化 docker commit build_Dockerfile_50

  • 测试

docker copy命令优化 docker commit build_nginx_51