Docker 镜像结构详解

Docker 镜像结构详解

一、镜像与容器关系

基本概念

  • Docker镜像:支撑Docker容器运行的静态文件系统,如centos:7镜像提供基本的CentOS 7发行版环境(不包含Linux内核)
  • Docker容器:动态的运行实例,包含进程、内存、CPU等资源

核心关系

  • 容器实质上是一个或多个运行进程,父进程为Docker守护进程
  • 容器隔离的关键:每个容器拥有独立的文件系统,由Docker镜像提供
  • 镜像包含应用程序运行所需的所有内容:
    • 代码/二进制文件
    • 运行时环境
    • 依赖项
    • 文件系统对象

静态到动态的转换

  • 转换依据:镜像的json配置文件
  • 转换执行者:Docker守护进程
  • 转换过程
    1. 解析镜像json文件
    2. 配置运行环境
    3. 运行指定进程
    4. 完成容器创建

容器运行后,镜像主要作用是为容器进程提供文件系统资源。

二、镜像的分层结构

Base镜像

两层含义

  1. 不依赖其他镜像,从scratch构建
  2. 作为基础镜像进行扩展

特点

  • 通常是各种Linux发行版镜像
  • 不同发行版区别主要在rootfs
  • 容器使用Docker host的kernel且不能修改

分层架构原理

Docker里的镜像绝大部分都是在别的镜像的基础上去进行创建的,也就是使用镜像的分层结构。实际上,Docker Hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来的。比如我们现在构建一个新的镜像,具体操作如下

实践示例vim Dockerfile

dockerfile 复制代码
FROM centos:7
RUN mkdir /galaxy
RUN touch /galaxy/cy
CMD ["/bin/bash"]

构建命令:

bash 复制代码
docker build -t centos:cy .

层级关系

  • 容器层:最顶部的可写层
  • 镜像层:容器层之下的所有只读层
  • 文件访问:上层文件覆盖下层同名文件,用户看到的是叠加后的文件系统

Docker镜像构建介绍

Docker镜像可以通过Docker hub或者阿里云等仓库中获取,这些镜像是由官方或者社区人员提供的,对于Docker用户来说并不能满足我们的需求,但是从无开始构建镜像成本大。常用的数据库、中间件、应用软件等都有现成的Docker官方镜像或社区创建的镜像,我们只需要稍作配置就可以直接使用。

使用现成镜像的好处除了省去自己做镜像的工作量外,更重要的是可以利用前人的经验。特别是使用那些官方镜像,因为Docker的工程师知道如何更好的在容器中运行软件。

当然,某些情况下我们也不得不自己构建镜像,比如找不到现成的镜像,比如自己开发的应用程序,需要在镜像中加入特定的功能。

三、Dockerfile详解

镜像构建方法

  1. docker commit命令(不推荐)
  2. 基于本地模板导入
  3. Dockerfile构建(推荐)

docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我们所需要的内容,再使用docker commit将容器打包为镜像即可。

bash 复制代码
# 1、先基于centos7运行容器,容器名为cy,并使用-it生成终端进入容器。 
[root@docker ~] docker run --name cy -it centos:7 /bin/bash
# 2、在容器中安装vim-common。
[root@2dc0f7d06aa8 /] yum -y install vim-common
# 3、退出容器后,使用docker commit将v1容器打包为镜像,新镜像名为centos:v1
[root@docker ~] docker commit cy centoscy:7
[root@docker ~] docker images | grep centos
[root@docker ~] docker run --name cy2 -it centoscy:7

Dockerfile优势

  • 可重复性强
  • 构建过程透明
  • 便于审计
  • 自动化程度高

Dockerfile结构

四个主要部分

  1. 基础镜像信息
  2. 维护者信息
  3. 镜像操作指令
  4. 容器启动执行指令

示例

dockerfile 复制代码
[root@docker ~] ls
Dockerfile
[root@docker ~] vim Dockerfile 
# 1、第一行必须指定,基础镜像信息 
FROM centos:7
# 2、维护者信息
MAINTAINER chenyu@example.com
# 3、镜像操作指令
RUN yum install -y httpd EXPOSE 80
# 4、容器启动执行指令
CMD ["/bin/bash"]

构建命令:

bash 复制代码
docker build -t httpd:cycy /root/

镜像构建过程

把构建容器所需要的指令都存放在Dockerfile文件中,这个文件的名字是固定的,不能够更改,再使用docker build命令构建容器,使用-t定义新的镜像名,如果构建镜像的Dockerfile文件不在当前目录下可以使用-f指定 Dockerfile文件路径,示例如下:

bash 复制代码
[root@docker ~] docker build -t httpd:cycy /root/

通过以上镜像的构建过程可以看出,Dockerfile文件内的指令会逐一运行,构建过程如下

  1. 下载centos7镜像
  2. 添加镜像构建者信息
  3. 基于centos7镜像启动容器,安装httpd软件,安装完毕后将容器打包为镜像
  4. 基于上一步生成的镜像启动容器,将80端口打开,打开后将容器打包为镜像
  5. 基于上一步生成的镜像启动容器,添加容器启动后需要执行的指令,再打包为镜像
ini 复制代码
[root@docker ~] docker history httpd:cycy

IMAGE		    CREATED			CREATED BY								     SIZE COMMENT
b44f420b6834	10 minutes ago	/bin/sh -c 	 #(nop) CMD ["/bin/bash"]		    0B	
65c05b81e846	10 minutes ago	/bin/sh -c 	 #(nop) EXPOSE 80				   0B	
e7e8c35f9b15	10 minutes ago	/bin/sh -c 	 yum -y install httpd			    203MB 
a9c67ae8f88e	13 minutes ago	/bin/sh -c 	 #(nop) MAINTAINER chenyu@example...   0B 
eeb6ee3f44bd	9 months ago	/bin/sh -c 	 #(nop) CMD ["/bin/bash"]			0B
<missing>	    9 months ago	/bin/sh -c 	 #(nop) LABEL org.label-schema.sc...	 0B
<missing>	    9 months ago	/bin/sh -c   #(nop) ADD file:b3ebbe8bd304723d4...	 204MB

镜像缓存机制

  • Docker缓存构建过程中的每一层临时镜像
  • 构建新镜像时复用缓存层,加速构建
  • 禁用缓存:docker build --no-cache

使用DockerFile文件构建完镜像以后,Docker会把构建过程中的每一层临时镜像进行缓存。在构建新镜像时,可以直接使用之前缓存的镜像层,这样能加速镜像的构建。镜像缓存示例如下

bash 复制代码
[root@docker ~] vim Dockerfile 
# 1、第一行必须指定,基础镜像信息 
FROM centos:7
# 2、维护者信息
MAINTAINER chenyu@example.com
# 3、镜像操作指令
RUN mkdir /galaxy
[root@docker ~] docker build -t cycentos:7 .

四、Dockerfile指令详解

1. FROM

FROM指令必须为Dockerfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令 运行都要依靠此基础镜像所提供的的环境。实际使用中,如果没有指定仓库,docker build会先从本机查找是否有此基 础镜像,如果没有会默认去Docker Hub Registry上拉取,再找不到就会报错,格式如下

Digest:镜像的哈希码,防止镜像被冒名顶替

dockerfile 复制代码
FROM <Repository>[:<Tag>]
FROM <Repository>@<Digest>
  • 必须为第一个非注释行
  • 指定基础镜像

2. MAINTAINER/LABEL

MAINTAINER指令用于让Dockerfile的作者提供个人的信息,Dockerfile并不限制MAINTAINER指令的位置, 但是建议放在FROM指令之后,在较新的Docker版本中,已经被LABEL替代,格式如下

LABEL指令用于让用户为镜像指定各种元数据(键值对的格式),格式如下

dockerfile 复制代码
MAINTAINER "cy@example.com"
LABEL <key>=<value> <key>=<value>
  • 作者信息
  • 镜像元数据

3. COPY/ADD

COPY指令用于复制宿主机上的文件到目标镜像中,格式如下

dockerfile 复制代码
COPY <src>... <dest>
COPY ["<src>",... "<dest>"]
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]

:要复制的源文件或者目录,支持通配符

:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否则,COPY指令会以 WORKDIR为其起始路径。如果路径中如果包含空白字符,建议使用第二种格式用引号引起来,否则会被 当成两个文件

  • 复制文件到镜像
  • ADD支持tar文件和URL

4. WORKDIR

WORKDIR指令用于指定工作目录,可以指多个,每个WORKDIR只影响他下面的指令,直到遇见 下一个WORKDIR为止。WORKDIR也可以调用由ENV指令定义的变量。,格式如下

dockerfile 复制代码
WORKDIR 相对路径或者绝对路径
  • 指定工作目录

5. VOLUME

VOLUME指令用于在镜像中创建一个挂载点目录。Volume有两种类型:绑定挂载卷和docker管 理的卷。在Dockerfile中只支持Docker管理的卷,也就是说只能指定容器内的路径,不能指定宿主机的路 径,格式如下

dockerfile 复制代码
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
  • 创建挂载点目录

6. EXPOSE

EXPOSE指令用于指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访 问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配-P的参数才能将待暴露的状态转换 为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟-p :443一个意思。EXPOSE指令可以一 次指定多个端口,例如:EXPOSE 11111/udp 11112/tcp,格式如下

dockerfile 复制代码
EXPOSE <port>[/<protocol>] [<port>[/<protocol>] ...]
  • 指定待暴露端口

7. ENV

ENV指令用于为镜像定义所需的环境变量,并可被ENV指令后面的其它指令所调用。调用格式为variable_name或者 {variable_name},使用docker run启动容器的时候加上-e的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指 定的此variable_name的值。但是不会影响到Dockerfile中已经引用过此变量的文件名,格式如下

dockerfile 复制代码
ENV <key> <value>
ENV <key>=<value>...
  • 设置环境变量

8. RUN

RUN指令运行于docker build过程中运行的程序,可以是任何命令。RUN指令后所执行的命令必须在FROM指令后的 基础镜像中存在才行,格式如下

dockerfile 复制代码
RUN <command>
RUN ["executable", "param1", "param2"]

通常是一个shell命令,系统默认会把后面的命令作为shell的子进程来运行,以"/bin/sh -c"来运行它。 第二种格式的参数是一个JSON格式的数组,其中"executable"为要运行的命令,后面的"paramN"为传递给命令的选项或参 数

  • 构建过程中执行的命令

9. CMD

CMD指令用于用户指定启动容器的默认要运行的程序,也就是PID为1的进程命令,且其运行结束后容器也会终止。如果 不指定,默认是bash。CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖。Dockerfile中可以存在多个CMD 指令,但仅最后一个生效。因为一个Docker容器只能运行一个PID为1的进程。类似于RUN指令,也可以运行任意命令或程 序,但是两者的运行时间点不同。RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器时,格式 如下

dockerfile 复制代码
CMD command param1 param2
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
  • 容器启动默认运行程序
  • 仅最后一个生效

10. ENTRYPOINT

ENTRYPOINT指令类似CMD指令的功能,用于为容器指定默认运行程序。Dockerfile中可以存在多个ENTRYPOINT指 令,但仅最后一个生效,与CMD区别在于,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这 些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序,格式如下

dockerfile 复制代码
ENTRYPOINT command param1 param2
ENTRYPOINT ["executable", "param1", "param2"]
  • 指定默认运行程序
  • 不会被docker run参数覆盖

11. USER

USER用于指定docker build过程中任何RUN、CMD等指令的用户名或者UID。默认情况下容器的运行用户为root, 格式如下

dockerfile 复制代码
USER <user>[:<group>]
USER <UID>[:<GID>]
  • 指定运行用户

五、核心要点总结

  1. 镜像的来源多种多样,可以通过Docker Hub社区获取,或者通过国内的公有镜像仓库中获取。
  2. Docker镜像是Docker容器运行的基础,没有Docker镜像,就不可能有Docker容器,这也是Docker的设计原则之一。
  3. Docker可以同时支持多种Linux镜像,模拟出多种操作系统环境。容器只能使用Docker host的kernel,并且不能修改
  4. base镜像有两层含义:不依赖其他镜像,从scratch构建;以此为基础镜像,进行扩展。
  5. 构建镜像的方式有三种:使用docker commit构建基于本地模板导入和使用Dockerfile构建构建
  6. docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我们所需要的内容,再 使用docker commit将容器打包为镜像即可。
  7. 用户可以使用docker import命令直接从一个操作系统模板文件导入一个镜像。
  8. 构建镜像的方式有三种:使用docker commit构建基于本地模板导入和使用Dockerfile构建构建。
  9. Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是 描述该层应当如何构建。
  10. 在Dockerfile语法中,RUN、CMD都是运行命令的指令,RUN是在构建镜像时运行的命令,CMD是容器启动时运行的命 令。
相关推荐
南枝异客10 小时前
CentOS 7 网络连接问题
linux·运维·centos
编码追梦人10 小时前
探索 Docker/K8s 部署 MySQL 的创新实践与优化技巧
mysql·docker·kubernetes
fire-flyer11 小时前
docker 跨架构兼容
docker·容器
缘的猿11 小时前
Kubernetes 中 ETCD 数据备份与恢复完整指南
容器·kubernetes·etcd
bestcxx11 小时前
(二十七)、k8s 部署前端项目
前端·容器·kubernetes
白白白白熊爱吃麦当劳12 小时前
k8s知识点总结5
docker·容器·kubernetes
阿方索13 小时前
Linux 正则表达式
linux·运维
K_i13414 小时前
Kubernetes HTTPS迁移:Ingress到GatewayAPI实战
容器·https·kubernetes
利刃大大15 小时前
【高并发服务器:前置知识】一、项目介绍 && 模块划分
运维·服务器·高并发·项目·cpp