Compare commits

...
46 Commits
Author SHA1 Message Date
yeasy 48e86b7bec fix(appendix): correct .dockerignore typo in image-slimming FAQ 2026-05-28 11:38:28 -07:00
yeasy 9230b49b6b fix(04): replace deprecated openjdk base image with eclipse-temurin and fix 地 typo 2026-05-28 11:38:28 -07:00
yeasy 2185211041 fix(07): note FROM uses pre-FROM ARG, not ENV, for variable substitution 2026-05-28 11:38:27 -07:00
yeasy f09e800020 fix(content): harden copy-paste safety examples 2026-05-27 18:17:24 -07:00
yeasy 3a9ee19d22 docs(content): refresh Kubernetes helper versions 2026-05-26 00:45:20 -07:00
yeasy ea26f96297 fix(content): remove demo registry credential 2026-05-25 01:13:10 -07:00
yeasy 30f26d9195 chore(17): remove obsolete coreos_README.md leftover
This 7-line file was never in SUMMARY.md and its content is a strict
subset of the current 17.1_coreos_intro.md. It looks like a draft
that survived the chapter rename to NN.M_topic.md convention.
Removed so the directory only contains files reachable from the ToC.
2026-05-24 12:56:55 -07:00
yeasy fb361bb1f0 fix(appendix): use H2 for 附录七 to match sibling appendix files
The other appendix entries (附录四 best_practices, 附录五 debug,
附录六 resources, 附录八 learning_roadmap) all use H2. glossary.md
was the lone H1, which broke the table-of-contents nesting in
GitBook. Demoted to H2 to align.
2026-05-24 01:03:34 -07:00
yeasy 036f0486db fix(content): tighten safety guidance 2026-05-24 00:57:03 -07:00
yeasy 28c23d003e fix(content): harden Docker examples 2026-05-23 21:28:59 -07:00
yeasy 6a55219310 fix(11.3): use COPY (not ADD) in Flask/Redis intro Dockerfile
§7.3 of this book explicitly recommends COPY over ADD for simple
file copies ("在大多数情况下,你应该使用 COPY,而不是 ADD"). The intro
Compose example showed ADD without needing tar extraction or URL
download, contradicting the canonical guidance. Switched to COPY
so the first Dockerfile readers see matches the rule the book
teaches a few chapters later.
2026-05-23 21:27:01 -07:00
yeasy 8f4d88e350 docs: refine Buildx guidance 2026-05-22 07:58:38 -07:00
yeasy 7f83abc53b fix(content): correct Docker Engine 29.0.0 date and align login-action version
Docker Engine 29.0.0 was released 2025-11-10 per docs.docker.com release
notes, not 2025-11-11 (off by one day). Also bring docker/login-action
reference in 18.6 up to v4 to match the 21.2 example (v4 has been current
since 2026-03-04).
2026-05-22 03:19:37 -07:00
yeasy ec0fa15835 docs(content): harden Kubernetes and supply-chain examples 2026-05-22 00:53:38 -07:00
yeasy 8b9e4518c8 docs(content): add Mac alternatives, K8s transition guide, CI/CD registry push example
- Add OrbStack/Colima comparison table to Mac install chapter (3.7.4)
- Add Docker→K8s concept mapping paragraph to Ch13 intro
- Add KubeKey/RKE2 deployment tool notes to Ch14
- Add production image checklist to Dockerfile chapter summary
- Add build-and-push-to-registry workflow example to GitHub Actions section
- Expand Ch18 security summary with dimension table
2026-05-21 21:33:58 -07:00
yeasy dae0af9ae7 fix(content): DCT expiry tense + bundle buildx --check additions 2026-05-21 20:49:42 -07:00
yeasy 10b09f35eb fix(content): update DCT expiry tense and prefer named multistage COPY 2026-05-21 20:49:19 -07:00
yeasy e17bef96d2 docs(content): update Compose and safety guidance 2026-05-21 20:13:39 -07:00
yeasy a3567ff6a0 docs: use digest-based cosign signing 2026-05-21 19:14:33 -07:00
yeasy 9ef842ebb3 docs: update distro caveats 2026-05-21 18:25:54 -07:00
yeasy 2e625a3cdf fix(content): harden install and security guidance 2026-05-21 01:23:44 -07:00
yeasy d47afa7e75 docs: harden Docker practice security examples 2026-05-20 15:58:46 -07:00
yeasy 1b651e5f8c fix(content): refresh Docker and Kubernetes guidance 2026-05-20 09:16:36 -07:00
yeasy e91fe87822 fix(content): harden kubernetes and signing examples 2026-05-20 08:49:28 -07:00
yeasy bb97d5a46a fix(13.2): remove incorrect "Node lifecycle" (Pending/Running/Terminated)
The "节点周期" subsection listed `Pending`/`Running`/`Terminated` as
node phases, but those are **Pod** phases (pod.status.phase). Kubernetes
nodes use Conditions (Ready/MemoryPressure/DiskPressure/PIDPressure)
not phases; there is no "Terminated" node state. Removed the
misleading subsection and clarified the surviving "节点状态" section
with an explicit note distinguishing Pod phases from node conditions,
and added the eviction behavior when a node becomes Unknown.
2026-05-20 08:37:43 -07:00
yeasy 1f69884c8f Fix Chinese quote formatting 2026-05-20 08:02:42 -07:00
yeasy 4f92b3aa70 fix(19.3): add security trade-off note for cAdvisor privileged: true
The cAdvisor compose examples in 19.3.3 use `privileged: true`, which
contradicts the minimum-privilege / cap_drop=all guidance in chapter 18.
Add an explicit trade-off note before the first cAdvisor snippet
explaining the inconsistency and pointing to 18.4 for capability-based
hardening alternatives (cap_add: [SYS_ADMIN] + device_cgroup_rules
instead of full privileged mode).

This addresses the Round 1 review finding about pedagogical conflict
between the monitoring and security chapters.
2026-05-20 00:18:37 -07:00
yeasy 58504e9316 fix(review-fixes): surgical content corrections from web-verified library deep review 2026-05-19 19:45:02 -07:00
yeasy 4075330dba Fix architecture mermaid and MySQL version 2026-05-19 18:43:11 -07:00
yeasy e6bf228066 docs: update image signing and login guidance 2026-05-19 15:29:26 -07:00
yeasy 1b15b65bc5 Clarify AKS pricing tiers 2026-05-19 08:20:49 -07:00
yeasy 26da467052 docs: link deployment engineering guide 2026-05-18 22:37:02 -07:00
yeasy 7abaff237a test: add project rules check 2026-05-18 22:37:02 -07:00
yeasy 1c4e0538d8 Fix CI PDF order 2026-05-18 08:06:51 -07:00
yeasy 0b8f6e9b60 Refresh metadata and workflows 2026-05-18 07:38:58 -07:00
yeasy 5650315cb4 Remove demo TLS key 2026-05-17 20:28:01 -07:00
yeasy e21794ebde Fix stale image facts 2026-05-17 20:28:01 -07:00
yeasy 78ca8f6d19 Refresh Docker ecosystem 2026-05-16 14:09:31 -07:00
yeasy 1ba904a9ff Fix markdown format issues 2026-05-15 09:58:19 -07:00
yeasy 705d162f05 Fix image docs typos 2026-05-15 09:16:33 -07:00
yeasy 92be0050fd Fix docker debug note, Go modules, Compose V2, CentOS Quay migration 2026-05-15 08:11:38 +00:00
yeasy a20d1b19c4 Fix 3 broken external links in appendix 2026-05-14 02:27:32 +00:00
yeasy 9f481e88ca Fix go mod, Docker Hub rate limit, Compose verbose, docker debug, htpasswd security 2026-05-13 08:30:20 +00:00
yeasy ef5a97fa09 Fix VirtioFS note, cri-socket, componentstatuses, ES version and typo 2026-05-13 08:26:24 +00:00
yeasy 3c5c5911b0 Fix nginx version, IPVS removal, cgroups v2 and Redis config 2026-05-13 08:22:20 +00:00
yeasy e2742313f2 Fix typo in Fedora install guide 2026-05-08 21:20:03 +00:00
91 changed files with 784 additions and 369 deletions
+9 -6
View File
@@ -24,13 +24,16 @@ jobs:
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra
- name: Install mdpress (latest)
- name: Install mdpress 0.7.10
env:
MDPRESS_VERSION: "0.7.10"
MDPRESS_SHA256: "17e53e455996940bbbce64c69c43b3fb543f1501e03b74cf0434074efebd2db4"
run: |
LATEST_TAG=$(curl -fsSL -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ github.token }}" https://api.github.com/repos/yeasy/mdpress/releases/latest | jq -r .tag_name)
VERSION="${LATEST_TAG#v}"
echo "Installing mdpress $VERSION"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/$LATEST_TAG/mdpress_${VERSION}_linux_amd64.tar.gz" -o /tmp/mdpress.tar.gz
tar xzf /tmp/mdpress.tar.gz -C /tmp mdpress
archive="/tmp/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz"
echo "Installing mdpress ${MDPRESS_VERSION}"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/v${MDPRESS_VERSION}/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz" -o "$archive"
echo "${MDPRESS_SHA256} $archive" | sha256sum -c -
tar xzf "$archive" -C /tmp mdpress
sudo mv /tmp/mdpress /usr/local/bin/
mdpress --version
+13 -8
View File
@@ -16,6 +16,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check project rules
run: python3 check_project_rules.py
- name: Install Chromium and CJK fonts
uses: browser-actions/setup-chrome@v2
with:
@@ -24,19 +26,22 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra
- name: Install mdpress (latest)
- name: Install mdpress 0.7.10
env:
MDPRESS_VERSION: "0.7.10"
MDPRESS_SHA256: "17e53e455996940bbbce64c69c43b3fb543f1501e03b74cf0434074efebd2db4"
run: |
LATEST_TAG=$(curl -fsSL -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ github.token }}" https://api.github.com/repos/yeasy/mdpress/releases/latest | jq -r .tag_name)
VERSION="${LATEST_TAG#v}"
echo "Installing mdpress $VERSION"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/$LATEST_TAG/mdpress_${VERSION}_linux_amd64.tar.gz" -o mdpress.tar.gz
tar xzf mdpress.tar.gz
archive="/tmp/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz"
echo "Installing mdpress ${MDPRESS_VERSION}"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/v${MDPRESS_VERSION}/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz" -o "$archive"
echo "${MDPRESS_SHA256} $archive" | sha256sum -c -
tar xzf "$archive"
sudo mv mdpress /usr/local/bin/
mdpress --version
- name: Build site
run: mdpress build --format site
- name: Build PDF
run: mdpress build --format pdf --output docker_practice.pdf
- name: Build site
run: npm run build
- name: Upload PDF as artifact
uses: actions/upload-artifact@v7
with:
+25 -3
View File
@@ -4,6 +4,7 @@ on: pull_request
permissions:
contents: write
pull-requests: write
checks: read
jobs:
dependabot:
@@ -12,15 +13,36 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v3
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
- name: Confirm required checks are configured
if: >
steps.metadata.outputs.package-ecosystem == 'github_actions' &&
contains(fromJSON('["version-update:semver-patch","version-update:semver-minor"]'), steps.metadata.outputs.update-type)
run: |
REQUIRED=$(gh api "repos/${GITHUB_REPOSITORY}/branches/${{ github.event.pull_request.base.ref }}/protection/required_status_checks" --jq '((.contexts // []) | length) + ((.checks // []) | length)' 2>/dev/null || echo 0)
if [ "$REQUIRED" -eq 0 ]; then
echo "No required status checks configured on the base branch; refusing Dependabot auto-merge."
exit 1
fi
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Approve low-risk Dependabot PR
if: >
steps.metadata.outputs.package-ecosystem == 'github_actions' &&
contains(fromJSON('["version-update:semver-patch","version-update:semver-minor"]'), steps.metadata.outputs.update-type)
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Dependabot PRs
- name: Enable auto-merge for low-risk Dependabot PRs
if: >
steps.metadata.outputs.package-ecosystem == 'github_actions' &&
contains(fromJSON('["version-update:semver-patch","version-update:semver-minor"]'), steps.metadata.outputs.update-type)
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
+9 -6
View File
@@ -31,13 +31,16 @@ jobs:
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra
- name: Install mdpress (latest)
- name: Install mdpress 0.7.10
env:
MDPRESS_VERSION: "0.7.10"
MDPRESS_SHA256: "17e53e455996940bbbce64c69c43b3fb543f1501e03b74cf0434074efebd2db4"
run: |
LATEST_TAG=$(curl -fsSL -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ github.token }}" https://api.github.com/repos/yeasy/mdpress/releases/latest | jq -r .tag_name)
VERSION="${LATEST_TAG#v}"
echo "Installing mdpress $VERSION"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/$LATEST_TAG/mdpress_${VERSION}_linux_amd64.tar.gz" -o /tmp/mdpress.tar.gz
tar xzf /tmp/mdpress.tar.gz -C /tmp mdpress
archive="/tmp/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz"
echo "Installing mdpress ${MDPRESS_VERSION}"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/v${MDPRESS_VERSION}/mdpress_${MDPRESS_VERSION}_linux_amd64.tar.gz" -o "$archive"
echo "${MDPRESS_SHA256} $archive" | sha256sum -c -
tar xzf "$archive" -C /tmp mdpress
sudo mv /tmp/mdpress /usr/local/bin/
mdpress --version
+2
View File
@@ -13,6 +13,7 @@ node_modules/
package-lock.json
docker-compose.override.yml
06_repository/demo/auth/nginx.htpasswd
# Editor configs
.obsidian/
@@ -26,6 +27,7 @@ Docker -- *_site/
# Check scripts
check*.py
!check_project_rules.py
find*.py
fix*.py
format*.py
+1 -1
View File
@@ -172,7 +172,7 @@ registry.example.com/myproject/myapp:v1.2.3
## 简写(使用 Docker Hub
nginx:1.30
nginx:1.28
ubuntu:24.04
## 省略标签(默认使用 latest)
+10 -10
View File
@@ -1,6 +1,6 @@
## 2.3 仓库
> **版本说明**本节示例基于 Docker v29.x 和常见镜像版本编写示例中的版本号 `nginx:1.30``mysql:8.4``mysql:5.7` 为演示用途实际使用时请访问 [Docker Hub 官方页面](https://hub.docker.com) 或相应镜像的发布页确认最新可用版本和标签。
> **版本说明**本节示例基于 Docker v29.x 和常见镜像版本编写示例中的版本号 `nginx:1.28``mysql:8.4``mysql:5.7` 为演示用途实际使用时请访问 [Docker Hub 官方页面](https://hub.docker.com) 或相应镜像的发布页确认最新可用版本和标签。
Docker Registry 是镜像分发和管理的核心组件本节将介绍 Registry 的基本概念公共和私有服务的选择以及镜像的安全管理
@@ -25,8 +25,8 @@ flowchart TB
subgraph RepoNginx ["Repository(仓库): nginx"]
direction LR
N1(":latest (tag)")
N2(":1.25 (tag)")
N3(":1.24 (tag)")
N2(":1.28 (tag)")
N3(":1.26 (tag)")
N4(":alpine (tag)")
N5("...")
N1 ~~~ N2 ~~~ N3 ~~~ N4 ~~~ N5
@@ -50,7 +50,7 @@ flowchart TB
|------|------|------|
| **Registry** | 存储镜像的服务 | Docker Hubghcr.io |
| **Repository (仓库)** | 同一软件的镜像集合 | `nginx``mysql``mycompany/myapp` |
| **Tag (标签)** | 仓库内的版本标识 | `latest``1.25``alpine` |
| **Tag (标签)** | 仓库内的版本标识 | `latest``1.28``alpine` |
#### 镜像的完整名称
@@ -73,7 +73,7 @@ registry.example.com/mycompany/myapp:v1.2.3
## Docker Hub 官方镜像(省略 registry 和用户名)
nginx:1.30
nginx:1.28
ubuntu:24.04
## Docker Hub 用户镜像
@@ -131,7 +131,7 @@ Google 的 Container Registry 已废弃并完成下线,当前应优先使用 A
由于网络原因在国内直接访问 Docker Hub 可能会很慢可以配置 **镜像加速器** (Registry Mirror) 来加速下载配置示例如下
```json
```jsonc
// /etc/docker/daemon.json
{
"registry-mirrors": [
@@ -217,7 +217,7 @@ $ docker login registry.example.com # 登录其他 Registry
## 拉取镜像
$ docker pull nginx:1.30
$ docker pull nginx:1.28
## 标记镜像(准备推送)
@@ -261,9 +261,9 @@ someuser/myapp # ⚠️ 需要评估
```bash
## 准备一个你有写权限的镜像地址
$ export IMAGE=<你的仓库名>/nginx:1.30
$ docker pull nginx:1.30
$ docker tag nginx:1.30 $IMAGE
$ export IMAGE=<你的仓库名>/nginx:1.28
$ docker pull nginx:1.28
$ docker tag nginx:1.28 $IMAGE
$ docker push $IMAGE
## 生成签名密钥(会生成 cosign.key / cosign.pub
+1 -1
View File
@@ -91,7 +91,7 @@ $ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin do
在真正执行前建议先用 `--dry-run` 预览脚本动作
```bash
$ curl -fsSL get.docker.com -o get-docker.sh
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh --dry-run
# 若需要测试频道:
+1 -1
View File
@@ -81,7 +81,7 @@ $ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin do
在真正执行前建议先用 `--dry-run` 预览脚本动作
```bash
$ curl -fsSL get.docker.com -o get-docker.sh
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh --dry-run
# 若需要测试频道:
+2 -2
View File
@@ -39,7 +39,7 @@ $ sudo dnf remove docker \
### 3.3.2 使用 dnf 安装
使用 dnf 包管理器安装是推荐的方式便于后续的更和管理
使用 dnf 包管理器安装是推荐的方式便于后续的更和管理
执行以下命令安装依赖包
@@ -91,7 +91,7 @@ $ sudo dnf -y install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING>
在真正执行前建议先用 `--dry-run` 预览脚本动作
```bash
$ curl -fsSL get.docker.com -o get-docker.sh
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh --dry-run
# 若需要测试频道:
+1 -1
View File
@@ -88,7 +88,7 @@ Docker 官方提醒:如果主机使用 `ufw` 或 `firewalld` 管理防火墙
在真正执行前建议先用 `--dry-run` 预览脚本动作
```bash
$ curl -fsSL get.docker.com -o get-docker.sh
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh --dry-run
# 若需要测试频道:
+1 -1
View File
@@ -114,7 +114,7 @@ $ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugi
在真正执行前建议先用 `--dry-run` 预览脚本动作
```bash
$ curl -fsSL get.docker.com -o get-docker.sh
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh --dry-run
# 若需要测试频道:
+10 -6
View File
@@ -90,9 +90,11 @@ $ sudo dnf versionlock clear
##### 挂载 ISO 镜像搭建本地 File
```bash
## 删除其他网络源
## 备份其他网络源
$ sudo rm -f /etc/yum.repos.d/*
$ repo_backup="/etc/yum.repos.d/backup-$(date +%Y%m%d%H%M%S)"
$ sudo mkdir -p "$repo_backup"
$ sudo find /etc/yum.repos.d -maxdepth 1 -type f -name '*.repo' -exec mv {} "$repo_backup"/ \;
## 挂载光盘或者iso镜像
@@ -106,7 +108,7 @@ $ sudo tee /etc/yum.repos.d/local-base.repo <<EOF
name=local_base
baseurl=file:///mnt
enabled=1
gpgcheck=0
gpgcheck=1
EOF
```
```bash
@@ -156,7 +158,9 @@ $ sudo createrepo_c /var/www/html/docker-ce/
##### DNF 客户端设置
```bash
$ sudo rm -f /etc/yum.repos.d/*
$ repo_backup="/etc/yum.repos.d/backup-$(date +%Y%m%d%H%M%S)"
$ sudo mkdir -p "$repo_backup"
$ sudo find /etc/yum.repos.d -maxdepth 1 -type f -name '*.repo' -exec mv {} "$repo_backup"/ \;
$ sudo tee /etc/yum.repos.d/local-files.repo <<EOF
[local_base]
name=local_base
@@ -165,7 +169,7 @@ name=local_base
baseurl=http://x.x.x.x/base
enabled=1
gpgcheck=0
gpgcheck=1
proxy=_none_
[docker-ce-stable]
name=docker-ce-stable
@@ -174,7 +178,7 @@ name=docker-ce-stable
baseurl=http://x.x.x.x/docker-ce
enabled=1
gpgcheck=0
gpgcheck=1
proxy=_none_
EOF
+20 -1
View File
@@ -72,6 +72,25 @@ $ docker stop webserver
$ docker rm webserver
```
### 3.7.4 镜像加速
### 3.7.4 替代容器运行时
Docker Desktop 并非 macOS 上运行容器的唯一选择以下两个工具也广泛使用各有侧重
| 特性 | Docker Desktop | OrbStack | Colima |
|------|---------------|-----------|--------|
| 启动速度 | 较慢 1030 | 极快 2 | 中等 510 |
| 空闲内存占用 | 46 GB | 200300 MB | 400 MB |
| 图形界面 | 完整 GUI | 轻量 GUI | 仅命令行 |
| Apple Silicon | 支持 | 原生优化 | 支持 |
| 商业许可 | 大型企业需付费 | 个人免费商业付费 | MIT 开源 |
| Kubernetes | 内置 | 内置 | 通过 k3s 支持 |
**[OrbStack](https://orbstack.dev/)**:以极低的资源占用和接近原生的 I/O 性能著称。对于 Apple Silicon 机型上需要频繁构建镜像的开发者,体验提升尤为明显。个人和教育用途免费。
**[Colima](https://github.com/abiosoft/colima)**:完全开源的命令行方案,底层基于 Lima 虚拟机。适合偏好终端工作流、不需要 GUI 的开发者,或企业中希望避免商业许可限制的团队。
> 上述三种工具均兼容标准的 `docker` CLI 命令 Docker Desktop 切换到 OrbStack Colima 已有的镜像和容器配置通常可以平滑迁移
### 3.7.5 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
+2 -2
View File
@@ -4,7 +4,7 @@
> **版本号最佳实践**
>
> - **永远指定版本号**避免使用 `latest` 标签应指定具体的版本 `ubuntu:24.04``nginx:1.30`以确保镜像内容稳定一致
> - **永远指定版本号**避免使用 `latest` 标签应指定具体的版本 `ubuntu:24.04``nginx:1.28`以确保镜像内容稳定一致
> - **在生产环境使用摘要**优先使用镜像摘要SHA256而非标签 `nginx@sha256:abc123...`因为摘要不可变
> - **定期评估依赖**即使指定了版本号仍应定期检查依赖的基础镜像是否有安全更新
@@ -160,7 +160,7 @@ root@e7009c6ce357:/# exit
Docker Hub 下载可能较慢可以配置镜像加速器
```json
```jsonc
// /etc/docker/daemon.json (Linux)
// ~/.docker/daemon.json (Docker Desktop)
{
+2 -2
View File
@@ -10,7 +10,7 @@
现在让我们以定制一个 Web 服务器为例子来讲解镜像是如何构建的
> **版本提示**以下示例中 `nginx` 镜像使用默认 `latest` 标签生产环境建议指定具体版本号 `nginx:1.30`以避免镜像更新带来的不兼容性
> **版本提示**以下示例中 `nginx` 镜像使用默认 `latest` 标签生产环境建议指定具体版本号 `nginx:1.28`以避免镜像更新带来的不兼容性
```bash
$ docker run --name webserver -d -p 8080:80 nginx
@@ -93,7 +93,7 @@ nginx 1.30 05a60462f8ba 12 days ago
nginx latest e43d811ce2f4 4 weeks ago 181.5 MB
```
> **版本说明**上面示例中 `nginx:1.30` 代表 1.30 系列的最新 patch 版本际应用中应根据需求选择确切的版本号而不是盲目使用 `latest`
> **版本说明**上面示例中 `nginx:1.30` 代表 1.30 系列的最新 patch 版本际应用中应根据需求选择确切的版本号而不是盲目使用 `latest`
我们还可以用 `docker history` 具体查看镜像内的历史记录例如先执行 `docker history nginx:v2`再对比 `docker history nginx:latest`就能看到我们刚刚提交出来的新层
+3 -3
View File
@@ -26,7 +26,7 @@ $ touch Dockerfile
```
其内容为
> **版本提示**下面示例中 `FROM nginx` 使用的是 `latest` 标签在实际应用中应使用明确的版本号 `FROM nginx:1.30`以确保 Dockerfile 的可重现性和稳定性
> **版本提示**下面示例中 `FROM nginx` 使用的是 `latest` 标签在实际应用中应使用明确的版本号 `FROM nginx:1.28`以确保 Dockerfile 的可重现性和稳定性
```docker
FROM nginx
@@ -40,7 +40,7 @@ RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
> **版本号最佳实践** `FROM` 指令中 **务必指定具体版本号** `FROM ubuntu:24.04` `FROM python:3.12-slim`而非 `FROM ubuntu` `FROM python:latest`这样可以确保 Dockerfile 在不同时间不同环境下构建出的镜像内容一致避免因基础镜像更新导致的不可预期的变化
[Docker Hub](https://hub.docker.com/search?q=&type=image&image_filter=official) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://hub.docker.com/_/nginx/)、[`redis`](https://hub.docker.com/_/redis/)、[`mongo`](https://hub.docker.com/_/mongo/)、[`mysql`](https://hub.docker.com/_/mysql/)、[`httpd`](https://hub.docker.com/_/httpd/)、[`php`](https://hub.docker.com/_/php/)、[`tomcat`](https://hub.docker.com/_/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://hub.docker.com/_/node)、[`openjdk`](https://hub.docker.com/_/openjdk/)、[`python`](https://hub.docker.com/_/python/)、[`ruby`](https://hub.docker.com/_/ruby/)、[`golang`](https://hub.docker.com/_/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
[Docker Hub](https://hub.docker.com/search?q=&type=image&image_filter=official) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://hub.docker.com/_/nginx/)、[`redis`](https://hub.docker.com/_/redis/)、[`mongo`](https://hub.docker.com/_/mongo/)、[`mysql`](https://hub.docker.com/_/mysql/)、[`httpd`](https://hub.docker.com/_/httpd/)、[`php`](https://hub.docker.com/_/php/)、[`tomcat`](https://hub.docker.com/_/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://hub.docker.com/_/node)、[`eclipse-temurin`](https://hub.docker.com/_/eclipse-temurin/)、[`python`](https://hub.docker.com/_/python/)、[`ruby`](https://hub.docker.com/_/ruby/)、[`golang`](https://hub.docker.com/_/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像官方镜像中还提供了一些更为基础的操作系统镜像 [`ubuntu`](https://hub.docker.com/_/ubuntu/)、[`debian`](https://hub.docker.com/_/debian/)、[`centos`](https://hub.docker.com/_/centos/)、[`fedora`](https://hub.docker.com/_/fedora/)、[`alpine`](https://hub.docker.com/_/alpine/) 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
@@ -98,7 +98,7 @@ Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
```
从命令的输出结果中我们可以清晰看到镜像的构建过程 `Step 2` 如同我们之前所说的那样`RUN` 指令启动了一个容器 `9cdc27646c7b`执行了所要求的命令并最后提交了这一层 `44aa4490ce2c`随后删除了所用到的这个容器 `9cdc27646c7b`
从命令的输出结果中我们可以清晰看到镜像的构建过程 `Step 2` 如同我们之前所说的那样`RUN` 指令启动了一个容器 `9cdc27646c7b`执行了所要求的命令并最后提交了这一层 `44aa4490ce2c`随后删除了所用到的这个容器 `9cdc27646c7b`
这里我们使用了 `docker build` 命令进行镜像构建其格式为
+1 -1
View File
@@ -10,7 +10,7 @@
比如我们想要创建一个 [OpenVZ](https://openvz.org) 的 Ubuntu 16.04 [模板](https://wiki.openvz.org/Download/template/precreated)的镜像:
> **版本提示**示例中的 Ubuntu 16.04 (Xenial) 已于 2021 4 月停止支持如果用于生产环境建议更新至 Ubuntu 22.04 LTS 或更新版本
> **版本提示**`noble` 对应 Ubuntu 24.04 LTS实际用于生产环境应选择仍在安全维护期内的发行版并按团队的基础镜像更新策略定期重建
```bash
$ docker import \
+1 -1
View File
@@ -60,7 +60,7 @@ Docker 镜像的每一层都有一个唯一的 ID,这个 ID 是根据该层的
Docker 使用联合文件系统 (Union FS) 与写时复制思路来实现这种分层挂载传统的实现方式常见于 `overlay2``aufs``btrfs``zfs` 等存储驱动而在 Docker Engine 29.0 及之后的全新安装中默认镜像后端已经变为 containerd image store它使用 snapshotter 来管理这些层
> **版本背景**Docker Engine 29.0发布于 2025 11 是一个重要版本分界点全新安装场景下默认 containerd image store 作为镜像存储后端这对镜像管理OCI 合规性和供应链安全都有深远影响如果你的 Docker 版本低于 29.0镜像存储仍使用传统的 classic store 路径
> **版本背景**Docker Engine 29.0.0 发布于 2025 11 10 是一个重要版本分界点Docker Engine 29.0 及之后的全新安装默认使 containerd image store从更早版本升级的 daemon 会继续使用 legacy graph driver直到显式启用 containerd image storeDocker Desktop 4.34 及之后也默认启用 containerd image store实际环境仍应以当前配置为准
虽然底层实现细节不同但它们都遵循上述的 **分层 + CoW** 模型因此无论你看到的是 `overlay2` 还是 containerd snapshotter理解镜像层容器层和写时复制的方式都是一样重要的
+2 -1
View File
@@ -249,10 +249,11 @@ $ docker exec myapp python manage.py migrate
$ docker exec -it myapp bash
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found
## 解决方案:使用调试容器
## 解决方案:使用调试容器(需要 Docker Desktop Pro/Team/Business 订阅)
$ docker debug myapp
```
> **注意**`docker debug` Docker Desktop 4.33+ 提供的功能需要 ProTeam Business 订阅它会附加一个包含常用调试工具vimcurlhtop 的工具箱到目标容器即使目标镜像基于 `scratch` 也能使用
---
### 5.4.7 常见问题
+1 -1
View File
@@ -14,7 +14,7 @@
- **镜像标签约定**
- `latest` `v1.0.0` 带标签的自定义镜像示例中指定具体版本
- `24.04``8.4`官方镜像的稳定版本分支
- 生产环境建议指定确版本号 `nginx:1.30.0``mysql:8.4.4`而非仅大版本号
- 生产环境建议指定确版本号 `nginx:1.28.0``mysql:8.4.4`而非仅大版本号
* [启动容器](5.1_run.md)
* [守护态运行](5.2_daemon.md)
+4 -3
View File
@@ -75,7 +75,7 @@ Docker Hub 对不同类型用户实施拉取速率限制(基于 6 小时周期
| **免费账户** (已登录) | 6 小时 200 次请求 |
| **Pro/Team/Business 账户** | 无限制公平使用政策 |
> **注意** 2025 4 所有付费订阅用户享有无限制拉取额度匿名用户和免费账户的限制保持不变建议在 CI/CD 环境中始终配置 `docker login` 以获得更高的拉取额度
> **注意**Docker 曾计划于 2025 4 调整拉取限制策略但在 2025 2 月宣布取消该计划目前付费订阅用户享有无限制拉取额度匿名用户和免费账户的限制保持不变建议在 CI/CD 环境中始终配置 `docker login` 以获得更高的拉取额度
#### 滥用限流
@@ -107,10 +107,11 @@ Docker Hub 对不同类型用户实施拉取速率限制(基于 6 小时周期
> ** 警告**绝不要在脚本或 CI/CD 系统中直接使用 `-p` 参数传递密码或 Token (类似 `docker login -p xxx`)这会导致凭证直接暴露在系统的命令历史进程列表和终端输出中
1. Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token (PAT)
2. Token 通过标准输入 (stdin) 安全传递给 Docker:
2. Token 保存在权限受限的本地文件或 CI secret 通过标准输入 (stdin) 传递给 Docker避免把真实 Token 写进脚本命令历史或日志
```bash
$ echo "dckr_pat_xxxxxxx" | docker login --username username --password-stdin
$ chmod 600 "$HOME/.dockerhub-token"
$ cat "$HOME/.dockerhub-token" | docker login --username username --password-stdin
```
#### 3. 关注镜像漏洞
+11 -2
View File
@@ -70,10 +70,14 @@ $ openssl x509 -req -days 750 -in "site.csr" -sha256 \
-CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
-out "docker.domain.com.crt" -extfile "site.cnf" -extensions server
```
配套 demo 中的 [`ssl/README.md`](demo/ssl/README.md) 只保留本地生成证书的占位说明真实私钥和证书不应提交到仓库
这样已经拥有了 `docker.domain.com` 的网站 SSL 私钥 `docker.domain.com.key` SSL 证书 `docker.domain.com.crt` CA 根证书 `root-ca.crt`
新建 `ssl` 文件夹并将 `docker.domain.com.key` `docker.domain.com.crt` `root-ca.crt` 这三个文件移入删除其他文件
> **安全提示**这些私钥和证书应在本地或受控部署环境中生成不要提交进 Git 仓库示例目录只保留占位说明运行前请按上面的步骤重新生成
### 6.3.2 配置私有仓库
私有仓库默认的配置文件位于 `/etc/docker/registry/config.yml`我们先在本地编辑 `config.yml`之后挂载到容器中
@@ -99,7 +103,7 @@ auth:
realm: basic-realm
path: /etc/docker/registry/auth/nginx.htpasswd
http:
addr: :443
addr: :5000
host: https://docker.domain.com
headers:
X-Content-Type-Options: [nosniff]
@@ -126,6 +130,9 @@ $ docker run --rm \
-Bbn username password > auth/nginx.htpasswd
```
> 将上面的 `username` `password` 替换为你自己的用户名和密码
>
> **安全提示**上述命令会将密码明文暴露在 shell 历史记录和进程列表中生产环境建议使用交互式方式输入密码不带 `-b` 参数或通过环境变量/文件传入
> 配套 demo [`auth/README.md`](demo/auth/README.md) 仅说明本地生成步骤生成的 `auth/nginx.htpasswd` 已被忽略不应提交
> **版本说明**使用 `httpd:2.4-alpine` 基于 Apache 2.4 的精简镜像如需其他版本可替换为 `httpd:latest` 或指定具体版本号如 `httpd:2.4.58-alpine`
@@ -138,7 +145,7 @@ services:
registry:
image: registry:2
ports:
- "443:443"
- "443:5000"
volumes:
- ./:/etc/docker/registry
- registry-data:/var/lib/registry
@@ -147,6 +154,8 @@ volumes:
registry-data:
```
本书配套的 `06_repository/demo/` 也采用同样约定容器内 registry 监听 `:5000`宿主机通过 `443:5000` 暴露 HTTPS 服务这样可以避免在容器内占用特权端口同时仍让客户端使用 `https://docker.domain.com` 访问
> **版本说明**Compose 配置中明确指定 `registry:2` 版本生产环境建议固定版本号 `registry:2.8.3`而非使用 `latest`以保证部署的可重复性
### 6.3.5 修改 Hosts 文件
+5
View File
@@ -0,0 +1,5 @@
# Generated Authentication File
Run the `htpasswd` command in section 6.3.3 to generate `nginx.htpasswd` locally before starting the demo registry.
Do not commit generated password hashes.
-2
View File
@@ -1,2 +0,0 @@
username:$2y$05$TRWvCC6ilpKpY3ICifw32Ok3.8SpG3etq8O5WGdCm9wvyDhtSbRgy
+3
View File
@@ -0,0 +1,3 @@
*
!.gitignore
!README.md
+5
View File
@@ -0,0 +1,5 @@
# Generated TLS Files
Run the certificate steps in section 6.3.1 to generate `docker.domain.com.key`, `docker.domain.com.crt`, and `root-ca.crt` locally before starting the demo registry.
Do not commit generated private keys or certificates.
@@ -1,35 +0,0 @@
-----BEGIN CERTIFICATE-----
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
7zJA
-----END CERTIFICATE-----
@@ -1,51 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
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
oKnsMQKCAQEA0iurbnOyrF050SfRdQcnG4shZs/HeBT2EB3CsR1OocWwXBeUkBlO
OKl+d1cYan1ppw+qGlbdQr+t3u7lLPFLUBghf+I/8CmSyiCbZlR4/LrePOmw551r
YXU1uvtFu/mQq3ieV+k4GOyHq3lhCDd61QFedyESfbkVK8f4ihvvX3izZAAtZfwU
HcmZ174vpwZploWQPsrL9A2B+Na42ccLM2qA45nPwXv1Jr/U6b/CzPw7r/4DvTXv
FIeolrELDkCgWBQ8lxB7Lt96BZy9Rbiwi1TzcP++BQu4IOwbAfq23tCybu8vDde4
Z15KVf7qyBansdqKx0njxWNu2/dpgKCPqQKCAQEAzWV4aajox7eeTi3iudP1Oxu3
OBiu8xie4xq0mlM13tMxAQry/uOAuTxbaQ48mNsSXdeYeSgmT45lUvFWROqdVScK
8gh04G1NiRAkITzXwCCwKkAQxvQppgypZ+aksBHkFQFBAIg2/mLizS4cicXNEY4G
vb+RImfn8MSqSMLu3cJ8zgFyqfRg6F0oHg9EgEwvbnCLYglN6Xm5KlZutjW4eu7m
Q+1y0lg05e7vFyj2UIVylfzT/aoF/xzXzNt9/LZs0klO3vMmaVyTjuF/71+AyphH
FTmpNs1wpZU2IRqKOMrimIhk6TTxvMSaN4pxKdLPqgHYLWhwkbsdmqtTnCljRg==
-----END RSA PRIVATE KEY-----
+1 -1
View File
@@ -34,7 +34,7 @@ Starting ──成功──> Healthy ──失败N次──> Unhealthy
#### Web 服务检查
```docker
# 注:nginx 镜像推荐使用具体的版本标签(如 nginx:1.30-alpine
# 注:nginx 镜像推荐使用具体的版本标签(如 nginx:1.28-alpine
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
+17 -6
View File
@@ -33,7 +33,7 @@ WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go mod init helloworld \
&& go get -d -v github.com/go-sql-driver/mysql \
&& go get github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
@@ -62,7 +62,8 @@ WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
RUN go mod init helloworld \
&& go get github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
```
编写 `Dockerfile.copy` 文件
@@ -125,7 +126,8 @@ RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
RUN go mod init helloworld \
&& go get github.com/go-sql-driver/mysql
COPY app.go .
@@ -137,7 +139,7 @@ RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
COPY --from=builder /go/src/github.com/go/helloworld/app .
CMD ["./app"]
```
@@ -158,6 +160,15 @@ go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
```
很明显使用多阶段构建的镜像体积小同时也完美解决了上边提到的问题
> **Go Modules 最佳实践**上述示例为简化演示在 Dockerfile 中临时执行 `go mod init`在实际项目中通常已在代码仓库中维护好 `go.mod` `go.sum` 文件推荐的 Dockerfile 写法是先拷贝这两个文件并执行 `go mod download` 以利用 Docker 层缓存再拷贝源码并构建
>
> ```docker
> COPY go.mod go.sum ./
> RUN go mod download
> COPY . .
> RUN go build -o app .
> ```
### 7.17.4 只构建某一阶段的镜像
我们可以使用 `as` 来为某一阶段命名例如
@@ -173,8 +184,8 @@ $ docker build --target builder -t username/imagename:tag .
### 7.17.5 构建时从其他镜像复制文件
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件我们也可以复制任意镜像中的文件
上面例子中我们使用 `COPY --from=builder /go/src/github.com/go/helloworld/app .` 通过已命名阶段`as builder`从上一阶段的镜像中复制文件命名引用比 `--from=0` 这种位置索引更易读也是当前官方推荐的最佳实践我们也可以复制任意镜像中的文件
```docker
COPY --from=nginx:1.30-alpine /etc/nginx/nginx.conf /nginx.conf
COPY --from=nginx:1.28-alpine /etc/nginx/nginx.conf /nginx.conf
```
@@ -128,8 +128,8 @@ RUN set -x ; cd ${LARAVEL_PATH} \
### 7.18.5 最后一个阶段构建 NGINX 镜像
```docker
# 注:nginx 镜像推荐使用具体的版本标签(如 nginx:1.30-alpine
FROM nginx:1.30-alpine as nginx
# 注:nginx 镜像推荐使用具体的版本标签(如 nginx:1.28-alpine
FROM nginx:1.28-alpine as nginx
ARG LARAVEL_PATH=/app/laravel
@@ -179,7 +179,7 @@ $ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
完整的 `Dockerfile` 文件如下
```docker
# 注:生产环境推荐使用具体的版本标签,如 node:22-alpine、composer:2.x、php:8.3-fpm-alpine、nginx:1.30-alpine
# 注:生产环境推荐使用具体的版本标签,如 node:22-alpine、composer:2.x、php:8.3-fpm-alpine、nginx:1.28-alpine
FROM node:22-alpine as frontend
COPY package.json /app/
@@ -229,7 +229,7 @@ RUN set -x ; cd ${LARAVEL_PATH} \
&& chmod -R 777 storage \
&& php artisan package:discover
FROM nginx:1.30-alpine as nginx
FROM nginx:1.28-alpine as nginx
ARG LARAVEL_PATH=/app/laravel
+3 -1
View File
@@ -158,9 +158,11 @@ RUN --mount=type=cache,target=/go/pkg/mod \
```docker
RUN --mount=type=secret,id=mysecret \
cat /run/secrets/mysecret
my-private-tool --token-file /run/secrets/mysecret
```
不要在构建命令中 `cat``echo` 或打印密钥内容应把 `/run/secrets/<id>` 路径交给真正消费密钥的工具
#### 3. Heredoc 语法
BuildKit 支持使用 heredoc 语法编写多行脚本无需行末反斜杠 `\` 连接
+2 -2
View File
@@ -31,9 +31,9 @@ CMD 有三种格式:
| 格式 | 语法 | 推荐程度 |
|------|------|---------|
| **exec 格式**| `CMD [可执行文件, 参数1, 参数2]` | **推荐** |
| **exec 格式**| `CMD ["可执行文件", "参数1", "参数2"]` | **推荐** |
| **shell 格式** | `CMD 命令 参数1 参数2` | 简单场景 |
| **参数格式** | `CMD [参数1, 参数2]` | 配合 ENTRYPOINT |
| **参数格式** | `CMD ["参数1", "参数2"]` | 配合 ENTRYPOINT |
#### exec 格式推荐
+1 -1
View File
@@ -40,7 +40,7 @@
| 格式 | 语法 | 推荐程度 |
|------|------|---------|
| **exec 格式**| `ENTRYPOINT [可执行文件, 参数1]` | **推荐** |
| **exec 格式**| `ENTRYPOINT ["可执行文件", "参数1"]` | **推荐** |
| **shell 格式** | `ENTRYPOINT 命令 参数` | 不推荐 |
```docker
+2
View File
@@ -89,6 +89,8 @@ const dbUrl = process.env.DATABASE_URL;
| `LABEL` | `LABEL version=$VERSION` |
| `FROM` | `FROM node:$NODE_VERSION` |
> **注意**`FROM` 是上表中的特例它只能引用在第一条 `FROM` 之前用 `ARG` 声明的变量**无法**使用 `ENV` 定义的环境变量`ENV` 要进入构建阶段后才生效其余指令才能引用 `ENV` 环境变量
---
### 7.6.5 运行时覆盖
+15
View File
@@ -21,6 +21,21 @@
| **LABEL** | 添加元数据 | 推荐 OCI 标准标签替代 MAINTAINER |
| **SHELL** | 更改默认 shell | 推荐 `["/bin/bash", "-o", "pipefail", "-c"]` |
### 生产镜像快速检查清单
在将镜像推向生产之前建议逐条过一遍以下清单
- [ ] 基础镜像选择了最小化版本 `alpine``distroless`
- [ ] 使用了[多阶段构建](7.17_multistage_builds.md)最终镜像不含编译工具链
- [ ] 以非 root 用户运行`USER` 指令
- [ ] `COPY` 优先于 `ADD`且仅复制必要文件
- [ ] `RUN` 指令合并了 `apt-get update && install && rm -rf /var/lib/apt/lists/*`
- [ ] 设置了 `HEALTHCHECK`
- [ ] 使用了 `.dockerignore` 排除 `.git``node_modules` 等无关文件
- [ ] 镜像标签使用了具体版本号或 commit hash而非 `latest`
> 更完整的编写指南见[附录Dockerfile 最佳实践](../appendix/best_practices.md)
### 延伸阅读
- [使用 Dockerfile 定制镜像](../04_image/4.5_build.md)Dockerfile 入门
+3 -1
View File
@@ -238,7 +238,7 @@ $ docker run -u $(id -u):$(id -g) ...
Docker Desktop Bind Mount 性能通常不如 Volume因为数据需要在宿主机文件系统和 Linux VM 之间同步
```bash
## 使用 :cached :delegated 提高性能macOS
## 使用 :cached :delegated 提高性能macOS Docker Desktop 4.5 及更早版本
$ docker run -v /host/path:/container/path:cached myapp
```
@@ -248,6 +248,8 @@ $ docker run -v /host/path:/container/path:cached myapp
| `:delegated` | 容器权威宿主机读取可能延迟 |
| `:consistent` | 默认完全一致 (最慢)|
> **注意**Docker Desktop 4.6+ 默认使用 VirtioFS 文件共享引擎上述 `:cached`/`:delegated` 选项已被静默忽略如需优化文件同步性能请参考 Docker Desktop [Synchronized file shares](https://docs.docker.com/desktop/synchronized-file-sharing/) 功能。
---
### 8.2.9 最佳实践
+1 -1
View File
@@ -25,7 +25,7 @@ ghi789... none null local
### 9.2.2 Bridge 网络默认
Bridge Docker 默认使用的网络模式Docker 启动时会自动创建 `docker0` 虚拟网桥所有未指定网络的容器会连接到这个网桥上
Bridge Docker 默认使用的网络模式在原生 Linux Docker Engine Docker 启动时通常会创建 `docker0` 虚拟网桥未指定网络的容器会连接到这个网桥上Docker Desktop 运行在虚拟机内宿主机上不会直接看到 `docker0`也不能假设宿主机可直接访问每个容器 IP
核心组件如下
+1 -1
View File
@@ -7,7 +7,7 @@
容器的网络访问规则如下
- **容器之间**可以通过 IP 或容器名 (自定义网络) 互通
- **宿主机访问容器**可以通过容器 IP 访问
- **宿主机访问容器**原生 Linux 环境下可按网络配置访问容器 IPDocker Desktop 上不能依赖容器 IP应通过端口映射容器名容器间 `host.docker.internal` 等机制访问
- **外部网络访问容器** 默认无法直接访问
为了让外部 (如你的浏览器其他局域网机器) 访问容器内的服务我们需要将容器的端口 **映射** 到宿主机的端口
+1 -1
View File
@@ -8,7 +8,7 @@ Overlay 网络在现有网络基础上建立虚拟网络,允许容器跨宿主
#### Overlay 网络工作原理
Overlay 网络通过隧道封装技术通常是 VXLAN将容器网络流量封装在宿主机物理网络的 UDP 数据包中传输
Overlay 网络通过隧道封装技术通常是 VXLAN将容器网络流量封装在宿主机物理网络的 UDP 数据包中传输Docker overlay 网络默认使用 `4789/udp` 作为数据通道端口同时 Swarm 控制面与节点通信还需要相应开放 `2377/tcp``7946/tcp` `7946/udp`
```text
容器 A (192.168.0.2)
+7 -7
View File
@@ -15,7 +15,7 @@ BuildKit 引入了多项新指令,旨在优化构建缓存和安全性。以
要使用最新的 Dockerfile 语法特性建议在 Dockerfile 开头添加语法指令
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
```
这将使用最新的稳定版语法解析器确保你可以使用所有最新特性
@@ -51,7 +51,7 @@ COPY --from=builder /app/dist /app/dist
`BuildKit` 提供了 `RUN --mount=type=cache` 指令可以实现上边的设想
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
FROM node:alpine as builder
@@ -105,7 +105,7 @@ RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/dist \
该指令可以将一个镜像 (或上一构建阶段) 的文件挂载到指定位置
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
RUN --mount=type=bind,from=php:alpine,source=/usr/local/bin/docker-php-entrypoint,target=/docker-php-entrypoint \
cat /docker-php-entrypoint
@@ -116,7 +116,7 @@ RUN --mount=type=bind,from=php:alpine,source=/usr/local/bin/docker-php-entrypoin
该指令可以将一个 `tmpfs` 文件系统挂载到指定位置
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
RUN --mount=type=tmpfs,target=/temp \
mount | grep /temp
@@ -127,10 +127,10 @@ RUN --mount=type=tmpfs,target=/temp \
该指令可以将一个文件 (例如密钥) 挂载到指定位置
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
cat /root/.aws/credentials
test -s /root/.aws/credentials && echo "credentials mounted"
```
```bash
$ docker build -t test --secret id=aws,src=$HOME/.aws/credentials .
@@ -141,7 +141,7 @@ $ docker build -t test --secret id=aws,src=$HOME/.aws/credentials .
该指令可以挂载 `ssh` 密钥
```docker
## syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
FROM alpine
RUN apk add --no-cache openssh-client
+11
View File
@@ -13,6 +13,17 @@ $ docker buildx build .
```
Buildx 使用 [BuildKit 引擎](10.1_buildkit.md)进行构建支持许多新的功能具体参考 [Buildkit](10.1_buildkit.md) 一节
需要注意的是默认 `docker` driver 会把构建结果加载到本地镜像存储使用 `docker-container`remotecloud builder 如未指定 `--load``--push` `--output`结果通常只保留在构建缓存中
#### 构建前检查
Buildx 0.15 起支持构建检查常规构建会默认检查 Dockerfile 与构建参数如果只想做检查而不真正构建可以使用 `--check`
```bash
$ docker buildx build --check .
```
这适合作为 CI 的快速门禁普通构建中的检查告警默认不会让构建失败 `--check` 发现问题会以非零状态退出需要把告警提升为错误时可在 Dockerfile 顶部配合 `# check=error=true`
#### 使用 `bake`
`docker buildx bake` 是一个高级构建命令支持从 HCLJSON Compose 文件中定义构建目标实现复杂的流水线构建
+1
View File
@@ -8,6 +8,7 @@ Docker Buildx 是 Docker 构建系统的重要进化,提供了高效、安全
| **缓存挂载** | `RUN --mount=type=cache` 加速依赖安装 |
| **Secret 挂载** | `RUN --mount=type=secret` 安全传递密钥 |
| **buildx build** | 替代 `docker build`支持更多构建功能 |
| **构建检查** | `--check` 可在不执行构建的情况下检查 Dockerfile 与构建参数 |
| **多架构构建** | `--platform` 参数一键构建多种架构镜像 |
| **Manifest List** | 多架构镜像的索引文件 |
| **SBOM** | 通过 `--sbom=true` 生成软件物料清单 |
+1 -1
View File
@@ -19,7 +19,7 @@ $ chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
```bash
$ docker compose version
Docker Compose version v5.x
Docker Compose version v5.x.x
```
### 11.2.3 卸载
+1 -1
View File
@@ -42,7 +42,7 @@ if __name__ == "__main__":
```docker
FROM python:3.12-alpine
ADD . /code
COPY . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]
+1 -1
View File
@@ -46,7 +46,7 @@ docker compose [-f=<arg>...] [options] [COMMAND] [ARGS...]
* `-p, --project-name NAME` 指定项目名称默认将使用所在目录名称作为项目名
* `--verbose` 输出更多调试信息
* `--verbose` 输出更多调试信息(**已弃用** Docker Compose V2 请改用 `docker --log-level debug compose ...` 或设置环境变量 `COMPOSE_DEBUG=1`)
* `-v, --version` 打印版本并退出
+10 -11
View File
@@ -150,7 +150,7 @@ services:
redis:
image: redis
healthcheck:
test: [CMD, redis-cli, ping]
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
@@ -158,7 +158,7 @@ services:
db:
image: postgres
healthcheck:
test: [CMD-SHELL, pg_isready -U postgres]
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
@@ -411,14 +411,13 @@ ports:
```yaml
services:
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
secrets:
- db_root_password
- my_other_secret
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
secrets:
- db_root_password
- my_other_secret
secrets:
db_root_password:
@@ -524,7 +523,7 @@ domainname: your_website.com
hostname: test
mac_address: 08-00-27-00-0C-0A
```
允许容器中运行一些特权命令
允许容器中运行一些特权命令`privileged: true` 会显著扩大容器对宿主机设备和 Linux capability 的访问范围生产环境应优先使用更窄的 `cap_add``devices` 或只读挂载更多说明见 [内核能力机制](../18_security/18.4_kernel_capability.md)
```yaml
privileged: true
+32 -13
View File
@@ -13,7 +13,10 @@ WordPress 是全球最流行的内容管理系统 (CMS)。使用 Docker Compose
```bash
wordpress/
compose.yaml
.env # 环境变量敏感信息
.env # 非敏感环境变量如版本端口
secrets/ # 本地密钥文件生产环境应由密钥管理系统提供
db_root_password.txt
db_password.txt
nginx/ # 可选反向代理配置
nginx.conf
```
@@ -21,7 +24,7 @@ wordpress/
### 11.8.2 编写 `compose.yaml`
这是一个生产可用的最小化配置
这是一个可运行的单机最小配置不是完整生产安全基线正式部署前应固定镜像版本放在反向代理/TLS 后面限制公开端口配置备份与监控并按 WordPress 与插件生命周期做升级测试
```yaml
services:
@@ -38,10 +41,13 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_root_password
- db_password
volumes:
- db_data:/var/lib/mysql
networks:
@@ -50,16 +56,20 @@ services:
# WordPress 服务
wordpress:
# 示例保留 latest 以便读者快速体验生产环境请固定到经过测试的明确版本标签
image: wordpress:latest
container_name: wordpress_app
restart: always
ports:
- "8000:80"
# 本机调试入口生产环境请通过反向代理发布 HTTPS
- "127.0.0.1:8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
WORDPRESS_DB_NAME: wordpress
secrets:
- db_password
volumes:
- wp_data:/var/www/html
# 增加上传文件大小限制
@@ -76,20 +86,29 @@ volumes:
networks:
wp_net:
secrets:
db_root_password:
file: ./secrets/db_root_password.txt
db_password:
file: ./secrets/db_password.txt
```
---
### 11.8.3 配置文件详解
#### 1. 环境变量文件 .env
#### 1. 密钥文件
为了安全不要在 `compose.yaml` 中直接写密码创建 `.env` 文件
不要把数据库密码写入 `compose.yaml``.env`命令行或 GitCompose 官方建议敏感值使用 secrets本地练习可用只读密钥文件模拟
```ini
DB_ROOT_PASSWORD=somestrongrootpassword
DB_PASSWORD=somestronguserpassword
```bash
mkdir -p secrets
printf '%s\n' 'somestrongrootpassword' > secrets/db_root_password.txt
printf '%s\n' 'somestronguserpassword' > secrets/db_password.txt
chmod 600 secrets/*.txt
```
Compose 会自动读取此同级目录下的文件
`secrets/` 加入 `.gitignore`生产环境应改用平台密钥管理能力而不是把真实密码放在项目目录
#### 2. 数据持久化
@@ -192,7 +211,7 @@ WordPress 支持 Redis 缓存以提高性能。
1. 检查 `docker compose logs wordpress`
2. 确认 `.env` 中的密码与 YAML 文件引用一致
3. 确认 `WORDPRESS_DB_HOST` 也是 `db` (服务名)
4. MySQL 8.0 可能需要几秒钟启动WordPress 会自动重试稍等片刻即可
4. MySQL 8.4 可能需要几秒钟启动WordPress 会自动重试稍等片刻即可
#### Q无法上传大文件
+1 -1
View File
@@ -75,7 +75,7 @@ flowchart TD
E["退出"]
U -->|1. REST API| D
K -->|2. gRPC| C
D -->|2. gRPC| C
C -->|3. 准备镜像和 Bundle| B
C -->|4. 启动 Shim| S
S -->|5. 执行| R
+1 -1
View File
@@ -198,7 +198,7 @@ $ docker run --rm --cpus=1 stress --cpu 4
#### Docker cgroups v2 的支持
Docker 19.03+ 默认优先使用 cgroups v2如果系统支持提供更好的性能和资源隔离如果需要明确控制或回退到 v1可以通过 Docker 守护进程配置文件 `/etc/docker/daemon.json` 修改 `cgroup-driver` 参数
Docker 20.10+ 开始支持 cgroups v2如果系统支持提供更好的性能和资源隔离如果需要明确控制或回退到 v1可以通过 Docker 守护进程配置文件 `/etc/docker/daemon.json` 修改 `cgroup-driver` 参数
```json
{
+5 -7
View File
@@ -19,7 +19,7 @@
### 13.2.1 节点
`Kubernetes` 节点是实际工作的点节点可以是虚拟机或者物理机器依赖于一个集群环境每个节点都有一些必要的服务以运行容器组并且它们都可以通过主节点来管理必要服务包括 Dockerkubelet 和代理服务
`Kubernetes` 节点是实际工作的点节点可以是虚拟机或者物理机器依赖于一个集群环境每个节点都有一些必要的组件以运行容器组并且它们都可以通过控制平面来管理必要组件包括 kubelet容器运行时 containerd CRI-O kube-proxyDocker 不再是 Kubernetes 节点的默认基线运行时
#### 容器状态
@@ -29,13 +29,11 @@
主机 IP 需要云平台来查询`Kubernetes` 把它作为状态的一部分来保存如果 `Kubernetes` 没有运行在云平台上节点 ID 就是必需的IP 地址可以变化并且可以包含多种类型的 IP 地址如公共 IP私有 IP动态 IPipv6 等等
##### 节点周期
通常来说节点有 `Pending``Running``Terminated` 三个周期如果 Kubernetes 发现了一个节点并且其可用那么 Kubernetes 就把它标记为 `Pending`然后在某个时刻Kubernetes 将会标记其为 `Running`节点的结束周期称为 `Terminated`一个已经 `Terminated` 的节点不会接受和调度任何请求并且已经在其上运行的容器组也会删除
##### 节点状态
节点的状态通过一组条件Conditions来描述主要条件包括 `Ready`kubelet 健康且可以接收 Pod`MemoryPressure`内存不足`DiskPressure`磁盘不足 `PIDPressure`进程数过多其中 `Ready` 条件最为关键值为 `True` 表示节点健康可调度`False` 表示节点异常`Unknown` 表示节点控制器超过一定时间未收到心跳
`Pending` / `Running` / `Succeeded` / `Failed` / `Unknown` **Pod 的生命周期阶段**`pod.status.phase`不是节点状态节点本身没有 `Terminated` 状态
节点的状态通过一组条件Conditions来描述主要条件包括 `Ready`kubelet 健康且可以接收 Pod`MemoryPressure`内存不足`DiskPressure`磁盘不足 `PIDPressure`进程数过多其中 `Ready` 条件最为关键值为 `True` 表示节点健康可调度`False` 表示节点异常`Unknown` 表示节点控制器超过一定时间未收到心跳被标记为 `Unknown` 较长时间的节点其上的 Pod 会被驱逐重新调度到其他节点
#### 节点管理
@@ -166,7 +164,7 @@ spec:
spec:
containers:
- name: nginx
image: nginx:1.30
image: nginx:1.28
ports:
- containerPort: 80
```
+6 -1
View File
@@ -62,6 +62,7 @@ Ingress 资源仍可正常使用,但建议新项目直接采用 Gateway API。
* **PVC (Persistent Volume Claim)**用户申请存储的声明
* **PV (Persistent Volume)**实际的存储资源 (NFSAWS EBSCeph )
* **StorageClass**定义存储类支持动态创建 PV
* **VolumeAttributesClass**Kubernetes 1.34 GA用于在 CSI 驱动支持 `ModifyVolume` 时动态调整卷属性例如性能等级或服务质量参数
### 13.4.4 Horizontal Pod Autoscaling
@@ -91,7 +92,7 @@ spec:
### 13.4.5 ConfigMap Secret
* **ConfigMap**存储非机密的配置数据 (配置文件环境变量)
* **Secret**存储机密数据 (密码Token证书) Etcd 中加密存储
* **Secret**存储机密数据 (密码Token证书)Secret 值默认只是 Base64 编码并存入 etcd生产环境应显式启用 etcd 静态加密或 KMS
通过将配置与镜像分离保证了容器的可移植性
@@ -114,3 +115,7 @@ metadata:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
```
### 13.4.7 Sidecar Containers
Kubernetes 1.33 Sidecar Containers 进入 GA它们通过 `initContainers` 中带 `restartPolicy: Always` 的容器表达既保留 init container 的启动顺序又会在主容器生命周期内持续运行对于旧集群或不需要启动顺序控制的场景仍可使用普通多容器 Pod
+2 -2
View File
@@ -31,7 +31,7 @@ spec:
spec:
containers:
- name: nginx
image: nginx:1.30
image: nginx:1.28
ports:
- containerPort: 80
```
@@ -73,7 +73,7 @@ kubectl get svc nginx-service
### 13.5.4 步骤 3模拟滚动更新
修改 `nginx-deployment.yaml`将镜像版本改为 `nginx:1.30-alpine`
修改 `nginx-deployment.yaml`将镜像版本改为 `nginx:1.28-alpine`
```bash
kubectl apply -f nginx-deployment.yaml
+2
View File
@@ -6,6 +6,8 @@
Kubernetes 的最小调度单位是 `Pod`一个 `Pod` 由一组紧密协作的容器构成它们共享网络命名空间IP 以及部分存储资源也可以根据需要对 Pod 进行端口映射
如果你已经熟悉 Docker可以用以下对照来理解 Kubernetes 的核心概念Docker 中的容器对应 Kubernetes `Pod`一个或多个容器的组合`docker-compose.yml` 的角色类似于 Kubernetes `Deployment` + `Service` 声明`docker run` 的端口映射和网络配置 Kubernetes 中由 `Service` `Ingress` 接管掌握这些映射关系有助于从单机 Docker 平滑过渡到集群编排
本章将分为 5 节介绍 `Kubernetes`
* [简介](13.1_intro.md)
+14 -13
View File
@@ -140,7 +140,7 @@ $ sysctl --system
#### 修改 `kubelet.service`可选IPVS 模式
> **注意**kube-proxy IPVS 模式已在 Kubernetes 1.35 中被标记为弃用并计划在后续版本中移除新部署建议使用默认的 iptables 模式或 nftables 模式Kubernetes 1.31+ 可用以下 IPVS 配置仅供需要兼容旧环境的场景参考
> **注意**kube-proxy IPVS 模式已在 Kubernetes 1.35 中被标记为弃用Kubernetes 1.36 文档仍将其列为已弃用模式并推荐迁移到 nftables新部署使用默认的 iptables 模式或 nftables 模式Kubernetes 1.33+ 稳定以下 IPVS 配置仅供旧环境参考
`/etc/systemd/system/kubelet.service.d/10-proxy-ipvs.conf` 写入以下内容
@@ -174,21 +174,20 @@ $ sudo kubeadm init \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--pod-network-cidr 10.244.0.0/16 \
--cri-socket unix:///run/containerd/containerd.sock \
--v 5 \
--ignore-preflight-errors=all
--v 5
```
* `--pod-network-cidr 10.244.0.0/16` 参数与后续 CNI 插件有关这里以 `flannel` 为例若后续部署其他类型的网络插件请更改此参数
> 执行可能出现错误例如缺少依赖包根据提示安装即可
> `kubeadm` 预检失败应按提示修复缺失依赖内核参数swap 或运行时配置实验环境确需忽略预检时只忽略明确理解且可接受的单项检查不建议使用 `--ignore-preflight-errors=all`
执行成功会输出
```bash
...
[addons] Applied essential addon: CoreDNS
I1116 12:35:13.270407 86677 request.go:538] Throttling request took 181.409184ms, request: POST:https://192.168.199.100:6443/api/v1/namespaces/kube-system/serviceaccounts
I1116 12:35:13.470292 86677 request.go:538] Throttling request took 186.088112ms, request: POST:https://192.168.199.100:6443/api/v1/namespaces/kube-system/configmaps
I1116 12:35:13.270407 86677 request.go:538] Throttling request took 181.409184ms, request: POST:https://<CONTROL_PLANE_HOST>:6443/api/v1/namespaces/kube-system/serviceaccounts
I1116 12:35:13.470292 86677 request.go:538] Throttling request took 186.088112ms, request: POST:https://<CONTROL_PLANE_HOST>:6443/api/v1/namespaces/kube-system/configmaps
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
@@ -205,8 +204,8 @@ Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
--discovery-token-ca-cert-hash sha256:5edb316fd0d8ea2792cba15cdf1c899a366f147aa03cba52d4e5c5884ad836fe
kubeadm join <CONTROL_PLANE_HOST>:6443 --token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<DISCOVERY_TOKEN_CA_CERT_HASH>
```
#### node 工作节点
@@ -218,12 +217,14 @@ $ systemctl enable containerd
$ systemctl start containerd
$ kubeadm join 192.168.199.100:6443 \
--token cz81zt.orsy9gm9v649e5lf \
--discovery-token-ca-cert-hash sha256:5edb316fd0d8ea2792cba15cdf1c899a366f147aa03cba52d4e5c5884ad836fe \
$ kubeadm join <CONTROL_PLANE_HOST>:6443 \
--token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<DISCOVERY_TOKEN_CA_CERT_HASH> \
--cri-socket unix:///run/containerd/containerd.sock
```
其中 `<CONTROL_PLANE_HOST>``<TOKEN>` `<DISCOVERY_TOKEN_CA_CERT_HASH>` 应使用你自己的 `kubeadm init` 输出不要复用示例值
### 14.1.7 查看服务
所有服务启动后通过 `crictl` 查看本地实际运行的容器这些服务大概分为三类主节点服务工作节点服务和其它服务
@@ -273,9 +274,9 @@ $ kubectl get node -o yaml | grep CIDR
podCIDRs:
```
```bash
# 注意v0.28.2 编写本文档时的最新版本请根据需要替换为当前版本
# 注意以下以 v0.28.4 示例安装前请到 releases 页面核验当前版本
# 参见 https://github.com/flannel-io/flannel/releases
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.28.2/Documentation/kube-flannel.yml
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.28.4/Documentation/kube-flannel.yml
```
### 14.1.10 master 节点默认不能运行 pod
+20 -18
View File
@@ -24,12 +24,12 @@
```bash
# 安装 cri-dockerd
# 注意v0.3.24 为编写本文档时的最新版本请根据需要替换为当前版本
# 注意以下以 v0.4.3 为示例安装前请到 releases 页面核验当前版本和校验值
# 参见 https://github.com/Mirantis/cri-dockerd/releases
$ cd /tmp
$ wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.24/cri-dockerd-0.3.24.amd64.tgz
$ tar xzvf cri-dockerd-0.3.24.amd64.tgz
$ wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.3/cri-dockerd-0.4.3.amd64.tgz
$ tar xzvf cri-dockerd-0.4.3.amd64.tgz
$ sudo mv cri-dockerd/cri-dockerd /usr/local/bin/
# 下载并安装 systemd service 文件
@@ -54,12 +54,12 @@ $ sudo /usr/local/bin/cri-dockerd --version
```bash
# 安装 cri-dockerd
# 注意v0.3.24 为编写本文档时的最新版本请根据需要替换为当前版本
# 注意以下以 v0.4.3 为示例安装前请到 releases 页面核验当前版本和校验值
# 参见 https://github.com/Mirantis/cri-dockerd/releases
$ cd /tmp
$ wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.24/cri-dockerd-0.3.24.amd64.tgz
$ tar xzvf cri-dockerd-0.3.24.amd64.tgz
$ wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.3/cri-dockerd-0.4.3.amd64.tgz
$ tar xzvf cri-dockerd-0.4.3.amd64.tgz
$ sudo mv cri-dockerd/cri-dockerd /usr/local/bin/
# 下载并安装 systemd service 文件
@@ -171,7 +171,7 @@ $ sysctl --system
#### 修改 `kubelet.service`可选IPVS 模式
> **注意**kube-proxy IPVS 模式已在 Kubernetes 1.35 中被标记为弃用并计划在后续版本中移除新部署建议使用默认的 iptables 模式或 nftables 模式Kubernetes 1.31+ 可用以下 IPVS 配置仅供需要兼容旧环境的场景参考
> **注意**kube-proxy IPVS 模式已在 Kubernetes 1.35 中被标记为弃用Kubernetes 1.36 文档仍将其列为已弃用模式并推荐迁移到 nftables新部署使用默认的 iptables 模式或 nftables 模式Kubernetes 1.33+ 稳定以下 IPVS 配置仅供旧环境参考
`/etc/systemd/system/kubelet.service.d/10-proxy-ipvs.conf` 写入以下内容
@@ -200,22 +200,21 @@ $ sudo systemctl daemon-reload
$ sudo kubeadm init --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--pod-network-cidr 10.244.0.0/16 \
--cri-socket unix:///var/run/cri-dockerd.sock \
--v 5 \
--ignore-preflight-errors=all
--v 5
```
* `--cri-socket unix:///var/run/cri-dockerd.sock` 参数指定使用 cri-dockerd 作为容器运行时接口
* `--pod-network-cidr 10.244.0.0/16` 参数与后续 CNI 插件有关这里以 `flannel` 为例若后续部署其他类型的网络插件请更改此参数
> 执行可能出现错误例如缺少依赖包根据提示安装即可
> `kubeadm` 预检失败应按提示修复缺失依赖内核参数swap 或运行时配置实验环境确需忽略预检时只忽略明确理解且可接受的单项检查不建议使用 `--ignore-preflight-errors=all`
执行成功会输出
```bash
...
[addons] Applied essential addon: CoreDNS
I1116 12:35:13.270407 86677 request.go:538] Throttling request took 181.409184ms, request: POST:https://192.168.199.100:6443/api/v1/namespaces/kube-system/serviceaccounts
I1116 12:35:13.470292 86677 request.go:538] Throttling request took 186.088112ms, request: POST:https://192.168.199.100:6443/api/v1/namespaces/kube-system/configmaps
I1116 12:35:13.270407 86677 request.go:538] Throttling request took 181.409184ms, request: POST:https://<CONTROL_PLANE_HOST>:6443/api/v1/namespaces/kube-system/serviceaccounts
I1116 12:35:13.470292 86677 request.go:538] Throttling request took 186.088112ms, request: POST:https://<CONTROL_PLANE_HOST>:6443/api/v1/namespaces/kube-system/configmaps
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
@@ -232,8 +231,8 @@ Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
--discovery-token-ca-cert-hash sha256:5edb316fd0d8ea2792cba15cdf1c899a366f147aa03cba52d4e5c5884ad836fe
kubeadm join <CONTROL_PLANE_HOST>:6443 --token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<DISCOVERY_TOKEN_CA_CERT_HASH>
```
#### node 工作节点
@@ -241,10 +240,13 @@ kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
**另一主机** 重复 **部署** 小节以前的步骤安装配置好 kubelet根据提示加入到集群
```bash
$ kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
--discovery-token-ca-cert-hash sha256:5edb316fd0d8ea2792cba15cdf1c899a366f147aa03cba52d4e5c5884ad836fe
$ kubeadm join <CONTROL_PLANE_HOST>:6443 --token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<DISCOVERY_TOKEN_CA_CERT_HASH> \
--cri-socket unix:///var/run/cri-dockerd.sock
```
其中 `<CONTROL_PLANE_HOST>``<TOKEN>` `<DISCOVERY_TOKEN_CA_CERT_HASH>` 应使用你自己的 `kubeadm init` 输出不要复用示例值
### 14.2.7 查看服务
所有服务启动后查看本地实际运行的 Docker 容器这些服务大概分为三类主节点服务工作节点服务和其它服务
@@ -290,9 +292,9 @@ $ kubectl get node -o yaml | grep CIDR
podCIDRs:
```
```bash
# 注意v0.28.2 编写本文档时的最新版本请根据需要替换为当前版本
# 注意以下以 v0.28.4 示例安装前请到 releases 页面核验当前版本
# 参见 https://github.com/flannel-io/flannel/releases
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.28.2/Documentation/kube-flannel.yml
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.28.4/Documentation/kube-flannel.yml
```
### 14.2.10 master 节点默认不能运行 pod
+1 -1
View File
@@ -17,7 +17,7 @@ K3s 的安装非常简单,官方提供了便捷的安装脚本。
#### 脚本安装
K3s 提供了极为便捷的安装脚本
K3s 提供了极为便捷的安装脚本该命令会从网络下载脚本并直接交给 `sh` 执行生产环境建议先下载审查脚本内容并按官方文档固定版本或安装参数
```bash
curl -sfL https://get.k3s.io | sh -
+10 -4
View File
@@ -29,14 +29,20 @@ $ kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-pro
### 14.7.3 登录
创建管理员服务账户并获取登录令牌
为历史 Dashboard 创建只读服务账户并获取短期登录令牌不要为 Dashboard 创建 `cluster-admin` 绑定如果确实需要临时管理员权限应走单独的 break-glass 审批和审计流程
```bash
$ kubectl create sa dashboard-admin -n kubernetes-dashboard
$ kubectl create sa dashboard-readonly -n kubernetes-dashboard
$ kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin
$ kubectl create clusterrole dashboard-readonly \
--verb=get,list,watch \
--resource=pods,deployments,services,configmaps,namespaces,nodes
$ kubectl create token dashboard-admin -n kubernetes-dashboard
$ kubectl create clusterrolebinding dashboard-readonly \
--clusterrole=dashboard-readonly \
--serviceaccount=kubernetes-dashboard:dashboard-readonly
$ kubectl create token dashboard-readonly -n kubernetes-dashboard --duration=1h
```
将输出的令牌粘贴到登录页面即可登录
+3 -1
View File
@@ -240,8 +240,10 @@ $ kubectl config set-context --current --namespace=my-namespace
# 显示集群信息
$ kubectl cluster-info
# 显示完整的集群状态包括所有组件
# 显示完整的集群状态componentstatuses 1.19 起已弃用建议使用下方替代命令
$ kubectl get componentstatuses
# 推荐替代
$ kubectl get --raw='/readyz?verbose'
```
### 14.8.15 version
+6
View File
@@ -5,6 +5,7 @@
你可以使用以下几种方式部署 Kubernetes接下来的小节会对各种方式进行详细介绍
* [使用 kubeadm 部署 (CRI 使用 containerd)](14.1_kubeadm.md)
* Kubernetes 也支持 CRI-O 等符合 CRI 的运行时本文以 containerd 为主线
* [使用 kubeadm 部署 (使用 Docker)](14.2_kubeadm-docker.md)
* [ Docker Desktop 使用](14.3_docker-desktop.md)
* [Kind - Kubernetes IN Docker](14.4_kind.md)
@@ -12,3 +13,8 @@
* [一步步部署 Kubernetes 集群](14.6_systemd.md)
* [部署 Dashboard](14.7_dashboard.md)
* [Kubernetes 命令行 kubectl](14.8_kubectl.md)
除了上述方式企业生产环境中还有两个常见的部署工具值得关注
* **[KubeKey](https://github.com/kubesphere/kubekey)**KubeSphere 社区开源的集群部署工具(CNCF 认证),支持一条命令从裸机部署到高可用集群,内置对 containerd 和多 Linux 发行版的适配,适合需要快速搭建私有化 Kubernetes 的团队。
* **[RKE2](https://docs.rke2.io/)**SUSE Rancher 出品的安全加固型 Kubernetes 发行版,默认启用 CIS 基准合规、SELinux 支持和 etcd 自动快照,适合对安全审计有严格要求的企业场景。
+1 -1
View File
@@ -7,7 +7,7 @@
| 特性 | Google GKE | AWS EKS | Azure AKS |
| :--- | :--- | :--- | :--- |
| **版本更新** | 最快通常是 K8s 新特性的首发地 | 相对保守注重稳定性 | 跟随社区更新速度适中 |
| **控制平面管理** | 全托管自动升级$0.10/h Free Tier 抵扣| 托管$0.10/h | 全托管控制平面免费 |
| **控制平面管理** | 全托管自动升级$0.10/h Free Tier 抵扣| 托管$0.10/h | 全托管Free 层适合开发/测试Standard/Premium 层默认包含 Uptime SLA |
| **节点管理** | GKE Autopilot 模式完全托管节点 | Managed Node Groups 简化管理 | Virtual Machine Scale Sets |
| **网络模型** | VPC-native, 性能优秀 | AWS VPC CNI, Pod 直接获取 VPC IP | Azure CNI (消耗 IP ) Kubenet |
| **集成度** | GCP 数据分析AI 服务集成紧密 | AWS IAM, ALB, CloudWatch 集成深度高 | Active Directory, Azure DevOps 集成好 |
+1 -1
View File
@@ -40,7 +40,7 @@
Kubernetes 作为一个容器编排系统为了屏蔽底层不同容器运行时的实现差异引入了 CRIContainer Runtime Interface标准
- 早期版本中Kubernetes 默认使用 docker 作为运行时通过一个名为 `dockershim` 的桥接组件对接 DockerDocker 再对接 containerd
- 随着 containerd 原生支持了 CRI 插件Kubernetes 开始直接与 containerd 通信去掉了 `dockershim` `dockerd` 的中间层这就是为什么从 Kubernetes 1.24+ 开始弃用 Docker引发了广泛关注实际上 Kubernetes 只是弃用 `dockershim`底层依然在使用 Docker 基因中诞生的 containerd参见 [Kubernetes 官方文档](https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd)。
- 随着 containerd 原生支持了 CRI 插件Kubernetes 开始直接与 containerd 通信去掉了 `dockershim` `dockerd` 的中间层这就是为什么从 Kubernetes 1.24+ 开始弃用 Docker引发了广泛关注实际上 Kubernetes 只是弃用 `dockershim`底层可以使用 containerdCRI-O 等符合 CRI 的运行时参见 [Kubernetes 官方文档](https://kubernetes.io/docs/setup/production-environment/container-runtimes/)。
- containerd 2.0+ 移除了已弃用的 CRI v1alpha2 接口仅保留 CRI v1Kubernetes 1.26+ 仅支持 CRI v1如果集群中仍有依赖 CRI v1alpha2 的组件升级 containerd 2.x 前需先完成迁移containerd 2.3+ 2.x 系列首个 LTS 版本支持从 1.7 LTS 直接升级生产环境推荐使用详见 [containerd 发布说明](https://github.com/containerd/containerd/releases)。
### 17.6.3 为什么直接使用 containerd
+8 -1
View File
@@ -1,12 +1,19 @@
# 第十七章 容器其它生态
> **版本说明**本章介绍的工具和运行时PodmanBuildahSkopeocontainerdKata ContainersgVisorWasmEdge 都保持活跃的开发建议
> **版本说明核验日期2026-05-16**本章介绍的工具和运行时PodmanBuildahSkopeocontainerdKata ContainersgVisorWasmEdge 都保持活跃的开发建议
> - 查阅各项目官方文档获取最新版本
> - 在生产环境使用前验证版本兼容性
> - 关注官方发布说明了解重大变更
本章将介绍 Docker Kubernetes 之外的容器生态技术
同时Docker 自身的生态也在向云构建AI 本地推理和企业级桌面安全扩展当前需要额外关注
* **Docker Model Runner** Docker Desktop / Docker Engine 中管理运行和服务本地 AI 模型支持 OpenAI Ollama 兼容 API并可将 GGUFSafetensors 等模型文件作为 OCI Artifact 管理
* **Docker Build Cloud**通过远程 BuildKit 和共享构建缓存加速本地与 CI 构建适合多平台镜像和团队共享缓存场景
* **Docker Offload**把容器构建和运行卸载到云端适合 VDI受限本机或不支持嵌套虚拟化的开发环境
* **Hardened Docker Desktop / Enhanced Container Isolation (ECI)**通过更强的命名空间隔离敏感挂载保护和系统调用限制降低桌面容器逃逸风险
## 本章内容
* [Fedora CoreOS 简介](17.1_coreos_intro.md)
-7
View File
@@ -1,7 +0,0 @@
## Fedora CoreOS
`CoreOS` 是一个专门为安全和大规模运行容器化工作负载而构建的新 Fedora 版本它继承了 Fedora Atomic Host CoreOS Container Linux 的优势
`CoreOS` 的安装文件和运行依赖非常小它提供了精简的 Linux 系统它使用 Linux 容器在更高的抽象层来管理你的服务而不是通过常规的包管理工具 `yum` `apt` 来安装包
同时`CoreOS` 几乎可以运行在任何平台`VirtualBox` `Amazon EC2` `QEMU/KVM` `VMware` `Bare Metal` `OpenStack`
+9 -4
View File
@@ -56,7 +56,7 @@ Rootless 模式允许在完全局限于非 `root` 用户的环境中运行 Docke
#### 配置运行 Rootless Docker
要在非 root 环境中运行 Docker需要简单几步
要在非 root 环境中运行 Docker需要先满足宿主机条件安装 `newuidmap` / `newgidmap`并在 `/etc/subuid` `/etc/subgid` 中为该用户分配足够的 subordinate UID/GID若系统级 Docker daemon 仍在运行命令行也仍可能连到 rootful socket因此应明确切换 Docker context `DOCKER_HOST`
1. 安装必要的依赖通常是 `uidmap` 工具包以便系统支持 `newuidmap` `newgidmap`
```bash
@@ -68,9 +68,11 @@ $ sudo apt-get install uidmap
$ su - testuser
```
3. 运行 Docker 官方提供的 Rootless 安装脚本
3. 下载并检查 Docker 官方提供的 Rootless 安装脚本确认来源和内容后再执行
```bash
$ curl -fsSL https://get.docker.com/rootless | sh
$ curl -fsSL https://get.docker.com/rootless -o install-rootless-docker.sh
$ less install-rootless-docker.sh
$ sh install-rootless-docker.sh
```
4. 配置环境变量指向新创建的私有 socket
@@ -80,6 +82,9 @@ $ docker version
```
安装并暴露相应的配置后该用户的环境将能独立启动属于他自己的 Docker Daemon即使由于某些未知 0-Day 漏洞使得攻击者突破了容器他们也只会受限于 `testuser` 这个非特权用户所在的有限系统环境内
> [!NOTE]
> Rootless 模式不是无条件替代 rootful Docker端口绑定网络cgroup存储驱动和系统服务自启动能力都受发行版内核与 systemd 用户服务配置影响生产环境应先验证具体工作负载并用 `loginctl enable-linger <user>` 等方式显式配置开机自启动
### 18.3.4 授权插件Authorization Plugin与访问策略
在企业环境中 Docker 守护进程的访问控制往往不仅限于文件系统权限还需要更细粒度的授权策略**Authorization Plugin** 机制允许在 API 层级对请求进行拦截和审批
@@ -110,4 +115,4 @@ $ docker version
### 18.3.5 结语
保障 Docker 服务端的安全主要是做减法关闭不必要的网络监听点严管 Socket 访问权限而一旦基础系统条件允许**毫不犹豫地在生产环境启用 Rootless 模式** 将是一项划算的安全加固选择
保障 Docker 服务端的安全主要是做减法关闭不必要的网络监听点严管 Socket 访问权限基础系统网络和存储约束都验证通过后Rootless 模式是一项值得优先评估的安全加固选择
+7 -3
View File
@@ -8,7 +8,7 @@
一个普通的 Linux 内核提供了 300 多个系统调用而一个正常运行的容器化应用例如 Nginx 服务通常只会用到几十个调用这就给攻击者留下了大量的闲置入口点来进行内核层的缓冲区溢出攻击
Docker 默认启用了 Seccomp 用预置的 [默认配置文件](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json) 将可以利用的系统调用缩减到了不足一半(默认禁用 44 个危险的系统调用,比如修改时区或重启系统)
Docker 默认启用了 Seccomp使用预置的 [默认配置文件](https://docs.docker.com/engine/security/seccomp/) 作为 allowlist:默认拒绝未显式允许的系统调用,并额外允许常见应用所需的调用。Docker 官方文档将其描述为默认禁用 44 个系统调用(内核与 Docker 版本不同会有差异),例如与内核模块、系统重启或特权命名空间操作相关的调用
如果你对应用的系统调用特征了如指掌你可以为容器定制专属规则
@@ -44,8 +44,8 @@ chmod: /etc/passwd: Operation not permitted
在开启了上述机制的机器上
- **AppArmor**: Docker 为所有启动的应用加载了一个默认的 `docker-default` 模板文件如果你的某些异常写行为比如往特殊的内核心脏目录写入配置不在 AppArmor 许可列表之上即使拥有物理 Root写入同样失败
- **SELinux**: 所有的 Docker 操作强制附加特殊上下文标识标签就算把主机的 `/` 绑定给了黑客的某服务黑客对不属于 Docker 可见的标签的文件进行读写尝试亦会被阻止
- **AppArmor**: 在启用 AppArmor 的系统上Docker 默认为容器加载 `docker-default` profile如果需要自定义策略先用 `apparmor_parser` 加载 profile再通过 `--security-opt apparmor=<profile>` 指定
- **SELinux**: 在启用 SELinux 集成的系统上容器与挂载目录需要正确的 SELinux label绑定挂载时常用 `:z` 表示多个容器共享`:Z` 表示该挂载只给单个容器使用不要对 `/home``/usr` 等系统目录随意使用 `:Z`否则可能破坏宿主机标签
如果想为某些受信任应用施加特定的外部强化文件策略可以通过如下方法指派规则表
@@ -53,6 +53,10 @@ chmod: /etc/passwd: Operation not permitted
$ docker run --rm -it \
--security-opt apparmor=custom-nginx-profile \
nginx
$ docker run --rm -it \
-v "$PWD/html":/usr/share/nginx/html:Z \
nginx
```
### 18.5.3 容器镜像漏洞静态扫描
+68 -59
View File
@@ -20,7 +20,9 @@ Trivy 是由 Aqua Security 开发的开源漏洞扫描器,以其轻量级、
```bash
# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh -o install-trivy.sh
less install-trivy.sh # 先审阅脚本内容
sudo sh install-trivy.sh -b /usr/local/bin
# 扫描本地镜像
trivy image nginx:latest
@@ -58,7 +60,9 @@ Grype 由 Anchore 开发,支持更广泛的软件包管理器和语言。
```bash
# 安装 Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh -o install-grype.sh
less install-grype.sh
sudo sh install-grype.sh -b /usr/local/bin
# 扫描镜像
grype docker:nginx:latest
@@ -77,7 +81,7 @@ Snyk 提供了商业级的安全扫描服务,特别适合企业环境。
**特点**
- 支持开源漏洞和许可证扫描
- 支持开源漏洞许可证扫描和修复建议
- 与多个 Git 平台深度集成GitHubGitLabBitbucket
- 提供修复建议和自动化修复 PR
- 支持 Kubernetes 部署后安全监控
@@ -114,7 +118,7 @@ $ docker scout quickview myapp:latest
| Docker 集成 | 原生 | 需安装 | 需安装 | 需安装 |
| 零依赖 | | | | |
| 离线模式 | | | | |
| 许可证扫描 | | | | |
| 许可证扫描 | | | | |
| 自动修复建议 | | | | |
| 开源免费 | 部分 | | | 部分 |
| IDE 集成 | | | | |
@@ -130,7 +134,9 @@ Syft 是 Anchore 推出的专业 SBOM 生成工具。
**安装**
```bash
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh -o install-syft.sh
less install-syft.sh
sudo sh install-syft.sh -b /usr/local/bin
```
**生成 SBOM**
@@ -234,8 +240,9 @@ cosign generate-key-pair
**签名镜像**
```bash
# 使用私钥签名推送到仓库前
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
# 先推送镜像再使用不可变 digest 签名
IMAGE_DIGEST="myregistry.com/myapp@sha256:<digest>"
cosign sign --key cosign.key "$IMAGE_DIGEST"
# 系统会提示输入私钥密码
```
@@ -243,7 +250,7 @@ cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
```bash
# 使用公钥验证
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
cosign verify --key cosign.pub "$IMAGE_DIGEST"
# 输出结果示例
# Verification successful!
@@ -260,10 +267,10 @@ cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
```bash
# GitHub Actions CI 中无需存储密钥
cosign sign --yes myregistry.com/myapp:v1.0.0
cosign sign --yes "$IMAGE_DIGEST"
# 验证时自动使用 OIDC 令牌验证身份
cosign verify myregistry.com/myapp:v1.0.0 \
cosign verify "$IMAGE_DIGEST" \
--certificate-identity https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
```
@@ -272,16 +279,13 @@ cosign verify myregistry.com/myapp:v1.0.0 \
> **注意DCT 退役时间线**
>
> Docker 已宣布[退役 Content Trust](https://www.docker.com/blog/retiring-docker-content-trust/)。关键节点:
> - 2025 8 月起最早一批 DCT 签名证书开始过期
> - 2025 9 30 日起新注册表不可再启用 DCT
> - **2028 3 31 **DCT 完全移除所有 DCT 数据永久删除
> Docker 已宣布[退役 Content Trust](https://www.docker.com/blog/retiring-docker-content-trust/)Docker Official Images 的部分 DCT 签名证书自 2025 年 8 月起已陆续过期;完整退役节奏应以 Docker 官方公告和你所使用的 registry 服务商文档为准。不要把云厂商特定日期直接当作 Docker 全局时间线
>
> 建议新项目直接使用上文介绍的 **Cosign (Sigstore)** 进行镜像签名现有 DCT 用户应尽早制定迁移计划
> 新项目应优先使用上文介绍的 **Cosign (Sigstore)** registry 原生签名/证明能力现有 DCT 用户应先盘点依赖验证替代方案制定迁移计划
Docker Content Trust 使用 Notary 实现镜像签名 Docker 官方的传统签名解决方案
**启用 DCT**
**历史用法示例不建议新项目采用**
```bash
# 在环境中启用 DCT
@@ -301,8 +305,8 @@ docker push --disable-content-trust myregistry.com/myapp:v1.0.0
# 首次推送时会提示创建 Delegation Key
# 密钥存储在 ~/.docker/trust/private/root_keys/ ~/.docker/trust/private/tuf_keys/
# 查看签名信息
docker inspect --format='{{.RepoDigests}}' myregistry.com/myapp:v1.0.0
# 查看 DCT/Notary 信任数据RepoDigests 只是内容摘要不等于签名验证
docker trust inspect --pretty myregistry.com/myapp:v1.0.0
```
### 18.6.4 供应链安全最佳实践
@@ -316,27 +320,20 @@ RUN apt-get update && apt-get install -y curl
# 推荐固定基础镜像版本和摘要
FROM ubuntu:22.04@sha256:a6d2b38300ce017add71440577d5b0a90460d0e6e0e14...完整 64 位哈希
RUN apt-get update && apt-get install -y curl=7.68.0-1ubuntu1
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
```
#### 2. 构建时扫描
Dockerfile 中集成安全扫描
CI/CD 构建阶段集成安全扫描避免在 Dockerfile 里从分支 URL 下载并执行远程安装脚本
```dockerfile
FROM golang:1.26-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:abcd1234...请替换为实际完整的 64 位摘要哈希
COPY --from=builder /app/app /app
```bash
docker run --rm \
-v "$PWD:/work" \
-w /work \
aquasec/trivy:latest \
fs . --exit-code 1 --severity HIGH,CRITICAL
```
#### 3. 运行时镜像扫描策略
@@ -417,6 +414,7 @@ jobs:
contents: read
packages: write
security-events: write
id-token: write
steps:
- name: Checkout code
@@ -425,7 +423,11 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image
- name: Install Cosign
if: github.event_name == 'push'
uses: sigstore/cosign-installer@v3
- name: Build Docker image for scan
uses: docker/build-push-action@v7
with:
context: .
@@ -462,25 +464,27 @@ jobs:
name: sbom
path: sbom-cyclonedx.json
- name: Sign image with Cosign
if: github.event_name == 'push'
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@v3
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image
if: github.event_name == 'push'
id: build-push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Sign image with Cosign
if: github.event_name == 'push'
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-push.outputs.digest }}
```
#### GitLab CI 工作流示例
@@ -489,8 +493,8 @@ jobs:
stages:
- build
- scan
- sign
- push
- sign
variables:
REGISTRY: registry.gitlab.com
@@ -498,52 +502,57 @@ variables:
build:
stage: build
image: docker:latest
image: docker:<version>-cli@sha256:<docker-cli-digest>
services:
- docker:dind
- name: docker:<version>-dind@sha256:<docker-dind-digest>
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
image: aquasec/trivy:<version>@sha256:<trivy-digest>
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 docker-archive://image.tar
allow_failure: false
scan:grype:
stage: scan
image: anchore/grype:latest
image: anchore/grype:<version>@sha256:<grype-digest>
script:
- grype docker-archive://image.tar
generate:sbom:
stage: scan
image: anchore/syft:latest
image: anchore/syft:<version>@sha256:<syft-digest>
script:
- syft docker-archive://image.tar -o cyclonedx > sbom.xml
artifacts:
reports:
sbom: sbom.xml
sign:
stage: sign
image: gcr.io/projectsigstore/cosign:latest
push:
stage: push
image: docker:<version>-cli@sha256:<docker-cli-digest>
services:
- name: docker:<version>-dind@sha256:<docker-dind-digest>
script:
- cosign sign --key $COSIGN_KEY $IMAGE_NAME:$CI_COMMIT_SHA
- docker load < image.tar
- echo "$REGISTRY_PASSWORD" | docker login --username "$REGISTRY_USER" --password-stdin "$REGISTRY"
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- DIGEST=$(docker buildx imagetools inspect "$IMAGE_NAME:$CI_COMMIT_SHA" --format '{{.Manifest.Digest}}')
- echo "$IMAGE_NAME@$DIGEST" > image-digest.txt
artifacts:
paths:
- image-digest.txt
only:
- main
push:
stage: push
image: docker:latest
services:
- docker:dind
sign:
stage: sign
image: gcr.io/projectsigstore/cosign:<version>@sha256:<cosign-digest>
script:
- docker load < image.tar
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- cosign sign --key $COSIGN_KEY "$(cat image-digest.txt)"
only:
- main
```
+9 -1
View File
@@ -1,6 +1,14 @@
## 本章小结
Docker 的安全性依赖于多层隔离机制的协同工作同时需要用户遵循最佳实践
Docker 的安全性依赖于多层隔离机制的协同工作同时需要用户遵循最佳实践本章涵盖的核心安全维度包括
| 维度 | 关键措施 |
|------|---------|
| **内核隔离** | Namespace 隔离进程/网络/文件系统Cgroups 限制资源使用 |
| **权限控制** | root 运行`--cap-drop ALL` 最小能力集`--read-only` 只读根文件系统 |
| **镜像安全** | 使用可信基础镜像定期扫描漏洞Trivy / Snyk启用 Docker Content Trust 签名验证 |
| **运行时防护** | Seccomp 系统调用过滤AppArmor / SELinux 强制访问控制 |
| **网络隔离** | 自定义 bridge 网络隔离容器通信限制容器对宿主机网络的访问 |
总体来看Docker 容器还是十分安全的特别是在容器内不使用 root 权限来运行进程的话
+3 -3
View File
@@ -160,10 +160,10 @@ $ docker compose up -d
* **节点 CPU 使用率**`100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)`
* **节点内存使用率**`(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100`
* **节点磁盘空间使用率**`(1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})) * 100`
* **容器 CPU**`sum by (name) (rate(container_cpu_usage_seconds_total[5m]))`
* **容器内存**`sum by (name) (container_memory_working_set_bytes)`
* **容器 CPU**`sum by (namespace, pod, container) (rate(container_cpu_usage_seconds_total[5m]))`
* **容器内存**`sum by (namespace, pod, container) (container_memory_working_set_bytes)`
说明不同版本 cAdvisor/Docker label 命名可能存在差异 ( `name``container``container_name`)如果查询为空建议先用 `label_values(container_cpu_usage_seconds_total, __name__)` Prometheus 图形界面查看可用 label
说明不同采集路径 label 命名不同Docker Compose 中独立部署的 cAdvisor 常见容器标签是 `name`Kubernetes kubelet `/metrics/cadvisor` `container_cpu_usage_seconds_total` 等稳定指标使用 `container``pod``namespace`如果查询为空先直接查询 `container_cpu_usage_seconds_total` 样本并 Prometheus 图形界面查看实际 label不要假设存在 `container_name`
#### Targets down 排错清单
+2 -2
View File
@@ -22,7 +22,7 @@ ELK (ElasticsearchLogstashKibana) 是目前业界最流行的开源日志
```yaml
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:9.3.3
image: docker.elastic.co/elasticsearch/elasticsearch:9.4.0
container_name: elasticsearch
environment:
- "discovery.type=single-node"
@@ -36,7 +36,7 @@ services:
- logging
kibana:
image: docker.elastic.co/kibana/kibana:9.3.3
image: docker.elastic.co/kibana/kibana:9.4.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
@@ -66,8 +66,11 @@ docker stats nginx redis
# 一次性输出不进入交互模式
docker stats --no-stream
# 指定刷新间隔单位默认 1
docker stats --no-stream --interval 2
# 2 秒采样一次docker stats 没有 --interval 选项
while true; do
docker stats --no-stream
sleep 2
done
# 格式化输出使用 Go 模板
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" --no-stream
@@ -100,6 +103,8 @@ done
cAdvisor Google 开发的容器监控工具提供比 `docker stats` 更详细的性能数据
> ** 安全权衡提示**下面的示例为简化部署使用了 `privileged: true` [ 18 ](../18_security/README.md) 最小权限 / `cap_drop=all`的原则相冲突生产环境建议改为按需授予能力 `cap_add: [SYS_ADMIN]` `device_cgroup_rules` 与精确的 `devices``volumes` 挂载并将 cAdvisor 部署在独立的监控网络中如何选择请参考 [18.4 ](../18_security/18.4_kernel_capability.md) 关于内核能力capabilities的细化授权
**Docker Compose 部署 cAdvisor**
```yaml
@@ -180,6 +185,8 @@ services:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- monitoring
@@ -256,7 +263,7 @@ scrape_configs:
- job_name: 'docker'
static_configs:
- targets: ['localhost:9323']
- targets: ['host.docker.internal:9323']
```
**常用的 Prometheus 查询PromQL**
@@ -274,7 +281,7 @@ rate(container_network_receive_bytes_total[5m]) / 1024 / 1024
rate(container_network_transmit_bytes_total[5m]) / 1024 / 1024
# 容器磁盘读取速率MB/s
rate(container_fs_io_current[5m]) / 1024 / 1024
rate(container_fs_reads_bytes_total[5m]) / 1024 / 1024
# CPU 限流情况
rate(container_cpu_cfs_throttled_seconds_total[5m])
@@ -623,5 +630,6 @@ docker exec <container_id> ping 8.8.8.8
docker inspect <container_id> | grep -A 10 NetworkSettings
# 解决方案更换网络驱动或调整 MTU
docker run --net=host myapp:latest # 使用宿主机网络性能最佳
# host 网络可降低网络栈开销但会放弃容器网络隔离仅在明确接受安全边界变化时使用
docker run --net=host myapp:latest
```
+3 -3
View File
@@ -36,7 +36,7 @@ Debian GNU/Linux 13
![Ubuntu 操作系统](../_images/ubuntu-logo.jpg)
`Ubuntu` 是一个以桌面应用为主的 `GNU/Linux` 操作系统其名称来自非洲南部祖鲁语或豪萨语的 ubuntu 一词 (官方译名 友帮拓另有 吾帮托乌班图有奔头 乌斑兔 等译名)`Ubuntu` 意思是 人性 以及 我的存在是因为大家的存在是非洲传统的一种价值观类似华人社会的 仁爱 思想`Ubuntu` 基于 `Debian` 发行版和 `GNOME/Unity` 桌面环境 `Debian` 的不同在于它每 6 个月会发布一个新版本 2 年推出一个长期支持 **(Long Term SupportLTS)** 版本一般支持 3 时间
`Ubuntu` 是一个以桌面应用为主的 `GNU/Linux` 操作系统其名称来自非洲南部祖鲁语或豪萨语的 ubuntu 一词 (官方译名 友帮拓另有 吾帮托乌班图有奔头 乌斑兔 等译名)`Ubuntu` 意思是 人性 以及 我的存在是因为大家的存在是非洲传统的一种价值观类似华人社会的 仁爱 思想`Ubuntu` 基于 `Debian` 发行版和 `GNOME/Unity` 桌面环境 `Debian` 的不同在于它每 6 个月会发布一个新版本 2 年推出一个长期支持 **(Long Term SupportLTS)** 版本LTS 版本通常提供 5 标准安全维护Ubuntu Pro/ESM 可进一步延长安全覆盖
#### 使用 Ubuntu 官方镜像
@@ -74,8 +74,8 @@ E: Unable to locate package curl
```bash
root@7d93de07bf76:/# apt-get update
Get:1 http://archive.ubuntu.com/ubuntu noble InRelease [256 kB]
Get:2 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Get:1 http://archive.ubuntu.com/ubuntu resolute InRelease [256 kB]
Get:2 http://security.ubuntu.com/ubuntu resolute-security InRelease [126 kB]
...
Fetched 25.8 MB in 8s (3215 kB/s)
Reading package lists... Done
+1 -1
View File
@@ -2,7 +2,7 @@
### 20.4.1 CentOS 系统简介
`CentOS` `Fedora` 都是基于 `Redhat` 的常见 Linux 分支`CentOS` 目前企业级服务器的常用操作系统`Fedora` 则主要面向个人桌面用户
`CentOS` `Fedora` 都是基于 `Redhat` 的常见 Linux 分支传统 `CentOS Linux` 是企业级服务器的常用操作系统但相关 Docker 官方镜像已停止维护新部署通常应评估 Rocky LinuxAlmaLinuxCentOS Stream 或直接使用 RHEL/UBI 等替代方案`Fedora` 则主要面向个人桌面用户
![CentOS 操作系统](../_images/centos-logo.png)
+2 -1
View File
@@ -66,7 +66,8 @@ deploy_staging:
stage: deploy
image: dtzar/helm-kubectl
script:
- kubectl config set-cluster k8s --server=$KUBE_URL --insecure-skip-tls-verify=true
- printf '%s' "$KUBE_CA_PEM" > kube-ca.crt
- kubectl config set-cluster k8s --server=$KUBE_URL --certificate-authority=kube-ca.crt --embed-certs=true
- kubectl config set-credentials admin --token=$KUBE_TOKEN
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
+49 -1
View File
@@ -34,11 +34,59 @@ jobs:
```
该示例会在 GitHub Actions 中构建当前仓库的 Docker 镜像不推送到 registry
### 21.2.2 最佳实践
### 21.2.2 构建并推送到 Registry
实际项目中通常需要在 CI 中构建镜像并推送到容器 Registry以下示例展示了多阶段构建 + 登录 + 推送的完整流程
```yaml
name: Build and Push
on:
push:
branches: [main]
permissions:
contents: read
packages: write
jobs:
build-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/setup-buildx-action@v4
- uses: docker/build-push-action@v7
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
```
关键说明
* `docker/login-action` 负责认证支持 Docker HubGHCRECR 等主流 Registry
* `cache-from` / `cache-to` 使用 GitHub Actions 原生缓存`type=gha`无需额外配置即可加速增量构建
* 标签同时使用 commit hash `latest`兼顾版本追溯与部署便利
### 21.2.3 最佳实践
* 固定 action 的主版本例如 `@v4` / `@v6`避免使用 `@master` 这类浮动引用
* 设置最小权限例如 `contents: read`需要写入权限时再打开
* 需要依赖缓存时优先使用官方支持的缓存方案例如针对语言包管理器的 cache BuildKit cache
* 敏感凭据Registry 密码Deploy Key 一律通过 `secrets` 注入禁止硬编码
* 多平台构建可在 `build-push-action` 中添加 `platforms: linux/amd64,linux/arm64`
如果你需要在某个步骤里直接运行容器镜像而不是构建镜像可以使用 `docker://` 语法
+5 -4
View File
@@ -362,13 +362,14 @@ services:
environment:
POSTGRES_DB: myappdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres_password
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
ports:
- "5432:5432"
# 只暴露给本机调试生产环境优先不发布数据库端口
- "127.0.0.1:5432:5432"
networks:
- backend
restart: unless-stopped
@@ -384,7 +385,7 @@ services:
depends_on:
- postgres
environment:
PGPASSWORD: postgres_password
PGPASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
volumes:
- ./backups:/backups
command: |
@@ -543,7 +544,7 @@ logfile ""
# 客户端输出缓冲限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
```
+26
View File
@@ -1,5 +1,31 @@
# 修订记录
* 1.9.2 2026-05-16
* 刷新 Docker 生态说明更新 Fedora CoreOSPodmancontainerd 等章节中的维护状态
* 修复镜像事实演示 TLS 密钥等后续审阅发现的问题
* 1.9.1 2026-05-08
* 使用 `browser-actions/setup-chrome` 替代 Ubuntu runner 上不稳定的 Chromium snap 安装
* 修正 Docker Engine 29 日期TLS 协议healthcheck 与交互式调试示例
* 1.9.0 2026-05-02
* 更新 Docker Engine 29nginxMySQL 8.4 LTSNode.js 22 LTS Kubernetes 相关版本说明
* 补充 ipvlannftablesGateway APIDocker Scout镜像安全与供应链安全内容
* 修复 Docker Hub 限流etcdctl APICompose healthcheckDocker Debug 等时效性内容
* 1.8.0 2026-04-27
* 补全 Dockerfile 指令参考与多处章节编号标题层级代码块和表格格式
* 增加预览 PDF 自动发布流程修复 mdpress 端口和导出相关配置
* 1.7.5 2026-04-05
* 将失效的 AtomHub 镜像替换为可用镜像源
* 1.7.4 2026-03-31
* 修复标题层级格式
* 1.7.3 2026-03-29
* 修复 Wikipedia URL 编码
* 1.7.2 2026-03-28
* 修正 macOSWindowsCompose Kubernetes 章节中的时效性内容和错误前提
* 收缩越界网络内容补充 bind mounttmpfs 与端口映射的关键限制说明
+1
View File
@@ -101,6 +101,7 @@ docker run -it --rm -p 4000:80 ccr.ccs.tencentyun.com/dockerpracticesig/docker_p
| 书名 | 与本书的关系 |
|------|------------|
| [前线部署工程实践指南](https://yeasy.gitbook.io/forward_deployed_engineering_guide/) | 讲容器、Kubernetes、GitOps、IaC、供应链、安全、观测与 SLO 如何组合成可交付、可审计、可运营的部署工程体系 |
| [智能体 Harness 工程指南](https://yeasy.gitbook.io/harness_engineering_guide) | Agent 基础设施中的容器化部署与隔离 |
| [大模型安全权威指南](https://yeasy.gitbook.io/ai_security_guide) | 容器安全与 AI 系统安全的交叉实践 |
| [区块链技术指南](https://yeasy.gitbook.io/blockchain_guide) | 区块链节点的容器化部署 |
+2 -1
View File
@@ -1,6 +1,6 @@
## 目录
* [前言](README.md)
* [Docker 从入门到实践](README.md)
* [修订记录](CHANGELOG.md)
* [如何贡献](CONTRIBUTING.md)
@@ -191,6 +191,7 @@
* [附录](appendix/README.md)
* [附录一常见问题与错误速查](appendix/faq/README.md)
* [常见错误处理](appendix/faq/errors.md)
* [附录二热门镜像介绍](appendix/repo/README.md)
* [Ubuntu](appendix/repo/ubuntu.md)
* [CentOS](appendix/repo/centos.md)
+1 -1
View File
@@ -14,7 +14,7 @@
#### 使用多阶段构建
Docker 17.05 以上版本中你可以使用[多阶段构建](../07_dockerfile/7.17_multistage_builds.md)来减少所构建镜像的大小
现代 Docker 版本中你可以使用[多阶段构建](../07_dockerfile/7.17_multistage_builds.md)来减少所构建镜像的大小该能力最早在 Docker 17.05 引入如今已是编写生产镜像的默认实践之一新项目还应结合 BuildKit 缓存挂载secret 挂载和多平台构建能力一起评估
#### 避免安装不必要的包
+6 -1
View File
@@ -14,7 +14,7 @@
```bash
$ sudo kill -SIGHUP $(pidof dockerd)
```
此时 dockerd 会在日志中输更多信息供分析
此时 dockerd 会在日志中输更多信息供分析
### 检查内核日志
@@ -42,8 +42,13 @@ $ docker system prune
```
如果你只是想 恢复出厂设置 Docker Desktop 里也提供了相应入口
只有在确认要丢弃本机全部镜像容器卷和构建缓存并且已经停止 Docker 服务完成备份或导出后才考虑删除 Docker 数据目录
```bash
$ sudo systemctl stop docker containerd
$ sudo cp -a /var/lib/docker /var/lib/docker.backup
$ sudo rm -rf /var/lib/docker
$ sudo systemctl start docker
```
### 常见故障排查
+7 -4
View File
@@ -1,5 +1,7 @@
# 附录一常见问题与错误速查
更多错误排查条目见 [常见错误处理](errors.md)
## 镜像相关
### 如何批量清理临时镜像文件
@@ -23,13 +25,13 @@
* 安装各个软件时候要指定准确的版本号并避免引入不需要的依赖
* 从安全角度考虑应用要尽量使用系统的库和依赖
* 如果安装应用时候需要配置一些特殊的环境变量在安装后要还原不需要保持的变量值
* 使用 Dockerfile 创建镜像时候要添加dockerignore 文件或使用干净的工作目录
* 使用 Dockerfile 创建镜像时候要添加 .dockerignore 文件或使用干净的工作目录
更多内容请查看 [Dockerfile 最佳实践](../best_practices.md)
### 碰到网络问题无法 pull 镜像命令行指定 http\_proxy 无效
Docker 配置文件中添加 `export http_proxy="http://<PROXY_HOST>:<PROXY_PORT>"`之后重启 Docker 服务即可
先区分代理要作用在哪一层Docker daemon 拉取镜像时推荐在 `daemon.json` `proxies` 字段或 systemd drop-in 中配置 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`然后重启 Docker 服务Docker CLI构建过程和容器内应用的代理应分别使用 `~/.docker/config.json``--build-arg` `docker run --env` 配置不要把 `export http_proxy=...` 当作 `daemon.json` 内容写入
## 容器相关
@@ -79,8 +81,9 @@ $ docker run --network=my-net --ip=172.25.3.3 -itd --name=my-container busybox
* 创建镜像时 `Dockerfile` 要通过 `EXPOSE` 指定正确的开放端口
* 容器启动时指定 `PublishAllPort = true`
* 创建容器时用 `-p HOST_PORT:CONTAINER_PORT` `--publish` 显式发布端口例如 `docker run -p 8080:80 nginx`
* 只想把镜像声明的 `EXPOSE` 端口随机发布到宿主机端口时 `-P` / `--publish-all`之后通过 `docker ps` 查看实际端口
* `Dockerfile` 中的 `EXPOSE` 只是镜像元数据不会自动发布端口
### 可以在一个容器中同时运行多个应用进程么
+1 -1
View File
@@ -3,7 +3,7 @@
| 错误信息 / 现象 | 可能原因 | 解决方案 |
| :--- | :--- | :--- |
| `Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?` | Docker 服务未启动 | Linux: `sudo systemctl start docker`<br>Mac/Win: 启动 Docker Desktop |
| `permission denied while trying to connect to the Docker daemon socket` | 当前用户不在 `docker` 用户组 | `sudo usermod -aG docker $USER` (需重新登录) |
| `permission denied while trying to connect to the Docker daemon socket` | 当前用户无权访问 Docker socket | 临时使用 `sudo docker ...`或确认风险后执行 `sudo usermod -aG docker $USER` 并重新登录`docker` 用户组等同授予 root 级权限安全要求较高的环境优先评估 Rootless mode |
| `manifest for ... not found: manifest unknown` | 镜像 tag 不存在 | 检查 Docker Hub 该镜像是否存在该 tag或拼写是否正确 |
| `connection refused` (pull image) | 网络不通或镜像源无法访问 | 检查网络配置[镜像加速器](../../03_install/3.9_mirror.md) |
| `Bind for 0.0.0.0:8080 failed: port is already allocated` | 端口被占用 | 检查占用端口的进程 (`lsof -i:8080`) 并杀掉或换个端口映射 (`-p 8081:80`) |
+2 -2
View File
@@ -1,4 +1,4 @@
# 附录七术语表
## 附录七术语表
本附录整理了本书中常见的一些专业术语及其解释
@@ -71,7 +71,7 @@
## S
* **Swarm (Docker Swarm)**Docker 原生的集群和编排管理工具可将多个 Docker 主机组合成一个统一的虚拟 Docker 主机池
* **Swarm (Docker Swarm)**Docker 原生的集群和编排管理工具可将多个 Docker 主机组合成一个统一的虚拟 Docker 主机池维护节点时通常将节点可用性设为 `Drain`这只影响 Swarm service 调度不会停止该节点上独立运行的容器
## U
+4 -3
View File
@@ -421,7 +421,7 @@ Kubernetes 进阶 (Week 24-36)
- [Docker 官方博客](https://www.docker.com/blog/)
- [Kubernetes 官方博客](https://kubernetes.io/blog/)
- [CNCF 博客](https://www.cncf.io/blog/)
- [DZone](https://dzone.com/containers-cloud)
- [DZone Cloud Architecture](https://dzone.com/cloud-architecture)
### 认证指南
@@ -478,7 +478,7 @@ Kubernetes 进阶 (Week 24-36)
# 1. 学习本书第 1-11 基础到中级
# 2. 完成 20+ 个实战项目
# 3. 参考官方学习指南
curl https://docker.training.kodekloud.com/dca-guide
# 参考 KodeKloud DCA 认证指南https://kodekloud.com/blog/docker-certified-associate-guide/
# 4. 模拟考试
- Linux Academy DCA 练习题
@@ -652,7 +652,8 @@ Host
Overlay
- 跨主机通信基于 VXLAN
- Swarm Kubernetes 标准
- Docker overlay 网络默认使用 UDP 4789 传输数据
- Swarm 标准Kubernetes 通常由 CNI 插件实现跨主机网络
- 性能略低支持分布式
macvlan
+1 -1
View File
@@ -9,7 +9,7 @@
> 2026 年了对于任何新项目**强烈建议** 使用以下生产级替代方案
> - [Rocky Linux](https://hub.docker.com/_/rockylinux)CentOS 原创始人发起的社区驱动项目,目前主流为 Rocky Linux 9。
> - [AlmaLinux](https://hub.docker.com/_/almalinux):由 CloudLinux 支持的企业级发行版,提供长期支持。
> - [CentOS Stream](https://hub.docker.com/r/centos/centos)RHEL 的上游开发分支 (适合开发测试,不建议用于生产环境)。
> - [CentOS Stream](https://quay.io/repository/centos/centos)RHEL 的上游开发分支,镜像已迁移至 Quay.io (适合开发测试,不建议用于生产环境)。
该仓库位于 [Docker Hub CentOS 官方镜像页](https://hub.docker.com/_/centos),提供了 CentOS 从 5 ~ 8 各个版本的镜像(仅作为历史归档,不再更新)。
+1 -1
View File
@@ -1,5 +1,5 @@
{
"title": "Docker -- 从入门到实践",
"title": "Docker 从入门到实践",
"author": "yeasy",
"language": "zh-hans",
"links": {
+201
View File
@@ -0,0 +1,201 @@
#!/usr/bin/env python3
"""Lightweight Markdown project checks for book repositories."""
from __future__ import annotations
import re
import sys
from pathlib import Path
from urllib.parse import unquote, urlparse
ROOT = Path(__file__).resolve().parent
SKIP_DIRS = {
".agent",
".git",
".github",
".idea",
".mdpress",
".mdpress_temp",
".mypy_cache",
".obsidian",
".playwright-cli",
".pytest_cache",
".vuepress",
"__pycache__",
"_book",
"dist",
"mcp_cache",
"node_modules",
"output",
}
SKIP_PREFIXES = ("_site",)
HTML_TARGET_RE = re.compile(
r"\b(?:href|src)\s*=\s*(?:\"([^\"]*)\"|'([^']*)'|([^\s>]+))",
re.IGNORECASE,
)
FENCE_RE = re.compile(r"^\s{0,3}(`{3,}|~{3,})")
def should_skip(path: Path) -> bool:
return any(
part in SKIP_DIRS or part.startswith(SKIP_PREFIXES)
for part in path.relative_to(ROOT).parts
)
def iter_markdown_files() -> list[Path]:
return sorted(path for path in ROOT.rglob("*.md") if not should_skip(path))
def strip_fenced_blocks(text: str) -> str:
output: list[str] = []
in_fence = False
fence_marker = ""
fence_len = 0
for line in text.splitlines():
match = FENCE_RE.match(line)
if match:
marker = match.group(1)
char = marker[0]
length = len(marker)
if not in_fence:
in_fence = True
fence_marker = char
fence_len = length
elif char == fence_marker and length >= fence_len:
in_fence = False
output.append("")
continue
output.append("" if in_fence else line)
return "\n".join(output)
def check_fences(path: Path, text: str) -> list[str]:
issues: list[str] = []
open_fence: tuple[str, int, int] | None = None
for line_no, line in enumerate(text.splitlines(), 1):
match = FENCE_RE.match(line)
if not match:
continue
marker = match.group(1)
char = marker[0]
length = len(marker)
if open_fence is None:
open_fence = (char, length, line_no)
continue
open_char, open_len, _ = open_fence
if char == open_char and length >= open_len:
open_fence = None
if open_fence is not None:
_, _, line_no = open_fence
issues.append(f"{path.relative_to(ROOT)}:{line_no}: unclosed fenced code block")
return issues
def is_local_target(raw_target: str) -> bool:
parsed = urlparse(raw_target)
return not parsed.scheme and not parsed.netloc and not raw_target.startswith("#")
def normalize_target(raw_target: str) -> str:
target = raw_target.strip()
target = target.split("?", 1)[0].split("#", 1)[0]
return unquote(target)
def parse_markdown_target(body: str, start: int) -> tuple[str, int]:
i = start
while i < len(body) and body[i].isspace():
i += 1
if i >= len(body):
return "", i
if body[i] == "<":
end = body.find(">", i + 1)
if end == -1:
return "", i + 1
return body[i + 1 : end].strip(), end + 1
target: list[str] = []
depth = 0
while i < len(body):
char = body[i]
if depth == 0 and (char.isspace() or char == ")"):
break
if char == "(":
depth += 1
elif char == ")":
depth -= 1
target.append(char)
i += 1
return "".join(target).strip(), i
def iter_markdown_link_targets(body: str):
pos = 0
while True:
start = body.find("](", pos)
if start == -1:
break
target, end = parse_markdown_target(body, start + 2)
if target:
yield start, target
pos = max(end + 1, start + 2)
def check_target(path: Path, raw_target: str, line_no: int) -> list[str]:
target = normalize_target(raw_target)
if not target or not is_local_target(raw_target):
return []
target_path = (path.parent / target).resolve()
try:
target_path.relative_to(ROOT)
except ValueError:
return []
if target_path.exists():
return []
return [
f"{path.relative_to(ROOT)}:{line_no}: missing local link target: {raw_target}"
]
def check_links(path: Path, text: str) -> list[str]:
issues: list[str] = []
body = strip_fenced_blocks(text)
for start, raw_target in iter_markdown_link_targets(body):
line_no = body[:start].count("\n") + 1
issues.extend(check_target(path, raw_target, line_no))
for match in HTML_TARGET_RE.finditer(body):
raw_target = next(group for group in match.groups() if group is not None).strip()
line_no = body[: match.start()].count("\n") + 1
issues.extend(check_target(path, raw_target, line_no))
return issues
def check_summary_links() -> list[str]:
summary = ROOT / "SUMMARY.md"
if not summary.exists():
return []
return check_links(summary, summary.read_text(encoding="utf-8", errors="ignore"))
def main() -> int:
issues: list[str] = []
files = iter_markdown_files()
for path in files:
text = path.read_text(encoding="utf-8", errors="ignore")
issues.extend(check_fences(path, text))
issues.extend(check_links(path, text))
issues.extend(check_summary_links())
unique_issues = sorted(set(issues))
if unique_issues:
print("\n".join(unique_issues))
print(f"\n{len(unique_issues)} issue(s) found across {len(files)} Markdown files.")
return 1
print(f"All {len(files)} Markdown files passed project checks.")
return 0
if __name__ == "__main__":
sys.exit(main())
+4 -4
View File
@@ -1,15 +1,15 @@
{
"name": "docker_practice",
"version": "1.7.2",
"version": "1.9.2",
"description": "docker_practice",
"main": "index.js",
"private": true,
"devDependencies": {
"vuepress": "1.9.10",
"vuepress-plugin-container": "^2.1.5",
"vuepress-theme-hope": "^1.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "node scripts/check_metadata.js",
"mdpress:help": "mdpress --help",
"build": "rm -rf _site _site_site && mdpress build --format site --output _site && rm -rf _site && mv _site_site _site",
"serve": "mdpress serve",
@@ -23,7 +23,7 @@
"url": "git+https://github.com/yeasy/docker_practice.git"
},
"author": "",
"license": "ISC",
"license": "CC-BY-NC-SA-4.0",
"bugs": {
"url": "https://github.com/yeasy/docker_practice/issues"
},
+15
View File
@@ -0,0 +1,15 @@
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
const readme = fs.readFileSync('README.md', 'utf8');
const topVersion = changelog.match(/^\* ([0-9]+\.[0-9]+\.[0-9]+)\b/m)?.[1];
if (pkg.version !== topVersion) {
throw new Error(`package.json version ${pkg.version} does not match CHANGELOG ${topVersion}`);
}
if (pkg.license !== 'CC-BY-NC-SA-4.0' || !readme.includes('CC BY-NC-SA 4.0')) {
throw new Error('package.json license must match README license');
}