精华内容
下载资源
问答
  • 容器镜像分层
    2022-05-21 15:01:48

    一、场景

    最常见的是容器镜像,将依赖、代码、配置分层后可以利用容器镜像层缓存机制加快构建和下载,这个场景使用分层是最优最简单的。

    k8s 移除 Docker 后,文档中的 Docker 都去掉了…现在也把常说的 Docker 镜像 改成了 容器镜像

    二、分层配置

    如果不需要自定义分层,这一步可以跳过

    在项目根目录中添加 layers.xml 配置文件,文件内容如下:

    <layers xmlns="http://www.springframework.org/schema/boot/layers"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
                              https://www.springframework.org/schema/boot/layers/layers-2.6.xsd">
        <application>
            <into layer="spring-boot-loader">
                <include>org/springframework/boot/loader/**</include>
            </into>
            <into layer="application"/>
        </application>
        <dependencies>
            <into layer="application">
                <includeModuleDependencies/>
            </into>
            <into layer="snapshot-dependencies">
                <include>*:*:*SNAPSHOT</include>
            </into>
            <into layer="sencond-dependencies">
                <include>com.example:*:*</include>
            </into>
            <into layer="dependencies"/>
        </dependencies>
        <layerOrder>
            <layer>dependencies</layer>
            <layer>spring-boot-loader</layer>
            <layer>sencond-dependencies</layer>
            <layer>snapshot-dependencies</layer>
            <layer>application</layer>
        </layerOrder>
    </layers>

    和官方示例相比这里增加了 sencond-dependencies ,算是二方库依赖,如果公司有自己框架,自己平台,根据依赖的稳定性(修改频率)进行更细的分层。

    依赖分层设计,可以参考 企业 Maven 依赖管理层次结构设计 。

    使用 IDEA,并且下载 layers-2.6.xsd 的情况下, <includeModuleDependencies/> 会报红,如下图所示:

    通过查看官方文档和 spring boot 代码,发现文档、代码和 xsd 定义存在不一致的地方,提了 issues#31115 、 pr#31117 、 pr#31126 , 代码已经合并, xsd经过修改,和文档保持一致。

    增加上面的配置后,修改插件使用该配置:

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot.version}</version>
        <executions>
            <execution>
                <id>repackage</id>
                <goals>
                    <goal>repackage</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <layers>
                <enabled>true</enabled>
                <configuration>${project.basedir}/layers.xml</configuration>
            </layers>
        </configuration>
    </plugin>

    三、容器镜像

    构建镜像有多种方式,官方文档介绍了 Dockerfile 和 Buildpacks 两种。

    3.1 Dockerfile

    通过 layertools 可以将 fat jar 按照分层定义中的层进行解压,命令如下:

    Usage:
      java -Djarmode=layertools -jar my-app.jar
    
    Available commands:
      list     List layers from the jar that can be extracted
      extract  Extracts layers from the jar for image creation
      help     Help about any command

    通过 java -Djarmode=layertools -jar my-app.jar extract 即可解压 jar 包到当前目录。为了方便,可以直接通过 Dockerfile 的多阶段构建进行,Dockerfile 如下:

    FROM eclipse-temurin:11-jre as builder
    WORKDIR application
    ARG JAR_FILE=target/*.jar
    COPY ${JAR_FILE} application.jar
    RUN java -Djarmode=layertools -jar application.jar extract
    
    FROM eclipse-temurin:11-jre
    WORKDIR application
    COPY --from=builder application/dependencies/ ./
    COPY --from=builder application/spring-boot-loader/ ./
    COPY --from=builder application/snapshot-dependencies/ ./
    COPY --from=builder application/application/ ./
    ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

    如果有自定义分层,记得按顺序加入到 COPY 部分

    一般情况下 target 下面只有一个 jar 后缀的包,此时可以直接执行下面的 Docker 命令:

    docker build --tag imageName:version .

    如果有多个 jar,需要通过参数指定:

    docker build  --build-arg JAR_FILE=path/to/myapp.jar --tag imageName:version .

    构建后查看镜像信息:

    IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
    e1d22f48893d   11 seconds ago   ENTRYPOINT ["java" "org.springframework.boot…   0B        buildkit.dockerfile.v0
    <missing>      11 seconds ago   COPY application/application/ ./ # buildkit     55.1kB    buildkit.dockerfile.v0
    <missing>      11 seconds ago   COPY application/snapshot-dependencies/ ./ #…   46.1MB    buildkit.dockerfile.v0
    <missing>      12 seconds ago   COPY application/spring-boot-loader/ ./ # bu…   252kB     buildkit.dockerfile.v0
    <missing>      12 seconds ago   COPY application/dependencies/ ./ # buildkit    216MB     buildkit.dockerfile.v0
    <missing>      10 minutes ago   WORKDIR /application                            0B        buildkit.dockerfile.v0
    <missing>      2 months ago     /bin/sh -c set -eux;   arch="$(dpkg --print-…   210MB     
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV JAVA_VERSION=8u322       0B        
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B        
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV PATH=/usr/local/openj…   0B        
    <missing>      2 months ago     /bin/sh -c { echo '#/bin/sh'; echo 'echo "$J…   27B       
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/local/…   0B        
    <missing>      2 months ago     /bin/sh -c set -eux;  apt-get update;  apt-g…   4.88MB    
    <missing>      2 months ago     /bin/sh -c #(nop)  CMD ["bash"]                 0B        
    <missing>      2 months ago     /bin/sh -c #(nop) ADD file:d48a85028743f16ed…   80.4MB

    层信息:

    "RootFS": {
     
        "Type": "layers",
        "Layers": [
            "sha256:1401df2b50d5de5a743b7bac3238ef3b7ce905ae39f54707b0ebb8eda3ab10bc",
            "sha256:43015d7c36457e91ff0994082e7016335d5aa7690e8b5c799d41c2bab47f086c",
            "sha256:f1bceed991c5891bd4bf3ad6e1ade5334e2c40ada40305b59fbf0a275ebbed17",
            "sha256:7a49a2f5a65e2f57886dd32ffe85542283b249ccefd7a1b5379632030912d804",
            "sha256:8791c93670dee0e973efce4424ea9b33caa49e7ef15c8e0bde1912b51c349524",
            "sha256:94c6796cee53f42ae2478affbfc8510c97c76e65015ec46309f83265df078ef8",
            "sha256:033be8a54968fe881ce71510862eacc0c3f3bdb6eb2af1a9130704bbe7442ae8",
            "sha256:ab0700832472168ad4a9060b3fbedf8cc44f22ff1d074aebb67d9ee466796515",
            "sha256:06a62903d01189112c0c8b6b68debaa170228e9d7cf868e1b9959001e877a4c4"
        ]
    }

    对代码进行简单修改后,重新构建镜像,再次查看:

    IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
    cc399ec3ba61   13 seconds ago   ENTRYPOINT ["java" "org.springframework.boot…   0B        buildkit.dockerfile.v0
    <missing>      13 seconds ago   COPY application/application/ ./ # buildkit     52.9kB    buildkit.dockerfile.v0
    <missing>      3 minutes ago    COPY application/snapshot-dependencies/ ./ #…   46.1MB    buildkit.dockerfile.v0
    <missing>      3 minutes ago    COPY application/spring-boot-loader/ ./ # bu…   252kB     buildkit.dockerfile.v0
    <missing>      3 minutes ago    COPY application/dependencies/ ./ # buildkit    216MB     buildkit.dockerfile.v0
    <missing>      13 minutes ago   WORKDIR /application                            0B        buildkit.dockerfile.v0
    <missing>      2 months ago     /bin/sh -c set -eux;   arch="$(dpkg --print-…   210MB     
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV JAVA_VERSION=8u322       0B        
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B        
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV PATH=/usr/local/openj…   0B        
    <missing>      2 months ago     /bin/sh -c { echo '#/bin/sh'; echo 'echo "$J…   27B       
    <missing>      2 months ago     /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/local/…   0B        
    <missing>      2 months ago     /bin/sh -c set -eux;  apt-get update;  apt-g…   4.88MB    
    <missing>      2 months ago     /bin/sh -c #(nop)  CMD ["bash"]                 0B        
    <missing>      2 months ago     /bin/sh -c #(nop) ADD file:d48a85028743f16ed…   80.4MB

    层信息:

    "RootFS": {
     
        "Type": "layers",
        "Layers": [
            "sha256:1401df2b50d5de5a743b7bac3238ef3b7ce905ae39f54707b0ebb8eda3ab10bc",
            "sha256:43015d7c36457e91ff0994082e7016335d5aa7690e8b5c799d41c2bab47f086c",
            "sha256:f1bceed991c5891bd4bf3ad6e1ade5334e2c40ada40305b59fbf0a275ebbed17",
            "sha256:7a49a2f5a65e2f57886dd32ffe85542283b249ccefd7a1b5379632030912d804",
            "sha256:8791c93670dee0e973efce4424ea9b33caa49e7ef15c8e0bde1912b51c349524",
            "sha256:94c6796cee53f42ae2478affbfc8510c97c76e65015ec46309f83265df078ef8",
            "sha256:033be8a54968fe881ce71510862eacc0c3f3bdb6eb2af1a9130704bbe7442ae8",
            "sha256:ab0700832472168ad4a9060b3fbedf8cc44f22ff1d074aebb67d9ee466796515",
            "sha256:4c0f187537195a34793722097d719f0c1247fec1648a6bdcf08f42556348af74"
        ]
    }

    和上面相比只有最上面的一层不同,通过分层尽可能利用Docker层缓存,可以减小镜像的差异,使得镜像更新时,只需要下载有差异的这一小部分。

    构建镜像后,我们通过 grype 检测镜像是否存在安全漏洞:

    $ grype 镜像名:版本
     ✔ Vulnerability DB        [no update available]
     ✔ Loaded image            
     ✔ Parsed image            
     ✔ Cataloged packages      [521 packages]
     ✔ Scanned image           [136 vulnerabilities]
    
    NAME                           INSTALLED           FIXED-IN                 TYPE          VULNERABILITY        SEVERITY   
    apt                            2.2.4                                        deb           CVE-2011-3374        Negligible  
    aviator                        3.3.0                                        java-archive  GHSA-xpv2-8ppj-79hh  Critical    
    bsdutils                       1:2.36.1-8+deb11u1                           deb           CVE-2022-0563        Negligible  
    coreutils                      8.32-4+b1                                    deb           CVE-2017-18018       Negligible  
    coreutils                      8.32-4+b1           (won't fix)              deb           CVE-2016-2781        Low         
    e2fsprogs                      1.46.2-2            (won't fix)              deb           CVE-2022-1304        High        
    gzip                           1.10-4              1.10-4+deb11u1           deb           CVE-2022-1271        Unknown     
    libapt-pkg6.0                  2.2.4                                        deb           CVE-2011-3374        Negligible  
    ...

    还可以对代码进行检查( dir:. 当前目录):

    $ grype dir:.  
     ✔ Vulnerability DB        [no update available]
     ✔ Indexed .               
     ✔ Cataloged packages      [378 packages]
     ✔ Scanned image           [36 vulnerabilities]
    
    NAME                           INSTALLED     FIXED-IN      TYPE          VULNERABILITY        SEVERITY 
    aviator                        3.3.0                       java-archive  GHSA-xpv2-8ppj-79hh  Critical  
    maven-aether-provider          3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-artifact                 3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-common-artifact-filters  3.2.0                       java-archive  CVE-2021-26291       Critical  
    maven-core                     3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-model                    3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-model-builder            3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-repository-metadata      3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-settings                 3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-settings-builder         3.1.1                       java-archive  CVE-2021-26291       Critical  
    maven-shared-utils             3.3.3                       java-archive  CVE-2021-26291       Critical  
    minio                          8.3.8                       java-archive  CVE-2021-21390       Medium    
    minio                          8.3.8                       java-archive  CVE-2020-11012       High      
    minio                          8.3.8                       java-archive  CVE-2021-21287       High

    3.2 Buildpacks

    Spring Boot 插件集成了 Buildpacks 功能,插件配置如下:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-image</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    执行 mvn org.springframework.boot:spring-boot-maven-plugin:build-image 即可构建镜像。

    构建完镜像后,运行时可能会遇到中文乱码,可以通过下面两种方式解决:

    1 运行镜像时,通过环境变量指定编码:

    docker run -e JAVA_OPTS="-Dfile.encoding=UTF-8" <image_name>

    2 配置 spring boot 插件,添加默认的 JVM 配置:

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot.version}</version>
        <executions>
            <execution>
                <goals>
                    <goal>build-image</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <image>
                <env>
                    <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
                    <BPE_APPEND_JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF-8</BPE_APPEND_JAVA_TOOL_OPTIONS>
                </env>
            </image>
        </configuration>
    </plugin>

    环境变量配置规则文档

    GitHub - paketo-buildpacks/environment-variables: A Cloud Native Buildpack that embeds environment variables into an image

    上面两个ENV配置介绍如下:

    1. 追加分隔符使用空格 ,xml配置保留空格( xml:space="preserve" )。
    2. 追加JVM参数

    四、Jar 包运行

    除了使用镜像外,当使用 Jar 包运行时,通过 jar xf xxxx.jar 可以解压 jar 包到当前目录,解压后通过下面命令启动:

    java org.springframework.boot.loader.JarLauncher

    通过这种方式运行时,可以相对方便的修改配置文件,可以替换更新某些 jar 依赖,不用在对整个 fat jar 进行操作。

     

    更多相关内容
  • 【docker系列】镜像分层原理及容器层写时复制

    千次阅读 多人点赞 2022-04-09 07:28:00
    本文核心内容:docker镜像分层容器层介绍、为什么会产生镜像分层?、什么是写时复制?

    一、镜像分层与容器层

    在进行docker pull 下载镜像的时候,通过下面的脚本运行过程可以看到镜像是分层下载并解压的。如nginx:1.20.2的镜像,其镜像是分为6层。下文中以<一串数字>:Pull Complete表示完成一个镜像层的下载和解压,一共是6个分层。

    # docker pull nginx:1.20.2
    1.20.2: Pulling from library/nginx
    c229119241af: Already exists 
    2906ff8f593b: Pull complete 
    605202120923: Pull complete 
    b0013ba53a96: Pull complete 
    f2e7470d98f2: Pull complete 
    8da6a894027c: Pull complete
    

    当我们运行一个新的容器的时候,实际上是在镜像分层的基础上新添加了一层:container layer(容器层)。之后所有容器运行时对文件系统产生的修改实际都只影响这一层。并且针对这一层所作的修改(写操作),在容器重启之后会全部丢失。所以说在使用docker的过程中,在需要修改运行时容器文件数据的时候,尽量去重新构建镜像而不是直接修改容器内文件。如果重构镜像解决不了的问题,使用数据卷。

    构建镜像的方法是通过Dockerfile定义,数据卷的使用详解,专栏后续文章笔者会详细介绍。

    在这里插入图片描述

    注意 :对于运行时的容器而言,镜像层只读的,容器层可读也可写。对于镜像层的只读文件,容器层如果想做修改,实际上是进行了写时复制操作。(下文介绍)。

    二、为什么会产生分层?

    通过上文的介绍,我们已经知道镜像是分层的,那么镜像分层的依据是什么?或者说构建镜像的时候究竟是什么动作产生了分层?我们来看下面的这张图,使用docker history查看镜像的构建历史。

    注意上图中红色边框的部分,我们可以看到:在进行ADD、COPY、执行shell脚本等操作的时候操作步骤对应的SIZE不等于0,正好是6个操作,和我们上文中nginx:1.20.2镜像分层的数量是一样的。所以我们可以做一个大胆的猜想:在镜像构建过程中需要向镜像写入数据的时候会产生分层,一个写操作指令产生一个分层。 大家可以自己去观察更多的镜像去验证这个猜想。笔者要说的是:我读过Dokcer的源码,所以这是一个可以被信任的结论。

    FROM debian:bullseye-slim  #写指令
    
    LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
    ENV NGINX_VERSION   1.20.2
    ENV NJS_VERSION     0.7.0
    ENV PKG_RELEASE     1~bullseye
    
    #写指令
    RUN set -x \    #这里省略若干行shell脚本
    
    COPY docker-entrypoint.sh /   #写指令
    COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d  #写指令
    COPY 20-envsubst-on-templates.sh /docker-entrypoint.d  #写指令
    COPY 30-tune-worker-processes.sh /docker-entrypoint.d  #写指令
    ENTRYPOINT ["/docker-entrypoint.sh"]
    
    EXPOSE 80
    STOPSIGNAL SIGQUIT
    CMD ["nginx", "-g", "daemon off;"]
    

    上面的这段代码是nginx:1.20.2的Dockerfile(镜像构建过程定义文档),也就是构建nginx:1.20.2镜像的构建步骤定义文档(官方)。其中FROM(ADD)指令–添加基础镜像或文件、RUN指令–执行命令行脚本、COPY指令–文件复制,这些都是写操作命令,都会产生新的镜像分层。

    三、什么是写时复制?

    上文中我们提到了一个概念:写时复制。这个概念如果用专业名词的方式说明还是比较难以理解,所以我用白话的方式说明一下。举个例子:

    • 一个授课老师写了一本练习册(原始镜像)。
    • 然后老师留作业了,练习册第12页。全班同学把练习册的第12页全都复印了一份,带回家做作业。

    在这里插入图片描述
    老师的练习册是原始文稿(正本)(原始文稿构建之后就只读不写,镜像层文件也是),同学们的练习册是在需要使用到的时候复印出来的,并在复印本(副本)上完成作业书写,不影响原来老师那本练习册(正本)的内容。这个就是典型的“写时复制”。
    对于容器而言,复制出来的文件在面向容器内的运行时软件时,会覆盖原始镜像文件(对于学生而言也只看自己复制出来那份–不要抬杠:抄作业的除外,不看老师的原始文件)。也就是说发生写时复制之后原始镜像文件被隐藏,容器读写操作都只认复制出来的副本文件。注意:该副本文件存在于容器层,容器重启之后容器层重新建立,上一次容器运行时对于文件的修改全部丢失!

    展开全文
  • Docker 进阶之镜像分层详解

    千次阅读 多人点赞 2022-06-29 10:08:59
    - 我们基于同一个镜像(ubuntu 18.4)启动了两个容器,会占用两倍磁盘空间吗? - 我们在容器内修改或者新建了某个文件,要修改原镜像吗? - 我们基于某镜像(ubuntu 18.04)新建一个镜像(myubuntu),需要将原镜像...

    导读

    可以想象,像 ubuntu等基础镜像,体积必然不小。那么,思考以下几个问题:

    • 我们基于同一个镜像(ubuntu 18.4)启动了两个容器,会占用两倍磁盘空间吗?
    • 我们在容器内修改或者新建了某个文件,要修改原镜像吗?
    • 我们基于某镜像(ubuntu 18.04)新建一个镜像(myubuntu),需要将原镜像文件全部拷贝到新镜像中吗?

    首先,让我们尝试思考下,如果我们去做,该如何高效的解决这些问题?

    • 方案一,…
    • 方案二,…
    • 方案三,…

    入门图解

    创建测试镜像

    我们创建一个最简单的镜像:

    1.构建测试镜像v1.0:docker build -t image_test:1.0 .

    FROM alpine:3.15.0 #除了继承基础镜像,啥也不做
    

    2.构建测试镜像v2.0:docker build -t image_test:2.0 .

    FROM alpine:3.15.0
    RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一个10M的文件file1
    

    3.构建测试镜像v3.0:docker build -t image_test:3.0 .

    FROM alpine:3.15.0
    RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一个10M的文件file1
    RUN dd if=/dev/zero of=file2 bs=10M count=1 #添加一个10M的文件file2
    

    这样本地就构建了3个测试镜像:

    img


    查看镜像

    我们有2种方法查看镜像:

    1. 使用docker inspect:获取镜像的元数据
    2. 使用docker history:查看镜像的构建历史 使用docker inspect

    使用docker inspect

    查看镜像的元数据。
    其中Parent可以看到父镜像, Layers这一项下面可以看到镜像的所有层。

    使用docker history

    使用docker history可以看到镜像的构建历史。
    我们每一行列出了镜像包含的层。

    img

    镜像分层图

    根据上面的docker history命令,我们可以轻松的画出三个镜像的分层图:

    img

    从上面的图可以看到,我们的镜像是分层的,我们的Dockerfile中新增一条指令,就会新增一层!
    如果我们将多个命令合成一个,那么也只会生成一层。修改一下上面的image_test:3.0,把两条RUN合并成一条:

    FROM alpine:3.15.0
    RUN dd if=/dev/zero of=file1 bs=10M count=1 && \
        dd if=/dev/zero of=file2 bs=10M count=1
    

    使用docker history 查看image_test:4.0,可以看到,只有2层了!

    img

    镜像分层的好处

    知道了镜像是分层的,那么我们是不是好奇为啥要这么设计呢?
    试想一下我们如果不分层会有什么问题?

    以拉取镜像为例!
    拉取镜像的镜像很大,比如Redis的镜像有100多M
    第一次我们拉取6.2版本的Redis,下载了完成的100M到本地,下次我要下载6.2.6版本的,是不是又得下载100M。
    尽管可能两个版本之间就改了几行配置文件。

    这样是非常低效的。如果能只下载有差异的部分就好了!

    这个痛点,也就是镜像分层要解决的问题。实际上,Docker也是这么实现的。
    第一次下载redis:6.2时,因为之前没有下载过,所以下载了所有的层,总共113M。网络慢点的话还是需要花一些时间的!

    img

    第二次下载redis:7.0-rc,就变得快了很多!因为前面3层是redis:6.2是一样的,这些层已经下载过了!

    img

    如果版本2是基于版本1的基础上,那么版本2不需要copy一份全量的数据,只需一份和版本1差异化的增量数据即可!

    这样的最终好处是,可以体现在以下方面:

    • 拉取更快:因为分层了,只需拉取本地不存在的层即可!
    • 存储更少:因为共同的层只需存储一份即可!
    • 运行时存储更少:容器运行时可以共享相同的层!

    对于第3点,多个基于相同镜像运行的容器,都可以直接使用相同的镜像层,每个容器只需一个自己的可写层即可:

    img


    Docker镜像加载原理

    下面这张图想必各位是不陌生了,再往下还有一张。那我们要如何在这么多不陌生的人里面脱颖而出呢?就看谁能把这两张图说出花来了哈。
    在这里插入图片描述

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

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

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

    在这里插入图片描述

    Docker镜像层都是只读的,容器层是可写的。 当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。

    那么,先让我们来重新认识一下与 Docker 镜像相关的 4 个概念:rootfs、Union mount、image 以及 layer。


    rootfs

    Rootfs:代表一个 Docker Container 在启动时(而非运行后)其内部进程可见的文件系统视角,或者是 Docker Container 的根目录。当然,该目录下含有 Docker Container 所需要的系统文件、工具、容器文件等。

    传统来说,Linux 操作系统内核启动时,内核首先会挂载一个只读(read-only)的 rootfs,当系统检测其完整性之后,决定是否将其切换为读写(read-write)模式,或者最后在 rootfs 之上另行挂载一种文件系统并忽略 rootfs。Docker 架构下,依然沿用 Linux 中 rootfs 的思想。当 Docker Daemon 为 Docker Container 挂载 rootfs 的时候,与传统 Linux 内核类似,将其设定为只读(read-only)模式。在 rootfs 挂载完毕之后,和 Linux 内核不一样的是,Docker Daemon 没有将 Docker Container 的文件系统设为读写(read-write)模式,而是利用 Union mount 的技术,在这个只读的 rootfs 之上再挂载一个读写(read-write)的文件系统,挂载时该读写(read-write)文件系统内空无一物。

    在这里插入图片描述

    正如 read-only 和 read-write 的含义那样,该容器中的进程对 rootfs 中的内容只拥有读权限,对于 read-write 读写文件系统中的内容既拥有读权限也拥有写权限。容器虽然只有一个文件系统,但该文件系统由“两层”组成,分别为读写文件系统和只读文件系统。


    Union mount

    Union mount:代表一种文件系统挂载的方式,允许同一时刻多种文件系统挂载在一起,并以一种文件系统的形式,呈现多种文件系统内容合并后的目录。

    一般情况下,通过某种文件系统挂载内容至挂载点的话,挂载点目录中原先的内容将会被隐藏。而 Union mount 则不会将挂载点目录中的内容隐藏,反而是将挂载点目录中的内容和被挂载的内容合并,并为合并后的内容提供一个统一独立的文件系统视角。通常来讲,被合并的文件系统中只有一个会以读写(read-write)模式挂载,而其他的文件系统的挂载模式均为只读(read-only)。实现这种 Union mount 技术的文件系统一般被称为 Union Filesystem,较为常见的有 UnionFS、AUFS、OverlayFS 等。

    Docker 实现容器文件系统 Union mount 时,提供多种具体的文件系统解决方案,如 Docker 早版本沿用至今的的 AUFS,还有在 docker 1.4.0 版本中开始支持的 OverlayFS 等。

     AUFS 挂载 Ubuntu 14.04 文件系统示意图
    AUFS 挂载 Ubuntu 文件系统示意图

    使用镜像 ubuntu 创建的容器中,可以暂且将该容器整个 rootfs 当成是一个文件系统。上文也提到,挂载时读写(read-write)文件系统中空无一物。既然如此,从用户视角来看,容器内文件系统和 rootfs 完全一样,用户完全可以按照往常习惯,无差别的使用自身视角下文件系统中的所有内容;然而,从内核的角度来看,两者在有着非常大的区别。追溯区别存在的根本原因,那就不得不提及 AUFS 等文件系统的 COW(copy-on-write)特性。

    COW 文件系统和其他文件系统最大的区别就是:从不覆写已有文件系统中已有的内容。由于通过 COW 文件系统将两个文件系统(rootfs 和 read-write filesystem)合并,最终用户视角为合并后的含有所有内容的文件系统,然而在 Linux 内核逻辑上依然可以区别两者,那就是用户对原先 rootfs 中的内容拥有只读权限,而对 read-write filesystem 中的内容拥有读写权限。

    既然对用户而言,全然不知哪些内容只读,哪些内容可读写,这些信息只有内核在接管,那么假设用户需要更新其视角下的文件 /etc/hosts,而该文件又恰巧是 rootfs 只读文件系统中的内容,内核是否会抛出异常或者驳回用户请求呢?答案是否定的。当此情形发生时,COW 文件系统首先不会覆写 read-only 文件系统中的文件,即不会覆写 rootfs 中 /etc/hosts,其次反而会将该文件拷贝至读写文件系统中,即拷贝至读写文件系统中的 /etc/hosts,最后再对后者进行更新操作。如此一来,纵使 rootfs 与 read-write filesystem 中均由 /etc/hosts,诸如 AUFS 类型的 COW 文件系统也能保证用户视角中只能看到 read-write filesystem 中的 /etc/hosts,即更新后的内容。

    当然,这样的特性同样支持 rootfs 中文件的删除等其他操作。例如:用户通过 apt-get 软件包管理工具安装 Golang,所有与 Golang 相关的内容都会被安装在读写文件系统中,而不会安装在 rootfs。此时用户又希望通过 apt-get 软件包管理工具删除所有关于 MySQL 的内容,恰巧这部分内容又都存在于 rootfs 中时,删除操作执行时同样不会删除 rootfs 实际存在的 MySQL,而是在 read-write filesystem 中删除该部分内容,导致最终 rootfs 中的 MySQL 对容器用户不可见,也不可访。

    掌握 Docker 中 rootfs 以及 Union mount 的概念之后,再来理解 Docker 镜像,就会变得水到渠成。


    image

    Docker 中 rootfs 的概念,起到容器文件系统中基石的作用。对于容器而言,其只读的特性,也是不难理解。神奇的是,实际情况下 Docker 的 rootfs 设计与实现比上文的描述还要精妙不少。

    继续以 ubuntu 为例,虽然通过 AUFS 可以实现 rootfs 与 read-write filesystem 的合并,但是考虑到 rootfs 自身接近 200MB 的磁盘大小,如果以这个 rootfs 的粒度来实现容器的创建与迁移等,是否会稍显笨重,同时也会大大降低镜像的灵活性。而且,若用户希望拥有一个 ubuntu 的 rootfs,那么是否有必要创建一个全新的 rootfs,毕竟 ubuntu 和 ubuntu 的 rootfs 中有很多一致的内容。

    Docker 中 image 的概念,非常巧妙的解决了以上的问题。最为简单的解释 image,就是 Docker 容器中只读文件系统 rootfs 的一部分。换言之,实际上 Docker 容器的 rootfs 可以由多个 image 来构成。多个 image 构成 rootfs 的方式依然沿用 Union mount 技术。
    在这里插入图片描述

    多个 Image 构成 rootfs 的示意图。图中,rootfs 中每一层 image 中的内容划分只为了阐述清楚 rootfs 由多个 image 构成,并不代表实际情况中 rootfs 中的内容划分。

    从上图可以看出,举例的容器 rootfs 包含 4 个 image,其中每个 image 中都有一些用户视角文件系统中的一部分内容。4 个 image 处于层叠的关系,除了最底层的 image,每一层的 image 都叠加在另一个 image 之上。另外,每一个 image 均含有一个 image ID,用以唯一的标记该 image。

    基于以上的概念,Docker Image 中又抽象出两种概念:Parent Image 以及 Base Image。除了容器 rootfs 最底层的 image,其余 image 都依赖于其底下的一个或多个 image,而 Docker 中将下一层的 image 称为上一层 image 的 Parent Image。imageID_0 是 imageID_1 的 Parent Image,imageID_2 是 imageID_3 的 Parent Image,而 imageID_0 没有 Parent Image。对于最下层的 image,即没有 Parent Image 的镜像,在 Docker 中习惯称之为 Base Image。

    通过 image 的形式,原先较为臃肿的 rootfs 被逐渐打散成轻便的多层。Image 除了轻便的特性,同时还有上文提到的只读特性,如此一来,在不同的容器、不同的 rootfs 中 image 完全可以用来复用。

    多 image 组织关系与复用关系如图下图(图中镜像名称的举例只为将 image 之间的关系阐述清楚,并不代表实际情况中相应名称 image 之间的关系):
    在这里插入图片描述

    多 image 组织关系示意图

    图中共罗列了 11 个 image,这 11 个 image 之间的关系呈现一副森林图。森林中含有两棵树,左边树中包含 5 个节点,即含有 5 个 image;右边树中包含 6 个节点,即含有 6 个 image。图中,有些 image 标记了红色字段,意味该 image 代表某一种容器镜像 rootfs 的最上层 image。如图中的 ubuntu:14.04,代表 imageID_3 为该类型容器 rootfs 的最上层,沿着该节点找到树的根节点,可以发现路径上还有 imageID_2,imageID_1 和 imageID_0。特殊的是,imageID_2 作为 imageID_3 的 Parent Image,同时又是容器镜像 ubuntu:12.04 的 rootfs 中的最上层,可见镜像 ubuntu:14.04 只是在镜像 ubuntu:12.04 之上,再另行叠加了一层。因此,在下载镜像 ubuntu:12.04 以及 ubuntu:14.04 时,只会下载一份 imageID_2、imageID_1 和 imageID_0,实现 image 的复用。同时,右边树中 mysql:5.6、mongo:2.2、debian:wheezy 和 debian:jessie 也呈现同样的关系。


    layer

    Docker 术语中,layer 是一个与 image 含义较为相近的词。容器镜像的 rootfs 是容器只读的文件系统,rootfs 又是由多个只读的 image 构成。于是,rootfs 中每个只读的 image 都可以称为一层 layer。

    除了只读的 image 之外,Docker Daemon 在创建容器时会在容器的 rootfs 之上,再 mount 一层 read-write filesystem,而这一层文件系统,也称为容器的一层 layer,常被称为 top layer。

    因此,总结而言,Docker 容器中的每一层只读的 image,以及最上层可读写的文件系统,均被称为 layer。如此一来,layer 的范畴比 image 多了一层,即多包含了最上层的 read-write filesystem。

    有了 layer 的概念,大家可以思考这样一个问题:容器文件系统分为只读的 rootfs,以及可读写的 top layer,那么容器运行时若在 top layer 中写入了内容,那这些内容是否可以持久化,并且也被其它容器复用?

    上文对于 image 的分析中,提到了 image 有复用的特性,既然如此,再提一个更为大胆的假设:容器的 top layer 是否可以转变为 image?

    答案是肯定的。Docker 的设计理念中,top layer 转变为 image 的行为(Docker 中称为 commit 操作),大大释放了容器 rootfs 的灵活性。Docker 的开发者完全可以基于某个镜像创建容器做开发工作,并且无论在开发周期的哪个时间点,都可以对容器进行 commit,将所有 top layer 中的内容打包为一个 image,构成一个新的镜像。Commit 完毕之后,用户完全可以基于新的镜像,进行开发、分发、测试、部署等。不仅 docker commit 的原理如此,基于 Dockerfile 的 docker build,其追核心的思想,也是不断将容器的 top layer 转化为 image。


    爽,酣畅淋漓啊这波读下来!!!


    Docker 镜像下载

    Docker Image 作为 Docker 生态中的精髓,下载过程中需要 Docker 架构中多个组件的协作。Docker 镜像的下载流程如图:
    在这里插入图片描述

    1、docker client发送镜像的tag到registry。
    2、registry根据镜像tag,得到镜像的manifest文件,返回给docker client。
    3、docker client拿到manifest文件后,根据其中的config的digest,也就是image ID,检查下镜像在本地是否存在。
    4、如果镜像不存在,则下载config文件,并根据config文件中的diff_ids得到镜像每一层解压后的digest。
    5、然后根据每层解压后的digest文件,检查本地是否存在,如果不存在,则通过manifest文件中的6、layer的digest下载该层并解压,然后校验解压后digest是否匹配。
    7、下载完所有层后,镜像就下载完毕。


    镜像存储

    Docker Daemon 执行镜像下载任务时,从 Docker Registry 处下载指定镜像之后,仍需要将镜像合理地存储于宿主机的文件系统中。更为具体而言,存储工作分为两个部分:

    (1) 存储镜像内容;

    (2) 在 graph 中注册镜像信息。

    说到镜像内容,需要强调的是,每一层 layer 的 Docker Image 内容都可以认为有两个部分组成:镜像中每一层 layer 中存储的文件系统内容,这部分内容一般可以认为是未来 Docker 容器的静态文件内容;另一部分内容指的是容器的 json 文件,json 文件代表的信息除了容器的基本属性信息之外,还包括未来容器运行时的动态信息,包括 ENV 等信息。

    存储镜像内容,意味着 Docker Daemon 所在宿主机上已经存在镜像的所有内容,除此之外,Docker Daemon 仍需要对所存储的镜像进行统计备案,以便用户在后续的镜像管理与使用过程中,可以有据可循。为此,Docker Daemon 设计了 graph,使用 graph 来接管这部分的工作。graph 负责记录有哪些镜像已经被正确存储,供 Docker Daemon 调用。


    镜像在远端仓库存储

    在本地起一个registry服务,然后推送三个镜像到镜像仓库。可以得到registry中的文件内容如下所示。registry中包含三个镜像: xxx/library/debian:latest,xxx/repo:tag和xxx/busybox:v1

    └── registry
        └── v2
            ├── blobs
            │   └── sha256
            │       ├── 0d
            │       │   └── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878
            │       │       └── data
            │       ├── 34
            │       │   └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
            │       │       └── data
            │       ...
            └── repositories
                ├── busybox
                │   ├── _layers
                │   │   └── sha256
                │   │       ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14
                │   │       │   └── link
                │   │       └── e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016
                │   │           └── link
                │   ├── _manifests
                │   │   ├── revisions
                │   │   │   └── sha256
                │   │   │       └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
                │   │   │           └── link
                │   │   └── tags
                │   │       └── v1
                │   │           ├── current
                │   │           │   └── link
                │   │           └── index
                │   │               └── sha256
                │   │                   └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
                │   │                       └── link
                │   └── _uploads
                ├── library
                │   └── debian
                │       ├── _layers
                │       │   └── sha256
                │       │       ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80
                │       │       │   └── link
                │       │       ├── 67283bbdd4a0dd32f555b4279fd546b3c69251342f0c6715b075cc72049d28a1
                │       │       │   └── link
                │       │       ...
                │       ├── _manifests
                │       │   ├── revisions
                │       │   │   └── sha256
                │       │   │       └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194
                │       │   │           └── link
                │       │   └── tags
                │       │       └── latest
                │       │           ├── current
                │       │           │   └── link
                │       │           └── index
                │       │               └── sha256
                │       │                   └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194
                │       │                       └── link
                │       └── _uploads
                └── repo
                    ├── _layers
                    │   └── sha256
                    │       ├── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878
                    │       │   └── link
                    │       ├── 3790aef225b922bc97aaba099fe762f7b115aec55a0083824b548a6a1e610719
                    │       │   └── link...
                    ├── _manifests
                    │   ├── revisions
                    │   │   └── sha256
                    │   │       └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4
                    │   │           └── link
                    │   └── tags
                    │       └── tag
                    │           ├── current
                    │           │   └── link
                    │           └── index
                    │               └── sha256
                    │                   └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4
                    │                       └── link
                    └── _uploads
    

    对上面的文件结构进行整理,可以得到如下图所示的结构:
    在这里插入图片描述
    registry有两个目录,分别为blobs和repositories,其中blobs保存的是镜像的manifest文件、config文件和layer文件内容,文件名字均为data,每个文件可能是manifest、config、layer中的一种。repositories保存的是镜像的repo、tag、layer摘要等信息。其中的_manifests文件夹下包含着镜像的 tags 和 revisions 信息,每一个镜像的每一个 tag 对应 tag 名相同的目录。每个 tag名目录下面有 current 目录和 index 目录, current 目录下的 link 文件保存了该 tag 目前的 manifest 文件的 sha256 编码,对应在 blobs 中的 sha256 目录下的 data 文件,而 index 目录则列出了该 tag 历史上传的所有版本的 sha256 编码信息。_revisions 目录里存放了该 repository 历史上上传版本的所有 sha256 编码信息。


    本地镜像存储

    镜像在本地存储目录为/var/lib/docker/image/overlay2,查看下面的文件结构:

    tree -L 4 /var/lib/docker/image/overlay2/
    /var/lib/docker/image/overlay2/
    ├── distribution
    │   ├── diffid-by-digest
    │   │   └── sha256
    │   │       ├── 0240c3db9dedbfe40ec02d465375aa5b059bf8ac78dc249d1f1c91b9429fce44
    │   │       ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80
    │   │       ├── 4cdd12619cf5ed0ae43b41cd51f26fbdbd1f5ded860e4188822ec29158218263
    │   │       ├── ...
    │   └── v2metadata-by-diffid
    │       └── sha256
    │           ├── 00188c48b6d80656e2344142a77bccf6927123e7492baf43df68e280b2baf7f2
    │           ├── 04fefa2a1a8fefaafde3b966f11d547e3bbaa2bb36bf90c58e33c1d305052fa9
    │           ├── ...
    ├── imagedb
    │   ├── content
    │   │   └── sha256
    │   │       ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14
    │   │       ├── ...
    │   └── metadata
    │       └── sha256
    │           ├── b8604a3fe8543c9e6afc29550de05b36cd162a97aa9b2833864ea8a5be11f3e2
    │           └── dabbfbe0c57b6e5cd4bc089818d3f664acfad496dc741c9a501e72d15e803b34
    ├── layerdb
    │   ├── mounts
    │   │   ├── 2d534be7517fb3efd9c14248eefdb4781924095fe304f5aa0c848f2e76c6bf08
    │   │   │   ├── init-id
    │   │   │   ├── mount-id
    │   │   │   └── parent
    │   │   ├──...
    │   ├── sha256
    │   │   ├── 0e16a5a61bcb4e6b2bb2d746c2d6789d6c0b66198208b831f74b52198d744189
    │   │   │   ├── cache-id
    │   │   │   ├── diff
    │   │   │   ├── parent
    │   │   │   ├── size
    │   │   │   └── tar-split.json.gz
    │   │   ├── 0ee0aa554b8be64c963aaaf162df152784d868d21a7414146cb819a93e4bdb9e
    │   │   │   ├── cache-id
    │   │   │   ├── diff
    │   │   │   ├── parent
    │   │   │   ├── size
    │   │   │   └── tar-split.json.gz
    │   │   ├── ...
    │   └── tmp
    └── repositories.json
    

    对上面的文件结构进行整理,可以得到如下图所示的结构:
    在这里插入图片描述

    展开全文
  • 镜像分层结构2. 镜像的构建 1. 镜像分层结构 共享宿主机的kernel base镜像提供的是最小的Linux发行版 同一docker主机支持运行多种Linux发行版 采用分层结构的最大好处是:共享资源 2. 镜像的构建 docker commit ...

    1. 镜像的分层结构

    在这里插入图片描述

    • 共享宿主机的kernel
    • base镜像提供的是最小的Linux发行版
    • 同一docker主机支持运行多种Linux发行版
    • 采用分层结构的最大好处是:共享资源

    比如当你已经拉取Nginx 1.17版本的镜像之后,若再次拉取Nginx 1.16 版本时,只需要下载不同的层即可,相同的层可以共享使用,这样就可以节省资源和网络带宽。当你删除其中某个镜像版本时,也只会删除掉不一样的层,因为其中共享的层另一个版本需要使用。

    在这里插入图片描述

    • Copy-on-Write 可写容器层

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

    • docker从上往下依次查找文件

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

    • 一个镜像最多127层

    2. 镜像的构建

    docker commit 构建新镜像三部曲

    • 运行容器
    • 修改容器
    • 将容器保存为新的镜像

    缺点:

    • 效率低、可重复性弱、容易出错
    • 使用者无法对镜像进行审计,存在安全隐患

    2.1 导入本地已经下载好的镜像

    docker load -i rhel7.tar
    

    在这里插入图片描述

    docker history rhel7:latest
    

    在这里插入图片描述

    2.2 运行容器

    docker run -it --rm rhel7 bash		# --rm退出之后就会自动删除
    

    在这里插入图片描述

    docker ps		#显示运行容器
    docker ps -a	#显示所有容器(包括停止的容器)
    
    docker pull busybox   				#拉取一个bysybox镜像
    docker history busybox:latest 		#查看busybox在开启的时候执行什么
    docker run -it busybox				#运行容器
    

    在这里插入图片描述

    2.3 修改容器

    上面在busybox容器中创建了几个文件,已经修改过容器,修改完毕后退出保存即可。

    使用ctrl+d 退出容器,但是这样退出容器就被停止了
    

    在这里插入图片描述

    #可以通过容器ID再次启动
    docker start 4b39bedf3340
    docker ps
    docker attach 4b39bedf3340 		#再次连接
    ctrl+pq							#这样退出表示让容器在后台运行
    

    在这里插入图片描述

    在这里插入图片描述

    2.4 将容器保存为新的镜像

    docker commit 4b39bedf3340 busybox:v1		#保存新的镜像
    docker images								#查看所有镜像
    docker history busybox:latest				#查看原始镜像
    docker history busybox:v1					#查看新的镜像
    

    在这里插入图片描述
    在这里插入图片描述

    可以看出,新保存的V1镜像只是在原始镜像上面又追加了一层,其余镜像层和原始镜像是完全一样的,一层一层往上叠加。

    docker rm -f 4b39bedf3340		#删除容器(正在运行的容器需要使用 -f 强制删除)
    docker run -it --rm busybox:v1
    

    在这里插入图片描述
    可以看到,通过这种打包镜像的方法,可以将我们修改后的数据得以保存,但是这种方法不推荐,因为将数据存到镜像层里面,它的文件系统读取速度效率非常低下,将数据存储到宿主机物理磁盘中更加高效,这个后面会说。

    展开全文
  • Docker 镜像及镜像分层
  • docker 镜像分层

    2022-05-23 19:01:34
    镜像是由多个镜像分层而来,这些层都是只读层; 启动一个容器,docker 会在顶部添加一个读写层,容器内的所有更改,都到了读写层; 镜像和容器最主要的区别:容器加上了顶层的读写层,所有修改都在此层内,不影响...
  • 彻底搞懂Docker镜像分层

    千次阅读 2022-02-20 15:48:08
    本文我们会深入的学习Docker的镜像分层的原理和实现,通过实例和与git的类比,帮助我们加深对镜像的理解!
  • 反思:码头上,每个集装箱,可以把他们理解为单独的一个镜像,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。无论这个镜像有多少层,对内每层的中间镜像都是各自独立的,对外他们又是一个统一...
  • docker镜像分层特点

    2021-08-15 22:24:48
    我们知道,docker镜像都是分层的,从我们pull下来的过程就知道这个现象,现在有这么一个Dockerfile文件 FROM centos:7 RUN yum install net-tools-y RUN yum install httpd -y RUN yum install elink -y CMD ["/...
  • Docker入门:镜像分层概念简述

    千次阅读 2022-03-04 02:10:19
    Docker入门:镜像分层概念简述一、Docker镜像分层机制二、UnionFS(联合文件系统)三、Docker镜像加载原理四、Docker采用镜像分层的好处 镜像的理解: (1)镜像是一种轻量级、可执行的独立软件包,它包含运行某个...
  • 早在集装箱没有出现的时候,...并且到了另外一个码头需要转运的话,有了在集装箱以后,直接把它运送到另一个容器内即可,完全可以保证里面的货物是整体的搬迁,并且不会损坏货物本身。那么docker 镜像在IT行业中也扮演
  • 如何得到镜像∶从远程仓库下载自己制作一个镜像 DockerFileUnionFS ( 联合文件系统 ): Union文件系统( UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时...
  • Downloaded newer image for nginx:1.20.2 docker.io/library/nginx:1.20.2 通过上面的操作,可以看出,镜像之间相同的部分是公用的,增量部分不会重复下载,这就是镜像分层的优势。 DockerHub中大部分镜像都将是...
  • Docker镜像详解(分层理解)

    千次阅读 多人点赞 2020-10-30 16:34:40
    镜像是什么? 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。将所有的应用和环境直接打包为...
  • Docker镜像分层结构

    2021-08-04 09:30:39
    Docker镜像分层结构
  • docker的镜像文件是分层管理的,如下图所示。 上图中: 1.最底层的bootfs为加载层,负责进行内存加载。 2.第二层为rootfs层,为系统环境,不同系统的镜像该层内容不一致。如centos和ubuntu的nginx镜像中,该层就不...
  • Docker镜像是一个只读的Docker容器模板,含有启动Docker容器所需的文件系统结构及其内容,因此是启动一个Docker容器的基础。Docker镜像的文件内容以及一些运行Docker容器的配置文件组成了Docker容器的静态文件系统...
  • 通过以上这种打包镜像的方法,可以将我们在容器镜像内修改的数据得以保存,但是这种方法不推荐,因为将数据存到镜像层里面,它的文件系统读取速度都效率非常低。 所以呢要将数据存储到宿主机的物理磁盘中,这样会...
  • Docker镜像分层提取文件
  • Docker镜像为什么分层?

    2021-09-15 13:55:34
    镜像分层:是为了共享资源 比如:有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像(因为镜像的ID唯一);同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。...
  • 容器 镜像

    2021-10-06 09:53:43
    容器镜像为了实现这种增量保存的机制,设计出了一个“分层”概念。也就是将镜像分为多个层(layer)进行组织,每当发生变更时,就创建一个新层来保存这些变更的部分。多个层叠加在一起就是一个完整的镜像,而这种层...
  • Docker入门-常用命令、镜像分层容器卷一、Docker常用命令帮助启动类命令镜像命令容器命令二、Docker镜像的分层分层的镜像UnionFS(联合文件系统)Docker镜像加载原理重点理解commit命令总结三、本地镜像发布到阿里云...
  • docker镜像分层

    2022-01-18 20:45:18
    4.dockerfile镜像分层 5.dockerfile分层原理 1.docker镜像分层(基于AUFS构建) 2.涉及技术 1.dockerfile 1.创建容器的方式 基于已有的模板文件进行创建 容器导出,导入 基于已有的镜像创建 ...
  • 之前一直使用Dockerfile的方式将Spring Boot应用构建成Docker Image, 发现jar包只有十几兆,但是构建出现的镜像有几百兆,传输的时候非常慢。后来发现spring-boot-maven-plugin插件可以分层构建Docker Image,尝试了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,164
精华内容 6,065
关键字:

容器镜像分层

友情链接: 5502_CODEC.zip