• 02.k8s之docker容器


    k8s之docker容器

    容器是什么?

    容器,就是一个被隔离的进程。

    为什么要隔离?

    1. 将应用程序与外界系统隔离,保证容器外系统安全
    2. 资源隔离,只能使用指定配额

    和虚拟机的区别是什么?

    虚拟机:虚拟的是硬件,需要在上面安装操作系统才能运行应用程序。

    容器:共享下层的硬件和操作系统。

    下图是官方的

    在这里插入图片描述

    其实上图关于容器的部分并不准确,APP也就是容器并不是运行在Docker上的,Docker只是在帮助用户创建进程时添加了各种Namespace参数,容器是特殊的进程,还是运行在操作系统上的。


    实现方式优势劣势
    虚拟机虚拟化硬件隔离程度非常高资源消耗大,启动慢
    容器直接利用下层的硬件和操作系统资源利用率高,运行速度快隔离程度低, 安全性低
    1. 虚拟机是硬件级别的隔离,而容器化是进程间的隔离。

    2. 虚拟化需要模拟硬件占用部分内存,并且对宿主机操作的系统调用需要经过虚拟化软件的拦截与转换,造成资源的开销。而容器就是一个普通的进程,基本无额外的计算资源的开销。

    3. 在Linux内核中有部分的资源和对象无法namespace化,如时间。

    4. 因为容器是共享宿主机内核,所以对外暴露的供给面非常的大。

    什么是容器化应用?

    镜像,就是将容器的初始化环境固化下来,将运行进程所需要的文件系统、依赖库、环境变量、启动参数等打包整合到一起,保存成一个静态的文件。

    容器化环境可以通过镜像快速重建容器,应用程序看到的就是一致的运行环境。

    容器化应用,也就是应用程序不直接与操作系统去打交道,而是将应用程序打包成镜像,再交给容器环境去运行

    镜像与容器的关系还可以用"序列化"和"反序列化"来理解,镜像就是序列化到磁盘的数据,而容器是反序列化后内存中的对象。

    在这里插入图片描述

    在这里插入图片描述

    常用镜像操作

    命令作用
    docker pull从远端仓库拉取镜像
    docker images列出当前本地已有镜像
    docker rmi删除不再使用的镜像

    常用容器操作

    命令作用例子
    docker run使用镜像启动容器
    docker ps列出正在运行的容器
    docker exec在容器内执行另一个程序
    docker stop停止容器
    docker start将停止的容器再次启动
    docker rm删除容器
    docker export将容器内的文件系统导出docker export -o rootfs.tar 容器ID

    容器被停止后,docker ps命令就看不到该容器了,需要使用docker ps -a来查看所有容器,包括已经停止的容器。

    可能会导致非常多已经停止的容器占用系统资源,所以建议docker run时添加--rm参数,在容器运行完毕时自动清除

    docker exec是如何进入到容器中的?

    该命令会创建一个新的进程加入到容器的namepsace中。

    /proc/{进程ID}/ns/下的虚拟文件会链接到真实的Namespace文件上。通过查看exec创建的进程ns文件可以看出和容器的Namespace文件一致

    [root@k8s-master proc]# ll /proc/288948/ns/pid
    lrwxrwxrwx 1 root root 0 Jul  8 11:27 /proc/288948/ns/pid -> 'pid:[4026532247]'
    
    [root@k8s-master proc]# ll /proc/289220/ns/pid
    lrwxrwxrwx 1 root root 0 Jul  8 11:27 /proc/289220/ns/pid -> 'pid:[4026532247]'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    docker run和docker exec的区别是什么?

    run是将镜像运行成容器并执行命令,该命令为1号进程。

    exec是在容器中执行一个命令,该命令是另一个进程,加入到了容器的namespace中。

    容器镜像

    镜像内部机制

    容器镜像内部是由许多的镜像层(Layer)组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像中共享,然后多个层像搭积木叠加起来,使用**联合文件系统(UnionFS)**将它们合并起来,最终形成容器看到的文件系统。

    镜像中的层级是只读层,而容器所在的层级是可读写层。

    在这里插入图片描述

    镜像的分层信息可以通过命令docker inspect 镜像名称获取,其中RootFs是对应的信息

    >>> docker inspect b3log/siyuan
    
    .....
    "RootFS": {
                "Type": "layers",
                "Layers": [
                    "sha256:24302eb7d9085da80f016e7e4ae55417e412fb7e0a8021e95e3b60c67cde557d",
                    "sha256:e7356c89d8c31fc628769b331f73d6e036e1d5900d2d2a3990c89ef91bce707a",
                    "sha256:90358380b9ea63cfb8832ae627455faf85596e822ff8abe9e1d7c8bbd93804ad",
                    "sha256:c6d8ffacc07d179562cd70114402e549d9fce92b12a019d3f4003eb94944d089"
                ]
            }
    ....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    好处是,如果多个镜像使用了相同的层,可以直接共享,减少磁盘空间的占用。比如nginx镜像和Tomcat镜像都是用了基础镜像centos,那么该基础镜像可以共享。

    在这里插入图片描述

    OverlayFS

    镜像层和容器是如何合并的呢?

    在这里插入图片描述

    lowerdir是镜像层,upperdir是容器层,如果双方有相同文件则展示容器层的文件。

    在容器写文件时,会先从镜像层拷贝一份文件到容器层,然后再写入,使用的是**写时复制(copy on write)**策略

    例子

    overlay2
    ├── lowerdirA
    │   ├── a     内容:AA
    │   └── b     内容:AA
    ├── lowerdirB
    │   └── a     内容: BB
    ├── merge
    ├── upper
    └── work
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行以下命令使用overlay进行合并层

    mount -t overlay overlay -o lowerdir=lowerdirA:lowerdirB,upperdir=upper,workdir=work merge
    
    • 1

    lowerdir为镜像层,upperdir为容器层,merge目录为最终展示层。

    可以看到merge目录中的a文件内容lowerdirA镜像层的内容

    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# cat merge/a 
    AA
    
    • 1
    • 2

    当我们修改megre目录中的a文件时,可以看到upperdir目录的会生成a文件并且内容修改后的内容

    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# ls upper/
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# echo upper > merge/a
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# ls upper/
    a
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# cat upper/a
    upper
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# cat merge/a
    upper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当删除文件merge/a时,会出现什么情况呢?

    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# rm merge/a
    rm: remove regular file ‘merge/a’? y
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# ll lowerdirA/
    total 8
    -rw-r--r-- 1 root root 3 Jul 12 19:11 a
    -rw-r--r-- 1 root root 3 Jul 12 19:10 b
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]# ll upper/
    total 0
    c--------- 1 root root 0, 0 Jul 12 19:31 a
    [root@iZwz93q4afq8ck02cesqh4Z k8s_learn]#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看出镜像层lowerdirA的文件a是不变的,而在容器层upper中的a文件类型变成了c,该文件类型,最终在展示层看不到该文件了。

    可以使用命令docker inspect来查看layer的路径

    >>> docker inspect xxx
    
    ....
    "GraphDriver": {
                "Data": {
                    "LowerDir": "/var/lib/docker/overlay2/641e486c54d15d2a8d807fd8964f4a4b8687cbcf95c176cd9a46553b1e80341d/diff:/var/lib/docker/overlay2/ed9ad4fb9d0f9bf3aea553c634e54fef89448cf43c5b662468d79f01cf41d0c3/diff:/var/lib/docker/overlay2/9db169e1ad2165f688e652ef06dfe9a3e465c31299f3c357a37a6919747efbc8/diff",
                    "MergedDir": "/var/lib/docker/overlay2/fa3166e545a2d1811dbeecb6f1fdda96b9f97b3cd629f32a8ea378aa79b1c780/merged",
                    "UpperDir": "/var/lib/docker/overlay2/fa3166e545a2d1811dbeecb6f1fdda96b9f97b3cd629f32a8ea378aa79b1c780/diff",
                    "WorkDir": "/var/lib/docker/overlay2/fa3166e545a2d1811dbeecb6f1fdda96b9f97b3cd629f32a8ea378aa79b1c780/work"
                },
                "Name": "overlay2"
            },
    ....
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Dockerfile

    Dockerfile是一个用来创建镜像的文本文件,该文件中的每一条命令都会成生成一个layer。

    例子:

    最简单的Dockerfile的例子

    FROM busybox                  # 选择基础镜像
    CMD echo "hello world"        # 启动容器时默认运行的命令
    
    • 1
    • 2

    FROM指令是构建使用的基础镜像

    CMD指令是用于启动容器时默认运行的命令

    使用docker build 即可执行创建镜像

    docker build -f Dockerfile .
    
    Sending build context to Docker daemon   7.68kB
    Step 1/2 : FROM busybox
     ---> d38589532d97
    Step 2/2 : CMD echo "hello world"
     ---> Running in c5a762edd1c8
    Removing intermediate container c5a762edd1c8
     ---> b61882f42db7
    Successfully built b61882f42db7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    容器与外部的交互

    如何拷贝宿主机的文件到容器内

    可以使用docker cp命令将宿主机的文件拷贝到容器中。

    docker cp a.txt 062:/tmp
    
    • 1

    其中的062为容器ID,如果想将容器中的文件拷贝到宿主机中,反过来即可。

    docker cp 062:/tmp/a.txt /tmp
    
    • 1

    注意,这里的拷贝是临时的,拷贝进容器中的文件只存在于容器中,不存在与镜像中,如果想要将文件拷贝到镜像中,在写Dockerfile时使用copy命令拷贝即可。

    宿主机与容器共享文件夹

    在使用镜像运行容器时,使用参数-v可以将宿主机中的文件夹映射到容器中,双方修改该文件夹中的内容,都可以及时看到。

    docker run -d --rm -v /tmp:/tmp redis
    
    • 1

    如何实现网络互通?

    docker提供三种网络模式:

    • null,无网络
    • host,直接使用宿主机网络,在创建容器时,使用–net=host参数。

    其实就是创建新的namespace,而是直接加入到宿主机的namesapce

    docker run -d --rm --net=host nginx:alpine
    
    • 1
    • bridge,桥接模式,由软件虚拟网卡与网桥,容器和宿主机都接入该网桥,即可正常发送数据包。可以使用参数--net=bridge创建容器,但这个是默认参数。
    docker run -d --rm nginx:alpine
    
    • 1
    网络模式优点缺点
    host因为是直接使用宿主机的网络,效率更高
    运行太多的容器,会导致端口发生冲突
    bridge因为有了网桥可以设置更多的策略,比如流量控制等需要软件模拟虚拟网卡与网桥,效率更低

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qGP1ISa-1660568353845)(https://gcore.jsdelivr.net/gh/tenqaz/BLOG-CDN@main/20220712213036.png)]

    关于k8s与docker的关系

    在2014年的时候,Docker如日中天,那么k8s自然选择基于docker上运行。

    在2016年k8s加入了CNCF,一个开源的云原生计算基金会。

    并且引入了一个接口标准:CRI,Container Runtime Interface。也就是规定kubelet该如何调用Container Runtime去管理容器和镜像,但这是一套全新的接口,和之前的Docker完全不兼容。目的很明显,不想绑定Docker,可以随时将Docker踢掉。

    因为docker已经非常成熟,各大厂商不可能将Docker全部替换。所以k8s在kubelet和Docker中间加一个"适配器",把Docker的接口转换成符合CRI标准的接口。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccaTpESe-1660568353858)(https://gcore.jsdelivr.net/gh/tenqaz/BLOG-CDN@main/20220717211119.png)]

    什么是containerd?

    不过 Docker 也没有“坐以待毙”,而是采取了“断臂求生”的策略,推动自身的重构,把原本单体架构的 Docker Engine 拆分成了多个模块,其中的 Docker daemon 部分就捐献给了 CNCF,形成了 containerd。

    containerd 作为 CNCF 的托管项目,自然是要符合 CRI 标准的。但 Docker 出于自己诸多原因的考虑,它只是在 Docker Engine 里调用了 containerd,外部的接口仍然保持不变,也就是说还不与 CRI 兼容。

    由于 Docker 的“固执己见”,这时 Kubernetes 里就出现了两种调用链:第一种是用 CRI 接口调用 dockershim,然后 dockershim 调用 Docker,Docker 再走 containerd 去操作容器。第二种是用 CRI 接口直接调用 containerd 去操作容器。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5o4JjlVu-1660568353859)(https://gcore.jsdelivr.net/gh/tenqaz/BLOG-CDN@main/20220717212252.png)]

    显而易见,使用第二种省去了dockershim和Docker Engine两个环节,损耗更少,性能也提升了。

    正式"弃用Docker"

    在2020年K8s弃用Docker支持,但该弃用支持弃用了"dockershim"的这个组件,也就是把dockershim移出kubelete,只是绕过Docker,直接调用了Docker内部的containerd而已。

    并且对docker也无影响,因为docker内部也是使用开源的containerd。

    唯一影响的是,k8s是直接操作containerd操作容器,那么它和docker是独立的工作环境,彼此都不能访问对方的容器和镜像,也就是docker ps看不到k8s运行的容器。改用crictl命令。

    Docker 重构自身,分离出 containerd,这是否算是一种“自掘坟墓”的行为呢?如果没有 containerd,那现在的情形会是怎么样的呢?

    Docker 是一个完整的软件产品线,不止是 containerd,它还包括了镜像构建、分发、测试等许多服务,甚至在 Docker Desktop 里还内置了 Kubernetes。

    docker分离containerd是一个很聪明的举动!与其将来被人分离或者抛弃不用,不如我主动革新,把Kubernates绑在我的战车上,这样cri的第一选择仍然是docker的自己人。
    一时的退让是为了更好的将来。

  • 相关阅读:
    12、分页插件
    Linux7-fork、内存管理相关的概念、fork写时拷贝技术
    代理IP在保护跨境商家网络安全中的重要作用
    网络相关知识
    [HECTF 2022]—Web WirteUp
    五、伊森商城 前端基础-Vue p24
    ADB环境搭建和抓取Crash日志实践总结
    变量使用volatile和不使用volatile的区别
    2 C++中的引用
    [附源码]计算机毕业设计springboot家庭教育app
  • 原文地址:https://blog.csdn.net/qq_22918243/article/details/126354735