Docker镜像原理及制作

1 Docker镜像介绍

1.1 镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件

1.2 Docker镜像加载原理

1.2.1 UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
Union(联合)文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

Docker 目前支持的联合文件系统包括 OverlayFS , AUFS , Btrfs , VFS ,ZFS 和 Device Mapper。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件体统包含所有底层的文件和目录

1.2.2 Docker镜像加载原理

docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

bootfs(boot file system) 主要包含bootloader和kernel,bootloader 主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就存在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

roorfs (root file system),在bootfs之上。包含的就是典型Linux系统中的 /dev ,/proc,/bin ,/etx 等标准的目录和文件。rootfs就是各种不同的操作系统发行版。比如Ubuntu,Centos等等。

对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host(宿主机)的kernel,自己只需要提供rootfs就行了,由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。

1.3 镜像的分层结构

我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载

# 下载镜像 docker pull 镜像名[:tag]
[@bjyf_50_20 ~]# docker pull mysql
Using default tag: latest      #如果不写 tag,默认就是 latest
latest: Pulling from library/mysql
6ec7b7d162b2: Pull complete    # 分层下载,docker image的核心 联合文件系统
fedd960d3481: Pull complete 
7ab947313861: Pull complete 
64f92f19e638: Pull complete 
3e80b17bff96: Pull complete 
014e976799f9: Pull complete 
59ae84fee1b3: Pull complete 
ffe10de703ea: Pull complete 
657af6d90c83: Pull complete 
98bfb480322c: Pull complete 
9f2c4202ac29: Pull complete 
a369b92bfc99: Pull complete 
Digest: sha256:365e891b22abd3336d65baefc475b4a9a1e29a01a7b6b5be04367fcc9f373bb7   #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest  #真实地址

# 等价于
docker pull mysql
docker pull docker.io/library/mysql:latest

# 指定版本下载
docker pull mysql:5.7
  • 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境的软件,包含了运行某个软件所需要的所有内容

  • 基于UnionFS联合文件系统,采用分层结构,一层一层的堆叠起来,像一个同心圆,但,从外面来说,只能看到最外层的文件系统

  • 分层结构好处:共享资源,便于复用(许多镜像都是从相同的Base基础镜像构建而来的,基础镜像只需要保存一份)

  • 镜像都是只读的,而由镜像生成的容器是可以修改的

  • 容器=镜像+读写层

Docker镜像原理及制作_第1张图片

为什么Docker镜像要采用这种分层的结构呢?

  • 采用这种分层结构最大的一个好处就是共享资源,比如有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一份base镜像,

  • 同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作 “容器层” ,“容器层” 之下的都叫镜像层。

查看镜像分层的方式可以通过 docker image inspect 命令

docker image inspect redis:latest

1.3.1 About Docker Image

Docker镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动docker容器

采用分层构建机制,最底层为bootfs,其上为rootfs
bootfs:用于系统引导的文件系统,包括bootloader和kernel,容器启动完成后会被卸载以节约内存资源

rootfs:位于bootfs之上,表现为docker容器的根文件系统
传统模式中,系统启动之时,内核挂载rootfs时会首先将其挂载为“只读”模式,完整性自检完成后将其重新挂载为读写模式

docker中,rootfs由内核挂载为“只读”模式,而后通过“联合挂载”技术额外挂载一个“可写”层

1.3.2 Docker Image Layer

位于下层的镜像称为父镜像(parent image),最底层的称为基础镜像(base image)
最上层为“可读写”层,其下的均为“只读”层

1.3.3 Docker Registry

启动容器时,docker daemon会试图从本地获取相关的镜像,本地镜像不存在时,其将从Registry中下载该镜像并保存到本地

1.4 Registry(repository and index)

1.4.1 Repository

由特定的docker镜像的所有迭代版本组成的镜像仓库

一个Registry中可以存在多个Repository

  • repository可以分为“顶层仓库”和“用户仓库”
  • 用户仓库名称格式为“用户名/仓库名”

每个仓库可以包含多个Tag(标签),每个标签对应一个镜像

1.4.2 Index

维护用户账户、镜像的校验以及公共命名空间的信息

相当于为Registry提供了一个完成用户认证等功能的检索接口

2. 创建镜像的方法

基于已有镜像的更新、基于本地模板导入、基于Dockerfile构建

2.1 基于已有镜像的更新

该方法是使用 docker commit 命令,其命令格式为:

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

主要参数选项包括:

  • -a -author=”” 作者信息

  • -m -message=”” 提交信息

  • -p -pause=true 提交时暂停容器运行

示例

# 先使用基础镜像创建一个容器,然后对容器进行修改,最后使用commit命令提交为一个新镜像
# 步骤
# 1.根据基础镜像,创建容器
  docker run --name mytomcat -p 8080:8080 -d tomcat
# 2.修改容器
  docker exec -it mytomcat /bin/bash
  /# cd /usr/local/tomcat/webapps/ROOT
  /# rm -f index.jsp
  /# echo welcome to tomcat > index.html
  /# exit
# 3.提交为新的镜像,语法:docker commit -m="描述信息" -a="作者" 容器ID或容器名 镜像名:tag
  docker ps -a
  docker commit -m="修改默认索引页" -a="admin" 镜像ID harbor.adtech.com/os/tomcat:v1
  docker inspect 镜像ID
# 4.使用新创建的harbor.adtech.com/os/tomat:v1镜像,生成一台容器实例:
  docker run --name tomcat_v1 -p 8080:8080 -d harbor.adtech.com/os/tomcat:v1
  docker exec -it tomcat_v1 /bin/bash

示例

docker run --name b1 -it busybox
mkdir -p /data/html
vi /data/html/index.html

Busybox welcome.

# 提交镜像,修改默认运行的命令(上面的容器不能关闭,需另开一个窗口制作镜像,新镜像默认打开html页面,而不是/bin/sh) docker commit -a "Song " -c 'CMD ["/bin/httpd","-f","-h","/data/html"]' -p b1 Song/httpd:v0.1 docker image ls # 可以打多个标签 docker tag Song/httpd:v0.1 Song/httpd:latest # 打开自定义的容器 docker run --name t1 Song/httpd:v0.1 # 删除标签 docker image rm song/httpd:latest ls /data/html cat /data/html/index.html

2.2 基于本地模板导入

wget http://download.openvz.org/template/precreated/ubuntu-18.04-x86_64-minimal.tar.gz
sudo cat ubuntu–18.04–x86_64–minimal.tar.gz | docker import -ubuntu:18.04
docker images

2.3 基于 Dockerfile 构建

Dockerfile是一个包含创建镜像所有命令的文本文件,使用docker build命令可以根据Dockerfile的内容创建镜像

2.3.1 Dockerfile简介:

Dockerfile 是用来构建 Docker 镜像的文件,是由一系列命令和参数构成的脚本

DockerfileFROM 命令开始,紧接着各种命令、参数等,最终会生成一个新的镜像

使用 docker build 创建镜像时,需要使用 Dockerfile 文件自动化制作 image 镜像

Dockerfile 有点像源码编译时 ./configure 后产生的 Makefile

2.3.2 使用Dockerfile构建镜像的步骤:

  1. 编写一个 dockerfile 文件
  2. 使用 docker build 构建镜像
  3. 使用 docker run 运行镜像
  4. docker push 发布镜像(DockerHub、阿里云镜像仓库)

2.3.3 语法规则

  1. 指令必须要大写,且后面必须跟参数
  2. 第一条指令必须是 FROM ,指定 Base image 基础镜像
  3. 指令按从上往下的顺序,依次执行
  4. 每条指令都会都会创建一个新的镜像层并提交
  5. # 表示注释

2.3.4 Dockerfile常用指令

2.3.4.1 FROM

指定基础镜像,即构建新镜像是基于哪个镜像

FROM harbor.adtech.com/os/centos:7.4
2.3.4.2 MAINTAINER

指定作者姓名或邮箱地址(已弃用)

MAINTAINER [email protected]
2.3.4.3 LABEL

设置镜像的标签

2.3.4.4 RUN

指定构建过程中要运行的 shell 命令 RUN RUN ["executable","param1","param2"]

RUN rm -rf /etc/yum.repos.d/*

RUN ["/bin/bash","-c","echo hello"]
2.3.4.5 ENV

设置环境变量

ENV PATH $PATH:/usr/local/nginx/sbin
2.3.4.6 WORKDIR

指定默认的工作目录,即进入容器后默认进入的目录,对于RUN,CMD,ENTRYPOINT、COPY、ADD生效

WORKDIR /usr/local/nginx
2.3.4.7 VOLUME

创建挂载点,也称容器数据卷,用于数据共享和持久化

创建挂载点,无法指定宿主机上对应的目录,宿主机上的目录是自动生成的, /data1/data2 表示容器中的两个目录VOLUME ["/data1","/data2"]

2.3.4.8 CMD

指定容器启动时要运行的命令,与RUN不同的是,这些命令不是在镜像构建过程中执行的支持三种格式

# 使用exec执行,推荐方式;
CMD ["executable","param1","param2"]
# 提供给ENTRYPOINT的默认参数
CMD ["param1","param2"]
# 在 /bin/sh 中执行,提供给需要交互的应用
CMD command param1 param2

注:指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条命令,只有最后一条会被执行

2.3.4.9 ENTRYPOINT

指定容器启动时要运行的命令,与 CMD 有区别

container 启动时执行的命令,但是一个 Dockerfile 只能有一条 ENTRYPOINT 命令,如果多条,则只执行最后一条,ENTRYPOINT 没有 CMD 的可替换特性

2.3.4.10 COPY

拷贝文件/目录到镜像中

格式为 COPY 。复制本地主机的 (为Dockerfile所在的目录的相对路径)到容器中的。当使用本地目录为源目录时,推荐使用 COPY

2.3.4.11 ADD

拷贝文件到镜像中,且会自动解压缩

将文件 拷贝到 container 的文件系统对应的路径 。所有拷贝到 container 中的文件和文件夹权限为 0755, uid 和 gid 为 0

如果要 ADD 本地文件,则本地文件必须在 docker build ,指定的 目录下

如果要 ADD 远程文件,则远程文件必须在 docker build ,指定的 目录下 docker build github.com/creack/docker-firefox

ADD 只有在 build 镜像的时候运行一次,后面的 container 的时候不会在重新加载

2.3.4.12 EXPOSE

声明容器运行的服务端口,即,指定对外暴漏的端口

EXPOSE 8022

Container 内部服务开启的端口,主机上要用还需在启动 container 时,做 host-container 的端口映射

docker run -d -p 127.0.0.1:8022:22 centos7-ssh
2.3.4.13 USER

为 RUN、CMD、ENTRYPOINT 执行命令指定运行用户

2.3.4.14 HEALTHCHECK

容器中服务健康检查

HEALTHCHECK CMD /healthcheck.sh
2.3.4.15 ARG

设置编译镜像时加入的参数

2.3.4.16 ONBUILD

当构建一个被继承的 Dockerfile 的时候就会运行 ONBUILD 指令,ONBUILD 是一个触发指令

2.3.4.17 STOPSIGNAL

设置容器的退出信号量

2.3.5 Dockerfile 制作

示例:自定义centos镜像

# 1.编写Dockerfile文件
vim Dockerfile2
FROM centos

ENV MYPATH /usr/local/centos
RUN mkdir -p $MYAPTH
WORKDIR $MYPATH

RUN yum -y install vim
RUN yum -y install wget
RUN yum -y install net-tools

EXPOSE 80

#创建挂载点,无法指定宿主机上对应的目录,宿主机上的目录是自动生成的,/data1 和 /data2表示容器中的两个目录
VOLUME ["/data1","/data2"]

CMD echo $MYPATH
CMD echo "---end---"
CMD ["/bin/bash"]

# 2.使用 docker build 构建镜像(注意后面的点号)
docker build -f Dockerfile2 -t harbor.adtech.com/os/centos:v1 .
    
# 3.使用 docker run 运行容器
docker run -it harbor.adtech.com/os/centos:v1 
    
# 4.查看镜像的变更历史和构建过程
docker history 镜像ID或镜像名    
   
# 5.验证挂载点(mounts)
docker inspect 镜像ID

示例:自定义 httpd 镜像

Dockerfile是一个包含创建镜像所有命令的文本文件,使用docker build命令可以根据DockerFile的内容创建镜像

# 1.创建Dockerfile文件

vim  Dockerfile   
# FROM:基于哪个(基础)镜像
FROM  centos

# RUN:运行命令时使用,RUN  或 RUN ["executable","param1","param2"],
# 例如:RUN ["/bin/bash","-c","echo hello"]
RUN yum -y install httpd   
#ADD 将文件拷贝到新产生的镜像的文件系统对应的路径。所有拷贝到新镜像中的文件和文件夹权限为0755,uid和gid为0
ADD start.sh /usr/local/bin/start.sh  
ADD index.html /var/www/html/index.html
# container启动时执行的命令或启动服务,但是一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD.
CMD /usr/local/bin/start.sh
 
# 2.创建start.sh脚本启动httpd服务和apache默认首页index.html文件

echo "/usr/sbin/httpd -DFOREGROUND" > /usr/local/bin/start.sh
chmod a+x /usr/local/bin/start.sh
echo "docker image build test" > index.html
 
# 3.构建新的镜像,语法:docker built -f Dockerfile文件的路径 -t 镜像名:tag 命令执行的上下文

docker build -f Dockerfile -t harbor.adtech.com/os/tomcat:v2 .
# 注:Dockerfile构建镜像,在指定新镜像的名称和tag后面的点并不是Dockerfile的路径,这是指定镜像的上下文空间,
# 也就是当COPY和ADD路径使用时候的相对路径,Dockerfile默认为当前路径,可以使用-f参数另外指定。
  
# 4.使用新镜像运行容器

docker run -p 8099:8080 -d harbor.adtech.com/os/tomcat:v2

示例:操作系统 CentOS7 基础镜像制作

cat Dockerfile
  FROM harbor.test.com/os/centos:7.4
  MAINTAINER [email protected]

  RUN rm -rf /etc/yum.repos.d/*
  
  COPY yum.repos.d /etc/yum.repos.d/
  
  RUN yum makecache \
   yum -y install vim rsync cronie which sudo krb5-workstation \
   yum -y install wget bzip2 openssh-server systemd net-tools iproute openssl098e \
  
   cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENTRYPOINT ["/usr/lib/systemd/systemd"]

示例:操作系统 CentOS6 基础镜像制作

# 编写Dockerfile
cat Dockerfile
FROM centos:6.9
MAINTAINER [email protected]

RUN rm -rf /etc/yum.repos.d/*

COPY yum.repos.d /etc/yum.repos.d/

RUN yum makecache \
yum install -y vim rsync cronie which sudo krb5-workstation wget bzip2 openssh-server upstart  openssl098e \

cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENTRYPOINT ["/sbin/init"]

#构建镜像
docker build -f Dockerfile -t harbor.test.com/os/centos:6.9 .
#上传镜像到私有仓库
docker push harbor.test.com/os/centos:6.9
#从私有仓库下载镜像
docker pull harbor.test.com/os/centos:6.9
#运行镜像
docker run --name lbs -d harbor.test.com/os/centos:6.9
#向容器中拷贝文件
docker cp /var/spool/cron/  lbs:/var/spool/cron/
#进入容器
docker exec -it lbs /bin/bash

2.3.6 ENTRYPOINT 与 CMD 之间的共同和异同:

共同点

  • 都可以指定 shellexec 函数调用的方式执行命令

  • 当在 ``dockerfile 中存在多个 CMDENTRYPOINT 指令时,只有最后一条指令生效,所以一般只有一条 CMDENTRYPOINT 指令

不同点

  • CMD 指令可以被 docker run 之后的参数覆盖。而 ENTRYPOINT 指令指定的命令不会被覆盖,而是将 docker run 指定的参数当作 ENTRYPOINT 指定命令的参数

  • CMD 指令可以为 ENTRYPOINT 指令设置默认参数,而且可以被 docker run 指定的参数覆盖

验证1

当存在多个CMD或ENTRYPOINT指令时,只有最后一条生效

# 编写 dockerfile 文件
vim cmdfile
FROM centos
CMD ["/bin/ls","-a"]
CMD ["/bin/bash"]

# 构建镜像
docker build -f cmdfile -t harbor.adtech.com/test/cmdfile:v1 .

# 运行容器查看只执行了最后一条CMD,其他的CMD并未执行
docker run -it harbor.adtech.com/test/cmdfile:v1

验证2

CMD 指令可以被 docker run 之后的参数(/bin/pwd)覆盖,不再运行 /bin/bash

docker run -it harbor.adtech.com/test/cmdfile:v1 /bin/pwd

验证3

docker run 之后的参数(-l)会被作为 ENTRYPOINT 指令的参数,组合成新的命令(ls /usr/local -l)

# 编写 dockerfile 文件
vim entrypointfile
FROM centos
ENTRYPOINT ["/bin/ls","/usr/local"]

# 构建镜像
docker build -f entrypointfile -t harbor.adtech.com/test/entrypointfile:v1 .

# 运行容器
docker run -it harbor.adtech.com/test/entrypointfile:v1 -l

2.3.7 将制作的镜像上传到私有Hub

docker login -u Song
docker push Song/httpd

2.3.8 镜像导入和导出

docker save -o myimages.gz Song/httpd:latest Song/httpd:v0.1
rsync -aP myimages.gz 10.134.40.86::root/tmp
docker load -i  myimages.gz

Docker导入镜像

cat centos.tar | docker import  -  centos6  

Docker导出镜像

docker  export  容器id号  > cenos6.tar  

在docker容器中运行hello world!

docker run centos:latest echo "hello world"
docker run 镜像名 echo "hello word"  

在容器中安装ntpdate的程序

docker  run  centos yum install ntpdate

3. 总结

Docker镜像原理及制作_第2张图片

你可能感兴趣的:(docker)