Docker(四)---镜像及构建镜像(commit&dockerfile方式)

Docker(四)—镜像及构建镜像(commit&dockerfile方式)

1.镜像的分层结构

在这里插入图片描述

共享宿主机的kernel

base镜像提供的是最小的Linux发行版

同一docker主机支持运行多种Linux发行版采用分层结构的最大好处是:共享资源

可以看到,新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

Docker镜像是由文件系统叠加而成。最底端是一个文件引导系统,即bootfs。Docker用户不会与引导文件系统有直接的交互。
Docker镜像的第二层是root文件系统rootfs,通常是一种或多种操作系统,例如ubuntu等。
在Docker中,文件系统永远都是只读的,在每次修改时,都是进行拷贝叠加从而形成最终的文件系统。Docker称这样的文件为镜像。
一个镜像可以迭代在另一个镜像的顶部。位于下方的镜像称之为父镜像,最底层的镜像称之为基础镜像。
最后,当从一个镜像启动容器时,Docker会在最顶层加载一个读写文件系统作为容器。

2.为什么Docker镜像要采用分层结构?

共享资源

比如:有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份
base 镜像;同时内存中也只需加载一份 base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享,我们将在后面更深入地讨论这个特性。
这时可能就有人会问了:如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc是否也会被修改?答案是不会! 修改会被限制在单个容器内。 这就是我们接下来要说的容器 Copy-on-Write 特性。
新数据会直接存放在最上面的容器层。 修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接 保存在容器层中,镜像层保持不变。
如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件。

base镜像

base 镜像简单来说就是不依赖其他任何镜像,完全从0开始建起。
其他镜像都是建立在他的之上,可以比喻为大楼的地基
base 镜像不依赖其他镜像,从 scratch 构建;其他镜像可以之为基础进行扩展。
所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。

3.镜像的写时复制特性

Copy-on-Write可写容器层

容器层以下所有镜像层都是只读的

docker从上往下依次查找文件

容器层保存镜像变化的部分,并不会对镜像本身进行任何修改

一个镜像最多127

在这里插入图片描述
Docker的这种机制我们称之为写时复制。
镜像是用来创建容器的,是容器的只读模板,默认可以从 docker hub 上下载

4.commit构建镜像

docker commit构建新镜像三部曲:

  • 运行容器

  • 修改容器

  • 将容器保存为新的镜像

缺点:

  • 效率低、可重复性弱、容易出错

  • 使用者无法对镜像进行审计,存在安全隐患

docker ps

将容器保存为新的镜像:

docker commit 3be43c7fa968 busybox:v1

查看指定镜像的创建历史:

docker history busybox:latest 
docker history busybox:v1

发现v1中使用了原来的busybox的两层,并且在上面新建了一层,叠加在上面

docker ps
docker rm -f 3be43c7fa968	#删除正在运行的容器需要-f参数强制删除
docker run -it --rm busybox:v1

可以看到即使删除了busybox容器,但修改的镜像被构建成了一个新的镜像,这种commit的方式将数据提交到了镜像层里,没有把数据落到宿主机的磁盘上
在这里插入图片描述

5.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 server1.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,如果指定了很多,只有最后一个有效。

demo1:COPY拷贝文件

COPY:把文件从build context复制到镜像
支持两种形式:COPY src dest 和 COPY ["src", "dest"]
src必须指定build context中的文件或目录

step1 创建一个Dockerfile:

mkdir docker
cd docker/
vim Dockerfile
FROM busybox	#指定开始的镜像,若镜像不存在,会去拉取该镜像
COPY testfile /	#把当前目录下的testfile文件拷贝到容器的根目录下

注意:
1.dockerfile的目录不要再根目录下,因为默认创建时会把当前目录的所有数据发送给docker引擎,如果在根目录下,就会把根下的所有数据发送给docker进行构建,这显然时不合理的
2.COPY参数要求要拷贝的文件必须在当前目录,不能写绝对路径,只能是相对路径。拷贝的目的地可以是目录或文件

echo redhat > testfile

在这里插入图片描述
step2 删除之前的镜像(没有做过之前的实验可忽略此步骤):

docker images
docker rmi busybox:v1
docker rmi busybox:v2
docker images

在这里插入图片描述
step3 构建镜像:

docker build -t demo:v1 .

可以看到在dockerfile里执行一行命令,提交一次。通过dockerfile可以清晰的看到在每一层里干了什么,可以实现安全审计功能;而commit方式构建镜像时是看不到的

docker history demo:v1 

在这里插入图片描述

demo2:RUN在容器中运行命令

RUN:在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim

step1 修改Dockerfile:

<font color=dahoc>step1 修改Dockerfile:
vim Dockerfile 
FROM busybox
COPY testfile /
RUN echo hello nigar !! > file1

说明:文件第一行内容是把镜像运行成一个容器,后面所有操作都是在容器中完成的

step2 构建镜像:

docker build -t demo:v2 .

v2是在v1的基础上又构建了一层,下面的层没有改变,直接共享了。我们可以看到COPY testfile /的动作没有重复再做,而是Using cache,即使用缓存 :
在这里插入图片描述
step3 测试:

docker run -it demo:v2

查看到file1的内容即为hello nigar !!
在这里插入图片描述

demo3:镜像分层的缓存特性

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
COPY testfile /
RUN echo hello nigar !! > file1
RUN echo hello nigar !! > file2

step2 构建镜像:

docker build -t demo:v3 .

Dockerfile没有变更时会使用本地的cache,无需重新做,可以加速构建过程。这就是镜像分层的缓存特性
在这里插入图片描述

step3 测试:

docker run -it demo:v3

在这里插入图片描述

demo4:ADD自动解压文件

ADD:用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像:
ADD html.tar /var/www
ADD http://ip/html.tar /var/www

step1 放一个nginx的压缩包在/root/docker下
step2 修改Dockerfile:

vim Dockerfile 
FROM busybox
ADD nginx-1.16.1.tar.gz /

step3 构建镜像:

docker build -t demo:v4 .

ADD的用法与COPY类似,但不同的是它可以将文件自动解压到dest

step4 测试:

docker run -it demo:v4

在这里插入图片描述

demo5:ENV定义环境变量

ENV:设置环境变量,变量可以被后续的指令使用:
ENV HOSTNAME sevrer1.example.com

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
ADD nginx-1.16.1.tar.gz /	#定义环境变量hostname为server1

step2 构建镜像:

docker build -t demo:v5 .

step3 测试:

docker run -it demo:v5

env查看到环境变量信息:
在这里插入图片描述

demo6:VOLUME声明数据卷,在封装应用容器时常用

VOLUME:申明数据卷,通常指定的是应用的数据挂载点:
VOLUME ["/var/www/html"]

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
ADD nginx-1.16.1.tar.gz /
VOLUME ["/data"]

step2 构建镜像:

docker build -t demo:v6 .

在这里插入图片描述
step3 查看镜像的创建历史:

docker history demo:v6

在这里插入图片描述
step4 进入容器,创建文件:

docker run -it demo:v6
按ctrl+p+q退出

在这里插入图片描述
step5 查看demov6的挂载信息:

docker ps	#找出对应的容器ID
docker inspect f9770256d7bc

docker引擎在启动熔器时发现定义了卷,会自动生成一个卷。而docker引擎在启动容器时,自动在本地为它创建了这个目录,并且挂载在容器内,可以让容器读取到本地的数据目录
在这里插入图片描述

step6 进入目录,查看到刚刚创建的文件file:

cd /var/lib/docker/volumes/2abf295690bffe9eb6caac67aa365a666d49a8ed1f42c108f7b754ac2af7f260/_data

在这里插入图片描述
step7 释放数据卷:

docker ps
docker rm -f f9	#删除容器(此处使用容器ID的简写,因为只有一个f9开头的ID)
docker volume prune	#释放数据卷
docker volume ls	#查看有哪些数据卷

在这里插入图片描述

demo7:WORKDIR设置镜像中的当前工作目录

WORKDIR:为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建。

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
WORKDIR /nginx
ADD nginx-1.16.1.tar.gz /nginx
VOLUME ["/data"]

step2 构建镜像:

docker build -t demo:v7 .

在这里插入图片描述
step3 测试:

docker run -it --rm demo:v7

我们可以发现,进入容器时就默认在/nginx这个目录下,而将nginx的包解压在了这个目录中。
这个目录之前并不存在,是WORKDIR自动创建的

在这里插入图片描述

demo8:CMD与ENTRYPOINT

这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。
CMD:

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
WORKDIR /nginx
ADD nginx-1.16.1.tar.gz /nginx
VOLUME ["/data"]
CMD echo 'hello docker!!!'

step2 构建镜像:

docker build -t demo:v8 .

在这里插入图片描述

step3 测试:

docker run -it --rm demo:v8
docker run -it --rm demo:v8 sh	#命令被覆盖

在这里插入图片描述

ENTRYPOINT:

step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
WORKDIR /nginx
ADD nginx-1.16.1.tar.gz /nginx
VOLUME ["/data"]
ENTRYPOINT echo 'hello docker!!!'

step2 构建镜像:

docker build -t demo:v9 .

在这里插入图片描述

step3 测试:

docker run -it --rm demo:v9
docker run -it --rm demo:v9 sh	#命令没有被覆盖

在这里插入图片描述

demo9:shell和exec格式的区别:

区别1:

exec格式:
step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV hostname server1
WORKDIR /nginx
ADD nginx-1.16.1.tar.gz /nginx
VOLUME ["/data"]
ENTRYPOINT ["/bin/echo","hello"]
CMD ["docker!!"]

step2 构建镜像:

docker build -t demo:v10 .

在这里插入图片描述

step3 测试:

docker run -it --rm demo:v10
docker run -it --rm demo:v10 linux	#输出的docker变为linux

说明:Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换。在shell格式时,ENTRYPOINT会忽略任何CMD或者docker run提供的参数
在这里插入图片描述

区别2:shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而下面的exec格式不会

shell格式:
step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV name redhat
ENTRYPOINT echo "hello,$name"

step2 构建镜像:

docker build -t demo:v11 .

step3 测试:

docker run -it --rm demo:v11

可以看到变量$name的值被输出了
在这里插入图片描述

exec格式:
step1 修改Dockerfile:

vim Dockerfile 
FROM busybox
ENV name redhat
ENTRYPOINT ["/bin/echo","hello,$name"]

step2 构建镜像:

docker build -t demo:v12 .

step3 测试:

docker run -it --rm demo:v12

可以看到变量$name没有被解析,而是直接输出了$name
在这里插入图片描述
所以exec格式需要改写:

vim Dockerfile 
FROM busybox
ENV name redhat
ENTRYPOINT ["/bin/sh","-c","echo hello,$name"]

再次测试,变量就被解析了
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值