说透 Docker:基础

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 说透 Docker:基础

容器化应用


什么是容器化应用


containerized applications 指容器化的应用,我们常常说使用镜像打包应用程序,使用 Docker 发布、部署应用程序,那么当你的应用成功在 Docker 上运行时,称这个应用是 containerized applications。


应用怎么打包


容器化应用的最主要特征是使用镜像打包应用的运行环境以及应用程序,可以通过 Docker 启动这个镜像,进而将 应用程序启动起来。


将一个应用程序打包为镜像,大约分为以下过程:

  • 编写 Dockerfile 文件 -- 定义构建镜像的流程
  • 选择一个基础镜像(操作系统) -- 操作系统
  • 安装应用的需要的环境 -- 运行环境
  • 复制程序文件 -- 应用程序
  • 启动 Dockerfile -- 生成镜像


操作系统运行环境Web程序(C#)Ubuntu 18.04.NET Core Runtime3.1安装运行环境操作系统运行环境Web程序(C#)


Docker 镜像组成


以 .NET Core(C#) 程序为例,一个 Docker 镜像的层次如下图所示:

微信图片_20220505181035.png

在 Docker 镜像中,操作系统是高度精简的,可能只有一个精简的 Shell,甚至没有 Shell。而且镜像中的操作系统还不包含内核,容器都是共享所在的宿主机的内核。所以有时会说容器仅包含必要的操作系统(通常只有操作系统文件和文件系统对象),容器中查看到的 Linux 内核版本与宿主机一致。


Docker 镜像的是由一系统文件组成的。

微信图片_20220505181040.png


联合文件系统


Linux 有名为 Unionfs 的文件系统服务,可以将不同文件夹中的文件联合到一个文件夹中。Unionfs 有称为分支的概念,一个分支包含了多个目录和文件,多个分支可以挂载在一起,在挂载时,可以指定一个分支优先级大于另一个分支,这样当两个分支都包含相同的文件名时,一个分支会优先于另一个分支,在合并的目录中,会看到高优先级分支的文件。

微信图片_20220505181047.jpg


Docker 中,层层组成镜像的技术也是联合文件系统,Union File System。Docker 镜像中的操作系统是根文件系统,在上一小节的图片中,可以看到有 bin、boot 等目录。我们都知道,Docker 镜像是由多层文件组成的,在上面的示例图片中有三层组成:根文件系统、环境依赖包、应用程序文件。当镜像层生成后,便不能被修改,如果再进行操作,则会在原来的基础上生成新的镜像层,层层联合,最终生成镜像。当然生成的镜像可能会因为层数太多或者操作过多,导致出现大量冗余,镜像臃肿。


Docker 的镜像分层是受 Linux Unionfs 启发而开发的,Docker 支持多种文件联合系统,如 AUFS、OverlayFS、VFS 等。


Docker 在不同系统中可以选择的联合文件系统:


Linux发行版 推荐的存储驱动程序 替代驱动程序
Ubuntu overlay2 overlaydevicemapper, aufs, zfs,vfs
Debian overlay2 overlay, devicemapper, aufs,vfs
CentOS overlay2 overlay, devicemapper, zfs,vfs

提示

Docker Desktop for Mac 和 Docker Desktop for Windows 不支持修改存储驱动程序,只能使用默认存储驱动程序。


Linux 内核


既然 Docker 容器需要与 Linux 内核结合才能使用,那么我们看一下 Linux 内核的功能,稍微了解一下 Linux 内核在支撑 Docker 容器运作中起到什么作用。


Linux 内核主要包含以下功能:

  • 内存管理:追踪记录有多少内存存储了什么以及存储在哪里;
  • 进程管理:确定哪些进程可以使用中央处理器(CPU)、何时使用以及持续多长时间;
  • 设备驱动程序:充当硬件与进程之间的调解程序/解释程序;
  • 系统调用和安全防护:接受程序请求调用系统服务;
  • 文件系统:操作系统中负责管理持久数据的子系统,在 Linux 中,一切皆文件。


Linux 层次结构如下:

微信图片_20220505181214.png


Docker 容器中包含了一个操作系统,包含简单的 shell 或者不包含,其层次结构如图所示:


微信图片_20220505181218.png


Docker 结构

本节将了解 Docker 的组成部件和结构。


Docker 服务与客户端


Docker 由 Service 和 Client 两部分组成,在服务器上可以不安装 Docker Client,可以通过 Http Api 等方式与 Docker Servie 通讯。


在安装了 Docker 的主机上执行命令 docker version 查看版本号。


Client: Docker Engine - Community
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        f0df350
 Built:             Wed Jun  2 11:58:10 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true
Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       b0f5bc3
  Built:            Wed Jun  2 11:56:35 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.6
  GitCommit:        d71fcd7d8303cbf684402823e425e9dd2e99285d
 runc:
  Version:          1.0.0-rc95
  GitCommit:        b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0


Docker 客户端

要想跟 Docker Server 通讯,可以使用 Restful API、UNIX 套接字或网络接口(Socket)。Docker 官方的客户端是一个二进制命令行程序,使用 Go 语言编写,我们也可以使用 C#、Java 等语言写一个类似的程序,Docker 客户端不需要安装到 Docker Server 所在的主机,Client 跟 Server 可以远程通讯。


Docker 的客户端是许多 Docker 用户与 Docker 交互的主要方式,当我们使用 docker run 之类的命令时,客户端会将这些命令发送到 Docker Server,由 Docker Server 解析并执行命令。


Docker for Linux 中最为常见的同主机通讯方式是 Unix 域套接字。很多软件都支持使用域套接字与 Docker 通讯,例如 CI/CD 软件 Jenkins,使用域套接字连接 Docker,能够利用 Docker 启动容器构建应用程序以及使用 Docker 来做一些不可描述的事情。


微信图片_20220505181303.png


容器运行时


容器运行时是提供运行环境并启动容器的软件,我们最常听说的是 Docker,此外还有 containerdCRI-O 等。可以毫不夸张的说,整个 Kubernetes 建立在容器之上。


默认情况下,Kubernetes 使用 容器运行时接口(Container Runtime Interface,CRI) 来与服务器中容器运行时交互。所以 Kubernetes 支持多种容器软件,但只能使用一种容器运行时进行工作,在有多个容器运行时的情况下,我们需要指定使用何种运行时,如果你不指定运行时,则 kubeadm 会自动尝试检测到系统上已经安装的运行时, 方法是扫描一组众所周知的 Unix 域套接字。


Linux 是多进程操作系统,为了让多个系统中的多个进程能够进行高效的通讯,出现和很多方法,其中一种是域套接字(Unix domain socket),只能用于在同一计算机中的进程间通讯,但是其效率高于网络套接字(socket),域套接字不需要经过网络协议处理,通过系统调用将数据从一个进程复制到另一个进程中。


域套接字使用一个 .sock 文件进行通讯,常见的容器软件其对应域套接字如下:


运行时 域套接字
Docker /var/run/dockershim.sock
containerd /run/containerd/containerd.sock
CRI-O /var/run/crio/crio.sock

 

同一主机下常见进程通讯方式有 共享内存、消息队列、管道通讯(共享文件)。

Unux 域套接字是套接字和管道之间的混合物。 在 Linux 中,有很多进程,为了让多个进程能够进行通讯,出现和很多方法,其中一种是套接字(socket)。一般的 socket 都是基于 TCP/IP 的,称为网络套接字,可以实现跨主机进程通讯。在 Linux 中有一种套接字,名为域套接字,只能用于在同一计算机中的进程间通讯,但是其效率高于网络套接字。域套接字使用一个 .sock 文件进行通讯。


当计算机中有多种容器运行时,Kubernetes 默认优先使用 Docker。

如果你想了解 CRI ,请点击:

https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md


Docker 引擎


Docker 引擎也可以说是 Docker Server,它由 Docker 守护进程(Docker daemon)、containerd 以及 runc 组成。


当使用 Docker client 输入命令时,命令会被发送到 Docker daemon ,daemon 会侦听请求并管理 Docker 对象,daemon 可以管理 镜像、容器、网络和存储卷等。

下面这个图是新 Docker 版本的结构组成。


微信图片_20220505181311.png


Docker 引擎变化


Docker 首次发布时,Docker 引擎由两个核心组件构成:LXC 和 Docker daemon,这也是很多文章中称 Docker 是基于 LXC 的原因,旧版本的 Docker 利用了 LXC、cgroups、Linux 内核编写。接下来我们了解一下 LXC 。


LXC (Linux Container)是 Linux 提供的一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,它是操作系统层面上的虚拟化技术。LXC 提供了对诸如命名空间(namespace) 和控制组(cgroups) 等基础工具的操作能力,它们是基于 Linux 内核的容器虚拟化技术。我们不需要深入了解这个东西。


微信图片_20220505181321.png

Docker 一开始是使用 LXC 做的,LXC 是一个很牛逼的开源项目,但是随着 Docker 的成熟,Docker 开始抛弃 LXC,自己动手手撕容器引擎。


为什么 Docker 要抛弃 LXC 呢?首先,LXC 是基于 Linux 的。这对于一个立志于跨平台的 Docker 来说是个问题,离开 LXC,怎么在 MAC、Windows 下运行?其次,如此核心的组件依赖于外部工具,这会给项目带来巨大风险,甚至影响其发展。


 

哈哈哈。。。其实笔者觉得不支持 Windows 也罢。。。


Docker 引擎的架构


下面是一张 Docker 的架构图。

微信图片_20220505181327.png


Docker client 和 Docker daemon 在前面已经介绍过了,接下来介绍其他组件。


containerd

containerd 是一个开源容器引擎,是从 Docker 开源出去的。之前有新闻说 Kubernetes 不再支持 Docker,只支持 containerd,很多人以为 Docker 不行了。


一开始 Docker 是一个 “大单体”,随着 Docker 的成长,Docker 开始进行模块化,Docker 中的许多模块都是可替换的,如 Docker 网络。支持容器运行的核心代码自然也抽出来,单独做一个模块,便是 containerd。Kubernetes 不再支持 Docker,只不过是降低依赖程度,减少对其他模块的依赖,只集中在 containerd 上。当我们安装 Docker 时,自然会包含 containerd。如果我们不需要 Docker 太多组件,那么我们可以仅仅安装 containerd,由 Kubernetes 调度,只不过我们不能使用 Docker client 了。因此可以说,Kubernetes 不再支持 Docker,并不代表会排斥 Docker。


containerd 的主要任务是容器的生命周期管理,如启动容器、暂停容器、停止容器等。containerd 位于 daemon 和 runc 所在的 OCI 层之间。


shim

shim 它的作用非常单一,那就是实现 CRI 规定的每个接口,然后把具体的 CRI 请求“翻译”成对后端容器项目的请求或者操作。


这里要区别一下,dockershim 和 containerd-shim,dockershim 是一个临时性的方案,dockershim 会在 Kubernetes v1.24中 删除(2022年),这也是 Kubernetes 不再支持 Docker 的另一组件。

提示

CRI 即 Container Runtime Interface,容器运行时接口,容器引擎要支持 Kubernetes ,需要实现 CRI 接口,例如 runc 、crun 两种是常见的 Container Runtime。


shim 是容器进程的父进程,shim 的生命周期跟容器一样长,shim 是一个轻量级的守护进程,它与容器进程紧密相关,但是 shim 与容器中的进程完全分离。shim 可以将容器的 stdin、stdout、srderr 流重定向到日志中,我们使用 docker logs 即可看到容器输出到控制台的流。


关于 shim,我们就先了解到这里,后面会继续讲解一个示例。


runc

runc 实质上是一个轻量级的、针对 Libcontainer 进行了包装的命令行交互工具,runc 生来只有一个作用——创建容器,即 runc 是一个由于运行容器的命令行工具。

提示

Libcontainer 取代了早期 Docker 架构中的 LXC。

如果主机安装了 Docker,我们可以使用 runc --help 来查看使用说明。我们可以这样来理解 runc,runc 是在隔离环境生成新的进程的工具,在这个隔离环境中有一个专用的根文件系统(ubuntu、centos等)和新的进程树,这个进程树的根进程 PID=1

博客推荐

笔者在查阅资料时,发现了这个大佬的博客,在这个大佬的博客中学会了很多东西。

博客推荐:https://iximiuz.com/en/

在后面的节中,我们将继续了解 Docker 中的网络和存储,并开始探究与 Kubernetes 相关的知识点。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
应用服务中间件 Linux Shell
Docker基础 docker创建centos7容器然后安装配置Nginx,并在浏览器访问
docker 在容器里下载配置nginx,并可在浏览器上访问的方法, 并将容器转为镜像,或将容器转为文件,再将文件转为镜像的两种方式,创建容器时绑定端口的方法,
Docker基础 docker创建centos7容器然后安装配置Nginx,并在浏览器访问
|
运维 安全 持续交付
利用ansible的角色快速批量一键部署基础docker环境
利用ansible的角色快速批量一键部署基础docker环境
447 0
利用ansible的角色快速批量一键部署基础docker环境
|
存储 缓存 安全
Docker自测题(基础、容器、镜像等)(上)
什么是 Docker ? Go 语言开发,容器虚拟化技术,C/S 架构,具有隔离、快速、轻便的特点。
|
NoSQL Redis 数据库
【Docker 基础教程】容器数据持久化(三)------ Redis的基础配置
【Docker 基础教程】容器数据持久化(三)------ Redis的基础配置
【Docker 基础教程】容器数据持久化(三)------ Redis的基础配置
|
关系型数据库 MySQL Shell
【Docker 基础教程】容器数据持久化(二) ------ Mysql的基础配置
【Docker 基础教程】容器数据持久化(二) ------ Mysql的基础配置
【Docker 基础教程】容器数据持久化(二) ------ Mysql的基础配置
|
存储 监控 Linux
Docker自测题(基础、容器、镜像等)(下)
什么是 Docker ? Go 语言开发,容器虚拟化技术,C/S 架构,具有隔离、快速、轻便的特点。
|
Shell Linux 应用服务中间件
Docker 基础 常用的其它命令
容器常用命令,后台启动容器,查看容器日志,查看容器进程,查看容器或镜像元数据,进入当前正在运行的容器,从容器内拷贝文件到主机,docker commit命令,将容器转为镜像等命令及参数详解
Docker 基础 常用的其它命令
|
Linux Shell Docker
Docker基础 容器基础命令操作与详解,学docker必须要会的容器命令
这篇文章写了我们学习docker必须要会的基础容器命令,文章会给出这些命令的详细解释和每个参数的使用方法,帮助大家更快的掌握这些命令
Docker基础 容器基础命令操作与详解,学docker必须要会的容器命令
|
关系型数据库 MySQL Go
Docker基础 常用命令、docker命令、镜像命令
Docker 常用命令、docker命令、镜像命令、docker帮助文档地址,镜像基础命令详解,参数解释,帮助方法
Docker基础 常用命令、docker命令、镜像命令
|
关系型数据库 MySQL Linux
docker基础 docker创建centos容器,然后在centos容器里下载MySQL并本地连接MySQL
docker基础,使用docker创建一个centos容器,并在该容器下载安装MySQL,并设定端口号,然后在本地连接MySQL
docker基础 docker创建centos容器,然后在centos容器里下载MySQL并本地连接MySQL