您的位置:首页 > 运维架构 > Docker

Dockerfile:镜像构建

2022-01-18 21:42 886 查看

[TOC]


Docker File

Docker 镜像原理

思考

  • Docker 镜像本质是什么?
  • Docker 中一个 centos 镜像为什么只有 200MB,而一个 centos 操作系统的 iso 文件要几个 G ?
  • Docker 中一个 tomcat 镜像为什么有 500MB,而一个 tomcat 安装包只有 70 多 MB ?

操作系统组成

  • 进程调度子系统
  • 进程通信子系统
  • 内存管理子系统
  • 设备管理子系统
  • 文件管理子系统
  • 网络通信子系统
  • 作业控制子系统

Linux 文件系统由 bootfs 和 rootfs 两部分组成

  • bootfs:包含 bootloader(引导加载程序)和 kernel(内核)。
  • rootfs:root 文件系统,包含的就是典型 Linux 系统中的 /dev,/proc,/bin,/etc 等标准目录和文件。
  • 不同的 linux 发行版,其 bootfs 基本一样,而 rootfs 则不同,如 ubuntu,centos 等。

Docker 镜像

  • Docker 镜像是由特殊的文件系统叠加而成。
  • 最底端是 bootfs,其使用的是宿主机的 bootfs 。
  • 第二层是 root 文件系统rootfs,称为 base image 。
  • 再往上可以叠加其他的镜像文件。
  • 统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。
  • 一个镜像可以放在另一个镜像的上面。位于下面的镜像称为父镜像,最底部的镜像成为基础镜像。
  • 当从一个镜像启动容器时,Docker 会在最顶层加载一个读写文件系统作为容器。

总结

  • Docker 镜像本质是什么? 是一个分层文件系统
  • Docker 中一个 centos 镜像为什么只有 200MB,而一个 centos 操作系统的 iso 文件要几个 G ?
      Centos 的 iso 镜像文件包含 bootfs 和 rootfs,而 docker 的 centos 镜像复用操作系统的 bootfs,只有 rootfs 和其他镜像层
  • Docker 中一个 tomcat 镜像为什么有 500MB,而一个 tomcat 安装包只有 70 多 MB ?
      由于 docker 中镜像是分层的,tomcat 虽然只有 70 多 MB,但他需要依赖于父镜像和基础镜像,所有整个对外暴露的 tomcat 镜像大小 500 多 MB

    Dockerfile

    镜像制作

    Docker 镜像如何制作?

    • 方法一:容器转为镜像
    docker commit 容器id 镜像名称:版本号
    docker save -o 压缩文件名称 镜像名称:版本号
    docker load –i 压缩文件名称
    • 方法二:使用 Dockerfile

    Dockerfile 概念

    什么是 Dockerfile ?

    • Dockerfile 是一个文本文件,包含了一条条的指令。
    • 每一条指令构建一层,基于基础镜像,最终构建出一个新的镜像。
    • 对于开发人员:可以为开发团队提供一个完全一致的开发环境。
    • 对于测试人员:可以直接拿开发时所构建的镜像或者通过 Dockerfile 文件构建一个新的镜像开始工作。
    • 对于运维人员:在部署时,可以实现应用的无缝移植。
    • 官方 Dockerfile 文件参考:https://github.com/CentOS/CentOS-Dockerfiles

    Dockerfile 主要组成部分

    1. 基础镜像信息:如 FROM centos:6.8
    2. 制作镜像时的操作命令:如 RUN yum insatll openssh-server -y
    3. 容器启动时的执行命令:如 CMD ["/bin/bash"]

    Dockerfile 常用命令

    • FROM:指定基础镜像。
    • MAINTAINER:指定维护者信息。
    • RUN:制作镜像过程中的操作命令。
    • ADD:加载主机文件(会自动解压)。
    • WORKDIR:设置当前工作目录。
    • VOLUME:设置卷,挂载并映射主机目录。
    • EXPOSE:指定对外的端口。
    • CMD:指定容器启动后的要执行的命令。
    • COPY:用于将本地文件或者目录拷贝进容器镜像。
    • ENV :环境变量。
    • ENTRYPOINT:容器启动后执行的命令。

    FROM 命令

    使用 Dockerfile 做定制的镜像,都是从基础镜像开始的。这个基础镜像可以是各种版本的操作系统、或带着中间件的操作系统、或是自定义的操作系统镜像。本地有就从本地直接用,本地没有会去下载。

    好习惯是将 Dockerfile 文件放在集中的位置,最好是共享存储里面。然后以项目名称创建目录,以 tag 名创建子目录。

    [root@localhost ~]# mkdir dockerfile
    [root@localhost ~]# cd dockerfile/
    [root@localhost dockerfile]# mkdir nginx
    [root@localhost dockerfile]# cd nginx
    [root@localhost nginx]# mkdir v1from
    [root@localhost nginx]# cd v1from/
    [root@localhost v1from]# vi Dockerfile

    注意创建 Dockerfile 名称就只能叫做“Dockerfile”,不然的话需要用 -f 参数指定 Dockerfile,看个人习惯。如果使用清晰的目录结构,建议使用 Dockerfile 命名。

    这个 Dockerfile 只有一行 from 命令,即引用 nginx 这个基础镜像,剩余的动作完全没有。这并没有实际意义,除了演示不会有人这么做 Dockerfile 的。

    # 构建镜像
    docker build -t nginx:v1from .

    注意

    • 最后的“.”,代表从本目录的 Dockerfile 执行,也可以使用绝对路径。
    • 打镜像的时候最后镜像名称和t ag 都由自己规划,即“:”前后都是自定义。
    • 如果名称和 tag 和之前版本重复,相当于提交新版本并覆盖原有镜像。

    使用新做的 nginx:v1from 镜像启动容器:


    RUN 命令

    用于执行后面跟着的命令行命令。有两种格式:

    RUN <命令行命令>
    RUN ["可执行文件", "参数1", "参数2", ...]

    仍然以 nginx 为基础镜像,这次增加一个 RUN 命令:

    说明:可以到 docker.com 上查看 nginx 镜像的说明文档,找到这个镜像中 nginx 默认 web 目录的位置为:/usr/share/nginx/html/,并使用 echo 命令打印一句话并重定向到该目录下的 index.html 文件中。

    容器启动后,再通过 nginx 看到这个静态页面查看命令执行结果。

    可见默认文件 index.html 的内容已被修改。

    注意事项

    • 命令行命令就是 shell 命令,常用于此的命令包括:yum install/apt-get、wget、tar 等,当然也可以用下一节学习的 COPY 命令,拷贝脚本到容器中,然后用 RUN 执行。
    • RUN 命令仅在 docker build 过程中生效,镜像完成后,docker run 后 RUN 脚本或命令则不再生效(可以用 yum 命令理解,已经在 docker build 的时候安装相应的东西进入镜像,启动容器的时候已经用到 yum 安装的内容,自然不用再安装一遍)。
    • RUN 是会触发镜像分层的,不合理的 RUN 会增加镜像大小。看以下四个例子:
    # docker build -t nginx:3runwithoutrm .
    FROM centos
    RUN yum install -y wget
    RUN wget http://nginx.org/download/nginx-1.19.10.tar.gz
    RUN tar -zxvf nginx-1.19.10.tar.gz
    
    # docker build -t nginx:1runwithoutrm .
    FROM centos
    RUN yum install wget && \
    wget http://nginx.org/download/nginx-1.19.10.tar.gz && \
    tar -zxvf nginx-1.19.10.tar.gz
    
    # docker build -t nginx:4runwithrm .
    FROM centos
    RUN yum install wget
    RUN wget http://nginx.org/download/nginx-1.19.10.tar.gz
    RUN tar -zxvf nginx-1.19.10.tar.gz
    RUN rm -f nginx-1.19.10.tar.gz
    
    # docker build -t nginx:1runwithrm .
    FROM centos
    RUN yum install wget && \
    wget http://nginx.org/download/nginx-1.19.10.tar.gz && \
    tar -zxvf nginx-1.19.10.tar.gz && \
    rm -f nginx-1.19.10.tar.gz

    结论

    • RUN 下载的文件,在同一个 RUN 中进行了删除,才能减小镜像大小(写时复制和联合挂载的特性)。
    • RUN 的个数并不是越少越好,细分后的 RUN 更容易在构建时候被当做 cache 利用到,一个 RUN 里过多的 && 连接命令则很难被复用。

    COPY 命令

    COPY 命令用于将本地文件或者目录拷贝进容器镜像。

    如下图,构建 dockerfile,目录 web,web 下有一个 a.html 文件,文件中一句话。

    启动容器并从浏览器中查看目录下的文件:

    注意事项

    • COPY 命令在拷贝目录的时候,和 cp 的习惯不一致,会把目录下面的内容拷走,而不拷目录本身,COPY a /path/to/ 相当于 cp a/* /path/to/。
    • COPY 命令在拷贝的时候,如果容器中最后一级目录不存在,会新建该目录并拷贝。综合上边一条,如果想要正确的把目录 a 拷贝到 /path/to/ 下,正确的写法是 COPY a /path/to/a(此时 /path/to 目录存在,/path/to/a 不存在)。

    CMD 命令

    CMD 用于容器启动时后执行命令,注意和 RUN 的区别(RUN 用于构建的时候执行命令)。

    在没有 ENTRYPOINT 的情况下,CMD 就是容器用来启动前台用户进程的命令。

    格式

    CMD <shell 命令>
    CMD ["可执行文件或命令", "参数1", "参数2", …]

    这里推荐第二种写法,不然的话第一种可执行文件只能用 sh 脚本。

    Dockerfile 说明

    1. yum 安装的 nginx,默认页面存放路径依然是 /usr/share/nginx/html;
    2. 该例子首先拷贝一个静态页面 default.html 到默认路径中;
    3. 然后新建一个静态页面路径,将 modify.html 文件拷贝到新建的路径中;
    4. COPY 一份修改后的配置文件到 /nginx 下;
    5. 最后用 CMD 启动 nginx,注意 -g daemon off; 是容器启动必须加的参数(别漏写“;”),以避免 nginx 后台启动,否则 nginx 后台启动将导致容器直接退出。

    1)docker run 时不加启动参数

    注意,此处 Dockerfile 中未加入 EXPOSE,需要指定后端端口,用小写 -p 启动:

    docker run -d -p 80:80 nginx:v4cmd

    2)docker run 时加启动参数

    docker run -d -p 81:80 nginx:v4cmd nginx -g "daemon off;" -c /nginx/nginx.conf

    可以看到,若启动时带了运行参数,则会覆盖掉 Dockerfile 中的 CMD 执行内容,以新的命令和参数启动。

    CMD 注意事项

    • CMD 如果有多个,只有最后一个生效。
    • CMD 会被传入的启动参数覆盖,这个特性在测试时很有用。

    ENTRYPOINT 命令

    和 CMD 是同类命令,可以单独使用,也可以和 CMD 配合使用。

    单独使用 ENTYRPOINT 格式

    ENTYRPOINT ["命令或者可执行脚本", "参数一", "参数二", …]

    单独使用 ENTRYPOINT 时,和 CMD 不同,其参数不会被 docker run 时传入的启动参数所覆盖,但是 docker run 传入的启动参数会作为 ENTRYPOINT 指定命令的参数传入。

    Dockerfile 示例内容

    FROM centos
    RUN yum install -y nginx
    COPY default.html /usr/share/nginx/html
    RUN mkdir -p /nginx/html
    COPY modify.html /nginx/html
    COPY nginx.conf /nginx
    ENTRYPOINT ["nginx","-g","daemon off;"]

    1)docker run 时不加启动参数

    2)docker run 时加启动参数

    docker run -d -p 81:80 nginx:v5entrypoint -g "daemon off;" -c /nginx/nginx.conf

    可以看到,modify.html 生效,但是这次传入的参数并不是完整的 nginx -g "daemon off;" -c /nginx/nginx.conf 命令,而是仅仅只有参数,并且参数覆盖了镜像中 ENTRYPOINT 的参数。


    ENTRYPOINT 和 CMD 混合使用

    若两个命令同时存在,CMD 本身不再运行命令,其内容变成 ENTRYPOINT 的参数,同时 CMD 的内容依然可以被 docker run 时候的启动参数覆盖,进而传递给 ENTRYPOINT。

    换句话说,两者组合,可以完成定参和变参的混合定义。

    Dockerfile 示例

    FROM centos
    RUN yum install -y nginx
    COPY default.html /usr/share/nginx/html
    RUN mkdir -p /nginx/html
    COPY modify.html /nginx/html
    COPY nginx.conf /nginx
    ENTRYPOINT ["nginx","-g","daemon off;","-c"]
    CMD ["/etc/nginx/nginx.conf"]

    1)docker run 时不带参数

    2)docker run 时带参数

    实际使用中,ENTRYPOINT 和 CMD 组合的方式会更常见一些


    VOLUME 命令

    实现效果和 docker run -v 挂载主机目录一样。

    写在 Dockerfile 中的好处是,万一在 docker run 时候忘记了 -v 参数,数据也不会丢失。

    另外 docker run -v 指令会覆盖 Dockerfile 的 VOLUME 指令。


    案例一:自定义 centos7 镜像

    需求:自定义 centos7 镜像

    1. 默认登录路径为 /usr
    2. 可以使用 vim

    实现步骤

    • 文件内容:

        定义父镜像:FROM centos:7
      1. 定义作者信息:MAINTAINER juno xxxx@xxxx.com
      2. 执行安装 vim 命令: RUN yum install -y vim
      3. 定义默认的工作目录:WORKDIR /usr
      4. 定义容器启动执行的命令:CMD /bin/bash
    • 通过 dockerfile 构建镜像:

    docker bulid –f dockerfile文件路径 –t 镜像名称:版本

    案例二:发布 SpringBoot 项目

    实现步骤

    • 文件内容:

      定义父镜像:FROM java:8
    • 定义作者信息:MAINTAINER juno xxxx@xxxx.com
    • 将jar包添加到容器:ADD springboot.jar app.jar
    • 定义容器启动执行的命令:CMD java–jar app.jar
  • 通过 dockerfile 构建镜像:

  • docker bulid –f dockerfile文件路径 –t 镜像名称:版本
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: