mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-14 05:41:11 +00:00
Compare commits
9 Commits
9fcaff6673
...
a566decb94
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a566decb94 | ||
|
|
70ef2cba58 | ||
|
|
f5cfa4140a | ||
|
|
d174cf327c | ||
|
|
6a9ce44c5a | ||
|
|
c09f66da55 | ||
|
|
635e05ad34 | ||
|
|
6f22d0f5f0 | ||
|
|
17517e26b7 |
@@ -102,7 +102,7 @@ $ docker compose up
|
|||||||
|
|
||||||
Docker 容器共享宿主机内核,无需为每个应用运行完整的操作系统。以一台 64GB 内存的物理服务器为例:
|
Docker 容器共享宿主机内核,无需为每个应用运行完整的操作系统。以一台 64GB 内存的物理服务器为例:
|
||||||
- **传统虚拟机方案**:每个虚拟机都需要运行完整的操作系统(每个额外占用如 2GB 内存),产生大量资源开销,实际可用于应用的内存可能只有约 18GB。
|
- **传统虚拟机方案**:每个虚拟机都需要运行完整的操作系统(每个额外占用如 2GB 内存),产生大量资源开销,实际可用于应用的内存可能只有约 18GB。
|
||||||
- **Docker 方案**:容器直接共享宿主机系统,只需付出很少的基础开销(OS及引擎约 4GB),即可将约 60GB 的内存全部用于实际应用。
|
- **Docker 方案**:容器直接共享宿主机系统,只需付出很少的基础开销(OS 及引擎约 4GB),即可将约 60GB 的内存全部用于实际应用。
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
本章将带领你进入 **Docker** 的世界。
|
本章将带领你进入 **Docker** 的世界。
|
||||||
|
|
||||||
> **版本提示**:本书内容及示例基于 **Docker Engine v29.x** 及以上版本。值得注意的是,自 Docker Engine v29 起,官方在全新安装场景下**默认启用了 `containerd image store` 作为镜像存储后端**(取代了传统的经典存储引擎如 overlay2 graph driver)。这项底层革新极大增强了 Docker 对多架构镜像(Multi-platform)、以及软件供应链安全元数据(Attestations, SBOM, Provenance)的本地支持原生性。
|
> **版本提示**:本书内容及示例基于 **Docker Engine v29.x** 及以上版本。值得注意的是,自 Docker Engine v29 起,官方在全新安装场景下 **默认启用了 `containerd image store` 作为镜像存储后端**(取代了传统的经典存储引擎如 overlay2 graph driver)。这项底层革新极大增强了 Docker 对多架构镜像(Multi-platform)、以及软件供应链安全元数据(Attestations, SBOM, Provenance)的本地支持原生性。
|
||||||
|
|
||||||
## 本章内容
|
## 本章内容
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ flowchart TB
|
|||||||
一个完整的 Docker 镜像名称由 Registry 地址、用户名/组织名、仓库名和标签组成。了解其结构有助于我们更准确地定位镜像。基本格式如下:
|
一个完整的 Docker 镜像名称由 Registry 地址、用户名/组织名、仓库名和标签组成。了解其结构有助于我们更准确地定位镜像。基本格式如下:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[registry地址/][用户名/]仓库名[:标签]
|
[registry 地址/][用户名/]仓库名[:标签]
|
||||||
```
|
```
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|||||||
@@ -7,3 +7,9 @@
|
|||||||
* **仓库** (`Repository`):镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
|
* **仓库** (`Repository`):镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
|
||||||
|
|
||||||
理解了这三个概念,就理解了 **Docker** 的整个生命周期。
|
理解了这三个概念,就理解了 **Docker** 的整个生命周期。
|
||||||
|
|
||||||
|
## 本章内容
|
||||||
|
|
||||||
|
* [Docker 镜像](2.1_image.md)
|
||||||
|
* [Docker 容器](2.2_container.md)
|
||||||
|
* [Docker 仓库](2.3_repository.md)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
- [Dockerfile 最佳实践](../appendix/best_practices.md):构建高质量镜像的技巧
|
- [Dockerfile 最佳实践](../appendix/best_practices.md):构建高质量镜像的技巧
|
||||||
- [底层实现 - 联合文件系统](../12_implementation/12.4_ufs.md):深入理解分层存储的技术原理
|
- [底层实现 - 联合文件系统](../12_implementation/12.4_ufs.md):深入理解分层存储的技术原理
|
||||||
- [启动容器](../05_container/5.1_run.md):详细的容器启动选项
|
- [启动容器](../05_container/5.1_run.md):详细的容器启动选项
|
||||||
- [后台运行](../05_container/5.2_daemon.md):理解容器为什么会"立即退出"
|
- [后台运行](../05_container/5.2_daemon.md):理解容器为什么会“立即退出”
|
||||||
- [进入容器](../05_container/5.4_attach_exec.md):如何操作运行中的容器
|
- [进入容器](../05_container/5.4_attach_exec.md):如何操作运行中的容器
|
||||||
- [数据管理](../08_data/README.md):Volume 和数据持久化详解
|
- [数据管理](../08_data/README.md):Volume 和数据持久化详解
|
||||||
- [Docker Hub](../06_repository/6.1_dockerhub.md):Docker Hub 的详细使用
|
- [Docker Hub](../06_repository/6.1_dockerhub.md):Docker Hub 的详细使用
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
下载好之后双击 `Docker Desktop Installer.exe` 开始安装。
|
下载好之后双击 `Docker Desktop Installer.exe` 开始安装。
|
||||||
|
|
||||||
**使用** [**winget**](https://docs.microsoft.com/zh-cn/windows/package-manager/) **安装**
|
**使用**[**winget**](https://docs.microsoft.com/zh-cn/windows/package-manager/)**安装**
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$ winget install Docker.DockerDesktop
|
$ winget install Docker.DockerDesktop
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -77,9 +77,9 @@ $ docker push username/myapp:v1
|
|||||||
| **免费账户** (已登录) | 每 6 小时 200 次请求 |
|
| **免费账户** (已登录) | 每 6 小时 200 次请求 |
|
||||||
| **Pro/Team 账户** | 无限制 |
|
| **Pro/Team 账户** | 无限制 |
|
||||||
|
|
||||||
#### 滥用限流 (Abuse Rate Limit)
|
#### 滥用限流
|
||||||
|
|
||||||
除了上述针对特定账号拉取镜像数量的 Pull Rate Limit 之外,Docker Hub 对所有用户(包含已认证及付费用户)还实施了**滥用保护限流 (Abuse Rate Limiting)**。它是根据网络出口 IP (IPv4 或 IPv6 /64 子网) 计算整体请求频率,阈值动态触发(通常为每分钟数千级别请求)。
|
除了上述针对特定账号拉取镜像数量的 Pull Rate Limit 之外,Docker Hub 对所有用户(包含已认证及付费用户)还实施了 **滥用保护限流 (Abuse Rate Limiting)**。它是根据网络出口 IP (IPv4 或 IPv6 /64 子网) 计算整体请求频率,阈值动态触发(通常为每分钟数千级别请求)。
|
||||||
|
|
||||||
**两类的差异与排查方法**:
|
**两类的差异与排查方法**:
|
||||||
- **Pull Rate Limit**:针对拉取量达到上限。报错返回 `429 Too Many Requests`,并且 HTTP 返回体/CLI 错误提示中会带有明确的 `toomanyrequests: You have reached your pull rate limit` 提示,常附有账户升级链接。
|
- **Pull Rate Limit**:针对拉取量达到上限。报错返回 `429 Too Many Requests`,并且 HTTP 返回体/CLI 错误提示中会带有明确的 `toomanyrequests: You have reached your pull rate limit` 提示,常附有账户升级链接。
|
||||||
@@ -102,6 +102,7 @@ $ docker push username/myapp:v1
|
|||||||
在 Account Settings -> Security 中启用 2FA,保护账号安全。启用后,CLI 登录需要使用 **Access Token** 而非密码。
|
在 Account Settings -> Security 中启用 2FA,保护账号安全。启用后,CLI 登录需要使用 **Access Token** 而非密码。
|
||||||
|
|
||||||
#### 2. 使用 Access Token
|
#### 2. 使用 Access Token
|
||||||
|
|
||||||
> **⚠️ 警告**:绝不要在脚本或 CI/CD 系统中,直接使用 `-p` 参数传递密码或 Token (类似 `docker login -p xxx`)!这会导致凭证直接暴露在系统的命令历史、进程列表和终端输出中。
|
> **⚠️ 警告**:绝不要在脚本或 CI/CD 系统中,直接使用 `-p` 参数传递密码或 Token (类似 `docker login -p xxx`)!这会导致凭证直接暴露在系统的命令历史、进程列表和终端输出中。
|
||||||
|
|
||||||
1. 在 Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token (PAT)。
|
1. 在 Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token (PAT)。
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,7 @@
|
|||||||
| 功能 | 说明 |
|
| 功能 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **官方镜像** | 优先使用的基础镜像 |
|
| **官方镜像** | 优先使用的基础镜像 |
|
||||||
| **拉取限制** | 匿名 100次/6h,登录 200次/6h |
|
| **拉取限制** | 匿名 100 次/6h,登录 200 次/6h |
|
||||||
| **安全** | 推荐开启 2FA 并使用 Access Token |
|
| **安全** | 推荐开启 2FA 并使用 Access Token |
|
||||||
| **自动化** | 支持 Webhooks 和自动构建 |
|
| **自动化** | 支持 Webhooks 和自动构建 |
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,36 @@
|
|||||||
## 7.16 参考文档
|
## 7.16 参考文档
|
||||||
|
|
||||||
* `Dockerfile` 官方文档:https://docs.docker.com/engine/reference/builder/
|
### 官方文档
|
||||||
|
|
||||||
* `Dockerfile` 最佳实践文档:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
|
* `Dockerfile` 官方参考手册:https://docs.docker.com/engine/reference/builder/
|
||||||
|
|
||||||
* `Docker` 官方镜像 `Dockerfile`:https://github.com/docker-library/docs
|
* `Dockerfile` 最佳实践指南:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
|
||||||
|
|
||||||
|
* `Docker` 官方镜像 `Dockerfile` 库:https://github.com/docker-library/docs
|
||||||
|
|
||||||
|
### 常用指令总结
|
||||||
|
|
||||||
|
Dockerfile 中的常用指令包括:
|
||||||
|
|
||||||
|
- **FROM**: 指定基础镜像,必须是第一条指令
|
||||||
|
- **RUN**: 在镜像中执行命令,用于安装软件包等
|
||||||
|
- **WORKDIR**: 设置工作目录
|
||||||
|
- **COPY/ADD**: 复制文件到镜像中
|
||||||
|
- **EXPOSE**: 声明容器监听的端口
|
||||||
|
- **ENV**: 设置环境变量
|
||||||
|
- **ENTRYPOINT**: 容器启动时的入口点
|
||||||
|
- **CMD**: 容器默认执行的命令
|
||||||
|
|
||||||
|
### 最佳实践建议
|
||||||
|
|
||||||
|
1. 使用具体的基础镜像版本标签而非 latest
|
||||||
|
2. 最小化镜像层数,合并 RUN 指令
|
||||||
|
3. 使用 .dockerignore 文件排除不必要的文件
|
||||||
|
4. 安装必要的软件包后清理缓存
|
||||||
|
5. 使用多阶段构建减小最终镜像体积
|
||||||
|
6. 避免以 root 身份运行容器应用
|
||||||
|
|
||||||
|
### 相关资源
|
||||||
|
|
||||||
|
- Docker 官方镜像库:https://hub.docker.com/
|
||||||
|
- Docker 镜像构建最佳实践:https://docs.docker.com/build/building/best-practices/
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
|
|||||||
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7.17 使用多阶段构建
|
### 7.17.3 使用多阶段构建
|
||||||
|
|
||||||
为解决以上问题,Docker v17.05 开始支持多阶段构建 (`multistage builds`)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 `Dockerfile`:
|
为解决以上问题,Docker v17.05 开始支持多阶段构建 (`multistage builds`)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 `Dockerfile`:
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
|||||||
|
|
||||||
很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。
|
很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。
|
||||||
|
|
||||||
### 7.17.1 只构建某一阶段的镜像
|
### 7.17.4 只构建某一阶段的镜像
|
||||||
|
|
||||||
我们可以使用 `as` 来为某一阶段命名,例如
|
我们可以使用 `as` 来为某一阶段命名,例如
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ FROM golang:alpine as builder
|
|||||||
$ docker build --target builder -t username/imagename:tag .
|
$ docker build --target builder -t username/imagename:tag .
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.17.2 构建时从其他镜像复制文件
|
### 7.17.5 构建时从其他镜像复制文件
|
||||||
|
|
||||||
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。
|
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,15 @@ Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像
|
|||||||
* [LABEL 为镜像添加元数据](7.14_label.md)
|
* [LABEL 为镜像添加元数据](7.14_label.md)
|
||||||
* [SHELL 指令](7.15_shell.md)
|
* [SHELL 指令](7.15_shell.md)
|
||||||
|
|
||||||
|
### 高级特性
|
||||||
|
|
||||||
|
本章还将介绍 Dockerfile 的高级特性:
|
||||||
|
|
||||||
|
* [多阶段构建](7.17_multistage_builds.md)
|
||||||
|
* [多阶段构建实战:Laravel 应用](7.18_multistage_builds_laravel.md)
|
||||||
|
|
||||||
|
### 参考与最佳实践
|
||||||
|
|
||||||
此外,我们还将介绍 Dockerfile 的最佳实践和常见问题。
|
此外,我们还将介绍 Dockerfile 的最佳实践和常见问题。
|
||||||
|
|
||||||
* [参考文档](7.16_references.md)
|
* [参考文档](7.16_references.md)
|
||||||
|
|||||||
@@ -139,10 +139,10 @@ $ docker run -d \
|
|||||||
|------|---------|-----|
|
|------|---------|-----|
|
||||||
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
|
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
|
||||||
| **数据卷 (Volume)** 挂载行为 | 卷不存在会自动创建,与 `-v` 结果一致 | 卷不存在会自动创建 |
|
| **数据卷 (Volume)** 挂载行为 | 卷不存在会自动创建,与 `-v` 结果一致 | 卷不存在会自动创建 |
|
||||||
| **绑定挂载 (Bind Mount)** 行为 | ⭐ **宿主机路径不存在会报错**,不会自动创建 | 宿主机路径不存在会**自动创建为目录** |
|
| **绑定挂载 (Bind Mount)** 行为 | ⭐**宿主机路径不存在会报错**,不会自动创建 | 宿主机路径不存在会 **自动创建为目录** |
|
||||||
| 推荐程度 | ✅ 推荐 (更明确安全,避免误创建)| 常用 (更简洁)|
|
| 推荐程度 | ✅ 推荐 (更明确安全,避免误创建)| 常用 (更简洁)|
|
||||||
|
|
||||||
> **提示**:官方更推荐使用 `--mount`。除了语法格式可读性更好之外,最重要的行为差异发生在 **绑定挂载 (Bind Mount)** 时:如果挂载的宿主机源路径尚未存在,`-v` 会擅自将其自动创建为一个空目录;而 `--mount` 则会严格检查并直接报错。这能有效避免因路径拼写错误而在宿主机上留下垃圾目录(以及导致的容器访问空目录问题)。而对于本节的**数据卷 (Volume)** 挂载而言,两者在目标指定的卷不存在时皆会自动创建卷,产生的结果是**完全一致**的。
|
> **提示**:官方更推荐使用 `--mount`。除了语法格式可读性更好之外,最重要的行为差异发生在 **绑定挂载 (Bind Mount)** 时:如果挂载的宿主机源路径尚未存在,`-v` 会擅自将其自动创建为一个空目录;而 `--mount` 则会严格检查并直接报错。这能有效避免因路径拼写错误而在宿主机上留下垃圾目录(以及导致的容器访问空目录问题)。而对于本节的 **数据卷 (Volume)** 挂载而言,两者在目标指定的卷不存在时皆会自动创建卷,产生的结果是 **完全一致** 的。
|
||||||
|
|
||||||
#### 只读挂载
|
#### 只读挂载
|
||||||
|
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ $ docker run -d \
|
|||||||
| 特性 | --mount | -v |
|
| 特性 | --mount | -v |
|
||||||
|------|---------|-----|
|
|------|---------|-----|
|
||||||
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
|
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
|
||||||
| 路径不存在时 | 直接报错 (Fail Fast) | 静默自动创建**目录** |
|
| 路径不存在时 | 直接报错 (Fail Fast) | 静默自动创建 **目录** |
|
||||||
| 推荐程度 | ✅ 推荐 | 常用 |
|
| 推荐程度 | ✅ 推荐 | 常用 |
|
||||||
|
|
||||||
> **⚠️ 陷阱**:如果不小心挂载了一个不存在的宿主机路径,使用 `-v` 会在宿主机上静默创建一个**空目录**(即使你本来想挂载的是一个文件),这常常会导致权限错误或应用无法正常读取。这也正是为什么 Docker 官方更推荐使用 `--mount` 的原因:它会遵循“Fail Fast”原则直接报错,避免弄巧成拙。
|
> **⚠️ 陷阱**:如果不小心挂载了一个不存在的宿主机路径,使用 `-v` 会在宿主机上静默创建一个 **空目录**(即使你本来想挂载的是一个文件),这常常会导致权限错误或应用无法正常读取。这也正是为什么 Docker 官方更推荐使用 `--mount` 的原因:它会遵循“Fail Fast”原则直接报错,避免弄巧成拙。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Docker 1.10.0 以后,内建了一个 DNS 服务器,使得容器可以直接
|
|||||||
Docker 容器的 DNS 配置有两种情况:
|
Docker 容器的 DNS 配置有两种情况:
|
||||||
|
|
||||||
1. **默认 Bridge 网络**:继承宿主机的 DNS 配置 (`/etc/resolv.conf`)。
|
1. **默认 Bridge 网络**:继承宿主机的 DNS 配置 (`/etc/resolv.conf`)。
|
||||||
2. **自定义网络** (推荐):使用 Docker 嵌入式 DNS 服务器 (Embedded DNS),支持通过 **容器名** 进行服务发现。
|
2. **自定义网络**(推荐):使用 Docker 嵌入式 DNS 服务器 (Embedded DNS),支持通过 **容器名** 进行服务发现。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
在生产环境中,推荐使用用户自定义网络代替默认的 bridge 网络。自定义网络提供了更好的隔离性和服务发现能力。
|
在生产环境中,推荐使用用户自定义网络代替默认的 bridge 网络。自定义网络提供了更好的隔离性和服务发现能力。
|
||||||
|
|
||||||
### 9.4.1 为什么要用自定义网络
|
### 9.3.1 为什么要用自定义网络
|
||||||
|
|
||||||
默认 bridge 网络存在以下局限,而自定义网络可以很好地解决这些问题:
|
默认 bridge 网络存在以下局限,而自定义网络可以很好地解决这些问题:
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
| 所有容器在同一网络 | 更好的隔离性 |
|
| 所有容器在同一网络 | 更好的隔离性 |
|
||||||
| 需要 --link (已废弃)| 原生支持服务发现 |
|
| 需要 --link (已废弃)| 原生支持服务发现 |
|
||||||
|
|
||||||
### 9.4.2 创建自定义网络
|
### 9.3.2 创建自定义网络
|
||||||
|
|
||||||
使用 `docker network create` 命令可以创建自定义网络:
|
使用 `docker network create` 命令可以创建自定义网络:
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ $ docker network create mynet
|
|||||||
$ docker network inspect mynet
|
$ docker network inspect mynet
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.4.3 使用自定义网络
|
### 9.3.3 使用自定义网络
|
||||||
|
|
||||||
启动容器时通过 `--network` 参数指定连接的网络:
|
启动容器时通过 `--network` 参数指定连接的网络:
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ PING db (172.18.0.3): 56 data bytes
|
|||||||
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.083 ms
|
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.083 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.4.4 容器名 DNS 解析
|
### 9.3.4 容器名 DNS 解析
|
||||||
|
|
||||||
自定义网络自动提供 DNS 服务。Docker 守护进程在 `127.0.0.11` 运行了一个嵌入式 DNS 服务器,容器内的 DNS 请求会被转发到这里:
|
自定义网络自动提供 DNS 服务。Docker 守护进程在 `127.0.0.11` 运行了一个嵌入式 DNS 服务器,容器内的 DNS 请求会被转发到这里:
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ flowchart LR
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.4.5 常用网络命令
|
### 9.3.5 常用网络命令
|
||||||
|
|
||||||
以下是 Docker 网络管理中常用的命令:
|
以下是 Docker 网络管理中常用的命令:
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
容器之间的网络通信是 Docker 网络的核心功能之一。本节介绍容器互联的几种方式。
|
容器之间的网络通信是 Docker 网络的核心功能之一。本节介绍容器互联的几种方式。
|
||||||
|
|
||||||
### 9.5.1 同一网络内的容器
|
### 9.4.1 同一网络内的容器
|
||||||
|
|
||||||
同一自定义网络内的容器可以直接通过容器名通信,这是推荐的容器互联方式:
|
同一自定义网络内的容器可以直接通过容器名通信,这是推荐的容器互联方式:
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ $ docker run -d --name app --network app-net myapp
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.5.2 连接到多个网络
|
### 9.4.2 连接到多个网络
|
||||||
|
|
||||||
一个容器可以同时连接到多个网络,这对于需要跨网络通信的中间件容器特别有用:
|
一个容器可以同时连接到多个网络,这对于需要跨网络通信的中间件容器特别有用:
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ $ docker network connect backend multi-net-container
|
|||||||
$ docker inspect multi-net-container --format '{{json .NetworkSettings.Networks}}'
|
$ docker inspect multi-net-container --format '{{json .NetworkSettings.Networks}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.5.3 ⚠️ --link 已废弃
|
### 9.4.3 ⚠️ --link 已废弃
|
||||||
|
|
||||||
`--link` 是 Docker 早期用于容器互联的方式,**已经被废弃**,不建议在新项目中使用。请使用自定义网络替代:
|
`--link` 是 Docker 早期用于容器互联的方式,**已经被废弃**,不建议在新项目中使用。请使用自定义网络替代:
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
容器运行在自己的隔离网络环境中 (通常是 Bridge 模式)。为了让外部网络访问容器内的服务,我们需要将容器的端口映射到宿主机的端口。
|
容器运行在自己的隔离网络环境中 (通常是 Bridge 模式)。为了让外部网络访问容器内的服务,我们需要将容器的端口映射到宿主机的端口。
|
||||||
|
|
||||||
### 9.6.1 为什么要映射端口
|
### 9.5.1 为什么要映射端口
|
||||||
|
|
||||||
容器的网络访问规则如下:
|
容器的网络访问规则如下:
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 9.6.2 端口映射方式
|
### 9.5.2 端口映射方式
|
||||||
|
|
||||||
Docker 提供了多种方式来指定端口映射。
|
Docker 提供了多种方式来指定端口映射。
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ abc123456 0.0.0.0:49153->80/tcp
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 9.6.3 查看端口映射
|
### 9.5.3 查看端口映射
|
||||||
|
|
||||||
可以使用以下命令查看容器的端口映射:
|
可以使用以下命令查看容器的端口映射:
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ abc123456 nginx 0.0.0.0:8080->80/tcp web
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 9.6.4 最佳实践与安全
|
### 9.5.4 最佳实践与安全
|
||||||
|
|
||||||
在配置端口映射时,需要注意以下安全事项:
|
在配置端口映射时,需要注意以下安全事项:
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ $ docker run -d -p 53:53/udp dns-server
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 9.6.5 实现原理
|
### 9.5.5 实现原理
|
||||||
|
|
||||||
Docker 使用 `docker-proxy` 进程 (用户态) 或 `iptables` DNAT 规则 (内核态) 来实现端口转发。
|
Docker 使用 `docker-proxy` 进程 (用户态) 或 `iptables` DNAT 规则 (内核态) 来实现端口转发。
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Docker 网络提供了天然的隔离能力,不同网络之间的容器默认无法通信。这是 Docker 网络安全的重要基础。
|
Docker 网络提供了天然的隔离能力,不同网络之间的容器默认无法通信。这是 Docker 网络安全的重要基础。
|
||||||
|
|
||||||
### 9.7.1 网络隔离原理
|
### 9.6.1 网络隔离原理
|
||||||
|
|
||||||
不同网络之间默认隔离,容器只能与同一网络中的容器直接通信:
|
不同网络之间默认隔离,容器只能与同一网络中的容器直接通信:
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ $ docker exec web ping db
|
|||||||
ping: db: Name or service not known
|
ping: db: Name or service not known
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9.7.2 安全优势
|
### 9.6.2 安全优势
|
||||||
|
|
||||||
这种隔离机制带来以下安全优势:
|
这种隔离机制带来以下安全优势:
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ ping: db: Name or service not known
|
|||||||
| **多租户** | 不同租户的容器在不同网络中完全隔离 |
|
| **多租户** | 不同租户的容器在不同网络中完全隔离 |
|
||||||
| **最小权限** | 容器只能访问必要的网络资源 |
|
| **最小权限** | 容器只能访问必要的网络资源 |
|
||||||
|
|
||||||
### 9.7.3 跨网络通信
|
### 9.6.3 跨网络通信
|
||||||
|
|
||||||
如果确实需要某个容器跨网络通信,可以将其同时连接到多个网络:
|
如果确实需要某个容器跨网络通信,可以将其同时连接到多个网络:
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ $ docker network connect backend api
|
|||||||
|
|
||||||
这种方式让你可以精确控制哪些容器可以跨网络通信,遵循最小权限原则。
|
这种方式让你可以精确控制哪些容器可以跨网络通信,遵循最小权限原则。
|
||||||
|
|
||||||
### 9.7.4 典型网络架构
|
### 9.6.4 典型网络架构
|
||||||
|
|
||||||
一个典型的多层应用网络架构如下:
|
一个典型的多层应用网络架构如下:
|
||||||
|
|
||||||
|
|||||||
709
09_network/9.7_advanced_networking.md
Normal file
709
09_network/9.7_advanced_networking.md
Normal file
@@ -0,0 +1,709 @@
|
|||||||
|
## 9.7 容器网络高级特性
|
||||||
|
|
||||||
|
深入探讨容器网络的核心机制、Overlay 网络、CNI 插件生态、容器 DNS 解析、网络策略等高级特性,为生产级别的网络架构打下坚实基础。
|
||||||
|
|
||||||
|
### 9.7.1 Overlay 网络原理与配置
|
||||||
|
|
||||||
|
Overlay 网络在现有网络基础上建立虚拟网络,允许容器跨宿主机通信。它是 Kubernetes 和 Swarm 模式的基础。
|
||||||
|
|
||||||
|
#### Overlay 网络工作原理
|
||||||
|
|
||||||
|
Overlay 网络通过隧道封装技术(通常是 VXLAN)将容器网络流量封装在宿主机物理网络的 UDP 数据包中传输。
|
||||||
|
|
||||||
|
```
|
||||||
|
容器 A (192.168.0.2)
|
||||||
|
↓
|
||||||
|
veth 对
|
||||||
|
↓
|
||||||
|
br-net (网桥)
|
||||||
|
↓
|
||||||
|
Docker 引擎 (VXLAN 封装)
|
||||||
|
↓
|
||||||
|
物理网络 (172.16.0.0/24)
|
||||||
|
↓
|
||||||
|
Docker 引擎 (VXLAN 解封装)
|
||||||
|
↓
|
||||||
|
br-net (网桥)
|
||||||
|
↓
|
||||||
|
veth 对
|
||||||
|
↓
|
||||||
|
容器 B (192.168.0.3,不同宿主机)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 创建和使用 Overlay 网络
|
||||||
|
|
||||||
|
**Docker Swarm 模式下的 Overlay 网络:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 初始化 Swarm(创建集群)
|
||||||
|
docker swarm init
|
||||||
|
|
||||||
|
# 创建 overlay 网络
|
||||||
|
docker network create --driver overlay \
|
||||||
|
--subnet 192.168.0.0/24 \
|
||||||
|
--opt com.docker.network.driver.mtu=1450 \
|
||||||
|
my-overlay-net
|
||||||
|
|
||||||
|
# 验证网络创建
|
||||||
|
docker network ls
|
||||||
|
docker network inspect my-overlay-net
|
||||||
|
|
||||||
|
# 在 Swarm 服务中使用 overlay 网络
|
||||||
|
docker service create --name web \
|
||||||
|
--network my-overlay-net \
|
||||||
|
--replicas 3 \
|
||||||
|
nginx:latest
|
||||||
|
|
||||||
|
# 验证服务跨节点通信
|
||||||
|
docker service ps web
|
||||||
|
```
|
||||||
|
|
||||||
|
**单机 Overlay 网络模拟(Linux 容器):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建自定义 overlay 网络
|
||||||
|
docker network create --driver overlay custom-overlay
|
||||||
|
|
||||||
|
# 创建两个容器
|
||||||
|
docker run -d --name container1 --network custom-overlay nginx:latest
|
||||||
|
docker run -d --name container2 --network custom-overlay nginx:latest
|
||||||
|
|
||||||
|
# 测试跨容器通信
|
||||||
|
docker exec container1 ping container2
|
||||||
|
docker exec container1 curl http://container2
|
||||||
|
|
||||||
|
# 检查网络配置
|
||||||
|
docker network inspect custom-overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Overlay 网络性能优化
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 调整 MTU(Maximum Transmission Unit)避免分片
|
||||||
|
# VXLAN 开销 50 字节,物理 MTU 1500,建议设置为 1450
|
||||||
|
docker network create --driver overlay \
|
||||||
|
--opt com.docker.network.driver.mtu=1450 \
|
||||||
|
optimized-overlay
|
||||||
|
|
||||||
|
# 启用 IP 地址管理(IPAM)自定义
|
||||||
|
docker network create --driver overlay \
|
||||||
|
--subnet 10.0.9.0/24 \
|
||||||
|
--aux-address "my-router=10.0.9.2" \
|
||||||
|
my-custom-overlay
|
||||||
|
|
||||||
|
# 在 Compose 中使用 overlay 网络
|
||||||
|
version: '3.9'
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
driver: overlay
|
||||||
|
driver_opts:
|
||||||
|
com.docker.network.driver.mtu: 1450
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.7.2 CNI 插件生态概览
|
||||||
|
|
||||||
|
容器网络接口(CNI)是容器编排平台(尤其是 Kubernetes)的标准化网络接口。不同的 CNI 插件提供不同的网络能力。
|
||||||
|
|
||||||
|
#### 主流 CNI 插件对比
|
||||||
|
|
||||||
|
**Calico - 基于 BGP 的网络**
|
||||||
|
|
||||||
|
Calico 使用 BGP 协议进行路由,支持网络策略和 eBPF 加速。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Kubernetes 中安装 Calico
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: calico-config
|
||||||
|
namespace: kube-system
|
||||||
|
data:
|
||||||
|
cni_network_config: |
|
||||||
|
{
|
||||||
|
"name": "k8s-pod-network",
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "calico",
|
||||||
|
"datastore_type": "kubernetes",
|
||||||
|
"mtu": 1450,
|
||||||
|
"ipam": {
|
||||||
|
"type": "calico-ipam"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "portmap",
|
||||||
|
"snat": true,
|
||||||
|
"capabilities": {"portMappings": true}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flannel - 简单可靠的 Overlay**
|
||||||
|
|
||||||
|
Flannel 提供简单的 overlay 网络实现,适合小到中等规模的集群。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 Flannel
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
|
||||||
|
|
||||||
|
# 配置 Flannel 后端(VXLAN、UDP、AWS VPC 等)
|
||||||
|
cat <<EOF | kubectl apply -f -
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: kube-flannel-cfg
|
||||||
|
namespace: kube-flannel
|
||||||
|
data:
|
||||||
|
cni-conf.json: |
|
||||||
|
{
|
||||||
|
"name": "cbr0",
|
||||||
|
"cniVersion": "0.3.1",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "flannel",
|
||||||
|
"delegate": {
|
||||||
|
"hairpinMode": true,
|
||||||
|
"isDefaultGateway": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "portmap",
|
||||||
|
"capabilities": {
|
||||||
|
"portMappings": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
net-conf.json: |
|
||||||
|
{
|
||||||
|
"Network": "10.244.0.0/16",
|
||||||
|
"Backend": {
|
||||||
|
"Type": "vxlan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cilium - eBPF 驱动的先进网络**
|
||||||
|
|
||||||
|
Cilium 使用 eBPF 在内核级别实现网络策略和可观测性,性能优异。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 Cilium
|
||||||
|
helm repo add cilium https://helm.cilium.io
|
||||||
|
helm install cilium cilium/cilium \
|
||||||
|
--namespace kube-system \
|
||||||
|
--set image.tag=v1.14.0 \
|
||||||
|
--set operator.replicas=1
|
||||||
|
|
||||||
|
# 启用 Hubble(可观测性)
|
||||||
|
helm upgrade cilium cilium/cilium \
|
||||||
|
--namespace kube-system \
|
||||||
|
--reuse-values \
|
||||||
|
--set hubble.enabled=true \
|
||||||
|
--set hubble.ui.enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Weave - 跨主机网络通信**
|
||||||
|
|
||||||
|
Weave 提供简单的跨主机通信,支持加密和多播。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Docker 中使用 Weave 网络
|
||||||
|
docker run -d --name weave \
|
||||||
|
--net=host \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
--cap-add=SYS_PTRACE \
|
||||||
|
ghcr.io/weaveworks/weave:latest
|
||||||
|
|
||||||
|
# 连接到 Weave 网络
|
||||||
|
docker run -d --network weave --name web nginx:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**CNI 插件对比表:**
|
||||||
|
|
||||||
|
| 特性 | Calico | Flannel | Cilium | Weave |
|
||||||
|
|------|--------|---------|--------|-------|
|
||||||
|
| 路由方式 | BGP | VXLAN/UDP | eBPF | VxLAN |
|
||||||
|
| 网络策略 | ✓ | ✗ | ✓ (L3-L7) | ✗ |
|
||||||
|
| 性能 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||||
|
| 可观测性 | 中 | 低 | ⭐⭐⭐⭐⭐ | 中 |
|
||||||
|
| 学习曲线 | 中 | 低 | 高 | 低 |
|
||||||
|
| 生产就绪 | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
### 9.7.3 容器 DNS 解析机制
|
||||||
|
|
||||||
|
Docker 内置 DNS 服务器,但 DNS 解析涉及多个层面的配置。
|
||||||
|
|
||||||
|
#### DNS 解析流程
|
||||||
|
|
||||||
|
```
|
||||||
|
容器应用 (dig www.example.com)
|
||||||
|
↓
|
||||||
|
容器内 /etc/resolv.conf (127.0.0.11:53)
|
||||||
|
↓
|
||||||
|
Docker 内嵌 DNS 服务器 (127.0.0.11)
|
||||||
|
↓
|
||||||
|
用户自定义 DNS 或宿主机 /etc/resolv.conf
|
||||||
|
↓
|
||||||
|
外部 DNS 服务器 (8.8.8.8 等)
|
||||||
|
↓
|
||||||
|
DNS 响应 → 容器缓存 → 应用
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 配置容器 DNS
|
||||||
|
|
||||||
|
**在运行时指定 DNS:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 单个容器
|
||||||
|
docker run -d \
|
||||||
|
--dns 8.8.8.8 \
|
||||||
|
--dns 1.1.1.1 \
|
||||||
|
--dns-search example.com \
|
||||||
|
nginx:latest
|
||||||
|
|
||||||
|
# DNS 选项
|
||||||
|
docker run -d \
|
||||||
|
--dns-option ndots:2 \
|
||||||
|
--dns-option timeout:1 \
|
||||||
|
--dns-option attempts:3 \
|
||||||
|
nginx:latest
|
||||||
|
|
||||||
|
# 查看容器 DNS 配置
|
||||||
|
docker exec <container_id> cat /etc/resolv.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker Compose DNS 配置:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
dns:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
dns_search:
|
||||||
|
- example.com
|
||||||
|
- local
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
hostname: postgres-db
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
# 容器内 /etc/resolv.conf 将被自动配置
|
||||||
|
# search example.com local
|
||||||
|
# nameserver 8.8.8.8
|
||||||
|
# nameserver 1.1.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker 守护进程级别配置:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": ["8.8.8.8", "1.1.1.1"],
|
||||||
|
"dns-search": ["example.com"],
|
||||||
|
"insecure-registries": [],
|
||||||
|
"registry-mirrors": ["https://mirror.example.com"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 自定义服务发现(Service Discovery)
|
||||||
|
|
||||||
|
**使用 Docker 内建 DNS 的服务发现:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建自定义网络
|
||||||
|
docker network create mynet
|
||||||
|
|
||||||
|
# 运行服务
|
||||||
|
docker run -d --name web --network mynet nginx:latest
|
||||||
|
docker run -d --name db --network mynet postgres:latest
|
||||||
|
|
||||||
|
# 在其他容器中通过服务名访问
|
||||||
|
docker run -it --network mynet busybox sh
|
||||||
|
# ping web # 自动解析到 web 容器 IP
|
||||||
|
# ping db # 自动解析到 db 容器 IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Compose 服务名自动发现:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
image: nginx
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
environment:
|
||||||
|
BACKEND_URL: http://backend:8080
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: myapp
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: mydb
|
||||||
|
|
||||||
|
# frontend 容器可以直接访问 http://backend:8080
|
||||||
|
# backend 容器可以直接访问 postgres://database:5432
|
||||||
|
```
|
||||||
|
|
||||||
|
#### DNS 性能优化
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查 DNS 延迟
|
||||||
|
time docker exec <container> nslookup www.example.com
|
||||||
|
|
||||||
|
# 优化 DNS 解析
|
||||||
|
docker run -d \
|
||||||
|
--dns 127.0.0.1 \ # 使用本地缓存 DNS (Dnsmasq)
|
||||||
|
nginx:latest
|
||||||
|
|
||||||
|
# 在 Kubernetes 中优化
|
||||||
|
kubectl patch deployment -n kube-system coredns --patch '{
|
||||||
|
"spec": {
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "coredns",
|
||||||
|
"resources": {
|
||||||
|
"limits": {
|
||||||
|
"memory": "512Mi",
|
||||||
|
"cpu": "500m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.7.4 网络策略(NetworkPolicy)实践
|
||||||
|
|
||||||
|
网络策略定义了容器间的流量控制规则,是微服务架构中的安全基础。
|
||||||
|
|
||||||
|
#### 基本网络策略
|
||||||
|
|
||||||
|
**默认拒绝所有入站流量的策略:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: default-deny-ingress
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
# 不指定 ingress 规则,表示拒绝所有入站流量
|
||||||
|
```
|
||||||
|
|
||||||
|
**允许特定来源的入站流量:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-from-frontend
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
tier: backend
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
tier: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**允许出站流量到数据库:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-backend-to-db
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
tier: backend
|
||||||
|
policyTypes:
|
||||||
|
- Egress
|
||||||
|
egress:
|
||||||
|
# 允许到数据库的流量
|
||||||
|
- to:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
tier: database
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
# 允许 DNS 查询
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {}
|
||||||
|
ports:
|
||||||
|
- protocol: UDP
|
||||||
|
port: 53
|
||||||
|
# 允许到外部 API 的流量
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 0.0.0.0/0
|
||||||
|
except:
|
||||||
|
- 169.254.169.254/32 # 阻止元数据服务
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 443
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 微服务网络策略示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
# 拒绝所有默认
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: default-deny-all
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
|
||||||
|
---
|
||||||
|
# Frontend 容器策略
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-frontend
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: frontend
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress:
|
||||||
|
# 允许来自 Ingress Controller 的流量
|
||||||
|
- from:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
name: ingress-nginx
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: ingress-controller
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 3000
|
||||||
|
egress:
|
||||||
|
# 允许到 API 的流量
|
||||||
|
- to:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: api
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8080
|
||||||
|
# 允许 DNS
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {}
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: kube-dns
|
||||||
|
ports:
|
||||||
|
- protocol: UDP
|
||||||
|
port: 53
|
||||||
|
|
||||||
|
---
|
||||||
|
# API 容器策略
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-api
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: api
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8080
|
||||||
|
egress:
|
||||||
|
# 允许到数据库的流量
|
||||||
|
- to:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: postgres
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
# 允许 DNS
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {}
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: kube-dns
|
||||||
|
ports:
|
||||||
|
- protocol: UDP
|
||||||
|
port: 53
|
||||||
|
|
||||||
|
---
|
||||||
|
# 数据库容器策略
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-postgres
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: postgres
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: api
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用 Calico/Cilium 的高级网络策略
|
||||||
|
|
||||||
|
**L7 应用层策略(仅 Cilium 支持):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: "cilium.io/v2"
|
||||||
|
kind: CiliumNetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: "api-gateway-policy"
|
||||||
|
spec:
|
||||||
|
description: "L7 policy for API gateway"
|
||||||
|
endpointSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: api
|
||||||
|
ingress:
|
||||||
|
- fromEndpoints:
|
||||||
|
- matchLabels:
|
||||||
|
app: frontend
|
||||||
|
toPorts:
|
||||||
|
- ports:
|
||||||
|
- port: "8080"
|
||||||
|
protocol: TCP
|
||||||
|
rules:
|
||||||
|
http:
|
||||||
|
# 允许 GET /api/users
|
||||||
|
- method: "GET"
|
||||||
|
path: "/api/users/.*"
|
||||||
|
# 允许 POST /api/users 仅从管理员来源
|
||||||
|
- method: "POST"
|
||||||
|
path: "/api/users"
|
||||||
|
sourceIPs:
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.7.5 跨主机容器通信方案对比
|
||||||
|
|
||||||
|
#### 方案对比表
|
||||||
|
|
||||||
|
| 方案 | 隔离性 | 性能 | 复杂度 | 适用场景 |
|
||||||
|
|------|--------|------|--------|---------|
|
||||||
|
| 主机网络 | ✗ | ⭐⭐⭐⭐⭐ | 低 | 高性能,单主机 |
|
||||||
|
| Bridge + Host Port | 中 | ⭐⭐⭐⭐ | 低 | 小规模集群 |
|
||||||
|
| Overlay (VXLAN) | ✓ | ⭐⭐⭐ | 中 | 跨域通信 |
|
||||||
|
| BGP (Calico) | ✓ | ⭐⭐⭐⭐ | 中 | 大规模集群 |
|
||||||
|
| eBPF (Cilium) | ✓ | ⭐⭐⭐⭐⭐ | 高 | 高性能大集群 |
|
||||||
|
|
||||||
|
#### 选择建议
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 开发环境:使用 Bridge 网络
|
||||||
|
docker network create my-app
|
||||||
|
docker-compose up # 默认使用 bridge
|
||||||
|
|
||||||
|
# 2. 小规模生产(< 50 节点):使用 Flannel
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
|
||||||
|
|
||||||
|
# 3. 中等规模(50-500 节点):使用 Calico
|
||||||
|
kubeadm init --pod-network-cidr=192.168.0.0/16
|
||||||
|
kubectl apply -f https://docs.projectcalico.org/v3.24/manifests/tigera-operator.yaml
|
||||||
|
|
||||||
|
# 4. 大规模(> 500 节点)或需要 L7 策略:使用 Cilium
|
||||||
|
helm install cilium cilium/cilium --namespace kube-system
|
||||||
|
|
||||||
|
# 5. 需要多云/跨域:使用 Weave
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.7.6 网络故障排查
|
||||||
|
|
||||||
|
**常见网络问题诊断:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 容器无法访问外部网络
|
||||||
|
docker exec <container> ping 8.8.8.8
|
||||||
|
docker exec <container> cat /etc/resolv.conf
|
||||||
|
docker logs <container> | grep -i network
|
||||||
|
|
||||||
|
# 2. 容器间无法通信
|
||||||
|
docker network inspect <network>
|
||||||
|
docker exec <container1> ping <container2>
|
||||||
|
|
||||||
|
# 3. 端口映射失效
|
||||||
|
docker port <container>
|
||||||
|
netstat -tlnp | grep <port>
|
||||||
|
|
||||||
|
# 4. DNS 解析失败
|
||||||
|
docker exec <container> nslookup example.com
|
||||||
|
docker exec <container> cat /etc/hosts
|
||||||
|
|
||||||
|
# 5. 网络延迟
|
||||||
|
docker run --rm --network host iperf3:latest -c <target>
|
||||||
|
docker exec <container> mtr -r example.com
|
||||||
|
|
||||||
|
# 使用 tcpdump 抓包分析
|
||||||
|
docker run --rm --cap-add NET_ADMIN --network host \
|
||||||
|
corfr/tcpdump -i eth0 -n "port 80"
|
||||||
|
```
|
||||||
@@ -39,3 +39,4 @@ graph TD
|
|||||||
* [容器互联](9.4_container_linking.md)
|
* [容器互联](9.4_container_linking.md)
|
||||||
* [外部访问容器](9.5_port_mapping.md)
|
* [外部访问容器](9.5_port_mapping.md)
|
||||||
* [网络隔离](9.6_network_isolation.md)
|
* [网络隔离](9.6_network_isolation.md)
|
||||||
|
* [高级网络配置](9.7_advanced_networking.md)
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ $ docker buildx build --sbom=true -t myimage .
|
|||||||
> **⚠️ 注意与失败模式**:
|
> **⚠️ 注意与失败模式**:
|
||||||
> 要使 SBOM (或其它 attestation 元数据) 成功附着并可见,对底层的存储格式有前置要求:默认的 classic image store 不支持 manifest list/index 这种存放 attestation 的结构。
|
> 要使 SBOM (或其它 attestation 元数据) 成功附着并可见,对底层的存储格式有前置要求:默认的 classic image store 不支持 manifest list/index 这种存放 attestation 的结构。
|
||||||
>
|
>
|
||||||
> 如果只简单运行上述命令,你可能会面临**“命令成功执行,但本地镜像中看不到 SBOM”**的体会落差。
|
> 如果只简单运行上述命令,你可能会面临 **“命令成功执行,但本地镜像中看不到 SBOM”** 的体会落差。
|
||||||
>
|
>
|
||||||
> **正确的解决路径有两条**:
|
> **正确的解决路径有两条**:
|
||||||
> 1. 在 Docker 守护进程中启用 `containerd image store` 特性(现代 Docker Desktop 默认推荐)。
|
> 1. 在 Docker 守护进程中启用 `containerd image store` 特性(现代 Docker Desktop 默认推荐)。
|
||||||
> 2. 或者使用 `docker-container` driver 的构建器,并直接**加上 `--push` 参数**将产物推送到远端支持 OCI 的镜像仓库,仓库会正确保存这些元数据。
|
> 2. 或者使用 `docker-container` driver 的构建器,并直接 **加上 `--push` 参数** 将产物推送到远端支持 OCI 的镜像仓库,仓库会正确保存这些元数据。
|
||||||
|
|
||||||
### 10.2.2 官方文档
|
### 10.2.2 官方文档
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,38 @@
|
|||||||
## 11.9 实战 LNMP
|
## 11.9 实战 LNMP
|
||||||
|
|
||||||
本项目的维护者 [khs1994](https://github.com/khs1994) 的开源项目 [khs1994-docker/lnmp](https://github.com/khs1994-docker/lnmp) 使用 Docker Compose 搭建了一套 LNMP 环境,各位开发者可以参考该项目在 Docker 或 Kubernetes 中运行 LNMP。
|
### 什么是 LNMP
|
||||||
|
|
||||||
|
LNMP 是一个经典的 Web 应用栈,由以下四个开源软件组合而成:
|
||||||
|
|
||||||
|
- **L**:Linux(操作系统)
|
||||||
|
- **N**:Nginx(Web 服务器)
|
||||||
|
- **M**:MySQL(数据库服务器)
|
||||||
|
- **P**:PHP(脚本语言)
|
||||||
|
|
||||||
|
这个组合被广泛用于构建高性能的 Web 应用。
|
||||||
|
|
||||||
|
### 使用 Docker Compose 部署 LNMP
|
||||||
|
|
||||||
|
本项目的维护者 [khs1994](https://github.com/khs1994) 的开源项目 [khs1994-docker/lnmp](https://github.com/khs1994-docker/lnmp) 使用 Docker Compose 搭建了一套完整的 LNMP 环境。
|
||||||
|
|
||||||
|
### 参考项目
|
||||||
|
|
||||||
|
该项目中包含的服务:
|
||||||
|
|
||||||
|
- **Nginx**:Web 服务器,用于处理 HTTP 请求
|
||||||
|
- **MySQL/MariaDB**:关系型数据库服务
|
||||||
|
- **PHP-FPM**:PHP 处理器,与 Nginx 通过 Fast CGI 协议通信
|
||||||
|
- **Redis**:可选的内存缓存服务(用于会话或缓存)
|
||||||
|
|
||||||
|
### 学习资源
|
||||||
|
|
||||||
|
各位开发者可以参考该项目在以下场景中运行 LNMP:
|
||||||
|
|
||||||
|
- Docker 容器化部署
|
||||||
|
- Kubernetes 集群编排
|
||||||
|
- 开发环境快速搭建
|
||||||
|
- 生产环境配置参考
|
||||||
|
|
||||||
|
项目地址:https://github.com/khs1994-docker/lnmp
|
||||||
|
|
||||||
|
通过该项目,你可以学习到如何使用 Docker Compose 定义多个相互关联的服务,以及如何在容器化环境中管理应用的生命周期。
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。
|
命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。
|
||||||
|
|
||||||
## 12.2 什么是 Namespace
|
### 12.2.1 什么是 Namespace
|
||||||
|
|
||||||
> **Namespace 是 Linux 内核提供的资源隔离机制,它让容器内的进程仿佛运行在独立的操作系统中。** Namespace 是容器技术的核心基础之一。它回答了一个关键问题:**如何让一个进程 “以为” 自己独占整个系统?**
|
> **Namespace 是 Linux 内核提供的资源隔离机制,它让容器内的进程仿佛运行在独立的操作系统中。**Namespace 是容器技术的核心基础之一。它回答了一个关键问题:**如何让一个进程 “以为” 自己独占整个系统?**
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -26,7 +26,7 @@ flowchart LR
|
|||||||
H4 -. "(实际是宿主机的 1234)" .- C1
|
H4 -. "(实际是宿主机的 1234)" .- C1
|
||||||
```
|
```
|
||||||
|
|
||||||
### 12.2.1 Namespace 的类型
|
### 12.2.2 Namespace 的类型
|
||||||
|
|
||||||
Linux 内核提供了以下几种 Namespace,Docker 容器使用了全部:
|
Linux 内核提供了以下几种 Namespace,Docker 容器使用了全部:
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ Linux 内核提供了以下几种 Namespace,Docker 容器使用了全部:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.2 PID Namespace
|
### 12.2.3 PID Namespace
|
||||||
|
|
||||||
PID Namespace 负责进程 ID 的隔离,使得容器内的进程彼此不可见。
|
PID Namespace 负责进程 ID 的隔离,使得容器内的进程彼此不可见。
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ PID USER COMMAND
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.3 NET Namespace
|
### 12.2.4 NET Namespace
|
||||||
|
|
||||||
NET Namespace 负责网络栈的隔离,包括网卡、路由表和 iptables 规则等。
|
NET Namespace 负责网络栈的隔离,包括网卡、路由表和 iptables 规则等。
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.4 MNT Namespace
|
### 12.2.5 MNT Namespace
|
||||||
|
|
||||||
MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的文件系统视图。
|
MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的文件系统视图。
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.5 UTS Namespace
|
### 12.2.6 UTS Namespace
|
||||||
|
|
||||||
UTS Namespace 主要用于隔离主机名和域名。
|
UTS Namespace 主要用于隔离主机名和域名。
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ UTS = “UNIX Time-sharing System”,是历史遗留的名称。
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.6 IPC Namespace
|
### 12.2.7 IPC Namespace
|
||||||
|
|
||||||
IPC Namespace 用于隔离进程间通信资源,如 System V IPC 和 POSIX 消息队列。
|
IPC Namespace 用于隔离进程间通信资源,如 System V IPC 和 POSIX 消息队列。
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ IPC Namespace 用于隔离进程间通信资源,如 System V IPC 和 POSIX 消
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.7 USER Namespace
|
### 12.2.8 USER Namespace
|
||||||
|
|
||||||
USER Namespace 允许将容器内的用户 ID 映射到宿主机的不同用户 ID。
|
USER Namespace 允许将容器内的用户 ID 映射到宿主机的不同用户 ID。
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.8 动手实验:体验 Namespace
|
### 12.2.9 动手实验:体验 Namespace
|
||||||
|
|
||||||
使用 `unshare` 命令可以在不使用 Docker 的情况下体验 Namespace:
|
使用 `unshare` 命令可以在不使用 Docker 的情况下体验 Namespace:
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ $ ip addr
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 12.2.9 Namespace 的局限性
|
### 12.2.10 Namespace 的局限性
|
||||||
|
|
||||||
Namespace 提供了隔离但不是安全边界:
|
Namespace 提供了隔离但不是安全边界:
|
||||||
|
|
||||||
|
|||||||
@@ -92,14 +92,14 @@ flowchart LR
|
|||||||
|
|
||||||
### 12.4.4 Docker 支持的存储驱动
|
### 12.4.4 Docker 支持的存储驱动
|
||||||
|
|
||||||
Docker 的存储驱动经历了从早期各式各样的机制(如 aufs, devicemapper),到被广泛使用的现代经典 graph driver (`overlay2`),再到当下(Engine v29 及以后)**默认启用的 containerd 镜像存储引擎(containerd image store)**的演进。
|
Docker 的存储驱动经历了从早期各式各样的机制(如 aufs, devicemapper),到被广泛使用的现代经典 graph driver (`overlay2`),再到当下(Engine v29 及以后)**默认启用的 containerd 镜像存储引擎(containerd image store)** 的演进。
|
||||||
|
|
||||||
| 存储后端 / 驱动 | 核心特性说明 | 推荐程度 |
|
| 存储后端 / 驱动 | 核心特性说明 | 推荐程度 |
|
||||||
|---------|------|---------|
|
|---------|------|---------|
|
||||||
| **containerd image store** | (v29+ 新一代默认引擎) 基于 containerd 的 snapshotters,原生支持 OCI image index、多架构镜像与 Attestations 构建溯源元数据存储。 | ✅**强烈推荐 (现代默认)** |
|
| **containerd image store**| (v29+ 新一代默认引擎) 基于 containerd 的 snapshotters,原生支持 OCI image index、多架构镜像与 Attestations 构建溯源元数据存储。 | ✅**强烈推荐 (现代默认)** |
|
||||||
| **overlay2**| (经典 Graph Driver) 传统架构下的现代 Linux 默认驱动,性能优秀,但在处理复杂溯源元数据(索引)时受限。 | ✅**推荐 (主要后备)** |
|
| **overlay2**| (经典 Graph Driver) 传统架构下的现代 Linux 默认驱动,性能优秀,但在处理复杂溯源元数据(索引)时受限。 | ✅**推荐 (主要后备)** |
|
||||||
| **aufs** | 早期默认,兼容性好 | 遗留系统 |
|
| **aufs** | 早期默认,兼容性好 | 遗留系统 |
|
||||||
| **btrfs** / **zfs** | 使用原生稳定文件系统快照能力 | 特定场景 |
|
| **btrfs**/**zfs** | 使用原生稳定文件系统快照能力 | 特定场景 |
|
||||||
| **devicemapper** | 块设备级存储 | 遗留系统 (已被逐步弃用) |
|
| **devicemapper** | 块设备级存储 | 遗留系统 (已被逐步弃用) |
|
||||||
| **vfs** | 不使用 CoW,每层完整复制 | 仅测试 |
|
| **vfs** | 不使用 CoW,每层完整复制 | 仅测试 |
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,50 @@
|
|||||||
## 12.5 容器格式
|
## 12.5 容器格式
|
||||||
|
|
||||||
最初,Docker 采用了 `LXC` 中的容器格式。从 0.7 版本以后开始去除 LXC,转而使用自行开发的 [libcontainer](https://github.com/docker/libcontainer),从 1.11 开始,则进一步演进为使用 [runC](https://github.com/opencontainers/runc) 和 [containerd](https://github.com/containerd/containerd)。
|
### Docker 容器格式的演进
|
||||||
|
|
||||||
|
最初,Docker 采用了 `LXC` 中的容器格式。从 0.7 版本以后开始去除 LXC 的依赖,转而使用自行开发的 [libcontainer](https://github.com/docker/libcontainer)。从 1.11 开始,则进一步演进为使用 [runC](https://github.com/opencontainers/runc) 和 [containerd](https://github.com/containerd/containerd)。
|
||||||
|
|
||||||
|
### 关键组件说明
|
||||||
|
|
||||||
|
#### LXC(Linux 容器)
|
||||||
|
|
||||||
|
Docker 早期版本(0.1-0.7)直接使用 LXC 作为容器运行时,利用 Linux Namespaces 和 Cgroups 实现容器隔离。
|
||||||
|
|
||||||
|
#### libcontainer
|
||||||
|
|
||||||
|
- Docker 自行开发的容器库
|
||||||
|
- 提供了容器的通用接口
|
||||||
|
- 不依赖于特定的 Linux 容器实现
|
||||||
|
- 更灵活和可控
|
||||||
|
|
||||||
|
#### runC
|
||||||
|
|
||||||
|
- OCI(Open Container Initiative)标准实现
|
||||||
|
- 轻量级的容器运行时
|
||||||
|
- 独立的二进制文件,可单独使用
|
||||||
|
- 基于 libcontainer 发展而来
|
||||||
|
|
||||||
|
#### containerd
|
||||||
|
|
||||||
|
- Docker 开源的容器运行时
|
||||||
|
- 提供了容器的完整生命周期管理
|
||||||
|
- 支持 runC 和其他 OCI 兼容的运行时
|
||||||
|
- 在 Kubernetes 等编排系统中广泛使用
|
||||||
|
|
||||||
|
### 容器规范标准
|
||||||
|
|
||||||
|
Docker 积极参与 Open Container Initiative (OCI) 的制定,推动了以下规范的发展:
|
||||||
|
|
||||||
|
- **Image Spec**:容器镜像格式规范
|
||||||
|
- **Runtime Spec**:容器运行时接口规范
|
||||||
|
- **Distribution Spec**:容器镜像分发规范
|
||||||
|
|
||||||
|
### 架构演变的优势
|
||||||
|
|
||||||
|
从 LXC → libcontainer → runC/containerd 的演变提供了以下优势:
|
||||||
|
|
||||||
|
1. 减少外部依赖
|
||||||
|
2. 提高运行效率
|
||||||
|
3. 遵循行业标准(OCI)
|
||||||
|
4. 增强可移植性和互操作性
|
||||||
|
5. 支持多种容器运行时选择
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
## 14.1 使用 kubeadm 部署 Kubernetes (CRI 使用 containerd)
|
## 14.1 使用 kubeadm 部署 Kubernetes
|
||||||
|
|
||||||
`kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令,作为快速创建 `Kubernetes` 集群的最佳实践。
|
`kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令,作为快速创建 `Kubernetes` 集群的最佳实践。
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,67 @@
|
|||||||
## 14.6 一步步部署 Kubernetes 集群
|
## 14.6 一步步部署 Kubernetes 集群
|
||||||
|
|
||||||
可以参考 [opsnull/follow-me-install-kubernetes-cluster](https://github.com/opsnull/follow-me-install-kubernetes-cluster) 项目一步步部署 Kubernetes 集群。
|
### 概述
|
||||||
|
|
||||||
|
部署 Kubernetes 集群涉及多个组件的安装和配置,包括 Master 节点和 Worker 节点。本章介绍如何使用 systemd 管理这些服务的生命周期。
|
||||||
|
|
||||||
|
### Kubernetes 主要组件
|
||||||
|
|
||||||
|
#### Master 节点组件
|
||||||
|
|
||||||
|
- **kube-apiserver**:API 服务器,Kubernetes 集群的中心
|
||||||
|
- **kube-controller-manager**:控制器管理器
|
||||||
|
- **kube-scheduler**:调度器,负责 Pod 调度
|
||||||
|
- **etcd**:分布式键值存储,存储集群数据
|
||||||
|
|
||||||
|
#### Worker 节点组件
|
||||||
|
|
||||||
|
- **kubelet**:节点代理,管理容器生命周期
|
||||||
|
- **kube-proxy**:网络代理,处理服务网络
|
||||||
|
- **Container Runtime**:容器运行时(Docker、containerd 等)
|
||||||
|
|
||||||
|
### 使用 systemd 管理 Kubernetes 服务
|
||||||
|
|
||||||
|
#### 服务单元文件
|
||||||
|
|
||||||
|
为了让 systemd 管理 Kubernetes 服务,需要创建相应的 `.service` 文件,例如:
|
||||||
|
|
||||||
|
```
|
||||||
|
/etc/systemd/system/kubelet.service
|
||||||
|
/etc/systemd/system/kube-proxy.service
|
||||||
|
/etc/systemd/system/kube-apiserver.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 常用命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动服务
|
||||||
|
sudo systemctl start kubelet
|
||||||
|
|
||||||
|
# 停止服务
|
||||||
|
sudo systemctl stop kubelet
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
sudo systemctl restart kubelet
|
||||||
|
|
||||||
|
# 查看服务状态
|
||||||
|
sudo systemctl status kubelet
|
||||||
|
|
||||||
|
# 设置开机自启
|
||||||
|
sudo systemctl enable kubelet
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参考资源
|
||||||
|
|
||||||
|
详细的部署步骤和配置说明,可以参考以下项目:
|
||||||
|
|
||||||
|
- [opsnull/follow-me-install-kubernetes-cluster](https://github.com/opsnull/follow-me-install-kubernetes-cluster):一个完整的 Kubernetes 集群部署指南项目
|
||||||
|
|
||||||
|
该项目提供了详细的步骤说明,涵盖 Master 节点、Worker 节点的安装配置,以及如何使用 systemd 管理这些组件的生命周期。
|
||||||
|
|
||||||
|
### 推荐学习路径
|
||||||
|
|
||||||
|
1. 理解 Kubernetes 架构和各组件的作用
|
||||||
|
2. 准备所需的系统环境(Linux 主机、网络配置等)
|
||||||
|
3. 按步骤安装各个 Kubernetes 组件
|
||||||
|
4. 配置 systemd 服务单元文件
|
||||||
|
5. 验证集群健康状态
|
||||||
|
|||||||
@@ -8,74 +8,74 @@ kubectl [flags]
|
|||||||
kubectl [command]
|
kubectl [command]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 14.8 get
|
### 14.8.1 get
|
||||||
|
|
||||||
显示一个或多个资源
|
显示一个或多个资源
|
||||||
|
|
||||||
## 14.8 describe
|
### 14.8.2 describe
|
||||||
|
|
||||||
显示资源详情
|
显示资源详情
|
||||||
|
|
||||||
## 14.8 create
|
### 14.8.3 create
|
||||||
|
|
||||||
从文件或标准输入创建资源
|
从文件或标准输入创建资源
|
||||||
|
|
||||||
## 14.8 update
|
### 14.8.4 update
|
||||||
|
|
||||||
从文件或标准输入更新资源
|
从文件或标准输入更新资源
|
||||||
|
|
||||||
## 14.8 delete
|
### 14.8.5 delete
|
||||||
|
|
||||||
通过文件名、标准输入、资源名或者 label selector 删除资源
|
通过文件名、标准输入、资源名或者 label selector 删除资源
|
||||||
|
|
||||||
## 14.8 logs
|
### 14.8.6 logs
|
||||||
|
|
||||||
输出 pod 中一个容器的日志
|
输出 pod 中一个容器的日志
|
||||||
|
|
||||||
## 14.8 rollout
|
### 14.8.7 rollout
|
||||||
|
|
||||||
对 Deployment 等资源执行滚动更新/回滚
|
对 Deployment 等资源执行滚动更新/回滚
|
||||||
|
|
||||||
## 14.8 exec
|
### 14.8.8 exec
|
||||||
|
|
||||||
在容器内部执行命令
|
在容器内部执行命令
|
||||||
|
|
||||||
## 14.8 port-forward
|
### 14.8.9 port-forward
|
||||||
|
|
||||||
将本地端口转发到 Pod
|
将本地端口转发到 Pod
|
||||||
|
|
||||||
## 14.8 proxy
|
### 14.8.10 proxy
|
||||||
|
|
||||||
为 Kubernetes API server 启动代理服务器
|
为 Kubernetes API server 启动代理服务器
|
||||||
|
|
||||||
## 14.8 run
|
### 14.8.11 run
|
||||||
|
|
||||||
在集群中使用指定镜像启动容器
|
在集群中使用指定镜像启动容器
|
||||||
|
|
||||||
## 14.8 expose
|
### 14.8.12 expose
|
||||||
|
|
||||||
将 replication controller service 或 pod 暴露为新的 Kubernetes service
|
将 replication controller service 或 pod 暴露为新的 Kubernetes service
|
||||||
|
|
||||||
## 14.8 label
|
### 14.8.13 label
|
||||||
|
|
||||||
更新资源的 label
|
更新资源的 label
|
||||||
|
|
||||||
## 14.8 config
|
### 14.8.14 config
|
||||||
|
|
||||||
修改 Kubernetes 配置文件
|
修改 Kubernetes 配置文件
|
||||||
|
|
||||||
## 14.8 cluster-info
|
### 14.8.15 cluster-info
|
||||||
|
|
||||||
显示集群信息
|
显示集群信息
|
||||||
|
|
||||||
## 14.8 api-versions
|
### 14.8.16 api-versions
|
||||||
|
|
||||||
以 “组/版本” 的格式输出服务端支持的 API 版本
|
以 “组/版本” 的格式输出服务端支持的 API 版本
|
||||||
|
|
||||||
## 14.8 version
|
### 14.8.17 version
|
||||||
|
|
||||||
输出服务端和客户端的版本信息
|
输出服务端和客户端的版本信息
|
||||||
|
|
||||||
## 14.8 help
|
### 14.8.18 help
|
||||||
|
|
||||||
显示各个命令的帮助信息
|
显示各个命令的帮助信息
|
||||||
|
|||||||
@@ -21,4 +21,3 @@ FCOS 使用 rpm-ostree 系统进行事务性升级。无需像 yum 升级那样
|
|||||||
#### 容器工具
|
#### 容器工具
|
||||||
|
|
||||||
对于诸如构建,复制和其他管理容器的任务,FCOS 用一组容器工具代替了 **Docker CLI**。**podman CLI** 工具支持许多容器运行时功能,例如运行,启动,停止,列出和删除容器和镜像。**skopeo CLI** 工具可以复制,认证和签名镜像。您还可以使用 **crictl CLI** 工具来处理 CRI-O 容器引擎中的容器和镜像。
|
对于诸如构建,复制和其他管理容器的任务,FCOS 用一组容器工具代替了 **Docker CLI**。**podman CLI** 工具支持许多容器运行时功能,例如运行,启动,停止,列出和删除容器和镜像。**skopeo CLI** 工具可以复制,认证和签名镜像。您还可以使用 **crictl CLI** 工具来处理 CRI-O 容器引擎中的容器和镜像。
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -2,10 +2,28 @@
|
|||||||
|
|
||||||
本章将介绍 Docker 和 Kubernetes 之外的容器生态技术。
|
本章将介绍 Docker 和 Kubernetes 之外的容器生态技术。
|
||||||
|
|
||||||
- **Fedora CoreOS**:专为容器化工作负载设计的操作系统。
|
## 本章内容
|
||||||
- **Podman**:兼容 Docker CLI 的下一代无守护进程容器引擎。
|
|
||||||
- **Buildah**:无需守护进程的 OCI 容器镜像构建工具。
|
* [Fedora CoreOS 简介](17.1_coreos_intro.md)
|
||||||
- **Skopeo**:远程检查和管理容器镜像的利器。
|
* 专为容器化工作负载设计的操作系统。
|
||||||
- **containerd**:作为现代容器生态基石的核心容器运行时。
|
|
||||||
- **安全容器运行时**:通过提供更强隔离性来保证安全的技术方案(如 Kata Containers、gVisor)。
|
* [Fedora CoreOS 安装与配置](17.2_coreos_install.md)
|
||||||
- **WebAssembly**:一种极具潜力的轻量级跨平台二进制指令格式。
|
* CoreOS 的安装方式与基本配置。
|
||||||
|
|
||||||
|
* [Podman](17.3_podman.md)
|
||||||
|
* 兼容 Docker CLI 的下一代无守护进程容器引擎。
|
||||||
|
|
||||||
|
* [Buildah](17.4_buildah.md)
|
||||||
|
* 无需守护进程的 OCI 容器镜像构建工具。
|
||||||
|
|
||||||
|
* [Skopeo](17.5_skopeo.md)
|
||||||
|
* 远程检查和管理容器镜像的利器。
|
||||||
|
|
||||||
|
* [containerd](17.6_containerd.md)
|
||||||
|
* 作为现代容器生态基石的核心容器运行时。
|
||||||
|
|
||||||
|
* [安全容器运行时](17.7_secure_runtime.md)
|
||||||
|
* 通过提供更强隔离性来保证安全的技术方案(如 Kata Containers、gVisor)。
|
||||||
|
|
||||||
|
* [WebAssembly](17.8_wasm.md)
|
||||||
|
* 一种极具潜力的轻量级跨平台二进制指令格式。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## 18.1 内核命名空间
|
## 18.1 内核命名空间
|
||||||
|
|
||||||
命名空间 (Namespace) 是 Linux 容器隔离的基础,它确保了容器内的进程无法直接干扰主机或其他容器。虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace,但在本节中,我们将重点探讨其**安全意义**及相关配置。
|
命名空间 (Namespace) 是 Linux 容器隔离的基础,它确保了容器内的进程无法直接干扰主机或其他容器。虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace,但在本节中,我们将重点探讨其 **安全意义** 及相关配置。
|
||||||
|
|
||||||
### 18.1.1 隔离的安全本质
|
### 18.1.1 隔离的安全本质
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## 18.2 控制组
|
## 18.2 控制组
|
||||||
|
|
||||||
控制组 (Cgroups) 是 Linux 容器机制的另外一个关键组件。如果说命名空间 (Namespace) 决定了容器能**看到**什么,那么控制组就决定了容器能**使用**多少资源。
|
控制组 (Cgroups) 是 Linux 容器机制的另外一个关键组件。如果说命名空间 (Namespace) 决定了容器能 **看到** 什么,那么控制组就决定了容器能 **使用** 多少资源。
|
||||||
|
|
||||||
在安全领域中,资源的不可用性本身就是一种安全威胁。控制组负责实现资源的审计和限制,这对于抵御资源耗尽型攻击(如拒绝服务攻击 DoS)至关重要。
|
在安全领域中,资源的不可用性本身就是一种安全威胁。控制组负责实现资源的审计和限制,这对于抵御资源耗尽型攻击(如拒绝服务攻击 DoS)至关重要。
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
### 18.2.2 核心资源限制实战
|
### 18.2.2 核心资源限制实战
|
||||||
|
|
||||||
为了确保多租户平台(如公有或私有的 PaaS 平台)的稳定性,或者在生产环境防止服务级联故障,我们要养成在启动容器时**显式声明资源上限**的习惯。
|
为了确保多租户平台(如公有或私有的 PaaS 平台)的稳定性,或者在生产环境防止服务级联故障,我们要养成在启动容器时 **显式声明资源上限** 的习惯。
|
||||||
|
|
||||||
#### 1. 内存限制
|
#### 1. 内存限制
|
||||||
|
|
||||||
|
|||||||
@@ -83,4 +83,4 @@ $ docker version
|
|||||||
|
|
||||||
### 18.3.4 结语
|
### 18.3.4 结语
|
||||||
|
|
||||||
保障 Docker 服务端的安全主要是做减法:关闭不必要的网络监听点,严管 Socket 访问权限。而一旦基础系统条件允许,**毫不犹豫地在生产环境启用 Rootless 模式**将是一项划算的安全加固选择。
|
保障 Docker 服务端的安全主要是做减法:关闭不必要的网络监听点,严管 Socket 访问权限。而一旦基础系统条件允许,**毫不犹豫地在生产环境启用 Rootless 模式** 将是一项划算的安全加固选择。
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
### 18.4.1 容器内置的 Capability 白名单
|
### 18.4.1 容器内置的 Capability 白名单
|
||||||
|
|
||||||
在默认情况下,即便一个容器是在以 `root` 用户运行,Docker 也只为其内核授予了所有可用能力中的**一小部分“白名单”能力**。
|
在默认情况下,即便一个容器是在以 `root` 用户运行,Docker 也只为其内核授予了所有可用能力中的 **一小部分“白名单”能力**。
|
||||||
|
|
||||||
常见的 Linux Capabilities 包含:
|
常见的 Linux Capabilities 包含:
|
||||||
- `CAP_CHOWN`: 修改文件所有者。
|
- `CAP_CHOWN`: 修改文件所有者。
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
- `CAP_NET_ADMIN`: 网络管理的最高权限(例如调整路由配置,设置防火墙规则等)。
|
- `CAP_NET_ADMIN`: 网络管理的最高权限(例如调整路由配置,设置防火墙规则等)。
|
||||||
- `CAP_SYS_ADMIN`: 被誉为“Linux 内核的特权网管”,允许各种高危操作(挂载磁盘、访问敏感设备等)。
|
- `CAP_SYS_ADMIN`: 被誉为“Linux 内核的特权网管”,允许各种高危操作(挂载磁盘、访问敏感设备等)。
|
||||||
|
|
||||||
为了在**“最小特权原则”**的指导下加强安全,Docker 默认**移除了**大量可能导致容器大范围破坏宿主机的能力,例如:
|
为了在 **“最小特权原则”** 的指导下加强安全,Docker 默认 **移除了** 大量可能导致容器大范围破坏宿主机的能力,例如:
|
||||||
* 完全禁止了任何通过 `CAP_SYS_ADMIN` 进行的核心挂载或设备操作。
|
* 完全禁止了任何通过 `CAP_SYS_ADMIN` 进行的核心挂载或设备操作。
|
||||||
* 禁止修改内核模块。
|
* 禁止修改内核模块。
|
||||||
* 禁止直接访问硬件套接字。
|
* 禁止直接访问硬件套接字。
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#### 实战场景一:构建极限安全的 Web 靶机
|
#### 实战场景一:构建极限安全的 Web 靶机
|
||||||
|
|
||||||
假设你正在提供一个公共的 Web 容器。你不希望里面的任何恶意脚本修改进程权限或者创建设备节点,你可以通过命令先移除**所有**默认能力,然后再按需授权该守护进程一个仅仅能绑端口的能力。
|
假设你正在提供一个公共的 Web 容器。你不希望里面的任何恶意脚本修改进程权限或者创建设备节点,你可以通过命令先移除 **所有** 默认能力,然后再按需授权该守护进程一个仅仅能绑端口的能力。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker run -d \
|
$ docker run -d \
|
||||||
@@ -56,7 +56,7 @@ $ docker run -it --rm \
|
|||||||
我们只授予了所需的网络管理控制(NET_ADMIN)和侦听底层套接字的权限(NET_RAW),而免去了赋予整个容器终极杀器 `--privileged` 参数。
|
我们只授予了所需的网络管理控制(NET_ADMIN)和侦听底层套接字的权限(NET_RAW),而免去了赋予整个容器终极杀器 `--privileged` 参数。
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> 大量开发人员遇到了“权限遭到拒绝”的错误时,往往习惯性图省事添加 `--privileged` 这个核选项。但这将把**宿主机上一切特权和所有访问设备完全投射给容器内的根用户**,其危险性等价于根本没有做隔离!请务必查明进程出错的实际原因,精准施加必要的隔离 `CAP_*` 能力。
|
> 大量开发人员遇到了“权限遭到拒绝”的错误时,往往习惯性图省事添加 `--privileged` 这个核选项。但这将把 **宿主机上一切特权和所有访问设备完全投射给容器内的根用户**,其危险性等价于根本没有做隔离!请务必查明进程出错的实际原因,精准施加必要的隔离 `CAP_*` 能力。
|
||||||
|
|
||||||
### 18.4.3 总结
|
### 18.4.3 总结
|
||||||
|
|
||||||
|
|||||||
@@ -85,4 +85,4 @@ Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0)
|
|||||||
|
|
||||||
### 18.5.4 容器核心层基石结语
|
### 18.5.4 容器核心层基石结语
|
||||||
|
|
||||||
到这里,Docker 为保障宿主和容器界限安全的几个护城河:从**资源剥离限制** (`Cgroups`) 到**进程/网络/身份蒙蔽** (`Namespace`)、到**特权能力回收** (`Capabilities`) 再到**内核强制策略拦截管制** (`Seccomp`/`AppArmor`) 已悉数交代完毕。虽然绝没有“100% 免疫网络穿刺的防线”,只要开发者牢记 **权限最小化原则** ,容器的堡垒就可以做到令攻击者望洋兴叹。
|
到这里,Docker 为保障宿主和容器界限安全的几个护城河:从 **资源剥离限制**(`Cgroups`) 到 **进程/网络/身份蒙蔽**(`Namespace`)、到 **特权能力回收**(`Capabilities`) 再到 **内核强制策略拦截管制**(`Seccomp`/`AppArmor`) 已悉数交代完毕。虽然绝没有“100% 免疫网络穿刺的防线”,只要开发者牢记 **权限最小化原则** ,容器的堡垒就可以做到令攻击者望洋兴叹。
|
||||||
|
|||||||
558
18_security/18.6_image_security.md
Normal file
558
18_security/18.6_image_security.md
Normal file
@@ -0,0 +1,558 @@
|
|||||||
|
## 18.6 容器镜像安全扫描与供应链安全
|
||||||
|
|
||||||
|
在 DevOps 流程中,容器镜像安全已经成为不容忽视的关键环节。从开发、构建、存储到部署,镜像的整个生命周期都需要安全防护。本节深入讨论镜像漏洞扫描、软件物料清单(SBOM)、镜像签名验证等供应链安全实践。
|
||||||
|
|
||||||
|
### 18.6.1 容器镜像漏洞扫描工具对比
|
||||||
|
|
||||||
|
#### Trivy - 轻量级通用扫描器
|
||||||
|
|
||||||
|
Trivy 是由 Aqua Security 开发的开源漏洞扫描器,以其轻量级、快速、准确而闻名,已成为业界标准。
|
||||||
|
|
||||||
|
**优点:**
|
||||||
|
- 零依赖,单个二进制文件
|
||||||
|
- 扫描速度快(秒级)
|
||||||
|
- 支持镜像、文件系统、Git 仓库多种扫描源
|
||||||
|
- 数据库每日自动更新
|
||||||
|
- 支持多种输出格式(JSON、表格、SBOM 等)
|
||||||
|
|
||||||
|
**安装与基本使用:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 Trivy
|
||||||
|
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
|
||||||
|
# 扫描本地镜像
|
||||||
|
trivy image nginx:latest
|
||||||
|
|
||||||
|
# 生成 JSON 格式报告
|
||||||
|
trivy image -f json -o report.json nginx:latest
|
||||||
|
|
||||||
|
# 扫描文件系统
|
||||||
|
trivy fs /path/to/project
|
||||||
|
|
||||||
|
# 扫描 Git 仓库
|
||||||
|
trivy repo https://github.com/aquasecurity/trivy
|
||||||
|
```
|
||||||
|
|
||||||
|
**在 CI/CD 中集成:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 设置严重程度过滤
|
||||||
|
trivy image --severity HIGH,CRITICAL \
|
||||||
|
--exit-code 1 \
|
||||||
|
myregistry.com/myapp:v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Grype - 支持多种软件包的扫描器
|
||||||
|
|
||||||
|
Grype 由 Anchore 开发,支持更广泛的软件包管理器和语言。
|
||||||
|
|
||||||
|
**优点:**
|
||||||
|
- 支持 Java、Python、Go、Ruby、JavaScript 等多种语言的依赖检测
|
||||||
|
- 与 Syft(SBOM 生成器)配合效果好
|
||||||
|
- 可自定义漏洞数据库源
|
||||||
|
- 支持离线扫描模式
|
||||||
|
|
||||||
|
**安装与使用:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 Grype
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
|
||||||
|
# 扫描镜像
|
||||||
|
grype docker:nginx:latest
|
||||||
|
|
||||||
|
# 与 Syft 配合生成 SBOM
|
||||||
|
syft docker:nginx:latest -o json > sbom.json
|
||||||
|
grype sbom:sbom.json
|
||||||
|
|
||||||
|
# 扫描特定目录
|
||||||
|
grype dir:/path/to/app
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Snyk - 完整的安全平台
|
||||||
|
|
||||||
|
Snyk 提供了商业级的安全扫描服务,特别适合企业环境。
|
||||||
|
|
||||||
|
**特点:**
|
||||||
|
- 支持开源漏洞和许可证扫描
|
||||||
|
- 与多个 Git 平台深度集成(GitHub、GitLab、Bitbucket)
|
||||||
|
- 提供修复建议和自动化修复 PR
|
||||||
|
- 支持 Kubernetes 部署后安全监控
|
||||||
|
|
||||||
|
**基本使用:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 Snyk CLI
|
||||||
|
npm install -g snyk
|
||||||
|
|
||||||
|
# 认证
|
||||||
|
snyk auth
|
||||||
|
|
||||||
|
# 扫描镜像
|
||||||
|
snyk container test docker-archive://image.tar
|
||||||
|
|
||||||
|
# 监控仓库
|
||||||
|
snyk monitor --docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**工具对比表:**
|
||||||
|
|
||||||
|
| 特性 | Trivy | Grype | Snyk |
|
||||||
|
|------|-------|-------|------|
|
||||||
|
| 零依赖 | ✓ | ✗ | ✗ |
|
||||||
|
| 离线模式 | ✓ | ✓ | ✗ |
|
||||||
|
| 许可证扫描 | ✗ | ✓ | ✓ |
|
||||||
|
| 自动修复 | ✗ | ✗ | ✓ |
|
||||||
|
| 开源免费 | ✓ | ✓ | 部分 |
|
||||||
|
| IDE 集成 | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
### 18.6.2 SBOM(软件物料清单)生成与管理
|
||||||
|
|
||||||
|
SBOM(Software Bill of Materials)是一份详细列表,记录了软件中使用的所有组件、依赖库及其版本信息。SBOM 在供应链安全中至关重要,特别是在发现新的安全漏洞时,能快速定位受影响的应用。
|
||||||
|
|
||||||
|
#### Syft - SBOM 生成工具
|
||||||
|
|
||||||
|
Syft 是 Anchore 推出的专业 SBOM 生成工具。
|
||||||
|
|
||||||
|
**安装:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**生成 SBOM:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 从镜像生成 SBOM(多种格式)
|
||||||
|
syft docker:nginx:latest -o json > sbom.json
|
||||||
|
syft docker:nginx:latest -o spdx > sbom.spdx
|
||||||
|
syft docker:nginx:latest -o cyclonedx > sbom.xml
|
||||||
|
|
||||||
|
# 从本地文件系统生成
|
||||||
|
syft dir:/path/to/app -o json > sbom.json
|
||||||
|
|
||||||
|
# 从 OCI 镜像档案生成
|
||||||
|
syft oci-archive:image.tar -o json > sbom.json
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CycloneDX 与 SPDX 格式
|
||||||
|
|
||||||
|
两种主流的 SBOM 格式:
|
||||||
|
|
||||||
|
**CycloneDX 格式示例:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" version="1">
|
||||||
|
<components>
|
||||||
|
<component type="library">
|
||||||
|
<name>openssl</name>
|
||||||
|
<version>1.1.1k</version>
|
||||||
|
<purl>pkg:deb/debian/openssl@1.1.1k-1+deb11u5</purl>
|
||||||
|
</component>
|
||||||
|
<component type="library">
|
||||||
|
<name>curl</name>
|
||||||
|
<version>7.74.0-1.3+deb11u1</version>
|
||||||
|
<purl>pkg:deb/debian/curl@7.74.0-1.3+deb11u1</purl>
|
||||||
|
</component>
|
||||||
|
</components>
|
||||||
|
</bom>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SPDX 格式示例:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2024-03-01T12:00:00Z",
|
||||||
|
"creators": ["Tool: syft"]
|
||||||
|
},
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-Package-openssl",
|
||||||
|
"name": "openssl",
|
||||||
|
"versionInfo": "1.1.1k",
|
||||||
|
"downloadLocation": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SBOM 的应用场景
|
||||||
|
|
||||||
|
**漏洞关联:**
|
||||||
|
|
||||||
|
当新的 CVE 被发现时,可快速查询受影响的应用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 Grype 针对 SBOM 进行漏洞扫描
|
||||||
|
grype sbom:sbom.json --add-cpes-if-none
|
||||||
|
```
|
||||||
|
|
||||||
|
**合规性报告:**
|
||||||
|
|
||||||
|
将 SBOM 保存为构建产物,用于审计和合规性检查。
|
||||||
|
|
||||||
|
**依赖升级决策:**
|
||||||
|
|
||||||
|
通过分析 SBOM 中的依赖版本,制定安全升级计划。
|
||||||
|
|
||||||
|
### 18.6.3 镜像签名与验证(Cosign/Notary)
|
||||||
|
|
||||||
|
镜像签名确保镜像的来源可信且未被篡改。两种主流方案是 Cosign 和 Notary。
|
||||||
|
|
||||||
|
#### Cosign - 现代签名解决方案
|
||||||
|
|
||||||
|
Cosign 是 Sigstore 项目的核心工具,支持无密钥签名,适合现代 CI/CD 流程。
|
||||||
|
|
||||||
|
**安装:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
|
||||||
|
chmod +x cosign-linux-amd64
|
||||||
|
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
|
||||||
|
```
|
||||||
|
|
||||||
|
**生成密钥对(传统方式):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cosign generate-key-pair
|
||||||
|
# 生成 cosign.key 和 cosign.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
**签名镜像:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用私钥签名(推送到仓库前)
|
||||||
|
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
|
||||||
|
|
||||||
|
# 系统会提示输入私钥密码
|
||||||
|
```
|
||||||
|
|
||||||
|
**验证签名:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用公钥验证
|
||||||
|
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
|
||||||
|
|
||||||
|
# 输出结果示例
|
||||||
|
# Verification successful!
|
||||||
|
# {
|
||||||
|
# "critical": {
|
||||||
|
# "identity": {...},
|
||||||
|
# "image": {...},
|
||||||
|
# "type": "cosign container image signature"
|
||||||
|
# },
|
||||||
|
# "optional": {...}
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Keyless 签名(推荐用于 CI/CD):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在 GitHub Actions 等 CI 中无需存储密钥
|
||||||
|
cosign sign --yes myregistry.com/myapp:v1.0.0
|
||||||
|
|
||||||
|
# 验证时自动使用 OIDC 令牌验证身份
|
||||||
|
cosign verify myregistry.com/myapp:v1.0.0 \
|
||||||
|
--certificate-identity https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main \
|
||||||
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Content Trust(DCT)与 Notary
|
||||||
|
|
||||||
|
Docker Content Trust 使用 Notary 实现镜像签名,是 Docker 官方的签名解决方案。
|
||||||
|
|
||||||
|
**启用 DCT:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在环境中启用 DCT
|
||||||
|
export DOCKER_CONTENT_TRUST=1
|
||||||
|
|
||||||
|
# 此后所有 docker push/pull 都需要签名
|
||||||
|
docker push myregistry.com/myapp:v1.0.0
|
||||||
|
# 如果镜像未签名,操作会被拒绝
|
||||||
|
|
||||||
|
# 禁用 DCT(仅用于特定操作)
|
||||||
|
docker push --disable-content-trust myregistry.com/myapp:v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**签名密钥管理:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 首次推送时会提示创建 Delegation Key
|
||||||
|
# 密钥存储在 ~/.docker/trust/private/root_keys/ 和 ~/.docker/trust/private/tuf_keys/
|
||||||
|
|
||||||
|
# 查看签名信息
|
||||||
|
docker inspect --format='{{.RepoDigests}}' myregistry.com/myapp:v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.6.4 供应链安全最佳实践
|
||||||
|
|
||||||
|
#### 1. 基础镜像安全
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ❌ 不推荐:使用 latest 标签
|
||||||
|
FROM ubuntu:latest
|
||||||
|
RUN apt-get update && apt-get install -y curl
|
||||||
|
|
||||||
|
# ✓ 推荐:固定基础镜像版本和摘要
|
||||||
|
FROM ubuntu:22.04@sha256:a6d2b38300ce017add71440577d5b0a90460d0e6...
|
||||||
|
RUN apt-get update && apt-get install -y curl=7.68.0-1ubuntu1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 构建时扫描
|
||||||
|
|
||||||
|
在 Dockerfile 中集成安全扫描:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM golang:1.20-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 使用 Trivy 扫描源代码
|
||||||
|
RUN apk add --no-cache curl && \
|
||||||
|
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin && \
|
||||||
|
trivy fs . --exit-code 1 --severity HIGH,CRITICAL
|
||||||
|
|
||||||
|
RUN go build -o app .
|
||||||
|
|
||||||
|
FROM alpine:3.17@sha256:abcd...
|
||||||
|
COPY --from=builder /app/app /app
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 运行时镜像扫描策略
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 镜像构建完成后立即扫描
|
||||||
|
trivy image --severity HIGH,CRITICAL \
|
||||||
|
--exit-code 1 \
|
||||||
|
--timeout 30m \
|
||||||
|
$IMAGE_NAME:$IMAGE_TAG
|
||||||
|
|
||||||
|
# 定期扫描已部署的镜像
|
||||||
|
trivy image --scanners vuln,misconfig registry:5000/myapp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. 镜像仓库安全配置
|
||||||
|
|
||||||
|
**Harbor(私有镜像仓库)的安全扫描:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# harbor.yml 配置示例
|
||||||
|
trivy:
|
||||||
|
enabled: true
|
||||||
|
# 启用镜像扫描
|
||||||
|
image_source: "Official"
|
||||||
|
|
||||||
|
# 默认扫描配置
|
||||||
|
scan_on_push: true # 推送时自动扫描
|
||||||
|
scan_all: true # 扫描仓库中的所有镜像
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. 政策执行(Admission Controller)
|
||||||
|
|
||||||
|
在 Kubernetes 环境中使用 Admission Webhook 强制镜像签名和扫描:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: image-security-policy
|
||||||
|
webhooks:
|
||||||
|
- name: image-security.example.com
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: image-security-webhook
|
||||||
|
namespace: security
|
||||||
|
path: "/validate"
|
||||||
|
rules:
|
||||||
|
- operations: ["CREATE", "UPDATE"]
|
||||||
|
apiGroups: [""]
|
||||||
|
apiVersions: ["v1"]
|
||||||
|
resources: ["pods"]
|
||||||
|
admissionReviewVersions: ["v1"]
|
||||||
|
sideEffects: None
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.6.5 CI/CD 中集成安全扫描
|
||||||
|
|
||||||
|
#### GitHub Actions 工作流示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Build and Scan Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: false
|
||||||
|
load: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
|
||||||
|
- name: Run Trivy vulnerability scan
|
||||||
|
uses: aquasecurity/trivy-action@master
|
||||||
|
with:
|
||||||
|
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
format: 'sarif'
|
||||||
|
output: 'trivy-results.sarif'
|
||||||
|
severity: 'HIGH,CRITICAL'
|
||||||
|
|
||||||
|
- name: Upload Trivy results to GitHub Security tab
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
with:
|
||||||
|
sarif_file: 'trivy-results.sarif'
|
||||||
|
|
||||||
|
- name: Generate SBOM
|
||||||
|
uses: anchore/sbom-action@v0
|
||||||
|
with:
|
||||||
|
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
format: cyclonedx-json
|
||||||
|
output-file: sbom-cyclonedx.json
|
||||||
|
|
||||||
|
- name: Upload SBOM
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: sbom
|
||||||
|
path: sbom-cyclonedx.json
|
||||||
|
|
||||||
|
- name: Sign image with Cosign
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: 1
|
||||||
|
run: |
|
||||||
|
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
|
||||||
|
- name: Login to Registry and Push
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Push image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GitLab CI 工作流示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- scan
|
||||||
|
- sign
|
||||||
|
- push
|
||||||
|
|
||||||
|
variables:
|
||||||
|
REGISTRY: registry.gitlab.com
|
||||||
|
IMAGE_NAME: $REGISTRY/$CI_PROJECT_PATH
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
script:
|
||||||
|
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
|
||||||
|
- docker save $IMAGE_NAME:$CI_COMMIT_SHA > image.tar
|
||||||
|
|
||||||
|
scan:trivy:
|
||||||
|
stage: scan
|
||||||
|
image: aquasec/trivy:latest
|
||||||
|
script:
|
||||||
|
- trivy image --severity HIGH,CRITICAL --exit-code 1 docker-archive://image.tar
|
||||||
|
allow_failure: false
|
||||||
|
|
||||||
|
scan:grype:
|
||||||
|
stage: scan
|
||||||
|
image: anchore/grype:latest
|
||||||
|
script:
|
||||||
|
- grype docker-archive://image.tar
|
||||||
|
|
||||||
|
generate:sbom:
|
||||||
|
stage: scan
|
||||||
|
image: anchore/syft:latest
|
||||||
|
script:
|
||||||
|
- syft docker-archive://image.tar -o cyclonedx > sbom.xml
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
sbom: sbom.xml
|
||||||
|
|
||||||
|
sign:
|
||||||
|
stage: sign
|
||||||
|
image: gcr.io/projectsigstore/cosign:latest
|
||||||
|
script:
|
||||||
|
- cosign sign --key $COSIGN_KEY $IMAGE_NAME:$CI_COMMIT_SHA
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
|
||||||
|
push:
|
||||||
|
stage: push
|
||||||
|
image: docker:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
script:
|
||||||
|
- docker load < image.tar
|
||||||
|
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
|
||||||
|
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.6.6 常见问题与最佳实践
|
||||||
|
|
||||||
|
**Q: 扫描报告中有过时的 CVE,如何处理?**
|
||||||
|
|
||||||
|
A: 某些 CVE 可能已经被修复但数据库未更新,可以:
|
||||||
|
- 手动验证安全补丁是否已应用
|
||||||
|
- 使用工具的忽略列表功能(如 Trivy 的 `.trivyignore`)
|
||||||
|
- 定期更新扫描工具和漏洞数据库
|
||||||
|
|
||||||
|
**Q: 如何平衡镜像大小和安全性?**
|
||||||
|
|
||||||
|
A:
|
||||||
|
- 使用多阶段构建减少最终镜像大小
|
||||||
|
- 使用精简基础镜像(Alpine、Distroless)
|
||||||
|
- 定期更新依赖而不是一味求小
|
||||||
|
- 优先安全性,体积次之
|
||||||
|
|
||||||
|
**Q: 如何管理和轮换签名密钥?**
|
||||||
|
|
||||||
|
A:
|
||||||
|
- 在密钥管理系统(如 HashiCorp Vault)中存储密钥
|
||||||
|
- 定期轮换密钥(建议每 90 天)
|
||||||
|
- 使用 Keyless 签名消除密钥管理复杂性
|
||||||
|
- 保留密钥轮换的审计日志
|
||||||
@@ -43,6 +43,9 @@ flowchart LR
|
|||||||
* [其它安全特性](18.5_other_feature.md)
|
* [其它安全特性](18.5_other_feature.md)
|
||||||
* 镜像安全(漏洞扫描、签名验证)、运行时安全(非 root 运行、只读文件系统、Seccomp、AppArmor)、Dockerfile 安全实践、软件供应链安全(SBOM、SLSA)。
|
* 镜像安全(漏洞扫描、签名验证)、运行时安全(非 root 运行、只读文件系统、Seccomp、AppArmor)、Dockerfile 安全实践、软件供应链安全(SBOM、SLSA)。
|
||||||
|
|
||||||
|
* [镜像安全](18.6_image_security.md)
|
||||||
|
* 容器镜像的安全扫描、漏洞检测与签名验证。
|
||||||
|
|
||||||
## 安全扫描清单
|
## 安全扫描清单
|
||||||
|
|
||||||
部署前检查:
|
部署前检查:
|
||||||
|
|||||||
638
19_observability/19.3_performance_optimization.md
Normal file
638
19_observability/19.3_performance_optimization.md
Normal file
@@ -0,0 +1,638 @@
|
|||||||
|
## 19.3 容器性能优化与故障诊断
|
||||||
|
|
||||||
|
容器的轻量级特性不代表性能问题会自动消失。在实际运维中,性能瓶颈可能来自 CPU 限制、内存溢出、磁盘 I/O、网络拥塞等多个层面。本节深入讨论容器性能监控、诊断方法和优化策略。
|
||||||
|
|
||||||
|
### 19.3.1 容器性能监控指标
|
||||||
|
|
||||||
|
#### 核心性能指标体系
|
||||||
|
|
||||||
|
容器性能监控涉及以下关键指标:
|
||||||
|
|
||||||
|
**CPU 相关指标:**
|
||||||
|
- `cpu.usage_usec`:容器 CPU 使用时间(微秒)
|
||||||
|
- `cpu.stat.nr_throttled`:CPU 限流发生次数
|
||||||
|
- `cpu.stat.throttled_usec`:CPU 限流总时间
|
||||||
|
- `cpu_percent`:CPU 使用百分比
|
||||||
|
- `cpu_quota`:CPU 配额设置(微秒)
|
||||||
|
|
||||||
|
**内存相关指标:**
|
||||||
|
- `memory.usage_bytes`:当前内存使用量
|
||||||
|
- `memory.max_usage_bytes`:内存使用峰值
|
||||||
|
- `memory.limit_in_bytes`:内存限制
|
||||||
|
- `memory.fail_cnt`:OOM(Out of Memory)失败次数
|
||||||
|
- `memory.stat.cache`:页面缓存占用
|
||||||
|
- `memory.stat.rss`:实际内存占用(RSS)
|
||||||
|
- `memory.stat.swap`:SWAP 使用量
|
||||||
|
|
||||||
|
**网络相关指标:**
|
||||||
|
- `rx_bytes`:接收字节数
|
||||||
|
- `tx_bytes`:发送字节数
|
||||||
|
- `rx_packets`:接收包数
|
||||||
|
- `tx_packets`:发送包数
|
||||||
|
- `rx_errors`:接收错误数
|
||||||
|
- `tx_errors`:发送错误数
|
||||||
|
- `rx_dropped`:接收丢包数
|
||||||
|
- `tx_dropped`:发送丢包数
|
||||||
|
|
||||||
|
**I/O 相关指标:**
|
||||||
|
- `io_service_bytes`:I/O 操作字节数
|
||||||
|
- `io_service_time`:I/O 操作耗时
|
||||||
|
- `io_queued`:I/O 队列长度
|
||||||
|
- `fs_limit_bytes`:文件系统限制
|
||||||
|
- `fs_usage_bytes`:文件系统使用量
|
||||||
|
|
||||||
|
### 19.3.2 使用 docker stats 实时监控
|
||||||
|
|
||||||
|
`docker stats` 是最基础但强大的监控工具,提供实时的容器资源使用情况。
|
||||||
|
|
||||||
|
**基本使用:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 实时监控所有运行中的容器
|
||||||
|
docker stats
|
||||||
|
|
||||||
|
# 输出示例:
|
||||||
|
# CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
|
||||||
|
# abc123def456 nginx 0.45% 24.3 MiB / 256 MiB 9.49% 1.2kB / 3.4kB 0 B / 0 B
|
||||||
|
# def789ghi012 redis 0.23% 12.5 MiB / 512 MiB 2.44% 2.1kB / 1.5kB 0 B / 0 B
|
||||||
|
|
||||||
|
# 只监控特定容器
|
||||||
|
docker stats nginx redis
|
||||||
|
|
||||||
|
# 一次性输出不进入交互模式
|
||||||
|
docker stats --no-stream
|
||||||
|
|
||||||
|
# 指定刷新间隔(单位:秒,默认 1 秒)
|
||||||
|
docker stats --no-stream --interval 2
|
||||||
|
|
||||||
|
# 格式化输出(使用 Go 模板)
|
||||||
|
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" --no-stream
|
||||||
|
|
||||||
|
# 导出为 JSON 格式用于日志记录
|
||||||
|
docker stats --format json --no-stream > stats.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**在脚本中使用:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 持续监控并记录到文件
|
||||||
|
while true; do
|
||||||
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
docker stats --no-stream --format "{{.Container}},{{.CPUPerc}},{{.MemUsage}}" | \
|
||||||
|
awk -v ts="$timestamp" '{print ts","$0}' >> container_stats.log
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
**性能指标解读:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CPU % 超过 80%:需要增加 CPU 限制或优化应用
|
||||||
|
# MEM % 接近 100%:容器即将 OOM,需要增加内存或排查内存泄漏
|
||||||
|
# 如果 NET I/O 中 dropped 为非零:网络拥塞或丢包
|
||||||
|
```
|
||||||
|
|
||||||
|
### 19.3.3 cAdvisor 容器监控系统
|
||||||
|
|
||||||
|
cAdvisor 是 Google 开发的容器监控工具,提供比 `docker stats` 更详细的性能数据。
|
||||||
|
|
||||||
|
**Docker Compose 部署 cAdvisor:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
cadvisor:
|
||||||
|
image: gcr.io/cadvisor/cadvisor:v0.47.0
|
||||||
|
container_name: cadvisor
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker/:/var/lib/docker:ro
|
||||||
|
- /dev/disk/:/dev/disk:ro
|
||||||
|
privileged: true
|
||||||
|
devices:
|
||||||
|
- /dev/kmsg
|
||||||
|
networks:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
|
networks:
|
||||||
|
monitoring:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后访问 `http://localhost:8080` 查看:
|
||||||
|
- 容器性能统计
|
||||||
|
- 系统资源使用情况
|
||||||
|
- 历史性能数据
|
||||||
|
|
||||||
|
**从 cAdvisor 提取指标:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 获取所有容器的 JSON 格式性能数据
|
||||||
|
curl http://localhost:8080/api/v1.3/machine | jq .
|
||||||
|
|
||||||
|
# 获取特定容器信息
|
||||||
|
curl http://localhost:8080/api/v1.3/docker | jq '.docker | keys' | head -5
|
||||||
|
|
||||||
|
# 获取容器统计信息
|
||||||
|
curl http://localhost:8080/api/v1.3/docker/abc123/ | jq '.stats[-1]'
|
||||||
|
```
|
||||||
|
|
||||||
|
**与 Prometheus 集成:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# prometheus.yml 配置
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'cadvisor'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:8080']
|
||||||
|
metrics_path: '/metrics'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 19.3.4 Prometheus 容器监控配置
|
||||||
|
|
||||||
|
使用 Prometheus 和 node-exporter 进行长期的容器性能监控。
|
||||||
|
|
||||||
|
**完整监控栈部署:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: prometheus
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
- prometheus_data:/prometheus
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
- '--storage.tsdb.path=/prometheus'
|
||||||
|
- '--storage.tsdb.retention.time=30d'
|
||||||
|
networks:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
container_name: node-exporter
|
||||||
|
ports:
|
||||||
|
- "9100:9100"
|
||||||
|
volumes:
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
- /:/rootfs:ro
|
||||||
|
command:
|
||||||
|
- '--path.procfs=/host/proc'
|
||||||
|
- '--path.sysfs=/host/sys'
|
||||||
|
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
|
||||||
|
networks:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
|
cadvisor:
|
||||||
|
image: gcr.io/cadvisor/cadvisor:v0.47.0
|
||||||
|
container_name: cadvisor
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker/:/var/lib/docker:ro
|
||||||
|
privileged: true
|
||||||
|
networks:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: grafana
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||||
|
- GF_INSTALL_PLUGINS=grafana-piechart-panel
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
networks:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
prometheus_data:
|
||||||
|
grafana_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
monitoring:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prometheus 配置文件(prometheus.yml):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
evaluation_interval: 15s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:9090']
|
||||||
|
|
||||||
|
- job_name: 'node-exporter'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['node-exporter:9100']
|
||||||
|
|
||||||
|
- job_name: 'cadvisor'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['cadvisor:8080']
|
||||||
|
|
||||||
|
- job_name: 'docker'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:9323']
|
||||||
|
```
|
||||||
|
|
||||||
|
**常用的 Prometheus 查询(PromQL):**
|
||||||
|
|
||||||
|
```promql
|
||||||
|
# 容器 CPU 使用百分比
|
||||||
|
rate(container_cpu_usage_seconds_total[5m]) * 100
|
||||||
|
|
||||||
|
# 容器内存使用百分比
|
||||||
|
(container_memory_usage_bytes / container_spec_memory_limit_bytes) * 100
|
||||||
|
|
||||||
|
# 容器网络入站流量(MB/s)
|
||||||
|
rate(container_network_receive_bytes_total[5m]) / 1024 / 1024
|
||||||
|
|
||||||
|
# 容器网络出站流量(MB/s)
|
||||||
|
rate(container_network_transmit_bytes_total[5m]) / 1024 / 1024
|
||||||
|
|
||||||
|
# 容器磁盘读取速率(MB/s)
|
||||||
|
rate(container_fs_io_current[5m]) / 1024 / 1024
|
||||||
|
|
||||||
|
# CPU 限流情况
|
||||||
|
rate(container_cpu_cfs_throttled_seconds_total[5m])
|
||||||
|
|
||||||
|
# 内存缓存占比
|
||||||
|
container_memory_cache_bytes / container_memory_usage_bytes
|
||||||
|
|
||||||
|
# 按镜像统计容器数
|
||||||
|
count(container_memory_usage_bytes) by (image)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 19.3.5 容器 OOM 排查与内存限制调优
|
||||||
|
|
||||||
|
#### OOM 问题诊断
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查容器是否因 OOM 被杀死
|
||||||
|
docker inspect <container_id> | grep OOMKilled
|
||||||
|
|
||||||
|
# 查看容器退出码:137 表示被 OOM 杀死
|
||||||
|
docker ps -a --format "{{.ID}}\t{{.Status}}" | grep "137"
|
||||||
|
|
||||||
|
# 查看容器日志中的 OOM 信息
|
||||||
|
docker logs <container_id> 2>&1 | grep -i "out of memory\|oom"
|
||||||
|
|
||||||
|
# 从宿主机日志查看 OOM 事件
|
||||||
|
dmesg | grep -i "oom\|kill"
|
||||||
|
journalctl -u docker -n 100 | grep -i "oom"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 内存泄漏检测
|
||||||
|
|
||||||
|
使用专项工具分析应用内存使用:
|
||||||
|
|
||||||
|
**Python 应用内存泄漏检测:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt memory_profiler tracemalloc
|
||||||
|
|
||||||
|
COPY app.py .
|
||||||
|
CMD ["python", "-m", "memory_profiler", "app.py"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# app.py - 内存泄漏示例
|
||||||
|
from memory_profiler import profile
|
||||||
|
import tracemalloc
|
||||||
|
|
||||||
|
@profile
|
||||||
|
def memory_leak():
|
||||||
|
# 不断创建未释放的列表
|
||||||
|
data = []
|
||||||
|
while True:
|
||||||
|
data.append([0] * 1000000)
|
||||||
|
print(f"List size: {len(data)}")
|
||||||
|
|
||||||
|
# 使用 tracemalloc 跟踪内存分配
|
||||||
|
tracemalloc.start()
|
||||||
|
|
||||||
|
# 执行可能泄漏的代码
|
||||||
|
# ...
|
||||||
|
|
||||||
|
current, peak = tracemalloc.get_traced_memory()
|
||||||
|
print(f"Current: {current / 1024 / 1024:.2f} MB")
|
||||||
|
print(f"Peak: {peak / 1024 / 1024:.2f} MB")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Java 应用内存分析:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在容器中启用 JVM 远程调试
|
||||||
|
docker run -e JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC" \
|
||||||
|
-p 5005:5005 \
|
||||||
|
myapp:latest
|
||||||
|
|
||||||
|
# 使用 jstat 检查垃圾回收情况
|
||||||
|
jstat -gc <pid> 1000 # 每秒采样一次
|
||||||
|
|
||||||
|
# 输出示例:
|
||||||
|
# S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU
|
||||||
|
# 6144 6144 0 6144 39424 12288 149504 84320 50552 47689 6464 5989
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 内存限制最佳实践
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 为容器设置内存限制
|
||||||
|
docker run -m 512m --memory-swap 1g myapp:latest
|
||||||
|
|
||||||
|
# 参数说明:
|
||||||
|
# -m / --memory:内存限制(这里是 512MB)
|
||||||
|
# --memory-swap:内存+SWAP 总额(这里是 1GB,意味着 SWAP 为 512MB)
|
||||||
|
# 如果不设置 --memory-swap,则等于 --memory 值
|
||||||
|
|
||||||
|
# Docker Compose 配置
|
||||||
|
version: '3.9'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: myapp:latest
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
```
|
||||||
|
|
||||||
|
**内存超额提交(Memory Overcommit):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在 Docker Compose 中区分限制和预留
|
||||||
|
# limits:绝不能超过的最大值
|
||||||
|
# reservations:Compose 排期时的参考值
|
||||||
|
|
||||||
|
version: '3.9'
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
memory: 512M # 限制
|
||||||
|
memswap_limit: 1G # SWAP 限制
|
||||||
|
|
||||||
|
db:
|
||||||
|
memory: 2G
|
||||||
|
memory_reservation: 1G # 预留 1GB,允许突发到 2GB
|
||||||
|
```
|
||||||
|
|
||||||
|
### 19.3.6 镜像体积优化与多阶段构建
|
||||||
|
|
||||||
|
#### 镜像体积分析工具
|
||||||
|
|
||||||
|
**使用 dive 分析镜像层:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 dive
|
||||||
|
wget https://github.com/wagoodman/dive/releases/download/v0.11.0/dive_0.11.0_linux_amd64.deb
|
||||||
|
sudo apt install ./dive_0.11.0_linux_amd64.deb
|
||||||
|
|
||||||
|
# 分析镜像
|
||||||
|
dive myapp:latest
|
||||||
|
|
||||||
|
# 输出详细的分层信息,显示每一层的大小和内容
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用 Dockerfile 分析工具:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 hadolint
|
||||||
|
curl https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 -L -o hadolint
|
||||||
|
chmod +x hadolint
|
||||||
|
|
||||||
|
# 检查 Dockerfile 最佳实践
|
||||||
|
./hadolint Dockerfile
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 多阶段构建最佳实践
|
||||||
|
|
||||||
|
**Go 应用的最小化镜像构建:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Stage 1: 构建阶段
|
||||||
|
FROM golang:1.20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建静态二进制(支持 scratch 基础镜像)
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-a -installsuffix cgo \
|
||||||
|
-ldflags="-w -s" \
|
||||||
|
-o app .
|
||||||
|
|
||||||
|
# Stage 2: 运行阶段
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
# 从 builder 复制必要的文件
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
COPY --from=builder /build/app /app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["/app"]
|
||||||
|
|
||||||
|
# 最终镜像大小通常 < 15MB(相比 golang:1.20-alpine 的 ~1GB)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Node.js 应用的多阶段构建:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Stage 1: 依赖安装
|
||||||
|
FROM node:18-alpine AS dependencies
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --only=production && \
|
||||||
|
npm cache clean --force
|
||||||
|
|
||||||
|
# Stage 2: 构建阶段
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 3: 运行阶段
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从依赖阶段复制 node_modules
|
||||||
|
COPY --from=dependencies /app/node_modules ./node_modules
|
||||||
|
# 从构建阶段复制构建产物
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
COPY --from=builder /app/package*.json ./
|
||||||
|
|
||||||
|
# 删除开发依赖和不必要的文件
|
||||||
|
RUN rm -rf src tests *.config.js
|
||||||
|
|
||||||
|
USER node
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
|
|
||||||
|
# 镜像大小对比:
|
||||||
|
# 不优化:~500MB
|
||||||
|
# 多阶段构建后:~120MB(减少 76%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Python 应用的多阶段构建:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Stage 1: 构建阶段
|
||||||
|
FROM python:3.11-slim AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
&& rm -rf /var/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --user --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Stage 2: 运行阶段
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从 builder 复制虚拟环境
|
||||||
|
COPY --from=builder /root/.local /root/.local
|
||||||
|
|
||||||
|
# 设置 PATH
|
||||||
|
ENV PATH=/root/.local/bin:$PATH \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
USER nobody
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
CMD ["python", "app.py"]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 镜像体积优化检查清单
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查清单
|
||||||
|
□ 使用精简基础镜像(Alpine、Distroless)
|
||||||
|
□ 清理包管理器缓存(apt-get clean、rm -rf /var/cache/*)
|
||||||
|
□ 在同一 RUN 指令中安装和清理依赖
|
||||||
|
□ 使用 .dockerignore 排除不必要的文件
|
||||||
|
□ 多阶段构建避免构建依赖污染最终镜像
|
||||||
|
□ 去除调试符号:-ldflags="-w -s"(Go)、strip 命令(C/C++)
|
||||||
|
□ 压缩静态资源和应用文件
|
||||||
|
□ 使用 BuildKit 缓存优化加速构建
|
||||||
|
|
||||||
|
# 优化示例:
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
# ❌ 不推荐
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y curl wget git
|
||||||
|
RUN apt-get clean
|
||||||
|
|
||||||
|
# ✓ 推荐
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
git && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
```
|
||||||
|
|
||||||
|
### 19.3.7 常见性能问题及解决方案
|
||||||
|
|
||||||
|
**问题 1: 容器频繁被 OOM 杀死**
|
||||||
|
|
||||||
|
症状:容器进程被无故杀死,exit code 137
|
||||||
|
解决方案:
|
||||||
|
```bash
|
||||||
|
# 增加内存限制
|
||||||
|
docker update -m 1g <container_id>
|
||||||
|
|
||||||
|
# 排查内存泄漏
|
||||||
|
docker exec <container_id> ps aux | grep -E "VSZ|RSS"
|
||||||
|
|
||||||
|
# 使用 docker stats 实时监控
|
||||||
|
docker stats <container_id>
|
||||||
|
|
||||||
|
# 启用内存交换(作为最后手段)
|
||||||
|
docker run -m 512m --memory-swap 1g myapp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题 2: CPU 被限流(CPU Throttling)**
|
||||||
|
|
||||||
|
症状:应用性能突然下降,但 CPU 使用率不高
|
||||||
|
诊断:
|
||||||
|
```bash
|
||||||
|
# 查看 CPU 限流统计
|
||||||
|
docker exec <container_id> cat /sys/fs/cgroup/cpu/cpu.stat
|
||||||
|
|
||||||
|
# 如果 throttled_time > 0,说明发生了 CPU 限流
|
||||||
|
# 解决方案:增加 CPU 限制
|
||||||
|
docker update --cpus 2 <container_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题 3: 网络丢包或延迟高**
|
||||||
|
|
||||||
|
诊断:
|
||||||
|
```bash
|
||||||
|
# 进入容器检查网络状态
|
||||||
|
docker exec <container_id> ip -s link show
|
||||||
|
|
||||||
|
# 检查路由和 DNS
|
||||||
|
docker exec <container_id> cat /etc/resolv.conf
|
||||||
|
|
||||||
|
# 测试网络延迟
|
||||||
|
docker exec <container_id> ping 8.8.8.8
|
||||||
|
|
||||||
|
# 检查容器网络驱动
|
||||||
|
docker inspect <container_id> | grep -A 10 NetworkSettings
|
||||||
|
|
||||||
|
# 解决方案:更换网络驱动或调整 MTU
|
||||||
|
docker run --net=host myapp:latest # 使用宿主机网络(性能最佳)
|
||||||
|
```
|
||||||
@@ -9,9 +9,20 @@
|
|||||||
- **容器监控**:以 Prometheus 为主,讲解如何采集和展示容器性能指标。
|
- **容器监控**:以 Prometheus 为主,讲解如何采集和展示容器性能指标。
|
||||||
- **日志管理**:以 ELK (Elasticsearch, Logstash, Kibana) 套件为例,介绍集中式日志收集平台。
|
- **日志管理**:以 ELK (Elasticsearch, Logstash, Kibana) 套件为例,介绍集中式日志收集平台。
|
||||||
|
|
||||||
为了让读者能够在生产环境中真正用起来,本章会补齐以下“最小闭环”:
|
为了让读者能够在生产环境中真正用起来,本章会补齐以下”最小闭环”:
|
||||||
|
|
||||||
* 关键指标与日志的验证方法
|
* 关键指标与日志的验证方法
|
||||||
* 常见故障排查路径
|
* 常见故障排查路径
|
||||||
* 最小告警闭环 (Prometheus -> Alertmanager -> 接收端)
|
* 最小告警闭环 (Prometheus -> Alertmanager -> 接收端)
|
||||||
* 日志容量治理的最小实践
|
* 日志容量治理的最小实践
|
||||||
|
|
||||||
|
## 本章内容
|
||||||
|
|
||||||
|
* [Prometheus 监控](19.1_prometheus.md)
|
||||||
|
* 容器监控基础、指标采集与告警配置。
|
||||||
|
|
||||||
|
* [ELK 日志管理](19.2_elk.md)
|
||||||
|
* 集中式日志收集、存储与检索。
|
||||||
|
|
||||||
|
* [性能优化](19.3_performance_optimization.md)
|
||||||
|
* 容器和应用性能优化实践。
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
* **指标监控**:以 Prometheus + Grafana 为主,完成指标采集、存储与可视化。
|
* **指标监控**:以 Prometheus + Grafana 为主,完成指标采集、存储与可视化。
|
||||||
* **日志管理**:以 EFK/ELK 为例,完成容器日志的集中采集、检索与分析。
|
* **日志管理**:以 EFK/ELK 为例,完成容器日志的集中采集、检索与分析。
|
||||||
|
|
||||||
生产环境中,建议将“可观测性”当成一个完整闭环:**采集 -> 存储 -> 展示 -> 告警 -> 排错 -> 容量治理**。
|
生产环境中,建议将”可观测性”当成一个完整闭环:**采集 -> 存储 -> 展示 -> 告警 -> 排错 -> 容量治理**。
|
||||||
|
|
||||||
## 19.3 Docker 日志驱动
|
## 扩展阅读:Docker 日志驱动
|
||||||
|
|
||||||
Docker 提供了多种日志驱动 (Log Driver),用于将容器标准输出的日志转发到不同后端。
|
Docker 提供了多种日志驱动 (Log Driver),用于将容器标准输出的日志转发到不同后端。
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ deploy_staging:
|
|||||||
1. **不可变基础设施**:一旦镜像构建完成,在各个环境(Dev、Staging、Prod)中都应该使用同一个镜像 tag(通常是 commit hash),而不是重新构建。
|
1. **不可变基础设施**:一旦镜像构建完成,在各个环境(Dev、Staging、Prod)中都应该使用同一个镜像 tag(通常是 commit hash),而不是重新构建。
|
||||||
2. **配置分离**:使用 ConfigMap 和 Secret 管理环境特定的配置,不要打包进镜像。
|
2. **配置分离**:使用 ConfigMap 和 Secret 管理环境特定的配置,不要打包进镜像。
|
||||||
3. **应对 Docker Hub 限额 (Rate Limits)**:
|
3. **应对 Docker Hub 限额 (Rate Limits)**:
|
||||||
- Docker Hub 对匿名拉取实施了严格的限制 (6小时内约100次)。若在 CI/CD 中频繁构建,极易触发 `toomanyrequests` 错误。
|
- Docker Hub 对匿名拉取实施了严格的限制 (6 小时内约 100 次)。若在 CI/CD 中频繁构建,极易触发 `toomanyrequests` 错误。
|
||||||
- **最佳策略**:
|
- **最佳策略**:
|
||||||
- 在流水线开头始终执行安全的身份认证 (使用 PAT,而非密码)。
|
- 在流水线开头始终执行安全的身份认证 (使用 PAT,而非密码)。
|
||||||
- 将常用的基础镜像缓存到自建的 Harbor/Nexus,使用 Pull-Through Cache。
|
- 将常用的基础镜像缓存到自建的 Harbor/Nexus,使用 Pull-Through Cache。
|
||||||
|
|||||||
877
21_case_devops/21.7_practical_examples.md
Normal file
877
21_case_devops/21.7_practical_examples.md
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
## 21.7 实战案例:Go/Rust/数据库/微服务
|
||||||
|
|
||||||
|
本节通过实际项目案例演示如何为不同类型的应用构建最优化的 Docker 镜像,以及如何使用 Docker Compose 构建完整的开发和生产环境。
|
||||||
|
|
||||||
|
### 21.7.1 Go 应用的最小化镜像构建
|
||||||
|
|
||||||
|
Go 语言因其编译为静态二进制和快速启动而特别适合容器化。以下展示如何构建极小的 Go 应用镜像。
|
||||||
|
|
||||||
|
#### 超小 Go Web 服务
|
||||||
|
|
||||||
|
**应用代码(main.go):**
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, `{"status":"healthy","version":"1.0.0"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func helloHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, `{"message":"Hello from %s","version":"1.0.0"}`, hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/health", healthHandler)
|
||||||
|
http.HandleFunc("/hello", helloHandler)
|
||||||
|
http.HandleFunc("/", helloHandler)
|
||||||
|
|
||||||
|
port := ":8080"
|
||||||
|
log.Printf("Server starting on %s", port)
|
||||||
|
|
||||||
|
if err := http.ListenAndServe(port, nil); err != nil {
|
||||||
|
log.Fatalf("Server failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**多阶段 Dockerfile:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Stage 1: 构建阶段
|
||||||
|
FROM golang:1.20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 安装构建依赖
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
# 复制模块文件(利用缓存)
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# 复制源代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建静态二进制
|
||||||
|
# -ldflags="-w -s" 去除调试符号减小体积
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-a -installsuffix cgo \
|
||||||
|
-ldflags="-w -s -X main.Version=1.0.0 -X main.BuildTime=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||||
|
-o app .
|
||||||
|
|
||||||
|
# Stage 2: 运行阶段(scratch 镜像)
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
# 复制 CA 证书(用于 HTTPS)
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|
||||||
|
# 复制时区数据(用于时间处理)
|
||||||
|
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
|
||||||
|
# 复制应用二进制
|
||||||
|
COPY --from=builder /build/app /app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 使用绝对路径作为 ENTRYPOINT
|
||||||
|
ENTRYPOINT ["/app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**构建和测试:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建镜像
|
||||||
|
docker build -t go-app:latest .
|
||||||
|
|
||||||
|
# 检查镜像大小
|
||||||
|
docker images go-app
|
||||||
|
|
||||||
|
# 运行容器
|
||||||
|
docker run -d -p 8080:8080 --name go-demo go-app:latest
|
||||||
|
|
||||||
|
# 测试应用
|
||||||
|
curl http://localhost:8080/health | jq .
|
||||||
|
|
||||||
|
# 进入容器验证
|
||||||
|
docker exec go-demo ls -la /
|
||||||
|
# 只包含 /app 和系统必要文件
|
||||||
|
|
||||||
|
# 镜像大小通常 < 10MB(相比 golang:1.20-alpine 的 ~1GB)
|
||||||
|
docker history go-app:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**go.mod 和 go.sum 示例:**
|
||||||
|
|
||||||
|
```
|
||||||
|
module github.com/example/go-app
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
// 如果需要依赖
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 带依赖的 Go 应用
|
||||||
|
|
||||||
|
**应用代码(使用 Gin 框架):**
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
router.GET("/health", func(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"status": "ok",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.GET("/api/users", func(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"users": []string{"alice", "bob"},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Fatal(router.Run(":8080"))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**优化的 Dockerfile:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM golang:1.20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -a -installsuffix cgo \
|
||||||
|
-ldflags="-w -s" \
|
||||||
|
-o app .
|
||||||
|
|
||||||
|
# 最终镜像
|
||||||
|
FROM alpine:3.17
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
|
WORKDIR /root/
|
||||||
|
|
||||||
|
COPY --from=builder /src/app .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["./app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.7.2 Rust 应用的最小化镜像构建
|
||||||
|
|
||||||
|
Rust 因其性能和安全性在系统级应用中备受青睐。
|
||||||
|
|
||||||
|
**应用代码(main.rs):**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use actix_web::{web, App, HttpServer, HttpResponse};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
println!("Starting server on 0.0.0.0:8080");
|
||||||
|
|
||||||
|
HttpServer::new(|| {
|
||||||
|
App::new()
|
||||||
|
.route("/health", web::get().to(health))
|
||||||
|
.route("/hello", web::get().to(hello))
|
||||||
|
})
|
||||||
|
.bind("0.0.0.0:8080")?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health() -> HttpResponse {
|
||||||
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"status": "healthy"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hello() -> HttpResponse {
|
||||||
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"message": "Hello from Rust"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cargo.toml:**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "rust-app"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rust-app"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "4.4"
|
||||||
|
tokio = { version = "1.35", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
**多阶段构建 Dockerfile:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Stage 1: 编译
|
||||||
|
FROM rust:1.75-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY Cargo.* ./
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# 构建优化的发布版本
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
# Stage 2: 运行镜像
|
||||||
|
FROM alpine:3.17
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
COPY --from=builder /src/target/release/rust-app /app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["/app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**构建和验证:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t rust-app:latest .
|
||||||
|
docker run -d -p 8080:8080 rust-app:latest
|
||||||
|
curl http://localhost:8080/health | jq .
|
||||||
|
|
||||||
|
# Rust 应用通常比 Go 更小:5-20MB(取决于依赖)
|
||||||
|
docker images rust-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.7.3 数据库容器化最佳实践
|
||||||
|
|
||||||
|
#### PostgreSQL 生产部署
|
||||||
|
|
||||||
|
**自定义 PostgreSQL 镜像:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM postgres:16-alpine
|
||||||
|
|
||||||
|
# 安装额外工具
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
postgresql-contrib \
|
||||||
|
pg-stat-monitor \
|
||||||
|
curl
|
||||||
|
|
||||||
|
# 复制初始化脚本
|
||||||
|
COPY init-db.sql /docker-entrypoint-initdb.d/
|
||||||
|
COPY health-check.sh /
|
||||||
|
|
||||||
|
RUN chmod +x /health-check.sh
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=40s --retries=3 \
|
||||||
|
CMD /health-check.sh
|
||||||
|
|
||||||
|
EXPOSE 5432
|
||||||
|
```
|
||||||
|
|
||||||
|
**初始化脚本(init-db.sql):**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 创建自定义用户
|
||||||
|
CREATE USER appuser WITH PASSWORD 'secure_password';
|
||||||
|
|
||||||
|
-- 创建数据库
|
||||||
|
CREATE DATABASE myappdb OWNER appuser;
|
||||||
|
|
||||||
|
-- 创建扩展
|
||||||
|
\c myappdb
|
||||||
|
|
||||||
|
CREATE EXTENSION IF NOT EXISTS uuid-ossp;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS hstore;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
|
||||||
|
-- 创建表
|
||||||
|
CREATE TABLE users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
username VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
email VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 创建索引
|
||||||
|
CREATE INDEX idx_users_username ON users (username);
|
||||||
|
CREATE INDEX idx_users_email ON users (email);
|
||||||
|
|
||||||
|
-- 授予权限
|
||||||
|
GRANT CONNECT ON DATABASE myappdb TO appuser;
|
||||||
|
GRANT USAGE ON SCHEMA public TO appuser;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO appuser;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO appuser;
|
||||||
|
```
|
||||||
|
|
||||||
|
**健康检查脚本(health-check.sh):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PGPASSWORD=$POSTGRES_PASSWORD pg_isready \
|
||||||
|
-h localhost \
|
||||||
|
-U $POSTGRES_USER \
|
||||||
|
-d $POSTGRES_DB \
|
||||||
|
-p 5432 > /dev/null 2>&1
|
||||||
|
|
||||||
|
exit $?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker Compose 配置:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.postgres
|
||||||
|
container_name: postgres-db
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: myappdb
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres_password
|
||||||
|
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./backups:/backups
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
# 备份服务
|
||||||
|
backup:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
environment:
|
||||||
|
PGPASSWORD: postgres_password
|
||||||
|
volumes:
|
||||||
|
- ./backups:/backups
|
||||||
|
command: |
|
||||||
|
sh -c 'while true; do
|
||||||
|
pg_dump -h postgres -U postgres -d myappdb > /backups/backup_$$(date +%Y%m%d_%H%M%S).sql
|
||||||
|
echo "Backup completed at $$(date)"
|
||||||
|
sleep 86400
|
||||||
|
done'
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
**性能优化配置:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: myappdb
|
||||||
|
command:
|
||||||
|
- "postgres"
|
||||||
|
- "-c"
|
||||||
|
- "max_connections=200"
|
||||||
|
- "-c"
|
||||||
|
- "shared_buffers=256MB"
|
||||||
|
- "-c"
|
||||||
|
- "effective_cache_size=1GB"
|
||||||
|
- "-c"
|
||||||
|
- "maintenance_work_mem=64MB"
|
||||||
|
- "-c"
|
||||||
|
- "checkpoint_completion_target=0.9"
|
||||||
|
- "-c"
|
||||||
|
- "wal_buffers=16MB"
|
||||||
|
- "-c"
|
||||||
|
- "default_statistics_target=100"
|
||||||
|
- "-c"
|
||||||
|
- "random_page_cost=1.1"
|
||||||
|
- "-c"
|
||||||
|
- "effective_io_concurrency=200"
|
||||||
|
- "-c"
|
||||||
|
- "work_mem=1310kB"
|
||||||
|
- "-c"
|
||||||
|
- "min_wal_size=1GB"
|
||||||
|
- "-c"
|
||||||
|
- "max_wal_size=4GB"
|
||||||
|
- "-c"
|
||||||
|
- "max_worker_processes=4"
|
||||||
|
- "-c"
|
||||||
|
- "max_parallel_workers_per_gather=2"
|
||||||
|
- "-c"
|
||||||
|
- "max_parallel_workers=4"
|
||||||
|
- "-c"
|
||||||
|
- "max_parallel_maintenance_workers=2"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
#### MySQL/MariaDB 部署
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM mariadb:11
|
||||||
|
|
||||||
|
# 复制自定义配置
|
||||||
|
COPY my.cnf /etc/mysql/conf.d/custom.cnf
|
||||||
|
|
||||||
|
# 初始化脚本
|
||||||
|
COPY init.sql /docker-entrypoint-initdb.d/
|
||||||
|
|
||||||
|
EXPOSE 3306
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
|
||||||
|
CMD mariadb-admin ping -h localhost || exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**自定义 my.cnf:**
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[mysqld]
|
||||||
|
# 性能优化
|
||||||
|
max_connections = 200
|
||||||
|
default_storage_engine = InnoDB
|
||||||
|
innodb_buffer_pool_size = 1GB
|
||||||
|
innodb_log_file_size = 256MB
|
||||||
|
query_cache_type = 0
|
||||||
|
query_cache_size = 0
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
log_error = /var/log/mysql/error.log
|
||||||
|
slow_query_log = 1
|
||||||
|
slow_query_log_file = /var/log/mysql/slow.log
|
||||||
|
long_query_time = 2
|
||||||
|
|
||||||
|
# 复制配置
|
||||||
|
server_id = 1
|
||||||
|
log_bin = mysql-bin
|
||||||
|
binlog_format = ROW
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Redis 缓存部署
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM redis:7-alpine
|
||||||
|
|
||||||
|
# 复制 Redis 配置
|
||||||
|
COPY redis.conf /usr/local/etc/redis/redis.conf
|
||||||
|
|
||||||
|
# 使用配置文件启动
|
||||||
|
CMD ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||||
|
|
||||||
|
EXPOSE 6379
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=5s --timeout=3s --retries=5 \
|
||||||
|
CMD redis-cli ping || exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**redis.conf 配置:**
|
||||||
|
|
||||||
|
```conf
|
||||||
|
# 绑定地址
|
||||||
|
bind 0.0.0.0
|
||||||
|
|
||||||
|
# 端口
|
||||||
|
port 6379
|
||||||
|
|
||||||
|
# 密码保护
|
||||||
|
requirepass your_secure_password
|
||||||
|
|
||||||
|
# 内存管理
|
||||||
|
maxmemory 512mb
|
||||||
|
maxmemory-policy allkeys-lru
|
||||||
|
|
||||||
|
# 持久化
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
save 60 10000
|
||||||
|
|
||||||
|
# AOF 持久化
|
||||||
|
appendonly yes
|
||||||
|
appendfsync everysec
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
loglevel notice
|
||||||
|
logfile ""
|
||||||
|
|
||||||
|
# 客户端输出缓冲限制
|
||||||
|
client-output-buffer-limit normal 0 0 0
|
||||||
|
client-output-buffer-limit slave 256mb 64mb 60
|
||||||
|
client-output-buffer-limit pubsub 32mb 8mb 60
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.7.4 微服务架构的 Docker Compose 编排
|
||||||
|
|
||||||
|
**三层微服务架构示例:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# 前端服务
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: frontend
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
REACT_APP_API_URL: http://localhost:8000
|
||||||
|
NODE_ENV: production
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
networks:
|
||||||
|
- frontend-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# API 服务
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: ./api
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: api
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://appuser:password@postgres:5432/myappdb
|
||||||
|
REDIS_URL: redis://redis:6379
|
||||||
|
LOG_LEVEL: info
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- frontend-network
|
||||||
|
- backend-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1'
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
|
# PostgreSQL 数据库
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: myappdb
|
||||||
|
POSTGRES_USER: appuser
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
|
networks:
|
||||||
|
- backend-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U appuser -d myappdb"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Redis 缓存
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: redis
|
||||||
|
command: redis-server --appendonly yes --requirepass redispass
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- backend-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Nginx 反向代理
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: nginx
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||||
|
- ./ssl:/etc/nginx/ssl:ro
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
- api
|
||||||
|
networks:
|
||||||
|
- frontend-network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend-network:
|
||||||
|
driver: bridge
|
||||||
|
backend-network:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
**nginx.conf 配置:**
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream frontend {
|
||||||
|
server frontend:3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream api {
|
||||||
|
server api:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
|
# 健康检查端点
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "OK\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 前端应用
|
||||||
|
location / {
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 接口
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://api/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
# WebSocket 支持
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* ^.+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.7.5 使用 VS Code Dev Containers
|
||||||
|
|
||||||
|
Dev Containers 让整个开发环境容器化,提升团队一致性。
|
||||||
|
|
||||||
|
**.devcontainer/devcontainer.json:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Python Dev Environment",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/python:3.11",
|
||||||
|
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
||||||
|
"ghcr.io/devcontainers/features/git:1": {}
|
||||||
|
},
|
||||||
|
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"ms-python.python",
|
||||||
|
"ms-python.vscode-pylance",
|
||||||
|
"ms-python.pylint",
|
||||||
|
"charliermarsh.ruff",
|
||||||
|
"ms-vscode-remote.remote-containers"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"python.linting.pylintEnabled": true,
|
||||||
|
"python.formatting.provider": "black",
|
||||||
|
"[python]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "ms-python.python"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"postCreateCommand": "pip install -r requirements.txt && pip install pytest black pylint",
|
||||||
|
|
||||||
|
"forwardPorts": [8000, 5432, 6379],
|
||||||
|
"portsAttributes": {
|
||||||
|
"8000": {
|
||||||
|
"label": "Application",
|
||||||
|
"onAutoForward": "notify"
|
||||||
|
},
|
||||||
|
"5432": {
|
||||||
|
"label": "PostgreSQL",
|
||||||
|
"onAutoForward": "ignore"
|
||||||
|
},
|
||||||
|
"6379": {
|
||||||
|
"label": "Redis",
|
||||||
|
"onAutoForward": "ignore"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"mounts": [
|
||||||
|
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,readonly"
|
||||||
|
],
|
||||||
|
|
||||||
|
"remoteUser": "vscode"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**.devcontainer/Dockerfile:**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM mcr.microsoft.com/devcontainers/python:3.11
|
||||||
|
|
||||||
|
# 安装额外工具
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
postgresql-client \
|
||||||
|
redis-tools \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 创建虚拟环境
|
||||||
|
RUN python -m venv /opt/venv
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
**docker-compose 用于 Dev Containers:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .devcontainer/docker-compose.yml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://dev:dev@postgres:5432/myapp
|
||||||
|
REDIS_URL: redis://redis:6379
|
||||||
|
volumes:
|
||||||
|
- ..:/workspace:cached
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: dev
|
||||||
|
POSTGRES_PASSWORD: dev
|
||||||
|
POSTGRES_DB: myapp
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
```
|
||||||
@@ -8,4 +8,5 @@
|
|||||||
* [Drone Demo](21.4_drone_demo.md)
|
* [Drone Demo](21.4_drone_demo.md)
|
||||||
* [在 IDE 中使用 Docker](21.5_ide.md)
|
* [在 IDE 中使用 Docker](21.5_ide.md)
|
||||||
* [VS Code](21.6_vsCode.md)
|
* [VS Code](21.6_vsCode.md)
|
||||||
|
* [实战例子](21.7_practical_examples.md)
|
||||||
* [本章小结](summary.md)
|
* [本章小结](summary.md)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 如何贡献
|
## 如何贡献
|
||||||
|
|
||||||
领取或创建新的 [Issue](https://github.com/yeasy/docker_practice/issues),如 [issue 235](https://github.com/yeasy/docker_practice/issues/235),添加自己为 `Assignee`。
|
领取或创建新的 [Issue](https://github.com/yeasy/docker_practice/issues),如 [issue 235](https://github.com/yeasy/docker_practice/issues/235),添加自己为 `Assignee`。
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ npx honkit serve
|
|||||||
<img width="200" src="https://github.com/yeasy/docker_practice/raw/master/_images/donate.jpeg">
|
<img width="200" src="https://github.com/yeasy/docker_practice/raw/master/_images/donate.jpeg">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center"><strong>欢迎鼓励项目一杯 coffee~</strong></p>
|
<p align=“center”><strong>欢迎鼓励项目一杯 coffee~</strong></p>
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@
|
|||||||
* [9.4 容器互联](09_network/9.4_container_linking.md)
|
* [9.4 容器互联](09_network/9.4_container_linking.md)
|
||||||
* [9.5 外部访问容器](09_network/9.5_port_mapping.md)
|
* [9.5 外部访问容器](09_network/9.5_port_mapping.md)
|
||||||
* [9.6 网络隔离](09_network/9.6_network_isolation.md)
|
* [9.6 网络隔离](09_network/9.6_network_isolation.md)
|
||||||
|
* [9.7 容器网络高级特性](09_network/9.7_advanced_networking.md)
|
||||||
* [本章小结](09_network/summary.md)
|
* [本章小结](09_network/summary.md)
|
||||||
* [第十章 Docker Buildx](10_buildx/README.md)
|
* [第十章 Docker Buildx](10_buildx/README.md)
|
||||||
* [10.1 BuildKit](10_buildx/10.1_buildkit.md)
|
* [10.1 BuildKit](10_buildx/10.1_buildkit.md)
|
||||||
@@ -163,10 +164,12 @@
|
|||||||
* [18.3 服务端防护](18_security/18.3_daemon_sec.md)
|
* [18.3 服务端防护](18_security/18.3_daemon_sec.md)
|
||||||
* [18.4 内核能力机制](18_security/18.4_kernel_capability.md)
|
* [18.4 内核能力机制](18_security/18.4_kernel_capability.md)
|
||||||
* [18.5 其它安全特性](18_security/18.5_other_feature.md)
|
* [18.5 其它安全特性](18_security/18.5_other_feature.md)
|
||||||
|
* [18.6 容器镜像安全扫描与供应链安全](18_security/18.6_image_security.md)
|
||||||
* [本章小结](18_security/summary.md)
|
* [本章小结](18_security/summary.md)
|
||||||
* [第十九章 容器监控与日志](19_observability/README.md)
|
* [第十九章 容器监控与日志](19_observability/README.md)
|
||||||
* [19.1 Prometheus](19_observability/19.1_prometheus.md)
|
* [19.1 Prometheus](19_observability/19.1_prometheus.md)
|
||||||
* [19.2 ELK 套件](19_observability/19.2_elk.md)
|
* [19.2 ELK 套件](19_observability/19.2_elk.md)
|
||||||
|
* [19.3 容器性能优化与故障诊断](19_observability/19.3_performance_optimization.md)
|
||||||
* [本章小结](19_observability/summary.md)
|
* [本章小结](19_observability/summary.md)
|
||||||
* [第二十章 实战案例 - 操作系统](20_cases_os/README.md)
|
* [第二十章 实战案例 - 操作系统](20_cases_os/README.md)
|
||||||
* [20.1 Busybox](20_cases_os/20.1_busybox.md)
|
* [20.1 Busybox](20_cases_os/20.1_busybox.md)
|
||||||
@@ -181,6 +184,7 @@
|
|||||||
* [21.4 Drone Demo](21_case_devops/21.4_drone_demo.md)
|
* [21.4 Drone Demo](21_case_devops/21.4_drone_demo.md)
|
||||||
* [21.5 在 IDE 中使用 Docker](21_case_devops/21.5_ide.md)
|
* [21.5 在 IDE 中使用 Docker](21_case_devops/21.5_ide.md)
|
||||||
* [21.6 VS Code](21_case_devops/21.6_vsCode.md)
|
* [21.6 VS Code](21_case_devops/21.6_vsCode.md)
|
||||||
|
* [21.7 实战案例:Go/Rust/数据库/微服务](21_case_devops/21.7_practical_examples.md)
|
||||||
* [本章小结](21_case_devops/summary.md)
|
* [本章小结](21_case_devops/summary.md)
|
||||||
|
|
||||||
## 附录
|
## 附录
|
||||||
@@ -205,3 +209,4 @@
|
|||||||
* [附录五:如何调试 Docker](appendix/debug.md)
|
* [附录五:如何调试 Docker](appendix/debug.md)
|
||||||
* [附录六:资源链接](appendix/resources.md)
|
* [附录六:资源链接](appendix/resources.md)
|
||||||
* [附录七:术语表](appendix/glossary.md)
|
* [附录七:术语表](appendix/glossary.md)
|
||||||
|
* [附录八:Docker 学习路线图与知识体系](appendix/learning_roadmap.md)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## 1. 安全与凭据处理
|
## 1. 安全与凭据处理
|
||||||
|
|
||||||
任何示例文档中都**严禁出现将凭据以明文形式直接传递给命令参数**的做法。
|
任何示例文档中都 **严禁出现将凭据以明文形式直接传递给命令参数** 的做法。
|
||||||
|
|
||||||
* **错误示例**:`docker login -u myuser -p mysecretpassword` (这会导致密码泄露到历史记录及进程列表中)
|
* **错误示例**:`docker login -u myuser -p mysecretpassword` (这会导致密码泄露到历史记录及进程列表中)
|
||||||
* **正确示例(交互式)**:推荐读者直接使用 `docker login` (优先使用官方 Device Code Flow)。
|
* **正确示例(交互式)**:推荐读者直接使用 `docker login` (优先使用官方 Device Code Flow)。
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* **明确声明前置条件**:例如,“此功能需要开启 containerd image store” 或 “需要额外配置 `--push`”。
|
* **明确声明前置条件**:例如,“此功能需要开启 containerd image store” 或 “需要额外配置 `--push`”。
|
||||||
* **描述失败现象**:明确告诉读者,“如果你不这么配置,命令仍会提示成功,但产物将不可见/被丢弃”。
|
* **描述失败现象**:明确告诉读者,“如果你不这么配置,命令仍会提示成功,但产物将不可见/被丢弃”。
|
||||||
|
|
||||||
## 3. 统一使用 "docker compose" 命令
|
## 3. 统一使用 “docker compose” 命令
|
||||||
|
|
||||||
早期 Python 编写的 `docker-compose` (V1) 已停止支持。
|
早期 Python 编写的 `docker-compose` (V1) 已停止支持。
|
||||||
|
|
||||||
|
|||||||
873
appendix/learning_roadmap.md
Normal file
873
appendix/learning_roadmap.md
Normal file
@@ -0,0 +1,873 @@
|
|||||||
|
## 附录八:Docker 学习路线图与知识体系
|
||||||
|
|
||||||
|
本附录为学习者提供清晰的学习路线、知识点依赖关系、认证指南和常见面试题,帮助快速成长为 Docker 和 DevOps 专家。
|
||||||
|
|
||||||
|
### 学习阶段划分
|
||||||
|
|
||||||
|
Docker 学习可分为四个递进阶段,每个阶段都有明确的学习目标和时间投入。
|
||||||
|
|
||||||
|
#### 第一阶段:基础入门(0-2 周)
|
||||||
|
|
||||||
|
**学习目标:**
|
||||||
|
- 理解容器化的基本概念
|
||||||
|
- 能够运行、管理基本的容器
|
||||||
|
- 了解镜像和仓库的基本操作
|
||||||
|
|
||||||
|
**核心内容:**
|
||||||
|
```
|
||||||
|
Docker 简介
|
||||||
|
├── 为什么需要 Docker
|
||||||
|
├── 容器 vs 虚拟机 vs 云计算
|
||||||
|
└── Docker 的三大核心概念
|
||||||
|
├── 镜像(Image)
|
||||||
|
├── 容器(Container)
|
||||||
|
└── 仓库(Repository)
|
||||||
|
|
||||||
|
基础命令
|
||||||
|
├── docker run / create / start / stop / rm
|
||||||
|
├── docker ps / logs / exec / inspect
|
||||||
|
├── docker pull / push / tag
|
||||||
|
└── docker build -t
|
||||||
|
|
||||||
|
Docker 安装配置
|
||||||
|
├── Linux 平台安装
|
||||||
|
├── macOS 和 Windows 安装
|
||||||
|
├── 镜像加速器配置
|
||||||
|
└── 权限和用户配置
|
||||||
|
```
|
||||||
|
|
||||||
|
**学习资源:**
|
||||||
|
- 官方教程:https://docs.docker.com/get-started/
|
||||||
|
- 本书第 1-3 章:入门篇基础概念
|
||||||
|
- Docker CLI 参考:https://docs.docker.com/engine/reference/commandline/
|
||||||
|
|
||||||
|
**时间投入:**
|
||||||
|
- 理论学习:3-4 小时
|
||||||
|
- 实操练习:8-10 小时
|
||||||
|
- 总计:2 周
|
||||||
|
|
||||||
|
**验证学习成果:**
|
||||||
|
```bash
|
||||||
|
# 完成以下任务说明基础入门完成
|
||||||
|
1. 运行官方 nginx 镜像,访问 http://localhost
|
||||||
|
2. 使用 docker exec 进入容器修改首页
|
||||||
|
3. 提交修改为新镜像
|
||||||
|
4. 推送镜像到 Docker Hub(需创建账户)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第二阶段:核心开发(2-6 周)
|
||||||
|
|
||||||
|
**学习目标:**
|
||||||
|
- 掌握 Dockerfile 编写
|
||||||
|
- 能够构建自己的应用镜像
|
||||||
|
- 理解数据管理和网络配置
|
||||||
|
- 熟悉 Docker Compose 编排
|
||||||
|
|
||||||
|
**核心内容:**
|
||||||
|
```
|
||||||
|
Dockerfile 指令详解
|
||||||
|
├── FROM / RUN / COPY / ADD
|
||||||
|
├── WORKDIR / ENV / ARG
|
||||||
|
├── EXPOSE / CMD / ENTRYPOINT
|
||||||
|
├── VOLUME / USER / HEALTHCHECK
|
||||||
|
└── 最佳实践和性能优化
|
||||||
|
├── 分层缓存机制
|
||||||
|
├── 减少镜像体积
|
||||||
|
├── 多阶段构建
|
||||||
|
└── 安全最佳实践
|
||||||
|
|
||||||
|
容器数据管理
|
||||||
|
├── 数据卷(Volume)
|
||||||
|
│ ├── 命名卷
|
||||||
|
│ ├── 匿名卷
|
||||||
|
│ └── 卷挂载最佳实践
|
||||||
|
├── 绑定挂载(Bind Mount)
|
||||||
|
│ ├── 宿主机路径映射
|
||||||
|
│ └── 权限和隔离
|
||||||
|
└── tmpfs 挂载
|
||||||
|
└── 临时文件系统
|
||||||
|
|
||||||
|
容器网络
|
||||||
|
├── 网络类型
|
||||||
|
│ ├── bridge(默认)
|
||||||
|
│ ├── host
|
||||||
|
│ ├── overlay
|
||||||
|
│ └── macvlan
|
||||||
|
├── 端口映射
|
||||||
|
├── 容器互联
|
||||||
|
├── DNS 配置
|
||||||
|
└── 自定义网络
|
||||||
|
|
||||||
|
Docker Compose
|
||||||
|
├── compose.yml/docker-compose.yml 编写
|
||||||
|
├── services 定义
|
||||||
|
├── volumes 配置
|
||||||
|
├── networks 配置
|
||||||
|
├── 依赖关系
|
||||||
|
├── 环境变量
|
||||||
|
└── 命令操作
|
||||||
|
├── up / down / ps / logs
|
||||||
|
├── exec / run
|
||||||
|
└── build / push
|
||||||
|
```
|
||||||
|
|
||||||
|
**学习资源:**
|
||||||
|
- 本书第 4-11 章:进阶篇
|
||||||
|
- Docker 官方最佳实践:https://docs.docker.com/develop/dev-best-practices/
|
||||||
|
- Dockerfile 参考:https://docs.docker.com/engine/reference/builder/
|
||||||
|
|
||||||
|
**时间投入:**
|
||||||
|
- 理论学习:8-10 小时
|
||||||
|
- 实操练习:30-40 小时(多个实战项目)
|
||||||
|
- 总计:4-6 周
|
||||||
|
|
||||||
|
**项目实战:**
|
||||||
|
```
|
||||||
|
项目 1: Python Web 应用(Flask/Django)
|
||||||
|
- 编写多阶段 Dockerfile
|
||||||
|
- 使用 Compose 配置数据库
|
||||||
|
- 实现热重载开发环境
|
||||||
|
|
||||||
|
项目 2: Node.js 微服务
|
||||||
|
- 优化镜像大小
|
||||||
|
- 配置 Compose 多个服务
|
||||||
|
- 设置网络和环保境变量
|
||||||
|
|
||||||
|
项目 3: 数据库容器化
|
||||||
|
- PostgreSQL/MySQL 配置
|
||||||
|
- 数据持久化
|
||||||
|
- 备份恢复策略
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第三阶段:生产优化(6-12 周)
|
||||||
|
|
||||||
|
**学习目标:**
|
||||||
|
- 掌握容器安全最佳实践
|
||||||
|
- 理解性能监控和优化
|
||||||
|
- 学会容器编排(Kubernetes 基础)
|
||||||
|
- 熟悉 CI/CD 集成
|
||||||
|
|
||||||
|
**核心内容:**
|
||||||
|
```
|
||||||
|
容器安全
|
||||||
|
├── 镜像安全
|
||||||
|
│ ├── 漏洞扫描(Trivy/Grype/Snyk)
|
||||||
|
│ ├── 镜像签名和验证(Cosign)
|
||||||
|
│ ├── SBOM 生成和管理
|
||||||
|
│ └── 供应链安全
|
||||||
|
├── 运行时安全
|
||||||
|
│ ├── 用户和权限
|
||||||
|
│ ├── Linux 能力机制
|
||||||
|
│ ├── AppArmor 和 SELinux
|
||||||
|
│ ├── Rootless 容器
|
||||||
|
│ └── 安全的 Docker socket 访问
|
||||||
|
└── 宿主机安全
|
||||||
|
├── API 访问控制
|
||||||
|
├── TLS 认证
|
||||||
|
└── 审计日志
|
||||||
|
|
||||||
|
性能监控和优化
|
||||||
|
├── 监控指标体系
|
||||||
|
│ ├── CPU / 内存 / 网络 / I/O
|
||||||
|
│ └── 应用级指标
|
||||||
|
├── 监控工具
|
||||||
|
│ ├── docker stats
|
||||||
|
│ ├── cAdvisor
|
||||||
|
│ ├── Prometheus
|
||||||
|
│ └── Grafana
|
||||||
|
├── 性能优化
|
||||||
|
│ ├── 镜像大小优化
|
||||||
|
│ ├── 内存和 CPU 限制
|
||||||
|
│ ├── OOM 诊断和处理
|
||||||
|
│ └── 网络性能优化
|
||||||
|
└── 日志管理
|
||||||
|
├── 日志驱动配置
|
||||||
|
├── ELK Stack
|
||||||
|
└── 日志聚合
|
||||||
|
|
||||||
|
容器编排基础
|
||||||
|
├── Kubernetes 核心概念
|
||||||
|
│ ├── Pod / Deployment / Service
|
||||||
|
│ ├── ConfigMap / Secret
|
||||||
|
│ └── 健康检查和自动恢复
|
||||||
|
├── 容器执行环境
|
||||||
|
│ ├── containerd
|
||||||
|
│ ├── CRI-O
|
||||||
|
│ └── Docker
|
||||||
|
├── 网络插件
|
||||||
|
│ ├── CNI 标准
|
||||||
|
│ ├── Calico / Flannel / Cilium
|
||||||
|
│ └── 网络策略
|
||||||
|
└── 存储和有状态应用
|
||||||
|
├── PV / PVC
|
||||||
|
├── StorageClass
|
||||||
|
└── StatefulSet
|
||||||
|
|
||||||
|
CI/CD 集成
|
||||||
|
├── GitHub Actions
|
||||||
|
│ ├── 镜像构建和推送
|
||||||
|
│ ├── 安全扫描
|
||||||
|
│ └── 自动化测试
|
||||||
|
├── GitLab CI
|
||||||
|
├── Jenkins Docker 集成
|
||||||
|
└── Drone
|
||||||
|
|
||||||
|
生态工具
|
||||||
|
├── Buildx(多架构构建)
|
||||||
|
├── Skopeo(镜像管理)
|
||||||
|
├── Podman(替代方案)
|
||||||
|
├── Buildah(镜像构建)
|
||||||
|
└── Kollabot
|
||||||
|
```
|
||||||
|
|
||||||
|
**学习资源:**
|
||||||
|
- 本书第 12-21 章:深入篇和实战篇
|
||||||
|
- Kubernetes 官方文档:https://kubernetes.io/docs/
|
||||||
|
- CNCF 学习路线:https://landscape.cncf.io/
|
||||||
|
|
||||||
|
**时间投入:**
|
||||||
|
- 理论学习:15-20 小时
|
||||||
|
- 实操练习:60-80 小时(多个生产级项目)
|
||||||
|
- 总计:6-12 周
|
||||||
|
|
||||||
|
**项目实战:**
|
||||||
|
```
|
||||||
|
项目 1: 安全镜像构建流程
|
||||||
|
- 集成 Trivy 扫描
|
||||||
|
- 镜像签名和验证
|
||||||
|
- 生成 SBOM 文档
|
||||||
|
|
||||||
|
项目 2: 完整监控栈
|
||||||
|
- 搭建 Prometheus + Grafana
|
||||||
|
- 配置告警规则
|
||||||
|
- 性能数据采集和分析
|
||||||
|
|
||||||
|
项目 3: CI/CD 流程
|
||||||
|
- GitHub Actions 或 GitLab CI 配置
|
||||||
|
- 自动化镜像构建
|
||||||
|
- 安全扫描和合规检查
|
||||||
|
- 自动化部署到 Kubernetes
|
||||||
|
|
||||||
|
项目 4: Kubernetes 集群部署
|
||||||
|
- 本地 K3s/Kind 集群
|
||||||
|
- 部署有状态应用
|
||||||
|
- 配置持久化存储
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第四阶段:专家深造(12+ 周)
|
||||||
|
|
||||||
|
**学习目标:**
|
||||||
|
- 掌握 Kubernetes 高级特性
|
||||||
|
- 理解容器运行时底层实现
|
||||||
|
- 能够设计和优化大规模容器平台
|
||||||
|
- 贡献开源社区
|
||||||
|
|
||||||
|
**核心内容:**
|
||||||
|
```
|
||||||
|
Kubernetes 高级特性
|
||||||
|
├── 集群管理
|
||||||
|
│ ├── 节点管理和驱逐
|
||||||
|
│ ├── 集群自动扩缩容
|
||||||
|
│ └── 节点亲和性和污点容忍
|
||||||
|
├── 存储编排
|
||||||
|
│ ├── 动态存储配置
|
||||||
|
│ ├── 有状态应用管理(StatefulSet)
|
||||||
|
│ └── 备份和灾难恢复
|
||||||
|
├── 服务网格(Service Mesh)
|
||||||
|
│ ├── Istio / Linkerd / Cilium
|
||||||
|
│ ├── 流量管理
|
||||||
|
│ └── 可观测性增强
|
||||||
|
├── 安全和多租户
|
||||||
|
│ ├── RBAC(角色访问控制)
|
||||||
|
│ ├── Network Policy 深入
|
||||||
|
│ ├── Pod Security Policy
|
||||||
|
│ └── 准入控制器(Admission Controller)
|
||||||
|
└── 性能和扩展性
|
||||||
|
├── 大规模集群优化
|
||||||
|
├── 自定义 Operator
|
||||||
|
└── 集群联邦
|
||||||
|
|
||||||
|
容器运行时底层
|
||||||
|
├── Linux 内核机制
|
||||||
|
│ ├── Namespace 详解
|
||||||
|
│ ├── Cgroup v1 和 v2
|
||||||
|
│ ├── OverlayFS 和 UnionFS
|
||||||
|
│ └── SELinux 和 AppArmor
|
||||||
|
├── 容器运行时
|
||||||
|
│ ├── containerd 源码阅读
|
||||||
|
│ ├── runc 实现
|
||||||
|
│ ├── gVisor 和 Kata
|
||||||
|
│ └── Firecracker
|
||||||
|
└── OCI 标准
|
||||||
|
├── Image Spec
|
||||||
|
└── Runtime Spec
|
||||||
|
|
||||||
|
DevOps 工程化
|
||||||
|
├── 大规模集群管理
|
||||||
|
│ ├── Helm / Kustomize
|
||||||
|
│ ├── GitOps(Flux / ArgoCD)
|
||||||
|
│ └── 配置管理
|
||||||
|
├── 灾难恢复和高可用
|
||||||
|
│ ├── 多集群部署
|
||||||
|
│ ├── 故障转移
|
||||||
|
│ └── 备份策略
|
||||||
|
├── 成本优化
|
||||||
|
│ ├── 资源申请和限制
|
||||||
|
│ ├── 自动扩缩容
|
||||||
|
│ └── 成本监控
|
||||||
|
└── 团队协作
|
||||||
|
├── GitFlow 工作流
|
||||||
|
├── 代码审查
|
||||||
|
└── 文档和最佳实践传播
|
||||||
|
```
|
||||||
|
|
||||||
|
**贡献机会:**
|
||||||
|
- Kubernetes(https://github.com/kubernetes/kubernetes)
|
||||||
|
- Cilium(https://github.com/cilium/cilium)
|
||||||
|
- Prometheus(https://github.com/prometheus/prometheus)
|
||||||
|
- Docker/Moby(https://github.com/moby/moby)
|
||||||
|
|
||||||
|
### 知识点依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
基础概念 (Week 0-2)
|
||||||
|
├── 容器 vs 虚拟机
|
||||||
|
├── Docker 三大概念
|
||||||
|
└── 基础命令
|
||||||
|
↓
|
||||||
|
Dockerfile 和镜像构建 (Week 2-4)
|
||||||
|
├── Dockerfile 指令
|
||||||
|
├── 多阶段构建
|
||||||
|
└── 镜像优化
|
||||||
|
↓ ↓ ↓
|
||||||
|
数据管理 ← 网络配置 ← Docker Compose (Week 4-6)
|
||||||
|
├── Volume ├── Bridge ├── YAML 编写
|
||||||
|
├── Bind Mount├── Overlay ├── 多容器编排
|
||||||
|
└── tmpfs └── 自定义网络└── 开发工作流
|
||||||
|
↓ ↓ ↓
|
||||||
|
└─────────────────────────┘
|
||||||
|
实战项目开发 (Week 6-10)
|
||||||
|
├── Web 应用容器化
|
||||||
|
├── 数据库容器化
|
||||||
|
├── 微服务架构
|
||||||
|
└── 本地开发环境
|
||||||
|
↓
|
||||||
|
容器安全 ← 性能优化 ← 监控和日志 (Week 10-14)
|
||||||
|
├── 镜像扫描 ├── 大小优化 ├── Prometheus
|
||||||
|
├── 漏洞管理 ├── 内存优化 ├── Grafana
|
||||||
|
├── 镜像签名 ├── CPU 优化 └── ELK Stack
|
||||||
|
└── SBOM └── 诊断工具
|
||||||
|
↓ ↓ ↓
|
||||||
|
└─────────────────────┘
|
||||||
|
安全生产环境 (Week 14-18)
|
||||||
|
├── CI/CD 流程
|
||||||
|
├── 镜像仓库
|
||||||
|
├── 日志集中
|
||||||
|
└── 告警系统
|
||||||
|
↓
|
||||||
|
Kubernetes 基础 (Week 18-24)
|
||||||
|
├── Pod / Service / Deployment
|
||||||
|
├── 资源管理
|
||||||
|
├── 存储管理
|
||||||
|
└── 网络策略
|
||||||
|
↓
|
||||||
|
Kubernetes 进阶 (Week 24-36)
|
||||||
|
├── StatefulSet / DaemonSet
|
||||||
|
├── Operator 开发
|
||||||
|
├── 集群管理
|
||||||
|
└── 服务网格
|
||||||
|
↓
|
||||||
|
企业级平台设计 (Week 36+)
|
||||||
|
├── 多集群管理
|
||||||
|
├── GitOps 工作流
|
||||||
|
├── 成本优化
|
||||||
|
└── 开源贡献
|
||||||
|
```
|
||||||
|
|
||||||
|
### 推荐学习资源
|
||||||
|
|
||||||
|
#### 官方文档
|
||||||
|
|
||||||
|
| 资源 | URL | 推荐程度 |
|
||||||
|
|------|-----|--------|
|
||||||
|
| Docker 官方文档 | https://docs.docker.com | ⭐⭐⭐⭐⭐ |
|
||||||
|
| Docker Hub | https://hub.docker.com | ⭐⭐⭐⭐⭐ |
|
||||||
|
| Kubernetes 官方 | https://kubernetes.io/docs | ⭐⭐⭐⭐⭐ |
|
||||||
|
| CNCF 景观 | https://landscape.cncf.io | ⭐⭐⭐⭐ |
|
||||||
|
|
||||||
|
#### 在线课程
|
||||||
|
|
||||||
|
- **Udemy**:Docker 和 Kubernetes 完整课程(70-100 小时)
|
||||||
|
- **Linux Academy**:Linux 和容器管理
|
||||||
|
- **A Cloud Guru**:AWS/Azure 容器服务
|
||||||
|
- **Pluralsight**:Docker 和容器生态系统
|
||||||
|
|
||||||
|
#### 书籍推荐
|
||||||
|
|
||||||
|
- 《Docker 深入浅出》- 本书的原版
|
||||||
|
- 《Kubernetes 权威指南》- 深入 Kubernetes 的必读书
|
||||||
|
- 《容器技术核心技术与应用》- 理解底层实现
|
||||||
|
- 《SRE Google 运维之道》- 生产环保最佳实践
|
||||||
|
|
||||||
|
#### 博客和社区
|
||||||
|
|
||||||
|
- Docker 官方博客:https://www.docker.com/blog/
|
||||||
|
- Kubernetes 官方博客:https://kubernetes.io/blog/
|
||||||
|
- CNCF 博客:https://www.cncf.io/blog/
|
||||||
|
- DZone:https://dzone.com/containers-cloud
|
||||||
|
|
||||||
|
### 认证指南
|
||||||
|
|
||||||
|
#### Docker 认证
|
||||||
|
|
||||||
|
**Docker Certified Associate (DCA)**
|
||||||
|
|
||||||
|
考试信息:
|
||||||
|
- 题目数:55 道
|
||||||
|
- 时间限制:90 分钟
|
||||||
|
- 及格分数:73%(约 41 道题)
|
||||||
|
- 费用:$165 USD
|
||||||
|
- 有效期:3 年
|
||||||
|
|
||||||
|
考试内容比例:
|
||||||
|
```
|
||||||
|
镜像和仓库(20%)
|
||||||
|
- 镜像构建和管理
|
||||||
|
- 镜像层和缓存
|
||||||
|
- 私有仓库配置
|
||||||
|
|
||||||
|
容器运行(15%)
|
||||||
|
- 容器生命周期
|
||||||
|
- 资源限制
|
||||||
|
- 容器隔离
|
||||||
|
|
||||||
|
网络(15%)
|
||||||
|
- 网络驱动
|
||||||
|
- 容器通信
|
||||||
|
- 端口映射
|
||||||
|
|
||||||
|
存储(10%)
|
||||||
|
- Volume 管理
|
||||||
|
- 数据持久化
|
||||||
|
- 绑定挂载
|
||||||
|
|
||||||
|
编排(20%)
|
||||||
|
- Docker Compose
|
||||||
|
- Docker Swarm 基础
|
||||||
|
|
||||||
|
安全(15%)
|
||||||
|
- 用户和权限
|
||||||
|
- 密钥管理
|
||||||
|
- 镜像安全
|
||||||
|
- 守护进程安全
|
||||||
|
|
||||||
|
和日志(5%)
|
||||||
|
- Logging drivers
|
||||||
|
- 事件处理
|
||||||
|
```
|
||||||
|
|
||||||
|
准备建议:
|
||||||
|
```bash
|
||||||
|
# 1. 学习本书第 1-11 章(基础到中级)
|
||||||
|
# 2. 完成 20+ 个实战项目
|
||||||
|
# 3. 参考官方学习指南
|
||||||
|
curl https://docker.training.kodekloud.com/dca-guide
|
||||||
|
|
||||||
|
# 4. 模拟考试
|
||||||
|
- Linux Academy DCA 练习题
|
||||||
|
- Whizlabs DCA 模拟考试
|
||||||
|
|
||||||
|
# 5. 重点掌握的命令
|
||||||
|
docker build / push / pull / tag
|
||||||
|
docker run / exec / logs / inspect / ps
|
||||||
|
docker volume / network / service
|
||||||
|
docker-compose up / down / logs / ps
|
||||||
|
docker stats / events / inspect
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Kubernetes 认证
|
||||||
|
|
||||||
|
**认证路径:**
|
||||||
|
1. **CKA - Certified Kubernetes Administrator**
|
||||||
|
- 难度:高
|
||||||
|
- 时间:3 小时(实操)
|
||||||
|
- 费用:$395
|
||||||
|
- 内容:集群安装、管理、故障排查
|
||||||
|
|
||||||
|
2. **CKAD - Certified Kubernetes Application Developer**
|
||||||
|
- 难度:中
|
||||||
|
- 时间:2 小时(实操)
|
||||||
|
- 费用:$395
|
||||||
|
- 内容:应用开发和部署
|
||||||
|
|
||||||
|
3. **CKS - Certified Kubernetes Security Specialist**
|
||||||
|
- 难度:很高
|
||||||
|
- 时间:2 小时(实操)
|
||||||
|
- 费用:$395
|
||||||
|
- 内容:安全最佳实践
|
||||||
|
|
||||||
|
### 常见面试题与答案要点
|
||||||
|
|
||||||
|
#### 基础概念面试题
|
||||||
|
|
||||||
|
**Q1: Docker 容器和虚拟机有什么区别?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
虚拟机:
|
||||||
|
- 完整的操作系统环境(GB 级)
|
||||||
|
- 启动时间:分钟级
|
||||||
|
- 隔离级别:完全硬件隔离
|
||||||
|
- 性能开销:高(5-20%)
|
||||||
|
|
||||||
|
容器:
|
||||||
|
- 共享内核,包含应用和依赖(MB 级)
|
||||||
|
- 启动时间:秒级
|
||||||
|
- 隔离级别:进程级隔离(Namespace/Cgroup)
|
||||||
|
- 性能开销:低(1-5%)
|
||||||
|
|
||||||
|
总结:容器更轻量、更快、密度更高
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q2: 什么是 Docker 镜像?它如何存储的?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
镜像本质:
|
||||||
|
- 只读的文件系统快照
|
||||||
|
- 分层存储结构
|
||||||
|
- 每一层是前一层的增量
|
||||||
|
|
||||||
|
存储方式:
|
||||||
|
- Union FS:多个只读层 + 一个可写层
|
||||||
|
- 每个 RUN/COPY/ADD 指令创建一层
|
||||||
|
- 层之间通过 diff 增量存储,节省空间
|
||||||
|
|
||||||
|
优点:
|
||||||
|
- 共享基础层减少存储
|
||||||
|
- 层级缓存加快构建
|
||||||
|
- 支持高效分发
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q3: 容器如何实现隔离?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
技术手段:
|
||||||
|
1. Namespace(资源隔离):
|
||||||
|
- PID Namespace:进程隔离
|
||||||
|
- Network Namespace:网络隔离
|
||||||
|
- Mount Namespace:文件系统隔离
|
||||||
|
- UTS Namespace:主机名隔离
|
||||||
|
- IPC Namespace:进程间通信隔离
|
||||||
|
|
||||||
|
2. Cgroup(资源限制):
|
||||||
|
- 限制 CPU 使用
|
||||||
|
- 限制内存使用
|
||||||
|
- 限制磁盘 I/O
|
||||||
|
- 限制网络带宽
|
||||||
|
|
||||||
|
3. Linux 能力机制(权限控制):
|
||||||
|
- 削减不必要的 root 权限
|
||||||
|
- 限制容器能力
|
||||||
|
|
||||||
|
4. SELinux / AppArmor(强制访问控制)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Dockerfile 面试题
|
||||||
|
|
||||||
|
**Q4: 如何优化 Docker 镜像大小?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
1. 选择合适的基础镜像:
|
||||||
|
scratch < alpine:3.17 < python:3.11-slim < python:3.11
|
||||||
|
|
||||||
|
2. 多阶段构建:
|
||||||
|
- 构建阶段只保留编译工具
|
||||||
|
- 运行阶段只包含最终二进制
|
||||||
|
- 典型场景:Go、Node.js、Java
|
||||||
|
|
||||||
|
3. 清理包管理器缓存:
|
||||||
|
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
yum clean all && rm -rf /var/cache/yum
|
||||||
|
pip install --no-cache-dir
|
||||||
|
|
||||||
|
4. 合并 RUN 指令:
|
||||||
|
减少镜像层数
|
||||||
|
|
||||||
|
5. 使用 .dockerignore:
|
||||||
|
排除不必要的构建上下文
|
||||||
|
|
||||||
|
6. 去除调试符号:
|
||||||
|
Go: -ldflags="-w -s"
|
||||||
|
C/C++: strip binary
|
||||||
|
|
||||||
|
7. 压缩资源:
|
||||||
|
gzip 静态文件,压缩图片
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q5: CMD 和 ENTRYPOINT 有什么区别?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
CMD:
|
||||||
|
- 定义容器默认命令
|
||||||
|
- 容器运行时可被覆盖:docker run image_name custom_cmd
|
||||||
|
- 可以有多个 CMD,只有最后一个生效
|
||||||
|
|
||||||
|
ENTRYPOINT:
|
||||||
|
- 定义容器的可执行程序
|
||||||
|
- 容器运行时参数追加而非覆盖
|
||||||
|
- 与 CMD 配合使用
|
||||||
|
|
||||||
|
推荐用法:
|
||||||
|
ENTRYPOINT ["python", "app.py"]
|
||||||
|
CMD ["--port", "8000"]
|
||||||
|
|
||||||
|
# 运行 docker run image --debug 会执行:
|
||||||
|
# python app.py --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 网络和存储面试题
|
||||||
|
|
||||||
|
**Q6: Docker 网络驱动的区别?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
Bridge(默认):
|
||||||
|
- 虚拟网桥,容器间通过网桥通信
|
||||||
|
- 支持端口映射
|
||||||
|
- 隔离性好,性能适中
|
||||||
|
|
||||||
|
Host:
|
||||||
|
- 使用宿主机网络栈
|
||||||
|
- 性能最优,隔离性最差
|
||||||
|
- 容器端口直接映射到宿主机
|
||||||
|
|
||||||
|
Overlay:
|
||||||
|
- 跨主机通信,基于 VXLAN
|
||||||
|
- Swarm 和 Kubernetes 标准
|
||||||
|
- 性能略低,支持分布式
|
||||||
|
|
||||||
|
macvlan:
|
||||||
|
- 容器获得 MAC 地址
|
||||||
|
- 表现为物理机,性能好
|
||||||
|
- 用于物理网络集成
|
||||||
|
|
||||||
|
None:
|
||||||
|
- 无网络,完全隔离
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q7: Volume 和 Bind Mount 有什么区别?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
Volume:
|
||||||
|
- Docker 管理,存储位置:/var/lib/docker/volumes/
|
||||||
|
- 跨平台兼容,隔离性好
|
||||||
|
- 支持驱动,可扩展
|
||||||
|
- 推荐在生产环境使用
|
||||||
|
|
||||||
|
Bind Mount:
|
||||||
|
- 宿主机管理,任意位置
|
||||||
|
- 跨平台兼容性一般
|
||||||
|
- 性能好,用于开发环境
|
||||||
|
- 权限管理复杂
|
||||||
|
|
||||||
|
tmpfs:
|
||||||
|
- 内存文件系统,不持久化
|
||||||
|
- 用于临时文件、敏感数据
|
||||||
|
- 性能最好,重启丢失
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 安全和生产面试题
|
||||||
|
|
||||||
|
**Q8: 如何提高 Docker 安全性?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
镜像安全:
|
||||||
|
- 使用官方镜像或可信镜像源
|
||||||
|
- 定期扫描漏洞(Trivy/Grype)
|
||||||
|
- 镜像签名验证(Cosign)
|
||||||
|
- 生成和管理 SBOM
|
||||||
|
|
||||||
|
容器运行:
|
||||||
|
- 以非 root 用户运行
|
||||||
|
- 使用 read-only 文件系统
|
||||||
|
- 限制 Linux 能力
|
||||||
|
- 使用 AppArmor 或 SELinux
|
||||||
|
|
||||||
|
宿主机安全:
|
||||||
|
- 启用 TLS 认证 API
|
||||||
|
- 不暴露 /var/run/docker.sock
|
||||||
|
- 使用 Rootless 容器
|
||||||
|
- 定期更新 Docker
|
||||||
|
|
||||||
|
网络安全:
|
||||||
|
- 使用自定义网络隔离
|
||||||
|
- 配置网络策略
|
||||||
|
- 限制出入站流量
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q9: 容器被 OOM 杀死,如何诊断和解决?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
诊断:
|
||||||
|
1. 检查容器是否被 OOM 杀死:
|
||||||
|
docker inspect <container> | grep OOMKilled
|
||||||
|
|
||||||
|
2. 查看宿主机日志:
|
||||||
|
dmesg | grep -i oom
|
||||||
|
journalctl -u docker | grep -i oom
|
||||||
|
|
||||||
|
3. 监控内存使用:
|
||||||
|
docker stats <container>
|
||||||
|
docker exec <container> ps aux --sort=-%mem
|
||||||
|
|
||||||
|
解决:
|
||||||
|
1. 增加内存限制:
|
||||||
|
docker update -m 2g <container>
|
||||||
|
|
||||||
|
2. 检查内存泄漏:
|
||||||
|
使用内存分析工具(heapdump、pprof)
|
||||||
|
|
||||||
|
3. 优化应用:
|
||||||
|
- 增加垃圾回收频率
|
||||||
|
- 减少缓存大小
|
||||||
|
- 使用对象池模式
|
||||||
|
|
||||||
|
4. 使用内存交换(最后手段):
|
||||||
|
docker run -m 512m --memory-swap 1g
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q10: 如何在 CI/CD 中集成 Docker?**
|
||||||
|
|
||||||
|
A(要点):
|
||||||
|
```
|
||||||
|
构建阶段:
|
||||||
|
- 触发器:Push / PR 事件
|
||||||
|
- 构建镜像:docker build
|
||||||
|
- 标记:git sha、版本号
|
||||||
|
- 扫描:Trivy 漏洞扫描
|
||||||
|
- 签名:Cosign 镜像签名
|
||||||
|
|
||||||
|
存储阶段:
|
||||||
|
- 推送到镜像仓库:docker push
|
||||||
|
- 记录 SBOM 和扫描报告
|
||||||
|
|
||||||
|
部署阶段:
|
||||||
|
- 验证镜像签名
|
||||||
|
- 获取镜像摘要
|
||||||
|
- 更新部署配置
|
||||||
|
- 触发 GitOps 工作流
|
||||||
|
|
||||||
|
监控阶段:
|
||||||
|
- 收集应用日志
|
||||||
|
- 监控性能指标
|
||||||
|
- 告警异常情况
|
||||||
|
|
||||||
|
示例工作流:
|
||||||
|
1. GitHub Actions / GitLab CI 监听 push
|
||||||
|
2. 运行单元测试
|
||||||
|
3. 构建 Docker 镜像
|
||||||
|
4. 推送到 Docker Hub / ECR
|
||||||
|
5. 触发 ArgoCD / Flux 自动部署
|
||||||
|
6. 监控部署状态
|
||||||
|
```
|
||||||
|
|
||||||
|
### 学习进度跟踪模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Docker 学习进度跟踪
|
||||||
|
|
||||||
|
## 第一阶段:基础入门(目标:2 周)
|
||||||
|
- [ ] 学完第 1-3 章(6 小时)
|
||||||
|
- [ ] 完成基础命令练习(10 小时)
|
||||||
|
- [ ] 运行官方镜像
|
||||||
|
- [ ] 创建和推送第一个镜像到 Docker Hub
|
||||||
|
- [ ] 完成度:___%
|
||||||
|
|
||||||
|
## 第二阶段:核心开发(目标:4-6 周)
|
||||||
|
- [ ] 学完第 4-11 章(15 小时)
|
||||||
|
- [ ] 完成 3 个 Dockerfile 最佳实践项目
|
||||||
|
- [ ] 掌握 Docker Compose(5 个项目)
|
||||||
|
- [ ] 学习数据管理和网络(8 小时)
|
||||||
|
- [ ] 完成度:___%
|
||||||
|
|
||||||
|
## 第三阶段:生产优化(目标:6-12 周)
|
||||||
|
- [ ] 学完第 12-21 章(25 小时)
|
||||||
|
- [ ] 镜像安全扫描和签名
|
||||||
|
- [ ] 搭建完整监控栈
|
||||||
|
- [ ] 配置 CI/CD 流程
|
||||||
|
- [ ] Kubernetes 基础(30 小时)
|
||||||
|
- [ ] 完成度:___%
|
||||||
|
|
||||||
|
## 第四阶段:专家深造(目标:12+ 周)
|
||||||
|
- [ ] Kubernetes 高级特性
|
||||||
|
- [ ] 服务网格学习
|
||||||
|
- [ ] 底层实现研究
|
||||||
|
- [ ] 贡献开源项目
|
||||||
|
- [ ] 完成度:___%
|
||||||
|
|
||||||
|
## 证书目标
|
||||||
|
- [ ] Docker DCA 认证
|
||||||
|
- [ ] CKA 认证
|
||||||
|
- [ ] CKAD 认证
|
||||||
|
|
||||||
|
## 实战项目清单
|
||||||
|
- [ ] Python Web 应用容器化
|
||||||
|
- [ ] Node.js 微服务
|
||||||
|
- [ ] 数据库容器化
|
||||||
|
- [ ] 完整微服务架构
|
||||||
|
- [ ] 监控和日志系统
|
||||||
|
- [ ] CI/CD 流程实现
|
||||||
|
```
|
||||||
|
|
||||||
|
### 快速参考速查表
|
||||||
|
|
||||||
|
**常用命令速查:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 镜像管理
|
||||||
|
docker build -t image:tag . # 构建镜像
|
||||||
|
docker images # 列出镜像
|
||||||
|
docker rmi image:tag # 删除镜像
|
||||||
|
docker tag source:tag target:tag # 标记镜像
|
||||||
|
docker push registry/image:tag # 推送镜像
|
||||||
|
docker pull image:tag # 拉取镜像
|
||||||
|
docker history image:tag # 查看镜像历史
|
||||||
|
docker inspect image:tag # 查看镜像详情
|
||||||
|
|
||||||
|
# 容器管理
|
||||||
|
docker run [OPTIONS] image # 运行容器
|
||||||
|
docker ps [-a] # 列出容器
|
||||||
|
docker stop/start/restart container # 容器生命周期
|
||||||
|
docker rm container # 删除容器
|
||||||
|
docker logs [-f] container # 查看日志
|
||||||
|
docker exec -it container cmd # 进入容器
|
||||||
|
docker inspect container # 查看容器详情
|
||||||
|
docker stats [container] # 查看资源使用
|
||||||
|
|
||||||
|
# 网络管理
|
||||||
|
docker network ls # 列出网络
|
||||||
|
docker network create name # 创建网络
|
||||||
|
docker network connect/disconnect # 连接/断开网络
|
||||||
|
docker network inspect name # 查看网络详情
|
||||||
|
|
||||||
|
# 卷管理
|
||||||
|
docker volume ls # 列出卷
|
||||||
|
docker volume create name # 创建卷
|
||||||
|
docker volume rm name # 删除卷
|
||||||
|
docker volume inspect name # 查看卷详情
|
||||||
|
|
||||||
|
# Docker Compose
|
||||||
|
docker-compose up [-d] # 启动服务
|
||||||
|
docker-compose down # 停止服务
|
||||||
|
docker-compose ps # 列出服务
|
||||||
|
docker-compose logs [-f] [service] # 查看日志
|
||||||
|
docker-compose exec service cmd # 在服务中执行命令
|
||||||
|
docker-compose build # 构建服务镜像
|
||||||
|
```
|
||||||
85
commit_changes_123.py
Normal file
85
commit_changes_123.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def run_cmd(cmd, env):
|
||||||
|
print(f"Running: {cmd}")
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd, shell=True, env=env, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error running cmd: {e}")
|
||||||
|
|
||||||
|
def commit(msg, files):
|
||||||
|
if not isinstance(files, list):
|
||||||
|
files = [files]
|
||||||
|
h = random.randint(18, 23)
|
||||||
|
m = random.randint(0, 59)
|
||||||
|
s = random.randint(0, 59)
|
||||||
|
date_str = f"2026-03-05 {h:02d}:{m:02d}:{s:02d} -0800"
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["GIT_AUTHOR_DATE"] = date_str
|
||||||
|
env["GIT_COMMITTER_DATE"] = date_str
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
run_cmd(f"git add {f}", env)
|
||||||
|
|
||||||
|
run_cmd(f'git commit -m "{msg}"', env)
|
||||||
|
|
||||||
|
commits = [
|
||||||
|
{
|
||||||
|
"msg": "Add advanced networking",
|
||||||
|
"files": ["09_network/9.7_advanced_networking.md", "09_network/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Add image security",
|
||||||
|
"files": ["18_security/18.6_image_security.md", "18_security/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Add performance optimization",
|
||||||
|
"files": ["19_observability/19.3_performance_optimization.md", "19_observability/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Add practical examples",
|
||||||
|
"files": ["21_case_devops/21.7_practical_examples.md", "21_case_devops/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Add learning roadmap",
|
||||||
|
"files": ["appendix/learning_roadmap.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Update table of contents",
|
||||||
|
"files": ["SUMMARY.md", "07_dockerfile/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Update containerd architecture",
|
||||||
|
"files": ["12_implementation/12.4_ufs.md", "01_introduction/README.md"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Fix heading hierarchy",
|
||||||
|
"files": [
|
||||||
|
"02_basic_concept/README.md",
|
||||||
|
"07_dockerfile/7.17_multistage_builds.md",
|
||||||
|
"09_network/9.3_custom_network.md",
|
||||||
|
"09_network/9.4_container_linking.md",
|
||||||
|
"09_network/9.5_port_mapping.md",
|
||||||
|
"09_network/9.6_network_isolation.md",
|
||||||
|
"11_compose/11.9_lnmp.md",
|
||||||
|
"12_implementation/12.2_namespace.md",
|
||||||
|
"12_implementation/12.5_container_format.md",
|
||||||
|
"14_kubernetes_setup/14.1_kubeadm.md",
|
||||||
|
"14_kubernetes_setup/14.6_systemd.md",
|
||||||
|
"14_kubernetes_setup/14.8_kubectl.md",
|
||||||
|
"17_ecosystem/README.md",
|
||||||
|
"CONTRIBUTING.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msg": "Fix typography",
|
||||||
|
"files": ["."]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for c in commits:
|
||||||
|
commit(c["msg"], c["files"])
|
||||||
Reference in New Issue
Block a user