Add more content and fix format

This commit is contained in:
Baohua Yang
2026-02-25 21:06:21 -08:00
parent dd449bc84f
commit ecab788013
119 changed files with 566 additions and 496 deletions

View File

@@ -1,4 +1,4 @@
## 1.3 为什么要使 Docker
## 1.3 为什么要用 Docker
在回答 为什么用 Docker 之前笔者想先问一个问题**你有没有经历过这些场景**

View File

@@ -1,4 +1,4 @@
## 1.4 本章小结
## 本章小结
- Docker 是一种轻量级虚拟化技术核心价值是 **环境一致性**
- 与虚拟机相比Docker 更轻量更快速资源利用率更高

View File

@@ -1,4 +1,4 @@
## 2.1 Docker 镜像
## 2.1 镜像
Docker 镜像作为容器运行的基石其设计理念和实现机制至关重要本节将深入探讨镜像的本质与操作系统的关系内容构成以及核心的分层存储机制

View File

@@ -1,4 +1,4 @@
## 2.2 Docker 容器
## 2.2 容器
容器是 Docker 技术的核心是应用实际运行的载体本节将从容器的本质与虚拟机的区别存储层机制以及生命周期管理等方面全面解析 Docker 容器

View File

@@ -1,4 +1,4 @@
## 2.3 Docker Registry
## 2.3 仓库
Docker Registry 是镜像分发和管理的核心组件本节将介绍 Registry 的基本概念公共和私有服务的选择以及镜像的安全管理

View File

@@ -1,4 +1,4 @@
## 2.4 本章小结
## 本章小结
| 概念 | 要点 |
|------|------|
@@ -13,7 +13,7 @@
- [获取镜像](../04_image/4.1_pull.md) Registry 下载镜像
- [使用 Dockerfile 定制镜像](../04_image/4.5_build.md)创建自己的镜像
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)构建高质量镜像的技巧
- [Dockerfile 最佳实践](../appendix/best_practices.md)构建高质量镜像的技巧
- [底层实现 - 联合文件系统](../12_implementation/12.4_ufs.md)深入理解分层存储的技术原理
| 概念 | 要点 |

View File

@@ -1,4 +1,4 @@
## 3.1 Ubuntu 安装 Docker
## 3.1 Ubuntu
Ubuntu Docker 最常用的运行环境之一本节将介绍如何在 Ubuntu 系统上安装 Docker并配置国内镜像加速

View File

@@ -1,4 +1,4 @@
## 3.2 Debian 安装 Docker
## 3.2 Debian
Debian 以其稳定性著称 Docker 的理想宿主系统本节将指导你在 Debian 上完成 Docker 的安装

View File

@@ -1,4 +1,4 @@
## 3.3 Fedora 安装 Docker
## 3.3 Fedora
Fedora 作为技术前沿的 Linux 发行版 Docker 有着良好的支持本节介绍在 Fedora 上的安装步骤

View File

@@ -1,4 +1,4 @@
## 3.4 CentOS 安装 Docker
## 3.4 CentOS
CentOS (及其替代品 Rocky LinuxAlmaLinux) 是企业级服务器常用的操作系统本节介绍在这些系统上安装 Docker 的步骤

View File

@@ -1,4 +1,4 @@
## 3.5 树莓派卡片电脑安装 Docker
## 3.5 Raspberry Pi
树莓派等 ARM 架构设备在物联网和边缘计算领域应用广泛本节介绍如何在树莓派上安装 Docker

View File

@@ -6,6 +6,9 @@
### 3.7.2 安装
> [!WARNING]
> **商业许可限制** 2021 年起Docker Desktop 对微型企业少于 250 名员工且年收入少于 1000 万美元个人使用教育和非商业开源项目仍然免费对于其他商业用途需要付费订阅企业用户请注意合规风险或考虑使用开源替代方案
Docker Desktop Mac 用户提供了无缝的 Docker 体验你可以选择使用 Homebrew 或手动下载安装包进行安装
#### 使用 Homebrew 安装

View File

@@ -8,6 +8,9 @@
### 3.8.2 安装
> [!WARNING]
> **商业许可限制** 2021 年起Docker Desktop 对微型企业少于 250 名员工且年收入少于 1000 万美元个人使用教育和非商业开源项目仍然免费对于其他商业用途需要付费订阅企业用户请注意合规风险或考虑使用开源替代方案
**手动下载安装**
点击以下[链接](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe)下载 Docker Desktop for Windows。

View File

@@ -1,4 +1,4 @@
## 3.11 本章小结
## 本章小结
Docker 支持在多种平台上安装和使用选择合适的安装方式是顺利使用 Docker 的第一步

View File

@@ -1,4 +1,4 @@
## 4.8 本章小结
## 本章小结
| 操作 | 命令 |
|------|------|

View File

@@ -1,4 +1,4 @@
## 5.1 启动容器
## 5.1 启动
本节将详细介绍 Docker 容器的启动方式包括新建启动和重新启动已停止的容器

View File

@@ -1,4 +1,4 @@
## 5.2 后台运行
## 5.2 守护态运行
在生产环境中我们通常需要容器持续运行不受终端关闭的影响本节将深入讲解如何让容器在后台运行以及理解容器生命周期的核心概念
@@ -57,8 +57,6 @@ $ docker run -d ubuntu:24.04
#### 核心原理容器的生命周期与主进程绑定
如下代码块所示展示了相关示例
```mermaid
flowchart TD
subgraph Lifecycle ["Docker 容器的生命周期 = 容器内 PID 1 进程的生命周期"]
@@ -190,7 +188,7 @@ $ docker logs -t myapp
1. **查看退出状态码**
```bash
$ docker ps -a --filter "name=mycontainer"
# 查看 STATUS 列,如 "Exited (1)" 表示异常退出
# 查看 STATUS 列,如 Exited (1) 表示异常退出
```
@@ -231,6 +229,6 @@ $ docker attach mycontainer
### 5.2.7 延伸阅读
- [进入容器](5.4_attach_exec.md)如何进入正在运行的容器执行命令
- [容器日志](../appendix/20.1_best_practices.md)生产环境的日志管理最佳实践
- [容器日志](../appendix/best_practices.md)生产环境的日志管理最佳实践
- [HEALTHCHECK 健康检查](../07_dockerfile/7.12_healthcheck.md)自动检测容器内服务是否正常
- [Docker Compose](../11_compose/README.md)管理多个后台容器的更好方式

View File

@@ -1,4 +1,4 @@
## 5.3 终止容器
## 5.3 终止
本节将介绍如何终止一个运行中的容器以及几种不同的终止方式及其区别

View File

@@ -1,4 +1,4 @@
## 5.5 导出和导入容器
## 5.5 导出和导入
当我们需要迁移容器或者备份容器时可以使用 Docker 的导入和导出功能本节将介绍这两个命令的使用方法

View File

@@ -1,4 +1,4 @@
## 5.6 删除容器
## 5.6 删除
随着容器的创建和停止系统中会积累大量的容器本节将介绍如何删除不再需要的容器以及如何清理所有停止的容器

View File

@@ -1,4 +1,4 @@
## 5.7 本章小结
## 本章小结
| 操作 | 命令 | 说明 |
|------|------|------|

View File

@@ -1,4 +1,4 @@
## 6.5 本章小结
## 本章小结
| 功能 | 说明 |
|------|------|

View File

@@ -2,8 +2,6 @@
### 7.10.1 基本语法
如下代码块所示展示了相关示例
```docker
WORKDIR <工作目录路径>
```
@@ -14,8 +12,6 @@ WORKDIR <工作目录路径>
### 7.10.2 基本用法
如下代码块所示展示了相关示例
```docker
WORKDIR /app
@@ -30,8 +26,6 @@ COPY . . # 复制到 /app/
#### 常见错误
如下代码块所示展示了相关示例
```docker
## ❌ 错误cd 在下一个 RUN 中无效
@@ -41,8 +35,6 @@ RUN echo "hello" > world.txt # 文件在根目录!
#### 原因分析
如下代码块所示展示了相关示例
```dockerfile
RUN cd /app
@@ -58,8 +50,6 @@ RUN echo "hello" > world.txt
#### 正确做法
如下代码块所示展示了相关示例
```docker
## ✅ 正确:使用 WORKDIR
@@ -85,8 +75,6 @@ RUN pwd # 输出 /a/b/c
### 7.10.5 使用环境变量
如下代码块所示展示了相关示例
```docker
ENV APP_HOME=/app
WORKDIR $APP_HOME
@@ -98,8 +86,6 @@ RUN pwd # 输出 /app
### 7.10.6 多阶段构建中的 WORKDIR
如下代码块所示展示了相关示例
```docker
## 构建阶段
@@ -123,8 +109,6 @@ COPY --from=builder /build/dist .
#### 1. 尽早设置 WORKDIR
如下代码块所示展示了相关示例
```docker
FROM node:20
WORKDIR /app # 尽早设置
@@ -137,8 +121,6 @@ CMD ["node", "server.js"]
#### 2. 使用绝对路径
如下代码块所示展示了相关示例
```docker
## ✅ 推荐:绝对路径,意图明确
@@ -151,8 +133,6 @@ WORKDIR app
#### 3. 不要用 RUN cd
如下代码块所示展示了相关示例
```docker
## ❌ 避免
@@ -166,8 +146,6 @@ RUN echo "hello" > world.txt
#### 4. 适时重置 WORKDIR
如下代码块所示展示了相关示例
```docker
WORKDIR /app
## ... 应用相关操作 ...

View File

@@ -2,8 +2,6 @@
### 7.11.1 基本语法
如下代码块所示展示了相关示例
```docker
USER <用户名>[:<用户组>]
USER <UID>[:<GID>]
@@ -38,8 +36,6 @@ flowchart LR
#### 创建并切换用户
如下代码块所示展示了相关示例
```docker
FROM node:20-alpine
@@ -64,8 +60,6 @@ CMD ["node", "server.js"]
#### 使用 UID/GID
如下代码块所示展示了相关示例
```docker
## 也可以使用数字
@@ -213,8 +207,6 @@ CMD ["node", "server.js"]
#### 1. 始终使用非 root 用户
如下代码块所示展示了相关示例
```docker
## ✅ 推荐
@@ -241,8 +233,6 @@ USER 1000:1000
#### 3. 多阶段构建中的 USER
如下代码块所示展示了相关示例
```docker
## 构建阶段可以用 root

View File

@@ -2,8 +2,6 @@
### 7.12.1 基本语法
如下代码块所示展示了相关示例
```docker
HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE
@@ -37,8 +35,6 @@ Starting ──成功──> Healthy ──失败N次──> Unhealthy
#### Web 服务检查
如下代码块所示展示了相关示例
```docker
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
@@ -93,8 +89,6 @@ HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1
#### 数据库
如下代码块所示展示了相关示例
```docker
## MySQL
@@ -107,8 +101,6 @@ HEALTHCHECK CMD redis-cli ping || exit 1
#### 自定义脚本
如下代码块所示展示了相关示例
```docker
COPY healthcheck.sh /usr/local/bin/
HEALTHCHECK CMD ["healthcheck.sh"]

View File

@@ -1,9 +1,7 @@
## 7.13 ONBUILD 为他人嫁衣裳
## 7.13 ONBUILD 为他人嫁衣裳
### 7.13.1 基本语法
如下代码块所示展示了相关示例
```docker
ONBUILD <其它指令>
```
@@ -60,8 +58,6 @@ FROM my-node-base
### 7.13.3 执行机制
如下代码块所示展示了相关示例
```bash
基础镜像构建:
Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触发器)
@@ -77,8 +73,6 @@ FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行
#### 1. 自动处理依赖安装
如下代码块所示展示了相关示例
```docker
## Python 基础镜像
@@ -88,8 +82,6 @@ ONBUILD RUN pip install -r requirements.txt
#### 2. 自动编译代码
如下代码块所示展示了相关示例
```docker
## Go 基础镜像
@@ -99,8 +91,6 @@ ONBUILD RUN go build -o app main.go
#### 3. 处理静态资源
如下代码块所示展示了相关示例
```docker
## Nginx 静态网站基础镜像

View File

@@ -2,8 +2,6 @@
### 7.14.1 基本语法
如下代码块所示展示了相关示例
```docker
LABEL <key>=<value> <key>=<value> ...
```
@@ -25,8 +23,6 @@ LABEL <key>=<value> <key>=<value> ...
#### 定义单个标签
如下代码块所示展示了相关示例
```docker
LABEL version="1.0"
LABEL description="这是一个 Web 应用服务器"
@@ -34,8 +30,6 @@ LABEL description="这是一个 Web 应用服务器"
#### 定义多个标签 (推荐)
如下代码块所示展示了相关示例
```docker
LABEL maintainer="user@example.com" \
version="1.2.0" \
@@ -65,8 +59,6 @@ LABEL maintainer="user@example.com" \
#### 示例
如下代码块所示展示了相关示例
```docker
LABEL org.opencontainers.image.authors="yeasy" \
org.opencontainers.image.documentation="https://yeasy.gitbooks.io" \

View File

@@ -2,8 +2,6 @@
### 7.15.1 基本语法
如下代码块所示展示了相关示例
```docker
SHELL ["executable", "parameters"]
```

View File

@@ -125,8 +125,6 @@ RUN set -x ; cd ${LARAVEL_PATH} \
### 7.18.5 最后一个阶段构建 NGINX 镜像
如下代码块所示展示了相关示例
```docker
FROM nginx:alpine as nginx

View File

@@ -2,8 +2,6 @@
### 7.1.1 基本语法
如下代码块所示展示了相关示例
```docker
RUN <command>
RUN ["executable", "param1", "param2"]
@@ -17,8 +15,6 @@ RUN ["executable", "param1", "param2"]
#### 1. Shell 格式
如下代码块所示展示了相关示例
```docker
RUN apt-get update
```
@@ -32,8 +28,6 @@ RUN apt-get update
#### 2. Exec 格式
如下代码块所示展示了相关示例
```docker
RUN ["apt-get", "update"]
```
@@ -106,8 +100,6 @@ RUN wget http://url | gzip -d > file
#### Q为什么 `RUN cd /app` 不生效
如下代码块所示展示了相关示例
```docker
RUN cd /app
RUN touch hello.txt
@@ -122,8 +114,6 @@ RUN touch hello.txt
#### Q环境变量不生效
如下代码块所示展示了相关示例
```docker
RUN export MY_VAR=hello
RUN echo $MY_VAR

View File

@@ -2,8 +2,6 @@
### 7.2.1 基本语法
如下代码块所示展示了相关示例
```docker
COPY [选项] <源路径>... <目标路径>
COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]
@@ -17,8 +15,6 @@ COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]
#### 复制单个文件
如下代码块所示展示了相关示例
```docker
## 复制文件到指定目录
@@ -31,8 +27,6 @@ COPY config.json /app/settings.json
#### 复制多个文件
如下代码块所示展示了相关示例
```docker
## 复制多个指定文件
@@ -46,8 +40,6 @@ COPY src/*.js /app/src/
#### 复制目录
如下代码块所示展示了相关示例
```docker
## 复制整个目录的内容(不是目录本身)
@@ -88,16 +80,12 @@ COPY app[0-9].js /app/ # app0.js ~ app9.js
#### 绝对路径
如下代码块所示展示了相关示例
```docker
COPY app.js /usr/src/app/
```
#### 相对路径 (基于 WORKDIR)
如下代码块所示展示了相关示例
```docker
WORKDIR /app
COPY package.json ./ # 复制到 /app/package.json
@@ -183,8 +171,6 @@ ADD app.tar.gz /app/
#### 从其他构建阶段复制
如下代码块所示展示了相关示例
```docker
## 构建阶段
@@ -203,8 +189,6 @@ COPY --from=builder /app/dist /usr/share/nginx/html
#### 使用 --link 优化缓存
如下代码块所示展示了相关示例
```docker
## 使用 --link 后,文件以独立层添加,不依赖前序指令
@@ -246,8 +230,6 @@ Dockerfile
#### 1. 利用缓存先复制依赖文件
如下代码块所示展示了相关示例
```docker
## ✅ 好:先复制依赖定义,再安装,最后复制代码
@@ -263,8 +245,6 @@ RUN npm install
#### 2. 使用 .dockerignore
如下代码块所示展示了相关示例
```docker
## 确保 node_modules 不被复制
@@ -276,8 +256,6 @@ COPY . .
#### 3. 明确复制路径
如下代码块所示展示了相关示例
```docker
## ✅ 好:明确的路径

View File

@@ -2,8 +2,6 @@
### 7.3.1 基本语法
如下代码块所示展示了相关示例
```docker
ADD [选项] <源路径>... <目标路径>
ADD [选项] ["<源路径>", ... "<目标路径>"]
@@ -34,8 +32,6 @@ ADD [选项] ["<源路径>", ... "<目标路径>"]
#### 基本用法 (自动解压本地 tar)
如下代码块所示展示了相关示例
```docker
## 自动解压 tar.gz 到目标目录
@@ -60,8 +56,6 @@ ADD ubuntu-noble-core-cloudimg-amd64-root.tar.gz /
#### 解压过程
如下代码块所示展示了相关示例
```bash
ADD app.tar.gz /app/
@@ -81,8 +75,6 @@ app.tar.gz 包含: /app/ 目录结果:
#### 基本用法
如下代码块所示展示了相关示例
```docker
## 从 URL 下载文件
@@ -100,8 +92,6 @@ ADD https://example.com/app.zip /app/app.zip
#### 推荐替代方案
如下代码块所示展示了相关示例
```docker
## ❌ 不推荐:使用 ADD 下载
@@ -123,8 +113,6 @@ RUN curl -fsSL https://example.com/app.tar.gz | tar -xz -C /app
### 7.3.5 修改文件所有者
如下代码块所示展示了相关示例
```docker
ADD --chown=node:node app.tar.gz /app/
ADD --chown=1000:1000 files/ /app/
@@ -136,8 +124,6 @@ ADD --chown=1000:1000 files/ /app/
#### 适合使用 ADD
如下代码块所示展示了相关示例
```docker
## 解压本地 tar 文件
@@ -151,8 +137,6 @@ ADD dist.tar.gz /app/
#### 不适合使用 ADD
如下代码块所示展示了相关示例
```docker
## 复制普通文件(用 COPY
@@ -202,8 +186,6 @@ ADD app.tar.gz /app/
#### 1. 默认使用 COPY
如下代码块所示展示了相关示例
```docker
## ✅ 大多数场景使用 COPY
@@ -212,8 +194,6 @@ COPY . /app/
#### 2. 仅在需要解压时使用 ADD
如下代码块所示展示了相关示例
```docker
## ✅ 自动解压场景
@@ -222,8 +202,6 @@ ADD app.tar.gz /app/
#### 3. 不要用 ADD 下载文件
如下代码块所示展示了相关示例
```docker
## ❌ 避免
@@ -236,8 +214,6 @@ RUN curl -fsSL https://example.com/file.tar.gz | tar -xz -C /app
#### 4. 解压后清理
如下代码块所示展示了相关示例
```docker
## 如果需要控制解压过程

View File

@@ -20,8 +20,6 @@ CMD 有三种格式:
#### exec 格式 (推荐)
如下代码块所示展示了相关示例
```docker
CMD ["nginx", "-g", "daemon off;"]
CMD ["python", "app.py"]
@@ -36,8 +34,6 @@ CMD ["node", "server.js"]
#### shell 格式
如下代码块所示展示了相关示例
```docker
CMD echo "Hello World"
CMD nginx -g "daemon off;"
@@ -70,8 +66,6 @@ CMD ["sh", "-c", "echo $HOME"]
#### 信号传递问题示例
如下代码块所示展示了相关示例
```docker
## ❌ shell 格式docker stop 会超时
@@ -116,8 +110,6 @@ CMD ["/bin/bash"] + cat /etc/os-release
#### 错误示例
如下代码块所示展示了相关示例
```docker
## ❌ 容器启动后立即退出
@@ -126,8 +118,6 @@ CMD service nginx start
#### 原因分析
如下代码块所示展示了相关示例
```bash
1. CMD service nginx start
↓ 被转换为
@@ -144,8 +134,6 @@ CMD service nginx start
#### 正确做法
如下代码块所示展示了相关示例
```docker
## ✅ 让 nginx 在前台运行
@@ -163,8 +151,6 @@ CMD ["nginx", "-g", "daemon off;"]
#### 单独使用 CMD
如下代码块所示展示了相关示例
```docker
## Dockerfile
@@ -178,8 +164,6 @@ $ docker run myimage curl -v ... # 完全覆盖
#### 搭配 ENTRYPOINT
如下代码块所示展示了相关示例
```docker
## Dockerfile
@@ -200,8 +184,6 @@ $ docker run myimage http://other.com # curl -s http://other.com参数覆盖
#### 1. 优先使用 exec 格式
如下代码块所示展示了相关示例
```docker
## ✅ 推荐
@@ -214,8 +196,6 @@ CMD ["sh", "-c", "echo $PATH && python app.py"]
#### 2. 确保应用在前台运行
如下代码块所示展示了相关示例
```docker
## ✅ 前台运行
@@ -231,8 +211,6 @@ CMD systemctl start nginx
#### 3. 使用双引号
如下代码块所示展示了相关示例
```docker
## ✅ 正确:双引号
@@ -245,8 +223,6 @@ CMD ['node', 'server.js']
#### 4. 配合 ENTRYPOINT 使用
如下代码块所示展示了相关示例
```docker
## 用于可配置参数的场景
@@ -275,8 +251,6 @@ CMD ["echo", "second"] # 只有这个生效
#### Q如何在 CMD 中使用环境变量
如下代码块所示展示了相关示例
```docker
## 方法1使用 shell 格式

View File

@@ -40,8 +40,6 @@ ENTRYPOINT nginx -g "daemon off;"
#### 行为对比
如下代码块所示展示了相关示例
```docker
## 只用 CMD
@@ -88,8 +86,6 @@ $ docker run myimage -v http://other.com # curl -s -v http://other.com ✓
#### 使用 CMD 的问题
如下代码块所示展示了相关示例
```docker
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
@@ -109,8 +105,6 @@ exec: "-i": executable file not found
#### 使用 ENTRYPOINT 解决
如下代码块所示展示了相关示例
```docker
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
@@ -129,8 +123,6 @@ HTTP/1.1 200 OK
#### 交互图示
如下代码块所示展示了相关示例
```bash
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
@@ -152,8 +144,6 @@ curl -s http://myip.ipip.net -i
#### 实现方式
如下代码块所示展示了相关示例
```docker
FROM redis:7-alpine
COPY docker-entrypoint.sh /usr/local/bin/
@@ -185,8 +175,6 @@ exec "$@"
#### 工作流程
如下代码块所示展示了相关示例
```bash
docker run redis docker run redis bash
│ │
@@ -209,8 +197,6 @@ docker-entrypoint.sh redis-server docker-entrypoint.sh bash
### 7.5.6 场景三带参数的应用
如下代码块所示展示了相关示例
```docker
FROM python:3.12-slim
WORKDIR /app
@@ -280,8 +266,6 @@ $ docker run --entrypoint /bin/cat myimage /etc/os-release
#### 1. 使用 exec 格式
如下代码块所示展示了相关示例
```docker
## ✅ 推荐
@@ -294,8 +278,6 @@ ENTRYPOINT python app.py
#### 2. 提供有意义的默认参数
如下代码块所示展示了相关示例
```docker
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

View File

@@ -2,8 +2,6 @@
### 7.6.1 基本语法
如下代码块所示展示了相关示例
```docker
## 格式一:单个变量
@@ -20,8 +18,6 @@ ENV <key1>=<value1> <key2>=<value2> ...
#### 设置单个变量
如下代码块所示展示了相关示例
```docker
ENV NODE_VERSION 20.10.0
ENV APP_ENV production
@@ -29,8 +25,6 @@ ENV APP_ENV production
#### 设置多个变量
如下代码块所示展示了相关示例
```docker
ENV NODE_VERSION=20.10.0 \
APP_ENV=production \
@@ -45,8 +39,6 @@ ENV NODE_VERSION=20.10.0 \
#### 1. 后续指令中使用
如下代码块所示展示了相关示例
```docker
ENV NODE_VERSION=20.10.0
@@ -67,8 +59,6 @@ COPY . $APP_HOME
#### 2. 容器运行时使用
如下代码块所示展示了相关示例
```docker
ENV DATABASE_URL=postgres://localhost/mydb
```
@@ -147,8 +137,6 @@ DATABASE_URL=postgres://localhost/mydb
#### 组合使用
如下代码块所示展示了相关示例
```docker
## ARG 接收构建时参数
@@ -175,8 +163,6 @@ $ docker build --build-arg NODE_VERSION=18 -t myapp .
#### 1. 统一管理版本号
如下代码块所示展示了相关示例
```docker
## ✅ 好:版本集中管理
@@ -193,8 +179,6 @@ RUN apt-get install nginx=1.25.0
#### 2. 不要存储敏感信息
如下代码块所示展示了相关示例
```docker
## ❌ 错误:密码写入镜像
@@ -209,8 +193,6 @@ ENV DB_PASSWORD=secret123
#### 3. 为应用提供合理默认值
如下代码块所示展示了相关示例
```docker
ENV APP_ENV=production \
APP_PORT=8080 \
@@ -219,8 +201,6 @@ ENV APP_ENV=production \
#### 4. 使用有意义的变量名
如下代码块所示展示了相关示例
```docker
## ✅ 好:清晰的命名
@@ -260,8 +240,6 @@ $ docker exec mycontainer env
#### Q多行 ENV 还是多个 ENV
如下代码块所示展示了相关示例
```docker
## ✅ 推荐:减少层数

View File

@@ -2,8 +2,6 @@
### 7.7.1 基本语法
如下代码块所示展示了相关示例
```docker
ARG <参数名>[=<默认值>]
```
@@ -37,8 +35,6 @@ ARG <参数名>[=<默认值>]
#### 定义和使用
如下代码块所示展示了相关示例
```docker
## 定义有默认值的 ARG
@@ -68,8 +64,6 @@ $ docker build --build-arg NODE_VERSION=18 -t myapp .
#### FROM 之前的 ARG
如下代码块所示展示了相关示例
```docker
## FROM 之前的 ARG 只能用于 FROM 指令
@@ -85,8 +79,6 @@ RUN echo $REGISTRY # 输出空
#### FROM 之后重新声明
如下代码块所示展示了相关示例
```docker
ARG NODE_VERSION=20
@@ -100,8 +92,6 @@ RUN echo "Node version: $NODE_VERSION"
#### 多阶段构建中的 ARG
如下代码块所示展示了相关示例
```docker
ARG BASE_VERSION=alpine
@@ -124,8 +114,6 @@ RUN echo "Running with Node $NODE_VERSION"
#### 1. 控制基础镜像版本
如下代码块所示展示了相关示例
```docker
ARG ALPINE_VERSION=3.19
FROM alpine:${ALPINE_VERSION}
@@ -137,8 +125,6 @@ $ docker build --build-arg ALPINE_VERSION=3.18 .
#### 2. 设置软件版本
如下代码块所示展示了相关示例
```docker
ARG NGINX_VERSION=1.25.0
@@ -147,8 +133,6 @@ RUN curl -fsSL https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar -x
#### 3. 配置构建环境
如下代码块所示展示了相关示例
```docker
ARG BUILD_ENV=production
ARG ENABLE_DEBUG=false
@@ -162,8 +146,6 @@ RUN if [ "$ENABLE_DEBUG" = "true" ]; then \
#### 4. 配置私有仓库
如下代码块所示展示了相关示例
```docker
ARG NPM_TOKEN
@@ -221,8 +203,6 @@ $ docker build --build-arg HTTP_PROXY=http://proxy:8080 .
#### 1. ARG 提供合理默认值
如下代码块所示展示了相关示例
```docker
## ✅ 好:有默认值
@@ -235,8 +215,6 @@ ARG NODE_VERSION
#### 2. 不要用 ARG 存储敏感信息
如下代码块所示展示了相关示例
```docker
## ❌ 错误:密码会被记录在镜像历史中
@@ -250,8 +228,6 @@ RUN echo "password=$DB_PASSWORD" > /app/.env
#### 3. 使用 ARG 提高构建灵活性
如下代码块所示展示了相关示例
```docker
ARG BASE_IMAGE=python:3.12-slim
FROM ${BASE_IMAGE}

View File

@@ -2,8 +2,6 @@
### 7.8.1 基本语法
如下代码块所示展示了相关示例
```docker
VOLUME ["/路径1", "/路径2"]
VOLUME /路径
@@ -47,8 +45,6 @@ flowchart LR
#### 定义单个卷
如下代码块所示展示了相关示例
```docker
FROM mysql:8.0
VOLUME /var/lib/mysql
@@ -56,8 +52,6 @@ VOLUME /var/lib/mysql
#### 定义多个卷
如下代码块所示展示了相关示例
```docker
FROM myapp
VOLUME ["/data", "/logs", "/config"]
@@ -113,8 +107,6 @@ RUN echo "hello" > /data/test.txt
#### 正确做法
如下代码块所示展示了相关示例
```docker
FROM ubuntu
@@ -133,8 +125,6 @@ VOLUME /data
#### 数据库持久化
如下代码块所示展示了相关示例
```docker
FROM postgres:15
VOLUME /var/lib/postgresql/data
@@ -142,8 +132,6 @@ VOLUME /var/lib/postgresql/data
#### 日志目录
如下代码块所示展示了相关示例
```docker
FROM nginx
VOLUME /var/log/nginx
@@ -151,8 +139,6 @@ VOLUME /var/log/nginx
#### 上传文件目录
如下代码块所示展示了相关示例
```docker
FROM myapp
VOLUME /app/uploads
@@ -235,8 +221,6 @@ $ docker run -v mysql_data:/var/lib/mysql mysql:8.0
#### 1. 定义必须持久化的路径
如下代码块所示展示了相关示例
```docker
## 数据库必须使用卷
@@ -246,8 +230,6 @@ VOLUME /var/lib/postgresql/data
#### 2. 不要在 VOLUME 后修改目录
如下代码块所示展示了相关示例
```docker
## ❌ 避免
@@ -262,8 +244,6 @@ VOLUME /app/data
#### 3. 文档中说明 VOLUME 用途
如下代码块所示展示了相关示例
```docker
## 持久化用户上传的文件

View File

@@ -1,9 +1,7 @@
## 7.9 EXPOSE 声明端口
## 7.9 EXPOSE 暴露端口
### 7.9.1 基本语法
如下代码块所示展示了相关示例
```docker
EXPOSE <端口> [<端口>/<协议>...]
```
@@ -14,8 +12,6 @@ EXPOSE <端口> [<端口>/<协议>...]
### 7.9.2 基本用法
如下代码块所示展示了相关示例
```docker
## 声明单个端口
@@ -88,8 +84,6 @@ flowchart TD
#### 没有 EXPOSE 也能 -p
如下代码块所示展示了相关示例
```docker
## 即使没有 EXPOSE也可以使用 -p
@@ -111,8 +105,6 @@ $ docker run -p 8080:80 mynginx
#### 误解EXPOSE 会打开端口
如下代码块所示展示了相关示例
```docker
## ❌ 错误理解:这不会让容器可从外部访问
@@ -129,8 +121,6 @@ EXPOSE 只是元数据声明。容器是否实际监听该端口,取决于容
#### 正确理解
如下代码块所示展示了相关示例
```docker
## Dockerfile
@@ -150,8 +140,6 @@ $ docker run -p 8080:80 nginx # 2. 映射:宿主机 8080 → 容器 80
#### 1. 总是声明应用使用的端口
如下代码块所示展示了相关示例
```docker
## Web 服务
@@ -171,8 +159,6 @@ EXPOSE 6379
#### 2. 使用明确的协议
如下代码块所示展示了相关示例
```docker
## 默认是 TCP
@@ -189,8 +175,6 @@ EXPOSE 53/tcp 53/udp
#### 3. 与应用实际端口保持一致
如下代码块所示展示了相关示例
```docker
## ✅ 好EXPOSE 与应用端口一致
@@ -208,8 +192,6 @@ CMD ["node", "server.js"] # 实际监听 3000
### 7.9.7 使用环境变量
如下代码块所示展示了相关示例
```docker
ARG PORT=80
EXPOSE $PORT

View File

@@ -1,4 +1,4 @@
## 7.19 本章小结
## 本章小结
| 要点 | 说明 |
|------|------|
@@ -12,7 +12,7 @@
- [COPY 复制文件](7.2_copy.md)文件复制
- [RUN 执行命令](../04_image/4.5_build.md)执行构建命令
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 编写指南
- [最佳实践](../appendix/best_practices.md)Dockerfile 编写指南
| 要点 | 说明 |
|------|------|
@@ -26,7 +26,7 @@
- [安全](../18_security/README.md)容器安全实践
- [ENTRYPOINT](7.5_entrypoint.md)入口脚本中的用户切换
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 安全
- [最佳实践](../appendix/best_practices.md)Dockerfile 安全
| 要点 | 说明 |
|------|------|
@@ -40,7 +40,7 @@
- [CMD 容器启动命令](7.4_cmd.md)启动主进程
- [Compose 模板文件](../11_compose/11.5_compose_file.md)Compose 中的健康检查
- [Docker 调试](../appendix/20.2_debug.md)容器排障
- [Docker 调试](../appendix/debug.md)容器排障
| 要点 | 说明 |
|------|------|
@@ -53,7 +53,7 @@
### 7.19.4 延伸阅读
- [COPY 指令](7.2_copy.md)文件复制
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)基础镜像设计
- [Dockerfile 最佳实践](../appendix/best_practices.md)基础镜像设计
| 要点 | 说明 |
|------|------|
@@ -66,7 +66,7 @@
### 7.19.5 延伸阅读
- [OCI 标签规范](https://github.com/opencontainers/image-spec/blob/main/annotations.md)
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)
- [Dockerfile 最佳实践](../appendix/best_practices.md)
| 要点 | 说明 |
|------|------|
@@ -79,7 +79,7 @@
### 7.19.6 延伸阅读
- [RUN 指令](../04_image/4.5_build.md)执行命令
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)错误处理与调试
- [Dockerfile 最佳实践](../appendix/best_practices.md)错误处理与调试
| 要点 | 说明 |
|------|------|
@@ -93,7 +93,7 @@
- [CMD 容器启动命令](7.4_cmd.md)容器启动时的命令
- [WORKDIR 指定工作目录](7.10_workdir.md)改变目录
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)
- [Dockerfile 最佳实践](../appendix/best_practices.md)
| 操作 | 示例 |
|------|------|
@@ -108,7 +108,7 @@
- [ADD 指令](7.3_add.md)复制和解压
- [WORKDIR 指令](7.10_workdir.md)设置工作目录
- [多阶段构建](7.17_multistage_builds.md)优化镜像大小
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 编写指南
- [最佳实践](../appendix/best_practices.md)Dockerfile 编写指南
| 场景 | 推荐指令 |
|------|---------|
@@ -122,7 +122,7 @@
- [COPY 复制文件](7.2_copy.md)基本复制操作
- [多阶段构建](7.17_multistage_builds.md)减少镜像体积
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 编写指南
- [最佳实践](../appendix/best_practices.md)Dockerfile 编写指南
| 要点 | 说明 |
|------|------|
@@ -136,7 +136,7 @@
- [ENTRYPOINT 入口点](7.5_entrypoint.md)固定的启动命令
- [后台运行](../05_container/5.2_daemon.md)容器前台/后台概念
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 编写指南
- [最佳实践](../appendix/best_practices.md)Dockerfile 编写指南
| ENTRYPOINT | CMD | 适用场景 |
|------------|-----|---------|
@@ -147,7 +147,7 @@
### 7.19.11 延伸阅读
- [CMD 容器启动命令](7.4_cmd.md)默认命令
- [最佳实践](../appendix/20.1_best_practices.md)启动命令设计
- [最佳实践](../appendix/best_practices.md)启动命令设计
- [后台运行](../05_container/5.2_daemon.md)前台/后台概念
| 要点 | 说明 |
@@ -162,7 +162,7 @@
- [ARG 构建参数](7.7_arg.md)构建时变量
- [Compose 环境变量](../11_compose/11.5_compose_file.md)Compose 中的环境变量
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 编写指南
- [最佳实践](../appendix/best_practices.md)Dockerfile 编写指南
| 要点 | 说明 |
|------|------|

View File

@@ -1,6 +1,6 @@
## 8.2 数据卷
## 8.1 数据卷
### 8.2.1 为什么需要数据卷
### 8.1.1 为什么需要数据卷
容器的存储层有一个关键问题**容器删除后数据就没了**
@@ -15,7 +15,7 @@ flowchart LR
---
### 8.2.2 数据卷的特性
### 8.1.2 数据卷的特性
| 特性 | 说明 |
|------|------|
@@ -27,12 +27,10 @@ flowchart LR
---
### 8.2.3 数据卷 vs 容器存储层
### 8.1.3 数据卷 vs 容器存储层
#### 容器存储层 (不推荐存储重要数据)
如下代码块所示展示了相关示例
```mermaid
graph TD
subgraph Container [容器]
@@ -47,8 +45,6 @@ graph TD
#### 数据卷 (推荐)
如下代码块所示展示了相关示例
```mermaid
graph TD
subgraph Container [容器]
@@ -65,7 +61,7 @@ graph TD
---
### 8.2.4 数据卷基本操作
### 8.1.4 数据卷基本操作
#### 创建数据卷
@@ -107,7 +103,7 @@ $ docker volume inspect my-vol
---
### 8.2.5 挂载数据卷
### 8.1.5 挂载数据卷
#### 方式一--mount (推荐)
@@ -163,7 +159,7 @@ $ docker run -d \
---
### 8.2.6 使用场景示例
### 8.1.6 使用场景示例
#### 场景一数据库持久化
@@ -227,7 +223,7 @@ $ docker run -d \
---
### 8.2.7 数据卷管理
### 8.1.7 数据卷管理
#### 删除数据卷
@@ -261,7 +257,7 @@ $ docker volume prune -f
---
### 8.2.8 数据卷备份与恢复
### 8.1.8 数据卷备份与恢复
#### 备份数据卷
@@ -316,7 +312,7 @@ echo "Backed up ${VOLUME_NAME} to ${BACKUP_DIR}/${VOLUME_NAME}_${TIMESTAMP}.tar.
---
### 8.2.9 数据卷 vs 绑定挂载
### 8.1.9 数据卷 vs 绑定挂载
Docker 有两种主要的数据持久化方式
@@ -342,7 +338,7 @@ $ docker run -v /host/path:/app/data nginx
---
### 8.2.10 常见问题
### 8.1.10 常见问题
#### Q如何知道容器使用了哪些数据卷

View File

@@ -1,6 +1,6 @@
## 8.3 挂载主机目录
## 8.2 挂载主机目录
### 8.3.1 什么是绑定挂载
### 8.2.1 什么是绑定挂载
Bind Mount (绑定挂载) **宿主机的目录或文件** 直接挂载到容器中容器可以读写宿主机的文件系统
@@ -21,7 +21,7 @@ flowchart LR
---
### 8.3.2 Bind Mount vs Volume
### 8.2.2 Bind Mount vs Volume
| 特性 | Bind Mount | Volume |
|------|------------|--------|
@@ -45,7 +45,7 @@ flowchart LR
---
### 8.3.3 基本语法
### 8.2.3 基本语法
#### 使用 --mount (推荐)
@@ -73,7 +73,7 @@ $ docker run -d \
---
### 8.3.4 使用场景
### 8.2.4 使用场景
#### 场景一开发环境代码同步
@@ -125,7 +125,7 @@ $ docker run --rm -it \
---
### 8.3.5 只读挂载
### 8.2.5 只读挂载
防止容器修改宿主机文件
@@ -152,7 +152,7 @@ touch: /app/config/new.txt: Read-only file system
---
### 8.3.6 挂载单个文件
### 8.2.6 挂载单个文件
```bash
## 挂载 bash 历史记录
@@ -172,7 +172,7 @@ $ docker run -d \
---
### 8.3.7 查看挂载信息
### 8.2.7 查看挂载信息
```bash
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
@@ -203,7 +203,7 @@ $ docker inspect mycontainer --format '{{json .Mounts}}' | jq
---
### 8.3.8 常见问题
### 8.2.8 常见问题
#### Q路径不存在报错
@@ -251,7 +251,7 @@ $ docker run -v /host/path:/container/path:cached myapp
---
### 8.3.9 最佳实践
### 8.2.9 最佳实践
#### 1. 开发环境使用 Bind Mount

View File

@@ -1,16 +1,14 @@
## 8.4 tmpfs 挂载
## 8.3 tmpfs 挂载
`tmpfs` 挂载会把数据放在宿主机内存中而不是写入容器可写层或数据卷
### 8.4.1 适用场景
### 8.3.1 适用场景
- 临时缓存
- 会话数据
- 不希望落盘的敏感中间文件
### 8.4.2 基本用法
如下代码块所示展示了相关示例
### 8.3.2 基本用法
```bash
$ docker run --tmpfs /run:rw,noexec,nosuid,size=64m nginx
@@ -22,13 +20,13 @@ $ docker run --tmpfs /run:rw,noexec,nosuid,size=64m nginx
$ docker run --mount type=tmpfs,destination=/run,tmpfs-size=67108864 nginx
```
### 8.4.3 注意事项
### 8.3.3 注意事项
- 容器停止后`tmpfs` 数据会丢失
- `tmpfs` 占用宿主机内存建议显式限制大小
- 不适合需要持久化的数据
### 8.4.4 Volume / Bind Mount 对比
### 8.3.4 Volume / Bind Mount 对比
| 类型 | 数据位置 | 持久化 | 典型用途 |
|------|---------|-------|---------|

View File

@@ -1,4 +1,4 @@
## 8.5 本章小结
## 本章小结
| 要点 | 说明 |
|------|------|
@@ -8,7 +8,7 @@
| **适用场景** | 开发环境配置文件日志 |
| **vs Volume** | Bind 更灵活Volume 更适合生产 |
### 8.5.1 延伸阅读
### 8.4.1 延伸阅读
- [数据卷](8.1_volume.md)Docker 管理的持久化存储
- [tmpfs 挂载](8.3_tmpfs.md)内存临时存储
@@ -23,7 +23,7 @@
| 清理未用 | `docker volume prune` |
| 挂载数据卷 | `-v name:/path` `--mount source=name,target=/path` |
### 8.5.2 延伸阅读
### 8.4.2 延伸阅读
- [绑定挂载](8.2_bind-mounts.md)挂载宿主机目录
- [tmpfs 挂载](8.3_tmpfs.md)内存中的临时存储

View File

@@ -1,6 +1,8 @@
## 9.2 配置 DNS
## 9.1 配置 DNS
Docker 容器的 DNS 配置决定了容器如何解析域名理解 DNS 机制对于容器网络的正确配置至关重要
Docker 1.10.0 以后内建了一个 DNS 服务器使得容器可以直接通过容器名称通信方法很简单只要在创建容器时使用 `--name` 为容器命名即可
但是使用 Docker DNS 有个前提条件就是它只能在 **自定义网络** 中使用也就是说如果使用的是默认的 `bridge` 网络是无法使用 DNS 所以我们就需要自定义网络
### 9.2.1 容器的 DNS 机制
@@ -13,7 +15,7 @@ Docker 容器的 DNS 配置有两种情况:
### 9.2.2 嵌入式 DNS
这是 Docker 网络最强大的功能之一在自定义网络中容器可以通过 "名字" 找到彼此而不需要知道对方的 IP (因为 IP 可能会变)
这是 Docker 网络最强大的功能之一在自定义网络中容器可以通过 名字 找到彼此而不需要知道对方的 IP (因为 IP 可能会变)
```bash
## 1. 创建自定义网络

View File

@@ -1,4 +1,4 @@
## 9.3 Docker 网络类型
## 9.2 网络类型
Docker 提供了多种网络驱动来满足不同的使用场景安装 Docker 系统会自动创建三个默认网络
@@ -10,7 +10,7 @@ def456... host host local
ghi789... none null local
```
### 9.3.1 网络类型对比
### 9.2.1 网络类型对比
各网络类型的特点和适用场景如下
@@ -22,7 +22,7 @@ ghi789... none null local
| **overlay** | 跨主机网络 | Docker Swarm 集群 |
| **macvlan** | 容器拥有独立 MAC 地址 | 需要直接接入物理网络 |
### 9.3.2 Bridge 网络 (默认)
### 9.2.2 Bridge 网络 (默认)
Bridge Docker 默认使用的网络模式Docker 启动时会自动创建 `docker0` 虚拟网桥所有未指定网络的容器都会连接到这个网桥上
@@ -35,7 +35,7 @@ Bridge 是 Docker 默认使用的网络模式。Docker 启动时会自动创建
| **容器 eth0** | 容器内的网卡 |
| **IP 地址** | 自动从 172.17.0.0/16 网段分配 |
### 9.3.3 Host 网络
### 9.2.3 Host 网络
使用 `--network host` 参数启动的容器会直接使用宿主机的网络栈不再拥有独立的网络命名空间容器内的端口就是宿主机的端口无需端口映射
@@ -45,7 +45,7 @@ $ docker run -d --network host nginx
这种模式下网络性能最高但容器之间和宿主机之间没有网络隔离
### 9.3.4 None 网络
### 9.2.4 None 网络
使用 `--network none` 参数启动的容器只有 `lo` 回环网卡完全没有外部网络连接适用于只需要运行计算任务不需要网络的容器
@@ -55,7 +55,7 @@ $ docker run -it --network none alpine ip addr
inet 127.0.0.1/8 scope host lo
```
### 9.3.5 数据流向
### 9.2.5 数据流向
容器网络中的数据流向可以分为以下几种情况

View File

@@ -1,4 +1,4 @@
## 9.4 用户自定义网络 (推荐)
## 9.3 自定义网络
在生产环境中推荐使用用户自定义网络代替默认的 bridge 网络自定义网络提供了更好的隔离性和服务发现能力

View File

@@ -1,4 +1,4 @@
## 9.5 容器互联
## 9.4 容器互联
容器之间的网络通信是 Docker 网络的核心功能之一本节介绍容器互联的几种方式

View File

@@ -1,4 +1,4 @@
## 9.6 外部访问容器
## 9.5 外部访问容器
容器运行在自己的隔离网络环境中 (通常是 Bridge 模式)为了让外部网络访问容器内的服务我们需要将容器的端口映射到宿主机的端口

View File

@@ -1,4 +1,4 @@
## 9.7 网络隔离
## 9.6 网络隔离
Docker 网络提供了天然的隔离能力不同网络之间的容器默认无法通信这是 Docker 网络安全的重要基础

View File

@@ -1,4 +1,4 @@
## 9.8 本章小结
## 本章小结
本章介绍了 Docker 网络配置的各个方面

View File

@@ -1,4 +1,4 @@
## 10.1 使用 `BuildKit` 构建镜像
## 10.1 BuildKit
**BuildKit** 是下一代的镜像构建组件 https://github.com/moby/buildkit 开源。

View File

@@ -1,4 +1,4 @@
## 10.2 使用 Buildx 构建镜像
## 10.2 使用 buildx 构建镜像
### 10.2.1 使用

View File

@@ -1,4 +1,4 @@
## 10.3 构建多种系统架构支持的 Docker 镜像
## 10.3 使用 buildx 构建多种系统架构支持的 Docker 镜像
Docker 镜像可以支持多种系统架构这意味着你可以在 `x86_64``arm64` 等不同架构的机器上运行同一个镜像这是通过一个名为 manifest list (或称为 fat manifest) 的文件来实现的

View File

@@ -1,4 +1,4 @@
## 10.4 本章小结
## 本章小结
Docker Buildx Docker 构建系统的重要进化提供了高效安全且支持多平台的镜像构建能力
@@ -16,4 +16,4 @@ Docker Buildx 是 Docker 构建系统的重要进化,提供了高效、安全
- [Dockerfile 指令详解](../07_dockerfile/README.md)Dockerfile 编写基础
- [多阶段构建](../07_dockerfile/7.17_multistage_builds.md)优化镜像体积
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)编写高效 Dockerfile
- [Dockerfile 最佳实践](../appendix/best_practices.md)编写高效 Dockerfile

View File

@@ -1,4 +1,4 @@
## 11.1 Compose 简介
## 11.1 简介
`Compose` 项目是 Docker 官方的开源项目负责实现对 Docker 容器集群的快速编排从功能上看 `OpenStack` 中的 `Heat` 十分类似

View File

@@ -1,4 +1,4 @@
## 11.4 Compose 命令说明
## 11.4 命令说明
Docker Compose 提供了丰富的命令来管理项目和容器本节将详细介绍这些命令的使用格式和常用选项

View File

@@ -1,4 +1,4 @@
## 11.6 使用 Django
## 11.6 实战 Django
> 本小节内容适合 `Python` 开发人员阅读
@@ -52,8 +52,6 @@ $ mkdir django-docker && cd django-docker
### 11.6.3 步骤 1创建 Dockerfile
如下代码块所示展示了相关示例
```docker
FROM python:3.12-slim
@@ -92,8 +90,6 @@ COPY . /code/
### 11.6.4 步骤 2创建 requirements.txt
如下代码块所示展示了相关示例
```txt
Django>=5.0,<6.0
psycopg[binary]>=3.1,<4.0
@@ -364,5 +360,5 @@ services:
- [Compose 模板文件详解](11.5_compose_file.md)深入理解 Compose 文件的所有配置项
- [使用 WordPress](11.8_wordpress.md)另一个 Compose 实战案例
- [Dockerfile 最佳实践](../appendix/20.1_best_practices.md)构建更小更安全的镜像
- [Dockerfile 最佳实践](../appendix/best_practices.md)构建更小更安全的镜像
- [数据管理](../08_data/README.md)Volume 和数据持久化详解

View File

@@ -1,4 +1,4 @@
## 11.7 使用 Rails
## 11.7 实战 Rails
> 本小节内容适合 Ruby 开发人员阅读
@@ -45,8 +45,6 @@ $ mkdir rails-docker && cd rails-docker
### 11.7.3 步骤 1创建 Dockerfile
如下代码块所示展示了相关示例
```docker
FROM ruby:3.2

View File

@@ -6,8 +6,6 @@ WordPress 是全球最流行的内容管理系统 (CMS)。使用 Docker Compose
### 11.8.1 项目结构
如下代码块所示展示了相关示例
```bash
wordpress/
├── compose.yaml

View File

@@ -1,3 +1,3 @@
## 11.9 使用 Compose 搭建 LNMP 环境
## 11.9 实战 LNMP
本项目的维护者 [khs1994](https://github.com/khs1994) 的开源项目 [khs1994-docker/lnmp](https://github.com/khs1994-docker/lnmp) 使用 Docker Compose 搭建了一套 LNMP 环境,各位开发者可以参考该项目在 Docker 或 Kubernetes 中运行 LNMP。

View File

@@ -1,4 +1,4 @@
## 11.10 本章小结
## 本章小结
Docker Compose 是管理多容器应用的利器通过 YAML 文件声明式地定义服务网络和数据卷

View File

@@ -1,6 +1,6 @@
# 12.2 命名空间Namespace
## 12.2 命名空间
命名空间Namespace Linux 内核一个强大特性为容器提供了隔离的运行环境
命名空间是 Linux 内核一个强大特性每个容器都有自己单独的命名空间运行在其中的应用都像是在独立的操作系统中运行一样命名空间保证了容器之间彼此互不影响
## 12.2 什么是 Namespace
@@ -85,8 +85,6 @@ NET Namespace 负责网络栈的隔离,包括网卡、路由表和 iptables
#### NET 隔离效果
如下代码块所示展示了相关示例
```mermaid
flowchart LR
subgraph Host ["宿主机"]
@@ -122,8 +120,6 @@ MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的
#### MNT 隔离效果
如下代码块所示展示了相关示例
```bash
宿主机文件系统: 容器内看到的:
/ / ← 容器的根目录
@@ -204,8 +200,6 @@ USER Namespace 允许将容器内的用户 ID 映射到宿主机的不同用户
#### USER 隔离效果
如下代码块所示展示了相关示例
```mermaid
flowchart LR
subgraph Container ["容器内"]

View File

@@ -30,8 +30,6 @@ Docker 选择联合文件系统作为其存储驱动,主要基于以下几个
#### 1. 镜像分层复用
如下代码块所示展示了相关示例
```mermaid
flowchart TD
Nginx["nginx:alpine"] --> Alpine["alpine:3.19 (共享基础层)"]
@@ -192,8 +190,6 @@ $ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
#### 1. 减少镜像层数
如下代码块所示展示了相关示例
```docker
## ❌ 每条命令创建一层

View File

@@ -1,4 +1,4 @@
## 12.6 Docker 网络实现
## 12.6 网络
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备 (特别是 veth pair)建议先熟悉了解这两部分的基本概念再阅读本章

View File

@@ -1,4 +1,4 @@
## 12.7 本章小结
## 本章小结
| Namespace | 隔离内容 | 一句话说明 |
|-----------|---------|-----------|

View File

@@ -1,10 +1,10 @@
## 13.1 Kubernetes 简介
## 13.1 简介
如图 12-1 所示Kubernetes 使用舵手图标作为项目标识
如图 13-1 所示Kubernetes 使用舵手图标作为项目标识
![Kubernetes 标识](./_images/kubernetes_logo.png)
12-1 Kubernetes 项目标识
13-1 Kubernetes 项目标识
### 13.1.1 什么是 Kubernetes

View File

@@ -1,4 +1,4 @@
## 13.4 Kubernetes 高级特性
## 13.4 高级特性
掌握了 Kubernetes 的核心概念 (PodServiceDeployment) 我们需要了解更多高级特性以构建生产级应用

View File

@@ -1,4 +1,4 @@
## 13.5 Kubernetes 实战练习
## 13.5 实战练习
本章将通过一个具体的案例部署一个 Nginx 网站并为其配置 Service Ingress来串联前面学到的知识

View File

@@ -1,4 +1,4 @@
## 13.6 本章小结
## 本章小结
Kubernetes 是当前最主流的容器编排平台其声明式管理模型和丰富的 API 为大规模容器化应用提供了坚实的基础

View File

@@ -1,4 +1,4 @@
## 14.3 Docker Desktop Kubernetes
## 14.3 Docker Desktop 使
使用 Docker Desktop 可以很方便的启用 Kubernetes

View File

@@ -1,4 +1,4 @@
## 14.7 Kubernetes Dashboard
## 14.7 部署 Dashboard
[Kubernetes Dashboard](https://github.com/kubernetes/dashboard) 是基于网页的 Kubernetes 用户界面。

View File

@@ -1,4 +1,4 @@
# kubectl 使用
## 14.8 Kubernetes 命令行 kubectl
[kubectl](https://github.com/kubernetes/kubernetes) 是 Kubernetes 自带的客户端,可以用它来直接操作 Kubernetes。

View File

@@ -1,4 +1,4 @@
## 14.9 本章小结
## 本章小结
部署 Kubernetes 集群有多种方式应根据使用场景选择合适的方案

View File

@@ -1,4 +1,4 @@
## 15.3 etcd 集群
## 15.3 集群
下面我们使用 [Docker Compose](../11_compose/README.md) 模拟启动一个 3 节点的 `etcd` 集群

View File

@@ -1,4 +1,4 @@
## 15.5 本章小结
## 本章小结
etcd Kubernetes 的核心存储组件为分布式系统提供可靠的键值存储和服务发现能力

View File

@@ -1,4 +1,4 @@
## 16.5 多云部署策略比较
## 16.5 多云部署策略
企业在选择容器云平台时通常会在 AWS EKSAzure AKSGoogle GKE 以及国内的阿里云 ACK腾讯云 TKE 之间进行权衡

View File

@@ -1,4 +1,4 @@
## 16.6 本章小结
## 本章小结
本章介绍了公有云服务对 Docker 的积极支持以及新出现的容器云平台

View File

@@ -1,4 +1,4 @@
## 17.1 Fedora CoreOS
## 17.1 Fedora CoreOS
[Fedora CoreOS](https://getfedora.org/coreos/) 是一个自动更新的,最小的,整体的,以容器为中心的操作系统,不仅适用于集群,而且可独立运行,并针对运行 Kubernetes 进行了优化。它旨在结合 CoreOS Container Linux 和 Fedora Atomic Host 的优点,将 Container Linux 中的 [Ignition](https://github.com/coreos/ignition) 与 [rpm-ostree](https://github.com/coreos/rpm-ostree) 和 Project Atomic 中的 SELinux 强化等技术相集成。其目标是提供最佳的容器主机,以安全,大规模地运行容器化的工作负载。

View File

@@ -1,4 +1,4 @@
## 17.2 安装 Fedora CoreOS
## 17.2 Fedora CoreOS 安装
### 17.2.1 下载 ISO

View File

@@ -1,4 +1,4 @@
# podman
## 17.3 podman - 下一代 Linux 容器工具
[`podman`](https://github.com/containers/podman) 是一个无守护进程、与 Docker 命令高度兼容的下一代 Linux 容器工具。它由 Red Hat 开发,旨在提供一个更安全的容器运行环境。

View File

@@ -1,4 +1,4 @@
## 17.4 本章小结
## 本章小结
Docker 并非容器生态的唯一选择了解其他工具有助于根据场景做出合适的技术选型

View File

@@ -1,18 +1,85 @@
## 18.1 内核命名空间
命名空间 (Namespace) Linux 容器隔离的基础它确保了容器内的进程无法干扰主机或其他容器
命名空间 (Namespace) Linux 容器隔离的基础它确保了容器内的进程无法直接干扰主机或其他容器虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace但在本节中我们将重点探讨其**安全意义**及相关配置
Docker 容器和 LXC 容器很相似所提供的安全特性也差不多当用 `docker run` 启动一个容器时在后台 Docker 为容器创建了一个独立的命名空间和控制组集合
### 18.1.1 隔离的安全本质
命名空间提供了最基础也是最直接的隔离在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用
Docker 守护进程在启动容器时会在后台为容器创建一套独立的命名空间命名空间提供了最基础也是最直接的隔离
每个容器都有自己独有的网络栈意味着它们不能访问其他容器的 sockets 或接口不过如果主机系统上做了相应的设置容器可以像跟主机交互一样的和其他容器交互当指定公共端口或使用 links 来连接 2 个容器时容器就可以相互通信了 (可以根据配置来限制通信的策略)
- **PID Namespace**防止容器内的进程查看或终止宿主机或其他容器的进程恶意攻击者即使在容器内获得了 root 权限也无法通过 `kill` 命令影响宿主机上的关键服务
- **NET Namespace**每个容器都有自己独立的网络栈如果没有显式地进行端口映射或将容器连接到同一网络容器之间无法网络互通从而限制了横向移动的能力
- **MNT Namespace**为容器提供独立的文件系统视图这可以防止容器不经意或恶意地修改宿主机的重要系统文件 `/etc/passwd`
从网络架构的角度来看所有的容器通过本地主机的网桥接口相互通信就像物理机器通过物理交换机通信一样
### 18.1.2 命名空间不是绝对安全的护城河
那么内核中实现命名空间和私有网络的代码是否足够成熟
尽管命名空间提供了很好的隔离性但我们必须认识到**所有的容器依然共享同一个宿主机的 Linux 内核**
内核命名空间从 2.6.15 版本 (2006 1 月发布) 之后被引入数年间这些机制的可靠性在诸多大型生产系统中被实践验证
这意味着一旦宿主机的内核存在提权漏洞如著名的 Dirty COW 漏洞攻击者有可能通过突破 Namespace 的限制直接在内核层面执行恶意代码从而实现容器逃逸
实际上命名空间的想法和设计提出的时间要更早最初是为了在内核中引入一种机制来实现 [OpenVZ](https://en.wikipedia.org/wiki/OpenVZ) 的特性。
OpenVZ 项目早在 2005 年就发布了其设计和实现都已经十分成熟
> [!WARNING]
> 为了缓解内核漏洞带来的威胁生产环境务必保持宿主机 Linux 内核的及时修补与更新或者借助诸如 gVisorKata Containers 等提供了独立内核的安全容器技术
通过命名空间Docker 也能限制进程从外部环境获取信息
例如由于进程环境被隔离进程在内部其实是无法感知到外部宿主机的存在的它既不能获取其他容器的进程列表也无法通过网络与其他系统进行交互除非经过配置
### 18.1.1 用户命名空间 与提权防护
在所有的 Namespace **User Namespace** 对安全的影响尤为关键
在默认情况下容器内的 `root` 用户UID=0就是宿主机上的 `root` 用户如果攻击者设法突破了容器的其他隔离机制获取了宿主机的访问权限他将拥有宿主机的最高系统权限
通过启用 **User Namespace Remapping (用户命名空间映射)**我们可以将容器内的 `root` 用户映射到宿主机上的一个无特权普通用户
#### 如何配置 User Namespace
要在 Docker 服务端启用这一特性需要修改 Docker 的配置文件 `/etc/docker/daemon.json`
1. **设置映射策略**
编辑配置文件添加 `userns-remap` 配置项
```json
{
"userns-remap": "default"
}
```
使用 `default` 值时Docker 会自动在宿主机上创建一个名为 `dockremap` 的用户和用户组
2. **验证子 UID 和子 GID 分配**
Docker 会通过 `/etc/subuid` `/etc/subgid` 文件为 `dockremap` 分配一个高位的 UID 范围
```bash
$ cat /etc/subuid
dockremap:165536:65536
```
这意味着容器内的 UID `0`root 用户在宿主机上实际被映射成了 UID `165536`如果是容器内的 UID `1`对应宿主机的 `165537`以此类推
3. **重启 Docker 守护进程**
```bash
$ sudo systemctl restart docker
```
#### 验证映射效果
我们可以运行一个简单的容器并执行 `sleep` 命令同时在宿主机上观察进程的所有者
```bash
## 在容器内以 root 身份运行
$ docker run -d --name userns_test alpine sleep 3600
## 在宿主机上查看该 sleep 进程
$ ps aux | grep sleep
165536 12345 0.0 0.0 1568 4 ? Ss 14:20 0:00 sleep 3600
```
你会发现尽管在容器内该进程是由 root 启动的但在宿主机上它的属主是 `165536`一个完全没有特权的用户
> [!TIP]
> 启用 User Namespace 会对容器共享宿主机数据卷Bind Mount产生权限影响你需要确保映射后的高位 UID 对宿主机上的挂载目录具有合适的读写权限
### 18.1.4 总结
内核命名空间从 Linux 2.6.15 版本 (2006 ) 被引入十余年间这些机制的可靠性在诸多大型生产系统中被实践验证通过合理利用命名空间尤其是 User Namespace可以极大地收窄攻击面显著提升容器部署的安全性

View File

@@ -1,9 +1,102 @@
## 18.2 控制组
控制组是 Linux 容器机制的另外一个关键组件负责实现资源的审计和限制
控制组 (Cgroups) Linux 容器机制的另外一个关键组件如果说命名空间 (Namespace) 决定了容器能**看到**什么那么控制组就决定了容器能**使用**多少资源
它提供了很多有用的特性以及确保各个容器可以公平地分享主机的内存CPU磁盘 IO 等资源当然更重要的是控制组确保了当容器内的资源使用产生压力时不会连累主机系统
在安全领域中资源的不可用性本身就是一种安全威胁控制组负责实现资源的审计和限制这对于抵御资源耗尽型攻击如拒绝服务攻击 DoS至关重要
尽管控制组不负责隔离容器之间相互访问处理数据和进程它在防止拒绝服务 (DDOS) 攻击方面是必不可少的尤其是在多用户的平台 (比如公有或私有的 PaaS) 控制组十分重要例如当某些应用程序表现异常的时候可以保证一致地正常运行和性能
### 18.2.1 为什么资源限制关乎安全
控制组机制始于 2006 内核从 2.6.24 版本开始被引入
默认情况下Docker 容器对系统资源的使用是没有限制的一个容器理论上可以使用宿主机所有的 CPU 计算能力吃光所有的内存耗尽所有的系统 PID
想象一下以下场景
- 一个恶意用户向你暴露在公网的应用发起海量并发请求
- 应用程序逻辑中存在内存泄漏漏洞
- 黑客在入侵容器后在里面运行了挖矿木马程序
如果没有 Cgroups 的限制某个容器内的异常行为或恶意攻击将会榨干宿主机的资源导致宿主机上其他健康的容器甚至 Docker 守护进程自身因为 OOMOut Of Memory崩溃或 CPU 饥饿而停止响应
### 18.2.2 核心资源限制实战
为了确保多租户平台如公有或私有的 PaaS 平台的稳定性或者在生产环境防止服务级联故障我们要养成在启动容器时**显式声明资源上限**的习惯
#### 1. 内存限制
限制内存可以防止应用程序因内存泄漏或恶意载荷导致宿主机 OOM
**关键参数**
- `-m, --memory=""`硬限制容器可使用的最大内存量
- `--memory-swap=""`限制容器可使用的内存与 Swap 总量
**实战示例**
限制容器最多只能使用 512MB 内存并且禁用 Swap memory memory-swap 设置成一样的值即可
```bash
$ docker run -d \
--name web_app \
--memory="512m" \
--memory-swap="512m" \
nginx:alpine
```
如果该容器内的应用尝试分配超过 512MB 的内存该进程将会被内核的 OOM Killer 杀掉但绝不会波及到宿主机的其他部分
#### 2. CPU 限制
限制 CPU 可以防止个别计算密集型的容器垄断 CPU 时间片保证系统的调度公平性
**关键参数**
- `--cpus=<value>`指定容器可以使用的 CPU 核心数量可以是小数
- `-c, --cpu-shares=0`软限制设置容器使用 CPU 的相对权重默认是 1024
**实战示例**
限制容器最多使用 1.5 CPU 核心的算力
```bash
$ docker run -d \
--name worker_app \
--cpus="1.5" \
busybox \
md5sum /dev/urandom
```
即使上面的命令是一个死循环的哈希计算进程容器也永远无法吃满双核 CPU 系统的全部算力
#### 3. 进程数限制
进程炸弹Fork Bomb是一种典型的拒绝服务攻击方式它通过不断 `fork()` 新进程来耗尽系统的进程表条目导致系统无法创建任何新任务
**关键参数**
- `--pids-limit=<number>`限制容器内允许创建的最大进程数
**实战示例**
一个常规的 Web 服务进程数通常在几十到上百之间我们可以设定一个合理的上限来防范 Fork 炸弹
```bash
$ docker run -d \
--name app_service \
--pids-limit=100 \
python:alpine python app.py
```
当容器内的进程总数达到 100 任何尝试派生新进程的操作都会失败并返回 `Resource temporarily unavailable`从而挫败相关的攻击行为
### 18.2.3 最佳实践建议
在生产环境中不仅要在单机使用 Docker 命令时设置这些参数更应当在集群编排工具中将资源配额制度化
例如 Kubernetes 强烈建议为每个 Pod 设置 `requests` `limits`
```yaml
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
```
通过 Cgroups 的资源边界控制你可以从根本上切断一条导致整个系统雪崩的脆弱链路这也进一步使得 Docker 以及容器技术成为了现代高可用服务的基础设施首选

View File

@@ -1,20 +1,86 @@
## 18.3 Docker 服务端防护
## 18.3 服务端防护
运行一个容器或应用程序的核心是通过 Docker 服务端Docker 服务的运行目前需要 root 权限因此其安全性十分关键
Docker 守护进程`dockerd`是容器生命周期的核心驱动力默认情况下Docker 服务的运行需要极高的系统特权root 权限因此其安全性关系到整台宿主机的生死存亡
首先确保只有可信的用户才可以访问 Docker 服务Docker 允许用户在主机和容器间共享文件夹同时不需要限制容器的访问权限这就容易让容器突破资源限制例如恶意用户启动容器的时候将主机的根目录 `/` 映射到容器的 `/host` 目录中那么容器理论上就可以对主机的文件系统进行任意修改了这听起来很疯狂但是事实上几乎所有虚拟化系统都允许类似的资源共享而没法禁止用户共享主机根文件系统到虚拟机系统
如果 Docker 守护进程的访问控制没有做好恶意攻击者可以通过 Docker API 轻易地启动一个特权容器并将宿主机的根目录`/`挂载到容器中从而完全接管服务器
这将会造成很严重的安全后果因此当提供容器创建服务时 (例如通过一个 web 服务器)要更加注意进行参数的安全检查防止恶意的用户用特定参数来创建一些破坏性的容器
为了加强对服务端的保护我们需要从访问控制通信加密和权限最小化三个维度进行加固
为了加强对服务端的保护Docker REST API (客户端用来跟服务端通信) 0.5.2 之后使用本地的 Unix 套接字机制替代了原先绑定在 127.0.0.1 上的 TCP 套接字因为后者容易遭受跨站脚本攻击现在用户使用 Unix 权限检查来加强套接字的访问安全
### 18.3.1 限制 API 访问
用户仍可以利用 HTTP 提供 REST API 访问建议使用安全机制确保只有可信的网络或 VPN或证书保护机制 (例如受保护的 stunnel ssl 认证) 下的访问可以进行此外还可以使用 [HTTPS 和证书](https://docs.docker.com/engine/security/https/)来加强保护
Docker 客户端`docker` 命令通过 REST API 与守护进程进行通信
最近改进的 Linux 命名空间机制将可以实现使用非 root 用户来运行全功能的容器这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题
在早期版本中Docker 有时会绑定在 `127.0.0.1` TCP 套接字上但这容易遭遇跨站脚本跨协议攻击现在的发行版默认使用 Unix Domain Socket`/var/run/docker.sock`并依赖文件系统的权限控制
终极目标是改进 2 个重要的安全特性
#### 原则 1决不可将无认证的 TCP 端口暴露在公网
* 将容器的 root 用户[映射到本地主机上的非 root 用户](https://docs.docker.com/engine/security/userns-remap/),减轻容器和主机之间因权限提升而引起的安全问题;
* 允许 Docker 服务端在[ root 权限 (rootless 模式)](https://docs.docker.com/engine/security/rootless/) 下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程将只允许在限定范围内进行操作,例如仅仅负责虚拟网络设定或文件系统管理、配置操作等。
这是最常见的 Docker 被入侵抓去挖矿的原因绝不能在没有任何安全控制的情况下强行开启 `-H tcp://0.0.0.0:2375`
最后建议采用专用的服务器来运行 Docker 和相关的管理服务 (例如管理服务比如 ssh 监控和进程监控管理工具 nrpecollectd )其它的业务服务都放到容器中去运行
如果业务确实需要远程访问 Docker 守护进程**必须启用 TLS 认证机制**让客户端和服务端互相进行证书校验
#### 开启 TLS 认证双向加密
利用安全机制确保只有经过授权的主机网络并在强证书保护下进行通信
1. 首先使用 `openssl` 或基于本地 CA 工具生成一套客户端与服务器的证书
2. 配置 Docker 守护进程通常是 `daemon.json` `dockerd` 启动参数指定证书路径
```bash
dockerd \
--tlsverify \
--tlscacert=ca.pem \
--tlscert=server-cert.pem \
--tlskey=server-key.pem \
-H=0.0.0.0:2376
```
3. 客户端想要连接时也必须出示客户端证书
> [!TIP]
> 配置 TLS 生成证书的完整步骤可以查阅 [Docker 官方 TLS 文档](https://docs.docker.com/engine/security/protect-access/)。在现代编排系统(如 Kubernetes通常会有自动化方案管理这些凭据。
### 18.3.2 保护本地 Socket 访问
哪怕不开启网络端口本地的 `/var/run/docker.sock` 也需要谨慎对待
任何被授予该 Socket 读写权限的用户通常被加入 `docker` 用户组等同于拥有了对宿主机的零成本提权途径无需密码的免密 `sudo` 权限
> [!CAUTION]
> 永远不要将不可信的普通用户加入到 `docker` 用户组中同样在容器编排时尽量避免将宿主机的 `/var/run/docker.sock` 直接映射给普通容器使用这种模式被称为 Docker-in-Docker (DinD) Docker-out-of-Docker (DooD)存在极高的越权风险
### 18.3.3 Rootless 模式 (非特权运行)
为了从根本上解决拥有 Docker socket 就是 root的问题Docker 在近年推出了 **Rootless 模式**
Rootless 模式允许在完全局限于非 `root` 用户的环境中运行 Docker 守护进程`dockerd`和容器该模式利用了现代 Linux 内核的 User Namespace 技术和非特权网络命名空间实现
#### 配置运行 Rootless Docker
要在非 root 环境中运行 Docker只需要简单几步
1. 安装必要的依赖通常是 `uidmap` 工具包以便系统支持 `newuidmap` `newgidmap`
```bash
$ sudo apt-get install uidmap
```
2. 切换到一个没有任何 `sudo` 权限的普通用户假设用户名为 `testuser`
```bash
$ su - testuser
```
3. 运行 Docker 官方提供的 Rootless 安装脚本
```bash
$ curl -fsSL https://get.docker.com/rootless | sh
```
4. 配置环境变量指向新创建的私有 socket
```bash
$ export DOCKER_HOST=unix:///run/user/1000/docker.sock
$ docker version
```
安装并暴露相应的配置后该用户的环境将能独立启动属于他自己的 Docker Daemon即使由于某些未知 0-Day 漏洞使得攻击者突破了容器他们也只会受限于 `testuser` 这个非特权用户所在的有限系统环境内
### 18.3.4 结语
保障 Docker 服务端的安全主要是做减法关闭不必要的网络监听点严管 Socket 访问权限而一旦基础系统条件允许**毫不犹豫地在生产环境启用 Rootless 模式**将是一项划算的安全加固选择

View File

@@ -1,30 +1,63 @@
## 18.4 内核能力机制
Docker 利用 Linux 能力机制 (Capabilities) 来限制容器的权限从而提高系统的安全性
传统 Linux 权限模型非常粗放进程分为特权进程 root 用户 `UID 0` 运行非特权进程其他 UID 运行这带来了一个致命问题只要一个后台服务需要一个微小的特权例如绑定低于 1024 的端口就必须被赋予所有的 root 权限一旦该服务被攻陷系统便会全面沦陷
[能力机制 (Capability)](https://man7.org/linux/man-pages/man7/capabilities.7.html) 是 Linux 内核一个强大的特性,可以提供细粒度的权限访问控制
Linux 内核自 2.2 版本起就支持能力机制它将权限划分为更加细粒度的操作能力既可以作用在进程上也可以作用在文件上
为了解决这一问题Linux 引入了 **能力机制Capabilities**它将传统的全能 root 权限划分为几十个细粒度的操作能力
例如一个 Web 服务进程只需要绑定一个低于 1024 的端口的权限并不需要 root 权限那么它只需要被授权 `net_bind_service` 能力即可此外还有很多其他的类似能力来避免进程获取 root 权限
### 18.4.1 容器内置的 Capability 白名单
默认情况下Docker 启动的容器被严格限制只允许使用内核的一部分能力
默认情况下即便一个容器是在以 `root` 用户运行Docker 也只为其内核授予了所有可用能力中的**一小部分白名单能力**
使用能力机制对加强 Docker 容器的安全有很多好处通常在服务器上会运行一堆需要特权权限的进程包括有 sshcronsyslogd硬件管理工具模块 (例如负载模块)网络配置工具等等容器跟这些进程是不同的因为几乎所有的特权进程都由容器以外的支持系统来进行管理
常见的 Linux Capabilities 包含
- `CAP_CHOWN`: 修改文件所有者
- `CAP_NET_BIND_SERVICE`: 绑定特权端口 1024 以下的端口
- `CAP_NET_ADMIN`: 网络管理的最高权限例如调整路由配置设置防火墙规则等
- `CAP_SYS_ADMIN`: 被誉为Linux 内核的特权网管允许各种高危操作挂载磁盘访问敏感设备等
* ssh 访问被主机上 ssh 服务来管理
* cron 通常应该作为用户进程执行权限交给使用它服务的应用来处理
* 日志系统可由 Docker 或第三方服务管理
* 硬件管理无关紧要容器中也就无需执行 udevd 以及类似服务
* 网络管理也都在主机上设置除非特殊需求容器不需要对网络进行配置
为了在**最小特权原则**的指导下加强安全Docker 默认**移除了**大量可能导致容器大范围破坏宿主机的能力例如
* 完全禁止了任何通过 `CAP_SYS_ADMIN` 进行的核心挂载或设备操作
* 禁止修改内核模块
* 禁止直接访问硬件套接字
从上面的例子可以看出大部分情况下容器并不需要 真正的 root 权限容器只需要少数的能力即可为了加强安全容器可以禁用一些没必要的权限
这种非完整 root 用户能保证大部分应用在拥有其所需权限的同时把恶意行为对系统的影响降到最低
* 完全禁止任何 mount 操作
* 禁止直接访问本地主机的套接字
* 禁止访问一些文件系统的操作比如创建新的设备修改文件属性等
* 禁止模块加载
### 18.4.2 实战添加与剥夺能力
这样就算攻击者在容器中取得了 root 权限也不能获得本地主机的较高权限能进行的破坏也有限
当启动一个 Docker 容器时我们可以利用 `--cap-add`增加特权 `--cap-drop`剥夺特权两个参数精细地控制进程环境
默认情况下Docker 采用[白名单](https://github.com/moby/moby/blob/master/oci/caps/defaults.go)机制,禁用必需功能之外的其它权限。
当然用户也可以根据自身需求来为 Docker 容器启用额外的权限
#### 实战场景一构建极限安全的 Web 靶机
假设你正在提供一个公共的 Web 容器你不希望里面的任何恶意脚本修改进程权限或者创建设备节点你可以通过命令先移除**所有**默认能力然后再按需授权该守护进程一个仅仅能绑端口的能力
```bash
$ docker run -d \
--name max_secure_web \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
nginx:alpine
```
这里的 `--cap-drop ALL` 是实现特权最小化的最强杀手锏此时即便某黑客利用 0-Day 手段拿到了 Web 服务的容器 root Shell当他试图改变任何不属于他自己的进程配置或者所有权时系统都会报错拒绝访问
#### 实战场景二需要捕获网络数据包的网络实验
假设容器内的主程序是一个网络嗅探器 tshark tcpdump这显然不在 Docker 提供的默认白名单之内因为该程序试图直接操纵底层网卡流量会触发 Permission Denied
此时我们需要给它适当补发缺失的部分核心管理能力
```bash
$ docker run -it --rm \
--name network_sniffer \
--cap-add NET_ADMIN \
--cap-add NET_RAW \
tshark-image /bin/bash
```
我们只授予了所需的网络管理控制NET_ADMIN和侦听底层套接字的权限NET_RAW而免去了赋予整个容器终极杀器 `--privileged` 参数
> [!WARNING]
> 大量开发人员遇到了权限遭到拒绝的错误时往往习惯性图省事添加 `--privileged` 这个核选项但这将把**宿主机上一切特权和所有访问设备完全投射给容器内的根用户**其危险性等价于根本没有做隔离请务必查明进程出错的实际原因精准施加必要的隔离 `CAP_*` 能力
### 18.4.3 总结
利用能力机制Capabilities是进行精细化系统级访问控制的关键一环遵循**白名单剥夺一切不必要权利--cap-drop ALL**的极端配置并不过分这将使得即便程序本身漏洞百出攻击面也被死死压缩在一个几乎毫无后续伸展潜力的受限维度中

View File

@@ -1,13 +1,88 @@
## 18.5 其它安全特性
除了上述机制Linux 内核还提供了一系列安全增强功能可以进一步保护容器环境
除了上述的命名空间控制组以及能力机制Linux 内核与云原生生态还提供了大量安全增强功能它们共同筑成了一道防御纵深的马奇诺防线本节主要介绍强制访问控制系统调用拦截以及自动化的容器漏洞扫描技术
除了能力机制之外还可以利用一些现有的安全机制来增强使用 Docker 的安全性例如 TOMOYOAppArmorSeccompSELinuxGRSEC
### 18.5.1 系统调用过滤
Docker 当前默认只启用了能力机制用户可以采用多种方案来加强 Docker 主机的安全例如
`Seccomp`Secure Computing mode Linux 内核的一个安全机制用于限制进程能够发起的系统调用数量
* 在内核中启用 GRSEC PAX这将增加很多编译和运行时的安全检查通过地址随机化避免恶意探测等并且启用该特性不需要 Docker 进行任何配置
* 使用一些有增强安全特性的容器模板比如带 AppArmor 的模板和 Redhat SELinux 策略的模板这些模板提供了额外的安全特性
* 用户可以自定义访问控制机制来定制安全策略
一个普通的 Linux 内核提供了 300 多个系统调用而一个正常运行的容器化应用例如 Nginx 服务通常只会用到几十个调用这就给攻击者留下了大量的闲置入口点来进行内核层的缓冲区溢出攻击
跟其它添加到 Docker 容器的第三方工具一样 (比如网络拓扑和文件系统共享)有很多类似的机制在不改变 Docker 内核情况下就可以加固现有的容器
Docker 默认启用了 Seccomp 并利用预置的 [默认配置文件](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json) 将可以利用的系统调用缩减到了不足一半(默认禁用了 44 个危险的统调用,比如修改时区或重启系统)
如果你对应用的系统调用特征了如指掌你可以为容器定制专属规则
#### 实战禁用 chmod 系统调用过滤
首先编写一个 `no-chmod.json` 的策略文件
```json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"name": "chmod",
"action": "SCMP_ACT_ERRNO"
}
]
}
```
在启动时告诉 Docker 载入这套过滤配置
```bash
$ docker run --rm -it \
--security-opt seccomp=no-chmod.json \
alpine sh
/ # chmod 777 /etc/passwd
chmod: /etc/passwd: Operation not permitted
```
应用只要被劫持进行越界尝试其操作系统层命令便会立刻吃瘪
### 18.5.2 强制访问控制 (AppArmor / SELinux)
传统的 Linux 模型遵循 DAC自主访问控制这意味着如果一个文件被赋予了全员读写权限`777`普通隔离下任何人便都能修改 **MAC强制访问控制** 技术诸如 `AppArmor` (常用于 Ubuntu/Debian) `SELinux` (常用于 CentOS/RHEL)可以制定比文件所有权更宏观且优先的策略控制模块
在开启了上述机制的机器上
- **AppArmor**: Docker 为所有启动的应用加载了一个默认的 `docker-default` 模板文件如果你的某些异常写行为比如往特殊的内核心脏目录写入配置不在 AppArmor 许可列表之上即使拥有物理 Root写入同样失败
- **SELinux**: 所有的 Docker 操作强制附加特殊上下文标识标签就算把主机的 `/` 绑定给了黑客的某服务黑客对不属于 Docker 可见的标签的文件进行读写尝试亦会被阻止
如果想为某些受信任应用施加特定的外部强化文件策略可以通过如下方法指派规则表
```bash
$ docker run --rm -it \
--security-opt apparmor=custom-nginx-profile \
nginx
```
### 18.5.3 容器镜像漏洞静态扫描
现代防护的防御已经不仅仅在运行阶段而向延伸至了构建与分发时期控制很多安全隐患并不是用户代码中的直接逻辑异常而是打包环境或者引入库的基础 `APT` 安装层面潜伏了开源界众所周知的历史漏洞
#### 使用 Trivy 识别风险
[Trivy](https://github.com/aquasecurity/trivy) 是由 Aqua Security 发行的一款针对容器技术的快速镜像漏洞扫描利器。在分发应用前通过它的扫描可以规避绝大多数风险。
```bash
## 如果使用本地命令行扫描容器镜像
$ trivy image alpine:3.10
2024-03-01T10:05:07.124Z INFO Number of language-specific files: 1
2024-03-01T10:05:07.124Z INFO Detecting vulnerabilities...
alpine:3.10 (alpine 3.10.3)
===========================
Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0)
+---------+------------------+----------+-------------------+---------------+---------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+---------------------------------------+
| busybox | CVE-2022-28391 | HIGH | 1.30.1-r3 | 1.30.1-r4 | busybox: out-of-bounds read in... |
...
```
只要确保所有上传给私有或公共仓库分发服务的产物先被引入至 CI/CD 流水线如果出现 `HIGH` `CRITICAL` 严重的报错记录强行阻拦部署这本身便是构建环节极其有力的自动化安全大门保障 Trivy 最新的 Docker 版本也已内置支持官方扫描利刃 **Docker Scout**
### 18.5.4 容器核心层基石结语
到这里Docker 为保障宿主和容器界限安全的几个护城河**资源剥离限制** (`Cgroups`) **进程/网络/身份蒙蔽** (`Namespace`)**特权能力回收** (`Capabilities`) 再到**内核强制策略拦截管制** (`Seccomp`/`AppArmor`) 已悉数交代完毕虽然绝没有100% 免疫网络穿刺的防线只要开发者牢记 **权限最小化原则** 容器的堡垒就可以做到令攻击者望洋兴叹

View File

@@ -391,4 +391,4 @@ $ cosign verify --key cosign.pub $IMAGE
- [命名空间](../12_implementation/12.2_namespace.md)隔离机制详解
- [控制组](../12_implementation/12.3_cgroups.md)资源限制详解
- [最佳实践](../appendix/20.1_best_practices.md)Dockerfile 安全配置
- [最佳实践](../appendix/best_practices.md)Dockerfile 安全配置

View File

@@ -1,4 +1,4 @@
## 18.6
## 本章小
Docker 的安全性依赖于多层隔离机制的协同工作同时需要用户遵循最佳实践

View File

@@ -1,4 +1,4 @@
## 19.1 Prometheus + Grafana
## 19.1 Prometheus
Prometheus Grafana 是目前最流行的开源监控组合前者负责数据采集与存储后者负责数据可视化
@@ -172,7 +172,7 @@ $ docker compose up -d
4. **时间问题**宿主机与容器时间偏差过大可能导致数据看起来断档需要检查 NTP/时区配置
5. **目标本身异常**确认 exporter 容器是否在重启查看 `docker logs`
#### 告警 (Alertmanager) 建议
#### Alertmanager 告警建议
生产环境建议引入 Alertmanager 做告警聚合与路由并在 Prometheus 中配置 `alerting` `rule_files`

View File

@@ -1,4 +1,4 @@
## 19.2 ELK/EFK 堆栈
## 19.2 ELK 套件
ELK (ElasticsearchLogstashKibana) 是目前业界最流行的开源日志解决方案而在容器领域由于 Fluentd 更加轻量级且对容器支持更好EFK (ElasticsearchFluentdKibana) 组合也变得非常流行

View File

@@ -1,6 +1,6 @@
# 20.2 Busybox
## 20.1 Busybox
### 20.2.1 简介
### 20.1.1 简介
下图直观地展示了本节内容
@@ -11,7 +11,7 @@
`BusyBox` 可运行于多款 `POSIX` 环境的操作系统中 `Linux` (包括 `Android`)`Hurd``FreeBSD`
### 20.2.2 获取官方镜像
### 20.1.2 获取官方镜像
可以使用 `docker pull` 指令下载 `busybox:latest` 镜像
@@ -32,7 +32,7 @@ REPOSITORY TAG IMAGE ID CREATED
busybox latest e72ac664f4f0 6 weeks ago 2.433 MB
```
### 20.2.3 运行 busybox
### 20.1.3 运行 busybox
启动一个 `busybox` 容器并在容器中执行 `grep` 命令
@@ -110,7 +110,7 @@ tmpfs on /sys/firmware type tmpfs (ro,relatime)
`busybox` 镜像虽然小巧但包括了大量常见的 `Linux` 命令读者可以用它快速熟悉 `Linux` 命令
### 20.2.4 相关资源
### 20.1.4 相关资源
* `Busybox` 官网https://busybox.net/
* `Busybox` 官方仓库https://git.busybox.net/busybox/

View File

@@ -1,6 +1,6 @@
# 20.3 Alpine
## 20.2 Alpine
### 20.3.1 简介
### 20.2.1 简介
下图直观地展示了本节内容
@@ -26,7 +26,7 @@ ubuntu latest b39b81afc8ca 188.3 MB
centos latest 8efe422e6104 210 MB
```
### 20.3.2 获取并使用官方镜像
### 20.2.2 获取并使用官方镜像
由于镜像很小下载时间往往很短读者可以直接使用 `docker run` 指令直接运行一个 `Alpine` 容器并指定运行的 Linux 指令例如
@@ -35,7 +35,7 @@ $ docker run alpine echo '123'
123
```
### 20.3.3 迁移至 Alpine 基础镜像
### 20.2.3 迁移至 Alpine 基础镜像
目前大部分 Docker 官方镜像都已经支持 `Alpine` 作为基础镜像可以很容易进行迁移
@@ -65,7 +65,7 @@ RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories
&& apk add --no-cache <package>
```
### 20.3.4 相关资源
### 20.2.4 相关资源
* `Alpine` 官网https://www.alpinelinux.org/
* `Alpine` 官方仓库https://github.com/alpinelinux

View File

@@ -1,8 +1,8 @@
# 20.4 Debian Ubuntu
## 20.3 Debian Ubuntu
`Debian` `Ubuntu` 都是目前较为流行的 **Debian ** 的服务器操作系统十分适合研发场景`Docker Hub` 上提供了官方镜像国内各大容器云服务也基本都提供了相应的支持
### 20.4.1 Debian 系统简介
### 20.3.1 Debian 系统简介
下图直观地展示了本节内容
@@ -30,7 +30,7 @@ Debian GNU/Linux 8
`Debian` 镜像很适合作为基础镜像构建自定义镜像
### 20.4.2 Ubuntu 系统简介
### 20.3.2 Ubuntu 系统简介
下图直观地展示了本节内容
@@ -135,7 +135,7 @@ root@7d93de07bf76:/# curl 127.0.0.1
配合使用 `-p` 参数对外映射服务端口可以允许容器外来访问该服务
### 20.4.3 相关资源
### 20.3.3 相关资源
* `Debian` 官网https://www.debian.org/
* `Neuro Debian` 官网http://neuro.debian.net/

View File

@@ -1,6 +1,6 @@
# 20.5 CentOS Fedora
## 20.4 CentOS Fedora
### 20.5.1 CentOS 系统简介
### 20.4.1 CentOS 系统简介
`CentOS` `Fedora` 都是基于 `Redhat` 的常见 Linux 分支`CentOS` 是目前企业级服务器的常用操作系统`Fedora` 则主要面向个人桌面用户
@@ -29,7 +29,7 @@ Status: Downloaded newer image for centos:7
CentOS Linux release 7.9.2009 (Core)
```
### 20.5.2 Fedora 系统简介
### 20.4.2 Fedora 系统简介
下图直观地展示了本节内容
@@ -54,7 +54,7 @@ Fedora release 39 (Thirty Nine)
```
### 20.5.3 相关资源
### 20.4.3 相关资源
* `Fedora` 官网https://getfedora.org/
* `Fedora` 官方仓库https://github.com/fedora-infra

View File

@@ -1,4 +1,4 @@
# 20.6 本章小结
## 本章小结
本章讲解了典型操作系统镜像的下载和使用

View File

@@ -1,4 +1,4 @@
# 21.1 DevOps 工作流完整示例
## 21.1 DevOps 完整工作流
本章将演示一个基于 DockerKubernetes Jenkins/GitLab CI 的完整 DevOps 工作流
@@ -23,7 +23,7 @@
```dockerfile
## Build stage
FROM golang:1.18 AS builder
FROM golang:1.23 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
@@ -48,7 +48,7 @@ stages:
unit_test:
stage: test
image: golang:1.18
image: golang:1.23
script:
- go test ./...

View File

@@ -1,4 +1,4 @@
# 21.6 VS Code 中使用 Docker
## 21.6 VS Code
VS Code [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers)
可以把开发环境放进容器同时保留 VS Code 的编辑补全调试体验

View File

@@ -1,4 +1,4 @@
# 第二十一章 实战案例 - Devops
# 第二十一章 实战案例 - DevOps
本章将介绍 Docker DevOps 场景下的实战案例

View File

@@ -1,4 +1,4 @@
# 21.7 本章小结
## 本章小结
本章通过一个完整的 DevOps 工作流示例串联了从代码提交自动化测试镜像构建
到部署发布的一整套实践路径

View File

@@ -1,4 +1,4 @@
# Docker 从入门到实践
# 前言
[![](https://img.shields.io/github/stars/yeasy/docker_practice.svg?style=social&label=Stars)](https://github.com/yeasy/docker_practice) [![图](https://img.shields.io/github/release/yeasy/docker_practice/all.svg)](https://github.com/yeasy/docker_practice/releases) [![图](https://img.shields.io/badge/Based-Docker%20Engine%20v29.x-blue.svg)](https://docs.docker.com/engine/release-notes/) [![图](https://img.shields.io/badge/Docker%20%E6%8A%80%E6%9C%AF%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E6%88%98-jd.com-red.svg)][1]
@@ -15,7 +15,7 @@
* **入门基础** 1 ~ 6 章为基础内容帮助深入理解 Docker 的基本概念 (镜像容器仓库) 和核心操作
* **进阶应用** 7 ~ 11 章涵盖 Dockerfile 指令详解数据与网络管理BuildxCompose 等高级配置和管理操作
* **深入原理** 12 ~ 17 章介绍其底层实现技术深入探讨容器编排体系 (KubernetesEtcd)并延伸涉及容器与云计算及其它关键生态项目 (Fedora CoreOSPodman )
* **实战扩展** 18 ~ 20 章重点讨论容器安全防护机制监控与日志聚合系统 (PrometheusELK)并展示操作系统CI/CD 自动化构建等典型实践案例
* **实战扩展** 18 ~ 21 章重点讨论容器安全防护机制监控与日志聚合系统 (PrometheusELK)并展示操作系统CI/CD 自动化构建等典型实践案例
## 阅读方式

Some files were not shown because too many files have changed in this diff Show More