手把手教你撸一个Docker

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 手把手教你撸一个Docker

我之前写过一篇文章,寻根学习法,最近又想着改个溯源学习法更好。


经常自学的人肯定有这个感受,有时候,无论我们怎么努力,有些东西好像都学不会,似乎很多框架、概念之后都藏着让我们无法理解的很多谜底。


Ruby on Rails 的,感觉 Rails 框架里一堆魔法;使用 Spring Boot 也一样。


今天无意中在一个公众号的留言里看到这样一则留言:


屏幕截图 2023-09-06 101810.png


曾经也是一度和他一样迷茫,Kubernetes 不是一个指引我们的舵手,而是一个让我们迷航的蜘蛛网。



屏幕截图 2023-09-06 101832.png


后来我想明白了,我之所以搞不明白,是因为我没有完全的弄清楚 Kubernetes 底层的细节。


那么要学好 Kubernetes, 我们需要掌握哪些底层的知识呢?


我认为有以下两个点是重中之重,也就是今天的这篇文章要讲的:


  • Linux 命名空间
  • Linux Control Group


只有弄明白这两点,才能真正理解容器和网络。


而在Linux命名空间里,Net namespace (网络命名空间)更是重中之重,我们在后续的分享里会继续专门做一个这样的分享。


这就是溯源学习法的一种应用场景,我们去追寻他背后的背后的背后的真理。



1. 从软件工程的角度去思考Linux 内核设计  


试想一下,我们要设计一个操作系统。


这个操作系统的功能需求,大致是这样的:

  • 可以接受用户键盘输入
  • 可以通过屏幕显示程序的输出
  • 可以让多用户运行不同的程序
  • 。。。


大家都知道电脑有很多物理接口,用户可以通过这些接口,接入不同的设备,这些设备支持不同的协议和标准。


作为工作多年的程序员,我们自然而然的希望把硬件接口模块化,然后在这个之上再做一些其他的工作。


接下来才是今天的主角


让我们从安全的角度,去思考一个问题:如何让多个用户运行自己程序时互不干扰?


我们自然会想到首先把我们的所谓的操作系统的内核与用户的应用程序隔离开来。


因此,下面的这张图就不难理解了:


屏幕截图 2023-09-06 101850.png


最下面一层就是硬件驱动层,再往上是Linux的内核,真正的去实现了底层的调用,用户进程的管理等等功能。


但是如何实现把让不同的用户之间运行的程序互不影响呢


这就不得不提 Linux 内核设计的精妙之处, 命名空间(Namespace)和控制组(Control Group)


Linux 通过命名空间实现了多用户之间的隔离,这样大家貌似在用同样的一台电脑,貌似有自己的磁盘空间,等等,但是很多时候这些都是Linux 内核给大家的一个假象。


与上面那张Linux 系统架构的图相比,我更喜欢下面这张。

屏幕截图 2023-09-06 101910.png



Linux内核分为8个命名空间


  • CGroup 命名空间
  • IPC 命名空间

  • PID 命名空间
  • MNT 命名空间
  • Network 命名空间
  • USER 命名空间
  • UTS命名空间
  • TIme 命名空间

下面我们详细说说这几个命名空间:


如果你不想了解他们的细节,那么记住一句话就可以:Linux 通过命名空间的机制,用户可以独立操作这些所谓的资源,而互不影响(划重点)


CGroup 命名空间

    CGroup是用来对资源进行隔离的,我们后面还会专门提到的。所谓的资源包括计算机的方方面面。比如说CPU、内存、磁盘、网络等等物理的,或者是操作系统定义的资源。


屏幕截图 2023-09-06 101923.png


IPC 命名空间

     IPC 大家都知道,是 Linux 用来进程间通信的。IPC namespace 使得 相同的标识符在两个 namespace 代表不同的消息队列,因此两个namespace 中的进程不能通过 IPC 来通信。


PID 命名空间

     PID 是Linux 中的进程的ID。Linux 通过隔离进程号,这样不同namespace 的进程可以使用相同的进程号。


MNT 命名空间

     隔离文件挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts里。在一个 命名空间里挂载、卸载的动作不会影响到其他命名空间。

 

    将一个进程放到一个特定的目录执行mnt namespace 允许不同 namespace 的进程看到的文件结构不同。


Net 命名空间

     Net 命名空间隔离网络资源。每个 namespace 都有自己的网络设备、IP、路由表、/proc/net 目录、端口号等。


     网络隔离可以保证独立使用网络资源,也就是我们可以给这个虚拟的网络设备设置IP地址等等,而且不会影响主机,或者其他Net 命名空间


USER 命名空间

     通过USER 命名空间,每个容器可以有不同的 user group id, 也就是说可以在容器内部用容器内部的用户执行程序而 Host 上的用户


    是不是有点拗口?其实还是那句话,Linux通过命名空间给用户一种你拥有电脑全部资源的错觉


 

UTS命名空间

     UTS(“UNIX Time-sharing System”) 也就是UNIX分时系统命名空间。


      UTS 命名空间允许每个容器拥有独立的主机名和域名。这有个好处,不然的话一台主机只能使用一个主机名。


      如果不能理解,可以想想我上面写的Linux通过命名空间给用户一种你拥有电脑全部资源的错觉


      这只不过是其中的一个方面而已。


嗯,基本上我们需要理解Kubernetes网络,或者最直接的,理解容器,遇到也就是这几个命名空间。



2. Syscall -- 用户与 Linux 内核交互的唯一途径  


让我们再回顾一下Linux 系统架构:


屏幕截图 2023-09-06 101939.png

要撸一个自己的Docker,我们需要了解这几个Syscall函数,或者也可以理解为命令:


  • clone创建新进程,假如参数里CLONE_NEW* 标志的话,会创建一个新的命名空间
  • setns:    允许把存在的进程加入已经存在的命名空间
  • unshare切换进程的命名空间
  • ioctl显示命名空间的相关信息
  • pivot_root改变当前进程的 MNT 命名空间



3. rootfs  


        rootfs 代表一个 Docker 容器在启动时(而非运行后)其内部进程可见的文件系统视角,或者叫 Docker 容器的根目录。

      先来看一下,Linux 操作系统内核启动时,内核会先挂载一个只读的 rootfs,当系统检测其完整性之后,决定是否将其切换到读写模式。

       Docker 沿用这种思想,不同的是,挂载rootfs 完毕之后,没有像 Linux 那样将容器的文件系统切换到读写模式,而是利用联合挂载技术,在这个只读的 rootfs 上挂载一个读写的文件系统,挂载后该读写文件系统空空如也。


        Docker 文件系统简单理解为:只读的 rootfs + 可读写的文件系统。


    一个最常见的 rootfs,或者说容器镜像,会包括如下所示的一些目录和文件,比如 /bin,/etc,/proc 等等。


      Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。


     用到的技术就是联合文件系统(Union File System) Union File System 也叫 UnionFS ,最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下。





4. 准备了这么久,让我们撸一Docker


package main
import (
 "fmt"
 "os"
 "os/exec"
 "syscall"
)
func main() {
 switch os.Args[1] {
 case "run":
  parent()
 case "child":
  child()
 default:
  panic("what should I do")
 }
}
func parent() {
  // func Command(name string, arg ...string) *Cmd
  // /proc/self/exe 它代表当前程序
 cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]…)…)
  // SysProcAttr holds optional, operating system-specific attributes.
  // Adding namespaces
  cmd.SysProcAttr = &syscall.SysProcAttr{
  Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
 }
 cmd.Stdin = os.Stdin
 cmd.Stdout = os.Stdout
 cmd.Stderr = os.Stderr
 if err := cmd.Run(); err != nil {
  fmt.Println("ERROR", err)
  os.Exit(1)
 }
}
func child() {
 must(syscall.Mount("rootfs", "rootfs", "", syscall.MS_BIND, ""))
 must(os.MkdirAll("rootfs/oldrootfs", 0700))
 // swap into a root filesystem
 must(syscall.PivotRoot("rootfs", "rootfs/oldrootfs"))
 must(os.Chdir("/"))
 cmd := exec.Command(os.Args[2], os.Args[3:]...)
 cmd.Stdin = os.Stdin
 cmd.Stdout = os.Stdout
 cmd.Stderr = os.Stderr
 if err := cmd.Run(); err != nil {
  fmt.Println("ERROR", err)
  os.Exit(1)
 }
}
func must(err error) {
 if err != nil {
  panic(err)
 }
}





5. 结尾


       我们回顾一下,容器其实主要使用的就是控制组 CGroup和命名空间Namespace 这两个来自Linux 系统的特性。


      命名空间主要是做资源隔离。通过命名空间,用户可以无所顾忌的操作系统的所有资源,因为这些资源都是虚拟出来的,而且都是独立的。


      控制组用来限制用户可以使用的资源。


      操作系统就像一个上帝视角,不仅为每个用户提供了资源的隔离,还从系统管理的角度为资源的使用提供了许多限制,这样得以让我们可以实现容器。


      最后,提一个问题,大家觉得Windows 系统的Docker 会怎么实现?

知道的可以留言哦。


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
Kubernetes API Docker
(一)零基础小白都能懂的超全Docker入门教程之开篇
(一)零基础小白都能懂的超全Docker入门教程之开篇
|
12月前
|
关系型数据库 MySQL 数据库
【Docker】手把手教你搭建好玩的docker项目合集
【Docker】手把手教你搭建好玩的docker项目合集
|
4月前
|
机器学习/深度学习 数据挖掘 Docker
《Docker 简易速速上手小册》第1章 Docker 基础入门(2024 最新版)
《Docker 简易速速上手小册》第1章 Docker 基础入门(2024 最新版)
70 2
|
4月前
|
应用服务中间件 Shell nginx
手把手教你玩转docker(二)
手把手教你玩转docker(二)
|
4月前
|
存储 应用服务中间件 nginx
手把手教你玩转docker(一)
手把手教你玩转docker
|
4月前
|
Kubernetes 安全 Docker
太厉害了,终于有人把K8S+Docker实战给讲的这么透彻
太厉害了,终于有人把K8S+Docker实战给讲的这么透彻
|
12月前
|
Cloud Native Docker Windows
Docker 实战教程之从入门到提高 (八)
Docker 实战教程之从入门到提高 (八)
|
Kubernetes API Docker
Docker入门之开篇
相信很多人和我开始时一样,听过Docker,听过Kubernetes,听过容器也听过集群,貌似很多高大上的技术都耳熟能详,但自己其实却并不了解,甚至不知道他们是什么?能干什么? 最初,我以为Docker以及k8s等技术只能用在大数据以及云等的场景,甚至学习上也需要专门的环境,但是,我发现我错了,而且错的很彻底,不知道你是否也这么认为过,觉得这些技术在平时的开发中根本用不到。如果是这样的,那我现在就告诉你,其实并不是这样的,下面就跟我一起来了解一下Docker吧。
Docker入门之开篇
|
12月前
|
存储 Kubernetes Ubuntu
Docker 实战教程之从入门到提高(一)
Docker 实战教程之从入门到提高(一)
|
存储 Java 虚拟化
手把手教你入门 Docker
Docker 是一种容器化平台,它可以让开发人员和系统管理员轻松地创建、部署和运行应用程序。Docker 可以将应用程序及其依赖项打包成一个独立的容器,该容器可以在任何环境中运行,而不需要修改应用程序或其依赖项。Docker 的目标是帮助开发人员和系统管理员更快、更可靠地构建、部署和运行应用程序。
518 0
手把手教你入门 Docker