k8s(一)走进docker

k8s(一)走进docker

Build,Ship and Run Any App,Anywhere

作者:lomtom

个人网站:lomtom.cn 🔗

个人公众号:博思奥园 🔗

你的支持就是我最大的动力。

k8s系列:

  1. k8s(一)走进docker
  2. k8s(二)Docker的HelloWorld
  3. k8s(三)走进k8s
  4. k8s(四)安装k8s集群
  5. k8s(五)k8s的HelloWorld
  6. k8s(六)走进Pod

docker
Build,Ship and Run Any App,Anywhere

在软件开发中,环境配置往往是最繁琐的任务之一。以安装 Python 应用为例,除了获取应用的二进制文件,系统还需安装 Python 解析器、满足应用的外部依赖,并配置环境变量等。考虑到不同操作系统的因素,比如跨平台兼容性,这些步骤使得配置运行时环境(runtime)变得复杂。

由于环境配置的复杂性,一个可行的解决方案是实现软件与其运行时环境一起安装。虚拟机的快照(snapshot)提供了这样一种解决方案,允许用户还原软件的原始环境。然而,虚拟机的缺点在于每个虚拟机都包含完整的操作系统,导致资源占用较大、启动较慢等问题。

为了克服虚拟机的这些不足,Linux 发展出了另一种虚拟化技术:容器。容器技术将应用程序及其所有运行时依赖封装在一个独立的容器中,实现了轻量级、快速启动、资源占用少的优势。

容器

容器是一个标准化的单元,是一个轻量级、可移植的软件打包技术。它将软件代码及其相关依赖打包,使应用程序可以在任何计算介质中运行。简单来讲,容器就像一个标准化的盒子,能够装很多不同类型的东西,并且装完后能够塞进很多不同类型的柜子里。

  • 容器的特性:
    • 封装性:将软件打包成标准化单元以进行开发、迁移和部署
    • 隔离性:计算、存储、网络等资源彼此隔离(运行环境的隔离)
    • 高效性:轻量、快速启停、快速部署与迁移
    • 职责分工明确:开发专心写代码,运维专注基础环境配置
    • 镜像增量分发
  • 容器使用难题
    • 统一平台:在k8s之前,并没有一个统一的平台去操作容器,基本上所有操作都由开发人员/运维人员完成
    • 易用性

虚拟机 VS 容器

img

  • 容器没有虚拟化层,所以容器通常称为轻量级虚拟化技术,然而他真正的实现是通过namespace来进行进程隔离,进入到容器内该Namespace下挂载的目录、文件或者网络。究其根源,容器只是宿主机器上的一个特殊的进程
  • 虚拟化虚拟的是硬件,容器虚拟的是操作系统,所以更高效

image-20220309103834583

docker使用

  1. 通常在项目工程里使用Makefile文件,指定一系列的操作步骤;
  2. 通过docker build构建成一个可运行镜像;
  3. 使用docker push推送到指定的镜像仓库;
  4. 在任何可以连接镜像仓库的地方,拉取镜像并且运行:docker pull docker run

docker原理

docker的运行时是runc,runc采用的技术:基于Linux内核的Cgroup(资源限制),Namespace(隔离),以及UnionFS(文件系统)等技术,对进程进行封装隔离

  1. Namespace:对进程进行隔离,解决了进程之间的可见性问题。

    • 系统可以为进程分配不同的 Namespace,使进程能够拥有独立的运行环境,并且能够拥有网络与宿主机器进行通信。而container本质上是宿主机器上的一个进程;
    • 一个进程的Namespace信息以一个文件的形式存在于宿主机器上。

    image-20220419082127765

  2. Cgroup:对进程组资源进行限制及监控。(全称为control Group)

    • 保证不同的进程组资源独立分配、进程组彼此隔离,即不同的进程组下的进程互不干扰 。这些资源包括:主机名、用户权限、文件系统、网络、进程号、进程间通信。

    • 对进程的资源控制通常存在于/sys/fs/cgroup,每一个进程都会在这里存在一个单独的文件夹,文件夹下会存在各种资源的使用限制,可以修改里面的值进行相应的资源控制。

      [root@master cpu]# cd /sys/fs/cgroup
      # 创建文件夹
      [root@master cpu]# mkdir lomtom
      # 进入文件夹
      [root@master cpu]# cd lomtom
      [root@master lomtom]# ls
      cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
      cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
      ...
      
      # 另起一个窗口,运行一个消耗cpu的程序(用写几个for循环即可)
      [root@master root]# cat > main.go << EOF
      package main
      
      func main() {
      	go func() {
      		for  {
      			
      		}
      	}()
      	go func() {
      		for  {
      			
      		}
      	}()
      	for  {
      		
      	}
      }
      EOF
      [root@master root]# go build ./main.go
      [root@master root]#./main
      
      
      # 查询该进程的进程号
      [root@master lomtom]# ps -ef|grep main|grep -v grep|awk '{print $2}'
      # 将进程号写入该文件夹下的cgroup.procs 
      [root@master lomtom]# echo 26630 > cgroup.procs 
      [root@master lomtom]# cat cgroup.procs 
      26630
      
      # 限制cpu使用率上限(10%)
      [root@master lomtom]# echo 10000 > cpu.cfs_quota_us 
      [root@master lomtom]# cat cpu.cfs_quota_us 
      10000
      

      限制前cpu占有率

      image-20220419100021909

      限制后cpu占有率

      image-20220419100236239

  3. 除此之外,还会对进程(也就是容器)切换根目录。并且docker镜像是以一层一层的结构构成,rootfs在原有的基础镜像(例如centos)增加自己所要执行的操作即在可读写层添加相应的层。

    img

    并且,下一步操作如果与之前的操作冲突,将会覆盖之前的。例如我先创建一个文件config,然后我又重新写入config文件,那么最终保存的就是最后一次执行的config文件。

    echo 你好 > config
    echo 你快乐吗 > config
    

    最终在config中只有“你快乐吗”,这在日常的使用中也是这样的,所以这很好理解。

    对于docker镜像来说,Dockerfile的每一步操作都会生成一层可读写层,并且后来的操作也就是上层会覆盖下层,可以通过docker history [images ID],来查看镜像的层级结构。

    [root@master ~]# docker images
    REPOSITORY                                                        TAG                 IMAGE ID            CREATED             SIZE
    rancher/mirrored-flannelcni-flannel                               v0.17.0             9247abf08677        7 weeks ago         59.8MB
    
    [root@master ~]# docker history 9247abf08677
    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    9247abf08677        7 weeks ago         ENTRYPOINT ["/opt/bin/flanneld"]                0B                  buildkit.dockerfile.v0
    <missing>           7 weeks ago         RUN /bin/sh -c /iptables-wrapper-installer.s…   1.93kB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         COPY dist/iptables-wrapper-installer.sh / # …   7.66kB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         COPY dist/mk-docker-opts.sh /opt/bin/ # buil…   2.14kB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         COPY dist/flanneld-amd64 /opt/bin/flanneld #…   39.8MB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         RUN /bin/sh -c apk add wireguard-tools --no-2.63MB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         RUN /bin/sh -c apk add --no-cache iproute2 n…   11.8MB              buildkit.dockerfile.v0
    <missing>           7 weeks ago         ENV FLANNEL_ARCH=amd64                          0B                  buildkit.dockerfile.v0
    <missing>           5 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
    <missing>           5 months ago        /bin/sh -c #(nop) ADD file:5a707b9d6cb5fff53…   5.62MB
    

    这样的一个好处是,当我在进行容器的分发时,发现底层层级没有发生改变时,那么每次发布只会发布改变的部分。

容器与k8s

CRI接口是kubelet调用容器运行时的grpc接口,也就是k8s定义的容器运行时接口。

而dockershim、cri-containerd都是CRI接口的实现。

而以下都是当下比较流行的运行时的实现。

  1. runc

容器公用主机资源,采用Ns,Cgroup等技术,本质上是一个进程,更轻量。

  1. kata

容器有独立的内核,裁剪了一些不必要的设备、总线、驱动、文件系统等,本质上是一个轻量化的虚拟机,更安全。

lomtom

标题:k8s(一)走进docker

作者:lomtom

链接:https://lomtom.cn/24bc125b