zoukankan      html  css  js  c++  java
  • 容器基础3:容器镜像

    1.容器概念回溯

    容器本质是一种特殊的进程
    namespace作用是视觉隔离,cgroups作用是限制,给沙箱围了已圈墙

    2.容器内看到的文件系统是什么样子?

    联想Mount namespace问题
    容器里的应用进程,按理应该看到一份完全独立的文件系统,这样就可以在自己容器目录(/tmp)下操作
    不受宿主机及其他容器影响

    3.拿c的代码去验证一下

    伪代码

    
    #define _GNU_SOURCE
    #include <sys/mount.h> 
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <sched.h>
    #include <signal.h>
    #include <unistd.h>
    #define STACK_SIZE (1024 * 1024)
    static char container_stack[STACK_SIZE];
    char* const container_args[] = {
      "/bin/bash",
      NULL
    };
    
    int container_main(void* arg)
    {  
      printf("Container - inside the container!
    ");
      execv(container_args[0], container_args);
      printf("Something's wrong!
    ");
      return 1;
    }
    
    int main()
    {
      printf("Parent - start a container!
    ");
      int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWNS | SIGCHLD , NULL);
      waitpid(container_pid, NULL, 0);
      printf("Parent - container stopped!
    ");
      return 0;
    }
    

    功能说明:main函数里,clone系统调用创建了新的子进程container_main,声明要为它启用Mount Namespace(即: CLONE_NEWNS标记)

    子进程执行的是/bin/bash。这个shell运行在了Mount Namespace隔离环境中

    编译代码

    
    $ gcc -o ns ns.c
    $ ./ns
    Parent - start a container!
    Container - inside the container!
    

    进入容器中,执行ls命令

    ls /tmp
    ...
    

    发现展示的内容和宿主机的内容是一样的

    发现即使开启了MountNamespace,容器进程总看到的文件系统还是和宿主机一毛一样

    4.重新认知一下Mount Namespace

    Mount Namespace修改的,是容器进程对文件系统“挂载点”视觉的认知。
    只有在“挂载”操作后,视觉才会被改变。在挂载之前,新容器会直接继承宿主机的挂载点

    如何修复呢?
    在容器执行前,先挂载

    
    int container_main(void* arg)
    {
      printf("Container - inside the container!
    ");
      // 如果你的机器的根目录的挂载类型是shared,那必须先重新挂载根目录
      // mount("", "/", NULL, MS_PRIVATE, "");
      mount("none", "/tmp", "tmpfs", 0, "");
      execv(container_args[0], container_args);
      printf("Something's wrong!
    ");
      return 1;
    }
    

    验证

    
    $ gcc -o ns ns.c
    $ ./ns
    Parent - start a container!
    Container - inside the container!
    $ ls /tmp
    

    发现变为空目录了,重新挂载生效了,容器内可以用mount -l检查

    
    $ mount -l | grep tmpfs
    none on /tmp type tmpfs (rw,relatime)
    

    挂载操作+mount namespace的操作,重新挂载的操作只在容器内的mount namespace中有效。

    而在宿主机上执行mount -l 可以发现没有tmpfs的挂载信息

    5.更优化的环境,容器中文件系统独立隔离环境,容器镜像

    换句话说,就是/分区下是独立的文件系统

    chroot帮忙了。change root system 改变进程的根目录到你指定的位置

    实际mount namespace就是基于chroot 改良发明的,也是linux 操作系统里的第一个namespace

    容器根目录更真实,一版会在根目录下挂载一个完整的文件系统,比如centos的iso,容器启动后,查看ls / 展示的就是centos的所有目录和文件

    挂载根目录,用来为容器提供隔离后的执行文件系统,就是容器镜像(rootfs)

    一般容器镜像,会包含如下内容

    
    $ ls /
    bin dev etc home lib lib64 mnt opt proc root run sbin sys tmp usr var
    

    进入容器后执行的/bin/bash,与宿主机的/bin/bash完全不同

    6.docker的核心原理

    为待创建的用户进程:
    1.启用Linux Namespace配置
    2.设置指定的Cgroups参数
    3.切换进程的根目录

    容器就诞生了。docker项目最后一步切换根目录上,优先使用pivot_root系统调用,如果不支持,才会使用chroot。这2个系统调用功能类似,但有细微区别

    7.rootfs的特殊性

    rootfs是操作系统包含的文件,配置,目录,不包括系统内核。linux中这2部分是分开的。操作系统只有开机启动才会加载指定版本的内核镜像

    同一个宿主机上的n个容器共享宿主机的系统内核

    8.镜像带来的强一致性

    之前:云端环境与本地服务器环境不同,环境不同,非常痛苦

    rootfs打包的不只是应用,而是操作系统层面的打包,应用以及所需要的所有依赖,都被封装一起

    应用的依赖,认知不能局限在语言层面,比如golang的godeps.json。实际上操作系统本身才是应用程序最完整的“依赖库”

    深入到操作系统层级的环境一致性

    9.镜像的分层

    用到了什么技术?
    Union File System联合文件系统能力,UnionFS

    将多个不同位置的目录联合挂载到同一个目录下

    
    $ docker image inspect ubuntu:latest
    ...
         "RootFS": {
          "Type": "layers",
          "Layers": [
            "sha256:f49017d4d5ce9c0f544c...",
            "sha256:8f2b771487e9d6354080...",
            "sha256:ccd4d61916aaa2159429...",
            "sha256:c01d74f99de40e097c73...",
            "sha256:268a067217b5fe78e000..."
          ]
        }
    

    rootfs的层次结构
    image

    • 只读层
      最下面的5层,挂载方式是只读
      这些层的内容。增量的方式分别包含了centos操作系统的一部分
    
    $ ls /var/lib/docker/aufs/diff/72b0744e06247c7d0...
    etc sbin usr var
    $ ls /var/lib/docker/aufs/diff/32e8e20064858c0f2...
    run
    $ ls /var/lib/docker/aufs/diff/a524a729adadedb900...
    bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
    
    • 可读写层
      最上面一层,rw方式挂载,没有写入文件前,整个目录空的,一旦容器里做了写操作,产生的内容增量出现在这个层里
      如果是删除只读层里的一个文件呢?
      通过在读写层创建一个whiteout文件,白名单文件,把你要删除的文件遮挡起来,其实并没有真正删除
      比如,你要删除只读层里一个名叫 foo 的文件,那么这个删除操作实际上是在可读写层创建了一个名叫.wh.foo 的文件。这样,当这两个层被联合挂载之后,foo 文件就会被.wh.foo 文件“遮挡”起来,“消失”了。这个功能,就是“ro+wh”的挂载方式,即只读 +whiteout 的含义。我喜欢把 whiteout 形象地翻译为:“白障”。
    • Init层
      只读层和读写层之间,docker的内部曾,专门存放/etc/hosts,/etc/resolv.conf等信息
      用户往往需要启动容器时写入一些特定的值hostname,就需要在读写层对他们进行修改
      这个修改支队当前容器有效,不希望docker commit 这些也提交
      所以单独抽了一个init层出来,用户docker commit提交的是读写层,不包含这些配置文件内容

    10.总结

    原创:做时间的朋友
  • 相关阅读:
    稳扎稳打Silverlight(47) 4.0UI之操作剪切板, 隐式样式, CompositeTransform, 拖放外部文件到程序中
    返璞归真 asp.net mvc (9) asp.net mvc 3.0 新特性之 View(Razor)
    返璞归真 asp.net mvc (6) asp.net mvc 2.0 新特性
    稳扎稳打Silverlight(48) 4.0其它之打印, 动态绑定, 增强的导航系统, 杂七杂八
    精进不休 .NET 4.0 (9) ADO.NET Entity Framework 4.1 之 Code First
    稳扎稳打Silverlight(42) 4.0控件之Viewbox, RichTextBox
    稳扎稳打Silverlight(53) 4.0通信之对WCF NetTcpBinding的支持, 在Socket通信中通过HTTP检索策略文件, HTTP请求中的ClientHttp和BrowserHttp
    稳扎稳打 Silverlight 4.0 系列文章索引
    稳扎稳打Silverlight(54) 4.0通信之对UDP协议的支持: 通过 UdpAnySourceMulticastClient 实现 ASM(Any Source Multicast),即“任意源多播”
    返璞归真 asp.net mvc (8) asp.net mvc 3.0 新特性之 Model
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14900388.html
Copyright © 2011-2022 走看看