zoukankan      html  css  js  c++  java
  • 5.docker容器数据卷

    docker基础

    1. docker前言知识(重要: 知道为什么学, 比学什么更重要): https://www.cnblogs.com/ITPower/p/12945685.html

    2. docker架构,原理,安装及简单应用: https://www.cnblogs.com/ITPower/p/12945711.html

    3. docker容器的命令: https://www.cnblogs.com/ITPower/p/12664292.html

    4. 镜像的原理: https://www.cnblogs.com/ITPower/p/12975385.html


    目录:

    1. docker数据卷的添加方式, 有两种: 命令添加, dockerfile添加

    2. 数据加载卷共享: --volumes-from


    一. 数据卷的添加方式

    有两种:

    1.1 . 直接命令添加

    1. 命令

    docker run -it -v /宿主机绝对路径目录:/容器绝对路径目录 镜像名

     -v命令除了可以挂在目录, 还有mkdir的作用. 也就是说, 如果挂在的目录不存在, 那么他会自动创建目录

    2. 查看数据卷是否挂在成功

    docker inspect 容器id

    docker inspect d804cc6b6e31, 看到挂在信息Mounts. 我们看到RW, 表示可以读写

    3. 容器和宿主机之间共享数据

    在容器中创建数据, 宿主机可以共享. 在宿主机创建数据, 容器可以共享到.

    4. 容器停止退出后, 主机修改的数据依然共享

    5. 设置带有权限的容器

    有时,我们只允许容器读数据, 不允许容器写数据. 这个怎么操作呢?

    docker run -it -v /宿主机绝对路径目录:/容器绝对路径目录:ro 镜像id

    这里的ro表示的是read only, 只读

    这是我们使用docker inspect 容器id查看挂在详情

    1.2  Dockerfile添加

    简单了解一下,dockerfile是什么?

    我们用类比的思想. 
    java 中 hello.java, ----> hello.class 源码文件
    docker中 image----->dockerfile  
    
    也就是说, hello.class是hello.java的源码文件. 而dockerfile就是image的源码文件

    下面我们来看看如何用dockerfile写容器目录的挂载

    1. 学习别人是怎么写dockerfile的

    我们可以看看别人的dockerfile是怎么写的

    在hub.docker.com中搜索tomcat. 我们来看看tomcat的dockerfile

    FROM openjdk:11-jdk
    
    ENV CATALINA_HOME /usr/local/tomcat
    ENV PATH $CATALINA_HOME/bin:$PATH
    RUN mkdir -p "$CATALINA_HOME"
    WORKDIR $CATALINA_HOME
    
    # let "Tomcat Native" live somewhere isolated
    ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib
    ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR
    
    # see https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/KEYS
    # see also "update.sh" (https://github.com/docker-library/tomcat/blob/master/update.sh)
    ENV GPG_KEYS 05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23
    
    ENV TOMCAT_MAJOR 8
    ENV TOMCAT_VERSION 8.5.55
    ENV TOMCAT_SHA512 996b653b4f81b40ae3620d6424593d23687e5ceb5cbd7357fc8d0e4b92f76903fe7fb20bf7316505e4e86269153fbfe62394bf45e59b4fb1cbc1bc95fad9eb7a
    
    RUN set -eux; 
        
        savedAptMark="$(apt-mark showmanual)"; 
        apt-get update; 
        apt-get install -y --no-install-recommends 
            gnupg dirmngr 
            wget ca-certificates 
        ; 
        
        ddist() { 
            local f="$1"; shift; 
            local distFile="$1"; shift; 
            local mvnFile="${1:-}"; 
            local success=; 
            local distUrl=; 
            for distUrl in 
    # https://issues.apache.org/jira/browse/INFRA-8753?focusedCommentId=14735394#comment-14735394
                "https://www.apache.org/dyn/closer.cgi?action=download&filename=$distFile" 
    # if the version is outdated (or we're grabbing the .asc file), we might have to pull from the dist/archive :/
                "https://www-us.apache.org/dist/$distFile" 
                "https://www.apache.org/dist/$distFile" 
                "https://archive.apache.org/dist/$distFile" 
    # if all else fails, let's try Maven (https://www.mail-archive.com/users@tomcat.apache.org/msg134940.html; https://mvnrepository.com/artifact/org.apache.tomcat/tomcat; https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/)
                ${mvnFile:+"https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/$mvnFile"} 
            ; do 
                if wget -O "$f" "$distUrl" && [ -s "$f" ]; then 
                    success=1; 
                    break; 
                fi; 
            done; 
            [ -n "$success" ]; 
        }; 
        
        ddist 'tomcat.tar.gz' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz"; 
        echo "$TOMCAT_SHA512 *tomcat.tar.gz" | sha512sum --strict --check -; 
        ddist 'tomcat.tar.gz.asc' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz.asc"; 
        export GNUPGHOME="$(mktemp -d)"; 
        for key in $GPG_KEYS; do 
            gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; 
        done; 
        gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; 
        tar -xf tomcat.tar.gz --strip-components=1; 
        rm bin/*.bat; 
        rm tomcat.tar.gz*; 
        command -v gpgconf && gpgconf --kill all || :; 
        rm -rf "$GNUPGHOME"; 
        
    # https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html#Default_web_applications
        mv webapps webapps.dist; 
        mkdir webapps; 
    # we don't delete them completely because they're frankly a pain to get back for users who do want them, and they're generally tiny (~7MB)
        
        nativeBuildDir="$(mktemp -d)"; 
        tar -xf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1; 
        apt-get install -y --no-install-recommends 
            dpkg-dev 
            gcc 
            libapr1-dev 
            libssl-dev 
            make 
        ; 
        ( 
            export CATALINA_HOME="$PWD"; 
            cd "$nativeBuildDir/native"; 
            gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; 
            aprConfig="$(command -v apr-1-config)"; 
            ./configure 
                --build="$gnuArch" 
                --libdir="$TOMCAT_NATIVE_LIBDIR" 
                --prefix="$CATALINA_HOME" 
                --with-apr="$aprConfig" 
                --with-java-home="$JAVA_HOME" 
                --with-ssl=yes; 
            make -j "$(nproc)"; 
            make install; 
        ); 
        rm -rf "$nativeBuildDir"; 
        rm bin/tomcat-native.tar.gz; 
        
    # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
        apt-mark auto '.*' > /dev/null; 
        [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; 
        find "$TOMCAT_NATIVE_LIBDIR" -type f -executable -exec ldd '{}' ';' 
            | awk '/=>/ { print $(NF-1) }' 
            | sort -u 
            | xargs -r dpkg-query --search 
            | cut -d: -f1 
            | sort -u 
            | xargs -r apt-mark manual 
        ; 
        apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; 
        rm -rf /var/lib/apt/lists/*; 
        
    # sh removes env vars it doesn't support (ones with periods)
    # https://github.com/docker-library/tomcat/issues/77
        find ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +; 
        
    # fix permissions (especially for running as non-root)
    # https://github.com/docker-library/tomcat/issues/35
        chmod -R +rX .; 
        chmod 777 logs temp work
    
    # verify Tomcat Native is working properly
    RUN set -e 
        && nativeLines="$(catalina.sh configtest 2>&1)" 
        && nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')" 
        && nativeLines="$(echo "$nativeLines" | sort -u)" 
        && if ! echo "$nativeLines" | grep -E 'INFO: Loaded( APR based)? Apache Tomcat Native library' >&2; then 
            echo >&2 "$nativeLines"; 
            exit 1; 
        fi
    
    EXPOSE 8080
    CMD ["catalina.sh", "run"]

    这是tomcat的dockerfile, 我们分解来看看

    FROM openjdk:11-jdk
    FROM相当于java中的extends. 继承自父类. 也就是说, tomcat运行, 依赖于openjdk.
    这就是为什么tomcat很大的原因, 因为他依赖其他镜像而存在.
    ENV CATALINA_HOME /usr/local/tomcat
    ENV PATH $CATALINA_HOME/bin:$PATH
    
    这里是设置环境变量
    EXPOSE 8080
    
    
    设置tomcat对外暴露的端口号是8080
    CMD ["catalina.sh", "run"]
    
    执行命令, 启动并运行tomcat

    2. 编写带有挂载目录的dockerfile

    那么下面我们来写一个挂载目录的dockerfile

    可在dockerfile中使用VOLUME命令来给镜像添加一个或多个数据卷

    VLOUME["容器目录1", "容器目录2", "容器目录3"]

    备注: 

    处于可移植和分享的考虑, 用-v /宿主机目录:/容器目录 这种方法不能够直接在Dockerfile中使用

    由于宿主机目录是依赖于特定主机的, 并不能够保证在所有的宿主机上都存在这样的目录. 

    所以, 这里只设置容器挂载目录, 与宿主机的对应目录, 自动生成, 可以试用docker inspect查看详情

    我们可以参考着上面的tomcat来写

    FROM centos
    VOLUME ["/dataVolumeContainer1", "/dataVolumeContainer2", "/dataVolumeContainer3"]
    CMD echo "finish.......success!!"
    CMD /bin/bash

    这是一个很简单的dockerfile, 依赖的父类镜像是centos, 启动的容器, 只做了一个目录挂载. 

    这个dockerfile写好了以后, 是什么意思呢? 大致可以理解为一下的含义

    docker run -it -v /HOST:/dataVolumeContainer1 -v /HOST:/dataVolumeContainer2 -v /HOST:/dataVolumeContainer3 镜像ID /bin/bash

    只是, 上面没有定义宿主机挂载的目录. 

    3. build生成镜像

    docker build -f dockerfile目录  -t 新的镜像名字 .
    docker build -f /docker/dockerfile/dockerfile -t lxl/centos .

     就像洋葱一样,一层套一层.

     4. 运行容器

    查看镜像

    docker images

     我们看到了刚刚生成的镜像. 这个镜像就是带有挂载目录的镜像. 我们可以通过运行容器查看挂载目录

    docker run -it 5a8ecf996d8d

     进来直接就可以看到挂载目录. 那么在容器和宿主机创建的目录, 都可以被共享

    比如: 我们在/dataVolumeContainer1中创建一个文件

    5. 查看目录挂载

     使用docker inspect查看宿主机中挂载目录

    docker inspect 0dcff7fc2508

     我们可以看到, 在宿主机上生成了一个默认的挂载目录. 

    进入第一个目录, 看看能否看到刚刚创建的container.txt文件

     确实是可以共享

    备注: Docker挂载主机目录Docker访问出现cannot open directory .: Permission denied

    解决办法: 在挂载目录后多加一个 --privileged=true 参数即可

     二. 容器数据卷

    2.1. 什么是容器数据卷?

    命名的容器挂载数据卷,其他的容器通过挂载这个父容器实现数据共享, 挂载数据卷的容器, 称之为数据卷容器.

    2.2. 构建容器数据卷

    以上一步新建的镜像lxl/centos为模板并运行容器dc01/dc02/dc03

    他们已经具有容器卷/dataVolumeContainer1, /dataVolumeContainer2, /dataVolumeContainer3

    容器间传递共享数据--volumes-from

    下面演示一下. 具体操作

    1. 先启动一个父容器dc01, 在/dataVolumeContainer2中新增内容

    docker run -it --name dc01 lxl/centos

    上面我创建了一个带有挂载卷的镜像lxl/centos. 这里启动的就是这个镜像

    --name 指定容器的名字是dc01, 如果不指定会默认生成一个

    接下来我们来查看是否有挂载卷目录

     

     果然, 生来自带三个目录

    现在,我们向目录1中写入文件

    2. dc2/dc3继承自dc1,  --volumes-from

    • 退出dc01容器, 但不要关闭, 使用命令
    ctrl + p + q
    • 先创建dc02, 继承自dc01容器
     docker run -it --name dc02 --volume-from dc01 lxl/centos

    --volume-from : 继承自父类容器dc01的加载卷
    使用的镜像依然是lxl/centos
    --name : 给容器命名为dc02
    • 查看dc02中是否能够看到刚刚在dc01中创建的文件dc01.txt

     没问题, 我们确实能够看到dc01创建的文件

    • 接下来, 我们在dc02创建一个文件

    • 退出dc02 容器
    ctrl + p + q
    • 进入dc01容器, 看看是否能够看到刚刚dc02创建的dc02.txt

    我们确实看到了, 这说明, dc01 和 dc02 的数据卷是共享

    • 在创建一个dc03容器, 继承自dc01 ,方法和dc02的创建类
    [root@localhost ~]# docker run -it --name dc03 --volumes-from dc01 lxl/centos
    [root@8545291ec601 /]# 
    [root@8545291ec601 /]# ls  
    bin  dataVolumeContainer1  dataVolumeContainer2  dataVolumeContainer3  dev  etc  home  lib  lib64  lost+found  media  mnt  opt    proc  root  run  sbin  srv  sys  tmp  usr  var
    [root@8545291ec601 /]# cd dataVolumeContainer1 
    [root@8545291ec601 dataVolumeContainer1]# ls
    dc01.txt  dc02.txt
    [root@8545291ec601 dataVolumeContainer1]# touch dc03.txt
    [root@8545291ec601 dataVolumeContainer1]# ls
    dc01.txt  dc02.txt  dc03.txt

    如上面命令, 我们在dc03容器中创建了dc03.txt, 查看dc01和dc02也是都可以看到的.

    3. 问题1: 删除掉dc01, dc02和dc03的加载卷是否会被删除呢? 

    查询当前启动的容器, dc01, dc02, dc03 都是启动中的状态
    [root@localhost ~
    ]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8545291ec601 lxl/centos "/bin/sh -c /bin/bash" 4 minutes ago Up 4 minutes dc03 d1cdf1ba3148 lxl/centos "/bin/sh -c /bin/bash" 12 minutes ago Up 12 minutes dc02 52a9525a9598 lxl/centos "/bin/sh -c /bin/bash" 20 minutes ago Up 20 minutes dc01 0dcff7fc2508 5a8ecf996d8d "/bin/sh -c /bin/bash" 22 hours ago Up 22 hours objective_mcclintock

    停止dc01容器, 我们知道dc02和dc03都是继承自dc01,那么dc01停止以后, dc02和dc03的数据卷还会共享么?
    [root@localhost
    ~]# docker stop 52a9525a9598 52a9525a9598 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8545291ec601 lxl/centos "/bin/sh -c /bin/bash" 4 minutes ago Up 4 minutes dc03 d1cdf1ba3148 lxl/centos "/bin/sh -c /bin/bash" 12 minutes ago Up 12 minutes dc02 0dcff7fc2508 5a8ecf996d8d "/bin/sh -c /bin/bash" 22 hours ago Up 22 hours objective_mcclintock [root@localhost ~]# [root@localhost ~]#


    进入dc02容器, 我们发现,dc02容器依然拥有数据卷, 下面的文件也都存在, dc03也是如此 [root@localhost ~
    ]# docker attach dc02 [root@d1cdf1ba3148 dataVolumeContainer1]# ls dc01.txt dc02.txt dc03.txt


     

     dc03也是如此

      [root@localhost ~]# docker attach dc03

      [root@8545291ec601 dataVolumeContainer1]# ls

      dc01.txt  dc02.txt  dc03.txt

    4. 问题2: 删掉dc01以后, dc03创建的文件,是否可以和dc02共享呢?

    现在是在dc03容器里面, 数据卷里有3个文件, 然后创建一个新的文件dc03-2.txt
    [root@8545291ec601 dataVolumeContainer1]#
    ls dc01.txt dc02.txt dc03.txt [root@8545291ec601 dataVolumeContainer1]# touch dc03
    -2.txt [root@8545291ec601 dataVolumeContainer1]# ls dc01.txt dc02.txt dc03-2.txt dc03.txt [root@8545291ec601 dataVolumeContainer1]# [root@localhost ~]# [root@localhost ~]#


    进入dc02容器, 查看dc02中是否可以看到刚刚dc03创建的文件. 我们发现,确实看到了 [root@localhost
    ~]# docker attach dc02 [root@d1cdf1ba3148 dataVolumeContainer1]# ls dc01.txt dc02.txt dc03-2.txt dc03.txt

    总结: 以上两个问题说明:容器之间信息的传递, 数据卷的生命周期一直持续到所有的容器生命周期全部结束

    仔细想想原因是什么? 

    因为dc01, dc02, dc03他们都是挂载在宿主机上. 他们每一次数据的改动都会被同步到宿主机. 他们最终的文件内容是和宿主机保持一致的.

    ----------

    最后来回顾一下, dockerfile容器挂载的几个步骤

    1. 编写dockerfile文件

    2. build生成镜像

    3. 启动容器

    4. 查看挂载卷. 

    5. 容器间共享数据卷 --volumes-from

  • 相关阅读:
    Google资深工程师深度讲解Go语言测试与性能调优(八)
    linux命令 对日志文件的IP出现的次数进行统计 并显示次数最多的前六名
    Java之不允许变量重定义
    Java之数组
    ATL之STDTHUNK
    Java之访问控制
    Java之线程初步II
    Android之Activating Components
    ATL之如何聚合一个组件
    WTL中对话框数据交换
  • 原文地址:https://www.cnblogs.com/ITPower/p/12985269.html
Copyright © 2011-2022 走看看