zoukankan      html  css  js  c++  java
  • 容器镜像

    镜像是用于创建容器的只读模板。
    目前,OCI镜像规范定义了容器镜像的开放规范、容器运行软件的构建规范。大多数容器运行时均支持此统一的开放镜像标准。
     
    Linux下的镜像存储基于UnionFS,利用union mount(UnionFS的一种挂载机制)将不同的目录挂载到同一个虚拟文件系统下,以实现Layer的概念。Layer可以被其它镜像所复用。
    挂载目录的时候会严格按照各目录之间的增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载。挂载到同一虚拟文件系统下的所有目录全部设置成read-only权限。
    当镜像被运行成一个容器时,会在所有read-only的目录挂载结束后,继续挂载一个空的read-write层,写操作是在read-only上的一种增量操作,不影响read-only目录。这个容器的read-write层也可以通过commit命令把它变成一个镜像顶层最新的read-only层。
     
    示例:使用Docker创建镜像
    rootfs的存储位置可以在daemon.json中指定:
    {
      "data-root": "/data/aikube/docker"
    }
    底层可能基于不同的文件系统,比如AUFS、btrfs、devicemapper、overlay。
    docker对不同文件系统分别做了对应定制的存储驱动,通过驱动把镜像存在磁盘上面。
    创建方法包括:
    (1)基于已有容器创建
    docker commit <container name | id> repo:tag
    -a --author=""         作者信息
    -m --message=""    提交信息
    -p  --pause=true     提交时暂停容器运行
    (2)导出容器为本地文件
    将容器的文件系统作为tar文件导出到stdout:
    docker export <container name | id> -o xxx.tar
    docker export <container name | id> > xxx.tar
    导出容器为文件,将丢弃所有历史记录和元数据信息。
    基于本地tgz文件导入:
    cat ubuntu-14.04-x86_64-minimal.tar.gz | docker import - ubuntu:14.04
    docker import /path/to/exampleimage.tgz
    基于本地目录导入:
    tar -c . | docker import - exampleimagedir
    (3)导出镜像为本地文件
    将镜像的文件系统作为tar文件导出到stdout:
    docker save repo:tag > xxx.tar
    docker save repo:tag  -o xxx.tar
    基于本地tar文件导入:
    docker load --input xxx.tar
    docker load < xxx.tar
    使用脚本批量加载要用到的所有镜像:
    imageTgzNames=(`ls /data/k8s-package/images`)
    for nameTgz in ${imageTgzNames[@]};
    do
      docker load < /data/k8s-package/images/${nameTgz}
    done
    (4)基于Dockerfile创建(正规方法)
    Dockerfile是一个文本格式的配置文件,可快速创建自定义镜像
    编写完成后通过docker build命令创建镜像,该命令会读取指定目录下的所有dockerfile
    docker build -t repo:tag /tmp/docker_builder/
    -f 指定Dockerfile,不指定的话默认构建当前目录下所有Dockerfile
    -t 指定镜像信息
    --no-cache=true/false 指定是否使用缓存。若使用,会遍历本地所有镜像,发现镜像与即将构建出的镜像一致时,将找到的镜像作为cache镜像,复用cache镜像作为构建结果。使用ADD/COPY或RUN命令存在外部依赖(如yum install)时一般需要不使用缓存。
    指定目录下不能有多余的文件,如果是当前目录则为.
    每个构建步骤都会对已有的文件系统进行操作,这样就会带来文件系统内容的变化,这些变化称之为changeset。
    把构建步骤所产生的变化依次作用到一个空文件夹上,就能够得到一个完整的镜像。 
    指令详解:
    ①第一行必须指定来自的镜像
    FROM repo:tag
    有时候会使用多个镜像,可以为镜像指定别名:
    FROM golang:1.15 as builder
    ②维护者信息
    MAINTAINER docker_user docker_user@email.com
    ③从上下文目录中复制文件或者目录到容器里指定路径
    ADD <源路径1> <目标路径>
    COPY <源路径1> <目标路径>
    区别:ADD的<源文件>为压缩格式为gzip、bzip2、xz的tar压缩文件时,会自动复制并解压到<目标路径>。
    在多个基础镜像间拷贝:
    COPY --from=builder /workspace/manager .
    ④运行命令
    RUN <command>
    此命令将于构建时在shell中运行
    ⑤设置容器启动命令(只能设置一次)
    CMD ["/usr/sbin/sshd","-D"]
    ENTRYPOINT ["java","-jar","/xxx.jar"]
    CMD命令设置容器启动后默认执行的命令及其参数,但CMD设置的命令能够被docker run命令后面的命令行参数替换
    ENTRYPOINT配置容器启动时的执行命令,即使运行docker run时指定了其他命令也不会覆盖,除非docker run使用了--entrypoint 参数
    两个命令同时使用时,CMD指定变参,相当于给ENTRYPOINT传参,例如:
    FROM nginx
    ENTRYPOINT ["nginx", "-c"] # 定参
    CMD ["/etc/nginx/nginx.conf"] # 变参
    直接运行容器时,会执行以下命令:
    nginx -c /etc/nginx/nginx.conf
    传参运行:
    docker run  nginx:test -c /etc/nginx/new.conf
    此时会覆盖参数
    ⑥参数
    环境变量设置:
    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...
    构建时的参数设置
    ARG <参数名>[=<默认值>]
    ⑦用户设置
    USER <用户名>[:<用户组>]
    overlay文件系统下docker镜像与容器存储结构
    以overlay这个文件系统为例,overlay文件系统的工作原理如下图 。
    lower层是镜像层,它是一个只读层。容器刚创建出来的时候,upper其实是空的。这个时候如果去读的话,所有数据都是从lower层读来的。
     
    upper层是容器的读写层,采用了写实复制的机制,只有对某些文件需要进行修改的时候才会从lower层把这个文件拷贝上来,之后所有的修改操作都会对upper层的副本进行修改。
    workdir充当中间层,当对upper层里面的副本进行修改时,会先放到workdir,然后再从workdir移到 upper 里面去。
    PS:这个是overlay 的工作机制。
    mergedir是统一视图层。从mergedir里面可以看到upper和lower中所有数据的整合。docker exec到容器里面,看到的文件系统其实就是mergedir统一视图层。
     
    overlay里面其实是没有真正的删除操作的。删除其实是通过对文件进行标记,然后从最上层的统一视图层去看,看到这个文件标记就认为这个文件是被删掉的。这个标记有两种方式:
        一种是 whiteout 的方式。
        第二个就是通过设置目录的一个扩展权限,通过设置扩展参数"trusted.overlay.opaque"为y来做到目录的删除。、
     
    devicemapper文件系统下docker镜像与容器存储结构
    devicemapper文件系统下实际有两种模式:
    (1)loop-lvm模式
    用一个稀疏文件来当成一个块设备,给devicemapper用,作为Docker镜像容器文件系统
    初始化时创建了两个比较大的稀疏文件:20G的Metadata loop file,默认在/var/lib/docker/devicemapper/metadata;100G的Data loop file,默认在/var/lib/docker/devicemapper/data
    把文件映射为两个伪设备,虽然是伪设备,但是可以像普通块设备一样,对其进行格式化为想要的文件系统,比如xfs
    基于这两个loop伪设备创建100GB大小的thin-pool(基于linux内核提供的dm机制),每启动一个容器就从 thin-pool里分出来10GB空间给该容器
    (2)direct-lvm模式
    直接使用块设备创建thin-pool,且块设备可以根据需要增长
     
    启动docker后/var/lib/docker目录的结构如下:
    [root@docker-100 docker]# tree ./
    ./
    ├── containers
    ├── devicemapper
    │   ├── devicemapper
    │   │   ├── data
    │   │   └── metadata
    │   └── metadata
    │       ├── base
    │       ├── deviceset-metadata
    │       └── transaction-metadata
    ├── graph
    ├── linkgraph.db
    ├── repositories-devicemapper
    ├── tmp
    ├── trust
    └── volumes
    /var/lib/docker/devicemapper/devicemapper/目录下有两个文件:data和metadata,用来存储对应的存储池和相关的元数据。
    /var/lib/docker/devicemapper/metadata/目录下有三个文件:base、transaction-metadata和deviceset-metadata,用来存放前面元数据的id、大小、以及UUID等信息。
     
    现在存在几个空目录:
    /var/lib/docker/containers
    /var/lib/docker/graph
    /var/lib/docker/tmp
    /var/lib/docker/trust
    /var/lib/docker/volumes
    还有一个文件/var/lib/docker/repositories-devicemapper 
     
    下载centos镜像:
    [root@docker-100 metadata]# docker images -a
    REPOSITORY      TAG             IMAGE ID             CREATED           VIRTUAL SIZE
    centos                latest        ce20c473cd8a      8 weeks ago           172.3 MB
    <none>            <none>     4234bfdd88f8      8 weeks ago            172.3 MB
    <none>            <none>     812e9d9d677f     8 weeks ago             172.3 MB
    <none>            <none>     168a69b62202     8 weeks ago             172.3 MB
    <none>             <none>     47d44cb6f252     3 months ago           0 B
    /var/lib/docker目录的结构变为:
    [root@docker-100 docker]# tree ./
    ./
    ├── containers
    ├── devicemapper
    │   ├── devicemapper
    │   │   ├── data
    │   │   └── metadata
    │   ├── metadata
    │   │   ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │   │   ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │   │   ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │   │   ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │   │   ├── base
    │   │   ├── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    │   │   ├── deviceset-metadata
    │   │   └── transaction-metadata
    │   └── mnt
    │       ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │       ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │       ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │       ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │       └── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    ├── graph
    │   ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   └── _tmp
    ├── linkgraph.db
    ├── repositories-devicemapper
    ├── tmp
    ├── trust
    └── volumes
     
    20 directories, 27 files

    查看/var/lib/docker/devicemapper/metadata目录下的内容(按device_id排序)

    [root@docker-100 metadata]# cat base
    {"device_id":1,"size":107374182400,"transaction_id":3,"initialized":true}
     
    [root@docker-100 metadata]# cat 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    {"device_id":2,"size":107374182400,"transaction_id":4,"initialized":false}
     
    [root@docker-100 metadata]# cat 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    {"device_id":3,"size":107374182400,"transaction_id":5,"initialized":false}
     
    [root@docker-100 metadata]# cat 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    {"device_id":4,"size":107374182400,"transaction_id":6,"initialized":false}
     
    [root@docker-100 metadata]# cat 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    {"device_id":5,"size":107374182400,"transaction_id":7,"initialized":false}
     
    [root@docker-100 metadata]# cat ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    {"device_id":6,"size":107374182400,"transaction_id":8,"initialized":false}

    除了base这个文件以外,其它都对应于刚才添加的中间件

    那么可以得出:
    /var/lib/devicemapper/metadata 目录下的文件(除了base、 deviceset- metadata、transaction-medatata)都是images本身和images的中间件信息,用来描述它们的id、size、transaction_id、是否initialized,并且它们大小都是一样的。
    /var/lib/docker/devicemapper下新出现了目录/var/lib/docker/devicemapper/mnt 
    它主要是用来挂载 images 和Container的目录,因为 devicemapper 本身就是通过在存储池中挂载的方式进行运行的。
     
     /var/lib/docker/graph目录下是在每个images本身及中间件,每个里面有三个文件,分别为:
        json文件是用来描述 images本身或者中间件的详细信息
        layersize是用来表示中间件的大小
        tar-data.json.gz
     
    另一个文件/var/lib/docker/repositories-devicemapper ,也其实就是记录 images本身(不是中间件)信息的文件;换句话说,它记录了镜像名称、镜像 tag(默认为 latest)、镜像ID等信息。
    内容为:
    {"Repositories":{"centos":{"latest":"ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e"}},"ConfirmDefPush":true}
    运行centos镜像,容器名设为centos
    [root@docker-100 docker]# tree /var/lib/docker/
    /var/lib/docker/
    ├── containers
    │   └── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    │       ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-json.log
    │       ├── config.json
    │       ├── hostconfig.json
    │       ├── hostname
    │       ├── hosts
    │       ├── resolv.conf
    │       ├── resolv.conf.hash
    │       └── secrets
    ├── devicemapper
    │   ├── devicemapper
    │   │   ├── data
    │   │   └── metadata
    │   ├── metadata
    │   │   ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │   │   ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │   │   ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │   │   ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │   │   ├── base
    │   │   ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    │   │   ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-init
    │   │   ├── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    │   │   ├── deviceset-metadata
    │   │   └── transaction-metadata
    │   └── mnt
    │       ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │       ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │       ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │       ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │       ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    │       ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-init
    │       └── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    ├── graph
    │   ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    │   │   ├── json
    │   │   ├── layersize
    │   │   ├── tar-data.json
    │   │   └── tar-data.json.gz.bak
    │   ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   ├── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    │   │   ├── json
    │   │   ├── layersize
    │   │   └── tar-data.json.gz
    │   └── _tmp
    ├── linkgraph.db
    ├── repositories-devicemapper
    ├── tmp
    ├── trust
    └── volumes
     
    24 directories, 37 files

    /var/lib/docker/devicemapper/metadata/目录下多了两个文件:

    [root@docker-100 containers]# tree /var/lib/docker/devicemapper/metadata/
    /var/lib/docker/devicemapper/metadata/
    ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    ├── base
    ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-init
    ├── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    ├── deviceset-metadata
    └── transaction-metadata
     
    0 directories, 10 files
    这两个文件对应于运行容器时在镜像顶部添加的可读写的层
    查看base+后面的5个中间件(包括 images本身)对应的文件+新增的两个文件,发现5个中间件对应的文件没变,新增的两个文件(c2adc… 和 c2adc…-init)描述了容器本身的一些信息。
    [root@docker-100 metadata]# cat base
    {"device_id":1,"size":107374182400,"transaction_id":3,"initialized":true}
     
    [root@docker-100 metadata]# cat 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    {"device_id":2,"size":107374182400,"transaction_id":4,"initialized":false}
     
    [root@docker-100 metadata]# cat 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    {"device_id":3,"size":107374182400,"transaction_id":5,"initialized":false}
     
    [root@docker-100 metadata]# cat 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    {"device_id":5,"size":107374182400,"transaction_id":7,"initialized":false}
     
    [root@docker-100 metadata]# cat 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    {"device_id":4,"size":107374182400,"transaction_id":6,"initialized":false}
     
    [root@docker-100 metadata]# cat ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
    {"device_id":6,"size":107374182400,"transaction_id":8,"initialized":false}
     
    [root@docker-100 metadata]# cat c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-init
    {"device_id":7,"size":107374182400,"transaction_id":9,"initialized":false}
     
    [root@docker-100 metadata]# cat c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    {"device_id":8,"size":107374182400,"transaction_id":10,"initialized":false}
    发生变化的第二个目录 /var/lib/docker/devicemapper/mnt,和metadata目录一样,只是在目录下新增了两个目录
    [root@docker-100 mnt]# tree /var/lib/docker/devicemapper/mnt/
    /var/lib/docker/devicemapper/mnt/
    ├── 168a69b6220279e6d5bd8dafd2edf71434a08e32b60a7060f7a705f64857169d
    ├── 4234bfdd88f8ed2bc4607bd2ebba2d41d61e2693ad0d184e7b05e1b57f8b8b33
    ├── 47d44cb6f252ea4f6aecf8a447972de5d9f9f2e2bec549a2f1d8f92557f4d05a
    ├── 812e9d9d677f15c39277b2edc8f9bc07354c899483409bb07d1c13c2b9c33ec8
    ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
    ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-init
    └── ce20c473cd8ac1fab6601529ce6a075743f2cf7a8f4cfed2216f8cfcb53bfc4e
     
    7 directories, 0 files
    发生变化的第三个目录/var/lib/docker/container:
    [root@docker-100 containers]# tree /var/lib/docker/containers/
    /var/lib/docker/containers/
    └── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052
        ├── c2adc691ad19428e44d655d9daa8573cd71b162e05faaaa824010929089bc052-json.log
        ├── config.json
        ├── hostconfig.json
        ├── hostname
        ├── hosts
        ├── resolv.conf
        ├── resolv.conf.hash
        └── secrets
     
    2 directories, 7 files
    下面有一个 c2adc…(容器本身)的目录,并且该目录下有许多子文件,如container log信息、配置文件(需要 cat config.json | Python -mjson.tools 查看)、还有host及resolv等配置
     
    如果在这时候删除该容器,就会发现容器的信息全都没了,所涉及的文件包括:
        /var/lib/docker/devicemapper/metadata/c2adc… 及 c2adc…-init 文件
        /var/lib/docker/devicemapper/mnt/c2adc… 及 c2adc…-init文件
        /var/lib/docker/container/c2adc文件夹
     
    总结:每个文件及作用
    1、/var/lib/docker/devicemapper/devicemapper/data      #用来存储相关的存储池数据     
    2、/var/lib/docker/devicemapper/devicemapper/metadata   #用来存储相关的元数据。3、/var/lib/docker/devicemapper/metadata/       #用来存储 device_id、size、传输_id、初始化信息
    4、/var/lib/docker/devicemapper/mnt                     #用来存储挂载信息
    5、/var/lib/docker/container/                           #用来存储容器信息
    6、/var/lib/docker/graph/                               #用来存储镜像中间件及本身详细信息和大小 、以及依赖信息7、/var/lib/docker/repositores-devicemapper             #用来存储镜像基本信息
    8、/var/lib/docker/tmp                                  #docker临时目录  
    9、/var/lib/docker/trust                                #docker信任目录
    10、/var/lib/docker/volumes                             #docker卷目录
  • 相关阅读:
    javascript的propertyIsEnumerable()方法
    delete删除属性
    浅入javascript正则表达式的规则.
    关于jQuery的$.proxy()应用.
    jQuery的删除的三种方法remove(),detach(),empty()
    我来提出一个问题,大家一起讨论讨论.
    如何采集淘宝最新价格
    C中的正则函数sscanf
    HTTP协议头部与Keep-Alive模式详解
    如何实现HTTPSERVER
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/14184096.html
Copyright © 2011-2022 走看看