diff --git a/03_install/3.1_ubuntu.md b/03_install/3.1_ubuntu.md index 5c880b7ff..8a1508316 100644 --- a/03_install/3.1_ubuntu.md +++ b/03_install/3.1_ubuntu.md @@ -122,7 +122,12 @@ $ sudo systemctl start docker ### 3.1.5 建立 docker 用户组 -默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 +默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好的做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 + +> ⚠️ **安全警告:`docker` 用户组等同于 `root` 权限** +> +> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。 +> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。 建立 `docker` 组: diff --git a/03_install/3.2_debian.md b/03_install/3.2_debian.md index 833a2bda6..b76228ed5 100644 --- a/03_install/3.2_debian.md +++ b/03_install/3.2_debian.md @@ -113,7 +113,12 @@ $ sudo systemctl start docker ### 3.2.5 建立 docker 用户组 -默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 +默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好的做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 + +> ⚠️ **安全警告:`docker` 用户组等同于 `root` 权限** +> +> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。 +> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。 建立 `docker` 组: diff --git a/03_install/3.3_fedora.md b/03_install/3.3_fedora.md index c8ca96235..ac5a81bff 100644 --- a/03_install/3.3_fedora.md +++ b/03_install/3.3_fedora.md @@ -121,7 +121,12 @@ $ sudo systemctl start docker ### 3.3.5 建立 docker 用户组 -默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 +默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好的做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 + +> ⚠️ **安全警告:`docker` 用户组等同于 `root` 权限** +> +> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。 +> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。 建立 `docker` 组: diff --git a/03_install/3.4_centos.md b/03_install/3.4_centos.md index dfd6d39a7..e6da6f627 100644 --- a/03_install/3.4_centos.md +++ b/03_install/3.4_centos.md @@ -126,7 +126,12 @@ $ sudo systemctl start docker ### 3.4.6 建立 docker 用户组 -默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 +默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好的做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 + +> ⚠️ **安全警告:`docker` 用户组等同于 `root` 权限** +> +> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。 +> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。 建立 `docker` 组: diff --git a/03_install/3.5_raspberry-pi.md b/03_install/3.5_raspberry-pi.md index 51a198a52..d2bfc6944 100644 --- a/03_install/3.5_raspberry-pi.md +++ b/03_install/3.5_raspberry-pi.md @@ -140,7 +140,12 @@ $ sudo systemctl start docker ### 3.5.5 建立 docker 用户组 -默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 +默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好的做法是将需要使用 `docker` 的用户加入 `docker` 用户组。 + +> ⚠️ **安全警告:`docker` 用户组等同于 `root` 权限** +> +> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。 +> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。 建立 `docker` 组: diff --git a/06_repository/6.1_dockerhub.md b/06_repository/6.1_dockerhub.md index 5abc1af7f..85d01fdf6 100644 --- a/06_repository/6.1_dockerhub.md +++ b/06_repository/6.1_dockerhub.md @@ -93,14 +93,13 @@ $ docker push username/myapp:v1 在 Account Settings -> Security 中启用 2FA,保护账号安全。启用后,CLI 登录需要使用 **Access Token** 而非密码。 #### 2. 使用 Access Token +> **⚠️ 警告**:绝不要在脚本或 CI/CD 系统中,直接使用 `-p` 参数传递密码或 Token (类似 `docker login -p xxx`)!这会导致凭证直接暴露在系统的命令历史、进程列表和终端输出中。 -不要在脚本或 CI/CD 中直接使用登录密码。 - -1. 在 Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token。 -2. 使用 Token 作为密码登录: +1. 在 Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token (PAT)。 +2. 将 Token 通过标准输入 (stdin) 安全传递给 Docker: ```bash -$ docker login -u username -p dckr_pat_xxxxxxx +$ echo "dckr_pat_xxxxxxx" | docker login --username username --password-stdin ``` #### 3. 关注镜像漏洞 diff --git a/08_data/8.1_volume.md b/08_data/8.1_volume.md index 4295ee4e0..379e058da 100644 --- a/08_data/8.1_volume.md +++ b/08_data/8.1_volume.md @@ -138,9 +138,11 @@ $ docker run -d \ | 特性 | --mount | -v | |------|---------|-----| | 语法 | 键值对,更清晰 | 冒号分隔,更简洁 | -| 自动创建卷 | source 不存在会报错 | 自动创建 | +| 自动创建卷 | source 不存在会自动创建 | 自动创建 | | 推荐程度 | ✅ 推荐 (更明确)| 常用 (更简洁)| +> **提示**:很多人误以为 `--mount` 遇到目标不存在时总是报错。实际上,那仅适用于**绑定挂载 (Bind Mount)**。对于**数据卷 (Volume)**,只要 `source` 指定的卷名称不存在,Docker 都会默默将其创建出来。 + #### 只读挂载 ```bash diff --git a/08_data/8.2_bind-mounts.md b/08_data/8.2_bind-mounts.md index dcbe234a9..257560c54 100644 --- a/08_data/8.2_bind-mounts.md +++ b/08_data/8.2_bind-mounts.md @@ -74,9 +74,11 @@ $ docker run -d \ | 特性 | --mount | -v | |------|---------|-----| | 语法 | 键值对,更清晰 | 冒号分隔,更简洁 | -| 路径不存在时 | 报错 | 自动创建目录 | +| 路径不存在时 | 直接报错 (Fail Fast) | 静默自动创建**目录** | | 推荐程度 | ✅ 推荐 | 常用 | +> **⚠️ 陷阱**:如果不小心挂载了一个不存在的宿主机路径,使用 `-v` 会在宿主机上静默创建一个**空目录**(即使你本来想挂载的是一个文件),这常常会导致权限错误或应用无法正常读取。这也正是为什么 Docker 官方更推荐使用 `--mount` 的原因:它会遵循“Fail Fast”原则直接报错,避免弄巧成拙。 + --- ### 8.2.4 使用场景 diff --git a/09_network/9.1_dns.md b/09_network/9.1_dns.md index fd6416e9c..38357a145 100644 --- a/09_network/9.1_dns.md +++ b/09_network/9.1_dns.md @@ -4,7 +4,7 @@ Docker 1.10.0 以后,内建了一个 DNS 服务器,使得容器可以直接 但是使用 Docker DNS 有个前提条件,就是它只能在 **自定义网络** 中使用。也就是说,如果使用的是默认的 `bridge` 网络,是无法使用 DNS 的,所以我们就需要自定义网络。 -### 9.2.1 容器的 DNS 机制 +### 9.1.1 容器的 DNS 机制 Docker 容器的 DNS 配置有两种情况: @@ -13,7 +13,7 @@ Docker 容器的 DNS 配置有两种情况: --- -### 9.2.2 嵌入式 DNS +### 9.1.2 嵌入式 DNS 这是 Docker 网络最强大的功能之一。在自定义网络中,容器可以通过 “名字” 找到彼此,而不需要知道对方的 IP (因为 IP 可能会变)。 @@ -38,7 +38,7 @@ Docker 守护进程在 `127.0.0.11` 运行了一个 DNS 服务器。容器内的 --- -### 9.2.3 配置 DNS 参数 +### 9.1.3 配置 DNS 参数 如果你需要手动配置容器的 DNS (例如使用内网 DNS 服务器),可以在 `docker run` 中使用以下参数: @@ -69,7 +69,7 @@ $ docker run -h myweb nginx --- -### 9.2.4 全局 DNS 配置 +### 9.1.4 全局 DNS 配置 如果希望所有容器都使用特定的 DNS 服务器 (而不是继承宿主机),可以修改 `/etc/docker/daemon.json`: @@ -86,7 +86,7 @@ $ docker run -h myweb nginx --- -### 9.2.5 常见问题 +### 9.1.5 常见问题 以下是使用容器 DNS 时常见的问题及解决方法: diff --git a/10_buildx/10.2_buildx.md b/10_buildx/10.2_buildx.md index 3dea09d00..f231ac2ba 100644 --- a/10_buildx/10.2_buildx.md +++ b/10_buildx/10.2_buildx.md @@ -38,6 +38,15 @@ $ docker buildx build --sbom=true -t myimage . 该命令会在构建结果中包含 SPDX 或 CycloneDX 格式的 SBOM 数据。 +> **⚠️ 注意与失败模式**: +> 要使 SBOM (或其它 attestation 元数据) 成功附着并可见,对底层的存储格式有前置要求:默认的 classic image store 不支持 manifest list/index 这种存放 attestation 的结构。 +> +> 如果只简单运行上述命令,你可能会面临**“命令成功执行,但本地镜像中看不到 SBOM”**的体会落差。 +> +> **正确的解决路径有两条**: +> 1. 在 Docker 守护进程中启用 `containerd image store` 特性(现代 Docker Desktop 默认推荐)。 +> 2. 或者使用 `docker-container` driver 的构建器,并直接**加上 `--push` 参数**将产物推送到远端支持 OCI 的镜像仓库,仓库会正确保存这些元数据。 + ### 10.2.2 官方文档 * https://docs.docker.com/engine/reference/commandline/buildx/ diff --git a/11_compose/README.md b/11_compose/README.md index 5b3934de5..cf33f8a94 100644 --- a/11_compose/README.md +++ b/11_compose/README.md @@ -2,6 +2,10 @@ `Docker Compose` 是 Docker 官方编排 (Orchestration) 项目之一,负责快速的部署分布式应用。 +> ⚠️ **重要提示:Compose V1 已停止支持** +> +> 早期基于 Python 编写的 Compose V1(命令为 `docker-compose`)已于 2023 年中正式停止支持。现已全面升级为基于 Go 编写的 Compose V2,作为 Docker CLI 的官方插件提供(命令为 `docker compose`,中间为空格)。本书强烈推荐且后续章节均以 V2 为核心标准进行讲解。 + 本章将介绍 `Compose` 项目情况以及安装和使用。 * [简介](11.1_introduction.md) diff --git a/14_kubernetes_setup/14.2_kubeadm-docker.md b/14_kubernetes_setup/14.2_kubeadm-docker.md index 6b89af142..f51ec287e 100644 --- a/14_kubernetes_setup/14.2_kubeadm-docker.md +++ b/14_kubernetes_setup/14.2_kubeadm-docker.md @@ -2,9 +2,13 @@ `kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令,作为快速创建 `Kubernetes` 集群的最佳实践。 -> ⚠️ **重要说明**:自 Kubernetes 1.24 起,内置 `dockershim` 已被移除,Kubernetes 默认不再直接使用 Docker Engine 作为容器运行时 (CRI)。因此,**更推荐参考** 同目录下的《[使用 kubeadm 部署 Kubernetes (CRI 使用 containerd)](14.1_kubeadm.md)》。 +> ⚠️ **强烈提示:Docker 与 Kubernetes 环境的时代分界** > -> 本文档主要用于历史环境/学习目的:如果你确实需要在较新版本中继续使用 Docker Engine,通常需要额外部署 `cri-dockerd` 并在 `kubeadm init/join` 中指定 `--cri-socket`。 +> 自 Kubernetes v1.24 起,内置的 `dockershim` 组件已被正式移除。这意味着 **Kubernetes 不再将 Docker Engine 作为默认内置的容器运行时**。虽然 Docker 仍然是你本地构建、管理镜像的绝佳工具,但它已不再是 kubelet 的默认运行时选项。 +> +> 因此,**强烈推荐** 读者直接参考同目录下的《[使用 kubeadm 部署 Kubernetes (CRI 使用 containerd)](14.1_kubeadm.md)》作为主要的部署路线。 +> +> 本文档保留,主要用于历史环境维护或特殊需求场景:如果你必须在较新的 Kubernetes 集群中继续使用 Docker Engine 作为底层运行时,你必须理解 CRI 层机制,额外部署并配置第三方兼容层 `cri-dockerd`,同时在部署时手动补充 `--cri-socket` 等参数约束。 ### 14.2.1 安装 Docker diff --git a/21_case_devops/21.1_devops_workflow.md b/21_case_devops/21.1_devops_workflow.md index 3531249b2..53cbf9847 100644 --- a/21_case_devops/21.1_devops_workflow.md +++ b/21_case_devops/21.1_devops_workflow.md @@ -58,8 +58,7 @@ build_image: services: - docker:20.10.16-dind script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD \ - $CI_REGISTRY + - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA @@ -81,4 +80,10 @@ deploy_staging: 1. **不可变基础设施**:一旦镜像构建完成,在各个环境(Dev、Staging、Prod)中都应该使用同一个镜像 tag(通常是 commit hash),而不是重新构建。 2. **配置分离**:使用 ConfigMap 和 Secret 管理环境特定的配置,不要打包进镜像。 -3. **GitOps**:考虑引入 ArgoCD,将部署配置也作为代码存储在 Git 中,实现 Git 驱动的部署同步。 +3. **应对 Docker Hub 限额 (Rate Limits)**: + - Docker Hub 对匿名拉取实施了严格的限制 (6小时内约100次)。若在 CI/CD 中频繁构建,极易触发 `toomanyrequests` 错误。 + - **最佳策略**: + - 在流水线开头始终执行安全的身份认证 (使用 PAT,而非密码)。 + - 将常用的基础镜像缓存到自建的 Harbor/Nexus,使用 Pull-Through Cache。 + - 开启 Docker 构建引擎 (BuildKit) 的 inline 或 registry 缓存功能,以降低全量拉取频率。 +4. **GitOps**:考虑引入 ArgoCD,将部署配置也作为代码存储在 Git 中,实现 Git 驱动的部署同步。 diff --git a/README.md b/README.md index b696625e3..2947b546b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![图](https://img.shields.io/github/stars/yeasy/docker_practice.svg?style=social&label=Stars)](https://github.com/yeasy/docker_practice) [![图](https://img.shields.io/github/release/yeasy/docker_practice/all.svg)](https://github.com/yeasy/docker_practice/releases) [![图](https://img.shields.io/badge/Based-Docker%20Engine%20v29.x-blue.svg)](https://docs.docker.com/engine/release-notes/) [![图](https://img.shields.io/badge/Docker%20%E6%8A%80%E6%9C%AF%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E6%88%98-jd.com-red.svg)][1] -**v1.6.0** +**v1.6.1** [Docker](https://www.docker.com) 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的维护效率,降低了云计算应用开发的成本!使用 Docker,可以让应用的部署、测试和分发都变得前所未有的高效和轻松! diff --git a/appendix/README.md b/appendix/README.md index a0c0fce25..4b13b853e 100644 --- a/appendix/README.md +++ b/appendix/README.md @@ -11,3 +11,4 @@ * [**如何调试 Docker**](debug.md):介绍 Docker 调试技巧和工具。 * [**资源链接**](resources.md):推荐更多 Docker 相关的学习资源。 * [**术语词表**](glossary.md):统一全书中英文术语、缩写与命令写法。 +* [**示例规范与写作公约**](example_guidelines.md):全书修改与贡献必须遵守的安全、格式及内容基线要求。 diff --git a/appendix/example_guidelines.md b/appendix/example_guidelines.md new file mode 100644 index 000000000..6ba443f8c --- /dev/null +++ b/appendix/example_guidelines.md @@ -0,0 +1,41 @@ +# 附录八:本书示例规范与写作公约 + +为了保证本书在漫长的技术迭代中保持高质量、高安全性以及良好的可读性,我们确立了以下项目写作规范。所有的修改与贡献都必须遵循这些原则。 + +## 1. 安全与凭据处理 + +任何示例文档中都**严禁出现将凭据以明文形式直接传递给命令参数**的做法。 + +* **错误示例**:`docker login -u myuser -p mysecretpassword` (这会导致密码泄露到历史记录及进程列表中) +* **正确示例(交互式)**:推荐读者直接使用 `docker login` (优先使用官方 Device Code Flow)。 +* **正确示例(自动化)**:使用标准输入流传递 Token:`echo $PAT | docker login -u myuser --password-stdin` + +## 2. 失败模式与前置条件说明 + +许多现代 Docker 功能 (如 Buildx, SBOM, Kubernetes CRI 集成) 均需要特定的后端存储或者组件支持。在介绍这些进阶命令时,必须: + +* **明确声明前置条件**:例如,“此功能需要开启 containerd image store” 或 “需要额外配置 `--push`”。 +* **描述失败现象**:明确告诉读者,“如果你不这么配置,命令仍会提示成功,但产物将不可见/被丢弃”。 + +## 3. 统一使用 "docker compose" 命令 + +早期 Python 编写的 `docker-compose` (V1) 已停止支持。 + +* **全书统一标准**:所有的编排命令必须书写为 `docker compose` (带空格的 V2 CLI 插件版)。 +* 不得在新的文档和案例中提及或使用旧版格式,除非是为了特意说明 V1 到 V2 的迁移。 + +## 4. 可复现性目标 (以可重建为目标) + +本书中的所有实战和案例 (尤其 OS 与 DevOps 章节) 应尽量给出“最小可复现实验环境”。 + +* 提供明确的基础镜像版本要求 (不要盲目依赖 `latest`)。 +* 明确宿主机的生命周期窗口(例如明确注出 Ubuntu 20.04 的支持结束时间)。 + +## 5. Markdown 格式纪律 + +我们使用自动化的格式检查工具 (`check_project_rules.py`) 进行 Lint。以下是必须遵守的排版底线: + +* **加粗与空格**:加粗符号内不得有空格(`**错误 黑体**` ❌, `**正确黑体**` ✅)。 +* **标题间距**:标题后必须跟恰好一个空行。 +* **中文标点**:正文叙述部分的引号必须使用中文弯引号 `“”`,不得使用英文直引号 `""`(除了代码块或 Mermaid 图表中)。 +* **层级与桥接**:禁止跳跃级别使用标题 (如 H2 后直接接 H4);且带有子标题的章节,在进入第一个子标题前,必须要有引导/过渡段落介绍该节内容。