Files
security-book/00.基础阶段/02.Linux基础/15.Docker/01.Docker介绍与安装.md
2025-08-27 14:13:17 +08:00

52 KiB
Raw Blame History

01.Docker介绍与安装

01.Docker介绍与安装

1. Docker是什么

Docker是一个在2013年开源的应用程序并且是一个基于go语言编写的PAAS服务。

Docker最早采用LXC技术之后改为自己研发并开源的runc技术运行容器。

Docker相比虚拟机的交付速度更快资源消耗更低Docker采用客户端、服务端架构使用远程api来管理和创建Docker容器。

Docker的三大理念是build构建、ship运输、run运行

Docker通过namespace、cgroup等技术来提供容器的资源隔离与安全保障。

2. Docker与虚拟机之间的对比

img

虚拟化 容器
隔离性强有独立的GUEST OS 共享内核和OS隔离性弱!
虚拟化性能差(>15%) 计算/存储无损耗无Guest OS内存开销(~200M)
虚拟机镜像庞大(十几G~几十G), 且实例化时不能共享 Docker容器镜象200~300M且公共基础镜象实例化时可以共享
虚拟机镜象缺乏统一标准 Docker提供了容器应用镜象事实标准OCI推动进一 步标准化
虚拟机创建慢(>2分钟) 秒级创建(<10s)相当于建立索引
虚拟机启动慢(>30s) 读文件逐个加载 秒级(<1s,不含应用本身启动)
资源虚拟化粒度低单机10~100虚拟机 单机支持1000+容器密度很高,适合大规模的部署
  • 资源利用率更高:一台物理机可以运行数百个容器,但一般只能运行数十个虚拟机
  • 开销更小:不需要启动单独的虚拟机占用硬件资源
  • 启动速度更快:可以在数秒内完成启动

3. Docker的组成

官网:https://docs.docker.com/get-started/overview/

Docker主机 host一个物理机或者虚拟机用于运行docker服务进程和容器

Docker服务端 ServerDocker守护进程运行docker容器

Docker客户端 client客户端使用docker命令或其他工具调用docker api

Docker仓库 registry保存镜像的仓库类似于git或svn这样的版本控制器

Docker镜像 images镜像可以理解为创建实例使用的模板

Docker容器 container容器是从镜像生成对外提供服务的一个或一组服务

image-20210531152229098

image-20210531152329941

4. Docker服务端软件选择

Docker CECommunity Edition社区版和 Docker EEEnterprise Edition企业版是 Docker 产品的两个主要版本,它们之间的主要区别在于目标用户、功能集、支持和维护等方面:

  1. 目标用户
    • Docker CE:面向个人开发者、小团队以及技术爱好者,主要用于开发和测试环境。
    • Docker EE:面向大型企业和组织,提供企业级的功能和支持。
  2. 功能集
    • Docker CE:提供基本的容器化功能,包括构建、运行和共享容器。
    • Docker EE:除了包含 CE 版本的所有功能外,还提供了额外的企业级特性,如增强的安全、管理、可扩展性和集成性。
  3. 支持和维护
    • Docker CE:社区支持,适合自我解决问题的开发者。
    • Docker EE:提供商业支持和专业服务,适合需要稳定运行环境的企业。
  4. 安全性
    • Docker CE:安全性相对较低,适合非生产环境。
    • Docker EE:提供更高级的安全特性,如镜像扫描、安全策略和合规性报告。
  5. 管理
    • Docker CE:通常不需要复杂的管理工具。
    • Docker EE:提供 Docker Universal Control Plane (UCP) 和 Docker Trusted Registry (DTR) 等管理工具,帮助企业更有效地管理容器环境。
  6. 成本
    • Docker CE:免费。
    • Docker EE:需要购买许可证。
  7. 更新和生命周期
    • Docker CE:更新频繁,可能包含实验性功能,生命周期较短。
    • Docker EE:更新周期更稳定,更注重稳定性和兼容性,生命周期较长。

5. Docker安装

  • 安装docker-ce
[root@localhost ~]# yum install -y yum-utils
[root@localhost ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@localhost ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
[root@localhost ~]# yum install docker-ce -y
  • 启动docker
[root@localhost ~]# systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
[root@localhost ~]# systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
     Active: active (running) since Sun 2025-03-23 23:36:27 CST; 10s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 29081 (dockerd)
      Tasks: 10
     Memory: 25.7M
        CPU: 141ms
     CGroup: /system.slice/docker.service
             └─29081 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

5.1 镜像加速配置

由于Docker Image仓库在国外目前从23年底开始国内陆续访问不到了所以要通过一些镜像加速器才能获取到镜像

[root@localhost ~]# docker pull nginx
Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded

# 无法从docker官方镜像仓库docker.io获取镜像

使用国内镜像加速器(不稳定,会经常变化)

[root@localhost ~]# mkdir -p /etc/docker
[root@localhost ~]# vim /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io"
  ]
}

# 重启容器服务
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl restart docker

# 可选加速地址(不一定有用,随时会跑路):
1、https://docker.m.daocloud.io 
2、https://docker.1panelproxy.com
3、https://atomhub.openatom.cn 
4、https://docker.1panel.live 
5、https://dockerhub.jobcher.com 
6、https://hub.rat.dev 
7、https://docker.registry.cyou 
8、https://docker.awsl9527.cn 
9、https://do.nark.eu.org/ 
10、https://docker.ckyl.me 
11、https://hub.uuuadc.top
12、https://docker.chenby.cn
13、https://docker.ckyl.me


# 实在不行的话可以在拉去的时候手动加上镜像加速器因为有时候写到daemon.json中也有可能访问不到
[root@localhost ~]# docker pull nginx
[root@localhost ~]# docker pull docker.m.daocloud.io/nginx
[root@localhost ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    53a18edff809   6 weeks ago   192MB

5.2 快速开始

[root@localhost ~]# docker pull nginx
[root@localhost ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
nginx        latest    d1a364dc548d   5 days ago   133MB
[root@localhost ~]# docker run -d -p 80:80 nginx
e617ca1db9a5d242e6b4145b9cd3dff9f7955c6ab1bf160f13fb6bec081a29e4
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                               NAMES
e617ca1db9a5   nginx     "/docker-entrypoint.…"   6 seconds ago   Up 5 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp   intelligent_turing
[root@localhost ~]# docker exec -it e617ca1db9a5 bash
root@e617ca1db9a5:/# cd /usr/share/nginx/html/
root@e617ca1db9a5:/usr/share/nginx/html# ls
50x.html  index.html
root@e617ca1db9a5:/usr/share/nginx/html# echo 'docker nginx test' > index.html 
[root@localhost ~]# curl 192.168.88.10
docker nginx test

[root@admin ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED                                                        NAMES
e0a818c40b7e   nginx     "/docker-entrypoint.…"   About an hour ago  0.0.0.0:9000->9000/tcp, :::9000->9000/tcp   determined_sanderson
9b066ef4bcd2   nginx     "/docker-entrypoint.…"   About an hour ago  90->80/tcp, :::90->80/tcp                   vigorous_hypatia
[root@admin ~]# docker stop e0a818c40b7e
e0a818c40b7e
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
  • -i--interactive
    • 功能保持标准输入stdin打开即使没有附加终端。
    • 作用:允许用户与容器内的命令进行交互。
  • -t--tty
    • 功能分配一个伪终端TTY
    • 作用:为用户创建一个类似本地终端的交互环境,支持颜色显示、光标操作等终端特性。
5.2.1 Docker快速搭建RPG小游戏
[root@localhost ~]# docker pull registry.cn-guangzhou.aliyuncs.com/welldene/games:rpg_game
[root@localhost ~]# docker run -d -p 8000:8000 -p 8787:8787  --name rpg -e HOST_IP=192.168.88.10 registry.cn-guangzhou.aliyuncs.com/welldene/games:rpg_game
9c4bc95c98836a1df0453c282196083e4cb0b5d06e507d5d4567a4c018c13272
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                                                        COMMAND         CREATED         STATUS         PORTS                                                                                      NAMES
35a6b44d5645   registry.cn-guangzhou.aliyuncs.com/welldene/games:rpg_game   "bash run.sh"   3 seconds ago   Up 2 seconds   0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp, 0.0.0.0:8787->8787/tcp, [::]:8787->8787/tcp   rpg

浏览器访问: http://IP:8787

image-20250324001839505

5.2.2 Docker参数说明
-d, --detach: 以守护进程方式运行容器  
-p, --publish: 映射容器端口到宿主机端口  
格式: `-p [hostPort]:[containerPort]`  

-P大写随机端口映射  

-v, --volume: 挂载数据卷  
格式: `-v [hostPath]:[containerPath]`  

-e, --env: 设置环境变量  
--name: 为容器指定名称  
--network: 指定容器所属网络  
--restart: 容器退出时的重启策略  
可选值: `no`, `on-failure`, `unless-stopped`, `always`  

-i, --interactive: 保持标准输入打开  
-t, --tty: 分配一个伪终端  
-u, --user: 指定运行容器的用户  
--entrypoint: 覆盖容器的默认入口点  
--rm: 容器退出后自动删除  
--hostname: 设置容器主机名  
--add-host: 添加自定义主机名到 IP 的映射  
--link: 添加到另一个容器的链接  
--expose: 暴露容器端口  
--volume-driver: 指定数据卷驱动程序  
--cpu-shares: 设置 CPU 权重  
--memory: 设置容器内存限制  

6. Docker核心技术

6.1 Linux namespace技术

如果一个宿主机运行了N个容器多个容器带来的以下问题怎么解决

  1. 怎么样保证每个容器都有不同的文件系统并且能互不影响?
  2. 一个docker主进程内的各个容器都是其子进程那么如何实现同一个主进程下不同类型的子进程各个子进程间通信能相互访问吗
  3. 每个容器怎么解决IP以及端口分配的问题
  4. 多个容器的主机名能一样吗?
  5. 每个容器都要不要有root用户怎么解决账户重名问题呢

以上问题怎么解决

Docker 的 Namespace 技术是实现容器隔离的核心机制之一。它通过 Linux Namespace 提供的隔离功能,为每个容器创建独立的资源视图,从而实现容器之间的隔离

namespace是Linux系统的底层概念在内核层实现即有一些不同类型的命名空间都部署在核内各个docker容器运行在同一个docker主进程并且共用同一个宿主机系统内核各个docker容器运行在宿主机的用户空间每个容器都要有类似于虚拟机一样的相互隔离的运行空间,但是容器技术是在一个进程内实现运行指定服务的运行环境,并且还可以保护宿主机内核不受其他进程的干扰和影响,如文件系统、网络空间、进程空间等,目前主要通过以下技术实现容器运行空间的相互隔离:

隔离类型 功能 系统调用参数 内核
MNT Namespacemount 提供磁盘挂载点和文件系统的隔离能力 CLONE_NEWNS 2.4.19
IPC NamespaceInter-Process Communication 提供进程间通信的隔离能力 CLONE_NEWIPC 2.6.19
UTS NamespaceUNIX Timesharing System 提供主机名隔离能力 CLONE_NEWUTS 2.6.19
PID NamespaceProcess Identification 提供进程隔离能力 CLONE_NEWPID 2.6.24
Net Namespacenetwork 提供网络隔离能力 CLONE_NEWNET 2.6.29
User Namespaceuser 提供用户隔离能力 CLONE_NEWUSER 3.8
6.1.1 MNT Namespace

每个容器都要有独立的根文件系统有独立的用户空间,以实现容器里面启动服务并且使用容器的运行环境。

  • 启动三个容器
[root@localhost ~]# docker run -d --name nginx-1 -p 80:80 nginx
0e72f06bba417073d1d4b2cb53e62c45b75edc699b737e46a157a3249f3a803e
[root@localhost ~]# docker run -d --name nginx-2 -p 81:80 nginx
c8ce6a0630b66e260eef16d8ecf48049eed7b893b87459888b634bf0e9e40f23
[root@localhost ~]# docker run -d --name nginx-3 -p 82:80 nginx
1cddbd412b5997f8935815c2f588431e100b752595ceaa92b95758ca45179096
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                 NAMES
b42378a51c40   nginx     "/docker-entrypoint.…"   2 seconds ago    Up 1 second     0.0.0.0:82->80/tcp, [::]:82->80/tcp   nginx-3
d30f033c2f29   nginx     "/docker-entrypoint.…"   5 seconds ago    Up 5 seconds    0.0.0.0:81->80/tcp, [::]:81->80/tcp   nginx-2
d34a012dcebc   nginx     "/docker-entrypoint.…"   10 seconds ago   Up 10 seconds   0.0.0.0:80->80/tcp, [::]:80->80/tcp   nginx-1
  • 连接进入某一个容器中,并创建一个文件
[root@localhost ~]# docker exec -it nginx-1 bash
root@d34a012dcebc:/# echo 'hello world test!' > /opt/test_nginx-1
root@d34a012dcebc:/# exit
  • 宿主机是使用了chroot技术把容器锁定到一个指定的运行目录里
[root@localhost ~]# find / -name test_nginx-1
/var/lib/docker/overlay2/075b51fb5d33011d4b449fde8c14199c1e30f86224862f68a6116b1cb1dacfdf/diff/opt/test_nginx-1
/var/lib/docker/overlay2/075b51fb5d33011d4b449fde8c14199c1e30f86224862f68a6116b1cb1dacfdf/merged/opt/test_nginx-1

在 Docker 中,文件系统是通过分层存储机制实现的,这与 Docker 的镜像和容器的架构有关。看到的两个文件路径反映了 Docker 的存储驱动(如 Overlay2的工作原理。

6.1.1.1 Docker 的存储架构
  1. 镜像层Read-Only
    • Docker 镜像是由多个只读层组成的。每一层代表了镜像的某个状态或修改。
    • 这些层是不可变的,一旦创建,不会被修改。
  2. 容器层Read-Write
    • 当你运行一个容器时Docker 会在镜像层之上添加一个可写层。
    • 容器的所有写操作(如创建文件、修改文件等)都会在这个可写层中进行,而不会影响下面的镜像层。
6.1.1.2 Overlay2 存储驱动

Docker 默认使用 Overlay2 存储驱动在支持的系统上。Overlay2 的工作机制如下:

  • merged 目录

    • 这是容器的根文件系统,是镜像层和容器层的联合视图。
    • 当你在容器中访问文件时,看到的是 merged 目录中的内容。
    • 例如,你在容器中创建的文件 /opt/test_nginx-1,在宿主机上可以通过 /var/lib/docker/overlay2/<id>/merged/opt/test_nginx-1 访问。
  • diff 目录

    • 这是容器的可写层,记录了容器对文件系统的修改。
    • 当你在容器中创建或修改文件时,实际的文件数据会存储在 diff 目录中。
    • 例如,你在容器中创建的文件 /opt/test_nginx-1,其实际数据存储在 /var/lib/docker/overlay2/<id>/diff/opt/test_nginx-1
  • 当你在容器中创建文件 /opt/test_nginx-1 时:

    • 文件的实际数据被写入到 diff 目录中。
    • merged 目录中通过联合文件系统OverlayFS的机制diff 目录中的文件映射到 merged 目录中,让你在容器中看到完整的文件系统视图。
6.1.2 IPC Namespace

一个容器内的进程间通信,允许一个容器内的不同进程数据互相访问,但是不能跨容器访问其他容器的数据

UTS Namespace包含了运行内核的名称、版本、底层体系结构类型等信息用于系统表示其中包含了hostname和域名它使得一个容器拥有属于自己hostname标识这个主机名标识独立于宿主机系统和其上的其他容器。

6.1.3 PID Namespace

Linux系统中有一个pid为1的进程init/systemd是其他所有进程的父进程那么在每个容器内也要有一个父进程来管理其下属的进程那么多个容器的进程通PID namespace进程隔离

  • 安装软件包
[root@localhost ~]# docker exec -it 065f06e5caa4 bash
root@0e72f06bba41:/# apt update
# ifconfig
root@0e72f06bba41:/# apt install -y net-tools
root@0e72f06bba41:/# apt install -y procps
root@0e72f06bba41:/# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          10  0 03:20 ?        00:00:00 nginx: master process nginx -g d
nginx        32      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        33      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        34      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        35      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        36      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        37      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        38      1  0 03:20 ?        00:00:00 nginx: worker process
nginx        39      1  0 03:20 ?        00:00:00 nginx: worker process
root         59      0  0 03:35 pts/0    00:00:00 bash
root        503     59  0 03:42 pts/0    00:00:00 ps -ef

那么宿主机的PID与容器内的PID是什么关系

[root@localhost ~]# yum install psmisc
[root@localhost ~]# pstree -p
[root@localhost ~]# pstree -p
systemd(1)─┬─NetworkManager(769)─┬─{NetworkManager}(772)
           │                     └─{NetworkManager}(775)
           ├─agetty(817)
           ├─atd(799)
           ├─auditd(693)─┬─sedispatch(695)
           │             ├─{auditd}(694)
           │             └─{auditd}(696)
           ├─bluetoothd(742)
           ├─chronyd(753)
           ├─containerd(28884)─┬─{containerd}(28886)
           │                   ├─{containerd}(28887)
           │                   ├─{containerd}(28888)
           │                   ├─{containerd}(28889)
           │                   ├─{containerd}(28890)
           │                   ├─{containerd}(28891)
           │                   ├─{containerd}(28892)
           │                   └─{containerd}(28894)
           ├─containerd-shim(30330)─┬─bash(30801)
           │                        ├─nginx(30353)─┬─nginx(30426)
           │                        │              ├─nginx(30427)
           │                        │              ├─nginx(30428)
           │                        │              └─nginx(30429)
           │                        ├─{containerd-shim}(30332)
           │                        ├─{containerd-shim}(30333)
           │                        ├─{containerd-shim}(30334)
           │                        ├─{containerd-shim}(30335)
           │                        ├─{containerd-shim}(30336)
           │                        ├─{containerd-shim}(30337)
           │                        ├─{containerd-shim}(30338)
           │                        ├─{containerd-shim}(30339)
           │                        ├─{containerd-shim}(30340)
           │                        ├─{containerd-shim}(30703)
           │                        └─{containerd-shim}(30909)
           ├─containerd-shim(30447)─┬─nginx(30469)─┬─nginx(30546)
           │                        │              ├─nginx(30547)
           │                        │              ├─nginx(30548)
           │                        │              └─nginx(30549)
           │                        ├─{containerd-shim}(30448)
           │                        ├─{containerd-shim}(30449)
           │                        ├─{containerd-shim}(30450)
           │                        ├─{containerd-shim}(30451)
           │                        ├─{containerd-shim}(30452)
           │                        ├─{containerd-shim}(30453)
           │                        ├─{containerd-shim}(30454)
           │                        ├─{containerd-shim}(30455)
           │                        ├─{containerd-shim}(30456)
           │                        └─{containerd-shim}(30705)
           ├─containerd-shim(30566)─┬─nginx(30587)─┬─nginx(30662)
           │                        │              ├─nginx(30663)
           │                        │              ├─nginx(30664)
           │                        │              └─nginx(30665)
           │                        ├─{containerd-shim}(30567)
           │                        ├─{containerd-shim}(30568)
           │                        ├─{containerd-shim}(30569)
           │                        ├─{containerd-shim}(30570)
           │                        ├─{containerd-shim}(30571)
           │                        ├─{containerd-shim}(30572)
           │                        ├─{containerd-shim}(30573)
           │                        ├─{containerd-shim}(30574)
           │                        ├─{containerd-shim}(30593)
           │                        ├─{containerd-shim}(30706)
           │                        └─{containerd-shim}(30780)
           ├─crond(801)
           ├─dbus-broker-lau(719)───dbus-broker(723)
           ├─dockerd(29156)─┬─docker-proxy(30381)─┬─{docker-proxy}(30382)
           │                │                     ├─{docker-proxy}(30383)
           │                │                     ├─{docker-proxy}(30384)
           │                │                     ├─{docker-proxy}(30385)
           │                │                     ├─{docker-proxy}(30386)
           │                │                     └─{docker-proxy}(30388)
           │                ├─docker-proxy(30387)─┬─{docker-proxy}(30389)
           │                │                     ├─{docker-proxy}(30390)
           │                │                     ├─{docker-proxy}(30391)
           │                │                     ├─{docker-proxy}(30392)
           │                │                     ├─{docker-proxy}(30393)
           │                │                     ├─{docker-proxy}(30394)
           │                │                     ├─{docker-proxy}(30395)
           │                │                     └─{docker-proxy}(30396)
           │                ├─docker-proxy(30497)─┬─{docker-proxy}(30498)
           │                │                     ├─{docker-proxy}(30499)
           │                │                     ├─{docker-proxy}(30500)
           │                │                     ├─{docker-proxy}(30501)
           │                │                     ├─{docker-proxy}(30502)
           │                │                     ├─{docker-proxy}(30503)
           │                │                     ├─{docker-proxy}(30504)
           │                │                     └─{docker-proxy}(30505)
           │                ├─docker-proxy(30506)─┬─{docker-proxy}(30507)
           │                │                     ├─{docker-proxy}(30508)
           │                │                     ├─{docker-proxy}(30509)
           │                │                     ├─{docker-proxy}(30510)
           │                │                     ├─{docker-proxy}(30511)
           │                │                     ├─{docker-proxy}(30512)
           │                │                     └─{docker-proxy}(30513)
           │                ├─docker-proxy(30616)─┬─{docker-proxy}(30617)
           │                │                     ├─{docker-proxy}(30618)
           │                │                     ├─{docker-proxy}(30619)
           │                │                     ├─{docker-proxy}(30620)
           │                │                     ├─{docker-proxy}(30621)
           │                │                     ├─{docker-proxy}(30622)
           │                │                     ├─{docker-proxy}(30624)
           │                │                     └─{docker-proxy}(30625)
           │                ├─docker-proxy(30623)─┬─{docker-proxy}(30626)
           │                │                     ├─{docker-proxy}(30627)
           │                │                     ├─{docker-proxy}(30628)
           │                │                     ├─{docker-proxy}(30629)
           │                │                     ├─{docker-proxy}(30630)
           │                │                     ├─{docker-proxy}(30631)
           │                │                     └─{docker-proxy}(30632)
           │                ├─{dockerd}(29157)
           │                ├─{dockerd}(29159)
           │                ├─{dockerd}(29161)
           │                ├─{dockerd}(29163)
           │                ├─{dockerd}(29407)
           │                ├─{dockerd}(29420)
           │                ├─{dockerd}(29430)
           │                ├─{dockerd}(29537)
           │                ├─{dockerd}(29711)
           │                ├─{dockerd}(29712)
           │                ├─{dockerd}(29823)
           │                ├─{dockerd}(29844)
           │                ├─{dockerd}(29845)
           │                ├─{dockerd}(30633)
           │                └─{dockerd}(30683)
           ├─irqbalance(729)───{irqbalance}(738)
           ├─lsmd(730)
           ├─mcelog(734)
           ├─polkitd(944)─┬─{polkitd}(975)
           │              ├─{polkitd}(976)
           │              ├─{polkitd}(979)
           │              ├─{polkitd}(980)
           │              ├─{polkitd}(981)
           │              ├─{polkitd}(982)
           │              └─{polkitd}(1004)
           ├─rsyslogd(1063)─┬─{rsyslogd}(1095)
           │                └─{rsyslogd}(1096)
           ├─sshd(786)─┬─sshd(1527)───sshd(1541)───bash(1542)───docker(30781)─┬─{docker}(30782)
           │           │                                                      ├─{docker}(30783)
           │           │                                                      ├─{docker}(30784)
           │           │                                                      ├─{docker}(30785)
           │           │                                                      ├─{docker}(30786)
           │           │                                                      ├─{docker}(30787)
           │           │                                                      ├─{docker}(30788)
           │           │                                                      └─{docker}(30807)
           │           └─sshd(30928)───sshd(30932)───bash(30933)───pstree(31158)
           ├─systemd(1532)───(sd-pam)(1534)
           ├─systemd-journal(635)
           ├─systemd-logind(739)
           ├─systemd-udevd(648)
           └─tuned(791)─┬─{tuned}(1106)
                        ├─{tuned}(1137)
                        └─{tuned}(1138)

[root@localhost ~]# ps aux | grep b42378a51c40
root       30566  0.0  0.9 1237984 16524 ?       Sl   18:42   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id b42378a51c402d7ffa408d331a61ebdefe1b920eb723cd343ebc8e5781bec03d -address /run/containerd/containerd.sock
root       31171  0.0  0.1   3880  2048 pts/1    S+   18:54   0:00 grep --color=auto b42378a51c40

在宿主机上查看容器的进程

[root@localhost ~]# docker top nginx-1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                30353               30330               0                   18:42               ?                   00:00:00            nginx: master process nginx -g daemon off;
101                 30426               30353               0                   18:42               ?                   00:00:00            nginx: worker process
101                 30427               30353               0                   18:42               ?                   00:00:00            nginx: worker process
101                 30428               30353               0                   18:42               ?                   00:00:00            nginx: worker process
101                 30429               30353               0                   18:42               ?                   00:00:00            nginx: worker process
root                30801               30330               0                   18:46               pts/0               00:00:00            bash

首先,可以看到容器内的进程在宿主机上的 PID。容器内的进程只能看到自己命名空间中的进程而无法看到宿主机或其他容器的进程

所以说明docker采用PID Namespace技术将容器内部的进程与宿主机的进程进行了隔离

并且,容器内部的进程和宿主机上的进程还存在一定的对应或者映射关系

  1. 独立的 PID 命名空间:
    • 每个 Docker 容器都有自己独立的 PID 命名空间。
    • 容器内的进程 PID 从 1 开始编号,与宿主机上的 PID 是相互独立的。
  2. PID 映射:
    • 容器内的进程 PID 与宿主机上的进程 PID 之间是有映射关系的。
  3. PID 可见性:
    • 容器内的进程只能看到容器内部的 PID。
    • 宿主机上的进程可以看到容器内部的 PID,但容器内的进程无法看到宿主机上的 PID。
  4. PID 隔离:
    • 容器内的进程无法访问或影响宿主机上的其他进程。
    • 宿主机上的进程可以访问和管理容器内的进程。
6.1.4 Net Namespace

每一个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP协议栈等Docker使用network namespace启动一个vethX接口这样容器将拥有它自己的桥接IP地址通常是docker0而docker0实质就是linux的虚拟网桥。

查看容器内部的IP网络信息发现有一个eth0的网卡

root@d34a012dcebc:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether ce:f2:f5:63:47:16  txqueuelen 0  (Ethernet)
        RX packets 4713  bytes 10994487 (10.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3888  bytes 212050 (207.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

而我们的宿主机上的网卡中多了个docker0的虚拟网桥这样以来通过Net Namespace将容器的网络与宿主机的网络进行隔离并且通过虚拟网桥docker0与容器进行网络通信

[root@localhost ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::38a5:cdff:fe6b:7dbc  prefixlen 64  scopeid 0x20<link>
        ether 3a:a5:cd:6b:7d:bc  txqueuelen 0  (Ethernet)
        RX packets 5820  bytes 11520579 (10.9 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9483  bytes 11508761 (10.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.10  netmask 255.255.255.0  broadcast 192.168.88.255
        inet6 fe80::20c:29ff:fe26:8384  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:26:83:84  txqueuelen 1000  (Ethernet)
        RX packets 235433  bytes 340010957 (324.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 54812  bytes 14709198 (14.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth51c3173: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::d8fa:18ff:fe0f:d176  prefixlen 64  scopeid 0x20<link>
        ether da:fa:18:0f:d1:76  txqueuelen 0  (Ethernet)
        RX packets 3888  bytes 212050 (207.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4714  bytes 10994557 (10.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth813b530: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::e498:2eff:fe08:2c5b  prefixlen 64  scopeid 0x20<link>
        ether e6:98:2e:08:2c:5b  txqueuelen 0  (Ethernet)
        RX packets 3  bytes 126 (126.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15  bytes 1118 (1.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

vethc11c399: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::a482:dfff:fe74:dec7  prefixlen 64  scopeid 0x20<link>
        ether a6:82:df:74:de:c7  txqueuelen 0  (Ethernet)
        RX packets 3  bytes 126 (126.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 18  bytes 1244 (1.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • docker0 网桥
    • 它是 Docker 默认创建的一个虚拟网桥,用于管理容器的网络通信。
    • 它为连接到它的容器提供了一个内部网络环境,允许容器之间通过这个网桥进行通信。
  • veth3ad3c5b 接口
    • 这是一个虚拟以太网接口,用于连接容器和宿主机的网络。
    • 它的一端连接到 docker0 网桥,另一端连接到容器的网络命名空间。
    • 当容器启动时Docker 会自动创建这样的 veth pair并将一端连接到 docker0,另一端连接到容器的网络命名空间。

逻辑图

image-20210603144141780

6.1.5 User Namespace

各个容器内可能会出现重名的用户和用户组名称或重复的用户UID或者GID那么怎么隔离各个容器内的用户空间呢

User Namespace允许在宿主机的各个容器空间内创建相同的用户名以及相同的uid和gid只是此用户的有效范围仅仅是当前的容器内不能访问另外一个容器内的文件系统即相互隔离、互不影响、永不相见

6.2 Linux control groups

在一个容器内部如果不对其做任何资源限制则宿主机会允许其占用无限大的内存空间有时候会因为代码bug程序会一直申请内存直到把宿主机内存占完为了避免此类的问题出现宿主机有必要对容器进行资源分配限制比如cpu、内存等Linux Cgroups的全称是Linux control Groups它最重要的作用就是限制一个进程组能够使用的资源上线包括cpu、内存、磁盘、网络等等。

  • 验证系统内核层已经默认开启cgroup功能
[root@localhost ~]# cat /boot/config-5.14.0-427.13.1.el9_4.x86_64 | grep cgroup -i
CONFIG_CGROUPS=y
# CONFIG_CGROUP_FAVOR_DYNMODS is not set
CONFIG_BLK_CGROUP=y
CONFIG_CGROUP_WRITEBACK=y
CONFIG_CGROUP_SCHED=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_RDMA=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_BPF=y
CONFIG_CGROUP_MISC=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_SOCK_CGROUP_DATA=y
CONFIG_BLK_CGROUP_RWSTAT=y
CONFIG_BLK_CGROUP_IOLATENCY=y
CONFIG_BLK_CGROUP_FC_APPID=y
# CONFIG_BLK_CGROUP_IOCOST is not set
# CONFIG_BLK_CGROUP_IOPRIO is not set
# CONFIG_BFQ_CGROUP_DEBUG is not set
CONFIG_NETFILTER_XT_MATCH_CGROUP=m
CONFIG_NET_CLS_CGROUP=y
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CGROUP_NET_CLASSID=y
# CONFIG_DEBUG_CGROUP_REF is not set
  • 关于内存的模块
[root@localhost ~]#  cat /boot/config-3.10.0-957.el7.x86_64 | grep mem -i | grep cg -i
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_MEMCG_KMEM=y

扩展阅读:

https://blog.csdn.net/qyf158236/article/details/110475457

6.2.1 Docker 中的 cgroups 资源限制
6.2.1.1 CPU 资源限制

Docker 提供了多种方式来限制容器的 CPU 使用:

  • --cpus:限制容器可以使用的 CPU 核心数量。例如,--cpus="1.5" 表示容器最多可以使用 1.5 个 CPU。
  • --cpu-shares:设置容器的 CPU 使用权重。默认值为 1024值越高分配的 CPU 时间片越多。
  • --cpu-period--cpu-quota:更细粒度地控制 CPU 时间片。--cpu-period 设置 CPU 时间片的周期(单位为微秒),--cpu-quota 设置每个周期内容器可以使用的 CPU 时间。
6.2.1.2 内存资源限制

Docker 可以通过以下参数限制容器的内存使用:

  • -m--memory:限制容器的物理内存使用量。例如,-m 512m 表示限制容器使用 512MB 的物理内存。
  • --memory-swap:限制容器的总内存使用量(物理内存 + 交换空间)。例如,--memory-swap=1g 表示容器可以使用 1GB 的总内存。
6.2.1.3 磁盘 I/O 资源限制

Docker 可以限制容器的磁盘 I/O 使用:

  • --blkio-weight:设置容器的块设备 I/O 权重,范围为 10 到 1000。
  • --device-read-bps--device-write-bps:限制特定设备的读写速率。例如,--device-read-bps /dev/sda:1mb 表示限制容器对 /dev/sda 的读取速率为 1MB/s。
6.2.2 查看和管理 cgroups 资源限制
  • 查看 cgroups 配置:可以通过访问 /sys/fs/cgroup 目录来查看容器的 cgroups 配置。例如,/sys/fs/cgroup/cpu/docker/<container_id> 目录下包含了容器的 CPU 资源限制文件。
  • 动态调整资源限制:在容器运行时,可以通过修改 cgroups 文件的内容来动态调整资源限制。
6.2.3 使用压缩工具测试
[root@bogon ~]# docker pull lorel/docker-stress-ng
Using default tag: latest
latest: Pulling from lorel/docker-stress-ng
c52e3ed763ff: Pull complete 
a3ed95caeb02: Pull complete 
7f831269c70e: Pull complete 
Digest: sha256:c8776b750869e274b340f8e8eb9a7d8fb2472edd5b25ff5b7d55728bca681322
Status: Downloaded newer image for lorel/docker-stress-ng:latest
6.2.3.1 测试CPU

不限制cpu使用

[root@bogon ~]# docker container run --name stress -it --rm lorel/docker-stress-ng:latest  --cpu 4
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 8 cpu
[root@bogon ~]# docker stats
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
92b0b8d916c1        stress              101.54%             15.81MiB / 983.3MiB   1.61%               648B / 0B           0B / 0B             9
[root@bogon ~]# top
top - 19:15:49 up 2 days,  2:38,  2 users,  load average: 7.02, 3.00, 1.15
Tasks: 131 total,  10 running, 121 sleeping,   0 stopped,   0 zombie
%Cpu(s): 99.7 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1006892 total,   100680 free,   320704 used,   585508 buff/cache
KiB Swap:  2097148 total,  2096628 free,      520 used.   422732 avail Mem 
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                     
40035 root      20   0    6908   4180    252 R 12.6  0.4   0:12.79 stress-ng-cpu                                                               
40037 root      20   0    6908   4180    252 R 12.6  0.4   0:12.78 stress-ng-cpu                                                               
40038 root      20   0    6908   2136    252 R 12.6  0.2   0:12.78 stress-ng-cpu                                                               
40040 root      20   0    6908   2136    252 R 12.6  0.2   0:12.78 stress-ng-cpu                                                               
40036 root      20   0    6908   2136    252 R 12.3  0.2   0:12.77 stress-ng-cpu                                                               
40039 root      20   0    6908   2136    252 R 12.3  0.2   0:12.78 stress-ng-cpu                                                               
40041 root      20   0    6908   4180    252 R 12.3  0.4   0:12.77 stress-ng-cpu                                                               
40042 root      20   0    6908   2136    252 R 12.3  0.2   0:12.77 stress-ng-cpu                                                               
    1 root      20   0  128484   7208   4196 S  0.0  0.7   0:10.12 systemd

可以看到cpu使用已经满了

重新启动容器加入CPU限制参数

[root@bogon ~]# docker container run --name stress --cpus=0.5 -it --rm lorel/docker-stress-ng:latest  --cpu 8
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 8 cpu
[root@bogon ~]# docker stats
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
845220ef9982        stress              51.57%              20.05MiB / 983.3MiB   2.04%               648B / 0B           0B / 0B             9

7. 容器规范

7.1 容器技术及其标准化组织 OCI

容器技术是一种轻量级的虚拟化技术,用于隔离应用程序及其依赖项,使其能够在不同的环境中一致地运行。除了 Docker 之外,还有其他多种容器运行时和工具,例如 CoreOS 的 rkt、阿里的 Pouch 和红帽的 Podman。为了确保容器生态系统的标准性和可持续发展Linux 基金会、Docker、微软、红帽、谷歌和 IBM 等公司在 2015 年 6 月共同成立了 Open Container Initiative (OCI) 组织。

7.1.1 OCI 的目标

OCI 的主要目标是制定开放的容器规范以确保不同容器技术之间的可移植性和互操作性。目前OCI 已经发布了两个核心规范:

  1. Runtime Spec:定义了容器运行时的规范,包括容器的生命周期管理、资源隔离和安全等。
  2. Image Format Spec:定义了容器镜像的格式和元数据,确保镜像可以在不同的容器运行时之间共享和运行。

通过遵循这些规范,不同的容器运行时和工具可以实现互操作性,从而推动容器技术的标准化和健康发展。

7.1.2 主流容器运行时

容器运行时是真正运行容器的地方,它需要与操作系统的内核紧密合作,为容器提供隔离的运行环境。以下是目前主流的三种容器运行时:

7.1.2.1 LXC (Linux Containers)
  • 简介LXC 是 Linux 上早期的容器运行时,它利用 Linux 内核的 Namespace 和 Cgroups 技术来实现进程隔离和资源管理。
  • 特点
    • 提供了完整的 Linux 系统环境,支持多种 Linux 发行版。
    • 早期 Docker 也曾使用 LXC 作为其默认的运行时。
  • 适用场景:适用于需要完整 Linux 系统环境的容器化应用。
7.1.2.2 Runc
  • 简介Runc 是目前 Docker 默认的容器运行时,它是一个轻量级的命令行工具,用于运行和管理容器。
  • 特点
    • 完全遵循 OCI 的 Runtime Spec 规范,确保与 OCI 标准的兼容性。
    • 由于其轻量级和高性能的特点Runc 已经成为许多容器运行时的底层实现。
  • 适用场景:适用于需要高性能和轻量级容器运行环境的场景。
7.1.2.3 Rkt (Rocket)
  • 简介Rkt 是由 CoreOS 开发的容器运行时,旨在提供一个安全、可靠且符合 OCI 规范的容器运行环境。
  • 特点
    • 与 Docker 不同Rkt 本身是一个独立的容器运行时,不依赖 Docker 的守护进程。
    • 提供了更好的安全性和隔离性,例如通过 AppArmor 和 SELinux 等安全机制。
  • 适用场景:适用于对安全性要求较高的容器化应用。

容器技术的发展离不开标准化的推动。OCI 通过制定 Runtime Spec 和 Image Format Spec为容器运行时和工具提供了统一的标准确保了不同容器技术之间的互操作性和可移植性。目前主流的容器运行时如 LXC、Runc 和 Rkt都遵循这些规范从而推动了容器技术的广泛应用和发展。

8. docker info信息

[root@localhost ~]# docker info 
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)
  scan: Docker Scan (Docker Inc.)

Server:
 Containers: 2		# 当前主机运行容器总数
  Running: 1		# 有几个容器是正在运行的
  Paused: 0			# 有几个容器是暂停的
  Stopped: 1		# 有几个容器是停止的
 Images: 1			# 当前服务器的镜像数
 Server Version: 20.10.6		# 服务端版本
 Storage Driver: overlay2		# 正在使用的存储引擎
  Backing Filesystem: xfs		# 后端文件系统,即服务器的磁盘文件系统
  Supports d_type: true			# 是否支持d_type
  Native Overlay Diff: true		# 是否支持差异数据存储
  userxattr: false
 Logging Driver: json-file		# 日志文件类型
 Cgroup Driver: cgroupfs		# cgroups类型
 Cgroup Version: 1
 Plugins:						# 插件
  Volume: local					# 卷
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive				# 是否支持swarm
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
 Default Runtime: runc			# 默认的runtime
 Init Binary: docker-init		# 初始化容器的守护进程
 containerd version: d71fcd7d8303cbf684402823e425e9dd2e99285d
 runc version: b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
 init version: de40ad0
 Security Options:				# 安全选项
  seccomp
   Profile: default
 Kernel Version: 3.10.0-693.el7.x86_64		# 宿主机内核版本
 Operating System: CentOS Linux 7 (Core)	# 宿主机操作系统
 OSType: linux								# 宿主机操作系统类型
 Architecture: x86_64						# 宿主机架构
 CPUs: 1									# 宿主机cpu数量
 Total Memory: 1.781GiB						# 宿主机总内存
 Name: docker-server						# 宿主机主机名
 ID: ARN5:ESPO:FEZ4:KDZ6:RWGG:WQ3X:SIXN:3FVG:ATXH:JAXA:ENGH:RAVE
 Docker Root Dir: /var/lib/docker			# 宿主机数据保存目录
 Debug Mode: false
 Registry: https://index.docker.io/v1/		# 镜像仓库
 Labels:
 Experimental: false						# 是否是测试版
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false				# 是否开启活动容器(重启不关闭容器)

9. docker 存储引擎

Docker 的存储引擎是 Docker 平台中用于管理容器和镜像数据的核心组件。它负责容器的文件系统、网络配置、权限管理等,以及其他与容器运行相关的任务。以下是对 Docker 存储引擎的详细介绍:

9.1 核心概念及工作原理

Docker 存储引擎的核心思想是“层”的概念。镜像是由多个只读层组成的,而容器则在镜像的基础上添加了一个可读写的层。这种分层架构使得镜像的复用和部署变得非常方便,同时减少了容器的体积。

Docker 使用联合文件系统Union File System来管理容器的文件系统。联合文件系统允许将多个目录或文件系统合并为一个统一的文件系统视图。Docker 的存储引擎通过这种机制,将镜像层和容器层合并在一起,使得容器能够看到一个完整的文件系统。

Docker 支持多种存储引擎,每种存储引擎都有其特点和适用场景。以下是一些常见的存储引擎:

AUFSAnother Union File System

  • 特点AUFS 是一种文件级的存储驱动,允许多个目录共享相同的文件系统层次结构。它通过联合挂载技术将多个目录挂载到一个单一的文件系统上。
  • 适用场景AUFS 曾是 Docker 早期版本的默认存储驱动,但在较新的 Docker 版本中已被 Overlay2 替代。

OverlayFS

  • 特点OverlayFS 是一种更现代的联合文件系统,从 Linux 内核 3.18 开始支持。它将文件系统简化为两层一个只读的下层lowerdir和一个可读写的上层upperdir统一后的视图称为合并层merged
  • 优势OverlayFS 支持页缓存共享多个容器如果读取相同层的同一个文件可以共享页缓存从而提高内存利用率。此外OverlayFS 在性能和稳定性方面表现更好,是目前 Docker 的默认存储驱动。

9.2 Docker 的 Overlay2 存储驱动介绍

  1. 什么是 Overlay2

Overlay2 是 Docker 中的一种存储驱动,用于管理容器和镜像的文件系统。它是 OverlayFS 的改进版本,解决了早期 Overlay 驱动可能遇到的 inode 耗尽问题。Overlay2 使用联合文件系统Union File System技术将多个文件系统层合并为一个统一的文件系统视图从而实现高效的容器文件系统管理。

  1. Overlay2 的工作原理

Overlay2 通过以下三个主要目录来管理文件系统:

  • LowerDir:只读层,包含基础镜像的文件系统。可以有多个只读层,每层都是独立的。
  • UpperDir:读写层,用于存储容器运行时的文件系统变更(即 diff 层)。
  • MergedDir:联合挂载后的视图,容器看到的完整文件系统。它将 LowerDirUpperDir 合并为一个统一的文件系统视图。
  • WorkDir:工作目录,用于联合挂载的内部操作,挂载后内容被清空。

当启动一个容器时Overlay2 会将镜像层(LowerDir)和容器层(UpperDir)联合挂载到 MergedDir,容器通过这个目录看到完整的文件系统。