docker(1)-环境和基本概念

文章目录

    • [1. docker环境和基本概念](#1. docker环境和基本概念)
      • [1. 安装(基于apt包管理器安装)](#1. 安装(基于apt包管理器安装))
      • [2. 卸载](#2. 卸载)
      • [3. 自定义镜像库](#3. 自定义镜像库)
      • [4. 将用户添加到docker组(不用每次都sudo)](#4. 将用户添加到docker组(不用每次都sudo))
      • [5. 镜像和容器的关系](#5. 镜像和容器的关系)
      • [6. docker解决了什么问题](#6. docker解决了什么问题)
      • [7. docker局限](#7. docker局限)
      • [8. docker带给开发的改变](#8. docker带给开发的改变)
      • [9. docker和虚拟机的区别](#9. docker和虚拟机的区别)
      • [10. 基本架构图](#10. 基本架构图)
      • [11. rootfs](#11. rootfs)
      • [12. linux namespace](#12. linux namespace)
      • [13. 进程命名空间](#13. 进程命名空间)
      • [14. cgroups](#14. cgroups)
        • [1. CPU 子系统 (cpu / cpuacct)](#1. CPU 子系统 (cpu / cpuacct))
        • [2. CFS (Completely Fair Scheduler)](#2. CFS (Completely Fair Scheduler))
        • [3. RT (Real-Time scheduling) 子系统](#3. RT (Real-Time scheduling) 子系统)
        • [4. 三者关系总结](#4. 三者关系总结)
        • [5. cpuset 子系统](#5. cpuset 子系统)
        • [6. cpuacct 子系统](#6. cpuacct 子系统)
        • [7. memory 子系统](#7. memory 子系统)
        • [8. blkio 子系统](#8. blkio 子系统)
        • [9. devices 子系统](#9. devices 子系统)
        • [10. freezer 子系统](#10. freezer 子系统)
        • [11. net_cls 子系统](#11. net_cls 子系统)
        • [12. net_prio 子系统](#12. net_prio 子系统)
      • [15. docker常用命令](#15. docker常用命令)
        • 1.环境信息
        • [2. 系统日志](#2. 系统日志)
        • [3. 容器生命周期](#3. 容器生命周期)
        • [4. 运维](#4. 运维)
        • [5. 镜像管理](#5. 镜像管理)
        • [6. 镜像仓库](#6. 镜像仓库)

1. docker环境和基本概念

1. 安装(基于apt包管理器安装)

shell 复制代码
sudo apt install docker.io

2. 卸载

shell 复制代码
sudo apt-get purge docker.io
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

3. 自定义镜像库

由于存在国内外数据下载速度不一致,所以采用自定义镜像库

linux:/etc/docker/daemon.json

macos:磁盘/用户/mac/(隐藏文件).docker

shell 复制代码
{
  "registry-mirrors": [
          "https://hub.xdark.top",
          "https://hub.littlediary.cn",
          "https://dockerpull.org",
          "https://hub.crdz.gq",
          "https://docker.1panel.live",
          "https://docker.unsee.tech",
          "https://docker.udayun.com",
          "https://docker.kejilion.pro",
          "https://registry.dockermirror.com",
          "https://docker.rainbond.cc",
          "https://hub.geekery.cn",
          "https://docker.1panelproxy.com",
          "https://docker.linkedbus.com",
          "https://docker.nastool.de"
  ]
}

接着载入:

linux:

shell 复制代码
sudo systemctl daemon-reload
sudo systemctl restart docker

macos:

shell 复制代码
sudo launchctl daemon-reload
sudo launchctl restart docker

4. 将用户添加到docker组(不用每次都sudo)

linux:

shell 复制代码
sudo groupadd docker       # 创建 docker 组(如果不存在)
sudo usermod -aG docker $USER

mac:

macOS 使用 Docker Desktop,它是一个普通用户应用,容器操作通过后台进程管理,不需要 Linux 用户组权限,在终端运行 Docker 命令时,只要 当前用户已经登录 Docker Desktop,就可以直接执行命令:

shell 复制代码
docker run hello-world

根本不需要 sudo,也不需要设置 docker 组

5. 镜像和容器的关系

是相同引用

镜像是模板,容器是实例

可以从一个镜像创建多个容器

shell 复制代码
docker run -it ubuntu bash   # 用 ubuntu 镜像运行一个容器
docker run -it ubuntu bash   # 再运行一个,还是独立的容器

容器删除了,镜像还在

镜像删除了,之前的容器还能跑(因为容器里已经有了写层),但你不能再用那个镜像新建容器

复制代码
镜像 (Image)         --->   容器 (Container)
只读层                +     可写层
模板                  +     实例

6. docker解决了什么问题

  1. 解决了应用程序本地运行环境与生产运行环境不一致的问题
  2. 解决了应用程序资源使用的问题,docker会一开始就为每个程序指定内存分配和CPU分配
  3. 让快速扩展、弹性伸缩变得简单

7. docker局限

docker是容器化技术,针对的是应用及应用所依赖的环境做容器化。遵循单一原则,一个容器只运行一个主进程。多个进程都部署在一个容器中,弊端很多。比如更新某个进程的镜像时,其他进程也会被迫重启,如果一个进程出问题导致容器挂了,所有进程都将无法访问。再根据官网的提倡的原则而言,容器 = 应用 + 依赖的执行环境而不是像虚拟机一样,把一堆进程都部署在一起

8. docker带给开发的改变

环境一致性:开发、测试、生产环境完全统一,避免"在我机器上能跑"的问题。

快速启动与隔离:一行命令即可运行服务,多版本环境互不干扰。

依赖管理清晰:每个项目在独立容器中运行,避免依赖冲突。

提升 CI/CD 效率:镜像打包后可直接部署,发布流程自动化、标准化。

支持微服务架构:每个服务单独容器化,方便扩展、升级和编排。

跨平台迁移方便:镜像自带环境与依赖,可在不同平台无差别运行。

9. docker和虚拟机的区别

复制代码
传统虚拟机 (VM)
┌───────────────────────────────┐
│          物理硬件 (Hardware)   │
├───────────────────────────────┤
│          主机操作系统 (Host OS)│
├───────────────────────────────┤
│          Hypervisor (虚拟机管理器) │
├───────────────┬───────────────┤
│   Guest OS    │   Guest OS    │   (每个虚拟机都有完整的操作系统)
│   Libraries   │   Libraries   │
│   App1        │   App2        │
└───────────────┴───────────────┘

Docker (容器)
┌───────────────────────────────┐
│          物理硬件 (Hardware)   │
├───────────────────────────────┤
│          主机操作系统 (Host OS)│
├───────────────────────────────┤
│          Docker Engine         │
├───────────────┬───────────────┤
│   Libraries   │   Libraries   │   (共享同一个内核,无需重复OS)
│   App1        │   App2        │
└───────────────┴───────────────┘
  1. 虚拟机:每个应用都需要跑一个完整的 Guest OS → 占用资源大,启动慢。
  2. Docker 容器:共享同一个内核,只隔离应用环境 → 占用小,启动快,部署灵活

10. 基本架构图

docker基本架构图:

复制代码
                 ┌──────────────────────────┐
                 │   Docker Registry (注册中心) │
                 │   Docker Hub / 私有仓库       │
                 └───────────▲──────────────┘
                             │ pull/push 镜像
                             │
┌────────────────────────────┴───────────────────────────┐
│                 Docker Host (主机)                      │
│                                                        │
│  ┌───────────────┐      ┌─────────────────────────┐    │
│  │ Docker Client │─────▶│ Docker Daemon (守护进程) │    │
│  │ (CLI / API)   │◀─────│  接收命令,管理容器/镜像    │    │
│  └───────────────┘      └─────────────────────────┘    │
│              │                                     │
│              │ run/start/stop                      │
│              ▼                                     │
│   ┌────────────────┐      ┌─────────────────────┐ │
│   │   Image (镜像) │───▶  │ Container (容器)     │ │
│   │  只读模板       │      │ 镜像运行的实例,带状态 │ │
│   └────────────────┘      └─────────────────────┘ │
│                                                        │
└────────────────────────────────────────────────────────┘
  1. 镜像(Image):Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统
  2. 容器(Container):容器是独立运行的一个或一组应用,是镜像运行时的实体
  3. 客户端(client):Docker 客户端通过命令行或者其他工具使用 Docker SDK(Software Development Kit,开发接口) 与 Docker 的守护进程通信
  4. 主机(host):一个物理或者虚拟的机器用于执行 Docker 守护进程和容器
  5. 注册中心(Registry):Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub提供了庞大的镜像集合供

11. rootfs

rootfs 是Docker 容器在启动时内部进程可见的文件系统,即Docker容器的根目录。rootfs通常包含一个操作系统运行所需的文件系统,例如可能包含经典的类Unix操作系统中的目录系统,如/dev、/proc、/bin、/etc、/lib、/usr、/tmp及运行Docker容器所需的配置文件、工具等。

12. linux namespace

Namespace(命名空间)是 Linux 内核提供的一种 隔离机制。

它把同一台 Linux 内核分成多个"虚拟的独立环境",每个环境里的进程都认为自己是独立的。

(大寝室->单间)

  1. PID Namespace
    隔离进程号(PID),容器里看到的进程号是从 1 开始的。
    好处:容器里的 ps -ef 看不到宿主机的进程。
  2. NET Namespace
    隔离网络协议栈(IP 地址、路由表、端口)。
    好处:容器里可以有独立的 IP 地址,绑定自己的端口。
  3. IPC Namespace
    隔离进程间通信(消息队列、信号量、共享内存)。
    好处:容器之间不能随便共享内存区。
  4. UTS Namespace
    隔离主机名和域名。
    好处:容器里可以设置自己的 hostname。
  5. MNT Namespace
    隔离文件系统挂载点。
    什么是挂载(mount)
    在 Linux/Unix 系统里,存储设备(硬盘分区、U盘、ISO 镜像等)上有自己的 文件系统。
    这些文件系统默认和系统根目录 / 是分开的,系统本身不能直接访问。
    挂载(mount) 就是把一个文件系统 "接到" 某个目录上,让操作系统能通过这个目录访问它的内容。
    挂载点(mount point)
    挂载点就是一个空目录,挂载文件系统后,这个目录就显示挂载文件系统的内容。
    比喻:
    系统根目录 / 是房子的大厅。
    外接硬盘有自己的内容。
    挂载点就是把硬盘"搬进"大厅的某个房间,比如 /mnt/usb,进去这个房间就能看到硬盘里的文件。)
    好处:容器有自己的 / 根目录,看不到宿主机完整的文件系统。)
  6. USER Namespace
    隔离用户和用户组 ID。
    好处:容器里的 root 用户,可以映射成宿主机的普通用户,增强安全性。

Docker Registry
Docker Host
Docker Client
Docker Engine
Components
docker command
HTTP REST API
docker CLI
Docker SDK (API)
Docker Daemon (dockerd)
Images
Containers
Docker Hub / Private Registry

13. 进程命名空间

这里直接拉取ubuntu,在里面进行命令操作

shell 复制代码
docker pull ubuntu:22.04

#启动ubuntu容器
docker run -it --privileged ubuntu:22.04 /bin/bash
#-it:交互式终端。
#--privileged:允许容器访问更多 Linux 功能,比如命名空间。
#/bin/bash:进入 bash
# 这个时候命令行会变成:root@<container_id>:/#

#安装命令包
apt update
apt install -y util-linux

#然后就可以运行命令
lsns -p <PID>

#退出容器
exit
#直接退出之后,虽然镜像在,但是容器即这个实例没了,之前操作也没有了(默认是不删除的),所以最好保存一下(哪怕只是重命名好记一点)
docker run -it --name myubuntu --privileged ubuntu:22.04 /bin/bash
#以后启动:
docker start -ai myubuntu

# 重命名
docker rename quirky_sammet myubuntu
# 以后可以用:
docker start -ai myubuntu

#在其他终端还想连这个docker
docker exec -it myubuntu /bin/bash
  1. 查看宿主机命名空间
shell 复制代码
lsns

显示所有命名空间(PID, NET, UTS, IPC, USER, MNT...)

  1. 查看某个进程命名空间
shell 复制代码
# pid=1的命名空间
lsns -p 1

#查看符号连接
ls -l /proc/1/ns
  1. 查看当前用户shell的命名空间
shell 复制代码
ls -l /proc/$$/ns
# $$代表当前shell的pid
  1. 查看容器命名空间
shell 复制代码
#获取容器内主进程id
docker inspect -f '{{.State.Pid}}' <容器ID或名字>

# 查看它命名空间
ls -l /proc/12345/ns
  1. 进入容器命名空间
shell 复制代码
nsenter --target 12345 --pid --uts --net --mount
  1. Docker 常见隔离实验操作
shell 复制代码
# (1)查看容器进程(宿主机 vs 容器)
# 宿主机看容器进程:
ps -ef | grep <容器名>
# 容器内看进程(PID 从 1 开始):
docker exec -it <容器> ps -ef

# (2) 网络隔离
# 查看容器网络接口:
docker exec -it <容器> ip addr
# 对比宿主机的:
ip addr

# (3) 挂载点隔离
# 容器里:
mount | head
# 宿主机:
mount | head
# 容器和宿主机看到的文件系统不同。

# (4) UTS 隔离(主机名)
# 宿主机:
hostname
# 容器:
docker exec -it <容器> hostname
# 不同容器有不同 hostname。

# (5) 用户隔离
# 容器里默认 root 其实是 容器内部的 root,和宿主机 root 不一样。
# 可以在容器里 id 查看:
docker exec -it <容器> id

14. cgroups

cgroup全称是control groups,被整合在了linux内核当中,把进程(tasks)放到组里面,对组设置权限,对进程进行控制。可以理解为用户和组的概念,用户会继承它所在组的权限

1. CPU 子系统 (cpu / cpuacct)

在 cgroups 中,cpu 子系统用来限制和调度进程的 CPU 使用率。

常见的控制点有:

cpu.shares:相对权重(权重高的 cgroup 能分到更多 CPU 时间片)。

cpu.cfs_quota_us 和 cpu.cfs_period_us:基于 CFS 调度器的配额机制,决定一个 cgroup 在一段时间内最多能用多少 CPU。

cpuacct:统计 CPU 使用情况(累计使用的 CPU 时间等)。

2. CFS (Completely Fair Scheduler)

CFS = 完全公平调度器,是 Linux 默认的 CPU 调度器。

思路:让所有进程尽量"公平"地分享 CPU 时间。

内部逻辑:维护一个红黑树,把每个进程的 虚拟运行时间 vruntime 排序。运行时间最少的进程最先被调度。

在 cgroups 里,CFS 的参数:

cpu.cfs_period_us:调度周期(默认 100ms = 100000 us)。

cpu.cfs_quota_us:在一个周期内允许使用的时间。

比如:cpu.cfs_quota_us = 20000 且 cpu.cfs_period_us = 100000 → 这个 cgroup 最多能用 20% CPU。

如果 -1,表示不限制。

应用场景:控制容器的 CPU 使用率,避免某个容器把宿主机 CPU 占满。

3. RT (Real-Time scheduling) 子系统

除了 cpu,Linux 还有 cpu.rt_runtime_us 和 cpu.rt_period_us,这是 实时调度 (RT) 的配置。

RT 调度类包括 SCHED_FIFO 和 SCHED_RR,用于需要确定性延迟的实时任务。

参数解释:

cpu.rt_period_us:实时任务的调度周期(比如 1s)。

cpu.rt_runtime_us:在该周期内,允许所有 RT 任务一共能运行的时间。

举个例子:

cpu.rt_period_us = 1000000 (1 秒)

cpu.rt_runtime_us = 200000 (200ms)

→ 那么在 1 秒内,实时任务最多能占用 20% CPU,避免把 CPU 时间全部抢走。

应用场景:

RT 主要用于对延迟极度敏感的任务(工业控制、音视频处理、机器人系统),普通容器一般不用。

shell 复制代码
# 限制某个容器只能用50%cpu

# 创建一个cgroup
# 注意到在这么深的文件夹内新建,因为这是内核挂载的cgroup虚拟文件系统
mkdir /sys/fs/cgroup/cpu/mygroup

# 限制周期100ms
echo 100000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_period_us
# 限制配额50ms -> 50%
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us

# 把进程 12345 加入这个 cgroup
echo 12345 > /sys/fs/cgroup/cpu/mygroup/cgroup.procs
4. 三者关系总结

cpu 子系统:cgroups 的 CPU 控制模块,总管 CPU 使用限制。

CFS:Linux 默认的公平调度器,用 cfs_quota/cfs_period 实现 CPU 限额。

RT:实时调度类,用 rt_runtime/rt_period 来保证实时任务的确定性。

5. cpuset 子系统

作用:控制进程运行在哪些 CPU 核心 和 NUMA 内存节点 上。

关键文件:

cpuset.cpus → 允许使用的 CPU 核(如 0-2,4 表示 CPU0、1、2、4)。

cpuset.mems → 允许使用的内存节点。

例子:让某个进程只能跑在 CPU 0 和 CPU 1:

shell 复制代码
mkdir /sys/fs/cgroup/cpuset/myset
echo 0-1 > /sys/fs/cgroup/cpuset/myset/cpuset.cpus
echo 0 > /sys/fs/cgroup/cpuset/myset/cpuset.mems
echo 12345 > /sys/fs/cgroup/cpuset/myset/cgroup.procs

应用场景:多核环境下做 CPU 绑定(如数据库绑核心,避免抖动)。

6. cpuacct 子系统

作用:统计 CPU 使用情况,通常和 cpu 子系统配合用。

文件:

cpuacct.usage(纳秒级 CPU 时间)

cpuacct.stat(用户态/内核态使用时间)

例子:

shell 复制代码
cat /sys/fs/cgroup/cpuacct/mygroup/cpuacct.usage
cat /sys/fs/cgroup/cpuacct/mygroup/cpuacct.stat

用来做 监控、计费。

7. memory 子系统

作用:限制/统计内存使用。

常见文件:

memory.limit_in_bytes → 内存上限

memory.usage_in_bytes → 当前内存使用量

memory.oom_control → 是否允许 OOM-killer

例子:限制某进程最多用 200MB 内存:

shell 复制代码
mkdir /sys/fs/cgroup/memory/mygroup
echo 200M > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes
echo 12345 > /sys/fs/cgroup/memory/mygroup/cgroup.procs

应用场景:避免某个容器爆内存导致宿主机崩溃。

8. blkio 子系统

作用:控制块设备 I/O 带宽/IOPS。

文件:

blkio.throttle.read_bps_device

blkio.throttle.write_bps_device

blkio.throttle.read_iops_device

例子:限制进程对 /dev/sda 的读速率不超过 1MB/s:

shell 复制代码
echo "8:0 1048576" > /sys/fs/cgroup/blkio/mygroup/blkio.throttle.read_bps_device

应用场景:防止某个任务疯狂读写硬盘,拖慢系统。

9. devices 子系统

作用:控制进程能否访问某些设备(如 /dev/sda, /dev/null)。

文件:

devices.allow

devices.deny

例子:允许访问 /dev/null:

shell 复制代码
echo "c 1:3 rwm" > /sys/fs/cgroup/devices/mygroup/devices.allow

场景:安全隔离,比如容器不能直接访问宿主机硬盘设备。

10. freezer 子系统

作用:挂起/恢复 cgroup 内所有进程。

文件:freezer.state(FROZEN/THAWED)

例子:冻结某个组:

shell 复制代码
echo FROZEN > /sys/fs/cgroup/freezer/mygroup/freezer.state

应用:暂停容器(类似 docker pause)。

11. net_cls 子系统

作用:给进程打上 网络流量 classid,用于 tc (traffic control) 匹配。

文件:net_cls.classid

例子:

shell 复制代码
echo 0x10001 > /sys/fs/cgroup/net_cls/mygroup/net_cls.classid

应用:不同容器流量走不同的 QoS 策略。

12. net_prio 子系统

作用:设置网络接口的优先级。

文件:net_prio.ifpriomap

例子:

shell 复制代码
echo "eth0 5" > /sys/fs/cgroup/net_prio/mygroup/net_prio.ifpriomap

应用:保证关键容器(如数据库)的网络优先级高。

总结表格

子系统 功能 例子/应用场景
cpu 限制 CPU 使用率 限制容器只能用 50% CPU
cpuset 绑定 CPU/内存节点 数据库绑核
cpuacct 统计 CPU 使用 监控/计费
memory 限制内存 防止 OOM
blkio 限制磁盘 IO 防止容器拖慢磁盘
devices 控制设备访问 禁止容器访问宿主硬盘
freezer 挂起/恢复进程 docker pause
net_cls 打标签,配合 tc 做流量控制 QoS
net_prio 设置网络优先级 关键服务优先

15. docker常用命令

1.环境信息
shell 复制代码
docker info

docker version
2. 系统日志
shell 复制代码
docker events : 从服务器获取实时事件
# OPTIONS说明:
# -f :根据条件过滤事件;
# --since :从指定的时间戳后显示所有事件;
# --until :流水时间显示到指定的时间为止;
# //第一个终端执行
# docker events
# //第二个终端操作容器
# docker start/stop/restart
# //查看第一个终端输出

# 获取容器日志
docker logs

# 查看指定镜像的创建历史
docker history
3. 容器生命周期
shell 复制代码
# 创建一个新容器但是不启动
docker create

# 创建一个新的容器并运行一个命令
docker run

#启动一个或多个已经被停止的容器
docker start 

# 停止一个运行中的容器(发送信号给主进程,可能造成数据未保存)
docker stop 

# 重启容器
docker restart 

# 杀掉一个运行中的容器(容器马上退出,可能造成数据没保存)
docker kill 

# 删除一个或多个容器。
docker rm

# 暂停容器中所有的进程。
docker pause 

# 恢复容器中所有的进程
docker unpause 
4. 运维
shell 复制代码
# 在运行的容器中执行命令
docker exec

# 列出容器
docker ps

# 获取容器/镜像的元数据
docker inspect

# 查看容器中运行的进程信息
docker top

# 连接到正在运行中的容器
docker attach

# 阻塞运行直到容器停止,然后打印出它的退出代码
docker wait

# 将文件系统作为一个tar归档文件导出到STDOUT
docker export

# 用于容器和主机之间的数据拷贝
docker cp

# 检查容器里面文件结构的更改
docker diff

# 重命名
docker rename

# 查看状态
docker stats

# docker update 命令用于 动态修改已经运行的容器的资源限制,不需要重新创建容器
docker update
5. 镜像管理
shell 复制代码
# 命令用于使用 Dockerfile 创建镜像
docker build

# 列出本地镜像
docker images

# 删除本地一个或多个镜像
docker rmi

# 标记本地镜像,将其归入某一仓库
docker tag

# 将指定镜像保存成 tar 归档文件
docker save

# 导入使用 docker save 命令导出的镜像
docker load

# 从归档文件中创建镜像
docker import

# 从容器创建一个新的镜像
docker commit
6. 镜像仓库
shell 复制代码
#登陆/登出到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub
docker login/logout

# 从镜像仓库中拉取或者更新指定镜像
docker pull

# 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库
docker push

# 从Docker Hub查找镜像
docker search
相关推荐
God__is__a__girl3 小时前
Docker Desktop 在 Windows 上启动失败:500 Internal Server Error 完整排查与修复指南
windows·docker·容器
海参崴-3 小时前
C++ 位运算从入门到精通(全知识点+面试题+实战应用)
开发语言·c++
摸鱼的后端3 小时前
Docker容器中Kingbase数据库授权到期更换解决方案
数据库·docker·容器
青岛少儿编程-王老师3 小时前
CCF编程能力等级认证GESP—C++1级—20260314
开发语言·c++
我还为发觉3 小时前
Linux 监控可视化|Prometheus+Node Exporter 一键部署教程
linux·运维·prometheus
liu****3 小时前
LangChain-AI应用开发框架(一)
c++·python·langchain·本地部署大模型
承渊政道3 小时前
【优选算法】(实战剖析链表核心操作技巧)
开发语言·数据结构·c++·vscode·学习·算法·链表
代码改善世界3 小时前
【C++初阶】string类(二):常用接口全解析
开发语言·c++
stolentime3 小时前
树套树+标记永久化:[POI 2006] TET-Tetris 3D&&SPOJ1741 TETRIS3D - Tetris 3D题解
c++·算法·线段树·树套树·标记永久化